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:
Linus Torvalds 2019-09-16 15:38:31 -07:00
commit d0a16fe934
38 changed files with 1288 additions and 675 deletions

View File

@ -21,7 +21,7 @@
| nds32: | TODO |
| nios2: | TODO |
| openrisc: | TODO |
| parisc: | TODO |
| parisc: | ok |
| powerpc: | ok |
| riscv: | TODO |
| s390: | ok |

View File

@ -21,7 +21,7 @@
| nds32: | TODO |
| nios2: | TODO |
| openrisc: | TODO |
| parisc: | TODO |
| parisc: | ok |
| powerpc: | ok |
| riscv: | TODO |
| s390: | TODO |

View File

@ -18,6 +18,9 @@ config KEXEC_CORE
select CRASH_CORE
bool
config KEXEC_ELF
bool
config HAVE_IMA_KEXEC
bool

View File

@ -61,6 +61,8 @@ config PARISC
select HAVE_KRETPROBES
select HAVE_DYNAMIC_FTRACE if $(cc-option,-fpatchable-function-entry=1,1)
select HAVE_FTRACE_MCOUNT_RECORD if HAVE_DYNAMIC_FTRACE
select HAVE_KPROBES_ON_FTRACE
select HAVE_DYNAMIC_FTRACE_WITH_REGS
help
The PA-RISC microprocessor is designed by Hewlett-Packard and used
@ -344,6 +346,29 @@ config NR_CPUS
depends on SMP
default "4"
config KEXEC
bool "Kexec system call"
select KEXEC_CORE
help
kexec is a system call that implements the ability to shutdown your
current kernel, and to start another kernel. It is like a reboot
but it is independent of the system firmware. And like a reboot
you can start any kernel with it, not just Linux.
It is an ongoing process to be certain the hardware in a machine
shutdown, so do not be surprised if this code does not
initially work for you.
config KEXEC_FILE
bool "kexec file based system call"
select KEXEC_CORE
select KEXEC_ELF
help
This enables the kexec_file_load() System call. This is
file based and takes file descriptors as system call argument
for kernel and initramfs as opposed to list of segments as
accepted by previous system call.
endmenu

View File

@ -1,3 +1,5 @@
firmware.c
real2.S
sizes.h
vmlinux
vmlinux.lds

View File

