xref: /kvmtool/virtio/blk.c (revision 47bf1d0f23b6c1753fe55b3c38edc65c4f9f4aa8)
1b30d05adSPekka Enberg #include "kvm/blk-virtio.h"
2b30d05adSPekka Enberg 
3c435b91dSPekka Enberg #include "kvm/virtio_blk.h"
4984b7ae0SPekka Enberg #include "kvm/virtio_pci.h"
5b30d05adSPekka Enberg #include "kvm/ioport.h"
68b1ff07eSPekka Enberg #include "kvm/kvm.h"
7b30d05adSPekka Enberg #include "kvm/pci.h"
8b30d05adSPekka Enberg 
98b1ff07eSPekka Enberg #define VIRTIO_BLK_IRQ		14
10984b7ae0SPekka Enberg 
11fbc2fbf9SPekka Enberg struct device {
1240ce993fSPekka Enberg 	struct virtio_blk_config	blk_config;
13c435b91dSPekka Enberg 	uint32_t			host_features;
14fbc2fbf9SPekka Enberg 	uint32_t			guest_features;
1540ce993fSPekka Enberg 	uint16_t			config_vector;
16fbc2fbf9SPekka Enberg 	uint8_t				status;
17*47bf1d0fSPekka Enberg 
18*47bf1d0fSPekka Enberg 	/* virtio queue */
19*47bf1d0fSPekka Enberg 	uint32_t			queue_pfn;
20*47bf1d0fSPekka Enberg 	uint16_t			queue_selector;
21fbc2fbf9SPekka Enberg };
22fbc2fbf9SPekka Enberg 
2340ce993fSPekka Enberg #define DISK_CYLINDERS	1024
2440ce993fSPekka Enberg #define DISK_HEADS	64
2540ce993fSPekka Enberg #define DISK_SECTORS	32
2640ce993fSPekka Enberg 
27c435b91dSPekka Enberg static struct device device = {
2840ce993fSPekka Enberg 	.blk_config		= (struct virtio_blk_config) {
2940ce993fSPekka Enberg 		.capacity		= DISK_CYLINDERS * DISK_HEADS * DISK_SECTORS,
3040ce993fSPekka Enberg 		/* VIRTIO_BLK_F_GEOMETRY */
3140ce993fSPekka Enberg 		.geometry		= {
3240ce993fSPekka Enberg 			.cylinders		= DISK_CYLINDERS,
3340ce993fSPekka Enberg 			.heads			= DISK_HEADS,
3440ce993fSPekka Enberg 			.sectors		= DISK_SECTORS,
3540ce993fSPekka Enberg 		},
3640ce993fSPekka Enberg 		/* VIRTIO_BLK_SIZE */
3740ce993fSPekka Enberg 		.blk_size		= 4096,
3840ce993fSPekka Enberg 	},
3998790f28SPekka Enberg 	.host_features		= (1UL << VIRTIO_BLK_F_GEOMETRY)
40cb938db9SPekka Enberg 				| (1UL << VIRTIO_BLK_F_RO)
41c435b91dSPekka Enberg 				| (1UL << VIRTIO_BLK_F_BLK_SIZE),
42c435b91dSPekka Enberg };
43fbc2fbf9SPekka Enberg 
4440ce993fSPekka Enberg static bool virtio_blk_config_in(void *data, unsigned long offset, int size, uint32_t count)
4540ce993fSPekka Enberg {
4640ce993fSPekka Enberg 	uint8_t *config_space = (uint8_t *) &device.blk_config;
4740ce993fSPekka Enberg 
4840ce993fSPekka Enberg 	if (size != 1 || count != 1)
4940ce993fSPekka Enberg 		return false;
5040ce993fSPekka Enberg 
5140ce993fSPekka Enberg 	ioport__write8(data, config_space[offset - VIRTIO_PCI_CONFIG_NOMSI]);
5240ce993fSPekka Enberg 
5340ce993fSPekka Enberg 	return true;
5440ce993fSPekka Enberg }
5540ce993fSPekka Enberg 
56fbc2fbf9SPekka Enberg static bool blk_virtio_in(struct kvm *self, uint16_t port, void *data, int size, uint32_t count)
57fbc2fbf9SPekka Enberg {
58fbc2fbf9SPekka Enberg 	unsigned long offset;
59fbc2fbf9SPekka Enberg 
60fbc2fbf9SPekka Enberg 	offset		= port - IOPORT_VIRTIO;
61fbc2fbf9SPekka Enberg 
62fbc2fbf9SPekka Enberg 	switch (offset) {
63fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_HOST_FEATURES:
64c435b91dSPekka Enberg 		ioport__write32(data, device.host_features);
65fbc2fbf9SPekka Enberg 		break;
66fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_GUEST_FEATURES:
678b1ff07eSPekka Enberg 		return false;
68fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_QUEUE_PFN:
698b1ff07eSPekka Enberg 		ioport__write32(data, 0x00);
708b1ff07eSPekka Enberg 		break;
71fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_QUEUE_NUM:
728b1ff07eSPekka Enberg 		ioport__write16(data, 0x10);
738b1ff07eSPekka Enberg 		break;
74fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_QUEUE_SEL:
75fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_QUEUE_NOTIFY:
76fbc2fbf9SPekka Enberg 		return false;
77fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_STATUS:
78fbc2fbf9SPekka Enberg 		ioport__write8(data, device.status);
79fbc2fbf9SPekka Enberg 		break;
80fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_ISR:
818b1ff07eSPekka Enberg 		ioport__write8(data, 0x1);
828b1ff07eSPekka Enberg 		kvm__irq_line(self, VIRTIO_BLK_IRQ, 0);
837e61688eSPekka Enberg 		break;
84fbc2fbf9SPekka Enberg 	case VIRTIO_MSI_CONFIG_VECTOR:
8540ce993fSPekka Enberg 		ioport__write16(data, device.config_vector);
8640ce993fSPekka Enberg 		break;
87fbc2fbf9SPekka Enberg 	default:
8840ce993fSPekka Enberg 		return virtio_blk_config_in(data, offset, size, count);
89fbc2fbf9SPekka Enberg 	};
90fbc2fbf9SPekka Enberg 
91fbc2fbf9SPekka Enberg 	return true;
92fbc2fbf9SPekka Enberg }
93fbc2fbf9SPekka Enberg 
94fbc2fbf9SPekka Enberg static bool blk_virtio_out(struct kvm *self, uint16_t port, void *data, int size, uint32_t count)
95fbc2fbf9SPekka Enberg {
96fbc2fbf9SPekka Enberg 	unsigned long offset;
97fbc2fbf9SPekka Enberg 
98fbc2fbf9SPekka Enberg 	offset		= port - IOPORT_VIRTIO;
99fbc2fbf9SPekka Enberg 
100fbc2fbf9SPekka Enberg 	switch (offset) {
101fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_GUEST_FEATURES:
102fbc2fbf9SPekka Enberg 		device.guest_features	= ioport__read32(data);
103fbc2fbf9SPekka Enberg 		break;
104fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_QUEUE_PFN:
105*47bf1d0fSPekka Enberg 		device.queue_pfn	= ioport__read32(data);
1067e61688eSPekka Enberg 		break;
107fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_QUEUE_SEL:
108*47bf1d0fSPekka Enberg 		device.queue_selector	= ioport__read16(data);
1097e61688eSPekka Enberg 		break;
1107e61688eSPekka Enberg 	case VIRTIO_PCI_QUEUE_NOTIFY:
1118b1ff07eSPekka Enberg 		kvm__irq_line(self, VIRTIO_BLK_IRQ, 1);
1127e61688eSPekka Enberg 		break;
113fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_STATUS:
114fbc2fbf9SPekka Enberg 		device.status		= ioport__read8(data);
115fbc2fbf9SPekka Enberg 		break;
116fbc2fbf9SPekka Enberg 	case VIRTIO_MSI_CONFIG_VECTOR:
11740ce993fSPekka Enberg 		device.config_vector	= VIRTIO_MSI_NO_VECTOR;
11840ce993fSPekka Enberg 		break;
119fbc2fbf9SPekka Enberg 	case VIRTIO_MSI_QUEUE_VECTOR:
12040ce993fSPekka Enberg 		break;
121fbc2fbf9SPekka Enberg 	default:
122fbc2fbf9SPekka Enberg 		return false;
123fbc2fbf9SPekka Enberg 	};
124fbc2fbf9SPekka Enberg 
125fbc2fbf9SPekka Enberg 	return true;
126fbc2fbf9SPekka Enberg }
127fbc2fbf9SPekka Enberg 
128fbc2fbf9SPekka Enberg static struct ioport_operations blk_virtio_io_ops = {
129fbc2fbf9SPekka Enberg 	.io_in		= blk_virtio_in,
130fbc2fbf9SPekka Enberg 	.io_out		= blk_virtio_out,
131fbc2fbf9SPekka Enberg };
132fbc2fbf9SPekka Enberg 
133b30d05adSPekka Enberg #define PCI_VENDOR_ID_REDHAT_QUMRANET		0x1af4
134b30d05adSPekka Enberg #define PCI_DEVICE_ID_VIRTIO_BLK		0x1001
135b30d05adSPekka Enberg #define PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET	0x1af4
136b30d05adSPekka Enberg #define PCI_SUBSYSTEM_ID_VIRTIO_BLK		0x0002
137b30d05adSPekka Enberg 
138fbc2fbf9SPekka Enberg static struct pci_device_header blk_virtio_pci_device = {
139b30d05adSPekka Enberg 	.vendor_id		= PCI_VENDOR_ID_REDHAT_QUMRANET,
140b30d05adSPekka Enberg 	.device_id		= PCI_DEVICE_ID_VIRTIO_BLK,
141b30d05adSPekka Enberg 	.header_type		= PCI_HEADER_TYPE_NORMAL,
142b30d05adSPekka Enberg 	.revision_id		= 0,
143b30d05adSPekka Enberg 	.class			= 0x010000,
144b30d05adSPekka Enberg 	.subsys_vendor_id	= PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET,
145b30d05adSPekka Enberg 	.subsys_id		= PCI_SUBSYSTEM_ID_VIRTIO_BLK,
146b30d05adSPekka Enberg 	.bar[0]			= IOPORT_VIRTIO | PCI_BASE_ADDRESS_SPACE_IO,
147dc53a427SPekka Enberg 	/* XXX: Is this IRQ setup OK? */
148dc53a427SPekka Enberg 	.irq_pin		= 1,
1498b1ff07eSPekka Enberg 	.irq_line		= VIRTIO_BLK_IRQ,
150b30d05adSPekka Enberg };
151b30d05adSPekka Enberg 
152b30d05adSPekka Enberg void blk_virtio__init(void)
153b30d05adSPekka Enberg {
154fbc2fbf9SPekka Enberg 	pci__register(&blk_virtio_pci_device, 1);
155b30d05adSPekka Enberg 
1568b1ff07eSPekka Enberg 	ioport__register(IOPORT_VIRTIO, &blk_virtio_io_ops, 256);
157b30d05adSPekka Enberg }
158