[PATCH 08/14] arm64: kexec_file: create purgatory

AKASHI Takahiro takahiro.akashi at linaro.org
Thu Aug 24 01:18:05 PDT 2017


This is a basic purgtory, or a kind of glue code between the two kernel,
for arm64. We will later add a feature of verifying a digest check against
loaded memory segments.

arch_kexec_apply_relocations_add() is responsible for re-linking any
relative symbols in purgatory. Please note that the purgatory is not
an executable, but a non-linked archive of binaries so relative symbols
contained here must be resolved at kexec load time.
Despite that arm64_kernel_start and arm64_dtb_addr are only such global
variables now, arch_kexec_apply_relocations_add() can manage more various
types of relocations.

Signed-off-by: AKASHI Takahiro <takahiro.akashi at linaro.org>
Cc: Catalin Marinas <catalin.marinas at arm.com>
Cc: Will Deacon <will.deacon at arm.com>
---
 arch/arm64/Makefile                    |   1 +
 arch/arm64/kernel/Makefile             |   1 +
 arch/arm64/kernel/machine_kexec_file.c | 199 +++++++++++++++++++++++++++++++++
 arch/arm64/purgatory/Makefile          |  24 ++++
 arch/arm64/purgatory/entry.S           |  28 +++++
 5 files changed, 253 insertions(+)
 create mode 100644 arch/arm64/kernel/machine_kexec_file.c
 create mode 100644 arch/arm64/purgatory/Makefile
 create mode 100644 arch/arm64/purgatory/entry.S

diff --git a/arch/arm64/Makefile b/arch/arm64/Makefile
index 9b41f1e3b1a0..429f60728c0a 100644
--- a/arch/arm64/Makefile
+++ b/arch/arm64/Makefile
@@ -105,6 +105,7 @@ core-$(CONFIG_XEN) += arch/arm64/xen/
 core-$(CONFIG_CRYPTO) += arch/arm64/crypto/
 libs-y		:= arch/arm64/lib/ $(libs-y)
 core-$(CONFIG_EFI_STUB) += $(objtree)/drivers/firmware/efi/libstub/lib.a
+core-$(CONFIG_KEXEC_FILE) += arch/arm64/purgatory/
 
 # Default target when executing plain make
 boot		:= arch/arm64/boot
diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile
index f2b4e816b6de..16e9f56b536a 100644
--- a/arch/arm64/kernel/Makefile
+++ b/arch/arm64/kernel/Makefile
@@ -50,6 +50,7 @@ arm64-obj-$(CONFIG_RANDOMIZE_BASE)	+= kaslr.o
 arm64-obj-$(CONFIG_HIBERNATION)		+= hibernate.o hibernate-asm.o
 arm64-obj-$(CONFIG_KEXEC)		+= machine_kexec.o relocate_kernel.o	\
 					   cpu-reset.o
+arm64-obj-$(CONFIG_KEXEC_FILE)		+= machine_kexec_file.o
 arm64-obj-$(CONFIG_ARM64_RELOC_TEST)	+= arm64-reloc-test.o
 arm64-reloc-test-y := reloc_test_core.o reloc_test_syms.o
 arm64-obj-$(CONFIG_CRASH_DUMP)		+= crash_dump.o
