xref: /kvmtool/virtio/blk.c (revision 03110ff36b3587a67d751f3b1529882d29aa83cd)
1b30d05adSPekka Enberg #include "kvm/blk-virtio.h"
2b30d05adSPekka Enberg 
3984b7ae0SPekka Enberg #include "kvm/virtio_pci.h"
476b6349fSPekka Enberg 
55a24a9f2SPekka Enberg #include "kvm/disk-image.h"
676b6349fSPekka Enberg #include "kvm/virtqueue.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 
21*03110ff3SAsias He #define VIRTIO_BLK_QUEUE_SIZE	128
2210eca11dSPekka Enberg 
23fbc2fbf9SPekka Enberg struct 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 
3310eca11dSPekka Enberg 	struct virt_queue		virt_queues[NUM_VIRT_QUEUES];
34fbc2fbf9SPekka Enberg };
35fbc2fbf9SPekka Enberg 
3640ce993fSPekka Enberg #define DISK_CYLINDERS	1024
3740ce993fSPekka Enberg #define DISK_HEADS	64
3840ce993fSPekka Enberg #define DISK_SECTORS	32
39*03110ff3SAsias He #define DISK_SEG_MAX	126
4040ce993fSPekka Enberg 
41c435b91dSPekka Enberg static struct device device = {
4240ce993fSPekka Enberg 	.blk_config		= (struct virtio_blk_config) {
4340ce993fSPekka Enberg 		.capacity		= DISK_CYLINDERS * DISK_HEADS * DISK_SECTORS,
44*03110ff3SAsias He 		.seg_max		= DISK_SEG_MAX,
4540ce993fSPekka Enberg 		/* VIRTIO_BLK_F_GEOMETRY */
4640ce993fSPekka Enberg 		.geometry		= {
4740ce993fSPekka Enberg 			.cylinders		= DISK_CYLINDERS,
4840ce993fSPekka Enberg 			.heads			= DISK_HEADS,
4940ce993fSPekka Enberg 			.sectors		= DISK_SECTORS,
5040ce993fSPekka Enberg 		},
5140ce993fSPekka Enberg 		/* VIRTIO_BLK_SIZE */
5240ce993fSPekka Enberg 		.blk_size		= 4096,
5340ce993fSPekka Enberg 	},
541ef2738dSCyrill Gorcunov 	/*
551ef2738dSCyrill Gorcunov 	 * Note we don't set VIRTIO_BLK_F_GEOMETRY here so the
561ef2738dSCyrill Gorcunov 	 * node kernel will compute disk geometry by own, the
571ef2738dSCyrill Gorcunov 	 * same applies to VIRTIO_BLK_F_BLK_SIZE
581ef2738dSCyrill Gorcunov 	 */
59*03110ff3SAsias He 	.host_features		= (1UL << VIRTIO_BLK_F_SEG_MAX),
60c435b91dSPekka Enberg };
61fbc2fbf9SPekka Enberg 
6240ce993fSPekka Enberg static bool virtio_blk_config_in(void *data, unsigned long offset, int size, uint32_t count)
6340ce993fSPekka Enberg {
6440ce993fSPekka Enberg 	uint8_t *config_space = (uint8_t *) &device.blk_config;
6540ce993fSPekka Enberg 
6640ce993fSPekka Enberg 	if (size != 1 || count != 1)
6740ce993fSPekka Enberg 		return false;
6840ce993fSPekka Enberg 
6940ce993fSPekka Enberg 	ioport__write8(data, config_space[offset - VIRTIO_PCI_CONFIG_NOMSI]);
7040ce993fSPekka Enberg 
7140ce993fSPekka Enberg 	return true;
7240ce993fSPekka Enberg }
7340ce993fSPekka Enberg 
74fbc2fbf9SPekka Enberg static bool blk_virtio_in(struct kvm *self, uint16_t port, void *data, int size, uint32_t count)
75fbc2fbf9SPekka Enberg {
76fbc2fbf9SPekka Enberg 	unsigned long offset;
77fbc2fbf9SPekka Enberg 
78d1888318SCyrill Gorcunov 	offset		= port - IOPORT_VIRTIO_BLK;
79fbc2fbf9SPekka Enberg 
80fbc2fbf9SPekka Enberg 	switch (offset) {
81fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_HOST_FEATURES:
82c435b91dSPekka Enberg 		ioport__write32(data, device.host_features);
83fbc2fbf9SPekka Enberg 		break;
84fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_GUEST_FEATURES:
858b1ff07eSPekka Enberg 		return false;
86fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_QUEUE_PFN:
8710eca11dSPekka Enberg 		ioport__write32(data, device.virt_queues[device.queue_selector].pfn);
888b1ff07eSPekka Enberg 		break;
89fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_QUEUE_NUM:
9010eca11dSPekka Enberg 		ioport__write16(data, VIRTIO_BLK_QUEUE_SIZE);
918b1ff07eSPekka Enberg 		break;
92fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_QUEUE_SEL:
93fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_QUEUE_NOTIFY:
94fbc2fbf9SPekka Enberg 		return false;
95fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_STATUS:
96fbc2fbf9SPekka Enberg 		ioport__write8(data, 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:
10340ce993fSPekka Enberg 		ioport__write16(data, device.config_vector);
10440ce993fSPekka Enberg 		break;
105fbc2fbf9SPekka Enberg 	default:
10640ce993fSPekka Enberg 		return virtio_blk_config_in(data, offset, size, count);
107fbc2fbf9SPekka Enberg 	};
108fbc2fbf9SPekka Enberg 
109fbc2fbf9SPekka Enberg 	return true;
110fbc2fbf9SPekka Enberg }
111fbc2fbf9SPekka Enberg 
112*03110ff3SAsias He static bool blk_virtio_request(struct kvm *self, struct virt_queue *queue)
1134155ba8cSPekka Enberg {
1144155ba8cSPekka Enberg 	struct vring_used_elem *used_elem;
1154155ba8cSPekka Enberg 	struct virtio_blk_outhdr *req;
116*03110ff3SAsias He 	uint16_t desc_block_last;
1174155ba8cSPekka Enberg 	struct vring_desc *desc;
118*03110ff3SAsias He 	uint16_t desc_status;
119*03110ff3SAsias He 	uint16_t desc_block;
120258dd093SPekka Enberg 	uint32_t block_len;
121*03110ff3SAsias He 	uint32_t block_cnt;
122*03110ff3SAsias He 	uint16_t desc_hdr;
1234155ba8cSPekka Enberg 	uint8_t *status;
124258dd093SPekka Enberg 	void *block;
125*03110ff3SAsias He 	int err;
126*03110ff3SAsias He 	int err_cnt;
1274155ba8cSPekka Enberg 
128*03110ff3SAsias He 	/* header */
129*03110ff3SAsias He 	desc_hdr		= virt_queue__pop(queue);
1304155ba8cSPekka Enberg 
131*03110ff3SAsias He 	if (desc_hdr >= queue->vring.num) {
1324155ba8cSPekka Enberg 		warning("fatal I/O error");
1334155ba8cSPekka Enberg 		return false;
1344155ba8cSPekka Enberg 	}
1354155ba8cSPekka Enberg 
136*03110ff3SAsias He 	desc			= virt_queue__get_desc(queue, desc_hdr);
1374155ba8cSPekka Enberg 	assert(!(desc->flags & VRING_DESC_F_INDIRECT));
1384155ba8cSPekka Enberg 
1394155ba8cSPekka Enberg 	req			= guest_flat_to_host(self, desc->addr);
1404155ba8cSPekka Enberg 
141*03110ff3SAsias He 	/* status */
142*03110ff3SAsias He 	desc_status		= desc_hdr;
143*03110ff3SAsias He 
144*03110ff3SAsias He 	do {
145*03110ff3SAsias He 		desc_block_last	= desc_status;
146*03110ff3SAsias He 		desc_status	= virt_queue__get_desc(queue, desc_status)->next;
147*03110ff3SAsias He 
148*03110ff3SAsias He 		if (desc_status >= queue->vring.num) {
149*03110ff3SAsias He 			warning("fatal I/O error");
150*03110ff3SAsias He 			return false;
151*03110ff3SAsias He 		}
152*03110ff3SAsias He 
153*03110ff3SAsias He 		desc		= virt_queue__get_desc(queue, desc_status);
154*03110ff3SAsias He 		assert(!(desc->flags & VRING_DESC_F_INDIRECT));
155*03110ff3SAsias He 
156*03110ff3SAsias He 	} while (desc->flags & VRING_DESC_F_NEXT);
157*03110ff3SAsias He 
158*03110ff3SAsias He 	status			= guest_flat_to_host(self, desc->addr);
159*03110ff3SAsias He 
1604155ba8cSPekka Enberg 	/* block */
161*03110ff3SAsias He 	desc_block		= desc_hdr;
162*03110ff3SAsias He 	block_cnt		= 0;
163*03110ff3SAsias He 	err_cnt			= 0;
164*03110ff3SAsias He 
165*03110ff3SAsias He 	do {
166*03110ff3SAsias He 		desc_block	= virt_queue__get_desc(queue, desc_block)->next;
167*03110ff3SAsias He 
168*03110ff3SAsias He 		desc		= virt_queue__get_desc(queue, desc_block);
1694155ba8cSPekka Enberg 		assert(!(desc->flags & VRING_DESC_F_INDIRECT));
1704155ba8cSPekka Enberg 
171258dd093SPekka Enberg 		block		= guest_flat_to_host(self, desc->addr);
172258dd093SPekka Enberg 		block_len	= desc->len;
1734155ba8cSPekka Enberg 
174258dd093SPekka Enberg 		switch (req->type) {
175*03110ff3SAsias He 		case VIRTIO_BLK_T_IN:
176258dd093SPekka Enberg 			err	= disk_image__read_sector(self->disk_image, req->sector, block, block_len);
177258dd093SPekka Enberg 			break;
178*03110ff3SAsias He 		case VIRTIO_BLK_T_OUT:
179258dd093SPekka Enberg 			err	= disk_image__write_sector(self->disk_image, req->sector, block, block_len);
180258dd093SPekka Enberg 			break;
181258dd093SPekka Enberg 		default:
1824155ba8cSPekka Enberg 			warning("request type %d", req->type);
183*03110ff3SAsias He 			err	= -1;
184a2c8c696SAsias He 		}
185a2c8c696SAsias He 
186*03110ff3SAsias He 		if (err)
187*03110ff3SAsias He 			err_cnt++;
188a267e01bSPekka Enberg 
189*03110ff3SAsias He 		req->sector	+= block_len >> SECTOR_SHIFT;
190*03110ff3SAsias He 		block_cnt	+= block_len;
191*03110ff3SAsias He 
192*03110ff3SAsias He 		if (desc_block == desc_block_last)
193*03110ff3SAsias He 			break;
194*03110ff3SAsias He 
195*03110ff3SAsias He 		if (desc_block >= queue->vring.num) {
196*03110ff3SAsias He 			warning("fatal I/O error");
197*03110ff3SAsias He 			return false;
198*03110ff3SAsias He 		}
199*03110ff3SAsias He 
200*03110ff3SAsias He 	} while (true);
201*03110ff3SAsias He 
202*03110ff3SAsias He 	*status			= err_cnt ? VIRTIO_BLK_S_IOERR : VIRTIO_BLK_S_OK;
203*03110ff3SAsias He 
204*03110ff3SAsias He 	used_elem		= virt_queue__get_used_elem(queue);
205*03110ff3SAsias He 	used_elem->id		= desc_hdr;
206*03110ff3SAsias He 	used_elem->len		= block_cnt;
2074155ba8cSPekka Enberg 
2084155ba8cSPekka Enberg 	return true;
2094155ba8cSPekka Enberg }
2104155ba8cSPekka Enberg 
211fbc2fbf9SPekka Enberg static bool blk_virtio_out(struct kvm *self, uint16_t port, void *data, int size, uint32_t count)
212fbc2fbf9SPekka Enberg {
213fbc2fbf9SPekka Enberg 	unsigned long offset;
214fbc2fbf9SPekka Enberg 
215d1888318SCyrill Gorcunov 	offset		= port - IOPORT_VIRTIO_BLK;
216fbc2fbf9SPekka Enberg 
217fbc2fbf9SPekka Enberg 	switch (offset) {
218fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_GUEST_FEATURES:
219fbc2fbf9SPekka Enberg 		device.guest_features	= ioport__read32(data);
220fbc2fbf9SPekka Enberg 		break;
22110eca11dSPekka Enberg 	case VIRTIO_PCI_QUEUE_PFN: {
22210eca11dSPekka Enberg 		struct virt_queue *queue;
22310eca11dSPekka Enberg 		void *p;
22410eca11dSPekka Enberg 
22510eca11dSPekka Enberg 		queue			= &device.virt_queues[device.queue_selector];
22610eca11dSPekka Enberg 
22710eca11dSPekka Enberg 		queue->pfn		= ioport__read32(data);
22810eca11dSPekka Enberg 
22910eca11dSPekka Enberg 		p			= guest_flat_to_host(self, queue->pfn << 12);
23010eca11dSPekka Enberg 
23110eca11dSPekka Enberg 		vring_init(&queue->vring, VIRTIO_BLK_QUEUE_SIZE, p, 4096);
23210eca11dSPekka Enberg 
2337e61688eSPekka Enberg 		break;
23410eca11dSPekka Enberg 	}
235fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_QUEUE_SEL:
23647bf1d0fSPekka Enberg 		device.queue_selector	= ioport__read16(data);
2377e61688eSPekka Enberg 		break;
23810eca11dSPekka Enberg 	case VIRTIO_PCI_QUEUE_NOTIFY: {
23910eca11dSPekka Enberg 		struct virt_queue *queue;
24010eca11dSPekka Enberg 		uint16_t queue_index;
24110eca11dSPekka Enberg 
24210eca11dSPekka Enberg 		queue_index		= ioport__read16(data);
24310eca11dSPekka Enberg 
24410eca11dSPekka Enberg 		queue			= &device.virt_queues[queue_index];
24510eca11dSPekka Enberg 
2464155ba8cSPekka Enberg 		while (queue->vring.avail->idx != queue->last_avail_idx) {
247*03110ff3SAsias He 			if (!blk_virtio_request(self, queue))
2484155ba8cSPekka Enberg 				return false;
24993d18b72SPekka Enberg 		}
2508b1ff07eSPekka Enberg 		kvm__irq_line(self, VIRTIO_BLK_IRQ, 1);
2515a24a9f2SPekka Enberg 
2527e61688eSPekka Enberg 		break;
25310eca11dSPekka Enberg 	}
254fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_STATUS:
255fbc2fbf9SPekka Enberg 		device.status		= ioport__read8(data);
256fbc2fbf9SPekka Enberg 		break;
257fbc2fbf9SPekka Enberg 	case VIRTIO_MSI_CONFIG_VECTOR:
25840ce993fSPekka Enberg 		device.config_vector	= VIRTIO_MSI_NO_VECTOR;
25940ce993fSPekka Enberg 		break;
260fbc2fbf9SPekka Enberg 	case VIRTIO_MSI_QUEUE_VECTOR:
26140ce993fSPekka Enberg 		break;
262fbc2fbf9SPekka Enberg 	default:
263fbc2fbf9SPekka Enberg 		return false;
264fbc2fbf9SPekka Enberg 	};
265fbc2fbf9SPekka Enberg 
266fbc2fbf9SPekka Enberg 	return true;
267fbc2fbf9SPekka Enberg }
268fbc2fbf9SPekka Enberg 
269fbc2fbf9SPekka Enberg static struct ioport_operations blk_virtio_io_ops = {
270fbc2fbf9SPekka Enberg 	.io_in		= blk_virtio_in,
271fbc2fbf9SPekka Enberg 	.io_out		= blk_virtio_out,
272fbc2fbf9SPekka Enberg };
273fbc2fbf9SPekka Enberg 
274b30d05adSPekka Enberg #define PCI_VENDOR_ID_REDHAT_QUMRANET		0x1af4
275b30d05adSPekka Enberg #define PCI_DEVICE_ID_VIRTIO_BLK		0x1001
276b30d05adSPekka Enberg #define PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET	0x1af4
277b30d05adSPekka Enberg #define PCI_SUBSYSTEM_ID_VIRTIO_BLK		0x0002
278b30d05adSPekka Enberg 
279fbc2fbf9SPekka Enberg static struct pci_device_header blk_virtio_pci_device = {
280b30d05adSPekka Enberg 	.vendor_id		= PCI_VENDOR_ID_REDHAT_QUMRANET,
281b30d05adSPekka Enberg 	.device_id		= PCI_DEVICE_ID_VIRTIO_BLK,
282b30d05adSPekka Enberg 	.header_type		= PCI_HEADER_TYPE_NORMAL,
283b30d05adSPekka Enberg 	.revision_id		= 0,
284b30d05adSPekka Enberg 	.class			= 0x010000,
285b30d05adSPekka Enberg 	.subsys_vendor_id	= PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET,
286b30d05adSPekka Enberg 	.subsys_id		= PCI_SUBSYSTEM_ID_VIRTIO_BLK,
287d1888318SCyrill Gorcunov 	.bar[0]			= IOPORT_VIRTIO_BLK | PCI_BASE_ADDRESS_SPACE_IO,
288dc53a427SPekka Enberg 	.irq_pin		= 1,
2898b1ff07eSPekka Enberg 	.irq_line		= VIRTIO_BLK_IRQ,
290b30d05adSPekka Enberg };
291b30d05adSPekka Enberg 
292ca7c891bSCyrill Gorcunov void blk_virtio__init(struct kvm *self)
293b30d05adSPekka Enberg {
2941f848897SPekka Enberg 	if (!self->disk_image)
2951f848897SPekka Enberg 		return;
2961f848897SPekka Enberg 
297a267e01bSPekka Enberg 	device.blk_config.capacity = self->disk_image->size / SECTOR_SIZE;
298ca7c891bSCyrill Gorcunov 
299fbc2fbf9SPekka Enberg 	pci__register(&blk_virtio_pci_device, 1);
300b30d05adSPekka Enberg 
301d1888318SCyrill Gorcunov 	ioport__register(IOPORT_VIRTIO_BLK, &blk_virtio_io_ops, 256);
302b30d05adSPekka Enberg }
303