xref: /kvmtool/virtio/blk.c (revision 4155ba8cda055b7831489e4c4a412b073493115b)
1b30d05adSPekka Enberg #include "kvm/blk-virtio.h"
2b30d05adSPekka Enberg 
310eca11dSPekka Enberg #include "kvm/virtio_ring.h"
4c435b91dSPekka Enberg #include "kvm/virtio_blk.h"
5984b7ae0SPekka Enberg #include "kvm/virtio_pci.h"
65a24a9f2SPekka Enberg #include "kvm/disk-image.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 
12*4155ba8cSPekka Enberg #include <inttypes.h>
13*4155ba8cSPekka Enberg #include <assert.h>
14*4155ba8cSPekka Enberg 
158b1ff07eSPekka Enberg #define VIRTIO_BLK_IRQ		14
16984b7ae0SPekka Enberg 
1710eca11dSPekka Enberg #define NUM_VIRT_QUEUES		1
1810eca11dSPekka Enberg 
1910eca11dSPekka Enberg #define VIRTIO_BLK_QUEUE_SIZE	16
2010eca11dSPekka Enberg 
2110eca11dSPekka Enberg struct virt_queue {
2210eca11dSPekka Enberg 	struct vring			vring;
23fe99fd4eSPekka Enberg 	uint32_t			pfn;
2493d18b72SPekka Enberg 	/* The last_avail_idx field is an index to ->ring of struct vring_avail.
25fe99fd4eSPekka Enberg 	   It's where we assume the next request index is at.  */
2693d18b72SPekka Enberg 	uint16_t			last_avail_idx;
2710eca11dSPekka Enberg };
2810eca11dSPekka Enberg 
29fbc2fbf9SPekka Enberg struct device {
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 
3910eca11dSPekka Enberg 	struct virt_queue		virt_queues[NUM_VIRT_QUEUES];
40fbc2fbf9SPekka Enberg };
41fbc2fbf9SPekka Enberg 
4240ce993fSPekka Enberg #define DISK_CYLINDERS	1024
4340ce993fSPekka Enberg #define DISK_HEADS	64
4440ce993fSPekka Enberg #define DISK_SECTORS	32
4540ce993fSPekka Enberg 
46c435b91dSPekka Enberg static struct device device = {
4740ce993fSPekka Enberg 	.blk_config		= (struct virtio_blk_config) {
4840ce993fSPekka Enberg 		.capacity		= DISK_CYLINDERS * DISK_HEADS * DISK_SECTORS,
4940ce993fSPekka Enberg 		/* VIRTIO_BLK_F_GEOMETRY */
5040ce993fSPekka Enberg 		.geometry		= {
5140ce993fSPekka Enberg 			.cylinders		= DISK_CYLINDERS,
5240ce993fSPekka Enberg 			.heads			= DISK_HEADS,
5340ce993fSPekka Enberg 			.sectors		= DISK_SECTORS,
5440ce993fSPekka Enberg 		},
5540ce993fSPekka Enberg 		/* VIRTIO_BLK_SIZE */
5640ce993fSPekka Enberg 		.blk_size		= 4096,
5740ce993fSPekka Enberg 	},
581ef2738dSCyrill Gorcunov 	/*
591ef2738dSCyrill Gorcunov 	 * Note we don't set VIRTIO_BLK_F_GEOMETRY here so the
601ef2738dSCyrill Gorcunov 	 * node kernel will compute disk geometry by own, the
611ef2738dSCyrill Gorcunov 	 * same applies to VIRTIO_BLK_F_BLK_SIZE
621ef2738dSCyrill Gorcunov 	 */
631ef2738dSCyrill Gorcunov 	.host_features		= (1UL << VIRTIO_BLK_F_RO),
64c435b91dSPekka Enberg };
65fbc2fbf9SPekka Enberg 
6640ce993fSPekka Enberg static bool virtio_blk_config_in(void *data, unsigned long offset, int size, uint32_t count)
6740ce993fSPekka Enberg {
6840ce993fSPekka Enberg 	uint8_t *config_space = (uint8_t *) &device.blk_config;
6940ce993fSPekka Enberg 
7040ce993fSPekka Enberg 	if (size != 1 || count != 1)
7140ce993fSPekka Enberg 		return false;
7240ce993fSPekka Enberg 
7340ce993fSPekka Enberg 	ioport__write8(data, config_space[offset - VIRTIO_PCI_CONFIG_NOMSI]);
7440ce993fSPekka Enberg 
7540ce993fSPekka Enberg 	return true;
7640ce993fSPekka Enberg }
7740ce993fSPekka Enberg 
78fbc2fbf9SPekka Enberg static bool blk_virtio_in(struct kvm *self, uint16_t port, void *data, int size, uint32_t count)
79fbc2fbf9SPekka Enberg {
80fbc2fbf9SPekka Enberg 	unsigned long offset;
81fbc2fbf9SPekka Enberg 
82fbc2fbf9SPekka Enberg 	offset		= port - IOPORT_VIRTIO;
83fbc2fbf9SPekka Enberg 
84fbc2fbf9SPekka Enberg 	switch (offset) {
85fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_HOST_FEATURES:
86c435b91dSPekka Enberg 		ioport__write32(data, device.host_features);
87fbc2fbf9SPekka Enberg 		break;
88fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_GUEST_FEATURES:
898b1ff07eSPekka Enberg 		return false;
90fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_QUEUE_PFN:
9110eca11dSPekka Enberg 		ioport__write32(data, device.virt_queues[device.queue_selector].pfn);
928b1ff07eSPekka Enberg 		break;
93fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_QUEUE_NUM:
9410eca11dSPekka Enberg 		ioport__write16(data, VIRTIO_BLK_QUEUE_SIZE);
958b1ff07eSPekka Enberg 		break;
96fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_QUEUE_SEL:
97fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_QUEUE_NOTIFY:
98fbc2fbf9SPekka Enberg 		return false;
99fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_STATUS:
100fbc2fbf9SPekka Enberg 		ioport__write8(data, 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:
10740ce993fSPekka Enberg 		ioport__write16(data, device.config_vector);
10840ce993fSPekka Enberg 		break;
109fbc2fbf9SPekka Enberg 	default:
11040ce993fSPekka Enberg 		return virtio_blk_config_in(data, offset, size, count);
111fbc2fbf9SPekka Enberg 	};
112fbc2fbf9SPekka Enberg 
113fbc2fbf9SPekka Enberg 	return true;
114fbc2fbf9SPekka Enberg }
115fbc2fbf9SPekka Enberg 
116*4155ba8cSPekka Enberg static bool blk_virtio_read(struct kvm *self, struct virt_queue *queue)
117*4155ba8cSPekka Enberg {
118*4155ba8cSPekka Enberg 	struct vring_used_elem *used_elem;
119*4155ba8cSPekka Enberg 	struct virtio_blk_outhdr *req;
120*4155ba8cSPekka Enberg 	struct vring_desc *desc;
121*4155ba8cSPekka Enberg 	uint16_t desc_ndx;
122*4155ba8cSPekka Enberg 	uint32_t dst_len;
123*4155ba8cSPekka Enberg 	uint8_t *status;
124*4155ba8cSPekka Enberg 	void *dst;
125*4155ba8cSPekka Enberg 	int err;
126*4155ba8cSPekka Enberg 
127*4155ba8cSPekka Enberg 	desc_ndx		= queue->vring.avail->ring[queue->last_avail_idx++ % queue->vring.num];
128*4155ba8cSPekka Enberg 
129*4155ba8cSPekka Enberg 	if (desc_ndx >= queue->vring.num) {
130*4155ba8cSPekka Enberg 		warning("fatal I/O error");
131*4155ba8cSPekka Enberg 		return false;
132*4155ba8cSPekka Enberg 	}
133*4155ba8cSPekka Enberg 
134*4155ba8cSPekka Enberg 	/* header */
135*4155ba8cSPekka Enberg 	desc			= &queue->vring.desc[desc_ndx];
136*4155ba8cSPekka Enberg 	assert(!(desc->flags & VRING_DESC_F_INDIRECT));
137*4155ba8cSPekka Enberg 
138*4155ba8cSPekka Enberg 	req			= guest_flat_to_host(self, desc->addr);
139*4155ba8cSPekka Enberg 
140*4155ba8cSPekka Enberg 	/* block */
141*4155ba8cSPekka Enberg 	desc			= &queue->vring.desc[desc->next];
142*4155ba8cSPekka Enberg 	assert(!(desc->flags & VRING_DESC_F_INDIRECT));
143*4155ba8cSPekka Enberg 
144*4155ba8cSPekka Enberg 	dst			= guest_flat_to_host(self, desc->addr);
145*4155ba8cSPekka Enberg 	dst_len			= desc->len;
146*4155ba8cSPekka Enberg 
147*4155ba8cSPekka Enberg 	/* status */
148*4155ba8cSPekka Enberg 	desc			= &queue->vring.desc[desc->next];
149*4155ba8cSPekka Enberg 	assert(!(desc->flags & VRING_DESC_F_INDIRECT));
150*4155ba8cSPekka Enberg 
151*4155ba8cSPekka Enberg 	status			= guest_flat_to_host(self, desc->addr);
152*4155ba8cSPekka Enberg 
153*4155ba8cSPekka Enberg 	info("reading sector %" PRIu64 " (%d bytes)", req->sector, dst_len);
154*4155ba8cSPekka Enberg 
155*4155ba8cSPekka Enberg 	if (req->type == VIRTIO_BLK_T_IN) {
156*4155ba8cSPekka Enberg 		err = disk_image__read_sector(self->disk_image, req->sector, dst, dst_len);
157*4155ba8cSPekka Enberg 
158*4155ba8cSPekka Enberg 		if (err)
159*4155ba8cSPekka Enberg 			*status			= VIRTIO_BLK_S_IOERR;
160*4155ba8cSPekka Enberg 		else
161*4155ba8cSPekka Enberg 			*status			= VIRTIO_BLK_S_OK;
162*4155ba8cSPekka Enberg 	} else {
163*4155ba8cSPekka Enberg 		warning("request type %d", req->type);
164*4155ba8cSPekka Enberg 		*status			= VIRTIO_BLK_S_IOERR;
165*4155ba8cSPekka Enberg 	}
166*4155ba8cSPekka Enberg 
167*4155ba8cSPekka Enberg 	used_elem		= &queue->vring.used->ring[queue->vring.used->idx++ % queue->vring.num];
168*4155ba8cSPekka Enberg 
169*4155ba8cSPekka Enberg 	used_elem->id		= desc_ndx;
170*4155ba8cSPekka Enberg 	used_elem->len		= 3;
171*4155ba8cSPekka Enberg 
172*4155ba8cSPekka Enberg 	return true;
173*4155ba8cSPekka Enberg }
174*4155ba8cSPekka Enberg 
175fbc2fbf9SPekka Enberg static bool blk_virtio_out(struct kvm *self, uint16_t port, void *data, int size, uint32_t count)
176fbc2fbf9SPekka Enberg {
177fbc2fbf9SPekka Enberg 	unsigned long offset;
178fbc2fbf9SPekka Enberg 
179fbc2fbf9SPekka Enberg 	offset		= port - IOPORT_VIRTIO;
180fbc2fbf9SPekka Enberg 
181fbc2fbf9SPekka Enberg 	switch (offset) {
182fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_GUEST_FEATURES:
183fbc2fbf9SPekka Enberg 		device.guest_features	= ioport__read32(data);
184fbc2fbf9SPekka Enberg 		break;
18510eca11dSPekka Enberg 	case VIRTIO_PCI_QUEUE_PFN: {
18610eca11dSPekka Enberg 		struct virt_queue *queue;
18710eca11dSPekka Enberg 		void *p;
18810eca11dSPekka Enberg 
18910eca11dSPekka Enberg 		queue			= &device.virt_queues[device.queue_selector];
19010eca11dSPekka Enberg 
19110eca11dSPekka Enberg 		queue->pfn		= ioport__read32(data);
19210eca11dSPekka Enberg 
19310eca11dSPekka Enberg 		p			= guest_flat_to_host(self, queue->pfn << 12);
19410eca11dSPekka Enberg 
19510eca11dSPekka Enberg 		vring_init(&queue->vring, VIRTIO_BLK_QUEUE_SIZE, p, 4096);
19610eca11dSPekka Enberg 
1977e61688eSPekka Enberg 		break;
19810eca11dSPekka Enberg 	}
199fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_QUEUE_SEL:
20047bf1d0fSPekka Enberg 		device.queue_selector	= ioport__read16(data);
2017e61688eSPekka Enberg 		break;
20210eca11dSPekka Enberg 	case VIRTIO_PCI_QUEUE_NOTIFY: {
20310eca11dSPekka Enberg 		struct virt_queue *queue;
20410eca11dSPekka Enberg 		uint16_t queue_index;
20510eca11dSPekka Enberg 
20610eca11dSPekka Enberg 		queue_index		= ioport__read16(data);
20710eca11dSPekka Enberg 
20810eca11dSPekka Enberg 		queue			= &device.virt_queues[queue_index];
20910eca11dSPekka Enberg 
210*4155ba8cSPekka Enberg 		while (queue->vring.avail->idx != queue->last_avail_idx) {
211*4155ba8cSPekka Enberg 			if (!blk_virtio_read(self, queue))
212*4155ba8cSPekka Enberg 				return false;
21393d18b72SPekka Enberg 		}
2148b1ff07eSPekka Enberg 		kvm__irq_line(self, VIRTIO_BLK_IRQ, 1);
2155a24a9f2SPekka Enberg 
2167e61688eSPekka Enberg 		break;
21710eca11dSPekka Enberg 	}
218fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_STATUS:
219fbc2fbf9SPekka Enberg 		device.status		= ioport__read8(data);
220fbc2fbf9SPekka Enberg 		break;
221fbc2fbf9SPekka Enberg 	case VIRTIO_MSI_CONFIG_VECTOR:
22240ce993fSPekka Enberg 		device.config_vector	= VIRTIO_MSI_NO_VECTOR;
22340ce993fSPekka Enberg 		break;
224fbc2fbf9SPekka Enberg 	case VIRTIO_MSI_QUEUE_VECTOR:
22540ce993fSPekka Enberg 		break;
226fbc2fbf9SPekka Enberg 	default:
227fbc2fbf9SPekka Enberg 		return false;
228fbc2fbf9SPekka Enberg 	};
229fbc2fbf9SPekka Enberg 
230fbc2fbf9SPekka Enberg 	return true;
231fbc2fbf9SPekka Enberg }
232fbc2fbf9SPekka Enberg 
233fbc2fbf9SPekka Enberg static struct ioport_operations blk_virtio_io_ops = {
234fbc2fbf9SPekka Enberg 	.io_in		= blk_virtio_in,
235fbc2fbf9SPekka Enberg 	.io_out		= blk_virtio_out,
236fbc2fbf9SPekka Enberg };
237fbc2fbf9SPekka Enberg 
238b30d05adSPekka Enberg #define PCI_VENDOR_ID_REDHAT_QUMRANET		0x1af4
239b30d05adSPekka Enberg #define PCI_DEVICE_ID_VIRTIO_BLK		0x1001
240b30d05adSPekka Enberg #define PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET	0x1af4
241b30d05adSPekka Enberg #define PCI_SUBSYSTEM_ID_VIRTIO_BLK		0x0002
242b30d05adSPekka Enberg 
243fbc2fbf9SPekka Enberg static struct pci_device_header blk_virtio_pci_device = {
244b30d05adSPekka Enberg 	.vendor_id		= PCI_VENDOR_ID_REDHAT_QUMRANET,
245b30d05adSPekka Enberg 	.device_id		= PCI_DEVICE_ID_VIRTIO_BLK,
246b30d05adSPekka Enberg 	.header_type		= PCI_HEADER_TYPE_NORMAL,
247b30d05adSPekka Enberg 	.revision_id		= 0,
248b30d05adSPekka Enberg 	.class			= 0x010000,
249b30d05adSPekka Enberg 	.subsys_vendor_id	= PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET,
250b30d05adSPekka Enberg 	.subsys_id		= PCI_SUBSYSTEM_ID_VIRTIO_BLK,
251b30d05adSPekka Enberg 	.bar[0]			= IOPORT_VIRTIO | PCI_BASE_ADDRESS_SPACE_IO,
252dc53a427SPekka Enberg 	.irq_pin		= 1,
2538b1ff07eSPekka Enberg 	.irq_line		= VIRTIO_BLK_IRQ,
254b30d05adSPekka Enberg };
255b30d05adSPekka Enberg 
256ca7c891bSCyrill Gorcunov void blk_virtio__init(struct kvm *self)
257b30d05adSPekka Enberg {
2581f848897SPekka Enberg 	if (!self->disk_image)
2591f848897SPekka Enberg 		return;
2601f848897SPekka Enberg 
2616160ab9dSAsias He 	device.blk_config.capacity = self->disk_image->size / 512;
262ca7c891bSCyrill Gorcunov 
263fbc2fbf9SPekka Enberg 	pci__register(&blk_virtio_pci_device, 1);
264b30d05adSPekka Enberg 
2658b1ff07eSPekka Enberg 	ioport__register(IOPORT_VIRTIO, &blk_virtio_io_ops, 256);
266b30d05adSPekka Enberg }
267