Merge branch 'parisc-5.4-1' of git://git.kernel.org/pub/scm/linux/kernel/git/deller/parisc-linux
Pull parisc updates from Helge Deller: - Make the powerpc implementation to read elf files available as a public kexec interface so it can be re-used on other architectures (Sven) - Implement kexec on parisc (Sven) - Add kprobes on ftrace on parisc (Sven) - Fix kernel crash with HSC-PCI cards based on card-mode Dino - Add assembly implementations for memset, strlen, strcpy, strncpy and strcat - Some cleanups, documentation updates, warning fixes, ... * 'parisc-5.4-1' of git://git.kernel.org/pub/scm/linux/kernel/git/deller/parisc-linux: (25 commits) parisc: Have git ignore generated real2.S and firmware.c parisc: Disable HP HSC-PCI Cards to prevent kernel crash parisc: add support for kexec_file_load() syscall parisc: wire up kexec_file_load syscall parisc: add kexec syscall support parisc: add __pdc_cpu_rendezvous() kprobes/parisc: remove arch_kprobe_on_func_entry() kexec_elf: support 32 bit ELF files kexec_elf: remove unused variable in kexec_elf_load() kexec_elf: remove Elf_Rel macro kexec_elf: remove PURGATORY_STACK_SIZE kexec_elf: remove parsing of section headers kexec_elf: change order of elf_*_to_cpu() functions kexec: add KEXEC_ELF parisc: Save some bytes in dino driver parisc: Drop comments which are already in pci.h parisc: Convert eisa_enumerator to use pr_cont() parisc: Avoid warning when loading hppb driver parisc: speed up flush_tlb_all_local with qemu parisc: Add ALTERNATIVE_CODE() and ALT_COND_RUN_ON_QEMU ...
This commit is contained in:
commit
d0a16fe934
|
@ -21,7 +21,7 @@
|
||||||
| nds32: | TODO |
|
| nds32: | TODO |
|
||||||
| nios2: | TODO |
|
| nios2: | TODO |
|
||||||
| openrisc: | TODO |
|
| openrisc: | TODO |
|
||||||
| parisc: | TODO |
|
| parisc: | ok |
|
||||||
| powerpc: | ok |
|
| powerpc: | ok |
|
||||||
| riscv: | TODO |
|
| riscv: | TODO |
|
||||||
| s390: | ok |
|
| s390: | ok |
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
| nds32: | TODO |
|
| nds32: | TODO |
|
||||||
| nios2: | TODO |
|
| nios2: | TODO |
|
||||||
| openrisc: | TODO |
|
| openrisc: | TODO |
|
||||||
| parisc: | TODO |
|
| parisc: | ok |
|
||||||
| powerpc: | ok |
|
| powerpc: | ok |
|
||||||
| riscv: | TODO |
|
| riscv: | TODO |
|
||||||
| s390: | TODO |
|
| s390: | TODO |
|
||||||
|
|
|
@ -18,6 +18,9 @@ config KEXEC_CORE
|
||||||
select CRASH_CORE
|
select CRASH_CORE
|
||||||
bool
|
bool
|
||||||
|
|
||||||
|
config KEXEC_ELF
|
||||||
|
bool
|
||||||
|
|
||||||
config HAVE_IMA_KEXEC
|
config HAVE_IMA_KEXEC
|
||||||
bool
|
bool
|
||||||
|
|
||||||
|
|
|
@ -61,6 +61,8 @@ config PARISC
|
||||||
select HAVE_KRETPROBES
|
select HAVE_KRETPROBES
|
||||||
select HAVE_DYNAMIC_FTRACE if $(cc-option,-fpatchable-function-entry=1,1)
|
select HAVE_DYNAMIC_FTRACE if $(cc-option,-fpatchable-function-entry=1,1)
|
||||||
select HAVE_FTRACE_MCOUNT_RECORD if HAVE_DYNAMIC_FTRACE
|
select HAVE_FTRACE_MCOUNT_RECORD if HAVE_DYNAMIC_FTRACE
|
||||||
|
select HAVE_KPROBES_ON_FTRACE
|
||||||
|
select HAVE_DYNAMIC_FTRACE_WITH_REGS
|
||||||
|
|
||||||
help
|
help
|
||||||
The PA-RISC microprocessor is designed by Hewlett-Packard and used
|
The PA-RISC microprocessor is designed by Hewlett-Packard and used
|
||||||
|
@ -344,6 +346,29 @@ config NR_CPUS
|
||||||
depends on SMP
|
depends on SMP
|
||||||
default "4"
|
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
|
endmenu
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
firmware.c
|
||||||
|
real2.S
|
||||||
sizes.h
|
sizes.h
|
||||||
vmlinux
|
vmlinux
|
||||||
vmlinux.lds
|
vmlinux.lds
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
#define ALT_COND_NO_ICACHE 0x04 /* if system has no i-cache */
|
#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_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_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_PxTLB 0x02 /* modify pdtlb, pitlb */
|
||||||
#define INSN_NOP 0x08000240 /* nop */
|
#define INSN_NOP 0x08000240 /* nop */
|
||||||
|
@ -21,7 +22,7 @@
|
||||||
|
|
||||||
struct alt_instr {
|
struct alt_instr {
|
||||||
s32 orig_offset; /* offset to original instructions */
|
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 cond; /* see ALT_COND_XXX */
|
||||||
u32 replacement; /* replacement instruction or code */
|
u32 replacement; /* replacement instruction or code */
|
||||||
};
|
};
|
||||||
|
@ -40,12 +41,20 @@ void apply_alternatives(struct alt_instr *start, struct alt_instr *end,
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
|
/* to replace one single instructions by a new instruction */
|
||||||
#define ALTERNATIVE(from, to, cond, replacement)\
|
#define ALTERNATIVE(from, to, cond, replacement)\
|
||||||
.section .altinstructions, "aw" ! \
|
.section .altinstructions, "aw" ! \
|
||||||
.word (from - .), (to - from)/4 ! \
|
.word (from - .), (to - from)/4 ! \
|
||||||
.word cond, replacement ! \
|
.word cond, replacement ! \
|
||||||
.previous
|
.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 /* __ASSEMBLY__ */
|
||||||
|
|
||||||
#endif /* __ASM_PARISC_ALTERNATIVE_H */
|
#endif /* __ASM_PARISC_ALTERNATIVE_H */
|
||||||
|
|
|
@ -30,6 +30,7 @@
|
||||||
enum fixed_addresses {
|
enum fixed_addresses {
|
||||||
/* Support writing RO kernel text via kprobes, jump labels, etc. */
|
/* Support writing RO kernel text via kprobes, jump labels, etc. */
|
||||||
FIX_TEXT_POKE0,
|
FIX_TEXT_POKE0,
|
||||||
|
FIX_TEXT_KEXEC,
|
||||||
FIX_BITMAP_COUNT
|
FIX_BITMAP_COUNT
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@ extern void mcount(void);
|
||||||
#define MCOUNT_ADDR ((unsigned long)mcount)
|
#define MCOUNT_ADDR ((unsigned long)mcount)
|
||||||
#define MCOUNT_INSN_SIZE 4
|
#define MCOUNT_INSN_SIZE 4
|
||||||
#define CC_USING_NOP_MCOUNT
|
#define CC_USING_NOP_MCOUNT
|
||||||
|
#define ARCH_SUPPORTS_FTRACE_OPS 1
|
||||||
extern unsigned long sys_call_table[];
|
extern unsigned long sys_call_table[];
|
||||||
|
|
||||||
extern unsigned long return_address(unsigned int);
|
extern unsigned long return_address(unsigned int);
|
||||||
|
|
|
@ -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 */
|
|
@ -91,6 +91,7 @@ int pdc_sti_call(unsigned long func, unsigned long flags,
|
||||||
unsigned long inptr, unsigned long outputr,
|
unsigned long inptr, unsigned long outputr,
|
||||||
unsigned long glob_cfg);
|
unsigned long glob_cfg);
|
||||||
|
|
||||||
|
int __pdc_cpu_rendezvous(void);
|
||||||
static inline char * os_id_to_string(u16 os_id) {
|
static inline char * os_id_to_string(u16 os_id) {
|
||||||
switch(os_id) {
|
switch(os_id) {
|
||||||
case OS_ID_NONE: return "No OS";
|
case OS_ID_NONE: return "No OS";
|
||||||
|
|
|
@ -8,4 +8,19 @@ extern void * memset(void *, int, size_t);
|
||||||
#define __HAVE_ARCH_MEMCPY
|
#define __HAVE_ARCH_MEMCPY
|
||||||
void * memcpy(void * dest,const void *src,size_t count);
|
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
|
#endif
|
||||||
|
|
|
@ -37,3 +37,5 @@ obj-$(CONFIG_FUNCTION_GRAPH_TRACER) += ftrace.o
|
||||||
obj-$(CONFIG_JUMP_LABEL) += jump_label.o
|
obj-$(CONFIG_JUMP_LABEL) += jump_label.o
|
||||||
obj-$(CONFIG_KGDB) += kgdb.o
|
obj-$(CONFIG_KGDB) += kgdb.o
|
||||||
obj-$(CONFIG_KPROBES) += kprobes.o
|
obj-$(CONFIG_KPROBES) += kprobes.o
|
||||||
|
obj-$(CONFIG_KEXEC) += kexec.o relocate_kernel.o
|
||||||
|
obj-$(CONFIG_KEXEC_FILE) += kexec_file.o
|
||||||
|
|
|
@ -28,7 +28,8 @@ void __init_or_module apply_alternatives(struct alt_instr *start,
|
||||||
|
|
||||||
for (entry = start; entry < end; entry++, index++) {
|
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);
|
from = (u32 *)((ulong)&entry->orig_offset + entry->orig_offset);
|
||||||
len = entry->len;
|
len = entry->len;
|
||||||
|
@ -49,6 +50,8 @@ void __init_or_module apply_alternatives(struct alt_instr *start,
|
||||||
continue;
|
continue;
|
||||||
if ((cond & ALT_COND_NO_ICACHE) && (cache_info.ic_size != 0))
|
if ((cond & ALT_COND_NO_ICACHE) && (cache_info.ic_size != 0))
|
||||||
continue;
|
continue;
|
||||||
|
if ((cond & ALT_COND_RUN_ON_QEMU) && !running_on_qemu)
|
||||||
|
continue;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If the PDC_MODEL capabilities has Non-coherent IO-PDIR bit
|
* 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)
|
if (replacement == INSN_NOP && len > 1)
|
||||||
replacement = 0xe8000002 + (len-2)*8; /* "b,n .+8" */
|
replacement = 0xe8000002 + (len-2)*8; /* "b,n .+8" */
|
||||||
|
|
||||||
pr_debug("Do %d: Cond 0x%x, Replace %02d instructions @ 0x%px with 0x%08x\n",
|
pr_debug("ALTERNATIVE %3d: Cond %2x, Replace %2d instructions to 0x%08x @ 0x%px (%pS)\n",
|
||||||
index, cond, len, from, replacement);
|
index, cond, len, replacement, from, from);
|
||||||
|
|
||||||
/* Replace instruction */
|
if (len < 0) {
|
||||||
*from = replacement;
|
/* 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++;
|
applied++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1996,6 +1996,7 @@ _mcount:
|
||||||
* calling mcount(), and 2 instructions for ftrace_stub(). That way we
|
* calling mcount(), and 2 instructions for ftrace_stub(). That way we
|
||||||
* have all on one L1 cacheline.
|
* have all on one L1 cacheline.
|
||||||
*/
|
*/
|
||||||
|
ldi 0, %arg3
|
||||||
b ftrace_function_trampoline
|
b ftrace_function_trampoline
|
||||||
copy %r3, %arg2 /* caller original %sp */
|
copy %r3, %arg2 /* caller original %sp */
|
||||||
ftrace_stub:
|
ftrace_stub:
|
||||||
|
@ -2048,6 +2049,7 @@ ftrace_caller:
|
||||||
LDREG 0(%r3), %r25
|
LDREG 0(%r3), %r25
|
||||||
copy %rp, %r26
|
copy %rp, %r26
|
||||||
ldo -8(%r25), %r25
|
ldo -8(%r25), %r25
|
||||||
|
ldi 0, %r23 /* no pt_regs */
|
||||||
b,l ftrace_function_trampoline, %rp
|
b,l ftrace_function_trampoline, %rp
|
||||||
copy %r3, %r24
|
copy %r3, %r24
|
||||||
|
|
||||||
|
@ -2075,6 +2077,103 @@ ftrace_caller:
|
||||||
|
|
||||||
ENDPROC_CFI(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
|
#endif
|
||||||
|
|
||||||
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
|
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
|
||||||
|
|
|
@ -311,6 +311,19 @@ int pdc_chassis_disp(unsigned long disp)
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
* pdc_chassis_warn - Fetches chassis warnings
|
||||||
* @retval: -1 on error, 0 on success
|
* @retval: -1 on error, 0 on success
|
||||||
|
|
|
@ -13,6 +13,8 @@
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
#include <linux/ftrace.h>
|
#include <linux/ftrace.h>
|
||||||
#include <linux/uaccess.h>
|
#include <linux/uaccess.h>
|
||||||
|
#include <linux/kprobes.h>
|
||||||
|
#include <linux/ptrace.h>
|
||||||
|
|
||||||
#include <asm/assembly.h>
|
#include <asm/assembly.h>
|
||||||
#include <asm/sections.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,
|
void notrace __hot ftrace_function_trampoline(unsigned long parent,
|
||||||
unsigned long self_addr,
|
unsigned long self_addr,
|
||||||
unsigned long org_sp_gr3)
|
unsigned long org_sp_gr3,
|
||||||
|
struct pt_regs *regs)
|
||||||
{
|
{
|
||||||
#ifndef CONFIG_DYNAMIC_FTRACE
|
#ifndef CONFIG_DYNAMIC_FTRACE
|
||||||
extern ftrace_func_t ftrace_trace_function;
|
extern ftrace_func_t ftrace_trace_function;
|
||||||
#endif
|
#endif
|
||||||
if (ftrace_trace_function != ftrace_stub)
|
extern struct ftrace_ops *function_trace_op;
|
||||||
ftrace_trace_function(self_addr, parent, NULL, NULL);
|
|
||||||
|
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
|
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
|
||||||
if (ftrace_graph_return != (trace_func_graph_ret_t) ftrace_stub ||
|
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;
|
unsigned long *parent_rp;
|
||||||
|
|
||||||
/* calculate pointer to %rp in stack */
|
/* calculate pointer to %rp in stack */
|
||||||
|
@ -96,6 +103,12 @@ int ftrace_update_ftrace_func(ftrace_func_t func)
|
||||||
return 0;
|
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)
|
unsigned long ftrace_call_adjust(unsigned long addr)
|
||||||
{
|
{
|
||||||
return addr+(FTRACE_PATCHABLE_FUNCTION_SIZE-1)*4;
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
#endif
|
#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
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
|
@ -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
|
||||||
|
};
|
|
@ -281,10 +281,6 @@ int __kprobes arch_trampoline_kprobe(struct kprobe *p)
|
||||||
{
|
{
|
||||||
return p->addr == trampoline_p.addr;
|
return p->addr == trampoline_p.addr;
|
||||||
}
|
}
|
||||||
bool arch_kprobe_on_func_entry(unsigned long offset)
|
|
||||||
{
|
|
||||||
return !offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
int __init arch_init_kprobes(void)
|
int __init arch_init_kprobes(void)
|
||||||
{
|
{
|
||||||
|
|
|
@ -174,6 +174,15 @@ fdtdone:
|
||||||
|
|
||||||
2: bv %r0(%r2)
|
2: bv %r0(%r2)
|
||||||
nop
|
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)
|
ENDPROC_CFI(flush_tlb_all_local)
|
||||||
|
|
||||||
.import cache_info,data
|
.import cache_info,data
|
||||||
|
|
|
@ -17,6 +17,10 @@
|
||||||
|
|
||||||
#include <linux/string.h>
|
#include <linux/string.h>
|
||||||
EXPORT_SYMBOL(memset);
|
EXPORT_SYMBOL(memset);
|
||||||
|
EXPORT_SYMBOL(strlen);
|
||||||
|
EXPORT_SYMBOL(strcpy);
|
||||||
|
EXPORT_SYMBOL(strncpy);
|
||||||
|
EXPORT_SYMBOL(strcat);
|
||||||
|
|
||||||
#include <linux/atomic.h>
|
#include <linux/atomic.h>
|
||||||
EXPORT_SYMBOL(__xchg8);
|
EXPORT_SYMBOL(__xchg8);
|
||||||
|
|
|
@ -34,17 +34,6 @@
|
||||||
#define DBG_RES(x...)
|
#define DBG_RES(x...)
|
||||||
#endif
|
#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_port_ops *pci_port __ro_after_init;
|
||||||
struct pci_bios_ops *pci_bios __ro_after_init;
|
struct pci_bios_ops *pci_bios __ro_after_init;
|
||||||
|
|
||||||
|
|
|
@ -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
|
|
@ -109,6 +109,7 @@ halt_processor(void)
|
||||||
/* REVISIT : does PM *know* this CPU isn't available? */
|
/* REVISIT : does PM *know* this CPU isn't available? */
|
||||||
set_cpu_online(smp_processor_id(), false);
|
set_cpu_online(smp_processor_id(), false);
|
||||||
local_irq_disable();
|
local_irq_disable();
|
||||||
|
__pdc_cpu_rendezvous();
|
||||||
for (;;)
|
for (;;)
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
|
@ -399,7 +399,8 @@
|
||||||
352 common pkey_alloc sys_pkey_alloc
|
352 common pkey_alloc sys_pkey_alloc
|
||||||
353 common pkey_free sys_pkey_free
|
353 common pkey_free sys_pkey_free
|
||||||
354 common rseq sys_rseq
|
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
|
403 32 clock_gettime64 sys_clock_gettime sys_clock_gettime
|
||||||
404 32 clock_settime64 sys_clock_settime sys_clock_settime
|
404 32 clock_settime64 sys_clock_settime sys_clock_settime
|
||||||
405 32 clock_adjtime64 sys_clock_adjtime sys_clock_adjtime
|
405 32 clock_adjtime64 sys_clock_adjtime sys_clock_adjtime
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
#include <linux/bug.h>
|
#include <linux/bug.h>
|
||||||
#include <linux/ratelimit.h>
|
#include <linux/ratelimit.h>
|
||||||
#include <linux/uaccess.h>
|
#include <linux/uaccess.h>
|
||||||
|
#include <linux/kdebug.h>
|
||||||
|
|
||||||
#include <asm/assembly.h>
|
#include <asm/assembly.h>
|
||||||
#include <asm/io.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);
|
static DEFINE_SPINLOCK(terminate_lock);
|
||||||
|
|
||||||
|
(void)notify_die(DIE_OOPS, msg, regs, 0, code, SIGTRAP);
|
||||||
bust_spinlocks(1);
|
bust_spinlocks(1);
|
||||||
|
|
||||||
set_eiem(0);
|
set_eiem(0);
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
# Makefile for parisc-specific library files
|
# Makefile for parisc-specific library files
|
||||||
#
|
#
|
||||||
|
|
||||||
lib-y := lusercopy.o bitops.o checksum.o io.o memset.o memcpy.o \
|
lib-y := lusercopy.o bitops.o checksum.o io.o memcpy.o \
|
||||||
ucmpdi2.o delay.o
|
ucmpdi2.o delay.o string.o
|
||||||
|
|
||||||
obj-y := iomap.o
|
obj-y := iomap.o
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
|
@ -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
|
|
@ -511,6 +511,7 @@ config KEXEC_FILE
|
||||||
select KEXEC_CORE
|
select KEXEC_CORE
|
||||||
select HAVE_IMA_KEXEC
|
select HAVE_IMA_KEXEC
|
||||||
select BUILD_BIN2C
|
select BUILD_BIN2C
|
||||||
|
select KEXEC_ELF
|
||||||
depends on PPC64
|
depends on PPC64
|
||||||
depends on CRYPTO=y
|
depends on CRYPTO=y
|
||||||
depends on CRYPTO_SHA256=y
|
depends on CRYPTO_SHA256=y
|
||||||
|
|
|
@ -23,541 +23,6 @@
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/types.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,
|
static void *elf64_load(struct kimage *image, char *kernel_buf,
|
||||||
unsigned long kernel_len, char *initrd,
|
unsigned long kernel_len, char *initrd,
|
||||||
unsigned long initrd_len, char *cmdline,
|
unsigned long initrd_len, char *cmdline,
|
||||||
|
@ -570,18 +35,18 @@ static void *elf64_load(struct kimage *image, char *kernel_buf,
|
||||||
void *fdt;
|
void *fdt;
|
||||||
const void *slave_code;
|
const void *slave_code;
|
||||||
struct elfhdr ehdr;
|
struct elfhdr ehdr;
|
||||||
struct elf_info elf_info;
|
struct kexec_elf_info elf_info;
|
||||||
struct kexec_buf kbuf = { .image = image, .buf_min = 0,
|
struct kexec_buf kbuf = { .image = image, .buf_min = 0,
|
||||||
.buf_max = ppc64_rma_size };
|
.buf_max = ppc64_rma_size };
|
||||||
struct kexec_buf pbuf = { .image = image, .buf_min = 0,
|
struct kexec_buf pbuf = { .image = image, .buf_min = 0,
|
||||||
.buf_max = ppc64_rma_size, .top_down = true,
|
.buf_max = ppc64_rma_size, .top_down = true,
|
||||||
.mem = KEXEC_BUF_MEM_UNKNOWN };
|
.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)
|
if (ret)
|
||||||
goto out;
|
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)
|
if (ret)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
|
@ -648,13 +113,13 @@ static void *elf64_load(struct kimage *image, char *kernel_buf,
|
||||||
pr_err("Error setting up the purgatory.\n");
|
pr_err("Error setting up the purgatory.\n");
|
||||||
|
|
||||||
out:
|
out:
|
||||||
elf_free_info(&elf_info);
|
kexec_free_elf_info(&elf_info);
|
||||||
|
|
||||||
/* Make kimage_file_post_load_cleanup free the fdt buffer for us. */
|
/* Make kimage_file_post_load_cleanup free the fdt buffer for us. */
|
||||||
return ret ? ERR_PTR(ret) : fdt;
|
return ret ? ERR_PTR(ret) : fdt;
|
||||||
}
|
}
|
||||||
|
|
||||||
const struct kexec_file_ops kexec_elf64_ops = {
|
const struct kexec_file_ops kexec_elf64_ops = {
|
||||||
.probe = elf64_probe,
|
.probe = kexec_elf_probe,
|
||||||
.load = elf64_load,
|
.load = elf64_load,
|
||||||
};
|
};
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
** (c) Copyright 1999 SuSE GmbH
|
** (c) Copyright 1999 SuSE GmbH
|
||||||
** (c) Copyright 1999,2000 Hewlett-Packard Company
|
** (c) Copyright 1999,2000 Hewlett-Packard Company
|
||||||
** (c) Copyright 2000 Grant Grundler
|
** (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)
|
** 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);
|
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
|
* 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 );
|
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
|
static void __init
|
||||||
dino_bios_init(void)
|
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_RAVEN_BADPAGE 0x01003000UL
|
||||||
#define CUJO_FIREHAWK_BADPAGE 0x01607000UL
|
#define CUJO_FIREHAWK_BADPAGE 0x01607000UL
|
||||||
|
|
||||||
static const char *dino_vers[] = {
|
static const char dino_vers[][4] = {
|
||||||
"2.0",
|
"2.0",
|
||||||
"2.1",
|
"2.1",
|
||||||
"3.0",
|
"3.0",
|
||||||
"3.1"
|
"3.1"
|
||||||
};
|
};
|
||||||
|
|
||||||
static const char *cujo_vers[] = {
|
static const char cujo_vers[][4] = {
|
||||||
"1.0",
|
"1.0",
|
||||||
"2.0"
|
"2.0"
|
||||||
};
|
};
|
||||||
|
|
|
@ -93,7 +93,7 @@ static int configure_memory(const unsigned char *buf,
|
||||||
res->start = mem_parent->start + get_24(buf+len+2);
|
res->start = mem_parent->start + get_24(buf+len+2);
|
||||||
res->end = res->start + get_16(buf+len+5)*1024;
|
res->end = res->start + get_16(buf+len+5)*1024;
|
||||||
res->flags = IORESOURCE_MEM;
|
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);
|
result = request_resource(mem_parent, res);
|
||||||
if (result < 0) {
|
if (result < 0) {
|
||||||
printk(KERN_ERR "EISA Enumerator: failed to claim EISA Bus address space!\n");
|
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++) {
|
for (i=0;i<HPEE_IRQ_MAX_ENT;i++) {
|
||||||
c = get_8(buf+len);
|
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) {
|
if (c & HPEE_IRQ_TRIG_LEVEL) {
|
||||||
eisa_make_irq_level(c & HPEE_IRQ_CHANNEL_MASK);
|
eisa_make_irq_level(c & HPEE_IRQ_CHANNEL_MASK);
|
||||||
} else {
|
} else {
|
||||||
|
@ -153,7 +153,7 @@ static int configure_dma(const unsigned char *buf)
|
||||||
|
|
||||||
for (i=0;i<HPEE_DMA_MAX_ENT;i++) {
|
for (i=0;i<HPEE_DMA_MAX_ENT;i++) {
|
||||||
c = get_8(buf+len);
|
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 ? */
|
/* fixme: maybe initialize the dma channel withthe timing ? */
|
||||||
len+=2;
|
len+=2;
|
||||||
if (!(c & HPEE_DMA_MORE)) {
|
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->start = get_16(buf+len+1);
|
||||||
res->end = get_16(buf+len+1)+(c&HPEE_PORT_SIZE_MASK)+1;
|
res->end = get_16(buf+len+1)+(c&HPEE_PORT_SIZE_MASK)+1;
|
||||||
res->flags = IORESOURCE_IO;
|
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);
|
result = request_resource(io_parent, res);
|
||||||
if (result < 0) {
|
if (result < 0) {
|
||||||
printk(KERN_ERR "EISA Enumerator: failed to claim EISA Bus address space!\n");
|
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;
|
pos = p0 + function_len;
|
||||||
}
|
}
|
||||||
printk("\n");
|
pr_cont("\n");
|
||||||
if (!id_string_used) {
|
if (!id_string_used) {
|
||||||
kfree(board);
|
kfree(board);
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,8 +61,6 @@ static int __init hppb_probe(struct parisc_device *dev)
|
||||||
}
|
}
|
||||||
card = card->next;
|
card = card->next;
|
||||||
}
|
}
|
||||||
printk(KERN_INFO "Found GeckoBoa at 0x%llx\n",
|
|
||||||
(unsigned long long) dev->hpa.start);
|
|
||||||
|
|
||||||
card->hpa = dev->hpa.start;
|
card->hpa = dev->hpa.start;
|
||||||
card->mmio_region.name = "HP-PB Bus";
|
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;
|
card->mmio_region.end = gsc_readl(dev->hpa.start + IO_IO_HIGH) - 1;
|
||||||
|
|
||||||
status = ccio_request_resource(dev, &card->mmio_region);
|
status = ccio_request_resource(dev, &card->mmio_region);
|
||||||
if(status < 0) {
|
|
||||||
printk(KERN_ERR "%s: failed to claim HP-PB bus space (%pR)\n",
|
pr_info("Found GeckoBoa at %pap, bus space %pR,%s claimed.\n",
|
||||||
__FILE__, &card->mmio_region);
|
&dev->hpa.start,
|
||||||
}
|
&card->mmio_region,
|
||||||
|
(status < 0) ? " not":"" );
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -216,6 +216,29 @@ extern int crash_prepare_elf64_headers(struct crash_mem *mem, int kernel_map,
|
||||||
void **addr, unsigned long *sz);
|
void **addr, unsigned long *sz);
|
||||||
#endif /* CONFIG_KEXEC_FILE */
|
#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 {
|
struct kimage {
|
||||||
kimage_entry_t head;
|
kimage_entry_t head;
|
||||||
kimage_entry_t *entry;
|
kimage_entry_t *entry;
|
||||||
|
|
|
@ -31,6 +31,7 @@
|
||||||
#define KEXEC_ARCH_DEFAULT ( 0 << 16)
|
#define KEXEC_ARCH_DEFAULT ( 0 << 16)
|
||||||
#define KEXEC_ARCH_386 ( 3 << 16)
|
#define KEXEC_ARCH_386 ( 3 << 16)
|
||||||
#define KEXEC_ARCH_68K ( 4 << 16)
|
#define KEXEC_ARCH_68K ( 4 << 16)
|
||||||
|
#define KEXEC_ARCH_PARISC (15 << 16)
|
||||||
#define KEXEC_ARCH_X86_64 (62 << 16)
|
#define KEXEC_ARCH_X86_64 (62 << 16)
|
||||||
#define KEXEC_ARCH_PPC (20 << 16)
|
#define KEXEC_ARCH_PPC (20 << 16)
|
||||||
#define KEXEC_ARCH_PPC64 (21 << 16)
|
#define KEXEC_ARCH_PPC64 (21 << 16)
|
||||||
|
|
|
@ -64,6 +64,7 @@ obj-$(CONFIG_CRASH_CORE) += crash_core.o
|
||||||
obj-$(CONFIG_KEXEC_CORE) += kexec_core.o
|
obj-$(CONFIG_KEXEC_CORE) += kexec_core.o
|
||||||
obj-$(CONFIG_KEXEC) += kexec.o
|
obj-$(CONFIG_KEXEC) += kexec.o
|
||||||
obj-$(CONFIG_KEXEC_FILE) += kexec_file.o
|
obj-$(CONFIG_KEXEC_FILE) += kexec_file.o
|
||||||
|
obj-$(CONFIG_KEXEC_ELF) += kexec_elf.o
|
||||||
obj-$(CONFIG_BACKTRACE_SELF_TEST) += backtracetest.o
|
obj-$(CONFIG_BACKTRACE_SELF_TEST) += backtracetest.o
|
||||||
obj-$(CONFIG_COMPAT) += compat.o
|
obj-$(CONFIG_COMPAT) += compat.o
|
||||||
obj-$(CONFIG_CGROUPS) += cgroup/
|
obj-$(CONFIG_CGROUPS) += cgroup/
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
Loading…
Reference in New Issue