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

Reply via email to