xref: /openbsd-src/usr.sbin/vmd/pci.c (revision 4e1ee0786f11cc571bd0be17d38e46f635c719fc)
1 /*	$OpenBSD: pci.c,v 1.29 2021/06/16 16:55:02 dv Exp $	*/
2 
3 /*
4  * Copyright (c) 2015 Mike Larkin <mlarkin@openbsd.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/types.h>
20 
21 #include <dev/pci/pcireg.h>
22 #include <dev/pci/pcidevs.h>
23 #include <dev/pv/virtioreg.h>
24 #include <machine/vmmvar.h>
25 
26 #include <string.h>
27 #include <unistd.h>
28 
29 #include "vmd.h"
30 #include "pci.h"
31 #include "vmm.h"
32 #include "i8259.h"
33 #include "atomicio.h"
34 
35 struct pci pci;
36 
37 extern char *__progname;
38 
39 /* PIC IRQs, assigned to devices in order */
40 const uint8_t pci_pic_irqs[PCI_MAX_PIC_IRQS] = {3, 5, 6, 7, 9, 10, 11, 12,
41     14, 15};
42 
43 /*
44  * pci_add_bar
45  *
46  * Adds a BAR for the PCI device 'id'. On access, 'barfn' will be
47  * called, and passed 'cookie' as an identifier.
48  *
49  * BARs are fixed size, meaning all I/O BARs requested have the
50  * same size and all MMIO BARs have the same size.
51  *
52  * Parameters:
53  *  id: PCI device to add the BAR to (local count, eg if id == 4,
54  *      this BAR is to be added to the VM's 5th PCI device)
55  *  type: type of the BAR to add (PCI_MAPREG_TYPE_xxx)
56  *  barfn: callback function invoked on BAR access
57  *  cookie: cookie passed to barfn on access
58  *
59  * Returns 0 if the BAR was added successfully, 1 otherwise.
60  */
61 int
62 pci_add_bar(uint8_t id, uint32_t type, void *barfn, void *cookie)
63 {
64 	uint8_t bar_reg_idx, bar_ct;
65 
66 	/* Check id */
67 	if (id >= pci.pci_dev_ct)
68 		return (1);
69 
70 	/* Can only add PCI_MAX_BARS BARs to any device */
71 	bar_ct = pci.pci_devices[id].pd_bar_ct;
72 	if (bar_ct >= PCI_MAX_BARS)
73 		return (1);
74 
75 	/* Compute BAR address and add */
76 	bar_reg_idx = (PCI_MAPREG_START + (bar_ct * 4)) / 4;
77 	if (type == PCI_MAPREG_TYPE_MEM) {
78 		if (pci.pci_next_mmio_bar >= VMM_PCI_MMIO_BAR_END)
79 			return (1);
80 
81 		pci.pci_devices[id].pd_cfg_space[bar_reg_idx] =
82 		    PCI_MAPREG_MEM_ADDR(pci.pci_next_mmio_bar);
83 		pci.pci_next_mmio_bar += VMM_PCI_MMIO_BAR_SIZE;
84 		pci.pci_devices[id].pd_barfunc[bar_ct] = barfn;
85 		pci.pci_devices[id].pd_bar_cookie[bar_ct] = cookie;
86 		pci.pci_devices[id].pd_bartype[bar_ct] = PCI_BAR_TYPE_MMIO;
87 		pci.pci_devices[id].pd_barsize[bar_ct] = VMM_PCI_MMIO_BAR_SIZE;
88 		pci.pci_devices[id].pd_bar_ct++;
89 	} else if (type == PCI_MAPREG_TYPE_IO) {
90 		if (pci.pci_next_io_bar >= VMM_PCI_IO_BAR_END)
91 			return (1);
92 
93 		pci.pci_devices[id].pd_cfg_space[bar_reg_idx] =
94 		    PCI_MAPREG_IO_ADDR(pci.pci_next_io_bar) |
95 		    PCI_MAPREG_TYPE_IO;
96 		pci.pci_next_io_bar += VMM_PCI_IO_BAR_SIZE;
97 		pci.pci_devices[id].pd_barfunc[bar_ct] = barfn;
98 		pci.pci_devices[id].pd_bar_cookie[bar_ct] = cookie;
99 		DPRINTF("%s: adding pci bar cookie for dev %d bar %d = %p",
100 		    __progname, id, bar_ct, cookie);
101 		pci.pci_devices[id].pd_bartype[bar_ct] = PCI_BAR_TYPE_IO;
102 		pci.pci_devices[id].pd_barsize[bar_ct] = VMM_PCI_IO_BAR_SIZE;
103 		pci.pci_devices[id].pd_bar_ct++;
104 	}
105 
106 	return (0);
107 }
108 
109 int
110 pci_set_bar_fn(uint8_t id, uint8_t bar_ct, void *barfn, void *cookie)
111 {
112 	/* Check id */
113 	if (id >= pci.pci_dev_ct)
114 		return (1);
115 
116 	if (bar_ct >= PCI_MAX_BARS)
117 		return (1);
118 
119 	pci.pci_devices[id].pd_barfunc[bar_ct] = barfn;
120 	pci.pci_devices[id].pd_bar_cookie[bar_ct] = cookie;
121 
122 	return (0);
123 }
124 
125 /*
126  * pci_get_dev_irq
127  *
128  * Returns the IRQ for the specified PCI device
129  *
130  * Parameters:
131  *  id: PCI device id to return IRQ for
132  *
133  * Return values:
134  *  The IRQ for the device, or 0xff if no device IRQ assigned
135  */
136 uint8_t
137 pci_get_dev_irq(uint8_t id)
138 {
139 	if (pci.pci_devices[id].pd_int)
140 		return pci.pci_devices[id].pd_irq;
141 	else
142 		return 0xFF;
143 }
144 
145 /*
146  * pci_add_device
147  *
148  * Adds a PCI device to the guest VM defined by the supplied parameters.
149  *
150  * Parameters:
151  *  id: the new PCI device ID (0 .. PCI_CONFIG_MAX_DEV)
152  *  vid: PCI VID of the new device
153  *  pid: PCI PID of the new device
154  *  class: PCI 'class' of the new device
155  *  subclass: PCI 'subclass' of the new device
156  *  subsys_vid: subsystem VID of the new device
157  *  subsys_id: subsystem ID of the new device
158  *  irq_needed: 1 if an IRQ should be assigned to this PCI device, 0 otherwise
159  *  csfunc: PCI config space callback function when the guest VM accesses
160  *      CS of this PCI device
161  *
162  * Return values:
163  *  0: the PCI device was added successfully. The PCI device ID is in 'id'.
164  *  1: the PCI device addition failed.
165  */
166 int
167 pci_add_device(uint8_t *id, uint16_t vid, uint16_t pid, uint8_t class,
168     uint8_t subclass, uint16_t subsys_vid, uint16_t subsys_id,
169     uint8_t irq_needed, pci_cs_fn_t csfunc)
170 {
171 	/* Exceeded max devices? */
172 	if (pci.pci_dev_ct >= PCI_CONFIG_MAX_DEV)
173 		return (1);
174 
175 	/* Exceeded max IRQs? */
176 	/* XXX we could share IRQs ... */
177 	if (pci.pci_next_pic_irq >= PCI_MAX_PIC_IRQS && irq_needed)
178 		return (1);
179 
180 	*id = pci.pci_dev_ct;
181 
182 	pci.pci_devices[*id].pd_vid = vid;
183 	pci.pci_devices[*id].pd_did = pid;
184 	pci.pci_devices[*id].pd_class = class;
185 	pci.pci_devices[*id].pd_subclass = subclass;
186 	pci.pci_devices[*id].pd_subsys_vid = subsys_vid;
187 	pci.pci_devices[*id].pd_subsys_id = subsys_id;
188 
189 	pci.pci_devices[*id].pd_csfunc = csfunc;
190 
191 	if (irq_needed) {
192 		pci.pci_devices[*id].pd_irq =
193 		    pci_pic_irqs[pci.pci_next_pic_irq];
194 		pci.pci_devices[*id].pd_int = 1;
195 		pci.pci_next_pic_irq++;
196 		DPRINTF("assigned irq %d to pci dev %d",
197 		    pci.pci_devices[*id].pd_irq, *id);
198 		pic_set_elcr(pci.pci_devices[*id].pd_irq, 1);
199 	}
200 
201 	pci.pci_dev_ct ++;
202 
203 	return (0);
204 }
205 
206 /*
207  * pci_init
208  *
209  * Initializes the PCI subsystem for the VM by adding a PCI host bridge
210  * as the first PCI device.
211  */
212 void
213 pci_init(void)
214 {
215 	uint8_t id;
216 
217 	memset(&pci, 0, sizeof(pci));
218 	pci.pci_next_mmio_bar = VMM_PCI_MMIO_BAR_BASE;
219 	pci.pci_next_io_bar = VMM_PCI_IO_BAR_BASE;
220 
221 	if (pci_add_device(&id, PCI_VENDOR_OPENBSD, PCI_PRODUCT_OPENBSD_PCHB,
222 	    PCI_CLASS_BRIDGE, PCI_SUBCLASS_BRIDGE_HOST,
223 	    PCI_VENDOR_OPENBSD, 0, 0, NULL)) {
224 		log_warnx("%s: can't add PCI host bridge", __progname);
225 		return;
226 	}
227 }
228 
229 void
230 pci_handle_address_reg(struct vm_run_params *vrp)
231 {
232 	struct vm_exit *vei = vrp->vrp_exit;
233 
234 	/*
235 	 * vei_dir == VEI_DIR_OUT : out instruction
236 	 *
237 	 * The guest wrote to the address register.
238 	 */
239 	if (vei->vei.vei_dir == VEI_DIR_OUT) {
240 		get_input_data(vei, &pci.pci_addr_reg);
241 	} else {
242 		/*
243 		 * vei_dir == VEI_DIR_IN : in instruction
244 		 *
245 		 * The guest read the address register
246 		 */
247 		set_return_data(vei, pci.pci_addr_reg);
248 	}
249 }
250 
251 uint8_t
252 pci_handle_io(struct vm_run_params *vrp)
253 {
254 	int i, j, k, l;
255 	uint16_t reg, b_hi, b_lo;
256 	pci_iobar_fn_t fn;
257 	struct vm_exit *vei = vrp->vrp_exit;
258 	uint8_t intr, dir;
259 
260 	k = -1;
261 	l = -1;
262 	reg = vei->vei.vei_port;
263 	dir = vei->vei.vei_dir;
264 	intr = 0xFF;
265 
266 	for (i = 0 ; i < pci.pci_dev_ct ; i++) {
267 		for (j = 0 ; j < pci.pci_devices[i].pd_bar_ct; j++) {
268 			b_lo = PCI_MAPREG_IO_ADDR(pci.pci_devices[i].pd_bar[j]);
269 			b_hi = b_lo + VMM_PCI_IO_BAR_SIZE;
270 			if (reg >= b_lo && reg < b_hi) {
271 				if (pci.pci_devices[i].pd_barfunc[j]) {
272 					k = j;
273 					l = i;
274 				}
275 			}
276 		}
277 	}
278 
279 	if (k >= 0 && l >= 0) {
280 		fn = (pci_iobar_fn_t)pci.pci_devices[l].pd_barfunc[k];
281 		if (fn(vei->vei.vei_dir, reg -
282 		    PCI_MAPREG_IO_ADDR(pci.pci_devices[l].pd_bar[k]),
283 		    &vei->vei.vei_data, &intr,
284 		    pci.pci_devices[l].pd_bar_cookie[k],
285 		    vei->vei.vei_size)) {
286 			log_warnx("%s: pci i/o access function failed",
287 			    __progname);
288 		}
289 	} else {
290 		DPRINTF("%s: no pci i/o function for reg 0x%llx (dir=%d "
291 		    "guest %%rip=0x%llx", __progname, (uint64_t)reg, dir,
292 		    vei->vrs.vrs_gprs[VCPU_REGS_RIP]);
293 		/* Reads from undefined ports return 0xFF */
294 		if (dir == VEI_DIR_IN)
295 			set_return_data(vei, 0xFFFFFFFF);
296 	}
297 
298 	if (intr != 0xFF) {
299 		intr = pci.pci_devices[l].pd_irq;
300 	}
301 
302 	return (intr);
303 }
304 
305 void
306 pci_handle_data_reg(struct vm_run_params *vrp)
307 {
308 	struct vm_exit *vei = vrp->vrp_exit;
309 	uint8_t b, d, f, o, baridx, ofs, sz;
310 	int ret;
311 	pci_cs_fn_t csfunc;
312 
313 	/* abort if the address register is wack */
314 	if (!(pci.pci_addr_reg & PCI_MODE1_ENABLE)) {
315 		/* if read, return FFs */
316 		if (vei->vei.vei_dir == VEI_DIR_IN)
317 			set_return_data(vei, 0xFFFFFFFF);
318 		log_warnx("invalid address register during pci read: "
319 		    "0x%llx", (uint64_t)pci.pci_addr_reg);
320 		return;
321 	}
322 
323 	/* I/Os to 0xCFC..0xCFF are permitted */
324 	ofs = vei->vei.vei_port - 0xCFC;
325 	sz = vei->vei.vei_size;
326 
327 	b = (pci.pci_addr_reg >> 16) & 0xff;
328 	d = (pci.pci_addr_reg >> 11) & 0x1f;
329 	f = (pci.pci_addr_reg >> 8) & 0x7;
330 	o = (pci.pci_addr_reg & 0xfc);
331 
332 	csfunc = pci.pci_devices[d].pd_csfunc;
333 	if (csfunc != NULL) {
334 		ret = csfunc(vei->vei.vei_dir, (o / 4), &vei->vei.vei_data);
335 		if (ret)
336 			log_warnx("cfg space access function failed for "
337 			    "pci device %d", d);
338 		return;
339 	}
340 
341 	/* No config space function, fallback to default simple r/w impl. */
342 
343 	o += ofs;
344 
345 	/*
346 	 * vei_dir == VEI_DIR_OUT : out instruction
347 	 *
348 	 * The guest wrote to the config space location denoted by the current
349 	 * value in the address register.
350 	 */
351 	if (vei->vei.vei_dir == VEI_DIR_OUT) {
352 		if ((o >= 0x10 && o <= 0x24) &&
353 		    vei->vei.vei_data == 0xffffffff) {
354 			/*
355 			 * Compute BAR index:
356 			 * o = 0x10 -> baridx = 0
357 			 * o = 0x14 -> baridx = 1
358 			 * o = 0x18 -> baridx = 2
359 			 * o = 0x1c -> baridx = 3
360 			 * o = 0x20 -> baridx = 4
361 			 * o = 0x24 -> baridx = 5
362 			 */
363 			baridx = (o / 4) - 4;
364 			if (baridx < pci.pci_devices[d].pd_bar_ct)
365 				vei->vei.vei_data = 0xfffff000;
366 			else
367 				vei->vei.vei_data = 0;
368 		}
369 
370 		/* IOBAR registers must have bit 0 set */
371 		if (o >= 0x10 && o <= 0x24) {
372 			baridx = (o / 4) - 4;
373 			if (baridx < pci.pci_devices[d].pd_bar_ct &&
374 			    pci.pci_devices[d].pd_bartype[baridx] ==
375 			    PCI_BAR_TYPE_IO)
376 				vei->vei.vei_data |= 1;
377 		}
378 
379 		/*
380 		 * Discard writes to "option rom base address" as none of our
381 		 * emulated devices have PCI option roms. Accept any other
382 		 * writes and copy data to config space registers.
383 		 */
384 		if (o != PCI_EXROMADDR_0)
385 			get_input_data(vei,
386 			    &pci.pci_devices[d].pd_cfg_space[o / 4]);
387 	} else {
388 		/*
389 		 * vei_dir == VEI_DIR_IN : in instruction
390 		 *
391 		 * The guest read from the config space location determined by
392 		 * the current value in the address register.
393 		 */
394 		if (d > pci.pci_dev_ct || b > 0 || f > 0)
395 			set_return_data(vei, 0xFFFFFFFF);
396 		else {
397 			switch (sz) {
398 			case 4:
399 				set_return_data(vei,
400 				    pci.pci_devices[d].pd_cfg_space[o / 4]);
401 				break;
402 			case 2:
403 				if (ofs == 0)
404 					set_return_data(vei, pci.pci_devices[d].
405 					    pd_cfg_space[o / 4]);
406 				else
407 					set_return_data(vei, pci.pci_devices[d].
408 					    pd_cfg_space[o / 4] >> 16);
409 				break;
410 			case 1:
411 				set_return_data(vei, pci.pci_devices[d].
412 				    pd_cfg_space[o / 4] >> (ofs * 8));
413 				break;
414 			}
415 		}
416 	}
417 }
418 
419 int
420 pci_dump(int fd)
421 {
422 	log_debug("%s: sending pci", __func__);
423 	if (atomicio(vwrite, fd, &pci, sizeof(pci)) != sizeof(pci)) {
424 		log_warnx("%s: error writing pci to fd", __func__);
425 		return (-1);
426 	}
427 	return (0);
428 }
429 
430 int
431 pci_restore(int fd)
432 {
433 	log_debug("%s: receiving pci", __func__);
434 	if (atomicio(read, fd, &pci, sizeof(pci)) != sizeof(pci)) {
435 		log_warnx("%s: error reading pci from fd", __func__);
436 		return (-1);
437 	}
438 	return (0);
439 }
440