xref: /aosp_15_r20/external/coreboot/src/acpi/acpi_apic.c (revision b9411a12aaaa7e1e6a6fb7c5e057f44ee179a49c)
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>
8 #include <device/device.h>
9 
acpi_create_madt_lapic(acpi_madt_lapic_t * lapic,u8 cpu,u8 apic)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 = ACPI_MADT_LAPIC_ENABLED;
15 	lapic->processor_id = cpu;
16 	lapic->apic_id = apic;
17 
18 	return lapic->length;
19 }
20 
acpi_create_madt_lx2apic(acpi_madt_lx2apic_t * lapic,u32 cpu,u32 apic)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 = ACPI_MADT_LAPIC_ENABLED;
27 	lapic->processor_id = cpu;
28 	lapic->x2apic_id = apic;
29 
30 	return lapic->length;
31 }
32 
acpi_create_madt_one_lapic(unsigned long current,u32 index,u32 lapic_id)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  */
acpi_create_madt_lapics(unsigned long current)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 
acpi_create_madt_ioapic(acpi_madt_ioapic_t * ioapic,u8 id,u32 addr,u32 gsi_base)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. */
acpi_create_madt_ioapic_from_hw(acpi_madt_ioapic_t * ioapic,u32 addr)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((uintptr_t)addr);
104 	u8 count = ioapic_get_max_vectors((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 
acpi_create_madt_irqoverride(acpi_madt_irqoverride_t * irqoverride,u8 bus,u8 source,u32 gsirq,u16 flags)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 
acpi_create_madt_sci_override(acpi_madt_irqoverride_t * irqoverride)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 	/* In systems without 8259, the SCI_INT field in the FADT contains the SCI GSI number
131 	   instead of the 8259 IRQ number */
132 	if (!CONFIG(ACPI_HAVE_PCAT_8259))
133 		irq = gsi;
134 
135 	return acpi_create_madt_irqoverride(irqoverride, MP_BUS_ISA, irq, gsi, flags);
136 }
137 
acpi_create_madt_ioapic_gsi0_default(unsigned long current)138 static unsigned long acpi_create_madt_ioapic_gsi0_default(unsigned long current)
139 {
140 	current += acpi_create_madt_ioapic_from_hw((acpi_madt_ioapic_t *)current, IO_APIC_ADDR);
141 
142 	current += acpi_create_madt_irqoverride((void *)current, MP_BUS_ISA, 0, 2,
143 						MP_IRQ_TRIGGER_EDGE | MP_IRQ_POLARITY_HIGH);
144 
145 	current += acpi_create_madt_sci_override((void *)current);
146 
147 	return current;
148 }
149 
acpi_create_madt_lapic_nmi(acpi_madt_lapic_nmi_t * lapic_nmi,u8 cpu,u16 flags,u8 lint)150 static int acpi_create_madt_lapic_nmi(acpi_madt_lapic_nmi_t *lapic_nmi, u8 cpu,
151 				u16 flags, u8 lint)
152 {
153 	lapic_nmi->type = LOCAL_APIC_NMI; /* Local APIC NMI structure */
154 	lapic_nmi->length = sizeof(acpi_madt_lapic_nmi_t);
155 	lapic_nmi->flags = flags;
156 	lapic_nmi->processor_id = cpu;
157 	lapic_nmi->lint = lint;
158 
159 	return lapic_nmi->length;
160 }
161 
acpi_create_madt_lx2apic_nmi(acpi_madt_lx2apic_nmi_t * lapic_nmi,u32 cpu,u16 flags,u8 lint)162 static int acpi_create_madt_lx2apic_nmi(acpi_madt_lx2apic_nmi_t *lapic_nmi, u32 cpu,
163 				 u16 flags, u8 lint)
164 {
165 	lapic_nmi->type = LOCAL_X2APIC_NMI; /* Local APIC NMI structure */
166 	lapic_nmi->length = sizeof(acpi_madt_lx2apic_nmi_t);
167 	lapic_nmi->flags = flags;
168 	lapic_nmi->processor_id = cpu;
169 	lapic_nmi->lint = lint;
170 	lapic_nmi->reserved[0] = 0;
171 	lapic_nmi->reserved[1] = 0;
172 	lapic_nmi->reserved[2] = 0;
173 
174 	return lapic_nmi->length;
175 }
176 
acpi_create_madt_lapic_nmis(unsigned long current)177 unsigned long acpi_create_madt_lapic_nmis(unsigned long current)
178 {
179 	const u16 flags = MP_IRQ_TRIGGER_EDGE | MP_IRQ_POLARITY_HIGH;
180 
181 	/* 1: LINT1 connect to NMI */
182 	/* create all subtables for processors */
183 	current += acpi_create_madt_lapic_nmi((acpi_madt_lapic_nmi_t *)current,
184 			ACPI_MADT_LAPIC_NMI_ALL_PROCESSORS, flags, 1);
185 
186 	if (!CONFIG(XAPIC_ONLY))
187 		current += acpi_create_madt_lx2apic_nmi((acpi_madt_lx2apic_nmi_t *)current,
188 			ACPI_MADT_LX2APIC_NMI_ALL_PROCESSORS, flags, 1);
189 
190 	return current;
191 }
192 
acpi_create_madt_lapics_with_nmis(unsigned long current)193 static unsigned long acpi_create_madt_lapics_with_nmis(unsigned long current)
194 {
195 	current = acpi_create_madt_lapics(current);
196 	current = acpi_create_madt_lapic_nmis(current);
197 	return current;
198 }
199 
acpi_create_srat_lapic(acpi_srat_lapic_t * lapic,u8 node,u8 apic)200 int acpi_create_srat_lapic(acpi_srat_lapic_t *lapic, u8 node, u8 apic)
201 {
202 	memset((void *)lapic, 0, sizeof(acpi_srat_lapic_t));
203 
204 	lapic->type = 0; /* Processor local APIC/SAPIC affinity structure */
205 	lapic->length = sizeof(acpi_srat_lapic_t);
206 	lapic->flags = (1 << 0); /* Enabled (the use of this structure). */
207 	lapic->proximity_domain_7_0 = node;
208 	/* TODO: proximity_domain_31_8, local SAPIC EID, clock domain. */
209 	lapic->apic_id = apic;
210 
211 	return lapic->length;
212 }
213 
acpi_create_srat_x2apic(acpi_srat_x2apic_t * x2apic,u32 node,u32 apic)214 int acpi_create_srat_x2apic(acpi_srat_x2apic_t *x2apic, u32 node, u32 apic)
215 {
216 	memset((void *)x2apic, 0, sizeof(acpi_srat_x2apic_t));
217 
218 	x2apic->type = 2; /* Processor x2APIC structure */
219 	x2apic->length = sizeof(acpi_srat_x2apic_t);
220 	x2apic->flags = (1 << 0); /* Enabled (the use of this structure). */
221 	x2apic->proximity_domain = node;
222 	x2apic->x2apic_id = apic;
223 
224 	return x2apic->length;
225 }
226 
acpi_arch_fill_madt(acpi_madt_t * madt,unsigned long current)227 unsigned long acpi_arch_fill_madt(acpi_madt_t *madt, unsigned long current)
228 {
229 	madt->lapic_addr = cpu_get_lapic_addr();
230 
231 	if (CONFIG(ACPI_HAVE_PCAT_8259))
232 		madt->flags |= ACPI_MADT_PCAT_COMPAT;
233 
234 	if (CONFIG(ACPI_COMMON_MADT_LAPIC))
235 		current = acpi_create_madt_lapics_with_nmis(current);
236 
237 	if (CONFIG(ACPI_COMMON_MADT_IOAPIC))
238 		current = acpi_create_madt_ioapic_gsi0_default(current);
239 
240 	return current;
241 }
242