From: Martin Schwidefsky Date: Thu, 29 Oct 2015 09:28:26 +0000 (+0100) Subject: s390/dump: rework CPU register dump code X-Git-Url: https://git.stricted.de/?a=commitdiff_plain;h=1a36a39e225d3558fb3776a3d3d7736cf1ec9f60;p=GitHub%2Fmoto-9609%2Fandroid_kernel_motorola_exynos9610.git s390/dump: rework CPU register dump code To collect the CPU registers of the crashed system allocated a single page with memblock_alloc_base and use it as a copy buffer. Replace the stop-and-store-status sigp with a store-status-at-address sigp in smp_save_dump_cpus() and smp_store_status(). In both cases the target CPU is already stopped and store-status-at-address avoids the detour via the absolute zero page. For kexec simplify s390_reset_system and call store_status() before the prefix register of the boot CPU has been set to zero. Use STPX to store the prefix register and remove dump_prefix_page. Acked-by: Michael Holzheu Signed-off-by: Martin Schwidefsky --- diff --git a/arch/s390/include/asm/fpu/internal.h b/arch/s390/include/asm/fpu/internal.h index 2559b16da525..ea91ddfe54eb 100644 --- a/arch/s390/include/asm/fpu/internal.h +++ b/arch/s390/include/asm/fpu/internal.h @@ -12,21 +12,13 @@ #include #include -static inline void save_vx_regs_safe(__vector128 *vxrs) +static inline void save_vx_regs(__vector128 *vxrs) { - unsigned long cr0, flags; - - flags = arch_local_irq_save(); - __ctl_store(cr0, 0, 0); - __ctl_set_bit(0, 17); - __ctl_set_bit(0, 18); asm volatile( " la 1,%0\n" " .word 0xe70f,0x1000,0x003e\n" /* vstm 0,15,0(1) */ " .word 0xe70f,0x1100,0x0c3e\n" /* vstm 16,31,256(1) */ : "=Q" (*(struct vx_array *) vxrs) : : "1"); - __ctl_load(cr0, 0, 0); - arch_local_irq_restore(flags); } static inline void convert_vx_to_fp(freg_t *fprs, __vector128 *vxrs) diff --git a/arch/s390/include/asm/ipl.h b/arch/s390/include/asm/ipl.h index 86634e71b69f..1dc55db8cd81 100644 --- a/arch/s390/include/asm/ipl.h +++ b/arch/s390/include/asm/ipl.h @@ -87,7 +87,6 @@ struct ipl_parameter_block { * IPL validity flags */ extern u32 ipl_flags; -extern u32 dump_prefix_page; struct dump_save_areas { struct save_area_ext **areas; @@ -176,7 +175,7 @@ enum diag308_rc { extern int diag308(unsigned long subcode, void *addr); extern void diag308_reset(void); -extern void store_status(void); +extern void store_status(void (*fn)(void *), void *data); extern void lgr_info_log(void); #endif /* _ASM_S390_IPL_H */ diff --git a/arch/s390/include/asm/reset.h b/arch/s390/include/asm/reset.h index 72786067b300..fe11fa88a0e0 100644 --- a/arch/s390/include/asm/reset.h +++ b/arch/s390/include/asm/reset.h @@ -15,6 +15,5 @@ struct reset_call { extern void register_reset_call(struct reset_call *reset); extern void unregister_reset_call(struct reset_call *reset); -extern void s390_reset_system(void (*fn_pre)(void), - void (*fn_post)(void *), void *data); +extern void s390_reset_system(void); #endif /* _ASM_S390_RESET_H */ diff --git a/arch/s390/include/asm/smp.h b/arch/s390/include/asm/smp.h index 5df26b11cf47..0cc383b9be7f 100644 --- a/arch/s390/include/asm/smp.h +++ b/arch/s390/include/asm/smp.h @@ -18,6 +18,7 @@ extern struct mutex smp_cpu_state_mutex; extern unsigned int smp_cpu_mt_shift; extern unsigned int smp_cpu_mtid; +extern __vector128 __initdata boot_cpu_vector_save_area[__NUM_VXRS]; extern int __cpu_up(unsigned int cpu, struct task_struct *tidle); @@ -55,7 +56,6 @@ static inline int smp_store_status(int cpu) { return 0; } static inline int smp_vcpu_scheduled(int cpu) { return 1; } static inline void smp_yield_cpu(int cpu) { } static inline void smp_fill_possible_mask(void) { } -static inline void smp_save_dump_cpus(void) { } #endif /* CONFIG_SMP */ diff --git a/arch/s390/kernel/early.c b/arch/s390/kernel/early.c index 3c31609df959..20a5caf6d981 100644 --- a/arch/s390/kernel/early.c +++ b/arch/s390/kernel/early.c @@ -335,6 +335,14 @@ static __init void detect_machine_facilities(void) } } +static inline void save_vector_registers(void) +{ +#ifdef CONFIG_CRASH_DUMP + if (test_facility(129)) + save_vx_regs(boot_cpu_vector_save_area); +#endif +} + static int __init disable_vector_extension(char *str) { S390_lowcore.machine_flags &= ~MACHINE_FLAG_VX; @@ -451,6 +459,7 @@ void __init startup_init(void) detect_diag9c(); detect_diag44(); detect_machine_facilities(); + save_vector_registers(); setup_topology(); sclp_early_detect(); lockdep_on(); diff --git a/arch/s390/kernel/ipl.c b/arch/s390/kernel/ipl.c index b1f0a90f933b..26d58cf72573 100644 --- a/arch/s390/kernel/ipl.c +++ b/arch/s390/kernel/ipl.c @@ -2039,10 +2039,7 @@ static void do_reset_calls(void) reset->fn(); } -u32 dump_prefix_page; - -void s390_reset_system(void (*fn_pre)(void), - void (*fn_post)(void *), void *data) +void s390_reset_system(void) { struct _lowcore *lc; @@ -2051,9 +2048,6 @@ void s390_reset_system(void (*fn_pre)(void), /* Stack for interrupt/machine check handler */ lc->panic_stack = S390_lowcore.panic_stack; - /* Save prefix page address for dump case */ - dump_prefix_page = (u32)(unsigned long) lc; - /* Disable prefixing */ set_prefix(0); @@ -2077,14 +2071,5 @@ void s390_reset_system(void (*fn_pre)(void), S390_lowcore.subchannel_id = 0; S390_lowcore.subchannel_nr = 0; - /* Store status at absolute zero */ - store_status(); - - /* Call function before reset */ - if (fn_pre) - fn_pre(); do_reset_calls(); - /* Call function after reset */ - if (fn_post) - fn_post(data); } diff --git a/arch/s390/kernel/machine_kexec.c b/arch/s390/kernel/machine_kexec.c index bf2cd699556f..2f1b7217c25c 100644 --- a/arch/s390/kernel/machine_kexec.c +++ b/arch/s390/kernel/machine_kexec.c @@ -34,40 +34,6 @@ extern const unsigned long long relocate_kernel_len; #ifdef CONFIG_CRASH_DUMP -/* - * Initialize CPU ELF notes - */ -static void setup_regs(void) -{ - struct save_area *sa, *sa_0; - unsigned long prefix; - int cpu, this_cpu; - - /* setup_regs is called with the prefix register = 0 */ - sa_0 = (struct save_area *) __LC_FPREGS_SAVE_AREA; - - /* Get status of this CPU out of absolute zero */ - prefix = (unsigned long) S390_lowcore.prefixreg_save_area; - sa = (struct save_area *)(prefix + __LC_FPREGS_SAVE_AREA); - memcpy(sa, sa_0, sizeof(struct save_area)); - if (MACHINE_HAS_VX) { - struct _lowcore *lc = (struct _lowcore *) prefix; - save_vx_regs_safe((void *) lc->vector_save_area_addr); - } - - /* Get status of the other CPUs */ - this_cpu = smp_find_processor_id(stap()); - for_each_online_cpu(cpu) { - if (cpu == this_cpu) - continue; - if (smp_store_status(cpu)) - continue; - prefix = (unsigned long) S390_lowcore.prefixreg_save_area; - sa = (struct save_area *)(prefix + __LC_FPREGS_SAVE_AREA); - memcpy(sa, sa_0, sizeof(struct save_area)); - } -} - /* * PM notifier callback for kdump */ @@ -99,14 +65,66 @@ static int __init machine_kdump_pm_init(void) arch_initcall(machine_kdump_pm_init); /* - * Start kdump: We expect here that a store status has been done on our CPU + * Reset the system, copy boot CPU registers to absolute zero, + * and jump to the kdump image */ static void __do_machine_kdump(void *image) { - int (*start_kdump)(int) = (void *)((struct kimage *) image)->start; + int (*start_kdump)(int); + unsigned long prefix; + + /* store_status() saved the prefix register to lowcore */ + prefix = (unsigned long) S390_lowcore.prefixreg_save_area; + + /* Now do the reset */ + s390_reset_system(); + + /* + * Copy dump CPU store status info to absolute zero. + * This need to be done *after* s390_reset_system set the + * prefix register of this CPU to zero + */ + memcpy((void *) __LC_FPREGS_SAVE_AREA, + (void *)(prefix + __LC_FPREGS_SAVE_AREA), 512); __load_psw_mask(PSW_MASK_BASE | PSW_DEFAULT_KEY | PSW_MASK_EA | PSW_MASK_BA); + start_kdump = (void *)((struct kimage *) image)->start; start_kdump(1); + + /* Die if start_kdump returns */ + disabled_wait((unsigned long) __builtin_return_address(0)); +} + +/* + * Start kdump: create a LGR log entry, store status of all CPUs and + * branch to __do_machine_kdump. + */ +static noinline void __machine_kdump(void *image) +{ + int this_cpu, cpu; + + lgr_info_log(); + /* Get status of the other CPUs */ + this_cpu = smp_find_processor_id(stap()); + for_each_online_cpu(cpu) { + if (cpu == this_cpu) + continue; + if (smp_store_status(cpu)) + continue; + } + /* Store status of the boot CPU */ + if (MACHINE_HAS_VX) + save_vx_regs((void *) &S390_lowcore.vector_save_area); + /* + * To create a good backchain for this CPU in the dump store_status + * is passed the address of a function. The address is saved into + * the PSW save area of the boot CPU and the function is invoked as + * a tail call of store_status. The backchain in the dump will look + * like this: + * restart_int_handler -> __machine_kexec -> __do_machine_kdump + * The call to store_status() will not return. + */ + store_status(__do_machine_kdump, image); } #endif @@ -229,10 +247,14 @@ static void __do_machine_kexec(void *data) relocate_kernel_t data_mover; struct kimage *image = data; + s390_reset_system(); data_mover = (relocate_kernel_t) page_to_phys(image->control_code_page); /* Call the moving routine */ (*data_mover)(&image->head, image->start); + + /* Die if kexec returns */ + disabled_wait((unsigned long) __builtin_return_address(0)); } /* @@ -245,14 +267,10 @@ static void __machine_kexec(void *data) tracing_off(); debug_locks_off(); #ifdef CONFIG_CRASH_DUMP - if (((struct kimage *) data)->type == KEXEC_TYPE_CRASH) { - - lgr_info_log(); - s390_reset_system(setup_regs, __do_machine_kdump, data); - } else + if (((struct kimage *) data)->type == KEXEC_TYPE_CRASH) + __machine_kdump(data); #endif - s390_reset_system(NULL, __do_machine_kexec, data); - disabled_wait((unsigned long) __builtin_return_address(0)); + __do_machine_kexec(data); } /* diff --git a/arch/s390/kernel/reipl.S b/arch/s390/kernel/reipl.S index b75a521e4fab..89ea8c213d82 100644 --- a/arch/s390/kernel/reipl.S +++ b/arch/s390/kernel/reipl.S @@ -9,12 +9,11 @@ #include # -# store_status +# Issue "store status" for the current CPU to its prefix page +# and call passed function afterwards # -# Prerequisites to run this function: -# - Prefix register is set to zero -# - Original prefix register is stored in "dump_prefix_page" -# - Lowcore protection is off +# r2 = Function to be called after store status +# r3 = Parameter for function # ENTRY(store_status) /* Save register one and load save area base */ @@ -53,23 +52,23 @@ ENTRY(store_status) /* CPU timer */ lghi %r1,__LC_CPU_TIMER_SAVE_AREA stpt 0(%r1) - /* Saved prefix register */ + /* Store prefix register */ lghi %r1,__LC_PREFIX_SAVE_AREA - larl %r2,dump_prefix_page - mvc 0(4,%r1),0(%r2) + stpx 0(%r1) /* Clock comparator - seven bytes */ lghi %r1,__LC_CLOCK_COMP_SAVE_AREA - larl %r2,.Lclkcmp - stckc 0(%r2) - mvc 1(7,%r1),1(%r2) + larl %r4,.Lclkcmp + stckc 0(%r4) + mvc 1(7,%r1),1(%r4) /* Program status word */ lghi %r1,__LC_PSW_SAVE_AREA - epsw %r2,%r3 - st %r2,0(%r1) - st %r3,4(%r1) - larl %r2,store_status + epsw %r4,%r5 + st %r4,0(%r1) + st %r5,4(%r1) stg %r2,8(%r1) - br %r14 + lgr %r1,%r2 + lgr %r2,%r3 + br %r1 .section .bss .align 8 @@ -84,9 +83,11 @@ ENTRY(store_status) ENTRY(do_reipl_asm) basr %r13,0 .Lpg0: lpswe .Lnewpsw-.Lpg0(%r13) -.Lpg1: brasl %r14,store_status +.Lpg1: lgr %r3,%r2 + larl %r2,.Lstatus + brasl %r14,store_status - lctlg %c6,%c6,.Lall-.Lpg0(%r13) +.Lstatus: lctlg %c6,%c6,.Lall-.Lpg0(%r13) lgr %r1,%r2 mvc __LC_PGM_NEW_PSW(16),.Lpcnew-.Lpg0(%r13) stsch .Lschib-.Lpg0(%r13) diff --git a/arch/s390/kernel/setup.c b/arch/s390/kernel/setup.c index 8f5107d6ebb3..22756bb0819e 100644 --- a/arch/s390/kernel/setup.c +++ b/arch/s390/kernel/setup.c @@ -865,11 +865,13 @@ void __init setup_arch(char **cmdline_p) check_initrd(); reserve_crashkernel(); +#ifdef CONFIG_CRASH_DUMP /* * Be aware that smp_save_dump_cpus() triggers a system reset. * Therefore CPU and device initialization should be done afterwards. */ smp_save_dump_cpus(); +#endif setup_resources(); setup_vmcoreinfo(); diff --git a/arch/s390/kernel/smp.c b/arch/s390/kernel/smp.c index d49a8cb404c2..2a69077d482c 100644 --- a/arch/s390/kernel/smp.c +++ b/arch/s390/kernel/smp.c @@ -80,6 +80,10 @@ EXPORT_SYMBOL(smp_cpu_mt_shift); unsigned int smp_cpu_mtid; EXPORT_SYMBOL(smp_cpu_mtid); +#ifdef CONFIG_CRASH_DUMP +__vector128 __initdata boot_cpu_vector_save_area[__NUM_VXRS]; +#endif + static unsigned int smp_max_threads __initdata = -1U; static int __init early_nosmt(char *s) @@ -105,8 +109,7 @@ DEFINE_MUTEX(smp_cpu_state_mutex); /* * Signal processor helper functions. */ -static inline int __pcpu_sigp_relax(u16 addr, u8 order, unsigned long parm, - u32 *status) +static inline int __pcpu_sigp_relax(u16 addr, u8 order, unsigned long parm) { int cc; @@ -538,53 +541,24 @@ EXPORT_SYMBOL(smp_ctl_clear_bit); #ifdef CONFIG_CRASH_DUMP -static void __init __smp_store_cpu_state(struct save_area_ext *sa_ext, - u16 address, int is_boot_cpu) -{ - void *lc = (void *)(unsigned long) store_prefix(); - unsigned long vx_sa; - - if (is_boot_cpu) { - /* Copy the registers of the boot CPU. */ - copy_oldmem_kernel(&sa_ext->sa, (void *) __LC_FPREGS_SAVE_AREA, - sizeof(sa_ext->sa)); - if (MACHINE_HAS_VX) - save_vx_regs_safe(sa_ext->vx_regs); - return; - } - /* Get the registers of a non-boot cpu. */ - __pcpu_sigp_relax(address, SIGP_STOP_AND_STORE_STATUS, 0, NULL); - memcpy_real(&sa_ext->sa, lc + __LC_FPREGS_SAVE_AREA, sizeof(sa_ext->sa)); - if (!MACHINE_HAS_VX) - return; - /* Get the VX registers */ - vx_sa = memblock_alloc(PAGE_SIZE, PAGE_SIZE); - if (!vx_sa) - panic("could not allocate memory for VX save area\n"); - __pcpu_sigp_relax(address, SIGP_STORE_ADDITIONAL_STATUS, vx_sa, NULL); - memcpy(sa_ext->vx_regs, (void *) vx_sa, sizeof(sa_ext->vx_regs)); - memblock_free(vx_sa, PAGE_SIZE); -} - int smp_store_status(int cpu) { - unsigned long vx_sa; - struct pcpu *pcpu; + struct pcpu *pcpu = pcpu_devices + cpu; + unsigned long pa; - pcpu = pcpu_devices + cpu; - if (__pcpu_sigp_relax(pcpu->address, SIGP_STOP_AND_STORE_STATUS, - 0, NULL) != SIGP_CC_ORDER_CODE_ACCEPTED) + pa = __pa(&pcpu->lowcore->floating_pt_save_area); + if (__pcpu_sigp_relax(pcpu->address, SIGP_STORE_STATUS_AT_ADDRESS, + pa) != SIGP_CC_ORDER_CODE_ACCEPTED) return -EIO; if (!MACHINE_HAS_VX) return 0; - vx_sa = __pa(pcpu->lowcore->vector_save_area_addr); - __pcpu_sigp_relax(pcpu->address, SIGP_STORE_ADDITIONAL_STATUS, - vx_sa, NULL); + pa = __pa(pcpu->lowcore->vector_save_area_addr); + if (__pcpu_sigp_relax(pcpu->address, SIGP_STORE_ADDITIONAL_STATUS, + pa) != SIGP_CC_ORDER_CODE_ACCEPTED) + return -EIO; return 0; } -#endif /* CONFIG_CRASH_DUMP */ - /* * Collect CPU state of the previous, crashed system. * There are four cases: @@ -593,7 +567,7 @@ int smp_store_status(int cpu) * The state for all CPUs except the boot CPU needs to be collected * with sigp stop-and-store-status. The boot CPU state is located in * the absolute lowcore of the memory stored in the HSA. The zcore code - * will allocate the save area and copy the boot CPU state from the HSA. + * will copy the boot CPU state from the HSA. * 2) stand-alone kdump for SCSI (zfcp dump with swapped memory) * condition: OLDMEM_BASE != NULL && ipl_info.type == IPL_TYPE_FCP_DUMP * The state for all CPUs except the boot CPU needs to be collected @@ -611,21 +585,49 @@ int smp_store_status(int cpu) * This case does not exist for s390 anymore, setup_arch explicitly * deactivates the elfcorehdr= kernel parameter */ +static __init void smp_save_cpu_vxrs(struct save_area_ext *sa_ext, u16 addr, + bool is_boot_cpu, unsigned long page) +{ + __vector128 *vxrs = (__vector128 *) page; + + if (is_boot_cpu) + vxrs = boot_cpu_vector_save_area; + else + __pcpu_sigp_relax(addr, SIGP_STORE_ADDITIONAL_STATUS, page); + memcpy(&sa_ext->vx_regs, vxrs, sizeof(sa_ext->vx_regs)); +} + +static __init void smp_save_cpu_regs(struct save_area_ext *sa_ext, u16 addr, + bool is_boot_cpu, unsigned long page) +{ + void *regs = (void *) page; + + if (is_boot_cpu) + copy_oldmem_kernel(regs, (void *) __LC_FPREGS_SAVE_AREA, 512); + else + __pcpu_sigp_relax(addr, SIGP_STORE_STATUS_AT_ADDRESS, page); + memcpy(&sa_ext->sa, regs, sizeof(sa_ext->sa)); +} + void __init smp_save_dump_cpus(void) { -#ifdef CONFIG_CRASH_DUMP int addr, cpu, boot_cpu_addr, max_cpu_addr; struct save_area_ext *sa_ext; + unsigned long page; bool is_boot_cpu; if (!(OLDMEM_BASE || ipl_info.type == IPL_TYPE_FCP_DUMP)) /* No previous system present, normal boot. */ return; + /* Allocate a page as dumping area for the store status sigps */ + page = memblock_alloc_base(PAGE_SIZE, PAGE_SIZE, 1UL << 31); + if (!page) + panic("could not allocate memory for save area\n"); /* Set multi-threading state to the previous system. */ pcpu_set_smt(sclp.mtid_prev); max_cpu_addr = SCLP_MAX_CORES << sclp.mtid_prev; for (cpu = 0, addr = 0; addr <= max_cpu_addr; addr++) { - if (__pcpu_sigp_relax(addr, SIGP_SENSE, 0, NULL) == + if (__pcpu_sigp_relax(addr, SIGP_SENSE, 0) == SIGP_CC_NOT_OPERATIONAL) continue; cpu += 1; @@ -634,7 +636,7 @@ void __init smp_save_dump_cpus(void) dump_save_areas.count = cpu; boot_cpu_addr = stap(); for (cpu = 0, addr = 0; addr <= max_cpu_addr; addr++) { - if (__pcpu_sigp_relax(addr, SIGP_SENSE, 0, NULL) == + if (__pcpu_sigp_relax(addr, SIGP_SENSE, 0) == SIGP_CC_NOT_OPERATIONAL) continue; sa_ext = (void *) memblock_alloc(sizeof(*sa_ext), 8); @@ -643,16 +645,24 @@ void __init smp_save_dump_cpus(void) panic("could not allocate memory for save area\n"); is_boot_cpu = (addr == boot_cpu_addr); cpu += 1; - if (is_boot_cpu && !OLDMEM_BASE) - /* Skip boot CPU for standard zfcp dump. */ - continue; - /* Get state for this CPU. */ - __smp_store_cpu_state(sa_ext, addr, is_boot_cpu); + if (MACHINE_HAS_VX) + /* Get the vector registers */ + smp_save_cpu_vxrs(sa_ext, addr, is_boot_cpu, page); + /* + * For a zfcp dump OLDMEM_BASE == NULL and the registers + * of the boot CPU are stored in the HSA. To retrieve + * these registers an SCLP request is required which is + * done by drivers/s390/char/zcore.c:init_cpu_info() + */ + if (!is_boot_cpu || OLDMEM_BASE) + /* Get the CPU registers */ + smp_save_cpu_regs(sa_ext, addr, is_boot_cpu, page); } + memblock_free(page, PAGE_SIZE); diag308_reset(); pcpu_set_smt(0); -#endif /* CONFIG_CRASH_DUMP */ } +#endif /* CONFIG_CRASH_DUMP */ void smp_cpu_set_polarization(int cpu, int val) { @@ -676,7 +686,7 @@ static struct sclp_core_info *smp_get_core_info(void) for (address = 0; address < (SCLP_MAX_CORES << smp_cpu_mt_shift); address += (1U << smp_cpu_mt_shift)) { - if (__pcpu_sigp_relax(address, SIGP_SENSE, 0, NULL) == + if (__pcpu_sigp_relax(address, SIGP_SENSE, 0) == SIGP_CC_NOT_OPERATIONAL) continue; info->core[info->configured].core_id = diff --git a/drivers/s390/char/zcore.c b/drivers/s390/char/zcore.c index 087da775c359..bed191a39c5b 100644 --- a/drivers/s390/char/zcore.c +++ b/drivers/s390/char/zcore.c @@ -129,8 +129,6 @@ static int __init init_cpu_info(void) TRACE("could not copy from HSA\n"); return -EIO; } - if (MACHINE_HAS_VX) - save_vx_regs_safe(sa_ext->vx_regs); return 0; } diff --git a/drivers/s390/cio/cio.c b/drivers/s390/cio/cio.c index 690b8547e828..e0d02952a7f4 100644 --- a/drivers/s390/cio/cio.c +++ b/drivers/s390/cio/cio.c @@ -917,7 +917,7 @@ void reipl_ccw_dev(struct ccw_dev_id *devid) { struct subchannel_id uninitialized_var(schid); - s390_reset_system(NULL, NULL, NULL); + s390_reset_system(); if (reipl_find_schid(devid, &schid) != 0) panic("IPL Device not found\n"); do_reipl_asm(*((__u32*)&schid));