xref: /kvmtool/virtio/blk.c (revision 407475bf94de5be1e8057c919cfa4af1900b7025)
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 
173fdf659dSSasha Levin #include <linux/types.h>
180528c2a7SPekka Enberg #include <pthread.h>
194155ba8cSPekka Enberg 
204749e795SSasha Levin #define VIRTIO_BLK_IRQ			9
21bc0363c8SCyrill Gorcunov #define VIRTIO_BLK_PIN			1
224749e795SSasha 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 
27fe2a70d1SSasha Levin struct blk_dev_job {
284749e795SSasha Levin 	struct virt_queue		*vq;
29fe2a70d1SSasha Levin 	struct blk_dev			*bdev;
304749e795SSasha Levin 	void				*job_id;
314749e795SSasha Levin };
324749e795SSasha Levin 
33fe2a70d1SSasha Levin struct blk_dev {
340528c2a7SPekka Enberg 	pthread_mutex_t			mutex;
350528c2a7SPekka Enberg 
3640ce993fSPekka Enberg 	struct virtio_blk_config	blk_config;
3738605e1cSSasha Levin 	struct disk_image		*disk;
383fdf659dSSasha Levin 	u32				host_features;
393fdf659dSSasha Levin 	u32				guest_features;
403fdf659dSSasha Levin 	u16				config_vector;
413fdf659dSSasha Levin 	u8				status;
424749e795SSasha Levin 	u8				idx;
4347bf1d0fSPekka Enberg 
4447bf1d0fSPekka Enberg 	/* virtio queue */
453fdf659dSSasha Levin 	u16				queue_selector;
4610eca11dSPekka Enberg 
4745e47970SAsias He 	struct virt_queue		vqs[NUM_VIRT_QUEUES];
48fe2a70d1SSasha Levin 	struct blk_dev_job		jobs[NUM_VIRT_QUEUES];
494749e795SSasha Levin 	struct pci_device_header	pci_device;
50fbc2fbf9SPekka Enberg };
51fbc2fbf9SPekka Enberg 
52fe2a70d1SSasha Levin static struct blk_dev *bdevs[VIRTIO_BLK_MAX_DEV];
5340ce993fSPekka Enberg 
54*407475bfSPekka Enberg static bool virtio_blk_dev_in(struct blk_dev *bdev, void *data, unsigned long offset, int size, u32 count)
5540ce993fSPekka Enberg {
56fe2a70d1SSasha Levin 	u8 *config_space = (u8 *) &bdev->blk_config;
5740ce993fSPekka Enberg 
5840ce993fSPekka Enberg 	if (size != 1 || count != 1)
5940ce993fSPekka Enberg 		return false;
6040ce993fSPekka Enberg 
6140ce993fSPekka Enberg 	ioport__write8(data, config_space[offset - VIRTIO_PCI_CONFIG_NOMSI]);
6240ce993fSPekka Enberg 
6340ce993fSPekka Enberg 	return true;
6440ce993fSPekka Enberg }
6540ce993fSPekka Enberg 
664749e795SSasha Levin /* Translate port into device id + offset in that device addr space */
67*407475bfSPekka Enberg static void virtio_blk_port2dev(u16 port, u16 base, u16 size, u16 *dev_idx, u16 *offset)
684749e795SSasha Levin {
694749e795SSasha Levin 	*dev_idx	= (port - base) / size;
704749e795SSasha Levin 	*offset		= port - (base + *dev_idx * size);
714749e795SSasha Levin }
72*407475bfSPekka Enberg 
733fdf659dSSasha Levin static bool virtio_blk_pci_io_in(struct kvm *self, u16 port, void *data, int size, u32 count)
74fbc2fbf9SPekka Enberg {
75*407475bfSPekka Enberg 	struct blk_dev *bdev;
764749e795SSasha Levin 	u16 offset, dev_idx;
770528c2a7SPekka Enberg 	bool ret = true;
780528c2a7SPekka Enberg 
79*407475bfSPekka Enberg 	virtio_blk_port2dev(port, IOPORT_VIRTIO_BLK, IOPORT_VIRTIO_BLK_SIZE, &dev_idx, &offset);
80fbc2fbf9SPekka Enberg 
81fe2a70d1SSasha Levin 	bdev = bdevs[dev_idx];
824749e795SSasha Levin 
83fe2a70d1SSasha Levin 	mutex_lock(&bdev->mutex);
84fbc2fbf9SPekka Enberg 
85fbc2fbf9SPekka Enberg 	switch (offset) {
86fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_HOST_FEATURES:
87fe2a70d1SSasha Levin 		ioport__write32(data, bdev->host_features);
88fbc2fbf9SPekka Enberg 		break;
89fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_GUEST_FEATURES:
900528c2a7SPekka Enberg 		ret		= false;
919ee67e60SAsias He 		break;
92fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_QUEUE_PFN:
93fe2a70d1SSasha Levin 		ioport__write32(data, bdev->vqs[bdev->queue_selector].pfn);
948b1ff07eSPekka Enberg 		break;
95fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_QUEUE_NUM:
9610eca11dSPekka Enberg 		ioport__write16(data, VIRTIO_BLK_QUEUE_SIZE);
978b1ff07eSPekka Enberg 		break;
98fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_QUEUE_SEL:
99fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_QUEUE_NOTIFY:
1000528c2a7SPekka Enberg 		ret		= false;
1019ee67e60SAsias He 		break;
102fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_STATUS:
103fe2a70d1SSasha Levin 		ioport__write8(data, bdev->status);
104fbc2fbf9SPekka Enberg 		break;
105fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_ISR:
1068b1ff07eSPekka Enberg 		ioport__write8(data, 0x1);
107fe2a70d1SSasha Levin 		kvm__irq_line(self, VIRTIO_BLK_IRQ + bdev->idx, 0);
1087e61688eSPekka Enberg 		break;
109fbc2fbf9SPekka Enberg 	case VIRTIO_MSI_CONFIG_VECTOR:
110fe2a70d1SSasha Levin 		ioport__write16(data, bdev->config_vector);
11140ce993fSPekka Enberg 		break;
112fbc2fbf9SPekka Enberg 	default:
113*407475bfSPekka Enberg 		ret = virtio_blk_dev_in(bdev, data, offset, size, count);
114*407475bfSPekka Enberg 		break;
115fbc2fbf9SPekka Enberg 	};
116fbc2fbf9SPekka Enberg 
117fe2a70d1SSasha Levin 	mutex_unlock(&bdev->mutex);
1180528c2a7SPekka Enberg 
1190528c2a7SPekka Enberg 	return ret;
120fbc2fbf9SPekka Enberg }
121fbc2fbf9SPekka Enberg 
1224749e795SSasha Levin static bool virtio_blk_do_io_request(struct kvm *self,
123fe2a70d1SSasha Levin 					struct blk_dev *bdev,
1244749e795SSasha Levin 					struct virt_queue *queue)
1254155ba8cSPekka Enberg {
12645e47970SAsias He 	struct iovec iov[VIRTIO_BLK_QUEUE_SIZE];
1274155ba8cSPekka Enberg 	struct virtio_blk_outhdr *req;
12870b53f25SSasha Levin 	ssize_t block_cnt = -1;
1293fdf659dSSasha Levin 	u16 out, in, head;
1303fdf659dSSasha Levin 	u8 *status;
1314155ba8cSPekka Enberg 
13245e47970SAsias He 	head			= virt_queue__get_iov(queue, iov, &out, &in, self);
1334155ba8cSPekka Enberg 
13445e47970SAsias He 	/* head */
13545e47970SAsias He 	req			= iov[0].iov_base;
13603110ff3SAsias He 
137258dd093SPekka Enberg 	switch (req->type) {
13803110ff3SAsias He 	case VIRTIO_BLK_T_IN:
139*407475bfSPekka Enberg 		block_cnt	= disk_image__read_sector_iov(bdev->disk, req->sector, iov + 1, in + out - 2);
140258dd093SPekka Enberg 		break;
14103110ff3SAsias He 	case VIRTIO_BLK_T_OUT:
142*407475bfSPekka Enberg 		block_cnt	= disk_image__write_sector_iov(bdev->disk, req->sector, iov + 1, in + out - 2);
143258dd093SPekka Enberg 		break;
144258dd093SPekka Enberg 	default:
1454155ba8cSPekka Enberg 		warning("request type %d", req->type);
14670b53f25SSasha Levin 		block_cnt	= -1;
147*407475bfSPekka Enberg 		break;
14803110ff3SAsias He 	}
14903110ff3SAsias He 
15045e47970SAsias He 	/* status */
15145e47970SAsias He 	status			= iov[out + in - 1].iov_base;
15270b53f25SSasha Levin 	*status			= (block_cnt < 0) ? VIRTIO_BLK_S_IOERR : VIRTIO_BLK_S_OK;
15303110ff3SAsias He 
15445e47970SAsias He 	virt_queue__set_used_elem(queue, head, block_cnt);
1554155ba8cSPekka Enberg 
1564155ba8cSPekka Enberg 	return true;
1574155ba8cSPekka Enberg }
1584155ba8cSPekka Enberg 
159fb0957f2SSasha Levin static void virtio_blk_do_io(struct kvm *kvm, void *param)
16045e47970SAsias He {
161fe2a70d1SSasha Levin 	struct blk_dev_job *job	= param;
162*407475bfSPekka Enberg 	struct virt_queue *vq;
163*407475bfSPekka Enberg 	struct blk_dev *bdev;
164*407475bfSPekka Enberg 
165*407475bfSPekka Enberg 	vq			= job->vq;
166*407475bfSPekka Enberg 	bdev			= job->bdev;
16745e47970SAsias He 
1680ea58e5bSPekka Enberg 	while (virt_queue__available(vq))
169fe2a70d1SSasha Levin 		virtio_blk_do_io_request(kvm, bdev, vq);
1700ea58e5bSPekka Enberg 
171fe2a70d1SSasha Levin 	kvm__irq_line(kvm, VIRTIO_BLK_IRQ + bdev->idx, 1);
1724baf6f73SSasha Levin }
1730528c2a7SPekka Enberg 
1743fdf659dSSasha Levin static bool virtio_blk_pci_io_out(struct kvm *self, u16 port, void *data, int size, u32 count)
175fbc2fbf9SPekka Enberg {
176*407475bfSPekka Enberg 	struct blk_dev *bdev;
1774749e795SSasha Levin 	u16 offset, dev_idx;
1780528c2a7SPekka Enberg 	bool ret = true;
1790528c2a7SPekka Enberg 
180*407475bfSPekka Enberg 	virtio_blk_port2dev(port, IOPORT_VIRTIO_BLK, IOPORT_VIRTIO_BLK_SIZE, &dev_idx, &offset);
181fbc2fbf9SPekka Enberg 
182fe2a70d1SSasha Levin 	bdev = bdevs[dev_idx];
1834749e795SSasha Levin 
184fe2a70d1SSasha Levin 	mutex_lock(&bdev->mutex);
185fbc2fbf9SPekka Enberg 
186fbc2fbf9SPekka Enberg 	switch (offset) {
187fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_GUEST_FEATURES:
188fe2a70d1SSasha Levin 		bdev->guest_features	= ioport__read32(data);
189fbc2fbf9SPekka Enberg 		break;
19010eca11dSPekka Enberg 	case VIRTIO_PCI_QUEUE_PFN: {
19110eca11dSPekka Enberg 		struct virt_queue *queue;
192fe2a70d1SSasha Levin 		struct blk_dev_job *job;
19310eca11dSPekka Enberg 		void *p;
19410eca11dSPekka Enberg 
195fe2a70d1SSasha Levin 		job = &bdev->jobs[bdev->queue_selector];
19610eca11dSPekka Enberg 
197fe2a70d1SSasha Levin 		queue			= &bdev->vqs[bdev->queue_selector];
19810eca11dSPekka Enberg 		queue->pfn		= ioport__read32(data);
19910eca11dSPekka Enberg 		p			= guest_flat_to_host(self, queue->pfn << 12);
20010eca11dSPekka Enberg 
20110eca11dSPekka Enberg 		vring_init(&queue->vring, VIRTIO_BLK_QUEUE_SIZE, p, 4096);
20210eca11dSPekka Enberg 
203fe2a70d1SSasha Levin 		*job			= (struct blk_dev_job) {
2044749e795SSasha Levin 			.vq			= queue,
205fe2a70d1SSasha Levin 			.bdev			= bdev,
2064749e795SSasha Levin 		};
2074749e795SSasha Levin 
2084749e795SSasha Levin 		job->job_id = thread_pool__add_job(self, virtio_blk_do_io, job);
209fb0957f2SSasha Levin 
2107e61688eSPekka Enberg 		break;
21110eca11dSPekka Enberg 	}
212fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_QUEUE_SEL:
213fe2a70d1SSasha Levin 		bdev->queue_selector	= ioport__read16(data);
2147e61688eSPekka Enberg 		break;
21510eca11dSPekka Enberg 	case VIRTIO_PCI_QUEUE_NOTIFY: {
2163fdf659dSSasha Levin 		u16 queue_index;
217*407475bfSPekka Enberg 
21810eca11dSPekka Enberg 		queue_index		= ioport__read16(data);
219fe2a70d1SSasha Levin 		thread_pool__do_job(bdev->jobs[queue_index].job_id);
220*407475bfSPekka Enberg 
2217e61688eSPekka Enberg 		break;
22210eca11dSPekka Enberg 	}
223fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_STATUS:
224fe2a70d1SSasha Levin 		bdev->status		= ioport__read8(data);
225fbc2fbf9SPekka Enberg 		break;
226fbc2fbf9SPekka Enberg 	case VIRTIO_MSI_CONFIG_VECTOR:
227fe2a70d1SSasha Levin 		bdev->config_vector	= VIRTIO_MSI_NO_VECTOR;
22840ce993fSPekka Enberg 		break;
229fbc2fbf9SPekka Enberg 	case VIRTIO_MSI_QUEUE_VECTOR:
23040ce993fSPekka Enberg 		break;
231fbc2fbf9SPekka Enberg 	default:
2320528c2a7SPekka Enberg 		ret			= false;
233*407475bfSPekka Enberg 		break;
234fbc2fbf9SPekka Enberg 	};
235fbc2fbf9SPekka Enberg 
236fe2a70d1SSasha Levin 	mutex_unlock(&bdev->mutex);
2370528c2a7SPekka Enberg 
2380528c2a7SPekka Enberg 	return ret;
239fbc2fbf9SPekka Enberg }
240fbc2fbf9SPekka Enberg 
241416b2c2dSAsias He static struct ioport_operations virtio_blk_io_ops = {
242416b2c2dSAsias He 	.io_in		= virtio_blk_pci_io_in,
243416b2c2dSAsias He 	.io_out		= virtio_blk_pci_io_out,
244fbc2fbf9SPekka Enberg };
245fbc2fbf9SPekka Enberg 
246b30d05adSPekka Enberg #define PCI_VENDOR_ID_REDHAT_QUMRANET		0x1af4
247b30d05adSPekka Enberg #define PCI_DEVICE_ID_VIRTIO_BLK		0x1001
248b30d05adSPekka Enberg #define PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET	0x1af4
249b30d05adSPekka Enberg #define PCI_SUBSYSTEM_ID_VIRTIO_BLK		0x0002
2504749e795SSasha Levin #define PCI_VIRTIO_BLK_DEVNUM 10
251b30d05adSPekka Enberg 
2524749e795SSasha Levin static int virtio_blk_find_empty_dev(void)
2534749e795SSasha Levin {
2544749e795SSasha Levin 	int i;
2554749e795SSasha Levin 
2564749e795SSasha Levin 	for (i = 0; i < VIRTIO_BLK_MAX_DEV; i++) {
257fe2a70d1SSasha Levin 		if (bdevs[i] == NULL)
2584749e795SSasha Levin 			return i;
2594749e795SSasha Levin 	}
2604749e795SSasha Levin 
2614749e795SSasha Levin 	return -1;
2624749e795SSasha Levin }
2634749e795SSasha Levin 
2644749e795SSasha Levin void virtio_blk__init(struct kvm *self, struct disk_image *disk)
2654749e795SSasha Levin {
2664749e795SSasha Levin 	u16 blk_dev_base_addr;
267fe2a70d1SSasha Levin 	struct blk_dev *bdev;
268*407475bfSPekka Enberg 	int new_dev_idx;
2694749e795SSasha Levin 
2704749e795SSasha Levin 	if (!disk)
2714749e795SSasha Levin 		return;
2724749e795SSasha Levin 
2734749e795SSasha Levin 	new_dev_idx		= virtio_blk_find_empty_dev();
2744749e795SSasha Levin 	if (new_dev_idx < 0)
2754749e795SSasha Levin 		die("Could not find an empty block device slot");
2764749e795SSasha Levin 
277fe2a70d1SSasha Levin 	bdevs[new_dev_idx]	= calloc(1, sizeof(struct blk_dev));
278fe2a70d1SSasha Levin 	if (bdevs[new_dev_idx] == NULL)
279fe2a70d1SSasha Levin 		die("Failed allocating bdev");
2804749e795SSasha Levin 
281fe2a70d1SSasha Levin 	bdev			= bdevs[new_dev_idx];
282fe2a70d1SSasha Levin 
2834749e795SSasha Levin 	blk_dev_base_addr	= IOPORT_VIRTIO_BLK + new_dev_idx * IOPORT_VIRTIO_BLK_SIZE;
2844749e795SSasha Levin 
285fe2a70d1SSasha Levin 	*bdev			= (struct blk_dev) {
2864749e795SSasha Levin 		.mutex			= PTHREAD_MUTEX_INITIALIZER,
2874749e795SSasha Levin 		.disk			= disk,
2884749e795SSasha Levin 		.idx			= new_dev_idx,
2894749e795SSasha Levin 		.blk_config		= (struct virtio_blk_config) {
2904749e795SSasha Levin 			.capacity		= disk->size / SECTOR_SIZE,
2914749e795SSasha Levin 		},
2924749e795SSasha Levin 		.pci_device = (struct pci_device_header) {
293b30d05adSPekka Enberg 			.vendor_id		= PCI_VENDOR_ID_REDHAT_QUMRANET,
294b30d05adSPekka Enberg 			.device_id		= PCI_DEVICE_ID_VIRTIO_BLK,
295b30d05adSPekka Enberg 			.header_type		= PCI_HEADER_TYPE_NORMAL,
296b30d05adSPekka Enberg 			.revision_id		= 0,
297b30d05adSPekka Enberg 			.class			= 0x010000,
298b30d05adSPekka Enberg 			.subsys_vendor_id	= PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET,
299b30d05adSPekka Enberg 			.subsys_id		= PCI_SUBSYSTEM_ID_VIRTIO_BLK,
3004749e795SSasha Levin 			.bar[0]			= blk_dev_base_addr | PCI_BASE_ADDRESS_SPACE_IO,
301bc0363c8SCyrill Gorcunov 			.irq_pin		= VIRTIO_BLK_PIN,
3024749e795SSasha Levin 			.irq_line		= VIRTIO_BLK_IRQ + new_dev_idx,
3034749e795SSasha Levin 		},
304b30d05adSPekka Enberg 	};
305b30d05adSPekka Enberg 
306fe2a70d1SSasha Levin 	pci__register(&bdev->pci_device, PCI_VIRTIO_BLK_DEVNUM + new_dev_idx);
307f05bbe8dSAsias He 
3084749e795SSasha Levin 	ioport__register(blk_dev_base_addr, &virtio_blk_io_ops, IOPORT_VIRTIO_BLK_SIZE);
309b30d05adSPekka Enberg }
310