It seems that for some PCIe device driver stuff we'll want to read registers in the extended configuration space. On i386/amd64 hardware these registers can be accessed through a memory mapped register window, advertised in the ACPI MCFG table. The diff below adds support for this mechanism. Some notable points:
1. ACPI is a pile of poo. Allegedly there are machines that have an MCFG table that's full of lies. 2. Mapping the registers consumes up to 256 MB of precious KVA. To minimize KVA usage, the code only maps the registers for the busses that are actualy found in the machine. 3. Some devices don't like it when their PCIe extended config space gets read. So pcidump -xxx should be used with care. Anyway, this could use some testing. I'm especially interested on testing on machines with AMD CPUs and older PCIe machines. Index: arch/amd64/conf/GENERIC =================================================================== RCS file: /cvs/src/sys/arch/amd64/conf/GENERIC,v retrieving revision 1.310 diff -u -p -r1.310 GENERIC --- arch/amd64/conf/GENERIC 15 Dec 2010 11:24:29 -0000 1.310 +++ arch/amd64/conf/GENERIC 26 Dec 2010 23:06:40 -0000 @@ -48,6 +48,7 @@ acpiec* at acpi? acpiprt* at acpi? acpitz* at acpi? acpimadt0 at acpi? +acpimcfg0 at acpi? acpiasus* at acpi? acpisony* at acpi? acpithinkpad* at acpi? Index: arch/amd64/include/pci_machdep.h =================================================================== RCS file: /cvs/src/sys/arch/amd64/include/pci_machdep.h,v retrieving revision 1.16 diff -u -p -r1.16 pci_machdep.h --- arch/amd64/include/pci_machdep.h 4 Dec 2010 17:06:31 -0000 1.16 +++ arch/amd64/include/pci_machdep.h 26 Dec 2010 23:06:40 -0000 @@ -59,6 +59,9 @@ typedef struct { * amd64-specific PCI variables and functions. * NOT TO BE USED DIRECTLY BY MACHINE INDEPENDENT CODE. */ +extern bus_addr_t pci_mcfg_addr; +extern int pci_mcfg_min_bus, pci_mcfg_max_bus; + struct pci_attach_args; extern struct extent *pciio_ex; Index: arch/amd64/pci/pci_machdep.c =================================================================== RCS file: /cvs/src/sys/arch/amd64/pci/pci_machdep.c,v retrieving revision 1.37 diff -u -p -r1.37 pci_machdep.c --- arch/amd64/pci/pci_machdep.c 4 Dec 2010 17:06:31 -0000 1.37 +++ arch/amd64/pci/pci_machdep.c 26 Dec 2010 23:06:40 -0000 @@ -96,6 +96,19 @@ #include <machine/mpbiosvar.h> #endif +/* + * Memory Mapped Configuration space access. + * + * Since mapping the whole configuration space will cost us up to + * 256MB of kernel virtual memory, we use seperate mappings per bus. + * The mappings are created on-demand, such that we only use kernel + * virtual memory for busses that are actually present. + */ +bus_addr_t pci_mcfg_addr; +int pci_mcfg_min_bus, pci_mcfg_max_bus; +bus_space_tag_t pci_mcfgt = X86_BUS_SPACE_MEM; +bus_space_handle_t pci_mcfgh[256]; + struct mutex pci_conf_lock = MUTEX_INITIALIZER(IPL_HIGH); #define PCI_CONF_LOCK() \ @@ -157,6 +170,14 @@ pci_make_tag(pci_chipset_tag_t pc, int b if (bus >= 256 || device >= 32 || function >= 8) panic("pci_make_tag: bad request"); + if (pci_mcfg_addr) { + if (bus < pci_mcfg_min_bus || bus > pci_mcfg_max_bus || + device >= 32 || function >= 8) + panic("pci_make_tag: bad request"); + + return (bus << 20) | (device << 15) | (function << 12); + } + return (PCI_MODE1_ENABLE | (bus << 16) | (device << 11) | (function << 8)); } @@ -164,6 +185,16 @@ pci_make_tag(pci_chipset_tag_t pc, int b void pci_decompose_tag(pci_chipset_tag_t pc, pcitag_t tag, int *bp, int *dp, int *fp) { + if (pci_mcfg_addr) { + if (bp != NULL) + *bp = (tag >> 20) & 0xff; + if (dp != NULL) + *dp = (tag >> 15) & 0x1f; + if (fp != NULL) + *fp = (tag >> 12) & 0x7; + return; + } + if (bp != NULL) *bp = (tag >> 16) & 0xff; if (dp != NULL) @@ -175,6 +206,9 @@ pci_decompose_tag(pci_chipset_tag_t pc, int pci_conf_size(pci_chipset_tag_t pc, pcitag_t tag) { + if (pci_mcfg_addr) + return PCIE_CONFIG_SPACE_SIZE; + return PCI_CONFIG_SPACE_SIZE; } @@ -182,6 +216,17 @@ pcireg_t pci_conf_read(pci_chipset_tag_t pc, pcitag_t tag, int reg) { pcireg_t data; + int bus; + + if (pci_mcfg_addr) { + pci_decompose_tag(pc, tag, &bus, NULL, NULL); + if (pci_mcfgh[bus] == 0 && + bus_space_map(pci_mcfgt, pci_mcfg_addr + (bus << 20), + 1 << 20, 0, &pci_mcfgh[bus])) + panic("pci_conf_read: cannot map mcfg space"); + return bus_space_read_4(pci_mcfgt, pci_mcfgh[bus], + (tag & 0x000ff000) | reg); + } PCI_CONF_LOCK(); outl(PCI_MODE1_ADDRESS_REG, tag | reg); @@ -195,6 +240,19 @@ pci_conf_read(pci_chipset_tag_t pc, pcit void pci_conf_write(pci_chipset_tag_t pc, pcitag_t tag, int reg, pcireg_t data) { + int bus; + + if (pci_mcfg_addr) { + pci_decompose_tag(pc, tag, &bus, NULL, NULL); + if (pci_mcfgh[bus] == 0 && + bus_space_map(pci_mcfgt, pci_mcfg_addr + (bus << 20), + 1 << 20, 0, &pci_mcfgh[bus])) + panic("pci_conf_write: cannot map mcfg space"); + bus_space_write_4(pci_mcfgt, pci_mcfgh[bus], + (tag & 0x000ff000) | reg, data); + return; + } + PCI_CONF_LOCK(); outl(PCI_MODE1_ADDRESS_REG, tag | reg); outl(PCI_MODE1_DATA_REG, data); Index: arch/i386/conf/GENERIC =================================================================== RCS file: /cvs/src/sys/arch/i386/conf/GENERIC,v retrieving revision 1.704 diff -u -p -r1.704 GENERIC --- arch/i386/conf/GENERIC 15 Dec 2010 11:24:29 -0000 1.704 +++ arch/i386/conf/GENERIC 26 Dec 2010 23:06:41 -0000 @@ -57,6 +57,7 @@ acpicpu* at acpi? acpidock* at acpi? acpiec* at acpi? acpimadt0 at acpi? +acpimcfg0 at acpi? acpiprt* at acpi? acpitz* at acpi? acpiasus* at acpi? Index: arch/i386/pci/pci_machdep.c =================================================================== RCS file: /cvs/src/sys/arch/i386/pci/pci_machdep.c,v retrieving revision 1.55 diff -u -p -r1.55 pci_machdep.c --- arch/i386/pci/pci_machdep.c 4 Dec 2010 17:06:31 -0000 1.55 +++ arch/i386/pci/pci_machdep.c 26 Dec 2010 23:06:41 -0000 @@ -114,6 +114,19 @@ extern bios_pciinfo_t *bios_pciinfo; int pci_mode = -1; +/* + * Memory Mapped Configuration space access. + * + * Since mapping the whole configuration space will cost us up to + * 256MB of kernel virtual memory, we use seperate mappings per bus. + * The mappings are created on-demand, such that we only use kernel + * virtual memory for busses that are actually present. + */ +bus_addr_t pci_mcfg_addr; +int pci_mcfg_min_bus, pci_mcfg_max_bus; +bus_space_tag_t pci_mcfgt = I386_BUS_SPACE_MEM; +bus_space_handle_t pci_mcfgh[256]; + struct mutex pci_conf_lock = MUTEX_INITIALIZER(IPL_HIGH); #define PCI_CONF_LOCK() \ @@ -211,6 +224,15 @@ pci_make_tag(pci_chipset_tag_t pc, int b { pcitag_t tag; + if (pci_mcfg_addr) { + if (bus < pci_mcfg_min_bus || bus > pci_mcfg_max_bus || + device >= 32 || function >= 8) + panic("pci_make_tag: bad request"); + + tag.mode1 = (bus << 20) | (device << 15) | (function << 12); + return tag; + } + switch (pci_mode) { case 1: if (bus >= 256 || device >= 32 || function >= 8) @@ -237,6 +259,15 @@ pci_make_tag(pci_chipset_tag_t pc, int b void pci_decompose_tag(pci_chipset_tag_t pc, pcitag_t tag, int *bp, int *dp, int *fp) { + if (pci_mcfg_addr) { + if (bp != NULL) + *bp = (tag.mode1 >> 20) & 0xff; + if (dp != NULL) + *dp = (tag.mode1 >> 15) & 0x1f; + if (fp != NULL) + *fp = (tag.mode1 >> 12) & 0x7; + return; + } switch (pci_mode) { case 1: @@ -263,6 +294,9 @@ pci_decompose_tag(pci_chipset_tag_t pc, int pci_conf_size(pci_chipset_tag_t pc, pcitag_t tag) { + if (pci_mcfg_addr) + return PCIE_CONFIG_SPACE_SIZE; + return PCI_CONFIG_SPACE_SIZE; } @@ -270,6 +304,18 @@ pcireg_t pci_conf_read(pci_chipset_tag_t pc, pcitag_t tag, int reg) { pcireg_t data; + int bus; + + if (pci_mcfg_addr) { + pci_decompose_tag(pc, tag, &bus, NULL, NULL); + if (pci_mcfgh[bus] == 0 && + bus_space_map(pci_mcfgt, pci_mcfg_addr + (bus << 20), + 1 << 20, 0, &pci_mcfgh[bus])) + panic("pci_conf_read: cannot map mcfg space"); + data = bus_space_read_4(pci_mcfgt, pci_mcfgh[bus], + (tag.mode1 & 0x000ff000) | reg); + return data; + } PCI_CONF_LOCK(); switch (pci_mode) { @@ -295,6 +341,18 @@ pci_conf_read(pci_chipset_tag_t pc, pcit void pci_conf_write(pci_chipset_tag_t pc, pcitag_t tag, int reg, pcireg_t data) { + int bus; + + if (pci_mcfg_addr) { + pci_decompose_tag(pc, tag, &bus, NULL, NULL); + if (pci_mcfgh[bus] == 0 && + bus_space_map(pci_mcfgt, pci_mcfg_addr + (bus << 20), + 1 << 20, 0, &pci_mcfgh[bus])) + panic("pci_conf_write: cannot map mcfg space"); + bus_space_write_4(pci_mcfgt, pci_mcfgh[bus], + (tag.mode1 & 0x000ff000) | reg, data); + return; + } PCI_CONF_LOCK(); switch (pci_mode) { Index: arch/i386/pci/pci_machdep.h =================================================================== RCS file: /cvs/src/sys/arch/i386/pci/pci_machdep.h,v retrieving revision 1.20 diff -u -p -r1.20 pci_machdep.h --- arch/i386/pci/pci_machdep.h 4 Dec 2010 17:06:31 -0000 1.20 +++ arch/i386/pci/pci_machdep.h 26 Dec 2010 23:06:41 -0000 @@ -75,6 +75,9 @@ struct { * NOT TO BE USED DIRECTLY BY MACHINE INDEPENDENT CODE. */ extern int pci_mode; +extern bus_addr_t pci_mcfg_addr; +extern int pci_mcfg_min_bus, pci_mcfg_max_bus; + int pci_mode_detect(void); extern struct extent *pciio_ex; Index: dev/acpi/acpimcfg.c =================================================================== RCS file: dev/acpi/acpimcfg.c diff -N dev/acpi/acpimcfg.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ dev/acpi/acpimcfg.c 26 Dec 2010 23:06:44 -0000 @@ -0,0 +1,73 @@ +/* $OpenBSD$ */ +/* + * Copyright (c) 2010 Mark Kettenis <kette...@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/device.h> + +#include <machine/apicvar.h> + +#include <dev/acpi/acpireg.h> +#include <dev/acpi/acpivar.h> +#include <dev/pci/pcivar.h> + +int acpimcfg_match(struct device *, void *, void *); +void acpimcfg_attach(struct device *, struct device *, void *); + +struct cfattach acpimcfg_ca = { + sizeof(struct device), acpimcfg_match, acpimcfg_attach +}; + +struct cfdriver acpimcfg_cd = { + NULL, "acpimcfg", DV_DULL +}; + +int +acpimcfg_match(struct device *parent, void *match, void *aux) +{ + struct acpi_attach_args *aaa = aux; + struct acpi_table_header *hdr; + + /* + * If we do not have a table, it is not us + */ + if (aaa->aaa_table == NULL) + return (0); + + /* + * If it is an MCFG table, we can attach + */ + hdr = (struct acpi_table_header *)aaa->aaa_table; + if (memcmp(hdr->signature, MCFG_SIG, sizeof(MCFG_SIG) - 1) != 0) + return (0); + + return (1); +} + +void +acpimcfg_attach(struct device *parent, struct device *self, void *aux) +{ + struct acpi_attach_args *aaa = aux; + struct acpi_mcfg *mcfg = (struct acpi_mcfg *)aaa->aaa_table; + + printf(" addr 0x%llx, bus %d-%d\n", mcfg->base_address, + mcfg->min_bus_number, mcfg->max_bus_number); + + pci_mcfg_addr = mcfg->base_address; + pci_mcfg_min_bus = mcfg->min_bus_number; + pci_mcfg_max_bus = mcfg->max_bus_number; +} Index: dev/acpi/acpireg.h =================================================================== RCS file: /cvs/src/sys/dev/acpi/acpireg.h,v retrieving revision 1.23 diff -u -p -r1.23 acpireg.h --- dev/acpi/acpireg.h 21 Jul 2010 19:35:15 -0000 1.23 +++ dev/acpi/acpireg.h 26 Dec 2010 23:06:44 -0000 @@ -380,6 +380,17 @@ struct acpi_hpet { u_int8_t page_protection; } __packed; +struct acpi_mcfg { + struct acpi_table_header hdr; +#define MCFG_SIG "MCFG" + u_int8_t reserved[8]; + u_int64_t base_address; + u_int16_t segment; + u_int8_t min_bus_number; + u_int8_t max_bus_number; + u_int32_t reserved1; +} __packed; + struct acpi_facs { u_int8_t signature[4]; #define FACS_SIG "FACS" Index: dev/acpi/files.acpi =================================================================== RCS file: /cvs/src/sys/dev/acpi/files.acpi,v retrieving revision 1.24 diff -u -p -r1.24 files.acpi --- dev/acpi/files.acpi 26 Jul 2010 11:29:23 -0000 1.24 +++ dev/acpi/files.acpi 26 Dec 2010 23:06:44 -0000 @@ -56,6 +56,11 @@ device acpimadt attach acpimadt at acpi file dev/acpi/acpimadt.c acpimadt +# Memory Mapped Configuration Space Address Description Table +device acpimcfg +attach acpimcfg at acpi +file dev/acpi/acpimcfg.c acpimcfg + # PCI Routing Table device acpiprt attach acpiprt at acpi