From: Heiko Carstens Date: Mon, 24 Feb 2025 14:59:05 +0000 (+0100) Subject: s390/boot: Add exception table support X-Git-Tag: v6.15-rc1~113^2~30 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=6067891b65cbf0068c2266d40a3a67df8535f72c;p=thirdparty%2Fkernel%2Flinux.git 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 Signed-off-by: Heiko Carstens Signed-off-by: Vasily Gorbik --- diff --git a/arch/s390/boot/Makefile b/arch/s390/boot/Makefile index 8bc1308ac892a..bee49626be4be 100644 --- a/arch/s390/boot/Makefile +++ b/arch/s390/boot/Makefile @@ -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 diff --git a/arch/s390/boot/boot.h b/arch/s390/boot/boot.h index bc3432fffff24..e045cae6e80a2 100644 --- a/arch/s390/boot/boot.h +++ b/arch/s390/boot/boot.h @@ -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); diff --git a/arch/s390/boot/head.S b/arch/s390/boot/head.S index 0a0afb9157187..0b511d5c030b4 100644 --- a/arch/s390/boot/head.S +++ b/arch/s390/boot/head.S @@ -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) diff --git a/arch/s390/boot/pgm_check_info.c b/arch/s390/boot/pgm_check.c similarity index 76% rename from arch/s390/boot/pgm_check_info.c rename to arch/s390/boot/pgm_check.c index e61ae4a159698..fa621fa5bc02a 100644 --- a/arch/s390/boot/pgm_check_info.c +++ b/arch/s390/boot/pgm_check.c @@ -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; } diff --git a/arch/s390/boot/vmlinux.lds.S b/arch/s390/boot/vmlinux.lds.S index 66670212a3611..50988022f9ea3 100644 --- a/arch/s390/boot/vmlinux.lds.S +++ b/arch/s390/boot/vmlinux.lds.S @@ -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*) }