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