1 /* 2 * Initialize machine setup information 3 * 4 * Copyright (C) 2017, Red Hat Inc, Andrew Jones <drjones@redhat.com> 5 * 6 * This work is licensed under the terms of the GNU LGPL, version 2. 7 */ 8 #include "libcflat.h" 9 #include "fwcfg.h" 10 #include "alloc_phys.h" 11 #include "argv.h" 12 13 extern char edata; 14 15 struct mbi_bootinfo { 16 u32 flags; 17 u32 mem_lower; 18 u32 mem_upper; 19 u32 boot_device; 20 u32 cmdline; 21 u32 mods_count; 22 u32 mods_addr; 23 u32 reserved[4]; /* 28-43 */ 24 u32 mmap_length; 25 u32 mmap_addr; 26 u32 reserved0[3]; /* 52-63 */ 27 u32 bootloader; 28 u32 reserved1[5]; /* 68-87 */ 29 u32 size; 30 }; 31 32 struct mbi_module { 33 u32 start, end; 34 u32 cmdline; 35 u32 unused; 36 }; 37 38 struct mbi_mem { 39 u32 size; 40 u64 base_addr; 41 u64 length; 42 u32 type; 43 } __attribute__((packed)); 44 45 #define ENV_SIZE 16384 46 47 void setup_env(char *env, int size); 48 void setup_multiboot(struct mbi_bootinfo *bootinfo); 49 void setup_libcflat(void); 50 51 char *initrd; 52 u32 initrd_size; 53 54 static char env[ENV_SIZE]; 55 static struct mbi_bootinfo *bootinfo; 56 57 #define HUGEPAGE_SIZE (1 << 21) 58 59 #ifdef __x86_64__ 60 void find_highmem(void) 61 { 62 /* Memory above 4 GB is only supported on 64-bit systems. */ 63 if (!(bootinfo->flags & 64)) 64 return; 65 66 u64 upper_end = bootinfo->mem_upper * 1024ull; 67 u64 best_start = (uintptr_t) &edata; 68 u64 best_end = upper_end; 69 u64 max_end = fwcfg_get_u64(FW_CFG_MAX_RAM); 70 if (max_end == 0) 71 max_end = -1ull; 72 bool found = false; 73 74 uintptr_t mmap = bootinfo->mmap_addr; 75 while (mmap < bootinfo->mmap_addr + bootinfo->mmap_length) { 76 struct mbi_mem *mem = (void *)mmap; 77 mmap += mem->size + 4; 78 if (mem->type != 1) 79 continue; 80 if (mem->base_addr <= (uintptr_t) &edata || 81 (mem->base_addr <= upper_end && mem->base_addr + mem->length <= upper_end)) 82 continue; 83 if (mem->length < best_end - best_start) 84 continue; 85 if (mem->base_addr >= max_end) 86 continue; 87 best_start = mem->base_addr; 88 best_end = mem->base_addr + mem->length; 89 if (best_end > max_end) 90 best_end = max_end; 91 found = true; 92 } 93 94 if (found) { 95 best_start = (best_start + HUGEPAGE_SIZE - 1) & -HUGEPAGE_SIZE; 96 best_end = best_end & -HUGEPAGE_SIZE; 97 phys_alloc_init(best_start, best_end - best_start); 98 } 99 } 100 #endif 101 102 void setup_multiboot(struct mbi_bootinfo *bi) 103 { 104 struct mbi_module *mods; 105 106 bootinfo = bi; 107 108 u64 best_start = (uintptr_t) &edata; 109 u64 best_end = bootinfo->mem_upper * 1024ull; 110 phys_alloc_init(best_start, best_end - best_start); 111 112 if (bootinfo->mods_count != 1) 113 return; 114 115 mods = (struct mbi_module *)(uintptr_t) bootinfo->mods_addr; 116 117 initrd = (char *)(uintptr_t) mods->start; 118 initrd_size = mods->end - mods->start; 119 } 120 121 void setup_libcflat(void) 122 { 123 if (initrd) { 124 /* environ is currently the only file in the initrd */ 125 u32 size = MIN(initrd_size, ENV_SIZE); 126 const char *str; 127 128 memcpy(env, initrd, size); 129 setup_env(env, size); 130 if ((str = getenv("BOOTLOADER")) && atol(str) != 0) 131 add_setup_arg("bootloader"); 132 } 133 } 134