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 }; 11907d82562SMark Johnston 12007d82562SMark Johnston static TAILQ_HEAD(pci_bar_list, pci_bar_allocation) pci_bars = 12107d82562SMark Johnston TAILQ_HEAD_INITIALIZER(pci_bars); 12201f9362eSCorvin Köhne 123366f6083SPeter Grehan #define PCI_EMUL_IOBASE 0x2000 124366f6083SPeter Grehan #define PCI_EMUL_IOLIMIT 0x10000 125366f6083SPeter Grehan 126e47fe318SCorvin Köhne #define PCI_EMUL_ROMSIZE 0x10000000 127e47fe318SCorvin Köhne 12812a6eb99SNeel Natu #define PCI_EMUL_ECFG_BASE 0xE0000000 /* 3.5GB */ 12912a6eb99SNeel Natu #define PCI_EMUL_ECFG_SIZE (MAXBUSES * 1024 * 1024) /* 1MB per bus */ 13012a6eb99SNeel Natu SYSRES_MEM(PCI_EMUL_ECFG_BASE, PCI_EMUL_ECFG_SIZE); 13112a6eb99SNeel Natu 1325cf21e48SCorvin Köhne /* 1335cf21e48SCorvin Köhne * OVMF always uses 0xC0000000 as base address for 32 bit PCI MMIO. Don't 1345cf21e48SCorvin Köhne * change this address without changing it in OVMF. 1355cf21e48SCorvin Köhne */ 1365cf21e48SCorvin Köhne #define PCI_EMUL_MEMBASE32 0xC0000000 13712a6eb99SNeel Natu #define PCI_EMUL_MEMLIMIT32 PCI_EMUL_ECFG_BASE 1384a4053e1SCorvin Köhne #define PCI_EMUL_MEMSIZE64 (32*GB) 139366f6083SPeter Grehan 140621b5090SJohn Baldwin static struct pci_devemu *pci_emul_finddev(const char *name); 141b3e9732aSJohn Baldwin static void pci_lintr_route(struct pci_devinst *pi); 1423cbf3585SJohn Baldwin static void pci_lintr_update(struct pci_devinst *pi); 1436a284cacSJohn Baldwin static void pci_cfgrw(int in, int bus, int slot, int func, int coff, 1446a284cacSJohn Baldwin int bytes, uint32_t *val); 145366f6083SPeter Grehan 14654335630SNeel Natu static __inline void 14754335630SNeel Natu CFGWRITE(struct pci_devinst *pi, int coff, uint32_t val, int bytes) 14854335630SNeel Natu { 14954335630SNeel Natu 15054335630SNeel Natu if (bytes == 1) 15154335630SNeel Natu pci_set_cfgdata8(pi, coff, val); 15254335630SNeel Natu else if (bytes == 2) 15354335630SNeel Natu pci_set_cfgdata16(pi, coff, val); 15454335630SNeel Natu else 15554335630SNeel Natu pci_set_cfgdata32(pi, coff, val); 15654335630SNeel Natu } 15754335630SNeel Natu 15854335630SNeel Natu static __inline uint32_t 15954335630SNeel Natu CFGREAD(struct pci_devinst *pi, int coff, int bytes) 16054335630SNeel Natu { 16154335630SNeel Natu 16254335630SNeel Natu if (bytes == 1) 16354335630SNeel Natu return (pci_get_cfgdata8(pi, coff)); 16454335630SNeel Natu else if (bytes == 2) 16554335630SNeel Natu return (pci_get_cfgdata16(pi, coff)); 16654335630SNeel Natu else 16754335630SNeel Natu return (pci_get_cfgdata32(pi, coff)); 16854335630SNeel Natu } 16954335630SNeel Natu 17045ddbf21SCorvin Köhne static int 17145ddbf21SCorvin Köhne is_pcir_bar(int coff) 17245ddbf21SCorvin Köhne { 17345ddbf21SCorvin Köhne return (coff >= PCIR_BAR(0) && coff < PCIR_BAR(PCI_BARMAX + 1)); 17445ddbf21SCorvin Köhne } 17545ddbf21SCorvin Köhne 17645ddbf21SCorvin Köhne static int 17745ddbf21SCorvin Köhne is_pcir_bios(int coff) 17845ddbf21SCorvin Köhne { 17945ddbf21SCorvin Köhne return (coff >= PCIR_BIOS && coff < PCIR_BIOS + 4); 18045ddbf21SCorvin Köhne } 18145ddbf21SCorvin Köhne 182366f6083SPeter Grehan /* 183366f6083SPeter Grehan * I/O access 184366f6083SPeter Grehan */ 185366f6083SPeter Grehan 186366f6083SPeter Grehan /* 187366f6083SPeter Grehan * Slot options are in the form: 188366f6083SPeter Grehan * 189d84882caSNeel Natu * <bus>:<slot>:<func>,<emul>[,<config>] 19099d65389SNeel Natu * <slot>[:<func>],<emul>[,<config>] 191366f6083SPeter Grehan * 192366f6083SPeter Grehan * slot is 0..31 19399d65389SNeel Natu * func is 0..7 194366f6083SPeter Grehan * emul is a string describing the type of PCI device e.g. virtio-net 195366f6083SPeter Grehan * config is an optional string, depending on the device, that can be 196366f6083SPeter Grehan * used for configuration. 197366f6083SPeter Grehan * Examples are: 198366f6083SPeter Grehan * 1,virtio-net,tap0 19999d65389SNeel Natu * 3:0,dummy 200366f6083SPeter Grehan */ 201366f6083SPeter Grehan static void 202366f6083SPeter Grehan pci_parse_slot_usage(char *aopt) 203366f6083SPeter Grehan { 204b05c77ffSNeel Natu 205332eff95SVincenzo Maffione EPRINTLN("Invalid PCI slot info field \"%s\"", aopt); 206366f6083SPeter Grehan } 207366f6083SPeter Grehan 208621b5090SJohn Baldwin /* 209621b5090SJohn Baldwin * Helper function to parse a list of comma-separated options where 210621b5090SJohn Baldwin * each option is formatted as "name[=value]". If no value is 211621b5090SJohn Baldwin * provided, the option is treated as a boolean and is given a value 212621b5090SJohn Baldwin * of true. 213621b5090SJohn Baldwin */ 214621b5090SJohn Baldwin int 215621b5090SJohn Baldwin pci_parse_legacy_config(nvlist_t *nvl, const char *opt) 216621b5090SJohn Baldwin { 217621b5090SJohn Baldwin char *config, *name, *tofree, *value; 218621b5090SJohn Baldwin 219621b5090SJohn Baldwin if (opt == NULL) 220621b5090SJohn Baldwin return (0); 221621b5090SJohn Baldwin 222621b5090SJohn Baldwin config = tofree = strdup(opt); 223621b5090SJohn Baldwin while ((name = strsep(&config, ",")) != NULL) { 224621b5090SJohn Baldwin value = strchr(name, '='); 225621b5090SJohn Baldwin if (value != NULL) { 226621b5090SJohn Baldwin *value = '\0'; 227621b5090SJohn Baldwin value++; 228621b5090SJohn Baldwin set_config_value_node(nvl, name, value); 229621b5090SJohn Baldwin } else 230621b5090SJohn Baldwin set_config_bool_node(nvl, name, true); 231621b5090SJohn Baldwin } 232621b5090SJohn Baldwin free(tofree); 233621b5090SJohn Baldwin return (0); 234621b5090SJohn Baldwin } 235621b5090SJohn Baldwin 236621b5090SJohn Baldwin /* 237621b5090SJohn Baldwin * PCI device configuration is stored in MIBs that encode the device's 238621b5090SJohn Baldwin * location: 239621b5090SJohn Baldwin * 240621b5090SJohn Baldwin * pci.<bus>.<slot>.<func> 241621b5090SJohn Baldwin * 242621b5090SJohn Baldwin * Where "bus", "slot", and "func" are all decimal values without 243621b5090SJohn Baldwin * leading zeroes. Each valid device must have a "device" node which 244621b5090SJohn Baldwin * identifies the driver model of the device. 245621b5090SJohn Baldwin * 246621b5090SJohn Baldwin * Device backends can provide a parser for the "config" string. If 247621b5090SJohn Baldwin * a custom parser is not provided, pci_parse_legacy_config() is used 248621b5090SJohn Baldwin * to parse the string. 249621b5090SJohn Baldwin */ 250b05c77ffSNeel Natu int 251d2bc4816SJohn Baldwin pci_parse_slot(char *opt) 252366f6083SPeter Grehan { 253621b5090SJohn Baldwin char node_name[sizeof("pci.XXX.XX.X")]; 254621b5090SJohn Baldwin struct pci_devemu *pde; 255d84882caSNeel Natu char *emul, *config, *str, *cp; 256d84882caSNeel Natu int error, bnum, snum, fnum; 257621b5090SJohn Baldwin nvlist_t *nvl; 258366f6083SPeter Grehan 259b05c77ffSNeel Natu error = -1; 260d84882caSNeel Natu str = strdup(opt); 26199d65389SNeel Natu 262d84882caSNeel Natu emul = config = NULL; 263d84882caSNeel Natu if ((cp = strchr(str, ',')) != NULL) { 264d84882caSNeel Natu *cp = '\0'; 265d84882caSNeel Natu emul = cp + 1; 266d84882caSNeel Natu if ((cp = strchr(emul, ',')) != NULL) { 267d84882caSNeel Natu *cp = '\0'; 268d84882caSNeel Natu config = cp + 1; 26999d65389SNeel Natu } 270d84882caSNeel Natu } else { 271b05c77ffSNeel Natu pci_parse_slot_usage(opt); 272b05c77ffSNeel Natu goto done; 273366f6083SPeter Grehan } 274366f6083SPeter Grehan 275d84882caSNeel Natu /* <bus>:<slot>:<func> */ 276d84882caSNeel Natu if (sscanf(str, "%d:%d:%d", &bnum, &snum, &fnum) != 3) { 277d84882caSNeel Natu bnum = 0; 278d84882caSNeel Natu /* <slot>:<func> */ 279d84882caSNeel Natu if (sscanf(str, "%d:%d", &snum, &fnum) != 2) { 280d84882caSNeel Natu fnum = 0; 281d84882caSNeel Natu /* <slot> */ 282d84882caSNeel Natu if (sscanf(str, "%d", &snum) != 1) { 283d84882caSNeel Natu snum = -1; 284d84882caSNeel Natu } 285d84882caSNeel Natu } 286d84882caSNeel Natu } 287b05c77ffSNeel Natu 288d84882caSNeel Natu if (bnum < 0 || bnum >= MAXBUSES || snum < 0 || snum >= MAXSLOTS || 289d84882caSNeel Natu fnum < 0 || fnum >= MAXFUNCS) { 290b05c77ffSNeel Natu pci_parse_slot_usage(opt); 291b05c77ffSNeel Natu goto done; 292b05c77ffSNeel Natu } 293b05c77ffSNeel Natu 294621b5090SJohn Baldwin pde = pci_emul_finddev(emul); 295621b5090SJohn Baldwin if (pde == NULL) { 296621b5090SJohn Baldwin EPRINTLN("pci slot %d:%d:%d: unknown device \"%s\"", bnum, snum, 297621b5090SJohn Baldwin fnum, emul); 298b05c77ffSNeel Natu goto done; 299b05c77ffSNeel Natu } 300b05c77ffSNeel Natu 301621b5090SJohn Baldwin snprintf(node_name, sizeof(node_name), "pci.%d.%d.%d", bnum, snum, 302621b5090SJohn Baldwin fnum); 303621b5090SJohn Baldwin nvl = find_config_node(node_name); 304621b5090SJohn Baldwin if (nvl != NULL) { 305621b5090SJohn Baldwin EPRINTLN("pci slot %d:%d:%d already occupied!", bnum, snum, 306621b5090SJohn Baldwin fnum); 307b05c77ffSNeel Natu goto done; 308b05c77ffSNeel Natu } 309621b5090SJohn Baldwin nvl = create_config_node(node_name); 310621b5090SJohn Baldwin if (pde->pe_alias != NULL) 311621b5090SJohn Baldwin set_config_value_node(nvl, "device", pde->pe_alias); 312621b5090SJohn Baldwin else 313621b5090SJohn Baldwin set_config_value_node(nvl, "device", pde->pe_emu); 314b05c77ffSNeel Natu 315621b5090SJohn Baldwin if (pde->pe_legacy_config != NULL) 316621b5090SJohn Baldwin error = pde->pe_legacy_config(nvl, config); 317621b5090SJohn Baldwin else 318621b5090SJohn Baldwin error = pci_parse_legacy_config(nvl, config); 319b05c77ffSNeel Natu done: 320d84882caSNeel Natu free(str); 321b05c77ffSNeel Natu return (error); 322366f6083SPeter Grehan } 323366f6083SPeter Grehan 324657d2158SMarcelo Araujo void 32575ce327aSMark Johnston pci_print_supported_devices(void) 326657d2158SMarcelo Araujo { 327657d2158SMarcelo Araujo struct pci_devemu **pdpp, *pdp; 328657d2158SMarcelo Araujo 329657d2158SMarcelo Araujo SET_FOREACH(pdpp, pci_devemu_set) { 330657d2158SMarcelo Araujo pdp = *pdpp; 331657d2158SMarcelo Araujo printf("%s\n", pdp->pe_emu); 332657d2158SMarcelo Araujo } 333657d2158SMarcelo Araujo } 334657d2158SMarcelo Araujo 335366f6083SPeter Grehan static int 336c9b4e987SNeel Natu pci_valid_pba_offset(struct pci_devinst *pi, uint64_t offset) 337c9b4e987SNeel Natu { 338c9b4e987SNeel Natu 339c9b4e987SNeel Natu if (offset < pi->pi_msix.pba_offset) 340c9b4e987SNeel Natu return (0); 341c9b4e987SNeel Natu 342c9b4e987SNeel Natu if (offset >= pi->pi_msix.pba_offset + pi->pi_msix.pba_size) { 343c9b4e987SNeel Natu return (0); 344c9b4e987SNeel Natu } 345c9b4e987SNeel Natu 346c9b4e987SNeel Natu return (1); 347c9b4e987SNeel Natu } 348c9b4e987SNeel Natu 349c9b4e987SNeel Natu int 350c9b4e987SNeel Natu pci_emul_msix_twrite(struct pci_devinst *pi, uint64_t offset, int size, 351c9b4e987SNeel Natu uint64_t value) 352c9b4e987SNeel Natu { 353c9b4e987SNeel Natu int msix_entry_offset; 354c9b4e987SNeel Natu int tab_index; 355c9b4e987SNeel Natu char *dest; 356c9b4e987SNeel Natu 357c9b4e987SNeel Natu /* support only 4 or 8 byte writes */ 358c9b4e987SNeel Natu if (size != 4 && size != 8) 359c9b4e987SNeel Natu return (-1); 360c9b4e987SNeel Natu 361c9b4e987SNeel Natu /* 362c9b4e987SNeel Natu * Return if table index is beyond what device supports 363c9b4e987SNeel Natu */ 364c9b4e987SNeel Natu tab_index = offset / MSIX_TABLE_ENTRY_SIZE; 365c9b4e987SNeel Natu if (tab_index >= pi->pi_msix.table_count) 366c9b4e987SNeel Natu return (-1); 367c9b4e987SNeel Natu 368c9b4e987SNeel Natu msix_entry_offset = offset % MSIX_TABLE_ENTRY_SIZE; 369c9b4e987SNeel Natu 370c9b4e987SNeel Natu /* support only aligned writes */ 371c9b4e987SNeel Natu if ((msix_entry_offset % size) != 0) 372c9b4e987SNeel Natu return (-1); 373c9b4e987SNeel Natu 374c9b4e987SNeel Natu dest = (char *)(pi->pi_msix.table + tab_index); 375c9b4e987SNeel Natu dest += msix_entry_offset; 376c9b4e987SNeel Natu 377c9b4e987SNeel Natu if (size == 4) 378c9b4e987SNeel Natu *((uint32_t *)dest) = value; 379c9b4e987SNeel Natu else 380c9b4e987SNeel Natu *((uint64_t *)dest) = value; 381c9b4e987SNeel Natu 382c9b4e987SNeel Natu return (0); 383c9b4e987SNeel Natu } 384c9b4e987SNeel Natu 385c9b4e987SNeel Natu uint64_t 386c9b4e987SNeel Natu pci_emul_msix_tread(struct pci_devinst *pi, uint64_t offset, int size) 387c9b4e987SNeel Natu { 388c9b4e987SNeel Natu char *dest; 389c9b4e987SNeel Natu int msix_entry_offset; 390c9b4e987SNeel Natu int tab_index; 391c9b4e987SNeel Natu uint64_t retval = ~0; 392c9b4e987SNeel Natu 3936a52209fSNeel Natu /* 3946a52209fSNeel Natu * The PCI standard only allows 4 and 8 byte accesses to the MSI-X 395463a577bSEitan Adler * table but we also allow 1 byte access to accommodate reads from 3966a52209fSNeel Natu * ddb. 3976a52209fSNeel Natu */ 3986a52209fSNeel Natu if (size != 1 && size != 4 && size != 8) 399c9b4e987SNeel Natu return (retval); 400c9b4e987SNeel Natu 401c9b4e987SNeel Natu msix_entry_offset = offset % MSIX_TABLE_ENTRY_SIZE; 402c9b4e987SNeel Natu 403c9b4e987SNeel Natu /* support only aligned reads */ 404c9b4e987SNeel Natu if ((msix_entry_offset % size) != 0) { 405c9b4e987SNeel Natu return (retval); 406c9b4e987SNeel Natu } 407c9b4e987SNeel Natu 408c9b4e987SNeel Natu tab_index = offset / MSIX_TABLE_ENTRY_SIZE; 409c9b4e987SNeel Natu 410c9b4e987SNeel Natu if (tab_index < pi->pi_msix.table_count) { 411c9b4e987SNeel Natu /* valid MSI-X Table access */ 412c9b4e987SNeel Natu dest = (char *)(pi->pi_msix.table + tab_index); 413c9b4e987SNeel Natu dest += msix_entry_offset; 414c9b4e987SNeel Natu 4156a52209fSNeel Natu if (size == 1) 4166a52209fSNeel Natu retval = *((uint8_t *)dest); 4176a52209fSNeel Natu else if (size == 4) 418c9b4e987SNeel Natu retval = *((uint32_t *)dest); 419c9b4e987SNeel Natu else 420c9b4e987SNeel Natu retval = *((uint64_t *)dest); 421c9b4e987SNeel Natu } else if (pci_valid_pba_offset(pi, offset)) { 422c9b4e987SNeel Natu /* return 0 for PBA access */ 423c9b4e987SNeel Natu retval = 0; 424c9b4e987SNeel Natu } 425c9b4e987SNeel Natu 426c9b4e987SNeel Natu return (retval); 427c9b4e987SNeel Natu } 428c9b4e987SNeel Natu 429aa12663fSNeel Natu int 430aa12663fSNeel Natu pci_msix_table_bar(struct pci_devinst *pi) 431aa12663fSNeel Natu { 432aa12663fSNeel Natu 433aa12663fSNeel Natu if (pi->pi_msix.table != NULL) 434aa12663fSNeel Natu return (pi->pi_msix.table_bar); 435aa12663fSNeel Natu else 436aa12663fSNeel Natu return (-1); 437aa12663fSNeel Natu } 438aa12663fSNeel Natu 439aa12663fSNeel Natu int 440aa12663fSNeel Natu pci_msix_pba_bar(struct pci_devinst *pi) 441aa12663fSNeel Natu { 442aa12663fSNeel Natu 443aa12663fSNeel Natu if (pi->pi_msix.table != NULL) 444aa12663fSNeel Natu return (pi->pi_msix.pba_bar); 445aa12663fSNeel Natu else 446aa12663fSNeel Natu return (-1); 447aa12663fSNeel Natu } 448aa12663fSNeel Natu 449c9b4e987SNeel Natu static int 4506a284cacSJohn Baldwin pci_emul_io_handler(struct vmctx *ctx __unused, int in, int port, 45178c2cd83SJohn Baldwin int bytes, uint32_t *eax, void *arg) 452366f6083SPeter Grehan { 453366f6083SPeter Grehan struct pci_devinst *pdi = arg; 454366f6083SPeter Grehan struct pci_devemu *pe = pdi->pi_d; 4554d1e669cSPeter Grehan uint64_t offset; 4564d1e669cSPeter Grehan int i; 457366f6083SPeter Grehan 458ed721684SMark Johnston assert(port >= 0); 459ed721684SMark Johnston 460366f6083SPeter Grehan for (i = 0; i <= PCI_BARMAX; i++) { 461366f6083SPeter Grehan if (pdi->pi_bar[i].type == PCIBAR_IO && 462ed721684SMark Johnston (uint64_t)port >= pdi->pi_bar[i].addr && 463ed721684SMark Johnston (uint64_t)port + bytes <= 464ed721684SMark Johnston pdi->pi_bar[i].addr + pdi->pi_bar[i].size) { 465366f6083SPeter Grehan offset = port - pdi->pi_bar[i].addr; 466366f6083SPeter Grehan if (in) 4676a284cacSJohn Baldwin *eax = (*pe->pe_barread)(pdi, i, 4684d1e669cSPeter Grehan offset, bytes); 469366f6083SPeter Grehan else 4706a284cacSJohn Baldwin (*pe->pe_barwrite)(pdi, i, offset, 4714d1e669cSPeter Grehan bytes, *eax); 472366f6083SPeter Grehan return (0); 473366f6083SPeter Grehan } 474366f6083SPeter Grehan } 475366f6083SPeter Grehan return (-1); 476366f6083SPeter Grehan } 477366f6083SPeter Grehan 478366f6083SPeter Grehan static int 4796a284cacSJohn Baldwin pci_emul_mem_handler(struct vmctx *ctx __unused, int vcpu __unused, int dir, 48078c2cd83SJohn Baldwin uint64_t addr, int size, uint64_t *val, void *arg1, long arg2) 4814d1e669cSPeter Grehan { 4824d1e669cSPeter Grehan struct pci_devinst *pdi = arg1; 4834d1e669cSPeter Grehan struct pci_devemu *pe = pdi->pi_d; 4844d1e669cSPeter Grehan uint64_t offset; 4854d1e669cSPeter Grehan int bidx = (int) arg2; 4864d1e669cSPeter Grehan 4874d1e669cSPeter Grehan assert(bidx <= PCI_BARMAX); 4884d1e669cSPeter Grehan assert(pdi->pi_bar[bidx].type == PCIBAR_MEM32 || 4894d1e669cSPeter Grehan pdi->pi_bar[bidx].type == PCIBAR_MEM64); 4904d1e669cSPeter Grehan assert(addr >= pdi->pi_bar[bidx].addr && 4914d1e669cSPeter Grehan addr + size <= pdi->pi_bar[bidx].addr + pdi->pi_bar[bidx].size); 4924d1e669cSPeter Grehan 4934d1e669cSPeter Grehan offset = addr - pdi->pi_bar[bidx].addr; 4944d1e669cSPeter Grehan 495b6ae8b05STycho Nightingale if (dir == MEM_F_WRITE) { 49667b6ffaaSTycho Nightingale if (size == 8) { 4976a284cacSJohn Baldwin (*pe->pe_barwrite)(pdi, bidx, offset, 498b6ae8b05STycho Nightingale 4, *val & 0xffffffff); 4996a284cacSJohn Baldwin (*pe->pe_barwrite)(pdi, bidx, offset + 4, 500b6ae8b05STycho Nightingale 4, *val >> 32); 501b6ae8b05STycho Nightingale } else { 5026a284cacSJohn Baldwin (*pe->pe_barwrite)(pdi, bidx, offset, 503b6ae8b05STycho Nightingale size, *val); 504b6ae8b05STycho Nightingale } 505b6ae8b05STycho Nightingale } else { 50667b6ffaaSTycho Nightingale if (size == 8) { 5076a284cacSJohn Baldwin *val = (*pe->pe_barread)(pdi, bidx, 508b6ae8b05STycho Nightingale offset, 4); 5096a284cacSJohn Baldwin *val |= (*pe->pe_barread)(pdi, bidx, 510b6ae8b05STycho Nightingale offset + 4, 4) << 32; 511b6ae8b05STycho Nightingale } else { 5126a284cacSJohn Baldwin *val = (*pe->pe_barread)(pdi, bidx, 513b6ae8b05STycho Nightingale offset, size); 514b6ae8b05STycho Nightingale } 515b6ae8b05STycho Nightingale } 5164d1e669cSPeter Grehan 5174d1e669cSPeter Grehan return (0); 5184d1e669cSPeter Grehan } 5194d1e669cSPeter Grehan 5204d1e669cSPeter Grehan 5214d1e669cSPeter Grehan static int 522366f6083SPeter Grehan pci_emul_alloc_resource(uint64_t *baseptr, uint64_t limit, uint64_t size, 523366f6083SPeter Grehan uint64_t *addr) 524366f6083SPeter Grehan { 525366f6083SPeter Grehan uint64_t base; 526366f6083SPeter Grehan 527366f6083SPeter Grehan assert((size & (size - 1)) == 0); /* must be a power of 2 */ 528366f6083SPeter Grehan 529366f6083SPeter Grehan base = roundup2(*baseptr, size); 530366f6083SPeter Grehan 531366f6083SPeter Grehan if (base + size <= limit) { 532366f6083SPeter Grehan *addr = base; 533366f6083SPeter Grehan *baseptr = base + size; 534366f6083SPeter Grehan return (0); 535366f6083SPeter Grehan } else 536366f6083SPeter Grehan return (-1); 537366f6083SPeter Grehan } 538366f6083SPeter Grehan 539028d9311SNeel Natu /* 540028d9311SNeel Natu * Register (or unregister) the MMIO or I/O region associated with the BAR 541028d9311SNeel Natu * register 'idx' of an emulated pci device. 542028d9311SNeel Natu */ 543028d9311SNeel Natu static void 544028d9311SNeel Natu modify_bar_registration(struct pci_devinst *pi, int idx, int registration) 545028d9311SNeel Natu { 546f8a6ec2dSD Scott Phillips struct pci_devemu *pe; 547028d9311SNeel Natu int error; 548028d9311SNeel Natu struct inout_port iop; 549028d9311SNeel Natu struct mem_range mr; 550028d9311SNeel Natu 551f8a6ec2dSD Scott Phillips pe = pi->pi_d; 552028d9311SNeel Natu switch (pi->pi_bar[idx].type) { 553028d9311SNeel Natu case PCIBAR_IO: 554028d9311SNeel Natu bzero(&iop, sizeof(struct inout_port)); 555028d9311SNeel Natu iop.name = pi->pi_name; 556028d9311SNeel Natu iop.port = pi->pi_bar[idx].addr; 557028d9311SNeel Natu iop.size = pi->pi_bar[idx].size; 558028d9311SNeel Natu if (registration) { 559028d9311SNeel Natu iop.flags = IOPORT_F_INOUT; 560028d9311SNeel Natu iop.handler = pci_emul_io_handler; 561028d9311SNeel Natu iop.arg = pi; 562028d9311SNeel Natu error = register_inout(&iop); 563028d9311SNeel Natu } else 564028d9311SNeel Natu error = unregister_inout(&iop); 565f8a6ec2dSD Scott Phillips if (pe->pe_baraddr != NULL) 5666a284cacSJohn Baldwin (*pe->pe_baraddr)(pi, idx, registration, 567f8a6ec2dSD Scott Phillips pi->pi_bar[idx].addr); 568028d9311SNeel Natu break; 569028d9311SNeel Natu case PCIBAR_MEM32: 570028d9311SNeel Natu case PCIBAR_MEM64: 571028d9311SNeel Natu bzero(&mr, sizeof(struct mem_range)); 572028d9311SNeel Natu mr.name = pi->pi_name; 573028d9311SNeel Natu mr.base = pi->pi_bar[idx].addr; 574028d9311SNeel Natu mr.size = pi->pi_bar[idx].size; 575028d9311SNeel Natu if (registration) { 576028d9311SNeel Natu mr.flags = MEM_F_RW; 577028d9311SNeel Natu mr.handler = pci_emul_mem_handler; 578028d9311SNeel Natu mr.arg1 = pi; 579028d9311SNeel Natu mr.arg2 = idx; 580028d9311SNeel Natu error = register_mem(&mr); 581028d9311SNeel Natu } else 582028d9311SNeel Natu error = unregister_mem(&mr); 583f8a6ec2dSD Scott Phillips if (pe->pe_baraddr != NULL) 5846a284cacSJohn Baldwin (*pe->pe_baraddr)(pi, idx, registration, 585f8a6ec2dSD Scott Phillips pi->pi_bar[idx].addr); 586028d9311SNeel Natu break; 587e47fe318SCorvin Köhne case PCIBAR_ROM: 588e47fe318SCorvin Köhne error = 0; 589e47fe318SCorvin Köhne if (pe->pe_baraddr != NULL) 5906a284cacSJohn Baldwin (*pe->pe_baraddr)(pi, idx, registration, 591e47fe318SCorvin Köhne pi->pi_bar[idx].addr); 592e47fe318SCorvin Köhne break; 593028d9311SNeel Natu default: 594028d9311SNeel Natu error = EINVAL; 595028d9311SNeel Natu break; 596028d9311SNeel Natu } 597028d9311SNeel Natu assert(error == 0); 598028d9311SNeel Natu } 599028d9311SNeel Natu 600028d9311SNeel Natu static void 601028d9311SNeel Natu unregister_bar(struct pci_devinst *pi, int idx) 602028d9311SNeel Natu { 603028d9311SNeel Natu 604028d9311SNeel Natu modify_bar_registration(pi, idx, 0); 605028d9311SNeel Natu } 606028d9311SNeel Natu 607028d9311SNeel Natu static void 608028d9311SNeel Natu register_bar(struct pci_devinst *pi, int idx) 609028d9311SNeel Natu { 610028d9311SNeel Natu 611028d9311SNeel Natu modify_bar_registration(pi, idx, 1); 612028d9311SNeel Natu } 613028d9311SNeel Natu 614e47fe318SCorvin Köhne /* Is the ROM enabled for the emulated pci device? */ 615e47fe318SCorvin Köhne static int 616e47fe318SCorvin Köhne romen(struct pci_devinst *pi) 617e47fe318SCorvin Köhne { 618e47fe318SCorvin Köhne return (pi->pi_bar[PCI_ROM_IDX].lobits & PCIM_BIOS_ENABLE) == 619e47fe318SCorvin Köhne PCIM_BIOS_ENABLE; 620e47fe318SCorvin Köhne } 621e47fe318SCorvin Köhne 622028d9311SNeel Natu /* Are we decoding i/o port accesses for the emulated pci device? */ 623028d9311SNeel Natu static int 624028d9311SNeel Natu porten(struct pci_devinst *pi) 625028d9311SNeel Natu { 626028d9311SNeel Natu uint16_t cmd; 627028d9311SNeel Natu 628028d9311SNeel Natu cmd = pci_get_cfgdata16(pi, PCIR_COMMAND); 629028d9311SNeel Natu 630028d9311SNeel Natu return (cmd & PCIM_CMD_PORTEN); 631028d9311SNeel Natu } 632028d9311SNeel Natu 633028d9311SNeel Natu /* Are we decoding memory accesses for the emulated pci device? */ 634028d9311SNeel Natu static int 635028d9311SNeel Natu memen(struct pci_devinst *pi) 636028d9311SNeel Natu { 637028d9311SNeel Natu uint16_t cmd; 638028d9311SNeel Natu 639028d9311SNeel Natu cmd = pci_get_cfgdata16(pi, PCIR_COMMAND); 640028d9311SNeel Natu 641028d9311SNeel Natu return (cmd & PCIM_CMD_MEMEN); 642028d9311SNeel Natu } 643028d9311SNeel Natu 644028d9311SNeel Natu /* 645028d9311SNeel Natu * Update the MMIO or I/O address that is decoded by the BAR register. 646028d9311SNeel Natu * 647028d9311SNeel Natu * If the pci device has enabled the address space decoding then intercept 648028d9311SNeel Natu * the address range decoded by the BAR register. 649028d9311SNeel Natu */ 650028d9311SNeel Natu static void 651028d9311SNeel Natu update_bar_address(struct pci_devinst *pi, uint64_t addr, int idx, int type) 652028d9311SNeel Natu { 653028d9311SNeel Natu int decode; 654028d9311SNeel Natu 655028d9311SNeel Natu if (pi->pi_bar[idx].type == PCIBAR_IO) 656028d9311SNeel Natu decode = porten(pi); 657028d9311SNeel Natu else 658028d9311SNeel Natu decode = memen(pi); 659028d9311SNeel Natu 660028d9311SNeel Natu if (decode) 661028d9311SNeel Natu unregister_bar(pi, idx); 662028d9311SNeel Natu 663028d9311SNeel Natu switch (type) { 664028d9311SNeel Natu case PCIBAR_IO: 665028d9311SNeel Natu case PCIBAR_MEM32: 666028d9311SNeel Natu pi->pi_bar[idx].addr = addr; 667028d9311SNeel Natu break; 668028d9311SNeel Natu case PCIBAR_MEM64: 669028d9311SNeel Natu pi->pi_bar[idx].addr &= ~0xffffffffUL; 670028d9311SNeel Natu pi->pi_bar[idx].addr |= addr; 671028d9311SNeel Natu break; 672028d9311SNeel Natu case PCIBAR_MEMHI64: 673028d9311SNeel Natu pi->pi_bar[idx].addr &= 0xffffffff; 674028d9311SNeel Natu pi->pi_bar[idx].addr |= addr; 675028d9311SNeel Natu break; 676028d9311SNeel Natu default: 677028d9311SNeel Natu assert(0); 678028d9311SNeel Natu } 679028d9311SNeel Natu 680028d9311SNeel Natu if (decode) 681028d9311SNeel Natu register_bar(pi, idx); 682028d9311SNeel Natu } 683028d9311SNeel Natu 6844d1e669cSPeter Grehan int 685038f5c7bSKonstantin Belousov pci_emul_alloc_bar(struct pci_devinst *pdi, int idx, enum pcibar_type type, 686038f5c7bSKonstantin Belousov uint64_t size) 687366f6083SPeter Grehan { 688e47fe318SCorvin Köhne assert((type == PCIBAR_ROM) || (idx >= 0 && idx <= PCI_BARMAX)); 689e47fe318SCorvin Köhne assert((type != PCIBAR_ROM) || (idx == PCI_ROM_IDX)); 690366f6083SPeter Grehan 691366f6083SPeter Grehan if ((size & (size - 1)) != 0) 692366f6083SPeter Grehan size = 1UL << flsl(size); /* round up to a power of 2 */ 693366f6083SPeter Grehan 694028d9311SNeel Natu /* Enforce minimum BAR sizes required by the PCI standard */ 695028d9311SNeel Natu if (type == PCIBAR_IO) { 696028d9311SNeel Natu if (size < 4) 697028d9311SNeel Natu size = 4; 698e47fe318SCorvin Köhne } else if (type == PCIBAR_ROM) { 699e47fe318SCorvin Köhne if (size < ~PCIM_BIOS_ADDR_MASK + 1) 700e47fe318SCorvin Köhne size = ~PCIM_BIOS_ADDR_MASK + 1; 701028d9311SNeel Natu } else { 702028d9311SNeel Natu if (size < 16) 703028d9311SNeel Natu size = 16; 704028d9311SNeel Natu } 705028d9311SNeel Natu 70601f9362eSCorvin Köhne /* 70701f9362eSCorvin Köhne * To reduce fragmentation of the MMIO space, we allocate the BARs by 70801f9362eSCorvin Köhne * size. Therefore, don't allocate the BAR yet. We create a list of all 70901f9362eSCorvin Köhne * BAR allocation which is sorted by BAR size. When all PCI devices are 71001f9362eSCorvin Köhne * initialized, we will assign an address to the BARs. 71101f9362eSCorvin Köhne */ 71201f9362eSCorvin Köhne 71301f9362eSCorvin Köhne /* create a new list entry */ 71401f9362eSCorvin Köhne struct pci_bar_allocation *const new_bar = malloc(sizeof(*new_bar)); 71501f9362eSCorvin Köhne memset(new_bar, 0, sizeof(*new_bar)); 71601f9362eSCorvin Köhne new_bar->pdi = pdi; 71701f9362eSCorvin Köhne new_bar->idx = idx; 71801f9362eSCorvin Köhne new_bar->type = type; 71901f9362eSCorvin Köhne new_bar->size = size; 72001f9362eSCorvin Köhne 72101f9362eSCorvin Köhne /* 72201f9362eSCorvin Köhne * Search for a BAR which size is lower than the size of our newly 72301f9362eSCorvin Köhne * allocated BAR. 72401f9362eSCorvin Köhne */ 72501f9362eSCorvin Köhne struct pci_bar_allocation *bar = NULL; 72601f9362eSCorvin Köhne TAILQ_FOREACH(bar, &pci_bars, chain) { 72701f9362eSCorvin Köhne if (bar->size < size) { 72801f9362eSCorvin Köhne break; 72901f9362eSCorvin Köhne } 73001f9362eSCorvin Köhne } 73101f9362eSCorvin Köhne 73201f9362eSCorvin Köhne if (bar == NULL) { 73301f9362eSCorvin Köhne /* 73401f9362eSCorvin Köhne * Either the list is empty or new BAR is the smallest BAR of 73501f9362eSCorvin Köhne * the list. Append it to the end of our list. 73601f9362eSCorvin Köhne */ 73701f9362eSCorvin Köhne TAILQ_INSERT_TAIL(&pci_bars, new_bar, chain); 73801f9362eSCorvin Köhne } else { 73901f9362eSCorvin Köhne /* 74001f9362eSCorvin Köhne * The found BAR is smaller than our new BAR. For that reason, 74101f9362eSCorvin Köhne * insert our new BAR before the found BAR. 74201f9362eSCorvin Köhne */ 74301f9362eSCorvin Köhne TAILQ_INSERT_BEFORE(bar, new_bar, chain); 74401f9362eSCorvin Köhne } 74501f9362eSCorvin Köhne 74601f9362eSCorvin Köhne /* 74701f9362eSCorvin Köhne * pci_passthru devices synchronize their physical and virtual command 74801f9362eSCorvin Köhne * register on init. For that reason, the virtual cmd reg should be 74901f9362eSCorvin Köhne * updated as early as possible. 75001f9362eSCorvin Köhne */ 75101f9362eSCorvin Köhne uint16_t enbit = 0; 75201f9362eSCorvin Köhne switch (type) { 75301f9362eSCorvin Köhne case PCIBAR_IO: 75401f9362eSCorvin Köhne enbit = PCIM_CMD_PORTEN; 75501f9362eSCorvin Köhne break; 75601f9362eSCorvin Köhne case PCIBAR_MEM64: 75701f9362eSCorvin Köhne case PCIBAR_MEM32: 75801f9362eSCorvin Köhne enbit = PCIM_CMD_MEMEN; 75901f9362eSCorvin Köhne break; 76001f9362eSCorvin Köhne default: 76101f9362eSCorvin Köhne enbit = 0; 76201f9362eSCorvin Köhne break; 76301f9362eSCorvin Köhne } 76401f9362eSCorvin Köhne 76501f9362eSCorvin Köhne const uint16_t cmd = pci_get_cfgdata16(pdi, PCIR_COMMAND); 76601f9362eSCorvin Köhne pci_set_cfgdata16(pdi, PCIR_COMMAND, cmd | enbit); 76701f9362eSCorvin Köhne 76801f9362eSCorvin Köhne return (0); 76901f9362eSCorvin Köhne } 77001f9362eSCorvin Köhne 77101f9362eSCorvin Köhne static int 77201f9362eSCorvin Köhne pci_emul_assign_bar(struct pci_devinst *const pdi, const int idx, 77301f9362eSCorvin Köhne const enum pcibar_type type, const uint64_t size) 77401f9362eSCorvin Köhne { 77501f9362eSCorvin Köhne int error; 77601f9362eSCorvin Köhne uint64_t *baseptr, limit, addr, mask, lobits, bar; 77701f9362eSCorvin Köhne 778366f6083SPeter Grehan switch (type) { 779366f6083SPeter Grehan case PCIBAR_NONE: 780366f6083SPeter Grehan baseptr = NULL; 78101f9362eSCorvin Köhne addr = mask = lobits = 0; 782366f6083SPeter Grehan break; 783366f6083SPeter Grehan case PCIBAR_IO: 784366f6083SPeter Grehan baseptr = &pci_emul_iobase; 785366f6083SPeter Grehan limit = PCI_EMUL_IOLIMIT; 786366f6083SPeter Grehan mask = PCIM_BAR_IO_BASE; 787366f6083SPeter Grehan lobits = PCIM_BAR_IO_SPACE; 788366f6083SPeter Grehan break; 789366f6083SPeter Grehan case PCIBAR_MEM64: 790366f6083SPeter Grehan /* 791366f6083SPeter Grehan * XXX 792366f6083SPeter Grehan * Some drivers do not work well if the 64-bit BAR is allocated 793366f6083SPeter Grehan * above 4GB. Allow for this by allocating small requests under 794366f6083SPeter Grehan * 4GB unless then allocation size is larger than some arbitrary 795670b364bSKonstantin Belousov * number (128MB currently). 796366f6083SPeter Grehan */ 797670b364bSKonstantin Belousov if (size > 128 * 1024 * 1024) { 798366f6083SPeter Grehan baseptr = &pci_emul_membase64; 7999922872bSKonstantin Belousov limit = pci_emul_memlim64; 800366f6083SPeter Grehan mask = PCIM_BAR_MEM_BASE; 801366f6083SPeter Grehan lobits = PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_64 | 802366f6083SPeter Grehan PCIM_BAR_MEM_PREFETCH; 80325d4944eSNeel Natu } else { 80425d4944eSNeel Natu baseptr = &pci_emul_membase32; 80525d4944eSNeel Natu limit = PCI_EMUL_MEMLIMIT32; 80625d4944eSNeel Natu mask = PCIM_BAR_MEM_BASE; 80725d4944eSNeel Natu lobits = PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_64; 808366f6083SPeter Grehan } 80925d4944eSNeel Natu break; 810366f6083SPeter Grehan case PCIBAR_MEM32: 811366f6083SPeter Grehan baseptr = &pci_emul_membase32; 812366f6083SPeter Grehan limit = PCI_EMUL_MEMLIMIT32; 813366f6083SPeter Grehan mask = PCIM_BAR_MEM_BASE; 814366f6083SPeter Grehan lobits = PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_32; 815366f6083SPeter Grehan break; 816e47fe318SCorvin Köhne case PCIBAR_ROM: 817e47fe318SCorvin Köhne /* do not claim memory for ROM. OVMF will do it for us. */ 818e47fe318SCorvin Köhne baseptr = NULL; 819e47fe318SCorvin Köhne limit = 0; 820e47fe318SCorvin Köhne mask = PCIM_BIOS_ADDR_MASK; 821e47fe318SCorvin Köhne lobits = 0; 822e47fe318SCorvin Köhne break; 823366f6083SPeter Grehan default: 824366f6083SPeter Grehan printf("pci_emul_alloc_base: invalid bar type %d\n", type); 825366f6083SPeter Grehan assert(0); 826366f6083SPeter Grehan } 827366f6083SPeter Grehan 828366f6083SPeter Grehan if (baseptr != NULL) { 829366f6083SPeter Grehan error = pci_emul_alloc_resource(baseptr, limit, size, &addr); 830366f6083SPeter Grehan if (error != 0) 831366f6083SPeter Grehan return (error); 8328ac8addaSCorvin Köhne } else { 8338ac8addaSCorvin Köhne addr = 0; 834366f6083SPeter Grehan } 835366f6083SPeter Grehan 836366f6083SPeter Grehan pdi->pi_bar[idx].type = type; 837366f6083SPeter Grehan pdi->pi_bar[idx].addr = addr; 838366f6083SPeter Grehan pdi->pi_bar[idx].size = size; 839e87a6f3eSCorvin Köhne /* 840e87a6f3eSCorvin Köhne * passthru devices are using same lobits as physical device they set 841e87a6f3eSCorvin Köhne * this property 842e87a6f3eSCorvin Köhne */ 843e87a6f3eSCorvin Köhne if (pdi->pi_bar[idx].lobits != 0) { 844e87a6f3eSCorvin Köhne lobits = pdi->pi_bar[idx].lobits; 845e87a6f3eSCorvin Köhne } else { 846e87a6f3eSCorvin Köhne pdi->pi_bar[idx].lobits = lobits; 847e87a6f3eSCorvin Köhne } 848366f6083SPeter Grehan 849366f6083SPeter Grehan /* Initialize the BAR register in config space */ 850366f6083SPeter Grehan bar = (addr & mask) | lobits; 851366f6083SPeter Grehan pci_set_cfgdata32(pdi, PCIR_BAR(idx), bar); 852366f6083SPeter Grehan 853366f6083SPeter Grehan if (type == PCIBAR_MEM64) { 854366f6083SPeter Grehan assert(idx + 1 <= PCI_BARMAX); 855366f6083SPeter Grehan pdi->pi_bar[idx + 1].type = PCIBAR_MEMHI64; 856366f6083SPeter Grehan pci_set_cfgdata32(pdi, PCIR_BAR(idx + 1), bar >> 32); 857366f6083SPeter Grehan } 858366f6083SPeter Grehan 859e47fe318SCorvin Köhne if (type != PCIBAR_ROM) { 860028d9311SNeel Natu register_bar(pdi, idx); 861e47fe318SCorvin Köhne } 862e47fe318SCorvin Köhne 863e47fe318SCorvin Köhne return (0); 864e47fe318SCorvin Köhne } 865e47fe318SCorvin Köhne 866e47fe318SCorvin Köhne int 867e47fe318SCorvin Köhne pci_emul_alloc_rom(struct pci_devinst *const pdi, const uint64_t size, 868e47fe318SCorvin Köhne void **const addr) 869e47fe318SCorvin Köhne { 870e47fe318SCorvin Köhne /* allocate ROM space once on first call */ 871e47fe318SCorvin Köhne if (pci_emul_rombase == 0) { 872e47fe318SCorvin Köhne pci_emul_rombase = vm_create_devmem(pdi->pi_vmctx, VM_PCIROM, 873e47fe318SCorvin Köhne "pcirom", PCI_EMUL_ROMSIZE); 874e47fe318SCorvin Köhne if (pci_emul_rombase == MAP_FAILED) { 875e47fe318SCorvin Köhne warnx("%s: failed to create rom segment", __func__); 876e47fe318SCorvin Köhne return (-1); 877e47fe318SCorvin Köhne } 878e47fe318SCorvin Köhne pci_emul_romlim = pci_emul_rombase + PCI_EMUL_ROMSIZE; 879e47fe318SCorvin Köhne pci_emul_romoffset = 0; 880e47fe318SCorvin Köhne } 881e47fe318SCorvin Köhne 882e47fe318SCorvin Köhne /* ROM size should be a power of 2 and greater than 2 KB */ 883e47fe318SCorvin Köhne const uint64_t rom_size = MAX(1UL << flsl(size), 884e47fe318SCorvin Köhne ~PCIM_BIOS_ADDR_MASK + 1); 885e47fe318SCorvin Köhne 886e47fe318SCorvin Köhne /* check if ROM fits into ROM space */ 887e47fe318SCorvin Köhne if (pci_emul_romoffset + rom_size > PCI_EMUL_ROMSIZE) { 888e47fe318SCorvin Köhne warnx("%s: no space left in rom segment:", __func__); 889e47fe318SCorvin Köhne warnx("%16lu bytes left", 890e47fe318SCorvin Köhne PCI_EMUL_ROMSIZE - pci_emul_romoffset); 891e47fe318SCorvin Köhne warnx("%16lu bytes required by %d/%d/%d", rom_size, pdi->pi_bus, 892e47fe318SCorvin Köhne pdi->pi_slot, pdi->pi_func); 893e47fe318SCorvin Köhne return (-1); 894e47fe318SCorvin Köhne } 895e47fe318SCorvin Köhne 896e47fe318SCorvin Köhne /* allocate ROM BAR */ 897e47fe318SCorvin Köhne const int error = pci_emul_alloc_bar(pdi, PCI_ROM_IDX, PCIBAR_ROM, 898e47fe318SCorvin Köhne rom_size); 899e47fe318SCorvin Köhne if (error) 900e47fe318SCorvin Köhne return error; 901e47fe318SCorvin Köhne 902e47fe318SCorvin Köhne /* return address */ 903e47fe318SCorvin Köhne *addr = pci_emul_rombase + pci_emul_romoffset; 904e47fe318SCorvin Köhne 905e47fe318SCorvin Köhne /* save offset into ROM Space */ 906e47fe318SCorvin Köhne pdi->pi_romoffset = pci_emul_romoffset; 907e47fe318SCorvin Köhne 908e47fe318SCorvin Köhne /* increase offset for next ROM */ 909e47fe318SCorvin Köhne pci_emul_romoffset += rom_size; 910366f6083SPeter Grehan 911366f6083SPeter Grehan return (0); 912366f6083SPeter Grehan } 913366f6083SPeter Grehan 914366f6083SPeter Grehan #define CAP_START_OFFSET 0x40 915366f6083SPeter Grehan static int 916366f6083SPeter Grehan pci_emul_add_capability(struct pci_devinst *pi, u_char *capdata, int caplen) 917366f6083SPeter Grehan { 918a96b8b80SJohn Baldwin int i, capoff, reallen; 919366f6083SPeter Grehan uint16_t sts; 920366f6083SPeter Grehan 921a96b8b80SJohn Baldwin assert(caplen > 0); 922366f6083SPeter Grehan 923366f6083SPeter Grehan reallen = roundup2(caplen, 4); /* dword aligned */ 924366f6083SPeter Grehan 925366f6083SPeter Grehan sts = pci_get_cfgdata16(pi, PCIR_STATUS); 926a96b8b80SJohn Baldwin if ((sts & PCIM_STATUS_CAPPRESENT) == 0) 927366f6083SPeter Grehan capoff = CAP_START_OFFSET; 928a96b8b80SJohn Baldwin else 929a96b8b80SJohn Baldwin capoff = pi->pi_capend + 1; 930366f6083SPeter Grehan 931366f6083SPeter Grehan /* Check if we have enough space */ 932a96b8b80SJohn Baldwin if (capoff + reallen > PCI_REGMAX + 1) 933366f6083SPeter Grehan return (-1); 934366f6083SPeter Grehan 935a96b8b80SJohn Baldwin /* Set the previous capability pointer */ 936a96b8b80SJohn Baldwin if ((sts & PCIM_STATUS_CAPPRESENT) == 0) { 937a96b8b80SJohn Baldwin pci_set_cfgdata8(pi, PCIR_CAP_PTR, capoff); 938a96b8b80SJohn Baldwin pci_set_cfgdata16(pi, PCIR_STATUS, sts|PCIM_STATUS_CAPPRESENT); 939a96b8b80SJohn Baldwin } else 940a96b8b80SJohn Baldwin pci_set_cfgdata8(pi, pi->pi_prevcap + 1, capoff); 941a96b8b80SJohn Baldwin 942366f6083SPeter Grehan /* Copy the capability */ 943366f6083SPeter Grehan for (i = 0; i < caplen; i++) 944366f6083SPeter Grehan pci_set_cfgdata8(pi, capoff + i, capdata[i]); 945366f6083SPeter Grehan 946366f6083SPeter Grehan /* Set the next capability pointer */ 947a96b8b80SJohn Baldwin pci_set_cfgdata8(pi, capoff + 1, 0); 948366f6083SPeter Grehan 949a96b8b80SJohn Baldwin pi->pi_prevcap = capoff; 950a96b8b80SJohn Baldwin pi->pi_capend = capoff + reallen - 1; 951366f6083SPeter Grehan return (0); 952366f6083SPeter Grehan } 953366f6083SPeter Grehan 954366f6083SPeter Grehan static struct pci_devemu * 955621b5090SJohn Baldwin pci_emul_finddev(const char *name) 956366f6083SPeter Grehan { 957366f6083SPeter Grehan struct pci_devemu **pdpp, *pdp; 958366f6083SPeter Grehan 959366f6083SPeter Grehan SET_FOREACH(pdpp, pci_devemu_set) { 960366f6083SPeter Grehan pdp = *pdpp; 961366f6083SPeter Grehan if (!strcmp(pdp->pe_emu, name)) { 962366f6083SPeter Grehan return (pdp); 963366f6083SPeter Grehan } 964366f6083SPeter Grehan } 965366f6083SPeter Grehan 966366f6083SPeter Grehan return (NULL); 967366f6083SPeter Grehan } 968366f6083SPeter Grehan 969a38e2a64SPeter Grehan static int 970d84882caSNeel Natu pci_emul_init(struct vmctx *ctx, struct pci_devemu *pde, int bus, int slot, 971d84882caSNeel Natu int func, struct funcinfo *fi) 972366f6083SPeter Grehan { 973366f6083SPeter Grehan struct pci_devinst *pdi; 974a38e2a64SPeter Grehan int err; 975a38e2a64SPeter Grehan 976994f858aSXin LI pdi = calloc(1, sizeof(struct pci_devinst)); 977366f6083SPeter Grehan 978366f6083SPeter Grehan pdi->pi_vmctx = ctx; 979d84882caSNeel Natu pdi->pi_bus = bus; 980366f6083SPeter Grehan pdi->pi_slot = slot; 98199d65389SNeel Natu pdi->pi_func = func; 9823cbf3585SJohn Baldwin pthread_mutex_init(&pdi->pi_lintr.lock, NULL); 9833cbf3585SJohn Baldwin pdi->pi_lintr.pin = 0; 9843cbf3585SJohn Baldwin pdi->pi_lintr.state = IDLE; 985b3e9732aSJohn Baldwin pdi->pi_lintr.pirq_pin = 0; 9863cbf3585SJohn Baldwin pdi->pi_lintr.ioapic_irq = 0; 987366f6083SPeter Grehan pdi->pi_d = pde; 988366f6083SPeter Grehan snprintf(pdi->pi_name, PI_NAMESZ, "%s-pci-%d", pde->pe_emu, slot); 989366f6083SPeter Grehan 990366f6083SPeter Grehan /* Disable legacy interrupts */ 991366f6083SPeter Grehan pci_set_cfgdata8(pdi, PCIR_INTLINE, 255); 992366f6083SPeter Grehan pci_set_cfgdata8(pdi, PCIR_INTPIN, 0); 993366f6083SPeter Grehan 9942729c9bbSJohn Baldwin pci_set_cfgdata8(pdi, PCIR_COMMAND, PCIM_CMD_BUSMASTEREN); 995366f6083SPeter Grehan 9966a284cacSJohn Baldwin err = (*pde->pe_init)(pdi, fi->fi_config); 997d84882caSNeel Natu if (err == 0) 998d84882caSNeel Natu fi->fi_devi = pdi; 999d84882caSNeel Natu else 1000366f6083SPeter Grehan free(pdi); 1001a38e2a64SPeter Grehan 1002a38e2a64SPeter Grehan return (err); 1003366f6083SPeter Grehan } 1004366f6083SPeter Grehan 1005366f6083SPeter Grehan void 1006366f6083SPeter Grehan pci_populate_msicap(struct msicap *msicap, int msgnum, int nextptr) 1007366f6083SPeter Grehan { 1008366f6083SPeter Grehan int mmc; 1009366f6083SPeter Grehan 1010366f6083SPeter Grehan /* Number of msi messages must be a power of 2 between 1 and 32 */ 1011366f6083SPeter Grehan assert((msgnum & (msgnum - 1)) == 0 && msgnum >= 1 && msgnum <= 32); 1012366f6083SPeter Grehan mmc = ffs(msgnum) - 1; 1013366f6083SPeter Grehan 1014366f6083SPeter Grehan bzero(msicap, sizeof(struct msicap)); 1015366f6083SPeter Grehan msicap->capid = PCIY_MSI; 1016366f6083SPeter Grehan msicap->nextptr = nextptr; 1017366f6083SPeter Grehan msicap->msgctrl = PCIM_MSICTRL_64BIT | (mmc << 1); 1018366f6083SPeter Grehan } 1019366f6083SPeter Grehan 1020366f6083SPeter Grehan int 1021366f6083SPeter Grehan pci_emul_add_msicap(struct pci_devinst *pi, int msgnum) 1022366f6083SPeter Grehan { 1023366f6083SPeter Grehan struct msicap msicap; 1024366f6083SPeter Grehan 1025366f6083SPeter Grehan pci_populate_msicap(&msicap, msgnum, 0); 1026366f6083SPeter Grehan 1027366f6083SPeter Grehan return (pci_emul_add_capability(pi, (u_char *)&msicap, sizeof(msicap))); 1028366f6083SPeter Grehan } 1029366f6083SPeter Grehan 1030c9b4e987SNeel Natu static void 1031c9b4e987SNeel Natu pci_populate_msixcap(struct msixcap *msixcap, int msgnum, int barnum, 1032a96b8b80SJohn Baldwin uint32_t msix_tab_size) 1033c9b4e987SNeel Natu { 1034c9b4e987SNeel Natu 1035c9b4e987SNeel Natu assert(msix_tab_size % 4096 == 0); 1036c9b4e987SNeel Natu 1037c9b4e987SNeel Natu bzero(msixcap, sizeof(struct msixcap)); 1038c9b4e987SNeel Natu msixcap->capid = PCIY_MSIX; 1039c9b4e987SNeel Natu 1040c9b4e987SNeel Natu /* 1041c9b4e987SNeel Natu * Message Control Register, all fields set to 1042c9b4e987SNeel Natu * zero except for the Table Size. 1043c9b4e987SNeel Natu * Note: Table size N is encoded as N-1 1044c9b4e987SNeel Natu */ 1045c9b4e987SNeel Natu msixcap->msgctrl = msgnum - 1; 1046c9b4e987SNeel Natu 1047c9b4e987SNeel Natu /* 1048c9b4e987SNeel Natu * MSI-X BAR setup: 1049c9b4e987SNeel Natu * - MSI-X table start at offset 0 1050c9b4e987SNeel Natu * - PBA table starts at a 4K aligned offset after the MSI-X table 1051c9b4e987SNeel Natu */ 1052c9b4e987SNeel Natu msixcap->table_info = barnum & PCIM_MSIX_BIR_MASK; 1053c9b4e987SNeel Natu msixcap->pba_info = msix_tab_size | (barnum & PCIM_MSIX_BIR_MASK); 1054c9b4e987SNeel Natu } 1055c9b4e987SNeel Natu 1056c9b4e987SNeel Natu static void 1057c9b4e987SNeel Natu pci_msix_table_init(struct pci_devinst *pi, int table_entries) 1058c9b4e987SNeel Natu { 1059c9b4e987SNeel Natu int i, table_size; 1060c9b4e987SNeel Natu 1061c9b4e987SNeel Natu assert(table_entries > 0); 1062c9b4e987SNeel Natu assert(table_entries <= MAX_MSIX_TABLE_ENTRIES); 1063c9b4e987SNeel Natu 1064c9b4e987SNeel Natu table_size = table_entries * MSIX_TABLE_ENTRY_SIZE; 1065994f858aSXin LI pi->pi_msix.table = calloc(1, table_size); 1066c9b4e987SNeel Natu 1067c9b4e987SNeel Natu /* set mask bit of vector control register */ 1068c9b4e987SNeel Natu for (i = 0; i < table_entries; i++) 1069c9b4e987SNeel Natu pi->pi_msix.table[i].vector_control |= PCIM_MSIX_VCTRL_MASK; 1070c9b4e987SNeel Natu } 1071c9b4e987SNeel Natu 1072c9b4e987SNeel Natu int 1073c9b4e987SNeel Natu pci_emul_add_msixcap(struct pci_devinst *pi, int msgnum, int barnum) 1074c9b4e987SNeel Natu { 1075c9b4e987SNeel Natu uint32_t tab_size; 1076c9b4e987SNeel Natu struct msixcap msixcap; 1077c9b4e987SNeel Natu 1078c9b4e987SNeel Natu assert(msgnum >= 1 && msgnum <= MAX_MSIX_TABLE_ENTRIES); 1079c9b4e987SNeel Natu assert(barnum >= 0 && barnum <= PCIR_MAX_BAR_0); 1080c9b4e987SNeel Natu 1081c9b4e987SNeel Natu tab_size = msgnum * MSIX_TABLE_ENTRY_SIZE; 1082c9b4e987SNeel Natu 1083c9b4e987SNeel Natu /* Align table size to nearest 4K */ 1084c9b4e987SNeel Natu tab_size = roundup2(tab_size, 4096); 1085c9b4e987SNeel Natu 1086c9b4e987SNeel Natu pi->pi_msix.table_bar = barnum; 1087c9b4e987SNeel Natu pi->pi_msix.pba_bar = barnum; 1088c9b4e987SNeel Natu pi->pi_msix.table_offset = 0; 1089c9b4e987SNeel Natu pi->pi_msix.table_count = msgnum; 1090c9b4e987SNeel Natu pi->pi_msix.pba_offset = tab_size; 10917a902ec0SNeel Natu pi->pi_msix.pba_size = PBA_SIZE(msgnum); 1092c9b4e987SNeel Natu 1093c9b4e987SNeel Natu pci_msix_table_init(pi, msgnum); 1094c9b4e987SNeel Natu 1095a96b8b80SJohn Baldwin pci_populate_msixcap(&msixcap, msgnum, barnum, tab_size); 1096c9b4e987SNeel Natu 1097c9b4e987SNeel Natu /* allocate memory for MSI-X Table and PBA */ 1098c9b4e987SNeel Natu pci_emul_alloc_bar(pi, barnum, PCIBAR_MEM32, 1099c9b4e987SNeel Natu tab_size + pi->pi_msix.pba_size); 1100c9b4e987SNeel Natu 1101c9b4e987SNeel Natu return (pci_emul_add_capability(pi, (u_char *)&msixcap, 1102c9b4e987SNeel Natu sizeof(msixcap))); 1103c9b4e987SNeel Natu } 1104c9b4e987SNeel Natu 110521368498SPeter Grehan static void 1106cd942e0fSPeter Grehan msixcap_cfgwrite(struct pci_devinst *pi, int capoff, int offset, 1107cd942e0fSPeter Grehan int bytes, uint32_t val) 1108cd942e0fSPeter Grehan { 1109cd942e0fSPeter Grehan uint16_t msgctrl, rwmask; 1110d74fdc6aSMarcelo Araujo int off; 1111cd942e0fSPeter Grehan 1112cd942e0fSPeter Grehan off = offset - capoff; 1113cd942e0fSPeter Grehan /* Message Control Register */ 1114cd942e0fSPeter Grehan if (off == 2 && bytes == 2) { 1115cd942e0fSPeter Grehan rwmask = PCIM_MSIXCTRL_MSIX_ENABLE | PCIM_MSIXCTRL_FUNCTION_MASK; 1116cd942e0fSPeter Grehan msgctrl = pci_get_cfgdata16(pi, offset); 1117cd942e0fSPeter Grehan msgctrl &= ~rwmask; 1118cd942e0fSPeter Grehan msgctrl |= val & rwmask; 1119cd942e0fSPeter Grehan val = msgctrl; 1120cd942e0fSPeter Grehan 1121cd942e0fSPeter Grehan pi->pi_msix.enabled = val & PCIM_MSIXCTRL_MSIX_ENABLE; 1122c9b4e987SNeel Natu pi->pi_msix.function_mask = val & PCIM_MSIXCTRL_FUNCTION_MASK; 11233cbf3585SJohn Baldwin pci_lintr_update(pi); 1124cd942e0fSPeter Grehan } 1125cd942e0fSPeter Grehan 1126cd942e0fSPeter Grehan CFGWRITE(pi, offset, val, bytes); 1127cd942e0fSPeter Grehan } 1128cd942e0fSPeter Grehan 112921368498SPeter Grehan static void 1130366f6083SPeter Grehan msicap_cfgwrite(struct pci_devinst *pi, int capoff, int offset, 1131366f6083SPeter Grehan int bytes, uint32_t val) 1132366f6083SPeter Grehan { 1133366f6083SPeter Grehan uint16_t msgctrl, rwmask, msgdata, mme; 1134366f6083SPeter Grehan uint32_t addrlo; 1135366f6083SPeter Grehan 1136366f6083SPeter Grehan /* 1137366f6083SPeter Grehan * If guest is writing to the message control register make sure 1138366f6083SPeter Grehan * we do not overwrite read-only fields. 1139366f6083SPeter Grehan */ 1140366f6083SPeter Grehan if ((offset - capoff) == 2 && bytes == 2) { 1141366f6083SPeter Grehan rwmask = PCIM_MSICTRL_MME_MASK | PCIM_MSICTRL_MSI_ENABLE; 1142366f6083SPeter Grehan msgctrl = pci_get_cfgdata16(pi, offset); 1143366f6083SPeter Grehan msgctrl &= ~rwmask; 1144366f6083SPeter Grehan msgctrl |= val & rwmask; 1145366f6083SPeter Grehan val = msgctrl; 11467840d1c4SJohn Baldwin } 11477840d1c4SJohn Baldwin CFGWRITE(pi, offset, val, bytes); 1148366f6083SPeter Grehan 11497840d1c4SJohn Baldwin msgctrl = pci_get_cfgdata16(pi, capoff + 2); 1150366f6083SPeter Grehan addrlo = pci_get_cfgdata32(pi, capoff + 4); 1151366f6083SPeter Grehan if (msgctrl & PCIM_MSICTRL_64BIT) 1152366f6083SPeter Grehan msgdata = pci_get_cfgdata16(pi, capoff + 12); 1153366f6083SPeter Grehan else 1154366f6083SPeter Grehan msgdata = pci_get_cfgdata16(pi, capoff + 8); 1155366f6083SPeter Grehan 1156366f6083SPeter Grehan mme = msgctrl & PCIM_MSICTRL_MME_MASK; 1157366f6083SPeter Grehan pi->pi_msi.enabled = msgctrl & PCIM_MSICTRL_MSI_ENABLE ? 1 : 0; 1158366f6083SPeter Grehan if (pi->pi_msi.enabled) { 11594f8be175SNeel Natu pi->pi_msi.addr = addrlo; 11604f8be175SNeel Natu pi->pi_msi.msg_data = msgdata; 11614f8be175SNeel Natu pi->pi_msi.maxmsgnum = 1 << (mme >> 4); 1162366f6083SPeter Grehan } else { 11634f8be175SNeel Natu pi->pi_msi.maxmsgnum = 0; 1164366f6083SPeter Grehan } 11653cbf3585SJohn Baldwin pci_lintr_update(pi); 1166366f6083SPeter Grehan } 1167366f6083SPeter Grehan 116898d920d9SMark Johnston static void 116998d920d9SMark Johnston pciecap_cfgwrite(struct pci_devinst *pi, int capoff __unused, int offset, 117074f80b23SNeel Natu int bytes, uint32_t val) 117174f80b23SNeel Natu { 117274f80b23SNeel Natu 117374f80b23SNeel Natu /* XXX don't write to the readonly parts */ 117474f80b23SNeel Natu CFGWRITE(pi, offset, val, bytes); 117574f80b23SNeel Natu } 117674f80b23SNeel Natu 117774f80b23SNeel Natu #define PCIECAP_VERSION 0x2 117874f80b23SNeel Natu int 117974f80b23SNeel Natu pci_emul_add_pciecap(struct pci_devinst *pi, int type) 118074f80b23SNeel Natu { 118174f80b23SNeel Natu int err; 118274f80b23SNeel Natu struct pciecap pciecap; 118374f80b23SNeel Natu 118474f80b23SNeel Natu bzero(&pciecap, sizeof(pciecap)); 118574f80b23SNeel Natu 1186129f93c5SChuck Tuffli /* 1187129f93c5SChuck Tuffli * Use the integrated endpoint type for endpoints on a root complex bus. 1188129f93c5SChuck Tuffli * 1189129f93c5SChuck Tuffli * NB: bhyve currently only supports a single PCI bus that is the root 1190129f93c5SChuck Tuffli * complex bus, so all endpoints are integrated. 1191129f93c5SChuck Tuffli */ 1192129f93c5SChuck Tuffli if ((type == PCIEM_TYPE_ENDPOINT) && (pi->pi_bus == 0)) 1193129f93c5SChuck Tuffli type = PCIEM_TYPE_ROOT_INT_EP; 1194129f93c5SChuck Tuffli 119574f80b23SNeel Natu pciecap.capid = PCIY_EXPRESS; 1196129f93c5SChuck Tuffli pciecap.pcie_capabilities = PCIECAP_VERSION | type; 1197129f93c5SChuck Tuffli if (type != PCIEM_TYPE_ROOT_INT_EP) { 119874f80b23SNeel Natu pciecap.link_capabilities = 0x411; /* gen1, x1 */ 119974f80b23SNeel Natu pciecap.link_status = 0x11; /* gen1, x1 */ 1200129f93c5SChuck Tuffli } 120174f80b23SNeel Natu 120274f80b23SNeel Natu err = pci_emul_add_capability(pi, (u_char *)&pciecap, sizeof(pciecap)); 120374f80b23SNeel Natu return (err); 120474f80b23SNeel Natu } 120574f80b23SNeel Natu 1206366f6083SPeter Grehan /* 1207366f6083SPeter Grehan * This function assumes that 'coff' is in the capabilities region of the 120821368498SPeter Grehan * config space. A capoff parameter of zero will force a search for the 120921368498SPeter Grehan * offset and type. 1210366f6083SPeter Grehan */ 121121368498SPeter Grehan void 121221368498SPeter Grehan pci_emul_capwrite(struct pci_devinst *pi, int offset, int bytes, uint32_t val, 121321368498SPeter Grehan uint8_t capoff, int capid) 1214366f6083SPeter Grehan { 121521368498SPeter Grehan uint8_t nextoff; 1216366f6083SPeter Grehan 1217366f6083SPeter Grehan /* Do not allow un-aligned writes */ 1218366f6083SPeter Grehan if ((offset & (bytes - 1)) != 0) 1219366f6083SPeter Grehan return; 1220366f6083SPeter Grehan 122121368498SPeter Grehan if (capoff == 0) { 1222366f6083SPeter Grehan /* Find the capability that we want to update */ 1223366f6083SPeter Grehan capoff = CAP_START_OFFSET; 1224366f6083SPeter Grehan while (1) { 1225366f6083SPeter Grehan nextoff = pci_get_cfgdata8(pi, capoff + 1); 1226a96b8b80SJohn Baldwin if (nextoff == 0) 1227a96b8b80SJohn Baldwin break; 1228366f6083SPeter Grehan if (offset >= capoff && offset < nextoff) 1229366f6083SPeter Grehan break; 1230366f6083SPeter Grehan 1231366f6083SPeter Grehan capoff = nextoff; 1232366f6083SPeter Grehan } 1233366f6083SPeter Grehan assert(offset >= capoff); 123421368498SPeter Grehan capid = pci_get_cfgdata8(pi, capoff); 123521368498SPeter Grehan } 1236366f6083SPeter Grehan 1237366f6083SPeter Grehan /* 12382a8d400aSPeter Grehan * Capability ID and Next Capability Pointer are readonly. 12392a8d400aSPeter Grehan * However, some o/s's do 4-byte writes that include these. 12402a8d400aSPeter Grehan * For this case, trim the write back to 2 bytes and adjust 12412a8d400aSPeter Grehan * the data. 1242366f6083SPeter Grehan */ 12432a8d400aSPeter Grehan if (offset == capoff || offset == capoff + 1) { 12442a8d400aSPeter Grehan if (offset == capoff && bytes == 4) { 12452a8d400aSPeter Grehan bytes = 2; 12462a8d400aSPeter Grehan offset += 2; 12472a8d400aSPeter Grehan val >>= 16; 12482a8d400aSPeter Grehan } else 1249366f6083SPeter Grehan return; 12502a8d400aSPeter Grehan } 1251366f6083SPeter Grehan 1252366f6083SPeter Grehan switch (capid) { 1253366f6083SPeter Grehan case PCIY_MSI: 1254366f6083SPeter Grehan msicap_cfgwrite(pi, capoff, offset, bytes, val); 1255366f6083SPeter Grehan break; 1256c9b4e987SNeel Natu case PCIY_MSIX: 1257c9b4e987SNeel Natu msixcap_cfgwrite(pi, capoff, offset, bytes, val); 1258c9b4e987SNeel Natu break; 125974f80b23SNeel Natu case PCIY_EXPRESS: 126074f80b23SNeel Natu pciecap_cfgwrite(pi, capoff, offset, bytes, val); 126174f80b23SNeel Natu break; 1262366f6083SPeter Grehan default: 1263366f6083SPeter Grehan break; 1264366f6083SPeter Grehan } 1265366f6083SPeter Grehan } 1266366f6083SPeter Grehan 1267366f6083SPeter Grehan static int 1268366f6083SPeter Grehan pci_emul_iscap(struct pci_devinst *pi, int offset) 1269366f6083SPeter Grehan { 1270366f6083SPeter Grehan uint16_t sts; 1271366f6083SPeter Grehan 1272366f6083SPeter Grehan sts = pci_get_cfgdata16(pi, PCIR_STATUS); 1273366f6083SPeter Grehan if ((sts & PCIM_STATUS_CAPPRESENT) != 0) { 1274a96b8b80SJohn Baldwin if (offset >= CAP_START_OFFSET && offset <= pi->pi_capend) 1275a96b8b80SJohn Baldwin return (1); 1276366f6083SPeter Grehan } 1277a96b8b80SJohn Baldwin return (0); 1278366f6083SPeter Grehan } 1279366f6083SPeter Grehan 12800ab13648SPeter Grehan static int 128198d920d9SMark Johnston pci_emul_fallback_handler(struct vmctx *ctx __unused, int vcpu __unused, 128298d920d9SMark Johnston int dir, uint64_t addr __unused, int size __unused, uint64_t *val, 128398d920d9SMark Johnston void *arg1 __unused, long arg2 __unused) 12840ab13648SPeter Grehan { 12850ab13648SPeter Grehan /* 12860ab13648SPeter Grehan * Ignore writes; return 0xff's for reads. The mem read code 12870ab13648SPeter Grehan * will take care of truncating to the correct size. 12880ab13648SPeter Grehan */ 12890ab13648SPeter Grehan if (dir == MEM_F_READ) { 12900ab13648SPeter Grehan *val = 0xffffffffffffffff; 12910ab13648SPeter Grehan } 12920ab13648SPeter Grehan 12930ab13648SPeter Grehan return (0); 12940ab13648SPeter Grehan } 12950ab13648SPeter Grehan 129612a6eb99SNeel Natu static int 12976a284cacSJohn Baldwin pci_emul_ecfg_handler(struct vmctx *ctx __unused, int vcpu __unused, int dir, 129878c2cd83SJohn Baldwin uint64_t addr, int bytes, uint64_t *val, void *arg1 __unused, 129978c2cd83SJohn Baldwin long arg2 __unused) 130012a6eb99SNeel Natu { 130112a6eb99SNeel Natu int bus, slot, func, coff, in; 130212a6eb99SNeel Natu 130312a6eb99SNeel Natu coff = addr & 0xfff; 130412a6eb99SNeel Natu func = (addr >> 12) & 0x7; 130512a6eb99SNeel Natu slot = (addr >> 15) & 0x1f; 130612a6eb99SNeel Natu bus = (addr >> 20) & 0xff; 130712a6eb99SNeel Natu in = (dir == MEM_F_READ); 130812a6eb99SNeel Natu if (in) 130912a6eb99SNeel Natu *val = ~0UL; 13106a284cacSJohn Baldwin pci_cfgrw(in, bus, slot, func, coff, bytes, (uint32_t *)val); 131112a6eb99SNeel Natu return (0); 131212a6eb99SNeel Natu } 131312a6eb99SNeel Natu 131412a6eb99SNeel Natu uint64_t 131512a6eb99SNeel Natu pci_ecfg_base(void) 131612a6eb99SNeel Natu { 131712a6eb99SNeel Natu 131812a6eb99SNeel Natu return (PCI_EMUL_ECFG_BASE); 131912a6eb99SNeel Natu } 132012a6eb99SNeel Natu 1321d84882caSNeel Natu #define BUSIO_ROUNDUP 32 13227d55d295SCorvin Köhne #define BUSMEM32_ROUNDUP (1024 * 1024) 13237d55d295SCorvin Köhne #define BUSMEM64_ROUNDUP (512 * 1024 * 1024) 1324d84882caSNeel Natu 1325a38e2a64SPeter Grehan int 1326366f6083SPeter Grehan init_pci(struct vmctx *ctx) 1327366f6083SPeter Grehan { 1328621b5090SJohn Baldwin char node_name[sizeof("pci.XXX.XX.X")]; 132912a6eb99SNeel Natu struct mem_range mr; 1330366f6083SPeter Grehan struct pci_devemu *pde; 1331d84882caSNeel Natu struct businfo *bi; 1332d84882caSNeel Natu struct slotinfo *si; 13333cbf3585SJohn Baldwin struct funcinfo *fi; 1334621b5090SJohn Baldwin nvlist_t *nvl; 1335621b5090SJohn Baldwin const char *emul; 13369f08548dSNeel Natu size_t lowmem; 13374a4053e1SCorvin Köhne int bus, slot, func; 13384a4053e1SCorvin Köhne int error; 1339366f6083SPeter Grehan 13405cf21e48SCorvin Köhne if (vm_get_lowmem_limit(ctx) > PCI_EMUL_MEMBASE32) 13415cf21e48SCorvin Köhne errx(EX_OSERR, "Invalid lowmem limit"); 13425cf21e48SCorvin Köhne 1343366f6083SPeter Grehan pci_emul_iobase = PCI_EMUL_IOBASE; 13445cf21e48SCorvin Köhne pci_emul_membase32 = PCI_EMUL_MEMBASE32; 13459922872bSKonstantin Belousov 13464a4053e1SCorvin Köhne pci_emul_membase64 = 4*GB + vm_get_highmem_size(ctx); 13474a4053e1SCorvin Köhne pci_emul_membase64 = roundup2(pci_emul_membase64, PCI_EMUL_MEMSIZE64); 13484a4053e1SCorvin Köhne pci_emul_memlim64 = pci_emul_membase64 + PCI_EMUL_MEMSIZE64; 1349366f6083SPeter Grehan 1350d84882caSNeel Natu for (bus = 0; bus < MAXBUSES; bus++) { 1351621b5090SJohn Baldwin snprintf(node_name, sizeof(node_name), "pci.%d", bus); 1352621b5090SJohn Baldwin nvl = find_config_node(node_name); 1353621b5090SJohn Baldwin if (nvl == NULL) 1354d84882caSNeel Natu continue; 1355621b5090SJohn Baldwin pci_businfo[bus] = calloc(1, sizeof(struct businfo)); 1356621b5090SJohn Baldwin bi = pci_businfo[bus]; 1357621b5090SJohn Baldwin 1358d84882caSNeel Natu /* 1359d84882caSNeel Natu * Keep track of the i/o and memory resources allocated to 1360d84882caSNeel Natu * this bus. 1361d84882caSNeel Natu */ 1362d84882caSNeel Natu bi->iobase = pci_emul_iobase; 1363d84882caSNeel Natu bi->membase32 = pci_emul_membase32; 1364d84882caSNeel Natu bi->membase64 = pci_emul_membase64; 1365d84882caSNeel Natu 136601f9362eSCorvin Köhne /* first run: init devices */ 136799d65389SNeel Natu for (slot = 0; slot < MAXSLOTS; slot++) { 1368d84882caSNeel Natu si = &bi->slotinfo[slot]; 136999d65389SNeel Natu for (func = 0; func < MAXFUNCS; func++) { 1370d84882caSNeel Natu fi = &si->si_funcs[func]; 1371621b5090SJohn Baldwin snprintf(node_name, sizeof(node_name), 1372621b5090SJohn Baldwin "pci.%d.%d.%d", bus, slot, func); 1373621b5090SJohn Baldwin nvl = find_config_node(node_name); 1374621b5090SJohn Baldwin if (nvl == NULL) 1375d84882caSNeel Natu continue; 1376621b5090SJohn Baldwin 1377621b5090SJohn Baldwin fi->fi_config = nvl; 1378621b5090SJohn Baldwin emul = get_config_value_node(nvl, "device"); 1379621b5090SJohn Baldwin if (emul == NULL) { 1380621b5090SJohn Baldwin EPRINTLN("pci slot %d:%d:%d: missing " 1381621b5090SJohn Baldwin "\"device\" value", bus, slot, func); 1382621b5090SJohn Baldwin return (EINVAL); 1383621b5090SJohn Baldwin } 1384621b5090SJohn Baldwin pde = pci_emul_finddev(emul); 1385621b5090SJohn Baldwin if (pde == NULL) { 1386621b5090SJohn Baldwin EPRINTLN("pci slot %d:%d:%d: unknown " 1387621b5090SJohn Baldwin "device \"%s\"", bus, slot, func, 1388621b5090SJohn Baldwin emul); 1389621b5090SJohn Baldwin return (EINVAL); 1390621b5090SJohn Baldwin } 1391621b5090SJohn Baldwin if (pde->pe_alias != NULL) { 1392621b5090SJohn Baldwin EPRINTLN("pci slot %d:%d:%d: legacy " 1393621b5090SJohn Baldwin "device \"%s\", use \"%s\" instead", 1394621b5090SJohn Baldwin bus, slot, func, emul, 1395621b5090SJohn Baldwin pde->pe_alias); 1396621b5090SJohn Baldwin return (EINVAL); 1397621b5090SJohn Baldwin } 1398621b5090SJohn Baldwin fi->fi_pde = pde; 1399d84882caSNeel Natu error = pci_emul_init(ctx, pde, bus, slot, 1400d84882caSNeel Natu func, fi); 1401a38e2a64SPeter Grehan if (error) 1402a38e2a64SPeter Grehan return (error); 1403366f6083SPeter Grehan } 1404366f6083SPeter Grehan } 1405d84882caSNeel Natu 140601f9362eSCorvin Köhne /* second run: assign BARs and free list */ 140701f9362eSCorvin Köhne struct pci_bar_allocation *bar; 140801f9362eSCorvin Köhne struct pci_bar_allocation *bar_tmp; 140901f9362eSCorvin Köhne TAILQ_FOREACH_SAFE(bar, &pci_bars, chain, bar_tmp) { 141001f9362eSCorvin Köhne pci_emul_assign_bar(bar->pdi, bar->idx, bar->type, 141101f9362eSCorvin Köhne bar->size); 141201f9362eSCorvin Köhne free(bar); 141301f9362eSCorvin Köhne } 141401f9362eSCorvin Köhne TAILQ_INIT(&pci_bars); 141501f9362eSCorvin Köhne 1416d84882caSNeel Natu /* 1417d84882caSNeel Natu * Add some slop to the I/O and memory resources decoded by 1418d84882caSNeel Natu * this bus to give a guest some flexibility if it wants to 1419d84882caSNeel Natu * reprogram the BARs. 1420d84882caSNeel Natu */ 1421d84882caSNeel Natu pci_emul_iobase += BUSIO_ROUNDUP; 1422d84882caSNeel Natu pci_emul_iobase = roundup2(pci_emul_iobase, BUSIO_ROUNDUP); 1423d84882caSNeel Natu bi->iolimit = pci_emul_iobase; 1424d84882caSNeel Natu 14257d55d295SCorvin Köhne pci_emul_membase32 += BUSMEM32_ROUNDUP; 1426d84882caSNeel Natu pci_emul_membase32 = roundup2(pci_emul_membase32, 14277d55d295SCorvin Köhne BUSMEM32_ROUNDUP); 1428d84882caSNeel Natu bi->memlimit32 = pci_emul_membase32; 1429d84882caSNeel Natu 14307d55d295SCorvin Köhne pci_emul_membase64 += BUSMEM64_ROUNDUP; 1431d84882caSNeel Natu pci_emul_membase64 = roundup2(pci_emul_membase64, 14327d55d295SCorvin Köhne BUSMEM64_ROUNDUP); 1433d84882caSNeel Natu bi->memlimit64 = pci_emul_membase64; 1434366f6083SPeter Grehan } 14350038ee98SPeter Grehan 14360038ee98SPeter Grehan /* 1437b3e9732aSJohn Baldwin * PCI backends are initialized before routing INTx interrupts 1438b3e9732aSJohn Baldwin * so that LPC devices are able to reserve ISA IRQs before 1439b3e9732aSJohn Baldwin * routing PIRQ pins. 1440b3e9732aSJohn Baldwin */ 1441b3e9732aSJohn Baldwin for (bus = 0; bus < MAXBUSES; bus++) { 1442b3e9732aSJohn Baldwin if ((bi = pci_businfo[bus]) == NULL) 1443b3e9732aSJohn Baldwin continue; 1444b3e9732aSJohn Baldwin 1445b3e9732aSJohn Baldwin for (slot = 0; slot < MAXSLOTS; slot++) { 1446b3e9732aSJohn Baldwin si = &bi->slotinfo[slot]; 1447b3e9732aSJohn Baldwin for (func = 0; func < MAXFUNCS; func++) { 1448b3e9732aSJohn Baldwin fi = &si->si_funcs[func]; 1449b3e9732aSJohn Baldwin if (fi->fi_devi == NULL) 1450b3e9732aSJohn Baldwin continue; 1451b3e9732aSJohn Baldwin pci_lintr_route(fi->fi_devi); 1452b3e9732aSJohn Baldwin } 1453b3e9732aSJohn Baldwin } 1454b3e9732aSJohn Baldwin } 1455b3e9732aSJohn Baldwin lpc_pirq_routed(); 1456b3e9732aSJohn Baldwin 1457b3e9732aSJohn Baldwin /* 14589f08548dSNeel Natu * The guest physical memory map looks like the following: 14599f08548dSNeel Natu * [0, lowmem) guest system memory 14605cf21e48SCorvin Köhne * [lowmem, 0xC0000000) memory hole (may be absent) 14615cf21e48SCorvin Köhne * [0xC0000000, 0xE0000000) PCI hole (32-bit BAR allocation) 146212a6eb99SNeel Natu * [0xE0000000, 0xF0000000) PCI extended config window 146312a6eb99SNeel Natu * [0xF0000000, 4GB) LAPIC, IOAPIC, HPET, firmware 14649f08548dSNeel Natu * [4GB, 4GB + highmem) 146512a6eb99SNeel Natu */ 146612a6eb99SNeel Natu 146712a6eb99SNeel Natu /* 14689f08548dSNeel Natu * Accesses to memory addresses that are not allocated to system 14699f08548dSNeel Natu * memory or PCI devices return 0xff's. 14700ab13648SPeter Grehan */ 1471be679db4SNeel Natu lowmem = vm_get_lowmem_size(ctx); 147212a6eb99SNeel Natu bzero(&mr, sizeof(struct mem_range)); 147312a6eb99SNeel Natu mr.name = "PCI hole"; 147412a6eb99SNeel Natu mr.flags = MEM_F_RW | MEM_F_IMMUTABLE; 147512a6eb99SNeel Natu mr.base = lowmem; 147612a6eb99SNeel Natu mr.size = (4ULL * 1024 * 1024 * 1024) - lowmem; 147712a6eb99SNeel Natu mr.handler = pci_emul_fallback_handler; 147812a6eb99SNeel Natu error = register_mem_fallback(&mr); 147912a6eb99SNeel Natu assert(error == 0); 14809f08548dSNeel Natu 148112a6eb99SNeel Natu /* PCI extended config space */ 148212a6eb99SNeel Natu bzero(&mr, sizeof(struct mem_range)); 148312a6eb99SNeel Natu mr.name = "PCI ECFG"; 148412a6eb99SNeel Natu mr.flags = MEM_F_RW | MEM_F_IMMUTABLE; 148512a6eb99SNeel Natu mr.base = PCI_EMUL_ECFG_BASE; 148612a6eb99SNeel Natu mr.size = PCI_EMUL_ECFG_SIZE; 148712a6eb99SNeel Natu mr.handler = pci_emul_ecfg_handler; 148812a6eb99SNeel Natu error = register_mem(&mr); 14890ab13648SPeter Grehan assert(error == 0); 1490a38e2a64SPeter Grehan 1491a38e2a64SPeter Grehan return (0); 1492366f6083SPeter Grehan } 1493366f6083SPeter Grehan 14943cbf3585SJohn Baldwin static void 149598d920d9SMark Johnston pci_apic_prt_entry(int bus __unused, int slot, int pin, int pirq_pin __unused, 149698d920d9SMark Johnston int ioapic_irq, void *arg __unused) 14973cbf3585SJohn Baldwin { 14983cbf3585SJohn Baldwin 1499b3e9732aSJohn Baldwin dsdt_line(" Package ()"); 15003cbf3585SJohn Baldwin dsdt_line(" {"); 15013cbf3585SJohn Baldwin dsdt_line(" 0x%X,", slot << 16 | 0xffff); 15023cbf3585SJohn Baldwin dsdt_line(" 0x%02X,", pin - 1); 15033cbf3585SJohn Baldwin dsdt_line(" Zero,"); 15043cbf3585SJohn Baldwin dsdt_line(" 0x%X", ioapic_irq); 1505b3e9732aSJohn Baldwin dsdt_line(" },"); 1506b3e9732aSJohn Baldwin } 1507b3e9732aSJohn Baldwin 1508b3e9732aSJohn Baldwin static void 150998d920d9SMark Johnston pci_pirq_prt_entry(int bus __unused, int slot, int pin, int pirq_pin, 151098d920d9SMark Johnston int ioapic_irq __unused, void *arg __unused) 1511b3e9732aSJohn Baldwin { 1512b3e9732aSJohn Baldwin char *name; 1513b3e9732aSJohn Baldwin 1514b3e9732aSJohn Baldwin name = lpc_pirq_name(pirq_pin); 1515b3e9732aSJohn Baldwin if (name == NULL) 1516b3e9732aSJohn Baldwin return; 1517b3e9732aSJohn Baldwin dsdt_line(" Package ()"); 1518b3e9732aSJohn Baldwin dsdt_line(" {"); 1519b3e9732aSJohn Baldwin dsdt_line(" 0x%X,", slot << 16 | 0xffff); 1520b3e9732aSJohn Baldwin dsdt_line(" 0x%02X,", pin - 1); 1521b3e9732aSJohn Baldwin dsdt_line(" %s,", name); 1522b3e9732aSJohn Baldwin dsdt_line(" 0x00"); 1523b3e9732aSJohn Baldwin dsdt_line(" },"); 1524b3e9732aSJohn Baldwin free(name); 15253cbf3585SJohn Baldwin } 15263cbf3585SJohn Baldwin 1527d84882caSNeel Natu /* 1528d84882caSNeel Natu * A bhyve virtual machine has a flat PCI hierarchy with a root port 1529d84882caSNeel Natu * corresponding to each PCI bus. 1530d84882caSNeel Natu */ 1531d84882caSNeel Natu static void 1532d84882caSNeel Natu pci_bus_write_dsdt(int bus) 1533e6c8bc29SJohn Baldwin { 1534d84882caSNeel Natu struct businfo *bi; 1535d84882caSNeel Natu struct slotinfo *si; 1536e6c8bc29SJohn Baldwin struct pci_devinst *pi; 1537b3e9732aSJohn Baldwin int count, func, slot; 1538e6c8bc29SJohn Baldwin 1539d84882caSNeel Natu /* 1540d84882caSNeel Natu * If there are no devices on this 'bus' then just return. 1541d84882caSNeel Natu */ 1542d84882caSNeel Natu if ((bi = pci_businfo[bus]) == NULL) { 1543d84882caSNeel Natu /* 1544d84882caSNeel Natu * Bus 0 is special because it decodes the I/O ports used 1545d84882caSNeel Natu * for PCI config space access even if there are no devices 1546d84882caSNeel Natu * on it. 1547d84882caSNeel Natu */ 1548d84882caSNeel Natu if (bus != 0) 1549d84882caSNeel Natu return; 1550d84882caSNeel Natu } 1551d84882caSNeel Natu 1552d84882caSNeel Natu dsdt_line(" Device (PC%02X)", bus); 1553e6c8bc29SJohn Baldwin dsdt_line(" {"); 1554e6c8bc29SJohn Baldwin dsdt_line(" Name (_HID, EisaId (\"PNP0A03\"))"); 1555d84882caSNeel Natu 1556d84882caSNeel Natu dsdt_line(" Method (_BBN, 0, NotSerialized)"); 1557d84882caSNeel Natu dsdt_line(" {"); 1558d84882caSNeel Natu dsdt_line(" Return (0x%08X)", bus); 1559d84882caSNeel Natu dsdt_line(" }"); 1560e6c8bc29SJohn Baldwin dsdt_line(" Name (_CRS, ResourceTemplate ()"); 1561e6c8bc29SJohn Baldwin dsdt_line(" {"); 1562e6c8bc29SJohn Baldwin dsdt_line(" WordBusNumber (ResourceProducer, MinFixed, " 1563e6c8bc29SJohn Baldwin "MaxFixed, PosDecode,"); 1564e6c8bc29SJohn Baldwin dsdt_line(" 0x0000, // Granularity"); 1565d84882caSNeel Natu dsdt_line(" 0x%04X, // Range Minimum", bus); 1566d84882caSNeel Natu dsdt_line(" 0x%04X, // Range Maximum", bus); 1567e6c8bc29SJohn Baldwin dsdt_line(" 0x0000, // Translation Offset"); 1568d84882caSNeel Natu dsdt_line(" 0x0001, // Length"); 1569e6c8bc29SJohn Baldwin dsdt_line(" ,, )"); 1570d84882caSNeel Natu 1571d84882caSNeel Natu if (bus == 0) { 1572e6c8bc29SJohn Baldwin dsdt_indent(3); 1573e6c8bc29SJohn Baldwin dsdt_fixed_ioport(0xCF8, 8); 1574e6c8bc29SJohn Baldwin dsdt_unindent(3); 1575d84882caSNeel Natu 1576e6c8bc29SJohn Baldwin dsdt_line(" WordIO (ResourceProducer, MinFixed, MaxFixed, " 1577e6c8bc29SJohn Baldwin "PosDecode, EntireRange,"); 1578e6c8bc29SJohn Baldwin dsdt_line(" 0x0000, // Granularity"); 1579e6c8bc29SJohn Baldwin dsdt_line(" 0x0000, // Range Minimum"); 1580e6c8bc29SJohn Baldwin dsdt_line(" 0x0CF7, // Range Maximum"); 1581e6c8bc29SJohn Baldwin dsdt_line(" 0x0000, // Translation Offset"); 1582e6c8bc29SJohn Baldwin dsdt_line(" 0x0CF8, // Length"); 1583e6c8bc29SJohn Baldwin dsdt_line(" ,, , TypeStatic)"); 1584d84882caSNeel Natu 1585e6c8bc29SJohn Baldwin dsdt_line(" WordIO (ResourceProducer, MinFixed, MaxFixed, " 1586e6c8bc29SJohn Baldwin "PosDecode, EntireRange,"); 1587e6c8bc29SJohn Baldwin dsdt_line(" 0x0000, // Granularity"); 1588e6c8bc29SJohn Baldwin dsdt_line(" 0x0D00, // Range Minimum"); 1589d84882caSNeel Natu dsdt_line(" 0x%04X, // Range Maximum", 1590d84882caSNeel Natu PCI_EMUL_IOBASE - 1); 1591e6c8bc29SJohn Baldwin dsdt_line(" 0x0000, // Translation Offset"); 1592d84882caSNeel Natu dsdt_line(" 0x%04X, // Length", 1593d84882caSNeel Natu PCI_EMUL_IOBASE - 0x0D00); 1594e6c8bc29SJohn Baldwin dsdt_line(" ,, , TypeStatic)"); 1595d84882caSNeel Natu 1596d84882caSNeel Natu if (bi == NULL) { 1597d84882caSNeel Natu dsdt_line(" })"); 1598d84882caSNeel Natu goto done; 1599d84882caSNeel Natu } 1600d84882caSNeel Natu } 1601d84882caSNeel Natu assert(bi != NULL); 1602d84882caSNeel Natu 1603d84882caSNeel Natu /* i/o window */ 1604d84882caSNeel Natu dsdt_line(" WordIO (ResourceProducer, MinFixed, MaxFixed, " 1605d84882caSNeel Natu "PosDecode, EntireRange,"); 1606d84882caSNeel Natu dsdt_line(" 0x0000, // Granularity"); 1607d84882caSNeel Natu dsdt_line(" 0x%04X, // Range Minimum", bi->iobase); 1608d84882caSNeel Natu dsdt_line(" 0x%04X, // Range Maximum", 1609d84882caSNeel Natu bi->iolimit - 1); 1610d84882caSNeel Natu dsdt_line(" 0x0000, // Translation Offset"); 1611d84882caSNeel Natu dsdt_line(" 0x%04X, // Length", 1612d84882caSNeel Natu bi->iolimit - bi->iobase); 1613d84882caSNeel Natu dsdt_line(" ,, , TypeStatic)"); 1614d84882caSNeel Natu 1615d84882caSNeel Natu /* mmio window (32-bit) */ 1616e6c8bc29SJohn Baldwin dsdt_line(" DWordMemory (ResourceProducer, PosDecode, " 1617e6c8bc29SJohn Baldwin "MinFixed, MaxFixed, NonCacheable, ReadWrite,"); 1618e6c8bc29SJohn Baldwin dsdt_line(" 0x00000000, // Granularity"); 1619d84882caSNeel Natu dsdt_line(" 0x%08X, // Range Minimum\n", bi->membase32); 1620e6c8bc29SJohn Baldwin dsdt_line(" 0x%08X, // Range Maximum\n", 1621d84882caSNeel Natu bi->memlimit32 - 1); 1622e6c8bc29SJohn Baldwin dsdt_line(" 0x00000000, // Translation Offset"); 1623d84882caSNeel Natu dsdt_line(" 0x%08X, // Length\n", 1624d84882caSNeel Natu bi->memlimit32 - bi->membase32); 1625e6c8bc29SJohn Baldwin dsdt_line(" ,, , AddressRangeMemory, TypeStatic)"); 1626d84882caSNeel Natu 1627d84882caSNeel Natu /* mmio window (64-bit) */ 1628e6c8bc29SJohn Baldwin dsdt_line(" QWordMemory (ResourceProducer, PosDecode, " 1629e6c8bc29SJohn Baldwin "MinFixed, MaxFixed, NonCacheable, ReadWrite,"); 1630e6c8bc29SJohn Baldwin dsdt_line(" 0x0000000000000000, // Granularity"); 1631d84882caSNeel Natu dsdt_line(" 0x%016lX, // Range Minimum\n", bi->membase64); 1632e6c8bc29SJohn Baldwin dsdt_line(" 0x%016lX, // Range Maximum\n", 1633d84882caSNeel Natu bi->memlimit64 - 1); 1634e6c8bc29SJohn Baldwin dsdt_line(" 0x0000000000000000, // Translation Offset"); 1635e6c8bc29SJohn Baldwin dsdt_line(" 0x%016lX, // Length\n", 1636d84882caSNeel Natu bi->memlimit64 - bi->membase64); 1637e6c8bc29SJohn Baldwin dsdt_line(" ,, , AddressRangeMemory, TypeStatic)"); 1638e6c8bc29SJohn Baldwin dsdt_line(" })"); 1639d84882caSNeel Natu 1640d84882caSNeel Natu count = pci_count_lintr(bus); 16413cbf3585SJohn Baldwin if (count != 0) { 16423cbf3585SJohn Baldwin dsdt_indent(2); 1643b3e9732aSJohn Baldwin dsdt_line("Name (PPRT, Package ()"); 16443cbf3585SJohn Baldwin dsdt_line("{"); 1645b3e9732aSJohn Baldwin pci_walk_lintr(bus, pci_pirq_prt_entry, NULL); 16463cbf3585SJohn Baldwin dsdt_line("})"); 1647b3e9732aSJohn Baldwin dsdt_line("Name (APRT, Package ()"); 1648b3e9732aSJohn Baldwin dsdt_line("{"); 1649b3e9732aSJohn Baldwin pci_walk_lintr(bus, pci_apic_prt_entry, NULL); 1650b3e9732aSJohn Baldwin dsdt_line("})"); 1651b3e9732aSJohn Baldwin dsdt_line("Method (_PRT, 0, NotSerialized)"); 1652b3e9732aSJohn Baldwin dsdt_line("{"); 1653b3e9732aSJohn Baldwin dsdt_line(" If (PICM)"); 1654b3e9732aSJohn Baldwin dsdt_line(" {"); 1655b3e9732aSJohn Baldwin dsdt_line(" Return (APRT)"); 1656b3e9732aSJohn Baldwin dsdt_line(" }"); 1657b3e9732aSJohn Baldwin dsdt_line(" Else"); 1658b3e9732aSJohn Baldwin dsdt_line(" {"); 1659b3e9732aSJohn Baldwin dsdt_line(" Return (PPRT)"); 1660b3e9732aSJohn Baldwin dsdt_line(" }"); 1661b3e9732aSJohn Baldwin dsdt_line("}"); 16623cbf3585SJohn Baldwin dsdt_unindent(2); 16633cbf3585SJohn Baldwin } 1664e6c8bc29SJohn Baldwin 1665e6c8bc29SJohn Baldwin dsdt_indent(2); 1666e6c8bc29SJohn Baldwin for (slot = 0; slot < MAXSLOTS; slot++) { 1667d84882caSNeel Natu si = &bi->slotinfo[slot]; 1668e6c8bc29SJohn Baldwin for (func = 0; func < MAXFUNCS; func++) { 1669d84882caSNeel Natu pi = si->si_funcs[func].fi_devi; 1670e6c8bc29SJohn Baldwin if (pi != NULL && pi->pi_d->pe_write_dsdt != NULL) 1671e6c8bc29SJohn Baldwin pi->pi_d->pe_write_dsdt(pi); 1672e6c8bc29SJohn Baldwin } 1673e6c8bc29SJohn Baldwin } 1674e6c8bc29SJohn Baldwin dsdt_unindent(2); 1675d84882caSNeel Natu done: 1676e6c8bc29SJohn Baldwin dsdt_line(" }"); 1677e6c8bc29SJohn Baldwin } 1678e6c8bc29SJohn Baldwin 1679d84882caSNeel Natu void 1680d84882caSNeel Natu pci_write_dsdt(void) 1681d84882caSNeel Natu { 1682d84882caSNeel Natu int bus; 1683d84882caSNeel Natu 1684b3e9732aSJohn Baldwin dsdt_indent(1); 1685b3e9732aSJohn Baldwin dsdt_line("Name (PICM, 0x00)"); 1686b3e9732aSJohn Baldwin dsdt_line("Method (_PIC, 1, NotSerialized)"); 1687b3e9732aSJohn Baldwin dsdt_line("{"); 1688b3e9732aSJohn Baldwin dsdt_line(" Store (Arg0, PICM)"); 1689b3e9732aSJohn Baldwin dsdt_line("}"); 1690b3e9732aSJohn Baldwin dsdt_line(""); 1691b3e9732aSJohn Baldwin dsdt_line("Scope (_SB)"); 1692b3e9732aSJohn Baldwin dsdt_line("{"); 1693d84882caSNeel Natu for (bus = 0; bus < MAXBUSES; bus++) 1694d84882caSNeel Natu pci_bus_write_dsdt(bus); 1695b3e9732aSJohn Baldwin dsdt_line("}"); 1696b3e9732aSJohn Baldwin dsdt_unindent(1); 1697d84882caSNeel Natu } 1698d84882caSNeel Natu 1699366f6083SPeter Grehan int 1700b100acf2SNeel Natu pci_bus_configured(int bus) 1701b100acf2SNeel Natu { 1702b100acf2SNeel Natu assert(bus >= 0 && bus < MAXBUSES); 1703b100acf2SNeel Natu return (pci_businfo[bus] != NULL); 1704b100acf2SNeel Natu } 1705b100acf2SNeel Natu 1706b100acf2SNeel Natu int 1707366f6083SPeter Grehan pci_msi_enabled(struct pci_devinst *pi) 1708366f6083SPeter Grehan { 1709366f6083SPeter Grehan return (pi->pi_msi.enabled); 1710366f6083SPeter Grehan } 1711366f6083SPeter Grehan 1712366f6083SPeter Grehan int 17134f8be175SNeel Natu pci_msi_maxmsgnum(struct pci_devinst *pi) 1714366f6083SPeter Grehan { 1715366f6083SPeter Grehan if (pi->pi_msi.enabled) 17164f8be175SNeel Natu return (pi->pi_msi.maxmsgnum); 1717366f6083SPeter Grehan else 1718366f6083SPeter Grehan return (0); 1719366f6083SPeter Grehan } 1720366f6083SPeter Grehan 1721c9b4e987SNeel Natu int 1722c9b4e987SNeel Natu pci_msix_enabled(struct pci_devinst *pi) 1723c9b4e987SNeel Natu { 1724c9b4e987SNeel Natu 1725c9b4e987SNeel Natu return (pi->pi_msix.enabled && !pi->pi_msi.enabled); 1726c9b4e987SNeel Natu } 1727c9b4e987SNeel Natu 1728c9b4e987SNeel Natu void 1729c9b4e987SNeel Natu pci_generate_msix(struct pci_devinst *pi, int index) 1730c9b4e987SNeel Natu { 1731c9b4e987SNeel Natu struct msix_table_entry *mte; 1732c9b4e987SNeel Natu 1733c9b4e987SNeel Natu if (!pci_msix_enabled(pi)) 1734c9b4e987SNeel Natu return; 1735c9b4e987SNeel Natu 1736c9b4e987SNeel Natu if (pi->pi_msix.function_mask) 1737c9b4e987SNeel Natu return; 1738c9b4e987SNeel Natu 1739c9b4e987SNeel Natu if (index >= pi->pi_msix.table_count) 1740c9b4e987SNeel Natu return; 1741c9b4e987SNeel Natu 1742c9b4e987SNeel Natu mte = &pi->pi_msix.table[index]; 1743c9b4e987SNeel Natu if ((mte->vector_control & PCIM_MSIX_VCTRL_MASK) == 0) { 1744c9b4e987SNeel Natu /* XXX Set PBA bit if interrupt is disabled */ 17454f8be175SNeel Natu vm_lapic_msi(pi->pi_vmctx, mte->addr, mte->msg_data); 1746c9b4e987SNeel Natu } 1747c9b4e987SNeel Natu } 1748c9b4e987SNeel Natu 1749366f6083SPeter Grehan void 17504f8be175SNeel Natu pci_generate_msi(struct pci_devinst *pi, int index) 1751366f6083SPeter Grehan { 1752366f6083SPeter Grehan 17534f8be175SNeel Natu if (pci_msi_enabled(pi) && index < pci_msi_maxmsgnum(pi)) { 17544f8be175SNeel Natu vm_lapic_msi(pi->pi_vmctx, pi->pi_msi.addr, 17554f8be175SNeel Natu pi->pi_msi.msg_data + index); 1756366f6083SPeter Grehan } 1757366f6083SPeter Grehan } 1758366f6083SPeter Grehan 17593cbf3585SJohn Baldwin static bool 17603cbf3585SJohn Baldwin pci_lintr_permitted(struct pci_devinst *pi) 17610038ee98SPeter Grehan { 17623cbf3585SJohn Baldwin uint16_t cmd; 17630038ee98SPeter Grehan 17643cbf3585SJohn Baldwin cmd = pci_get_cfgdata16(pi, PCIR_COMMAND); 17653cbf3585SJohn Baldwin return (!(pi->pi_msi.enabled || pi->pi_msix.enabled || 17663cbf3585SJohn Baldwin (cmd & PCIM_CMD_INTxDIS))); 17673cbf3585SJohn Baldwin } 17683cbf3585SJohn Baldwin 1769b3e9732aSJohn Baldwin void 17703cbf3585SJohn Baldwin pci_lintr_request(struct pci_devinst *pi) 17713cbf3585SJohn Baldwin { 1772d84882caSNeel Natu struct businfo *bi; 17733cbf3585SJohn Baldwin struct slotinfo *si; 1774b3e9732aSJohn Baldwin int bestpin, bestcount, pin; 17753cbf3585SJohn Baldwin 1776d84882caSNeel Natu bi = pci_businfo[pi->pi_bus]; 1777d84882caSNeel Natu assert(bi != NULL); 1778d84882caSNeel Natu 17793cbf3585SJohn Baldwin /* 1780b3e9732aSJohn Baldwin * Just allocate a pin from our slot. The pin will be 1781b3e9732aSJohn Baldwin * assigned IRQs later when interrupts are routed. 17823cbf3585SJohn Baldwin */ 1783d84882caSNeel Natu si = &bi->slotinfo[pi->pi_slot]; 17843cbf3585SJohn Baldwin bestpin = 0; 17853cbf3585SJohn Baldwin bestcount = si->si_intpins[0].ii_count; 17863cbf3585SJohn Baldwin for (pin = 1; pin < 4; pin++) { 17873cbf3585SJohn Baldwin if (si->si_intpins[pin].ii_count < bestcount) { 17883cbf3585SJohn Baldwin bestpin = pin; 17893cbf3585SJohn Baldwin bestcount = si->si_intpins[pin].ii_count; 17903cbf3585SJohn Baldwin } 17913cbf3585SJohn Baldwin } 17923cbf3585SJohn Baldwin 17933cbf3585SJohn Baldwin si->si_intpins[bestpin].ii_count++; 17943cbf3585SJohn Baldwin pi->pi_lintr.pin = bestpin + 1; 17953cbf3585SJohn Baldwin pci_set_cfgdata8(pi, PCIR_INTPIN, bestpin + 1); 1796b3e9732aSJohn Baldwin } 1797b3e9732aSJohn Baldwin 1798b3e9732aSJohn Baldwin static void 1799b3e9732aSJohn Baldwin pci_lintr_route(struct pci_devinst *pi) 1800b3e9732aSJohn Baldwin { 1801b3e9732aSJohn Baldwin struct businfo *bi; 1802b3e9732aSJohn Baldwin struct intxinfo *ii; 1803b3e9732aSJohn Baldwin 1804b3e9732aSJohn Baldwin if (pi->pi_lintr.pin == 0) 1805b3e9732aSJohn Baldwin return; 1806b3e9732aSJohn Baldwin 1807b3e9732aSJohn Baldwin bi = pci_businfo[pi->pi_bus]; 1808b3e9732aSJohn Baldwin assert(bi != NULL); 1809b3e9732aSJohn Baldwin ii = &bi->slotinfo[pi->pi_slot].si_intpins[pi->pi_lintr.pin - 1]; 1810b3e9732aSJohn Baldwin 1811b3e9732aSJohn Baldwin /* 1812b3e9732aSJohn Baldwin * Attempt to allocate an I/O APIC pin for this intpin if one 1813b3e9732aSJohn Baldwin * is not yet assigned. 1814b3e9732aSJohn Baldwin */ 1815b3e9732aSJohn Baldwin if (ii->ii_ioapic_irq == 0) 18161b4496d0SAlexander Motin ii->ii_ioapic_irq = ioapic_pci_alloc_irq(pi); 1817b3e9732aSJohn Baldwin assert(ii->ii_ioapic_irq > 0); 1818b3e9732aSJohn Baldwin 1819b3e9732aSJohn Baldwin /* 1820b3e9732aSJohn Baldwin * Attempt to allocate a PIRQ pin for this intpin if one is 1821b3e9732aSJohn Baldwin * not yet assigned. 1822b3e9732aSJohn Baldwin */ 1823b3e9732aSJohn Baldwin if (ii->ii_pirq_pin == 0) 18241b4496d0SAlexander Motin ii->ii_pirq_pin = pirq_alloc_pin(pi); 1825b3e9732aSJohn Baldwin assert(ii->ii_pirq_pin > 0); 1826b3e9732aSJohn Baldwin 1827b3e9732aSJohn Baldwin pi->pi_lintr.ioapic_irq = ii->ii_ioapic_irq; 1828b3e9732aSJohn Baldwin pi->pi_lintr.pirq_pin = ii->ii_pirq_pin; 1829b3e9732aSJohn Baldwin pci_set_cfgdata8(pi, PCIR_INTLINE, pirq_irq(ii->ii_pirq_pin)); 18300038ee98SPeter Grehan } 18310038ee98SPeter Grehan 18320038ee98SPeter Grehan void 18330038ee98SPeter Grehan pci_lintr_assert(struct pci_devinst *pi) 18340038ee98SPeter Grehan { 18350038ee98SPeter Grehan 18363cbf3585SJohn Baldwin assert(pi->pi_lintr.pin > 0); 1837ac7304a7SNeel Natu 18383cbf3585SJohn Baldwin pthread_mutex_lock(&pi->pi_lintr.lock); 18393cbf3585SJohn Baldwin if (pi->pi_lintr.state == IDLE) { 18403cbf3585SJohn Baldwin if (pci_lintr_permitted(pi)) { 18413cbf3585SJohn Baldwin pi->pi_lintr.state = ASSERTED; 1842b3e9732aSJohn Baldwin pci_irq_assert(pi); 18433cbf3585SJohn Baldwin } else 18443cbf3585SJohn Baldwin pi->pi_lintr.state = PENDING; 18450038ee98SPeter Grehan } 18463cbf3585SJohn Baldwin pthread_mutex_unlock(&pi->pi_lintr.lock); 1847ac7304a7SNeel Natu } 18480038ee98SPeter Grehan 18490038ee98SPeter Grehan void 18500038ee98SPeter Grehan pci_lintr_deassert(struct pci_devinst *pi) 18510038ee98SPeter Grehan { 18520038ee98SPeter Grehan 18533cbf3585SJohn Baldwin assert(pi->pi_lintr.pin > 0); 1854ac7304a7SNeel Natu 18553cbf3585SJohn Baldwin pthread_mutex_lock(&pi->pi_lintr.lock); 18563cbf3585SJohn Baldwin if (pi->pi_lintr.state == ASSERTED) { 18573cbf3585SJohn Baldwin pi->pi_lintr.state = IDLE; 1858b3e9732aSJohn Baldwin pci_irq_deassert(pi); 18593cbf3585SJohn Baldwin } else if (pi->pi_lintr.state == PENDING) 18603cbf3585SJohn Baldwin pi->pi_lintr.state = IDLE; 18613cbf3585SJohn Baldwin pthread_mutex_unlock(&pi->pi_lintr.lock); 18623cbf3585SJohn Baldwin } 18633cbf3585SJohn Baldwin 18643cbf3585SJohn Baldwin static void 18653cbf3585SJohn Baldwin pci_lintr_update(struct pci_devinst *pi) 18663cbf3585SJohn Baldwin { 18673cbf3585SJohn Baldwin 18683cbf3585SJohn Baldwin pthread_mutex_lock(&pi->pi_lintr.lock); 18693cbf3585SJohn Baldwin if (pi->pi_lintr.state == ASSERTED && !pci_lintr_permitted(pi)) { 1870b3e9732aSJohn Baldwin pci_irq_deassert(pi); 18713cbf3585SJohn Baldwin pi->pi_lintr.state = PENDING; 18723cbf3585SJohn Baldwin } else if (pi->pi_lintr.state == PENDING && pci_lintr_permitted(pi)) { 18733cbf3585SJohn Baldwin pi->pi_lintr.state = ASSERTED; 1874b3e9732aSJohn Baldwin pci_irq_assert(pi); 18753cbf3585SJohn Baldwin } 18763cbf3585SJohn Baldwin pthread_mutex_unlock(&pi->pi_lintr.lock); 18773cbf3585SJohn Baldwin } 18783cbf3585SJohn Baldwin 18793cbf3585SJohn Baldwin int 1880d84882caSNeel Natu pci_count_lintr(int bus) 18813cbf3585SJohn Baldwin { 18823cbf3585SJohn Baldwin int count, slot, pin; 1883d84882caSNeel Natu struct slotinfo *slotinfo; 18843cbf3585SJohn Baldwin 18853cbf3585SJohn Baldwin count = 0; 1886d84882caSNeel Natu if (pci_businfo[bus] != NULL) { 18873cbf3585SJohn Baldwin for (slot = 0; slot < MAXSLOTS; slot++) { 1888d84882caSNeel Natu slotinfo = &pci_businfo[bus]->slotinfo[slot]; 18893cbf3585SJohn Baldwin for (pin = 0; pin < 4; pin++) { 1890d84882caSNeel Natu if (slotinfo->si_intpins[pin].ii_count != 0) 18913cbf3585SJohn Baldwin count++; 18923cbf3585SJohn Baldwin } 18933cbf3585SJohn Baldwin } 1894d84882caSNeel Natu } 18953cbf3585SJohn Baldwin return (count); 18963cbf3585SJohn Baldwin } 18973cbf3585SJohn Baldwin 18983cbf3585SJohn Baldwin void 1899d84882caSNeel Natu pci_walk_lintr(int bus, pci_lintr_cb cb, void *arg) 19003cbf3585SJohn Baldwin { 1901d84882caSNeel Natu struct businfo *bi; 1902d84882caSNeel Natu struct slotinfo *si; 19033cbf3585SJohn Baldwin struct intxinfo *ii; 19043cbf3585SJohn Baldwin int slot, pin; 19053cbf3585SJohn Baldwin 1906d84882caSNeel Natu if ((bi = pci_businfo[bus]) == NULL) 1907d84882caSNeel Natu return; 1908d84882caSNeel Natu 19093cbf3585SJohn Baldwin for (slot = 0; slot < MAXSLOTS; slot++) { 1910d84882caSNeel Natu si = &bi->slotinfo[slot]; 19113cbf3585SJohn Baldwin for (pin = 0; pin < 4; pin++) { 1912d84882caSNeel Natu ii = &si->si_intpins[pin]; 19133cbf3585SJohn Baldwin if (ii->ii_count != 0) 1914b3e9732aSJohn Baldwin cb(bus, slot, pin + 1, ii->ii_pirq_pin, 1915b3e9732aSJohn Baldwin ii->ii_ioapic_irq, arg); 19163cbf3585SJohn Baldwin } 19170038ee98SPeter Grehan } 1918ac7304a7SNeel Natu } 19190038ee98SPeter Grehan 192099d65389SNeel Natu /* 192199d65389SNeel Natu * Return 1 if the emulated device in 'slot' is a multi-function device. 192299d65389SNeel Natu * Return 0 otherwise. 192399d65389SNeel Natu */ 192499d65389SNeel Natu static int 1925d84882caSNeel Natu pci_emul_is_mfdev(int bus, int slot) 192699d65389SNeel Natu { 1927d84882caSNeel Natu struct businfo *bi; 1928d84882caSNeel Natu struct slotinfo *si; 192999d65389SNeel Natu int f, numfuncs; 19300038ee98SPeter Grehan 193199d65389SNeel Natu numfuncs = 0; 1932d84882caSNeel Natu if ((bi = pci_businfo[bus]) != NULL) { 1933d84882caSNeel Natu si = &bi->slotinfo[slot]; 193499d65389SNeel Natu for (f = 0; f < MAXFUNCS; f++) { 1935d84882caSNeel Natu if (si->si_funcs[f].fi_devi != NULL) { 193699d65389SNeel Natu numfuncs++; 193799d65389SNeel Natu } 193899d65389SNeel Natu } 1939d84882caSNeel Natu } 194099d65389SNeel Natu return (numfuncs > 1); 194199d65389SNeel Natu } 194299d65389SNeel Natu 194399d65389SNeel Natu /* 194499d65389SNeel Natu * Ensure that the PCIM_MFDEV bit is properly set (or unset) depending on 194599d65389SNeel Natu * whether or not is a multi-function being emulated in the pci 'slot'. 194699d65389SNeel Natu */ 194799d65389SNeel Natu static void 1948d84882caSNeel Natu pci_emul_hdrtype_fixup(int bus, int slot, int off, int bytes, uint32_t *rv) 194999d65389SNeel Natu { 195099d65389SNeel Natu int mfdev; 195199d65389SNeel Natu 195299d65389SNeel Natu if (off <= PCIR_HDRTYPE && off + bytes > PCIR_HDRTYPE) { 1953d84882caSNeel Natu mfdev = pci_emul_is_mfdev(bus, slot); 195499d65389SNeel Natu switch (bytes) { 195599d65389SNeel Natu case 1: 195699d65389SNeel Natu case 2: 195799d65389SNeel Natu *rv &= ~PCIM_MFDEV; 195899d65389SNeel Natu if (mfdev) { 195999d65389SNeel Natu *rv |= PCIM_MFDEV; 196099d65389SNeel Natu } 196199d65389SNeel Natu break; 196299d65389SNeel Natu case 4: 196399d65389SNeel Natu *rv &= ~(PCIM_MFDEV << 16); 196499d65389SNeel Natu if (mfdev) { 196599d65389SNeel Natu *rv |= (PCIM_MFDEV << 16); 196699d65389SNeel Natu } 196799d65389SNeel Natu break; 196899d65389SNeel Natu } 196999d65389SNeel Natu } 197099d65389SNeel Natu } 19710038ee98SPeter Grehan 197256282675SJohn Baldwin /* 197356282675SJohn Baldwin * Update device state in response to changes to the PCI command 197456282675SJohn Baldwin * register. 197556282675SJohn Baldwin */ 197656282675SJohn Baldwin void 197756282675SJohn Baldwin pci_emul_cmd_changed(struct pci_devinst *pi, uint16_t old) 197856282675SJohn Baldwin { 197956282675SJohn Baldwin int i; 198056282675SJohn Baldwin uint16_t changed, new; 198156282675SJohn Baldwin 198256282675SJohn Baldwin new = pci_get_cfgdata16(pi, PCIR_COMMAND); 198356282675SJohn Baldwin changed = old ^ new; 198456282675SJohn Baldwin 198556282675SJohn Baldwin /* 198656282675SJohn Baldwin * If the MMIO or I/O address space decoding has changed then 198756282675SJohn Baldwin * register/unregister all BARs that decode that address space. 198856282675SJohn Baldwin */ 1989e47fe318SCorvin Köhne for (i = 0; i <= PCI_BARMAX_WITH_ROM; i++) { 199056282675SJohn Baldwin switch (pi->pi_bar[i].type) { 199156282675SJohn Baldwin case PCIBAR_NONE: 199256282675SJohn Baldwin case PCIBAR_MEMHI64: 199356282675SJohn Baldwin break; 199456282675SJohn Baldwin case PCIBAR_IO: 199556282675SJohn Baldwin /* I/O address space decoding changed? */ 199656282675SJohn Baldwin if (changed & PCIM_CMD_PORTEN) { 199756282675SJohn Baldwin if (new & PCIM_CMD_PORTEN) 199856282675SJohn Baldwin register_bar(pi, i); 199956282675SJohn Baldwin else 200056282675SJohn Baldwin unregister_bar(pi, i); 200156282675SJohn Baldwin } 200256282675SJohn Baldwin break; 2003e47fe318SCorvin Köhne case PCIBAR_ROM: 2004e47fe318SCorvin Köhne /* skip (un-)register of ROM if it disabled */ 2005e47fe318SCorvin Köhne if (!romen(pi)) 2006e47fe318SCorvin Köhne break; 2007e47fe318SCorvin Köhne /* fallthrough */ 200856282675SJohn Baldwin case PCIBAR_MEM32: 200956282675SJohn Baldwin case PCIBAR_MEM64: 201056282675SJohn Baldwin /* MMIO address space decoding changed? */ 201156282675SJohn Baldwin if (changed & PCIM_CMD_MEMEN) { 201256282675SJohn Baldwin if (new & PCIM_CMD_MEMEN) 201356282675SJohn Baldwin register_bar(pi, i); 201456282675SJohn Baldwin else 201556282675SJohn Baldwin unregister_bar(pi, i); 201656282675SJohn Baldwin } 201756282675SJohn Baldwin break; 201856282675SJohn Baldwin default: 201956282675SJohn Baldwin assert(0); 202056282675SJohn Baldwin } 202156282675SJohn Baldwin } 202256282675SJohn Baldwin 202356282675SJohn Baldwin /* 202456282675SJohn Baldwin * If INTx has been unmasked and is pending, assert the 202556282675SJohn Baldwin * interrupt. 202656282675SJohn Baldwin */ 202756282675SJohn Baldwin pci_lintr_update(pi); 202856282675SJohn Baldwin } 202956282675SJohn Baldwin 2030028d9311SNeel Natu static void 203154335630SNeel Natu pci_emul_cmdsts_write(struct pci_devinst *pi, int coff, uint32_t new, int bytes) 2032028d9311SNeel Natu { 203356282675SJohn Baldwin int rshift; 203456282675SJohn Baldwin uint32_t cmd, old, readonly; 203554335630SNeel Natu 203654335630SNeel Natu cmd = pci_get_cfgdata16(pi, PCIR_COMMAND); /* stash old value */ 2037028d9311SNeel Natu 2038028d9311SNeel Natu /* 203954335630SNeel Natu * From PCI Local Bus Specification 3.0 sections 6.2.2 and 6.2.3. 204054335630SNeel Natu * 204154335630SNeel Natu * XXX Bits 8, 11, 12, 13, 14 and 15 in the status register are 204254335630SNeel Natu * 'write 1 to clear'. However these bits are not set to '1' by 204354335630SNeel Natu * any device emulation so it is simpler to treat them as readonly. 2044028d9311SNeel Natu */ 204554335630SNeel Natu rshift = (coff & 0x3) * 8; 204654335630SNeel Natu readonly = 0xFFFFF880 >> rshift; 2047028d9311SNeel Natu 204854335630SNeel Natu old = CFGREAD(pi, coff, bytes); 204954335630SNeel Natu new &= ~readonly; 205054335630SNeel Natu new |= (old & readonly); 205154335630SNeel Natu CFGWRITE(pi, coff, new, bytes); /* update config */ 205254335630SNeel Natu 205356282675SJohn Baldwin pci_emul_cmd_changed(pi, cmd); 2054028d9311SNeel Natu } 2055028d9311SNeel Natu 205612a6eb99SNeel Natu static void 20576a284cacSJohn Baldwin pci_cfgrw(int in, int bus, int slot, int func, int coff, int bytes, 20586a284cacSJohn Baldwin uint32_t *eax) 2059366f6083SPeter Grehan { 2060d84882caSNeel Natu struct businfo *bi; 2061d84882caSNeel Natu struct slotinfo *si; 2062366f6083SPeter Grehan struct pci_devinst *pi; 2063366f6083SPeter Grehan struct pci_devemu *pe; 206412a6eb99SNeel Natu int idx, needcfg; 2065028d9311SNeel Natu uint64_t addr, bar, mask; 2066366f6083SPeter Grehan 206712a6eb99SNeel Natu if ((bi = pci_businfo[bus]) != NULL) { 206812a6eb99SNeel Natu si = &bi->slotinfo[slot]; 206912a6eb99SNeel Natu pi = si->si_funcs[func].fi_devi; 2070d84882caSNeel Natu } else 207190415e0bSNeel Natu pi = NULL; 207290415e0bSNeel Natu 207399d65389SNeel Natu /* 207412a6eb99SNeel Natu * Just return if there is no device at this slot:func or if the 207512a6eb99SNeel Natu * the guest is doing an un-aligned access. 207699d65389SNeel Natu */ 207712a6eb99SNeel Natu if (pi == NULL || (bytes != 1 && bytes != 2 && bytes != 4) || 207812a6eb99SNeel Natu (coff & (bytes - 1)) != 0) { 2079366f6083SPeter Grehan if (in) 2080366f6083SPeter Grehan *eax = 0xffffffff; 208112a6eb99SNeel Natu return; 208212a6eb99SNeel Natu } 208312a6eb99SNeel Natu 208412a6eb99SNeel Natu /* 208512a6eb99SNeel Natu * Ignore all writes beyond the standard config space and return all 208612a6eb99SNeel Natu * ones on reads. 208712a6eb99SNeel Natu */ 208812a6eb99SNeel Natu if (coff >= PCI_REGMAX + 1) { 208912a6eb99SNeel Natu if (in) { 209012a6eb99SNeel Natu *eax = 0xffffffff; 209112a6eb99SNeel Natu /* 209212a6eb99SNeel Natu * Extended capabilities begin at offset 256 in config 209312a6eb99SNeel Natu * space. Absence of extended capabilities is signaled 209412a6eb99SNeel Natu * with all 0s in the extended capability header at 209512a6eb99SNeel Natu * offset 256. 209612a6eb99SNeel Natu */ 209712a6eb99SNeel Natu if (coff <= PCI_REGMAX + 4) 209812a6eb99SNeel Natu *eax = 0x00000000; 209912a6eb99SNeel Natu } 210012a6eb99SNeel Natu return; 2101366f6083SPeter Grehan } 2102366f6083SPeter Grehan 2103366f6083SPeter Grehan pe = pi->pi_d; 2104366f6083SPeter Grehan 2105366f6083SPeter Grehan /* 2106366f6083SPeter Grehan * Config read 2107366f6083SPeter Grehan */ 2108366f6083SPeter Grehan if (in) { 2109366f6083SPeter Grehan /* Let the device emulation override the default handler */ 211099d65389SNeel Natu if (pe->pe_cfgread != NULL) { 21116a284cacSJohn Baldwin needcfg = pe->pe_cfgread(pi, coff, bytes, eax); 211299d65389SNeel Natu } else { 211399d65389SNeel Natu needcfg = 1; 211499d65389SNeel Natu } 2115366f6083SPeter Grehan 211654335630SNeel Natu if (needcfg) 211754335630SNeel Natu *eax = CFGREAD(pi, coff, bytes); 211899d65389SNeel Natu 211912a6eb99SNeel Natu pci_emul_hdrtype_fixup(bus, slot, coff, bytes, eax); 2120366f6083SPeter Grehan } else { 2121366f6083SPeter Grehan /* Let the device emulation override the default handler */ 2122366f6083SPeter Grehan if (pe->pe_cfgwrite != NULL && 21236a284cacSJohn Baldwin (*pe->pe_cfgwrite)(pi, coff, bytes, *eax) == 0) 212412a6eb99SNeel Natu return; 2125366f6083SPeter Grehan 2126366f6083SPeter Grehan /* 2127e47fe318SCorvin Köhne * Special handling for write to BAR and ROM registers 2128366f6083SPeter Grehan */ 212945ddbf21SCorvin Köhne if (is_pcir_bar(coff) || is_pcir_bios(coff)) { 2130366f6083SPeter Grehan /* 2131366f6083SPeter Grehan * Ignore writes to BAR registers that are not 2132366f6083SPeter Grehan * 4-byte aligned. 2133366f6083SPeter Grehan */ 2134366f6083SPeter Grehan if (bytes != 4 || (coff & 0x3) != 0) 213512a6eb99SNeel Natu return; 213645ddbf21SCorvin Köhne 213745ddbf21SCorvin Köhne if (is_pcir_bar(coff)) { 2138366f6083SPeter Grehan idx = (coff - PCIR_BAR(0)) / 4; 213945ddbf21SCorvin Köhne } else if (is_pcir_bios(coff)) { 2140e47fe318SCorvin Köhne idx = PCI_ROM_IDX; 214145ddbf21SCorvin Köhne } else { 214245ddbf21SCorvin Köhne errx(4, "%s: invalid BAR offset %d", __func__, 214345ddbf21SCorvin Köhne coff); 2144e47fe318SCorvin Köhne } 214545ddbf21SCorvin Köhne 2146028d9311SNeel Natu mask = ~(pi->pi_bar[idx].size - 1); 2147366f6083SPeter Grehan switch (pi->pi_bar[idx].type) { 2148366f6083SPeter Grehan case PCIBAR_NONE: 2149028d9311SNeel Natu pi->pi_bar[idx].addr = bar = 0; 2150366f6083SPeter Grehan break; 2151366f6083SPeter Grehan case PCIBAR_IO: 2152028d9311SNeel Natu addr = *eax & mask; 2153028d9311SNeel Natu addr &= 0xffff; 2154e87a6f3eSCorvin Köhne bar = addr | pi->pi_bar[idx].lobits; 2155028d9311SNeel Natu /* 2156028d9311SNeel Natu * Register the new BAR value for interception 2157028d9311SNeel Natu */ 2158028d9311SNeel Natu if (addr != pi->pi_bar[idx].addr) { 2159028d9311SNeel Natu update_bar_address(pi, addr, idx, 2160028d9311SNeel Natu PCIBAR_IO); 2161028d9311SNeel Natu } 2162366f6083SPeter Grehan break; 2163366f6083SPeter Grehan case PCIBAR_MEM32: 2164028d9311SNeel Natu addr = bar = *eax & mask; 2165e87a6f3eSCorvin Köhne bar |= pi->pi_bar[idx].lobits; 2166028d9311SNeel Natu if (addr != pi->pi_bar[idx].addr) { 2167028d9311SNeel Natu update_bar_address(pi, addr, idx, 2168028d9311SNeel Natu PCIBAR_MEM32); 2169028d9311SNeel Natu } 2170366f6083SPeter Grehan break; 2171366f6083SPeter Grehan case PCIBAR_MEM64: 2172028d9311SNeel Natu addr = bar = *eax & mask; 2173e87a6f3eSCorvin Köhne bar |= pi->pi_bar[idx].lobits; 2174028d9311SNeel Natu if (addr != (uint32_t)pi->pi_bar[idx].addr) { 2175028d9311SNeel Natu update_bar_address(pi, addr, idx, 2176028d9311SNeel Natu PCIBAR_MEM64); 2177028d9311SNeel Natu } 2178366f6083SPeter Grehan break; 2179366f6083SPeter Grehan case PCIBAR_MEMHI64: 2180366f6083SPeter Grehan mask = ~(pi->pi_bar[idx - 1].size - 1); 2181028d9311SNeel Natu addr = ((uint64_t)*eax << 32) & mask; 2182028d9311SNeel Natu bar = addr >> 32; 2183028d9311SNeel Natu if (bar != pi->pi_bar[idx - 1].addr >> 32) { 2184028d9311SNeel Natu update_bar_address(pi, addr, idx - 1, 2185028d9311SNeel Natu PCIBAR_MEMHI64); 2186028d9311SNeel Natu } 2187366f6083SPeter Grehan break; 2188e47fe318SCorvin Köhne case PCIBAR_ROM: 2189e47fe318SCorvin Köhne addr = bar = *eax & mask; 2190e47fe318SCorvin Köhne if (memen(pi) && romen(pi)) { 2191e47fe318SCorvin Köhne unregister_bar(pi, idx); 2192e47fe318SCorvin Köhne } 2193e47fe318SCorvin Köhne pi->pi_bar[idx].addr = addr; 2194e47fe318SCorvin Köhne pi->pi_bar[idx].lobits = *eax & 2195e47fe318SCorvin Köhne PCIM_BIOS_ENABLE; 2196e47fe318SCorvin Köhne /* romen could have changed it value */ 2197e47fe318SCorvin Köhne if (memen(pi) && romen(pi)) { 2198e47fe318SCorvin Köhne register_bar(pi, idx); 2199e47fe318SCorvin Köhne } 2200e47fe318SCorvin Köhne bar |= pi->pi_bar[idx].lobits; 2201e47fe318SCorvin Köhne break; 2202366f6083SPeter Grehan default: 2203366f6083SPeter Grehan assert(0); 2204366f6083SPeter Grehan } 2205366f6083SPeter Grehan pci_set_cfgdata32(pi, coff, bar); 2206cd942e0fSPeter Grehan 2207366f6083SPeter Grehan } else if (pci_emul_iscap(pi, coff)) { 220821368498SPeter Grehan pci_emul_capwrite(pi, coff, bytes, *eax, 0, 0); 220954335630SNeel Natu } else if (coff >= PCIR_COMMAND && coff < PCIR_REVID) { 221054335630SNeel Natu pci_emul_cmdsts_write(pi, coff, *eax, bytes); 2211366f6083SPeter Grehan } else { 2212366f6083SPeter Grehan CFGWRITE(pi, coff, *eax, bytes); 2213366f6083SPeter Grehan } 2214366f6083SPeter Grehan } 221512a6eb99SNeel Natu } 2216366f6083SPeter Grehan 221712a6eb99SNeel Natu static int cfgenable, cfgbus, cfgslot, cfgfunc, cfgoff; 221812a6eb99SNeel Natu 221912a6eb99SNeel Natu static int 222008b05de1SJohn Baldwin pci_emul_cfgaddr(struct vmctx *ctx __unused, int in, 222198d920d9SMark Johnston int port __unused, int bytes, uint32_t *eax, void *arg __unused) 222212a6eb99SNeel Natu { 222312a6eb99SNeel Natu uint32_t x; 222412a6eb99SNeel Natu 222512a6eb99SNeel Natu if (bytes != 4) { 222612a6eb99SNeel Natu if (in) 222712a6eb99SNeel Natu *eax = (bytes == 2) ? 0xffff : 0xff; 222812a6eb99SNeel Natu return (0); 222912a6eb99SNeel Natu } 223012a6eb99SNeel Natu 223112a6eb99SNeel Natu if (in) { 223212a6eb99SNeel Natu x = (cfgbus << 16) | (cfgslot << 11) | (cfgfunc << 8) | cfgoff; 223312a6eb99SNeel Natu if (cfgenable) 223412a6eb99SNeel Natu x |= CONF1_ENABLE; 223512a6eb99SNeel Natu *eax = x; 223612a6eb99SNeel Natu } else { 223712a6eb99SNeel Natu x = *eax; 223812a6eb99SNeel Natu cfgenable = (x & CONF1_ENABLE) == CONF1_ENABLE; 22391b0e2f0bSCorvin Köhne cfgoff = (x & PCI_REGMAX) & ~0x03; 224012a6eb99SNeel Natu cfgfunc = (x >> 8) & PCI_FUNCMAX; 224112a6eb99SNeel Natu cfgslot = (x >> 11) & PCI_SLOTMAX; 224212a6eb99SNeel Natu cfgbus = (x >> 16) & PCI_BUSMAX; 224312a6eb99SNeel Natu } 224412a6eb99SNeel Natu 224512a6eb99SNeel Natu return (0); 224612a6eb99SNeel Natu } 224712a6eb99SNeel Natu INOUT_PORT(pci_cfgaddr, CONF1_ADDR_PORT, IOPORT_F_INOUT, pci_emul_cfgaddr); 224812a6eb99SNeel Natu 224912a6eb99SNeel Natu static int 22506a284cacSJohn Baldwin pci_emul_cfgdata(struct vmctx *ctx __unused, int in, int port, 225178c2cd83SJohn Baldwin int bytes, uint32_t *eax, void *arg __unused) 225212a6eb99SNeel Natu { 225312a6eb99SNeel Natu int coff; 225412a6eb99SNeel Natu 225512a6eb99SNeel Natu assert(bytes == 1 || bytes == 2 || bytes == 4); 225612a6eb99SNeel Natu 225712a6eb99SNeel Natu coff = cfgoff + (port - CONF1_DATA_PORT); 225812a6eb99SNeel Natu if (cfgenable) { 22596a284cacSJohn Baldwin pci_cfgrw(in, cfgbus, cfgslot, cfgfunc, coff, bytes, eax); 226012a6eb99SNeel Natu } else { 226112a6eb99SNeel Natu /* Ignore accesses to cfgdata if not enabled by cfgaddr */ 226212a6eb99SNeel Natu if (in) 226312a6eb99SNeel Natu *eax = 0xffffffff; 226412a6eb99SNeel Natu } 2265366f6083SPeter Grehan return (0); 2266366f6083SPeter Grehan } 2267366f6083SPeter Grehan 2268366f6083SPeter Grehan INOUT_PORT(pci_cfgdata, CONF1_DATA_PORT+0, IOPORT_F_INOUT, pci_emul_cfgdata); 2269366f6083SPeter Grehan INOUT_PORT(pci_cfgdata, CONF1_DATA_PORT+1, IOPORT_F_INOUT, pci_emul_cfgdata); 2270366f6083SPeter Grehan INOUT_PORT(pci_cfgdata, CONF1_DATA_PORT+2, IOPORT_F_INOUT, pci_emul_cfgdata); 2271366f6083SPeter Grehan INOUT_PORT(pci_cfgdata, CONF1_DATA_PORT+3, IOPORT_F_INOUT, pci_emul_cfgdata); 2272366f6083SPeter Grehan 2273483d953aSJohn Baldwin #ifdef BHYVE_SNAPSHOT 2274483d953aSJohn Baldwin /* 2275483d953aSJohn Baldwin * Saves/restores PCI device emulated state. Returns 0 on success. 2276483d953aSJohn Baldwin */ 2277483d953aSJohn Baldwin static int 2278483d953aSJohn Baldwin pci_snapshot_pci_dev(struct vm_snapshot_meta *meta) 2279483d953aSJohn Baldwin { 2280483d953aSJohn Baldwin struct pci_devinst *pi; 2281483d953aSJohn Baldwin int i; 2282483d953aSJohn Baldwin int ret; 2283483d953aSJohn Baldwin 2284483d953aSJohn Baldwin pi = meta->dev_data; 2285483d953aSJohn Baldwin 2286483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(pi->pi_msi.enabled, meta, ret, done); 2287483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(pi->pi_msi.addr, meta, ret, done); 2288483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(pi->pi_msi.msg_data, meta, ret, done); 2289483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(pi->pi_msi.maxmsgnum, meta, ret, done); 2290483d953aSJohn Baldwin 2291483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(pi->pi_msix.enabled, meta, ret, done); 2292483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(pi->pi_msix.table_bar, meta, ret, done); 2293483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(pi->pi_msix.pba_bar, meta, ret, done); 2294483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(pi->pi_msix.table_offset, meta, ret, done); 2295483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(pi->pi_msix.table_count, meta, ret, done); 2296483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(pi->pi_msix.pba_offset, meta, ret, done); 2297483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(pi->pi_msix.pba_size, meta, ret, done); 2298483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(pi->pi_msix.function_mask, meta, ret, done); 2299483d953aSJohn Baldwin 2300483d953aSJohn Baldwin SNAPSHOT_BUF_OR_LEAVE(pi->pi_cfgdata, sizeof(pi->pi_cfgdata), 2301483d953aSJohn Baldwin meta, ret, done); 2302483d953aSJohn Baldwin 2303ed721684SMark Johnston for (i = 0; i < (int)nitems(pi->pi_bar); i++) { 2304483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(pi->pi_bar[i].type, meta, ret, done); 2305483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(pi->pi_bar[i].size, meta, ret, done); 2306483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(pi->pi_bar[i].addr, meta, ret, done); 2307483d953aSJohn Baldwin } 2308483d953aSJohn Baldwin 2309483d953aSJohn Baldwin /* Restore MSI-X table. */ 2310483d953aSJohn Baldwin for (i = 0; i < pi->pi_msix.table_count; i++) { 2311483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(pi->pi_msix.table[i].addr, 2312483d953aSJohn Baldwin meta, ret, done); 2313483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(pi->pi_msix.table[i].msg_data, 2314483d953aSJohn Baldwin meta, ret, done); 2315483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(pi->pi_msix.table[i].vector_control, 2316483d953aSJohn Baldwin meta, ret, done); 2317483d953aSJohn Baldwin } 2318483d953aSJohn Baldwin 2319483d953aSJohn Baldwin done: 2320483d953aSJohn Baldwin return (ret); 2321483d953aSJohn Baldwin } 2322483d953aSJohn Baldwin 2323483d953aSJohn Baldwin static int 2324483d953aSJohn Baldwin pci_find_slotted_dev(const char *dev_name, struct pci_devemu **pde, 2325483d953aSJohn Baldwin struct pci_devinst **pdi) 2326483d953aSJohn Baldwin { 2327483d953aSJohn Baldwin struct businfo *bi; 2328483d953aSJohn Baldwin struct slotinfo *si; 2329483d953aSJohn Baldwin struct funcinfo *fi; 2330483d953aSJohn Baldwin int bus, slot, func; 2331483d953aSJohn Baldwin 2332483d953aSJohn Baldwin assert(dev_name != NULL); 2333483d953aSJohn Baldwin assert(pde != NULL); 2334483d953aSJohn Baldwin assert(pdi != NULL); 2335483d953aSJohn Baldwin 2336483d953aSJohn Baldwin for (bus = 0; bus < MAXBUSES; bus++) { 2337483d953aSJohn Baldwin if ((bi = pci_businfo[bus]) == NULL) 2338483d953aSJohn Baldwin continue; 2339483d953aSJohn Baldwin 2340483d953aSJohn Baldwin for (slot = 0; slot < MAXSLOTS; slot++) { 2341483d953aSJohn Baldwin si = &bi->slotinfo[slot]; 2342483d953aSJohn Baldwin for (func = 0; func < MAXFUNCS; func++) { 2343483d953aSJohn Baldwin fi = &si->si_funcs[func]; 2344621b5090SJohn Baldwin if (fi->fi_pde == NULL) 2345483d953aSJohn Baldwin continue; 2346621b5090SJohn Baldwin if (strcmp(dev_name, fi->fi_pde->pe_emu) != 0) 2347483d953aSJohn Baldwin continue; 2348483d953aSJohn Baldwin 2349621b5090SJohn Baldwin *pde = fi->fi_pde; 2350483d953aSJohn Baldwin *pdi = fi->fi_devi; 2351483d953aSJohn Baldwin return (0); 2352483d953aSJohn Baldwin } 2353483d953aSJohn Baldwin } 2354483d953aSJohn Baldwin } 2355483d953aSJohn Baldwin 2356483d953aSJohn Baldwin return (EINVAL); 2357483d953aSJohn Baldwin } 2358483d953aSJohn Baldwin 2359483d953aSJohn Baldwin int 2360483d953aSJohn Baldwin pci_snapshot(struct vm_snapshot_meta *meta) 2361483d953aSJohn Baldwin { 2362483d953aSJohn Baldwin struct pci_devemu *pde; 2363483d953aSJohn Baldwin struct pci_devinst *pdi; 2364483d953aSJohn Baldwin int ret; 2365483d953aSJohn Baldwin 2366483d953aSJohn Baldwin assert(meta->dev_name != NULL); 2367483d953aSJohn Baldwin 2368483d953aSJohn Baldwin ret = pci_find_slotted_dev(meta->dev_name, &pde, &pdi); 2369483d953aSJohn Baldwin if (ret != 0) { 2370483d953aSJohn Baldwin fprintf(stderr, "%s: no such name: %s\r\n", 2371483d953aSJohn Baldwin __func__, meta->dev_name); 2372483d953aSJohn Baldwin memset(meta->buffer.buf_start, 0, meta->buffer.buf_size); 2373483d953aSJohn Baldwin return (0); 2374483d953aSJohn Baldwin } 2375483d953aSJohn Baldwin 2376483d953aSJohn Baldwin meta->dev_data = pdi; 2377483d953aSJohn Baldwin 2378483d953aSJohn Baldwin if (pde->pe_snapshot == NULL) { 2379483d953aSJohn Baldwin fprintf(stderr, "%s: not implemented yet for: %s\r\n", 2380483d953aSJohn Baldwin __func__, meta->dev_name); 2381483d953aSJohn Baldwin return (-1); 2382483d953aSJohn Baldwin } 2383483d953aSJohn Baldwin 2384483d953aSJohn Baldwin ret = pci_snapshot_pci_dev(meta); 2385483d953aSJohn Baldwin if (ret != 0) { 2386483d953aSJohn Baldwin fprintf(stderr, "%s: failed to snapshot pci dev\r\n", 2387483d953aSJohn Baldwin __func__); 2388483d953aSJohn Baldwin return (-1); 2389483d953aSJohn Baldwin } 2390483d953aSJohn Baldwin 2391483d953aSJohn Baldwin ret = (*pde->pe_snapshot)(meta); 2392483d953aSJohn Baldwin 2393483d953aSJohn Baldwin return (ret); 2394483d953aSJohn Baldwin } 2395483d953aSJohn Baldwin 2396483d953aSJohn Baldwin int 23976a284cacSJohn Baldwin pci_pause(const char *dev_name) 2398483d953aSJohn Baldwin { 2399483d953aSJohn Baldwin struct pci_devemu *pde; 2400483d953aSJohn Baldwin struct pci_devinst *pdi; 2401483d953aSJohn Baldwin int ret; 2402483d953aSJohn Baldwin 2403483d953aSJohn Baldwin assert(dev_name != NULL); 2404483d953aSJohn Baldwin 2405483d953aSJohn Baldwin ret = pci_find_slotted_dev(dev_name, &pde, &pdi); 2406483d953aSJohn Baldwin if (ret != 0) { 2407483d953aSJohn Baldwin /* 2408483d953aSJohn Baldwin * It is possible to call this function without 2409483d953aSJohn Baldwin * checking that the device is inserted first. 2410483d953aSJohn Baldwin */ 2411483d953aSJohn Baldwin fprintf(stderr, "%s: no such name: %s\n", __func__, dev_name); 2412483d953aSJohn Baldwin return (0); 2413483d953aSJohn Baldwin } 2414483d953aSJohn Baldwin 2415483d953aSJohn Baldwin if (pde->pe_pause == NULL) { 2416483d953aSJohn Baldwin /* The pause/resume functionality is optional. */ 2417483d953aSJohn Baldwin fprintf(stderr, "%s: not implemented for: %s\n", 2418483d953aSJohn Baldwin __func__, dev_name); 2419483d953aSJohn Baldwin return (0); 2420483d953aSJohn Baldwin } 2421483d953aSJohn Baldwin 24226a284cacSJohn Baldwin return (*pde->pe_pause)(pdi); 2423483d953aSJohn Baldwin } 2424483d953aSJohn Baldwin 2425483d953aSJohn Baldwin int 24266a284cacSJohn Baldwin pci_resume(const char *dev_name) 2427483d953aSJohn Baldwin { 2428483d953aSJohn Baldwin struct pci_devemu *pde; 2429483d953aSJohn Baldwin struct pci_devinst *pdi; 2430483d953aSJohn Baldwin int ret; 2431483d953aSJohn Baldwin 2432483d953aSJohn Baldwin assert(dev_name != NULL); 2433483d953aSJohn Baldwin 2434483d953aSJohn Baldwin ret = pci_find_slotted_dev(dev_name, &pde, &pdi); 2435483d953aSJohn Baldwin if (ret != 0) { 2436483d953aSJohn Baldwin /* 2437483d953aSJohn Baldwin * It is possible to call this function without 2438483d953aSJohn Baldwin * checking that the device is inserted first. 2439483d953aSJohn Baldwin */ 2440483d953aSJohn Baldwin fprintf(stderr, "%s: no such name: %s\n", __func__, dev_name); 2441483d953aSJohn Baldwin return (0); 2442483d953aSJohn Baldwin } 2443483d953aSJohn Baldwin 2444483d953aSJohn Baldwin if (pde->pe_resume == NULL) { 2445483d953aSJohn Baldwin /* The pause/resume functionality is optional. */ 2446483d953aSJohn Baldwin fprintf(stderr, "%s: not implemented for: %s\n", 2447483d953aSJohn Baldwin __func__, dev_name); 2448483d953aSJohn Baldwin return (0); 2449483d953aSJohn Baldwin } 2450483d953aSJohn Baldwin 24516a284cacSJohn Baldwin return (*pde->pe_resume)(pdi); 2452483d953aSJohn Baldwin } 2453483d953aSJohn Baldwin #endif 2454483d953aSJohn Baldwin 2455366f6083SPeter Grehan #define PCI_EMUL_TEST 2456366f6083SPeter Grehan #ifdef PCI_EMUL_TEST 2457366f6083SPeter Grehan /* 2458366f6083SPeter Grehan * Define a dummy test device 2459366f6083SPeter Grehan */ 2460d84882caSNeel Natu #define DIOSZ 8 24614d1e669cSPeter Grehan #define DMEMSZ 4096 2462366f6083SPeter Grehan struct pci_emul_dsoftc { 24634d1e669cSPeter Grehan uint8_t ioregs[DIOSZ]; 2464fd4e0d4cSNeel Natu uint8_t memregs[2][DMEMSZ]; 2465366f6083SPeter Grehan }; 2466366f6083SPeter Grehan 24674d1e669cSPeter Grehan #define PCI_EMUL_MSI_MSGS 4 24684d1e669cSPeter Grehan #define PCI_EMUL_MSIX_MSGS 16 2469366f6083SPeter Grehan 2470b67e81dbSJohn Baldwin static int 24716a284cacSJohn Baldwin pci_emul_dinit(struct pci_devinst *pi, nvlist_t *nvl __unused) 2472366f6083SPeter Grehan { 2473366f6083SPeter Grehan int error; 2474366f6083SPeter Grehan struct pci_emul_dsoftc *sc; 2475366f6083SPeter Grehan 2476994f858aSXin LI sc = calloc(1, sizeof(struct pci_emul_dsoftc)); 2477366f6083SPeter Grehan 2478366f6083SPeter Grehan pi->pi_arg = sc; 2479366f6083SPeter Grehan 2480366f6083SPeter Grehan pci_set_cfgdata16(pi, PCIR_DEVICE, 0x0001); 2481366f6083SPeter Grehan pci_set_cfgdata16(pi, PCIR_VENDOR, 0x10DD); 2482366f6083SPeter Grehan pci_set_cfgdata8(pi, PCIR_CLASS, 0x02); 2483366f6083SPeter Grehan 24844d1e669cSPeter Grehan error = pci_emul_add_msicap(pi, PCI_EMUL_MSI_MSGS); 2485366f6083SPeter Grehan assert(error == 0); 2486366f6083SPeter Grehan 24874d1e669cSPeter Grehan error = pci_emul_alloc_bar(pi, 0, PCIBAR_IO, DIOSZ); 24884d1e669cSPeter Grehan assert(error == 0); 24894d1e669cSPeter Grehan 24904d1e669cSPeter Grehan error = pci_emul_alloc_bar(pi, 1, PCIBAR_MEM32, DMEMSZ); 2491366f6083SPeter Grehan assert(error == 0); 2492366f6083SPeter Grehan 2493fd4e0d4cSNeel Natu error = pci_emul_alloc_bar(pi, 2, PCIBAR_MEM32, DMEMSZ); 2494fd4e0d4cSNeel Natu assert(error == 0); 2495fd4e0d4cSNeel Natu 2496366f6083SPeter Grehan return (0); 2497366f6083SPeter Grehan } 2498366f6083SPeter Grehan 2499b67e81dbSJohn Baldwin static void 25006a284cacSJohn Baldwin pci_emul_diow(struct pci_devinst *pi, int baridx, uint64_t offset, int size, 250198d920d9SMark Johnston uint64_t value) 2502366f6083SPeter Grehan { 2503366f6083SPeter Grehan int i; 2504366f6083SPeter Grehan struct pci_emul_dsoftc *sc = pi->pi_arg; 2505366f6083SPeter Grehan 25064d1e669cSPeter Grehan if (baridx == 0) { 25074d1e669cSPeter Grehan if (offset + size > DIOSZ) { 25084d1e669cSPeter Grehan printf("diow: iow too large, offset %ld size %d\n", 25094d1e669cSPeter Grehan offset, size); 2510366f6083SPeter Grehan return; 2511366f6083SPeter Grehan } 2512366f6083SPeter Grehan 2513366f6083SPeter Grehan if (size == 1) { 25144d1e669cSPeter Grehan sc->ioregs[offset] = value & 0xff; 2515366f6083SPeter Grehan } else if (size == 2) { 25164d1e669cSPeter Grehan *(uint16_t *)&sc->ioregs[offset] = value & 0xffff; 25174d1e669cSPeter Grehan } else if (size == 4) { 25184d1e669cSPeter Grehan *(uint32_t *)&sc->ioregs[offset] = value; 2519366f6083SPeter Grehan } else { 25204d1e669cSPeter Grehan printf("diow: iow unknown size %d\n", size); 2521366f6083SPeter Grehan } 2522366f6083SPeter Grehan 2523366f6083SPeter Grehan /* 2524366f6083SPeter Grehan * Special magic value to generate an interrupt 2525366f6083SPeter Grehan */ 2526366f6083SPeter Grehan if (offset == 4 && size == 4 && pci_msi_enabled(pi)) 25274f8be175SNeel Natu pci_generate_msi(pi, value % pci_msi_maxmsgnum(pi)); 2528366f6083SPeter Grehan 2529366f6083SPeter Grehan if (value == 0xabcdef) { 25304f8be175SNeel Natu for (i = 0; i < pci_msi_maxmsgnum(pi); i++) 2531366f6083SPeter Grehan pci_generate_msi(pi, i); 2532366f6083SPeter Grehan } 2533366f6083SPeter Grehan } 2534366f6083SPeter Grehan 2535fd4e0d4cSNeel Natu if (baridx == 1 || baridx == 2) { 25364d1e669cSPeter Grehan if (offset + size > DMEMSZ) { 25374d1e669cSPeter Grehan printf("diow: memw too large, offset %ld size %d\n", 25384d1e669cSPeter Grehan offset, size); 25394d1e669cSPeter Grehan return; 25404d1e669cSPeter Grehan } 25414d1e669cSPeter Grehan 2542fd4e0d4cSNeel Natu i = baridx - 1; /* 'memregs' index */ 2543fd4e0d4cSNeel Natu 25444d1e669cSPeter Grehan if (size == 1) { 2545fd4e0d4cSNeel Natu sc->memregs[i][offset] = value; 25464d1e669cSPeter Grehan } else if (size == 2) { 2547fd4e0d4cSNeel Natu *(uint16_t *)&sc->memregs[i][offset] = value; 25484d1e669cSPeter Grehan } else if (size == 4) { 2549fd4e0d4cSNeel Natu *(uint32_t *)&sc->memregs[i][offset] = value; 25504d1e669cSPeter Grehan } else if (size == 8) { 2551fd4e0d4cSNeel Natu *(uint64_t *)&sc->memregs[i][offset] = value; 25524d1e669cSPeter Grehan } else { 25534d1e669cSPeter Grehan printf("diow: memw unknown size %d\n", size); 25544d1e669cSPeter Grehan } 25554d1e669cSPeter Grehan 25564d1e669cSPeter Grehan /* 25574d1e669cSPeter Grehan * magic interrupt ?? 25584d1e669cSPeter Grehan */ 25594d1e669cSPeter Grehan } 25604d1e669cSPeter Grehan 25619f3dba68SPedro F. Giffuni if (baridx > 2 || baridx < 0) { 25624d1e669cSPeter Grehan printf("diow: unknown bar idx %d\n", baridx); 25634d1e669cSPeter Grehan } 25644d1e669cSPeter Grehan } 25654d1e669cSPeter Grehan 25664d1e669cSPeter Grehan static uint64_t 25676a284cacSJohn Baldwin pci_emul_dior(struct pci_devinst *pi, int baridx, uint64_t offset, int size) 2568366f6083SPeter Grehan { 2569366f6083SPeter Grehan struct pci_emul_dsoftc *sc = pi->pi_arg; 2570366f6083SPeter Grehan uint32_t value; 2571fd4e0d4cSNeel Natu int i; 2572366f6083SPeter Grehan 25734d1e669cSPeter Grehan if (baridx == 0) { 25744d1e669cSPeter Grehan if (offset + size > DIOSZ) { 25754d1e669cSPeter Grehan printf("dior: ior too large, offset %ld size %d\n", 25764d1e669cSPeter Grehan offset, size); 2577366f6083SPeter Grehan return (0); 2578366f6083SPeter Grehan } 2579366f6083SPeter Grehan 25806e43f3edSPedro F. Giffuni value = 0; 2581366f6083SPeter Grehan if (size == 1) { 25824d1e669cSPeter Grehan value = sc->ioregs[offset]; 2583366f6083SPeter Grehan } else if (size == 2) { 25844d1e669cSPeter Grehan value = *(uint16_t *) &sc->ioregs[offset]; 25854d1e669cSPeter Grehan } else if (size == 4) { 25864d1e669cSPeter Grehan value = *(uint32_t *) &sc->ioregs[offset]; 2587366f6083SPeter Grehan } else { 25884d1e669cSPeter Grehan printf("dior: ior unknown size %d\n", size); 25894d1e669cSPeter Grehan } 25904d1e669cSPeter Grehan } 25914d1e669cSPeter Grehan 2592fd4e0d4cSNeel Natu if (baridx == 1 || baridx == 2) { 25934d1e669cSPeter Grehan if (offset + size > DMEMSZ) { 25944d1e669cSPeter Grehan printf("dior: memr too large, offset %ld size %d\n", 25954d1e669cSPeter Grehan offset, size); 25964d1e669cSPeter Grehan return (0); 25974d1e669cSPeter Grehan } 25984d1e669cSPeter Grehan 2599fd4e0d4cSNeel Natu i = baridx - 1; /* 'memregs' index */ 2600fd4e0d4cSNeel Natu 26014d1e669cSPeter Grehan if (size == 1) { 2602fd4e0d4cSNeel Natu value = sc->memregs[i][offset]; 26034d1e669cSPeter Grehan } else if (size == 2) { 2604fd4e0d4cSNeel Natu value = *(uint16_t *) &sc->memregs[i][offset]; 26054d1e669cSPeter Grehan } else if (size == 4) { 2606fd4e0d4cSNeel Natu value = *(uint32_t *) &sc->memregs[i][offset]; 26074d1e669cSPeter Grehan } else if (size == 8) { 2608fd4e0d4cSNeel Natu value = *(uint64_t *) &sc->memregs[i][offset]; 26094d1e669cSPeter Grehan } else { 26104d1e669cSPeter Grehan printf("dior: ior unknown size %d\n", size); 26114d1e669cSPeter Grehan } 26124d1e669cSPeter Grehan } 26134d1e669cSPeter Grehan 26144d1e669cSPeter Grehan 26159f3dba68SPedro F. Giffuni if (baridx > 2 || baridx < 0) { 26164d1e669cSPeter Grehan printf("dior: unknown bar idx %d\n", baridx); 26174d1e669cSPeter Grehan return (0); 2618366f6083SPeter Grehan } 2619366f6083SPeter Grehan 2620366f6083SPeter Grehan return (value); 2621366f6083SPeter Grehan } 2622366f6083SPeter Grehan 2623483d953aSJohn Baldwin #ifdef BHYVE_SNAPSHOT 2624c9faf698SMark Johnston static int 262598d920d9SMark Johnston pci_emul_snapshot(struct vm_snapshot_meta *meta __unused) 2626483d953aSJohn Baldwin { 2627483d953aSJohn Baldwin return (0); 2628483d953aSJohn Baldwin } 2629483d953aSJohn Baldwin #endif 2630483d953aSJohn Baldwin 263137045dfaSMark Johnston static const struct pci_devemu pci_dummy = { 2632366f6083SPeter Grehan .pe_emu = "dummy", 2633366f6083SPeter Grehan .pe_init = pci_emul_dinit, 26344d1e669cSPeter Grehan .pe_barwrite = pci_emul_diow, 2635483d953aSJohn Baldwin .pe_barread = pci_emul_dior, 2636483d953aSJohn Baldwin #ifdef BHYVE_SNAPSHOT 2637483d953aSJohn Baldwin .pe_snapshot = pci_emul_snapshot, 2638483d953aSJohn Baldwin #endif 2639366f6083SPeter Grehan }; 2640366f6083SPeter Grehan PCI_EMUL_SET(pci_dummy); 2641366f6083SPeter Grehan 2642366f6083SPeter Grehan #endif /* PCI_EMUL_TEST */ 2643