linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Henry Willard <henry.willard@oracle.com>
To: <catalin.marinas@arm.com>, <will@kernel.org>,
	<mark.rutland@arm.com>, <tabba@google.com>,
	<keescook@chromium.org>, <ardb@kernel.org>,
	<samitolvanen@google.com>, <joe@perches.com>,
	<nixiaoming@huawei.com>,
	linux-arm-kernel@lists.infradead.org,
	linux-kernel@vger.kernel.org
Subject: [PATCH] arm64: kexec: add support for kexec with spin-table
Date: Wed, 14 Jul 2021 10:41:13 -0700	[thread overview]
Message-ID: <1626284473-1168-1-git-send-email-henry.willard@oracle.com> (raw)

With one special exception kexec is not supported on systems
that use spin-table as the cpu enablement method instead of PSCI.
The spin-table implementation lacks cpu_die() and several other
methods needed by the hotplug framework used by kexec on Arm64.

Some embedded systems may not have a need for the Arm Trusted
Firmware, or they may lack it during early bring-up. Some of
these may have a more primitive version of u-boot that uses a
special device from which to load the kernel. Kexec can be
especially useful for testing new kernels in such an environment.

What is needed to support kexec is some place for cpu_die to park
the secondary CPUs outside the kernel while the primary copies
the new kernel into place and starts it. One possibility is to
use the control-code-page where arm64_relocate_new_kernel_size()
executes, but that requires a complicated and racy dance to get
the secondary CPUs from the control-code-page to the new
kernel after it has been copied.

The spin-table mechanism is setup before the Linux kernel
is entered with details provided in the device tree. The
"release-address" DT variable provides the address of a word the
secondary CPUs are polling. The boot CPU will store the real address
of secondary_holding_pen() at that address, and the secondary CPUs
will branch to that address. secondary_holding_pen() is another
loop where the secondary CPUs wait to be called up by the boot CPU.

This patch uses that mechanism to implement cpu_die(). In modern
versions of u-boot that implement spin-table, the address of the
loop in protected memory can be derived from the "release-address"
value. The patch validates the existence of the loop before
proceeding. smp_spin_table_cpu_die() uses cpu_soft_restart() to
branch to the loop with the MMU and caching turned off where the
CPU waits until released by the new kernel. After that kexec
reboot proceeds normally.

The special exception is the kdump capture kernel, which gets
started even if the secondaries can't be stopped.

Signed-off-by: Henry Willard <henry.willard@oracle.com>
---
 arch/arm64/kernel/smp_spin_table.c | 111 +++++++++++++++++++++++++++++++++++++
 1 file changed, 111 insertions(+)

diff --git a/arch/arm64/kernel/smp_spin_table.c b/arch/arm64/kernel/smp_spin_table.c
index 7e1624ecab3c..35c7fa764476 100644
--- a/arch/arm64/kernel/smp_spin_table.c
+++ b/arch/arm64/kernel/smp_spin_table.c
@@ -13,16 +13,27 @@
 #include <linux/mm.h>
 
 #include <asm/cacheflush.h>
+#include <asm/daifflags.h>
 #include <asm/cpu_ops.h>
 #include <asm/cputype.h>
 #include <asm/io.h>
 #include <asm/smp_plat.h>
+#include <asm/mmu_context.h>
+#include <asm/kexec.h>
+
+#include "cpu-reset.h"
 
 extern void secondary_holding_pen(void);
 volatile unsigned long __section(".mmuoff.data.read")
 secondary_holding_pen_release = INVALID_HWID;
 
 static phys_addr_t cpu_release_addr[NR_CPUS];
