Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > linux.kernel > #1472712 > unrolled thread
| Started by | Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com> |
|---|---|
| First post | 2016-08-30 19:50 +0200 |
| Last post | 2016-08-30 20:00 +0200 |
| Articles | 9 — 1 participant |
Back to article view | Back to linux.kernel
[PATCH v7 00/13] kexec_file_load implementation for PowerPC Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com> - 2016-08-30 19:50 +0200
[PATCH v7 03/13] kexec_file: Factor out kexec_locate_mem_hole from kexec_add_buffer. Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com> - 2016-08-30 19:50 +0200
[PATCH v7 13/13] powerpc: Enable CONFIG_KEXEC_FILE in powerpc server defconfigs. Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com> - 2016-08-30 19:50 +0200
[PATCH v7 08/13] powerpc: Add functions to read ELF files of any endianness. Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com> - 2016-08-30 19:50 +0200
[PATCH v7 06/13] powerpc: Generalize elf64_apply_relocate_add. Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com> - 2016-08-30 19:50 +0200
[PATCH v7 05/13] powerpc: Factor out relocation code from module_64.c to elf_util_64.c. Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com> - 2016-08-30 19:50 +0200
[PATCH v7 10/13] powerpc: Add code to work with device trees in kexec_file_load. Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com> - 2016-08-30 19:50 +0200
[PATCH v7 01/13] kexec_file: Allow arch-specific memory walking for kexec_add_buffer Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com> - 2016-08-30 20:00 +0200
[PATCH v7 02/13] kexec_file: Change kexec_add_buffer to take kexec_buf as argument. Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com> - 2016-08-30 20:00 +0200
| From | Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com> |
|---|---|
| Date | 2016-08-30 19:50 +0200 |
| Subject | [PATCH v7 00/13] kexec_file_load implementation for PowerPC |
| Message-ID | <sbVkC-7WI-3@gated-at.bofh.it> |
The purpose of this new version of the series is to allow building with
CONFIG_KEXEC=n and CONFIG_KEXEC_FILE=y. This is done by patch 4, which
is new in v7. The other patches have very little changes, just to fix
checkpatch warnings, as noted in the changelog.
Note that at this moment the powerpc tree doesn't build with
CONFIG_KEXEC=n even without this series applied. I posted a separate
patch fixing that issue:
https://lists.ozlabs.org/pipermail/linuxppc-dev/2016-August/147909.html
This series doesn't depend on that patch, and they don't conflict in
any way.
Original cover letter:
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 v7:
- Rebased on top of v4.8-rc4.
- Patch "powerpc: Change places using CONFIG_KEXEC to use CONFIG_KEXEC_CORE
instead."
- New patch. Fixes build when CONFIG_KEXEC=n and CONFIG_KEXEC_FILE=y.
- Patch "powerpc: Adapt elf64_apply_relocate_add for kexec_file_load."
- Fixed checkpatch warning "else is not generally useful after a break
or return".
- Fixed checkpatch warnings about line length. (Andrew Morton)
- Patch "powerpc: Add code to work with device trees in kexec_file_load."
- Remove space before tabs in doc comment for setup_new_fdt. (Andrew Morton)
- Fixed checkpatch warnings about line length.
- Patch "powerpc: Add support for loading ELF kernels with kexec_file_load."
- Removed duplicate #include <linux/kexec.h>.
Changes for v6:
- Based directly on top of v4.8-rc1.
- Patch "powerpc: Adapt elf64_apply_relocate_add for kexec_file_load."
- Allow undefined symbols if they are relocations for the TOC in the
big endian ABI.
- Fixed build error in this patch by adding the ehdr member to elf_info
here instead of in the next patch.
- Initialize elf_info.ehdr in module_64.c:module_frob_arch_sections.
- Patch "powerpc: Add code to work with device trees in kexec_file_load."
- Changed find_debug_console to look for /chosen instead of receiving
its offset as an argument.
- setup_new_fdt: no need to find /chosen again after deleting the memory
reservation for initrd.
- Patch "powerpc: Add support for loading ELF kernels with kexec_file_load."
- Don't pass the offset to /chosen to find_debug_console.
- Patch "powerpc: Allow userspace to set device tree properties in kexec_file_load"
- Dropped patch.
- Patch "powerpc: Add purgatory for kexec_file_load implementation."
- Make boot/string.S use the DOTSYM macro so that it can be
used by the ppc64 big endian purgatory.
- Use -mcall-aixdesc to compile the purgatory on big endian ppc64.
Changes for v5:
- Rebased series on v4.8-rc1 + the extend kexec_file_load series.
- Patch "powerpc: Adapt elf64_apply_relocate_add for kexec_file_load."
- New patch. These changes were previously in patch 10.
The code itself is unchanged from v4.
- Patch "powerpc: Implement kexec_file_load."
- Moved arch_kexec_walk_mem, arch_kexec_apply_relocations_add and
setup_purgatory from patch 10 to this patch.
- arch_kexec_apply_relocations_add is unchanged from v4.
- Fixed off-by-one error in arch_kexec_walk_mem when passing range
to func.
- Moved setup_purgatory from kexec_elf_64.c to machine_kexec_64.c,
and changed it to receive a pointer to the slave code directly
rather than a struct elf_info and getting the pointer from there.
- Patch "powerpc: Add code to work with device trees in kexec_file_load."
- New patch. These changes were previously in patch 10.
- find_debug_console moved from kexec_elf_64.c to machine_kexec_64.c.
The code is unchanged from v4.
- setup_new_fdt is a new function factored out of elf64_load. The only
code change from v4 is to create /chosen if it doesn't exist yet.
- Patch "powerpc: Add support for loading ELF kernels with kexec_file_load."
- This patch was too big, so moved some of its changes to other patches
to facilitate review.
- Allow loading ELF file type ET_DYN, which is what the BE kernel uses.
- The code adapting the device tree for booting the new kernel was moved
out of elf64_load to setup_new_fdt.
- Patch "powerpc: Allow userspace to set device tree properties in kexec_file_load"
- New patch.
- The code in this patch didn't exist in v4.
- This is the only patch that depends on the extend kexec_file_load series.
- Patch "powerpc: Enable CONFIG_KEXEC_FILE in powerpc server defconfigs."
- New patch.
Changes for v4:
- Rebased series on today's powerpc/next.
- Patch "kexec_file: Remove unused members from struct kexec_buf.":
- Dropped from the series.
- Patch "kexec_file: Allow arch-specific memory walking function for
kexec_add_buffer":
- Changed subject line to be more specific. Was: "kexec_file: Generalize
kexec_add_buffer.".
- Changed description to refer to x86 arch instead of Intel arch.
- Moved documentation comments for struct kexec_buf to this patch.
- Patch "kexec_file: Change kexec_add_buffer to take kexec_buf as argument.":
- New patch.
- Patch "kexec_file: Factor out kexec_locate_mem_hole from kexec_add_buffer.":
- Changed kexec_locate_mem_hole to take kexec_buf as argument.
- Improved description of kexec_locate_mem_hole in documentation comment.
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 (13):
kexec_file: Allow arch-specific memory walking for kexec_add_buffer
kexec_file: Change kexec_add_buffer to take kexec_buf as argument.
kexec_file: Factor out kexec_locate_mem_hole from kexec_add_buffer.
powerpc: Change places using CONFIG_KEXEC to use CONFIG_KEXEC_CORE
instead.
powerpc: Factor out relocation code from module_64.c to elf_util_64.c.
powerpc: Generalize elf64_apply_relocate_add.
powerpc: Adapt elf64_apply_relocate_add for kexec_file_load.
powerpc: Add functions to read ELF files of any endianness.
powerpc: Implement kexec_file_load.
powerpc: Add code to work with device trees in kexec_file_load.
powerpc: Add support for loading ELF kernels with kexec_file_load.
powerpc: Add purgatory for kexec_file_load implementation.
powerpc: Enable CONFIG_KEXEC_FILE in powerpc server defconfigs.
arch/powerpc/Kconfig | 15 +-
arch/powerpc/Makefile | 4 +
arch/powerpc/boot/string.S | 67 ++--
arch/powerpc/configs/powernv_defconfig | 2 +
arch/powerpc/configs/ppc64_defconfig | 2 +
arch/powerpc/configs/pseries_defconfig | 2 +
arch/powerpc/include/asm/debug.h | 2 +-
arch/powerpc/include/asm/elf_util.h | 92 +++++
arch/powerpc/include/asm/kexec.h | 16 +-
arch/powerpc/include/asm/kexec_elf_64.h | 10 +
arch/powerpc/include/asm/machdep.h | 4 +-
arch/powerpc/include/asm/module.h | 14 +-
arch/powerpc/include/asm/smp.h | 2 +-
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 | 11 +-
arch/powerpc/kernel/elf_util.c | 476 ++++++++++++++++++++++++++
arch/powerpc/kernel/elf_util_64.c | 376 ++++++++++++++++++++
arch/powerpc/kernel/head_64.S | 2 +-
arch/powerpc/kernel/kexec_elf_64.c | 282 +++++++++++++++
arch/powerpc/kernel/machine_kexec_64.c | 476 ++++++++++++++++++++++++++
arch/powerpc/kernel/misc_32.S | 2 +-
arch/powerpc/kernel/misc_64.S | 6 +-
arch/powerpc/kernel/module_64.c | 329 +++---------------
arch/powerpc/kernel/prom.c | 2 +-
arch/powerpc/kernel/setup_64.c | 4 +-
arch/powerpc/kernel/smp.c | 6 +-
arch/powerpc/kernel/traps.c | 2 +-
arch/powerpc/platforms/85xx/corenet_generic.c | 2 +-
arch/powerpc/platforms/85xx/smp.c | 8 +-
arch/powerpc/platforms/cell/spu_base.c | 2 +-
arch/powerpc/platforms/powernv/setup.c | 6 +-
arch/powerpc/platforms/ps3/setup.c | 4 +-
arch/powerpc/platforms/pseries/Makefile | 2 +-
arch/powerpc/platforms/pseries/setup.c | 4 +-
arch/powerpc/purgatory/.gitignore | 2 +
arch/powerpc/purgatory/Makefile | 46 +++
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 | 2 +
arch/powerpc/purgatory/v2wrap.S | 134 ++++++++
arch/x86/kernel/crash.c | 37 +-
arch/x86/kernel/kexec-bzimage64.c | 48 +--
include/linux/kexec.h | 36 +-
kernel/kexec_file.c | 141 ++++----
kernel/kexec_internal.h | 16 -
60 files changed, 2706 insertions(+), 472 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-08-30 19:50 +0200 |
| Subject | [PATCH v7 03/13] kexec_file: Factor out kexec_locate_mem_hole from kexec_add_buffer. |
| Message-ID | <sbVkC-7WI-23@gated-at.bofh.it> |
| In reply to | #1472712 |
kexec_locate_mem_hole will be used by the PowerPC kexec_file_load
implementation to find free memory for the purgatory stack.
Signed-off-by: Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com>
Acked-by: Dave Young <dyoung@redhat.com>
---
include/linux/kexec.h | 1 +
kernel/kexec_file.c | 25 ++++++++++++++++++++-----
2 files changed, 21 insertions(+), 5 deletions(-)
diff --git a/include/linux/kexec.h b/include/linux/kexec.h
index be39903edae1..d419d0e51fe5 100644
--- a/include/linux/kexec.h
+++ b/include/linux/kexec.h
@@ -176,6 +176,7 @@ struct kexec_buf {
int __weak arch_kexec_walk_mem(struct kexec_buf *kbuf,
int (*func)(u64, u64, void *));
extern int kexec_add_buffer(struct kexec_buf *kbuf);
+int kexec_locate_mem_hole(struct kexec_buf *kbuf);
#endif /* CONFIG_KEXEC_FILE */
struct kimage {
diff --git a/kernel/kexec_file.c b/kernel/kexec_file.c
index 25690d64e111..3401816700f3 100644
--- a/kernel/kexec_file.c
+++ b/kernel/kexec_file.c
@@ -450,6 +450,23 @@ int __weak arch_kexec_walk_mem(struct kexec_buf *kbuf,
}
/**
+ * kexec_locate_mem_hole - find free memory for the purgatory or the next kernel
+ * @kbuf: Parameters for the memory search.
+ *
+ * On success, kbuf->mem will have the start address of the memory region found.
+ *
+ * Return: 0 on success, negative errno on error.
+ */
+int kexec_locate_mem_hole(struct kexec_buf *kbuf)
+{
+ int ret;
+
+ ret = arch_kexec_walk_mem(kbuf, locate_mem_hole_callback);
+
+ return ret == 1 ? 0 : -EADDRNOTAVAIL;
+}
+
+/**
* kexec_add_buffer - place a buffer in a kexec segment
* @kbuf: Buffer contents and memory parameters.
*
@@ -489,11 +506,9 @@ int kexec_add_buffer(struct kexec_buf *kbuf)
kbuf->buf_align = max(kbuf->buf_align, PAGE_SIZE);
/* Walk the RAM ranges and allocate a suitable range for the buffer */
- 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;
- }
+ ret = kexec_locate_mem_hole(kbuf);
+ if (ret)
+ return ret;
/* Found a suitable memory range */
ksegment = &kbuf->image->segment[kbuf->image->nr_segments];
--
1.9.1
[toc] | [prev] | [next] | [standalone]
| From | Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com> |
|---|---|
| Date | 2016-08-30 19:50 +0200 |
| Subject | [PATCH v7 13/13] powerpc: Enable CONFIG_KEXEC_FILE in powerpc server defconfigs. |
| Message-ID | <sbVkC-7WI-27@gated-at.bofh.it> |
| In reply to | #1472712 |
Enable CONFIG_KEXEC_FILE in powernv_defconfig, ppc64_defconfig and pseries_defconfig. It depends on CONFIG_CRYPTO_SHA256=y, so add that as well. Signed-off-by: Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com> --- arch/powerpc/configs/powernv_defconfig | 2 ++ arch/powerpc/configs/ppc64_defconfig | 2 ++ arch/powerpc/configs/pseries_defconfig | 2 ++ 3 files changed, 6 insertions(+) diff --git a/arch/powerpc/configs/powernv_defconfig b/arch/powerpc/configs/powernv_defconfig index dce352e9153b..319e1fb7b0c9 100644 --- a/arch/powerpc/configs/powernv_defconfig +++ b/arch/powerpc/configs/powernv_defconfig @@ -47,6 +47,7 @@ CONFIG_BINFMT_MISC=m CONFIG_PPC_TRANSACTIONAL_MEM=y CONFIG_HOTPLUG_CPU=y CONFIG_KEXEC=y +CONFIG_KEXEC_FILE=y CONFIG_IRQ_ALL_CPUS=y CONFIG_NUMA=y CONFIG_MEMORY_HOTPLUG=y @@ -298,6 +299,7 @@ CONFIG_CRYPTO_CCM=m CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_HMAC=y CONFIG_CRYPTO_MICHAEL_MIC=m +CONFIG_CRYPTO_SHA256=y CONFIG_CRYPTO_TGR192=m CONFIG_CRYPTO_WP512=m CONFIG_CRYPTO_ANUBIS=m diff --git a/arch/powerpc/configs/ppc64_defconfig b/arch/powerpc/configs/ppc64_defconfig index 0a8d250cb97e..a0355ccc7f55 100644 --- a/arch/powerpc/configs/ppc64_defconfig +++ b/arch/powerpc/configs/ppc64_defconfig @@ -44,6 +44,7 @@ CONFIG_HZ_100=y CONFIG_BINFMT_MISC=m CONFIG_PPC_TRANSACTIONAL_MEM=y CONFIG_KEXEC=y +CONFIG_KEXEC_FILE=y CONFIG_CRASH_DUMP=y CONFIG_IRQ_ALL_CPUS=y CONFIG_MEMORY_HOTREMOVE=y @@ -333,6 +334,7 @@ CONFIG_CRYPTO_TEST=m CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_HMAC=y CONFIG_CRYPTO_MICHAEL_MIC=m +CONFIG_CRYPTO_SHA256=y CONFIG_CRYPTO_TGR192=m CONFIG_CRYPTO_WP512=m CONFIG_CRYPTO_ANUBIS=m diff --git a/arch/powerpc/configs/pseries_defconfig b/arch/powerpc/configs/pseries_defconfig index 654aeffc57ef..23af4a72930e 100644 --- a/arch/powerpc/configs/pseries_defconfig +++ b/arch/powerpc/configs/pseries_defconfig @@ -50,6 +50,7 @@ CONFIG_HZ_100=y CONFIG_BINFMT_MISC=m CONFIG_PPC_TRANSACTIONAL_MEM=y CONFIG_KEXEC=y +CONFIG_KEXEC_FILE=y CONFIG_IRQ_ALL_CPUS=y CONFIG_MEMORY_HOTPLUG=y CONFIG_MEMORY_HOTREMOVE=y @@ -300,6 +301,7 @@ CONFIG_CRYPTO_TEST=m CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_HMAC=y CONFIG_CRYPTO_MICHAEL_MIC=m +CONFIG_CRYPTO_SHA256=y CONFIG_CRYPTO_TGR192=m CONFIG_CRYPTO_WP512=m CONFIG_CRYPTO_ANUBIS=m -- 1.9.1
[toc] | [prev] | [next] | [standalone]
| From | Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com> |
|---|---|
| Date | 2016-08-30 19:50 +0200 |
| Subject | [PATCH v7 08/13] powerpc: Add functions to read ELF files of any endianness. |
| Message-ID | <sbVkD-7WI-55@gated-at.bofh.it> |
| In reply to | #1472712 |
A little endian kernel might need to kexec a big endian kernel (the
opposite is less likely but could happen as well), so we can't just cast
the buffer with the binary to ELF structs and use them as is done
elsewhere.
This patch adds functions which do byte-swapping as necessary when
populating the ELF structs. These functions will be used in the next
patch in the series.
Signed-off-by: Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com>
---
arch/powerpc/include/asm/elf_util.h | 18 ++
arch/powerpc/kernel/Makefile | 2 +-
arch/powerpc/kernel/elf_util.c | 476 ++++++++++++++++++++++++++++++++++++
3 files changed, 495 insertions(+), 1 deletion(-)
diff --git a/arch/powerpc/include/asm/elf_util.h b/arch/powerpc/include/asm/elf_util.h
index 5a27e8ceb88a..18703d56eabd 100644
--- a/arch/powerpc/include/asm/elf_util.h
+++ b/arch/powerpc/include/asm/elf_util.h
@@ -20,7 +20,14 @@
#include <linux/elf.h>
struct elf_info {
+ /*
+ * Where the ELF binary contents are kept.
+ * Memory managed by the user of the struct.
+ */
+ const char *buffer;
+
const struct elfhdr *ehdr;
+ const struct elf_phdr *proghdrs;
struct elf_shdr *sechdrs;
/* Index of stubs section. */
@@ -64,6 +71,17 @@ static inline unsigned long my_r2(const struct elf_info *elf_info)
return elf_info->sechdrs[elf_info->toc_section].sh_addr + 0x8000;
}
+static inline bool elf_is_elf_file(const struct elfhdr *ehdr)
+{
+ return memcmp(ehdr->e_ident, ELFMAG, SELFMAG) == 0;
+}
+
+int elf_read_from_buffer(const char *buf, size_t len, struct elfhdr *ehdr,
+ struct elf_info *elf_info);
+void elf_init_elf_info(const struct elfhdr *ehdr, struct elf_shdr *sechdrs,
+ struct elf_info *elf_info);
+void elf_free_info(struct elf_info *elf_info);
+
int elf64_apply_relocate_add(const struct elf_info *elf_info,
const char *strtab, const Elf64_Rela *rela,
unsigned int num_rela, void *syms_base,
diff --git a/arch/powerpc/kernel/Makefile b/arch/powerpc/kernel/Makefile
index fd0dd18a6605..fd550a65d450 100644
--- a/arch/powerpc/kernel/Makefile
+++ b/arch/powerpc/kernel/Makefile
@@ -124,7 +124,7 @@ obj-y += iomap.o
endif
ifeq ($(CONFIG_MODULES)$(CONFIG_WORD_SIZE),y64)
-obj-y += elf_util_64.o
+obj-y += elf_util.o elf_util_64.o
endif
obj64-$(CONFIG_PPC_TRANSACTIONAL_MEM) += tm.o
diff --git a/arch/powerpc/kernel/elf_util.c b/arch/powerpc/kernel/elf_util.c
new file mode 100644
index 000000000000..1df4a116ad90
--- /dev/null
+++ b/arch/powerpc/kernel/elf_util.c
@@ -0,0 +1,476 @@
+/*
+ * Utility functions to work with ELF files.
+ *
+ * Copyright (C) 2016, IBM Corporation
+ *
+ * Based on kexec-tools' kexec-elf.c. Heavily modified for the
+ * kernel by Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com>.
+ *
+ * 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.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/slab.h>
+#include <asm/elf_util.h>
+#include <asm-generic/module.h>
+
+#if ELF_CLASS == ELFCLASS32
+#define elf_addr_to_cpu elf32_to_cpu
+
+#ifndef Elf_Rel
+#define Elf_Rel Elf32_Rel
+#endif /* Elf_Rel */
+#else /* ELF_CLASS == ELFCLASS32 */
+#define elf_addr_to_cpu elf64_to_cpu
+
+#ifndef Elf_Rel
+#define Elf_Rel Elf64_Rel
+#endif /* Elf_Rel */
+
+static uint64_t elf64_to_cpu(const struct elfhdr *ehdr, uint64_t value)
+{
+ if (ehdr->e_ident[EI_DATA] == ELFDATA2LSB)
+ value = le64_to_cpu(value);
+ else if (ehdr->e_ident[EI_DATA] == ELFDATA2MSB)
+ value = be64_to_cpu(value);
+
+ return value;
+}
+#endif /* ELF_CLASS == ELFCLASS32 */
+
+static uint16_t elf16_to_cpu(const struct elfhdr *ehdr, uint16_t value)
+{
+ if (ehdr->e_ident[EI_DATA] == ELFDATA2LSB)
+ value = le16_to_cpu(value);
+ else if (ehdr->e_ident[EI_DATA] == ELFDATA2MSB)
+ value = be16_to_cpu(value);
+
+ return value;
+}
+
+static uint32_t elf32_to_cpu(const struct elfhdr *ehdr, uint32_t value)
+{
+ if (ehdr->e_ident[EI_DATA] == ELFDATA2LSB)
+ value = le32_to_cpu(value);
+ else if (ehdr->e_ident[EI_DATA] == ELFDATA2MSB)
+ value = be32_to_cpu(value);
+
+ return value;
+}
+
+/**
+ * elf_is_ehdr_sane - check that it is safe to use the ELF header
+ * @buf_len: size of the buffer in which the ELF file is loaded.
+ */
+static bool elf_is_ehdr_sane(const struct elfhdr *ehdr, size_t buf_len)
+{
+ if (ehdr->e_phnum > 0 && ehdr->e_phentsize != sizeof(struct elf_phdr)) {
+ pr_debug("Bad program header size.\n");
+ return false;
+ } else if (ehdr->e_shnum > 0 &&
+ ehdr->e_shentsize != sizeof(struct elf_shdr)) {
+ pr_debug("Bad section header size.\n");
+ return false;
+ } else if (ehdr->e_ident[EI_VERSION] != EV_CURRENT ||
+ ehdr->e_version != EV_CURRENT) {
+ pr_debug("Unknown ELF version.\n");
+ return false;
+ }
+
+ if (ehdr->e_phoff > 0 && ehdr->e_phnum > 0) {
+ size_t phdr_size;
+
+ /*
+ * e_phnum is at most 65535 so calculating the size of the
+ * program header cannot overflow.
+ */
+ phdr_size = sizeof(struct elf_phdr) * ehdr->e_phnum;
+
+ /* Sanity check the program header table location. */
+ if (ehdr->e_phoff + phdr_size < ehdr->e_phoff) {
+ pr_debug("Program headers at invalid location.\n");
+ return false;
+ } else if (ehdr->e_phoff + phdr_size > buf_len) {
+ pr_debug("Program headers truncated.\n");
+ return false;
+ }
+ }
+
+ if (ehdr->e_shoff > 0 && ehdr->e_shnum > 0) {
+ size_t shdr_size;
+
+ /*
+ * e_shnum is at most 65536 so calculating
+ * the size of the section header cannot overflow.
+ */
+ shdr_size = sizeof(struct elf_shdr) * ehdr->e_shnum;
+
+ /* Sanity check the section header table location. */
+ if (ehdr->e_shoff + shdr_size < ehdr->e_shoff) {
+ pr_debug("Section headers at invalid location.\n");
+ return false;
+ } else if (ehdr->e_shoff + shdr_size > buf_len) {
+ pr_debug("Section headers truncated.\n");
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static int elf_read_ehdr(const char *buf, size_t len, struct elfhdr *ehdr)
+{
+ struct elfhdr *buf_ehdr;
+
+ if (len < sizeof(*buf_ehdr)) {
+ pr_debug("Buffer is too small to hold ELF header.\n");
+ return -ENOEXEC;
+ }
+
+ memset(ehdr, 0, sizeof(*ehdr));
+ memcpy(ehdr->e_ident, buf, sizeof(ehdr->e_ident));
+ if (!elf_is_elf_file(ehdr)) {
+ pr_debug("No ELF header magic.\n");
+ return -ENOEXEC;
+ }
+
+ if (ehdr->e_ident[EI_CLASS] != ELF_CLASS) {
+ pr_debug("Not a supported ELF class.\n");
+ return -1;
+ } else if (ehdr->e_ident[EI_DATA] != ELFDATA2LSB &&
+ ehdr->e_ident[EI_DATA] != ELFDATA2MSB) {
+ pr_debug("Not a supported ELF data format.\n");
+ return -ENOEXEC;
+ }
+
+ buf_ehdr = (struct elfhdr *) buf;
+ if (elf16_to_cpu(ehdr, buf_ehdr->e_ehsize) != sizeof(*buf_ehdr)) {
+ pr_debug("Bad ELF header size.\n");
+ return -ENOEXEC;
+ }
+
+ ehdr->e_type = elf16_to_cpu(ehdr, buf_ehdr->e_type);
+ ehdr->e_machine = elf16_to_cpu(ehdr, buf_ehdr->e_machine);
+ ehdr->e_version = elf32_to_cpu(ehdr, buf_ehdr->e_version);
+ ehdr->e_entry = elf_addr_to_cpu(ehdr, buf_ehdr->e_entry);
+ ehdr->e_phoff = elf_addr_to_cpu(ehdr, buf_ehdr->e_phoff);
+ ehdr->e_shoff = elf_addr_to_cpu(ehdr, buf_ehdr->e_shoff);
+ ehdr->e_flags = elf32_to_cpu(ehdr, buf_ehdr->e_flags);
+ ehdr->e_phentsize = elf16_to_cpu(ehdr, buf_ehdr->e_phentsize);
+ ehdr->e_phnum = elf16_to_cpu(ehdr, buf_ehdr->e_phnum);
+ ehdr->e_shentsize = elf16_to_cpu(ehdr, buf_ehdr->e_shentsize);
+ ehdr->e_shnum = elf16_to_cpu(ehdr, buf_ehdr->e_shnum);
+ ehdr->e_shstrndx = elf16_to_cpu(ehdr, buf_ehdr->e_shstrndx);
+
+ return elf_is_ehdr_sane(ehdr, len) ? 0 : -ENOEXEC;
+}
+
+/**
+ * elf_is_phdr_sane - check that it is safe to use the program header
+ * @buf_len: size of the buffer in which the ELF file is loaded.
+ */
+static bool elf_is_phdr_sane(const struct elf_phdr *phdr, size_t buf_len)
+{
+
+ if (phdr->p_offset + phdr->p_filesz < phdr->p_offset) {
+ pr_debug("ELF segment location wraps around.\n");
+ return false;
+ } else if (phdr->p_offset + phdr->p_filesz > buf_len) {
+ pr_debug("ELF segment not in file.\n");
+ return false;
+ } else if (phdr->p_paddr + phdr->p_memsz < phdr->p_paddr) {
+ pr_debug("ELF segment address wraps around.\n");
+ return false;
+ }
+
+ return true;
+}
+
+static int elf_read_phdr(const char *buf, size_t len, struct elf_info *elf_info,
+ int idx)
+{
+ /* Override the const in proghdrs, we are the ones doing the loading. */
+ struct elf_phdr *phdr = (struct elf_phdr *) &elf_info->proghdrs[idx];
+ const char *pbuf;
+ struct elf_phdr *buf_phdr;
+
+ pbuf = buf + elf_info->ehdr->e_phoff + (idx * sizeof(*buf_phdr));
+ buf_phdr = (struct elf_phdr *) pbuf;
+
+ phdr->p_type = elf32_to_cpu(elf_info->ehdr, buf_phdr->p_type);
+ phdr->p_offset = elf_addr_to_cpu(elf_info->ehdr, buf_phdr->p_offset);
+ phdr->p_paddr = elf_addr_to_cpu(elf_info->ehdr, buf_phdr->p_paddr);
+ phdr->p_vaddr = elf_addr_to_cpu(elf_info->ehdr, buf_phdr->p_vaddr);
+ phdr->p_flags = elf32_to_cpu(elf_info->ehdr, buf_phdr->p_flags);
+
+ /*
+ * The following fields have a type equivalent to Elf_Addr
+ * both in 32 bit and 64 bit ELF.
+ */
+ phdr->p_filesz = elf_addr_to_cpu(elf_info->ehdr, buf_phdr->p_filesz);
+ phdr->p_memsz = elf_addr_to_cpu(elf_info->ehdr, buf_phdr->p_memsz);
+ phdr->p_align = elf_addr_to_cpu(elf_info->ehdr, buf_phdr->p_align);
+
+ return elf_is_phdr_sane(phdr, len) ? 0 : -ENOEXEC;
+}
+
+/**
+ * elf_read_phdrs - read the program headers from the buffer
+ *
+ * This function assumes that the program header table was checked for sanity.
+ * Use elf_is_ehdr_sane() if it wasn't.
+ */
+static int elf_read_phdrs(const char *buf, size_t len,
+ struct elf_info *elf_info)
+{
+ size_t phdr_size, i;
+ const struct elfhdr *ehdr = elf_info->ehdr;
+
+ /*
+ * e_phnum is at most 65535 so calculating the size of the
+ * program header cannot overflow.
+ */
+ phdr_size = sizeof(struct elf_phdr) * ehdr->e_phnum;
+
+ elf_info->proghdrs = kzalloc(phdr_size, GFP_KERNEL);
+ if (!elf_info->proghdrs)
+ return -ENOMEM;
+
+ for (i = 0; i < ehdr->e_phnum; i++) {
+ int ret;
+
+ ret = elf_read_phdr(buf, len, elf_info, i);
+ if (ret) {
+ kfree(elf_info->proghdrs);
+ elf_info->proghdrs = NULL;
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * elf_is_shdr_sane - check that it is safe to use the section header
+ * @buf_len: size of the buffer in which the ELF file is loaded.
+ */
+static bool elf_is_shdr_sane(const struct elf_shdr *shdr, size_t buf_len)
+{
+ bool size_ok;
+
+ /* SHT_NULL headers have undefined values, so we can't check them. */
+ if (shdr->sh_type == SHT_NULL)
+ return true;
+
+ /* Now verify sh_entsize */
+ switch (shdr->sh_type) {
+ case SHT_SYMTAB:
+ size_ok = shdr->sh_entsize == sizeof(Elf_Sym);
+ break;
+ case SHT_RELA:
+ size_ok = shdr->sh_entsize == sizeof(Elf_Rela);
+ break;
+ case SHT_DYNAMIC:
+ size_ok = shdr->sh_entsize == sizeof(Elf_Dyn);
+ break;
+ case SHT_REL:
+ size_ok = shdr->sh_entsize == sizeof(Elf_Rel);
+ break;
+ case SHT_NOTE:
+ case SHT_PROGBITS:
+ case SHT_HASH:
+ case SHT_NOBITS:
+ default:
+ /*
+ * This is a section whose entsize requirements
+ * I don't care about. If I don't know about
+ * the section I can't care about it's entsize
+ * requirements.
+ */
+ size_ok = true;
+ break;
+ }
+
+ if (!size_ok) {
+ pr_debug("ELF section with wrong entry size.\n");
+ return false;
+ } else if (shdr->sh_addr + shdr->sh_size < shdr->sh_addr) {
+ pr_debug("ELF section address wraps around.\n");
+ return false;
+ }
+
+ if (shdr->sh_type != SHT_NOBITS) {
+ if (shdr->sh_offset + shdr->sh_size < shdr->sh_offset) {
+ pr_debug("ELF section location wraps around.\n");
+ return false;
+ } else if (shdr->sh_offset + shdr->sh_size > buf_len) {
+ pr_debug("ELF section not in file.\n");
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static int elf_read_shdr(const char *buf, size_t len, struct elf_info *elf_info,
+ int idx)
+{
+ struct elf_shdr *shdr = &elf_info->sechdrs[idx];
+ const struct elfhdr *ehdr = elf_info->ehdr;
+ const char *sbuf;
+ struct elf_shdr *buf_shdr;
+
+ sbuf = buf + ehdr->e_shoff + idx * sizeof(*buf_shdr);
+ buf_shdr = (struct elf_shdr *) sbuf;
+
+ shdr->sh_name = elf32_to_cpu(ehdr, buf_shdr->sh_name);
+ shdr->sh_type = elf32_to_cpu(ehdr, buf_shdr->sh_type);
+ shdr->sh_addr = elf_addr_to_cpu(ehdr, buf_shdr->sh_addr);
+ shdr->sh_offset = elf_addr_to_cpu(ehdr, buf_shdr->sh_offset);
+ shdr->sh_link = elf32_to_cpu(ehdr, buf_shdr->sh_link);
+ shdr->sh_info = elf32_to_cpu(ehdr, buf_shdr->sh_info);
+
+ /*
+ * The following fields have a type equivalent to Elf_Addr
+ * both in 32 bit and 64 bit ELF.
+ */
+ shdr->sh_flags = elf_addr_to_cpu(ehdr, buf_shdr->sh_flags);
+ shdr->sh_size = elf_addr_to_cpu(ehdr, buf_shdr->sh_size);
+ shdr->sh_addralign = elf_addr_to_cpu(ehdr, buf_shdr->sh_addralign);
+ shdr->sh_entsize = elf_addr_to_cpu(ehdr, buf_shdr->sh_entsize);
+
+ return elf_is_shdr_sane(shdr, len) ? 0 : -ENOEXEC;
+}
+
+/**
+ * elf_read_shdrs - read the section headers from the buffer
+ *
+ * This function assumes that the section header table was checked for sanity.
+ * Use elf_is_ehdr_sane() if it wasn't.
+ */
+static int elf_read_shdrs(const char *buf, size_t len,
+ struct elf_info *elf_info)
+{
+ size_t shdr_size, i;
+
+ /*
+ * e_shnum is at most 65536 so calculating
+ * the size of the section header cannot overflow.
+ */
+ shdr_size = sizeof(struct elf_shdr) * elf_info->ehdr->e_shnum;
+
+ elf_info->sechdrs = kzalloc(shdr_size, GFP_KERNEL);
+ if (!elf_info->sechdrs)
+ return -ENOMEM;
+
+ for (i = 0; i < elf_info->ehdr->e_shnum; i++) {
+ int ret;
+
+ ret = elf_read_shdr(buf, len, elf_info, i);
+ if (ret) {
+ kfree(elf_info->sechdrs);
+ elf_info->sechdrs = NULL;
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * elf_read_from_buffer - read ELF file and sets up ELF header and ELF info
+ * @buf: Buffer to read ELF file from.
+ * @len: Size of @buf.
+ * @ehdr: Pointer to existing struct which will be populated.
+ * @elf_info: Pointer to existing struct which will be populated.
+ *
+ * This function allows reading ELF files with different byte order than
+ * the kernel, byte-swapping the fields as needed.
+ *
+ * Return:
+ * On success returns 0, and the caller should call elf_free_info(elf_info) to
+ * free the memory allocated for the section and program headers.
+ */
+int elf_read_from_buffer(const char *buf, size_t len, struct elfhdr *ehdr,
+ struct elf_info *elf_info)
+{
+ int ret;
+
+ ret = elf_read_ehdr(buf, len, ehdr);
+ if (ret)
+ return ret;
+
+ elf_info->buffer = buf;
+ elf_info->ehdr = ehdr;
+ if (ehdr->e_phoff > 0 && ehdr->e_phnum > 0) {
+ ret = elf_read_phdrs(buf, len, elf_info);
+ if (ret)
+ return ret;
+ }
+ if (ehdr->e_shoff > 0 && ehdr->e_shnum > 0) {
+ ret = elf_read_shdrs(buf, len, elf_info);
+ if (ret) {
+ kfree(elf_info->proghdrs);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * elf_init_elf_info - initialize elf_info from existing information
+ * @ehdr: Pointer to already loaded ELF header.
+ * @sechdrs: Pointer to already loaded section headers contents.
+ * @elf_info: Pointer to existing struct which will be populated.
+ *
+ * If the ELF binary was not loaded using elf_read_from_buffer, this function
+ * can be used to obtain a struct elf_info for use with other ELF utility
+ * functions.
+ *
+ * The caller is responsible for managing @sechdrs memory, users of this
+ * function don't need to call elf_free_info() if they free this memory
+ * separately.
+ */
+void elf_init_elf_info(const struct elfhdr *ehdr, struct elf_shdr *sechdrs,
+ struct elf_info *elf_info)
+{
+ int i;
+ const char *shstrtab;
+
+ memset(elf_info, 0, sizeof(*elf_info));
+
+ /* Section header string table. */
+ shstrtab = (const char *) sechdrs[ehdr->e_shstrndx].sh_offset;
+
+ for (i = 0; i < ehdr->e_shnum; i++) {
+ if (sechdrs[i].sh_size == 0)
+ continue;
+
+ if (!strcmp(&shstrtab[sechdrs[i].sh_name], ".stubs"))
+ elf_info->stubs_section = i;
+ else if (!strcmp(&shstrtab[sechdrs[i].sh_name], ".toc"))
+ elf_info->toc_section = i;
+ }
+
+ elf_info->ehdr = ehdr;
+ elf_info->sechdrs = sechdrs;
+}
+
+/**
+ * elf_free_info - free memory allocated by elf_read_from_buffer
+ */
+void elf_free_info(struct elf_info *elf_info)
+{
+ kfree(elf_info->proghdrs);
+ kfree(elf_info->sechdrs);
+ memset(elf_info, 0, sizeof(*elf_info));
+}
--
1.9.1
[toc] | [prev] | [next] | [standalone]
| From | Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com> |
|---|---|
| Date | 2016-08-30 19:50 +0200 |
| Subject | [PATCH v7 06/13] powerpc: Generalize elf64_apply_relocate_add. |
| Message-ID | <sbVkD-7WI-59@gated-at.bofh.it> |
| In reply to | #1472712 |
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>
---
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 b929560ecd5b..590e2413974f 100644
--- a/arch/powerpc/kernel/module_64.c
+++ b/arch/powerpc/kernel/module_64.c
@@ -439,16 +439,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) {
@@ -460,8 +470,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-08-30 19:50 +0200 |
| Subject | [PATCH v7 05/13] powerpc: Factor out relocation code from module_64.c to elf_util_64.c. |
| Message-ID | <sbVkD-7WI-45@gated-at.bofh.it> |
| In reply to | #1472712 |
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>
---
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 5eaf01455e41..fd0dd18a6605 100644
--- a/arch/powerpc/kernel/Makefile
+++ b/arch/powerpc/kernel/Makefile
@@ -123,6 +123,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 183368e008cf..b929560ecd5b 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,14 +424,14 @@ 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 (is_early_mcount_callsite(instruction - 1))
return 1;
if (*instruction != PPC_INST_NOP) {
pr_err("%s: Expect noop after relocate, got %08x\n",
- me->name, *instruction);
+ obj_name, *instruction);
return 0;
}
/* ld r2,R2_STACK_OFFSET(r1) */
@@ -513,11 +445,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);
@@ -528,191 +456,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
@@ -746,10 +495,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) {
@@ -778,13 +527,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-08-30 19:50 +0200 |
| Subject | [PATCH v7 10/13] powerpc: Add code to work with device trees in kexec_file_load. |
| Message-ID | <sbVkD-7WI-65@gated-at.bofh.it> |
| In reply to | #1472712 |
kexec_file_load needs to set up the device tree that will be used
by the next kernel and check whether it provides a console
that can be used by the purgatory.
[akpm@linux-foundation.org: coding-style fixes]
Signed-off-by: Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
---
arch/powerpc/include/asm/kexec.h | 3 +
arch/powerpc/kernel/machine_kexec_64.c | 221 +++++++++++++++++++++++++++++++++
2 files changed, 224 insertions(+)
diff --git a/arch/powerpc/include/asm/kexec.h b/arch/powerpc/include/asm/kexec.h
index 0c7e020d935a..73f88b5f9bd1 100644
--- a/arch/powerpc/include/asm/kexec.h
+++ b/arch/powerpc/include/asm/kexec.h
@@ -96,6 +96,9 @@ int setup_purgatory(struct kimage *image, const void *slave_code,
const void *fdt, unsigned long kernel_load_addr,
unsigned long fdt_load_addr, unsigned long stack_top,
int debug);
+int setup_new_fdt(void *fdt, unsigned long initrd_load_addr,
+ unsigned long initrd_len, const char *cmdline);
+bool find_debug_console(const void *fdt);
#endif /* CONFIG_KEXEC_FILE */
#else /* !CONFIG_KEXEC_CORE */
diff --git a/arch/powerpc/kernel/machine_kexec_64.c b/arch/powerpc/kernel/machine_kexec_64.c
index 1e678dc5096a..31c5090705e0 100644
--- a/arch/powerpc/kernel/machine_kexec_64.c
+++ b/arch/powerpc/kernel/machine_kexec_64.c
@@ -683,4 +683,225 @@ int setup_purgatory(struct kimage *image, const void *slave_code,
return 0;
}
+/*
+ * setup_new_fdt() - modify /chosen and memory reservation for the next kernel
+ * @fdt:
+ * @initrd_load_addr: Address where the next initrd will be loaded.
+ * @initrd_len: Size of the next initrd, or 0 if there will be none.
+ * @cmdline: Command line for the next kernel, or NULL if there will
+ * be none.
+ *
+ * Return: 0 on success, or negative errno on error.
+ */
+int setup_new_fdt(void *fdt, unsigned long initrd_load_addr,
+ unsigned long initrd_len, const char *cmdline)
+{
+ uint64_t oldfdt_addr;
+ int i, ret, chosen_node;
+ const void *prop;
+
+ /* Remove memory reservation for the current device tree. */
+ oldfdt_addr = __pa(initial_boot_params);
+ for (i = 0; i < fdt_num_mem_rsv(fdt); i++) {
+ uint64_t rsv_start, rsv_size;
+
+ ret = fdt_get_mem_rsv(fdt, i, &rsv_start, &rsv_size);
+ if (ret) {
+ pr_err("Malformed device tree.\n");
+ return -EINVAL;
+ }
+
+ if (rsv_start == oldfdt_addr &&
+ rsv_size == fdt_totalsize(initial_boot_params)) {
+ ret = fdt_del_mem_rsv(fdt, i);
+ if (ret) {
+ pr_err("Error deleting fdt reservation.\n");
+ return -EINVAL;
+ }
+
+ pr_debug("Removed old device tree reservation.\n");
+ break;
+ }
+ }
+
+ chosen_node = fdt_path_offset(fdt, "/chosen");
+ if (chosen_node == -FDT_ERR_NOTFOUND) {
+ chosen_node = fdt_add_subnode(fdt, fdt_path_offset(fdt, "/"),
+ "chosen");
+ if (chosen_node < 0) {
+ pr_err("Error creating /chosen.\n");
+ return -EINVAL;
+ }
+ } else if (chosen_node < 0) {
+ pr_err("Malformed device tree: error reading /chosen.\n");
+ return -EINVAL;
+ }
+
+ /* Did we boot using an initrd? */
+ prop = fdt_getprop(fdt, chosen_node, "linux,initrd-start", NULL);
+ if (prop) {
+ uint64_t tmp_start, tmp_end, tmp_size, tmp_sizepg;
+
+ tmp_start = fdt64_to_cpu(*((const fdt64_t *) prop));
+
+ prop = fdt_getprop(fdt, chosen_node, "linux,initrd-end", NULL);
+ if (!prop) {
+ pr_err("Malformed device tree.\n");
+ return -EINVAL;
+ }
+ tmp_end = fdt64_to_cpu(*((const fdt64_t *) prop));
+
+ /*
+ * kexec reserves exact initrd size, while firmware may
+ * reserve a multiple of PAGE_SIZE, so check for both.
+ */
+ tmp_size = tmp_end - tmp_start;
+ tmp_sizepg = round_up(tmp_size, PAGE_SIZE);
+
+ /* Remove memory reservation for the current initrd. */
+ for (i = 0; i < fdt_num_mem_rsv(fdt); i++) {
+ uint64_t rsv_start, rsv_size;
+
+ ret = fdt_get_mem_rsv(fdt, i, &rsv_start, &rsv_size);
+ if (ret) {
+ pr_err("Malformed device tree.\n");
+ return -EINVAL;
+ }
+
+ if (rsv_start == tmp_start &&
+ (rsv_size == tmp_size || rsv_size == tmp_sizepg)) {
+ ret = fdt_del_mem_rsv(fdt, i);
+ if (ret) {
+ pr_err("Error deleting fdt reservation.\n");
+ return -EINVAL;
+ }
+ pr_debug("Removed old initrd reservation.\n");
+
+ break;
+ }
+ }
+
+ /* If there's no new initrd, delete the old initrd's info. */
+ if (initrd_len == 0) {
+ ret = fdt_delprop(fdt, chosen_node,
+ "linux,initrd-start");
+ if (ret) {
+ pr_err("Error deleting linux,initrd-start.\n");
+ return -EINVAL;
+ }
+
+ ret = fdt_delprop(fdt, chosen_node, "linux,initrd-end");
+ if (ret) {
+ pr_err("Error deleting linux,initrd-end.\n");
+ return -EINVAL;
+ }
+ }
+ }
+
+ if (initrd_len) {
+ ret = fdt_setprop_u64(fdt, chosen_node,
+ "linux,initrd-start",
+ initrd_load_addr);
+ if (ret < 0) {
+ pr_err("Error setting up the new device tree.\n");
+ return -EINVAL;
+ }
+
+ /* initrd-end is the first address after the initrd image. */
+ ret = fdt_setprop_u64(fdt, chosen_node, "linux,initrd-end",
+ initrd_load_addr + initrd_len);
+ if (ret < 0) {
+ pr_err("Error setting up the new device tree.\n");
+ return -EINVAL;
+ }
+
+ ret = fdt_add_mem_rsv(fdt, initrd_load_addr, initrd_len);
+ if (ret) {
+ pr_err("Error reserving initrd memory: %s\n",
+ fdt_strerror(ret));
+ return -EINVAL;
+ }
+ }
+
+ if (cmdline != NULL) {
+ ret = fdt_setprop_string(fdt, chosen_node, "bootargs", cmdline);
+ if (ret < 0) {
+ pr_err("Error setting up the new device tree.\n");
+ return -EINVAL;
+ }
+ } else {
+ ret = fdt_delprop(fdt, chosen_node, "bootargs");
+ if (ret && ret != -FDT_ERR_NOTFOUND) {
+ pr_err("Error deleting bootargs.\n");
+ return -EINVAL;
+ }
+ }
+
+ ret = fdt_setprop(fdt, chosen_node, "linux,booted-from-kexec", NULL, 0);
+ if (ret) {
+ pr_err("Error setting up the new device tree.\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/**
+ * find_debug_console() - find out whether there is a console for the purgatory
+ * @fdt: Flattened device tree to search.
+ */
+bool find_debug_console(const void *fdt)
+{
+ int len;
+ int console_node, chosen_node;
+ const void *prop, *colon;
+
+ chosen_node = fdt_path_offset(fdt, "/chosen");
+ if (chosen_node < 0) {
+ pr_err("Malformed device tree: /chosen not found.\n");
+ return false;
+ }
+
+ prop = fdt_getprop(fdt, chosen_node, "stdout-path", &len);
+ if (prop == NULL) {
+ if (len == -FDT_ERR_NOTFOUND) {
+ prop = fdt_getprop(fdt, chosen_node,
+ "linux,stdout-path", &len);
+ if (prop == NULL) {
+ pr_debug("Unable to find [linux,]stdout-path.\n");
+ return false;
+ }
+ } else {
+ pr_debug("Error finding console: %s\n",
+ fdt_strerror(len));
+ return false;
+ }
+ }
+
+ /*
+ * stdout-path can have a ':' separating the path from device-specific
+ * information, so we should only consider what's before it.
+ */
+ colon = strchr(prop, ':');
+ if (colon != NULL)
+ len = colon - prop;
+ else
+ len -= 1; /* Ignore the terminating NUL. */
+
+ console_node = fdt_path_offset_namelen(fdt, prop, len);
+ if (console_node < 0) {
+ pr_debug("Error finding console: %s\n",
+ fdt_strerror(console_node));
+ return false;
+ }
+
+ if (fdt_node_check_compatible(fdt, console_node, "hvterm1") == 0)
+ return true;
+ else if (fdt_node_check_compatible(fdt, console_node,
+ "hvterm-protocol") == 0)
+ return true;
+
+ return false;
+}
+
#endif /* CONFIG_KEXEC_FILE */
--
1.9.1
[toc] | [prev] | [next] | [standalone]
| From | Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com> |
|---|---|
| Date | 2016-08-30 20:00 +0200 |
| Subject | [PATCH v7 01/13] kexec_file: Allow arch-specific memory walking for kexec_add_buffer |
| Message-ID | <sbVuh-80h-9@gated-at.bofh.it> |
| In reply to | #1472712 |
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>
Acked-by: Dave Young <dyoung@redhat.com>
Acked-by: Balbir Singh <bsingharora@gmail.com>
---
include/linux/kexec.h | 29 ++++++++++++++++++++++++++++-
kernel/kexec_file.c | 30 ++++++++++++++++++++++--------
kernel/kexec_internal.h | 16 ----------------
3 files changed, 50 insertions(+), 25 deletions(-)
diff --git a/include/linux/kexec.h b/include/linux/kexec.h
index d7437777baaa..2d6a1ab3b630 100644
--- a/include/linux/kexec.h
+++ b/include/linux/kexec.h
@@ -148,7 +148,34 @@ struct kexec_file_ops {
kexec_verify_sig_t *verify_sig;
#endif
};
-#endif
+
+/**
+ * struct kexec_buf - parameters for finding a place for a buffer in memory
+ * @image: kexec image in which memory to search.
+ * @buffer: Contents which will be copied to the allocated memory.
+ * @bufsz: Size of @buffer.
+ * @mem: On return will have address of the buffer in memory.
+ * @memsz: Size for the buffer in memory.
+ * @buf_align: Minimum alignment needed.
+ * @buf_min: The buffer can't be placed below this address.
+ * @buf_max: The buffer can't be placed above this address.
+ * @top_down: Allocate from top of memory.
+ */
+struct kexec_buf {
+ struct kimage *image;
+ char *buffer;
+ unsigned long bufsz;
+ unsigned long mem;
+ unsigned long memsz;
+ unsigned long buf_align;
+ unsigned long buf_min;
+ unsigned long buf_max;
+ bool top_down;
+};
+
+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 503bc2d348e5..4a9581acbba4 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.
@@ -474,14 +495,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 0a52315d9c62..4cef7e4706b0 100644
--- a/kernel/kexec_internal.h
+++ b/kernel/kexec_internal.h
@@ -20,22 +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;
- char *buffer;
- unsigned long bufsz;
- 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 | Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com> |
|---|---|
| Date | 2016-08-30 20:00 +0200 |
| Subject | [PATCH v7 02/13] kexec_file: Change kexec_add_buffer to take kexec_buf as argument. |
| Message-ID | <sbVuh-80h-13@gated-at.bofh.it> |
| In reply to | #1472712 |
This is done to simplify the kexec_add_buffer argument list.
Adapt all callers to set up a kexec_buf to pass to kexec_add_buffer.
In addition, change the type of kexec_buf.buffer from char * to void *.
There is no particular reason for it to be a char *, and the change
allows us to get rid of 3 existing casts to char * in the code.
Signed-off-by: Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com>
Acked-by: Dave Young <dyoung@redhat.com>
Acked-by: Balbir Singh <bsingharora@gmail.com>
---
arch/x86/kernel/crash.c | 37 ++++++++--------
arch/x86/kernel/kexec-bzimage64.c | 48 +++++++++++----------
include/linux/kexec.h | 8 +---
kernel/kexec_file.c | 88 ++++++++++++++++++---------------------
4 files changed, 87 insertions(+), 94 deletions(-)
diff --git a/arch/x86/kernel/crash.c b/arch/x86/kernel/crash.c
index 9616cf76940c..38a1cdf6aa05 100644
--- a/arch/x86/kernel/crash.c
+++ b/arch/x86/kernel/crash.c
@@ -615,9 +615,9 @@ static int determine_backup_region(u64 start, u64 end, void *arg)
int crash_load_segments(struct kimage *image)
{
- unsigned long src_start, src_sz, elf_sz;
- void *elf_addr;
int ret;
+ struct kexec_buf kbuf = { .image = image, .buf_min = 0,
+ .buf_max = ULONG_MAX, .top_down = false };
/*
* Determine and load a segment for backup area. First 640K RAM
@@ -631,43 +631,44 @@ int crash_load_segments(struct kimage *image)
if (ret < 0)
return ret;
- src_start = image->arch.backup_src_start;
- src_sz = image->arch.backup_src_sz;
-
/* Add backup segment. */
- if (src_sz) {
+ if (image->arch.backup_src_sz) {
+ kbuf.buffer = &crash_zero_bytes;
+ kbuf.bufsz = sizeof(crash_zero_bytes);
+ kbuf.memsz = image->arch.backup_src_sz;
+ kbuf.buf_align = PAGE_SIZE;
/*
* Ideally there is no source for backup segment. This is
* copied in purgatory after crash. Just add a zero filled
* segment for now to make sure checksum logic works fine.
*/
- ret = kexec_add_buffer(image, (char *)&crash_zero_bytes,
- sizeof(crash_zero_bytes), src_sz,
- PAGE_SIZE, 0, -1, 0,
- &image->arch.backup_load_addr);
+ ret = kexec_add_buffer(&kbuf);
if (ret)
return ret;
+ image->arch.backup_load_addr = kbuf.mem;
pr_debug("Loaded backup region at 0x%lx backup_start=0x%lx memsz=0x%lx\n",
- image->arch.backup_load_addr, src_start, src_sz);
+ image->arch.backup_load_addr,
+ image->arch.backup_src_start, kbuf.memsz);
}
/* Prepare elf headers and add a segment */
- ret = prepare_elf_headers(image, &elf_addr, &elf_sz);
+ ret = prepare_elf_headers(image, &kbuf.buffer, &kbuf.bufsz);
if (ret)
return ret;
- image->arch.elf_headers = elf_addr;
- image->arch.elf_headers_sz = elf_sz;
+ image->arch.elf_headers = kbuf.buffer;
+ image->arch.elf_headers_sz = kbuf.bufsz;
- ret = kexec_add_buffer(image, (char *)elf_addr, elf_sz, elf_sz,
- ELF_CORE_HEADER_ALIGN, 0, -1, 0,
- &image->arch.elf_load_addr);
+ kbuf.memsz = kbuf.bufsz;
+ kbuf.buf_align = ELF_CORE_HEADER_ALIGN;
+ ret = kexec_add_buffer(&kbuf);
if (ret) {
vfree((void *)image->arch.elf_headers);
return ret;
}
+ image->arch.elf_load_addr = kbuf.mem;
pr_debug("Loaded ELF headers at 0x%lx bufsz=0x%lx memsz=0x%lx\n",
- image->arch.elf_load_addr, elf_sz, elf_sz);
+ image->arch.elf_load_addr, kbuf.bufsz, kbuf.bufsz);
return ret;
}
diff --git a/arch/x86/kernel/kexec-bzimage64.c b/arch/x86/kernel/kexec-bzimage64.c
index f2356bda2b05..4b3a75329fb6 100644
--- a/arch/x86/kernel/kexec-bzimage64.c
+++ b/arch/x86/kernel/kexec-bzimage64.c
@@ -331,17 +331,17 @@ static void *bzImage64_load(struct kimage *image, char *kernel,
struct setup_header *header;
int setup_sects, kern16_size, ret = 0;
- unsigned long setup_header_size, params_cmdline_sz, params_misc_sz;
+ unsigned long setup_header_size, params_cmdline_sz;
struct boot_params *params;
unsigned long bootparam_load_addr, kernel_load_addr, initrd_load_addr;
unsigned long purgatory_load_addr;
- unsigned long kernel_bufsz, kernel_memsz, kernel_align;
- char *kernel_buf;
struct bzimage64_data *ldata;
struct kexec_entry64_regs regs64;
void *stack;
unsigned int setup_hdr_offset = offsetof(struct boot_params, hdr);
unsigned int efi_map_offset, efi_map_sz, efi_setup_data_offset;
+ struct kexec_buf kbuf = { .image = image, .buf_max = ULONG_MAX,
+ .top_down = true };
header = (struct setup_header *)(kernel + setup_hdr_offset);
setup_sects = header->setup_sects;
@@ -402,11 +402,11 @@ static void *bzImage64_load(struct kimage *image, char *kernel,
params_cmdline_sz = sizeof(struct boot_params) + cmdline_len +
MAX_ELFCOREHDR_STR_LEN;
params_cmdline_sz = ALIGN(params_cmdline_sz, 16);
- params_misc_sz = params_cmdline_sz + efi_map_sz +
+ kbuf.bufsz = params_cmdline_sz + efi_map_sz +
sizeof(struct setup_data) +
sizeof(struct efi_setup_data);
- params = kzalloc(params_misc_sz, GFP_KERNEL);
+ params = kzalloc(kbuf.bufsz, GFP_KERNEL);
if (!params)
return ERR_PTR(-ENOMEM);
efi_map_offset = params_cmdline_sz;
@@ -418,37 +418,41 @@ static void *bzImage64_load(struct kimage *image, char *kernel,
/* Is there a limit on setup header size? */
memcpy(¶ms->hdr, (kernel + setup_hdr_offset), setup_header_size);
- ret = kexec_add_buffer(image, (char *)params, params_misc_sz,
- params_misc_sz, 16, MIN_BOOTPARAM_ADDR,
- ULONG_MAX, 1, &bootparam_load_addr);
+ kbuf.buffer = params;
+ kbuf.memsz = kbuf.bufsz;
+ kbuf.buf_align = 16;
+ kbuf.buf_min = MIN_BOOTPARAM_ADDR;
+ ret = kexec_add_buffer(&kbuf);
if (ret)
goto out_free_params;
+ bootparam_load_addr = kbuf.mem;
pr_debug("Loaded boot_param, command line and misc at 0x%lx bufsz=0x%lx memsz=0x%lx\n",
- bootparam_load_addr, params_misc_sz, params_misc_sz);
+ bootparam_load_addr, kbuf.bufsz, kbuf.bufsz);
/* Load kernel */
- kernel_buf = kernel + kern16_size;
- kernel_bufsz = kernel_len - kern16_size;
- kernel_memsz = PAGE_ALIGN(header->init_size);
- kernel_align = header->kernel_alignment;
-
- ret = kexec_add_buffer(image, kernel_buf,
- kernel_bufsz, kernel_memsz, kernel_align,
- MIN_KERNEL_LOAD_ADDR, ULONG_MAX, 1,
- &kernel_load_addr);
+ kbuf.buffer = kernel + kern16_size;
+ kbuf.bufsz = kernel_len - kern16_size;
+ kbuf.memsz = PAGE_ALIGN(header->init_size);
+ kbuf.buf_align = header->kernel_alignment;
+ kbuf.buf_min = MIN_KERNEL_LOAD_ADDR;
+ ret = kexec_add_buffer(&kbuf);
if (ret)
goto out_free_params;
+ kernel_load_addr = kbuf.mem;
pr_debug("Loaded 64bit kernel at 0x%lx bufsz=0x%lx memsz=0x%lx\n",
- kernel_load_addr, kernel_memsz, kernel_memsz);
+ kernel_load_addr, kbuf.bufsz, kbuf.memsz);
/* Load initrd high */
if (initrd) {
- ret = kexec_add_buffer(image, initrd, initrd_len, initrd_len,
- PAGE_SIZE, MIN_INITRD_LOAD_ADDR,
- ULONG_MAX, 1, &initrd_load_addr);
+ kbuf.buffer = initrd;
+ kbuf.bufsz = kbuf.memsz = initrd_len;
+ kbuf.buf_align = PAGE_SIZE;
+ kbuf.buf_min = MIN_INITRD_LOAD_ADDR;
+ ret = kexec_add_buffer(&kbuf);
if (ret)
goto out_free_params;
+ initrd_load_addr = kbuf.mem;
pr_debug("Loaded initrd at 0x%lx bufsz=0x%lx memsz=0x%lx\n",
initrd_load_addr, initrd_len, initrd_len);
diff --git a/include/linux/kexec.h b/include/linux/kexec.h
index 2d6a1ab3b630..be39903edae1 100644
--- a/include/linux/kexec.h
+++ b/include/linux/kexec.h
@@ -163,7 +163,7 @@ struct kexec_file_ops {
*/
struct kexec_buf {
struct kimage *image;
- char *buffer;
+ void *buffer;
unsigned long bufsz;
unsigned long mem;
unsigned long memsz;
@@ -175,6 +175,7 @@ struct kexec_buf {
int __weak arch_kexec_walk_mem(struct kexec_buf *kbuf,
int (*func)(u64, u64, void *));
+extern int kexec_add_buffer(struct kexec_buf *kbuf);
#endif /* CONFIG_KEXEC_FILE */
struct kimage {
@@ -239,11 +240,6 @@ extern asmlinkage long sys_kexec_load(unsigned long entry,
struct kexec_segment __user *segments,
unsigned long flags);
extern int kernel_kexec(void);
-extern int kexec_add_buffer(struct kimage *image, char *buffer,
- unsigned long bufsz, unsigned long memsz,
- unsigned long buf_align, unsigned long buf_min,
- unsigned long buf_max, bool top_down,
- unsigned long *load_addr);
extern struct page *kimage_alloc_control_pages(struct kimage *image,
unsigned int order);
extern int kexec_load_purgatory(struct kimage *image, unsigned long min,
diff --git a/kernel/kexec_file.c b/kernel/kexec_file.c
index 4a9581acbba4..25690d64e111 100644
--- a/kernel/kexec_file.c
+++ b/kernel/kexec_file.c
@@ -449,25 +449,27 @@ int __weak arch_kexec_walk_mem(struct kexec_buf *kbuf,
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.
+/**
+ * kexec_add_buffer - place a buffer in a kexec segment
+ * @kbuf: Buffer contents and memory parameters.
+ *
+ * This function assumes that kexec_mutex is held.
+ * On successful return, @kbuf->mem will have the physical address of
+ * the buffer in memory.
+ *
+ * Return: 0 on success, negative errno on error.
*/
-int kexec_add_buffer(struct kimage *image, char *buffer, unsigned long bufsz,
- unsigned long memsz, unsigned long buf_align,
- unsigned long buf_min, unsigned long buf_max,
- bool top_down, unsigned long *load_addr)
+int kexec_add_buffer(struct kexec_buf *kbuf)
{
struct kexec_segment *ksegment;
- struct kexec_buf buf, *kbuf;
int ret;
/* Currently adding segment this way is allowed only in file mode */
- if (!image->file_mode)
+ if (!kbuf->image->file_mode)
return -EINVAL;
- if (image->nr_segments >= KEXEC_SEGMENT_MAX)
+ if (kbuf->image->nr_segments >= KEXEC_SEGMENT_MAX)
return -EINVAL;
/*
@@ -477,22 +479,14 @@ int kexec_add_buffer(struct kimage *image, char *buffer, unsigned long bufsz,
* logic goes through list of segments to make sure there are
* no destination overlaps.
*/
- if (!list_empty(&image->control_pages)) {
+ if (!list_empty(&kbuf->image->control_pages)) {
WARN_ON(1);
return -EINVAL;
}
- memset(&buf, 0, sizeof(struct kexec_buf));
- kbuf = &buf;
- kbuf->image = image;
- kbuf->buffer = buffer;
- kbuf->bufsz = bufsz;
-
- kbuf->memsz = ALIGN(memsz, PAGE_SIZE);
- kbuf->buf_align = max(buf_align, PAGE_SIZE);
- kbuf->buf_min = buf_min;
- kbuf->buf_max = buf_max;
- kbuf->top_down = top_down;
+ /* Ensure minimum alignment needed for segments. */
+ kbuf->memsz = ALIGN(kbuf->memsz, PAGE_SIZE);
+ kbuf->buf_align = max(kbuf->buf_align, PAGE_SIZE);
/* Walk the RAM ranges and allocate a suitable range for the buffer */
ret = arch_kexec_walk_mem(kbuf, locate_mem_hole_callback);
@@ -502,13 +496,12 @@ int kexec_add_buffer(struct kimage *image, char *buffer, unsigned long bufsz,
}
/* Found a suitable memory range */
- ksegment = &image->segment[image->nr_segments];
+ ksegment = &kbuf->image->segment[kbuf->image->nr_segments];
ksegment->kbuf = kbuf->buffer;
ksegment->bufsz = kbuf->bufsz;
ksegment->mem = kbuf->mem;
ksegment->memsz = kbuf->memsz;
- image->nr_segments++;
- *load_addr = ksegment->mem;
+ kbuf->image->nr_segments++;
return 0;
}
@@ -630,13 +623,15 @@ static int __kexec_load_purgatory(struct kimage *image, unsigned long min,
unsigned long max, int top_down)
{
struct purgatory_info *pi = &image->purgatory_info;
- unsigned long align, buf_align, bss_align, buf_sz, bss_sz, bss_pad;
- unsigned long memsz, entry, load_addr, curr_load_addr, bss_addr, offset;
+ unsigned long align, bss_align, bss_sz, bss_pad;
+ unsigned long entry, load_addr, curr_load_addr, bss_addr, offset;
unsigned char *buf_addr, *src;
int i, ret = 0, entry_sidx = -1;
const Elf_Shdr *sechdrs_c;
Elf_Shdr *sechdrs = NULL;
- void *purgatory_buf = NULL;
+ struct kexec_buf kbuf = { .image = image, .bufsz = 0, .buf_align = 1,
+ .buf_min = min, .buf_max = max,
+ .top_down = top_down };
/*
* sechdrs_c points to section headers in purgatory and are read
@@ -702,9 +697,7 @@ static int __kexec_load_purgatory(struct kimage *image, unsigned long min,
}
/* Determine how much memory is needed to load relocatable object. */
- buf_align = 1;
bss_align = 1;
- buf_sz = 0;
bss_sz = 0;
for (i = 0; i < pi->ehdr->e_shnum; i++) {
@@ -713,10 +706,10 @@ static int __kexec_load_purgatory(struct kimage *image, unsigned long min,
align = sechdrs[i].sh_addralign;
if (sechdrs[i].sh_type != SHT_NOBITS) {
- if (buf_align < align)
- buf_align = align;
- buf_sz = ALIGN(buf_sz, align);
- buf_sz += sechdrs[i].sh_size;
+ if (kbuf.buf_align < align)
+ kbuf.buf_align = align;
+ kbuf.bufsz = ALIGN(kbuf.bufsz, align);
+ kbuf.bufsz += sechdrs[i].sh_size;
} else {
/* bss section */
if (bss_align < align)
@@ -728,32 +721,31 @@ static int __kexec_load_purgatory(struct kimage *image, unsigned long min,
/* Determine the bss padding required to align bss properly */
bss_pad = 0;
- if (buf_sz & (bss_align - 1))
- bss_pad = bss_align - (buf_sz & (bss_align - 1));
+ if (kbuf.bufsz & (bss_align - 1))
+ bss_pad = bss_align - (kbuf.bufsz & (bss_align - 1));
- memsz = buf_sz + bss_pad + bss_sz;
+ kbuf.memsz = kbuf.bufsz + bss_pad + bss_sz;
/* Allocate buffer for purgatory */
- purgatory_buf = vzalloc(buf_sz);
- if (!purgatory_buf) {
+ kbuf.buffer = vzalloc(kbuf.bufsz);
+ if (!kbuf.buffer) {
ret = -ENOMEM;
goto out;
}
- if (buf_align < bss_align)
- buf_align = bss_align;
+ if (kbuf.buf_align < bss_align)
+ kbuf.buf_align = bss_align;
/* Add buffer to segment list */
- ret = kexec_add_buffer(image, purgatory_buf, buf_sz, memsz,
- buf_align, min, max, top_down,
- &pi->purgatory_load_addr);
+ ret = kexec_add_buffer(&kbuf);
if (ret)
goto out;
+ pi->purgatory_load_addr = kbuf.mem;
/* Load SHF_ALLOC sections */
- buf_addr = purgatory_buf;
+ buf_addr = kbuf.buffer;
load_addr = curr_load_addr = pi->purgatory_load_addr;
- bss_addr = load_addr + buf_sz + bss_pad;
+ bss_addr = load_addr + kbuf.bufsz + bss_pad;
for (i = 0; i < pi->ehdr->e_shnum; i++) {
if (!(sechdrs[i].sh_flags & SHF_ALLOC))
@@ -799,11 +791,11 @@ static int __kexec_load_purgatory(struct kimage *image, unsigned long min,
* Used later to identify which section is purgatory and skip it
* from checksumming.
*/
- pi->purgatory_buf = purgatory_buf;
+ pi->purgatory_buf = kbuf.buffer;
return ret;
out:
vfree(sechdrs);
- vfree(purgatory_buf);
+ vfree(kbuf.buffer);
return ret;
}
--
1.9.1
[toc] | [prev] | [standalone]
Back to top | Article view | linux.kernel
csiph-web