int efi_load_image(const char *file, efi_loaded_image_t **loaded_image, efi_handle_t *h) { void *exe; size_t size; efi_handle_t handle; efi_status_t efiret = EFI_SUCCESS; exe = read_file(file, &size); if (!exe) return -EINVAL; efiret = BS->load_image(false, efi_parent_image, efi_device_path, exe, size, &handle); if (EFI_ERROR(efiret)) { pr_err("failed to LoadImage: %s\n", efi_strerror(efiret)); goto out; }; efiret = BS->open_protocol(handle, &efi_loaded_image_protocol_guid, (void **)loaded_image, efi_parent_image, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); if (EFI_ERROR(efiret)) { pr_err("failed to OpenProtocol: %s\n", efi_strerror(efiret)); BS->unload_image(handle); goto out; } *h = handle; out: memset(exe, 0, size); free(exe); return -efi_errno(efiret); }
/** * Reallocate external memory * * @v old_ptr Memory previously allocated by umalloc(), or UNULL * @v new_size Requested size * @ret new_ptr Allocated memory, or UNULL * * Calling realloc() with a new size of zero is a valid way to free a * memory block. */ static userptr_t efi_urealloc ( userptr_t old_ptr, size_t new_size ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; EFI_PHYSICAL_ADDRESS phys_addr; unsigned int new_pages, old_pages; userptr_t new_ptr = UNOWHERE; size_t old_size; EFI_STATUS efirc; /* Allocate new memory if necessary. If allocation fails, * return without touching the old block. */ if ( new_size ) { new_pages = ( EFI_SIZE_TO_PAGES ( new_size ) + 1 ); if ( ( efirc = bs->AllocatePages ( AllocateAnyPages, EfiBootServicesData, new_pages, &phys_addr ) ) != 0 ) { DBG ( "EFI could not allocate %d pages: %s\n", new_pages, efi_strerror ( efirc ) ); return UNULL; } assert ( phys_addr != 0 ); new_ptr = phys_to_user ( phys_addr + EFI_PAGE_SIZE ); copy_to_user ( new_ptr, -EFI_PAGE_SIZE, &new_size, sizeof ( new_size ) ); DBG ( "EFI allocated %d pages at %llx\n", new_pages, phys_addr ); } /* Copy across relevant part of the old data region (if any), * then free it. Note that at this point either (a) new_ptr * is valid, or (b) new_size is 0; either way, the memcpy() is * valid. */ if ( old_ptr && ( old_ptr != UNOWHERE ) ) { copy_from_user ( &old_size, old_ptr, -EFI_PAGE_SIZE, sizeof ( old_size ) ); memcpy_user ( new_ptr, 0, old_ptr, 0, ( (old_size < new_size) ? old_size : new_size )); old_pages = ( EFI_SIZE_TO_PAGES ( old_size ) + 1 ); phys_addr = user_to_phys ( old_ptr, -EFI_PAGE_SIZE ); if ( ( efirc = bs->FreePages ( phys_addr, old_pages ) ) != 0 ){ DBG ( "EFI could not free %d pages at %llx: %s\n", old_pages, phys_addr, efi_strerror ( efirc ) ); /* Not fatal; we have leaked memory but successfully * allocated (if asked to do so). */ } DBG ( "EFI freed %d pages at %llx\n", old_pages, phys_addr ); } return new_ptr; }
static int efi_execute_image(const char *file) { efi_handle_t handle; efi_loaded_image_t *loaded_image; efi_status_t efiret; struct linux_kernel_header *image_header; const char *options; int ret; ret = efi_load_image(file, &loaded_image, &handle); if (ret) return ret; image_header = (struct linux_kernel_header *)loaded_image->image_base; if (image_header->boot_flag == 0xAA55 && image_header->header == 0x53726448) { pr_debug("Linux kernel detected. Adding bootargs."); options = linux_bootargs_get(); pr_err("add linux options '%s'\n", options); loaded_image->load_options = xstrdup_char_to_wchar(options); loaded_image->load_options_size = (strlen(options) + 1) * sizeof(wchar_t); } efiret = BS->start_image(handle, NULL, NULL); if (EFI_ERROR(efiret)) pr_err("failed to StartImage: %s\n", efi_strerror(efiret)); BS->unload_image(handle); efi_connect_all(); efi_register_devices(); return -efi_errno(efiret); }
/** * Delay for a fixed number of microseconds * * @v usecs Number of microseconds for which to delay */ static void efi_udelay ( unsigned long usecs ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; EFI_STATUS efirc; if ( ( efirc = bs->Stall ( usecs ) ) != 0 ) { DBG ( "EFI could not delay for %ldus: %s\n", usecs, efi_strerror ( efirc ) ); /* Probably screwed */ } }
/** * String write to device * * @v io_addr I/O address * @v data Data buffer * @v size Size of values * @v count Number of values to write */ void efi_iowrites ( volatile void *io_addr, const void *data, size_t size, unsigned int count ) { EFI_CPU_IO_PROTOCOL_IO_MEM write; EFI_STATUS efirc; write = ( IS_PORT_ADDRESS ( io_addr ) ? cpu_io->Io.Write : cpu_io->Mem.Write ); if ( ( efirc = write ( cpu_io, efi_width ( size ), ( intptr_t ) io_addr, count, ( void * ) data ) ) != 0 ) { DBG ( "EFI I/O write at %p failed: %s\n", io_addr, efi_strerror ( efirc ) ); } }
/** * String read from device * * @v io_addr I/O address * @v data Data buffer * @v size Size of values * @v count Number of values to read */ void efi_ioreads ( volatile void *io_addr, void *data, size_t size, unsigned int count ) { EFI_CPU_IO_PROTOCOL_IO_MEM read; EFI_STATUS efirc; read = ( IS_PORT_ADDRESS ( io_addr ) ? cpu_io->Io.Read : cpu_io->Mem.Read ); if ( ( efirc = read ( cpu_io, efi_width ( size ), ( intptr_t ) io_addr, count, ( void * ) data ) ) != 0 ) { DBG ( "EFI I/O string read at %p failed: %s\n", io_addr, efi_strerror ( efirc ) ); } }
/** * Get current system time in ticks * * @ret ticks Current time, in ticks */ static unsigned long efi_currticks ( void ) { UINT64 time; EFI_STATUS efirc; /* Read CPU timer 0 (TSC) */ if ( ( efirc = cpu_arch->GetTimerValue ( cpu_arch, 0, &time, NULL ) ) != 0 ) { DBG ( "EFI could not read CPU timer: %s\n", efi_strerror ( efirc ) ); /* Probably screwed */ return -1UL; } return ( time >> EFI_TIMER0_SHIFT ); }
int efipci_write ( struct pci_device *pci, unsigned long location, unsigned long value ) { EFI_STATUS efirc; if ( ( efirc = efipci->Pci.Write ( efipci, EFIPCI_WIDTH ( location ), efipci_address ( pci, location ), 1, &value ) ) != 0 ) { DBG ( "EFIPCI config write to %02x:%02x.%x offset %02lx " "failed: %s\n", pci->bus, PCI_SLOT ( pci->devfn ), PCI_FUNC ( pci->devfn ), EFIPCI_OFFSET ( location ), efi_strerror ( efirc ) ); return -EIO; } return 0; }
/** * Read from device * * @v io_addr I/O address * @v size Size of value * @ret data Value read */ unsigned long long efi_ioread ( volatile void *io_addr, size_t size ) { EFI_CPU_IO_PROTOCOL_IO_MEM read; unsigned long long data = 0; EFI_STATUS efirc; read = ( IS_PORT_ADDRESS ( io_addr ) ? cpu_io->Io.Read : cpu_io->Mem.Read ); if ( ( efirc = read ( cpu_io, efi_width ( size ), ( intptr_t ) io_addr, 1, ( void * ) &data ) ) != 0 ) { DBG ( "EFI I/O read at %p failed: %s\n", io_addr, efi_strerror ( efirc ) ); return -1ULL; } return data; }
/** * Probe EFI image * * @v image EFI file * @ret rc Return status code */ static int efi_image_probe ( struct image *image ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; EFI_HANDLE handle; EFI_STATUS efirc; /* Attempt loading image */ if ( ( efirc = bs->LoadImage ( FALSE, efi_image_handle, NULL, user_to_virt ( image->data, 0 ), image->len, &handle ) ) != 0 ) { /* Not an EFI image */ DBGC ( image, "EFIIMAGE %p could not load: %s\n", image, efi_strerror ( efirc ) ); return -ENOEXEC; } /* Unload the image. We can't leave it loaded, because we * have no "unload" operation. */ bs->UnloadImage ( handle ); return 0; }
static int efi_execute_image(const char *file) { void *exe; size_t size; efi_handle_t handle; efi_status_t efiret; const char *options; efi_loaded_image_t *loaded_image; exe = read_file(file, &size); if (!exe) return -EINVAL; efiret = BS->load_image(false, efi_parent_image, efi_device_path, exe, size, &handle); if (EFI_ERROR(efiret)) { pr_err("failed to LoadImage: %s\n", efi_strerror(efiret)); return -efi_errno(efiret);; }; efiret = BS->open_protocol(handle, &efi_loaded_image_protocol_guid, (void **)&loaded_image, efi_parent_image, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); if (EFI_ERROR(efiret)) return -efi_errno(efiret); options = linux_bootargs_get(); loaded_image->load_options = strdup_char_to_wchar(options); loaded_image->load_options_size = (strlen(options) + 1) * sizeof(wchar_t); efiret = BS->start_image(handle, NULL, NULL); efi_connect_all(); efi_register_devices(); return 0; }
/** * Execute EFI image * * @v image EFI image * @ret rc Return status code */ static int efi_image_exec ( struct image *image ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; struct efi_snp_device *snpdev; EFI_DEVICE_PATH_PROTOCOL *path; union { EFI_LOADED_IMAGE_PROTOCOL *image; void *interface; } loaded; EFI_HANDLE handle; wchar_t *cmdline; EFI_STATUS efirc; int rc; /* Find an appropriate device handle to use */ snpdev = last_opened_snpdev(); if ( ! snpdev ) { DBGC ( image, "EFIIMAGE %p could not identify SNP device\n", image ); rc = -ENODEV; goto err_no_snpdev; } /* Install file I/O protocols */ if ( ( rc = efi_file_install ( &snpdev->handle ) ) != 0 ) { DBGC ( image, "EFIIMAGE %p could not install file protocol: " "%s\n", image, strerror ( rc ) ); goto err_file_install; } /* Install iPXE download protocol */ if ( ( rc = efi_download_install ( &snpdev->handle ) ) != 0 ) { DBGC ( image, "EFIIMAGE %p could not install iPXE download " "protocol: %s\n", image, strerror ( rc ) ); goto err_download_install; } /* Create device path for image */ path = efi_image_path ( image, &snpdev->path ); if ( ! path ) { DBGC ( image, "EFIIMAGE %p could not create device path\n", image ); rc = -ENOMEM; goto err_image_path; } /* Create command line for image */ cmdline = efi_image_cmdline ( image ); if ( ! cmdline ) { DBGC ( image, "EFIIMAGE %p could not create command line\n", image ); rc = -ENOMEM; goto err_cmdline; } /* Attempt loading image */ if ( ( efirc = bs->LoadImage ( FALSE, efi_image_handle, path, user_to_virt ( image->data, 0 ), image->len, &handle ) ) != 0 ) { /* Not an EFI image */ DBGC ( image, "EFIIMAGE %p could not load: %s\n", image, efi_strerror ( efirc ) ); rc = -ENOEXEC; goto err_load_image; } /* Get the loaded image protocol for the newly loaded image */ efirc = bs->OpenProtocol ( handle, &efi_loaded_image_protocol_guid, &loaded.interface, efi_image_handle, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL ); if ( efirc ) { /* Should never happen */ rc = EFIRC_TO_RC ( efirc ); goto err_open_protocol; } /* Sanity checks */ assert ( loaded.image->ParentHandle == efi_image_handle ); assert ( loaded.image->DeviceHandle == snpdev->handle ); assert ( loaded.image->LoadOptionsSize == 0 ); assert ( loaded.image->LoadOptions == NULL ); /* Set command line */ loaded.image->LoadOptions = cmdline; loaded.image->LoadOptionsSize = ( ( wcslen ( cmdline ) + 1 /* NUL */ ) * sizeof ( wchar_t ) ); /* Start the image */ if ( ( efirc = bs->StartImage ( handle, NULL, NULL ) ) != 0 ) { DBGC ( image, "EFIIMAGE %p returned with status %s\n", image, efi_strerror ( efirc ) ); rc = EFIRC_TO_RC ( efirc ); goto err_start_image; } /* Success */ rc = 0; err_start_image: err_open_protocol: /* Unload the image. We can't leave it loaded, because we * have no "unload" operation. */ if ( ( efirc = bs->UnloadImage ( handle ) ) != 0 ) { DBGC ( image, "EFIIMAGE %p could not unload: %s\n", image, efi_strerror ( efirc ) ); } err_load_image: free ( cmdline ); err_cmdline: free ( path ); err_image_path: efi_download_uninstall ( snpdev->handle ); err_download_install: efi_file_uninstall ( snpdev->handle ); err_file_install: err_no_snpdev: return rc; }