xref: /kvmtool/virtio/blk.c (revision 1ef2738d03969936ba3d974a5a2fa9b5a8fcaa30)
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 
128b1ff07eSPekka Enberg #define VIRTIO_BLK_IRQ		14
13984b7ae0SPekka Enberg 
1410eca11dSPekka Enberg #define NUM_VIRT_QUEUES		1
1510eca11dSPekka Enberg 
1610eca11dSPekka Enberg #define VIRTIO_BLK_QUEUE_SIZE	16
1710eca11dSPekka Enberg 
1810eca11dSPekka Enberg struct virt_queue {
1910eca11dSPekka Enberg 	struct vring			vring;
20fe99fd4eSPekka Enberg 	uint32_t			pfn;
21fe99fd4eSPekka Enberg 	/* The next_avail_ndx field is an index to ->ring of struct vring_avail.
22fe99fd4eSPekka Enberg 	   It's where we assume the next request index is at.  */
23fe99fd4eSPekka Enberg 	uint16_t			next_avail_ndx;
2410eca11dSPekka Enberg };
2510eca11dSPekka Enberg 
26fbc2fbf9SPekka Enberg struct device {
2740ce993fSPekka Enberg 	struct virtio_blk_config	blk_config;
28c435b91dSPekka Enberg 	uint32_t			host_features;
29fbc2fbf9SPekka Enberg 	uint32_t			guest_features;
3040ce993fSPekka Enberg 	uint16_t			config_vector;
31fbc2fbf9SPekka Enberg 	uint8_t				status;
3247bf1d0fSPekka Enberg 
3347bf1d0fSPekka Enberg 	/* virtio queue */
3447bf1d0fSPekka Enberg 	uint16_t			queue_selector;
3510eca11dSPekka Enberg 
3610eca11dSPekka Enberg 	struct virt_queue		virt_queues[NUM_VIRT_QUEUES];
37fbc2fbf9SPekka Enberg };
38fbc2fbf9SPekka Enberg 
3940ce993fSPekka Enberg #define DISK_CYLINDERS	1024
4040ce993fSPekka Enberg #define DISK_HEADS	64
4140ce993fSPekka Enberg #define DISK_SECTORS	32
4240ce993fSPekka Enberg 
43c435b91dSPekka Enberg static struct device device = {
4440ce993fSPekka Enberg 	.blk_config		= (struct virtio_blk_config) {
4540ce993fSPekka Enberg 		.capacity		= DISK_CYLINDERS * DISK_HEADS * DISK_SECTORS,
4640ce993fSPekka Enberg 		/* VIRTIO_BLK_F_GEOMETRY */
4740ce993fSPekka Enberg 		.geometry		= {
4840ce993fSPekka Enberg 			.cylinders		= DISK_CYLINDERS,
4940ce993fSPekka Enberg 			.heads			= DISK_HEADS,
5040ce993fSPekka Enberg 			.sectors		= DISK_SECTORS,
5140ce993fSPekka Enberg 		},
5240ce993fSPekka Enberg 		/* VIRTIO_BLK_SIZE */
5340ce993fSPekka Enberg 		.blk_size		= 4096,
5440ce993fSPekka Enberg 	},
55*1ef2738dSCyrill Gorcunov 	/*
56*1ef2738dSCyrill Gorcunov 	 * Note we don't set VIRTIO_BLK_F_GEOMETRY here so the
57*1ef2738dSCyrill Gorcunov 	 * node kernel will compute disk geometry by own, the
58*1ef2738dSCyrill Gorcunov 	 * same applies to VIRTIO_BLK_F_BLK_SIZE
59*1ef2738dSCyrill Gorcunov 	 */
60*1ef2738dSCyrill Gorcunov 	.host_features		= (1UL << VIRTIO_BLK_F_RO),
61c435b91dSPekka Enberg };
62fbc2fbf9SPekka Enberg 
6340ce993fSPekka Enberg static bool virtio_blk_config_in(void *data, unsigned long offset, int size, uint32_t count)
6440ce993fSPekka Enberg {
6540ce993fSPekka Enberg 	uint8_t *config_space = (uint8_t *) &device.blk_config;
6640ce993fSPekka Enberg 
6740ce993fSPekka Enberg 	if (size != 1 || count != 1)
6840ce993fSPekka Enberg 		return false;
6940ce993fSPekka Enberg 
7040ce993fSPekka Enberg 	ioport__write8(data, config_space[offset - VIRTIO_PCI_CONFIG_NOMSI]);
7140ce993fSPekka Enberg 
7240ce993fSPekka Enberg 	return true;
7340ce993fSPekka Enberg }
7440ce993fSPekka Enberg 
75fbc2fbf9SPekka Enberg static bool blk_virtio_in(struct kvm *self, uint16_t port, void *data, int size, uint32_t count)
76fbc2fbf9SPekka Enberg {
77fbc2fbf9SPekka Enberg 	unsigned long offset;
78fbc2fbf9SPekka Enberg 
79fbc2fbf9SPekka Enberg 	offset		= port - IOPORT_VIRTIO;
80fbc2fbf9SPekka Enberg 
81fbc2fbf9SPekka Enberg 	switch (offset) {
82fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_HOST_FEATURES:
83c435b91dSPekka Enberg 		ioport__write32(data, device.host_features);
84fbc2fbf9SPekka Enberg 		break;
85fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_GUEST_FEATURES:
868b1ff07eSPekka Enberg 		return false;
87fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_QUEUE_PFN:
8810eca11dSPekka Enberg 		ioport__write32(data, device.virt_queues[device.queue_selector].pfn);
898b1ff07eSPekka Enberg 		break;
90fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_QUEUE_NUM:
9110eca11dSPekka Enberg 		ioport__write16(data, VIRTIO_BLK_QUEUE_SIZE);
928b1ff07eSPekka Enberg 		break;
93fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_QUEUE_SEL:
94fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_QUEUE_NOTIFY:
95fbc2fbf9SPekka Enberg 		return false;
96fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_STATUS:
97fbc2fbf9SPekka Enberg 		ioport__write8(data, device.status);
98fbc2fbf9SPekka Enberg 		break;
99fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_ISR:
1008b1ff07eSPekka Enberg 		ioport__write8(data, 0x1);
1018b1ff07eSPekka Enberg 		kvm__irq_line(self, VIRTIO_BLK_IRQ, 0);
1027e61688eSPekka Enberg 		break;
103fbc2fbf9SPekka Enberg 	case VIRTIO_MSI_CONFIG_VECTOR:
10440ce993fSPekka Enberg 		ioport__write16(data, device.config_vector);
10540ce993fSPekka Enberg 		break;
106fbc2fbf9SPekka Enberg 	default:
10740ce993fSPekka Enberg 		return virtio_blk_config_in(data, offset, size, count);
108fbc2fbf9SPekka Enberg 	};
109fbc2fbf9SPekka Enberg 
110fbc2fbf9SPekka Enberg 	return true;
111fbc2fbf9SPekka Enberg }
112fbc2fbf9SPekka Enberg 
113fbc2fbf9SPekka Enberg static bool blk_virtio_out(struct kvm *self, uint16_t port, void *data, int size, uint32_t count)
114fbc2fbf9SPekka Enberg {
115fbc2fbf9SPekka Enberg 	unsigned long offset;
116fbc2fbf9SPekka Enberg 
117fbc2fbf9SPekka Enberg 	offset		= port - IOPORT_VIRTIO;
118fbc2fbf9SPekka Enberg 
119fbc2fbf9SPekka Enberg 	switch (offset) {
120fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_GUEST_FEATURES:
121fbc2fbf9SPekka Enberg 		device.guest_features	= ioport__read32(data);
122fbc2fbf9SPekka Enberg 		break;
12310eca11dSPekka Enberg 	case VIRTIO_PCI_QUEUE_PFN: {
12410eca11dSPekka Enberg 		struct virt_queue *queue;
12510eca11dSPekka Enberg 		void *p;
12610eca11dSPekka Enberg 
12710eca11dSPekka Enberg 		queue			= &device.virt_queues[device.queue_selector];
12810eca11dSPekka Enberg 
12910eca11dSPekka Enberg 		queue->pfn		= ioport__read32(data);
13010eca11dSPekka Enberg 
13110eca11dSPekka Enberg 		p			= guest_flat_to_host(self, queue->pfn << 12);
13210eca11dSPekka Enberg 
13310eca11dSPekka Enberg 		vring_init(&queue->vring, VIRTIO_BLK_QUEUE_SIZE, p, 4096);
13410eca11dSPekka Enberg 
1357e61688eSPekka Enberg 		break;
13610eca11dSPekka Enberg 	}
137fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_QUEUE_SEL:
13847bf1d0fSPekka Enberg 		device.queue_selector	= ioport__read16(data);
1397e61688eSPekka Enberg 		break;
14010eca11dSPekka Enberg 	case VIRTIO_PCI_QUEUE_NOTIFY: {
141fe99fd4eSPekka Enberg 		struct virtio_blk_outhdr *req;
14210eca11dSPekka Enberg 		struct virt_queue *queue;
143fe99fd4eSPekka Enberg 		struct vring_desc *desc;
14410eca11dSPekka Enberg 		uint16_t queue_index;
145fe99fd4eSPekka Enberg 		uint16_t desc_ndx;
1465a24a9f2SPekka Enberg 		uint32_t dst_len;
1475a24a9f2SPekka Enberg 		uint8_t *status;
1485a24a9f2SPekka Enberg 		void *dst;
14910eca11dSPekka Enberg 
15010eca11dSPekka Enberg 		queue_index		= ioport__read16(data);
15110eca11dSPekka Enberg 
15210eca11dSPekka Enberg 		queue			= &device.virt_queues[queue_index];
15310eca11dSPekka Enberg 
154fe99fd4eSPekka Enberg 		desc_ndx		= queue->vring.avail->ring[queue->next_avail_ndx++ % queue->vring.num];
155fe99fd4eSPekka Enberg 
156fe99fd4eSPekka Enberg 		if (queue->vring.avail->idx != queue->next_avail_ndx) {
157fe99fd4eSPekka Enberg 			/*
158fe99fd4eSPekka Enberg 			 * The hypervisor and the guest disagree on next index.
159fe99fd4eSPekka Enberg 			 */
160fe99fd4eSPekka Enberg 			warning("I/O error");
161fe99fd4eSPekka Enberg 			break;
162fe99fd4eSPekka Enberg 		}
163fe99fd4eSPekka Enberg 
1645a24a9f2SPekka Enberg 		/* header */
165fe99fd4eSPekka Enberg 		desc			= &queue->vring.desc[desc_ndx];
166fe99fd4eSPekka Enberg 
167fe99fd4eSPekka Enberg 		req			= guest_flat_to_host(self, desc->addr);
168fe99fd4eSPekka Enberg 
1695a24a9f2SPekka Enberg 		/* block */
1705a24a9f2SPekka Enberg 		desc			= &queue->vring.desc[desc->next];
1715a24a9f2SPekka Enberg 
1725a24a9f2SPekka Enberg 		dst			= guest_flat_to_host(self, desc->addr);
1735a24a9f2SPekka Enberg 		dst_len			= desc->len;
1745a24a9f2SPekka Enberg 
1755a24a9f2SPekka Enberg 		/* status */
1765a24a9f2SPekka Enberg 		desc			= &queue->vring.desc[desc->next];
1775a24a9f2SPekka Enberg 
1785a24a9f2SPekka Enberg 		status			= guest_flat_to_host(self, desc->addr);
1795a24a9f2SPekka Enberg 
1805a24a9f2SPekka Enberg 		if (self->disk_image) {
1815a24a9f2SPekka Enberg 			int err;
1825a24a9f2SPekka Enberg 
1835a24a9f2SPekka Enberg 			err = disk_image__read_sector(self->disk_image, req->sector, dst, dst_len);
1845a24a9f2SPekka Enberg 
1855a24a9f2SPekka Enberg 			if (err)
1865a24a9f2SPekka Enberg 				*status			= VIRTIO_BLK_S_IOERR;
1875a24a9f2SPekka Enberg 			else
1885a24a9f2SPekka Enberg 				*status			= VIRTIO_BLK_S_OK;
1895a24a9f2SPekka Enberg 		} else
1905a24a9f2SPekka Enberg 			*status			= VIRTIO_BLK_S_IOERR;
1915a24a9f2SPekka Enberg 
1925a24a9f2SPekka Enberg 		queue->vring.used->idx++;
1935a24a9f2SPekka Enberg 
1948b1ff07eSPekka Enberg 		kvm__irq_line(self, VIRTIO_BLK_IRQ, 1);
1955a24a9f2SPekka Enberg 
1967e61688eSPekka Enberg 		break;
19710eca11dSPekka Enberg 	}
198fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_STATUS:
199fbc2fbf9SPekka Enberg 		device.status		= ioport__read8(data);
200fbc2fbf9SPekka Enberg 		break;
201fbc2fbf9SPekka Enberg 	case VIRTIO_MSI_CONFIG_VECTOR:
20240ce993fSPekka Enberg 		device.config_vector	= VIRTIO_MSI_NO_VECTOR;
20340ce993fSPekka Enberg 		break;
204fbc2fbf9SPekka Enberg 	case VIRTIO_MSI_QUEUE_VECTOR:
20540ce993fSPekka Enberg 		break;
206fbc2fbf9SPekka Enberg 	default:
207fbc2fbf9SPekka Enberg 		return false;
208fbc2fbf9SPekka Enberg 	};
209fbc2fbf9SPekka Enberg 
210fbc2fbf9SPekka Enberg 	return true;
211fbc2fbf9SPekka Enberg }
212fbc2fbf9SPekka Enberg 
213fbc2fbf9SPekka Enberg static struct ioport_operations blk_virtio_io_ops = {
214fbc2fbf9SPekka Enberg 	.io_in		= blk_virtio_in,
215fbc2fbf9SPekka Enberg 	.io_out		= blk_virtio_out,
216fbc2fbf9SPekka Enberg };
217fbc2fbf9SPekka Enberg 
218b30d05adSPekka Enberg #define PCI_VENDOR_ID_REDHAT_QUMRANET		0x1af4
219b30d05adSPekka Enberg #define PCI_DEVICE_ID_VIRTIO_BLK		0x1001
220b30d05adSPekka Enberg #define PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET	0x1af4
221b30d05adSPekka Enberg #define PCI_SUBSYSTEM_ID_VIRTIO_BLK		0x0002
222b30d05adSPekka Enberg 
223fbc2fbf9SPekka Enberg static struct pci_device_header blk_virtio_pci_device = {
224b30d05adSPekka Enberg 	.vendor_id		= PCI_VENDOR_ID_REDHAT_QUMRANET,
225b30d05adSPekka Enberg 	.device_id		= PCI_DEVICE_ID_VIRTIO_BLK,
226b30d05adSPekka Enberg 	.header_type		= PCI_HEADER_TYPE_NORMAL,
227b30d05adSPekka Enberg 	.revision_id		= 0,
228b30d05adSPekka Enberg 	.class			= 0x010000,
229b30d05adSPekka Enberg 	.subsys_vendor_id	= PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET,
230b30d05adSPekka Enberg 	.subsys_id		= PCI_SUBSYSTEM_ID_VIRTIO_BLK,
231b30d05adSPekka Enberg 	.bar[0]			= IOPORT_VIRTIO | PCI_BASE_ADDRESS_SPACE_IO,
232dc53a427SPekka Enberg 	.irq_pin		= 1,
2338b1ff07eSPekka Enberg 	.irq_line		= VIRTIO_BLK_IRQ,
234b30d05adSPekka Enberg };
235b30d05adSPekka Enberg 
236ca7c891bSCyrill Gorcunov void blk_virtio__init(struct kvm *self)
237b30d05adSPekka Enberg {
238*1ef2738dSCyrill Gorcunov 	if (self->disk_image)
239*1ef2738dSCyrill Gorcunov 		device.blk_config.capacity = self->disk_image->size;
240ca7c891bSCyrill Gorcunov 
241fbc2fbf9SPekka Enberg 	pci__register(&blk_virtio_pci_device, 1);
242b30d05adSPekka Enberg 
2438b1ff07eSPekka Enberg 	ioport__register(IOPORT_VIRTIO, &blk_virtio_io_ops, 256);
244b30d05adSPekka Enberg }
245