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