From ed2973cb8284be35e041f3f1d5d9440f12cab91b Mon Sep 17 00:00:00 2001 From: Stan Skowronek Date: Tue, 19 Jan 2021 11:50:48 +0100 Subject: [PATCH] arm64: apple: CPU start driver This driver is needed to spawn CPUs for SMP on Apple Silicon platforms. Apple Silicon doesn't have EL3, so PSCI isn't an option. Signed-off-by: Stan Skowronek --- .../devicetree/bindings/arm/cpus.yaml | 1 + MAINTAINERS | 6 + arch/arm64/kernel/Makefile | 1 + arch/arm64/kernel/apple_cpustart.c | 153 ++++++++++++++++++ 4 files changed, 161 insertions(+) create mode 100644 arch/arm64/kernel/apple_cpustart.c diff --git a/Documentation/devicetree/bindings/arm/cpus.yaml b/Documentation/devicetree/bindings/arm/cpus.yaml index 7dd27d237d8889..1649da3c05ede6 100644 --- a/Documentation/devicetree/bindings/arm/cpus.yaml +++ b/Documentation/devicetree/bindings/arm/cpus.yaml @@ -179,6 +179,7 @@ properties: - enum: - psci - spin-table + - apple # On ARM 32-bit systems this property is optional - enum: - actions,s500-smp diff --git a/MAINTAINERS b/MAINTAINERS index 7e13d6bb8b00ef..0716358604631e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1230,6 +1230,12 @@ L: linux-input@vger.kernel.org S: Odd fixes F: drivers/input/mouse/bcm5974.c +APPLE CPU START DRIVER +M: Stan Skowronek +L: linux-arm-kernel@lists.infradead.org +S: Maintained +F: arch/arm64/kernel/apple_cpustart.c + APPLE SMC DRIVER M: Henrik Rydberg L: linux-hwmon@vger.kernel.org diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile index 86364ab6f13fdf..7f7e33e4e1ab30 100644 --- a/arch/arm64/kernel/Makefile +++ b/arch/arm64/kernel/Makefile @@ -59,6 +59,7 @@ obj-$(CONFIG_CRASH_CORE) += crash_core.o obj-$(CONFIG_ARM_SDE_INTERFACE) += sdei.o obj-$(CONFIG_ARM64_PTR_AUTH) += pointer_auth.o obj-$(CONFIG_ARM64_MTE) += mte.o +obj-$(CONFIG_ARCH_APPLE) += apple_cpustart.o obj-y += vdso/ probes/ obj-$(CONFIG_COMPAT_VDSO) += vdso32/ diff --git a/arch/arm64/kernel/apple_cpustart.c b/arch/arm64/kernel/apple_cpustart.c new file mode 100644 index 00000000000000..41d049eaaec7bf --- /dev/null +++ b/arch/arm64/kernel/apple_cpustart.c @@ -0,0 +1,153 @@ +/* SPDX-License-Identifier: (GPL-2.0 or BSD-3-Clause) */ +/* + * Copyright (C) 2020 Corellium LLC + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define MAGIC_UNLOCK 0xc5acce55 + +struct cpu_apple_start_info { + void __iomem *pmgr_start; + u64 pmgr_start_size; + void __iomem *cputrc_rvbar; + void __iomem *dbg_unlock; +}; + +extern void apple_aic_cpu_prepare(unsigned int cpu); + +static int cpu_apple_start0_unlocked = 0; +static DEFINE_PER_CPU(struct cpu_apple_start_info, cpu_apple_start_info); + +static int __init cpu_apple_start_init(unsigned int cpu) +{ + return 0; +} + +static int cpu_apple_start_prepare(unsigned int cpu) +{ + struct device_node *node; + struct cpu_apple_start_info *info; + + info = per_cpu_ptr(&cpu_apple_start_info, cpu); + + if (info->pmgr_start && info->cputrc_rvbar && info->dbg_unlock) + return 0; + + node = of_find_compatible_node(NULL, NULL, "apple,startcpu"); + if (!node) { + pr_err("%s: missing startcpu node in device tree.\n", __func__); + return -EINVAL; + } + + if (!info->pmgr_start) { + info->pmgr_start = of_iomap(node, cpu * 3); + if (!info->pmgr_start) { + pr_err("%s: failed to map start register for CPU %d.\n", + __func__, cpu); + return -EINVAL; + } + if (!of_get_address(node, cpu * 3, &info->pmgr_start_size, + NULL)) + info->pmgr_start_size = 8; + } + + if (!info->cputrc_rvbar) { + info->cputrc_rvbar = of_iomap(node, cpu * 3 + 1); + if (!info->cputrc_rvbar) { + pr_err("%s: failed to map reset address register for CPU %d.\n", + __func__, cpu); + return -EINVAL; + } + } + + if (!info->dbg_unlock) { + info->dbg_unlock = of_iomap(node, cpu * 3 + 2); + if (!info->dbg_unlock) { + pr_err("%s: failed to map unlock register for CPU %d.\n", + __func__, cpu); + return -EINVAL; + } + } + + if (cpu) + apple_aic_cpu_prepare(cpu); + + return 0; +} + +static int cpu_apple_start_boot(unsigned int cpu) +{ + struct cpu_apple_start_info *info; + unsigned long addr; + + if (!cpu_apple_start0_unlocked) { + if (!cpu_apple_start_prepare(0)) { + info = per_cpu_ptr(&cpu_apple_start_info, 0); + writel(MAGIC_UNLOCK, info->dbg_unlock); + cpu_apple_start0_unlocked = 1; + } else + pr_err("%s: failed to unlock boot CPU\n", __func__); + } + + info = per_cpu_ptr(&cpu_apple_start_info, cpu); + + if (!info->pmgr_start || !info->cputrc_rvbar || !info->dbg_unlock) + return -EINVAL; + + writeq(__pa_symbol(secondary_entry) | 1, info->cputrc_rvbar); + readq(info->cputrc_rvbar); + writeq(__pa_symbol(secondary_entry) | 1, info->cputrc_rvbar); + addr = readq(info->cputrc_rvbar) & 0xFFFFFFFFFul; + dsb(sy); + + if (addr != (__pa_symbol(secondary_entry) | 1)) + pr_err("%s: CPU%d reset address: 0x%lx, failed to set to 0x%lx.\n", + __func__, cpu, addr, + (unsigned long)(__pa_symbol(secondary_entry) | 1)); + + writel(MAGIC_UNLOCK, info->dbg_unlock); + + writel(1 << cpu, info->pmgr_start); + if (info->pmgr_start_size >= 12) { + if (cpu < 4) { + writel(1 << cpu, info->pmgr_start + 4); + writel(0, info->pmgr_start + 8); + } else { + writel(0, info->pmgr_start + 4); + writel(1 << (cpu - 4), info->pmgr_start + 8); + } + } else + writel(1 << cpu, info->pmgr_start + 4); + + dsb(sy); + sev(); + + return 0; +} + +static void cpu_apple_wfi(void) +{ + /* can't do a proper WFI, because the CPU tends to lose state; will need + a proper wrapper sequence */ + dsb(sy); + wfe(); +} + +const struct cpu_operations cpu_apple_start_ops = { + .name = "apple", + .cpu_init = cpu_apple_start_init, + .cpu_prepare = cpu_apple_start_prepare, + .cpu_boot = cpu_apple_start_boot, + .cpu_wfi = cpu_apple_wfi, +};