1366f6083SPeter Grehan /*-
24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause
31de7b4b8SPedro F. Giffuni *
4366f6083SPeter Grehan * Copyright (c) 2011 NetApp, Inc.
5366f6083SPeter Grehan * All rights reserved.
6366f6083SPeter Grehan *
7366f6083SPeter Grehan * Redistribution and use in source and binary forms, with or without
8366f6083SPeter Grehan * modification, are permitted provided that the following conditions
9366f6083SPeter Grehan * are met:
10366f6083SPeter Grehan * 1. Redistributions of source code must retain the above copyright
11366f6083SPeter Grehan * notice, this list of conditions and the following disclaimer.
12366f6083SPeter Grehan * 2. Redistributions in binary form must reproduce the above copyright
13366f6083SPeter Grehan * notice, this list of conditions and the following disclaimer in the
14366f6083SPeter Grehan * documentation and/or other materials provided with the distribution.
15366f6083SPeter Grehan *
16366f6083SPeter Grehan * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
17366f6083SPeter Grehan * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18366f6083SPeter Grehan * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19366f6083SPeter Grehan * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
20366f6083SPeter Grehan * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21366f6083SPeter Grehan * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22366f6083SPeter Grehan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23366f6083SPeter Grehan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24366f6083SPeter Grehan * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25366f6083SPeter Grehan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26366f6083SPeter Grehan * SUCH DAMAGE.
27366f6083SPeter Grehan */
28366f6083SPeter Grehan
29366f6083SPeter Grehan #include <sys/param.h>
30366f6083SPeter Grehan #include <sys/linker_set.h>
31e47fe318SCorvin Köhne #include <sys/mman.h>
32366f6083SPeter Grehan
33366f6083SPeter Grehan #include <ctype.h>
34fc7207c8SEmmanuel Vadot #include <err.h>
357e12dfe5SEnji Cooper #include <errno.h>
363cbf3585SJohn Baldwin #include <pthread.h>
37366f6083SPeter Grehan #include <stdio.h>
38366f6083SPeter Grehan #include <stdlib.h>
39366f6083SPeter Grehan #include <string.h>
40366f6083SPeter Grehan #include <strings.h>
41366f6083SPeter Grehan #include <assert.h>
42028d9311SNeel Natu #include <stdbool.h>
435cf21e48SCorvin Köhne #include <sysexits.h>
44366f6083SPeter Grehan
45366f6083SPeter Grehan #include <machine/vmm.h>
46483d953aSJohn Baldwin #include <machine/vmm_snapshot.h>
47366f6083SPeter Grehan #include <vmmapi.h>
48366f6083SPeter Grehan
49e6c8bc29SJohn Baldwin #include "acpi.h"
50e285ef8dSPeter Grehan #include "bhyverun.h"
51621b5090SJohn Baldwin #include "config.h"
52332eff95SVincenzo Maffione #include "debug.h"
5355c13f6eSMark Johnston #ifdef __amd64__
5431cf78c9SMark Johnston #include "amd64/inout.h"
5555c13f6eSMark Johnston #endif
564d1e669cSPeter Grehan #include "mem.h"
57366f6083SPeter Grehan #include "pci_emul.h"
5855c13f6eSMark Johnston #ifdef __amd64__
5955c13f6eSMark Johnston #include "amd64/pci_lpc.h"
60ffaed739SCorvin Köhne #include "pci_passthru.h"
61fc98569fSMark Johnston #endif
626632a0a4SCorvin Köhne #include "qemu_fwcfg.h"
63366f6083SPeter Grehan
64366f6083SPeter Grehan #define CONF1_ADDR_PORT 0x0cf8
65366f6083SPeter Grehan #define CONF1_DATA_PORT 0x0cfc
66366f6083SPeter Grehan
6775543036SPeter Grehan #define CONF1_ENABLE 0x80000000ul
6875543036SPeter Grehan
69d84882caSNeel Natu #define MAXBUSES (PCI_BUSMAX + 1)
7099d65389SNeel Natu #define MAXSLOTS (PCI_SLOTMAX + 1)
7199d65389SNeel Natu #define MAXFUNCS (PCI_FUNCMAX + 1)
72366f6083SPeter Grehan
734a4053e1SCorvin Köhne #define GB (1024 * 1024 * 1024UL)
744a4053e1SCorvin Köhne
753cbf3585SJohn Baldwin struct funcinfo {
76621b5090SJohn Baldwin nvlist_t *fi_config;
77621b5090SJohn Baldwin struct pci_devemu *fi_pde;
783cbf3585SJohn Baldwin struct pci_devinst *fi_devi;
793cbf3585SJohn Baldwin };
803cbf3585SJohn Baldwin
813cbf3585SJohn Baldwin struct intxinfo {
823cbf3585SJohn Baldwin int ii_count;
830efad4acSJessica Clarke struct pci_irq ii_irq;
843cbf3585SJohn Baldwin };
853cbf3585SJohn Baldwin
863cbf3585SJohn Baldwin struct slotinfo {
873cbf3585SJohn Baldwin struct intxinfo si_intpins[4];
883cbf3585SJohn Baldwin struct funcinfo si_funcs[MAXFUNCS];
89d84882caSNeel Natu };
90d84882caSNeel Natu
91d84882caSNeel Natu struct businfo {
92d84882caSNeel Natu uint16_t iobase, iolimit; /* I/O window */
93d84882caSNeel Natu uint32_t membase32, memlimit32; /* mmio window below 4GB */
94d84882caSNeel Natu uint64_t membase64, memlimit64; /* mmio window above 4GB */
95d84882caSNeel Natu struct slotinfo slotinfo[MAXSLOTS];
96d84882caSNeel Natu };
97d84882caSNeel Natu
98d84882caSNeel Natu static struct businfo *pci_businfo[MAXBUSES];
99366f6083SPeter Grehan
100366f6083SPeter Grehan SET_DECLARE(pci_devemu_set, struct pci_devemu);
101366f6083SPeter Grehan
102366f6083SPeter Grehan static uint64_t pci_emul_iobase;
103e47fe318SCorvin Köhne static uint8_t *pci_emul_rombase;
104e47fe318SCorvin Köhne static uint64_t pci_emul_romoffset;
105e47fe318SCorvin Köhne static uint8_t *pci_emul_romlim;
106366f6083SPeter Grehan static uint64_t pci_emul_membase32;
107366f6083SPeter Grehan static uint64_t pci_emul_membase64;
1089922872bSKonstantin Belousov static uint64_t pci_emul_memlim64;
109366f6083SPeter Grehan
11001f9362eSCorvin Köhne struct pci_bar_allocation {
11101f9362eSCorvin Köhne TAILQ_ENTRY(pci_bar_allocation) chain;
11201f9362eSCorvin Köhne struct pci_devinst *pdi;
11301f9362eSCorvin Köhne int idx;
11401f9362eSCorvin Köhne enum pcibar_type type;
11501f9362eSCorvin Köhne uint64_t size;
11601f9362eSCorvin Köhne };
11707d82562SMark Johnston
11807d82562SMark Johnston static TAILQ_HEAD(pci_bar_list, pci_bar_allocation) pci_bars =
11907d82562SMark Johnston TAILQ_HEAD_INITIALIZER(pci_bars);
12001f9362eSCorvin Köhne
1216632a0a4SCorvin Köhne struct boot_device {
1226632a0a4SCorvin Köhne TAILQ_ENTRY(boot_device) boot_device_chain;
1236632a0a4SCorvin Köhne struct pci_devinst *pdi;
1246632a0a4SCorvin Köhne int bootindex;
1256632a0a4SCorvin Köhne };
1266632a0a4SCorvin Köhne static TAILQ_HEAD(boot_list, boot_device) boot_devices = TAILQ_HEAD_INITIALIZER(
1276632a0a4SCorvin Köhne boot_devices);
1286632a0a4SCorvin Köhne
129f286f746SMark Johnston #if defined(__amd64__)
130366f6083SPeter Grehan #define PCI_EMUL_IOBASE 0x2000
131366f6083SPeter Grehan #define PCI_EMUL_IOLIMIT 0x10000
132f286f746SMark Johnston #define PCI_EMUL_IOMASK 0xffff
133f286f746SMark Johnston /*
134f286f746SMark Johnston * OVMF always uses 0xc0000000 as base address for 32 bit PCI MMIO. Don't
135f286f746SMark Johnston * change this address without changing it in OVMF.
136f286f746SMark Johnston */
137f286f746SMark Johnston #define PCI_EMUL_MEMBASE32 0xc0000000
138f286f746SMark Johnston #elif defined(__aarch64__)
13995b948c1SJessica Clarke #define PCI_EMUL_IOBASE 0xdf000000UL
14095b948c1SJessica Clarke #define PCI_EMUL_IOLIMIT 0xe0000000UL
14195b948c1SJessica Clarke #define PCI_EMUL_MEMBASE32 0xa0000000UL
142f286f746SMark Johnston #else
143f286f746SMark Johnston #error Unsupported platform
144f286f746SMark Johnston #endif
145366f6083SPeter Grehan
146e47fe318SCorvin Köhne #define PCI_EMUL_ROMSIZE 0x10000000
147e47fe318SCorvin Köhne
14812a6eb99SNeel Natu #define PCI_EMUL_ECFG_BASE 0xE0000000 /* 3.5GB */
14912a6eb99SNeel Natu #define PCI_EMUL_ECFG_SIZE (MAXBUSES * 1024 * 1024) /* 1MB per bus */
150f286f746SMark Johnston #ifdef __amd64__
15112a6eb99SNeel Natu SYSRES_MEM(PCI_EMUL_ECFG_BASE, PCI_EMUL_ECFG_SIZE);
152f286f746SMark Johnston #endif
15312a6eb99SNeel Natu
15412a6eb99SNeel Natu #define PCI_EMUL_MEMLIMIT32 PCI_EMUL_ECFG_BASE
1554a4053e1SCorvin Köhne #define PCI_EMUL_MEMSIZE64 (32*GB)
156366f6083SPeter Grehan
157b3e9732aSJohn Baldwin static void pci_lintr_route(struct pci_devinst *pi);
1583cbf3585SJohn Baldwin static void pci_lintr_update(struct pci_devinst *pi);
15955c13f6eSMark Johnston
16055c13f6eSMark Johnston static struct pci_devemu *pci_emul_finddev(const char *name);
1616a284cacSJohn Baldwin static void pci_cfgrw(int in, int bus, int slot, int func, int coff,
1626a284cacSJohn Baldwin int bytes, uint32_t *val);
163366f6083SPeter Grehan
16454335630SNeel Natu static __inline void
CFGWRITE(struct pci_devinst * pi,int coff,uint32_t val,int bytes)16554335630SNeel Natu CFGWRITE(struct pci_devinst *pi, int coff, uint32_t val, int bytes)
16654335630SNeel Natu {
16754335630SNeel Natu
16854335630SNeel Natu if (bytes == 1)
16954335630SNeel Natu pci_set_cfgdata8(pi, coff, val);
17054335630SNeel Natu else if (bytes == 2)
17154335630SNeel Natu pci_set_cfgdata16(pi, coff, val);
17254335630SNeel Natu else
17354335630SNeel Natu pci_set_cfgdata32(pi, coff, val);
17454335630SNeel Natu }
17554335630SNeel Natu
17654335630SNeel Natu static __inline uint32_t
CFGREAD(struct pci_devinst * pi,int coff,int bytes)17754335630SNeel Natu CFGREAD(struct pci_devinst *pi, int coff, int bytes)
17854335630SNeel Natu {
17954335630SNeel Natu
18054335630SNeel Natu if (bytes == 1)
18154335630SNeel Natu return (pci_get_cfgdata8(pi, coff));
18254335630SNeel Natu else if (bytes == 2)
18354335630SNeel Natu return (pci_get_cfgdata16(pi, coff));
18454335630SNeel Natu else
18554335630SNeel Natu return (pci_get_cfgdata32(pi, coff));
18654335630SNeel Natu }
18754335630SNeel Natu
18845ddbf21SCorvin Köhne static int
is_pcir_bar(int coff)18945ddbf21SCorvin Köhne is_pcir_bar(int coff)
19045ddbf21SCorvin Köhne {
19145ddbf21SCorvin Köhne return (coff >= PCIR_BAR(0) && coff < PCIR_BAR(PCI_BARMAX + 1));
19245ddbf21SCorvin Köhne }
19345ddbf21SCorvin Köhne
19445ddbf21SCorvin Köhne static int
is_pcir_bios(int coff)19545ddbf21SCorvin Köhne is_pcir_bios(int coff)
19645ddbf21SCorvin Köhne {
19745ddbf21SCorvin Köhne return (coff >= PCIR_BIOS && coff < PCIR_BIOS + 4);
19845ddbf21SCorvin Köhne }
19945ddbf21SCorvin Köhne
200366f6083SPeter Grehan /*
201366f6083SPeter Grehan * I/O access
202366f6083SPeter Grehan */
203366f6083SPeter Grehan
204366f6083SPeter Grehan /*
205366f6083SPeter Grehan * Slot options are in the form:
206366f6083SPeter Grehan *
207d84882caSNeel Natu * <bus>:<slot>:<func>,<emul>[,<config>]
20899d65389SNeel Natu * <slot>[:<func>],<emul>[,<config>]
209366f6083SPeter Grehan *
210366f6083SPeter Grehan * slot is 0..31
21199d65389SNeel Natu * func is 0..7
212366f6083SPeter Grehan * emul is a string describing the type of PCI device e.g. virtio-net
213366f6083SPeter Grehan * config is an optional string, depending on the device, that can be
214366f6083SPeter Grehan * used for configuration.
215366f6083SPeter Grehan * Examples are:
216366f6083SPeter Grehan * 1,virtio-net,tap0
21799d65389SNeel Natu * 3:0,dummy
218366f6083SPeter Grehan */
219366f6083SPeter Grehan static void
pci_parse_slot_usage(char * aopt)220366f6083SPeter Grehan pci_parse_slot_usage(char *aopt)
221366f6083SPeter Grehan {
222b05c77ffSNeel Natu
223332eff95SVincenzo Maffione EPRINTLN("Invalid PCI slot info field \"%s\"", aopt);
224366f6083SPeter Grehan }
225366f6083SPeter Grehan
226621b5090SJohn Baldwin /*
227621b5090SJohn Baldwin * Helper function to parse a list of comma-separated options where
228621b5090SJohn Baldwin * each option is formatted as "name[=value]". If no value is
229621b5090SJohn Baldwin * provided, the option is treated as a boolean and is given a value
230621b5090SJohn Baldwin * of true.
231621b5090SJohn Baldwin */
232621b5090SJohn Baldwin int
pci_parse_legacy_config(nvlist_t * nvl,const char * opt)233621b5090SJohn Baldwin pci_parse_legacy_config(nvlist_t *nvl, const char *opt)
234621b5090SJohn Baldwin {
235621b5090SJohn Baldwin char *config, *name, *tofree, *value;
236621b5090SJohn Baldwin
237621b5090SJohn Baldwin if (opt == NULL)
238621b5090SJohn Baldwin return (0);
239621b5090SJohn Baldwin
240621b5090SJohn Baldwin config = tofree = strdup(opt);
241621b5090SJohn Baldwin while ((name = strsep(&config, ",")) != NULL) {
242621b5090SJohn Baldwin value = strchr(name, '=');
243621b5090SJohn Baldwin if (value != NULL) {
244621b5090SJohn Baldwin *value = '\0';
245621b5090SJohn Baldwin value++;
246621b5090SJohn Baldwin set_config_value_node(nvl, name, value);
247621b5090SJohn Baldwin } else
248621b5090SJohn Baldwin set_config_bool_node(nvl, name, true);
249621b5090SJohn Baldwin }
250621b5090SJohn Baldwin free(tofree);
251621b5090SJohn Baldwin return (0);
252621b5090SJohn Baldwin }
253621b5090SJohn Baldwin
254621b5090SJohn Baldwin /*
255621b5090SJohn Baldwin * PCI device configuration is stored in MIBs that encode the device's
256621b5090SJohn Baldwin * location:
257621b5090SJohn Baldwin *
258621b5090SJohn Baldwin * pci.<bus>.<slot>.<func>
259621b5090SJohn Baldwin *
260621b5090SJohn Baldwin * Where "bus", "slot", and "func" are all decimal values without
261621b5090SJohn Baldwin * leading zeroes. Each valid device must have a "device" node which
262621b5090SJohn Baldwin * identifies the driver model of the device.
263621b5090SJohn Baldwin *
264621b5090SJohn Baldwin * Device backends can provide a parser for the "config" string. If
265621b5090SJohn Baldwin * a custom parser is not provided, pci_parse_legacy_config() is used
266621b5090SJohn Baldwin * to parse the string.
267621b5090SJohn Baldwin */
268b05c77ffSNeel Natu int
pci_parse_slot(char * opt)269d2bc4816SJohn Baldwin pci_parse_slot(char *opt)
270366f6083SPeter Grehan {
271621b5090SJohn Baldwin char node_name[sizeof("pci.XXX.XX.X")];
272621b5090SJohn Baldwin struct pci_devemu *pde;
273d84882caSNeel Natu char *emul, *config, *str, *cp;
274d84882caSNeel Natu int error, bnum, snum, fnum;
275621b5090SJohn Baldwin nvlist_t *nvl;
276366f6083SPeter Grehan
277b05c77ffSNeel Natu error = -1;
278d84882caSNeel Natu str = strdup(opt);
27999d65389SNeel Natu
280d84882caSNeel Natu emul = config = NULL;
281d84882caSNeel Natu if ((cp = strchr(str, ',')) != NULL) {
282d84882caSNeel Natu *cp = '\0';
283d84882caSNeel Natu emul = cp + 1;
284d84882caSNeel Natu if ((cp = strchr(emul, ',')) != NULL) {
285d84882caSNeel Natu *cp = '\0';
286d84882caSNeel Natu config = cp + 1;
28799d65389SNeel Natu }
288d84882caSNeel Natu } else {
289b05c77ffSNeel Natu pci_parse_slot_usage(opt);
290b05c77ffSNeel Natu goto done;
291366f6083SPeter Grehan }
292366f6083SPeter Grehan
293d84882caSNeel Natu /* <bus>:<slot>:<func> */
294d84882caSNeel Natu if (sscanf(str, "%d:%d:%d", &bnum, &snum, &fnum) != 3) {
295d84882caSNeel Natu bnum = 0;
296d84882caSNeel Natu /* <slot>:<func> */
297d84882caSNeel Natu if (sscanf(str, "%d:%d", &snum, &fnum) != 2) {
298d84882caSNeel Natu fnum = 0;
299d84882caSNeel Natu /* <slot> */
300d84882caSNeel Natu if (sscanf(str, "%d", &snum) != 1) {
301d84882caSNeel Natu snum = -1;
302d84882caSNeel Natu }
303d84882caSNeel Natu }
304d84882caSNeel Natu }
305b05c77ffSNeel Natu
306d84882caSNeel Natu if (bnum < 0 || bnum >= MAXBUSES || snum < 0 || snum >= MAXSLOTS ||
307d84882caSNeel Natu fnum < 0 || fnum >= MAXFUNCS) {
308b05c77ffSNeel Natu pci_parse_slot_usage(opt);
309b05c77ffSNeel Natu goto done;
310b05c77ffSNeel Natu }
311b05c77ffSNeel Natu
312621b5090SJohn Baldwin pde = pci_emul_finddev(emul);
313621b5090SJohn Baldwin if (pde == NULL) {
314621b5090SJohn Baldwin EPRINTLN("pci slot %d:%d:%d: unknown device \"%s\"", bnum, snum,
315621b5090SJohn Baldwin fnum, emul);
316b05c77ffSNeel Natu goto done;
317b05c77ffSNeel Natu }
318b05c77ffSNeel Natu
319621b5090SJohn Baldwin snprintf(node_name, sizeof(node_name), "pci.%d.%d.%d", bnum, snum,
320621b5090SJohn Baldwin fnum);
321621b5090SJohn Baldwin nvl = find_config_node(node_name);
322621b5090SJohn Baldwin if (nvl != NULL) {
323621b5090SJohn Baldwin EPRINTLN("pci slot %d:%d:%d already occupied!", bnum, snum,
324621b5090SJohn Baldwin fnum);
325b05c77ffSNeel Natu goto done;
326b05c77ffSNeel Natu }
327621b5090SJohn Baldwin nvl = create_config_node(node_name);
328621b5090SJohn Baldwin if (pde->pe_alias != NULL)
329621b5090SJohn Baldwin set_config_value_node(nvl, "device", pde->pe_alias);
330621b5090SJohn Baldwin else
331621b5090SJohn Baldwin set_config_value_node(nvl, "device", pde->pe_emu);
332b05c77ffSNeel Natu
333621b5090SJohn Baldwin if (pde->pe_legacy_config != NULL)
334621b5090SJohn Baldwin error = pde->pe_legacy_config(nvl, config);
335621b5090SJohn Baldwin else
336621b5090SJohn Baldwin error = pci_parse_legacy_config(nvl, config);
337b05c77ffSNeel Natu done:
338d84882caSNeel Natu free(str);
339b05c77ffSNeel Natu return (error);
340366f6083SPeter Grehan }
341366f6083SPeter Grehan
342657d2158SMarcelo Araujo void
pci_print_supported_devices(void)34375ce327aSMark Johnston pci_print_supported_devices(void)
344657d2158SMarcelo Araujo {
345657d2158SMarcelo Araujo struct pci_devemu **pdpp, *pdp;
346657d2158SMarcelo Araujo
347657d2158SMarcelo Araujo SET_FOREACH(pdpp, pci_devemu_set) {
348657d2158SMarcelo Araujo pdp = *pdpp;
349657d2158SMarcelo Araujo printf("%s\n", pdp->pe_emu);
350657d2158SMarcelo Araujo }
351657d2158SMarcelo Araujo }
352657d2158SMarcelo Araujo
353ffaed739SCorvin Köhne uint32_t
pci_config_read_reg(const struct pcisel * const host_sel,nvlist_t * nvl,const uint32_t reg,const uint8_t size,const uint32_t def)354ffaed739SCorvin Köhne pci_config_read_reg(const struct pcisel *const host_sel, nvlist_t *nvl,
355ffaed739SCorvin Köhne const uint32_t reg, const uint8_t size, const uint32_t def)
356ffaed739SCorvin Köhne {
357ffaed739SCorvin Köhne const char *config;
358ffaed739SCorvin Köhne const nvlist_t *pci_regs;
359ffaed739SCorvin Köhne
360ffaed739SCorvin Köhne assert(size == 1 || size == 2 || size == 4);
361ffaed739SCorvin Köhne
362ffaed739SCorvin Köhne pci_regs = find_relative_config_node(nvl, "pcireg");
363ffaed739SCorvin Köhne if (pci_regs == NULL) {
364ffaed739SCorvin Köhne return def;
365ffaed739SCorvin Köhne }
366ffaed739SCorvin Köhne
367ffaed739SCorvin Köhne switch (reg) {
368ffaed739SCorvin Köhne case PCIR_DEVICE:
369ffaed739SCorvin Köhne config = get_config_value_node(pci_regs, "device");
370ffaed739SCorvin Köhne break;
371ffaed739SCorvin Köhne case PCIR_VENDOR:
372ffaed739SCorvin Köhne config = get_config_value_node(pci_regs, "vendor");
373ffaed739SCorvin Köhne break;
374ffaed739SCorvin Köhne case PCIR_REVID:
375ffaed739SCorvin Köhne config = get_config_value_node(pci_regs, "revid");
376ffaed739SCorvin Köhne break;
377ffaed739SCorvin Köhne case PCIR_SUBVEND_0:
378ffaed739SCorvin Köhne config = get_config_value_node(pci_regs, "subvendor");
379ffaed739SCorvin Köhne break;
380ffaed739SCorvin Köhne case PCIR_SUBDEV_0:
381ffaed739SCorvin Köhne config = get_config_value_node(pci_regs, "subdevice");
382ffaed739SCorvin Köhne break;
383ffaed739SCorvin Köhne default:
384ffaed739SCorvin Köhne return (-1);
385ffaed739SCorvin Köhne }
386ffaed739SCorvin Köhne
387ffaed739SCorvin Köhne if (config == NULL) {
388ffaed739SCorvin Köhne return def;
389ffaed739SCorvin Köhne } else if (host_sel != NULL && strcmp(config, "host") == 0) {
390fc98569fSMark Johnston #ifdef __amd64__
39101d53c34SMark Johnston return pci_host_read_config(host_sel, reg, size);
392fc98569fSMark Johnston #else
393fc98569fSMark Johnston errx(1, "cannot fetch host PCI configuration");
394fc98569fSMark Johnston #endif
395ffaed739SCorvin Köhne } else {
396ffaed739SCorvin Köhne return strtol(config, NULL, 16);
397ffaed739SCorvin Köhne }
398ffaed739SCorvin Köhne }
399ffaed739SCorvin Köhne
400366f6083SPeter Grehan static int
pci_valid_pba_offset(struct pci_devinst * pi,uint64_t offset)401c9b4e987SNeel Natu pci_valid_pba_offset(struct pci_devinst *pi, uint64_t offset)
402c9b4e987SNeel Natu {
403c9b4e987SNeel Natu
404c9b4e987SNeel Natu if (offset < pi->pi_msix.pba_offset)
405c9b4e987SNeel Natu return (0);
406c9b4e987SNeel Natu
407c9b4e987SNeel Natu if (offset >= pi->pi_msix.pba_offset + pi->pi_msix.pba_size) {
408c9b4e987SNeel Natu return (0);
409c9b4e987SNeel Natu }
410c9b4e987SNeel Natu
411c9b4e987SNeel Natu return (1);
412c9b4e987SNeel Natu }
413c9b4e987SNeel Natu
414c9b4e987SNeel Natu int
pci_emul_msix_twrite(struct pci_devinst * pi,uint64_t offset,int size,uint64_t value)415c9b4e987SNeel Natu pci_emul_msix_twrite(struct pci_devinst *pi, uint64_t offset, int size,
416c9b4e987SNeel Natu uint64_t value)
417c9b4e987SNeel Natu {
418c9b4e987SNeel Natu int msix_entry_offset;
419c9b4e987SNeel Natu int tab_index;
420c9b4e987SNeel Natu char *dest;
421c9b4e987SNeel Natu
422c9b4e987SNeel Natu /* support only 4 or 8 byte writes */
423c9b4e987SNeel Natu if (size != 4 && size != 8)
424c9b4e987SNeel Natu return (-1);
425c9b4e987SNeel Natu
426c9b4e987SNeel Natu /*
427c9b4e987SNeel Natu * Return if table index is beyond what device supports
428c9b4e987SNeel Natu */
429c9b4e987SNeel Natu tab_index = offset / MSIX_TABLE_ENTRY_SIZE;
430c9b4e987SNeel Natu if (tab_index >= pi->pi_msix.table_count)
431c9b4e987SNeel Natu return (-1);
432c9b4e987SNeel Natu
433c9b4e987SNeel Natu msix_entry_offset = offset % MSIX_TABLE_ENTRY_SIZE;
434c9b4e987SNeel Natu
435c9b4e987SNeel Natu /* support only aligned writes */
436c9b4e987SNeel Natu if ((msix_entry_offset % size) != 0)
437c9b4e987SNeel Natu return (-1);
438c9b4e987SNeel Natu
439c9b4e987SNeel Natu dest = (char *)(pi->pi_msix.table + tab_index);
440c9b4e987SNeel Natu dest += msix_entry_offset;
441c9b4e987SNeel Natu
442c9b4e987SNeel Natu if (size == 4)
443c9b4e987SNeel Natu *((uint32_t *)dest) = value;
444c9b4e987SNeel Natu else
445c9b4e987SNeel Natu *((uint64_t *)dest) = value;
446c9b4e987SNeel Natu
447c9b4e987SNeel Natu return (0);
448c9b4e987SNeel Natu }
449c9b4e987SNeel Natu
450c9b4e987SNeel Natu uint64_t
pci_emul_msix_tread(struct pci_devinst * pi,uint64_t offset,int size)451c9b4e987SNeel Natu pci_emul_msix_tread(struct pci_devinst *pi, uint64_t offset, int size)
452c9b4e987SNeel Natu {
453c9b4e987SNeel Natu char *dest;
454c9b4e987SNeel Natu int msix_entry_offset;
455c9b4e987SNeel Natu int tab_index;
456c9b4e987SNeel Natu uint64_t retval = ~0;
457c9b4e987SNeel Natu
4586a52209fSNeel Natu /*
4596a52209fSNeel Natu * The PCI standard only allows 4 and 8 byte accesses to the MSI-X
460463a577bSEitan Adler * table but we also allow 1 byte access to accommodate reads from
4616a52209fSNeel Natu * ddb.
4626a52209fSNeel Natu */
4636a52209fSNeel Natu if (size != 1 && size != 4 && size != 8)
464c9b4e987SNeel Natu return (retval);
465c9b4e987SNeel Natu
466c9b4e987SNeel Natu msix_entry_offset = offset % MSIX_TABLE_ENTRY_SIZE;
467c9b4e987SNeel Natu
468c9b4e987SNeel Natu /* support only aligned reads */
469c9b4e987SNeel Natu if ((msix_entry_offset % size) != 0) {
470c9b4e987SNeel Natu return (retval);
471c9b4e987SNeel Natu }
472c9b4e987SNeel Natu
473c9b4e987SNeel Natu tab_index = offset / MSIX_TABLE_ENTRY_SIZE;
474c9b4e987SNeel Natu
475c9b4e987SNeel Natu if (tab_index < pi->pi_msix.table_count) {
476c9b4e987SNeel Natu /* valid MSI-X Table access */
477c9b4e987SNeel Natu dest = (char *)(pi->pi_msix.table + tab_index);
478c9b4e987SNeel Natu dest += msix_entry_offset;
479c9b4e987SNeel Natu
4806a52209fSNeel Natu if (size == 1)
4816a52209fSNeel Natu retval = *((uint8_t *)dest);
4826a52209fSNeel Natu else if (size == 4)
483c9b4e987SNeel Natu retval = *((uint32_t *)dest);
484c9b4e987SNeel Natu else
485c9b4e987SNeel Natu retval = *((uint64_t *)dest);
486c9b4e987SNeel Natu } else if (pci_valid_pba_offset(pi, offset)) {
487c9b4e987SNeel Natu /* return 0 for PBA access */
488c9b4e987SNeel Natu retval = 0;
489c9b4e987SNeel Natu }
490c9b4e987SNeel Natu
491c9b4e987SNeel Natu return (retval);
492c9b4e987SNeel Natu }
493c9b4e987SNeel Natu
494aa12663fSNeel Natu int
pci_msix_table_bar(struct pci_devinst * pi)495aa12663fSNeel Natu pci_msix_table_bar(struct pci_devinst *pi)
496aa12663fSNeel Natu {
497aa12663fSNeel Natu
498aa12663fSNeel Natu if (pi->pi_msix.table != NULL)
499aa12663fSNeel Natu return (pi->pi_msix.table_bar);
500aa12663fSNeel Natu else
501aa12663fSNeel Natu return (-1);
502aa12663fSNeel Natu }
503aa12663fSNeel Natu
504aa12663fSNeel Natu int
pci_msix_pba_bar(struct pci_devinst * pi)505aa12663fSNeel Natu pci_msix_pba_bar(struct pci_devinst *pi)
506aa12663fSNeel Natu {
507aa12663fSNeel Natu
508aa12663fSNeel Natu if (pi->pi_msix.table != NULL)
509aa12663fSNeel Natu return (pi->pi_msix.pba_bar);
510aa12663fSNeel Natu else
511aa12663fSNeel Natu return (-1);
512aa12663fSNeel Natu }
513aa12663fSNeel Natu
51431cf78c9SMark Johnston #ifdef __amd64__
515c9b4e987SNeel Natu static int
pci_emul_io_handler(struct vmctx * ctx __unused,int in,int port,int bytes,uint32_t * eax,void * arg)5166a284cacSJohn Baldwin pci_emul_io_handler(struct vmctx *ctx __unused, int in, int port,
51778c2cd83SJohn Baldwin int bytes, uint32_t *eax, void *arg)
518366f6083SPeter Grehan {
519366f6083SPeter Grehan struct pci_devinst *pdi = arg;
520366f6083SPeter Grehan struct pci_devemu *pe = pdi->pi_d;
5214d1e669cSPeter Grehan uint64_t offset;
5224d1e669cSPeter Grehan int i;
523366f6083SPeter Grehan
524ed721684SMark Johnston assert(port >= 0);
525ed721684SMark Johnston
526366f6083SPeter Grehan for (i = 0; i <= PCI_BARMAX; i++) {
527366f6083SPeter Grehan if (pdi->pi_bar[i].type == PCIBAR_IO &&
528ed721684SMark Johnston (uint64_t)port >= pdi->pi_bar[i].addr &&
529ed721684SMark Johnston (uint64_t)port + bytes <=
530ed721684SMark Johnston pdi->pi_bar[i].addr + pdi->pi_bar[i].size) {
531366f6083SPeter Grehan offset = port - pdi->pi_bar[i].addr;
532366f6083SPeter Grehan if (in)
5336a284cacSJohn Baldwin *eax = (*pe->pe_barread)(pdi, i,
5344d1e669cSPeter Grehan offset, bytes);
535366f6083SPeter Grehan else
5366a284cacSJohn Baldwin (*pe->pe_barwrite)(pdi, i, offset,
5374d1e669cSPeter Grehan bytes, *eax);
538366f6083SPeter Grehan return (0);
539366f6083SPeter Grehan }
540366f6083SPeter Grehan }
541366f6083SPeter Grehan return (-1);
542366f6083SPeter Grehan }
54331cf78c9SMark Johnston #else
54431cf78c9SMark Johnston static int
pci_emul_iomem_handler(struct vcpu * vcpu __unused,int dir,uint64_t addr,int size,uint64_t * val,void * arg1,long arg2)54531cf78c9SMark Johnston pci_emul_iomem_handler(struct vcpu *vcpu __unused, int dir,
54631cf78c9SMark Johnston uint64_t addr, int size, uint64_t *val, void *arg1, long arg2)
54731cf78c9SMark Johnston {
54831cf78c9SMark Johnston struct pci_devinst *pdi = arg1;
54931cf78c9SMark Johnston struct pci_devemu *pe = pdi->pi_d;
55031cf78c9SMark Johnston uint64_t offset;
55131cf78c9SMark Johnston int bidx = (int)arg2;
55231cf78c9SMark Johnston
55331cf78c9SMark Johnston assert(bidx <= PCI_BARMAX);
55431cf78c9SMark Johnston assert(pdi->pi_bar[bidx].type == PCIBAR_IO);
55531cf78c9SMark Johnston assert(addr >= pdi->pi_bar[bidx].addr &&
55631cf78c9SMark Johnston addr + size <= pdi->pi_bar[bidx].addr + pdi->pi_bar[bidx].size);
55731cf78c9SMark Johnston assert(size == 1 || size == 2 || size == 4);
55831cf78c9SMark Johnston
55931cf78c9SMark Johnston offset = addr - pdi->pi_bar[bidx].addr;
56031cf78c9SMark Johnston if (dir == MEM_F_READ)
56131cf78c9SMark Johnston *val = (*pe->pe_barread)(pdi, bidx, offset, size);
56231cf78c9SMark Johnston else
56331cf78c9SMark Johnston (*pe->pe_barwrite)(pdi, bidx, offset, size, *val);
56431cf78c9SMark Johnston
56531cf78c9SMark Johnston return (0);
56631cf78c9SMark Johnston }
56731cf78c9SMark Johnston #endif /* !__amd64__ */
568366f6083SPeter Grehan
569366f6083SPeter Grehan static int
pci_emul_mem_handler(struct vcpu * vcpu __unused,int dir,uint64_t addr,int size,uint64_t * val,void * arg1,long arg2)5707d9ef309SJohn Baldwin pci_emul_mem_handler(struct vcpu *vcpu __unused, int dir,
57178c2cd83SJohn Baldwin uint64_t addr, int size, uint64_t *val, void *arg1, long arg2)
5724d1e669cSPeter Grehan {
5734d1e669cSPeter Grehan struct pci_devinst *pdi = arg1;
5744d1e669cSPeter Grehan struct pci_devemu *pe = pdi->pi_d;
5754d1e669cSPeter Grehan uint64_t offset;
5764d1e669cSPeter Grehan int bidx = (int)arg2;
5774d1e669cSPeter Grehan
5784d1e669cSPeter Grehan assert(bidx <= PCI_BARMAX);
5794d1e669cSPeter Grehan assert(pdi->pi_bar[bidx].type == PCIBAR_MEM32 ||
5804d1e669cSPeter Grehan pdi->pi_bar[bidx].type == PCIBAR_MEM64);
5814d1e669cSPeter Grehan assert(addr >= pdi->pi_bar[bidx].addr &&
5824d1e669cSPeter Grehan addr + size <= pdi->pi_bar[bidx].addr + pdi->pi_bar[bidx].size);
5834d1e669cSPeter Grehan
5844d1e669cSPeter Grehan offset = addr - pdi->pi_bar[bidx].addr;
5854d1e669cSPeter Grehan
586b6ae8b05STycho Nightingale if (dir == MEM_F_WRITE) {
58767b6ffaaSTycho Nightingale if (size == 8) {
5886a284cacSJohn Baldwin (*pe->pe_barwrite)(pdi, bidx, offset,
589b6ae8b05STycho Nightingale 4, *val & 0xffffffff);
5906a284cacSJohn Baldwin (*pe->pe_barwrite)(pdi, bidx, offset + 4,
591b6ae8b05STycho Nightingale 4, *val >> 32);
592b6ae8b05STycho Nightingale } else {
5936a284cacSJohn Baldwin (*pe->pe_barwrite)(pdi, bidx, offset,
594b6ae8b05STycho Nightingale size, *val);
595b6ae8b05STycho Nightingale }
596b6ae8b05STycho Nightingale } else {
59767b6ffaaSTycho Nightingale if (size == 8) {
5986a284cacSJohn Baldwin *val = (*pe->pe_barread)(pdi, bidx,
599b6ae8b05STycho Nightingale offset, 4);
6006a284cacSJohn Baldwin *val |= (*pe->pe_barread)(pdi, bidx,
601b6ae8b05STycho Nightingale offset + 4, 4) << 32;
602b6ae8b05STycho Nightingale } else {
6036a284cacSJohn Baldwin *val = (*pe->pe_barread)(pdi, bidx,
604b6ae8b05STycho Nightingale offset, size);
605b6ae8b05STycho Nightingale }
606b6ae8b05STycho Nightingale }
6074d1e669cSPeter Grehan
6084d1e669cSPeter Grehan return (0);
6094d1e669cSPeter Grehan }
6104d1e669cSPeter Grehan
6114d1e669cSPeter Grehan
6124d1e669cSPeter Grehan static int
pci_emul_alloc_resource(uint64_t * baseptr,uint64_t limit,uint64_t size,uint64_t * addr)613366f6083SPeter Grehan pci_emul_alloc_resource(uint64_t *baseptr, uint64_t limit, uint64_t size,
614366f6083SPeter Grehan uint64_t *addr)
615366f6083SPeter Grehan {
616366f6083SPeter Grehan uint64_t base;
617366f6083SPeter Grehan
618366f6083SPeter Grehan assert((size & (size - 1)) == 0); /* must be a power of 2 */
619366f6083SPeter Grehan
620366f6083SPeter Grehan base = roundup2(*baseptr, size);
621366f6083SPeter Grehan
622366f6083SPeter Grehan if (base + size <= limit) {
623366f6083SPeter Grehan *addr = base;
624366f6083SPeter Grehan *baseptr = base + size;
625366f6083SPeter Grehan return (0);
626366f6083SPeter Grehan } else
627366f6083SPeter Grehan return (-1);
628366f6083SPeter Grehan }
629366f6083SPeter Grehan
630028d9311SNeel Natu /*
631028d9311SNeel Natu * Register (or unregister) the MMIO or I/O region associated with the BAR
632028d9311SNeel Natu * register 'idx' of an emulated pci device.
633028d9311SNeel Natu */
634028d9311SNeel Natu static void
modify_bar_registration(struct pci_devinst * pi,int idx,int registration)635028d9311SNeel Natu modify_bar_registration(struct pci_devinst *pi, int idx, int registration)
636028d9311SNeel Natu {
637f8a6ec2dSD Scott Phillips struct pci_devemu *pe;
638028d9311SNeel Natu int error;
63931cf78c9SMark Johnston enum pcibar_type type;
640028d9311SNeel Natu
641f8a6ec2dSD Scott Phillips pe = pi->pi_d;
64231cf78c9SMark Johnston type = pi->pi_bar[idx].type;
64331cf78c9SMark Johnston switch (type) {
644028d9311SNeel Natu case PCIBAR_IO:
64531cf78c9SMark Johnston {
64631cf78c9SMark Johnston #ifdef __amd64__
64731cf78c9SMark Johnston struct inout_port iop;
64831cf78c9SMark Johnston
649028d9311SNeel Natu bzero(&iop, sizeof(struct inout_port));
650028d9311SNeel Natu iop.name = pi->pi_name;
651028d9311SNeel Natu iop.port = pi->pi_bar[idx].addr;
652028d9311SNeel Natu iop.size = pi->pi_bar[idx].size;
653028d9311SNeel Natu if (registration) {
654028d9311SNeel Natu iop.flags = IOPORT_F_INOUT;
655028d9311SNeel Natu iop.handler = pci_emul_io_handler;
656028d9311SNeel Natu iop.arg = pi;
657028d9311SNeel Natu error = register_inout(&iop);
658028d9311SNeel Natu } else
659028d9311SNeel Natu error = unregister_inout(&iop);
66031cf78c9SMark Johnston #else
66131cf78c9SMark Johnston struct mem_range mr;
66231cf78c9SMark Johnston
66331cf78c9SMark Johnston bzero(&mr, sizeof(struct mem_range));
66431cf78c9SMark Johnston mr.name = pi->pi_name;
66531cf78c9SMark Johnston mr.base = pi->pi_bar[idx].addr;
66631cf78c9SMark Johnston mr.size = pi->pi_bar[idx].size;
66731cf78c9SMark Johnston if (registration) {
66831cf78c9SMark Johnston mr.flags = MEM_F_RW;
66931cf78c9SMark Johnston mr.handler = pci_emul_iomem_handler;
67031cf78c9SMark Johnston mr.arg1 = pi;
67131cf78c9SMark Johnston mr.arg2 = idx;
67231cf78c9SMark Johnston error = register_mem(&mr);
67331cf78c9SMark Johnston } else
67431cf78c9SMark Johnston error = unregister_mem(&mr);
67531cf78c9SMark Johnston #endif
676028d9311SNeel Natu break;
67731cf78c9SMark Johnston }
678028d9311SNeel Natu case PCIBAR_MEM32:
679028d9311SNeel Natu case PCIBAR_MEM64:
68031cf78c9SMark Johnston {
68131cf78c9SMark Johnston struct mem_range mr;
68231cf78c9SMark Johnston
683028d9311SNeel Natu bzero(&mr, sizeof(struct mem_range));
684028d9311SNeel Natu mr.name = pi->pi_name;
685028d9311SNeel Natu mr.base = pi->pi_bar[idx].addr;
686028d9311SNeel Natu mr.size = pi->pi_bar[idx].size;
687028d9311SNeel Natu if (registration) {
688028d9311SNeel Natu mr.flags = MEM_F_RW;
689028d9311SNeel Natu mr.handler = pci_emul_mem_handler;
690028d9311SNeel Natu mr.arg1 = pi;
691028d9311SNeel Natu mr.arg2 = idx;
692028d9311SNeel Natu error = register_mem(&mr);
693028d9311SNeel Natu } else
694028d9311SNeel Natu error = unregister_mem(&mr);
695028d9311SNeel Natu break;
69631cf78c9SMark Johnston }
697e47fe318SCorvin Köhne case PCIBAR_ROM:
698e47fe318SCorvin Köhne error = 0;
699e47fe318SCorvin Köhne break;
700028d9311SNeel Natu default:
701028d9311SNeel Natu error = EINVAL;
702028d9311SNeel Natu break;
703028d9311SNeel Natu }
704028d9311SNeel Natu assert(error == 0);
7050dea4f06SMark Johnston
7060dea4f06SMark Johnston if (pe->pe_baraddr != NULL)
7070dea4f06SMark Johnston (*pe->pe_baraddr)(pi, idx, registration, pi->pi_bar[idx].addr);
708028d9311SNeel Natu }
709028d9311SNeel Natu
710028d9311SNeel Natu static void
unregister_bar(struct pci_devinst * pi,int idx)711028d9311SNeel Natu unregister_bar(struct pci_devinst *pi, int idx)
712028d9311SNeel Natu {
713028d9311SNeel Natu
714028d9311SNeel Natu modify_bar_registration(pi, idx, 0);
715028d9311SNeel Natu }
716028d9311SNeel Natu
717028d9311SNeel Natu static void
register_bar(struct pci_devinst * pi,int idx)718028d9311SNeel Natu register_bar(struct pci_devinst *pi, int idx)
719028d9311SNeel Natu {
720028d9311SNeel Natu
721028d9311SNeel Natu modify_bar_registration(pi, idx, 1);
722028d9311SNeel Natu }
723028d9311SNeel Natu
724e47fe318SCorvin Köhne /* Is the ROM enabled for the emulated pci device? */
725e47fe318SCorvin Köhne static int
romen(struct pci_devinst * pi)726e47fe318SCorvin Köhne romen(struct pci_devinst *pi)
727e47fe318SCorvin Köhne {
728e47fe318SCorvin Köhne return (pi->pi_bar[PCI_ROM_IDX].lobits & PCIM_BIOS_ENABLE) ==
729e47fe318SCorvin Köhne PCIM_BIOS_ENABLE;
730e47fe318SCorvin Köhne }
731e47fe318SCorvin Köhne
732028d9311SNeel Natu /* Are we decoding i/o port accesses for the emulated pci device? */
733028d9311SNeel Natu static int
porten(struct pci_devinst * pi)734028d9311SNeel Natu porten(struct pci_devinst *pi)
735028d9311SNeel Natu {
736028d9311SNeel Natu uint16_t cmd;
737028d9311SNeel Natu
738028d9311SNeel Natu cmd = pci_get_cfgdata16(pi, PCIR_COMMAND);
739028d9311SNeel Natu
740028d9311SNeel Natu return (cmd & PCIM_CMD_PORTEN);
741028d9311SNeel Natu }
742028d9311SNeel Natu
743028d9311SNeel Natu /* Are we decoding memory accesses for the emulated pci device? */
744028d9311SNeel Natu static int
memen(struct pci_devinst * pi)745028d9311SNeel Natu memen(struct pci_devinst *pi)
746028d9311SNeel Natu {
747028d9311SNeel Natu uint16_t cmd;
748028d9311SNeel Natu
749028d9311SNeel Natu cmd = pci_get_cfgdata16(pi, PCIR_COMMAND);
750028d9311SNeel Natu
751028d9311SNeel Natu return (cmd & PCIM_CMD_MEMEN);
752028d9311SNeel Natu }
753028d9311SNeel Natu
754028d9311SNeel Natu /*
755028d9311SNeel Natu * Update the MMIO or I/O address that is decoded by the BAR register.
756028d9311SNeel Natu *
757028d9311SNeel Natu * If the pci device has enabled the address space decoding then intercept
758028d9311SNeel Natu * the address range decoded by the BAR register.
759028d9311SNeel Natu */
760028d9311SNeel Natu static void
update_bar_address(struct pci_devinst * pi,uint64_t addr,int idx,int type)761028d9311SNeel Natu update_bar_address(struct pci_devinst *pi, uint64_t addr, int idx, int type)
762028d9311SNeel Natu {
763028d9311SNeel Natu int decode;
764028d9311SNeel Natu
765028d9311SNeel Natu if (pi->pi_bar[idx].type == PCIBAR_IO)
766028d9311SNeel Natu decode = porten(pi);
767028d9311SNeel Natu else
768028d9311SNeel Natu decode = memen(pi);
769028d9311SNeel Natu
770028d9311SNeel Natu if (decode)
771028d9311SNeel Natu unregister_bar(pi, idx);
772028d9311SNeel Natu
773028d9311SNeel Natu switch (type) {
774028d9311SNeel Natu case PCIBAR_IO:
775028d9311SNeel Natu case PCIBAR_MEM32:
776028d9311SNeel Natu pi->pi_bar[idx].addr = addr;
777028d9311SNeel Natu break;
778028d9311SNeel Natu case PCIBAR_MEM64:
779028d9311SNeel Natu pi->pi_bar[idx].addr &= ~0xffffffffUL;
780028d9311SNeel Natu pi->pi_bar[idx].addr |= addr;
781028d9311SNeel Natu break;
782028d9311SNeel Natu case PCIBAR_MEMHI64:
783028d9311SNeel Natu pi->pi_bar[idx].addr &= 0xffffffff;
784028d9311SNeel Natu pi->pi_bar[idx].addr |= addr;
785028d9311SNeel Natu break;
786028d9311SNeel Natu default:
787028d9311SNeel Natu assert(0);
788028d9311SNeel Natu }
789028d9311SNeel Natu
790028d9311SNeel Natu if (decode)
791028d9311SNeel Natu register_bar(pi, idx);
792028d9311SNeel Natu }
793028d9311SNeel Natu
7944d1e669cSPeter Grehan int
pci_emul_alloc_bar(struct pci_devinst * pdi,int idx,enum pcibar_type type,uint64_t size)795038f5c7bSKonstantin Belousov pci_emul_alloc_bar(struct pci_devinst *pdi, int idx, enum pcibar_type type,
796038f5c7bSKonstantin Belousov uint64_t size)
797366f6083SPeter Grehan {
798e47fe318SCorvin Köhne assert((type == PCIBAR_ROM) || (idx >= 0 && idx <= PCI_BARMAX));
799e47fe318SCorvin Köhne assert((type != PCIBAR_ROM) || (idx == PCI_ROM_IDX));
800366f6083SPeter Grehan
801366f6083SPeter Grehan if ((size & (size - 1)) != 0)
802366f6083SPeter Grehan size = 1UL << flsl(size); /* round up to a power of 2 */
803366f6083SPeter Grehan
804028d9311SNeel Natu /* Enforce minimum BAR sizes required by the PCI standard */
805028d9311SNeel Natu if (type == PCIBAR_IO) {
806028d9311SNeel Natu if (size < 4)
807028d9311SNeel Natu size = 4;
808e47fe318SCorvin Köhne } else if (type == PCIBAR_ROM) {
809e47fe318SCorvin Köhne if (size < ~PCIM_BIOS_ADDR_MASK + 1)
810e47fe318SCorvin Köhne size = ~PCIM_BIOS_ADDR_MASK + 1;
811028d9311SNeel Natu } else {
812028d9311SNeel Natu if (size < 16)
813028d9311SNeel Natu size = 16;
814028d9311SNeel Natu }
815028d9311SNeel Natu
81601f9362eSCorvin Köhne /*
81701f9362eSCorvin Köhne * To reduce fragmentation of the MMIO space, we allocate the BARs by
81801f9362eSCorvin Köhne * size. Therefore, don't allocate the BAR yet. We create a list of all
81901f9362eSCorvin Köhne * BAR allocation which is sorted by BAR size. When all PCI devices are
82001f9362eSCorvin Köhne * initialized, we will assign an address to the BARs.
82101f9362eSCorvin Köhne */
82201f9362eSCorvin Köhne
82301f9362eSCorvin Köhne /* create a new list entry */
82401f9362eSCorvin Köhne struct pci_bar_allocation *const new_bar = malloc(sizeof(*new_bar));
82501f9362eSCorvin Köhne memset(new_bar, 0, sizeof(*new_bar));
82601f9362eSCorvin Köhne new_bar->pdi = pdi;
82701f9362eSCorvin Köhne new_bar->idx = idx;
82801f9362eSCorvin Köhne new_bar->type = type;
82901f9362eSCorvin Köhne new_bar->size = size;
83001f9362eSCorvin Köhne
83101f9362eSCorvin Köhne /*
83201f9362eSCorvin Köhne * Search for a BAR which size is lower than the size of our newly
83301f9362eSCorvin Köhne * allocated BAR.
83401f9362eSCorvin Köhne */
83501f9362eSCorvin Köhne struct pci_bar_allocation *bar = NULL;
83601f9362eSCorvin Köhne TAILQ_FOREACH(bar, &pci_bars, chain) {
83701f9362eSCorvin Köhne if (bar->size < size) {
83801f9362eSCorvin Köhne break;
83901f9362eSCorvin Köhne }
84001f9362eSCorvin Köhne }
84101f9362eSCorvin Köhne
84201f9362eSCorvin Köhne if (bar == NULL) {
84301f9362eSCorvin Köhne /*
84401f9362eSCorvin Köhne * Either the list is empty or new BAR is the smallest BAR of
84501f9362eSCorvin Köhne * the list. Append it to the end of our list.
84601f9362eSCorvin Köhne */
84701f9362eSCorvin Köhne TAILQ_INSERT_TAIL(&pci_bars, new_bar, chain);
84801f9362eSCorvin Köhne } else {
84901f9362eSCorvin Köhne /*
85001f9362eSCorvin Köhne * The found BAR is smaller than our new BAR. For that reason,
85101f9362eSCorvin Köhne * insert our new BAR before the found BAR.
85201f9362eSCorvin Köhne */
85301f9362eSCorvin Köhne TAILQ_INSERT_BEFORE(bar, new_bar, chain);
85401f9362eSCorvin Köhne }
85501f9362eSCorvin Köhne
85601f9362eSCorvin Köhne /*
85701f9362eSCorvin Köhne * pci_passthru devices synchronize their physical and virtual command
85801f9362eSCorvin Köhne * register on init. For that reason, the virtual cmd reg should be
85901f9362eSCorvin Köhne * updated as early as possible.
86001f9362eSCorvin Köhne */
86101f9362eSCorvin Köhne uint16_t enbit = 0;
86201f9362eSCorvin Köhne switch (type) {
86301f9362eSCorvin Köhne case PCIBAR_IO:
86401f9362eSCorvin Köhne enbit = PCIM_CMD_PORTEN;
86501f9362eSCorvin Köhne break;
86601f9362eSCorvin Köhne case PCIBAR_MEM64:
86701f9362eSCorvin Köhne case PCIBAR_MEM32:
86801f9362eSCorvin Köhne enbit = PCIM_CMD_MEMEN;
86901f9362eSCorvin Köhne break;
87001f9362eSCorvin Köhne default:
87101f9362eSCorvin Köhne enbit = 0;
87201f9362eSCorvin Köhne break;
87301f9362eSCorvin Köhne }
87401f9362eSCorvin Köhne
87501f9362eSCorvin Köhne const uint16_t cmd = pci_get_cfgdata16(pdi, PCIR_COMMAND);
87601f9362eSCorvin Köhne pci_set_cfgdata16(pdi, PCIR_COMMAND, cmd | enbit);
87701f9362eSCorvin Köhne
87801f9362eSCorvin Köhne return (0);
87901f9362eSCorvin Köhne }
88001f9362eSCorvin Köhne
88101f9362eSCorvin Köhne static int
pci_emul_assign_bar(struct pci_devinst * const pdi,const int idx,const enum pcibar_type type,const uint64_t size)88201f9362eSCorvin Köhne pci_emul_assign_bar(struct pci_devinst *const pdi, const int idx,
88301f9362eSCorvin Köhne const enum pcibar_type type, const uint64_t size)
88401f9362eSCorvin Köhne {
88501f9362eSCorvin Köhne int error;
88601f9362eSCorvin Köhne uint64_t *baseptr, limit, addr, mask, lobits, bar;
88701f9362eSCorvin Köhne
888366f6083SPeter Grehan switch (type) {
889366f6083SPeter Grehan case PCIBAR_NONE:
890366f6083SPeter Grehan baseptr = NULL;
89101f9362eSCorvin Köhne addr = mask = lobits = 0;
892366f6083SPeter Grehan break;
893366f6083SPeter Grehan case PCIBAR_IO:
894366f6083SPeter Grehan baseptr = &pci_emul_iobase;
895366f6083SPeter Grehan limit = PCI_EMUL_IOLIMIT;
896366f6083SPeter Grehan mask = PCIM_BAR_IO_BASE;
897366f6083SPeter Grehan lobits = PCIM_BAR_IO_SPACE;
898366f6083SPeter Grehan break;
899366f6083SPeter Grehan case PCIBAR_MEM64:
900366f6083SPeter Grehan /*
901366f6083SPeter Grehan * XXX
902366f6083SPeter Grehan * Some drivers do not work well if the 64-bit BAR is allocated
903366f6083SPeter Grehan * above 4GB. Allow for this by allocating small requests under
904366f6083SPeter Grehan * 4GB unless then allocation size is larger than some arbitrary
905670b364bSKonstantin Belousov * number (128MB currently).
906366f6083SPeter Grehan */
907670b364bSKonstantin Belousov if (size > 128 * 1024 * 1024) {
908366f6083SPeter Grehan baseptr = &pci_emul_membase64;
9099922872bSKonstantin Belousov limit = pci_emul_memlim64;
910366f6083SPeter Grehan mask = PCIM_BAR_MEM_BASE;
911366f6083SPeter Grehan lobits = PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_64 |
912366f6083SPeter Grehan PCIM_BAR_MEM_PREFETCH;
91325d4944eSNeel Natu } else {
91425d4944eSNeel Natu baseptr = &pci_emul_membase32;
91525d4944eSNeel Natu limit = PCI_EMUL_MEMLIMIT32;
91625d4944eSNeel Natu mask = PCIM_BAR_MEM_BASE;
91725d4944eSNeel Natu lobits = PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_64;
918366f6083SPeter Grehan }
91925d4944eSNeel Natu break;
920366f6083SPeter Grehan case PCIBAR_MEM32:
921366f6083SPeter Grehan baseptr = &pci_emul_membase32;
922366f6083SPeter Grehan limit = PCI_EMUL_MEMLIMIT32;
923366f6083SPeter Grehan mask = PCIM_BAR_MEM_BASE;
924366f6083SPeter Grehan lobits = PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_32;
925366f6083SPeter Grehan break;
926e47fe318SCorvin Köhne case PCIBAR_ROM:
927e47fe318SCorvin Köhne /* do not claim memory for ROM. OVMF will do it for us. */
928e47fe318SCorvin Köhne baseptr = NULL;
929e47fe318SCorvin Köhne limit = 0;
930e47fe318SCorvin Köhne mask = PCIM_BIOS_ADDR_MASK;
931e47fe318SCorvin Köhne lobits = 0;
932e47fe318SCorvin Köhne break;
933366f6083SPeter Grehan default:
934366f6083SPeter Grehan printf("pci_emul_alloc_base: invalid bar type %d\n", type);
935366f6083SPeter Grehan assert(0);
936366f6083SPeter Grehan }
937366f6083SPeter Grehan
938366f6083SPeter Grehan if (baseptr != NULL) {
939366f6083SPeter Grehan error = pci_emul_alloc_resource(baseptr, limit, size, &addr);
940366f6083SPeter Grehan if (error != 0)
941366f6083SPeter Grehan return (error);
9428ac8addaSCorvin Köhne } else {
9438ac8addaSCorvin Köhne addr = 0;
944366f6083SPeter Grehan }
945366f6083SPeter Grehan
946366f6083SPeter Grehan pdi->pi_bar[idx].type = type;
947366f6083SPeter Grehan pdi->pi_bar[idx].addr = addr;
948366f6083SPeter Grehan pdi->pi_bar[idx].size = size;
949e87a6f3eSCorvin Köhne /*
950e87a6f3eSCorvin Köhne * passthru devices are using same lobits as physical device they set
951e87a6f3eSCorvin Köhne * this property
952e87a6f3eSCorvin Köhne */
953e87a6f3eSCorvin Köhne if (pdi->pi_bar[idx].lobits != 0) {
954e87a6f3eSCorvin Köhne lobits = pdi->pi_bar[idx].lobits;
955e87a6f3eSCorvin Köhne } else {
956e87a6f3eSCorvin Köhne pdi->pi_bar[idx].lobits = lobits;
957e87a6f3eSCorvin Köhne }
958366f6083SPeter Grehan
959366f6083SPeter Grehan /* Initialize the BAR register in config space */
960366f6083SPeter Grehan bar = (addr & mask) | lobits;
961366f6083SPeter Grehan pci_set_cfgdata32(pdi, PCIR_BAR(idx), bar);
962366f6083SPeter Grehan
963366f6083SPeter Grehan if (type == PCIBAR_MEM64) {
964366f6083SPeter Grehan assert(idx + 1 <= PCI_BARMAX);
965366f6083SPeter Grehan pdi->pi_bar[idx + 1].type = PCIBAR_MEMHI64;
966366f6083SPeter Grehan pci_set_cfgdata32(pdi, PCIR_BAR(idx + 1), bar >> 32);
967366f6083SPeter Grehan }
968366f6083SPeter Grehan
969e47fe318SCorvin Köhne if (type != PCIBAR_ROM) {
970028d9311SNeel Natu register_bar(pdi, idx);
971e47fe318SCorvin Köhne }
972e47fe318SCorvin Köhne
973e47fe318SCorvin Köhne return (0);
974e47fe318SCorvin Köhne }
975e47fe318SCorvin Köhne
976e47fe318SCorvin Köhne int
pci_emul_alloc_rom(struct pci_devinst * const pdi,const uint64_t size,void ** const addr)977e47fe318SCorvin Köhne pci_emul_alloc_rom(struct pci_devinst *const pdi, const uint64_t size,
978e47fe318SCorvin Köhne void **const addr)
979e47fe318SCorvin Köhne {
980e47fe318SCorvin Köhne /* allocate ROM space once on first call */
981e47fe318SCorvin Köhne if (pci_emul_rombase == 0) {
982e47fe318SCorvin Köhne pci_emul_rombase = vm_create_devmem(pdi->pi_vmctx, VM_PCIROM,
983e47fe318SCorvin Köhne "pcirom", PCI_EMUL_ROMSIZE);
984e47fe318SCorvin Köhne if (pci_emul_rombase == MAP_FAILED) {
985e47fe318SCorvin Köhne warnx("%s: failed to create rom segment", __func__);
986e47fe318SCorvin Köhne return (-1);
987e47fe318SCorvin Köhne }
988e47fe318SCorvin Köhne pci_emul_romlim = pci_emul_rombase + PCI_EMUL_ROMSIZE;
989e47fe318SCorvin Köhne pci_emul_romoffset = 0;
990e47fe318SCorvin Köhne }
991e47fe318SCorvin Köhne
992e47fe318SCorvin Köhne /* ROM size should be a power of 2 and greater than 2 KB */
993e47fe318SCorvin Köhne const uint64_t rom_size = MAX(1UL << flsl(size),
994e47fe318SCorvin Köhne ~PCIM_BIOS_ADDR_MASK + 1);
995e47fe318SCorvin Köhne
996e47fe318SCorvin Köhne /* check if ROM fits into ROM space */
997e47fe318SCorvin Köhne if (pci_emul_romoffset + rom_size > PCI_EMUL_ROMSIZE) {
998e47fe318SCorvin Köhne warnx("%s: no space left in rom segment:", __func__);
999e47fe318SCorvin Köhne warnx("%16lu bytes left",
1000e47fe318SCorvin Köhne PCI_EMUL_ROMSIZE - pci_emul_romoffset);
1001e47fe318SCorvin Köhne warnx("%16lu bytes required by %d/%d/%d", rom_size, pdi->pi_bus,
1002e47fe318SCorvin Köhne pdi->pi_slot, pdi->pi_func);
1003e47fe318SCorvin Köhne return (-1);
1004e47fe318SCorvin Köhne }
1005e47fe318SCorvin Köhne
1006e47fe318SCorvin Köhne /* allocate ROM BAR */
1007e47fe318SCorvin Köhne const int error = pci_emul_alloc_bar(pdi, PCI_ROM_IDX, PCIBAR_ROM,
1008e47fe318SCorvin Köhne rom_size);
1009e47fe318SCorvin Köhne if (error)
1010e47fe318SCorvin Köhne return error;
1011e47fe318SCorvin Köhne
1012e47fe318SCorvin Köhne /* return address */
1013e47fe318SCorvin Köhne *addr = pci_emul_rombase + pci_emul_romoffset;
1014e47fe318SCorvin Köhne
1015e47fe318SCorvin Köhne /* save offset into ROM Space */
1016e47fe318SCorvin Köhne pdi->pi_romoffset = pci_emul_romoffset;
1017e47fe318SCorvin Köhne
1018e47fe318SCorvin Köhne /* increase offset for next ROM */
1019e47fe318SCorvin Köhne pci_emul_romoffset += rom_size;
1020366f6083SPeter Grehan
1021366f6083SPeter Grehan return (0);
1022366f6083SPeter Grehan }
1023366f6083SPeter Grehan
10246632a0a4SCorvin Köhne int
pci_emul_add_boot_device(struct pci_devinst * pi,int bootindex)10256632a0a4SCorvin Köhne pci_emul_add_boot_device(struct pci_devinst *pi, int bootindex)
10266632a0a4SCorvin Köhne {
10276632a0a4SCorvin Köhne struct boot_device *new_device, *device;
10286632a0a4SCorvin Köhne
10296632a0a4SCorvin Köhne /* don't permit a negative bootindex */
10306632a0a4SCorvin Köhne if (bootindex < 0) {
10316632a0a4SCorvin Köhne errx(4, "Invalid bootindex %d for %s", bootindex, pi->pi_name);
10326632a0a4SCorvin Köhne }
10336632a0a4SCorvin Köhne
10346632a0a4SCorvin Köhne /* alloc new boot device */
10356632a0a4SCorvin Köhne new_device = calloc(1, sizeof(struct boot_device));
10366632a0a4SCorvin Köhne if (new_device == NULL) {
10376632a0a4SCorvin Köhne return (ENOMEM);
10386632a0a4SCorvin Köhne }
10396632a0a4SCorvin Köhne new_device->pdi = pi;
10406632a0a4SCorvin Köhne new_device->bootindex = bootindex;
10416632a0a4SCorvin Köhne
10426632a0a4SCorvin Köhne /* search for boot device with higher boot index */
10436632a0a4SCorvin Köhne TAILQ_FOREACH(device, &boot_devices, boot_device_chain) {
10446632a0a4SCorvin Köhne if (device->bootindex == bootindex) {
10456632a0a4SCorvin Köhne errx(4,
10466632a0a4SCorvin Köhne "Could not set bootindex %d for %s. Bootindex already occupied by %s",
10476632a0a4SCorvin Köhne bootindex, pi->pi_name, device->pdi->pi_name);
10486632a0a4SCorvin Köhne } else if (device->bootindex > bootindex) {
10496632a0a4SCorvin Köhne break;
10506632a0a4SCorvin Köhne }
10516632a0a4SCorvin Köhne }
10526632a0a4SCorvin Köhne
10536632a0a4SCorvin Köhne /* add boot device to queue */
10546632a0a4SCorvin Köhne if (device == NULL) {
10556632a0a4SCorvin Köhne TAILQ_INSERT_TAIL(&boot_devices, new_device, boot_device_chain);
10566632a0a4SCorvin Köhne } else {
10576632a0a4SCorvin Köhne TAILQ_INSERT_BEFORE(device, new_device, boot_device_chain);
10586632a0a4SCorvin Köhne }
10596632a0a4SCorvin Köhne
10606632a0a4SCorvin Köhne return (0);
10616632a0a4SCorvin Köhne }
10626632a0a4SCorvin Köhne
1063366f6083SPeter Grehan #define CAP_START_OFFSET 0x40
1064366f6083SPeter Grehan static int
pci_emul_add_capability(struct pci_devinst * pi,u_char * capdata,int caplen)1065366f6083SPeter Grehan pci_emul_add_capability(struct pci_devinst *pi, u_char *capdata, int caplen)
1066366f6083SPeter Grehan {
1067a96b8b80SJohn Baldwin int i, capoff, reallen;
1068366f6083SPeter Grehan uint16_t sts;
1069366f6083SPeter Grehan
1070a96b8b80SJohn Baldwin assert(caplen > 0);
1071366f6083SPeter Grehan
1072366f6083SPeter Grehan reallen = roundup2(caplen, 4); /* dword aligned */
1073366f6083SPeter Grehan
1074366f6083SPeter Grehan sts = pci_get_cfgdata16(pi, PCIR_STATUS);
1075a96b8b80SJohn Baldwin if ((sts & PCIM_STATUS_CAPPRESENT) == 0)
1076366f6083SPeter Grehan capoff = CAP_START_OFFSET;
1077a96b8b80SJohn Baldwin else
1078a96b8b80SJohn Baldwin capoff = pi->pi_capend + 1;
1079366f6083SPeter Grehan
1080366f6083SPeter Grehan /* Check if we have enough space */
1081a96b8b80SJohn Baldwin if (capoff + reallen > PCI_REGMAX + 1)
1082366f6083SPeter Grehan return (-1);
1083366f6083SPeter Grehan
1084a96b8b80SJohn Baldwin /* Set the previous capability pointer */
1085a96b8b80SJohn Baldwin if ((sts & PCIM_STATUS_CAPPRESENT) == 0) {
1086a96b8b80SJohn Baldwin pci_set_cfgdata8(pi, PCIR_CAP_PTR, capoff);
1087a96b8b80SJohn Baldwin pci_set_cfgdata16(pi, PCIR_STATUS, sts|PCIM_STATUS_CAPPRESENT);
1088a96b8b80SJohn Baldwin } else
1089a96b8b80SJohn Baldwin pci_set_cfgdata8(pi, pi->pi_prevcap + 1, capoff);
1090a96b8b80SJohn Baldwin
1091366f6083SPeter Grehan /* Copy the capability */
1092366f6083SPeter Grehan for (i = 0; i < caplen; i++)
1093366f6083SPeter Grehan pci_set_cfgdata8(pi, capoff + i, capdata[i]);
1094366f6083SPeter Grehan
1095366f6083SPeter Grehan /* Set the next capability pointer */
1096a96b8b80SJohn Baldwin pci_set_cfgdata8(pi, capoff + 1, 0);
1097366f6083SPeter Grehan
1098a96b8b80SJohn Baldwin pi->pi_prevcap = capoff;
1099a96b8b80SJohn Baldwin pi->pi_capend = capoff + reallen - 1;
1100366f6083SPeter Grehan return (0);
1101366f6083SPeter Grehan }
1102366f6083SPeter Grehan
1103366f6083SPeter Grehan static struct pci_devemu *
pci_emul_finddev(const char * name)1104621b5090SJohn Baldwin pci_emul_finddev(const char *name)
1105366f6083SPeter Grehan {
1106366f6083SPeter Grehan struct pci_devemu **pdpp, *pdp;
1107366f6083SPeter Grehan
1108366f6083SPeter Grehan SET_FOREACH(pdpp, pci_devemu_set) {
1109366f6083SPeter Grehan pdp = *pdpp;
1110366f6083SPeter Grehan if (!strcmp(pdp->pe_emu, name)) {
1111366f6083SPeter Grehan return (pdp);
1112366f6083SPeter Grehan }
1113366f6083SPeter Grehan }
1114366f6083SPeter Grehan
1115366f6083SPeter Grehan return (NULL);
1116366f6083SPeter Grehan }
1117366f6083SPeter Grehan
1118a38e2a64SPeter Grehan static int
pci_emul_init(struct vmctx * ctx,struct pci_devemu * pde,int bus,int slot,int func,struct funcinfo * fi)1119d84882caSNeel Natu pci_emul_init(struct vmctx *ctx, struct pci_devemu *pde, int bus, int slot,
1120d84882caSNeel Natu int func, struct funcinfo *fi)
1121366f6083SPeter Grehan {
1122366f6083SPeter Grehan struct pci_devinst *pdi;
1123a38e2a64SPeter Grehan int err;
1124a38e2a64SPeter Grehan
1125994f858aSXin LI pdi = calloc(1, sizeof(struct pci_devinst));
1126366f6083SPeter Grehan
1127366f6083SPeter Grehan pdi->pi_vmctx = ctx;
1128d84882caSNeel Natu pdi->pi_bus = bus;
1129366f6083SPeter Grehan pdi->pi_slot = slot;
113099d65389SNeel Natu pdi->pi_func = func;
11313cbf3585SJohn Baldwin pthread_mutex_init(&pdi->pi_lintr.lock, NULL);
11323cbf3585SJohn Baldwin pdi->pi_lintr.pin = 0;
11333cbf3585SJohn Baldwin pdi->pi_lintr.state = IDLE;
11340efad4acSJessica Clarke pci_irq_init_irq(&pdi->pi_lintr.irq);
1135366f6083SPeter Grehan pdi->pi_d = pde;
113614c80457SVitaliy Gusev snprintf(pdi->pi_name, PI_NAMESZ, "%s@pci.%d.%d.%d", pde->pe_emu, bus,
113714c80457SVitaliy Gusev slot, func);
1138366f6083SPeter Grehan
1139366f6083SPeter Grehan /* Disable legacy interrupts */
1140366f6083SPeter Grehan pci_set_cfgdata8(pdi, PCIR_INTLINE, 255);
1141366f6083SPeter Grehan pci_set_cfgdata8(pdi, PCIR_INTPIN, 0);
1142366f6083SPeter Grehan
11432729c9bbSJohn Baldwin pci_set_cfgdata8(pdi, PCIR_COMMAND, PCIM_CMD_BUSMASTEREN);
1144366f6083SPeter Grehan
11456a284cacSJohn Baldwin err = (*pde->pe_init)(pdi, fi->fi_config);
1146d84882caSNeel Natu if (err == 0)
1147d84882caSNeel Natu fi->fi_devi = pdi;
1148d84882caSNeel Natu else
1149366f6083SPeter Grehan free(pdi);
1150a38e2a64SPeter Grehan
1151a38e2a64SPeter Grehan return (err);
1152366f6083SPeter Grehan }
1153366f6083SPeter Grehan
1154366f6083SPeter Grehan void
pci_populate_msicap(struct msicap * msicap,int msgnum,int nextptr)1155366f6083SPeter Grehan pci_populate_msicap(struct msicap *msicap, int msgnum, int nextptr)
1156366f6083SPeter Grehan {
1157366f6083SPeter Grehan int mmc;
1158366f6083SPeter Grehan
1159366f6083SPeter Grehan /* Number of msi messages must be a power of 2 between 1 and 32 */
1160366f6083SPeter Grehan assert((msgnum & (msgnum - 1)) == 0 && msgnum >= 1 && msgnum <= 32);
1161366f6083SPeter Grehan mmc = ffs(msgnum) - 1;
1162366f6083SPeter Grehan
1163366f6083SPeter Grehan bzero(msicap, sizeof(struct msicap));
1164366f6083SPeter Grehan msicap->capid = PCIY_MSI;
1165366f6083SPeter Grehan msicap->nextptr = nextptr;
1166366f6083SPeter Grehan msicap->msgctrl = PCIM_MSICTRL_64BIT | (mmc << 1);
1167366f6083SPeter Grehan }
1168366f6083SPeter Grehan
1169366f6083SPeter Grehan int
pci_emul_add_msicap(struct pci_devinst * pi,int msgnum)1170366f6083SPeter Grehan pci_emul_add_msicap(struct pci_devinst *pi, int msgnum)
1171366f6083SPeter Grehan {
1172366f6083SPeter Grehan struct msicap msicap;
1173366f6083SPeter Grehan
1174366f6083SPeter Grehan pci_populate_msicap(&msicap, msgnum, 0);
1175366f6083SPeter Grehan
1176366f6083SPeter Grehan return (pci_emul_add_capability(pi, (u_char *)&msicap, sizeof(msicap)));
1177366f6083SPeter Grehan }
1178366f6083SPeter Grehan
1179c9b4e987SNeel Natu static void
pci_populate_msixcap(struct msixcap * msixcap,int msgnum,int barnum,uint32_t msix_tab_size)1180c9b4e987SNeel Natu pci_populate_msixcap(struct msixcap *msixcap, int msgnum, int barnum,
1181a96b8b80SJohn Baldwin uint32_t msix_tab_size)
1182c9b4e987SNeel Natu {
1183c9b4e987SNeel Natu
1184c9b4e987SNeel Natu assert(msix_tab_size % 4096 == 0);
1185c9b4e987SNeel Natu
1186c9b4e987SNeel Natu bzero(msixcap, sizeof(struct msixcap));
1187c9b4e987SNeel Natu msixcap->capid = PCIY_MSIX;
1188c9b4e987SNeel Natu
1189c9b4e987SNeel Natu /*
1190c9b4e987SNeel Natu * Message Control Register, all fields set to
1191c9b4e987SNeel Natu * zero except for the Table Size.
1192c9b4e987SNeel Natu * Note: Table size N is encoded as N-1
1193c9b4e987SNeel Natu */
1194c9b4e987SNeel Natu msixcap->msgctrl = msgnum - 1;
1195c9b4e987SNeel Natu
1196c9b4e987SNeel Natu /*
1197c9b4e987SNeel Natu * MSI-X BAR setup:
1198c9b4e987SNeel Natu * - MSI-X table start at offset 0
1199c9b4e987SNeel Natu * - PBA table starts at a 4K aligned offset after the MSI-X table
1200c9b4e987SNeel Natu */
1201c9b4e987SNeel Natu msixcap->table_info = barnum & PCIM_MSIX_BIR_MASK;
1202c9b4e987SNeel Natu msixcap->pba_info = msix_tab_size | (barnum & PCIM_MSIX_BIR_MASK);
1203c9b4e987SNeel Natu }
1204c9b4e987SNeel Natu
1205c9b4e987SNeel Natu static void
pci_msix_table_init(struct pci_devinst * pi,int table_entries)1206c9b4e987SNeel Natu pci_msix_table_init(struct pci_devinst *pi, int table_entries)
1207c9b4e987SNeel Natu {
1208c9b4e987SNeel Natu int i, table_size;
1209c9b4e987SNeel Natu
1210c9b4e987SNeel Natu assert(table_entries > 0);
1211c9b4e987SNeel Natu assert(table_entries <= MAX_MSIX_TABLE_ENTRIES);
1212c9b4e987SNeel Natu
1213c9b4e987SNeel Natu table_size = table_entries * MSIX_TABLE_ENTRY_SIZE;
1214994f858aSXin LI pi->pi_msix.table = calloc(1, table_size);
1215c9b4e987SNeel Natu
1216c9b4e987SNeel Natu /* set mask bit of vector control register */
1217c9b4e987SNeel Natu for (i = 0; i < table_entries; i++)
1218c9b4e987SNeel Natu pi->pi_msix.table[i].vector_control |= PCIM_MSIX_VCTRL_MASK;
1219c9b4e987SNeel Natu }
1220c9b4e987SNeel Natu
1221c9b4e987SNeel Natu int
pci_emul_add_msixcap(struct pci_devinst * pi,int msgnum,int barnum)1222c9b4e987SNeel Natu pci_emul_add_msixcap(struct pci_devinst *pi, int msgnum, int barnum)
1223c9b4e987SNeel Natu {
1224c9b4e987SNeel Natu uint32_t tab_size;
1225c9b4e987SNeel Natu struct msixcap msixcap;
1226c9b4e987SNeel Natu
1227c9b4e987SNeel Natu assert(msgnum >= 1 && msgnum <= MAX_MSIX_TABLE_ENTRIES);
1228c9b4e987SNeel Natu assert(barnum >= 0 && barnum <= PCIR_MAX_BAR_0);
1229c9b4e987SNeel Natu
1230c9b4e987SNeel Natu tab_size = msgnum * MSIX_TABLE_ENTRY_SIZE;
1231c9b4e987SNeel Natu
1232c9b4e987SNeel Natu /* Align table size to nearest 4K */
1233c9b4e987SNeel Natu tab_size = roundup2(tab_size, 4096);
1234c9b4e987SNeel Natu
1235c9b4e987SNeel Natu pi->pi_msix.table_bar = barnum;
1236c9b4e987SNeel Natu pi->pi_msix.pba_bar = barnum;
1237c9b4e987SNeel Natu pi->pi_msix.table_offset = 0;
1238c9b4e987SNeel Natu pi->pi_msix.table_count = msgnum;
1239c9b4e987SNeel Natu pi->pi_msix.pba_offset = tab_size;
12407a902ec0SNeel Natu pi->pi_msix.pba_size = PBA_SIZE(msgnum);
1241c9b4e987SNeel Natu
1242c9b4e987SNeel Natu pci_msix_table_init(pi, msgnum);
1243c9b4e987SNeel Natu
1244a96b8b80SJohn Baldwin pci_populate_msixcap(&msixcap, msgnum, barnum, tab_size);
1245c9b4e987SNeel Natu
1246c9b4e987SNeel Natu /* allocate memory for MSI-X Table and PBA */
1247c9b4e987SNeel Natu pci_emul_alloc_bar(pi, barnum, PCIBAR_MEM32,
1248c9b4e987SNeel Natu tab_size + pi->pi_msix.pba_size);
1249c9b4e987SNeel Natu
1250c9b4e987SNeel Natu return (pci_emul_add_capability(pi, (u_char *)&msixcap,
1251c9b4e987SNeel Natu sizeof(msixcap)));
1252c9b4e987SNeel Natu }
1253c9b4e987SNeel Natu
125421368498SPeter Grehan static void
msixcap_cfgwrite(struct pci_devinst * pi,int capoff,int offset,int bytes,uint32_t val)1255cd942e0fSPeter Grehan msixcap_cfgwrite(struct pci_devinst *pi, int capoff, int offset,
1256cd942e0fSPeter Grehan int bytes, uint32_t val)
1257cd942e0fSPeter Grehan {
1258cd942e0fSPeter Grehan uint16_t msgctrl, rwmask;
1259d74fdc6aSMarcelo Araujo int off;
1260cd942e0fSPeter Grehan
1261cd942e0fSPeter Grehan off = offset - capoff;
1262cd942e0fSPeter Grehan /* Message Control Register */
1263cd942e0fSPeter Grehan if (off == 2 && bytes == 2) {
1264cd942e0fSPeter Grehan rwmask = PCIM_MSIXCTRL_MSIX_ENABLE | PCIM_MSIXCTRL_FUNCTION_MASK;
1265cd942e0fSPeter Grehan msgctrl = pci_get_cfgdata16(pi, offset);
1266cd942e0fSPeter Grehan msgctrl &= ~rwmask;
1267cd942e0fSPeter Grehan msgctrl |= val & rwmask;
1268cd942e0fSPeter Grehan val = msgctrl;
1269cd942e0fSPeter Grehan
1270cd942e0fSPeter Grehan pi->pi_msix.enabled = val & PCIM_MSIXCTRL_MSIX_ENABLE;
1271c9b4e987SNeel Natu pi->pi_msix.function_mask = val & PCIM_MSIXCTRL_FUNCTION_MASK;
12723cbf3585SJohn Baldwin pci_lintr_update(pi);
1273cd942e0fSPeter Grehan }
1274cd942e0fSPeter Grehan
1275cd942e0fSPeter Grehan CFGWRITE(pi, offset, val, bytes);
1276cd942e0fSPeter Grehan }
1277cd942e0fSPeter Grehan
127821368498SPeter Grehan static void
msicap_cfgwrite(struct pci_devinst * pi,int capoff,int offset,int bytes,uint32_t val)1279366f6083SPeter Grehan msicap_cfgwrite(struct pci_devinst *pi, int capoff, int offset,
1280366f6083SPeter Grehan int bytes, uint32_t val)
1281366f6083SPeter Grehan {
1282366f6083SPeter Grehan uint16_t msgctrl, rwmask, msgdata, mme;
1283366f6083SPeter Grehan uint32_t addrlo;
1284366f6083SPeter Grehan
1285366f6083SPeter Grehan /*
1286366f6083SPeter Grehan * If guest is writing to the message control register make sure
1287366f6083SPeter Grehan * we do not overwrite read-only fields.
1288366f6083SPeter Grehan */
1289366f6083SPeter Grehan if ((offset - capoff) == 2 && bytes == 2) {
1290366f6083SPeter Grehan rwmask = PCIM_MSICTRL_MME_MASK | PCIM_MSICTRL_MSI_ENABLE;
1291366f6083SPeter Grehan msgctrl = pci_get_cfgdata16(pi, offset);
1292366f6083SPeter Grehan msgctrl &= ~rwmask;
1293366f6083SPeter Grehan msgctrl |= val & rwmask;
1294366f6083SPeter Grehan val = msgctrl;
12957840d1c4SJohn Baldwin }
12967840d1c4SJohn Baldwin CFGWRITE(pi, offset, val, bytes);
1297366f6083SPeter Grehan
12987840d1c4SJohn Baldwin msgctrl = pci_get_cfgdata16(pi, capoff + 2);
1299366f6083SPeter Grehan addrlo = pci_get_cfgdata32(pi, capoff + 4);
1300366f6083SPeter Grehan if (msgctrl & PCIM_MSICTRL_64BIT)
1301366f6083SPeter Grehan msgdata = pci_get_cfgdata16(pi, capoff + 12);
1302366f6083SPeter Grehan else
1303366f6083SPeter Grehan msgdata = pci_get_cfgdata16(pi, capoff + 8);
1304366f6083SPeter Grehan
1305366f6083SPeter Grehan mme = msgctrl & PCIM_MSICTRL_MME_MASK;
1306366f6083SPeter Grehan pi->pi_msi.enabled = msgctrl & PCIM_MSICTRL_MSI_ENABLE ? 1 : 0;
1307366f6083SPeter Grehan if (pi->pi_msi.enabled) {
13084f8be175SNeel Natu pi->pi_msi.addr = addrlo;
13094f8be175SNeel Natu pi->pi_msi.msg_data = msgdata;
13104f8be175SNeel Natu pi->pi_msi.maxmsgnum = 1 << (mme >> 4);
1311366f6083SPeter Grehan } else {
13124f8be175SNeel Natu pi->pi_msi.maxmsgnum = 0;
1313366f6083SPeter Grehan }
13143cbf3585SJohn Baldwin pci_lintr_update(pi);
1315366f6083SPeter Grehan }
1316366f6083SPeter Grehan
131798d920d9SMark Johnston static void
pciecap_cfgwrite(struct pci_devinst * pi,int capoff __unused,int offset,int bytes,uint32_t val)131898d920d9SMark Johnston pciecap_cfgwrite(struct pci_devinst *pi, int capoff __unused, int offset,
131974f80b23SNeel Natu int bytes, uint32_t val)
132074f80b23SNeel Natu {
132174f80b23SNeel Natu
132274f80b23SNeel Natu /* XXX don't write to the readonly parts */
132374f80b23SNeel Natu CFGWRITE(pi, offset, val, bytes);
132474f80b23SNeel Natu }
132574f80b23SNeel Natu
132674f80b23SNeel Natu #define PCIECAP_VERSION 0x2
132774f80b23SNeel Natu int
pci_emul_add_pciecap(struct pci_devinst * pi,int type)132874f80b23SNeel Natu pci_emul_add_pciecap(struct pci_devinst *pi, int type)
132974f80b23SNeel Natu {
133074f80b23SNeel Natu int err;
133174f80b23SNeel Natu struct pciecap pciecap;
133274f80b23SNeel Natu
133374f80b23SNeel Natu bzero(&pciecap, sizeof(pciecap));
133474f80b23SNeel Natu
1335129f93c5SChuck Tuffli /*
1336129f93c5SChuck Tuffli * Use the integrated endpoint type for endpoints on a root complex bus.
1337129f93c5SChuck Tuffli *
1338129f93c5SChuck Tuffli * NB: bhyve currently only supports a single PCI bus that is the root
1339129f93c5SChuck Tuffli * complex bus, so all endpoints are integrated.
1340129f93c5SChuck Tuffli */
1341129f93c5SChuck Tuffli if ((type == PCIEM_TYPE_ENDPOINT) && (pi->pi_bus == 0))
1342129f93c5SChuck Tuffli type = PCIEM_TYPE_ROOT_INT_EP;
1343129f93c5SChuck Tuffli
134474f80b23SNeel Natu pciecap.capid = PCIY_EXPRESS;
1345129f93c5SChuck Tuffli pciecap.pcie_capabilities = PCIECAP_VERSION | type;
1346129f93c5SChuck Tuffli if (type != PCIEM_TYPE_ROOT_INT_EP) {
134774f80b23SNeel Natu pciecap.link_capabilities = 0x411; /* gen1, x1 */
134874f80b23SNeel Natu pciecap.link_status = 0x11; /* gen1, x1 */
1349129f93c5SChuck Tuffli }
135074f80b23SNeel Natu
135174f80b23SNeel Natu err = pci_emul_add_capability(pi, (u_char *)&pciecap, sizeof(pciecap));
135274f80b23SNeel Natu return (err);
135374f80b23SNeel Natu }
135474f80b23SNeel Natu
1355366f6083SPeter Grehan /*
1356366f6083SPeter Grehan * This function assumes that 'coff' is in the capabilities region of the
135721368498SPeter Grehan * config space. A capoff parameter of zero will force a search for the
135821368498SPeter Grehan * offset and type.
1359366f6083SPeter Grehan */
136021368498SPeter Grehan void
pci_emul_capwrite(struct pci_devinst * pi,int offset,int bytes,uint32_t val,uint8_t capoff,int capid)136121368498SPeter Grehan pci_emul_capwrite(struct pci_devinst *pi, int offset, int bytes, uint32_t val,
136221368498SPeter Grehan uint8_t capoff, int capid)
1363366f6083SPeter Grehan {
136421368498SPeter Grehan uint8_t nextoff;
1365366f6083SPeter Grehan
1366366f6083SPeter Grehan /* Do not allow un-aligned writes */
1367366f6083SPeter Grehan if ((offset & (bytes - 1)) != 0)
1368366f6083SPeter Grehan return;
1369366f6083SPeter Grehan
137021368498SPeter Grehan if (capoff == 0) {
1371366f6083SPeter Grehan /* Find the capability that we want to update */
1372366f6083SPeter Grehan capoff = CAP_START_OFFSET;
1373366f6083SPeter Grehan while (1) {
1374366f6083SPeter Grehan nextoff = pci_get_cfgdata8(pi, capoff + 1);
1375a96b8b80SJohn Baldwin if (nextoff == 0)
1376a96b8b80SJohn Baldwin break;
1377366f6083SPeter Grehan if (offset >= capoff && offset < nextoff)
1378366f6083SPeter Grehan break;
1379366f6083SPeter Grehan
1380366f6083SPeter Grehan capoff = nextoff;
1381366f6083SPeter Grehan }
1382366f6083SPeter Grehan assert(offset >= capoff);
138321368498SPeter Grehan capid = pci_get_cfgdata8(pi, capoff);
138421368498SPeter Grehan }
1385366f6083SPeter Grehan
1386366f6083SPeter Grehan /*
13872a8d400aSPeter Grehan * Capability ID and Next Capability Pointer are readonly.
13882a8d400aSPeter Grehan * However, some o/s's do 4-byte writes that include these.
13892a8d400aSPeter Grehan * For this case, trim the write back to 2 bytes and adjust
13902a8d400aSPeter Grehan * the data.
1391366f6083SPeter Grehan */
13922a8d400aSPeter Grehan if (offset == capoff || offset == capoff + 1) {
13932a8d400aSPeter Grehan if (offset == capoff && bytes == 4) {
13942a8d400aSPeter Grehan bytes = 2;
13952a8d400aSPeter Grehan offset += 2;
13962a8d400aSPeter Grehan val >>= 16;
13972a8d400aSPeter Grehan } else
1398366f6083SPeter Grehan return;
13992a8d400aSPeter Grehan }
1400366f6083SPeter Grehan
1401366f6083SPeter Grehan switch (capid) {
1402366f6083SPeter Grehan case PCIY_MSI:
1403366f6083SPeter Grehan msicap_cfgwrite(pi, capoff, offset, bytes, val);
1404366f6083SPeter Grehan break;
1405c9b4e987SNeel Natu case PCIY_MSIX:
1406c9b4e987SNeel Natu msixcap_cfgwrite(pi, capoff, offset, bytes, val);
1407c9b4e987SNeel Natu break;
140874f80b23SNeel Natu case PCIY_EXPRESS:
140974f80b23SNeel Natu pciecap_cfgwrite(pi, capoff, offset, bytes, val);
141074f80b23SNeel Natu break;
1411366f6083SPeter Grehan default:
1412366f6083SPeter Grehan break;
1413366f6083SPeter Grehan }
1414366f6083SPeter Grehan }
1415366f6083SPeter Grehan
1416366f6083SPeter Grehan static int
pci_emul_iscap(struct pci_devinst * pi,int offset)1417366f6083SPeter Grehan pci_emul_iscap(struct pci_devinst *pi, int offset)
1418366f6083SPeter Grehan {
1419366f6083SPeter Grehan uint16_t sts;
1420366f6083SPeter Grehan
1421366f6083SPeter Grehan sts = pci_get_cfgdata16(pi, PCIR_STATUS);
1422366f6083SPeter Grehan if ((sts & PCIM_STATUS_CAPPRESENT) != 0) {
1423a96b8b80SJohn Baldwin if (offset >= CAP_START_OFFSET && offset <= pi->pi_capend)
1424a96b8b80SJohn Baldwin return (1);
1425366f6083SPeter Grehan }
1426a96b8b80SJohn Baldwin return (0);
1427366f6083SPeter Grehan }
1428366f6083SPeter Grehan
14290ab13648SPeter Grehan static int
pci_emul_fallback_handler(struct vcpu * vcpu __unused,int dir,uint64_t addr __unused,int size __unused,uint64_t * val,void * arg1 __unused,long arg2 __unused)14307d9ef309SJohn Baldwin pci_emul_fallback_handler(struct vcpu *vcpu __unused, int dir,
14317d9ef309SJohn Baldwin uint64_t addr __unused, int size __unused, uint64_t *val,
143298d920d9SMark Johnston void *arg1 __unused, long arg2 __unused)
14330ab13648SPeter Grehan {
14340ab13648SPeter Grehan /*
14350ab13648SPeter Grehan * Ignore writes; return 0xff's for reads. The mem read code
14360ab13648SPeter Grehan * will take care of truncating to the correct size.
14370ab13648SPeter Grehan */
14380ab13648SPeter Grehan if (dir == MEM_F_READ) {
14390ab13648SPeter Grehan *val = 0xffffffffffffffff;
14400ab13648SPeter Grehan }
14410ab13648SPeter Grehan
14420ab13648SPeter Grehan return (0);
14430ab13648SPeter Grehan }
14440ab13648SPeter Grehan
144512a6eb99SNeel Natu static int
pci_emul_ecfg_handler(struct vcpu * vcpu __unused,int dir,uint64_t addr,int bytes,uint64_t * val,void * arg1 __unused,long arg2 __unused)14467d9ef309SJohn Baldwin pci_emul_ecfg_handler(struct vcpu *vcpu __unused, int dir, uint64_t addr,
14477d9ef309SJohn Baldwin int bytes, uint64_t *val, void *arg1 __unused, long arg2 __unused)
144812a6eb99SNeel Natu {
144912a6eb99SNeel Natu int bus, slot, func, coff, in;
145012a6eb99SNeel Natu
145112a6eb99SNeel Natu coff = addr & 0xfff;
145212a6eb99SNeel Natu func = (addr >> 12) & 0x7;
145312a6eb99SNeel Natu slot = (addr >> 15) & 0x1f;
145412a6eb99SNeel Natu bus = (addr >> 20) & 0xff;
145512a6eb99SNeel Natu in = (dir == MEM_F_READ);
145612a6eb99SNeel Natu if (in)
145712a6eb99SNeel Natu *val = ~0UL;
14586a284cacSJohn Baldwin pci_cfgrw(in, bus, slot, func, coff, bytes, (uint32_t *)val);
145912a6eb99SNeel Natu return (0);
146012a6eb99SNeel Natu }
146112a6eb99SNeel Natu
146212a6eb99SNeel Natu uint64_t
pci_ecfg_base(void)146312a6eb99SNeel Natu pci_ecfg_base(void)
146412a6eb99SNeel Natu {
146512a6eb99SNeel Natu
146612a6eb99SNeel Natu return (PCI_EMUL_ECFG_BASE);
146712a6eb99SNeel Natu }
146812a6eb99SNeel Natu
14696632a0a4SCorvin Köhne static int
init_bootorder(void)14706632a0a4SCorvin Köhne init_bootorder(void)
14716632a0a4SCorvin Köhne {
14726632a0a4SCorvin Köhne struct boot_device *device;
14736632a0a4SCorvin Köhne FILE *fp;
14746632a0a4SCorvin Köhne char *bootorder;
14756632a0a4SCorvin Köhne size_t bootorder_len;
14766632a0a4SCorvin Köhne
14776632a0a4SCorvin Köhne if (TAILQ_EMPTY(&boot_devices))
14786632a0a4SCorvin Köhne return (0);
14796632a0a4SCorvin Köhne
14806632a0a4SCorvin Köhne fp = open_memstream(&bootorder, &bootorder_len);
14816632a0a4SCorvin Köhne TAILQ_FOREACH(device, &boot_devices, boot_device_chain) {
14826632a0a4SCorvin Köhne fprintf(fp, "/pci@i0cf8/pci@%d,%d\n",
14836632a0a4SCorvin Köhne device->pdi->pi_slot, device->pdi->pi_func);
14846632a0a4SCorvin Köhne }
14856632a0a4SCorvin Köhne fclose(fp);
14866632a0a4SCorvin Köhne
14876632a0a4SCorvin Köhne return (qemu_fwcfg_add_file("bootorder", bootorder_len, bootorder));
14886632a0a4SCorvin Köhne }
14896632a0a4SCorvin Köhne
1490d84882caSNeel Natu #define BUSIO_ROUNDUP 32
14917d55d295SCorvin Köhne #define BUSMEM32_ROUNDUP (1024 * 1024)
14927d55d295SCorvin Köhne #define BUSMEM64_ROUNDUP (512 * 1024 * 1024)
1493d84882caSNeel Natu
1494a38e2a64SPeter Grehan int
init_pci(struct vmctx * ctx)1495366f6083SPeter Grehan init_pci(struct vmctx *ctx)
1496366f6083SPeter Grehan {
1497621b5090SJohn Baldwin char node_name[sizeof("pci.XXX.XX.X")];
149812a6eb99SNeel Natu struct mem_range mr;
1499366f6083SPeter Grehan struct pci_devemu *pde;
1500d84882caSNeel Natu struct businfo *bi;
1501d84882caSNeel Natu struct slotinfo *si;
15023cbf3585SJohn Baldwin struct funcinfo *fi;
1503621b5090SJohn Baldwin nvlist_t *nvl;
1504621b5090SJohn Baldwin const char *emul;
15059f08548dSNeel Natu size_t lowmem;
15064a4053e1SCorvin Köhne int bus, slot, func;
15074a4053e1SCorvin Köhne int error;
1508366f6083SPeter Grehan
15095cf21e48SCorvin Köhne if (vm_get_lowmem_limit(ctx) > PCI_EMUL_MEMBASE32)
15105cf21e48SCorvin Köhne errx(EX_OSERR, "Invalid lowmem limit");
15115cf21e48SCorvin Köhne
1512366f6083SPeter Grehan pci_emul_iobase = PCI_EMUL_IOBASE;
15135cf21e48SCorvin Köhne pci_emul_membase32 = PCI_EMUL_MEMBASE32;
15149922872bSKonstantin Belousov
1515e497fe86SMark Johnston pci_emul_membase64 = vm_get_highmem_base(ctx) +
1516e497fe86SMark Johnston vm_get_highmem_size(ctx);
15174a4053e1SCorvin Köhne pci_emul_membase64 = roundup2(pci_emul_membase64, PCI_EMUL_MEMSIZE64);
15184a4053e1SCorvin Köhne pci_emul_memlim64 = pci_emul_membase64 + PCI_EMUL_MEMSIZE64;
1519366f6083SPeter Grehan
15206632a0a4SCorvin Köhne TAILQ_INIT(&boot_devices);
15216632a0a4SCorvin Köhne
1522d84882caSNeel Natu for (bus = 0; bus < MAXBUSES; bus++) {
1523621b5090SJohn Baldwin snprintf(node_name, sizeof(node_name), "pci.%d", bus);
1524621b5090SJohn Baldwin nvl = find_config_node(node_name);
1525621b5090SJohn Baldwin if (nvl == NULL)
1526d84882caSNeel Natu continue;
1527621b5090SJohn Baldwin pci_businfo[bus] = calloc(1, sizeof(struct businfo));
1528621b5090SJohn Baldwin bi = pci_businfo[bus];
1529621b5090SJohn Baldwin
1530d84882caSNeel Natu /*
1531d84882caSNeel Natu * Keep track of the i/o and memory resources allocated to
1532d84882caSNeel Natu * this bus.
1533d84882caSNeel Natu */
1534d84882caSNeel Natu bi->iobase = pci_emul_iobase;
1535d84882caSNeel Natu bi->membase32 = pci_emul_membase32;
1536d84882caSNeel Natu bi->membase64 = pci_emul_membase64;
1537d84882caSNeel Natu
153801f9362eSCorvin Köhne /* first run: init devices */
153999d65389SNeel Natu for (slot = 0; slot < MAXSLOTS; slot++) {
1540d84882caSNeel Natu si = &bi->slotinfo[slot];
154199d65389SNeel Natu for (func = 0; func < MAXFUNCS; func++) {
1542d84882caSNeel Natu fi = &si->si_funcs[func];
1543621b5090SJohn Baldwin snprintf(node_name, sizeof(node_name),
1544621b5090SJohn Baldwin "pci.%d.%d.%d", bus, slot, func);
1545621b5090SJohn Baldwin nvl = find_config_node(node_name);
1546621b5090SJohn Baldwin if (nvl == NULL)
1547d84882caSNeel Natu continue;
1548621b5090SJohn Baldwin
1549621b5090SJohn Baldwin fi->fi_config = nvl;
1550621b5090SJohn Baldwin emul = get_config_value_node(nvl, "device");
1551621b5090SJohn Baldwin if (emul == NULL) {
1552621b5090SJohn Baldwin EPRINTLN("pci slot %d:%d:%d: missing "
1553621b5090SJohn Baldwin "\"device\" value", bus, slot, func);
1554621b5090SJohn Baldwin return (EINVAL);
1555621b5090SJohn Baldwin }
1556621b5090SJohn Baldwin pde = pci_emul_finddev(emul);
1557621b5090SJohn Baldwin if (pde == NULL) {
1558621b5090SJohn Baldwin EPRINTLN("pci slot %d:%d:%d: unknown "
1559621b5090SJohn Baldwin "device \"%s\"", bus, slot, func,
1560621b5090SJohn Baldwin emul);
1561621b5090SJohn Baldwin return (EINVAL);
1562621b5090SJohn Baldwin }
1563621b5090SJohn Baldwin if (pde->pe_alias != NULL) {
1564621b5090SJohn Baldwin EPRINTLN("pci slot %d:%d:%d: legacy "
1565621b5090SJohn Baldwin "device \"%s\", use \"%s\" instead",
1566621b5090SJohn Baldwin bus, slot, func, emul,
1567621b5090SJohn Baldwin pde->pe_alias);
1568621b5090SJohn Baldwin return (EINVAL);
1569621b5090SJohn Baldwin }
1570621b5090SJohn Baldwin fi->fi_pde = pde;
1571d84882caSNeel Natu error = pci_emul_init(ctx, pde, bus, slot,
1572d84882caSNeel Natu func, fi);
1573a38e2a64SPeter Grehan if (error)
1574a38e2a64SPeter Grehan return (error);
1575366f6083SPeter Grehan }
1576366f6083SPeter Grehan }
1577d84882caSNeel Natu
157801f9362eSCorvin Köhne /* second run: assign BARs and free list */
157901f9362eSCorvin Köhne struct pci_bar_allocation *bar;
158001f9362eSCorvin Köhne struct pci_bar_allocation *bar_tmp;
158101f9362eSCorvin Köhne TAILQ_FOREACH_SAFE(bar, &pci_bars, chain, bar_tmp) {
158201f9362eSCorvin Köhne pci_emul_assign_bar(bar->pdi, bar->idx, bar->type,
158301f9362eSCorvin Köhne bar->size);
158401f9362eSCorvin Köhne free(bar);
158501f9362eSCorvin Köhne }
158601f9362eSCorvin Köhne TAILQ_INIT(&pci_bars);
158701f9362eSCorvin Köhne
1588d84882caSNeel Natu /*
1589d84882caSNeel Natu * Add some slop to the I/O and memory resources decoded by
1590d84882caSNeel Natu * this bus to give a guest some flexibility if it wants to
1591d84882caSNeel Natu * reprogram the BARs.
1592d84882caSNeel Natu */
1593d84882caSNeel Natu pci_emul_iobase += BUSIO_ROUNDUP;
1594d84882caSNeel Natu pci_emul_iobase = roundup2(pci_emul_iobase, BUSIO_ROUNDUP);
1595d84882caSNeel Natu bi->iolimit = pci_emul_iobase;
1596d84882caSNeel Natu
15977d55d295SCorvin Köhne pci_emul_membase32 += BUSMEM32_ROUNDUP;
1598d84882caSNeel Natu pci_emul_membase32 = roundup2(pci_emul_membase32,
15997d55d295SCorvin Köhne BUSMEM32_ROUNDUP);
1600d84882caSNeel Natu bi->memlimit32 = pci_emul_membase32;
1601d84882caSNeel Natu
16027d55d295SCorvin Köhne pci_emul_membase64 += BUSMEM64_ROUNDUP;
1603d84882caSNeel Natu pci_emul_membase64 = roundup2(pci_emul_membase64,
16047d55d295SCorvin Köhne BUSMEM64_ROUNDUP);
1605d84882caSNeel Natu bi->memlimit64 = pci_emul_membase64;
1606366f6083SPeter Grehan }
16070038ee98SPeter Grehan
16080038ee98SPeter Grehan /*
1609b3e9732aSJohn Baldwin * PCI backends are initialized before routing INTx interrupts
1610b3e9732aSJohn Baldwin * so that LPC devices are able to reserve ISA IRQs before
1611b3e9732aSJohn Baldwin * routing PIRQ pins.
1612b3e9732aSJohn Baldwin */
1613b3e9732aSJohn Baldwin for (bus = 0; bus < MAXBUSES; bus++) {
1614b3e9732aSJohn Baldwin if ((bi = pci_businfo[bus]) == NULL)
1615b3e9732aSJohn Baldwin continue;
1616b3e9732aSJohn Baldwin
1617b3e9732aSJohn Baldwin for (slot = 0; slot < MAXSLOTS; slot++) {
1618b3e9732aSJohn Baldwin si = &bi->slotinfo[slot];
1619b3e9732aSJohn Baldwin for (func = 0; func < MAXFUNCS; func++) {
1620b3e9732aSJohn Baldwin fi = &si->si_funcs[func];
1621b3e9732aSJohn Baldwin if (fi->fi_devi == NULL)
1622b3e9732aSJohn Baldwin continue;
1623b3e9732aSJohn Baldwin pci_lintr_route(fi->fi_devi);
1624b3e9732aSJohn Baldwin }
1625b3e9732aSJohn Baldwin }
1626b3e9732aSJohn Baldwin }
16270efad4acSJessica Clarke #ifdef __amd64__
1628b3e9732aSJohn Baldwin lpc_pirq_routed();
162955c13f6eSMark Johnston #endif
1630b3e9732aSJohn Baldwin
16316632a0a4SCorvin Köhne if ((error = init_bootorder()) != 0) {
16326632a0a4SCorvin Köhne warnx("%s: Unable to init bootorder", __func__);
16336632a0a4SCorvin Köhne return (error);
16346632a0a4SCorvin Köhne }
16356632a0a4SCorvin Köhne
1636b3e9732aSJohn Baldwin /*
1637f286f746SMark Johnston * The guest physical memory map looks like the following on amd64:
16389f08548dSNeel Natu * [0, lowmem) guest system memory
16395cf21e48SCorvin Köhne * [lowmem, 0xC0000000) memory hole (may be absent)
16405cf21e48SCorvin Köhne * [0xC0000000, 0xE0000000) PCI hole (32-bit BAR allocation)
164112a6eb99SNeel Natu * [0xE0000000, 0xF0000000) PCI extended config window
164212a6eb99SNeel Natu * [0xF0000000, 4GB) LAPIC, IOAPIC, HPET, firmware
1643f286f746SMark Johnston * [4GB, 4GB + highmem) guest system memory
1644f286f746SMark Johnston * [roundup(4GB + highmem, 32GB), ...) PCI 64-bit BAR allocation
1645f286f746SMark Johnston *
1646f286f746SMark Johnston * On arm64 the guest physical memory map looks like this:
1647f286f746SMark Johnston * [0x0DF00000, 0x10000000) PCI I/O memory
1648f286f746SMark Johnston * [0xA0000000, 0xE0000000) PCI 32-bit BAR allocation
1649f286f746SMark Johnston * [0xE0000000, 0xF0000000) PCI extended config window
1650f286f746SMark Johnston * [4GB, 4GB + highmem) guest system memory
1651f286f746SMark Johnston * [roundup(4GB + highmem, 32GB), ...) PCI 64-bit BAR allocation
1652f286f746SMark Johnston *
1653f286f746SMark Johnston * "lowmem" is guest memory below 0xC0000000. amd64 guests provisioned
1654f286f746SMark Johnston * with less than 3GB of RAM will have no memory above the 4GB boundary.
1655f286f746SMark Johnston * System memory for arm64 guests is all above the 4GB boundary.
165612a6eb99SNeel Natu */
165712a6eb99SNeel Natu
165812a6eb99SNeel Natu /*
16599f08548dSNeel Natu * Accesses to memory addresses that are not allocated to system
16609f08548dSNeel Natu * memory or PCI devices return 0xff's.
16610ab13648SPeter Grehan */
1662be679db4SNeel Natu lowmem = vm_get_lowmem_size(ctx);
166312a6eb99SNeel Natu bzero(&mr, sizeof(struct mem_range));
166412a6eb99SNeel Natu mr.name = "PCI hole";
166512a6eb99SNeel Natu mr.flags = MEM_F_RW | MEM_F_IMMUTABLE;
166612a6eb99SNeel Natu mr.base = lowmem;
166712a6eb99SNeel Natu mr.size = (4ULL * 1024 * 1024 * 1024) - lowmem;
166812a6eb99SNeel Natu mr.handler = pci_emul_fallback_handler;
166912a6eb99SNeel Natu error = register_mem_fallback(&mr);
167012a6eb99SNeel Natu assert(error == 0);
16719f08548dSNeel Natu
167212a6eb99SNeel Natu /* PCI extended config space */
167312a6eb99SNeel Natu bzero(&mr, sizeof(struct mem_range));
167412a6eb99SNeel Natu mr.name = "PCI ECFG";
167512a6eb99SNeel Natu mr.flags = MEM_F_RW | MEM_F_IMMUTABLE;
167612a6eb99SNeel Natu mr.base = PCI_EMUL_ECFG_BASE;
167712a6eb99SNeel Natu mr.size = PCI_EMUL_ECFG_SIZE;
167812a6eb99SNeel Natu mr.handler = pci_emul_ecfg_handler;
167912a6eb99SNeel Natu error = register_mem(&mr);
16800ab13648SPeter Grehan assert(error == 0);
1681a38e2a64SPeter Grehan
1682a38e2a64SPeter Grehan return (0);
1683366f6083SPeter Grehan }
1684366f6083SPeter Grehan
168555c13f6eSMark Johnston #ifdef __amd64__
16863cbf3585SJohn Baldwin static void
pci_apic_prt_entry(int bus __unused,int slot,int pin,struct pci_irq * irq,void * arg __unused)16870efad4acSJessica Clarke pci_apic_prt_entry(int bus __unused, int slot, int pin, struct pci_irq *irq,
16880efad4acSJessica Clarke void *arg __unused)
16893cbf3585SJohn Baldwin {
16903cbf3585SJohn Baldwin
1691b3e9732aSJohn Baldwin dsdt_line(" Package ()");
16923cbf3585SJohn Baldwin dsdt_line(" {");
16933cbf3585SJohn Baldwin dsdt_line(" 0x%X,", slot << 16 | 0xffff);
16943cbf3585SJohn Baldwin dsdt_line(" 0x%02X,", pin - 1);
16953cbf3585SJohn Baldwin dsdt_line(" Zero,");
16960efad4acSJessica Clarke dsdt_line(" 0x%X", irq->ioapic_irq);
1697b3e9732aSJohn Baldwin dsdt_line(" },");
1698b3e9732aSJohn Baldwin }
1699b3e9732aSJohn Baldwin
1700b3e9732aSJohn Baldwin static void
pci_pirq_prt_entry(int bus __unused,int slot,int pin,struct pci_irq * irq,void * arg __unused)17010efad4acSJessica Clarke pci_pirq_prt_entry(int bus __unused, int slot, int pin, struct pci_irq *irq,
17020efad4acSJessica Clarke void *arg __unused)
1703b3e9732aSJohn Baldwin {
1704b3e9732aSJohn Baldwin char *name;
1705b3e9732aSJohn Baldwin
17060efad4acSJessica Clarke name = lpc_pirq_name(irq->pirq_pin);
1707b3e9732aSJohn Baldwin if (name == NULL)
1708b3e9732aSJohn Baldwin return;
1709b3e9732aSJohn Baldwin dsdt_line(" Package ()");
1710b3e9732aSJohn Baldwin dsdt_line(" {");
1711b3e9732aSJohn Baldwin dsdt_line(" 0x%X,", slot << 16 | 0xffff);
1712b3e9732aSJohn Baldwin dsdt_line(" 0x%02X,", pin - 1);
1713b3e9732aSJohn Baldwin dsdt_line(" %s,", name);
1714b3e9732aSJohn Baldwin dsdt_line(" 0x00");
1715b3e9732aSJohn Baldwin dsdt_line(" },");
1716b3e9732aSJohn Baldwin free(name);
17173cbf3585SJohn Baldwin }
171855c13f6eSMark Johnston #endif
17193cbf3585SJohn Baldwin
1720d84882caSNeel Natu /*
1721d84882caSNeel Natu * A bhyve virtual machine has a flat PCI hierarchy with a root port
1722d84882caSNeel Natu * corresponding to each PCI bus.
1723d84882caSNeel Natu */
1724d84882caSNeel Natu static void
pci_bus_write_dsdt(int bus)1725d84882caSNeel Natu pci_bus_write_dsdt(int bus)
1726e6c8bc29SJohn Baldwin {
1727d84882caSNeel Natu struct businfo *bi;
1728d84882caSNeel Natu struct slotinfo *si;
1729e6c8bc29SJohn Baldwin struct pci_devinst *pi;
173055c13f6eSMark Johnston int func, slot;
1731e6c8bc29SJohn Baldwin
1732d84882caSNeel Natu /*
1733d84882caSNeel Natu * If there are no devices on this 'bus' then just return.
1734d84882caSNeel Natu */
1735d84882caSNeel Natu if ((bi = pci_businfo[bus]) == NULL) {
1736d84882caSNeel Natu /*
1737d84882caSNeel Natu * Bus 0 is special because it decodes the I/O ports used
1738d84882caSNeel Natu * for PCI config space access even if there are no devices
1739d84882caSNeel Natu * on it.
1740d84882caSNeel Natu */
1741d84882caSNeel Natu if (bus != 0)
1742d84882caSNeel Natu return;
1743d84882caSNeel Natu }
1744d84882caSNeel Natu
1745d84882caSNeel Natu dsdt_line(" Device (PC%02X)", bus);
1746e6c8bc29SJohn Baldwin dsdt_line(" {");
1747e6c8bc29SJohn Baldwin dsdt_line(" Name (_HID, EisaId (\"PNP0A03\"))");
1748d84882caSNeel Natu
1749d84882caSNeel Natu dsdt_line(" Method (_BBN, 0, NotSerialized)");
1750d84882caSNeel Natu dsdt_line(" {");
1751d84882caSNeel Natu dsdt_line(" Return (0x%08X)", bus);
1752d84882caSNeel Natu dsdt_line(" }");
1753e6c8bc29SJohn Baldwin dsdt_line(" Name (_CRS, ResourceTemplate ()");
1754e6c8bc29SJohn Baldwin dsdt_line(" {");
1755e6c8bc29SJohn Baldwin dsdt_line(" WordBusNumber (ResourceProducer, MinFixed, "
1756e6c8bc29SJohn Baldwin "MaxFixed, PosDecode,");
1757e6c8bc29SJohn Baldwin dsdt_line(" 0x0000, // Granularity");
1758d84882caSNeel Natu dsdt_line(" 0x%04X, // Range Minimum", bus);
1759d84882caSNeel Natu dsdt_line(" 0x%04X, // Range Maximum", bus);
1760e6c8bc29SJohn Baldwin dsdt_line(" 0x0000, // Translation Offset");
1761d84882caSNeel Natu dsdt_line(" 0x0001, // Length");
1762e6c8bc29SJohn Baldwin dsdt_line(" ,, )");
1763d84882caSNeel Natu
1764f286f746SMark Johnston #ifdef __amd64__
1765d84882caSNeel Natu if (bus == 0) {
1766e6c8bc29SJohn Baldwin dsdt_indent(3);
1767e6c8bc29SJohn Baldwin dsdt_fixed_ioport(0xCF8, 8);
1768e6c8bc29SJohn Baldwin dsdt_unindent(3);
1769d84882caSNeel Natu
1770e6c8bc29SJohn Baldwin dsdt_line(" WordIO (ResourceProducer, MinFixed, MaxFixed, "
1771e6c8bc29SJohn Baldwin "PosDecode, EntireRange,");
1772e6c8bc29SJohn Baldwin dsdt_line(" 0x0000, // Granularity");
1773e6c8bc29SJohn Baldwin dsdt_line(" 0x0000, // Range Minimum");
1774e6c8bc29SJohn Baldwin dsdt_line(" 0x0CF7, // Range Maximum");
1775e6c8bc29SJohn Baldwin dsdt_line(" 0x0000, // Translation Offset");
1776e6c8bc29SJohn Baldwin dsdt_line(" 0x0CF8, // Length");
1777e6c8bc29SJohn Baldwin dsdt_line(" ,, , TypeStatic)");
1778d84882caSNeel Natu
1779e6c8bc29SJohn Baldwin dsdt_line(" WordIO (ResourceProducer, MinFixed, MaxFixed, "
1780e6c8bc29SJohn Baldwin "PosDecode, EntireRange,");
1781e6c8bc29SJohn Baldwin dsdt_line(" 0x0000, // Granularity");
1782e6c8bc29SJohn Baldwin dsdt_line(" 0x0D00, // Range Minimum");
1783d84882caSNeel Natu dsdt_line(" 0x%04X, // Range Maximum",
1784d84882caSNeel Natu PCI_EMUL_IOBASE - 1);
1785e6c8bc29SJohn Baldwin dsdt_line(" 0x0000, // Translation Offset");
1786d84882caSNeel Natu dsdt_line(" 0x%04X, // Length",
1787d84882caSNeel Natu PCI_EMUL_IOBASE - 0x0D00);
1788e6c8bc29SJohn Baldwin dsdt_line(" ,, , TypeStatic)");
1789d84882caSNeel Natu
1790d84882caSNeel Natu if (bi == NULL) {
1791d84882caSNeel Natu dsdt_line(" })");
1792d84882caSNeel Natu goto done;
1793d84882caSNeel Natu }
1794d84882caSNeel Natu }
1795f286f746SMark Johnston #endif
1796d84882caSNeel Natu assert(bi != NULL);
1797d84882caSNeel Natu
1798d84882caSNeel Natu /* i/o window */
1799d84882caSNeel Natu dsdt_line(" WordIO (ResourceProducer, MinFixed, MaxFixed, "
1800d84882caSNeel Natu "PosDecode, EntireRange,");
1801d84882caSNeel Natu dsdt_line(" 0x0000, // Granularity");
1802d84882caSNeel Natu dsdt_line(" 0x%04X, // Range Minimum", bi->iobase);
1803d84882caSNeel Natu dsdt_line(" 0x%04X, // Range Maximum",
1804d84882caSNeel Natu bi->iolimit - 1);
1805d84882caSNeel Natu dsdt_line(" 0x0000, // Translation Offset");
1806d84882caSNeel Natu dsdt_line(" 0x%04X, // Length",
1807d84882caSNeel Natu bi->iolimit - bi->iobase);
1808d84882caSNeel Natu dsdt_line(" ,, , TypeStatic)");
1809d84882caSNeel Natu
1810d84882caSNeel Natu /* mmio window (32-bit) */
1811e6c8bc29SJohn Baldwin dsdt_line(" DWordMemory (ResourceProducer, PosDecode, "
1812e6c8bc29SJohn Baldwin "MinFixed, MaxFixed, NonCacheable, ReadWrite,");
1813e6c8bc29SJohn Baldwin dsdt_line(" 0x00000000, // Granularity");
1814d84882caSNeel Natu dsdt_line(" 0x%08X, // Range Minimum\n", bi->membase32);
1815e6c8bc29SJohn Baldwin dsdt_line(" 0x%08X, // Range Maximum\n",
1816d84882caSNeel Natu bi->memlimit32 - 1);
1817e6c8bc29SJohn Baldwin dsdt_line(" 0x00000000, // Translation Offset");
1818d84882caSNeel Natu dsdt_line(" 0x%08X, // Length\n",
1819d84882caSNeel Natu bi->memlimit32 - bi->membase32);
1820e6c8bc29SJohn Baldwin dsdt_line(" ,, , AddressRangeMemory, TypeStatic)");
1821d84882caSNeel Natu
1822d84882caSNeel Natu /* mmio window (64-bit) */
1823e6c8bc29SJohn Baldwin dsdt_line(" QWordMemory (ResourceProducer, PosDecode, "
1824e6c8bc29SJohn Baldwin "MinFixed, MaxFixed, NonCacheable, ReadWrite,");
1825e6c8bc29SJohn Baldwin dsdt_line(" 0x0000000000000000, // Granularity");
1826d84882caSNeel Natu dsdt_line(" 0x%016lX, // Range Minimum\n", bi->membase64);
1827e6c8bc29SJohn Baldwin dsdt_line(" 0x%016lX, // Range Maximum\n",
1828d84882caSNeel Natu bi->memlimit64 - 1);
1829e6c8bc29SJohn Baldwin dsdt_line(" 0x0000000000000000, // Translation Offset");
1830e6c8bc29SJohn Baldwin dsdt_line(" 0x%016lX, // Length\n",
1831d84882caSNeel Natu bi->memlimit64 - bi->membase64);
1832e6c8bc29SJohn Baldwin dsdt_line(" ,, , AddressRangeMemory, TypeStatic)");
1833e6c8bc29SJohn Baldwin dsdt_line(" })");
1834d84882caSNeel Natu
183555c13f6eSMark Johnston #ifdef __amd64__
183655c13f6eSMark Johnston if (pci_count_lintr(bus) != 0) {
18373cbf3585SJohn Baldwin dsdt_indent(2);
1838b3e9732aSJohn Baldwin dsdt_line("Name (PPRT, Package ()");
18393cbf3585SJohn Baldwin dsdt_line("{");
1840b3e9732aSJohn Baldwin pci_walk_lintr(bus, pci_pirq_prt_entry, NULL);
18413cbf3585SJohn Baldwin dsdt_line("})");
1842b3e9732aSJohn Baldwin dsdt_line("Name (APRT, Package ()");
1843b3e9732aSJohn Baldwin dsdt_line("{");
1844b3e9732aSJohn Baldwin pci_walk_lintr(bus, pci_apic_prt_entry, NULL);
1845b3e9732aSJohn Baldwin dsdt_line("})");
1846b3e9732aSJohn Baldwin dsdt_line("Method (_PRT, 0, NotSerialized)");
1847b3e9732aSJohn Baldwin dsdt_line("{");
1848b3e9732aSJohn Baldwin dsdt_line(" If (PICM)");
1849b3e9732aSJohn Baldwin dsdt_line(" {");
1850b3e9732aSJohn Baldwin dsdt_line(" Return (APRT)");
1851b3e9732aSJohn Baldwin dsdt_line(" }");
1852b3e9732aSJohn Baldwin dsdt_line(" Else");
1853b3e9732aSJohn Baldwin dsdt_line(" {");
1854b3e9732aSJohn Baldwin dsdt_line(" Return (PPRT)");
1855b3e9732aSJohn Baldwin dsdt_line(" }");
1856b3e9732aSJohn Baldwin dsdt_line("}");
18573cbf3585SJohn Baldwin dsdt_unindent(2);
18583cbf3585SJohn Baldwin }
185955c13f6eSMark Johnston #endif
1860e6c8bc29SJohn Baldwin
1861e6c8bc29SJohn Baldwin dsdt_indent(2);
1862e6c8bc29SJohn Baldwin for (slot = 0; slot < MAXSLOTS; slot++) {
1863d84882caSNeel Natu si = &bi->slotinfo[slot];
1864e6c8bc29SJohn Baldwin for (func = 0; func < MAXFUNCS; func++) {
1865d84882caSNeel Natu pi = si->si_funcs[func].fi_devi;
1866e6c8bc29SJohn Baldwin if (pi != NULL && pi->pi_d->pe_write_dsdt != NULL)
1867e6c8bc29SJohn Baldwin pi->pi_d->pe_write_dsdt(pi);
1868e6c8bc29SJohn Baldwin }
1869e6c8bc29SJohn Baldwin }
1870e6c8bc29SJohn Baldwin dsdt_unindent(2);
1871f286f746SMark Johnston #ifdef __amd64__
1872d84882caSNeel Natu done:
1873f286f746SMark Johnston #endif
1874e6c8bc29SJohn Baldwin dsdt_line(" }");
1875e6c8bc29SJohn Baldwin }
1876e6c8bc29SJohn Baldwin
1877d84882caSNeel Natu void
pci_write_dsdt(void)1878d84882caSNeel Natu pci_write_dsdt(void)
1879d84882caSNeel Natu {
1880d84882caSNeel Natu int bus;
1881d84882caSNeel Natu
1882b3e9732aSJohn Baldwin dsdt_indent(1);
1883b3e9732aSJohn Baldwin dsdt_line("Name (PICM, 0x00)");
1884b3e9732aSJohn Baldwin dsdt_line("Method (_PIC, 1, NotSerialized)");
1885b3e9732aSJohn Baldwin dsdt_line("{");
1886b3e9732aSJohn Baldwin dsdt_line(" Store (Arg0, PICM)");
1887b3e9732aSJohn Baldwin dsdt_line("}");
1888b3e9732aSJohn Baldwin dsdt_line("");
1889b3e9732aSJohn Baldwin dsdt_line("Scope (_SB)");
1890b3e9732aSJohn Baldwin dsdt_line("{");
1891d84882caSNeel Natu for (bus = 0; bus < MAXBUSES; bus++)
1892d84882caSNeel Natu pci_bus_write_dsdt(bus);
1893b3e9732aSJohn Baldwin dsdt_line("}");
1894b3e9732aSJohn Baldwin dsdt_unindent(1);
1895d84882caSNeel Natu }
1896d84882caSNeel Natu
1897366f6083SPeter Grehan int
pci_bus_configured(int bus)1898b100acf2SNeel Natu pci_bus_configured(int bus)
1899b100acf2SNeel Natu {
1900b100acf2SNeel Natu assert(bus >= 0 && bus < MAXBUSES);
1901b100acf2SNeel Natu return (pci_businfo[bus] != NULL);
1902b100acf2SNeel Natu }
1903b100acf2SNeel Natu
1904b100acf2SNeel Natu int
pci_msi_enabled(struct pci_devinst * pi)1905366f6083SPeter Grehan pci_msi_enabled(struct pci_devinst *pi)
1906366f6083SPeter Grehan {
1907366f6083SPeter Grehan return (pi->pi_msi.enabled);
1908366f6083SPeter Grehan }
1909366f6083SPeter Grehan
1910366f6083SPeter Grehan int
pci_msi_maxmsgnum(struct pci_devinst * pi)19114f8be175SNeel Natu pci_msi_maxmsgnum(struct pci_devinst *pi)
1912366f6083SPeter Grehan {
1913366f6083SPeter Grehan if (pi->pi_msi.enabled)
19144f8be175SNeel Natu return (pi->pi_msi.maxmsgnum);
1915366f6083SPeter Grehan else
1916366f6083SPeter Grehan return (0);
1917366f6083SPeter Grehan }
1918366f6083SPeter Grehan
1919c9b4e987SNeel Natu int
pci_msix_enabled(struct pci_devinst * pi)1920c9b4e987SNeel Natu pci_msix_enabled(struct pci_devinst *pi)
1921c9b4e987SNeel Natu {
1922c9b4e987SNeel Natu
1923c9b4e987SNeel Natu return (pi->pi_msix.enabled && !pi->pi_msi.enabled);
1924c9b4e987SNeel Natu }
1925c9b4e987SNeel Natu
1926c9b4e987SNeel Natu void
pci_generate_msix(struct pci_devinst * pi,int index)1927c9b4e987SNeel Natu pci_generate_msix(struct pci_devinst *pi, int index)
1928c9b4e987SNeel Natu {
1929c9b4e987SNeel Natu struct msix_table_entry *mte;
1930c9b4e987SNeel Natu
1931c9b4e987SNeel Natu if (!pci_msix_enabled(pi))
1932c9b4e987SNeel Natu return;
1933c9b4e987SNeel Natu
1934c9b4e987SNeel Natu if (pi->pi_msix.function_mask)
1935c9b4e987SNeel Natu return;
1936c9b4e987SNeel Natu
1937c9b4e987SNeel Natu if (index >= pi->pi_msix.table_count)
1938c9b4e987SNeel Natu return;
1939c9b4e987SNeel Natu
1940c9b4e987SNeel Natu mte = &pi->pi_msix.table[index];
1941c9b4e987SNeel Natu if ((mte->vector_control & PCIM_MSIX_VCTRL_MASK) == 0) {
1942c9b4e987SNeel Natu /* XXX Set PBA bit if interrupt is disabled */
1943dc6a00f2SMark Johnston vm_raise_msi(pi->pi_vmctx, mte->addr, mte->msg_data,
1944dc6a00f2SMark Johnston pi->pi_bus, pi->pi_slot, pi->pi_func);
1945c9b4e987SNeel Natu }
1946c9b4e987SNeel Natu }
1947c9b4e987SNeel Natu
1948366f6083SPeter Grehan void
pci_generate_msi(struct pci_devinst * pi,int index)19494f8be175SNeel Natu pci_generate_msi(struct pci_devinst *pi, int index)
1950366f6083SPeter Grehan {
1951366f6083SPeter Grehan
19524f8be175SNeel Natu if (pci_msi_enabled(pi) && index < pci_msi_maxmsgnum(pi)) {
1953dc6a00f2SMark Johnston vm_raise_msi(pi->pi_vmctx, pi->pi_msi.addr,
1954dc6a00f2SMark Johnston pi->pi_msi.msg_data + index,
1955dc6a00f2SMark Johnston pi->pi_bus, pi->pi_slot, pi->pi_func);
1956366f6083SPeter Grehan }
1957366f6083SPeter Grehan }
1958366f6083SPeter Grehan
19593cbf3585SJohn Baldwin static bool
pci_lintr_permitted(struct pci_devinst * pi)19603cbf3585SJohn Baldwin pci_lintr_permitted(struct pci_devinst *pi)
19610038ee98SPeter Grehan {
19623cbf3585SJohn Baldwin uint16_t cmd;
19630038ee98SPeter Grehan
19643cbf3585SJohn Baldwin cmd = pci_get_cfgdata16(pi, PCIR_COMMAND);
19653cbf3585SJohn Baldwin return (!(pi->pi_msi.enabled || pi->pi_msix.enabled ||
19663cbf3585SJohn Baldwin (cmd & PCIM_CMD_INTxDIS)));
19673cbf3585SJohn Baldwin }
19683cbf3585SJohn Baldwin
1969b3e9732aSJohn Baldwin void
pci_lintr_request(struct pci_devinst * pi)19703cbf3585SJohn Baldwin pci_lintr_request(struct pci_devinst *pi)
19713cbf3585SJohn Baldwin {
1972d84882caSNeel Natu struct businfo *bi;
19733cbf3585SJohn Baldwin struct slotinfo *si;
1974b3e9732aSJohn Baldwin int bestpin, bestcount, pin;
19753cbf3585SJohn Baldwin
1976d84882caSNeel Natu bi = pci_businfo[pi->pi_bus];
1977d84882caSNeel Natu assert(bi != NULL);
1978d84882caSNeel Natu
19793cbf3585SJohn Baldwin /*
1980b3e9732aSJohn Baldwin * Just allocate a pin from our slot. The pin will be
1981b3e9732aSJohn Baldwin * assigned IRQs later when interrupts are routed.
19823cbf3585SJohn Baldwin */
1983d84882caSNeel Natu si = &bi->slotinfo[pi->pi_slot];
19843cbf3585SJohn Baldwin bestpin = 0;
19853cbf3585SJohn Baldwin bestcount = si->si_intpins[0].ii_count;
19863cbf3585SJohn Baldwin for (pin = 1; pin < 4; pin++) {
19873cbf3585SJohn Baldwin if (si->si_intpins[pin].ii_count < bestcount) {
19883cbf3585SJohn Baldwin bestpin = pin;
19893cbf3585SJohn Baldwin bestcount = si->si_intpins[pin].ii_count;
19903cbf3585SJohn Baldwin }
19913cbf3585SJohn Baldwin }
19923cbf3585SJohn Baldwin
19933cbf3585SJohn Baldwin si->si_intpins[bestpin].ii_count++;
19943cbf3585SJohn Baldwin pi->pi_lintr.pin = bestpin + 1;
19953cbf3585SJohn Baldwin pci_set_cfgdata8(pi, PCIR_INTPIN, bestpin + 1);
1996b3e9732aSJohn Baldwin }
1997b3e9732aSJohn Baldwin
1998b3e9732aSJohn Baldwin static void
pci_lintr_route(struct pci_devinst * pi)1999b3e9732aSJohn Baldwin pci_lintr_route(struct pci_devinst *pi)
2000b3e9732aSJohn Baldwin {
2001b3e9732aSJohn Baldwin struct businfo *bi;
2002b3e9732aSJohn Baldwin struct intxinfo *ii;
20030efad4acSJessica Clarke struct pci_irq *irq;
2004b3e9732aSJohn Baldwin
2005b3e9732aSJohn Baldwin if (pi->pi_lintr.pin == 0)
2006b3e9732aSJohn Baldwin return;
2007b3e9732aSJohn Baldwin
2008b3e9732aSJohn Baldwin bi = pci_businfo[pi->pi_bus];
2009b3e9732aSJohn Baldwin assert(bi != NULL);
2010b3e9732aSJohn Baldwin ii = &bi->slotinfo[pi->pi_slot].si_intpins[pi->pi_lintr.pin - 1];
20110efad4acSJessica Clarke irq = &ii->ii_irq;
20120efad4acSJessica Clarke pci_irq_route(pi, irq);
20130efad4acSJessica Clarke pi->pi_lintr.irq = *irq;
20140efad4acSJessica Clarke pci_set_cfgdata8(pi, PCIR_INTLINE, pci_irq_intline(irq));
20150038ee98SPeter Grehan }
20160038ee98SPeter Grehan
20170038ee98SPeter Grehan void
pci_lintr_assert(struct pci_devinst * pi)20180038ee98SPeter Grehan pci_lintr_assert(struct pci_devinst *pi)
20190038ee98SPeter Grehan {
20200038ee98SPeter Grehan
20213cbf3585SJohn Baldwin assert(pi->pi_lintr.pin > 0);
2022ac7304a7SNeel Natu
20233cbf3585SJohn Baldwin pthread_mutex_lock(&pi->pi_lintr.lock);
20243cbf3585SJohn Baldwin if (pi->pi_lintr.state == IDLE) {
20253cbf3585SJohn Baldwin if (pci_lintr_permitted(pi)) {
20263cbf3585SJohn Baldwin pi->pi_lintr.state = ASSERTED;
2027b3e9732aSJohn Baldwin pci_irq_assert(pi);
20283cbf3585SJohn Baldwin } else
20293cbf3585SJohn Baldwin pi->pi_lintr.state = PENDING;
20300038ee98SPeter Grehan }
20313cbf3585SJohn Baldwin pthread_mutex_unlock(&pi->pi_lintr.lock);
2032ac7304a7SNeel Natu }
20330038ee98SPeter Grehan
20340038ee98SPeter Grehan void
pci_lintr_deassert(struct pci_devinst * pi)20350038ee98SPeter Grehan pci_lintr_deassert(struct pci_devinst *pi)
20360038ee98SPeter Grehan {
20370038ee98SPeter Grehan
20383cbf3585SJohn Baldwin assert(pi->pi_lintr.pin > 0);
2039ac7304a7SNeel Natu
20403cbf3585SJohn Baldwin pthread_mutex_lock(&pi->pi_lintr.lock);
20413cbf3585SJohn Baldwin if (pi->pi_lintr.state == ASSERTED) {
20423cbf3585SJohn Baldwin pi->pi_lintr.state = IDLE;
2043b3e9732aSJohn Baldwin pci_irq_deassert(pi);
20443cbf3585SJohn Baldwin } else if (pi->pi_lintr.state == PENDING)
20453cbf3585SJohn Baldwin pi->pi_lintr.state = IDLE;
20463cbf3585SJohn Baldwin pthread_mutex_unlock(&pi->pi_lintr.lock);
20473cbf3585SJohn Baldwin }
20483cbf3585SJohn Baldwin
20493cbf3585SJohn Baldwin static void
pci_lintr_update(struct pci_devinst * pi)20503cbf3585SJohn Baldwin pci_lintr_update(struct pci_devinst *pi)
20513cbf3585SJohn Baldwin {
20523cbf3585SJohn Baldwin
20533cbf3585SJohn Baldwin pthread_mutex_lock(&pi->pi_lintr.lock);
20543cbf3585SJohn Baldwin if (pi->pi_lintr.state == ASSERTED && !pci_lintr_permitted(pi)) {
2055b3e9732aSJohn Baldwin pci_irq_deassert(pi);
20563cbf3585SJohn Baldwin pi->pi_lintr.state = PENDING;
20573cbf3585SJohn Baldwin } else if (pi->pi_lintr.state == PENDING && pci_lintr_permitted(pi)) {
20583cbf3585SJohn Baldwin pi->pi_lintr.state = ASSERTED;
2059b3e9732aSJohn Baldwin pci_irq_assert(pi);
20603cbf3585SJohn Baldwin }
20613cbf3585SJohn Baldwin pthread_mutex_unlock(&pi->pi_lintr.lock);
20623cbf3585SJohn Baldwin }
20633cbf3585SJohn Baldwin
20643cbf3585SJohn Baldwin int
pci_count_lintr(int bus)2065d84882caSNeel Natu pci_count_lintr(int bus)
20663cbf3585SJohn Baldwin {
20673cbf3585SJohn Baldwin int count, slot, pin;
2068d84882caSNeel Natu struct slotinfo *slotinfo;
20693cbf3585SJohn Baldwin
20703cbf3585SJohn Baldwin count = 0;
2071d84882caSNeel Natu if (pci_businfo[bus] != NULL) {
20723cbf3585SJohn Baldwin for (slot = 0; slot < MAXSLOTS; slot++) {
2073d84882caSNeel Natu slotinfo = &pci_businfo[bus]->slotinfo[slot];
20743cbf3585SJohn Baldwin for (pin = 0; pin < 4; pin++) {
2075d84882caSNeel Natu if (slotinfo->si_intpins[pin].ii_count != 0)
20763cbf3585SJohn Baldwin count++;
20773cbf3585SJohn Baldwin }
20783cbf3585SJohn Baldwin }
2079d84882caSNeel Natu }
20803cbf3585SJohn Baldwin return (count);
20813cbf3585SJohn Baldwin }
20823cbf3585SJohn Baldwin
20833cbf3585SJohn Baldwin void
pci_walk_lintr(int bus,pci_lintr_cb cb,void * arg)2084d84882caSNeel Natu pci_walk_lintr(int bus, pci_lintr_cb cb, void *arg)
20853cbf3585SJohn Baldwin {
2086d84882caSNeel Natu struct businfo *bi;
2087d84882caSNeel Natu struct slotinfo *si;
20883cbf3585SJohn Baldwin struct intxinfo *ii;
20893cbf3585SJohn Baldwin int slot, pin;
20903cbf3585SJohn Baldwin
2091d84882caSNeel Natu if ((bi = pci_businfo[bus]) == NULL)
2092d84882caSNeel Natu return;
2093d84882caSNeel Natu
20943cbf3585SJohn Baldwin for (slot = 0; slot < MAXSLOTS; slot++) {
2095d84882caSNeel Natu si = &bi->slotinfo[slot];
20963cbf3585SJohn Baldwin for (pin = 0; pin < 4; pin++) {
2097d84882caSNeel Natu ii = &si->si_intpins[pin];
20983cbf3585SJohn Baldwin if (ii->ii_count != 0)
20990efad4acSJessica Clarke cb(bus, slot, pin + 1, &ii->ii_irq, arg);
21003cbf3585SJohn Baldwin }
21010038ee98SPeter Grehan }
2102ac7304a7SNeel Natu }
21030038ee98SPeter Grehan
210499d65389SNeel Natu /*
210599d65389SNeel Natu * Return 1 if the emulated device in 'slot' is a multi-function device.
210699d65389SNeel Natu * Return 0 otherwise.
210799d65389SNeel Natu */
210899d65389SNeel Natu static int
pci_emul_is_mfdev(int bus,int slot)2109d84882caSNeel Natu pci_emul_is_mfdev(int bus, int slot)
211099d65389SNeel Natu {
2111d84882caSNeel Natu struct businfo *bi;
2112d84882caSNeel Natu struct slotinfo *si;
211399d65389SNeel Natu int f, numfuncs;
21140038ee98SPeter Grehan
211599d65389SNeel Natu numfuncs = 0;
2116d84882caSNeel Natu if ((bi = pci_businfo[bus]) != NULL) {
2117d84882caSNeel Natu si = &bi->slotinfo[slot];
211899d65389SNeel Natu for (f = 0; f < MAXFUNCS; f++) {
2119d84882caSNeel Natu if (si->si_funcs[f].fi_devi != NULL) {
212099d65389SNeel Natu numfuncs++;
212199d65389SNeel Natu }
212299d65389SNeel Natu }
2123d84882caSNeel Natu }
212499d65389SNeel Natu return (numfuncs > 1);
212599d65389SNeel Natu }
212699d65389SNeel Natu
212799d65389SNeel Natu /*
212899d65389SNeel Natu * Ensure that the PCIM_MFDEV bit is properly set (or unset) depending on
212999d65389SNeel Natu * whether or not is a multi-function being emulated in the pci 'slot'.
213099d65389SNeel Natu */
213199d65389SNeel Natu static void
pci_emul_hdrtype_fixup(int bus,int slot,int off,int bytes,uint32_t * rv)2132d84882caSNeel Natu pci_emul_hdrtype_fixup(int bus, int slot, int off, int bytes, uint32_t *rv)
213399d65389SNeel Natu {
213499d65389SNeel Natu int mfdev;
213599d65389SNeel Natu
213699d65389SNeel Natu if (off <= PCIR_HDRTYPE && off + bytes > PCIR_HDRTYPE) {
2137d84882caSNeel Natu mfdev = pci_emul_is_mfdev(bus, slot);
213899d65389SNeel Natu switch (bytes) {
213999d65389SNeel Natu case 1:
214099d65389SNeel Natu case 2:
214199d65389SNeel Natu *rv &= ~PCIM_MFDEV;
214299d65389SNeel Natu if (mfdev) {
214399d65389SNeel Natu *rv |= PCIM_MFDEV;
214499d65389SNeel Natu }
214599d65389SNeel Natu break;
214699d65389SNeel Natu case 4:
214799d65389SNeel Natu *rv &= ~(PCIM_MFDEV << 16);
214899d65389SNeel Natu if (mfdev) {
214999d65389SNeel Natu *rv |= (PCIM_MFDEV << 16);
215099d65389SNeel Natu }
215199d65389SNeel Natu break;
215299d65389SNeel Natu }
215399d65389SNeel Natu }
215499d65389SNeel Natu }
21550038ee98SPeter Grehan
215656282675SJohn Baldwin /*
215756282675SJohn Baldwin * Update device state in response to changes to the PCI command
215856282675SJohn Baldwin * register.
215956282675SJohn Baldwin */
216056282675SJohn Baldwin void
pci_emul_cmd_changed(struct pci_devinst * pi,uint16_t old)216156282675SJohn Baldwin pci_emul_cmd_changed(struct pci_devinst *pi, uint16_t old)
216256282675SJohn Baldwin {
216356282675SJohn Baldwin int i;
216456282675SJohn Baldwin uint16_t changed, new;
216556282675SJohn Baldwin
216656282675SJohn Baldwin new = pci_get_cfgdata16(pi, PCIR_COMMAND);
216756282675SJohn Baldwin changed = old ^ new;
216856282675SJohn Baldwin
216956282675SJohn Baldwin /*
217056282675SJohn Baldwin * If the MMIO or I/O address space decoding has changed then
217156282675SJohn Baldwin * register/unregister all BARs that decode that address space.
217256282675SJohn Baldwin */
2173e47fe318SCorvin Köhne for (i = 0; i <= PCI_BARMAX_WITH_ROM; i++) {
217456282675SJohn Baldwin switch (pi->pi_bar[i].type) {
217556282675SJohn Baldwin case PCIBAR_NONE:
217656282675SJohn Baldwin case PCIBAR_MEMHI64:
217756282675SJohn Baldwin break;
217856282675SJohn Baldwin case PCIBAR_IO:
217956282675SJohn Baldwin /* I/O address space decoding changed? */
218056282675SJohn Baldwin if (changed & PCIM_CMD_PORTEN) {
218156282675SJohn Baldwin if (new & PCIM_CMD_PORTEN)
218256282675SJohn Baldwin register_bar(pi, i);
218356282675SJohn Baldwin else
218456282675SJohn Baldwin unregister_bar(pi, i);
218556282675SJohn Baldwin }
218656282675SJohn Baldwin break;
2187e47fe318SCorvin Köhne case PCIBAR_ROM:
2188e47fe318SCorvin Köhne /* skip (un-)register of ROM if it disabled */
2189e47fe318SCorvin Köhne if (!romen(pi))
2190e47fe318SCorvin Köhne break;
2191e47fe318SCorvin Köhne /* fallthrough */
219256282675SJohn Baldwin case PCIBAR_MEM32:
219356282675SJohn Baldwin case PCIBAR_MEM64:
219456282675SJohn Baldwin /* MMIO address space decoding changed? */
219556282675SJohn Baldwin if (changed & PCIM_CMD_MEMEN) {
219656282675SJohn Baldwin if (new & PCIM_CMD_MEMEN)
219756282675SJohn Baldwin register_bar(pi, i);
219856282675SJohn Baldwin else
219956282675SJohn Baldwin unregister_bar(pi, i);
220056282675SJohn Baldwin }
220156282675SJohn Baldwin break;
220256282675SJohn Baldwin default:
220356282675SJohn Baldwin assert(0);
220456282675SJohn Baldwin }
220556282675SJohn Baldwin }
220656282675SJohn Baldwin
220756282675SJohn Baldwin /*
220856282675SJohn Baldwin * If INTx has been unmasked and is pending, assert the
220956282675SJohn Baldwin * interrupt.
221056282675SJohn Baldwin */
221156282675SJohn Baldwin pci_lintr_update(pi);
221256282675SJohn Baldwin }
221356282675SJohn Baldwin
2214028d9311SNeel Natu static void
pci_emul_cmdsts_write(struct pci_devinst * pi,int coff,uint32_t new,int bytes)221554335630SNeel Natu pci_emul_cmdsts_write(struct pci_devinst *pi, int coff, uint32_t new, int bytes)
2216028d9311SNeel Natu {
221756282675SJohn Baldwin int rshift;
221856282675SJohn Baldwin uint32_t cmd, old, readonly;
221954335630SNeel Natu
222054335630SNeel Natu cmd = pci_get_cfgdata16(pi, PCIR_COMMAND); /* stash old value */
2221028d9311SNeel Natu
2222028d9311SNeel Natu /*
222354335630SNeel Natu * From PCI Local Bus Specification 3.0 sections 6.2.2 and 6.2.3.
222454335630SNeel Natu *
222554335630SNeel Natu * XXX Bits 8, 11, 12, 13, 14 and 15 in the status register are
222654335630SNeel Natu * 'write 1 to clear'. However these bits are not set to '1' by
222754335630SNeel Natu * any device emulation so it is simpler to treat them as readonly.
2228028d9311SNeel Natu */
222954335630SNeel Natu rshift = (coff & 0x3) * 8;
223054335630SNeel Natu readonly = 0xFFFFF880 >> rshift;
2231028d9311SNeel Natu
223254335630SNeel Natu old = CFGREAD(pi, coff, bytes);
223354335630SNeel Natu new &= ~readonly;
223454335630SNeel Natu new |= (old & readonly);
223554335630SNeel Natu CFGWRITE(pi, coff, new, bytes); /* update config */
223654335630SNeel Natu
223756282675SJohn Baldwin pci_emul_cmd_changed(pi, cmd);
2238028d9311SNeel Natu }
2239028d9311SNeel Natu
224012a6eb99SNeel Natu static void
pci_cfgrw(int in,int bus,int slot,int func,int coff,int bytes,uint32_t * valp)22416a284cacSJohn Baldwin pci_cfgrw(int in, int bus, int slot, int func, int coff, int bytes,
2242f4841d8aSMark Johnston uint32_t *valp)
2243366f6083SPeter Grehan {
2244d84882caSNeel Natu struct businfo *bi;
2245d84882caSNeel Natu struct slotinfo *si;
2246366f6083SPeter Grehan struct pci_devinst *pi;
2247366f6083SPeter Grehan struct pci_devemu *pe;
224812a6eb99SNeel Natu int idx, needcfg;
2249028d9311SNeel Natu uint64_t addr, bar, mask;
2250366f6083SPeter Grehan
225112a6eb99SNeel Natu if ((bi = pci_businfo[bus]) != NULL) {
225212a6eb99SNeel Natu si = &bi->slotinfo[slot];
225312a6eb99SNeel Natu pi = si->si_funcs[func].fi_devi;
2254d84882caSNeel Natu } else
225590415e0bSNeel Natu pi = NULL;
225690415e0bSNeel Natu
225799d65389SNeel Natu /*
225812a6eb99SNeel Natu * Just return if there is no device at this slot:func or if the
2259ec8a394dSElyes Haouas * guest is doing an un-aligned access.
226099d65389SNeel Natu */
226112a6eb99SNeel Natu if (pi == NULL || (bytes != 1 && bytes != 2 && bytes != 4) ||
226212a6eb99SNeel Natu (coff & (bytes - 1)) != 0) {
2263366f6083SPeter Grehan if (in)
2264f4841d8aSMark Johnston *valp = 0xffffffff;
226512a6eb99SNeel Natu return;
226612a6eb99SNeel Natu }
226712a6eb99SNeel Natu
226812a6eb99SNeel Natu /*
226912a6eb99SNeel Natu * Ignore all writes beyond the standard config space and return all
227012a6eb99SNeel Natu * ones on reads.
227112a6eb99SNeel Natu */
227212a6eb99SNeel Natu if (coff >= PCI_REGMAX + 1) {
227312a6eb99SNeel Natu if (in) {
2274f4841d8aSMark Johnston *valp = 0xffffffff;
227512a6eb99SNeel Natu /*
227612a6eb99SNeel Natu * Extended capabilities begin at offset 256 in config
227712a6eb99SNeel Natu * space. Absence of extended capabilities is signaled
227812a6eb99SNeel Natu * with all 0s in the extended capability header at
227912a6eb99SNeel Natu * offset 256.
228012a6eb99SNeel Natu */
228112a6eb99SNeel Natu if (coff <= PCI_REGMAX + 4)
2282f4841d8aSMark Johnston *valp = 0x00000000;
228312a6eb99SNeel Natu }
228412a6eb99SNeel Natu return;
2285366f6083SPeter Grehan }
2286366f6083SPeter Grehan
2287366f6083SPeter Grehan pe = pi->pi_d;
2288366f6083SPeter Grehan
2289366f6083SPeter Grehan /*
2290366f6083SPeter Grehan * Config read
2291366f6083SPeter Grehan */
2292366f6083SPeter Grehan if (in) {
2293366f6083SPeter Grehan /* Let the device emulation override the default handler */
229499d65389SNeel Natu if (pe->pe_cfgread != NULL) {
2295f4841d8aSMark Johnston needcfg = pe->pe_cfgread(pi, coff, bytes, valp);
229699d65389SNeel Natu } else {
229799d65389SNeel Natu needcfg = 1;
229899d65389SNeel Natu }
2299366f6083SPeter Grehan
230054335630SNeel Natu if (needcfg)
2301f4841d8aSMark Johnston *valp = CFGREAD(pi, coff, bytes);
230299d65389SNeel Natu
2303f4841d8aSMark Johnston pci_emul_hdrtype_fixup(bus, slot, coff, bytes, valp);
2304366f6083SPeter Grehan } else {
2305366f6083SPeter Grehan /* Let the device emulation override the default handler */
2306366f6083SPeter Grehan if (pe->pe_cfgwrite != NULL &&
2307f4841d8aSMark Johnston (*pe->pe_cfgwrite)(pi, coff, bytes, *valp) == 0)
230812a6eb99SNeel Natu return;
2309366f6083SPeter Grehan
2310366f6083SPeter Grehan /*
2311e47fe318SCorvin Köhne * Special handling for write to BAR and ROM registers
2312366f6083SPeter Grehan */
231345ddbf21SCorvin Köhne if (is_pcir_bar(coff) || is_pcir_bios(coff)) {
2314366f6083SPeter Grehan /*
2315366f6083SPeter Grehan * Ignore writes to BAR registers that are not
2316366f6083SPeter Grehan * 4-byte aligned.
2317366f6083SPeter Grehan */
2318366f6083SPeter Grehan if (bytes != 4 || (coff & 0x3) != 0)
231912a6eb99SNeel Natu return;
232045ddbf21SCorvin Köhne
232145ddbf21SCorvin Köhne if (is_pcir_bar(coff)) {
2322366f6083SPeter Grehan idx = (coff - PCIR_BAR(0)) / 4;
232345ddbf21SCorvin Köhne } else if (is_pcir_bios(coff)) {
2324e47fe318SCorvin Köhne idx = PCI_ROM_IDX;
232545ddbf21SCorvin Köhne } else {
232645ddbf21SCorvin Köhne errx(4, "%s: invalid BAR offset %d", __func__,
232745ddbf21SCorvin Köhne coff);
2328e47fe318SCorvin Köhne }
232945ddbf21SCorvin Köhne
2330028d9311SNeel Natu mask = ~(pi->pi_bar[idx].size - 1);
2331366f6083SPeter Grehan switch (pi->pi_bar[idx].type) {
2332366f6083SPeter Grehan case PCIBAR_NONE:
2333028d9311SNeel Natu pi->pi_bar[idx].addr = bar = 0;
2334366f6083SPeter Grehan break;
2335366f6083SPeter Grehan case PCIBAR_IO:
2336f4841d8aSMark Johnston addr = *valp & mask;
2337f286f746SMark Johnston #if defined(PCI_EMUL_IOMASK)
2338f286f746SMark Johnston addr &= PCI_EMUL_IOMASK;
2339f286f746SMark Johnston #endif
2340e87a6f3eSCorvin Köhne bar = addr | pi->pi_bar[idx].lobits;
2341028d9311SNeel Natu /*
2342028d9311SNeel Natu * Register the new BAR value for interception
2343028d9311SNeel Natu */
2344028d9311SNeel Natu if (addr != pi->pi_bar[idx].addr) {
2345028d9311SNeel Natu update_bar_address(pi, addr, idx,
2346028d9311SNeel Natu PCIBAR_IO);
2347028d9311SNeel Natu }
2348366f6083SPeter Grehan break;
2349366f6083SPeter Grehan case PCIBAR_MEM32:
2350f4841d8aSMark Johnston addr = bar = *valp & mask;
2351e87a6f3eSCorvin Köhne bar |= pi->pi_bar[idx].lobits;
2352028d9311SNeel Natu if (addr != pi->pi_bar[idx].addr) {
2353028d9311SNeel Natu update_bar_address(pi, addr, idx,
2354028d9311SNeel Natu PCIBAR_MEM32);
2355028d9311SNeel Natu }
2356366f6083SPeter Grehan break;
2357366f6083SPeter Grehan case PCIBAR_MEM64:
2358f4841d8aSMark Johnston addr = bar = *valp & mask;
2359e87a6f3eSCorvin Köhne bar |= pi->pi_bar[idx].lobits;
2360028d9311SNeel Natu if (addr != (uint32_t)pi->pi_bar[idx].addr) {
2361028d9311SNeel Natu update_bar_address(pi, addr, idx,
2362028d9311SNeel Natu PCIBAR_MEM64);
2363028d9311SNeel Natu }
2364366f6083SPeter Grehan break;
2365366f6083SPeter Grehan case PCIBAR_MEMHI64:
2366366f6083SPeter Grehan mask = ~(pi->pi_bar[idx - 1].size - 1);
2367f4841d8aSMark Johnston addr = ((uint64_t)*valp << 32) & mask;
2368028d9311SNeel Natu bar = addr >> 32;
2369028d9311SNeel Natu if (bar != pi->pi_bar[idx - 1].addr >> 32) {
2370028d9311SNeel Natu update_bar_address(pi, addr, idx - 1,
2371028d9311SNeel Natu PCIBAR_MEMHI64);
2372028d9311SNeel Natu }
2373366f6083SPeter Grehan break;
2374e47fe318SCorvin Köhne case PCIBAR_ROM:
2375f4841d8aSMark Johnston addr = bar = *valp & mask;
2376e47fe318SCorvin Köhne if (memen(pi) && romen(pi)) {
2377e47fe318SCorvin Köhne unregister_bar(pi, idx);
2378e47fe318SCorvin Köhne }
2379e47fe318SCorvin Köhne pi->pi_bar[idx].addr = addr;
2380f4841d8aSMark Johnston pi->pi_bar[idx].lobits = *valp &
2381e47fe318SCorvin Köhne PCIM_BIOS_ENABLE;
2382e47fe318SCorvin Köhne /* romen could have changed it value */
2383e47fe318SCorvin Köhne if (memen(pi) && romen(pi)) {
2384e47fe318SCorvin Köhne register_bar(pi, idx);
2385e47fe318SCorvin Köhne }
2386e47fe318SCorvin Köhne bar |= pi->pi_bar[idx].lobits;
2387e47fe318SCorvin Köhne break;
2388366f6083SPeter Grehan default:
2389366f6083SPeter Grehan assert(0);
2390366f6083SPeter Grehan }
2391366f6083SPeter Grehan pci_set_cfgdata32(pi, coff, bar);
2392cd942e0fSPeter Grehan
2393366f6083SPeter Grehan } else if (pci_emul_iscap(pi, coff)) {
2394f4841d8aSMark Johnston pci_emul_capwrite(pi, coff, bytes, *valp, 0, 0);
239554335630SNeel Natu } else if (coff >= PCIR_COMMAND && coff < PCIR_REVID) {
2396f4841d8aSMark Johnston pci_emul_cmdsts_write(pi, coff, *valp, bytes);
2397366f6083SPeter Grehan } else {
2398f4841d8aSMark Johnston CFGWRITE(pi, coff, *valp, bytes);
2399366f6083SPeter Grehan }
2400366f6083SPeter Grehan }
240112a6eb99SNeel Natu }
2402366f6083SPeter Grehan
240331cf78c9SMark Johnston #ifdef __amd64__
240412a6eb99SNeel Natu static int cfgenable, cfgbus, cfgslot, cfgfunc, cfgoff;
240512a6eb99SNeel Natu
240612a6eb99SNeel Natu static int
pci_emul_cfgaddr(struct vmctx * ctx __unused,int in,int port __unused,int bytes,uint32_t * eax,void * arg __unused)240708b05de1SJohn Baldwin pci_emul_cfgaddr(struct vmctx *ctx __unused, int in,
240898d920d9SMark Johnston int port __unused, int bytes, uint32_t *eax, void *arg __unused)
240912a6eb99SNeel Natu {
241012a6eb99SNeel Natu uint32_t x;
241112a6eb99SNeel Natu
241212a6eb99SNeel Natu if (bytes != 4) {
241312a6eb99SNeel Natu if (in)
241412a6eb99SNeel Natu *eax = (bytes == 2) ? 0xffff : 0xff;
241512a6eb99SNeel Natu return (0);
241612a6eb99SNeel Natu }
241712a6eb99SNeel Natu
241812a6eb99SNeel Natu if (in) {
241912a6eb99SNeel Natu x = (cfgbus << 16) | (cfgslot << 11) | (cfgfunc << 8) | cfgoff;
242012a6eb99SNeel Natu if (cfgenable)
242112a6eb99SNeel Natu x |= CONF1_ENABLE;
242212a6eb99SNeel Natu *eax = x;
242312a6eb99SNeel Natu } else {
242412a6eb99SNeel Natu x = *eax;
242512a6eb99SNeel Natu cfgenable = (x & CONF1_ENABLE) == CONF1_ENABLE;
24261b0e2f0bSCorvin Köhne cfgoff = (x & PCI_REGMAX) & ~0x03;
242712a6eb99SNeel Natu cfgfunc = (x >> 8) & PCI_FUNCMAX;
242812a6eb99SNeel Natu cfgslot = (x >> 11) & PCI_SLOTMAX;
242912a6eb99SNeel Natu cfgbus = (x >> 16) & PCI_BUSMAX;
243012a6eb99SNeel Natu }
243112a6eb99SNeel Natu
243212a6eb99SNeel Natu return (0);
243312a6eb99SNeel Natu }
243412a6eb99SNeel Natu INOUT_PORT(pci_cfgaddr, CONF1_ADDR_PORT, IOPORT_F_INOUT, pci_emul_cfgaddr);
243512a6eb99SNeel Natu
243612a6eb99SNeel Natu static int
pci_emul_cfgdata(struct vmctx * ctx __unused,int in,int port,int bytes,uint32_t * eax,void * arg __unused)24376a284cacSJohn Baldwin pci_emul_cfgdata(struct vmctx *ctx __unused, int in, int port,
243878c2cd83SJohn Baldwin int bytes, uint32_t *eax, void *arg __unused)
243912a6eb99SNeel Natu {
244012a6eb99SNeel Natu int coff;
244112a6eb99SNeel Natu
244212a6eb99SNeel Natu assert(bytes == 1 || bytes == 2 || bytes == 4);
244312a6eb99SNeel Natu
244412a6eb99SNeel Natu coff = cfgoff + (port - CONF1_DATA_PORT);
244512a6eb99SNeel Natu if (cfgenable) {
24466a284cacSJohn Baldwin pci_cfgrw(in, cfgbus, cfgslot, cfgfunc, coff, bytes, eax);
244712a6eb99SNeel Natu } else {
244812a6eb99SNeel Natu /* Ignore accesses to cfgdata if not enabled by cfgaddr */
244912a6eb99SNeel Natu if (in)
245012a6eb99SNeel Natu *eax = 0xffffffff;
245112a6eb99SNeel Natu }
2452366f6083SPeter Grehan return (0);
2453366f6083SPeter Grehan }
2454366f6083SPeter Grehan
2455366f6083SPeter Grehan INOUT_PORT(pci_cfgdata, CONF1_DATA_PORT+0, IOPORT_F_INOUT, pci_emul_cfgdata);
2456366f6083SPeter Grehan INOUT_PORT(pci_cfgdata, CONF1_DATA_PORT+1, IOPORT_F_INOUT, pci_emul_cfgdata);
2457366f6083SPeter Grehan INOUT_PORT(pci_cfgdata, CONF1_DATA_PORT+2, IOPORT_F_INOUT, pci_emul_cfgdata);
2458366f6083SPeter Grehan INOUT_PORT(pci_cfgdata, CONF1_DATA_PORT+3, IOPORT_F_INOUT, pci_emul_cfgdata);
245931cf78c9SMark Johnston #endif
2460366f6083SPeter Grehan
2461483d953aSJohn Baldwin #ifdef BHYVE_SNAPSHOT
2462483d953aSJohn Baldwin /*
2463483d953aSJohn Baldwin * Saves/restores PCI device emulated state. Returns 0 on success.
2464483d953aSJohn Baldwin */
2465483d953aSJohn Baldwin static int
pci_snapshot_pci_dev(struct vm_snapshot_meta * meta)2466483d953aSJohn Baldwin pci_snapshot_pci_dev(struct vm_snapshot_meta *meta)
2467483d953aSJohn Baldwin {
2468483d953aSJohn Baldwin struct pci_devinst *pi;
2469483d953aSJohn Baldwin int i;
2470483d953aSJohn Baldwin int ret;
2471483d953aSJohn Baldwin
2472483d953aSJohn Baldwin pi = meta->dev_data;
2473483d953aSJohn Baldwin
2474483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(pi->pi_msi.enabled, meta, ret, done);
2475483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(pi->pi_msi.addr, meta, ret, done);
2476483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(pi->pi_msi.msg_data, meta, ret, done);
2477483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(pi->pi_msi.maxmsgnum, meta, ret, done);
2478483d953aSJohn Baldwin
2479483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(pi->pi_msix.enabled, meta, ret, done);
2480483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(pi->pi_msix.table_bar, meta, ret, done);
2481483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(pi->pi_msix.pba_bar, meta, ret, done);
2482483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(pi->pi_msix.table_offset, meta, ret, done);
2483483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(pi->pi_msix.table_count, meta, ret, done);
2484483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(pi->pi_msix.pba_offset, meta, ret, done);
2485483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(pi->pi_msix.pba_size, meta, ret, done);
2486483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(pi->pi_msix.function_mask, meta, ret, done);
2487483d953aSJohn Baldwin
2488483d953aSJohn Baldwin SNAPSHOT_BUF_OR_LEAVE(pi->pi_cfgdata, sizeof(pi->pi_cfgdata),
2489483d953aSJohn Baldwin meta, ret, done);
2490483d953aSJohn Baldwin
2491ed721684SMark Johnston for (i = 0; i < (int)nitems(pi->pi_bar); i++) {
2492483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(pi->pi_bar[i].type, meta, ret, done);
2493483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(pi->pi_bar[i].size, meta, ret, done);
2494483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(pi->pi_bar[i].addr, meta, ret, done);
2495483d953aSJohn Baldwin }
2496483d953aSJohn Baldwin
2497483d953aSJohn Baldwin /* Restore MSI-X table. */
2498483d953aSJohn Baldwin for (i = 0; i < pi->pi_msix.table_count; i++) {
2499483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(pi->pi_msix.table[i].addr,
2500483d953aSJohn Baldwin meta, ret, done);
2501483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(pi->pi_msix.table[i].msg_data,
2502483d953aSJohn Baldwin meta, ret, done);
2503483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(pi->pi_msix.table[i].vector_control,
2504483d953aSJohn Baldwin meta, ret, done);
2505483d953aSJohn Baldwin }
2506483d953aSJohn Baldwin
2507483d953aSJohn Baldwin done:
2508483d953aSJohn Baldwin return (ret);
2509483d953aSJohn Baldwin }
2510483d953aSJohn Baldwin
2511483d953aSJohn Baldwin int
pci_snapshot(struct vm_snapshot_meta * meta)2512483d953aSJohn Baldwin pci_snapshot(struct vm_snapshot_meta *meta)
2513483d953aSJohn Baldwin {
2514483d953aSJohn Baldwin struct pci_devemu *pde;
2515483d953aSJohn Baldwin struct pci_devinst *pdi;
2516483d953aSJohn Baldwin int ret;
2517483d953aSJohn Baldwin
2518483d953aSJohn Baldwin assert(meta->dev_name != NULL);
2519483d953aSJohn Baldwin
2520381ef27dSVitaliy Gusev pdi = meta->dev_data;
2521381ef27dSVitaliy Gusev pde = pdi->pi_d;
2522483d953aSJohn Baldwin
2523381ef27dSVitaliy Gusev if (pde->pe_snapshot == NULL)
2524381ef27dSVitaliy Gusev return (ENOTSUP);
2525483d953aSJohn Baldwin
2526483d953aSJohn Baldwin ret = pci_snapshot_pci_dev(meta);
2527381ef27dSVitaliy Gusev if (ret == 0)
2528483d953aSJohn Baldwin ret = (*pde->pe_snapshot)(meta);
2529483d953aSJohn Baldwin
2530483d953aSJohn Baldwin return (ret);
2531483d953aSJohn Baldwin }
2532483d953aSJohn Baldwin
2533483d953aSJohn Baldwin int
pci_pause(struct pci_devinst * pdi)2534381ef27dSVitaliy Gusev pci_pause(struct pci_devinst *pdi)
2535483d953aSJohn Baldwin {
2536381ef27dSVitaliy Gusev struct pci_devemu *pde = pdi->pi_d;
2537483d953aSJohn Baldwin
2538483d953aSJohn Baldwin if (pde->pe_pause == NULL) {
2539483d953aSJohn Baldwin /* The pause/resume functionality is optional. */
2540483d953aSJohn Baldwin return (0);
2541483d953aSJohn Baldwin }
2542483d953aSJohn Baldwin
25436a284cacSJohn Baldwin return (*pde->pe_pause)(pdi);
2544483d953aSJohn Baldwin }
2545483d953aSJohn Baldwin
2546483d953aSJohn Baldwin int
pci_resume(struct pci_devinst * pdi)2547381ef27dSVitaliy Gusev pci_resume(struct pci_devinst *pdi)
2548483d953aSJohn Baldwin {
2549381ef27dSVitaliy Gusev struct pci_devemu *pde = pdi->pi_d;
2550483d953aSJohn Baldwin
2551483d953aSJohn Baldwin if (pde->pe_resume == NULL) {
2552483d953aSJohn Baldwin /* The pause/resume functionality is optional. */
2553483d953aSJohn Baldwin return (0);
2554483d953aSJohn Baldwin }
2555483d953aSJohn Baldwin
25566a284cacSJohn Baldwin return (*pde->pe_resume)(pdi);
2557483d953aSJohn Baldwin }
2558483d953aSJohn Baldwin #endif
2559483d953aSJohn Baldwin
2560366f6083SPeter Grehan #define PCI_EMUL_TEST
2561366f6083SPeter Grehan #ifdef PCI_EMUL_TEST
2562366f6083SPeter Grehan /*
2563366f6083SPeter Grehan * Define a dummy test device
2564366f6083SPeter Grehan */
2565d84882caSNeel Natu #define DIOSZ 8
25664d1e669cSPeter Grehan #define DMEMSZ 4096
2567366f6083SPeter Grehan struct pci_emul_dsoftc {
25684d1e669cSPeter Grehan uint8_t ioregs[DIOSZ];
2569fd4e0d4cSNeel Natu uint8_t memregs[2][DMEMSZ];
2570366f6083SPeter Grehan };
2571366f6083SPeter Grehan
25724d1e669cSPeter Grehan #define PCI_EMUL_MSI_MSGS 4
25734d1e669cSPeter Grehan #define PCI_EMUL_MSIX_MSGS 16
2574366f6083SPeter Grehan
2575b67e81dbSJohn Baldwin static int
pci_emul_dinit(struct pci_devinst * pi,nvlist_t * nvl __unused)25766a284cacSJohn Baldwin pci_emul_dinit(struct pci_devinst *pi, nvlist_t *nvl __unused)
2577366f6083SPeter Grehan {
2578366f6083SPeter Grehan int error;
2579366f6083SPeter Grehan struct pci_emul_dsoftc *sc;
2580366f6083SPeter Grehan
2581994f858aSXin LI sc = calloc(1, sizeof(struct pci_emul_dsoftc));
2582366f6083SPeter Grehan
2583366f6083SPeter Grehan pi->pi_arg = sc;
2584366f6083SPeter Grehan
2585366f6083SPeter Grehan pci_set_cfgdata16(pi, PCIR_DEVICE, 0x0001);
2586366f6083SPeter Grehan pci_set_cfgdata16(pi, PCIR_VENDOR, 0x10DD);
2587366f6083SPeter Grehan pci_set_cfgdata8(pi, PCIR_CLASS, 0x02);
2588366f6083SPeter Grehan
25894d1e669cSPeter Grehan error = pci_emul_add_msicap(pi, PCI_EMUL_MSI_MSGS);
2590366f6083SPeter Grehan assert(error == 0);
2591366f6083SPeter Grehan
25924d1e669cSPeter Grehan error = pci_emul_alloc_bar(pi, 0, PCIBAR_IO, DIOSZ);
25934d1e669cSPeter Grehan assert(error == 0);
25944d1e669cSPeter Grehan
25954d1e669cSPeter Grehan error = pci_emul_alloc_bar(pi, 1, PCIBAR_MEM32, DMEMSZ);
2596366f6083SPeter Grehan assert(error == 0);
2597366f6083SPeter Grehan
2598fd4e0d4cSNeel Natu error = pci_emul_alloc_bar(pi, 2, PCIBAR_MEM32, DMEMSZ);
2599fd4e0d4cSNeel Natu assert(error == 0);
2600fd4e0d4cSNeel Natu
2601366f6083SPeter Grehan return (0);
2602366f6083SPeter Grehan }
2603366f6083SPeter Grehan
2604b67e81dbSJohn Baldwin static void
pci_emul_diow(struct pci_devinst * pi,int baridx,uint64_t offset,int size,uint64_t value)26056a284cacSJohn Baldwin pci_emul_diow(struct pci_devinst *pi, int baridx, uint64_t offset, int size,
260698d920d9SMark Johnston uint64_t value)
2607366f6083SPeter Grehan {
2608366f6083SPeter Grehan int i;
2609366f6083SPeter Grehan struct pci_emul_dsoftc *sc = pi->pi_arg;
2610366f6083SPeter Grehan
26114d1e669cSPeter Grehan if (baridx == 0) {
26124d1e669cSPeter Grehan if (offset + size > DIOSZ) {
26134d1e669cSPeter Grehan printf("diow: iow too large, offset %ld size %d\n",
26144d1e669cSPeter Grehan offset, size);
2615366f6083SPeter Grehan return;
2616366f6083SPeter Grehan }
2617366f6083SPeter Grehan
2618366f6083SPeter Grehan if (size == 1) {
26194d1e669cSPeter Grehan sc->ioregs[offset] = value & 0xff;
2620366f6083SPeter Grehan } else if (size == 2) {
26214d1e669cSPeter Grehan *(uint16_t *)&sc->ioregs[offset] = value & 0xffff;
26224d1e669cSPeter Grehan } else if (size == 4) {
26234d1e669cSPeter Grehan *(uint32_t *)&sc->ioregs[offset] = value;
2624366f6083SPeter Grehan } else {
26254d1e669cSPeter Grehan printf("diow: iow unknown size %d\n", size);
2626366f6083SPeter Grehan }
2627366f6083SPeter Grehan
2628366f6083SPeter Grehan /*
2629366f6083SPeter Grehan * Special magic value to generate an interrupt
2630366f6083SPeter Grehan */
2631366f6083SPeter Grehan if (offset == 4 && size == 4 && pci_msi_enabled(pi))
26324f8be175SNeel Natu pci_generate_msi(pi, value % pci_msi_maxmsgnum(pi));
2633366f6083SPeter Grehan
2634366f6083SPeter Grehan if (value == 0xabcdef) {
26354f8be175SNeel Natu for (i = 0; i < pci_msi_maxmsgnum(pi); i++)
2636366f6083SPeter Grehan pci_generate_msi(pi, i);
2637366f6083SPeter Grehan }
2638366f6083SPeter Grehan }
2639366f6083SPeter Grehan
2640fd4e0d4cSNeel Natu if (baridx == 1 || baridx == 2) {
26414d1e669cSPeter Grehan if (offset + size > DMEMSZ) {
26424d1e669cSPeter Grehan printf("diow: memw too large, offset %ld size %d\n",
26434d1e669cSPeter Grehan offset, size);
26444d1e669cSPeter Grehan return;
26454d1e669cSPeter Grehan }
26464d1e669cSPeter Grehan
2647fd4e0d4cSNeel Natu i = baridx - 1; /* 'memregs' index */
2648fd4e0d4cSNeel Natu
26494d1e669cSPeter Grehan if (size == 1) {
2650fd4e0d4cSNeel Natu sc->memregs[i][offset] = value;
26514d1e669cSPeter Grehan } else if (size == 2) {
2652fd4e0d4cSNeel Natu *(uint16_t *)&sc->memregs[i][offset] = value;
26534d1e669cSPeter Grehan } else if (size == 4) {
2654fd4e0d4cSNeel Natu *(uint32_t *)&sc->memregs[i][offset] = value;
26554d1e669cSPeter Grehan } else if (size == 8) {
2656fd4e0d4cSNeel Natu *(uint64_t *)&sc->memregs[i][offset] = value;
26574d1e669cSPeter Grehan } else {
26584d1e669cSPeter Grehan printf("diow: memw unknown size %d\n", size);
26594d1e669cSPeter Grehan }
26604d1e669cSPeter Grehan
26614d1e669cSPeter Grehan /*
26624d1e669cSPeter Grehan * magic interrupt ??
26634d1e669cSPeter Grehan */
26644d1e669cSPeter Grehan }
26654d1e669cSPeter Grehan
26669f3dba68SPedro F. Giffuni if (baridx > 2 || baridx < 0) {
26674d1e669cSPeter Grehan printf("diow: unknown bar idx %d\n", baridx);
26684d1e669cSPeter Grehan }
26694d1e669cSPeter Grehan }
26704d1e669cSPeter Grehan
26714d1e669cSPeter Grehan static uint64_t
pci_emul_dior(struct pci_devinst * pi,int baridx,uint64_t offset,int size)26726a284cacSJohn Baldwin pci_emul_dior(struct pci_devinst *pi, int baridx, uint64_t offset, int size)
2673366f6083SPeter Grehan {
2674366f6083SPeter Grehan struct pci_emul_dsoftc *sc = pi->pi_arg;
2675366f6083SPeter Grehan uint32_t value;
2676fd4e0d4cSNeel Natu int i;
2677366f6083SPeter Grehan
26784d1e669cSPeter Grehan if (baridx == 0) {
26794d1e669cSPeter Grehan if (offset + size > DIOSZ) {
26804d1e669cSPeter Grehan printf("dior: ior too large, offset %ld size %d\n",
26814d1e669cSPeter Grehan offset, size);
2682366f6083SPeter Grehan return (0);
2683366f6083SPeter Grehan }
2684366f6083SPeter Grehan
26856e43f3edSPedro F. Giffuni value = 0;
2686366f6083SPeter Grehan if (size == 1) {
26874d1e669cSPeter Grehan value = sc->ioregs[offset];
2688366f6083SPeter Grehan } else if (size == 2) {
26894d1e669cSPeter Grehan value = *(uint16_t *) &sc->ioregs[offset];
26904d1e669cSPeter Grehan } else if (size == 4) {
26914d1e669cSPeter Grehan value = *(uint32_t *) &sc->ioregs[offset];
2692366f6083SPeter Grehan } else {
26934d1e669cSPeter Grehan printf("dior: ior unknown size %d\n", size);
26944d1e669cSPeter Grehan }
26954d1e669cSPeter Grehan }
26964d1e669cSPeter Grehan
2697fd4e0d4cSNeel Natu if (baridx == 1 || baridx == 2) {
26984d1e669cSPeter Grehan if (offset + size > DMEMSZ) {
26994d1e669cSPeter Grehan printf("dior: memr too large, offset %ld size %d\n",
27004d1e669cSPeter Grehan offset, size);
27014d1e669cSPeter Grehan return (0);
27024d1e669cSPeter Grehan }
27034d1e669cSPeter Grehan
2704fd4e0d4cSNeel Natu i = baridx - 1; /* 'memregs' index */
2705fd4e0d4cSNeel Natu
27064d1e669cSPeter Grehan if (size == 1) {
2707fd4e0d4cSNeel Natu value = sc->memregs[i][offset];
27084d1e669cSPeter Grehan } else if (size == 2) {
2709fd4e0d4cSNeel Natu value = *(uint16_t *) &sc->memregs[i][offset];
27104d1e669cSPeter Grehan } else if (size == 4) {
2711fd4e0d4cSNeel Natu value = *(uint32_t *) &sc->memregs[i][offset];
27124d1e669cSPeter Grehan } else if (size == 8) {
2713fd4e0d4cSNeel Natu value = *(uint64_t *) &sc->memregs[i][offset];
27144d1e669cSPeter Grehan } else {
27154d1e669cSPeter Grehan printf("dior: ior unknown size %d\n", size);
27164d1e669cSPeter Grehan }
27174d1e669cSPeter Grehan }
27184d1e669cSPeter Grehan
27194d1e669cSPeter Grehan
27209f3dba68SPedro F. Giffuni if (baridx > 2 || baridx < 0) {
27214d1e669cSPeter Grehan printf("dior: unknown bar idx %d\n", baridx);
27224d1e669cSPeter Grehan return (0);
2723366f6083SPeter Grehan }
2724366f6083SPeter Grehan
2725366f6083SPeter Grehan return (value);
2726366f6083SPeter Grehan }
2727366f6083SPeter Grehan
2728483d953aSJohn Baldwin #ifdef BHYVE_SNAPSHOT
2729381ef27dSVitaliy Gusev struct pci_devinst *
pci_next(const struct pci_devinst * cursor)2730381ef27dSVitaliy Gusev pci_next(const struct pci_devinst *cursor)
2731381ef27dSVitaliy Gusev {
2732381ef27dSVitaliy Gusev unsigned bus = 0, slot = 0, func = 0;
2733381ef27dSVitaliy Gusev struct businfo *bi;
2734381ef27dSVitaliy Gusev struct slotinfo *si;
2735381ef27dSVitaliy Gusev struct funcinfo *fi;
2736381ef27dSVitaliy Gusev
2737381ef27dSVitaliy Gusev bus = cursor ? cursor->pi_bus : 0;
2738381ef27dSVitaliy Gusev slot = cursor ? cursor->pi_slot : 0;
2739381ef27dSVitaliy Gusev func = cursor ? (cursor->pi_func + 1) : 0;
2740381ef27dSVitaliy Gusev
2741381ef27dSVitaliy Gusev for (; bus < MAXBUSES; bus++) {
2742381ef27dSVitaliy Gusev if ((bi = pci_businfo[bus]) == NULL)
2743381ef27dSVitaliy Gusev continue;
2744381ef27dSVitaliy Gusev
2745381ef27dSVitaliy Gusev if (slot >= MAXSLOTS)
2746381ef27dSVitaliy Gusev slot = 0;
2747381ef27dSVitaliy Gusev
2748381ef27dSVitaliy Gusev for (; slot < MAXSLOTS; slot++) {
2749381ef27dSVitaliy Gusev si = &bi->slotinfo[slot];
2750381ef27dSVitaliy Gusev if (func >= MAXFUNCS)
2751381ef27dSVitaliy Gusev func = 0;
2752381ef27dSVitaliy Gusev for (; func < MAXFUNCS; func++) {
2753381ef27dSVitaliy Gusev fi = &si->si_funcs[func];
2754381ef27dSVitaliy Gusev if (fi->fi_devi == NULL)
2755381ef27dSVitaliy Gusev continue;
2756381ef27dSVitaliy Gusev
2757381ef27dSVitaliy Gusev return (fi->fi_devi);
2758381ef27dSVitaliy Gusev }
2759381ef27dSVitaliy Gusev }
2760381ef27dSVitaliy Gusev }
2761381ef27dSVitaliy Gusev
2762381ef27dSVitaliy Gusev return (NULL);
2763381ef27dSVitaliy Gusev }
2764381ef27dSVitaliy Gusev
2765c9faf698SMark Johnston static int
pci_emul_snapshot(struct vm_snapshot_meta * meta __unused)276698d920d9SMark Johnston pci_emul_snapshot(struct vm_snapshot_meta *meta __unused)
2767483d953aSJohn Baldwin {
2768483d953aSJohn Baldwin return (0);
2769483d953aSJohn Baldwin }
2770483d953aSJohn Baldwin #endif
2771483d953aSJohn Baldwin
277237045dfaSMark Johnston static const struct pci_devemu pci_dummy = {
2773366f6083SPeter Grehan .pe_emu = "dummy",
2774366f6083SPeter Grehan .pe_init = pci_emul_dinit,
27754d1e669cSPeter Grehan .pe_barwrite = pci_emul_diow,
2776483d953aSJohn Baldwin .pe_barread = pci_emul_dior,
2777483d953aSJohn Baldwin #ifdef BHYVE_SNAPSHOT
2778483d953aSJohn Baldwin .pe_snapshot = pci_emul_snapshot,
2779483d953aSJohn Baldwin #endif
2780366f6083SPeter Grehan };
2781366f6083SPeter Grehan PCI_EMUL_SET(pci_dummy);
2782366f6083SPeter Grehan
2783366f6083SPeter Grehan #endif /* PCI_EMUL_TEST */
2784