linux/drivers/acpi/processor_core.c
<<
>>
Prefs
   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
  17static 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
  32static 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
  48static 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
  65static 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 */
  87static 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 (!(gicc->flags & ACPI_MADT_ENABLED))
  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
 109static phys_cpuid_t map_madt_entry(struct acpi_table_madt *madt,
 110                                   int type, u32 acpi_id)
 111{
 112        unsigned long madt_end, entry;
 113        phys_cpuid_t phys_id = PHYS_CPUID_INVALID;      /* CPU hardware ID */
 114
 115        if (!madt)
 116                return phys_id;
 117
 118        entry = (unsigned long)madt;
 119        madt_end = entry + madt->header.length;
 120
 121        /* Parse all entries looking for a match. */
 122
 123        entry += sizeof(struct acpi_table_madt);
 124        while (entry + sizeof(struct acpi_subtable_header) < madt_end) {
 125                struct acpi_subtable_header *header =
 126                        (struct acpi_subtable_header *)entry;
 127                if (header->type == ACPI_MADT_TYPE_LOCAL_APIC) {
 128                        if (!map_lapic_id(header, acpi_id, &phys_id))
 129                                break;
 130                } else if (header->type == ACPI_MADT_TYPE_LOCAL_X2APIC) {
 131                        if (!map_x2apic_id(header, type, acpi_id, &phys_id))
 132                                break;
 133                } else if (header->type == ACPI_MADT_TYPE_LOCAL_SAPIC) {
 134                        if (!map_lsapic_id(header, type, acpi_id, &phys_id))
 135                                break;
 136                } else if (header->type == ACPI_MADT_TYPE_GENERIC_INTERRUPT) {
 137                        if (!map_gicc_mpidr(header, type, acpi_id, &phys_id))
 138                                break;
 139                }
 140                entry += header->length;
 141        }
 142        return phys_id;
 143}
 144
 145phys_cpuid_t __init acpi_map_madt_entry(u32 acpi_id)
 146{
 147        struct acpi_table_madt *madt = NULL;
 148        phys_cpuid_t rv;
 149
 150        acpi_get_table(ACPI_SIG_MADT, 0,
 151                       (struct acpi_table_header **)&madt);
 152        if (!madt)
 153                return PHYS_CPUID_INVALID;
 154
 155        rv = map_madt_entry(madt, 1, acpi_id);
 156
 157        acpi_put_table((struct acpi_table_header *)madt);
 158
 159        return rv;
 160}
 161
 162static phys_cpuid_t map_mat_entry(acpi_handle handle, int type, u32 acpi_id)
 163{
 164        struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
 165        union acpi_object *obj;
 166        struct acpi_subtable_header *header;
 167        phys_cpuid_t phys_id = PHYS_CPUID_INVALID;
 168
 169        if (ACPI_FAILURE(acpi_evaluate_object(handle, "_MAT", NULL, &buffer)))
 170                goto exit;
 171
 172        if (!buffer.length || !buffer.pointer)
 173                goto exit;
 174
 175        obj = buffer.pointer;
 176        if (obj->type != ACPI_TYPE_BUFFER ||
 177            obj->buffer.length < sizeof(struct acpi_subtable_header)) {
 178                goto exit;
 179        }
 180
 181        header = (struct acpi_subtable_header *)obj->buffer.pointer;
 182        if (header->type == ACPI_MADT_TYPE_LOCAL_APIC)
 183                map_lapic_id(header, acpi_id, &phys_id);
 184        else if (header->type == ACPI_MADT_TYPE_LOCAL_SAPIC)
 185                map_lsapic_id(header, type, acpi_id, &phys_id);
 186        else if (header->type == ACPI_MADT_TYPE_LOCAL_X2APIC)
 187                map_x2apic_id(header, type, acpi_id, &phys_id);
 188        else if (header->type == ACPI_MADT_TYPE_GENERIC_INTERRUPT)
 189                map_gicc_mpidr(header, type, acpi_id, &phys_id);
 190
 191exit:
 192        kfree(buffer.pointer);
 193        return phys_id;
 194}
 195
 196phys_cpuid_t acpi_get_phys_id(acpi_handle handle, int type, u32 acpi_id)
 197{
 198        phys_cpuid_t phys_id;
 199
 200        phys_id = map_mat_entry(handle, type, acpi_id);
 201        if (invalid_phys_cpuid(phys_id))
 202                phys_id = map_madt_entry(get_madt_table(), type, acpi_id);
 203
 204        return phys_id;
 205}
 206EXPORT_SYMBOL_GPL(acpi_get_phys_id);
 207
 208int acpi_map_cpuid(phys_cpuid_t phys_id, u32 acpi_id)
 209{
 210#ifdef CONFIG_SMP
 211        int i;
 212#endif
 213
 214        if (invalid_phys_cpuid(phys_id)) {
 215                /*
 216                 * On UP processor, there is no _MAT or MADT table.
 217                 * So above phys_id is always set to PHYS_CPUID_INVALID.
 218                 *
 219                 * BIOS may define multiple CPU handles even for UP processor.
 220                 * For example,
 221                 *
 222                 * Scope (_PR)
 223                 * {
 224                 *     Processor (CPU0, 0x00, 0x00000410, 0x06) {}
 225                 *     Processor (CPU1, 0x01, 0x00000410, 0x06) {}
 226                 *     Processor (CPU2, 0x02, 0x00000410, 0x06) {}
 227                 *     Processor (CPU3, 0x03, 0x00000410, 0x06) {}
 228                 * }
 229                 *
 230                 * Ignores phys_id and always returns 0 for the processor
 231                 * handle with acpi id 0 if nr_cpu_ids is 1.
 232                 * This should be the case if SMP tables are not found.
 233                 * Return -EINVAL for other CPU's handle.
 234                 */
 235                if (nr_cpu_ids <= 1 && acpi_id == 0)
 236                        return acpi_id;
 237                else
 238                        return -EINVAL;
 239        }
 240
 241#ifdef CONFIG_SMP
 242        for_each_possible_cpu(i) {
 243                if (cpu_physical_id(i) == phys_id)
 244                        return i;
 245        }
 246#else
 247        /* In UP kernel, only processor 0 is valid */
 248        if (phys_id == 0)
 249                return phys_id;
 250#endif
 251        return -ENODEV;
 252}
 253
 254int acpi_get_cpuid(acpi_handle handle, int type, u32 acpi_id)
 255{
 256        phys_cpuid_t phys_id;
 257
 258        phys_id = acpi_get_phys_id(handle, type, acpi_id);
 259
 260        return acpi_map_cpuid(phys_id, acpi_id);
 261}
 262EXPORT_SYMBOL_GPL(acpi_get_cpuid);
 263
 264#ifdef CONFIG_ACPI_HOTPLUG_IOAPIC
 265static int get_ioapic_id(struct acpi_subtable_header *entry, u32 gsi_base,
 266                         u64 *phys_addr, int *ioapic_id)
 267{
 268        struct acpi_madt_io_apic *ioapic = (struct acpi_madt_io_apic *)entry;
 269
 270        if (ioapic->global_irq_base != gsi_base)
 271                return 0;
 272
 273        *phys_addr = ioapic->address;
 274        *ioapic_id = ioapic->id;
 275        return 1;
 276}
 277
 278static int parse_madt_ioapic_entry(u32 gsi_base, u64 *phys_addr)
 279{
 280        struct acpi_subtable_header *hdr;
 281        unsigned long madt_end, entry;
 282        struct acpi_table_madt *madt;
 283        int apic_id = -1;
 284
 285        madt = get_madt_table();
 286        if (!madt)
 287                return apic_id;
 288
 289        entry = (unsigned long)madt;
 290        madt_end = entry + madt->header.length;
 291
 292        /* Parse all entries looking for a match. */
 293        entry += sizeof(struct acpi_table_madt);
 294        while (entry + sizeof(struct acpi_subtable_header) < madt_end) {
 295                hdr = (struct acpi_subtable_header *)entry;
 296                if (hdr->type == ACPI_MADT_TYPE_IO_APIC &&
 297                    get_ioapic_id(hdr, gsi_base, phys_addr, &apic_id))
 298                        break;
 299                else
 300                        entry += hdr->length;
 301        }
 302
 303        return apic_id;
 304}
 305
 306static int parse_mat_ioapic_entry(acpi_handle handle, u32 gsi_base,
 307                                  u64 *phys_addr)
 308{
 309        struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
 310        struct acpi_subtable_header *header;
 311        union acpi_object *obj;
 312        int apic_id = -1;
 313
 314        if (ACPI_FAILURE(acpi_evaluate_object(handle, "_MAT", NULL, &buffer)))
 315                goto exit;
 316
 317        if (!buffer.length || !buffer.pointer)
 318                goto exit;
 319
 320        obj = buffer.pointer;
 321        if (obj->type != ACPI_TYPE_BUFFER ||
 322            obj->buffer.length < sizeof(struct acpi_subtable_header))
 323                goto exit;
 324
 325        header = (struct acpi_subtable_header *)obj->buffer.pointer;
 326        if (header->type == ACPI_MADT_TYPE_IO_APIC)
 327                get_ioapic_id(header, gsi_base, phys_addr, &apic_id);
 328
 329exit:
 330        kfree(buffer.pointer);
 331        return apic_id;
 332}
 333
 334/**
 335 * acpi_get_ioapic_id - Get IOAPIC ID and physical address matching @gsi_base
 336 * @handle:     ACPI object for IOAPIC device
 337 * @gsi_base:   GSI base to match with
 338 * @phys_addr:  Pointer to store physical address of matching IOAPIC record
 339 *
 340 * Walk resources returned by ACPI_MAT method, then ACPI MADT table, to search
 341 * for an ACPI IOAPIC record matching @gsi_base.
 342 * Return IOAPIC id and store physical address in @phys_addr if found a match,
 343 * otherwise return <0.
 344 */
 345int acpi_get_ioapic_id(acpi_handle handle, u32 gsi_base, u64 *phys_addr)
 346{
 347        int apic_id;
 348
 349        apic_id = parse_mat_ioapic_entry(handle, gsi_base, phys_addr);
 350        if (apic_id == -1)
 351                apic_id = parse_madt_ioapic_entry(gsi_base, phys_addr);
 352
 353        return apic_id;
 354}
 355#endif /* CONFIG_ACPI_HOTPLUG_IOAPIC */
 356