blob: 7befe0961f47c4c53a3005348ffbbebc6b6ffb9c [file] [log] [blame]
/*
* Initialize machine setup information
*
* Copyright (C) 2017, Red Hat Inc, Andrew Jones <drjones@redhat.com>
*
* This work is licensed under the terms of the GNU LGPL, version 2.
*/
#include "libcflat.h"
#include "fwcfg.h"
#include "alloc_phys.h"
#include "argv.h"
extern char edata;
struct mbi_bootinfo {
u32 flags;
u32 mem_lower;
u32 mem_upper;
u32 boot_device;
u32 cmdline;
u32 mods_count;
u32 mods_addr;
u32 reserved[4]; /* 28-43 */
u32 mmap_length;
u32 mmap_addr;
u32 reserved0[3]; /* 52-63 */
u32 bootloader;
u32 reserved1[5]; /* 68-87 */
u32 size;
};
struct mbi_module {
u32 start, end;
u32 cmdline;
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);
void setup_multiboot(struct mbi_bootinfo *bootinfo);
void setup_libcflat(void);
char *initrd;
u32 initrd_size;
static char env[ENV_SIZE];
static 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;
u64 max_end = fwcfg_get_u64(FW_CFG_MAX_RAM);
if (max_end == 0)
max_end = -1ull;
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;
if (mem->base_addr >= max_end)
continue;
best_start = mem->base_addr;
best_end = mem->base_addr + mem->length;
if (best_end > max_end)
best_end = max_end;
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;
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;
mods = (struct mbi_module *)(uintptr_t) bootinfo->mods_addr;
initrd = (char *)(uintptr_t) mods->start;
initrd_size = mods->end - mods->start;
}
void setup_libcflat(void)
{
if (initrd) {
/* environ is currently the only file in the initrd */
u32 size = MIN(initrd_size, ENV_SIZE);
const char *str;
memcpy(env, initrd, size);
setup_env(env, size);
if ((str = getenv("BOOTLOADER")) && atol(str) != 0)
add_setup_arg("bootloader");
}
}