@ -8,6 +8,7 @@
#define ALT_COND_NO_ICACHE 0x04 /* if system has no i-cache */
#define ALT_COND_NO_SPLIT_TLB 0x08 /* if split_tlb == 0 */
#define ALT_COND_NO_IOC_FDC 0x10 /* if I/O cache does not need flushes */
#define ALT_COND_RUN_ON_QEMU 0x20 /* if running on QEMU */
#define INSN_PxTLB 0x02 /* modify pdtlb, pitlb */
#define INSN_NOP 0x08000240 /* nop */
@ -21,7 +22,7 @@
struct alt_instr {
s32 orig_offset; /* offset to original instructions */
u32 len; /* end of original instructions */
s32 len; /* end of original instructions */
u32 cond; /* see ALT_COND_XXX */
u32 replacement; /* replacement instruction or code */
};
@ -40,12 +41,20 @@ void apply_alternatives(struct alt_instr *start, struct alt_instr *end,
#else
/* to replace one single instructions by a new instruction */
#define ALTERNATIVE(from, to, cond, replacement)\
.section .altinstructions, "aw" ! \
.word (from - .), (to - from)/4 ! \
.word cond, replacement ! \
.previous
/* to replace multiple instructions by new code */
#define ALTERNATIVE_CODE(from, num_instructions, cond, new_instr_ptr)\
.section .altinstructions, "aw" ! \
.word (from - .), -num_instructions ! \
.word cond, (new_instr_ptr - .) ! \
.previous
#endif /* __ASSEMBLY__ */
#endif /* __ASM_PARISC_ALTERNATIVE_H */

View File

@ -30,6 +30,7 @@
enum fixed_addresses {
/* Support writing RO kernel text via kprobes, jump labels, etc. */
FIX_TEXT_POKE0,
FIX_TEXT_KEXEC,
FIX_BITMAP_COUNT
};

View File

@ -8,6 +8,7 @@ extern void mcount(void);
#define MCOUNT_ADDR ((unsigned long)mcount)
#define MCOUNT_INSN_SIZE 4
#define CC_USING_NOP_MCOUNT
#define ARCH_SUPPORTS_FTRACE_OPS 1
extern unsigned long sys_call_table[];
extern unsigned long return_address(unsigned int);

View File

@ -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 */

View File

@ -91,6 +91,7 @@ int pdc_sti_call(unsigned long func, unsigned long flags,
unsigned long inptr, unsigned long outputr,
unsigned long glob_cfg);
int __pdc_cpu_rendezvous(void);
static inline char * os_id_to_string(u16 os_id) {
switch(os_id) {
case OS_ID_NONE: return "No OS";

View File

@ -8,4 +8,19 @@ extern void * memset(void *, int, size_t);
#define __HAVE_ARCH_MEMCPY
void * memcpy(void * dest,const void *src,size_t count);
#define __HAVE_ARCH_STRLEN
extern size_t strlen(const char *s);
#define __HAVE_ARCH_STRCPY
extern char *strcpy(char *dest, const char *src);
#define __HAVE_ARCH_STRNCPY
extern char *strncpy(char *dest, const char *src, size_t count);
#define __HAVE_ARCH_STRCAT
extern char *strcat(char *dest, const char *src);
#define __HAVE_ARCH_MEMSET
extern void *memset(void *, int, size_t);
#endif

View File

@ -37,3 +37,5 @@ obj-$(CONFIG_FUNCTION_GRAPH_TRACER) += ftrace.o
obj-$(CONFIG_JUMP_LABEL) += jump_label.o
obj-$(CONFIG_KGDB) += kgdb.o
obj-$(CONFIG_KPROBES) += kprobes.o
obj-$(CONFIG_KEXEC) += kexec.o relocate_kernel.o
obj-$(CONFIG_KEXEC_FILE) += kexec_file.o

View File

@ -28,7 +28,8 @@ void __init_or_module apply_alternatives(struct alt_instr *start,
for (entry = start; entry < end; entry++, index++) {
u32 *from, len, cond, replacement;
u32 *from, cond, replacement;
s32 len;
from = (u32 *)((ulong)&entry->orig_offset + entry->orig_offset);
len = entry->len;
@ -49,6 +50,8 @@ void __init_or_module apply_alternatives(struct alt_instr *start,
continue;
if ((cond & ALT_COND_NO_ICACHE) && (cache_info.ic_size != 0))
continue;
if ((cond & ALT_COND_RUN_ON_QEMU) && !running_on_qemu)
continue;
/*
* If the PDC_MODEL capabilities has Non-coherent IO-PDIR bit
@ -74,11 +77,19 @@ void __init_or_module apply_alternatives(struct alt_instr *start,
if (replacement == INSN_NOP && len > 1)
replacement = 0xe8000002 + (len-2)*8; /* "b,n .+8" */
pr_debug("Do %d: Cond 0x%x, Replace %02d instructions @ 0x%px with 0x%08x\n",
index, cond, len, from, replacement);
pr_debug("ALTERNATIVE %3d: Cond %2x, Replace %2d instructions to 0x%08x @ 0x%px (%pS)\n",
index, cond, len, replacement, from, from);
/* Replace instruction */
*from = replacement;
if (len < 0) {
/* Replace multiple instruction by new code */
u32 *source;
len = -len;
source = (u32 *)((ulong)&entry->replacement + entry->replacement);
memcpy(from, source, 4 * len);
} else {
/* Replace by one instruction */
*from = replacement;
}
applied++;
}

View File

@ -1996,6 +1996,7 @@ _mcount:
* calling mcount(), and 2 instructions for ftrace_stub(). That way we
* have all on one L1 cacheline.
*/
ldi 0, %arg3
b ftrace_function_trampoline
copy %r3, %arg2 /* caller original %sp */
ftrace_stub:
@ -2048,6 +2049,7 @@ ftrace_caller:
LDREG 0(%r3), %r25
copy %rp, %r26
ldo -8(%r25), %r25
ldi 0, %r23 /* no pt_regs */
b,l ftrace_function_trampoline, %rp
copy %r3, %r24
@ -2075,6 +2077,103 @@ ftrace_caller:
ENDPROC_CFI(ftrace_caller)
#ifdef CONFIG_HAVE_DYNAMIC_FTRACE_WITH_REGS
ENTRY_CFI(ftrace_regs_caller,caller,frame=FTRACE_FRAME_SIZE+PT_SZ_ALGN,
CALLS,SAVE_RP,SAVE_SP)
ftrace_regs_caller:
.global ftrace_regs_caller
ldo -FTRACE_FRAME_SIZE(%sp), %r1
STREG %rp, -RP_OFFSET(%r1)
copy %sp, %r1
ldo PT_SZ_ALGN(%sp), %sp
STREG %rp, PT_GR2(%r1)
STREG %r3, PT_GR3(%r1)
STREG %r4, PT_GR4(%r1)
STREG %r5, PT_GR5(%r1)
STREG %r6, PT_GR6(%r1)
STREG %r7, PT_GR7(%r1)
STREG %r8, PT_GR8(%r1)
STREG %r9, PT_GR9(%r1)
STREG %r10, PT_GR10(%r1)
STREG %r11, PT_GR11(%r1)
STREG %r12, PT_GR12(%r1)
STREG %r13, PT_GR13(%r1)
STREG %r14, PT_GR14(%r1)
STREG %r15, PT_GR15(%r1)
STREG %r16, PT_GR16(%r1)
STREG %r17, PT_GR17(%r1)
STREG %r18, PT_GR18(%r1)
STREG %r19, PT_GR19(%r1)
STREG %r20, PT_GR20(%r1)
STREG %r21, PT_GR21(%r1)
STREG %r22, PT_GR22(%r1)
STREG %r23, PT_GR23(%r1)
STREG %r24, PT_GR24(%r1)
STREG %r25, PT_GR25(%r1)
STREG %r26, PT_GR26(%r1)
STREG %r27, PT_GR27(%r1)
STREG %r28, PT_GR28(%r1)
STREG %r29, PT_GR29(%r1)
STREG %r30, PT_GR30(%r1)
STREG %r31, PT_GR31(%r1)
mfctl %cr11, %r26
STREG %r26, PT_SAR(%r1)
copy %rp, %r26
LDREG -FTRACE_FRAME_SIZE-PT_SZ_ALGN(%sp), %r25
ldo -8(%r25), %r25
copy %r3, %arg2
b,l ftrace_function_trampoline, %rp
copy %r1, %arg3 /* struct pt_regs */
ldo -PT_SZ_ALGN(%sp), %r1
LDREG PT_SAR(%r1), %rp
mtctl %rp, %cr11
LDREG PT_GR2(%r1), %rp
LDREG PT_GR3(%r1), %r3
LDREG PT_GR4(%r1), %r4
LDREG PT_GR5(%r1), %r5
LDREG PT_GR6(%r1), %r6
LDREG PT_GR7(%r1), %r7
LDREG PT_GR8(%r1), %r8
LDREG PT_GR9(%r1), %r9
LDREG PT_GR10(%r1),%r10
LDREG PT_GR11(%r1),%r11
LDREG PT_GR12(%r1),%r12
LDREG PT_GR13(%r1),%r13
LDREG PT_GR14(%r1),%r14
LDREG PT_GR15(%r1),%r15
LDREG PT_GR16(%r1),%r16
LDREG PT_GR17(%r1),%r17
LDREG PT_GR18(%r1),%r18
LDREG PT_GR19(%r1),%r19
LDREG PT_GR20(%r1),%r20
LDREG PT_GR21(%r1),%r21
LDREG PT_GR22(%r1),%r22
LDREG PT_GR23(%r1),%r23
LDREG PT_GR24(%r1),%r24
LDREG PT_GR25(%r1),%r25
LDREG PT_GR26(%r1),%r26
LDREG PT_GR27(%r1),%r27
LDREG PT_GR28(%r1),%r28
LDREG PT_GR29(%r1),%r29
LDREG PT_GR30(%r1),%r30
LDREG PT_GR31(%r1),%r31
ldo -PT_SZ_ALGN(%sp), %sp
LDREGM -FTRACE_FRAME_SIZE(%sp), %r1
/* Adjust return point to jump back to beginning of traced function */
ldo -4(%r1), %r1
bv,n (%r1)
ENDPROC_CFI(ftrace_regs_caller)
#endif
#endif
#ifdef CONFIG_FUNCTION_GRAPH_TRACER

View File

@ -311,6 +311,19 @@ int pdc_chassis_disp(unsigned long disp)
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
* @retval: -1 on error, 0 on success

View File

@ -13,6 +13,8 @@
#include <linux/init.h>
#include <linux/ftrace.h>
#include <linux/uaccess.h>
#include <linux/kprobes.h>
#include <linux/ptrace.h>
#include <asm/assembly.h>
#include <asm/sections.h>
@ -48,17 +50,22 @@ static void __hot prepare_ftrace_return(unsigned long *parent,
void notrace __hot ftrace_function_trampoline(unsigned long parent,
unsigned long self_addr,
unsigned long org_sp_gr3)
unsigned long org_sp_gr3,
struct pt_regs *regs)
{
#ifndef CONFIG_DYNAMIC_FTRACE
extern ftrace_func_t ftrace_trace_function;
#endif
if (ftrace_trace_function != ftrace_stub)
ftrace_trace_function(self_addr, parent, NULL, NULL);
extern struct ftrace_ops *function_trace_op;
if (function_trace_op->flags & FTRACE_OPS_FL_ENABLED &&
ftrace_trace_function != ftrace_stub)
ftrace_trace_function(self_addr, parent,
function_trace_op, regs);
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
if (ftrace_graph_return != (trace_func_graph_ret_t) ftrace_stub ||
ftrace_graph_entry != ftrace_graph_entry_stub) {
ftrace_graph_entry != ftrace_graph_entry_stub) {
unsigned long *parent_rp;
/* calculate pointer to %rp in stack */
@ -96,6 +103,12 @@ int ftrace_update_ftrace_func(ftrace_func_t func)
return 0;
}
int ftrace_modify_call(struct dyn_ftrace *rec, unsigned long old_addr,
unsigned long addr)
{
return 0;
}
unsigned long ftrace_call_adjust(unsigned long addr)
{
return addr+(FTRACE_PATCHABLE_FUNCTION_SIZE-1)*4;
@ -187,3 +200,46 @@ int ftrace_make_nop(struct module *mod, struct dyn_ftrace *rec,
return 0;
}
#endif
#ifdef CONFIG_KPROBES_ON_FTRACE
void kprobe_ftrace_handler(unsigned long ip, unsigned long parent_ip,
struct ftrace_ops *ops, struct pt_regs *regs)
{
struct kprobe_ctlblk *kcb;
struct kprobe *p = get_kprobe((kprobe_opcode_t *)ip);
if (unlikely(!p) || kprobe_disabled(p))
return;
if (kprobe_running()) {
kprobes_inc_nmissed_count(p);
return;
}
__this_cpu_write(current_kprobe, p);
kcb = get_kprobe_ctlblk();
kcb->kprobe_status = KPROBE_HIT_ACTIVE;
regs->iaoq[0] = ip;
regs->iaoq[1] = ip + 4;
if (!p->pre_handler || !p->pre_handler(p, regs)) {
regs->iaoq[0] = ip + 4;
regs->iaoq[1] = ip + 8;
if (unlikely(p->post_handler)) {
kcb->kprobe_status = KPROBE_HIT_SSDONE;
p->post_handler(p, regs, 0);
}
}
__this_cpu_write(current_kprobe, NULL);
}
NOKPROBE_SYMBOL(kprobe_ftrace_handler);
int arch_prepare_kprobe_ftrace(struct kprobe *p)
{
p->ainsn.insn = NULL;
return 0;
}
#endif

112
arch/parisc/kernel/kexec.c Normal file
View File

@ -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;
}

View File

@ -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
};

View File

@ -281,10 +281,6 @@ int __kprobes arch_trampoline_kprobe(struct kprobe *p)
{
return p->addr == trampoline_p.addr;
}
bool arch_kprobe_on_func_entry(unsigned long offset)
{
return !offset;
}
int __init arch_init_kprobes(void)
{

View File

@ -174,6 +174,15 @@ fdtdone:
2: bv %r0(%r2)
nop
/*
* When running in qemu, drop whole flush_tlb_all_local function and
* replace by one pdtlbe instruction, for which QEMU will drop all
* local TLB entries.
*/
3: pdtlbe %r0(%sr1,%r0)
bv,n %r0(%r2)
ALTERNATIVE_CODE(flush_tlb_all_local, 2, ALT_COND_RUN_ON_QEMU, 3b)
ENDPROC_CFI(flush_tlb_all_local)
.import cache_info,data

View File

@ -17,6 +17,10 @@
#include <linux/string.h>
EXPORT_SYMBOL(memset);
EXPORT_SYMBOL(strlen);
EXPORT_SYMBOL(strcpy);
EXPORT_SYMBOL(strncpy);
EXPORT_SYMBOL(strcat);
#include <linux/atomic.h>
EXPORT_SYMBOL(__xchg8);

View File

@ -34,17 +34,6 @@
#define DBG_RES(x...)
#endif
/* To be used as: mdelay(pci_post_reset_delay);
*
* post_reset is the time the kernel should stall to prevent anyone from
* accessing the PCI bus once #RESET is de-asserted.
* PCI spec somewhere says 1 second but with multi-PCI bus systems,
* this makes the boot time much longer than necessary.
* 20ms seems to work for all the HP PCI implementations to date.
*
* #define pci_post_reset_delay 50
*/
struct pci_port_ops *pci_port __ro_after_init;
struct pci_bios_ops *pci_bios __ro_after_init;

View File

@ -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

View File

@ -109,6 +109,7 @@ halt_processor(void)
/* REVISIT : does PM *know* this CPU isn't available? */
set_cpu_online(smp_processor_id(), false);
local_irq_disable();
__pdc_cpu_rendezvous();
for (;;)
;
}

View File

@ -399,7 +399,8 @@
352 common pkey_alloc sys_pkey_alloc
353 common pkey_free sys_pkey_free
354 common rseq sys_rseq
# 355 through 402 are unassigned to sync up with generic numbers
355 common kexec_file_load sys_kexec_file_load sys_kexec_file_load
# up to 402 is unassigned and reserved for arch specific syscalls
403 32 clock_gettime64 sys_clock_gettime sys_clock_gettime
404 32 clock_settime64 sys_clock_settime sys_clock_settime
405 32 clock_adjtime64 sys_clock_adjtime sys_clock_adjtime

View File

@ -29,6 +29,7 @@
#include <linux/bug.h>
#include <linux/ratelimit.h>
#include <linux/uaccess.h>
#include <linux/kdebug.h>
#include <asm/assembly.h>
#include <asm/io.h>
@ -414,6 +415,7 @@ void parisc_terminate(char *msg, struct pt_regs *regs, int code, unsigned long o
{
static DEFINE_SPINLOCK(terminate_lock);
(void)notify_die(DIE_OOPS, msg, regs, 0, code, SIGTRAP);
bust_spinlocks(1);
set_eiem(0);

View File

@ -3,7 +3,7 @@
# Makefile for parisc-specific library files
#
lib-y := lusercopy.o bitops.o checksum.o io.o memset.o memcpy.o \
ucmpdi2.o delay.o
lib-y := lusercopy.o bitops.o checksum.o io.o memcpy.o \
ucmpdi2.o delay.o string.o
obj-y := iomap.o

View File

@ -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;
}

136
arch/parisc/lib/string.S Normal file
View File

@ -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

View File

@ -511,6 +511,7 @@ config KEXEC_FILE
select KEXEC_CORE
select HAVE_IMA_KEXEC
select BUILD_BIN2C
select KEXEC_ELF
depends on PPC64
depends on CRYPTO=y
depends on CRYPTO_SHA256=y

View File

@ -23,541 +23,6 @@
#include <linux/slab.h>
#include <linux/types.h>
#define PURGATORY_STACK_SIZE (16 * 1024)
#define elf_addr_to_cpu elf64_to_cpu
#ifndef Elf_Rel
#define Elf_Rel Elf64_Rel
#endif /* Elf_Rel */
struct elf_info {
/*
* Where the ELF binary contents are kept.
* Memory managed by the user of the struct.
*/
const char *buffer;
const struct elfhdr *ehdr;
const struct elf_phdr *proghdrs;
struct elf_shdr *sechdrs;
};
static inline bool elf_is_elf_file(const struct elfhdr *ehdr)
{
return memcmp(ehdr->e_ident, ELFMAG, SELFMAG) == 0;
}
static uint64_t elf64_to_cpu(const struct elfhdr *ehdr, uint64_t value)
{
if (ehdr->e_ident[EI_DATA] == ELFDATA2LSB)
value = le64_to_cpu(value);
else if (ehdr->e_ident[EI_DATA] == ELFDATA2MSB)
value = be64_to_cpu(value);
return value;
}
static uint16_t elf16_to_cpu(const struct elfhdr *ehdr, uint16_t value)
{
if (ehdr->e_ident[EI_DATA] == ELFDATA2LSB)
value = le16_to_cpu(value);
else if (ehdr->e_ident[EI_DATA] == ELFDATA2MSB)
value = be16_to_cpu(value);
return value;
}
static uint32_t elf32_to_cpu(const struct elfhdr *ehdr, uint32_t value)
{
if (ehdr->e_ident[EI_DATA] == ELFDATA2LSB)
value = le32_to_cpu(value);
else if (ehdr->e_ident[EI_DATA] == ELFDATA2MSB)
value = be32_to_cpu(value);
return value;
}
/**
* elf_is_ehdr_sane - check that it is safe to use the ELF header
* @buf_len: size of the buffer in which the ELF file is loaded.
*/
static bool elf_is_ehdr_sane(const struct elfhdr *ehdr, size_t buf_len)
{
if (ehdr->e_phnum > 0 && ehdr->e_phentsize != sizeof(struct elf_phdr)) {
pr_debug("Bad program header size.\n");
return false;
} else if (ehdr->e_shnum > 0 &&
ehdr->e_shentsize != sizeof(struct elf_shdr)) {
pr_debug("Bad section header size.\n");
return false;
} else if (ehdr->e_ident[EI_VERSION] != EV_CURRENT ||
ehdr->e_version != EV_CURRENT) {
pr_debug("Unknown ELF version.\n");
return false;
}
if (ehdr->e_phoff > 0 && ehdr->e_phnum > 0) {
size_t phdr_size;
/*
* e_phnum is at most 65535 so calculating the size of the
* program header cannot overflow.
*/
phdr_size = sizeof(struct elf_phdr) * ehdr->e_phnum;
/* Sanity check the program header table location. */
if (ehdr->e_phoff + phdr_size < ehdr->e_phoff) {
pr_debug("Program headers at invalid location.\n");
return false;
} else if (ehdr->e_phoff + phdr_size > buf_len) {
pr_debug("Program headers truncated.\n");
return false;
}
}
if (ehdr->e_shoff > 0 && ehdr->e_shnum > 0) {
size_t shdr_size;
/*
* e_shnum is at most 65536 so calculating
* the size of the section header cannot overflow.
*/
shdr_size = sizeof(struct elf_shdr) * ehdr->e_shnum;
/* Sanity check the section header table location. */
if (ehdr->e_shoff + shdr_size < ehdr->e_shoff) {
pr_debug("Section headers at invalid location.\n");
return false;
} else if (ehdr->e_shoff + shdr_size > buf_len) {
pr_debug("Section headers truncated.\n");
return false;
}
}
return true;
}
static int elf_read_ehdr(const char *buf, size_t len, struct elfhdr *ehdr)
{
struct elfhdr *buf_ehdr;
if (len < sizeof(*buf_ehdr)) {
pr_debug("Buffer is too small to hold ELF header.\n");
return -ENOEXEC;
}
memset(ehdr, 0, sizeof(*ehdr));
memcpy(ehdr->e_ident, buf, sizeof(ehdr->e_ident));
if (!elf_is_elf_file(ehdr)) {
pr_debug("No ELF header magic.\n");
return -ENOEXEC;
}
if (ehdr->e_ident[EI_CLASS] != ELF_CLASS) {
pr_debug("Not a supported ELF class.\n");
return -ENOEXEC;
} else if (ehdr->e_ident[EI_DATA] != ELFDATA2LSB &&
ehdr->e_ident[EI_DATA] != ELFDATA2MSB) {
pr_debug("Not a supported ELF data format.\n");
return -ENOEXEC;
}
buf_ehdr = (struct elfhdr *) buf;
if (elf16_to_cpu(ehdr, buf_ehdr->e_ehsize) != sizeof(*buf_ehdr)) {
pr_debug("Bad ELF header size.\n");
return -ENOEXEC;
}
ehdr->e_type = elf16_to_cpu(ehdr, buf_ehdr->e_type);
ehdr->e_machine = elf16_to_cpu(ehdr, buf_ehdr->e_machine);
ehdr->e_version = elf32_to_cpu(ehdr, buf_ehdr->e_version);
ehdr->e_entry = elf_addr_to_cpu(ehdr, buf_ehdr->e_entry);
ehdr->e_phoff = elf_addr_to_cpu(ehdr, buf_ehdr->e_phoff);
ehdr->e_shoff = elf_addr_to_cpu(ehdr, buf_ehdr->e_shoff);
ehdr->e_flags = elf32_to_cpu(ehdr, buf_ehdr->e_flags);
ehdr->e_phentsize = elf16_to_cpu(ehdr, buf_ehdr->e_phentsize);
ehdr->e_phnum = elf16_to_cpu(ehdr, buf_ehdr->e_phnum);
ehdr->e_shentsize = elf16_to_cpu(ehdr, buf_ehdr->e_shentsize);
ehdr->e_shnum = elf16_to_cpu(ehdr, buf_ehdr->e_shnum);
ehdr->e_shstrndx = elf16_to_cpu(ehdr, buf_ehdr->e_shstrndx);
return elf_is_ehdr_sane(ehdr, len) ? 0 : -ENOEXEC;
}
/**
* elf_is_phdr_sane - check that it is safe to use the program header
* @buf_len: size of the buffer in which the ELF file is loaded.
*/
static bool elf_is_phdr_sane(const struct elf_phdr *phdr, size_t buf_len)
{
if (phdr->p_offset + phdr->p_filesz < phdr->p_offset) {
pr_debug("ELF segment location wraps around.\n");
return false;
} else if (phdr->p_offset + phdr->p_filesz > buf_len) {
pr_debug("ELF segment not in file.\n");
return false;
} else if (phdr->p_paddr + phdr->p_memsz < phdr->p_paddr) {
pr_debug("ELF segment address wraps around.\n");
return false;
}
return true;
}
static int elf_read_phdr(const char *buf, size_t len, struct elf_info *elf_info,
int idx)
{
/* Override the const in proghdrs, we are the ones doing the loading. */
struct elf_phdr *phdr = (struct elf_phdr *) &elf_info->proghdrs[idx];
const char *pbuf;
struct elf_phdr *buf_phdr;
pbuf = buf + elf_info->ehdr->e_phoff + (idx * sizeof(*buf_phdr));
buf_phdr = (struct elf_phdr *) pbuf;
phdr->p_type = elf32_to_cpu(elf_info->ehdr, buf_phdr->p_type);
phdr->p_offset = elf_addr_to_cpu(elf_info->ehdr, buf_phdr->p_offset);
phdr->p_paddr = elf_addr_to_cpu(elf_info->ehdr, buf_phdr->p_paddr);
phdr->p_vaddr = elf_addr_to_cpu(elf_info->ehdr, buf_phdr->p_vaddr);
phdr->p_flags = elf32_to_cpu(elf_info->ehdr, buf_phdr->p_flags);
/*
* The following fields have a type equivalent to Elf_Addr
* both in 32 bit and 64 bit ELF.
*/
phdr->p_filesz = elf_addr_to_cpu(elf_info->ehdr, buf_phdr->p_filesz);
phdr->p_memsz = elf_addr_to_cpu(elf_info->ehdr, buf_phdr->p_memsz);
phdr->p_align = elf_addr_to_cpu(elf_info->ehdr, buf_phdr->p_align);
return elf_is_phdr_sane(phdr, len) ? 0 : -ENOEXEC;
}
/**
* elf_read_phdrs - read the program headers from the buffer
*
* This function assumes that the program header table was checked for sanity.
* Use elf_is_ehdr_sane() if it wasn't.
*/
static int elf_read_phdrs(const char *buf, size_t len,
struct elf_info *elf_info)
{
size_t phdr_size, i;
const struct elfhdr *ehdr = elf_info->ehdr;
/*
* e_phnum is at most 65535 so calculating the size of the
* program header cannot overflow.
*/
phdr_size = sizeof(struct elf_phdr) * ehdr->e_phnum;
elf_info->proghdrs = kzalloc(phdr_size, GFP_KERNEL);
if (!elf_info->proghdrs)
return -ENOMEM;
for (i = 0; i < ehdr->e_phnum; i++) {
int ret;
ret = elf_read_phdr(buf, len, elf_info, i);
if (ret) {
kfree(elf_info->proghdrs);
elf_info->proghdrs = NULL;
return ret;
}
}
return 0;
}
/**
* elf_is_shdr_sane - check that it is safe to use the section header
* @buf_len: size of the buffer in which the ELF file is loaded.
*/
static bool elf_is_shdr_sane(const struct elf_shdr *shdr, size_t buf_len)
{
bool size_ok;
/* SHT_NULL headers have undefined values, so we can't check them. */
if (shdr->sh_type == SHT_NULL)
return true;
/* Now verify sh_entsize */
switch (shdr->sh_type) {
case SHT_SYMTAB:
size_ok = shdr->sh_entsize == sizeof(Elf_Sym);
break;
case SHT_RELA:
size_ok = shdr->sh_entsize == sizeof(Elf_Rela);
break;
case SHT_DYNAMIC:
size_ok = shdr->sh_entsize == sizeof(Elf_Dyn);
break;
case SHT_REL:
size_ok = shdr->sh_entsize == sizeof(Elf_Rel);
break;
case SHT_NOTE:
case SHT_PROGBITS:
case SHT_HASH:
case SHT_NOBITS:
default:
/*
* This is a section whose entsize requirements
* I don't care about. If I don't know about
* the section I can't care about it's entsize
* requirements.
*/
size_ok = true;
break;
}
if (!size_ok) {
pr_debug("ELF section with wrong entry size.\n");
return false;
} else if (shdr->sh_addr + shdr->sh_size < shdr->sh_addr) {
pr_debug("ELF section address wraps around.\n");
return false;
}
if (shdr->sh_type != SHT_NOBITS) {
if (shdr->sh_offset + shdr->sh_size < shdr->sh_offset) {
pr_debug("ELF section location wraps around.\n");
return false;
} else if (shdr->sh_offset + shdr->sh_size > buf_len) {
pr_debug("ELF section not in file.\n");
return false;
}
}
return true;
}
static int elf_read_shdr(const char *buf, size_t len, struct elf_info *elf_info,
int idx)
{
struct elf_shdr *shdr = &elf_info->sechdrs[idx];
const struct elfhdr *ehdr = elf_info->ehdr;
const char *sbuf;
struct elf_shdr *buf_shdr;
sbuf = buf + ehdr->e_shoff + idx * sizeof(*buf_shdr);
buf_shdr = (struct elf_shdr *) sbuf;
shdr->sh_name = elf32_to_cpu(ehdr, buf_shdr->sh_name);
shdr->sh_type = elf32_to_cpu(ehdr, buf_shdr->sh_type);
shdr->sh_addr = elf_addr_to_cpu(ehdr, buf_shdr->sh_addr);
shdr->sh_offset = elf_addr_to_cpu(ehdr, buf_shdr->sh_offset);
shdr->sh_link = elf32_to_cpu(ehdr, buf_shdr->sh_link);
shdr->sh_info = elf32_to_cpu(ehdr, buf_shdr->sh_info);
/*
* The following fields have a type equivalent to Elf_Addr
* both in 32 bit and 64 bit ELF.
*/
shdr->sh_flags = elf_addr_to_cpu(ehdr, buf_shdr->sh_flags);
shdr->sh_size = elf_addr_to_cpu(ehdr, buf_shdr->sh_size);
shdr->sh_addralign = elf_addr_to_cpu(ehdr, buf_shdr->sh_addralign);
shdr->sh_entsize = elf_addr_to_cpu(ehdr, buf_shdr->sh_entsize);
return elf_is_shdr_sane(shdr, len) ? 0 : -ENOEXEC;
}
/**
* elf_read_shdrs - read the section headers from the buffer
*
* This function assumes that the section header table was checked for sanity.
* Use elf_is_ehdr_sane() if it wasn't.
*/
static int elf_read_shdrs(const char *buf, size_t len,
struct elf_info *elf_info)
{
size_t shdr_size, i;
/*
* e_shnum is at most 65536 so calculating
* the size of the section header cannot overflow.
*/
shdr_size = sizeof(struct elf_shdr) * elf_info->ehdr->e_shnum;
elf_info->sechdrs = kzalloc(shdr_size, GFP_KERNEL);
if (!elf_info->sechdrs)
return -ENOMEM;
for (i = 0; i < elf_info->ehdr->e_shnum; i++) {
int ret;
ret = elf_read_shdr(buf, len, elf_info, i);
if (ret) {
kfree(elf_info->sechdrs);
elf_info->sechdrs = NULL;
return ret;
}
}
return 0;
}
/**
* elf_read_from_buffer - read ELF file and sets up ELF header and ELF info
* @buf: Buffer to read ELF file from.
* @len: Size of @buf.
* @ehdr: Pointer to existing struct which will be populated.
* @elf_info: Pointer to existing struct which will be populated.
*
* This function allows reading ELF files with different byte order than
* the kernel, byte-swapping the fields as needed.
*
* Return:
* On success returns 0, and the caller should call elf_free_info(elf_info) to
* free the memory allocated for the section and program headers.
*/
int elf_read_from_buffer(const char *buf, size_t len, struct elfhdr *ehdr,
struct elf_info *elf_info)
{
int ret;
ret = elf_read_ehdr(buf, len, ehdr);
if (ret)
return ret;
elf_info->buffer = buf;
elf_info->ehdr = ehdr;
if (ehdr->e_phoff > 0 && ehdr->e_phnum > 0) {
ret = elf_read_phdrs(buf, len, elf_info);
if (ret)
return ret;
}
if (ehdr->e_shoff > 0 && ehdr->e_shnum > 0) {
ret = elf_read_shdrs(buf, len, elf_info);
if (ret) {
kfree(elf_info->proghdrs);
return ret;
}
}
return 0;
}
/**
* elf_free_info - free memory allocated by elf_read_from_buffer
*/
void elf_free_info(struct elf_info *elf_info)
{
kfree(elf_info->proghdrs);
kfree(elf_info->sechdrs);
memset(elf_info, 0, sizeof(*elf_info));
}
/**
* build_elf_exec_info - read ELF executable and check that we can use it
*/
static int build_elf_exec_info(const char *buf, size_t len, struct elfhdr *ehdr,
struct elf_info *elf_info)
{
int i;
int ret;
ret = elf_read_from_buffer(buf, len, ehdr, elf_info);
if (ret)
return ret;
/* Big endian vmlinux has type ET_DYN. */
if (ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN) {
pr_err("Not an ELF executable.\n");
goto error;
} else if (!elf_info->proghdrs) {
pr_err("No ELF program header.\n");
goto error;
}
for (i = 0; i < ehdr->e_phnum; i++) {
/*
* Kexec does not support loading interpreters.
* In addition this check keeps us from attempting
* to kexec ordinay executables.
*/
if (elf_info->proghdrs[i].p_type == PT_INTERP) {
pr_err("Requires an ELF interpreter.\n");
goto error;
}
}
return 0;
error:
elf_free_info(elf_info);
return -ENOEXEC;
}
static int elf64_probe(const char *buf, unsigned long len)
{
struct elfhdr ehdr;
struct elf_info elf_info;
int ret;
ret = build_elf_exec_info(buf, len, &ehdr, &elf_info);
if (ret)
return ret;
elf_free_info(&elf_info);
return elf_check_arch(&ehdr) ? 0 : -ENOEXEC;
}
/**
* elf_exec_load - load ELF executable image
* @lowest_load_addr: On return, will be the address where the first PT_LOAD
* section will be loaded in memory.
*
* Return:
* 0 on success, negative value on failure.
*/
static int elf_exec_load(struct kimage *image, struct elfhdr *ehdr,
struct elf_info *elf_info,
unsigned long *lowest_load_addr)
{
unsigned long base = 0, lowest_addr = UINT_MAX;
int ret;
size_t i;
struct kexec_buf kbuf = { .image = image, .buf_max = ppc64_rma_size,
.top_down = false };
/* Read in the PT_LOAD segments. */
for (i = 0; i < ehdr->e_phnum; i++) {
unsigned long load_addr;
size_t size;
const struct elf_phdr *phdr;
phdr = &elf_info->proghdrs[i];
if (phdr->p_type != PT_LOAD)
continue;
size = phdr->p_filesz;
if (size > phdr->p_memsz)
size = phdr->p_memsz;
kbuf.buffer = (void *) elf_info->buffer + phdr->p_offset;
kbuf.bufsz = size;
kbuf.memsz = phdr->p_memsz;
kbuf.buf_align = phdr->p_align;
kbuf.buf_min = phdr->p_paddr + base;
kbuf.mem = KEXEC_BUF_MEM_UNKNOWN;
ret = kexec_add_buffer(&kbuf);
if (ret)
goto out;
load_addr = kbuf.mem;
if (load_addr < lowest_addr)
lowest_addr = load_addr;
}
/* Update entry point to reflect new load address. */
ehdr->e_entry += base;
*lowest_load_addr = lowest_addr;
ret = 0;
out:
return ret;
}
static void *elf64_load(struct kimage *image, char *kernel_buf,
unsigned long kernel_len, char *initrd,
unsigned long initrd_len, char *cmdline,
@ -570,18 +35,18 @@ static void *elf64_load(struct kimage *image, char *kernel_buf,
void *fdt;
const void *slave_code;
struct elfhdr ehdr;
struct elf_info elf_info;
struct kexec_elf_info elf_info;
struct kexec_buf kbuf = { .image = image, .buf_min = 0,
.buf_max = ppc64_rma_size };
struct kexec_buf pbuf = { .image = image, .buf_min = 0,
.buf_max = ppc64_rma_size, .top_down = true,
.mem = KEXEC_BUF_MEM_UNKNOWN };
ret = build_elf_exec_info(kernel_buf, kernel_len, &ehdr, &elf_info);
ret = kexec_build_elf_info(kernel_buf, kernel_len, &ehdr, &elf_info);
if (ret)
goto out;
ret = elf_exec_load(image, &ehdr, &elf_info, &kernel_load_addr);
ret = kexec_elf_load(image, &ehdr, &elf_info, &kbuf, &kernel_load_addr);
if (ret)
goto out;
@ -648,13 +113,13 @@ static void *elf64_load(struct kimage *image, char *kernel_buf,
pr_err("Error setting up the purgatory.\n");
out:
elf_free_info(&elf_info);
kexec_free_elf_info(&elf_info);
/* Make kimage_file_post_load_cleanup free the fdt buffer for us. */
return ret ? ERR_PTR(ret) : fdt;
}
const struct kexec_file_ops kexec_elf64_ops = {
.probe = elf64_probe,
.probe = kexec_elf_probe,
.load = elf64_load,
};

View File

@ -6,7 +6,7 @@
** (c) Copyright 1999 SuSE GmbH
** (c) Copyright 1999,2000 Hewlett-Packard Company
** (c) Copyright 2000 Grant Grundler
** (c) Copyright 2006 Helge Deller
** (c) Copyright 2006-2019 Helge Deller
**
**
** This module provides access to Dino PCI bus (config/IOport spaces)
@ -156,6 +156,15 @@ static inline struct dino_device *DINO_DEV(struct pci_hba_data *hba)
return container_of(hba, struct dino_device, hba);
}
/* Check if PCI device is behind a Card-mode Dino. */
static int pci_dev_is_behind_card_dino(struct pci_dev *dev)
{
struct dino_device *dino_dev;
dino_dev = DINO_DEV(parisc_walk_tree(dev->bus->bridge));
return is_card_dino(&dino_dev->hba.dev->id);
}
/*
* Dino Configuration Space Accessor Functions
*/
@ -437,6 +446,21 @@ static void quirk_cirrus_cardbus(struct pci_dev *dev)
}
DECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_CIRRUS, PCI_DEVICE_ID_CIRRUS_6832, quirk_cirrus_cardbus );
#ifdef CONFIG_TULIP
static void pci_fixup_tulip(struct pci_dev *dev)
{
if (!pci_dev_is_behind_card_dino(dev))
return;
if (!(pci_resource_flags(dev, 1) & IORESOURCE_MEM))
return;
pr_warn("%s: HP HSC-PCI Cards with card-mode Dino not yet supported.\n",
pci_name(dev));
/* Disable this card by zeroing the PCI resources */
memset(&dev->resource[0], 0, sizeof(dev->resource[0]));
memset(&dev->resource[1], 0, sizeof(dev->resource[1]));
}
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_DEC, PCI_ANY_ID, pci_fixup_tulip);
#endif /* CONFIG_TULIP */
static void __init
dino_bios_init(void)
@ -863,14 +887,14 @@ static int __init dino_common_init(struct parisc_device *dev,
#define CUJO_RAVEN_BADPAGE 0x01003000UL
#define CUJO_FIREHAWK_BADPAGE 0x01607000UL
static const char *dino_vers[] = {
static const char dino_vers[][4] = {
"2.0",
"2.1",
"3.0",
"3.1"
};
static const char *cujo_vers[] = {
static const char cujo_vers[][4] = {
"1.0",
"2.0"
};

View File

@ -93,7 +93,7 @@ static int configure_memory(const unsigned char *buf,
res->start = mem_parent->start + get_24(buf+len+2);
res->end = res->start + get_16(buf+len+5)*1024;
res->flags = IORESOURCE_MEM;
printk("memory %lx-%lx ", (unsigned long)res->start, (unsigned long)res->end);
pr_cont("memory %pR ", res);
result = request_resource(mem_parent, res);
if (result < 0) {
printk(KERN_ERR "EISA Enumerator: failed to claim EISA Bus address space!\n");
@ -123,7 +123,7 @@ static int configure_irq(const unsigned char *buf)
for (i=0;i<HPEE_IRQ_MAX_ENT;i++) {
c = get_8(buf+len);
printk("IRQ %d ", c & HPEE_IRQ_CHANNEL_MASK);
pr_cont("IRQ %d ", c & HPEE_IRQ_CHANNEL_MASK);
if (c & HPEE_IRQ_TRIG_LEVEL) {
eisa_make_irq_level(c & HPEE_IRQ_CHANNEL_MASK);
} else {
@ -153,7 +153,7 @@ static int configure_dma(const unsigned char *buf)
for (i=0;i<HPEE_DMA_MAX_ENT;i++) {
c = get_8(buf+len);
printk("DMA %d ", c&HPEE_DMA_CHANNEL_MASK);
pr_cont("DMA %d ", c&HPEE_DMA_CHANNEL_MASK);
/* fixme: maybe initialize the dma channel withthe timing ? */
len+=2;
if (!(c & HPEE_DMA_MORE)) {
@ -183,7 +183,7 @@ static int configure_port(const unsigned char *buf, struct resource *io_parent,
res->start = get_16(buf+len+1);
res->end = get_16(buf+len+1)+(c&HPEE_PORT_SIZE_MASK)+1;
res->flags = IORESOURCE_IO;
printk("ioports %lx-%lx ", (unsigned long)res->start, (unsigned long)res->end);
pr_cont("ioports %pR ", res);
result = request_resource(io_parent, res);
if (result < 0) {
printk(KERN_ERR "EISA Enumerator: failed to claim EISA Bus address space!\n");
@ -401,7 +401,7 @@ static int parse_slot_config(int slot,
}
pos = p0 + function_len;
}
printk("\n");
pr_cont("\n");
if (!id_string_used) {
kfree(board);
}

View File

@ -61,8 +61,6 @@ static int __init hppb_probe(struct parisc_device *dev)
}
card = card->next;
}
printk(KERN_INFO "Found GeckoBoa at 0x%llx\n",
(unsigned long long) dev->hpa.start);
card->hpa = dev->hpa.start;
card->mmio_region.name = "HP-PB Bus";
@ -72,10 +70,11 @@ static int __init hppb_probe(struct parisc_device *dev)
card->mmio_region.end = gsc_readl(dev->hpa.start + IO_IO_HIGH) - 1;
status = ccio_request_resource(dev, &card->mmio_region);
if(status < 0) {
printk(KERN_ERR "%s: failed to claim HP-PB bus space (%pR)\n",
__FILE__, &card->mmio_region);
}
pr_info("Found GeckoBoa at %pap, bus space %pR,%s claimed.\n",
&dev->hpa.start,
&card->mmio_region,
(status < 0) ? " not":"" );
return 0;
}

View File

@ -216,6 +216,29 @@ extern int crash_prepare_elf64_headers(struct crash_mem *mem, int kernel_map,
void **addr, unsigned long *sz);
#endif /* CONFIG_KEXEC_FILE */
#ifdef CONFIG_KEXEC_ELF
struct kexec_elf_info {
/*
* Where the ELF binary contents are kept.
* Memory managed by the user of the struct.
*/
const char *buffer;
const struct elfhdr *ehdr;
const struct elf_phdr *proghdrs;
};
int kexec_build_elf_info(const char *buf, size_t len, struct elfhdr *ehdr,
struct kexec_elf_info *elf_info);
int kexec_elf_load(struct kimage *image, struct elfhdr *ehdr,
struct kexec_elf_info *elf_info,
struct kexec_buf *kbuf,
unsigned long *lowest_load_addr);
void kexec_free_elf_info(struct kexec_elf_info *elf_info);
int kexec_elf_probe(const char *buf, unsigned long len);
#endif
struct kimage {
kimage_entry_t head;
kimage_entry_t *entry;

View File

@ -31,6 +31,7 @@
#define KEXEC_ARCH_DEFAULT ( 0 << 16)
#define KEXEC_ARCH_386 ( 3 << 16)
#define KEXEC_ARCH_68K ( 4 << 16)
#define KEXEC_ARCH_PARISC (15 << 16)
#define KEXEC_ARCH_X86_64 (62 << 16)
#define KEXEC_ARCH_PPC (20 << 16)
#define KEXEC_ARCH_PPC64 (21 << 16)

View File

@ -64,6 +64,7 @@ obj-$(CONFIG_CRASH_CORE) += crash_core.o
obj-$(CONFIG_KEXEC_CORE) += kexec_core.o
obj-$(CONFIG_KEXEC) += kexec.o
obj-$(CONFIG_KEXEC_FILE) += kexec_file.o
obj-$(CONFIG_KEXEC_ELF) += kexec_elf.o
obj-$(CONFIG_BACKTRACE_SELF_TEST) += backtracetest.o
obj-$(CONFIG_COMPAT) += compat.o
obj-$(CONFIG_CGROUPS) += cgroup/

430
kernel/kexec_elf.c Normal file
View File

@ -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;
}