xref: /kvmtool/virtio/blk.c (revision 4749e795e294476953cbd5ec9761757346a9eb30)
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"
12fb0957f2SSasha Levin #include "kvm/threadpool.h"
13b30d05adSPekka Enberg 
1420c64ecaSPekka Enberg #include <linux/virtio_ring.h>
1520c64ecaSPekka Enberg #include <linux/virtio_blk.h>
160528c2a7SPekka Enberg 
174155ba8cSPekka Enberg #include <inttypes.h>
180528c2a7SPekka Enberg #include <pthread.h>
194155ba8cSPekka Enberg 
20*4749e795SSasha Levin #define VIRTIO_BLK_IRQ		9
21bc0363c8SCyrill Gorcunov #define VIRTIO_BLK_PIN		1
22*4749e795SSasha Levin #define VIRTIO_BLK_MAX_DEV	4
2310eca11dSPekka Enberg #define NUM_VIRT_QUEUES		1
2410eca11dSPekka Enberg 
2503110ff3SAsias He #define VIRTIO_BLK_QUEUE_SIZE	128
2610eca11dSPekka Enberg 
27*4749e795SSasha Levin struct blk_device_job {
28*4749e795SSasha Levin 	struct virt_queue		*vq;
29*4749e795SSasha Levin 	struct blk_device		*blk_device;
30*4749e795SSasha Levin 	void				*job_id;
31*4749e795SSasha Levin };
32*4749e795SSasha Levin 
33802e07a0SAsias He struct blk_device {
340528c2a7SPekka Enberg 	pthread_mutex_t			mutex;
350528c2a7SPekka Enberg 
3640ce993fSPekka Enberg 	struct virtio_blk_config	blk_config;
3738605e1cSSasha Levin 	struct disk_image		*disk;
38c435b91dSPekka Enberg 	uint32_t			host_features;
39fbc2fbf9SPekka Enberg 	uint32_t			guest_features;
4040ce993fSPekka Enberg 	uint16_t			config_vector;
41fbc2fbf9SPekka Enberg 	uint8_t				status;
42*4749e795SSasha Levin 	u8				idx;
4347bf1d0fSPekka Enberg 
4447bf1d0fSPekka Enberg 	/* virtio queue */
4547bf1d0fSPekka Enberg 	uint16_t			queue_selector;
4610eca11dSPekka Enberg 
4745e47970SAsias He 	struct virt_queue		vqs[NUM_VIRT_QUEUES];
48*4749e795SSasha Levin 	struct blk_device_job		jobs[NUM_VIRT_QUEUES];
49*4749e795SSasha Levin 	struct pci_device_header	pci_device;
50fbc2fbf9SPekka Enberg };
51fbc2fbf9SPekka Enberg 
52*4749e795SSasha Levin static struct blk_device *blk_devices[VIRTIO_BLK_MAX_DEV];
5340ce993fSPekka Enberg 
54*4749e795SSasha Levin static bool virtio_blk_pci_io_device_specific_in(struct blk_device *blk_device,
55*4749e795SSasha Levin 						void *data,
56*4749e795SSasha Levin 						unsigned long offset,
57*4749e795SSasha Levin 						int size,
58*4749e795SSasha Levin 						uint32_t count)
5940ce993fSPekka Enberg {
60*4749e795SSasha Levin 	uint8_t *config_space = (uint8_t *) &blk_device->blk_config;
6140ce993fSPekka Enberg 
6240ce993fSPekka Enberg 	if (size != 1 || count != 1)
6340ce993fSPekka Enberg 		return false;
6440ce993fSPekka Enberg 
6540ce993fSPekka Enberg 	ioport__write8(data, config_space[offset - VIRTIO_PCI_CONFIG_NOMSI]);
6640ce993fSPekka Enberg 
6740ce993fSPekka Enberg 	return true;
6840ce993fSPekka Enberg }
6940ce993fSPekka Enberg 
70*4749e795SSasha Levin /* Translate port into device id + offset in that device addr space */
71*4749e795SSasha Levin static void virtio_blk_port2dev(u16 port,
72*4749e795SSasha Levin 				u16 base,
73*4749e795SSasha Levin 				u16 size,
74*4749e795SSasha Levin 				u16 *dev_idx,
75*4749e795SSasha Levin 				u16 *offset)
76*4749e795SSasha Levin {
77*4749e795SSasha Levin 	*dev_idx	= (port - base) / size;
78*4749e795SSasha Levin 	*offset		= port - (base + *dev_idx * size);
79*4749e795SSasha Levin }
80416b2c2dSAsias He static bool virtio_blk_pci_io_in(struct kvm *self, uint16_t port, void *data, int size, uint32_t count)
81fbc2fbf9SPekka Enberg {
82*4749e795SSasha Levin 	u16 offset, dev_idx;
830528c2a7SPekka Enberg 	bool ret = true;
84*4749e795SSasha Levin 	struct blk_device *blk_device;
850528c2a7SPekka Enberg 
86*4749e795SSasha Levin 	virtio_blk_port2dev(port, IOPORT_VIRTIO_BLK, IOPORT_VIRTIO_BLK_SIZE,
87*4749e795SSasha Levin 				&dev_idx, &offset);
88fbc2fbf9SPekka Enberg 
89*4749e795SSasha Levin 	blk_device = blk_devices[dev_idx];
90*4749e795SSasha Levin 
91*4749e795SSasha Levin 	mutex_lock(&blk_device->mutex);
92fbc2fbf9SPekka Enberg 
93fbc2fbf9SPekka Enberg 	switch (offset) {
94fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_HOST_FEATURES:
95*4749e795SSasha Levin 		ioport__write32(data, blk_device->host_features);
96fbc2fbf9SPekka Enberg 		break;
97fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_GUEST_FEATURES:
980528c2a7SPekka Enberg 		ret		= false;
999ee67e60SAsias He 		break;
100fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_QUEUE_PFN:
101*4749e795SSasha Levin 		ioport__write32(data, blk_device->vqs[blk_device->queue_selector].pfn);
1028b1ff07eSPekka Enberg 		break;
103fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_QUEUE_NUM:
10410eca11dSPekka Enberg 		ioport__write16(data, VIRTIO_BLK_QUEUE_SIZE);
1058b1ff07eSPekka Enberg 		break;
106fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_QUEUE_SEL:
107fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_QUEUE_NOTIFY:
1080528c2a7SPekka Enberg 		ret		= false;
1099ee67e60SAsias He 		break;
110fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_STATUS:
111*4749e795SSasha Levin 		ioport__write8(data, blk_device->status);
112fbc2fbf9SPekka Enberg 		break;
113fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_ISR:
1148b1ff07eSPekka Enberg 		ioport__write8(data, 0x1);
115*4749e795SSasha Levin 		kvm__irq_line(self, VIRTIO_BLK_IRQ + blk_device->idx, 0);
1167e61688eSPekka Enberg 		break;
117fbc2fbf9SPekka Enberg 	case VIRTIO_MSI_CONFIG_VECTOR:
118*4749e795SSasha Levin 		ioport__write16(data, blk_device->config_vector);
11940ce993fSPekka Enberg 		break;
120fbc2fbf9SPekka Enberg 	default:
121*4749e795SSasha Levin 		ret = virtio_blk_pci_io_device_specific_in(blk_device, data, offset, size, count);
122fbc2fbf9SPekka Enberg 	};
123fbc2fbf9SPekka Enberg 
124*4749e795SSasha Levin 	mutex_unlock(&blk_device->mutex);
1250528c2a7SPekka Enberg 
1260528c2a7SPekka Enberg 	return ret;
127fbc2fbf9SPekka Enberg }
128fbc2fbf9SPekka Enberg 
129*4749e795SSasha Levin static bool virtio_blk_do_io_request(struct kvm *self,
130*4749e795SSasha Levin 					struct blk_device *blk_device,
131*4749e795SSasha Levin 					struct virt_queue *queue)
1324155ba8cSPekka Enberg {
13345e47970SAsias He 	struct iovec iov[VIRTIO_BLK_QUEUE_SIZE];
1344155ba8cSPekka Enberg 	struct virtio_blk_outhdr *req;
13570b53f25SSasha Levin 	ssize_t block_cnt = -1;
13645e47970SAsias He 	uint16_t out, in, head;
1374155ba8cSPekka Enberg 	uint8_t *status;
1384155ba8cSPekka Enberg 
13945e47970SAsias He 	head		= virt_queue__get_iov(queue, iov, &out, &in, self);
1404155ba8cSPekka Enberg 
14145e47970SAsias He 	/* head */
14245e47970SAsias He 	req		= iov[0].iov_base;
14303110ff3SAsias He 
144258dd093SPekka Enberg 	switch (req->type) {
14503110ff3SAsias He 	case VIRTIO_BLK_T_IN:
146*4749e795SSasha Levin 		block_cnt = disk_image__read_sector_iov(blk_device->disk, req->sector, iov + 1, in + out - 2);
14770b53f25SSasha Levin 
148258dd093SPekka Enberg 		break;
14903110ff3SAsias He 	case VIRTIO_BLK_T_OUT:
150*4749e795SSasha Levin 		block_cnt = disk_image__write_sector_iov(blk_device->disk, req->sector, iov + 1, in + out - 2);
15170b53f25SSasha Levin 
152258dd093SPekka Enberg 		break;
15370b53f25SSasha Levin 
154258dd093SPekka Enberg 	default:
1554155ba8cSPekka Enberg 		warning("request type %d", req->type);
15670b53f25SSasha Levin 		block_cnt = -1;
15703110ff3SAsias He 	}
15803110ff3SAsias He 
15945e47970SAsias He 	/* status */
16045e47970SAsias He 	status			= iov[out + in - 1].iov_base;
16170b53f25SSasha Levin 	*status			= (block_cnt < 0) ? VIRTIO_BLK_S_IOERR : VIRTIO_BLK_S_OK;
16203110ff3SAsias He 
16345e47970SAsias He 	virt_queue__set_used_elem(queue, head, block_cnt);
1644155ba8cSPekka Enberg 
1654155ba8cSPekka Enberg 	return true;
1664155ba8cSPekka Enberg }
1674155ba8cSPekka Enberg 
168fb0957f2SSasha Levin static void virtio_blk_do_io(struct kvm *kvm, void *param)
16945e47970SAsias He {
170*4749e795SSasha Levin 	struct blk_device_job *job = param;
171*4749e795SSasha Levin 	struct virt_queue *vq = job->vq;
172*4749e795SSasha Levin 	struct blk_device *blk_device = job->blk_device;
17345e47970SAsias He 
1740ea58e5bSPekka Enberg 	while (virt_queue__available(vq))
175*4749e795SSasha Levin 		virtio_blk_do_io_request(kvm, blk_device, vq);
1760ea58e5bSPekka Enberg 
177*4749e795SSasha Levin 	kvm__irq_line(kvm, VIRTIO_BLK_IRQ + blk_device->idx, 1);
1784baf6f73SSasha Levin }
1790528c2a7SPekka Enberg 
180416b2c2dSAsias He static bool virtio_blk_pci_io_out(struct kvm *self, uint16_t port, void *data, int size, uint32_t count)
181fbc2fbf9SPekka Enberg {
182*4749e795SSasha Levin 	u16 offset, dev_idx;
1830528c2a7SPekka Enberg 	bool ret = true;
184*4749e795SSasha Levin 	struct blk_device *blk_device;
1850528c2a7SPekka Enberg 
186*4749e795SSasha Levin 	virtio_blk_port2dev(port, IOPORT_VIRTIO_BLK, IOPORT_VIRTIO_BLK_SIZE,
187*4749e795SSasha Levin 						&dev_idx, &offset);
188fbc2fbf9SPekka Enberg 
189*4749e795SSasha Levin 	blk_device = blk_devices[dev_idx];
190*4749e795SSasha Levin 
191*4749e795SSasha Levin 	mutex_lock(&blk_device->mutex);
192fbc2fbf9SPekka Enberg 
193fbc2fbf9SPekka Enberg 	switch (offset) {
194fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_GUEST_FEATURES:
195*4749e795SSasha Levin 		blk_device->guest_features	= ioport__read32(data);
196fbc2fbf9SPekka Enberg 		break;
19710eca11dSPekka Enberg 	case VIRTIO_PCI_QUEUE_PFN: {
19810eca11dSPekka Enberg 		struct virt_queue *queue;
199*4749e795SSasha Levin 		struct blk_device_job *job;
20010eca11dSPekka Enberg 		void *p;
20110eca11dSPekka Enberg 
202*4749e795SSasha Levin 		job = &blk_device->jobs[blk_device->queue_selector];
20310eca11dSPekka Enberg 
204*4749e795SSasha Levin 		queue			= &blk_device->vqs[blk_device->queue_selector];
20510eca11dSPekka Enberg 		queue->pfn		= ioport__read32(data);
20610eca11dSPekka Enberg 		p			= guest_flat_to_host(self, queue->pfn << 12);
20710eca11dSPekka Enberg 
20810eca11dSPekka Enberg 		vring_init(&queue->vring, VIRTIO_BLK_QUEUE_SIZE, p, 4096);
20910eca11dSPekka Enberg 
210*4749e795SSasha Levin 		*job = (struct blk_device_job) {
211*4749e795SSasha Levin 			.vq		= queue,
212*4749e795SSasha Levin 			.blk_device	= blk_device,
213*4749e795SSasha Levin 		};
214*4749e795SSasha Levin 
215*4749e795SSasha Levin 		job->job_id = thread_pool__add_job(self, virtio_blk_do_io, job);
216fb0957f2SSasha Levin 
2177e61688eSPekka Enberg 		break;
21810eca11dSPekka Enberg 	}
219fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_QUEUE_SEL:
220*4749e795SSasha Levin 		blk_device->queue_selector	= ioport__read16(data);
2217e61688eSPekka Enberg 		break;
22210eca11dSPekka Enberg 	case VIRTIO_PCI_QUEUE_NOTIFY: {
22310eca11dSPekka Enberg 		uint16_t queue_index;
22410eca11dSPekka Enberg 		queue_index		= ioport__read16(data);
225*4749e795SSasha Levin 		thread_pool__do_job(blk_device->jobs[queue_index].job_id);
2267e61688eSPekka Enberg 		break;
22710eca11dSPekka Enberg 	}
228fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_STATUS:
229*4749e795SSasha Levin 		blk_device->status		= ioport__read8(data);
230fbc2fbf9SPekka Enberg 		break;
231fbc2fbf9SPekka Enberg 	case VIRTIO_MSI_CONFIG_VECTOR:
232*4749e795SSasha Levin 		blk_device->config_vector	= VIRTIO_MSI_NO_VECTOR;
23340ce993fSPekka Enberg 		break;
234fbc2fbf9SPekka Enberg 	case VIRTIO_MSI_QUEUE_VECTOR:
23540ce993fSPekka Enberg 		break;
236fbc2fbf9SPekka Enberg 	default:
2370528c2a7SPekka Enberg 		ret		= false;
238fbc2fbf9SPekka Enberg 	};
239fbc2fbf9SPekka Enberg 
240*4749e795SSasha Levin 	mutex_unlock(&blk_device->mutex);
2410528c2a7SPekka Enberg 
2420528c2a7SPekka Enberg 	return ret;
243fbc2fbf9SPekka Enberg }
244fbc2fbf9SPekka Enberg 
245416b2c2dSAsias He static struct ioport_operations virtio_blk_io_ops = {
246416b2c2dSAsias He 	.io_in		= virtio_blk_pci_io_in,
247416b2c2dSAsias He 	.io_out		= virtio_blk_pci_io_out,
248fbc2fbf9SPekka Enberg };
249fbc2fbf9SPekka Enberg 
250b30d05adSPekka Enberg #define PCI_VENDOR_ID_REDHAT_QUMRANET		0x1af4
251b30d05adSPekka Enberg #define PCI_DEVICE_ID_VIRTIO_BLK		0x1001
252b30d05adSPekka Enberg #define PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET	0x1af4
253b30d05adSPekka Enberg #define PCI_SUBSYSTEM_ID_VIRTIO_BLK		0x0002
254*4749e795SSasha Levin #define PCI_VIRTIO_BLK_DEVNUM 10
255b30d05adSPekka Enberg 
256*4749e795SSasha Levin static int virtio_blk_find_empty_dev(void)
257*4749e795SSasha Levin {
258*4749e795SSasha Levin 	int i;
259*4749e795SSasha Levin 
260*4749e795SSasha Levin 	for (i = 0; i < VIRTIO_BLK_MAX_DEV; i++) {
261*4749e795SSasha Levin 		if (blk_devices[i] == NULL)
262*4749e795SSasha Levin 			return i;
263*4749e795SSasha Levin 	}
264*4749e795SSasha Levin 
265*4749e795SSasha Levin 	return -1;
266*4749e795SSasha Levin }
267*4749e795SSasha Levin 
268*4749e795SSasha Levin void virtio_blk__init(struct kvm *self, struct disk_image *disk)
269*4749e795SSasha Levin {
270*4749e795SSasha Levin 	int new_dev_idx;
271*4749e795SSasha Levin 	u16 blk_dev_base_addr;
272*4749e795SSasha Levin 	struct blk_device *blk_device;
273*4749e795SSasha Levin 
274*4749e795SSasha Levin 	if (!disk)
275*4749e795SSasha Levin 		return;
276*4749e795SSasha Levin 
277*4749e795SSasha Levin 	new_dev_idx = virtio_blk_find_empty_dev();
278*4749e795SSasha Levin 	if (new_dev_idx < 0)
279*4749e795SSasha Levin 		die("Could not find an empty block device slot");
280*4749e795SSasha Levin 
281*4749e795SSasha Levin 	blk_devices[new_dev_idx] = calloc(1, sizeof(struct blk_device));
282*4749e795SSasha Levin 	if (blk_devices[new_dev_idx] == NULL)
283*4749e795SSasha Levin 		die("Failed allocating blk_device");
284*4749e795SSasha Levin 
285*4749e795SSasha Levin 	blk_device = blk_devices[new_dev_idx];
286*4749e795SSasha Levin 	blk_dev_base_addr = IOPORT_VIRTIO_BLK + new_dev_idx * IOPORT_VIRTIO_BLK_SIZE;
287*4749e795SSasha Levin 
288*4749e795SSasha Levin 	*blk_device = (struct blk_device) {
289*4749e795SSasha Levin 		.mutex			= PTHREAD_MUTEX_INITIALIZER,
290*4749e795SSasha Levin 		.disk			= disk,
291*4749e795SSasha Levin 		.idx			= new_dev_idx,
292*4749e795SSasha Levin 		.blk_config		= (struct virtio_blk_config) {
293*4749e795SSasha Levin 			.capacity	= disk->size / SECTOR_SIZE,
294*4749e795SSasha Levin 		},
295*4749e795SSasha Levin 		.pci_device = (struct pci_device_header) {
296b30d05adSPekka Enberg 			.vendor_id		= PCI_VENDOR_ID_REDHAT_QUMRANET,
297b30d05adSPekka Enberg 			.device_id		= PCI_DEVICE_ID_VIRTIO_BLK,
298b30d05adSPekka Enberg 			.header_type		= PCI_HEADER_TYPE_NORMAL,
299b30d05adSPekka Enberg 			.revision_id		= 0,
300b30d05adSPekka Enberg 			.class			= 0x010000,
301b30d05adSPekka Enberg 			.subsys_vendor_id	= PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET,
302b30d05adSPekka Enberg 			.subsys_id		= PCI_SUBSYSTEM_ID_VIRTIO_BLK,
303*4749e795SSasha Levin 			.bar[0]			= blk_dev_base_addr | PCI_BASE_ADDRESS_SPACE_IO,
304bc0363c8SCyrill Gorcunov 			.irq_pin		= VIRTIO_BLK_PIN,
305*4749e795SSasha Levin 			.irq_line		= VIRTIO_BLK_IRQ + new_dev_idx,
306*4749e795SSasha Levin 		},
307b30d05adSPekka Enberg 	};
308b30d05adSPekka Enberg 
309*4749e795SSasha Levin 	pci__register(&blk_device->pci_device, PCI_VIRTIO_BLK_DEVNUM + new_dev_idx);
310f05bbe8dSAsias He 
311*4749e795SSasha Levin 	ioport__register(blk_dev_base_addr, &virtio_blk_io_ops, IOPORT_VIRTIO_BLK_SIZE);
312b30d05adSPekka Enberg }
313