275 lines
7.4 KiB
C

#include "misc.h"
#ifdef CONFIG_RANDOMIZE_BASE
#include <asm/msr.h>
#include <asm/e820.h>
#include <asm/archrandom.h>
static inline int rdrand(unsigned long *v)
{
int ok;
asm volatile("1: " RDRAND_LONG "\n\t"
"jc 2f\n\t"
"decl %0\n\t"
"jnz 1b\n\t"
"2:"
: "=r" (ok), "=a" (*v)
: "0" (RDRAND_RETRY_LOOPS));
return ok;
}
#define I8254_PORT_CONTROL 0x43
#define I8254_PORT_COUNTER0 0x40
#define I8254_CMD_READBACK 0xC0
#define I8254_SELECT_COUNTER0 0x02
#define I8254_STATUS_NOTREADY 0x40
static inline u16 i8254(void)
{
u16 status, timer;
do {
outb(I8254_PORT_CONTROL,
I8254_CMD_READBACK | I8254_SELECT_COUNTER0);
status = inb(I8254_PORT_COUNTER0);
timer = inb(I8254_PORT_COUNTER0);
timer |= inb(I8254_PORT_COUNTER0) << 8;
} while (status & I8254_STATUS_NOTREADY);
return timer;
}
static unsigned long get_random_long(void)
{
unsigned long random;
if (has_cpuflag(X86_FEATURE_RDRAND)) {
debug_putstr("KASLR using RDRAND...\n");
if (rdrand(&random))
return random;
}
if (has_cpuflag(X86_FEATURE_TSC)) {
uint32_t raw;
debug_putstr("KASLR using RDTSC...\n");
rdtscl(raw);
/* Only use the low bits of rdtsc. */
random = raw & 0xffff;
} else {
debug_putstr("KASLR using i8254...\n");
random = i8254();
}
/* Extend timer bits poorly... */
random |= (random << 16);
#ifdef CONFIG_X86_64
random |= (random << 32) | (random << 48);
#endif
return random;
}
static unsigned long find_minimum_location(unsigned long input,
unsigned long input_size,
unsigned long output,
unsigned long output_size)
{
u64 initrd_start, initrd_size;
u64 cmd_line, cmd_line_size;
unsigned long unsafe, unsafe_len;
char *ptr;
/*
* Mark off the region that is unsafe to overlap during
* decompression (see calculations at top of misc.c).
*/
unsafe_len = (output_size >> 12) + 32768 + 18;
unsafe = (unsigned long)input + input_size - unsafe_len;
/*
* Locate other regions that cannot be over-written during
* decompression: initrd, cmd_line.
*/
initrd_start = (u64)real_mode->ext_ramdisk_image << 32;
initrd_start |= real_mode->hdr.ramdisk_image;
initrd_size = (u64)real_mode->ext_ramdisk_size << 32;
initrd_size |= real_mode->hdr.ramdisk_size;
cmd_line = (u64)real_mode->ext_cmd_line_ptr << 32;
cmd_line |= real_mode->hdr.cmd_line_ptr;
/* Calculate size of cmd_line. */
ptr = (char *)(unsigned long)cmd_line;
for (cmd_line_size = 0; ptr[cmd_line_size++]; )
;
/* Minimum location must be above all these regions: */
output = max(output, unsafe + unsafe_len);
output = max(output, (unsigned long)free_mem_ptr + BOOT_HEAP_SIZE);
output = max(output, (unsigned long)free_mem_end_ptr + BOOT_STACK_SIZE);
output = max(output, (unsigned long)initrd_start
+ (unsigned long)initrd_size);
output = max(output, (unsigned long)cmd_line
+ (unsigned long)cmd_line_size);
/* Make sure the location is still aligned. */
output = ALIGN(output, CONFIG_PHYSICAL_ALIGN);
return output;
}
/*
* This routine is used to count how many aligned slots that can hold the
* kernel exist across all e820 entries. It is called in two phases, once
* to count valid memory regions available for the kernel image, and a
* second time to select one from those seen.
*
* It is first called with "counting" set to true, where it expects to be
* called once for each e820 entry. In this mode, it will update *count
* with how many slots are available in the given e820 entry. Once the walk
* across all e820 entries has finished, the caller will have a total count
* of all valid memory regions available for the kernel image.
*
* Once the first pass of entry walking is finished, the caller selects one
* of the possible slots (stored in *count), and performs a second walk,
* with "counting" set to false. In this mode, *count is decremented until
* the corresponding slot is found in a specific e820 region, at which
* point, the function returns that address, and the walk terminates.
*/
static unsigned long process_e820_entry(struct e820entry *entry, bool counting,
unsigned long minimum,
unsigned long image_size,
unsigned long *count)
{
u64 addr, size;
unsigned long alignments;
/* Skip non-RAM entries. */
if (entry->type != E820_RAM)
return 0;
/* Ignore entries entirely above our maximum. */
if (entry->addr >= CONFIG_RANDOMIZE_BASE_MAX_OFFSET)
return 0;
/* Ignore entries entirely below our minimum. */
if (entry->addr + entry->size < minimum)
return 0;
size = entry->size;
addr = entry->addr;
/* Potentially raise address to minimum location. */
if (addr < minimum)
addr = minimum;
/* Potentially raise address to meet alignment requirements. */
addr = ALIGN(addr, CONFIG_PHYSICAL_ALIGN);
/* Did we raise the address above the bounds of this e820 region? */
if (addr > entry->addr + entry->size)
return 0;
/* Reduce size by any delta from the original address. */
size -= addr - entry->addr;
/* Reduce maximum image starting location to maximum limit. */
if (addr + size > CONFIG_RANDOMIZE_BASE_MAX_OFFSET)
size = CONFIG_RANDOMIZE_BASE_MAX_OFFSET - addr;
/* Ignore entries that cannot hold even a single kernel image. */
if (size < image_size)
return 0;
/*
* Reduce size by kernel image size so we can see how many aligned
* starting addresses will fit without running past the end of a
* region. XXX: adjacent e820 regions are not detected, so up to
* image_size / CONFIG_PHYSICAL_ALIGN slots may go unused across
* adjacent regions.
*/
size -= image_size;
/* Now we know how many aligned slots can contain the image. */
alignments = (size / CONFIG_PHYSICAL_ALIGN) + 1;
/* In the first pass, just counting all the e820 entries? */
if (counting) {
*count += alignments;
return 0;
}
/* Otherwise we're counting down to find a specific aligned slot. */
if (*count < alignments) {
/* Desired region is in this entry. */
return addr + (CONFIG_PHYSICAL_ALIGN * *count);
} else {
/* Desired region is beyond this entry. */
*count -= alignments;
return 0;
}
}
static unsigned long find_random_e820(unsigned long minimum,
unsigned long size)
{
int i;
unsigned long addr, count;
/* Make sure minimum is aligned. */
minimum = ALIGN(minimum, CONFIG_PHYSICAL_ALIGN);
/* Verify potential e820 positions. */
count = 0;
for (i = 0; i < real_mode->e820_entries; i++) {
process_e820_entry(&real_mode->e820_map[i], true, minimum,
size, &count);
}
/* Handle crazy case of nothing fitting. */
if (count == 0)
return 0;
count = get_random_long() % count;
/* Select desired e820 position. */
for (i = 0; i < real_mode->e820_entries; i++) {
addr = process_e820_entry(&real_mode->e820_map[i], false,
minimum, size, &count);
if (addr)
return addr;
}
return 0;
}
unsigned char *choose_kernel_location(unsigned char *input,
unsigned long input_size,
unsigned char *output,
unsigned long output_size)
{
unsigned long choice = (unsigned long)output;
unsigned long random;
if (cmdline_find_option_bool("nokaslr")) {
debug_putstr("KASLR disabled...\n");
goto out;
}
choice = find_minimum_location((unsigned long)input, input_size,
(unsigned long)output, output_size);
random = find_random_e820(choice, output_size);
if (!random) {
debug_putstr("KASLR could not find suitable E820 region...\n");
goto out;
}
/* Always enforce the minimum. */
if (random < choice)
goto out;
choice = random;
out:
return (unsigned char *)choice;
}
#endif /* CONFIG_RANDOMIZE_BASE */