aboutsummaryrefslogtreecommitdiff
path: root/arch/arm64/kernel/smp.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm64/kernel/smp.c')
-rw-r--r--arch/arm64/kernel/smp.c142
1 files changed, 128 insertions, 14 deletions
diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c
index 2673120f46aa..5adbcb5ae26f 100644
--- a/arch/arm64/kernel/smp.c
+++ b/arch/arm64/kernel/smp.c
@@ -35,6 +35,7 @@
#include <linux/clockchips.h>
#include <linux/completion.h>
#include <linux/of.h>
+#include <linux/acpi.h>
#include <asm/atomic.h>
#include <asm/cacheflush.h>
@@ -286,11 +287,6 @@ void __init smp_prepare_boot_cpu(void)
static void (*smp_cross_call)(const struct cpumask *, unsigned int);
-/*
- * Enumerate the possible CPU set from the device tree and build the
- * cpu logical map array containing MPIDR values related to logical
- * cpus. Assumes that cpu_logical_map(0) has already been initialized.
- */
static int __init of_smp_init_cpus(void)
{
struct device_node *dn = NULL;
@@ -400,23 +396,141 @@ next:
return 0;
}
-/*
- * In ACPI mode, the cpu possible map was enumerated before SMP
- * initialization when MADT table was parsed, so we can get the
- * possible map here to initialize CPUs.
- */
-static void __init acpi_smp_init_cpus(void)
+#ifdef CONFIG_ACPI
+
+static int __init
+acpi_smp_check_madt_entry(struct acpi_subtable_header *header,
+ const unsigned long end)
{
- int cpu;
+ struct acpi_madt_generic_interrupt *processor;
+
+ if (header->type != ACPI_MADT_TYPE_GENERIC_INTERRUPT)
+ return -EINVAL;
+
+ processor = (struct acpi_madt_generic_interrupt *)header;
+
+ if (BAD_MADT_ENTRY(processor, end) ||
+ !(processor->flags & ACPI_MADT_ENABLED))
+ return -EINVAL;
+
+ /*
+ * Non affinity bits must be set to 0 in the CPU UID
+ */
+ if (processor->uid & ~MPIDR_HWID_BITMASK)
+ return -ENOENT;
+
+ return 0;
+}
+
+#define FOR_EACH_MADT_ENTRY(entry, end) \
+ for (; (((unsigned long)entry) + sizeof(struct acpi_subtable_header)) < end; \
+ entry = (struct acpi_subtable_header *) \
+ ((unsigned long)entry + entry->length))
+
+static int __init
+acpi_smp_parse_madt(struct acpi_subtable_header *header, const unsigned long end)
+{
+ struct acpi_madt_generic_interrupt *processor;
+ unsigned int i, status, cpu = 0;
+
+ /* First entry represents boot CPU and needs to be valid */
+ status = acpi_smp_check_madt_entry(header, end);
+ if (status) {
+ pr_err("Failed to parse boot CPU MADT entry\n");
+ return status;
+ }
+
+ processor = (struct acpi_madt_generic_interrupt *)header;
+ if (cpu_logical_map(0) != (u64)processor->uid) {
+ pr_err("Wrong boot CPU placement in MADT\n");
+ return -ENOENT;
+ }
+
+ /*
+ * Update CPU logic to GIC id mapping.
+ * cpu_logical_map has already been initialized and the boot CPU doesn't
+ * need the enable-method so continue.
+ */
+ cpu_physical_id(cpu++) = processor->gic_id;
+
+ /* Now handle with rest of CPUs */
+ header = (struct acpi_subtable_header *)
+ ((unsigned long)header + header->length);
+ FOR_EACH_MADT_ENTRY(header, end) {
+ u64 hwid;
+
+ if (acpi_smp_check_madt_entry(header, end))
+ continue;
+
+ processor = (struct acpi_madt_generic_interrupt *)header;
+ hwid = processor->uid;
+
+ /*
+ * Duplicate MPIDRs are a recipe for disaster. Scan
+ * all initialized entries and check for
+ * duplicates. If any is found just ignore the CPU.
+ * cpu_logical_map was initialized to INVALID_HWID to
+ * avoid matching valid MPIDR values.
+ */
+ for (i = 0; (i < cpu) && (i < NR_CPUS); i++) {
+ if (cpu_logical_map(i) == hwid) {
+ pr_err("Duplicate CPU UID: 0x%x in MADT\n",
+ (int)processor->uid);
+ continue;
+ }
+ }
+
+ if (cpu >= NR_CPUS)
+ continue;
- for_each_possible_cpu(cpu) {
if (cpu_acpi_read_ops(cpu) != 0)
continue;
- cpu_ops[cpu]->cpu_init(NULL, cpu);
+ if (cpu_ops[cpu]->cpu_init(NULL, cpu))
+ continue;
+
+ pr_debug("CPU logical map [%d] = 0x%lx\n", cpu, (long int)hwid);
+ set_cpu_possible(cpu, true);
+ cpu_logical_map(cpu) = hwid;
+ cpu_physical_id(cpu++) = processor->gic_id;
}
+
+ if (cpu > NR_CPUS)
+ pr_warning("no. of cores (%d) greater than configured maximum"
+ " of %d - clipping\n", cpu, NR_CPUS);
+
+ return 0;
+}
+
+
+static void __init acpi_smp_init_cpus(void)
+{
+ int count;
+
+ /*
+ * The GIC corresponding to the boot processor must be the first entry
+ * in the list of interrupt controller descriptors. If that is true,
+ * we keep on parsing next ones. While we are here, fill in
+ * cpu_physical_id map for ACPI processor driver too.
+ */
+ count = acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_INTERRUPT,
+ acpi_smp_parse_madt, 1);
+ if (count == 1)
+ pr_warning("None of secondaries found, assuming UP\n");
+ else if (count <= 0)
+ pr_err("Failed to parse MADT during SMP init, not enabling"
+ " secondaries\n");
+
}
+#else
+static inline void acpi_smp_init_cpus(void) {}
+#endif
+/*
+ * Enumerate the possible CPU set either from the device tree or MADT
+ * and build the cpu logical map array containing MPIDR values related to
+ * logical CPUs. Assumes that cpu_logical_map(0) has already been initialized.
+ */
void __init smp_init_cpus(void)
{
if (!of_smp_init_cpus())