x86: allow using memory above 4 GiB
diff --git a/lib/alloc_phys.h b/lib/alloc_phys.h
index 7cf59a6..ea38f91 100644
--- a/lib/alloc_phys.h
+++ b/lib/alloc_phys.h
@@ -44,4 +44,10 @@
*/
extern void phys_alloc_get_unused(phys_addr_t *p_base, phys_addr_t *p_top);
+/*
+ * Search for memory that can only be used when the MMU is on, and reinitialize
+ * the physical memory allocator using it.
+ */
+extern void find_highmem(void);
+
#endif /* _ALLOC_PHYS_H_ */
diff --git a/lib/vmalloc.c b/lib/vmalloc.c
index b583786..5022a31 100644
--- a/lib/vmalloc.c
+++ b/lib/vmalloc.c
@@ -86,6 +86,10 @@
.align_min = PAGE_SIZE,
};
+void __attribute__((__weak__)) find_highmem(void)
+{
+}
+
void setup_vm()
{
phys_addr_t base, top;
@@ -95,11 +99,24 @@
phys_alloc_get_unused(&base, &top);
assert(base != top || page_alloc_initialized());
+ /*
+ * Give low memory immediately to the page allocator,
+ * so that it can be used to allocate page tables.
+ */
if (!page_alloc_initialized()) {
base = (base + PAGE_SIZE - 1) & -PAGE_SIZE;
top = top & -PAGE_SIZE;
free_pages(phys_to_virt(base), top - base);
}
+
+ find_highmem();
+ phys_alloc_get_unused(&base, &top);
page_root = setup_mmu(top);
+ if (base != top) {
+ base = (base + PAGE_SIZE - 1) & -PAGE_SIZE;
+ top = top & -PAGE_SIZE;
+ free_pages(phys_to_virt(base), top - base);
+ }
+
alloc_ops = &vmalloc_ops;
}
diff --git a/lib/x86/setup.c b/lib/x86/setup.c
index 3a2741a..b5941cd 100644
--- a/lib/x86/setup.c
+++ b/lib/x86/setup.c
@@ -20,7 +20,8 @@
u32 cmdline;
u32 mods_count;
u32 mods_addr;
- u32 reserved[5]; /* 28-47 */
+ u32 reserved[4]; /* 28-43 */
+ u32 mmap_length;
u32 mmap_addr;
u32 reserved0[3]; /* 52-63 */
u32 bootloader;
@@ -34,6 +35,13 @@
u32 unused;
};
+struct mbi_mem {
+ u32 size;
+ u64 base_addr;
+ u64 length;
+ u32 type;
+} __attribute__((packed));
+
#define ENV_SIZE 16384
void setup_env(char *env, int size);
@@ -44,14 +52,55 @@
u32 initrd_size;
static char env[ENV_SIZE];
+static struct mbi_bootinfo *bootinfo;
-void setup_multiboot(struct mbi_bootinfo *bootinfo)
+#define HUGEPAGE_SIZE (1 << 21)
+
+#ifdef __x86_64__
+void find_highmem(void)
+{
+ /* Memory above 4 GB is only supported on 64-bit systems. */
+ if (!(bootinfo->flags & 64))
+ return;
+
+ u64 upper_end = bootinfo->mem_upper * 1024ull;
+ u64 best_start = (uintptr_t) &edata;
+ u64 best_end = upper_end;
+ bool found = false;
+
+ uintptr_t mmap = bootinfo->mmap_addr;
+ while (mmap < bootinfo->mmap_addr + bootinfo->mmap_length) {
+ struct mbi_mem *mem = (void *)mmap;
+ mmap += mem->size + 4;
+ if (mem->type != 1)
+ continue;
+ if (mem->base_addr <= (uintptr_t) &edata ||
+ (mem->base_addr <= upper_end && mem->base_addr + mem->length <= upper_end))
+ continue;
+ if (mem->length < best_end - best_start)
+ continue;
+ best_start = mem->base_addr;
+ best_end = mem->base_addr + mem->length;
+ found = true;
+ }
+
+ if (found) {
+ best_start = (best_start + HUGEPAGE_SIZE - 1) & -HUGEPAGE_SIZE;
+ best_end = best_end & -HUGEPAGE_SIZE;
+ phys_alloc_init(best_start, best_end - best_start);
+ }
+}
+#endif
+
+void setup_multiboot(struct mbi_bootinfo *bi)
{
struct mbi_module *mods;
- /* TODO: use e820 */
- u64 end_of_memory = bootinfo->mem_upper * 1024ull;
- phys_alloc_init((uintptr_t) &edata, end_of_memory - (uintptr_t) &edata);
+ bootinfo = bi;
+
+ u64 best_start = (uintptr_t) &edata;
+ u64 best_end = bootinfo->mem_upper * 1024ull;
+ phys_alloc_init(best_start, best_end - best_start);
if (bootinfo->mods_count != 1)
return;