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