Commit 7dd6b334 authored by Michael Holzheu's avatar Michael Holzheu Committed by Heiko Carstens

[S390] Add PSW restart shutdown trigger

With this patch a new S390 shutdown trigger "restart" is added. If under
z/VM "systerm restart" is entered or under the HMC the "PSW restart" button
is pressed, the PSW located at 0 (31 bit) or 0x1a0 (64 bit) bit is loaded.
Now we execute do_restart() that processes the restart action that is
defined under /sys/firmware/shutdown_actions/on_restart. Currently the
following actions are possible: reipl (default), stop, vmcmd, dump, and
dump_reipl.
Signed-off-by: default avatarMichael Holzheu <holzheu@linux.vnet.ibm.com>
Signed-off-by: default avatarHeiko Carstens <heiko.carstens@de.ibm.com>
parent 944291de
...@@ -18,6 +18,7 @@ void system_call(void); ...@@ -18,6 +18,7 @@ void system_call(void);
void pgm_check_handler(void); void pgm_check_handler(void);
void mcck_int_handler(void); void mcck_int_handler(void);
void io_int_handler(void); void io_int_handler(void);
void psw_restart_int_handler(void);
#ifdef CONFIG_32BIT #ifdef CONFIG_32BIT
...@@ -150,7 +151,10 @@ struct _lowcore { ...@@ -150,7 +151,10 @@ struct _lowcore {
*/ */
__u32 ipib; /* 0x0e00 */ __u32 ipib; /* 0x0e00 */
__u32 ipib_checksum; /* 0x0e04 */ __u32 ipib_checksum; /* 0x0e04 */
__u8 pad_0x0e08[0x0f00-0x0e08]; /* 0x0e08 */
/* 64 bit save area */
__u64 save_area_64; /* 0x0e08 */
__u8 pad_0x0e10[0x0f00-0x0e10]; /* 0x0e10 */
/* Extended facility list */ /* Extended facility list */
__u64 stfle_fac_list[32]; /* 0x0f00 */ __u64 stfle_fac_list[32]; /* 0x0f00 */
...@@ -286,7 +290,10 @@ struct _lowcore { ...@@ -286,7 +290,10 @@ struct _lowcore {
*/ */
__u64 ipib; /* 0x0e00 */ __u64 ipib; /* 0x0e00 */
__u32 ipib_checksum; /* 0x0e08 */ __u32 ipib_checksum; /* 0x0e08 */
__u8 pad_0x0e0c[0x0f00-0x0e0c]; /* 0x0e0c */
/* 64 bit save area */
__u64 save_area_64; /* 0x0e0c */
__u8 pad_0x0e14[0x0f00-0x0e14]; /* 0x0e14 */
/* Extended facility list */ /* Extended facility list */
__u64 stfle_fac_list[32]; /* 0x0f00 */ __u64 stfle_fac_list[32]; /* 0x0f00 */
......
...@@ -113,6 +113,7 @@ extern void pfault_fini(void); ...@@ -113,6 +113,7 @@ extern void pfault_fini(void);
extern void cmma_init(void); extern void cmma_init(void);
extern int memcpy_real(void *, void *, size_t); extern int memcpy_real(void *, void *, size_t);
extern void copy_to_absolute_zero(void *dest, void *src, size_t count);
#define finish_arch_switch(prev) do { \ #define finish_arch_switch(prev) do { \
set_fs(current->thread.mm_segment); \ set_fs(current->thread.mm_segment); \
......
...@@ -142,6 +142,7 @@ int main(void) ...@@ -142,6 +142,7 @@ int main(void)
DEFINE(__LC_FPREGS_SAVE_AREA, offsetof(struct _lowcore, floating_pt_save_area)); DEFINE(__LC_FPREGS_SAVE_AREA, offsetof(struct _lowcore, floating_pt_save_area));
DEFINE(__LC_GPREGS_SAVE_AREA, offsetof(struct _lowcore, gpregs_save_area)); DEFINE(__LC_GPREGS_SAVE_AREA, offsetof(struct _lowcore, gpregs_save_area));
DEFINE(__LC_CREGS_SAVE_AREA, offsetof(struct _lowcore, cregs_save_area)); DEFINE(__LC_CREGS_SAVE_AREA, offsetof(struct _lowcore, cregs_save_area));
DEFINE(__LC_SAVE_AREA_64, offsetof(struct _lowcore, save_area_64));
#ifdef CONFIG_32BIT #ifdef CONFIG_32BIT
DEFINE(SAVE_AREA_BASE, offsetof(struct _lowcore, extended_save_area_addr)); DEFINE(SAVE_AREA_BASE, offsetof(struct _lowcore, extended_save_area_addr));
#else /* CONFIG_32BIT */ #else /* CONFIG_32BIT */
......
...@@ -849,6 +849,34 @@ restart_crash: ...@@ -849,6 +849,34 @@ restart_crash:
restart_go: restart_go:
#endif #endif
#
# PSW restart interrupt handler
#
ENTRY(psw_restart_int_handler)
st %r15,__LC_SAVE_AREA_64(%r0) # save r15
basr %r15,0
0: l %r15,.Lrestart_stack-0b(%r15) # load restart stack
l %r15,0(%r15)
ahi %r15,-SP_SIZE # make room for pt_regs
stm %r0,%r14,SP_R0(%r15) # store gprs %r0-%r14 to stack
mvc SP_R15(4,%r15),__LC_SAVE_AREA_64(%r0)# store saved %r15 to stack
mvc SP_PSW(8,%r15),__LC_RST_OLD_PSW(%r0) # store restart old psw
xc __SF_BACKCHAIN(4,%r15),__SF_BACKCHAIN(%r15) # set backchain to 0
basr %r14,0
1: l %r14,.Ldo_restart-1b(%r14)
basr %r14,%r14
basr %r14,0 # load disabled wait PSW if
2: lpsw restart_psw_crash-2b(%r14) # do_restart returns
.align 4
.Ldo_restart:
.long do_restart
.Lrestart_stack:
.long restart_stack
.align 8
restart_psw_crash:
.long 0x000a0000,0x00000000 + restart_psw_crash
.section .kprobes.text, "ax" .section .kprobes.text, "ax"
#ifdef CONFIG_CHECK_STACK #ifdef CONFIG_CHECK_STACK
......
...@@ -865,6 +865,26 @@ restart_crash: ...@@ -865,6 +865,26 @@ restart_crash:
restart_go: restart_go:
#endif #endif
#
# PSW restart interrupt handler
#
ENTRY(psw_restart_int_handler)
stg %r15,__LC_SAVE_AREA_64(%r0) # save r15
larl %r15,restart_stack # load restart stack
lg %r15,0(%r15)
aghi %r15,-SP_SIZE # make room for pt_regs
stmg %r0,%r14,SP_R0(%r15) # store gprs %r0-%r14 to stack
mvc SP_R15(8,%r15),__LC_SAVE_AREA_64(%r0)# store saved %r15 to stack
mvc SP_PSW(16,%r15),__LC_RST_OLD_PSW(%r0)# store restart old psw
xc __SF_BACKCHAIN(8,%r15),__SF_BACKCHAIN(%r15) # set backchain to 0
brasl %r14,do_restart
larl %r14,restart_psw_crash # load disabled wait PSW if
lpswe 0(%r14) # do_restart returns
.align 8
restart_psw_crash:
.quad 0x0002000080000000,0x0000000000000000 + restart_psw_crash
.section .kprobes.text, "ax" .section .kprobes.text, "ax"
#ifdef CONFIG_CHECK_STACK #ifdef CONFIG_CHECK_STACK
......
...@@ -45,11 +45,13 @@ ...@@ -45,11 +45,13 @@
* - halt * - halt
* - power off * - power off
* - reipl * - reipl
* - restart
*/ */
#define ON_PANIC_STR "on_panic" #define ON_PANIC_STR "on_panic"
#define ON_HALT_STR "on_halt" #define ON_HALT_STR "on_halt"
#define ON_POFF_STR "on_poff" #define ON_POFF_STR "on_poff"
#define ON_REIPL_STR "on_reboot" #define ON_REIPL_STR "on_reboot"
#define ON_RESTART_STR "on_restart"
struct shutdown_action; struct shutdown_action;
struct shutdown_trigger { struct shutdown_trigger {
...@@ -1544,17 +1546,20 @@ static char vmcmd_on_reboot[128]; ...@@ -1544,17 +1546,20 @@ static char vmcmd_on_reboot[128];
static char vmcmd_on_panic[128]; static char vmcmd_on_panic[128];
static char vmcmd_on_halt[128]; static char vmcmd_on_halt[128];
static char vmcmd_on_poff[128]; static char vmcmd_on_poff[128];
static char vmcmd_on_restart[128];
DEFINE_IPL_ATTR_STR_RW(vmcmd, on_reboot, "%s\n", "%s\n", vmcmd_on_reboot); DEFINE_IPL_ATTR_STR_RW(vmcmd, on_reboot, "%s\n", "%s\n", vmcmd_on_reboot);
DEFINE_IPL_ATTR_STR_RW(vmcmd, on_panic, "%s\n", "%s\n", vmcmd_on_panic); DEFINE_IPL_ATTR_STR_RW(vmcmd, on_panic, "%s\n", "%s\n", vmcmd_on_panic);
DEFINE_IPL_ATTR_STR_RW(vmcmd, on_halt, "%s\n", "%s\n", vmcmd_on_halt); DEFINE_IPL_ATTR_STR_RW(vmcmd, on_halt, "%s\n", "%s\n", vmcmd_on_halt);
DEFINE_IPL_ATTR_STR_RW(vmcmd, on_poff, "%s\n", "%s\n", vmcmd_on_poff); DEFINE_IPL_ATTR_STR_RW(vmcmd, on_poff, "%s\n", "%s\n", vmcmd_on_poff);
DEFINE_IPL_ATTR_STR_RW(vmcmd, on_restart, "%s\n", "%s\n", vmcmd_on_restart);
static struct attribute *vmcmd_attrs[] = { static struct attribute *vmcmd_attrs[] = {
&sys_vmcmd_on_reboot_attr.attr, &sys_vmcmd_on_reboot_attr.attr,
&sys_vmcmd_on_panic_attr.attr, &sys_vmcmd_on_panic_attr.attr,
&sys_vmcmd_on_halt_attr.attr, &sys_vmcmd_on_halt_attr.attr,
&sys_vmcmd_on_poff_attr.attr, &sys_vmcmd_on_poff_attr.attr,
&sys_vmcmd_on_restart_attr.attr,
NULL, NULL,
}; };
...@@ -1576,6 +1581,8 @@ static void vmcmd_run(struct shutdown_trigger *trigger) ...@@ -1576,6 +1581,8 @@ static void vmcmd_run(struct shutdown_trigger *trigger)
cmd = vmcmd_on_halt; cmd = vmcmd_on_halt;
else if (strcmp(trigger->name, ON_POFF_STR) == 0) else if (strcmp(trigger->name, ON_POFF_STR) == 0)
cmd = vmcmd_on_poff; cmd = vmcmd_on_poff;
else if (strcmp(trigger->name, ON_RESTART_STR) == 0)
cmd = vmcmd_on_restart;
else else
return; return;
...@@ -1707,6 +1714,34 @@ static void do_panic(void) ...@@ -1707,6 +1714,34 @@ static void do_panic(void)
stop_run(&on_panic_trigger); stop_run(&on_panic_trigger);
} }
/* on restart */
static struct shutdown_trigger on_restart_trigger = {ON_RESTART_STR,
&reipl_action};
static ssize_t on_restart_show(struct kobject *kobj,
struct kobj_attribute *attr, char *page)
{
return sprintf(page, "%s\n", on_restart_trigger.action->name);
}
static ssize_t on_restart_store(struct kobject *kobj,
struct kobj_attribute *attr,
const char *buf, size_t len)
{
return set_trigger(buf, &on_restart_trigger, len);
}
static struct kobj_attribute on_restart_attr =
__ATTR(on_restart, 0644, on_restart_show, on_restart_store);
void do_restart(void)
{
smp_send_stop();
on_restart_trigger.action->fn(&on_restart_trigger);
stop_run(&on_restart_trigger);
}
/* on halt */ /* on halt */
static struct shutdown_trigger on_halt_trigger = {ON_HALT_STR, &stop_action}; static struct shutdown_trigger on_halt_trigger = {ON_HALT_STR, &stop_action};
...@@ -1783,7 +1818,9 @@ static void __init shutdown_triggers_init(void) ...@@ -1783,7 +1818,9 @@ static void __init shutdown_triggers_init(void)
if (sysfs_create_file(&shutdown_actions_kset->kobj, if (sysfs_create_file(&shutdown_actions_kset->kobj,
&on_poff_attr.attr)) &on_poff_attr.attr))
goto fail; goto fail;
if (sysfs_create_file(&shutdown_actions_kset->kobj,
&on_restart_attr.attr))
goto fail;
return; return;
fail: fail:
panic("shutdown_triggers_init failed\n"); panic("shutdown_triggers_init failed\n");
......
...@@ -346,7 +346,7 @@ setup_lowcore(void) ...@@ -346,7 +346,7 @@ setup_lowcore(void)
lc = __alloc_bootmem_low(LC_PAGES * PAGE_SIZE, LC_PAGES * PAGE_SIZE, 0); lc = __alloc_bootmem_low(LC_PAGES * PAGE_SIZE, LC_PAGES * PAGE_SIZE, 0);
lc->restart_psw.mask = PSW_BASE_BITS | PSW_DEFAULT_KEY; lc->restart_psw.mask = PSW_BASE_BITS | PSW_DEFAULT_KEY;
lc->restart_psw.addr = lc->restart_psw.addr =
PSW_ADDR_AMODE | (unsigned long) restart_int_handler; PSW_ADDR_AMODE | (unsigned long) psw_restart_int_handler;
if (user_mode != HOME_SPACE_MODE) if (user_mode != HOME_SPACE_MODE)
lc->restart_psw.mask |= PSW_ASC_HOME; lc->restart_psw.mask |= PSW_ASC_HOME;
lc->external_new_psw.mask = psw_kernel_bits; lc->external_new_psw.mask = psw_kernel_bits;
...@@ -529,6 +529,27 @@ static void __init setup_memory_end(void) ...@@ -529,6 +529,27 @@ static void __init setup_memory_end(void)
memory_end = memory_size; memory_end = memory_size;
} }
void *restart_stack __attribute__((__section__(".data")));
/*
* Setup new PSW and allocate stack for PSW restart interrupt
*/
static void __init setup_restart_psw(void)
{
psw_t psw;
restart_stack = __alloc_bootmem(ASYNC_SIZE, ASYNC_SIZE, 0);
restart_stack += ASYNC_SIZE;
/*
* Setup restart PSW for absolute zero lowcore. This is necesary
* if PSW restart is done on an offline CPU that has lowcore zero
*/
psw.mask = PSW_BASE_BITS | PSW_DEFAULT_KEY;
psw.addr = PSW_ADDR_AMODE | (unsigned long) psw_restart_int_handler;
copy_to_absolute_zero(&S390_lowcore.restart_psw, &psw, sizeof(psw));
}
static void __init static void __init
setup_memory(void) setup_memory(void)
{ {
...@@ -792,6 +813,7 @@ setup_arch(char **cmdline_p) ...@@ -792,6 +813,7 @@ setup_arch(char **cmdline_p)
setup_addressing_mode(); setup_addressing_mode();
setup_memory(); setup_memory();
setup_resources(); setup_resources();
setup_restart_psw();
setup_lowcore(); setup_lowcore();
cpu_init(); cpu_init();
......
...@@ -468,6 +468,11 @@ int __cpuinit start_secondary(void *cpuvoid) ...@@ -468,6 +468,11 @@ int __cpuinit start_secondary(void *cpuvoid)
ipi_call_lock(); ipi_call_lock();
set_cpu_online(smp_processor_id(), true); set_cpu_online(smp_processor_id(), true);
ipi_call_unlock(); ipi_call_unlock();
__ctl_clear_bit(0, 28); /* Disable lowcore protection */
S390_lowcore.restart_psw.mask = PSW_BASE_BITS | PSW_DEFAULT_KEY;
S390_lowcore.restart_psw.addr =
PSW_ADDR_AMODE | (unsigned long) psw_restart_int_handler;
__ctl_set_bit(0, 28); /* Enable lowcore protection */
/* Switch on interrupts */ /* Switch on interrupts */
local_irq_enable(); local_irq_enable();
/* cpu_idle will call schedule for us */ /* cpu_idle will call schedule for us */
...@@ -507,7 +512,11 @@ static int __cpuinit smp_alloc_lowcore(int cpu) ...@@ -507,7 +512,11 @@ static int __cpuinit smp_alloc_lowcore(int cpu)
memset((char *)lowcore + 512, 0, sizeof(*lowcore) - 512); memset((char *)lowcore + 512, 0, sizeof(*lowcore) - 512);
lowcore->async_stack = async_stack + ASYNC_SIZE; lowcore->async_stack = async_stack + ASYNC_SIZE;
lowcore->panic_stack = panic_stack + PAGE_SIZE; lowcore->panic_stack = panic_stack + PAGE_SIZE;
lowcore->restart_psw.mask = PSW_BASE_BITS | PSW_DEFAULT_KEY;
lowcore->restart_psw.addr =
PSW_ADDR_AMODE | (unsigned long) restart_int_handler;
if (user_mode != HOME_SPACE_MODE)
lowcore->restart_psw.mask |= PSW_ASC_HOME;
#ifndef CONFIG_64BIT #ifndef CONFIG_64BIT
if (MACHINE_HAS_IEEE) { if (MACHINE_HAS_IEEE) {
unsigned long save_area; unsigned long save_area;
......
...@@ -85,3 +85,19 @@ int memcpy_real(void *dest, void *src, size_t count) ...@@ -85,3 +85,19 @@ int memcpy_real(void *dest, void *src, size_t count)
arch_local_irq_restore(flags); arch_local_irq_restore(flags);
return rc; return rc;
} }
/*
* Copy memory to absolute zero
*/
void copy_to_absolute_zero(void *dest, void *src, size_t count)
{
unsigned long cr0;
BUG_ON((unsigned long) dest + count >= sizeof(struct _lowcore));
preempt_disable();
__ctl_store(cr0, 0, 0);
__ctl_clear_bit(0, 28); /* disable lowcore protection */
memcpy_real(dest + store_prefix(), src, count);
__ctl_load(cr0, 0, 0);
preempt_enable();
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment