xref: /kvmtool/virtio/blk.c (revision fb0957f29981d280fe890b7aadcee4f3df95ca65)
1416b2c2dSAsias He #include "kvm/virtio-blk.h"
2b30d05adSPekka Enberg 
3416b2c2dSAsias He #include "kvm/virtio-pci.h"
476b6349fSPekka Enberg 
55a24a9f2SPekka Enberg #include "kvm/disk-image.h"
639d6af07SAsias He #include "kvm/virtio.h"
7b30d05adSPekka Enberg #include "kvm/ioport.h"
84ef0f4d6SPekka Enberg #include "kvm/mutex.h"
9fe99fd4eSPekka Enberg #include "kvm/util.h"
108b1ff07eSPekka Enberg #include "kvm/kvm.h"
11b30d05adSPekka Enberg #include "kvm/pci.h"
12*fb0957f2SSasha Levin #include "kvm/threadpool.h"
13b30d05adSPekka Enberg 
1420c64ecaSPekka Enberg #include <linux/virtio_ring.h>
1520c64ecaSPekka Enberg #include <linux/virtio_blk.h>
160528c2a7SPekka Enberg 
174155ba8cSPekka Enberg #include <inttypes.h>
180528c2a7SPekka Enberg #include <pthread.h>
194155ba8cSPekka Enberg 
20bc0363c8SCyrill Gorcunov #define VIRTIO_BLK_IRQ		15
21bc0363c8SCyrill Gorcunov #define VIRTIO_BLK_PIN		1
22984b7ae0SPekka Enberg 
2310eca11dSPekka Enberg #define NUM_VIRT_QUEUES		1
2410eca11dSPekka Enberg 
2503110ff3SAsias He #define VIRTIO_BLK_QUEUE_SIZE	128
2610eca11dSPekka Enberg 
27802e07a0SAsias He struct blk_device {
280528c2a7SPekka Enberg 	pthread_mutex_t			mutex;
290528c2a7SPekka Enberg 
3040ce993fSPekka Enberg 	struct virtio_blk_config	blk_config;
31c435b91dSPekka Enberg 	uint32_t			host_features;
32fbc2fbf9SPekka Enberg 	uint32_t			guest_features;
3340ce993fSPekka Enberg 	uint16_t			config_vector;
34fbc2fbf9SPekka Enberg 	uint8_t				status;
3547bf1d0fSPekka Enberg 
3647bf1d0fSPekka Enberg 	/* virtio queue */
3747bf1d0fSPekka Enberg 	uint16_t			queue_selector;
3810eca11dSPekka Enberg 
3945e47970SAsias He 	struct virt_queue		vqs[NUM_VIRT_QUEUES];
40*fb0957f2SSasha Levin 
41*fb0957f2SSasha Levin 	void			*jobs[NUM_VIRT_QUEUES];
42fbc2fbf9SPekka Enberg };
43fbc2fbf9SPekka Enberg 
4403110ff3SAsias He #define DISK_SEG_MAX	126
4540ce993fSPekka Enberg 
46802e07a0SAsias He static struct blk_device blk_device = {
470528c2a7SPekka Enberg 	.mutex			= PTHREAD_MUTEX_INITIALIZER,
480528c2a7SPekka Enberg 
4940ce993fSPekka Enberg 	.blk_config		= (struct virtio_blk_config) {
50e199f440SPekka Enberg 		/* VIRTIO_BLK_F_SEG_MAX */
5103110ff3SAsias He 		.seg_max		= DISK_SEG_MAX,
5240ce993fSPekka Enberg 	},
531ef2738dSCyrill Gorcunov 	/*
541ef2738dSCyrill Gorcunov 	 * Note we don't set VIRTIO_BLK_F_GEOMETRY here so the
551ef2738dSCyrill Gorcunov 	 * node kernel will compute disk geometry by own, the
561ef2738dSCyrill Gorcunov 	 * same applies to VIRTIO_BLK_F_BLK_SIZE
571ef2738dSCyrill Gorcunov 	 */
5803110ff3SAsias He 	.host_features		= (1UL << VIRTIO_BLK_F_SEG_MAX),
59c435b91dSPekka Enberg };
60fbc2fbf9SPekka Enberg 
61416b2c2dSAsias He static bool virtio_blk_pci_io_device_specific_in(void *data, unsigned long offset, int size, uint32_t count)
6240ce993fSPekka Enberg {
63802e07a0SAsias He 	uint8_t *config_space = (uint8_t *) &blk_device.blk_config;
6440ce993fSPekka Enberg 
6540ce993fSPekka Enberg 	if (size != 1 || count != 1)
6640ce993fSPekka Enberg 		return false;
6740ce993fSPekka Enberg 
6840ce993fSPekka Enberg 	ioport__write8(data, config_space[offset - VIRTIO_PCI_CONFIG_NOMSI]);
6940ce993fSPekka Enberg 
7040ce993fSPekka Enberg 	return true;
7140ce993fSPekka Enberg }
7240ce993fSPekka Enberg 
73416b2c2dSAsias He static bool virtio_blk_pci_io_in(struct kvm *self, uint16_t port, void *data, int size, uint32_t count)
74fbc2fbf9SPekka Enberg {
75fbc2fbf9SPekka Enberg 	unsigned long offset;
760528c2a7SPekka Enberg 	bool ret = true;
770528c2a7SPekka Enberg 
784ef0f4d6SPekka Enberg 	mutex_lock(&blk_device.mutex);
79fbc2fbf9SPekka Enberg 
80d1888318SCyrill Gorcunov 	offset		= port - IOPORT_VIRTIO_BLK;
81fbc2fbf9SPekka Enberg 
82fbc2fbf9SPekka Enberg 	switch (offset) {
83fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_HOST_FEATURES:
84802e07a0SAsias He 		ioport__write32(data, blk_device.host_features);
85fbc2fbf9SPekka Enberg 		break;
86fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_GUEST_FEATURES:
870528c2a7SPekka Enberg 		ret		= false;
889ee67e60SAsias He 		break;
89fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_QUEUE_PFN:
9045e47970SAsias He 		ioport__write32(data, blk_device.vqs[blk_device.queue_selector].pfn);
918b1ff07eSPekka Enberg 		break;
92fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_QUEUE_NUM:
9310eca11dSPekka Enberg 		ioport__write16(data, VIRTIO_BLK_QUEUE_SIZE);
948b1ff07eSPekka Enberg 		break;
95fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_QUEUE_SEL:
96fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_QUEUE_NOTIFY:
970528c2a7SPekka Enberg 		ret		= false;
989ee67e60SAsias He 		break;
99fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_STATUS:
100802e07a0SAsias He 		ioport__write8(data, blk_device.status);
101fbc2fbf9SPekka Enberg 		break;
102fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_ISR:
1038b1ff07eSPekka Enberg 		ioport__write8(data, 0x1);
1048b1ff07eSPekka Enberg 		kvm__irq_line(self, VIRTIO_BLK_IRQ, 0);
1057e61688eSPekka Enberg 		break;
106fbc2fbf9SPekka Enberg 	case VIRTIO_MSI_CONFIG_VECTOR:
107802e07a0SAsias He 		ioport__write16(data, blk_device.config_vector);
10840ce993fSPekka Enberg 		break;
109fbc2fbf9SPekka Enberg 	default:
1100528c2a7SPekka Enberg 		ret		= virtio_blk_pci_io_device_specific_in(data, offset, size, count);
111fbc2fbf9SPekka Enberg 	};
112fbc2fbf9SPekka Enberg 
1134ef0f4d6SPekka Enberg 	mutex_unlock(&blk_device.mutex);
1140528c2a7SPekka Enberg 
1150528c2a7SPekka Enberg 	return ret;
116fbc2fbf9SPekka Enberg }
117fbc2fbf9SPekka Enberg 
11845e47970SAsias He static bool virtio_blk_do_io_request(struct kvm *self, struct virt_queue *queue)
1194155ba8cSPekka Enberg {
12045e47970SAsias He 	struct iovec iov[VIRTIO_BLK_QUEUE_SIZE];
1214155ba8cSPekka Enberg 	struct virtio_blk_outhdr *req;
12270b53f25SSasha Levin 	ssize_t block_cnt = -1;
12345e47970SAsias He 	uint16_t out, in, head;
1244155ba8cSPekka Enberg 	uint8_t *status;
1254155ba8cSPekka Enberg 
12645e47970SAsias He 	head		= virt_queue__get_iov(queue, iov, &out, &in, self);
1274155ba8cSPekka Enberg 
12845e47970SAsias He 	/* head */
12945e47970SAsias He 	req		= iov[0].iov_base;
13003110ff3SAsias He 
131258dd093SPekka Enberg 	switch (req->type) {
13203110ff3SAsias He 	case VIRTIO_BLK_T_IN:
1332d103098SSasha Levin 		block_cnt = disk_image__read_sector_iov(self->disk_image, req->sector, iov + 1, in + out - 2);
13470b53f25SSasha Levin 
135258dd093SPekka Enberg 		break;
13603110ff3SAsias He 	case VIRTIO_BLK_T_OUT:
1372d103098SSasha Levin 		block_cnt = disk_image__write_sector_iov(self->disk_image, req->sector, iov + 1, in + out - 2);
13870b53f25SSasha Levin 
139258dd093SPekka Enberg 		break;
14070b53f25SSasha Levin 
141258dd093SPekka Enberg 	default:
1424155ba8cSPekka Enberg 		warning("request type %d", req->type);
14370b53f25SSasha Levin 		block_cnt = -1;
14403110ff3SAsias He 	}
14503110ff3SAsias He 
14645e47970SAsias He 	/* status */
14745e47970SAsias He 	status			= iov[out + in - 1].iov_base;
14870b53f25SSasha Levin 	*status			= (block_cnt < 0) ? VIRTIO_BLK_S_IOERR : VIRTIO_BLK_S_OK;
14903110ff3SAsias He 
15045e47970SAsias He 	virt_queue__set_used_elem(queue, head, block_cnt);
1514155ba8cSPekka Enberg 
1524155ba8cSPekka Enberg 	return true;
1534155ba8cSPekka Enberg }
1544155ba8cSPekka Enberg 
155*fb0957f2SSasha Levin static void virtio_blk_do_io(struct kvm *kvm, void *param)
15645e47970SAsias He {
157*fb0957f2SSasha Levin 	struct virt_queue *vq = param;
15845e47970SAsias He 
1590ea58e5bSPekka Enberg 	while (virt_queue__available(vq))
1604baf6f73SSasha Levin 		virtio_blk_do_io_request(kvm, vq);
1610ea58e5bSPekka Enberg 
1624baf6f73SSasha Levin 	kvm__irq_line(kvm, VIRTIO_BLK_IRQ, 1);
1634baf6f73SSasha Levin }
1640528c2a7SPekka Enberg 
165416b2c2dSAsias He static bool virtio_blk_pci_io_out(struct kvm *self, uint16_t port, void *data, int size, uint32_t count)
166fbc2fbf9SPekka Enberg {
167fbc2fbf9SPekka Enberg 	unsigned long offset;
1680528c2a7SPekka Enberg 	bool ret = true;
1690528c2a7SPekka Enberg 
1704ef0f4d6SPekka Enberg 	mutex_lock(&blk_device.mutex);
171fbc2fbf9SPekka Enberg 
172d1888318SCyrill Gorcunov 	offset		= port - IOPORT_VIRTIO_BLK;
173fbc2fbf9SPekka Enberg 
174fbc2fbf9SPekka Enberg 	switch (offset) {
175fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_GUEST_FEATURES:
176802e07a0SAsias He 		blk_device.guest_features	= ioport__read32(data);
177fbc2fbf9SPekka Enberg 		break;
17810eca11dSPekka Enberg 	case VIRTIO_PCI_QUEUE_PFN: {
17910eca11dSPekka Enberg 		struct virt_queue *queue;
18010eca11dSPekka Enberg 		void *p;
18110eca11dSPekka Enberg 
18245e47970SAsias He 		queue			= &blk_device.vqs[blk_device.queue_selector];
18310eca11dSPekka Enberg 
18410eca11dSPekka Enberg 		queue->pfn		= ioport__read32(data);
18510eca11dSPekka Enberg 
18610eca11dSPekka Enberg 		p			= guest_flat_to_host(self, queue->pfn << 12);
18710eca11dSPekka Enberg 
18810eca11dSPekka Enberg 		vring_init(&queue->vring, VIRTIO_BLK_QUEUE_SIZE, p, 4096);
18910eca11dSPekka Enberg 
190*fb0957f2SSasha Levin 		blk_device.jobs[blk_device.queue_selector] =
191*fb0957f2SSasha Levin 			thread_pool__add_jobtype(self, virtio_blk_do_io, queue);
192*fb0957f2SSasha Levin 
1937e61688eSPekka Enberg 		break;
19410eca11dSPekka Enberg 	}
195fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_QUEUE_SEL:
196802e07a0SAsias He 		blk_device.queue_selector	= ioport__read16(data);
1977e61688eSPekka Enberg 		break;
19810eca11dSPekka Enberg 	case VIRTIO_PCI_QUEUE_NOTIFY: {
19910eca11dSPekka Enberg 		uint16_t queue_index;
20010eca11dSPekka Enberg 		queue_index		= ioport__read16(data);
201*fb0957f2SSasha Levin 		thread_pool__signal_work(blk_device.jobs[queue_index]);
2027e61688eSPekka Enberg 		break;
20310eca11dSPekka Enberg 	}
204fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_STATUS:
205802e07a0SAsias He 		blk_device.status		= ioport__read8(data);
206fbc2fbf9SPekka Enberg 		break;
207fbc2fbf9SPekka Enberg 	case VIRTIO_MSI_CONFIG_VECTOR:
208802e07a0SAsias He 		blk_device.config_vector	= VIRTIO_MSI_NO_VECTOR;
20940ce993fSPekka Enberg 		break;
210fbc2fbf9SPekka Enberg 	case VIRTIO_MSI_QUEUE_VECTOR:
21140ce993fSPekka Enberg 		break;
212fbc2fbf9SPekka Enberg 	default:
2130528c2a7SPekka Enberg 		ret		= false;
214fbc2fbf9SPekka Enberg 	};
215fbc2fbf9SPekka Enberg 
2164ef0f4d6SPekka Enberg 	mutex_unlock(&blk_device.mutex);
2170528c2a7SPekka Enberg 
2180528c2a7SPekka Enberg 	return ret;
219fbc2fbf9SPekka Enberg }
220fbc2fbf9SPekka Enberg 
221416b2c2dSAsias He static struct ioport_operations virtio_blk_io_ops = {
222416b2c2dSAsias He 	.io_in		= virtio_blk_pci_io_in,
223416b2c2dSAsias He 	.io_out		= virtio_blk_pci_io_out,
224fbc2fbf9SPekka Enberg };
225fbc2fbf9SPekka Enberg 
226b30d05adSPekka Enberg #define PCI_VENDOR_ID_REDHAT_QUMRANET		0x1af4
227b30d05adSPekka Enberg #define PCI_DEVICE_ID_VIRTIO_BLK		0x1001
228b30d05adSPekka Enberg #define PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET	0x1af4
229b30d05adSPekka Enberg #define PCI_SUBSYSTEM_ID_VIRTIO_BLK		0x0002
230b30d05adSPekka Enberg 
231416b2c2dSAsias He static struct pci_device_header virtio_blk_pci_device = {
232b30d05adSPekka Enberg 	.vendor_id		= PCI_VENDOR_ID_REDHAT_QUMRANET,
233b30d05adSPekka Enberg 	.device_id		= PCI_DEVICE_ID_VIRTIO_BLK,
234b30d05adSPekka Enberg 	.header_type		= PCI_HEADER_TYPE_NORMAL,
235b30d05adSPekka Enberg 	.revision_id		= 0,
236b30d05adSPekka Enberg 	.class			= 0x010000,
237b30d05adSPekka Enberg 	.subsys_vendor_id	= PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET,
238b30d05adSPekka Enberg 	.subsys_id		= PCI_SUBSYSTEM_ID_VIRTIO_BLK,
239d1888318SCyrill Gorcunov 	.bar[0]			= IOPORT_VIRTIO_BLK | PCI_BASE_ADDRESS_SPACE_IO,
240bc0363c8SCyrill Gorcunov 	.irq_pin		= VIRTIO_BLK_PIN,
2418b1ff07eSPekka Enberg 	.irq_line		= VIRTIO_BLK_IRQ,
242b30d05adSPekka Enberg };
243b30d05adSPekka Enberg 
244f05bbe8dSAsias He #define PCI_VIRTIO_BLK_DEVNUM 1
245f05bbe8dSAsias He 
246416b2c2dSAsias He void virtio_blk__init(struct kvm *self)
247b30d05adSPekka Enberg {
2481f848897SPekka Enberg 	if (!self->disk_image)
2491f848897SPekka Enberg 		return;
2501f848897SPekka Enberg 
251802e07a0SAsias He 	blk_device.blk_config.capacity = self->disk_image->size / SECTOR_SIZE;
252ca7c891bSCyrill Gorcunov 
253416b2c2dSAsias He 	pci__register(&virtio_blk_pci_device, PCI_VIRTIO_BLK_DEVNUM);
254b30d05adSPekka Enberg 
255416b2c2dSAsias He 	ioport__register(IOPORT_VIRTIO_BLK, &virtio_blk_io_ops, IOPORT_VIRTIO_BLK_SIZE);
256b30d05adSPekka Enberg }
257