static grub_err_t grub_linuxefi_unload (void) { grub_dl_unref (my_mod); loaded = 0; if (initrd_mem) grub_efi_free_pages((grub_efi_physical_address_t)initrd_mem, BYTES_TO_PAGES(params->ramdisk_size)); if (linux_cmdline) grub_efi_free_pages((grub_efi_physical_address_t)linux_cmdline, BYTES_TO_PAGES(params->cmdline_size + 1)); if (kernel_mem) grub_efi_free_pages((grub_efi_physical_address_t)kernel_mem, BYTES_TO_PAGES(kernel_size)); if (params) grub_efi_free_pages((grub_efi_physical_address_t)params, BYTES_TO_PAGES(16384)); return GRUB_ERR_NONE; }
/* Allocate pages below a specified address */ void * grub_efi_allocate_pages_max (grub_efi_physical_address_t max, grub_efi_uintn_t pages) { grub_efi_status_t status; grub_efi_boot_services_t *b; grub_efi_physical_address_t address = max; if (max > 0xffffffff) return 0; b = grub_efi_system_table->boot_services; status = efi_call_4 (b->allocate_pages, GRUB_EFI_ALLOCATE_MAX_ADDRESS, GRUB_EFI_LOADER_DATA, pages, &address); if (status != GRUB_EFI_SUCCESS) return 0; if (address == 0) { /* Uggh, the address 0 was allocated... This is too annoying, so reallocate another one. */ address = max; status = efi_call_4 (b->allocate_pages, GRUB_EFI_ALLOCATE_MAX_ADDRESS, GRUB_EFI_LOADER_DATA, pages, &address); grub_efi_free_pages (0, pages); if (status != GRUB_EFI_SUCCESS) return 0; } return (void *) ((grub_addr_t) address); }
/* Allocate pages. Return the pointer to the first of allocated pages. */ void * grub_efi_allocate_pages_real (grub_efi_physical_address_t address, grub_efi_uintn_t pages, grub_efi_allocate_type_t alloctype, grub_efi_memory_type_t memtype) { grub_efi_status_t status; grub_efi_boot_services_t *b; /* Limit the memory access to less than 4GB for 32-bit platforms. */ if (address > GRUB_EFI_MAX_USABLE_ADDRESS) return 0; b = grub_efi_system_table->boot_services; status = efi_call_4 (b->allocate_pages, alloctype, memtype, pages, &address); if (status != GRUB_EFI_SUCCESS) return 0; if (address == 0) { /* Uggh, the address 0 was allocated... This is too annoying, so reallocate another one. */ address = GRUB_EFI_MAX_USABLE_ADDRESS; status = efi_call_4 (b->allocate_pages, alloctype, memtype, pages, &address); grub_efi_free_pages (0, pages); if (status != GRUB_EFI_SUCCESS) return 0; } return (void *) ((grub_addr_t) address); }
/* Allocate pages. Return the pointer to the first of allocated pages. */ void * grub_efi_allocate_pages (grub_efi_physical_address_t address, grub_efi_uintn_t pages) { grub_efi_allocate_type_t type; grub_efi_status_t status; grub_efi_boot_services_t *b; #if 1 /* Limit the memory access to less than 4GB for 32-bit platforms. */ if (address > GRUB_EFI_MAX_USABLE_ADDRESS) return 0; #endif #if 1 if (address == 0) { type = GRUB_EFI_ALLOCATE_MAX_ADDRESS; address = GRUB_EFI_MAX_USABLE_ADDRESS; } else type = GRUB_EFI_ALLOCATE_ADDRESS; #else if (address == 0) type = GRUB_EFI_ALLOCATE_ANY_PAGES; else type = GRUB_EFI_ALLOCATE_ADDRESS; #endif b = grub_efi_system_table->boot_services; status = efi_call_4 (b->allocate_pages, type, GRUB_EFI_LOADER_DATA, pages, &address); if (status != GRUB_EFI_SUCCESS) return 0; if (address == 0) { /* Uggh, the address 0 was allocated... This is too annoying, so reallocate another one. */ address = GRUB_EFI_MAX_USABLE_ADDRESS; status = efi_call_4 (b->allocate_pages, type, GRUB_EFI_LOADER_DATA, pages, &address); grub_efi_free_pages (0, pages); if (status != GRUB_EFI_SUCCESS) return 0; } return (void *) ((grub_addr_t) address); }
int grub_efi_get_memory_map (grub_efi_uintn_t *map_key, grub_efi_uintn_t *descriptor_size, grub_efi_uint32_t *descriptor_version) { grub_efi_status_t status; grub_efi_boot_services_t *b; grub_efi_uintn_t key; grub_efi_uint32_t version; grub_efi_uintn_t tmp_mmap_size; /* Allow some parameters to be missing. */ if (! map_key) map_key = &key; if (! descriptor_version) descriptor_version = &version; while (1) { b = grub_efi_system_table->boot_services; tmp_mmap_size = PAGES_TO_BYTES(mmap_pages); status = Call_Service_5 (b->get_memory_map, &tmp_mmap_size, mmap_buf, map_key, descriptor_size, descriptor_version); if (status == GRUB_EFI_SUCCESS) { mmap_size = tmp_mmap_size; return 1; } else if (status != GRUB_EFI_BUFFER_TOO_SMALL) return -1; /* we need a larger buffer */ if (mmap_buf) grub_efi_free_pages ((grub_addr_t) mmap_buf, mmap_pages); /* get 1 more page than we need, just in case */ mmap_pages = BYTES_TO_PAGES(tmp_mmap_size + 4095) + 1; mmap_buf = grub_efi_allocate_pages (0, mmap_pages); if (! mmap_buf) { mmap_pages = 0; grub_printf ("cannot allocate memory for memory map"); return -1; } } }
static grub_err_t grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), int argc, char *argv[]) { grub_file_t file = 0; struct linux_kernel_header lh; grub_ssize_t len, start, filelen; void *kernel = NULL; grub_dl_ref (my_mod); if (argc == 0) { grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); goto fail; } file = grub_file_open (argv[0]); if (! file) goto fail; filelen = grub_file_size (file); kernel = grub_malloc(filelen); if (!kernel) { grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("cannot allocate kernel buffer")); goto fail; } if (grub_file_read (file, kernel, filelen) != filelen) { grub_error (GRUB_ERR_FILE_READ_ERROR, N_("Can't read kernel %s"), argv[0]); goto fail; } grub_tpm_measure (kernel, filelen, GRUB_KERNEL_PCR); if (! grub_linuxefi_secure_validate (kernel, filelen)) { grub_error (GRUB_ERR_INVALID_COMMAND, N_("%s has invalid signature"), argv[0]); grub_free (kernel); goto fail; } params = grub_efi_allocate_pages_max (0x3fffffff, BYTES_TO_PAGES(16384)); if (! params) { grub_error (GRUB_ERR_OUT_OF_MEMORY, "cannot allocate kernel parameters"); goto fail; } grub_memset (params, 0, 16384); grub_memcpy (&lh, kernel, sizeof (lh)); if (lh.boot_flag != grub_cpu_to_le16 (0xaa55)) { grub_error (GRUB_ERR_BAD_OS, N_("invalid magic number")); goto fail; } if (lh.setup_sects > GRUB_LINUX_MAX_SETUP_SECTS) { grub_error (GRUB_ERR_BAD_OS, N_("too many setup sectors")); goto fail; } if (lh.version < grub_cpu_to_le16 (0x020b)) { grub_error (GRUB_ERR_BAD_OS, N_("kernel too old")); goto fail; } if (!lh.handover_offset) { grub_error (GRUB_ERR_BAD_OS, N_("kernel doesn't support EFI handover")); goto fail; } linux_cmdline = grub_efi_allocate_pages_max(0x3fffffff, BYTES_TO_PAGES(lh.cmdline_size + 1)); if (!linux_cmdline) { grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("can't allocate cmdline")); goto fail; } grub_memcpy (linux_cmdline, LINUX_IMAGE, sizeof (LINUX_IMAGE)); grub_create_loader_cmdline (argc, argv, linux_cmdline + sizeof (LINUX_IMAGE) - 1, lh.cmdline_size - (sizeof (LINUX_IMAGE) - 1)); grub_pass_verity_hash(&lh, linux_cmdline); lh.cmd_line_ptr = (grub_uint32_t)(grub_uint64_t)linux_cmdline; handover_offset = lh.handover_offset; start = (lh.setup_sects + 1) * 512; len = grub_file_size(file) - start; kernel_mem = grub_efi_allocate_pages(lh.pref_address, BYTES_TO_PAGES(lh.init_size)); if (!kernel_mem) kernel_mem = grub_efi_allocate_pages_max(0x3fffffff, BYTES_TO_PAGES(lh.init_size)); if (!kernel_mem) { grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("can't allocate kernel")); goto fail; } grub_memcpy (kernel_mem, (char *)kernel + start, len); grub_loader_set (grub_linuxefi_boot, grub_linuxefi_unload, 0); loaded=1; lh.code32_start = (grub_uint32_t)(grub_uint64_t) kernel_mem; grub_memcpy (params, &lh, 2 * 512); params->type_of_loader = 0x21; fail: if (file) grub_file_close (file); if (kernel) grub_free (kernel); if (grub_errno != GRUB_ERR_NONE) { grub_dl_unref (my_mod); loaded = 0; } if (linux_cmdline && !loaded) grub_efi_free_pages((grub_efi_physical_address_t)linux_cmdline, BYTES_TO_PAGES(lh.cmdline_size + 1)); if (kernel_mem && !loaded) grub_efi_free_pages((grub_efi_physical_address_t)kernel_mem, BYTES_TO_PAGES(kernel_size)); if (params && !loaded) grub_efi_free_pages((grub_efi_physical_address_t)params, BYTES_TO_PAGES(16384)); return grub_errno; }
static grub_err_t grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)), int argc, char *argv[]) { grub_file_t *files = 0; int i, nfiles = 0; grub_size_t size = 0; grub_uint8_t *ptr; if (argc == 0) { grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); goto fail; } if (!loaded) { grub_error (GRUB_ERR_BAD_ARGUMENT, N_("you need to load the kernel first")); goto fail; } files = grub_zalloc (argc * sizeof (files[0])); if (!files) goto fail; for (i = 0; i < argc; i++) { grub_file_filter_disable_compression (); files[i] = grub_file_open (argv[i]); if (! files[i]) goto fail; nfiles++; size += ALIGN_UP (grub_file_size (files[i]), 4); } initrd_mem = grub_efi_allocate_pages_max (0x3fffffff, BYTES_TO_PAGES(size)); if (!initrd_mem) { grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("can't allocate initrd")); goto fail; } params->ramdisk_size = size; params->ramdisk_image = (grub_uint32_t)(grub_uint64_t) initrd_mem; ptr = initrd_mem; for (i = 0; i < nfiles; i++) { grub_ssize_t cursize = grub_file_size (files[i]); if (grub_file_read (files[i], ptr, cursize) != cursize) { if (!grub_errno) grub_error (GRUB_ERR_FILE_READ_ERROR, N_("premature end of file %s"), argv[i]); goto fail; } grub_tpm_measure (ptr, cursize, GRUB_INITRD_PCR); ptr += cursize; grub_memset (ptr, 0, ALIGN_UP_OVERHEAD (cursize, 4)); ptr += ALIGN_UP_OVERHEAD (cursize, 4); } params->ramdisk_size = size; fail: for (i = 0; i < nfiles; i++) grub_file_close (files[i]); grub_free (files); if (initrd_mem && grub_errno) grub_efi_free_pages((grub_efi_physical_address_t)initrd_mem, BYTES_TO_PAGES(size)); return grub_errno; }
/* Allocate pages. Return the pointer to the first of allocated pages. */ static void * grub_efi_allocate_pages_real (grub_efi_physical_address_t address, grub_efi_uintn_t pages, grub_efi_memory_type_t memtype) { grub_efi_allocate_type_t type; grub_efi_status_t status; grub_efi_boot_services_t *b; /* Limit the memory access to less than 2GB to avoid 64bit * compatible problem of grub */ if (address > 0x7fffffff) return 0; if (address == 0) { type = GRUB_EFI_ALLOCATE_MAX_ADDRESS; address = 0x7fffffff; } else type = GRUB_EFI_ALLOCATE_ADDRESS; b = grub_efi_system_table->boot_services; status = Call_Service_4 (b->allocate_pages, type, memtype, pages, &address); if (status != GRUB_EFI_SUCCESS) { /* EFI_NOT_FOUND means the region was unavailable, which means we can probably just use it. This is only for hacks to start with */ if (memtype == GRUB_EFI_RUNTIME_SERVICES_DATA && status == GRUB_EFI_NOT_FOUND) return (void *) ((grub_addr_t) address); else return 0; } if (address == 0) { /* Uggh, the address 0 was allocated... This is too annoying, so reallocate another one. */ address = 0x7fffffff; status = Call_Service_4 (b->allocate_pages, type, GRUB_EFI_LOADER_DATA, pages, &address); grub_efi_free_pages (0, pages); if (status != GRUB_EFI_SUCCESS) return 0; } /* We don't want to free anything we've allocated for runtime */ if (allocated_pages && memtype != GRUB_EFI_RUNTIME_SERVICES_DATA) { unsigned i; for (i = 0; i < MAX_ALLOCATED_PAGES; i++) if (allocated_pages[i].addr == 0) { allocated_pages[i].addr = address; allocated_pages[i].num_pages = pages; break; } if (i == MAX_ALLOCATED_PAGES) { grub_printf ("too many page allocations"); return NULL; } } return (void *) ((grub_addr_t) address); }