xref: /kvmtool/pci.c (revision b403f2f7f7c1b85752e2d2bcf29bed358b024626)
1 #include "kvm/devices.h"
2 #include "kvm/pci.h"
3 #include "kvm/ioport.h"
4 #include "kvm/util.h"
5 #include "kvm/kvm.h"
6 
7 #include <linux/err.h>
8 #include <assert.h>
9 
10 #define PCI_BAR_OFFSET(b)		(offsetof(struct pci_device_header, bar[b]))
11 
12 static union pci_config_address		pci_config_address;
13 
14 /* This is within our PCI gap - in an unused area.
15  * Note this is a PCI *bus address*, is used to assign BARs etc.!
16  * (That's why it can still 32bit even with 64bit guests-- 64bit
17  * PCI isn't currently supported.)
18  */
19 static u32 io_space_blocks		= KVM_PCI_MMIO_AREA;
20 
21 u32 pci_get_io_space_block(u32 size)
22 {
23 	u32 block = io_space_blocks;
24 	io_space_blocks += size;
25 
26 	return block;
27 }
28 
29 static void *pci_config_address_ptr(u16 port)
30 {
31 	unsigned long offset;
32 	void *base;
33 
34 	offset	= port - PCI_CONFIG_ADDRESS;
35 	base	= &pci_config_address;
36 
37 	return base + offset;
38 }
39 
40 static bool pci_config_address_out(struct ioport *ioport, struct kvm *kvm, u16 port, void *data, int size)
41 {
42 	void *p = pci_config_address_ptr(port);
43 
44 	memcpy(p, data, size);
45 
46 	return true;
47 }
48 
49 static bool pci_config_address_in(struct ioport *ioport, struct kvm *kvm, u16 port, void *data, int size)
50 {
51 	void *p = pci_config_address_ptr(port);
52 
53 	memcpy(data, p, size);
54 
55 	return true;
56 }
57 
58 static struct ioport_operations pci_config_address_ops = {
59 	.io_in	= pci_config_address_in,
60 	.io_out	= pci_config_address_out,
61 };
62 
63 static bool pci_device_exists(u8 bus_number, u8 device_number, u8 function_number)
64 {
65 	if (pci_config_address.bus_number != bus_number)
66 		return false;
67 
68 	if (pci_config_address.function_number != function_number)
69 		return false;
70 
71 	return !IS_ERR_OR_NULL(device__find_dev(DEVICE_BUS_PCI, device_number));
72 }
73 
74 static bool pci_config_data_out(struct ioport *ioport, struct kvm *kvm, u16 port, void *data, int size)
75 {
76 	/*
77 	 * If someone accesses PCI configuration space offsets that are not
78 	 * aligned to 4 bytes, it uses ioports to signify that.
79 	 */
80 	pci_config_address.reg_offset = port - PCI_CONFIG_DATA;
81 
82 	pci__config_wr(kvm, pci_config_address, data, size);
83 
84 	return true;
85 }
86 
87 static bool pci_config_data_in(struct ioport *ioport, struct kvm *kvm, u16 port, void *data, int size)
88 {
89 	/*
90 	 * If someone accesses PCI configuration space offsets that are not
91 	 * aligned to 4 bytes, it uses ioports to signify that.
92 	 */
93 	pci_config_address.reg_offset = port - PCI_CONFIG_DATA;
94 
95 	pci__config_rd(kvm, pci_config_address, data, 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 void pci__config_wr(struct kvm *kvm, union pci_config_address addr, void *data, int size)
106 {
107 	u8 dev_num;
108 
109 	dev_num	= addr.device_number;
110 
111 	if (pci_device_exists(0, dev_num, 0)) {
112 		unsigned long offset;
113 
114 		offset = addr.w & 0xff;
115 		if (offset < sizeof(struct pci_device_header)) {
116 			void *p = device__find_dev(DEVICE_BUS_PCI, dev_num)->data;
117 			struct pci_device_header *hdr = p;
118 			u8 bar = (offset - PCI_BAR_OFFSET(0)) / (sizeof(u32));
119 			u32 sz = PCI_IO_SIZE;
120 
121 			if (bar < 6 && hdr->bar_size[bar])
122 				sz = hdr->bar_size[bar];
123 
124 			/*
125 			 * If the kernel masks the BAR it would expect to find the
126 			 * size of the BAR there next time it reads from it.
127 			 * When the kernel got the size it would write the address
128 			 * back.
129 			 */
130 			if (*(u32 *)(p + offset)) {
131 				/* See if kernel tries to mask one of the BARs */
132 				if ((offset >= PCI_BAR_OFFSET(0)) &&
133 				    (offset <= PCI_BAR_OFFSET(6)) &&
134 				    (ioport__read32(data)  == 0xFFFFFFFF))
135 					memcpy(p + offset, &sz, sizeof(sz));
136 				    else
137 					memcpy(p + offset, data, size);
138 			}
139 		}
140 	}
141 }
142 
143 void pci__config_rd(struct kvm *kvm, union pci_config_address addr, void *data, int size)
144 {
145 	u8 dev_num;
146 
147 	dev_num	= addr.device_number;
148 
149 	if (pci_device_exists(0, dev_num, 0)) {
150 		unsigned long offset;
151 
152 		offset = addr.w & 0xff;
153 		if (offset < sizeof(struct pci_device_header)) {
154 			void *p = device__find_dev(DEVICE_BUS_PCI, dev_num)->data;
155 
156 			memcpy(data, p + offset, size);
157 		} else {
158 			memset(data, 0x00, size);
159 		}
160 	} else {
161 		memset(data, 0xff, size);
162 	}
163 }
164 
165 static void pci_config_mmio_access(u64 addr, u8 *data, u32 len, u8 is_write, void *kvm)
166 {
167 	union pci_config_address cfg_addr;
168 
169 	addr			-= KVM_PCI_CFG_AREA;
170 	cfg_addr.w		= (u32)addr;
171 	cfg_addr.enable_bit	= 1;
172 
173 	if (is_write)
174 		pci__config_wr(kvm, cfg_addr, data, len);
175 	else
176 		pci__config_rd(kvm, cfg_addr, data, len);
177 }
178 
179 struct pci_device_header *pci__find_dev(u8 dev_num)
180 {
181 	struct device_header *hdr = device__find_dev(DEVICE_BUS_PCI, dev_num);
182 
183 	if (IS_ERR_OR_NULL(hdr))
184 		return NULL;
185 
186 	return hdr->data;
187 }
188 
189 int pci__init(struct kvm *kvm)
190 {
191 	int r;
192 
193 	r = ioport__register(kvm, PCI_CONFIG_DATA + 0, &pci_config_data_ops, 4, NULL);
194 	if (r < 0)
195 		return r;
196 
197 	r = ioport__register(kvm, PCI_CONFIG_ADDRESS + 0, &pci_config_address_ops, 4, NULL);
198 	if (r < 0)
199 		goto err_unregister_data;
200 
201 	r = kvm__register_mmio(kvm, KVM_PCI_CFG_AREA, PCI_CFG_SIZE, false,
202 			       pci_config_mmio_access, kvm);
203 	if (r < 0)
204 		goto err_unregister_addr;
205 
206 	return 0;
207 
208 err_unregister_addr:
209 	ioport__unregister(kvm, PCI_CONFIG_ADDRESS);
210 err_unregister_data:
211 	ioport__unregister(kvm, PCI_CONFIG_DATA);
212 	return r;
213 }
214 dev_base_init(pci__init);
215 
216 int pci__exit(struct kvm *kvm)
217 {
218 	ioport__unregister(kvm, PCI_CONFIG_DATA);
219 	ioport__unregister(kvm, PCI_CONFIG_ADDRESS);
220 
221 	return 0;
222 }
223 dev_base_exit(pci__exit);
224