├── .gitignore ├── tools ├── chainloader └── Makefile ├── grub └── grub.cfg ├── install_ubuntu_deps.sh ├── src ├── hw │ ├── serial.h │ └── serial1.c ├── main.h ├── kernel │ ├── tls.hpp │ ├── start32.c │ ├── tls.cpp │ ├── elf.hpp │ ├── dylib.hpp │ ├── kernel_start.c │ ├── panic.cpp │ ├── init_libc.c │ ├── elf.cpp │ ├── start.asm │ └── start64.asm ├── kprint.h ├── crt │ ├── c_stubs.c │ ├── heap.c │ ├── cxxabi.cpp │ ├── malloc.c │ ├── print.c │ ├── c_abi.c │ ├── ubsan.c │ └── udiv.c ├── CMakeLists.txt ├── linker.ld └── multiboot.h ├── .gitmodules ├── machines ├── shared │ ├── library │ │ ├── interface.hpp │ │ ├── CMakeLists.txt │ │ └── library.cpp │ ├── CMakeLists.txt │ ├── README.md │ └── main.cpp ├── default │ ├── CMakeLists.txt │ └── main.cpp ├── eastl │ ├── CMakeLists.txt │ └── main.cpp ├── 32bit │ ├── CMakeLists.txt │ └── main.cpp └── chainloader │ ├── hotswap.c │ └── chainloader.cpp ├── ext └── CMakeLists.txt ├── run.sh ├── README.md └── barebones.cmake /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.d 3 | chainloader 4 | temp_disk 5 | grub.iso 6 | build/ 7 | -------------------------------------------------------------------------------- /tools/chainloader: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fwsGonzo/barebones/HEAD/tools/chainloader -------------------------------------------------------------------------------- /grub/grub.cfg: -------------------------------------------------------------------------------- 1 | set timeout=0 2 | set default=0 # Set the default menu entry 3 | 4 | menuentry "My kernel" { 5 | multiboot /boot/mykernel 6 | boot 7 | } 8 | -------------------------------------------------------------------------------- /install_ubuntu_deps.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | git submodule update --init --recursive 3 | sudo apt install build-essential g++-multilib nasm qemu xorriso cmake cmake-curses-gui 4 | -------------------------------------------------------------------------------- /src/hw/serial.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | extern void __serial_print1(const char*); 4 | extern void __serial_print(const char*, int); 5 | extern void __serial_putchr(void*, char); 6 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "ext/EASTL"] 2 | path = ext/EASTL 3 | url = https://github.com/electronicarts/EASTL.git 4 | [submodule "ext/tinyprintf"] 5 | path = ext/tinyprintf 6 | url = git@github.com:fwsGonzo/tinyprintf.git 7 | -------------------------------------------------------------------------------- /src/main.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | extern void kernel_main(uint32_t eax, uint32_t ebx); 9 | 10 | #ifdef __cplusplus 11 | } 12 | #endif 13 | -------------------------------------------------------------------------------- /machines/shared/library/interface.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | struct jumptable 5 | { 6 | int (*kprintf)(const char* string, ...); 7 | void* (*malloc) (size_t); 8 | void (*free) (void*); 9 | }; 10 | 11 | typedef int (*init_function_t) (jumptable*); 12 | -------------------------------------------------------------------------------- /src/kernel/tls.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | struct tls_table 5 | { 6 | // thread self-pointer 7 | void* tls_data; // 0x0 8 | // per-cpu cpuid 9 | int cpuid; 10 | 11 | uintptr_t pad[3]; 12 | uintptr_t guard; // _SENTINEL_VALUE_ 13 | }; 14 | extern tls_table tls; 15 | -------------------------------------------------------------------------------- /src/kernel/start32.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | extern uint32_t multiboot_data_magic; 4 | extern uint32_t multiboot_data_address; 5 | extern void kernel_start(uint32_t eax, uint32_t ebx); 6 | 7 | void begin_enter_longmode() 8 | { 9 | kernel_start(multiboot_data_magic, multiboot_data_address); 10 | } 11 | -------------------------------------------------------------------------------- /machines/default/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.1) 2 | project (kernel C CXX) 3 | include(../../barebones.cmake) 4 | 5 | add_machine_image( 6 | # name, binary and description 7 | mykernel "my_kernel" "This is a test kernel!" 8 | # list of source files 9 | main.cpp 10 | ) 11 | 12 | target_compile_definitions(mykernel PRIVATE MYTEST="4") 13 | -------------------------------------------------------------------------------- /machines/eastl/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.1) 2 | project (kernel C CXX) 3 | 4 | option(EASTL "" ON) 5 | option(RTTI_EXCEPTIONS "" ON) 6 | include(../../barebones.cmake) 7 | 8 | add_machine_image( 9 | # name, binary and description 10 | mykernel "eastl_kernel" "This is a test for EASTL!" 11 | # list of source files 12 | main.cpp 13 | ) 14 | -------------------------------------------------------------------------------- /machines/32bit/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.1) 2 | project (kernel C CXX) 3 | 4 | option(BUILD_32 "" ON) 5 | include(../../barebones.cmake) 6 | 7 | add_machine_image( 8 | # name, binary and description 9 | mykernel "my_kernel" "This is a 32-bit test kernel!" 10 | # list of source files 11 | main.cpp 12 | ) 13 | 14 | target_compile_definitions(mykernel PRIVATE MYTEST="4") 15 | -------------------------------------------------------------------------------- /src/kernel/tls.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "tls.hpp" 3 | #ifdef __x86_64__ 4 | static_assert(offsetof(tls_table, guard) == 0x28, "TLS is at 0x28 on amd64"); 5 | #elif __i386__ 6 | static_assert(offsetof(tls_table, guard) == 0x14, "TLS is at 0x18 on i386"); 7 | #endif 8 | 9 | // we have to store this in .data otherwise .bss initialization 10 | // will overwrite this in stdlib init 11 | struct tls_table tls; 12 | -------------------------------------------------------------------------------- /src/kernel/elf.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | struct Elf 5 | { 6 | static bool validate(Elf64_Ehdr* hdr); 7 | static const Elf64_Shdr* section_by_name(const Elf64_Ehdr*, const char* name); 8 | 9 | // get the symbol associated with @name 10 | static const Elf64_Sym* resolve_name(const Elf64_Ehdr*, const char* name); 11 | // dynamic loader 12 | static void perform_relocations(Elf64_Ehdr* hdr); 13 | }; 14 | -------------------------------------------------------------------------------- /machines/chainloader/hotswap.c: -------------------------------------------------------------------------------- 1 | #include 2 | asm(".org 0x2000"); 3 | 4 | void hotswap(const char* base, int len, char* dest, void* start, 5 | uintptr_t magic, uintptr_t bootinfo) 6 | { 7 | for (int i = 0; i < len; i++) 8 | dest[i] = base[i]; 9 | 10 | asm volatile("jmp *%0" : : "r" (start), "a" (magic), "b" (bootinfo)); 11 | asm volatile (".global __hotswap_end;\n__hotswap_end:"); 12 | __builtin_unreachable(); 13 | } 14 | -------------------------------------------------------------------------------- /src/kprint.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef __cplusplus 4 | extern "C" { 5 | #endif 6 | 7 | #include 8 | 9 | // better, more familiar way to print! 10 | extern int kprintf(const char* fmt, ...) __attribute__((format(printf, 1, 2))); 11 | 12 | // print text directly to serial port 13 | static inline void kprint(const char* text) 14 | { 15 | __serial_print1(text); 16 | } 17 | 18 | #include 19 | 20 | #ifdef __cplusplus 21 | } 22 | #endif 23 | -------------------------------------------------------------------------------- /machines/shared/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.1) 2 | project (kernel C CXX) 3 | include(../../barebones.cmake) 4 | 5 | add_machine_image( 6 | # name, binary and description 7 | mykernel "my_kernel" "This is a test kernel!" 8 | # list of source files 9 | main.cpp 10 | ) 11 | #target_compile_definitions(mykernel PRIVATE MYTEST="4") 12 | 13 | add_subdirectory(library) 14 | add_machine_payload(mykernel mylib_payload 15 | ${CMAKE_BINARY_DIR}/library/libmylib.so mylib) 16 | add_dependencies(mylib_payload mylib) 17 | -------------------------------------------------------------------------------- /src/crt/c_stubs.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | char *getenv(const char *name) { 8 | //kprintf("getenv called for %s\n", name); 9 | (void) name; 10 | return NULL; 11 | } 12 | int setenv(const char *name, const char *value, int overwrite) { 13 | (void) name; 14 | (void) value; 15 | (void) overwrite; 16 | return -1; 17 | } 18 | int dl_iterate_phdr( 19 | int (*callback) (struct dl_phdr_info *info, size_t size, void *data), void *data) 20 | { 21 | return 0; 22 | } 23 | -------------------------------------------------------------------------------- /machines/shared/README.md: -------------------------------------------------------------------------------- 1 | ## Machine with shared library loader 2 | 3 | This tiny machine allows you to call into a function in a shared library, and just enough to make it usable. No constructors or destructors. 4 | 5 | ## Questions 6 | 7 | - Why is the init function using the C calling convention? 8 | - It's to make it easier to look up the functions name, as it won't be mangled. 9 | - Can you look up functions from the shared library? 10 | - No, and that is ideally something done at loading time where functions are resolved automatically, which is something called dynamic linking. 11 | -------------------------------------------------------------------------------- /ext/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | # EASTL C++ library 3 | if (EASTL) 4 | add_library(eastl STATIC 5 | EASTL/source/allocator_eastl.cpp EASTL/source/assert.cpp 6 | EASTL/source/fixed_pool.cpp EASTL/source/hashtable.cpp 7 | EASTL/source/intrusive_list.cpp EASTL/source/numeric_limits.cpp 8 | EASTL/source/red_black_tree.cpp EASTL/source/string.cpp 9 | ) 10 | target_include_directories(eastl PUBLIC 11 | EASTL/include 12 | EASTL/test/packages/EABase/include/Common 13 | ) 14 | endif() 15 | 16 | # Tiny-printf library 17 | add_library(tinyprintf STATIC 18 | tinyprintf/tinyprintf.c 19 | ) 20 | target_include_directories(tinyprintf PUBLIC 21 | tinyprintf 22 | ) 23 | target_compile_definitions(tinyprintf PUBLIC TINYPRINTF_OVERRIDE_LIBC=0) 24 | -------------------------------------------------------------------------------- /machines/shared/library/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.1) 2 | project(mylib VERSION 1.0 DESCRIPTION "mylib description") 3 | 4 | add_library(mylib SHARED 5 | library.cpp 6 | ) 7 | 8 | # unfortunately we have to do this to shake some of the arguments from the 9 | # top level project 10 | set(CMAKE_CXX_FLAGS "-Wall -Wextra -g -O2 -ffreestanding -fno-omit-frame-pointer") 11 | 12 | target_compile_options(mylib PRIVATE "-nostdlib") 13 | target_compile_options(mylib PRIVATE "-Os") 14 | 15 | set_target_properties(mylib PROPERTIES VERSION ${PROJECT_VERSION}) 16 | set_target_properties(mylib PROPERTIES SOVERSION 1) 17 | 18 | set_target_properties(mylib PROPERTIES PUBLIC_HEADER library.hpp) 19 | target_include_directories(mylib PRIVATE .) 20 | -------------------------------------------------------------------------------- /src/kernel/dylib.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | struct Dylib 5 | { 6 | static Elf64_Ehdr* load(const void* address); 7 | 8 | template 9 | static T resolve_function(const Elf64_Ehdr* hdr, const char* name); 10 | }; 11 | 12 | 13 | inline Elf64_Ehdr* Dylib::load(const void* address) 14 | { 15 | auto* hdr = (Elf64_Ehdr*) address; 16 | // validate shared library ELF header 17 | assert(Elf::validate(hdr)); 18 | // resolve symbolic references from PLT and GOT 19 | Elf::perform_relocations(hdr); 20 | return hdr; 21 | } 22 | template 23 | inline T Dylib::resolve_function(const Elf64_Ehdr* hdr, const char* name) 24 | { 25 | const auto* sym = Elf::resolve_name(hdr, name); 26 | if (sym == nullptr) return T(); 27 | return (T) &((char*) hdr)[sym->st_value]; 28 | } 29 | -------------------------------------------------------------------------------- /machines/shared/library/library.cpp: -------------------------------------------------------------------------------- 1 | #include "interface.hpp" 2 | static int test_value = 666; 3 | 4 | struct Test { 5 | int a; 6 | }; 7 | Test test; 8 | 9 | extern "C" 10 | __attribute__((noinline)) 11 | void test_function(jumptable* table) 12 | { 13 | table->kprintf("Hello from test()! jumptable = %p\n", table); 14 | } 15 | 16 | extern "C" 17 | int init(jumptable* t) 18 | { 19 | jumptable* table = t; 20 | table->kprintf("Hello world from a shared library! table = %p\n", table); 21 | table->kprintf("test_value == %d\n", test_value); 22 | 23 | char* data = (char*) table->malloc(4096); 24 | table->kprintf("malloc() returned %p\n", data); 25 | 26 | for (int i = 0; i < 4096; i++) { 27 | data[i] = i & 0xFF; 28 | } 29 | table->free(data); 30 | 31 | test.a = 55; 32 | test_function(t); 33 | return 0; 34 | } 35 | -------------------------------------------------------------------------------- /machines/shared/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | extern char _binary_mylib_start; 8 | extern char _binary_mylib_end; 9 | #include 10 | #include "library/interface.hpp" // a made-up interface 11 | 12 | void kernel_main(const uint32_t, const uint32_t) 13 | { 14 | // load address is the start of the mylib blob 15 | const auto* hdr = Dylib::load(&_binary_mylib_start); 16 | assert(hdr != nullptr); 17 | 18 | // translate init function name into symbol and create callable function 19 | auto init = Dylib::resolve_function(hdr, "init"); 20 | assert(init != nullptr); 21 | 22 | // setup call table 23 | static jumptable table; 24 | table.kprintf = kprintf; 25 | table.malloc = malloc; 26 | table.free = free; 27 | 28 | // call into library function 29 | const int result = init(&table); 30 | kprintf("init call result: %d\n", result); 31 | } 32 | -------------------------------------------------------------------------------- /src/kernel/kernel_start.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | static void __init_paging() 6 | { 7 | static uintptr_t pdir0[512] __attribute__((aligned(4096))); 8 | // unmap zero page 9 | assert(((uintptr_t) pdir0 & 0xfff) == 0); 10 | pdir0[0] = 0x0; // unpresent zero page 11 | for (uintptr_t i = 1; i < 512; i++) { 12 | pdir0[i] = (i * 0x1000) | 0x3; // RW + P 13 | } 14 | // install into PML2 entry 0 15 | uintptr_t* pml2 = (uintptr_t*) 0x3000; 16 | pml2[0] = ((uintptr_t) pdir0) | 0x3; // RW + P; 17 | __asm__ ("invlpg 0x0"); 18 | } 19 | 20 | void kernel_start(uint32_t eax, uint32_t ebx) 21 | { 22 | kprintf("kernel_start(eax: %x, ebx: %x)\n", eax, ebx); 23 | 24 | extern void __init_stdlib(uint32_t, uint32_t); 25 | __init_stdlib(eax, ebx); 26 | 27 | // we have to do this after initializing .bss 28 | // NOTE: don't enable this until you catch CPU exceptions! 29 | //__init_paging(); 30 | 31 | extern void kernel_main(uint32_t, uint32_t); 32 | kernel_main(eax, ebx); 33 | } 34 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | set(KERNEL_SOURCES 3 | # .c files 4 | kernel/kernel_start.c 5 | kernel/init_libc.c 6 | hw/serial1.c 7 | crt/c_abi.c 8 | crt/c_stubs.c 9 | crt/heap.c 10 | crt/malloc.c 11 | crt/print.c 12 | crt/ubsan.c 13 | # .cpp files 14 | crt/cxxabi.cpp 15 | kernel/elf.cpp 16 | kernel/tls.cpp 17 | kernel/panic.cpp 18 | # .asm files (for NASM) 19 | kernel/start.asm 20 | ) 21 | if (BUILD_32) 22 | list(APPEND KERNEL_SOURCES 23 | kernel/start32.c 24 | crt/udiv.c 25 | ) 26 | else() 27 | list(APPEND KERNEL_SOURCES 28 | kernel/start64.asm 29 | ) 30 | endif() 31 | 32 | add_library(kernel STATIC ${KERNEL_SOURCES}) 33 | target_include_directories(kernel PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) 34 | target_link_libraries(kernel tinyprintf) 35 | 36 | if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") 37 | set_source_files_properties( 38 | kernel/panic.cpp 39 | PROPERTIES COMPILE_FLAGS -Wno-frame-address) 40 | endif() 41 | 42 | if (RTTI_EXCEPTIONS) 43 | target_compile_definitions(kernel PRIVATE -DEH_ENABLED) 44 | endif() 45 | -------------------------------------------------------------------------------- /src/crt/heap.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | static uintptr_t heap_start; 7 | static uintptr_t heap_end; 8 | // heap_max really should be set to max physical 9 | // by reading the value from multiboot meminfo table 10 | static uintptr_t heap_max = 0xC0000000; 11 | 12 | void __init_heap(void* free_begin) 13 | { 14 | heap_start = (uintptr_t) free_begin; 15 | if (heap_start & 0xF) heap_start += 0x10 - (heap_start & 0xF); 16 | heap_end = heap_start; 17 | assert(((heap_start & 0xF) == 0) && "Heap should be aligned"); 18 | } 19 | 20 | // data segment size 21 | void* sbrk(intptr_t increment) 22 | { 23 | uintptr_t old = heap_end; 24 | if (heap_end + increment <= heap_max) 25 | { 26 | heap_end += increment; 27 | return (void*) old; 28 | } 29 | return (void*) -1; 30 | } 31 | 32 | int posix_memalign(void **memptr, size_t alignment, size_t size) 33 | { 34 | uintptr_t addr = (uintptr_t) malloc(size + alignment); 35 | const intptr_t offset = addr & (alignment-1); 36 | if (offset) addr += (alignment-1) - offset; 37 | *memptr = (void*) addr; 38 | return 0; 39 | } 40 | -------------------------------------------------------------------------------- /run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | GRAPHICS=-nographic 4 | 5 | for i in "$@" 6 | do 7 | case $i in 8 | --kvm) 9 | KVM="--enable-kvm -cpu host" 10 | shift # past argument with no value 11 | ;; 12 | --vga) 13 | GRAPHICS="-vga std" 14 | shift # past argument with no value 15 | ;; 16 | --sanitize) 17 | OPTION="sanitize" 18 | shift # past argument with no value 19 | ;; 20 | --clean) 21 | rm -rf $BUILD_DIR/ 22 | exit 0 23 | ;; 24 | *) 25 | # unknown option 26 | echo "--kvm, --vga, --sanitize, --clean" 27 | ;; 28 | esac 29 | done 30 | 31 | MACHINE=machines/${1-default} 32 | BUILD_DIR=$MACHINE/build 33 | 34 | pushd $MACHINE 35 | mkdir -p build 36 | pushd build 37 | cmake .. 38 | make -j4 $OPTION 39 | BINARY=$BUILD_DIR/`cat binary.txt` 40 | popd 41 | popd 42 | 43 | # NOTE: if building with -march=native, make sure to enable KVM, 44 | # as emulated qemu only supports up to SSE3 instructions 45 | CLASS=`od -An -t x1 -j 4 -N 1 $BINARY` 46 | if [ $CLASS == "02" ]; then 47 | echo "Starting 64-bit kernel: $BINARY" 48 | qemu-system-x86_64 $KVM -kernel tools/chainloader -initrd $BINARY $GRAPHICS 49 | else 50 | echo "Starting 32-bit kernel: $BINARY" 51 | qemu-system-x86_64 $KVM -kernel $BINARY $GRAPHICS 52 | fi 53 | -------------------------------------------------------------------------------- /src/kernel/panic.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | // less risky when the stack is blown out 4 | static char buffer[4096]; 5 | 6 | #define frp(N, ra) \ 7 | (__builtin_frame_address(N) != nullptr) && \ 8 | (ra = __builtin_return_address(N)) != nullptr && ra != (void*)-1 9 | 10 | static void print_trace(const int N, const void* ra) 11 | { 12 | snprintf(buffer, sizeof(buffer), 13 | "[%d] %p\n", 14 | N, ra); 15 | kprint(buffer); 16 | } 17 | 18 | extern "C" 19 | void print_backtrace() 20 | { 21 | kprintf("\nBacktrace:\n"); 22 | void* ra; 23 | if (frp(0, ra)) { 24 | print_trace(0, ra); 25 | if (frp(1, ra)) { 26 | print_trace(1, ra); 27 | if (frp(2, ra)) { 28 | print_trace(2, ra); 29 | } 30 | } 31 | } 32 | } 33 | 34 | extern "C" 35 | __attribute__((noreturn)) 36 | void panic(const char* reason) 37 | { 38 | kprintf("\n\n!!! PANIC !!!\n%s\n", reason); 39 | 40 | print_backtrace(); 41 | 42 | // the end 43 | kprintf("\nKernel halting...\n"); 44 | while (1) asm("cli; hlt"); 45 | __builtin_unreachable(); 46 | } 47 | 48 | extern "C" 49 | void abort() 50 | { 51 | panic("Abort called"); 52 | } 53 | 54 | extern "C" 55 | void abort_message(const char* fmt, ...) 56 | { 57 | va_list arg; 58 | va_start (arg, fmt); 59 | int bytes = tfp_vsnprintf(buffer, sizeof(buffer), fmt, arg); 60 | (void) bytes; 61 | va_end (arg); 62 | panic(buffer); 63 | } 64 | -------------------------------------------------------------------------------- /machines/32bit/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | static bool test_sse(); 6 | static int test_value = 0; 7 | 8 | void kernel_main(const uint32_t eax, const uint32_t ebx) 9 | { 10 | kprint("------------------\n"); 11 | kprintf("* Multiboot EAX: 0x%x\n", eax); 12 | kprintf("* Multiboot EBX: 0x%x\n", ebx); 13 | // some helpful self-checks 14 | kprint("* SSE instructions ... "); 15 | kprint(test_sse() ? "work!\n" : "did NOT work!\n"); 16 | kprint("* Global constructors ... "); 17 | kprint(test_value ? "work!\n" : "did NOT work!\n"); 18 | 19 | #ifdef UBSAN_ENABLED 20 | // when undefined sanitizer is enabled, we can manually trigger it like this 21 | uint64_t test = 0; 22 | char* misaligned = (char*) &test + 2; 23 | *(uint32_t*) misaligned = 0x12345678; 24 | kprintf("kernel_main is at %p\n", kernel_main); 25 | kprintf("misaligned is %p\n", (void*) test); 26 | #endif 27 | 28 | kprint( 29 | "\n" 30 | "Hello OSdev world!\n" 31 | KERNEL_BINARY " (" KERNEL_DESC ")\n" 32 | "\n" 33 | "Press Ctrl+A -> X to close\n" 34 | ); 35 | } 36 | 37 | __attribute__((constructor)) 38 | static void my_cpp_constructor() { 39 | test_value = 1; 40 | } 41 | 42 | bool test_sse() 43 | { 44 | typedef union 45 | { 46 | __m128i i128; 47 | int32_t i32[4]; 48 | } imm; 49 | volatile imm xmm1; 50 | xmm1.i128 = _mm_set_epi32(1, 2, 3, 4); 51 | return ( 52 | xmm1.i32[0] == 4 && xmm1.i32[1] == 3 && 53 | xmm1.i32[2] == 2 && xmm1.i32[3] == 1); 54 | } 55 | -------------------------------------------------------------------------------- /machines/default/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | static bool test_sse(); 6 | static int test_value = 0; 7 | 8 | void kernel_main(const uint32_t eax, const uint32_t ebx) 9 | { 10 | kprint("------------------\n"); 11 | kprintf("* Multiboot EAX: 0x%x\n", eax); 12 | kprintf("* Multiboot EBX: 0x%x\n", ebx); 13 | // some helpful self-checks 14 | kprint("* SSE instructions ... "); 15 | kprint(test_sse() ? "work!\n" : "did NOT work!\n"); 16 | kprint("* Global constructors ... "); 17 | kprint(test_value ? "work!\n" : "did NOT work!\n"); 18 | 19 | #ifdef UBSAN_ENABLED 20 | // when undefined sanitizer is enabled, we can manually trigger it like this 21 | uint64_t test = 0; 22 | char* misaligned = (char*) &test + 2; 23 | *(uint32_t*) misaligned = 0x12345678; 24 | kprintf("kernel_main is at %p\n", kernel_main); 25 | kprintf("misaligned is %p\n", (void*) test); 26 | #endif 27 | 28 | kprint( 29 | "\n" 30 | "Hello OSdev world!\n" 31 | KERNEL_BINARY " (" KERNEL_DESC ")\n" 32 | "\n" 33 | "Press Ctrl+A -> X to close\n" 34 | ); 35 | } 36 | 37 | __attribute__((constructor)) 38 | static void my_cpp_constructor() { 39 | test_value = 1; 40 | } 41 | 42 | bool test_sse() 43 | { 44 | typedef union 45 | { 46 | __m128i i128; 47 | int32_t i32[4]; 48 | } imm; 49 | volatile imm xmm1; 50 | xmm1.i128 = _mm_set_epi32(1, 2, 3, 4); 51 | return ( 52 | xmm1.i32[0] == 4 && xmm1.i32[1] == 3 && 53 | xmm1.i32[2] == 2 && xmm1.i32[3] == 1); 54 | } 55 | -------------------------------------------------------------------------------- /src/hw/serial1.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | static const uint16_t port = 0x3F8; // Serial port 1 4 | static char initialized = 0; 5 | 6 | static inline uint8_t inb(int port) 7 | { 8 | int ret; 9 | asm ("xorl %eax,%eax"); 10 | asm ("inb %%dx,%%al":"=a" (ret):"d"(port)); 11 | return ret; 12 | } 13 | static inline void outb(int port, uint8_t data) 14 | { 15 | asm ("outb %%al,%%dx"::"a" (data), "d"(port)); 16 | } 17 | 18 | static inline void init_serial_if_needed() 19 | { 20 | if (initialized) return; 21 | initialized = 1; 22 | // properly initialize serial port 23 | outb(port + 1, 0x00); // Disable all interrupts 24 | outb(port + 3, 0x80); // Enable DLAB (set baud rate divisor) 25 | outb(port + 0, 0x03); // Set divisor to 3 (lo byte) 38400 baud 26 | outb(port + 1, 0x00); // (hi byte) 27 | outb(port + 3, 0x03); // 8 bits, no parity, one stop bit 28 | outb(port + 2, 0xC7); // Enable FIFO, clear them, with 14-byte threshold 29 | } 30 | 31 | void __serial_print1(const char* cstr) 32 | { 33 | init_serial_if_needed(); 34 | while (*cstr) { 35 | while (!(inb(port + 5) & 0x20)) /* */; 36 | outb(port, *cstr++); 37 | } 38 | } 39 | void __serial_print(const char* str, int len) 40 | { 41 | init_serial_if_needed(); 42 | for (int i = 0; i < len; i++) { 43 | while (!(inb(port + 5) & 0x20)) /* */; 44 | outb(port, str[i]); 45 | } 46 | } 47 | 48 | // buffered serial output 49 | static char buffer[256]; 50 | static unsigned cnt = 0; 51 | 52 | void fflush(void* fileno) 53 | { 54 | (void) fileno; 55 | __serial_print(buffer, cnt); 56 | cnt = 0; 57 | } 58 | 59 | void __serial_putchr(const void* file, char c) 60 | { 61 | (void) file; 62 | buffer[cnt++] = c; 63 | if (c == '\n' || cnt == sizeof(buffer)) { 64 | fflush(0); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /machines/eastl/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | class IdioticException : public std::exception 8 | { 9 | const char* oh_god; 10 | public: 11 | IdioticException(const char* reason) : oh_god(reason) {} 12 | const char* what() const noexcept override 13 | { 14 | return oh_god; 15 | } 16 | }; 17 | 18 | using callback_t = int (*) (eastl::vector&); 19 | static void test_rtti(); 20 | 21 | 22 | void kernel_main(uint32_t /*eax*/, uint32_t /*ebx*/) 23 | { 24 | kprintf(KERNEL_DESC "\n"); 25 | 26 | const callback_t callback = [] (auto& vec) -> int { 27 | kprintf("EASTL vector size: %zu (last element is %d)\n", 28 | vec.size(), vec.back()); 29 | throw IdioticException{"Test!"}; 30 | }; 31 | 32 | eastl::vector test; 33 | int caught = 0; 34 | for (int i = 0; i < 16; i++) 35 | { 36 | test.push_back(i); 37 | try { 38 | assert(callback(test) == 0); 39 | } 40 | catch (const std::exception& e) { 41 | kprintf("Exceptions caught: %d\n", ++caught); 42 | } 43 | } 44 | assert(caught == 16); 45 | test_rtti(); 46 | throw IdioticException("This is on purpose"); 47 | } 48 | 49 | class A 50 | { 51 | public: 52 | virtual void f() { kprintf("A::f() called\n"); } 53 | }; 54 | 55 | class B : public A 56 | { 57 | public: 58 | void f() { kprintf("B::f() called\n"); } 59 | }; 60 | 61 | static void test_rtti() 62 | { 63 | A a; 64 | B b; 65 | a.f(); // A::f() 66 | b.f(); // B::f() 67 | 68 | A *pA = &a; 69 | B *pB = &b; 70 | pA->f(); // A::f() 71 | pB->f(); // B::f() 72 | 73 | pA = &b; 74 | // pB = &a; // not allowed 75 | pB = dynamic_cast(&a); // allowed but it returns NULL 76 | assert(pB == nullptr); 77 | } 78 | -------------------------------------------------------------------------------- /src/kernel/init_libc.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | extern size_t strlen(const char* str); 8 | extern char _end; 9 | 10 | static void* multiboot_free_begin(intptr_t mb_addr) 11 | { 12 | const multiboot_info_t* info = (multiboot_info_t*) mb_addr; 13 | uintptr_t end = (uintptr_t) &_end; 14 | 15 | if (info->flags & MULTIBOOT_INFO_CMDLINE) { 16 | const char* cmdline = (char*) (intptr_t) info->cmdline; 17 | const uintptr_t pot_end = info->cmdline + strlen(cmdline); 18 | if (pot_end > end) end = pot_end; 19 | } 20 | 21 | const multiboot_module_t* mod = (multiboot_module_t*) (intptr_t) info->mods_addr; 22 | const multiboot_module_t* mods_end = mod + info->mods_count; 23 | 24 | for (; mod < mods_end; mod++) 25 | { 26 | if (mod->mod_end > end) end = mod->mod_end; 27 | } 28 | return (void*) end; 29 | } 30 | 31 | void __init_stdlib(uint32_t mb_magic, uint32_t mb_addr) 32 | { 33 | assert(mb_magic == 0x2badb002); 34 | 35 | // 1. enable printf facilities 36 | init_printf(NULL, __serial_putchr); 37 | 38 | // 2. find end of multiboot areas 39 | void* free_begin = multiboot_free_begin(mb_addr); 40 | assert(free_begin >= (void*) &_end); 41 | 42 | // 3. initialize heap (malloc, etc.) 43 | extern void __init_heap(void*); 44 | __init_heap(free_begin); 45 | 46 | #ifdef EH_ENABLED 47 | /// 4. initialize exceptions before we run constructors 48 | extern char __eh_frame_start[]; 49 | extern void __register_frame(void*); 50 | __register_frame(&__eh_frame_start); 51 | #endif 52 | 53 | // 5. call global C/C++ constructors 54 | extern void(*__init_array_start [])(); 55 | extern void(*__init_array_end [])(); 56 | int count = __init_array_end - __init_array_start; 57 | for (int i = 0; i < count; i++) { 58 | __init_array_start[i](); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/crt/cxxabi.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | extern "C" void* malloc(size_t); 6 | extern "C" void free(void*); 7 | 8 | //#define DEBUG_HEAP 9 | #ifdef DEBUG_HEAP 10 | #define HPRINT(fmt, ...) kprintf(fmt, __VA_ARGS__) 11 | #else 12 | #define HPRINT(fmt, ...) /* fmt */ 13 | #endif 14 | 15 | void* operator new(size_t size) 16 | { 17 | void* res = malloc(size); 18 | HPRINT("operator new: size %u => %p\n", size, res); 19 | return res; 20 | } 21 | void* operator new[](size_t size) 22 | { 23 | void* res = malloc(size); 24 | HPRINT("operator new[]: size %u => %p\n", size, res); 25 | return res; 26 | } 27 | 28 | void operator delete(void* ptr) 29 | { 30 | HPRINT("operator delete: %p\n", ptr); 31 | free(ptr); 32 | } 33 | void operator delete[](void* ptr) 34 | { 35 | HPRINT("operator delete[]: %p\n", ptr); 36 | free(ptr); 37 | } 38 | // C++14 sized deallocation 39 | void operator delete(void* ptr, std::size_t) 40 | { 41 | free(ptr); 42 | } 43 | void operator delete [](void* ptr, std::size_t) 44 | { 45 | free(ptr); 46 | } 47 | 48 | extern "C" void __cxa_pure_virtual() 49 | { 50 | kprintf("Unimplemented pure virtual function called\n"); 51 | } 52 | 53 | /// EASTL glue /// 54 | 55 | void* operator new[] ( 56 | size_t size, /* size */ 57 | const char*, /* pName */ 58 | int, /* flags */ 59 | unsigned, /* debugFlags */ 60 | const char*, /* file */ 61 | int) /* line */ 62 | { 63 | void* res = malloc(size); 64 | HPRINT("eastl new: size: %u = %p\n", size, res); 65 | return res; 66 | } 67 | void* operator new[] ( 68 | size_t size, /* size */ 69 | size_t align, /* alignment */ 70 | size_t, /* alignmentOffset */ 71 | const char*, /* pName */ 72 | int, /* flags */ 73 | unsigned, /* debugFlags */ 74 | const char*, /* file */ 75 | int) /* line */ 76 | { 77 | void* res = malloc(size); 78 | HPRINT("eastl aligned new: size %u align %u => %p\n", 79 | size, align, res); 80 | (void) align; 81 | return res; 82 | } 83 | -------------------------------------------------------------------------------- /src/linker.ld: -------------------------------------------------------------------------------- 1 | /** 2 | * Inspired from the IncludeOS unikernel (https://github.com/hioa-cs/IncludeOS) 3 | * 4 | * This is the smallest possible linker script that supports 5 | * normal C and C++ operation, including global constructors, 6 | * exceptions and linker GC sections option. 7 | **/ 8 | ENTRY(_start) 9 | 10 | SECTIONS 11 | { 12 | PROVIDE(_ELF_START_ = . + 0x100000); 13 | PROVIDE(_LOAD_START_ = _ELF_START_); 14 | . = _ELF_START_; 15 | 16 | .multiboot (_ELF_START_ ): { 17 | PROVIDE(_MULTIBOOT_START_ = .); 18 | KEEP(*(.multiboot)) 19 | } 20 | 21 | .text ALIGN(0x10) : 22 | { 23 | _TEXT_START_ = .; 24 | *(.text*) 25 | *(.gnu.linkonce.t*) 26 | _TEXT_END_ = .; 27 | } 28 | 29 | .rodata : 30 | { 31 | _RODATA_START_ = .; 32 | *(.rodata*) 33 | *(.gnu.linkonce.r*) 34 | _RODATA_END_ = .; 35 | } 36 | 37 | .init_array : 38 | { 39 | PROVIDE_HIDDEN (__init_array_start = .); 40 | KEEP (*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*))) 41 | KEEP (*(.init_array .ctors)) 42 | PROVIDE_HIDDEN (__init_array_end = .); 43 | } 44 | /*.fini_array : 45 | { 46 | PROVIDE_HIDDEN (__fini_array_start = .); 47 | KEEP (*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*))) 48 | KEEP (*(.fini_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .dtors)) 49 | PROVIDE_HIDDEN (__fini_array_end = .); 50 | }*/ 51 | 52 | /* For stack unwinding (exception handling) */ 53 | .eh_frame_hdr ALIGN(0x8): 54 | { 55 | KEEP(*(.eh_frame_hdr*)) 56 | } 57 | .eh_frame ALIGN(0x8): 58 | { 59 | PROVIDE (__eh_frame_start = .); 60 | KEEP(*(.eh_frame)) 61 | LONG (0); 62 | } 63 | .gcc_except_table : 64 | { 65 | *(.gcc_except_table) 66 | } 67 | 68 | .data : 69 | { 70 | _DATA_START_ = .; 71 | *(.data*) 72 | *(.gnu.linkonce.d*) 73 | _DATA_END_ = .; 74 | } 75 | 76 | PROVIDE(_LOAD_END_ = .); 77 | 78 | .bss : 79 | { 80 | _BSS_START_ = .; 81 | *(.bss .bss.* .gnu.linkonce.b.*) 82 | *(COMMON) 83 | _BSS_END_ = .; 84 | } 85 | 86 | . = ALIGN(0x10); 87 | _end = .; 88 | } 89 | -------------------------------------------------------------------------------- /tools/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # This is the old Makefile for the project, before moving to CMake 3 | # if CMake is not for you, then this may still be useful for you to build 4 | # a single kernel from the source tree. Just keep in mind you need to add 5 | # your own main file, and that the source tree is outdated. Good luck. 6 | # 7 | 8 | # kernel binary 9 | OUT = mykernel 10 | # .c files (add your own!) 11 | C_FILES = src/kernel/kernel_start.c \ 12 | src/hw/serial1.c \ 13 | src/crt/c_abi.c src/crt/heap.c src/crt/malloc.c \ 14 | src/crt/ubsan.c \ 15 | src/prnt/print.c src/prnt/mini-printf.c 16 | # .cpp files 17 | CPP_FILES=src/main.cpp src/crt/cxxabi.cpp \ 18 | src/kernel/tls.cpp src/kernel/panic.cpp 19 | # .asm files for NASM 20 | ASM_FILES=src/kernel/start.asm src/kernel/start64.asm 21 | # includes 22 | INCLUDE=-Isrc 23 | # EASTL C++ library 24 | INCLUDE +=-Iext/EASTL/include -Iext/EASTL/test/packages/EABase/include/Common 25 | CPP_FILES += ext/EASTL/source/allocator_eastl.cpp ext/EASTL/source/assert.cpp \ 26 | ext/EASTL/source/fixed_pool.cpp ext/EASTL/source/hashtable.cpp \ 27 | ext/EASTL/source/intrusive_list.cpp ext/EASTL/source/numeric_limits.cpp \ 28 | ext/EASTL/source/red_black_tree.cpp ext/EASTL/source/string.cpp 29 | 30 | GDEFS = 31 | LIBS = 32 | OPTIMIZE = -Ofast -mfpmath=sse -msse3 #-march=native 33 | 34 | ## to enable LLVM / ThinLTO use these ## 35 | #LD=ld.lld # path to your LLD binary 36 | #LTO_DEFS=-flto=full # full or thin 37 | 38 | OPTIONS=-m64 $(INCLUDE) $(GDEFS) $(OPTIMIZE) $(LTO_DEFS) 39 | WARNS=-Wall -Wextra -pedantic 40 | COMMON=-ffreestanding -nostdlib -MMD -fstack-protector-strong -fno-omit-frame-pointer $(OPTIONS) $(WARNS) 41 | # inject some random numbers into a symbol so we can get some free random bits 42 | SSP=$(shell hexdump -n 8 -e '4/4 "%08X" 1 "\n"' /dev/random) 43 | LDFLAGS=-static -nostdlib -N -melf_x86_64 --strip-all --script=src/linker.ld --defsym __SSP__=0x$(SSP) 44 | CFLAGS=-std=gnu11 $(COMMON) 45 | CXXFLAGS=-std=c++14 -fno-exceptions -fno-rtti $(COMMON) 46 | CFLAGS += $(SANITIZE) 47 | CXXFLAGS += $(SANITIZE) #-fno-sanitize=function 48 | 49 | COBJ=$(C_FILES:.c=.o) 50 | CXXOBJ=$(CPP_FILES:.cpp=.o) 51 | ASMOBJ=$(ASM_FILES:.asm=.o) 52 | DEPS=$(CXXOBJ:.o=.d) $(COBJ:.o=.d) 53 | 54 | .PHONY: clean all executable 55 | 56 | %.o: %.asm 57 | nasm -f elf64 -o $@ $< 58 | 59 | %.o: %.c 60 | $(CC) $(CFLAGS) -c $< -o $@ 61 | 62 | %.o: %.cpp 63 | $(CXX) $(CXXFLAGS) -c $< -o $@ 64 | 65 | all: $(COBJ) $(CXXOBJ) $(ASMOBJ) 66 | $(LD) $(LDFLAGS) $(COBJ) $(CXXOBJ) $(ASMOBJ) $(LIBS) -o $(OUT) 67 | 68 | sanitize: 69 | SANITIZE="-fsanitize=undefined -fno-sanitize=vptr" $(MAKE) all 70 | 71 | chainloader: $(COBJ) $(CXXOBJ) $(ASMOBJ) 72 | $(LD) $(LDFLAGS) $(COBJ) $(CXXOBJ) $(ASMOBJ) $(LIBS) -o chainloader 73 | 74 | executable: 75 | $(info $(OUT)) 76 | @true 77 | 78 | clean: 79 | $(RM) $(OUT) $(COBJ) $(CXXOBJ) $(ASMOBJ) $(DEPS) 80 | 81 | -include $(DEPS) 82 | -------------------------------------------------------------------------------- /src/kernel/elf.cpp: -------------------------------------------------------------------------------- 1 | #include "elf.hpp" 2 | #include 3 | #include 4 | #include 5 | #include 6 | static constexpr bool DEBUG_ELF = false; 7 | 8 | bool Elf::validate(Elf64_Ehdr* hdr) 9 | { 10 | return (hdr->e_ident[EI_MAG0] == ELFMAG0) 11 | && (hdr->e_ident[EI_MAG1] == ELFMAG1) 12 | && (hdr->e_ident[EI_MAG2] == ELFMAG2) 13 | && (hdr->e_ident[EI_MAG3] == ELFMAG3) 14 | // we will need 64-bit addresses and such 15 | && (hdr->e_ident[EI_CLASS] == ELFCLASS64); 16 | } 17 | 18 | template 19 | inline T* elf_offset(const Elf64_Ehdr* hdr, intptr_t ofs) { 20 | return (T*) &((char*) hdr)[ofs]; 21 | } 22 | 23 | static const Elf64_Sym* 24 | elf_sym_index(const Elf64_Ehdr* hdr, const Elf64_Shdr* shdr, uint32_t symidx) 25 | { 26 | assert(symidx < shdr->sh_size / sizeof(Elf64_Sym)); 27 | auto* symtab = elf_offset(hdr, shdr->sh_offset); 28 | return &symtab[symidx]; 29 | } 30 | 31 | const Elf64_Shdr* Elf::section_by_name(const Elf64_Ehdr* hdr, const char* name) 32 | { 33 | const auto* shdr = elf_offset (hdr, hdr->e_shoff); 34 | const auto& shstrtab = shdr[hdr->e_shnum-1]; 35 | const char* strings = elf_offset(hdr, shstrtab.sh_offset); 36 | 37 | for (auto i = 0; i < hdr->e_shnum; i++) 38 | { 39 | const char* shname = &strings[shdr[i].sh_name]; 40 | if (strcmp(shname, name) == 0) { 41 | return &shdr[i]; 42 | } 43 | } 44 | return nullptr; 45 | } 46 | 47 | const Elf64_Sym* Elf::resolve_name(const Elf64_Ehdr* hdr, const char* name) 48 | { 49 | const auto* sym_hdr = Elf::section_by_name(hdr, ".symtab"); 50 | assert(sym_hdr != nullptr); 51 | const auto* str_hdr = Elf::section_by_name(hdr, ".strtab"); 52 | assert(str_hdr != nullptr); 53 | 54 | const Elf64_Sym* symtab = elf_sym_index(hdr, sym_hdr, 0); 55 | const size_t symtab_ents = sym_hdr->sh_size / sizeof(Elf64_Sym); 56 | const char* strtab = elf_offset(hdr, str_hdr->sh_offset); 57 | 58 | for (size_t i = 0; i < symtab_ents; i++) 59 | { 60 | const char* symname = &strtab[symtab[i].st_name]; 61 | //kprintf("Testing %s vs %s\n", symname, name); 62 | if (strcmp(symname, name) == 0) { 63 | return &symtab[i]; 64 | } 65 | } 66 | return nullptr; 67 | } 68 | 69 | static void elf_print_sym(const Elf64_Sym* sym) 70 | { 71 | kprintf("-> Sym is at %p with size %llu, type %u name %u\n", 72 | (void*) sym->st_value, sym->st_size, 73 | ELF64_ST_TYPE(sym->st_info), sym->st_name); 74 | } 75 | 76 | static void elf_relocate_section(Elf64_Ehdr* hdr, const char* section_name) 77 | { 78 | const auto* rela = Elf::section_by_name(hdr, section_name); 79 | const auto* dyn_hdr = Elf::section_by_name(hdr, ".dynsym"); 80 | const size_t rela_ents = rela->sh_size / sizeof(Elf64_Rela); 81 | auto* rela_addr = elf_offset(hdr, rela->sh_offset); 82 | for (size_t i = 0; i < rela_ents; i++) { 83 | const uint32_t symidx = ELF64_R_SYM(rela_addr[i].r_info); 84 | auto* sym = elf_sym_index(hdr, dyn_hdr, symidx); 85 | 86 | const uint8_t type = ELF64_ST_TYPE(sym->st_info); 87 | if (type == STT_FUNC || type == STT_OBJECT) 88 | { 89 | uintptr_t entry = (uintptr_t) hdr + rela_addr[i].r_offset; 90 | uintptr_t final = (uintptr_t) hdr + sym->st_value; 91 | if constexpr (DEBUG_ELF) 92 | { 93 | kprintf("Relocating rela %zu with sym idx %u where %p -> %p\n", 94 | i, symidx, (void*) entry, (void*) final); 95 | elf_print_sym(sym); 96 | } 97 | *(char**) entry = (char*) final; 98 | } 99 | } 100 | } 101 | 102 | void Elf::perform_relocations(Elf64_Ehdr* hdr) 103 | { 104 | elf_relocate_section(hdr, ".rela.plt"); 105 | elf_relocate_section(hdr, ".rela.dyn"); 106 | } 107 | -------------------------------------------------------------------------------- /src/crt/malloc.c: -------------------------------------------------------------------------------- 1 | // 2 | // As written by Snaipe @ https://github.com/Snaipe/malloc 3 | // 4 | #include 5 | #include 6 | 7 | static inline size_t word_align(size_t size) { 8 | return size + ((sizeof(size_t) - 1) & ~(sizeof(size_t) - 1)); 9 | } 10 | 11 | struct chunk { 12 | struct chunk *next, *prev; 13 | size_t size; 14 | int free; 15 | void *data; 16 | }; 17 | 18 | typedef struct chunk *Chunk; 19 | 20 | static void *malloc_base() { 21 | static Chunk b = NULL; 22 | if (!b) { 23 | b = sbrk(word_align(sizeof(struct chunk))); 24 | if (b == (void*) -1) { 25 | _exit(127); 26 | } 27 | b->next = NULL; 28 | b->prev = NULL; 29 | b->size = 0; 30 | b->free = 0; 31 | b->data = NULL; 32 | } 33 | return b; 34 | } 35 | 36 | Chunk malloc_chunk_find(size_t s, Chunk *heap) { 37 | Chunk c = malloc_base(); 38 | for (; c && (!c->free || c->size < s); *heap = c, c = c->next); 39 | return c; 40 | } 41 | 42 | void malloc_merge_next(Chunk c) { 43 | c->size = c->size + c->next->size + sizeof(struct chunk); 44 | c->next = c->next->next; 45 | if (c->next) { 46 | c->next->prev = c; 47 | } 48 | } 49 | 50 | void malloc_split_next(Chunk c, size_t size) { 51 | Chunk newc = (Chunk)((char*) c + size); 52 | newc->prev = c; 53 | newc->next = c->next; 54 | newc->size = c->size - size; 55 | newc->free = 1; 56 | newc->data = newc + 1; 57 | if (c->next) { 58 | c->next->prev = newc; 59 | } 60 | c->next = newc; 61 | c->size = size - sizeof(struct chunk); 62 | } 63 | 64 | void *malloc(size_t size) { 65 | if (!size) return NULL; 66 | size_t length = word_align(size + sizeof(struct chunk)); 67 | Chunk prev = NULL; 68 | Chunk c = malloc_chunk_find(size, &prev); 69 | if (!c) { 70 | Chunk newc = sbrk(length); 71 | if (newc == (void*) -1) { 72 | return NULL; 73 | } 74 | newc->next = NULL; 75 | newc->prev = prev; 76 | newc->size = length - sizeof(struct chunk); 77 | newc->data = newc + 1; 78 | prev->next = newc; 79 | c = newc; 80 | } else if (length + sizeof(size_t) < c->size) { 81 | malloc_split_next(c, length); 82 | } 83 | c->free = 0; 84 | return c->data; 85 | } 86 | 87 | void free(void *ptr) { 88 | if (!ptr || ptr < malloc_base() || ptr > sbrk(0)) return; 89 | Chunk c = (Chunk) ptr - 1; 90 | if (c->data != ptr) return; 91 | c->free = 1; 92 | 93 | if (c->next && c->next->free) { 94 | malloc_merge_next(c); 95 | } 96 | if (c->prev->free) { 97 | malloc_merge_next(c = c->prev); 98 | } 99 | if (!c->next) { 100 | c->prev->next = NULL; 101 | sbrk(- c->size - sizeof(struct chunk)); 102 | } 103 | } 104 | 105 | void *calloc(size_t nmemb, size_t size) { 106 | size_t length = nmemb * size; 107 | void *ptr = malloc(length); 108 | if (ptr) { 109 | char *dst = ptr; 110 | for (size_t i = 0; i < length; *dst = 0, ++dst, ++i); 111 | } 112 | return ptr; 113 | } 114 | 115 | void *realloc(void *ptr, size_t size) { 116 | void *newptr = malloc(size); 117 | if (newptr && ptr && ptr >= malloc_base() && ptr <= sbrk(0)) { 118 | Chunk c = (Chunk) ptr - 1; 119 | if (c->data == ptr) { 120 | size_t length = c->size > size ? size : c->size; 121 | char *dst = newptr, *src = ptr; 122 | for (size_t i = 0; i < length; *dst = *src, ++src, ++dst, ++i); 123 | free(ptr); 124 | } 125 | } 126 | return newptr; 127 | } 128 | -------------------------------------------------------------------------------- /src/crt/print.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | FILE* stderr = NULL; 6 | FILE* stdout = NULL; 7 | 8 | char* hex32_to_str(char buffer[], unsigned int val) 9 | { 10 | char const lut[] = "0123456789ABCDEF"; 11 | for (int i = 0; i < 4; i++) 12 | { 13 | buffer[i*2+0] = lut[(val >> (28-i*8)) & 0xF]; 14 | buffer[i*2+1] = lut[(val >> (24-i*8)) & 0xF]; 15 | } 16 | buffer[8] = 0; 17 | return buffer; 18 | } 19 | 20 | char* int32_to_str(char buffer[], int val) 21 | { 22 | char* b = buffer; 23 | // negation 24 | if (val < 0) { *b++ = '-'; val *= -1; } 25 | // move to end of repr. 26 | int tmp = val; 27 | do { ++b; tmp /= 10; } while (tmp); 28 | *b = 0; 29 | // move back and insert as we go 30 | do { 31 | *--b = '0' + (val % 10); 32 | val /= 10; 33 | } while (val); 34 | return buffer; 35 | } 36 | 37 | int kprintf(const char* fmt, ...) 38 | { 39 | char buffer[4096]; 40 | va_list va; 41 | va_start(va, fmt); 42 | int ret = tfp_vsnprintf(buffer, sizeof(buffer), fmt, va); 43 | va_end(va); 44 | 45 | kprint(buffer); 46 | return ret; 47 | } 48 | 49 | #undef snprintf 50 | __attribute__((format (printf, 3, 4))) 51 | int snprintf(char *s, size_t maxlen, const char *format, ...) 52 | { 53 | va_list arg; 54 | va_start (arg, format); 55 | int bytes = tfp_vsnprintf(s, maxlen, format, arg); 56 | va_end (arg); 57 | return bytes; 58 | } 59 | 60 | #undef printf 61 | #undef fprintf 62 | #undef vfprintf 63 | int vfprintf(FILE* fp, const char *format, va_list ap) 64 | { 65 | (void) fp; 66 | char buffer[4096]; 67 | int len = tfp_vsnprintf(buffer, sizeof(buffer), format, ap); 68 | __serial_print(buffer, len); 69 | return len; 70 | } 71 | 72 | __attribute__((format (printf, 2, 3))) 73 | int fprintf(FILE* stream, const char* fmt, ...) 74 | { 75 | va_list arg; 76 | va_start (arg, fmt); 77 | int bytes = vfprintf(stream, fmt, arg); 78 | va_end (arg); 79 | return bytes; 80 | } 81 | 82 | __attribute__((format(printf, 2, 3))) 83 | int __printf_chk (int flag, const char *format, ...) 84 | { 85 | (void) flag; 86 | va_list ap; 87 | va_start (ap, format); 88 | int bytes = vfprintf (stdout, format, ap); 89 | va_end (ap); 90 | return bytes; 91 | } 92 | int __fprintf_chk(FILE* fp, int flag, const char* format, ...) 93 | { 94 | (void) flag; 95 | va_list arg; 96 | va_start (arg, format); 97 | int bytes = vfprintf(fp, format, arg); 98 | va_end (arg); 99 | return bytes; 100 | } 101 | int __vfprintf_chk(FILE* fp, int flag, const char *format, va_list ap) 102 | { 103 | (void) flag; 104 | int bytes = vfprintf (fp, format, ap); 105 | return bytes; 106 | } 107 | int __vsprintf_chk(char* s, int flag, size_t slen, const char* format, va_list args) 108 | { 109 | (void) flag; 110 | int res = tfp_vsnprintf(s, slen, format, args); 111 | assert ((size_t) res < slen); 112 | return res; 113 | } 114 | int __vsnprintf_chk (char *s, size_t maxlen, int flags, size_t slen, 115 | const char *format, va_list args) 116 | { 117 | assert (slen < maxlen); 118 | (void) flags; 119 | return tfp_vsnprintf(s, slen, format, args); 120 | } 121 | __attribute__((format(printf, 4, 5))) 122 | int __sprintf_chk(char* s, int flags, size_t slen, const char *format, ...) 123 | { 124 | va_list arg; 125 | va_start (arg, format); 126 | int bytes = __vsprintf_chk(s, flags, slen, format, arg); 127 | va_end (arg); 128 | return bytes; 129 | } 130 | int __snprintf_chk (char *s, size_t maxlen, int flags, size_t slen, 131 | const char *format, ...) 132 | { 133 | va_list arg; 134 | int done; 135 | 136 | va_start (arg, format); 137 | done = __vsnprintf_chk (s, maxlen, flags, slen, format, arg); 138 | va_end (arg); 139 | 140 | return done; 141 | } 142 | -------------------------------------------------------------------------------- /machines/chainloader/chainloader.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | //#define DEBUG 9 | #ifdef DEBUG 10 | #define PRINT(X,...) kprintf(X,##__VA_ARGS__); 11 | #else 12 | #define PRINT(X,...) 13 | #endif 14 | 15 | extern "C" void hotswap(const char* base, int len, char* dest, void* start, 16 | uintptr_t magic, uintptr_t bootinfo); 17 | 18 | struct mods_t { 19 | multiboot_module_t* mod; 20 | uint32_t count; 21 | }; 22 | 23 | static multiboot_info_t* bootinfo(uint32_t addr) 24 | { 25 | return (multiboot_info_t*) addr; 26 | } 27 | static mods_t modules(const multiboot_info_t* info) 28 | { 29 | if (info and info->flags & MULTIBOOT_INFO_MODS && info->mods_count > 0) 30 | { 31 | return mods_t {(multiboot_module_t*) info->mods_addr, info->mods_count}; 32 | } 33 | return mods_t{nullptr, 0}; 34 | } 35 | 36 | void promote_mod_to_kernel(multiboot_info_t* info) 37 | { 38 | assert(info->mods_count > 0); 39 | auto* mod = (multiboot_module_t*) info->mods_addr; 40 | 41 | // move commandline to a relatively safe area 42 | const uint32_t RELATIVELY_SAFE_AREA = 0x8000; 43 | strcpy((char*) RELATIVELY_SAFE_AREA, (const char*) mod->cmdline); 44 | info->cmdline = RELATIVELY_SAFE_AREA; 45 | 46 | // Subtract one module 47 | info->mods_count--; 48 | 49 | if (info->mods_count) 50 | info->mods_addr = (uint32_t) ((multiboot_module_t*)info->mods_addr + 1); 51 | } 52 | 53 | extern "C" 54 | void main(uint32_t mb_magic, uint32_t mb_addr) 55 | { 56 | mods_t mods = modules(bootinfo(mb_addr)); 57 | PRINT("\n%u-bit chainloader found %u modules\n", 58 | sizeof(void*) * 8, mods.count); 59 | 60 | if (mods.count < 1) { 61 | kprintf("\nchainloader: No modules passed to multiboot!\n"); 62 | return; 63 | } 64 | 65 | multiboot_module_t binary = mods.mod[0]; 66 | 67 | // treat module as 64-bit ELF 68 | auto* elf = (Elf64_Ehdr*) binary.mod_start; 69 | auto* phdr = (Elf64_Phdr*) (binary.mod_start + elf->e_phoff); 70 | // program header should be valid & loadable 71 | assert(phdr->p_paddr > 0xFFF && phdr->p_type == PT_LOAD); 72 | // program headers 73 | const uint32_t program_headers = elf->e_phnum; 74 | 75 | // update multiboot info for new kernel 76 | promote_mod_to_kernel(bootinfo(mb_addr)); 77 | 78 | PRINT("Preparing for jump to %s. Multiboot magic: 0x%x, addr 0x%x\n", 79 | (char*) binary.cmdline, mb_magic, mb_addr); 80 | 81 | // measure & allocate kernel buffer 82 | int32_t len = 0; 83 | for (const auto* hdr = phdr; hdr < phdr + program_headers; hdr++) 84 | { 85 | uint64_t end = hdr->p_paddr + hdr->p_memsz; 86 | if (len < end) len = end; 87 | } 88 | len -= phdr->p_offset; 89 | PRINT("Length in memory: %d (%d kB)\n", len, len / 1024); 90 | 91 | char* kernel = new char[len]; 92 | 93 | // build kernel 94 | for (const auto* hdr = phdr; hdr < phdr + program_headers; hdr++) 95 | { 96 | const char* src = (char*) elf + hdr->p_offset; 97 | int offset = hdr->p_paddr - phdr->p_paddr; 98 | memcpy(&kernel[offset], src, hdr->p_filesz); 99 | PRINT("Segment src: %p offset: %d -> dst: %p\n", src, offset, &kernel[offset]); 100 | } 101 | 102 | // Move hotswap function away from binary 103 | void* hotswap_addr = (void*)0x2000; 104 | extern char __hotswap_end; 105 | memcpy(hotswap_addr,(void*)&hotswap, &__hotswap_end - (char*)&hotswap ); 106 | 107 | // Prepare to load ELF segment 108 | const char* base = kernel; 109 | char* dest = (char*) phdr->p_paddr; 110 | void* start = (void*) elf->e_entry; 111 | 112 | asm("cli"); 113 | PRINT("Jumping to %p located in %p:%d\n", start, dest, len); 114 | 115 | ((decltype(&hotswap))hotswap_addr)(base, len, dest, start, mb_magic, mb_addr); 116 | __builtin_unreachable(); 117 | } 118 | -------------------------------------------------------------------------------- /src/kernel/start.asm: -------------------------------------------------------------------------------- 1 | ;; stack base address at EBDA border 2 | ;; NOTE: Multiboot can use 9d400 to 9ffff 3 | %define STACK_LOCATION 0x9D400 4 | 5 | ;; multiboot magic 6 | %define MB_MAGIC 0x1BADB002 7 | %define MB_FLAGS 0x3 ;; ALIGN + MEMINFO 8 | 9 | extern _MULTIBOOT_START_ 10 | extern _LOAD_START_ 11 | extern _LOAD_END_ 12 | extern _end 13 | extern begin_enter_longmode 14 | extern kernel_start 15 | 16 | ALIGN 4 17 | section .multiboot 18 | dd MB_MAGIC 19 | dd MB_FLAGS 20 | dd -(MB_MAGIC + MB_FLAGS) 21 | dd _MULTIBOOT_START_ 22 | dd _LOAD_START_ 23 | dd _LOAD_END_ 24 | dd _end 25 | dd _start 26 | 27 | section .data 28 | multiboot_data_magic: dq 0 29 | multiboot_data_address: dq 0 30 | global multiboot_data_magic 31 | global multiboot_data_address 32 | 33 | [BITS 32] 34 | section .text 35 | global _start ;; make _start a global symbol 36 | _start: 37 | cli 38 | 39 | ;; load simple GDT 40 | lgdt [gdtr] 41 | 42 | ;; activate new GDT 43 | jmp 0x8:rock_bottom ;; code seg 44 | rock_bottom: 45 | mov cx, 0x10 ;; data seg 46 | mov ss, cx 47 | mov ds, cx 48 | mov es, cx 49 | mov fs, cx 50 | mov cx, 0x18 ;; GS seg 51 | mov gs, cx 52 | 53 | ;; 32-bit stack ptr 54 | mov esp, STACK_LOCATION 55 | mov ebp, esp 56 | 57 | ;; store multiboot params 58 | mov DWORD [multiboot_data_magic], eax 59 | mov DWORD [multiboot_data_address], ebx 60 | 61 | ;;ASM_PRINT(strings.phase1) 62 | call enable_cpu_feat 63 | 64 | ;; zero .bss (used to be in the C portion, but easier to do here) 65 | extern _BSS_START_ 66 | extern _BSS_END_ 67 | mov edi, _BSS_START_ 68 | mov ecx, _BSS_END_ 69 | sub ecx, _BSS_START_ 70 | mov eax, 0 71 | rep stosb 72 | 73 | ;; Enable stack protector: 74 | ;; GS is located at 0x1000 75 | ;; Linux uses GS:0x14 to access stack protector value 76 | ;; Copy RDTSC.EAX to this location as preliminary value 77 | rdtsc 78 | mov DWORD [0x1014], eax 79 | 80 | ;; for 32-bit kernels just call kernel_start here 81 | call begin_enter_longmode 82 | ;; stop 83 | cli 84 | hlt 85 | 86 | enable_cpu_feat: 87 | ;; enable SSE (pretty much always exists) 88 | mov eax, cr0 89 | and ax, 0xFFFB ;clear coprocessor emulation CR0.EM 90 | or ax, 0x2 ;set coprocessor monitoring CR0.MP 91 | mov cr0, eax 92 | mov eax, cr4 93 | or ax, 3 << 9 ;set CR4.OSFXSR and CR4.OSXMMEXCPT at the same time 94 | or ax, 0x20 ;enable native FPU exception handling 95 | mov cr4, eax 96 | ;; read out CPU features 97 | mov eax, 1 98 | xor ecx, ecx 99 | cpuid 100 | mov edx, ecx 101 | ;; check for XSAVE support (bit 26) 102 | and ecx, 0x04000000 103 | jz xsave_not_supported 104 | ;; enable XSAVE 105 | mov eax, cr4 106 | or eax, 0x40000 107 | mov cr4, eax 108 | ;; check for AVX support (bit 28) 109 | and edx, 0x10000000 110 | jz xsave_not_supported 111 | ;; enable AVX 112 | xor ecx, ecx 113 | xgetbv 114 | or eax, 0x7 115 | xsetbv 116 | xsave_not_supported: 117 | ret 118 | 119 | 120 | ALIGN 32 121 | gdtr: 122 | dw gdt32_end - gdt32 - 1 123 | dd gdt32 124 | ALIGN 32 125 | gdt32: 126 | ;; Entry 0x0: Null descriptor 127 | dq 0x0 128 | ;; Entry 0x8: Code segment 129 | dw 0xffff ;Limit 130 | dw 0x0000 ;Base 15:00 131 | db 0x00 ;Base 23:16 132 | dw 0xcf9a ;Flags / Limit / Type [F,L,F,Type] 133 | db 0x00 ;Base 32:24 134 | ;; Entry 0x10: Data segment 135 | dw 0xffff ;Limit 136 | dw 0x0000 ;Base 15:00 137 | db 0x00 ;Base 23:16 138 | dw 0xcf92 ;Flags / Limit / Type [F,L,F,Type] 139 | db 0x00 ;Base 32:24 140 | ;; Entry 0x18: GS Data segment 141 | dw 0x0100 ;Limit 142 | dw 0x1000 ;Base 15:00 143 | db 0x00 ;Base 23:16 144 | dw 0x4092 ;Flags / Limit / Type [F,L,F,Type] 145 | db 0x00 ;Base 32:24 146 | gdt32_end: 147 | -------------------------------------------------------------------------------- /src/crt/c_abi.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | __attribute__((noreturn)) void panic(const char*); 8 | void* _impure_ptr = NULL; 9 | 10 | void __stack_chk_fail_local() 11 | { 12 | panic("Stack protector: Canary modified"); 13 | __builtin_unreachable(); 14 | } 15 | __attribute__((used)) 16 | void __stack_chk_fail() 17 | { 18 | panic("Stack protector: Canary modified"); 19 | __builtin_unreachable(); 20 | } 21 | void __assert_fail(const char *assertion, 22 | const char *file, 23 | unsigned int line, 24 | const char *function) 25 | { 26 | char buffer[4096]; 27 | snprintf(buffer, sizeof(buffer), 28 | "Assertion failed: %s in %s:%u, function %s\n", 29 | assertion, file, line, function); 30 | panic(buffer); 31 | __builtin_unreachable(); 32 | } 33 | void __assert_func( 34 | const char *file, 35 | int line, 36 | const char *func, 37 | const char *failedexpr) 38 | { 39 | kprintf( 40 | "assertion \"%s\" failed: file \"%s\", line %d%s%s\n", 41 | failedexpr, file, line, 42 | func ? ", function: " : "", func ? func : ""); 43 | extern void abort() __attribute__((noreturn)); 44 | abort(); 45 | } 46 | void _exit(int status) 47 | { 48 | char buffer[64]; 49 | snprintf(buffer, sizeof(buffer), 50 | "Exit called with status %d\n", status); 51 | panic(buffer); 52 | __builtin_unreachable(); 53 | } 54 | 55 | __attribute__((used)) 56 | void* memset(char* dest, int ch, size_t size) 57 | { 58 | for (size_t i = 0; i < size; i++) 59 | dest[i] = ch; 60 | return dest; 61 | } 62 | void* memcpy(char* dest, const char* src, size_t size) 63 | { 64 | for (size_t i = 0; i < size; i++) 65 | dest[i] = src[i]; 66 | return dest; 67 | } 68 | void* memmove(char* dest, const char* src, size_t size) 69 | { 70 | if (dest <= src) 71 | { 72 | for (size_t i = 0; i < size; i++) 73 | dest[i] = src[i]; 74 | } 75 | else 76 | { 77 | for (int i = size-1; i >= 0; i--) 78 | dest[i] = src[i]; 79 | } 80 | return dest; 81 | } 82 | int memcmp(const void* ptr1, const void* ptr2, size_t n) 83 | { 84 | const uint8_t* iter1 = (const uint8_t*) ptr1; 85 | const uint8_t* iter2 = (const uint8_t*) ptr2; 86 | while (n > 0 && *iter1 == *iter2) { 87 | iter1++; 88 | iter2++; 89 | n--; 90 | } 91 | return n == 0 ? 0 : (*iter1 - *iter2); 92 | } 93 | 94 | // naive version (needed for EASTL) 95 | float ceilf(float n) 96 | { 97 | long int i = (int) n; 98 | return (n == (float) i) ? n : i + 1; 99 | } 100 | 101 | char* strcpy(char* dst, const char* src) 102 | { 103 | while ((*dst++ = *src++)); 104 | *dst = 0; 105 | return dst; 106 | } 107 | size_t strlen(const char* str) 108 | { 109 | const char* iter; 110 | for (iter = str; *iter; ++iter); 111 | return iter - str; 112 | } 113 | int strcmp(const char* str1, const char* str2) 114 | { 115 | while (*str1 != 0 && *str2 != 0 && *str1 == *str2) { 116 | str1++; 117 | str2++; 118 | } 119 | return *str1 - *str2; 120 | } 121 | char* strcat(char* dest, const char* src) 122 | { 123 | strcpy(dest + strlen(dest), src); 124 | return dest; 125 | } 126 | 127 | void* __memcpy_chk(void* dest, const void* src, size_t len, size_t destlen) 128 | { 129 | assert (len <= destlen); 130 | return memcpy(dest, src, len); 131 | } 132 | void* __memset_chk(void* dest, int c, size_t len, size_t destlen) 133 | { 134 | assert (len <= destlen); 135 | return memset(dest, c, len); 136 | } 137 | char* __strcat_chk(char* dest, const char* src, size_t destlen) 138 | { 139 | size_t len = strlen(dest) + strlen(src) + 1; 140 | assert (len <= destlen); 141 | return strcat(dest, src); 142 | } 143 | 144 | int isdigit(int c) 145 | { 146 | return (('0' <= c) && (c <= '9')) ? 1 : 0; 147 | } 148 | int isalpha(int c) 149 | { 150 | return ((c > 64 && c < 91) || (c > 96 && c <= 122)) ? 1 : 0; 151 | } 152 | int isxdigit(int c) 153 | { 154 | return (isdigit(c) || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f')); 155 | } 156 | int isupper(int c) 157 | { 158 | return (c >= 'A' && c <= 'Z'); 159 | } 160 | -------------------------------------------------------------------------------- /src/crt/ubsan.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | extern void panic(const char*) __attribute__((noreturn)); 5 | extern void print_backtrace(); 6 | 7 | struct source_location { 8 | const char *file_name; 9 | struct { 10 | uint32_t line; 11 | uint32_t column; 12 | }; 13 | }; 14 | struct type_descriptor { 15 | uint16_t type_kind; 16 | uint16_t type_info; 17 | char type_name[1]; 18 | }; 19 | const char* type_kind_string[] = { 20 | "load of", "store to", "reference binding to", "member access within", 21 | "member call on", "constructor call on", "downcast of", "downcast of" 22 | }; 23 | 24 | struct out_of_bounds { 25 | struct source_location src; 26 | struct type_descriptor* array_type; 27 | struct type_descriptor* index_type; 28 | }; 29 | struct overflow { 30 | struct source_location src; 31 | }; 32 | struct mismatch { 33 | struct source_location src; 34 | struct type_descriptor* type; 35 | unsigned char log_align; 36 | unsigned char type_kind; 37 | }; 38 | struct function_type_mismatch { 39 | const struct source_location src; 40 | const struct type_descriptor* type; 41 | }; 42 | struct nonnull_return { 43 | struct source_location src; 44 | struct source_location attr; 45 | }; 46 | struct unreachable { 47 | struct source_location src; 48 | }; 49 | 50 | static void print_src_location(const struct source_location* src) { 51 | kprintf("ubsan: %s at line %u col %u\n", 52 | src->file_name, src->line, src->column); 53 | } 54 | 55 | static void undefined_throw(const char* error) { 56 | kprintf("ubsan: %s", error); 57 | print_backtrace(); 58 | kprintf("\n"); 59 | } 60 | 61 | /// Undefined-behavior sanitizer 62 | void __ubsan_handle_out_of_bounds(struct out_of_bounds* data) 63 | { 64 | print_src_location(&data->src); 65 | undefined_throw("Out-of-bounds access"); 66 | } 67 | void __ubsan_handle_missing_return() 68 | { 69 | undefined_throw("Missing return"); 70 | } 71 | void __ubsan_handle_nonnull_return(struct nonnull_return* data) 72 | { 73 | print_src_location(&data->src); 74 | undefined_throw("Non-null return"); 75 | } 76 | 77 | void __ubsan_handle_add_overflow() 78 | { 79 | undefined_throw("Overflow on addition"); 80 | } 81 | void __ubsan_handle_sub_overflow() 82 | { 83 | undefined_throw("Overflow on subtraction"); 84 | } 85 | void __ubsan_handle_mul_overflow() 86 | { 87 | undefined_throw("Overflow on multiplication"); 88 | } 89 | void __ubsan_handle_negate_overflow() 90 | { 91 | undefined_throw("Overflow on negation"); 92 | } 93 | void __ubsan_handle_pointer_overflow() 94 | { 95 | undefined_throw("Pointer overflow"); 96 | } 97 | void __ubsan_handle_divrem_overflow(struct overflow* data, 98 | unsigned long lhs, 99 | unsigned long rhs) 100 | { 101 | print_src_location(&data->src); 102 | kprintf("ubsan: LHS %lu / RHS %lu\n", lhs, rhs); 103 | undefined_throw("Division remainder overflow"); 104 | } 105 | void __ubsan_handle_float_cast_overflow() 106 | { 107 | undefined_throw("Float-cast overflow"); 108 | } 109 | void __ubsan_handle_shift_out_of_bounds() 110 | { 111 | undefined_throw("Shift out-of-bounds"); 112 | } 113 | 114 | void __ubsan_handle_type_mismatch_v1(struct mismatch* data, uintptr_t ptr) 115 | { 116 | print_src_location(&data->src); 117 | 118 | const char* reason = "Type mismatch"; 119 | const long alignment = 1 << data->log_align; 120 | 121 | if (alignment && (ptr & (alignment-1)) != 0) { 122 | reason = "Misaligned access"; 123 | } 124 | else if (ptr == 0) { 125 | reason = "Null-pointer access"; 126 | } 127 | char buffer[2048]; 128 | // TODO: resolve symbol name 129 | snprintf(buffer, sizeof(buffer), 130 | "%s on ptr %p (aligned %lu)\n" 131 | "ubsan: type name %s\n", 132 | reason, 133 | (void*) ptr, 134 | alignment, 135 | data->type->type_name); 136 | undefined_throw(buffer); 137 | } 138 | void __ubsan_handle_function_type_mismatch( 139 | struct function_type_mismatch* data, 140 | unsigned long ptr) 141 | { 142 | print_src_location(&data->src); 143 | 144 | char buffer[2048]; 145 | snprintf(buffer, sizeof(buffer), 146 | "Function type mismatch on ptr %p\n" 147 | "ubsan: type name %s\n" 148 | "ubsan: function %p", 149 | (void*) ptr, 150 | data->type->type_name, 151 | (void*) ptr); // TODO: resolve symbol name 152 | undefined_throw(buffer); 153 | } 154 | void __ubsan_handle_nonnull_arg() 155 | { 156 | undefined_throw("Non-null argument violated"); 157 | } 158 | 159 | void __ubsan_handle_invalid_builtin() 160 | { 161 | undefined_throw("Invalid built-in function"); 162 | } 163 | void __ubsan_handle_load_invalid_value() 164 | { 165 | undefined_throw("Load of invalid value"); 166 | } 167 | __attribute__((noreturn)) 168 | void __ubsan_handle_builtin_unreachable(struct unreachable* data) 169 | { 170 | print_src_location(&data->src); 171 | panic("Unreachable code reached"); 172 | } 173 | -------------------------------------------------------------------------------- /src/kernel/start64.asm: -------------------------------------------------------------------------------- 1 | [BITS 32] 2 | global begin_enter_longmode:function 3 | extern __serial_print1 4 | extern kernel_start 5 | 6 | %define STACK_LOCATION 0x9D000 7 | %define P4_TAB 0x1000 ;; one page 8 | %define P3_TAB 0x2000 ;; one page 9 | %define P2_TAB 0x3000 ;; many pages 10 | 11 | %define NUM_P3_ENTRIES 4 12 | %define NUM_P2_ENTRIES (NUM_P3_ENTRIES * 512) 13 | 14 | %define IA32_EFER 0xC0000080 15 | %define IA32_STAR 0xC0000081 16 | %define IA32_LSTAR 0xc0000082 17 | %define IA32_FMASK 0xc0000084 18 | %define IA32_FS_BASE 0xc0000100 19 | %define IA32_GS_BASE 0xc0000101 20 | %define IA32_KERNEL_GS_BASE 0xc0000102 21 | 22 | ;; CR0 paging enable bit 23 | %define PAGING_ENABLE 0x80000000 24 | ;; CR0 Supervisor write-protect enable 25 | %define SUPER_WP_ENABLE 0x10000 26 | 27 | ;; EFER Longmode bit 28 | %define LONGMODE_ENABLE 0x100 29 | ;; EFER Execute Disable bit 30 | %define NX_ENABLE 0x800 31 | ;; EFER Syscall enable bit 32 | %define SYSCALL_ENABLE 0x1 33 | 34 | extern multiboot_data_magic 35 | extern multiboot_data_address 36 | 37 | SECTION .text 38 | begin_enter_longmode: 39 | ;; disable old paging 40 | mov eax, cr0 41 | and eax, 0x7fffffff ;; clear PG (bit 31) 42 | mov cr0, eax 43 | 44 | ;; address for Page Map Level 4 45 | mov edi, P4_TAB 46 | mov cr3, edi 47 | ;; clear out P4 and P3 48 | mov ecx, 0x2000 / 0x4 49 | xor eax, eax ; Nullify the A-register. 50 | rep stosd 51 | 52 | ;; create page map entry 53 | mov edi, P4_TAB 54 | mov DWORD [edi], P3_TAB | 0x3 ;; present+write 55 | 56 | ;; create 1GB mappings 57 | mov ecx, NUM_P3_ENTRIES 58 | mov edi, P3_TAB 59 | mov eax, P2_TAB | 0x3 ;; present + write 60 | mov ebx, 0x0 61 | 62 | .p3_loop: 63 | mov DWORD [edi], eax ;; Low word 64 | mov DWORD [edi+4], ebx ;; High word 65 | add eax, 1 << 12 ;; page increments 66 | adc ebx, 0 ;; increment high word when CF set 67 | add edi, 8 68 | loop .p3_loop 69 | 70 | ;; create 2MB mappings 71 | mov ecx, NUM_P2_ENTRIES 72 | mov edi, P2_TAB 73 | mov eax, 0x0 | 0x3 | 1 << 7 ;; present + write + huge 74 | mov ebx, 0x0 75 | 76 | .p2_loop: 77 | mov DWORD [edi], eax ;; Low word 78 | mov DWORD [edi+4], ebx ;; High word 79 | add eax, 1 << 21 ;; 2MB increments 80 | adc ebx, 0 ;; increment high word when CF set 81 | add edi, 8 82 | loop .p2_loop 83 | 84 | ;; enable PAE 85 | mov eax, cr4 86 | or eax, 1 << 5 87 | mov cr4, eax 88 | 89 | ;; enable long mode 90 | mov ecx, IA32_EFER 91 | rdmsr 92 | or eax, (LONGMODE_ENABLE | NX_ENABLE | SYSCALL_ENABLE) 93 | wrmsr 94 | 95 | ;; enable paging 96 | mov eax, cr0 ; Set the A-register to control register 0. 97 | or eax, (PAGING_ENABLE | SUPER_WP_ENABLE) 98 | mov cr0, eax ; Set control register 0 to the A-register. 99 | 100 | ;; load 64-bit GDT 101 | lgdt [__gdt64_base_pointer] 102 | jmp GDT64.Code:long_mode 103 | 104 | 105 | [BITS 64] 106 | long_mode: 107 | cli 108 | 109 | ;; segment regs 110 | mov cx, GDT64.Data 111 | mov ds, cx 112 | mov es, cx 113 | mov fs, cx 114 | mov gs, cx 115 | mov ss, cx 116 | 117 | ;; set up new stack for 64-bit 118 | push rsp 119 | mov rsp, STACK_LOCATION 120 | push 0 121 | push 0 122 | mov rbp, rsp 123 | 124 | mov ecx, IA32_STAR 125 | mov edx, 0x8 126 | mov eax, 0x0 127 | wrmsr 128 | 129 | ;; Enable stack protector: 130 | ;; On amd64 FS should point to tls-table for cpu0 131 | ;; Linux uses FS:0x28 to access stack protector value 132 | extern tls 133 | mov ecx, IA32_FS_BASE 134 | mov edx, 0x0 135 | mov eax, tls 136 | wrmsr 137 | ;; Set "random" stack protector value 138 | extern __SSP__ 139 | rdtsc 140 | mov rcx, __SSP__ 141 | xor rax, rcx 142 | ;; Install in TLS table 143 | mov QWORD [tls+0x28], rax 144 | 145 | ;; geronimo! 146 | mov edi, DWORD[multiboot_data_magic] 147 | mov esi, DWORD[multiboot_data_address] 148 | call kernel_start 149 | ;; warning that we returned from kernel_start 150 | mov rdi, strings.panic 151 | call __serial_print1 152 | cli 153 | hlt 154 | 155 | SECTION .data 156 | strings: 157 | .panic: db `Returned from kernel_start! Halting...\n`,0x0 158 | GDT64: 159 | .Null: equ $ - GDT64 ; The null descriptor. 160 | dq 0 161 | .Code: equ $ - GDT64 ; The code descriptor. 162 | dw 0 ; Limit (low). 163 | dw 0 ; Base (low). 164 | db 0 ; Base (middle) 165 | db 10011010b ; Access (exec/read). 166 | db 00100000b ; Granularity. 167 | db 0 ; Base (high). 168 | .Data: equ $ - GDT64 ; The data descriptor. 169 | dw 0 ; Limit (low). 170 | dw 0 ; Base (low). 171 | db 0 ; Base (middle) 172 | db 10010010b ; Access (read/write). 173 | db 00000000b ; Granularity. 174 | db 0 ; Base (high). 175 | .Task: equ $ - GDT64 ; TSS descriptor. 176 | dq 0 177 | dq 0 178 | 179 | dw 0x0 ;; alignment padding 180 | __gdt64_base_pointer: 181 | dw $ - GDT64 - 1 ; Limit. 182 | dq GDT64 ; Base. 183 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Barebones multiboot kernel 2 | 3 | - Intended for beginner OS development 4 | - Run the dependency installation script (or install the equivalent packages) 5 | - Build and boot a tiny test kernel in Qemu with `./run.sh default`. 6 | - There is a 32-bit test kernel as well: `./run.sh 32bit` 7 | - You can run any of the machines in the `machines` folder this way. 8 | - If building with the NATIVE option (or anything that requires AVX), remember to run with KVM enabled (./run.sh --kvm) so that Qemu will present the modern instruction set features to the guest operating system. 9 | - VGA textmode using ./run.sh --vga (NOTE: you must implement VGA support yourself! You will (if you're lucky) see a black screen.) 10 | - Use ./build_iso.sh to run on real hardware or a hypervisor like VirtualBox, but keep serial port logging in mind! 11 | 12 | ## Features 13 | 14 | - 600kb stack and working GDT (with room for IST task) 15 | - Multiboot parameters passed to kernel start 16 | - assert(), kprintf() and snprintf() 17 | - Very basic heap implementation (malloc/free, new/delete) 18 | - C and C++ global constructors, C++ RTTI and exceptions 19 | - Stack protector support 20 | - EASTL C++ support, which has many useful containers 21 | - Produces tiny machine images 22 | - Machine image is 6944 bytes with minimal build on GCC 9.1 23 | - Machine image is 6376 bytes with minimal LTO build on Clang 8 24 | - Remember to disable stack protector to shave a few extra bytes off! 25 | - LTO and ThinLTO support if you are using clang 26 | - Undefined-sanitizer support to catch some problems during runtime 27 | - Option to unmap zero page (for the very common null-pointer access bugs) 28 | - You have to enable this yourself, after CPU exception handling 29 | 30 | ## Running 31 | 32 | Use `./run.sh ` to build and run your kernel stored in machines folder. 33 | - By default run.sh will build and run the `default` machine. The project is located in `machines/default/` and most of the output comes from `main.cpp`. 34 | - Use argument --kvm if you want to use hardware-accelerated virtualization, or you have built with -march=native in which you must use that. Enable virtualization in your BIOS settings if you haven't. 35 | - Use argument --vga to get a graphical window, which starts out in VGA textmode accessible at 0xb8000. 36 | - Use argument --sanitize to enable the undefined-sanitizer, catching problems like misaligned accesses and overflows. 37 | 38 | ## Changing build options 39 | 40 | Go into the build folder in your machines folder and type `ccmake ..`, which opens a GUI with some adjustable settings. After changing settings press C to configure the changes, and then E to exit the GUI. Changes will be rebuilt automatically using run.sh, or you could simply use `make` like normally in the build folder itself. 41 | 42 | ## Goal 43 | 44 | The goal is to provide a barebones kernel project that implements the most basic C/C++ voodoo to let people start reading/writing to registers and devices immediately. The goal is also to provide people with a choice of using C or C++, or a mix of both. 45 | 46 | ## Future work 47 | 48 | - Add TLS api and support for most thread local variables 49 | - Add support for LTO with GCC (no ideas on how to do this yet) 50 | - Optional 512b bootloader to avoid GRUB (although not recommended) 51 | 52 | ## Validate output 53 | 54 | ./run.sh output should match: 55 | ``` 56 | ------------------ 57 | * Multiboot EAX: 0x2badb002 58 | * Multiboot EBX: 0x9500 59 | * SSE instructions ... work! 60 | * Global constructors ... work! 61 | 62 | Hello OSdev world! 63 | my_kernel (This is a test kernel!) 64 | 65 | Press Ctrl+A -> X to close 66 | Returned from kernel_start! Halting... 67 | ``` 68 | 69 | ## Common issues 70 | 71 | - File format not recognized 72 | - You changed linker or ELF architecture without rebuilding all the files 73 | - Nothing happens when I start the kernel 74 | - If you are building with -march=native, you need to enable KVM and run the kernel natively, because the machine code is full of extended instructions (like AVX) 75 | - I can't use normal C/C++ library functions 76 | - Make sure you are using something that exists in EASTL, or you will have to implement it yourself 77 | - I can write to the zero page 78 | - Yes you can. Welcome to (identity-mapped) kernel space. Set up your own pagetables! 79 | - Do I really need a cross-compiler to work on this project? 80 | - No, that is a misconception that the OSdev community holds dear to its heart, but its not necessary. The Linux-native compilers will use FS/GS for TLS, and will require you to fake the table that (among other things) contain the stack sentinel value. Otherwise, you can reasonably expect normal library calls to be optimized (printf -> write). 81 | - When booting the GRUB image nothing happens, it looks to be crashing etc. 82 | - Make sure you didn't compile with GCC and then link with LLD, and especially don't mix LTO into this. 83 | - Minimal builds can be risky, especially -Oz on clang. 84 | - There could be a genuine issue, so let me know. 85 | - I'm having problems compiling or linking when using exceptions! 86 | - Make sure that you use your own exceptions, and don't rely on anything that belongs to the C++ standard library, which is not present 87 | - I keep getting errors about not finding `bits/c++config.h` when building for 32-bit! 88 | - You will need to install the C++ multilib package for your compiler, such as `g++-8-multilib`. 89 | 90 | ## Undefined sanitizer 91 | 92 | - Works on GCC and Clang 93 | - Easy to support, but has some caveats 94 | - It's miles better if you can resolve symbol addresses to names 95 | - Has not been extensively verified 96 | 97 | ## Link-Time Optimization 98 | 99 | - Works on Clang out of the box, just make sure you use the correct LLD that comes with your compiler 100 | - Both ThinLTO and full LTO works! 101 | - Enable LTO option with ccmake and set the LINKER_EXE option to point to a lld executable that is compatible with your Clang version. For example `ld.lld-8`. 102 | - Does not work on GCC at all due to how complicated it is to call the LTO-helpers manually 103 | 104 | ## Thread-Local Storage 105 | 106 | - The basic foundation has been laid down 107 | - Linker script needs to be amended to store .tbss and .tdata sections 108 | - Functionality for creating new thread storage needs to be created 109 | 110 | ## EASTL, RTTI and exceptions 111 | 112 | - To make use of EASTL you will need to enable the EASTL CMake option 113 | - Tons of useful containers that work right out of the box 114 | - To use RTTI and exceptions in C++ you will need to enable the RTTI_EXCEPTIONS option 115 | - This option bloats the binary a great deal, due to the C++ ABI bundles, as well as the libgcc dependency 116 | 117 | ## Qemu defaults 118 | 119 | Qemu provides you with some default devices to start with 120 | - IDE for disk devices, but you can use PCI to get information 121 | - e1000-compatible network card 122 | - If you add -vga std you will get VGA graphics area at 0xb8000 123 | - In graphics mode: PS/2 keyboard and mouse 124 | 125 | As an example, use this function to clear the VGA textmode screen: 126 | ``` 127 | uint16_t* vga = (uint16_t*) 0xb8000; 128 | for (int i = 0; i < 25*80; i++) 129 | { 130 | vga[i] = ' ' | (7 << 8); 131 | } 132 | ``` 133 | What it's actually doing is filling the text buffer with spaces. Spaces that have a gray color. You just can't see them, because spaces are spaces. 134 | -------------------------------------------------------------------------------- /src/crt/udiv.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | typedef unsigned su_int; 4 | typedef long long di_int; 5 | typedef unsigned long long du_int; 6 | 7 | typedef union 8 | { 9 | du_int all; 10 | struct 11 | { 12 | su_int low; 13 | su_int high; 14 | } s; 15 | } udwords; 16 | 17 | du_int 18 | __udivmoddi4(du_int a, du_int b, du_int* rem) 19 | { 20 | const unsigned n_uword_bits = sizeof(su_int) * CHAR_BIT; 21 | const unsigned n_udword_bits = sizeof(du_int) * CHAR_BIT; 22 | udwords n; 23 | n.all = a; 24 | udwords d; 25 | d.all = b; 26 | udwords q; 27 | udwords r; 28 | unsigned sr; 29 | /* special cases, X is unknown, K != 0 */ 30 | if (n.s.high == 0) 31 | { 32 | if (d.s.high == 0) 33 | { 34 | /* 0 X 35 | * --- 36 | * 0 X 37 | */ 38 | if (rem) 39 | *rem = n.s.low % d.s.low; 40 | return n.s.low / d.s.low; 41 | } 42 | /* 0 X 43 | * --- 44 | * K X 45 | */ 46 | if (rem) 47 | *rem = n.s.low; 48 | return 0; 49 | } 50 | /* n.s.high != 0 */ 51 | if (d.s.low == 0) 52 | { 53 | if (d.s.high == 0) 54 | { 55 | /* K X 56 | * --- 57 | * 0 0 58 | */ 59 | if (rem) 60 | *rem = n.s.high % d.s.low; 61 | return n.s.high / d.s.low; 62 | } 63 | /* d.s.high != 0 */ 64 | if (n.s.low == 0) 65 | { 66 | /* K 0 67 | * --- 68 | * K 0 69 | */ 70 | if (rem) 71 | { 72 | r.s.high = n.s.high % d.s.high; 73 | r.s.low = 0; 74 | *rem = r.all; 75 | } 76 | return n.s.high / d.s.high; 77 | } 78 | /* K K 79 | * --- 80 | * K 0 81 | */ 82 | if ((d.s.high & (d.s.high - 1)) == 0) /* if d is a power of 2 */ 83 | { 84 | if (rem) 85 | { 86 | r.s.low = n.s.low; 87 | r.s.high = n.s.high & (d.s.high - 1); 88 | *rem = r.all; 89 | } 90 | return n.s.high >> __builtin_ctz(d.s.high); 91 | } 92 | /* K K 93 | * --- 94 | * K 0 95 | */ 96 | sr = __builtin_clz(d.s.high) - __builtin_clz(n.s.high); 97 | /* 0 <= sr <= n_uword_bits - 2 or sr large */ 98 | if (sr > n_uword_bits - 2) 99 | { 100 | if (rem) 101 | *rem = n.all; 102 | return 0; 103 | } 104 | ++sr; 105 | /* 1 <= sr <= n_uword_bits - 1 */ 106 | /* q.all = n.all << (n_udword_bits - sr); */ 107 | q.s.low = 0; 108 | q.s.high = n.s.low << (n_uword_bits - sr); 109 | /* r.all = n.all >> sr; */ 110 | r.s.high = n.s.high >> sr; 111 | r.s.low = (n.s.high << (n_uword_bits - sr)) | (n.s.low >> sr); 112 | } 113 | else /* d.s.low != 0 */ 114 | { 115 | if (d.s.high == 0) 116 | { 117 | /* K X 118 | * --- 119 | * 0 K 120 | */ 121 | if ((d.s.low & (d.s.low - 1)) == 0) /* if d is a power of 2 */ 122 | { 123 | if (rem) 124 | *rem = n.s.low & (d.s.low - 1); 125 | if (d.s.low == 1) 126 | return n.all; 127 | sr = __builtin_ctz(d.s.low); 128 | q.s.high = n.s.high >> sr; 129 | q.s.low = (n.s.high << (n_uword_bits - sr)) | (n.s.low >> sr); 130 | return q.all; 131 | } 132 | /* K X 133 | * --- 134 | * 0 K 135 | */ 136 | sr = 1 + n_uword_bits + __builtin_clz(d.s.low) - __builtin_clz(n.s.high); 137 | /* 2 <= sr <= n_udword_bits - 1 138 | * q.all = n.all << (n_udword_bits - sr); 139 | * r.all = n.all >> sr; 140 | */ 141 | if (sr == n_uword_bits) 142 | { 143 | q.s.low = 0; 144 | q.s.high = n.s.low; 145 | r.s.high = 0; 146 | r.s.low = n.s.high; 147 | } 148 | else if (sr < n_uword_bits) // 2 <= sr <= n_uword_bits - 1 149 | { 150 | q.s.low = 0; 151 | q.s.high = n.s.low << (n_uword_bits - sr); 152 | r.s.high = n.s.high >> sr; 153 | r.s.low = (n.s.high << (n_uword_bits - sr)) | (n.s.low >> sr); 154 | } 155 | else // n_uword_bits + 1 <= sr <= n_udword_bits - 1 156 | { 157 | q.s.low = n.s.low << (n_udword_bits - sr); 158 | q.s.high = (n.s.high << (n_udword_bits - sr)) | 159 | (n.s.low >> (sr - n_uword_bits)); 160 | r.s.high = 0; 161 | r.s.low = n.s.high >> (sr - n_uword_bits); 162 | } 163 | } 164 | else 165 | { 166 | /* K X 167 | * --- 168 | * K K 169 | */ 170 | sr = __builtin_clz(d.s.high) - __builtin_clz(n.s.high); 171 | /* 0 <= sr <= n_uword_bits - 1 or sr large */ 172 | if (sr > n_uword_bits - 1) 173 | { 174 | if (rem) 175 | *rem = n.all; 176 | return 0; 177 | } 178 | ++sr; 179 | /* 1 <= sr <= n_uword_bits */ 180 | /* q.all = n.all << (n_udword_bits - sr); */ 181 | q.s.low = 0; 182 | if (sr == n_uword_bits) 183 | { 184 | q.s.high = n.s.low; 185 | r.s.high = 0; 186 | r.s.low = n.s.high; 187 | } 188 | else 189 | { 190 | q.s.high = n.s.low << (n_uword_bits - sr); 191 | r.s.high = n.s.high >> sr; 192 | r.s.low = (n.s.high << (n_uword_bits - sr)) | (n.s.low >> sr); 193 | } 194 | } 195 | } 196 | /* Not a special case 197 | * q and r are initialized with: 198 | * q.all = n.all << (n_udword_bits - sr); 199 | * r.all = n.all >> sr; 200 | * 1 <= sr <= n_udword_bits - 1 201 | */ 202 | su_int carry = 0; 203 | for (; sr > 0; --sr) 204 | { 205 | /* r:q = ((r:q) << 1) | carry */ 206 | r.s.high = (r.s.high << 1) | (r.s.low >> (n_uword_bits - 1)); 207 | r.s.low = (r.s.low << 1) | (q.s.high >> (n_uword_bits - 1)); 208 | q.s.high = (q.s.high << 1) | (q.s.low >> (n_uword_bits - 1)); 209 | q.s.low = (q.s.low << 1) | carry; 210 | /* carry = 0; 211 | * if (r.all >= d.all) 212 | * { 213 | * r.all -= d.all; 214 | * carry = 1; 215 | * } 216 | */ 217 | const di_int s = (di_int)(d.all - r.all - 1) >> (n_udword_bits - 1); 218 | carry = s & 1; 219 | r.all -= d.all & s; 220 | } 221 | q.all = (q.all << 1) | carry; 222 | if (rem) 223 | *rem = r.all; 224 | return q.all; 225 | } 226 | 227 | du_int 228 | __udivdi3(du_int a, du_int b) 229 | { 230 | return __udivmoddi4(a, b, 0); 231 | } 232 | 233 | du_int 234 | __umoddi3(du_int a, du_int b) 235 | { 236 | du_int r; 237 | __udivmoddi4(a, b, &r); 238 | return r; 239 | } 240 | -------------------------------------------------------------------------------- /barebones.cmake: -------------------------------------------------------------------------------- 1 | option(NATIVE "Compile natively for this CPU" OFF) 2 | option(MINIMAL "Compile small executable" OFF) 3 | option(EASTL "Compile with EASTL C++ library" OFF) 4 | option(LTO_ENABLE "Enable LTO (Clang-only)" OFF) 5 | option(STACK_PROTECTOR "Enable stack protector (SSP)" ON) 6 | option(UBSAN "Enable the undefined sanitizer" OFF) 7 | option(STRIPPED "Strip the executable" OFF) 8 | option(DEBUG "Build and preserve debugging information" OFF) 9 | option(RTTI_EXCEPTIONS "Enable C++ RTTI and exceptions" OFF) 10 | option(BUILD_32 "Build a 32-bit kernel" OFF) 11 | set(CPP_VERSION "c++17" CACHE STRING "C++ version compiler argument") 12 | set(C_VERSION "gnu11" CACHE STRING "C version compiler argument") 13 | set(LINKER_EXE "ld" CACHE STRING "Linker to use") 14 | 15 | if (BUILD_32) 16 | set(ELF_FORMAT "i386") 17 | set(CMAKE_ASM_NASM_OBJECT_FORMAT "elf32") 18 | set(OBJCOPY_TARGET "elf32-x86-32") 19 | set(TARGET_TRIPLE "i686-pc-linux") 20 | set(CAPABS "-m32") 21 | else() 22 | set(ELF_FORMAT "x86_64") 23 | set(CMAKE_ASM_NASM_OBJECT_FORMAT "elf64") 24 | set(OBJCOPY_TARGET "elf64-x86-64") 25 | set(TARGET_TRIPLE "x86_64-pc-linux") 26 | set(CAPABS "-m64 -fno-omit-frame-pointer -fPIE") 27 | endif() 28 | enable_language(ASM_NASM) 29 | set(CAPABS "${CAPABS} -Wall -Wextra -g -ffreestanding") 30 | 31 | # Optimization flags 32 | set(OPTIMIZE "-mfpmath=sse -msse3") 33 | if (NATIVE) 34 | set(OPTIMIZE "${OPTIMIZE} -Ofast -march=native") 35 | elseif (MINIMAL) 36 | if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") 37 | set(OPTIMIZE "${OPTIMIZE} -Oz") 38 | else() 39 | set(OPTIMIZE "${OPTIMIZE} -Os -ffunction-sections -fdata-sections") 40 | endif() 41 | endif() 42 | 43 | set(CMAKE_CXX_FLAGS "-MMD ${CAPABS} ${OPTIMIZE} -std=${CPP_VERSION}") 44 | set(CMAKE_C_FLAGS "-MMD ${CAPABS} ${OPTIMIZE} -std=${C_VERSION}") 45 | if (LTO_ENABLE) 46 | if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") 47 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -flto=thin") 48 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -flto=thin") 49 | else() 50 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -flto") 51 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -flto") 52 | endif() 53 | # BUG: workaround for LTO bug 54 | set(KERNEL_LIBRARY --whole-archive kernel --no-whole-archive -gc-sections) 55 | else() 56 | set(KERNEL_LIBRARY kernel) 57 | endif() 58 | 59 | if (NOT RTTI_EXCEPTIONS) 60 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-exceptions -fno-rtti") 61 | endif() 62 | if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") 63 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -target ${TARGET_TRIPLE}") 64 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -target ${TARGET_TRIPLE}") 65 | endif() 66 | 67 | # Sanitizer options 68 | if (UBSAN) 69 | set(UBSAN_PARAMS "-fsanitize=undefined -fno-sanitize=vptr -DUBSAN_ENABLED") 70 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${UBSAN_PARAMS}") 71 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${UBSAN_PARAMS}") 72 | if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") 73 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-sanitize=function") 74 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-sanitize=function") 75 | endif() 76 | endif() 77 | if (STACK_PROTECTOR) 78 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fstack-protector-strong") 79 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fstack-protector-strong") 80 | endif() 81 | 82 | # linker stuff 83 | set(CMAKE_LINKER ${LINKER_EXE}) 84 | set(CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS) # this removed -rdynamic from linker output 85 | set(CMAKE_CXX_LINK_EXECUTABLE " -o ") 86 | 87 | set(BBPATH ${CMAKE_CURRENT_LIST_DIR}) 88 | 89 | # linker arguments 90 | string(RANDOM LENGTH 16 ALPHABET 0123456789abcdef SSP_VALUE) 91 | set(LDSCRIPT "${BBPATH}/src/linker.ld") 92 | set(LDFLAGS "-static -nostdlib -N -melf_${ELF_FORMAT} --script=${LDSCRIPT} --defsym __SSP__=0x${SSP_VALUE}") 93 | if (NOT DEBUG AND STRIPPED) 94 | set(LDFLAGS "${LDFLAGS} -s") 95 | elseif (NOT DEBUG) 96 | set(LDFLAGS "${LDFLAGS} -S") 97 | endif() 98 | if (MINIMAL) 99 | set(LDFLAGS "${LDFLAGS} --gc-sections") 100 | endif() 101 | 102 | # Compiler, C and C++ libraries 103 | include(ExternalProject) 104 | ExternalProject_Add(exceptions 105 | PREFIX exceptions 106 | URL https://github.com/fwsGonzo/barebones/releases/download/exceptions/exceptions.zip 107 | URL_HASH SHA1=8851485a7134eb8743069439235c1a2a9728ea58 108 | CONFIGURE_COMMAND "" 109 | BUILD_COMMAND "" 110 | UPDATE_COMMAND "" 111 | INSTALL_COMMAND "" 112 | ) 113 | 114 | # gcc-9 --print-file-name libgcc.a 115 | if (BUILD_32) 116 | execute_process(COMMAND ${CMAKE_C_COMPILER} -m32 --print-file-name libgcc_eh.a 117 | OUTPUT_VARIABLE LIBGCC_ARCHIVE OUTPUT_STRIP_TRAILING_WHITESPACE) 118 | else() 119 | execute_process(COMMAND ${CMAKE_C_COMPILER} -m64 --print-file-name libgcc_eh.a 120 | OUTPUT_VARIABLE LIBGCC_ARCHIVE OUTPUT_STRIP_TRAILING_WHITESPACE) 121 | endif() 122 | 123 | add_library(libgcc STATIC IMPORTED) 124 | set_target_properties(libgcc PROPERTIES LINKER_LANGUAGE CXX) 125 | if (EXISTS ${LIBGCC_ARCHIVE}) 126 | set_target_properties(libgcc PROPERTIES IMPORTED_LOCATION ${LIBGCC_ARCHIVE}) 127 | else() 128 | # Use this if you don't have a compatible libgcc_eh.a in your system: 129 | set_target_properties(libgcc PROPERTIES IMPORTED_LOCATION exceptions/src/exceptions/libgcc.a) 130 | endif() 131 | add_dependencies(libgcc exceptions) 132 | 133 | if (RTTI_EXCEPTIONS) 134 | add_library(cxxabi STATIC IMPORTED) 135 | set_target_properties(cxxabi PROPERTIES LINKER_LANGUAGE CXX) 136 | set_target_properties(cxxabi PROPERTIES IMPORTED_LOCATION exceptions/src/exceptions/libc++abi.a) 137 | add_dependencies(cxxabi exceptions) 138 | 139 | # Add --eh-frame-hdr for exception tables 140 | set(LDFLAGS "${LDFLAGS} --eh-frame-hdr") 141 | set(CXX_ABI_LIBS cxxabi) 142 | endif() 143 | 144 | # Machine image creation 145 | function(add_machine_image NAME BINARY_NAME BINARY_DESC) 146 | add_executable(${NAME} ${ARGN}) 147 | set_target_properties(${NAME} PROPERTIES OUTPUT_NAME ${BINARY_NAME}) 148 | target_include_directories(${NAME} PRIVATE src) 149 | 150 | target_compile_definitions(${NAME} PRIVATE KERNEL_BINARY="${BINARY_NAME}") 151 | target_compile_definitions(${NAME} PRIVATE KERNEL_DESC="${BINARY_DESC}") 152 | 153 | add_subdirectory(${BBPATH}/ext ext) 154 | if (EASTL) 155 | target_link_libraries(${NAME} eastl) 156 | endif() 157 | 158 | add_subdirectory(${BBPATH}/src src) 159 | 160 | target_link_libraries(${NAME} 161 | # BUG: unfortunately, there is an LLD bug that prevents ASM objects 162 | # from linking with outside files that are in archives, unless they are 163 | # --whole-archived, which sort of defeats the purpose of LTO 164 | ${KERNEL_LIBRARY} 165 | tinyprintf 166 | ${CXX_ABI_LIBS} 167 | libgcc 168 | kernel 169 | ) 170 | 171 | set_target_properties(${NAME} PROPERTIES LINK_FLAGS "${LDFLAGS}") 172 | # write out the binary name to a known file to simplify some scripts 173 | file(WRITE ${CMAKE_BINARY_DIR}/binary.txt ${BINARY_NAME}) 174 | endfunction() 175 | 176 | function(create_payload LIB_NAME BLOB_FILE BLOB_NAME) 177 | # call objcopy with curated filename, and then 178 | # create a static library LIB_NAME with payload 179 | set(OBJECT_FILE ${BLOB_NAME}_generated.o) 180 | add_custom_command( 181 | OUTPUT ${OBJECT_FILE} 182 | # temporarily name blob file as BLOB_NAME so that we can better control 183 | # the symbols generated on the inside of the kernel 184 | COMMAND cp ${BLOB_FILE} ${BLOB_NAME} 185 | COMMAND ${CMAKE_OBJCOPY} -I binary -O ${OBJCOPY_TARGET} -B i386 ${BLOB_NAME} ${OBJECT_FILE} 186 | COMMAND rm -f ${BLOB_NAME} 187 | DEPENDS ${BLOB_FILE} 188 | WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} 189 | ) 190 | add_library(${LIB_NAME} STATIC ${OBJECT_FILE}) 191 | set_target_properties(${LIB_NAME} PROPERTIES LINKER_LANGUAGE C) 192 | endfunction() 193 | function (add_machine_payload TARGET LIB_NAME BLOB_FILE BLOB_NAME) 194 | create_payload(${LIB_NAME} ${BLOB_FILE} ${BLOB_NAME}) 195 | target_link_libraries(${TARGET} --whole-archive mylib_payload --no-whole-archive) 196 | endfunction() 197 | -------------------------------------------------------------------------------- /src/multiboot.h: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 1999,2003,2007,2008,2009,2010 Free Software Foundation, Inc. 2 | * 3 | * Permission is hereby granted, free of charge, to any person obtaining a copy 4 | * of this software and associated documentation files (the "Software"), to 5 | * deal in the Software without restriction, including without limitation the 6 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 7 | * sell copies of the Software, and to permit persons to whom the Software is 8 | * furnished to do so, subject to the following conditions: 9 | * 10 | * The above copyright notice and this permission notice shall be included in 11 | * all copies or substantial portions of the Software. 12 | * 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ANY 16 | * DEVELOPER OR DISTRIBUTOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 17 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 18 | * IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | */ 20 | 21 | #ifndef MULTIBOOT_HEADER 22 | #define MULTIBOOT_HEADER 1 23 | 24 | /* How many bytes from the start of the file we search for the header. */ 25 | #define MULTIBOOT_SEARCH 8192 26 | #define MULTIBOOT_HEADER_ALIGN 4 27 | 28 | /* The magic field should contain this. */ 29 | #define MULTIBOOT_HEADER_MAGIC 0x1BADB002 30 | 31 | /* This should be in %eax. */ 32 | #define MULTIBOOT_BOOTLOADER_MAGIC 0x2BADB002 33 | 34 | /* Alignment of multiboot modules. */ 35 | #define MULTIBOOT_MOD_ALIGN 0x00001000 36 | 37 | /* Alignment of the multiboot info structure. */ 38 | #define MULTIBOOT_INFO_ALIGN 0x00000004 39 | 40 | /* Flags set in the 'flags' member of the multiboot header. */ 41 | 42 | /* Align all boot modules on i386 page (4KB) boundaries. */ 43 | #define MULTIBOOT_PAGE_ALIGN 0x00000001 44 | 45 | /* Must pass memory information to OS. */ 46 | #define MULTIBOOT_MEMORY_INFO 0x00000002 47 | 48 | /* Must pass video information to OS. */ 49 | #define MULTIBOOT_VIDEO_MODE 0x00000004 50 | 51 | /* This flag indicates the use of the address fields in the header. */ 52 | #define MULTIBOOT_AOUT_KLUDGE 0x00010000 53 | 54 | /* Flags to be set in the 'flags' member of the multiboot info structure. */ 55 | 56 | /* is there basic lower/upper memory information? */ 57 | #define MULTIBOOT_INFO_MEMORY 0x00000001 58 | /* is there a boot device set? */ 59 | #define MULTIBOOT_INFO_BOOTDEV 0x00000002 60 | /* is the command-line defined? */ 61 | #define MULTIBOOT_INFO_CMDLINE 0x00000004 62 | /* are there modules to do something with? */ 63 | #define MULTIBOOT_INFO_MODS 0x00000008 64 | 65 | /* These next two are mutually exclusive */ 66 | 67 | /* is there a symbol table loaded? */ 68 | #define MULTIBOOT_INFO_AOUT_SYMS 0x00000010 69 | /* is there an ELF section header table? */ 70 | #define MULTIBOOT_INFO_ELF_SHDR 0X00000020 71 | 72 | /* is there a full memory map? */ 73 | #define MULTIBOOT_INFO_MEM_MAP 0x00000040 74 | 75 | /* Is there drive info? */ 76 | #define MULTIBOOT_INFO_DRIVE_INFO 0x00000080 77 | 78 | /* Is there a config table? */ 79 | #define MULTIBOOT_INFO_CONFIG_TABLE 0x00000100 80 | 81 | /* Is there a boot loader name? */ 82 | #define MULTIBOOT_INFO_BOOT_LOADER_NAME 0x00000200 83 | 84 | /* Is there a APM table? */ 85 | #define MULTIBOOT_INFO_APM_TABLE 0x00000400 86 | 87 | /* Is there video information? */ 88 | #define MULTIBOOT_INFO_VBE_INFO 0x00000800 89 | #define MULTIBOOT_INFO_FRAMEBUFFER_INFO 0x00001000 90 | 91 | #ifndef ASM_FILE 92 | 93 | typedef unsigned char multiboot_uint8_t; 94 | typedef unsigned short multiboot_uint16_t; 95 | typedef unsigned int multiboot_uint32_t; 96 | typedef unsigned long long multiboot_uint64_t; 97 | 98 | struct multiboot_header 99 | { 100 | /* Must be MULTIBOOT_MAGIC - see above. */ 101 | multiboot_uint32_t magic; 102 | 103 | /* Feature flags. */ 104 | multiboot_uint32_t flags; 105 | 106 | /* The above fields plus this one must equal 0 mod 2^32. */ 107 | multiboot_uint32_t checksum; 108 | 109 | /* These are only valid if MULTIBOOT_AOUT_KLUDGE is set. */ 110 | multiboot_uint32_t header_addr; 111 | multiboot_uint32_t load_addr; 112 | multiboot_uint32_t load_end_addr; 113 | multiboot_uint32_t bss_end_addr; 114 | multiboot_uint32_t entry_addr; 115 | 116 | /* These are only valid if MULTIBOOT_VIDEO_MODE is set. */ 117 | multiboot_uint32_t mode_type; 118 | multiboot_uint32_t width; 119 | multiboot_uint32_t height; 120 | multiboot_uint32_t depth; 121 | }; 122 | 123 | /* The symbol table for a.out. */ 124 | struct multiboot_aout_symbol_table 125 | { 126 | multiboot_uint32_t tabsize; 127 | multiboot_uint32_t strsize; 128 | multiboot_uint32_t addr; 129 | multiboot_uint32_t reserved; 130 | }; 131 | typedef struct multiboot_aout_symbol_table multiboot_aout_symbol_table_t; 132 | 133 | /* The section header table for ELF. */ 134 | struct multiboot_elf_section_header_table 135 | { 136 | multiboot_uint32_t num; 137 | multiboot_uint32_t size; 138 | multiboot_uint32_t addr; 139 | multiboot_uint32_t shndx; 140 | }; 141 | typedef struct multiboot_elf_section_header_table multiboot_elf_section_header_table_t; 142 | 143 | struct multiboot_info 144 | { 145 | /* Multiboot info version number */ 146 | multiboot_uint32_t flags; 147 | 148 | /* Available memory from BIOS */ 149 | multiboot_uint32_t mem_lower; 150 | multiboot_uint32_t mem_upper; 151 | 152 | /* "root" partition */ 153 | multiboot_uint32_t boot_device; 154 | 155 | /* Kernel command line */ 156 | multiboot_uint32_t cmdline; 157 | 158 | /* Boot-Module list */ 159 | multiboot_uint32_t mods_count; 160 | multiboot_uint32_t mods_addr; 161 | 162 | union 163 | { 164 | multiboot_aout_symbol_table_t aout_sym; 165 | multiboot_elf_section_header_table_t elf_sec; 166 | } u; 167 | 168 | /* Memory Mapping buffer */ 169 | multiboot_uint32_t mmap_length; 170 | multiboot_uint32_t mmap_addr; 171 | 172 | /* Drive Info buffer */ 173 | multiboot_uint32_t drives_length; 174 | multiboot_uint32_t drives_addr; 175 | 176 | /* ROM configuration table */ 177 | multiboot_uint32_t config_table; 178 | 179 | /* Boot Loader Name */ 180 | multiboot_uint32_t boot_loader_name; 181 | 182 | /* APM table */ 183 | multiboot_uint32_t apm_table; 184 | 185 | /* Video */ 186 | multiboot_uint32_t vbe_control_info; 187 | multiboot_uint32_t vbe_mode_info; 188 | multiboot_uint16_t vbe_mode; 189 | multiboot_uint16_t vbe_interface_seg; 190 | multiboot_uint16_t vbe_interface_off; 191 | multiboot_uint16_t vbe_interface_len; 192 | 193 | multiboot_uint64_t framebuffer_addr; 194 | multiboot_uint32_t framebuffer_pitch; 195 | multiboot_uint32_t framebuffer_width; 196 | multiboot_uint32_t framebuffer_height; 197 | multiboot_uint8_t framebuffer_bpp; 198 | #define MULTIBOOT_FRAMEBUFFER_TYPE_INDEXED 0 199 | #define MULTIBOOT_FRAMEBUFFER_TYPE_RGB 1 200 | #define MULTIBOOT_FRAMEBUFFER_TYPE_EGA_TEXT 2 201 | multiboot_uint8_t framebuffer_type; 202 | union 203 | { 204 | struct 205 | { 206 | multiboot_uint32_t framebuffer_palette_addr; 207 | multiboot_uint16_t framebuffer_palette_num_colors; 208 | }; 209 | struct 210 | { 211 | multiboot_uint8_t framebuffer_red_field_position; 212 | multiboot_uint8_t framebuffer_red_mask_size; 213 | multiboot_uint8_t framebuffer_green_field_position; 214 | multiboot_uint8_t framebuffer_green_mask_size; 215 | multiboot_uint8_t framebuffer_blue_field_position; 216 | multiboot_uint8_t framebuffer_blue_mask_size; 217 | }; 218 | }; 219 | }; 220 | typedef struct multiboot_info multiboot_info_t; 221 | 222 | struct multiboot_color 223 | { 224 | multiboot_uint8_t red; 225 | multiboot_uint8_t green; 226 | multiboot_uint8_t blue; 227 | }; 228 | 229 | struct multiboot_mmap_entry 230 | { 231 | multiboot_uint32_t size; 232 | multiboot_uint64_t addr; 233 | multiboot_uint64_t len; 234 | #define MULTIBOOT_MEMORY_AVAILABLE 1 235 | #define MULTIBOOT_MEMORY_RESERVED 2 236 | #define MULTIBOOT_MEMORY_ACPI_RECLAIMABLE 3 237 | #define MULTIBOOT_MEMORY_NVS 4 238 | #define MULTIBOOT_MEMORY_BADRAM 5 239 | multiboot_uint32_t type; 240 | } __attribute__((packed)); 241 | typedef struct multiboot_mmap_entry multiboot_memory_map_t; 242 | 243 | struct multiboot_mod_list 244 | { 245 | /* the memory used goes from bytes 'mod_start' to 'mod_end-1' inclusive */ 246 | multiboot_uint32_t mod_start; 247 | multiboot_uint32_t mod_end; 248 | 249 | /* Module command line */ 250 | multiboot_uint32_t cmdline; 251 | 252 | /* padding to take it to 16 bytes (must be zero) */ 253 | multiboot_uint32_t pad; 254 | }; 255 | typedef struct multiboot_mod_list multiboot_module_t; 256 | 257 | /* APM BIOS info. */ 258 | struct multiboot_apm_info 259 | { 260 | multiboot_uint16_t version; 261 | multiboot_uint16_t cseg; 262 | multiboot_uint32_t offset; 263 | multiboot_uint16_t cseg_16; 264 | multiboot_uint16_t dseg; 265 | multiboot_uint16_t flags; 266 | multiboot_uint16_t cseg_len; 267 | multiboot_uint16_t cseg_16_len; 268 | multiboot_uint16_t dseg_len; 269 | }; 270 | 271 | #endif /* ! ASM_FILE */ 272 | 273 | #endif /* ! MULTIBOOT_HEADER */ 274 | --------------------------------------------------------------------------------