[PATCH v3 17/19] x86, kaslr: Add support of kernel physical address randomization above 4G

From: Baoquan He
Date: Fri Mar 04 2016 - 11:28:26 EST


In kaslr implementation mechanism, process_e820_entry and
slots_fetch_random are 2 key functions.

process_e820_entry is used to parse passed in memory region and
store available slot area information into array slot_areas[].
slots_fetch_random is used to get a random value and translate
it into starting address of corresponding slot.

In this patch, for adding support of kernel physical address
randomization above 4G, both of these two functions are changed
based on the new slot_area data structure.

Now kernel can be reloaded and decompressed anywhere of the whole
physical memory, even near 64T at most.

Signed-off-by: Baoquan He <bhe@xxxxxxxxxx>
---
arch/x86/boot/compressed/aslr.c | 68 ++++++++++++++++++++++++++++++-----------
1 file changed, 51 insertions(+), 17 deletions(-)

diff --git a/arch/x86/boot/compressed/aslr.c b/arch/x86/boot/compressed/aslr.c
index 75ea931..d5ef3b4 100644
--- a/arch/x86/boot/compressed/aslr.c
+++ b/arch/x86/boot/compressed/aslr.c
@@ -293,27 +293,40 @@ static void slots_append(unsigned long addr)

static unsigned long slots_fetch_random(void)
{
+ unsigned long random;
+ int i;
+
/* Handle case of no slots stored. */
if (slot_max == 0)
return 0;

- return slots[get_random_long() % slot_max];
+ random = get_random_long() % slot_max;
+
+ for (i = 0; i < slot_area_index; i++) {
+ if (random >= slot_areas[i].num) {
+ random -= slot_areas[i].num;
+ continue;
+ }
+ return slot_areas[i].addr + random * CONFIG_PHYSICAL_ALIGN;
+ }
+
+ if (i == slot_area_index)
+ debug_putstr("Something wrong happened in slots_fetch_random()...\n");
+ return 0;
}

static void process_e820_entry(struct e820entry *entry,
unsigned long minimum,
unsigned long image_size)
{
- struct mem_vector region, img;
+ struct mem_vector region, out;
+ struct slot_area slot_area;
+ unsigned long min, start_orig;

/* Skip non-RAM entries. */
if (entry->type != E820_RAM)
return;

- /* Ignore entries entirely above our maximum. */
- if (entry->addr >= CONFIG_RANDOMIZE_BASE_MAX_OFFSET)
- return;
-
/* Ignore entries entirely below our minimum. */
if (entry->addr + entry->size < minimum)
return;
@@ -321,10 +334,17 @@ static void process_e820_entry(struct e820entry *entry,
region.start = entry->addr;
region.size = entry->size;

+repeat:
+ start_orig = region.start;
+
/* Potentially raise address to minimum location. */
if (region.start < minimum)
region.start = minimum;

+ /* Return if slot area array is full */
+ if (slot_area_index == MAX_SLOT_AREA)
+ return;
+
/* Potentially raise address to meet alignment requirements. */
region.start = ALIGN(region.start, CONFIG_PHYSICAL_ALIGN);

@@ -333,20 +353,30 @@ static void process_e820_entry(struct e820entry *entry,
return;

/* Reduce size by any delta from the original address. */
- region.size -= region.start - entry->addr;
+ region.size -= region.start - start_orig;

- /* Reduce maximum size to fit end of image within maximum limit. */
- if (region.start + region.size > CONFIG_RANDOMIZE_BASE_MAX_OFFSET)
- region.size = CONFIG_RANDOMIZE_BASE_MAX_OFFSET - region.start;
+ /* Return if region can't contain decompressed kernel */
+ if (region.size < image_size)
+ return;

- /* Walk each aligned slot and check for avoided areas. */
- for (img.start = region.start, img.size = image_size ;
- mem_contains(&region, &img) ;
- img.start += CONFIG_PHYSICAL_ALIGN) {
- if (mem_avoid_overlap(&img))
- continue;
- slots_append(img.start);
+ if (!mem_avoid_overlap(&region)) {
+ store_slot_info(&region, image_size);
+ return;
}
+
+ min = mem_min_overlap(&region, &out);
+
+ if (min > region.start + image_size) {
+ struct mem_vector tmp;
+
+ tmp.start = region.start;
+ tmp.size = min - region.start;
+ store_slot_info(&tmp, image_size);
+ }
+
+ region.size -= out.start - region.start + out.size;
+ region.start = out.start + out.size;
+ goto repeat;
}

static unsigned long find_random_phy_addr(unsigned long minimum,
@@ -361,6 +391,10 @@ static unsigned long find_random_phy_addr(unsigned long minimum,
/* Verify potential e820 positions, appending to slots list. */
for (i = 0; i < real_mode->e820_entries; i++) {
process_e820_entry(&real_mode->e820_map[i], minimum, size);
+ if (slot_area_index == MAX_SLOT_AREA) {
+ debug_putstr("Stop processing e820 since slot_areas is full...\n");
+ break;
+ }
}

return slots_fetch_random();
--
2.5.0