linux/arch/s390/boot/kaslr.c
<<
>>
Prefs
   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
  16struct prno_parm {
  17        u32 res;
  18        u32 reseed_counter;
  19        u64 stream_bytes;
  20        u8  V[112];
  21        u8  C[112];
  22};
  23
  24struct prng_parm {
  25        u8  parm_block[32];
  26        u32 reseed_counter;
  27        u64 byte_counter;
  28};
  29
  30static 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
  44static 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
  91unsigned 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}
 146