#include #include #include #include #include #include #include #include #include #include #if defined (UEFI) EFI_SYSTEM_TABLE *gST; EFI_BOOT_SERVICES *gBS; EFI_RUNTIME_SERVICES *gRT; EFI_HANDLE efi_image_handle; EFI_MEMORY_DESCRIPTOR *efi_mmap = NULL; UINTN efi_mmap_size = 0, efi_desc_size = 0; UINT32 efi_desc_ver = 0; #endif bool editor_enabled = true; bool parse_resolution(size_t *width, size_t *height, size_t *bpp, const char *buf) { size_t res[3] = {0}; const char *first = buf; for (size_t i = 0; i < 3; i++) { const char *last; size_t x = strtoui(first, &last, 10); if (first == last) break; res[i] = x; if (*last == 0) break; first = last + 1; } if (res[0] == 0 || res[1] == 0) return false; if (res[2] == 0) res[2] = 32; *width = res[0], *height = res[1]; if (bpp != NULL) *bpp = res[2]; return true; } // This integer sqrt implementation has been adapted from: // https://stackoverflow.com/questions/1100090/looking-for-an-efficient-integer-square-root-algorithm-for-arm-thumb2 uint64_t sqrt(uint64_t a_nInput) { uint64_t op = a_nInput; uint64_t res = 0; uint64_t one = (uint64_t)1 << 62; // "one" starts at the highest power of four <= than the argument. while (one > op) { one >>= 2; } while (one != 0) { if (op >= res + one) { op = op - (res + one); res = res + 2 * one; } res >>= 1; one >>= 2; } return res; } size_t get_trailing_zeros(uint64_t val) { for (size_t i = 0; i < 64; i++) { if ((val & 1) != 0) { return i; } val >>= 1; } return 64; } uint32_t oct2bin(uint8_t *str, uint32_t max) { uint32_t value = 0; while (max-- > 0) { value <<= 3; value += *str++ - '0'; } return value; } uint32_t hex2bin(uint8_t *str, uint32_t size) { uint32_t value = 0; while (size-- > 0) { value <<= 4; if (*str >= '0' && *str <= '9') value += (uint32_t)((*str) - '0'); else if (*str >= 'A' && *str <= 'F') value += (uint32_t)((*str) - 'A' + 10); else if (*str >= 'a' && *str <= 'f') value += (uint32_t)((*str) - 'a' + 10); str++; } return value; } #if defined (UEFI) no_unwind bool efi_boot_services_exited = false; bool efi_exit_boot_services(void) { EFI_STATUS status; EFI_MEMORY_DESCRIPTOR tmp_mmap[1]; efi_mmap_size = sizeof(tmp_mmap); UINTN mmap_key = 0; gBS->GetMemoryMap(&efi_mmap_size, tmp_mmap, &mmap_key, &efi_desc_size, &efi_desc_ver); efi_mmap_size += 4096; status = gBS->FreePool(efi_mmap); if (status) { goto fail; } status = gBS->AllocatePool(EfiLoaderData, efi_mmap_size, (void **)&efi_mmap); if (status) { goto fail; } EFI_MEMORY_DESCRIPTOR *efi_copy; status = gBS->AllocatePool(EfiLoaderData, efi_mmap_size * 2, (void **)&efi_copy); if (status) { goto fail; } const size_t EFI_COPY_MAX_ENTRIES = (efi_mmap_size * 2) / efi_desc_size; size_t retries = 0; retry: status = gBS->GetMemoryMap(&efi_mmap_size, efi_mmap, &mmap_key, &efi_desc_size, &efi_desc_ver); if (retries == 0 && status) { goto fail; } // Be gone, UEFI! status = gBS->ExitBootServices(efi_image_handle, mmap_key); if (status) { if (retries == 128) { goto fail; } retries++; goto retry; } #if defined(__x86_64__) || defined(__i386__) asm volatile ("cli" ::: "memory"); #elif defined (__aarch64__) asm volatile ("msr daifset, #15" ::: "memory"); #else #error Unknown architecture #endif // Go through new EFI memmap and free up bootloader entries size_t entry_count = efi_mmap_size / efi_desc_size; size_t efi_copy_i = 0; for (size_t i = 0; i < entry_count; i++) { EFI_MEMORY_DESCRIPTOR *orig_entry = (void *)efi_mmap + i * efi_desc_size; EFI_MEMORY_DESCRIPTOR *new_entry = (void *)efi_copy + efi_copy_i * efi_desc_size; memcpy(new_entry, orig_entry, efi_desc_size); uint64_t base = orig_entry->PhysicalStart; uint64_t length = orig_entry->NumberOfPages * 4096; uint64_t top = base + length; // Find for a match in the untouched memory map for (size_t j = 0; j < untouched_memmap_entries; j++) { if (untouched_memmap[j].type != MEMMAP_USABLE) continue; if (top > untouched_memmap[j].base && top <= untouched_memmap[j].base + untouched_memmap[j].length) { if (untouched_memmap[j].base < base) { new_entry->NumberOfPages = (base - untouched_memmap[j].base) / 4096; efi_copy_i++; if (efi_copy_i == EFI_COPY_MAX_ENTRIES) { panic(false, "efi: New memory map exhausted"); } new_entry = (void *)efi_copy + efi_copy_i * efi_desc_size; memcpy(new_entry, orig_entry, efi_desc_size); new_entry->NumberOfPages -= (base - untouched_memmap[j].base) / 4096; new_entry->PhysicalStart = base; new_entry->VirtualStart = new_entry->PhysicalStart; length = new_entry->NumberOfPages * 4096; top = base + length; } if (untouched_memmap[j].base > base) { new_entry->NumberOfPages = (untouched_memmap[j].base - base) / 4096; efi_copy_i++; if (efi_copy_i == EFI_COPY_MAX_ENTRIES) { panic(false, "efi: New memory map exhausted"); } new_entry = (void *)efi_copy + efi_copy_i * efi_desc_size; memcpy(new_entry, orig_entry, efi_desc_size); new_entry->NumberOfPages -= (untouched_memmap[j].base - base) / 4096; new_entry->PhysicalStart = untouched_memmap[j].base; new_entry->VirtualStart = new_entry->PhysicalStart; base = new_entry->PhysicalStart; length = new_entry->NumberOfPages * 4096; top = base + length; } if (length < untouched_memmap[j].length) { panic(false, "efi: Memory map corruption"); } new_entry->Type = EfiConventionalMemory; if (length == untouched_memmap[j].length) { // It's a perfect match! break; } new_entry->NumberOfPages = untouched_memmap[j].length / 4096; efi_copy_i++; if (efi_copy_i == EFI_COPY_MAX_ENTRIES) { panic(false, "efi: New memory map exhausted"); } new_entry = (void *)efi_copy + efi_copy_i * efi_desc_size; memcpy(new_entry, orig_entry, efi_desc_size); new_entry->NumberOfPages = (length - untouched_memmap[j].length) / 4096; new_entry->PhysicalStart = base + untouched_memmap[j].length; new_entry->VirtualStart = new_entry->PhysicalStart; break; } } efi_copy_i++; if (efi_copy_i == EFI_COPY_MAX_ENTRIES) { panic(false, "efi: New memory map exhausted"); } } efi_mmap = efi_copy; efi_mmap_size = efi_copy_i * efi_desc_size; efi_boot_services_exited = true; printv("efi: Exited boot services.\n"); return true; fail: panic(false, "efi: Failed to exit boot services"); } #endif