1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (C) 2005 Intel Corporation |
4 | * Copyright (C) 2009 Hewlett-Packard Development Company, L.P. |
5 | * |
6 | * Alex Chiang <achiang@hp.com> |
7 | * - Unified x86/ia64 implementations |
8 | * |
9 | * I/O APIC hotplug support |
10 | * Yinghai Lu <yinghai@kernel.org> |
11 | * Jiang Liu <jiang.liu@intel.com> |
12 | */ |
13 | #include <linux/export.h> |
14 | #include <linux/acpi.h> |
15 | #include <acpi/processor.h> |
16 | |
17 | static struct acpi_table_madt *get_madt_table(void) |
18 | { |
19 | static struct acpi_table_madt *madt; |
20 | static int read_madt; |
21 | |
22 | if (!read_madt) { |
23 | if (ACPI_FAILURE(acpi_get_table(ACPI_SIG_MADT, 0, |
24 | (struct acpi_table_header **)&madt))) |
25 | madt = NULL; |
26 | read_madt++; |
27 | } |
28 | |
29 | return madt; |
30 | } |
31 | |
32 | static int map_lapic_id(struct acpi_subtable_header *entry, |
33 | u32 acpi_id, phys_cpuid_t *apic_id) |
34 | { |
35 | struct acpi_madt_local_apic *lapic = |
36 | container_of(entry, struct acpi_madt_local_apic, header); |
37 | |
38 | if (!(lapic->lapic_flags & ACPI_MADT_ENABLED)) |
39 | return -ENODEV; |
40 | |
41 | if (lapic->processor_id != acpi_id) |
42 | return -EINVAL; |
43 | |
44 | *apic_id = lapic->id; |
45 | return 0; |
46 | } |
47 | |
48 | static int map_x2apic_id(struct acpi_subtable_header *entry, |
49 | int device_declaration, u32 acpi_id, phys_cpuid_t *apic_id) |
50 | { |
51 | struct acpi_madt_local_x2apic *apic = |
52 | container_of(entry, struct acpi_madt_local_x2apic, header); |
53 | |
54 | if (!(apic->lapic_flags & ACPI_MADT_ENABLED)) |
55 | return -ENODEV; |
56 | |
57 | if (device_declaration && (apic->uid == acpi_id)) { |
58 | *apic_id = apic->local_apic_id; |
59 | return 0; |
60 | } |
61 | |
62 | return -EINVAL; |
63 | } |
64 | |
65 | static int map_lsapic_id(struct acpi_subtable_header *entry, |
66 | int device_declaration, u32 acpi_id, phys_cpuid_t *apic_id) |
67 | { |
68 | struct acpi_madt_local_sapic *lsapic = |
69 | container_of(entry, struct acpi_madt_local_sapic, header); |
70 | |
71 | if (!(lsapic->lapic_flags & ACPI_MADT_ENABLED)) |
72 | return -ENODEV; |
73 | |
74 | if (device_declaration) { |
75 | if ((entry->length < 16) || (lsapic->uid != acpi_id)) |
76 | return -EINVAL; |
77 | } else if (lsapic->processor_id != acpi_id) |
78 | return -EINVAL; |
79 | |
80 | *apic_id = (lsapic->id << 8) | lsapic->eid; |
81 | return 0; |
82 | } |
83 | |
84 | /* |
85 | * Retrieve the ARM CPU physical identifier (MPIDR) |
86 | */ |
87 | static int map_gicc_mpidr(struct acpi_subtable_header *entry, |
88 | int device_declaration, u32 acpi_id, phys_cpuid_t *mpidr) |
89 | { |
90 | struct acpi_madt_generic_interrupt *gicc = |
91 | container_of(entry, struct acpi_madt_generic_interrupt, header); |
92 | |
93 | if (!acpi_gicc_is_usable(gicc)) |
94 | return -ENODEV; |
95 | |
96 | /* device_declaration means Device object in DSDT, in the |
97 | * GIC interrupt model, logical processors are required to |
98 | * have a Processor Device object in the DSDT, so we should |
99 | * check device_declaration here |
100 | */ |
101 | if (device_declaration && (gicc->uid == acpi_id)) { |
102 | *mpidr = gicc->arm_mpidr; |
103 | return 0; |
104 | } |
105 | |
106 | return -EINVAL; |
107 | } |
108 | |
109 | /* |
110 | * Retrieve the RISC-V hartid for the processor |
111 | */ |
112 | static int map_rintc_hartid(struct acpi_subtable_header *entry, |
113 | int device_declaration, u32 acpi_id, |
114 | phys_cpuid_t *hartid) |
115 | { |
116 | struct acpi_madt_rintc *rintc = |
117 | container_of(entry, struct acpi_madt_rintc, header); |
118 | |
119 | if (!(rintc->flags & ACPI_MADT_ENABLED)) |
120 | return -ENODEV; |
121 | |
122 | /* device_declaration means Device object in DSDT, in the |
123 | * RISC-V, logical processors are required to |
124 | * have a Processor Device object in the DSDT, so we should |
125 | * check device_declaration here |
126 | */ |
127 | if (device_declaration && rintc->uid == acpi_id) { |
128 | *hartid = rintc->hart_id; |
129 | return 0; |
130 | } |
131 | |
132 | return -EINVAL; |
133 | } |
134 | |
135 | /* |
136 | * Retrieve LoongArch CPU physical id |
137 | */ |
138 | static int map_core_pic_id(struct acpi_subtable_header *entry, |
139 | int device_declaration, u32 acpi_id, phys_cpuid_t *phys_id) |
140 | { |
141 | struct acpi_madt_core_pic *core_pic = |
142 | container_of(entry, struct acpi_madt_core_pic, header); |
143 | |
144 | if (!(core_pic->flags & ACPI_MADT_ENABLED)) |
145 | return -ENODEV; |
146 | |
147 | /* device_declaration means Device object in DSDT, in LoongArch |
148 | * system, logical processor acpi_id is required in _UID property |
149 | * of DSDT table, so we should check device_declaration here |
150 | */ |
151 | if (device_declaration && (core_pic->processor_id == acpi_id)) { |
152 | *phys_id = core_pic->core_id; |
153 | return 0; |
154 | } |
155 | |
156 | return -EINVAL; |
157 | } |
158 | |
159 | static phys_cpuid_t map_madt_entry(struct acpi_table_madt *madt, |
160 | int type, u32 acpi_id) |
161 | { |
162 | unsigned long madt_end, entry; |
163 | phys_cpuid_t phys_id = PHYS_CPUID_INVALID; /* CPU hardware ID */ |
164 | |
165 | if (!madt) |
166 | return phys_id; |
167 | |
168 | entry = (unsigned long)madt; |
169 | madt_end = entry + madt->header.length; |
170 | |
171 | /* Parse all entries looking for a match. */ |
172 | |
173 | entry += sizeof(struct acpi_table_madt); |
174 | while (entry + sizeof(struct acpi_subtable_header) < madt_end) { |
175 | struct acpi_subtable_header * = |
176 | (struct acpi_subtable_header *)entry; |
177 | if (header->type == ACPI_MADT_TYPE_LOCAL_APIC) { |
178 | if (!map_lapic_id(entry: header, acpi_id, apic_id: &phys_id)) |
179 | break; |
180 | } else if (header->type == ACPI_MADT_TYPE_LOCAL_X2APIC) { |
181 | if (!map_x2apic_id(entry: header, device_declaration: type, acpi_id, apic_id: &phys_id)) |
182 | break; |
183 | } else if (header->type == ACPI_MADT_TYPE_LOCAL_SAPIC) { |
184 | if (!map_lsapic_id(entry: header, device_declaration: type, acpi_id, apic_id: &phys_id)) |
185 | break; |
186 | } else if (header->type == ACPI_MADT_TYPE_GENERIC_INTERRUPT) { |
187 | if (!map_gicc_mpidr(entry: header, device_declaration: type, acpi_id, mpidr: &phys_id)) |
188 | break; |
189 | } else if (header->type == ACPI_MADT_TYPE_RINTC) { |
190 | if (!map_rintc_hartid(entry: header, device_declaration: type, acpi_id, hartid: &phys_id)) |
191 | break; |
192 | } else if (header->type == ACPI_MADT_TYPE_CORE_PIC) { |
193 | if (!map_core_pic_id(entry: header, device_declaration: type, acpi_id, phys_id: &phys_id)) |
194 | break; |
195 | } |
196 | entry += header->length; |
197 | } |
198 | return phys_id; |
199 | } |
200 | |
201 | phys_cpuid_t __init acpi_map_madt_entry(u32 acpi_id) |
202 | { |
203 | struct acpi_table_madt *madt = NULL; |
204 | phys_cpuid_t rv; |
205 | |
206 | acpi_get_table(ACPI_SIG_MADT, instance: 0, |
207 | out_table: (struct acpi_table_header **)&madt); |
208 | if (!madt) |
209 | return PHYS_CPUID_INVALID; |
210 | |
211 | rv = map_madt_entry(madt, type: 1, acpi_id); |
212 | |
213 | acpi_put_table(table: (struct acpi_table_header *)madt); |
214 | |
215 | return rv; |
216 | } |
217 | |
218 | static phys_cpuid_t map_mat_entry(acpi_handle handle, int type, u32 acpi_id) |
219 | { |
220 | struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; |
221 | union acpi_object *obj; |
222 | struct acpi_subtable_header *; |
223 | phys_cpuid_t phys_id = PHYS_CPUID_INVALID; |
224 | |
225 | if (ACPI_FAILURE(acpi_evaluate_object(handle, "_MAT" , NULL, &buffer))) |
226 | goto exit; |
227 | |
228 | if (!buffer.length || !buffer.pointer) |
229 | goto exit; |
230 | |
231 | obj = buffer.pointer; |
232 | if (obj->type != ACPI_TYPE_BUFFER || |
233 | obj->buffer.length < sizeof(struct acpi_subtable_header)) { |
234 | goto exit; |
235 | } |
236 | |
237 | header = (struct acpi_subtable_header *)obj->buffer.pointer; |
238 | if (header->type == ACPI_MADT_TYPE_LOCAL_APIC) |
239 | map_lapic_id(entry: header, acpi_id, apic_id: &phys_id); |
240 | else if (header->type == ACPI_MADT_TYPE_LOCAL_SAPIC) |
241 | map_lsapic_id(entry: header, device_declaration: type, acpi_id, apic_id: &phys_id); |
242 | else if (header->type == ACPI_MADT_TYPE_LOCAL_X2APIC) |
243 | map_x2apic_id(entry: header, device_declaration: type, acpi_id, apic_id: &phys_id); |
244 | else if (header->type == ACPI_MADT_TYPE_GENERIC_INTERRUPT) |
245 | map_gicc_mpidr(entry: header, device_declaration: type, acpi_id, mpidr: &phys_id); |
246 | else if (header->type == ACPI_MADT_TYPE_CORE_PIC) |
247 | map_core_pic_id(entry: header, device_declaration: type, acpi_id, phys_id: &phys_id); |
248 | |
249 | exit: |
250 | kfree(objp: buffer.pointer); |
251 | return phys_id; |
252 | } |
253 | |
254 | phys_cpuid_t acpi_get_phys_id(acpi_handle handle, int type, u32 acpi_id) |
255 | { |
256 | phys_cpuid_t phys_id; |
257 | |
258 | phys_id = map_mat_entry(handle, type, acpi_id); |
259 | if (invalid_phys_cpuid(phys_id)) |
260 | phys_id = map_madt_entry(madt: get_madt_table(), type, acpi_id); |
261 | |
262 | return phys_id; |
263 | } |
264 | EXPORT_SYMBOL_GPL(acpi_get_phys_id); |
265 | |
266 | int acpi_map_cpuid(phys_cpuid_t phys_id, u32 acpi_id) |
267 | { |
268 | #ifdef CONFIG_SMP |
269 | int i; |
270 | #endif |
271 | |
272 | if (invalid_phys_cpuid(phys_id)) { |
273 | /* |
274 | * On UP processor, there is no _MAT or MADT table. |
275 | * So above phys_id is always set to PHYS_CPUID_INVALID. |
276 | * |
277 | * BIOS may define multiple CPU handles even for UP processor. |
278 | * For example, |
279 | * |
280 | * Scope (_PR) |
281 | * { |
282 | * Processor (CPU0, 0x00, 0x00000410, 0x06) {} |
283 | * Processor (CPU1, 0x01, 0x00000410, 0x06) {} |
284 | * Processor (CPU2, 0x02, 0x00000410, 0x06) {} |
285 | * Processor (CPU3, 0x03, 0x00000410, 0x06) {} |
286 | * } |
287 | * |
288 | * Ignores phys_id and always returns 0 for the processor |
289 | * handle with acpi id 0 if nr_cpu_ids is 1. |
290 | * This should be the case if SMP tables are not found. |
291 | * Return -EINVAL for other CPU's handle. |
292 | */ |
293 | if (nr_cpu_ids <= 1 && acpi_id == 0) |
294 | return acpi_id; |
295 | else |
296 | return -EINVAL; |
297 | } |
298 | |
299 | #ifdef CONFIG_SMP |
300 | for_each_possible_cpu(i) { |
301 | if (cpu_physical_id(i) == phys_id) |
302 | return i; |
303 | } |
304 | #else |
305 | /* In UP kernel, only processor 0 is valid */ |
306 | if (phys_id == 0) |
307 | return phys_id; |
308 | #endif |
309 | return -ENODEV; |
310 | } |
311 | |
312 | int acpi_get_cpuid(acpi_handle handle, int type, u32 acpi_id) |
313 | { |
314 | phys_cpuid_t phys_id; |
315 | |
316 | phys_id = acpi_get_phys_id(handle, type, acpi_id); |
317 | |
318 | return acpi_map_cpuid(phys_id, acpi_id); |
319 | } |
320 | EXPORT_SYMBOL_GPL(acpi_get_cpuid); |
321 | |
322 | #ifdef CONFIG_ACPI_HOTPLUG_IOAPIC |
323 | static int get_ioapic_id(struct acpi_subtable_header *entry, u32 gsi_base, |
324 | u64 *phys_addr, int *ioapic_id) |
325 | { |
326 | struct acpi_madt_io_apic *ioapic = (struct acpi_madt_io_apic *)entry; |
327 | |
328 | if (ioapic->global_irq_base != gsi_base) |
329 | return 0; |
330 | |
331 | *phys_addr = ioapic->address; |
332 | *ioapic_id = ioapic->id; |
333 | return 1; |
334 | } |
335 | |
336 | static int parse_madt_ioapic_entry(u32 gsi_base, u64 *phys_addr) |
337 | { |
338 | struct acpi_subtable_header *hdr; |
339 | unsigned long madt_end, entry; |
340 | struct acpi_table_madt *madt; |
341 | int apic_id = -1; |
342 | |
343 | madt = get_madt_table(); |
344 | if (!madt) |
345 | return apic_id; |
346 | |
347 | entry = (unsigned long)madt; |
348 | madt_end = entry + madt->header.length; |
349 | |
350 | /* Parse all entries looking for a match. */ |
351 | entry += sizeof(struct acpi_table_madt); |
352 | while (entry + sizeof(struct acpi_subtable_header) < madt_end) { |
353 | hdr = (struct acpi_subtable_header *)entry; |
354 | if (hdr->type == ACPI_MADT_TYPE_IO_APIC && |
355 | get_ioapic_id(entry: hdr, gsi_base, phys_addr, ioapic_id: &apic_id)) |
356 | break; |
357 | else |
358 | entry += hdr->length; |
359 | } |
360 | |
361 | return apic_id; |
362 | } |
363 | |
364 | static int parse_mat_ioapic_entry(acpi_handle handle, u32 gsi_base, |
365 | u64 *phys_addr) |
366 | { |
367 | struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; |
368 | struct acpi_subtable_header *; |
369 | union acpi_object *obj; |
370 | int apic_id = -1; |
371 | |
372 | if (ACPI_FAILURE(acpi_evaluate_object(handle, "_MAT" , NULL, &buffer))) |
373 | goto exit; |
374 | |
375 | if (!buffer.length || !buffer.pointer) |
376 | goto exit; |
377 | |
378 | obj = buffer.pointer; |
379 | if (obj->type != ACPI_TYPE_BUFFER || |
380 | obj->buffer.length < sizeof(struct acpi_subtable_header)) |
381 | goto exit; |
382 | |
383 | header = (struct acpi_subtable_header *)obj->buffer.pointer; |
384 | if (header->type == ACPI_MADT_TYPE_IO_APIC) |
385 | get_ioapic_id(entry: header, gsi_base, phys_addr, ioapic_id: &apic_id); |
386 | |
387 | exit: |
388 | kfree(objp: buffer.pointer); |
389 | return apic_id; |
390 | } |
391 | |
392 | /** |
393 | * acpi_get_ioapic_id - Get IOAPIC ID and physical address matching @gsi_base |
394 | * @handle: ACPI object for IOAPIC device |
395 | * @gsi_base: GSI base to match with |
396 | * @phys_addr: Pointer to store physical address of matching IOAPIC record |
397 | * |
398 | * Walk resources returned by ACPI_MAT method, then ACPI MADT table, to search |
399 | * for an ACPI IOAPIC record matching @gsi_base. |
400 | * Return IOAPIC id and store physical address in @phys_addr if found a match, |
401 | * otherwise return <0. |
402 | */ |
403 | int acpi_get_ioapic_id(acpi_handle handle, u32 gsi_base, u64 *phys_addr) |
404 | { |
405 | int apic_id; |
406 | |
407 | apic_id = parse_mat_ioapic_entry(handle, gsi_base, phys_addr); |
408 | if (apic_id == -1) |
409 | apic_id = parse_madt_ioapic_entry(gsi_base, phys_addr); |
410 | |
411 | return apic_id; |
412 | } |
413 | #endif /* CONFIG_ACPI_HOTPLUG_IOAPIC */ |
414 | |