Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > linux.kernel > #1428116 > unrolled thread
| Started by | Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com> |
|---|---|
| First post | 2016-06-21 21:50 +0200 |
| Last post | 2016-06-23 18:50 +0200 |
| Articles | 13 — 3 participants |
Back to article view | Back to linux.kernel
[PATCH v3 0/9] kexec_file_load implementation for PowerPC Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com> - 2016-06-21 21:50 +0200
[PATCH v3 9/9] powerpc: Add purgatory for kexec_file_load implementation. Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com> - 2016-06-21 21:50 +0200
[PATCH v3 2/9] kexec_file: Generalize kexec_add_buffer. Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com> - 2016-06-21 22:00 +0200
Re: [PATCH v3 2/9] kexec_file: Generalize kexec_add_buffer. Dave Young <dyoung@redhat.com> - 2016-06-22 12:30 +0200
Re: [PATCH v3 2/9] kexec_file: Generalize kexec_add_buffer. Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com> - 2016-06-23 01:40 +0200
Re: [PATCH v3 2/9] kexec_file: Generalize kexec_add_buffer. Dave Young <dyoung@redhat.com> - 2016-06-23 04:30 +0200
[PATCH v3 4/9] powerpc: Factor out relocation code from module_64.c to elf_util_64.c. Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com> - 2016-06-21 22:00 +0200
[PATCH v3 5/9] powerpc: Generalize elf64_apply_relocate_add. Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com> - 2016-06-21 22:00 +0200
[PATCH v3 7/9] powerpc: Implement kexec_file_load. Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com> - 2016-06-21 22:00 +0200
Re: [PATCH v3 0/9] kexec_file_load implementation for PowerPC Balbir Singh <bsingharora@gmail.com> - 2016-06-22 15:40 +0200
Re: [PATCH v3 0/9] kexec_file_load implementation for PowerPC Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com> - 2016-06-22 19:10 +0200
Re: [PATCH v3 0/9] kexec_file_load implementation for PowerPC Balbir Singh <bsingharora@gmail.com> - 2016-06-23 02:00 +0200
Re: [PATCH v3 0/9] kexec_file_load implementation for PowerPC Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com> - 2016-06-23 18:50 +0200
| From | Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com> |
|---|---|
| Date | 2016-06-21 21:50 +0200 |
| Subject | [PATCH v3 0/9] kexec_file_load implementation for PowerPC |
| Message-ID | <rMzQl-1Gf-3@gated-at.bofh.it> |
Hello,
This patch series implements the kexec_file_load system call on PowerPC.
This system call moves the reading of the kernel, initrd and the device tree
from the userspace kexec tool to the kernel. This is needed if you want to
do one or both of the following:
1. only allow loading of signed kernels.
2. "measure" (i.e., record the hashes of) the kernel, initrd, kernel
command line and other boot inputs for the Integrity Measurement
Architecture subsystem.
The above are the functions kexec already has built into kexec_file_load.
Yesterday I posted a set of patches which allows a third feature:
3. have IMA pass-on its event log (where integrity measurements are
registered) accross kexec to the second kernel, so that the event
history is preserved.
Because OpenPower uses an intermediary Linux instance as a boot loader
(skiroot), feature 1 is needed to implement secure boot for the platform,
while features 2 and 3 are needed to implement trusted boot.
This patch series starts by removing an x86 assumption from kexec_file:
kexec_add_buffer uses iomem to find reserved memory ranges, but PowerPC
uses the memblock subsystem. A hook is added so that each arch can
specify how memory ranges can be found.
Also, the memory-walking logic in kexec_add_buffer is useful in this
implementation to find a free area for the purgatory's stack, so the
next patch moves that logic to kexec_locate_mem_hole.
The kexec_file_load system call needs to apply relocations to the
purgatory but adding code for that would duplicate functionality with
the module loading mechanism, which also needs to apply relocations to
the kernel modules. Therefore, this patch series factors out the module
relocation code so that it can be shared.
One thing that is still missing is crashkernel support, which I intend
to submit shortly. For now, arch_kexec_kernel_image_probe rejects crash
kernels.
This code is based on kexec-tools, but with many modifications to adapt
it to the kernel environment and facilities. Except the purgatory,
which only has minimal changes.
Changes for v3:
- Rebased series on today's powerpc/next.
- Patch "kexec_file: Generalize kexec_add_buffer.":
- Removed most arguments from arch_kexec_walk_mem and pass kbuf
explicitly.
- Patch "powerpc: Add functions to read ELF files of any endianness.":
- Fixed whitespace issues found by checkpatch.pl.
- Patch "powerpc: Factor out relocation code from module_64.c to
elf_util_64.c.":
- Changed to use the new PPC64_ELF_ABI_v2 macro.
- Patch "powerpc: Add support for loading ELF kernels with
kexec_file_load.":
- Adapted arch_kexec_walk_mem implementation to changes in its
argument list.
- Fixed whitespace and GPL header issues found by checkpatch.pl.
- Patch "powerpc: Add purgatory for kexec_file_load implementation.":
- Fixed whitespace and GPL header issues found by checkpatch.pl.
- Changed to use the new PPC64_ELF_ABI_v2 macro.
Changes for v2:
- All patches: forgot to add Signed-off-by lines in v1, so added them now.
- Patch "kexec_file: Generalize kexec_add_buffer.": broke in two, one
adding arch_kexec_walk_mem and the other adding kexec_locate_mem_hole.
- Patch "powerpc: Implement kexec_file_load.":
- Moved relocation changes and the arch_kexec_walk_mem implementation
to the next patch in the series.
- Removed pr_fmt from machine_kexec_64.c, since the patch doesn't add
any call to pr_debug in that file.
- Changed arch_kexec_kernel_image_probe to reject crash kernels.
Changes for v3:
- Rebased series on today's powerpc/next.
- Patch "kexec_file: Generalize kexec_add_buffer.":
- Removed most arguments from arch_kexec_walk_mem and pass kbuf
explicitly.
- Patch "powerpc: Add functions to read ELF files of any endianness.":
- Fixed whitespace issues found by checkpatch.pl.
- Patch "powerpc: Factor out relocation code from module_64.c to
elf_util_64.c.":
- Changed to use the new PPC64_ELF_ABI_v2 macro.
- Patch "powerpc: Add support for loading ELF kernels with
kexec_file_load.":
- Adapted arch_kexec_walk_mem implementation to changes in its
argument list.
- Fixed whitespace and GPL header issues found by checkpatch.pl.
- Patch "powerpc: Add purgatory for kexec_file_load implementation.":
- Fixed whitespace and GPL header issues found by checkpatch.pl.
- Changed to use the new PPC64_ELF_ABI_v2 macro.
Changes for v2:
- All patches: forgot to add Signed-off-by lines in v1, so added them now.
- Patch "kexec_file: Generalize kexec_add_buffer.": broke in two, one
adding arch_kexec_walk_mem and the other adding kexec_locate_mem_hole.
- Patch "powerpc: Implement kexec_file_load.":
- Moved relocation changes and the arch_kexec_walk_mem implementation
to the next patch in the series.
- Removed pr_fmt from machine_kexec_64.c, since the patch doesn't add
any call to pr_debug in that file.
- Changed arch_kexec_kernel_image_probe to reject crash kernels.
Thiago Jung Bauermann (9):
kexec_file: Remove unused members from struct kexec_buf.
kexec_file: Generalize kexec_add_buffer.
kexec_file: Factor out kexec_locate_mem_hole from kexec_add_buffer.
powerpc: Factor out relocation code from module_64.c to elf_util_64.c.
powerpc: Generalize elf64_apply_relocate_add.
powerpc: Add functions to read ELF files of any endianness.
powerpc: Implement kexec_file_load.
powerpc: Add support for loading ELF kernels with kexec_file_load.
powerpc: Add purgatory for kexec_file_load implementation.
arch/powerpc/Kconfig | 13 +
arch/powerpc/Makefile | 4 +
arch/powerpc/include/asm/elf_util.h | 92 +++++
arch/powerpc/include/asm/kexec_elf_64.h | 10 +
arch/powerpc/include/asm/module.h | 14 +-
arch/powerpc/include/asm/systbl.h | 1 +
arch/powerpc/include/asm/unistd.h | 2 +-
arch/powerpc/include/uapi/asm/unistd.h | 1 +
arch/powerpc/kernel/Makefile | 7 +
arch/powerpc/kernel/elf_util.c | 476 +++++++++++++++++++++++++
arch/powerpc/kernel/elf_util_64.c | 374 ++++++++++++++++++++
arch/powerpc/kernel/kexec_elf_64.c | 560 ++++++++++++++++++++++++++++++
arch/powerpc/kernel/machine_kexec_64.c | 134 +++++++
arch/powerpc/kernel/module_64.c | 328 +++--------------
arch/powerpc/purgatory/.gitignore | 2 +
arch/powerpc/purgatory/Makefile | 36 ++
arch/powerpc/purgatory/console-ppc64.c | 38 ++
arch/powerpc/purgatory/crashdump-ppc64.h | 42 +++
arch/powerpc/purgatory/crashdump_backup.c | 36 ++
arch/powerpc/purgatory/crtsavres.S | 5 +
arch/powerpc/purgatory/hvCall.S | 27 ++
arch/powerpc/purgatory/hvCall.h | 8 +
arch/powerpc/purgatory/kexec-sha256.h | 11 +
arch/powerpc/purgatory/ppc64_asm.h | 20 ++
arch/powerpc/purgatory/printf.c | 164 +++++++++
arch/powerpc/purgatory/purgatory-ppc64.c | 41 +++
arch/powerpc/purgatory/purgatory-ppc64.h | 6 +
arch/powerpc/purgatory/purgatory.c | 62 ++++
arch/powerpc/purgatory/purgatory.h | 11 +
arch/powerpc/purgatory/sha256.c | 6 +
arch/powerpc/purgatory/sha256.h | 1 +
arch/powerpc/purgatory/string.S | 1 +
arch/powerpc/purgatory/v2wrap.S | 134 +++++++
include/linux/kexec.h | 23 +-
kernel/kexec_file.c | 100 ++++--
kernel/kexec_internal.h | 16 -
36 files changed, 2476 insertions(+), 330 deletions(-)
create mode 100644 arch/powerpc/include/asm/elf_util.h
create mode 100644 arch/powerpc/include/asm/kexec_elf_64.h
create mode 100644 arch/powerpc/kernel/elf_util.c
create mode 100644 arch/powerpc/kernel/elf_util_64.c
create mode 100644 arch/powerpc/kernel/kexec_elf_64.c
create mode 100644 arch/powerpc/purgatory/.gitignore
create mode 100644 arch/powerpc/purgatory/Makefile
create mode 100644 arch/powerpc/purgatory/console-ppc64.c
create mode 100644 arch/powerpc/purgatory/crashdump-ppc64.h
create mode 100644 arch/powerpc/purgatory/crashdump_backup.c
create mode 100644 arch/powerpc/purgatory/crtsavres.S
create mode 100644 arch/powerpc/purgatory/hvCall.S
create mode 100644 arch/powerpc/purgatory/hvCall.h
create mode 100644 arch/powerpc/purgatory/kexec-sha256.h
create mode 100644 arch/powerpc/purgatory/ppc64_asm.h
create mode 100644 arch/powerpc/purgatory/printf.c
create mode 100644 arch/powerpc/purgatory/purgatory-ppc64.c
create mode 100644 arch/powerpc/purgatory/purgatory-ppc64.h
create mode 100644 arch/powerpc/purgatory/purgatory.c
create mode 100644 arch/powerpc/purgatory/purgatory.h
create mode 100644 arch/powerpc/purgatory/sha256.c
create mode 100644 arch/powerpc/purgatory/sha256.h
create mode 100644 arch/powerpc/purgatory/string.S
create mode 100644 arch/powerpc/purgatory/v2wrap.S
--
1.9.1
[toc] | [next] | [standalone]
| From | Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com> |
|---|---|
| Date | 2016-06-21 21:50 +0200 |
| Subject | [PATCH v3 9/9] powerpc: Add purgatory for kexec_file_load implementation. |
| Message-ID | <rMzQm-1Gf-33@gated-at.bofh.it> |
| In reply to | #1428116 |
This purgatory implementation comes from kexec-tools, almost unchanged.
The only changes were that the sha256_regions global variable was
renamed to sha_regions to match what kexec_file_load expects, and to
use the sha256.c file from x86's purgatory to avoid adding yet another
SHA-256 implementation.
Also, some formatting warnings found by checkpatch.pl were fixed.
Signed-off-by: Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com>
Cc: kexec@lists.infradead.org
Cc: linux-kernel@vger.kernel.org
---
arch/powerpc/Makefile | 4 +
arch/powerpc/purgatory/.gitignore | 2 +
arch/powerpc/purgatory/Makefile | 36 +++++++
arch/powerpc/purgatory/console-ppc64.c | 38 +++++++
arch/powerpc/purgatory/crashdump-ppc64.h | 42 ++++++++
arch/powerpc/purgatory/crashdump_backup.c | 36 +++++++
arch/powerpc/purgatory/crtsavres.S | 5 +
arch/powerpc/purgatory/hvCall.S | 27 +++++
arch/powerpc/purgatory/hvCall.h | 8 ++
arch/powerpc/purgatory/kexec-sha256.h | 11 ++
arch/powerpc/purgatory/ppc64_asm.h | 20 ++++
arch/powerpc/purgatory/printf.c | 164 ++++++++++++++++++++++++++++++
arch/powerpc/purgatory/purgatory-ppc64.c | 41 ++++++++
arch/powerpc/purgatory/purgatory-ppc64.h | 6 ++
arch/powerpc/purgatory/purgatory.c | 62 +++++++++++
arch/powerpc/purgatory/purgatory.h | 11 ++
arch/powerpc/purgatory/sha256.c | 6 ++
arch/powerpc/purgatory/sha256.h | 1 +
arch/powerpc/purgatory/string.S | 1 +
arch/powerpc/purgatory/v2wrap.S | 134 ++++++++++++++++++++++++
20 files changed, 655 insertions(+)
diff --git a/arch/powerpc/Makefile b/arch/powerpc/Makefile
index 709a22a3e824..293322855cce 100644
--- a/arch/powerpc/Makefile
+++ b/arch/powerpc/Makefile
@@ -249,6 +249,7 @@ core-y += arch/powerpc/kernel/ \
core-$(CONFIG_XMON) += arch/powerpc/xmon/
core-$(CONFIG_KVM) += arch/powerpc/kvm/
core-$(CONFIG_PERF_EVENTS) += arch/powerpc/perf/
+core-$(CONFIG_KEXEC_FILE) += arch/powerpc/purgatory/
drivers-$(CONFIG_OPROFILE) += arch/powerpc/oprofile/
@@ -370,6 +371,9 @@ archclean:
$(Q)$(MAKE) $(clean)=$(boot)
archprepare: checkbin
+ifeq ($(CONFIG_KEXEC_FILE),y)
+ $(Q)$(MAKE) $(build)=arch/powerpc/purgatory arch/powerpc/purgatory/kexec-purgatory.c
+endif
# Use the file '.tmp_gas_check' for binutils tests, as gas won't output
# to stdout and these checks are run even on install targets.
diff --git a/arch/powerpc/purgatory/.gitignore b/arch/powerpc/purgatory/.gitignore
new file mode 100644
index 000000000000..e9e66f178a6d
--- /dev/null
+++ b/arch/powerpc/purgatory/.gitignore
@@ -0,0 +1,2 @@
+kexec-purgatory.c
+purgatory.ro
diff --git a/arch/powerpc/purgatory/Makefile b/arch/powerpc/purgatory/Makefile
new file mode 100644
index 000000000000..63daf95e5703
--- /dev/null
+++ b/arch/powerpc/purgatory/Makefile
@@ -0,0 +1,36 @@
+purgatory-y := purgatory.o printf.o string.o v2wrap.o hvCall.o \
+ purgatory-ppc64.o console-ppc64.o crashdump_backup.o \
+ crtsavres.o sha256.o
+
+targets += $(purgatory-y)
+PURGATORY_OBJS = $(addprefix $(obj)/,$(purgatory-y))
+
+LDFLAGS_purgatory.ro := -e purgatory_start -r --no-undefined -nostartfiles \
+ -nostdlib -nodefaultlibs
+targets += purgatory.ro
+
+# Default KBUILD_CFLAGS can have -pg option set when FTRACE is enabled. That
+# in turn leaves some undefined symbols like __fentry__ in purgatory and not
+# sure how to relocate those. Like kexec-tools, use custom flags.
+
+KBUILD_CFLAGS := -Wall -Wstrict-prototypes -fno-strict-aliasing \
+ -fno-zero-initialized-in-bss -fno-builtin -ffreestanding \
+ -fno-PIC -fno-PIE -fno-stack-protector -fno-exceptions \
+ -msoft-float -MD -Os
+KBUILD_CFLAGS += -m$(CONFIG_WORD_SIZE)
+
+$(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/powerpc/purgatory/console-ppc64.c b/arch/powerpc/purgatory/console-ppc64.c
new file mode 100644
index 000000000000..3d07be0b5d08
--- /dev/null
+++ b/arch/powerpc/purgatory/console-ppc64.c
@@ -0,0 +1,38 @@
+/*
+ * kexec: Linux boots Linux
+ *
+ * Created by: Mohan Kumar M (mohan@in.ibm.com)
+ *
+ * Copyright (C) IBM Corporation, 2005. All rights reserved
+ *
+ * Code taken from kexec-tools.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation (version 2 of the License).
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "hvCall.h"
+#include <asm/byteorder.h>
+
+extern int debug;
+
+void putchar(int c)
+{
+ char buff[8];
+ unsigned long *lbuf = (unsigned long *)buff;
+
+ if (!debug) /* running on non pseries */
+ return;
+
+ if (c == '\n')
+ putchar('\r');
+
+ buff[0] = c;
+ plpar_hcall_norets(H_PUT_TERM_CHAR, 0, 1, __cpu_to_be64(*lbuf), 0);
+}
diff --git a/arch/powerpc/purgatory/crashdump-ppc64.h b/arch/powerpc/purgatory/crashdump-ppc64.h
new file mode 100644
index 000000000000..90064b49ebfe
--- /dev/null
+++ b/arch/powerpc/purgatory/crashdump-ppc64.h
@@ -0,0 +1,42 @@
+#ifndef CRASHDUMP_PPC64_H
+#define CRASHDUMP_PPC64_H
+
+#include <linux/types.h>
+
+struct kexec_info;
+int load_crashdump_segments(struct kexec_info *info, char *mod_cmdline,
+ uint64_t max_addr, unsigned long min_base);
+void add_usable_mem_rgns(unsigned long long base, unsigned long long size);
+
+#define PAGE_OFFSET 0xC000000000000000ULL
+#define KERNELBASE PAGE_OFFSET
+#define VMALLOCBASE 0xD000000000000000ULL
+
+#define __pa(x) ((unsigned long)(x)-PAGE_OFFSET)
+#define MAXMEM (-KERNELBASE-VMALLOCBASE)
+
+#define COMMAND_LINE_SIZE 512 /* from kernel */
+/* Backup Region, First 64K of System RAM. */
+#define BACKUP_SRC_START 0x0000
+#define BACKUP_SRC_END 0xffff
+#define BACKUP_SRC_SIZE (BACKUP_SRC_END - BACKUP_SRC_START + 1)
+
+#define KDUMP_BACKUP_LIMIT BACKUP_SRC_SIZE
+
+#define KERNEL_RUN_AT_ZERO_MAGIC 0x72756e30 /* "run0" */
+
+extern uint64_t crash_base;
+extern uint64_t crash_size;
+extern uint64_t memory_limit;
+extern unsigned int rtas_base;
+extern unsigned int rtas_size;
+extern uint64_t opal_base;
+extern uint64_t opal_size;
+
+uint64_t lmb_size;
+unsigned int num_of_lmbs;
+
+#define DRCONF_ADDR 0
+#define DRCONF_FLAGS 20
+
+#endif /* CRASHDUMP_PPC64_H */
diff --git a/arch/powerpc/purgatory/crashdump_backup.c b/arch/powerpc/purgatory/crashdump_backup.c
new file mode 100644
index 000000000000..11ccafdcc9ad
--- /dev/null
+++ b/arch/powerpc/purgatory/crashdump_backup.c
@@ -0,0 +1,36 @@
+/*
+ * kexec: Linux boots Linux
+ *
+ * Created by: Mohan Kumar M (mohan@in.ibm.com)
+ *
+ * Copyright (C) IBM Corporation, 2005. All rights reserved
+ *
+ * Code taken from kexec-tools.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation (version 2 of the License).
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "../boot/string.h"
+#include "crashdump-ppc64.h"
+
+extern unsigned long backup_start;
+
+/* Backup first 32KB of memory to backup region reserved by kexec */
+void crashdump_backup_memory(void)
+{
+ void *dest, *src;
+
+ src = (void *)BACKUP_SRC_START;
+
+ if (backup_start) {
+ dest = (void *)(backup_start);
+ memcpy(dest, src, BACKUP_SRC_SIZE);
+ }
+}
diff --git a/arch/powerpc/purgatory/crtsavres.S b/arch/powerpc/purgatory/crtsavres.S
new file mode 100644
index 000000000000..5d17e1c0d575
--- /dev/null
+++ b/arch/powerpc/purgatory/crtsavres.S
@@ -0,0 +1,5 @@
+#ifndef CONFIG_CC_OPTIMIZE_FOR_SIZE
+#define CONFIG_CC_OPTIMIZE_FOR_SIZE 1
+#endif
+
+#include "../lib/crtsavres.S"
diff --git a/arch/powerpc/purgatory/hvCall.S b/arch/powerpc/purgatory/hvCall.S
new file mode 100644
index 000000000000..a96c4898f1d8
--- /dev/null
+++ b/arch/powerpc/purgatory/hvCall.S
@@ -0,0 +1,27 @@
+/*
+ * This file contains the generic function to perform a call to the
+ * pSeries LPAR hypervisor.
+ *
+ * Taken from linux/arch/powerpc/platforms/pseries/hvCall.S
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#include "ppc64_asm.h"
+
+#define HVSC .long 0x44000022
+.text
+ .machine ppc64
+.globl DOTSYM(plpar_hcall_norets)
+DOTSYM(plpar_hcall_norets):
+ or 6,6,6 # medium low priority
+ mfcr 0
+ stw 0,8(1)
+
+ HVSC /* invoke the hypervisor */
+
+ lwz 0,8(1)
+ mtcrf 0xff,0
+ blr /* return r3 = status */
diff --git a/arch/powerpc/purgatory/hvCall.h b/arch/powerpc/purgatory/hvCall.h
new file mode 100644
index 000000000000..187e24d8b964
--- /dev/null
+++ b/arch/powerpc/purgatory/hvCall.h
@@ -0,0 +1,8 @@
+#ifndef HVCALL_H
+#define HVCALL_H
+
+#define H_PUT_TERM_CHAR 0x58
+
+long plpar_hcall_norets(unsigned long opcode, ...);
+
+#endif
diff --git a/arch/powerpc/purgatory/kexec-sha256.h b/arch/powerpc/purgatory/kexec-sha256.h
new file mode 100644
index 000000000000..4418ed02c052
--- /dev/null
+++ b/arch/powerpc/purgatory/kexec-sha256.h
@@ -0,0 +1,11 @@
+#ifndef KEXEC_SHA256_H
+#define KEXEC_SHA256_H
+
+struct kexec_sha_region {
+ unsigned long start;
+ unsigned long len;
+};
+
+#define SHA256_REGIONS 16
+
+#endif /* KEXEC_SHA256_H */
diff --git a/arch/powerpc/purgatory/ppc64_asm.h b/arch/powerpc/purgatory/ppc64_asm.h
new file mode 100644
index 000000000000..95d721718237
--- /dev/null
+++ b/arch/powerpc/purgatory/ppc64_asm.h
@@ -0,0 +1,20 @@
+/*
+ * ppc64_asm.h - common defines for PPC64 assembly parts
+ *
+ * Code taken from kexec-tools.
+ *
+ * This source code is licensed under the GNU General Public License,
+ * Version 2. See the file COPYING for more details.
+ */
+
+#include <asm/types.h>
+
+/*
+ * ABIv1 requires dot symbol while ABIv2 does not.
+ */
+#ifdef PPC64_ELF_ABI_v2
+#define DOTSYM(a) a
+#else
+#define GLUE(a, b) a##b
+#define DOTSYM(a) GLUE(., a)
+#endif
diff --git a/arch/powerpc/purgatory/printf.c b/arch/powerpc/purgatory/printf.c
new file mode 100644
index 000000000000..c5f425b55fd5
--- /dev/null
+++ b/arch/powerpc/purgatory/printf.c
@@ -0,0 +1,164 @@
+/*
+ * Code taken from kexec-tools.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation (version 2 of the License).
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <stdarg.h>
+#include "purgatory.h"
+#include "../boot/string.h"
+
+#define CHAR_BIT 8
+
+/*
+ * Output
+ * =============================================================================
+ */
+
+#define LONG_LONG_SHIFT ((int)((sizeof(unsigned long long)*CHAR_BIT) - 4))
+#define LONG_SHIFT ((int)((sizeof(unsigned long)*CHAR_BIT) - 4))
+#define INT_SHIFT ((int)((sizeof(unsigned int)*CHAR_BIT) - 4))
+#define SHRT_SHIFT ((int)((sizeof(unsigned short)*CHAR_BIT) - 4))
+#define CHAR_SHIFT ((int)((sizeof(unsigned char)*CHAR_BIT) - 4))
+
+/**************************************************************************
+PRINTF and friends
+
+ Formats:
+ %x - 4 bytes int (8 hex digits, lower case)
+ %X - 4 bytes int (8 hex digits, upper case)
+ %lx - 8 bytes long (16 hex digits, lower case)
+ %lX - 8 bytes long (16 hex digits, upper case)
+ %hx - 2 bytes int (4 hex digits, lower case)
+ %hX - 2 bytes int (4 hex digits, upper case)
+ %hhx - 1 byte int (2 hex digits, lower case)
+ %hhX - 1 byte int (2 hex digits, upper case)
+ - optional # prefixes 0x or 0X
+ %d - decimal int
+ %c - char
+ %s - string
+ Note: width specification not supported
+**************************************************************************/
+void vsprintf(char *buffer, const char *fmt, va_list args)
+{
+ char *p;
+
+ for ( ; *fmt != '\0'; ++fmt) {
+ if (*fmt != '%') {
+ if (buffer)
+ *buffer++ = *fmt;
+ else
+ putchar(*fmt);
+ continue;
+ }
+ if (*++fmt == 's') {
+ for (p = va_arg(args, char *); *p != '\0'; p++)
+ if (buffer)
+ *buffer++ = *p;
+ else
+ putchar(*p);
+ } else { /* Length of item is bounded */
+ char tmp[40], *q = tmp;
+ int shift = INT_SHIFT;
+
+ if (*fmt == 'L') {
+ shift = LONG_LONG_SHIFT;
+ fmt++;
+ } else if (*fmt == 'l') {
+ shift = LONG_SHIFT;
+ fmt++;
+ } else if (*fmt == 'h') {
+ shift = SHRT_SHIFT;
+ fmt++;
+ if (*fmt == 'h') {
+ shift = CHAR_SHIFT;
+ fmt++;
+ }
+ }
+
+ /*
+ * Before each format q points to tmp buffer
+ * After each format q points past end of item
+ */
+ if ((*fmt | 0x20) == 'x') {
+ /* With x86 gcc, sizeof(long) == sizeof(int) */
+ unsigned long long h;
+ int ncase;
+
+ if (shift > LONG_SHIFT)
+ h = va_arg(args, unsigned long long);
+ else if (shift > INT_SHIFT)
+ h = va_arg(args, unsigned long);
+ else
+ h = va_arg(args, unsigned int);
+
+ ncase = (*fmt & 0x20);
+ for ( ; shift >= 0; shift -= 4)
+ *q++ = "0123456789ABCDEF"[(h >> shift) & 0xF] | ncase;
+ } else if (*fmt == 'd') {
+ char *r;
+ long i;
+
+ if (shift > LONG_SHIFT)
+ i = va_arg(args, long long);
+ else if (shift > INT_SHIFT)
+ i = va_arg(args, long);
+ else
+ i = va_arg(args, int);
+
+ if (i < 0) {
+ *q++ = '-';
+ i = -i;
+ }
+ p = q; /* save beginning of digits */
+ do {
+ *q++ = '0' + (i % 10);
+ i /= 10;
+ } while (i);
+ /* reverse digits, stop in middle */
+ r = q; /* don't alter q */
+ while (--r > p) {
+ i = *r;
+ *r = *p;
+ *p++ = i;
+ }
+ } else if (*fmt == 'c')
+ *q++ = va_arg(args, int);
+ else
+ *q++ = *fmt;
+ /* now output the saved string */
+ for (p = tmp; p < q; ++p)
+ if (buffer)
+ *buffer++ = *p;
+ else
+ putchar(*p);
+ }
+ }
+ if (buffer)
+ *buffer = '\0';
+}
+
+void sprintf(char *buffer, const char *fmt, ...)
+{
+ va_list args;
+
+ va_start(args, fmt);
+ vsprintf(buffer, fmt, args);
+ va_end(args);
+}
+
+void printf(const char *fmt, ...)
+{
+ va_list args;
+
+ va_start(args, fmt);
+ vsprintf(0, fmt, args);
+ va_end(args);
+}
diff --git a/arch/powerpc/purgatory/purgatory-ppc64.c b/arch/powerpc/purgatory/purgatory-ppc64.c
new file mode 100644
index 000000000000..0be65a424ab1
--- /dev/null
+++ b/arch/powerpc/purgatory/purgatory-ppc64.c
@@ -0,0 +1,41 @@
+/*
+ * kexec: Linux boots Linux
+ *
+ * Created by: Mohan Kumar M (mohan@in.ibm.com)
+ *
+ * Copyright (C) IBM Corporation, 2005. All rights reserved
+ *
+ * Code taken from kexec-tools.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation (version 2 of the License).
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "purgatory.h"
+#include "purgatory-ppc64.h"
+
+unsigned int panic_kernel = 0;
+unsigned long backup_start = 0;
+unsigned long stack = 0;
+unsigned long dt_offset = 0;
+unsigned long my_toc = 0;
+unsigned long kernel = 0;
+unsigned int debug = 0;
+unsigned long opal_base = 0;
+unsigned long opal_entry = 0;
+
+void setup_arch(void)
+{
+}
+
+void post_verification_setup_arch(void)
+{
+ if (panic_kernel)
+ crashdump_backup_memory();
+}
diff --git a/arch/powerpc/purgatory/purgatory-ppc64.h b/arch/powerpc/purgatory/purgatory-ppc64.h
new file mode 100644
index 000000000000..52eaf4394c48
--- /dev/null
+++ b/arch/powerpc/purgatory/purgatory-ppc64.h
@@ -0,0 +1,6 @@
+#ifndef PURGATORY_PPC64_H
+#define PURGATORY_PPC64_H
+
+void crashdump_backup_memory(void);
+
+#endif /* PURGATORY_PPC64_H */
diff --git a/arch/powerpc/purgatory/purgatory.c b/arch/powerpc/purgatory/purgatory.c
new file mode 100644
index 000000000000..5b006d685cf2
--- /dev/null
+++ b/arch/powerpc/purgatory/purgatory.c
@@ -0,0 +1,62 @@
+/*
+ * Code taken from kexec-tools.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation (version 2 of the License).
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "purgatory.h"
+#include "sha256.h"
+#include "../boot/string.h"
+#include "kexec-sha256.h"
+
+struct kexec_sha_region sha_regions[SHA256_REGIONS] = {};
+u8 sha256_digest[SHA256_DIGEST_SIZE] = { 0 };
+
+int verify_sha256_digest(void)
+{
+ struct kexec_sha_region *ptr, *end;
+ u8 digest[SHA256_DIGEST_SIZE];
+ size_t i;
+ struct sha256_state sctx;
+
+ sha256_init(&sctx);
+ end = &sha_regions[sizeof(sha_regions)/sizeof(sha_regions[0])];
+ for (ptr = sha_regions; ptr < end; ptr++)
+ sha256_update(&sctx, (uint8_t *)(ptr->start), ptr->len);
+ sha256_final(&sctx, digest);
+
+ if (memcmp(digest, sha256_digest, sizeof(digest)) != 0) {
+ printf("sha256 digests do not match :(\n");
+ printf(" digest: ");
+ for (i = 0; i < sizeof(digest); i++)
+ printf("%hhx ", digest[i]);
+ printf("\n");
+
+ printf("sha256_digest: ");
+ for (i = 0; i < sizeof(sha256_digest); i++)
+ printf("%hhx ", sha256_digest[i]);
+
+ printf("\n");
+ return 1;
+ }
+ return 0;
+}
+
+void purgatory(void)
+{
+ printf("I'm in purgatory\n");
+ setup_arch();
+ if (verify_sha256_digest()) {
+ /* loop forever */
+ for (;;)
+ ;
+ }
+ post_verification_setup_arch();
+}
diff --git a/arch/powerpc/purgatory/purgatory.h b/arch/powerpc/purgatory/purgatory.h
new file mode 100644
index 000000000000..788ce4930a30
--- /dev/null
+++ b/arch/powerpc/purgatory/purgatory.h
@@ -0,0 +1,11 @@
+#ifndef PURGATORY_H
+#define PURGATORY_H
+
+void putchar(int ch);
+void sprintf(char *buffer, const char *fmt, ...)
+ __attribute__ ((format (printf, 2, 3)));
+void printf(const char *fmt, ...) __attribute__ ((format (printf, 1, 2)));
+void setup_arch(void);
+void post_verification_setup_arch(void);
+
+#endif /* PURGATORY_H */
diff --git a/arch/powerpc/purgatory/sha256.c b/arch/powerpc/purgatory/sha256.c
new file mode 100644
index 000000000000..6abee1877d56
--- /dev/null
+++ b/arch/powerpc/purgatory/sha256.c
@@ -0,0 +1,6 @@
+#include "../boot/string.h"
+
+/* Avoid including x86's boot/string.h in sha256.c. */
+#define BOOT_STRING_H
+
+#include "../../x86/purgatory/sha256.c"
diff --git a/arch/powerpc/purgatory/sha256.h b/arch/powerpc/purgatory/sha256.h
new file mode 100644
index 000000000000..72818f3a207e
--- /dev/null
+++ b/arch/powerpc/purgatory/sha256.h
@@ -0,0 +1 @@
+#include "../../x86/purgatory/sha256.h"
diff --git a/arch/powerpc/purgatory/string.S b/arch/powerpc/purgatory/string.S
new file mode 100644
index 000000000000..3a1e23ff4017
--- /dev/null
+++ b/arch/powerpc/purgatory/string.S
@@ -0,0 +1 @@
+#include "../boot/string.S"
diff --git a/arch/powerpc/purgatory/v2wrap.S b/arch/powerpc/purgatory/v2wrap.S
new file mode 100644
index 000000000000..c9a981c39a78
--- /dev/null
+++ b/arch/powerpc/purgatory/v2wrap.S
@@ -0,0 +1,134 @@
+#
+# kexec: Linux boots Linux
+#
+# Copyright (C) 2004 - 2005, Milton D Miller II, IBM Corporation
+# Copyright (C) 2006, Mohan Kumar M (mohan@in.ibm.com), IBM Corporation
+#
+# Code taken from kexec-tools.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation (version 2 of the License).
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+#include "ppc64_asm.h"
+
+# v2wrap.S
+# a wrapper to call purgatory code to backup first
+# 32kB of first kernel into the backup region
+# reserved by kexec-tools.
+# Invokes ppc64 kernel with the expected arguments
+# of kernel(device-tree, phys-offset, 0)
+
+#
+# calling convention:
+# r3 = physical number of this cpu (all cpus)
+# r4 = address of this chunk (master only)
+# master enters at purgatory_start (aka first byte of this chunk)
+# slaves (additional cpus), if any, enter a copy of the
+# first 0x100 bytes of this code relocated to 0x0
+#
+# in other words,
+# a copy of the first 0x100 bytes of this code is copied to 0
+# and the slaves are sent to address 0x60
+# with r3 = their physical cpu number.
+
+#define LOADADDR(rn,name) \
+ lis rn,name##@highest; \
+ ori rn,rn,name##@higher; \
+ rldicr rn,rn,32,31; \
+ oris rn,rn,name##@h; \
+ ori rn,rn,name##@l
+
+ .machine ppc64
+ .align 8
+ .globl purgatory_start
+purgatory_start: b master
+ .org purgatory_start + 0x5c # ABI: possible run_at_load flag at 0x5c
+ .globl run_at_load
+run_at_load:
+ .long 0
+ .size run_at_load, . - run_at_load
+ .org purgatory_start + 0x60 # ABI: slaves start at 60 with r3=phys
+slave: b $
+ .org purgatory_start + 0x100 # ABI: end of copied region
+ .size purgatory_start, . - purgatory_start
+
+#
+# The above 0x100 bytes at purgatory_start are replaced with the
+# code from the kernel (or next stage) by kexec/arch/ppc64/kexec-elf-ppc64.c
+#
+
+master:
+ or 1,1,1 # low priority to let other threads catchup
+ isync
+ mr 17,3 # save cpu id to r17
+ mr 15,4 # save physical address in reg15
+
+ LOADADDR(6,my_toc)
+ ld 2,0(6) #setup toc
+
+ LOADADDR(6,stack)
+ ld 1,0(6) #setup stack
+
+ subi 1,1,112
+ bl DOTSYM(purgatory)
+ nop
+
+ or 3,3,3 # ok now to high priority, lets boot
+ lis 6,0x1
+ mtctr 6 # delay a bit for slaves to catch up
+83: bdnz 83b # before we overwrite 0-100 again
+
+ LOADADDR(16, dt_offset)
+ ld 3,0(16) # load device-tree address
+ mr 16,3 # save dt address in reg16
+#ifdef __BIG_ENDIAN__
+ lwz 6,20(3) # fetch version number
+#else
+ li 4,20
+ lwbrx 6,3,4 # fetch BE version number
+#endif
+ cmpwi 0,6,2 # v2 ?
+ blt 80f
+#ifdef __BIG_ENDIAN__
+ stw 17,28(3) # save my cpu number as boot_cpu_phys
+#else
+ li 4,28
+ stwbrx 17,3,4 # Store my cpu as BE value
+#endif
+80:
+ LOADADDR(6,opal_base) # For OPAL early debug
+ ld 8,0(6) # load the OPAL base address in r8
+ LOADADDR(6,opal_entry) # For OPAL early debug
+ ld 9,0(6) # load the OPAL entry address in r9
+ LOADADDR(6,kernel)
+ ld 4,0(6) # load the kernel address
+ LOADADDR(6,run_at_load) # the load flag
+ lwz 7,0(6) # possibly patched by kexec-elf-ppc64
+ stw 7,0x5c(4) # and patch it into the kernel
+ mr 3,16 # restore dt address
+
+ mfmsr 5
+ andi. 10,5,1 # test MSR_LE
+ bne little_endian
+
+ li 5,0 # r5 will be 0 for kernel
+ mtctr 4 # prepare branch to
+ bctr # start kernel
+
+little_endian: # book3s-only
+ mtsrr0 4 # prepare branch to
+
+ clrrdi 5,5,1 # clear MSR_LE
+ mtsrr1 5
+
+ li 5,0 # r5 will be 0 for kernel
+
+ # skip cache flush, do we care?
+
+ rfid # update MSR and start kernel
--
1.9.1
[toc] | [prev] | [next] | [standalone]
| From | Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com> |
|---|---|
| Date | 2016-06-21 22:00 +0200 |
| Subject | [PATCH v3 2/9] kexec_file: Generalize kexec_add_buffer. |
| Message-ID | <rMA02-1Ki-3@gated-at.bofh.it> |
| In reply to | #1428116 |
Allow architectures to specify different memory walking functions for
kexec_add_buffer. Intel uses iomem to track reserved memory ranges,
but PowerPC uses the memblock subsystem.
Signed-off-by: Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com>
Cc: Eric Biederman <ebiederm@xmission.com>
Cc: Dave Young <dyoung@redhat.com>
Cc: kexec@lists.infradead.org
Cc: linux-kernel@vger.kernel.org
---
include/linux/kexec.h | 19 ++++++++++++++++++-
kernel/kexec_file.c | 30 ++++++++++++++++++++++--------
kernel/kexec_internal.h | 14 --------------
3 files changed, 40 insertions(+), 23 deletions(-)
diff --git a/include/linux/kexec.h b/include/linux/kexec.h
index e8acb2b43dd9..3d91bcfc180d 100644
--- a/include/linux/kexec.h
+++ b/include/linux/kexec.h
@@ -146,7 +146,24 @@ struct kexec_file_ops {
kexec_verify_sig_t *verify_sig;
#endif
};
-#endif
+
+/*
+ * Keeps track of buffer parameters as provided by caller for requesting
+ * memory placement of buffer.
+ */
+struct kexec_buf {
+ struct kimage *image;
+ unsigned long mem;
+ unsigned long memsz;
+ unsigned long buf_align;
+ unsigned long buf_min;
+ unsigned long buf_max;
+ bool top_down; /* allocate from top of memory hole */
+};
+
+int __weak arch_kexec_walk_mem(struct kexec_buf *kbuf,
+ int (*func)(u64, u64, void *));
+#endif /* CONFIG_KEXEC_FILE */
struct kimage {
kimage_entry_t head;
diff --git a/kernel/kexec_file.c b/kernel/kexec_file.c
index b6eec7527e9f..b1f1f6402518 100644
--- a/kernel/kexec_file.c
+++ b/kernel/kexec_file.c
@@ -428,6 +428,27 @@ static int locate_mem_hole_callback(u64 start, u64 end, void *arg)
return locate_mem_hole_bottom_up(start, end, kbuf);
}
+/**
+ * arch_kexec_walk_mem - call func(data) on free memory regions
+ * @kbuf: Context info for the search. Also passed to @func.
+ * @func: Function to call for each memory region.
+ *
+ * Return: The memory walk will stop when func returns a non-zero value
+ * and that value will be returned. If all free regions are visited without
+ * func returning non-zero, then zero will be returned.
+ */
+int __weak arch_kexec_walk_mem(struct kexec_buf *kbuf,
+ int (*func)(u64, u64, void *))
+{
+ if (kbuf->image->type == KEXEC_TYPE_CRASH)
+ return walk_iomem_res_desc(crashk_res.desc,
+ IORESOURCE_SYSTEM_RAM | IORESOURCE_BUSY,
+ crashk_res.start, crashk_res.end,
+ kbuf, func);
+ else
+ return walk_system_ram_res(0, ULONG_MAX, kbuf, func);
+}
+
/*
* Helper function for placing a buffer in a kexec segment. This assumes
* that kexec_mutex is held.
@@ -472,14 +493,7 @@ int kexec_add_buffer(struct kimage *image, char *buffer, unsigned long bufsz,
kbuf->top_down = top_down;
/* Walk the RAM ranges and allocate a suitable range for the buffer */
- if (image->type == KEXEC_TYPE_CRASH)
- ret = walk_iomem_res_desc(crashk_res.desc,
- IORESOURCE_SYSTEM_RAM | IORESOURCE_BUSY,
- crashk_res.start, crashk_res.end, kbuf,
- locate_mem_hole_callback);
- else
- ret = walk_system_ram_res(0, -1, kbuf,
- locate_mem_hole_callback);
+ ret = arch_kexec_walk_mem(kbuf, locate_mem_hole_callback);
if (ret != 1) {
/* A suitable memory range could not be found for buffer */
return -EADDRNOTAVAIL;
diff --git a/kernel/kexec_internal.h b/kernel/kexec_internal.h
index eefd5bf960c2..4cef7e4706b0 100644
--- a/kernel/kexec_internal.h
+++ b/kernel/kexec_internal.h
@@ -20,20 +20,6 @@ struct kexec_sha_region {
unsigned long len;
};
-/*
- * Keeps track of buffer parameters as provided by caller for requesting
- * memory placement of buffer.
- */
-struct kexec_buf {
- struct kimage *image;
- unsigned long mem;
- unsigned long memsz;
- unsigned long buf_align;
- unsigned long buf_min;
- unsigned long buf_max;
- bool top_down; /* allocate from top of memory hole */
-};
-
void kimage_file_post_load_cleanup(struct kimage *image);
#else /* CONFIG_KEXEC_FILE */
static inline void kimage_file_post_load_cleanup(struct kimage *image) { }
--
1.9.1
[toc] | [prev] | [next] | [standalone]
| From | Dave Young <dyoung@redhat.com> |
|---|---|
| Date | 2016-06-22 12:30 +0200 |
| Subject | Re: [PATCH v3 2/9] kexec_file: Generalize kexec_add_buffer. |
| Message-ID | <rMNzY-28F-15@gated-at.bofh.it> |
| In reply to | #1428119 |
The patch looks good, but could the subject be more specific?
For example just like the first sentence of the patch descriotion:
Allow architectures to specify their own memory walking function
On 06/21/16 at 04:48pm, Thiago Jung Bauermann wrote:
> Allow architectures to specify different memory walking functions for
> kexec_add_buffer. Intel uses iomem to track reserved memory ranges,
> but PowerPC uses the memblock subsystem.
>
> Signed-off-by: Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com>
> Cc: Eric Biederman <ebiederm@xmission.com>
> Cc: Dave Young <dyoung@redhat.com>
> Cc: kexec@lists.infradead.org
> Cc: linux-kernel@vger.kernel.org
> ---
> include/linux/kexec.h | 19 ++++++++++++++++++-
> kernel/kexec_file.c | 30 ++++++++++++++++++++++--------
> kernel/kexec_internal.h | 14 --------------
> 3 files changed, 40 insertions(+), 23 deletions(-)
>
> diff --git a/include/linux/kexec.h b/include/linux/kexec.h
> index e8acb2b43dd9..3d91bcfc180d 100644
> --- a/include/linux/kexec.h
> +++ b/include/linux/kexec.h
> @@ -146,7 +146,24 @@ struct kexec_file_ops {
> kexec_verify_sig_t *verify_sig;
> #endif
> };
> -#endif
> +
> +/*
> + * Keeps track of buffer parameters as provided by caller for requesting
> + * memory placement of buffer.
> + */
> +struct kexec_buf {
> + struct kimage *image;
> + unsigned long mem;
> + unsigned long memsz;
> + unsigned long buf_align;
> + unsigned long buf_min;
> + unsigned long buf_max;
> + bool top_down; /* allocate from top of memory hole */
> +};
> +
> +int __weak arch_kexec_walk_mem(struct kexec_buf *kbuf,
> + int (*func)(u64, u64, void *));
> +#endif /* CONFIG_KEXEC_FILE */
>
> struct kimage {
> kimage_entry_t head;
> diff --git a/kernel/kexec_file.c b/kernel/kexec_file.c
> index b6eec7527e9f..b1f1f6402518 100644
> --- a/kernel/kexec_file.c
> +++ b/kernel/kexec_file.c
> @@ -428,6 +428,27 @@ static int locate_mem_hole_callback(u64 start, u64 end, void *arg)
> return locate_mem_hole_bottom_up(start, end, kbuf);
> }
>
> +/**
> + * arch_kexec_walk_mem - call func(data) on free memory regions
> + * @kbuf: Context info for the search. Also passed to @func.
> + * @func: Function to call for each memory region.
> + *
> + * Return: The memory walk will stop when func returns a non-zero value
> + * and that value will be returned. If all free regions are visited without
> + * func returning non-zero, then zero will be returned.
> + */
> +int __weak arch_kexec_walk_mem(struct kexec_buf *kbuf,
> + int (*func)(u64, u64, void *))
> +{
> + if (kbuf->image->type == KEXEC_TYPE_CRASH)
> + return walk_iomem_res_desc(crashk_res.desc,
> + IORESOURCE_SYSTEM_RAM | IORESOURCE_BUSY,
> + crashk_res.start, crashk_res.end,
> + kbuf, func);
> + else
> + return walk_system_ram_res(0, ULONG_MAX, kbuf, func);
> +}
> +
> /*
> * Helper function for placing a buffer in a kexec segment. This assumes
> * that kexec_mutex is held.
> @@ -472,14 +493,7 @@ int kexec_add_buffer(struct kimage *image, char *buffer, unsigned long bufsz,
> kbuf->top_down = top_down;
>
> /* Walk the RAM ranges and allocate a suitable range for the buffer */
> - if (image->type == KEXEC_TYPE_CRASH)
> - ret = walk_iomem_res_desc(crashk_res.desc,
> - IORESOURCE_SYSTEM_RAM | IORESOURCE_BUSY,
> - crashk_res.start, crashk_res.end, kbuf,
> - locate_mem_hole_callback);
> - else
> - ret = walk_system_ram_res(0, -1, kbuf,
> - locate_mem_hole_callback);
> + ret = arch_kexec_walk_mem(kbuf, locate_mem_hole_callback);
> if (ret != 1) {
> /* A suitable memory range could not be found for buffer */
> return -EADDRNOTAVAIL;
> diff --git a/kernel/kexec_internal.h b/kernel/kexec_internal.h
> index eefd5bf960c2..4cef7e4706b0 100644
> --- a/kernel/kexec_internal.h
> +++ b/kernel/kexec_internal.h
> @@ -20,20 +20,6 @@ struct kexec_sha_region {
> unsigned long len;
> };
>
> -/*
> - * Keeps track of buffer parameters as provided by caller for requesting
> - * memory placement of buffer.
> - */
> -struct kexec_buf {
> - struct kimage *image;
> - unsigned long mem;
> - unsigned long memsz;
> - unsigned long buf_align;
> - unsigned long buf_min;
> - unsigned long buf_max;
> - bool top_down; /* allocate from top of memory hole */
> -};
> -
> void kimage_file_post_load_cleanup(struct kimage *image);
> #else /* CONFIG_KEXEC_FILE */
> static inline void kimage_file_post_load_cleanup(struct kimage *image) { }
Thanks
Dave
[toc] | [prev] | [next] | [standalone]
| From | Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com> |
|---|---|
| Date | 2016-06-23 01:40 +0200 |
| Subject | Re: [PATCH v3 2/9] kexec_file: Generalize kexec_add_buffer. |
| Message-ID | <rMZUu-1Ig-5@gated-at.bofh.it> |
| In reply to | #1428663 |
Am Mittwoch, 22 Juni 2016, 18:20:47 schrieb Dave Young:
> The patch looks good, but could the subject be more specific?
>
> For example just like the first sentence of the patch descriotion:
> Allow architectures to specify their own memory walking function
Ok, What about this? I also changed the description to refer to x86 arch
instead of Intel arch.
[]'s
Thiago Jung Bauermann
IBM Linux Technology Center
Subject: [PATCH 2/9] kexec_file: Allow arch-specific memory walking for
kexec_add_buffer
Allow architectures to specify a different memory walking function for
kexec_add_buffer. x86 uses iomem to track reserved memory ranges, but
PowerPC uses the memblock subsystem.
Signed-off-by: Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com>
Cc: Eric Biederman <ebiederm@xmission.com>
Cc: Dave Young <dyoung@redhat.com>
Cc: kexec@lists.infradead.org
Cc: linux-kernel@vger.kernel.org
---
include/linux/kexec.h | 19 ++++++++++++++++++-
kernel/kexec_file.c | 30 ++++++++++++++++++++++--------
kernel/kexec_internal.h | 14 --------------
3 files changed, 40 insertions(+), 23 deletions(-)
diff --git a/include/linux/kexec.h b/include/linux/kexec.h
index e8acb2b43dd9..3d91bcfc180d 100644
--- a/include/linux/kexec.h
+++ b/include/linux/kexec.h
@@ -146,7 +146,24 @@ struct kexec_file_ops {
kexec_verify_sig_t *verify_sig;
#endif
};
-#endif
+
+/*
+ * Keeps track of buffer parameters as provided by caller for requesting
+ * memory placement of buffer.
+ */
+struct kexec_buf {
+ struct kimage *image;
+ unsigned long mem;
+ unsigned long memsz;
+ unsigned long buf_align;
+ unsigned long buf_min;
+ unsigned long buf_max;
+ bool top_down; /* allocate from top of memory hole */
+};
+
+int __weak arch_kexec_walk_mem(struct kexec_buf *kbuf,
+ int (*func)(u64, u64, void *));
+#endif /* CONFIG_KEXEC_FILE */
struct kimage {
kimage_entry_t head;
diff --git a/kernel/kexec_file.c b/kernel/kexec_file.c
index b6eec7527e9f..b1f1f6402518 100644
--- a/kernel/kexec_file.c
+++ b/kernel/kexec_file.c
@@ -428,6 +428,27 @@ static int locate_mem_hole_callback(u64 start, u64 end, void *arg)
return locate_mem_hole_bottom_up(start, end, kbuf);
}
+/**
+ * arch_kexec_walk_mem - call func(data) on free memory regions
+ * @kbuf: Context info for the search. Also passed to @func.
+ * @func: Function to call for each memory region.
+ *
+ * Return: The memory walk will stop when func returns a non-zero value
+ * and that value will be returned. If all free regions are visited without
+ * func returning non-zero, then zero will be returned.
+ */
+int __weak arch_kexec_walk_mem(struct kexec_buf *kbuf,
+ int (*func)(u64, u64, void *))
+{
+ if (kbuf->image->type == KEXEC_TYPE_CRASH)
+ return walk_iomem_res_desc(crashk_res.desc,
+ IORESOURCE_SYSTEM_RAM | IORESOURCE_BUSY,
+ crashk_res.start, crashk_res.end,
+ kbuf, func);
+ else
+ return walk_system_ram_res(0, ULONG_MAX, kbuf, func);
+}
+
/*
* Helper function for placing a buffer in a kexec segment. This assumes
* that kexec_mutex is held.
@@ -472,14 +493,7 @@ int kexec_add_buffer(struct kimage *image, char *buffer, unsigned long bufsz,
kbuf->top_down = top_down;
/* Walk the RAM ranges and allocate a suitable range for the buffer */
- if (image->type == KEXEC_TYPE_CRASH)
- ret = walk_iomem_res_desc(crashk_res.desc,
- IORESOURCE_SYSTEM_RAM | IORESOURCE_BUSY,
- crashk_res.start, crashk_res.end, kbuf,
- locate_mem_hole_callback);
- else
- ret = walk_system_ram_res(0, -1, kbuf,
- locate_mem_hole_callback);
+ ret = arch_kexec_walk_mem(kbuf, locate_mem_hole_callback);
if (ret != 1) {
/* A suitable memory range could not be found for buffer */
return -EADDRNOTAVAIL;
diff --git a/kernel/kexec_internal.h b/kernel/kexec_internal.h
index eefd5bf960c2..4cef7e4706b0 100644
--- a/kernel/kexec_internal.h
+++ b/kernel/kexec_internal.h
@@ -20,20 +20,6 @@ struct kexec_sha_region {
unsigned long len;
};
-/*
- * Keeps track of buffer parameters as provided by caller for requesting
- * memory placement of buffer.
- */
-struct kexec_buf {
- struct kimage *image;
- unsigned long mem;
- unsigned long memsz;
- unsigned long buf_align;
- unsigned long buf_min;
- unsigned long buf_max;
- bool top_down; /* allocate from top of memory hole */
-};
-
void kimage_file_post_load_cleanup(struct kimage *image);
#else /* CONFIG_KEXEC_FILE */
static inline void kimage_file_post_load_cleanup(struct kimage *image) { }
--
1.9.1
[toc] | [prev] | [next] | [standalone]
| From | Dave Young <dyoung@redhat.com> |
|---|---|
| Date | 2016-06-23 04:30 +0200 |
| Subject | Re: [PATCH v3 2/9] kexec_file: Generalize kexec_add_buffer. |
| Message-ID | <rN2yZ-3yi-1@gated-at.bofh.it> |
| In reply to | #1429342 |
On 06/22/16 at 08:30pm, Thiago Jung Bauermann wrote:
> Am Mittwoch, 22 Juni 2016, 18:20:47 schrieb Dave Young:
> > The patch looks good, but could the subject be more specific?
> >
> > For example just like the first sentence of the patch descriotion:
> > Allow architectures to specify their own memory walking function
>
> Ok, What about this? I also changed the description to refer to x86 arch
> instead of Intel arch.
It looks good to me.
Thanks
Dave
>
> []'s
> Thiago Jung Bauermann
> IBM Linux Technology Center
>
>
> Subject: [PATCH 2/9] kexec_file: Allow arch-specific memory walking for
> kexec_add_buffer
>
> Allow architectures to specify a different memory walking function for
> kexec_add_buffer. x86 uses iomem to track reserved memory ranges, but
> PowerPC uses the memblock subsystem.
>
> Signed-off-by: Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com>
> Cc: Eric Biederman <ebiederm@xmission.com>
> Cc: Dave Young <dyoung@redhat.com>
> Cc: kexec@lists.infradead.org
> Cc: linux-kernel@vger.kernel.org
> ---
> include/linux/kexec.h | 19 ++++++++++++++++++-
> kernel/kexec_file.c | 30 ++++++++++++++++++++++--------
> kernel/kexec_internal.h | 14 --------------
> 3 files changed, 40 insertions(+), 23 deletions(-)
>
> diff --git a/include/linux/kexec.h b/include/linux/kexec.h
> index e8acb2b43dd9..3d91bcfc180d 100644
> --- a/include/linux/kexec.h
> +++ b/include/linux/kexec.h
> @@ -146,7 +146,24 @@ struct kexec_file_ops {
> kexec_verify_sig_t *verify_sig;
> #endif
> };
> -#endif
> +
> +/*
> + * Keeps track of buffer parameters as provided by caller for requesting
> + * memory placement of buffer.
> + */
> +struct kexec_buf {
> + struct kimage *image;
> + unsigned long mem;
> + unsigned long memsz;
> + unsigned long buf_align;
> + unsigned long buf_min;
> + unsigned long buf_max;
> + bool top_down; /* allocate from top of memory hole */
> +};
> +
> +int __weak arch_kexec_walk_mem(struct kexec_buf *kbuf,
> + int (*func)(u64, u64, void *));
> +#endif /* CONFIG_KEXEC_FILE */
>
> struct kimage {
> kimage_entry_t head;
> diff --git a/kernel/kexec_file.c b/kernel/kexec_file.c
> index b6eec7527e9f..b1f1f6402518 100644
> --- a/kernel/kexec_file.c
> +++ b/kernel/kexec_file.c
> @@ -428,6 +428,27 @@ static int locate_mem_hole_callback(u64 start, u64 end, void *arg)
> return locate_mem_hole_bottom_up(start, end, kbuf);
> }
>
> +/**
> + * arch_kexec_walk_mem - call func(data) on free memory regions
> + * @kbuf: Context info for the search. Also passed to @func.
> + * @func: Function to call for each memory region.
> + *
> + * Return: The memory walk will stop when func returns a non-zero value
> + * and that value will be returned. If all free regions are visited without
> + * func returning non-zero, then zero will be returned.
> + */
> +int __weak arch_kexec_walk_mem(struct kexec_buf *kbuf,
> + int (*func)(u64, u64, void *))
> +{
> + if (kbuf->image->type == KEXEC_TYPE_CRASH)
> + return walk_iomem_res_desc(crashk_res.desc,
> + IORESOURCE_SYSTEM_RAM | IORESOURCE_BUSY,
> + crashk_res.start, crashk_res.end,
> + kbuf, func);
> + else
> + return walk_system_ram_res(0, ULONG_MAX, kbuf, func);
> +}
> +
> /*
> * Helper function for placing a buffer in a kexec segment. This assumes
> * that kexec_mutex is held.
> @@ -472,14 +493,7 @@ int kexec_add_buffer(struct kimage *image, char *buffer, unsigned long bufsz,
> kbuf->top_down = top_down;
>
> /* Walk the RAM ranges and allocate a suitable range for the buffer */
> - if (image->type == KEXEC_TYPE_CRASH)
> - ret = walk_iomem_res_desc(crashk_res.desc,
> - IORESOURCE_SYSTEM_RAM | IORESOURCE_BUSY,
> - crashk_res.start, crashk_res.end, kbuf,
> - locate_mem_hole_callback);
> - else
> - ret = walk_system_ram_res(0, -1, kbuf,
> - locate_mem_hole_callback);
> + ret = arch_kexec_walk_mem(kbuf, locate_mem_hole_callback);
> if (ret != 1) {
> /* A suitable memory range could not be found for buffer */
> return -EADDRNOTAVAIL;
> diff --git a/kernel/kexec_internal.h b/kernel/kexec_internal.h
> index eefd5bf960c2..4cef7e4706b0 100644
> --- a/kernel/kexec_internal.h
> +++ b/kernel/kexec_internal.h
> @@ -20,20 +20,6 @@ struct kexec_sha_region {
> unsigned long len;
> };
>
> -/*
> - * Keeps track of buffer parameters as provided by caller for requesting
> - * memory placement of buffer.
> - */
> -struct kexec_buf {
> - struct kimage *image;
> - unsigned long mem;
> - unsigned long memsz;
> - unsigned long buf_align;
> - unsigned long buf_min;
> - unsigned long buf_max;
> - bool top_down; /* allocate from top of memory hole */
> -};
> -
> void kimage_file_post_load_cleanup(struct kimage *image);
> #else /* CONFIG_KEXEC_FILE */
> static inline void kimage_file_post_load_cleanup(struct kimage *image) { }
> --
> 1.9.1
>
>
>
> _______________________________________________
> kexec mailing list
> kexec@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/kexec
>
>
[toc] | [prev] | [next] | [standalone]
| From | Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com> |
|---|---|
| Date | 2016-06-21 22:00 +0200 |
| Subject | [PATCH v3 4/9] powerpc: Factor out relocation code from module_64.c to elf_util_64.c. |
| Message-ID | <rMA02-1Ki-9@gated-at.bofh.it> |
| In reply to | #1428116 |
The kexec_file_load system call needs to relocate the purgatory, so
factor out the module relocation code so that it can be shared.
This patch's purpose is to move the ELF relocation logic from
apply_relocate_add to elf_util_64.c with as few changes as
possible. The following changes were needed:
To avoid having module-specific code in a general purpose utility
function, struct elf_info was created to contain the information
needed for ELF binaries manipulation.
my_r2, stub_for_addr and create_stub were changed to use it instead of
having to receive a struct module, since they are called from
elf64_apply_relocate_add.
local_entry_offset and squash_toc_save_inst were only used by
apply_rellocate_add, so they were moved to elf_util_64.c as well.
Signed-off-by: Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Michael Ellerman <mpe@ellerman.id.au>
Cc: Torsten Duwe <duwe@suse.de>
Cc: kexec@lists.infradead.org
Cc: linux-kernel@vger.kernel.org
---
arch/powerpc/include/asm/elf_util.h | 70 ++++++++
arch/powerpc/include/asm/module.h | 14 +-
arch/powerpc/kernel/Makefile | 4 +
arch/powerpc/kernel/elf_util_64.c | 269 +++++++++++++++++++++++++++++++
arch/powerpc/kernel/module_64.c | 312 ++++--------------------------------
5 files changed, 386 insertions(+), 283 deletions(-)
diff --git a/arch/powerpc/include/asm/elf_util.h b/arch/powerpc/include/asm/elf_util.h
new file mode 100644
index 000000000000..37372559fe62
--- /dev/null
+++ b/arch/powerpc/include/asm/elf_util.h
@@ -0,0 +1,70 @@
+/*
+ * Utility functions to work with ELF files.
+ *
+ * Copyright (C) 2016, IBM Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _ASM_POWERPC_ELF_UTIL_H
+#define _ASM_POWERPC_ELF_UTIL_H
+
+#include <linux/elf.h>
+
+struct elf_info {
+ struct elf_shdr *sechdrs;
+
+ /* Index of stubs section. */
+ unsigned int stubs_section;
+ /* Index of TOC section. */
+ unsigned int toc_section;
+};
+
+#ifdef __powerpc64__
+#ifdef PPC64_ELF_ABI_v2
+
+/* An address is simply the address of the function. */
+typedef unsigned long func_desc_t;
+#else
+
+/* An address is address of the OPD entry, which contains address of fn. */
+typedef struct ppc64_opd_entry func_desc_t;
+#endif /* PPC64_ELF_ABI_v2 */
+
+/* Like PPC32, we need little trampolines to do > 24-bit jumps (into
+ the kernel itself). But on PPC64, these need to be used for every
+ jump, actually, to reset r2 (TOC+0x8000). */
+struct ppc64_stub_entry
+{
+ /* 28 byte jump instruction sequence (7 instructions). We only
+ * need 6 instructions on ABIv2 but we always allocate 7 so
+ * so we don't have to modify the trampoline load instruction. */
+ u32 jump[7];
+ /* Used by ftrace to identify stubs */
+ u32 magic;
+ /* Data for the above code */
+ func_desc_t funcdata;
+};
+#endif
+
+/* r2 is the TOC pointer: it actually points 0x8000 into the TOC (this
+ gives the value maximum span in an instruction which uses a signed
+ offset) */
+static inline unsigned long my_r2(const struct elf_info *elf_info)
+{
+ return elf_info->sechdrs[elf_info->toc_section].sh_addr + 0x8000;
+}
+
+int elf64_apply_relocate_add(const struct elf_info *elf_info,
+ const char *strtab, unsigned int symindex,
+ unsigned int relsec, const char *obj_name);
+
+#endif /* _ASM_POWERPC_ELF_UTIL_H */
diff --git a/arch/powerpc/include/asm/module.h b/arch/powerpc/include/asm/module.h
index cd4ffd86765f..f2073115d518 100644
--- a/arch/powerpc/include/asm/module.h
+++ b/arch/powerpc/include/asm/module.h
@@ -12,7 +12,14 @@
#include <linux/list.h>
#include <asm/bug.h>
#include <asm-generic/module.h>
+#include <asm/elf_util.h>
+/* Both low and high 16 bits are added as SIGNED additions, so if low
+ 16 bits has high bit set, high 16 bits must be adjusted. These
+ macros do that (stolen from binutils). */
+#define PPC_LO(v) ((v) & 0xffff)
+#define PPC_HI(v) (((v) >> 16) & 0xffff)
+#define PPC_HA(v) PPC_HI ((v) + 0x8000)
#ifndef __powerpc64__
/*
@@ -33,8 +40,7 @@ struct ppc_plt_entry {
struct mod_arch_specific {
#ifdef __powerpc64__
- unsigned int stubs_section; /* Index of stubs section in module */
- unsigned int toc_section; /* What section is the TOC? */
+ struct elf_info elf_info;
bool toc_fixed; /* Have we fixed up .TOC.? */
#ifdef CONFIG_DYNAMIC_FTRACE
unsigned long toc;
@@ -90,6 +96,10 @@ static inline int module_finalize_ftrace(struct module *mod, const Elf_Shdr *sec
}
#endif
+unsigned long stub_for_addr(const struct elf_info *elf_info, unsigned long addr,
+ const char *obj_name);
+int restore_r2(u32 *instruction, const char *obj_name);
+
struct exception_table_entry;
void sort_ex_table(struct exception_table_entry *start,
struct exception_table_entry *finish);
diff --git a/arch/powerpc/kernel/Makefile b/arch/powerpc/kernel/Makefile
index 2da380fcc34c..e99f626acc85 100644
--- a/arch/powerpc/kernel/Makefile
+++ b/arch/powerpc/kernel/Makefile
@@ -124,6 +124,10 @@ ifneq ($(CONFIG_PPC_INDIRECT_PIO),y)
obj-y += iomap.o
endif
+ifeq ($(CONFIG_MODULES)$(CONFIG_WORD_SIZE),y64)
+obj-y += elf_util_64.o
+endif
+
obj64-$(CONFIG_PPC_TRANSACTIONAL_MEM) += tm.o
obj-$(CONFIG_PPC64) += $(obj64-y)
diff --git a/arch/powerpc/kernel/elf_util_64.c b/arch/powerpc/kernel/elf_util_64.c
new file mode 100644
index 000000000000..decad2c34f38
--- /dev/null
+++ b/arch/powerpc/kernel/elf_util_64.c
@@ -0,0 +1,269 @@
+/*
+ * Utility functions to work with ELF files.
+ *
+ * Copyright (C) 2016, IBM Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <asm/ppc-opcode.h>
+#include <asm/elf_util.h>
+
+/*
+ * We just need to use the functions defined in <asm/module.h>, so just declare
+ * struct module here and avoid having to import <linux/module.h>.
+ */
+struct module;
+#include <asm/module.h>
+
+#ifdef PPC64_ELF_ABI_v2
+/* PowerPC64 specific values for the Elf64_Sym st_other field. */
+#define STO_PPC64_LOCAL_BIT 5
+#define STO_PPC64_LOCAL_MASK (7 << STO_PPC64_LOCAL_BIT)
+#define PPC64_LOCAL_ENTRY_OFFSET(other) \
+ (((1 << (((other) & STO_PPC64_LOCAL_MASK) >> STO_PPC64_LOCAL_BIT)) >> 2) << 2)
+
+static unsigned int local_entry_offset(const Elf64_Sym *sym)
+{
+ /* sym->st_other indicates offset to local entry point
+ * (otherwise it will assume r12 is the address of the start
+ * of function and try to derive r2 from it). */
+ return PPC64_LOCAL_ENTRY_OFFSET(sym->st_other);
+}
+#else
+static unsigned int local_entry_offset(const Elf64_Sym *sym)
+{
+ return 0;
+}
+#endif
+
+#ifdef CC_USING_MPROFILE_KERNEL
+/*
+ * In case of _mcount calls, do not save the current callee's TOC (in r2) into
+ * the original caller's stack frame. If we did we would clobber the saved TOC
+ * value of the original caller.
+ */
+static void squash_toc_save_inst(const char *name, unsigned long addr)
+{
+ struct ppc64_stub_entry *stub = (struct ppc64_stub_entry *)addr;
+
+ /* Only for calls to _mcount */
+ if (strcmp("_mcount", name) != 0)
+ return;
+
+ stub->jump[2] = PPC_INST_NOP;
+}
+#else
+static void squash_toc_save_inst(const char *name, unsigned long addr) { }
+#endif
+
+/**
+ * elf64_apply_relocate_add - apply 64 bit RELA relocations
+ * @elf_info: Support information for the ELF binary being relocated.
+ * @strtab: String table for the associated symbol table.
+ * @symindex: Section header index for the associated symbol table.
+ * @relsec: Section header index for the relocations to apply.
+ * @obj_name: The name of the ELF binary, for information messages.
+ */
+int elf64_apply_relocate_add(const struct elf_info *elf_info,
+ const char *strtab, unsigned int symindex,
+ unsigned int relsec, const char *obj_name)
+{
+ unsigned int i;
+ Elf64_Shdr *sechdrs = elf_info->sechdrs;
+ Elf64_Rela *rela = (void *)sechdrs[relsec].sh_addr;
+ Elf64_Sym *sym;
+ unsigned long *location;
+ unsigned long value;
+
+
+ for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rela); i++) {
+ /* This is where to make the change */
+ location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr
+ + rela[i].r_offset;
+ /* This is the symbol it is referring to */
+ sym = (Elf64_Sym *)sechdrs[symindex].sh_addr
+ + ELF64_R_SYM(rela[i].r_info);
+
+ pr_debug("RELOC at %p: %li-type as %s (0x%lx) + %li\n",
+ location, (long)ELF64_R_TYPE(rela[i].r_info),
+ strtab + sym->st_name, (unsigned long)sym->st_value,
+ (long)rela[i].r_addend);
+
+ /* `Everything is relative'. */
+ value = sym->st_value + rela[i].r_addend;
+
+ switch (ELF64_R_TYPE(rela[i].r_info)) {
+ case R_PPC64_ADDR32:
+ /* Simply set it */
+ *(u32 *)location = value;
+ break;
+
+ case R_PPC64_ADDR64:
+ /* Simply set it */
+ *(unsigned long *)location = value;
+ break;
+
+ case R_PPC64_TOC:
+ *(unsigned long *)location = my_r2(elf_info);
+ break;
+
+ case R_PPC64_TOC16:
+ /* Subtract TOC pointer */
+ value -= my_r2(elf_info);
+ if (value + 0x8000 > 0xffff) {
+ pr_err("%s: bad TOC16 relocation (0x%lx)\n",
+ obj_name, value);
+ return -ENOEXEC;
+ }
+ *((uint16_t *) location)
+ = (*((uint16_t *) location) & ~0xffff)
+ | (value & 0xffff);
+ break;
+
+ case R_PPC64_TOC16_LO:
+ /* Subtract TOC pointer */
+ value -= my_r2(elf_info);
+ *((uint16_t *) location)
+ = (*((uint16_t *) location) & ~0xffff)
+ | (value & 0xffff);
+ break;
+
+ case R_PPC64_TOC16_DS:
+ /* Subtract TOC pointer */
+ value -= my_r2(elf_info);
+ if ((value & 3) != 0 || value + 0x8000 > 0xffff) {
+ pr_err("%s: bad TOC16_DS relocation (0x%lx)\n",
+ obj_name, value);
+ return -ENOEXEC;
+ }
+ *((uint16_t *) location)
+ = (*((uint16_t *) location) & ~0xfffc)
+ | (value & 0xfffc);
+ break;
+
+ case R_PPC64_TOC16_LO_DS:
+ /* Subtract TOC pointer */
+ value -= my_r2(elf_info);
+ if ((value & 3) != 0) {
+ pr_err("%s: bad TOC16_LO_DS relocation (0x%lx)\n",
+ obj_name, value);
+ return -ENOEXEC;
+ }
+ *((uint16_t *) location)
+ = (*((uint16_t *) location) & ~0xfffc)
+ | (value & 0xfffc);
+ break;
+
+ case R_PPC64_TOC16_HA:
+ /* Subtract TOC pointer */
+ value -= my_r2(elf_info);
+ value = ((value + 0x8000) >> 16);
+ *((uint16_t *) location)
+ = (*((uint16_t *) location) & ~0xffff)
+ | (value & 0xffff);
+ break;
+
+ case R_PPC_REL24:
+ /* FIXME: Handle weak symbols here --RR */
+ if (sym->st_shndx == SHN_UNDEF) {
+ /* External: go via stub */
+ value = stub_for_addr(elf_info, value, obj_name);
+ if (!value)
+ return -ENOENT;
+ if (!restore_r2((u32 *)location + 1, obj_name))
+ return -ENOEXEC;
+
+ squash_toc_save_inst(strtab + sym->st_name, value);
+ } else
+ value += local_entry_offset(sym);
+
+ /* Convert value to relative */
+ value -= (unsigned long)location;
+ if (value + 0x2000000 > 0x3ffffff || (value & 3) != 0){
+ pr_err("%s: REL24 %li out of range!\n",
+ obj_name, (long int)value);
+ return -ENOEXEC;
+ }
+
+ /* Only replace bits 2 through 26 */
+ *(uint32_t *)location
+ = (*(uint32_t *)location & ~0x03fffffc)
+ | (value & 0x03fffffc);
+ break;
+
+ case R_PPC64_REL64:
+ /* 64 bits relative (used by features fixups) */
+ *location = value - (unsigned long)location;
+ break;
+
+ case R_PPC64_TOCSAVE:
+ /*
+ * Marker reloc indicates we don't have to save r2.
+ * That would only save us one instruction, so ignore
+ * it.
+ */
+ break;
+
+ case R_PPC64_ENTRY:
+ /*
+ * Optimize ELFv2 large code model entry point if
+ * the TOC is within 2GB range of current location.
+ */
+ value = my_r2(elf_info) - (unsigned long)location;
+ if (value + 0x80008000 > 0xffffffff)
+ break;
+ /*
+ * Check for the large code model prolog sequence:
+ * ld r2, ...(r12)
+ * add r2, r2, r12
+ */
+ if ((((uint32_t *)location)[0] & ~0xfffc)
+ != 0xe84c0000)
+ break;
+ if (((uint32_t *)location)[1] != 0x7c426214)
+ break;
+ /*
+ * If found, replace it with:
+ * addis r2, r12, (.TOC.-func)@ha
+ * addi r2, r12, (.TOC.-func)@l
+ */
+ ((uint32_t *)location)[0] = 0x3c4c0000 + PPC_HA(value);
+ ((uint32_t *)location)[1] = 0x38420000 + PPC_LO(value);
+ break;
+
+ case R_PPC64_REL16_HA:
+ /* Subtract location pointer */
+ value -= (unsigned long)location;
+ value = ((value + 0x8000) >> 16);
+ *((uint16_t *) location)
+ = (*((uint16_t *) location) & ~0xffff)
+ | (value & 0xffff);
+ break;
+
+ case R_PPC64_REL16_LO:
+ /* Subtract location pointer */
+ value -= (unsigned long)location;
+ *((uint16_t *) location)
+ = (*((uint16_t *) location) & ~0xffff)
+ | (value & 0xffff);
+ break;
+
+ default:
+ pr_err("%s: Unknown ADD relocation: %lu\n",
+ obj_name,
+ (unsigned long)ELF64_R_TYPE(rela[i].r_info));
+ return -ENOEXEC;
+ }
+ }
+
+ return 0;
+}
diff --git a/arch/powerpc/kernel/module_64.c b/arch/powerpc/kernel/module_64.c
index f703f343358e..f2b25b4f9066 100644
--- a/arch/powerpc/kernel/module_64.c
+++ b/arch/powerpc/kernel/module_64.c
@@ -19,7 +19,7 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/module.h>
-#include <linux/elf.h>
+#include <asm/elf_util.h>
#include <linux/moduleloader.h>
#include <linux/err.h>
#include <linux/vmalloc.h>
@@ -43,9 +43,6 @@
#ifdef PPC64_ELF_ABI_v2
-/* An address is simply the address of the function. */
-typedef unsigned long func_desc_t;
-
static func_desc_t func_desc(unsigned long addr)
{
return addr;
@@ -58,25 +55,8 @@ static unsigned long stub_func_addr(func_desc_t func)
{
return func;
}
-
-/* PowerPC64 specific values for the Elf64_Sym st_other field. */
-#define STO_PPC64_LOCAL_BIT 5
-#define STO_PPC64_LOCAL_MASK (7 << STO_PPC64_LOCAL_BIT)
-#define PPC64_LOCAL_ENTRY_OFFSET(other) \
- (((1 << (((other) & STO_PPC64_LOCAL_MASK) >> STO_PPC64_LOCAL_BIT)) >> 2) << 2)
-
-static unsigned int local_entry_offset(const Elf64_Sym *sym)
-{
- /* sym->st_other indicates offset to local entry point
- * (otherwise it will assume r12 is the address of the start
- * of function and try to derive r2 from it). */
- return PPC64_LOCAL_ENTRY_OFFSET(sym->st_other);
-}
#else
-/* An address is address of the OPD entry, which contains address of fn. */
-typedef struct ppc64_opd_entry func_desc_t;
-
static func_desc_t func_desc(unsigned long addr)
{
return *(struct ppc64_opd_entry *)addr;
@@ -89,29 +69,10 @@ static unsigned long stub_func_addr(func_desc_t func)
{
return func.funcaddr;
}
-static unsigned int local_entry_offset(const Elf64_Sym *sym)
-{
- return 0;
-}
#endif
#define STUB_MAGIC 0x73747562 /* stub */
-/* Like PPC32, we need little trampolines to do > 24-bit jumps (into
- the kernel itself). But on PPC64, these need to be used for every
- jump, actually, to reset r2 (TOC+0x8000). */
-struct ppc64_stub_entry
-{
- /* 28 byte jump instruction sequence (7 instructions). We only
- * need 6 instructions on ABIv2 but we always allocate 7 so
- * so we don't have to modify the trampoline load instruction. */
- u32 jump[7];
- /* Used by ftrace to identify stubs */
- u32 magic;
- /* Data for the above code */
- func_desc_t funcdata;
-};
-
/*
* PPC64 uses 24 bit jumps, but we need to jump into other modules or
* the kernel which may be further. So we jump to a stub.
@@ -346,9 +307,9 @@ int module_frob_arch_sections(Elf64_Ehdr *hdr,
for (i = 1; i < hdr->e_shnum; i++) {
char *p;
if (strcmp(secstrings + sechdrs[i].sh_name, ".stubs") == 0)
- me->arch.stubs_section = i;
+ me->arch.elf_info.stubs_section = i;
else if (strcmp(secstrings + sechdrs[i].sh_name, ".toc") == 0)
- me->arch.toc_section = i;
+ me->arch.elf_info.toc_section = i;
else if (strcmp(secstrings+sechdrs[i].sh_name,"__versions")==0)
dedotify_versions((void *)hdr + sechdrs[i].sh_offset,
sechdrs[i].sh_size);
@@ -364,7 +325,7 @@ int module_frob_arch_sections(Elf64_Ehdr *hdr,
+ sechdrs[sechdrs[i].sh_link].sh_offset);
}
- if (!me->arch.stubs_section) {
+ if (!me->arch.elf_info.stubs_section) {
pr_err("%s: doesn't contain .stubs.\n", me->name);
return -ENOEXEC;
}
@@ -373,44 +334,32 @@ int module_frob_arch_sections(Elf64_Ehdr *hdr,
to some reasonable value in case the module calls out to
other functions via a stub, or if a function pointer escapes
the module by some means. */
- if (!me->arch.toc_section)
- me->arch.toc_section = me->arch.stubs_section;
+ if (!me->arch.elf_info.toc_section)
+ me->arch.elf_info.toc_section = me->arch.elf_info.stubs_section;
/* Override the stubs size */
- sechdrs[me->arch.stubs_section].sh_size = get_stubs_size(hdr, sechdrs);
- return 0;
-}
+ sechdrs[me->arch.elf_info.stubs_section].sh_size = get_stubs_size(hdr, sechdrs);
-/* r2 is the TOC pointer: it actually points 0x8000 into the TOC (this
- gives the value maximum span in an instruction which uses a signed
- offset) */
-static inline unsigned long my_r2(const Elf64_Shdr *sechdrs, struct module *me)
-{
- return sechdrs[me->arch.toc_section].sh_addr + 0x8000;
-}
+ /* For the elf_util functions. */
+ me->arch.elf_info.sechdrs = sechdrs;
-/* Both low and high 16 bits are added as SIGNED additions, so if low
- 16 bits has high bit set, high 16 bits must be adjusted. These
- macros do that (stolen from binutils). */
-#define PPC_LO(v) ((v) & 0xffff)
-#define PPC_HI(v) (((v) >> 16) & 0xffff)
-#define PPC_HA(v) PPC_HI ((v) + 0x8000)
+ return 0;
+}
/* Patch stub to reference function and correct r2 value. */
-static inline int create_stub(const Elf64_Shdr *sechdrs,
+static inline int create_stub(const struct elf_info *elf_info,
struct ppc64_stub_entry *entry,
- unsigned long addr,
- struct module *me)
+ unsigned long addr, const char *obj_name)
{
long reladdr;
memcpy(entry->jump, ppc64_stub_insns, sizeof(ppc64_stub_insns));
/* Stub uses address relative to r2. */
- reladdr = (unsigned long)entry - my_r2(sechdrs, me);
+ reladdr = (unsigned long)entry - my_r2(elf_info);
if (reladdr > 0x7FFFFFFF || reladdr < -(0x80000000L)) {
pr_err("%s: Address %p of stub out of range of %p.\n",
- me->name, (void *)reladdr, (void *)my_r2);
+ obj_name, (void *)reladdr, (void *)my_r2);
return 0;
}
pr_debug("Stub %p get data from reladdr %li\n", entry, reladdr);
@@ -425,17 +374,17 @@ static inline int create_stub(const Elf64_Shdr *sechdrs,
/* Create stub to jump to function described in this OPD/ptr: we need the
stub to set up the TOC ptr (r2) for the function. */
-static unsigned long stub_for_addr(const Elf64_Shdr *sechdrs,
- unsigned long addr,
- struct module *me)
+unsigned long stub_for_addr(const struct elf_info *elf_info, unsigned long addr,
+ const char *obj_name)
{
+ struct elf_shdr *stubs_sec = &elf_info->sechdrs[elf_info->stubs_section];
struct ppc64_stub_entry *stubs;
unsigned int i, num_stubs;
- num_stubs = sechdrs[me->arch.stubs_section].sh_size / sizeof(*stubs);
+ num_stubs = stubs_sec->sh_size / sizeof(*stubs);
/* Find this stub, or if that fails, the next avail. entry */
- stubs = (void *)sechdrs[me->arch.stubs_section].sh_addr;
+ stubs = (void *) stubs_sec->sh_addr;
for (i = 0; stub_func_addr(stubs[i].funcdata); i++) {
BUG_ON(i >= num_stubs);
@@ -443,7 +392,7 @@ static unsigned long stub_for_addr(const Elf64_Shdr *sechdrs,
return (unsigned long)&stubs[i];
}
- if (!create_stub(sechdrs, &stubs[i], addr, me))
+ if (!create_stub(elf_info, &stubs[i], addr, obj_name))
return 0;
return (unsigned long)&stubs[i];
@@ -465,24 +414,7 @@ static bool is_early_mcount_callsite(u32 *instruction)
return false;
}
-/*
- * In case of _mcount calls, do not save the current callee's TOC (in r2) into
- * the original caller's stack frame. If we did we would clobber the saved TOC
- * value of the original caller.
- */
-static void squash_toc_save_inst(const char *name, unsigned long addr)
-{
- struct ppc64_stub_entry *stub = (struct ppc64_stub_entry *)addr;
-
- /* Only for calls to _mcount */
- if (strcmp("_mcount", name) != 0)
- return;
-
- stub->jump[2] = PPC_INST_NOP;
-}
#else
-static void squash_toc_save_inst(const char *name, unsigned long addr) { }
-
/* without -mprofile-kernel, mcount calls are never early */
static bool is_early_mcount_callsite(u32 *instruction)
{
@@ -492,13 +424,13 @@ static bool is_early_mcount_callsite(u32 *instruction)
/* We expect a noop next: if it is, replace it with instruction to
restore r2. */
-static int restore_r2(u32 *instruction, struct module *me)
+int restore_r2(u32 *instruction, const char *obj_name)
{
if (*instruction != PPC_INST_NOP) {
if (is_early_mcount_callsite(instruction - 1))
return 1;
pr_err("%s: Expect noop after relocate, got %08x\n",
- me->name, *instruction);
+ obj_name, *instruction);
return 0;
}
/* ld r2,R2_STACK_OFFSET(r1) */
@@ -512,11 +444,7 @@ int apply_relocate_add(Elf64_Shdr *sechdrs,
unsigned int relsec,
struct module *me)
{
- unsigned int i;
- Elf64_Rela *rela = (void *)sechdrs[relsec].sh_addr;
Elf64_Sym *sym;
- unsigned long *location;
- unsigned long value;
pr_debug("Applying ADD relocate section %u to %u\n", relsec,
sechdrs[relsec].sh_info);
@@ -527,191 +455,12 @@ int apply_relocate_add(Elf64_Shdr *sechdrs,
/* It's theoretically possible that a module doesn't want a
* .TOC. so don't fail it just for that. */
if (sym)
- sym->st_value = my_r2(sechdrs, me);
+ sym->st_value = my_r2(&me->arch.elf_info);
me->arch.toc_fixed = true;
}
- for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rela); i++) {
- /* This is where to make the change */
- location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr
- + rela[i].r_offset;
- /* This is the symbol it is referring to */
- sym = (Elf64_Sym *)sechdrs[symindex].sh_addr
- + ELF64_R_SYM(rela[i].r_info);
-
- pr_debug("RELOC at %p: %li-type as %s (0x%lx) + %li\n",
- location, (long)ELF64_R_TYPE(rela[i].r_info),
- strtab + sym->st_name, (unsigned long)sym->st_value,
- (long)rela[i].r_addend);
-
- /* `Everything is relative'. */
- value = sym->st_value + rela[i].r_addend;
-
- switch (ELF64_R_TYPE(rela[i].r_info)) {
- case R_PPC64_ADDR32:
- /* Simply set it */
- *(u32 *)location = value;
- break;
-
- case R_PPC64_ADDR64:
- /* Simply set it */
- *(unsigned long *)location = value;
- break;
-
- case R_PPC64_TOC:
- *(unsigned long *)location = my_r2(sechdrs, me);
- break;
-
- case R_PPC64_TOC16:
- /* Subtract TOC pointer */
- value -= my_r2(sechdrs, me);
- if (value + 0x8000 > 0xffff) {
- pr_err("%s: bad TOC16 relocation (0x%lx)\n",
- me->name, value);
- return -ENOEXEC;
- }
- *((uint16_t *) location)
- = (*((uint16_t *) location) & ~0xffff)
- | (value & 0xffff);
- break;
-
- case R_PPC64_TOC16_LO:
- /* Subtract TOC pointer */
- value -= my_r2(sechdrs, me);
- *((uint16_t *) location)
- = (*((uint16_t *) location) & ~0xffff)
- | (value & 0xffff);
- break;
-
- case R_PPC64_TOC16_DS:
- /* Subtract TOC pointer */
- value -= my_r2(sechdrs, me);
- if ((value & 3) != 0 || value + 0x8000 > 0xffff) {
- pr_err("%s: bad TOC16_DS relocation (0x%lx)\n",
- me->name, value);
- return -ENOEXEC;
- }
- *((uint16_t *) location)
- = (*((uint16_t *) location) & ~0xfffc)
- | (value & 0xfffc);
- break;
-
- case R_PPC64_TOC16_LO_DS:
- /* Subtract TOC pointer */
- value -= my_r2(sechdrs, me);
- if ((value & 3) != 0) {
- pr_err("%s: bad TOC16_LO_DS relocation (0x%lx)\n",
- me->name, value);
- return -ENOEXEC;
- }
- *((uint16_t *) location)
- = (*((uint16_t *) location) & ~0xfffc)
- | (value & 0xfffc);
- break;
-
- case R_PPC64_TOC16_HA:
- /* Subtract TOC pointer */
- value -= my_r2(sechdrs, me);
- value = ((value + 0x8000) >> 16);
- *((uint16_t *) location)
- = (*((uint16_t *) location) & ~0xffff)
- | (value & 0xffff);
- break;
-
- case R_PPC_REL24:
- /* FIXME: Handle weak symbols here --RR */
- if (sym->st_shndx == SHN_UNDEF) {
- /* External: go via stub */
- value = stub_for_addr(sechdrs, value, me);
- if (!value)
- return -ENOENT;
- if (!restore_r2((u32 *)location + 1, me))
- return -ENOEXEC;
-
- squash_toc_save_inst(strtab + sym->st_name, value);
- } else
- value += local_entry_offset(sym);
-
- /* Convert value to relative */
- value -= (unsigned long)location;
- if (value + 0x2000000 > 0x3ffffff || (value & 3) != 0){
- pr_err("%s: REL24 %li out of range!\n",
- me->name, (long int)value);
- return -ENOEXEC;
- }
-
- /* Only replace bits 2 through 26 */
- *(uint32_t *)location
- = (*(uint32_t *)location & ~0x03fffffc)
- | (value & 0x03fffffc);
- break;
-
- case R_PPC64_REL64:
- /* 64 bits relative (used by features fixups) */
- *location = value - (unsigned long)location;
- break;
-
- case R_PPC64_TOCSAVE:
- /*
- * Marker reloc indicates we don't have to save r2.
- * That would only save us one instruction, so ignore
- * it.
- */
- break;
-
- case R_PPC64_ENTRY:
- /*
- * Optimize ELFv2 large code model entry point if
- * the TOC is within 2GB range of current location.
- */
- value = my_r2(sechdrs, me) - (unsigned long)location;
- if (value + 0x80008000 > 0xffffffff)
- break;
- /*
- * Check for the large code model prolog sequence:
- * ld r2, ...(r12)
- * add r2, r2, r12
- */
- if ((((uint32_t *)location)[0] & ~0xfffc)
- != 0xe84c0000)
- break;
- if (((uint32_t *)location)[1] != 0x7c426214)
- break;
- /*
- * If found, replace it with:
- * addis r2, r12, (.TOC.-func)@ha
- * addi r2, r12, (.TOC.-func)@l
- */
- ((uint32_t *)location)[0] = 0x3c4c0000 + PPC_HA(value);
- ((uint32_t *)location)[1] = 0x38420000 + PPC_LO(value);
- break;
-
- case R_PPC64_REL16_HA:
- /* Subtract location pointer */
- value -= (unsigned long)location;
- value = ((value + 0x8000) >> 16);
- *((uint16_t *) location)
- = (*((uint16_t *) location) & ~0xffff)
- | (value & 0xffff);
- break;
-
- case R_PPC64_REL16_LO:
- /* Subtract location pointer */
- value -= (unsigned long)location;
- *((uint16_t *) location)
- = (*((uint16_t *) location) & ~0xffff)
- | (value & 0xffff);
- break;
-
- default:
- pr_err("%s: Unknown ADD relocation: %lu\n",
- me->name,
- (unsigned long)ELF64_R_TYPE(rela[i].r_info));
- return -ENOEXEC;
- }
- }
-
- return 0;
+ return elf64_apply_relocate_add(&me->arch.elf_info, strtab, symindex,
+ relsec, me->name);
}
#ifdef CONFIG_DYNAMIC_FTRACE
@@ -745,10 +494,10 @@ static unsigned long create_ftrace_stub(const Elf64_Shdr *sechdrs, struct module
};
long reladdr;
- num_stubs = sechdrs[me->arch.stubs_section].sh_size / sizeof(*entry);
+ num_stubs = sechdrs[me->arch.elf_info.stubs_section].sh_size / sizeof(*entry);
/* Find the next available stub entry */
- entry = (void *)sechdrs[me->arch.stubs_section].sh_addr;
+ entry = (void *)sechdrs[me->arch.elf_info.stubs_section].sh_addr;
for (i = 0; i < num_stubs && stub_func_addr(entry->funcdata); i++, entry++);
if (i >= num_stubs) {
@@ -777,13 +526,14 @@ static unsigned long create_ftrace_stub(const Elf64_Shdr *sechdrs, struct module
#else
static unsigned long create_ftrace_stub(const Elf64_Shdr *sechdrs, struct module *me)
{
- return stub_for_addr(sechdrs, (unsigned long)ftrace_caller, me);
+ return stub_for_addr(&me->arch.elf_info, (unsigned long)ftrace_caller,
+ me->name);
}
#endif
int module_finalize_ftrace(struct module *mod, const Elf_Shdr *sechdrs)
{
- mod->arch.toc = my_r2(sechdrs, mod);
+ mod->arch.toc = my_r2(&mod->arch.elf_info);
mod->arch.tramp = create_ftrace_stub(sechdrs, mod);
if (!mod->arch.tramp)
--
1.9.1
[toc] | [prev] | [next] | [standalone]
| From | Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com> |
|---|---|
| Date | 2016-06-21 22:00 +0200 |
| Subject | [PATCH v3 5/9] powerpc: Generalize elf64_apply_relocate_add. |
| Message-ID | <rMA02-1Ki-11@gated-at.bofh.it> |
| In reply to | #1428116 |
When apply_relocate_add is called, modules are already loaded at their
final location in memory so Elf64_Shdr.sh_addr can be used for accessing
the section contents as well as the base address for relocations.
This is not the case for kexec's purgatory, because it will only be
copied to its final location right before being executed. Therefore,
it needs to be relocated while it is still in a temporary buffer. In
this case, Elf64_Shdr.sh_addr can't be used to access the sections'
contents.
This patch allows elf64_apply_relocate_add to be used when the ELF
binary is not yet at its final location by adding an addr_base argument
to specify the address at which the section will be loaded, and rela,
loc_base and syms_base to point to the sections' contents.
Signed-off-by: Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Michael Ellerman <mpe@ellerman.id.au>
Cc: Torsten Duwe <duwe@suse.de>
Cc: kexec@lists.infradead.org
Cc: linux-kernel@vger.kernel.org
---
arch/powerpc/include/asm/elf_util.h | 6 ++--
arch/powerpc/kernel/elf_util_64.c | 63 +++++++++++++++++++++++++------------
arch/powerpc/kernel/module_64.c | 17 ++++++++--
3 files changed, 61 insertions(+), 25 deletions(-)
diff --git a/arch/powerpc/include/asm/elf_util.h b/arch/powerpc/include/asm/elf_util.h
index 37372559fe62..a012ba03282d 100644
--- a/arch/powerpc/include/asm/elf_util.h
+++ b/arch/powerpc/include/asm/elf_util.h
@@ -64,7 +64,9 @@ static inline unsigned long my_r2(const struct elf_info *elf_info)
}
int elf64_apply_relocate_add(const struct elf_info *elf_info,
- const char *strtab, unsigned int symindex,
- unsigned int relsec, const char *obj_name);
+ const char *strtab, const Elf64_Rela *rela,
+ unsigned int num_rela, void *syms_base,
+ void *loc_base, Elf64_Addr addr_base,
+ const char *obj_name);
#endif /* _ASM_POWERPC_ELF_UTIL_H */
diff --git a/arch/powerpc/kernel/elf_util_64.c b/arch/powerpc/kernel/elf_util_64.c
index decad2c34f38..8e5d400ac9f2 100644
--- a/arch/powerpc/kernel/elf_util_64.c
+++ b/arch/powerpc/kernel/elf_util_64.c
@@ -69,33 +69,56 @@ static void squash_toc_save_inst(const char *name, unsigned long addr) { }
* elf64_apply_relocate_add - apply 64 bit RELA relocations
* @elf_info: Support information for the ELF binary being relocated.
* @strtab: String table for the associated symbol table.
- * @symindex: Section header index for the associated symbol table.
- * @relsec: Section header index for the relocations to apply.
+ * @rela: Contents of the section with the relocations to apply.
+ * @num_rela: Number of relocation entries in the section.
+ * @syms_base: Contents of the associated symbol table.
+ * @loc_base: Contents of the section to which relocations apply.
+ * @addr_base: The address where the section will be loaded in memory.
* @obj_name: The name of the ELF binary, for information messages.
+ *
+ * Applies RELA relocations to an ELF file already at its final location
+ * in memory (in which case loc_base == addr_base), or still in a temporary
+ * buffer.
*/
int elf64_apply_relocate_add(const struct elf_info *elf_info,
- const char *strtab, unsigned int symindex,
- unsigned int relsec, const char *obj_name)
+ const char *strtab, const Elf64_Rela *rela,
+ unsigned int num_rela, void *syms_base,
+ void *loc_base, Elf64_Addr addr_base,
+ const char *obj_name)
{
unsigned int i;
- Elf64_Shdr *sechdrs = elf_info->sechdrs;
- Elf64_Rela *rela = (void *)sechdrs[relsec].sh_addr;
- Elf64_Sym *sym;
unsigned long *location;
+ unsigned long address;
unsigned long value;
+ const char *name;
+ Elf64_Sym *sym;
+
+ for (i = 0; i < num_rela; i++) {
+ /*
+ * rels[i].r_offset contains the byte offset from the beginning
+ * of section to the storage unit affected.
+ *
+ * This is the location to update in the temporary buffer where
+ * the section is currently loaded. The section will finally
+ * be loaded to a different address later, pointed to by
+ * addr_base.
+ */
+ location = loc_base + rela[i].r_offset;
+
+ /* Final address of the location. */
+ address = addr_base + rela[i].r_offset;
+ /* This is the symbol the relocation is referring to. */
+ sym = (Elf64_Sym *) syms_base + ELF64_R_SYM(rela[i].r_info);
- for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rela); i++) {
- /* This is where to make the change */
- location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr
- + rela[i].r_offset;
- /* This is the symbol it is referring to */
- sym = (Elf64_Sym *)sechdrs[symindex].sh_addr
- + ELF64_R_SYM(rela[i].r_info);
+ if (sym->st_name)
+ name = strtab + sym->st_name;
+ else
+ name = "<unnamed symbol>";
pr_debug("RELOC at %p: %li-type as %s (0x%lx) + %li\n",
location, (long)ELF64_R_TYPE(rela[i].r_info),
- strtab + sym->st_name, (unsigned long)sym->st_value,
+ name, (unsigned long)sym->st_value,
(long)rela[i].r_addend);
/* `Everything is relative'. */
@@ -187,7 +210,7 @@ int elf64_apply_relocate_add(const struct elf_info *elf_info,
value += local_entry_offset(sym);
/* Convert value to relative */
- value -= (unsigned long)location;
+ value -= address;
if (value + 0x2000000 > 0x3ffffff || (value & 3) != 0){
pr_err("%s: REL24 %li out of range!\n",
obj_name, (long int)value);
@@ -202,7 +225,7 @@ int elf64_apply_relocate_add(const struct elf_info *elf_info,
case R_PPC64_REL64:
/* 64 bits relative (used by features fixups) */
- *location = value - (unsigned long)location;
+ *location = value - address;
break;
case R_PPC64_TOCSAVE:
@@ -218,7 +241,7 @@ int elf64_apply_relocate_add(const struct elf_info *elf_info,
* Optimize ELFv2 large code model entry point if
* the TOC is within 2GB range of current location.
*/
- value = my_r2(elf_info) - (unsigned long)location;
+ value = my_r2(elf_info) - address;
if (value + 0x80008000 > 0xffffffff)
break;
/*
@@ -242,7 +265,7 @@ int elf64_apply_relocate_add(const struct elf_info *elf_info,
case R_PPC64_REL16_HA:
/* Subtract location pointer */
- value -= (unsigned long)location;
+ value -= address;
value = ((value + 0x8000) >> 16);
*((uint16_t *) location)
= (*((uint16_t *) location) & ~0xffff)
@@ -251,7 +274,7 @@ int elf64_apply_relocate_add(const struct elf_info *elf_info,
case R_PPC64_REL16_LO:
/* Subtract location pointer */
- value -= (unsigned long)location;
+ value -= address;
*((uint16_t *) location)
= (*((uint16_t *) location) & ~0xffff)
| (value & 0xffff);
diff --git a/arch/powerpc/kernel/module_64.c b/arch/powerpc/kernel/module_64.c
index f2b25b4f9066..7f3b17bcac05 100644
--- a/arch/powerpc/kernel/module_64.c
+++ b/arch/powerpc/kernel/module_64.c
@@ -438,16 +438,26 @@ int restore_r2(u32 *instruction, const char *obj_name)
return 1;
}
+/*
+ * When this function is called, the module is already at its final location in
+ * memory, so Elf64_Shdr.sh_addr can be used for accessing the section
+ * contents as well as the base address for relocations.
+ */
int apply_relocate_add(Elf64_Shdr *sechdrs,
const char *strtab,
unsigned int symindex,
unsigned int relsec,
struct module *me)
{
+ Elf64_Shdr *rel_section = &sechdrs[relsec];
+ void *syms_base = (void *) sechdrs[symindex].sh_addr;
+ Elf64_Addr addr_base = sechdrs[rel_section->sh_info].sh_addr;
+ const Elf64_Rela *rela = (const Elf64_Rela *) rel_section->sh_addr;
+ unsigned int num_rela = rel_section->sh_size / sizeof(Elf64_Rela);
Elf64_Sym *sym;
pr_debug("Applying ADD relocate section %u to %u\n", relsec,
- sechdrs[relsec].sh_info);
+ rel_section->sh_info);
/* First time we're called, we can fix up .TOC. */
if (!me->arch.toc_fixed) {
@@ -459,8 +469,9 @@ int apply_relocate_add(Elf64_Shdr *sechdrs,
me->arch.toc_fixed = true;
}
- return elf64_apply_relocate_add(&me->arch.elf_info, strtab, symindex,
- relsec, me->name);
+ return elf64_apply_relocate_add(&me->arch.elf_info, strtab, rela,
+ num_rela, syms_base, (void *) addr_base,
+ addr_base, me->name);
}
#ifdef CONFIG_DYNAMIC_FTRACE
--
1.9.1
[toc] | [prev] | [next] | [standalone]
| From | Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com> |
|---|---|
| Date | 2016-06-21 22:00 +0200 |
| Subject | [PATCH v3 7/9] powerpc: Implement kexec_file_load. |
| Message-ID | <rMA02-1Ki-13@gated-at.bofh.it> |
| In reply to | #1428116 |
Adds the basic machinery needed by kexec_file_load.
Signed-off-by: Josh Sklar <sklar@linux.vnet.ibm.com>
Signed-off-by: Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com>
Cc: kexec@lists.infradead.org
Cc: linux-kernel@vger.kernel.org
---
arch/powerpc/Kconfig | 13 +++++++++
arch/powerpc/include/asm/systbl.h | 1 +
arch/powerpc/include/asm/unistd.h | 2 +-
arch/powerpc/include/uapi/asm/unistd.h | 1 +
arch/powerpc/kernel/machine_kexec_64.c | 50 ++++++++++++++++++++++++++++++++++
5 files changed, 66 insertions(+), 1 deletion(-)
diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
index 01f7464d9fea..3ed5770b89e4 100644
--- a/arch/powerpc/Kconfig
+++ b/arch/powerpc/Kconfig
@@ -457,6 +457,19 @@ config KEXEC
interface is strongly in flux, so no good recommendation can be
made.
+config KEXEC_FILE
+ bool "kexec file based system call"
+ select KEXEC_CORE
+ select BUILD_BIN2C
+ depends on PPC64
+ depends on CRYPTO=y
+ depends on CRYPTO_SHA256=y
+ help
+ This is a new version of the kexec system call. This call is
+ file based and takes in file descriptors as system call arguments
+ for kernel and initramfs as opposed to a list of segments as is the
+ case for the older kexec call.
+
config CRASH_DUMP
bool "Build a kdump crash kernel"
depends on PPC64 || 6xx || FSL_BOOKE || (44x && !SMP)
diff --git a/arch/powerpc/include/asm/systbl.h b/arch/powerpc/include/asm/systbl.h
index 2fc5d4db503c..4b369d83fe9c 100644
--- a/arch/powerpc/include/asm/systbl.h
+++ b/arch/powerpc/include/asm/systbl.h
@@ -386,3 +386,4 @@ SYSCALL(mlock2)
SYSCALL(copy_file_range)
COMPAT_SYS_SPU(preadv2)
COMPAT_SYS_SPU(pwritev2)
+SYSCALL(kexec_file_load)
diff --git a/arch/powerpc/include/asm/unistd.h b/arch/powerpc/include/asm/unistd.h
index cf12c580f6b2..a01e97d3f305 100644
--- a/arch/powerpc/include/asm/unistd.h
+++ b/arch/powerpc/include/asm/unistd.h
@@ -12,7 +12,7 @@
#include <uapi/asm/unistd.h>
-#define NR_syscalls 382
+#define NR_syscalls 383
#define __NR__exit __NR_exit
diff --git a/arch/powerpc/include/uapi/asm/unistd.h b/arch/powerpc/include/uapi/asm/unistd.h
index e9f5f41aa55a..2f26335a3c42 100644
--- a/arch/powerpc/include/uapi/asm/unistd.h
+++ b/arch/powerpc/include/uapi/asm/unistd.h
@@ -392,5 +392,6 @@
#define __NR_copy_file_range 379
#define __NR_preadv2 380
#define __NR_pwritev2 381
+#define __NR_kexec_file_load 382
#endif /* _UAPI_ASM_POWERPC_UNISTD_H_ */
diff --git a/arch/powerpc/kernel/machine_kexec_64.c b/arch/powerpc/kernel/machine_kexec_64.c
index 50bf55135ef8..b242f2293a6e 100644
--- a/arch/powerpc/kernel/machine_kexec_64.c
+++ b/arch/powerpc/kernel/machine_kexec_64.c
@@ -31,6 +31,10 @@
#include <asm/hw_breakpoint.h>
#include <asm/asm-prototypes.h>
+#ifdef CONFIG_KEXEC_FILE
+static struct kexec_file_ops *kexec_file_loaders[] = { };
+#endif
+
#ifdef CONFIG_PPC_BOOK3E
int default_machine_kexec_prepare(struct kimage *image)
{
@@ -427,3 +431,49 @@ static int __init export_htab_values(void)
}
late_initcall(export_htab_values);
#endif /* CONFIG_PPC_STD_MMU_64 */
+
+#ifdef CONFIG_KEXEC_FILE
+int arch_kexec_kernel_image_probe(struct kimage *image, void *buf,
+ unsigned long buf_len)
+{
+ int i, ret = -ENOEXEC;
+ struct kexec_file_ops *fops;
+
+ /* We don't support crash kernels yet. */
+ if (image->type == KEXEC_TYPE_CRASH)
+ return -ENOTSUPP;
+
+ for (i = 0; i < ARRAY_SIZE(kexec_file_loaders); i++) {
+ fops = kexec_file_loaders[i];
+ if (!fops || !fops->probe)
+ continue;
+
+ ret = fops->probe(buf, buf_len);
+ if (!ret) {
+ image->fops = fops;
+ return ret;
+ }
+ }
+
+ return ret;
+}
+
+void *arch_kexec_kernel_image_load(struct kimage *image)
+{
+ if (!image->fops || !image->fops->load)
+ return ERR_PTR(-ENOEXEC);
+
+ return image->fops->load(image, image->kernel_buf,
+ image->kernel_buf_len, image->initrd_buf,
+ image->initrd_buf_len, image->cmdline_buf,
+ image->cmdline_buf_len);
+}
+
+int arch_kimage_file_post_load_cleanup(struct kimage *image)
+{
+ if (!image->fops || !image->fops->cleanup)
+ return 0;
+
+ return image->fops->cleanup(image->image_loader_data);
+}
+#endif /* CONFIG_KEXEC_FILE */
--
1.9.1
[toc] | [prev] | [next] | [standalone]
| From | Balbir Singh <bsingharora@gmail.com> |
|---|---|
| Date | 2016-06-22 15:40 +0200 |
| Message-ID | <rMQxP-44t-7@gated-at.bofh.it> |
| In reply to | #1428116 |
On Tue, 21 Jun 2016 16:48:32 -0300 Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com> wrote: > Hello, > > This patch series implements the kexec_file_load system call on > PowerPC. > > This system call moves the reading of the kernel, initrd and the > device tree from the userspace kexec tool to the kernel. This is > needed if you want to do one or both of the following: > > 1. only allow loading of signed kernels. > 2. "measure" (i.e., record the hashes of) the kernel, initrd, kernel > command line and other boot inputs for the Integrity Measurement > Architecture subsystem. > > The above are the functions kexec already has built into > kexec_file_load. Yesterday I posted a set of patches which allows a > third feature: > > 3. have IMA pass-on its event log (where integrity measurements are > registered) accross kexec to the second kernel, so that the event > history is preserved. OK.. and this is safe? Do both the kernels need to be signed by the same certificate? Balbir Singh
[toc] | [prev] | [next] | [standalone]
| From | Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com> |
|---|---|
| Date | 2016-06-22 19:10 +0200 |
| Message-ID | <rMTP4-6im-49@gated-at.bofh.it> |
| In reply to | #1428796 |
Hello Balbir, Am Mittwoch, 22 Juni 2016, 23:29:46 schrieb Balbir Singh: > On Tue, 21 Jun 2016 16:48:32 -0300 > Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com> wrote: > > This patch series implements the kexec_file_load system call on > > PowerPC. > > > > This system call moves the reading of the kernel, initrd and the > > device tree from the userspace kexec tool to the kernel. This is > > needed if you want to do one or both of the following: > > > > 1. only allow loading of signed kernels. > > 2. "measure" (i.e., record the hashes of) the kernel, initrd, kernel > > > > command line and other boot inputs for the Integrity Measurement > > Architecture subsystem. > > > > The above are the functions kexec already has built into > > kexec_file_load. Yesterday I posted a set of patches which allows a > > third feature: > > > > 3. have IMA pass-on its event log (where integrity measurements are > > > > registered) accross kexec to the second kernel, so that the event > > history is preserved. > > OK.. and this is safe? Do both the kernels need to be signed by the > same certificate? They don't. The integrity of the event log (assuming that is what you mean by "this" in "this is safe") is guaranteed by the TPM device. Each event in the measurement list extends a PCR and records its PCR value. It is cryptographically guaranteed that if you replay the PCR extends recorded in the event log and in the end of the process they match the current PCR values in the TPM device, then that event log is correct. The kernel signature serves to ensure that you only run kernels from an authorized provider. It doesn't play a role in integrity assurance, which aims to verify that the machine is really running the code it says it is running. As I understand it, at least. It's a bit subtle and I could be missing something... []'s Thiago Jung Bauermann IBM Linux Technology Center
[toc] | [prev] | [next] | [standalone]
| From | Balbir Singh <bsingharora@gmail.com> |
|---|---|
| Date | 2016-06-23 02:00 +0200 |
| Message-ID | <rN0dP-1Pz-7@gated-at.bofh.it> |
| In reply to | #1429005 |
On 23/06/16 03:02, Thiago Jung Bauermann wrote: > Hello Balbir, > Hi Thiago >>> 3. have IMA pass-on its event log (where integrity measurements are >>> >>> registered) accross kexec to the second kernel, so that the event >>> history is preserved. >> >> OK.. and this is safe? Do both the kernels need to be signed by the >> same certificate? > > They don't. The integrity of the event log (assuming that is what you mean > by "this" in "this is safe") is guaranteed by the TPM device. Each event in > the measurement list extends a PCR and records its PCR value. It is > cryptographically guaranteed that if you replay the PCR extends recorded in > the event log and in the end of the process they match the current PCR > values in the TPM device, then that event log is correct. What I meant was how does the new kernel know that the old kernel did not cheat while passing on the values? I presume because we trust that kernel via a signature. and How do we know the new kernel is safe to load - I guess via a signature that the new kernel is signed with (assuming it is present in the key ring). Balbir Singh
[toc] | [prev] | [next] | [standalone]
| From | Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com> |
|---|---|
| Date | 2016-06-23 18:50 +0200 |
| Message-ID | <rNfZg-4gz-17@gated-at.bofh.it> |
| In reply to | #1429349 |
Am Donnerstag, 23 Juni 2016, 09:57:51 schrieb Balbir Singh: > On 23/06/16 03:02, Thiago Jung Bauermann wrote: > >>> 3. have IMA pass-on its event log (where integrity measurements are > >>> > >>> registered) accross kexec to the second kernel, so that the event > >>> history is preserved. > >> > >> OK.. and this is safe? Do both the kernels need to be signed by the > >> same certificate? > > > > They don't. The integrity of the event log (assuming that is what you > > mean by "this" in "this is safe") is guaranteed by the TPM device. Each > > event in the measurement list extends a PCR and records its PCR value. > > It is cryptographically guaranteed that if you replay the PCR extends > > recorded in the event log and in the end of the process they match the > > current PCR values in the TPM device, then that event log is correct. > > What I meant was how does the new kernel know that the old kernel did not > cheat while passing on the values? I presume because we trust that kernel > via a signature. Sorry, I still don't understand your concern. What kind of cheating? Which values? If it's the values in the event log, there's no need to trust the old kernel. The new kernel knows that the old kernel didn't pass wrong measurement values in the event log because it can recalculate the PCR extend operations recorded in the log and compare the results of the replay with the current PCR values stored in the TPM device. If they match, then the event log is guaranteed to be correct. If they don't match, either the memory was corrupted somehow during the kexec process, or the old kernel tried to pass a falsified event log. There's no known way to construct an alternative series of PCR extend operations that will result in the same final value in the PCR register of the TPM device. If you can do that, you discovered a hash collision attack on the SHA-1 or SHA-256 algorithms (depending on which algorithm is being used by IMA in the event log). Or a bug in the TPM device implementation. > and > > How do we know the new kernel is safe to load - I guess via a signature > that the new kernel is signed with (assuming it is present in the key > ring). Correct. That goal is met by signature verification, not by integrity assurance. I'll note that even with both of my patch series there's still code missing for kernel signature verification in PowerPC. I believe there's not a file format defined yet for how to store a signature in a PowerPC kernel image. Integrity assurance doesn't depend on kernel signature verification though. There's value in both my patch series even without kernel signature verification support. They're complementary features. []'s Thiago Jung Bauermann IBM Linux Technology Center
[toc] | [prev] | [standalone]
Back to top | Article view | linux.kernel
csiph-web