1457c8996SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
24d5d4cd8SAlex Chiang /*
34d5d4cd8SAlex Chiang * Copyright (C) 2005 Intel Corporation
44d5d4cd8SAlex Chiang * Copyright (C) 2009 Hewlett-Packard Development Company, L.P.
54d5d4cd8SAlex Chiang *
64d5d4cd8SAlex Chiang * Alex Chiang <[email protected]>
74d5d4cd8SAlex Chiang * - Unified x86/ia64 implementations
8ecf5636dSYinghai Lu *
9ecf5636dSYinghai Lu * I/O APIC hotplug support
10ecf5636dSYinghai Lu * Yinghai Lu <[email protected]>
11ecf5636dSYinghai Lu * Jiang Liu <[email protected]>
124d5d4cd8SAlex Chiang */
13214f2c90SPaul Gortmaker #include <linux/export.h>
148b48463fSLv Zheng #include <linux/acpi.h>
154d5d4cd8SAlex Chiang #include <acpi/processor.h>
164d5d4cd8SAlex Chiang
get_madt_table(void)17ecf5636dSYinghai Lu static struct acpi_table_madt *get_madt_table(void)
18ecf5636dSYinghai Lu {
19ecf5636dSYinghai Lu static struct acpi_table_madt *madt;
20ecf5636dSYinghai Lu static int read_madt;
21ecf5636dSYinghai Lu
22ecf5636dSYinghai Lu if (!read_madt) {
23ecf5636dSYinghai Lu if (ACPI_FAILURE(acpi_get_table(ACPI_SIG_MADT, 0,
24ecf5636dSYinghai Lu (struct acpi_table_header **)&madt)))
25ecf5636dSYinghai Lu madt = NULL;
26ecf5636dSYinghai Lu read_madt++;
27ecf5636dSYinghai Lu }
28ecf5636dSYinghai Lu
29ecf5636dSYinghai Lu return madt;
30ecf5636dSYinghai Lu }
31ecf5636dSYinghai Lu
map_lapic_id(struct acpi_subtable_header * entry,u32 acpi_id,phys_cpuid_t * apic_id)3278ed8bd2SAlex Chiang static int map_lapic_id(struct acpi_subtable_header *entry,
3309c3f2bdSDou Liyang u32 acpi_id, phys_cpuid_t *apic_id)
3478ed8bd2SAlex Chiang {
3578ed8bd2SAlex Chiang struct acpi_madt_local_apic *lapic =
36ef86c3f4SFabian Frederick container_of(entry, struct acpi_madt_local_apic, header);
3711130736SAlex Chiang
3809c3f2bdSDou Liyang if (!(lapic->lapic_flags & ACPI_MADT_ENABLED))
39038d7b59SHanjun Guo return -ENODEV;
4011130736SAlex Chiang
4111130736SAlex Chiang if (lapic->processor_id != acpi_id)
42038d7b59SHanjun Guo return -EINVAL;
4311130736SAlex Chiang
4478ed8bd2SAlex Chiang *apic_id = lapic->id;
45038d7b59SHanjun Guo return 0;
4678ed8bd2SAlex Chiang }
4778ed8bd2SAlex Chiang
map_x2apic_id(struct acpi_subtable_header * entry,int device_declaration,u32 acpi_id,phys_cpuid_t * apic_id)4878ed8bd2SAlex Chiang static int map_x2apic_id(struct acpi_subtable_header *entry,
4909c3f2bdSDou Liyang int device_declaration, u32 acpi_id, phys_cpuid_t *apic_id)
5078ed8bd2SAlex Chiang {
5178ed8bd2SAlex Chiang struct acpi_madt_local_x2apic *apic =
52ef86c3f4SFabian Frederick container_of(entry, struct acpi_madt_local_x2apic, header);
5378ed8bd2SAlex Chiang
5409c3f2bdSDou Liyang if (!(apic->lapic_flags & ACPI_MADT_ENABLED))
55038d7b59SHanjun Guo return -ENODEV;
5678ed8bd2SAlex Chiang
57d6742095SAlex Chiang if (device_declaration && (apic->uid == acpi_id)) {
58d6742095SAlex Chiang *apic_id = apic->local_apic_id;
59038d7b59SHanjun Guo return 0;
6078ed8bd2SAlex Chiang }
6178ed8bd2SAlex Chiang
62038d7b59SHanjun Guo return -EINVAL;
6378ed8bd2SAlex Chiang }
6478ed8bd2SAlex Chiang
map_lsapic_id(struct acpi_subtable_header * entry,int device_declaration,u32 acpi_id,phys_cpuid_t * apic_id)6578ed8bd2SAlex Chiang static int map_lsapic_id(struct acpi_subtable_header *entry,
6609c3f2bdSDou Liyang int device_declaration, u32 acpi_id, phys_cpuid_t *apic_id)
6778ed8bd2SAlex Chiang {
6878ed8bd2SAlex Chiang struct acpi_madt_local_sapic *lsapic =
69ef86c3f4SFabian Frederick container_of(entry, struct acpi_madt_local_sapic, header);
7078ed8bd2SAlex Chiang
7109c3f2bdSDou Liyang if (!(lsapic->lapic_flags & ACPI_MADT_ENABLED))
72038d7b59SHanjun Guo return -ENODEV;
7378ed8bd2SAlex Chiang
7478ed8bd2SAlex Chiang if (device_declaration) {
75eae701ceSAlex Chiang if ((entry->length < 16) || (lsapic->uid != acpi_id))
76038d7b59SHanjun Guo return -EINVAL;
77eae701ceSAlex Chiang } else if (lsapic->processor_id != acpi_id)
78038d7b59SHanjun Guo return -EINVAL;
79eae701ceSAlex Chiang
80eae701ceSAlex Chiang *apic_id = (lsapic->id << 8) | lsapic->eid;
81038d7b59SHanjun Guo return 0;
8278ed8bd2SAlex Chiang }
8378ed8bd2SAlex Chiang
84020295b4SHanjun Guo /*
85020295b4SHanjun Guo * Retrieve the ARM CPU physical identifier (MPIDR)
86020295b4SHanjun Guo */
map_gicc_mpidr(struct acpi_subtable_header * entry,int device_declaration,u32 acpi_id,phys_cpuid_t * mpidr)87020295b4SHanjun Guo static int map_gicc_mpidr(struct acpi_subtable_header *entry,
8809c3f2bdSDou Liyang int device_declaration, u32 acpi_id, phys_cpuid_t *mpidr)
89020295b4SHanjun Guo {
90020295b4SHanjun Guo struct acpi_madt_generic_interrupt *gicc =
91020295b4SHanjun Guo container_of(entry, struct acpi_madt_generic_interrupt, header);
92020295b4SHanjun Guo
93*d633da5dSJames Morse if (!(gicc->flags &
94*d633da5dSJames Morse (ACPI_MADT_ENABLED | ACPI_MADT_GICC_ONLINE_CAPABLE)))
95020295b4SHanjun Guo return -ENODEV;
96020295b4SHanjun Guo
97020295b4SHanjun Guo /* device_declaration means Device object in DSDT, in the
98020295b4SHanjun Guo * GIC interrupt model, logical processors are required to
99020295b4SHanjun Guo * have a Processor Device object in the DSDT, so we should
100020295b4SHanjun Guo * check device_declaration here
101020295b4SHanjun Guo */
102020295b4SHanjun Guo if (device_declaration && (gicc->uid == acpi_id)) {
103020295b4SHanjun Guo *mpidr = gicc->arm_mpidr;
104020295b4SHanjun Guo return 0;
105020295b4SHanjun Guo }
106020295b4SHanjun Guo
107020295b4SHanjun Guo return -EINVAL;
108020295b4SHanjun Guo }
109020295b4SHanjun Guo
1108b7809e2SSunil V L /*
1118b7809e2SSunil V L * Retrieve the RISC-V hartid for the processor
1128b7809e2SSunil V L */
map_rintc_hartid(struct acpi_subtable_header * entry,int device_declaration,u32 acpi_id,phys_cpuid_t * hartid)1138b7809e2SSunil V L static int map_rintc_hartid(struct acpi_subtable_header *entry,
1148b7809e2SSunil V L int device_declaration, u32 acpi_id,
1158b7809e2SSunil V L phys_cpuid_t *hartid)
1168b7809e2SSunil V L {
1178b7809e2SSunil V L struct acpi_madt_rintc *rintc =
1188b7809e2SSunil V L container_of(entry, struct acpi_madt_rintc, header);
1198b7809e2SSunil V L
1208b7809e2SSunil V L if (!(rintc->flags & ACPI_MADT_ENABLED))
1218b7809e2SSunil V L return -ENODEV;
1228b7809e2SSunil V L
1238b7809e2SSunil V L /* device_declaration means Device object in DSDT, in the
1248b7809e2SSunil V L * RISC-V, logical processors are required to
1258b7809e2SSunil V L * have a Processor Device object in the DSDT, so we should
1268b7809e2SSunil V L * check device_declaration here
1278b7809e2SSunil V L */
1288b7809e2SSunil V L if (device_declaration && rintc->uid == acpi_id) {
1298b7809e2SSunil V L *hartid = rintc->hart_id;
1308b7809e2SSunil V L return 0;
1318b7809e2SSunil V L }
1328b7809e2SSunil V L
1338b7809e2SSunil V L return -EINVAL;
1348b7809e2SSunil V L }
1358b7809e2SSunil V L
136f6fcf03cSBibo Mao /*
137f6fcf03cSBibo Mao * Retrieve LoongArch CPU physical id
138f6fcf03cSBibo Mao */
map_core_pic_id(struct acpi_subtable_header * entry,int device_declaration,u32 acpi_id,phys_cpuid_t * phys_id)139f6fcf03cSBibo Mao static int map_core_pic_id(struct acpi_subtable_header *entry,
140f6fcf03cSBibo Mao int device_declaration, u32 acpi_id, phys_cpuid_t *phys_id)
141f6fcf03cSBibo Mao {
142f6fcf03cSBibo Mao struct acpi_madt_core_pic *core_pic =
143f6fcf03cSBibo Mao container_of(entry, struct acpi_madt_core_pic, header);
144f6fcf03cSBibo Mao
145f6fcf03cSBibo Mao if (!(core_pic->flags & ACPI_MADT_ENABLED))
146f6fcf03cSBibo Mao return -ENODEV;
147f6fcf03cSBibo Mao
148f6fcf03cSBibo Mao /* device_declaration means Device object in DSDT, in LoongArch
149f6fcf03cSBibo Mao * system, logical processor acpi_id is required in _UID property
150f6fcf03cSBibo Mao * of DSDT table, so we should check device_declaration here
151f6fcf03cSBibo Mao */
152f6fcf03cSBibo Mao if (device_declaration && (core_pic->processor_id == acpi_id)) {
153f6fcf03cSBibo Mao *phys_id = core_pic->core_id;
154f6fcf03cSBibo Mao return 0;
155f6fcf03cSBibo Mao }
156f6fcf03cSBibo Mao
157f6fcf03cSBibo Mao return -EINVAL;
158f6fcf03cSBibo Mao }
159f6fcf03cSBibo Mao
map_madt_entry(struct acpi_table_madt * madt,int type,u32 acpi_id)160fb7c2baeSDavid Daney static phys_cpuid_t map_madt_entry(struct acpi_table_madt *madt,
16109c3f2bdSDou Liyang int type, u32 acpi_id)
16278ed8bd2SAlex Chiang {
16378ed8bd2SAlex Chiang unsigned long madt_end, entry;
164828aef37SCatalin Marinas phys_cpuid_t phys_id = PHYS_CPUID_INVALID; /* CPU hardware ID */
16578ed8bd2SAlex Chiang
16678ed8bd2SAlex Chiang if (!madt)
167af8f3f51SHanjun Guo return phys_id;
16878ed8bd2SAlex Chiang
16978ed8bd2SAlex Chiang entry = (unsigned long)madt;
17078ed8bd2SAlex Chiang madt_end = entry + madt->header.length;
17178ed8bd2SAlex Chiang
17278ed8bd2SAlex Chiang /* Parse all entries looking for a match. */
17378ed8bd2SAlex Chiang
17478ed8bd2SAlex Chiang entry += sizeof(struct acpi_table_madt);
17578ed8bd2SAlex Chiang while (entry + sizeof(struct acpi_subtable_header) < madt_end) {
17678ed8bd2SAlex Chiang struct acpi_subtable_header *header =
17778ed8bd2SAlex Chiang (struct acpi_subtable_header *)entry;
17878ed8bd2SAlex Chiang if (header->type == ACPI_MADT_TYPE_LOCAL_APIC) {
17909c3f2bdSDou Liyang if (!map_lapic_id(header, acpi_id, &phys_id))
18078ed8bd2SAlex Chiang break;
18178ed8bd2SAlex Chiang } else if (header->type == ACPI_MADT_TYPE_LOCAL_X2APIC) {
18209c3f2bdSDou Liyang if (!map_x2apic_id(header, type, acpi_id, &phys_id))
18378ed8bd2SAlex Chiang break;
18478ed8bd2SAlex Chiang } else if (header->type == ACPI_MADT_TYPE_LOCAL_SAPIC) {
18509c3f2bdSDou Liyang if (!map_lsapic_id(header, type, acpi_id, &phys_id))
18678ed8bd2SAlex Chiang break;
187020295b4SHanjun Guo } else if (header->type == ACPI_MADT_TYPE_GENERIC_INTERRUPT) {
18809c3f2bdSDou Liyang if (!map_gicc_mpidr(header, type, acpi_id, &phys_id))
189020295b4SHanjun Guo break;
1908b7809e2SSunil V L } else if (header->type == ACPI_MADT_TYPE_RINTC) {
1918b7809e2SSunil V L if (!map_rintc_hartid(header, type, acpi_id, &phys_id))
1928b7809e2SSunil V L break;
193f6fcf03cSBibo Mao } else if (header->type == ACPI_MADT_TYPE_CORE_PIC) {
194f6fcf03cSBibo Mao if (!map_core_pic_id(header, type, acpi_id, &phys_id))
195f6fcf03cSBibo Mao break;
19678ed8bd2SAlex Chiang }
19778ed8bd2SAlex Chiang entry += header->length;
19878ed8bd2SAlex Chiang }
199af8f3f51SHanjun Guo return phys_id;
20078ed8bd2SAlex Chiang }
20178ed8bd2SAlex Chiang
acpi_map_madt_entry(u32 acpi_id)202fb7c2baeSDavid Daney phys_cpuid_t __init acpi_map_madt_entry(u32 acpi_id)
203fb7c2baeSDavid Daney {
204fb7c2baeSDavid Daney struct acpi_table_madt *madt = NULL;
205fb7c2baeSDavid Daney phys_cpuid_t rv;
206fb7c2baeSDavid Daney
2076b11d1d6SLv Zheng acpi_get_table(ACPI_SIG_MADT, 0,
2086b11d1d6SLv Zheng (struct acpi_table_header **)&madt);
209fb7c2baeSDavid Daney if (!madt)
210fb7c2baeSDavid Daney return PHYS_CPUID_INVALID;
211fb7c2baeSDavid Daney
21209c3f2bdSDou Liyang rv = map_madt_entry(madt, 1, acpi_id);
213fb7c2baeSDavid Daney
2146b11d1d6SLv Zheng acpi_put_table((struct acpi_table_header *)madt);
215fb7c2baeSDavid Daney
216fb7c2baeSDavid Daney return rv;
217fb7c2baeSDavid Daney }
218fb7c2baeSDavid Daney
acpi_get_madt_revision(void)21909c3f2bdSDou Liyang int __init acpi_get_madt_revision(void)
22078ed8bd2SAlex Chiang {
22178ed8bd2SAlex Chiang struct acpi_table_header *madt = NULL;
22278ed8bd2SAlex Chiang int revision;
22378ed8bd2SAlex Chiang
224828aef37SCatalin Marinas if (ACPI_FAILURE(acpi_get_table(ACPI_SIG_MADT, 0, &madt)))
22578ed8bd2SAlex Chiang return -EINVAL;
22678ed8bd2SAlex Chiang
22778ed8bd2SAlex Chiang revision = madt->revision;
22878ed8bd2SAlex Chiang
22978ed8bd2SAlex Chiang acpi_put_table(madt);
23078ed8bd2SAlex Chiang
23178ed8bd2SAlex Chiang return revision;
23278ed8bd2SAlex Chiang }
23378ed8bd2SAlex Chiang
map_mat_entry(acpi_handle handle,int type,u32 acpi_id)23478ed8bd2SAlex Chiang static phys_cpuid_t map_mat_entry(acpi_handle handle, int type, u32 acpi_id)
23578ed8bd2SAlex Chiang {
23678ed8bd2SAlex Chiang struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
23778ed8bd2SAlex Chiang union acpi_object *obj;
23878ed8bd2SAlex Chiang struct acpi_subtable_header *header;
23913ca62b2SJiang Liu phys_cpuid_t phys_id = PHYS_CPUID_INVALID;
24009c3f2bdSDou Liyang
24113ca62b2SJiang Liu if (ACPI_FAILURE(acpi_evaluate_object(handle, "_MAT", NULL, &buffer)))
24209c3f2bdSDou Liyang goto exit;
24313ca62b2SJiang Liu
24409c3f2bdSDou Liyang if (!buffer.length || !buffer.pointer)
245020295b4SHanjun Guo goto exit;
24609c3f2bdSDou Liyang
247f6fcf03cSBibo Mao obj = buffer.pointer;
248f6fcf03cSBibo Mao if (obj->type != ACPI_TYPE_BUFFER ||
24978ed8bd2SAlex Chiang obj->buffer.length < sizeof(struct acpi_subtable_header)) {
25078ed8bd2SAlex Chiang goto exit;
25178ed8bd2SAlex Chiang }
252af8f3f51SHanjun Guo
25378ed8bd2SAlex Chiang header = (struct acpi_subtable_header *)obj->buffer.pointer;
25478ed8bd2SAlex Chiang if (header->type == ACPI_MADT_TYPE_LOCAL_APIC)
25509c3f2bdSDou Liyang map_lapic_id(header, acpi_id, &phys_id);
25678ed8bd2SAlex Chiang else if (header->type == ACPI_MADT_TYPE_LOCAL_SAPIC)
257828aef37SCatalin Marinas map_lsapic_id(header, type, acpi_id, &phys_id);
25878ed8bd2SAlex Chiang else if (header->type == ACPI_MADT_TYPE_LOCAL_X2APIC)
25909c3f2bdSDou Liyang map_x2apic_id(header, type, acpi_id, &phys_id);
260ddcc18f5SHanjun Guo else if (header->type == ACPI_MADT_TYPE_GENERIC_INTERRUPT)
26109c3f2bdSDou Liyang map_gicc_mpidr(header, type, acpi_id, &phys_id);
262ca9f62acSJiang Liu else if (header->type == ACPI_MADT_TYPE_CORE_PIC)
263af8f3f51SHanjun Guo map_core_pic_id(header, type, acpi_id, &phys_id);
264ca9f62acSJiang Liu
265166deb0fSJan Beulich exit:
266ca9f62acSJiang Liu kfree(buffer.pointer);
267828aef37SCatalin Marinas return phys_id;
268ca9f62acSJiang Liu }
269ca9f62acSJiang Liu
acpi_get_phys_id(acpi_handle handle,int type,u32 acpi_id)270ca9f62acSJiang Liu phys_cpuid_t acpi_get_phys_id(acpi_handle handle, int type, u32 acpi_id)
271ca9f62acSJiang Liu {
272ca9f62acSJiang Liu phys_cpuid_t phys_id;
273ddcc18f5SHanjun Guo
274d640113fSLin Ming phys_id = map_mat_entry(handle, type, acpi_id);
275d640113fSLin Ming if (invalid_phys_cpuid(phys_id))
276828aef37SCatalin Marinas phys_id = map_madt_entry(get_madt_table(), type, acpi_id);
277d640113fSLin Ming
278d640113fSLin Ming return phys_id;
279d640113fSLin Ming }
280d640113fSLin Ming EXPORT_SYMBOL_GPL(acpi_get_phys_id);
281d640113fSLin Ming
acpi_map_cpuid(phys_cpuid_t phys_id,u32 acpi_id)282d640113fSLin Ming int acpi_map_cpuid(phys_cpuid_t phys_id, u32 acpi_id)
283d640113fSLin Ming {
284d640113fSLin Ming #ifdef CONFIG_SMP
285d640113fSLin Ming int i;
286d640113fSLin Ming #endif
287d640113fSLin Ming
288d640113fSLin Ming if (invalid_phys_cpuid(phys_id)) {
289af8f3f51SHanjun Guo /*
290c4686c71SThomas Renninger * On UP processor, there is no _MAT or MADT table.
291c4686c71SThomas Renninger * So above phys_id is always set to PHYS_CPUID_INVALID.
292d3da7cb9SHanjun Guo *
293d640113fSLin Ming * BIOS may define multiple CPU handles even for UP processor.
294c4686c71SThomas Renninger * For example,
295d640113fSLin Ming *
296d640113fSLin Ming * Scope (_PR)
297d3da7cb9SHanjun Guo * {
298d640113fSLin Ming * Processor (CPU0, 0x00, 0x00000410, 0x06) {}
29978ed8bd2SAlex Chiang * Processor (CPU1, 0x01, 0x00000410, 0x06) {}
300932df741SLin Ming * Processor (CPU2, 0x02, 0x00000410, 0x06) {}
30178ed8bd2SAlex Chiang * Processor (CPU3, 0x03, 0x00000410, 0x06) {}
302af8f3f51SHanjun Guo * }
30378ed8bd2SAlex Chiang *
30478ed8bd2SAlex Chiang * Ignores phys_id and always returns 0 for the processor
305932df741SLin Ming * handle with acpi id 0 if nr_cpu_ids is 1.
306932df741SLin Ming * This should be the case if SMP tables are not found.
307af8f3f51SHanjun Guo * Return -EINVAL for other CPU's handle.
308af8f3f51SHanjun Guo */
309932df741SLin Ming if (nr_cpu_ids <= 1 && acpi_id == 0)
310d3da7cb9SHanjun Guo return acpi_id;
31178ed8bd2SAlex Chiang else
312ca9f62acSJiang Liu return -EINVAL;
313ca9f62acSJiang Liu }
314ca9f62acSJiang Liu
315828aef37SCatalin Marinas #ifdef CONFIG_SMP
316ca9f62acSJiang Liu for_each_possible_cpu(i) {
317af8f3f51SHanjun Guo if (cpu_physical_id(i) == phys_id)
318ca9f62acSJiang Liu return i;
319af8f3f51SHanjun Guo }
320ca9f62acSJiang Liu #else
32178ed8bd2SAlex Chiang /* In UP kernel, only processor 0 is valid */
322ecf5636dSYinghai Lu if (phys_id == 0)
323ecf5636dSYinghai Lu return phys_id;
324ecf5636dSYinghai Lu #endif
325ecf5636dSYinghai Lu return -ENODEV;
326ecf5636dSYinghai Lu }
327ecf5636dSYinghai Lu
acpi_get_cpuid(acpi_handle handle,int type,u32 acpi_id)328ecf5636dSYinghai Lu int acpi_get_cpuid(acpi_handle handle, int type, u32 acpi_id)
329ecf5636dSYinghai Lu {
330ecf5636dSYinghai Lu phys_cpuid_t phys_id;
331ecf5636dSYinghai Lu
332ecf5636dSYinghai Lu phys_id = acpi_get_phys_id(handle, type, acpi_id);
333ecf5636dSYinghai Lu
334ecf5636dSYinghai Lu return acpi_map_cpuid(phys_id, acpi_id);
335ecf5636dSYinghai Lu }
336ecf5636dSYinghai Lu EXPORT_SYMBOL_GPL(acpi_get_cpuid);
337ecf5636dSYinghai Lu
338ecf5636dSYinghai Lu #ifdef CONFIG_ACPI_HOTPLUG_IOAPIC
get_ioapic_id(struct acpi_subtable_header * entry,u32 gsi_base,u64 * phys_addr,int * ioapic_id)339ecf5636dSYinghai Lu static int get_ioapic_id(struct acpi_subtable_header *entry, u32 gsi_base,
340ecf5636dSYinghai Lu u64 *phys_addr, int *ioapic_id)
341ecf5636dSYinghai Lu {
342ecf5636dSYinghai Lu struct acpi_madt_io_apic *ioapic = (struct acpi_madt_io_apic *)entry;
343ecf5636dSYinghai Lu
344ecf5636dSYinghai Lu if (ioapic->global_irq_base != gsi_base)
345ecf5636dSYinghai Lu return 0;
346ecf5636dSYinghai Lu
347ecf5636dSYinghai Lu *phys_addr = ioapic->address;
348ecf5636dSYinghai Lu *ioapic_id = ioapic->id;
349ecf5636dSYinghai Lu return 1;
350ecf5636dSYinghai Lu }
351ecf5636dSYinghai Lu
parse_madt_ioapic_entry(u32 gsi_base,u64 * phys_addr)352ecf5636dSYinghai Lu static int parse_madt_ioapic_entry(u32 gsi_base, u64 *phys_addr)
353ecf5636dSYinghai Lu {
354ecf5636dSYinghai Lu struct acpi_subtable_header *hdr;
355ecf5636dSYinghai Lu unsigned long madt_end, entry;
356ecf5636dSYinghai Lu struct acpi_table_madt *madt;
357ecf5636dSYinghai Lu int apic_id = -1;
358ecf5636dSYinghai Lu
359ecf5636dSYinghai Lu madt = get_madt_table();
360ecf5636dSYinghai Lu if (!madt)
361ecf5636dSYinghai Lu return apic_id;
362ecf5636dSYinghai Lu
363ecf5636dSYinghai Lu entry = (unsigned long)madt;
364ecf5636dSYinghai Lu madt_end = entry + madt->header.length;
365ecf5636dSYinghai Lu
366ecf5636dSYinghai Lu /* Parse all entries looking for a match. */
367ecf5636dSYinghai Lu entry += sizeof(struct acpi_table_madt);
368ecf5636dSYinghai Lu while (entry + sizeof(struct acpi_subtable_header) < madt_end) {
369ecf5636dSYinghai Lu hdr = (struct acpi_subtable_header *)entry;
370ecf5636dSYinghai Lu if (hdr->type == ACPI_MADT_TYPE_IO_APIC &&
371ecf5636dSYinghai Lu get_ioapic_id(hdr, gsi_base, phys_addr, &apic_id))
372ecf5636dSYinghai Lu break;
373ecf5636dSYinghai Lu else
374ecf5636dSYinghai Lu entry += hdr->length;
375ecf5636dSYinghai Lu }
376ecf5636dSYinghai Lu
377ecf5636dSYinghai Lu return apic_id;
378ecf5636dSYinghai Lu }
379ecf5636dSYinghai Lu
parse_mat_ioapic_entry(acpi_handle handle,u32 gsi_base,u64 * phys_addr)380ecf5636dSYinghai Lu static int parse_mat_ioapic_entry(acpi_handle handle, u32 gsi_base,
381ecf5636dSYinghai Lu u64 *phys_addr)
382ecf5636dSYinghai Lu {
383ecf5636dSYinghai Lu struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
384ecf5636dSYinghai Lu struct acpi_subtable_header *header;
385ecf5636dSYinghai Lu union acpi_object *obj;
386ecf5636dSYinghai Lu int apic_id = -1;
387ecf5636dSYinghai Lu
388ecf5636dSYinghai Lu if (ACPI_FAILURE(acpi_evaluate_object(handle, "_MAT", NULL, &buffer)))
389ecf5636dSYinghai Lu goto exit;
390ecf5636dSYinghai Lu
391ecf5636dSYinghai Lu if (!buffer.length || !buffer.pointer)
392ecf5636dSYinghai Lu goto exit;
393ecf5636dSYinghai Lu
394ecf5636dSYinghai Lu obj = buffer.pointer;
395ecf5636dSYinghai Lu if (obj->type != ACPI_TYPE_BUFFER ||
396ecf5636dSYinghai Lu obj->buffer.length < sizeof(struct acpi_subtable_header))
397ecf5636dSYinghai Lu goto exit;
398ecf5636dSYinghai Lu
399ecf5636dSYinghai Lu header = (struct acpi_subtable_header *)obj->buffer.pointer;
400ecf5636dSYinghai Lu if (header->type == ACPI_MADT_TYPE_IO_APIC)
401ecf5636dSYinghai Lu get_ioapic_id(header, gsi_base, phys_addr, &apic_id);
402ecf5636dSYinghai Lu
403ecf5636dSYinghai Lu exit:
404ecf5636dSYinghai Lu kfree(buffer.pointer);
405ecf5636dSYinghai Lu return apic_id;
406ecf5636dSYinghai Lu }
407ecf5636dSYinghai Lu
408ecf5636dSYinghai Lu /**
409ecf5636dSYinghai Lu * acpi_get_ioapic_id - Get IOAPIC ID and physical address matching @gsi_base
410ecf5636dSYinghai Lu * @handle: ACPI object for IOAPIC device
411ecf5636dSYinghai Lu * @gsi_base: GSI base to match with
412ecf5636dSYinghai Lu * @phys_addr: Pointer to store physical address of matching IOAPIC record
413ecf5636dSYinghai Lu *
414ecf5636dSYinghai Lu * Walk resources returned by ACPI_MAT method, then ACPI MADT table, to search
415 * for an ACPI IOAPIC record matching @gsi_base.
416 * Return IOAPIC id and store physical address in @phys_addr if found a match,
417 * otherwise return <0.
418 */
acpi_get_ioapic_id(acpi_handle handle,u32 gsi_base,u64 * phys_addr)419 int acpi_get_ioapic_id(acpi_handle handle, u32 gsi_base, u64 *phys_addr)
420 {
421 int apic_id;
422
423 apic_id = parse_mat_ioapic_entry(handle, gsi_base, phys_addr);
424 if (apic_id == -1)
425 apic_id = parse_madt_ioapic_entry(gsi_base, phys_addr);
426
427 return apic_id;
428 }
429 #endif /* CONFIG_ACPI_HOTPLUG_IOAPIC */
430