/** * Get the PXE entry point address. * * @return Whether the entry point could be found. */ static bool get_entry_point(void) { bios_regs_t regs; pxenv_t *pxenv; pxe_t *pxe; /* Use the PXE installation check function. */ bios_regs_init(®s); regs.eax = INT1A_PXE_INSTALL_CHECK; bios_call(0x1a, ®s); if (regs.eax != INT1A_PXE_INSTALL_CHECK_RET || regs.eflags & X86_FLAGS_CF) { dprintf("pxe: loaded via PXE but PXE not available\n"); return false; } /* Get the PXENV+ structure. */ pxenv = (pxenv_t *)segoff_to_linear((regs.es << 16) | regs.bx); if (memcmp(pxenv->signature, PXENV_SIGNATURE, sizeof(pxenv->signature)) != 0) { boot_error("PXENV+ structure has incorrect signature"); } else if (!checksum_range(pxenv, pxenv->length)) { boot_error("PXENV+ structure is corrupt"); } /* Get the !PXE structure. */ pxe = (pxe_t *)segoff_to_linear(pxenv->pxe_ptr); if (memcmp(pxe->signature, PXE_SIGNATURE, sizeof(pxe->signature)) != 0) { boot_error("!PXE structure has incorrect signature"); } else if (!checksum_range(pxe, pxe->length)) { boot_error("!PXE structure is corrupt"); } pxe_entry_point = pxe->entry_point_16; return true; }
/** Load an EFI executable. * @param _loader Pointer to loader data. */ static __noreturn void efi_loader_load(void *_loader) { efi_loader_t *loader = _loader; void *buf; efi_handle_t image_handle; efi_loaded_image_t *image; efi_char16_t *str; efi_uintn_t str_size; efi_status_t status; status_t ret; /* Allocate a buffer to read the image into. */ buf = malloc_large(loader->handle->size); /* Read it in. */ ret = fs_read(loader->handle, buf, loader->handle->size, 0); if (ret != STATUS_SUCCESS) boot_error("Error reading EFI image: %pS", ret); /* Ask the firmware to load the image. */ status = efi_call( efi_boot_services->load_image, false, efi_image_handle, NULL, buf, loader->handle->size, &image_handle); if (status != EFI_SUCCESS) boot_error("Error loading EFI image (0x%zx)", status); /* Get the loaded image protocol. */ status = efi_get_loaded_image(image_handle, &image); if (status != EFI_SUCCESS) boot_error("Error getting loaded image protocol (0x%zx)", status); /* Try to identify the handle of the device the image was on. */ image->device_handle = efi_device_get_handle(loader->handle->mount->device); image->file_path = loader->efi_path; fs_close(loader->handle); /* Convert the arguments to UTF-16. FIXME: UTF-8 internally? */ str_size = (strlen(loader->args.string) + 1) * sizeof(*str); str = malloc(str_size); for (size_t i = 0; i < str_size / sizeof(*str); i++) str[i] = loader->args.string[i]; image->load_options = str; image->load_options_size = str_size; loader_preboot(); /* Free up extra memory we've allocated. */ efi_memory_cleanup(); /* Start the image. */ status = efi_call(efi_boot_services->start_image, image_handle, &str_size, &str); if (status != EFI_SUCCESS) dprintf("efi: loaded image returned status 0x%zx\n", status); /* We can't do anything here - the loaded image may have done things making * our internal state invalid. Just pass through the error to whatever * loaded us. */ efi_exit(status, str, str_size); }
/** Perform architecture-specific setup tasks. * @param loader LAOS loader data structure. */ void laos_arch_setup(laos_loader_t *loader) { laos_tag_pagetables_t *tag; target_ptr_t virt; uint32_t *l1, *l2; phys_ptr_t phys; /* Allocate a 1MB temporary mapping region for the kernel. */ if(!allocator_alloc(&loader->alloc, 0x100000, 0x100000, &virt)) boot_error("Unable to allocate temporary mapping region"); /* Create a second level table to cover the region. */ phys_memory_alloc(PAGE_SIZE, PAGE_SIZE, 0, 0, PHYS_MEMORY_PAGETABLES, 0, &phys); memset((void *)phys, 0, PAGE_SIZE); /* Insert it into the first level table, then point its last entry to * itself. */ l1 = (uint32_t *)loader->mmu->l1; l1[virt / 0x100000] = phys | (1<<0); l2 = (uint32_t *)phys; l2[255] = phys | (1<<1) | (1<<4); /* Add the pagetables tag. */ tag = laos_allocate_tag(loader, LAOS_TAG_PAGETABLES, sizeof(*tag)); tag->l1 = loader->mmu->l1; tag->mapping = virt; }
/** * Get the BOOTP reply packet. * * @return Pointer to BOOTP reply packet. */ static bootp_packet_t *get_bootp_packet(void) { pxenv_get_cached_info_t ci; /* Obtain the BOOTP reply packet. */ ci.packet_type = PXENV_PACKET_TYPE_CACHED_REPLY; ci.buffer = 0; ci.buffer_size = 0; if (pxe_call(PXENV_GET_CACHED_INFO, &ci) != PXENV_EXIT_SUCCESS) boot_error("Failed to get PXE BOOTP packet (0x%x)", ci.status); return (bootp_packet_t *)segoff_to_linear(ci.buffer); }
/** * Handle a configuration error. * * The action of this function depends on whether the error results from an * error in a configuration file, or if being executed from the shell. If the * error is in the file, the function will display an error UI and not return. * If the error occurred in the shell, it will print the error message to the * console and return. * * @param fmt Format string used to create the error message. * @param ... Arguments to substitute into format. */ void config_error(const char *fmt, ...) { va_list args; const char *cmd; va_start(args, fmt); /* Set current_command to NULL so that it will not be set if the handler * calls boot_error() and then the user goes into the shell. */ cmd = current_command; current_command = NULL; if (current_error_handler) { current_error_handler(cmd, fmt, args); } else { vsnprintf(temp_buf, TEMP_BUF_LEN, fmt, args); boot_error("%s", temp_buf); } va_end(args); }
/** Check a kernel image and determine the target type. * @param loader LAOS loader data structure. */ void laos_arch_check(laos_loader_t *loader) { if(!elf_check(loader->kernel, ELFCLASS32, ELFDATA2LSB, ELF_EM_ARM)) boot_error("Kernel image is not for this architecture"); loader->target = TARGET_TYPE_32BIT; }
/** * Load an x86 Linux kernel. * * @param loader Loader internal data. */ __noreturn void linux_arch_load(linux_loader_t *loader) { linux_params_t *params; size_t cmdline_size, setup_size, load_size; char *cmdline; void *virt; phys_ptr_t phys, initrd_max; list_t memory_map; status_t ret; static_assert(sizeof(linux_params_t) == PAGE_SIZE); static_assert(offsetof(linux_params_t, hdr) == LINUX_HEADER_OFFSET); // Allocate memory for the parameters data (the "zero page"). params = memory_alloc(sizeof(linux_params_t), 0, 0x10000, 0x90000, MEMORY_TYPE_RECLAIMABLE, 0, NULL); memset(params, 0, sizeof(*params)); // Read in the kernel header. ret = fs_read(loader->kernel, ¶ms->hdr, sizeof(params->hdr), offsetof(linux_params_t, hdr)); if (ret != STATUS_SUCCESS) boot_error("Error reading kernel header: %pS", ret); // Start populating required fields in the header. Don't set heap_end_ptr or // the CAN_USE_HEAP flag, as these appear to only be required by the 16-bit // entry point which we do not use. params->hdr.type_of_loader = 0xff; // Calculate the maximum command line size. cmdline_size = (params->hdr.version >= 0x0206) ? params->hdr.cmdline_size : 255; if (strlen(loader->cmdline) > cmdline_size) boot_error("Kernel command line is too long"); // Allocate memory for command line. cmdline_size = round_up(cmdline_size + 1, PAGE_SIZE); cmdline = memory_alloc(cmdline_size, 0, 0x10000, 0x90000, MEMORY_TYPE_RECLAIMABLE, 0, NULL); strncpy(cmdline, loader->cmdline, cmdline_size); cmdline[cmdline_size] = 0; params->hdr.cmd_line_ptr = (uint32_t)((ptr_t)cmdline); // Determine the setup code size. if (params->hdr.setup_sects) setup_size = (params->hdr.setup_sects + 1) * 512; else setup_size = 5 * 512; // Load size is total file size minus setup size. load_size = loader->kernel->size - setup_size; // Allocate memory for the kernel image. virt = allocate_kernel(params, load_size, &phys); if (!virt) boot_error("Insufficient memory available for kernel image"); params->hdr.code32_start = phys + params->hdr.code32_start - LINUX_BZIMAGE_ADDR; // Read in the kernel image. ret = fs_read(loader->kernel, virt, load_size, setup_size); if (ret != STATUS_SUCCESS) boot_error("Error reading kernel image: %pS", ret); // Load in the initrd(s). if (loader->initrd_size) { initrd_max = (params->hdr.version >= 0x0203) ? params->hdr.initrd_addr_max : 0x37ffffff; // It is recommended that the initrd be loaded as high as possible. virt = memory_alloc( round_up(loader->initrd_size, PAGE_SIZE), 0, 0x100000, initrd_max + 1, MEMORY_TYPE_MODULES, MEMORY_ALLOC_HIGH, &phys); dprintf( "linux: loading initrd to 0x%" PRIxPHYS " (size: 0x%zx, max: 0x%" PRIxPHYS ")\n", phys, loader->initrd_size, initrd_max); linux_initrd_load(loader, virt); params->hdr.ramdisk_image = phys; params->hdr.ramdisk_size = loader->initrd_size; } // Set the video mode. linux_video_set(loader); // Perform pre-boot tasks. loader_preboot(); // Get the final memory map and print it out for informational purposes. // Note that the memory_finalize() is necessary on EFI in order to free up // any internal allocations in the EFI memory map so that they will be free // to the kernel. dprintf("linux: final physical memory map:\n"); memory_finalize(&memory_map); memory_map_dump(&memory_map); // Get platform code to do any setup it needs and enter the kernel. // For BIOS, this will obtain information usually obtained by the real-mode // bootstrap when using the 16-bit boot protocol, then jump to the 32-bit // entry point. For EFI, this will enter the kernel using the handover // protocol. linux_platform_load(loader, params); }