summaryrefslogtreecommitdiffstats
path: root/arch/s390/boot
diff options
context:
space:
mode:
Diffstat (limited to 'arch/s390/boot')
-rw-r--r--arch/s390/boot/Makefile2
-rw-r--r--arch/s390/boot/boot.h1
-rw-r--r--arch/s390/boot/compressed/.gitignore3
-rw-r--r--arch/s390/boot/compressed/decompressor.c8
-rw-r--r--arch/s390/boot/compressed/vmlinux.lds.S3
-rw-r--r--arch/s390/boot/head.S34
-rw-r--r--arch/s390/boot/ipl_parm.c25
-rw-r--r--arch/s390/boot/kaslr.c41
-rw-r--r--arch/s390/boot/mem_detect.c7
-rw-r--r--arch/s390/boot/pgm_check_info.c90
-rw-r--r--arch/s390/boot/startup.c27
-rw-r--r--arch/s390/boot/uv.c3
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, &param, &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) &&
OpenPOWER on IntegriCloud