1366f6083SPeter Grehan /*- 21de7b4b8SPedro F. Giffuni * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 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 * $FreeBSD$ 29366f6083SPeter Grehan */ 30366f6083SPeter Grehan 31366f6083SPeter Grehan #include <sys/cdefs.h> 32366f6083SPeter Grehan __FBSDID("$FreeBSD$"); 33366f6083SPeter Grehan 34366f6083SPeter Grehan #include <sys/param.h> 35366f6083SPeter Grehan #include <sys/linker_set.h> 369922872bSKonstantin Belousov #include <vm/vm.h> 379922872bSKonstantin Belousov #include <vm/vm_param.h> 389922872bSKonstantin Belousov #include <vm/pmap.h> 39366f6083SPeter Grehan 40366f6083SPeter Grehan #include <ctype.h> 417e12dfe5SEnji Cooper #include <errno.h> 423cbf3585SJohn Baldwin #include <pthread.h> 43366f6083SPeter Grehan #include <stdio.h> 44366f6083SPeter Grehan #include <stdlib.h> 45366f6083SPeter Grehan #include <string.h> 46366f6083SPeter Grehan #include <strings.h> 47366f6083SPeter Grehan #include <assert.h> 48028d9311SNeel Natu #include <stdbool.h> 49366f6083SPeter Grehan 50366f6083SPeter Grehan #include <machine/vmm.h> 51483d953aSJohn Baldwin #include <machine/vmm_snapshot.h> 529922872bSKonstantin Belousov #include <machine/cpufunc.h> 539922872bSKonstantin Belousov #include <machine/specialreg.h> 54366f6083SPeter Grehan #include <vmmapi.h> 55366f6083SPeter Grehan 56e6c8bc29SJohn Baldwin #include "acpi.h" 57e285ef8dSPeter Grehan #include "bhyverun.h" 58621b5090SJohn Baldwin #include "config.h" 59332eff95SVincenzo Maffione #include "debug.h" 60366f6083SPeter Grehan #include "inout.h" 613cbf3585SJohn Baldwin #include "ioapic.h" 624d1e669cSPeter Grehan #include "mem.h" 63366f6083SPeter Grehan #include "pci_emul.h" 64b3e9732aSJohn Baldwin #include "pci_irq.h" 65e6c8bc29SJohn Baldwin #include "pci_lpc.h" 66366f6083SPeter Grehan 67366f6083SPeter Grehan #define CONF1_ADDR_PORT 0x0cf8 68366f6083SPeter Grehan #define CONF1_DATA_PORT 0x0cfc 69366f6083SPeter Grehan 7075543036SPeter Grehan #define CONF1_ENABLE 0x80000000ul 7175543036SPeter Grehan 72d84882caSNeel Natu #define MAXBUSES (PCI_BUSMAX + 1) 7399d65389SNeel Natu #define MAXSLOTS (PCI_SLOTMAX + 1) 7499d65389SNeel Natu #define MAXFUNCS (PCI_FUNCMAX + 1) 75366f6083SPeter Grehan 763cbf3585SJohn Baldwin struct funcinfo { 77621b5090SJohn Baldwin nvlist_t *fi_config; 78621b5090SJohn Baldwin struct pci_devemu *fi_pde; 793cbf3585SJohn Baldwin struct pci_devinst *fi_devi; 803cbf3585SJohn Baldwin }; 813cbf3585SJohn Baldwin 823cbf3585SJohn Baldwin struct intxinfo { 833cbf3585SJohn Baldwin int ii_count; 84b3e9732aSJohn Baldwin int ii_pirq_pin; 853cbf3585SJohn Baldwin int ii_ioapic_irq; 863cbf3585SJohn Baldwin }; 873cbf3585SJohn Baldwin 883cbf3585SJohn Baldwin struct slotinfo { 893cbf3585SJohn Baldwin struct intxinfo si_intpins[4]; 903cbf3585SJohn Baldwin struct funcinfo si_funcs[MAXFUNCS]; 91d84882caSNeel Natu }; 92d84882caSNeel Natu 93d84882caSNeel Natu struct businfo { 94d84882caSNeel Natu uint16_t iobase, iolimit; /* I/O window */ 95d84882caSNeel Natu uint32_t membase32, memlimit32; /* mmio window below 4GB */ 96d84882caSNeel Natu uint64_t membase64, memlimit64; /* mmio window above 4GB */ 97d84882caSNeel Natu struct slotinfo slotinfo[MAXSLOTS]; 98d84882caSNeel Natu }; 99d84882caSNeel Natu 100d84882caSNeel Natu static struct businfo *pci_businfo[MAXBUSES]; 101366f6083SPeter Grehan 102366f6083SPeter Grehan SET_DECLARE(pci_devemu_set, struct pci_devemu); 103366f6083SPeter Grehan 104366f6083SPeter Grehan static uint64_t pci_emul_iobase; 105366f6083SPeter Grehan static uint64_t pci_emul_membase32; 106366f6083SPeter Grehan static uint64_t pci_emul_membase64; 1079922872bSKonstantin Belousov static uint64_t pci_emul_memlim64; 108366f6083SPeter Grehan 109366f6083SPeter Grehan #define PCI_EMUL_IOBASE 0x2000 110366f6083SPeter Grehan #define PCI_EMUL_IOLIMIT 0x10000 111366f6083SPeter Grehan 11212a6eb99SNeel Natu #define PCI_EMUL_ECFG_BASE 0xE0000000 /* 3.5GB */ 11312a6eb99SNeel Natu #define PCI_EMUL_ECFG_SIZE (MAXBUSES * 1024 * 1024) /* 1MB per bus */ 11412a6eb99SNeel Natu SYSRES_MEM(PCI_EMUL_ECFG_BASE, PCI_EMUL_ECFG_SIZE); 11512a6eb99SNeel Natu 11612a6eb99SNeel Natu #define PCI_EMUL_MEMLIMIT32 PCI_EMUL_ECFG_BASE 117366f6083SPeter Grehan 118621b5090SJohn Baldwin static struct pci_devemu *pci_emul_finddev(const char *name); 119b3e9732aSJohn Baldwin static void pci_lintr_route(struct pci_devinst *pi); 1203cbf3585SJohn Baldwin static void pci_lintr_update(struct pci_devinst *pi); 12112a6eb99SNeel Natu static void pci_cfgrw(struct vmctx *ctx, int vcpu, int in, int bus, int slot, 12212a6eb99SNeel Natu int func, int coff, int bytes, uint32_t *val); 123366f6083SPeter Grehan 12454335630SNeel Natu static __inline void 12554335630SNeel Natu CFGWRITE(struct pci_devinst *pi, int coff, uint32_t val, int bytes) 12654335630SNeel Natu { 12754335630SNeel Natu 12854335630SNeel Natu if (bytes == 1) 12954335630SNeel Natu pci_set_cfgdata8(pi, coff, val); 13054335630SNeel Natu else if (bytes == 2) 13154335630SNeel Natu pci_set_cfgdata16(pi, coff, val); 13254335630SNeel Natu else 13354335630SNeel Natu pci_set_cfgdata32(pi, coff, val); 13454335630SNeel Natu } 13554335630SNeel Natu 13654335630SNeel Natu static __inline uint32_t 13754335630SNeel Natu CFGREAD(struct pci_devinst *pi, int coff, int bytes) 13854335630SNeel Natu { 13954335630SNeel Natu 14054335630SNeel Natu if (bytes == 1) 14154335630SNeel Natu return (pci_get_cfgdata8(pi, coff)); 14254335630SNeel Natu else if (bytes == 2) 14354335630SNeel Natu return (pci_get_cfgdata16(pi, coff)); 14454335630SNeel Natu else 14554335630SNeel Natu return (pci_get_cfgdata32(pi, coff)); 14654335630SNeel Natu } 14754335630SNeel Natu 148366f6083SPeter Grehan /* 149366f6083SPeter Grehan * I/O access 150366f6083SPeter Grehan */ 151366f6083SPeter Grehan 152366f6083SPeter Grehan /* 153366f6083SPeter Grehan * Slot options are in the form: 154366f6083SPeter Grehan * 155d84882caSNeel Natu * <bus>:<slot>:<func>,<emul>[,<config>] 15699d65389SNeel Natu * <slot>[:<func>],<emul>[,<config>] 157366f6083SPeter Grehan * 158366f6083SPeter Grehan * slot is 0..31 15999d65389SNeel Natu * func is 0..7 160366f6083SPeter Grehan * emul is a string describing the type of PCI device e.g. virtio-net 161366f6083SPeter Grehan * config is an optional string, depending on the device, that can be 162366f6083SPeter Grehan * used for configuration. 163366f6083SPeter Grehan * Examples are: 164366f6083SPeter Grehan * 1,virtio-net,tap0 16599d65389SNeel Natu * 3:0,dummy 166366f6083SPeter Grehan */ 167366f6083SPeter Grehan static void 168366f6083SPeter Grehan pci_parse_slot_usage(char *aopt) 169366f6083SPeter Grehan { 170b05c77ffSNeel Natu 171332eff95SVincenzo Maffione EPRINTLN("Invalid PCI slot info field \"%s\"", aopt); 172366f6083SPeter Grehan } 173366f6083SPeter Grehan 174621b5090SJohn Baldwin /* 175621b5090SJohn Baldwin * Helper function to parse a list of comma-separated options where 176621b5090SJohn Baldwin * each option is formatted as "name[=value]". If no value is 177621b5090SJohn Baldwin * provided, the option is treated as a boolean and is given a value 178621b5090SJohn Baldwin * of true. 179621b5090SJohn Baldwin */ 180621b5090SJohn Baldwin int 181621b5090SJohn Baldwin pci_parse_legacy_config(nvlist_t *nvl, const char *opt) 182621b5090SJohn Baldwin { 183621b5090SJohn Baldwin char *config, *name, *tofree, *value; 184621b5090SJohn Baldwin 185621b5090SJohn Baldwin if (opt == NULL) 186621b5090SJohn Baldwin return (0); 187621b5090SJohn Baldwin 188621b5090SJohn Baldwin config = tofree = strdup(opt); 189621b5090SJohn Baldwin while ((name = strsep(&config, ",")) != NULL) { 190621b5090SJohn Baldwin value = strchr(name, '='); 191621b5090SJohn Baldwin if (value != NULL) { 192621b5090SJohn Baldwin *value = '\0'; 193621b5090SJohn Baldwin value++; 194621b5090SJohn Baldwin set_config_value_node(nvl, name, value); 195621b5090SJohn Baldwin } else 196621b5090SJohn Baldwin set_config_bool_node(nvl, name, true); 197621b5090SJohn Baldwin } 198621b5090SJohn Baldwin free(tofree); 199621b5090SJohn Baldwin return (0); 200621b5090SJohn Baldwin } 201621b5090SJohn Baldwin 202621b5090SJohn Baldwin /* 203621b5090SJohn Baldwin * PCI device configuration is stored in MIBs that encode the device's 204621b5090SJohn Baldwin * location: 205621b5090SJohn Baldwin * 206621b5090SJohn Baldwin * pci.<bus>.<slot>.<func> 207621b5090SJohn Baldwin * 208621b5090SJohn Baldwin * Where "bus", "slot", and "func" are all decimal values without 209621b5090SJohn Baldwin * leading zeroes. Each valid device must have a "device" node which 210621b5090SJohn Baldwin * identifies the driver model of the device. 211621b5090SJohn Baldwin * 212621b5090SJohn Baldwin * Device backends can provide a parser for the "config" string. If 213621b5090SJohn Baldwin * a custom parser is not provided, pci_parse_legacy_config() is used 214621b5090SJohn Baldwin * to parse the string. 215621b5090SJohn Baldwin */ 216b05c77ffSNeel Natu int 217d2bc4816SJohn Baldwin pci_parse_slot(char *opt) 218366f6083SPeter Grehan { 219621b5090SJohn Baldwin char node_name[sizeof("pci.XXX.XX.X")]; 220621b5090SJohn Baldwin struct pci_devemu *pde; 221d84882caSNeel Natu char *emul, *config, *str, *cp; 222d84882caSNeel Natu int error, bnum, snum, fnum; 223621b5090SJohn Baldwin nvlist_t *nvl; 224366f6083SPeter Grehan 225b05c77ffSNeel Natu error = -1; 226d84882caSNeel Natu str = strdup(opt); 22799d65389SNeel Natu 228d84882caSNeel Natu emul = config = NULL; 229d84882caSNeel Natu if ((cp = strchr(str, ',')) != NULL) { 230d84882caSNeel Natu *cp = '\0'; 231d84882caSNeel Natu emul = cp + 1; 232d84882caSNeel Natu if ((cp = strchr(emul, ',')) != NULL) { 233d84882caSNeel Natu *cp = '\0'; 234d84882caSNeel Natu config = cp + 1; 23599d65389SNeel Natu } 236d84882caSNeel Natu } else { 237b05c77ffSNeel Natu pci_parse_slot_usage(opt); 238b05c77ffSNeel Natu goto done; 239366f6083SPeter Grehan } 240366f6083SPeter Grehan 241d84882caSNeel Natu /* <bus>:<slot>:<func> */ 242d84882caSNeel Natu if (sscanf(str, "%d:%d:%d", &bnum, &snum, &fnum) != 3) { 243d84882caSNeel Natu bnum = 0; 244d84882caSNeel Natu /* <slot>:<func> */ 245d84882caSNeel Natu if (sscanf(str, "%d:%d", &snum, &fnum) != 2) { 246d84882caSNeel Natu fnum = 0; 247d84882caSNeel Natu /* <slot> */ 248d84882caSNeel Natu if (sscanf(str, "%d", &snum) != 1) { 249d84882caSNeel Natu snum = -1; 250d84882caSNeel Natu } 251d84882caSNeel Natu } 252d84882caSNeel Natu } 253b05c77ffSNeel Natu 254d84882caSNeel Natu if (bnum < 0 || bnum >= MAXBUSES || snum < 0 || snum >= MAXSLOTS || 255d84882caSNeel Natu fnum < 0 || fnum >= MAXFUNCS) { 256b05c77ffSNeel Natu pci_parse_slot_usage(opt); 257b05c77ffSNeel Natu goto done; 258b05c77ffSNeel Natu } 259b05c77ffSNeel Natu 260621b5090SJohn Baldwin pde = pci_emul_finddev(emul); 261621b5090SJohn Baldwin if (pde == NULL) { 262621b5090SJohn Baldwin EPRINTLN("pci slot %d:%d:%d: unknown device \"%s\"", bnum, snum, 263621b5090SJohn Baldwin fnum, emul); 264b05c77ffSNeel Natu goto done; 265b05c77ffSNeel Natu } 266b05c77ffSNeel Natu 267621b5090SJohn Baldwin snprintf(node_name, sizeof(node_name), "pci.%d.%d.%d", bnum, snum, 268621b5090SJohn Baldwin fnum); 269621b5090SJohn Baldwin nvl = find_config_node(node_name); 270621b5090SJohn Baldwin if (nvl != NULL) { 271621b5090SJohn Baldwin EPRINTLN("pci slot %d:%d:%d already occupied!", bnum, snum, 272621b5090SJohn Baldwin fnum); 273b05c77ffSNeel Natu goto done; 274b05c77ffSNeel Natu } 275621b5090SJohn Baldwin nvl = create_config_node(node_name); 276621b5090SJohn Baldwin if (pde->pe_alias != NULL) 277621b5090SJohn Baldwin set_config_value_node(nvl, "device", pde->pe_alias); 278621b5090SJohn Baldwin else 279621b5090SJohn Baldwin set_config_value_node(nvl, "device", pde->pe_emu); 280b05c77ffSNeel Natu 281621b5090SJohn Baldwin if (pde->pe_legacy_config != NULL) 282621b5090SJohn Baldwin error = pde->pe_legacy_config(nvl, config); 283621b5090SJohn Baldwin else 284621b5090SJohn Baldwin error = pci_parse_legacy_config(nvl, config); 285b05c77ffSNeel Natu done: 286d84882caSNeel Natu free(str); 287b05c77ffSNeel Natu return (error); 288366f6083SPeter Grehan } 289366f6083SPeter Grehan 290657d2158SMarcelo Araujo void 291657d2158SMarcelo Araujo pci_print_supported_devices() 292657d2158SMarcelo Araujo { 293657d2158SMarcelo Araujo struct pci_devemu **pdpp, *pdp; 294657d2158SMarcelo Araujo 295657d2158SMarcelo Araujo SET_FOREACH(pdpp, pci_devemu_set) { 296657d2158SMarcelo Araujo pdp = *pdpp; 297657d2158SMarcelo Araujo printf("%s\n", pdp->pe_emu); 298657d2158SMarcelo Araujo } 299657d2158SMarcelo Araujo } 300657d2158SMarcelo Araujo 301366f6083SPeter Grehan static int 302c9b4e987SNeel Natu pci_valid_pba_offset(struct pci_devinst *pi, uint64_t offset) 303c9b4e987SNeel Natu { 304c9b4e987SNeel Natu 305c9b4e987SNeel Natu if (offset < pi->pi_msix.pba_offset) 306c9b4e987SNeel Natu return (0); 307c9b4e987SNeel Natu 308c9b4e987SNeel Natu if (offset >= pi->pi_msix.pba_offset + pi->pi_msix.pba_size) { 309c9b4e987SNeel Natu return (0); 310c9b4e987SNeel Natu } 311c9b4e987SNeel Natu 312c9b4e987SNeel Natu return (1); 313c9b4e987SNeel Natu } 314c9b4e987SNeel Natu 315c9b4e987SNeel Natu int 316c9b4e987SNeel Natu pci_emul_msix_twrite(struct pci_devinst *pi, uint64_t offset, int size, 317c9b4e987SNeel Natu uint64_t value) 318c9b4e987SNeel Natu { 319c9b4e987SNeel Natu int msix_entry_offset; 320c9b4e987SNeel Natu int tab_index; 321c9b4e987SNeel Natu char *dest; 322c9b4e987SNeel Natu 323c9b4e987SNeel Natu /* support only 4 or 8 byte writes */ 324c9b4e987SNeel Natu if (size != 4 && size != 8) 325c9b4e987SNeel Natu return (-1); 326c9b4e987SNeel Natu 327c9b4e987SNeel Natu /* 328c9b4e987SNeel Natu * Return if table index is beyond what device supports 329c9b4e987SNeel Natu */ 330c9b4e987SNeel Natu tab_index = offset / MSIX_TABLE_ENTRY_SIZE; 331c9b4e987SNeel Natu if (tab_index >= pi->pi_msix.table_count) 332c9b4e987SNeel Natu return (-1); 333c9b4e987SNeel Natu 334c9b4e987SNeel Natu msix_entry_offset = offset % MSIX_TABLE_ENTRY_SIZE; 335c9b4e987SNeel Natu 336c9b4e987SNeel Natu /* support only aligned writes */ 337c9b4e987SNeel Natu if ((msix_entry_offset % size) != 0) 338c9b4e987SNeel Natu return (-1); 339c9b4e987SNeel Natu 340c9b4e987SNeel Natu dest = (char *)(pi->pi_msix.table + tab_index); 341c9b4e987SNeel Natu dest += msix_entry_offset; 342c9b4e987SNeel Natu 343c9b4e987SNeel Natu if (size == 4) 344c9b4e987SNeel Natu *((uint32_t *)dest) = value; 345c9b4e987SNeel Natu else 346c9b4e987SNeel Natu *((uint64_t *)dest) = value; 347c9b4e987SNeel Natu 348c9b4e987SNeel Natu return (0); 349c9b4e987SNeel Natu } 350c9b4e987SNeel Natu 351c9b4e987SNeel Natu uint64_t 352c9b4e987SNeel Natu pci_emul_msix_tread(struct pci_devinst *pi, uint64_t offset, int size) 353c9b4e987SNeel Natu { 354c9b4e987SNeel Natu char *dest; 355c9b4e987SNeel Natu int msix_entry_offset; 356c9b4e987SNeel Natu int tab_index; 357c9b4e987SNeel Natu uint64_t retval = ~0; 358c9b4e987SNeel Natu 3596a52209fSNeel Natu /* 3606a52209fSNeel Natu * The PCI standard only allows 4 and 8 byte accesses to the MSI-X 361463a577bSEitan Adler * table but we also allow 1 byte access to accommodate reads from 3626a52209fSNeel Natu * ddb. 3636a52209fSNeel Natu */ 3646a52209fSNeel Natu if (size != 1 && size != 4 && size != 8) 365c9b4e987SNeel Natu return (retval); 366c9b4e987SNeel Natu 367c9b4e987SNeel Natu msix_entry_offset = offset % MSIX_TABLE_ENTRY_SIZE; 368c9b4e987SNeel Natu 369c9b4e987SNeel Natu /* support only aligned reads */ 370c9b4e987SNeel Natu if ((msix_entry_offset % size) != 0) { 371c9b4e987SNeel Natu return (retval); 372c9b4e987SNeel Natu } 373c9b4e987SNeel Natu 374c9b4e987SNeel Natu tab_index = offset / MSIX_TABLE_ENTRY_SIZE; 375c9b4e987SNeel Natu 376c9b4e987SNeel Natu if (tab_index < pi->pi_msix.table_count) { 377c9b4e987SNeel Natu /* valid MSI-X Table access */ 378c9b4e987SNeel Natu dest = (char *)(pi->pi_msix.table + tab_index); 379c9b4e987SNeel Natu dest += msix_entry_offset; 380c9b4e987SNeel Natu 3816a52209fSNeel Natu if (size == 1) 3826a52209fSNeel Natu retval = *((uint8_t *)dest); 3836a52209fSNeel Natu else if (size == 4) 384c9b4e987SNeel Natu retval = *((uint32_t *)dest); 385c9b4e987SNeel Natu else 386c9b4e987SNeel Natu retval = *((uint64_t *)dest); 387c9b4e987SNeel Natu } else if (pci_valid_pba_offset(pi, offset)) { 388c9b4e987SNeel Natu /* return 0 for PBA access */ 389c9b4e987SNeel Natu retval = 0; 390c9b4e987SNeel Natu } 391c9b4e987SNeel Natu 392c9b4e987SNeel Natu return (retval); 393c9b4e987SNeel Natu } 394c9b4e987SNeel Natu 395aa12663fSNeel Natu int 396aa12663fSNeel Natu pci_msix_table_bar(struct pci_devinst *pi) 397aa12663fSNeel Natu { 398aa12663fSNeel Natu 399aa12663fSNeel Natu if (pi->pi_msix.table != NULL) 400aa12663fSNeel Natu return (pi->pi_msix.table_bar); 401aa12663fSNeel Natu else 402aa12663fSNeel Natu return (-1); 403aa12663fSNeel Natu } 404aa12663fSNeel Natu 405aa12663fSNeel Natu int 406aa12663fSNeel Natu pci_msix_pba_bar(struct pci_devinst *pi) 407aa12663fSNeel Natu { 408aa12663fSNeel Natu 409aa12663fSNeel Natu if (pi->pi_msix.table != NULL) 410aa12663fSNeel Natu return (pi->pi_msix.pba_bar); 411aa12663fSNeel Natu else 412aa12663fSNeel Natu return (-1); 413aa12663fSNeel Natu } 414aa12663fSNeel Natu 415c9b4e987SNeel Natu static int 4164d1e669cSPeter Grehan pci_emul_io_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes, 417366f6083SPeter Grehan uint32_t *eax, void *arg) 418366f6083SPeter Grehan { 419366f6083SPeter Grehan struct pci_devinst *pdi = arg; 420366f6083SPeter Grehan struct pci_devemu *pe = pdi->pi_d; 4214d1e669cSPeter Grehan uint64_t offset; 4224d1e669cSPeter Grehan int i; 423366f6083SPeter Grehan 424366f6083SPeter Grehan for (i = 0; i <= PCI_BARMAX; i++) { 425366f6083SPeter Grehan if (pdi->pi_bar[i].type == PCIBAR_IO && 426366f6083SPeter Grehan port >= pdi->pi_bar[i].addr && 427c9b4e987SNeel Natu port + bytes <= pdi->pi_bar[i].addr + pdi->pi_bar[i].size) { 428366f6083SPeter Grehan offset = port - pdi->pi_bar[i].addr; 429366f6083SPeter Grehan if (in) 4304d1e669cSPeter Grehan *eax = (*pe->pe_barread)(ctx, vcpu, pdi, i, 4314d1e669cSPeter Grehan offset, bytes); 432366f6083SPeter Grehan else 4334d1e669cSPeter Grehan (*pe->pe_barwrite)(ctx, vcpu, pdi, i, offset, 4344d1e669cSPeter Grehan bytes, *eax); 435366f6083SPeter Grehan return (0); 436366f6083SPeter Grehan } 437366f6083SPeter Grehan } 438366f6083SPeter Grehan return (-1); 439366f6083SPeter Grehan } 440366f6083SPeter Grehan 441366f6083SPeter Grehan static int 4424d1e669cSPeter Grehan pci_emul_mem_handler(struct vmctx *ctx, int vcpu, int dir, uint64_t addr, 4434d1e669cSPeter Grehan int size, uint64_t *val, void *arg1, long arg2) 4444d1e669cSPeter Grehan { 4454d1e669cSPeter Grehan struct pci_devinst *pdi = arg1; 4464d1e669cSPeter Grehan struct pci_devemu *pe = pdi->pi_d; 4474d1e669cSPeter Grehan uint64_t offset; 4484d1e669cSPeter Grehan int bidx = (int) arg2; 4494d1e669cSPeter Grehan 4504d1e669cSPeter Grehan assert(bidx <= PCI_BARMAX); 4514d1e669cSPeter Grehan assert(pdi->pi_bar[bidx].type == PCIBAR_MEM32 || 4524d1e669cSPeter Grehan pdi->pi_bar[bidx].type == PCIBAR_MEM64); 4534d1e669cSPeter Grehan assert(addr >= pdi->pi_bar[bidx].addr && 4544d1e669cSPeter Grehan addr + size <= pdi->pi_bar[bidx].addr + pdi->pi_bar[bidx].size); 4554d1e669cSPeter Grehan 4564d1e669cSPeter Grehan offset = addr - pdi->pi_bar[bidx].addr; 4574d1e669cSPeter Grehan 458b6ae8b05STycho Nightingale if (dir == MEM_F_WRITE) { 45967b6ffaaSTycho Nightingale if (size == 8) { 460b6ae8b05STycho Nightingale (*pe->pe_barwrite)(ctx, vcpu, pdi, bidx, offset, 461b6ae8b05STycho Nightingale 4, *val & 0xffffffff); 462b6ae8b05STycho Nightingale (*pe->pe_barwrite)(ctx, vcpu, pdi, bidx, offset + 4, 463b6ae8b05STycho Nightingale 4, *val >> 32); 464b6ae8b05STycho Nightingale } else { 465b6ae8b05STycho Nightingale (*pe->pe_barwrite)(ctx, vcpu, pdi, bidx, offset, 466b6ae8b05STycho Nightingale size, *val); 467b6ae8b05STycho Nightingale } 468b6ae8b05STycho Nightingale } else { 46967b6ffaaSTycho Nightingale if (size == 8) { 470b6ae8b05STycho Nightingale *val = (*pe->pe_barread)(ctx, vcpu, pdi, bidx, 471b6ae8b05STycho Nightingale offset, 4); 472b6ae8b05STycho Nightingale *val |= (*pe->pe_barread)(ctx, vcpu, pdi, bidx, 473b6ae8b05STycho Nightingale offset + 4, 4) << 32; 474b6ae8b05STycho Nightingale } else { 475b6ae8b05STycho Nightingale *val = (*pe->pe_barread)(ctx, vcpu, pdi, bidx, 476b6ae8b05STycho Nightingale offset, size); 477b6ae8b05STycho Nightingale } 478b6ae8b05STycho Nightingale } 4794d1e669cSPeter Grehan 4804d1e669cSPeter Grehan return (0); 4814d1e669cSPeter Grehan } 4824d1e669cSPeter Grehan 4834d1e669cSPeter Grehan 4844d1e669cSPeter Grehan static int 485366f6083SPeter Grehan pci_emul_alloc_resource(uint64_t *baseptr, uint64_t limit, uint64_t size, 486366f6083SPeter Grehan uint64_t *addr) 487366f6083SPeter Grehan { 488366f6083SPeter Grehan uint64_t base; 489366f6083SPeter Grehan 490366f6083SPeter Grehan assert((size & (size - 1)) == 0); /* must be a power of 2 */ 491366f6083SPeter Grehan 492366f6083SPeter Grehan base = roundup2(*baseptr, size); 493366f6083SPeter Grehan 494366f6083SPeter Grehan if (base + size <= limit) { 495366f6083SPeter Grehan *addr = base; 496366f6083SPeter Grehan *baseptr = base + size; 497366f6083SPeter Grehan return (0); 498366f6083SPeter Grehan } else 499366f6083SPeter Grehan return (-1); 500366f6083SPeter Grehan } 501366f6083SPeter Grehan 502028d9311SNeel Natu /* 503028d9311SNeel Natu * Register (or unregister) the MMIO or I/O region associated with the BAR 504028d9311SNeel Natu * register 'idx' of an emulated pci device. 505028d9311SNeel Natu */ 506028d9311SNeel Natu static void 507028d9311SNeel Natu modify_bar_registration(struct pci_devinst *pi, int idx, int registration) 508028d9311SNeel Natu { 509f8a6ec2dSD Scott Phillips struct pci_devemu *pe; 510028d9311SNeel Natu int error; 511028d9311SNeel Natu struct inout_port iop; 512028d9311SNeel Natu struct mem_range mr; 513028d9311SNeel Natu 514f8a6ec2dSD Scott Phillips pe = pi->pi_d; 515028d9311SNeel Natu switch (pi->pi_bar[idx].type) { 516028d9311SNeel Natu case PCIBAR_IO: 517028d9311SNeel Natu bzero(&iop, sizeof(struct inout_port)); 518028d9311SNeel Natu iop.name = pi->pi_name; 519028d9311SNeel Natu iop.port = pi->pi_bar[idx].addr; 520028d9311SNeel Natu iop.size = pi->pi_bar[idx].size; 521028d9311SNeel Natu if (registration) { 522028d9311SNeel Natu iop.flags = IOPORT_F_INOUT; 523028d9311SNeel Natu iop.handler = pci_emul_io_handler; 524028d9311SNeel Natu iop.arg = pi; 525028d9311SNeel Natu error = register_inout(&iop); 526028d9311SNeel Natu } else 527028d9311SNeel Natu error = unregister_inout(&iop); 528f8a6ec2dSD Scott Phillips if (pe->pe_baraddr != NULL) 529f8a6ec2dSD Scott Phillips (*pe->pe_baraddr)(pi->pi_vmctx, pi, idx, registration, 530f8a6ec2dSD Scott Phillips pi->pi_bar[idx].addr); 531028d9311SNeel Natu break; 532028d9311SNeel Natu case PCIBAR_MEM32: 533028d9311SNeel Natu case PCIBAR_MEM64: 534028d9311SNeel Natu bzero(&mr, sizeof(struct mem_range)); 535028d9311SNeel Natu mr.name = pi->pi_name; 536028d9311SNeel Natu mr.base = pi->pi_bar[idx].addr; 537028d9311SNeel Natu mr.size = pi->pi_bar[idx].size; 538028d9311SNeel Natu if (registration) { 539028d9311SNeel Natu mr.flags = MEM_F_RW; 540028d9311SNeel Natu mr.handler = pci_emul_mem_handler; 541028d9311SNeel Natu mr.arg1 = pi; 542028d9311SNeel Natu mr.arg2 = idx; 543028d9311SNeel Natu error = register_mem(&mr); 544028d9311SNeel Natu } else 545028d9311SNeel Natu error = unregister_mem(&mr); 546f8a6ec2dSD Scott Phillips if (pe->pe_baraddr != NULL) 547f8a6ec2dSD Scott Phillips (*pe->pe_baraddr)(pi->pi_vmctx, pi, idx, registration, 548f8a6ec2dSD Scott Phillips pi->pi_bar[idx].addr); 549028d9311SNeel Natu break; 550028d9311SNeel Natu default: 551028d9311SNeel Natu error = EINVAL; 552028d9311SNeel Natu break; 553028d9311SNeel Natu } 554028d9311SNeel Natu assert(error == 0); 555028d9311SNeel Natu } 556028d9311SNeel Natu 557028d9311SNeel Natu static void 558028d9311SNeel Natu unregister_bar(struct pci_devinst *pi, int idx) 559028d9311SNeel Natu { 560028d9311SNeel Natu 561028d9311SNeel Natu modify_bar_registration(pi, idx, 0); 562028d9311SNeel Natu } 563028d9311SNeel Natu 564028d9311SNeel Natu static void 565028d9311SNeel Natu register_bar(struct pci_devinst *pi, int idx) 566028d9311SNeel Natu { 567028d9311SNeel Natu 568028d9311SNeel Natu modify_bar_registration(pi, idx, 1); 569028d9311SNeel Natu } 570028d9311SNeel Natu 571028d9311SNeel Natu /* Are we decoding i/o port accesses for the emulated pci device? */ 572028d9311SNeel Natu static int 573028d9311SNeel Natu porten(struct pci_devinst *pi) 574028d9311SNeel Natu { 575028d9311SNeel Natu uint16_t cmd; 576028d9311SNeel Natu 577028d9311SNeel Natu cmd = pci_get_cfgdata16(pi, PCIR_COMMAND); 578028d9311SNeel Natu 579028d9311SNeel Natu return (cmd & PCIM_CMD_PORTEN); 580028d9311SNeel Natu } 581028d9311SNeel Natu 582028d9311SNeel Natu /* Are we decoding memory accesses for the emulated pci device? */ 583028d9311SNeel Natu static int 584028d9311SNeel Natu memen(struct pci_devinst *pi) 585028d9311SNeel Natu { 586028d9311SNeel Natu uint16_t cmd; 587028d9311SNeel Natu 588028d9311SNeel Natu cmd = pci_get_cfgdata16(pi, PCIR_COMMAND); 589028d9311SNeel Natu 590028d9311SNeel Natu return (cmd & PCIM_CMD_MEMEN); 591028d9311SNeel Natu } 592028d9311SNeel Natu 593028d9311SNeel Natu /* 594028d9311SNeel Natu * Update the MMIO or I/O address that is decoded by the BAR register. 595028d9311SNeel Natu * 596028d9311SNeel Natu * If the pci device has enabled the address space decoding then intercept 597028d9311SNeel Natu * the address range decoded by the BAR register. 598028d9311SNeel Natu */ 599028d9311SNeel Natu static void 600028d9311SNeel Natu update_bar_address(struct pci_devinst *pi, uint64_t addr, int idx, int type) 601028d9311SNeel Natu { 602028d9311SNeel Natu int decode; 603028d9311SNeel Natu 604028d9311SNeel Natu if (pi->pi_bar[idx].type == PCIBAR_IO) 605028d9311SNeel Natu decode = porten(pi); 606028d9311SNeel Natu else 607028d9311SNeel Natu decode = memen(pi); 608028d9311SNeel Natu 609028d9311SNeel Natu if (decode) 610028d9311SNeel Natu unregister_bar(pi, idx); 611028d9311SNeel Natu 612028d9311SNeel Natu switch (type) { 613028d9311SNeel Natu case PCIBAR_IO: 614028d9311SNeel Natu case PCIBAR_MEM32: 615028d9311SNeel Natu pi->pi_bar[idx].addr = addr; 616028d9311SNeel Natu break; 617028d9311SNeel Natu case PCIBAR_MEM64: 618028d9311SNeel Natu pi->pi_bar[idx].addr &= ~0xffffffffUL; 619028d9311SNeel Natu pi->pi_bar[idx].addr |= addr; 620028d9311SNeel Natu break; 621028d9311SNeel Natu case PCIBAR_MEMHI64: 622028d9311SNeel Natu pi->pi_bar[idx].addr &= 0xffffffff; 623028d9311SNeel Natu pi->pi_bar[idx].addr |= addr; 624028d9311SNeel Natu break; 625028d9311SNeel Natu default: 626028d9311SNeel Natu assert(0); 627028d9311SNeel Natu } 628028d9311SNeel Natu 629028d9311SNeel Natu if (decode) 630028d9311SNeel Natu register_bar(pi, idx); 631028d9311SNeel Natu } 632028d9311SNeel Natu 6334d1e669cSPeter Grehan int 634038f5c7bSKonstantin Belousov pci_emul_alloc_bar(struct pci_devinst *pdi, int idx, enum pcibar_type type, 635038f5c7bSKonstantin Belousov uint64_t size) 636366f6083SPeter Grehan { 637028d9311SNeel Natu int error; 638366f6083SPeter Grehan uint64_t *baseptr, limit, addr, mask, lobits, bar; 6392729c9bbSJohn Baldwin uint16_t cmd, enbit; 640366f6083SPeter Grehan 641366f6083SPeter Grehan assert(idx >= 0 && idx <= PCI_BARMAX); 642366f6083SPeter Grehan 643366f6083SPeter Grehan if ((size & (size - 1)) != 0) 644366f6083SPeter Grehan size = 1UL << flsl(size); /* round up to a power of 2 */ 645366f6083SPeter Grehan 646028d9311SNeel Natu /* Enforce minimum BAR sizes required by the PCI standard */ 647028d9311SNeel Natu if (type == PCIBAR_IO) { 648028d9311SNeel Natu if (size < 4) 649028d9311SNeel Natu size = 4; 650028d9311SNeel Natu } else { 651028d9311SNeel Natu if (size < 16) 652028d9311SNeel Natu size = 16; 653028d9311SNeel Natu } 654028d9311SNeel Natu 655366f6083SPeter Grehan switch (type) { 656366f6083SPeter Grehan case PCIBAR_NONE: 657366f6083SPeter Grehan baseptr = NULL; 6582729c9bbSJohn Baldwin addr = mask = lobits = enbit = 0; 659366f6083SPeter Grehan break; 660366f6083SPeter Grehan case PCIBAR_IO: 661366f6083SPeter Grehan baseptr = &pci_emul_iobase; 662366f6083SPeter Grehan limit = PCI_EMUL_IOLIMIT; 663366f6083SPeter Grehan mask = PCIM_BAR_IO_BASE; 664366f6083SPeter Grehan lobits = PCIM_BAR_IO_SPACE; 6652729c9bbSJohn Baldwin enbit = PCIM_CMD_PORTEN; 666366f6083SPeter Grehan break; 667366f6083SPeter Grehan case PCIBAR_MEM64: 668366f6083SPeter Grehan /* 669366f6083SPeter Grehan * XXX 670366f6083SPeter Grehan * Some drivers do not work well if the 64-bit BAR is allocated 671366f6083SPeter Grehan * above 4GB. Allow for this by allocating small requests under 672366f6083SPeter Grehan * 4GB unless then allocation size is larger than some arbitrary 673670b364bSKonstantin Belousov * number (128MB currently). 674366f6083SPeter Grehan */ 675670b364bSKonstantin Belousov if (size > 128 * 1024 * 1024) { 676366f6083SPeter Grehan baseptr = &pci_emul_membase64; 6779922872bSKonstantin Belousov limit = pci_emul_memlim64; 678366f6083SPeter Grehan mask = PCIM_BAR_MEM_BASE; 679366f6083SPeter Grehan lobits = PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_64 | 680366f6083SPeter Grehan PCIM_BAR_MEM_PREFETCH; 68125d4944eSNeel Natu } else { 68225d4944eSNeel Natu baseptr = &pci_emul_membase32; 68325d4944eSNeel Natu limit = PCI_EMUL_MEMLIMIT32; 68425d4944eSNeel Natu mask = PCIM_BAR_MEM_BASE; 68525d4944eSNeel Natu lobits = PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_64; 686366f6083SPeter Grehan } 6872729c9bbSJohn Baldwin enbit = PCIM_CMD_MEMEN; 68825d4944eSNeel Natu break; 689366f6083SPeter Grehan case PCIBAR_MEM32: 690366f6083SPeter Grehan baseptr = &pci_emul_membase32; 691366f6083SPeter Grehan limit = PCI_EMUL_MEMLIMIT32; 692366f6083SPeter Grehan mask = PCIM_BAR_MEM_BASE; 693366f6083SPeter Grehan lobits = PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_32; 6942729c9bbSJohn Baldwin enbit = PCIM_CMD_MEMEN; 695366f6083SPeter Grehan break; 696366f6083SPeter Grehan default: 697366f6083SPeter Grehan printf("pci_emul_alloc_base: invalid bar type %d\n", type); 698366f6083SPeter Grehan assert(0); 699366f6083SPeter Grehan } 700366f6083SPeter Grehan 701366f6083SPeter Grehan if (baseptr != NULL) { 702366f6083SPeter Grehan error = pci_emul_alloc_resource(baseptr, limit, size, &addr); 703366f6083SPeter Grehan if (error != 0) 704366f6083SPeter Grehan return (error); 705366f6083SPeter Grehan } 706366f6083SPeter Grehan 707366f6083SPeter Grehan pdi->pi_bar[idx].type = type; 708366f6083SPeter Grehan pdi->pi_bar[idx].addr = addr; 709366f6083SPeter Grehan pdi->pi_bar[idx].size = size; 710366f6083SPeter Grehan 711366f6083SPeter Grehan /* Initialize the BAR register in config space */ 712366f6083SPeter Grehan bar = (addr & mask) | lobits; 713366f6083SPeter Grehan pci_set_cfgdata32(pdi, PCIR_BAR(idx), bar); 714366f6083SPeter Grehan 715366f6083SPeter Grehan if (type == PCIBAR_MEM64) { 716366f6083SPeter Grehan assert(idx + 1 <= PCI_BARMAX); 717366f6083SPeter Grehan pdi->pi_bar[idx + 1].type = PCIBAR_MEMHI64; 718366f6083SPeter Grehan pci_set_cfgdata32(pdi, PCIR_BAR(idx + 1), bar >> 32); 719366f6083SPeter Grehan } 720366f6083SPeter Grehan 7212729c9bbSJohn Baldwin cmd = pci_get_cfgdata16(pdi, PCIR_COMMAND); 7222729c9bbSJohn Baldwin if ((cmd & enbit) != enbit) 7232729c9bbSJohn Baldwin pci_set_cfgdata16(pdi, PCIR_COMMAND, cmd | enbit); 724028d9311SNeel Natu register_bar(pdi, idx); 725366f6083SPeter Grehan 726366f6083SPeter Grehan return (0); 727366f6083SPeter Grehan } 728366f6083SPeter Grehan 729366f6083SPeter Grehan #define CAP_START_OFFSET 0x40 730366f6083SPeter Grehan static int 731366f6083SPeter Grehan pci_emul_add_capability(struct pci_devinst *pi, u_char *capdata, int caplen) 732366f6083SPeter Grehan { 733a96b8b80SJohn Baldwin int i, capoff, reallen; 734366f6083SPeter Grehan uint16_t sts; 735366f6083SPeter Grehan 736a96b8b80SJohn Baldwin assert(caplen > 0); 737366f6083SPeter Grehan 738366f6083SPeter Grehan reallen = roundup2(caplen, 4); /* dword aligned */ 739366f6083SPeter Grehan 740366f6083SPeter Grehan sts = pci_get_cfgdata16(pi, PCIR_STATUS); 741a96b8b80SJohn Baldwin if ((sts & PCIM_STATUS_CAPPRESENT) == 0) 742366f6083SPeter Grehan capoff = CAP_START_OFFSET; 743a96b8b80SJohn Baldwin else 744a96b8b80SJohn Baldwin capoff = pi->pi_capend + 1; 745366f6083SPeter Grehan 746366f6083SPeter Grehan /* Check if we have enough space */ 747a96b8b80SJohn Baldwin if (capoff + reallen > PCI_REGMAX + 1) 748366f6083SPeter Grehan return (-1); 749366f6083SPeter Grehan 750a96b8b80SJohn Baldwin /* Set the previous capability pointer */ 751a96b8b80SJohn Baldwin if ((sts & PCIM_STATUS_CAPPRESENT) == 0) { 752a96b8b80SJohn Baldwin pci_set_cfgdata8(pi, PCIR_CAP_PTR, capoff); 753a96b8b80SJohn Baldwin pci_set_cfgdata16(pi, PCIR_STATUS, sts|PCIM_STATUS_CAPPRESENT); 754a96b8b80SJohn Baldwin } else 755a96b8b80SJohn Baldwin pci_set_cfgdata8(pi, pi->pi_prevcap + 1, capoff); 756a96b8b80SJohn Baldwin 757366f6083SPeter Grehan /* Copy the capability */ 758366f6083SPeter Grehan for (i = 0; i < caplen; i++) 759366f6083SPeter Grehan pci_set_cfgdata8(pi, capoff + i, capdata[i]); 760366f6083SPeter Grehan 761366f6083SPeter Grehan /* Set the next capability pointer */ 762a96b8b80SJohn Baldwin pci_set_cfgdata8(pi, capoff + 1, 0); 763366f6083SPeter Grehan 764a96b8b80SJohn Baldwin pi->pi_prevcap = capoff; 765a96b8b80SJohn Baldwin pi->pi_capend = capoff + reallen - 1; 766366f6083SPeter Grehan return (0); 767366f6083SPeter Grehan } 768366f6083SPeter Grehan 769366f6083SPeter Grehan static struct pci_devemu * 770621b5090SJohn Baldwin pci_emul_finddev(const char *name) 771366f6083SPeter Grehan { 772366f6083SPeter Grehan struct pci_devemu **pdpp, *pdp; 773366f6083SPeter Grehan 774366f6083SPeter Grehan SET_FOREACH(pdpp, pci_devemu_set) { 775366f6083SPeter Grehan pdp = *pdpp; 776366f6083SPeter Grehan if (!strcmp(pdp->pe_emu, name)) { 777366f6083SPeter Grehan return (pdp); 778366f6083SPeter Grehan } 779366f6083SPeter Grehan } 780366f6083SPeter Grehan 781366f6083SPeter Grehan return (NULL); 782366f6083SPeter Grehan } 783366f6083SPeter Grehan 784a38e2a64SPeter Grehan static int 785d84882caSNeel Natu pci_emul_init(struct vmctx *ctx, struct pci_devemu *pde, int bus, int slot, 786d84882caSNeel Natu int func, struct funcinfo *fi) 787366f6083SPeter Grehan { 788366f6083SPeter Grehan struct pci_devinst *pdi; 789a38e2a64SPeter Grehan int err; 790a38e2a64SPeter Grehan 791994f858aSXin LI pdi = calloc(1, sizeof(struct pci_devinst)); 792366f6083SPeter Grehan 793366f6083SPeter Grehan pdi->pi_vmctx = ctx; 794d84882caSNeel Natu pdi->pi_bus = bus; 795366f6083SPeter Grehan pdi->pi_slot = slot; 79699d65389SNeel Natu pdi->pi_func = func; 7973cbf3585SJohn Baldwin pthread_mutex_init(&pdi->pi_lintr.lock, NULL); 7983cbf3585SJohn Baldwin pdi->pi_lintr.pin = 0; 7993cbf3585SJohn Baldwin pdi->pi_lintr.state = IDLE; 800b3e9732aSJohn Baldwin pdi->pi_lintr.pirq_pin = 0; 8013cbf3585SJohn Baldwin pdi->pi_lintr.ioapic_irq = 0; 802366f6083SPeter Grehan pdi->pi_d = pde; 803366f6083SPeter Grehan snprintf(pdi->pi_name, PI_NAMESZ, "%s-pci-%d", pde->pe_emu, slot); 804366f6083SPeter Grehan 805366f6083SPeter Grehan /* Disable legacy interrupts */ 806366f6083SPeter Grehan pci_set_cfgdata8(pdi, PCIR_INTLINE, 255); 807366f6083SPeter Grehan pci_set_cfgdata8(pdi, PCIR_INTPIN, 0); 808366f6083SPeter Grehan 8092729c9bbSJohn Baldwin pci_set_cfgdata8(pdi, PCIR_COMMAND, PCIM_CMD_BUSMASTEREN); 810366f6083SPeter Grehan 811621b5090SJohn Baldwin err = (*pde->pe_init)(ctx, pdi, fi->fi_config); 812d84882caSNeel Natu if (err == 0) 813d84882caSNeel Natu fi->fi_devi = pdi; 814d84882caSNeel Natu else 815366f6083SPeter Grehan free(pdi); 816a38e2a64SPeter Grehan 817a38e2a64SPeter Grehan return (err); 818366f6083SPeter Grehan } 819366f6083SPeter Grehan 820366f6083SPeter Grehan void 821366f6083SPeter Grehan pci_populate_msicap(struct msicap *msicap, int msgnum, int nextptr) 822366f6083SPeter Grehan { 823366f6083SPeter Grehan int mmc; 824366f6083SPeter Grehan 825366f6083SPeter Grehan /* Number of msi messages must be a power of 2 between 1 and 32 */ 826366f6083SPeter Grehan assert((msgnum & (msgnum - 1)) == 0 && msgnum >= 1 && msgnum <= 32); 827366f6083SPeter Grehan mmc = ffs(msgnum) - 1; 828366f6083SPeter Grehan 829366f6083SPeter Grehan bzero(msicap, sizeof(struct msicap)); 830366f6083SPeter Grehan msicap->capid = PCIY_MSI; 831366f6083SPeter Grehan msicap->nextptr = nextptr; 832366f6083SPeter Grehan msicap->msgctrl = PCIM_MSICTRL_64BIT | (mmc << 1); 833366f6083SPeter Grehan } 834366f6083SPeter Grehan 835366f6083SPeter Grehan int 836366f6083SPeter Grehan pci_emul_add_msicap(struct pci_devinst *pi, int msgnum) 837366f6083SPeter Grehan { 838366f6083SPeter Grehan struct msicap msicap; 839366f6083SPeter Grehan 840366f6083SPeter Grehan pci_populate_msicap(&msicap, msgnum, 0); 841366f6083SPeter Grehan 842366f6083SPeter Grehan return (pci_emul_add_capability(pi, (u_char *)&msicap, sizeof(msicap))); 843366f6083SPeter Grehan } 844366f6083SPeter Grehan 845c9b4e987SNeel Natu static void 846c9b4e987SNeel Natu pci_populate_msixcap(struct msixcap *msixcap, int msgnum, int barnum, 847a96b8b80SJohn Baldwin uint32_t msix_tab_size) 848c9b4e987SNeel Natu { 849c9b4e987SNeel Natu 850c9b4e987SNeel Natu assert(msix_tab_size % 4096 == 0); 851c9b4e987SNeel Natu 852c9b4e987SNeel Natu bzero(msixcap, sizeof(struct msixcap)); 853c9b4e987SNeel Natu msixcap->capid = PCIY_MSIX; 854c9b4e987SNeel Natu 855c9b4e987SNeel Natu /* 856c9b4e987SNeel Natu * Message Control Register, all fields set to 857c9b4e987SNeel Natu * zero except for the Table Size. 858c9b4e987SNeel Natu * Note: Table size N is encoded as N-1 859c9b4e987SNeel Natu */ 860c9b4e987SNeel Natu msixcap->msgctrl = msgnum - 1; 861c9b4e987SNeel Natu 862c9b4e987SNeel Natu /* 863c9b4e987SNeel Natu * MSI-X BAR setup: 864c9b4e987SNeel Natu * - MSI-X table start at offset 0 865c9b4e987SNeel Natu * - PBA table starts at a 4K aligned offset after the MSI-X table 866c9b4e987SNeel Natu */ 867c9b4e987SNeel Natu msixcap->table_info = barnum & PCIM_MSIX_BIR_MASK; 868c9b4e987SNeel Natu msixcap->pba_info = msix_tab_size | (barnum & PCIM_MSIX_BIR_MASK); 869c9b4e987SNeel Natu } 870c9b4e987SNeel Natu 871c9b4e987SNeel Natu static void 872c9b4e987SNeel Natu pci_msix_table_init(struct pci_devinst *pi, int table_entries) 873c9b4e987SNeel Natu { 874c9b4e987SNeel Natu int i, table_size; 875c9b4e987SNeel Natu 876c9b4e987SNeel Natu assert(table_entries > 0); 877c9b4e987SNeel Natu assert(table_entries <= MAX_MSIX_TABLE_ENTRIES); 878c9b4e987SNeel Natu 879c9b4e987SNeel Natu table_size = table_entries * MSIX_TABLE_ENTRY_SIZE; 880994f858aSXin LI pi->pi_msix.table = calloc(1, table_size); 881c9b4e987SNeel Natu 882c9b4e987SNeel Natu /* set mask bit of vector control register */ 883c9b4e987SNeel Natu for (i = 0; i < table_entries; i++) 884c9b4e987SNeel Natu pi->pi_msix.table[i].vector_control |= PCIM_MSIX_VCTRL_MASK; 885c9b4e987SNeel Natu } 886c9b4e987SNeel Natu 887c9b4e987SNeel Natu int 888c9b4e987SNeel Natu pci_emul_add_msixcap(struct pci_devinst *pi, int msgnum, int barnum) 889c9b4e987SNeel Natu { 890c9b4e987SNeel Natu uint32_t tab_size; 891c9b4e987SNeel Natu struct msixcap msixcap; 892c9b4e987SNeel Natu 893c9b4e987SNeel Natu assert(msgnum >= 1 && msgnum <= MAX_MSIX_TABLE_ENTRIES); 894c9b4e987SNeel Natu assert(barnum >= 0 && barnum <= PCIR_MAX_BAR_0); 895c9b4e987SNeel Natu 896c9b4e987SNeel Natu tab_size = msgnum * MSIX_TABLE_ENTRY_SIZE; 897c9b4e987SNeel Natu 898c9b4e987SNeel Natu /* Align table size to nearest 4K */ 899c9b4e987SNeel Natu tab_size = roundup2(tab_size, 4096); 900c9b4e987SNeel Natu 901c9b4e987SNeel Natu pi->pi_msix.table_bar = barnum; 902c9b4e987SNeel Natu pi->pi_msix.pba_bar = barnum; 903c9b4e987SNeel Natu pi->pi_msix.table_offset = 0; 904c9b4e987SNeel Natu pi->pi_msix.table_count = msgnum; 905c9b4e987SNeel Natu pi->pi_msix.pba_offset = tab_size; 9067a902ec0SNeel Natu pi->pi_msix.pba_size = PBA_SIZE(msgnum); 907c9b4e987SNeel Natu 908c9b4e987SNeel Natu pci_msix_table_init(pi, msgnum); 909c9b4e987SNeel Natu 910a96b8b80SJohn Baldwin pci_populate_msixcap(&msixcap, msgnum, barnum, tab_size); 911c9b4e987SNeel Natu 912c9b4e987SNeel Natu /* allocate memory for MSI-X Table and PBA */ 913c9b4e987SNeel Natu pci_emul_alloc_bar(pi, barnum, PCIBAR_MEM32, 914c9b4e987SNeel Natu tab_size + pi->pi_msix.pba_size); 915c9b4e987SNeel Natu 916c9b4e987SNeel Natu return (pci_emul_add_capability(pi, (u_char *)&msixcap, 917c9b4e987SNeel Natu sizeof(msixcap))); 918c9b4e987SNeel Natu } 919c9b4e987SNeel Natu 92021368498SPeter Grehan static void 921cd942e0fSPeter Grehan msixcap_cfgwrite(struct pci_devinst *pi, int capoff, int offset, 922cd942e0fSPeter Grehan int bytes, uint32_t val) 923cd942e0fSPeter Grehan { 924cd942e0fSPeter Grehan uint16_t msgctrl, rwmask; 925d74fdc6aSMarcelo Araujo int off; 926cd942e0fSPeter Grehan 927cd942e0fSPeter Grehan off = offset - capoff; 928cd942e0fSPeter Grehan /* Message Control Register */ 929cd942e0fSPeter Grehan if (off == 2 && bytes == 2) { 930cd942e0fSPeter Grehan rwmask = PCIM_MSIXCTRL_MSIX_ENABLE | PCIM_MSIXCTRL_FUNCTION_MASK; 931cd942e0fSPeter Grehan msgctrl = pci_get_cfgdata16(pi, offset); 932cd942e0fSPeter Grehan msgctrl &= ~rwmask; 933cd942e0fSPeter Grehan msgctrl |= val & rwmask; 934cd942e0fSPeter Grehan val = msgctrl; 935cd942e0fSPeter Grehan 936cd942e0fSPeter Grehan pi->pi_msix.enabled = val & PCIM_MSIXCTRL_MSIX_ENABLE; 937c9b4e987SNeel Natu pi->pi_msix.function_mask = val & PCIM_MSIXCTRL_FUNCTION_MASK; 9383cbf3585SJohn Baldwin pci_lintr_update(pi); 939cd942e0fSPeter Grehan } 940cd942e0fSPeter Grehan 941cd942e0fSPeter Grehan CFGWRITE(pi, offset, val, bytes); 942cd942e0fSPeter Grehan } 943cd942e0fSPeter Grehan 94421368498SPeter Grehan static void 945366f6083SPeter Grehan msicap_cfgwrite(struct pci_devinst *pi, int capoff, int offset, 946366f6083SPeter Grehan int bytes, uint32_t val) 947366f6083SPeter Grehan { 948366f6083SPeter Grehan uint16_t msgctrl, rwmask, msgdata, mme; 949366f6083SPeter Grehan uint32_t addrlo; 950366f6083SPeter Grehan 951366f6083SPeter Grehan /* 952366f6083SPeter Grehan * If guest is writing to the message control register make sure 953366f6083SPeter Grehan * we do not overwrite read-only fields. 954366f6083SPeter Grehan */ 955366f6083SPeter Grehan if ((offset - capoff) == 2 && bytes == 2) { 956366f6083SPeter Grehan rwmask = PCIM_MSICTRL_MME_MASK | PCIM_MSICTRL_MSI_ENABLE; 957366f6083SPeter Grehan msgctrl = pci_get_cfgdata16(pi, offset); 958366f6083SPeter Grehan msgctrl &= ~rwmask; 959366f6083SPeter Grehan msgctrl |= val & rwmask; 960366f6083SPeter Grehan val = msgctrl; 9617840d1c4SJohn Baldwin } 9627840d1c4SJohn Baldwin CFGWRITE(pi, offset, val, bytes); 963366f6083SPeter Grehan 9647840d1c4SJohn Baldwin msgctrl = pci_get_cfgdata16(pi, capoff + 2); 965366f6083SPeter Grehan addrlo = pci_get_cfgdata32(pi, capoff + 4); 966366f6083SPeter Grehan if (msgctrl & PCIM_MSICTRL_64BIT) 967366f6083SPeter Grehan msgdata = pci_get_cfgdata16(pi, capoff + 12); 968366f6083SPeter Grehan else 969366f6083SPeter Grehan msgdata = pci_get_cfgdata16(pi, capoff + 8); 970366f6083SPeter Grehan 971366f6083SPeter Grehan mme = msgctrl & PCIM_MSICTRL_MME_MASK; 972366f6083SPeter Grehan pi->pi_msi.enabled = msgctrl & PCIM_MSICTRL_MSI_ENABLE ? 1 : 0; 973366f6083SPeter Grehan if (pi->pi_msi.enabled) { 9744f8be175SNeel Natu pi->pi_msi.addr = addrlo; 9754f8be175SNeel Natu pi->pi_msi.msg_data = msgdata; 9764f8be175SNeel Natu pi->pi_msi.maxmsgnum = 1 << (mme >> 4); 977366f6083SPeter Grehan } else { 9784f8be175SNeel Natu pi->pi_msi.maxmsgnum = 0; 979366f6083SPeter Grehan } 9803cbf3585SJohn Baldwin pci_lintr_update(pi); 981366f6083SPeter Grehan } 982366f6083SPeter Grehan 98374f80b23SNeel Natu void 98474f80b23SNeel Natu pciecap_cfgwrite(struct pci_devinst *pi, int capoff, int offset, 98574f80b23SNeel Natu int bytes, uint32_t val) 98674f80b23SNeel Natu { 98774f80b23SNeel Natu 98874f80b23SNeel Natu /* XXX don't write to the readonly parts */ 98974f80b23SNeel Natu CFGWRITE(pi, offset, val, bytes); 99074f80b23SNeel Natu } 99174f80b23SNeel Natu 99274f80b23SNeel Natu #define PCIECAP_VERSION 0x2 99374f80b23SNeel Natu int 99474f80b23SNeel Natu pci_emul_add_pciecap(struct pci_devinst *pi, int type) 99574f80b23SNeel Natu { 99674f80b23SNeel Natu int err; 99774f80b23SNeel Natu struct pciecap pciecap; 99874f80b23SNeel Natu 99974f80b23SNeel Natu bzero(&pciecap, sizeof(pciecap)); 100074f80b23SNeel Natu 1001129f93c5SChuck Tuffli /* 1002129f93c5SChuck Tuffli * Use the integrated endpoint type for endpoints on a root complex bus. 1003129f93c5SChuck Tuffli * 1004129f93c5SChuck Tuffli * NB: bhyve currently only supports a single PCI bus that is the root 1005129f93c5SChuck Tuffli * complex bus, so all endpoints are integrated. 1006129f93c5SChuck Tuffli */ 1007129f93c5SChuck Tuffli if ((type == PCIEM_TYPE_ENDPOINT) && (pi->pi_bus == 0)) 1008129f93c5SChuck Tuffli type = PCIEM_TYPE_ROOT_INT_EP; 1009129f93c5SChuck Tuffli 101074f80b23SNeel Natu pciecap.capid = PCIY_EXPRESS; 1011129f93c5SChuck Tuffli pciecap.pcie_capabilities = PCIECAP_VERSION | type; 1012129f93c5SChuck Tuffli if (type != PCIEM_TYPE_ROOT_INT_EP) { 101374f80b23SNeel Natu pciecap.link_capabilities = 0x411; /* gen1, x1 */ 101474f80b23SNeel Natu pciecap.link_status = 0x11; /* gen1, x1 */ 1015129f93c5SChuck Tuffli } 101674f80b23SNeel Natu 101774f80b23SNeel Natu err = pci_emul_add_capability(pi, (u_char *)&pciecap, sizeof(pciecap)); 101874f80b23SNeel Natu return (err); 101974f80b23SNeel Natu } 102074f80b23SNeel Natu 1021366f6083SPeter Grehan /* 1022366f6083SPeter Grehan * This function assumes that 'coff' is in the capabilities region of the 102321368498SPeter Grehan * config space. A capoff parameter of zero will force a search for the 102421368498SPeter Grehan * offset and type. 1025366f6083SPeter Grehan */ 102621368498SPeter Grehan void 102721368498SPeter Grehan pci_emul_capwrite(struct pci_devinst *pi, int offset, int bytes, uint32_t val, 102821368498SPeter Grehan uint8_t capoff, int capid) 1029366f6083SPeter Grehan { 103021368498SPeter Grehan uint8_t nextoff; 1031366f6083SPeter Grehan 1032366f6083SPeter Grehan /* Do not allow un-aligned writes */ 1033366f6083SPeter Grehan if ((offset & (bytes - 1)) != 0) 1034366f6083SPeter Grehan return; 1035366f6083SPeter Grehan 103621368498SPeter Grehan if (capoff == 0) { 1037366f6083SPeter Grehan /* Find the capability that we want to update */ 1038366f6083SPeter Grehan capoff = CAP_START_OFFSET; 1039366f6083SPeter Grehan while (1) { 1040366f6083SPeter Grehan nextoff = pci_get_cfgdata8(pi, capoff + 1); 1041a96b8b80SJohn Baldwin if (nextoff == 0) 1042a96b8b80SJohn Baldwin break; 1043366f6083SPeter Grehan if (offset >= capoff && offset < nextoff) 1044366f6083SPeter Grehan break; 1045366f6083SPeter Grehan 1046366f6083SPeter Grehan capoff = nextoff; 1047366f6083SPeter Grehan } 1048366f6083SPeter Grehan assert(offset >= capoff); 104921368498SPeter Grehan capid = pci_get_cfgdata8(pi, capoff); 105021368498SPeter Grehan } 1051366f6083SPeter Grehan 1052366f6083SPeter Grehan /* 10532a8d400aSPeter Grehan * Capability ID and Next Capability Pointer are readonly. 10542a8d400aSPeter Grehan * However, some o/s's do 4-byte writes that include these. 10552a8d400aSPeter Grehan * For this case, trim the write back to 2 bytes and adjust 10562a8d400aSPeter Grehan * the data. 1057366f6083SPeter Grehan */ 10582a8d400aSPeter Grehan if (offset == capoff || offset == capoff + 1) { 10592a8d400aSPeter Grehan if (offset == capoff && bytes == 4) { 10602a8d400aSPeter Grehan bytes = 2; 10612a8d400aSPeter Grehan offset += 2; 10622a8d400aSPeter Grehan val >>= 16; 10632a8d400aSPeter Grehan } else 1064366f6083SPeter Grehan return; 10652a8d400aSPeter Grehan } 1066366f6083SPeter Grehan 1067366f6083SPeter Grehan switch (capid) { 1068366f6083SPeter Grehan case PCIY_MSI: 1069366f6083SPeter Grehan msicap_cfgwrite(pi, capoff, offset, bytes, val); 1070366f6083SPeter Grehan break; 1071c9b4e987SNeel Natu case PCIY_MSIX: 1072c9b4e987SNeel Natu msixcap_cfgwrite(pi, capoff, offset, bytes, val); 1073c9b4e987SNeel Natu break; 107474f80b23SNeel Natu case PCIY_EXPRESS: 107574f80b23SNeel Natu pciecap_cfgwrite(pi, capoff, offset, bytes, val); 107674f80b23SNeel Natu break; 1077366f6083SPeter Grehan default: 1078366f6083SPeter Grehan break; 1079366f6083SPeter Grehan } 1080366f6083SPeter Grehan } 1081366f6083SPeter Grehan 1082366f6083SPeter Grehan static int 1083366f6083SPeter Grehan pci_emul_iscap(struct pci_devinst *pi, int offset) 1084366f6083SPeter Grehan { 1085366f6083SPeter Grehan uint16_t sts; 1086366f6083SPeter Grehan 1087366f6083SPeter Grehan sts = pci_get_cfgdata16(pi, PCIR_STATUS); 1088366f6083SPeter Grehan if ((sts & PCIM_STATUS_CAPPRESENT) != 0) { 1089a96b8b80SJohn Baldwin if (offset >= CAP_START_OFFSET && offset <= pi->pi_capend) 1090a96b8b80SJohn Baldwin return (1); 1091366f6083SPeter Grehan } 1092a96b8b80SJohn Baldwin return (0); 1093366f6083SPeter Grehan } 1094366f6083SPeter Grehan 10950ab13648SPeter Grehan static int 10960ab13648SPeter Grehan pci_emul_fallback_handler(struct vmctx *ctx, int vcpu, int dir, uint64_t addr, 10970ab13648SPeter Grehan int size, uint64_t *val, void *arg1, long arg2) 10980ab13648SPeter Grehan { 10990ab13648SPeter Grehan /* 11000ab13648SPeter Grehan * Ignore writes; return 0xff's for reads. The mem read code 11010ab13648SPeter Grehan * will take care of truncating to the correct size. 11020ab13648SPeter Grehan */ 11030ab13648SPeter Grehan if (dir == MEM_F_READ) { 11040ab13648SPeter Grehan *val = 0xffffffffffffffff; 11050ab13648SPeter Grehan } 11060ab13648SPeter Grehan 11070ab13648SPeter Grehan return (0); 11080ab13648SPeter Grehan } 11090ab13648SPeter Grehan 111012a6eb99SNeel Natu static int 111112a6eb99SNeel Natu pci_emul_ecfg_handler(struct vmctx *ctx, int vcpu, int dir, uint64_t addr, 111212a6eb99SNeel Natu int bytes, uint64_t *val, void *arg1, long arg2) 111312a6eb99SNeel Natu { 111412a6eb99SNeel Natu int bus, slot, func, coff, in; 111512a6eb99SNeel Natu 111612a6eb99SNeel Natu coff = addr & 0xfff; 111712a6eb99SNeel Natu func = (addr >> 12) & 0x7; 111812a6eb99SNeel Natu slot = (addr >> 15) & 0x1f; 111912a6eb99SNeel Natu bus = (addr >> 20) & 0xff; 112012a6eb99SNeel Natu in = (dir == MEM_F_READ); 112112a6eb99SNeel Natu if (in) 112212a6eb99SNeel Natu *val = ~0UL; 112312a6eb99SNeel Natu pci_cfgrw(ctx, vcpu, in, bus, slot, func, coff, bytes, (uint32_t *)val); 112412a6eb99SNeel Natu return (0); 112512a6eb99SNeel Natu } 112612a6eb99SNeel Natu 112712a6eb99SNeel Natu uint64_t 112812a6eb99SNeel Natu pci_ecfg_base(void) 112912a6eb99SNeel Natu { 113012a6eb99SNeel Natu 113112a6eb99SNeel Natu return (PCI_EMUL_ECFG_BASE); 113212a6eb99SNeel Natu } 113312a6eb99SNeel Natu 1134d84882caSNeel Natu #define BUSIO_ROUNDUP 32 1135d84882caSNeel Natu #define BUSMEM_ROUNDUP (1024 * 1024) 1136d84882caSNeel Natu 1137a38e2a64SPeter Grehan int 1138366f6083SPeter Grehan init_pci(struct vmctx *ctx) 1139366f6083SPeter Grehan { 1140621b5090SJohn Baldwin char node_name[sizeof("pci.XXX.XX.X")]; 114112a6eb99SNeel Natu struct mem_range mr; 1142366f6083SPeter Grehan struct pci_devemu *pde; 1143d84882caSNeel Natu struct businfo *bi; 1144d84882caSNeel Natu struct slotinfo *si; 11453cbf3585SJohn Baldwin struct funcinfo *fi; 1146621b5090SJohn Baldwin nvlist_t *nvl; 1147621b5090SJohn Baldwin const char *emul; 11489f08548dSNeel Natu size_t lowmem; 11499922872bSKonstantin Belousov uint64_t cpu_maxphysaddr, pci_emul_memresv64; 11509922872bSKonstantin Belousov u_int regs[4]; 11519922872bSKonstantin Belousov int bus, slot, func, error; 1152366f6083SPeter Grehan 1153366f6083SPeter Grehan pci_emul_iobase = PCI_EMUL_IOBASE; 11549f08548dSNeel Natu pci_emul_membase32 = vm_get_lowmem_limit(ctx); 11559922872bSKonstantin Belousov 11569922872bSKonstantin Belousov do_cpuid(0x80000008, regs); 11579922872bSKonstantin Belousov cpu_maxphysaddr = 1ULL << (regs[0] & 0xff); 11589922872bSKonstantin Belousov if (cpu_maxphysaddr > VM_MAXUSER_ADDRESS_LA48) 11599922872bSKonstantin Belousov cpu_maxphysaddr = VM_MAXUSER_ADDRESS_LA48; 11609922872bSKonstantin Belousov pci_emul_memresv64 = cpu_maxphysaddr / 4; 11619922872bSKonstantin Belousov /* 11629922872bSKonstantin Belousov * Max power of 2 that is less then 11639922872bSKonstantin Belousov * cpu_maxphysaddr - pci_emul_memresv64. 11649922872bSKonstantin Belousov */ 11659922872bSKonstantin Belousov pci_emul_membase64 = 1ULL << (flsl(cpu_maxphysaddr - 11669922872bSKonstantin Belousov pci_emul_memresv64) - 1); 11679922872bSKonstantin Belousov pci_emul_memlim64 = cpu_maxphysaddr; 1168366f6083SPeter Grehan 1169d84882caSNeel Natu for (bus = 0; bus < MAXBUSES; bus++) { 1170621b5090SJohn Baldwin snprintf(node_name, sizeof(node_name), "pci.%d", bus); 1171621b5090SJohn Baldwin nvl = find_config_node(node_name); 1172621b5090SJohn Baldwin if (nvl == NULL) 1173d84882caSNeel Natu continue; 1174621b5090SJohn Baldwin pci_businfo[bus] = calloc(1, sizeof(struct businfo)); 1175621b5090SJohn Baldwin bi = pci_businfo[bus]; 1176621b5090SJohn Baldwin 1177d84882caSNeel Natu /* 1178d84882caSNeel Natu * Keep track of the i/o and memory resources allocated to 1179d84882caSNeel Natu * this bus. 1180d84882caSNeel Natu */ 1181d84882caSNeel Natu bi->iobase = pci_emul_iobase; 1182d84882caSNeel Natu bi->membase32 = pci_emul_membase32; 1183d84882caSNeel Natu bi->membase64 = pci_emul_membase64; 1184d84882caSNeel Natu 118599d65389SNeel Natu for (slot = 0; slot < MAXSLOTS; slot++) { 1186d84882caSNeel Natu si = &bi->slotinfo[slot]; 118799d65389SNeel Natu for (func = 0; func < MAXFUNCS; func++) { 1188d84882caSNeel Natu fi = &si->si_funcs[func]; 1189621b5090SJohn Baldwin snprintf(node_name, sizeof(node_name), 1190621b5090SJohn Baldwin "pci.%d.%d.%d", bus, slot, func); 1191621b5090SJohn Baldwin nvl = find_config_node(node_name); 1192621b5090SJohn Baldwin if (nvl == NULL) 1193d84882caSNeel Natu continue; 1194621b5090SJohn Baldwin 1195621b5090SJohn Baldwin fi->fi_config = nvl; 1196621b5090SJohn Baldwin emul = get_config_value_node(nvl, "device"); 1197621b5090SJohn Baldwin if (emul == NULL) { 1198621b5090SJohn Baldwin EPRINTLN("pci slot %d:%d:%d: missing " 1199621b5090SJohn Baldwin "\"device\" value", bus, slot, func); 1200621b5090SJohn Baldwin return (EINVAL); 1201621b5090SJohn Baldwin } 1202621b5090SJohn Baldwin pde = pci_emul_finddev(emul); 1203621b5090SJohn Baldwin if (pde == NULL) { 1204621b5090SJohn Baldwin EPRINTLN("pci slot %d:%d:%d: unknown " 1205621b5090SJohn Baldwin "device \"%s\"", bus, slot, func, 1206621b5090SJohn Baldwin emul); 1207621b5090SJohn Baldwin return (EINVAL); 1208621b5090SJohn Baldwin } 1209621b5090SJohn Baldwin if (pde->pe_alias != NULL) { 1210621b5090SJohn Baldwin EPRINTLN("pci slot %d:%d:%d: legacy " 1211621b5090SJohn Baldwin "device \"%s\", use \"%s\" instead", 1212621b5090SJohn Baldwin bus, slot, func, emul, 1213621b5090SJohn Baldwin pde->pe_alias); 1214621b5090SJohn Baldwin return (EINVAL); 1215621b5090SJohn Baldwin } 1216621b5090SJohn Baldwin fi->fi_pde = pde; 1217d84882caSNeel Natu error = pci_emul_init(ctx, pde, bus, slot, 1218d84882caSNeel Natu func, fi); 1219a38e2a64SPeter Grehan if (error) 1220a38e2a64SPeter Grehan return (error); 1221366f6083SPeter Grehan } 1222366f6083SPeter Grehan } 1223d84882caSNeel Natu 1224d84882caSNeel Natu /* 1225d84882caSNeel Natu * Add some slop to the I/O and memory resources decoded by 1226d84882caSNeel Natu * this bus to give a guest some flexibility if it wants to 1227d84882caSNeel Natu * reprogram the BARs. 1228d84882caSNeel Natu */ 1229d84882caSNeel Natu pci_emul_iobase += BUSIO_ROUNDUP; 1230d84882caSNeel Natu pci_emul_iobase = roundup2(pci_emul_iobase, BUSIO_ROUNDUP); 1231d84882caSNeel Natu bi->iolimit = pci_emul_iobase; 1232d84882caSNeel Natu 1233d84882caSNeel Natu pci_emul_membase32 += BUSMEM_ROUNDUP; 1234d84882caSNeel Natu pci_emul_membase32 = roundup2(pci_emul_membase32, 1235d84882caSNeel Natu BUSMEM_ROUNDUP); 1236d84882caSNeel Natu bi->memlimit32 = pci_emul_membase32; 1237d84882caSNeel Natu 1238d84882caSNeel Natu pci_emul_membase64 += BUSMEM_ROUNDUP; 1239d84882caSNeel Natu pci_emul_membase64 = roundup2(pci_emul_membase64, 1240d84882caSNeel Natu BUSMEM_ROUNDUP); 1241d84882caSNeel Natu bi->memlimit64 = pci_emul_membase64; 1242366f6083SPeter Grehan } 12430038ee98SPeter Grehan 12440038ee98SPeter Grehan /* 1245b3e9732aSJohn Baldwin * PCI backends are initialized before routing INTx interrupts 1246b3e9732aSJohn Baldwin * so that LPC devices are able to reserve ISA IRQs before 1247b3e9732aSJohn Baldwin * routing PIRQ pins. 1248b3e9732aSJohn Baldwin */ 1249b3e9732aSJohn Baldwin for (bus = 0; bus < MAXBUSES; bus++) { 1250b3e9732aSJohn Baldwin if ((bi = pci_businfo[bus]) == NULL) 1251b3e9732aSJohn Baldwin continue; 1252b3e9732aSJohn Baldwin 1253b3e9732aSJohn Baldwin for (slot = 0; slot < MAXSLOTS; slot++) { 1254b3e9732aSJohn Baldwin si = &bi->slotinfo[slot]; 1255b3e9732aSJohn Baldwin for (func = 0; func < MAXFUNCS; func++) { 1256b3e9732aSJohn Baldwin fi = &si->si_funcs[func]; 1257b3e9732aSJohn Baldwin if (fi->fi_devi == NULL) 1258b3e9732aSJohn Baldwin continue; 1259b3e9732aSJohn Baldwin pci_lintr_route(fi->fi_devi); 1260b3e9732aSJohn Baldwin } 1261b3e9732aSJohn Baldwin } 1262b3e9732aSJohn Baldwin } 1263b3e9732aSJohn Baldwin lpc_pirq_routed(); 1264b3e9732aSJohn Baldwin 1265b3e9732aSJohn Baldwin /* 12669f08548dSNeel Natu * The guest physical memory map looks like the following: 12679f08548dSNeel Natu * [0, lowmem) guest system memory 12689f08548dSNeel Natu * [lowmem, lowmem_limit) memory hole (may be absent) 126912a6eb99SNeel Natu * [lowmem_limit, 0xE0000000) PCI hole (32-bit BAR allocation) 127012a6eb99SNeel Natu * [0xE0000000, 0xF0000000) PCI extended config window 127112a6eb99SNeel Natu * [0xF0000000, 4GB) LAPIC, IOAPIC, HPET, firmware 12729f08548dSNeel Natu * [4GB, 4GB + highmem) 127312a6eb99SNeel Natu */ 127412a6eb99SNeel Natu 127512a6eb99SNeel Natu /* 12769f08548dSNeel Natu * Accesses to memory addresses that are not allocated to system 12779f08548dSNeel Natu * memory or PCI devices return 0xff's. 12780ab13648SPeter Grehan */ 1279be679db4SNeel Natu lowmem = vm_get_lowmem_size(ctx); 128012a6eb99SNeel Natu bzero(&mr, sizeof(struct mem_range)); 128112a6eb99SNeel Natu mr.name = "PCI hole"; 128212a6eb99SNeel Natu mr.flags = MEM_F_RW | MEM_F_IMMUTABLE; 128312a6eb99SNeel Natu mr.base = lowmem; 128412a6eb99SNeel Natu mr.size = (4ULL * 1024 * 1024 * 1024) - lowmem; 128512a6eb99SNeel Natu mr.handler = pci_emul_fallback_handler; 128612a6eb99SNeel Natu error = register_mem_fallback(&mr); 128712a6eb99SNeel Natu assert(error == 0); 12889f08548dSNeel Natu 128912a6eb99SNeel Natu /* PCI extended config space */ 129012a6eb99SNeel Natu bzero(&mr, sizeof(struct mem_range)); 129112a6eb99SNeel Natu mr.name = "PCI ECFG"; 129212a6eb99SNeel Natu mr.flags = MEM_F_RW | MEM_F_IMMUTABLE; 129312a6eb99SNeel Natu mr.base = PCI_EMUL_ECFG_BASE; 129412a6eb99SNeel Natu mr.size = PCI_EMUL_ECFG_SIZE; 129512a6eb99SNeel Natu mr.handler = pci_emul_ecfg_handler; 129612a6eb99SNeel Natu error = register_mem(&mr); 12970ab13648SPeter Grehan assert(error == 0); 1298a38e2a64SPeter Grehan 1299a38e2a64SPeter Grehan return (0); 1300366f6083SPeter Grehan } 1301366f6083SPeter Grehan 13023cbf3585SJohn Baldwin static void 1303b3e9732aSJohn Baldwin pci_apic_prt_entry(int bus, int slot, int pin, int pirq_pin, int ioapic_irq, 1304b3e9732aSJohn Baldwin void *arg) 13053cbf3585SJohn Baldwin { 13063cbf3585SJohn Baldwin 1307b3e9732aSJohn Baldwin dsdt_line(" Package ()"); 13083cbf3585SJohn Baldwin dsdt_line(" {"); 13093cbf3585SJohn Baldwin dsdt_line(" 0x%X,", slot << 16 | 0xffff); 13103cbf3585SJohn Baldwin dsdt_line(" 0x%02X,", pin - 1); 13113cbf3585SJohn Baldwin dsdt_line(" Zero,"); 13123cbf3585SJohn Baldwin dsdt_line(" 0x%X", ioapic_irq); 1313b3e9732aSJohn Baldwin dsdt_line(" },"); 1314b3e9732aSJohn Baldwin } 1315b3e9732aSJohn Baldwin 1316b3e9732aSJohn Baldwin static void 1317b3e9732aSJohn Baldwin pci_pirq_prt_entry(int bus, int slot, int pin, int pirq_pin, int ioapic_irq, 1318b3e9732aSJohn Baldwin void *arg) 1319b3e9732aSJohn Baldwin { 1320b3e9732aSJohn Baldwin char *name; 1321b3e9732aSJohn Baldwin 1322b3e9732aSJohn Baldwin name = lpc_pirq_name(pirq_pin); 1323b3e9732aSJohn Baldwin if (name == NULL) 1324b3e9732aSJohn Baldwin return; 1325b3e9732aSJohn Baldwin dsdt_line(" Package ()"); 1326b3e9732aSJohn Baldwin dsdt_line(" {"); 1327b3e9732aSJohn Baldwin dsdt_line(" 0x%X,", slot << 16 | 0xffff); 1328b3e9732aSJohn Baldwin dsdt_line(" 0x%02X,", pin - 1); 1329b3e9732aSJohn Baldwin dsdt_line(" %s,", name); 1330b3e9732aSJohn Baldwin dsdt_line(" 0x00"); 1331b3e9732aSJohn Baldwin dsdt_line(" },"); 1332b3e9732aSJohn Baldwin free(name); 13333cbf3585SJohn Baldwin } 13343cbf3585SJohn Baldwin 1335d84882caSNeel Natu /* 1336d84882caSNeel Natu * A bhyve virtual machine has a flat PCI hierarchy with a root port 1337d84882caSNeel Natu * corresponding to each PCI bus. 1338d84882caSNeel Natu */ 1339d84882caSNeel Natu static void 1340d84882caSNeel Natu pci_bus_write_dsdt(int bus) 1341e6c8bc29SJohn Baldwin { 1342d84882caSNeel Natu struct businfo *bi; 1343d84882caSNeel Natu struct slotinfo *si; 1344e6c8bc29SJohn Baldwin struct pci_devinst *pi; 1345b3e9732aSJohn Baldwin int count, func, slot; 1346e6c8bc29SJohn Baldwin 1347d84882caSNeel Natu /* 1348d84882caSNeel Natu * If there are no devices on this 'bus' then just return. 1349d84882caSNeel Natu */ 1350d84882caSNeel Natu if ((bi = pci_businfo[bus]) == NULL) { 1351d84882caSNeel Natu /* 1352d84882caSNeel Natu * Bus 0 is special because it decodes the I/O ports used 1353d84882caSNeel Natu * for PCI config space access even if there are no devices 1354d84882caSNeel Natu * on it. 1355d84882caSNeel Natu */ 1356d84882caSNeel Natu if (bus != 0) 1357d84882caSNeel Natu return; 1358d84882caSNeel Natu } 1359d84882caSNeel Natu 1360d84882caSNeel Natu dsdt_line(" Device (PC%02X)", bus); 1361e6c8bc29SJohn Baldwin dsdt_line(" {"); 1362e6c8bc29SJohn Baldwin dsdt_line(" Name (_HID, EisaId (\"PNP0A03\"))"); 1363d84882caSNeel Natu 1364d84882caSNeel Natu dsdt_line(" Method (_BBN, 0, NotSerialized)"); 1365d84882caSNeel Natu dsdt_line(" {"); 1366d84882caSNeel Natu dsdt_line(" Return (0x%08X)", bus); 1367d84882caSNeel Natu dsdt_line(" }"); 1368e6c8bc29SJohn Baldwin dsdt_line(" Name (_CRS, ResourceTemplate ()"); 1369e6c8bc29SJohn Baldwin dsdt_line(" {"); 1370e6c8bc29SJohn Baldwin dsdt_line(" WordBusNumber (ResourceProducer, MinFixed, " 1371e6c8bc29SJohn Baldwin "MaxFixed, PosDecode,"); 1372e6c8bc29SJohn Baldwin dsdt_line(" 0x0000, // Granularity"); 1373d84882caSNeel Natu dsdt_line(" 0x%04X, // Range Minimum", bus); 1374d84882caSNeel Natu dsdt_line(" 0x%04X, // Range Maximum", bus); 1375e6c8bc29SJohn Baldwin dsdt_line(" 0x0000, // Translation Offset"); 1376d84882caSNeel Natu dsdt_line(" 0x0001, // Length"); 1377e6c8bc29SJohn Baldwin dsdt_line(" ,, )"); 1378d84882caSNeel Natu 1379d84882caSNeel Natu if (bus == 0) { 1380e6c8bc29SJohn Baldwin dsdt_indent(3); 1381e6c8bc29SJohn Baldwin dsdt_fixed_ioport(0xCF8, 8); 1382e6c8bc29SJohn Baldwin dsdt_unindent(3); 1383d84882caSNeel Natu 1384e6c8bc29SJohn Baldwin dsdt_line(" WordIO (ResourceProducer, MinFixed, MaxFixed, " 1385e6c8bc29SJohn Baldwin "PosDecode, EntireRange,"); 1386e6c8bc29SJohn Baldwin dsdt_line(" 0x0000, // Granularity"); 1387e6c8bc29SJohn Baldwin dsdt_line(" 0x0000, // Range Minimum"); 1388e6c8bc29SJohn Baldwin dsdt_line(" 0x0CF7, // Range Maximum"); 1389e6c8bc29SJohn Baldwin dsdt_line(" 0x0000, // Translation Offset"); 1390e6c8bc29SJohn Baldwin dsdt_line(" 0x0CF8, // Length"); 1391e6c8bc29SJohn Baldwin dsdt_line(" ,, , TypeStatic)"); 1392d84882caSNeel Natu 1393e6c8bc29SJohn Baldwin dsdt_line(" WordIO (ResourceProducer, MinFixed, MaxFixed, " 1394e6c8bc29SJohn Baldwin "PosDecode, EntireRange,"); 1395e6c8bc29SJohn Baldwin dsdt_line(" 0x0000, // Granularity"); 1396e6c8bc29SJohn Baldwin dsdt_line(" 0x0D00, // Range Minimum"); 1397d84882caSNeel Natu dsdt_line(" 0x%04X, // Range Maximum", 1398d84882caSNeel Natu PCI_EMUL_IOBASE - 1); 1399e6c8bc29SJohn Baldwin dsdt_line(" 0x0000, // Translation Offset"); 1400d84882caSNeel Natu dsdt_line(" 0x%04X, // Length", 1401d84882caSNeel Natu PCI_EMUL_IOBASE - 0x0D00); 1402e6c8bc29SJohn Baldwin dsdt_line(" ,, , TypeStatic)"); 1403d84882caSNeel Natu 1404d84882caSNeel Natu if (bi == NULL) { 1405d84882caSNeel Natu dsdt_line(" })"); 1406d84882caSNeel Natu goto done; 1407d84882caSNeel Natu } 1408d84882caSNeel Natu } 1409d84882caSNeel Natu assert(bi != NULL); 1410d84882caSNeel Natu 1411d84882caSNeel Natu /* i/o window */ 1412d84882caSNeel Natu dsdt_line(" WordIO (ResourceProducer, MinFixed, MaxFixed, " 1413d84882caSNeel Natu "PosDecode, EntireRange,"); 1414d84882caSNeel Natu dsdt_line(" 0x0000, // Granularity"); 1415d84882caSNeel Natu dsdt_line(" 0x%04X, // Range Minimum", bi->iobase); 1416d84882caSNeel Natu dsdt_line(" 0x%04X, // Range Maximum", 1417d84882caSNeel Natu bi->iolimit - 1); 1418d84882caSNeel Natu dsdt_line(" 0x0000, // Translation Offset"); 1419d84882caSNeel Natu dsdt_line(" 0x%04X, // Length", 1420d84882caSNeel Natu bi->iolimit - bi->iobase); 1421d84882caSNeel Natu dsdt_line(" ,, , TypeStatic)"); 1422d84882caSNeel Natu 1423d84882caSNeel Natu /* mmio window (32-bit) */ 1424e6c8bc29SJohn Baldwin dsdt_line(" DWordMemory (ResourceProducer, PosDecode, " 1425e6c8bc29SJohn Baldwin "MinFixed, MaxFixed, NonCacheable, ReadWrite,"); 1426e6c8bc29SJohn Baldwin dsdt_line(" 0x00000000, // Granularity"); 1427d84882caSNeel Natu dsdt_line(" 0x%08X, // Range Minimum\n", bi->membase32); 1428e6c8bc29SJohn Baldwin dsdt_line(" 0x%08X, // Range Maximum\n", 1429d84882caSNeel Natu bi->memlimit32 - 1); 1430e6c8bc29SJohn Baldwin dsdt_line(" 0x00000000, // Translation Offset"); 1431d84882caSNeel Natu dsdt_line(" 0x%08X, // Length\n", 1432d84882caSNeel Natu bi->memlimit32 - bi->membase32); 1433e6c8bc29SJohn Baldwin dsdt_line(" ,, , AddressRangeMemory, TypeStatic)"); 1434d84882caSNeel Natu 1435d84882caSNeel Natu /* mmio window (64-bit) */ 1436e6c8bc29SJohn Baldwin dsdt_line(" QWordMemory (ResourceProducer, PosDecode, " 1437e6c8bc29SJohn Baldwin "MinFixed, MaxFixed, NonCacheable, ReadWrite,"); 1438e6c8bc29SJohn Baldwin dsdt_line(" 0x0000000000000000, // Granularity"); 1439d84882caSNeel Natu dsdt_line(" 0x%016lX, // Range Minimum\n", bi->membase64); 1440e6c8bc29SJohn Baldwin dsdt_line(" 0x%016lX, // Range Maximum\n", 1441d84882caSNeel Natu bi->memlimit64 - 1); 1442e6c8bc29SJohn Baldwin dsdt_line(" 0x0000000000000000, // Translation Offset"); 1443e6c8bc29SJohn Baldwin dsdt_line(" 0x%016lX, // Length\n", 1444d84882caSNeel Natu bi->memlimit64 - bi->membase64); 1445e6c8bc29SJohn Baldwin dsdt_line(" ,, , AddressRangeMemory, TypeStatic)"); 1446e6c8bc29SJohn Baldwin dsdt_line(" })"); 1447d84882caSNeel Natu 1448d84882caSNeel Natu count = pci_count_lintr(bus); 14493cbf3585SJohn Baldwin if (count != 0) { 14503cbf3585SJohn Baldwin dsdt_indent(2); 1451b3e9732aSJohn Baldwin dsdt_line("Name (PPRT, Package ()"); 14523cbf3585SJohn Baldwin dsdt_line("{"); 1453b3e9732aSJohn Baldwin pci_walk_lintr(bus, pci_pirq_prt_entry, NULL); 14543cbf3585SJohn Baldwin dsdt_line("})"); 1455b3e9732aSJohn Baldwin dsdt_line("Name (APRT, Package ()"); 1456b3e9732aSJohn Baldwin dsdt_line("{"); 1457b3e9732aSJohn Baldwin pci_walk_lintr(bus, pci_apic_prt_entry, NULL); 1458b3e9732aSJohn Baldwin dsdt_line("})"); 1459b3e9732aSJohn Baldwin dsdt_line("Method (_PRT, 0, NotSerialized)"); 1460b3e9732aSJohn Baldwin dsdt_line("{"); 1461b3e9732aSJohn Baldwin dsdt_line(" If (PICM)"); 1462b3e9732aSJohn Baldwin dsdt_line(" {"); 1463b3e9732aSJohn Baldwin dsdt_line(" Return (APRT)"); 1464b3e9732aSJohn Baldwin dsdt_line(" }"); 1465b3e9732aSJohn Baldwin dsdt_line(" Else"); 1466b3e9732aSJohn Baldwin dsdt_line(" {"); 1467b3e9732aSJohn Baldwin dsdt_line(" Return (PPRT)"); 1468b3e9732aSJohn Baldwin dsdt_line(" }"); 1469b3e9732aSJohn Baldwin dsdt_line("}"); 14703cbf3585SJohn Baldwin dsdt_unindent(2); 14713cbf3585SJohn Baldwin } 1472e6c8bc29SJohn Baldwin 1473e6c8bc29SJohn Baldwin dsdt_indent(2); 1474e6c8bc29SJohn Baldwin for (slot = 0; slot < MAXSLOTS; slot++) { 1475d84882caSNeel Natu si = &bi->slotinfo[slot]; 1476e6c8bc29SJohn Baldwin for (func = 0; func < MAXFUNCS; func++) { 1477d84882caSNeel Natu pi = si->si_funcs[func].fi_devi; 1478e6c8bc29SJohn Baldwin if (pi != NULL && pi->pi_d->pe_write_dsdt != NULL) 1479e6c8bc29SJohn Baldwin pi->pi_d->pe_write_dsdt(pi); 1480e6c8bc29SJohn Baldwin } 1481e6c8bc29SJohn Baldwin } 1482e6c8bc29SJohn Baldwin dsdt_unindent(2); 1483d84882caSNeel Natu done: 1484e6c8bc29SJohn Baldwin dsdt_line(" }"); 1485e6c8bc29SJohn Baldwin } 1486e6c8bc29SJohn Baldwin 1487d84882caSNeel Natu void 1488d84882caSNeel Natu pci_write_dsdt(void) 1489d84882caSNeel Natu { 1490d84882caSNeel Natu int bus; 1491d84882caSNeel Natu 1492b3e9732aSJohn Baldwin dsdt_indent(1); 1493b3e9732aSJohn Baldwin dsdt_line("Name (PICM, 0x00)"); 1494b3e9732aSJohn Baldwin dsdt_line("Method (_PIC, 1, NotSerialized)"); 1495b3e9732aSJohn Baldwin dsdt_line("{"); 1496b3e9732aSJohn Baldwin dsdt_line(" Store (Arg0, PICM)"); 1497b3e9732aSJohn Baldwin dsdt_line("}"); 1498b3e9732aSJohn Baldwin dsdt_line(""); 1499b3e9732aSJohn Baldwin dsdt_line("Scope (_SB)"); 1500b3e9732aSJohn Baldwin dsdt_line("{"); 1501d84882caSNeel Natu for (bus = 0; bus < MAXBUSES; bus++) 1502d84882caSNeel Natu pci_bus_write_dsdt(bus); 1503b3e9732aSJohn Baldwin dsdt_line("}"); 1504b3e9732aSJohn Baldwin dsdt_unindent(1); 1505d84882caSNeel Natu } 1506d84882caSNeel Natu 1507366f6083SPeter Grehan int 1508b100acf2SNeel Natu pci_bus_configured(int bus) 1509b100acf2SNeel Natu { 1510b100acf2SNeel Natu assert(bus >= 0 && bus < MAXBUSES); 1511b100acf2SNeel Natu return (pci_businfo[bus] != NULL); 1512b100acf2SNeel Natu } 1513b100acf2SNeel Natu 1514b100acf2SNeel Natu int 1515366f6083SPeter Grehan pci_msi_enabled(struct pci_devinst *pi) 1516366f6083SPeter Grehan { 1517366f6083SPeter Grehan return (pi->pi_msi.enabled); 1518366f6083SPeter Grehan } 1519366f6083SPeter Grehan 1520366f6083SPeter Grehan int 15214f8be175SNeel Natu pci_msi_maxmsgnum(struct pci_devinst *pi) 1522366f6083SPeter Grehan { 1523366f6083SPeter Grehan if (pi->pi_msi.enabled) 15244f8be175SNeel Natu return (pi->pi_msi.maxmsgnum); 1525366f6083SPeter Grehan else 1526366f6083SPeter Grehan return (0); 1527366f6083SPeter Grehan } 1528366f6083SPeter Grehan 1529c9b4e987SNeel Natu int 1530c9b4e987SNeel Natu pci_msix_enabled(struct pci_devinst *pi) 1531c9b4e987SNeel Natu { 1532c9b4e987SNeel Natu 1533c9b4e987SNeel Natu return (pi->pi_msix.enabled && !pi->pi_msi.enabled); 1534c9b4e987SNeel Natu } 1535c9b4e987SNeel Natu 1536c9b4e987SNeel Natu void 1537c9b4e987SNeel Natu pci_generate_msix(struct pci_devinst *pi, int index) 1538c9b4e987SNeel Natu { 1539c9b4e987SNeel Natu struct msix_table_entry *mte; 1540c9b4e987SNeel Natu 1541c9b4e987SNeel Natu if (!pci_msix_enabled(pi)) 1542c9b4e987SNeel Natu return; 1543c9b4e987SNeel Natu 1544c9b4e987SNeel Natu if (pi->pi_msix.function_mask) 1545c9b4e987SNeel Natu return; 1546c9b4e987SNeel Natu 1547c9b4e987SNeel Natu if (index >= pi->pi_msix.table_count) 1548c9b4e987SNeel Natu return; 1549c9b4e987SNeel Natu 1550c9b4e987SNeel Natu mte = &pi->pi_msix.table[index]; 1551c9b4e987SNeel Natu if ((mte->vector_control & PCIM_MSIX_VCTRL_MASK) == 0) { 1552c9b4e987SNeel Natu /* XXX Set PBA bit if interrupt is disabled */ 15534f8be175SNeel Natu vm_lapic_msi(pi->pi_vmctx, mte->addr, mte->msg_data); 1554c9b4e987SNeel Natu } 1555c9b4e987SNeel Natu } 1556c9b4e987SNeel Natu 1557366f6083SPeter Grehan void 15584f8be175SNeel Natu pci_generate_msi(struct pci_devinst *pi, int index) 1559366f6083SPeter Grehan { 1560366f6083SPeter Grehan 15614f8be175SNeel Natu if (pci_msi_enabled(pi) && index < pci_msi_maxmsgnum(pi)) { 15624f8be175SNeel Natu vm_lapic_msi(pi->pi_vmctx, pi->pi_msi.addr, 15634f8be175SNeel Natu pi->pi_msi.msg_data + index); 1564366f6083SPeter Grehan } 1565366f6083SPeter Grehan } 1566366f6083SPeter Grehan 15673cbf3585SJohn Baldwin static bool 15683cbf3585SJohn Baldwin pci_lintr_permitted(struct pci_devinst *pi) 15690038ee98SPeter Grehan { 15703cbf3585SJohn Baldwin uint16_t cmd; 15710038ee98SPeter Grehan 15723cbf3585SJohn Baldwin cmd = pci_get_cfgdata16(pi, PCIR_COMMAND); 15733cbf3585SJohn Baldwin return (!(pi->pi_msi.enabled || pi->pi_msix.enabled || 15743cbf3585SJohn Baldwin (cmd & PCIM_CMD_INTxDIS))); 15753cbf3585SJohn Baldwin } 15763cbf3585SJohn Baldwin 1577b3e9732aSJohn Baldwin void 15783cbf3585SJohn Baldwin pci_lintr_request(struct pci_devinst *pi) 15793cbf3585SJohn Baldwin { 1580d84882caSNeel Natu struct businfo *bi; 15813cbf3585SJohn Baldwin struct slotinfo *si; 1582b3e9732aSJohn Baldwin int bestpin, bestcount, pin; 15833cbf3585SJohn Baldwin 1584d84882caSNeel Natu bi = pci_businfo[pi->pi_bus]; 1585d84882caSNeel Natu assert(bi != NULL); 1586d84882caSNeel Natu 15873cbf3585SJohn Baldwin /* 1588b3e9732aSJohn Baldwin * Just allocate a pin from our slot. The pin will be 1589b3e9732aSJohn Baldwin * assigned IRQs later when interrupts are routed. 15903cbf3585SJohn Baldwin */ 1591d84882caSNeel Natu si = &bi->slotinfo[pi->pi_slot]; 15923cbf3585SJohn Baldwin bestpin = 0; 15933cbf3585SJohn Baldwin bestcount = si->si_intpins[0].ii_count; 15943cbf3585SJohn Baldwin for (pin = 1; pin < 4; pin++) { 15953cbf3585SJohn Baldwin if (si->si_intpins[pin].ii_count < bestcount) { 15963cbf3585SJohn Baldwin bestpin = pin; 15973cbf3585SJohn Baldwin bestcount = si->si_intpins[pin].ii_count; 15983cbf3585SJohn Baldwin } 15993cbf3585SJohn Baldwin } 16003cbf3585SJohn Baldwin 16013cbf3585SJohn Baldwin si->si_intpins[bestpin].ii_count++; 16023cbf3585SJohn Baldwin pi->pi_lintr.pin = bestpin + 1; 16033cbf3585SJohn Baldwin pci_set_cfgdata8(pi, PCIR_INTPIN, bestpin + 1); 1604b3e9732aSJohn Baldwin } 1605b3e9732aSJohn Baldwin 1606b3e9732aSJohn Baldwin static void 1607b3e9732aSJohn Baldwin pci_lintr_route(struct pci_devinst *pi) 1608b3e9732aSJohn Baldwin { 1609b3e9732aSJohn Baldwin struct businfo *bi; 1610b3e9732aSJohn Baldwin struct intxinfo *ii; 1611b3e9732aSJohn Baldwin 1612b3e9732aSJohn Baldwin if (pi->pi_lintr.pin == 0) 1613b3e9732aSJohn Baldwin return; 1614b3e9732aSJohn Baldwin 1615b3e9732aSJohn Baldwin bi = pci_businfo[pi->pi_bus]; 1616b3e9732aSJohn Baldwin assert(bi != NULL); 1617b3e9732aSJohn Baldwin ii = &bi->slotinfo[pi->pi_slot].si_intpins[pi->pi_lintr.pin - 1]; 1618b3e9732aSJohn Baldwin 1619b3e9732aSJohn Baldwin /* 1620b3e9732aSJohn Baldwin * Attempt to allocate an I/O APIC pin for this intpin if one 1621b3e9732aSJohn Baldwin * is not yet assigned. 1622b3e9732aSJohn Baldwin */ 1623b3e9732aSJohn Baldwin if (ii->ii_ioapic_irq == 0) 16241b4496d0SAlexander Motin ii->ii_ioapic_irq = ioapic_pci_alloc_irq(pi); 1625b3e9732aSJohn Baldwin assert(ii->ii_ioapic_irq > 0); 1626b3e9732aSJohn Baldwin 1627b3e9732aSJohn Baldwin /* 1628b3e9732aSJohn Baldwin * Attempt to allocate a PIRQ pin for this intpin if one is 1629b3e9732aSJohn Baldwin * not yet assigned. 1630b3e9732aSJohn Baldwin */ 1631b3e9732aSJohn Baldwin if (ii->ii_pirq_pin == 0) 16321b4496d0SAlexander Motin ii->ii_pirq_pin = pirq_alloc_pin(pi); 1633b3e9732aSJohn Baldwin assert(ii->ii_pirq_pin > 0); 1634b3e9732aSJohn Baldwin 1635b3e9732aSJohn Baldwin pi->pi_lintr.ioapic_irq = ii->ii_ioapic_irq; 1636b3e9732aSJohn Baldwin pi->pi_lintr.pirq_pin = ii->ii_pirq_pin; 1637b3e9732aSJohn Baldwin pci_set_cfgdata8(pi, PCIR_INTLINE, pirq_irq(ii->ii_pirq_pin)); 16380038ee98SPeter Grehan } 16390038ee98SPeter Grehan 16400038ee98SPeter Grehan void 16410038ee98SPeter Grehan pci_lintr_assert(struct pci_devinst *pi) 16420038ee98SPeter Grehan { 16430038ee98SPeter Grehan 16443cbf3585SJohn Baldwin assert(pi->pi_lintr.pin > 0); 1645ac7304a7SNeel Natu 16463cbf3585SJohn Baldwin pthread_mutex_lock(&pi->pi_lintr.lock); 16473cbf3585SJohn Baldwin if (pi->pi_lintr.state == IDLE) { 16483cbf3585SJohn Baldwin if (pci_lintr_permitted(pi)) { 16493cbf3585SJohn Baldwin pi->pi_lintr.state = ASSERTED; 1650b3e9732aSJohn Baldwin pci_irq_assert(pi); 16513cbf3585SJohn Baldwin } else 16523cbf3585SJohn Baldwin pi->pi_lintr.state = PENDING; 16530038ee98SPeter Grehan } 16543cbf3585SJohn Baldwin pthread_mutex_unlock(&pi->pi_lintr.lock); 1655ac7304a7SNeel Natu } 16560038ee98SPeter Grehan 16570038ee98SPeter Grehan void 16580038ee98SPeter Grehan pci_lintr_deassert(struct pci_devinst *pi) 16590038ee98SPeter Grehan { 16600038ee98SPeter Grehan 16613cbf3585SJohn Baldwin assert(pi->pi_lintr.pin > 0); 1662ac7304a7SNeel Natu 16633cbf3585SJohn Baldwin pthread_mutex_lock(&pi->pi_lintr.lock); 16643cbf3585SJohn Baldwin if (pi->pi_lintr.state == ASSERTED) { 16653cbf3585SJohn Baldwin pi->pi_lintr.state = IDLE; 1666b3e9732aSJohn Baldwin pci_irq_deassert(pi); 16673cbf3585SJohn Baldwin } else if (pi->pi_lintr.state == PENDING) 16683cbf3585SJohn Baldwin pi->pi_lintr.state = IDLE; 16693cbf3585SJohn Baldwin pthread_mutex_unlock(&pi->pi_lintr.lock); 16703cbf3585SJohn Baldwin } 16713cbf3585SJohn Baldwin 16723cbf3585SJohn Baldwin static void 16733cbf3585SJohn Baldwin pci_lintr_update(struct pci_devinst *pi) 16743cbf3585SJohn Baldwin { 16753cbf3585SJohn Baldwin 16763cbf3585SJohn Baldwin pthread_mutex_lock(&pi->pi_lintr.lock); 16773cbf3585SJohn Baldwin if (pi->pi_lintr.state == ASSERTED && !pci_lintr_permitted(pi)) { 1678b3e9732aSJohn Baldwin pci_irq_deassert(pi); 16793cbf3585SJohn Baldwin pi->pi_lintr.state = PENDING; 16803cbf3585SJohn Baldwin } else if (pi->pi_lintr.state == PENDING && pci_lintr_permitted(pi)) { 16813cbf3585SJohn Baldwin pi->pi_lintr.state = ASSERTED; 1682b3e9732aSJohn Baldwin pci_irq_assert(pi); 16833cbf3585SJohn Baldwin } 16843cbf3585SJohn Baldwin pthread_mutex_unlock(&pi->pi_lintr.lock); 16853cbf3585SJohn Baldwin } 16863cbf3585SJohn Baldwin 16873cbf3585SJohn Baldwin int 1688d84882caSNeel Natu pci_count_lintr(int bus) 16893cbf3585SJohn Baldwin { 16903cbf3585SJohn Baldwin int count, slot, pin; 1691d84882caSNeel Natu struct slotinfo *slotinfo; 16923cbf3585SJohn Baldwin 16933cbf3585SJohn Baldwin count = 0; 1694d84882caSNeel Natu if (pci_businfo[bus] != NULL) { 16953cbf3585SJohn Baldwin for (slot = 0; slot < MAXSLOTS; slot++) { 1696d84882caSNeel Natu slotinfo = &pci_businfo[bus]->slotinfo[slot]; 16973cbf3585SJohn Baldwin for (pin = 0; pin < 4; pin++) { 1698d84882caSNeel Natu if (slotinfo->si_intpins[pin].ii_count != 0) 16993cbf3585SJohn Baldwin count++; 17003cbf3585SJohn Baldwin } 17013cbf3585SJohn Baldwin } 1702d84882caSNeel Natu } 17033cbf3585SJohn Baldwin return (count); 17043cbf3585SJohn Baldwin } 17053cbf3585SJohn Baldwin 17063cbf3585SJohn Baldwin void 1707d84882caSNeel Natu pci_walk_lintr(int bus, pci_lintr_cb cb, void *arg) 17083cbf3585SJohn Baldwin { 1709d84882caSNeel Natu struct businfo *bi; 1710d84882caSNeel Natu struct slotinfo *si; 17113cbf3585SJohn Baldwin struct intxinfo *ii; 17123cbf3585SJohn Baldwin int slot, pin; 17133cbf3585SJohn Baldwin 1714d84882caSNeel Natu if ((bi = pci_businfo[bus]) == NULL) 1715d84882caSNeel Natu return; 1716d84882caSNeel Natu 17173cbf3585SJohn Baldwin for (slot = 0; slot < MAXSLOTS; slot++) { 1718d84882caSNeel Natu si = &bi->slotinfo[slot]; 17193cbf3585SJohn Baldwin for (pin = 0; pin < 4; pin++) { 1720d84882caSNeel Natu ii = &si->si_intpins[pin]; 17213cbf3585SJohn Baldwin if (ii->ii_count != 0) 1722b3e9732aSJohn Baldwin cb(bus, slot, pin + 1, ii->ii_pirq_pin, 1723b3e9732aSJohn Baldwin ii->ii_ioapic_irq, arg); 17243cbf3585SJohn Baldwin } 17250038ee98SPeter Grehan } 1726ac7304a7SNeel Natu } 17270038ee98SPeter Grehan 172899d65389SNeel Natu /* 172999d65389SNeel Natu * Return 1 if the emulated device in 'slot' is a multi-function device. 173099d65389SNeel Natu * Return 0 otherwise. 173199d65389SNeel Natu */ 173299d65389SNeel Natu static int 1733d84882caSNeel Natu pci_emul_is_mfdev(int bus, int slot) 173499d65389SNeel Natu { 1735d84882caSNeel Natu struct businfo *bi; 1736d84882caSNeel Natu struct slotinfo *si; 173799d65389SNeel Natu int f, numfuncs; 17380038ee98SPeter Grehan 173999d65389SNeel Natu numfuncs = 0; 1740d84882caSNeel Natu if ((bi = pci_businfo[bus]) != NULL) { 1741d84882caSNeel Natu si = &bi->slotinfo[slot]; 174299d65389SNeel Natu for (f = 0; f < MAXFUNCS; f++) { 1743d84882caSNeel Natu if (si->si_funcs[f].fi_devi != NULL) { 174499d65389SNeel Natu numfuncs++; 174599d65389SNeel Natu } 174699d65389SNeel Natu } 1747d84882caSNeel Natu } 174899d65389SNeel Natu return (numfuncs > 1); 174999d65389SNeel Natu } 175099d65389SNeel Natu 175199d65389SNeel Natu /* 175299d65389SNeel Natu * Ensure that the PCIM_MFDEV bit is properly set (or unset) depending on 175399d65389SNeel Natu * whether or not is a multi-function being emulated in the pci 'slot'. 175499d65389SNeel Natu */ 175599d65389SNeel Natu static void 1756d84882caSNeel Natu pci_emul_hdrtype_fixup(int bus, int slot, int off, int bytes, uint32_t *rv) 175799d65389SNeel Natu { 175899d65389SNeel Natu int mfdev; 175999d65389SNeel Natu 176099d65389SNeel Natu if (off <= PCIR_HDRTYPE && off + bytes > PCIR_HDRTYPE) { 1761d84882caSNeel Natu mfdev = pci_emul_is_mfdev(bus, slot); 176299d65389SNeel Natu switch (bytes) { 176399d65389SNeel Natu case 1: 176499d65389SNeel Natu case 2: 176599d65389SNeel Natu *rv &= ~PCIM_MFDEV; 176699d65389SNeel Natu if (mfdev) { 176799d65389SNeel Natu *rv |= PCIM_MFDEV; 176899d65389SNeel Natu } 176999d65389SNeel Natu break; 177099d65389SNeel Natu case 4: 177199d65389SNeel Natu *rv &= ~(PCIM_MFDEV << 16); 177299d65389SNeel Natu if (mfdev) { 177399d65389SNeel Natu *rv |= (PCIM_MFDEV << 16); 177499d65389SNeel Natu } 177599d65389SNeel Natu break; 177699d65389SNeel Natu } 177799d65389SNeel Natu } 177899d65389SNeel Natu } 17790038ee98SPeter Grehan 178056282675SJohn Baldwin /* 178156282675SJohn Baldwin * Update device state in response to changes to the PCI command 178256282675SJohn Baldwin * register. 178356282675SJohn Baldwin */ 178456282675SJohn Baldwin void 178556282675SJohn Baldwin pci_emul_cmd_changed(struct pci_devinst *pi, uint16_t old) 178656282675SJohn Baldwin { 178756282675SJohn Baldwin int i; 178856282675SJohn Baldwin uint16_t changed, new; 178956282675SJohn Baldwin 179056282675SJohn Baldwin new = pci_get_cfgdata16(pi, PCIR_COMMAND); 179156282675SJohn Baldwin changed = old ^ new; 179256282675SJohn Baldwin 179356282675SJohn Baldwin /* 179456282675SJohn Baldwin * If the MMIO or I/O address space decoding has changed then 179556282675SJohn Baldwin * register/unregister all BARs that decode that address space. 179656282675SJohn Baldwin */ 179756282675SJohn Baldwin for (i = 0; i <= PCI_BARMAX; i++) { 179856282675SJohn Baldwin switch (pi->pi_bar[i].type) { 179956282675SJohn Baldwin case PCIBAR_NONE: 180056282675SJohn Baldwin case PCIBAR_MEMHI64: 180156282675SJohn Baldwin break; 180256282675SJohn Baldwin case PCIBAR_IO: 180356282675SJohn Baldwin /* I/O address space decoding changed? */ 180456282675SJohn Baldwin if (changed & PCIM_CMD_PORTEN) { 180556282675SJohn Baldwin if (new & PCIM_CMD_PORTEN) 180656282675SJohn Baldwin register_bar(pi, i); 180756282675SJohn Baldwin else 180856282675SJohn Baldwin unregister_bar(pi, i); 180956282675SJohn Baldwin } 181056282675SJohn Baldwin break; 181156282675SJohn Baldwin case PCIBAR_MEM32: 181256282675SJohn Baldwin case PCIBAR_MEM64: 181356282675SJohn Baldwin /* MMIO address space decoding changed? */ 181456282675SJohn Baldwin if (changed & PCIM_CMD_MEMEN) { 181556282675SJohn Baldwin if (new & PCIM_CMD_MEMEN) 181656282675SJohn Baldwin register_bar(pi, i); 181756282675SJohn Baldwin else 181856282675SJohn Baldwin unregister_bar(pi, i); 181956282675SJohn Baldwin } 182056282675SJohn Baldwin break; 182156282675SJohn Baldwin default: 182256282675SJohn Baldwin assert(0); 182356282675SJohn Baldwin } 182456282675SJohn Baldwin } 182556282675SJohn Baldwin 182656282675SJohn Baldwin /* 182756282675SJohn Baldwin * If INTx has been unmasked and is pending, assert the 182856282675SJohn Baldwin * interrupt. 182956282675SJohn Baldwin */ 183056282675SJohn Baldwin pci_lintr_update(pi); 183156282675SJohn Baldwin } 183256282675SJohn Baldwin 1833028d9311SNeel Natu static void 183454335630SNeel Natu pci_emul_cmdsts_write(struct pci_devinst *pi, int coff, uint32_t new, int bytes) 1835028d9311SNeel Natu { 183656282675SJohn Baldwin int rshift; 183756282675SJohn Baldwin uint32_t cmd, old, readonly; 183854335630SNeel Natu 183954335630SNeel Natu cmd = pci_get_cfgdata16(pi, PCIR_COMMAND); /* stash old value */ 1840028d9311SNeel Natu 1841028d9311SNeel Natu /* 184254335630SNeel Natu * From PCI Local Bus Specification 3.0 sections 6.2.2 and 6.2.3. 184354335630SNeel Natu * 184454335630SNeel Natu * XXX Bits 8, 11, 12, 13, 14 and 15 in the status register are 184554335630SNeel Natu * 'write 1 to clear'. However these bits are not set to '1' by 184654335630SNeel Natu * any device emulation so it is simpler to treat them as readonly. 1847028d9311SNeel Natu */ 184854335630SNeel Natu rshift = (coff & 0x3) * 8; 184954335630SNeel Natu readonly = 0xFFFFF880 >> rshift; 1850028d9311SNeel Natu 185154335630SNeel Natu old = CFGREAD(pi, coff, bytes); 185254335630SNeel Natu new &= ~readonly; 185354335630SNeel Natu new |= (old & readonly); 185454335630SNeel Natu CFGWRITE(pi, coff, new, bytes); /* update config */ 185554335630SNeel Natu 185656282675SJohn Baldwin pci_emul_cmd_changed(pi, cmd); 1857028d9311SNeel Natu } 1858028d9311SNeel Natu 185912a6eb99SNeel Natu static void 186012a6eb99SNeel Natu pci_cfgrw(struct vmctx *ctx, int vcpu, int in, int bus, int slot, int func, 186112a6eb99SNeel Natu int coff, int bytes, uint32_t *eax) 1862366f6083SPeter Grehan { 1863d84882caSNeel Natu struct businfo *bi; 1864d84882caSNeel Natu struct slotinfo *si; 1865366f6083SPeter Grehan struct pci_devinst *pi; 1866366f6083SPeter Grehan struct pci_devemu *pe; 186712a6eb99SNeel Natu int idx, needcfg; 1868028d9311SNeel Natu uint64_t addr, bar, mask; 1869366f6083SPeter Grehan 187012a6eb99SNeel Natu if ((bi = pci_businfo[bus]) != NULL) { 187112a6eb99SNeel Natu si = &bi->slotinfo[slot]; 187212a6eb99SNeel Natu pi = si->si_funcs[func].fi_devi; 1873d84882caSNeel Natu } else 187490415e0bSNeel Natu pi = NULL; 187590415e0bSNeel Natu 187699d65389SNeel Natu /* 187712a6eb99SNeel Natu * Just return if there is no device at this slot:func or if the 187812a6eb99SNeel Natu * the guest is doing an un-aligned access. 187999d65389SNeel Natu */ 188012a6eb99SNeel Natu if (pi == NULL || (bytes != 1 && bytes != 2 && bytes != 4) || 188112a6eb99SNeel Natu (coff & (bytes - 1)) != 0) { 1882366f6083SPeter Grehan if (in) 1883366f6083SPeter Grehan *eax = 0xffffffff; 188412a6eb99SNeel Natu return; 188512a6eb99SNeel Natu } 188612a6eb99SNeel Natu 188712a6eb99SNeel Natu /* 188812a6eb99SNeel Natu * Ignore all writes beyond the standard config space and return all 188912a6eb99SNeel Natu * ones on reads. 189012a6eb99SNeel Natu */ 189112a6eb99SNeel Natu if (coff >= PCI_REGMAX + 1) { 189212a6eb99SNeel Natu if (in) { 189312a6eb99SNeel Natu *eax = 0xffffffff; 189412a6eb99SNeel Natu /* 189512a6eb99SNeel Natu * Extended capabilities begin at offset 256 in config 189612a6eb99SNeel Natu * space. Absence of extended capabilities is signaled 189712a6eb99SNeel Natu * with all 0s in the extended capability header at 189812a6eb99SNeel Natu * offset 256. 189912a6eb99SNeel Natu */ 190012a6eb99SNeel Natu if (coff <= PCI_REGMAX + 4) 190112a6eb99SNeel Natu *eax = 0x00000000; 190212a6eb99SNeel Natu } 190312a6eb99SNeel Natu return; 1904366f6083SPeter Grehan } 1905366f6083SPeter Grehan 1906366f6083SPeter Grehan pe = pi->pi_d; 1907366f6083SPeter Grehan 1908366f6083SPeter Grehan /* 1909366f6083SPeter Grehan * Config read 1910366f6083SPeter Grehan */ 1911366f6083SPeter Grehan if (in) { 1912366f6083SPeter Grehan /* Let the device emulation override the default handler */ 191399d65389SNeel Natu if (pe->pe_cfgread != NULL) { 191412a6eb99SNeel Natu needcfg = pe->pe_cfgread(ctx, vcpu, pi, coff, bytes, 191512a6eb99SNeel Natu eax); 191699d65389SNeel Natu } else { 191799d65389SNeel Natu needcfg = 1; 191899d65389SNeel Natu } 1919366f6083SPeter Grehan 192054335630SNeel Natu if (needcfg) 192154335630SNeel Natu *eax = CFGREAD(pi, coff, bytes); 192299d65389SNeel Natu 192312a6eb99SNeel Natu pci_emul_hdrtype_fixup(bus, slot, coff, bytes, eax); 1924366f6083SPeter Grehan } else { 1925366f6083SPeter Grehan /* Let the device emulation override the default handler */ 1926366f6083SPeter Grehan if (pe->pe_cfgwrite != NULL && 1927366f6083SPeter Grehan (*pe->pe_cfgwrite)(ctx, vcpu, pi, coff, bytes, *eax) == 0) 192812a6eb99SNeel Natu return; 1929366f6083SPeter Grehan 1930366f6083SPeter Grehan /* 1931366f6083SPeter Grehan * Special handling for write to BAR registers 1932366f6083SPeter Grehan */ 1933366f6083SPeter Grehan if (coff >= PCIR_BAR(0) && coff < PCIR_BAR(PCI_BARMAX + 1)) { 1934366f6083SPeter Grehan /* 1935366f6083SPeter Grehan * Ignore writes to BAR registers that are not 1936366f6083SPeter Grehan * 4-byte aligned. 1937366f6083SPeter Grehan */ 1938366f6083SPeter Grehan if (bytes != 4 || (coff & 0x3) != 0) 193912a6eb99SNeel Natu return; 1940366f6083SPeter Grehan idx = (coff - PCIR_BAR(0)) / 4; 1941028d9311SNeel Natu mask = ~(pi->pi_bar[idx].size - 1); 1942366f6083SPeter Grehan switch (pi->pi_bar[idx].type) { 1943366f6083SPeter Grehan case PCIBAR_NONE: 1944028d9311SNeel Natu pi->pi_bar[idx].addr = bar = 0; 1945366f6083SPeter Grehan break; 1946366f6083SPeter Grehan case PCIBAR_IO: 1947028d9311SNeel Natu addr = *eax & mask; 1948028d9311SNeel Natu addr &= 0xffff; 1949028d9311SNeel Natu bar = addr | PCIM_BAR_IO_SPACE; 1950028d9311SNeel Natu /* 1951028d9311SNeel Natu * Register the new BAR value for interception 1952028d9311SNeel Natu */ 1953028d9311SNeel Natu if (addr != pi->pi_bar[idx].addr) { 1954028d9311SNeel Natu update_bar_address(pi, addr, idx, 1955028d9311SNeel Natu PCIBAR_IO); 1956028d9311SNeel Natu } 1957366f6083SPeter Grehan break; 1958366f6083SPeter Grehan case PCIBAR_MEM32: 1959028d9311SNeel Natu addr = bar = *eax & mask; 1960366f6083SPeter Grehan bar |= PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_32; 1961028d9311SNeel Natu if (addr != pi->pi_bar[idx].addr) { 1962028d9311SNeel Natu update_bar_address(pi, addr, idx, 1963028d9311SNeel Natu PCIBAR_MEM32); 1964028d9311SNeel Natu } 1965366f6083SPeter Grehan break; 1966366f6083SPeter Grehan case PCIBAR_MEM64: 1967028d9311SNeel Natu addr = bar = *eax & mask; 1968366f6083SPeter Grehan bar |= PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_64 | 1969366f6083SPeter Grehan PCIM_BAR_MEM_PREFETCH; 1970028d9311SNeel Natu if (addr != (uint32_t)pi->pi_bar[idx].addr) { 1971028d9311SNeel Natu update_bar_address(pi, addr, idx, 1972028d9311SNeel Natu PCIBAR_MEM64); 1973028d9311SNeel Natu } 1974366f6083SPeter Grehan break; 1975366f6083SPeter Grehan case PCIBAR_MEMHI64: 1976366f6083SPeter Grehan mask = ~(pi->pi_bar[idx - 1].size - 1); 1977028d9311SNeel Natu addr = ((uint64_t)*eax << 32) & mask; 1978028d9311SNeel Natu bar = addr >> 32; 1979028d9311SNeel Natu if (bar != pi->pi_bar[idx - 1].addr >> 32) { 1980028d9311SNeel Natu update_bar_address(pi, addr, idx - 1, 1981028d9311SNeel Natu PCIBAR_MEMHI64); 1982028d9311SNeel Natu } 1983366f6083SPeter Grehan break; 1984366f6083SPeter Grehan default: 1985366f6083SPeter Grehan assert(0); 1986366f6083SPeter Grehan } 1987366f6083SPeter Grehan pci_set_cfgdata32(pi, coff, bar); 1988cd942e0fSPeter Grehan 1989366f6083SPeter Grehan } else if (pci_emul_iscap(pi, coff)) { 199021368498SPeter Grehan pci_emul_capwrite(pi, coff, bytes, *eax, 0, 0); 199154335630SNeel Natu } else if (coff >= PCIR_COMMAND && coff < PCIR_REVID) { 199254335630SNeel Natu pci_emul_cmdsts_write(pi, coff, *eax, bytes); 1993366f6083SPeter Grehan } else { 1994366f6083SPeter Grehan CFGWRITE(pi, coff, *eax, bytes); 1995366f6083SPeter Grehan } 1996366f6083SPeter Grehan } 199712a6eb99SNeel Natu } 1998366f6083SPeter Grehan 199912a6eb99SNeel Natu static int cfgenable, cfgbus, cfgslot, cfgfunc, cfgoff; 200012a6eb99SNeel Natu 200112a6eb99SNeel Natu static int 200212a6eb99SNeel Natu pci_emul_cfgaddr(struct vmctx *ctx, int vcpu, int in, int port, int bytes, 200312a6eb99SNeel Natu uint32_t *eax, void *arg) 200412a6eb99SNeel Natu { 200512a6eb99SNeel Natu uint32_t x; 200612a6eb99SNeel Natu 200712a6eb99SNeel Natu if (bytes != 4) { 200812a6eb99SNeel Natu if (in) 200912a6eb99SNeel Natu *eax = (bytes == 2) ? 0xffff : 0xff; 201012a6eb99SNeel Natu return (0); 201112a6eb99SNeel Natu } 201212a6eb99SNeel Natu 201312a6eb99SNeel Natu if (in) { 201412a6eb99SNeel Natu x = (cfgbus << 16) | (cfgslot << 11) | (cfgfunc << 8) | cfgoff; 201512a6eb99SNeel Natu if (cfgenable) 201612a6eb99SNeel Natu x |= CONF1_ENABLE; 201712a6eb99SNeel Natu *eax = x; 201812a6eb99SNeel Natu } else { 201912a6eb99SNeel Natu x = *eax; 202012a6eb99SNeel Natu cfgenable = (x & CONF1_ENABLE) == CONF1_ENABLE; 202112a6eb99SNeel Natu cfgoff = x & PCI_REGMAX; 202212a6eb99SNeel Natu cfgfunc = (x >> 8) & PCI_FUNCMAX; 202312a6eb99SNeel Natu cfgslot = (x >> 11) & PCI_SLOTMAX; 202412a6eb99SNeel Natu cfgbus = (x >> 16) & PCI_BUSMAX; 202512a6eb99SNeel Natu } 202612a6eb99SNeel Natu 202712a6eb99SNeel Natu return (0); 202812a6eb99SNeel Natu } 202912a6eb99SNeel Natu INOUT_PORT(pci_cfgaddr, CONF1_ADDR_PORT, IOPORT_F_INOUT, pci_emul_cfgaddr); 203012a6eb99SNeel Natu 203112a6eb99SNeel Natu static int 203212a6eb99SNeel Natu pci_emul_cfgdata(struct vmctx *ctx, int vcpu, int in, int port, int bytes, 203312a6eb99SNeel Natu uint32_t *eax, void *arg) 203412a6eb99SNeel Natu { 203512a6eb99SNeel Natu int coff; 203612a6eb99SNeel Natu 203712a6eb99SNeel Natu assert(bytes == 1 || bytes == 2 || bytes == 4); 203812a6eb99SNeel Natu 203912a6eb99SNeel Natu coff = cfgoff + (port - CONF1_DATA_PORT); 204012a6eb99SNeel Natu if (cfgenable) { 204112a6eb99SNeel Natu pci_cfgrw(ctx, vcpu, in, cfgbus, cfgslot, cfgfunc, coff, bytes, 204212a6eb99SNeel Natu eax); 204312a6eb99SNeel Natu } else { 204412a6eb99SNeel Natu /* Ignore accesses to cfgdata if not enabled by cfgaddr */ 204512a6eb99SNeel Natu if (in) 204612a6eb99SNeel Natu *eax = 0xffffffff; 204712a6eb99SNeel Natu } 2048366f6083SPeter Grehan return (0); 2049366f6083SPeter Grehan } 2050366f6083SPeter Grehan 2051366f6083SPeter Grehan INOUT_PORT(pci_cfgdata, CONF1_DATA_PORT+0, IOPORT_F_INOUT, pci_emul_cfgdata); 2052366f6083SPeter Grehan INOUT_PORT(pci_cfgdata, CONF1_DATA_PORT+1, IOPORT_F_INOUT, pci_emul_cfgdata); 2053366f6083SPeter Grehan INOUT_PORT(pci_cfgdata, CONF1_DATA_PORT+2, IOPORT_F_INOUT, pci_emul_cfgdata); 2054366f6083SPeter Grehan INOUT_PORT(pci_cfgdata, CONF1_DATA_PORT+3, IOPORT_F_INOUT, pci_emul_cfgdata); 2055366f6083SPeter Grehan 2056483d953aSJohn Baldwin #ifdef BHYVE_SNAPSHOT 2057483d953aSJohn Baldwin /* 2058483d953aSJohn Baldwin * Saves/restores PCI device emulated state. Returns 0 on success. 2059483d953aSJohn Baldwin */ 2060483d953aSJohn Baldwin static int 2061483d953aSJohn Baldwin pci_snapshot_pci_dev(struct vm_snapshot_meta *meta) 2062483d953aSJohn Baldwin { 2063483d953aSJohn Baldwin struct pci_devinst *pi; 2064483d953aSJohn Baldwin int i; 2065483d953aSJohn Baldwin int ret; 2066483d953aSJohn Baldwin 2067483d953aSJohn Baldwin pi = meta->dev_data; 2068483d953aSJohn Baldwin 2069483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(pi->pi_msi.enabled, meta, ret, done); 2070483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(pi->pi_msi.addr, meta, ret, done); 2071483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(pi->pi_msi.msg_data, meta, ret, done); 2072483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(pi->pi_msi.maxmsgnum, meta, ret, done); 2073483d953aSJohn Baldwin 2074483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(pi->pi_msix.enabled, meta, ret, done); 2075483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(pi->pi_msix.table_bar, meta, ret, done); 2076483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(pi->pi_msix.pba_bar, meta, ret, done); 2077483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(pi->pi_msix.table_offset, meta, ret, done); 2078483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(pi->pi_msix.table_count, meta, ret, done); 2079483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(pi->pi_msix.pba_offset, meta, ret, done); 2080483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(pi->pi_msix.pba_size, meta, ret, done); 2081483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(pi->pi_msix.function_mask, meta, ret, done); 2082483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(pi->pi_msix.pba_page_offset, meta, ret, done); 2083483d953aSJohn Baldwin 2084483d953aSJohn Baldwin SNAPSHOT_BUF_OR_LEAVE(pi->pi_cfgdata, sizeof(pi->pi_cfgdata), 2085483d953aSJohn Baldwin meta, ret, done); 2086483d953aSJohn Baldwin 2087483d953aSJohn Baldwin for (i = 0; i < nitems(pi->pi_bar); i++) { 2088483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(pi->pi_bar[i].type, meta, ret, done); 2089483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(pi->pi_bar[i].size, meta, ret, done); 2090483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(pi->pi_bar[i].addr, meta, ret, done); 2091483d953aSJohn Baldwin } 2092483d953aSJohn Baldwin 2093483d953aSJohn Baldwin /* Restore MSI-X table. */ 2094483d953aSJohn Baldwin for (i = 0; i < pi->pi_msix.table_count; i++) { 2095483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(pi->pi_msix.table[i].addr, 2096483d953aSJohn Baldwin meta, ret, done); 2097483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(pi->pi_msix.table[i].msg_data, 2098483d953aSJohn Baldwin meta, ret, done); 2099483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(pi->pi_msix.table[i].vector_control, 2100483d953aSJohn Baldwin meta, ret, done); 2101483d953aSJohn Baldwin } 2102483d953aSJohn Baldwin 2103483d953aSJohn Baldwin done: 2104483d953aSJohn Baldwin return (ret); 2105483d953aSJohn Baldwin } 2106483d953aSJohn Baldwin 2107483d953aSJohn Baldwin static int 2108483d953aSJohn Baldwin pci_find_slotted_dev(const char *dev_name, struct pci_devemu **pde, 2109483d953aSJohn Baldwin struct pci_devinst **pdi) 2110483d953aSJohn Baldwin { 2111483d953aSJohn Baldwin struct businfo *bi; 2112483d953aSJohn Baldwin struct slotinfo *si; 2113483d953aSJohn Baldwin struct funcinfo *fi; 2114483d953aSJohn Baldwin int bus, slot, func; 2115483d953aSJohn Baldwin 2116483d953aSJohn Baldwin assert(dev_name != NULL); 2117483d953aSJohn Baldwin assert(pde != NULL); 2118483d953aSJohn Baldwin assert(pdi != NULL); 2119483d953aSJohn Baldwin 2120483d953aSJohn Baldwin for (bus = 0; bus < MAXBUSES; bus++) { 2121483d953aSJohn Baldwin if ((bi = pci_businfo[bus]) == NULL) 2122483d953aSJohn Baldwin continue; 2123483d953aSJohn Baldwin 2124483d953aSJohn Baldwin for (slot = 0; slot < MAXSLOTS; slot++) { 2125483d953aSJohn Baldwin si = &bi->slotinfo[slot]; 2126483d953aSJohn Baldwin for (func = 0; func < MAXFUNCS; func++) { 2127483d953aSJohn Baldwin fi = &si->si_funcs[func]; 2128621b5090SJohn Baldwin if (fi->fi_pde == NULL) 2129483d953aSJohn Baldwin continue; 2130621b5090SJohn Baldwin if (strcmp(dev_name, fi->fi_pde->pe_emu) != 0) 2131483d953aSJohn Baldwin continue; 2132483d953aSJohn Baldwin 2133621b5090SJohn Baldwin *pde = fi->fi_pde; 2134483d953aSJohn Baldwin *pdi = fi->fi_devi; 2135483d953aSJohn Baldwin return (0); 2136483d953aSJohn Baldwin } 2137483d953aSJohn Baldwin } 2138483d953aSJohn Baldwin } 2139483d953aSJohn Baldwin 2140483d953aSJohn Baldwin return (EINVAL); 2141483d953aSJohn Baldwin } 2142483d953aSJohn Baldwin 2143483d953aSJohn Baldwin int 2144483d953aSJohn Baldwin pci_snapshot(struct vm_snapshot_meta *meta) 2145483d953aSJohn Baldwin { 2146483d953aSJohn Baldwin struct pci_devemu *pde; 2147483d953aSJohn Baldwin struct pci_devinst *pdi; 2148483d953aSJohn Baldwin int ret; 2149483d953aSJohn Baldwin 2150483d953aSJohn Baldwin assert(meta->dev_name != NULL); 2151483d953aSJohn Baldwin 2152483d953aSJohn Baldwin ret = pci_find_slotted_dev(meta->dev_name, &pde, &pdi); 2153483d953aSJohn Baldwin if (ret != 0) { 2154483d953aSJohn Baldwin fprintf(stderr, "%s: no such name: %s\r\n", 2155483d953aSJohn Baldwin __func__, meta->dev_name); 2156483d953aSJohn Baldwin memset(meta->buffer.buf_start, 0, meta->buffer.buf_size); 2157483d953aSJohn Baldwin return (0); 2158483d953aSJohn Baldwin } 2159483d953aSJohn Baldwin 2160483d953aSJohn Baldwin meta->dev_data = pdi; 2161483d953aSJohn Baldwin 2162483d953aSJohn Baldwin if (pde->pe_snapshot == NULL) { 2163483d953aSJohn Baldwin fprintf(stderr, "%s: not implemented yet for: %s\r\n", 2164483d953aSJohn Baldwin __func__, meta->dev_name); 2165483d953aSJohn Baldwin return (-1); 2166483d953aSJohn Baldwin } 2167483d953aSJohn Baldwin 2168483d953aSJohn Baldwin ret = pci_snapshot_pci_dev(meta); 2169483d953aSJohn Baldwin if (ret != 0) { 2170483d953aSJohn Baldwin fprintf(stderr, "%s: failed to snapshot pci dev\r\n", 2171483d953aSJohn Baldwin __func__); 2172483d953aSJohn Baldwin return (-1); 2173483d953aSJohn Baldwin } 2174483d953aSJohn Baldwin 2175483d953aSJohn Baldwin ret = (*pde->pe_snapshot)(meta); 2176483d953aSJohn Baldwin 2177483d953aSJohn Baldwin return (ret); 2178483d953aSJohn Baldwin } 2179483d953aSJohn Baldwin 2180483d953aSJohn Baldwin int 2181483d953aSJohn Baldwin pci_pause(struct vmctx *ctx, const char *dev_name) 2182483d953aSJohn Baldwin { 2183483d953aSJohn Baldwin struct pci_devemu *pde; 2184483d953aSJohn Baldwin struct pci_devinst *pdi; 2185483d953aSJohn Baldwin int ret; 2186483d953aSJohn Baldwin 2187483d953aSJohn Baldwin assert(dev_name != NULL); 2188483d953aSJohn Baldwin 2189483d953aSJohn Baldwin ret = pci_find_slotted_dev(dev_name, &pde, &pdi); 2190483d953aSJohn Baldwin if (ret != 0) { 2191483d953aSJohn Baldwin /* 2192483d953aSJohn Baldwin * It is possible to call this function without 2193483d953aSJohn Baldwin * checking that the device is inserted first. 2194483d953aSJohn Baldwin */ 2195483d953aSJohn Baldwin fprintf(stderr, "%s: no such name: %s\n", __func__, dev_name); 2196483d953aSJohn Baldwin return (0); 2197483d953aSJohn Baldwin } 2198483d953aSJohn Baldwin 2199483d953aSJohn Baldwin if (pde->pe_pause == NULL) { 2200483d953aSJohn Baldwin /* The pause/resume functionality is optional. */ 2201483d953aSJohn Baldwin fprintf(stderr, "%s: not implemented for: %s\n", 2202483d953aSJohn Baldwin __func__, dev_name); 2203483d953aSJohn Baldwin return (0); 2204483d953aSJohn Baldwin } 2205483d953aSJohn Baldwin 2206483d953aSJohn Baldwin return (*pde->pe_pause)(ctx, pdi); 2207483d953aSJohn Baldwin } 2208483d953aSJohn Baldwin 2209483d953aSJohn Baldwin int 2210483d953aSJohn Baldwin pci_resume(struct vmctx *ctx, const char *dev_name) 2211483d953aSJohn Baldwin { 2212483d953aSJohn Baldwin struct pci_devemu *pde; 2213483d953aSJohn Baldwin struct pci_devinst *pdi; 2214483d953aSJohn Baldwin int ret; 2215483d953aSJohn Baldwin 2216483d953aSJohn Baldwin assert(dev_name != NULL); 2217483d953aSJohn Baldwin 2218483d953aSJohn Baldwin ret = pci_find_slotted_dev(dev_name, &pde, &pdi); 2219483d953aSJohn Baldwin if (ret != 0) { 2220483d953aSJohn Baldwin /* 2221483d953aSJohn Baldwin * It is possible to call this function without 2222483d953aSJohn Baldwin * checking that the device is inserted first. 2223483d953aSJohn Baldwin */ 2224483d953aSJohn Baldwin fprintf(stderr, "%s: no such name: %s\n", __func__, dev_name); 2225483d953aSJohn Baldwin return (0); 2226483d953aSJohn Baldwin } 2227483d953aSJohn Baldwin 2228483d953aSJohn Baldwin if (pde->pe_resume == NULL) { 2229483d953aSJohn Baldwin /* The pause/resume functionality is optional. */ 2230483d953aSJohn Baldwin fprintf(stderr, "%s: not implemented for: %s\n", 2231483d953aSJohn Baldwin __func__, dev_name); 2232483d953aSJohn Baldwin return (0); 2233483d953aSJohn Baldwin } 2234483d953aSJohn Baldwin 2235483d953aSJohn Baldwin return (*pde->pe_resume)(ctx, pdi); 2236483d953aSJohn Baldwin } 2237483d953aSJohn Baldwin #endif 2238483d953aSJohn Baldwin 2239366f6083SPeter Grehan #define PCI_EMUL_TEST 2240366f6083SPeter Grehan #ifdef PCI_EMUL_TEST 2241366f6083SPeter Grehan /* 2242366f6083SPeter Grehan * Define a dummy test device 2243366f6083SPeter Grehan */ 2244d84882caSNeel Natu #define DIOSZ 8 22454d1e669cSPeter Grehan #define DMEMSZ 4096 2246366f6083SPeter Grehan struct pci_emul_dsoftc { 22474d1e669cSPeter Grehan uint8_t ioregs[DIOSZ]; 2248fd4e0d4cSNeel Natu uint8_t memregs[2][DMEMSZ]; 2249366f6083SPeter Grehan }; 2250366f6083SPeter Grehan 22514d1e669cSPeter Grehan #define PCI_EMUL_MSI_MSGS 4 22524d1e669cSPeter Grehan #define PCI_EMUL_MSIX_MSGS 16 2253366f6083SPeter Grehan 2254b67e81dbSJohn Baldwin static int 2255621b5090SJohn Baldwin pci_emul_dinit(struct vmctx *ctx, struct pci_devinst *pi, nvlist_t *nvl) 2256366f6083SPeter Grehan { 2257366f6083SPeter Grehan int error; 2258366f6083SPeter Grehan struct pci_emul_dsoftc *sc; 2259366f6083SPeter Grehan 2260994f858aSXin LI sc = calloc(1, sizeof(struct pci_emul_dsoftc)); 2261366f6083SPeter Grehan 2262366f6083SPeter Grehan pi->pi_arg = sc; 2263366f6083SPeter Grehan 2264366f6083SPeter Grehan pci_set_cfgdata16(pi, PCIR_DEVICE, 0x0001); 2265366f6083SPeter Grehan pci_set_cfgdata16(pi, PCIR_VENDOR, 0x10DD); 2266366f6083SPeter Grehan pci_set_cfgdata8(pi, PCIR_CLASS, 0x02); 2267366f6083SPeter Grehan 22684d1e669cSPeter Grehan error = pci_emul_add_msicap(pi, PCI_EMUL_MSI_MSGS); 2269366f6083SPeter Grehan assert(error == 0); 2270366f6083SPeter Grehan 22714d1e669cSPeter Grehan error = pci_emul_alloc_bar(pi, 0, PCIBAR_IO, DIOSZ); 22724d1e669cSPeter Grehan assert(error == 0); 22734d1e669cSPeter Grehan 22744d1e669cSPeter Grehan error = pci_emul_alloc_bar(pi, 1, PCIBAR_MEM32, DMEMSZ); 2275366f6083SPeter Grehan assert(error == 0); 2276366f6083SPeter Grehan 2277fd4e0d4cSNeel Natu error = pci_emul_alloc_bar(pi, 2, PCIBAR_MEM32, DMEMSZ); 2278fd4e0d4cSNeel Natu assert(error == 0); 2279fd4e0d4cSNeel Natu 2280366f6083SPeter Grehan return (0); 2281366f6083SPeter Grehan } 2282366f6083SPeter Grehan 2283b67e81dbSJohn Baldwin static void 22844d1e669cSPeter Grehan pci_emul_diow(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int baridx, 22854d1e669cSPeter Grehan uint64_t offset, int size, uint64_t value) 2286366f6083SPeter Grehan { 2287366f6083SPeter Grehan int i; 2288366f6083SPeter Grehan struct pci_emul_dsoftc *sc = pi->pi_arg; 2289366f6083SPeter Grehan 22904d1e669cSPeter Grehan if (baridx == 0) { 22914d1e669cSPeter Grehan if (offset + size > DIOSZ) { 22924d1e669cSPeter Grehan printf("diow: iow too large, offset %ld size %d\n", 22934d1e669cSPeter Grehan offset, size); 2294366f6083SPeter Grehan return; 2295366f6083SPeter Grehan } 2296366f6083SPeter Grehan 2297366f6083SPeter Grehan if (size == 1) { 22984d1e669cSPeter Grehan sc->ioregs[offset] = value & 0xff; 2299366f6083SPeter Grehan } else if (size == 2) { 23004d1e669cSPeter Grehan *(uint16_t *)&sc->ioregs[offset] = value & 0xffff; 23014d1e669cSPeter Grehan } else if (size == 4) { 23024d1e669cSPeter Grehan *(uint32_t *)&sc->ioregs[offset] = value; 2303366f6083SPeter Grehan } else { 23044d1e669cSPeter Grehan printf("diow: iow unknown size %d\n", size); 2305366f6083SPeter Grehan } 2306366f6083SPeter Grehan 2307366f6083SPeter Grehan /* 2308366f6083SPeter Grehan * Special magic value to generate an interrupt 2309366f6083SPeter Grehan */ 2310366f6083SPeter Grehan if (offset == 4 && size == 4 && pci_msi_enabled(pi)) 23114f8be175SNeel Natu pci_generate_msi(pi, value % pci_msi_maxmsgnum(pi)); 2312366f6083SPeter Grehan 2313366f6083SPeter Grehan if (value == 0xabcdef) { 23144f8be175SNeel Natu for (i = 0; i < pci_msi_maxmsgnum(pi); i++) 2315366f6083SPeter Grehan pci_generate_msi(pi, i); 2316366f6083SPeter Grehan } 2317366f6083SPeter Grehan } 2318366f6083SPeter Grehan 2319fd4e0d4cSNeel Natu if (baridx == 1 || baridx == 2) { 23204d1e669cSPeter Grehan if (offset + size > DMEMSZ) { 23214d1e669cSPeter Grehan printf("diow: memw too large, offset %ld size %d\n", 23224d1e669cSPeter Grehan offset, size); 23234d1e669cSPeter Grehan return; 23244d1e669cSPeter Grehan } 23254d1e669cSPeter Grehan 2326fd4e0d4cSNeel Natu i = baridx - 1; /* 'memregs' index */ 2327fd4e0d4cSNeel Natu 23284d1e669cSPeter Grehan if (size == 1) { 2329fd4e0d4cSNeel Natu sc->memregs[i][offset] = value; 23304d1e669cSPeter Grehan } else if (size == 2) { 2331fd4e0d4cSNeel Natu *(uint16_t *)&sc->memregs[i][offset] = value; 23324d1e669cSPeter Grehan } else if (size == 4) { 2333fd4e0d4cSNeel Natu *(uint32_t *)&sc->memregs[i][offset] = value; 23344d1e669cSPeter Grehan } else if (size == 8) { 2335fd4e0d4cSNeel Natu *(uint64_t *)&sc->memregs[i][offset] = value; 23364d1e669cSPeter Grehan } else { 23374d1e669cSPeter Grehan printf("diow: memw unknown size %d\n", size); 23384d1e669cSPeter Grehan } 23394d1e669cSPeter Grehan 23404d1e669cSPeter Grehan /* 23414d1e669cSPeter Grehan * magic interrupt ?? 23424d1e669cSPeter Grehan */ 23434d1e669cSPeter Grehan } 23444d1e669cSPeter Grehan 23459f3dba68SPedro F. Giffuni if (baridx > 2 || baridx < 0) { 23464d1e669cSPeter Grehan printf("diow: unknown bar idx %d\n", baridx); 23474d1e669cSPeter Grehan } 23484d1e669cSPeter Grehan } 23494d1e669cSPeter Grehan 23504d1e669cSPeter Grehan static uint64_t 23514d1e669cSPeter Grehan pci_emul_dior(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int baridx, 23524d1e669cSPeter Grehan uint64_t offset, int size) 2353366f6083SPeter Grehan { 2354366f6083SPeter Grehan struct pci_emul_dsoftc *sc = pi->pi_arg; 2355366f6083SPeter Grehan uint32_t value; 2356fd4e0d4cSNeel Natu int i; 2357366f6083SPeter Grehan 23584d1e669cSPeter Grehan if (baridx == 0) { 23594d1e669cSPeter Grehan if (offset + size > DIOSZ) { 23604d1e669cSPeter Grehan printf("dior: ior too large, offset %ld size %d\n", 23614d1e669cSPeter Grehan offset, size); 2362366f6083SPeter Grehan return (0); 2363366f6083SPeter Grehan } 2364366f6083SPeter Grehan 23656e43f3edSPedro F. Giffuni value = 0; 2366366f6083SPeter Grehan if (size == 1) { 23674d1e669cSPeter Grehan value = sc->ioregs[offset]; 2368366f6083SPeter Grehan } else if (size == 2) { 23694d1e669cSPeter Grehan value = *(uint16_t *) &sc->ioregs[offset]; 23704d1e669cSPeter Grehan } else if (size == 4) { 23714d1e669cSPeter Grehan value = *(uint32_t *) &sc->ioregs[offset]; 2372366f6083SPeter Grehan } else { 23734d1e669cSPeter Grehan printf("dior: ior unknown size %d\n", size); 23744d1e669cSPeter Grehan } 23754d1e669cSPeter Grehan } 23764d1e669cSPeter Grehan 2377fd4e0d4cSNeel Natu if (baridx == 1 || baridx == 2) { 23784d1e669cSPeter Grehan if (offset + size > DMEMSZ) { 23794d1e669cSPeter Grehan printf("dior: memr too large, offset %ld size %d\n", 23804d1e669cSPeter Grehan offset, size); 23814d1e669cSPeter Grehan return (0); 23824d1e669cSPeter Grehan } 23834d1e669cSPeter Grehan 2384fd4e0d4cSNeel Natu i = baridx - 1; /* 'memregs' index */ 2385fd4e0d4cSNeel Natu 23864d1e669cSPeter Grehan if (size == 1) { 2387fd4e0d4cSNeel Natu value = sc->memregs[i][offset]; 23884d1e669cSPeter Grehan } else if (size == 2) { 2389fd4e0d4cSNeel Natu value = *(uint16_t *) &sc->memregs[i][offset]; 23904d1e669cSPeter Grehan } else if (size == 4) { 2391fd4e0d4cSNeel Natu value = *(uint32_t *) &sc->memregs[i][offset]; 23924d1e669cSPeter Grehan } else if (size == 8) { 2393fd4e0d4cSNeel Natu value = *(uint64_t *) &sc->memregs[i][offset]; 23944d1e669cSPeter Grehan } else { 23954d1e669cSPeter Grehan printf("dior: ior unknown size %d\n", size); 23964d1e669cSPeter Grehan } 23974d1e669cSPeter Grehan } 23984d1e669cSPeter Grehan 23994d1e669cSPeter Grehan 24009f3dba68SPedro F. Giffuni if (baridx > 2 || baridx < 0) { 24014d1e669cSPeter Grehan printf("dior: unknown bar idx %d\n", baridx); 24024d1e669cSPeter Grehan return (0); 2403366f6083SPeter Grehan } 2404366f6083SPeter Grehan 2405366f6083SPeter Grehan return (value); 2406366f6083SPeter Grehan } 2407366f6083SPeter Grehan 2408483d953aSJohn Baldwin #ifdef BHYVE_SNAPSHOT 2409483d953aSJohn Baldwin int 2410483d953aSJohn Baldwin pci_emul_snapshot(struct vm_snapshot_meta *meta) 2411483d953aSJohn Baldwin { 2412483d953aSJohn Baldwin 2413483d953aSJohn Baldwin return (0); 2414483d953aSJohn Baldwin } 2415483d953aSJohn Baldwin #endif 2416483d953aSJohn Baldwin 2417366f6083SPeter Grehan struct pci_devemu pci_dummy = { 2418366f6083SPeter Grehan .pe_emu = "dummy", 2419366f6083SPeter Grehan .pe_init = pci_emul_dinit, 24204d1e669cSPeter Grehan .pe_barwrite = pci_emul_diow, 2421483d953aSJohn Baldwin .pe_barread = pci_emul_dior, 2422483d953aSJohn Baldwin #ifdef BHYVE_SNAPSHOT 2423483d953aSJohn Baldwin .pe_snapshot = pci_emul_snapshot, 2424483d953aSJohn Baldwin #endif 2425366f6083SPeter Grehan }; 2426366f6083SPeter Grehan PCI_EMUL_SET(pci_dummy); 2427366f6083SPeter Grehan 2428366f6083SPeter Grehan #endif /* PCI_EMUL_TEST */ 2429