diff options
Diffstat (limited to 'src/acpi/acpi_apic.c')
-rw-r--r-- | src/acpi/acpi_apic.c | 229 |
1 files changed, 229 insertions, 0 deletions
diff --git a/src/acpi/acpi_apic.c b/src/acpi/acpi_apic.c new file mode 100644 index 0000000000..38daaab514 --- /dev/null +++ b/src/acpi/acpi_apic.c @@ -0,0 +1,229 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include <acpi/acpi.h> +#include <arch/ioapic.h> +#include <arch/smp/mpspec.h> +#include <commonlib/sort.h> +#include <cpu/cpu.h> + +static int acpi_create_madt_lapic(acpi_madt_lapic_t *lapic, u8 cpu, u8 apic) +{ + lapic->type = LOCAL_APIC; /* Local APIC structure */ + lapic->length = sizeof(acpi_madt_lapic_t); + lapic->flags = (1 << 0); /* Processor/LAPIC enabled */ + lapic->processor_id = cpu; + lapic->apic_id = apic; + + return lapic->length; +} + +static int acpi_create_madt_lx2apic(acpi_madt_lx2apic_t *lapic, u32 cpu, u32 apic) +{ + lapic->type = LOCAL_X2APIC; /* Local APIC structure */ + lapic->reserved = 0; + lapic->length = sizeof(acpi_madt_lx2apic_t); + lapic->flags = (1 << 0); /* Processor/LAPIC enabled */ + lapic->processor_id = cpu; + lapic->x2apic_id = apic; + + return lapic->length; +} + +unsigned long acpi_create_madt_one_lapic(unsigned long current, u32 index, u32 lapic_id) +{ + if (lapic_id <= ACPI_MADT_MAX_LAPIC_ID) + current += acpi_create_madt_lapic((acpi_madt_lapic_t *)current, index, + lapic_id); + else + current += acpi_create_madt_lx2apic((acpi_madt_lx2apic_t *)current, index, + lapic_id); + + return current; +} + +/* Increase if necessary. Currently all x86 CPUs only have 2 SMP threads */ +#define MAX_THREAD_ID 1 +/* + * From ACPI 6.4 spec: + * "The advent of multi-threaded processors yielded multiple logical processors + * executing on common processor hardware. ACPI defines logical processors in + * an identical manner as physical processors. To ensure that non + * multi-threading aware OSPM implementations realize optimal performance on + * platforms containing multi-threaded processors, two guidelines should be + * followed. The first is the same as above, that is, OSPM should initialize + * processors in the order that they appear in the MADT. The second is that + * platform firmware should list the first logical processor of each of the + * individual multi-threaded processors in the MADT before listing any of the + * second logical processors. This approach should be used for all successive + * logical processors." + */ +static unsigned long acpi_create_madt_lapics(unsigned long current) +{ + struct device *cpu; + int index, apic_ids[CONFIG_MAX_CPUS] = {0}, num_cpus = 0, sort_start = 0; + for (unsigned int thread_id = 0; thread_id <= MAX_THREAD_ID; thread_id++) { + for (cpu = all_devices; cpu; cpu = cpu->next) { + if (!is_enabled_cpu(cpu)) + continue; + if (num_cpus >= ARRAY_SIZE(apic_ids)) + break; + if (cpu->path.apic.thread_id != thread_id) + continue; + apic_ids[num_cpus++] = cpu->path.apic.apic_id; + } + bubblesort(&apic_ids[sort_start], num_cpus - sort_start, NUM_ASCENDING); + sort_start = num_cpus; + } + for (index = 0; index < num_cpus; index++) + current = acpi_create_madt_one_lapic(current, index, apic_ids[index]); + + return current; +} + +static int acpi_create_madt_ioapic(acpi_madt_ioapic_t *ioapic, u8 id, u32 addr, + u32 gsi_base) +{ + ioapic->type = IO_APIC; /* I/O APIC structure */ + ioapic->length = sizeof(acpi_madt_ioapic_t); + ioapic->reserved = 0x00; + ioapic->gsi_base = gsi_base; + ioapic->ioapic_id = id; + ioapic->ioapic_addr = addr; + + return ioapic->length; +} + +/* For a system with multiple I/O APICs it's required that the one potentially + routing i8259 via ExtNMI delivery calls this first to get GSI #0. */ +int acpi_create_madt_ioapic_from_hw(acpi_madt_ioapic_t *ioapic, u32 addr) +{ + static u32 gsi_base; + u32 my_base; + u8 id = get_ioapic_id((void *)(uintptr_t)addr); + u8 count = ioapic_get_max_vectors((void *)(uintptr_t)addr); + + my_base = gsi_base; + gsi_base += count; + return acpi_create_madt_ioapic(ioapic, id, addr, my_base); +} + +static int acpi_create_madt_irqoverride(acpi_madt_irqoverride_t *irqoverride, + u8 bus, u8 source, u32 gsirq, u16 flags) +{ + irqoverride->type = IRQ_SOURCE_OVERRIDE; /* Interrupt source override */ + irqoverride->length = sizeof(acpi_madt_irqoverride_t); + irqoverride->bus = bus; + irqoverride->source = source; + irqoverride->gsirq = gsirq; + irqoverride->flags = flags; + + return irqoverride->length; +} + +static int acpi_create_madt_sci_override(acpi_madt_irqoverride_t *irqoverride) +{ + u8 gsi, irq, flags; + + ioapic_get_sci_pin(&gsi, &irq, &flags); + + if (!CONFIG(ACPI_HAVE_PCAT_8259)) + irq = gsi; + + irqoverride->type = IRQ_SOURCE_OVERRIDE; /* Interrupt source override */ + irqoverride->length = sizeof(acpi_madt_irqoverride_t); + irqoverride->bus = MP_BUS_ISA; + irqoverride->source = irq; + irqoverride->gsirq = gsi; + irqoverride->flags = flags; + + return irqoverride->length; +} + +unsigned long acpi_create_madt_ioapic_gsi0_default(unsigned long current) +{ + current += acpi_create_madt_ioapic_from_hw((acpi_madt_ioapic_t *)current, IO_APIC_ADDR); + + current += acpi_create_madt_irqoverride((void *)current, MP_BUS_ISA, 0, 2, + MP_IRQ_TRIGGER_EDGE | MP_IRQ_POLARITY_HIGH); + + current += acpi_create_madt_sci_override((void *)current); + + return current; +} + +static int acpi_create_madt_lapic_nmi(acpi_madt_lapic_nmi_t *lapic_nmi, u8 cpu, + u16 flags, u8 lint) +{ + lapic_nmi->type = LOCAL_APIC_NMI; /* Local APIC NMI structure */ + lapic_nmi->length = sizeof(acpi_madt_lapic_nmi_t); + lapic_nmi->flags = flags; + lapic_nmi->processor_id = cpu; + lapic_nmi->lint = lint; + + return lapic_nmi->length; +} + +static int acpi_create_madt_lx2apic_nmi(acpi_madt_lx2apic_nmi_t *lapic_nmi, u32 cpu, + u16 flags, u8 lint) +{ + lapic_nmi->type = LOCAL_X2APIC_NMI; /* Local APIC NMI structure */ + lapic_nmi->length = sizeof(acpi_madt_lx2apic_nmi_t); + lapic_nmi->flags = flags; + lapic_nmi->processor_id = cpu; + lapic_nmi->lint = lint; + lapic_nmi->reserved[0] = 0; + lapic_nmi->reserved[1] = 0; + lapic_nmi->reserved[2] = 0; + + return lapic_nmi->length; +} + +unsigned long acpi_create_madt_lapic_nmis(unsigned long current) +{ + const u16 flags = MP_IRQ_TRIGGER_EDGE | MP_IRQ_POLARITY_HIGH; + + /* 1: LINT1 connect to NMI */ + /* create all subtables for processors */ + current += acpi_create_madt_lapic_nmi((acpi_madt_lapic_nmi_t *)current, + ACPI_MADT_LAPIC_NMI_ALL_PROCESSORS, flags, 1); + + if (!CONFIG(XAPIC_ONLY)) + current += acpi_create_madt_lx2apic_nmi((acpi_madt_lx2apic_nmi_t *)current, + ACPI_MADT_LX2APIC_NMI_ALL_PROCESSORS, flags, 1); + + return current; +} + +unsigned long acpi_create_madt_lapics_with_nmis(unsigned long current) +{ + current = acpi_create_madt_lapics(current); + current = acpi_create_madt_lapic_nmis(current); + return current; +} + +int acpi_create_srat_lapic(acpi_srat_lapic_t *lapic, u8 node, u8 apic) +{ + memset((void *)lapic, 0, sizeof(acpi_srat_lapic_t)); + + lapic->type = 0; /* Processor local APIC/SAPIC affinity structure */ + lapic->length = sizeof(acpi_srat_lapic_t); + lapic->flags = (1 << 0); /* Enabled (the use of this structure). */ + lapic->proximity_domain_7_0 = node; + /* TODO: proximity_domain_31_8, local SAPIC EID, clock domain. */ + lapic->apic_id = apic; + + return lapic->length; +} + +int acpi_create_srat_x2apic(acpi_srat_x2apic_t *x2apic, u32 node, u32 apic) +{ + memset((void *)x2apic, 0, sizeof(acpi_srat_x2apic_t)); + + x2apic->type = 2; /* Processor x2APIC structure */ + x2apic->length = sizeof(acpi_srat_x2apic_t); + x2apic->flags = (1 << 0); /* Enabled (the use of this structure). */ + x2apic->proximity_domain = node; + x2apic->x2apic_id = apic; + + return x2apic->length; +} |