xref: /kvmtool/virtio/blk.c (revision 4ef0f4d620fa5df6d61ff6d4fff7f03680c7e73e)
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"
8*4ef0f4d6SPekka 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 
74*4ef0f4d6SPekka 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;
840528c2a7SPekka Enberg 		goto out_unlock;
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;
940528c2a7SPekka Enberg 		goto out_unlock;
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);
1070528c2a7SPekka Enberg 		goto out_unlock;
108fbc2fbf9SPekka Enberg 	};
109fbc2fbf9SPekka Enberg 
1100528c2a7SPekka Enberg out_unlock:
111*4ef0f4d6SPekka Enberg 	mutex_unlock(&blk_device.mutex);
1120528c2a7SPekka Enberg 
1130528c2a7SPekka Enberg 	return ret;
114fbc2fbf9SPekka Enberg }
115fbc2fbf9SPekka Enberg 
11645e47970SAsias He static bool virtio_blk_do_io_request(struct kvm *self, struct virt_queue *queue)
1174155ba8cSPekka Enberg {
11845e47970SAsias He 	struct iovec iov[VIRTIO_BLK_QUEUE_SIZE];
1194155ba8cSPekka Enberg 	struct virtio_blk_outhdr *req;
12045e47970SAsias He 	uint32_t block_len, block_cnt;
12145e47970SAsias He 	uint16_t out, in, head;
1224155ba8cSPekka Enberg 	uint8_t *status;
123f0952e64SPekka Enberg 	bool io_error;
124258dd093SPekka Enberg 	void *block;
125f0952e64SPekka Enberg 	int err, i;
126f0952e64SPekka Enberg 
127f0952e64SPekka Enberg 	io_error	= false;
1284155ba8cSPekka Enberg 
12945e47970SAsias He 	head		= virt_queue__get_iov(queue, iov, &out, &in, self);
1304155ba8cSPekka Enberg 
13145e47970SAsias He 	/* head */
13245e47970SAsias He 	req		= iov[0].iov_base;
13303110ff3SAsias He 
1344155ba8cSPekka Enberg 	/* block */
13503110ff3SAsias He 	block_cnt	= 0;
13603110ff3SAsias He 
13745e47970SAsias He 	for (i = 1; i < out + in - 1; i++) {
13845e47970SAsias He 		block		= iov[i].iov_base;
13945e47970SAsias He 		block_len	= iov[i].iov_len;
1404155ba8cSPekka Enberg 
141258dd093SPekka Enberg 		switch (req->type) {
14203110ff3SAsias He 		case VIRTIO_BLK_T_IN:
143258dd093SPekka Enberg 			err	= disk_image__read_sector(self->disk_image, req->sector, block, block_len);
144258dd093SPekka Enberg 			break;
14503110ff3SAsias He 		case VIRTIO_BLK_T_OUT:
146258dd093SPekka Enberg 			err	= disk_image__write_sector(self->disk_image, req->sector, block, block_len);
147258dd093SPekka Enberg 			break;
148258dd093SPekka Enberg 		default:
1494155ba8cSPekka Enberg 			warning("request type %d", req->type);
150f0952e64SPekka Enberg 			io_error	= true;
151a2c8c696SAsias He 		}
152a2c8c696SAsias He 
15303110ff3SAsias He 		req->sector	+= block_len >> SECTOR_SHIFT;
15403110ff3SAsias He 		block_cnt	+= block_len;
15503110ff3SAsias He 	}
15603110ff3SAsias He 
15745e47970SAsias He 	/* status */
15845e47970SAsias He 	status			= iov[out + in - 1].iov_base;
159f0952e64SPekka Enberg 	*status			= io_error ? VIRTIO_BLK_S_IOERR : VIRTIO_BLK_S_OK;
16003110ff3SAsias He 
16145e47970SAsias He 	virt_queue__set_used_elem(queue, head, block_cnt);
1624155ba8cSPekka Enberg 
1634155ba8cSPekka Enberg 	return true;
1644155ba8cSPekka Enberg }
1654155ba8cSPekka Enberg 
16645e47970SAsias He static void virtio_blk_handle_callback(struct kvm *self, uint16_t queue_index)
16745e47970SAsias He {
1680ea58e5bSPekka Enberg 	struct virt_queue *vq = &blk_device.vqs[queue_index];
16945e47970SAsias He 
1700ea58e5bSPekka Enberg 	while (virt_queue__available(vq))
17145e47970SAsias He 		virtio_blk_do_io_request(self, vq);
1720ea58e5bSPekka Enberg 
17345e47970SAsias He 	kvm__irq_line(self, VIRTIO_BLK_IRQ, 1);
17445e47970SAsias He 
17545e47970SAsias He }
1760528c2a7SPekka Enberg 
177416b2c2dSAsias He static bool virtio_blk_pci_io_out(struct kvm *self, uint16_t port, void *data, int size, uint32_t count)
178fbc2fbf9SPekka Enberg {
179fbc2fbf9SPekka Enberg 	unsigned long offset;
1800528c2a7SPekka Enberg 	bool ret = true;
1810528c2a7SPekka Enberg 
182*4ef0f4d6SPekka Enberg 	mutex_lock(&blk_device.mutex);
183fbc2fbf9SPekka Enberg 
184d1888318SCyrill Gorcunov 	offset		= port - IOPORT_VIRTIO_BLK;
185fbc2fbf9SPekka Enberg 
186fbc2fbf9SPekka Enberg 	switch (offset) {
187fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_GUEST_FEATURES:
188802e07a0SAsias He 		blk_device.guest_features	= ioport__read32(data);
189fbc2fbf9SPekka Enberg 		break;
19010eca11dSPekka Enberg 	case VIRTIO_PCI_QUEUE_PFN: {
19110eca11dSPekka Enberg 		struct virt_queue *queue;
19210eca11dSPekka Enberg 		void *p;
19310eca11dSPekka Enberg 
19445e47970SAsias He 		queue			= &blk_device.vqs[blk_device.queue_selector];
19510eca11dSPekka Enberg 
19610eca11dSPekka Enberg 		queue->pfn		= ioport__read32(data);
19710eca11dSPekka Enberg 
19810eca11dSPekka Enberg 		p			= guest_flat_to_host(self, queue->pfn << 12);
19910eca11dSPekka Enberg 
20010eca11dSPekka Enberg 		vring_init(&queue->vring, VIRTIO_BLK_QUEUE_SIZE, p, 4096);
20110eca11dSPekka Enberg 
2027e61688eSPekka Enberg 		break;
20310eca11dSPekka Enberg 	}
204fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_QUEUE_SEL:
205802e07a0SAsias He 		blk_device.queue_selector	= ioport__read16(data);
2067e61688eSPekka Enberg 		break;
20710eca11dSPekka Enberg 	case VIRTIO_PCI_QUEUE_NOTIFY: {
20810eca11dSPekka Enberg 		uint16_t queue_index;
20910eca11dSPekka Enberg 		queue_index		= ioport__read16(data);
21045e47970SAsias He 		virtio_blk_handle_callback(self, queue_index);
2117e61688eSPekka Enberg 		break;
21210eca11dSPekka Enberg 	}
213fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_STATUS:
214802e07a0SAsias He 		blk_device.status		= ioport__read8(data);
215fbc2fbf9SPekka Enberg 		break;
216fbc2fbf9SPekka Enberg 	case VIRTIO_MSI_CONFIG_VECTOR:
217802e07a0SAsias He 		blk_device.config_vector	= VIRTIO_MSI_NO_VECTOR;
21840ce993fSPekka Enberg 		break;
219fbc2fbf9SPekka Enberg 	case VIRTIO_MSI_QUEUE_VECTOR:
22040ce993fSPekka Enberg 		break;
221fbc2fbf9SPekka Enberg 	default:
2220528c2a7SPekka Enberg 		ret		= false;
2230528c2a7SPekka Enberg 		goto out_unlock;
224fbc2fbf9SPekka Enberg 	};
225fbc2fbf9SPekka Enberg 
2260528c2a7SPekka Enberg out_unlock:
227*4ef0f4d6SPekka Enberg 	mutex_unlock(&blk_device.mutex);
2280528c2a7SPekka Enberg 
2290528c2a7SPekka Enberg 	return ret;
230fbc2fbf9SPekka Enberg }
231fbc2fbf9SPekka Enberg 
232416b2c2dSAsias He static struct ioport_operations virtio_blk_io_ops = {
233416b2c2dSAsias He 	.io_in		= virtio_blk_pci_io_in,
234416b2c2dSAsias He 	.io_out		= virtio_blk_pci_io_out,
235fbc2fbf9SPekka Enberg };
236fbc2fbf9SPekka Enberg 
237b30d05adSPekka Enberg #define PCI_VENDOR_ID_REDHAT_QUMRANET		0x1af4
238b30d05adSPekka Enberg #define PCI_DEVICE_ID_VIRTIO_BLK		0x1001
239b30d05adSPekka Enberg #define PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET	0x1af4
240b30d05adSPekka Enberg #define PCI_SUBSYSTEM_ID_VIRTIO_BLK		0x0002
241b30d05adSPekka Enberg 
242416b2c2dSAsias He static struct pci_device_header virtio_blk_pci_device = {
243b30d05adSPekka Enberg 	.vendor_id		= PCI_VENDOR_ID_REDHAT_QUMRANET,
244b30d05adSPekka Enberg 	.device_id		= PCI_DEVICE_ID_VIRTIO_BLK,
245b30d05adSPekka Enberg 	.header_type		= PCI_HEADER_TYPE_NORMAL,
246b30d05adSPekka Enberg 	.revision_id		= 0,
247b30d05adSPekka Enberg 	.class			= 0x010000,
248b30d05adSPekka Enberg 	.subsys_vendor_id	= PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET,
249b30d05adSPekka Enberg 	.subsys_id		= PCI_SUBSYSTEM_ID_VIRTIO_BLK,
250d1888318SCyrill Gorcunov 	.bar[0]			= IOPORT_VIRTIO_BLK | PCI_BASE_ADDRESS_SPACE_IO,
251dc53a427SPekka Enberg 	.irq_pin		= 1,
2528b1ff07eSPekka Enberg 	.irq_line		= VIRTIO_BLK_IRQ,
253b30d05adSPekka Enberg };
254b30d05adSPekka Enberg 
255f05bbe8dSAsias He #define PCI_VIRTIO_BLK_DEVNUM 1
256f05bbe8dSAsias He 
257416b2c2dSAsias He void virtio_blk__init(struct kvm *self)
258b30d05adSPekka Enberg {
2591f848897SPekka Enberg 	if (!self->disk_image)
2601f848897SPekka Enberg 		return;
2611f848897SPekka Enberg 
262802e07a0SAsias He 	blk_device.blk_config.capacity = self->disk_image->size / SECTOR_SIZE;
263ca7c891bSCyrill Gorcunov 
264416b2c2dSAsias He 	pci__register(&virtio_blk_pci_device, PCI_VIRTIO_BLK_DEVNUM);
265b30d05adSPekka Enberg 
266416b2c2dSAsias He 	ioport__register(IOPORT_VIRTIO_BLK, &virtio_blk_io_ops, IOPORT_VIRTIO_BLK_SIZE);
267b30d05adSPekka Enberg }
268