[bus][pci] Support for dynamically assigning BARs and bridges if needed

In the case of platforms where a bios or firmware has not already
assigned all the resources, do so. Requires the platform supply one or
more ranges of physical address space and IO that can be mapped into
BARs.

Handles iterating through bridges, computing the sizes of all the
peripherals downstream and rolling that up as well.
This commit is contained in:
Travis Geiselbrecht
2022-01-30 15:12:38 -08:00
parent 36e73e0fac
commit f1431b81d0
19 changed files with 1058 additions and 80 deletions

View File

@@ -8,6 +8,7 @@
#pragma once
#include <lk/compiler.h>
#include <stdbool.h>
#include <sys/types.h>
__BEGIN_CDECLS
@@ -31,11 +32,15 @@ void register_int_handler_msi(unsigned int vector, int_handler handler, void *ar
/* Allocate a run of interrupts with alignment log2 in a platform specific way.
* Used for PCI MSI and possibly other use cases.
*/
status_t platform_allocate_interrupts(size_t count, uint align_log2, unsigned int *vector);
status_t platform_allocate_interrupts(size_t count, uint align_log2, bool msi, unsigned int *vector);
/* Map the incoming interrupt line number from the pci bus config to raw
* vector number, usable in the above apis.
*/
status_t platform_pci_int_to_vector(unsigned int pci_int, unsigned int *vector);
/* Ask the platform to compute for us the value to stuff in the MSI address and data fields. */
status_t platform_compute_msi_values(unsigned int vector, unsigned int cpu, bool edge,
uint64_t *msi_address_out, uint16_t *msi_data_out);
__END_CDECLS

View File

@@ -170,9 +170,9 @@ status_t platform_pci_int_to_vector(unsigned int pci_int, unsigned int *vector)
return NO_ERROR;
}
status_t platform_allocate_interrupts(size_t count, uint align_log2, unsigned int *vector) {
LTRACEF("count %zu, align %u\n", count, align_log2);
if (align_log2 > 1) {
status_t platform_allocate_interrupts(size_t count, uint align_log2, bool msi, unsigned int *vector) {
LTRACEF("count %zu align %u msi %d\n", count, align_log2, msi);
if (align_log2 > 0) {
PANIC_UNIMPLEMENTED;
}
@@ -196,3 +196,15 @@ status_t platform_allocate_interrupts(size_t count, uint align_log2, unsigned in
return err;
}
status_t platform_compute_msi_values(unsigned int vector, unsigned int cpu, bool edge,
uint64_t *msi_address_out, uint16_t *msi_data_out) {
// only handle edge triggered at the moment
DEBUG_ASSERT(edge);
*msi_data_out = (vector & 0xff) | (0<<15); // edge triggered
*msi_address_out = 0xfee00000 | (cpu << 12);
return NO_ERROR;
}

View File

@@ -39,6 +39,9 @@ static const MemMapEntry base_memmap[] = {
[VIRT_SMMU] = { 0x09050000, 0x00020000 },
[VIRT_PCDIMM_ACPI] = { 0x09070000, MEMORY_HOTPLUG_IO_LEN },
[VIRT_ACPI_GED] = { 0x09080000, ACPI_GED_EVT_SEL_LEN },
[VIRT_NVDIMM_ACPI] = { 0x09090000, NVDIMM_ACPI_IO_LEN},
[VIRT_PVTIME] = { 0x090a0000, 0x00010000 },
[VIRT_SECURE_GPIO] = { 0x090b0000, 0x00001000 },
[VIRT_MMIO] = { 0x0a000000, 0x00000200 },
/* ...repeating for a total of NUM_VIRTIO_TRANSPORTS, each of that size */
[VIRT_PLATFORM_BUS] = { 0x0c000000, 0x02000000 },
@@ -94,7 +97,9 @@ static const int a15irqmap[] = {
#define ARM_GENERIC_TIMER_VIRTUAL_INT 27
#define ARM_GENERIC_TIMER_PHYSICAL_INT 30
#define UART0_INT (32 + 1)
#define VIRTIO0_INT (32 + 16)
#define PCIE_INT_BASE (32 + 3)
#define VIRTIO0_INT_BASE (32 + 16)
#define MSI_INT_BASE (32 + 48)
#define MAX_INT 128

View File

@@ -189,24 +189,30 @@ void platform_init(void) {
if (err == NO_ERROR) {
// add some additional resources to the pci bus manager in case it needs to configure
if (pcie_state.info.io_len > 0) {
pci_bus_mgr_add_resource(PCI_RESOURCE_IO_RANGE, pcie_state.info.io_base_mmio, pcie_state.info.io_base, pcie_state.info.io_len);
// we can only deal with a mapping of io base 0 to the mmio base
DEBUG_ASSERT(pcie_state.info.io_base == 0);
pci_bus_mgr_add_resource(PCI_RESOURCE_IO_RANGE, pcie_state.info.io_base, pcie_state.info.io_len);
// TODO: set the mmio base somehow so pci knows what to do with it
}
if (pcie_state.info.mmio_len > 0) {
pci_bus_mgr_add_resource(PCI_RESOURCE_MMIO_RANGE, pcie_state.info.mmio_base, 0, pcie_state.info.mmio_len);
pci_bus_mgr_add_resource(PCI_RESOURCE_MMIO_RANGE, pcie_state.info.mmio_base, pcie_state.info.mmio_len);
}
if (pcie_state.info.mmio64_len > 0) {
pci_bus_mgr_add_resource(PCI_RESOURCE_MMIO64_RANGE, pcie_state.info.mmio64_base, 0, pcie_state.info.mmio64_len);
pci_bus_mgr_add_resource(PCI_RESOURCE_MMIO64_RANGE, pcie_state.info.mmio64_base, pcie_state.info.mmio64_len);
}
// start the bus manager
pci_bus_mgr_init();
pci_bus_mgr_assign_resources();
}
}
/* detect any virtio devices */
uint virtio_irqs[NUM_VIRTIO_TRANSPORTS];
for (int i = 0; i < NUM_VIRTIO_TRANSPORTS; i++) {
virtio_irqs[i] = VIRTIO0_INT + i;
virtio_irqs[i] = VIRTIO0_INT_BASE + i;
}
virtio_mmio_detect((void *)VIRTIO_BASE, NUM_VIRTIO_TRANSPORTS, virtio_irqs, 0x200);
@@ -235,11 +241,63 @@ void platform_init(void) {
}
status_t platform_pci_int_to_vector(unsigned int pci_int, unsigned int *vector) {
// at the moment there's no translation between PCI IRQs and native irqs
*vector = pci_int;
// only 4 legacy vectors supported, within PCIE_INT_BASE and PCIE_INT_BASE + 3
if (pci_int >= 4) {
return ERR_OUT_OF_RANGE;
}
*vector = pci_int + PCIE_INT_BASE;
return NO_ERROR;
}
status_t platform_allocate_interrupts(size_t count, uint align_log2, unsigned int *vector) {
return ERR_NOT_SUPPORTED;
status_t platform_allocate_interrupts(size_t count, uint align_log2, bool msi, unsigned int *vector) {
TRACEF("count %zu align %u msi %d\n", count, align_log2, msi);
// TODO: handle nonzero alignment, count > 0, and add locking
// list of allocated msi interrupts
static uint64_t msi_bitmap = 0;
// cannot handle allocating for anything but MSI interrupts
if (!msi) {
return ERR_NOT_SUPPORTED;
}
// cannot deal with alignment yet
DEBUG_ASSERT(align_log2 == 0);
DEBUG_ASSERT(count == 1);
// make a copy of the bitmap
int allocated = -1;
for (size_t i = 0; i < sizeof(msi_bitmap) * 8; i++) {
if ((msi_bitmap & (1UL << i)) == 0) {
msi_bitmap |= (1UL << i);
allocated = i;
break;
}
}
if (allocated < 0) {
return ERR_NOT_FOUND;
}
allocated += MSI_INT_BASE;
TRACEF("allocated msi at %u\n", allocated);
*vector = allocated;
return NO_ERROR;
}
status_t platform_compute_msi_values(unsigned int vector, unsigned int cpu, bool edge,
uint64_t *msi_address_out, uint16_t *msi_data_out) {
// only handle edge triggered at the moment
DEBUG_ASSERT(edge);
// only handle cpu 0
DEBUG_ASSERT(cpu == 0);
// TODO: call through to the appropriate gic driver to deal with GICv2 vs v3
*msi_data_out = vector;
*msi_address_out = 0x08020040; // address of the GICv2 MSI port
return NO_ERROR;
}

View File

@@ -161,17 +161,23 @@ void platform_init(void) {
if (err == NO_ERROR) {
// add some additional resources to the pci bus manager in case it needs to configure
if (pcie_state.info.io_len > 0) {
pci_bus_mgr_add_resource(PCI_RESOURCE_IO_RANGE, pcie_state.info.io_base_mmio, pcie_state.info.io_base, pcie_state.info.io_len);
// we can only deal with a mapping of io base 0 to the mmio base
DEBUG_ASSERT(pcie_state.info.io_base == 0);
pci_bus_mgr_add_resource(PCI_RESOURCE_IO_RANGE, pcie_state.info.io_base, pcie_state.info.io_len);
// TODO: set the mmio base somehow so pci knows what to do with it
}
if (pcie_state.info.mmio_len > 0) {
pci_bus_mgr_add_resource(PCI_RESOURCE_MMIO_RANGE, pcie_state.info.mmio_base, 0, pcie_state.info.mmio_len);
pci_bus_mgr_add_resource(PCI_RESOURCE_MMIO_RANGE, pcie_state.info.mmio_base, pcie_state.info.mmio_len);
}
if (pcie_state.info.mmio64_len > 0) {
pci_bus_mgr_add_resource(PCI_RESOURCE_MMIO64_RANGE, pcie_state.info.mmio64_base, 0, pcie_state.info.mmio64_len);
pci_bus_mgr_add_resource(PCI_RESOURCE_MMIO64_RANGE, pcie_state.info.mmio64_base, pcie_state.info.mmio64_len);
}
// start the bus manager
pci_bus_mgr_init();
pci_bus_mgr_assign_resources(); // add some additional resources to the pci bus manager in case it needs to configure
};
}

View File

@@ -102,6 +102,11 @@ status_t platform_pci_int_to_vector(unsigned int pci_int, unsigned int *vector)
return NO_ERROR;
}
status_t platform_allocate_interrupts(size_t count, uint align_log2, unsigned int *vector) {
status_t platform_allocate_interrupts(size_t count, uint align_log2, bool msi, unsigned int *vector) {
return ERR_NOT_SUPPORTED;
}
status_t platform_compute_msi_values(unsigned int vector, unsigned int cpu, bool edge,
uint64_t *msi_address_out, uint16_t *msi_data_out) {
return ERR_NOT_SUPPORTED;
}