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 <holzheu@linux.vnet.ibm.com> Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
This commit is contained in:
parent
f08b841463
commit
1a36a39e22
|
@ -12,21 +12,13 @@
|
||||||
#include <asm/ctl_reg.h>
|
#include <asm/ctl_reg.h>
|
||||||
#include <asm/fpu/types.h>
|
#include <asm/fpu/types.h>
|
||||||
|
|
||||||
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(
|
asm volatile(
|
||||||
" la 1,%0\n"
|
" la 1,%0\n"
|
||||||
" .word 0xe70f,0x1000,0x003e\n" /* vstm 0,15,0(1) */
|
" .word 0xe70f,0x1000,0x003e\n" /* vstm 0,15,0(1) */
|
||||||
" .word 0xe70f,0x1100,0x0c3e\n" /* vstm 16,31,256(1) */
|
" .word 0xe70f,0x1100,0x0c3e\n" /* vstm 16,31,256(1) */
|
||||||
: "=Q" (*(struct vx_array *) vxrs) : : "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)
|
static inline void convert_vx_to_fp(freg_t *fprs, __vector128 *vxrs)
|
||||||
|
|
|
@ -87,7 +87,6 @@ struct ipl_parameter_block {
|
||||||
* IPL validity flags
|
* IPL validity flags
|
||||||
*/
|
*/
|
||||||
extern u32 ipl_flags;
|
extern u32 ipl_flags;
|
||||||
extern u32 dump_prefix_page;
|
|
||||||
|
|
||||||
struct dump_save_areas {
|
struct dump_save_areas {
|
||||||
struct save_area_ext **areas;
|
struct save_area_ext **areas;
|
||||||
|
@ -176,7 +175,7 @@ enum diag308_rc {
|
||||||
|
|
||||||
extern int diag308(unsigned long subcode, void *addr);
|
extern int diag308(unsigned long subcode, void *addr);
|
||||||
extern void diag308_reset(void);
|
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);
|
extern void lgr_info_log(void);
|
||||||
|
|
||||||
#endif /* _ASM_S390_IPL_H */
|
#endif /* _ASM_S390_IPL_H */
|
||||||
|
|
|
@ -15,6 +15,5 @@ struct reset_call {
|
||||||
|
|
||||||
extern void register_reset_call(struct reset_call *reset);
|
extern void register_reset_call(struct reset_call *reset);
|
||||||
extern void unregister_reset_call(struct reset_call *reset);
|
extern void unregister_reset_call(struct reset_call *reset);
|
||||||
extern void s390_reset_system(void (*fn_pre)(void),
|
extern void s390_reset_system(void);
|
||||||
void (*fn_post)(void *), void *data);
|
|
||||||
#endif /* _ASM_S390_RESET_H */
|
#endif /* _ASM_S390_RESET_H */
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
extern struct mutex smp_cpu_state_mutex;
|
extern struct mutex smp_cpu_state_mutex;
|
||||||
extern unsigned int smp_cpu_mt_shift;
|
extern unsigned int smp_cpu_mt_shift;
|
||||||
extern unsigned int smp_cpu_mtid;
|
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);
|
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 int smp_vcpu_scheduled(int cpu) { return 1; }
|
||||||
static inline void smp_yield_cpu(int cpu) { }
|
static inline void smp_yield_cpu(int cpu) { }
|
||||||
static inline void smp_fill_possible_mask(void) { }
|
static inline void smp_fill_possible_mask(void) { }
|
||||||
static inline void smp_save_dump_cpus(void) { }
|
|
||||||
|
|
||||||
#endif /* CONFIG_SMP */
|
#endif /* CONFIG_SMP */
|
||||||
|
|
||||||
|
|
|
@ -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)
|
static int __init disable_vector_extension(char *str)
|
||||||
{
|
{
|
||||||
S390_lowcore.machine_flags &= ~MACHINE_FLAG_VX;
|
S390_lowcore.machine_flags &= ~MACHINE_FLAG_VX;
|
||||||
|
@ -451,6 +459,7 @@ void __init startup_init(void)
|
||||||
detect_diag9c();
|
detect_diag9c();
|
||||||
detect_diag44();
|
detect_diag44();
|
||||||
detect_machine_facilities();
|
detect_machine_facilities();
|
||||||
|
save_vector_registers();
|
||||||
setup_topology();
|
setup_topology();
|
||||||
sclp_early_detect();
|
sclp_early_detect();
|
||||||
lockdep_on();
|
lockdep_on();
|
||||||
|
|
|
@ -2039,10 +2039,7 @@ static void do_reset_calls(void)
|
||||||
reset->fn();
|
reset->fn();
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 dump_prefix_page;
|
void s390_reset_system(void)
|
||||||
|
|
||||||
void s390_reset_system(void (*fn_pre)(void),
|
|
||||||
void (*fn_post)(void *), void *data)
|
|
||||||
{
|
{
|
||||||
struct _lowcore *lc;
|
struct _lowcore *lc;
|
||||||
|
|
||||||
|
@ -2051,9 +2048,6 @@ void s390_reset_system(void (*fn_pre)(void),
|
||||||
/* Stack for interrupt/machine check handler */
|
/* Stack for interrupt/machine check handler */
|
||||||
lc->panic_stack = S390_lowcore.panic_stack;
|
lc->panic_stack = S390_lowcore.panic_stack;
|
||||||
|
|
||||||
/* Save prefix page address for dump case */
|
|
||||||
dump_prefix_page = (u32)(unsigned long) lc;
|
|
||||||
|
|
||||||
/* Disable prefixing */
|
/* Disable prefixing */
|
||||||
set_prefix(0);
|
set_prefix(0);
|
||||||
|
|
||||||
|
@ -2077,14 +2071,5 @@ void s390_reset_system(void (*fn_pre)(void),
|
||||||
S390_lowcore.subchannel_id = 0;
|
S390_lowcore.subchannel_id = 0;
|
||||||
S390_lowcore.subchannel_nr = 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();
|
do_reset_calls();
|
||||||
/* Call function after reset */
|
|
||||||
if (fn_post)
|
|
||||||
fn_post(data);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,40 +34,6 @@ extern const unsigned long long relocate_kernel_len;
|
||||||
|
|
||||||
#ifdef CONFIG_CRASH_DUMP
|
#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
|
* PM notifier callback for kdump
|
||||||
*/
|
*/
|
||||||
|
@ -99,14 +65,66 @@ static int __init machine_kdump_pm_init(void)
|
||||||
arch_initcall(machine_kdump_pm_init);
|
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)
|
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);
|
__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);
|
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
|
#endif
|
||||||
|
|
||||||
|
@ -229,10 +247,14 @@ static void __do_machine_kexec(void *data)
|
||||||
relocate_kernel_t data_mover;
|
relocate_kernel_t data_mover;
|
||||||
struct kimage *image = data;
|
struct kimage *image = data;
|
||||||
|
|
||||||
|
s390_reset_system();
|
||||||
data_mover = (relocate_kernel_t) page_to_phys(image->control_code_page);
|
data_mover = (relocate_kernel_t) page_to_phys(image->control_code_page);
|
||||||
|
|
||||||
/* Call the moving routine */
|
/* Call the moving routine */
|
||||||
(*data_mover)(&image->head, image->start);
|
(*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();
|
tracing_off();
|
||||||
debug_locks_off();
|
debug_locks_off();
|
||||||
#ifdef CONFIG_CRASH_DUMP
|
#ifdef CONFIG_CRASH_DUMP
|
||||||
if (((struct kimage *) data)->type == KEXEC_TYPE_CRASH) {
|
if (((struct kimage *) data)->type == KEXEC_TYPE_CRASH)
|
||||||
|
__machine_kdump(data);
|
||||||
lgr_info_log();
|
|
||||||
s390_reset_system(setup_regs, __do_machine_kdump, data);
|
|
||||||
} else
|
|
||||||
#endif
|
#endif
|
||||||
s390_reset_system(NULL, __do_machine_kexec, data);
|
__do_machine_kexec(data);
|
||||||
disabled_wait((unsigned long) __builtin_return_address(0));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -9,12 +9,11 @@
|
||||||
#include <asm/sigp.h>
|
#include <asm/sigp.h>
|
||||||
|
|
||||||
#
|
#
|
||||||
# store_status
|
# Issue "store status" for the current CPU to its prefix page
|
||||||
|
# and call passed function afterwards
|
||||||
#
|
#
|
||||||
# Prerequisites to run this function:
|
# r2 = Function to be called after store status
|
||||||
# - Prefix register is set to zero
|
# r3 = Parameter for function
|
||||||
# - Original prefix register is stored in "dump_prefix_page"
|
|
||||||
# - Lowcore protection is off
|
|
||||||
#
|
#
|
||||||
ENTRY(store_status)
|
ENTRY(store_status)
|
||||||
/* Save register one and load save area base */
|
/* Save register one and load save area base */
|
||||||
|
@ -53,23 +52,23 @@ ENTRY(store_status)
|
||||||
/* CPU timer */
|
/* CPU timer */
|
||||||
lghi %r1,__LC_CPU_TIMER_SAVE_AREA
|
lghi %r1,__LC_CPU_TIMER_SAVE_AREA
|
||||||
stpt 0(%r1)
|
stpt 0(%r1)
|
||||||
/* Saved prefix register */
|
/* Store prefix register */
|
||||||
lghi %r1,__LC_PREFIX_SAVE_AREA
|
lghi %r1,__LC_PREFIX_SAVE_AREA
|
||||||
larl %r2,dump_prefix_page
|
stpx 0(%r1)
|
||||||
mvc 0(4,%r1),0(%r2)
|
|
||||||
/* Clock comparator - seven bytes */
|
/* Clock comparator - seven bytes */
|
||||||
lghi %r1,__LC_CLOCK_COMP_SAVE_AREA
|
lghi %r1,__LC_CLOCK_COMP_SAVE_AREA
|
||||||
larl %r2,.Lclkcmp
|
larl %r4,.Lclkcmp
|
||||||
stckc 0(%r2)
|
stckc 0(%r4)
|
||||||
mvc 1(7,%r1),1(%r2)
|
mvc 1(7,%r1),1(%r4)
|
||||||
/* Program status word */
|
/* Program status word */
|
||||||
lghi %r1,__LC_PSW_SAVE_AREA
|
lghi %r1,__LC_PSW_SAVE_AREA
|
||||||
epsw %r2,%r3
|
epsw %r4,%r5
|
||||||
st %r2,0(%r1)
|
st %r4,0(%r1)
|
||||||
st %r3,4(%r1)
|
st %r5,4(%r1)
|
||||||
larl %r2,store_status
|
|
||||||
stg %r2,8(%r1)
|
stg %r2,8(%r1)
|
||||||
br %r14
|
lgr %r1,%r2
|
||||||
|
lgr %r2,%r3
|
||||||
|
br %r1
|
||||||
|
|
||||||
.section .bss
|
.section .bss
|
||||||
.align 8
|
.align 8
|
||||||
|
@ -84,9 +83,11 @@ ENTRY(store_status)
|
||||||
ENTRY(do_reipl_asm)
|
ENTRY(do_reipl_asm)
|
||||||
basr %r13,0
|
basr %r13,0
|
||||||
.Lpg0: lpswe .Lnewpsw-.Lpg0(%r13)
|
.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
|
lgr %r1,%r2
|
||||||
mvc __LC_PGM_NEW_PSW(16),.Lpcnew-.Lpg0(%r13)
|
mvc __LC_PGM_NEW_PSW(16),.Lpcnew-.Lpg0(%r13)
|
||||||
stsch .Lschib-.Lpg0(%r13)
|
stsch .Lschib-.Lpg0(%r13)
|
||||||
|
|
|
@ -865,11 +865,13 @@ void __init setup_arch(char **cmdline_p)
|
||||||
|
|
||||||
check_initrd();
|
check_initrd();
|
||||||
reserve_crashkernel();
|
reserve_crashkernel();
|
||||||
|
#ifdef CONFIG_CRASH_DUMP
|
||||||
/*
|
/*
|
||||||
* Be aware that smp_save_dump_cpus() triggers a system reset.
|
* Be aware that smp_save_dump_cpus() triggers a system reset.
|
||||||
* Therefore CPU and device initialization should be done afterwards.
|
* Therefore CPU and device initialization should be done afterwards.
|
||||||
*/
|
*/
|
||||||
smp_save_dump_cpus();
|
smp_save_dump_cpus();
|
||||||
|
#endif
|
||||||
|
|
||||||
setup_resources();
|
setup_resources();
|
||||||
setup_vmcoreinfo();
|
setup_vmcoreinfo();
|
||||||
|
|
|
@ -80,6 +80,10 @@ EXPORT_SYMBOL(smp_cpu_mt_shift);
|
||||||
unsigned int smp_cpu_mtid;
|
unsigned int smp_cpu_mtid;
|
||||||
EXPORT_SYMBOL(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 unsigned int smp_max_threads __initdata = -1U;
|
||||||
|
|
||||||
static int __init early_nosmt(char *s)
|
static int __init early_nosmt(char *s)
|
||||||
|
@ -105,8 +109,7 @@ DEFINE_MUTEX(smp_cpu_state_mutex);
|
||||||
/*
|
/*
|
||||||
* Signal processor helper functions.
|
* Signal processor helper functions.
|
||||||
*/
|
*/
|
||||||
static inline int __pcpu_sigp_relax(u16 addr, u8 order, unsigned long parm,
|
static inline int __pcpu_sigp_relax(u16 addr, u8 order, unsigned long parm)
|
||||||
u32 *status)
|
|
||||||
{
|
{
|
||||||
int cc;
|
int cc;
|
||||||
|
|
||||||
|
@ -538,53 +541,24 @@ EXPORT_SYMBOL(smp_ctl_clear_bit);
|
||||||
|
|
||||||
#ifdef CONFIG_CRASH_DUMP
|
#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)
|
int smp_store_status(int cpu)
|
||||||
{
|
{
|
||||||
unsigned long vx_sa;
|
struct pcpu *pcpu = pcpu_devices + cpu;
|
||||||
struct pcpu *pcpu;
|
unsigned long pa;
|
||||||
|
|
||||||
pcpu = pcpu_devices + cpu;
|
pa = __pa(&pcpu->lowcore->floating_pt_save_area);
|
||||||
if (__pcpu_sigp_relax(pcpu->address, SIGP_STOP_AND_STORE_STATUS,
|
if (__pcpu_sigp_relax(pcpu->address, SIGP_STORE_STATUS_AT_ADDRESS,
|
||||||
0, NULL) != SIGP_CC_ORDER_CODE_ACCEPTED)
|
pa) != SIGP_CC_ORDER_CODE_ACCEPTED)
|
||||||
return -EIO;
|
return -EIO;
|
||||||
if (!MACHINE_HAS_VX)
|
if (!MACHINE_HAS_VX)
|
||||||
return 0;
|
return 0;
|
||||||
vx_sa = __pa(pcpu->lowcore->vector_save_area_addr);
|
pa = __pa(pcpu->lowcore->vector_save_area_addr);
|
||||||
__pcpu_sigp_relax(pcpu->address, SIGP_STORE_ADDITIONAL_STATUS,
|
if (__pcpu_sigp_relax(pcpu->address, SIGP_STORE_ADDITIONAL_STATUS,
|
||||||
vx_sa, NULL);
|
pa) != SIGP_CC_ORDER_CODE_ACCEPTED)
|
||||||
|
return -EIO;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* CONFIG_CRASH_DUMP */
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Collect CPU state of the previous, crashed system.
|
* Collect CPU state of the previous, crashed system.
|
||||||
* There are four cases:
|
* 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
|
* 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
|
* 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
|
* 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)
|
* 2) stand-alone kdump for SCSI (zfcp dump with swapped memory)
|
||||||
* condition: OLDMEM_BASE != NULL && ipl_info.type == IPL_TYPE_FCP_DUMP
|
* condition: OLDMEM_BASE != NULL && ipl_info.type == IPL_TYPE_FCP_DUMP
|
||||||
* The state for all CPUs except the boot CPU needs to be collected
|
* 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
|
* This case does not exist for s390 anymore, setup_arch explicitly
|
||||||
* deactivates the elfcorehdr= kernel parameter
|
* 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)
|
void __init smp_save_dump_cpus(void)
|
||||||
{
|
{
|
||||||
#ifdef CONFIG_CRASH_DUMP
|
|
||||||
int addr, cpu, boot_cpu_addr, max_cpu_addr;
|
int addr, cpu, boot_cpu_addr, max_cpu_addr;
|
||||||
struct save_area_ext *sa_ext;
|
struct save_area_ext *sa_ext;
|
||||||
|
unsigned long page;
|
||||||
bool is_boot_cpu;
|
bool is_boot_cpu;
|
||||||
|
|
||||||
if (!(OLDMEM_BASE || ipl_info.type == IPL_TYPE_FCP_DUMP))
|
if (!(OLDMEM_BASE || ipl_info.type == IPL_TYPE_FCP_DUMP))
|
||||||
/* No previous system present, normal boot. */
|
/* No previous system present, normal boot. */
|
||||||
return;
|
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. */
|
/* Set multi-threading state to the previous system. */
|
||||||
pcpu_set_smt(sclp.mtid_prev);
|
pcpu_set_smt(sclp.mtid_prev);
|
||||||
max_cpu_addr = SCLP_MAX_CORES << sclp.mtid_prev;
|
max_cpu_addr = SCLP_MAX_CORES << sclp.mtid_prev;
|
||||||
for (cpu = 0, addr = 0; addr <= max_cpu_addr; addr++) {
|
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)
|
SIGP_CC_NOT_OPERATIONAL)
|
||||||
continue;
|
continue;
|
||||||
cpu += 1;
|
cpu += 1;
|
||||||
|
@ -634,7 +636,7 @@ void __init smp_save_dump_cpus(void)
|
||||||
dump_save_areas.count = cpu;
|
dump_save_areas.count = cpu;
|
||||||
boot_cpu_addr = stap();
|
boot_cpu_addr = stap();
|
||||||
for (cpu = 0, addr = 0; addr <= max_cpu_addr; addr++) {
|
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)
|
SIGP_CC_NOT_OPERATIONAL)
|
||||||
continue;
|
continue;
|
||||||
sa_ext = (void *) memblock_alloc(sizeof(*sa_ext), 8);
|
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");
|
panic("could not allocate memory for save area\n");
|
||||||
is_boot_cpu = (addr == boot_cpu_addr);
|
is_boot_cpu = (addr == boot_cpu_addr);
|
||||||
cpu += 1;
|
cpu += 1;
|
||||||
if (is_boot_cpu && !OLDMEM_BASE)
|
if (MACHINE_HAS_VX)
|
||||||
/* Skip boot CPU for standard zfcp dump. */
|
/* Get the vector registers */
|
||||||
continue;
|
smp_save_cpu_vxrs(sa_ext, addr, is_boot_cpu, page);
|
||||||
/* Get state for this CPU. */
|
/*
|
||||||
__smp_store_cpu_state(sa_ext, addr, is_boot_cpu);
|
* 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();
|
diag308_reset();
|
||||||
pcpu_set_smt(0);
|
pcpu_set_smt(0);
|
||||||
#endif /* CONFIG_CRASH_DUMP */
|
|
||||||
}
|
}
|
||||||
|
#endif /* CONFIG_CRASH_DUMP */
|
||||||
|
|
||||||
void smp_cpu_set_polarization(int cpu, int val)
|
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;
|
for (address = 0;
|
||||||
address < (SCLP_MAX_CORES << smp_cpu_mt_shift);
|
address < (SCLP_MAX_CORES << smp_cpu_mt_shift);
|
||||||
address += (1U << 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)
|
SIGP_CC_NOT_OPERATIONAL)
|
||||||
continue;
|
continue;
|
||||||
info->core[info->configured].core_id =
|
info->core[info->configured].core_id =
|
||||||
|
|
|
@ -129,8 +129,6 @@ static int __init init_cpu_info(void)
|
||||||
TRACE("could not copy from HSA\n");
|
TRACE("could not copy from HSA\n");
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
if (MACHINE_HAS_VX)
|
|
||||||
save_vx_regs_safe(sa_ext->vx_regs);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -917,7 +917,7 @@ void reipl_ccw_dev(struct ccw_dev_id *devid)
|
||||||
{
|
{
|
||||||
struct subchannel_id uninitialized_var(schid);
|
struct subchannel_id uninitialized_var(schid);
|
||||||
|
|
||||||
s390_reset_system(NULL, NULL, NULL);
|
s390_reset_system();
|
||||||
if (reipl_find_schid(devid, &schid) != 0)
|
if (reipl_find_schid(devid, &schid) != 0)
|
||||||
panic("IPL Device not found\n");
|
panic("IPL Device not found\n");
|
||||||
do_reipl_asm(*((__u32*)&schid));
|
do_reipl_asm(*((__u32*)&schid));
|
||||||
|
|
Loading…
Reference in New Issue