Beispiel #1
0
/**
 * 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(&regs);
  regs.eax = INT1A_PXE_INSTALL_CHECK;
  bios_call(0x1a, &regs);
  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;
}
Beispiel #2
0
/** 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);
}
Beispiel #3
0
/** 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;
}
Beispiel #4
0
/**
 * 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);
}
Beispiel #5
0
/**
 * 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);
}
Beispiel #6
0
/** 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;
}
Beispiel #7
0
/**
 * 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, &params->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);
}