162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright IBM Corp. 2019 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci#include <linux/pgtable.h> 662306a36Sopenharmony_ci#include <asm/physmem_info.h> 762306a36Sopenharmony_ci#include <asm/cpacf.h> 862306a36Sopenharmony_ci#include <asm/timex.h> 962306a36Sopenharmony_ci#include <asm/sclp.h> 1062306a36Sopenharmony_ci#include <asm/kasan.h> 1162306a36Sopenharmony_ci#include "decompressor.h" 1262306a36Sopenharmony_ci#include "boot.h" 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#define PRNG_MODE_TDES 1 1562306a36Sopenharmony_ci#define PRNG_MODE_SHA512 2 1662306a36Sopenharmony_ci#define PRNG_MODE_TRNG 3 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_cistruct prno_parm { 1962306a36Sopenharmony_ci u32 res; 2062306a36Sopenharmony_ci u32 reseed_counter; 2162306a36Sopenharmony_ci u64 stream_bytes; 2262306a36Sopenharmony_ci u8 V[112]; 2362306a36Sopenharmony_ci u8 C[112]; 2462306a36Sopenharmony_ci}; 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_cistruct prng_parm { 2762306a36Sopenharmony_ci u8 parm_block[32]; 2862306a36Sopenharmony_ci u32 reseed_counter; 2962306a36Sopenharmony_ci u64 byte_counter; 3062306a36Sopenharmony_ci}; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_cistatic int check_prng(void) 3362306a36Sopenharmony_ci{ 3462306a36Sopenharmony_ci if (!cpacf_query_func(CPACF_KMC, CPACF_KMC_PRNG)) { 3562306a36Sopenharmony_ci sclp_early_printk("KASLR disabled: CPU has no PRNG\n"); 3662306a36Sopenharmony_ci return 0; 3762306a36Sopenharmony_ci } 3862306a36Sopenharmony_ci if (cpacf_query_func(CPACF_PRNO, CPACF_PRNO_TRNG)) 3962306a36Sopenharmony_ci return PRNG_MODE_TRNG; 4062306a36Sopenharmony_ci if (cpacf_query_func(CPACF_PRNO, CPACF_PRNO_SHA512_DRNG_GEN)) 4162306a36Sopenharmony_ci return PRNG_MODE_SHA512; 4262306a36Sopenharmony_ci else 4362306a36Sopenharmony_ci return PRNG_MODE_TDES; 4462306a36Sopenharmony_ci} 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_cistatic int get_random(unsigned long limit, unsigned long *value) 4762306a36Sopenharmony_ci{ 4862306a36Sopenharmony_ci struct prng_parm prng = { 4962306a36Sopenharmony_ci /* initial parameter block for tdes mode, copied from libica */ 5062306a36Sopenharmony_ci .parm_block = { 5162306a36Sopenharmony_ci 0x0F, 0x2B, 0x8E, 0x63, 0x8C, 0x8E, 0xD2, 0x52, 5262306a36Sopenharmony_ci 0x64, 0xB7, 0xA0, 0x7B, 0x75, 0x28, 0xB8, 0xF4, 5362306a36Sopenharmony_ci 0x75, 0x5F, 0xD2, 0xA6, 0x8D, 0x97, 0x11, 0xFF, 5462306a36Sopenharmony_ci 0x49, 0xD8, 0x23, 0xF3, 0x7E, 0x21, 0xEC, 0xA0 5562306a36Sopenharmony_ci }, 5662306a36Sopenharmony_ci }; 5762306a36Sopenharmony_ci unsigned long seed, random; 5862306a36Sopenharmony_ci struct prno_parm prno; 5962306a36Sopenharmony_ci __u64 entropy[4]; 6062306a36Sopenharmony_ci int mode, i; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci mode = check_prng(); 6362306a36Sopenharmony_ci seed = get_tod_clock_fast(); 6462306a36Sopenharmony_ci switch (mode) { 6562306a36Sopenharmony_ci case PRNG_MODE_TRNG: 6662306a36Sopenharmony_ci cpacf_trng(NULL, 0, (u8 *) &random, sizeof(random)); 6762306a36Sopenharmony_ci break; 6862306a36Sopenharmony_ci case PRNG_MODE_SHA512: 6962306a36Sopenharmony_ci cpacf_prno(CPACF_PRNO_SHA512_DRNG_SEED, &prno, NULL, 0, 7062306a36Sopenharmony_ci (u8 *) &seed, sizeof(seed)); 7162306a36Sopenharmony_ci cpacf_prno(CPACF_PRNO_SHA512_DRNG_GEN, &prno, (u8 *) &random, 7262306a36Sopenharmony_ci sizeof(random), NULL, 0); 7362306a36Sopenharmony_ci break; 7462306a36Sopenharmony_ci case PRNG_MODE_TDES: 7562306a36Sopenharmony_ci /* add entropy */ 7662306a36Sopenharmony_ci *(unsigned long *) prng.parm_block ^= seed; 7762306a36Sopenharmony_ci for (i = 0; i < 16; i++) { 7862306a36Sopenharmony_ci cpacf_kmc(CPACF_KMC_PRNG, prng.parm_block, 7962306a36Sopenharmony_ci (u8 *) entropy, (u8 *) entropy, 8062306a36Sopenharmony_ci sizeof(entropy)); 8162306a36Sopenharmony_ci memcpy(prng.parm_block, entropy, sizeof(entropy)); 8262306a36Sopenharmony_ci } 8362306a36Sopenharmony_ci random = seed; 8462306a36Sopenharmony_ci cpacf_kmc(CPACF_KMC_PRNG, prng.parm_block, (u8 *) &random, 8562306a36Sopenharmony_ci (u8 *) &random, sizeof(random)); 8662306a36Sopenharmony_ci break; 8762306a36Sopenharmony_ci default: 8862306a36Sopenharmony_ci return -1; 8962306a36Sopenharmony_ci } 9062306a36Sopenharmony_ci *value = random % limit; 9162306a36Sopenharmony_ci return 0; 9262306a36Sopenharmony_ci} 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_cistatic void sort_reserved_ranges(struct reserved_range *res, unsigned long size) 9562306a36Sopenharmony_ci{ 9662306a36Sopenharmony_ci struct reserved_range tmp; 9762306a36Sopenharmony_ci int i, j; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci for (i = 1; i < size; i++) { 10062306a36Sopenharmony_ci tmp = res[i]; 10162306a36Sopenharmony_ci for (j = i - 1; j >= 0 && res[j].start > tmp.start; j--) 10262306a36Sopenharmony_ci res[j + 1] = res[j]; 10362306a36Sopenharmony_ci res[j + 1] = tmp; 10462306a36Sopenharmony_ci } 10562306a36Sopenharmony_ci} 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_cistatic unsigned long iterate_valid_positions(unsigned long size, unsigned long align, 10862306a36Sopenharmony_ci unsigned long _min, unsigned long _max, 10962306a36Sopenharmony_ci struct reserved_range *res, size_t res_count, 11062306a36Sopenharmony_ci bool pos_count, unsigned long find_pos) 11162306a36Sopenharmony_ci{ 11262306a36Sopenharmony_ci unsigned long start, end, tmp_end, range_pos, pos = 0; 11362306a36Sopenharmony_ci struct reserved_range *res_end = res + res_count; 11462306a36Sopenharmony_ci struct reserved_range *skip_res; 11562306a36Sopenharmony_ci int i; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci align = max(align, 8UL); 11862306a36Sopenharmony_ci _min = round_up(_min, align); 11962306a36Sopenharmony_ci for_each_physmem_usable_range(i, &start, &end) { 12062306a36Sopenharmony_ci if (_min >= end) 12162306a36Sopenharmony_ci continue; 12262306a36Sopenharmony_ci start = round_up(start, align); 12362306a36Sopenharmony_ci if (start >= _max) 12462306a36Sopenharmony_ci break; 12562306a36Sopenharmony_ci start = max(_min, start); 12662306a36Sopenharmony_ci end = min(_max, end); 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci while (start + size <= end) { 12962306a36Sopenharmony_ci /* skip reserved ranges below the start */ 13062306a36Sopenharmony_ci while (res && res->end <= start) { 13162306a36Sopenharmony_ci res++; 13262306a36Sopenharmony_ci if (res >= res_end) 13362306a36Sopenharmony_ci res = NULL; 13462306a36Sopenharmony_ci } 13562306a36Sopenharmony_ci skip_res = NULL; 13662306a36Sopenharmony_ci tmp_end = end; 13762306a36Sopenharmony_ci /* has intersecting reserved range */ 13862306a36Sopenharmony_ci if (res && res->start < end) { 13962306a36Sopenharmony_ci skip_res = res; 14062306a36Sopenharmony_ci tmp_end = res->start; 14162306a36Sopenharmony_ci } 14262306a36Sopenharmony_ci if (start + size <= tmp_end) { 14362306a36Sopenharmony_ci range_pos = (tmp_end - start - size) / align + 1; 14462306a36Sopenharmony_ci if (pos_count) { 14562306a36Sopenharmony_ci pos += range_pos; 14662306a36Sopenharmony_ci } else { 14762306a36Sopenharmony_ci if (range_pos >= find_pos) 14862306a36Sopenharmony_ci return start + (find_pos - 1) * align; 14962306a36Sopenharmony_ci find_pos -= range_pos; 15062306a36Sopenharmony_ci } 15162306a36Sopenharmony_ci } 15262306a36Sopenharmony_ci if (!skip_res) 15362306a36Sopenharmony_ci break; 15462306a36Sopenharmony_ci start = round_up(skip_res->end, align); 15562306a36Sopenharmony_ci } 15662306a36Sopenharmony_ci } 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci return pos_count ? pos : 0; 15962306a36Sopenharmony_ci} 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci/* 16262306a36Sopenharmony_ci * Two types of decompressor memory allocations/reserves are considered 16362306a36Sopenharmony_ci * differently. 16462306a36Sopenharmony_ci * 16562306a36Sopenharmony_ci * "Static" or "single" allocations are done via physmem_alloc_range() and 16662306a36Sopenharmony_ci * physmem_reserve(), and they are listed in physmem_info.reserved[]. Each 16762306a36Sopenharmony_ci * type of "static" allocation can only have one allocation per type and 16862306a36Sopenharmony_ci * cannot have chains. 16962306a36Sopenharmony_ci * 17062306a36Sopenharmony_ci * On the other hand, "dynamic" or "repetitive" allocations are done via 17162306a36Sopenharmony_ci * physmem_alloc_top_down(). These allocations are tightly packed together 17262306a36Sopenharmony_ci * top down from the end of online memory. physmem_alloc_pos represents 17362306a36Sopenharmony_ci * current position where those allocations start. 17462306a36Sopenharmony_ci * 17562306a36Sopenharmony_ci * Functions randomize_within_range() and iterate_valid_positions() 17662306a36Sopenharmony_ci * only consider "dynamic" allocations by never looking above 17762306a36Sopenharmony_ci * physmem_alloc_pos. "Static" allocations, however, are explicitly 17862306a36Sopenharmony_ci * considered by checking the "res" (reserves) array. The first 17962306a36Sopenharmony_ci * reserved_range of a "dynamic" allocation may also be checked along the 18062306a36Sopenharmony_ci * way, but it will always be above the maximum value anyway. 18162306a36Sopenharmony_ci */ 18262306a36Sopenharmony_ciunsigned long randomize_within_range(unsigned long size, unsigned long align, 18362306a36Sopenharmony_ci unsigned long min, unsigned long max) 18462306a36Sopenharmony_ci{ 18562306a36Sopenharmony_ci struct reserved_range res[RR_MAX]; 18662306a36Sopenharmony_ci unsigned long max_pos, pos; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci memcpy(res, physmem_info.reserved, sizeof(res)); 18962306a36Sopenharmony_ci sort_reserved_ranges(res, ARRAY_SIZE(res)); 19062306a36Sopenharmony_ci max = min(max, get_physmem_alloc_pos()); 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci max_pos = iterate_valid_positions(size, align, min, max, res, ARRAY_SIZE(res), true, 0); 19362306a36Sopenharmony_ci if (!max_pos) 19462306a36Sopenharmony_ci return 0; 19562306a36Sopenharmony_ci if (get_random(max_pos, &pos)) 19662306a36Sopenharmony_ci return 0; 19762306a36Sopenharmony_ci return iterate_valid_positions(size, align, min, max, res, ARRAY_SIZE(res), false, pos + 1); 19862306a36Sopenharmony_ci} 199