s390/boot: Add exception table support

The early boot code contains various open-coded inline assemblies with
exception handling. In order to handle possible exceptions each of them
changes the program check new psw, and restores it.

In order to simplify the various inline assemblies add simple exception
table support: the program check handler is called with a fully populated
pt_regs on the stack and may change the psw and register members. When the
program check handler returns the psw and registers from pt_regs will be
used to continue execution.

The program check handler searches the exception table for an entry which
matches the address of the program check. If such an entry is found the psw
address within pt_regs on the stack is replaced with a fixup address, and
execution continues at the new address.

If no entry is found the psw is changed to a disabled wait psw and
execution stops.

Before entering the C part of the program check handler the address of the
program check new psw is replaced to a minimalistic handler.
This is supposed to help against program check loops. If an exception
happens while in program check processing the register contents of the
original exception are restored and a disabled wait psw is loaded.

Acked-by: Alexander Gordeev <agordeev@linux.ibm.com>
Signed-off-by: Heiko Carstens <hca@linux.ibm.com>
Signed-off-by: Vasily Gorbik <gor@linux.ibm.com>
This commit is contained in:
Heiko Carstens
2025-02-24 15:59:05 +01:00
committed by Vasily Gorbik
parent b10ac5d77c
commit 6067891b65
5 changed files with 39 additions and 11 deletions

View File

@@ -26,7 +26,7 @@ CFLAGS_sclp_early_core.o += -I$(srctree)/drivers/s390/char
obj-y := head.o als.o startup.o physmem_info.o ipl_parm.o ipl_report.o vmem.o
obj-y += string.o ebcdic.o sclp_early_core.o mem.o ipl_vmparm.o cmdline.o
obj-y += version.o pgm_check_info.o ctype.o ipl_data.o relocs.o alternative.o
obj-y += version.o pgm_check.o ctype.o ipl_data.o relocs.o alternative.o
obj-y += uv.o printk.o
obj-$(CONFIG_RANDOMIZE_BASE) += kaslr.o
obj-y += $(if $(CONFIG_KERNEL_UNCOMPRESSED),,decompressor.o) info.o

View File

@@ -65,7 +65,7 @@ void verify_facilities(void);
void print_missing_facilities(void);
void sclp_early_setup_buffer(void);
void alt_debug_setup(char *str);
void print_pgm_check_info(struct pt_regs *regs);
void do_pgm_check(struct pt_regs *regs);
unsigned long randomize_within_range(unsigned long size, unsigned long align,
unsigned long min, unsigned long max);
void setup_vmem(unsigned long kernel_start, unsigned long kernel_end, unsigned long asce_limit);

View File

@@ -293,12 +293,6 @@ SYM_CODE_END(startup_normal)
#include "head_kdump.S"
#
# This program check is active immediately after kernel start
# and until early_pgm_check_handler is set in kernel/early.c
# It simply saves general/control registers and psw in
# the save area and does disabled wait with a faulty address.
#
SYM_CODE_START_LOCAL(startup_pgm_check_handler)
stmg %r8,%r15,__LC_SAVE_AREA
la %r8,4095
@@ -318,7 +312,12 @@ SYM_CODE_START_LOCAL(startup_pgm_check_handler)
mvc __PT_R0(128,%r2),__LC_GPREGS_SAVE_AREA-4095(%r8)
mvc __PT_LAST_BREAK(8,%r2),__LC_PGM_LAST_BREAK
mvc __PT_INT_CODE(4,%r2),__LC_PGM_INT_CODE
brasl %r14,print_pgm_check_info
brasl %r14,do_pgm_check
larl %r9,startup_pgm_check_handler
stg %r9,__LC_PGM_NEW_PSW+8
mvc __LC_RETURN_PSW(16),STACK_FRAME_OVERHEAD+__PT_PSW(%r15)
lmg %r0,%r15,STACK_FRAME_OVERHEAD+__PT_R0(%r15)
lpswe __LC_RETURN_PSW
.Lold_psw_disabled_wait:
la %r8,4095
lmg %r0,%r15,__LC_GPREGS_SAVE_AREA-4095(%r8)

View File

@@ -32,11 +32,36 @@ void print_stacktrace(unsigned long sp)
}
}
void print_pgm_check_info(struct pt_regs *regs)
extern struct exception_table_entry __start___ex_table[];
extern struct exception_table_entry __stop___ex_table[];
static inline unsigned long extable_insn(const struct exception_table_entry *x)
{
return (unsigned long)&x->insn + x->insn;
}
static bool ex_handler(struct pt_regs *regs)
{
const struct exception_table_entry *ex;
for (ex = __start___ex_table; ex < __stop___ex_table; ex++) {
if (extable_insn(ex) != regs->psw.addr)
continue;
if (ex->type != EX_TYPE_FIXUP)
return false;
regs->psw.addr = extable_fixup(ex);
return true;
}
return false;
}
void do_pgm_check(struct pt_regs *regs)
{
struct psw_bits *psw = &psw_bits(regs->psw);
unsigned long *gpregs = regs->gprs;
if (ex_handler(regs))
return;
if (bootdebug)
boot_rb_dump();
boot_emerg("Linux version %s\n", kernel_version);
@@ -60,4 +85,8 @@ void print_pgm_check_info(struct pt_regs *regs)
print_stacktrace(gpregs[15]);
boot_emerg("Last Breaking-Event-Address:\n");
boot_emerg(" [<%016lx>] %pS\n", regs->last_break, (void *)regs->last_break);
/* Convert to disabled wait PSW */
psw->io = 0;
psw->ext = 0;
psw->wait = 1;
}

View File

@@ -40,6 +40,7 @@ SECTIONS
*(.rodata.*)
_erodata = . ;
}
EXCEPTION_TABLE(16)
.got : {
*(.got)
}
@@ -165,7 +166,6 @@ SECTIONS
/DISCARD/ : {
COMMON_DISCARDS
*(.eh_frame)
*(__ex_table)
*(*__ksymtab*)
*(___kcrctab*)
}