]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
s390/boot: Add exception table support
authorHeiko Carstens <hca@linux.ibm.com>
Mon, 24 Feb 2025 14:59:05 +0000 (15:59 +0100)
committerVasily Gorbik <gor@linux.ibm.com>
Tue, 4 Mar 2025 16:25:22 +0000 (17:25 +0100)
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>
arch/s390/boot/Makefile
arch/s390/boot/boot.h
arch/s390/boot/head.S
arch/s390/boot/pgm_check.c [moved from arch/s390/boot/pgm_check_info.c with 76% similarity]
arch/s390/boot/vmlinux.lds.S

index 8bc1308ac892ab71d7671dbd74e1656f33513e95..bee49626be4bec910293f591adb82dec81f6a915 100644 (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
index bc3432fffff24f621c585872167ffc498d25947e..e045cae6e80a206232e66c151f11269a710e6a64 100644 (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);
index 0a0afb91571872e9165827a780b3f14aaca1d63c..0b511d5c030b4f3b6e4dfbbb60f685c5bf79cdd0 100644 (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)
similarity index 76%
rename from arch/s390/boot/pgm_check_info.c
rename to arch/s390/boot/pgm_check.c
index e61ae4a159698a5b3e690e0dc3a611f15d78fbc1..fa621fa5bc02ae858ebcd096f76904e5a7a2312b 100644 (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;
 }
index 66670212a36118ba79d41341cc4edd7a8cb4c2d4..50988022f9ea329e20f98dab44e6de4f2ac16e5a 100644 (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*)
        }