Skip to content

Commit

Permalink
arm64: apple: CPU start driver
Browse files Browse the repository at this point in the history
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 <stan@corellium.com>
  • Loading branch information
Stan-Corellium authored and Mohamed Mediouni committed Jan 19, 2021
1 parent 44a8f09 commit ed2973c
Show file tree
Hide file tree
Showing 4 changed files with 161 additions and 0 deletions.
1 change: 1 addition & 0 deletions Documentation/devicetree/bindings/arm/cpus.yaml
Expand Up @@ -179,6 +179,7 @@ properties:
- enum:
- psci
- spin-table
- apple

This comment has been minimized.

Copy link
@konradybcio

konradybcio Jan 19, 2021

Please sort alphabetically. Also, perhaps apple-cpustart would be more fitting?

# On ARM 32-bit systems this property is optional
- enum:
- actions,s500-smp
Expand Down
6 changes: 6 additions & 0 deletions MAINTAINERS
Expand Up @@ -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 <stan@corellium.com>
L: linux-arm-kernel@lists.infradead.org
S: Maintained
F: arch/arm64/kernel/apple_cpustart.c

APPLE SMC DRIVER
M: Henrik Rydberg <rydberg@bitmath.org>
L: linux-hwmon@vger.kernel.org
Expand Down
1 change: 1 addition & 0 deletions arch/arm64/kernel/Makefile
Expand Up @@ -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

This comment has been minimized.

Copy link
@konradybcio

konradybcio Jan 19, 2021

Please sort the CONFIG_ part alphabetically.


obj-y += vdso/ probes/
obj-$(CONFIG_COMPAT_VDSO) += vdso32/
Expand Down
153 changes: 153 additions & 0 deletions 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 <linux/init.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/smp.h>
#include <linux/delay.h>
#include <linux/mm.h>

#include <asm/cpu_ops.h>
#include <asm/errno.h>
#include <asm/smp_plat.h>
#include <asm/io.h>

#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

This comment has been minimized.

Copy link
@kholk

kholk Jan 19, 2021

This is a big one and that needs proper explanation. You are missing the big part: WHY?!

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,

This comment has been minimized.

Copy link
@kholk

kholk Jan 19, 2021

Is hotplugging not available on Apple SoCs?
You should really add callbacks for that.

Also, please rename your function to reflect callback names, e.g. cpu_apple_start_cpu_init

};

1 comment on commit ed2973c

@konradybcio
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Commitmsg: "arm64: kernel: Add Apple CPU..."

Please sign in to comment.