From 24963496571d62ea50b79507e57ebea639ecc106 Mon Sep 17 00:00:00 2001 From: pyao12 Date: Fri, 29 May 2026 19:25:56 +0800 Subject: [PATCH] [feat] BootLoader -> Kernel --- Makefile | 39 +++++++-- boot.c | 194 ++++++++++++++++++++++++++++++++++++++++-- kernel/entry.cpp | 13 +++ kernel/kernel.ld | 45 ++++++++++ kernel/main.cpp | 18 ++-- kernel/memory/pmm.cpp | 15 ++-- kernel/serial.cpp | 15 ++-- 7 files changed, 302 insertions(+), 37 deletions(-) create mode 100644 kernel/entry.cpp create mode 100644 kernel/kernel.ld diff --git a/Makefile b/Makefile index 0d94f0f..f667d03 100644 --- a/Makefile +++ b/Makefile @@ -3,15 +3,22 @@ CXXFLAGS = -Iinclude -Iefi/inc -ffreestanding -fno-stack-protector -fno-stack-ch LDFLAGS = -shared -Bsymbolic -Tefi/gnuefi/elf_x86_64_efi.lds LDLIBS = --no-undefined +KERNEL_CXXFLAGS = $(CXXFLAGS) -DGNU_EFI_USE_MS_ABI -fno-pic +KERNEL_LDFLAGS = -Tkernel/kernel.ld -no-pie -z max-page-size=0x1000 + EFI_CFLAGS = -Iefi/inc -Iefi/inc/x86_64 -Iefi/inc/protocol \ -ffreestanding -fno-stack-protector -fno-stack-check \ -fshort-wchar -mno-red-zone -maccumulate-outgoing-args \ -fPIC -fno-strict-aliasing -Wall -Wextra -Wstrict-prototypes \ -DGNU_EFI_USE_MS_ABI -DCONFIG_x86_64 -std=c11 -O2 -g -SRC_C = $(wildcard *.c) -SRC_CPP = $(wildcard */*.cpp) $(wildcard */*/*.cpp) -OBJ = $(SRC_C:%.c=build/%.o) $(SRC_CPP:%.cpp=build/%.o) +BOOT_OBJ = build/boot.o + +KERNEL_CPP = kernel/entry.cpp kernel/main.cpp kernel/serial.cpp \ + kernel/memory/heap.cpp kernel/memory/pmm.cpp \ + graphics/context.cpp graphics/draw.cpp \ + fonts/pixel_font.cpp +KERNEL_OBJ = $(KERNEL_CPP:%.cpp=build/%.o) EFI_TOP_C = $(wildcard efi/lib/*.c) EFI_TOP_S = $(wildcard efi/lib/*.S) @@ -26,10 +33,13 @@ EFI_X86_OBJ = $(EFI_X86_C:efi/lib/x86_64/%.c=build/efi/lib/x86_64/%.o) $(EFI_X86 EFI_RT_OBJ = $(EFI_RT_C:efi/lib/runtime/%.c=build/efi/lib/runtime/%.o) EFI_OBJ = $(EFI_CRT0_OBJ) $(EFI_RELOC_OBJ) $(EFI_TOP_OBJ) $(EFI_X86_OBJ) $(EFI_RT_OBJ) -all: _bd $(EFI_OBJ) $(OBJ) - @echo "* Linking EFI..." - @ld $(LDFLAGS) $(EFI_OBJ) $(OBJ) -o build/boot.so $(LDLIBS) +all: _bd $(EFI_OBJ) $(BOOT_OBJ) $(KERNEL_OBJ) + @echo "* Linking bootloader..." + @ld $(LDFLAGS) $(EFI_OBJ) $(BOOT_OBJ) -o build/boot.so $(LDLIBS) @objcopy -j .text -j .sdata -j .data -j .rodata -j .dynamic -j .dynsym -j .rel -j .rela -j .rel.* -j .rela.* -j .reloc --output-target efi-app-x86_64 --subsystem=10 build/boot.so build/BOOTX64.EFI + @echo "* Linking Kernel.elf..." + @ld $(KERNEL_LDFLAGS) $(KERNEL_OBJ) -o build/Kernel.elf + @echo "Done." _bd: @mkdir -p build/graphics build/kernel build/fonts build/kernel/memory \ @@ -67,13 +77,26 @@ build/%.o: %.c @echo "Compile C $<" @gcc $(CFLAGS) -c $< -o $@ -build/%.o: %.cpp +build/kernel/%.o: kernel/%.cpp | _bd @echo "Compile CPP $<" - @g++ $(CXXFLAGS) -c $< -o $@ + @g++ $(KERNEL_CXXFLAGS) -c $< -o $@ + +build/kernel/memory/%.o: kernel/memory/%.cpp | _bd + @echo "Compile CPP $<" + @g++ $(KERNEL_CXXFLAGS) -c $< -o $@ + +build/graphics/%.o: graphics/%.cpp | _bd + @echo "Compile CPP $<" + @g++ $(KERNEL_CXXFLAGS) -c $< -o $@ + +build/fonts/%.o: fonts/%.cpp | _bd + @echo "Compile CPP $<" + @g++ $(KERNEL_CXXFLAGS) -c $< -o $@ vdir: all @mkdir -p vdir/EFI/BOOT @cp build/BOOTX64.EFI vdir/EFI/BOOT + @cp build/Kernel.elf vdir/ run: vdir @echo "Launching QEMU" diff --git a/boot.c b/boot.c index fe91196..592b0af 100644 --- a/boot.c +++ b/boot.c @@ -1,9 +1,192 @@ #include #include -extern void kernel_main(); +typedef struct { + UINT8 e_ident[16]; + UINT16 e_type; + UINT16 e_machine; + UINT32 e_version; + UINT64 e_entry; + UINT64 e_phoff; + UINT64 e_shoff; + UINT32 e_flags; + UINT16 e_ehsize; + UINT16 e_phentsize; + UINT16 e_phnum; + UINT16 e_shentsize; + UINT16 e_shnum; + UINT16 e_shstrndx; +} Elf64_Ehdr; -EFI_STATUS EFIAPI efi_main (EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable) { +typedef struct { + UINT32 p_type; + UINT32 p_flags; + UINT64 p_offset; + UINT64 p_vaddr; + UINT64 p_paddr; + UINT64 p_filesz; + UINT64 p_memsz; + UINT64 p_align; +} Elf64_Phdr; + +#define PT_LOAD 1 +#define ELFMAG 0x464C457F + +#define PAGE_SIZE_4K 4096 + +static void load_kernel(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable) { + EFI_STATUS status; + EFI_GUID loadedImageGuid = EFI_LOADED_IMAGE_PROTOCOL_GUID; + EFI_GUID fsGuid = EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID; + EFI_LOADED_IMAGE_PROTOCOL *loadedImage = NULL; + EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *fs = NULL; + EFI_FILE_PROTOCOL *root = NULL; + EFI_FILE_PROTOCOL *kernelFile = NULL; + VOID *phdrBuf = NULL; + Elf64_Phdr *phdrs = NULL; + Elf64_Ehdr ehdr; + UINTN readSize; + UINT16 i; + + status = uefi_call_wrapper(BS->OpenProtocol, 6, + ImageHandle, &loadedImageGuid, (void**)&loadedImage, + ImageHandle, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); + if (EFI_ERROR(status)) { + uefi_call_wrapper(SystemTable->ConOut->OutputString, 2, SystemTable->ConOut, + L"ERR: loaded_image proto\n"); + goto halt; + } + + status = uefi_call_wrapper(BS->OpenProtocol, 6, + loadedImage->DeviceHandle, &fsGuid, (void**)&fs, + ImageHandle, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); + if (EFI_ERROR(status)) { + uefi_call_wrapper(SystemTable->ConOut->OutputString, 2, SystemTable->ConOut, + L"ERR: file system proto\n"); + goto halt; + } + + status = uefi_call_wrapper(fs->OpenVolume, 1, fs, &root); + if (EFI_ERROR(status)) { + uefi_call_wrapper(SystemTable->ConOut->OutputString, 2, SystemTable->ConOut, + L"ERR: open volume\n"); + goto halt; + } + + status = uefi_call_wrapper(root->Open, 5, root, &kernelFile, L"Kernel.elf", + EFI_FILE_MODE_READ, 0); + if (EFI_ERROR(status)) { + uefi_call_wrapper(SystemTable->ConOut->OutputString, 2, SystemTable->ConOut, + L"ERR: open Kernel.elf\n"); + goto close_root; + } + + readSize = sizeof(ehdr); + status = uefi_call_wrapper(kernelFile->Read, 3, kernelFile, &readSize, &ehdr); + if (EFI_ERROR(status) || readSize != sizeof(ehdr)) { + uefi_call_wrapper(SystemTable->ConOut->OutputString, 2, SystemTable->ConOut, + L"ERR: read ELF hdr\n"); + goto close_all; + } + + if (*(UINT32*)ehdr.e_ident != ELFMAG) { + uefi_call_wrapper(SystemTable->ConOut->OutputString, 2, SystemTable->ConOut, + L"ERR: bad ELF magic\n"); + goto close_all; + } + + { + UINTN phdrSize = ehdr.e_phnum * ehdr.e_phentsize; + phdrBuf = AllocatePool(phdrSize); + if (!phdrBuf) { + uefi_call_wrapper(SystemTable->ConOut->OutputString, 2, SystemTable->ConOut, + L"ERR: alloc PHDRs\n"); + goto close_all; + } + + uefi_call_wrapper(kernelFile->SetPosition, 2, kernelFile, ehdr.e_phoff); + readSize = phdrSize; + status = uefi_call_wrapper(kernelFile->Read, 3, kernelFile, &readSize, phdrBuf); + if (EFI_ERROR(status) || readSize != phdrSize) { + uefi_call_wrapper(SystemTable->ConOut->OutputString, 2, SystemTable->ConOut, + L"ERR: read PHDRs\n"); + goto free_phdr; + } + phdrs = (Elf64_Phdr *)phdrBuf; + } + + { + UINT64 minAddr = ~0ULL; + UINT64 maxAddr = 0; + + for (i = 0; i < ehdr.e_phnum; i++) { + if (phdrs[i].p_type == PT_LOAD) { + UINT64 end = phdrs[i].p_vaddr + phdrs[i].p_memsz; + if (phdrs[i].p_vaddr < minAddr) minAddr = phdrs[i].p_vaddr; + if (end > maxAddr) maxAddr = end; + } + } + + UINT64 base = minAddr & ~(PAGE_SIZE_4K - 1); + UINT64 top = (maxAddr + PAGE_SIZE_4K - 1) & ~(PAGE_SIZE_4K - 1); + UINTN numPages = (UINTN)((top - base) / PAGE_SIZE_4K); + EFI_PHYSICAL_ADDRESS allocAddr = (EFI_PHYSICAL_ADDRESS)base; + + status = uefi_call_wrapper(BS->AllocatePages, 4, + AllocateAddress, EfiLoaderData, numPages, &allocAddr); + if (EFI_ERROR(status)) { + uefi_call_wrapper(SystemTable->ConOut->OutputString, 2, SystemTable->ConOut, + L"ERR: alloc kernel mem\n"); + goto free_phdr; + } + } + + for (i = 0; i < ehdr.e_phnum; i++) { + if (phdrs[i].p_type != PT_LOAD) continue; + + uefi_call_wrapper(kernelFile->SetPosition, 2, kernelFile, phdrs[i].p_offset); + readSize = (UINTN)phdrs[i].p_filesz; + uefi_call_wrapper(kernelFile->Read, 3, kernelFile, &readSize, + (VOID*)(UINTN)phdrs[i].p_vaddr); + + if (phdrs[i].p_memsz > phdrs[i].p_filesz) { + UINT8 *bss = (UINT8*)(UINTN)(phdrs[i].p_vaddr + phdrs[i].p_filesz); + UINTN bssSize = (UINTN)(phdrs[i].p_memsz - phdrs[i].p_filesz); + for (UINTN j = 0; j < bssSize; j++) bss[j] = 0; + } + } + + FreePool(phdrBuf); + phdrBuf = NULL; + uefi_call_wrapper(kernelFile->Close, 1, kernelFile); + kernelFile = NULL; + uefi_call_wrapper(root->Close, 1, root); + root = NULL; + + uefi_call_wrapper(SystemTable->ConOut->OutputString, 2, SystemTable->ConOut, + L"* Jumping to kernel...\n"); + + { + typedef void (*KernelEntry)(EFI_HANDLE, EFI_SYSTEM_TABLE *); + KernelEntry entry = (KernelEntry)(UINTN)ehdr.e_entry; + entry(ImageHandle, SystemTable); + } + + uefi_call_wrapper(SystemTable->ConOut->OutputString, 2, SystemTable->ConOut, + L"ERR: kernel returned\n"); + goto halt; + +free_phdr: + if (phdrBuf) FreePool(phdrBuf); +close_all: + if (kernelFile) uefi_call_wrapper(kernelFile->Close, 1, kernelFile); +close_root: + if (root) uefi_call_wrapper(root->Close, 1, root); +halt: + while (1) __asm__ volatile("hlt"); +} + +EFI_STATUS EFIAPI efi_main(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable) { InitializeLib(ImageHandle, SystemTable); uefi_call_wrapper(SystemTable->ConOut->ClearScreen, 1, SystemTable->ConOut); @@ -12,11 +195,10 @@ EFI_STATUS EFIAPI efi_main (EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTabl uefi_call_wrapper(SystemTable->ConOut->SetCursorPosition, 3, SystemTable->ConOut, 0, 2); uefi_call_wrapper(SystemTable->ConOut->OutputString, 2, SystemTable->ConOut, L"* The system has booted successfully.\n"); - uefi_call_wrapper(SystemTable->ConOut->SetCursorPosition, 3, SystemTable->ConOut, 0, 3); - uefi_call_wrapper(SystemTable->ConOut->OutputString, 2, SystemTable->ConOut, L"* Jumping to kernel...\n"); + uefi_call_wrapper(SystemTable->ConOut->OutputString, 2, SystemTable->ConOut, L"* Loading kernel...\n"); - kernel_main(); + load_kernel(ImageHandle, SystemTable); return EFI_SUCCESS; -} \ No newline at end of file +} diff --git a/kernel/entry.cpp b/kernel/entry.cpp new file mode 100644 index 0000000..6e13f77 --- /dev/null +++ b/kernel/entry.cpp @@ -0,0 +1,13 @@ +#include +#include + +EFI_SYSTEM_TABLE *ST = NULL; + +extern "C" void kernel_main(); + +extern "C" void _start(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable) { + (void)ImageHandle; + ST = SystemTable; + kernel_main(); + while (1) ASM ("hlt"); +} diff --git a/kernel/kernel.ld b/kernel/kernel.ld new file mode 100644 index 0000000..3de4c7e --- /dev/null +++ b/kernel/kernel.ld @@ -0,0 +1,45 @@ +OUTPUT_FORMAT("elf64-x86-64") +OUTPUT_ARCH(i386:x86-64) +ENTRY(_start) + +PHDRS +{ + text PT_LOAD FLAGS(5); + rodata PT_LOAD FLAGS(4); + data PT_LOAD FLAGS(6); +} + +SECTIONS +{ + . = 0x100000 + SIZEOF_HEADERS; + + .text : ALIGN(4096) { + *(.text*) + *(.gnu.linkonce.t.*) + } :text + + .rodata : ALIGN(4096) { + *(.rodata*) + *(.gnu.linkonce.r.*) + } :rodata + + .data : ALIGN(4096) { + *(.data*) + *(.gnu.linkonce.d.*) + } :data + + .bss : ALIGN(4096) { + __bss_start = .; + *(.bss*) + *(COMMON) + __bss_end = .; + } :data + + /DISCARD/ : { + *(.comment) + *(.note.*) + *(.eh_frame*) + *(.gcc_except_table*) + *(.note.GNU-stack) + } +} diff --git a/kernel/main.cpp b/kernel/main.cpp index 08b6163..7241513 100644 --- a/kernel/main.cpp +++ b/kernel/main.cpp @@ -1,5 +1,4 @@ #include -#include #include #include #include @@ -8,12 +7,14 @@ #include #include +extern EFI_SYSTEM_TABLE *ST; + inline void init_gop() { // 初始化 GOP EFI_GUID gop_guid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID; EFI_GRAPHICS_OUTPUT_PROTOCOL *GOP; - uefi_call_wrapper((void*)ST->BootServices->SetWatchdogTimer, 4, 0, 0, 0, NULL); - uefi_call_wrapper((void*)ST->BootServices->LocateProtocol, 3, &gop_guid, NULL, (void **)&GOP); + uefi_call_wrapper(ST->BootServices->SetWatchdogTimer, 4, 0, 0, 0, NULL); + uefi_call_wrapper(ST->BootServices->LocateProtocol, 3, &gop_guid, NULL, (void **)&GOP); gfx_init(GOP); } @@ -23,7 +24,7 @@ inline void init_serial() { EFI_GUID gEfiSerialIoProtocolGuid = EFI_SERIAL_IO_PROTOCOL_GUID; EFI_HANDLE *SerialHandles = NULL; UINTN NumSerials = 0; - EFI_STATUS status = uefi_call_wrapper((void*)ST->BootServices->LocateHandleBuffer, 5, + EFI_STATUS status = uefi_call_wrapper(ST->BootServices->LocateHandleBuffer, 5, ByProtocol, &gEfiSerialIoProtocolGuid, NULL, @@ -31,7 +32,7 @@ inline void init_serial() { &SerialHandles ); if (status == EFI_SUCCESS && NumSerials > 0) { - status = uefi_call_wrapper((void*)ST->BootServices->HandleProtocol, 3, + status = uefi_call_wrapper(ST->BootServices->HandleProtocol, 3, SerialHandles[0], &gEfiSerialIoProtocolGuid, (void **)&SerialIo); if (status == EFI_SUCCESS) { serial_init(SerialIo); @@ -46,11 +47,10 @@ extern "C" void kernel_main() { init_gop(); init_serial(); - // 都用uefi_call_wrapper,不用会PF,不知道为什么 - uefi_call_wrapper((void*)ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, 5); - uefi_call_wrapper((void*)ST->ConOut->OutputString, 2, ST->ConOut, L"Kernel is running!\n"); + uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, 5); + uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, (CHAR16*)L"Kernel is running!\n"); - uefi_call_wrapper((void*)ST->ConOut->ClearScreen, 1, ST->ConOut); + uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut); serial_write("\n\n"); // 防止和前面串了serial.log看不清 // init memory managers diff --git a/kernel/memory/pmm.cpp b/kernel/memory/pmm.cpp index 82c3f57..9483b03 100644 --- a/kernel/memory/pmm.cpp +++ b/kernel/memory/pmm.cpp @@ -1,7 +1,8 @@ #include -#include #include +extern EFI_SYSTEM_TABLE *ST; + pmm_t g_pmm; static inline void bitmap_set(UINTN idx) { @@ -31,7 +32,7 @@ EFI_STATUS pmm_init() { UINT32 desc_version; EFI_STATUS status = uefi_call_wrapper( - (void*)ST->BootServices->GetMemoryMap, 5, + ST->BootServices->GetMemoryMap, 5, &map_size, NULL, &map_key, &desc_size, &desc_version ); @@ -39,17 +40,17 @@ EFI_STATUS pmm_init() { EFI_MEMORY_DESCRIPTOR* mem_map = NULL; status = uefi_call_wrapper( - (void*)ST->BootServices->AllocatePool, 3, + ST->BootServices->AllocatePool, 3, EfiLoaderData, map_size, (void**)&mem_map ); if (EFI_ERROR(status)) return status; status = uefi_call_wrapper( - (void*)ST->BootServices->GetMemoryMap, 5, + ST->BootServices->GetMemoryMap, 5, &map_size, mem_map, &map_key, &desc_size, &desc_version ); if (EFI_ERROR(status)) { - uefi_call_wrapper((void*)ST->BootServices->FreePool, 1, mem_map); + uefi_call_wrapper(ST->BootServices->FreePool, 1, mem_map); return status; } @@ -96,7 +97,7 @@ EFI_STATUS pmm_init() { if (bitmap_addr == 0) { serial_write("PMM: ERROR - no space for bitmap!\n"); - uefi_call_wrapper((void*)ST->BootServices->FreePool, 1, mem_map); + uefi_call_wrapper(ST->BootServices->FreePool, 1, mem_map); return EFI_OUT_OF_RESOURCES; } @@ -155,7 +156,7 @@ EFI_STATUS pmm_init() { } if (prev) *(void**)prev = NULL; - uefi_call_wrapper((void*)ST->BootServices->FreePool, 1, mem_map); + uefi_call_wrapper(ST->BootServices->FreePool, 1, mem_map); serial_write("PMM: init OK, total "); serial_write_hex(total_pages); diff --git a/kernel/serial.cpp b/kernel/serial.cpp index a98d81a..4681623 100644 --- a/kernel/serial.cpp +++ b/kernel/serial.cpp @@ -1,28 +1,29 @@ #include -#include + +extern EFI_SYSTEM_TABLE *ST; serial_context g_serial; void serial_init(EFI_SERIAL_IO_PROTOCOL *SerialIo) { g_serial.SerialIo = SerialIo; - uefi_call_wrapper((void*)SerialIo->Reset, 1, SerialIo); + uefi_call_wrapper(SerialIo->Reset, 1, SerialIo); } void serial_write_char(char c) { if (!g_serial.SerialIo) { - uefi_call_wrapper((void*)ST->ConOut->OutputString, 2, ST->ConOut, L"serial: null io\n"); + uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, (CHAR16*)L"serial: null io\n"); return; } UINTN size = 1; - EFI_STATUS status = uefi_call_wrapper((void*)g_serial.SerialIo->Write, 3, g_serial.SerialIo, &size, &c); + EFI_STATUS status = uefi_call_wrapper(g_serial.SerialIo->Write, 3, g_serial.SerialIo, &size, &c); if (status != EFI_SUCCESS) { - uefi_call_wrapper((void*)ST->ConOut->OutputString, 2, ST->ConOut, L"serial: write failed\n"); + uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, (CHAR16*)L"serial: write failed\n"); } } void serial_write(const char *str) { if (!g_serial.SerialIo) { - uefi_call_wrapper((void*)ST->ConOut->OutputString, 2, ST->ConOut, L"serial: null io\n"); + uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, (CHAR16*)L"serial: null io\n"); return; } while (*str) { @@ -47,7 +48,7 @@ char serial_read_char() { if (!g_serial.SerialIo) return 0; char c = 0; UINTN size = 1; - EFI_STATUS status = uefi_call_wrapper((void*)g_serial.SerialIo->Read, 3, g_serial.SerialIo, &size, &c); + EFI_STATUS status = uefi_call_wrapper(g_serial.SerialIo->Read, 3, g_serial.SerialIo, &size, &c); if (status != EFI_SUCCESS || size == 0) return 0; return c; } \ No newline at end of file