hrev45777 adds 3 changesets to branch 'master' old head: bc7a518375cc478c3630ad875fc23cbae51e8db0 new head: 26a4510e591b2d12b5191918ca4e92a5f94b2bd5 overview: http://cgit.haiku-os.org/haiku/log/?qt=range&q=26a4510+%5Ebc7a518 ---------------------------------------------------------------------------- ce353e5: pci: added pcie mechanism for config space access. * pci-acpi.cpp is based on the bootloader bios_ia32/acpi.cpp. The ACPI module has already a dependency on the PCI module. Using pci-acpi.cpp eases the simple task of finding the PCIe base address to map the config space. * pci_read_config and pci_write_config in pci_controller.h were using an uint8 for offsets in the config space. Switched to uint16 to enable access to the extended config space (0x100 and upper). Added a check for these offsets in pci_mech[1|2]_[read|write]_config() for x86 and other platforms as these mechanisms don't support a priori the extended config space. e1c4476: pci: switched PCI::[Read|Write]Config to type uint16 for the offset. 26a4510: pci: added pci_find_extended_capability(). * added PCI Extended Capabilities definitions. * pci_find_capability() parameter offset is now optional. [ Jérôme Duval <jerome.duval@xxxxxxxxx> ] ---------------------------------------------------------------------------- 14 files changed, 592 insertions(+), 53 deletions(-) headers/os/drivers/PCI.h | 36 +++ .../pci/arch/m68k/atari/pci_atari.cpp | 8 +- .../pci/arch/ppc/openfirmware/grackle.cpp | 10 +- .../pci/arch/ppc/openfirmware/uninorth.cpp | 14 +- .../kernel/bus_managers/pci/arch/x86/Jamfile | 5 + .../bus_managers/pci/arch/x86/pci_acpi.cpp | 270 +++++++++++++++++++ .../kernel/bus_managers/pci/arch/x86/pci_acpi.h | 27 ++ .../bus_managers/pci/arch/x86/pci_bios.cpp | 4 +- .../kernel/bus_managers/pci/arch/x86/pci_bios.h | 4 +- .../bus_managers/pci/arch/x86/pci_controller.cpp | 142 +++++++++- src/add-ons/kernel/bus_managers/pci/pci.cpp | 97 +++++-- src/add-ons/kernel/bus_managers/pci/pci.h | 19 +- .../kernel/bus_managers/pci/pci_controller.h | 4 +- .../kernel/bus_managers/pci/pci_private.h | 5 +- ############################################################################ Commit: ce353e5d6e65080fe81c58aa5c61abef824ad00e URL: http://cgit.haiku-os.org/haiku/commit/?id=ce353e5 Author: Jérôme Duval <jerome.duval@xxxxxxxxx> Date: Sat Jun 22 17:13:39 2013 UTC pci: added pcie mechanism for config space access. * pci-acpi.cpp is based on the bootloader bios_ia32/acpi.cpp. The ACPI module has already a dependency on the PCI module. Using pci-acpi.cpp eases the simple task of finding the PCIe base address to map the config space. * pci_read_config and pci_write_config in pci_controller.h were using an uint8 for offsets in the config space. Switched to uint16 to enable access to the extended config space (0x100 and upper). Added a check for these offsets in pci_mech[1|2]_[read|write]_config() for x86 and other platforms as these mechanisms don't support a priori the extended config space. ---------------------------------------------------------------------------- diff --git a/src/add-ons/kernel/bus_managers/pci/arch/m68k/atari/pci_atari.cpp b/src/add-ons/kernel/bus_managers/pci/arch/m68k/atari/pci_atari.cpp index d29f51c..c81474a 100644 --- a/src/add-ons/kernel/bus_managers/pci/arch/m68k/atari/pci_atari.cpp +++ b/src/add-ons/kernel/bus_managers/pci/arch/m68k/atari/pci_atari.cpp @@ -82,9 +82,9 @@ static int m68k_atari_enable_config(struct m68k_atari_fake_host_bridge *bridge, uint8 bus, uint8 slot, uint8 function, uint8 offset); static status_t m68k_atari_read_pci_config(void *cookie, uint8 bus, uint8 device, - uint8 function, uint8 offset, uint8 size, uint32 *value); + uint8 function, uint16 offset, uint8 size, uint32 *value); static status_t m68k_atari_write_pci_config(void *cookie, uint8 bus, - uint8 device, uint8 function, uint8 offset, uint8 size, + uint8 device, uint8 function, uint16 offset, uint8 size, uint32 value); static status_t m68k_atari_get_max_bus_devices(void *cookie, int32 *count); static status_t m68k_atari_read_pci_irq(void *cookie, uint8 bus, uint8 device, @@ -103,7 +103,7 @@ static pci_controller sM68kAtariPCIController = { static status_t m68k_atari_read_pci_config(void *cookie, uint8 bus, uint8 device, uint8 function, - uint8 offset, uint8 size, uint32 *value) + uint16 offset, uint8 size, uint32 *value) { struct fake_pci_device *devices = (struct fake_pci_device *)cookie; struct fake_pci_device *dev; @@ -183,7 +183,7 @@ m68k_atari_read_pci_config(void *cookie, uint8 bus, uint8 device, uint8 function static status_t m68k_atari_write_pci_config(void *cookie, uint8 bus, uint8 device, - uint8 function, uint8 offset, uint8 size, uint32 value) + uint8 function, uint16 offset, uint8 size, uint32 value) { #if 0 if (m68k_atari_enable_config(bridge, bus, device, function, offset)) { diff --git a/src/add-ons/kernel/bus_managers/pci/arch/ppc/openfirmware/grackle.cpp b/src/add-ons/kernel/bus_managers/pci/arch/ppc/openfirmware/grackle.cpp index 7a67ca0..b0a98f1 100644 --- a/src/add-ons/kernel/bus_managers/pci/arch/ppc/openfirmware/grackle.cpp +++ b/src/add-ons/kernel/bus_managers/pci/arch/ppc/openfirmware/grackle.cpp @@ -80,13 +80,16 @@ struct grackle_host_bridge { static status_t grackle_read_pci_config(void *cookie, uint8 bus, uint8 device, uint8 function, - uint8 offset, uint8 size, uint32 *value) + uint16 offset, uint8 size, uint32 *value) { grackle_host_bridge *bridge = (grackle_host_bridge*)cookie; TRACE("grackle_read_pci_config(bus=%u, dev=%u, func=%u, offset=%u, " "size=%u)\n", (int)bus, (int)device, (int)function, (int)offset, (int)size); + if (offset > 0xff) + return B_BAD_VALUE; + out32rb(bridge->address_registers, (1 << 31) | (bus << 16) | ((device & 0x1f) << 11) | ((function & 0x7) << 8) | (offset & 0xfc)); @@ -115,13 +118,16 @@ grackle_read_pci_config(void *cookie, uint8 bus, uint8 device, uint8 function, static status_t grackle_write_pci_config(void *cookie, uint8 bus, uint8 device, uint8 function, - uint8 offset, uint8 size, uint32 value) + uint16 offset, uint8 size, uint32 value) { grackle_host_bridge *bridge = (grackle_host_bridge*)cookie; TRACE("grackle_write_pci_config(bus=%u, dev=%u, func=%u, offset=%u, " "size=%u, value=%lu)\n", (int)bus, (int)device, (int)function, (int)offset, (int)size, value); + if (offset > 0xff) + return B_BAD_VALUE; + out32rb(bridge->address_registers, (1 << 31) | (bus << 16) | ((device & 0x1f) << 11) | ((function & 0x7) << 8) | (offset & 0xfc)); diff --git a/src/add-ons/kernel/bus_managers/pci/arch/ppc/openfirmware/uninorth.cpp b/src/add-ons/kernel/bus_managers/pci/arch/ppc/openfirmware/uninorth.cpp index d7a1f4c..7997129 100644 --- a/src/add-ons/kernel/bus_managers/pci/arch/ppc/openfirmware/uninorth.cpp +++ b/src/add-ons/kernel/bus_managers/pci/arch/ppc/openfirmware/uninorth.cpp @@ -74,9 +74,9 @@ static int uninorth_enable_config(struct uninorth_host_bridge *bridge, uint8 bus, uint8 slot, uint8 function, uint8 offset); static status_t uninorth_read_pci_config(void *cookie, uint8 bus, uint8 device, - uint8 function, uint8 offset, uint8 size, uint32 *value); + uint8 function, uint16 offset, uint8 size, uint32 *value); static status_t uninorth_write_pci_config(void *cookie, uint8 bus, - uint8 device, uint8 function, uint8 offset, uint8 size, + uint8 device, uint8 function, uint16 offset, uint8 size, uint32 value); static status_t uninorth_get_max_bus_devices(void *cookie, int32 *count); static status_t uninorth_read_pci_irq(void *cookie, uint8 bus, uint8 device, @@ -95,10 +95,13 @@ static pci_controller sUniNorthPCIController = { static status_t uninorth_read_pci_config(void *cookie, uint8 bus, uint8 device, uint8 function, - uint8 offset, uint8 size, uint32 *value) + uint16 offset, uint8 size, uint32 *value) { uninorth_host_bridge *bridge = (uninorth_host_bridge*)cookie; + if (offset > 0xff) + return B_BAD_VALUE; + addr_t caoff = bridge->data_registers + (offset & 0x07); if (uninorth_enable_config(bridge, bus, device, function, offset) != 0) { @@ -124,10 +127,13 @@ uninorth_read_pci_config(void *cookie, uint8 bus, uint8 device, uint8 function, static status_t uninorth_write_pci_config(void *cookie, uint8 bus, uint8 device, - uint8 function, uint8 offset, uint8 size, uint32 value) + uint8 function, uint16 offset, uint8 size, uint32 value) { uninorth_host_bridge *bridge = (uninorth_host_bridge*)cookie; + if (offset > 0xff) + return B_BAD_VALUE; + addr_t caoff = bridge->data_registers + (offset & 0x07); if (uninorth_enable_config(bridge, bus, device, function, offset)) { diff --git a/src/add-ons/kernel/bus_managers/pci/arch/x86/Jamfile b/src/add-ons/kernel/bus_managers/pci/arch/x86/Jamfile index f03f6c7..980b450 100644 --- a/src/add-ons/kernel/bus_managers/pci/arch/x86/Jamfile +++ b/src/add-ons/kernel/bus_managers/pci/arch/x86/Jamfile @@ -4,7 +4,12 @@ SubDirHdrs [ FDirName $(SUBDIR) $(DOTDOT) $(DOTDOT) ] ; UsePrivateHeaders kernel [ FDirName kernel arch x86 ] [ FDirName kernel util ] ; +SubDirHdrs $(HAIKU_TOP) src add-ons kernel bus_managers acpi acpica include ; +SubDirHdrs $(HAIKU_TOP) src add-ons kernel bus_managers acpi acpica include + platform ; + KernelStaticLibrary pci_arch_bus_manager : + pci_acpi.cpp pci_arch_info.cpp pci_arch_module.cpp pci_bios.cpp diff --git a/src/add-ons/kernel/bus_managers/pci/arch/x86/pci_acpi.cpp b/src/add-ons/kernel/bus_managers/pci/arch/x86/pci_acpi.cpp new file mode 100644 index 0000000..f1fb0fa --- /dev/null +++ b/src/add-ons/kernel/bus_managers/pci/arch/x86/pci_acpi.cpp @@ -0,0 +1,270 @@ +/* + * Copyright 2011, Rene Gollent, rene@xxxxxxxxxxx. + * Copyright 2008, Dustin Howett, dustin.howett@xxxxxxxxx. All rights reserved. + * Copyright 2007, Michael Lotz, mmlr@xxxxxxxx + * Copyright 2004-2005, Axel Dörfler, axeld@xxxxxxxxxxxxxxxx. + * Distributed under the terms of the MIT License. + * + * Copyright 2001, Travis Geiselbrecht. All rights reserved. + * Distributed under the terms of the NewOS License. +*/ + + +#include "pci_acpi.h" + +#include <string.h> + +#include <KernelExport.h> + +#include <arch/x86/arch_acpi.h> + + +//#define TRACE_ACPI +#ifdef TRACE_ACPI +# define TRACE(x) dprintf x +#else +# define TRACE(x) ; +#endif + +static struct scan_spots_struct acpi_scan_spots[] = { + { 0x0, 0x1000, 0x1000 }, + { 0x9f000, 0x10000, 0x1000 }, + { 0xe0000, 0x110000, 0x20000 }, + { 0xfd000, 0xfe000, 0x1000}, + { 0, 0, 0 } +}; + +static acpi_descriptor_header* sAcpiRsdt; // System Description Table +static acpi_descriptor_header* sAcpiXsdt; // Extended System Description Table +static int32 sNumEntries = -1; + + +static status_t +acpi_validate_rsdp(acpi_rsdp* rsdp) +{ + const char* data = (const char*)rsdp; + unsigned char checksum = 0; + for (uint32 i = 0; i < sizeof(acpi_rsdp_legacy); i++) + checksum += data[i]; + + if ((checksum & 0xff) != 0) { + TRACE(("acpi: rsdp failed basic checksum\n")); + return B_BAD_DATA; + } + + // for ACPI 2.0+ we need to also validate the extended checksum + if (rsdp->revision > 0) { + for (uint32 i = sizeof(acpi_rsdp_legacy); + i < sizeof(acpi_rsdp_extended); i++) { + checksum += data[i]; + } + + if ((checksum & 0xff) != 0) { + TRACE(("acpi: rsdp failed extended checksum\n")); + return B_BAD_DATA; + } + } + + return B_OK; +} + + +static status_t +acpi_validate_rsdt(acpi_descriptor_header* rsdt) +{ + const char* data = (const char*)rsdt; + unsigned char checksum = 0; + for (uint32 i = 0; i < rsdt->length; i++) + checksum += data[i]; + + return checksum == 0 ? B_OK : B_BAD_DATA; +} + + +static status_t +acpi_check_rsdt(acpi_rsdp* rsdp) +{ + if (acpi_validate_rsdp(rsdp) != B_OK) + return B_BAD_DATA; + + bool usingXsdt = false; + + TRACE(("acpi: found rsdp at %p oem id: %.6s, rev %d\n", + rsdp, rsdp->oem_id, rsdp->revision)); + TRACE(("acpi: rsdp points to rsdt at 0x%lx\n", rsdp->rsdt_address)); + + uint32 length = 0; + acpi_descriptor_header* rsdt = NULL; + area_id rsdtArea = -1; + if (rsdp->revision > 0) { + length = rsdp->xsdt_length; + rsdtArea = map_physical_memory("rsdt acpi", + (uint32)rsdp->xsdt_address, rsdp->xsdt_length, B_ANY_KERNEL_ADDRESS, + B_KERNEL_READ_AREA, (void **)&rsdt); + if (rsdt != NULL + && strncmp(rsdt->signature, ACPI_XSDT_SIGNATURE, 4) != 0) { + delete_area(rsdtArea); + rsdt = NULL; + TRACE(("acpi: invalid extended system description table\n")); + } else + usingXsdt = true; + } + + // if we're ACPI v1 or we fail to map the XSDT for some reason, + // attempt to use the RSDT instead. + if (rsdt == NULL) { + // map and validate the root system description table + rsdtArea = map_physical_memory("rsdt acpi", + rsdp->rsdt_address, sizeof(acpi_descriptor_header), + B_ANY_KERNEL_ADDRESS, B_KERNEL_READ_AREA, (void **)&rsdt); + if (rsdt != NULL + && strncmp(rsdt->signature, ACPI_RSDT_SIGNATURE, 4) != 0) { + delete_area(rsdtArea); + rsdt = NULL; + TRACE(("acpi: invalid root system description table\n")); + return B_ERROR; + } + + length = rsdt->length; + // Map the whole table, not just the header + TRACE(("acpi: rsdt length: %lu\n", length)); + delete_area(rsdtArea); + rsdtArea = map_physical_memory("rsdt acpi", + rsdp->rsdt_address, length, B_ANY_KERNEL_ADDRESS, + B_KERNEL_READ_AREA, (void **)&rsdt); + } + + if (rsdt != NULL) { + if (acpi_validate_rsdt(rsdt) != B_OK) { + TRACE(("acpi: rsdt failed checksum validation\n")); + delete_area(rsdtArea); + return B_ERROR; + } else { + if (usingXsdt) + sAcpiXsdt = rsdt; + else + sAcpiRsdt = rsdt; + TRACE(("acpi: found valid %s at %p\n", + usingXsdt ? ACPI_XSDT_SIGNATURE : ACPI_RSDT_SIGNATURE, + rsdt)); + } + } else + return B_ERROR; + + return B_OK; +} + + +template<typename PointerType> +acpi_descriptor_header* +acpi_find_table_generic(const char* signature, acpi_descriptor_header* acpiSdt) +{ + if (acpiSdt == NULL) + return NULL; + + if (sNumEntries == -1) { + // if using the xsdt, our entries are 64 bits wide. + sNumEntries = (acpiSdt->length + - sizeof(acpi_descriptor_header)) + / sizeof(PointerType); + } + + if (sNumEntries <= 0) { + TRACE(("acpi: root system description table is empty\n")); + return NULL; + } + + TRACE(("acpi: searching %ld entries for table '%.4s'\n", sNumEntries, + signature)); + + PointerType* pointer = (PointerType*)((uint8*)acpiSdt + + sizeof(acpi_descriptor_header)); + + acpi_descriptor_header* header = NULL; + area_id headerArea = -1; + for (int32 j = 0; j < sNumEntries; j++, pointer++) { + headerArea = map_physical_memory("acpi header", (uint32)*pointer, + sizeof(acpi_descriptor_header), B_ANY_KERNEL_ADDRESS, + B_KERNEL_READ_AREA, (void **)&header); + + if (header == NULL + || strncmp(header->signature, signature, 4) != 0) { + // not interesting for us + TRACE(("acpi: Looking for '%.4s'. Skipping '%.4s'\n", + signature, header != NULL ? header->signature : "null")); + + if (header != NULL) { + delete_area(headerArea); + header = NULL; + } + + continue; + } + + TRACE(("acpi: Found '%.4s' @ %p\n", signature, pointer)); + break; + } + + + if (header == NULL) + return NULL; + + // Map the whole table, not just the header + uint32 length = header->length; + delete_area(headerArea); + + headerArea = map_physical_memory("acpi table", + (uint32)*pointer, length, B_ANY_KERNEL_ADDRESS, + B_KERNEL_READ_AREA, (void **)&header); + return header; +} + + +void* +acpi_find_table(const char* signature) +{ + if (sAcpiRsdt != NULL) + return acpi_find_table_generic<uint32>(signature, sAcpiRsdt); + else if (sAcpiXsdt != NULL) + return acpi_find_table_generic<uint64>(signature, sAcpiXsdt); + + return NULL; +} + + +void +acpi_init() +{ + // Try to find the ACPI RSDP. + for (int32 i = 0; acpi_scan_spots[i].length > 0; i++) { + acpi_rsdp* rsdp = NULL; + + TRACE(("acpi_init: entry base 0x%lx, limit 0x%lx\n", + acpi_scan_spots[i].start, acpi_scan_spots[i].stop)); + + char* start = NULL; + area_id rsdpArea = map_physical_memory("acpi rsdp", + acpi_scan_spots[i].start, acpi_scan_spots[i].length, + B_ANY_KERNEL_ADDRESS, B_KERNEL_READ_AREA, (void **)&start); + if (rsdpArea < B_OK) { + TRACE(("acpi_init: couldn't map %s\n", strerror(rsdpArea))); + break; + } + for (char *pointer = start; + (addr_t)pointer < (addr_t)start + acpi_scan_spots[i].length; + pointer += 16) { + if (strncmp(pointer, ACPI_RSDP_SIGNATURE, 8) == 0) { + TRACE(("acpi_init: found ACPI RSDP signature at %p\n", + pointer)); + rsdp = (acpi_rsdp*)pointer; + } + } + + if (rsdp != NULL && acpi_check_rsdt(rsdp) == B_OK) { + delete_area(rsdpArea); + break; + } + delete_area(rsdpArea); + } + +} diff --git a/src/add-ons/kernel/bus_managers/pci/arch/x86/pci_acpi.h b/src/add-ons/kernel/bus_managers/pci/arch/x86/pci_acpi.h new file mode 100644 index 0000000..8be3856 --- /dev/null +++ b/src/add-ons/kernel/bus_managers/pci/arch/x86/pci_acpi.h @@ -0,0 +1,27 @@ +/* + * Copyright 2005, Axel Dörfler, axeld@xxxxxxxxxxxxxxxx. All rights reserved. + * Distributed under the terms of the MIT License. + */ +#ifndef PCI_ACPI_H +#define PCI_ACPI_H + +#include <SupportDefs.h> + +#ifdef __cplusplus +extern "C" { +#endif + +struct scan_spots_struct { + uint32 start; + uint32 stop; + uint32 length; +}; + +void *acpi_find_table(const char *signature); +void acpi_init(void); + +#ifdef __cplusplus +} +#endif + +#endif /* PCI_ACPI_H */ diff --git a/src/add-ons/kernel/bus_managers/pci/arch/x86/pci_bios.cpp b/src/add-ons/kernel/bus_managers/pci/arch/x86/pci_bios.cpp index 1852f70..ef34b2b 100644 --- a/src/add-ons/kernel/bus_managers/pci/arch/x86/pci_bios.cpp +++ b/src/add-ons/kernel/bus_managers/pci/arch/x86/pci_bios.cpp @@ -17,7 +17,7 @@ pci_bios_init(void) status_t pci_bios_read_config(void *cookie, uint8 bus, uint8 device, uint8 function, - uint8 offset, uint8 size, uint32 *value) + uint16 offset, uint8 size, uint32 *value) { return B_ERROR; } @@ -25,7 +25,7 @@ pci_bios_read_config(void *cookie, uint8 bus, uint8 device, uint8 function, status_t pci_bios_write_config(void *cookie, uint8 bus, uint8 device, uint8 function, - uint8 offset, uint8 size, uint32 value) + uint16 offset, uint8 size, uint32 value) { return B_ERROR; } diff --git a/src/add-ons/kernel/bus_managers/pci/arch/x86/pci_bios.h b/src/add-ons/kernel/bus_managers/pci/arch/x86/pci_bios.h index 89807f1..64d05b6 100644 --- a/src/add-ons/kernel/bus_managers/pci/arch/x86/pci_bios.h +++ b/src/add-ons/kernel/bus_managers/pci/arch/x86/pci_bios.h @@ -7,11 +7,11 @@ status_t pci_bios_init(void); status_t pci_bios_read_config(void *cookie, uint8 bus, uint8 device, uint8 function, - uint8 offset, uint8 size, uint32 *value); + uint16 offset, uint8 size, uint32 *value); status_t pci_bios_write_config(void *cookie, uint8 bus, uint8 device, uint8 function, - uint8 offset, uint8 size, uint32 value); + uint16 offset, uint8 size, uint32 value); status_t pci_bios_get_max_bus_devices(void *cookie, int32 *count); diff --git a/src/add-ons/kernel/bus_managers/pci/arch/x86/pci_controller.cpp b/src/add-ons/kernel/bus_managers/pci/arch/x86/pci_controller.cpp index 4a0fd7f..e702ca7 100644 --- a/src/add-ons/kernel/bus_managers/pci/arch/x86/pci_controller.cpp +++ b/src/add-ons/kernel/bus_managers/pci/arch/x86/pci_controller.cpp @@ -3,14 +3,20 @@ * Distributed under the terms of the MIT License. */ + #include <KernelExport.h> #include <driver_settings.h> #include <string.h> -#include "pci_irq.h" + +#include "pci_acpi.h" +#include "arch_cpu.h" #include "pci_bios.h" -#include "pci_private.h" #include "pci_controller.h" -#include "arch_cpu.h" +#include "pci_irq.h" +#include "pci_private.h" + +#include "acpi.h" + #define PCI_MECH1_REQ_PORT 0xCF8 #define PCI_MECH1_DATA_PORT 0xCFC @@ -38,11 +44,14 @@ spinlock sConfigLock = B_SPINLOCK_INITIALIZER; static status_t pci_mech1_read_config(void *cookie, uint8 bus, uint8 device, uint8 function, - uint8 offset, uint8 size, uint32 *value) + uint16 offset, uint8 size, uint32 *value) { cpu_status cpu; status_t status = B_OK; + if (offset > 0xff) + return B_BAD_VALUE; + PCI_LOCK_CONFIG(cpu); out32(PCI_MECH1_REQ_DATA(bus, device, function, offset), PCI_MECH1_REQ_PORT); switch (size) { @@ -67,11 +76,14 @@ pci_mech1_read_config(void *cookie, uint8 bus, uint8 device, uint8 function, static status_t pci_mech1_write_config(void *cookie, uint8 bus, uint8 device, uint8 function, - uint8 offset, uint8 size, uint32 value) + uint16 offset, uint8 size, uint32 value) { cpu_status cpu; status_t status = B_OK; + if (offset > 0xff) + return B_BAD_VALUE; + PCI_LOCK_CONFIG(cpu); out32(PCI_MECH1_REQ_DATA(bus, device, function, offset), PCI_MECH1_REQ_PORT); switch (size) { @@ -104,11 +116,14 @@ pci_mech1_get_max_bus_devices(void *cookie, int32 *count) static status_t pci_mech2_read_config(void *cookie, uint8 bus, uint8 device, uint8 function, - uint8 offset, uint8 size, uint32 *value) + uint16 offset, uint8 size, uint32 *value) { cpu_status cpu; status_t status = B_OK; + if (offset > 0xff) + return B_BAD_VALUE; + PCI_LOCK_CONFIG(cpu); out8((uint8)(0xf0 | (function << 1)), PCI_MECH2_ENABLE_PORT); out8(bus, PCI_MECH2_FORWARD_PORT); @@ -135,11 +150,14 @@ pci_mech2_read_config(void *cookie, uint8 bus, uint8 device, uint8 function, static status_t pci_mech2_write_config(void *cookie, uint8 bus, uint8 device, uint8 function, - uint8 offset, uint8 size, uint32 value) + uint16 offset, uint8 size, uint32 value) { cpu_status cpu; status_t status = B_OK; + if (offset > 0xff) + return B_BAD_VALUE; + PCI_LOCK_CONFIG(cpu); out8((uint8)(0xf0 | (function << 1)), PCI_MECH2_ENABLE_PORT); out8(bus, PCI_MECH2_FORWARD_PORT); @@ -172,6 +190,71 @@ pci_mech2_get_max_bus_devices(void *cookie, int32 *count) } +addr_t sPCIeBase = 0; +#define PCIE_VADDR(base, bus, slot, func, reg) ((base) + \ + ((((bus) & 0xff) << 20) | (((slot) & 0x1f) << 15) | \ + (((func) & 0x7) << 12) | ((reg) & 0xfff))) + +static status_t +pci_mechpcie_read_config(void *cookie, uint8 bus, uint8 device, uint8 function, + uint16 offset, uint8 size, uint32 *value) +{ + status_t status = B_OK; + + addr_t address = PCIE_VADDR(sPCIeBase, bus, device, function, offset); + + switch (size) { + case 1: + *value = *(uint8*)address; + break; + case 2: + *value = *(uint16*)address; + break; + case 4: + *value = *(uint32*)address; + break; + default: + status = B_ERROR; + break; + } + return status; +} + + +static status_t +pci_mechpcie_write_config(void *cookie, uint8 bus, uint8 device, uint8 function, + uint16 offset, uint8 size, uint32 value) +{ + status_t status = B_OK; + + addr_t address = PCIE_VADDR(sPCIeBase, bus, device, function, offset); + switch (size) { + case 1: + *(uint8*)address = value; + break; + case 2: + *(uint16*)address = value; + break; + case 4: + *(uint32*)address = value; + break; + default: + status = B_ERROR; + break; + } + + return status; +} + + +static status_t +pci_mechpcie_get_max_bus_devices(void *cookie, int32 *count) +{ + *count = 32; + return B_OK; +} + + void * pci_ram_address(const void *physical_address_in_system_memory) { @@ -197,6 +280,15 @@ pci_controller pci_controller_x86_mech2 = pci_x86_irq_write, }; +pci_controller pci_controller_x86_mechpcie = +{ + pci_mechpcie_read_config, + pci_mechpcie_write_config, + pci_mechpcie_get_max_bus_devices, + pci_x86_irq_read, + pci_x86_irq_write, +}; + pci_controller pci_controller_x86_bios = { pci_bios_read_config, @@ -212,6 +304,7 @@ pci_controller_init(void) { bool search_mech1 = true; bool search_mech2 = true; + bool search_mechpcie = true; bool search_bios = true; void *config = NULL; status_t status; @@ -225,11 +318,13 @@ pci_controller_init(void) const char *mech = get_driver_parameter(config, "mechanism", NULL, NULL); if (mech) { - search_mech1 = search_mech2 = search_bios = false; + search_mech1 = search_mech2 = search_mechpcie = search_bios = false; if (strcmp(mech, "1") == 0) search_mech1 = true; else if (strcmp(mech, "2") == 0) search_mech2 = true; + else if (strcmp(mech, "pcie") == 0) + search_mechpcie = true; else if (strcmp(mech, "bios") == 0) search_bios = true; else @@ -240,10 +335,39 @@ pci_controller_init(void) // TODO: check safemode "don't call the BIOS" setting and unset search_bios! - // PCI configuration mechanism 1 is the preferred one. + // PCI configuration mechanism PCIe is the preferred one. + // If it doesn't work, try mechanism 1. // If it doesn't work, try mechanism 2. // Finally, try to fallback to PCI BIOS + if (search_mechpcie) { + acpi_init(); + struct acpi_table_mcfg* mcfg = + (struct acpi_table_mcfg*)acpi_find_table("MCFG"); + if (mcfg != NULL) { + struct acpi_mcfg_allocation* end = (struct acpi_mcfg_allocation*) + ((char*)mcfg + mcfg->Header.Length); + struct acpi_mcfg_allocation* alloc = (struct acpi_mcfg_allocation*) + (mcfg + 1); + for (; alloc < end; alloc++) { + dprintf("PCI: mechanism addr: %" B_PRIx64 ", seg: %x, start: " + "%x, end: %x\n", alloc->Address, alloc->PciSegment, + alloc->StartBusNumber, alloc->EndBusNumber); + if (alloc->PciSegment == 0) { + area_id mcfgArea = map_physical_memory("acpi mcfg", + alloc->Address, (alloc->EndBusNumber + 1) << 20, + B_ANY_KERNEL_ADDRESS, B_KERNEL_READ_AREA + | B_KERNEL_WRITE_AREA, (void **)&sPCIeBase); + if (mcfgArea < 0) + break; + dprintf("PCI: mechanism pcie controller found\n"); + return pci_controller_add(&pci_controller_x86_mechpcie, + NULL); + } + } + } + } + if (search_mech1) { // check for mechanism 1 out32(0x80000000, PCI_MECH1_REQ_PORT); diff --git a/src/add-ons/kernel/bus_managers/pci/pci_controller.h b/src/add-ons/kernel/bus_managers/pci/pci_controller.h index 0c39c35..3a37e80 100644 --- a/src/add-ons/kernel/bus_managers/pci/pci_controller.h +++ b/src/add-ons/kernel/bus_managers/pci/pci_controller.h @@ -13,12 +13,12 @@ typedef struct pci_controller // read PCI config space status_t (*read_pci_config)(void *cookie, uint8 bus, uint8 device, uint8 function, - uint8 offset, uint8 size, uint32 *value); + uint16 offset, uint8 size, uint32 *value); // write PCI config space status_t (*write_pci_config)(void *cookie, uint8 bus, uint8 device, uint8 function, - uint8 offset, uint8 size, uint32 value); + uint16 offset, uint8 size, uint32 value); status_t (*get_max_bus_devices)(void *cookie, int32 *count); ############################################################################ Commit: e1c44764ef30ce2fd53219561bd3c2d582334882 URL: http://cgit.haiku-os.org/haiku/commit/?id=e1c4476 Author: Jérôme Duval <jerome.duval@xxxxxxxxx> Date: Sat Jun 22 17:35:43 2013 UTC pci: switched PCI::[Read|Write]Config to type uint16 for the offset. ---------------------------------------------------------------------------- diff --git a/src/add-ons/kernel/bus_managers/pci/pci.cpp b/src/add-ons/kernel/bus_managers/pci/pci.cpp index 67f0585..fc3f5dc 100644 --- a/src/add-ons/kernel/bus_managers/pci/pci.cpp +++ b/src/add-ons/kernel/bus_managers/pci/pci.cpp @@ -1467,7 +1467,7 @@ PCI::_RefreshDeviceInfo(PCIBus *bus) status_t PCI::ReadConfig(uint8 domain, uint8 bus, uint8 device, uint8 function, - uint8 offset, uint8 size, uint32 *value) + uint16 offset, uint8 size, uint32 *value) { domain_data *info = _GetDomainData(domain); if (!info) @@ -1490,7 +1490,7 @@ PCI::ReadConfig(uint8 domain, uint8 bus, uint8 device, uint8 function, uint32 PCI::ReadConfig(uint8 domain, uint8 bus, uint8 device, uint8 function, - uint8 offset, uint8 size) + uint16 offset, uint8 size) { uint32 value; if (ReadConfig(domain, bus, device, function, offset, size, &value) @@ -1502,7 +1502,7 @@ PCI::ReadConfig(uint8 domain, uint8 bus, uint8 device, uint8 function, uint32 -PCI::ReadConfig(PCIDev *device, uint8 offset, uint8 size) +PCI::ReadConfig(PCIDev *device, uint16 offset, uint8 size) { uint32 value; if (ReadConfig(device->domain, device->bus, device->device, @@ -1515,7 +1515,7 @@ PCI::ReadConfig(PCIDev *device, uint8 offset, uint8 size) status_t PCI::WriteConfig(uint8 domain, uint8 bus, uint8 device, uint8 function, - uint8 offset, uint8 size, uint32 value) + uint16 offset, uint8 size, uint32 value) { domain_data *info = _GetDomainData(domain); if (!info) @@ -1537,7 +1537,7 @@ PCI::WriteConfig(uint8 domain, uint8 bus, uint8 device, uint8 function, status_t -PCI::WriteConfig(PCIDev *device, uint8 offset, uint8 size, uint32 value) +PCI::WriteConfig(PCIDev *device, uint16 offset, uint8 size, uint32 value) { return WriteConfig(device->domain, device->bus, device->device, device->function, offset, size, value); diff --git a/src/add-ons/kernel/bus_managers/pci/pci.h b/src/add-ons/kernel/bus_managers/pci/pci.h index c86676f..bb7f9dc 100644 --- a/src/add-ons/kernel/bus_managers/pci/pci.h +++ b/src/add-ons/kernel/bus_managers/pci/pci.h @@ -77,17 +77,17 @@ public: status_t GetNthInfo(long index, pci_info *outInfo); status_t ReadConfig(uint8 domain, uint8 bus, uint8 device, - uint8 function, uint8 offset, uint8 size, + uint8 function, uint16 offset, uint8 size, uint32 *value); uint32 ReadConfig(uint8 domain, uint8 bus, uint8 device, - uint8 function, uint8 offset, uint8 size); - uint32 ReadConfig(PCIDev *device, uint8 offset, + uint8 function, uint16 offset, uint8 size); + uint32 ReadConfig(PCIDev *device, uint16 offset, uint8 size); status_t WriteConfig(uint8 domain, uint8 bus, uint8 device, - uint8 function, uint8 offset, uint8 size, + uint8 function, uint16 offset, uint8 size, uint32 value); - status_t WriteConfig(PCIDev *device, uint8 offset, + status_t WriteConfig(PCIDev *device, uint16 offset, uint8 size, uint32 value); status_t FindCapability(uint8 domain, uint8 bus, ############################################################################ Revision: hrev45777 Commit: 26a4510e591b2d12b5191918ca4e92a5f94b2bd5 URL: http://cgit.haiku-os.org/haiku/commit/?id=26a4510 Author: Jérôme Duval <jerome.duval@xxxxxxxxx> Date: Sat Jun 22 17:43:00 2013 UTC pci: added pci_find_extended_capability(). * added PCI Extended Capabilities definitions. * pci_find_capability() parameter offset is now optional. ---------------------------------------------------------------------------- diff --git a/headers/os/drivers/PCI.h b/headers/os/drivers/PCI.h index ef0b506..bff025f 100644 --- a/headers/os/drivers/PCI.h +++ b/headers/os/drivers/PCI.h @@ -196,6 +196,7 @@ struct pci_module_info { #define PCI_header_type 0x0e /* (1 byte) header type */ #define PCI_bist 0x0f /* (1 byte) built-in self-test */ +#define PCI_extended_capability 0x100 /* (4 bytes) extended capability */ /* --- @@ -690,6 +691,41 @@ struct pci_module_info { #define PCI_cap_id_sata 0x12 /* Serial ATA Capability */ #define PCI_cap_id_pciaf 0x13 /* PCI Advanced Features */ +/** PCI Extended Capabilities */ +#define PCI_extcap_id(x) (x & 0x0000ffff) +#define PCI_extcap_version(x) ((x & 0x000f0000) >> 16) +#define PCI_extcap_next_ptr(x) ((x & 0xfff00000) >> 20) + +#define PCI_extcap_id_aer 0x0001 /* Advanced Error Reporting */ +#define PCI_extcap_id_vc 0x0002 /* Virtual Channel */ +#define PCI_extcap_id_serial 0x0003 /* Serial Number */ +#define PCI_extcap_id_power_budget 0x0004 /* Power Budgeting */ +#define PCI_extcap_id_rcl_decl 0x0005 /* Root Complex Link Declaration */ +#define PCI_extcap_id_rcil_ctl 0x0006 /* Root Complex Internal Link Control */ +#define PCI_extcap_id_rcec_assoc 0x0007 /* Root Complex Event Collector Association */ +#define PCI_extcap_id_mfvc 0x0008 /* MultiFunction Virtual Channel */ +#define PCI_extcap_id_vc2 0x0009 /* Virtual Channel 2 */ +#define PCI_extcap_id_rcrb_header 0x000a /* RCRB Header */ +#define PCI_extcap_id_vendor 0x000b /* Vendor Unique */ +#define PCI_extcap_id_acs 0x000d /* Access Control Services */ +#define PCI_extcap_id_ari 0x000e /* Alternative Routing Id Interpretation */ +#define PCI_extcap_id_ats 0x000f /* Address Translation Services */ +#define PCI_extcap_id_srio_virtual 0x0010 /* Single Root I/O Virtualization */ +#define PCI_extcap_id_mrio_virtual 0x0011 /* Multiple Root I/O Virtual */ +#define PCI_extcap_id_multicast 0x0012 /* Multicast */ +#define PCI_extcap_id_page_request 0x0013 /* Page Request */ +#define PCI_extcap_id_amd 0x0014 /* AMD Reserved */ +#define PCI_extcap_id_resizable_bar 0x0015 /* Resizable Bar */ +#define PCI_extcap_id_dyn_power_alloc 0x0016 /* Dynamic Power Allocation */ +#define PCI_extcap_id_tph_requester 0x0017 /* TPH Requester */ +#define PCI_extcap_id_latency_tolerance 0x0018 /* Latency Tolerance Reporting */ +#define PCI_extcap_id_2ndpcie 0x0019 /* Secondary PCIe */ +#define PCI_extcap_id_pmux 0x001a /* Protocol Multiplexing */ +#define PCI_extcap_id_pasid 0x001b /* Process Address Space Id */ +#define PCI_extcap_id_ln_requester 0x001c /* LN Requester */ +#define PCI_extcap_id_dpc 0x001d /* Downstream Porto Containment */ +#define PCI_extcap_id_l1pm 0x001e /* L1 Power Management Substates */ + /** Power Management Control Status Register settings */ #define PCI_pm_mask 0x03 #define PCI_pm_ctrl 0x02 diff --git a/src/add-ons/kernel/bus_managers/pci/pci.cpp b/src/add-ons/kernel/bus_managers/pci/pci.cpp index fc3f5dc..1b8a1c0 100644 --- a/src/add-ons/kernel/bus_managers/pci/pci.cpp +++ b/src/add-ons/kernel/bus_managers/pci/pci.cpp @@ -75,8 +75,8 @@ pci_write_config(uint8 virtualBus, uint8 device, uint8 function, uint8 offset, status_t -pci_find_capability(uchar virtualBus, uchar device, uchar function, - uchar capID, uchar *offset) +pci_find_capability(uint8 virtualBus, uint8 device, uint8 function, + uint8 capID, uint8 *offset) { uint8 bus; uint8 domain; @@ -88,6 +88,20 @@ pci_find_capability(uchar virtualBus, uchar device, uchar function, status_t +pci_find_extended_capability(uint8 virtualBus, uint8 device, uint8 function, + uint16 capID, uint16 *offset) +{ + uint8 bus; + uint8 domain; + if (gPCI->ResolveVirtualBus(virtualBus, &domain, &bus) != B_OK) + return B_ERROR; + + return gPCI->FindExtendedCapability(domain, bus, device, function, capID, + offset); +} + + +status_t pci_reserve_device(uchar virtualBus, uchar device, uchar function, const char *driverName, void *nodeCookie) { @@ -1548,14 +1562,10 @@ status_t PCI::FindCapability(uint8 domain, uint8 bus, uint8 device, uint8 function, uint8 capID, uint8 *offset) { - if (offset == NULL) { - TRACE_CAP("PCI: FindCapability() ERROR %u:%u:%u capability %#02x offset NULL pointer\n", bus, device, function, capID); - return B_BAD_VALUE; - } - uint16 status = ReadConfig(domain, bus, device, function, PCI_status, 2); if (!(status & PCI_status_capabilities)) { - TRACE_CAP("PCI: find_pci_capability ERROR %u:%u:%u capability %#02x not supported\n", bus, device, function, capID); + FLOW("PCI: find_pci_capability ERROR %u:%u:%u capability %#02x " + "not supported\n", bus, device, function, capID); return B_ERROR; } @@ -1566,27 +1576,29 @@ PCI::FindCapability(uint8 domain, uint8 bus, uint8 device, uint8 function, switch (headerType & PCI_header_type_mask) { case PCI_header_type_generic: case PCI_header_type_PCI_to_PCI_bridge: - capPointer = ReadConfig(domain, bus, device, function, - PCI_capabilities_ptr, 1); + capPointer = PCI_capabilities_ptr; break; case PCI_header_type_cardbus: - capPointer = ReadConfig(domain, bus, device, function, - PCI_capabilities_ptr_2, 1); + capPointer = PCI_capabilities_ptr_2; break; default: - TRACE_CAP("PCI: find_pci_capability ERROR %u:%u:%u capability %#02x unknown header type\n", bus, device, function, capID); + TRACE_CAP("PCI: find_pci_capability ERROR %u:%u:%u capability " + "%#02x unknown header type\n", bus, device, function, capID); return B_ERROR; } + capPointer = ReadConfig(domain, bus, device, function, capPointer, 1); capPointer &= ~3; if (capPointer == 0) { - TRACE_CAP("PCI: find_pci_capability ERROR %u:%u:%u capability %#02x empty list\n", bus, device, function, capID); + TRACE_CAP("PCI: find_pci_capability ERROR %u:%u:%u capability %#02x " + "empty list\n", bus, device, function, capID); return B_NAME_NOT_FOUND; } for (int i = 0; i < 48; i++) { if (ReadConfig(domain, bus, device, function, capPointer, 1) == capID) { - *offset = capPointer; + if (offset != NULL) + *offset = capPointer; return B_OK; } @@ -1611,6 +1623,51 @@ PCI::FindCapability(PCIDev *device, uint8 capID, uint8 *offset) } +status_t +PCI::FindExtendedCapability(uint8 domain, uint8 bus, uint8 device, + uint8 function, uint16 capID, uint16 *offset) +{ + if (FindCapability(domain, bus, device, function, PCI_cap_id_pcie) + != B_OK) { + FLOW("PCI:FindExtendedCapability ERROR %u:%u:%u capability %#02x " + "not supported\n", bus, device, function, capID); + return B_ERROR; + } + uint16 capPointer = PCI_extended_capability; + uint32 capability = ReadConfig(domain, bus, device, function, + capPointer, 4); + + if (capability == 0 || capability == 0xffffffff) + return B_NAME_NOT_FOUND; + + for (int i = 0; i < 48; i++) { + if (PCI_extcap_id(capability) == capID) { + if (offset != NULL) + *offset = capPointer; + return B_OK; + } + + capPointer = PCI_extcap_next_ptr(capability) & ~3; + if (capPointer < PCI_extended_capability) + return B_NAME_NOT_FOUND; + capability = ReadConfig(domain, bus, device, function, + capPointer, 4); + } + + TRACE_CAP("PCI:FindExtendedCapability ERROR %u:%u:%u capability %#04x " + "circular list\n", bus, device, function, capID); + return B_ERROR; +} + + +status_t +PCI::FindExtendedCapability(PCIDev *device, uint16 capID, uint16 *offset) +{ + return FindExtendedCapability(device->domain, device->bus, device->device, + device->function, capID, offset); +} + + PCIDev * PCI::FindDevice(uint8 domain, uint8 bus, uint8 device, uint8 function) { diff --git a/src/add-ons/kernel/bus_managers/pci/pci.h b/src/add-ons/kernel/bus_managers/pci/pci.h index bb7f9dc..5ec5da5 100644 --- a/src/add-ons/kernel/bus_managers/pci/pci.h +++ b/src/add-ons/kernel/bus_managers/pci/pci.h @@ -92,9 +92,14 @@ public: status_t FindCapability(uint8 domain, uint8 bus, uint8 device, uint8 function, uint8 capID, - uint8 *offset); + uint8 *offset = NULL); status_t FindCapability(PCIDev *device, uint8 capID, - uint8 *offset); + uint8 *offset = NULL); + status_t FindExtendedCapability(uint8 domain, uint8 bus, + uint8 device, uint8 function, uint16 capID, + uint16 *offset = NULL); + status_t FindExtendedCapability(PCIDev *device, + uint16 capID, uint16 *offset = NULL); status_t ResolveVirtualBus(uint8 virtualBus, uint8 *domain, uint8 *bus); diff --git a/src/add-ons/kernel/bus_managers/pci/pci_private.h b/src/add-ons/kernel/bus_managers/pci/pci_private.h index 973dc41..a58220e 100644 --- a/src/add-ons/kernel/bus_managers/pci/pci_private.h +++ b/src/add-ons/kernel/bus_managers/pci/pci_private.h @@ -46,7 +46,10 @@ extern "C" { void * pci_ram_address(const void *physical_address_in_system_memory); -status_t pci_find_capability(uchar bus, uchar device, uchar function, uchar cap_id, uchar *offset); +status_t pci_find_capability(uint8 bus, uint8 device, uint8 function, + uint8 cap_id, uint8 *offset = NULL); +status_t pci_find_extended_capability(uint8 bus, uint8 device, uint8 function, + uint16 cap_id, uint16 *offset = NULL); status_t pci_reserve_device(uchar virtualBus, uchar device, uchar function, const char *driverName, void *nodeCookie);