Commit d0a16fe9 authored by Linus Torvalds's avatar Linus Torvalds
Browse files

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
  ...
parents 76f0f227 fcc16a9e
......@@ -21,7 +21,7 @@
| nds32: | TODO |
| nios2: | TODO |
| openrisc: | TODO |
| parisc: | TODO |
| parisc: | ok |
| powerpc: | ok |
| riscv: | TODO |
| s390: | ok |
......
......@@ -21,7 +21,7 @@
| nds32: | TODO |
| nios2: | TODO |
| openrisc: | TODO |
| parisc: | TODO |
| parisc: | ok |
| powerpc: | ok |
| riscv: | TODO |
| s390: | TODO |
......
......@@ -18,6 +18,9 @@ config KEXEC_CORE
select CRASH_CORE
bool
config KEXEC_ELF
bool
config HAVE_IMA_KEXEC
bool
......
......@@ -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
......
firmware.c
real2.S
sizes.h
vmlinux
vmlinux.lds
......@@ -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 */
......@@ -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
};
......
......@@ -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);
......
/* 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 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";
......
......@@ -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
......@@ -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
......@@ -28,7 +28,8 @@ void __init_or_module apply_alternatives(struct alt_instr *start,
for (entry = start; entry < end; entry++, index++) {
u32 *from, len, cond, replacement;
u32 *from, cond, replacement;
s32 len;
from = (u32 *)((ulong)&entry->orig_offset + entry->orig_offset);
len = entry->len;
......@@ -49,6 +50,8 @@ void __init_or_module apply_alternatives(struct alt_instr *start,
continue;
if ((cond & ALT_COND_NO_ICACHE) && (cache_info.ic_size != 0))
continue;
if ((cond & ALT_COND_RUN_ON_QEMU) && !running_on_qemu)
continue;
/*
* If the PDC_MODEL capabilities has Non-coherent IO-PDIR bit
......@@ -74,11 +77,19 @@ void __init_or_module apply_alternatives(struct alt_instr *start,
if (replacement == INSN_NOP && len > 1)
replacement = 0xe8000002 + (len-2)*8; /* "b,n .+8" */
pr_debug("Do %d: Cond 0x%x, Replace %02d instructions @ 0x%px with 0x%08x\n",
index, cond, len, from, replacement);
/* Replace instruction */
*from = replacement;
pr_debug("ALTERNATIVE %3d: Cond %2x, Replace %2d instructions to 0x%08x @ 0x%px (%pS)\n",
index, cond, len, replacement, from, from);
if (len < 0) {
/* Replace multiple instruction by new code */
u32 *source;
len = -len;
source = (u32 *)((ulong)&entry->replacement + entry->replacement);
memcpy(from, source, 4 * len);
} else {
/* Replace by one instruction */
*from = replacement;
}
applied++;
}
......
......@@ -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
......
......@@ -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
......
......@@ -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
// 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;
}
// 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,