18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright IBM Corp. 2019 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci#include <linux/pgtable.h> 68c2ecf20Sopenharmony_ci#include <asm/mem_detect.h> 78c2ecf20Sopenharmony_ci#include <asm/cpacf.h> 88c2ecf20Sopenharmony_ci#include <asm/timex.h> 98c2ecf20Sopenharmony_ci#include <asm/sclp.h> 108c2ecf20Sopenharmony_ci#include "compressed/decompressor.h" 118c2ecf20Sopenharmony_ci#include "boot.h" 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#define PRNG_MODE_TDES 1 148c2ecf20Sopenharmony_ci#define PRNG_MODE_SHA512 2 158c2ecf20Sopenharmony_ci#define PRNG_MODE_TRNG 3 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_cistruct prno_parm { 188c2ecf20Sopenharmony_ci u32 res; 198c2ecf20Sopenharmony_ci u32 reseed_counter; 208c2ecf20Sopenharmony_ci u64 stream_bytes; 218c2ecf20Sopenharmony_ci u8 V[112]; 228c2ecf20Sopenharmony_ci u8 C[112]; 238c2ecf20Sopenharmony_ci}; 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_cistruct prng_parm { 268c2ecf20Sopenharmony_ci u8 parm_block[32]; 278c2ecf20Sopenharmony_ci u32 reseed_counter; 288c2ecf20Sopenharmony_ci u64 byte_counter; 298c2ecf20Sopenharmony_ci}; 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cistatic int check_prng(void) 328c2ecf20Sopenharmony_ci{ 338c2ecf20Sopenharmony_ci if (!cpacf_query_func(CPACF_KMC, CPACF_KMC_PRNG)) { 348c2ecf20Sopenharmony_ci sclp_early_printk("KASLR disabled: CPU has no PRNG\n"); 358c2ecf20Sopenharmony_ci return 0; 368c2ecf20Sopenharmony_ci } 378c2ecf20Sopenharmony_ci if (cpacf_query_func(CPACF_PRNO, CPACF_PRNO_TRNG)) 388c2ecf20Sopenharmony_ci return PRNG_MODE_TRNG; 398c2ecf20Sopenharmony_ci if (cpacf_query_func(CPACF_PRNO, CPACF_PRNO_SHA512_DRNG_GEN)) 408c2ecf20Sopenharmony_ci return PRNG_MODE_SHA512; 418c2ecf20Sopenharmony_ci else 428c2ecf20Sopenharmony_ci return PRNG_MODE_TDES; 438c2ecf20Sopenharmony_ci} 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_cistatic int get_random(unsigned long limit, unsigned long *value) 468c2ecf20Sopenharmony_ci{ 478c2ecf20Sopenharmony_ci struct prng_parm prng = { 488c2ecf20Sopenharmony_ci /* initial parameter block for tdes mode, copied from libica */ 498c2ecf20Sopenharmony_ci .parm_block = { 508c2ecf20Sopenharmony_ci 0x0F, 0x2B, 0x8E, 0x63, 0x8C, 0x8E, 0xD2, 0x52, 518c2ecf20Sopenharmony_ci 0x64, 0xB7, 0xA0, 0x7B, 0x75, 0x28, 0xB8, 0xF4, 528c2ecf20Sopenharmony_ci 0x75, 0x5F, 0xD2, 0xA6, 0x8D, 0x97, 0x11, 0xFF, 538c2ecf20Sopenharmony_ci 0x49, 0xD8, 0x23, 0xF3, 0x7E, 0x21, 0xEC, 0xA0 548c2ecf20Sopenharmony_ci }, 558c2ecf20Sopenharmony_ci }; 568c2ecf20Sopenharmony_ci unsigned long seed, random; 578c2ecf20Sopenharmony_ci struct prno_parm prno; 588c2ecf20Sopenharmony_ci __u64 entropy[4]; 598c2ecf20Sopenharmony_ci int mode, i; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci mode = check_prng(); 628c2ecf20Sopenharmony_ci seed = get_tod_clock_fast(); 638c2ecf20Sopenharmony_ci switch (mode) { 648c2ecf20Sopenharmony_ci case PRNG_MODE_TRNG: 658c2ecf20Sopenharmony_ci cpacf_trng(NULL, 0, (u8 *) &random, sizeof(random)); 668c2ecf20Sopenharmony_ci break; 678c2ecf20Sopenharmony_ci case PRNG_MODE_SHA512: 688c2ecf20Sopenharmony_ci cpacf_prno(CPACF_PRNO_SHA512_DRNG_SEED, &prno, NULL, 0, 698c2ecf20Sopenharmony_ci (u8 *) &seed, sizeof(seed)); 708c2ecf20Sopenharmony_ci cpacf_prno(CPACF_PRNO_SHA512_DRNG_GEN, &prno, (u8 *) &random, 718c2ecf20Sopenharmony_ci sizeof(random), NULL, 0); 728c2ecf20Sopenharmony_ci break; 738c2ecf20Sopenharmony_ci case PRNG_MODE_TDES: 748c2ecf20Sopenharmony_ci /* add entropy */ 758c2ecf20Sopenharmony_ci *(unsigned long *) prng.parm_block ^= seed; 768c2ecf20Sopenharmony_ci for (i = 0; i < 16; i++) { 778c2ecf20Sopenharmony_ci cpacf_kmc(CPACF_KMC_PRNG, prng.parm_block, 788c2ecf20Sopenharmony_ci (u8 *) entropy, (u8 *) entropy, 798c2ecf20Sopenharmony_ci sizeof(entropy)); 808c2ecf20Sopenharmony_ci memcpy(prng.parm_block, entropy, sizeof(entropy)); 818c2ecf20Sopenharmony_ci } 828c2ecf20Sopenharmony_ci random = seed; 838c2ecf20Sopenharmony_ci cpacf_kmc(CPACF_KMC_PRNG, prng.parm_block, (u8 *) &random, 848c2ecf20Sopenharmony_ci (u8 *) &random, sizeof(random)); 858c2ecf20Sopenharmony_ci break; 868c2ecf20Sopenharmony_ci default: 878c2ecf20Sopenharmony_ci return -1; 888c2ecf20Sopenharmony_ci } 898c2ecf20Sopenharmony_ci *value = random % limit; 908c2ecf20Sopenharmony_ci return 0; 918c2ecf20Sopenharmony_ci} 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci/* 948c2ecf20Sopenharmony_ci * To randomize kernel base address we have to consider several facts: 958c2ecf20Sopenharmony_ci * 1. physical online memory might not be continuous and have holes. mem_detect 968c2ecf20Sopenharmony_ci * info contains list of online memory ranges we should consider. 978c2ecf20Sopenharmony_ci * 2. we have several memory regions which are occupied and we should not 988c2ecf20Sopenharmony_ci * overlap and destroy them. Currently safe_addr tells us the border below 998c2ecf20Sopenharmony_ci * which all those occupied regions are. We are safe to use anything above 1008c2ecf20Sopenharmony_ci * safe_addr. 1018c2ecf20Sopenharmony_ci * 3. the upper limit might apply as well, even if memory above that limit is 1028c2ecf20Sopenharmony_ci * online. Currently those limitations are: 1038c2ecf20Sopenharmony_ci * 3.1. Limit set by "mem=" kernel command line option 1048c2ecf20Sopenharmony_ci * 3.2. memory reserved at the end for kasan initialization. 1058c2ecf20Sopenharmony_ci * 4. kernel base address must be aligned to THREAD_SIZE (kernel stack size). 1068c2ecf20Sopenharmony_ci * Which is required for CONFIG_CHECK_STACK. Currently THREAD_SIZE is 4 pages 1078c2ecf20Sopenharmony_ci * (16 pages when the kernel is built with kasan enabled) 1088c2ecf20Sopenharmony_ci * Assumptions: 1098c2ecf20Sopenharmony_ci * 1. kernel size (including .bss size) and upper memory limit are page aligned. 1108c2ecf20Sopenharmony_ci * 2. mem_detect memory region start is THREAD_SIZE aligned / end is PAGE_SIZE 1118c2ecf20Sopenharmony_ci * aligned (in practice memory configurations granularity on z/VM and LPAR 1128c2ecf20Sopenharmony_ci * is 1mb). 1138c2ecf20Sopenharmony_ci * 1148c2ecf20Sopenharmony_ci * To guarantee uniform distribution of kernel base address among all suitable 1158c2ecf20Sopenharmony_ci * addresses we generate random value just once. For that we need to build a 1168c2ecf20Sopenharmony_ci * continuous range in which every value would be suitable. We can build this 1178c2ecf20Sopenharmony_ci * range by simply counting all suitable addresses (let's call them positions) 1188c2ecf20Sopenharmony_ci * which would be valid as kernel base address. To count positions we iterate 1198c2ecf20Sopenharmony_ci * over online memory ranges. For each range which is big enough for the 1208c2ecf20Sopenharmony_ci * kernel image we count all suitable addresses we can put the kernel image at 1218c2ecf20Sopenharmony_ci * that is 1228c2ecf20Sopenharmony_ci * (end - start - kernel_size) / THREAD_SIZE + 1 1238c2ecf20Sopenharmony_ci * Two functions count_valid_kernel_positions and position_to_address help 1248c2ecf20Sopenharmony_ci * to count positions in memory range given and then convert position back 1258c2ecf20Sopenharmony_ci * to address. 1268c2ecf20Sopenharmony_ci */ 1278c2ecf20Sopenharmony_cistatic unsigned long count_valid_kernel_positions(unsigned long kernel_size, 1288c2ecf20Sopenharmony_ci unsigned long _min, 1298c2ecf20Sopenharmony_ci unsigned long _max) 1308c2ecf20Sopenharmony_ci{ 1318c2ecf20Sopenharmony_ci unsigned long start, end, pos = 0; 1328c2ecf20Sopenharmony_ci int i; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci for_each_mem_detect_block(i, &start, &end) { 1358c2ecf20Sopenharmony_ci if (_min >= end) 1368c2ecf20Sopenharmony_ci continue; 1378c2ecf20Sopenharmony_ci if (start >= _max) 1388c2ecf20Sopenharmony_ci break; 1398c2ecf20Sopenharmony_ci start = max(_min, start); 1408c2ecf20Sopenharmony_ci end = min(_max, end); 1418c2ecf20Sopenharmony_ci if (end - start < kernel_size) 1428c2ecf20Sopenharmony_ci continue; 1438c2ecf20Sopenharmony_ci pos += (end - start - kernel_size) / THREAD_SIZE + 1; 1448c2ecf20Sopenharmony_ci } 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci return pos; 1478c2ecf20Sopenharmony_ci} 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_cistatic unsigned long position_to_address(unsigned long pos, unsigned long kernel_size, 1508c2ecf20Sopenharmony_ci unsigned long _min, unsigned long _max) 1518c2ecf20Sopenharmony_ci{ 1528c2ecf20Sopenharmony_ci unsigned long start, end; 1538c2ecf20Sopenharmony_ci int i; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci for_each_mem_detect_block(i, &start, &end) { 1568c2ecf20Sopenharmony_ci if (_min >= end) 1578c2ecf20Sopenharmony_ci continue; 1588c2ecf20Sopenharmony_ci if (start >= _max) 1598c2ecf20Sopenharmony_ci break; 1608c2ecf20Sopenharmony_ci start = max(_min, start); 1618c2ecf20Sopenharmony_ci end = min(_max, end); 1628c2ecf20Sopenharmony_ci if (end - start < kernel_size) 1638c2ecf20Sopenharmony_ci continue; 1648c2ecf20Sopenharmony_ci if ((end - start - kernel_size) / THREAD_SIZE + 1 >= pos) 1658c2ecf20Sopenharmony_ci return start + (pos - 1) * THREAD_SIZE; 1668c2ecf20Sopenharmony_ci pos -= (end - start - kernel_size) / THREAD_SIZE + 1; 1678c2ecf20Sopenharmony_ci } 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci return 0; 1708c2ecf20Sopenharmony_ci} 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ciunsigned long get_random_base(unsigned long safe_addr) 1738c2ecf20Sopenharmony_ci{ 1748c2ecf20Sopenharmony_ci unsigned long memory_limit = get_mem_detect_end(); 1758c2ecf20Sopenharmony_ci unsigned long base_pos, max_pos, kernel_size; 1768c2ecf20Sopenharmony_ci unsigned long kasan_needs; 1778c2ecf20Sopenharmony_ci int i; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci if (memory_end_set) 1808c2ecf20Sopenharmony_ci memory_limit = min(memory_limit, memory_end); 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_BLK_DEV_INITRD) && INITRD_START && INITRD_SIZE) { 1838c2ecf20Sopenharmony_ci if (safe_addr < INITRD_START + INITRD_SIZE) 1848c2ecf20Sopenharmony_ci safe_addr = INITRD_START + INITRD_SIZE; 1858c2ecf20Sopenharmony_ci } 1868c2ecf20Sopenharmony_ci safe_addr = ALIGN(safe_addr, THREAD_SIZE); 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci if ((IS_ENABLED(CONFIG_KASAN))) { 1898c2ecf20Sopenharmony_ci /* 1908c2ecf20Sopenharmony_ci * Estimate kasan memory requirements, which it will reserve 1918c2ecf20Sopenharmony_ci * at the very end of available physical memory. To estimate 1928c2ecf20Sopenharmony_ci * that, we take into account that kasan would require 1938c2ecf20Sopenharmony_ci * 1/8 of available physical memory (for shadow memory) + 1948c2ecf20Sopenharmony_ci * creating page tables for the whole memory + shadow memory 1958c2ecf20Sopenharmony_ci * region (1 + 1/8). To keep page tables estimates simple take 1968c2ecf20Sopenharmony_ci * the double of combined ptes size. 1978c2ecf20Sopenharmony_ci */ 1988c2ecf20Sopenharmony_ci memory_limit = get_mem_detect_end(); 1998c2ecf20Sopenharmony_ci if (memory_end_set && memory_limit > memory_end) 2008c2ecf20Sopenharmony_ci memory_limit = memory_end; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci /* for shadow memory */ 2038c2ecf20Sopenharmony_ci kasan_needs = memory_limit / 8; 2048c2ecf20Sopenharmony_ci /* for paging structures */ 2058c2ecf20Sopenharmony_ci kasan_needs += (memory_limit + kasan_needs) / PAGE_SIZE / 2068c2ecf20Sopenharmony_ci _PAGE_ENTRIES * _PAGE_TABLE_SIZE * 2; 2078c2ecf20Sopenharmony_ci memory_limit -= kasan_needs; 2088c2ecf20Sopenharmony_ci } 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci kernel_size = vmlinux.image_size + vmlinux.bss_size; 2118c2ecf20Sopenharmony_ci if (safe_addr + kernel_size > memory_limit) 2128c2ecf20Sopenharmony_ci return 0; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci max_pos = count_valid_kernel_positions(kernel_size, safe_addr, memory_limit); 2158c2ecf20Sopenharmony_ci if (!max_pos) { 2168c2ecf20Sopenharmony_ci sclp_early_printk("KASLR disabled: not enough memory\n"); 2178c2ecf20Sopenharmony_ci return 0; 2188c2ecf20Sopenharmony_ci } 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci /* we need a value in the range [1, base_pos] inclusive */ 2218c2ecf20Sopenharmony_ci if (get_random(max_pos, &base_pos)) 2228c2ecf20Sopenharmony_ci return 0; 2238c2ecf20Sopenharmony_ci return position_to_address(base_pos + 1, kernel_size, safe_addr, memory_limit); 2248c2ecf20Sopenharmony_ci} 225