pci/x86: Reimplement PCIe configuration mechanism using ECAM.

This allows us to drop the PCI-specific ACPI management entirely.
Confirmed working on x86, and the fallbacks when there is no ACPI
also still work.

Change-Id: I6dac9f5539f99b934b17b341634ce22628bc66fd
Reviewed-on: https://review.haiku-os.org/c/haiku/+/6470
Reviewed-by: X512 <danger_mail@list.ru>
Reviewed-by: waddlesplash <waddlesplash@gmail.com>
This commit is contained in:
Augustin Cavalier 2023-05-24 17:54:33 -04:00 committed by waddlesplash
parent 19ae638fb6
commit 1a88b571ca
9 changed files with 99 additions and 426 deletions

View File

@ -120,6 +120,7 @@ ECAMPCIController::RegisterDevice(device_node* parent)
}
#if !defined(ECAM_PCI_CONTROLLER_NO_INIT)
status_t
ECAMPCIController::InitDriver(device_node* node, ECAMPCIController*& outDriver)
{
@ -155,6 +156,7 @@ ECAMPCIController::UninitDriver()
{
delete this;
}
#endif
addr_t

View File

@ -140,8 +140,14 @@ public:
protected:
status_t ReadResourceInfo() final;
status_t ReadResourceInfo(device_node* parent);
uint8 fStartBusNumber{};
uint8 fEndBusNumber{};
private:
friend class X86PCIControllerMethPcie;
static acpi_status AcpiCrsScanCallback(acpi_resource *res, void *context);
inline acpi_status AcpiCrsScanCallbackInt(acpi_resource *res);
};

View File

