xref: /kvmtool/virtio/blk.c (revision 10eca11d7288f472fb6326d92274406d1e5dbb34)
1b30d05adSPekka Enberg #include "kvm/blk-virtio.h"
2b30d05adSPekka Enberg 
3*10eca11dSPekka Enberg #include "kvm/virtio_ring.h"
4c435b91dSPekka Enberg #include "kvm/virtio_blk.h"
5984b7ae0SPekka Enberg #include "kvm/virtio_pci.h"
6b30d05adSPekka Enberg #include "kvm/ioport.h"
78b1ff07eSPekka Enberg #include "kvm/kvm.h"
8b30d05adSPekka Enberg #include "kvm/pci.h"
9b30d05adSPekka Enberg 
108b1ff07eSPekka Enberg #define VIRTIO_BLK_IRQ		14
11984b7ae0SPekka Enberg 
12*10eca11dSPekka Enberg #define NUM_VIRT_QUEUES		1
13*10eca11dSPekka Enberg 
14*10eca11dSPekka Enberg #define VIRTIO_BLK_QUEUE_SIZE	16
15*10eca11dSPekka Enberg 
16*10eca11dSPekka Enberg struct virt_queue {
17*10eca11dSPekka Enberg 	uint32_t			pfn;
18*10eca11dSPekka Enberg 	struct vring			vring;
19*10eca11dSPekka Enberg };
20*10eca11dSPekka Enberg 
21fbc2fbf9SPekka Enberg struct device {
2240ce993fSPekka Enberg 	struct virtio_blk_config	blk_config;
23c435b91dSPekka Enberg 	uint32_t			host_features;
24fbc2fbf9SPekka Enberg 	uint32_t			guest_features;
2540ce993fSPekka Enberg 	uint16_t			config_vector;
26fbc2fbf9SPekka Enberg 	uint8_t				status;
2747bf1d0fSPekka Enberg 
2847bf1d0fSPekka Enberg 	/* virtio queue */
2947bf1d0fSPekka Enberg 	uint16_t			queue_selector;
30*10eca11dSPekka Enberg 
31*10eca11dSPekka Enberg 	struct virt_queue		virt_queues[NUM_VIRT_QUEUES];
32fbc2fbf9SPekka Enberg };
33fbc2fbf9SPekka Enberg 
3440ce993fSPekka Enberg #define DISK_CYLINDERS	1024
3540ce993fSPekka Enberg #define DISK_HEADS	64
3640ce993fSPekka Enberg #define DISK_SECTORS	32
3740ce993fSPekka Enberg 
38c435b91dSPekka Enberg static struct device device = {
3940ce993fSPekka Enberg 	.blk_config		= (struct virtio_blk_config) {
4040ce993fSPekka Enberg 		.capacity		= DISK_CYLINDERS * DISK_HEADS * DISK_SECTORS,
4140ce993fSPekka Enberg 		/* VIRTIO_BLK_F_GEOMETRY */
4240ce993fSPekka Enberg 		.geometry		= {
4340ce993fSPekka Enberg 			.cylinders		= DISK_CYLINDERS,
4440ce993fSPekka Enberg 			.heads			= DISK_HEADS,
4540ce993fSPekka Enberg 			.sectors		= DISK_SECTORS,
4640ce993fSPekka Enberg 		},
4740ce993fSPekka Enberg 		/* VIRTIO_BLK_SIZE */
4840ce993fSPekka Enberg 		.blk_size		= 4096,
4940ce993fSPekka Enberg 	},
5098790f28SPekka Enberg 	.host_features		= (1UL << VIRTIO_BLK_F_GEOMETRY)
51cb938db9SPekka Enberg 				| (1UL << VIRTIO_BLK_F_RO)
52c435b91dSPekka Enberg 				| (1UL << VIRTIO_BLK_F_BLK_SIZE),
53c435b91dSPekka Enberg };
54fbc2fbf9SPekka Enberg 
5540ce993fSPekka Enberg static bool virtio_blk_config_in(void *data, unsigned long offset, int size, uint32_t count)
5640ce993fSPekka Enberg {
5740ce993fSPekka Enberg 	uint8_t *config_space = (uint8_t *) &device.blk_config;
5840ce993fSPekka Enberg 
5940ce993fSPekka Enberg 	if (size != 1 || count != 1)
6040ce993fSPekka Enberg 		return false;
6140ce993fSPekka Enberg 
6240ce993fSPekka Enberg 	ioport__write8(data, config_space[offset - VIRTIO_PCI_CONFIG_NOMSI]);
6340ce993fSPekka Enberg 
6440ce993fSPekka Enberg 	return true;
6540ce993fSPekka Enberg }
6640ce993fSPekka Enberg 
67fbc2fbf9SPekka Enberg static bool blk_virtio_in(struct kvm *self, uint16_t port, void *data, int size, uint32_t count)
68fbc2fbf9SPekka Enberg {
69fbc2fbf9SPekka Enberg 	unsigned long offset;
70fbc2fbf9SPekka Enberg 
71fbc2fbf9SPekka Enberg 	offset		= port - IOPORT_VIRTIO;
72fbc2fbf9SPekka Enberg 
73fbc2fbf9SPekka Enberg 	switch (offset) {
74fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_HOST_FEATURES:
75c435b91dSPekka Enberg 		ioport__write32(data, device.host_features);
76fbc2fbf9SPekka Enberg 		break;
77fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_GUEST_FEATURES:
788b1ff07eSPekka Enberg 		return false;
79fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_QUEUE_PFN:
80*10eca11dSPekka Enberg 		ioport__write32(data, device.virt_queues[device.queue_selector].pfn);
818b1ff07eSPekka Enberg 		break;
82fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_QUEUE_NUM:
83*10eca11dSPekka Enberg 		ioport__write16(data, VIRTIO_BLK_QUEUE_SIZE);
848b1ff07eSPekka Enberg 		break;
85fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_QUEUE_SEL:
86fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_QUEUE_NOTIFY:
87fbc2fbf9SPekka Enberg 		return false;
88fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_STATUS:
89fbc2fbf9SPekka Enberg 		ioport__write8(data, device.status);
90fbc2fbf9SPekka Enberg 		break;
91fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_ISR:
928b1ff07eSPekka Enberg 		ioport__write8(data, 0x1);
938b1ff07eSPekka Enberg 		kvm__irq_line(self, VIRTIO_BLK_IRQ, 0);
947e61688eSPekka Enberg 		break;
95fbc2fbf9SPekka Enberg 	case VIRTIO_MSI_CONFIG_VECTOR:
9640ce993fSPekka Enberg 		ioport__write16(data, device.config_vector);
9740ce993fSPekka Enberg 		break;
98fbc2fbf9SPekka Enberg 	default:
9940ce993fSPekka Enberg 		return virtio_blk_config_in(data, offset, size, count);
100fbc2fbf9SPekka Enberg 	};
101fbc2fbf9SPekka Enberg 
102fbc2fbf9SPekka Enberg 	return true;
103fbc2fbf9SPekka Enberg }
104fbc2fbf9SPekka Enberg 
105fbc2fbf9SPekka Enberg static bool blk_virtio_out(struct kvm *self, uint16_t port, void *data, int size, uint32_t count)
106fbc2fbf9SPekka Enberg {
107fbc2fbf9SPekka Enberg 	unsigned long offset;
108fbc2fbf9SPekka Enberg 
109fbc2fbf9SPekka Enberg 	offset		= port - IOPORT_VIRTIO;
110fbc2fbf9SPekka Enberg 
111fbc2fbf9SPekka Enberg 	switch (offset) {
112fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_GUEST_FEATURES:
113fbc2fbf9SPekka Enberg 		device.guest_features	= ioport__read32(data);
114fbc2fbf9SPekka Enberg 		break;
115*10eca11dSPekka Enberg 	case VIRTIO_PCI_QUEUE_PFN: {
116*10eca11dSPekka Enberg 		struct virt_queue *queue;
117*10eca11dSPekka Enberg 		void *p;
118*10eca11dSPekka Enberg 
119*10eca11dSPekka Enberg 		queue			= &device.virt_queues[device.queue_selector];
120*10eca11dSPekka Enberg 
121*10eca11dSPekka Enberg 		queue->pfn		= ioport__read32(data);
122*10eca11dSPekka Enberg 
123*10eca11dSPekka Enberg 		p			= guest_flat_to_host(self, queue->pfn << 12);
124*10eca11dSPekka Enberg 
125*10eca11dSPekka Enberg 		vring_init(&queue->vring, VIRTIO_BLK_QUEUE_SIZE, p, 4096);
126*10eca11dSPekka Enberg 
1277e61688eSPekka Enberg 		break;
128*10eca11dSPekka Enberg 	}
129fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_QUEUE_SEL:
13047bf1d0fSPekka Enberg 		device.queue_selector	= ioport__read16(data);
1317e61688eSPekka Enberg 		break;
132*10eca11dSPekka Enberg 	case VIRTIO_PCI_QUEUE_NOTIFY: {
133*10eca11dSPekka Enberg 		struct virt_queue *queue;
134*10eca11dSPekka Enberg 		uint16_t queue_index;
135*10eca11dSPekka Enberg 
136*10eca11dSPekka Enberg 		queue_index		= ioport__read16(data);
137*10eca11dSPekka Enberg 
138*10eca11dSPekka Enberg 		queue			= &device.virt_queues[queue_index];
139*10eca11dSPekka Enberg 
1408b1ff07eSPekka Enberg 		kvm__irq_line(self, VIRTIO_BLK_IRQ, 1);
1417e61688eSPekka Enberg 		break;
142*10eca11dSPekka Enberg 	}
143fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_STATUS:
144fbc2fbf9SPekka Enberg 		device.status		= ioport__read8(data);
145fbc2fbf9SPekka Enberg 		break;
146fbc2fbf9SPekka Enberg 	case VIRTIO_MSI_CONFIG_VECTOR:
14740ce993fSPekka Enberg 		device.config_vector	= VIRTIO_MSI_NO_VECTOR;
14840ce993fSPekka Enberg 		break;
149fbc2fbf9SPekka Enberg 	case VIRTIO_MSI_QUEUE_VECTOR:
15040ce993fSPekka Enberg 		break;
151fbc2fbf9SPekka Enberg 	default:
152fbc2fbf9SPekka Enberg 		return false;
153fbc2fbf9SPekka Enberg 	};
154fbc2fbf9SPekka Enberg 
155fbc2fbf9SPekka Enberg 	return true;
156fbc2fbf9SPekka Enberg }
157fbc2fbf9SPekka Enberg 
158fbc2fbf9SPekka Enberg static struct ioport_operations blk_virtio_io_ops = {
159fbc2fbf9SPekka Enberg 	.io_in		= blk_virtio_in,
160fbc2fbf9SPekka Enberg 	.io_out		= blk_virtio_out,
161fbc2fbf9SPekka Enberg };
162fbc2fbf9SPekka Enberg 
163b30d05adSPekka Enberg #define PCI_VENDOR_ID_REDHAT_QUMRANET		0x1af4
164b30d05adSPekka Enberg #define PCI_DEVICE_ID_VIRTIO_BLK		0x1001
165b30d05adSPekka Enberg #define PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET	0x1af4
166b30d05adSPekka Enberg #define PCI_SUBSYSTEM_ID_VIRTIO_BLK		0x0002
167b30d05adSPekka Enberg 
168fbc2fbf9SPekka Enberg static struct pci_device_header blk_virtio_pci_device = {
169b30d05adSPekka Enberg 	.vendor_id		= PCI_VENDOR_ID_REDHAT_QUMRANET,
170b30d05adSPekka Enberg 	.device_id		= PCI_DEVICE_ID_VIRTIO_BLK,
171b30d05adSPekka Enberg 	.header_type		= PCI_HEADER_TYPE_NORMAL,
172b30d05adSPekka Enberg 	.revision_id		= 0,
173b30d05adSPekka Enberg 	.class			= 0x010000,
174b30d05adSPekka Enberg 	.subsys_vendor_id	= PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET,
175b30d05adSPekka Enberg 	.subsys_id		= PCI_SUBSYSTEM_ID_VIRTIO_BLK,
176b30d05adSPekka Enberg 	.bar[0]			= IOPORT_VIRTIO | PCI_BASE_ADDRESS_SPACE_IO,
177dc53a427SPekka Enberg 	/* XXX: Is this IRQ setup OK? */
178dc53a427SPekka Enberg 	.irq_pin		= 1,
1798b1ff07eSPekka Enberg 	.irq_line		= VIRTIO_BLK_IRQ,
180b30d05adSPekka Enberg };
181b30d05adSPekka Enberg 
182b30d05adSPekka Enberg void blk_virtio__init(void)
183b30d05adSPekka Enberg {
184fbc2fbf9SPekka Enberg 	pci__register(&blk_virtio_pci_device, 1);
185b30d05adSPekka Enberg 
1868b1ff07eSPekka Enberg 	ioport__register(IOPORT_VIRTIO, &blk_virtio_io_ops, 256);
187b30d05adSPekka Enberg }
188