Support multiple memory ranges.
Bug: 114733240
Change-Id: Ifbad8688e0a0e17a4732b637711919fc18d68f7a
diff --git a/inc/hf/boot_params.h b/inc/hf/boot_params.h
index f338e84..0a6ab8c 100644
--- a/inc/hf/boot_params.h
+++ b/inc/hf/boot_params.h
@@ -4,17 +4,24 @@
#include "hf/mm.h"
+#define MAX_MEM_RANGES 20
+
+struct mem_range {
+ paddr_t begin;
+ paddr_t end;
+};
+
struct boot_params {
- paddr_t mem_begin;
- paddr_t mem_end;
+ struct mem_range mem_ranges[MAX_MEM_RANGES];
+ size_t mem_ranges_count;
paddr_t initrd_begin;
paddr_t initrd_end;
size_t kernel_arg;
};
struct boot_params_update {
- paddr_t reserved_begin;
- paddr_t reserved_end;
+ struct mem_range reserved_ranges[MAX_MEM_RANGES];
+ size_t reserved_ranges_count;
paddr_t initrd_begin;
paddr_t initrd_end;
};
diff --git a/inc/hf/load.h b/inc/hf/load.h
index 5e4ead3..d1f239c 100644
--- a/inc/hf/load.h
+++ b/inc/hf/load.h
@@ -3,11 +3,13 @@
#include <stddef.h>
#include <stdint.h>
+#include "hf/boot_params.h"
#include "hf/cpio.h"
#include "hf/memiter.h"
#include "hf/mm.h"
bool load_primary(const struct memiter *cpio, size_t kernel_arg,
struct memiter *initrd);
-bool load_secondary(const struct memiter *cpio, paddr_t mem_begin,
- paddr_t *mem_end);
+bool load_secondary(const struct memiter *cpio,
+ const struct boot_params *params,
+ struct boot_params_update *update);
diff --git a/src/fdt_handler.c b/src/fdt_handler.c
index 4a93e66..91a17d8 100644
--- a/src/fdt_handler.c
+++ b/src/fdt_handler.c
@@ -108,14 +108,15 @@
return true;
}
-static void find_memory_range(const struct fdt_node *root,
- struct boot_params *p)
+static void find_memory_ranges(const struct fdt_node *root,
+ struct boot_params *p)
{
struct fdt_node n = *root;
const char *name;
uint64_t address_size;
uint64_t size_size;
uint64_t entry_size;
+ size_t mem_range_index = 0;
/* Get the sizes of memory range addresses and sizes. */
if (fdt_read_number(&n, "#address-cells", &address_size)) {
@@ -153,16 +154,24 @@
size_t len =
convert_number(data + address_size, size_size);
- if (len > pa_addr(p->mem_end) - pa_addr(p->mem_begin)) {
- /* Remember the largest range we've found. */
- p->mem_begin = pa_init(addr);
- p->mem_end = pa_init(addr + len);
+ if (mem_range_index < MAX_MEM_RANGES) {
+ p->mem_ranges[mem_range_index].begin =
+ pa_init(addr);
+ p->mem_ranges[mem_range_index].end =
+ pa_init(addr + len);
+ ++mem_range_index;
+ } else {
+ dlog("Found memory range %u in FDT but only "
+ "%u supported, ignoring additional range "
+ "of size %u.\n",
+ mem_range_index, MAX_MEM_RANGES, len);
}
size -= entry_size;
data += entry_size;
}
} while (fdt_next_sibling(&n, &name));
+ p->mem_ranges_count = mem_range_index;
/* TODO: Check for "reserved-memory" nodes. */
}
@@ -199,9 +208,8 @@
goto out_unmap_fdt;
}
- p->mem_begin = pa_init(0);
- p->mem_end = pa_init(0);
- find_memory_range(&n, p);
+ p->mem_ranges_count = 0;
+ find_memory_ranges(&n, p);
if (!find_initrd(&n, p)) {
goto out_unmap_fdt;
@@ -224,6 +232,7 @@
struct fdt_header *fdt;
struct fdt_node n;
bool ret = false;
+ size_t i;
/* Map the fdt header in. */
fdt = mm_identity_map(fdt_addr, pa_add(fdt_addr, fdt_header_size()),
@@ -277,9 +286,12 @@
}
/* Patch fdt to reserve memory for secondary VMs. */
- fdt_add_mem_reservation(
- fdt, pa_addr(p->reserved_begin),
- pa_addr(p->reserved_end) - pa_addr(p->reserved_begin));
+ for (i = 0; i < p->reserved_ranges_count; ++i) {
+ fdt_add_mem_reservation(
+ fdt, pa_addr(p->reserved_ranges[i].begin),
+ pa_addr(p->reserved_ranges[i].end) -
+ pa_addr(p->reserved_ranges[i].begin));
+ }
ret = true;
diff --git a/src/load.c b/src/load.c
index 6ed2d1b..0e45aba 100644
--- a/src/load.c
+++ b/src/load.c
@@ -1,8 +1,10 @@
#include "hf/load.h"
+#include <assert.h>
#include <stdbool.h>
#include "hf/api.h"
+#include "hf/boot_params.h"
#include "hf/dlog.h"
#include "hf/memiter.h"
#include "hf/mm.h"
@@ -146,26 +148,113 @@
}
/**
- * Loads all secondary VMs in the given memory range. "mem_end" is updated to
- * reflect the fact that some of the memory isn't available to the primary VM
- * anymore.
+ * Try to find a memory range of the given size within the given ranges, and
+ * remove it from them. Return true on success, or false if no large enough
+ * contiguous range is found.
*/
-bool load_secondary(const struct memiter *cpio, paddr_t mem_begin,
- paddr_t *mem_end)
+bool carve_out_mem_range(struct mem_range *mem_ranges, size_t mem_ranges_count,
+ uint64_t size_to_find, paddr_t *found_begin,
+ paddr_t *found_end)
+{
+ size_t i;
+
+ /* TODO(b/116191358): Consider being cleverer about how we pack VMs
+ * together, with a non-greedy algorithm. */
+ for (i = 0; i < mem_ranges_count; ++i) {
+ if (size_to_find <=
+ pa_addr(mem_ranges[i].end) - pa_addr(mem_ranges[i].begin)) {
+ /* This range is big enough, take some of it from the
+ * end and reduce its size accordingly. */
+ *found_end = mem_ranges[i].end;
+ *found_begin = pa_init(pa_addr(mem_ranges[i].end) -
+ size_to_find);
+ mem_ranges[i].end = *found_begin;
+ return true;
+ }
+ }
+ return false;
+}
+
+/**
+ * Given arrays of memory ranges before and after memory was removed for
+ * secondary VMs, add the difference to the reserved ranges of the given update.
+ * Return true on success, or false if there would be more than MAX_MEM_RANGES
+ * reserved ranges after adding the new ones.
+ * `before` and `after` must be arrays of exactly `mem_ranges_count` elements.
+ */
+bool update_reserved_ranges(struct boot_params_update *update,
+ const struct mem_range *before,
+ const struct mem_range *after,
+ size_t mem_ranges_count)
+{
+ size_t i;
+
+ for (i = 0; i < mem_ranges_count; ++i) {
+ if (pa_addr(after[i].begin) > pa_addr(before[i].begin)) {
+ if (update->reserved_ranges_count >= MAX_MEM_RANGES) {
+ dlog("Too many reserved ranges after loading "
+ "secondary VMs.\n");
+ return false;
+ }
+ update->reserved_ranges[update->reserved_ranges_count]
+ .begin = before[i].begin;
+ update->reserved_ranges[update->reserved_ranges_count]
+ .end = after[i].begin;
+ update->reserved_ranges_count++;
+ }
+ if (pa_addr(after[i].end) < pa_addr(before[i].end)) {
+ if (update->reserved_ranges_count >= MAX_MEM_RANGES) {
+ dlog("Too many reserved ranges after loading "
+ "secondary VMs.\n");
+ return false;
+ }
+ update->reserved_ranges[update->reserved_ranges_count]
+ .begin = after[i].end;
+ update->reserved_ranges[update->reserved_ranges_count]
+ .end = before[i].end;
+ update->reserved_ranges_count++;
+ }
+ }
+
+ return true;
+}
+
+/**
+ * Loads all secondary VMs into the memory ranges from the given params.
+ * Memory reserved for the VMs is added to the `reserved_ranges` of `update`.
+ */
+bool load_secondary(const struct memiter *cpio,
+ const struct boot_params *params,
+ struct boot_params_update *update)
{
struct memiter it;
struct memiter str;
uint64_t mem;
uint64_t cpu;
uint32_t count;
+ struct mem_range mem_ranges_available[MAX_MEM_RANGES];
+ size_t i;
+
+ static_assert(
+ sizeof(mem_ranges_available) == sizeof(params->mem_ranges),
+ "mem_range arrays must be the same size for memcpy.");
+ static_assert(sizeof(mem_ranges_available) < 500,
+ "This will use too much stack, either make "
+ "MAX_MEM_RANGES smaller or change this.");
+ memcpy(mem_ranges_available, params->mem_ranges,
+ sizeof(mem_ranges_available));
if (!find_file(cpio, "vms.txt", &it)) {
dlog("vms.txt is missing\n");
return true;
}
- /* Round the last address down to the page size. */
- *mem_end = pa_init(pa_addr(*mem_end) & ~(PAGE_SIZE - 1));
+ /* Round the last addresses down to the page size. */
+ for (i = 0; i < params->mem_ranges_count; ++i) {
+ mem_ranges_available[i].end =
+ pa_init(pa_addr(mem_ranges_available[i].end) &
+ ~(PAGE_SIZE - 1));
+ }
for (count = 0;
memiter_parse_uint(&it, &mem) && memiter_parse_uint(&it, &cpu) &&
@@ -183,11 +272,6 @@
/* Round up to page size. */
mem = (mem + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
- if (mem > pa_addr(*mem_end) - pa_addr(mem_begin)) {
- dlog("Not enough memory for vm %u (%u bytes)\n", count,
- mem);
- continue;
- }
if (mem < kernel.limit - kernel.next) {
dlog("Kernel is larger than available memory for vm "
@@ -196,11 +280,15 @@
continue;
}
- secondary_mem_end = *mem_end;
- *mem_end = pa_init(pa_addr(*mem_end) - mem);
- secondary_mem_begin = *mem_end;
+ if (!carve_out_mem_range(
+ mem_ranges_available, params->mem_ranges_count, mem,
+ &secondary_mem_begin, &secondary_mem_end)) {
+ dlog("Not enough memory for vm %u (%u bytes)\n", count,
+ mem);
+ continue;
+ }
- if (!copy_to_unmapped(*mem_end, kernel.next,
+ if (!copy_to_unmapped(secondary_mem_begin, kernel.next,
kernel.limit - kernel.next)) {
dlog("Unable to copy kernel for vm %u\n", count);
continue;
@@ -237,12 +325,19 @@
}
dlog("Loaded VM%u with %u vcpus, entry at 0x%x\n", count, cpu,
- pa_addr(*mem_end));
+ pa_addr(secondary_mem_begin));
vm_start_vcpu(&secondary_vm[count], 0, secondary_entry, 0);
}
secondary_vm_count = count;
- return true;
+ /* Add newly reserved areas to update params by looking at the
+ * difference between the available ranges from the original params and
+ * the updated mem_ranges_available. We assume that the number and order
+ * of available ranges is the same, i.e. we don't remove any ranges
+ * above only make them smaller. */
+ return update_reserved_ranges(update, params->mem_ranges,
+ mem_ranges_available,
+ params->mem_ranges_count);
}
diff --git a/src/main.c b/src/main.c
index 7326cb0..3b9018d 100644
--- a/src/main.c
+++ b/src/main.c
@@ -45,10 +45,10 @@
{
struct boot_params params;
struct boot_params_update update;
- paddr_t new_mem_end;
struct memiter primary_initrd;
struct memiter cpio;
void *initrd;
+ size_t i;
dlog("Initialising hafnium\n");
@@ -63,8 +63,12 @@
panic("unable to retrieve boot params");
}
- dlog("Memory range: 0x%x - 0x%x\n", pa_addr(params.mem_begin),
- pa_addr(params.mem_end) - 1);
+ for (i = 0; i < params.mem_ranges_count; ++i) {
+ dlog("Memory range: 0x%x - 0x%x\n",
+ pa_addr(params.mem_ranges[i].begin),
+ pa_addr(params.mem_ranges[i].end) - 1);
+ }
+
dlog("Ramdisk range: 0x%x - 0x%x\n", pa_addr(params.initrd_begin),
pa_addr(params.initrd_end) - 1);
@@ -79,20 +83,20 @@
pa_addr(params.initrd_end) - pa_addr(params.initrd_begin));
/* Load all VMs. */
- new_mem_end = params.mem_end;
if (!load_primary(&cpio, params.kernel_arg, &primary_initrd)) {
panic("unable to load primary VM");
}
- if (!load_secondary(&cpio, params.mem_begin, &new_mem_end)) {
+ update.initrd_begin = pa_from_va(va_from_ptr(primary_initrd.next));
+ update.initrd_end = pa_from_va(va_from_ptr(primary_initrd.limit));
+ /* load_secondary will add regions assigned to the secondary VMs from
+ * mem_ranges to reserved_ranges. */
+ update.reserved_ranges_count = 0;
+ if (!load_secondary(&cpio, ¶ms, &update)) {
panic("unable to load secondary VMs");
}
- /* Prepare to run by updating bootparams as seens by primary VM. */
- update.initrd_begin = pa_from_va(va_from_ptr(primary_initrd.next));
- update.initrd_end = pa_from_va(va_from_ptr(primary_initrd.limit));
- update.reserved_begin = new_mem_end;
- update.reserved_end = params.mem_end;
+ /* Prepare to run by updating bootparams as seen by primary VM. */
if (!plat_update_boot_params(&update)) {
panic("plat_update_boot_params failed");
}