| /* 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; |
| } |
| |
| static 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; |
| } |
| |
| static 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; |
| } |
| |
| unsigned long acpi_arch_fill_madt(acpi_madt_t *madt, unsigned long current) |
| { |
| madt->lapic_addr = cpu_get_lapic_addr(); |
| |
| if (CONFIG(ACPI_HAVE_PCAT_8259)) |
| madt->flags |= 1; |
| |
| if (CONFIG(ACPI_COMMON_MADT_LAPIC)) |
| current = acpi_create_madt_lapics_with_nmis(current); |
| |
| if (CONFIG(ACPI_COMMON_MADT_IOAPIC)) |
| current = acpi_create_madt_ioapic_gsi0_default(current); |
| |
| return current; |
| } |