xref: /kvmtool/virtio/blk.c (revision 45e47970eb967c54e71ba1e77e7189e8c5dd497c)
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 
33*45e47970SAsias 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:
76*45e47970SAsias 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 
101*45e47970SAsias He static bool virtio_blk_do_io_request(struct kvm *self, struct virt_queue *queue)
1024155ba8cSPekka Enberg {
103*45e47970SAsias He 
104*45e47970SAsias He 	struct iovec iov[VIRTIO_BLK_QUEUE_SIZE];
1054155ba8cSPekka Enberg 	struct virtio_blk_outhdr *req;
106*45e47970SAsias He 	uint32_t block_len, block_cnt;
107*45e47970SAsias He 	uint16_t out, in, head;
108*45e47970SAsias He 	int err, err_cnt, i;
1094155ba8cSPekka Enberg 	uint8_t *status;
110258dd093SPekka Enberg 	void *block;
1114155ba8cSPekka Enberg 
112*45e47970SAsias He 	head			= virt_queue__get_iov(queue, iov, &out, &in, self);
1134155ba8cSPekka Enberg 
114*45e47970SAsias He 	/* head */
115*45e47970SAsias He 	req		= iov[0].iov_base;
11603110ff3SAsias He 
1174155ba8cSPekka Enberg 	/* block */
11803110ff3SAsias He 	block_cnt	= 0;
11903110ff3SAsias He 	err_cnt		= 0;
12003110ff3SAsias He 
121*45e47970SAsias He 	for (i = 1; i < out + in - 1; i++) {
122*45e47970SAsias He 		block		= iov[i].iov_base;
123*45e47970SAsias He 		block_len	= iov[i].iov_len;
1244155ba8cSPekka Enberg 
125258dd093SPekka Enberg 		switch (req->type) {
12603110ff3SAsias He 		case VIRTIO_BLK_T_IN:
127258dd093SPekka Enberg 			err	= disk_image__read_sector(self->disk_image, req->sector, block, block_len);
128258dd093SPekka Enberg 			break;
12903110ff3SAsias He 		case VIRTIO_BLK_T_OUT:
130258dd093SPekka Enberg 			err	= disk_image__write_sector(self->disk_image, req->sector, block, block_len);
131258dd093SPekka Enberg 			break;
132258dd093SPekka Enberg 		default:
1334155ba8cSPekka Enberg 			warning("request type %d", req->type);
13403110ff3SAsias He 			err	= -1;
135a2c8c696SAsias He 		}
136a2c8c696SAsias He 
13703110ff3SAsias He 		if (err)
13803110ff3SAsias He 			err_cnt++;
139a267e01bSPekka Enberg 
14003110ff3SAsias He 		req->sector	+= block_len >> SECTOR_SHIFT;
14103110ff3SAsias He 		block_cnt	+= block_len;
14203110ff3SAsias He 	}
14303110ff3SAsias He 
144*45e47970SAsias He 	/* status */
145*45e47970SAsias He 	status			= iov[out + in - 1].iov_base;
14603110ff3SAsias He 	*status			= err_cnt ? VIRTIO_BLK_S_IOERR : VIRTIO_BLK_S_OK;
14703110ff3SAsias He 
148*45e47970SAsias He 	virt_queue__set_used_elem(queue, head, block_cnt);
1494155ba8cSPekka Enberg 
1504155ba8cSPekka Enberg 	return true;
1514155ba8cSPekka Enberg }
1524155ba8cSPekka Enberg 
153*45e47970SAsias He static void virtio_blk_handle_callback(struct kvm *self, uint16_t queue_index)
154*45e47970SAsias He {
155*45e47970SAsias He 	struct virt_queue *vq;
156*45e47970SAsias He 
157*45e47970SAsias He 	vq = &blk_device.vqs[queue_index];
158*45e47970SAsias He 	while (virt_queue__available(vq)) {
159*45e47970SAsias He 		virtio_blk_do_io_request(self, vq);
160*45e47970SAsias He 	}
161*45e47970SAsias He 	kvm__irq_line(self, VIRTIO_BLK_IRQ, 1);
162*45e47970SAsias He 
163*45e47970SAsias He }
164416b2c2dSAsias He static bool virtio_blk_pci_io_out(struct kvm *self, uint16_t port, void *data, int size, uint32_t count)
165fbc2fbf9SPekka Enberg {
166fbc2fbf9SPekka Enberg 	unsigned long offset;
167fbc2fbf9SPekka Enberg 
168d1888318SCyrill Gorcunov 	offset		= port - IOPORT_VIRTIO_BLK;
169fbc2fbf9SPekka Enberg 
170fbc2fbf9SPekka Enberg 	switch (offset) {
171fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_GUEST_FEATURES:
172802e07a0SAsias He 		blk_device.guest_features	= ioport__read32(data);
173fbc2fbf9SPekka Enberg 		break;
17410eca11dSPekka Enberg 	case VIRTIO_PCI_QUEUE_PFN: {
17510eca11dSPekka Enberg 		struct virt_queue *queue;
17610eca11dSPekka Enberg 		void *p;
17710eca11dSPekka Enberg 
178*45e47970SAsias He 		queue			= &blk_device.vqs[blk_device.queue_selector];
17910eca11dSPekka Enberg 
18010eca11dSPekka Enberg 		queue->pfn		= ioport__read32(data);
18110eca11dSPekka Enberg 
18210eca11dSPekka Enberg 		p			= guest_flat_to_host(self, queue->pfn << 12);
18310eca11dSPekka Enberg 
18410eca11dSPekka Enberg 		vring_init(&queue->vring, VIRTIO_BLK_QUEUE_SIZE, p, 4096);
18510eca11dSPekka Enberg 
1867e61688eSPekka Enberg 		break;
18710eca11dSPekka Enberg 	}
188fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_QUEUE_SEL:
189802e07a0SAsias He 		blk_device.queue_selector	= ioport__read16(data);
1907e61688eSPekka Enberg 		break;
19110eca11dSPekka Enberg 	case VIRTIO_PCI_QUEUE_NOTIFY: {
19210eca11dSPekka Enberg 		uint16_t queue_index;
19310eca11dSPekka Enberg 		queue_index		= ioport__read16(data);
194*45e47970SAsias He 		virtio_blk_handle_callback(self, queue_index);
1957e61688eSPekka Enberg 		break;
19610eca11dSPekka Enberg 	}
197fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_STATUS:
198802e07a0SAsias He 		blk_device.status		= ioport__read8(data);
199fbc2fbf9SPekka Enberg 		break;
200fbc2fbf9SPekka Enberg 	case VIRTIO_MSI_CONFIG_VECTOR:
201802e07a0SAsias He 		blk_device.config_vector	= VIRTIO_MSI_NO_VECTOR;
20240ce993fSPekka Enberg 		break;
203fbc2fbf9SPekka Enberg 	case VIRTIO_MSI_QUEUE_VECTOR:
20440ce993fSPekka Enberg 		break;
205fbc2fbf9SPekka Enberg 	default:
206fbc2fbf9SPekka Enberg 		return false;
207fbc2fbf9SPekka Enberg 	};
208fbc2fbf9SPekka Enberg 
209fbc2fbf9SPekka Enberg 	return true;
210fbc2fbf9SPekka Enberg }
211fbc2fbf9SPekka Enberg 
212416b2c2dSAsias He static struct ioport_operations virtio_blk_io_ops = {
213416b2c2dSAsias He 	.io_in		= virtio_blk_pci_io_in,
214416b2c2dSAsias He 	.io_out		= virtio_blk_pci_io_out,
215fbc2fbf9SPekka Enberg };
216fbc2fbf9SPekka Enberg 
217b30d05adSPekka Enberg #define PCI_VENDOR_ID_REDHAT_QUMRANET		0x1af4
218b30d05adSPekka Enberg #define PCI_DEVICE_ID_VIRTIO_BLK		0x1001
219b30d05adSPekka Enberg #define PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET	0x1af4
220b30d05adSPekka Enberg #define PCI_SUBSYSTEM_ID_VIRTIO_BLK		0x0002
221b30d05adSPekka Enberg 
222416b2c2dSAsias He static struct pci_device_header virtio_blk_pci_device = {
223b30d05adSPekka Enberg 	.vendor_id		= PCI_VENDOR_ID_REDHAT_QUMRANET,
224b30d05adSPekka Enberg 	.device_id		= PCI_DEVICE_ID_VIRTIO_BLK,
225b30d05adSPekka Enberg 	.header_type		= PCI_HEADER_TYPE_NORMAL,
226b30d05adSPekka Enberg 	.revision_id		= 0,
227b30d05adSPekka Enberg 	.class			= 0x010000,
228b30d05adSPekka Enberg 	.subsys_vendor_id	= PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET,
229b30d05adSPekka Enberg 	.subsys_id		= PCI_SUBSYSTEM_ID_VIRTIO_BLK,
230d1888318SCyrill Gorcunov 	.bar[0]			= IOPORT_VIRTIO_BLK | PCI_BASE_ADDRESS_SPACE_IO,
231dc53a427SPekka Enberg 	.irq_pin		= 1,
2328b1ff07eSPekka Enberg 	.irq_line		= VIRTIO_BLK_IRQ,
233b30d05adSPekka Enberg };
234b30d05adSPekka Enberg 
235f05bbe8dSAsias He #define PCI_VIRTIO_BLK_DEVNUM 1
236f05bbe8dSAsias He 
237416b2c2dSAsias He void virtio_blk__init(struct kvm *self)
238b30d05adSPekka Enberg {
2391f848897SPekka Enberg 	if (!self->disk_image)
2401f848897SPekka Enberg 		return;
2411f848897SPekka Enberg 
242802e07a0SAsias He 	blk_device.blk_config.capacity = self->disk_image->size / SECTOR_SIZE;
243ca7c891bSCyrill Gorcunov 
244416b2c2dSAsias He 	pci__register(&virtio_blk_pci_device, PCI_VIRTIO_BLK_DEVNUM);
245b30d05adSPekka Enberg 
246416b2c2dSAsias He 	ioport__register(IOPORT_VIRTIO_BLK, &virtio_blk_io_ops, IOPORT_VIRTIO_BLK_SIZE);
247b30d05adSPekka Enberg }
248