Arthur Heymans | 92a3b67 | 2023-06-22 21:30:58 +0200 | [diff] [blame] | 1 | /* SPDX-License-Identifier: GPL-2.0-only */ |
| 2 | |
| 3 | #include <acpi/acpi.h> |
| 4 | #include <arch/ioapic.h> |
| 5 | #include <arch/smp/mpspec.h> |
| 6 | #include <commonlib/sort.h> |
| 7 | #include <cpu/cpu.h> |
Felix Held | 4a9ed70 | 2023-12-05 22:09:14 +0100 | [diff] [blame] | 8 | #include <device/device.h> |
Arthur Heymans | 92a3b67 | 2023-06-22 21:30:58 +0200 | [diff] [blame] | 9 | |
| 10 | static int acpi_create_madt_lapic(acpi_madt_lapic_t *lapic, u8 cpu, u8 apic) |
| 11 | { |
| 12 | lapic->type = LOCAL_APIC; /* Local APIC structure */ |
| 13 | lapic->length = sizeof(acpi_madt_lapic_t); |
| 14 | lapic->flags = (1 << 0); /* Processor/LAPIC enabled */ |
| 15 | lapic->processor_id = cpu; |
| 16 | lapic->apic_id = apic; |
| 17 | |
| 18 | return lapic->length; |
| 19 | } |
| 20 | |
| 21 | static int acpi_create_madt_lx2apic(acpi_madt_lx2apic_t *lapic, u32 cpu, u32 apic) |
| 22 | { |
| 23 | lapic->type = LOCAL_X2APIC; /* Local APIC structure */ |
| 24 | lapic->reserved = 0; |
| 25 | lapic->length = sizeof(acpi_madt_lx2apic_t); |
| 26 | lapic->flags = (1 << 0); /* Processor/LAPIC enabled */ |
| 27 | lapic->processor_id = cpu; |
| 28 | lapic->x2apic_id = apic; |
| 29 | |
| 30 | return lapic->length; |
| 31 | } |
| 32 | |
| 33 | unsigned long acpi_create_madt_one_lapic(unsigned long current, u32 index, u32 lapic_id) |
| 34 | { |
| 35 | if (lapic_id <= ACPI_MADT_MAX_LAPIC_ID) |
| 36 | current += acpi_create_madt_lapic((acpi_madt_lapic_t *)current, index, |
| 37 | lapic_id); |
| 38 | else |
| 39 | current += acpi_create_madt_lx2apic((acpi_madt_lx2apic_t *)current, index, |
| 40 | lapic_id); |
| 41 | |
| 42 | return current; |
| 43 | } |
| 44 | |
| 45 | /* Increase if necessary. Currently all x86 CPUs only have 2 SMP threads */ |
| 46 | #define MAX_THREAD_ID 1 |
| 47 | /* |
| 48 | * From ACPI 6.4 spec: |
| 49 | * "The advent of multi-threaded processors yielded multiple logical processors |
| 50 | * executing on common processor hardware. ACPI defines logical processors in |
| 51 | * an identical manner as physical processors. To ensure that non |
| 52 | * multi-threading aware OSPM implementations realize optimal performance on |
| 53 | * platforms containing multi-threaded processors, two guidelines should be |
| 54 | * followed. The first is the same as above, that is, OSPM should initialize |
| 55 | * processors in the order that they appear in the MADT. The second is that |
| 56 | * platform firmware should list the first logical processor of each of the |
| 57 | * individual multi-threaded processors in the MADT before listing any of the |
| 58 | * second logical processors. This approach should be used for all successive |
| 59 | * logical processors." |
| 60 | */ |
| 61 | static unsigned long acpi_create_madt_lapics(unsigned long current) |
| 62 | { |
| 63 | struct device *cpu; |
| 64 | int index, apic_ids[CONFIG_MAX_CPUS] = {0}, num_cpus = 0, sort_start = 0; |
| 65 | for (unsigned int thread_id = 0; thread_id <= MAX_THREAD_ID; thread_id++) { |
| 66 | for (cpu = all_devices; cpu; cpu = cpu->next) { |
| 67 | if (!is_enabled_cpu(cpu)) |
| 68 | continue; |
| 69 | if (num_cpus >= ARRAY_SIZE(apic_ids)) |
| 70 | break; |
| 71 | if (cpu->path.apic.thread_id != thread_id) |
| 72 | continue; |
| 73 | apic_ids[num_cpus++] = cpu->path.apic.apic_id; |
| 74 | } |
| 75 | bubblesort(&apic_ids[sort_start], num_cpus - sort_start, NUM_ASCENDING); |
| 76 | sort_start = num_cpus; |
| 77 | } |
| 78 | for (index = 0; index < num_cpus; index++) |
| 79 | current = acpi_create_madt_one_lapic(current, index, apic_ids[index]); |
| 80 | |
| 81 | return current; |
| 82 | } |
| 83 | |
| 84 | static int acpi_create_madt_ioapic(acpi_madt_ioapic_t *ioapic, u8 id, u32 addr, |
| 85 | u32 gsi_base) |
| 86 | { |
| 87 | ioapic->type = IO_APIC; /* I/O APIC structure */ |
| 88 | ioapic->length = sizeof(acpi_madt_ioapic_t); |
| 89 | ioapic->reserved = 0x00; |
| 90 | ioapic->gsi_base = gsi_base; |
| 91 | ioapic->ioapic_id = id; |
| 92 | ioapic->ioapic_addr = addr; |
| 93 | |
| 94 | return ioapic->length; |
| 95 | } |
| 96 | |
| 97 | /* For a system with multiple I/O APICs it's required that the one potentially |
| 98 | routing i8259 via ExtNMI delivery calls this first to get GSI #0. */ |
| 99 | int acpi_create_madt_ioapic_from_hw(acpi_madt_ioapic_t *ioapic, u32 addr) |
| 100 | { |
| 101 | static u32 gsi_base; |
| 102 | u32 my_base; |
| 103 | u8 id = get_ioapic_id((void *)(uintptr_t)addr); |
| 104 | u8 count = ioapic_get_max_vectors((void *)(uintptr_t)addr); |
| 105 | |
| 106 | my_base = gsi_base; |
| 107 | gsi_base += count; |
| 108 | return acpi_create_madt_ioapic(ioapic, id, addr, my_base); |
| 109 | } |
| 110 | |
| 111 | static int acpi_create_madt_irqoverride(acpi_madt_irqoverride_t *irqoverride, |
| 112 | u8 bus, u8 source, u32 gsirq, u16 flags) |
| 113 | { |
| 114 | irqoverride->type = IRQ_SOURCE_OVERRIDE; /* Interrupt source override */ |
| 115 | irqoverride->length = sizeof(acpi_madt_irqoverride_t); |
| 116 | irqoverride->bus = bus; |
| 117 | irqoverride->source = source; |
| 118 | irqoverride->gsirq = gsirq; |
| 119 | irqoverride->flags = flags; |
| 120 | |
| 121 | return irqoverride->length; |
| 122 | } |
| 123 | |
| 124 | static int acpi_create_madt_sci_override(acpi_madt_irqoverride_t *irqoverride) |
| 125 | { |
| 126 | u8 gsi, irq, flags; |
| 127 | |
| 128 | ioapic_get_sci_pin(&gsi, &irq, &flags); |
| 129 | |
| 130 | if (!CONFIG(ACPI_HAVE_PCAT_8259)) |
| 131 | irq = gsi; |
| 132 | |
| 133 | irqoverride->type = IRQ_SOURCE_OVERRIDE; /* Interrupt source override */ |
| 134 | irqoverride->length = sizeof(acpi_madt_irqoverride_t); |
| 135 | irqoverride->bus = MP_BUS_ISA; |
| 136 | irqoverride->source = irq; |
| 137 | irqoverride->gsirq = gsi; |
| 138 | irqoverride->flags = flags; |
| 139 | |
| 140 | return irqoverride->length; |
| 141 | } |
| 142 | |
Arthur Heymans | cd46e5f | 2023-06-22 21:34:16 +0200 | [diff] [blame] | 143 | static unsigned long acpi_create_madt_ioapic_gsi0_default(unsigned long current) |
Arthur Heymans | 92a3b67 | 2023-06-22 21:30:58 +0200 | [diff] [blame] | 144 | { |
| 145 | current += acpi_create_madt_ioapic_from_hw((acpi_madt_ioapic_t *)current, IO_APIC_ADDR); |
| 146 | |
| 147 | current += acpi_create_madt_irqoverride((void *)current, MP_BUS_ISA, 0, 2, |
| 148 | MP_IRQ_TRIGGER_EDGE | MP_IRQ_POLARITY_HIGH); |
| 149 | |
| 150 | current += acpi_create_madt_sci_override((void *)current); |
| 151 | |
| 152 | return current; |
| 153 | } |
| 154 | |
| 155 | static int acpi_create_madt_lapic_nmi(acpi_madt_lapic_nmi_t *lapic_nmi, u8 cpu, |
| 156 | u16 flags, u8 lint) |
| 157 | { |
| 158 | lapic_nmi->type = LOCAL_APIC_NMI; /* Local APIC NMI structure */ |
| 159 | lapic_nmi->length = sizeof(acpi_madt_lapic_nmi_t); |
| 160 | lapic_nmi->flags = flags; |
| 161 | lapic_nmi->processor_id = cpu; |
| 162 | lapic_nmi->lint = lint; |
| 163 | |
| 164 | return lapic_nmi->length; |
| 165 | } |
| 166 | |
| 167 | static int acpi_create_madt_lx2apic_nmi(acpi_madt_lx2apic_nmi_t *lapic_nmi, u32 cpu, |
| 168 | u16 flags, u8 lint) |
| 169 | { |
| 170 | lapic_nmi->type = LOCAL_X2APIC_NMI; /* Local APIC NMI structure */ |
| 171 | lapic_nmi->length = sizeof(acpi_madt_lx2apic_nmi_t); |
| 172 | lapic_nmi->flags = flags; |
| 173 | lapic_nmi->processor_id = cpu; |
| 174 | lapic_nmi->lint = lint; |
| 175 | lapic_nmi->reserved[0] = 0; |
| 176 | lapic_nmi->reserved[1] = 0; |
| 177 | lapic_nmi->reserved[2] = 0; |
| 178 | |
| 179 | return lapic_nmi->length; |
| 180 | } |
| 181 | |
| 182 | unsigned long acpi_create_madt_lapic_nmis(unsigned long current) |
| 183 | { |
| 184 | const u16 flags = MP_IRQ_TRIGGER_EDGE | MP_IRQ_POLARITY_HIGH; |
| 185 | |
| 186 | /* 1: LINT1 connect to NMI */ |
| 187 | /* create all subtables for processors */ |
| 188 | current += acpi_create_madt_lapic_nmi((acpi_madt_lapic_nmi_t *)current, |
| 189 | ACPI_MADT_LAPIC_NMI_ALL_PROCESSORS, flags, 1); |
| 190 | |
| 191 | if (!CONFIG(XAPIC_ONLY)) |
| 192 | current += acpi_create_madt_lx2apic_nmi((acpi_madt_lx2apic_nmi_t *)current, |
| 193 | ACPI_MADT_LX2APIC_NMI_ALL_PROCESSORS, flags, 1); |
| 194 | |
| 195 | return current; |
| 196 | } |
| 197 | |
Arthur Heymans | cd46e5f | 2023-06-22 21:34:16 +0200 | [diff] [blame] | 198 | static unsigned long acpi_create_madt_lapics_with_nmis(unsigned long current) |
Arthur Heymans | 92a3b67 | 2023-06-22 21:30:58 +0200 | [diff] [blame] | 199 | { |
| 200 | current = acpi_create_madt_lapics(current); |
| 201 | current = acpi_create_madt_lapic_nmis(current); |
| 202 | return current; |
| 203 | } |
| 204 | |
| 205 | int acpi_create_srat_lapic(acpi_srat_lapic_t *lapic, u8 node, u8 apic) |
| 206 | { |
| 207 | memset((void *)lapic, 0, sizeof(acpi_srat_lapic_t)); |
| 208 | |
| 209 | lapic->type = 0; /* Processor local APIC/SAPIC affinity structure */ |
| 210 | lapic->length = sizeof(acpi_srat_lapic_t); |
| 211 | lapic->flags = (1 << 0); /* Enabled (the use of this structure). */ |
| 212 | lapic->proximity_domain_7_0 = node; |
| 213 | /* TODO: proximity_domain_31_8, local SAPIC EID, clock domain. */ |
| 214 | lapic->apic_id = apic; |
| 215 | |
| 216 | return lapic->length; |
| 217 | } |
| 218 | |
| 219 | int acpi_create_srat_x2apic(acpi_srat_x2apic_t *x2apic, u32 node, u32 apic) |
| 220 | { |
| 221 | memset((void *)x2apic, 0, sizeof(acpi_srat_x2apic_t)); |
| 222 | |
| 223 | x2apic->type = 2; /* Processor x2APIC structure */ |
| 224 | x2apic->length = sizeof(acpi_srat_x2apic_t); |
| 225 | x2apic->flags = (1 << 0); /* Enabled (the use of this structure). */ |
| 226 | x2apic->proximity_domain = node; |
| 227 | x2apic->x2apic_id = apic; |
| 228 | |
| 229 | return x2apic->length; |
| 230 | } |
Arthur Heymans | cd46e5f | 2023-06-22 21:34:16 +0200 | [diff] [blame] | 231 | |
| 232 | unsigned long acpi_arch_fill_madt(acpi_madt_t *madt, unsigned long current) |
| 233 | { |
| 234 | madt->lapic_addr = cpu_get_lapic_addr(); |
| 235 | |
| 236 | if (CONFIG(ACPI_HAVE_PCAT_8259)) |
| 237 | madt->flags |= 1; |
| 238 | |
| 239 | if (CONFIG(ACPI_COMMON_MADT_LAPIC)) |
| 240 | current = acpi_create_madt_lapics_with_nmis(current); |
| 241 | |
| 242 | if (CONFIG(ACPI_COMMON_MADT_IOAPIC)) |
| 243 | current = acpi_create_madt_ioapic_gsi0_default(current); |
| 244 | |
| 245 | return current; |
| 246 | } |