Merge tag 's390-5.2-1' of git://git.kernel.org/pub/scm/linux/kernel/git/s390/linux
Pull s390 updates from Martin Schwidefsky: - Support for kernel address space layout randomization - Add support for kernel image signature verification - Convert s390 to the generic get_user_pages_fast code - Convert s390 to the stack unwind API analog to x86 - Add support for CPU directed interrupts for PCI devices - Provide support for MIO instructions to the PCI base layer, this will allow the use of direct PCI mappings in user space code - Add the basic KVM guest ultravisor interface for protected VMs - Add AT_HWCAP bits for several new hardware capabilities - Update the CPU measurement facility counter definitions to SVN 6 - Arnds cleanup patches for his quest to get LLVM compiles working - A vfio-ccw update with bug fixes and support for halt and clear - Improvements for the hardware TRNG code - Another round of cleanup for the QDIO layer - Numerous cleanups and bug fixes * tag 's390-5.2-1' of git://git.kernel.org/pub/scm/linux/kernel/git/s390/linux: (98 commits) s390/vdso: drop unnecessary cc-ldoption s390: fix clang -Wpointer-sign warnigns in boot code s390: drop CONFIG_VIRT_TO_BUS s390: boot, purgatory: pass $(CLANG_FLAGS) where needed s390: only build for new CPUs with clang s390: simplify disabled_wait s390/ftrace: use HAVE_FUNCTION_GRAPH_RET_ADDR_PTR s390/unwind: introduce stack unwind API s390/opcodes: add missing instructions to the disassembler s390/bug: add entry size to the __bug_table section s390: use proper expoline sections for .dma code s390/nospec: rename assembler generated expoline thunks s390: add missing ENDPROC statements to assembler functions locking/lockdep: check for freed initmem in static_obj() s390/kernel: add support for kernel address space layout randomization (KASLR) s390/kernel: introduce .dma sections s390/sclp: do not use static sccbs s390/kprobes: use static buffer for insn_page s390/kernel: convert SYSCALL and PGM_CHECK handlers to .quad s390/kernel: build a relocatable kernel ...
This commit is contained in:
@@ -39,6 +39,7 @@ CFLAGS_smp.o := -Wno-nonnull
|
||||
#
|
||||
CFLAGS_stacktrace.o += -fno-optimize-sibling-calls
|
||||
CFLAGS_dumpstack.o += -fno-optimize-sibling-calls
|
||||
CFLAGS_unwind_bc.o += -fno-optimize-sibling-calls
|
||||
|
||||
#
|
||||
# Pass UTS_MACHINE for user_regset definition
|
||||
@@ -51,7 +52,7 @@ obj-y += debug.o irq.o ipl.o dis.o diag.o vdso.o early_nobss.o
|
||||
obj-y += sysinfo.o lgr.o os_info.o machine_kexec.o pgm_check.o
|
||||
obj-y += runtime_instr.o cache.o fpu.o dumpstack.o guarded_storage.o sthyi.o
|
||||
obj-y += entry.o reipl.o relocate_kernel.o kdebugfs.o alternative.o
|
||||
obj-y += nospec-branch.o ipl_vmparm.o
|
||||
obj-y += nospec-branch.o ipl_vmparm.o machine_kexec_reloc.o unwind_bc.o
|
||||
|
||||
extra-y += head64.o vmlinux.lds
|
||||
|
||||
@@ -77,6 +78,8 @@ obj-$(CONFIG_JUMP_LABEL) += jump_label.o
|
||||
obj-$(CONFIG_KEXEC_FILE) += machine_kexec_file.o kexec_image.o
|
||||
obj-$(CONFIG_KEXEC_FILE) += kexec_elf.o
|
||||
|
||||
obj-$(CONFIG_IMA) += ima_arch.o
|
||||
|
||||
obj-$(CONFIG_PERF_EVENTS) += perf_event.o perf_cpum_cf_common.o
|
||||
obj-$(CONFIG_PERF_EVENTS) += perf_cpum_cf.o perf_cpum_sf.o
|
||||
obj-$(CONFIG_PERF_EVENTS) += perf_cpum_cf_events.o perf_regs.o
|
||||
@@ -86,7 +89,7 @@ obj-$(CONFIG_TRACEPOINTS) += trace.o
|
||||
|
||||
# vdso
|
||||
obj-y += vdso64/
|
||||
obj-$(CONFIG_COMPAT) += vdso32/
|
||||
obj-$(CONFIG_COMPAT_VDSO) += vdso32/
|
||||
|
||||
chkbss := head64.o early_nobss.o
|
||||
include $(srctree)/arch/s390/scripts/Makefile.chkbss
|
||||
|
@@ -16,6 +16,7 @@
|
||||
#include <asm/pgtable.h>
|
||||
#include <asm/gmap.h>
|
||||
#include <asm/nmi.h>
|
||||
#include <asm/stacktrace.h>
|
||||
|
||||
int main(void)
|
||||
{
|
||||
|
@@ -28,6 +28,7 @@ ENTRY(s390_base_mcck_handler)
|
||||
1: la %r1,4095
|
||||
lmg %r0,%r15,__LC_GPREGS_SAVE_AREA-4095(%r1)
|
||||
lpswe __LC_MCK_OLD_PSW
|
||||
ENDPROC(s390_base_mcck_handler)
|
||||
|
||||
.section .bss
|
||||
.align 8
|
||||
@@ -48,6 +49,7 @@ ENTRY(s390_base_ext_handler)
|
||||
1: lmg %r0,%r15,__LC_SAVE_AREA_ASYNC
|
||||
ni __LC_EXT_OLD_PSW+1,0xfd # clear wait state bit
|
||||
lpswe __LC_EXT_OLD_PSW
|
||||
ENDPROC(s390_base_ext_handler)
|
||||
|
||||
.section .bss
|
||||
.align 8
|
||||
@@ -68,6 +70,7 @@ ENTRY(s390_base_pgm_handler)
|
||||
lmg %r0,%r15,__LC_SAVE_AREA_SYNC
|
||||
lpswe __LC_PGM_OLD_PSW
|
||||
1: lpswe disabled_wait_psw-0b(%r13)
|
||||
ENDPROC(s390_base_pgm_handler)
|
||||
|
||||
.align 8
|
||||
disabled_wait_psw:
|
||||
@@ -79,71 +82,3 @@ disabled_wait_psw:
|
||||
s390_base_pgm_handler_fn:
|
||||
.quad 0
|
||||
.previous
|
||||
|
||||
#
|
||||
# Calls diag 308 subcode 1 and continues execution
|
||||
#
|
||||
ENTRY(diag308_reset)
|
||||
larl %r4,.Lctlregs # Save control registers
|
||||
stctg %c0,%c15,0(%r4)
|
||||
lg %r2,0(%r4) # Disable lowcore protection
|
||||
nilh %r2,0xefff
|
||||
larl %r4,.Lctlreg0
|
||||
stg %r2,0(%r4)
|
||||
lctlg %c0,%c0,0(%r4)
|
||||
larl %r4,.Lfpctl # Floating point control register
|
||||
stfpc 0(%r4)
|
||||
larl %r4,.Lprefix # Save prefix register
|
||||
stpx 0(%r4)
|
||||
larl %r4,.Lprefix_zero # Set prefix register to 0
|
||||
spx 0(%r4)
|
||||
larl %r4,.Lcontinue_psw # Save PSW flags
|
||||
epsw %r2,%r3
|
||||
stm %r2,%r3,0(%r4)
|
||||
larl %r4,.Lrestart_psw # Setup restart PSW at absolute 0
|
||||
lghi %r3,0
|
||||
lg %r4,0(%r4) # Save PSW
|
||||
sturg %r4,%r3 # Use sturg, because of large pages
|
||||
lghi %r1,1
|
||||
lghi %r0,0
|
||||
diag %r0,%r1,0x308
|
||||
.Lrestart_part2:
|
||||
lhi %r0,0 # Load r0 with zero
|
||||
lhi %r1,2 # Use mode 2 = ESAME (dump)
|
||||
sigp %r1,%r0,SIGP_SET_ARCHITECTURE # Switch to ESAME mode
|
||||
sam64 # Switch to 64 bit addressing mode
|
||||
larl %r4,.Lctlregs # Restore control registers
|
||||
lctlg %c0,%c15,0(%r4)
|
||||
larl %r4,.Lfpctl # Restore floating point ctl register
|
||||
lfpc 0(%r4)
|
||||
larl %r4,.Lprefix # Restore prefix register
|
||||
spx 0(%r4)
|
||||
larl %r4,.Lcontinue_psw # Restore PSW flags
|
||||
lpswe 0(%r4)
|
||||
.Lcontinue:
|
||||
BR_EX %r14
|
||||
.align 16
|
||||
.Lrestart_psw:
|
||||
.long 0x00080000,0x80000000 + .Lrestart_part2
|
||||
|
||||
.section .data..nosave,"aw",@progbits
|
||||
.align 8
|
||||
.Lcontinue_psw:
|
||||
.quad 0,.Lcontinue
|
||||
.previous
|
||||
|
||||
.section .bss
|
||||
.align 8
|
||||
.Lctlreg0:
|
||||
.quad 0
|
||||
.Lctlregs:
|
||||
.rept 16
|
||||
.quad 0
|
||||
.endr
|
||||
.Lfpctl:
|
||||
.long 0
|
||||
.Lprefix:
|
||||
.long 0
|
||||
.Lprefix_zero:
|
||||
.long 0
|
||||
.previous
|
||||
|
@@ -13,6 +13,7 @@
|
||||
#include <linux/debugfs.h>
|
||||
#include <asm/diag.h>
|
||||
#include <asm/trace/diag.h>
|
||||
#include <asm/sections.h>
|
||||
|
||||
struct diag_stat {
|
||||
unsigned int counter[NR_DIAG_STAT];
|
||||
@@ -49,6 +50,9 @@ static const struct diag_desc diag_map[NR_DIAG_STAT] = {
|
||||
[DIAG_STAT_X500] = { .code = 0x500, .name = "Virtio Service" },
|
||||
};
|
||||
|
||||
struct diag_ops __bootdata_preserved(diag_dma_ops);
|
||||
struct diag210 *__bootdata_preserved(__diag210_tmp_dma);
|
||||
|
||||
static int show_diag_stat(struct seq_file *m, void *v)
|
||||
{
|
||||
struct diag_stat *stat;
|
||||
@@ -139,30 +143,10 @@ EXPORT_SYMBOL(diag_stat_inc_norecursion);
|
||||
/*
|
||||
* Diagnose 14: Input spool file manipulation
|
||||
*/
|
||||
static inline int __diag14(unsigned long rx, unsigned long ry1,
|
||||
unsigned long subcode)
|
||||
{
|
||||
register unsigned long _ry1 asm("2") = ry1;
|
||||
register unsigned long _ry2 asm("3") = subcode;
|
||||
int rc = 0;
|
||||
|
||||
asm volatile(
|
||||
" sam31\n"
|
||||
" diag %2,2,0x14\n"
|
||||
" sam64\n"
|
||||
" ipm %0\n"
|
||||
" srl %0,28\n"
|
||||
: "=d" (rc), "+d" (_ry2)
|
||||
: "d" (rx), "d" (_ry1)
|
||||
: "cc");
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int diag14(unsigned long rx, unsigned long ry1, unsigned long subcode)
|
||||
{
|
||||
diag_stat_inc(DIAG_STAT_X014);
|
||||
return __diag14(rx, ry1, subcode);
|
||||
return diag_dma_ops.diag14(rx, ry1, subcode);
|
||||
}
|
||||
EXPORT_SYMBOL(diag14);
|
||||
|
||||
@@ -195,30 +179,17 @@ EXPORT_SYMBOL(diag204);
|
||||
*/
|
||||
int diag210(struct diag210 *addr)
|
||||
{
|
||||
/*
|
||||
* diag 210 needs its data below the 2GB border, so we
|
||||
* use a static data area to be sure
|
||||
*/
|
||||
static struct diag210 diag210_tmp;
|
||||
static DEFINE_SPINLOCK(diag210_lock);
|
||||
unsigned long flags;
|
||||
int ccode;
|
||||
|
||||
spin_lock_irqsave(&diag210_lock, flags);
|
||||
diag210_tmp = *addr;
|
||||
*__diag210_tmp_dma = *addr;
|
||||
|
||||
diag_stat_inc(DIAG_STAT_X210);
|
||||
asm volatile(
|
||||
" lhi %0,-1\n"
|
||||
" sam31\n"
|
||||
" diag %1,0,0x210\n"
|
||||
"0: ipm %0\n"
|
||||
" srl %0,28\n"
|
||||
"1: sam64\n"
|
||||
EX_TABLE(0b, 1b)
|
||||
: "=&d" (ccode) : "a" (&diag210_tmp) : "cc", "memory");
|
||||
ccode = diag_dma_ops.diag210(__diag210_tmp_dma);
|
||||
|
||||
*addr = diag210_tmp;
|
||||
*addr = *__diag210_tmp_dma;
|
||||
spin_unlock_irqrestore(&diag210_lock, flags);
|
||||
|
||||
return ccode;
|
||||
@@ -243,27 +214,9 @@ EXPORT_SYMBOL(diag224);
|
||||
/*
|
||||
* Diagnose 26C: Access Certain System Information
|
||||
*/
|
||||
static inline int __diag26c(void *req, void *resp, enum diag26c_sc subcode)
|
||||
{
|
||||
register unsigned long _req asm("2") = (addr_t) req;
|
||||
register unsigned long _resp asm("3") = (addr_t) resp;
|
||||
register unsigned long _subcode asm("4") = subcode;
|
||||
register unsigned long _rc asm("5") = -EOPNOTSUPP;
|
||||
|
||||
asm volatile(
|
||||
" sam31\n"
|
||||
" diag %[rx],%[ry],0x26c\n"
|
||||
"0: sam64\n"
|
||||
EX_TABLE(0b,0b)
|
||||
: "+d" (_rc)
|
||||
: [rx] "d" (_req), "d" (_resp), [ry] "d" (_subcode)
|
||||
: "cc", "memory");
|
||||
return _rc;
|
||||
}
|
||||
|
||||
int diag26c(void *req, void *resp, enum diag26c_sc subcode)
|
||||
{
|
||||
diag_stat_inc(DIAG_STAT_X26C);
|
||||
return __diag26c(req, resp, subcode);
|
||||
return diag_dma_ops.diag26c(req, resp, subcode);
|
||||
}
|
||||
EXPORT_SYMBOL(diag26c);
|
||||
|
@@ -21,95 +21,124 @@
|
||||
#include <asm/debug.h>
|
||||
#include <asm/dis.h>
|
||||
#include <asm/ipl.h>
|
||||
#include <asm/unwind.h>
|
||||
|
||||
/*
|
||||
* For dump_trace we have tree different stack to consider:
|
||||
* - the panic stack which is used if the kernel stack has overflown
|
||||
* - the asynchronous interrupt stack (cpu related)
|
||||
* - the synchronous kernel stack (process related)
|
||||
* The stack trace can start at any of the three stacks and can potentially
|
||||
* touch all of them. The order is: panic stack, async stack, sync stack.
|
||||
*/
|
||||
static unsigned long __no_sanitize_address
|
||||
__dump_trace(dump_trace_func_t func, void *data, unsigned long sp,
|
||||
unsigned long low, unsigned long high)
|
||||
const char *stack_type_name(enum stack_type type)
|
||||
{
|
||||
struct stack_frame *sf;
|
||||
struct pt_regs *regs;
|
||||
|
||||
while (1) {
|
||||
if (sp < low || sp > high - sizeof(*sf))
|
||||
return sp;
|
||||
sf = (struct stack_frame *) sp;
|
||||
if (func(data, sf->gprs[8], 0))
|
||||
return sp;
|
||||
/* Follow the backchain. */
|
||||
while (1) {
|
||||
low = sp;
|
||||
sp = sf->back_chain;
|
||||
if (!sp)
|
||||
break;
|
||||
if (sp <= low || sp > high - sizeof(*sf))
|
||||
return sp;
|
||||
sf = (struct stack_frame *) sp;
|
||||
if (func(data, sf->gprs[8], 1))
|
||||
return sp;
|
||||
}
|
||||
/* Zero backchain detected, check for interrupt frame. */
|
||||
sp = (unsigned long) (sf + 1);
|
||||
if (sp <= low || sp > high - sizeof(*regs))
|
||||
return sp;
|
||||
regs = (struct pt_regs *) sp;
|
||||
if (!user_mode(regs)) {
|
||||
if (func(data, regs->psw.addr, 1))
|
||||
return sp;
|
||||
}
|
||||
low = sp;
|
||||
sp = regs->gprs[15];
|
||||
switch (type) {
|
||||
case STACK_TYPE_TASK:
|
||||
return "task";
|
||||
case STACK_TYPE_IRQ:
|
||||
return "irq";
|
||||
case STACK_TYPE_NODAT:
|
||||
return "nodat";
|
||||
case STACK_TYPE_RESTART:
|
||||
return "restart";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
void dump_trace(dump_trace_func_t func, void *data, struct task_struct *task,
|
||||
unsigned long sp)
|
||||
static inline bool in_stack(unsigned long sp, struct stack_info *info,
|
||||
enum stack_type type, unsigned long low,
|
||||
unsigned long high)
|
||||
{
|
||||
unsigned long frame_size;
|
||||
if (sp < low || sp >= high)
|
||||
return false;
|
||||
info->type = type;
|
||||
info->begin = low;
|
||||
info->end = high;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool in_task_stack(unsigned long sp, struct task_struct *task,
|
||||
struct stack_info *info)
|
||||
{
|
||||
unsigned long stack;
|
||||
|
||||
stack = (unsigned long) task_stack_page(task);
|
||||
return in_stack(sp, info, STACK_TYPE_TASK, stack, stack + THREAD_SIZE);
|
||||
}
|
||||
|
||||
static bool in_irq_stack(unsigned long sp, struct stack_info *info)
|
||||
{
|
||||
unsigned long frame_size, top;
|
||||
|
||||
frame_size = STACK_FRAME_OVERHEAD + sizeof(struct pt_regs);
|
||||
#ifdef CONFIG_CHECK_STACK
|
||||
sp = __dump_trace(func, data, sp,
|
||||
S390_lowcore.nodat_stack + frame_size - THREAD_SIZE,
|
||||
S390_lowcore.nodat_stack + frame_size);
|
||||
#endif
|
||||
sp = __dump_trace(func, data, sp,
|
||||
S390_lowcore.async_stack + frame_size - THREAD_SIZE,
|
||||
S390_lowcore.async_stack + frame_size);
|
||||
task = task ?: current;
|
||||
__dump_trace(func, data, sp,
|
||||
(unsigned long)task_stack_page(task),
|
||||
(unsigned long)task_stack_page(task) + THREAD_SIZE);
|
||||
top = S390_lowcore.async_stack + frame_size;
|
||||
return in_stack(sp, info, STACK_TYPE_IRQ, top - THREAD_SIZE, top);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dump_trace);
|
||||
|
||||
static int show_address(void *data, unsigned long address, int reliable)
|
||||
static bool in_nodat_stack(unsigned long sp, struct stack_info *info)
|
||||
{
|
||||
if (reliable)
|
||||
printk(" [<%016lx>] %pSR \n", address, (void *)address);
|
||||
else
|
||||
printk("([<%016lx>] %pSR)\n", address, (void *)address);
|
||||
unsigned long frame_size, top;
|
||||
|
||||
frame_size = STACK_FRAME_OVERHEAD + sizeof(struct pt_regs);
|
||||
top = S390_lowcore.nodat_stack + frame_size;
|
||||
return in_stack(sp, info, STACK_TYPE_NODAT, top - THREAD_SIZE, top);
|
||||
}
|
||||
|
||||
static bool in_restart_stack(unsigned long sp, struct stack_info *info)
|
||||
{
|
||||
unsigned long frame_size, top;
|
||||
|
||||
frame_size = STACK_FRAME_OVERHEAD + sizeof(struct pt_regs);
|
||||
top = S390_lowcore.restart_stack + frame_size;
|
||||
return in_stack(sp, info, STACK_TYPE_RESTART, top - THREAD_SIZE, top);
|
||||
}
|
||||
|
||||
int get_stack_info(unsigned long sp, struct task_struct *task,
|
||||
struct stack_info *info, unsigned long *visit_mask)
|
||||
{
|
||||
if (!sp)
|
||||
goto unknown;
|
||||
|
||||
task = task ? : current;
|
||||
|
||||
/* Check per-task stack */
|
||||
if (in_task_stack(sp, task, info))
|
||||
goto recursion_check;
|
||||
|
||||
if (task != current)
|
||||
goto unknown;
|
||||
|
||||
/* Check per-cpu stacks */
|
||||
if (!in_irq_stack(sp, info) &&
|
||||
!in_nodat_stack(sp, info) &&
|
||||
!in_restart_stack(sp, info))
|
||||
goto unknown;
|
||||
|
||||
recursion_check:
|
||||
/*
|
||||
* Make sure we don't iterate through any given stack more than once.
|
||||
* If it comes up a second time then there's something wrong going on:
|
||||
* just break out and report an unknown stack type.
|
||||
*/
|
||||
if (*visit_mask & (1UL << info->type)) {
|
||||
printk_deferred_once(KERN_WARNING
|
||||
"WARNING: stack recursion on stack type %d\n",
|
||||
info->type);
|
||||
goto unknown;
|
||||
}
|
||||
*visit_mask |= 1UL << info->type;
|
||||
return 0;
|
||||
unknown:
|
||||
info->type = STACK_TYPE_UNKNOWN;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
void show_stack(struct task_struct *task, unsigned long *stack)
|
||||
{
|
||||
unsigned long sp = (unsigned long) stack;
|
||||
struct unwind_state state;
|
||||
|
||||
if (!sp)
|
||||
sp = task ? task->thread.ksp : current_stack_pointer();
|
||||
printk("Call Trace:\n");
|
||||
dump_trace(show_address, NULL, task, sp);
|
||||
if (!task)
|
||||
task = current;
|
||||
debug_show_held_locks(task);
|
||||
unwind_for_each_frame(&state, task, NULL, (unsigned long) stack)
|
||||
printk(state.reliable ? " [<%016lx>] %pSR \n" :
|
||||
"([<%016lx>] %pSR)\n",
|
||||
state.ip, (void *) state.ip);
|
||||
debug_show_held_locks(task ? : current);
|
||||
}
|
||||
|
||||
static void show_last_breaking_event(struct pt_regs *regs)
|
||||
|
@@ -30,6 +30,7 @@
|
||||
#include <asm/sclp.h>
|
||||
#include <asm/facility.h>
|
||||
#include <asm/boot_data.h>
|
||||
#include <asm/pci_insn.h>
|
||||
#include "entry.h"
|
||||
|
||||
/*
|
||||
@@ -138,9 +139,9 @@ static void early_pgm_check_handler(void)
|
||||
unsigned long addr;
|
||||
|
||||
addr = S390_lowcore.program_old_psw.addr;
|
||||
fixup = search_exception_tables(addr);
|
||||
fixup = s390_search_extables(addr);
|
||||
if (!fixup)
|
||||
disabled_wait(0);
|
||||
disabled_wait();
|
||||
/* Disable low address protection before storing into lowcore. */
|
||||
__ctl_store(cr0, 0, 0);
|
||||
cr0_new = cr0 & ~(1UL << 28);
|
||||
@@ -235,6 +236,7 @@ static __init void detect_machine_facilities(void)
|
||||
clock_comparator_max = -1ULL >> 1;
|
||||
__ctl_set_bit(0, 53);
|
||||
}
|
||||
enable_mio_ctl();
|
||||
}
|
||||
|
||||
static inline void save_vector_registers(void)
|
||||
@@ -296,7 +298,7 @@ static void __init check_image_bootable(void)
|
||||
sclp_early_printk("Linux kernel boot failure: An attempt to boot a vmlinux ELF image failed.\n");
|
||||
sclp_early_printk("This image does not contain all parts necessary for starting up. Use\n");
|
||||
sclp_early_printk("bzImage or arch/s390/boot/compressed/vmlinux instead.\n");
|
||||
disabled_wait(0xbadb007);
|
||||
disabled_wait();
|
||||
}
|
||||
|
||||
void __init startup_init(void)
|
||||
@@ -309,7 +311,6 @@ void __init startup_init(void)
|
||||
setup_facility_list();
|
||||
detect_machine_type();
|
||||
setup_arch_string();
|
||||
ipl_store_parameters();
|
||||
setup_boot_command_line();
|
||||
detect_diag9c();
|
||||
detect_diag44();
|
||||
|
@@ -25,7 +25,7 @@ static void __init reset_tod_clock(void)
|
||||
return;
|
||||
/* TOD clock not running. Set the clock to Unix Epoch. */
|
||||
if (set_tod_clock(TOD_UNIX_EPOCH) != 0 || store_tod_clock(&time) != 0)
|
||||
disabled_wait(0);
|
||||
disabled_wait();
|
||||
|
||||
memset(tod_clock_base, 0, 16);
|
||||
*(__u64 *) &tod_clock_base[1] = TOD_UNIX_EPOCH;
|
||||
|
@@ -224,6 +224,7 @@ ENTRY(__bpon)
|
||||
.globl __bpon
|
||||
BPON
|
||||
BR_EX %r14
|
||||
ENDPROC(__bpon)
|
||||
|
||||
/*
|
||||
* Scheduler resume function, called by switch_to
|
||||
@@ -248,6 +249,7 @@ ENTRY(__switch_to)
|
||||
lmg %r6,%r15,__SF_GPRS(%r15) # load gprs of next task
|
||||
ALTERNATIVE "", ".insn s,0xb2800000,_LPP_OFFSET", 40
|
||||
BR_EX %r14
|
||||
ENDPROC(__switch_to)
|
||||
|
||||
.L__critical_start:
|
||||
|
||||
@@ -324,6 +326,7 @@ sie_exit:
|
||||
EX_TABLE(.Lrewind_pad4,.Lsie_fault)
|
||||
EX_TABLE(.Lrewind_pad2,.Lsie_fault)
|
||||
EX_TABLE(sie_exit,.Lsie_fault)
|
||||
ENDPROC(sie64a)
|
||||
EXPORT_SYMBOL(sie64a)
|
||||
EXPORT_SYMBOL(sie_exit)
|
||||
#endif
|
||||
@@ -358,19 +361,19 @@ ENTRY(system_call)
|
||||
# load address of system call table
|
||||
lg %r10,__THREAD_sysc_table(%r13,%r12)
|
||||
llgh %r8,__PT_INT_CODE+2(%r11)
|
||||
slag %r8,%r8,2 # shift and test for svc 0
|
||||
slag %r8,%r8,3 # shift and test for svc 0
|
||||
jnz .Lsysc_nr_ok
|
||||
# svc 0: system call number in %r1
|
||||
llgfr %r1,%r1 # clear high word in r1
|
||||
cghi %r1,NR_syscalls
|
||||
jnl .Lsysc_nr_ok
|
||||
sth %r1,__PT_INT_CODE+2(%r11)
|
||||
slag %r8,%r1,2
|
||||
slag %r8,%r1,3
|
||||
.Lsysc_nr_ok:
|
||||
xc __SF_BACKCHAIN(8,%r15),__SF_BACKCHAIN(%r15)
|
||||
stg %r2,__PT_ORIG_GPR2(%r11)
|
||||
stg %r7,STACK_FRAME_OVERHEAD(%r15)
|
||||
lgf %r9,0(%r8,%r10) # get system call add.
|
||||
lg %r9,0(%r8,%r10) # get system call add.
|
||||
TSTMSK __TI_flags(%r12),_TIF_TRACE
|
||||
jnz .Lsysc_tracesys
|
||||
BASR_EX %r14,%r9 # call sys_xxxx
|
||||
@@ -556,8 +559,8 @@ ENTRY(system_call)
|
||||
lghi %r0,NR_syscalls
|
||||
clgr %r0,%r2
|
||||
jnh .Lsysc_tracenogo
|
||||
sllg %r8,%r2,2
|
||||
lgf %r9,0(%r8,%r10)
|
||||
sllg %r8,%r2,3
|
||||
lg %r9,0(%r8,%r10)
|
||||
.Lsysc_tracego:
|
||||
lmg %r3,%r7,__PT_R3(%r11)
|
||||
stg %r7,STACK_FRAME_OVERHEAD(%r15)
|
||||
@@ -570,6 +573,7 @@ ENTRY(system_call)
|
||||
lgr %r2,%r11 # pass pointer to pt_regs
|
||||
larl %r14,.Lsysc_return
|
||||
jg do_syscall_trace_exit
|
||||
ENDPROC(system_call)
|
||||
|
||||
#
|
||||
# a new process exits the kernel with ret_from_fork
|
||||
@@ -584,10 +588,16 @@ ENTRY(ret_from_fork)
|
||||
jne .Lsysc_tracenogo
|
||||
# it's a kernel thread
|
||||
lmg %r9,%r10,__PT_R9(%r11) # load gprs
|
||||
la %r2,0(%r10)
|
||||
BASR_EX %r14,%r9
|
||||
j .Lsysc_tracenogo
|
||||
ENDPROC(ret_from_fork)
|
||||
|
||||
ENTRY(kernel_thread_starter)
|
||||
la %r2,0(%r10)
|
||||
BASR_EX %r14,%r9
|
||||
j .Lsysc_tracenogo
|
||||
ENDPROC(kernel_thread_starter)
|
||||
|
||||
/*
|
||||
* Program check handler routine
|
||||
@@ -665,9 +675,9 @@ ENTRY(pgm_check_handler)
|
||||
larl %r1,pgm_check_table
|
||||
llgh %r10,__PT_INT_CODE+2(%r11)
|
||||
nill %r10,0x007f
|
||||
sll %r10,2
|
||||
sll %r10,3
|
||||
je .Lpgm_return
|
||||
lgf %r9,0(%r10,%r1) # load address of handler routine
|
||||
lg %r9,0(%r10,%r1) # load address of handler routine
|
||||
lgr %r2,%r11 # pass pointer to pt_regs
|
||||
BASR_EX %r14,%r9 # branch to interrupt-handler
|
||||
.Lpgm_return:
|
||||
@@ -698,6 +708,7 @@ ENTRY(pgm_check_handler)
|
||||
stg %r14,__LC_RETURN_PSW+8
|
||||
lghi %r14,_PIF_SYSCALL | _PIF_PER_TRAP
|
||||
lpswe __LC_RETURN_PSW # branch to .Lsysc_per and enable irqs
|
||||
ENDPROC(pgm_check_handler)
|
||||
|
||||
/*
|
||||
* IO interrupt handler routine
|
||||
@@ -926,6 +937,7 @@ ENTRY(io_int_handler)
|
||||
ssm __LC_PGM_NEW_PSW # disable I/O and ext. interrupts
|
||||
TRACE_IRQS_OFF
|
||||
j .Lio_return
|
||||
ENDPROC(io_int_handler)
|
||||
|
||||
/*
|
||||
* External interrupt handler routine
|
||||
@@ -965,6 +977,7 @@ ENTRY(ext_int_handler)
|
||||
lghi %r3,EXT_INTERRUPT
|
||||
brasl %r14,do_IRQ
|
||||
j .Lio_return
|
||||
ENDPROC(ext_int_handler)
|
||||
|
||||
/*
|
||||
* Load idle PSW. The second "half" of this function is in .Lcleanup_idle.
|
||||
@@ -989,6 +1002,7 @@ ENTRY(psw_idle)
|
||||
lpswe __SF_EMPTY(%r15)
|
||||
BR_EX %r14
|
||||
.Lpsw_idle_end:
|
||||
ENDPROC(psw_idle)
|
||||
|
||||
/*
|
||||
* Store floating-point controls and floating-point or vector register
|
||||
@@ -1031,6 +1045,7 @@ ENTRY(save_fpu_regs)
|
||||
.Lsave_fpu_regs_exit:
|
||||
BR_EX %r14
|
||||
.Lsave_fpu_regs_end:
|
||||
ENDPROC(save_fpu_regs)
|
||||
EXPORT_SYMBOL(save_fpu_regs)
|
||||
|
||||
/*
|
||||
@@ -1077,6 +1092,7 @@ load_fpu_regs:
|
||||
.Lload_fpu_regs_exit:
|
||||
BR_EX %r14
|
||||
.Lload_fpu_regs_end:
|
||||
ENDPROC(load_fpu_regs)
|
||||
|
||||
.L__critical_end:
|
||||
|
||||
@@ -1206,6 +1222,7 @@ ENTRY(mcck_int_handler)
|
||||
lg %r15,__LC_NODAT_STACK
|
||||
la %r11,STACK_FRAME_OVERHEAD(%r15)
|
||||
j .Lmcck_skip
|
||||
ENDPROC(mcck_int_handler)
|
||||
|
||||
#
|
||||
# PSW restart interrupt handler
|
||||
@@ -1232,6 +1249,7 @@ ENTRY(restart_int_handler)
|
||||
2: sigp %r4,%r3,SIGP_STOP # sigp stop to current cpu
|
||||
brc 2,2b
|
||||
3: j 3b
|
||||
ENDPROC(restart_int_handler)
|
||||
|
||||
.section .kprobes.text, "ax"
|
||||
|
||||
@@ -1241,7 +1259,7 @@ ENTRY(restart_int_handler)
|
||||
* No need to properly save the registers, we are going to panic anyway.
|
||||
* Setup a pt_regs so that show_trace can provide a good call trace.
|
||||
*/
|
||||
stack_overflow:
|
||||
ENTRY(stack_overflow)
|
||||
lg %r15,__LC_NODAT_STACK # change to panic stack
|
||||
la %r11,STACK_FRAME_OVERHEAD(%r15)
|
||||
stmg %r0,%r7,__PT_R0(%r11)
|
||||
@@ -1251,9 +1269,10 @@ stack_overflow:
|
||||
xc __SF_BACKCHAIN(8,%r15),__SF_BACKCHAIN(%r15)
|
||||
lgr %r2,%r11 # pass pointer to pt_regs
|
||||
jg kernel_stack_overflow
|
||||
ENDPROC(stack_overflow)
|
||||
#endif
|
||||
|
||||
cleanup_critical:
|
||||
ENTRY(cleanup_critical)
|
||||
#if IS_ENABLED(CONFIG_KVM)
|
||||
clg %r9,BASED(.Lcleanup_table_sie) # .Lsie_gmap
|
||||
jl 0f
|
||||
@@ -1289,6 +1308,7 @@ cleanup_critical:
|
||||
clg %r9,BASED(.Lcleanup_table+104) # .Lload_fpu_regs_end
|
||||
jl .Lcleanup_load_fpu_regs
|
||||
0: BR_EX %r14,%r11
|
||||
ENDPROC(cleanup_critical)
|
||||
|
||||
.align 8
|
||||
.Lcleanup_table:
|
||||
@@ -1512,7 +1532,7 @@ cleanup_critical:
|
||||
.quad .Lsie_skip - .Lsie_entry
|
||||
#endif
|
||||
.section .rodata, "a"
|
||||
#define SYSCALL(esame,emu) .long __s390x_ ## esame
|
||||
#define SYSCALL(esame,emu) .quad __s390x_ ## esame
|
||||
.globl sys_call_table
|
||||
sys_call_table:
|
||||
#include "asm/syscall_table.h"
|
||||
@@ -1520,7 +1540,7 @@ sys_call_table:
|
||||
|
||||
#ifdef CONFIG_COMPAT
|
||||
|
||||
#define SYSCALL(esame,emu) .long __s390_ ## emu
|
||||
#define SYSCALL(esame,emu) .quad __s390_ ## emu
|
||||
.globl sys_call_table_emu
|
||||
sys_call_table_emu:
|
||||
#include "asm/syscall_table.h"
|
||||
|
@@ -65,7 +65,7 @@ int setup_profiling_timer(unsigned int multiplier);
|
||||
void __init time_init(void);
|
||||
int pfn_is_nosave(unsigned long);
|
||||
void s390_early_resume(void);
|
||||
unsigned long prepare_ftrace_return(unsigned long parent, unsigned long ip);
|
||||
unsigned long prepare_ftrace_return(unsigned long parent, unsigned long sp, unsigned long ip);
|
||||
|
||||
struct s390_mmap_arg_struct;
|
||||
struct fadvise64_64_args;
|
||||
|
@@ -201,17 +201,18 @@ device_initcall(ftrace_plt_init);
|
||||
* Hook the return address and push it in the stack of return addresses
|
||||
* in current thread info.
|
||||
*/
|
||||
unsigned long prepare_ftrace_return(unsigned long parent, unsigned long ip)
|
||||
unsigned long prepare_ftrace_return(unsigned long ra, unsigned long sp,
|
||||
unsigned long ip)
|
||||
{
|
||||
if (unlikely(ftrace_graph_is_dead()))
|
||||
goto out;
|
||||
if (unlikely(atomic_read(¤t->tracing_graph_pause)))
|
||||
goto out;
|
||||
ip -= MCOUNT_INSN_SIZE;
|
||||
if (!function_graph_enter(parent, ip, 0, NULL))
|
||||
parent = (unsigned long) return_to_handler;
|
||||
if (!function_graph_enter(ra, ip, 0, (void *) sp))
|
||||
ra = (unsigned long) return_to_handler;
|
||||
out:
|
||||
return parent;
|
||||
return ra;
|
||||
}
|
||||
NOKPROBE_SYMBOL(prepare_ftrace_return);
|
||||
|
||||
|
@@ -26,7 +26,6 @@ ENTRY(startup_continue)
|
||||
0: larl %r1,tod_clock_base
|
||||
mvc 0(16,%r1),__LC_BOOT_CLOCK
|
||||
larl %r13,.LPG1 # get base
|
||||
lctlg %c0,%c15,.Lctl-.LPG1(%r13) # load control registers
|
||||
larl %r0,boot_vdso_data
|
||||
stg %r0,__LC_VDSO_PER_CPU
|
||||
#
|
||||
@@ -61,22 +60,6 @@ ENTRY(startup_continue)
|
||||
|
||||
.align 16
|
||||
.LPG1:
|
||||
.Lctl: .quad 0x04040000 # cr0: AFP registers & secondary space
|
||||
.quad 0 # cr1: primary space segment table
|
||||
.quad .Lduct # cr2: dispatchable unit control table
|
||||
.quad 0 # cr3: instruction authorization
|
||||
.quad 0xffff # cr4: instruction authorization
|
||||
.quad .Lduct # cr5: primary-aste origin
|
||||
.quad 0 # cr6: I/O interrupts
|
||||
.quad 0 # cr7: secondary space segment table
|
||||
.quad 0 # cr8: access registers translation
|
||||
.quad 0 # cr9: tracing off
|
||||
.quad 0 # cr10: tracing off
|
||||
.quad 0 # cr11: tracing off
|
||||
.quad 0 # cr12: tracing off
|
||||
.quad 0 # cr13: home space segment table
|
||||
.quad 0xc0000000 # cr14: machine check handling off
|
||||
.quad .Llinkage_stack # cr15: linkage stack operations
|
||||
.Lpcmsk:.quad 0x0000000180000000
|
||||
.L4malign:.quad 0xffffffffffc00000
|
||||
.Lscan2g:.quad 0x80000000 + 0x20000 - 8 # 2GB + 128K - 8
|
||||
@@ -84,14 +67,5 @@ ENTRY(startup_continue)
|
||||
.Lparmaddr:
|
||||
.quad PARMAREA
|
||||
.align 64
|
||||
.Lduct: .long 0,.Laste,.Laste,0,.Lduald,0,0,0
|
||||
.long 0,0,0,0,0,0,0,0
|
||||
.Laste: .quad 0,0xffffffffffffffff,0,0,0,0,0,0
|
||||
.align 128
|
||||
.Lduald:.rept 8
|
||||
.long 0x80000000,0,0,0 # invalid access-list entries
|
||||
.endr
|
||||
.Llinkage_stack:
|
||||
.long 0,0,0x89000000,0,0,0,0x8a000000,0
|
||||
.Ldw: .quad 0x0002000180000000,0x0000000000000000
|
||||
.Laregs:.long 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
||||
|
14
arch/s390/kernel/ima_arch.c
Normal file
14
arch/s390/kernel/ima_arch.c
Normal file
@@ -0,0 +1,14 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
#include <linux/ima.h>
|
||||
#include <asm/boot_data.h>
|
||||
|
||||
bool arch_ima_get_secureboot(void)
|
||||
{
|
||||
return ipl_secure_flag;
|
||||
}
|
||||
|
||||
const char * const *arch_get_ima_policy(void)
|
||||
{
|
||||
return NULL;
|
||||
}
|
@@ -31,6 +31,7 @@
|
||||
#include <asm/os_info.h>
|
||||
#include <asm/sections.h>
|
||||
#include <asm/boot_data.h>
|
||||
#include <asm/uv.h>
|
||||
#include "entry.h"
|
||||
|
||||
#define IPL_PARM_BLOCK_VERSION 0
|
||||
@@ -119,11 +120,15 @@ static char *dump_type_str(enum dump_type type)
|
||||
}
|
||||
}
|
||||
|
||||
struct ipl_parameter_block __bootdata(early_ipl_block);
|
||||
int __bootdata(early_ipl_block_valid);
|
||||
int __bootdata_preserved(ipl_block_valid);
|
||||
struct ipl_parameter_block __bootdata_preserved(ipl_block);
|
||||
int __bootdata_preserved(ipl_secure_flag);
|
||||
|
||||
static int ipl_block_valid;
|
||||
static struct ipl_parameter_block ipl_block;
|
||||
unsigned long __bootdata_preserved(ipl_cert_list_addr);
|
||||
unsigned long __bootdata_preserved(ipl_cert_list_size);
|
||||
|
||||
unsigned long __bootdata(early_ipl_comp_list_addr);
|
||||
unsigned long __bootdata(early_ipl_comp_list_size);
|
||||
|
||||
static int reipl_capabilities = IPL_TYPE_UNKNOWN;
|
||||
|
||||
@@ -246,11 +251,11 @@ static __init enum ipl_type get_ipl_type(void)
|
||||
if (!ipl_block_valid)
|
||||
return IPL_TYPE_UNKNOWN;
|
||||
|
||||
switch (ipl_block.hdr.pbt) {
|
||||
case DIAG308_IPL_TYPE_CCW:
|
||||
switch (ipl_block.pb0_hdr.pbt) {
|
||||
case IPL_PBT_CCW:
|
||||
return IPL_TYPE_CCW;
|
||||
case DIAG308_IPL_TYPE_FCP:
|
||||
if (ipl_block.ipl_info.fcp.opt == DIAG308_IPL_OPT_DUMP)
|
||||
case IPL_PBT_FCP:
|
||||
if (ipl_block.fcp.opt == IPL_PB0_FCP_OPT_DUMP)
|
||||
return IPL_TYPE_FCP_DUMP;
|
||||
else
|
||||
return IPL_TYPE_FCP;
|
||||
@@ -269,12 +274,35 @@ static ssize_t ipl_type_show(struct kobject *kobj, struct kobj_attribute *attr,
|
||||
|
||||
static struct kobj_attribute sys_ipl_type_attr = __ATTR_RO(ipl_type);
|
||||
|
||||
static ssize_t ipl_secure_show(struct kobject *kobj,
|
||||
struct kobj_attribute *attr, char *page)
|
||||
{
|
||||
return sprintf(page, "%i\n", !!ipl_secure_flag);
|
||||
}
|
||||
|
||||
static struct kobj_attribute sys_ipl_secure_attr =
|
||||
__ATTR(secure, 0444, ipl_secure_show, NULL);
|
||||
|
||||
static ssize_t ipl_has_secure_show(struct kobject *kobj,
|
||||
struct kobj_attribute *attr, char *page)
|
||||
{
|
||||
if (MACHINE_IS_LPAR)
|
||||
return sprintf(page, "%i\n", !!sclp.has_sipl);
|
||||
else if (MACHINE_IS_VM)
|
||||
return sprintf(page, "%i\n", !!sclp.has_sipl_g2);
|
||||
else
|
||||
return sprintf(page, "%i\n", 0);
|
||||
}
|
||||
|
||||
static struct kobj_attribute sys_ipl_has_secure_attr =
|
||||
__ATTR(has_secure, 0444, ipl_has_secure_show, NULL);
|
||||
|
||||
static ssize_t ipl_vm_parm_show(struct kobject *kobj,
|
||||
struct kobj_attribute *attr, char *page)
|
||||
{
|
||||
char parm[DIAG308_VMPARM_SIZE + 1] = {};
|
||||
|
||||
if (ipl_block_valid && (ipl_block.hdr.pbt == DIAG308_IPL_TYPE_CCW))
|
||||
if (ipl_block_valid && (ipl_block.pb0_hdr.pbt == IPL_PBT_CCW))
|
||||
ipl_block_get_ascii_vmparm(parm, sizeof(parm), &ipl_block);
|
||||
return sprintf(page, "%s\n", parm);
|
||||
}
|
||||
@@ -287,12 +315,11 @@ static ssize_t sys_ipl_device_show(struct kobject *kobj,
|
||||
{
|
||||
switch (ipl_info.type) {
|
||||
case IPL_TYPE_CCW:
|
||||
return sprintf(page, "0.%x.%04x\n", ipl_block.ipl_info.ccw.ssid,
|
||||
ipl_block.ipl_info.ccw.devno);
|
||||
return sprintf(page, "0.%x.%04x\n", ipl_block.ccw.ssid,
|
||||
ipl_block.ccw.devno);
|
||||
case IPL_TYPE_FCP:
|
||||
case IPL_TYPE_FCP_DUMP:
|
||||
return sprintf(page, "0.0.%04x\n",
|
||||
ipl_block.ipl_info.fcp.devno);
|
||||
return sprintf(page, "0.0.%04x\n", ipl_block.fcp.devno);
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
@@ -316,8 +343,8 @@ static ssize_t ipl_scp_data_read(struct file *filp, struct kobject *kobj,
|
||||
struct bin_attribute *attr, char *buf,
|
||||
loff_t off, size_t count)
|
||||
{
|
||||
unsigned int size = ipl_block.ipl_info.fcp.scp_data_len;
|
||||
void *scp_data = &ipl_block.ipl_info.fcp.scp_data;
|
||||
unsigned int size = ipl_block.fcp.scp_data_len;
|
||||
void *scp_data = &ipl_block.fcp.scp_data;
|
||||
|
||||
return memory_read_from_buffer(buf, count, &off, scp_data, size);
|
||||
}
|
||||
@@ -333,13 +360,13 @@ static struct bin_attribute *ipl_fcp_bin_attrs[] = {
|
||||
/* FCP ipl device attributes */
|
||||
|
||||
DEFINE_IPL_ATTR_RO(ipl_fcp, wwpn, "0x%016llx\n",
|
||||
(unsigned long long)ipl_block.ipl_info.fcp.wwpn);
|
||||
(unsigned long long)ipl_block.fcp.wwpn);
|
||||
DEFINE_IPL_ATTR_RO(ipl_fcp, lun, "0x%016llx\n",
|
||||
(unsigned long long)ipl_block.ipl_info.fcp.lun);
|
||||
(unsigned long long)ipl_block.fcp.lun);
|
||||
DEFINE_IPL_ATTR_RO(ipl_fcp, bootprog, "%lld\n",
|
||||
(unsigned long long)ipl_block.ipl_info.fcp.bootprog);
|
||||
(unsigned long long)ipl_block.fcp.bootprog);
|
||||
DEFINE_IPL_ATTR_RO(ipl_fcp, br_lba, "%lld\n",
|
||||
(unsigned long long)ipl_block.ipl_info.fcp.br_lba);
|
||||
(unsigned long long)ipl_block.fcp.br_lba);
|
||||
|
||||
static ssize_t ipl_ccw_loadparm_show(struct kobject *kobj,
|
||||
struct kobj_attribute *attr, char *page)
|
||||
@@ -365,6 +392,8 @@ static struct attribute *ipl_fcp_attrs[] = {
|
||||
&sys_ipl_fcp_bootprog_attr.attr,
|
||||
&sys_ipl_fcp_br_lba_attr.attr,
|
||||
&sys_ipl_ccw_loadparm_attr.attr,
|
||||
&sys_ipl_secure_attr.attr,
|
||||
&sys_ipl_has_secure_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
@@ -380,6 +409,8 @@ static struct attribute *ipl_ccw_attrs_vm[] = {
|
||||
&sys_ipl_device_attr.attr,
|
||||
&sys_ipl_ccw_loadparm_attr.attr,
|
||||
&sys_ipl_vm_parm_attr.attr,
|
||||
&sys_ipl_secure_attr.attr,
|
||||
&sys_ipl_has_secure_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
@@ -387,6 +418,8 @@ static struct attribute *ipl_ccw_attrs_lpar[] = {
|
||||
&sys_ipl_type_attr.attr,
|
||||
&sys_ipl_device_attr.attr,
|
||||
&sys_ipl_ccw_loadparm_attr.attr,
|
||||
&sys_ipl_secure_attr.attr,
|
||||
&sys_ipl_has_secure_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
@@ -495,14 +528,14 @@ static ssize_t reipl_generic_vmparm_store(struct ipl_parameter_block *ipb,
|
||||
if (!(isalnum(buf[i]) || isascii(buf[i]) || isprint(buf[i])))
|
||||
return -EINVAL;
|
||||
|
||||
memset(ipb->ipl_info.ccw.vm_parm, 0, DIAG308_VMPARM_SIZE);
|
||||
ipb->ipl_info.ccw.vm_parm_len = ip_len;
|
||||
memset(ipb->ccw.vm_parm, 0, DIAG308_VMPARM_SIZE);
|
||||
ipb->ccw.vm_parm_len = ip_len;
|
||||
if (ip_len > 0) {
|
||||
ipb->ipl_info.ccw.vm_flags |= DIAG308_VM_FLAGS_VP_VALID;
|
||||
memcpy(ipb->ipl_info.ccw.vm_parm, buf, ip_len);
|
||||
ASCEBC(ipb->ipl_info.ccw.vm_parm, ip_len);
|
||||
ipb->ccw.vm_flags |= IPL_PB0_CCW_VM_FLAG_VP;
|
||||
memcpy(ipb->ccw.vm_parm, buf, ip_len);
|
||||
ASCEBC(ipb->ccw.vm_parm, ip_len);
|
||||
} else {
|
||||
ipb->ipl_info.ccw.vm_flags &= ~DIAG308_VM_FLAGS_VP_VALID;
|
||||
ipb->ccw.vm_flags &= ~IPL_PB0_CCW_VM_FLAG_VP;
|
||||
}
|
||||
|
||||
return len;
|
||||
@@ -549,8 +582,8 @@ static ssize_t reipl_fcp_scpdata_read(struct file *filp, struct kobject *kobj,
|
||||
struct bin_attribute *attr,
|
||||
char *buf, loff_t off, size_t count)
|
||||
{
|
||||
size_t size = reipl_block_fcp->ipl_info.fcp.scp_data_len;
|
||||
void *scp_data = reipl_block_fcp->ipl_info.fcp.scp_data;
|
||||
size_t size = reipl_block_fcp->fcp.scp_data_len;
|
||||
void *scp_data = reipl_block_fcp->fcp.scp_data;
|
||||
|
||||
return memory_read_from_buffer(buf, count, &off, scp_data, size);
|
||||
}
|
||||
@@ -566,17 +599,17 @@ static ssize_t reipl_fcp_scpdata_write(struct file *filp, struct kobject *kobj,
|
||||
if (off)
|
||||
return -EINVAL;
|
||||
|
||||
memcpy(reipl_block_fcp->ipl_info.fcp.scp_data, buf, count);
|
||||
memcpy(reipl_block_fcp->fcp.scp_data, buf, count);
|
||||
if (scpdata_len % 8) {
|
||||
padding = 8 - (scpdata_len % 8);
|
||||
memset(reipl_block_fcp->ipl_info.fcp.scp_data + scpdata_len,
|
||||
memset(reipl_block_fcp->fcp.scp_data + scpdata_len,
|
||||
0, padding);
|
||||
scpdata_len += padding;
|
||||
}
|
||||
|
||||
reipl_block_fcp->ipl_info.fcp.scp_data_len = scpdata_len;
|
||||
reipl_block_fcp->hdr.len = IPL_PARM_BLK_FCP_LEN + scpdata_len;
|
||||
reipl_block_fcp->hdr.blk0_len = IPL_PARM_BLK0_FCP_LEN + scpdata_len;
|
||||
reipl_block_fcp->hdr.len = IPL_BP_FCP_LEN + scpdata_len;
|
||||
reipl_block_fcp->fcp.len = IPL_BP0_FCP_LEN + scpdata_len;
|
||||
reipl_block_fcp->fcp.scp_data_len = scpdata_len;
|
||||
|
||||
return count;
|
||||
}
|
||||
@@ -590,20 +623,20 @@ static struct bin_attribute *reipl_fcp_bin_attrs[] = {
|
||||
};
|
||||
|
||||
DEFINE_IPL_ATTR_RW(reipl_fcp, wwpn, "0x%016llx\n", "%llx\n",
|
||||
reipl_block_fcp->ipl_info.fcp.wwpn);
|
||||
reipl_block_fcp->fcp.wwpn);
|
||||
DEFINE_IPL_ATTR_RW(reipl_fcp, lun, "0x%016llx\n", "%llx\n",
|
||||
reipl_block_fcp->ipl_info.fcp.lun);
|
||||
reipl_block_fcp->fcp.lun);
|
||||
DEFINE_IPL_ATTR_RW(reipl_fcp, bootprog, "%lld\n", "%lld\n",
|
||||
reipl_block_fcp->ipl_info.fcp.bootprog);
|
||||
reipl_block_fcp->fcp.bootprog);
|
||||
DEFINE_IPL_ATTR_RW(reipl_fcp, br_lba, "%lld\n", "%lld\n",
|
||||
reipl_block_fcp->ipl_info.fcp.br_lba);
|
||||
reipl_block_fcp->fcp.br_lba);
|
||||
DEFINE_IPL_ATTR_RW(reipl_fcp, device, "0.0.%04llx\n", "0.0.%llx\n",
|
||||
reipl_block_fcp->ipl_info.fcp.devno);
|
||||
reipl_block_fcp->fcp.devno);
|
||||
|
||||
static void reipl_get_ascii_loadparm(char *loadparm,
|
||||
struct ipl_parameter_block *ibp)
|
||||
{
|
||||
memcpy(loadparm, ibp->hdr.loadparm, LOADPARM_LEN);
|
||||
memcpy(loadparm, ibp->common.loadparm, LOADPARM_LEN);
|
||||
EBCASC(loadparm, LOADPARM_LEN);
|
||||
loadparm[LOADPARM_LEN] = 0;
|
||||
strim(loadparm);
|
||||
@@ -638,11 +671,11 @@ static ssize_t reipl_generic_loadparm_store(struct ipl_parameter_block *ipb,
|
||||
return -EINVAL;
|
||||
}
|
||||
/* initialize loadparm with blanks */
|
||||
memset(ipb->hdr.loadparm, ' ', LOADPARM_LEN);
|
||||
memset(ipb->common.loadparm, ' ', LOADPARM_LEN);
|
||||
/* copy and convert to ebcdic */
|
||||
memcpy(ipb->hdr.loadparm, buf, lp_len);
|
||||
ASCEBC(ipb->hdr.loadparm, LOADPARM_LEN);
|
||||
ipb->hdr.flags |= DIAG308_FLAGS_LP_VALID;
|
||||
memcpy(ipb->common.loadparm, buf, lp_len);
|
||||
ASCEBC(ipb->common.loadparm, LOADPARM_LEN);
|
||||
ipb->common.flags |= IPL_PB0_FLAG_LOADPARM;
|
||||
return len;
|
||||
}
|
||||
|
||||
@@ -680,7 +713,7 @@ static struct attribute_group reipl_fcp_attr_group = {
|
||||
};
|
||||
|
||||
/* CCW reipl device attributes */
|
||||
DEFINE_IPL_CCW_ATTR_RW(reipl_ccw, device, reipl_block_ccw->ipl_info.ccw);
|
||||
DEFINE_IPL_CCW_ATTR_RW(reipl_ccw, device, reipl_block_ccw->ccw);
|
||||
|
||||
/* NSS wrapper */
|
||||
static ssize_t reipl_nss_loadparm_show(struct kobject *kobj,
|
||||
@@ -742,7 +775,7 @@ static struct attribute_group reipl_ccw_attr_group_lpar = {
|
||||
static void reipl_get_ascii_nss_name(char *dst,
|
||||
struct ipl_parameter_block *ipb)
|
||||
{
|
||||
memcpy(dst, ipb->ipl_info.ccw.nss_name, NSS_NAME_SIZE);
|
||||
memcpy(dst, ipb->ccw.nss_name, NSS_NAME_SIZE);
|
||||
EBCASC(dst, NSS_NAME_SIZE);
|
||||
dst[NSS_NAME_SIZE] = 0;
|
||||
}
|
||||
@@ -770,16 +803,14 @@ static ssize_t reipl_nss_name_store(struct kobject *kobj,
|
||||
if (nss_len > NSS_NAME_SIZE)
|
||||
return -EINVAL;
|
||||
|
||||
memset(reipl_block_nss->ipl_info.ccw.nss_name, 0x40, NSS_NAME_SIZE);
|
||||
memset(reipl_block_nss->ccw.nss_name, 0x40, NSS_NAME_SIZE);
|
||||
if (nss_len > 0) {
|
||||
reipl_block_nss->ipl_info.ccw.vm_flags |=
|
||||
DIAG308_VM_FLAGS_NSS_VALID;
|
||||
memcpy(reipl_block_nss->ipl_info.ccw.nss_name, buf, nss_len);
|
||||
ASCEBC(reipl_block_nss->ipl_info.ccw.nss_name, nss_len);
|
||||
EBC_TOUPPER(reipl_block_nss->ipl_info.ccw.nss_name, nss_len);
|
||||
reipl_block_nss->ccw.vm_flags |= IPL_PB0_CCW_VM_FLAG_NSS;
|
||||
memcpy(reipl_block_nss->ccw.nss_name, buf, nss_len);
|
||||
ASCEBC(reipl_block_nss->ccw.nss_name, nss_len);
|
||||
EBC_TOUPPER(reipl_block_nss->ccw.nss_name, nss_len);
|
||||
} else {
|
||||
reipl_block_nss->ipl_info.ccw.vm_flags &=
|
||||
~DIAG308_VM_FLAGS_NSS_VALID;
|
||||
reipl_block_nss->ccw.vm_flags &= ~IPL_PB0_CCW_VM_FLAG_NSS;
|
||||
}
|
||||
|
||||
return len;
|
||||
@@ -866,15 +897,21 @@ static void __reipl_run(void *unused)
|
||||
{
|
||||
switch (reipl_type) {
|
||||
case IPL_TYPE_CCW:
|
||||
uv_set_shared(__pa(reipl_block_ccw));
|
||||
diag308(DIAG308_SET, reipl_block_ccw);
|
||||
uv_remove_shared(__pa(reipl_block_ccw));
|
||||
diag308(DIAG308_LOAD_CLEAR, NULL);
|
||||
break;
|
||||
case IPL_TYPE_FCP:
|
||||
uv_set_shared(__pa(reipl_block_fcp));
|
||||
diag308(DIAG308_SET, reipl_block_fcp);
|
||||
uv_remove_shared(__pa(reipl_block_fcp));
|
||||
diag308(DIAG308_LOAD_CLEAR, NULL);
|
||||
break;
|
||||
case IPL_TYPE_NSS:
|
||||
uv_set_shared(__pa(reipl_block_nss));
|
||||
diag308(DIAG308_SET, reipl_block_nss);
|
||||
uv_remove_shared(__pa(reipl_block_nss));
|
||||
diag308(DIAG308_LOAD_CLEAR, NULL);
|
||||
break;
|
||||
case IPL_TYPE_UNKNOWN:
|
||||
@@ -883,7 +920,7 @@ static void __reipl_run(void *unused)
|
||||
case IPL_TYPE_FCP_DUMP:
|
||||
break;
|
||||
}
|
||||
disabled_wait((unsigned long) __builtin_return_address(0));
|
||||
disabled_wait();
|
||||
}
|
||||
|
||||
static void reipl_run(struct shutdown_trigger *trigger)
|
||||
@@ -893,10 +930,10 @@ static void reipl_run(struct shutdown_trigger *trigger)
|
||||
|
||||
static void reipl_block_ccw_init(struct ipl_parameter_block *ipb)
|
||||
{
|
||||
ipb->hdr.len = IPL_PARM_BLK_CCW_LEN;
|
||||
ipb->hdr.len = IPL_BP_CCW_LEN;
|
||||
ipb->hdr.version = IPL_PARM_BLOCK_VERSION;
|
||||
ipb->hdr.blk0_len = IPL_PARM_BLK0_CCW_LEN;
|
||||
ipb->hdr.pbt = DIAG308_IPL_TYPE_CCW;
|
||||
ipb->pb0_hdr.len = IPL_BP0_CCW_LEN;
|
||||
ipb->pb0_hdr.pbt = IPL_PBT_CCW;
|
||||
}
|
||||
|
||||
static void reipl_block_ccw_fill_parms(struct ipl_parameter_block *ipb)
|
||||
@@ -904,21 +941,20 @@ static void reipl_block_ccw_fill_parms(struct ipl_parameter_block *ipb)
|
||||
/* LOADPARM */
|
||||
/* check if read scp info worked and set loadparm */
|
||||
if (sclp_ipl_info.is_valid)
|
||||
memcpy(ipb->hdr.loadparm, &sclp_ipl_info.loadparm, LOADPARM_LEN);
|
||||
memcpy(ipb->ccw.loadparm, &sclp_ipl_info.loadparm, LOADPARM_LEN);
|
||||
else
|
||||
/* read scp info failed: set empty loadparm (EBCDIC blanks) */
|
||||
memset(ipb->hdr.loadparm, 0x40, LOADPARM_LEN);
|
||||
ipb->hdr.flags = DIAG308_FLAGS_LP_VALID;
|
||||
memset(ipb->ccw.loadparm, 0x40, LOADPARM_LEN);
|
||||
ipb->ccw.flags = IPL_PB0_FLAG_LOADPARM;
|
||||
|
||||
/* VM PARM */
|
||||
if (MACHINE_IS_VM && ipl_block_valid &&
|
||||
(ipl_block.ipl_info.ccw.vm_flags & DIAG308_VM_FLAGS_VP_VALID)) {
|
||||
(ipl_block.ccw.vm_flags & IPL_PB0_CCW_VM_FLAG_VP)) {
|
||||
|
||||
ipb->ipl_info.ccw.vm_flags |= DIAG308_VM_FLAGS_VP_VALID;
|
||||
ipb->ipl_info.ccw.vm_parm_len =
|
||||
ipl_block.ipl_info.ccw.vm_parm_len;
|
||||
memcpy(ipb->ipl_info.ccw.vm_parm,
|
||||
ipl_block.ipl_info.ccw.vm_parm, DIAG308_VMPARM_SIZE);
|
||||
ipb->ccw.vm_flags |= IPL_PB0_CCW_VM_FLAG_VP;
|
||||
ipb->ccw.vm_parm_len = ipl_block.ccw.vm_parm_len;
|
||||
memcpy(ipb->ccw.vm_parm,
|
||||
ipl_block.ccw.vm_parm, DIAG308_VMPARM_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -958,8 +994,8 @@ static int __init reipl_ccw_init(void)
|
||||
|
||||
reipl_block_ccw_init(reipl_block_ccw);
|
||||
if (ipl_info.type == IPL_TYPE_CCW) {
|
||||
reipl_block_ccw->ipl_info.ccw.ssid = ipl_block.ipl_info.ccw.ssid;
|
||||
reipl_block_ccw->ipl_info.ccw.devno = ipl_block.ipl_info.ccw.devno;
|
||||
reipl_block_ccw->ccw.ssid = ipl_block.ccw.ssid;
|
||||
reipl_block_ccw->ccw.devno = ipl_block.ccw.devno;
|
||||
reipl_block_ccw_fill_parms(reipl_block_ccw);
|
||||
}
|
||||
|
||||
@@ -997,14 +1033,14 @@ static int __init reipl_fcp_init(void)
|
||||
* is invalid in the SCSI IPL parameter block, so take it
|
||||
* always from sclp_ipl_info.
|
||||
*/
|
||||
memcpy(reipl_block_fcp->hdr.loadparm, sclp_ipl_info.loadparm,
|
||||
memcpy(reipl_block_fcp->fcp.loadparm, sclp_ipl_info.loadparm,
|
||||
LOADPARM_LEN);
|
||||
} else {
|
||||
reipl_block_fcp->hdr.len = IPL_PARM_BLK_FCP_LEN;
|
||||
reipl_block_fcp->hdr.len = IPL_BP_FCP_LEN;
|
||||
reipl_block_fcp->hdr.version = IPL_PARM_BLOCK_VERSION;
|
||||
reipl_block_fcp->hdr.blk0_len = IPL_PARM_BLK0_FCP_LEN;
|
||||
reipl_block_fcp->hdr.pbt = DIAG308_IPL_TYPE_FCP;
|
||||
reipl_block_fcp->ipl_info.fcp.opt = DIAG308_IPL_OPT_IPL;
|
||||
reipl_block_fcp->fcp.len = IPL_BP0_FCP_LEN;
|
||||
reipl_block_fcp->fcp.pbt = IPL_PBT_FCP;
|
||||
reipl_block_fcp->fcp.opt = IPL_PB0_FCP_OPT_IPL;
|
||||
}
|
||||
reipl_capabilities |= IPL_TYPE_FCP;
|
||||
return 0;
|
||||
@@ -1022,10 +1058,10 @@ static int __init reipl_type_init(void)
|
||||
/*
|
||||
* If we have an OS info reipl block, this will be used
|
||||
*/
|
||||
if (reipl_block->hdr.pbt == DIAG308_IPL_TYPE_FCP) {
|
||||
if (reipl_block->pb0_hdr.pbt == IPL_PBT_FCP) {
|
||||
memcpy(reipl_block_fcp, reipl_block, size);
|
||||
reipl_type = IPL_TYPE_FCP;
|
||||
} else if (reipl_block->hdr.pbt == DIAG308_IPL_TYPE_CCW) {
|
||||
} else if (reipl_block->pb0_hdr.pbt == IPL_PBT_CCW) {
|
||||
memcpy(reipl_block_ccw, reipl_block, size);
|
||||
reipl_type = IPL_TYPE_CCW;
|
||||
}
|
||||
@@ -1070,15 +1106,15 @@ static struct shutdown_action __refdata reipl_action = {
|
||||
/* FCP dump device attributes */
|
||||
|
||||
DEFINE_IPL_ATTR_RW(dump_fcp, wwpn, "0x%016llx\n", "%llx\n",
|
||||
dump_block_fcp->ipl_info.fcp.wwpn);
|
||||
dump_block_fcp->fcp.wwpn);
|
||||
DEFINE_IPL_ATTR_RW(dump_fcp, lun, "0x%016llx\n", "%llx\n",
|
||||
dump_block_fcp->ipl_info.fcp.lun);
|
||||
dump_block_fcp->fcp.lun);
|
||||
DEFINE_IPL_ATTR_RW(dump_fcp, bootprog, "%lld\n", "%lld\n",
|
||||
dump_block_fcp->ipl_info.fcp.bootprog);
|
||||
dump_block_fcp->fcp.bootprog);
|
||||
DEFINE_IPL_ATTR_RW(dump_fcp, br_lba, "%lld\n", "%lld\n",
|
||||
dump_block_fcp->ipl_info.fcp.br_lba);
|
||||
dump_block_fcp->fcp.br_lba);
|
||||
DEFINE_IPL_ATTR_RW(dump_fcp, device, "0.0.%04llx\n", "0.0.%llx\n",
|
||||
dump_block_fcp->ipl_info.fcp.devno);
|
||||
dump_block_fcp->fcp.devno);
|
||||
|
||||
static struct attribute *dump_fcp_attrs[] = {
|
||||
&sys_dump_fcp_device_attr.attr,
|
||||
@@ -1095,7 +1131,7 @@ static struct attribute_group dump_fcp_attr_group = {
|
||||
};
|
||||
|
||||
/* CCW dump device attributes */
|
||||
DEFINE_IPL_CCW_ATTR_RW(dump_ccw, device, dump_block_ccw->ipl_info.ccw);
|
||||
DEFINE_IPL_CCW_ATTR_RW(dump_ccw, device, dump_block_ccw->ccw);
|
||||
|
||||
static struct attribute *dump_ccw_attrs[] = {
|
||||
&sys_dump_ccw_device_attr.attr,
|
||||
@@ -1145,7 +1181,9 @@ static struct kset *dump_kset;
|
||||
|
||||
static void diag308_dump(void *dump_block)
|
||||
{
|
||||
uv_set_shared(__pa(dump_block));
|
||||
diag308(DIAG308_SET, dump_block);
|
||||
uv_remove_shared(__pa(dump_block));
|
||||
while (1) {
|
||||
if (diag308(DIAG308_LOAD_NORMAL_DUMP, NULL) != 0x302)
|
||||
break;
|
||||
@@ -1187,10 +1225,10 @@ static int __init dump_ccw_init(void)
|
||||
free_page((unsigned long)dump_block_ccw);
|
||||
return rc;
|
||||
}
|
||||
dump_block_ccw->hdr.len = IPL_PARM_BLK_CCW_LEN;
|
||||
dump_block_ccw->hdr.len = IPL_BP_CCW_LEN;
|
||||
dump_block_ccw->hdr.version = IPL_PARM_BLOCK_VERSION;
|
||||
dump_block_ccw->hdr.blk0_len = IPL_PARM_BLK0_CCW_LEN;
|
||||
dump_block_ccw->hdr.pbt = DIAG308_IPL_TYPE_CCW;
|
||||
dump_block_ccw->ccw.len = IPL_BP0_CCW_LEN;
|
||||
dump_block_ccw->ccw.pbt = IPL_PBT_CCW;
|
||||
dump_capabilities |= DUMP_TYPE_CCW;
|
||||
return 0;
|
||||
}
|
||||
@@ -1209,11 +1247,11 @@ static int __init dump_fcp_init(void)
|
||||
free_page((unsigned long)dump_block_fcp);
|
||||
return rc;
|
||||
}
|
||||
dump_block_fcp->hdr.len = IPL_PARM_BLK_FCP_LEN;
|
||||
dump_block_fcp->hdr.len = IPL_BP_FCP_LEN;
|
||||
dump_block_fcp->hdr.version = IPL_PARM_BLOCK_VERSION;
|
||||
dump_block_fcp->hdr.blk0_len = IPL_PARM_BLK0_FCP_LEN;
|
||||
dump_block_fcp->hdr.pbt = DIAG308_IPL_TYPE_FCP;
|
||||
dump_block_fcp->ipl_info.fcp.opt = DIAG308_IPL_OPT_DUMP;
|
||||
dump_block_fcp->fcp.len = IPL_BP0_FCP_LEN;
|
||||
dump_block_fcp->fcp.pbt = IPL_PBT_FCP;
|
||||
dump_block_fcp->fcp.opt = IPL_PB0_FCP_OPT_DUMP;
|
||||
dump_capabilities |= DUMP_TYPE_FCP;
|
||||
return 0;
|
||||
}
|
||||
@@ -1337,7 +1375,7 @@ static void stop_run(struct shutdown_trigger *trigger)
|
||||
{
|
||||
if (strcmp(trigger->name, ON_PANIC_STR) == 0 ||
|
||||
strcmp(trigger->name, ON_RESTART_STR) == 0)
|
||||
disabled_wait((unsigned long) __builtin_return_address(0));
|
||||
disabled_wait();
|
||||
smp_stop_cpu();
|
||||
}
|
||||
|
||||
@@ -1572,7 +1610,7 @@ static int __init s390_ipl_init(void)
|
||||
* READ SCP info provides the correct value.
|
||||
*/
|
||||
if (memcmp(sclp_ipl_info.loadparm, str, sizeof(str)) == 0 && ipl_block_valid)
|
||||
memcpy(sclp_ipl_info.loadparm, ipl_block.hdr.loadparm, LOADPARM_LEN);
|
||||
memcpy(sclp_ipl_info.loadparm, ipl_block.ccw.loadparm, LOADPARM_LEN);
|
||||
shutdown_actions_init();
|
||||
shutdown_triggers_init();
|
||||
return 0;
|
||||
@@ -1657,15 +1695,15 @@ void __init setup_ipl(void)
|
||||
ipl_info.type = get_ipl_type();
|
||||
switch (ipl_info.type) {
|
||||
case IPL_TYPE_CCW:
|
||||
ipl_info.data.ccw.dev_id.ssid = ipl_block.ipl_info.ccw.ssid;
|
||||
ipl_info.data.ccw.dev_id.devno = ipl_block.ipl_info.ccw.devno;
|
||||
ipl_info.data.ccw.dev_id.ssid = ipl_block.ccw.ssid;
|
||||
ipl_info.data.ccw.dev_id.devno = ipl_block.ccw.devno;
|
||||
break;
|
||||
case IPL_TYPE_FCP:
|
||||
case IPL_TYPE_FCP_DUMP:
|
||||
ipl_info.data.fcp.dev_id.ssid = 0;
|
||||
ipl_info.data.fcp.dev_id.devno = ipl_block.ipl_info.fcp.devno;
|
||||
ipl_info.data.fcp.wwpn = ipl_block.ipl_info.fcp.wwpn;
|
||||
ipl_info.data.fcp.lun = ipl_block.ipl_info.fcp.lun;
|
||||
ipl_info.data.fcp.dev_id.devno = ipl_block.fcp.devno;
|
||||
ipl_info.data.fcp.wwpn = ipl_block.fcp.wwpn;
|
||||
ipl_info.data.fcp.lun = ipl_block.fcp.lun;
|
||||
break;
|
||||
case IPL_TYPE_NSS:
|
||||
case IPL_TYPE_UNKNOWN:
|
||||
@@ -1675,14 +1713,6 @@ void __init setup_ipl(void)
|
||||
atomic_notifier_chain_register(&panic_notifier_list, &on_panic_nb);
|
||||
}
|
||||
|
||||
void __init ipl_store_parameters(void)
|
||||
{
|
||||
if (early_ipl_block_valid) {
|
||||
memcpy(&ipl_block, &early_ipl_block, sizeof(ipl_block));
|
||||
ipl_block_valid = 1;
|
||||
}
|
||||
}
|
||||
|
||||
void s390_reset_system(void)
|
||||
{
|
||||
/* Disable prefixing */
|
||||
@@ -1690,5 +1720,139 @@ void s390_reset_system(void)
|
||||
|
||||
/* Disable lowcore protection */
|
||||
__ctl_clear_bit(0, 28);
|
||||
diag308_reset();
|
||||
diag_dma_ops.diag308_reset();
|
||||
}
|
||||
|
||||
#ifdef CONFIG_KEXEC_FILE
|
||||
|
||||
int ipl_report_add_component(struct ipl_report *report, struct kexec_buf *kbuf,
|
||||
unsigned char flags, unsigned short cert)
|
||||
{
|
||||
struct ipl_report_component *comp;
|
||||
|
||||
comp = vzalloc(sizeof(*comp));
|
||||
if (!comp)
|
||||
return -ENOMEM;
|
||||
list_add_tail(&comp->list, &report->components);
|
||||
|
||||
comp->entry.addr = kbuf->mem;
|
||||
comp->entry.len = kbuf->memsz;
|
||||
comp->entry.flags = flags;
|
||||
comp->entry.certificate_index = cert;
|
||||
|
||||
report->size += sizeof(comp->entry);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ipl_report_add_certificate(struct ipl_report *report, void *key,
|
||||
unsigned long addr, unsigned long len)
|
||||
{
|
||||
struct ipl_report_certificate *cert;
|
||||
|
||||
cert = vzalloc(sizeof(*cert));
|
||||
if (!cert)
|
||||
return -ENOMEM;
|
||||
list_add_tail(&cert->list, &report->certificates);
|
||||
|
||||
cert->entry.addr = addr;
|
||||
cert->entry.len = len;
|
||||
cert->key = key;
|
||||
|
||||
report->size += sizeof(cert->entry);
|
||||
report->size += cert->entry.len;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct ipl_report *ipl_report_init(struct ipl_parameter_block *ipib)
|
||||
{
|
||||
struct ipl_report *report;
|
||||
|
||||
report = vzalloc(sizeof(*report));
|
||||
if (!report)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
report->ipib = ipib;
|
||||
INIT_LIST_HEAD(&report->components);
|
||||
INIT_LIST_HEAD(&report->certificates);
|
||||
|
||||
report->size = ALIGN(ipib->hdr.len, 8);
|
||||
report->size += sizeof(struct ipl_rl_hdr);
|
||||
report->size += sizeof(struct ipl_rb_components);
|
||||
report->size += sizeof(struct ipl_rb_certificates);
|
||||
|
||||
return report;
|
||||
}
|
||||
|
||||
void *ipl_report_finish(struct ipl_report *report)
|
||||
{
|
||||
struct ipl_report_certificate *cert;
|
||||
struct ipl_report_component *comp;
|
||||
struct ipl_rb_certificates *certs;
|
||||
struct ipl_parameter_block *ipib;
|
||||
struct ipl_rb_components *comps;
|
||||
struct ipl_rl_hdr *rl_hdr;
|
||||
void *buf, *ptr;
|
||||
|
||||
buf = vzalloc(report->size);
|
||||
if (!buf)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
ptr = buf;
|
||||
|
||||
memcpy(ptr, report->ipib, report->ipib->hdr.len);
|
||||
ipib = ptr;
|
||||
if (ipl_secure_flag)
|
||||
ipib->hdr.flags |= IPL_PL_FLAG_SIPL;
|
||||
ipib->hdr.flags |= IPL_PL_FLAG_IPLSR;
|
||||
ptr += report->ipib->hdr.len;
|
||||
ptr = PTR_ALIGN(ptr, 8);
|
||||
|
||||
rl_hdr = ptr;
|
||||
ptr += sizeof(*rl_hdr);
|
||||
|
||||
comps = ptr;
|
||||
comps->rbt = IPL_RBT_COMPONENTS;
|
||||
ptr += sizeof(*comps);
|
||||
list_for_each_entry(comp, &report->components, list) {
|
||||
memcpy(ptr, &comp->entry, sizeof(comp->entry));
|
||||
ptr += sizeof(comp->entry);
|
||||
}
|
||||
comps->len = ptr - (void *)comps;
|
||||
|
||||
certs = ptr;
|
||||
certs->rbt = IPL_RBT_CERTIFICATES;
|
||||
ptr += sizeof(*certs);
|
||||
list_for_each_entry(cert, &report->certificates, list) {
|
||||
memcpy(ptr, &cert->entry, sizeof(cert->entry));
|
||||
ptr += sizeof(cert->entry);
|
||||
}
|
||||
certs->len = ptr - (void *)certs;
|
||||
rl_hdr->len = ptr - (void *)rl_hdr;
|
||||
|
||||
list_for_each_entry(cert, &report->certificates, list) {
|
||||
memcpy(ptr, cert->key, cert->entry.len);
|
||||
ptr += cert->entry.len;
|
||||
}
|
||||
|
||||
BUG_ON(ptr > buf + report->size);
|
||||
return buf;
|
||||
}
|
||||
|
||||
int ipl_report_free(struct ipl_report *report)
|
||||
{
|
||||
struct ipl_report_component *comp, *ncomp;
|
||||
struct ipl_report_certificate *cert, *ncert;
|
||||
|
||||
list_for_each_entry_safe(comp, ncomp, &report->components, list)
|
||||
vfree(comp);
|
||||
|
||||
list_for_each_entry_safe(cert, ncert, &report->certificates, list)
|
||||
vfree(cert);
|
||||
|
||||
vfree(report);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@@ -11,11 +11,11 @@ size_t ipl_block_get_ascii_vmparm(char *dest, size_t size,
|
||||
char has_lowercase = 0;
|
||||
|
||||
len = 0;
|
||||
if ((ipb->ipl_info.ccw.vm_flags & DIAG308_VM_FLAGS_VP_VALID) &&
|
||||
(ipb->ipl_info.ccw.vm_parm_len > 0)) {
|
||||
if ((ipb->ccw.vm_flags & IPL_PB0_CCW_VM_FLAG_VP) &&
|
||||
(ipb->ccw.vm_parm_len > 0)) {
|
||||
|
||||
len = min_t(size_t, size - 1, ipb->ipl_info.ccw.vm_parm_len);
|
||||
memcpy(dest, ipb->ipl_info.ccw.vm_parm, len);
|
||||
len = min_t(size_t, size - 1, ipb->ccw.vm_parm_len);
|
||||
memcpy(dest, ipb->ccw.vm_parm, len);
|
||||
/* If at least one character is lowercase, we assume mixed
|
||||
* case; otherwise we convert everything to lowercase.
|
||||
*/
|
||||
|
@@ -26,6 +26,7 @@
|
||||
#include <asm/lowcore.h>
|
||||
#include <asm/irq.h>
|
||||
#include <asm/hw_irq.h>
|
||||
#include <asm/stacktrace.h>
|
||||
#include "entry.h"
|
||||
|
||||
DEFINE_PER_CPU_SHARED_ALIGNED(struct irq_stat, irq_stat);
|
||||
@@ -73,7 +74,6 @@ static const struct irq_class irqclass_sub_desc[] = {
|
||||
{.irq = IRQEXT_CMC, .name = "CMC", .desc = "[EXT] CPU-Measurement: Counter"},
|
||||
{.irq = IRQEXT_FTP, .name = "FTP", .desc = "[EXT] HMC FTP Service"},
|
||||
{.irq = IRQIO_CIO, .name = "CIO", .desc = "[I/O] Common I/O Layer Interrupt"},
|
||||
{.irq = IRQIO_QAI, .name = "QAI", .desc = "[I/O] QDIO Adapter Interrupt"},
|
||||
{.irq = IRQIO_DAS, .name = "DAS", .desc = "[I/O] DASD"},
|
||||
{.irq = IRQIO_C15, .name = "C15", .desc = "[I/O] 3215"},
|
||||
{.irq = IRQIO_C70, .name = "C70", .desc = "[I/O] 3270"},
|
||||
@@ -81,14 +81,16 @@ static const struct irq_class irqclass_sub_desc[] = {
|
||||
{.irq = IRQIO_VMR, .name = "VMR", .desc = "[I/O] Unit Record Devices"},
|
||||
{.irq = IRQIO_LCS, .name = "LCS", .desc = "[I/O] LCS"},
|
||||
{.irq = IRQIO_CTC, .name = "CTC", .desc = "[I/O] CTC"},
|
||||
{.irq = IRQIO_APB, .name = "APB", .desc = "[I/O] AP Bus"},
|
||||
{.irq = IRQIO_ADM, .name = "ADM", .desc = "[I/O] EADM Subchannel"},
|
||||
{.irq = IRQIO_CSC, .name = "CSC", .desc = "[I/O] CHSC Subchannel"},
|
||||
{.irq = IRQIO_PCI, .name = "PCI", .desc = "[I/O] PCI Interrupt" },
|
||||
{.irq = IRQIO_MSI, .name = "MSI", .desc = "[I/O] MSI Interrupt" },
|
||||
{.irq = IRQIO_VIR, .name = "VIR", .desc = "[I/O] Virtual I/O Devices"},
|
||||
{.irq = IRQIO_VAI, .name = "VAI", .desc = "[I/O] Virtual I/O Devices AI"},
|
||||
{.irq = IRQIO_GAL, .name = "GAL", .desc = "[I/O] GIB Alert"},
|
||||
{.irq = IRQIO_QAI, .name = "QAI", .desc = "[AIO] QDIO Adapter Interrupt"},
|
||||
{.irq = IRQIO_APB, .name = "APB", .desc = "[AIO] AP Bus"},
|
||||
{.irq = IRQIO_PCF, .name = "PCF", .desc = "[AIO] PCI Floating Interrupt"},
|
||||
{.irq = IRQIO_PCD, .name = "PCD", .desc = "[AIO] PCI Directed Interrupt"},
|
||||
{.irq = IRQIO_MSI, .name = "MSI", .desc = "[AIO] MSI Interrupt"},
|
||||
{.irq = IRQIO_VAI, .name = "VAI", .desc = "[AIO] Virtual I/O Devices AI"},
|
||||
{.irq = IRQIO_GAL, .name = "GAL", .desc = "[AIO] GIB Alert"},
|
||||
{.irq = NMI_NMI, .name = "NMI", .desc = "[NMI] Machine Check"},
|
||||
{.irq = CPU_RST, .name = "RST", .desc = "[CPU] CPU Restart"},
|
||||
};
|
||||
@@ -116,6 +118,34 @@ void do_IRQ(struct pt_regs *regs, int irq)
|
||||
set_irq_regs(old_regs);
|
||||
}
|
||||
|
||||
static void show_msi_interrupt(struct seq_file *p, int irq)
|
||||
{
|
||||
struct irq_desc *desc;
|
||||
unsigned long flags;
|
||||
int cpu;
|
||||
|
||||
irq_lock_sparse();
|
||||
desc = irq_to_desc(irq);
|
||||
if (!desc)
|
||||
goto out;
|
||||
|
||||
raw_spin_lock_irqsave(&desc->lock, flags);
|
||||
seq_printf(p, "%3d: ", irq);
|
||||
for_each_online_cpu(cpu)
|
||||
seq_printf(p, "%10u ", kstat_irqs_cpu(irq, cpu));
|
||||
|
||||
if (desc->irq_data.chip)
|
||||
seq_printf(p, " %8s", desc->irq_data.chip->name);
|
||||
|
||||
if (desc->action)
|
||||
seq_printf(p, " %s", desc->action->name);
|
||||
|
||||
seq_putc(p, '\n');
|
||||
raw_spin_unlock_irqrestore(&desc->lock, flags);
|
||||
out:
|
||||
irq_unlock_sparse();
|
||||
}
|
||||
|
||||
/*
|
||||
* show_interrupts is needed by /proc/interrupts.
|
||||
*/
|
||||
@@ -128,7 +158,7 @@ int show_interrupts(struct seq_file *p, void *v)
|
||||
if (index == 0) {
|
||||
seq_puts(p, " ");
|
||||
for_each_online_cpu(cpu)
|
||||
seq_printf(p, "CPU%d ", cpu);
|
||||
seq_printf(p, "CPU%-8d", cpu);
|
||||
seq_putc(p, '\n');
|
||||
}
|
||||
if (index < NR_IRQS_BASE) {
|
||||
@@ -139,9 +169,10 @@ int show_interrupts(struct seq_file *p, void *v)
|
||||
seq_putc(p, '\n');
|
||||
goto out;
|
||||
}
|
||||
if (index > NR_IRQS_BASE)
|
||||
if (index < nr_irqs) {
|
||||
show_msi_interrupt(p, index);
|
||||
goto out;
|
||||
|
||||
}
|
||||
for (index = 0; index < NR_ARCH_IRQS; index++) {
|
||||
seq_printf(p, "%s: ", irqclass_sub_desc[index].name);
|
||||
irq = irqclass_sub_desc[index].irq;
|
||||
|
@@ -10,19 +10,26 @@
|
||||
#include <linux/errno.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/kexec.h>
|
||||
#include <asm/ipl.h>
|
||||
#include <asm/setup.h>
|
||||
|
||||
static int kexec_file_add_elf_kernel(struct kimage *image,
|
||||
struct s390_load_data *data,
|
||||
char *kernel, unsigned long kernel_len)
|
||||
static int kexec_file_add_kernel_elf(struct kimage *image,
|
||||
struct s390_load_data *data)
|
||||
{
|
||||
struct kexec_buf buf;
|
||||
const Elf_Ehdr *ehdr;
|
||||
const Elf_Phdr *phdr;
|
||||
Elf_Addr entry;
|
||||
void *kernel;
|
||||
int i, ret;
|
||||
|
||||
kernel = image->kernel_buf;
|
||||
ehdr = (Elf_Ehdr *)kernel;
|
||||
buf.image = image;
|
||||
if (image->type == KEXEC_TYPE_CRASH)
|
||||
entry = STARTUP_KDUMP_OFFSET;
|
||||
else
|
||||
entry = ehdr->e_entry;
|
||||
|
||||
phdr = (void *)ehdr + ehdr->e_phoff;
|
||||
for (i = 0; i < ehdr->e_phnum; i++, phdr++) {
|
||||
@@ -33,30 +40,27 @@ static int kexec_file_add_elf_kernel(struct kimage *image,
|
||||
buf.bufsz = phdr->p_filesz;
|
||||
|
||||
buf.mem = ALIGN(phdr->p_paddr, phdr->p_align);
|
||||
buf.memsz = phdr->p_memsz;
|
||||
|
||||
if (phdr->p_paddr == 0) {
|
||||
data->kernel_buf = buf.buffer;
|
||||
data->memsz += STARTUP_NORMAL_OFFSET;
|
||||
|
||||
buf.buffer += STARTUP_NORMAL_OFFSET;
|
||||
buf.bufsz -= STARTUP_NORMAL_OFFSET;
|
||||
|
||||
buf.mem += STARTUP_NORMAL_OFFSET;
|
||||
buf.memsz -= STARTUP_NORMAL_OFFSET;
|
||||
}
|
||||
|
||||
if (image->type == KEXEC_TYPE_CRASH)
|
||||
buf.mem += crashk_res.start;
|
||||
buf.memsz = phdr->p_memsz;
|
||||
data->memsz = ALIGN(data->memsz, phdr->p_align) + buf.memsz;
|
||||
|
||||
if (entry - phdr->p_paddr < phdr->p_memsz) {
|
||||
data->kernel_buf = buf.buffer;
|
||||
data->kernel_mem = buf.mem;
|
||||
data->parm = buf.buffer + PARMAREA;
|
||||
}
|
||||
|
||||
ipl_report_add_component(data->report, &buf,
|
||||
IPL_RB_COMPONENT_FLAG_SIGNED |
|
||||
IPL_RB_COMPONENT_FLAG_VERIFIED,
|
||||
IPL_RB_CERT_UNKNOWN);
|
||||
ret = kexec_add_buffer(&buf);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
data->memsz += buf.memsz;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return data->memsz ? 0 : -EINVAL;
|
||||
}
|
||||
|
||||
static void *s390_elf_load(struct kimage *image,
|
||||
@@ -64,11 +68,10 @@ static void *s390_elf_load(struct kimage *image,
|
||||
char *initrd, unsigned long initrd_len,
|
||||
char *cmdline, unsigned long cmdline_len)
|
||||
{
|
||||
struct s390_load_data data = {0};
|
||||
const Elf_Ehdr *ehdr;
|
||||
const Elf_Phdr *phdr;
|
||||
size_t size;
|
||||
int i, ret;
|
||||
int i;
|
||||
|
||||
/* image->fobs->probe already checked for valid ELF magic number. */
|
||||
ehdr = (Elf_Ehdr *)kernel;
|
||||
@@ -101,24 +104,7 @@ static void *s390_elf_load(struct kimage *image,
|
||||
if (size > kernel_len)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
ret = kexec_file_add_elf_kernel(image, &data, kernel, kernel_len);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
if (!data.memsz)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
if (initrd) {
|
||||
ret = kexec_file_add_initrd(image, &data, initrd, initrd_len);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
ret = kexec_file_add_purgatory(image, &data);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
return kexec_file_update_kernel(image, &data);
|
||||
return kexec_file_add_components(image, kexec_file_add_kernel_elf);
|
||||
}
|
||||
|
||||
static int s390_elf_probe(const char *buf, unsigned long len)
|
||||
@@ -144,4 +130,7 @@ static int s390_elf_probe(const char *buf, unsigned long len)
|
||||
const struct kexec_file_ops s390_kexec_elf_ops = {
|
||||
.probe = s390_elf_probe,
|
||||
.load = s390_elf_load,
|
||||
#ifdef CONFIG_KEXEC_VERIFY_SIG
|
||||
.verify_sig = s390_verify_sig,
|
||||
#endif /* CONFIG_KEXEC_VERIFY_SIG */
|
||||
};
|
||||
|
@@ -10,31 +10,34 @@
|
||||
#include <linux/errno.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/kexec.h>
|
||||
#include <asm/ipl.h>
|
||||
#include <asm/setup.h>
|
||||
|
||||
static int kexec_file_add_image_kernel(struct kimage *image,
|
||||
struct s390_load_data *data,
|
||||
char *kernel, unsigned long kernel_len)
|
||||
static int kexec_file_add_kernel_image(struct kimage *image,
|
||||
struct s390_load_data *data)
|
||||
{
|
||||
struct kexec_buf buf;
|
||||
int ret;
|
||||
|
||||
buf.image = image;
|
||||
|
||||
buf.buffer = kernel + STARTUP_NORMAL_OFFSET;
|
||||
buf.bufsz = kernel_len - STARTUP_NORMAL_OFFSET;
|
||||
buf.buffer = image->kernel_buf;
|
||||
buf.bufsz = image->kernel_buf_len;
|
||||
|
||||
buf.mem = STARTUP_NORMAL_OFFSET;
|
||||
buf.mem = 0;
|
||||
if (image->type == KEXEC_TYPE_CRASH)
|
||||
buf.mem += crashk_res.start;
|
||||
buf.memsz = buf.bufsz;
|
||||
|
||||
ret = kexec_add_buffer(&buf);
|
||||
data->kernel_buf = image->kernel_buf;
|
||||
data->kernel_mem = buf.mem;
|
||||
data->parm = image->kernel_buf + PARMAREA;
|
||||
data->memsz += buf.memsz;
|
||||
|
||||
data->kernel_buf = kernel;
|
||||
data->memsz += buf.memsz + STARTUP_NORMAL_OFFSET;
|
||||
|
||||
return ret;
|
||||
ipl_report_add_component(data->report, &buf,
|
||||
IPL_RB_COMPONENT_FLAG_SIGNED |
|
||||
IPL_RB_COMPONENT_FLAG_VERIFIED,
|
||||
IPL_RB_CERT_UNKNOWN);
|
||||
return kexec_add_buffer(&buf);
|
||||
}
|
||||
|
||||
static void *s390_image_load(struct kimage *image,
|
||||
@@ -42,24 +45,7 @@ static void *s390_image_load(struct kimage *image,
|
||||
char *initrd, unsigned long initrd_len,
|
||||
char *cmdline, unsigned long cmdline_len)
|
||||
{
|
||||
struct s390_load_data data = {0};
|
||||
int ret;
|
||||
|
||||
ret = kexec_file_add_image_kernel(image, &data, kernel, kernel_len);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
if (initrd) {
|
||||
ret = kexec_file_add_initrd(image, &data, initrd, initrd_len);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
ret = kexec_file_add_purgatory(image, &data);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
return kexec_file_update_kernel(image, &data);
|
||||
return kexec_file_add_components(image, kexec_file_add_kernel_image);
|
||||
}
|
||||
|
||||
static int s390_image_probe(const char *buf, unsigned long len)
|
||||
@@ -73,4 +59,7 @@ static int s390_image_probe(const char *buf, unsigned long len)
|
||||
const struct kexec_file_ops s390_kexec_image_ops = {
|
||||
.probe = s390_image_probe,
|
||||
.load = s390_image_load,
|
||||
#ifdef CONFIG_KEXEC_VERIFY_SIG
|
||||
.verify_sig = s390_verify_sig,
|
||||
#endif /* CONFIG_KEXEC_VERIFY_SIG */
|
||||
};
|
||||
|
@@ -27,29 +27,30 @@ DEFINE_PER_CPU(struct kprobe_ctlblk, kprobe_ctlblk);
|
||||
|
||||
struct kretprobe_blackpoint kretprobe_blacklist[] = { };
|
||||
|
||||
DEFINE_INSN_CACHE_OPS(dmainsn);
|
||||
DEFINE_INSN_CACHE_OPS(s390_insn);
|
||||
|
||||
static void *alloc_dmainsn_page(void)
|
||||
static int insn_page_in_use;
|
||||
static char insn_page[PAGE_SIZE] __aligned(PAGE_SIZE);
|
||||
|
||||
static void *alloc_s390_insn_page(void)
|
||||
{
|
||||
void *page;
|
||||
|
||||
page = (void *) __get_free_page(GFP_KERNEL | GFP_DMA);
|
||||
if (page)
|
||||
set_memory_x((unsigned long) page, 1);
|
||||
return page;
|
||||
if (xchg(&insn_page_in_use, 1) == 1)
|
||||
return NULL;
|
||||
set_memory_x((unsigned long) &insn_page, 1);
|
||||
return &insn_page;
|
||||
}
|
||||
|
||||
static void free_dmainsn_page(void *page)
|
||||
static void free_s390_insn_page(void *page)
|
||||
{
|
||||
set_memory_nx((unsigned long) page, 1);
|
||||
free_page((unsigned long)page);
|
||||
xchg(&insn_page_in_use, 0);
|
||||
}
|
||||
|
||||
struct kprobe_insn_cache kprobe_dmainsn_slots = {
|
||||
.mutex = __MUTEX_INITIALIZER(kprobe_dmainsn_slots.mutex),
|
||||
.alloc = alloc_dmainsn_page,
|
||||
.free = free_dmainsn_page,
|
||||
.pages = LIST_HEAD_INIT(kprobe_dmainsn_slots.pages),
|
||||
struct kprobe_insn_cache kprobe_s390_insn_slots = {
|
||||
.mutex = __MUTEX_INITIALIZER(kprobe_s390_insn_slots.mutex),
|
||||
.alloc = alloc_s390_insn_page,
|
||||
.free = free_s390_insn_page,
|
||||
.pages = LIST_HEAD_INIT(kprobe_s390_insn_slots.pages),
|
||||
.insn_size = MAX_INSN_SIZE,
|
||||
};
|
||||
|
||||
@@ -102,7 +103,7 @@ static int s390_get_insn_slot(struct kprobe *p)
|
||||
*/
|
||||
p->ainsn.insn = NULL;
|
||||
if (is_kernel_addr(p->addr))
|
||||
p->ainsn.insn = get_dmainsn_slot();
|
||||
p->ainsn.insn = get_s390_insn_slot();
|
||||
else if (is_module_addr(p->addr))
|
||||
p->ainsn.insn = get_insn_slot();
|
||||
return p->ainsn.insn ? 0 : -ENOMEM;
|
||||
@@ -114,7 +115,7 @@ static void s390_free_insn_slot(struct kprobe *p)
|
||||
if (!p->ainsn.insn)
|
||||
return;
|
||||
if (is_kernel_addr(p->addr))
|
||||
free_dmainsn_slot(p->ainsn.insn, 0);
|
||||
free_s390_insn_slot(p->ainsn.insn, 0);
|
||||
else
|
||||
free_insn_slot(p->ainsn.insn, 0);
|
||||
p->ainsn.insn = NULL;
|
||||
@@ -572,7 +573,7 @@ static int kprobe_trap_handler(struct pt_regs *regs, int trapnr)
|
||||
* In case the user-specified fault handler returned
|
||||
* zero, try to fix up.
|
||||
*/
|
||||
entry = search_exception_tables(regs->psw.addr);
|
||||
entry = s390_search_extables(regs->psw.addr);
|
||||
if (entry) {
|
||||
regs->psw.addr = extable_fixup(entry);
|
||||
return 1;
|
||||
|
@@ -27,6 +27,7 @@
|
||||
#include <asm/cacheflush.h>
|
||||
#include <asm/os_info.h>
|
||||
#include <asm/set_memory.h>
|
||||
#include <asm/stacktrace.h>
|
||||
#include <asm/switch_to.h>
|
||||
#include <asm/nmi.h>
|
||||
|
||||
@@ -95,7 +96,7 @@ static void __do_machine_kdump(void *image)
|
||||
start_kdump(1);
|
||||
|
||||
/* Die if start_kdump returns */
|
||||
disabled_wait((unsigned long) __builtin_return_address(0));
|
||||
disabled_wait();
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -253,6 +254,9 @@ void arch_crash_save_vmcoreinfo(void)
|
||||
VMCOREINFO_SYMBOL(high_memory);
|
||||
VMCOREINFO_LENGTH(lowcore_ptr, NR_CPUS);
|
||||
mem_assign_absolute(S390_lowcore.vmcore_info, paddr_vmcoreinfo_note());
|
||||
vmcoreinfo_append_str("SDMA=%lx\n", __sdma);
|
||||
vmcoreinfo_append_str("EDMA=%lx\n", __edma);
|
||||
vmcoreinfo_append_str("KERNELOFFSET=%lx\n", kaslr_offset());
|
||||
}
|
||||
|
||||
void machine_shutdown(void)
|
||||
@@ -280,7 +284,7 @@ static void __do_machine_kexec(void *data)
|
||||
(*data_mover)(&image->head, image->start);
|
||||
|
||||
/* Die if kexec returns */
|
||||
disabled_wait((unsigned long) __builtin_return_address(0));
|
||||
disabled_wait();
|
||||
}
|
||||
|
||||
/*
|
||||
|
@@ -8,7 +8,12 @@
|
||||
*/
|
||||
|
||||
#include <linux/elf.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/kexec.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/verification.h>
|
||||
#include <asm/boot_data.h>
|
||||
#include <asm/ipl.h>
|
||||
#include <asm/setup.h>
|
||||
|
||||
const struct kexec_file_ops * const kexec_file_loaders[] = {
|
||||
@@ -17,38 +22,78 @@ const struct kexec_file_ops * const kexec_file_loaders[] = {
|
||||
NULL,
|
||||
};
|
||||
|
||||
int *kexec_file_update_kernel(struct kimage *image,
|
||||
struct s390_load_data *data)
|
||||
#ifdef CONFIG_KEXEC_VERIFY_SIG
|
||||
/*
|
||||
* Module signature information block.
|
||||
*
|
||||
* The constituents of the signature section are, in order:
|
||||
*
|
||||
* - Signer's name
|
||||
* - Key identifier
|
||||
* - Signature data
|
||||
* - Information block
|
||||
*/
|
||||
struct module_signature {
|
||||
u8 algo; /* Public-key crypto algorithm [0] */
|
||||
u8 hash; /* Digest algorithm [0] */
|
||||
u8 id_type; /* Key identifier type [PKEY_ID_PKCS7] */
|
||||
u8 signer_len; /* Length of signer's name [0] */
|
||||
u8 key_id_len; /* Length of key identifier [0] */
|
||||
u8 __pad[3];
|
||||
__be32 sig_len; /* Length of signature data */
|
||||
};
|
||||
|
||||
#define PKEY_ID_PKCS7 2
|
||||
|
||||
int s390_verify_sig(const char *kernel, unsigned long kernel_len)
|
||||
{
|
||||
unsigned long *loc;
|
||||
const unsigned long marker_len = sizeof(MODULE_SIG_STRING) - 1;
|
||||
struct module_signature *ms;
|
||||
unsigned long sig_len;
|
||||
|
||||
if (image->cmdline_buf_len >= ARCH_COMMAND_LINE_SIZE)
|
||||
return ERR_PTR(-EINVAL);
|
||||
/* Skip signature verification when not secure IPLed. */
|
||||
if (!ipl_secure_flag)
|
||||
return 0;
|
||||
|
||||
if (image->cmdline_buf_len)
|
||||
memcpy(data->kernel_buf + COMMAND_LINE_OFFSET,
|
||||
image->cmdline_buf, image->cmdline_buf_len);
|
||||
if (marker_len > kernel_len)
|
||||
return -EKEYREJECTED;
|
||||
|
||||
if (image->type == KEXEC_TYPE_CRASH) {
|
||||
loc = (unsigned long *)(data->kernel_buf + OLDMEM_BASE_OFFSET);
|
||||
*loc = crashk_res.start;
|
||||
if (memcmp(kernel + kernel_len - marker_len, MODULE_SIG_STRING,
|
||||
marker_len))
|
||||
return -EKEYREJECTED;
|
||||
kernel_len -= marker_len;
|
||||
|
||||
loc = (unsigned long *)(data->kernel_buf + OLDMEM_SIZE_OFFSET);
|
||||
*loc = crashk_res.end - crashk_res.start + 1;
|
||||
ms = (void *)kernel + kernel_len - sizeof(*ms);
|
||||
kernel_len -= sizeof(*ms);
|
||||
|
||||
sig_len = be32_to_cpu(ms->sig_len);
|
||||
if (sig_len >= kernel_len)
|
||||
return -EKEYREJECTED;
|
||||
kernel_len -= sig_len;
|
||||
|
||||
if (ms->id_type != PKEY_ID_PKCS7)
|
||||
return -EKEYREJECTED;
|
||||
|
||||
if (ms->algo != 0 ||
|
||||
ms->hash != 0 ||
|
||||
ms->signer_len != 0 ||
|
||||
ms->key_id_len != 0 ||
|
||||
ms->__pad[0] != 0 ||
|
||||
ms->__pad[1] != 0 ||
|
||||
ms->__pad[2] != 0) {
|
||||
return -EBADMSG;
|
||||
}
|
||||
|
||||
if (image->initrd_buf) {
|
||||
loc = (unsigned long *)(data->kernel_buf + INITRD_START_OFFSET);
|
||||
*loc = data->initrd_load_addr;
|
||||
|
||||
loc = (unsigned long *)(data->kernel_buf + INITRD_SIZE_OFFSET);
|
||||
*loc = image->initrd_buf_len;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
return verify_pkcs7_signature(kernel, kernel_len,
|
||||
kernel + kernel_len, sig_len,
|
||||
VERIFY_USE_PLATFORM_KEYRING,
|
||||
VERIFYING_MODULE_SIGNATURE,
|
||||
NULL, NULL);
|
||||
}
|
||||
#endif /* CONFIG_KEXEC_VERIFY_SIG */
|
||||
|
||||
static int kexec_file_update_purgatory(struct kimage *image)
|
||||
static int kexec_file_update_purgatory(struct kimage *image,
|
||||
struct s390_load_data *data)
|
||||
{
|
||||
u64 entry, type;
|
||||
int ret;
|
||||
@@ -90,7 +135,8 @@ static int kexec_file_update_purgatory(struct kimage *image)
|
||||
return ret;
|
||||
}
|
||||
|
||||
int kexec_file_add_purgatory(struct kimage *image, struct s390_load_data *data)
|
||||
static int kexec_file_add_purgatory(struct kimage *image,
|
||||
struct s390_load_data *data)
|
||||
{
|
||||
struct kexec_buf buf;
|
||||
int ret;
|
||||
@@ -105,21 +151,21 @@ int kexec_file_add_purgatory(struct kimage *image, struct s390_load_data *data)
|
||||
ret = kexec_load_purgatory(image, &buf);
|
||||
if (ret)
|
||||
return ret;
|
||||
data->memsz += buf.memsz;
|
||||
|
||||
ret = kexec_file_update_purgatory(image);
|
||||
return ret;
|
||||
return kexec_file_update_purgatory(image, data);
|
||||
}
|
||||
|
||||
int kexec_file_add_initrd(struct kimage *image, struct s390_load_data *data,
|
||||
char *initrd, unsigned long initrd_len)
|
||||
static int kexec_file_add_initrd(struct kimage *image,
|
||||
struct s390_load_data *data)
|
||||
{
|
||||
struct kexec_buf buf;
|
||||
int ret;
|
||||
|
||||
buf.image = image;
|
||||
|
||||
buf.buffer = initrd;
|
||||
buf.bufsz = initrd_len;
|
||||
buf.buffer = image->initrd_buf;
|
||||
buf.bufsz = image->initrd_buf_len;
|
||||
|
||||
data->memsz = ALIGN(data->memsz, PAGE_SIZE);
|
||||
buf.mem = data->memsz;
|
||||
@@ -127,11 +173,115 @@ int kexec_file_add_initrd(struct kimage *image, struct s390_load_data *data,
|
||||
buf.mem += crashk_res.start;
|
||||
buf.memsz = buf.bufsz;
|
||||
|
||||
data->initrd_load_addr = buf.mem;
|
||||
data->parm->initrd_start = buf.mem;
|
||||
data->parm->initrd_size = buf.memsz;
|
||||
data->memsz += buf.memsz;
|
||||
|
||||
ret = kexec_add_buffer(&buf);
|
||||
return ret;
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return ipl_report_add_component(data->report, &buf, 0, 0);
|
||||
}
|
||||
|
||||
static int kexec_file_add_ipl_report(struct kimage *image,
|
||||
struct s390_load_data *data)
|
||||
{
|
||||
__u32 *lc_ipl_parmblock_ptr;
|
||||
unsigned int len, ncerts;
|
||||
struct kexec_buf buf;
|
||||
unsigned long addr;
|
||||
void *ptr, *end;
|
||||
|
||||
buf.image = image;
|
||||
|
||||
data->memsz = ALIGN(data->memsz, PAGE_SIZE);
|
||||
buf.mem = data->memsz;
|
||||
if (image->type == KEXEC_TYPE_CRASH)
|
||||
buf.mem += crashk_res.start;
|
||||
|
||||
ptr = (void *)ipl_cert_list_addr;
|
||||
end = ptr + ipl_cert_list_size;
|
||||
ncerts = 0;
|
||||
while (ptr < end) {
|
||||
ncerts++;
|
||||
len = *(unsigned int *)ptr;
|
||||
ptr += sizeof(len);
|
||||
ptr += len;
|
||||
}
|
||||
|
||||
addr = data->memsz + data->report->size;
|
||||
addr += ncerts * sizeof(struct ipl_rb_certificate_entry);
|
||||
ptr = (void *)ipl_cert_list_addr;
|
||||
while (ptr < end) {
|
||||
len = *(unsigned int *)ptr;
|
||||
ptr += sizeof(len);
|
||||
ipl_report_add_certificate(data->report, ptr, addr, len);
|
||||
addr += len;
|
||||
ptr += len;
|
||||
}
|
||||
|
||||
buf.buffer = ipl_report_finish(data->report);
|
||||
buf.bufsz = data->report->size;
|
||||
buf.memsz = buf.bufsz;
|
||||
|
||||
data->memsz += buf.memsz;
|
||||
|
||||
lc_ipl_parmblock_ptr =
|
||||
data->kernel_buf + offsetof(struct lowcore, ipl_parmblock_ptr);
|
||||
*lc_ipl_parmblock_ptr = (__u32)buf.mem;
|
||||
|
||||
return kexec_add_buffer(&buf);
|
||||
}
|
||||
|
||||
void *kexec_file_add_components(struct kimage *image,
|
||||
int (*add_kernel)(struct kimage *image,
|
||||
struct s390_load_data *data))
|
||||
{
|
||||
struct s390_load_data data = {0};
|
||||
int ret;
|
||||
|
||||
data.report = ipl_report_init(&ipl_block);
|
||||
if (IS_ERR(data.report))
|
||||
return data.report;
|
||||
|
||||
ret = add_kernel(image, &data);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
if (image->cmdline_buf_len >= ARCH_COMMAND_LINE_SIZE) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
memcpy(data.parm->command_line, image->cmdline_buf,
|
||||
image->cmdline_buf_len);
|
||||
|
||||
if (image->type == KEXEC_TYPE_CRASH) {
|
||||
data.parm->oldmem_base = crashk_res.start;
|
||||
data.parm->oldmem_size = crashk_res.end - crashk_res.start + 1;
|
||||
}
|
||||
|
||||
if (image->initrd_buf) {
|
||||
ret = kexec_file_add_initrd(image, &data);
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = kexec_file_add_purgatory(image, &data);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
if (data.kernel_mem == 0) {
|
||||
unsigned long restart_psw = 0x0008000080000000UL;
|
||||
restart_psw += image->start;
|
||||
memcpy(data.kernel_buf, &restart_psw, sizeof(restart_psw));
|
||||
image->start = 0;
|
||||
}
|
||||
|
||||
ret = kexec_file_add_ipl_report(image, &data);
|
||||
out:
|
||||
ipl_report_free(data.report);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
int arch_kexec_apply_relocations_add(struct purgatory_info *pi,
|
||||
@@ -140,7 +290,7 @@ int arch_kexec_apply_relocations_add(struct purgatory_info *pi,
|
||||
const Elf_Shdr *symtab)
|
||||
{
|
||||
Elf_Rela *relas;
|
||||
int i;
|
||||
int i, r_type;
|
||||
|
||||
relas = (void *)pi->ehdr + relsec->sh_offset;
|
||||
|
||||
@@ -174,46 +324,8 @@ int arch_kexec_apply_relocations_add(struct purgatory_info *pi,
|
||||
|
||||
addr = section->sh_addr + relas[i].r_offset;
|
||||
|
||||
switch (ELF64_R_TYPE(relas[i].r_info)) {
|
||||
case R_390_8: /* Direct 8 bit. */
|
||||
*(u8 *)loc = val;
|
||||
break;
|
||||
case R_390_12: /* Direct 12 bit. */
|
||||
*(u16 *)loc &= 0xf000;
|
||||
*(u16 *)loc |= val & 0xfff;
|
||||
break;
|
||||
case R_390_16: /* Direct 16 bit. */
|
||||
*(u16 *)loc = val;
|
||||
break;
|
||||
case R_390_20: /* Direct 20 bit. */
|
||||
*(u32 *)loc &= 0xf00000ff;
|
||||
*(u32 *)loc |= (val & 0xfff) << 16; /* DL */
|
||||
*(u32 *)loc |= (val & 0xff000) >> 4; /* DH */
|
||||
break;
|
||||
case R_390_32: /* Direct 32 bit. */
|
||||
*(u32 *)loc = val;
|
||||
break;
|
||||
case R_390_64: /* Direct 64 bit. */
|
||||
*(u64 *)loc = val;
|
||||
break;
|
||||
case R_390_PC16: /* PC relative 16 bit. */
|
||||
*(u16 *)loc = (val - addr);
|
||||
break;
|
||||
case R_390_PC16DBL: /* PC relative 16 bit shifted by 1. */
|
||||
*(u16 *)loc = (val - addr) >> 1;
|
||||
break;
|
||||
case R_390_PC32DBL: /* PC relative 32 bit shifted by 1. */
|
||||
*(u32 *)loc = (val - addr) >> 1;
|
||||
break;
|
||||
case R_390_PC32: /* PC relative 32 bit. */
|
||||
*(u32 *)loc = (val - addr);
|
||||
break;
|
||||
case R_390_PC64: /* PC relative 64 bit. */
|
||||
*(u64 *)loc = (val - addr);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
r_type = ELF64_R_TYPE(relas[i].r_info);
|
||||
arch_kexec_do_relocs(r_type, loc, val, addr);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -225,10 +337,8 @@ int arch_kexec_kernel_image_probe(struct kimage *image, void *buf,
|
||||
* load memory in head.S will be accessed, e.g. to register the next
|
||||
* command line. If the next kernel were smaller the current kernel
|
||||
* will panic at load.
|
||||
*
|
||||
* 0x11000 = sizeof(head.S)
|
||||
*/
|
||||
if (buf_len < 0x11000)
|
||||
if (buf_len < HEAD_END)
|
||||
return -ENOEXEC;
|
||||
|
||||
return kexec_image_probe_default(image, buf, buf_len);
|
||||
|
53
arch/s390/kernel/machine_kexec_reloc.c
Normal file
53
arch/s390/kernel/machine_kexec_reloc.c
Normal file
@@ -0,0 +1,53 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include <linux/elf.h>
|
||||
|
||||
int arch_kexec_do_relocs(int r_type, void *loc, unsigned long val,
|
||||
unsigned long addr)
|
||||
{
|
||||
switch (r_type) {
|
||||
case R_390_NONE:
|
||||
break;
|
||||
case R_390_8: /* Direct 8 bit. */
|
||||
*(u8 *)loc = val;
|
||||
break;
|
||||
case R_390_12: /* Direct 12 bit. */
|
||||
*(u16 *)loc &= 0xf000;
|
||||
*(u16 *)loc |= val & 0xfff;
|
||||
break;
|
||||
case R_390_16: /* Direct 16 bit. */
|
||||
*(u16 *)loc = val;
|
||||
break;
|
||||
case R_390_20: /* Direct 20 bit. */
|
||||
*(u32 *)loc &= 0xf00000ff;
|
||||
*(u32 *)loc |= (val & 0xfff) << 16; /* DL */
|
||||
*(u32 *)loc |= (val & 0xff000) >> 4; /* DH */
|
||||
break;
|
||||
case R_390_32: /* Direct 32 bit. */
|
||||
*(u32 *)loc = val;
|
||||
break;
|
||||
case R_390_64: /* Direct 64 bit. */
|
||||
*(u64 *)loc = val;
|
||||
break;
|
||||
case R_390_PC16: /* PC relative 16 bit. */
|
||||
*(u16 *)loc = (val - addr);
|
||||
break;
|
||||
case R_390_PC16DBL: /* PC relative 16 bit shifted by 1. */
|
||||
*(u16 *)loc = (val - addr) >> 1;
|
||||
break;
|
||||
case R_390_PC32DBL: /* PC relative 32 bit shifted by 1. */
|
||||
*(u32 *)loc = (val - addr) >> 1;
|
||||
break;
|
||||
case R_390_PC32: /* PC relative 32 bit. */
|
||||
*(u32 *)loc = (val - addr);
|
||||
break;
|
||||
case R_390_PC64: /* PC relative 64 bit. */
|
||||
*(u64 *)loc = (val - addr);
|
||||
break;
|
||||
case R_390_RELATIVE:
|
||||
*(unsigned long *) loc = val;
|
||||
break;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
@@ -20,6 +20,7 @@
|
||||
|
||||
ENTRY(ftrace_stub)
|
||||
BR_EX %r14
|
||||
ENDPROC(ftrace_stub)
|
||||
|
||||
#define STACK_FRAME_SIZE (STACK_FRAME_OVERHEAD + __PT_SIZE)
|
||||
#define STACK_PTREGS (STACK_FRAME_OVERHEAD)
|
||||
@@ -28,7 +29,7 @@ ENTRY(ftrace_stub)
|
||||
|
||||
ENTRY(_mcount)
|
||||
BR_EX %r14
|
||||
|
||||
ENDPROC(_mcount)
|
||||
EXPORT_SYMBOL(_mcount)
|
||||
|
||||
ENTRY(ftrace_caller)
|
||||
@@ -61,10 +62,11 @@ ENTRY(ftrace_caller)
|
||||
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
|
||||
# The j instruction gets runtime patched to a nop instruction.
|
||||
# See ftrace_enable_ftrace_graph_caller.
|
||||
ENTRY(ftrace_graph_caller)
|
||||
.globl ftrace_graph_caller
|
||||
ftrace_graph_caller:
|
||||
j ftrace_graph_caller_end
|
||||
lg %r2,(STACK_PTREGS_GPRS+14*8)(%r15)
|
||||
lg %r3,(STACK_PTREGS_PSW+8)(%r15)
|
||||
lmg %r2,%r3,(STACK_PTREGS_GPRS+14*8)(%r15)
|
||||
lg %r4,(STACK_PTREGS_PSW+8)(%r15)
|
||||
brasl %r14,prepare_ftrace_return
|
||||
stg %r2,(STACK_PTREGS_GPRS+14*8)(%r15)
|
||||
ftrace_graph_caller_end:
|
||||
@@ -73,6 +75,7 @@ ftrace_graph_caller_end:
|
||||
lg %r1,(STACK_PTREGS_PSW+8)(%r15)
|
||||
lmg %r2,%r15,(STACK_PTREGS_GPRS+2*8)(%r15)
|
||||
BR_EX %r1
|
||||
ENDPROC(ftrace_caller)
|
||||
|
||||
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
|
||||
|
||||
@@ -86,5 +89,6 @@ ENTRY(return_to_handler)
|
||||
lgr %r14,%r2
|
||||
lmg %r2,%r5,32(%r15)
|
||||
BR_EX %r14
|
||||
ENDPROC(return_to_handler)
|
||||
|
||||
#endif
|
||||
|
@@ -125,7 +125,7 @@ void nmi_free_per_cpu(struct lowcore *lc)
|
||||
static notrace void s390_handle_damage(void)
|
||||
{
|
||||
smp_emergency_stop();
|
||||
disabled_wait((unsigned long) __builtin_return_address(0));
|
||||
disabled_wait();
|
||||
while (1);
|
||||
}
|
||||
NOKPROBE_SYMBOL(s390_handle_damage);
|
||||
|
@@ -38,7 +38,7 @@ static int __init nospec_report(void)
|
||||
{
|
||||
if (test_facility(156))
|
||||
pr_info("Spectre V2 mitigation: etokens\n");
|
||||
if (IS_ENABLED(CC_USING_EXPOLINE) && !nospec_disable)
|
||||
if (__is_defined(CC_USING_EXPOLINE) && !nospec_disable)
|
||||
pr_info("Spectre V2 mitigation: execute trampolines\n");
|
||||
if (__test_facility(82, S390_lowcore.alt_stfle_fac_list))
|
||||
pr_info("Spectre V2 mitigation: limited branch prediction\n");
|
||||
@@ -64,10 +64,10 @@ void __init nospec_auto_detect(void)
|
||||
* The machine supports etokens.
|
||||
* Disable expolines and disable nobp.
|
||||
*/
|
||||
if (IS_ENABLED(CC_USING_EXPOLINE))
|
||||
if (__is_defined(CC_USING_EXPOLINE))
|
||||
nospec_disable = 1;
|
||||
__clear_facility(82, S390_lowcore.alt_stfle_fac_list);
|
||||
} else if (IS_ENABLED(CC_USING_EXPOLINE)) {
|
||||
} else if (__is_defined(CC_USING_EXPOLINE)) {
|
||||
/*
|
||||
* The kernel has been compiled with expolines.
|
||||
* Keep expolines enabled and disable nobp.
|
||||
|
@@ -15,7 +15,7 @@ ssize_t cpu_show_spectre_v2(struct device *dev,
|
||||
{
|
||||
if (test_facility(156))
|
||||
return sprintf(buf, "Mitigation: etokens\n");
|
||||
if (IS_ENABLED(CC_USING_EXPOLINE) && !nospec_disable)
|
||||
if (__is_defined(CC_USING_EXPOLINE) && !nospec_disable)
|
||||
return sprintf(buf, "Mitigation: execute trampolines\n");
|
||||
if (__test_facility(82, S390_lowcore.alt_stfle_fac_list))
|
||||
return sprintf(buf, "Mitigation: limited branch prediction\n");
|
||||
|
@@ -2,8 +2,8 @@
|
||||
/*
|
||||
* Performance event support for s390x - CPU-measurement Counter Facility
|
||||
*
|
||||
* Copyright IBM Corp. 2012, 2017
|
||||
* Author(s): Hendrik Brueckner <brueckner@linux.vnet.ibm.com>
|
||||
* Copyright IBM Corp. 2012, 2019
|
||||
* Author(s): Hendrik Brueckner <brueckner@linux.ibm.com>
|
||||
*/
|
||||
#define KMSG_COMPONENT "cpum_cf"
|
||||
#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
|
||||
@@ -26,7 +26,7 @@ static enum cpumf_ctr_set get_counter_set(u64 event)
|
||||
set = CPUMF_CTR_SET_USER;
|
||||
else if (event < 128)
|
||||
set = CPUMF_CTR_SET_CRYPTO;
|
||||
else if (event < 256)
|
||||
else if (event < 288)
|
||||
set = CPUMF_CTR_SET_EXT;
|
||||
else if (event >= 448 && event < 496)
|
||||
set = CPUMF_CTR_SET_MT_DIAG;
|
||||
@@ -50,12 +50,19 @@ static int validate_ctr_version(const struct hw_perf_event *hwc)
|
||||
err = -EOPNOTSUPP;
|
||||
break;
|
||||
case CPUMF_CTR_SET_CRYPTO:
|
||||
if ((cpuhw->info.csvn >= 1 && cpuhw->info.csvn <= 5 &&
|
||||
hwc->config > 79) ||
|
||||
(cpuhw->info.csvn >= 6 && hwc->config > 83))
|
||||
err = -EOPNOTSUPP;
|
||||
break;
|
||||
case CPUMF_CTR_SET_EXT:
|
||||
if (cpuhw->info.csvn < 1)
|
||||
err = -EOPNOTSUPP;
|
||||
if ((cpuhw->info.csvn == 1 && hwc->config > 159) ||
|
||||
(cpuhw->info.csvn == 2 && hwc->config > 175) ||
|
||||
(cpuhw->info.csvn > 2 && hwc->config > 255))
|
||||
(cpuhw->info.csvn >= 3 && cpuhw->info.csvn <= 5
|
||||
&& hwc->config > 255) ||
|
||||
(cpuhw->info.csvn >= 6 && hwc->config > 287))
|
||||
err = -EOPNOTSUPP;
|
||||
break;
|
||||
case CPUMF_CTR_SET_MT_DIAG:
|
||||
|
@@ -306,15 +306,20 @@ static size_t cf_diag_ctrset_size(enum cpumf_ctr_set ctrset,
|
||||
ctrset_size = 2;
|
||||
break;
|
||||
case CPUMF_CTR_SET_CRYPTO:
|
||||
ctrset_size = 16;
|
||||
if (info->csvn >= 1 && info->csvn <= 5)
|
||||
ctrset_size = 16;
|
||||
else if (info->csvn == 6)
|
||||
ctrset_size = 20;
|
||||
break;
|
||||
case CPUMF_CTR_SET_EXT:
|
||||
if (info->csvn == 1)
|
||||
ctrset_size = 32;
|
||||
else if (info->csvn == 2)
|
||||
ctrset_size = 48;
|
||||
else if (info->csvn >= 3)
|
||||
else if (info->csvn >= 3 && info->csvn <= 5)
|
||||
ctrset_size = 128;
|
||||
else if (info->csvn == 6)
|
||||
ctrset_size = 160;
|
||||
break;
|
||||
case CPUMF_CTR_SET_MT_DIAG:
|
||||
if (info->csvn > 3)
|
||||
|
@@ -31,22 +31,26 @@ CPUMF_EVENT_ATTR(cf_fvn3, PROBLEM_STATE_CPU_CYCLES, 0x0020);
|
||||
CPUMF_EVENT_ATTR(cf_fvn3, PROBLEM_STATE_INSTRUCTIONS, 0x0021);
|
||||
CPUMF_EVENT_ATTR(cf_fvn3, L1D_DIR_WRITES, 0x0004);
|
||||
CPUMF_EVENT_ATTR(cf_fvn3, L1D_PENALTY_CYCLES, 0x0005);
|
||||
CPUMF_EVENT_ATTR(cf_svn_generic, PRNG_FUNCTIONS, 0x0040);
|
||||
CPUMF_EVENT_ATTR(cf_svn_generic, PRNG_CYCLES, 0x0041);
|
||||
CPUMF_EVENT_ATTR(cf_svn_generic, PRNG_BLOCKED_FUNCTIONS, 0x0042);
|
||||
CPUMF_EVENT_ATTR(cf_svn_generic, PRNG_BLOCKED_CYCLES, 0x0043);
|
||||
CPUMF_EVENT_ATTR(cf_svn_generic, SHA_FUNCTIONS, 0x0044);
|
||||
CPUMF_EVENT_ATTR(cf_svn_generic, SHA_CYCLES, 0x0045);
|
||||
CPUMF_EVENT_ATTR(cf_svn_generic, SHA_BLOCKED_FUNCTIONS, 0x0046);
|
||||
CPUMF_EVENT_ATTR(cf_svn_generic, SHA_BLOCKED_CYCLES, 0x0047);
|
||||
CPUMF_EVENT_ATTR(cf_svn_generic, DEA_FUNCTIONS, 0x0048);
|
||||
CPUMF_EVENT_ATTR(cf_svn_generic, DEA_CYCLES, 0x0049);
|
||||
CPUMF_EVENT_ATTR(cf_svn_generic, DEA_BLOCKED_FUNCTIONS, 0x004a);
|
||||
CPUMF_EVENT_ATTR(cf_svn_generic, DEA_BLOCKED_CYCLES, 0x004b);
|
||||
CPUMF_EVENT_ATTR(cf_svn_generic, AES_FUNCTIONS, 0x004c);
|
||||
CPUMF_EVENT_ATTR(cf_svn_generic, AES_CYCLES, 0x004d);
|
||||
CPUMF_EVENT_ATTR(cf_svn_generic, AES_BLOCKED_FUNCTIONS, 0x004e);
|
||||
CPUMF_EVENT_ATTR(cf_svn_generic, AES_BLOCKED_CYCLES, 0x004f);
|
||||
CPUMF_EVENT_ATTR(cf_svn_12345, PRNG_FUNCTIONS, 0x0040);
|
||||
CPUMF_EVENT_ATTR(cf_svn_12345, PRNG_CYCLES, 0x0041);
|
||||
CPUMF_EVENT_ATTR(cf_svn_12345, PRNG_BLOCKED_FUNCTIONS, 0x0042);
|
||||
CPUMF_EVENT_ATTR(cf_svn_12345, PRNG_BLOCKED_CYCLES, 0x0043);
|
||||
CPUMF_EVENT_ATTR(cf_svn_12345, SHA_FUNCTIONS, 0x0044);
|
||||
CPUMF_EVENT_ATTR(cf_svn_12345, SHA_CYCLES, 0x0045);
|
||||
CPUMF_EVENT_ATTR(cf_svn_12345, SHA_BLOCKED_FUNCTIONS, 0x0046);
|
||||
CPUMF_EVENT_ATTR(cf_svn_12345, SHA_BLOCKED_CYCLES, 0x0047);
|
||||
CPUMF_EVENT_ATTR(cf_svn_12345, DEA_FUNCTIONS, 0x0048);
|
||||
CPUMF_EVENT_ATTR(cf_svn_12345, DEA_CYCLES, 0x0049);
|
||||
CPUMF_EVENT_ATTR(cf_svn_12345, DEA_BLOCKED_FUNCTIONS, 0x004a);
|
||||
CPUMF_EVENT_ATTR(cf_svn_12345, DEA_BLOCKED_CYCLES, 0x004b);
|
||||
CPUMF_EVENT_ATTR(cf_svn_12345, AES_FUNCTIONS, 0x004c);
|
||||
CPUMF_EVENT_ATTR(cf_svn_12345, AES_CYCLES, 0x004d);
|
||||
CPUMF_EVENT_ATTR(cf_svn_12345, AES_BLOCKED_FUNCTIONS, 0x004e);
|
||||
CPUMF_EVENT_ATTR(cf_svn_12345, AES_BLOCKED_CYCLES, 0x004f);
|
||||
CPUMF_EVENT_ATTR(cf_svn_6, ECC_FUNCTION_COUNT, 0x0050);
|
||||
CPUMF_EVENT_ATTR(cf_svn_6, ECC_CYCLES_COUNT, 0x0051);
|
||||
CPUMF_EVENT_ATTR(cf_svn_6, ECC_BLOCKED_FUNCTION_COUNT, 0x0052);
|
||||
CPUMF_EVENT_ATTR(cf_svn_6, ECC_BLOCKED_CYCLES_COUNT, 0x0053);
|
||||
CPUMF_EVENT_ATTR(cf_z10, L1I_L2_SOURCED_WRITES, 0x0080);
|
||||
CPUMF_EVENT_ATTR(cf_z10, L1D_L2_SOURCED_WRITES, 0x0081);
|
||||
CPUMF_EVENT_ATTR(cf_z10, L1I_L3_LOCAL_WRITES, 0x0082);
|
||||
@@ -262,23 +266,47 @@ static struct attribute *cpumcf_fvn3_pmu_event_attr[] __initdata = {
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct attribute *cpumcf_svn_generic_pmu_event_attr[] __initdata = {
|
||||
CPUMF_EVENT_PTR(cf_svn_generic, PRNG_FUNCTIONS),
|
||||
CPUMF_EVENT_PTR(cf_svn_generic, PRNG_CYCLES),
|
||||
CPUMF_EVENT_PTR(cf_svn_generic, PRNG_BLOCKED_FUNCTIONS),
|
||||
CPUMF_EVENT_PTR(cf_svn_generic, PRNG_BLOCKED_CYCLES),
|
||||
CPUMF_EVENT_PTR(cf_svn_generic, SHA_FUNCTIONS),
|
||||
CPUMF_EVENT_PTR(cf_svn_generic, SHA_CYCLES),
|
||||
CPUMF_EVENT_PTR(cf_svn_generic, SHA_BLOCKED_FUNCTIONS),
|
||||
CPUMF_EVENT_PTR(cf_svn_generic, SHA_BLOCKED_CYCLES),
|
||||
CPUMF_EVENT_PTR(cf_svn_generic, DEA_FUNCTIONS),
|
||||
CPUMF_EVENT_PTR(cf_svn_generic, DEA_CYCLES),
|
||||
CPUMF_EVENT_PTR(cf_svn_generic, DEA_BLOCKED_FUNCTIONS),
|
||||
CPUMF_EVENT_PTR(cf_svn_generic, DEA_BLOCKED_CYCLES),
|
||||
CPUMF_EVENT_PTR(cf_svn_generic, AES_FUNCTIONS),
|
||||
CPUMF_EVENT_PTR(cf_svn_generic, AES_CYCLES),
|
||||
CPUMF_EVENT_PTR(cf_svn_generic, AES_BLOCKED_FUNCTIONS),
|
||||
CPUMF_EVENT_PTR(cf_svn_generic, AES_BLOCKED_CYCLES),
|
||||
static struct attribute *cpumcf_svn_12345_pmu_event_attr[] __initdata = {
|
||||
CPUMF_EVENT_PTR(cf_svn_12345, PRNG_FUNCTIONS),
|
||||
CPUMF_EVENT_PTR(cf_svn_12345, PRNG_CYCLES),
|
||||
CPUMF_EVENT_PTR(cf_svn_12345, PRNG_BLOCKED_FUNCTIONS),
|
||||
CPUMF_EVENT_PTR(cf_svn_12345, PRNG_BLOCKED_CYCLES),
|
||||
CPUMF_EVENT_PTR(cf_svn_12345, SHA_FUNCTIONS),
|
||||
CPUMF_EVENT_PTR(cf_svn_12345, SHA_CYCLES),
|
||||
CPUMF_EVENT_PTR(cf_svn_12345, SHA_BLOCKED_FUNCTIONS),
|
||||
CPUMF_EVENT_PTR(cf_svn_12345, SHA_BLOCKED_CYCLES),
|
||||
CPUMF_EVENT_PTR(cf_svn_12345, DEA_FUNCTIONS),
|
||||
CPUMF_EVENT_PTR(cf_svn_12345, DEA_CYCLES),
|
||||
CPUMF_EVENT_PTR(cf_svn_12345, DEA_BLOCKED_FUNCTIONS),
|
||||
CPUMF_EVENT_PTR(cf_svn_12345, DEA_BLOCKED_CYCLES),
|
||||
CPUMF_EVENT_PTR(cf_svn_12345, AES_FUNCTIONS),
|
||||
CPUMF_EVENT_PTR(cf_svn_12345, AES_CYCLES),
|
||||
CPUMF_EVENT_PTR(cf_svn_12345, AES_BLOCKED_FUNCTIONS),
|
||||
CPUMF_EVENT_PTR(cf_svn_12345, AES_BLOCKED_CYCLES),
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct attribute *cpumcf_svn_6_pmu_event_attr[] __initdata = {
|
||||
CPUMF_EVENT_PTR(cf_svn_12345, PRNG_FUNCTIONS),
|
||||
CPUMF_EVENT_PTR(cf_svn_12345, PRNG_CYCLES),
|
||||
CPUMF_EVENT_PTR(cf_svn_12345, PRNG_BLOCKED_FUNCTIONS),
|
||||
CPUMF_EVENT_PTR(cf_svn_12345, PRNG_BLOCKED_CYCLES),
|
||||
CPUMF_EVENT_PTR(cf_svn_12345, SHA_FUNCTIONS),
|
||||
CPUMF_EVENT_PTR(cf_svn_12345, SHA_CYCLES),
|
||||
CPUMF_EVENT_PTR(cf_svn_12345, SHA_BLOCKED_FUNCTIONS),
|
||||
CPUMF_EVENT_PTR(cf_svn_12345, SHA_BLOCKED_CYCLES),
|
||||
CPUMF_EVENT_PTR(cf_svn_12345, DEA_FUNCTIONS),
|
||||
CPUMF_EVENT_PTR(cf_svn_12345, DEA_CYCLES),
|
||||
CPUMF_EVENT_PTR(cf_svn_12345, DEA_BLOCKED_FUNCTIONS),
|
||||
CPUMF_EVENT_PTR(cf_svn_12345, DEA_BLOCKED_CYCLES),
|
||||
CPUMF_EVENT_PTR(cf_svn_12345, AES_FUNCTIONS),
|
||||
CPUMF_EVENT_PTR(cf_svn_12345, AES_CYCLES),
|
||||
CPUMF_EVENT_PTR(cf_svn_12345, AES_BLOCKED_FUNCTIONS),
|
||||
CPUMF_EVENT_PTR(cf_svn_12345, AES_BLOCKED_CYCLES),
|
||||
CPUMF_EVENT_PTR(cf_svn_6, ECC_FUNCTION_COUNT),
|
||||
CPUMF_EVENT_PTR(cf_svn_6, ECC_CYCLES_COUNT),
|
||||
CPUMF_EVENT_PTR(cf_svn_6, ECC_BLOCKED_FUNCTION_COUNT),
|
||||
CPUMF_EVENT_PTR(cf_svn_6, ECC_BLOCKED_CYCLES_COUNT),
|
||||
NULL,
|
||||
};
|
||||
|
||||
@@ -562,7 +590,18 @@ __init const struct attribute_group **cpumf_cf_event_group(void)
|
||||
default:
|
||||
cfvn = none;
|
||||
}
|
||||
csvn = cpumcf_svn_generic_pmu_event_attr;
|
||||
|
||||
/* Determine version specific crypto set */
|
||||
switch (ci.csvn) {
|
||||
case 1 ... 5:
|
||||
csvn = cpumcf_svn_12345_pmu_event_attr;
|
||||
break;
|
||||
case 6:
|
||||
csvn = cpumcf_svn_6_pmu_event_attr;
|
||||
break;
|
||||
default:
|
||||
csvn = none;
|
||||
}
|
||||
|
||||
/* Determine model-specific counter set(s) */
|
||||
get_cpu_id(&cpu_id);
|
||||
|
@@ -21,6 +21,7 @@
|
||||
#include <asm/lowcore.h>
|
||||
#include <asm/processor.h>
|
||||
#include <asm/sysinfo.h>
|
||||
#include <asm/unwind.h>
|
||||
|
||||
const char *perf_pmu_name(void)
|
||||
{
|
||||
@@ -219,20 +220,13 @@ static int __init service_level_perf_register(void)
|
||||
}
|
||||
arch_initcall(service_level_perf_register);
|
||||
|
||||
static int __perf_callchain_kernel(void *data, unsigned long address, int reliable)
|
||||
{
|
||||
struct perf_callchain_entry_ctx *entry = data;
|
||||
|
||||
perf_callchain_store(entry, address);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void perf_callchain_kernel(struct perf_callchain_entry_ctx *entry,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
if (user_mode(regs))
|
||||
return;
|
||||
dump_trace(__perf_callchain_kernel, entry, NULL, regs->gprs[15]);
|
||||
struct unwind_state state;
|
||||
|
||||
unwind_for_each_frame(&state, current, regs, 0)
|
||||
perf_callchain_store(entry, state.ip);
|
||||
}
|
||||
|
||||
/* Perf definitions for PMU event attributes in sysfs */
|
||||
|
@@ -7,7 +7,7 @@
|
||||
|
||||
#include <linux/linkage.h>
|
||||
|
||||
#define PGM_CHECK(handler) .long handler
|
||||
#define PGM_CHECK(handler) .quad handler
|
||||
#define PGM_CHECK_DEFAULT PGM_CHECK(default_trap_handler)
|
||||
|
||||
/*
|
||||
|
@@ -37,6 +37,7 @@
|
||||
#include <asm/irq.h>
|
||||
#include <asm/nmi.h>
|
||||
#include <asm/smp.h>
|
||||
#include <asm/stacktrace.h>
|
||||
#include <asm/switch_to.h>
|
||||
#include <asm/runtime_instr.h>
|
||||
#include "entry.h"
|
||||
|
@@ -109,7 +109,8 @@ static void show_cpu_summary(struct seq_file *m, void *v)
|
||||
{
|
||||
static const char *hwcap_str[] = {
|
||||
"esan3", "zarch", "stfle", "msa", "ldisp", "eimm", "dfp",
|
||||
"edat", "etf3eh", "highgprs", "te", "vx", "vxd", "vxe", "gs"
|
||||
"edat", "etf3eh", "highgprs", "te", "vx", "vxd", "vxe", "gs",
|
||||
"vxe2", "vxp", "sort", "dflt"
|
||||
};
|
||||
static const char * const int_hwcap_str[] = {
|
||||
"sie"
|
||||
|
@@ -73,6 +73,7 @@ ENTRY(store_status)
|
||||
lgr %r9,%r2
|
||||
lgr %r2,%r3
|
||||
BR_EX %r9
|
||||
ENDPROC(store_status)
|
||||
|
||||
.section .bss
|
||||
.align 8
|
||||
|
@@ -58,11 +58,15 @@ ENTRY(relocate_kernel)
|
||||
j .base
|
||||
.done:
|
||||
sgr %r0,%r0 # clear register r0
|
||||
cghi %r3,0
|
||||
je .diag
|
||||
la %r4,load_psw-.base(%r13) # load psw-address into the register
|
||||
o %r3,4(%r4) # or load address into psw
|
||||
st %r3,4(%r4)
|
||||
mvc 0(8,%r0),0(%r4) # copy psw to absolute address 0
|
||||
.diag:
|
||||
diag %r0,%r0,0x308
|
||||
ENDPROC(relocate_kernel)
|
||||
|
||||
.align 8
|
||||
load_psw:
|
||||
|
@@ -50,6 +50,7 @@
|
||||
#include <linux/compat.h>
|
||||
#include <linux/start_kernel.h>
|
||||
|
||||
#include <asm/boot_data.h>
|
||||
#include <asm/ipl.h>
|
||||
#include <asm/facility.h>
|
||||
#include <asm/smp.h>
|
||||
@@ -65,11 +66,13 @@
|
||||
#include <asm/diag.h>
|
||||
#include <asm/os_info.h>
|
||||
#include <asm/sclp.h>
|
||||
#include <asm/stacktrace.h>
|
||||
#include <asm/sysinfo.h>
|
||||
#include <asm/numa.h>
|
||||
#include <asm/alternative.h>
|
||||
#include <asm/nospec-branch.h>
|
||||
#include <asm/mem_detect.h>
|
||||
#include <asm/uv.h>
|
||||
#include "entry.h"
|
||||
|
||||
/*
|
||||
@@ -89,12 +92,25 @@ char elf_platform[ELF_PLATFORM_SIZE];
|
||||
|
||||
unsigned long int_hwcap = 0;
|
||||
|
||||
#ifdef CONFIG_PROTECTED_VIRTUALIZATION_GUEST
|
||||
int __bootdata_preserved(prot_virt_guest);
|
||||
#endif
|
||||
|
||||
int __bootdata(noexec_disabled);
|
||||
int __bootdata(memory_end_set);
|
||||
unsigned long __bootdata(memory_end);
|
||||
unsigned long __bootdata(max_physmem_end);
|
||||
struct mem_detect_info __bootdata(mem_detect);
|
||||
|
||||
struct exception_table_entry *__bootdata_preserved(__start_dma_ex_table);
|
||||
struct exception_table_entry *__bootdata_preserved(__stop_dma_ex_table);
|
||||
unsigned long __bootdata_preserved(__swsusp_reset_dma);
|
||||
unsigned long __bootdata_preserved(__stext_dma);
|
||||
unsigned long __bootdata_preserved(__etext_dma);
|
||||
unsigned long __bootdata_preserved(__sdma);
|
||||
unsigned long __bootdata_preserved(__edma);
|
||||
unsigned long __bootdata_preserved(__kaslr_offset);
|
||||
|
||||
unsigned long VMALLOC_START;
|
||||
EXPORT_SYMBOL(VMALLOC_START);
|
||||
|
||||
@@ -736,6 +752,15 @@ static void __init reserve_initrd(void)
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Reserve the memory area used to pass the certificate lists
|
||||
*/
|
||||
static void __init reserve_certificate_list(void)
|
||||
{
|
||||
if (ipl_cert_list_addr)
|
||||
memblock_reserve(ipl_cert_list_addr, ipl_cert_list_size);
|
||||
}
|
||||
|
||||
static void __init reserve_mem_detect_info(void)
|
||||
{
|
||||
unsigned long start, size;
|
||||
@@ -814,9 +839,10 @@ static void __init reserve_kernel(void)
|
||||
{
|
||||
unsigned long start_pfn = PFN_UP(__pa(_end));
|
||||
|
||||
memblock_reserve(0, PARMAREA_END);
|
||||
memblock_reserve(0, HEAD_END);
|
||||
memblock_reserve((unsigned long)_stext, PFN_PHYS(start_pfn)
|
||||
- (unsigned long)_stext);
|
||||
memblock_reserve(__sdma, __edma - __sdma);
|
||||
}
|
||||
|
||||
static void __init setup_memory(void)
|
||||
@@ -914,7 +940,15 @@ static int __init setup_hwcaps(void)
|
||||
elf_hwcap |= HWCAP_S390_VXRS_EXT;
|
||||
if (test_facility(135))
|
||||
elf_hwcap |= HWCAP_S390_VXRS_BCD;
|
||||
if (test_facility(148))
|
||||
elf_hwcap |= HWCAP_S390_VXRS_EXT2;
|
||||
if (test_facility(152))
|
||||
elf_hwcap |= HWCAP_S390_VXRS_PDE;
|
||||
}
|
||||
if (test_facility(150))
|
||||
elf_hwcap |= HWCAP_S390_SORT;
|
||||
if (test_facility(151))
|
||||
elf_hwcap |= HWCAP_S390_DFLT;
|
||||
|
||||
/*
|
||||
* Guarded storage support HWCAP_S390_GS is bit 12.
|
||||
@@ -1022,6 +1056,38 @@ static void __init setup_control_program_code(void)
|
||||
asm volatile("diag %0,0,0x318\n" : : "d" (diag318_info.val));
|
||||
}
|
||||
|
||||
/*
|
||||
* Print the component list from the IPL report
|
||||
*/
|
||||
static void __init log_component_list(void)
|
||||
{
|
||||
struct ipl_rb_component_entry *ptr, *end;
|
||||
char *str;
|
||||
|
||||
if (!early_ipl_comp_list_addr)
|
||||
return;
|
||||
if (ipl_block.hdr.flags & IPL_PL_FLAG_IPLSR)
|
||||
pr_info("Linux is running with Secure-IPL enabled\n");
|
||||
else
|
||||
pr_info("Linux is running with Secure-IPL disabled\n");
|
||||
ptr = (void *) early_ipl_comp_list_addr;
|
||||
end = (void *) ptr + early_ipl_comp_list_size;
|
||||
pr_info("The IPL report contains the following components:\n");
|
||||
while (ptr < end) {
|
||||
if (ptr->flags & IPL_RB_COMPONENT_FLAG_SIGNED) {
|
||||
if (ptr->flags & IPL_RB_COMPONENT_FLAG_VERIFIED)
|
||||
str = "signed, verified";
|
||||
else
|
||||
str = "signed, verification failed";
|
||||
} else {
|
||||
str = "not signed";
|
||||
}
|
||||
pr_info("%016llx - %016llx (%s)\n",
|
||||
ptr->addr, ptr->addr + ptr->len, str);
|
||||
ptr++;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Setup function called from init/main.c just after the banner
|
||||
* was printed.
|
||||
@@ -1042,6 +1108,8 @@ void __init setup_arch(char **cmdline_p)
|
||||
else
|
||||
pr_info("Linux is running as a guest in 64-bit mode\n");
|
||||
|
||||
log_component_list();
|
||||
|
||||
/* Have one command line that is parsed and saved in /proc/cmdline */
|
||||
/* boot_command_line has been already set up in early.c */
|
||||
*cmdline_p = boot_command_line;
|
||||
@@ -1073,6 +1141,7 @@ void __init setup_arch(char **cmdline_p)
|
||||
reserve_oldmem();
|
||||
reserve_kernel();
|
||||
reserve_initrd();
|
||||
reserve_certificate_list();
|
||||
reserve_mem_detect_info();
|
||||
memblock_allow_resize();
|
||||
|
||||
|
@@ -53,6 +53,7 @@
|
||||
#include <asm/sigp.h>
|
||||
#include <asm/idle.h>
|
||||
#include <asm/nmi.h>
|
||||
#include <asm/stacktrace.h>
|
||||
#include <asm/topology.h>
|
||||
#include "entry.h"
|
||||
|
||||
@@ -689,7 +690,7 @@ void __init smp_save_dump_cpus(void)
|
||||
smp_save_cpu_regs(sa, addr, is_boot_cpu, page);
|
||||
}
|
||||
memblock_free(page, PAGE_SIZE);
|
||||
diag308_reset();
|
||||
diag_dma_ops.diag308_reset();
|
||||
pcpu_set_smt(0);
|
||||
}
|
||||
#endif /* CONFIG_CRASH_DUMP */
|
||||
|
@@ -11,59 +11,52 @@
|
||||
#include <linux/stacktrace.h>
|
||||
#include <linux/kallsyms.h>
|
||||
#include <linux/export.h>
|
||||
|
||||
static int __save_address(void *data, unsigned long address, int nosched)
|
||||
{
|
||||
struct stack_trace *trace = data;
|
||||
|
||||
if (nosched && in_sched_functions(address))
|
||||
return 0;
|
||||
if (trace->skip > 0) {
|
||||
trace->skip--;
|
||||
return 0;
|
||||
}
|
||||
if (trace->nr_entries < trace->max_entries) {
|
||||
trace->entries[trace->nr_entries++] = address;
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int save_address(void *data, unsigned long address, int reliable)
|
||||
{
|
||||
return __save_address(data, address, 0);
|
||||
}
|
||||
|
||||
static int save_address_nosched(void *data, unsigned long address, int reliable)
|
||||
{
|
||||
return __save_address(data, address, 1);
|
||||
}
|
||||
#include <asm/stacktrace.h>
|
||||
#include <asm/unwind.h>
|
||||
|
||||
void save_stack_trace(struct stack_trace *trace)
|
||||
{
|
||||
unsigned long sp;
|
||||
struct unwind_state state;
|
||||
|
||||
sp = current_stack_pointer();
|
||||
dump_trace(save_address, trace, NULL, sp);
|
||||
unwind_for_each_frame(&state, current, NULL, 0) {
|
||||
if (trace->nr_entries >= trace->max_entries)
|
||||
break;
|
||||
if (trace->skip > 0)
|
||||
trace->skip--;
|
||||
else
|
||||
trace->entries[trace->nr_entries++] = state.ip;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(save_stack_trace);
|
||||
|
||||
void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace)
|
||||
{
|
||||
unsigned long sp;
|
||||
struct unwind_state state;
|
||||
|
||||
sp = tsk->thread.ksp;
|
||||
if (tsk == current)
|
||||
sp = current_stack_pointer();
|
||||
dump_trace(save_address_nosched, trace, tsk, sp);
|
||||
unwind_for_each_frame(&state, tsk, NULL, 0) {
|
||||
if (trace->nr_entries >= trace->max_entries)
|
||||
break;
|
||||
if (in_sched_functions(state.ip))
|
||||
continue;
|
||||
if (trace->skip > 0)
|
||||
trace->skip--;
|
||||
else
|
||||
trace->entries[trace->nr_entries++] = state.ip;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(save_stack_trace_tsk);
|
||||
|
||||
void save_stack_trace_regs(struct pt_regs *regs, struct stack_trace *trace)
|
||||
{
|
||||
unsigned long sp;
|
||||
struct unwind_state state;
|
||||
|
||||
sp = kernel_stack_pointer(regs);
|
||||
dump_trace(save_address, trace, NULL, sp);
|
||||
unwind_for_each_frame(&state, current, regs, 0) {
|
||||
if (trace->nr_entries >= trace->max_entries)
|
||||
break;
|
||||
if (trace->skip > 0)
|
||||
trace->skip--;
|
||||
else
|
||||
trace->entries[trace->nr_entries++] = state.ip;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(save_stack_trace_regs);
|
||||
|
@@ -108,6 +108,7 @@ ENTRY(swsusp_arch_suspend)
|
||||
lmg %r6,%r15,STACK_FRAME_OVERHEAD + __SF_GPRS(%r15)
|
||||
lghi %r2,0
|
||||
BR_EX %r14
|
||||
ENDPROC(swsusp_arch_suspend)
|
||||
|
||||
/*
|
||||
* Restore saved memory image to correct place and restore register context.
|
||||
@@ -154,20 +155,13 @@ ENTRY(swsusp_arch_resume)
|
||||
ptlb /* flush tlb */
|
||||
|
||||
/* Reset System */
|
||||
larl %r1,restart_entry
|
||||
larl %r2,.Lrestart_diag308_psw
|
||||
og %r1,0(%r2)
|
||||
stg %r1,0(%r0)
|
||||
larl %r1,.Lnew_pgm_check_psw
|
||||
epsw %r2,%r3
|
||||
stm %r2,%r3,0(%r1)
|
||||
mvc __LC_PGM_NEW_PSW(16,%r0),0(%r1)
|
||||
lghi %r0,0
|
||||
diag %r0,%r0,0x308
|
||||
restart_entry:
|
||||
lhi %r1,1
|
||||
sigp %r1,%r0,SIGP_SET_ARCHITECTURE
|
||||
sam64
|
||||
larl %r1,__swsusp_reset_dma
|
||||
lg %r1,0(%r1)
|
||||
BASR_EX %r14,%r1
|
||||
#ifdef CONFIG_SMP
|
||||
larl %r1,smp_cpu_mt_shift
|
||||
icm %r1,15,0(%r1)
|
||||
@@ -267,6 +261,7 @@ restore_registers:
|
||||
lmg %r6,%r15,STACK_FRAME_OVERHEAD + __SF_GPRS(%r15)
|
||||
lghi %r2,0
|
||||
BR_EX %r14
|
||||
ENDPROC(swsusp_arch_resume)
|
||||
|
||||
.section .data..nosave,"aw",@progbits
|
||||
.align 8
|
||||
@@ -275,8 +270,6 @@ restore_registers:
|
||||
.Lpanic_string:
|
||||
.asciz "Resume not possible because suspend CPU is no longer available\n"
|
||||
.align 8
|
||||
.Lrestart_diag308_psw:
|
||||
.long 0x00080000,0x80000000
|
||||
.Lrestart_suspend_psw:
|
||||
.quad 0x0000000180000000,restart_suspend
|
||||
.Lnew_pgm_check_psw:
|
||||
|
@@ -49,7 +49,7 @@ void do_report_trap(struct pt_regs *regs, int si_signo, int si_code, char *str)
|
||||
report_user_fault(regs, si_signo, 0);
|
||||
} else {
|
||||
const struct exception_table_entry *fixup;
|
||||
fixup = search_exception_tables(regs->psw.addr);
|
||||
fixup = s390_search_extables(regs->psw.addr);
|
||||
if (fixup)
|
||||
regs->psw.addr = extable_fixup(fixup);
|
||||
else {
|
||||
@@ -263,5 +263,6 @@ NOKPROBE_SYMBOL(kernel_stack_overflow);
|
||||
|
||||
void __init trap_init(void)
|
||||
{
|
||||
sort_extable(__start_dma_ex_table, __stop_dma_ex_table);
|
||||
local_mcck_enable();
|
||||
}
|
||||
|
155
arch/s390/kernel/unwind_bc.c
Normal file
155
arch/s390/kernel/unwind_bc.c
Normal file
@@ -0,0 +1,155 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#include <linux/sched.h>
|
||||
#include <linux/sched/task.h>
|
||||
#include <linux/sched/task_stack.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <asm/sections.h>
|
||||
#include <asm/ptrace.h>
|
||||
#include <asm/bitops.h>
|
||||
#include <asm/stacktrace.h>
|
||||
#include <asm/unwind.h>
|
||||
|
||||
unsigned long unwind_get_return_address(struct unwind_state *state)
|
||||
{
|
||||
if (unwind_done(state))
|
||||
return 0;
|
||||
return __kernel_text_address(state->ip) ? state->ip : 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(unwind_get_return_address);
|
||||
|
||||
static bool outside_of_stack(struct unwind_state *state, unsigned long sp)
|
||||
{
|
||||
return (sp <= state->sp) ||
|
||||
(sp + sizeof(struct stack_frame) > state->stack_info.end);
|
||||
}
|
||||
|
||||
static bool update_stack_info(struct unwind_state *state, unsigned long sp)
|
||||
{
|
||||
struct stack_info *info = &state->stack_info;
|
||||
unsigned long *mask = &state->stack_mask;
|
||||
|
||||
/* New stack pointer leaves the current stack */
|
||||
if (get_stack_info(sp, state->task, info, mask) != 0 ||
|
||||
!on_stack(info, sp, sizeof(struct stack_frame)))
|
||||
/* 'sp' does not point to a valid stack */
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool unwind_next_frame(struct unwind_state *state)
|
||||
{
|
||||
struct stack_info *info = &state->stack_info;
|
||||
struct stack_frame *sf;
|
||||
struct pt_regs *regs;
|
||||
unsigned long sp, ip;
|
||||
bool reliable;
|
||||
|
||||
regs = state->regs;
|
||||
if (unlikely(regs)) {
|
||||
sp = READ_ONCE_TASK_STACK(state->task, regs->gprs[15]);
|
||||
if (unlikely(outside_of_stack(state, sp))) {
|
||||
if (!update_stack_info(state, sp))
|
||||
goto out_err;
|
||||
}
|
||||
sf = (struct stack_frame *) sp;
|
||||
ip = READ_ONCE_TASK_STACK(state->task, sf->gprs[8]);
|
||||
reliable = false;
|
||||
regs = NULL;
|
||||
} else {
|
||||
sf = (struct stack_frame *) state->sp;
|
||||
sp = READ_ONCE_TASK_STACK(state->task, sf->back_chain);
|
||||
if (likely(sp)) {
|
||||
/* Non-zero back-chain points to the previous frame */
|
||||
if (unlikely(outside_of_stack(state, sp))) {
|
||||
if (!update_stack_info(state, sp))
|
||||
goto out_err;
|
||||
}
|
||||
sf = (struct stack_frame *) sp;
|
||||
ip = READ_ONCE_TASK_STACK(state->task, sf->gprs[8]);
|
||||
reliable = true;
|
||||
} else {
|
||||
/* No back-chain, look for a pt_regs structure */
|
||||
sp = state->sp + STACK_FRAME_OVERHEAD;
|
||||
if (!on_stack(info, sp, sizeof(struct pt_regs)))
|
||||
goto out_stop;
|
||||
regs = (struct pt_regs *) sp;
|
||||
if (user_mode(regs))
|
||||
goto out_stop;
|
||||
ip = READ_ONCE_TASK_STACK(state->task, regs->psw.addr);
|
||||
reliable = true;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
|
||||
/* Decode any ftrace redirection */
|
||||
if (ip == (unsigned long) return_to_handler)
|
||||
ip = ftrace_graph_ret_addr(state->task, &state->graph_idx,
|
||||
ip, (void *) sp);
|
||||
#endif
|
||||
|
||||
/* Update unwind state */
|
||||
state->sp = sp;
|
||||
state->ip = ip;
|
||||
state->regs = regs;
|
||||
state->reliable = reliable;
|
||||
return true;
|
||||
|
||||
out_err:
|
||||
state->error = true;
|
||||
out_stop:
|
||||
state->stack_info.type = STACK_TYPE_UNKNOWN;
|
||||
return false;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(unwind_next_frame);
|
||||
|
||||
void __unwind_start(struct unwind_state *state, struct task_struct *task,
|
||||
struct pt_regs *regs, unsigned long sp)
|
||||
{
|
||||
struct stack_info *info = &state->stack_info;
|
||||
unsigned long *mask = &state->stack_mask;
|
||||
struct stack_frame *sf;
|
||||
unsigned long ip;
|
||||
bool reliable;
|
||||
|
||||
memset(state, 0, sizeof(*state));
|
||||
state->task = task;
|
||||
state->regs = regs;
|
||||
|
||||
/* Don't even attempt to start from user mode regs: */
|
||||
if (regs && user_mode(regs)) {
|
||||
info->type = STACK_TYPE_UNKNOWN;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Get current stack pointer and initialize stack info */
|
||||
if (get_stack_info(sp, task, info, mask) != 0 ||
|
||||
!on_stack(info, sp, sizeof(struct stack_frame))) {
|
||||
/* Something is wrong with the stack pointer */
|
||||
info->type = STACK_TYPE_UNKNOWN;
|
||||
state->error = true;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Get the instruction pointer from pt_regs or the stack frame */
|
||||
if (regs) {
|
||||
ip = READ_ONCE_TASK_STACK(state->task, regs->psw.addr);
|
||||
reliable = true;
|
||||
} else {
|
||||
sf = (struct stack_frame *) sp;
|
||||
ip = READ_ONCE_TASK_STACK(state->task, sf->gprs[8]);
|
||||
reliable = false;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
|
||||
/* Decode any ftrace redirection */
|
||||
if (ip == (unsigned long) return_to_handler)
|
||||
ip = ftrace_graph_ret_addr(state->task, &state->graph_idx,
|
||||
ip, NULL);
|
||||
#endif
|
||||
|
||||
/* Update unwind state */
|
||||
state->sp = sp;
|
||||
state->ip = ip;
|
||||
state->reliable = reliable;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(__unwind_start);
|
@@ -29,7 +29,7 @@
|
||||
#include <asm/vdso.h>
|
||||
#include <asm/facility.h>
|
||||
|
||||
#ifdef CONFIG_COMPAT
|
||||
#ifdef CONFIG_COMPAT_VDSO
|
||||
extern char vdso32_start, vdso32_end;
|
||||
static void *vdso32_kbase = &vdso32_start;
|
||||
static unsigned int vdso32_pages;
|
||||
@@ -55,7 +55,7 @@ static vm_fault_t vdso_fault(const struct vm_special_mapping *sm,
|
||||
|
||||
vdso_pagelist = vdso64_pagelist;
|
||||
vdso_pages = vdso64_pages;
|
||||
#ifdef CONFIG_COMPAT
|
||||
#ifdef CONFIG_COMPAT_VDSO
|
||||
if (vma->vm_mm->context.compat_mm) {
|
||||
vdso_pagelist = vdso32_pagelist;
|
||||
vdso_pages = vdso32_pages;
|
||||
@@ -76,7 +76,7 @@ static int vdso_mremap(const struct vm_special_mapping *sm,
|
||||
unsigned long vdso_pages;
|
||||
|
||||
vdso_pages = vdso64_pages;
|
||||
#ifdef CONFIG_COMPAT
|
||||
#ifdef CONFIG_COMPAT_VDSO
|
||||
if (vma->vm_mm->context.compat_mm)
|
||||
vdso_pages = vdso32_pages;
|
||||
#endif
|
||||
@@ -223,7 +223,7 @@ int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp)
|
||||
return 0;
|
||||
|
||||
vdso_pages = vdso64_pages;
|
||||
#ifdef CONFIG_COMPAT
|
||||
#ifdef CONFIG_COMPAT_VDSO
|
||||
mm->context.compat_mm = is_compat_task();
|
||||
if (mm->context.compat_mm)
|
||||
vdso_pages = vdso32_pages;
|
||||
@@ -280,7 +280,7 @@ static int __init vdso_init(void)
|
||||
int i;
|
||||
|
||||
vdso_init_data(vdso_data);
|
||||
#ifdef CONFIG_COMPAT
|
||||
#ifdef CONFIG_COMPAT_VDSO
|
||||
/* Calculate the size of the 32 bit vDSO */
|
||||
vdso32_pages = ((&vdso32_end - &vdso32_start
|
||||
+ PAGE_SIZE - 1) >> PAGE_SHIFT) + 1;
|
||||
|
@@ -19,7 +19,7 @@ KBUILD_AFLAGS_31 += -m31 -s
|
||||
KBUILD_CFLAGS_31 := $(filter-out -m64,$(KBUILD_CFLAGS))
|
||||
KBUILD_CFLAGS_31 += -m31 -fPIC -shared -fno-common -fno-builtin
|
||||
KBUILD_CFLAGS_31 += -nostdlib -Wl,-soname=linux-vdso32.so.1 \
|
||||
$(call cc-ldoption, -Wl$(comma)--hash-style=both)
|
||||
-Wl,--hash-style=both
|
||||
|
||||
$(targets:%=$(obj)/%.dbg): KBUILD_CFLAGS = $(KBUILD_CFLAGS_31)
|
||||
$(targets:%=$(obj)/%.dbg): KBUILD_AFLAGS = $(KBUILD_AFLAGS_31)
|
||||
|
@@ -19,7 +19,7 @@ KBUILD_AFLAGS_64 += -m64 -s
|
||||
KBUILD_CFLAGS_64 := $(filter-out -m64,$(KBUILD_CFLAGS))
|
||||
KBUILD_CFLAGS_64 += -m64 -fPIC -shared -fno-common -fno-builtin
|
||||
KBUILD_CFLAGS_64 += -nostdlib -Wl,-soname=linux-vdso64.so.1 \
|
||||
$(call cc-ldoption, -Wl$(comma)--hash-style=both)
|
||||
-Wl,--hash-style=both
|
||||
|
||||
$(targets:%=$(obj)/%.dbg): KBUILD_CFLAGS = $(KBUILD_CFLAGS_64)
|
||||
$(targets:%=$(obj)/%.dbg): KBUILD_AFLAGS = $(KBUILD_AFLAGS_64)
|
||||
|
@@ -72,6 +72,7 @@ SECTIONS
|
||||
__end_ro_after_init = .;
|
||||
|
||||
RW_DATA_SECTION(0x100, PAGE_SIZE, THREAD_SIZE)
|
||||
BOOT_DATA_PRESERVED
|
||||
|
||||
_edata = .; /* End of data section */
|
||||
|
||||
@@ -143,6 +144,18 @@ SECTIONS
|
||||
INIT_DATA_SECTION(0x100)
|
||||
|
||||
PERCPU_SECTION(0x100)
|
||||
|
||||
.dynsym ALIGN(8) : {
|
||||
__dynsym_start = .;
|
||||
*(.dynsym)
|
||||
__dynsym_end = .;
|
||||
}
|
||||
.rela.dyn ALIGN(8) : {
|
||||
__rela_dyn_start = .;
|
||||
*(.rela*)
|
||||
__rela_dyn_end = .;
|
||||
}
|
||||
|
||||
. = ALIGN(PAGE_SIZE);
|
||||
__init_end = .; /* freed after init ends here */
|
||||
|
||||
@@ -161,6 +174,12 @@ SECTIONS
|
||||
QUAD(__bss_stop - __bss_start) /* bss_size */
|
||||
QUAD(__boot_data_start) /* bootdata_off */
|
||||
QUAD(__boot_data_end - __boot_data_start) /* bootdata_size */
|
||||
QUAD(__boot_data_preserved_start) /* bootdata_preserved_off */
|
||||
QUAD(__boot_data_preserved_end -
|
||||
__boot_data_preserved_start) /* bootdata_preserved_size */
|
||||
QUAD(__dynsym_start) /* dynsym_start */
|
||||
QUAD(__rela_dyn_start) /* rela_dyn_start */
|
||||
QUAD(__rela_dyn_end) /* rela_dyn_end */
|
||||
} :NONE
|
||||
|
||||
/* Debugging sections. */
|
||||
|
Reference in New Issue
Block a user