1/*	$OpenBSD: pci.c,v 1.31 2023/02/06 20:33:34 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
35struct pci pci;
36
37extern char *__progname;
38
39/* PIC IRQs, assigned to devices in order */
40const 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 */
61int
62pci_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 += VM_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] = VM_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 >= VM_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 += VM_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] = VM_PCI_IO_BAR_SIZE;
103		pci.pci_devices[id].pd_bar_ct++;
104	}
105
106	return (0);
107}
108
109int
110pci_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 */
136uint8_t
137pci_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 */
166int
167pci_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 */
212void
213pci_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 = VM_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
229void
230pci_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
251uint8_t
252pci_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 + VM_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
305void
306pci_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
419int
420pci_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
430int
431pci_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
441/*
442 * Find the first PCI device based on PCI Subsystem ID
443 * (e.g. PCI_PRODUCT_VIRTIO_BLOCK).
444 *
445 * Returns the PCI device id of the first matching device, if found.
446 * Otherwise, returns -1.
447 */
448int
449pci_find_first_device(uint16_t subsys_id)
450{
451	int i;
452
453	for (i = 0; i < pci.pci_dev_ct; i++)
454		if (pci.pci_devices[i].pd_subsys_id == subsys_id)
455			return (i);
456	return (-1);
457}
458