diff options
Diffstat (limited to 'arch/s390/boot')
-rw-r--r-- | arch/s390/boot/Makefile | 2 | ||||
-rw-r--r-- | arch/s390/boot/boot.h | 1 | ||||
-rw-r--r-- | arch/s390/boot/compressed/.gitignore | 3 | ||||
-rw-r--r-- | arch/s390/boot/compressed/decompressor.c | 8 | ||||
-rw-r--r-- | arch/s390/boot/compressed/vmlinux.lds.S | 3 | ||||
-rw-r--r-- | arch/s390/boot/head.S | 34 | ||||
-rw-r--r-- | arch/s390/boot/ipl_parm.c | 25 | ||||
-rw-r--r-- | arch/s390/boot/kaslr.c | 41 | ||||
-rw-r--r-- | arch/s390/boot/mem_detect.c | 7 | ||||
-rw-r--r-- | arch/s390/boot/pgm_check_info.c | 90 | ||||
-rw-r--r-- | arch/s390/boot/startup.c | 27 | ||||
-rw-r--r-- | arch/s390/boot/uv.c | 3 |
12 files changed, 210 insertions, 34 deletions
diff --git a/arch/s390/boot/Makefile b/arch/s390/boot/Makefile index 4cf0bddb7d92..e2c47d3a1c89 100644 --- a/arch/s390/boot/Makefile +++ b/arch/s390/boot/Makefile @@ -36,7 +36,7 @@ CFLAGS_sclp_early_core.o += -I$(srctree)/drivers/s390/char obj-y := head.o als.o startup.o mem_detect.o ipl_parm.o ipl_report.o obj-y += string.o ebcdic.o sclp_early_core.o mem.o ipl_vmparm.o cmdline.o -obj-y += version.o ctype.o text_dma.o +obj-y += version.o pgm_check_info.o ctype.o text_dma.o obj-$(CONFIG_PROTECTED_VIRTUALIZATION_GUEST) += uv.o obj-$(CONFIG_RELOCATABLE) += machine_kexec_reloc.o obj-$(CONFIG_RANDOMIZE_BASE) += kaslr.o diff --git a/arch/s390/boot/boot.h b/arch/s390/boot/boot.h index 1c3b2b257637..2ea603f70c3b 100644 --- a/arch/s390/boot/boot.h +++ b/arch/s390/boot/boot.h @@ -10,6 +10,7 @@ void parse_boot_command_line(void); void setup_memory_end(void); void verify_facilities(void); void print_missing_facilities(void); +void print_pgm_check_info(void); unsigned long get_random_base(unsigned long safe_addr); extern int kaslr_enabled; diff --git a/arch/s390/boot/compressed/.gitignore b/arch/s390/boot/compressed/.gitignore index 45aeb4f08752..e72fcd7ecebb 100644 --- a/arch/s390/boot/compressed/.gitignore +++ b/arch/s390/boot/compressed/.gitignore @@ -1,5 +1,2 @@ -sizes.h vmlinux vmlinux.lds -vmlinux.scr.lds -vmlinux.bin.full diff --git a/arch/s390/boot/compressed/decompressor.c b/arch/s390/boot/compressed/decompressor.c index 45046630c56a..368fd372c875 100644 --- a/arch/s390/boot/compressed/decompressor.c +++ b/arch/s390/boot/compressed/decompressor.c @@ -30,13 +30,13 @@ extern unsigned char _compressed_start[]; extern unsigned char _compressed_end[]; #ifdef CONFIG_HAVE_KERNEL_BZIP2 -#define HEAP_SIZE 0x400000 +#define BOOT_HEAP_SIZE 0x400000 #else -#define HEAP_SIZE 0x10000 +#define BOOT_HEAP_SIZE 0x10000 #endif static unsigned long free_mem_ptr = (unsigned long) _end; -static unsigned long free_mem_end_ptr = (unsigned long) _end + HEAP_SIZE; +static unsigned long free_mem_end_ptr = (unsigned long) _end + BOOT_HEAP_SIZE; #ifdef CONFIG_KERNEL_GZIP #include "../../../../lib/decompress_inflate.c" @@ -62,7 +62,7 @@ static unsigned long free_mem_end_ptr = (unsigned long) _end + HEAP_SIZE; #include "../../../../lib/decompress_unxz.c" #endif -#define decompress_offset ALIGN((unsigned long)_end + HEAP_SIZE, PAGE_SIZE) +#define decompress_offset ALIGN((unsigned long)_end + BOOT_HEAP_SIZE, PAGE_SIZE) unsigned long mem_safe_offset(void) { diff --git a/arch/s390/boot/compressed/vmlinux.lds.S b/arch/s390/boot/compressed/vmlinux.lds.S index 635217eb3d91..44561b2c3712 100644 --- a/arch/s390/boot/compressed/vmlinux.lds.S +++ b/arch/s390/boot/compressed/vmlinux.lds.S @@ -37,9 +37,9 @@ SECTIONS * .dma section for code, data, ex_table that need to stay below 2 GB, * even when the kernel is relocate: above 2 GB. */ + . = ALIGN(PAGE_SIZE); _sdma = .; .dma.text : { - . = ALIGN(PAGE_SIZE); _stext_dma = .; *(.dma.text) . = ALIGN(PAGE_SIZE); @@ -52,6 +52,7 @@ SECTIONS _stop_dma_ex_table = .; } .dma.data : { *(.dma.data) } + . = ALIGN(PAGE_SIZE); _edma = .; BOOT_DATA diff --git a/arch/s390/boot/head.S b/arch/s390/boot/head.S index 2087bed6e60f..dae10961d072 100644 --- a/arch/s390/boot/head.S +++ b/arch/s390/boot/head.S @@ -60,8 +60,10 @@ __HEAD .long 0x02000690,0x60000050 .long 0x020006e0,0x20000050 - .org 0x1a0 + .org __LC_RST_NEW_PSW # 0x1a0 .quad 0,iplstart + .org __LC_PGM_NEW_PSW # 0x1d0 + .quad 0x0000000180000000,startup_pgm_check_handler .org 0x200 @@ -327,7 +329,7 @@ ENTRY(startup_kdump) .quad .Lduct # cr5: primary-aste origin .quad 0 # cr6: I/O interrupts .quad 0 # cr7: secondary space segment table - .quad 0 # cr8: access registers translation + .quad 0x0000000000008000 # cr8: access registers translation .quad 0 # cr9: tracing off .quad 0 # cr10: tracing off .quad 0 # cr11: tracing off @@ -352,6 +354,34 @@ ENTRY(startup_kdump) #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. +# +ENTRY(startup_pgm_check_handler) + stmg %r0,%r15,__LC_SAVE_AREA_SYNC + la %r1,4095 + stctg %c0,%c15,__LC_CREGS_SAVE_AREA-4095(%r1) + mvc __LC_GPREGS_SAVE_AREA-4095(128,%r1),__LC_SAVE_AREA_SYNC + mvc __LC_PSW_SAVE_AREA-4095(16,%r1),__LC_PGM_OLD_PSW + mvc __LC_RETURN_PSW(16),__LC_PGM_OLD_PSW + ni __LC_RETURN_PSW,0xfc # remove IO and EX bits + ni __LC_RETURN_PSW+1,0xfb # remove MCHK bit + oi __LC_RETURN_PSW+1,0x2 # set wait state bit + larl %r2,.Lold_psw_disabled_wait + stg %r2,__LC_PGM_NEW_PSW+8 + l %r15,.Ldump_info_stack-.Lold_psw_disabled_wait(%r2) + brasl %r14,print_pgm_check_info +.Lold_psw_disabled_wait: + la %r1,4095 + lmg %r0,%r15,__LC_GPREGS_SAVE_AREA-4095(%r1) + lpswe __LC_RETURN_PSW # disabled wait +.Ldump_info_stack: + .long 0x5000 + PAGE_SIZE - STACK_FRAME_OVERHEAD +ENDPROC(startup_pgm_check_handler) + +# # params at 10400 (setup.h) # Must be keept in sync with struct parmarea in setup.h # diff --git a/arch/s390/boot/ipl_parm.c b/arch/s390/boot/ipl_parm.c index b8aa6a9f937b..357adad991d2 100644 --- a/arch/s390/boot/ipl_parm.c +++ b/arch/s390/boot/ipl_parm.c @@ -7,13 +7,16 @@ #include <asm/sections.h> #include <asm/boot_data.h> #include <asm/facility.h> +#include <asm/pgtable.h> #include <asm/uv.h> #include "boot.h" char __bootdata(early_command_line)[COMMAND_LINE_SIZE]; struct ipl_parameter_block __bootdata_preserved(ipl_block); int __bootdata_preserved(ipl_block_valid); +unsigned int __bootdata_preserved(zlib_dfltcc_support) = ZLIB_DFLTCC_FULL; +unsigned long __bootdata(vmalloc_size) = VMALLOC_DEFAULT_SIZE; unsigned long __bootdata(memory_end); int __bootdata(memory_end_set); int __bootdata(noexec_disabled); @@ -219,18 +222,34 @@ void parse_boot_command_line(void) while (*args) { args = next_arg(args, ¶m, &val); - if (!strcmp(param, "mem")) { - memory_end = memparse(val, NULL); + if (!strcmp(param, "mem") && val) { + memory_end = round_down(memparse(val, NULL), PAGE_SIZE); memory_end_set = 1; } + if (!strcmp(param, "vmalloc") && val) + vmalloc_size = round_up(memparse(val, NULL), PAGE_SIZE); + + if (!strcmp(param, "dfltcc")) { + if (!strcmp(val, "off")) + zlib_dfltcc_support = ZLIB_DFLTCC_DISABLED; + else if (!strcmp(val, "on")) + zlib_dfltcc_support = ZLIB_DFLTCC_FULL; + else if (!strcmp(val, "def_only")) + zlib_dfltcc_support = ZLIB_DFLTCC_DEFLATE_ONLY; + else if (!strcmp(val, "inf_only")) + zlib_dfltcc_support = ZLIB_DFLTCC_INFLATE_ONLY; + else if (!strcmp(val, "always")) + zlib_dfltcc_support = ZLIB_DFLTCC_FULL_DEBUG; + } + if (!strcmp(param, "noexec")) { rc = kstrtobool(val, &enabled); if (!rc && !enabled) noexec_disabled = 1; } - if (!strcmp(param, "facilities")) + if (!strcmp(param, "facilities") && val) modify_fac_list(val); if (!strcmp(param, "nokaslr")) diff --git a/arch/s390/boot/kaslr.c b/arch/s390/boot/kaslr.c index c34a6387ce38..5d12352545c5 100644 --- a/arch/s390/boot/kaslr.c +++ b/arch/s390/boot/kaslr.c @@ -3,6 +3,7 @@ * Copyright IBM Corp. 2019 */ #include <asm/mem_detect.h> +#include <asm/pgtable.h> #include <asm/cpacf.h> #include <asm/timex.h> #include <asm/sclp.h> @@ -90,8 +91,10 @@ static unsigned long get_random(unsigned long limit) unsigned long get_random_base(unsigned long safe_addr) { + unsigned long memory_limit = memory_end_set ? memory_end : 0; unsigned long base, start, end, kernel_size; unsigned long block_sum, offset; + unsigned long kasan_needs; int i; if (IS_ENABLED(CONFIG_BLK_DEV_INITRD) && INITRD_START && INITRD_SIZE) { @@ -100,14 +103,36 @@ unsigned long get_random_base(unsigned long safe_addr) } safe_addr = ALIGN(safe_addr, THREAD_SIZE); + if ((IS_ENABLED(CONFIG_KASAN))) { + /* + * Estimate kasan memory requirements, which it will reserve + * at the very end of available physical memory. To estimate + * that, we take into account that kasan would require + * 1/8 of available physical memory (for shadow memory) + + * creating page tables for the whole memory + shadow memory + * region (1 + 1/8). To keep page tables estimates simple take + * the double of combined ptes size. + */ + memory_limit = get_mem_detect_end(); + if (memory_end_set && memory_limit > memory_end) + memory_limit = memory_end; + + /* for shadow memory */ + kasan_needs = memory_limit / 8; + /* for paging structures */ + kasan_needs += (memory_limit + kasan_needs) / PAGE_SIZE / + _PAGE_ENTRIES * _PAGE_TABLE_SIZE * 2; + memory_limit -= kasan_needs; + } + kernel_size = vmlinux.image_size + vmlinux.bss_size; block_sum = 0; for_each_mem_detect_block(i, &start, &end) { - if (memory_end_set) { - if (start >= memory_end) + if (memory_limit) { + if (start >= memory_limit) break; - if (end > memory_end) - end = memory_end; + if (end > memory_limit) + end = memory_limit; } if (end - start < kernel_size) continue; @@ -125,11 +150,11 @@ unsigned long get_random_base(unsigned long safe_addr) base = safe_addr; block_sum = offset = 0; for_each_mem_detect_block(i, &start, &end) { - if (memory_end_set) { - if (start >= memory_end) + if (memory_limit) { + if (start >= memory_limit) break; - if (end > memory_end) - end = memory_end; + if (end > memory_limit) + end = memory_limit; } if (end - start < kernel_size) continue; diff --git a/arch/s390/boot/mem_detect.c b/arch/s390/boot/mem_detect.c index 5d316fe40480..62e7c13ce85c 100644 --- a/arch/s390/boot/mem_detect.c +++ b/arch/s390/boot/mem_detect.c @@ -63,13 +63,6 @@ void add_mem_detect_block(u64 start, u64 end) mem_detect.count++; } -static unsigned long get_mem_detect_end(void) -{ - if (mem_detect.count) - return __get_mem_detect_block_ptr(mem_detect.count - 1)->end; - return 0; -} - static int __diag260(unsigned long rx1, unsigned long rx2) { register unsigned long _rx1 asm("2") = rx1; diff --git a/arch/s390/boot/pgm_check_info.c b/arch/s390/boot/pgm_check_info.c new file mode 100644 index 000000000000..83b5b7915c32 --- /dev/null +++ b/arch/s390/boot/pgm_check_info.c @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <linux/kernel.h> +#include <linux/string.h> +#include <asm/lowcore.h> +#include <asm/sclp.h> +#include "boot.h" + +const char hex_asc[] = "0123456789abcdef"; + +#define add_val_as_hex(dst, val) \ + __add_val_as_hex(dst, (const unsigned char *)&val, sizeof(val)) + +static char *__add_val_as_hex(char *dst, const unsigned char *src, size_t count) +{ + while (count--) + dst = hex_byte_pack(dst, *src++); + return dst; +} + +static char *add_str(char *dst, char *src) +{ + strcpy(dst, src); + return dst + strlen(dst); +} + +void print_pgm_check_info(void) +{ + struct psw_bits *psw = &psw_bits(S390_lowcore.psw_save_area); + unsigned short ilc = S390_lowcore.pgm_ilc >> 1; + char buf[256]; + int row, col; + char *p; + + add_str(buf, "Linux version "); + strlcat(buf, kernel_version, sizeof(buf)); + sclp_early_printk(buf); + + p = add_str(buf, "Kernel fault: interruption code "); + p = add_val_as_hex(buf + strlen(buf), S390_lowcore.pgm_code); + p = add_str(p, " ilc:"); + *p++ = hex_asc_lo(ilc); + add_str(p, "\n"); + sclp_early_printk(buf); + + p = add_str(buf, "PSW : "); + p = add_val_as_hex(p, S390_lowcore.psw_save_area.mask); + p = add_str(p, " "); + p = add_val_as_hex(p, S390_lowcore.psw_save_area.addr); + add_str(p, "\n"); + sclp_early_printk(buf); + + p = add_str(buf, " R:"); + *p++ = hex_asc_lo(psw->per); + p = add_str(p, " T:"); + *p++ = hex_asc_lo(psw->dat); + p = add_str(p, " IO:"); + *p++ = hex_asc_lo(psw->io); + p = add_str(p, " EX:"); + *p++ = hex_asc_lo(psw->ext); + p = add_str(p, " Key:"); + *p++ = hex_asc_lo(psw->key); + p = add_str(p, " M:"); + *p++ = hex_asc_lo(psw->mcheck); + p = add_str(p, " W:"); + *p++ = hex_asc_lo(psw->wait); + p = add_str(p, " P:"); + *p++ = hex_asc_lo(psw->pstate); + p = add_str(p, " AS:"); + *p++ = hex_asc_lo(psw->as); + p = add_str(p, " CC:"); + *p++ = hex_asc_lo(psw->cc); + p = add_str(p, " PM:"); + *p++ = hex_asc_lo(psw->pm); + p = add_str(p, " RI:"); + *p++ = hex_asc_lo(psw->ri); + p = add_str(p, " EA:"); + *p++ = hex_asc_lo(psw->eaba); + add_str(p, "\n"); + sclp_early_printk(buf); + + for (row = 0; row < 4; row++) { + p = add_str(buf, row == 0 ? "GPRS:" : " "); + for (col = 0; col < 4; col++) { + p = add_str(p, " "); + p = add_val_as_hex(p, S390_lowcore.gpregs_save_area[row * 4 + col]); + } + add_str(p, "\n"); + sclp_early_printk(buf); + } +} diff --git a/arch/s390/boot/startup.c b/arch/s390/boot/startup.c index 7b0d05414618..3b3a11f95269 100644 --- a/arch/s390/boot/startup.c +++ b/arch/s390/boot/startup.c @@ -46,7 +46,7 @@ struct diag_ops __bootdata_preserved(diag_dma_ops) = { .diag0c = _diag0c_dma, .diag308_reset = _diag308_reset_dma }; -static struct diag210 _diag210_tmp_dma __section(".dma.data"); +static struct diag210 _diag210_tmp_dma __section(.dma.data); struct diag210 *__bootdata_preserved(__diag210_tmp_dma) = &_diag210_tmp_dma; void _swsusp_reset_dma(void); unsigned long __bootdata_preserved(__swsusp_reset_dma) = __pa(_swsusp_reset_dma); @@ -101,10 +101,18 @@ static void handle_relocs(unsigned long offset) dynsym = (Elf64_Sym *) vmlinux.dynsym_start; for (rela = rela_start; rela < rela_end; rela++) { loc = rela->r_offset + offset; - val = rela->r_addend + offset; + val = rela->r_addend; r_sym = ELF64_R_SYM(rela->r_info); - if (r_sym) - val += dynsym[r_sym].st_value; + if (r_sym) { + if (dynsym[r_sym].st_shndx != SHN_UNDEF) + val += dynsym[r_sym].st_value + offset; + } else { + /* + * 0 == undefined symbol table index (STN_UNDEF), + * used for R_390_RELATIVE, only add KASLR offset + */ + val += offset; + } r_type = ELF64_R_TYPE(rela->r_info); rc = arch_kexec_do_relocs(r_type, (void *) loc, val, 0); if (rc) @@ -112,6 +120,11 @@ static void handle_relocs(unsigned long offset) } } +static void clear_bss_section(void) +{ + memset((void *)vmlinux.default_lma + vmlinux.image_size, 0, vmlinux.bss_size); +} + void startup_kernel(void) { unsigned long random_lma; @@ -151,11 +164,17 @@ void startup_kernel(void) } else if (__kaslr_offset) memcpy((void *)vmlinux.default_lma, img, vmlinux.image_size); + clear_bss_section(); copy_bootdata(); if (IS_ENABLED(CONFIG_RELOCATABLE)) handle_relocs(__kaslr_offset); if (__kaslr_offset) { + /* + * Save KASLR offset for early dumps, before vmcore_info is set. + * Mark as uneven to distinguish from real vmcore_info pointer. + */ + S390_lowcore.vmcore_info = __kaslr_offset | 0x1UL; /* Clear non-relocated kernel */ if (IS_ENABLED(CONFIG_KERNEL_UNCOMPRESSED)) memset(img, 0, vmlinux.image_size); diff --git a/arch/s390/boot/uv.c b/arch/s390/boot/uv.c index ed007f4a6444..3f501159ee9f 100644 --- a/arch/s390/boot/uv.c +++ b/arch/s390/boot/uv.c @@ -15,7 +15,8 @@ void uv_query_info(void) if (!test_facility(158)) return; - if (uv_call(0, (uint64_t)&uvcb)) + /* rc==0x100 means that there is additional data we do not process */ + if (uv_call(0, (uint64_t)&uvcb) && uvcb.header.rc != 0x100) return; if (test_bit_inv(BIT_UVC_CMD_SET_SHARED_ACCESS, (unsigned long *)uvcb.inst_calls_list) && |