Add basic UEFI loader to lk
Thids adds a uefi_load <block dev name> command, which will parse the PE header and do some basic validation checks. Bug: 294283461 Test: uefi_load virtio0 Change-Id: I97393652526bda5be1b995e59647e239c64d31d6
This commit is contained in:
committed by
Kelvin Zhang
parent
4e9edd234f
commit
c750ed0fa3
54
lib/uefi/defer.h
Normal file
54
lib/uefi/defer.h
Normal file
@@ -0,0 +1,54 @@
|
||||
#ifndef __DEFER_HEADER_
|
||||
#define __DEFER_HEADER_
|
||||
|
||||
// Macro for running a block of code before function exits.
|
||||
// Example:
|
||||
// DEFER {
|
||||
// fclose(hc);
|
||||
// hc = nullptr;
|
||||
// };
|
||||
// It works by creating a new local variable struct holding the lambda, the
|
||||
// destructor of that struct will invoke the lambda.
|
||||
|
||||
// ScopeGuard ensures that the specified functor is executed no matter how the
|
||||
// current scope exits.
|
||||
template <typename F> class ScopeGuard {
|
||||
public:
|
||||
constexpr ScopeGuard(F &&f) : f_(static_cast<F &&>(f)) {}
|
||||
constexpr ScopeGuard(ScopeGuard &&that) noexcept
|
||||
: f_(that.f_), active_(that.active_) {
|
||||
that.active_ = false;
|
||||
}
|
||||
|
||||
template <typename Functor>
|
||||
constexpr ScopeGuard(ScopeGuard<Functor> &&that)
|
||||
: f_(that.f_), active_(that.active_) {
|
||||
that.active_ = false;
|
||||
}
|
||||
|
||||
~ScopeGuard() { f_(); }
|
||||
|
||||
ScopeGuard() = delete;
|
||||
ScopeGuard(const ScopeGuard &) = delete;
|
||||
void operator=(const ScopeGuard &) = delete;
|
||||
void operator=(ScopeGuard &&that) = delete;
|
||||
|
||||
private:
|
||||
template <typename Functor> friend class ScopeGuard;
|
||||
F f_;
|
||||
bool active_ = true;
|
||||
};
|
||||
|
||||
constexpr struct {
|
||||
template <typename F> constexpr auto operator<<(F &&f) const noexcept {
|
||||
return ScopeGuard<F>(static_cast<F &&>(f));
|
||||
}
|
||||
} deferrer;
|
||||
|
||||
#define TOKENPASTE1(x, y) x##y
|
||||
#define TOKENPASTE2(x, y) TOKENPASTE1(x, y)
|
||||
#define DEFER \
|
||||
auto TOKENPASTE2(_deferred_lambda_call, __COUNTER__) = deferrer \
|
||||
<< [&]() mutable
|
||||
|
||||
#endif
|
||||
208
lib/uefi/pe.h
Normal file
208
lib/uefi/pe.h
Normal file
@@ -0,0 +1,208 @@
|
||||
#ifndef __PE_HEADER_
|
||||
#define __PE_HEADER_
|
||||
|
||||
#include <endian.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
static constexpr size_t IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16;
|
||||
static constexpr size_t IMAGE_SIZEOF_SHORT_NAME = 8;
|
||||
|
||||
static constexpr uint32_t kPEHeader = 0x4550;
|
||||
struct IMAGE_NT_HEADERS64;
|
||||
|
||||
struct IMAGE_DOS_HEADER { // DOS .EXE header
|
||||
u16 e_magic; // Magic number
|
||||
u16 e_cblp; // Bytes on last page of file
|
||||
u16 e_cp; // Pages in file
|
||||
u16 e_crlc; // Relocations
|
||||
u16 e_cparhdr; // Size of header in paragraphs
|
||||
u16 e_minalloc; // Minimum extra paragraphs needed
|
||||
u16 e_maxalloc; // Maximum extra paragraphs needed
|
||||
u16 e_ss; // Initial (relative) SS value
|
||||
u16 e_sp; // Initial SP value
|
||||
u16 e_csum; // Checksum
|
||||
u16 e_ip; // Initial IP value
|
||||
u16 e_cs; // Initial (relative) CS value
|
||||
u16 e_lfarlc; // File address of relocation table
|
||||
u16 e_ovno; // Overlay number
|
||||
u16 e_res[4]; // Reserved words
|
||||
u16 e_oemid; // OEM identifier (for e_oeminfo)
|
||||
u16 e_oeminfo; // OEM information; e_oemid specific
|
||||
u16 e_res2[10]; // Reserved words
|
||||
u32 e_lfanew; // File address of new exe header
|
||||
|
||||
constexpr bool CheckMagic() const { return LE32(e_magic) == 0x5A4D; }
|
||||
IMAGE_NT_HEADERS64 *GetPEHeader() {
|
||||
auto address = reinterpret_cast<char *>(this);
|
||||
const auto pe_header =
|
||||
reinterpret_cast<IMAGE_NT_HEADERS64 *>(address + e_lfanew);
|
||||
return pe_header;
|
||||
}
|
||||
const IMAGE_NT_HEADERS64 *GetPEHeader() const {
|
||||
auto address = reinterpret_cast<const char *>(this);
|
||||
const auto pe_header =
|
||||
reinterpret_cast<const IMAGE_NT_HEADERS64 *>(address + e_lfanew);
|
||||
return pe_header;
|
||||
}
|
||||
} __attribute__((packed));
|
||||
|
||||
enum class ArchitectureType : u16 {
|
||||
Unknown = 0x00,
|
||||
ALPHAAXPOld = 0x183,
|
||||
ALPHAAXP = 0x184,
|
||||
ALPHAAXP64Bit = 0x284,
|
||||
AM33 = 0x1D3,
|
||||
AMD64 = 0x8664,
|
||||
ARM = 0x1C0,
|
||||
ARM64 = 0xAA64,
|
||||
ARMNT = 0x1C4,
|
||||
CLRPureMSIL = 0xC0EE,
|
||||
EBC = 0xEBC,
|
||||
I386 = 0x14C,
|
||||
I860 = 0x14D,
|
||||
IA64 = 0x200,
|
||||
LOONGARCH32 = 0x6232,
|
||||
LOONGARCH64 = 0x6264,
|
||||
M32R = 0x9041,
|
||||
MIPS16 = 0x266,
|
||||
MIPSFPU = 0x366,
|
||||
MIPSFPU16 = 0x466,
|
||||
MOTOROLA68000 = 0x268,
|
||||
POWERPC = 0x1F0,
|
||||
POWERPCFP = 0x1F1,
|
||||
POWERPC64 = 0x1F2,
|
||||
R3000 = 0x162,
|
||||
R4000 = 0x166,
|
||||
R10000 = 0x168,
|
||||
RISCV32 = 0x5032,
|
||||
RISCV64 = 0x5064,
|
||||
RISCV128 = 0x5128,
|
||||
SH3 = 0x1A2,
|
||||
SH3DSP = 0x1A3,
|
||||
SH4 = 0x1A6,
|
||||
SH5 = 0x1A8,
|
||||
THUMB = 0x1C2,
|
||||
WCEMIPSV2 = 0x169
|
||||
};
|
||||
|
||||
struct IMAGE_FILE_HEADER {
|
||||
u32 Signature;
|
||||
ArchitectureType Machine;
|
||||
u16 NumberOfSections;
|
||||
u32 TimeDateStamp;
|
||||
u32 PointerToSymbolTable;
|
||||
u32 NumberOfSymbols;
|
||||
u16 SizeOfOptionalHeader;
|
||||
u16 Characteristics;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct IMAGE_DATA_DIRECTORY {
|
||||
u32 VirtualAddress;
|
||||
u32 Size;
|
||||
} __attribute__((packed));
|
||||
|
||||
enum SubsystemType : u16 {
|
||||
Unknown = 0x00,
|
||||
Native = 0x01,
|
||||
WindowsGUI = 0x02,
|
||||
WindowsCUI = 0x03,
|
||||
OS2CUI = 0x05,
|
||||
POSIXCUI = 0x07,
|
||||
Windows9xNative = 0x08,
|
||||
WindowsCEGUI = 0x09,
|
||||
EFIApplication = 0x0A,
|
||||
EFIBootServiceDriver = 0x0B,
|
||||
EFIRuntimeDriver = 0x0C,
|
||||
EFIROM = 0x0D,
|
||||
Xbox = 0x0E,
|
||||
WindowsBootApplication = 0x10
|
||||
};
|
||||
|
||||
constexpr const char *ToString(SubsystemType type) {
|
||||
switch (type) {
|
||||
case Native:
|
||||
return "Native";
|
||||
case WindowsGUI:
|
||||
return "WindowsGUI";
|
||||
case WindowsCUI:
|
||||
return "WindowsCUI";
|
||||
case OS2CUI:
|
||||
return "OS2CUI";
|
||||
case POSIXCUI:
|
||||
return "POSIXCUI";
|
||||
case Windows9xNative:
|
||||
return "Windows9xNative";
|
||||
case WindowsCEGUI:
|
||||
return "WindowsCEGUI";
|
||||
case EFIApplication:
|
||||
return "EFIApplication";
|
||||
case EFIBootServiceDriver:
|
||||
return "EFIBootServiceDriver";
|
||||
case EFIRuntimeDriver:
|
||||
return "EFIRuntimeDriver";
|
||||
case EFIROM:
|
||||
return "EFIROM";
|
||||
case Xbox:
|
||||
return "Xbox";
|
||||
case WindowsBootApplication:
|
||||
return "WindowsBootApplication";
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
struct IMAGE_OPTIONAL_HEADER64 {
|
||||
u16 Magic;
|
||||
u8 MajorLinkerVersion;
|
||||
u8 MinorLinkerVersion;
|
||||
u32 SizeOfCode;
|
||||
u32 SizeOfInitializedData;
|
||||
u32 SizeOfUninitializedData;
|
||||
u32 AddressOfEntryPoint;
|
||||
u32 BaseOfCode;
|
||||
u64 ImageBase;
|
||||
u32 SectionAlignment;
|
||||
u32 FileAlignment;
|
||||
u16 MajorOperatingSystemVersion;
|
||||
u16 MinorOperatingSystemVersion;
|
||||
u16 MajorImageVersion;
|
||||
u16 MinorImageVersion;
|
||||
u16 MajorSubsystemVersion;
|
||||
u16 MinorSubsystemVersion;
|
||||
u32 Win32VersionValue;
|
||||
u32 SizeOfImage;
|
||||
u32 SizeOfHeaders;
|
||||
u32 CheckSum;
|
||||
SubsystemType Subsystem;
|
||||
u16 DllCharacteristics;
|
||||
u64 SizeOfStackReserve;
|
||||
u64 SizeOfStackCommit;
|
||||
u64 SizeOfHeapReserve;
|
||||
u64 SizeOfHeapCommit;
|
||||
u32 LoaderFlags;
|
||||
u32 NumberOfRvaAndSizes;
|
||||
IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
|
||||
} __attribute__((packed));
|
||||
|
||||
struct IMAGE_SECTION_HEADER {
|
||||
char Name[IMAGE_SIZEOF_SHORT_NAME];
|
||||
union {
|
||||
u32 PhysicalAddress;
|
||||
u32 VirtualSize;
|
||||
} Misc;
|
||||
u32 VirtualAddress;
|
||||
u32 SizeOfRawData;
|
||||
u32 PointerToRawData;
|
||||
u32 PointerToRelocations;
|
||||
u32 PointerToLinenumbers;
|
||||
u16 NumberOfRelocations;
|
||||
u16 NumberOfLinenumbers;
|
||||
u32 Characteristics;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct IMAGE_NT_HEADERS64 {
|
||||
IMAGE_FILE_HEADER FileHeader;
|
||||
IMAGE_OPTIONAL_HEADER64 OptionalHeader;
|
||||
} __attribute__((packed));
|
||||
|
||||
#endif
|
||||
8
lib/uefi/rules.mk
Normal file
8
lib/uefi/rules.mk
Normal file
@@ -0,0 +1,8 @@
|
||||
LOCAL_DIR := $(GET_LOCAL_DIR)
|
||||
|
||||
MODULE := $(LOCAL_DIR)
|
||||
|
||||
MODULE_SRCS += \
|
||||
$(LOCAL_DIR)/uefi.cpp \
|
||||
|
||||
include make/module.mk
|
||||
76
lib/uefi/uefi.cpp
Normal file
76
lib/uefi/uefi.cpp
Normal file
@@ -0,0 +1,76 @@
|
||||
#include "defer.h"
|
||||
#include "pe.h"
|
||||
|
||||
#include <lib/bio.h>
|
||||
#include <lib/heap.h>
|
||||
#include <lk/console_cmd.h>
|
||||
#include <lk/debug.h>
|
||||
#include <lk/err.h>
|
||||
#include <lk/trace.h>
|
||||
#include <platform.h>
|
||||
#include <string.h>
|
||||
|
||||
// ASCII "PE\x0\x0"
|
||||
static constexpr uint32_t kPEHeader = 0x4550;
|
||||
|
||||
int load_pe_file(const char *blkdev) {
|
||||
bdev_t *dev = bio_open(blkdev);
|
||||
if (!dev) {
|
||||
printf("error opening block device %s\n", blkdev);
|
||||
return -1;
|
||||
}
|
||||
DEFER { bio_close(dev); };
|
||||
constexpr size_t kBlocKSize = 4096;
|
||||
|
||||
lk_time_t t = current_time();
|
||||
uint8_t *address = (uint8_t *)malloc(kBlocKSize);
|
||||
ssize_t err = bio_read(dev, (void *)address, 0, kBlocKSize);
|
||||
t = current_time() - t;
|
||||
dprintf(INFO, "bio_read returns %d, took %u msecs (%d bytes/sec)\n", (int)err,
|
||||
(uint)t, (uint32_t)((uint64_t)err * 1000 / t));
|
||||
|
||||
const auto dos_header = reinterpret_cast<const IMAGE_DOS_HEADER *>(address);
|
||||
if (!dos_header->CheckMagic()) {
|
||||
printf("DOS Magic check failed %x\n", dos_header->e_magic);
|
||||
return -2;
|
||||
}
|
||||
if (dos_header->e_lfanew > kBlocKSize - sizeof(IMAGE_FILE_HEADER)) {
|
||||
printf("Invalid PE header offset %d exceeds maximum read size of %u - %u\n",
|
||||
dos_header->e_lfanew, kBlocKSize, sizeof(IMAGE_FILE_HEADER));
|
||||
return -3;
|
||||
}
|
||||
const auto pe_header = dos_header->GetPEHeader();
|
||||
const auto file_header = &pe_header->FileHeader;
|
||||
if (LE32(file_header->Signature) != kPEHeader) {
|
||||
printf("COFF Magic check failed %x\n", LE32(file_header->Signature));
|
||||
return -4;
|
||||
}
|
||||
printf("PE header machine type: %x\n",
|
||||
static_cast<int>(file_header->Machine));
|
||||
if (file_header->SizeOfOptionalHeader != sizeof(IMAGE_OPTIONAL_HEADER64)) {
|
||||
printf("Unexpected size of optional header %d, expected %d\n",
|
||||
file_header->SizeOfOptionalHeader, sizeof(IMAGE_OPTIONAL_HEADER64));
|
||||
return -5;
|
||||
}
|
||||
const auto optional_header = &pe_header->OptionalHeader;
|
||||
if (optional_header->Subsystem != SubsystemType::EFIApplication) {
|
||||
printf("Unsupported Subsystem type: %d %s\n", optional_header->Subsystem,
|
||||
ToString(optional_header->Subsystem));
|
||||
}
|
||||
printf("Valid UEFI application found.\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cmd_uefi_load(int argc, const console_cmd_args *argv) {
|
||||
if (argc != 2) {
|
||||
printf("Usage: %s <name of block device to load from>\n", argv[0].str);
|
||||
return 1;
|
||||
}
|
||||
load_pe_file(argv[1].str);
|
||||
return 0;
|
||||
}
|
||||
|
||||
STATIC_COMMAND_START
|
||||
STATIC_COMMAND("uefi_load", "load UEFI application and run it", &cmd_uefi_load)
|
||||
STATIC_COMMAND_END(uefi);
|
||||
@@ -1,6 +1,7 @@
|
||||
# main project for qemu-aarch64
|
||||
MODULES += \
|
||||
app/shell
|
||||
app/shell \
|
||||
lib/uefi \
|
||||
|
||||
include project/virtual/test.mk
|
||||
include project/virtual/fs.mk
|
||||
|
||||
@@ -10,7 +10,7 @@ MODULES += \
|
||||
lib/cksum \
|
||||
lib/debugcommands \
|
||||
lib/unittest \
|
||||
lib/version
|
||||
lib/version \
|
||||
|
||||
# set a build system variable for other modules to include test code
|
||||
# on their own.
|
||||
|
||||
Reference in New Issue
Block a user