1 /* 2 * Initialize machine setup information 3 * 4 * Copyright (C) 2017, Red Hat Inc, Andrew Jones <drjones@redhat.com> 5 * Copyright (C) 2021, Google Inc, Zixuan Wang <zixuanwang@google.com> 6 * 7 * This work is licensed under the terms of the GNU LGPL, version 2. 8 */ 9 #include "libcflat.h" 10 #include "fwcfg.h" 11 #include "alloc_phys.h" 12 #include "argv.h" 13 #include "desc.h" 14 #include "apic.h" 15 #include "apic-defs.h" 16 #include "asm/setup.h" 17 18 extern char edata; 19 20 struct mbi_bootinfo { 21 u32 flags; 22 u32 mem_lower; 23 u32 mem_upper; 24 u32 boot_device; 25 u32 cmdline; 26 u32 mods_count; 27 u32 mods_addr; 28 u32 reserved[4]; /* 28-43 */ 29 u32 mmap_length; 30 u32 mmap_addr; 31 u32 reserved0[3]; /* 52-63 */ 32 u32 bootloader; 33 u32 reserved1[5]; /* 68-87 */ 34 u32 size; 35 }; 36 37 struct mbi_module { 38 u32 start, end; 39 u32 cmdline; 40 u32 unused; 41 }; 42 43 struct mbi_mem { 44 u32 size; 45 u64 base_addr; 46 u64 length; 47 u32 type; 48 } __attribute__((packed)); 49 50 #define ENV_SIZE 16384 51 52 void setup_env(char *env, int size); 53 void setup_multiboot(struct mbi_bootinfo *bootinfo); 54 void setup_libcflat(void); 55 56 char *initrd; 57 u32 initrd_size; 58 59 static char env[ENV_SIZE]; 60 static struct mbi_bootinfo *bootinfo; 61 62 #define HUGEPAGE_SIZE (1 << 21) 63 64 #ifdef __x86_64__ 65 void find_highmem(void) 66 { 67 /* Memory above 4 GB is only supported on 64-bit systems. */ 68 if (!(bootinfo->flags & 64)) 69 return; 70 71 u64 upper_end = bootinfo->mem_upper * 1024ull; 72 u64 best_start = (uintptr_t) &edata; 73 u64 best_end = upper_end; 74 u64 max_end = fwcfg_get_u64(FW_CFG_MAX_RAM); 75 if (max_end == 0) 76 max_end = -1ull; 77 bool found = false; 78 79 uintptr_t mmap = bootinfo->mmap_addr; 80 while (mmap < bootinfo->mmap_addr + bootinfo->mmap_length) { 81 struct mbi_mem *mem = (void *)mmap; 82 mmap += mem->size + 4; 83 if (mem->type != 1) 84 continue; 85 if (mem->base_addr <= (uintptr_t) &edata || 86 (mem->base_addr <= upper_end && mem->base_addr + mem->length <= upper_end)) 87 continue; 88 if (mem->length < best_end - best_start) 89 continue; 90 if (mem->base_addr >= max_end) 91 continue; 92 best_start = mem->base_addr; 93 best_end = mem->base_addr + mem->length; 94 if (best_end > max_end) 95 best_end = max_end; 96 found = true; 97 } 98 99 if (found) { 100 best_start = (best_start + HUGEPAGE_SIZE - 1) & -HUGEPAGE_SIZE; 101 best_end = best_end & -HUGEPAGE_SIZE; 102 phys_alloc_init(best_start, best_end - best_start); 103 } 104 } 105 106 /* Setup TSS for the current processor, and return TSS offset within GDT */ 107 unsigned long setup_tss(u8 *stacktop) 108 { 109 u32 id; 110 tss64_t *tss_entry; 111 112 id = apic_id(); 113 114 /* Runtime address of current TSS */ 115 tss_entry = &tss[id]; 116 117 /* Update TSS */ 118 memset((void *)tss_entry, 0, sizeof(tss64_t)); 119 120 /* Update TSS descriptors; each descriptor takes up 2 entries */ 121 set_gdt_entry(TSS_MAIN + id * 16, (unsigned long)tss_entry, 0xffff, 0x89, 0); 122 123 return TSS_MAIN + id * 16; 124 } 125 #else 126 /* Setup TSS for the current processor, and return TSS offset within GDT */ 127 unsigned long setup_tss(u8 *stacktop) 128 { 129 u32 id; 130 tss32_t *tss_entry; 131 132 id = apic_id(); 133 134 /* Runtime address of current TSS */ 135 tss_entry = &tss[id]; 136 137 /* Update TSS */ 138 memset((void *)tss_entry, 0, sizeof(tss32_t)); 139 tss_entry->ss0 = KERNEL_DS; 140 141 /* Update descriptors for TSS and percpu data segment. */ 142 set_gdt_entry(TSS_MAIN + id * 8, 143 (unsigned long)tss_entry, 0xffff, 0x89, 0); 144 set_gdt_entry(TSS_MAIN + MAX_TEST_CPUS * 8 + id * 8, 145 (unsigned long)stacktop - 4096, 0xfffff, 0x93, 0xc0); 146 147 return TSS_MAIN + id * 8; 148 } 149 #endif 150 151 void setup_multiboot(struct mbi_bootinfo *bi) 152 { 153 struct mbi_module *mods; 154 155 bootinfo = bi; 156 157 u64 best_start = (uintptr_t) &edata; 158 u64 best_end = bootinfo->mem_upper * 1024ull; 159 phys_alloc_init(best_start, best_end - best_start); 160 161 if (bootinfo->mods_count != 1) 162 return; 163 164 mods = (struct mbi_module *)(uintptr_t) bootinfo->mods_addr; 165 166 initrd = (char *)(uintptr_t) mods->start; 167 initrd_size = mods->end - mods->start; 168 } 169 170 void setup_libcflat(void) 171 { 172 if (initrd) { 173 /* environ is currently the only file in the initrd */ 174 u32 size = MIN(initrd_size, ENV_SIZE); 175 const char *str; 176 177 memcpy(env, initrd, size); 178 setup_env(env, size); 179 if ((str = getenv("BOOTLOADER")) && atol(str) != 0) 180 add_setup_arg("bootloader"); 181 } 182 } 183