@ -18,7 +18,13 @@ status_t
ECAMPCIControllerACPI::ReadResourceInfo()
{
DeviceNodePutter<&gDeviceManager> parent(gDeviceManager->get_parent_node(fNode));
return ReadResourceInfo(parent.Get());
}
status_t
ECAMPCIControllerACPI::ReadResourceInfo(device_node* parent)
{
dprintf("initialize PCI controller from ACPI\n");
acpi_module_info* acpiModule;
@ -30,7 +36,7 @@ ECAMPCIControllerACPI::ReadResourceInfo()
acpi_mcfg *mcfg;
CHECK_RET(acpiModule->get_table(ACPI_MCFG_SIGNATURE, 0, (void**)&mcfg));
CHECK_RET(gDeviceManager->get_driver(parent.Get(), (driver_module_info**)&acpiDeviceModule,
CHECK_RET(gDeviceManager->get_driver(parent, (driver_module_info**)&acpiDeviceModule,
(void**)&acpiDevice));
acpi_status acpi_res = acpiDeviceModule->walk_resources(acpiDevice, (char *)"_CRS",
@ -54,10 +60,10 @@ ECAMPCIControllerACPI::ReadResourceInfo()
continue;
}
uint8 startBusNumber = alloc->start_bus_number;
uint8 endBusNumber = alloc->end_bus_number;
fStartBusNumber = alloc->start_bus_number;
fEndBusNumber = alloc->end_bus_number;
fRegsLen = (endBusNumber - startBusNumber + 1) << 20;
fRegsLen = (fEndBusNumber - fStartBusNumber + 1) << 20;
fRegsArea.SetTo(map_physical_memory("PCI Config MMIO",
alloc->address, fRegsLen, B_ANY_KERNEL_ADDRESS,
B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA, (void **)&fRegs));

View File

@ -1,15 +1,17 @@
SubDir HAIKU_TOP src add-ons kernel busses pci x86 ;
SubDirC++Flags -fno-rtti ;
SubDirC++Flags -fno-rtti -DECAM_PCI_CONTROLLER_NO_INIT ;
UsePrivateKernelHeaders ;
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 ;
SubDirHdrs $(HAIKU_TOP) src system kernel arch generic ;
KernelAddon <pci>x86 :
pci_acpi.cpp
X86PCIController.cpp
ECAMPCIController.cpp
ECAMPCIControllerACPI.cpp
kernel_interface.cpp
;
SEARCH on [ FGristFiles ECAMPCIController.cpp ECAMPCIControllerACPI.cpp ]
= [ FDirName $(HAIKU_TOP) src add-ons kernel busses pci ecam ] ;

View File

@ -5,11 +5,9 @@
#include "X86PCIController.h"
#include "pci_acpi.h"
#include <AutoDeleterDrivers.h>
#include <util/AutoLock.h>
#include "acpi.h"
#include <string.h>
#include <new>
@ -240,6 +238,13 @@ status_t X86PCIControllerMeth1::GetMaxBusDevices(int32& count)
}
status_t
X86PCIControllerMeth1::Finalize()
{
return B_OK;
}
//#pragma mark - X86PCIControllerMeth2
@ -327,46 +332,39 @@ status_t X86PCIControllerMeth2::GetMaxBusDevices(int32& count)
}
status_t
X86PCIControllerMeth2::Finalize()
{
return B_OK;
}
//#pragma mark - X86PCIControllerMethPcie
#define PCIE_VADDR(base, bus, slot, func, reg) ((base) + \
((((bus) & 0xff) << 20) | (((slot) & 0x1f) << 15) | \
(((func) & 0x7) << 12) | ((reg) & 0xfff)))
status_t
X86PCIControllerMethPcie::InitDriverInt(device_node* node)
{
CHECK_RET(X86PCIController::InitDriverInt(node));
status_t status = X86PCIController::InitDriverInt(node);
if (status != B_OK)
return status;
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 **)&fPCIeBase);
if (mcfgArea < 0)
break;
fStartBusNumber = alloc->StartBusNumber;
fEndBusNumber = alloc->EndBusNumber;
dprintf("PCI: mechanism pcie controller found\n");
return B_OK;
}
}
// search ACPI
device_node *acpiNode = NULL;
{
device_node* deviceRoot = gDeviceManager->get_root_node();
device_attr acpiAttrs[] = {
{ B_DEVICE_BUS, B_STRING_TYPE, { .string = "acpi" }},
{ ACPI_DEVICE_HID_ITEM, B_STRING_TYPE, { .string = "PNP0A08" }},
{ NULL }
};
if (gDeviceManager->find_child_node(deviceRoot, acpiAttrs, &acpiNode) != B_OK)
return ENODEV;
}
return B_ERROR;
status = fECAMPCIController.ReadResourceInfo(acpiNode);
gDeviceManager->put_node(acpiNode);
return status;
}
@ -376,27 +374,12 @@ X86PCIControllerMethPcie::ReadConfig(
uint16 offset, uint8 size, uint32 &value)
{
// fallback to mechanism 1 for out of range busses
if (bus < fStartBusNumber || bus > fEndBusNumber) {
if (bus < fECAMPCIController.fStartBusNumber || bus > fECAMPCIController.fEndBusNumber) {
return X86PCIControllerMeth1::ReadConfig(bus, device, function, offset,
size, value);
}
addr_t address = PCIE_VADDR(fPCIeBase, 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:
return B_ERROR;
}
return B_OK;
return fECAMPCIController.ReadConfig(bus, device, function, offset, size, value);
}
@ -406,32 +389,32 @@ X86PCIControllerMethPcie::WriteConfig(
uint16 offset, uint8 size, uint32 value)
{
// fallback to mechanism 1 for out of range busses
if (bus < fStartBusNumber || bus > fEndBusNumber) {
if (bus < fECAMPCIController.fStartBusNumber || bus > fECAMPCIController.fEndBusNumber) {
return X86PCIControllerMeth1::WriteConfig(bus, device, function, offset,
size, value);
}
addr_t address = PCIE_VADDR(fPCIeBase, 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:
return B_ERROR;
}
return B_OK;
return fECAMPCIController.WriteConfig(bus, device, function, offset, size, value);
}
status_t X86PCIControllerMethPcie::GetMaxBusDevices(int32& count)
status_t
X86PCIControllerMethPcie::GetMaxBusDevices(int32& count)
{
count = 32;
return fECAMPCIController.GetMaxBusDevices(count);
}
status_t
X86PCIControllerMethPcie::GetRange(uint32 index, pci_resource_range* range)
{
return fECAMPCIController.GetRange(index, range);
}
status_t
X86PCIControllerMethPcie::Finalize()
{
// No need to call fECAMPCIController.Finalize(): IRQ routing is handled by IOAPIC on x86.
return B_OK;
}

View File

@ -12,6 +12,8 @@
#include <AutoDeleterOS.h>
#include <lock.h>
#include "../ecam/ECAMPCIController.h"
#define CHECK_RET(err) {status_t _err = (err); if (_err < B_OK) return _err;}
@ -37,6 +39,10 @@ public:
virtual status_t GetMaxBusDevices(int32& count) = 0;
virtual status_t GetRange(uint32 index, pci_resource_range* range);
virtual status_t Finalize() = 0;
status_t ReadIrq(
uint8 bus, uint8 device, uint8 function,
uint8 pin, uint8& irq);
@ -45,8 +51,6 @@ public:
uint8 bus, uint8 device, uint8 function,
uint8 pin, uint8 irq);
status_t GetRange(uint32 index, pci_resource_range* range);
protected:
static status_t CreateDriver(device_node* node, X86PCIController* driver,
X86PCIController*& driverOut);
@ -56,10 +60,6 @@ protected:
spinlock fLock = B_SPINLOCK_INITIALIZER;
device_node* fNode{};
addr_t fPCIeBase{};
uint8 fStartBusNumber{};
uint8 fEndBusNumber{};
};
@ -78,6 +78,8 @@ public:
uint16 offset, uint8 size, uint32 value) override;
status_t GetMaxBusDevices(int32& count) override;
status_t Finalize() override;
};
@ -96,6 +98,8 @@ public:
uint16 offset, uint8 size, uint32 value) final;
status_t GetMaxBusDevices(int32& count) final;
status_t Finalize() final;
};
@ -114,6 +118,13 @@ public:
uint16 offset, uint8 size, uint32 value) final;
status_t GetMaxBusDevices(int32& count) final;
status_t GetRange(uint32 index, pci_resource_range* range) final;
status_t Finalize() final;
private:
ECAMPCIControllerACPI fECAMPCIController;
};

