xref: /kvmtool/virtio/blk.c (revision 2d1030985722e60cf57f00224bda60a334077287)
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"
12b30d05adSPekka Enberg 
1320c64ecaSPekka Enberg #include <linux/virtio_ring.h>
1420c64ecaSPekka Enberg #include <linux/virtio_blk.h>
150528c2a7SPekka Enberg 
164155ba8cSPekka Enberg #include <inttypes.h>
170528c2a7SPekka Enberg #include <pthread.h>
184155ba8cSPekka Enberg 
198b1ff07eSPekka Enberg #define VIRTIO_BLK_IRQ		14
20984b7ae0SPekka Enberg 
2110eca11dSPekka Enberg #define NUM_VIRT_QUEUES		1
2210eca11dSPekka Enberg 
2303110ff3SAsias He #define VIRTIO_BLK_QUEUE_SIZE	128
2410eca11dSPekka Enberg 
25802e07a0SAsias He struct blk_device {
260528c2a7SPekka Enberg 	pthread_mutex_t			mutex;
270528c2a7SPekka Enberg 
2840ce993fSPekka Enberg 	struct virtio_blk_config	blk_config;
29c435b91dSPekka Enberg 	uint32_t			host_features;
30fbc2fbf9SPekka Enberg 	uint32_t			guest_features;
3140ce993fSPekka Enberg 	uint16_t			config_vector;
32fbc2fbf9SPekka Enberg 	uint8_t				status;
3347bf1d0fSPekka Enberg 
3447bf1d0fSPekka Enberg 	/* virtio queue */
3547bf1d0fSPekka Enberg 	uint16_t			queue_selector;
3610eca11dSPekka Enberg 
3745e47970SAsias He 	struct virt_queue		vqs[NUM_VIRT_QUEUES];
38fbc2fbf9SPekka Enberg };
39fbc2fbf9SPekka Enberg 
4003110ff3SAsias He #define DISK_SEG_MAX	126
4140ce993fSPekka Enberg 
42802e07a0SAsias He static struct blk_device blk_device = {
430528c2a7SPekka Enberg 	.mutex			= PTHREAD_MUTEX_INITIALIZER,
440528c2a7SPekka Enberg 
4540ce993fSPekka Enberg 	.blk_config		= (struct virtio_blk_config) {
46e199f440SPekka Enberg 		/* VIRTIO_BLK_F_SEG_MAX */
4703110ff3SAsias He 		.seg_max		= DISK_SEG_MAX,
4840ce993fSPekka Enberg 	},
491ef2738dSCyrill Gorcunov 	/*
501ef2738dSCyrill Gorcunov 	 * Note we don't set VIRTIO_BLK_F_GEOMETRY here so the
511ef2738dSCyrill Gorcunov 	 * node kernel will compute disk geometry by own, the
521ef2738dSCyrill Gorcunov 	 * same applies to VIRTIO_BLK_F_BLK_SIZE
531ef2738dSCyrill Gorcunov 	 */
5403110ff3SAsias He 	.host_features		= (1UL << VIRTIO_BLK_F_SEG_MAX),
55c435b91dSPekka Enberg };
56fbc2fbf9SPekka Enberg 
57416b2c2dSAsias He static bool virtio_blk_pci_io_device_specific_in(void *data, unsigned long offset, int size, uint32_t count)
5840ce993fSPekka Enberg {
59802e07a0SAsias He 	uint8_t *config_space = (uint8_t *) &blk_device.blk_config;
6040ce993fSPekka Enberg 
6140ce993fSPekka Enberg 	if (size != 1 || count != 1)
6240ce993fSPekka Enberg 		return false;
6340ce993fSPekka Enberg 
6440ce993fSPekka Enberg 	ioport__write8(data, config_space[offset - VIRTIO_PCI_CONFIG_NOMSI]);
6540ce993fSPekka Enberg 
6640ce993fSPekka Enberg 	return true;
6740ce993fSPekka Enberg }
6840ce993fSPekka Enberg 
69416b2c2dSAsias He static bool virtio_blk_pci_io_in(struct kvm *self, uint16_t port, void *data, int size, uint32_t count)
70fbc2fbf9SPekka Enberg {
71fbc2fbf9SPekka Enberg 	unsigned long offset;
720528c2a7SPekka Enberg 	bool ret = true;
730528c2a7SPekka Enberg 
744ef0f4d6SPekka Enberg 	mutex_lock(&blk_device.mutex);
75fbc2fbf9SPekka Enberg 
76d1888318SCyrill Gorcunov 	offset		= port - IOPORT_VIRTIO_BLK;
77fbc2fbf9SPekka Enberg 
78fbc2fbf9SPekka Enberg 	switch (offset) {
79fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_HOST_FEATURES:
80802e07a0SAsias He 		ioport__write32(data, blk_device.host_features);
81fbc2fbf9SPekka Enberg 		break;
82fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_GUEST_FEATURES:
830528c2a7SPekka Enberg 		ret		= false;
849ee67e60SAsias He 		break;
85fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_QUEUE_PFN:
8645e47970SAsias He 		ioport__write32(data, blk_device.vqs[blk_device.queue_selector].pfn);
878b1ff07eSPekka Enberg 		break;
88fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_QUEUE_NUM:
8910eca11dSPekka Enberg 		ioport__write16(data, VIRTIO_BLK_QUEUE_SIZE);
908b1ff07eSPekka Enberg 		break;
91fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_QUEUE_SEL:
92fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_QUEUE_NOTIFY:
930528c2a7SPekka Enberg 		ret		= false;
949ee67e60SAsias He 		break;
95fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_STATUS:
96802e07a0SAsias He 		ioport__write8(data, blk_device.status);
97fbc2fbf9SPekka Enberg 		break;
98fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_ISR:
998b1ff07eSPekka Enberg 		ioport__write8(data, 0x1);
1008b1ff07eSPekka Enberg 		kvm__irq_line(self, VIRTIO_BLK_IRQ, 0);
1017e61688eSPekka Enberg 		break;
102fbc2fbf9SPekka Enberg 	case VIRTIO_MSI_CONFIG_VECTOR:
103802e07a0SAsias He 		ioport__write16(data, blk_device.config_vector);
10440ce993fSPekka Enberg 		break;
105fbc2fbf9SPekka Enberg 	default:
1060528c2a7SPekka Enberg 		ret		= virtio_blk_pci_io_device_specific_in(data, offset, size, count);
107fbc2fbf9SPekka Enberg 	};
108fbc2fbf9SPekka Enberg 
1094ef0f4d6SPekka Enberg 	mutex_unlock(&blk_device.mutex);
1100528c2a7SPekka Enberg 
1110528c2a7SPekka Enberg 	return ret;
112fbc2fbf9SPekka Enberg }
113fbc2fbf9SPekka Enberg 
11445e47970SAsias He static bool virtio_blk_do_io_request(struct kvm *self, struct virt_queue *queue)
1154155ba8cSPekka Enberg {
11645e47970SAsias He 	struct iovec iov[VIRTIO_BLK_QUEUE_SIZE];
1174155ba8cSPekka Enberg 	struct virtio_blk_outhdr *req;
11870b53f25SSasha Levin 	ssize_t block_cnt = -1;
11945e47970SAsias He 	uint16_t out, in, head;
1204155ba8cSPekka Enberg 	uint8_t *status;
1214155ba8cSPekka Enberg 
12245e47970SAsias He 	head		= virt_queue__get_iov(queue, iov, &out, &in, self);
1234155ba8cSPekka Enberg 
12445e47970SAsias He 	/* head */
12545e47970SAsias He 	req		= iov[0].iov_base;
12603110ff3SAsias He 
127258dd093SPekka Enberg 	switch (req->type) {
12803110ff3SAsias He 	case VIRTIO_BLK_T_IN:
129*2d103098SSasha Levin 		block_cnt = disk_image__read_sector_iov(self->disk_image, req->sector, iov + 1, in + out - 2);
13070b53f25SSasha Levin 
131258dd093SPekka Enberg 		break;
13203110ff3SAsias He 	case VIRTIO_BLK_T_OUT:
133*2d103098SSasha Levin 		block_cnt = disk_image__write_sector_iov(self->disk_image, req->sector, iov + 1, in + out - 2);
13470b53f25SSasha Levin 
135258dd093SPekka Enberg 		break;
13670b53f25SSasha Levin 
137258dd093SPekka Enberg 	default:
1384155ba8cSPekka Enberg 		warning("request type %d", req->type);
13970b53f25SSasha Levin 		block_cnt = -1;
14003110ff3SAsias He 	}
14103110ff3SAsias He 
14245e47970SAsias He 	/* status */
14345e47970SAsias He 	status			= iov[out + in - 1].iov_base;
14470b53f25SSasha Levin 	*status			= (block_cnt < 0) ? VIRTIO_BLK_S_IOERR : VIRTIO_BLK_S_OK;
14503110ff3SAsias He 
14645e47970SAsias He 	virt_queue__set_used_elem(queue, head, block_cnt);
1474155ba8cSPekka Enberg 
1484155ba8cSPekka Enberg 	return true;
1494155ba8cSPekka Enberg }
1504155ba8cSPekka Enberg 
15145e47970SAsias He static void virtio_blk_handle_callback(struct kvm *self, uint16_t queue_index)
15245e47970SAsias He {
1530ea58e5bSPekka Enberg 	struct virt_queue *vq = &blk_device.vqs[queue_index];
15445e47970SAsias He 
1550ea58e5bSPekka Enberg 	while (virt_queue__available(vq))
15645e47970SAsias He 		virtio_blk_do_io_request(self, vq);
1570ea58e5bSPekka Enberg 
15845e47970SAsias He 	kvm__irq_line(self, VIRTIO_BLK_IRQ, 1);
15945e47970SAsias He 
16045e47970SAsias He }
1610528c2a7SPekka Enberg 
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;
1650528c2a7SPekka Enberg 	bool ret = true;
1660528c2a7SPekka Enberg 
1674ef0f4d6SPekka Enberg 	mutex_lock(&blk_device.mutex);
168fbc2fbf9SPekka Enberg 
169d1888318SCyrill Gorcunov 	offset		= port - IOPORT_VIRTIO_BLK;
170fbc2fbf9SPekka Enberg 
171fbc2fbf9SPekka Enberg 	switch (offset) {
172fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_GUEST_FEATURES:
173802e07a0SAsias He 		blk_device.guest_features	= ioport__read32(data);
174fbc2fbf9SPekka Enberg 		break;
17510eca11dSPekka Enberg 	case VIRTIO_PCI_QUEUE_PFN: {
17610eca11dSPekka Enberg 		struct virt_queue *queue;
17710eca11dSPekka Enberg 		void *p;
17810eca11dSPekka Enberg 
17945e47970SAsias He 		queue			= &blk_device.vqs[blk_device.queue_selector];
18010eca11dSPekka Enberg 
18110eca11dSPekka Enberg 		queue->pfn		= ioport__read32(data);
18210eca11dSPekka Enberg 
18310eca11dSPekka Enberg 		p			= guest_flat_to_host(self, queue->pfn << 12);
18410eca11dSPekka Enberg 
18510eca11dSPekka Enberg 		vring_init(&queue->vring, VIRTIO_BLK_QUEUE_SIZE, p, 4096);
18610eca11dSPekka Enberg 
1877e61688eSPekka Enberg 		break;
18810eca11dSPekka Enberg 	}
189fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_QUEUE_SEL:
190802e07a0SAsias He 		blk_device.queue_selector	= ioport__read16(data);
1917e61688eSPekka Enberg 		break;
19210eca11dSPekka Enberg 	case VIRTIO_PCI_QUEUE_NOTIFY: {
19310eca11dSPekka Enberg 		uint16_t queue_index;
19410eca11dSPekka Enberg 		queue_index		= ioport__read16(data);
19545e47970SAsias He 		virtio_blk_handle_callback(self, queue_index);
1967e61688eSPekka Enberg 		break;
19710eca11dSPekka Enberg 	}
198fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_STATUS:
199802e07a0SAsias He 		blk_device.status		= ioport__read8(data);
200fbc2fbf9SPekka Enberg 		break;
201fbc2fbf9SPekka Enberg 	case VIRTIO_MSI_CONFIG_VECTOR:
202802e07a0SAsias He 		blk_device.config_vector	= VIRTIO_MSI_NO_VECTOR;
20340ce993fSPekka Enberg 		break;
204fbc2fbf9SPekka Enberg 	case VIRTIO_MSI_QUEUE_VECTOR:
20540ce993fSPekka Enberg 		break;
206fbc2fbf9SPekka Enberg 	default:
2070528c2a7SPekka Enberg 		ret		= false;
208fbc2fbf9SPekka Enberg 	};
209fbc2fbf9SPekka Enberg 
2104ef0f4d6SPekka Enberg 	mutex_unlock(&blk_device.mutex);
2110528c2a7SPekka Enberg 
2120528c2a7SPekka Enberg 	return ret;
213fbc2fbf9SPekka Enberg }
214fbc2fbf9SPekka Enberg 
215416b2c2dSAsias He static struct ioport_operations virtio_blk_io_ops = {
216416b2c2dSAsias He 	.io_in		= virtio_blk_pci_io_in,
217416b2c2dSAsias He 	.io_out		= virtio_blk_pci_io_out,
218fbc2fbf9SPekka Enberg };
219fbc2fbf9SPekka Enberg 
220b30d05adSPekka Enberg #define PCI_VENDOR_ID_REDHAT_QUMRANET		0x1af4
221b30d05adSPekka Enberg #define PCI_DEVICE_ID_VIRTIO_BLK		0x1001
222b30d05adSPekka Enberg #define PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET	0x1af4
223b30d05adSPekka Enberg #define PCI_SUBSYSTEM_ID_VIRTIO_BLK		0x0002
224b30d05adSPekka Enberg 
225416b2c2dSAsias He static struct pci_device_header virtio_blk_pci_device = {
226b30d05adSPekka Enberg 	.vendor_id		= PCI_VENDOR_ID_REDHAT_QUMRANET,
227b30d05adSPekka Enberg 	.device_id		= PCI_DEVICE_ID_VIRTIO_BLK,
228b30d05adSPekka Enberg 	.header_type		= PCI_HEADER_TYPE_NORMAL,
229b30d05adSPekka Enberg 	.revision_id		= 0,
230b30d05adSPekka Enberg 	.class			= 0x010000,
231b30d05adSPekka Enberg 	.subsys_vendor_id	= PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET,
232b30d05adSPekka Enberg 	.subsys_id		= PCI_SUBSYSTEM_ID_VIRTIO_BLK,
233d1888318SCyrill Gorcunov 	.bar[0]			= IOPORT_VIRTIO_BLK | PCI_BASE_ADDRESS_SPACE_IO,
234dc53a427SPekka Enberg 	.irq_pin		= 1,
2358b1ff07eSPekka Enberg 	.irq_line		= VIRTIO_BLK_IRQ,
236b30d05adSPekka Enberg };
237b30d05adSPekka Enberg 
238f05bbe8dSAsias He #define PCI_VIRTIO_BLK_DEVNUM 1
239f05bbe8dSAsias He 
240416b2c2dSAsias He void virtio_blk__init(struct kvm *self)
241b30d05adSPekka Enberg {
2421f848897SPekka Enberg 	if (!self->disk_image)
2431f848897SPekka Enberg 		return;
2441f848897SPekka Enberg 
245802e07a0SAsias He 	blk_device.blk_config.capacity = self->disk_image->size / SECTOR_SIZE;
246ca7c891bSCyrill Gorcunov 
247416b2c2dSAsias He 	pci__register(&virtio_blk_pci_device, PCI_VIRTIO_BLK_DEVNUM);
248b30d05adSPekka Enberg 
249416b2c2dSAsias He 	ioport__register(IOPORT_VIRTIO_BLK, &virtio_blk_io_ops, IOPORT_VIRTIO_BLK_SIZE);
250b30d05adSPekka Enberg }
251