xref: /kvmtool/pci.c (revision 6d1a6f6817901932a666eaf5d493654b7a26d924)
1 #include "kvm/pci.h"
2 #include "kvm/ioport.h"
3 #include "kvm/util.h"
4 
5 #include <stdint.h>
6 
7 static struct pci_config_address pci_config_address;
8 
9 static bool pci_config_address_out(struct kvm *self, uint16_t port, void *data, int size, uint32_t count)
10 {
11 	struct pci_config_address *addr = data;
12 
13 	pci_config_address	= *addr;
14 
15 	return true;
16 }
17 
18 static bool pci_config_address_in(struct kvm *self, uint16_t port, void *data, int size, uint32_t count)
19 {
20 	struct pci_config_address *addr = data;
21 
22 	*addr		=  pci_config_address;
23 
24 	return true;
25 }
26 
27 static struct ioport_operations pci_config_address_ops = {
28 	.io_in		= pci_config_address_in,
29 	.io_out		= pci_config_address_out,
30 };
31 
32 static bool pci_config_data_out(struct kvm *self, uint16_t port, void *data, int size, uint32_t count)
33 {
34 	return true;
35 }
36 
37 #define PCI_VENDOR_ID_REDHAT_QUMRANET		0x1af4
38 #define PCI_DEVICE_ID_VIRTIO_BLK		0x1001
39 #define PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET	0x1af4
40 #define PCI_SUBSYSTEM_ID_VIRTIO_BLK		0x0002
41 
42 static struct pci_device_header virtio_device = {
43 	.vendor_id		= PCI_VENDOR_ID_REDHAT_QUMRANET,
44 	.device_id		= PCI_DEVICE_ID_VIRTIO_BLK,
45 	.header_type		= PCI_HEADER_TYPE_NORMAL,
46 	.revision_id		= 0,
47 	.class			= 0x010000,
48 	.subsys_vendor_id	= PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET,
49 	.subsys_id		= PCI_SUBSYSTEM_ID_VIRTIO_BLK,
50 	.bar[0]			= IOPORT_VIRTIO | PCI_BASE_ADDRESS_SPACE_IO,
51 };
52 
53 static bool pci_device_matches(uint8_t bus_number, uint8_t device_number, uint8_t function_number)
54 {
55 	if (pci_config_address.bus_number != bus_number)
56 		return false;
57 
58 	if (pci_config_address.device_number != device_number)
59 		return false;
60 
61 	return pci_config_address.function_number == function_number;
62 }
63 
64 static bool pci_config_data_in(struct kvm *self, uint16_t port, void *data, int size, uint32_t count)
65 {
66 	unsigned long start;
67 
68 	/*
69 	 * If someone accesses PCI configuration space offsets that are not
70 	 * aligned to 4 bytes, it uses ioports to signify that.
71 	 */
72 	start = port - PCI_CONFIG_DATA;
73 
74 	if (pci_device_matches(0, 1, 0)) {
75 		unsigned long offset;
76 
77 		offset = start + (pci_config_address.register_number << 2);
78 		if (offset < sizeof(struct pci_device_header)) {
79 			void *p = &virtio_device;
80 			memcpy(data, p + offset, size);
81 		} else
82 			memset(data, 0x00, size);
83 	} else
84 		memset(data, 0xff, size);
85 
86 	return true;
87 }
88 
89 static struct ioport_operations pci_config_data_ops = {
90 	.io_in		= pci_config_data_in,
91 	.io_out		= pci_config_data_out,
92 };
93 
94 static bool virtio_in(struct kvm *self, uint16_t port, void *data, int size, uint32_t count)
95 {
96 	return true;
97 }
98 
99 static bool virtio_out(struct kvm *self, uint16_t port, void *data, int size, uint32_t count)
100 {
101 	return true;
102 }
103 
104 static struct ioport_operations virtio_io_ops = {
105 	.io_in		= virtio_in,
106 	.io_out		= virtio_out,
107 };
108 
109 void pci__init(void)
110 {
111 
112 	ioport__register(IOPORT_VIRTIO,		&virtio_io_ops);
113 
114 	ioport__register(PCI_CONFIG_DATA + 0,	&pci_config_data_ops);
115 	ioport__register(PCI_CONFIG_DATA + 1,	&pci_config_data_ops);
116 	ioport__register(PCI_CONFIG_DATA + 2,	&pci_config_data_ops);
117 	ioport__register(PCI_CONFIG_DATA + 3,	&pci_config_data_ops);
118 
119 	ioport__register(PCI_CONFIG_ADDRESS,	&pci_config_address_ops);
120 }
121