xref: /openbmc/linux/arch/s390/boot/kaslr.c (revision b2d24b97)
1b2d24b97SGerald Schaefer // SPDX-License-Identifier: GPL-2.0
2b2d24b97SGerald Schaefer /*
3b2d24b97SGerald Schaefer  * Copyright IBM Corp. 2019
4b2d24b97SGerald Schaefer  */
5b2d24b97SGerald Schaefer #include <asm/mem_detect.h>
6b2d24b97SGerald Schaefer #include <asm/cpacf.h>
7b2d24b97SGerald Schaefer #include <asm/timex.h>
8b2d24b97SGerald Schaefer #include <asm/sclp.h>
9b2d24b97SGerald Schaefer #include "compressed/decompressor.h"
10b2d24b97SGerald Schaefer 
11b2d24b97SGerald Schaefer #define PRNG_MODE_TDES	 1
12b2d24b97SGerald Schaefer #define PRNG_MODE_SHA512 2
13b2d24b97SGerald Schaefer #define PRNG_MODE_TRNG	 3
14b2d24b97SGerald Schaefer 
15b2d24b97SGerald Schaefer struct prno_parm {
16b2d24b97SGerald Schaefer 	u32 res;
17b2d24b97SGerald Schaefer 	u32 reseed_counter;
18b2d24b97SGerald Schaefer 	u64 stream_bytes;
19b2d24b97SGerald Schaefer 	u8  V[112];
20b2d24b97SGerald Schaefer 	u8  C[112];
21b2d24b97SGerald Schaefer };
22b2d24b97SGerald Schaefer 
23b2d24b97SGerald Schaefer struct prng_parm {
24b2d24b97SGerald Schaefer 	u8  parm_block[32];
25b2d24b97SGerald Schaefer 	u32 reseed_counter;
26b2d24b97SGerald Schaefer 	u64 byte_counter;
27b2d24b97SGerald Schaefer };
28b2d24b97SGerald Schaefer 
29b2d24b97SGerald Schaefer static int check_prng(void)
30b2d24b97SGerald Schaefer {
31b2d24b97SGerald Schaefer 	if (!cpacf_query_func(CPACF_KMC, CPACF_KMC_PRNG)) {
32b2d24b97SGerald Schaefer 		sclp_early_printk("KASLR disabled: CPU has no PRNG\n");
33b2d24b97SGerald Schaefer 		return 0;
34b2d24b97SGerald Schaefer 	}
35b2d24b97SGerald Schaefer 	if (cpacf_query_func(CPACF_PRNO, CPACF_PRNO_TRNG))
36b2d24b97SGerald Schaefer 		return PRNG_MODE_TRNG;
37b2d24b97SGerald Schaefer 	if (cpacf_query_func(CPACF_PRNO, CPACF_PRNO_SHA512_DRNG_GEN))
38b2d24b97SGerald Schaefer 		return PRNG_MODE_SHA512;
39b2d24b97SGerald Schaefer 	else
40b2d24b97SGerald Schaefer 		return PRNG_MODE_TDES;
41b2d24b97SGerald Schaefer }
42b2d24b97SGerald Schaefer 
43b2d24b97SGerald Schaefer static unsigned long get_random(unsigned long limit)
44b2d24b97SGerald Schaefer {
45b2d24b97SGerald Schaefer 	struct prng_parm prng = {
46b2d24b97SGerald Schaefer 		/* initial parameter block for tdes mode, copied from libica */
47b2d24b97SGerald Schaefer 		.parm_block = {
48b2d24b97SGerald Schaefer 			0x0F, 0x2B, 0x8E, 0x63, 0x8C, 0x8E, 0xD2, 0x52,
49b2d24b97SGerald Schaefer 			0x64, 0xB7, 0xA0, 0x7B, 0x75, 0x28, 0xB8, 0xF4,
50b2d24b97SGerald Schaefer 			0x75, 0x5F, 0xD2, 0xA6, 0x8D, 0x97, 0x11, 0xFF,
51b2d24b97SGerald Schaefer 			0x49, 0xD8, 0x23, 0xF3, 0x7E, 0x21, 0xEC, 0xA0
52b2d24b97SGerald Schaefer 		},
53b2d24b97SGerald Schaefer 	};
54b2d24b97SGerald Schaefer 	unsigned long seed, random;
55b2d24b97SGerald Schaefer 	struct prno_parm prno;
56b2d24b97SGerald Schaefer 	__u64 entropy[4];
57b2d24b97SGerald Schaefer 	int mode, i;
58b2d24b97SGerald Schaefer 
59b2d24b97SGerald Schaefer 	mode = check_prng();
60b2d24b97SGerald Schaefer 	seed = get_tod_clock_fast();
61b2d24b97SGerald Schaefer 	switch (mode) {
62b2d24b97SGerald Schaefer 	case PRNG_MODE_TRNG:
63b2d24b97SGerald Schaefer 		cpacf_trng(NULL, 0, (u8 *) &random, sizeof(random));
64b2d24b97SGerald Schaefer 		break;
65b2d24b97SGerald Schaefer 	case PRNG_MODE_SHA512:
66b2d24b97SGerald Schaefer 		cpacf_prno(CPACF_PRNO_SHA512_DRNG_SEED, &prno, NULL, 0,
67b2d24b97SGerald Schaefer 			   (u8 *) &seed, sizeof(seed));
68b2d24b97SGerald Schaefer 		cpacf_prno(CPACF_PRNO_SHA512_DRNG_GEN, &prno, (u8 *) &random,
69b2d24b97SGerald Schaefer 			   sizeof(random), NULL, 0);
70b2d24b97SGerald Schaefer 		break;
71b2d24b97SGerald Schaefer 	case PRNG_MODE_TDES:
72b2d24b97SGerald Schaefer 		/* add entropy */
73b2d24b97SGerald Schaefer 		*(unsigned long *) prng.parm_block ^= seed;
74b2d24b97SGerald Schaefer 		for (i = 0; i < 16; i++) {
75b2d24b97SGerald Schaefer 			cpacf_kmc(CPACF_KMC_PRNG, prng.parm_block,
76b2d24b97SGerald Schaefer 				  (char *) entropy, (char *) entropy,
77b2d24b97SGerald Schaefer 				  sizeof(entropy));
78b2d24b97SGerald Schaefer 			memcpy(prng.parm_block, entropy, sizeof(entropy));
79b2d24b97SGerald Schaefer 		}
80b2d24b97SGerald Schaefer 		random = seed;
81b2d24b97SGerald Schaefer 		cpacf_kmc(CPACF_KMC_PRNG, prng.parm_block, (u8 *) &random,
82b2d24b97SGerald Schaefer 			  (u8 *) &random, sizeof(random));
83b2d24b97SGerald Schaefer 		break;
84b2d24b97SGerald Schaefer 	default:
85b2d24b97SGerald Schaefer 		random = 0;
86b2d24b97SGerald Schaefer 	}
87b2d24b97SGerald Schaefer 	return random % limit;
88b2d24b97SGerald Schaefer }
89b2d24b97SGerald Schaefer 
90b2d24b97SGerald Schaefer unsigned long get_random_base(unsigned long safe_addr)
91b2d24b97SGerald Schaefer {
92b2d24b97SGerald Schaefer 	unsigned long base, start, end, kernel_size;
93b2d24b97SGerald Schaefer 	unsigned long block_sum, offset;
94b2d24b97SGerald Schaefer 	int i;
95b2d24b97SGerald Schaefer 
96b2d24b97SGerald Schaefer 	if (IS_ENABLED(CONFIG_BLK_DEV_INITRD) && INITRD_START && INITRD_SIZE) {
97b2d24b97SGerald Schaefer 		if (safe_addr < INITRD_START + INITRD_SIZE)
98b2d24b97SGerald Schaefer 			safe_addr = INITRD_START + INITRD_SIZE;
99b2d24b97SGerald Schaefer 	}
100b2d24b97SGerald Schaefer 	safe_addr = ALIGN(safe_addr, THREAD_SIZE);
101b2d24b97SGerald Schaefer 
102b2d24b97SGerald Schaefer 	kernel_size = vmlinux.image_size + vmlinux.bss_size;
103b2d24b97SGerald Schaefer 	block_sum = 0;
104b2d24b97SGerald Schaefer 	for_each_mem_detect_block(i, &start, &end) {
105b2d24b97SGerald Schaefer 		if (memory_end_set) {
106b2d24b97SGerald Schaefer 			if (start >= memory_end)
107b2d24b97SGerald Schaefer 				break;
108b2d24b97SGerald Schaefer 			if (end > memory_end)
109b2d24b97SGerald Schaefer 				end = memory_end;
110b2d24b97SGerald Schaefer 		}
111b2d24b97SGerald Schaefer 		if (end - start < kernel_size)
112b2d24b97SGerald Schaefer 			continue;
113b2d24b97SGerald Schaefer 		block_sum += end - start - kernel_size;
114b2d24b97SGerald Schaefer 	}
115b2d24b97SGerald Schaefer 	if (!block_sum) {
116b2d24b97SGerald Schaefer 		sclp_early_printk("KASLR disabled: not enough memory\n");
117b2d24b97SGerald Schaefer 		return 0;
118b2d24b97SGerald Schaefer 	}
119b2d24b97SGerald Schaefer 
120b2d24b97SGerald Schaefer 	base = get_random(block_sum);
121b2d24b97SGerald Schaefer 	if (base == 0)
122b2d24b97SGerald Schaefer 		return 0;
123b2d24b97SGerald Schaefer 	if (base < safe_addr)
124b2d24b97SGerald Schaefer 		base = safe_addr;
125b2d24b97SGerald Schaefer 	block_sum = offset = 0;
126b2d24b97SGerald Schaefer 	for_each_mem_detect_block(i, &start, &end) {
127b2d24b97SGerald Schaefer 		if (memory_end_set) {
128b2d24b97SGerald Schaefer 			if (start >= memory_end)
129b2d24b97SGerald Schaefer 				break;
130b2d24b97SGerald Schaefer 			if (end > memory_end)
131b2d24b97SGerald Schaefer 				end = memory_end;
132b2d24b97SGerald Schaefer 		}
133b2d24b97SGerald Schaefer 		if (end - start < kernel_size)
134b2d24b97SGerald Schaefer 			continue;
135b2d24b97SGerald Schaefer 		block_sum += end - start - kernel_size;
136b2d24b97SGerald Schaefer 		if (base <= block_sum) {
137b2d24b97SGerald Schaefer 			base = start + base - offset;
138b2d24b97SGerald Schaefer 			base = ALIGN_DOWN(base, THREAD_SIZE);
139b2d24b97SGerald Schaefer 			break;
140b2d24b97SGerald Schaefer 		}
141b2d24b97SGerald Schaefer 		offset = block_sum;
142b2d24b97SGerald Schaefer 	}
143b2d24b97SGerald Schaefer 	return base;
144b2d24b97SGerald Schaefer }
145