commit 74f74b5ebc22d4699e3645d66dd9b0b9914d4293
parent d1b06f3b59bca0a6a7ab9ec3fc8748ed05d61bfa
Author: Matsuda Kenji <info@mtkn.jp>
Date: Sat, 30 Mar 2024 14:47:24 +0900
divide directory
Diffstat:
M | .gitignore | | | 1 | + |
A | kernel/main.c | | | 29 | +++++++++++++++++++++++++++++ |
A | kernel/memmap.ld | | | 13 | +++++++++++++ |
A | uefi/uefi.c | | | 120 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | uefi/uefi.h | | | 408 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | uefi/utils.c | | | 265 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | uefi/utils.h | | | 71 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
7 files changed, 907 insertions(+), 0 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -1 +1,2 @@
include
+OVMF.fd
diff --git a/kernel/main.c b/kernel/main.c
@@ -0,0 +1,28 @@
+#include "../uefi/uefi.h"
+
+typedef unsigned char uint8_t;
+typedef unsigned int uint32_t;
+typedef unsigned long long int uint64_t;
+
+// Argument order is to interface to the MS-ABI
+void
+kernel_main(void *dummy0, void *dummy1, void *dummy2,
+ EFI_GRAPHICS_OUTPUT_PROTOCOL *gop)
+{
+ uint32_t *fb_base = (uint32_t *)gop->Mode->FrameBufferBase;
+ uint32_t hrez = gop->Mode->Info->HorizontalResolution;
+ uint32_t vrez = gop->Mode->Info->VerticalResolution;
+ uint32_t sl = gop->Mode->Info->PixelsPerScanLine;
+ uint32_t cx = hrez / 2;
+ uint32_t cy = vrez / 2;
+ for (int i = 0; i < vrez; i++) {
+ for (int j = 0; j < hrez; j++) {
+ if ((cx-j)*(cx-j) + (cy-i)*(cy-i) < (vrez/3) * (vrez/3)) {
+ fb_base[i * sl + j] = 0xff << 16;
+ } else {
+ fb_base[i * sl + j] = 0xffffff;
+ }
+ }
+ }
+ for(;;);
+}
+\ No newline at end of file
diff --git a/kernel/memmap.ld b/kernel/memmap.ld
@@ -0,0 +1,12 @@
+SECTIONS
+{
+ .text 0x100000 : {
+ *(.text)
+ }
+ .data : {
+ *(.data)
+ }
+ .bss : {
+ *(.bss)
+ }
+}
+\ No newline at end of file
diff --git a/uefi/uefi.c b/uefi/uefi.c
@@ -0,0 +1,120 @@
+#include "uefi.h"
+#include "utils.h"
+EFI_SYSTEM_TABLE *SystemTable;
+
+EFI_STATUS
+EfiMain(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *systab)
+{
+ // init global variable.
+ SystemTable = systab;
+
+ EFI_STATUS stat;
+
+ // Output firmware bender.
+ SystemTable->ConOut->ClearScreen(SystemTable->ConOut);
+ efi_printf("vendor: %w\n", SystemTable->FirmwareVendor);
+ efi_printf("EfiMain: 0x%x\n", EfiMain);
+
+ // Output memory map information.
+ UINTN mmsize = 8196;
+ char mmbuf[8196];
+ EFI_MEMORY_DESCRIPTOR *mmap;
+ UINTN mkey;
+ UINTN dsize;
+ UINT32 dver;
+ mmap = (EFI_MEMORY_DESCRIPTOR *) mmbuf;
+ stat = SystemTable->BootServices->GetMemoryMap(&mmsize, mmap, &mkey, &dsize, &dver);
+ if (stat != EFI_SUCCESS) {
+ efi_printf("get memory map: %d\n", stat);
+ return stat;
+ }
+
+ for (; mmap < (EFI_MEMORY_DESCRIPTOR *)(mmbuf + mmsize);
+ mmap = (EFI_MEMORY_DESCRIPTOR *) (((char *)mmap) + dsize)) {
+ if (mmap->Type != EfiConventionalMemory) {
+ continue;
+ }
+ efi_printf("%d\t%x\t%x\t%x\t%x\n", mmap->Type, mmap->VirtualStart,
+ mmap->PhysicalStart, mmap->NumberOfPages, mmap->Attribute);
+ }
+
+ EFI_GRAPHICS_OUTPUT_PROTOCOL *gop;
+ stat = open_gop(ImageHandle, &gop);
+ if (stat != EFI_SUCCESS) {
+ efi_printf("open_gop: %d\n", stat);
+ return stat;
+ }
+ UINT32 *frame_buffer = (UINT32 *)gop->Mode->FrameBufferBase;
+ for (UINTN i = 0; i < gop->Mode->FrameBufferSize/4; i++) {
+ frame_buffer[i] = 0x427f94;
+ }
+ efi_printf("frame buffer: base: %x, size: %x\n", frame_buffer,
+ gop->Mode->FrameBufferSize);
+
+ EFI_FILE_PROTOCOL *root, *kernel;
+ stat = open_root(ImageHandle, &root);
+ if (stat != EFI_SUCCESS) {
+ efi_printf("open_root: %d\n", stat);
+ return stat;
+ }
+ stat = root->Open(root, &kernel, L"setos.elf",
+ EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE, 0);
+ if (stat != EFI_SUCCESS) {
+ efi_printf("open file \"kernel.elf\": %d\n", stat);
+ return stat;
+ }
+ ElfHdr kernel_ehdr;
+ stat = read_ehdr(kernel, &kernel_ehdr);
+ if (stat != EFI_SUCCESS) {
+ efi_printf("read ehdr of \"kernel.elf\": %d\n", stat);
+ return stat;
+ }
+ efi_printf("type:\t%x\nphoff:\t%x\nphentsize:\t%x\nphnum:\t%x\n",
+ kernel_ehdr.type, kernel_ehdr.phoff,
+ kernel_ehdr.phentsize, kernel_ehdr.phnum);
+ if (kernel_ehdr.phnum > 32) {
+ efi_printf("too many phdrs\n");
+ return EFI_BUFFER_TOO_SMALL;
+ }
+ Phdr kernel_phdr[32];
+ stat = read_phdr(kernel, &kernel_ehdr, kernel_phdr);
+ if (stat != EFI_SUCCESS) {
+ efi_printf("read phdr of \"kernel.elf\": %d\n", stat);
+ return stat;
+ }
+ for (UINT16 i = 0; i < kernel_ehdr.phnum; i++) {
+ efi_printf("type: %x, offset: %x, vaddr: %x\n",
+ kernel_phdr[i].type, kernel_phdr[i].offset, kernel_phdr[i].vaddr);
+ }
+ stat = load_elf(kernel, &kernel_ehdr, kernel_phdr);
+ if (stat != EFI_SUCCESS) {
+ efi_printf("load \"kernel.elf\": %d\n", stat);
+ return stat;
+ }
+ stat = kernel->Close(kernel);
+ if (stat != EFI_SUCCESS) {
+ efi_printf("close file: %d\n", stat);
+ return stat;
+ }
+
+ efi_printf("frame buffer: base: %x, %x, size: %x\n", frame_buffer,
+ gop->Mode->FrameBufferBase,
+ gop->Mode->FrameBufferSize);
+
+ mmsize = 8196;
+ stat = SystemTable->BootServices->GetMemoryMap(&mmsize, mmap, &mkey, &dsize, &dver);
+ if (stat != EFI_SUCCESS) {
+ efi_printf("get memory map: %d\n", stat);
+ return stat;
+ }
+ stat = SystemTable->BootServices->ExitBootServices(ImageHandle, mkey);
+ if (stat != EFI_SUCCESS) {
+ efi_printf("exit boot services: %d\n", stat);
+ return stat;
+ }
+
+ typedef void (* Kernel) (EFI_GRAPHICS_OUTPUT_PROTOCOL *);
+ ((Kernel) kernel_ehdr.entry)(gop);
+
+ return EFI_SUCCESS;
+}
diff --git a/uefi/uefi.h b/uefi/uefi.h
@@ -0,0 +1,408 @@
+#define IN
+#define OUT
+#define OPTIONAL
+#define CONST
+#define EFIAPI
+
+#define NULL 0L
+
+typedef unsigned short CHAR16;
+typedef unsigned long long UINTN;
+typedef unsigned char UINT8;
+typedef short INT16;
+typedef unsigned short UINT16;
+typedef unsigned UINT32;
+typedef unsigned long long UINT64;
+typedef UINTN EFI_STATUS;
+typedef void *EFI_HANDLE;
+typedef UINT64 EFI_PHYSICAL_ADDRESS;
+typedef UINT64 EFI_VIRTUAL_ADDRESS;
+typedef void VOID;
+typedef struct EFI_GUID {
+ UINT32 Data1;
+ UINT16 Data2;
+ UINT16 Data3;
+ UINT8 Data4[8];
+} EFI_GUID;
+
+#define EFI_LOADED_IMAGE_PROTOCOL_GUID \
+ {0x5b1b31a1, 0x9562, 0x11d2, \
+ {0x8e, 0x3f, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b}}
+#define EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID \
+ {0x9042a9de, 0x23dc, 0x4a38, \
+ {0x96, 0xfb, 0x7a, 0xde, 0xd0, 0x80, 0x51, 0x6a}}
+#define EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID \
+ {0x964e5b22, 0x6459, 0x11d2, \
+ {0x8e, 0x39, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b}}
+#define EFI_FILE_INFO_ID \
+ {0x9576e92, 0x6d3f, 0x11d2, \
+ {0x8e, 0x39, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b}}
+
+// EFI_STATUS
+enum {
+ EFI_SUCCESS = 0,
+
+ EFI_INVALID_PARAMETER = 0x8000000000000002,
+ EFI_BUFFER_TOO_SMALL = 0x8000000000000005,
+};
+
+typedef struct _EFI_SYSTEM_TABLE EFI_SYSTEM_TABLE;
+
+typedef struct {
+ UINT16 ScanCode;
+ CHAR16 UnicodeChar;
+} EFI_INPUT_KEY;
+
+typedef struct _EFI_SIMPLE_TEXT_INPUT_PROTOCOL EFI_SIMPLE_TEXT_INPUT_PROTOCOL;
+
+typedef
+EFI_STATUS (EFIAPI *EFI_INPUT_READ_KEY)(
+ IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *This,
+ OUT EFI_INPUT_KEY *Key
+ );
+
+typedef struct _EFI_SIMPLE_TEXT_INPUT_PROTOCOL {
+ void *dummy;
+ EFI_INPUT_READ_KEY ReadKeyStroke;
+} EFI_SIMPLE_TEXT_INPUT_PROTOCOL;
+
+typedef struct _EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL;
+
+typedef
+EFI_STATUS (EFIAPI *EFI_TEXT_STRING)(
+ IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
+ IN CHAR16 *String
+ );
+
+typedef
+EFI_STATUS (EFIAPI *EFI_TEXT_CLEAR_SCREEN)(
+ IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This
+ );
+
+struct _EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL {
+ void *dummy;
+ EFI_TEXT_STRING OutputString;
+ void *dummy1[4];
+ EFI_TEXT_CLEAR_SCREEN ClearScreen;
+};
+
+typedef enum {
+ EfiReservedMemoryType,
+ EfiLoaderCode,
+ EfiLoaderData,
+ EfiBootServicesCode,
+ EfiBootServicesData,
+ EfiRuntimeServicesCode,
+ EfiRuntimeServicesData,
+ EfiConventionalMemory,
+ EfiUnusableMemory,
+ EfiACPIReclaimMemory,
+ EfiACPIMemoryNVS,
+ EfiMemoryMappedIO,
+ EfiMemoryMappedIOPortSpace,
+ EfiPalCode,
+ EfiPersistentMemory,
+ EfiUnacceptedMemoryType,
+ EfiMaxMemoryType,
+} EFI_MEMORY_TYPE;
+
+typedef struct {
+ UINT32 Type;
+ EFI_PHYSICAL_ADDRESS PhysicalStart;
+ EFI_VIRTUAL_ADDRESS VirtualStart;
+ UINT64 NumberOfPages;
+ UINT64 Attribute;
+} EFI_MEMORY_DESCRIPTOR;
+
+typedef struct _EFI_GRAPHICS_OUTPUT_PROTOCOL EFI_GRAPHICS_OUTPUT_PROTOCOL;
+
+typedef enum {
+ PixelRedGreenBlueReserved8BitPerColor,
+ PixelBlueGreenRedReserved8BitPerColor,
+ PixelBitMask,
+ PixelBltOnly,
+ PixelFormatMax,
+} EFI_GRAPHICS_PIXEL_FORMAT;
+
+typedef struct {
+ UINT32 RedMask;
+ UINT32 GreenMask;
+ UINT32 BlueMask;
+ UINT32 ReservedMask;
+} EFI_PIXEL_BITMASK;
+
+typedef struct {
+ UINT32 Version;
+ UINT32 HorizontalResolution;
+ UINT32 VerticalResolution;
+ EFI_GRAPHICS_PIXEL_FORMAT PixelFromat; // This is not fixed in size.
+ EFI_PIXEL_BITMASK PixelInformation;
+ UINT32 PixelsPerScanLine;
+} EFI_GRAPHICS_OUTPUT_MODE_INFORMATION;
+
+typedef struct {
+ UINT32 MaxMode;
+ UINT32 Mode;
+ EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *Info;
+ UINTN SizeOfInfo;
+ EFI_PHYSICAL_ADDRESS FrameBufferBase;
+ UINTN FrameBufferSize;
+} EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE;
+
+typedef
+EFI_STATUS (EFIAPI *EFI_GRAPHICS_OUTPUT_PROTOCOL_QUERY_MODE) (
+ );
+
+typedef
+EFI_STATUS (EFIAPI *EFI_GRAPHICS_OUTPUT_PROTOCOL_SET_MODE) (
+ );
+
+typedef
+EFI_STATUS (EFIAPI *EFI_GRAPHICS_OUTPUT_PROTOCOL_BLT) (
+ );
+
+struct _EFI_GRAPHICS_OUTPUT_PROTOCOL {
+ EFI_GRAPHICS_OUTPUT_PROTOCOL_QUERY_MODE QueryMode;
+ EFI_GRAPHICS_OUTPUT_PROTOCOL_SET_MODE SetMode;
+ EFI_GRAPHICS_OUTPUT_PROTOCOL_BLT Blt;
+ EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE *Mode;
+};
+
+typedef struct _EFI_FILE_PROTOCOL EFI_FILE_PROTOCOL;
+
+typedef
+EFI_STATUS (EFIAPI *EFI_FILE_OPEN) (
+ IN EFI_FILE_PROTOCOL *This,
+ OUT EFI_FILE_PROTOCOL **NewHandle,
+ IN CHAR16 *FileName,
+ IN UINT64 OpenMode,
+ IN UINT64 Attributes
+ );
+
+#define EFI_FILE_MODE_READ 0x0000000000000001
+#define EFI_FILE_MODE_WRITE 0x0000000000000002
+#define EFI_FILE_MODE_CREATE 0x8000000000000000
+
+typedef
+EFI_STATUS (EFIAPI *EFI_FILE_CLOSE) (
+ IN EFI_FILE_PROTOCOL *This
+ );
+
+typedef
+EFI_STATUS (EFIAPI *EFI_FILE_DELETE) (
+ IN EFI_FILE_PROTOCOL *This
+ );
+
+typedef
+EFI_STATUS (EFIAPI *EFI_FILE_READ) (
+ IN EFI_FILE_PROTOCOL *This,
+ IN OUT UINTN *BufferSize,
+ OUT VOID *Buffer
+ );
+
+typedef
+EFI_STATUS (EFIAPI *EFI_FILE_WRITE) (
+ IN EFI_FILE_PROTOCOL *This,
+ IN OUT UINTN *BufferSize,
+ IN VOID *Buffer
+ );
+
+typedef
+EFI_STATUS (EFIAPI *EFI_FILE_SET_POSITION) (
+ IN EFI_FILE_PROTOCOL *This,
+ IN UINT64 Position
+ );
+
+typedef
+EFI_STATUS (EFIAPI *EFI_FILE_GET_POSITION) (
+ IN EFI_FILE_PROTOCOL *This,
+ OUT UINT64 *Position
+ );
+
+typedef
+EFI_STATUS (EFIAPI *EFI_FILE_GET_INFO) (
+ IN EFI_FILE_PROTOCOL *This,
+ IN EFI_GUID *InformationType,
+ IN OUT UINTN *BufferSize,
+ OUT VOID *Buffer
+ );
+
+struct _EFI_FILE_PROTOCOL {
+ UINT64 Revision;
+ EFI_FILE_OPEN Open;
+ EFI_FILE_CLOSE Close;
+ EFI_FILE_DELETE Delete;
+ EFI_FILE_READ Read;
+ EFI_FILE_WRITE Write;
+ EFI_FILE_GET_POSITION GetPosition;
+ EFI_FILE_SET_POSITION SetPosition;
+ EFI_FILE_GET_INFO GetInfo;
+ void *dummy1[6];
+};
+
+typedef struct {
+ UINT16 Year;
+ UINT8 Month;
+ UINT8 Day;
+ UINT8 Hour;
+ UINT8 Minute;
+ UINT8 Second;
+ UINT8 Pad1;
+ UINT32 NanoSecond;
+ INT16 TimeZone;
+ UINT8 Daylight;
+ UINT8 Pad2;
+} EFI_TIME;
+
+typedef struct {
+ UINT64 Size;
+ UINT64 FileSize;
+ UINT64 PhysicalSize;
+ EFI_TIME CreateTime;
+ EFI_TIME LastAccessTime;
+ EFI_TIME ModificationTime;
+ UINT64 Attribute;
+ CHAR16 FileName[];
+} EFI_FILE_INFO;
+
+typedef struct _EFI_LOADED_IMAGE_PROTOCOL EFI_LOADED_IMAGE_PROTOCOL;
+
+struct _EFI_LOADED_IMAGE_PROTOCOL{
+ UINT32 Revision;
+ EFI_HANDLE ParentHandle;
+ EFI_SYSTEM_TABLE *SystemTable;
+
+ EFI_HANDLE DeviceHandle;
+ void *dummy[2];
+
+ UINT32 LoadOptionSize;
+ VOID *LoadOptions;
+
+ VOID *ImageBase;
+ UINT64 ImageSize;
+ char dummy1[4 + 4];
+ void *dummy2;
+};
+
+typedef struct _EFI_SIMPLE_FILE_SYSTEM_PROTOCOL EFI_SIMPLE_FILE_SYSTEM_PROTOCOL;
+
+typedef
+EFI_STATUS (EFIAPI *EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_OPEN_VOLUME)(
+ IN EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *This,
+ OUT EFI_FILE_PROTOCOL **Root
+ );
+
+struct _EFI_SIMPLE_FILE_SYSTEM_PROTOCOL {
+ UINT64 Revision;
+ EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_OPEN_VOLUME OpenVolume;
+};
+
+typedef enum {
+ AllocateAnyPages,
+ AllocateMaxAddress,
+ AllocateAddress,
+ MaxAllocateType
+} EFI_ALLOCATE_TYPE;
+
+typedef
+EFI_STATUS (EFIAPI *EFI_ALLOCATE_PAGES)(
+ IN EFI_ALLOCATE_TYPE Type,
+ IN EFI_MEMORY_TYPE MemoryType,
+ IN UINTN Pages,
+ IN OUT EFI_PHYSICAL_ADDRESS *Memory
+ );
+
+typedef
+EFI_STATUS (EFIAPI *EFI_GET_MEMORY_MAP)(
+ IN OUT UINTN *MemoryMapSize,
+ OUT EFI_MEMORY_DESCRIPTOR *MemoryMap,
+ OUT UINTN *MapKey,
+ OUT UINTN *DescriptorSize,
+ OUT UINT32 *DescriptorVersion
+ );
+
+typedef
+EFI_STATUS (EFIAPI *EFI_EXIT_BOOT_SERVICES)(
+ IN EFI_HANDLE ImageHandle,
+ IN UINTN MapKey
+ );
+
+typedef
+EFI_STATUS (EFIAPI *EFI_OPEN_PROTOCOL)(
+ IN EFI_HANDLE Handle,
+ IN EFI_GUID *Protocol,
+ OUT VOID **Interface OPTIONAL,
+ IN EFI_HANDLE AgentHandle,
+ IN EFI_HANDLE ControllerHandle,
+ IN UINT32 Attributes
+ );
+
+typedef enum {
+ AllHandles,
+ ByRegisterNotify,
+ ByProtocol
+} EFI_LOCATE_SEARCH_TYPE;
+
+typedef
+EFI_STATUS (EFIAPI *EFI_LOCATE_HANDLE_BUFFER)(
+ IN EFI_LOCATE_SEARCH_TYPE SearchType,
+ IN EFI_GUID *Protocol OPTIONAL,
+ IN VOID *SearchKey OPTIONAL,
+ OUT UINTN *NoHandles,
+ OUT EFI_HANDLE **Buffer
+ );
+
+#define EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL 0x1
+
+typedef struct {
+} EFI_RUNTIME_SERVICES;
+
+typedef struct {
+ UINT64 Signature;
+ UINT32 Revision;
+ UINT32 HeaderSize;
+ UINT32 CRC32;
+ UINT32 Reserved;
+} EFI_TABLE_HEADER;
+
+typedef struct {
+ EFI_TABLE_HEADER Hdr;
+ // task priority services
+ void *task_priority_services[2];
+ // memory services
+ EFI_ALLOCATE_PAGES AllocatePages;
+ void *memory_services0[1];
+ EFI_GET_MEMORY_MAP GetMemoryMap;
+ void *memory_services1[2];
+ // event timer services
+ void *event_timer_services[6];
+ // protocol handler services
+ void *protocol_handler_services[9];
+ // image services
+ void *image_services0[4];
+ EFI_EXIT_BOOT_SERVICES ExitBootServices;
+ void *miscellaneus_services[3];
+ void *driversupport_services[2];
+ EFI_OPEN_PROTOCOL OpenProtocol;
+ void *open_and_close_protocol_services[2];
+ void *library_servies0[1];
+ EFI_LOCATE_HANDLE_BUFFER LocateHandleBuffer;
+ void *library_servies1[3];
+ void *crc_services[1];
+ void *miscellaneus_services1[3];
+} EFI_BOOT_SERVICES;
+
+struct _EFI_SYSTEM_TABLE {
+ EFI_TABLE_HEADER Hdr;
+ CHAR16 *FirmwareVendor;
+ char dummy1[4];
+ EFI_HANDLE ConsoleInHandle;
+ EFI_SIMPLE_TEXT_INPUT_PROTOCOL *ConIn;
+ EFI_HANDLE ConsoleOutHandle;
+ EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *ConOut;
+ EFI_HANDLE StandardErrorHandle;
+ EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *StdErr;
+ EFI_RUNTIME_SERVICES *RuntimeServices;
+ EFI_BOOT_SERVICES *BootServices;
+ char dummy2[16];
+};
diff --git a/uefi/utils.c b/uefi/utils.c
@@ -0,0 +1,265 @@
+#include <stdarg.h> // TODO: implement stdarg.h by my self
+
+#include "uefi.h"
+#include "utils.h"
+
+CHAR16 *
+sprinth(UINT64 n, CHAR16 s[19])
+{
+ int i;
+ int d;
+ s[0] = L'0';
+ s[1] = L'x';
+ for (i = 0; i < 16; i++) {
+ d = (n >> (64 - (i + 1) * 4)) & 0xf;
+ if (d <= 9)
+ s[i + 2] = L'0' + d;
+ else
+ s[i + 2] = L'a' + d - 10;
+ }
+ s[19] = L'\0';
+ return s;
+}
+
+char *
+wstr2str(CHAR16 *s16, char *s8)
+{
+ char *s = s8;
+ for (;*s16;) {
+ *s8++ = (char) (*s16++ & 0xff);
+ }
+ *s8 = '\0';
+ return s;
+}
+
+int
+efi_printf(char *fmt, ...)
+{
+ va_list ap;
+ CHAR16 _buf[1024], *buf = _buf;
+ EFI_STATUS stat;
+
+ char *s;
+ CHAR16 *w;
+ long long d, e;
+ unsigned long long f, g, h;
+
+ va_start(ap, fmt);
+ for (;*fmt;) {
+ if (*fmt != '%') {
+ if (*fmt == '\n') {
+ *buf++ = (CHAR16) '\n';
+ *buf++ = (CHAR16) '\r';
+ fmt++;
+ continue;
+ } else {
+ *buf++ = (CHAR16) *fmt++;
+ continue;
+ }
+ }
+ fmt++;
+ switch (*fmt++) {
+ case '%':
+ *buf++ = L'%';
+ break;
+ case 's':
+ s = va_arg(ap, char *);
+ for (;*s;) {
+ *buf++ = (CHAR16) *s++;
+ }
+ break;
+ case 'w':
+ w = va_arg(ap, CHAR16 *);
+ for (;*w;) {
+ *buf++ = *w++;
+ }
+ break;
+ case 'd':
+ d = va_arg(ap, int);
+ if (d == 0) {
+ *buf++ = '0';
+ break;
+ } else if (d < 0) {
+ *buf++ = '-';
+ d = -d;
+ }
+ for (e = 1; e <= d; e *= 10) {
+ if (e > e * 10) { // integer too big;
+ return -1;
+ }
+ }
+ for (e /= 10; e >= 1; e /= 10) {
+ *buf++ = (CHAR16) ('0' + ((d / e) % 10));
+ }
+ break;
+ case 'x':
+ f = va_arg(ap, unsigned int);
+ if (f == 0) {
+ *buf++ = '0';
+ break;
+ }
+ for (g = 1; g <= f; g *= 0x10) {
+ if (g > g * 0x10) { // integer too big.
+ return -1;
+ }
+ }
+ for (g /= 0x10; g >= 1; g /= 0x10) {
+ h = ((f / g) % 0x10);
+ if (h < 0xa) {
+ *buf++ = (CHAR16) ('0' + h);
+ } else {
+ *buf++ = (CHAR16) ('a' + h - 0xa);
+ }
+ }
+ break;
+ }
+ }
+ va_end(ap);
+ *buf = L'\0';
+
+ stat = SystemTable->ConOut->OutputString(SystemTable->ConOut, _buf);
+ if (stat != EFI_SUCCESS) {
+ return -1;
+ }
+ return (int) (buf - _buf);
+}
+
+EFI_STATUS
+open_root(EFI_HANDLE ImageHandle, EFI_FILE_PROTOCOL **root)
+{
+ EFI_STATUS stat;
+ EFI_LOADED_IMAGE_PROTOCOL *li;
+ EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *fs;
+
+ stat = SystemTable->BootServices->OpenProtocol(
+ ImageHandle, &(EFI_GUID)EFI_LOADED_IMAGE_PROTOCOL_GUID,
+ (VOID **) &li, ImageHandle, NULL, EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL
+ );
+ if (stat != EFI_SUCCESS) {
+ return stat;
+ }
+
+ stat = SystemTable->BootServices->OpenProtocol(
+ li->DeviceHandle, &(EFI_GUID)EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID,
+ (VOID **) &fs, ImageHandle, NULL, EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL
+ );
+ if (stat != EFI_SUCCESS) {
+ return stat;
+ }
+
+ stat = fs->OpenVolume(fs, root);
+ return stat;
+}
+
+EFI_STATUS
+open_gop(EFI_HANDLE ImageHandle, EFI_GRAPHICS_OUTPUT_PROTOCOL **gop)
+{
+ EFI_STATUS stat;
+ UINTN n = 0;
+ EFI_HANDLE *_gop;
+ stat = SystemTable->BootServices->LocateHandleBuffer(
+ ByProtocol, &(EFI_GUID)EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID, NULL,
+ &n, &_gop
+ );
+ if (stat != EFI_SUCCESS) {
+ efi_printf("locate handle buffer: %d\n", stat);
+ return stat;
+ }
+ stat = SystemTable->BootServices->OpenProtocol(
+ _gop[0], &(EFI_GUID)EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID,
+ (VOID **) gop, ImageHandle, NULL, EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL
+ );
+ if (stat != EFI_SUCCESS) {
+ efi_printf("open protocol: %d\n", stat);
+ return stat;
+ }
+ return EFI_SUCCESS;
+}
+
+EFI_STATUS
+read_ehdr(EFI_FILE_PROTOCOL *elf, ElfHdr *ehdr)
+{
+ EFI_STATUS stat;
+ UINTN ehdr_size = sizeof(ElfHdr);
+
+ stat = elf->SetPosition(elf, 0);
+ if (stat != EFI_SUCCESS) {
+ efi_printf("set position: %d\n", stat);
+ return stat;
+ }
+ stat = elf->Read(elf, &ehdr_size, (VOID *) ehdr);
+ if (stat != EFI_SUCCESS) {
+ efi_printf("read: %d\n", stat);
+ return stat;
+ }
+ // TODO: check file format.
+ // for now, it assumes that elf is 2's complement, 64-bit little endian,
+ // System V ABI...
+ if (ehdr->ident[0] != 0x7f || ehdr->ident[1] != 'E' ||
+ ehdr->ident[2] != 'L' || ehdr->ident[3] != 'F') {
+ efi_printf("not an elf file\n");
+ return EFI_INVALID_PARAMETER;
+ }
+
+ return EFI_SUCCESS;
+}
+
+EFI_STATUS
+read_phdr(EFI_FILE_PROTOCOL *elf, ElfHdr *ehdr, Phdr phdr[])
+{
+ EFI_STATUS stat;
+ UINTN n;
+
+ stat = elf->SetPosition(elf, ehdr->phoff);
+ if (stat != EFI_SUCCESS) {
+ efi_printf("set position: %d\n", stat);
+ return stat;
+ }
+ // TODO: need overflow check??
+ n = ehdr->phentsize * ehdr->phnum;
+ stat = elf->Read(elf, &n, (VOID *) phdr);
+ if (stat != EFI_SUCCESS) {
+ efi_printf("read phdr: %d\n", stat);
+ return stat;
+ }
+ return EFI_SUCCESS;
+}
+
+EFI_STATUS
+load_elf(EFI_FILE_PROTOCOL *elf, ElfHdr *ehdr, Phdr phdr[])
+{
+ EFI_STATUS stat;
+ EFI_PHYSICAL_ADDRESS addr;
+ UINT64 size;
+ for (UINT16 i = 0; i < ehdr->phnum; i++) {
+ if (phdr[i].type != PT_LOAD) {
+ continue;
+ }
+ efi_printf("load_elf: allocate %d at %x\n",
+ phdr[i].memsz, phdr[i].vaddr
+ );
+ stat = SystemTable->BootServices->AllocatePages(
+ AllocateAddress, EfiLoaderData,
+ (phdr[i].memsz + 0xfff) / 0x1000, &phdr[i].vaddr
+ );
+ if (stat != EFI_SUCCESS) {
+ efi_printf("allocate pages: %d\n", stat);
+ return stat;
+ }
+ efi_printf("load_elf: phdr[i].vaddr: %x\n", phdr[i].vaddr);
+ stat = elf->SetPosition(elf, phdr[i].offset);
+ if (stat != EFI_SUCCESS) {
+ efi_printf("set position: %d\n", stat);
+ return stat;
+ }
+ addr = (EFI_PHYSICAL_ADDRESS) phdr[i].vaddr;
+ efi_printf("load_elf: read to %x\n", addr);
+ size = phdr[i].filesz;
+ stat = elf->Read(elf, &size, (VOID *)addr);
+ if (stat != EFI_SUCCESS) {
+ efi_printf("read to memory: %d\n", stat);
+ return stat;
+ }
+ }
+ return EFI_SUCCESS;
+}
diff --git a/uefi/utils.h b/uefi/utils.h
@@ -0,0 +1,70 @@
+// #include "uefi.h"
+
+extern EFI_SYSTEM_TABLE *SystemTable;
+
+// Printh converts the integer into a string of hex representation.
+// It returns the second argument.
+// The second argument should have length of 19 ("0x" + "0123456789abcdef" + "\0").
+CHAR16 *sprinth(UINT64, CHAR16[19]);
+
+// Wstr2str converts UTF16-encoded string s16 to string.
+// It ignores the high byte.
+// caller must ensure that s has enough space.
+// It returns s.
+char *wstr2str(CHAR16 *s16, char *s8);
+
+// Efi_printf prints formatted string to ConOut.
+// Can't format a string longer than 1024 CHAR16s.
+// It returns number of CHAR16s written excluding the final NULL.
+int efi_printf(char *fmt, ...);
+
+// Open_gop opens the graphics output protocol.
+EFI_STATUS open_gop(EFI_HANDLE, EFI_GRAPHICS_OUTPUT_PROTOCOL **);
+
+// Open_root opens the root directory of the efi image.
+EFI_STATUS open_root(EFI_HANDLE, EFI_FILE_PROTOCOL **);
+
+// ELF header.
+typedef struct {
+ unsigned char ident[16];
+ UINT16 type;
+ UINT16 machine;
+ UINT32 version;
+ UINTN entry;
+ UINTN phoff;
+ UINTN shoff;
+ UINT32 flags;
+ UINT16 ehsize; // ELF header's size.
+ UINT16 phentsize;
+ UINT16 phnum;
+ UINT16 shentsize;
+ UINT16 shnum;
+ UINT16 shstrndx;
+} ElfHdr;
+
+typedef struct {
+ UINT32 type;
+ UINT32 flags;
+ UINTN offset;
+ UINTN vaddr;
+ UINTN paddr;
+ UINT64 filesz;
+ UINT64 memsz;
+ UINT64 align;
+} Phdr;
+
+#define PT_LOAD 1
+
+// Read_ehdr reads elf header and populates ehdr.
+// It modifies elf's offset.
+EFI_STATUS read_ehdr(EFI_FILE_PROTOCOL *elf, ElfHdr *ehdr);
+
+// Read_phdr reads elf's program headers and populates phdr.
+// It uses ehdr to read phdr.
+// It modifies elf's offset.
+// Caller should allocate enough space in phdr.
+EFI_STATUS read_phdr(EFI_FILE_PROTOCOL *elf, ElfHdr *ehdr, Phdr phdr[]);
+
+// Load_elf copies loadable segments of elf to the memory specified by the
+// vaddr field of each Phdr.
+EFI_STATUS load_elf(EFI_FILE_PROTOCOL *elf, ElfHdr *ehdr, Phdr phdr[]);
+\ No newline at end of file