| 1 | #if defined (__x86_64__) || defined (__i386__) |
| 2 | |
| 3 | #include <stdint.h> |
| 4 | #include <stddef.h> |
| 5 | #include <stdnoreturn.h> |
| 6 | #include <protos/multiboot1.h> |
| 7 | #include <protos/multiboot.h> |
| 8 | #include <config.h> |
| 9 | #include <lib/libc.h> |
| 10 | #include <lib/elf.h> |
| 11 | #include <lib/misc.h> |
| 12 | #include <lib/config.h> |
| 13 | #include <lib/print.h> |
| 14 | #include <lib/uri.h> |
| 15 | #include <lib/tpm.h> |
| 16 | #include <lib/fb.h> |
| 17 | #include <lib/term.h> |
| 18 | #include <lib/elsewhere.h> |
| 19 | #include <sys/pic.h> |
| 20 | #include <sys/cpu.h> |
| 21 | #include <sys/idt.h> |
| 22 | #include <sys/iommu.h> |
| 23 | #include <sys/lapic.h> |
| 24 | #include <fs/file.h> |
| 25 | #include <mm/vmm.h> |
| 26 | #include <mm/pmm.h> |
| 27 | #include <drivers/vga_textmode.h> |
| 28 | |
| 29 | #define LIMINE_BRAND "Limine " LIMINE_VERSION |
| 30 | |
| 31 | #define MEMMAP_MAX 256 |
| 32 | |
| 33 | // Returns the size required to store the multiboot info. |
| 34 | static size_t get_multiboot1_info_size( |
| 35 | char *cmdline, |
| 36 | size_t modules_count, size_t modules_cmdlines_size, |
| 37 | uint32_t section_entry_size, uint32_t section_num |
| 38 | ) { |
| 39 | #define OVERFLOW panic(true, "multiboot1: info size overflow") |
| 40 | return ALIGN_UP(sizeof(struct multiboot1_info), 16, OVERFLOW) + |
| 41 | ALIGN_UP(strlen(cmdline) + 1, 16, OVERFLOW) + |
| 42 | ALIGN_UP(sizeof(LIMINE_BRAND), 16, OVERFLOW) + |
| 43 | ALIGN_UP(CHECKED_MUL(section_entry_size, section_num, OVERFLOW), 16, OVERFLOW) + |
| 44 | ALIGN_UP(CHECKED_MUL(sizeof(struct multiboot1_module), modules_count, OVERFLOW), 16, OVERFLOW) + |
| 45 | ALIGN_UP(modules_cmdlines_size, 16, OVERFLOW) + |
| 46 | ALIGN_UP(sizeof(struct multiboot1_mmap_entry) * MEMMAP_MAX, 16, OVERFLOW); |
| 47 | #undef OVERFLOW |
| 48 | } |
| 49 | |
| 50 | static void *mb1_info_alloc(void **mb1_info_raw, size_t size) { |
| 51 | void *ret = *mb1_info_raw; |
| 52 | *mb1_info_raw += ALIGN_UP(size, 16, panic(true, "multiboot: info alloc overflow")); |
| 53 | return ret; |
| 54 | } |
| 55 | |
| 56 | noreturn void multiboot1_load(char *config, char *cmdline) { |
| 57 | struct file_handle *kernel_file; |
| 58 | |
| 59 | #if defined (UEFI) |
| 60 | if (cmdline != NULL) { |
| 61 | tpm_measure(TPM_PCR_BOOT_AUTH, TPM_EV_IPL, |
| 62 | cmdline, strlen(cmdline), "cmdline: ", cmdline); |
| 63 | } |
| 64 | #endif |
| 65 | |
| 66 | char *kernel_path = config_get_value(config, 0, "PATH"); |
| 67 | if (kernel_path == NULL) { |
| 68 | kernel_path = config_get_value(config, 0, "KERNEL_PATH"); |
| 69 | } |
| 70 | if (kernel_path == NULL) { |
| 71 | panic(true, "multiboot1: Executable path not specified"); |
| 72 | } |
| 73 | |
| 74 | print("multiboot1: Loading executable `%#`...\n", kernel_path); |
| 75 | |
| 76 | if ((kernel_file = uri_open(kernel_path, MEMMAP_KERNEL_AND_MODULES, false |
| 77 | #if defined (__i386__) |
| 78 | , NULL, NULL |
| 79 | #endif |
| 80 | )) == NULL) |
| 81 | panic(true, "multiboot1: Failed to open executable with path `%#`. Is the path correct?", kernel_path); |
| 82 | |
| 83 | uint8_t *kernel = kernel_file->fd; |
| 84 | |
| 85 | size_t kernel_file_size = kernel_file->size; |
| 86 | |
| 87 | #if defined (UEFI) |
| 88 | tpm_measure_path(TPM_PCR_BOOT_AUTH, TPM_EV_IPL, "path: ", kernel_path); |
| 89 | tpm_measure(TPM_PCR_LOADED_IMAGES, TPM_EV_IPL, |
| 90 | kernel, kernel_file_size, "path: ", kernel_path); |
| 91 | #endif |
| 92 | |
| 93 | fclose(kernel_file); |
| 94 | |
| 95 | struct multiboot1_header header = {0}; |
| 96 | size_t header_offset = 0; |
| 97 | |
| 98 | // Per Multiboot spec, header must be within first 8192 bytes and 4-byte aligned. |
| 99 | // Ensure we don't read past end of file when checking magic or copying header. |
| 100 | size_t search_limit = 8192; |
| 101 | if (kernel_file_size < sizeof(struct multiboot1_header)) { |
| 102 | panic(true, "multiboot1: Kernel file too small to contain header"); |
| 103 | } |
| 104 | if (search_limit > kernel_file_size - sizeof(struct multiboot1_header)) { |
| 105 | search_limit = kernel_file_size - sizeof(struct multiboot1_header); |
| 106 | } |
| 107 | |
| 108 | for (header_offset = 0; header_offset <= search_limit; header_offset += 4) { |
| 109 | uint32_t v = *(uint32_t *)(kernel + header_offset); |
| 110 | |
| 111 | if (v == MULTIBOOT1_HEADER_MAGIC) { |
| 112 | memcpy(&header, kernel + header_offset, sizeof(header)); |
| 113 | break; |
| 114 | } |
| 115 | } |
| 116 | |
| 117 | if (header.magic != MULTIBOOT1_HEADER_MAGIC) { |
| 118 | panic(true, "multiboot1: Invalid magic"); |
| 119 | } |
| 120 | |
| 121 | if (header.magic + header.flags + header.checksum) |
| 122 | panic(true, "multiboot1: Header checksum is invalid"); |
| 123 | |
| 124 | bool section_hdr_info_valid = false; |
| 125 | struct elf_section_hdr_info section_hdr_info = {0}; |
| 126 | |
| 127 | uint64_t entry_point; |
| 128 | struct elsewhere_range *ranges; |
| 129 | uint64_t ranges_count = 1; |
| 130 | |
| 131 | if (header.flags & (1 << 16)) { |
| 132 | if (header.load_addr > header.header_addr) |
| 133 | panic(true, "multiboot1: Illegal load address"); |
| 134 | |
| 135 | size_t addr_diff = header.header_addr - header.load_addr; |
| 136 | if (addr_diff > header_offset) |
| 137 | panic(true, "multiboot1: Address tag offset underflow"); |
| 138 | |
| 139 | size_t load_src = header_offset - addr_diff; |
| 140 | |
| 141 | size_t load_size; |
| 142 | if (header.load_end_addr) { |
| 143 | if (header.load_end_addr < header.load_addr) |
| 144 | panic(true, "multiboot1: Load end address less than load address"); |
| 145 | load_size = header.load_end_addr - header.load_addr; |
| 146 | } else { |
| 147 | if (load_src > kernel_file_size) |
| 148 | panic(true, "multiboot1: Load source exceeds kernel file size"); |
| 149 | load_size = kernel_file_size - load_src; |
| 150 | } |
| 151 | |
| 152 | uint32_t bss_size = 0; |
| 153 | if (header.bss_end_addr) { |
| 154 | uintptr_t bss_addr = CHECKED_ADD((uintptr_t)header.load_addr, load_size, |
| 155 | panic(true, "multiboot1: load_addr + load_size overflow")); |
| 156 | if (header.bss_end_addr < bss_addr) |
| 157 | panic(true, "multiboot1: Illegal bss end address"); |
| 158 | |
| 159 | bss_size = header.bss_end_addr - bss_addr; |
| 160 | } |
| 161 | |
| 162 | if (load_src > kernel_file_size || load_size > kernel_file_size - load_src) { |
| 163 | panic(true, "multiboot1: load_src + load_size exceeds kernel file size"); |
| 164 | } |
| 165 | |
| 166 | size_t full_size = CHECKED_ADD(load_size, bss_size, |
| 167 | panic(true, "multiboot1: load_size + bss_size overflow")); |
| 168 | |
| 169 | void *elsewhere = ext_mem_alloc(full_size); |
| 170 | |
| 171 | memcpy(elsewhere, kernel + load_src, load_size); |
| 172 | |
| 173 | entry_point = header.entry_addr; |
| 174 | |
| 175 | ranges = ext_mem_alloc(sizeof(struct elsewhere_range)); |
| 176 | |
| 177 | ranges->elsewhere = (uintptr_t)elsewhere; |
| 178 | ranges->target = header.load_addr; |
| 179 | ranges->length = full_size; |
| 180 | } else { |
| 181 | int bits = elf_bits(kernel, kernel_file_size); |
| 182 | |
| 183 | switch (bits) { |
| 184 | case 32: |
| 185 | if (!elf32_load_elsewhere(kernel, kernel_file_size, &entry_point, &ranges)) |
| 186 | panic(true, "multiboot1: ELF32 load failure"); |
| 187 | |
| 188 | section_hdr_info = elf32_section_hdr_info(kernel, kernel_file_size); |
| 189 | section_hdr_info_valid = true; |
| 190 | break; |
| 191 | case 64: { |
| 192 | if (!elf64_load_elsewhere(kernel, kernel_file_size, &entry_point, &ranges)) |
| 193 | panic(true, "multiboot1: ELF64 load failure"); |
| 194 | |
| 195 | section_hdr_info = elf64_section_hdr_info(kernel, kernel_file_size); |
| 196 | section_hdr_info_valid = true; |
| 197 | break; |
| 198 | } |
| 199 | default: |
| 200 | panic(true, "multiboot1: Invalid ELF file bitness"); |
| 201 | } |
| 202 | } |
| 203 | |
| 204 | size_t n_modules; |
| 205 | size_t modules_cmdlines_size = 0; |
| 206 | |
| 207 | for (n_modules = 0;; n_modules++) { |
| 208 | struct conf_tuple conf_tuple = config_get_tuple(config, n_modules, "MODULE_PATH", "MODULE_STRING"); |
| 209 | if (!conf_tuple.value1) break; |
| 210 | |
| 211 | char *module_cmdline = conf_tuple.value2; |
| 212 | if (!module_cmdline) module_cmdline = ""; |
| 213 | modules_cmdlines_size += ALIGN_UP(strlen(module_cmdline) + 1, 16, panic(true, "multiboot: info size overflow")); |
| 214 | } |
| 215 | |
| 216 | size_t mb1_info_size = get_multiboot1_info_size( |
| 217 | cmdline, |
| 218 | n_modules, |
| 219 | modules_cmdlines_size, |
| 220 | section_hdr_info_valid ? section_hdr_info.section_entry_size : 0, |
| 221 | section_hdr_info_valid ? section_hdr_info.num : 0 |
| 222 | ); |
| 223 | |
| 224 | // Realloc elsewhere ranges to include mb1 info, modules, and elf sections |
| 225 | uint64_t ranges_max = ranges_count |
| 226 | + 1 /* mb1 info range */ |
| 227 | + n_modules |
| 228 | + (section_hdr_info_valid ? section_hdr_info.num : 0); |
| 229 | struct elsewhere_range *new_ranges = ext_mem_alloc_counted(ranges_max, sizeof(struct elsewhere_range)); |
| 230 | |
| 231 | memcpy(new_ranges, ranges, sizeof(struct elsewhere_range) * ranges_count); |
| 232 | pmm_free(ranges, sizeof(struct elsewhere_range) * ranges_count); |
| 233 | ranges = new_ranges; |
| 234 | |
| 235 | // GRUB allocates boot info at 0x10000, *except* if the kernel happens |
| 236 | // to overlap this region, then it gets moved to right after the |
| 237 | // kernel, or whichever PHDR happens to sit at 0x10000. |
| 238 | // Allocate it wherever, then move it to where GRUB puts it |
| 239 | // afterwards. |
| 240 | |
| 241 | // Elsewhere append mb1 info *after* kernel but *before* modules. |
| 242 | void *mb1_info_raw = ext_mem_alloc(mb1_info_size); |
| 243 | uint64_t mb1_info_final_loc = 0x10000; |
| 244 | |
| 245 | if (!elsewhere_append(true /* flexible target */, |
| 246 | ranges, &ranges_count, ranges_max, |
| 247 | mb1_info_raw, &mb1_info_final_loc, mb1_info_size)) { |
| 248 | panic(true, "multiboot1: Cannot allocate mb1 info"); |
| 249 | } |
| 250 | |
| 251 | size_t mb1_info_slide = (size_t)mb1_info_raw - mb1_info_final_loc; |
| 252 | |
| 253 | struct multiboot1_info *multiboot1_info = |
| 254 | mb1_info_alloc(&mb1_info_raw, sizeof(struct multiboot1_info)); |
| 255 | |
| 256 | if (section_hdr_info_valid == true) { |
| 257 | size_t section_table_size = CHECKED_MUL(section_hdr_info.section_entry_size, section_hdr_info.num, |
| 258 | panic(true, "multiboot1: ELF section table size overflow")); |
| 259 | if (section_hdr_info.section_offset > kernel_file_size || |
| 260 | section_table_size > kernel_file_size - section_hdr_info.section_offset) { |
| 261 | panic(true, "multiboot1: ELF section headers out of bounds"); |
| 262 | } |
| 263 | |
| 264 | multiboot1_info->elf_sect.num = section_hdr_info.num; |
| 265 | multiboot1_info->elf_sect.size = section_hdr_info.section_entry_size; |
| 266 | multiboot1_info->elf_sect.shndx = section_hdr_info.str_section_idx; |
| 267 | |
| 268 | void *sections = mb1_info_alloc(&mb1_info_raw, section_table_size); |
| 269 | |
| 270 | multiboot1_info->elf_sect.addr = (uintptr_t)sections - mb1_info_slide; |
| 271 | |
| 272 | memcpy(sections, kernel + section_hdr_info.section_offset, section_table_size); |
| 273 | |
| 274 | int bits = elf_bits(kernel, kernel_file_size); |
| 275 | |
| 276 | if ((bits == 64 && section_hdr_info.section_entry_size < sizeof(struct elf64_shdr)) || |
| 277 | (bits == 32 && section_hdr_info.section_entry_size < sizeof(struct elf32_shdr))) { |
| 278 | panic(true, "multiboot1: ELF section entry size too small"); |
| 279 | } |
| 280 | |
| 281 | for (size_t i = 0; i < section_hdr_info.num; i++) { |
| 282 | if (bits == 64) { |
| 283 | struct elf64_shdr *shdr = (void *)sections + i * section_hdr_info.section_entry_size; |
| 284 | |
| 285 | if (shdr->sh_addr != 0 || shdr->sh_size == 0) { |
| 286 | continue; |
| 287 | } |
| 288 | |
| 289 | if (shdr->sh_offset > kernel_file_size || |
| 290 | shdr->sh_size > kernel_file_size - shdr->sh_offset) { |
| 291 | continue; |
| 292 | } |
| 293 | |
| 294 | uint64_t section = (uint64_t)-1; /* no target preference, use top */ |
| 295 | |
| 296 | if (!elsewhere_append(true /* flexible target */, |
| 297 | ranges, &ranges_count, ranges_max, |
| 298 | kernel + shdr->sh_offset, §ion, shdr->sh_size)) { |
| 299 | panic(true, "multiboot1: Cannot allocate elf sections"); |
| 300 | } |
| 301 | |
| 302 | shdr->sh_addr = section; |
| 303 | } else { |
| 304 | struct elf32_shdr *shdr = (void *)sections + i * section_hdr_info.section_entry_size; |
| 305 | |
| 306 | if (shdr->sh_addr != 0 || shdr->sh_size == 0) { |
| 307 | continue; |
| 308 | } |
| 309 | |
| 310 | if (shdr->sh_offset > kernel_file_size || |
| 311 | shdr->sh_size > kernel_file_size - shdr->sh_offset) { |
| 312 | continue; |
| 313 | } |
| 314 | |
| 315 | uint64_t section = (uint64_t)-1; /* no target preference, use top */ |
| 316 | |
| 317 | if (!elsewhere_append(true /* flexible target */, |
| 318 | ranges, &ranges_count, ranges_max, |
| 319 | kernel + shdr->sh_offset, §ion, shdr->sh_size)) { |
| 320 | panic(true, "multiboot1: Cannot allocate elf sections"); |
| 321 | } |
| 322 | |
| 323 | shdr->sh_addr = section; |
| 324 | } |
| 325 | } |
| 326 | |
| 327 | multiboot1_info->flags |= (1 << 5); |
| 328 | } |
| 329 | |
| 330 | if (n_modules) { |
| 331 | struct multiboot1_module *mods = |
| 332 | mb1_info_alloc(&mb1_info_raw, sizeof(struct multiboot1_module) * n_modules); |
| 333 | |
| 334 | multiboot1_info->mods_count = n_modules; |
| 335 | multiboot1_info->mods_addr = (size_t)mods - mb1_info_slide; |
| 336 | |
| 337 | for (size_t i = 0; i < n_modules; i++) { |
| 338 | struct multiboot1_module *m = mods + i; |
| 339 | |
| 340 | struct conf_tuple conf_tuple = config_get_tuple(config, i, "MODULE_PATH", "MODULE_STRING"); |
| 341 | char *module_path = conf_tuple.value1; |
| 342 | if (module_path == NULL) |
| 343 | panic(true, "multiboot1: Module disappeared unexpectedly"); |
| 344 | |
| 345 | print("multiboot1: Loading module `%#`...\n", module_path); |
| 346 | |
| 347 | struct file_handle *f; |
| 348 | if ((f = uri_open(module_path, MEMMAP_BOOTLOADER_RECLAIMABLE, false |
| 349 | #if defined (__i386__) |
| 350 | , NULL, NULL |
| 351 | #endif |
| 352 | )) == NULL) |
| 353 | panic(true, "multiboot1: Failed to open module with path `%#`. Is the path correct?", module_path); |
| 354 | |
| 355 | char *module_cmdline = conf_tuple.value2; |
| 356 | if (module_cmdline == NULL) { |
| 357 | module_cmdline = ""; |
| 358 | } |
| 359 | char *lowmem_modstr = mb1_info_alloc(&mb1_info_raw, strlen(module_cmdline) + 1); |
| 360 | strcpy(lowmem_modstr, module_cmdline); |
| 361 | |
| 362 | void *module_addr = f->fd; |
| 363 | uint64_t module_target = (uint64_t)-1; /* no target preference, use top */ |
| 364 | |
| 365 | #if defined (UEFI) |
| 366 | tpm_measure_path(TPM_PCR_BOOT_AUTH, TPM_EV_IPL, "module_path: ", module_path); |
| 367 | tpm_measure(TPM_PCR_LOADED_IMAGES, TPM_EV_IPL, |
| 368 | module_addr, f->size, "module_path: ", module_path); |
| 369 | #endif |
| 370 | |
| 371 | if (!elsewhere_append(true /* flexible target */, |
| 372 | ranges, &ranges_count, ranges_max, |
| 373 | module_addr, &module_target, f->size)) { |
| 374 | panic(true, "multiboot1: Cannot allocate module"); |
| 375 | } |
| 376 | |
| 377 | m->begin = module_target; |
| 378 | m->end = m->begin + f->size; |
| 379 | m->cmdline = (uint32_t)(size_t)lowmem_modstr - mb1_info_slide; |
| 380 | m->pad = 0; |
| 381 | |
| 382 | fclose(f); |
| 383 | |
| 384 | if (verbose) { |
| 385 | print("multiboot1: Requested module %u:\n", (uint32_t)i); |
| 386 | print(" Path: %s\n", module_path); |
| 387 | print(" String: \"%s\"\n", module_cmdline ?: ""); |
| 388 | print(" Begin: %x\n", m->begin); |
| 389 | print(" End: %x\n", m->end); |
| 390 | } |
| 391 | } |
| 392 | |
| 393 | multiboot1_info->flags |= (1 << 3); |
| 394 | } |
| 395 | |
| 396 | char *lowmem_cmdline = mb1_info_alloc(&mb1_info_raw, strlen(cmdline) + 1); |
| 397 | strcpy(lowmem_cmdline, cmdline); |
| 398 | multiboot1_info->cmdline = (uint32_t)(size_t)lowmem_cmdline - mb1_info_slide; |
| 399 | multiboot1_info->flags |= (1 << 2); |
| 400 | |
| 401 | char *bootload_name = LIMINE_BRAND; |
| 402 | char *lowmem_bootname = mb1_info_alloc(&mb1_info_raw, strlen(bootload_name) + 1); |
| 403 | strcpy(lowmem_bootname, bootload_name); |
| 404 | |
| 405 | multiboot1_info->bootloader_name = (uint32_t)(size_t)lowmem_bootname - mb1_info_slide; |
| 406 | multiboot1_info->flags |= (1 << 9); |
| 407 | |
| 408 | term_notready(); |
| 409 | |
| 410 | size_t req_width = 0; |
| 411 | size_t req_height = 0; |
| 412 | size_t req_bpp = 0; |
| 413 | #if defined (BIOS) |
| 414 | { |
| 415 | char *textmode_str = config_get_value(config, 0, "TEXTMODE"); |
| 416 | bool textmode = textmode_str != NULL && strcmp(textmode_str, "yes") == 0; |
| 417 | if (textmode) { |
| 418 | goto textmode; |
| 419 | } |
| 420 | } |
| 421 | #endif |
| 422 | |
| 423 | |
| 424 | if (header.flags & (1 << 2)) { |
| 425 | req_width = header.fb_width; |
| 426 | req_height = header.fb_height; |
| 427 | req_bpp = header.fb_bpp; |
| 428 | |
| 429 | if (header.fb_mode == 0) { |
| 430 | #if defined (UEFI) |
| 431 | modeset:; |
| 432 | #endif |
| 433 | char *resolution = config_get_value(config, 0, "RESOLUTION"); |
| 434 | if (resolution != NULL) |
| 435 | parse_resolution(&req_width, &req_height, &req_bpp, resolution); |
| 436 | |
| 437 | struct fb_info *fbs; |
| 438 | size_t fbs_count; |
| 439 | fb_init(&fbs, &fbs_count, req_width, req_height, req_bpp, false, false); |
| 440 | if (fbs_count == 0) { |
| 441 | #if defined (UEFI) |
| 442 | goto skip_modeset; |
| 443 | #elif defined (BIOS) |
| 444 | textmode: |
| 445 | vga_textmode_init(false); |
| 446 | |
| 447 | multiboot1_info->fb_addr = 0xb8000; |
| 448 | multiboot1_info->fb_width = 80; |
| 449 | multiboot1_info->fb_height = 25; |
| 450 | multiboot1_info->fb_bpp = 16; |
| 451 | multiboot1_info->fb_pitch = 2 * 80; |
| 452 | multiboot1_info->fb_type = 2; |
| 453 | #endif |
| 454 | } else { |
| 455 | multiboot1_info->fb_addr = (uint64_t)fbs[0].framebuffer_addr; |
| 456 | multiboot1_info->fb_width = fbs[0].framebuffer_width; |
| 457 | multiboot1_info->fb_height = fbs[0].framebuffer_height; |
| 458 | multiboot1_info->fb_bpp = fbs[0].framebuffer_bpp; |
| 459 | multiboot1_info->fb_pitch = fbs[0].framebuffer_pitch; |
| 460 | multiboot1_info->fb_type = 1; |
| 461 | multiboot1_info->fb_red_mask_size = fbs[0].red_mask_size; |
| 462 | multiboot1_info->fb_red_mask_shift = fbs[0].red_mask_shift; |
| 463 | multiboot1_info->fb_green_mask_size = fbs[0].green_mask_size; |
| 464 | multiboot1_info->fb_green_mask_shift = fbs[0].green_mask_shift; |
| 465 | multiboot1_info->fb_blue_mask_size = fbs[0].blue_mask_size; |
| 466 | multiboot1_info->fb_blue_mask_shift = fbs[0].blue_mask_shift; |
| 467 | } |
| 468 | } else { |
| 469 | #if defined (UEFI) |
| 470 | print("multiboot1: Warning: Cannot use text mode with UEFI\n"); |
| 471 | goto modeset; |
| 472 | #elif defined (BIOS) |
| 473 | goto textmode; |
| 474 | #endif |
| 475 | } |
| 476 | |
| 477 | multiboot1_info->flags |= (1 << 12); |
| 478 | |
| 479 | #if defined (UEFI) |
| 480 | skip_modeset:; |
| 481 | #endif |
| 482 | } else { |
| 483 | #if defined (UEFI) |
| 484 | panic(true, "multiboot1: Cannot use text mode with UEFI."); |
| 485 | #elif defined (BIOS) |
| 486 | vga_textmode_init(false); |
| 487 | #endif |
| 488 | } |
| 489 | |
| 490 | // Load relocation stub where it won't get overwritten (hopefully) |
| 491 | size_t reloc_stub_size = (size_t)multiboot_reloc_stub_end - (size_t)multiboot_reloc_stub; |
| 492 | void *reloc_stub = ext_mem_alloc(reloc_stub_size); |
| 493 | memcpy(reloc_stub, multiboot_reloc_stub, reloc_stub_size); |
| 494 | |
| 495 | #if defined (UEFI) |
| 496 | efi_exit_boot_services(); |
| 497 | #endif |
| 498 | |
| 499 | size_t mb_mmap_count; |
| 500 | struct memmap_entry *raw_memmap = get_raw_memmap(&mb_mmap_count); |
| 501 | |
| 502 | if (mb_mmap_count > MEMMAP_MAX) { |
| 503 | panic(false, "multiboot1: Too many memory map entries."); |
| 504 | } |
| 505 | |
| 506 | size_t mb_mmap_len = mb_mmap_count * sizeof(struct multiboot1_mmap_entry); |
| 507 | struct multiboot1_mmap_entry *mmap = mb1_info_alloc(&mb1_info_raw, mb_mmap_len); |
| 508 | |
| 509 | // Multiboot is bad and passes raw memmap. We do the same to support it. |
| 510 | for (size_t i = 0; i < mb_mmap_count; i++) { |
| 511 | mmap[i].size = sizeof(struct multiboot1_mmap_entry) - 4; |
| 512 | mmap[i].addr = raw_memmap[i].base; |
| 513 | mmap[i].len = raw_memmap[i].length; |
| 514 | mmap[i].type = raw_memmap[i].type; |
| 515 | } |
| 516 | |
| 517 | struct meminfo memory_info = mmap_get_info(mb_mmap_count, raw_memmap); |
| 518 | |
| 519 | // Convert the uppermem and lowermem fields from bytes to |
| 520 | // KiB. |
| 521 | multiboot1_info->mem_lower = memory_info.lowermem / 1024; |
| 522 | multiboot1_info->mem_upper = memory_info.uppermem / 1024; |
| 523 | |
| 524 | multiboot1_info->mmap_length = mb_mmap_len; |
| 525 | multiboot1_info->mmap_addr = (uint32_t)(size_t)mmap - mb1_info_slide; |
| 526 | multiboot1_info->flags |= (1 << 0) | (1 << 6); |
| 527 | |
| 528 | if (rdmsr(0x1b) & (1 << 10)) { |
| 529 | if (x2apic_disable()) { |
| 530 | printv("multiboot1: Firmware had x2APIC enabled, reverted to xAPIC mode\n"); |
| 531 | } else { |
| 532 | printv("multiboot1: Firmware has x2APIC enabled and it could not be disabled\n"); |
| 533 | } |
| 534 | } |
| 535 | |
| 536 | iommu_disable_all(); |
| 537 | |
| 538 | irq_flush_type = IRQ_PIC_ONLY_FLUSH; |
| 539 | |
| 540 | common_spinup(multiboot_spinup_32, 6, |
| 541 | (uint32_t)(uintptr_t)reloc_stub, (uint32_t)0x2badb002, |
| 542 | (uint32_t)mb1_info_final_loc, (uint32_t)entry_point, |
| 543 | (uint32_t)(uintptr_t)ranges, (uint32_t)ranges_count); |
| 544 | } |
| 545 | |
| 546 | #endif |