162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Entropy functions used on early boot for KASLR base and memory 462306a36Sopenharmony_ci * randomization. The base randomization is done in the compressed 562306a36Sopenharmony_ci * kernel and memory randomization is done early when the regular 662306a36Sopenharmony_ci * kernel starts. This file is included in the compressed kernel and 762306a36Sopenharmony_ci * normally linked in the regular. 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci#include <asm/asm.h> 1062306a36Sopenharmony_ci#include <asm/kaslr.h> 1162306a36Sopenharmony_ci#include <asm/msr.h> 1262306a36Sopenharmony_ci#include <asm/archrandom.h> 1362306a36Sopenharmony_ci#include <asm/e820/api.h> 1462306a36Sopenharmony_ci#include <asm/shared/io.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci/* 1762306a36Sopenharmony_ci * When built for the regular kernel, several functions need to be stubbed out 1862306a36Sopenharmony_ci * or changed to their regular kernel equivalent. 1962306a36Sopenharmony_ci */ 2062306a36Sopenharmony_ci#ifndef KASLR_COMPRESSED_BOOT 2162306a36Sopenharmony_ci#include <asm/cpufeature.h> 2262306a36Sopenharmony_ci#include <asm/setup.h> 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#define debug_putstr(v) early_printk("%s", v) 2562306a36Sopenharmony_ci#define has_cpuflag(f) boot_cpu_has(f) 2662306a36Sopenharmony_ci#define get_boot_seed() kaslr_offset() 2762306a36Sopenharmony_ci#endif 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#define I8254_PORT_CONTROL 0x43 3062306a36Sopenharmony_ci#define I8254_PORT_COUNTER0 0x40 3162306a36Sopenharmony_ci#define I8254_CMD_READBACK 0xC0 3262306a36Sopenharmony_ci#define I8254_SELECT_COUNTER0 0x02 3362306a36Sopenharmony_ci#define I8254_STATUS_NOTREADY 0x40 3462306a36Sopenharmony_cistatic inline u16 i8254(void) 3562306a36Sopenharmony_ci{ 3662306a36Sopenharmony_ci u16 status, timer; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci do { 3962306a36Sopenharmony_ci outb(I8254_CMD_READBACK | I8254_SELECT_COUNTER0, 4062306a36Sopenharmony_ci I8254_PORT_CONTROL); 4162306a36Sopenharmony_ci status = inb(I8254_PORT_COUNTER0); 4262306a36Sopenharmony_ci timer = inb(I8254_PORT_COUNTER0); 4362306a36Sopenharmony_ci timer |= inb(I8254_PORT_COUNTER0) << 8; 4462306a36Sopenharmony_ci } while (status & I8254_STATUS_NOTREADY); 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci return timer; 4762306a36Sopenharmony_ci} 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ciunsigned long kaslr_get_random_long(const char *purpose) 5062306a36Sopenharmony_ci{ 5162306a36Sopenharmony_ci#ifdef CONFIG_X86_64 5262306a36Sopenharmony_ci const unsigned long mix_const = 0x5d6008cbf3848dd3UL; 5362306a36Sopenharmony_ci#else 5462306a36Sopenharmony_ci const unsigned long mix_const = 0x3f39e593UL; 5562306a36Sopenharmony_ci#endif 5662306a36Sopenharmony_ci unsigned long raw, random = get_boot_seed(); 5762306a36Sopenharmony_ci bool use_i8254 = true; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci if (purpose) { 6062306a36Sopenharmony_ci debug_putstr(purpose); 6162306a36Sopenharmony_ci debug_putstr(" KASLR using"); 6262306a36Sopenharmony_ci } 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci if (has_cpuflag(X86_FEATURE_RDRAND)) { 6562306a36Sopenharmony_ci if (purpose) 6662306a36Sopenharmony_ci debug_putstr(" RDRAND"); 6762306a36Sopenharmony_ci if (rdrand_long(&raw)) { 6862306a36Sopenharmony_ci random ^= raw; 6962306a36Sopenharmony_ci use_i8254 = false; 7062306a36Sopenharmony_ci } 7162306a36Sopenharmony_ci } 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci if (has_cpuflag(X86_FEATURE_TSC)) { 7462306a36Sopenharmony_ci if (purpose) 7562306a36Sopenharmony_ci debug_putstr(" RDTSC"); 7662306a36Sopenharmony_ci raw = rdtsc(); 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci random ^= raw; 7962306a36Sopenharmony_ci use_i8254 = false; 8062306a36Sopenharmony_ci } 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci if (use_i8254) { 8362306a36Sopenharmony_ci if (purpose) 8462306a36Sopenharmony_ci debug_putstr(" i8254"); 8562306a36Sopenharmony_ci random ^= i8254(); 8662306a36Sopenharmony_ci } 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci /* Circular multiply for better bit diffusion */ 8962306a36Sopenharmony_ci asm(_ASM_MUL "%3" 9062306a36Sopenharmony_ci : "=a" (random), "=d" (raw) 9162306a36Sopenharmony_ci : "a" (random), "rm" (mix_const)); 9262306a36Sopenharmony_ci random += raw; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci if (purpose) 9562306a36Sopenharmony_ci debug_putstr("...\n"); 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci return random; 9862306a36Sopenharmony_ci} 99