+static unsigned int spin_table_loop[4] = {
+	0xd503205f,        /* wfe */
+	0x58000060,        /* ldr  x0, spin_table_cpu_release_addr */
+	0xb4ffffc0,        /* cbnz x0, 0b */
+	0xd61f0000         /* br   x0 */
+};
 
 /*
  * Write secondary_holding_pen_release in a way that is guaranteed to be
@@ -119,9 +130,109 @@ static int smp_spin_table_cpu_boot(unsigned int cpu)
 	return 0;
 }
 
+
+/*
+ * There is a four instruction loop set aside in protected
+ * memory by u-boot where secondary CPUs wait for the kernel to
+ * start.
+ *
+ * 0:       wfe
+ *          ldr    x0, spin_table_cpu_release_addr
+ *          cbz    x0, 0b
+ *          br     x0
+ * spin_table_cpu_release_addr:
+ *          .quad  0
+ *
+ * The address of spin_table_cpu_release_addr is passed in the
+ * "release-address" property in the device table.
+ * smp_spin_table_cpu_prepare() stores the real address of
+ * secondary_holding_pen() where the secondary CPUs loop
+ * until they are released one at a time by smp_spin_table_cpu_boot().
+ * We reuse the spin-table loop by clearing spin_table_cpu_release_addr,
+ * and branching to the beginning of the loop via cpu_soft_restart(),
+ * which turns off the MMU and caching.
+ */
+static void smp_spin_table_cpu_die(unsigned int cpu)
+{
+	__le64 __iomem *release_addr;
+	unsigned int *spin_table_inst;
+	unsigned long spin_table_start;
+
+	if (!cpu_release_addr[cpu])
+		goto spin;
+
+	spin_table_start = (cpu_release_addr[cpu] - sizeof(spin_table_loop));
+
+	/*
+	 * The cpu-release-addr may or may not be inside the linear mapping.
+	 * As ioremap_cache will either give us a new mapping or reuse the
+	 * existing linear mapping, we can use it to cover both cases. In
+	 * either case the memory will be MT_NORMAL.
+	 */
+	release_addr = ioremap_cache(spin_table_start,
+				sizeof(*release_addr) +
+				sizeof(spin_table_loop));
+
+	if (!release_addr)
+		goto spin;
+
+	spin_table_inst = (unsigned int *)release_addr;
+	if (spin_table_inst[0] != spin_table_loop[0] ||
+		spin_table_inst[1] != spin_table_loop[1] ||
+		spin_table_inst[2] != spin_table_loop[2] ||
+		spin_table_inst[3] != spin_table_loop[3])
+		goto spin;
+
+	/*
+	 * Clear the release address, so that we can use it again
+	 */
+	writeq_relaxed(0, release_addr + 2);
+	dcache_clean_inval_poc((__force unsigned long)(release_addr + 2),
+			(__force unsigned long)(release_addr + 2) +
+				    sizeof(*release_addr));
+
+	iounmap(release_addr);
+
+	local_daif_mask();
+	cpu_soft_restart(spin_table_start, 0, 0, 0);
+
+	BUG();  /* Should never get here */
+
+spin:
+	cpu_park_loop();
+
+}
+
+static int smp_spin_table_cpu_kill(unsigned int cpu)
+{
+	unsigned long start, end;
+
+	start = jiffies;
+	end = start + msecs_to_jiffies(100);
+
+	do {
+		if (!cpu_online(cpu)) {
+			pr_info("CPU%d killed\n", cpu);
+			return 0;
+		}
+	} while (time_before(jiffies, end));
+	pr_warn("CPU%d may not have shut down cleanly\n", cpu);
+	return -ETIMEDOUT;
+
+}
+
+/* Nothing to do here */
+static int smp_spin_table_cpu_disable(unsigned int cpu)
+{
+	return 0;
+}
+
 const struct cpu_operations smp_spin_table_ops = {
 	.name		= "spin-table",
 	.cpu_init	= smp_spin_table_cpu_init,
 	.cpu_prepare	= smp_spin_table_cpu_prepare,
 	.cpu_boot	= smp_spin_table_cpu_boot,
+	.cpu_die	= smp_spin_table_cpu_die,
+	.cpu_kill	= smp_spin_table_cpu_kill,
+	.cpu_disable	= smp_spin_table_cpu_disable,
 };
-- 
1.8.3.1


             reply	other threads:[~2021-07-14 17:41 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-07-14 17:41 Henry Willard [this message]
2021-07-14 18:47 ` [PATCH] arm64: kexec: add support for kexec with spin-table Mark Rutland
2021-07-15  0:08   ` Henry Willard

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1626284473-1168-1-git-send-email-henry.willard@oracle.com \
    --to=henry.willard@oracle.com \
    --cc=ardb@kernel.org \
    --cc=catalin.marinas@arm.com \
    --cc=joe@perches.com \
    --cc=keescook@chromium.org \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=mark.rutland@arm.com \
    --cc=nixiaoming@huawei.com \
    --cc=samitolvanen@google.com \
    --cc=tabba@google.com \
    --cc=will@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).