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> 36e47fe318SCorvin Köhne #include <sys/mman.h> 37366f6083SPeter Grehan 38366f6083SPeter Grehan #include <ctype.h> 39fc7207c8SEmmanuel Vadot #include <err.h> 407e12dfe5SEnji Cooper #include <errno.h> 413cbf3585SJohn Baldwin #include <pthread.h> 42366f6083SPeter Grehan #include <stdio.h> 43366f6083SPeter Grehan #include <stdlib.h> 44366f6083SPeter Grehan #include <string.h> 45366f6083SPeter Grehan #include <strings.h> 46366f6083SPeter Grehan #include <assert.h> 47028d9311SNeel Natu #include <stdbool.h> 485cf21e48SCorvin Köhne #include <sysexits.h> 49366f6083SPeter Grehan 50366f6083SPeter Grehan #include <machine/vmm.h> 51483d953aSJohn Baldwin #include <machine/vmm_snapshot.h> 52366f6083SPeter Grehan #include <vmmapi.h> 53366f6083SPeter Grehan 54e6c8bc29SJohn Baldwin #include "acpi.h" 55e285ef8dSPeter Grehan #include "bhyverun.h" 56621b5090SJohn Baldwin #include "config.h" 57332eff95SVincenzo Maffione #include "debug.h" 58366f6083SPeter Grehan #include "inout.h" 593cbf3585SJohn Baldwin #include "ioapic.h" 604d1e669cSPeter Grehan #include "mem.h" 61366f6083SPeter Grehan #include "pci_emul.h" 62b3e9732aSJohn Baldwin #include "pci_irq.h" 63e6c8bc29SJohn Baldwin #include "pci_lpc.h" 64366f6083SPeter Grehan 65366f6083SPeter Grehan #define CONF1_ADDR_PORT 0x0cf8 66366f6083SPeter Grehan #define CONF1_DATA_PORT 0x0cfc 67366f6083SPeter Grehan 6875543036SPeter Grehan #define CONF1_ENABLE 0x80000000ul 6975543036SPeter Grehan 70d84882caSNeel Natu #define MAXBUSES (PCI_BUSMAX + 1) 7199d65389SNeel Natu #define MAXSLOTS (PCI_SLOTMAX + 1) 7299d65389SNeel Natu #define MAXFUNCS (PCI_FUNCMAX + 1) 73366f6083SPeter Grehan 744a4053e1SCorvin Köhne #define GB (1024 * 1024 * 1024UL) 754a4053e1SCorvin Köhne 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; 105e47fe318SCorvin Köhne static uint8_t *pci_emul_rombase; 106e47fe318SCorvin Köhne static uint64_t pci_emul_romoffset; 107e47fe318SCorvin Köhne static uint8_t *pci_emul_romlim; 108366f6083SPeter Grehan static uint64_t pci_emul_membase32; 109366f6083SPeter Grehan static uint64_t pci_emul_membase64; 1109922872bSKonstantin Belousov static uint64_t pci_emul_memlim64; 111366f6083SPeter Grehan 11201f9362eSCorvin Köhne struct pci_bar_allocation { 11301f9362eSCorvin Köhne TAILQ_ENTRY(pci_bar_allocation) chain; 11401f9362eSCorvin Köhne struct pci_devinst *pdi; 11501f9362eSCorvin Köhne int idx; 11601f9362eSCorvin Köhne enum pcibar_type type; 11701f9362eSCorvin Köhne uint64_t size; 11801f9362eSCorvin Köhne }; 11901f9362eSCorvin Köhne TAILQ_HEAD(pci_bar_list, pci_bar_allocation) pci_bars = TAILQ_HEAD_INITIALIZER( 12001f9362eSCorvin Köhne pci_bars); 12101f9362eSCorvin Köhne 122366f6083SPeter Grehan #define PCI_EMUL_IOBASE 0x2000 123366f6083SPeter Grehan #define PCI_EMUL_IOLIMIT 0x10000 124366f6083SPeter Grehan 125e47fe318SCorvin Köhne #define PCI_EMUL_ROMSIZE 0x10000000 126e47fe318SCorvin Köhne 12712a6eb99SNeel Natu #define PCI_EMUL_ECFG_BASE 0xE0000000 /* 3.5GB */ 12812a6eb99SNeel Natu #define PCI_EMUL_ECFG_SIZE (MAXBUSES * 1024 * 1024) /* 1MB per bus */ 12912a6eb99SNeel Natu SYSRES_MEM(PCI_EMUL_ECFG_BASE, PCI_EMUL_ECFG_SIZE); 13012a6eb99SNeel Natu 1315cf21e48SCorvin Köhne /* 1325cf21e48SCorvin Köhne * OVMF always uses 0xC0000000 as base address for 32 bit PCI MMIO. Don't 1335cf21e48SCorvin Köhne * change this address without changing it in OVMF. 1345cf21e48SCorvin Köhne */ 1355cf21e48SCorvin Köhne #define PCI_EMUL_MEMBASE32 0xC0000000 13612a6eb99SNeel Natu #define PCI_EMUL_MEMLIMIT32 PCI_EMUL_ECFG_BASE 1374a4053e1SCorvin Köhne #define PCI_EMUL_MEMSIZE64 (32*GB) 138366f6083SPeter Grehan 139621b5090SJohn Baldwin static struct pci_devemu *pci_emul_finddev(const char *name); 140b3e9732aSJohn Baldwin static void pci_lintr_route(struct pci_devinst *pi); 1413cbf3585SJohn Baldwin static void pci_lintr_update(struct pci_devinst *pi); 14212a6eb99SNeel Natu static void pci_cfgrw(struct vmctx *ctx, int vcpu, int in, int bus, int slot, 14312a6eb99SNeel Natu int func, int coff, int bytes, uint32_t *val); 144366f6083SPeter Grehan 14554335630SNeel Natu static __inline void 14654335630SNeel Natu CFGWRITE(struct pci_devinst *pi, int coff, uint32_t val, int bytes) 14754335630SNeel Natu { 14854335630SNeel Natu 14954335630SNeel Natu if (bytes == 1) 15054335630SNeel Natu pci_set_cfgdata8(pi, coff, val); 15154335630SNeel Natu else if (bytes == 2) 15254335630SNeel Natu pci_set_cfgdata16(pi, coff, val); 15354335630SNeel Natu else 15454335630SNeel Natu pci_set_cfgdata32(pi, coff, val); 15554335630SNeel Natu } 15654335630SNeel Natu 15754335630SNeel Natu static __inline uint32_t 15854335630SNeel Natu CFGREAD(struct pci_devinst *pi, int coff, int bytes) 15954335630SNeel Natu { 16054335630SNeel Natu 16154335630SNeel Natu if (bytes == 1) 16254335630SNeel Natu return (pci_get_cfgdata8(pi, coff)); 16354335630SNeel Natu else if (bytes == 2) 16454335630SNeel Natu return (pci_get_cfgdata16(pi, coff)); 16554335630SNeel Natu else 16654335630SNeel Natu return (pci_get_cfgdata32(pi, coff)); 16754335630SNeel Natu } 16854335630SNeel Natu 169366f6083SPeter Grehan /* 170366f6083SPeter Grehan * I/O access 171366f6083SPeter Grehan */ 172366f6083SPeter Grehan 173366f6083SPeter Grehan /* 174366f6083SPeter Grehan * Slot options are in the form: 175366f6083SPeter Grehan * 176d84882caSNeel Natu * <bus>:<slot>:<func>,<emul>[,<config>] 17799d65389SNeel Natu * <slot>[:<func>],<emul>[,<config>] 178366f6083SPeter Grehan * 179366f6083SPeter Grehan * slot is 0..31 18099d65389SNeel Natu * func is 0..7 181366f6083SPeter Grehan * emul is a string describing the type of PCI device e.g. virtio-net 182366f6083SPeter Grehan * config is an optional string, depending on the device, that can be 183366f6083SPeter Grehan * used for configuration. 184366f6083SPeter Grehan * Examples are: 185366f6083SPeter Grehan * 1,virtio-net,tap0 18699d65389SNeel Natu * 3:0,dummy 187366f6083SPeter Grehan */ 188366f6083SPeter Grehan static void 189366f6083SPeter Grehan pci_parse_slot_usage(char *aopt) 190366f6083SPeter Grehan { 191b05c77ffSNeel Natu 192332eff95SVincenzo Maffione EPRINTLN("Invalid PCI slot info field \"%s\"", aopt); 193366f6083SPeter Grehan } 194366f6083SPeter Grehan 195621b5090SJohn Baldwin /* 196621b5090SJohn Baldwin * Helper function to parse a list of comma-separated options where 197621b5090SJohn Baldwin * each option is formatted as "name[=value]". If no value is 198621b5090SJohn Baldwin * provided, the option is treated as a boolean and is given a value 199621b5090SJohn Baldwin * of true. 200621b5090SJohn Baldwin */ 201621b5090SJohn Baldwin int 202621b5090SJohn Baldwin pci_parse_legacy_config(nvlist_t *nvl, const char *opt) 203621b5090SJohn Baldwin { 204621b5090SJohn Baldwin char *config, *name, *tofree, *value; 205621b5090SJohn Baldwin 206621b5090SJohn Baldwin if (opt == NULL) 207621b5090SJohn Baldwin return (0); 208621b5090SJohn Baldwin 209621b5090SJohn Baldwin config = tofree = strdup(opt); 210621b5090SJohn Baldwin while ((name = strsep(&config, ",")) != NULL) { 211621b5090SJohn Baldwin value = strchr(name, '='); 212621b5090SJohn Baldwin if (value != NULL) { 213621b5090SJohn Baldwin *value = '\0'; 214621b5090SJohn Baldwin value++; 215621b5090SJohn Baldwin set_config_value_node(nvl, name, value); 216621b5090SJohn Baldwin } else 217621b5090SJohn Baldwin set_config_bool_node(nvl, name, true); 218621b5090SJohn Baldwin } 219621b5090SJohn Baldwin free(tofree); 220621b5090SJohn Baldwin return (0); 221621b5090SJohn Baldwin } 222621b5090SJohn Baldwin 223621b5090SJohn Baldwin /* 224621b5090SJohn Baldwin * PCI device configuration is stored in MIBs that encode the device's 225621b5090SJohn Baldwin * location: 226621b5090SJohn Baldwin * 227621b5090SJohn Baldwin * pci.<bus>.<slot>.<func> 228621b5090SJohn Baldwin * 229621b5090SJohn Baldwin * Where "bus", "slot", and "func" are all decimal values without 230621b5090SJohn Baldwin * leading zeroes. Each valid device must have a "device" node which 231621b5090SJohn Baldwin * identifies the driver model of the device. 232621b5090SJohn Baldwin * 233621b5090SJohn Baldwin * Device backends can provide a parser for the "config" string. If 234621b5090SJohn Baldwin * a custom parser is not provided, pci_parse_legacy_config() is used 235621b5090SJohn Baldwin * to parse the string. 236621b5090SJohn Baldwin */ 237b05c77ffSNeel Natu int 238d2bc4816SJohn Baldwin pci_parse_slot(char *opt) 239366f6083SPeter Grehan { 240621b5090SJohn Baldwin char node_name[sizeof("pci.XXX.XX.X")]; 241621b5090SJohn Baldwin struct pci_devemu *pde; 242d84882caSNeel Natu char *emul, *config, *str, *cp; 243d84882caSNeel Natu int error, bnum, snum, fnum; 244621b5090SJohn Baldwin nvlist_t *nvl; 245366f6083SPeter Grehan 246b05c77ffSNeel Natu error = -1; 247d84882caSNeel Natu str = strdup(opt); 24899d65389SNeel Natu 249d84882caSNeel Natu emul = config = NULL; 250d84882caSNeel Natu if ((cp = strchr(str, ',')) != NULL) { 251d84882caSNeel Natu *cp = '\0'; 252d84882caSNeel Natu emul = cp + 1; 253d84882caSNeel Natu if ((cp = strchr(emul, ',')) != NULL) { 254d84882caSNeel Natu *cp = '\0'; 255d84882caSNeel Natu config = cp + 1; 25699d65389SNeel Natu } 257d84882caSNeel Natu } else { 258b05c77ffSNeel Natu pci_parse_slot_usage(opt); 259b05c77ffSNeel Natu goto done; 260366f6083SPeter Grehan } 261366f6083SPeter Grehan 262d84882caSNeel Natu /* <bus>:<slot>:<func> */ 263d84882caSNeel Natu if (sscanf(str, "%d:%d:%d", &bnum, &snum, &fnum) != 3) { 264d84882caSNeel Natu bnum = 0; 265d84882caSNeel Natu /* <slot>:<func> */ 266d84882caSNeel Natu if (sscanf(str, "%d:%d", &snum, &fnum) != 2) { 267d84882caSNeel Natu fnum = 0; 268d84882caSNeel Natu /* <slot> */ 269d84882caSNeel Natu if (sscanf(str, "%d", &snum) != 1) { 270d84882caSNeel Natu snum = -1; 271d84882caSNeel Natu } 272d84882caSNeel Natu } 273d84882caSNeel Natu } 274b05c77ffSNeel Natu 275d84882caSNeel Natu if (bnum < 0 || bnum >= MAXBUSES || snum < 0 || snum >= MAXSLOTS || 276d84882caSNeel Natu fnum < 0 || fnum >= MAXFUNCS) { 277b05c77ffSNeel Natu pci_parse_slot_usage(opt); 278b05c77ffSNeel Natu goto done; 279b05c77ffSNeel Natu } 280b05c77ffSNeel Natu 281621b5090SJohn Baldwin pde = pci_emul_finddev(emul); 282621b5090SJohn Baldwin if (pde == NULL) { 283621b5090SJohn Baldwin EPRINTLN("pci slot %d:%d:%d: unknown device \"%s\"", bnum, snum, 284621b5090SJohn Baldwin fnum, emul); 285b05c77ffSNeel Natu goto done; 286b05c77ffSNeel Natu } 287b05c77ffSNeel Natu 288621b5090SJohn Baldwin snprintf(node_name, sizeof(node_name), "pci.%d.%d.%d", bnum, snum, 289621b5090SJohn Baldwin fnum); 290621b5090SJohn Baldwin nvl = find_config_node(node_name); 291621b5090SJohn Baldwin if (nvl != NULL) { 292621b5090SJohn Baldwin EPRINTLN("pci slot %d:%d:%d already occupied!", bnum, snum, 293621b5090SJohn Baldwin fnum); 294b05c77ffSNeel Natu goto done; 295b05c77ffSNeel Natu } 296621b5090SJohn Baldwin nvl = create_config_node(node_name); 297621b5090SJohn Baldwin if (pde->pe_alias != NULL) 298621b5090SJohn Baldwin set_config_value_node(nvl, "device", pde->pe_alias); 299621b5090SJohn Baldwin else 300621b5090SJohn Baldwin set_config_value_node(nvl, "device", pde->pe_emu); 301b05c77ffSNeel Natu 302621b5090SJohn Baldwin if (pde->pe_legacy_config != NULL) 303621b5090SJohn Baldwin error = pde->pe_legacy_config(nvl, config); 304621b5090SJohn Baldwin else 305621b5090SJohn Baldwin error = pci_parse_legacy_config(nvl, config); 306b05c77ffSNeel Natu done: 307d84882caSNeel Natu free(str); 308b05c77ffSNeel Natu return (error); 309366f6083SPeter Grehan } 310366f6083SPeter Grehan 311657d2158SMarcelo Araujo void 312657d2158SMarcelo Araujo pci_print_supported_devices() 313657d2158SMarcelo Araujo { 314657d2158SMarcelo Araujo struct pci_devemu **pdpp, *pdp; 315657d2158SMarcelo Araujo 316657d2158SMarcelo Araujo SET_FOREACH(pdpp, pci_devemu_set) { 317657d2158SMarcelo Araujo pdp = *pdpp; 318657d2158SMarcelo Araujo printf("%s\n", pdp->pe_emu); 319657d2158SMarcelo Araujo } 320657d2158SMarcelo Araujo } 321657d2158SMarcelo Araujo 322366f6083SPeter Grehan static int 323c9b4e987SNeel Natu pci_valid_pba_offset(struct pci_devinst *pi, uint64_t offset) 324c9b4e987SNeel Natu { 325c9b4e987SNeel Natu 326c9b4e987SNeel Natu if (offset < pi->pi_msix.pba_offset) 327c9b4e987SNeel Natu return (0); 328c9b4e987SNeel Natu 329c9b4e987SNeel Natu if (offset >= pi->pi_msix.pba_offset + pi->pi_msix.pba_size) { 330c9b4e987SNeel Natu return (0); 331c9b4e987SNeel Natu } 332c9b4e987SNeel Natu 333c9b4e987SNeel Natu return (1); 334c9b4e987SNeel Natu } 335c9b4e987SNeel Natu 336c9b4e987SNeel Natu int 337c9b4e987SNeel Natu pci_emul_msix_twrite(struct pci_devinst *pi, uint64_t offset, int size, 338c9b4e987SNeel Natu uint64_t value) 339c9b4e987SNeel Natu { 340c9b4e987SNeel Natu int msix_entry_offset; 341c9b4e987SNeel Natu int tab_index; 342c9b4e987SNeel Natu char *dest; 343c9b4e987SNeel Natu 344c9b4e987SNeel Natu /* support only 4 or 8 byte writes */ 345c9b4e987SNeel Natu if (size != 4 && size != 8) 346c9b4e987SNeel Natu return (-1); 347c9b4e987SNeel Natu 348c9b4e987SNeel Natu /* 349c9b4e987SNeel Natu * Return if table index is beyond what device supports 350c9b4e987SNeel Natu */ 351c9b4e987SNeel Natu tab_index = offset / MSIX_TABLE_ENTRY_SIZE; 352c9b4e987SNeel Natu if (tab_index >= pi->pi_msix.table_count) 353c9b4e987SNeel Natu return (-1); 354c9b4e987SNeel Natu 355c9b4e987SNeel Natu msix_entry_offset = offset % MSIX_TABLE_ENTRY_SIZE; 356c9b4e987SNeel Natu 357c9b4e987SNeel Natu /* support only aligned writes */ 358c9b4e987SNeel Natu if ((msix_entry_offset % size) != 0) 359c9b4e987SNeel Natu return (-1); 360c9b4e987SNeel Natu 361c9b4e987SNeel Natu dest = (char *)(pi->pi_msix.table + tab_index); 362c9b4e987SNeel Natu dest += msix_entry_offset; 363c9b4e987SNeel Natu 364c9b4e987SNeel Natu if (size == 4) 365c9b4e987SNeel Natu *((uint32_t *)dest) = value; 366c9b4e987SNeel Natu else 367c9b4e987SNeel Natu *((uint64_t *)dest) = value; 368c9b4e987SNeel Natu 369c9b4e987SNeel Natu return (0); 370c9b4e987SNeel Natu } 371c9b4e987SNeel Natu 372c9b4e987SNeel Natu uint64_t 373c9b4e987SNeel Natu pci_emul_msix_tread(struct pci_devinst *pi, uint64_t offset, int size) 374c9b4e987SNeel Natu { 375c9b4e987SNeel Natu char *dest; 376c9b4e987SNeel Natu int msix_entry_offset; 377c9b4e987SNeel Natu int tab_index; 378c9b4e987SNeel Natu uint64_t retval = ~0; 379c9b4e987SNeel Natu 3806a52209fSNeel Natu /* 3816a52209fSNeel Natu * The PCI standard only allows 4 and 8 byte accesses to the MSI-X 382463a577bSEitan Adler * table but we also allow 1 byte access to accommodate reads from 3836a52209fSNeel Natu * ddb. 3846a52209fSNeel Natu */ 3856a52209fSNeel Natu if (size != 1 && size != 4 && size != 8) 386c9b4e987SNeel Natu return (retval); 387c9b4e987SNeel Natu 388c9b4e987SNeel Natu msix_entry_offset = offset % MSIX_TABLE_ENTRY_SIZE; 389c9b4e987SNeel Natu 390c9b4e987SNeel Natu /* support only aligned reads */ 391c9b4e987SNeel Natu if ((msix_entry_offset % size) != 0) { 392c9b4e987SNeel Natu return (retval); 393c9b4e987SNeel Natu } 394c9b4e987SNeel Natu 395c9b4e987SNeel Natu tab_index = offset / MSIX_TABLE_ENTRY_SIZE; 396c9b4e987SNeel Natu 397c9b4e987SNeel Natu if (tab_index < pi->pi_msix.table_count) { 398c9b4e987SNeel Natu /* valid MSI-X Table access */ 399c9b4e987SNeel Natu dest = (char *)(pi->pi_msix.table + tab_index); 400c9b4e987SNeel Natu dest += msix_entry_offset; 401c9b4e987SNeel Natu 4026a52209fSNeel Natu if (size == 1) 4036a52209fSNeel Natu retval = *((uint8_t *)dest); 4046a52209fSNeel Natu else if (size == 4) 405c9b4e987SNeel Natu retval = *((uint32_t *)dest); 406c9b4e987SNeel Natu else 407c9b4e987SNeel Natu retval = *((uint64_t *)dest); 408c9b4e987SNeel Natu } else if (pci_valid_pba_offset(pi, offset)) { 409c9b4e987SNeel Natu /* return 0 for PBA access */ 410c9b4e987SNeel Natu retval = 0; 411c9b4e987SNeel Natu } 412c9b4e987SNeel Natu 413c9b4e987SNeel Natu return (retval); 414c9b4e987SNeel Natu } 415c9b4e987SNeel Natu 416aa12663fSNeel Natu int 417aa12663fSNeel Natu pci_msix_table_bar(struct pci_devinst *pi) 418aa12663fSNeel Natu { 419aa12663fSNeel Natu 420aa12663fSNeel Natu if (pi->pi_msix.table != NULL) 421aa12663fSNeel Natu return (pi->pi_msix.table_bar); 422aa12663fSNeel Natu else 423aa12663fSNeel Natu return (-1); 424aa12663fSNeel Natu } 425aa12663fSNeel Natu 426aa12663fSNeel Natu int 427aa12663fSNeel Natu pci_msix_pba_bar(struct pci_devinst *pi) 428aa12663fSNeel Natu { 429aa12663fSNeel Natu 430aa12663fSNeel Natu if (pi->pi_msix.table != NULL) 431aa12663fSNeel Natu return (pi->pi_msix.pba_bar); 432aa12663fSNeel Natu else 433aa12663fSNeel Natu return (-1); 434aa12663fSNeel Natu } 435aa12663fSNeel Natu 436c9b4e987SNeel Natu static int 4374d1e669cSPeter Grehan pci_emul_io_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes, 438366f6083SPeter Grehan uint32_t *eax, void *arg) 439366f6083SPeter Grehan { 440366f6083SPeter Grehan struct pci_devinst *pdi = arg; 441366f6083SPeter Grehan struct pci_devemu *pe = pdi->pi_d; 4424d1e669cSPeter Grehan uint64_t offset; 4434d1e669cSPeter Grehan int i; 444366f6083SPeter Grehan 445366f6083SPeter Grehan for (i = 0; i <= PCI_BARMAX; i++) { 446366f6083SPeter Grehan if (pdi->pi_bar[i].type == PCIBAR_IO && 447366f6083SPeter Grehan port >= pdi->pi_bar[i].addr && 448c9b4e987SNeel Natu port + bytes <= pdi->pi_bar[i].addr + pdi->pi_bar[i].size) { 449366f6083SPeter Grehan offset = port - pdi->pi_bar[i].addr; 450366f6083SPeter Grehan if (in) 4514d1e669cSPeter Grehan *eax = (*pe->pe_barread)(ctx, vcpu, pdi, i, 4524d1e669cSPeter Grehan offset, bytes); 453366f6083SPeter Grehan else 4544d1e669cSPeter Grehan (*pe->pe_barwrite)(ctx, vcpu, pdi, i, offset, 4554d1e669cSPeter Grehan bytes, *eax); 456366f6083SPeter Grehan return (0); 457366f6083SPeter Grehan } 458366f6083SPeter Grehan } 459366f6083SPeter Grehan return (-1); 460366f6083SPeter Grehan } 461366f6083SPeter Grehan 462366f6083SPeter Grehan static int 4634d1e669cSPeter Grehan pci_emul_mem_handler(struct vmctx *ctx, int vcpu, int dir, uint64_t addr, 4644d1e669cSPeter Grehan int size, uint64_t *val, void *arg1, long arg2) 4654d1e669cSPeter Grehan { 4664d1e669cSPeter Grehan struct pci_devinst *pdi = arg1; 4674d1e669cSPeter Grehan struct pci_devemu *pe = pdi->pi_d; 4684d1e669cSPeter Grehan uint64_t offset; 4694d1e669cSPeter Grehan int bidx = (int) arg2; 4704d1e669cSPeter Grehan 4714d1e669cSPeter Grehan assert(bidx <= PCI_BARMAX); 4724d1e669cSPeter Grehan assert(pdi->pi_bar[bidx].type == PCIBAR_MEM32 || 4734d1e669cSPeter Grehan pdi->pi_bar[bidx].type == PCIBAR_MEM64); 4744d1e669cSPeter Grehan assert(addr >= pdi->pi_bar[bidx].addr && 4754d1e669cSPeter Grehan addr + size <= pdi->pi_bar[bidx].addr + pdi->pi_bar[bidx].size); 4764d1e669cSPeter Grehan 4774d1e669cSPeter Grehan offset = addr - pdi->pi_bar[bidx].addr; 4784d1e669cSPeter Grehan 479b6ae8b05STycho Nightingale if (dir == MEM_F_WRITE) { 48067b6ffaaSTycho Nightingale if (size == 8) { 481b6ae8b05STycho Nightingale (*pe->pe_barwrite)(ctx, vcpu, pdi, bidx, offset, 482b6ae8b05STycho Nightingale 4, *val & 0xffffffff); 483b6ae8b05STycho Nightingale (*pe->pe_barwrite)(ctx, vcpu, pdi, bidx, offset + 4, 484b6ae8b05STycho Nightingale 4, *val >> 32); 485b6ae8b05STycho Nightingale } else { 486b6ae8b05STycho Nightingale (*pe->pe_barwrite)(ctx, vcpu, pdi, bidx, offset, 487b6ae8b05STycho Nightingale size, *val); 488b6ae8b05STycho Nightingale } 489b6ae8b05STycho Nightingale } else { 49067b6ffaaSTycho Nightingale if (size == 8) { 491b6ae8b05STycho Nightingale *val = (*pe->pe_barread)(ctx, vcpu, pdi, bidx, 492b6ae8b05STycho Nightingale offset, 4); 493b6ae8b05STycho Nightingale *val |= (*pe->pe_barread)(ctx, vcpu, pdi, bidx, 494b6ae8b05STycho Nightingale offset + 4, 4) << 32; 495b6ae8b05STycho Nightingale } else { 496b6ae8b05STycho Nightingale *val = (*pe->pe_barread)(ctx, vcpu, pdi, bidx, 497b6ae8b05STycho Nightingale offset, size); 498b6ae8b05STycho Nightingale } 499b6ae8b05STycho Nightingale } 5004d1e669cSPeter Grehan 5014d1e669cSPeter Grehan return (0); 5024d1e669cSPeter Grehan } 5034d1e669cSPeter Grehan 5044d1e669cSPeter Grehan 5054d1e669cSPeter Grehan static int 506366f6083SPeter Grehan pci_emul_alloc_resource(uint64_t *baseptr, uint64_t limit, uint64_t size, 507366f6083SPeter Grehan uint64_t *addr) 508366f6083SPeter Grehan { 509366f6083SPeter Grehan uint64_t base; 510366f6083SPeter Grehan 511366f6083SPeter Grehan assert((size & (size - 1)) == 0); /* must be a power of 2 */ 512366f6083SPeter Grehan 513366f6083SPeter Grehan base = roundup2(*baseptr, size); 514366f6083SPeter Grehan 515366f6083SPeter Grehan if (base + size <= limit) { 516366f6083SPeter Grehan *addr = base; 517366f6083SPeter Grehan *baseptr = base + size; 518366f6083SPeter Grehan return (0); 519366f6083SPeter Grehan } else 520366f6083SPeter Grehan return (-1); 521366f6083SPeter Grehan } 522366f6083SPeter Grehan 523028d9311SNeel Natu /* 524028d9311SNeel Natu * Register (or unregister) the MMIO or I/O region associated with the BAR 525028d9311SNeel Natu * register 'idx' of an emulated pci device. 526028d9311SNeel Natu */ 527028d9311SNeel Natu static void 528028d9311SNeel Natu modify_bar_registration(struct pci_devinst *pi, int idx, int registration) 529028d9311SNeel Natu { 530f8a6ec2dSD Scott Phillips struct pci_devemu *pe; 531028d9311SNeel Natu int error; 532028d9311SNeel Natu struct inout_port iop; 533028d9311SNeel Natu struct mem_range mr; 534028d9311SNeel Natu 535f8a6ec2dSD Scott Phillips pe = pi->pi_d; 536028d9311SNeel Natu switch (pi->pi_bar[idx].type) { 537028d9311SNeel Natu case PCIBAR_IO: 538028d9311SNeel Natu bzero(&iop, sizeof(struct inout_port)); 539028d9311SNeel Natu iop.name = pi->pi_name; 540028d9311SNeel Natu iop.port = pi->pi_bar[idx].addr; 541028d9311SNeel Natu iop.size = pi->pi_bar[idx].size; 542028d9311SNeel Natu if (registration) { 543028d9311SNeel Natu iop.flags = IOPORT_F_INOUT; 544028d9311SNeel Natu iop.handler = pci_emul_io_handler; 545028d9311SNeel Natu iop.arg = pi; 546028d9311SNeel Natu error = register_inout(&iop); 547028d9311SNeel Natu } else 548028d9311SNeel Natu error = unregister_inout(&iop); 549f8a6ec2dSD Scott Phillips if (pe->pe_baraddr != NULL) 550f8a6ec2dSD Scott Phillips (*pe->pe_baraddr)(pi->pi_vmctx, pi, idx, registration, 551f8a6ec2dSD Scott Phillips pi->pi_bar[idx].addr); 552028d9311SNeel Natu break; 553028d9311SNeel Natu case PCIBAR_MEM32: 554028d9311SNeel Natu case PCIBAR_MEM64: 555028d9311SNeel Natu bzero(&mr, sizeof(struct mem_range)); 556028d9311SNeel Natu mr.name = pi->pi_name; 557028d9311SNeel Natu mr.base = pi->pi_bar[idx].addr; 558028d9311SNeel Natu mr.size = pi->pi_bar[idx].size; 559028d9311SNeel Natu if (registration) { 560028d9311SNeel Natu mr.flags = MEM_F_RW; 561028d9311SNeel Natu mr.handler = pci_emul_mem_handler; 562028d9311SNeel Natu mr.arg1 = pi; 563028d9311SNeel Natu mr.arg2 = idx; 564028d9311SNeel Natu error = register_mem(&mr); 565028d9311SNeel Natu } else 566028d9311SNeel Natu error = unregister_mem(&mr); 567f8a6ec2dSD Scott Phillips if (pe->pe_baraddr != NULL) 568f8a6ec2dSD Scott Phillips (*pe->pe_baraddr)(pi->pi_vmctx, pi, idx, registration, 569f8a6ec2dSD Scott Phillips pi->pi_bar[idx].addr); 570028d9311SNeel Natu break; 571e47fe318SCorvin Köhne case PCIBAR_ROM: 572e47fe318SCorvin Köhne error = 0; 573e47fe318SCorvin Köhne if (pe->pe_baraddr != NULL) 574e47fe318SCorvin Köhne (*pe->pe_baraddr)(pi->pi_vmctx, pi, idx, registration, 575e47fe318SCorvin Köhne pi->pi_bar[idx].addr); 576e47fe318SCorvin Köhne break; 577028d9311SNeel Natu default: 578028d9311SNeel Natu error = EINVAL; 579028d9311SNeel Natu break; 580028d9311SNeel Natu } 581028d9311SNeel Natu assert(error == 0); 582028d9311SNeel Natu } 583028d9311SNeel Natu 584028d9311SNeel Natu static void 585028d9311SNeel Natu unregister_bar(struct pci_devinst *pi, int idx) 586028d9311SNeel Natu { 587028d9311SNeel Natu 588028d9311SNeel Natu modify_bar_registration(pi, idx, 0); 589028d9311SNeel Natu } 590028d9311SNeel Natu 591028d9311SNeel Natu static void 592028d9311SNeel Natu register_bar(struct pci_devinst *pi, int idx) 593028d9311SNeel Natu { 594028d9311SNeel Natu 595028d9311SNeel Natu modify_bar_registration(pi, idx, 1); 596028d9311SNeel Natu } 597028d9311SNeel Natu 598e47fe318SCorvin Köhne /* Is the ROM enabled for the emulated pci device? */ 599e47fe318SCorvin Köhne static int 600e47fe318SCorvin Köhne romen(struct pci_devinst *pi) 601e47fe318SCorvin Köhne { 602e47fe318SCorvin Köhne return (pi->pi_bar[PCI_ROM_IDX].lobits & PCIM_BIOS_ENABLE) == 603e47fe318SCorvin Köhne PCIM_BIOS_ENABLE; 604e47fe318SCorvin Köhne } 605e47fe318SCorvin Köhne 606028d9311SNeel Natu /* Are we decoding i/o port accesses for the emulated pci device? */ 607028d9311SNeel Natu static int 608028d9311SNeel Natu porten(struct pci_devinst *pi) 609028d9311SNeel Natu { 610028d9311SNeel Natu uint16_t cmd; 611028d9311SNeel Natu 612028d9311SNeel Natu cmd = pci_get_cfgdata16(pi, PCIR_COMMAND); 613028d9311SNeel Natu 614028d9311SNeel Natu return (cmd & PCIM_CMD_PORTEN); 615028d9311SNeel Natu } 616028d9311SNeel Natu 617028d9311SNeel Natu /* Are we decoding memory accesses for the emulated pci device? */ 618028d9311SNeel Natu static int 619028d9311SNeel Natu memen(struct pci_devinst *pi) 620028d9311SNeel Natu { 621028d9311SNeel Natu uint16_t cmd; 622028d9311SNeel Natu 623028d9311SNeel Natu cmd = pci_get_cfgdata16(pi, PCIR_COMMAND); 624028d9311SNeel Natu 625028d9311SNeel Natu return (cmd & PCIM_CMD_MEMEN); 626028d9311SNeel Natu } 627028d9311SNeel Natu 628028d9311SNeel Natu /* 629028d9311SNeel Natu * Update the MMIO or I/O address that is decoded by the BAR register. 630028d9311SNeel Natu * 631028d9311SNeel Natu * If the pci device has enabled the address space decoding then intercept 632028d9311SNeel Natu * the address range decoded by the BAR register. 633028d9311SNeel Natu */ 634028d9311SNeel Natu static void 635028d9311SNeel Natu update_bar_address(struct pci_devinst *pi, uint64_t addr, int idx, int type) 636028d9311SNeel Natu { 637028d9311SNeel Natu int decode; 638028d9311SNeel Natu 639028d9311SNeel Natu if (pi->pi_bar[idx].type == PCIBAR_IO) 640028d9311SNeel Natu decode = porten(pi); 641028d9311SNeel Natu else 642028d9311SNeel Natu decode = memen(pi); 643028d9311SNeel Natu 644028d9311SNeel Natu if (decode) 645028d9311SNeel Natu unregister_bar(pi, idx); 646028d9311SNeel Natu 647028d9311SNeel Natu switch (type) { 648028d9311SNeel Natu case PCIBAR_IO: 649028d9311SNeel Natu case PCIBAR_MEM32: 650028d9311SNeel Natu pi->pi_bar[idx].addr = addr; 651028d9311SNeel Natu break; 652028d9311SNeel Natu case PCIBAR_MEM64: 653028d9311SNeel Natu pi->pi_bar[idx].addr &= ~0xffffffffUL; 654028d9311SNeel Natu pi->pi_bar[idx].addr |= addr; 655028d9311SNeel Natu break; 656028d9311SNeel Natu case PCIBAR_MEMHI64: 657028d9311SNeel Natu pi->pi_bar[idx].addr &= 0xffffffff; 658028d9311SNeel Natu pi->pi_bar[idx].addr |= addr; 659028d9311SNeel Natu break; 660028d9311SNeel Natu default: 661028d9311SNeel Natu assert(0); 662028d9311SNeel Natu } 663028d9311SNeel Natu 664028d9311SNeel Natu if (decode) 665028d9311SNeel Natu register_bar(pi, idx); 666028d9311SNeel Natu } 667028d9311SNeel Natu 6684d1e669cSPeter Grehan int 669038f5c7bSKonstantin Belousov pci_emul_alloc_bar(struct pci_devinst *pdi, int idx, enum pcibar_type type, 670038f5c7bSKonstantin Belousov uint64_t size) 671366f6083SPeter Grehan { 672e47fe318SCorvin Köhne assert((type == PCIBAR_ROM) || (idx >= 0 && idx <= PCI_BARMAX)); 673e47fe318SCorvin Köhne assert((type != PCIBAR_ROM) || (idx == PCI_ROM_IDX)); 674366f6083SPeter Grehan 675366f6083SPeter Grehan if ((size & (size - 1)) != 0) 676366f6083SPeter Grehan size = 1UL << flsl(size); /* round up to a power of 2 */ 677366f6083SPeter Grehan 678028d9311SNeel Natu /* Enforce minimum BAR sizes required by the PCI standard */ 679028d9311SNeel Natu if (type == PCIBAR_IO) { 680028d9311SNeel Natu if (size < 4) 681028d9311SNeel Natu size = 4; 682e47fe318SCorvin Köhne } else if (type == PCIBAR_ROM) { 683e47fe318SCorvin Köhne if (size < ~PCIM_BIOS_ADDR_MASK + 1) 684e47fe318SCorvin Köhne size = ~PCIM_BIOS_ADDR_MASK + 1; 685028d9311SNeel Natu } else { 686028d9311SNeel Natu if (size < 16) 687028d9311SNeel Natu size = 16; 688028d9311SNeel Natu } 689028d9311SNeel Natu 69001f9362eSCorvin Köhne /* 69101f9362eSCorvin Köhne * To reduce fragmentation of the MMIO space, we allocate the BARs by 69201f9362eSCorvin Köhne * size. Therefore, don't allocate the BAR yet. We create a list of all 69301f9362eSCorvin Köhne * BAR allocation which is sorted by BAR size. When all PCI devices are 69401f9362eSCorvin Köhne * initialized, we will assign an address to the BARs. 69501f9362eSCorvin Köhne */ 69601f9362eSCorvin Köhne 69701f9362eSCorvin Köhne /* create a new list entry */ 69801f9362eSCorvin Köhne struct pci_bar_allocation *const new_bar = malloc(sizeof(*new_bar)); 69901f9362eSCorvin Köhne memset(new_bar, 0, sizeof(*new_bar)); 70001f9362eSCorvin Köhne new_bar->pdi = pdi; 70101f9362eSCorvin Köhne new_bar->idx = idx; 70201f9362eSCorvin Köhne new_bar->type = type; 70301f9362eSCorvin Köhne new_bar->size = size; 70401f9362eSCorvin Köhne 70501f9362eSCorvin Köhne /* 70601f9362eSCorvin Köhne * Search for a BAR which size is lower than the size of our newly 70701f9362eSCorvin Köhne * allocated BAR. 70801f9362eSCorvin Köhne */ 70901f9362eSCorvin Köhne struct pci_bar_allocation *bar = NULL; 71001f9362eSCorvin Köhne TAILQ_FOREACH(bar, &pci_bars, chain) { 71101f9362eSCorvin Köhne if (bar->size < size) { 71201f9362eSCorvin Köhne break; 71301f9362eSCorvin Köhne } 71401f9362eSCorvin Köhne } 71501f9362eSCorvin Köhne 71601f9362eSCorvin Köhne if (bar == NULL) { 71701f9362eSCorvin Köhne /* 71801f9362eSCorvin Köhne * Either the list is empty or new BAR is the smallest BAR of 71901f9362eSCorvin Köhne * the list. Append it to the end of our list. 72001f9362eSCorvin Köhne */ 72101f9362eSCorvin Köhne TAILQ_INSERT_TAIL(&pci_bars, new_bar, chain); 72201f9362eSCorvin Köhne } else { 72301f9362eSCorvin Köhne /* 72401f9362eSCorvin Köhne * The found BAR is smaller than our new BAR. For that reason, 72501f9362eSCorvin Köhne * insert our new BAR before the found BAR. 72601f9362eSCorvin Köhne */ 72701f9362eSCorvin Köhne TAILQ_INSERT_BEFORE(bar, new_bar, chain); 72801f9362eSCorvin Köhne } 72901f9362eSCorvin Köhne 73001f9362eSCorvin Köhne /* 73101f9362eSCorvin Köhne * pci_passthru devices synchronize their physical and virtual command 73201f9362eSCorvin Köhne * register on init. For that reason, the virtual cmd reg should be 73301f9362eSCorvin Köhne * updated as early as possible. 73401f9362eSCorvin Köhne */ 73501f9362eSCorvin Köhne uint16_t enbit = 0; 73601f9362eSCorvin Köhne switch (type) { 73701f9362eSCorvin Köhne case PCIBAR_IO: 73801f9362eSCorvin Köhne enbit = PCIM_CMD_PORTEN; 73901f9362eSCorvin Köhne break; 74001f9362eSCorvin Köhne case PCIBAR_MEM64: 74101f9362eSCorvin Köhne case PCIBAR_MEM32: 74201f9362eSCorvin Köhne enbit = PCIM_CMD_MEMEN; 74301f9362eSCorvin Köhne break; 74401f9362eSCorvin Köhne default: 74501f9362eSCorvin Köhne enbit = 0; 74601f9362eSCorvin Köhne break; 74701f9362eSCorvin Köhne } 74801f9362eSCorvin Köhne 74901f9362eSCorvin Köhne const uint16_t cmd = pci_get_cfgdata16(pdi, PCIR_COMMAND); 75001f9362eSCorvin Köhne pci_set_cfgdata16(pdi, PCIR_COMMAND, cmd | enbit); 75101f9362eSCorvin Köhne 75201f9362eSCorvin Köhne return (0); 75301f9362eSCorvin Köhne } 75401f9362eSCorvin Köhne 75501f9362eSCorvin Köhne static int 75601f9362eSCorvin Köhne pci_emul_assign_bar(struct pci_devinst *const pdi, const int idx, 75701f9362eSCorvin Köhne const enum pcibar_type type, const uint64_t size) 75801f9362eSCorvin Köhne { 75901f9362eSCorvin Köhne int error; 76001f9362eSCorvin Köhne uint64_t *baseptr, limit, addr, mask, lobits, bar; 76101f9362eSCorvin Köhne 762366f6083SPeter Grehan switch (type) { 763366f6083SPeter Grehan case PCIBAR_NONE: 764366f6083SPeter Grehan baseptr = NULL; 76501f9362eSCorvin Köhne addr = mask = lobits = 0; 766366f6083SPeter Grehan break; 767366f6083SPeter Grehan case PCIBAR_IO: 768366f6083SPeter Grehan baseptr = &pci_emul_iobase; 769366f6083SPeter Grehan limit = PCI_EMUL_IOLIMIT; 770366f6083SPeter Grehan mask = PCIM_BAR_IO_BASE; 771366f6083SPeter Grehan lobits = PCIM_BAR_IO_SPACE; 772366f6083SPeter Grehan break; 773366f6083SPeter Grehan case PCIBAR_MEM64: 774366f6083SPeter Grehan /* 775366f6083SPeter Grehan * XXX 776366f6083SPeter Grehan * Some drivers do not work well if the 64-bit BAR is allocated 777366f6083SPeter Grehan * above 4GB. Allow for this by allocating small requests under 778366f6083SPeter Grehan * 4GB unless then allocation size is larger than some arbitrary 779670b364bSKonstantin Belousov * number (128MB currently). 780366f6083SPeter Grehan */ 781670b364bSKonstantin Belousov if (size > 128 * 1024 * 1024) { 782366f6083SPeter Grehan baseptr = &pci_emul_membase64; 7839922872bSKonstantin Belousov limit = pci_emul_memlim64; 784366f6083SPeter Grehan mask = PCIM_BAR_MEM_BASE; 785366f6083SPeter Grehan lobits = PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_64 | 786366f6083SPeter Grehan PCIM_BAR_MEM_PREFETCH; 78725d4944eSNeel Natu } else { 78825d4944eSNeel Natu baseptr = &pci_emul_membase32; 78925d4944eSNeel Natu limit = PCI_EMUL_MEMLIMIT32; 79025d4944eSNeel Natu mask = PCIM_BAR_MEM_BASE; 79125d4944eSNeel Natu lobits = PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_64; 792366f6083SPeter Grehan } 79325d4944eSNeel Natu break; 794366f6083SPeter Grehan case PCIBAR_MEM32: 795366f6083SPeter Grehan baseptr = &pci_emul_membase32; 796366f6083SPeter Grehan limit = PCI_EMUL_MEMLIMIT32; 797366f6083SPeter Grehan mask = PCIM_BAR_MEM_BASE; 798366f6083SPeter Grehan lobits = PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_32; 799366f6083SPeter Grehan break; 800e47fe318SCorvin Köhne case PCIBAR_ROM: 801e47fe318SCorvin Köhne /* do not claim memory for ROM. OVMF will do it for us. */ 802e47fe318SCorvin Köhne baseptr = NULL; 803e47fe318SCorvin Köhne limit = 0; 804e47fe318SCorvin Köhne mask = PCIM_BIOS_ADDR_MASK; 805e47fe318SCorvin Köhne lobits = 0; 806e47fe318SCorvin Köhne break; 807366f6083SPeter Grehan default: 808366f6083SPeter Grehan printf("pci_emul_alloc_base: invalid bar type %d\n", type); 809366f6083SPeter Grehan assert(0); 810366f6083SPeter Grehan } 811366f6083SPeter Grehan 812366f6083SPeter Grehan if (baseptr != NULL) { 813366f6083SPeter Grehan error = pci_emul_alloc_resource(baseptr, limit, size, &addr); 814366f6083SPeter Grehan if (error != 0) 815366f6083SPeter Grehan return (error); 816366f6083SPeter Grehan } 817366f6083SPeter Grehan 818366f6083SPeter Grehan pdi->pi_bar[idx].type = type; 819366f6083SPeter Grehan pdi->pi_bar[idx].addr = addr; 820366f6083SPeter Grehan pdi->pi_bar[idx].size = size; 821e87a6f3eSCorvin Köhne /* 822e87a6f3eSCorvin Köhne * passthru devices are using same lobits as physical device they set 823e87a6f3eSCorvin Köhne * this property 824e87a6f3eSCorvin Köhne */ 825e87a6f3eSCorvin Köhne if (pdi->pi_bar[idx].lobits != 0) { 826e87a6f3eSCorvin Köhne lobits = pdi->pi_bar[idx].lobits; 827e87a6f3eSCorvin Köhne } else { 828e87a6f3eSCorvin Köhne pdi->pi_bar[idx].lobits = lobits; 829e87a6f3eSCorvin Köhne } 830366f6083SPeter Grehan 831366f6083SPeter Grehan /* Initialize the BAR register in config space */ 832366f6083SPeter Grehan bar = (addr & mask) | lobits; 833366f6083SPeter Grehan pci_set_cfgdata32(pdi, PCIR_BAR(idx), bar); 834366f6083SPeter Grehan 835366f6083SPeter Grehan if (type == PCIBAR_MEM64) { 836366f6083SPeter Grehan assert(idx + 1 <= PCI_BARMAX); 837366f6083SPeter Grehan pdi->pi_bar[idx + 1].type = PCIBAR_MEMHI64; 838366f6083SPeter Grehan pci_set_cfgdata32(pdi, PCIR_BAR(idx + 1), bar >> 32); 839366f6083SPeter Grehan } 840366f6083SPeter Grehan 841e47fe318SCorvin Köhne if (type != PCIBAR_ROM) { 842028d9311SNeel Natu register_bar(pdi, idx); 843e47fe318SCorvin Köhne } 844e47fe318SCorvin Köhne 845e47fe318SCorvin Köhne return (0); 846e47fe318SCorvin Köhne } 847e47fe318SCorvin Köhne 848e47fe318SCorvin Köhne int 849e47fe318SCorvin Köhne pci_emul_alloc_rom(struct pci_devinst *const pdi, const uint64_t size, 850e47fe318SCorvin Köhne void **const addr) 851e47fe318SCorvin Köhne { 852e47fe318SCorvin Köhne /* allocate ROM space once on first call */ 853e47fe318SCorvin Köhne if (pci_emul_rombase == 0) { 854e47fe318SCorvin Köhne pci_emul_rombase = vm_create_devmem(pdi->pi_vmctx, VM_PCIROM, 855e47fe318SCorvin Köhne "pcirom", PCI_EMUL_ROMSIZE); 856e47fe318SCorvin Köhne if (pci_emul_rombase == MAP_FAILED) { 857e47fe318SCorvin Köhne warnx("%s: failed to create rom segment", __func__); 858e47fe318SCorvin Köhne return (-1); 859e47fe318SCorvin Köhne } 860e47fe318SCorvin Köhne pci_emul_romlim = pci_emul_rombase + PCI_EMUL_ROMSIZE; 861e47fe318SCorvin Köhne pci_emul_romoffset = 0; 862e47fe318SCorvin Köhne } 863e47fe318SCorvin Köhne 864e47fe318SCorvin Köhne /* ROM size should be a power of 2 and greater than 2 KB */ 865e47fe318SCorvin Köhne const uint64_t rom_size = MAX(1UL << flsl(size), 866e47fe318SCorvin Köhne ~PCIM_BIOS_ADDR_MASK + 1); 867e47fe318SCorvin Köhne 868e47fe318SCorvin Köhne /* check if ROM fits into ROM space */ 869e47fe318SCorvin Köhne if (pci_emul_romoffset + rom_size > PCI_EMUL_ROMSIZE) { 870e47fe318SCorvin Köhne warnx("%s: no space left in rom segment:", __func__); 871e47fe318SCorvin Köhne warnx("%16lu bytes left", 872e47fe318SCorvin Köhne PCI_EMUL_ROMSIZE - pci_emul_romoffset); 873e47fe318SCorvin Köhne warnx("%16lu bytes required by %d/%d/%d", rom_size, pdi->pi_bus, 874e47fe318SCorvin Köhne pdi->pi_slot, pdi->pi_func); 875e47fe318SCorvin Köhne return (-1); 876e47fe318SCorvin Köhne } 877e47fe318SCorvin Köhne 878e47fe318SCorvin Köhne /* allocate ROM BAR */ 879e47fe318SCorvin Köhne const int error = pci_emul_alloc_bar(pdi, PCI_ROM_IDX, PCIBAR_ROM, 880e47fe318SCorvin Köhne rom_size); 881e47fe318SCorvin Köhne if (error) 882e47fe318SCorvin Köhne return error; 883e47fe318SCorvin Köhne 884e47fe318SCorvin Köhne /* return address */ 885e47fe318SCorvin Köhne *addr = pci_emul_rombase + pci_emul_romoffset; 886e47fe318SCorvin Köhne 887e47fe318SCorvin Köhne /* save offset into ROM Space */ 888e47fe318SCorvin Köhne pdi->pi_romoffset = pci_emul_romoffset; 889e47fe318SCorvin Köhne 890e47fe318SCorvin Köhne /* increase offset for next ROM */ 891e47fe318SCorvin Köhne pci_emul_romoffset += rom_size; 892366f6083SPeter Grehan 893366f6083SPeter Grehan return (0); 894366f6083SPeter Grehan } 895366f6083SPeter Grehan 896366f6083SPeter Grehan #define CAP_START_OFFSET 0x40 897366f6083SPeter Grehan static int 898366f6083SPeter Grehan pci_emul_add_capability(struct pci_devinst *pi, u_char *capdata, int caplen) 899366f6083SPeter Grehan { 900a96b8b80SJohn Baldwin int i, capoff, reallen; 901366f6083SPeter Grehan uint16_t sts; 902366f6083SPeter Grehan 903a96b8b80SJohn Baldwin assert(caplen > 0); 904366f6083SPeter Grehan 905366f6083SPeter Grehan reallen = roundup2(caplen, 4); /* dword aligned */ 906366f6083SPeter Grehan 907366f6083SPeter Grehan sts = pci_get_cfgdata16(pi, PCIR_STATUS); 908a96b8b80SJohn Baldwin if ((sts & PCIM_STATUS_CAPPRESENT) == 0) 909366f6083SPeter Grehan capoff = CAP_START_OFFSET; 910a96b8b80SJohn Baldwin else 911a96b8b80SJohn Baldwin capoff = pi->pi_capend + 1; 912366f6083SPeter Grehan 913366f6083SPeter Grehan /* Check if we have enough space */ 914a96b8b80SJohn Baldwin if (capoff + reallen > PCI_REGMAX + 1) 915366f6083SPeter Grehan return (-1); 916366f6083SPeter Grehan 917a96b8b80SJohn Baldwin /* Set the previous capability pointer */ 918a96b8b80SJohn Baldwin if ((sts & PCIM_STATUS_CAPPRESENT) == 0) { 919a96b8b80SJohn Baldwin pci_set_cfgdata8(pi, PCIR_CAP_PTR, capoff); 920a96b8b80SJohn Baldwin pci_set_cfgdata16(pi, PCIR_STATUS, sts|PCIM_STATUS_CAPPRESENT); 921a96b8b80SJohn Baldwin } else 922a96b8b80SJohn Baldwin pci_set_cfgdata8(pi, pi->pi_prevcap + 1, capoff); 923a96b8b80SJohn Baldwin 924366f6083SPeter Grehan /* Copy the capability */ 925366f6083SPeter Grehan for (i = 0; i < caplen; i++) 926366f6083SPeter Grehan pci_set_cfgdata8(pi, capoff + i, capdata[i]); 927366f6083SPeter Grehan 928366f6083SPeter Grehan /* Set the next capability pointer */ 929a96b8b80SJohn Baldwin pci_set_cfgdata8(pi, capoff + 1, 0); 930366f6083SPeter Grehan 931a96b8b80SJohn Baldwin pi->pi_prevcap = capoff; 932a96b8b80SJohn Baldwin pi->pi_capend = capoff + reallen - 1; 933366f6083SPeter Grehan return (0); 934366f6083SPeter Grehan } 935366f6083SPeter Grehan 936366f6083SPeter Grehan static struct pci_devemu * 937621b5090SJohn Baldwin pci_emul_finddev(const char *name) 938366f6083SPeter Grehan { 939366f6083SPeter Grehan struct pci_devemu **pdpp, *pdp; 940366f6083SPeter Grehan 941366f6083SPeter Grehan SET_FOREACH(pdpp, pci_devemu_set) { 942366f6083SPeter Grehan pdp = *pdpp; 943366f6083SPeter Grehan if (!strcmp(pdp->pe_emu, name)) { 944366f6083SPeter Grehan return (pdp); 945366f6083SPeter Grehan } 946366f6083SPeter Grehan } 947366f6083SPeter Grehan 948366f6083SPeter Grehan return (NULL); 949366f6083SPeter Grehan } 950366f6083SPeter Grehan 951a38e2a64SPeter Grehan static int 952d84882caSNeel Natu pci_emul_init(struct vmctx *ctx, struct pci_devemu *pde, int bus, int slot, 953d84882caSNeel Natu int func, struct funcinfo *fi) 954366f6083SPeter Grehan { 955366f6083SPeter Grehan struct pci_devinst *pdi; 956a38e2a64SPeter Grehan int err; 957a38e2a64SPeter Grehan 958994f858aSXin LI pdi = calloc(1, sizeof(struct pci_devinst)); 959366f6083SPeter Grehan 960366f6083SPeter Grehan pdi->pi_vmctx = ctx; 961d84882caSNeel Natu pdi->pi_bus = bus; 962366f6083SPeter Grehan pdi->pi_slot = slot; 96399d65389SNeel Natu pdi->pi_func = func; 9643cbf3585SJohn Baldwin pthread_mutex_init(&pdi->pi_lintr.lock, NULL); 9653cbf3585SJohn Baldwin pdi->pi_lintr.pin = 0; 9663cbf3585SJohn Baldwin pdi->pi_lintr.state = IDLE; 967b3e9732aSJohn Baldwin pdi->pi_lintr.pirq_pin = 0; 9683cbf3585SJohn Baldwin pdi->pi_lintr.ioapic_irq = 0; 969366f6083SPeter Grehan pdi->pi_d = pde; 970366f6083SPeter Grehan snprintf(pdi->pi_name, PI_NAMESZ, "%s-pci-%d", pde->pe_emu, slot); 971366f6083SPeter Grehan 972366f6083SPeter Grehan /* Disable legacy interrupts */ 973366f6083SPeter Grehan pci_set_cfgdata8(pdi, PCIR_INTLINE, 255); 974366f6083SPeter Grehan pci_set_cfgdata8(pdi, PCIR_INTPIN, 0); 975366f6083SPeter Grehan 9762729c9bbSJohn Baldwin pci_set_cfgdata8(pdi, PCIR_COMMAND, PCIM_CMD_BUSMASTEREN); 977366f6083SPeter Grehan 978621b5090SJohn Baldwin err = (*pde->pe_init)(ctx, pdi, fi->fi_config); 979d84882caSNeel Natu if (err == 0) 980d84882caSNeel Natu fi->fi_devi = pdi; 981d84882caSNeel Natu else 982366f6083SPeter Grehan free(pdi); 983a38e2a64SPeter Grehan 984a38e2a64SPeter Grehan return (err); 985366f6083SPeter Grehan } 986366f6083SPeter Grehan 987366f6083SPeter Grehan void 988366f6083SPeter Grehan pci_populate_msicap(struct msicap *msicap, int msgnum, int nextptr) 989366f6083SPeter Grehan { 990366f6083SPeter Grehan int mmc; 991366f6083SPeter Grehan 992366f6083SPeter Grehan /* Number of msi messages must be a power of 2 between 1 and 32 */ 993366f6083SPeter Grehan assert((msgnum & (msgnum - 1)) == 0 && msgnum >= 1 && msgnum <= 32); 994366f6083SPeter Grehan mmc = ffs(msgnum) - 1; 995366f6083SPeter Grehan 996366f6083SPeter Grehan bzero(msicap, sizeof(struct msicap)); 997366f6083SPeter Grehan msicap->capid = PCIY_MSI; 998366f6083SPeter Grehan msicap->nextptr = nextptr; 999366f6083SPeter Grehan msicap->msgctrl = PCIM_MSICTRL_64BIT | (mmc << 1); 1000366f6083SPeter Grehan } 1001366f6083SPeter Grehan 1002366f6083SPeter Grehan int 1003366f6083SPeter Grehan pci_emul_add_msicap(struct pci_devinst *pi, int msgnum) 1004366f6083SPeter Grehan { 1005366f6083SPeter Grehan struct msicap msicap; 1006366f6083SPeter Grehan 1007366f6083SPeter Grehan pci_populate_msicap(&msicap, msgnum, 0); 1008366f6083SPeter Grehan 1009366f6083SPeter Grehan return (pci_emul_add_capability(pi, (u_char *)&msicap, sizeof(msicap))); 1010366f6083SPeter Grehan } 1011366f6083SPeter Grehan 1012c9b4e987SNeel Natu static void 1013c9b4e987SNeel Natu pci_populate_msixcap(struct msixcap *msixcap, int msgnum, int barnum, 1014a96b8b80SJohn Baldwin uint32_t msix_tab_size) 1015c9b4e987SNeel Natu { 1016c9b4e987SNeel Natu 1017c9b4e987SNeel Natu assert(msix_tab_size % 4096 == 0); 1018c9b4e987SNeel Natu 1019c9b4e987SNeel Natu bzero(msixcap, sizeof(struct msixcap)); 1020c9b4e987SNeel Natu msixcap->capid = PCIY_MSIX; 1021c9b4e987SNeel Natu 1022c9b4e987SNeel Natu /* 1023c9b4e987SNeel Natu * Message Control Register, all fields set to 1024c9b4e987SNeel Natu * zero except for the Table Size. 1025c9b4e987SNeel Natu * Note: Table size N is encoded as N-1 1026c9b4e987SNeel Natu */ 1027c9b4e987SNeel Natu msixcap->msgctrl = msgnum - 1; 1028c9b4e987SNeel Natu 1029c9b4e987SNeel Natu /* 1030c9b4e987SNeel Natu * MSI-X BAR setup: 1031c9b4e987SNeel Natu * - MSI-X table start at offset 0 1032c9b4e987SNeel Natu * - PBA table starts at a 4K aligned offset after the MSI-X table 1033c9b4e987SNeel Natu */ 1034c9b4e987SNeel Natu msixcap->table_info = barnum & PCIM_MSIX_BIR_MASK; 1035c9b4e987SNeel Natu msixcap->pba_info = msix_tab_size | (barnum & PCIM_MSIX_BIR_MASK); 1036c9b4e987SNeel Natu } 1037c9b4e987SNeel Natu 1038c9b4e987SNeel Natu static void 1039c9b4e987SNeel Natu pci_msix_table_init(struct pci_devinst *pi, int table_entries) 1040c9b4e987SNeel Natu { 1041c9b4e987SNeel Natu int i, table_size; 1042c9b4e987SNeel Natu 1043c9b4e987SNeel Natu assert(table_entries > 0); 1044c9b4e987SNeel Natu assert(table_entries <= MAX_MSIX_TABLE_ENTRIES); 1045c9b4e987SNeel Natu 1046c9b4e987SNeel Natu table_size = table_entries * MSIX_TABLE_ENTRY_SIZE; 1047994f858aSXin LI pi->pi_msix.table = calloc(1, table_size); 1048c9b4e987SNeel Natu 1049c9b4e987SNeel Natu /* set mask bit of vector control register */ 1050c9b4e987SNeel Natu for (i = 0; i < table_entries; i++) 1051c9b4e987SNeel Natu pi->pi_msix.table[i].vector_control |= PCIM_MSIX_VCTRL_MASK; 1052c9b4e987SNeel Natu } 1053c9b4e987SNeel Natu 1054c9b4e987SNeel Natu int 1055c9b4e987SNeel Natu pci_emul_add_msixcap(struct pci_devinst *pi, int msgnum, int barnum) 1056c9b4e987SNeel Natu { 1057c9b4e987SNeel Natu uint32_t tab_size; 1058c9b4e987SNeel Natu struct msixcap msixcap; 1059c9b4e987SNeel Natu 1060c9b4e987SNeel Natu assert(msgnum >= 1 && msgnum <= MAX_MSIX_TABLE_ENTRIES); 1061c9b4e987SNeel Natu assert(barnum >= 0 && barnum <= PCIR_MAX_BAR_0); 1062c9b4e987SNeel Natu 1063c9b4e987SNeel Natu tab_size = msgnum * MSIX_TABLE_ENTRY_SIZE; 1064c9b4e987SNeel Natu 1065c9b4e987SNeel Natu /* Align table size to nearest 4K */ 1066c9b4e987SNeel Natu tab_size = roundup2(tab_size, 4096); 1067c9b4e987SNeel Natu 1068c9b4e987SNeel Natu pi->pi_msix.table_bar = barnum; 1069c9b4e987SNeel Natu pi->pi_msix.pba_bar = barnum; 1070c9b4e987SNeel Natu pi->pi_msix.table_offset = 0; 1071c9b4e987SNeel Natu pi->pi_msix.table_count = msgnum; 1072c9b4e987SNeel Natu pi->pi_msix.pba_offset = tab_size; 10737a902ec0SNeel Natu pi->pi_msix.pba_size = PBA_SIZE(msgnum); 1074c9b4e987SNeel Natu 1075c9b4e987SNeel Natu pci_msix_table_init(pi, msgnum); 1076c9b4e987SNeel Natu 1077a96b8b80SJohn Baldwin pci_populate_msixcap(&msixcap, msgnum, barnum, tab_size); 1078c9b4e987SNeel Natu 1079c9b4e987SNeel Natu /* allocate memory for MSI-X Table and PBA */ 1080c9b4e987SNeel Natu pci_emul_alloc_bar(pi, barnum, PCIBAR_MEM32, 1081c9b4e987SNeel Natu tab_size + pi->pi_msix.pba_size); 1082c9b4e987SNeel Natu 1083c9b4e987SNeel Natu return (pci_emul_add_capability(pi, (u_char *)&msixcap, 1084c9b4e987SNeel Natu sizeof(msixcap))); 1085c9b4e987SNeel Natu } 1086c9b4e987SNeel Natu 108721368498SPeter Grehan static void 1088cd942e0fSPeter Grehan msixcap_cfgwrite(struct pci_devinst *pi, int capoff, int offset, 1089cd942e0fSPeter Grehan int bytes, uint32_t val) 1090cd942e0fSPeter Grehan { 1091cd942e0fSPeter Grehan uint16_t msgctrl, rwmask; 1092d74fdc6aSMarcelo Araujo int off; 1093cd942e0fSPeter Grehan 1094cd942e0fSPeter Grehan off = offset - capoff; 1095cd942e0fSPeter Grehan /* Message Control Register */ 1096cd942e0fSPeter Grehan if (off == 2 && bytes == 2) { 1097cd942e0fSPeter Grehan rwmask = PCIM_MSIXCTRL_MSIX_ENABLE | PCIM_MSIXCTRL_FUNCTION_MASK; 1098cd942e0fSPeter Grehan msgctrl = pci_get_cfgdata16(pi, offset); 1099cd942e0fSPeter Grehan msgctrl &= ~rwmask; 1100cd942e0fSPeter Grehan msgctrl |= val & rwmask; 1101cd942e0fSPeter Grehan val = msgctrl; 1102cd942e0fSPeter Grehan 1103cd942e0fSPeter Grehan pi->pi_msix.enabled = val & PCIM_MSIXCTRL_MSIX_ENABLE; 1104c9b4e987SNeel Natu pi->pi_msix.function_mask = val & PCIM_MSIXCTRL_FUNCTION_MASK; 11053cbf3585SJohn Baldwin pci_lintr_update(pi); 1106cd942e0fSPeter Grehan } 1107cd942e0fSPeter Grehan 1108cd942e0fSPeter Grehan CFGWRITE(pi, offset, val, bytes); 1109cd942e0fSPeter Grehan } 1110cd942e0fSPeter Grehan 111121368498SPeter Grehan static void 1112366f6083SPeter Grehan msicap_cfgwrite(struct pci_devinst *pi, int capoff, int offset, 1113366f6083SPeter Grehan int bytes, uint32_t val) 1114366f6083SPeter Grehan { 1115366f6083SPeter Grehan uint16_t msgctrl, rwmask, msgdata, mme; 1116366f6083SPeter Grehan uint32_t addrlo; 1117366f6083SPeter Grehan 1118366f6083SPeter Grehan /* 1119366f6083SPeter Grehan * If guest is writing to the message control register make sure 1120366f6083SPeter Grehan * we do not overwrite read-only fields. 1121366f6083SPeter Grehan */ 1122366f6083SPeter Grehan if ((offset - capoff) == 2 && bytes == 2) { 1123366f6083SPeter Grehan rwmask = PCIM_MSICTRL_MME_MASK | PCIM_MSICTRL_MSI_ENABLE; 1124366f6083SPeter Grehan msgctrl = pci_get_cfgdata16(pi, offset); 1125366f6083SPeter Grehan msgctrl &= ~rwmask; 1126366f6083SPeter Grehan msgctrl |= val & rwmask; 1127366f6083SPeter Grehan val = msgctrl; 11287840d1c4SJohn Baldwin } 11297840d1c4SJohn Baldwin CFGWRITE(pi, offset, val, bytes); 1130366f6083SPeter Grehan 11317840d1c4SJohn Baldwin msgctrl = pci_get_cfgdata16(pi, capoff + 2); 1132366f6083SPeter Grehan addrlo = pci_get_cfgdata32(pi, capoff + 4); 1133366f6083SPeter Grehan if (msgctrl & PCIM_MSICTRL_64BIT) 1134366f6083SPeter Grehan msgdata = pci_get_cfgdata16(pi, capoff + 12); 1135366f6083SPeter Grehan else 1136366f6083SPeter Grehan msgdata = pci_get_cfgdata16(pi, capoff + 8); 1137366f6083SPeter Grehan 1138366f6083SPeter Grehan mme = msgctrl & PCIM_MSICTRL_MME_MASK; 1139366f6083SPeter Grehan pi->pi_msi.enabled = msgctrl & PCIM_MSICTRL_MSI_ENABLE ? 1 : 0; 1140366f6083SPeter Grehan if (pi->pi_msi.enabled) { 11414f8be175SNeel Natu pi->pi_msi.addr = addrlo; 11424f8be175SNeel Natu pi->pi_msi.msg_data = msgdata; 11434f8be175SNeel Natu pi->pi_msi.maxmsgnum = 1 << (mme >> 4); 1144366f6083SPeter Grehan } else { 11454f8be175SNeel Natu pi->pi_msi.maxmsgnum = 0; 1146366f6083SPeter Grehan } 11473cbf3585SJohn Baldwin pci_lintr_update(pi); 1148366f6083SPeter Grehan } 1149366f6083SPeter Grehan 115074f80b23SNeel Natu void 115174f80b23SNeel Natu pciecap_cfgwrite(struct pci_devinst *pi, int capoff, int offset, 115274f80b23SNeel Natu int bytes, uint32_t val) 115374f80b23SNeel Natu { 115474f80b23SNeel Natu 115574f80b23SNeel Natu /* XXX don't write to the readonly parts */ 115674f80b23SNeel Natu CFGWRITE(pi, offset, val, bytes); 115774f80b23SNeel Natu } 115874f80b23SNeel Natu 115974f80b23SNeel Natu #define PCIECAP_VERSION 0x2 116074f80b23SNeel Natu int 116174f80b23SNeel Natu pci_emul_add_pciecap(struct pci_devinst *pi, int type) 116274f80b23SNeel Natu { 116374f80b23SNeel Natu int err; 116474f80b23SNeel Natu struct pciecap pciecap; 116574f80b23SNeel Natu 116674f80b23SNeel Natu bzero(&pciecap, sizeof(pciecap)); 116774f80b23SNeel Natu 1168129f93c5SChuck Tuffli /* 1169129f93c5SChuck Tuffli * Use the integrated endpoint type for endpoints on a root complex bus. 1170129f93c5SChuck Tuffli * 1171129f93c5SChuck Tuffli * NB: bhyve currently only supports a single PCI bus that is the root 1172129f93c5SChuck Tuffli * complex bus, so all endpoints are integrated. 1173129f93c5SChuck Tuffli */ 1174129f93c5SChuck Tuffli if ((type == PCIEM_TYPE_ENDPOINT) && (pi->pi_bus == 0)) 1175129f93c5SChuck Tuffli type = PCIEM_TYPE_ROOT_INT_EP; 1176129f93c5SChuck Tuffli 117774f80b23SNeel Natu pciecap.capid = PCIY_EXPRESS; 1178129f93c5SChuck Tuffli pciecap.pcie_capabilities = PCIECAP_VERSION | type; 1179129f93c5SChuck Tuffli if (type != PCIEM_TYPE_ROOT_INT_EP) { 118074f80b23SNeel Natu pciecap.link_capabilities = 0x411; /* gen1, x1 */ 118174f80b23SNeel Natu pciecap.link_status = 0x11; /* gen1, x1 */ 1182129f93c5SChuck Tuffli } 118374f80b23SNeel Natu 118474f80b23SNeel Natu err = pci_emul_add_capability(pi, (u_char *)&pciecap, sizeof(pciecap)); 118574f80b23SNeel Natu return (err); 118674f80b23SNeel Natu } 118774f80b23SNeel Natu 1188366f6083SPeter Grehan /* 1189366f6083SPeter Grehan * This function assumes that 'coff' is in the capabilities region of the 119021368498SPeter Grehan * config space. A capoff parameter of zero will force a search for the 119121368498SPeter Grehan * offset and type. 1192366f6083SPeter Grehan */ 119321368498SPeter Grehan void 119421368498SPeter Grehan pci_emul_capwrite(struct pci_devinst *pi, int offset, int bytes, uint32_t val, 119521368498SPeter Grehan uint8_t capoff, int capid) 1196366f6083SPeter Grehan { 119721368498SPeter Grehan uint8_t nextoff; 1198366f6083SPeter Grehan 1199366f6083SPeter Grehan /* Do not allow un-aligned writes */ 1200366f6083SPeter Grehan if ((offset & (bytes - 1)) != 0) 1201366f6083SPeter Grehan return; 1202366f6083SPeter Grehan 120321368498SPeter Grehan if (capoff == 0) { 1204366f6083SPeter Grehan /* Find the capability that we want to update */ 1205366f6083SPeter Grehan capoff = CAP_START_OFFSET; 1206366f6083SPeter Grehan while (1) { 1207366f6083SPeter Grehan nextoff = pci_get_cfgdata8(pi, capoff + 1); 1208a96b8b80SJohn Baldwin if (nextoff == 0) 1209a96b8b80SJohn Baldwin break; 1210366f6083SPeter Grehan if (offset >= capoff && offset < nextoff) 1211366f6083SPeter Grehan break; 1212366f6083SPeter Grehan 1213366f6083SPeter Grehan capoff = nextoff; 1214366f6083SPeter Grehan } 1215366f6083SPeter Grehan assert(offset >= capoff); 121621368498SPeter Grehan capid = pci_get_cfgdata8(pi, capoff); 121721368498SPeter Grehan } 1218366f6083SPeter Grehan 1219366f6083SPeter Grehan /* 12202a8d400aSPeter Grehan * Capability ID and Next Capability Pointer are readonly. 12212a8d400aSPeter Grehan * However, some o/s's do 4-byte writes that include these. 12222a8d400aSPeter Grehan * For this case, trim the write back to 2 bytes and adjust 12232a8d400aSPeter Grehan * the data. 1224366f6083SPeter Grehan */ 12252a8d400aSPeter Grehan if (offset == capoff || offset == capoff + 1) { 12262a8d400aSPeter Grehan if (offset == capoff && bytes == 4) { 12272a8d400aSPeter Grehan bytes = 2; 12282a8d400aSPeter Grehan offset += 2; 12292a8d400aSPeter Grehan val >>= 16; 12302a8d400aSPeter Grehan } else 1231366f6083SPeter Grehan return; 12322a8d400aSPeter Grehan } 1233366f6083SPeter Grehan 1234366f6083SPeter Grehan switch (capid) { 1235366f6083SPeter Grehan case PCIY_MSI: 1236366f6083SPeter Grehan msicap_cfgwrite(pi, capoff, offset, bytes, val); 1237366f6083SPeter Grehan break; 1238c9b4e987SNeel Natu case PCIY_MSIX: 1239c9b4e987SNeel Natu msixcap_cfgwrite(pi, capoff, offset, bytes, val); 1240c9b4e987SNeel Natu break; 124174f80b23SNeel Natu case PCIY_EXPRESS: 124274f80b23SNeel Natu pciecap_cfgwrite(pi, capoff, offset, bytes, val); 124374f80b23SNeel Natu break; 1244366f6083SPeter Grehan default: 1245366f6083SPeter Grehan break; 1246366f6083SPeter Grehan } 1247366f6083SPeter Grehan } 1248366f6083SPeter Grehan 1249366f6083SPeter Grehan static int 1250366f6083SPeter Grehan pci_emul_iscap(struct pci_devinst *pi, int offset) 1251366f6083SPeter Grehan { 1252366f6083SPeter Grehan uint16_t sts; 1253366f6083SPeter Grehan 1254366f6083SPeter Grehan sts = pci_get_cfgdata16(pi, PCIR_STATUS); 1255366f6083SPeter Grehan if ((sts & PCIM_STATUS_CAPPRESENT) != 0) { 1256a96b8b80SJohn Baldwin if (offset >= CAP_START_OFFSET && offset <= pi->pi_capend) 1257a96b8b80SJohn Baldwin return (1); 1258366f6083SPeter Grehan } 1259a96b8b80SJohn Baldwin return (0); 1260366f6083SPeter Grehan } 1261366f6083SPeter Grehan 12620ab13648SPeter Grehan static int 12630ab13648SPeter Grehan pci_emul_fallback_handler(struct vmctx *ctx, int vcpu, int dir, uint64_t addr, 12640ab13648SPeter Grehan int size, uint64_t *val, void *arg1, long arg2) 12650ab13648SPeter Grehan { 12660ab13648SPeter Grehan /* 12670ab13648SPeter Grehan * Ignore writes; return 0xff's for reads. The mem read code 12680ab13648SPeter Grehan * will take care of truncating to the correct size. 12690ab13648SPeter Grehan */ 12700ab13648SPeter Grehan if (dir == MEM_F_READ) { 12710ab13648SPeter Grehan *val = 0xffffffffffffffff; 12720ab13648SPeter Grehan } 12730ab13648SPeter Grehan 12740ab13648SPeter Grehan return (0); 12750ab13648SPeter Grehan } 12760ab13648SPeter Grehan 127712a6eb99SNeel Natu static int 127812a6eb99SNeel Natu pci_emul_ecfg_handler(struct vmctx *ctx, int vcpu, int dir, uint64_t addr, 127912a6eb99SNeel Natu int bytes, uint64_t *val, void *arg1, long arg2) 128012a6eb99SNeel Natu { 128112a6eb99SNeel Natu int bus, slot, func, coff, in; 128212a6eb99SNeel Natu 128312a6eb99SNeel Natu coff = addr & 0xfff; 128412a6eb99SNeel Natu func = (addr >> 12) & 0x7; 128512a6eb99SNeel Natu slot = (addr >> 15) & 0x1f; 128612a6eb99SNeel Natu bus = (addr >> 20) & 0xff; 128712a6eb99SNeel Natu in = (dir == MEM_F_READ); 128812a6eb99SNeel Natu if (in) 128912a6eb99SNeel Natu *val = ~0UL; 129012a6eb99SNeel Natu pci_cfgrw(ctx, vcpu, in, bus, slot, func, coff, bytes, (uint32_t *)val); 129112a6eb99SNeel Natu return (0); 129212a6eb99SNeel Natu } 129312a6eb99SNeel Natu 129412a6eb99SNeel Natu uint64_t 129512a6eb99SNeel Natu pci_ecfg_base(void) 129612a6eb99SNeel Natu { 129712a6eb99SNeel Natu 129812a6eb99SNeel Natu return (PCI_EMUL_ECFG_BASE); 129912a6eb99SNeel Natu } 130012a6eb99SNeel Natu 1301d84882caSNeel Natu #define BUSIO_ROUNDUP 32 13027d55d295SCorvin Köhne #define BUSMEM32_ROUNDUP (1024 * 1024) 13037d55d295SCorvin Köhne #define BUSMEM64_ROUNDUP (512 * 1024 * 1024) 1304d84882caSNeel Natu 1305a38e2a64SPeter Grehan int 1306366f6083SPeter Grehan init_pci(struct vmctx *ctx) 1307366f6083SPeter Grehan { 1308621b5090SJohn Baldwin char node_name[sizeof("pci.XXX.XX.X")]; 130912a6eb99SNeel Natu struct mem_range mr; 1310366f6083SPeter Grehan struct pci_devemu *pde; 1311d84882caSNeel Natu struct businfo *bi; 1312d84882caSNeel Natu struct slotinfo *si; 13133cbf3585SJohn Baldwin struct funcinfo *fi; 1314621b5090SJohn Baldwin nvlist_t *nvl; 1315621b5090SJohn Baldwin const char *emul; 13169f08548dSNeel Natu size_t lowmem; 13174a4053e1SCorvin Köhne int bus, slot, func; 13184a4053e1SCorvin Köhne int error; 1319366f6083SPeter Grehan 13205cf21e48SCorvin Köhne if (vm_get_lowmem_limit(ctx) > PCI_EMUL_MEMBASE32) 13215cf21e48SCorvin Köhne errx(EX_OSERR, "Invalid lowmem limit"); 13225cf21e48SCorvin Köhne 1323366f6083SPeter Grehan pci_emul_iobase = PCI_EMUL_IOBASE; 13245cf21e48SCorvin Köhne pci_emul_membase32 = PCI_EMUL_MEMBASE32; 13259922872bSKonstantin Belousov 13264a4053e1SCorvin Köhne pci_emul_membase64 = 4*GB + vm_get_highmem_size(ctx); 13274a4053e1SCorvin Köhne pci_emul_membase64 = roundup2(pci_emul_membase64, PCI_EMUL_MEMSIZE64); 13284a4053e1SCorvin Köhne pci_emul_memlim64 = pci_emul_membase64 + PCI_EMUL_MEMSIZE64; 1329366f6083SPeter Grehan 1330d84882caSNeel Natu for (bus = 0; bus < MAXBUSES; bus++) { 1331621b5090SJohn Baldwin snprintf(node_name, sizeof(node_name), "pci.%d", bus); 1332621b5090SJohn Baldwin nvl = find_config_node(node_name); 1333621b5090SJohn Baldwin if (nvl == NULL) 1334d84882caSNeel Natu continue; 1335621b5090SJohn Baldwin pci_businfo[bus] = calloc(1, sizeof(struct businfo)); 1336621b5090SJohn Baldwin bi = pci_businfo[bus]; 1337621b5090SJohn Baldwin 1338d84882caSNeel Natu /* 1339d84882caSNeel Natu * Keep track of the i/o and memory resources allocated to 1340d84882caSNeel Natu * this bus. 1341d84882caSNeel Natu */ 1342d84882caSNeel Natu bi->iobase = pci_emul_iobase; 1343d84882caSNeel Natu bi->membase32 = pci_emul_membase32; 1344d84882caSNeel Natu bi->membase64 = pci_emul_membase64; 1345d84882caSNeel Natu 134601f9362eSCorvin Köhne /* first run: init devices */ 134799d65389SNeel Natu for (slot = 0; slot < MAXSLOTS; slot++) { 1348d84882caSNeel Natu si = &bi->slotinfo[slot]; 134999d65389SNeel Natu for (func = 0; func < MAXFUNCS; func++) { 1350d84882caSNeel Natu fi = &si->si_funcs[func]; 1351621b5090SJohn Baldwin snprintf(node_name, sizeof(node_name), 1352621b5090SJohn Baldwin "pci.%d.%d.%d", bus, slot, func); 1353621b5090SJohn Baldwin nvl = find_config_node(node_name); 1354621b5090SJohn Baldwin if (nvl == NULL) 1355d84882caSNeel Natu continue; 1356621b5090SJohn Baldwin 1357621b5090SJohn Baldwin fi->fi_config = nvl; 1358621b5090SJohn Baldwin emul = get_config_value_node(nvl, "device"); 1359621b5090SJohn Baldwin if (emul == NULL) { 1360621b5090SJohn Baldwin EPRINTLN("pci slot %d:%d:%d: missing " 1361621b5090SJohn Baldwin "\"device\" value", bus, slot, func); 1362621b5090SJohn Baldwin return (EINVAL); 1363621b5090SJohn Baldwin } 1364621b5090SJohn Baldwin pde = pci_emul_finddev(emul); 1365621b5090SJohn Baldwin if (pde == NULL) { 1366621b5090SJohn Baldwin EPRINTLN("pci slot %d:%d:%d: unknown " 1367621b5090SJohn Baldwin "device \"%s\"", bus, slot, func, 1368621b5090SJohn Baldwin emul); 1369621b5090SJohn Baldwin return (EINVAL); 1370621b5090SJohn Baldwin } 1371621b5090SJohn Baldwin if (pde->pe_alias != NULL) { 1372621b5090SJohn Baldwin EPRINTLN("pci slot %d:%d:%d: legacy " 1373621b5090SJohn Baldwin "device \"%s\", use \"%s\" instead", 1374621b5090SJohn Baldwin bus, slot, func, emul, 1375621b5090SJohn Baldwin pde->pe_alias); 1376621b5090SJohn Baldwin return (EINVAL); 1377621b5090SJohn Baldwin } 1378621b5090SJohn Baldwin fi->fi_pde = pde; 1379d84882caSNeel Natu error = pci_emul_init(ctx, pde, bus, slot, 1380d84882caSNeel Natu func, fi); 1381a38e2a64SPeter Grehan if (error) 1382a38e2a64SPeter Grehan return (error); 1383366f6083SPeter Grehan } 1384366f6083SPeter Grehan } 1385d84882caSNeel Natu 138601f9362eSCorvin Köhne /* second run: assign BARs and free list */ 138701f9362eSCorvin Köhne struct pci_bar_allocation *bar; 138801f9362eSCorvin Köhne struct pci_bar_allocation *bar_tmp; 138901f9362eSCorvin Köhne TAILQ_FOREACH_SAFE(bar, &pci_bars, chain, bar_tmp) { 139001f9362eSCorvin Köhne pci_emul_assign_bar(bar->pdi, bar->idx, bar->type, 139101f9362eSCorvin Köhne bar->size); 139201f9362eSCorvin Köhne free(bar); 139301f9362eSCorvin Köhne } 139401f9362eSCorvin Köhne TAILQ_INIT(&pci_bars); 139501f9362eSCorvin Köhne 1396d84882caSNeel Natu /* 1397d84882caSNeel Natu * Add some slop to the I/O and memory resources decoded by 1398d84882caSNeel Natu * this bus to give a guest some flexibility if it wants to 1399d84882caSNeel Natu * reprogram the BARs. 1400d84882caSNeel Natu */ 1401d84882caSNeel Natu pci_emul_iobase += BUSIO_ROUNDUP; 1402d84882caSNeel Natu pci_emul_iobase = roundup2(pci_emul_iobase, BUSIO_ROUNDUP); 1403d84882caSNeel Natu bi->iolimit = pci_emul_iobase; 1404d84882caSNeel Natu 14057d55d295SCorvin Köhne pci_emul_membase32 += BUSMEM32_ROUNDUP; 1406d84882caSNeel Natu pci_emul_membase32 = roundup2(pci_emul_membase32, 14077d55d295SCorvin Köhne BUSMEM32_ROUNDUP); 1408d84882caSNeel Natu bi->memlimit32 = pci_emul_membase32; 1409d84882caSNeel Natu 14107d55d295SCorvin Köhne pci_emul_membase64 += BUSMEM64_ROUNDUP; 1411d84882caSNeel Natu pci_emul_membase64 = roundup2(pci_emul_membase64, 14127d55d295SCorvin Köhne BUSMEM64_ROUNDUP); 1413d84882caSNeel Natu bi->memlimit64 = pci_emul_membase64; 1414366f6083SPeter Grehan } 14150038ee98SPeter Grehan 14160038ee98SPeter Grehan /* 1417b3e9732aSJohn Baldwin * PCI backends are initialized before routing INTx interrupts 1418b3e9732aSJohn Baldwin * so that LPC devices are able to reserve ISA IRQs before 1419b3e9732aSJohn Baldwin * routing PIRQ pins. 1420b3e9732aSJohn Baldwin */ 1421b3e9732aSJohn Baldwin for (bus = 0; bus < MAXBUSES; bus++) { 1422b3e9732aSJohn Baldwin if ((bi = pci_businfo[bus]) == NULL) 1423b3e9732aSJohn Baldwin continue; 1424b3e9732aSJohn Baldwin 1425b3e9732aSJohn Baldwin for (slot = 0; slot < MAXSLOTS; slot++) { 1426b3e9732aSJohn Baldwin si = &bi->slotinfo[slot]; 1427b3e9732aSJohn Baldwin for (func = 0; func < MAXFUNCS; func++) { 1428b3e9732aSJohn Baldwin fi = &si->si_funcs[func]; 1429b3e9732aSJohn Baldwin if (fi->fi_devi == NULL) 1430b3e9732aSJohn Baldwin continue; 1431b3e9732aSJohn Baldwin pci_lintr_route(fi->fi_devi); 1432b3e9732aSJohn Baldwin } 1433b3e9732aSJohn Baldwin } 1434b3e9732aSJohn Baldwin } 1435b3e9732aSJohn Baldwin lpc_pirq_routed(); 1436b3e9732aSJohn Baldwin 1437b3e9732aSJohn Baldwin /* 14389f08548dSNeel Natu * The guest physical memory map looks like the following: 14399f08548dSNeel Natu * [0, lowmem) guest system memory 14405cf21e48SCorvin Köhne * [lowmem, 0xC0000000) memory hole (may be absent) 14415cf21e48SCorvin Köhne * [0xC0000000, 0xE0000000) PCI hole (32-bit BAR allocation) 144212a6eb99SNeel Natu * [0xE0000000, 0xF0000000) PCI extended config window 144312a6eb99SNeel Natu * [0xF0000000, 4GB) LAPIC, IOAPIC, HPET, firmware 14449f08548dSNeel Natu * [4GB, 4GB + highmem) 144512a6eb99SNeel Natu */ 144612a6eb99SNeel Natu 144712a6eb99SNeel Natu /* 14489f08548dSNeel Natu * Accesses to memory addresses that are not allocated to system 14499f08548dSNeel Natu * memory or PCI devices return 0xff's. 14500ab13648SPeter Grehan */ 1451be679db4SNeel Natu lowmem = vm_get_lowmem_size(ctx); 145212a6eb99SNeel Natu bzero(&mr, sizeof(struct mem_range)); 145312a6eb99SNeel Natu mr.name = "PCI hole"; 145412a6eb99SNeel Natu mr.flags = MEM_F_RW | MEM_F_IMMUTABLE; 145512a6eb99SNeel Natu mr.base = lowmem; 145612a6eb99SNeel Natu mr.size = (4ULL * 1024 * 1024 * 1024) - lowmem; 145712a6eb99SNeel Natu mr.handler = pci_emul_fallback_handler; 145812a6eb99SNeel Natu error = register_mem_fallback(&mr); 145912a6eb99SNeel Natu assert(error == 0); 14609f08548dSNeel Natu 146112a6eb99SNeel Natu /* PCI extended config space */ 146212a6eb99SNeel Natu bzero(&mr, sizeof(struct mem_range)); 146312a6eb99SNeel Natu mr.name = "PCI ECFG"; 146412a6eb99SNeel Natu mr.flags = MEM_F_RW | MEM_F_IMMUTABLE; 146512a6eb99SNeel Natu mr.base = PCI_EMUL_ECFG_BASE; 146612a6eb99SNeel Natu mr.size = PCI_EMUL_ECFG_SIZE; 146712a6eb99SNeel Natu mr.handler = pci_emul_ecfg_handler; 146812a6eb99SNeel Natu error = register_mem(&mr); 14690ab13648SPeter Grehan assert(error == 0); 1470a38e2a64SPeter Grehan 1471a38e2a64SPeter Grehan return (0); 1472366f6083SPeter Grehan } 1473366f6083SPeter Grehan 14743cbf3585SJohn Baldwin static void 1475b3e9732aSJohn Baldwin pci_apic_prt_entry(int bus, int slot, int pin, int pirq_pin, int ioapic_irq, 1476b3e9732aSJohn Baldwin void *arg) 14773cbf3585SJohn Baldwin { 14783cbf3585SJohn Baldwin 1479b3e9732aSJohn Baldwin dsdt_line(" Package ()"); 14803cbf3585SJohn Baldwin dsdt_line(" {"); 14813cbf3585SJohn Baldwin dsdt_line(" 0x%X,", slot << 16 | 0xffff); 14823cbf3585SJohn Baldwin dsdt_line(" 0x%02X,", pin - 1); 14833cbf3585SJohn Baldwin dsdt_line(" Zero,"); 14843cbf3585SJohn Baldwin dsdt_line(" 0x%X", ioapic_irq); 1485b3e9732aSJohn Baldwin dsdt_line(" },"); 1486b3e9732aSJohn Baldwin } 1487b3e9732aSJohn Baldwin 1488b3e9732aSJohn Baldwin static void 1489b3e9732aSJohn Baldwin pci_pirq_prt_entry(int bus, int slot, int pin, int pirq_pin, int ioapic_irq, 1490b3e9732aSJohn Baldwin void *arg) 1491b3e9732aSJohn Baldwin { 1492b3e9732aSJohn Baldwin char *name; 1493b3e9732aSJohn Baldwin 1494b3e9732aSJohn Baldwin name = lpc_pirq_name(pirq_pin); 1495b3e9732aSJohn Baldwin if (name == NULL) 1496b3e9732aSJohn Baldwin return; 1497b3e9732aSJohn Baldwin dsdt_line(" Package ()"); 1498b3e9732aSJohn Baldwin dsdt_line(" {"); 1499b3e9732aSJohn Baldwin dsdt_line(" 0x%X,", slot << 16 | 0xffff); 1500b3e9732aSJohn Baldwin dsdt_line(" 0x%02X,", pin - 1); 1501b3e9732aSJohn Baldwin dsdt_line(" %s,", name); 1502b3e9732aSJohn Baldwin dsdt_line(" 0x00"); 1503b3e9732aSJohn Baldwin dsdt_line(" },"); 1504b3e9732aSJohn Baldwin free(name); 15053cbf3585SJohn Baldwin } 15063cbf3585SJohn Baldwin 1507d84882caSNeel Natu /* 1508d84882caSNeel Natu * A bhyve virtual machine has a flat PCI hierarchy with a root port 1509d84882caSNeel Natu * corresponding to each PCI bus. 1510d84882caSNeel Natu */ 1511d84882caSNeel Natu static void 1512d84882caSNeel Natu pci_bus_write_dsdt(int bus) 1513e6c8bc29SJohn Baldwin { 1514d84882caSNeel Natu struct businfo *bi; 1515d84882caSNeel Natu struct slotinfo *si; 1516e6c8bc29SJohn Baldwin struct pci_devinst *pi; 1517b3e9732aSJohn Baldwin int count, func, slot; 1518e6c8bc29SJohn Baldwin 1519d84882caSNeel Natu /* 1520d84882caSNeel Natu * If there are no devices on this 'bus' then just return. 1521d84882caSNeel Natu */ 1522d84882caSNeel Natu if ((bi = pci_businfo[bus]) == NULL) { 1523d84882caSNeel Natu /* 1524d84882caSNeel Natu * Bus 0 is special because it decodes the I/O ports used 1525d84882caSNeel Natu * for PCI config space access even if there are no devices 1526d84882caSNeel Natu * on it. 1527d84882caSNeel Natu */ 1528d84882caSNeel Natu if (bus != 0) 1529d84882caSNeel Natu return; 1530d84882caSNeel Natu } 1531d84882caSNeel Natu 1532d84882caSNeel Natu dsdt_line(" Device (PC%02X)", bus); 1533e6c8bc29SJohn Baldwin dsdt_line(" {"); 1534e6c8bc29SJohn Baldwin dsdt_line(" Name (_HID, EisaId (\"PNP0A03\"))"); 1535d84882caSNeel Natu 1536d84882caSNeel Natu dsdt_line(" Method (_BBN, 0, NotSerialized)"); 1537d84882caSNeel Natu dsdt_line(" {"); 1538d84882caSNeel Natu dsdt_line(" Return (0x%08X)", bus); 1539d84882caSNeel Natu dsdt_line(" }"); 1540e6c8bc29SJohn Baldwin dsdt_line(" Name (_CRS, ResourceTemplate ()"); 1541e6c8bc29SJohn Baldwin dsdt_line(" {"); 1542e6c8bc29SJohn Baldwin dsdt_line(" WordBusNumber (ResourceProducer, MinFixed, " 1543e6c8bc29SJohn Baldwin "MaxFixed, PosDecode,"); 1544e6c8bc29SJohn Baldwin dsdt_line(" 0x0000, // Granularity"); 1545d84882caSNeel Natu dsdt_line(" 0x%04X, // Range Minimum", bus); 1546d84882caSNeel Natu dsdt_line(" 0x%04X, // Range Maximum", bus); 1547e6c8bc29SJohn Baldwin dsdt_line(" 0x0000, // Translation Offset"); 1548d84882caSNeel Natu dsdt_line(" 0x0001, // Length"); 1549e6c8bc29SJohn Baldwin dsdt_line(" ,, )"); 1550d84882caSNeel Natu 1551d84882caSNeel Natu if (bus == 0) { 1552e6c8bc29SJohn Baldwin dsdt_indent(3); 1553e6c8bc29SJohn Baldwin dsdt_fixed_ioport(0xCF8, 8); 1554e6c8bc29SJohn Baldwin dsdt_unindent(3); 1555d84882caSNeel Natu 1556e6c8bc29SJohn Baldwin dsdt_line(" WordIO (ResourceProducer, MinFixed, MaxFixed, " 1557e6c8bc29SJohn Baldwin "PosDecode, EntireRange,"); 1558e6c8bc29SJohn Baldwin dsdt_line(" 0x0000, // Granularity"); 1559e6c8bc29SJohn Baldwin dsdt_line(" 0x0000, // Range Minimum"); 1560e6c8bc29SJohn Baldwin dsdt_line(" 0x0CF7, // Range Maximum"); 1561e6c8bc29SJohn Baldwin dsdt_line(" 0x0000, // Translation Offset"); 1562e6c8bc29SJohn Baldwin dsdt_line(" 0x0CF8, // Length"); 1563e6c8bc29SJohn Baldwin dsdt_line(" ,, , TypeStatic)"); 1564d84882caSNeel Natu 1565e6c8bc29SJohn Baldwin dsdt_line(" WordIO (ResourceProducer, MinFixed, MaxFixed, " 1566e6c8bc29SJohn Baldwin "PosDecode, EntireRange,"); 1567e6c8bc29SJohn Baldwin dsdt_line(" 0x0000, // Granularity"); 1568e6c8bc29SJohn Baldwin dsdt_line(" 0x0D00, // Range Minimum"); 1569d84882caSNeel Natu dsdt_line(" 0x%04X, // Range Maximum", 1570d84882caSNeel Natu PCI_EMUL_IOBASE - 1); 1571e6c8bc29SJohn Baldwin dsdt_line(" 0x0000, // Translation Offset"); 1572d84882caSNeel Natu dsdt_line(" 0x%04X, // Length", 1573d84882caSNeel Natu PCI_EMUL_IOBASE - 0x0D00); 1574e6c8bc29SJohn Baldwin dsdt_line(" ,, , TypeStatic)"); 1575d84882caSNeel Natu 1576d84882caSNeel Natu if (bi == NULL) { 1577d84882caSNeel Natu dsdt_line(" })"); 1578d84882caSNeel Natu goto done; 1579d84882caSNeel Natu } 1580d84882caSNeel Natu } 1581d84882caSNeel Natu assert(bi != NULL); 1582d84882caSNeel Natu 1583d84882caSNeel Natu /* i/o window */ 1584d84882caSNeel Natu dsdt_line(" WordIO (ResourceProducer, MinFixed, MaxFixed, " 1585d84882caSNeel Natu "PosDecode, EntireRange,"); 1586d84882caSNeel Natu dsdt_line(" 0x0000, // Granularity"); 1587d84882caSNeel Natu dsdt_line(" 0x%04X, // Range Minimum", bi->iobase); 1588d84882caSNeel Natu dsdt_line(" 0x%04X, // Range Maximum", 1589d84882caSNeel Natu bi->iolimit - 1); 1590d84882caSNeel Natu dsdt_line(" 0x0000, // Translation Offset"); 1591d84882caSNeel Natu dsdt_line(" 0x%04X, // Length", 1592d84882caSNeel Natu bi->iolimit - bi->iobase); 1593d84882caSNeel Natu dsdt_line(" ,, , TypeStatic)"); 1594d84882caSNeel Natu 1595d84882caSNeel Natu /* mmio window (32-bit) */ 1596e6c8bc29SJohn Baldwin dsdt_line(" DWordMemory (ResourceProducer, PosDecode, " 1597e6c8bc29SJohn Baldwin "MinFixed, MaxFixed, NonCacheable, ReadWrite,"); 1598e6c8bc29SJohn Baldwin dsdt_line(" 0x00000000, // Granularity"); 1599d84882caSNeel Natu dsdt_line(" 0x%08X, // Range Minimum\n", bi->membase32); 1600e6c8bc29SJohn Baldwin dsdt_line(" 0x%08X, // Range Maximum\n", 1601d84882caSNeel Natu bi->memlimit32 - 1); 1602e6c8bc29SJohn Baldwin dsdt_line(" 0x00000000, // Translation Offset"); 1603d84882caSNeel Natu dsdt_line(" 0x%08X, // Length\n", 1604d84882caSNeel Natu bi->memlimit32 - bi->membase32); 1605e6c8bc29SJohn Baldwin dsdt_line(" ,, , AddressRangeMemory, TypeStatic)"); 1606d84882caSNeel Natu 1607d84882caSNeel Natu /* mmio window (64-bit) */ 1608e6c8bc29SJohn Baldwin dsdt_line(" QWordMemory (ResourceProducer, PosDecode, " 1609e6c8bc29SJohn Baldwin "MinFixed, MaxFixed, NonCacheable, ReadWrite,"); 1610e6c8bc29SJohn Baldwin dsdt_line(" 0x0000000000000000, // Granularity"); 1611d84882caSNeel Natu dsdt_line(" 0x%016lX, // Range Minimum\n", bi->membase64); 1612e6c8bc29SJohn Baldwin dsdt_line(" 0x%016lX, // Range Maximum\n", 1613d84882caSNeel Natu bi->memlimit64 - 1); 1614e6c8bc29SJohn Baldwin dsdt_line(" 0x0000000000000000, // Translation Offset"); 1615e6c8bc29SJohn Baldwin dsdt_line(" 0x%016lX, // Length\n", 1616d84882caSNeel Natu bi->memlimit64 - bi->membase64); 1617e6c8bc29SJohn Baldwin dsdt_line(" ,, , AddressRangeMemory, TypeStatic)"); 1618e6c8bc29SJohn Baldwin dsdt_line(" })"); 1619d84882caSNeel Natu 1620d84882caSNeel Natu count = pci_count_lintr(bus); 16213cbf3585SJohn Baldwin if (count != 0) { 16223cbf3585SJohn Baldwin dsdt_indent(2); 1623b3e9732aSJohn Baldwin dsdt_line("Name (PPRT, Package ()"); 16243cbf3585SJohn Baldwin dsdt_line("{"); 1625b3e9732aSJohn Baldwin pci_walk_lintr(bus, pci_pirq_prt_entry, NULL); 16263cbf3585SJohn Baldwin dsdt_line("})"); 1627b3e9732aSJohn Baldwin dsdt_line("Name (APRT, Package ()"); 1628b3e9732aSJohn Baldwin dsdt_line("{"); 1629b3e9732aSJohn Baldwin pci_walk_lintr(bus, pci_apic_prt_entry, NULL); 1630b3e9732aSJohn Baldwin dsdt_line("})"); 1631b3e9732aSJohn Baldwin dsdt_line("Method (_PRT, 0, NotSerialized)"); 1632b3e9732aSJohn Baldwin dsdt_line("{"); 1633b3e9732aSJohn Baldwin dsdt_line(" If (PICM)"); 1634b3e9732aSJohn Baldwin dsdt_line(" {"); 1635b3e9732aSJohn Baldwin dsdt_line(" Return (APRT)"); 1636b3e9732aSJohn Baldwin dsdt_line(" }"); 1637b3e9732aSJohn Baldwin dsdt_line(" Else"); 1638b3e9732aSJohn Baldwin dsdt_line(" {"); 1639b3e9732aSJohn Baldwin dsdt_line(" Return (PPRT)"); 1640b3e9732aSJohn Baldwin dsdt_line(" }"); 1641b3e9732aSJohn Baldwin dsdt_line("}"); 16423cbf3585SJohn Baldwin dsdt_unindent(2); 16433cbf3585SJohn Baldwin } 1644e6c8bc29SJohn Baldwin 1645e6c8bc29SJohn Baldwin dsdt_indent(2); 1646e6c8bc29SJohn Baldwin for (slot = 0; slot < MAXSLOTS; slot++) { 1647d84882caSNeel Natu si = &bi->slotinfo[slot]; 1648e6c8bc29SJohn Baldwin for (func = 0; func < MAXFUNCS; func++) { 1649d84882caSNeel Natu pi = si->si_funcs[func].fi_devi; 1650e6c8bc29SJohn Baldwin if (pi != NULL && pi->pi_d->pe_write_dsdt != NULL) 1651e6c8bc29SJohn Baldwin pi->pi_d->pe_write_dsdt(pi); 1652e6c8bc29SJohn Baldwin } 1653e6c8bc29SJohn Baldwin } 1654e6c8bc29SJohn Baldwin dsdt_unindent(2); 1655d84882caSNeel Natu done: 1656e6c8bc29SJohn Baldwin dsdt_line(" }"); 1657e6c8bc29SJohn Baldwin } 1658e6c8bc29SJohn Baldwin 1659d84882caSNeel Natu void 1660d84882caSNeel Natu pci_write_dsdt(void) 1661d84882caSNeel Natu { 1662d84882caSNeel Natu int bus; 1663d84882caSNeel Natu 1664b3e9732aSJohn Baldwin dsdt_indent(1); 1665b3e9732aSJohn Baldwin dsdt_line("Name (PICM, 0x00)"); 1666b3e9732aSJohn Baldwin dsdt_line("Method (_PIC, 1, NotSerialized)"); 1667b3e9732aSJohn Baldwin dsdt_line("{"); 1668b3e9732aSJohn Baldwin dsdt_line(" Store (Arg0, PICM)"); 1669b3e9732aSJohn Baldwin dsdt_line("}"); 1670b3e9732aSJohn Baldwin dsdt_line(""); 1671b3e9732aSJohn Baldwin dsdt_line("Scope (_SB)"); 1672b3e9732aSJohn Baldwin dsdt_line("{"); 1673d84882caSNeel Natu for (bus = 0; bus < MAXBUSES; bus++) 1674d84882caSNeel Natu pci_bus_write_dsdt(bus); 1675b3e9732aSJohn Baldwin dsdt_line("}"); 1676b3e9732aSJohn Baldwin dsdt_unindent(1); 1677d84882caSNeel Natu } 1678d84882caSNeel Natu 1679366f6083SPeter Grehan int 1680b100acf2SNeel Natu pci_bus_configured(int bus) 1681b100acf2SNeel Natu { 1682b100acf2SNeel Natu assert(bus >= 0 && bus < MAXBUSES); 1683b100acf2SNeel Natu return (pci_businfo[bus] != NULL); 1684b100acf2SNeel Natu } 1685b100acf2SNeel Natu 1686b100acf2SNeel Natu int 1687366f6083SPeter Grehan pci_msi_enabled(struct pci_devinst *pi) 1688366f6083SPeter Grehan { 1689366f6083SPeter Grehan return (pi->pi_msi.enabled); 1690366f6083SPeter Grehan } 1691366f6083SPeter Grehan 1692366f6083SPeter Grehan int 16934f8be175SNeel Natu pci_msi_maxmsgnum(struct pci_devinst *pi) 1694366f6083SPeter Grehan { 1695366f6083SPeter Grehan if (pi->pi_msi.enabled) 16964f8be175SNeel Natu return (pi->pi_msi.maxmsgnum); 1697366f6083SPeter Grehan else 1698366f6083SPeter Grehan return (0); 1699366f6083SPeter Grehan } 1700366f6083SPeter Grehan 1701c9b4e987SNeel Natu int 1702c9b4e987SNeel Natu pci_msix_enabled(struct pci_devinst *pi) 1703c9b4e987SNeel Natu { 1704c9b4e987SNeel Natu 1705c9b4e987SNeel Natu return (pi->pi_msix.enabled && !pi->pi_msi.enabled); 1706c9b4e987SNeel Natu } 1707c9b4e987SNeel Natu 1708c9b4e987SNeel Natu void 1709c9b4e987SNeel Natu pci_generate_msix(struct pci_devinst *pi, int index) 1710c9b4e987SNeel Natu { 1711c9b4e987SNeel Natu struct msix_table_entry *mte; 1712c9b4e987SNeel Natu 1713c9b4e987SNeel Natu if (!pci_msix_enabled(pi)) 1714c9b4e987SNeel Natu return; 1715c9b4e987SNeel Natu 1716c9b4e987SNeel Natu if (pi->pi_msix.function_mask) 1717c9b4e987SNeel Natu return; 1718c9b4e987SNeel Natu 1719c9b4e987SNeel Natu if (index >= pi->pi_msix.table_count) 1720c9b4e987SNeel Natu return; 1721c9b4e987SNeel Natu 1722c9b4e987SNeel Natu mte = &pi->pi_msix.table[index]; 1723c9b4e987SNeel Natu if ((mte->vector_control & PCIM_MSIX_VCTRL_MASK) == 0) { 1724c9b4e987SNeel Natu /* XXX Set PBA bit if interrupt is disabled */ 17254f8be175SNeel Natu vm_lapic_msi(pi->pi_vmctx, mte->addr, mte->msg_data); 1726c9b4e987SNeel Natu } 1727c9b4e987SNeel Natu } 1728c9b4e987SNeel Natu 1729366f6083SPeter Grehan void 17304f8be175SNeel Natu pci_generate_msi(struct pci_devinst *pi, int index) 1731366f6083SPeter Grehan { 1732366f6083SPeter Grehan 17334f8be175SNeel Natu if (pci_msi_enabled(pi) && index < pci_msi_maxmsgnum(pi)) { 17344f8be175SNeel Natu vm_lapic_msi(pi->pi_vmctx, pi->pi_msi.addr, 17354f8be175SNeel Natu pi->pi_msi.msg_data + index); 1736366f6083SPeter Grehan } 1737366f6083SPeter Grehan } 1738366f6083SPeter Grehan 17393cbf3585SJohn Baldwin static bool 17403cbf3585SJohn Baldwin pci_lintr_permitted(struct pci_devinst *pi) 17410038ee98SPeter Grehan { 17423cbf3585SJohn Baldwin uint16_t cmd; 17430038ee98SPeter Grehan 17443cbf3585SJohn Baldwin cmd = pci_get_cfgdata16(pi, PCIR_COMMAND); 17453cbf3585SJohn Baldwin return (!(pi->pi_msi.enabled || pi->pi_msix.enabled || 17463cbf3585SJohn Baldwin (cmd & PCIM_CMD_INTxDIS))); 17473cbf3585SJohn Baldwin } 17483cbf3585SJohn Baldwin 1749b3e9732aSJohn Baldwin void 17503cbf3585SJohn Baldwin pci_lintr_request(struct pci_devinst *pi) 17513cbf3585SJohn Baldwin { 1752d84882caSNeel Natu struct businfo *bi; 17533cbf3585SJohn Baldwin struct slotinfo *si; 1754b3e9732aSJohn Baldwin int bestpin, bestcount, pin; 17553cbf3585SJohn Baldwin 1756d84882caSNeel Natu bi = pci_businfo[pi->pi_bus]; 1757d84882caSNeel Natu assert(bi != NULL); 1758d84882caSNeel Natu 17593cbf3585SJohn Baldwin /* 1760b3e9732aSJohn Baldwin * Just allocate a pin from our slot. The pin will be 1761b3e9732aSJohn Baldwin * assigned IRQs later when interrupts are routed. 17623cbf3585SJohn Baldwin */ 1763d84882caSNeel Natu si = &bi->slotinfo[pi->pi_slot]; 17643cbf3585SJohn Baldwin bestpin = 0; 17653cbf3585SJohn Baldwin bestcount = si->si_intpins[0].ii_count; 17663cbf3585SJohn Baldwin for (pin = 1; pin < 4; pin++) { 17673cbf3585SJohn Baldwin if (si->si_intpins[pin].ii_count < bestcount) { 17683cbf3585SJohn Baldwin bestpin = pin; 17693cbf3585SJohn Baldwin bestcount = si->si_intpins[pin].ii_count; 17703cbf3585SJohn Baldwin } 17713cbf3585SJohn Baldwin } 17723cbf3585SJohn Baldwin 17733cbf3585SJohn Baldwin si->si_intpins[bestpin].ii_count++; 17743cbf3585SJohn Baldwin pi->pi_lintr.pin = bestpin + 1; 17753cbf3585SJohn Baldwin pci_set_cfgdata8(pi, PCIR_INTPIN, bestpin + 1); 1776b3e9732aSJohn Baldwin } 1777b3e9732aSJohn Baldwin 1778b3e9732aSJohn Baldwin static void 1779b3e9732aSJohn Baldwin pci_lintr_route(struct pci_devinst *pi) 1780b3e9732aSJohn Baldwin { 1781b3e9732aSJohn Baldwin struct businfo *bi; 1782b3e9732aSJohn Baldwin struct intxinfo *ii; 1783b3e9732aSJohn Baldwin 1784b3e9732aSJohn Baldwin if (pi->pi_lintr.pin == 0) 1785b3e9732aSJohn Baldwin return; 1786b3e9732aSJohn Baldwin 1787b3e9732aSJohn Baldwin bi = pci_businfo[pi->pi_bus]; 1788b3e9732aSJohn Baldwin assert(bi != NULL); 1789b3e9732aSJohn Baldwin ii = &bi->slotinfo[pi->pi_slot].si_intpins[pi->pi_lintr.pin - 1]; 1790b3e9732aSJohn Baldwin 1791b3e9732aSJohn Baldwin /* 1792b3e9732aSJohn Baldwin * Attempt to allocate an I/O APIC pin for this intpin if one 1793b3e9732aSJohn Baldwin * is not yet assigned. 1794b3e9732aSJohn Baldwin */ 1795b3e9732aSJohn Baldwin if (ii->ii_ioapic_irq == 0) 17961b4496d0SAlexander Motin ii->ii_ioapic_irq = ioapic_pci_alloc_irq(pi); 1797b3e9732aSJohn Baldwin assert(ii->ii_ioapic_irq > 0); 1798b3e9732aSJohn Baldwin 1799b3e9732aSJohn Baldwin /* 1800b3e9732aSJohn Baldwin * Attempt to allocate a PIRQ pin for this intpin if one is 1801b3e9732aSJohn Baldwin * not yet assigned. 1802b3e9732aSJohn Baldwin */ 1803b3e9732aSJohn Baldwin if (ii->ii_pirq_pin == 0) 18041b4496d0SAlexander Motin ii->ii_pirq_pin = pirq_alloc_pin(pi); 1805b3e9732aSJohn Baldwin assert(ii->ii_pirq_pin > 0); 1806b3e9732aSJohn Baldwin 1807b3e9732aSJohn Baldwin pi->pi_lintr.ioapic_irq = ii->ii_ioapic_irq; 1808b3e9732aSJohn Baldwin pi->pi_lintr.pirq_pin = ii->ii_pirq_pin; 1809b3e9732aSJohn Baldwin pci_set_cfgdata8(pi, PCIR_INTLINE, pirq_irq(ii->ii_pirq_pin)); 18100038ee98SPeter Grehan } 18110038ee98SPeter Grehan 18120038ee98SPeter Grehan void 18130038ee98SPeter Grehan pci_lintr_assert(struct pci_devinst *pi) 18140038ee98SPeter Grehan { 18150038ee98SPeter Grehan 18163cbf3585SJohn Baldwin assert(pi->pi_lintr.pin > 0); 1817ac7304a7SNeel Natu 18183cbf3585SJohn Baldwin pthread_mutex_lock(&pi->pi_lintr.lock); 18193cbf3585SJohn Baldwin if (pi->pi_lintr.state == IDLE) { 18203cbf3585SJohn Baldwin if (pci_lintr_permitted(pi)) { 18213cbf3585SJohn Baldwin pi->pi_lintr.state = ASSERTED; 1822b3e9732aSJohn Baldwin pci_irq_assert(pi); 18233cbf3585SJohn Baldwin } else 18243cbf3585SJohn Baldwin pi->pi_lintr.state = PENDING; 18250038ee98SPeter Grehan } 18263cbf3585SJohn Baldwin pthread_mutex_unlock(&pi->pi_lintr.lock); 1827ac7304a7SNeel Natu } 18280038ee98SPeter Grehan 18290038ee98SPeter Grehan void 18300038ee98SPeter Grehan pci_lintr_deassert(struct pci_devinst *pi) 18310038ee98SPeter Grehan { 18320038ee98SPeter Grehan 18333cbf3585SJohn Baldwin assert(pi->pi_lintr.pin > 0); 1834ac7304a7SNeel Natu 18353cbf3585SJohn Baldwin pthread_mutex_lock(&pi->pi_lintr.lock); 18363cbf3585SJohn Baldwin if (pi->pi_lintr.state == ASSERTED) { 18373cbf3585SJohn Baldwin pi->pi_lintr.state = IDLE; 1838b3e9732aSJohn Baldwin pci_irq_deassert(pi); 18393cbf3585SJohn Baldwin } else if (pi->pi_lintr.state == PENDING) 18403cbf3585SJohn Baldwin pi->pi_lintr.state = IDLE; 18413cbf3585SJohn Baldwin pthread_mutex_unlock(&pi->pi_lintr.lock); 18423cbf3585SJohn Baldwin } 18433cbf3585SJohn Baldwin 18443cbf3585SJohn Baldwin static void 18453cbf3585SJohn Baldwin pci_lintr_update(struct pci_devinst *pi) 18463cbf3585SJohn Baldwin { 18473cbf3585SJohn Baldwin 18483cbf3585SJohn Baldwin pthread_mutex_lock(&pi->pi_lintr.lock); 18493cbf3585SJohn Baldwin if (pi->pi_lintr.state == ASSERTED && !pci_lintr_permitted(pi)) { 1850b3e9732aSJohn Baldwin pci_irq_deassert(pi); 18513cbf3585SJohn Baldwin pi->pi_lintr.state = PENDING; 18523cbf3585SJohn Baldwin } else if (pi->pi_lintr.state == PENDING && pci_lintr_permitted(pi)) { 18533cbf3585SJohn Baldwin pi->pi_lintr.state = ASSERTED; 1854b3e9732aSJohn Baldwin pci_irq_assert(pi); 18553cbf3585SJohn Baldwin } 18563cbf3585SJohn Baldwin pthread_mutex_unlock(&pi->pi_lintr.lock); 18573cbf3585SJohn Baldwin } 18583cbf3585SJohn Baldwin 18593cbf3585SJohn Baldwin int 1860d84882caSNeel Natu pci_count_lintr(int bus) 18613cbf3585SJohn Baldwin { 18623cbf3585SJohn Baldwin int count, slot, pin; 1863d84882caSNeel Natu struct slotinfo *slotinfo; 18643cbf3585SJohn Baldwin 18653cbf3585SJohn Baldwin count = 0; 1866d84882caSNeel Natu if (pci_businfo[bus] != NULL) { 18673cbf3585SJohn Baldwin for (slot = 0; slot < MAXSLOTS; slot++) { 1868d84882caSNeel Natu slotinfo = &pci_businfo[bus]->slotinfo[slot]; 18693cbf3585SJohn Baldwin for (pin = 0; pin < 4; pin++) { 1870d84882caSNeel Natu if (slotinfo->si_intpins[pin].ii_count != 0) 18713cbf3585SJohn Baldwin count++; 18723cbf3585SJohn Baldwin } 18733cbf3585SJohn Baldwin } 1874d84882caSNeel Natu } 18753cbf3585SJohn Baldwin return (count); 18763cbf3585SJohn Baldwin } 18773cbf3585SJohn Baldwin 18783cbf3585SJohn Baldwin void 1879d84882caSNeel Natu pci_walk_lintr(int bus, pci_lintr_cb cb, void *arg) 18803cbf3585SJohn Baldwin { 1881d84882caSNeel Natu struct businfo *bi; 1882d84882caSNeel Natu struct slotinfo *si; 18833cbf3585SJohn Baldwin struct intxinfo *ii; 18843cbf3585SJohn Baldwin int slot, pin; 18853cbf3585SJohn Baldwin 1886d84882caSNeel Natu if ((bi = pci_businfo[bus]) == NULL) 1887d84882caSNeel Natu return; 1888d84882caSNeel Natu 18893cbf3585SJohn Baldwin for (slot = 0; slot < MAXSLOTS; slot++) { 1890d84882caSNeel Natu si = &bi->slotinfo[slot]; 18913cbf3585SJohn Baldwin for (pin = 0; pin < 4; pin++) { 1892d84882caSNeel Natu ii = &si->si_intpins[pin]; 18933cbf3585SJohn Baldwin if (ii->ii_count != 0) 1894b3e9732aSJohn Baldwin cb(bus, slot, pin + 1, ii->ii_pirq_pin, 1895b3e9732aSJohn Baldwin ii->ii_ioapic_irq, arg); 18963cbf3585SJohn Baldwin } 18970038ee98SPeter Grehan } 1898ac7304a7SNeel Natu } 18990038ee98SPeter Grehan 190099d65389SNeel Natu /* 190199d65389SNeel Natu * Return 1 if the emulated device in 'slot' is a multi-function device. 190299d65389SNeel Natu * Return 0 otherwise. 190399d65389SNeel Natu */ 190499d65389SNeel Natu static int 1905d84882caSNeel Natu pci_emul_is_mfdev(int bus, int slot) 190699d65389SNeel Natu { 1907d84882caSNeel Natu struct businfo *bi; 1908d84882caSNeel Natu struct slotinfo *si; 190999d65389SNeel Natu int f, numfuncs; 19100038ee98SPeter Grehan 191199d65389SNeel Natu numfuncs = 0; 1912d84882caSNeel Natu if ((bi = pci_businfo[bus]) != NULL) { 1913d84882caSNeel Natu si = &bi->slotinfo[slot]; 191499d65389SNeel Natu for (f = 0; f < MAXFUNCS; f++) { 1915d84882caSNeel Natu if (si->si_funcs[f].fi_devi != NULL) { 191699d65389SNeel Natu numfuncs++; 191799d65389SNeel Natu } 191899d65389SNeel Natu } 1919d84882caSNeel Natu } 192099d65389SNeel Natu return (numfuncs > 1); 192199d65389SNeel Natu } 192299d65389SNeel Natu 192399d65389SNeel Natu /* 192499d65389SNeel Natu * Ensure that the PCIM_MFDEV bit is properly set (or unset) depending on 192599d65389SNeel Natu * whether or not is a multi-function being emulated in the pci 'slot'. 192699d65389SNeel Natu */ 192799d65389SNeel Natu static void 1928d84882caSNeel Natu pci_emul_hdrtype_fixup(int bus, int slot, int off, int bytes, uint32_t *rv) 192999d65389SNeel Natu { 193099d65389SNeel Natu int mfdev; 193199d65389SNeel Natu 193299d65389SNeel Natu if (off <= PCIR_HDRTYPE && off + bytes > PCIR_HDRTYPE) { 1933d84882caSNeel Natu mfdev = pci_emul_is_mfdev(bus, slot); 193499d65389SNeel Natu switch (bytes) { 193599d65389SNeel Natu case 1: 193699d65389SNeel Natu case 2: 193799d65389SNeel Natu *rv &= ~PCIM_MFDEV; 193899d65389SNeel Natu if (mfdev) { 193999d65389SNeel Natu *rv |= PCIM_MFDEV; 194099d65389SNeel Natu } 194199d65389SNeel Natu break; 194299d65389SNeel Natu case 4: 194399d65389SNeel Natu *rv &= ~(PCIM_MFDEV << 16); 194499d65389SNeel Natu if (mfdev) { 194599d65389SNeel Natu *rv |= (PCIM_MFDEV << 16); 194699d65389SNeel Natu } 194799d65389SNeel Natu break; 194899d65389SNeel Natu } 194999d65389SNeel Natu } 195099d65389SNeel Natu } 19510038ee98SPeter Grehan 195256282675SJohn Baldwin /* 195356282675SJohn Baldwin * Update device state in response to changes to the PCI command 195456282675SJohn Baldwin * register. 195556282675SJohn Baldwin */ 195656282675SJohn Baldwin void 195756282675SJohn Baldwin pci_emul_cmd_changed(struct pci_devinst *pi, uint16_t old) 195856282675SJohn Baldwin { 195956282675SJohn Baldwin int i; 196056282675SJohn Baldwin uint16_t changed, new; 196156282675SJohn Baldwin 196256282675SJohn Baldwin new = pci_get_cfgdata16(pi, PCIR_COMMAND); 196356282675SJohn Baldwin changed = old ^ new; 196456282675SJohn Baldwin 196556282675SJohn Baldwin /* 196656282675SJohn Baldwin * If the MMIO or I/O address space decoding has changed then 196756282675SJohn Baldwin * register/unregister all BARs that decode that address space. 196856282675SJohn Baldwin */ 1969e47fe318SCorvin Köhne for (i = 0; i <= PCI_BARMAX_WITH_ROM; i++) { 197056282675SJohn Baldwin switch (pi->pi_bar[i].type) { 197156282675SJohn Baldwin case PCIBAR_NONE: 197256282675SJohn Baldwin case PCIBAR_MEMHI64: 197356282675SJohn Baldwin break; 197456282675SJohn Baldwin case PCIBAR_IO: 197556282675SJohn Baldwin /* I/O address space decoding changed? */ 197656282675SJohn Baldwin if (changed & PCIM_CMD_PORTEN) { 197756282675SJohn Baldwin if (new & PCIM_CMD_PORTEN) 197856282675SJohn Baldwin register_bar(pi, i); 197956282675SJohn Baldwin else 198056282675SJohn Baldwin unregister_bar(pi, i); 198156282675SJohn Baldwin } 198256282675SJohn Baldwin break; 1983e47fe318SCorvin Köhne case PCIBAR_ROM: 1984e47fe318SCorvin Köhne /* skip (un-)register of ROM if it disabled */ 1985e47fe318SCorvin Köhne if (!romen(pi)) 1986e47fe318SCorvin Köhne break; 1987e47fe318SCorvin Köhne /* fallthrough */ 198856282675SJohn Baldwin case PCIBAR_MEM32: 198956282675SJohn Baldwin case PCIBAR_MEM64: 199056282675SJohn Baldwin /* MMIO address space decoding changed? */ 199156282675SJohn Baldwin if (changed & PCIM_CMD_MEMEN) { 199256282675SJohn Baldwin if (new & PCIM_CMD_MEMEN) 199356282675SJohn Baldwin register_bar(pi, i); 199456282675SJohn Baldwin else 199556282675SJohn Baldwin unregister_bar(pi, i); 199656282675SJohn Baldwin } 199756282675SJohn Baldwin break; 199856282675SJohn Baldwin default: 199956282675SJohn Baldwin assert(0); 200056282675SJohn Baldwin } 200156282675SJohn Baldwin } 200256282675SJohn Baldwin 200356282675SJohn Baldwin /* 200456282675SJohn Baldwin * If INTx has been unmasked and is pending, assert the 200556282675SJohn Baldwin * interrupt. 200656282675SJohn Baldwin */ 200756282675SJohn Baldwin pci_lintr_update(pi); 200856282675SJohn Baldwin } 200956282675SJohn Baldwin 2010028d9311SNeel Natu static void 201154335630SNeel Natu pci_emul_cmdsts_write(struct pci_devinst *pi, int coff, uint32_t new, int bytes) 2012028d9311SNeel Natu { 201356282675SJohn Baldwin int rshift; 201456282675SJohn Baldwin uint32_t cmd, old, readonly; 201554335630SNeel Natu 201654335630SNeel Natu cmd = pci_get_cfgdata16(pi, PCIR_COMMAND); /* stash old value */ 2017028d9311SNeel Natu 2018028d9311SNeel Natu /* 201954335630SNeel Natu * From PCI Local Bus Specification 3.0 sections 6.2.2 and 6.2.3. 202054335630SNeel Natu * 202154335630SNeel Natu * XXX Bits 8, 11, 12, 13, 14 and 15 in the status register are 202254335630SNeel Natu * 'write 1 to clear'. However these bits are not set to '1' by 202354335630SNeel Natu * any device emulation so it is simpler to treat them as readonly. 2024028d9311SNeel Natu */ 202554335630SNeel Natu rshift = (coff & 0x3) * 8; 202654335630SNeel Natu readonly = 0xFFFFF880 >> rshift; 2027028d9311SNeel Natu 202854335630SNeel Natu old = CFGREAD(pi, coff, bytes); 202954335630SNeel Natu new &= ~readonly; 203054335630SNeel Natu new |= (old & readonly); 203154335630SNeel Natu CFGWRITE(pi, coff, new, bytes); /* update config */ 203254335630SNeel Natu 203356282675SJohn Baldwin pci_emul_cmd_changed(pi, cmd); 2034028d9311SNeel Natu } 2035028d9311SNeel Natu 203612a6eb99SNeel Natu static void 203712a6eb99SNeel Natu pci_cfgrw(struct vmctx *ctx, int vcpu, int in, int bus, int slot, int func, 203812a6eb99SNeel Natu int coff, int bytes, uint32_t *eax) 2039366f6083SPeter Grehan { 2040d84882caSNeel Natu struct businfo *bi; 2041d84882caSNeel Natu struct slotinfo *si; 2042366f6083SPeter Grehan struct pci_devinst *pi; 2043366f6083SPeter Grehan struct pci_devemu *pe; 204412a6eb99SNeel Natu int idx, needcfg; 2045028d9311SNeel Natu uint64_t addr, bar, mask; 2046366f6083SPeter Grehan 204712a6eb99SNeel Natu if ((bi = pci_businfo[bus]) != NULL) { 204812a6eb99SNeel Natu si = &bi->slotinfo[slot]; 204912a6eb99SNeel Natu pi = si->si_funcs[func].fi_devi; 2050d84882caSNeel Natu } else 205190415e0bSNeel Natu pi = NULL; 205290415e0bSNeel Natu 205399d65389SNeel Natu /* 205412a6eb99SNeel Natu * Just return if there is no device at this slot:func or if the 205512a6eb99SNeel Natu * the guest is doing an un-aligned access. 205699d65389SNeel Natu */ 205712a6eb99SNeel Natu if (pi == NULL || (bytes != 1 && bytes != 2 && bytes != 4) || 205812a6eb99SNeel Natu (coff & (bytes - 1)) != 0) { 2059366f6083SPeter Grehan if (in) 2060366f6083SPeter Grehan *eax = 0xffffffff; 206112a6eb99SNeel Natu return; 206212a6eb99SNeel Natu } 206312a6eb99SNeel Natu 206412a6eb99SNeel Natu /* 206512a6eb99SNeel Natu * Ignore all writes beyond the standard config space and return all 206612a6eb99SNeel Natu * ones on reads. 206712a6eb99SNeel Natu */ 206812a6eb99SNeel Natu if (coff >= PCI_REGMAX + 1) { 206912a6eb99SNeel Natu if (in) { 207012a6eb99SNeel Natu *eax = 0xffffffff; 207112a6eb99SNeel Natu /* 207212a6eb99SNeel Natu * Extended capabilities begin at offset 256 in config 207312a6eb99SNeel Natu * space. Absence of extended capabilities is signaled 207412a6eb99SNeel Natu * with all 0s in the extended capability header at 207512a6eb99SNeel Natu * offset 256. 207612a6eb99SNeel Natu */ 207712a6eb99SNeel Natu if (coff <= PCI_REGMAX + 4) 207812a6eb99SNeel Natu *eax = 0x00000000; 207912a6eb99SNeel Natu } 208012a6eb99SNeel Natu return; 2081366f6083SPeter Grehan } 2082366f6083SPeter Grehan 2083366f6083SPeter Grehan pe = pi->pi_d; 2084366f6083SPeter Grehan 2085366f6083SPeter Grehan /* 2086366f6083SPeter Grehan * Config read 2087366f6083SPeter Grehan */ 2088366f6083SPeter Grehan if (in) { 2089366f6083SPeter Grehan /* Let the device emulation override the default handler */ 209099d65389SNeel Natu if (pe->pe_cfgread != NULL) { 209112a6eb99SNeel Natu needcfg = pe->pe_cfgread(ctx, vcpu, pi, coff, bytes, 209212a6eb99SNeel Natu eax); 209399d65389SNeel Natu } else { 209499d65389SNeel Natu needcfg = 1; 209599d65389SNeel Natu } 2096366f6083SPeter Grehan 209754335630SNeel Natu if (needcfg) 209854335630SNeel Natu *eax = CFGREAD(pi, coff, bytes); 209999d65389SNeel Natu 210012a6eb99SNeel Natu pci_emul_hdrtype_fixup(bus, slot, coff, bytes, eax); 2101366f6083SPeter Grehan } else { 2102366f6083SPeter Grehan /* Let the device emulation override the default handler */ 2103366f6083SPeter Grehan if (pe->pe_cfgwrite != NULL && 2104366f6083SPeter Grehan (*pe->pe_cfgwrite)(ctx, vcpu, pi, coff, bytes, *eax) == 0) 210512a6eb99SNeel Natu return; 2106366f6083SPeter Grehan 2107366f6083SPeter Grehan /* 2108e47fe318SCorvin Köhne * Special handling for write to BAR and ROM registers 2109366f6083SPeter Grehan */ 2110e47fe318SCorvin Köhne if ((coff >= PCIR_BAR(0) && coff < PCIR_BAR(PCI_BARMAX + 1)) || 2111e47fe318SCorvin Köhne (coff >= PCIR_BIOS && coff < PCIR_BIOS + 4)) { 2112366f6083SPeter Grehan /* 2113366f6083SPeter Grehan * Ignore writes to BAR registers that are not 2114366f6083SPeter Grehan * 4-byte aligned. 2115366f6083SPeter Grehan */ 2116366f6083SPeter Grehan if (bytes != 4 || (coff & 0x3) != 0) 211712a6eb99SNeel Natu return; 2118e47fe318SCorvin Köhne if (coff != PCIR_BIOS) { 2119366f6083SPeter Grehan idx = (coff - PCIR_BAR(0)) / 4; 2120e47fe318SCorvin Köhne } else { 2121e47fe318SCorvin Köhne idx = PCI_ROM_IDX; 2122e47fe318SCorvin Köhne } 2123028d9311SNeel Natu mask = ~(pi->pi_bar[idx].size - 1); 2124366f6083SPeter Grehan switch (pi->pi_bar[idx].type) { 2125366f6083SPeter Grehan case PCIBAR_NONE: 2126028d9311SNeel Natu pi->pi_bar[idx].addr = bar = 0; 2127366f6083SPeter Grehan break; 2128366f6083SPeter Grehan case PCIBAR_IO: 2129028d9311SNeel Natu addr = *eax & mask; 2130028d9311SNeel Natu addr &= 0xffff; 2131e87a6f3eSCorvin Köhne bar = addr | pi->pi_bar[idx].lobits; 2132028d9311SNeel Natu /* 2133028d9311SNeel Natu * Register the new BAR value for interception 2134028d9311SNeel Natu */ 2135028d9311SNeel Natu if (addr != pi->pi_bar[idx].addr) { 2136028d9311SNeel Natu update_bar_address(pi, addr, idx, 2137028d9311SNeel Natu PCIBAR_IO); 2138028d9311SNeel Natu } 2139366f6083SPeter Grehan break; 2140366f6083SPeter Grehan case PCIBAR_MEM32: 2141028d9311SNeel Natu addr = bar = *eax & mask; 2142e87a6f3eSCorvin Köhne bar |= pi->pi_bar[idx].lobits; 2143028d9311SNeel Natu if (addr != pi->pi_bar[idx].addr) { 2144028d9311SNeel Natu update_bar_address(pi, addr, idx, 2145028d9311SNeel Natu PCIBAR_MEM32); 2146028d9311SNeel Natu } 2147366f6083SPeter Grehan break; 2148366f6083SPeter Grehan case PCIBAR_MEM64: 2149028d9311SNeel Natu addr = bar = *eax & mask; 2150e87a6f3eSCorvin Köhne bar |= pi->pi_bar[idx].lobits; 2151028d9311SNeel Natu if (addr != (uint32_t)pi->pi_bar[idx].addr) { 2152028d9311SNeel Natu update_bar_address(pi, addr, idx, 2153028d9311SNeel Natu PCIBAR_MEM64); 2154028d9311SNeel Natu } 2155366f6083SPeter Grehan break; 2156366f6083SPeter Grehan case PCIBAR_MEMHI64: 2157366f6083SPeter Grehan mask = ~(pi->pi_bar[idx - 1].size - 1); 2158028d9311SNeel Natu addr = ((uint64_t)*eax << 32) & mask; 2159028d9311SNeel Natu bar = addr >> 32; 2160028d9311SNeel Natu if (bar != pi->pi_bar[idx - 1].addr >> 32) { 2161028d9311SNeel Natu update_bar_address(pi, addr, idx - 1, 2162028d9311SNeel Natu PCIBAR_MEMHI64); 2163028d9311SNeel Natu } 2164366f6083SPeter Grehan break; 2165e47fe318SCorvin Köhne case PCIBAR_ROM: 2166e47fe318SCorvin Köhne addr = bar = *eax & mask; 2167e47fe318SCorvin Köhne if (memen(pi) && romen(pi)) { 2168e47fe318SCorvin Köhne unregister_bar(pi, idx); 2169e47fe318SCorvin Köhne } 2170e47fe318SCorvin Köhne pi->pi_bar[idx].addr = addr; 2171e47fe318SCorvin Köhne pi->pi_bar[idx].lobits = *eax & 2172e47fe318SCorvin Köhne PCIM_BIOS_ENABLE; 2173e47fe318SCorvin Köhne /* romen could have changed it value */ 2174e47fe318SCorvin Köhne if (memen(pi) && romen(pi)) { 2175e47fe318SCorvin Köhne register_bar(pi, idx); 2176e47fe318SCorvin Köhne } 2177e47fe318SCorvin Köhne bar |= pi->pi_bar[idx].lobits; 2178e47fe318SCorvin Köhne break; 2179366f6083SPeter Grehan default: 2180366f6083SPeter Grehan assert(0); 2181366f6083SPeter Grehan } 2182366f6083SPeter Grehan pci_set_cfgdata32(pi, coff, bar); 2183cd942e0fSPeter Grehan 2184366f6083SPeter Grehan } else if (pci_emul_iscap(pi, coff)) { 218521368498SPeter Grehan pci_emul_capwrite(pi, coff, bytes, *eax, 0, 0); 218654335630SNeel Natu } else if (coff >= PCIR_COMMAND && coff < PCIR_REVID) { 218754335630SNeel Natu pci_emul_cmdsts_write(pi, coff, *eax, bytes); 2188366f6083SPeter Grehan } else { 2189366f6083SPeter Grehan CFGWRITE(pi, coff, *eax, bytes); 2190366f6083SPeter Grehan } 2191366f6083SPeter Grehan } 219212a6eb99SNeel Natu } 2193366f6083SPeter Grehan 219412a6eb99SNeel Natu static int cfgenable, cfgbus, cfgslot, cfgfunc, cfgoff; 219512a6eb99SNeel Natu 219612a6eb99SNeel Natu static int 219712a6eb99SNeel Natu pci_emul_cfgaddr(struct vmctx *ctx, int vcpu, int in, int port, int bytes, 219812a6eb99SNeel Natu uint32_t *eax, void *arg) 219912a6eb99SNeel Natu { 220012a6eb99SNeel Natu uint32_t x; 220112a6eb99SNeel Natu 220212a6eb99SNeel Natu if (bytes != 4) { 220312a6eb99SNeel Natu if (in) 220412a6eb99SNeel Natu *eax = (bytes == 2) ? 0xffff : 0xff; 220512a6eb99SNeel Natu return (0); 220612a6eb99SNeel Natu } 220712a6eb99SNeel Natu 220812a6eb99SNeel Natu if (in) { 220912a6eb99SNeel Natu x = (cfgbus << 16) | (cfgslot << 11) | (cfgfunc << 8) | cfgoff; 221012a6eb99SNeel Natu if (cfgenable) 221112a6eb99SNeel Natu x |= CONF1_ENABLE; 221212a6eb99SNeel Natu *eax = x; 221312a6eb99SNeel Natu } else { 221412a6eb99SNeel Natu x = *eax; 221512a6eb99SNeel Natu cfgenable = (x & CONF1_ENABLE) == CONF1_ENABLE; 22161b0e2f0bSCorvin Köhne cfgoff = (x & PCI_REGMAX) & ~0x03; 221712a6eb99SNeel Natu cfgfunc = (x >> 8) & PCI_FUNCMAX; 221812a6eb99SNeel Natu cfgslot = (x >> 11) & PCI_SLOTMAX; 221912a6eb99SNeel Natu cfgbus = (x >> 16) & PCI_BUSMAX; 222012a6eb99SNeel Natu } 222112a6eb99SNeel Natu 222212a6eb99SNeel Natu return (0); 222312a6eb99SNeel Natu } 222412a6eb99SNeel Natu INOUT_PORT(pci_cfgaddr, CONF1_ADDR_PORT, IOPORT_F_INOUT, pci_emul_cfgaddr); 222512a6eb99SNeel Natu 222612a6eb99SNeel Natu static int 222712a6eb99SNeel Natu pci_emul_cfgdata(struct vmctx *ctx, int vcpu, int in, int port, int bytes, 222812a6eb99SNeel Natu uint32_t *eax, void *arg) 222912a6eb99SNeel Natu { 223012a6eb99SNeel Natu int coff; 223112a6eb99SNeel Natu 223212a6eb99SNeel Natu assert(bytes == 1 || bytes == 2 || bytes == 4); 223312a6eb99SNeel Natu 223412a6eb99SNeel Natu coff = cfgoff + (port - CONF1_DATA_PORT); 223512a6eb99SNeel Natu if (cfgenable) { 223612a6eb99SNeel Natu pci_cfgrw(ctx, vcpu, in, cfgbus, cfgslot, cfgfunc, coff, bytes, 223712a6eb99SNeel Natu eax); 223812a6eb99SNeel Natu } else { 223912a6eb99SNeel Natu /* Ignore accesses to cfgdata if not enabled by cfgaddr */ 224012a6eb99SNeel Natu if (in) 224112a6eb99SNeel Natu *eax = 0xffffffff; 224212a6eb99SNeel Natu } 2243366f6083SPeter Grehan return (0); 2244366f6083SPeter Grehan } 2245366f6083SPeter Grehan 2246366f6083SPeter Grehan INOUT_PORT(pci_cfgdata, CONF1_DATA_PORT+0, IOPORT_F_INOUT, pci_emul_cfgdata); 2247366f6083SPeter Grehan INOUT_PORT(pci_cfgdata, CONF1_DATA_PORT+1, IOPORT_F_INOUT, pci_emul_cfgdata); 2248366f6083SPeter Grehan INOUT_PORT(pci_cfgdata, CONF1_DATA_PORT+2, IOPORT_F_INOUT, pci_emul_cfgdata); 2249366f6083SPeter Grehan INOUT_PORT(pci_cfgdata, CONF1_DATA_PORT+3, IOPORT_F_INOUT, pci_emul_cfgdata); 2250366f6083SPeter Grehan 2251483d953aSJohn Baldwin #ifdef BHYVE_SNAPSHOT 2252483d953aSJohn Baldwin /* 2253483d953aSJohn Baldwin * Saves/restores PCI device emulated state. Returns 0 on success. 2254483d953aSJohn Baldwin */ 2255483d953aSJohn Baldwin static int 2256483d953aSJohn Baldwin pci_snapshot_pci_dev(struct vm_snapshot_meta *meta) 2257483d953aSJohn Baldwin { 2258483d953aSJohn Baldwin struct pci_devinst *pi; 2259483d953aSJohn Baldwin int i; 2260483d953aSJohn Baldwin int ret; 2261483d953aSJohn Baldwin 2262483d953aSJohn Baldwin pi = meta->dev_data; 2263483d953aSJohn Baldwin 2264483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(pi->pi_msi.enabled, meta, ret, done); 2265483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(pi->pi_msi.addr, meta, ret, done); 2266483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(pi->pi_msi.msg_data, meta, ret, done); 2267483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(pi->pi_msi.maxmsgnum, meta, ret, done); 2268483d953aSJohn Baldwin 2269483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(pi->pi_msix.enabled, meta, ret, done); 2270483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(pi->pi_msix.table_bar, meta, ret, done); 2271483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(pi->pi_msix.pba_bar, meta, ret, done); 2272483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(pi->pi_msix.table_offset, meta, ret, done); 2273483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(pi->pi_msix.table_count, meta, ret, done); 2274483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(pi->pi_msix.pba_offset, meta, ret, done); 2275483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(pi->pi_msix.pba_size, meta, ret, done); 2276483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(pi->pi_msix.function_mask, meta, ret, done); 2277483d953aSJohn Baldwin 2278483d953aSJohn Baldwin SNAPSHOT_BUF_OR_LEAVE(pi->pi_cfgdata, sizeof(pi->pi_cfgdata), 2279483d953aSJohn Baldwin meta, ret, done); 2280483d953aSJohn Baldwin 2281483d953aSJohn Baldwin for (i = 0; i < nitems(pi->pi_bar); i++) { 2282483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(pi->pi_bar[i].type, meta, ret, done); 2283483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(pi->pi_bar[i].size, meta, ret, done); 2284483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(pi->pi_bar[i].addr, meta, ret, done); 2285483d953aSJohn Baldwin } 2286483d953aSJohn Baldwin 2287483d953aSJohn Baldwin /* Restore MSI-X table. */ 2288483d953aSJohn Baldwin for (i = 0; i < pi->pi_msix.table_count; i++) { 2289483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(pi->pi_msix.table[i].addr, 2290483d953aSJohn Baldwin meta, ret, done); 2291483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(pi->pi_msix.table[i].msg_data, 2292483d953aSJohn Baldwin meta, ret, done); 2293483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(pi->pi_msix.table[i].vector_control, 2294483d953aSJohn Baldwin meta, ret, done); 2295483d953aSJohn Baldwin } 2296483d953aSJohn Baldwin 2297483d953aSJohn Baldwin done: 2298483d953aSJohn Baldwin return (ret); 2299483d953aSJohn Baldwin } 2300483d953aSJohn Baldwin 2301483d953aSJohn Baldwin static int 2302483d953aSJohn Baldwin pci_find_slotted_dev(const char *dev_name, struct pci_devemu **pde, 2303483d953aSJohn Baldwin struct pci_devinst **pdi) 2304483d953aSJohn Baldwin { 2305483d953aSJohn Baldwin struct businfo *bi; 2306483d953aSJohn Baldwin struct slotinfo *si; 2307483d953aSJohn Baldwin struct funcinfo *fi; 2308483d953aSJohn Baldwin int bus, slot, func; 2309483d953aSJohn Baldwin 2310483d953aSJohn Baldwin assert(dev_name != NULL); 2311483d953aSJohn Baldwin assert(pde != NULL); 2312483d953aSJohn Baldwin assert(pdi != NULL); 2313483d953aSJohn Baldwin 2314483d953aSJohn Baldwin for (bus = 0; bus < MAXBUSES; bus++) { 2315483d953aSJohn Baldwin if ((bi = pci_businfo[bus]) == NULL) 2316483d953aSJohn Baldwin continue; 2317483d953aSJohn Baldwin 2318483d953aSJohn Baldwin for (slot = 0; slot < MAXSLOTS; slot++) { 2319483d953aSJohn Baldwin si = &bi->slotinfo[slot]; 2320483d953aSJohn Baldwin for (func = 0; func < MAXFUNCS; func++) { 2321483d953aSJohn Baldwin fi = &si->si_funcs[func]; 2322621b5090SJohn Baldwin if (fi->fi_pde == NULL) 2323483d953aSJohn Baldwin continue; 2324621b5090SJohn Baldwin if (strcmp(dev_name, fi->fi_pde->pe_emu) != 0) 2325483d953aSJohn Baldwin continue; 2326483d953aSJohn Baldwin 2327621b5090SJohn Baldwin *pde = fi->fi_pde; 2328483d953aSJohn Baldwin *pdi = fi->fi_devi; 2329483d953aSJohn Baldwin return (0); 2330483d953aSJohn Baldwin } 2331483d953aSJohn Baldwin } 2332483d953aSJohn Baldwin } 2333483d953aSJohn Baldwin 2334483d953aSJohn Baldwin return (EINVAL); 2335483d953aSJohn Baldwin } 2336483d953aSJohn Baldwin 2337483d953aSJohn Baldwin int 2338483d953aSJohn Baldwin pci_snapshot(struct vm_snapshot_meta *meta) 2339483d953aSJohn Baldwin { 2340483d953aSJohn Baldwin struct pci_devemu *pde; 2341483d953aSJohn Baldwin struct pci_devinst *pdi; 2342483d953aSJohn Baldwin int ret; 2343483d953aSJohn Baldwin 2344483d953aSJohn Baldwin assert(meta->dev_name != NULL); 2345483d953aSJohn Baldwin 2346483d953aSJohn Baldwin ret = pci_find_slotted_dev(meta->dev_name, &pde, &pdi); 2347483d953aSJohn Baldwin if (ret != 0) { 2348483d953aSJohn Baldwin fprintf(stderr, "%s: no such name: %s\r\n", 2349483d953aSJohn Baldwin __func__, meta->dev_name); 2350483d953aSJohn Baldwin memset(meta->buffer.buf_start, 0, meta->buffer.buf_size); 2351483d953aSJohn Baldwin return (0); 2352483d953aSJohn Baldwin } 2353483d953aSJohn Baldwin 2354483d953aSJohn Baldwin meta->dev_data = pdi; 2355483d953aSJohn Baldwin 2356483d953aSJohn Baldwin if (pde->pe_snapshot == NULL) { 2357483d953aSJohn Baldwin fprintf(stderr, "%s: not implemented yet for: %s\r\n", 2358483d953aSJohn Baldwin __func__, meta->dev_name); 2359483d953aSJohn Baldwin return (-1); 2360483d953aSJohn Baldwin } 2361483d953aSJohn Baldwin 2362483d953aSJohn Baldwin ret = pci_snapshot_pci_dev(meta); 2363483d953aSJohn Baldwin if (ret != 0) { 2364483d953aSJohn Baldwin fprintf(stderr, "%s: failed to snapshot pci dev\r\n", 2365483d953aSJohn Baldwin __func__); 2366483d953aSJohn Baldwin return (-1); 2367483d953aSJohn Baldwin } 2368483d953aSJohn Baldwin 2369483d953aSJohn Baldwin ret = (*pde->pe_snapshot)(meta); 2370483d953aSJohn Baldwin 2371483d953aSJohn Baldwin return (ret); 2372483d953aSJohn Baldwin } 2373483d953aSJohn Baldwin 2374483d953aSJohn Baldwin int 2375483d953aSJohn Baldwin pci_pause(struct vmctx *ctx, const char *dev_name) 2376483d953aSJohn Baldwin { 2377483d953aSJohn Baldwin struct pci_devemu *pde; 2378483d953aSJohn Baldwin struct pci_devinst *pdi; 2379483d953aSJohn Baldwin int ret; 2380483d953aSJohn Baldwin 2381483d953aSJohn Baldwin assert(dev_name != NULL); 2382483d953aSJohn Baldwin 2383483d953aSJohn Baldwin ret = pci_find_slotted_dev(dev_name, &pde, &pdi); 2384483d953aSJohn Baldwin if (ret != 0) { 2385483d953aSJohn Baldwin /* 2386483d953aSJohn Baldwin * It is possible to call this function without 2387483d953aSJohn Baldwin * checking that the device is inserted first. 2388483d953aSJohn Baldwin */ 2389483d953aSJohn Baldwin fprintf(stderr, "%s: no such name: %s\n", __func__, dev_name); 2390483d953aSJohn Baldwin return (0); 2391483d953aSJohn Baldwin } 2392483d953aSJohn Baldwin 2393483d953aSJohn Baldwin if (pde->pe_pause == NULL) { 2394483d953aSJohn Baldwin /* The pause/resume functionality is optional. */ 2395483d953aSJohn Baldwin fprintf(stderr, "%s: not implemented for: %s\n", 2396483d953aSJohn Baldwin __func__, dev_name); 2397483d953aSJohn Baldwin return (0); 2398483d953aSJohn Baldwin } 2399483d953aSJohn Baldwin 2400483d953aSJohn Baldwin return (*pde->pe_pause)(ctx, pdi); 2401483d953aSJohn Baldwin } 2402483d953aSJohn Baldwin 2403483d953aSJohn Baldwin int 2404483d953aSJohn Baldwin pci_resume(struct vmctx *ctx, const char *dev_name) 2405483d953aSJohn Baldwin { 2406483d953aSJohn Baldwin struct pci_devemu *pde; 2407483d953aSJohn Baldwin struct pci_devinst *pdi; 2408483d953aSJohn Baldwin int ret; 2409483d953aSJohn Baldwin 2410483d953aSJohn Baldwin assert(dev_name != NULL); 2411483d953aSJohn Baldwin 2412483d953aSJohn Baldwin ret = pci_find_slotted_dev(dev_name, &pde, &pdi); 2413483d953aSJohn Baldwin if (ret != 0) { 2414483d953aSJohn Baldwin /* 2415483d953aSJohn Baldwin * It is possible to call this function without 2416483d953aSJohn Baldwin * checking that the device is inserted first. 2417483d953aSJohn Baldwin */ 2418483d953aSJohn Baldwin fprintf(stderr, "%s: no such name: %s\n", __func__, dev_name); 2419483d953aSJohn Baldwin return (0); 2420483d953aSJohn Baldwin } 2421483d953aSJohn Baldwin 2422483d953aSJohn Baldwin if (pde->pe_resume == NULL) { 2423483d953aSJohn Baldwin /* The pause/resume functionality is optional. */ 2424483d953aSJohn Baldwin fprintf(stderr, "%s: not implemented for: %s\n", 2425483d953aSJohn Baldwin __func__, dev_name); 2426483d953aSJohn Baldwin return (0); 2427483d953aSJohn Baldwin } 2428483d953aSJohn Baldwin 2429483d953aSJohn Baldwin return (*pde->pe_resume)(ctx, pdi); 2430483d953aSJohn Baldwin } 2431483d953aSJohn Baldwin #endif 2432483d953aSJohn Baldwin 2433366f6083SPeter Grehan #define PCI_EMUL_TEST 2434366f6083SPeter Grehan #ifdef PCI_EMUL_TEST 2435366f6083SPeter Grehan /* 2436366f6083SPeter Grehan * Define a dummy test device 2437366f6083SPeter Grehan */ 2438d84882caSNeel Natu #define DIOSZ 8 24394d1e669cSPeter Grehan #define DMEMSZ 4096 2440366f6083SPeter Grehan struct pci_emul_dsoftc { 24414d1e669cSPeter Grehan uint8_t ioregs[DIOSZ]; 2442fd4e0d4cSNeel Natu uint8_t memregs[2][DMEMSZ]; 2443366f6083SPeter Grehan }; 2444366f6083SPeter Grehan 24454d1e669cSPeter Grehan #define PCI_EMUL_MSI_MSGS 4 24464d1e669cSPeter Grehan #define PCI_EMUL_MSIX_MSGS 16 2447366f6083SPeter Grehan 2448b67e81dbSJohn Baldwin static int 2449621b5090SJohn Baldwin pci_emul_dinit(struct vmctx *ctx, struct pci_devinst *pi, nvlist_t *nvl) 2450366f6083SPeter Grehan { 2451366f6083SPeter Grehan int error; 2452366f6083SPeter Grehan struct pci_emul_dsoftc *sc; 2453366f6083SPeter Grehan 2454994f858aSXin LI sc = calloc(1, sizeof(struct pci_emul_dsoftc)); 2455366f6083SPeter Grehan 2456366f6083SPeter Grehan pi->pi_arg = sc; 2457366f6083SPeter Grehan 2458366f6083SPeter Grehan pci_set_cfgdata16(pi, PCIR_DEVICE, 0x0001); 2459366f6083SPeter Grehan pci_set_cfgdata16(pi, PCIR_VENDOR, 0x10DD); 2460366f6083SPeter Grehan pci_set_cfgdata8(pi, PCIR_CLASS, 0x02); 2461366f6083SPeter Grehan 24624d1e669cSPeter Grehan error = pci_emul_add_msicap(pi, PCI_EMUL_MSI_MSGS); 2463366f6083SPeter Grehan assert(error == 0); 2464366f6083SPeter Grehan 24654d1e669cSPeter Grehan error = pci_emul_alloc_bar(pi, 0, PCIBAR_IO, DIOSZ); 24664d1e669cSPeter Grehan assert(error == 0); 24674d1e669cSPeter Grehan 24684d1e669cSPeter Grehan error = pci_emul_alloc_bar(pi, 1, PCIBAR_MEM32, DMEMSZ); 2469366f6083SPeter Grehan assert(error == 0); 2470366f6083SPeter Grehan 2471fd4e0d4cSNeel Natu error = pci_emul_alloc_bar(pi, 2, PCIBAR_MEM32, DMEMSZ); 2472fd4e0d4cSNeel Natu assert(error == 0); 2473fd4e0d4cSNeel Natu 2474366f6083SPeter Grehan return (0); 2475366f6083SPeter Grehan } 2476366f6083SPeter Grehan 2477b67e81dbSJohn Baldwin static void 24784d1e669cSPeter Grehan pci_emul_diow(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int baridx, 24794d1e669cSPeter Grehan uint64_t offset, int size, uint64_t value) 2480366f6083SPeter Grehan { 2481366f6083SPeter Grehan int i; 2482366f6083SPeter Grehan struct pci_emul_dsoftc *sc = pi->pi_arg; 2483366f6083SPeter Grehan 24844d1e669cSPeter Grehan if (baridx == 0) { 24854d1e669cSPeter Grehan if (offset + size > DIOSZ) { 24864d1e669cSPeter Grehan printf("diow: iow too large, offset %ld size %d\n", 24874d1e669cSPeter Grehan offset, size); 2488366f6083SPeter Grehan return; 2489366f6083SPeter Grehan } 2490366f6083SPeter Grehan 2491366f6083SPeter Grehan if (size == 1) { 24924d1e669cSPeter Grehan sc->ioregs[offset] = value & 0xff; 2493366f6083SPeter Grehan } else if (size == 2) { 24944d1e669cSPeter Grehan *(uint16_t *)&sc->ioregs[offset] = value & 0xffff; 24954d1e669cSPeter Grehan } else if (size == 4) { 24964d1e669cSPeter Grehan *(uint32_t *)&sc->ioregs[offset] = value; 2497366f6083SPeter Grehan } else { 24984d1e669cSPeter Grehan printf("diow: iow unknown size %d\n", size); 2499366f6083SPeter Grehan } 2500366f6083SPeter Grehan 2501366f6083SPeter Grehan /* 2502366f6083SPeter Grehan * Special magic value to generate an interrupt 2503366f6083SPeter Grehan */ 2504366f6083SPeter Grehan if (offset == 4 && size == 4 && pci_msi_enabled(pi)) 25054f8be175SNeel Natu pci_generate_msi(pi, value % pci_msi_maxmsgnum(pi)); 2506366f6083SPeter Grehan 2507366f6083SPeter Grehan if (value == 0xabcdef) { 25084f8be175SNeel Natu for (i = 0; i < pci_msi_maxmsgnum(pi); i++) 2509366f6083SPeter Grehan pci_generate_msi(pi, i); 2510366f6083SPeter Grehan } 2511366f6083SPeter Grehan } 2512366f6083SPeter Grehan 2513fd4e0d4cSNeel Natu if (baridx == 1 || baridx == 2) { 25144d1e669cSPeter Grehan if (offset + size > DMEMSZ) { 25154d1e669cSPeter Grehan printf("diow: memw too large, offset %ld size %d\n", 25164d1e669cSPeter Grehan offset, size); 25174d1e669cSPeter Grehan return; 25184d1e669cSPeter Grehan } 25194d1e669cSPeter Grehan 2520fd4e0d4cSNeel Natu i = baridx - 1; /* 'memregs' index */ 2521fd4e0d4cSNeel Natu 25224d1e669cSPeter Grehan if (size == 1) { 2523fd4e0d4cSNeel Natu sc->memregs[i][offset] = value; 25244d1e669cSPeter Grehan } else if (size == 2) { 2525fd4e0d4cSNeel Natu *(uint16_t *)&sc->memregs[i][offset] = value; 25264d1e669cSPeter Grehan } else if (size == 4) { 2527fd4e0d4cSNeel Natu *(uint32_t *)&sc->memregs[i][offset] = value; 25284d1e669cSPeter Grehan } else if (size == 8) { 2529fd4e0d4cSNeel Natu *(uint64_t *)&sc->memregs[i][offset] = value; 25304d1e669cSPeter Grehan } else { 25314d1e669cSPeter Grehan printf("diow: memw unknown size %d\n", size); 25324d1e669cSPeter Grehan } 25334d1e669cSPeter Grehan 25344d1e669cSPeter Grehan /* 25354d1e669cSPeter Grehan * magic interrupt ?? 25364d1e669cSPeter Grehan */ 25374d1e669cSPeter Grehan } 25384d1e669cSPeter Grehan 25399f3dba68SPedro F. Giffuni if (baridx > 2 || baridx < 0) { 25404d1e669cSPeter Grehan printf("diow: unknown bar idx %d\n", baridx); 25414d1e669cSPeter Grehan } 25424d1e669cSPeter Grehan } 25434d1e669cSPeter Grehan 25444d1e669cSPeter Grehan static uint64_t 25454d1e669cSPeter Grehan pci_emul_dior(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int baridx, 25464d1e669cSPeter Grehan uint64_t offset, int size) 2547366f6083SPeter Grehan { 2548366f6083SPeter Grehan struct pci_emul_dsoftc *sc = pi->pi_arg; 2549366f6083SPeter Grehan uint32_t value; 2550fd4e0d4cSNeel Natu int i; 2551366f6083SPeter Grehan 25524d1e669cSPeter Grehan if (baridx == 0) { 25534d1e669cSPeter Grehan if (offset + size > DIOSZ) { 25544d1e669cSPeter Grehan printf("dior: ior too large, offset %ld size %d\n", 25554d1e669cSPeter Grehan offset, size); 2556366f6083SPeter Grehan return (0); 2557366f6083SPeter Grehan } 2558366f6083SPeter Grehan 25596e43f3edSPedro F. Giffuni value = 0; 2560366f6083SPeter Grehan if (size == 1) { 25614d1e669cSPeter Grehan value = sc->ioregs[offset]; 2562366f6083SPeter Grehan } else if (size == 2) { 25634d1e669cSPeter Grehan value = *(uint16_t *) &sc->ioregs[offset]; 25644d1e669cSPeter Grehan } else if (size == 4) { 25654d1e669cSPeter Grehan value = *(uint32_t *) &sc->ioregs[offset]; 2566366f6083SPeter Grehan } else { 25674d1e669cSPeter Grehan printf("dior: ior unknown size %d\n", size); 25684d1e669cSPeter Grehan } 25694d1e669cSPeter Grehan } 25704d1e669cSPeter Grehan 2571fd4e0d4cSNeel Natu if (baridx == 1 || baridx == 2) { 25724d1e669cSPeter Grehan if (offset + size > DMEMSZ) { 25734d1e669cSPeter Grehan printf("dior: memr too large, offset %ld size %d\n", 25744d1e669cSPeter Grehan offset, size); 25754d1e669cSPeter Grehan return (0); 25764d1e669cSPeter Grehan } 25774d1e669cSPeter Grehan 2578fd4e0d4cSNeel Natu i = baridx - 1; /* 'memregs' index */ 2579fd4e0d4cSNeel Natu 25804d1e669cSPeter Grehan if (size == 1) { 2581fd4e0d4cSNeel Natu value = sc->memregs[i][offset]; 25824d1e669cSPeter Grehan } else if (size == 2) { 2583fd4e0d4cSNeel Natu value = *(uint16_t *) &sc->memregs[i][offset]; 25844d1e669cSPeter Grehan } else if (size == 4) { 2585fd4e0d4cSNeel Natu value = *(uint32_t *) &sc->memregs[i][offset]; 25864d1e669cSPeter Grehan } else if (size == 8) { 2587fd4e0d4cSNeel Natu value = *(uint64_t *) &sc->memregs[i][offset]; 25884d1e669cSPeter Grehan } else { 25894d1e669cSPeter Grehan printf("dior: ior unknown size %d\n", size); 25904d1e669cSPeter Grehan } 25914d1e669cSPeter Grehan } 25924d1e669cSPeter Grehan 25934d1e669cSPeter Grehan 25949f3dba68SPedro F. Giffuni if (baridx > 2 || baridx < 0) { 25954d1e669cSPeter Grehan printf("dior: unknown bar idx %d\n", baridx); 25964d1e669cSPeter Grehan return (0); 2597366f6083SPeter Grehan } 2598366f6083SPeter Grehan 2599366f6083SPeter Grehan return (value); 2600366f6083SPeter Grehan } 2601366f6083SPeter Grehan 2602483d953aSJohn Baldwin #ifdef BHYVE_SNAPSHOT 2603483d953aSJohn Baldwin int 2604483d953aSJohn Baldwin pci_emul_snapshot(struct vm_snapshot_meta *meta) 2605483d953aSJohn Baldwin { 2606483d953aSJohn Baldwin 2607483d953aSJohn Baldwin return (0); 2608483d953aSJohn Baldwin } 2609483d953aSJohn Baldwin #endif 2610483d953aSJohn Baldwin 2611366f6083SPeter Grehan struct pci_devemu pci_dummy = { 2612366f6083SPeter Grehan .pe_emu = "dummy", 2613366f6083SPeter Grehan .pe_init = pci_emul_dinit, 26144d1e669cSPeter Grehan .pe_barwrite = pci_emul_diow, 2615483d953aSJohn Baldwin .pe_barread = pci_emul_dior, 2616483d953aSJohn Baldwin #ifdef BHYVE_SNAPSHOT 2617483d953aSJohn Baldwin .pe_snapshot = pci_emul_snapshot, 2618483d953aSJohn Baldwin #endif 2619366f6083SPeter Grehan }; 2620366f6083SPeter Grehan PCI_EMUL_SET(pci_dummy); 2621366f6083SPeter Grehan 2622366f6083SPeter Grehan #endif /* PCI_EMUL_TEST */ 2623