Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Heiko Carstens and committed by
Vasily Gorbik
6067891b b10ac5d7

+39 -11
+1 -1
arch/s390/boot/Makefile
··· 26 26 27 27 obj-y := head.o als.o startup.o physmem_info.o ipl_parm.o ipl_report.o vmem.o 28 28 obj-y += string.o ebcdic.o sclp_early_core.o mem.o ipl_vmparm.o cmdline.o 29 - obj-y += version.o pgm_check_info.o ctype.o ipl_data.o relocs.o alternative.o 29 + obj-y += version.o pgm_check.o ctype.o ipl_data.o relocs.o alternative.o 30 30 obj-y += uv.o printk.o 31 31 obj-$(CONFIG_RANDOMIZE_BASE) += kaslr.o 32 32 obj-y += $(if $(CONFIG_KERNEL_UNCOMPRESSED),,decompressor.o) info.o
+1 -1
arch/s390/boot/boot.h
··· 65 65 void print_missing_facilities(void); 66 66 void sclp_early_setup_buffer(void); 67 67 void alt_debug_setup(char *str); 68 - void print_pgm_check_info(struct pt_regs *regs); 68 + void do_pgm_check(struct pt_regs *regs); 69 69 unsigned long randomize_within_range(unsigned long size, unsigned long align, 70 70 unsigned long min, unsigned long max); 71 71 void setup_vmem(unsigned long kernel_start, unsigned long kernel_end, unsigned long asce_limit);
+6 -7
arch/s390/boot/head.S
··· 293 293 294 294 #include "head_kdump.S" 295 295 296 - # 297 - # This program check is active immediately after kernel start 298 - # and until early_pgm_check_handler is set in kernel/early.c 299 - # It simply saves general/control registers and psw in 300 - # the save area and does disabled wait with a faulty address. 301 - # 302 296 SYM_CODE_START_LOCAL(startup_pgm_check_handler) 303 297 stmg %r8,%r15,__LC_SAVE_AREA 304 298 la %r8,4095 ··· 312 318 mvc __PT_R0(128,%r2),__LC_GPREGS_SAVE_AREA-4095(%r8) 313 319 mvc __PT_LAST_BREAK(8,%r2),__LC_PGM_LAST_BREAK 314 320 mvc __PT_INT_CODE(4,%r2),__LC_PGM_INT_CODE 315 - brasl %r14,print_pgm_check_info 321 + brasl %r14,do_pgm_check 322 + larl %r9,startup_pgm_check_handler 323 + stg %r9,__LC_PGM_NEW_PSW+8 324 + mvc __LC_RETURN_PSW(16),STACK_FRAME_OVERHEAD+__PT_PSW(%r15) 325 + lmg %r0,%r15,STACK_FRAME_OVERHEAD+__PT_R0(%r15) 326 + lpswe __LC_RETURN_PSW 316 327 .Lold_psw_disabled_wait: 317 328 la %r8,4095 318 329 lmg %r0,%r15,__LC_GPREGS_SAVE_AREA-4095(%r8)
+30 -1
arch/s390/boot/pgm_check_info.c arch/s390/boot/pgm_check.c
··· 32 32 } 33 33 } 34 34 35 - void print_pgm_check_info(struct pt_regs *regs) 35 + extern struct exception_table_entry __start___ex_table[]; 36 + extern struct exception_table_entry __stop___ex_table[]; 37 + 38 + static inline unsigned long extable_insn(const struct exception_table_entry *x) 39 + { 40 + return (unsigned long)&x->insn + x->insn; 41 + } 42 + 43 + static bool ex_handler(struct pt_regs *regs) 44 + { 45 + const struct exception_table_entry *ex; 46 + 47 + for (ex = __start___ex_table; ex < __stop___ex_table; ex++) { 48 + if (extable_insn(ex) != regs->psw.addr) 49 + continue; 50 + if (ex->type != EX_TYPE_FIXUP) 51 + return false; 52 + regs->psw.addr = extable_fixup(ex); 53 + return true; 54 + } 55 + return false; 56 + } 57 + 58 + void do_pgm_check(struct pt_regs *regs) 36 59 { 37 60 struct psw_bits *psw = &psw_bits(regs->psw); 38 61 unsigned long *gpregs = regs->gprs; 39 62 63 + if (ex_handler(regs)) 64 + return; 40 65 if (bootdebug) 41 66 boot_rb_dump(); 42 67 boot_emerg("Linux version %s\n", kernel_version); ··· 85 60 print_stacktrace(gpregs[15]); 86 61 boot_emerg("Last Breaking-Event-Address:\n"); 87 62 boot_emerg(" [<%016lx>] %pS\n", regs->last_break, (void *)regs->last_break); 63 + /* Convert to disabled wait PSW */ 64 + psw->io = 0; 65 + psw->ext = 0; 66 + psw->wait = 1; 88 67 }
+1 -1
arch/s390/boot/vmlinux.lds.S
··· 40 40 *(.rodata.*) 41 41 _erodata = . ; 42 42 } 43 + EXCEPTION_TABLE(16) 43 44 .got : { 44 45 *(.got) 45 46 } ··· 166 165 /DISCARD/ : { 167 166 COMMON_DISCARDS 168 167 *(.eh_frame) 169 - *(__ex_table) 170 168 *(*__ksymtab*) 171 169 *(___kcrctab*) 172 170 }