xref: /kvmtool/virtio/blk.c (revision 0ea58e5bbc3775506d99a8d9e1c78c371426a6ac)
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"
8fe99fd4eSPekka Enberg #include "kvm/util.h"
98b1ff07eSPekka Enberg #include "kvm/kvm.h"
10b30d05adSPekka Enberg #include "kvm/pci.h"
11b30d05adSPekka Enberg 
1220c64ecaSPekka Enberg #include <linux/virtio_ring.h>
1320c64ecaSPekka Enberg #include <linux/virtio_blk.h>
144155ba8cSPekka Enberg #include <inttypes.h>
154155ba8cSPekka Enberg #include <assert.h>
164155ba8cSPekka Enberg 
178b1ff07eSPekka Enberg #define VIRTIO_BLK_IRQ		14
18984b7ae0SPekka Enberg 
1910eca11dSPekka Enberg #define NUM_VIRT_QUEUES		1
2010eca11dSPekka Enberg 
2103110ff3SAsias He #define VIRTIO_BLK_QUEUE_SIZE	128
2210eca11dSPekka Enberg 
23802e07a0SAsias He struct blk_device {
2440ce993fSPekka Enberg 	struct virtio_blk_config	blk_config;
25c435b91dSPekka Enberg 	uint32_t			host_features;
26fbc2fbf9SPekka Enberg 	uint32_t			guest_features;
2740ce993fSPekka Enberg 	uint16_t			config_vector;
28fbc2fbf9SPekka Enberg 	uint8_t				status;
2947bf1d0fSPekka Enberg 
3047bf1d0fSPekka Enberg 	/* virtio queue */
3147bf1d0fSPekka Enberg 	uint16_t			queue_selector;
3210eca11dSPekka Enberg 
3345e47970SAsias He 	struct virt_queue		vqs[NUM_VIRT_QUEUES];
34fbc2fbf9SPekka Enberg };
35fbc2fbf9SPekka Enberg 
3603110ff3SAsias He #define DISK_SEG_MAX	126
3740ce993fSPekka Enberg 
38802e07a0SAsias He static struct blk_device blk_device = {
3940ce993fSPekka Enberg 	.blk_config		= (struct virtio_blk_config) {
40e199f440SPekka Enberg 		/* VIRTIO_BLK_F_SEG_MAX */
4103110ff3SAsias He 		.seg_max		= DISK_SEG_MAX,
4240ce993fSPekka Enberg 	},
431ef2738dSCyrill Gorcunov 	/*
441ef2738dSCyrill Gorcunov 	 * Note we don't set VIRTIO_BLK_F_GEOMETRY here so the
451ef2738dSCyrill Gorcunov 	 * node kernel will compute disk geometry by own, the
461ef2738dSCyrill Gorcunov 	 * same applies to VIRTIO_BLK_F_BLK_SIZE
471ef2738dSCyrill Gorcunov 	 */
4803110ff3SAsias He 	.host_features		= (1UL << VIRTIO_BLK_F_SEG_MAX),
49c435b91dSPekka Enberg };
50fbc2fbf9SPekka Enberg 
51416b2c2dSAsias He static bool virtio_blk_pci_io_device_specific_in(void *data, unsigned long offset, int size, uint32_t count)
5240ce993fSPekka Enberg {
53802e07a0SAsias He 	uint8_t *config_space = (uint8_t *) &blk_device.blk_config;
5440ce993fSPekka Enberg 
5540ce993fSPekka Enberg 	if (size != 1 || count != 1)
5640ce993fSPekka Enberg 		return false;
5740ce993fSPekka Enberg 
5840ce993fSPekka Enberg 	ioport__write8(data, config_space[offset - VIRTIO_PCI_CONFIG_NOMSI]);
5940ce993fSPekka Enberg 
6040ce993fSPekka Enberg 	return true;
6140ce993fSPekka Enberg }
6240ce993fSPekka Enberg 
63416b2c2dSAsias He static bool virtio_blk_pci_io_in(struct kvm *self, uint16_t port, void *data, int size, uint32_t count)
64fbc2fbf9SPekka Enberg {
65fbc2fbf9SPekka Enberg 	unsigned long offset;
66fbc2fbf9SPekka Enberg 
67d1888318SCyrill Gorcunov 	offset		= port - IOPORT_VIRTIO_BLK;
68fbc2fbf9SPekka Enberg 
69fbc2fbf9SPekka Enberg 	switch (offset) {
70fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_HOST_FEATURES:
71802e07a0SAsias He 		ioport__write32(data, blk_device.host_features);
72fbc2fbf9SPekka Enberg 		break;
73fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_GUEST_FEATURES:
748b1ff07eSPekka Enberg 		return false;
75fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_QUEUE_PFN:
7645e47970SAsias He 		ioport__write32(data, blk_device.vqs[blk_device.queue_selector].pfn);
778b1ff07eSPekka Enberg 		break;
78fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_QUEUE_NUM:
7910eca11dSPekka Enberg 		ioport__write16(data, VIRTIO_BLK_QUEUE_SIZE);
808b1ff07eSPekka Enberg 		break;
81fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_QUEUE_SEL:
82fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_QUEUE_NOTIFY:
83fbc2fbf9SPekka Enberg 		return false;
84fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_STATUS:
85802e07a0SAsias He 		ioport__write8(data, blk_device.status);
86fbc2fbf9SPekka Enberg 		break;
87fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_ISR:
888b1ff07eSPekka Enberg 		ioport__write8(data, 0x1);
898b1ff07eSPekka Enberg 		kvm__irq_line(self, VIRTIO_BLK_IRQ, 0);
907e61688eSPekka Enberg 		break;
91fbc2fbf9SPekka Enberg 	case VIRTIO_MSI_CONFIG_VECTOR:
92802e07a0SAsias He 		ioport__write16(data, blk_device.config_vector);
9340ce993fSPekka Enberg 		break;
94fbc2fbf9SPekka Enberg 	default:
95416b2c2dSAsias He 		return virtio_blk_pci_io_device_specific_in(data, offset, size, count);
96fbc2fbf9SPekka Enberg 	};
97fbc2fbf9SPekka Enberg 
98fbc2fbf9SPekka Enberg 	return true;
99fbc2fbf9SPekka Enberg }
100fbc2fbf9SPekka Enberg 
10145e47970SAsias He static bool virtio_blk_do_io_request(struct kvm *self, struct virt_queue *queue)
1024155ba8cSPekka Enberg {
10345e47970SAsias He 
10445e47970SAsias He 	struct iovec iov[VIRTIO_BLK_QUEUE_SIZE];
1054155ba8cSPekka Enberg 	struct virtio_blk_outhdr *req;
10645e47970SAsias He 	uint32_t block_len, block_cnt;
10745e47970SAsias He 	uint16_t out, in, head;
1084155ba8cSPekka Enberg 	uint8_t *status;
109f0952e64SPekka Enberg 	bool io_error;
110258dd093SPekka Enberg 	void *block;
111f0952e64SPekka Enberg 	int err, i;
112f0952e64SPekka Enberg 
113f0952e64SPekka Enberg 	io_error	= false;
1144155ba8cSPekka Enberg 
11545e47970SAsias He 	head		= virt_queue__get_iov(queue, iov, &out, &in, self);
1164155ba8cSPekka Enberg 
11745e47970SAsias He 	/* head */
11845e47970SAsias He 	req		= iov[0].iov_base;
11903110ff3SAsias He 
1204155ba8cSPekka Enberg 	/* block */
12103110ff3SAsias He 	block_cnt	= 0;
12203110ff3SAsias He 
12345e47970SAsias He 	for (i = 1; i < out + in - 1; i++) {
12445e47970SAsias He 		block		= iov[i].iov_base;
12545e47970SAsias He 		block_len	= iov[i].iov_len;
1264155ba8cSPekka Enberg 
127258dd093SPekka Enberg 		switch (req->type) {
12803110ff3SAsias He 		case VIRTIO_BLK_T_IN:
129258dd093SPekka Enberg 			err	= disk_image__read_sector(self->disk_image, req->sector, block, block_len);
130258dd093SPekka Enberg 			break;
13103110ff3SAsias He 		case VIRTIO_BLK_T_OUT:
132258dd093SPekka Enberg 			err	= disk_image__write_sector(self->disk_image, req->sector, block, block_len);
133258dd093SPekka Enberg 			break;
134258dd093SPekka Enberg 		default:
1354155ba8cSPekka Enberg 			warning("request type %d", req->type);
136f0952e64SPekka Enberg 			io_error	= true;
137a2c8c696SAsias He 		}
138a2c8c696SAsias He 
13903110ff3SAsias He 		req->sector	+= block_len >> SECTOR_SHIFT;
14003110ff3SAsias He 		block_cnt	+= block_len;
14103110ff3SAsias He 	}
14203110ff3SAsias He 
14345e47970SAsias He 	/* status */
14445e47970SAsias He 	status			= iov[out + in - 1].iov_base;
145f0952e64SPekka Enberg 	*status			= io_error ? VIRTIO_BLK_S_IOERR : VIRTIO_BLK_S_OK;
14603110ff3SAsias He 
14745e47970SAsias He 	virt_queue__set_used_elem(queue, head, block_cnt);
1484155ba8cSPekka Enberg 
1494155ba8cSPekka Enberg 	return true;
1504155ba8cSPekka Enberg }
1514155ba8cSPekka Enberg 
15245e47970SAsias He static void virtio_blk_handle_callback(struct kvm *self, uint16_t queue_index)
15345e47970SAsias He {
154*0ea58e5bSPekka Enberg 	struct virt_queue *vq = &blk_device.vqs[queue_index];
15545e47970SAsias He 
156*0ea58e5bSPekka Enberg 	while (virt_queue__available(vq))
15745e47970SAsias He 		virtio_blk_do_io_request(self, vq);
158*0ea58e5bSPekka Enberg 
15945e47970SAsias He 	kvm__irq_line(self, VIRTIO_BLK_IRQ, 1);
16045e47970SAsias He 
16145e47970SAsias He }
162416b2c2dSAsias He static bool virtio_blk_pci_io_out(struct kvm *self, uint16_t port, void *data, int size, uint32_t count)
163fbc2fbf9SPekka Enberg {
164fbc2fbf9SPekka Enberg 	unsigned long offset;
165fbc2fbf9SPekka Enberg 
166d1888318SCyrill Gorcunov 	offset		= port - IOPORT_VIRTIO_BLK;
167fbc2fbf9SPekka Enberg 
168fbc2fbf9SPekka Enberg 	switch (offset) {
169fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_GUEST_FEATURES:
170802e07a0SAsias He 		blk_device.guest_features	= ioport__read32(data);
171fbc2fbf9SPekka Enberg 		break;
17210eca11dSPekka Enberg 	case VIRTIO_PCI_QUEUE_PFN: {
17310eca11dSPekka Enberg 		struct virt_queue *queue;
17410eca11dSPekka Enberg 		void *p;
17510eca11dSPekka Enberg 
17645e47970SAsias He 		queue			= &blk_device.vqs[blk_device.queue_selector];
17710eca11dSPekka Enberg 
17810eca11dSPekka Enberg 		queue->pfn		= ioport__read32(data);
17910eca11dSPekka Enberg 
18010eca11dSPekka Enberg 		p			= guest_flat_to_host(self, queue->pfn << 12);
18110eca11dSPekka Enberg 
18210eca11dSPekka Enberg 		vring_init(&queue->vring, VIRTIO_BLK_QUEUE_SIZE, p, 4096);
18310eca11dSPekka Enberg 
1847e61688eSPekka Enberg 		break;
18510eca11dSPekka Enberg 	}
186fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_QUEUE_SEL:
187802e07a0SAsias He 		blk_device.queue_selector	= ioport__read16(data);
1887e61688eSPekka Enberg 		break;
18910eca11dSPekka Enberg 	case VIRTIO_PCI_QUEUE_NOTIFY: {
19010eca11dSPekka Enberg 		uint16_t queue_index;
19110eca11dSPekka Enberg 		queue_index		= ioport__read16(data);
19245e47970SAsias He 		virtio_blk_handle_callback(self, queue_index);
1937e61688eSPekka Enberg 		break;
19410eca11dSPekka Enberg 	}
195fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_STATUS:
196802e07a0SAsias He 		blk_device.status		= ioport__read8(data);
197fbc2fbf9SPekka Enberg 		break;
198fbc2fbf9SPekka Enberg 	case VIRTIO_MSI_CONFIG_VECTOR:
199802e07a0SAsias He 		blk_device.config_vector	= VIRTIO_MSI_NO_VECTOR;
20040ce993fSPekka Enberg 		break;
201fbc2fbf9SPekka Enberg 	case VIRTIO_MSI_QUEUE_VECTOR:
20240ce993fSPekka Enberg 		break;
203fbc2fbf9SPekka Enberg 	default:
204fbc2fbf9SPekka Enberg 		return false;
205fbc2fbf9SPekka Enberg 	};
206fbc2fbf9SPekka Enberg 
207fbc2fbf9SPekka Enberg 	return true;
208fbc2fbf9SPekka Enberg }
209fbc2fbf9SPekka Enberg 
210416b2c2dSAsias He static struct ioport_operations virtio_blk_io_ops = {
211416b2c2dSAsias He 	.io_in		= virtio_blk_pci_io_in,
212416b2c2dSAsias He 	.io_out		= virtio_blk_pci_io_out,
213fbc2fbf9SPekka Enberg };
214fbc2fbf9SPekka Enberg 
215b30d05adSPekka Enberg #define PCI_VENDOR_ID_REDHAT_QUMRANET		0x1af4
216b30d05adSPekka Enberg #define PCI_DEVICE_ID_VIRTIO_BLK		0x1001
217b30d05adSPekka Enberg #define PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET	0x1af4
218b30d05adSPekka Enberg #define PCI_SUBSYSTEM_ID_VIRTIO_BLK		0x0002
219b30d05adSPekka Enberg 
220416b2c2dSAsias He static struct pci_device_header virtio_blk_pci_device = {
221b30d05adSPekka Enberg 	.vendor_id		= PCI_VENDOR_ID_REDHAT_QUMRANET,
222b30d05adSPekka Enberg 	.device_id		= PCI_DEVICE_ID_VIRTIO_BLK,
223b30d05adSPekka Enberg 	.header_type		= PCI_HEADER_TYPE_NORMAL,
224b30d05adSPekka Enberg 	.revision_id		= 0,
225b30d05adSPekka Enberg 	.class			= 0x010000,
226b30d05adSPekka Enberg 	.subsys_vendor_id	= PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET,
227b30d05adSPekka Enberg 	.subsys_id		= PCI_SUBSYSTEM_ID_VIRTIO_BLK,
228d1888318SCyrill Gorcunov 	.bar[0]			= IOPORT_VIRTIO_BLK | PCI_BASE_ADDRESS_SPACE_IO,
229dc53a427SPekka Enberg 	.irq_pin		= 1,
2308b1ff07eSPekka Enberg 	.irq_line		= VIRTIO_BLK_IRQ,
231b30d05adSPekka Enberg };
232b30d05adSPekka Enberg 
233f05bbe8dSAsias He #define PCI_VIRTIO_BLK_DEVNUM 1
234f05bbe8dSAsias He 
235416b2c2dSAsias He void virtio_blk__init(struct kvm *self)
236b30d05adSPekka Enberg {
2371f848897SPekka Enberg 	if (!self->disk_image)
2381f848897SPekka Enberg 		return;
2391f848897SPekka Enberg 
240802e07a0SAsias He 	blk_device.blk_config.capacity = self->disk_image->size / SECTOR_SIZE;
241ca7c891bSCyrill Gorcunov 
242416b2c2dSAsias He 	pci__register(&virtio_blk_pci_device, PCI_VIRTIO_BLK_DEVNUM);
243b30d05adSPekka Enberg 
244416b2c2dSAsias He 	ioport__register(IOPORT_VIRTIO_BLK, &virtio_blk_io_ops, IOPORT_VIRTIO_BLK_SIZE);
245b30d05adSPekka Enberg }
246