diff --git a/arch/arm64/kernel/machine_kexec_file.c b/arch/arm64/kernel/machine_kexec_file.c
new file mode 100644
index 000000000000..183f7776d6dd
--- /dev/null
+++ b/arch/arm64/kernel/machine_kexec_file.c
@@ -0,0 +1,199 @@
+/*
+ * kexec_file for arm64
+ *
+ * Copyright (C) 2017 Linaro Limited
+ * Author: AKASHI Takahiro <takahiro.akashi at linaro.org>
+ *
+ * Most code is derived from arm64 port of kexec-tools
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#define pr_fmt(fmt) "kexec_file: " fmt
+
+#include <linux/elf.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <asm/byteorder.h>
+
+/*
+ * Apply purgatory relocations.
+ *
+ * ehdr: Pointer to elf headers
+ * sechdrs: Pointer to section headers.
+ * relsec: section index of SHT_RELA section.
+ *
+ * Note:
+ * Currently R_AARCH64_ABS64, R_AARCH64_LD_PREL_LO19 and R_AARCH64_CALL26
+ * are the only types to be generated from purgatory code.
+ * If we add more functionalities, other types may also be used.
+ */
+int arch_kexec_apply_relocations_add(const Elf64_Ehdr *ehdr,
+				     Elf64_Shdr *sechdrs, unsigned int relsec)
+{
+	Elf64_Rela *rel;
+	Elf64_Shdr *section, *symtabsec;
+	Elf64_Sym *sym;
+	const char *strtab, *name, *shstrtab;
+	unsigned long address, sec_base, value;
+	void *location;
+	u64 *loc64;
+	u32 *loc32, imm;
+	unsigned int i;
+
+	/*
+	 * ->sh_offset has been modified to keep the pointer to section
+	 * contents in memory
+	 */
+	rel = (void *)sechdrs[relsec].sh_offset;
+
+	/* Section to which relocations apply */
+	section = &sechdrs[sechdrs[relsec].sh_info];
+
+	pr_debug("reloc: Applying relocate section %u to %u\n", relsec,
+		 sechdrs[relsec].sh_info);
+
+	/* Associated symbol table */
+	symtabsec = &sechdrs[sechdrs[relsec].sh_link];
+
+	/* String table */
+	if (symtabsec->sh_link >= ehdr->e_shnum) {
+		/* Invalid strtab section number */
+		pr_err("reloc: Invalid string table section index %d\n",
+		       symtabsec->sh_link);
+		return -ENOEXEC;
+	}
+
+	strtab = (char *)sechdrs[symtabsec->sh_link].sh_offset;
+
+	/* section header string table */
+	shstrtab = (char *)sechdrs[ehdr->e_shstrndx].sh_offset;
+
+	for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) {
+
+		/*
+		 * rel[i].r_offset contains byte offset from beginning
+		 * of section to the storage unit affected.
+		 *
+		 * This is location to update (->sh_offset). This is temporary
+		 * buffer where section is currently loaded. This will finally
+		 * be loaded to a different address later, pointed to by
+		 * ->sh_addr. kexec takes care of moving it
+		 *  (kexec_load_segment()).
+		 */
+		location = (void *)(section->sh_offset + rel[i].r_offset);
+
+		/* Final address of the location */
+		address = section->sh_addr + rel[i].r_offset;
+
+		/*
+		 * rel[i].r_info contains information about symbol table index
+		 * w.r.t which relocation must be made and type of relocation
+		 * to apply. ELF64_R_SYM() and ELF64_R_TYPE() macros get
+		 * these respectively.
+		 */
+		sym = (Elf64_Sym *)symtabsec->sh_offset +
+				ELF64_R_SYM(rel[i].r_info);
+
+		if (sym->st_name)
+			name = strtab + sym->st_name;
+		else
+			name = shstrtab + sechdrs[sym->st_shndx].sh_name;
+
+		pr_debug("Symbol: %-16s info: %02x shndx: %02x value=%llx size: %llx reloc type:%d\n",
+			 name, sym->st_info, sym->st_shndx, sym->st_value,
+			 sym->st_size, (int)ELF64_R_TYPE(rel[i].r_info));
+
+		if (sym->st_shndx == SHN_UNDEF) {
+			pr_err("reloc: Undefined symbol: %s\n", name);
+			return -ENOEXEC;
+		}
+
+		if (sym->st_shndx == SHN_COMMON) {
+			pr_err("reloc: symbol '%s' in common section\n", name);
+			return -ENOEXEC;
+		}
+
+		if (sym->st_shndx == SHN_ABS) {
+			sec_base = 0;
+		} else if (sym->st_shndx < ehdr->e_shnum) {
+			sec_base = sechdrs[sym->st_shndx].sh_addr;
+		} else {
+			pr_err("reloc: Invalid section %d for symbol %s\n",
+			       sym->st_shndx, name);
+			return -ENOEXEC;
+		}
+
+		value = sym->st_value;
+		value += sec_base;
+		value += rel[i].r_addend;
+
+		switch (ELF64_R_TYPE(rel[i].r_info)) {
+		case R_AARCH64_ABS64:
+			loc64 = location;
+			*loc64 = cpu_to_elf64(ehdr,
+					elf64_to_cpu(ehdr, *loc64) + value);
+			break;
+		case R_AARCH64_PREL32:
+			loc32 = location;
+			*loc32 = cpu_to_elf32(ehdr,
+					elf32_to_cpu(ehdr, *loc32) + value
+								- address);
+			break;
+		case R_AARCH64_LD_PREL_LO19:
+			loc32 = location;
+			*loc32 = cpu_to_le32(le32_to_cpu(*loc32)
+				+ (((value - address) << 3) & 0xffffe0));
+			break;
+		case R_AARCH64_ADR_PREL_LO21:
+			if (value & 3) {
+				pr_err("reloc: Unaligned value: %lx\n", value);
+				return -ENOEXEC;
+			}
+			loc32 = location;
+			*loc32 = cpu_to_le32(le32_to_cpu(*loc32)
+				+ (((value - address) << 3) & 0xffffe0));
+			break;
+		case R_AARCH64_ADR_PREL_PG_HI21:
+			imm = ((value & ~0xfff) - (address & ~0xfff)) >> 12;
+			loc32 = location;
+			*loc32 = cpu_to_le32(le32_to_cpu(*loc32)
+				+ ((imm & 3) << 29)
+				+ ((imm & 0x1ffffc) << (5 - 2)));
+			break;
+		case R_AARCH64_ADD_ABS_LO12_NC:
+			loc32 = location;
+			*loc32 = cpu_to_le32(le32_to_cpu(*loc32)
+				+ ((value & 0xfff) << 10));
+			break;
+		case R_AARCH64_JUMP26:
+			loc32 = location;
+			*loc32 = cpu_to_le32(le32_to_cpu(*loc32)
+				+ (((value - address) >> 2) & 0x3ffffff));
+			break;
+		case R_AARCH64_CALL26:
+			loc32 = location;
+			*loc32 = cpu_to_le32(le32_to_cpu(*loc32)
+				+ (((value - address) >> 2) & 0x3ffffff));
+			break;
+		case R_AARCH64_LDST64_ABS_LO12_NC:
+			if (value & 7) {
+				pr_err("reloc: Unaligned value: %lx\n", value);
+				return -ENOEXEC;
+			}
+			loc32 = location;
+			*loc32 = cpu_to_le32(le32_to_cpu(*loc32)
+				+ ((value & 0xff8) << (10 - 3)));
+			break;
+		default:
+			pr_err("reloc: Unknown relocation type: %llu\n",
+			       ELF64_R_TYPE(rel[i].r_info));
+			return -ENOEXEC;
+		}
+	}
+
+	return 0;
+}
diff --git a/arch/arm64/purgatory/Makefile b/arch/arm64/purgatory/Makefile
new file mode 100644
index 000000000000..c2127a2cbd51
--- /dev/null
+++ b/arch/arm64/purgatory/Makefile
@@ -0,0 +1,24 @@
+OBJECT_FILES_NON_STANDARD := y
+
+purgatory-y := entry.o
+
+targets += $(purgatory-y)
+PURGATORY_OBJS = $(addprefix $(obj)/,$(purgatory-y))
+
+LDFLAGS_purgatory.ro := -e purgatory_start -r --no-undefined \
+					-nostdlib -z nodefaultlib
+targets += purgatory.ro
+
+$(obj)/purgatory.ro: $(PURGATORY_OBJS) FORCE
+		$(call if_changed,ld)
+
+targets += kexec_purgatory.c
+
+CMD_BIN2C = $(objtree)/scripts/basic/bin2c
+quiet_cmd_bin2c = BIN2C $@
+	cmd_bin2c = $(CMD_BIN2C) kexec_purgatory < $< > $@
+
+$(obj)/kexec_purgatory.c: $(obj)/purgatory.ro FORCE
+	$(call if_changed,bin2c)
+
+obj-${CONFIG_KEXEC_FILE}	+= kexec_purgatory.o
diff --git a/arch/arm64/purgatory/entry.S b/arch/arm64/purgatory/entry.S
new file mode 100644
index 000000000000..bc4e6b3bf8a1
--- /dev/null
+++ b/arch/arm64/purgatory/entry.S
@@ -0,0 +1,28 @@
+/*
+ * kexec core purgatory
+ */
+#include <linux/linkage.h>
+
+.text
+
+ENTRY(purgatory_start)
+	/* Start new image. */
+	ldr	x17, arm64_kernel_entry
+	ldr	x0, arm64_dtb_addr
+	mov	x1, xzr
+	mov	x2, xzr
+	mov	x3, xzr
+	br	x17
+END(purgatory_start)
+
+.data
+
+.align 3
+
+ENTRY(arm64_kernel_entry)
+	.quad	0
+END(arm64_kernel_entry)
+
+ENTRY(arm64_dtb_addr)
+	.quad	0
+END(arm64_dtb_addr)
-- 
2.14.1




More information about the kexec mailing list