From 474090d4a0c160395db7c258028f37154f4886dd Mon Sep 17 00:00:00 2001 From: Patrick Wuttke Date: Mon, 15 Jan 2024 09:48:48 +0100 Subject: [PATCH] Added UEFI application as loader for the 64bit kernel. --- .gitignore | 7 +- SConstruct | 14 -- targets/i686/SConscript | 40 ++-- linker.ld => targets/i686/linker.ld | 0 targets/x86_64/SConscript | 110 +++++++-- targets/x86_64/linker.ld | 61 +++++ targets/x86_64/src/crt/crti.s | 8 +- targets/x86_64/src/crt/crtn.s | 6 +- targets/x86_64/src/kernel/boot.s | 40 +--- targets/x86_64/src/kernel/startup.cpp | 22 +- targets/x86_64/src/loader/boot.asm | 37 --- targets/x86_64/src/loader/data.c | 218 ++++++++++++++++++ targets/x86_64/src/loader/loader.c | 20 -- targets/x86_64/src/loader/main.cpp | 178 +++++++++++++++ targets/x86_64/src/loader/miniefi.hpp | 110 +++++++++ targets/x86_64/src/loader/minimalloc.cpp | 121 ++++++++++ targets/x86_64/src/loader/minimalloc.hpp | 16 ++ targets/x86_64/src/loader/miniprintf.cpp | 226 +++++++++++++++++++ targets/x86_64/src/loader/miniprintf.hpp | 18 ++ targets/x86_64/src/loader/multiboot.h | 274 ----------------------- targets/x86_64/src/loader/print.inc | 70 ------ 21 files changed, 1101 insertions(+), 495 deletions(-) rename linker.ld => targets/i686/linker.ld (100%) create mode 100644 targets/x86_64/linker.ld delete mode 100644 targets/x86_64/src/loader/boot.asm create mode 100644 targets/x86_64/src/loader/data.c delete mode 100644 targets/x86_64/src/loader/loader.c create mode 100644 targets/x86_64/src/loader/main.cpp create mode 100644 targets/x86_64/src/loader/miniefi.hpp create mode 100644 targets/x86_64/src/loader/minimalloc.cpp create mode 100644 targets/x86_64/src/loader/minimalloc.hpp create mode 100644 targets/x86_64/src/loader/miniprintf.cpp create mode 100644 targets/x86_64/src/loader/miniprintf.hpp delete mode 100644 targets/x86_64/src/loader/multiboot.h delete mode 100644 targets/x86_64/src/loader/print.inc diff --git a/.gitignore b/.gitignore index 7816930..88c9167 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,13 @@ +# Staging Folder +staging/ + # Project Folder (at least for now) .idea -# Compiled Binaries and ISOs +# Compiled Binaries and Images *.bin +*.efi +*.img *.iso # Compile Commands diff --git a/SConstruct b/SConstruct index acfaa99..675644e 100644 --- a/SConstruct +++ b/SConstruct @@ -14,27 +14,13 @@ AddOption( target = GetOption('target') env = Environment(tools = ['default', 'compilation_db']) -env.Append(CFLAGS = ['-ffreestanding']) -env.Append(CXXFLAGS = ['-ffreestanding', '-fno-exceptions', '-fno-rtti', '-std=c++20']) -env.Append(LINKFLAGS = ['-T', 'linker.ld', '-ffreestanding', '-nostdlib']) -env.Append(CPPPATH = ['#targets/_any/include', '#bastl/include']) env.Append(CCFLAGS = ['-g', '-O0']) -env['KERNEL_SOURCES'] = [] -env['KERNEL_DEPENDENCIES'] = [] env['ISO_FILES'] = [] env = SConscript('bastl/SConscript', exports = 'env') env = SConscript('targets/_any/SConscript', exports = 'env') env = SConscript(f'targets/{target}/SConscript', exports = 'env') -prog_os = env.Program( - target = 'os.bin', - source = env['KERNEL_SOURCES'], - LIBS = ['gcc'], - LINKCOM = env['KERNEL_LINKCOM'] -) -env.Depends(prog_os, env['KERNEL_DEPENDENCIES']) -env.Default(prog_os) comp_db = env.CompilationDatabase(target = '#compile_commands.json') env.Default(comp_db) diff --git a/targets/i686/SConscript b/targets/i686/SConscript index ea8bb86..3d73cd2 100644 --- a/targets/i686/SConscript +++ b/targets/i686/SConscript @@ -1,42 +1,54 @@ Import('env') -env['AS'] = 'i686-elf-as' -env['CC'] = 'i686-elf-gcc' -env['CXX'] = 'i686-elf-g++' -env['LD'] = 'i686-elf-g++' +kernel_env = env.Clone() +kernel_env['AS'] = 'i686-elf-as' +kernel_env['CC'] = 'i686-elf-gcc' +kernel_env['CXX'] = 'i686-elf-g++' +kernel_env['LD'] = 'i686-elf-g++' + +kernel_env.Append(CFLAGS = ['-ffreestanding']) +kernel_env.Append(CXXFLAGS = ['-ffreestanding', '-fno-exceptions', '-fno-rtti', '-std=c++20']) +kernel_env.Append(LINKFLAGS = ['-T', kernel_env.File('linker.ld').abspath, '-ffreestanding', '-nostdlib']) +kernel_env.Append(CPPPATH = ['#targets/_any/include', '#bastl/include']) def get_crt_object(name: str) -> str: import subprocess - cmd = [env['CXX']] - cmd.extend(env['CXXFLAGS']) + cmd = [kernel_env['CXX']] + cmd.extend(kernel_env['CXXFLAGS']) cmd.append(f'-print-file-name={name}') result = subprocess.run(cmd, stdout=subprocess.PIPE) return result.stdout.decode('utf-8').strip() + crtbegin_o = get_crt_object('crtbegin.o') crtend_o = get_crt_object('crtend.o') -crti_o = env.Object('src/crt/crti.s') -crtn_o = env.Object('src/crt/crtn.s') -env['KERNEL_LINKCOM'] = env['LINKCOM'].replace('$_LIBFLAGS', f'{crti_o[0].abspath} {crtbegin_o} $_LIBFLAGS {crtend_o} {crtn_o[0].abspath}') +crti_o = kernel_env.Object('src/crt/crti.s') +crtn_o = kernel_env.Object('src/crt/crtn.s') +kernel_env['LINKCOM'] = env['LINKCOM'].replace('$_LIBFLAGS', f'{crti_o[0].abspath} {crtbegin_o} $_LIBFLAGS {crtend_o} {crtn_o[0].abspath}') -i686_sources = Split(''' +kernel_sources = env['KERNEL_SOURCES'] + Split(''' src/kernel/boot.s src/kernel/startup.cpp ''') +prog_kernel = kernel_env.Program( + target = '#kernel.i686.bin', + source = kernel_sources, + LIBS = ['gcc'] +) +kernel_env.Depends(prog_kernel, [crti_o, crtn_o]) +kernel_env.Default(prog_kernel) i686_iso_files = [ { - "source": env.File("boot/grub.cfg"), + "source": kernel_env.File("boot/grub.cfg"), "target": "boot/grub/grub.cfg" }, { - "source": env.File("#os.bin"), + "source": kernel_env.File("#os.bin"), "target": "boot/os.bin" } ] -env.Append(KERNEL_SOURCES = [env.File(f) for f in i686_sources]) -env.Append(KERNEL_DEPENDENCIES = [crti_o, crtn_o]) env.Append(ISO_FILES = i686_iso_files) Return('env') \ No newline at end of file diff --git a/linker.ld b/targets/i686/linker.ld similarity index 100% rename from linker.ld rename to targets/i686/linker.ld diff --git a/targets/x86_64/SConscript b/targets/x86_64/SConscript index 4a7bc75..282c419 100644 --- a/targets/x86_64/SConscript +++ b/targets/x86_64/SConscript @@ -1,28 +1,44 @@ Import('env') -env['AS'] = 'x86_64-elf-as' -env['CC'] = 'x86_64-elf-gcc' -env['CXX'] = 'x86_64-elf-g++' -env['LD'] = 'x86_64-elf-g++' +### Kernel +kernel_env = env.Clone() +kernel_env['AS'] = 'x86_64-elf-as' +kernel_env['CC'] = 'x86_64-elf-gcc' +kernel_env['CXX'] = 'x86_64-elf-g++' +kernel_env['LD'] = 'x86_64-elf-g++' + +kernel_env.Append(CFLAGS = ['-ffreestanding']) +kernel_env.Append(CXXFLAGS = ['-ffreestanding', '-fno-exceptions', '-fno-rtti', '-std=c++20']) +kernel_env.Append(LINKFLAGS = ['-T', kernel_env.File('linker.ld').abspath, '-ffreestanding', '-nostdlib', '-mcmodel=large', '-mno-red-zone', '-mno-mmx', '-mno-sse', '-mno-sse2']) +kernel_env.Append(CPPPATH = ['#targets/_any/include', '#bastl/include']) def get_crt_object(name: str) -> str: import subprocess - cmd = [env['CXX']] - cmd.extend(env['CXXFLAGS']) + cmd = [kernel_env['CXX']] + cmd.extend(kernel_env['CXXFLAGS']) cmd.append(f'-print-file-name={name}') result = subprocess.run(cmd, stdout=subprocess.PIPE) return result.stdout.decode('utf-8').strip() + crtbegin_o = get_crt_object('crtbegin.o') crtend_o = get_crt_object('crtend.o') -crti_o = env.Object('src/crt/crti.s') -crtn_o = env.Object('src/crt/crtn.s') +crti_o = kernel_env.Object('src/crt/crti.s') +crtn_o = kernel_env.Object('src/crt/crtn.s') +kernel_env['LINKCOM'] = env['LINKCOM'].replace('$_LIBFLAGS', f'{crti_o[0].abspath} {crtbegin_o} $_LIBFLAGS -lgcc {crtend_o} {crtn_o[0].abspath}') -x86_64_sources = Split(''' +kernel_sources = env['KERNEL_SOURCES'] + Split(''' + src/kernel/boot.s src/kernel/startup.cpp ''') -env['KERNEL_LINKCOM'] = env['LINKCOM'].replace('$_LIBFLAGS', f'{crti_o[0].abspath} {crtbegin_o} $_LIBFLAGS {crtend_o} {crtn_o[0].abspath}') +kernel_target = kernel_env.File('#kernel.x86_64.bin') +prog_kernel = kernel_env.Program( + target = kernel_target, + source = kernel_sources +) +kernel_env.Depends(prog_kernel, [crti_o, crtn_o]) +kernel_env.Default(prog_kernel) x86_64_iso_files = [ { @@ -39,19 +55,77 @@ x86_64_iso_files = [ } ] -# also compile the loader (as i686-elf) +### UEFI Loader +uefi_env = env.Clone() +uefi_env['AS'] = 'x86_64-w64-mingw32-as' +uefi_env['CC'] = 'x86_64-w64-mingw32-gcc' +uefi_env['CXX'] = 'x86_64-w64-mingw32-g++' +uefi_env['LD'] = 'x86_64-w64-mingw32-g++' + +uefi_env.Append(CFLAGS = ['-ffreestanding']) +uefi_env.Append(CXXFLAGS = ['-ffreestanding', '-fno-exceptions', '-fno-rtti', '-std=c++20']) +uefi_env.Append(LINKFLAGS = ['-nostdlib', '-Wl,-dll', '-shared', '-Wl,--subsystem,10', '-e', 'efi_main']) +uefi_env.Append(CPPPATH = ['/usr/include/efi']) + loader_sources = Split(''' - src/loader/boot.asm + src/loader/data.c + src/loader/main.cpp + src/loader/minimalloc.cpp + src/loader/miniprintf.cpp ''') -nasm_include_dir = env.Dir('src/loader') -prog_loader = env.Command( - '#loader.bin', 'src/loader/boot.asm', f'nasm -I"{nasm_include_dir.abspath}" "$SOURCE" -f bin -o "$TARGET"' +loader_target = uefi_env.File('#loader.x86_64.efi') +prog_loader = uefi_env.Program( + target = loader_target, + source = loader_sources +) +uefi_env.Default(prog_loader) + +### Bootable Image +def runcmd(*args, **kwargs): + from subprocess import run + run(*args, **kwargs, check=True) + +def build_img(target, source, env): + fatfile = target[0].abspath + runcmd(['dd', 'if=/dev/zero', f'of={fatfile}', 'bs=1K', 'count=1440']) + runcmd(['mformat', '-i', fatfile, '-f', '1440', '::']) + runcmd(['mmd', '-i', fatfile, '::/EFI']) + runcmd(['mmd', '-i', fatfile, '::/EFI/BOOT']) + runcmd(['mcopy', '-i', fatfile, loader_target.abspath, '::/EFI/BOOT/BOOTX64.EFI']) + runcmd(['mcopy', '-i', fatfile, kernel_target.abspath, '::/kernel.bin']) + +img_target = env.File('#boot.x86_64.fat.img') +cmd_image = env.Command( + target = img_target, + source = [prog_kernel, prog_loader], + action = build_img ) +def build_iso(target, source, env): + import os + import shutil + + isofile = target[0].abspath + + # prepare the staging folder + staging_dir = env.Dir('#staging/x86_64_iso').abspath + shutil.rmtree(staging_dir, ignore_errors=True) + os.makedirs(staging_dir) + + # copy files to the staging folder + shutil.copy(img_target.abspath, os.path.join(staging_dir, 'fat.img')) + + runcmd(['xorriso', '-as', 'mkisofs', '-R', '-f', '-e', 'fat.img', '-no-emul-boot', '-o', target[0].abspath, staging_dir]) + +iso_target = env.File('#system.x86_64.iso') +cmd_iso = env.Command( + target = iso_target, + source = [cmd_image], + action = build_iso +) +env.Default(cmd_iso) + # finally update the environment -env.Append(KERNEL_SOURCES = [env.File(f) for f in x86_64_sources]) -env.Append(KERNEL_DEPENDENCIES = [crti_o, crtn_o, prog_loader]) -env.Append(LINKFLAGS = ['-mcmodel=large', '-mno-red-zone', '-mno-mmx', '-mno-sse', '-mno-sse2']) env.Append(ISO_FILES = x86_64_iso_files) Return('env') diff --git a/targets/x86_64/linker.ld b/targets/x86_64/linker.ld new file mode 100644 index 0000000..8c1bd32 --- /dev/null +++ b/targets/x86_64/linker.ld @@ -0,0 +1,61 @@ +/* The bootloader will look at this image and start execution at the symbol + designated as the entry point. */ +ENTRY(_start) + +/* Tell where the various sections of the object files will be put in the final + kernel image. */ +SECTIONS +{ + /* It used to be universally recommended to use 1M as a start offset, + as it was effectively guaranteed to be available under BIOS systems. + However, UEFI has made things more complicated, and experimental data + strongly suggests that 2M is a safer place to load. In 2016, a new + feature was introduced to the multiboot2 spec to inform bootloaders + that a kernel can be loaded anywhere within a range of addresses and + will be able to relocate itself to run from such a loader-selected + address, in order to give the loader freedom in selecting a span of + memory which is verified to be available by the firmware, in order to + work around this issue. This does not use that feature, so 2M was + chosen as a safer option than the traditional 1M. */ + . = 2M; + + /* First put the multiboot header, as it is required to be put very early + in the image or the bootloader won't recognize the file format. + Next we'll put the .text section. */ + .text BLOCK(4K) : ALIGN(4K) + { + *(.multiboot) + *(.text) + } + + /* Read-only data. */ + .rodata BLOCK(4K) : ALIGN(4K) + { + start_ctors = .; + *(SORT(.ctors*)) /* Note the "SORT" */ + end_ctors = .; + + start_dtors = .; + *(SORT(.dtors*)) + end_dtors = .; + + *(.rodata) + } + + /* Read-write data (initialized) */ + .data BLOCK(4K) : ALIGN(4K) + { + *(.data) + } + + /* Read-write data (uninitialized) and stack */ + .bss BLOCK(4K) : ALIGN(4K) + { + *(COMMON) + *(.bss) + } + + /* The compiler may produce other sections, by default it will put them in + a segment with the same name. Simply add stuff here as needed. */ + gKernelEnd = .; +} \ No newline at end of file diff --git a/targets/x86_64/src/crt/crti.s b/targets/x86_64/src/crt/crti.s index 3b9195b..a110e59 100644 --- a/targets/x86_64/src/crt/crti.s +++ b/targets/x86_64/src/crt/crti.s @@ -1,10 +1,10 @@ -/* x86 crti.s */ +/* x86_64 crti.s */ .section .init .global _init .type _init, @function _init: push %rbp - movl %esp, %ebp + movq %rsp, %rbp /* gcc will nicely put the contents of crtbegin.o's .init section here. */ .section .fini @@ -12,5 +12,5 @@ _init: .type _fini, @function _fini: push %rbp - movl %esp, %ebp - /* gcc will nicely put the contents of crtbegin.o's .fini section here. */ + movq %rsp, %rbp + /* gcc will nicely put the contents of crtbegin.o's .fini section here. */ \ No newline at end of file diff --git a/targets/x86_64/src/crt/crtn.s b/targets/x86_64/src/crt/crtn.s index 6b674ba..b446d9f 100644 --- a/targets/x86_64/src/crt/crtn.s +++ b/targets/x86_64/src/crt/crtn.s @@ -1,10 +1,10 @@ -/* x86 crtn.s */ +/* x86_64 crtn.s */ .section .init /* gcc will nicely put the contents of crtend.o's .init section here. */ - pop %rbp + popq %rbp ret .section .fini /* gcc will nicely put the contents of crtend.o's .fini section here. */ - pop %rbp + popq %rbp ret diff --git a/targets/x86_64/src/kernel/boot.s b/targets/x86_64/src/kernel/boot.s index f502c78..c89fb4f 100644 --- a/targets/x86_64/src/kernel/boot.s +++ b/targets/x86_64/src/kernel/boot.s @@ -1,16 +1,3 @@ -// constants for the multiboot header -.set ALIGN, 1<<0 /* align loaded modules on page boundaries */ -.set MEMINFO, 1<<1 /* provide memory map */ -.set FLAGS, ALIGN | MEMINFO /* this is the Multiboot 'flag' field */ -.set MAGIC, 0x1BADB002 /* 'magic number' lets bootloader find the header */ -.set CHECKSUM, -(MAGIC + FLAGS) /* checksum of above, to prove we are multiboot */ - -// the actual multiboot header -.section .multiboot -.align 4 -.long MAGIC -.long FLAGS -.long CHECKSUM // stack section .section .bss @@ -24,34 +11,13 @@ stack_top: .global _start .type _start, @function _start: - /* - The bootloader has loaded us into 32-bit protected mode on a x86 - machine. Interrupts are disabled. Paging is disabled. The processor - state is as defined in the multiboot standard. The kernel has full - control of the CPU. The kernel can only make use of hardware features - and any code it provides as part of itself. There's no printf - function, unless the kernel provides its own header and a - printf implementation. There are no security restrictions, no - safeguards, no debugging mechanisms, only what the kernel provides - itself. It has absolute and complete power over the - machine. - */ - // store the multiboot info - mov %ebx, (gMultibootHeader) + mov %rbx, (gMultibootHeader) // setup stack - mov $stack_top, %esp + mov $stack_top, %rsp - // call the constructors - mov $start_ctors, %ebx - jmp .ctors_loop_end -.ctors_loop_start: - call *(%ebx) - add 4, %ebx -.ctors_loop_end: - cmp $end_ctors, %ebx - jb .ctors_loop_start + call _init // enter high-level kernel call kernel_main diff --git a/targets/x86_64/src/kernel/startup.cpp b/targets/x86_64/src/kernel/startup.cpp index e51c816..7c41c22 100644 --- a/targets/x86_64/src/kernel/startup.cpp +++ b/targets/x86_64/src/kernel/startup.cpp @@ -40,6 +40,22 @@ void initHeapFromMultibootHeader(const uint32_t firstEntryAddr, const size_t buf addr += sizeof(entry.size) + entry.size; } } + +using initfunc_t = void *(); +extern "C" initfunc_t** start_ctors; +extern "C" initfunc_t** end_ctors; + +void initGlobals() +{ + for (initfunc_t** initFunc = start_ctors; initFunc != end_ctors; ++initFunc) + { + // TODO: this confuses me + if (*initFunc == reinterpret_cast(0xffffffffffffffff)) { + break; + } + (*initFunc)(); + } +} } extern "C" @@ -52,7 +68,7 @@ void main(); void kernel_main() { - _init(); + initGlobals(); /* Initialize terminal interface */ tty::initialize(); @@ -60,8 +76,8 @@ void kernel_main() std::printf("Kernel End: %p\n", &gKernelEnd); /* Initialize the heap */ - assert(gMultibootHeader->flags & MULTIBOOT_INFO_MEM_MAP); - initHeapFromMultibootHeader(gMultibootHeader->mmap_addr, gMultibootHeader->mmap_length); + // assert(gMultibootHeader->flags & MULTIBOOT_INFO_MEM_MAP); + // initHeapFromMultibootHeader(gMultibootHeader->mmap_addr, gMultibootHeader->mmap_length); main(); diff --git a/targets/x86_64/src/loader/boot.asm b/targets/x86_64/src/loader/boot.asm deleted file mode 100644 index 369c24f..0000000 --- a/targets/x86_64/src/loader/boot.asm +++ /dev/null @@ -1,37 +0,0 @@ - -;;; Code -[ORG 0x7c00] ; this is where NASM expects the code to be located (important for offsets and addresses) - jmp start - -%include "print.inc" - -start: - xor ax, ax ; sets AX to 0 - mov ds, ax ; writes the 0 to DS (you can't directly write to DS) DS points to the "data segment" - mov ss, ax ; same as above, but now for the "stack segment" - mov sp, 0x9c00 ; initialize the stack - cld ; clear the direction flag (so lodsb increments si and doesn't decrement it) - - mov ax, 0xb800 ; text video memory - mov es, ax ; store inside ES (extended segment), used later by stosw - - call clear_screen - - mov si, msg ; set the SI (source index) register to our string - call print_string - -;;; The end of it -hang: - jmp hang - -;;; Data -msg db 'Hello World', 0 -terminal_posx db 0 ; X position for writing text -terminal_posy db 0 ; Y position for writing text - -;;; Padding and Signature -times 510-($-$$) db 0 - -; boot signature (so QEMU boots this) -db 0x55 -db 0xAA diff --git a/targets/x86_64/src/loader/data.c b/targets/x86_64/src/loader/data.c new file mode 100644 index 0000000..bc14572 --- /dev/null +++ b/targets/x86_64/src/loader/data.c @@ -0,0 +1,218 @@ +/*++ + +Copyright (c) 1998 Intel Corporation + +Module Name: + + data.c + +Abstract: + + EFI library global data + + + +Revision History + +--*/ + +#include "lib.h" + +// +// LibInitialized - TRUE once InitializeLib() is called for the first time +// + +BOOLEAN LibInitialized = FALSE; + +// +// ImageHandle - Current ImageHandle, as passed to InitializeLib +// +EFI_HANDLE LibImageHandle; + +// +// ST - pointer to the EFI system table +// + +EFI_SYSTEM_TABLE *ST; + +// +// BS - pointer to the boot services table +// + +EFI_BOOT_SERVICES *BS; + + +// +// Default pool allocation type +// + +EFI_MEMORY_TYPE PoolAllocationType = EfiBootServicesData; + +// +// Unicode collation functions that are in use +// + +EFI_UNICODE_COLLATION_INTERFACE LibStubUnicodeInterface = { + NULL, // LibStubStriCmp, + NULL, // LibStubMetaiMatch, + NULL, // LibStubStrLwrUpr, + NULL, // LibStubStrLwrUpr, + NULL, // FatToStr + NULL, // StrToFat + NULL // SupportedLanguages +}; + +EFI_UNICODE_COLLATION_INTERFACE *UnicodeInterface = &LibStubUnicodeInterface; + +// +// Root device path +// + +EFI_DEVICE_PATH RootDevicePath[] = { + {END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE, {END_DEVICE_PATH_LENGTH,0}} +}; + +EFI_DEVICE_PATH EndDevicePath[] = { + {END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE, {END_DEVICE_PATH_LENGTH, 0}} +}; + +EFI_DEVICE_PATH EndInstanceDevicePath[] = { + {END_DEVICE_PATH_TYPE, END_INSTANCE_DEVICE_PATH_SUBTYPE, {END_DEVICE_PATH_LENGTH, 0}} +}; + + +// +// EFI IDs +// + +EFI_GUID gEfiGlobalVariableGuid = EFI_GLOBAL_VARIABLE; +EFI_GUID NullGuid = { 0,0,0,{0,0,0,0,0,0,0,0} }; + +// +// Protocol IDs +// + +EFI_GUID gEfiDevicePathProtocolGuid = EFI_DEVICE_PATH_PROTOCOL_GUID; +EFI_GUID gEfiDevicePathToTextProtocolGuid = EFI_DEVICE_PATH_TO_TEXT_PROTOCOL_GUID; +EFI_GUID gEfiDevicePathFromTextProtocolGuid = EFI_DEVICE_PATH_FROM_TEXT_PROTOCOL_GUID; +EFI_GUID gEfiDevicePathUtilitiesProtocolGuid = EFI_DEVICE_PATH_UTILITIES_PROTOCOL_GUID; +EFI_GUID gEfiLoadedImageProtocolGuid = EFI_LOADED_IMAGE_PROTOCOL_GUID; +EFI_GUID gEfiSimpleTextInProtocolGuid = EFI_SIMPLE_TEXT_INPUT_PROTOCOL_GUID; +EFI_GUID gEfiSimpleTextOutProtocolGuid = EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL_GUID; +EFI_GUID gEfiBlockIoProtocolGuid = EFI_BLOCK_IO_PROTOCOL_GUID; +EFI_GUID gEfiBlockIo2ProtocolGuid = EFI_BLOCK_IO2_PROTOCOL_GUID; +EFI_GUID gEfiDiskIoProtocolGuid = EFI_DISK_IO_PROTOCOL_GUID; +EFI_GUID gEfiDiskIo2ProtocolGuid = EFI_DISK_IO2_PROTOCOL_GUID; +EFI_GUID gEfiSimpleFileSystemProtocolGuid = EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID; +EFI_GUID gEfiLoadFileProtocolGuid = EFI_LOAD_FILE_PROTOCOL_GUID; +EFI_GUID gEfiDeviceIoProtocolGuid = EFI_DEVICE_IO_PROTOCOL_GUID; +EFI_GUID gEfiUnicodeCollationProtocolGuid = EFI_UNICODE_COLLATION_PROTOCOL_GUID; +EFI_GUID gEfiSerialIoProtocolGuid = EFI_SERIAL_IO_PROTOCOL_GUID; +EFI_GUID gEfiSimpleNetworkProtocolGuid = EFI_SIMPLE_NETWORK_PROTOCOL_GUID; +EFI_GUID gEfiPxeBaseCodeProtocolGuid = EFI_PXE_BASE_CODE_PROTOCOL_GUID; +EFI_GUID gEfiPxeBaseCodeCallbackProtocolGuid = EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL_GUID; +EFI_GUID gEfiNetworkInterfaceIdentifierProtocolGuid = EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL_GUID; +EFI_GUID gEFiUiInterfaceProtocolGuid = EFI_UI_INTERFACE_PROTOCOL_GUID; +EFI_GUID gEfiPciIoProtocolGuid = EFI_PCI_IO_PROTOCOL_GUID; +EFI_GUID gEfiPciRootBridgeIoProtocolGuid = EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_GUID; +EFI_GUID gEfiDriverBindingProtocolGuid = EFI_DRIVER_BINDING_PROTOCOL_GUID; +EFI_GUID gEfiComponentNameProtocolGuid = EFI_COMPONENT_NAME_PROTOCOL_GUID; +EFI_GUID gEfiComponentName2ProtocolGuid = EFI_COMPONENT_NAME2_PROTOCOL_GUID; +EFI_GUID gEfiHashProtocolGuid = EFI_HASH_PROTOCOL_GUID; +EFI_GUID gEfiPlatformDriverOverrideProtocolGuid = EFI_PLATFORM_DRIVER_OVERRIDE_PROTOCOL_GUID; +EFI_GUID gEfiBusSpecificDriverOverrideProtocolGuid = EFI_BUS_SPECIFIC_DRIVER_OVERRIDE_PROTOCOL_GUID; +EFI_GUID gEfiDriverFamilyOverrideProtocolGuid = EFI_DRIVER_FAMILY_OVERRIDE_PROTOCOL_GUID; +EFI_GUID gEfiEbcProtocolGuid = EFI_EBC_PROTOCOL_GUID; + +// +// File system information IDs +// + +EFI_GUID gEfiFileInfoGuid = EFI_FILE_INFO_ID; +EFI_GUID gEfiFileSystemInfoGuid = EFI_FILE_SYSTEM_INFO_ID; +EFI_GUID gEfiFileSystemVolumeLabelInfoIdGuid = EFI_FILE_SYSTEM_VOLUME_LABEL_ID; + +// +// Reference implementation public protocol IDs +// + +EFI_GUID InternalShellProtocol = INTERNAL_SHELL_GUID; +EFI_GUID VariableStoreProtocol = VARIABLE_STORE_PROTOCOL; +EFI_GUID LegacyBootProtocol = LEGACY_BOOT_PROTOCOL; +EFI_GUID VgaClassProtocol = VGA_CLASS_DRIVER_PROTOCOL; + +EFI_GUID TextOutSpliterProtocol = TEXT_OUT_SPLITER_PROTOCOL; +EFI_GUID ErrorOutSpliterProtocol = ERROR_OUT_SPLITER_PROTOCOL; +EFI_GUID TextInSpliterProtocol = TEXT_IN_SPLITER_PROTOCOL; +/* Added for GOP support */ +EFI_GUID gEfiGraphicsOutputProtocolGuid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID; +EFI_GUID gEfiEdidDiscoveredProtocolGuid = EFI_EDID_DISCOVERED_PROTOCOL_GUID; +EFI_GUID gEfiEdidActiveProtocolGuid = EFI_EDID_ACTIVE_PROTOCOL_GUID; +EFI_GUID gEfiEdidOverrideProtocolGuid = EFI_EDID_OVERRIDE_PROTOCOL_GUID; + +EFI_GUID AdapterDebugProtocol = ADAPTER_DEBUG_PROTOCOL; + +// +// Device path media protocol IDs +// +EFI_GUID gEfiPcAnsiGuid = EFI_PC_ANSI_GUID; +EFI_GUID gEfiVT100Guid = EFI_VT_100_GUID; +EFI_GUID gEfiVT100PlusGuid = EFI_VT_100_PLUS_GUID; +EFI_GUID gEfiVTUTF8Guid = EFI_VT_UTF8_GUID; + +// +// EFI GPT Partition Type GUIDs +// +EFI_GUID EfiPartTypeSystemPartitionGuid = EFI_PART_TYPE_EFI_SYSTEM_PART_GUID; +EFI_GUID EfiPartTypeLegacyMbrGuid = EFI_PART_TYPE_LEGACY_MBR_GUID; + + +// +// Reference implementation Vendor Device Path Guids +// +EFI_GUID UnknownDevice = UNKNOWN_DEVICE_GUID; + +// +// Configuration Table GUIDs +// + +EFI_GUID MpsTableGuid = MPS_TABLE_GUID; +EFI_GUID AcpiTableGuid = ACPI_TABLE_GUID; +EFI_GUID SMBIOSTableGuid = SMBIOS_TABLE_GUID; +EFI_GUID SMBIOS3TableGuid = SMBIOS3_TABLE_GUID; +EFI_GUID SalSystemTableGuid = SAL_SYSTEM_TABLE_GUID; +EFI_GUID EfiDtbTableGuid = EFI_DTB_TABLE_GUID; + +// +// Network protocol GUIDs +// +EFI_GUID Ip4ServiceBindingProtocol = EFI_IP4_SERVICE_BINDING_PROTOCOL; +EFI_GUID Ip4Protocol = EFI_IP4_PROTOCOL; +EFI_GUID Udp4ServiceBindingProtocol = EFI_UDP4_SERVICE_BINDING_PROTOCOL; +EFI_GUID Udp4Protocol = EFI_UDP4_PROTOCOL; +EFI_GUID Tcp4ServiceBindingProtocol = EFI_TCP4_SERVICE_BINDING_PROTOCOL; +EFI_GUID Tcp4Protocol = EFI_TCP4_PROTOCOL; + +// +// Pointer protocol GUIDs +// +EFI_GUID SimplePointerProtocol = EFI_SIMPLE_POINTER_PROTOCOL_GUID; +EFI_GUID AbsolutePointerProtocol = EFI_ABSOLUTE_POINTER_PROTOCOL_GUID; + +// +// Debugger protocol GUIDs +// +EFI_GUID gEfiDebugImageInfoTableGuid = EFI_DEBUG_IMAGE_INFO_TABLE_GUID; +EFI_GUID gEfiDebugSupportProtocolGuid = EFI_DEBUG_SUPPORT_PROTOCOL_GUID; + +// +// Console extension protocol GUIDs +// +EFI_GUID SimpleTextInputExProtocol = EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID; + +// +// Shell protocol GUIDs +// +EFI_GUID ShellProtocolGuid = EFI_SHELL_PROTOCOL_GUID; +EFI_GUID ShellParametersProtocolGuid = EFI_SHELL_PARAMETERS_PROTOCOL_GUID; +EFI_GUID ShellDynamicCommandProtocolGuid = EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL_GUID; diff --git a/targets/x86_64/src/loader/loader.c b/targets/x86_64/src/loader/loader.c deleted file mode 100644 index f1bbd1f..0000000 --- a/targets/x86_64/src/loader/loader.c +++ /dev/null @@ -1,20 +0,0 @@ - -#include -#include "multiboot.h" - -void load_kernel_module(const multiboot_module_t* module) -{ - -} - -void loader_main(const multiboot_info_t* multibootInfo) -{ - if (multibootInfo->flags & MULTIBOOT_INFO_MODS) - { - const multiboot_module_t* modules = (multiboot_module_t*) multibootInfo->mods_addr; - for (uint32_t modIdx = 0; modIdx < multibootInfo->mods_count; ++modIdx) - { - load_kernel_module(&modules[modIdx]); - } - } -} diff --git a/targets/x86_64/src/loader/main.cpp b/targets/x86_64/src/loader/main.cpp new file mode 100644 index 0000000..c87c0a0 --- /dev/null +++ b/targets/x86_64/src/loader/main.cpp @@ -0,0 +1,178 @@ + +extern "C" +{ +#include +#include +} +#include "miniefi.hpp" +#include "minimalloc.hpp" +#include "miniprintf.hpp" + +namespace +{ +EFI_STATUS openImageVolume(EFI_HANDLE image, EFI_FILE_HANDLE& outHandle) +{ + EFI_LOADED_IMAGE* loadedImage = nullptr; + EFI_GUID lipGuid = EFI_LOADED_IMAGE_PROTOCOL_GUID; + EFI_FILE_IO_INTERFACE* ioVolume = nullptr; + EFI_GUID fsGuid = EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID; + EFI_FILE_HANDLE volume = nullptr; + + EFI_STATUS status = BS->HandleProtocol(image, &lipGuid, reinterpret_cast(&loadedImage)); + if (EFI_ERROR(status)) { + return status; + } + status = BS->HandleProtocol(loadedImage->DeviceHandle, &fsGuid, reinterpret_cast(&ioVolume)); + if (EFI_ERROR(status)) { + return status; + } + status = ioVolume->OpenVolume(ioVolume, &volume); + if (EFI_ERROR(status)) { + return status; + } + outHandle = volume; + + return EFI_SUCCESS; +} + +void print(const CHAR16* text) +{ + ST->ConOut->OutputString(ST->ConOut, const_cast(text)); +} + +void putchar(char c) +{ + if (c == '\n') + { + print(L"\r\n"); + return; + } + CHAR16 buffer[2]; + buffer[0] = c; + buffer[1] = L'\0'; + print(buffer); +} + +void waitForKey() +{ + EFI_INPUT_KEY Key; + + // reset input buffer to flush previous key strokes + EFI_STATUS status = ST->ConIn->Reset(ST->ConIn, FALSE); + if (EFI_ERROR(status)) + { + return; + } + + // now wait for a key + while ((status = ST->ConIn->ReadKeyStroke(ST->ConIn, &Key)) == EFI_NOT_READY); +} + +void printAndWait(const CHAR16* text) +{ + print(text); + waitForKey(); +} + +EFI_STATUS getEfiFileInfo(EFI_FILE_HANDLE fileHandle, EFI_FILE_INFO*& outFileInfo) +{ + static const EFI_GUID GUID = EFI_FILE_INFO_ID; + UINTN size = 0; + EFI_STATUS status = fileHandle->GetInfo(fileHandle, const_cast(&GUID), &size, nullptr); + if (status != EFI_BUFFER_TOO_SMALL) { + return status; + } + void* buffer = malloc(size); + if (buffer == nullptr) { + return EFI_OUT_OF_RESOURCES; + } + status = fileHandle->GetInfo(fileHandle, const_cast(&GUID), &size, buffer); + if (EFI_ERROR(status)) + { + free(buffer); + return status; + } + outFileInfo = static_cast(buffer); + return EFI_SUCCESS; +} +} + +#define DIE_ON_ERROR(message) \ +if (EFI_ERROR(status)) \ +{ \ + printAndWait(message L"\r\n"); \ + return status; \ +} + +extern "C" EFI_STATUS efi_main(EFI_HANDLE imageHandle, EFI_SYSTEM_TABLE* systemTable) +{ + /* Store the system table for future use in other functions */ + ST = systemTable; + BS = systemTable->BootServices; + + initMiniMalloc(); + initMiniPrintf(&putchar); + + ST->ConOut->ClearScreen(ST->ConOut); + + EFI_FILE_HANDLE volume; + EFI_STATUS status = openImageVolume(imageHandle, volume); + DIE_ON_ERROR(L"Error opening image volume."); + + const CHAR16* fileName = L"kernel.bin"; + EFI_FILE_HANDLE fileHandle = nullptr; + status = volume->Open(volume, &fileHandle, const_cast(fileName), EFI_FILE_MODE_READ, EFI_FILE_READ_ONLY | EFI_FILE_HIDDEN | EFI_FILE_SYSTEM); + DIE_ON_ERROR(L"Error opening file for reading."); + EFI_FILE_INFO* info = nullptr; + status = getEfiFileInfo(fileHandle, info); + DIE_ON_ERROR(L"Error getting file info."); + printf("File size: %d.\n", static_cast(info->FileSize)); + free(info); + + Elf64Header header = {}; + UINTN bufferSize = sizeof(Elf64Header); + status = fileHandle->Read(fileHandle, &bufferSize, &header); + DIE_ON_ERROR("Error reading EFI64 header."); + printf("headerSize: %d\n", static_cast(header.type)); + printf("entry: 0x%X\n", static_cast(header.entry)); + + for (uint16_t phtEntry = 0; phtEntry < header.phtEntryCount; ++phtEntry) + { + status = fileHandle->SetPosition(fileHandle, header.phtOffset + phtEntry * header.phtEntrySize); + DIE_ON_ERROR("Error seeking in EFI64 file."); + Efi64ProgramHeader programHeader = {}; + bufferSize = sizeof(Efi64ProgramHeader); + status = fileHandle->Read(fileHandle, &bufferSize, &programHeader); + DIE_ON_ERROR("Error reading EFI64 program header."); + printf("Program Header %d:\n", phtEntry); + printf(" type: %d\n", static_cast(programHeader.type)); + printf(" flags: %d\n", static_cast(programHeader.flags)); + printf(" vaddr: 0x%X\n", programHeader.vaddr); + printf(" paddr: 0x%X\n", programHeader.paddr); + printf(" fileSize: %d\n", static_cast(programHeader.fileSize)); + printf(" memSize: %d\n", static_cast(programHeader.memSize)); + + if (programHeader.type != ElfProgramHeaderType::LOAD) { + continue; + } + print(L"Loading segment...\r\n"); + status = fileHandle->SetPosition(fileHandle, programHeader.offset); + DIE_ON_ERROR("Error seeking to EFI64 section."); + void* targetAddr = reinterpret_cast(programHeader.paddr); + bufferSize = programHeader.fileSize; + status = fileHandle->Read(fileHandle, &bufferSize, targetAddr); + DIE_ON_ERROR("Error reading EFI64 section."); + } + + // up up and away! + __asm__ __volatile__ + ( + "jmp %0" + : + : "a"(header.entry) + ); + + waitForKey(); + + return EFI_SUCCESS; +} diff --git a/targets/x86_64/src/loader/miniefi.hpp b/targets/x86_64/src/loader/miniefi.hpp new file mode 100644 index 0000000..b0d269c --- /dev/null +++ b/targets/x86_64/src/loader/miniefi.hpp @@ -0,0 +1,110 @@ + +#pragma once + +#if !defined(MINIEFI_HPP_INCLUDED) +#define MINIEFI_HPP_INCLUDED 1 + +#include + +inline const uint8_t ELF_MAGIC[4] = { 0x7F, 0x45, 0x4c, 0x46 }; + +enum class ElfClass : uint8_t +{ + ELF32 = 1, + ELF64 = 2 +}; + +enum class ElfData : uint8_t +{ + LITTLE_ENDIAN = 1, + BIG_ENDIAN = 2 +}; + +enum class ElfOsAbi : uint8_t +{ + SYSTEM_V = 0, + HP_UX = 1, + NETBSD = 2, + LINUX = 3, + GNU_HURD = 4, + SOLARIS = 6, + AIX = 7, + IRIX = 8, + FREEBSD = 9, + TRU64 = 10, + NOVELL_MODESTO = 11, + OPENBSD = 12, + OPENVMS = 13, + NONSTOP_KERNEL = 14, + AROS = 15, + FENIXOS = 16, + NUXI_CLOUDABI = 17, + OPENVOS = 18 +}; + +enum class EfiType : uint16_t +{ + NONE = 0, + RELOCATABLE = 1, + EXECUTABLE = 2, + SHARED_OBJECT = 3, + CORE = 4 +}; + +enum class EfiMachine : uint16_t +{ + // I won't add all of them ... + NONE = 0, + X86 = 3, + X86_64 = 0x3E +}; + +struct Elf64Header +{ + uint8_t magic[4]; + ElfClass elfClass; + ElfData data; + uint8_t version; + ElfOsAbi osabi; + uint8_t abiversion; + uint8_t padding__[7]; + EfiType type; + uint16_t machine; + uint32_t version2; + uint64_t entry; + uint64_t phtOffset; + uint64_t shtOffset; + uint32_t flags; + uint16_t headerSize; + uint16_t phtEntrySize; + uint16_t phtEntryCount; + uint16_t shtEntrySize; + uint16_t shtEntryCount; + uint16_t shtNameSectionIndex; +} __attribute__((packed)); + +enum class ElfProgramHeaderType +{ + NONE = 0, + LOAD = 1, + DYNAMIC = 2, + INTERP = 3, + NOTE = 4, + SHLIB = 5, + PROGRAM_HEADER_TABLE = 6, + TLS = 7 +}; + +struct Efi64ProgramHeader +{ + ElfProgramHeaderType type; + uint32_t flags; + uint64_t offset; + uint64_t vaddr; + uint64_t paddr; + uint64_t fileSize; + uint64_t memSize; + uint64_t align; +} __attribute__((packed)); + +#endif // MINIEFI_HPP_INCLUDED diff --git a/targets/x86_64/src/loader/minimalloc.cpp b/targets/x86_64/src/loader/minimalloc.cpp new file mode 100644 index 0000000..a1bb875 --- /dev/null +++ b/targets/x86_64/src/loader/minimalloc.cpp @@ -0,0 +1,121 @@ + + +#include + +namespace +{ +inline const size_t INIT_MALLOC_SPACE_ELEMENTS = 1024; +inline const size_t INIT_MALLOC_SPACE_BYTES = INIT_MALLOC_SPACE_ELEMENTS * sizeof(max_align_t); + +max_align_t gInitMallocSpace[INIT_MALLOC_SPACE_ELEMENTS]; + +struct MallocBlock +{ + size_t elements; + MallocBlock* nextBlock; +}; +struct AllocInfo +{ + size_t elements; +}; +static_assert(sizeof(MallocBlock) <= sizeof(max_align_t)); +static_assert(sizeof(AllocInfo) <= sizeof(max_align_t)); + +MallocBlock* gNextBlock; +} + +void initMiniMalloc() +{ + gNextBlock = reinterpret_cast(gInitMallocSpace); + gNextBlock->elements = INIT_MALLOC_SPACE_ELEMENTS; + gNextBlock->nextBlock = nullptr; +} + +extern "C" +{ +void* malloc(size_t size) noexcept +{ + if (size == 0) + { + return nullptr; + } + + const size_t requiredElements = (size + sizeof(max_align_t) - 1) / sizeof(max_align_t) + 1; + MallocBlock* prevBlock = nullptr; + for (MallocBlock* block = gNextBlock; block != nullptr; block = block->nextBlock) + { + if (block->elements >= requiredElements) + { + MallocBlock* newBlock = nullptr; + if (block->elements > requiredElements) + { + newBlock = reinterpret_cast(reinterpret_cast(block) + requiredElements); + newBlock->nextBlock = block->nextBlock; + newBlock->elements = block->elements - requiredElements; + } else + { + newBlock = block->nextBlock; + } + if (prevBlock != nullptr) + { + prevBlock->nextBlock = newBlock; + } + AllocInfo* allocInfo = reinterpret_cast(block); + allocInfo->elements = requiredElements; + return reinterpret_cast(block) + 1; + } + prevBlock = block; + } + + return nullptr; +} + +void free(void* memory) noexcept +{ + if (memory == nullptr) + { + return; + } + MallocBlock* block = reinterpret_cast(static_cast(memory) - 1); + block->nextBlock = gNextBlock; + gNextBlock = block; +} +} + +void* operator new(size_t count) +{ + if (void* ptr = malloc(count); ptr != nullptr) + { + return ptr; + } + // TODO: some kind of abort +} + +void operator delete(void* data) noexcept +{ + free(data); +} + +void* operator new[](size_t count) +{ + if (void* ptr = malloc(count); ptr != nullptr) + { + return ptr; + } + // TODO: some kind of abort +} + +void operator delete[](void* data) noexcept +{ + free(data); +} + +void operator delete(void* data, size_t /* size */) noexcept +{ + free(data); +} + +void operator delete[](void* data, size_t /* size */) noexcept +{ + free(data); +} diff --git a/targets/x86_64/src/loader/minimalloc.hpp b/targets/x86_64/src/loader/minimalloc.hpp new file mode 100644 index 0000000..5ad342a --- /dev/null +++ b/targets/x86_64/src/loader/minimalloc.hpp @@ -0,0 +1,16 @@ + +#pragma once + +#if !defined(MINIMALLOC_HPP_INCLUDED) +#define MINIMALLOC_HPP_INCLUDED 1 + +#include + +void initMiniMalloc(); +extern "C" +{ +void* malloc(size_t size) noexcept; +void free(void* memory) noexcept; +} + +#endif // MINIMALLOC_HPP_INCLUDED diff --git a/targets/x86_64/src/loader/miniprintf.cpp b/targets/x86_64/src/loader/miniprintf.cpp new file mode 100644 index 0000000..afdd27a --- /dev/null +++ b/targets/x86_64/src/loader/miniprintf.cpp @@ -0,0 +1,226 @@ + +#include "miniprintf.hpp" + +#include +#include + +namespace +{ +putchar_t putchar; + +size_t strlen(const char* str) +{ + size_t len = 0; + while (str[len] != '\0') { ++len; } + return len; +} + +int printInt(long value) +{ + if (value == 0) + { + putchar('0'); + return 1; + } + if (value < 0) + { + putchar('-'); + return printInt(-value) + 1; + } + + char digits[19]; // 9223372036854775807 has 10 digits + char* pos = &digits[0]; + + while (value > 0) + { + *pos = static_cast('0' + (value % 10)); + value /= 10; + ++pos; + } + for (char* chr = pos - 1; chr >= digits; --chr) + { + putchar(*chr); + } + return pos - digits; +} + +int printUInt(uint64_t value) +{ + if (value == 0) + { + putchar('0'); + return 1; + } + + char digits[20]; // 18,446,744,073,709,551,615 has 20 digits + char* pos = &digits[0]; + + while (value > 0) + { + *pos = static_cast('0' + (value % 10)); + value /= 10; + ++pos; + } + for (char* chr = pos - 1; chr >= digits; --chr) + { + putchar(*chr); + } + return pos - digits; +} + +int printHexInt(uint64_t value) +{ + if (value == 0) + { + putchar('0'); + return 1; + } + + char digits[16]; // FFFFFFFFFFFFFFFF has 16 digits + char* pos = &digits[0]; + + while (value > 0) + { + const uint64_t digit = (value % 16); + if (digit < 10) + { + *pos = static_cast('0' + digit); + } + else + { + *pos = static_cast('A' + digit - 10); + } + value /= 16; + ++pos; + } + for (char* chr = pos - 1; chr >= digits; --chr) + { + putchar(*chr); + } + return pos - digits; +} + +int printPointer(void* ptr) +{ + return printHexInt(reinterpret_cast(ptr)); +} + +int printString(const char* str) +{ + const size_t len = strlen(str); + + for (size_t idx = 0; idx < len; ++idx) + { + putchar(str[idx]); + } + return static_cast(len); +} + +int printByteSize(size_t bytes) +{ + const char* suffixes[] = { + "B", "KiB", "MiB", "GiB", "TiB" + }; + int suffixIdx = 0; + for (; suffixIdx < sizeof(suffixes) / sizeof(suffixes[0]); ++suffixIdx) + { + if (bytes < 1024) { + break; + } + bytes /= 1024; + } + return printUInt(bytes) + printString(suffixes[suffixIdx]); +} +} + +void initMiniPrintf(putchar_t putcharFn) noexcept +{ + putchar = putcharFn; +} + +extern "C" +{ +int printf(const char* format, ...) noexcept +{ + va_list parameters; + va_start(parameters, format); + const int result = vprintf(format, parameters); + va_end(parameters); + return result; +} + +int vprintf(const char* format, va_list vlist) +{ + int result = 0; + const char* pos = format; + while (*pos != '\0') + { + if (*pos == '%') + { + ++pos; + switch (*pos) + { + case '%': + putchar('%'); + ++result; + break; + case '\0': + // TODO: invalid format string, do something + // assert(!"Invalid format string, ends with %."); + return -1; + case 'b': + result += printByteSize(va_arg(vlist, size_t)); + break; + case 'c': + { + const char chr = static_cast(va_arg(vlist, int)); + putchar(chr); + ++result; + break; + } + case 's': + result += printString(va_arg(vlist, const char*)); + break; + case 'd': + result += printInt(va_arg(vlist, int)); + break; + case 'p': + result += printPointer(va_arg(vlist, void*)); + break; + case 'X': + result += printHexInt(va_arg(vlist, unsigned)); + break; + case 'l': + ++pos; + switch (*pos) + { + case '\0': + // TODO: invalid format string, do something + // assert(!"Invalid format string, ends with %."); + return -1; + case 'd': + result += printInt(va_arg(vlist, long)); + break; + case 'X': + result += printHexInt(va_arg(vlist, long)); + break; + default: + return -1; + } + default: + // TODO + // assert(!"Invalid format string, unknown format identifier."); + return -1; + } + } + else + { + putchar(*pos); + } + ++pos; + } + ++result; + return result; +} +} // extern "C" + diff --git a/targets/x86_64/src/loader/miniprintf.hpp b/targets/x86_64/src/loader/miniprintf.hpp new file mode 100644 index 0000000..ff2f509 --- /dev/null +++ b/targets/x86_64/src/loader/miniprintf.hpp @@ -0,0 +1,18 @@ + +#pragma once + +#if !defined(MINIPRINTF_HPP_INCLUDED) +#define MINIPRINTF_HPP_INCLUDED 1 + +#include + +using putchar_t = void (*)(char c); + +void initMiniPrintf(putchar_t putcharFn) noexcept; +extern "C" +{ +int printf(const char* format, ...) noexcept; +int vprintf(const char* format, va_list vlist); +} + +#endif // !defined(MINIPRINTF_HPP_INCLUDED) diff --git a/targets/x86_64/src/loader/multiboot.h b/targets/x86_64/src/loader/multiboot.h deleted file mode 100644 index f6302ea..0000000 --- a/targets/x86_64/src/loader/multiboot.h +++ /dev/null @@ -1,274 +0,0 @@ -/* multiboot.h - Multiboot header file. */ -/* Copyright (C) 1999,2003,2007,2008,2009,2010 Free Software Foundation, Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ANY - * DEVELOPER OR DISTRIBUTOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR - * IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -#ifndef MULTIBOOT_HEADER -#define MULTIBOOT_HEADER 1 - -/* How many bytes from the start of the file we search for the header. */ -#define MULTIBOOT_SEARCH 8192 -#define MULTIBOOT_HEADER_ALIGN 4 - -/* The magic field should contain this. */ -#define MULTIBOOT_HEADER_MAGIC 0x1BADB002 - -/* This should be in %eax. */ -#define MULTIBOOT_BOOTLOADER_MAGIC 0x2BADB002 - -/* Alignment of multiboot modules. */ -#define MULTIBOOT_MOD_ALIGN 0x00001000 - -/* Alignment of the multiboot info structure. */ -#define MULTIBOOT_INFO_ALIGN 0x00000004 - -/* Flags set in the ’flags’ member of the multiboot header. */ - -/* Align all boot modules on i386 page (4KB) boundaries. */ -#define MULTIBOOT_PAGE_ALIGN 0x00000001 - -/* Must pass memory information to OS. */ -#define MULTIBOOT_MEMORY_INFO 0x00000002 - -/* Must pass video information to OS. */ -#define MULTIBOOT_VIDEO_MODE 0x00000004 - -/* This flag indicates the use of the address fields in the header. */ -#define MULTIBOOT_AOUT_KLUDGE 0x00010000 - -/* Flags to be set in the ’flags’ member of the multiboot info structure. */ - -/* is there basic lower/upper memory information? */ -#define MULTIBOOT_INFO_MEMORY 0x00000001 -/* is there a boot device set? */ -#define MULTIBOOT_INFO_BOOTDEV 0x00000002 -/* is the command-line defined? */ -#define MULTIBOOT_INFO_CMDLINE 0x00000004 -/* are there modules to do something with? */ -#define MULTIBOOT_INFO_MODS 0x00000008 - -/* These next two are mutually exclusive */ - -/* is there a symbol table loaded? */ -#define MULTIBOOT_INFO_AOUT_SYMS 0x00000010 -/* is there an ELF section header table? */ -#define MULTIBOOT_INFO_ELF_SHDR 0X00000020 - -/* is there a full memory map? */ -#define MULTIBOOT_INFO_MEM_MAP 0x00000040 - -/* Is there drive info? */ -#define MULTIBOOT_INFO_DRIVE_INFO 0x00000080 - -/* Is there a config table? */ -#define MULTIBOOT_INFO_CONFIG_TABLE 0x00000100 - -/* Is there a boot loader name? */ -#define MULTIBOOT_INFO_BOOT_LOADER_NAME 0x00000200 - -/* Is there a APM table? */ -#define MULTIBOOT_INFO_APM_TABLE 0x00000400 - -/* Is there video information? */ -#define MULTIBOOT_INFO_VBE_INFO 0x00000800 -#define MULTIBOOT_INFO_FRAMEBUFFER_INFO 0x00001000 - -#ifndef ASM_FILE - -typedef unsigned char multiboot_uint8_t; -typedef unsigned short multiboot_uint16_t; -typedef unsigned int multiboot_uint32_t; -typedef unsigned long long multiboot_uint64_t; - -struct multiboot_header -{ - /* Must be MULTIBOOT_MAGIC - see above. */ - multiboot_uint32_t magic; - - /* Feature flags. */ - multiboot_uint32_t flags; - - /* The above fields plus this one must equal 0 mod 2^32. */ - multiboot_uint32_t checksum; - - /* These are only valid if MULTIBOOT_AOUT_KLUDGE is set. */ - multiboot_uint32_t header_addr; - multiboot_uint32_t load_addr; - multiboot_uint32_t load_end_addr; - multiboot_uint32_t bss_end_addr; - multiboot_uint32_t entry_addr; - - /* These are only valid if MULTIBOOT_VIDEO_MODE is set. */ - multiboot_uint32_t mode_type; - multiboot_uint32_t width; - multiboot_uint32_t height; - multiboot_uint32_t depth; -}; - -/* The symbol table for a.out. */ -struct multiboot_aout_symbol_table -{ - multiboot_uint32_t tabsize; - multiboot_uint32_t strsize; - multiboot_uint32_t addr; - multiboot_uint32_t reserved; -}; -typedef struct multiboot_aout_symbol_table multiboot_aout_symbol_table_t; - -/* The section header table for ELF. */ -struct multiboot_elf_section_header_table -{ - multiboot_uint32_t num; - multiboot_uint32_t size; - multiboot_uint32_t addr; - multiboot_uint32_t shndx; -}; -typedef struct multiboot_elf_section_header_table multiboot_elf_section_header_table_t; - -struct multiboot_info -{ - /* Multiboot info version number */ - multiboot_uint32_t flags; - - /* Available memory from BIOS */ - multiboot_uint32_t mem_lower; - multiboot_uint32_t mem_upper; - - /* "root" partition */ - multiboot_uint32_t boot_device; - - /* Kernel command line */ - multiboot_uint32_t cmdline; - - /* Boot-Module list */ - multiboot_uint32_t mods_count; - multiboot_uint32_t mods_addr; - - union - { - multiboot_aout_symbol_table_t aout_sym; - multiboot_elf_section_header_table_t elf_sec; - } u; - - /* Memory Mapping buffer */ - multiboot_uint32_t mmap_length; - multiboot_uint32_t mmap_addr; - - /* Drive Info buffer */ - multiboot_uint32_t drives_length; - multiboot_uint32_t drives_addr; - - /* ROM configuration table */ - multiboot_uint32_t config_table; - - /* Boot Loader Name */ - multiboot_uint32_t boot_loader_name; - - /* APM table */ - multiboot_uint32_t apm_table; - - /* Video */ - multiboot_uint32_t vbe_control_info; - multiboot_uint32_t vbe_mode_info; - multiboot_uint16_t vbe_mode; - multiboot_uint16_t vbe_interface_seg; - multiboot_uint16_t vbe_interface_off; - multiboot_uint16_t vbe_interface_len; - - multiboot_uint64_t framebuffer_addr; - multiboot_uint32_t framebuffer_pitch; - multiboot_uint32_t framebuffer_width; - multiboot_uint32_t framebuffer_height; - multiboot_uint8_t framebuffer_bpp; -#define MULTIBOOT_FRAMEBUFFER_TYPE_INDEXED 0 -#define MULTIBOOT_FRAMEBUFFER_TYPE_RGB 1 -#define MULTIBOOT_FRAMEBUFFER_TYPE_EGA_TEXT 2 - multiboot_uint8_t framebuffer_type; - union - { - struct - { - multiboot_uint32_t framebuffer_palette_addr; - multiboot_uint16_t framebuffer_palette_num_colors; - }; - struct - { - multiboot_uint8_t framebuffer_red_field_position; - multiboot_uint8_t framebuffer_red_mask_size; - multiboot_uint8_t framebuffer_green_field_position; - multiboot_uint8_t framebuffer_green_mask_size; - multiboot_uint8_t framebuffer_blue_field_position; - multiboot_uint8_t framebuffer_blue_mask_size; - }; - }; -}; -typedef struct multiboot_info multiboot_info_t; - -struct multiboot_color -{ - multiboot_uint8_t red; - multiboot_uint8_t green; - multiboot_uint8_t blue; -}; - -struct multiboot_mmap_entry -{ - multiboot_uint32_t size; - multiboot_uint64_t addr; - multiboot_uint64_t len; -#define MULTIBOOT_MEMORY_AVAILABLE 1 -#define MULTIBOOT_MEMORY_RESERVED 2 -#define MULTIBOOT_MEMORY_ACPI_RECLAIMABLE 3 -#define MULTIBOOT_MEMORY_NVS 4 -#define MULTIBOOT_MEMORY_BADRAM 5 - multiboot_uint32_t type; -} __attribute__((packed)); -typedef struct multiboot_mmap_entry multiboot_memory_map_t; - -struct multiboot_mod_list -{ - /* the memory used goes from bytes ’mod_start’ to ’mod_end-1’ inclusive */ - multiboot_uint32_t mod_start; - multiboot_uint32_t mod_end; - - /* Module command line */ - multiboot_uint32_t cmdline; - - /* padding to take it to 16 bytes (must be zero) */ - multiboot_uint32_t pad; -}; -typedef struct multiboot_mod_list multiboot_module_t; - -/* APM BIOS info. */ -struct multiboot_apm_info -{ - multiboot_uint16_t version; - multiboot_uint16_t cseg; - multiboot_uint32_t offset; - multiboot_uint16_t cseg_16; - multiboot_uint16_t dseg; - multiboot_uint16_t flags; - multiboot_uint16_t cseg_len; - multiboot_uint16_t cseg_16_len; - multiboot_uint16_t dseg_len; -}; - -#endif /* ! ASM_FILE */ - -#endif /* ! MULTIBOOT_HEADER */ diff --git a/targets/x86_64/src/loader/print.inc b/targets/x86_64/src/loader/print.inc deleted file mode 100644 index add8336..0000000 --- a/targets/x86_64/src/loader/print.inc +++ /dev/null @@ -1,70 +0,0 @@ - -;;; Procedure to print a text on the screen. Takes the address of a 0-terminated string in the SI register. -print_string: -print_string_loop: - lodsb ; load whatever is at SI to AL and increment SI - or al, al ; check if AL is 0 - jz print_string_loop_done ; if AL is 0, go to the end - - call print_char ; print the character in AL - jmp print_string_loop - -print_string_loop_done: - add byte [terminal_posy], 1 ; next row - mov byte [terminal_posx], 0 ; back to the left - ret - -;;; Procedure to print a single character. Takes the character in the AL register. -print_char: - mov ah, 0x0F ; set the higher half of AX to white on black color - mov cx, ax ; preserve the content for later - - ; now calculate the memory offset for the Y position - movzx ax, byte [terminal_posy] ; first load the Y position - mov dx, 160 ; then multiply it with 160 (2 bytes per char, 80 columns) - mul dx ; stores the result in AX - - ; next calculate the offset for the X position - movzx bx, byte [terminal_posx] ; load the X position - shl bx, 1 ; multiply by 2 (2 bytes per char) - - ; finally add the two - mov di, 0 - add di, ax ; y offset - add di, bx ; x offset - - ; restore the character and write it - mov ax, cx - stosw - - ; advance the X position and done - add byte [terminal_posx], 1 - ret - -;;; Procedure to clear the screen (set everything to 0) -clear_screen: - mov ax, 0 - mov di, 0 -clear_screen_loop: - stosw ; write the 0 (2 bytes at a time) - cmp di, 2 * 25 * 60 ; check if we cleaned all 80 * 60 * 2 bytes - jne clear_screen_loop ; if not, repeat - mov byte [terminal_posx], 0 - mov byte [terminal_posy], 0 - ret - -; ;;; Procedure to print a text on the using the BIOS. Takes the address to a 0-terminated string in the SI register. -; bios_print: -; bios_print_loop: -; lodsb ; load whatever is at SI to AL and increment SI -; or al, al ; check if AL is 0 -; jz bios_print_done ; if AL is 0, go to the end -; -; ; invoke Int 10/AH=0Eh (VIDEO - TELETYPE OUTPUT) -; ; Takes a character from AL, writes it to the screen and advances the cursor. -; mov ah, 0x0E -; mov bh, 0 -; int 0x10 -; jmp bios_print_loop ; next char -; bios_print_done: -; ret \ No newline at end of file