View File

@ -51,6 +51,9 @@ pci_controller_module_info gPciControllerDriver = {
},
.get_range = [](void *cookie, uint32 index, pci_resource_range* range) {
return static_cast<X86PCIController*>(cookie)->GetRange(index, range);
},
.finalize = [](void *cookie) {
return static_cast<X86PCIController*>(cookie)->Finalize();
}
};

View File

@ -1,313 +0,0 @@
/*
* Copyright 2011, Rene Gollent, rene@gollent.com.
* Copyright 2008, Dustin Howett, dustin.howett@gmail.com. All rights reserved.
* Copyright 2007, Michael Lotz, mmlr@mlotz.ch
* Copyright 2004-2005, Axel Dörfler, axeld@pinc-software.de.
* 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 <boot_item.h>
#include <acpi.h>
//#define TRACE_ACPI
#ifdef TRACE_ACPI
# define TRACE(x...) dprintf(x)
#else
# define TRACE(x...) ;
#endif
#define ERROR(x...) dprintf(x)
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) {
ERROR("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%" B_PRIx32 "\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;
ERROR("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) {
ERROR("acpi: couldn't map rsdt header\n");
return B_ERROR;
}
if (strncmp(rsdt->signature, ACPI_RSDT_SIGNATURE, 4) != 0) {
delete_area(rsdtArea);
rsdt = NULL;
ERROR("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: %" B_PRIu32 "\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) {
ERROR("acpi: root system description table is empty\n");
return NULL;
}
TRACE("acpi: searching %" B_PRId32 " 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 pointer from boot loader, if any
phys_addr_t* acpiRootPointer = (phys_addr_t*)get_boot_item("ACPI_ROOT_POINTER", NULL);
if (acpiRootPointer != NULL) {
acpi_rsdp* rsdp = NULL;
area_id rsdpArea = map_physical_memory("acpi rsdp",
*acpiRootPointer, B_PAGE_SIZE,
B_ANY_KERNEL_ADDRESS, B_KERNEL_READ_AREA, (void **)&rsdp);
if (rsdpArea < B_OK) {
ERROR("acpi_init: couldn't map %s\n", strerror(rsdpArea));
} else {
status_t status = B_ERROR;
if (strncmp((char*)rsdp, ACPI_RSDP_SIGNATURE, 8) == 0)
status = acpi_check_rsdt(rsdp);
delete_area(rsdpArea);
if (status == B_OK)
return;
}
}
// Try to find the ACPI RSDP in EBDA
uint8* start = NULL;
area_id ebdaArea = map_physical_memory("ebda", 0, B_PAGE_SIZE,
B_ANY_KERNEL_ADDRESS, B_KERNEL_READ_AREA, (void **)&start);
if (ebdaArea < 0) {
ERROR("acpi_init: couldn't map 1K: %s\n", strerror(ebdaArea));
} else {
phys_addr_t ebda = *(uint16*)&start[0x40e];
ebda <<= 4;
TRACE("acpi_init: 0x%lx\n", ebda);
delete_area(ebdaArea);
if (ebda > 0x400) {
acpi_scan_spots[0].start = ebda;
acpi_scan_spots[0].stop = ebda + 0x1000;
acpi_scan_spots[0].length = 0x1000;
// will be scanned first
}
}
// 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%" B_PRIx32 ", limit 0x%" B_PRIx32 "\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) {
ERROR("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);
}
}

View File

@ -1,27 +0,0 @@
/*
* Copyright 2005, Axel Dörfler, axeld@pinc-software.de. 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 */