aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/features/core/jump-labels/arch-support.txt2
-rw-r--r--Documentation/features/debug/kprobes-on-ftrace/arch-support.txt2
-rw-r--r--arch/Kconfig3
-rw-r--r--arch/parisc/Kconfig25
-rw-r--r--arch/parisc/boot/compressed/.gitignore2
-rw-r--r--arch/parisc/include/asm/alternative.h11
-rw-r--r--arch/parisc/include/asm/fixmap.h1
-rw-r--r--arch/parisc/include/asm/ftrace.h1
-rw-r--r--arch/parisc/include/asm/kexec.h37
-rw-r--r--arch/parisc/include/asm/pdc.h1
-rw-r--r--arch/parisc/include/asm/string.h15
-rw-r--r--arch/parisc/kernel/Makefile2
-rw-r--r--arch/parisc/kernel/alternative.c23
-rw-r--r--arch/parisc/kernel/entry.S99
-rw-r--r--arch/parisc/kernel/firmware.c13
-rw-r--r--arch/parisc/kernel/ftrace.c64
-rw-r--r--arch/parisc/kernel/kexec.c112
-rw-r--r--arch/parisc/kernel/kexec_file.c86
-rw-r--r--arch/parisc/kernel/kprobes.c4
-rw-r--r--arch/parisc/kernel/pacache.S9
-rw-r--r--arch/parisc/kernel/parisc_ksyms.c4
-rw-r--r--arch/parisc/kernel/pci.c11
-rw-r--r--arch/parisc/kernel/relocate_kernel.S149
-rw-r--r--arch/parisc/kernel/smp.c1
-rw-r--r--arch/parisc/kernel/syscalls/syscall.tbl3
-rw-r--r--arch/parisc/kernel/traps.c2
-rw-r--r--arch/parisc/lib/Makefile4
-rw-r--r--arch/parisc/lib/memset.c91
-rw-r--r--arch/parisc/lib/string.S136
-rw-r--r--arch/powerpc/Kconfig1
-rw-r--r--arch/powerpc/kernel/kexec_elf_64.c545
-rw-r--r--drivers/parisc/dino.c30
-rw-r--r--drivers/parisc/eisa_enumerator.c10
-rw-r--r--drivers/parisc/hppb.c11
-rw-r--r--include/linux/kexec.h23
-rw-r--r--include/uapi/linux/kexec.h1
-rw-r--r--kernel/Makefile1
-rw-r--r--kernel/kexec_elf.c430
38 files changed, 1289 insertions, 676 deletions
diff --git a/Documentation/features/core/jump-labels/arch-support.txt b/Documentation/features/core/jump-labels/arch-support.txt
index 7fc2e243dee9..cae7be2f7725 100644
--- a/Documentation/features/core/jump-labels/arch-support.txt
+++ b/Documentation/features/core/jump-labels/arch-support.txt
@@ -21,7 +21,7 @@
| nds32: | TODO |
| nios2: | TODO |
| openrisc: | TODO |
- | parisc: | TODO |
+ | parisc: | ok |
| powerpc: | ok |
| riscv: | TODO |
| s390: | ok |
diff --git a/Documentation/features/debug/kprobes-on-ftrace/arch-support.txt b/Documentation/features/debug/kprobes-on-ftrace/arch-support.txt
index 68f266944d5f..4fae0464ddff 100644
--- a/Documentation/features/debug/kprobes-on-ftrace/arch-support.txt
+++ b/Documentation/features/debug/kprobes-on-ftrace/arch-support.txt
@@ -21,7 +21,7 @@
| nds32: | TODO |
| nios2: | TODO |
| openrisc: | TODO |
- | parisc: | TODO |
+ | parisc: | ok |
| powerpc: | ok |
| riscv: | TODO |
| s390: | TODO |
diff --git a/arch/Kconfig b/arch/Kconfig
index aa6bdb3df5c1..71d9ae0c0ea1 100644
--- a/arch/Kconfig
+++ b/arch/Kconfig
@@ -18,6 +18,9 @@ config KEXEC_CORE
select CRASH_CORE
bool
+config KEXEC_ELF
+ bool
+
config HAVE_IMA_KEXEC
bool
diff --git a/arch/parisc/Kconfig b/arch/parisc/Kconfig
index 6d732e451071..2e757c785239 100644
--- a/arch/parisc/Kconfig
+++ b/arch/parisc/Kconfig
@@ -61,6 +61,8 @@ config PARISC
select HAVE_KRETPROBES
select HAVE_DYNAMIC_FTRACE if $(cc-option,-fpatchable-function-entry=1,1)
select HAVE_FTRACE_MCOUNT_RECORD if HAVE_DYNAMIC_FTRACE
+ select HAVE_KPROBES_ON_FTRACE
+ select HAVE_DYNAMIC_FTRACE_WITH_REGS
help
The PA-RISC microprocessor is designed by Hewlett-Packard and used
@@ -344,6 +346,29 @@ config NR_CPUS
depends on SMP
default "4"
+config KEXEC
+ bool "Kexec system call"
+ select KEXEC_CORE
+ help
+ kexec is a system call that implements the ability to shutdown your
+ current kernel, and to start another kernel. It is like a reboot
+ but it is independent of the system firmware. And like a reboot
+ you can start any kernel with it, not just Linux.
+
+ It is an ongoing process to be certain the hardware in a machine
+ shutdown, so do not be surprised if this code does not
+ initially work for you.
+
+config KEXEC_FILE
+ bool "kexec file based system call"
+ select KEXEC_CORE
+ select KEXEC_ELF
+ help
+ This enables the kexec_file_load() System call. This is
+ file based and takes file descriptors as system call argument
+ for kernel and initramfs as opposed to list of segments as
+ accepted by previous system call.
+
endmenu
diff --git a/arch/parisc/boot/compressed/.gitignore b/arch/parisc/boot/compressed/.gitignore
index ae06b9b4c02f..926cd41c1069 100644
--- a/arch/parisc/boot/compressed/.gitignore
+++ b/arch/parisc/boot/compressed/.gitignore
@@ -1,3 +1,5 @@
+firmware.c
+real2.S
sizes.h
vmlinux
vmlinux.lds
diff --git a/arch/parisc/include/asm/alternative.h b/arch/parisc/include/asm/alternative.h
index 793d8baa3a10..0ec54f43d6d2 100644
--- a/arch/parisc/include/asm/alternative.h
+++ b/arch/parisc/include/asm/alternative.h
@@ -8,6 +8,7 @@
#define ALT_COND_NO_ICACHE 0x04 /* if system has no i-cache */
#define ALT_COND_NO_SPLIT_TLB 0x08 /* if split_tlb == 0 */
#define ALT_COND_NO_IOC_FDC 0x10 /* if I/O cache does not need flushes */
+#define ALT_COND_RUN_ON_QEMU 0x20 /* if running on QEMU */
#define INSN_PxTLB 0x02 /* modify pdtlb, pitlb */
#define INSN_NOP 0x08000240 /* nop */
@@ -21,7 +22,7 @@
struct alt_instr {
s32 orig_offset; /* offset to original instructions */
- u32 len; /* end of original instructions */
+ s32 len; /* end of original instructions */
u32 cond; /* see ALT_COND_XXX */
u32 replacement; /* replacement instruction or code */
};
@@ -40,12 +41,20 @@ void apply_alternatives(struct alt_instr *start, struct alt_instr *end,
#else
+/* to replace one single instructions by a new instruction */
#define ALTERNATIVE(from, to, cond, replacement)\
.section .altinstructions, "aw" ! \
.word (from - .), (to - from)/4 ! \
.word cond, replacement ! \
.previous
+/* to replace multiple instructions by new code */
+#define ALTERNATIVE_CODE(from, num_instructions, cond, new_instr_ptr)\
+ .section .altinstructions, "aw" ! \
+ .word (from - .), -num_instructions ! \
+ .word cond, (new_instr_ptr - .) ! \
+ .previous
+
#endif /* __ASSEMBLY__ */
#endif /* __ASM_PARISC_ALTERNATIVE_H */
diff --git a/arch/parisc/include/asm/fixmap.h b/arch/parisc/include/asm/fixmap.h
index 288da73d4cc0..e480b2c05407 100644
--- a/arch/parisc/include/asm/fixmap.h
+++ b/arch/parisc/include/asm/fixmap.h
@@ -30,6 +30,7 @@
enum fixed_addresses {
/* Support writing RO kernel text via kprobes, jump labels, etc. */
FIX_TEXT_POKE0,
+ FIX_TEXT_KEXEC,
FIX_BITMAP_COUNT
};
diff --git a/arch/parisc/include/asm/ftrace.h b/arch/parisc/include/asm/ftrace.h
index 958c0aa5dbb2..a7cf0d05ccf4 100644
--- a/arch/parisc/include/asm/ftrace.h
+++ b/arch/parisc/include/asm/ftrace.h
@@ -8,6 +8,7 @@ extern void mcount(void);
#define MCOUNT_ADDR ((unsigned long)mcount)
#define MCOUNT_INSN_SIZE 4
#define CC_USING_NOP_MCOUNT
+#define ARCH_SUPPORTS_FTRACE_OPS 1
extern unsigned long sys_call_table[];
extern unsigned long return_address(unsigned int);
diff --git a/arch/parisc/include/asm/kexec.h b/arch/parisc/include/asm/kexec.h
new file mode 100644
index 000000000000..a99ea747d7ed
--- /dev/null
+++ b/arch/parisc/include/asm/kexec.h
@@ -0,0 +1,37 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _ASM_PARISC_KEXEC_H
+#define _ASM_PARISC_KEXEC_H
+
+#ifdef CONFIG_KEXEC
+
+/* Maximum physical address we can use pages from */
+#define KEXEC_SOURCE_MEMORY_LIMIT (-1UL)
+/* Maximum address we can reach in physical address mode */
+#define KEXEC_DESTINATION_MEMORY_LIMIT (-1UL)
+/* Maximum address we can use for the control code buffer */
+#define KEXEC_CONTROL_MEMORY_LIMIT (-1UL)
+
+#define KEXEC_CONTROL_PAGE_SIZE 4096
+
+#define KEXEC_ARCH KEXEC_ARCH_PARISC
+#define ARCH_HAS_KIMAGE_ARCH
+
+#ifndef __ASSEMBLY__
+
+struct kimage_arch {
+ unsigned long initrd_start;
+ unsigned long initrd_end;
+ unsigned long cmdline;
+};
+
+static inline void crash_setup_regs(struct pt_regs *newregs,
+ struct pt_regs *oldregs)
+{
+ /* Dummy implementation for now */
+}
+
+#endif /* __ASSEMBLY__ */
+
+#endif /* CONFIG_KEXEC */
+
+#endif /* _ASM_PARISC_KEXEC_H */
diff --git a/arch/parisc/include/asm/pdc.h b/arch/parisc/include/asm/pdc.h
index 19bb2e46cd36..b388d8176588 100644
--- a/arch/parisc/include/asm/pdc.h
+++ b/arch/parisc/include/asm/pdc.h
@@ -91,6 +91,7 @@ int pdc_sti_call(unsigned long func, unsigned long flags,
unsigned long inptr, unsigned long outputr,
unsigned long glob_cfg);
+int __pdc_cpu_rendezvous(void);
static inline char * os_id_to_string(u16 os_id) {
switch(os_id) {
case OS_ID_NONE: return "No OS";
diff --git a/arch/parisc/include/asm/string.h b/arch/parisc/include/asm/string.h
index f6e1132f4e35..4a0c9dbd62fd 100644
--- a/arch/parisc/include/asm/string.h
+++ b/arch/parisc/include/asm/string.h
@@ -8,4 +8,19 @@ extern void * memset(void *, int, size_t);
#define __HAVE_ARCH_MEMCPY
void * memcpy(void * dest,const void *src,size_t count);
+#define __HAVE_ARCH_STRLEN
+extern size_t strlen(const char *s);
+
+#define __HAVE_ARCH_STRCPY
+extern char *strcpy(char *dest, const char *src);
+
+#define __HAVE_ARCH_STRNCPY
+extern char *strncpy(char *dest, const char *src, size_t count);
+
+#define __HAVE_ARCH_STRCAT
+extern char *strcat(char *dest, const char *src);
+
+#define __HAVE_ARCH_MEMSET
+extern void *memset(void *, int, size_t);
+
#endif
diff --git a/arch/parisc/kernel/Makefile b/arch/parisc/kernel/Makefile
index c232266b517c..2663c8f8be11 100644
--- a/arch/parisc/kernel/Makefile
+++ b/arch/parisc/kernel/Makefile
@@ -37,3 +37,5 @@ obj-$(CONFIG_FUNCTION_GRAPH_TRACER) += ftrace.o
obj-$(CONFIG_JUMP_LABEL) += jump_label.o
obj-$(CONFIG_KGDB) += kgdb.o
obj-$(CONFIG_KPROBES) += kprobes.o
+obj-$(CONFIG_KEXEC) += kexec.o relocate_kernel.o
+obj-$(CONFIG_KEXEC_FILE) += kexec_file.o
diff --git a/arch/parisc/kernel/alternative.c b/arch/parisc/kernel/alternative.c
index ca1f5ca0540a..3c66d5c4d90d 100644
--- a/arch/parisc/kernel/alternative.c
+++ b/arch/parisc/kernel/alternative.c
@@ -28,7 +28,8 @@ void __init_or_module apply_alternatives(struct alt_instr *start,
for (entry = start; entry < end; entry++, index++) {
- u32 *from, len, cond, replacement;
+ u32 *from, cond, replacement;
+ s32 len;
from = (u32 *)((ulong)&entry->orig_offset + entry->orig_offset);
len = entry->len;
@@ -49,6 +50,8 @@ void __init_or_module apply_alternatives(struct alt_instr *start,
continue;
if ((cond & ALT_COND_NO_ICACHE) && (cache_info.ic_size != 0))
continue;
+ if ((cond & ALT_COND_RUN_ON_QEMU) && !running_on_qemu)
+ continue;
/*
* If the PDC_MODEL capabilities has Non-coherent IO-PDIR bit
@@ -74,11 +77,19 @@ void __init_or_module apply_alternatives(struct alt_instr *start,
if (replacement == INSN_NOP && len > 1)
replacement = 0xe8000002 + (len-2)*8; /* "b,n .+8" */
- pr_debug("Do %d: Cond 0x%x, Replace %02d instructions @ 0x%px with 0x%08x\n",
- index, cond, len, from, replacement);
-
- /* Replace instruction */
- *from = replacement;
+ pr_debug("ALTERNATIVE %3d: Cond %2x, Replace %2d instructions to 0x%08x @ 0x%px (%pS)\n",
+ index, cond, len, replacement, from, from);
+
+ if (len < 0) {
+ /* Replace multiple instruction by new code */
+ u32 *source;
+ len = -len;
+ source = (u32 *)((ulong)&entry->replacement + entry->replacement);
+ memcpy(from, source, 4 * len);
+ } else {
+ /* Replace by one instruction */
+ *from = replacement;
+ }
applied++;
}
diff --git a/arch/parisc/kernel/entry.S b/arch/parisc/kernel/entry.S
index d9d3387f7c47..1d1d748c227f 100644
--- a/arch/parisc/kernel/entry.S
+++ b/arch/parisc/kernel/entry.S
@@ -1996,6 +1996,7 @@ _mcount:
* calling mcount(), and 2 instructions for ftrace_stub(). That way we
* have all on one L1 cacheline.
*/
+ ldi 0, %arg3
b ftrace_function_trampoline
copy %r3, %arg2 /* caller original %sp */
ftrace_stub:
@@ -2048,6 +2049,7 @@ ftrace_caller:
LDREG 0(%r3), %r25
copy %rp, %r26
ldo -8(%r25), %r25
+ ldi 0, %r23 /* no pt_regs */
b,l ftrace_function_trampoline, %rp
copy %r3, %r24
@@ -2075,6 +2077,103 @@ ftrace_caller:
ENDPROC_CFI(ftrace_caller)
+#ifdef CONFIG_HAVE_DYNAMIC_FTRACE_WITH_REGS
+ENTRY_CFI(ftrace_regs_caller,caller,frame=FTRACE_FRAME_SIZE+PT_SZ_ALGN,
+ CALLS,SAVE_RP,SAVE_SP)
+ftrace_regs_caller:
+ .global ftrace_regs_caller
+
+ ldo -FTRACE_FRAME_SIZE(%sp), %r1
+ STREG %rp, -RP_OFFSET(%r1)
+
+ copy %sp, %r1
+ ldo PT_SZ_ALGN(%sp), %sp
+
+ STREG %rp, PT_GR2(%r1)
+ STREG %r3, PT_GR3(%r1)
+ STREG %r4, PT_GR4(%r1)
+ STREG %r5, PT_GR5(%r1)
+ STREG %r6, PT_GR6(%r1)
+ STREG %r7, PT_GR7(%r1)
+ STREG %r8, PT_GR8(%r1)
+ STREG %r9, PT_GR9(%r1)
+ STREG %r10, PT_GR10(%r1)
+ STREG %r11, PT_GR11(%r1)
+ STREG %r12, PT_GR12(%r1)
+ STREG %r13, PT_GR13(%r1)
+ STREG %r14, PT_GR14(%r1)
+ STREG %r15, PT_GR15(%r1)
+ STREG %r16, PT_GR16(%r1)
+ STREG %r17, PT_GR17(%r1)
+ STREG %r18, PT_GR18(%r1)
+ STREG %r19, PT_GR19(%r1)
+ STREG %r20, PT_GR20(%r1)
+ STREG %r21, PT_GR21(%r1)
+ STREG %r22, PT_GR22(%r1)
+ STREG %r23, PT_GR23(%r1)
+ STREG %r24, PT_GR24(%r1)
+ STREG %r25, PT_GR25(%r1)
+ STREG %r26, PT_GR26(%r1)
+ STREG %r27, PT_GR27(%r1)
+ STREG %r28, PT_GR28(%r1)
+ STREG %r29, PT_GR29(%r1)
+ STREG %r30, PT_GR30(%r1)
+ STREG %r31, PT_GR31(%r1)
+ mfctl %cr11, %r26
+ STREG %r26, PT_SAR(%r1)
+
+ copy %rp, %r26
+ LDREG -FTRACE_FRAME_SIZE-PT_SZ_ALGN(%sp), %r25
+ ldo -8(%r25), %r25
+ copy %r3, %arg2
+ b,l ftrace_function_trampoline, %rp
+ copy %r1, %arg3 /* struct pt_regs */
+
+ ldo -PT_SZ_ALGN(%sp), %r1
+
+ LDREG PT_SAR(%r1), %rp
+ mtctl %rp, %cr11
+
+ LDREG PT_GR2(%r1), %rp
+ LDREG PT_GR3(%r1), %r3
+ LDREG PT_GR4(%r1), %r4
+ LDREG PT_GR5(%r1), %r5
+ LDREG PT_GR6(%r1), %r6
+ LDREG PT_GR7(%r1), %r7
+ LDREG PT_GR8(%r1), %r8
+ LDREG PT_GR9(%r1), %r9
+ LDREG PT_GR10(%r1),%r10
+ LDREG PT_GR11(%r1),%r11
+ LDREG PT_GR12(%r1),%r12
+ LDREG PT_GR13(%r1),%r13
+ LDREG PT_GR14(%r1),%r14
+ LDREG PT_GR15(%r1),%r15
+ LDREG PT_GR16(%r1),%r16
+ LDREG PT_GR17(%r1),%r17
+ LDREG PT_GR18(%r1),%r18
+ LDREG PT_GR19(%r1),%r19
+ LDREG PT_GR20(%r1),%r20
+ LDREG PT_GR21(%r1),%r21
+ LDREG PT_GR22(%r1),%r22
+ LDREG PT_GR23(%r1),%r23
+ LDREG PT_GR24(%r1),%r24
+ LDREG PT_GR25(%r1),%r25
+ LDREG PT_GR26(%r1),%r26
+ LDREG PT_GR27(%r1),%r27
+ LDREG PT_GR28(%r1),%r28
+ LDREG PT_GR29(%r1),%r29
+ LDREG PT_GR30(%r1),%r30
+ LDREG PT_GR31(%r1),%r31
+
+ ldo -PT_SZ_ALGN(%sp), %sp
+ LDREGM -FTRACE_FRAME_SIZE(%sp), %r1
+ /* Adjust return point to jump back to beginning of traced function */
+ ldo -4(%r1), %r1
+ bv,n (%r1)
+
+ENDPROC_CFI(ftrace_regs_caller)
+
+#endif
#endif
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
diff --git a/arch/parisc/kernel/firmware.c b/arch/parisc/kernel/firmware.c
index 58cc08e7fd12..1d976f2ebff0 100644
--- a/arch/parisc/kernel/firmware.c
+++ b/arch/parisc/kernel/firmware.c
@@ -312,6 +312,19 @@ int pdc_chassis_disp(unsigned long disp)
}
/**
+ * pdc_cpu_rendenzvous - Stop currently executing CPU
+ * @retval: -1 on error, 0 on success
+ */
+int __pdc_cpu_rendezvous(void)
+{
+ if (is_pdc_pat())
+ return mem_pdc_call(PDC_PAT_CPU, PDC_PAT_CPU_RENDEZVOUS);
+ else
+ return mem_pdc_call(PDC_PROC, 1, 0);
+}
+
+
+/**
* pdc_chassis_warn - Fetches chassis warnings
* @retval: -1 on error, 0 on success
*/
diff --git a/arch/parisc/kernel/ftrace.c b/arch/parisc/kernel/ftrace.c
index b6fb30f2e4bf..b836fc61a24f 100644
--- a/arch/parisc/kernel/ftrace.c
+++ b/arch/parisc/kernel/ftrace.c
@@ -13,6 +13,8 @@
#include <linux/init.h>
#include <linux/ftrace.h>
#include <linux/uaccess.h>
+#include <linux/kprobes.h>
+#include <linux/ptrace.h>
#include <asm/assembly.h>
#include <asm/sections.h>
@@ -48,17 +50,22 @@ static void __hot prepare_ftrace_return(unsigned long *parent,
void notrace __hot ftrace_function_trampoline(unsigned long parent,
unsigned long self_addr,
- unsigned long org_sp_gr3)
+ unsigned long org_sp_gr3,
+ struct pt_regs *regs)
{
#ifndef CONFIG_DYNAMIC_FTRACE
extern ftrace_func_t ftrace_trace_function;
#endif
- if (ftrace_trace_function != ftrace_stub)
- ftrace_trace_function(self_addr, parent, NULL, NULL);
+ extern struct ftrace_ops *function_trace_op;
+
+ if (function_trace_op->flags & FTRACE_OPS_FL_ENABLED &&
+ ftrace_trace_function != ftrace_stub)
+ ftrace_trace_function(self_addr, parent,
+ function_trace_op, regs);
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
if (ftrace_graph_return != (trace_func_graph_ret_t) ftrace_stub ||
- ftrace_graph_entry != ftrace_graph_entry_stub) {
+ ftrace_graph_entry != ftrace_graph_entry_stub) {
unsigned long *parent_rp;
/* calculate pointer to %rp in stack */
@@ -96,6 +103,12 @@ int ftrace_update_ftrace_func(ftrace_func_t func)
return 0;
}
+int ftrace_modify_call(struct dyn_ftrace *rec, unsigned long old_addr,
+ unsigned long addr)
+{
+ return 0;
+}
+
unsigned long ftrace_call_adjust(unsigned long addr)
{
return addr+(FTRACE_PATCHABLE_FUNCTION_SIZE-1)*4;
@@ -187,3 +200,46 @@ int ftrace_make_nop(struct module *mod, struct dyn_ftrace *rec,
return 0;
}
#endif
+
+#ifdef CONFIG_KPROBES_ON_FTRACE
+void kprobe_ftrace_handler(unsigned long ip, unsigned long parent_ip,
+ struct ftrace_ops *ops, struct pt_regs *regs)
+{
+ struct kprobe_ctlblk *kcb;
+ struct kprobe *p = get_kprobe((kprobe_opcode_t *)ip);
+
+ if (unlikely(!p) || kprobe_disabled(p))
+ return;
+
+ if (kprobe_running()) {
+ kprobes_inc_nmissed_count(p);
+ return;
+ }
+
+ __this_cpu_write(current_kprobe, p);
+
+ kcb = get_kprobe_ctlblk();
+ kcb->kprobe_status = KPROBE_HIT_ACTIVE;
+
+ regs->iaoq[0] = ip;
+ regs->iaoq[1] = ip + 4;
+
+ if (!p->pre_handler || !p->pre_handler(p, regs)) {
+ regs->iaoq[0] = ip + 4;
+ regs->iaoq[1] = ip + 8;
+
+ if (unlikely(p->post_handler)) {
+ kcb->kprobe_status = KPROBE_HIT_SSDONE;
+ p->post_handler(p, regs, 0);
+ }
+ }
+ __this_cpu_write(current_kprobe, NULL);
+}
+NOKPROBE_SYMBOL(kprobe_ftrace_handler);
+
+int arch_prepare_kprobe_ftrace(struct kprobe *p)
+{
+ p->ainsn.insn = NULL;
+ return 0;
+}
+#endif
diff --git a/arch/parisc/kernel/kexec.c b/arch/parisc/kernel/kexec.c
new file mode 100644
index 000000000000..5eb7f30edc1f
--- /dev/null
+++ b/arch/parisc/kernel/kexec.c
@@ -0,0 +1,112 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/kernel.h>
+#include <linux/console.h>
+#include <linux/kexec.h>
+#include <linux/delay.h>
+#include <asm/cacheflush.h>
+#include <asm/sections.h>
+
+extern void relocate_new_kernel(unsigned long head,
+ unsigned long start,
+ unsigned long phys);
+
+extern const unsigned int relocate_new_kernel_size;
+extern unsigned int kexec_initrd_start_offset;
+extern unsigned int kexec_initrd_end_offset;
+extern unsigned int kexec_cmdline_offset;
+extern unsigned int kexec_free_mem_offset;
+
+static void kexec_show_segment_info(const struct kimage *kimage,
+ unsigned long n)
+{
+ pr_debug(" segment[%lu]: %016lx - %016lx, 0x%lx bytes, %lu pages\n",
+ n,
+ kimage->segment[n].mem,
+ kimage->segment[n].mem + kimage->segment[n].memsz,
+ (unsigned long)kimage->segment[n].memsz,
+ (unsigned long)kimage->segment[n].memsz / PAGE_SIZE);
+}
+
+static void kexec_image_info(const struct kimage *kimage)
+{
+ unsigned long i;
+
+ pr_debug("kexec kimage info:\n");
+ pr_debug(" type: %d\n", kimage->type);
+ pr_debug(" start: %lx\n", kimage->start);
+ pr_debug(" head: %lx\n", kimage->head);
+ pr_debug(" nr_segments: %lu\n", kimage->nr_segments);
+
+ for (i = 0; i < kimage->nr_segments; i++)
+ kexec_show_segment_info(kimage, i);
+
+#ifdef CONFIG_KEXEC_FILE
+ if (kimage->file_mode) {
+ pr_debug("cmdline: %.*s\n", (int)kimage->cmdline_buf_len,
+ kimage->cmdline_buf);
+ }
+#endif
+}
+
+void machine_kexec_cleanup(struct kimage *kimage)
+{
+}
+
+void machine_crash_shutdown(struct pt_regs *regs)
+{
+}
+
+void machine_shutdown(void)
+{
+ smp_send_stop();
+ while (num_online_cpus() > 1) {
+ cpu_relax();
+ mdelay(1);
+ }
+}
+
+void machine_kexec(struct kimage *image)
+{
+#ifdef CONFIG_64BIT
+ Elf64_Fdesc desc;
+#endif
+ void (*reloc)(unsigned long head,
+ unsigned long start,
+ unsigned long phys);
+
+ unsigned long phys = page_to_phys(image->control_code_page);
+ void *virt = (void *)__fix_to_virt(FIX_TEXT_KEXEC);
+ struct kimage_arch *arch = &image->arch;
+
+ set_fixmap(FIX_TEXT_KEXEC, phys);
+
+ flush_cache_all();
+
+#ifdef CONFIG_64BIT
+ reloc = (void *)&desc;
+ desc.addr = (long long)virt;
+#else
+ reloc = (void *)virt;
+#endif
+
+ memcpy(virt, dereference_function_descriptor(relocate_new_kernel),
+ relocate_new_kernel_size);
+
+ *(unsigned long *)(virt + kexec_cmdline_offset) = arch->cmdline;
+ *(unsigned long *)(virt + kexec_initrd_start_offset) = arch->initrd_start;
+ *(unsigned long *)(virt + kexec_initrd_end_offset) = arch->initrd_end;
+ *(unsigned long *)(virt + kexec_free_mem_offset) = PAGE0->mem_free;
+
+ flush_cache_all();
+ flush_tlb_all();
+ local_irq_disable();
+
+ reloc(image->head & PAGE_MASK, image->start, phys);
+}
+
+int machine_kexec_prepare(struct kimage *image)
+{
+ kexec_image_info(image);
+ return 0;
+}
diff --git a/arch/parisc/kernel/kexec_file.c b/arch/parisc/kernel/kexec_file.c
new file mode 100644
index 000000000000..8c534204f0fd
--- /dev/null
+++ b/arch/parisc/kernel/kexec_file.c
@@ -0,0 +1,86 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Load ELF vmlinux file for the kexec_file_load syscall.
+ *
+ * Copyright (c) 2019 Sven Schnelle <svens@stackframe.org>
+ *
+ */
+#include <linux/elf.h>
+#include <linux/kexec.h>
+#include <linux/libfdt.h>
+#include <linux/module.h>
+#include <linux/of_fdt.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+static void *elf_load(struct kimage *image, char *kernel_buf,
+ unsigned long kernel_len, char *initrd,
+ unsigned long initrd_len, char *cmdline,
+ unsigned long cmdline_len)
+{
+ int ret, i;
+ unsigned long kernel_load_addr;
+ struct elfhdr ehdr;
+ struct kexec_elf_info elf_info;
+ struct kexec_buf kbuf = { .image = image, .buf_min = 0,
+ .buf_max = -1UL, };
+
+ ret = kexec_build_elf_info(kernel_buf, kernel_len, &ehdr, &elf_info);
+ if (ret)
+ goto out;
+
+ ret = kexec_elf_load(image, &ehdr, &elf_info, &kbuf, &kernel_load_addr);
+ if (ret)
+ goto out;
+
+ image->start = __pa(elf_info.ehdr->e_entry);
+
+ for (i = 0; i < image->nr_segments; i++)
+ image->segment[i].mem = __pa(image->segment[i].mem);
+
+ pr_debug("Loaded the kernel at 0x%lx, entry at 0x%lx\n",
+ kernel_load_addr, image->start);
+
+ if (initrd != NULL) {
+ kbuf.buffer = initrd;
+ kbuf.bufsz = kbuf.memsz = initrd_len;
+ kbuf.buf_align = PAGE_SIZE;
+ kbuf.top_down = false;
+ kbuf.mem = KEXEC_BUF_MEM_UNKNOWN;
+ ret = kexec_add_buffer(&kbuf);
+ if (ret)
+ goto out;
+
+ pr_debug("Loaded initrd at 0x%lx\n", kbuf.mem);
+ image->arch.initrd_start = kbuf.mem;
+ image->arch.initrd_end = kbuf.mem + initrd_len;
+ }
+
+ if (cmdline != NULL) {
+ kbuf.buffer = cmdline;
+ kbuf.bufsz = kbuf.memsz = ALIGN(cmdline_len, 8);
+ kbuf.buf_align = PAGE_SIZE;
+ kbuf.top_down = false;
+ kbuf.buf_min = PAGE0->mem_free + PAGE_SIZE;
+ kbuf.buf_max = kernel_load_addr;
+ kbuf.mem = KEXEC_BUF_MEM_UNKNOWN;
+ ret = kexec_add_buffer(&kbuf);
+ if (ret)
+ goto out;
+
+ pr_debug("Loaded cmdline at 0x%lx\n", kbuf.mem);
+ image->arch.cmdline = kbuf.mem;
+ }
+out:
+ return NULL;
+}
+
+const struct kexec_file_ops kexec_elf_ops = {
+ .probe = kexec_elf_probe,
+ .load = elf_load,
+};
+
+const struct kexec_file_ops * const kexec_file_loaders[] = {
+ &kexec_elf_ops,
+ NULL
+};
diff --git a/arch/parisc/kernel/kprobes.c b/arch/parisc/kernel/kprobes.c
index 5d7f2692ac5a..77ec51818916 100644
--- a/arch/parisc/kernel/kprobes.c
+++ b/arch/parisc/kernel/kprobes.c
@@ -281,10 +281,6 @@ int __kprobes arch_trampoline_kprobe(struct kprobe *p)
{
return p->addr == trampoline_p.addr;
}
-bool arch_kprobe_on_func_entry(unsigned long offset)
-{
- return !offset;
-}
int __init arch_init_kprobes(void)
{
diff --git a/arch/parisc/kernel/pacache.S b/arch/parisc/kernel/pacache.S
index df46b0e5a915..fa092ed1e837 100644
--- a/arch/parisc/kernel/pacache.S
+++ b/arch/parisc/kernel/pacache.S
@@ -174,6 +174,15 @@ fdtdone:
2: bv %r0(%r2)
nop
+
+ /*
+ * When running in qemu, drop whole flush_tlb_all_local function and
+ * replace by one pdtlbe instruction, for which QEMU will drop all
+ * local TLB entries.
+ */
+3: pdtlbe %r0(%sr1,%r0)
+ bv,n %r0(%r2)
+ ALTERNATIVE_CODE(flush_tlb_all_local, 2, ALT_COND_RUN_ON_QEMU, 3b)
ENDPROC_CFI(flush_tlb_all_local)
.import cache_info,data
diff --git a/arch/parisc/kernel/parisc_ksyms.c b/arch/parisc/kernel/parisc_ksyms.c
index e8a6a751dfd8..8ed409ecec93 100644
--- a/arch/parisc/kernel/parisc_ksyms.c
+++ b/arch/parisc/kernel/parisc_ksyms.c
@@ -17,6 +17,10 @@
#include <linux/string.h>
EXPORT_SYMBOL(memset);
+EXPORT_SYMBOL(strlen);
+EXPORT_SYMBOL(strcpy);
+EXPORT_SYMBOL(strncpy);
+EXPORT_SYMBOL(strcat);
#include <linux/atomic.h>
EXPORT_SYMBOL(__xchg8);
diff --git a/arch/parisc/kernel/pci.c b/arch/parisc/kernel/pci.c
index bc41ca243cfe..cf285b17a5ae 100644
--- a/arch/parisc/kernel/pci.c
+++ b/arch/parisc/kernel/pci.c
@@ -34,17 +34,6 @@
#define DBG_RES(x...)
#endif
-/* To be used as: mdelay(pci_post_reset_delay);
- *
- * post_reset is the time the kernel should stall to prevent anyone from
- * accessing the PCI bus once #RESET is de-asserted.
- * PCI spec somewhere says 1 second but with multi-PCI bus systems,
- * this makes the boot time much longer than necessary.
- * 20ms seems to work for all the HP PCI implementations to date.
- *
- * #define pci_post_reset_delay 50
- */
-
struct pci_port_ops *pci_port __ro_after_init;
struct pci_bios_ops *pci_bios __ro_after_init;
diff --git a/arch/parisc/kernel/relocate_kernel.S b/arch/parisc/kernel/relocate_kernel.S
new file mode 100644
index 000000000000..2561e52b8d9b
--- /dev/null
+++ b/arch/parisc/kernel/relocate_kernel.S
@@ -0,0 +1,149 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#include <linux/linkage.h>
+#include <linux/kexec.h>
+
+#include <asm/assembly.h>
+#include <asm/asm-offsets.h>
+#include <asm/page.h>
+#include <asm/setup.h>
+#include <asm/psw.h>
+
+.level PA_ASM_LEVEL
+
+.macro kexec_param name
+.align 8
+ENTRY(kexec\()_\name)
+#ifdef CONFIG_64BIT
+ .dword 0
+#else
+ .word 0
+#endif
+
+ENTRY(kexec\()_\name\()_offset)
+ .word kexec\()_\name - relocate_new_kernel
+.endm
+
+.text
+
+/* args:
+ * r26 - kimage->head
+ * r25 - start address of kernel
+ * r24 - physical address of relocate code
+ */
+
+ENTRY_CFI(relocate_new_kernel)
+0: copy %arg1, %rp
+ /* disable I and Q bit, so we are allowed to execute RFI */
+ rsm PSW_SM_I, %r0
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+
+ rsm PSW_SM_Q, %r0
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+
+ /*
+ * After return-from-interrupt, we want to run without Code/Data
+ * translation enabled just like on a normal boot.
+ */
+
+ /* calculate new physical execution address */
+ ldo 1f-0b(%arg2), %r1
+ mtctl %r0, %cr17 /* IIASQ */
+ mtctl %r0, %cr17 /* IIASQ */
+ mtctl %r1, %cr18 /* IIAOQ */
+ ldo 4(%r1),%r1
+ mtctl %r1, %cr18 /* IIAOQ */
+#ifdef CONFIG_64BIT
+ depdi,z 1, PSW_W_BIT, 1, %r1
+ mtctl %r1, %cr22 /* IPSW */
+#else
+ mtctl %r0, %cr22 /* IPSW */
+#endif
+ /* lets go... */
+ rfi
+1: nop
+ nop
+
+.Lloop:
+ LDREG,ma REG_SZ(%arg0), %r3
+ /* If crash kernel, no copy needed */
+ cmpib,COND(=),n 0,%r3,boot
+
+ bb,<,n %r3, 31 - IND_DONE_BIT, boot
+ bb,>=,n %r3, 31 - IND_INDIRECTION_BIT, .Lnotind
+ /* indirection, load and restart */
+ movb %r3, %arg0, .Lloop
+ depi 0, 31, PAGE_SHIFT, %arg0
+
+.Lnotind:
+ bb,>=,n %r3, 31 - IND_DESTINATION_BIT, .Lnotdest
+ b .Lloop
+ copy %r3, %r20
+
+.Lnotdest:
+ bb,>= %r3, 31 - IND_SOURCE_BIT, .Lloop
+ depi 0, 31, PAGE_SHIFT, %r3
+ copy %r3, %r21
+
+ /* copy page */
+ copy %r0, %r18
+ zdepi 1, 31 - PAGE_SHIFT, 1, %r18
+ add %r20, %r18, %r17
+
+ depi 0, 31, PAGE_SHIFT, %r20
+.Lcopy:
+ copy %r20, %r12
+ LDREG,ma REG_SZ(%r21), %r8
+ LDREG,ma REG_SZ(%r21), %r9
+ LDREG,ma REG_SZ(%r21), %r10
+ LDREG,ma REG_SZ(%r21), %r11
+ STREG,ma %r8, REG_SZ(%r20)
+ STREG,ma %r9, REG_SZ(%r20)
+ STREG,ma %r10, REG_SZ(%r20)
+ STREG,ma %r11, REG_SZ(%r20)
+
+#ifndef CONFIG_64BIT
+ LDREG,ma REG_SZ(%r21), %r8
+ LDREG,ma REG_SZ(%r21), %r9
+ LDREG,ma REG_SZ(%r21), %r10
+ LDREG,ma REG_SZ(%r21), %r11
+ STREG,ma %r8, REG_SZ(%r20)
+ STREG,ma %r9, REG_SZ(%r20)
+ STREG,ma %r10, REG_SZ(%r20)
+ STREG,ma %r11, REG_SZ(%r20)
+#endif
+
+ fdc %r0(%r12)
+ cmpb,COND(<<) %r20,%r17,.Lcopy
+ fic (%sr4, %r12)
+ b,n .Lloop
+
+boot:
+ mtctl %r0, %cr15
+
+ LDREG kexec_free_mem-0b(%arg2), %arg0
+ LDREG kexec_cmdline-0b(%arg2), %arg1
+ LDREG kexec_initrd_end-0b(%arg2), %arg3
+ LDREG kexec_initrd_start-0b(%arg2), %arg2
+ bv,n %r0(%rp)
+
+ENDPROC_CFI(relocate_new_kernel);
+
+ENTRY(relocate_new_kernel_size)
+ .word relocate_new_kernel_size - relocate_new_kernel
+
+kexec_param cmdline
+kexec_param initrd_start
+kexec_param initrd_end
+kexec_param free_mem
diff --git a/arch/parisc/kernel/smp.c b/arch/parisc/kernel/smp.c
index cbd074ba22da..e202c37e56af 100644
--- a/arch/parisc/kernel/smp.c
+++ b/arch/parisc/kernel/smp.c
@@ -109,6 +109,7 @@ halt_processor(void)
/* REVISIT : does PM *know* this CPU isn't available? */
set_cpu_online(smp_processor_id(), false);
local_irq_disable();
+ __pdc_cpu_rendezvous();
for (;;)
;
}
diff --git a/arch/parisc/kernel/syscalls/syscall.tbl b/arch/parisc/kernel/syscalls/syscall.tbl
index 670d1371aca1..285ff516150c 100644
--- a/arch/parisc/kernel/syscalls/syscall.tbl
+++ b/arch/parisc/kernel/syscalls/syscall.tbl
@@ -399,7 +399,8 @@
352 common pkey_alloc sys_pkey_alloc
353 common pkey_free sys_pkey_free
354 common rseq sys_rseq
-# 355 through 402 are unassigned to sync up with generic numbers
+355 common kexec_file_load sys_kexec_file_load sys_kexec_file_load
+# up to 402 is unassigned and reserved for arch specific syscalls
403 32 clock_gettime64 sys_clock_gettime sys_clock_gettime
404 32 clock_settime64 sys_clock_settime sys_clock_settime
405 32 clock_adjtime64 sys_clock_adjtime sys_clock_adjtime
diff --git a/arch/parisc/kernel/traps.c b/arch/parisc/kernel/traps.c
index 58dcf445e32f..82fc01189488 100644
--- a/arch/parisc/kernel/traps.c
+++ b/arch/parisc/kernel/traps.c
@@ -29,6 +29,7 @@
#include <linux/bug.h>
#include <linux/ratelimit.h>
#include <linux/uaccess.h>
+#include <linux/kdebug.h>
#include <asm/assembly.h>
#include <asm/io.h>
@@ -414,6 +415,7 @@ void parisc_terminate(char *msg, struct pt_regs *regs, int code, unsigned long o
{
static DEFINE_SPINLOCK(terminate_lock);
+ (void)notify_die(DIE_OOPS, msg, regs, 0, code, SIGTRAP);
bust_spinlocks(1);
set_eiem(0);
diff --git a/arch/parisc/lib/Makefile b/arch/parisc/lib/Makefile
index 7b197667faf6..2d7a9974dbae 100644
--- a/arch/parisc/lib/Makefile
+++ b/arch/parisc/lib/Makefile
@@ -3,7 +3,7 @@
# Makefile for parisc-specific library files
#
-lib-y := lusercopy.o bitops.o checksum.o io.o memset.o memcpy.o \
- ucmpdi2.o delay.o
+lib-y := lusercopy.o bitops.o checksum.o io.o memcpy.o \
+ ucmpdi2.o delay.o string.o
obj-y := iomap.o
diff --git a/arch/parisc/lib/memset.c b/arch/parisc/lib/memset.c
deleted file mode 100644
index 1d7929bd7642..000000000000
--- a/arch/parisc/lib/memset.c
+++ /dev/null
@@ -1,91 +0,0 @@
-/* Copyright (C) 1991, 1997 Free Software Foundation, Inc.
- This file is part of the GNU C Library.
-
- The GNU C Library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
-
- The GNU C Library 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
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with the GNU C Library; if not, write to the Free
- Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
- 02111-1307 USA. */
-
-/* Slight modifications for pa-risc linux - Paul Bame <bame@debian.org> */
-
-#include <linux/types.h>
-#include <asm/string.h>
-
-#define OPSIZ (BITS_PER_LONG/8)
-typedef unsigned long op_t;
-
-void *
-memset (void *dstpp, int sc, size_t len)
-{
- unsigned int c = sc;
- long int dstp = (long int) dstpp;
-
- if (len >= 8)
- {
- size_t xlen;
- op_t cccc;
-
- cccc = (unsigned char) c;
- cccc |= cccc << 8;
- cccc |= cccc << 16;
- if (OPSIZ > 4)
- /* Do the shift in two steps to avoid warning if long has 32 bits. */
- cccc |= (cccc << 16) << 16;
-
- /* There are at least some bytes to set.
- No need to test for LEN == 0 in this alignment loop. */
- while (dstp % OPSIZ != 0)
- {
- ((unsigned char *) dstp)[0] = c;
- dstp += 1;
- len -= 1;
- }
-
- /* Write 8 `op_t' per iteration until less than 8 `op_t' remain. */
- xlen = len / (OPSIZ * 8);
- while (xlen > 0)
- {
- ((op_t *) dstp)[0] = cccc;
- ((op_t *) dstp)[1] = cccc;
- ((op_t *) dstp)[2] = cccc;
- ((op_t *) dstp)[3] = cccc;
- ((op_t *) dstp)[4] = cccc;
- ((op_t *) dstp)[5] = cccc;
- ((op_t *) dstp)[6] = cccc;
- ((op_t *) dstp)[7] = cccc;
- dstp += 8 * OPSIZ;
- xlen -= 1;
- }
- len %= OPSIZ * 8;
-
- /* Write 1 `op_t' per iteration until less than OPSIZ bytes remain. */
- xlen = len / OPSIZ;
- while (xlen > 0)
- {
- ((op_t *) dstp)[0] = cccc;
- dstp += OPSIZ;
- xlen -= 1;
- }
- len %= OPSIZ;
- }
-
- /* Write the last few bytes. */
- while (len > 0)
- {
- ((unsigned char *) dstp)[0] = c;
- dstp += 1;
- len -= 1;
- }
-
- return dstpp;
-}
diff --git a/arch/parisc/lib/string.S b/arch/parisc/lib/string.S
new file mode 100644
index 000000000000..4a64264427a6
--- /dev/null
+++ b/arch/parisc/lib/string.S
@@ -0,0 +1,136 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PA-RISC assembly string functions
+ *
+ * Copyright (C) 2019 Helge Deller <deller@gmx.de>
+ */
+
+#include <asm/assembly.h>
+#include <linux/linkage.h>
+
+ .section .text.hot
+ .level PA_ASM_LEVEL
+
+ t0 = r20
+ t1 = r21
+ t2 = r22
+
+ENTRY_CFI(strlen, frame=0,no_calls)
+ or,COND(<>) arg0,r0,ret0
+ b,l,n .Lstrlen_null_ptr,r0
+ depwi 0,31,2,ret0
+ cmpb,COND(<>) arg0,ret0,.Lstrlen_not_aligned
+ ldw,ma 4(ret0),t0
+ cmpib,tr 0,r0,.Lstrlen_loop
+ uxor,nbz r0,t0,r0
+.Lstrlen_not_aligned:
+ uaddcm arg0,ret0,t1
+ shladd t1,3,r0,t1
+ mtsar t1
+ depwi -1,%sar,32,t0
+ uxor,nbz r0,t0,r0
+.Lstrlen_loop:
+ b,l,n .Lstrlen_end_loop,r0
+ ldw,ma 4(ret0),t0
+ cmpib,tr 0,r0,.Lstrlen_loop
+ uxor,nbz r0,t0,r0
+.Lstrlen_end_loop:
+ extrw,u,<> t0,7,8,r0
+ addib,tr,n -3,ret0,.Lstrlen_out
+ extrw,u,<> t0,15,8,r0
+ addib,tr,n -2,ret0,.Lstrlen_out
+ extrw,u,<> t0,23,8,r0
+ addi -1,ret0,ret0
+.Lstrlen_out:
+ bv r0(rp)
+ uaddcm ret0,arg0,ret0
+.Lstrlen_null_ptr:
+ bv,n r0(rp)
+ENDPROC_CFI(strlen)
+
+
+ENTRY_CFI(strcpy, frame=0,no_calls)
+ ldb 0(arg1),t0
+ stb t0,0(arg0)
+ ldo 0(arg0),ret0
+ ldo 1(arg1),t1
+ cmpb,= r0,t0,2f
+ ldo 1(arg0),t2
+1: ldb 0(t1),arg1
+ stb arg1,0(t2)
+ ldo 1(t1),t1
+ cmpb,<> r0,arg1,1b
+ ldo 1(t2),t2
+2: bv,n r0(rp)
+ENDPROC_CFI(strcpy)
+
+
+ENTRY_CFI(strncpy, frame=0,no_calls)
+ ldb 0(arg1),t0
+ stb t0,0(arg0)
+ ldo 1(arg1),t1
+ ldo 0(arg0),ret0
+ cmpb,= r0,t0,2f
+ ldo 1(arg0),arg1
+1: ldo -1(arg2),arg2
+ cmpb,COND(=),n r0,arg2,2f
+ ldb 0(t1),arg0
+ stb arg0,0(arg1)
+ ldo 1(t1),t1
+ cmpb,<> r0,arg0,1b
+ ldo 1(arg1),arg1
+2: bv,n r0(rp)
+ENDPROC_CFI(strncpy)
+
+
+ENTRY_CFI(strcat, frame=0,no_calls)
+ ldb 0(arg0),t0
+ cmpb,= t0,r0,2f
+ ldo 0(arg0),ret0
+ ldo 1(arg0),arg0
+1: ldb 0(arg0),t1
+ cmpb,<>,n r0,t1,1b
+ ldo 1(arg0),arg0
+2: ldb 0(arg1),t2
+ stb t2,0(arg0)
+ ldo 1(arg0),arg0
+ ldb 0(arg1),t0
+ cmpb,<> r0,t0,2b
+ ldo 1(arg1),arg1
+ bv,n r0(rp)
+ENDPROC_CFI(strcat)
+
+
+ENTRY_CFI(memset, frame=0,no_calls)
+ copy arg0,ret0
+ cmpb,COND(=) r0,arg0,4f
+ copy arg0,t2
+ cmpb,COND(=) r0,arg2,4f
+ ldo -1(arg2),arg3
+ subi -1,arg3,t0
+ subi 0,t0,t1
+ cmpiclr,COND(>=) 0,t1,arg2
+ ldo -1(t1),arg2
+ extru arg2,31,2,arg0
+2: stb arg1,0(t2)
+ ldo 1(t2),t2
+ addib,>= -1,arg0,2b
+ ldo -1(arg3),arg3
+ cmpiclr,COND(<=) 4,arg2,r0
+ b,l,n 4f,r0
+#ifdef CONFIG_64BIT
+ depd,* r0,63,2,arg2
+#else
+ depw r0,31,2,arg2
+#endif
+ ldo 1(t2),t2
+3: stb arg1,-1(t2)
+ stb arg1,0(t2)
+ stb arg1,1(t2)
+ stb arg1,2(t2)
+ addib,COND(>) -4,arg2,3b
+ ldo 4(t2),t2
+4: bv,n r0(rp)
+ENDPROC_CFI(memset)
+
+ .end
diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
index d8dcd8820369..891cd23f9c8b 100644
--- a/arch/powerpc/Kconfig
+++ b/arch/powerpc/Kconfig
@@ -511,6 +511,7 @@ config KEXEC_FILE
select KEXEC_CORE
select HAVE_IMA_KEXEC
select BUILD_BIN2C
+ select KEXEC_ELF
depends on PPC64
depends on CRYPTO=y
depends on CRYPTO_SHA256=y
diff --git a/arch/powerpc/kernel/kexec_elf_64.c b/arch/powerpc/kernel/kexec_elf_64.c
index 83cf7b852876..3072fd6dbe94 100644
--- a/arch/powerpc/kernel/kexec_elf_64.c
+++ b/arch/powerpc/kernel/kexec_elf_64.c
@@ -23,541 +23,6 @@
#include <linux/slab.h>
#include <linux/types.h>
-#define PURGATORY_STACK_SIZE (16 * 1024)
-
-#define elf_addr_to_cpu elf64_to_cpu
-
-#ifndef Elf_Rel
-#define Elf_Rel Elf64_Rel
-#endif /* Elf_Rel */
-
-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;
-};
-
-static inline bool elf_is_elf_file(const struct elfhdr *ehdr)
-{
- return memcmp(ehdr->e_ident, ELFMAG, SELFMAG) == 0;
-}
-
-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;
-}
-
-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 -ENOEXEC;
- } 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_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));
-}
-/**
- * build_elf_exec_info - read ELF executable and check that we can use it
- */
-static int build_elf_exec_info(const char *buf, size_t len, struct elfhdr *ehdr,
- struct elf_info *elf_info)
-{
- int i;
- int ret;
-
- ret = elf_read_from_buffer(buf, len, ehdr, elf_info);
- if (ret)
- return ret;
-
- /* Big endian vmlinux has type ET_DYN. */
- if (ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN) {
- pr_err("Not an ELF executable.\n");
- goto error;
- } else if (!elf_info->proghdrs) {
- pr_err("No ELF program header.\n");
- goto error;
- }
-
- for (i = 0; i < ehdr->e_phnum; i++) {
- /*
- * Kexec does not support loading interpreters.
- * In addition this check keeps us from attempting
- * to kexec ordinay executables.
- */
- if (elf_info->proghdrs[i].p_type == PT_INTERP) {
- pr_err("Requires an ELF interpreter.\n");
- goto error;
- }
- }
-
- return 0;
-error:
- elf_free_info(elf_info);
- return -ENOEXEC;
-}
-
-static int elf64_probe(const char *buf, unsigned long len)
-{
- struct elfhdr ehdr;
- struct elf_info elf_info;
- int ret;
-
- ret = build_elf_exec_info(buf, len, &ehdr, &elf_info);
- if (ret)
- return ret;
-
- elf_free_info(&elf_info);
-
- return elf_check_arch(&ehdr) ? 0 : -ENOEXEC;
-}
-
-/**
- * elf_exec_load - load ELF executable image
- * @lowest_load_addr: On return, will be the address where the first PT_LOAD
- * section will be loaded in memory.
- *
- * Return:
- * 0 on success, negative value on failure.
- */
-static int elf_exec_load(struct kimage *image, struct elfhdr *ehdr,
- struct elf_info *elf_info,
- unsigned long *lowest_load_addr)
-{
- unsigned long base = 0, lowest_addr = UINT_MAX;
- int ret;
- size_t i;
- struct kexec_buf kbuf = { .image = image, .buf_max = ppc64_rma_size,
- .top_down = false };
-
- /* Read in the PT_LOAD segments. */
- for (i = 0; i < ehdr->e_phnum; i++) {
- unsigned long load_addr;
- size_t size;
- const struct elf_phdr *phdr;
-
- phdr = &elf_info->proghdrs[i];
- if (phdr->p_type != PT_LOAD)
- continue;
-
- size = phdr->p_filesz;
- if (size > phdr->p_memsz)
- size = phdr->p_memsz;
-
- kbuf.buffer = (void *) elf_info->buffer + phdr->p_offset;
- kbuf.bufsz = size;
- kbuf.memsz = phdr->p_memsz;
- kbuf.buf_align = phdr->p_align;
- kbuf.buf_min = phdr->p_paddr + base;
- kbuf.mem = KEXEC_BUF_MEM_UNKNOWN;
- ret = kexec_add_buffer(&kbuf);
- if (ret)
- goto out;
- load_addr = kbuf.mem;
-
- if (load_addr < lowest_addr)
- lowest_addr = load_addr;
- }
-
- /* Update entry point to reflect new load address. */
- ehdr->e_entry += base;
-
- *lowest_load_addr = lowest_addr;
- ret = 0;
- out:
- return ret;
-}
-
static void *elf64_load(struct kimage *image, char *kernel_buf,
unsigned long kernel_len, char *initrd,
unsigned long initrd_len, char *cmdline,
@@ -570,18 +35,18 @@ static void *elf64_load(struct kimage *image, char *kernel_buf,
void *fdt;
const void *slave_code;
struct elfhdr ehdr;
- struct elf_info elf_info;
+ struct kexec_elf_info elf_info;
struct kexec_buf kbuf = { .image = image, .buf_min = 0,
.buf_max = ppc64_rma_size };
struct kexec_buf pbuf = { .image = image, .buf_min = 0,
.buf_max = ppc64_rma_size, .top_down = true,
.mem = KEXEC_BUF_MEM_UNKNOWN };
- ret = build_elf_exec_info(kernel_buf, kernel_len, &ehdr, &elf_info);
+ ret = kexec_build_elf_info(kernel_buf, kernel_len, &ehdr, &elf_info);
if (ret)
goto out;
- ret = elf_exec_load(image, &ehdr, &elf_info, &kernel_load_addr);
+ ret = kexec_elf_load(image, &ehdr, &elf_info, &kbuf, &kernel_load_addr);
if (ret)
goto out;
@@ -648,13 +113,13 @@ static void *elf64_load(struct kimage *image, char *kernel_buf,
pr_err("Error setting up the purgatory.\n");
out:
- elf_free_info(&elf_info);
+ kexec_free_elf_info(&elf_info);
/* Make kimage_file_post_load_cleanup free the fdt buffer for us. */
return ret ? ERR_PTR(ret) : fdt;
}
const struct kexec_file_ops kexec_elf64_ops = {
- .probe = elf64_probe,
+ .probe = kexec_elf_probe,
.load = elf64_load,
};
diff --git a/drivers/parisc/dino.c b/drivers/parisc/dino.c
index 3c730103e637..2f1cac89ddf5 100644
--- a/drivers/parisc/dino.c
+++ b/drivers/parisc/dino.c
@@ -6,7 +6,7 @@
** (c) Copyright 1999 SuSE GmbH
** (c) Copyright 1999,2000 Hewlett-Packard Company
** (c) Copyright 2000 Grant Grundler
-** (c) Copyright 2006 Helge Deller
+** (c) Copyright 2006-2019 Helge Deller
**
**
** This module provides access to Dino PCI bus (config/IOport spaces)
@@ -156,6 +156,15 @@ static inline struct dino_device *DINO_DEV(struct pci_hba_data *hba)
return container_of(hba, struct dino_device, hba);
}
+/* Check if PCI device is behind a Card-mode Dino. */
+static int pci_dev_is_behind_card_dino(struct pci_dev *dev)
+{
+ struct dino_device *dino_dev;
+
+ dino_dev = DINO_DEV(parisc_walk_tree(dev->bus->bridge));
+ return is_card_dino(&dino_dev->hba.dev->id);
+}
+
/*
* Dino Configuration Space Accessor Functions
*/
@@ -437,6 +446,21 @@ static void quirk_cirrus_cardbus(struct pci_dev *dev)
}
DECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_CIRRUS, PCI_DEVICE_ID_CIRRUS_6832, quirk_cirrus_cardbus );
+#ifdef CONFIG_TULIP
+static void pci_fixup_tulip(struct pci_dev *dev)
+{
+ if (!pci_dev_is_behind_card_dino(dev))
+ return;
+ if (!(pci_resource_flags(dev, 1) & IORESOURCE_MEM))
+ return;
+ pr_warn("%s: HP HSC-PCI Cards with card-mode Dino not yet supported.\n",
+ pci_name(dev));
+ /* Disable this card by zeroing the PCI resources */
+ memset(&dev->resource[0], 0, sizeof(dev->resource[0]));
+ memset(&dev->resource[1], 0, sizeof(dev->resource[1]));
+}
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_DEC, PCI_ANY_ID, pci_fixup_tulip);
+#endif /* CONFIG_TULIP */
static void __init
dino_bios_init(void)
@@ -863,14 +887,14 @@ static int __init dino_common_init(struct parisc_device *dev,
#define CUJO_RAVEN_BADPAGE 0x01003000UL
#define CUJO_FIREHAWK_BADPAGE 0x01607000UL
-static const char *dino_vers[] = {
+static const char dino_vers[][4] = {
"2.0",
"2.1",
"3.0",
"3.1"
};
-static const char *cujo_vers[] = {
+static const char cujo_vers[][4] = {
"1.0",
"2.0"
};
diff --git a/drivers/parisc/eisa_enumerator.c b/drivers/parisc/eisa_enumerator.c
index 9c08222c0cc6..f54a6f450391 100644
--- a/drivers/parisc/eisa_enumerator.c
+++ b/drivers/parisc/eisa_enumerator.c
@@ -93,7 +93,7 @@ static int configure_memory(const unsigned char *buf,
res->start = mem_parent->start + get_24(buf+len+2);
res->end = res->start + get_16(buf+len+5)*1024;
res->flags = IORESOURCE_MEM;
- printk("memory %lx-%lx ", (unsigned long)res->start, (unsigned long)res->end);
+ pr_cont("memory %pR ", res);
result = request_resource(mem_parent, res);
if (result < 0) {
printk(KERN_ERR "EISA Enumerator: failed to claim EISA Bus address space!\n");
@@ -123,7 +123,7 @@ static int configure_irq(const unsigned char *buf)
for (i=0;i<HPEE_IRQ_MAX_ENT;i++) {
c = get_8(buf+len);
- printk("IRQ %d ", c & HPEE_IRQ_CHANNEL_MASK);
+ pr_cont("IRQ %d ", c & HPEE_IRQ_CHANNEL_MASK);
if (c & HPEE_IRQ_TRIG_LEVEL) {
eisa_make_irq_level(c & HPEE_IRQ_CHANNEL_MASK);
} else {
@@ -153,7 +153,7 @@ static int configure_dma(const unsigned char *buf)
for (i=0;i<HPEE_DMA_MAX_ENT;i++) {
c = get_8(buf+len);
- printk("DMA %d ", c&HPEE_DMA_CHANNEL_MASK);
+ pr_cont("DMA %d ", c&HPEE_DMA_CHANNEL_MASK);
/* fixme: maybe initialize the dma channel withthe timing ? */
len+=2;
if (!(c & HPEE_DMA_MORE)) {
@@ -183,7 +183,7 @@ static int configure_port(const unsigned char *buf, struct resource *io_parent,
res->start = get_16(buf+len+1);
res->end = get_16(buf+len+1)+(c&HPEE_PORT_SIZE_MASK)+1;
res->flags = IORESOURCE_IO;
- printk("ioports %lx-%lx ", (unsigned long)res->start, (unsigned long)res->end);
+ pr_cont("ioports %pR ", res);
result = request_resource(io_parent, res);
if (result < 0) {
printk(KERN_ERR "EISA Enumerator: failed to claim EISA Bus address space!\n");
@@ -401,7 +401,7 @@ static int parse_slot_config(int slot,
}
pos = p0 + function_len;
}
- printk("\n");
+ pr_cont("\n");
if (!id_string_used) {
kfree(board);
}
diff --git a/drivers/parisc/hppb.c b/drivers/parisc/hppb.c
index 44e12c83cfa8..e60e68664654 100644
--- a/drivers/parisc/hppb.c
+++ b/drivers/parisc/hppb.c
@@ -61,8 +61,6 @@ static int __init hppb_probe(struct parisc_device *dev)
}
card = card->next;
}
- printk(KERN_INFO "Found GeckoBoa at 0x%llx\n",
- (unsigned long long) dev->hpa.start);
card->hpa = dev->hpa.start;
card->mmio_region.name = "HP-PB Bus";
@@ -72,10 +70,11 @@ static int __init hppb_probe(struct parisc_device *dev)
card->mmio_region.end = gsc_readl(dev->hpa.start + IO_IO_HIGH) - 1;
status = ccio_request_resource(dev, &card->mmio_region);
- if(status < 0) {
- printk(KERN_ERR "%s: failed to claim HP-PB bus space (%pR)\n",
- __FILE__, &card->mmio_region);
- }
+
+ pr_info("Found GeckoBoa at %pap, bus space %pR,%s claimed.\n",
+ &dev->hpa.start,
+ &card->mmio_region,
+ (status < 0) ? " not":"" );
return 0;
}
diff --git a/include/linux/kexec.h b/include/linux/kexec.h
index b9b1bc5f9669..f0b809258ed3 100644
--- a/include/linux/kexec.h
+++ b/include/linux/kexec.h
@@ -216,6 +216,29 @@ extern int crash_prepare_elf64_headers(struct crash_mem *mem, int kernel_map,
void **addr, unsigned long *sz);
#endif /* CONFIG_KEXEC_FILE */
+#ifdef CONFIG_KEXEC_ELF
+struct kexec_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;
+};
+
+int kexec_build_elf_info(const char *buf, size_t len, struct elfhdr *ehdr,
+ struct kexec_elf_info *elf_info);
+
+int kexec_elf_load(struct kimage *image, struct elfhdr *ehdr,
+ struct kexec_elf_info *elf_info,
+ struct kexec_buf *kbuf,
+ unsigned long *lowest_load_addr);
+
+void kexec_free_elf_info(struct kexec_elf_info *elf_info);
+int kexec_elf_probe(const char *buf, unsigned long len);
+#endif
struct kimage {
kimage_entry_t head;
kimage_entry_t *entry;
diff --git a/include/uapi/linux/kexec.h b/include/uapi/linux/kexec.h
index 6d112868272d..05669c87a0af 100644
--- a/include/uapi/linux/kexec.h
+++ b/include/uapi/linux/kexec.h
@@ -31,6 +31,7 @@
#define KEXEC_ARCH_DEFAULT ( 0 << 16)
#define KEXEC_ARCH_386 ( 3 << 16)
#define KEXEC_ARCH_68K ( 4 << 16)
+#define KEXEC_ARCH_PARISC (15 << 16)
#define KEXEC_ARCH_X86_64 (62 << 16)
#define KEXEC_ARCH_PPC (20 << 16)
#define KEXEC_ARCH_PPC64 (21 << 16)
diff --git a/kernel/Makefile b/kernel/Makefile
index ef0d95a190b4..48c5376d290a 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -64,6 +64,7 @@ obj-$(CONFIG_CRASH_CORE) += crash_core.o
obj-$(CONFIG_KEXEC_CORE) += kexec_core.o
obj-$(CONFIG_KEXEC) += kexec.o
obj-$(CONFIG_KEXEC_FILE) += kexec_file.o
+obj-$(CONFIG_KEXEC_ELF) += kexec_elf.o
obj-$(CONFIG_BACKTRACE_SELF_TEST) += backtracetest.o
obj-$(CONFIG_COMPAT) += compat.o
obj-$(CONFIG_CGROUPS) += cgroup/
diff --git a/kernel/kexec_elf.c b/kernel/kexec_elf.c
new file mode 100644
index 000000000000..d3689632e8b9
--- /dev/null
+++ b/kernel/kexec_elf.c
@@ -0,0 +1,430 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Load ELF vmlinux file for the kexec_file_load syscall.
+ *
+ * Copyright (C) 2004 Adam Litke (agl@us.ibm.com)
+ * Copyright (C) 2004 IBM Corp.
+ * Copyright (C) 2005 R Sharada (sharada@in.ibm.com)
+ * Copyright (C) 2006 Mohan Kumar M (mohan@in.ibm.com)
+ * Copyright (C) 2016 IBM Corporation
+ *
+ * Based on kexec-tools' kexec-elf-exec.c and kexec-elf-ppc64.c.
+ * Heavily modified for the kernel by
+ * Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com>.
+ */
+
+#define pr_fmt(fmt) "kexec_elf: " fmt
+
+#include <linux/elf.h>
+#include <linux/kexec.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+static inline bool elf_is_elf_file(const struct elfhdr *ehdr)
+{
+ return memcmp(ehdr->e_ident, ELFMAG, SELFMAG) == 0;
+}
+
+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;
+}
+
+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;
+}
+
+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;
+}
+
+/**
+ * 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 -ENOEXEC;
+ } 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_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);
+
+ switch (ehdr->e_ident[EI_CLASS]) {
+ case ELFCLASS64:
+ ehdr->e_entry = elf64_to_cpu(ehdr, buf_ehdr->e_entry);
+ ehdr->e_phoff = elf64_to_cpu(ehdr, buf_ehdr->e_phoff);
+ ehdr->e_shoff = elf64_to_cpu(ehdr, buf_ehdr->e_shoff);
+ break;
+
+ case ELFCLASS32:
+ ehdr->e_entry = elf32_to_cpu(ehdr, buf_ehdr->e_entry);
+ ehdr->e_phoff = elf32_to_cpu(ehdr, buf_ehdr->e_phoff);
+ ehdr->e_shoff = elf32_to_cpu(ehdr, buf_ehdr->e_shoff);
+ break;
+
+ default:
+ pr_debug("Unknown ELF class.\n");
+ return -EINVAL;
+ }
+
+ 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 kexec_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 struct elfhdr *ehdr = elf_info->ehdr;
+ 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_flags = elf32_to_cpu(elf_info->ehdr, buf_phdr->p_flags);
+
+ switch (ehdr->e_ident[EI_CLASS]) {
+ case ELFCLASS64:
+ phdr->p_offset = elf64_to_cpu(ehdr, buf_phdr->p_offset);
+ phdr->p_paddr = elf64_to_cpu(ehdr, buf_phdr->p_paddr);
+ phdr->p_vaddr = elf64_to_cpu(ehdr, buf_phdr->p_vaddr);
+ phdr->p_filesz = elf64_to_cpu(ehdr, buf_phdr->p_filesz);
+ phdr->p_memsz = elf64_to_cpu(ehdr, buf_phdr->p_memsz);
+ phdr->p_align = elf64_to_cpu(ehdr, buf_phdr->p_align);
+ break;
+
+ case ELFCLASS32:
+ phdr->p_offset = elf32_to_cpu(ehdr, buf_phdr->p_offset);
+ phdr->p_paddr = elf32_to_cpu(ehdr, buf_phdr->p_paddr);
+ phdr->p_vaddr = elf32_to_cpu(ehdr, buf_phdr->p_vaddr);
+ phdr->p_filesz = elf32_to_cpu(ehdr, buf_phdr->p_filesz);
+ phdr->p_memsz = elf32_to_cpu(ehdr, buf_phdr->p_memsz);
+ phdr->p_align = elf32_to_cpu(ehdr, buf_phdr->p_align);
+ break;
+
+ default:
+ pr_debug("Unknown ELF class.\n");
+ return -EINVAL;
+ }
+
+ 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 kexec_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_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
+ * kexec_free_elf_info(elf_info) to free the memory allocated for the section
+ * and program headers.
+ */
+static int elf_read_from_buffer(const char *buf, size_t len,
+ struct elfhdr *ehdr,
+ struct kexec_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;
+ }
+ return 0;
+}
+
+/**
+ * kexec_free_elf_info - free memory allocated by elf_read_from_buffer
+ */
+void kexec_free_elf_info(struct kexec_elf_info *elf_info)
+{
+ kfree(elf_info->proghdrs);
+ memset(elf_info, 0, sizeof(*elf_info));
+}
+/**
+ * kexec_build_elf_info - read ELF executable and check that we can use it
+ */
+int kexec_build_elf_info(const char *buf, size_t len, struct elfhdr *ehdr,
+ struct kexec_elf_info *elf_info)
+{
+ int i;
+ int ret;
+
+ ret = elf_read_from_buffer(buf, len, ehdr, elf_info);
+ if (ret)
+ return ret;
+
+ /* Big endian vmlinux has type ET_DYN. */
+ if (ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN) {
+ pr_err("Not an ELF executable.\n");
+ goto error;
+ } else if (!elf_info->proghdrs) {
+ pr_err("No ELF program header.\n");
+ goto error;
+ }
+
+ for (i = 0; i < ehdr->e_phnum; i++) {
+ /*
+ * Kexec does not support loading interpreters.
+ * In addition this check keeps us from attempting
+ * to kexec ordinay executables.
+ */
+ if (elf_info->proghdrs[i].p_type == PT_INTERP) {
+ pr_err("Requires an ELF interpreter.\n");
+ goto error;
+ }
+ }
+
+ return 0;
+error:
+ kexec_free_elf_info(elf_info);
+ return -ENOEXEC;
+}
+
+
+int kexec_elf_probe(const char *buf, unsigned long len)
+{
+ struct elfhdr ehdr;
+ struct kexec_elf_info elf_info;
+ int ret;
+
+ ret = kexec_build_elf_info(buf, len, &ehdr, &elf_info);
+ if (ret)
+ return ret;
+
+ kexec_free_elf_info(&elf_info);
+
+ return elf_check_arch(&ehdr) ? 0 : -ENOEXEC;
+}
+
+/**
+ * kexec_elf_load - load ELF executable image
+ * @lowest_load_addr: On return, will be the address where the first PT_LOAD
+ * section will be loaded in memory.
+ *
+ * Return:
+ * 0 on success, negative value on failure.
+ */
+int kexec_elf_load(struct kimage *image, struct elfhdr *ehdr,
+ struct kexec_elf_info *elf_info,
+ struct kexec_buf *kbuf,
+ unsigned long *lowest_load_addr)
+{
+ unsigned long lowest_addr = UINT_MAX;
+ int ret;
+ size_t i;
+
+ /* Read in the PT_LOAD segments. */
+ for (i = 0; i < ehdr->e_phnum; i++) {
+ unsigned long load_addr;
+ size_t size;
+ const struct elf_phdr *phdr;
+
+ phdr = &elf_info->proghdrs[i];
+ if (phdr->p_type != PT_LOAD)
+ continue;
+
+ size = phdr->p_filesz;
+ if (size > phdr->p_memsz)
+ size = phdr->p_memsz;
+
+ kbuf->buffer = (void *) elf_info->buffer + phdr->p_offset;
+ kbuf->bufsz = size;
+ kbuf->memsz = phdr->p_memsz;
+ kbuf->buf_align = phdr->p_align;
+ kbuf->buf_min = phdr->p_paddr;
+ kbuf->mem = KEXEC_BUF_MEM_UNKNOWN;
+ ret = kexec_add_buffer(kbuf);
+ if (ret)
+ goto out;
+ load_addr = kbuf->mem;
+
+ if (load_addr < lowest_addr)
+ lowest_addr = load_addr;
+ }
+
+ *lowest_load_addr = lowest_addr;
+ ret = 0;
+ out:
+ return ret;
+}