diff options
| -rw-r--r-- | arch/s390/Kconfig | 10 | ||||
| -rw-r--r-- | arch/s390/boot/Makefile | 1 | ||||
| -rw-r--r-- | arch/s390/boot/boot.h | 3 | ||||
| -rw-r--r-- | arch/s390/boot/ipl_parm.c | 15 | ||||
| -rw-r--r-- | arch/s390/boot/kaslr.c | 144 | ||||
| -rw-r--r-- | arch/s390/boot/startup.c | 31 | ||||
| -rw-r--r-- | arch/s390/include/asm/setup.h | 6 | ||||
| -rw-r--r-- | arch/s390/kernel/machine_kexec.c | 1 | ||||
| -rw-r--r-- | arch/s390/kernel/setup.c | 1 |
9 files changed, 207 insertions, 5 deletions
diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig index 4c99e4f5f366..8c15392ee985 100644 --- a/arch/s390/Kconfig +++ b/arch/s390/Kconfig | |||
| @@ -637,6 +637,16 @@ config RELOCATABLE | |||
| 637 | The relocations make the kernel image about 15% larger (compressed | 637 | The relocations make the kernel image about 15% larger (compressed |
| 638 | 10%), but are discarded at runtime. | 638 | 10%), but are discarded at runtime. |
| 639 | 639 | ||
| 640 | config RANDOMIZE_BASE | ||
| 641 | bool "Randomize the address of the kernel image (KASLR)" | ||
| 642 | depends on RELOCATABLE | ||
| 643 | default y | ||
| 644 | help | ||
| 645 | In support of Kernel Address Space Layout Randomization (KASLR), | ||
| 646 | this randomizes the address at which the kernel image is loaded, | ||
| 647 | as a security feature that deters exploit attempts relying on | ||
| 648 | knowledge of the location of kernel internals. | ||
| 649 | |||
| 640 | endmenu | 650 | endmenu |
| 641 | 651 | ||
| 642 | menu "Memory setup" | 652 | menu "Memory setup" |
diff --git a/arch/s390/boot/Makefile b/arch/s390/boot/Makefile index 88932c25ad26..cb40317dc3f7 100644 --- a/arch/s390/boot/Makefile +++ b/arch/s390/boot/Makefile | |||
| @@ -33,6 +33,7 @@ obj-y += string.o ebcdic.o sclp_early_core.o mem.o ipl_vmparm.o cmdline.o | |||
| 33 | obj-y += ctype.o text_dma.o | 33 | obj-y += ctype.o text_dma.o |
| 34 | obj-$(CONFIG_PROTECTED_VIRTUALIZATION_GUEST) += uv.o | 34 | obj-$(CONFIG_PROTECTED_VIRTUALIZATION_GUEST) += uv.o |
| 35 | obj-$(CONFIG_RELOCATABLE) += machine_kexec_reloc.o | 35 | obj-$(CONFIG_RELOCATABLE) += machine_kexec_reloc.o |
| 36 | obj-$(CONFIG_RANDOMIZE_BASE) += kaslr.o | ||
| 36 | targets := bzImage startup.a section_cmp.boot.data section_cmp.boot.preserved.data $(obj-y) | 37 | targets := bzImage startup.a section_cmp.boot.data section_cmp.boot.preserved.data $(obj-y) |
| 37 | subdir- := compressed | 38 | subdir- := compressed |
| 38 | 39 | ||
diff --git a/arch/s390/boot/boot.h b/arch/s390/boot/boot.h index ca395fcff15e..ad57c2205a71 100644 --- a/arch/s390/boot/boot.h +++ b/arch/s390/boot/boot.h | |||
| @@ -9,6 +9,9 @@ void setup_boot_command_line(void); | |||
| 9 | void parse_boot_command_line(void); | 9 | void parse_boot_command_line(void); |
| 10 | void setup_memory_end(void); | 10 | void setup_memory_end(void); |
| 11 | void print_missing_facilities(void); | 11 | void print_missing_facilities(void); |
| 12 | unsigned long get_random_base(unsigned long safe_addr); | ||
| 13 | |||
| 14 | extern int kaslr_enabled; | ||
| 12 | 15 | ||
| 13 | unsigned long read_ipl_report(unsigned long safe_offset); | 16 | unsigned long read_ipl_report(unsigned long safe_offset); |
| 14 | 17 | ||
diff --git a/arch/s390/boot/ipl_parm.c b/arch/s390/boot/ipl_parm.c index 96777092fb14..b49bd97d33af 100644 --- a/arch/s390/boot/ipl_parm.c +++ b/arch/s390/boot/ipl_parm.c | |||
| @@ -18,6 +18,8 @@ unsigned long __bootdata(memory_end); | |||
| 18 | int __bootdata(memory_end_set); | 18 | int __bootdata(memory_end_set); |
| 19 | int __bootdata(noexec_disabled); | 19 | int __bootdata(noexec_disabled); |
| 20 | 20 | ||
| 21 | int kaslr_enabled __section(.data); | ||
| 22 | |||
| 21 | static inline int __diag308(unsigned long subcode, void *addr) | 23 | static inline int __diag308(unsigned long subcode, void *addr) |
| 22 | { | 24 | { |
| 23 | register unsigned long _addr asm("0") = (unsigned long)addr; | 25 | register unsigned long _addr asm("0") = (unsigned long)addr; |
| @@ -214,6 +216,7 @@ void parse_boot_command_line(void) | |||
| 214 | char *args; | 216 | char *args; |
| 215 | int rc; | 217 | int rc; |
| 216 | 218 | ||
| 219 | kaslr_enabled = IS_ENABLED(CONFIG_RANDOMIZE_BASE); | ||
| 217 | args = strcpy(command_line_buf, early_command_line); | 220 | args = strcpy(command_line_buf, early_command_line); |
| 218 | while (*args) { | 221 | while (*args) { |
| 219 | args = next_arg(args, ¶m, &val); | 222 | args = next_arg(args, ¶m, &val); |
| @@ -231,15 +234,21 @@ void parse_boot_command_line(void) | |||
| 231 | 234 | ||
| 232 | if (!strcmp(param, "facilities")) | 235 | if (!strcmp(param, "facilities")) |
| 233 | modify_fac_list(val); | 236 | modify_fac_list(val); |
| 237 | |||
| 238 | if (!strcmp(param, "nokaslr")) | ||
| 239 | kaslr_enabled = 0; | ||
| 234 | } | 240 | } |
| 235 | } | 241 | } |
| 236 | 242 | ||
| 237 | void setup_memory_end(void) | 243 | void setup_memory_end(void) |
| 238 | { | 244 | { |
| 239 | #ifdef CONFIG_CRASH_DUMP | 245 | #ifdef CONFIG_CRASH_DUMP |
| 240 | if (!OLDMEM_BASE && ipl_block_valid && | 246 | if (OLDMEM_BASE) { |
| 241 | ipl_block.pb0_hdr.pbt == IPL_PBT_FCP && | 247 | kaslr_enabled = 0; |
| 242 | ipl_block.fcp.opt == IPL_PB0_FCP_OPT_DUMP) { | 248 | } else if (ipl_block_valid && |
| 249 | ipl_block.pb0_hdr.pbt == IPL_PBT_FCP && | ||
| 250 | ipl_block.fcp.opt == IPL_PB0_FCP_OPT_DUMP) { | ||
| 251 | kaslr_enabled = 0; | ||
| 243 | if (!sclp_early_get_hsa_size(&memory_end) && memory_end) | 252 | if (!sclp_early_get_hsa_size(&memory_end) && memory_end) |
| 244 | memory_end_set = 1; | 253 | memory_end_set = 1; |
| 245 | } | 254 | } |
diff --git a/arch/s390/boot/kaslr.c b/arch/s390/boot/kaslr.c new file mode 100644 index 000000000000..3bdd8132e56b --- /dev/null +++ b/arch/s390/boot/kaslr.c | |||
| @@ -0,0 +1,144 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 2 | /* | ||
| 3 | * Copyright IBM Corp. 2019 | ||
| 4 | */ | ||
| 5 | #include <asm/mem_detect.h> | ||
| 6 | #include <asm/cpacf.h> | ||
| 7 | #include <asm/timex.h> | ||
| 8 | #include <asm/sclp.h> | ||
| 9 | #include "compressed/decompressor.h" | ||
| 10 | |||
| 11 | #define PRNG_MODE_TDES 1 | ||
| 12 | #define PRNG_MODE_SHA512 2 | ||
| 13 | #define PRNG_MODE_TRNG 3 | ||
| 14 | |||
| 15 | struct prno_parm { | ||
| 16 | u32 res; | ||
| 17 | u32 reseed_counter; | ||
| 18 | u64 stream_bytes; | ||
| 19 | u8 V[112]; | ||
| 20 | u8 C[112]; | ||
| 21 | }; | ||
| 22 | |||
| 23 | struct prng_parm { | ||
| 24 | u8 parm_block[32]; | ||
| 25 | u32 reseed_counter; | ||
| 26 | u64 byte_counter; | ||
| 27 | }; | ||
| 28 | |||
| 29 | static int check_prng(void) | ||
| 30 | { | ||
| 31 | if (!cpacf_query_func(CPACF_KMC, CPACF_KMC_PRNG)) { | ||
| 32 | sclp_early_printk("KASLR disabled: CPU has no PRNG\n"); | ||
| 33 | return 0; | ||
| 34 | } | ||
| 35 | if (cpacf_query_func(CPACF_PRNO, CPACF_PRNO_TRNG)) | ||
| 36 | return PRNG_MODE_TRNG; | ||
| 37 | if (cpacf_query_func(CPACF_PRNO, CPACF_PRNO_SHA512_DRNG_GEN)) | ||
| 38 | return PRNG_MODE_SHA512; | ||
| 39 | else | ||
| 40 | return PRNG_MODE_TDES; | ||
| 41 | } | ||
| 42 | |||
| 43 | static unsigned long get_random(unsigned long limit) | ||
| 44 | { | ||
| 45 | struct prng_parm prng = { | ||
| 46 | /* initial parameter block for tdes mode, copied from libica */ | ||
| 47 | .parm_block = { | ||
| 48 | 0x0F, 0x2B, 0x8E, 0x63, 0x8C, 0x8E, 0xD2, 0x52, | ||
| 49 | 0x64, 0xB7, 0xA0, 0x7B, 0x75, 0x28, 0xB8, 0xF4, | ||
| 50 | 0x75, 0x5F, 0xD2, 0xA6, 0x8D, 0x97, 0x11, 0xFF, | ||
| 51 | 0x49, 0xD8, 0x23, 0xF3, 0x7E, 0x21, 0xEC, 0xA0 | ||
| 52 | }, | ||
| 53 | }; | ||
| 54 | unsigned long seed, random; | ||
| 55 | struct prno_parm prno; | ||
| 56 | __u64 entropy[4]; | ||
| 57 | int mode, i; | ||
| 58 | |||
| 59 | mode = check_prng(); | ||
| 60 | seed = get_tod_clock_fast(); | ||
| 61 | switch (mode) { | ||
| 62 | case PRNG_MODE_TRNG: | ||
| 63 | cpacf_trng(NULL, 0, (u8 *) &random, sizeof(random)); | ||
| 64 | break; | ||
| 65 | case PRNG_MODE_SHA512: | ||
| 66 | cpacf_prno(CPACF_PRNO_SHA512_DRNG_SEED, &prno, NULL, 0, | ||
| 67 | (u8 *) &seed, sizeof(seed)); | ||
| 68 | cpacf_prno(CPACF_PRNO_SHA512_DRNG_GEN, &prno, (u8 *) &random, | ||
| 69 | sizeof(random), NULL, 0); | ||
| 70 | break; | ||
| 71 | case PRNG_MODE_TDES: | ||
| 72 | /* add entropy */ | ||
| 73 | *(unsigned long *) prng.parm_block ^= seed; | ||
| 74 | for (i = 0; i < 16; i++) { | ||
| 75 | cpacf_kmc(CPACF_KMC_PRNG, prng.parm_block, | ||
| 76 | (char *) entropy, (char *) entropy, | ||
| 77 | sizeof(entropy)); | ||
| 78 | memcpy(prng.parm_block, entropy, sizeof(entropy)); | ||
| 79 | } | ||
| 80 | random = seed; | ||
| 81 | cpacf_kmc(CPACF_KMC_PRNG, prng.parm_block, (u8 *) &random, | ||
| 82 | (u8 *) &random, sizeof(random)); | ||
| 83 | break; | ||
| 84 | default: | ||
| 85 | random = 0; | ||
| 86 | } | ||
| 87 | return random % limit; | ||
| 88 | } | ||
| 89 | |||
| 90 | unsigned long get_random_base(unsigned long safe_addr) | ||
| 91 | { | ||
| 92 | unsigned long base, start, end, kernel_size; | ||
| 93 | unsigned long block_sum, offset; | ||
| 94 | int i; | ||
| 95 | |||
| 96 | if (IS_ENABLED(CONFIG_BLK_DEV_INITRD) && INITRD_START && INITRD_SIZE) { | ||
| 97 | if (safe_addr < INITRD_START + INITRD_SIZE) | ||
| 98 | safe_addr = INITRD_START + INITRD_SIZE; | ||
| 99 | } | ||
| 100 | safe_addr = ALIGN(safe_addr, THREAD_SIZE); | ||
| 101 | |||
| 102 | kernel_size = vmlinux.image_size + vmlinux.bss_size; | ||
| 103 | block_sum = 0; | ||
| 104 | for_each_mem_detect_block(i, &start, &end) { | ||
| 105 | if (memory_end_set) { | ||
| 106 | if (start >= memory_end) | ||
| 107 | break; | ||
| 108 | if (end > memory_end) | ||
| 109 | end = memory_end; | ||
| 110 | } | ||
| 111 | if (end - start < kernel_size) | ||
| 112 | continue; | ||
| 113 | block_sum += end - start - kernel_size; | ||
| 114 | } | ||
| 115 | if (!block_sum) { | ||
| 116 | sclp_early_printk("KASLR disabled: not enough memory\n"); | ||
| 117 | return 0; | ||
| 118 | } | ||
| 119 | |||
| 120 | base = get_random(block_sum); | ||
| 121 | if (base == 0) | ||
| 122 | return 0; | ||
| 123 | if (base < safe_addr) | ||
| 124 | base = safe_addr; | ||
| 125 | block_sum = offset = 0; | ||
| 126 | for_each_mem_detect_block(i, &start, &end) { | ||
| 127 | if (memory_end_set) { | ||
| 128 | if (start >= memory_end) | ||
| 129 | break; | ||
| 130 | if (end > memory_end) | ||
| 131 | end = memory_end; | ||
| 132 | } | ||
| 133 | if (end - start < kernel_size) | ||
| 134 | continue; | ||
| 135 | block_sum += end - start - kernel_size; | ||
| 136 | if (base <= block_sum) { | ||
| 137 | base = start + base - offset; | ||
| 138 | base = ALIGN_DOWN(base, THREAD_SIZE); | ||
| 139 | break; | ||
| 140 | } | ||
| 141 | offset = block_sum; | ||
| 142 | } | ||
| 143 | return base; | ||
| 144 | } | ||
diff --git a/arch/s390/boot/startup.c b/arch/s390/boot/startup.c index e3f339d248ce..4401e992bda1 100644 --- a/arch/s390/boot/startup.c +++ b/arch/s390/boot/startup.c | |||
| @@ -12,6 +12,7 @@ | |||
| 12 | 12 | ||
| 13 | extern char __boot_data_start[], __boot_data_end[]; | 13 | extern char __boot_data_start[], __boot_data_end[]; |
| 14 | extern char __boot_data_preserved_start[], __boot_data_preserved_end[]; | 14 | extern char __boot_data_preserved_start[], __boot_data_preserved_end[]; |
| 15 | unsigned long __bootdata_preserved(__kaslr_offset); | ||
| 15 | 16 | ||
| 16 | /* | 17 | /* |
| 17 | * Some code and data needs to stay below 2 GB, even when the kernel would be | 18 | * Some code and data needs to stay below 2 GB, even when the kernel would be |
| @@ -113,6 +114,7 @@ static void handle_relocs(unsigned long offset) | |||
| 113 | 114 | ||
| 114 | void startup_kernel(void) | 115 | void startup_kernel(void) |
| 115 | { | 116 | { |
| 117 | unsigned long random_lma; | ||
| 116 | unsigned long safe_addr; | 118 | unsigned long safe_addr; |
| 117 | void *img; | 119 | void *img; |
| 118 | 120 | ||
| @@ -126,12 +128,37 @@ void startup_kernel(void) | |||
| 126 | parse_boot_command_line(); | 128 | parse_boot_command_line(); |
| 127 | setup_memory_end(); | 129 | setup_memory_end(); |
| 128 | detect_memory(); | 130 | detect_memory(); |
| 131 | |||
| 132 | random_lma = __kaslr_offset = 0; | ||
| 133 | if (IS_ENABLED(CONFIG_RANDOMIZE_BASE) && kaslr_enabled) { | ||
| 134 | random_lma = get_random_base(safe_addr); | ||
| 135 | if (random_lma) { | ||
| 136 | __kaslr_offset = random_lma - vmlinux.default_lma; | ||
| 137 | img = (void *)vmlinux.default_lma; | ||
| 138 | vmlinux.default_lma += __kaslr_offset; | ||
| 139 | vmlinux.entry += __kaslr_offset; | ||
| 140 | vmlinux.bootdata_off += __kaslr_offset; | ||
| 141 | vmlinux.bootdata_preserved_off += __kaslr_offset; | ||
| 142 | vmlinux.rela_dyn_start += __kaslr_offset; | ||
| 143 | vmlinux.rela_dyn_end += __kaslr_offset; | ||
| 144 | vmlinux.dynsym_start += __kaslr_offset; | ||
| 145 | } | ||
| 146 | } | ||
| 147 | |||
| 129 | if (!IS_ENABLED(CONFIG_KERNEL_UNCOMPRESSED)) { | 148 | if (!IS_ENABLED(CONFIG_KERNEL_UNCOMPRESSED)) { |
| 130 | img = decompress_kernel(); | 149 | img = decompress_kernel(); |
| 131 | memmove((void *)vmlinux.default_lma, img, vmlinux.image_size); | 150 | memmove((void *)vmlinux.default_lma, img, vmlinux.image_size); |
| 132 | } | 151 | } else if (__kaslr_offset) |
| 152 | memcpy((void *)vmlinux.default_lma, img, vmlinux.image_size); | ||
| 153 | |||
| 133 | copy_bootdata(); | 154 | copy_bootdata(); |
| 134 | if (IS_ENABLED(CONFIG_RELOCATABLE)) | 155 | if (IS_ENABLED(CONFIG_RELOCATABLE)) |
| 135 | handle_relocs(0); | 156 | handle_relocs(__kaslr_offset); |
| 157 | |||
| 158 | if (__kaslr_offset) { | ||
| 159 | /* Clear non-relocated kernel */ | ||
| 160 | if (IS_ENABLED(CONFIG_KERNEL_UNCOMPRESSED)) | ||
| 161 | memset(img, 0, vmlinux.image_size); | ||
| 162 | } | ||
| 136 | vmlinux.entry(); | 163 | vmlinux.entry(); |
| 137 | } | 164 | } |
diff --git a/arch/s390/include/asm/setup.h b/arch/s390/include/asm/setup.h index d202756d6291..925889d360c1 100644 --- a/arch/s390/include/asm/setup.h +++ b/arch/s390/include/asm/setup.h | |||
| @@ -147,6 +147,12 @@ extern void (*_machine_restart)(char *command); | |||
| 147 | extern void (*_machine_halt)(void); | 147 | extern void (*_machine_halt)(void); |
| 148 | extern void (*_machine_power_off)(void); | 148 | extern void (*_machine_power_off)(void); |
| 149 | 149 | ||
| 150 | extern unsigned long __kaslr_offset; | ||
| 151 | static inline unsigned long kaslr_offset(void) | ||
| 152 | { | ||
| 153 | return __kaslr_offset; | ||
| 154 | } | ||
| 155 | |||
| 150 | #else /* __ASSEMBLY__ */ | 156 | #else /* __ASSEMBLY__ */ |
| 151 | 157 | ||
| 152 | #define IPL_DEVICE (IPL_DEVICE_OFFSET) | 158 | #define IPL_DEVICE (IPL_DEVICE_OFFSET) |
diff --git a/arch/s390/kernel/machine_kexec.c b/arch/s390/kernel/machine_kexec.c index 4b998d639c32..e2ba7b7f574e 100644 --- a/arch/s390/kernel/machine_kexec.c +++ b/arch/s390/kernel/machine_kexec.c | |||
| @@ -255,6 +255,7 @@ void arch_crash_save_vmcoreinfo(void) | |||
| 255 | mem_assign_absolute(S390_lowcore.vmcore_info, paddr_vmcoreinfo_note()); | 255 | mem_assign_absolute(S390_lowcore.vmcore_info, paddr_vmcoreinfo_note()); |
| 256 | vmcoreinfo_append_str("SDMA=%lx\n", __sdma); | 256 | vmcoreinfo_append_str("SDMA=%lx\n", __sdma); |
| 257 | vmcoreinfo_append_str("EDMA=%lx\n", __edma); | 257 | vmcoreinfo_append_str("EDMA=%lx\n", __edma); |
| 258 | vmcoreinfo_append_str("KERNELOFFSET=%lx\n", kaslr_offset()); | ||
| 258 | } | 259 | } |
| 259 | 260 | ||
| 260 | void machine_shutdown(void) | 261 | void machine_shutdown(void) |
diff --git a/arch/s390/kernel/setup.c b/arch/s390/kernel/setup.c index 4ccaf5ed96ee..64e4bc9dd130 100644 --- a/arch/s390/kernel/setup.c +++ b/arch/s390/kernel/setup.c | |||
| @@ -108,6 +108,7 @@ unsigned long __bootdata_preserved(__stext_dma); | |||
| 108 | unsigned long __bootdata_preserved(__etext_dma); | 108 | unsigned long __bootdata_preserved(__etext_dma); |
| 109 | unsigned long __bootdata_preserved(__sdma); | 109 | unsigned long __bootdata_preserved(__sdma); |
| 110 | unsigned long __bootdata_preserved(__edma); | 110 | unsigned long __bootdata_preserved(__edma); |
| 111 | unsigned long __bootdata_preserved(__kaslr_offset); | ||
| 111 | 112 | ||
| 112 | unsigned long VMALLOC_START; | 113 | unsigned long VMALLOC_START; |
| 113 | EXPORT_SYMBOL(VMALLOC_START); | 114 | EXPORT_SYMBOL(VMALLOC_START); |
