├── .github └── workflows │ └── main.yml ├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── LICENSE ├── README.md ├── build.py ├── installer ├── CMakeLists.txt ├── dump_as_array.py └── hyper_install.c ├── loader ├── CMakeLists.txt ├── allocator.c ├── arch │ ├── aarch64 │ │ ├── CMakeLists.txt │ │ ├── aarch64_handover.h │ │ ├── boot_protocol │ │ │ ├── CMakeLists.txt │ │ │ └── ultra_impl.c │ │ ├── elf.c │ │ ├── handover.asm │ │ ├── handover_impl.c │ │ ├── include │ │ │ └── arch │ │ │ │ ├── constants.h │ │ │ │ ├── elf.h │ │ │ │ ├── handover_flags.h │ │ │ │ └── virtual_memory.h │ │ └── virtual_memory.c │ └── x86 │ │ ├── CMakeLists.txt │ │ ├── bios │ │ ├── CMakeLists.txt │ │ ├── a20.asm │ │ ├── apm.c │ │ ├── bios_call.asm │ │ ├── bios_call.h │ │ ├── bios_disk_services.c │ │ ├── bios_disk_services.h │ │ ├── bios_entry.asm │ │ ├── bios_entry.c │ │ ├── bios_find.c │ │ ├── bios_handover.asm │ │ ├── bios_handover.c │ │ ├── bios_memory_services.c │ │ ├── bios_memory_services.h │ │ ├── bios_video_services.c │ │ ├── bios_video_services.h │ │ ├── boot_record │ │ │ ├── CMakeLists.txt │ │ │ └── boot_record.asm │ │ └── linker.ld │ │ ├── boot_protocol │ │ ├── CMakeLists.txt │ │ └── ultra_impl.c │ │ ├── elf.c │ │ ├── handover_impl.c │ │ ├── include │ │ ├── arch │ │ │ ├── constants.h │ │ │ ├── elf.h │ │ │ ├── handover_flags.h │ │ │ └── virtual_memory.h │ │ └── pio.h │ │ ├── uefi │ │ ├── CMakeLists.txt │ │ ├── uefi_handover.asm │ │ └── uefi_handover.c │ │ └── virtual_memory.c ├── boot_protocol │ ├── CMakeLists.txt │ ├── boot_protocol.c │ └── ultra.c ├── common │ ├── CMakeLists.txt │ ├── conversions.c │ ├── dynamic_buffer.c │ ├── format.c │ ├── log.c │ ├── panic.c │ ├── rw_helpers.c │ ├── string.c │ └── string_view.c ├── config.c ├── edid.c ├── elf.c ├── filesystem │ ├── CMakeLists.txt │ ├── block_cache.c │ ├── bulk_read.c │ ├── fat │ │ ├── CMakeLists.txt │ │ ├── fat.c │ │ └── structures.h │ ├── filesystem.c │ ├── filesystem_table.c │ ├── gpt.c │ ├── iso9660 │ │ ├── CMakeLists.txt │ │ ├── iso9660.c │ │ └── iso9660_structures.h │ ├── mbr.c │ └── path.c ├── gcc_builtins.c ├── include │ ├── allocator.h │ ├── apm.h │ ├── boot_protocol.h │ ├── boot_protocol │ │ └── ultra_impl.h │ ├── common │ │ ├── align.h │ │ ├── attributes.h │ │ ├── bug.h │ │ ├── constants.h │ │ ├── conversions.h │ │ ├── ctype.h │ │ ├── dynamic_buffer.h │ │ ├── format.h │ │ ├── hardened_string.h │ │ ├── helpers.h │ │ ├── log.h │ │ ├── minmax.h │ │ ├── panic.h │ │ ├── range.h │ │ ├── rw_helpers.h │ │ ├── string.h │ │ ├── string_view.h │ │ └── types.h │ ├── config.h │ ├── disk_services.h │ ├── edid.h │ ├── elf.h │ ├── elf │ │ ├── context.h │ │ ├── machine.h │ │ └── structures.h │ ├── filesystem │ │ ├── block_cache.h │ │ ├── bulk_read.h │ │ ├── filesystem.h │ │ ├── filesystem_table.h │ │ ├── gpt.h │ │ ├── guid.h │ │ ├── mbr.h │ │ └── path.h │ ├── handover.h │ ├── handover_impl.h │ ├── hyper.h │ ├── memory_services.h │ ├── services.h │ ├── services_impl.h │ ├── uefi │ │ ├── globals.h │ │ ├── helpers.h │ │ ├── relocator.h │ │ └── structures.h │ ├── video_services.h │ ├── virtual_memory.h │ └── virtual_memory_impl.h ├── loader.c ├── memory_services.c ├── services_impl.c ├── uefi │ ├── CMakeLists.txt │ ├── include │ │ └── platform_config.h │ ├── linker.ld │ ├── relocator.c │ ├── stubs.c │ ├── uefi_disk_services.c │ ├── uefi_disk_services.h │ ├── uefi_entry.c │ ├── uefi_find.c │ ├── uefi_helpers.c │ ├── uefi_memory_services.c │ ├── uefi_memory_services.h │ ├── uefi_video_services.c │ └── uefi_video_services.h └── virtual_memory.c ├── tests ├── conftest.py ├── disk_image.py ├── kernel │ ├── CMakeLists.txt │ ├── build.sh │ ├── common │ │ └── log.c │ ├── fb_font.c │ ├── fb_tty.c │ ├── include │ │ ├── common │ │ │ ├── log.h │ │ │ └── string_ex.h │ │ ├── fb_font.h │ │ ├── fb_tty.h │ │ ├── test_ctl.h │ │ └── ultra_helpers.h │ ├── kernel.c │ ├── link_1gb_generic.ld │ ├── link_1mb_generic.ld │ ├── link_generic_64bit_higher_half.ld │ ├── link_i686_higher_half.ld │ ├── test_ctl_aarch64.c │ ├── test_ctl_generic.c │ ├── test_ctl_impl.h │ ├── test_ctl_x86.c │ └── ultra_helpers.c ├── make_disk_image.py ├── options.py ├── path_guesser.py ├── pytest.ini └── test_loader.py └── toolchain ├── toolchain_clang.cmake ├── toolchain_dummy.cmake └── toolchain_gcc.cmake /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: [push, pull_request] 3 | 4 | concurrency: 5 | group: ${{ github.head_ref || format('{0}-{1}', github.ref, github.run_number) }} 6 | cancel-in-progress: true 7 | 8 | jobs: 9 | build: 10 | runs-on: ${{ matrix.os }} 11 | 12 | strategy: 13 | fail-fast: true 14 | matrix: 15 | os: [ubuntu-latest, macos-latest] 16 | platform: [bios, uefi] 17 | toolchain: [gcc, clang] 18 | arch: [i686, amd64, aarch64] 19 | exclude: 20 | # FIXME: the GCC build somehow ends up being corrupted with 21 | # libexec/cc1 containing garbage data. 22 | - os: macos-latest 23 | toolchain: gcc 24 | - arch: i686 25 | platform: uefi 26 | - arch: amd64 27 | platform: bios 28 | - arch: aarch64 29 | platform: bios 30 | # GCC (or at least ld) doesn't support aarch64 mingw 31 | - arch: aarch64 32 | toolchain: gcc 33 | 34 | steps: 35 | - uses: actions/checkout@v3 36 | with: 37 | submodules: true 38 | 39 | - if: ${{ matrix.toolchain == 'gcc' }} 40 | name: Restore cache 41 | uses: actions/cache@v4.2.2 42 | env: 43 | gcc-version: 13.2.0 44 | with: 45 | path: "${{ github.workspace }}/toolchain/tools_${{ matrix.arch }}_${{ matrix.platform }}_gcc" 46 | key: "${{ runner.os }}-${{ runner.arch }}-${{ matrix.arch }}-${{ matrix.platform }}-gcc-${{ env.gcc-version }}" 47 | 48 | - name: Build platform toolchain & Hyper 49 | run: "${{ github.workspace }}/build.py --arch ${{ matrix.arch }} --platform ${{ matrix.platform }} --toolchain ${{ matrix.toolchain }} --fetch-test-dependencies --no-tune-native --e9-debug-log=on" 50 | 51 | - name: Build test kernel 52 | run: ${{ github.workspace }}/tests/kernel/build.sh 53 | 54 | - name: Run tests 55 | run: python3 -m pytest ${{ github.workspace }}/tests/test_loader.py -rs -s -v --junitxml result.xml 56 | 57 | - name: Publish test results 58 | uses: mikepenz/action-junit-report@v3 59 | if: always() 60 | with: 61 | report_paths: result.xml 62 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | build-*/ 3 | !toolchain 4 | toolchain/* 5 | !toolchain/toolchain_gcc.cmake 6 | !toolchain/toolchain_clang.cmake 7 | !toolchain/toolchain_dummy.cmake 8 | installer/build 9 | installer/tmp 10 | installer/src 11 | installer/mbr_embedded.c 12 | installer/iso_mbr_embedded.c 13 | installer/stage2_embedded.c 14 | installer/hyper_install 15 | installer/hyper_install.exe 16 | *.pyc 17 | tests/kernel/build 18 | tests/kernel/ultra_protocol.h 19 | !tests/kernel/include/ 20 | tests/kernel/include/* 21 | !tests/kernel/common 22 | tests/kernel/common/* 23 | !tests/kernel/include/common 24 | tests/kernel/include/common/* 25 | !tests/kernel/include/common/log.h 26 | !tests/kernel/include/common/string_ex.h 27 | !tests/kernel/common/log.c 28 | tests/kernel/gcc_builtins.c 29 | cmake-build-debug/ 30 | .idea/ 31 | .vscode/ 32 | .DS_Store 33 | !toolchain/build.sh 34 | .pytest_cache 35 | __pycache__ 36 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "loader/boot_protocol/ultra_protocol"] 2 | path = loader/boot_protocol/ultra_protocol 3 | url = https://github.com/UltraOS/UltraProtocol 4 | [submodule "build_utils"] 5 | path = build_utils 6 | url = https://github.com/UltraOS/BuildUtils 7 | [submodule "tests/image_utils"] 8 | path = tests/image_utils 9 | url = https://github.com/UltraOS/ImageUtils 10 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16) 2 | 3 | if (HYPER_PLATFORM) 4 | string(TOLOWER ${HYPER_PLATFORM} HYPER_PLATFORM) 5 | endif () 6 | 7 | if (HYPER_ARCH) 8 | string(TOLOWER ${HYPER_ARCH} HYPER_ARCH) 9 | endif () 10 | 11 | if (NOT HYPER_ARCH) 12 | set(HYPER_ARCH "i686") 13 | elseif ((NOT HYPER_ARCH STREQUAL "i686") AND 14 | (NOT HYPER_ARCH STREQUAL "amd64") AND 15 | (NOT HYPER_ARCH STREQUAL "aarch64")) 16 | message(FATAL_ERROR "Unknown architecture '${HYPER_ARCH}'") 17 | endif () 18 | 19 | if (NOT HYPER_PLATFORM) 20 | if (HYPER_ARCH AND NOT HYPER_ARCH STREQUAL "i686") 21 | set(HYPER_PLATFORM "uefi") 22 | else () 23 | set(HYPER_ARCH "i686") 24 | set(HYPER_PLATFORM "bios") 25 | endif () 26 | elseif ((NOT HYPER_PLATFORM STREQUAL "bios") AND 27 | (NOT HYPER_PLATFORM STREQUAL "uefi")) 28 | message(FATAL_ERROR "Unknown platform '${HYPER_PLATFORM}'") 29 | endif () 30 | 31 | if (HYPER_TOOLCHAIN) 32 | string(TOLOWER ${HYPER_TOOLCHAIN} HYPER_TOOLCHAIN) 33 | endif () 34 | 35 | if (NOT HYPER_TOOLCHAIN) 36 | if ($ENV{CLION_IDE}) 37 | set(HYPER_TOOLCHAIN dummy) 38 | message("Detected CLion, using a dummy toolchain file") 39 | else () 40 | set(HYPER_TOOLCHAIN "gcc") 41 | endif () 42 | endif () 43 | 44 | 45 | if (HYPER_TOOLCHAIN STREQUAL "gcc" OR HYPER_TOOLCHAIN STREQUAL "clang") 46 | set(HYPER_TOOLCHAIN_DIR "${CMAKE_SOURCE_DIR}/toolchain") 47 | include("${HYPER_TOOLCHAIN_DIR}/toolchain_${HYPER_TOOLCHAIN}.cmake") 48 | else () 49 | include("${HYPER_TOOLCHAIN}") 50 | endif() 51 | 52 | project(Hyper C) 53 | 54 | add_subdirectory(loader) 55 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 UltraOS 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /installer/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16) 2 | 3 | project(HyperInstaller C) 4 | 5 | set(OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}") 6 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${OUTPUT_DIRECTORY}") 7 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG "${OUTPUT_DIRECTORY}") 8 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE "${OUTPUT_DIRECTORY}") 9 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_DEBUG "${OUTPUT_DIRECTORY}") 10 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_RELEASE "${OUTPUT_DIRECTORY}") 11 | set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_DEBUG "${OUTPUT_DIRECTORY}") 12 | set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_RELEASE "${OUTPUT_DIRECTORY}") 13 | 14 | if (NOT MBR_PATH) 15 | message(FATAL_ERROR "MBR_PATH must be specified") 16 | endif () 17 | 18 | if (NOT ISO_MBR_PATH) 19 | message(FATAL_ERROR "ISO_MBR_PATH must be specified") 20 | endif () 21 | 22 | if (NOT STAGE2_PATH) 23 | message(FATAL_ERROR "STAGE2_PATH must be specified") 24 | endif () 25 | 26 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O2") 27 | 28 | set(MBR_EMBEDDED_PATH "${PROJECT_SOURCE_DIR}/mbr_embedded.c") 29 | set(ISO_MBR_EMBEDDED_PATH "${PROJECT_SOURCE_DIR}/iso_mbr_embedded.c") 30 | set(STAGE2_EMBEDDED_PATH "${PROJECT_SOURCE_DIR}/stage2_embedded.c") 31 | 32 | add_custom_command( 33 | OUTPUT ${MBR_EMBEDDED_PATH} 34 | COMMAND python3 ${PROJECT_SOURCE_DIR}/dump_as_array.py ${MBR_PATH} ${MBR_EMBEDDED_PATH} mbr 35 | DEPENDS ${MBR_PATH} 36 | ) 37 | 38 | add_custom_command( 39 | OUTPUT ${ISO_MBR_EMBEDDED_PATH} 40 | COMMAND python3 ${PROJECT_SOURCE_DIR}/dump_as_array.py ${ISO_MBR_PATH} ${ISO_MBR_EMBEDDED_PATH} iso_mbr 41 | DEPENDS ${ISO_MBR_PATH} 42 | ) 43 | 44 | add_custom_command( 45 | OUTPUT ${STAGE2_EMBEDDED_PATH} 46 | COMMAND python3 ${PROJECT_SOURCE_DIR}/dump_as_array.py ${STAGE2_PATH} ${STAGE2_EMBEDDED_PATH} stage2 47 | DEPENDS ${STAGE2_PATH} 48 | ) 49 | 50 | add_executable(hyper_install hyper_install.c ${MBR_EMBEDDED_PATH} ${ISO_MBR_EMBEDDED_PATH} ${STAGE2_EMBEDDED_PATH}) 51 | set_property(TARGET hyper_install PROPERTY C_STANDARD 11) 52 | if (MSVC) 53 | target_compile_options(hyper_install PRIVATE /W4 /WX) 54 | else () 55 | target_compile_options(hyper_install PRIVATE -Wall -Wextra -Wpedantic -Werror) 56 | endif () 57 | 58 | if (UNIX AND NOT APPLE) 59 | target_link_libraries(hyper_install -static) 60 | endif () 61 | -------------------------------------------------------------------------------- /installer/dump_as_array.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | import argparse 3 | import os 4 | 5 | BYTES_PER_LINE = 8 6 | 7 | def main(): 8 | parser = argparse.ArgumentParser("Dump a binary file as a C array") 9 | parser.add_argument("input_file", type=str) 10 | parser.add_argument("output_file", type=str) 11 | parser.add_argument("variable_name", type=str, 12 | help="Variable prefix to use for buffer & size") 13 | args = parser.parse_args() 14 | 15 | in_file = open(args.input_file, "rb") 16 | in_file_size = os.path.getsize(args.input_file) 17 | if not in_file_size: 18 | print(f"{args.input_file} is empty!") 19 | exit(1) 20 | 21 | with open(args.output_file, "w") as out_file: 22 | out_file.write(f"unsigned long {args.variable_name}_size = {in_file_size};\n") 23 | out_file.write(f"unsigned char {args.variable_name}_data[] = ") 24 | out_file.write("{\n ") 25 | 26 | for i in range(1, in_file_size + 1): 27 | byte = in_file.read(1) 28 | out_file.write(f"0x{byte.hex().upper()},") 29 | 30 | if i == in_file_size: 31 | break 32 | 33 | if (i % BYTES_PER_LINE) == 0: 34 | out_file.write("\n ") 35 | else: 36 | out_file.write(" ") 37 | 38 | out_file.write("\n};\n") 39 | 40 | if __name__ == "__main__": 41 | main() 42 | -------------------------------------------------------------------------------- /loader/allocator.c: -------------------------------------------------------------------------------- 1 | #undef HYPER_ALLOCATION_AUDIT 2 | 3 | #include "common/log.h" 4 | #include "common/format.h" 5 | #include "common/string.h" 6 | #include "common/align.h" 7 | #include "common/string_view.h" 8 | 9 | #include "services.h" 10 | #include "memory_services.h" 11 | 12 | #include "allocator.h" 13 | 14 | #define ANY_ADDRESS "" 15 | 16 | static void allocation_did_fail(const struct allocation_spec *spec) 17 | { 18 | u32 type = spec->type ?: ALLOCATOR_DEFAULT_ALLOC_TYPE; 19 | bool is_critical = spec->flags & ALLOCATE_CRITICAL; 20 | enum log_level lvl = is_critical ? LOG_LEVEL_ERR : LOG_LEVEL_WARN; 21 | char address_as_string[32]; 22 | 23 | if (spec->flags & ALLOCATE_PRECISE) { 24 | snprintf(address_as_string, sizeof(address_as_string), "0x%016llX", 25 | spec->addr); 26 | } else { 27 | struct string_view any_address_str = SV(ANY_ADDRESS); 28 | memcpy(address_as_string, any_address_str.text, any_address_str.size + 1); 29 | } 30 | 31 | printlvl(lvl, 32 | "failed to satisfy an allocation at %s with %zu pages of type 0x%08X\n", 33 | address_as_string, spec->pages, type); 34 | 35 | if (is_critical) 36 | loader_abort(); 37 | } 38 | 39 | #ifdef MEM_DEBUG_SPRAY 40 | static void allocation_spray(u64 ptr, size_t pages) 41 | { 42 | size_t i, dwords; 43 | u32 *dword_ptr = ADDR_TO_PTR(ptr); 44 | 45 | if (page_range_outside_of_address_space(ptr, pages)) 46 | return; 47 | 48 | dwords = pages << (PAGE_SHIFT - 2); 49 | for (i = 0; i < dwords; ++i) 50 | dword_ptr[i] = 0xDEADBEEF; 51 | } 52 | #else 53 | static void allocation_spray(u64 ptr, size_t pages) 54 | { 55 | UNUSED(ptr); 56 | UNUSED(pages); 57 | } 58 | #endif 59 | 60 | u64 allocate_pages_ex(const struct allocation_spec *spec) 61 | { 62 | u64 result; 63 | u32 type = spec->type ?: ALLOCATOR_DEFAULT_ALLOC_TYPE; 64 | 65 | if (spec->flags & ALLOCATE_PRECISE) { 66 | result = ms_allocate_pages_at(spec->addr, spec->pages, type); 67 | } else { 68 | u64 ceiling = spec->ceiling ?: ALLOCATOR_DEFAULT_CEILING; 69 | result = ms_allocate_pages(spec->pages, ceiling, type); 70 | } 71 | 72 | if (!result) { 73 | allocation_did_fail(spec); 74 | return 0; 75 | } 76 | 77 | allocation_spray(result, spec->pages); 78 | 79 | if (spec->flags & ALLOCATE_STACK) 80 | result += ((u64)spec->pages) << PAGE_SHIFT; 81 | 82 | return result; 83 | } 84 | 85 | void free_pages(void *address, size_t count) 86 | { 87 | ms_free_pages((ptr_t)address, count); 88 | } 89 | 90 | void free_bytes(void *address, size_t count) 91 | { 92 | size_t page_count = PAGE_ROUND_UP(count) / PAGE_SIZE; 93 | free_pages(address, page_count); 94 | } 95 | -------------------------------------------------------------------------------- /loader/arch/aarch64/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | target_sources( 2 | ${LOADER_EXECUTABLE} 3 | PRIVATE 4 | handover.asm 5 | handover_impl.c 6 | elf.c 7 | virtual_memory.c 8 | ) 9 | 10 | add_loader_c_flags(-mgeneral-regs-only -mno-red-zone) 11 | 12 | target_include_directories( 13 | ${LOADER_EXECUTABLE} 14 | PRIVATE 15 | include 16 | ) 17 | 18 | if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${HYPER_PLATFORM}") 19 | add_subdirectory(${HYPER_PLATFORM}) 20 | endif () 21 | 22 | add_subdirectory(boot_protocol) 23 | -------------------------------------------------------------------------------- /loader/arch/aarch64/aarch64_handover.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef __ASSEMBLER__ 4 | #define handover_info_aarch64_arg0 0 5 | #define handover_info_aarch64_arg1 8 6 | #define handover_info_aarch64_entrypoint 16 7 | #define handover_info_aarch64_stack 24 8 | #define handover_info_aarch64_direct_map_base 32 9 | 10 | #define handover_info_aarch64_ttbr0 40 11 | #define handover_info_aarch64_ttbr1 48 12 | #define handover_info_aarch64_mair 56 13 | #define handover_info_aarch64_tcr 64 14 | #define handover_info_aarch64_sctlr 72 15 | 16 | #define handover_info_aarch64_unmap_lower_half 80 17 | 18 | #else 19 | #include "common/types.h" 20 | #include "common/attributes.h" 21 | 22 | // Make sure the macros above are aligned with these fields if changing them 23 | struct handover_info_aarch64 { 24 | u64 arg0, arg1; 25 | u64 entrypoint; 26 | u64 stack; 27 | u64 direct_map_base; 28 | 29 | // Same for all ELs 30 | u64 ttbr0, ttbr1; 31 | u64 mair, tcr; 32 | u64 sctlr; 33 | 34 | bool unmap_lower_half; 35 | }; 36 | 37 | NORETURN 38 | void kernel_handover_aarch64(struct handover_info_aarch64 *hia); 39 | 40 | u32 current_el(void); 41 | u64 read_id_aa64mmfr0_el1(void); 42 | u64 read_id_aa64mmfr1_el1(void); 43 | 44 | u64 read_hcr_el2(void); 45 | void write_hcr_el2(u64); 46 | #endif 47 | -------------------------------------------------------------------------------- /loader/arch/aarch64/boot_protocol/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | target_sources( 2 | ${LOADER_EXECUTABLE} 3 | PRIVATE 4 | ultra_impl.c 5 | ) 6 | -------------------------------------------------------------------------------- /loader/arch/aarch64/boot_protocol/ultra_impl.c: -------------------------------------------------------------------------------- 1 | #include "common/minmax.h" 2 | #include "handover.h" 3 | #include "boot_protocol/ultra_impl.h" 4 | 5 | #define AARCH64_HIGHER_HALF_BASE 0xFFFFFFFF80000000 6 | #define AARCH64_48BIT_DIRECT_MAP_BASE 0xFFFF000000000000 7 | #define AARCH64_52BIT_DIRECT_MAP_BASE 0xFFF0000000000000 8 | 9 | u32 ultra_get_flags_for_binary_options(struct binary_options *bo, 10 | enum elf_arch arch) 11 | { 12 | UNUSED(bo); 13 | UNUSED(arch); 14 | return 0; 15 | } 16 | 17 | u64 ultra_higher_half_base(u32 flags) 18 | { 19 | UNUSED(flags); 20 | return 0xFFFFFFFF80000000; 21 | } 22 | 23 | u64 ultra_higher_half_size(u32 flags) 24 | { 25 | return (0xFFFFFFFFFFFFFFFF - ultra_higher_half_base(flags)) + 1; 26 | } 27 | 28 | u64 ultra_direct_map_base(u32 flags) 29 | { 30 | if (flags & HO_AARCH64_52_BIT_IA) 31 | return AARCH64_52BIT_DIRECT_MAP_BASE; 32 | 33 | return AARCH64_48BIT_DIRECT_MAP_BASE; 34 | } 35 | 36 | u64 ultra_max_binary_address(u32 flags) 37 | { 38 | UNUSED(flags); 39 | 40 | // No known limitations 41 | return 0xFFFFFFFFFFFFFFFF; 42 | } 43 | 44 | bool ultra_should_map_high_memory(u32 flags) 45 | { 46 | UNUSED(flags); 47 | return true; 48 | } 49 | 50 | u64 ultra_adjust_direct_map_min_size(u64 direct_map_min_size, u32 flags) 51 | { 52 | UNUSED(flags); 53 | return MAX(direct_map_min_size, 4ull * GB); 54 | } 55 | 56 | u64 ultra_adjust_direct_map_min_size_for_lower_half(u64 direct_map_min_size, 57 | u32 flags) 58 | { 59 | UNUSED(flags); 60 | return direct_map_min_size; 61 | } 62 | 63 | bool ultra_configure_pt_type(struct handover_info *hi, u8 pt_levels, 64 | enum pt_constraint constraint, 65 | enum pt_type *out_type) 66 | { 67 | enum pt_type type = PT_TYPE_AARCH64_4K_GRANULE_48_BIT; 68 | 69 | if ((pt_levels == 5 || constraint == PT_CONSTRAINT_AT_LEAST) && 70 | handover_is_flag_supported(HO_AARCH64_52_BIT_IA)) 71 | { 72 | hi->flags |= HO_AARCH64_52_BIT_IA; 73 | type = PT_TYPE_AARCH64_4K_GRANULE_52_BIT; 74 | } 75 | 76 | if (pt_levels == 5 && type != PT_TYPE_AARCH64_4K_GRANULE_52_BIT && 77 | constraint != PT_CONSTRAINT_MAX) 78 | return false; 79 | 80 | *out_type = type; 81 | return true; 82 | } 83 | -------------------------------------------------------------------------------- /loader/arch/aarch64/elf.c: -------------------------------------------------------------------------------- 1 | #include "elf/machine.h" 2 | #include "elf/context.h" 3 | 4 | bool elf_machine_to_arch(Elf32_Half machine, 5 | enum elf_arch *out_arch, 6 | u8 *out_expected_ptr_width) 7 | { 8 | if (machine != EM_AARCH64) 9 | return false; 10 | 11 | *out_expected_ptr_width = 8; 12 | *out_arch = ELF_ARCH_AARCH64; 13 | return true; 14 | } 15 | 16 | bool elf_is_supported_load_ctx(struct elf_load_ctx *ctx) 17 | { 18 | return !(ctx->alloc_anywhere && !ctx->use_va); 19 | } 20 | -------------------------------------------------------------------------------- /loader/arch/aarch64/handover.asm: -------------------------------------------------------------------------------- 1 | #include "aarch64_handover.h" 2 | 3 | #define CURRENT_EL_EL_MASK #0b1100 4 | #define CURRENT_EL_1 #0b0100 5 | 6 | .text 7 | 8 | .global current_el 9 | current_el: 10 | mrs x0, currentel 11 | and x0, x0, CURRENT_EL_EL_MASK 12 | lsr x0, x0, 2 13 | ret 14 | 15 | .global read_id_aa64mmfr0_el1 16 | read_id_aa64mmfr0_el1: 17 | mrs x0, id_aa64mmfr0_el1 18 | ret 19 | 20 | .global read_id_aa64mmfr1_el1 21 | read_id_aa64mmfr1_el1: 22 | mrs x0, id_aa64mmfr1_el1 23 | ret 24 | 25 | .global read_hcr_el2 26 | read_hcr_el2: 27 | mrs x0, hcr_el2 28 | ret 29 | 30 | .global write_hcr_el2 31 | write_hcr_el2: 32 | msr hcr_el2, x0 33 | ret 34 | 35 | #define SCTLR_M 0b1 36 | #define SPSEL_ELX 0b1 37 | 38 | /* 39 | * TODO: Figure out the barrier spam mess here. 40 | * I don't know whether this order is correct or whether these are all 41 | * really necessary, but better safe than sorry. For now just spam 42 | * instruction and data barriers everywhere. 43 | */ 44 | 45 | .global kernel_handover_aarch64 46 | kernel_handover_aarch64: 47 | // Mask all interrupts 48 | msr daifset, #0b1111 49 | 50 | ldr x1, [x0, handover_info_aarch64_ttbr0] 51 | ldr x2, [x0, handover_info_aarch64_ttbr1] 52 | ldr x3, [x0, handover_info_aarch64_mair] 53 | ldr x4, [x0, handover_info_aarch64_tcr] 54 | ldr x5, [x0, handover_info_aarch64_sctlr] 55 | 56 | isb 57 | dsb nsh 58 | 59 | // Disable MMU while we mess with translation registers 60 | mrs x6, sctlr_el1 61 | and x6, x6, ~SCTLR_M 62 | msr sctlr_el1, x6 63 | 64 | isb 65 | dsb nsh 66 | 67 | msr mair_el1, x3 68 | msr TCR_el1, x4 69 | msr ttbr0_el1, x1 70 | msr ttbr1_el1, x2 71 | 72 | isb 73 | tlbi vmalle1 74 | dsb nsh 75 | 76 | // Finally enable the MMU again with the provided configuration 77 | msr sctlr_el1, x5 78 | 79 | isb 80 | dsb nsh 81 | 82 | ldr x3, [x0, handover_info_aarch64_stack] 83 | msr spsel, #SPSEL_ELX 84 | mov sp, x3 85 | 86 | ldr x30, [x0, handover_info_aarch64_entrypoint] 87 | 88 | ldr x3, [x0, handover_info_aarch64_direct_map_base] 89 | add x0, x0, x3 90 | adr x4, .higher_half 91 | add x3, x3, x4 92 | br x3 93 | 94 | .higher_half: 95 | ldrb w1, [x0, handover_info_aarch64_unmap_lower_half] 96 | cmp x1, #0 97 | beq .unmap_done 98 | 99 | msr ttbr0_el1, xzr 100 | tlbi vmalle1 101 | dsb nsh 102 | 103 | .unmap_done: 104 | ldr x1, [x0, handover_info_aarch64_arg1] 105 | ldr x0, [x0, handover_info_aarch64_arg0] 106 | 107 | mov x2, xzr 108 | mov x3, xzr 109 | mov x4, xzr 110 | mov x5, xzr 111 | mov x6, xzr 112 | mov x7, xzr 113 | mov x8, xzr 114 | mov x9, xzr 115 | mov x10, xzr 116 | mov x11, xzr 117 | mov x12, xzr 118 | mov x13, xzr 119 | mov x14, xzr 120 | mov x15, xzr 121 | mov x16, xzr 122 | mov x17, xzr 123 | mov x18, xzr 124 | mov x19, xzr 125 | mov x20, xzr 126 | mov x21, xzr 127 | mov x22, xzr 128 | mov x23, xzr 129 | mov x24, xzr 130 | mov x25, xzr 131 | mov x26, xzr 132 | mov x27, xzr 133 | mov x28, xzr 134 | mov x29, xzr 135 | 136 | msr nzcv, xzr 137 | ret 138 | -------------------------------------------------------------------------------- /loader/arch/aarch64/include/arch/constants.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define PAGE_SIZE 4096 4 | #define PAGE_SHIFT 12 5 | -------------------------------------------------------------------------------- /loader/arch/aarch64/include/arch/elf.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #define ARCH_STRUCT_VIEW(arch, data, type, action) \ 6 | (void)arch; \ 7 | const struct Elf64_##type *view = data; \ 8 | action 9 | -------------------------------------------------------------------------------- /loader/arch/aarch64/include/arch/handover_flags.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define HO_AARCH64_52_BIT_IA_BIT 28 4 | #define HO_AARCH64_52_BIT_IA (1 << HO_AARCH64_52_BIT_IA_BIT) 5 | -------------------------------------------------------------------------------- /loader/arch/aarch64/include/arch/virtual_memory.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common/types.h" 4 | 5 | extern u32 g_aarch64_access_flag_mask; 6 | 7 | #define PAGE_PRESENT (1ull << 0) 8 | 9 | /* 10 | * This is supposed to be an index into the APTable, but it's located at 11 | * different offsets depending on whether this is a table or a block 12 | * descriptor. We currently don't have such abstraction, so just hardcode 13 | * this to zero. 14 | */ 15 | #define PAGE_READWRITE (0) 16 | 17 | #define PAGE_AARCH64_BLOCK_OR_PAGE_DESCRIPTOR (0ull << 1) 18 | #define PAGE_AARCH64_TABLE_DESCRIPTOR (1ull << 1) 19 | #define PAGE_AARCH64_ACCESS_FLAG (1ull << 10) 20 | 21 | #define PAGE_NORMAL (PAGE_AARCH64_TABLE_DESCRIPTOR | \ 22 | g_aarch64_access_flag_mask) 23 | #define PAGE_HUGE (PAGE_AARCH64_BLOCK_OR_PAGE_DESCRIPTOR | \ 24 | g_aarch64_access_flag_mask) 25 | 26 | enum pt_type { 27 | PT_TYPE_AARCH64_4K_GRANULE_48_BIT = 4, 28 | PT_TYPE_AARCH64_4K_GRANULE_52_BIT = 5, 29 | }; 30 | 31 | static inline size_t pt_depth(enum pt_type pt) 32 | { 33 | return (size_t)pt; 34 | } 35 | 36 | static inline bool pt_is_huge_page(u64 entry) 37 | { 38 | return (entry & PAGE_AARCH64_TABLE_DESCRIPTOR) == 0; 39 | } 40 | -------------------------------------------------------------------------------- /loader/arch/aarch64/virtual_memory.c: -------------------------------------------------------------------------------- 1 | #include "common/bug.h" 2 | #include "common/string.h" 3 | #include "common/rw_helpers.h" 4 | 5 | #include "virtual_memory.h" 6 | #include "virtual_memory_impl.h" 7 | 8 | /* 9 | * We pretend TTBR0 & 1 are actually entries inside an extra page table level 10 | * for simplicity to make it more like x86. 11 | */ 12 | static u8 unified_pt_depth(enum pt_type type) 13 | { 14 | return pt_depth(type) + 1; 15 | } 16 | 17 | void page_table_init(struct page_table *pt, enum pt_type type, 18 | u64 max_table_address) 19 | { 20 | ptr_t root_page = pt_get_table_page(max_table_address); 21 | OOPS_ON(!root_page); 22 | 23 | pt->root = ADDR_TO_PTR(root_page); 24 | pt->levels = unified_pt_depth(type); 25 | pt->base_shift = PAGE_SHIFT; 26 | pt->max_table_address = max_table_address; 27 | 28 | // We currently don't support 52-bit OA, so this is the mask 29 | pt->entry_address_mask = ~(BIT_MASK(48, 64) | BIT_MASK(0, PAGE_SHIFT)); 30 | 31 | pt->entry_width = 8; 32 | pt->table_width_shift = 9; 33 | pt->write_slot = write_u64; 34 | pt->read_slot = read_u64; 35 | } 36 | 37 | #define LOOKUP_LEVEL_MINUS_1 4 38 | #define LOOKUP_LEVEL_MINUS_1_WIDTH_SHIFT 4 39 | 40 | u8 pt_table_width_shift_for_level(struct page_table *pt, size_t idx) 41 | { 42 | if (pt->levels == unified_pt_depth(PT_TYPE_AARCH64_4K_GRANULE_52_BIT) && 43 | idx == LOOKUP_LEVEL_MINUS_1) 44 | return LOOKUP_LEVEL_MINUS_1_WIDTH_SHIFT; 45 | 46 | return pt->table_width_shift; 47 | } 48 | -------------------------------------------------------------------------------- /loader/arch/x86/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | macro(add_loader_nasm_flags) 2 | add_loader_lang_flags(ASM_NASM ${ARGN}) 3 | endmacro() 4 | 5 | target_sources( 6 | ${LOADER_EXECUTABLE} 7 | PRIVATE 8 | handover_impl.c 9 | elf.c 10 | virtual_memory.c 11 | ) 12 | 13 | target_include_directories( 14 | ${LOADER_EXECUTABLE} 15 | PRIVATE 16 | include 17 | ) 18 | 19 | if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${HYPER_PLATFORM}") 20 | add_subdirectory(${HYPER_PLATFORM}) 21 | endif () 22 | 23 | add_subdirectory(boot_protocol) 24 | -------------------------------------------------------------------------------- /loader/arch/x86/bios/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | target_link_options( 2 | ${LOADER_EXECUTABLE} 3 | PRIVATE 4 | --oformat binary 5 | -T${CMAKE_CURRENT_SOURCE_DIR}/linker.ld 6 | -nostdlib 7 | --orphan-handling=error 8 | ) 9 | 10 | add_loader_nasm_flags(-felf32) 11 | 12 | add_loader_c_flags( 13 | -mno-80387 14 | -mno-mmx 15 | -mno-3dnow 16 | -mno-sse 17 | -mno-sse2 18 | ) 19 | 20 | target_sources( 21 | ${LOADER_EXECUTABLE} 22 | PRIVATE 23 | a20.asm 24 | bios_call.asm 25 | bios_entry.asm 26 | bios_handover.asm 27 | ) 28 | 29 | target_sources( 30 | ${LOADER_EXECUTABLE} 31 | PRIVATE 32 | apm.c 33 | bios_disk_services.c 34 | bios_entry.c 35 | bios_find.c 36 | bios_handover.c 37 | bios_memory_services.c 38 | bios_video_services.c 39 | ) 40 | 41 | add_subdirectory(boot_record) 42 | 43 | set(MBR_FULL_PATH "${MBR_PATH}/${MBR_BINARY}") 44 | set(ISO_MBR_FULL_PATH "${MBR_PATH}/${ISO_MBR_BINARY}") 45 | set(ISO_BOOT_REC_FULL_PATH "${MBR_PATH}/${ISO_BOOT_REC_BINARY}") 46 | set(STAGE2_FULL_PATH "${STAGE2_PATH}/${STAGE2_BINARY}") 47 | set(ISO_STAGE2_FULL_PATH "${STAGE2_PATH}/hyper_iso_boot") 48 | 49 | add_custom_command( 50 | OUTPUT 51 | ${ISO_STAGE2_FULL_PATH} 52 | COMMAND 53 | "${CMAKE_COMMAND}" -E cat ${ISO_BOOT_REC_FULL_PATH} ${STAGE2_FULL_PATH} > ${ISO_STAGE2_FULL_PATH} 54 | DEPENDS 55 | ${ISO_BOOT_REC_BINARY} ${STAGE2_BINARY} 56 | COMMENT 57 | "Generating iso stage2 loader" 58 | ) 59 | add_custom_target(hyper_iso_boot ALL DEPENDS ${ISO_STAGE2_FULL_PATH}) 60 | 61 | include(ExternalProject) 62 | ExternalProject_Add( 63 | installer 64 | SOURCE_DIR 65 | ${PROJECT_SOURCE_DIR}/installer 66 | BUILD_COMMAND 67 | "${CMAKE_MAKE_PROGRAM}" 68 | CMAKE_ARGS 69 | -DMBR_PATH=${MBR_FULL_PATH} 70 | -DISO_MBR_PATH=${ISO_MBR_FULL_PATH} 71 | -DSTAGE2_PATH=${STAGE2_FULL_PATH} 72 | -DCMAKE_MAKE_PROGRAM="${CMAKE_MAKE_PROGRAM}" 73 | # Don't inherit C compiler which may be tied to target rather than host. 74 | # There should ideally be a way to pass this through or otherwise request 75 | # A toolchain from the subproject. 76 | #-DCMAKE_C_COMPILER=${CMAKE_C_COMPILER} 77 | -DCMAKE_C_COMPILER_WORKS=1 78 | INSTALL_COMMAND 79 | cmake -E echo "Skipping install step" 80 | ) 81 | ExternalProject_Add_StepDependencies( 82 | installer 83 | build 84 | ${MBR_BINARY} 85 | ${ISO_MBR_BINARY} 86 | ${STAGE2_BINARY} 87 | ) 88 | -------------------------------------------------------------------------------- /loader/arch/x86/bios/apm.c: -------------------------------------------------------------------------------- 1 | #define MSG_FMT(msg) "BIOS-APM: " msg 2 | 3 | #include "common/format.h" 4 | #include "common/log.h" 5 | #include "bios_call.h" 6 | #include "services_impl.h" 7 | #include "apm.h" 8 | 9 | #define APM_SIGNATURE 0x504D 10 | #define APM_POWER_DEVICE_ID_APM_BIOS 0x0000 11 | 12 | #define APM_FLAG_32BIT_INTERFACE_SUPPORTED (1 << 1) 13 | 14 | #define APM_INT 0x15 15 | #define APM_CMD 0x53 16 | #define MAKE_APM_CMD(cmd) ((APM_CMD << 8) | (cmd)) 17 | 18 | #define APM_INSTALLATION_CHECK MAKE_APM_CMD(0x00) 19 | #define APM_PM32_INTERFACE_CONNECT MAKE_APM_CMD(0x03) 20 | #define APM_INTERFACE_DISCONNECT MAKE_APM_CMD(0x04) 21 | 22 | static bool check_apm_call( 23 | const struct real_mode_regs *in_regs, 24 | const struct real_mode_regs *out_regs 25 | ) 26 | { 27 | if (is_carry_set(out_regs)) { 28 | print_warn( 29 | "APM call 0x%04X failed: %d\n", 30 | in_regs->eax, (out_regs->eax & 0xFFFF) >> 8 31 | ); 32 | return false; 33 | } 34 | 35 | if (in_regs->eax == APM_INSTALLATION_CHECK) { 36 | u16 signature = out_regs->ebx & 0xFFFF; 37 | 38 | if (unlikely(signature != APM_SIGNATURE)) { 39 | print_warn("bad APM signature 0x%04X\n", signature); 40 | return false; 41 | } 42 | } 43 | 44 | return true; 45 | } 46 | 47 | bool services_setup_apm(struct apm_info *out_info) 48 | { 49 | struct real_mode_regs out_regs, in_regs = { 0 }; 50 | 51 | // All queries will be for the APM BIOS "device" 52 | in_regs.ebx = APM_POWER_DEVICE_ID_APM_BIOS; 53 | 54 | // 1. Check if APM exists at all 55 | in_regs.eax = APM_INSTALLATION_CHECK; 56 | bios_call(APM_INT, &in_regs, &out_regs); 57 | if (!check_apm_call(&in_regs, &out_regs)) 58 | return false; 59 | 60 | if (!(out_regs.ecx & APM_FLAG_32BIT_INTERFACE_SUPPORTED)) { 61 | print_warn("APM doesn't support 32-bit interface\n"); 62 | return false; 63 | } 64 | 65 | // 2. Disconnect if anything was connected previously, ignore return value 66 | in_regs.eax = APM_INTERFACE_DISCONNECT; 67 | bios_call(APM_INT, &in_regs, &out_regs); 68 | 69 | // 3. Connect the 32-bit interface 70 | in_regs.eax = APM_PM32_INTERFACE_CONNECT; 71 | bios_call(APM_INT, &in_regs, &out_regs); 72 | if (!check_apm_call(&in_regs, &out_regs)) 73 | return false; 74 | 75 | print_info("32-bit PM interface connected\n"); 76 | out_info->pm_code_segment = out_regs.eax & 0xFFFF; 77 | out_info->pm_code_segment_length = out_regs.esi & 0xFFFF; 78 | out_info->pm_offset = out_regs.ebx; 79 | 80 | out_info->rm_code_segment = out_regs.ecx & 0xFFFF; 81 | out_info->rm_code_segment_length = out_regs.esi >> 16; 82 | 83 | out_info->data_segment = out_regs.edx & 0xFFFF; 84 | out_info->data_segment_length = out_regs.edi & 0xFFFF; 85 | 86 | // 4. Recheck flags, as they might change after 32-bit interface install 87 | in_regs.eax = APM_INSTALLATION_CHECK; 88 | bios_call(APM_INT, &in_regs, &out_regs); 89 | if (unlikely(!check_apm_call(&in_regs, &out_regs))) { 90 | in_regs.eax = APM_INTERFACE_DISCONNECT; 91 | bios_call(APM_INT, &in_regs, &out_regs); 92 | return false; 93 | } 94 | 95 | out_info->version = out_regs.eax & 0xFFFF; 96 | out_info->flags = out_regs.ecx & 0xFFFF; 97 | return true; 98 | } 99 | -------------------------------------------------------------------------------- /loader/arch/x86/bios/bios_call.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common/types.h" 4 | #include "common/constants.h" 5 | #include "common/bug.h" 6 | 7 | struct real_mode_regs { 8 | u32 eax; 9 | u32 ebx; 10 | u32 ecx; 11 | u32 edx; 12 | u32 esi; 13 | u32 edi; 14 | u32 ebp; 15 | u16 gs; 16 | u16 fs; 17 | u16 es; 18 | u16 ds; 19 | u32 flags; 20 | }; 21 | 22 | struct real_mode_addr { 23 | u16 segment; 24 | u16 offset; 25 | }; 26 | 27 | static inline bool is_carry_set(const struct real_mode_regs *regs) 28 | { 29 | return regs->flags & (1 << 0); 30 | } 31 | 32 | static inline bool is_zero_set(const struct real_mode_regs *regs) 33 | { 34 | return regs->flags & (1 << 6); 35 | } 36 | 37 | u32 bios_read_bda(u16 offset, u8 width); 38 | 39 | NORETURN 40 | void bios_jmp_to_reset_vector(void); 41 | 42 | void bios_call(u32 number, const struct real_mode_regs *in, struct real_mode_regs *out); 43 | 44 | static inline void* from_real_mode_addr(u16 segment, u16 offset) 45 | { 46 | return (void*)(((u32)segment << 4) + offset); 47 | } 48 | 49 | static inline void as_real_mode_addr(ptr_t addr, struct real_mode_addr *rm_addr) 50 | { 51 | BUG_ON(addr > (1 * MB)); 52 | 53 | *rm_addr = (struct real_mode_addr) { 54 | .offset = addr & 0xF, 55 | .segment = (addr & 0xFFFF0) >> 4 56 | }; 57 | } 58 | -------------------------------------------------------------------------------- /loader/arch/x86/bios/bios_disk_services.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | void bios_disk_services_init(void); 4 | -------------------------------------------------------------------------------- /loader/arch/x86/bios/bios_entry.asm: -------------------------------------------------------------------------------- 1 | extern enable_a20 2 | extern bios_entry 3 | 4 | STAGE2_LOAD_BASE: equ 0x00007E00 5 | STAGE2_STACK_END: equ STAGE2_LOAD_BASE 6 | STAGE2_STACK_BEGIN: equ 0x00000500 7 | 8 | section .entry 9 | 10 | stage2_magic: db "HyperST2" 11 | 12 | ; pad to 16 13 | dq 0 14 | 15 | main: 16 | BITS 16 17 | mov sp, STAGE2_LOAD_BASE 18 | cld 19 | 20 | call enable_a20 21 | mov [a20_enabled], al 22 | cli 23 | 24 | lgdt [gdt_ptr] 25 | 26 | PROTECTED_MODE_BIT: equ (1 << 0) 27 | mov eax, cr0 28 | or eax, PROTECTED_MODE_BIT 29 | mov cr0, eax 30 | 31 | jmp gdt_struct.code32:.protected_mode 32 | 33 | .protected_mode: 34 | BITS 32 35 | 36 | mov ax, gdt_struct.data32 37 | mov ds, ax 38 | mov es, ax 39 | mov fs, ax 40 | mov gs, ax 41 | mov ss, ax 42 | 43 | %ifdef STACK_DEBUG_SPRAY 44 | mov eax, 0xCAFEBABE 45 | mov ecx, (STAGE2_STACK_END - STAGE2_STACK_BEGIN) / 4 46 | mov edi, STAGE2_STACK_BEGIN 47 | rep stosd 48 | %endif 49 | 50 | call bios_entry 51 | 52 | align 16 53 | section .real_data 54 | 55 | global gdt_ptr 56 | gdt_ptr: 57 | dw gdt_struct_end - gdt_struct - 1 58 | dd gdt_struct 59 | 60 | ; access 61 | READWRITE: equ (1 << 1) 62 | EXECUTABLE: equ (1 << 3) 63 | CODE_OR_DATA: equ (1 << 4) 64 | PRESENT: equ (1 << 7) 65 | 66 | ; flags 67 | MODE_32BIT: equ (1 << 6) 68 | MODE_64BIT: equ (1 << 5) 69 | PAGE_GRANULARITY: equ (1 << 7) 70 | 71 | align 16 72 | gdt_struct: 73 | .null: equ $ - gdt_struct 74 | dq 0x0000000000000000 75 | 76 | .code32: equ $ - gdt_struct 77 | ; 32 bit code segment descriptor 78 | dw 0xFFFF ; limit 79 | dw 0x0000 ; base 80 | db 0x00 ; base 81 | db READWRITE | EXECUTABLE | CODE_OR_DATA | PRESENT 82 | db MODE_32BIT | PAGE_GRANULARITY | 0x0F ; 4 bits of flags + 4 bits of limit 83 | db 0x00 ; base 84 | 85 | .data32: equ $ - gdt_struct 86 | ; 32 bit data segment descriptor 87 | dw 0xFFFF ; limit 88 | dw 0x0000 ; base 89 | db 0x00 ; base 90 | db READWRITE | CODE_OR_DATA | PRESENT 91 | db MODE_32BIT | PAGE_GRANULARITY | 0x0F ; 4 bits of flags + 4 bits of limit 92 | db 0x00 ; base 93 | 94 | .code16: equ $ - gdt_struct 95 | ; 16 bit code segment descriptor 96 | dw 0xFFFF ; limit 97 | dw 0x0000 ; base 98 | db 0x00 ; base 99 | db READWRITE | EXECUTABLE | CODE_OR_DATA | PRESENT 100 | db 0x00 ; byte granularity 101 | db 0x00 ; base 102 | 103 | .data16: equ $ - gdt_struct 104 | ; 16 bit data segment descriptor 105 | dw 0xFFFF ; limit 106 | dw 0x0000 ; base 107 | db 0x00 ; base 108 | db READWRITE | CODE_OR_DATA | PRESENT 109 | db 0x00 ; byte granularity 110 | db 0x00 ; base 111 | 112 | .code64: equ $ - gdt_struct 113 | ; 64 bit code segment descriptor 114 | dw 0xFFFF ; limit 115 | dw 0x0000 ; base 116 | db 0x00 ; base 117 | db READWRITE | EXECUTABLE | CODE_OR_DATA | PRESENT 118 | db MODE_64BIT | PAGE_GRANULARITY | 0x0F ; 4 bits of flags + 4 bits of limit 119 | db 0x00 ; base 120 | gdt_struct_end: 121 | 122 | global a20_enabled 123 | a20_enabled: db 0 124 | -------------------------------------------------------------------------------- /loader/arch/x86/bios/bios_entry.c: -------------------------------------------------------------------------------- 1 | #include "common/log.h" 2 | #include "common/panic.h" 3 | #include "common/string.h" 4 | #include "services.h" 5 | 6 | #include "bios_memory_services.h" 7 | #include "bios_video_services.h" 8 | #include "bios_disk_services.h" 9 | #include "bios_call.h" 10 | 11 | extern u8 a20_enabled; 12 | extern u8 section_bss_begin[]; 13 | extern u8 section_bss_end[]; 14 | 15 | enum service_provider services_get_provider(void) 16 | { 17 | return SERVICE_PROVIDER_BIOS; 18 | } 19 | 20 | void loader_abort(void) 21 | { 22 | /* 23 | * INT 0x16, AH = 0x01 24 | * https://oldlinux.superglobalmegacorp.com/Linux.old/docs/interrupts/int-html/rb-1755.htm 25 | * 26 | * INT 0x16, AH = 0x00 27 | * https://oldlinux.superglobalmegacorp.com/Linux.old/docs/interrupts/int-html/rb-1754.htm 28 | */ 29 | 30 | struct real_mode_regs regs = { 0 }; 31 | 32 | for (;;) { 33 | // Any keystrokes pending? 34 | regs.eax = 0x0100; 35 | regs.flags = 0; 36 | bios_call(0x16, ®s, ®s); 37 | 38 | if (is_zero_set(®s)) 39 | break; 40 | 41 | // Pop one keystroke 42 | regs.eax = 0; 43 | bios_call(0x16, ®s, ®s); 44 | } 45 | 46 | print_err("Loading aborted! Press any key to reboot...\n"); 47 | 48 | regs.eax = 0; 49 | bios_call(0x16, ®s, ®s); 50 | 51 | bios_jmp_to_reset_vector(); 52 | } 53 | 54 | void bios_entry(void) 55 | { 56 | memzero(section_bss_begin, section_bss_end - section_bss_begin); 57 | 58 | bios_video_services_init(); 59 | BUG_ON(!a20_enabled); 60 | 61 | bios_memory_services_init(); 62 | bios_disk_services_init(); 63 | 64 | loader_entry(); 65 | } 66 | -------------------------------------------------------------------------------- /loader/arch/x86/bios/bios_find.c: -------------------------------------------------------------------------------- 1 | #define MSG_FMT(msg) "BIOS-TBL: " msg 2 | 3 | #include "common/constants.h" 4 | #include "common/string.h" 5 | #include "common/log.h" 6 | #include "services.h" 7 | #include "bios_call.h" 8 | 9 | #define RSDP_SIGNATURE "RSD PTR " 10 | #define RSDP_SIGNATURE_LEN 8 11 | #define RSDP_ALIGNMENT 16 12 | 13 | /* 14 | * ACPI 6.4 (5.2.5.1 Finding the RSDP on IA-PC Systems) 15 | * ---------------------------------------------------------------------------------------------------- 16 | * OSPM finds the Root System Description Pointer (RSDP) structure by searching physical memory ranges 17 | * on 16-byte boundaries for a valid Root System Description Pointer structure signature and checksum 18 | * match as follows: 19 | * - The first 1 KB of the Extended BIOS Data Area (EBDA). For EISA or MCA systems, the EBDA can 20 | * be found in the two-byte location 40:0Eh on the BIOS data area. 21 | * - The BIOS read-only memory space between 0E0000h and 0FFFFFh. 22 | * ---------------------------------------------------------------------------------------------------- 23 | */ 24 | 25 | // contains (ebda_base >> 4), aka segment value 26 | #define BDA_EBDA_POINTER_OFFSET 0x0E 27 | 28 | #define EBDA_SEARCH_BASE 0x00400 29 | #define BIOS_AREA_SEARCH_BASE 0xE0000 30 | #define BIOS_AREA_SEARCH_END 0xFFFFF 31 | 32 | #define EBDA_SEARCH_SIZE (1 * KB) 33 | 34 | static u32 find_signature_in_range( 35 | const char *signature, size_t length, u32 align, u32 addr, u32 end 36 | ) 37 | { 38 | // Don't attempt to search too low 39 | if (addr <= EBDA_SEARCH_BASE) 40 | return 0; 41 | 42 | for (; addr < end; addr += align) { 43 | if (memcmp((void*)addr, signature, length) != 0) 44 | continue; 45 | 46 | return addr; 47 | } 48 | 49 | return 0; 50 | } 51 | 52 | ptr_t services_find_rsdp(void) 53 | { 54 | u32 address, ebda_address; 55 | 56 | ebda_address = bios_read_bda(BDA_EBDA_POINTER_OFFSET, 2); 57 | ebda_address <<= 4; 58 | 59 | address = find_signature_in_range( 60 | RSDP_SIGNATURE, RSDP_SIGNATURE_LEN, RSDP_ALIGNMENT, 61 | ebda_address, ebda_address + EBDA_SEARCH_SIZE 62 | ); 63 | if (address == 0) { 64 | address = find_signature_in_range( 65 | RSDP_SIGNATURE, RSDP_SIGNATURE_LEN, RSDP_ALIGNMENT, 66 | BIOS_AREA_SEARCH_BASE, BIOS_AREA_SEARCH_END 67 | ); 68 | } 69 | 70 | if (address) 71 | print_info("found RSDP at 0x%08X\n", address); 72 | 73 | return address; 74 | } 75 | 76 | ptr_t services_find_dtb(void) 77 | { 78 | return 0; 79 | } 80 | 81 | /* 82 | * On non-UEFI systems, the 32-bit SMBIOS Entry Point structure, can be located 83 | * by application software by searching for the anchor-string on paragraph 84 | * (16-byte) boundaries within the physical memory address range 000F0000h to 85 | * 000FFFFFh. 86 | */ 87 | #define SMBIOS_RANGE_BEGIN 0x000F0000 88 | #define SMBIOS_RANGE_END 0x000FFFFF 89 | #define SMBIOS_ALIGNMENT 16 90 | 91 | #define SMBIOS_2_ANCHOR_STRING "_SM_" 92 | #define SMBIOS_2_ANCHOR_STRING_LENGTH 4 93 | 94 | #define SMBIOS_3_ANCHOR_STRING "_SM3_" 95 | #define SMBIOS_3_ANCHOR_STRING_LENGTH 5 96 | 97 | ptr_t services_find_smbios(void) 98 | { 99 | u8 bitness = 64; 100 | u32 address; 101 | 102 | address = find_signature_in_range( 103 | SMBIOS_3_ANCHOR_STRING, SMBIOS_3_ANCHOR_STRING_LENGTH, 104 | SMBIOS_ALIGNMENT, SMBIOS_RANGE_BEGIN, SMBIOS_RANGE_END 105 | ); 106 | if (address == 0) { 107 | address = find_signature_in_range( 108 | SMBIOS_2_ANCHOR_STRING, SMBIOS_2_ANCHOR_STRING_LENGTH, 109 | SMBIOS_ALIGNMENT, SMBIOS_RANGE_BEGIN, SMBIOS_RANGE_END 110 | ); 111 | bitness = 32; 112 | } 113 | 114 | if (address) { 115 | print_info("found (%d-bit) SMBIOS entry at 0x%08X\n", 116 | bitness, address); 117 | } 118 | 119 | return address; 120 | } 121 | -------------------------------------------------------------------------------- /loader/arch/x86/bios/bios_handover.asm: -------------------------------------------------------------------------------- 1 | section .text 2 | 3 | EFER_NUMBER: equ 0xC0000080 4 | LONG_MODE_BIT: equ (1 << 8) 5 | PAGING_BIT: equ (1 << 31) 6 | LONG_MODE_CODE_SELECTOR: equ 0x28 7 | EFLAGS_RESERVED_BIT: equ (1 << 1) 8 | 9 | struc x86_handover_info 10 | .arg0: resq 1 11 | .arg1: resq 1 12 | 13 | .entrypoint: resq 1 14 | .stack: resq 1 15 | .direct_map_base: resq 1 16 | .cr3: resd 1 17 | .is_long_mode: resb 1 18 | .unmap_lower_half: resb 1 19 | .is_pae: resb 1 20 | endstruc 21 | 22 | ; NORETURN 23 | ; void kernel_handover_x86(struct x86_handover_info *info) 24 | global kernel_handover_x86 25 | kernel_handover_x86: 26 | mov ebx, [esp + 4] 27 | 28 | mov eax, [ebx + x86_handover_info.cr3] 29 | mov cr3, eax 30 | 31 | mov al, [ebx + x86_handover_info.is_long_mode] 32 | test al, al 33 | jz do_kernel_handover_i386 34 | 35 | ; ============================ amd64 handover code ============================ 36 | do_kernel_handover_amd64: 37 | mov ecx, EFER_NUMBER 38 | rdmsr 39 | or eax, LONG_MODE_BIT 40 | wrmsr 41 | 42 | mov eax, cr0 43 | or eax, PAGING_BIT 44 | mov cr0, eax 45 | 46 | jmp LONG_MODE_CODE_SELECTOR:.long_mode 47 | 48 | BITS 64 49 | .long_mode: 50 | mov rax, [rbx + x86_handover_info.direct_map_base] 51 | add rax, .higher_half 52 | jmp rax 53 | 54 | .higher_half: 55 | mov rdi, [rbx + x86_handover_info.arg0] 56 | mov rsi, [rbx + x86_handover_info.arg1] 57 | 58 | mov rsp, [rbx + x86_handover_info.stack] 59 | mov r8, [rbx + x86_handover_info.entrypoint] 60 | 61 | mov al, [rbx + x86_handover_info.unmap_lower_half] 62 | test al, al 63 | jz .unmap_done 64 | 65 | ; unmap lower half 66 | mov rax, cr3 67 | mov rcx, [rbx + x86_handover_info.direct_map_base] 68 | add rcx, rax 69 | mov qword [rcx], 0x0000000000000000 70 | mov cr3, rax 71 | 72 | .unmap_done: 73 | push qword 0x0000000000000000 ; fake ret address 74 | push r8 ; kernel entry 75 | 76 | xor rax, rax 77 | xor rcx, rcx 78 | xor rdx, rdx 79 | xor rbx, rbx 80 | xor rbp, rbp 81 | xor r8, r8 82 | xor r9, r9 83 | xor r10, r10 84 | xor r11, r11 85 | xor r12, r12 86 | xor r13, r13 87 | xor r14, r14 88 | xor r15, r15 89 | 90 | push qword 0x0000000000000000 | EFLAGS_RESERVED_BIT 91 | popfq 92 | 93 | ret 94 | 95 | BITS 32 96 | ; ============================ i386 handover code ============================ 97 | do_kernel_handover_i386: 98 | mov eax, cr0 99 | or eax, PAGING_BIT 100 | mov cr0, eax 101 | 102 | mov eax, [ebx + x86_handover_info.direct_map_base] 103 | add ebx, eax 104 | add eax, .higher_half 105 | jmp eax 106 | 107 | .higher_half: 108 | mov dl, [ebx + x86_handover_info.unmap_lower_half] 109 | test dl, dl 110 | jz .unmap_done 111 | 112 | ; Transform bytes into 4MiB pages 113 | shr eax, 22 114 | 115 | mov dl, [ebx + x86_handover_info.is_pae] 116 | test dl, dl 117 | jz .not_pae 118 | 119 | ; Transform 4MiB pages into 512MiB pages 120 | ; (actually 1GiB, but the loop below unmaps in 4 byte strides) 121 | ; NOTE: this expects direct_map_base to be GiB-aligned 122 | shr eax, 7 123 | 124 | .not_pae: 125 | mov ecx, eax 126 | mov eax, cr3 127 | add eax, [ebx + x86_handover_info.direct_map_base] 128 | 129 | .unmap_one: 130 | mov dword [eax], 0x00000000 131 | add eax, 4 132 | 133 | dec ecx 134 | jnz .unmap_one 135 | 136 | .reload_cr3: 137 | mov eax, cr3 138 | mov cr3, eax 139 | 140 | .unmap_done: 141 | mov esp, [ebx + x86_handover_info.stack] 142 | 143 | ; SysV ABI alignment 144 | push dword 0x00000000 145 | push dword 0x00000000 146 | 147 | push dword [ebx + x86_handover_info.arg1] 148 | push dword [ebx + x86_handover_info.arg0] 149 | 150 | push dword 0x00000000 ; fake ret address 151 | push dword [ebx + x86_handover_info.entrypoint] 152 | 153 | xor eax, eax 154 | xor ecx, ecx 155 | xor edx, edx 156 | xor ebx, ebx 157 | xor ebp, ebp 158 | xor esi, esi 159 | xor edi, edi 160 | 161 | push dword 0x00000000 | EFLAGS_RESERVED_BIT 162 | popfd 163 | 164 | ret 165 | -------------------------------------------------------------------------------- /loader/arch/x86/bios/bios_handover.c: -------------------------------------------------------------------------------- 1 | #include "common/constants.h" 2 | #include "handover.h" 3 | #include "bios_call.h" 4 | #include "services_impl.h" 5 | 6 | void handover_prepare_for(struct handover_info *hi) 7 | { 8 | UNUSED(hi); 9 | } 10 | 11 | static void cr4_prepare(struct handover_info *hi) 12 | { 13 | u32 cr4 = handover_flags_to_cr4(hi->flags); 14 | asm volatile("mov %0, %%cr4" ::"r"(cr4)); 15 | } 16 | 17 | static struct x86_handover_info { 18 | u64 arg0, arg1; 19 | u64 entrypoint; 20 | u64 stack; 21 | u64 direct_map_base; 22 | u32 cr3; 23 | 24 | bool is_long_mode; 25 | bool unmap_lower_half; 26 | bool is_pae; 27 | } handover_info; 28 | 29 | NORETURN 30 | void kernel_handover_x86(struct x86_handover_info *info); 31 | 32 | NORETURN 33 | void kernel_handover(struct handover_info *hi) 34 | { 35 | cr4_prepare(hi); 36 | 37 | if (hi->flags & HO_X86_LME) { 38 | handover_info.is_long_mode = true; 39 | 40 | /* 41 | * AMD Hammer Family Processor BIOS and Kernel Developer’s Guide 42 | * 12.21 Detect Target Operating Mode Callback 43 | * --------------------------------------------------------------------- 44 | * The operating system notifies the BIOS what the expected operating 45 | * mode is with the Detect Target Operating Mode callback (INT 15, 46 | * function EC00h). Based on the target operating mode, the BIOS can 47 | * enable or disable mode specific performance and functional 48 | * optimizations that are not visible to system software. 49 | */ 50 | struct real_mode_regs regs = {}; 51 | regs.eax = 0xEC00; 52 | regs.ebx = 0x02; 53 | bios_call(0x15, ®s, ®s); 54 | } 55 | 56 | handover_info.arg0 = hi->arg0; 57 | handover_info.arg1 = hi->arg1; 58 | handover_info.entrypoint = hi->entrypoint; 59 | handover_info.stack = hi->stack; 60 | handover_info.direct_map_base = hi->direct_map_base; 61 | handover_info.cr3 = pt_get_root(&hi->pt); 62 | handover_info.unmap_lower_half = hi->flags & HO_HIGHER_HALF_ONLY; 63 | handover_info.is_pae = hi->flags & HO_X86_PAE; 64 | 65 | kernel_handover_x86(&handover_info); 66 | } 67 | -------------------------------------------------------------------------------- /loader/arch/x86/bios/bios_memory_services.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common/types.h" 4 | 5 | void bios_memory_services_init(void); 6 | 7 | bool bios_memory_services_check_key(size_t key); 8 | -------------------------------------------------------------------------------- /loader/arch/x86/bios/bios_video_services.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | void bios_video_services_init(void); 4 | -------------------------------------------------------------------------------- /loader/arch/x86/bios/boot_record/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | 3 | project(BootRecord ASM_NASM) 4 | 5 | set(CMAKE_ASM_NASM_FLAGS -fbin) 6 | 7 | # Prevent cmake from attempting to link the flat binary 8 | set(CMAKE_ASM_NASM_LINK_EXECUTABLE 9 | "\"${CMAKE_COMMAND}\" -E copy \"\" \"\"") 10 | 11 | add_executable(hyper_mbr boot_record.asm) 12 | 13 | add_executable(hyper_iso_mbr boot_record.asm) 14 | target_compile_definitions(hyper_iso_mbr PUBLIC HYPER_ISO_MBR) 15 | 16 | add_executable(hyper_iso_boot_rec boot_record.asm) 17 | target_compile_definitions(hyper_iso_boot_rec PUBLIC HYPER_ISO_BOOT_RECORD) 18 | 19 | set(MBR_BINARY hyper_mbr PARENT_SCOPE) 20 | set(ISO_MBR_BINARY hyper_iso_mbr PARENT_SCOPE) 21 | set(ISO_BOOT_REC_BINARY hyper_iso_boot_rec PARENT_SCOPE) 22 | set(MBR_PATH ${PROJECT_BINARY_DIR} PARENT_SCOPE) 23 | -------------------------------------------------------------------------------- /loader/arch/x86/bios/linker.ld: -------------------------------------------------------------------------------- 1 | EBDA_BEGIN = 0x00080000; 2 | STAGE2_LOAD_BASE = 0x00007E00; 3 | REAL_MODE_SEGMENT0_END = 0x00010000; 4 | 5 | SECTIONS 6 | { 7 | . = STAGE2_LOAD_BASE; 8 | 9 | .entry : { 10 | *(.entry*) 11 | } 12 | 13 | /* make sure all data needed by real mode is within the first 64K */ 14 | .real : { 15 | *(.real_code*) 16 | *(.real_data*) 17 | ASSERT(. < REAL_MODE_SEGMENT0_END, "Real mode section is too large"); 18 | } 19 | 20 | .text : { 21 | *(.text .text.*) 22 | } 23 | 24 | .rodata : { 25 | *(.rodata .rodata.*) 26 | } 27 | 28 | .boot_protocols : { 29 | boot_protocols_begin = .; 30 | *(.boot_protocols) 31 | boot_protocols_end = .; 32 | } 33 | 34 | .filesystems : { 35 | filesystems_begin = .; 36 | *(.filesystems) 37 | filesystems_end = .; 38 | } 39 | 40 | .cleanup_handlers : { 41 | cleanup_handlers_begin = .; 42 | *(.cleanup_handlers) 43 | cleanup_handlers_end = .; 44 | } 45 | 46 | .data : { 47 | *(.data .data.*) 48 | } 49 | 50 | ASSERT((. - STAGE2_LOAD_BASE) < 128K, "Looks like stage2 is now bigger than 256 sectors, please correct the STAGE2_BYTES_TO_LOAD constant in the MBR") 51 | 52 | .bss : { 53 | section_bss_begin = .; 54 | *(COMMON) 55 | *(.bss .bss.*) 56 | section_bss_end = .; 57 | 58 | ASSERT(. < EBDA_BEGIN, "Loader executable is too large"); 59 | } 60 | 61 | /* 62 | * Must be a separate section because of "section type mismatch for ..." 63 | */ 64 | .rel_sections : { 65 | *(.rel.plt) 66 | *(.rel.dyn) 67 | } 68 | 69 | .gotplt_sections : { 70 | *(.got) 71 | *(.got.plt) 72 | *(.plt) 73 | *(.iplt) 74 | } 75 | 76 | ASSERT(SIZEOF(.gotplt_sections) == 0 && SIZEOF(.rel_sections) == 0, 77 | "Relocation sections are not empty, recheck build flags") 78 | 79 | .symtab : { *(.symtab) } 80 | .strtab : { *(.strtab) } 81 | .shstrtab : { *(.shstrtab) } 82 | .symtab_shndx : { *(.symtab_shndx) } 83 | ASSERT(SIZEOF(.symtab_shndx) == 0, "Too many sections(?)") 84 | 85 | /DISCARD/ : { 86 | *(.comment*) 87 | *(.eh_frame) 88 | *(.debug*) 89 | *(.note.*) 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /loader/arch/x86/boot_protocol/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | target_sources( 2 | ${LOADER_EXECUTABLE} 3 | PRIVATE 4 | ultra_impl.c 5 | ) 6 | -------------------------------------------------------------------------------- /loader/arch/x86/boot_protocol/ultra_impl.c: -------------------------------------------------------------------------------- 1 | #define MSG_FMT(msg) "ULTRA-PROT-X86: " msg 2 | 3 | #include "common/minmax.h" 4 | #include "virtual_memory.h" 5 | #include "handover.h" 6 | #include "memory_services.h" 7 | #include "boot_protocol/ultra_impl.h" 8 | 9 | #define AMD64_HIGHER_HALF_BASE 0xFFFFFFFF80000000 10 | #define I686_HIGHER_HALF_BASE 0xC0000000 11 | 12 | #define AMD64_DIRECT_MAP_BASE 0xFFFF800000000000 13 | #define AMD64_LA57_DIRECT_MAP_BASE 0xFF00000000000000 14 | #define I686_DIRECT_MAP_BASE I686_HIGHER_HALF_BASE 15 | 16 | u64 ultra_higher_half_base(u32 flags) 17 | { 18 | if (flags & HO_X86_LME) 19 | return AMD64_HIGHER_HALF_BASE; 20 | 21 | return I686_HIGHER_HALF_BASE; 22 | } 23 | 24 | u64 ultra_higher_half_size(u32 flags) 25 | { 26 | u64 hh = ultra_higher_half_base(flags); 27 | u64 max_addr = 4ull * GB; 28 | 29 | if (flags & HO_X86_LME) 30 | max_addr = 0xFFFFFFFFFFFFFFFF; 31 | 32 | return (max_addr - hh) + 1; 33 | } 34 | 35 | u64 ultra_direct_map_base(u32 flags) 36 | { 37 | if (flags & HO_X86_LME) { 38 | if (flags & HO_X86_LA57) 39 | return AMD64_LA57_DIRECT_MAP_BASE; 40 | 41 | return AMD64_DIRECT_MAP_BASE; 42 | } 43 | 44 | return I686_DIRECT_MAP_BASE; 45 | } 46 | 47 | u64 ultra_max_binary_address(u32 flags) 48 | { 49 | if (flags & HO_X86_LME) { 50 | #ifdef __i386__ 51 | return (4ull * GB); 52 | #else 53 | // No known limitations 54 | return 0xFFFFFFFFFFFFFFFF; 55 | #endif 56 | } 57 | 58 | // Must be accessible from the higher half 59 | return (4ull * GB) - I686_DIRECT_MAP_BASE; 60 | } 61 | 62 | bool ultra_should_map_high_memory(u32 flags) 63 | { 64 | return flags & HO_X86_LME; 65 | } 66 | 67 | u32 ultra_get_flags_for_binary_options(struct binary_options *bo, 68 | enum elf_arch arch) 69 | { 70 | if (arch == ELF_ARCH_I386) { 71 | if (bo->allocate_anywhere) 72 | oops("allocate-anywhere is only allowed for 64 bit kernels\n"); 73 | 74 | return 0; 75 | } 76 | 77 | return HO_X86_LME; 78 | } 79 | 80 | bool ultra_configure_pt_type(struct handover_info *hi, u8 pt_levels, 81 | enum pt_constraint constraint, 82 | enum pt_type *out_type) 83 | { 84 | enum pt_type type; 85 | 86 | if (handover_is_flag_supported(HO_X86_PSE)) 87 | hi->flags |= HO_X86_PSE; 88 | 89 | if (hi->flags & HO_X86_LME) { 90 | hi->flags |= HO_X86_PAE; 91 | type = PT_TYPE_AMD64_4LVL; 92 | 93 | if ((pt_levels == 5 || constraint == PT_CONSTRAINT_AT_LEAST) && 94 | handover_is_flag_supported(HO_X86_LA57)) 95 | { 96 | hi->flags |= HO_X86_LA57; 97 | type = PT_TYPE_AMD64_5LVL; 98 | } 99 | 100 | if (pt_levels == 5 && type != PT_TYPE_AMD64_5LVL && 101 | constraint != PT_CONSTRAINT_MAX) 102 | return false; 103 | } else { 104 | type = PT_TYPE_I386_NO_PAE; 105 | 106 | if ((pt_levels == 3 || constraint == PT_CONSTRAINT_AT_LEAST) && 107 | handover_is_flag_supported(HO_X86_PAE)) 108 | { 109 | hi->flags |= HO_X86_PAE; 110 | type = PT_TYPE_I386_PAE; 111 | } 112 | 113 | if (pt_levels == 3 && type != PT_TYPE_I386_PAE && 114 | constraint != PT_CONSTRAINT_MAX) 115 | return false; 116 | } 117 | 118 | *out_type = type; 119 | return true; 120 | } 121 | 122 | u64 ultra_adjust_direct_map_min_size(u64 direct_map_min_size, u32 flags) 123 | { 124 | if (!(flags & HO_X86_LME)) { 125 | u64 ret = (4ull * GB) - I686_DIRECT_MAP_BASE; 126 | BUG_ON(ret < direct_map_min_size); 127 | return ret; 128 | } 129 | 130 | return MAX(direct_map_min_size, 4ull * GB); 131 | } 132 | 133 | u64 ultra_adjust_direct_map_min_size_for_lower_half(u64 direct_map_min_size, 134 | u32 flags) 135 | { 136 | if (flags & HO_X86_LME) 137 | return direct_map_min_size; 138 | 139 | return I686_DIRECT_MAP_BASE; 140 | } 141 | -------------------------------------------------------------------------------- /loader/arch/x86/elf.c: -------------------------------------------------------------------------------- 1 | #include "elf/machine.h" 2 | #include "elf/context.h" 3 | 4 | bool elf_machine_to_arch(Elf32_Half machine, enum elf_arch *out_arch, 5 | u8 *out_expected_ptr_width) 6 | { 7 | bool out = true; 8 | 9 | switch (machine) { 10 | case EM_386: 11 | *out_expected_ptr_width = 4; 12 | *out_arch = ELF_ARCH_I386; 13 | break; 14 | case EM_AMD64: 15 | *out_expected_ptr_width = 8; 16 | *out_arch = ELF_ARCH_AMD64; 17 | break; 18 | default: 19 | out = false; 20 | } 21 | 22 | return out; 23 | } 24 | 25 | bool elf_is_supported_load_ctx(struct elf_load_ctx *ctx) 26 | { 27 | struct elf_binary_info *info = ctx->bi; 28 | bool ret = false; 29 | 30 | switch (info->arch) { 31 | case ELF_ARCH_I386: 32 | ret = !ctx->alloc_anywhere; 33 | break; 34 | case ELF_ARCH_AMD64: 35 | ret = !(ctx->alloc_anywhere && !ctx->use_va); 36 | break; 37 | default: 38 | break; 39 | } 40 | 41 | return ret; 42 | } 43 | -------------------------------------------------------------------------------- /loader/arch/x86/handover_impl.c: -------------------------------------------------------------------------------- 1 | #include "common/align.h" 2 | #include "common/string_view.h" 3 | 4 | #include "handover.h" 5 | 6 | struct cpuid_res { 7 | u32 a; 8 | u32 b; 9 | u32 c; 10 | u32 d; 11 | }; 12 | 13 | void cpuid(u32 function, struct cpuid_res *id) 14 | { 15 | asm volatile("cpuid" 16 | : "=a"(id->a), "=b"(id->b), "=c"(id->c), "=d"(id->d) 17 | : "a"(function), "c"(0)); 18 | } 19 | 20 | 21 | static u64 get_i686_higher_half_length(u64 direct_map_base) 22 | { 23 | BUG_ON(!direct_map_base || !IS_ALIGNED(direct_map_base, GB)); 24 | return (4ull * GB) - direct_map_base; 25 | } 26 | 27 | u64 handover_get_minimum_map_length(u64 direct_map_base, u32 flags) 28 | { 29 | if (flags & HO_X86_LME) 30 | return (4ull * GB); 31 | 32 | // At least the entire higher half 33 | return get_i686_higher_half_length(direct_map_base); 34 | } 35 | 36 | u64 handover_get_max_pt_address(u64 direct_map_base, u32 flags) 37 | { 38 | if (flags & HO_X86_LME) 39 | // Handover code relies on this being a 32 bit value 40 | return (4ull * GB); 41 | 42 | // Must be accessible from the higher half 43 | return get_i686_higher_half_length(direct_map_base); 44 | } 45 | 46 | #define CR4_PSE (1 << 4) 47 | #define CR4_PAE (1 << 5) 48 | #define CR4_LA57 (1 << 12) 49 | 50 | u32 handover_flags_to_cr4(u32 flags) 51 | { 52 | u32 cr4 = 0; 53 | 54 | if (flags & HO_X86_PSE) 55 | cr4 |= CR4_PSE; 56 | if (flags & HO_X86_PAE) 57 | cr4 |= CR4_PAE; 58 | if (flags & HO_X86_LA57) 59 | cr4 |= CR4_LA57; 60 | 61 | return cr4; 62 | } 63 | 64 | bool handover_flags_map[32] = {}; 65 | struct string_view handover_flags_to_string[32] = { 66 | [HO_X86_LME_BIT] = SV("Long Mode"), 67 | [HO_X86_PSE_BIT] = SV("Page Size Extension"), 68 | [HO_X86_PAE_BIT] = SV("Physical Address Extension"), 69 | [HO_X86_LA57_BIT] = SV("5-Level Paging"), 70 | }; 71 | 72 | #define HIGHEST_FUNCTION_PARAMETER_AND_MANUFACTURER_ID_NUMBER 0x00000000 73 | #define PROCESSOR_INFO_AND_FEATURE_BITS_FUNCTION_NUMBER 0x00000001 74 | #define EXTENDED_FEATURES_FUNCTION_NUMBER 0x00000007 75 | #define HIGHEST_IMPLEMENTED_EXTENDED_FUNCTION_NUMBER 0x80000000 76 | #define EXTENDED_PROCESSOR_INFO_FUNCTION_NUMBER 0x80000001 77 | 78 | #define CPUID_LONG_MODE (1 << 29) 79 | #define CPUID_PSE (1 << 3) 80 | #define CPUID_PAE (1 << 6) 81 | #define CPUID_LA57 (1 << 16) 82 | 83 | void initialize_flags_map(void) 84 | { 85 | struct cpuid_res id; 86 | u32 highest_number; 87 | 88 | cpuid(HIGHEST_FUNCTION_PARAMETER_AND_MANUFACTURER_ID_NUMBER, &id); 89 | highest_number = id.a; 90 | 91 | if (highest_number >= PROCESSOR_INFO_AND_FEATURE_BITS_FUNCTION_NUMBER) { 92 | cpuid(PROCESSOR_INFO_AND_FEATURE_BITS_FUNCTION_NUMBER, &id); 93 | handover_flags_map[HO_X86_PSE_BIT] = id.d & CPUID_PSE; 94 | handover_flags_map[HO_X86_PAE_BIT] = id.d & CPUID_PAE; 95 | } 96 | 97 | if (highest_number >= EXTENDED_FEATURES_FUNCTION_NUMBER) { 98 | cpuid(EXTENDED_FEATURES_FUNCTION_NUMBER, &id); 99 | handover_flags_map[HO_X86_LA57_BIT] = id.c & CPUID_LA57; 100 | } 101 | 102 | cpuid(HIGHEST_IMPLEMENTED_EXTENDED_FUNCTION_NUMBER, &id); 103 | highest_number = id.a; 104 | 105 | // Guard against bogus function numbers if it's not supported 106 | if ((highest_number <= HIGHEST_IMPLEMENTED_EXTENDED_FUNCTION_NUMBER) || 107 | ((highest_number - HIGHEST_IMPLEMENTED_EXTENDED_FUNCTION_NUMBER) > 0xFF)) 108 | return; 109 | 110 | cpuid(EXTENDED_PROCESSOR_INFO_FUNCTION_NUMBER, &id); 111 | handover_flags_map[HO_X86_LME_BIT] = id.d & CPUID_LONG_MODE; 112 | } 113 | -------------------------------------------------------------------------------- /loader/arch/x86/include/arch/constants.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define PAGE_SIZE 4096 4 | #define PAGE_SHIFT 12 5 | -------------------------------------------------------------------------------- /loader/arch/x86/include/arch/elf.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #define ARCH_STRUCT_VIEW(arch, data, type, action) \ 6 | switch (arch) { \ 7 | case ELF_ARCH_I386: { \ 8 | const struct Elf32_##type *view = data; \ 9 | action \ 10 | break; \ 11 | } \ 12 | case ELF_ARCH_AMD64: { \ 13 | const struct Elf64_##type *view = data; \ 14 | action \ 15 | break; \ 16 | } \ 17 | default: \ 18 | BUG(); \ 19 | } 20 | -------------------------------------------------------------------------------- /loader/arch/x86/include/arch/handover_flags.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // x86 long mode enable 4 | #define HO_X86_LME_BIT 28 5 | #define HO_X86_LME (1 << HO_X86_LME_BIT) 6 | 7 | // x86 page size extension 8 | #define HO_X86_PSE_BIT 29 9 | #define HO_X86_PSE (1 << HO_X86_PSE_BIT) 10 | 11 | // x86 physical address extension 12 | #define HO_X86_PAE_BIT 30 13 | #define HO_X86_PAE (1 << HO_X86_PAE_BIT) 14 | 15 | // x86 57 bit linear address (5 level paging) 16 | #define HO_X86_LA57_BIT 31 17 | #define HO_X86_LA57 (1 << HO_X86_LA57_BIT) 18 | 19 | u32 handover_flags_to_cr4(u32 flags); 20 | -------------------------------------------------------------------------------- /loader/arch/x86/include/arch/virtual_memory.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common/types.h" 4 | 5 | #define PAGE_PRESENT (1 << 0) 6 | #define PAGE_READWRITE (1 << 1) 7 | #define PAGE_NORMAL (0 << 7) 8 | #define PAGE_HUGE (1 << 7) 9 | 10 | enum pt_type { 11 | PT_TYPE_I386_NO_PAE = 2, 12 | PT_TYPE_I386_PAE = 3, 13 | PT_TYPE_AMD64_4LVL = 4, 14 | PT_TYPE_AMD64_5LVL = 5, 15 | }; 16 | 17 | static inline size_t pt_depth(enum pt_type pt) 18 | { 19 | return (size_t)pt; 20 | } 21 | 22 | static inline bool pt_is_huge_page(u64 entry) 23 | { 24 | return (entry & PAGE_HUGE) == PAGE_HUGE; 25 | } 26 | -------------------------------------------------------------------------------- /loader/arch/x86/include/pio.h: -------------------------------------------------------------------------------- 1 | #include "common/types.h" 2 | 3 | static inline void out8(u16 port, u8 data) 4 | { 5 | asm volatile("outb %0, %1" ::"a"(data), "Nd"(port)); 6 | } 7 | 8 | static inline void out16(u16 port, u16 data) 9 | { 10 | asm volatile("outw %0, %1" ::"a"(data), "Nd"(port)); 11 | } 12 | 13 | static inline void out32(uint16_t port, uint32_t data) 14 | { 15 | asm volatile("outl %0, %1" ::"a"(data), "Nd"(port)); 16 | } 17 | 18 | static inline u8 in8(u16 port) 19 | { 20 | u8 out_value = 0; 21 | asm volatile("inb %1, %0" : "=a"(out_value) : "Nd"(port) :); 22 | return out_value; 23 | } 24 | -------------------------------------------------------------------------------- /loader/arch/x86/uefi/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_loader_c_flags( 2 | -mno-80387 3 | -mno-mmx 4 | -mno-3dnow 5 | -mno-sse 6 | -mno-sse2 7 | ) 8 | add_loader_nasm_flags(-fwin64) 9 | 10 | target_sources( 11 | ${LOADER_EXECUTABLE} 12 | PRIVATE 13 | uefi_handover.c 14 | uefi_handover.asm 15 | ) 16 | -------------------------------------------------------------------------------- /loader/arch/x86/uefi/uefi_handover.c: -------------------------------------------------------------------------------- 1 | #include "common/constants.h" 2 | #include "common/rw_helpers.h" 3 | #include "handover.h" 4 | #include "services_impl.h" 5 | #include "uefi/relocator.h" 6 | 7 | extern char gdt_ptr[]; 8 | 9 | extern char gdt_struct_begin[]; 10 | extern char gdt_struct_end[]; 11 | 12 | extern char kernel_handover_x86_compat_code_begin[]; 13 | extern char kernel_handover_x86_compat_code_end[]; 14 | 15 | struct x86_handover_info { 16 | u64 arg0, arg1; 17 | u64 entrypoint; 18 | u64 stack; 19 | u64 direct_map_base; 20 | u32 compat_code_addr; 21 | u32 cr3, cr4; 22 | bool is_long_mode; 23 | bool unmap_lower_half; 24 | } *xhi_relocated; 25 | u32 kernel_handover_x86_compat_code_relocated; 26 | 27 | /* 28 | * We drop down to protected mode to set the desired paging mode, so handover 29 | * code MUST be located below 4GiB. Make sure we never go above that. 30 | */ 31 | #define UEFI_HANDOVER_MAX_PHYS_ADDR (4ull * GB) 32 | 33 | static struct relocation_entry relocations[] = { 34 | { 35 | .begin = gdt_struct_begin, 36 | .end = gdt_struct_end, 37 | .max_address = UEFI_HANDOVER_MAX_PHYS_ADDR, 38 | .memory_type = EfiLoaderData, 39 | .user = gdt_ptr + 2, 40 | .cb = write_u64, 41 | }, 42 | { 43 | .begin = kernel_handover_x86_compat_code_begin, 44 | .end = kernel_handover_x86_compat_code_end, 45 | .max_address = UEFI_HANDOVER_MAX_PHYS_ADDR, 46 | .memory_type = EfiLoaderCode, 47 | .user = &kernel_handover_x86_compat_code_relocated, 48 | .cb = write_u32_u64, 49 | }, 50 | { 51 | .size = sizeof(*xhi_relocated), 52 | .max_address = UEFI_HANDOVER_MAX_PHYS_ADDR, 53 | .memory_type = EfiLoaderData, 54 | .user = &xhi_relocated, 55 | .cb = write_u64, 56 | }, 57 | {} 58 | }; 59 | 60 | void handover_prepare_for(struct handover_info *hi) 61 | { 62 | struct relocation_entry *re = relocations; 63 | 64 | /* 65 | * The higher half base for 32-bit kernels is definitely somewhere below 66 | * 4GiB, most likely around the 3GiB area. Make sure the handover code 67 | * lives in the physical memory range that fits direct-mapped higher 68 | * half area for those cases as well. 69 | */ 70 | if (!(hi->flags & HO_X86_LME)) { 71 | u64 max_address = UEFI_HANDOVER_MAX_PHYS_ADDR - hi->direct_map_base; 72 | 73 | BUG_ON(!max_address || (max_address > UEFI_HANDOVER_MAX_PHYS_ADDR)); 74 | 75 | while (re->max_address) { 76 | re->max_address = max_address; 77 | re++; 78 | } 79 | } 80 | 81 | relocate_entries(relocations); 82 | } 83 | 84 | NORETURN 85 | void kernel_handover_x86(struct x86_handover_info *hi); 86 | 87 | NORETURN 88 | void kernel_handover(struct handover_info *hi) 89 | { 90 | *xhi_relocated = (struct x86_handover_info) { 91 | .arg0 = hi->arg0, 92 | .arg1 = hi->arg1, 93 | .entrypoint = hi->entrypoint, 94 | .stack = hi->stack, 95 | .direct_map_base = hi->direct_map_base, 96 | .compat_code_addr = kernel_handover_x86_compat_code_relocated, 97 | .cr3 = pt_get_root(&hi->pt), 98 | .cr4 = handover_flags_to_cr4(hi->flags), 99 | .is_long_mode = hi->flags & HO_X86_LME, 100 | .unmap_lower_half = hi->flags & HO_HIGHER_HALF_ONLY 101 | }; 102 | 103 | kernel_handover_x86(xhi_relocated); 104 | } 105 | -------------------------------------------------------------------------------- /loader/arch/x86/virtual_memory.c: -------------------------------------------------------------------------------- 1 | #include "common/bug.h" 2 | #include "common/string.h" 3 | #include "common/rw_helpers.h" 4 | 5 | #include "virtual_memory.h" 6 | #include "virtual_memory_impl.h" 7 | 8 | void page_table_init(struct page_table *pt, enum pt_type type, 9 | u64 max_table_address) 10 | { 11 | ptr_t root_page = pt_get_table_page(max_table_address); 12 | OOPS_ON(!root_page); 13 | 14 | pt->root = ADDR_TO_PTR(root_page); 15 | pt->levels = pt_depth(type); 16 | pt->base_shift = PAGE_SHIFT; 17 | pt->max_table_address = max_table_address; 18 | 19 | // 52 is the maximum supported number of physical bits 20 | pt->entry_address_mask = ~(BIT_MASK(52, 64) | BIT_MASK(0, PAGE_SHIFT)); 21 | 22 | switch (type) { 23 | case PT_TYPE_I386_NO_PAE: 24 | pt->entry_width = 4; 25 | pt->table_width_shift = 10; 26 | break; 27 | 28 | case PT_TYPE_I386_PAE: 29 | case PT_TYPE_AMD64_4LVL: 30 | case PT_TYPE_AMD64_5LVL: 31 | pt->entry_width = 8; 32 | pt->table_width_shift = 9; 33 | break; 34 | 35 | default: 36 | BUG(); 37 | } 38 | 39 | if (pt->entry_width == 8) { 40 | pt->write_slot = write_u64; 41 | pt->read_slot = read_u64; 42 | } else { 43 | pt->write_slot = write_u32_u64; 44 | pt->read_slot = read_u32_zero_extend; 45 | } 46 | 47 | /* 48 | * 32-bit PAE paging is a bit strange in that the root table consists of 49 | * only four pointers, which have really strange semantics: 50 | * 51 | * 1. On intel, they're cached in shadow registers as soon as CR3 is loaded 52 | * with a new table. What this means is, modifications to the root table 53 | * won't be picked up until a full CR3 flush occurs. 54 | * 2. The WRITE bit for the root table entries is reserved, only the 55 | * PRESENT bit must be set. 56 | * 57 | * The semantics above make it really annoying to deal with lazy allocation 58 | * of the PAE tables, so let's pre-populate all root table slots right away. 59 | */ 60 | if (type == PT_TYPE_I386_PAE) { 61 | size_t i; 62 | ptr_t entry; 63 | void *table = pt->root; 64 | 65 | for (i = 0; i < 4; ++i) { 66 | entry = pt_get_table_page(pt->max_table_address); 67 | OOPS_ON(!entry); 68 | 69 | pt->write_slot(table, entry | PAGE_PRESENT); 70 | table += pt->entry_width; 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /loader/boot_protocol/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | target_sources( 2 | ${LOADER_EXECUTABLE} 3 | PRIVATE 4 | boot_protocol.c 5 | ultra.c 6 | ) 7 | -------------------------------------------------------------------------------- /loader/boot_protocol/boot_protocol.c: -------------------------------------------------------------------------------- 1 | #include "boot_protocol.h" 2 | 3 | CTOR_SECTION_DEFINE_ITERATOR(boot_protocol_entry, boot_protocols); 4 | 5 | #define PROTOCOL_KEY SV("protocol") 6 | 7 | void boot(struct config *cfg, struct loadable_entry *le) 8 | { 9 | struct string_view protocol_name; 10 | struct boot_protocol *proto = NULL; 11 | boot_protocol_entry *entry; 12 | 13 | CFG_MANDATORY_GET(string, cfg, le, PROTOCOL_KEY, &protocol_name); 14 | 15 | for (entry = boot_protocols_begin; entry < boot_protocols_end; ++entry) { 16 | if (!sv_equals_caseless((*entry)->name, protocol_name)) 17 | continue; 18 | 19 | proto = *entry; 20 | break; 21 | } 22 | 23 | if (!proto) 24 | oops("unsupported boot protocol: %pSV\n", &protocol_name); 25 | 26 | if (proto->known_mm_types) 27 | mm_declare_known_mm_types(proto->known_mm_types); 28 | 29 | proto->boot(cfg, le); 30 | } 31 | -------------------------------------------------------------------------------- /loader/common/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | target_sources( 2 | ${LOADER_EXECUTABLE} 3 | PRIVATE 4 | conversions.c 5 | dynamic_buffer.c 6 | format.c 7 | log.c 8 | panic.c 9 | string.c 10 | string_view.c 11 | rw_helpers.c 12 | ) 13 | -------------------------------------------------------------------------------- /loader/common/dynamic_buffer.c: -------------------------------------------------------------------------------- 1 | #include "common/dynamic_buffer.h" 2 | #include "common/string.h" 3 | 4 | bool dynamic_buffer_grow(struct dynamic_buffer *db) 5 | { 6 | size_t entries_per_inc = DYNAMIC_BUFFER_GROWTH_INCREMENT / db->elem_size; 7 | size_t new_capacity = db->capacity + entries_per_inc; 8 | size_t old_cap_bytes = db->capacity * db->elem_size; 9 | 10 | void *new_buf = allocate_bytes(new_capacity * db->elem_size); 11 | if (unlikely(!new_buf)) 12 | return false; 13 | 14 | if (old_cap_bytes) { 15 | memcpy(new_buf, db->buf, old_cap_bytes); 16 | free_bytes(db->buf, old_cap_bytes); 17 | } 18 | 19 | db->buf = new_buf; 20 | db->capacity = new_capacity; 21 | return true; 22 | } 23 | -------------------------------------------------------------------------------- /loader/common/log.c: -------------------------------------------------------------------------------- 1 | #include "common/log.h" 2 | #include "common/format.h" 3 | 4 | // TODO: remove this hack 5 | #if __i386__ 6 | #include "pio.h" 7 | #endif 8 | 9 | #include "video_services.h" 10 | 11 | static enum log_level current_level = LOG_LEVEL_INFO; 12 | 13 | enum log_level logger_set_level(enum log_level level) 14 | { 15 | enum log_level prev = current_level; 16 | current_level = level; 17 | return prev; 18 | } 19 | 20 | #ifdef HYPER_SERIAL_LOG 21 | 22 | #define SERIAL_COM1 0x3F8 23 | 24 | #define INTERRUPT_ENABLE_REGISTER 1 25 | #define LINE_CONTROL_REGISTER 3 26 | #define DATA_REGISTER_BAUD_LO 0 27 | #define DATA_REGISTER_BAUD_HI 1 28 | #define LINE_STATUS_REGISTER 5 29 | 30 | #define SET_BAUD_MODE (1 << 7) 31 | #define DATA_WIDTH_8 (0b11) 32 | #define STOP_BIT_1 (0b0 << 2) 33 | #define PARITY_MODE_NONE (0b000 << 3) 34 | #define INTERRUPT_MODE_NONE (0b0000) 35 | 36 | #define STATUS_BUSY (1 << 5) 37 | 38 | static void serial_init(void) { 39 | const uint16_t baud_divisor = 115200 / HYPER_SERIAL_BAUD_RATE; 40 | out8(SERIAL_COM1 + LINE_CONTROL_REGISTER, SET_BAUD_MODE); 41 | out8(SERIAL_COM1 + DATA_REGISTER_BAUD_LO, (baud_divisor << 8) >> 8); 42 | out8(SERIAL_COM1 + DATA_REGISTER_BAUD_HI, baud_divisor >> 8); 43 | 44 | out8(SERIAL_COM1 + LINE_CONTROL_REGISTER, DATA_WIDTH_8 | STOP_BIT_1 | PARITY_MODE_NONE); 45 | 46 | out8(SERIAL_COM1 + INTERRUPT_ENABLE_REGISTER, INTERRUPT_MODE_NONE); 47 | } 48 | 49 | static void write_serial(const char* msg, size_t len) { 50 | while (len--) { 51 | while ((in8(SERIAL_COM1 + LINE_STATUS_REGISTER) & STATUS_BUSY) == 0) 52 | ; 53 | 54 | out8(SERIAL_COM1, *msg++); 55 | } 56 | } 57 | #else 58 | static void serial_init(void) {} 59 | 60 | static void write_serial(const char* msg, size_t len) { 61 | UNUSED(msg); 62 | UNUSED(len); 63 | } 64 | #endif 65 | 66 | void logger_init(void) { 67 | serial_init(); 68 | } 69 | 70 | static int extract_message_level(const char **msg_ptr) 71 | { 72 | const char *msg = *msg_ptr; 73 | 74 | if (msg[0] != LOG_LEVEL_PREFIX[0] || !msg[1]) 75 | return LOG_LEVEL_INFO; 76 | 77 | switch (msg[1]) { 78 | case '0' ... '3': 79 | *msg_ptr += 2; 80 | return msg[1] - '0'; 81 | } 82 | 83 | return LOG_LEVEL_INFO; 84 | } 85 | 86 | static enum color get_color_for_level(enum log_level level) 87 | { 88 | switch (level) { 89 | default: 90 | case LOG_LEVEL_INFO: 91 | return COLOR_GRAY; 92 | case LOG_LEVEL_WARN: 93 | return COLOR_YELLOW; 94 | case LOG_LEVEL_ERR: 95 | return COLOR_RED; 96 | } 97 | } 98 | 99 | 100 | static void write_0xe9(const char *msg, size_t len) 101 | { 102 | #ifdef HYPER_E9_LOG 103 | while (len--) 104 | asm volatile("outb %0, %1" ::"a"(*msg++), "Nd"(0xE9)); 105 | #else 106 | UNUSED(msg); 107 | UNUSED(len); 108 | #endif 109 | } 110 | 111 | void vprintlvl(enum log_level level, const char *msg, va_list vlist) 112 | { 113 | static char log_buf[256]; 114 | int chars; 115 | enum color col; 116 | 117 | if (unlikely(!msg)) 118 | return; 119 | 120 | if (level < current_level) 121 | return; 122 | 123 | col = get_color_for_level(level); 124 | 125 | chars = vscnprintf(log_buf, sizeof(log_buf), msg, vlist); 126 | write_0xe9(log_buf, chars); 127 | write_serial(log_buf, chars); 128 | vs_write_tty(log_buf, chars, col); 129 | } 130 | 131 | void vprint(const char *msg, va_list vlist) 132 | { 133 | enum log_level level; 134 | level = extract_message_level(&msg); 135 | 136 | vprintlvl(level, msg, vlist); 137 | } 138 | 139 | void print(const char *msg, ...) 140 | { 141 | va_list vlist; 142 | va_start(vlist, msg); 143 | vprint(msg, vlist); 144 | va_end(vlist); 145 | } 146 | 147 | void printlvl(enum log_level level, const char *msg, ...) 148 | { 149 | va_list vlist; 150 | va_start(vlist, msg); 151 | vprintlvl(level, msg, vlist); 152 | va_end(vlist); 153 | } 154 | -------------------------------------------------------------------------------- /loader/common/panic.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "common/panic.h" 3 | #include "common/log.h" 4 | #include "services.h" 5 | 6 | void panic(const char *reason, ...) 7 | { 8 | va_list vlist; 9 | va_start(vlist, reason); 10 | vprintlvl(LOG_LEVEL_ERR, reason, vlist); 11 | va_end(vlist); 12 | 13 | loader_abort(); 14 | } 15 | 16 | void oops(const char *reason, ...) 17 | { 18 | print_err("Oops!\n"); 19 | 20 | va_list vlist; 21 | va_start(vlist, reason); 22 | vprintlvl(LOG_LEVEL_ERR, reason, vlist); 23 | va_end(vlist); 24 | 25 | loader_abort(); 26 | } 27 | -------------------------------------------------------------------------------- /loader/common/rw_helpers.c: -------------------------------------------------------------------------------- 1 | #include "common/bug.h" 2 | #include "common/rw_helpers.h" 3 | 4 | u32 read_u32(void *ptr) { return *(u32*)ptr; } 5 | u64 read_u32_zero_extend(void *ptr) { return read_u32(ptr); } 6 | 7 | u64 read_u64(void *ptr) { return *(u64*)ptr; } 8 | 9 | void write_u32(void *ptr, u32 val) 10 | { 11 | u32 *dword = ptr; 12 | *dword = val; 13 | } 14 | 15 | void write_u32_u64(void *ptr, u64 val) 16 | { 17 | write_u32(ptr, (u32)val); 18 | } 19 | 20 | void write_u32_checked_u64(void *ptr, u64 val) 21 | { 22 | BUG_ON(val > 0xFFFFFFFF); 23 | write_u32_u64(ptr, val); 24 | } 25 | 26 | void write_u64(void *ptr, u64 val) 27 | { 28 | u64 *qword = ptr; 29 | *qword = val; 30 | } 31 | -------------------------------------------------------------------------------- /loader/common/string.c: -------------------------------------------------------------------------------- 1 | #include "common/string.h" 2 | 3 | #ifdef HARDENED_STRING 4 | #include "log.h" 5 | #include "services.h" 6 | 7 | void die_on_runtime_oob(const char *func, const char *file, size_t line, 8 | size_t size, size_t dst_size, size_t src_size) 9 | { 10 | print_err("BUG: out of bounds %s() call at %s:%zu: %zu bytes with dst=%zu", 11 | func, file, line, size, dst_size); 12 | 13 | if (src_size) 14 | print_err(" src=%zu", src_size); 15 | 16 | print_err("\n"); 17 | loader_abort(); 18 | } 19 | #endif 20 | 21 | #ifdef PLATFORM_WANTS_GENERIC_STRING 22 | 23 | #ifdef memmove 24 | #undef memmove 25 | #endif 26 | 27 | void *MEMMOVE_FUNC(void *dest, const void *src, size_t count) 28 | { 29 | char *cd = dest; 30 | const char *cs = src; 31 | 32 | if (src < dest) { 33 | cs += count; 34 | cd += count; 35 | 36 | while (count--) 37 | *--cd = *--cs; 38 | } else { 39 | memcpy(dest, src, count); 40 | } 41 | 42 | return dest; 43 | } 44 | 45 | #ifdef memcpy 46 | #undef memcpy 47 | #endif 48 | 49 | void *MEMCPY_FUNC(void *dest, const void *src, size_t count) 50 | { 51 | char *cd = dest; 52 | const char *cs = src; 53 | 54 | while (count--) 55 | *cd++ = *cs++; 56 | 57 | return dest; 58 | } 59 | 60 | #ifdef memset 61 | #undef memset 62 | #endif 63 | 64 | void *MEMSET_FUNC(void *dest, int ch, size_t count) 65 | { 66 | unsigned char fill = ch; 67 | unsigned char *cdest = dest; 68 | 69 | while (count--) 70 | *cdest++ = fill; 71 | 72 | return dest; 73 | } 74 | 75 | #ifdef memcmp 76 | #undef memcmp 77 | #endif 78 | 79 | int MEMCMP_FUNC(const void *lhs, const void *rhs, size_t count) 80 | { 81 | const u8 *byte_lhs = lhs; 82 | const u8 *byte_rhs = rhs; 83 | size_t i; 84 | 85 | for (i = 0; i < count; ++i) { 86 | if (byte_lhs[i] != byte_rhs[i]) 87 | return byte_lhs[i] - byte_rhs[i]; 88 | } 89 | 90 | return 0; 91 | } 92 | 93 | #ifdef strlen 94 | #undef strlen 95 | #endif 96 | 97 | size_t STRLEN_FUNC(const char *str) 98 | { 99 | const char *str1; 100 | 101 | for (str1 = str; *str1; str1++); 102 | 103 | return str1 - str; 104 | } 105 | #endif 106 | -------------------------------------------------------------------------------- /loader/common/string_view.c: -------------------------------------------------------------------------------- 1 | #include "common/string_view.h" 2 | #include "common/ctype.h" 3 | 4 | bool sv_equals(struct string_view lhs, struct string_view rhs) 5 | { 6 | size_t i; 7 | 8 | if (lhs.size != rhs.size) 9 | return false; 10 | 11 | for (i = 0; i < lhs.size; ++i) { 12 | if (lhs.text[i] != rhs.text[i]) 13 | return false; 14 | } 15 | 16 | return true; 17 | } 18 | 19 | bool sv_equals_caseless(struct string_view lhs, struct string_view rhs) 20 | { 21 | size_t i; 22 | 23 | if (lhs.size != rhs.size) 24 | return false; 25 | 26 | for (i = 0; i < lhs.size; ++i) { 27 | if (tolower(lhs.text[i]) != tolower(rhs.text[i])) 28 | return false; 29 | } 30 | 31 | return true; 32 | } 33 | 34 | bool sv_starts_with(struct string_view str, struct string_view prefix) 35 | { 36 | size_t i; 37 | 38 | if (prefix.size > str.size) 39 | return false; 40 | if (prefix.size == 0) 41 | return true; 42 | 43 | for (i = 0; i < prefix.size; ++i) { 44 | if (str.text[i] != prefix.text[i]) 45 | return false; 46 | } 47 | 48 | return true; 49 | } 50 | 51 | ssize_t sv_find(struct string_view str, struct string_view needle, size_t starting_at) 52 | { 53 | size_t i, j, k; 54 | 55 | BUG_ON(starting_at > str.size); 56 | 57 | if (needle.size > (str.size - starting_at)) 58 | return -1; 59 | if (sv_empty(needle)) 60 | return 0; 61 | 62 | for (i = starting_at; i < str.size - needle.size + 1; ++i) { 63 | if (str.text[i] != needle.text[0]) 64 | continue; 65 | 66 | j = i; 67 | k = 0; 68 | 69 | while (k < needle.size) { 70 | if (str.text[j++] != needle.text[k]) 71 | break; 72 | 73 | k++; 74 | } 75 | 76 | if (k == needle.size) 77 | return i; 78 | } 79 | 80 | return -1; 81 | } 82 | -------------------------------------------------------------------------------- /loader/edid.c: -------------------------------------------------------------------------------- 1 | #include "common/log.h" 2 | #include "edid.h" 3 | 4 | void edid_get_native_resolution(struct edid *e, size_t *native_width, size_t *native_height) 5 | { 6 | struct timing_descriptor *td = &e->detailed_timing_descriptors[0]; 7 | 8 | *native_height = td->vertical_active_lines_lo; 9 | *native_height |= td->vertical_active_lines_hi << 8; 10 | 11 | *native_width = td->horizontal_active_pixels_lo; 12 | *native_width |= td->horizontal_active_pixels_hi << 8; 13 | } 14 | 15 | u8 edid_calculate_checksum(struct edid *e) 16 | { 17 | u8 edid_checksum = 0; 18 | u8 *e_bytes = (u8*)e; 19 | size_t i; 20 | 21 | for (i = 0; i < sizeof(struct edid); ++i) 22 | edid_checksum += e_bytes[i]; 23 | 24 | return edid_checksum; 25 | } 26 | -------------------------------------------------------------------------------- /loader/filesystem/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | target_sources( 2 | ${LOADER_EXECUTABLE} 3 | PRIVATE 4 | block_cache.c 5 | bulk_read.c 6 | filesystem.c 7 | filesystem_table.c 8 | gpt.c 9 | mbr.c 10 | path.c 11 | ) 12 | 13 | add_subdirectory(fat) 14 | add_subdirectory(iso9660) 15 | -------------------------------------------------------------------------------- /loader/filesystem/bulk_read.c: -------------------------------------------------------------------------------- 1 | #include "common/align.h" 2 | #include "common/minmax.h" 3 | 4 | #include "filesystem/bulk_read.h" 5 | 6 | struct bulk_read_req { 7 | struct file *f; 8 | 9 | void *buf; 10 | u64 file_off; 11 | u32 bytes; 12 | 13 | u16 fs_block_mask; 14 | u16 disk_block_mask; 15 | }; 16 | 17 | static inline size_t br_wanted_block_count(struct bulk_read_req *br) 18 | { 19 | size_t bytes = (br->file_off & br->fs_block_mask) + br->bytes; 20 | bytes = ALIGN_UP(bytes, br->fs_block_mask + 1); 21 | 22 | return bytes >> file_block_shift(br->f); 23 | } 24 | 25 | static bool do_bulk_read(struct bulk_read_req *br, file_get_range_t get_range) 26 | { 27 | struct filesystem *fs = br->f->fs; 28 | struct disk *d = &fs->d; 29 | struct block_range out_range; 30 | u32 bytes_in_range, file_off_in_block; 31 | size_t want_blocks; 32 | u64 file_block; 33 | 34 | while (br->bytes) { 35 | want_blocks = br_wanted_block_count(br); 36 | file_off_in_block = br->file_off & br->fs_block_mask; 37 | file_block = br->file_off >> fs->block_shift; 38 | 39 | if (!get_range(br->f, file_block, want_blocks, &out_range)) 40 | return false; 41 | 42 | BUG_ON(out_range.blocks == 0); 43 | bytes_in_range = (out_range.blocks << fs->block_shift) - file_off_in_block; 44 | bytes_in_range = MIN(bytes_in_range, br->bytes); 45 | 46 | if (is_block_range_hole(&out_range)) { 47 | memset(br->buf, 0, bytes_in_range); 48 | goto next; 49 | } 50 | 51 | out_range.part_byte_off += file_off_in_block; 52 | 53 | // Request is unaligned to disk block, do a bounce buffer read 54 | if ((out_range.part_byte_off & br->disk_block_mask) || 55 | (bytes_in_range & br->disk_block_mask)) 56 | { 57 | u64 full_off = fs->lba_range.begin << d->block_shift; 58 | full_off += out_range.part_byte_off; 59 | 60 | if (!ds_read(d->handle, br->buf, full_off, bytes_in_range)) 61 | return false; 62 | } else { 63 | u64 full_off = fs->lba_range.begin; 64 | full_off += out_range.part_byte_off >> d->block_shift; 65 | 66 | if (!ds_read_blocks(d->handle, br->buf, full_off, bytes_in_range >> d->block_shift)) 67 | return false; 68 | } 69 | 70 | next: 71 | br->buf += bytes_in_range; 72 | br->file_off += bytes_in_range; 73 | br->bytes -= bytes_in_range; 74 | } 75 | 76 | return true; 77 | } 78 | 79 | bool bulk_read_file(struct file* f, void *buffer, u64 offset, u32 bytes, 80 | file_get_range_t get_range) 81 | { 82 | struct filesystem *fs = f->fs; 83 | struct disk *d = &fs->d; 84 | u64 parts[3]; 85 | u8 block_shift; 86 | u16 block_mask, block_size; 87 | size_t i; 88 | 89 | struct bulk_read_req br = { 90 | .f = f, 91 | .buf = buffer, 92 | .file_off = offset, 93 | .disk_block_mask = (1 << d->block_shift) - 1, 94 | .fs_block_mask = (1 << fs->block_shift) - 1, 95 | }; 96 | 97 | fs_check_read(f, offset, bytes); 98 | 99 | block_shift = fs->block_shift; 100 | if (unlikely(fs->block_shift < d->block_shift)) 101 | block_shift = d->block_shift; 102 | 103 | block_mask = (1 << block_shift) - 1; 104 | block_size = 1 << block_shift; 105 | 106 | // Calculate unaligned head bytes 107 | parts[0] = offset & block_mask; 108 | if (parts[0]) 109 | parts[0] = MIN(block_size - parts[0], bytes); 110 | 111 | bytes -= parts[0]; 112 | 113 | // Calculate unaligned tail bytes 114 | parts[2] = bytes & block_mask; 115 | bytes -= parts[2]; 116 | 117 | parts[1] = bytes; 118 | 119 | for (i = 0; i < 3; ++i) { 120 | if (!parts[i]) 121 | continue; 122 | 123 | br.bytes = parts[i]; 124 | if (!do_bulk_read(&br, get_range)) 125 | return false; 126 | } 127 | 128 | return true; 129 | } 130 | -------------------------------------------------------------------------------- /loader/filesystem/fat/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | target_sources( 2 | ${LOADER_EXECUTABLE} 3 | PRIVATE 4 | fat.c 5 | ) 6 | -------------------------------------------------------------------------------- /loader/filesystem/fat/structures.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common/types.h" 4 | #include "common/bug.h" 5 | 6 | struct PACKED dos20_bpb { 7 | u16 bytes_per_sector; 8 | u8 sectors_per_cluster; 9 | u16 reserved_sectors; 10 | u8 fat_count; 11 | u16 max_root_dir_entries; 12 | u16 total_logical_sectors_fat12_or_16; 13 | u8 media_descriptor; 14 | u16 sectors_per_fat_fat12_or_16; 15 | }; 16 | BUILD_BUG_ON(sizeof(struct dos20_bpb) != 13); 17 | 18 | struct PACKED dos30_bpb { 19 | struct dos20_bpb d20_bpb; 20 | 21 | u16 sectors_per_track; 22 | u16 heads; 23 | u16 hidden_sectors; 24 | }; 25 | BUILD_BUG_ON(sizeof(struct dos30_bpb) != 19); 26 | 27 | struct PACKED dos32_bpb { 28 | struct dos30_bpb d20_bpb; 29 | 30 | u16 total_logical_sectors; 31 | }; 32 | BUILD_BUG_ON(sizeof(struct dos32_bpb) != 21); 33 | 34 | struct PACKED dos33_bpb { 35 | struct dos20_bpb d20_bpb; 36 | 37 | u16 sectors_per_track; 38 | u16 heads; 39 | u32 hidden_sectors; 40 | u32 total_logical_sectors_fat32; 41 | }; 42 | BUILD_BUG_ON(sizeof(struct dos33_bpb) != 25); 43 | 44 | struct PACKED fat12_or_16_ebpb { 45 | struct dos33_bpb d33_bpb; 46 | 47 | u8 physical_drive_number; 48 | u8 reserved; 49 | u8 signature; 50 | u32 volume_id; 51 | char volume_label[11]; 52 | char filesystem_type[8]; 53 | }; 54 | BUILD_BUG_ON(sizeof(struct fat12_or_16_ebpb) != 51); 55 | 56 | struct PACKED fat32_ebpb { 57 | struct dos33_bpb d33_bpb; 58 | 59 | u32 sectors_per_fat; 60 | u16 ext_flags; 61 | u16 version; 62 | u32 root_dir_cluster; 63 | u16 fs_information_sector; 64 | u16 backup_boot_sectors; 65 | u8 reserved[12]; 66 | u8 drive_number; 67 | u8 unused_3; 68 | u8 signature; 69 | u32 volume_id; 70 | char volume_label[11]; 71 | char filesystem_type[8]; 72 | }; 73 | BUILD_BUG_ON(sizeof(struct fat32_ebpb) != 79); 74 | 75 | #define FAT_SHORT_NAME_LENGTH 8 76 | #define FAT_SHORT_EXTENSION_LENGTH 3 77 | #define FAT_FULL_SHORT_NAME_LENGTH (FAT_SHORT_NAME_LENGTH + FAT_SHORT_EXTENSION_LENGTH) 78 | 79 | #define END_OF_DIRECTORY_MARK 0x00 80 | #define DELETED_FILE_MARK 0xE5 81 | 82 | #define LONG_NAME_ATTRIBUTE 0x0F 83 | #define DEVICE_ATTRIBUTE (1 << 6) 84 | #define ARCHIVE_ATTRIBUTE (1 << 5) 85 | #define SUBDIR_ATTRIBUTE (1 << 4) 86 | #define VOLUME_LABEL_ATTRIBUTE (1 << 3) 87 | #define SYSTEM_ATTRIBUTE (1 << 2) 88 | #define HIDDEN_ATTRIBUTE (1 << 1) 89 | #define READ_ONLY_ATTRIBUTE (1 << 0) 90 | 91 | #define LOWERCASE_NAME_BIT (1 << 3) 92 | #define LOWERCASE_EXTENSION_BIT (1 << 4) 93 | 94 | struct PACKED fat_directory_entry { 95 | char filename[FAT_SHORT_NAME_LENGTH]; 96 | char extension[FAT_SHORT_EXTENSION_LENGTH]; 97 | 98 | u8 attributes; 99 | u8 case_info; 100 | u8 created_ms; 101 | u16 created_time; 102 | u16 created_date; 103 | u16 last_accessed_date; 104 | u16 cluster_high; 105 | u16 last_modified_time; 106 | u16 last_modified_date; 107 | u16 cluster_low; 108 | u32 size; 109 | }; 110 | BUILD_BUG_ON(sizeof(struct fat_directory_entry) != 32); 111 | 112 | #define BYTES_PER_UCS2_CHAR 2 113 | 114 | #define NAME_1_CHARS 5 115 | #define NAME_2_CHARS 6 116 | #define NAME_3_CHARS 2 117 | #define CHARS_PER_LONG_ENTRY (NAME_1_CHARS + NAME_2_CHARS + NAME_3_CHARS) 118 | 119 | #define LAST_LOGICAL_ENTRY_BIT (1 << 6) 120 | #define SEQUENCE_NUM_BIT_MASK 0b11111 121 | 122 | struct PACKED long_name_fat_directory_entry { 123 | u8 sequence_number; 124 | u8 name_1[NAME_1_CHARS * BYTES_PER_UCS2_CHAR]; 125 | u8 attributes; 126 | u8 type; 127 | u8 checksum; 128 | u8 name_2[NAME_2_CHARS * BYTES_PER_UCS2_CHAR]; 129 | u16 first_cluster; 130 | u8 name_3[NAME_3_CHARS * BYTES_PER_UCS2_CHAR]; 131 | }; 132 | BUILD_BUG_ON(sizeof(struct long_name_fat_directory_entry) != 32); 133 | -------------------------------------------------------------------------------- /loader/filesystem/filesystem.c: -------------------------------------------------------------------------------- 1 | #define MSG_FMT(msg) "FS: " msg 2 | 3 | #include "common/types.h" 4 | #include "common/string_view.h" 5 | 6 | #include "filesystem/filesystem.h" 7 | #include "filesystem/filesystem_table.h" 8 | #include "filesystem/mbr.h" 9 | #include "filesystem/gpt.h" 10 | 11 | CTOR_SECTION_DEFINE_ITERATOR(filesystem_type_entry, filesystems); 12 | 13 | void fs_check_read(struct file *f, u64 offset, u32 size) 14 | { 15 | u64 final_offset = offset + size; 16 | 17 | if (unlikely(size == 0)) 18 | goto die; 19 | 20 | if (unlikely(final_offset < offset)) 21 | goto die; 22 | 23 | if (unlikely(final_offset > f->size)) 24 | goto die; 25 | 26 | return; 27 | 28 | die: 29 | panic("BUG: invalid read at offset %llu with size %u!\n", offset, size); 30 | } 31 | 32 | enum fs_detect_type { 33 | FS_DETECT_CD, 34 | FS_DETECT_HDD, 35 | }; 36 | 37 | static struct filesystem* 38 | fs_do_detect(const struct disk *d, struct range lba_range, 39 | struct block_cache *bc, enum fs_detect_type dt) 40 | { 41 | filesystem_type_entry *fse; 42 | struct filesystem *fs; 43 | 44 | for (fse = filesystems_begin; fse < filesystems_end; ++fse) { 45 | struct filesystem_type *fst = *fse; 46 | bool is_cd_fs = fst->flags & FS_TYPE_CD; 47 | 48 | if (is_cd_fs != (dt == FS_DETECT_CD)) 49 | continue; 50 | 51 | if ((fs = fst->detect(d, lba_range, bc))) 52 | return fs; 53 | } 54 | 55 | return NULL; 56 | } 57 | 58 | struct filesystem *fs_try_detect(const struct disk *d, struct range lba_range, 59 | struct block_cache *bc) 60 | { 61 | return fs_do_detect(d, lba_range, bc, FS_DETECT_HDD); 62 | } 63 | 64 | static bool detect_entire(const struct disk *d, struct block_cache *bc, 65 | enum fs_detect_type dt) 66 | { 67 | struct filesystem *fs; 68 | struct range lba_range = { 0, d->sectors }; 69 | 70 | fs = fs_do_detect(d, lba_range, bc, dt); 71 | if (!fs) 72 | return false; 73 | 74 | fst_add_raw_fs_entry(d, fs); 75 | return true; 76 | } 77 | 78 | static bool detect_cd(const struct disk *d, struct block_cache *bc) 79 | { 80 | return detect_entire(d, bc, FS_DETECT_CD); 81 | } 82 | 83 | static void detect_raw(const struct disk *d, struct block_cache *bc) 84 | { 85 | detect_entire(d, bc, FS_DETECT_HDD); 86 | } 87 | 88 | void fs_detect_all(struct disk *d, struct block_cache *bc) 89 | { 90 | if (detect_cd(d, bc)) 91 | return; 92 | 93 | if (!block_cache_refill(bc, 0)) 94 | return; 95 | 96 | if (gpt_initialize(d, bc)) 97 | return; 98 | 99 | if (mbr_initialize(d, bc)) 100 | return; 101 | 102 | detect_raw(d, bc); 103 | } 104 | -------------------------------------------------------------------------------- /loader/filesystem/gpt.c: -------------------------------------------------------------------------------- 1 | #include "common/helpers.h" 2 | #include "common/log.h" 3 | 4 | #include "filesystem/gpt.h" 5 | #include "filesystem/guid.h" 6 | #include "filesystem/filesystem.h" 7 | #include "filesystem/filesystem_table.h" 8 | 9 | struct gpt_header { 10 | u64 Signature; 11 | u32 Revision; 12 | u32 HeaderSize; 13 | u32 HeaderCRC32; 14 | u32 Reserved; 15 | u64 MyLBA; 16 | u64 AlternateLBA; 17 | u64 FirstUsableLBA; 18 | u64 LastUsableLBA; 19 | struct guid DiskGUID; 20 | u64 PartitionEntryLBA; 21 | u32 NumberOfPartitionEntries; 22 | u32 SizeOfPartitionEntry; 23 | u32 PartitionEntryArrayCRC32; 24 | u32 Reserved1; 25 | // u8 Reserved1[512 - 92]; A bit useless, comment out for now 26 | }; 27 | BUILD_BUG_ON(sizeof(struct gpt_header) != 96); 28 | 29 | struct gpt_partition_entry { 30 | struct guid PartitionTypeGUID; 31 | struct guid UniquePartitionGUID; 32 | u64 StartingLBA; 33 | u64 EndingLBA; 34 | u64 Attributes; 35 | u16 PartitionName[36]; 36 | }; 37 | BUILD_BUG_ON(sizeof(struct gpt_partition_entry) != 128); 38 | 39 | #define UNUSED_PARTITION_GUID \ 40 | { 0x00000000, 0x0000, 0x0000, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } }; 41 | static struct guid unused_part_guid = UNUSED_PARTITION_GUID; 42 | 43 | struct gpt_part_ctx { 44 | struct block_cache *bc; 45 | struct guid *disk_guid; 46 | struct gpt_partition_entry pe; 47 | const struct disk *d; 48 | size_t part_idx; 49 | }; 50 | 51 | static void gpt_initialize_partition(struct gpt_part_ctx *ctx) 52 | { 53 | struct filesystem *fs = NULL; 54 | struct range lba_range; 55 | 56 | if (guid_compare(&unused_part_guid, &ctx->pe.PartitionTypeGUID) == 0) 57 | return; 58 | 59 | lba_range = (struct range) { 60 | ctx->pe.StartingLBA, 61 | ctx->pe.EndingLBA 62 | }; 63 | 64 | fs = fs_try_detect(ctx->d, lba_range, ctx->bc); 65 | if (!fs) 66 | return; 67 | 68 | fst_add_gpt_fs_entry(ctx->d, ctx->part_idx, ctx->disk_guid, 69 | &ctx->pe.UniquePartitionGUID, fs); 70 | } 71 | 72 | static void gpt_do_initialize(const struct disk *d, struct block_cache *bc) 73 | { 74 | struct gpt_header hdr; 75 | struct gpt_part_ctx part_ctx; 76 | u64 current_off; 77 | 78 | if (!block_cache_read(bc, &hdr, 1 << d->block_shift, 79 | sizeof(struct gpt_header))) 80 | return; 81 | 82 | if (hdr.SizeOfPartitionEntry < sizeof(struct gpt_partition_entry)) { 83 | print_warn("invalid GPT partition entry size %u, skipped (disk %u)\n", 84 | hdr.SizeOfPartitionEntry, d->id); 85 | return; 86 | } 87 | 88 | part_ctx.bc = bc; 89 | part_ctx.disk_guid = &hdr.DiskGUID; 90 | part_ctx.d = d; 91 | current_off = hdr.PartitionEntryLBA << d->block_shift; 92 | 93 | for (part_ctx.part_idx = 0; 94 | part_ctx.part_idx < hdr.NumberOfPartitionEntries; 95 | ++part_ctx.part_idx) 96 | { 97 | if (!block_cache_read(bc, &part_ctx.pe, current_off, 98 | sizeof(struct gpt_partition_entry))) 99 | continue; 100 | 101 | gpt_initialize_partition(&part_ctx); 102 | current_off += hdr.SizeOfPartitionEntry; 103 | } 104 | } 105 | 106 | // "EFI PART" 107 | #define GPT_SIGNATURE 0x5452415020494645 108 | 109 | bool gpt_initialize(const struct disk *d, struct block_cache *bc) 110 | { 111 | u64 signature; 112 | 113 | if (!block_cache_read(bc, &signature, disk_block_size(d), 114 | sizeof(signature))) 115 | return false; 116 | 117 | if (signature != GPT_SIGNATURE) 118 | return false; 119 | 120 | gpt_do_initialize(d, bc); 121 | return true; 122 | } 123 | -------------------------------------------------------------------------------- /loader/filesystem/iso9660/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | target_sources( 2 | ${LOADER_EXECUTABLE} 3 | PRIVATE 4 | iso9660.c 5 | ) 6 | -------------------------------------------------------------------------------- /loader/filesystem/mbr.c: -------------------------------------------------------------------------------- 1 | #include "common/attributes.h" 2 | #include "common/range.h" 3 | #include "common/log.h" 4 | 5 | #include "filesystem/filesystem.h" 6 | #include "filesystem/filesystem_table.h" 7 | #include "filesystem/mbr.h" 8 | 9 | struct PACKED mbr_partition_entry { 10 | u8 status; 11 | u8 chs_begin[3]; 12 | u8 type; 13 | u8 chs_end[3]; 14 | u32 first_block; 15 | u32 block_count; 16 | }; 17 | BUILD_BUG_ON(sizeof(struct mbr_partition_entry) != 16); 18 | 19 | enum { 20 | MBR_EMPTY_PARTITION = 0x00, 21 | MBR_EBR_PARTITION = 0x05 22 | }; 23 | 24 | #define OFFSET_TO_MBR_PARTITION_LIST 0x01BE 25 | 26 | static void mbr_do_initialize(const struct disk *d, struct block_cache *bc, 27 | size_t base_index, u64 sector_offset) 28 | { 29 | struct mbr_partition_entry partitions[4]; 30 | u64 part_abs_byte_off = (sector_offset << d->block_shift) + OFFSET_TO_MBR_PARTITION_LIST; 31 | bool is_ebr = base_index != 0; 32 | size_t i, max_partitions = is_ebr ? 2 : 4; 33 | 34 | if (!block_cache_read(bc, partitions, part_abs_byte_off, sizeof(partitions))) 35 | return; 36 | 37 | for (i = 0; i < max_partitions; ++i) { 38 | struct mbr_partition_entry *p = &partitions[i]; 39 | u64 real_partition_offset = sector_offset + p->first_block; 40 | struct range lba_range = { 41 | real_partition_offset, 42 | real_partition_offset + p->block_count 43 | }; 44 | struct filesystem *fs = NULL; 45 | 46 | if (p->type == MBR_EMPTY_PARTITION) 47 | continue; 48 | 49 | if (p->type == MBR_EBR_PARTITION) { 50 | if (is_ebr && i == 0) { 51 | print_warn("EBR with chain at index 0"); 52 | break; 53 | } 54 | 55 | mbr_do_initialize(d, bc, base_index + (is_ebr ? 1 : 4), 56 | real_partition_offset); 57 | continue; 58 | } 59 | 60 | if (i == 1 && is_ebr) { 61 | print_warn("EBR with a non-EBR entry at index 1 (0x%X)", p->type); 62 | break; 63 | } 64 | 65 | fs = fs_try_detect(d, lba_range, bc); 66 | if (fs) 67 | fst_add_mbr_fs_entry(d, base_index + i, fs); 68 | } 69 | } 70 | 71 | #define MBR_SIGNATURE 0xAA55 72 | #define MBR_OFFSET_TO_SIGNATURE 510 73 | 74 | bool mbr_initialize(const struct disk *d, struct block_cache *bc) 75 | { 76 | u16 signature; 77 | 78 | if (!block_cache_read(bc, &signature, MBR_OFFSET_TO_SIGNATURE, 79 | sizeof(signature))) 80 | return false; 81 | 82 | if (signature != MBR_SIGNATURE) 83 | return false; 84 | 85 | mbr_do_initialize(d, bc, 0, 0); 86 | return true; 87 | } 88 | -------------------------------------------------------------------------------- /loader/gcc_builtins.c: -------------------------------------------------------------------------------- 1 | /* 2 | * https://gcc.gnu.org/onlinedocs/gccint/Integer-library-routines.html 3 | * We implement these ourselves as clang doesn't exactly offer an easy 4 | * way to use an existing clang_rt for arch-none-none type targets. 5 | */ 6 | 7 | #include "common/types.h" 8 | #include "common/bug.h" 9 | 10 | #define QWORD_HI(x) (((unsigned int)(((x) & 0xFFFFFFFF00000000) >> 32))) 11 | #define QWORD_LO(x) (((unsigned int)(((x) & 0x00000000FFFFFFFF) ))) 12 | 13 | #ifdef __i386__ 14 | 15 | // https://en.wikipedia.org/wiki/Division_algorithm#Long_division 16 | static u64 do_64bit_division(u64 a, u64 b, u64 *c) 17 | { 18 | u64 quotient = 0, remainder = 0; 19 | u32 bit = 64 - __builtin_clzll(a); 20 | 21 | while (bit-- > 0) { 22 | remainder = (remainder << 1 | ((a >> bit) & 1)); 23 | 24 | if (remainder >= b) { 25 | remainder -= b; 26 | quotient |= 1ull << bit; 27 | } 28 | } 29 | 30 | if (c) 31 | *c = remainder; 32 | 33 | return quotient; 34 | } 35 | 36 | /* 37 | * Documentation says this takes in an "unsigned long", which is a lie. 38 | * It takes in an 8 byte integer for all inputs & outputs. 39 | * 40 | * NOTE: none of the other overloads of these functions are ever referenced, 41 | * so they're not implemented here. 42 | */ 43 | u64 __udivmoddi4(u64 a, u64 b, u64 *c) 44 | { 45 | if (b > a) { 46 | if (c) 47 | *c = a; 48 | 49 | return 0; 50 | } 51 | 52 | if (b == a) { 53 | if (c) 54 | *c = 0; 55 | 56 | return 1; 57 | } 58 | 59 | if (!QWORD_HI(b)) { 60 | BUG_ON(b == 0); 61 | 62 | if (b == 1) { 63 | if (c) 64 | *c = 0; 65 | 66 | return a; 67 | } 68 | 69 | 70 | if (!QWORD_HI(a)) { 71 | u32 lo_a = QWORD_LO(a); 72 | u32 lo_b = QWORD_LO(b); 73 | 74 | if (c) 75 | *c = lo_a % lo_b; 76 | 77 | return lo_a / lo_b; 78 | } 79 | } 80 | 81 | // All fast paths failed, do a full 64 bit division 82 | return do_64bit_division(a, b, c); 83 | } 84 | 85 | u64 __umoddi3(u64 a, u64 b) 86 | { 87 | u64 c; 88 | 89 | __udivmoddi4(a, b, &c); 90 | return c; 91 | } 92 | 93 | u64 __udivdi3(u64 a, u64 b) 94 | { 95 | return __udivmoddi4(a, b, NULL); 96 | } 97 | #endif 98 | -------------------------------------------------------------------------------- /loader/include/allocator.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common/types.h" 4 | #include "common/attributes.h" 5 | #include "common/constants.h" 6 | #include "common/align.h" 7 | #include "common/helpers.h" 8 | #include "memory_services.h" 9 | 10 | #define ALLOCATOR_DEFAULT_CEILING (4ull * GB) 11 | #define ALLOCATOR_DEFAULT_ALLOC_TYPE MEMORY_TYPE_LOADER_RECLAIMABLE 12 | 13 | // ALLOCATE_CEILING is implicit if ALLOCATE_PRECISE is not set 14 | #define ALLOCATE_PRECISE (1 << 0) 15 | #define ALLOCATE_CRITICAL (1 << 1) 16 | #define ALLOCATE_STACK (1 << 2) 17 | 18 | struct allocation_spec { 19 | union { 20 | u64 addr; 21 | u64 ceiling; 22 | }; 23 | 24 | size_t pages; 25 | 26 | u32 flags; 27 | u32 type; 28 | }; 29 | 30 | u64 allocate_pages_ex(const struct allocation_spec*); 31 | 32 | static ALWAYS_INLINE 33 | void *allocate_pages_with_flags(size_t count, u32 flags) 34 | { 35 | struct allocation_spec spec = { 36 | .pages = count, 37 | .flags = flags 38 | }; 39 | return ADDR_TO_PTR(allocate_pages_ex(&spec)); 40 | } 41 | 42 | static ALWAYS_INLINE 43 | void *allocate_pages(size_t count) 44 | { 45 | return allocate_pages_with_flags(count, 0); 46 | } 47 | 48 | static ALWAYS_INLINE 49 | void *allocate_critical_pages(size_t count) 50 | { 51 | return allocate_pages_with_flags(count, ALLOCATE_CRITICAL); 52 | } 53 | 54 | static ALWAYS_INLINE 55 | void *allocate_bytes(size_t count) 56 | { 57 | size_t page_count = PAGE_ROUND_UP(count) >> PAGE_SHIFT; 58 | return allocate_pages(page_count); 59 | } 60 | 61 | static ALWAYS_INLINE 62 | void *allocate_critical_bytes(size_t count) 63 | { 64 | size_t page_count = PAGE_ROUND_UP(count) >> PAGE_SHIFT; 65 | return allocate_critical_pages(page_count); 66 | } 67 | 68 | void free_pages(void*, size_t); 69 | void free_bytes(void*, size_t); 70 | 71 | #ifdef HYPER_ALLOCATION_AUDIT 72 | #include "common/log.h" 73 | 74 | #define ALLOCATION_TRACE(addr, count, units) \ 75 | print("allocation at %s:%d => 0x%016llX (%llu " units ")\n", \ 76 | __FILE__, __LINE__, (u64)((ptr_t)(addr)), (u64)(count)) 77 | 78 | #define FREE_TRACE(addr, count, units) \ 79 | print("free of 0x%016llX at %s:%d (%llu " units ")\n", \ 80 | (u64)((ptr_t)(addr)), __FILE__, __LINE__, (u64)(count)) 81 | 82 | #define allocate_pages_ex(spec) ({ \ 83 | u64 ret; \ 84 | ret = allocate_pages_ex(spec); \ 85 | ALLOCATION_TRACE(ret, (spec)->pages, "pages"); \ 86 | ret; \ 87 | }) 88 | 89 | #define allocate_pages_with_flags(count, flags) ({ \ 90 | void *ret; \ 91 | ret = allocate_pages_with_flags(count, flags); \ 92 | ALLOCATION_TRACE(ret, count, "pages"); \ 93 | ret; \ 94 | }) 95 | 96 | #define allocate_pages(count) ({ \ 97 | void *ret; \ 98 | ret = allocate_pages(count); \ 99 | ALLOCATION_TRACE(ret, count, "pages"); \ 100 | ret; \ 101 | }) 102 | 103 | #define allocate_critical_pages(count) ({ \ 104 | void *ret; \ 105 | ret = allocate_critical_pages(count); \ 106 | ALLOCATION_TRACE(ret, count, "pages"); \ 107 | ret; \ 108 | }) 109 | 110 | #define allocate_bytes(count) ({ \ 111 | void *ret; \ 112 | ret = allocate_bytes(count); \ 113 | ALLOCATION_TRACE(ret, count, "bytes"); \ 114 | ret; \ 115 | }) 116 | 117 | #define allocate_critical_bytes(count) ({ \ 118 | void *ret; \ 119 | ret = allocate_critical_bytes(count); \ 120 | ALLOCATION_TRACE(ret, count, "bytes"); \ 121 | ret; \ 122 | }) 123 | 124 | #define free_pages(addr, count) ({ \ 125 | FREE_TRACE(addr, count, "pages"); \ 126 | free_pages(addr, count); \ 127 | }) 128 | 129 | #define free_bytes(addr, count) ({ \ 130 | FREE_TRACE(addr, count, "bytes"); \ 131 | free_bytes(addr, count); \ 132 | }) 133 | #endif 134 | -------------------------------------------------------------------------------- /loader/include/apm.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common/types.h" 4 | 5 | struct apm_info { 6 | u16 version; 7 | u16 flags; 8 | 9 | u16 pm_code_segment; 10 | u16 pm_code_segment_length; 11 | u32 pm_offset; 12 | 13 | u16 rm_code_segment; 14 | u16 rm_code_segment_length; 15 | 16 | u16 data_segment; 17 | u16 data_segment_length; 18 | }; 19 | -------------------------------------------------------------------------------- /loader/include/boot_protocol.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common/string_view.h" 4 | #include "common/attributes.h" 5 | #include "common/helpers.h" 6 | 7 | #include "config.h" 8 | 9 | typedef struct boot_protocol *boot_protocol_entry; 10 | 11 | #define DECLARE_BOOT_PROTOCOL(type) \ 12 | static boot_protocol_entry CONCAT(type, hook) \ 13 | CTOR_SECTION(boot_protocols) USED = &type 14 | 15 | struct boot_protocol { 16 | struct string_view name; 17 | u64 *known_mm_types; 18 | NORETURN void (*boot) (struct config*, struct loadable_entry*); 19 | }; 20 | 21 | NORETURN 22 | void boot(struct config*, struct loadable_entry*); 23 | -------------------------------------------------------------------------------- /loader/include/boot_protocol/ultra_impl.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common/types.h" 4 | #include "filesystem/path.h" 5 | #include "handover.h" 6 | #include "elf.h" 7 | #include "virtual_memory.h" 8 | 9 | struct binary_options { 10 | struct full_path path; 11 | struct filesystem *fs; 12 | bool allocate_anywhere; 13 | }; 14 | 15 | u32 ultra_get_flags_for_binary_options(struct binary_options *bo, 16 | enum elf_arch arch); 17 | 18 | struct kernel_info { 19 | struct binary_options bin_opts; 20 | struct elf_binary_info bin_info; 21 | struct file *binary; 22 | 23 | bool is_higher_half; 24 | struct handover_info hi; 25 | }; 26 | 27 | enum pt_constraint { 28 | PT_CONSTRAINT_AT_LEAST, 29 | PT_CONSTRAINT_EXACTLY, 30 | PT_CONSTRAINT_MAX, 31 | }; 32 | 33 | u64 ultra_higher_half_base(u32 flags); 34 | u64 ultra_higher_half_size(u32 flags); 35 | u64 ultra_direct_map_base(u32 flags); 36 | u64 ultra_max_binary_address(u32 flags); 37 | bool ultra_should_map_high_memory(u32 flags); 38 | 39 | u64 ultra_adjust_direct_map_min_size(u64 direct_map_min_size, u32 flags); 40 | u64 ultra_adjust_direct_map_min_size_for_lower_half(u64 direct_map_min_size, 41 | u32 flags); 42 | 43 | bool ultra_configure_pt_type(struct handover_info *hi, u8 pt_levels, 44 | enum pt_constraint constraint, 45 | enum pt_type *out_type); 46 | 47 | 48 | -------------------------------------------------------------------------------- /loader/include/common/align.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "arch/constants.h" 4 | 5 | #define ALIGN_UP_MASK(x, mask) (((x) + (mask)) & ~(mask)) 6 | #define ALIGN_UP(x, val) ALIGN_UP_MASK(x, (typeof(x))(val) - 1) 7 | 8 | #define ALIGN_DOWN_MASK(x, mask) ((x) & ~(mask)) 9 | #define ALIGN_DOWN(x, val) ALIGN_DOWN_MASK(x, (typeof(x))(val) - 1) 10 | 11 | #define IS_ALIGNED_MASK(x, mask) (((x) & (mask)) == 0) 12 | #define IS_ALIGNED(x, val) IS_ALIGNED_MASK(x, (typeof(x))(val) - 1) 13 | 14 | #define PAGE_ROUND_UP(size) ALIGN_UP(size, PAGE_SIZE) 15 | #define PAGE_ROUND_DOWN(size) ALIGN_DOWN(size, PAGE_SIZE) 16 | 17 | #define BIT_MASK(start_bit, end_bit) \ 18 | (((1ull << ((end_bit) - (start_bit))) - 1) << start_bit) 19 | -------------------------------------------------------------------------------- /loader/include/common/attributes.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #define PACKED __attribute__((packed)) 6 | #define WEAK __attribute__((weak)) 7 | 8 | #define NORETURN __attribute__((__noreturn__)) 9 | 10 | #ifdef __clang__ 11 | #define PRINTF_DECL(fmt_idx, args_idx) __attribute__((format(printf, fmt_idx, args_idx))) 12 | #elif defined(__GNUC__) 13 | #define PRINTF_DECL(fmt_idx, args_idx) __attribute__((format(gnu_printf, fmt_idx, args_idx))) 14 | #else 15 | #define PRINTF_DECL(fmt_idx, args_idx) 16 | #endif 17 | 18 | #define USED __attribute__((used)) 19 | 20 | #define ALWAYS_INLINE inline __attribute__((always_inline)) 21 | 22 | #define ERROR_EMITTER(msg) __attribute__((__error__(msg))) 23 | 24 | #define STRING_SECTION(name) __attribute__((section(name))) 25 | #define SECTION(name) STRING_SECTION(TO_STR(name)) 26 | 27 | #if defined(_MSC_VER) 28 | #define MSVC_WRAP_CTOR_SECTION(name, letter) \ 29 | ".rdata$" TO_STR(name) "_" TO_STR(letter) 30 | 31 | #define CTOR_SECTION(name) STRING_SECTION(MSVC_WRAP_CTOR_SECTION(name, b)) 32 | 33 | #define CTOR_SECTION_DEFINE_ITERATOR(type, section) \ 34 | __declspec(allocate(MSVC_WRAP_CTOR_SECTION(section, a))) \ 35 | static unsigned PASTE(section, _guard_label_begin) USED = 0xDEADBEEF; \ 36 | \ 37 | __declspec(allocate(MSVC_WRAP_CTOR_SECTION(section, a))) \ 38 | type PASTE(section, _begin)[] USED = {}; \ 39 | \ 40 | __declspec(allocate(MSVC_WRAP_CTOR_SECTION(section, c))) \ 41 | type PASTE(section, _end)[] USED = {}; \ 42 | \ 43 | __declspec(allocate(MSVC_WRAP_CTOR_SECTION(section, c))) \ 44 | static unsigned PASTE(section, _guard_lable_end) USED = 0xCAFEBABE; 45 | #else 46 | #define CTOR_SECTION_DEFINE_ITERATOR(type, section) \ 47 | extern type PASTE(section, _begin)[]; \ 48 | extern type PASTE(section, _end)[]; 49 | 50 | #define CTOR_SECTION(name) STRING_SECTION("." TO_STR(name)) 51 | #endif 52 | 53 | #if __has_attribute(__fallthrough__) 54 | #define FALLTHROUGH __attribute__((__fallthrough__)) 55 | #else 56 | #define FALLTHROUGH do {} while (0) 57 | #endif 58 | -------------------------------------------------------------------------------- /loader/include/common/bug.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "panic.h" 4 | 5 | #define BUG() panic("BUG! At %s() in file %s:%d\n", __func__, __FILE__, __LINE__) 6 | #define DIE() panic("Unrecoverable error! At %s() in file %s:%d\n", __func__, __FILE__, __LINE__) 7 | 8 | #define BUG_ON(expr) \ 9 | do { \ 10 | if (unlikely(expr)) \ 11 | BUG(); \ 12 | } while (0) 13 | 14 | #define DIE_ON(expr) \ 15 | do { \ 16 | if (unlikely(expr)) \ 17 | DIE(); \ 18 | } while (0) 19 | 20 | #define DIE_UNLESS(expr) \ 21 | do { \ 22 | if (unlikely(!(expr))) \ 23 | DIE(); \ 24 | } while (0) 25 | -------------------------------------------------------------------------------- /loader/include/common/constants.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define KB ((unsigned long)1024) 4 | #define MB ((unsigned long)1024 * KB) 5 | #define GB ((unsigned long)1024 * MB) 6 | -------------------------------------------------------------------------------- /loader/include/common/conversions.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "string_view.h" 4 | #include "types.h" 5 | 6 | bool str_to_i64_with_base(struct string_view str, i64 *res, unsigned int base); 7 | bool str_to_u64_with_base(struct string_view str, u64 *res, unsigned int base); 8 | bool str_to_i32_with_base(struct string_view str, i32 *res, unsigned int base); 9 | bool str_to_u32_with_base(struct string_view str, u32 *res, unsigned int base); 10 | bool str_to_i16_with_base(struct string_view str, i16 *res, unsigned int base); 11 | bool str_to_u16_with_base(struct string_view str, u16 *res, unsigned int base); 12 | bool str_to_i8_with_base(struct string_view str, i8 *res, unsigned int base); 13 | bool str_to_u8_with_base(struct string_view str, u8 *res, unsigned int base); 14 | 15 | static inline bool str_to_i64(struct string_view str, i64 *res) 16 | { 17 | return str_to_i64_with_base(str, res, 0); 18 | } 19 | 20 | static inline bool str_to_u64(struct string_view str, u64 *res) 21 | { 22 | return str_to_u64_with_base(str, res, 0); 23 | } 24 | 25 | static inline bool str_to_i32(struct string_view str, i32 *res) 26 | { 27 | return str_to_i32_with_base(str, res, 0); 28 | } 29 | 30 | static inline bool str_to_u32(struct string_view str, u32 *res) 31 | { 32 | return str_to_u32_with_base(str, res, 0); 33 | } 34 | 35 | static inline bool str_to_i16(struct string_view str, i16 *res) 36 | { 37 | return str_to_i16_with_base(str, res, 0); 38 | } 39 | 40 | static inline bool str_to_u16(struct string_view str, u16 *res) 41 | { 42 | return str_to_u16_with_base(str, res, 0); 43 | } 44 | 45 | static inline bool str_to_i8(struct string_view str, i8 *res) 46 | { 47 | return str_to_i8_with_base(str, res, 0); 48 | } 49 | 50 | static inline bool str_to_u8(struct string_view str, u8 *res) 51 | { 52 | return str_to_u8_with_base(str, res, 0); 53 | } 54 | -------------------------------------------------------------------------------- /loader/include/common/ctype.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "types.h" 4 | #include "bug.h" 5 | 6 | #define LOWER_TO_UPPER_OFFSET ('a' - 'A') 7 | BUILD_BUG_ON(LOWER_TO_UPPER_OFFSET < 0); 8 | 9 | static inline bool isupper(char c) 10 | { 11 | return c >= 'A' && c <= 'Z'; 12 | } 13 | 14 | static inline bool islower(char c) 15 | { 16 | return c >= 'a' && c <= 'z'; 17 | } 18 | 19 | static inline bool isalnum(char c) 20 | { 21 | return (c >= 'A' && c <= 'Z') || 22 | (c >= 'a' && c <= 'z') || 23 | (c >= '0' && c <= '9'); 24 | } 25 | 26 | static inline char tolower(char c) 27 | { 28 | if (isupper(c)) 29 | return c + LOWER_TO_UPPER_OFFSET; 30 | 31 | return c; 32 | } 33 | 34 | static inline void str_tolower(char* str, size_t size) 35 | { 36 | size_t i; 37 | 38 | for (i = 0; i < size; ++i) 39 | str[i] = tolower(str[i]); 40 | } 41 | 42 | static inline char toupper(char c) 43 | { 44 | if (islower(c)) 45 | return c - LOWER_TO_UPPER_OFFSET; 46 | 47 | return c; 48 | } 49 | 50 | static inline void str_toupper(char* str, size_t size) 51 | { 52 | size_t i; 53 | 54 | for (i = 0; i < size; ++i) 55 | str[i] = toupper(str[i]); 56 | } 57 | -------------------------------------------------------------------------------- /loader/include/common/dynamic_buffer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common/types.h" 4 | #include "common/constants.h" 5 | #include "bug.h" 6 | #include "allocator.h" 7 | 8 | struct dynamic_buffer { 9 | size_t size; 10 | size_t capacity; 11 | size_t elem_size; 12 | void *buf; 13 | }; 14 | 15 | #define DYNAMIC_BUFFER_GROWTH_INCREMENT PAGE_SIZE 16 | 17 | bool dynamic_buffer_grow(struct dynamic_buffer *db); 18 | 19 | static inline bool dynamic_buffer_init(struct dynamic_buffer *db, size_t elem_size, bool lazy) 20 | { 21 | BUG_ON(elem_size == 0); 22 | BUG_ON(elem_size > DYNAMIC_BUFFER_GROWTH_INCREMENT); 23 | 24 | db->elem_size = elem_size; 25 | db->size = 0; 26 | db->capacity = 0; 27 | db->buf = NULL; 28 | 29 | return lazy ? true : dynamic_buffer_grow(db); 30 | } 31 | 32 | static inline void *dynamic_buffer_get_slot(struct dynamic_buffer *db, size_t i) 33 | { 34 | BUG_ON(i >= db->size); 35 | return db->buf + (i * db->elem_size); 36 | } 37 | 38 | static inline void dynamic_buffer_release(struct dynamic_buffer *db) 39 | { 40 | if (!db->capacity) 41 | return; 42 | 43 | free_bytes(db->buf, db->elem_size * db->capacity); 44 | db->size = db->capacity = 0; 45 | db->buf = NULL; 46 | } 47 | 48 | static inline void *dynamic_buffer_slot_alloc(struct dynamic_buffer *db) 49 | { 50 | if (db->size == db->capacity && !dynamic_buffer_grow(db)) 51 | return false; 52 | 53 | return dynamic_buffer_get_slot(db, db->size++); 54 | } 55 | -------------------------------------------------------------------------------- /loader/include/common/format.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "attributes.h" 5 | #include "types.h" 6 | 7 | int vsnprintf(char *restrict buffer, size_t capacity, const char *fmt, va_list vlist); 8 | 9 | static inline int vscnprintf(char *restrict buffer, size_t capacity, const char *fmt, va_list vlist) 10 | { 11 | int would_have_been_written = vsnprintf(buffer, capacity, fmt, vlist); 12 | 13 | if (would_have_been_written < 0) 14 | return would_have_been_written; 15 | if ((size_t)would_have_been_written < capacity) 16 | return would_have_been_written; 17 | 18 | return capacity ? capacity - 1 : 0; 19 | } 20 | 21 | PRINTF_DECL(3, 4) 22 | static inline int snprintf(char *restrict buffer, size_t capacity, const char *fmt, ...) 23 | { 24 | va_list list; 25 | int written; 26 | va_start(list, fmt); 27 | written = vsnprintf(buffer, capacity, fmt, list); 28 | va_end(list); 29 | 30 | return written; 31 | } 32 | 33 | PRINTF_DECL(3, 4) 34 | static inline int scnprintf(char *restrict buffer, size_t capacity, const char *fmt, ...) 35 | { 36 | va_list list; 37 | int written; 38 | va_start(list, fmt); 39 | written = vscnprintf(buffer, capacity, fmt, list); 40 | va_end(list); 41 | 42 | return written; 43 | } 44 | -------------------------------------------------------------------------------- /loader/include/common/hardened_string.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "attributes.h" 3 | 4 | ERROR_EMITTER("attempted an out of bounds read (invalid src size)") 5 | void emit_out_of_bounds_read_compile_error(void); 6 | 7 | ERROR_EMITTER("attempted an out of bounds write (invalid dest size)") 8 | void emit_out_of_bounds_write_compile_error(void); 9 | 10 | NORETURN 11 | void die_on_runtime_oob(const char *func, const char *file, size_t line, 12 | size_t size, size_t dst_size, size_t src_size); 13 | 14 | #define HARDENED_CHECK_OOB_DST_OR_SRC() \ 15 | do { \ 16 | size_t dest_size = __builtin_object_size(dest, 1); \ 17 | size_t src_size = __builtin_object_size(src, 1); \ 18 | \ 19 | if (__builtin_constant_p(count)) { \ 20 | if (dest_size < count) \ 21 | emit_out_of_bounds_write_compile_error(); \ 22 | if (src_size < count) \ 23 | emit_out_of_bounds_read_compile_error(); \ 24 | } \ 25 | \ 26 | if (dest_size < count || src_size < count) \ 27 | die_on_runtime_oob(__FUNCTION__, file, line, count, \ 28 | dest_size, src_size); \ 29 | } while (0) 30 | 31 | 32 | static ALWAYS_INLINE void *hardened_memcpy(void *dest, const void *src, size_t count, 33 | const char *file, size_t line) 34 | { 35 | HARDENED_CHECK_OOB_DST_OR_SRC(); 36 | return __builtin_memcpy(dest, src, count); 37 | } 38 | #define memcpy(dest, src, count) hardened_memcpy(dest, src, count, __FILE__, __LINE__) 39 | 40 | static ALWAYS_INLINE void *hardened_memmove(void *dest, const void *src, size_t count, 41 | const char *file, size_t line) 42 | { 43 | HARDENED_CHECK_OOB_DST_OR_SRC(); 44 | return __builtin_memmove(dest, src, count); 45 | } 46 | #define memmove(dest, src, count) hardened_memmove(dest, src, count, __FILE__, __LINE__) 47 | 48 | static ALWAYS_INLINE void *hardened_memset(void *dest, int val, size_t count, 49 | const char *file, size_t line) 50 | { 51 | size_t dest_size = __builtin_object_size(dest, 1); 52 | 53 | if (__builtin_constant_p(count) && dest_size < count) 54 | emit_out_of_bounds_write_compile_error(); 55 | 56 | if (dest_size < count) 57 | die_on_runtime_oob(__FUNCTION__, file, line, count, dest_size, 0); 58 | 59 | return __builtin_memset(dest, val, count); 60 | } 61 | #define memset(dest, val, count) hardened_memset(dest, val, count, __FILE__, __LINE__) 62 | 63 | static ALWAYS_INLINE int hardened_memcmp(const void *dest, const void *src, size_t count, 64 | const char *file, size_t line) 65 | { 66 | HARDENED_CHECK_OOB_DST_OR_SRC(); 67 | return __builtin_memcmp(dest, src, count); 68 | } 69 | #define memcmp(lhs, rhs, count) hardened_memcmp(lhs, rhs, count, __FILE__, __LINE__) 70 | 71 | static ALWAYS_INLINE size_t hardened_strlen(const char *str, 72 | const char *file, size_t line) 73 | { 74 | size_t str_size, ret; 75 | 76 | str_size = __builtin_object_size(str, 1); 77 | ret = __builtin_strlen(str); 78 | 79 | // FIXME: use strnlen so that we guarantee no OOB access for known lengths 80 | if (str_size <= ret) 81 | die_on_runtime_oob(__FUNCTION__, file, line, ret, str_size, 0); 82 | 83 | return ret; 84 | } 85 | #define strlen(str) hardened_strlen(str, __FILE__, __LINE__) 86 | 87 | static ALWAYS_INLINE void *hardened_memzero(void *dest, size_t count, 88 | const char *file, size_t line) 89 | { 90 | return hardened_memset(dest, 0, count, file, line); 91 | } 92 | #define memzero(dest, count) hardened_memzero(dest, count, __FILE__, __LINE__) 93 | -------------------------------------------------------------------------------- /loader/include/common/helpers.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define DO_CONCAT(x, y) x##y 4 | #define CONCAT(x, y) DO_CONCAT(x, y) 5 | #define UNIQUE(x) CONCAT(x, __COUNTER__) 6 | 7 | #define DO_PASTE(x, y) x##y 8 | #define PASTE(x, y) DO_PASTE(x, y) 9 | 10 | #define DO_TO_STR(x) #x 11 | #define TO_STR(x) DO_TO_STR(x) 12 | 13 | #define ARE_SAME_TYPE(x, y) __builtin_types_compatible_p(typeof(x), typeof(y)) 14 | 15 | #define DO_CONTAINER_OF(ptr, ptr_name, type, member) ({ \ 16 | char *ptr_name = (char*)(ptr); \ 17 | BUILD_BUG_ON(!ARE_SAME_TYPE(*(ptr), ((type*)sizeof(type))->member) && \ 18 | !ARE_SAME_TYPE(*(ptr), void)); \ 19 | ((type*)(ptr_name - offsetof(type, member))); }) 20 | 21 | #define container_of(ptr, type, member) DO_CONTAINER_OF(ptr, UNIQUE(uptr), type, member) 22 | 23 | #define likely(expr) __builtin_expect(!!(expr), 1) 24 | #define unlikely(expr) __builtin_expect(!!(expr), 0) 25 | 26 | #define CEILING_DIVIDE(x, y) (!!(x) + (((x) - !!(x)) / (y))) 27 | 28 | #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) 29 | 30 | #define UNUSED(x) (void)(x) 31 | 32 | #define BUILD_BUG_ON_WITH_MSG(expr, msg) _Static_assert(!(expr), msg) 33 | #define BUILD_BUG_ON(expr) BUILD_BUG_ON_WITH_MSG(expr, "BUILD BUG: " #expr " evaluated to true") 34 | -------------------------------------------------------------------------------- /loader/include/common/log.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "attributes.h" 5 | 6 | // Inspired by linux kern_levels 7 | #define LOG_LEVEL_PREFIX "\x1" 8 | #define LOG_INFO LOG_LEVEL_PREFIX "1" 9 | #define LOG_WARN LOG_LEVEL_PREFIX "2" 10 | #define LOG_ERR LOG_LEVEL_PREFIX "3" 11 | 12 | enum log_level { 13 | LOG_LEVEL_INFO = 1, 14 | LOG_LEVEL_WARN = 2, 15 | LOG_LEVEL_ERR = 3, 16 | }; 17 | 18 | enum log_level logger_set_level(enum log_level level); 19 | 20 | void logger_init(void); 21 | 22 | void vprintlvl(enum log_level, const char *msg, va_list vlist); 23 | void vprint(const char *msg, va_list vlist); 24 | 25 | PRINTF_DECL(2, 3) 26 | void printlvl(enum log_level, const char *msg, ...); 27 | 28 | PRINTF_DECL(1, 2) 29 | void print(const char *msg, ...); 30 | 31 | #ifndef MSG_FMT 32 | #define MSG_FMT(msg) msg 33 | #endif 34 | 35 | 36 | #ifdef HYPER_STRIP_INFO_LOG 37 | PRINTF_DECL(1, 2) 38 | static inline void print_info(const char *msg, ...) 39 | { 40 | UNUSED(msg); 41 | } 42 | #else 43 | #define print_info(msg, ...) print((LOG_INFO MSG_FMT(msg)), ##__VA_ARGS__) 44 | #endif 45 | 46 | #define print_dbg(cond, msg, ...) \ 47 | if (cond) \ 48 | print_info(msg, __VA_ARGS__) 49 | 50 | #define print_warn(msg, ...) print((LOG_WARN MSG_FMT(msg)), ##__VA_ARGS__) 51 | #define print_err(msg, ...) print((LOG_ERR MSG_FMT(msg)), ##__VA_ARGS__) 52 | -------------------------------------------------------------------------------- /loader/include/common/minmax.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "helpers.h" 4 | 5 | #define COMPARE(x, y, op) ((x) op (y) ? (x) : (y)) 6 | 7 | #define RUNTIME_COMPARE(x, y, x_name, y_name, op) ({ \ 8 | typeof(x) x_name = x; \ 9 | typeof(y) y_name = y; \ 10 | COMPARE(x_name, y_name, op); \ 11 | }) 12 | 13 | #define DO_COMPARE(x, y, op) \ 14 | __builtin_choose_expr(__builtin_constant_p(x) && __builtin_constant_p(y), \ 15 | COMPARE(x, y, op), \ 16 | RUNTIME_COMPARE(x, y, CONCAT(ux, __COUNTER__), CONCAT(uy, __COUNTER__), op)) 17 | 18 | #define MIN(x, y) DO_COMPARE(x, y, <) 19 | #define MAX(x, y) DO_COMPARE(x, y, >) 20 | -------------------------------------------------------------------------------- /loader/include/common/panic.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "attributes.h" 4 | 5 | NORETURN 6 | PRINTF_DECL(1, 2) 7 | void panic(const char *reason, ...); 8 | 9 | NORETURN 10 | PRINTF_DECL(1, 2) 11 | void oops(const char *reason, ...); 12 | 13 | #define OOPS_ON(expr) \ 14 | do { \ 15 | if (unlikely(expr)) \ 16 | oops(#expr " evaluated to true\n"); \ 17 | } while (0) 18 | -------------------------------------------------------------------------------- /loader/include/common/range.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "types.h" 4 | 5 | struct range { 6 | u64 begin; 7 | u64 end; 8 | }; 9 | 10 | static inline void range_advance_begin(struct range *range, u64 by) 11 | { 12 | range->begin += by; 13 | } 14 | 15 | static inline void range_set_length(struct range *range, u64 length) 16 | { 17 | range->end = range->begin + length; 18 | } 19 | 20 | static inline u64 range_length(struct range *range) 21 | { 22 | return range->end - range->begin; 23 | } 24 | -------------------------------------------------------------------------------- /loader/include/common/rw_helpers.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "types.h" 3 | 4 | u32 read_u32(void *ptr); 5 | u64 read_u32_zero_extend(void *ptr); 6 | u64 read_u64(void *ptr); 7 | 8 | void write_u32(void *ptr, u32 val); 9 | void write_u64(void *ptr, u64 val); 10 | 11 | void write_u32_u64(void *ptr, u64 val); 12 | void write_u32_checked_u64(void *ptr, u64 val); 13 | -------------------------------------------------------------------------------- /loader/include/common/string.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "types.h" 4 | #include "attributes.h" 5 | 6 | #if __has_include("platform_config.h") 7 | #include "platform_config.h" 8 | #else 9 | #define PLATFORM_WANTS_GENERIC_STRING 10 | #endif 11 | 12 | #ifdef PLATFORM_HAS_MEMCPY 13 | #define MEMCPY_FUNC memcpy_generic 14 | #else 15 | #define MEMCPY_FUNC memcpy 16 | #endif 17 | 18 | #ifdef PLATFORM_HAS_MEMMOVE 19 | #define MEMMOVE_FUNC memmove_generic 20 | #else 21 | #define MEMMOVE_FUNC memmove 22 | #endif 23 | 24 | #ifdef PLATFORM_HAS_MEMSET 25 | #define MEMSET_FUNC memset_generic 26 | #else 27 | #define MEMSET_FUNC memset 28 | #endif 29 | 30 | #ifdef PLATFORM_HAS_MEMCMP 31 | #define MEMCMP_FUNC memcmp_generic 32 | #else 33 | #define MEMCMP_FUNC memcmp 34 | #endif 35 | 36 | #ifdef PLATFORM_HAS_STRLEN 37 | #define STRLEN_FUNC strlen_generic 38 | #else 39 | #define STRLEN_FUNC strlen 40 | #endif 41 | 42 | #ifdef PLATFORM_WANTS_GENERIC_STRING 43 | void *MEMCPY_FUNC(void *dest, const void *src, size_t count); 44 | void *MEMMOVE_FUNC(void *dest, const void *src, size_t count); 45 | void *MEMSET_FUNC(void *dest, int ch, size_t count); 46 | int MEMCMP_FUNC(const void *lhs, const void *rhs, size_t count); 47 | size_t STRLEN_FUNC(const char *str); 48 | #endif 49 | 50 | #ifdef HARDENED_STRING 51 | #include "hardened_string.h" 52 | #else 53 | #define memcpy __builtin_memcpy 54 | #define memmove __builtin_memmove 55 | #define memset __builtin_memset 56 | #define memcmp __builtin_memcmp 57 | #define strlen __builtin_strlen 58 | 59 | static ALWAYS_INLINE void *memzero(void *dest, size_t count) 60 | { 61 | return memset(dest, 0, count); 62 | } 63 | #endif 64 | -------------------------------------------------------------------------------- /loader/include/common/string_view.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common/types.h" 4 | #include "common/bug.h" 5 | #include "common/string.h" 6 | 7 | struct string_view { 8 | const char *text; 9 | size_t size; 10 | }; 11 | 12 | #define SV_CONSTEXPR(str) (struct string_view) { (str), sizeof((str)) - 1 } 13 | #define SV(str) \ 14 | __builtin_choose_expr(__builtin_constant_p((str)), \ 15 | SV_CONSTEXPR((str)), \ 16 | (struct string_view) { (str), strlen((str)) }) 17 | 18 | bool sv_equals(struct string_view lhs, struct string_view rhs); 19 | bool sv_equals_caseless(struct string_view lhs, struct string_view rhs); 20 | bool sv_starts_with(struct string_view str, struct string_view prefix); 21 | ssize_t sv_find(struct string_view str, struct string_view needle, size_t starting_at); 22 | 23 | static inline bool sv_empty(struct string_view str) 24 | { 25 | return str.size == 0; 26 | } 27 | 28 | static inline bool sv_contains(struct string_view str, struct string_view needle) 29 | { 30 | return sv_find(str, needle, 0) >= 0; 31 | } 32 | 33 | static inline void sv_offset_by(struct string_view *str, size_t value) 34 | { 35 | BUG_ON(str->size < value); 36 | str->text += value; 37 | str->size -= value; 38 | } 39 | 40 | static inline void sv_extend_by(struct string_view *str, size_t value) 41 | { 42 | BUG_ON(!str->text); 43 | str->size += value; 44 | } 45 | 46 | static inline void sv_clear(struct string_view *str) 47 | { 48 | str->text = NULL; 49 | str->size = 0; 50 | } 51 | 52 | static inline bool sv_pop_one(struct string_view *str, char *c) 53 | { 54 | if (sv_empty(*str)) 55 | return false; 56 | 57 | *c = str->text[0]; 58 | sv_offset_by(str, 1); 59 | return true; 60 | } 61 | 62 | static inline void sv_terminated_copy(char *dst, struct string_view sv) 63 | { 64 | memcpy(dst, sv.text, sv.size); 65 | dst[sv.size] = '\0'; 66 | } 67 | -------------------------------------------------------------------------------- /loader/include/common/types.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "helpers.h" 8 | 9 | // signed types 10 | typedef int8_t i8; 11 | typedef int16_t i16; 12 | typedef int i32; 13 | typedef int64_t i64; 14 | 15 | // unsigned types 16 | typedef uint8_t u8; 17 | typedef uint16_t u16; 18 | typedef unsigned int u32; 19 | typedef uint64_t u64; 20 | 21 | typedef size_t ptr_t; 22 | #define ADDR_TO_PTR(addr) ((void*)((ptr_t)(addr))) 23 | 24 | #if defined(__x86_64__) || defined(__aarch64__) 25 | typedef i64 ssize_t; 26 | #elif defined(__i386__) 27 | typedef i32 ssize_t; 28 | #else 29 | #error unknown architecture 30 | #endif 31 | 32 | BUILD_BUG_ON(sizeof(i8) != 1); 33 | BUILD_BUG_ON(sizeof(i16) != 2); 34 | BUILD_BUG_ON(sizeof(i32) != 4); 35 | BUILD_BUG_ON(sizeof(i64) != 8); 36 | 37 | BUILD_BUG_ON(sizeof(u8) != 1); 38 | BUILD_BUG_ON(sizeof(u16) != 2); 39 | BUILD_BUG_ON(sizeof(u32) != 4); 40 | BUILD_BUG_ON(sizeof(u64) != 8); 41 | 42 | BUILD_BUG_ON(sizeof(bool) != 1); 43 | -------------------------------------------------------------------------------- /loader/include/disk_services.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common/types.h" 4 | 5 | #define DISK_STS_REMOVABLE (1 << 0) 6 | 7 | struct disk { 8 | u64 sectors; 9 | void *handle; 10 | u32 id; 11 | 12 | u8 block_shift; 13 | u8 status; 14 | }; 15 | 16 | static inline u32 disk_block_size(const struct disk *d) 17 | { 18 | return 1 << d->block_shift; 19 | } 20 | 21 | /* 22 | * Number of disks that can be queried. 23 | */ 24 | u32 ds_get_disk_count(void); 25 | 26 | /* 27 | * Retrieves information about a disk at idx. 28 | * idx -> disk to retrieve. 29 | * out_disk -> pointer to data that receives disk information. 30 | */ 31 | void ds_query_disk(size_t idx, struct disk *out_disk); 32 | 33 | /* 34 | * Reads byte aligned data from disk. 35 | * handle -> one of disk handles returned by list_disks. 36 | * buffer -> first byte of the buffer that receives data. 37 | * offset -> byte offset of where to start reading. 38 | * bytes -> number of bytes to read. 39 | * Returns true if data was read successfully, false otherwise. 40 | */ 41 | bool ds_read(void *handle, void *buffer, u64 offset, size_t bytes); 42 | 43 | /* 44 | * Reads sectors from a disk. 45 | * handle -> one of disk handles returned by list_disks. 46 | * buffer -> first byte of the buffer that receives data. 47 | * sector -> first sector from which data is read. 48 | * count -> number of sectors to read. 49 | * Returns true if data was read successfully, false otherwise. 50 | */ 51 | bool ds_read_blocks(void *handle, void *buffer, u64 sector, size_t blocks); 52 | -------------------------------------------------------------------------------- /loader/include/edid.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common/types.h" 4 | #include "common/attributes.h" 5 | #include "common/bug.h" 6 | 7 | struct PACKED timing_information { 8 | u8 x_resolution; 9 | u8 vertical_frequency : 6; 10 | u8 aspect_ratio : 2; 11 | }; 12 | 13 | struct PACKED timing_descriptor { 14 | u16 pixel_clock; 15 | u8 horizontal_active_pixels_lo; 16 | u8 horizontal_blanking_pixels_lo; 17 | u8 horizontal_blanking_pixels_hi : 4; 18 | u8 horizontal_active_pixels_hi : 4; 19 | u8 vertical_active_lines_lo; 20 | u8 vertical_blanking_lines_lo; 21 | u8 vertical_blanking_lines_hi : 4; 22 | u8 vertical_active_lines_hi : 4; 23 | u8 horizontal_front_porch; 24 | u8 horizontal_sync_pulse_width; 25 | u8 vertical_sync_pulse_width_lo : 4; 26 | u8 vertical_front_porch_lo : 4; 27 | u8 vertical_sync_pulse_hi : 2; 28 | u8 vertical_front_porch_hi : 2; 29 | u8 horizontal_sync_pulse_width_hi : 2; 30 | u8 horizontal_front_porch_hi : 2; 31 | u8 horizontal_image_size_mm_lo; 32 | u8 vertical_image_size_mm_lo; 33 | u8 verticaL_image_size_mm_hi : 4; 34 | u8 horizontal_image_size_mm_hi : 4; 35 | u8 horizontal_border_pixels_half; 36 | u8 vertical_border_lines_half; 37 | u8 features_bitmap; 38 | }; 39 | 40 | struct PACKED edid { 41 | u8 header[8]; 42 | u16 manufacturer_id; 43 | u16 manufacturer_product_code; 44 | u32 serial_number; 45 | u8 week_of_manufacture; 46 | u8 year_of_manufacture; 47 | u8 edid_version; 48 | u8 edid_revision; 49 | u8 video_input_parameters; 50 | u8 horizontal_screen_size_cm; 51 | u8 vertical_screen_size_cm; 52 | u8 display_gamma; 53 | u8 features_bitmap; 54 | u8 red_green_least_significant_bits; 55 | u8 blue_white_least_significant_bits; 56 | u8 red_x_value_most_significant_bits; 57 | u8 red_y_value_most_significant_bits; 58 | u8 green_x_value_most_significant_bits; 59 | u8 green_y_value_most_significant_bits; 60 | u8 blue_x_value_most_significant_bits; 61 | u8 blue_y_value_most_significant_bits; 62 | u8 default_white_x_point_value_most_significant_bits; 63 | u8 default_white_y_point_value_most_significant_bits; 64 | u8 established_timing_bitmap[3]; 65 | struct timing_information standard_timing_information[8]; 66 | struct timing_descriptor detailed_timing_descriptors[4]; 67 | u8 number_of_extensions; 68 | u8 checksum; 69 | }; 70 | BUILD_BUG_ON(sizeof(struct edid) != 128); 71 | 72 | void edid_get_native_resolution(struct edid *e, size_t *native_width, size_t *native_height); 73 | u8 edid_calculate_checksum(struct edid *e); 74 | -------------------------------------------------------------------------------- /loader/include/elf.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common/types.h" 4 | #include "filesystem/block_cache.h" 5 | 6 | struct file; 7 | 8 | #define ELF_ALLOCATE_ANYWHERE (1 << 0) 9 | #define ELF_USE_VIRTUAL_ADDRESSES (1 << 1) 10 | 11 | struct elf_io { 12 | struct file *binary; 13 | struct block_cache hdr_cache; 14 | }; 15 | 16 | struct elf_load_spec { 17 | struct elf_io io; 18 | 19 | u32 flags; 20 | 21 | u32 memory_type; 22 | u64 binary_ceiling; 23 | u64 higher_half_base; 24 | }; 25 | 26 | enum elf_arch { 27 | ELF_ARCH_INVALID = 0, 28 | ELF_ARCH_I386 = 1, 29 | ELF_ARCH_AMD64 = 2, 30 | ELF_ARCH_AARCH64 = 3, 31 | }; 32 | 33 | struct elf_binary_info { 34 | u64 entrypoint_address; 35 | 36 | u64 virtual_base; 37 | u64 virtual_ceiling; 38 | 39 | u64 physical_base; 40 | u64 physical_ceiling; 41 | 42 | enum elf_arch arch; 43 | }; 44 | 45 | struct elf_error { 46 | const char *reason; 47 | u64 args[3]; 48 | u8 arg_count; 49 | }; 50 | 51 | // Called automatically by elf_load if needed 52 | bool elf_init_io_cache(struct elf_io *io, struct elf_error *err); 53 | 54 | bool elf_load(struct elf_load_spec *spec, 55 | struct elf_binary_info *out_info, 56 | struct elf_error *out_error); 57 | 58 | bool elf_get_arch(struct elf_io *io, enum elf_arch *arch, 59 | struct elf_error *err); 60 | 61 | void elf_pretty_print_error(const struct elf_error *err, const char *prefix); 62 | -------------------------------------------------------------------------------- /loader/include/elf/context.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "structures.h" 4 | 5 | struct elf_load_ph { 6 | Elf64_Addr phys_addr, virt_addr; 7 | Elf64_Xword memsz, filesz; 8 | Elf64_Off fileoff; 9 | }; 10 | 11 | struct elf_ph_info { 12 | Elf64_Half count; 13 | Elf64_Half entsize; 14 | Elf64_Off off; 15 | }; 16 | 17 | struct elf_load_ctx { 18 | struct elf_load_spec *spec; 19 | bool alloc_anywhere; 20 | bool use_va; 21 | struct elf_ph_info ph_info; 22 | struct elf_binary_info *bi; 23 | struct elf_error *err; 24 | }; 25 | 26 | bool elf_is_supported_load_ctx(struct elf_load_ctx *ctx); 27 | -------------------------------------------------------------------------------- /loader/include/elf/machine.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "structures.h" 3 | #include "elf.h" 4 | 5 | bool elf_machine_to_arch(Elf32_Half machine, enum elf_arch *out_arch, 6 | u8 *out_expected_ptr_width); 7 | -------------------------------------------------------------------------------- /loader/include/elf/structures.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common/types.h" 4 | 5 | #define EI_MAG0 0 6 | #define EI_MAG1 1 7 | #define EI_MAG2 2 8 | #define EI_MAG3 3 9 | #define EI_CLASS 4 10 | #define EI_DATA 5 11 | #define EI_VERSION 6 12 | #define EI_OSABI 7 13 | #define EI_ABIVERSION 8 14 | #define EI_PAD 9 15 | #define EI_NIDENT 16 16 | 17 | #define ELFMAG0 0x7f 18 | #define ELFMAG1 'E' 19 | #define ELFMAG2 'L' 20 | #define ELFMAG3 'F' 21 | 22 | #define ELFCLASS32 1 23 | #define ELFCLASS64 2 24 | 25 | #define ELFDATA2LSB 1 26 | #define ELFDATA2MSB 2 27 | 28 | #define EM_386 3 29 | #define EM_AMD64 62 30 | #define EM_AARCH64 183 31 | 32 | #define ET_NONE 0 33 | #define ET_REL 1 34 | #define ET_EXEC 2 35 | #define ET_DYN 3 36 | #define ET_CORE 4 37 | #define ET_LOPROC 0xFF00 38 | #define ET_HIPROC 0xFFFF 39 | 40 | typedef u32 Elf32_Addr; 41 | typedef u16 Elf32_Half; 42 | typedef u32 Elf32_Off; 43 | typedef i32 Elf32_Sword; 44 | typedef u32 Elf32_Word; 45 | 46 | typedef u64 Elf64_Addr; 47 | typedef u16 Elf64_Half; 48 | typedef u64 Elf64_Off; 49 | typedef i32 Elf64_Sword; 50 | typedef u32 Elf64_Word; 51 | typedef u64 Elf64_Xword; 52 | typedef i64 Elf64_Sxword; 53 | 54 | struct Elf32_Ehdr { 55 | unsigned char e_ident[EI_NIDENT]; 56 | Elf32_Half e_type; 57 | Elf32_Half e_machine; 58 | Elf32_Word e_version; 59 | Elf32_Addr e_entry; 60 | Elf32_Off e_phoff; 61 | Elf32_Off e_shoff; 62 | Elf32_Word e_flags; 63 | Elf32_Half e_ehsize; 64 | Elf32_Half e_phentsize; 65 | Elf32_Half e_phnum; 66 | Elf32_Half e_shentsize; 67 | Elf32_Half e_shnum; 68 | Elf32_Half e_shstrndx; 69 | }; 70 | 71 | struct Elf64_Ehdr { 72 | unsigned char e_ident[EI_NIDENT]; 73 | Elf64_Half e_type; 74 | Elf64_Half e_machine; 75 | Elf64_Word e_version; 76 | Elf64_Addr e_entry; 77 | Elf64_Off e_phoff; 78 | Elf64_Off e_shoff; 79 | Elf64_Word e_flags; 80 | Elf64_Half e_ehsize; 81 | Elf64_Half e_phentsize; 82 | Elf64_Half e_phnum; 83 | Elf64_Half e_shentsize; 84 | Elf64_Half e_shnum; 85 | Elf64_Half e_shstrndx; 86 | }; 87 | 88 | #define PT_NULL 0 89 | #define PT_LOAD 1 90 | #define PT_DYNAMIC 2 91 | #define PT_INTERP 3 92 | #define PT_NOTE 4 93 | #define PT_SHLIB 5 94 | #define PT_PHDR 6 95 | #define PT_TLS 7 96 | #define PT_LOOS 0x60000000 97 | #define PT_SUNW_UNWIND 0x6464e550 98 | #define PT_SUNW_EH_FRAME 0x6474e550 99 | #define PT_LOSUNW 0x6ffffffa 100 | #define PT_SUNWBSS 0x6ffffffa 101 | #define PT_SUNWSTACK 0x6ffffffb 102 | #define PT_SUNWDTRACE 0x6ffffffc 103 | #define PT_SUNWCAP 0x6ffffffd 104 | #define PT_HISUNW 0x6fffffff 105 | #define PT_HIOS 0x6fffffff 106 | #define PT_LOPROC 0x70000000 107 | #define PT_HIPROC 0x7fffffff 108 | 109 | #define PN_XNUM 0xFFFF 110 | 111 | struct Elf32_Phdr { 112 | Elf32_Word p_type; 113 | Elf32_Off p_offset; 114 | Elf32_Addr p_vaddr; 115 | Elf32_Addr p_paddr; 116 | Elf32_Word p_filesz; 117 | Elf32_Word p_memsz; 118 | Elf32_Word p_flags; 119 | Elf32_Word p_align; 120 | }; 121 | 122 | struct Elf64_Phdr { 123 | Elf64_Word p_type; 124 | Elf64_Word p_flags; 125 | Elf64_Off p_offset; 126 | Elf64_Addr p_vaddr; 127 | Elf64_Addr p_paddr; 128 | Elf64_Xword p_filesz; 129 | Elf64_Xword p_memsz; 130 | Elf64_Xword p_align; 131 | }; 132 | -------------------------------------------------------------------------------- /loader/include/filesystem/block_cache.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common/types.h" 4 | #include "common/constants.h" 5 | 6 | typedef bool (*block_cache_refill_cb_t)(void *user_ptr, void *buf, u64 block, 7 | size_t count); 8 | 9 | struct block_cache { 10 | block_cache_refill_cb_t refill_blocks_cb; 11 | 12 | void *user_ptr; 13 | 14 | void *cache_buf; 15 | size_t cache_block_cap; 16 | u64 cache_base; 17 | 18 | u32 nocopy_refs; 19 | 20 | u16 block_size; 21 | u8 block_shift; 22 | 23 | #define BC_EMPTY (1 << 0) 24 | #define BC_DIRECT_IO (1 << 1) 25 | u8 flags; 26 | }; 27 | 28 | void block_cache_init(struct block_cache *bc, block_cache_refill_cb_t cb, 29 | void *user_ptr, u8 block_shift, void *cache_buf, 30 | size_t buf_block_cap); 31 | void block_cache_release(struct block_cache *bc); 32 | 33 | /* 34 | * Refill the cache with blocks starting at 'base_block' 35 | */ 36 | bool block_cache_refill(struct block_cache *bc, u64 base_block); 37 | 38 | /* 39 | * Read data at 'byte_off' with 'count' bytes and store in 'buf'. 40 | * bc->refill_blocks_cb() is called as needed to satisfy the request. 41 | */ 42 | bool block_cache_read(struct block_cache *bc, void *buf, u64 byte_off, 43 | size_t count); 44 | 45 | /* 46 | * Read data at 'block' with 'count' blocks and store in 'buf'. 47 | * bc->refill_blocks_cb() is called as needed to satisfy the request. 48 | */ 49 | bool block_cache_read_blocks(struct block_cache *bc, void *buf, u64 block, 50 | size_t count); 51 | 52 | 53 | /* 54 | * Cache data at 'byte_off' with 'count' and return the pointer to the internal 55 | * buffer where the cached data at 'byte_off' is located. 56 | */ 57 | bool block_cache_take_ref(struct block_cache *bc, void **buf, u64 byte_off, 58 | size_t count); 59 | void block_cache_release_ref(struct block_cache *bc); 60 | 61 | // Able to read blocks into a buffer other than 'cache_buf' 62 | static inline void block_cache_enable_direct_io(struct block_cache *bc) 63 | { 64 | bc->flags |= BC_DIRECT_IO; 65 | } 66 | 67 | static inline void *block_cache_get_buf(struct block_cache *bc) 68 | { 69 | return bc->cache_buf; 70 | } 71 | -------------------------------------------------------------------------------- /loader/include/filesystem/bulk_read.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "filesystem.h" 3 | 4 | /* 5 | * Used by the filesystems that allow sparse holes inside files as a space 6 | * optimization. The entire range is considered zero-filled and no read 7 | * request is issued to the block device for such block ranges. 8 | */ 9 | #define BLOCK_RANGE_OFF_HOLE 0xFFFFFFFFFFFFFFFF 10 | 11 | struct block_range { 12 | u64 part_byte_off; 13 | size_t blocks; 14 | }; 15 | 16 | static inline bool is_block_range_hole(struct block_range *br) 17 | { 18 | return br->part_byte_off == BLOCK_RANGE_OFF_HOLE; 19 | } 20 | 21 | static inline void block_range_make_hole(struct block_range *br) 22 | { 23 | br->part_byte_off = BLOCK_RANGE_OFF_HOLE; 24 | } 25 | 26 | /* 27 | * Retrieves a contiguous range of blocks in a file at an offset 28 | * 'file_block_off' up to 'want_blocks' in size (implementations 29 | * are allowed to return a larger block count). 30 | * Blocks are calculated & requested in file system block size, 31 | * disk block size is not taken into account and is handled 32 | * internally by bulk_read_file. 33 | */ 34 | typedef bool (*file_get_range_t)(struct file*, u64 file_block_off, 35 | size_t want_blocks, struct block_range *out); 36 | 37 | bool bulk_read_file(struct file *f, void *buffer, u64 offset, u32 bytes, 38 | file_get_range_t get_range); 39 | -------------------------------------------------------------------------------- /loader/include/filesystem/filesystem.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common/types.h" 4 | #include "common/string_view.h" 5 | #include "common/range.h" 6 | 7 | #include "disk_services.h" 8 | #include "block_cache.h" 9 | 10 | struct filesystem; 11 | 12 | struct file { 13 | struct filesystem *fs; 14 | u64 size; 15 | }; 16 | 17 | struct dir_iter_ctx { 18 | /* 19 | * This is not very good, but it allows us to avoid allocations 20 | * when iterating directories. 21 | */ 22 | _Alignas(u64) 23 | char opaque[4 * sizeof(u64)]; 24 | }; 25 | 26 | #define DIR_REC_MAX_NAME_LEN 255 27 | struct dir_rec { 28 | char name[DIR_REC_MAX_NAME_LEN]; 29 | u8 name_len; 30 | 31 | #define DIR_REC_SUBDIR (1 << 0) 32 | u8 flags; 33 | 34 | u64 size; 35 | 36 | _Alignas(u64) 37 | char opaque[2 * sizeof(u64)]; 38 | }; 39 | 40 | static inline bool dir_rec_is_subdir(struct dir_rec *rec) 41 | { 42 | return rec->flags & DIR_REC_SUBDIR; 43 | } 44 | 45 | struct filesystem { 46 | struct disk d; 47 | struct range lba_range; 48 | u8 block_shift; 49 | 50 | // ctx is initialized from the root directory if 'rec' is NULL. 51 | void (*iter_ctx_init)(struct filesystem *fs, struct dir_iter_ctx *ctx, struct dir_rec *rec); 52 | bool (*next_dir_rec)(struct filesystem *fs, struct dir_iter_ctx *ctx, struct dir_rec *out_rec); 53 | 54 | struct file *(*open_file)(struct filesystem *fs, struct dir_rec *rec); 55 | void (*close_file)(struct file*); 56 | bool (*read_file)(struct file*, void *buffer, u64 offset, u32 bytes); 57 | 58 | void (*release)(struct filesystem *fs); 59 | }; 60 | 61 | typedef 62 | struct filesystem* 63 | (*fs_detect_t)( 64 | const struct disk *d, 65 | struct range lba_range, 66 | struct block_cache *bc 67 | ); 68 | 69 | #define FS_TYPE_CD (1 << 0) 70 | 71 | struct filesystem_type { 72 | struct string_view name; 73 | u32 flags; 74 | fs_detect_t detect; 75 | }; 76 | 77 | typedef struct filesystem_type *filesystem_type_entry; 78 | 79 | #define DECLARE_FILESYSTEM(type) \ 80 | static filesystem_type_entry CONCAT(type, hook) \ 81 | CTOR_SECTION(filesystems) USED = &type 82 | 83 | static inline u8 fs_block_shift(struct filesystem *fs) 84 | { 85 | return fs->block_shift; 86 | } 87 | 88 | static inline u8 file_block_shift(struct file *f) 89 | { 90 | return fs_block_shift(f->fs); 91 | } 92 | 93 | void fs_check_read(struct file *f, u64 offset, u32 size); 94 | void fs_detect_all(struct disk *d, struct block_cache *bc); 95 | 96 | struct filesystem *fs_try_detect(const struct disk *d, struct range lba_range, 97 | struct block_cache *bc); 98 | -------------------------------------------------------------------------------- /loader/include/filesystem/filesystem_table.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "filesystem.h" 4 | #include "guid.h" 5 | #include "path.h" 6 | 7 | enum fse_type { 8 | FSE_TYPE_RAW, 9 | FSE_TYPE_MBR, 10 | FSE_TYPE_GPT 11 | }; 12 | 13 | struct fs_entry { 14 | void *disk_handle; 15 | u32 disk_id; 16 | u32 partition_index; 17 | u16 entry_type; 18 | struct guid disk_guid; 19 | struct guid partition_guid; 20 | struct filesystem *fs; 21 | }; 22 | 23 | void fst_init(void); 24 | 25 | void fst_add_raw_fs_entry(const struct disk *d, struct filesystem*); 26 | 27 | void fst_add_mbr_fs_entry(const struct disk *d, u32 partition_index, 28 | struct filesystem*); 29 | 30 | void fst_add_gpt_fs_entry(const struct disk *d, u32 partition_index, 31 | const struct guid *disk_guid, 32 | const struct guid *partition_guid, 33 | struct filesystem*); 34 | 35 | const struct fs_entry *fst_fs_by_full_path(const struct full_path *path); 36 | 37 | void fst_set_origin(struct fs_entry*); 38 | const struct fs_entry *fst_get_origin(void); 39 | 40 | struct fs_entry *fst_list(size_t *count); 41 | -------------------------------------------------------------------------------- /loader/include/filesystem/gpt.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "disk_services.h" 4 | #include "block_cache.h" 5 | 6 | bool gpt_initialize(const struct disk *d, struct block_cache *bc); 7 | -------------------------------------------------------------------------------- /loader/include/filesystem/guid.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common/string.h" 4 | 5 | struct guid { 6 | u32 data1; 7 | u16 data2; 8 | u16 data3; 9 | u8 data4[8]; 10 | }; 11 | 12 | static inline int guid_compare(const struct guid *lhs, const struct guid *rhs) 13 | { 14 | return memcmp(lhs, rhs, sizeof(*rhs)); 15 | } 16 | -------------------------------------------------------------------------------- /loader/include/filesystem/mbr.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "disk_services.h" 4 | #include "block_cache.h" 5 | 6 | bool mbr_initialize(const struct disk *d, struct block_cache *bc); 7 | -------------------------------------------------------------------------------- /loader/include/filesystem/path.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "filesystem.h" 4 | #include "guid.h" 5 | 6 | #define MAX_PATH_SIZE 255 7 | 8 | enum disk_identifier { 9 | DISK_IDENTIFIER_INVALID, 10 | DISK_IDENTIFIER_INDEX, 11 | DISK_IDENTIFIER_UUID, 12 | DISK_IDENTIFIER_ORIGIN 13 | }; 14 | 15 | enum partition_identifier { 16 | PARTITION_IDENTIFIER_INVALID, 17 | PARTITION_IDENTIFIER_RAW, 18 | PARTITION_IDENTIFIER_INDEX, 19 | PARTITION_IDENTIFIER_UUID, 20 | PARTITION_IDENTIFIER_ORIGIN 21 | }; 22 | 23 | struct full_path { 24 | enum disk_identifier disk_id_type; 25 | 26 | union { 27 | struct guid disk_guid; 28 | u32 disk_index; 29 | }; 30 | 31 | enum partition_identifier partition_id_type; 32 | 33 | union { 34 | struct guid partition_guid; 35 | u32 partition_index; 36 | }; 37 | 38 | struct string_view path_within_partition; 39 | }; 40 | 41 | 42 | bool path_parse(struct string_view path, struct full_path *out_path); 43 | struct file *path_open(struct filesystem *fs, struct string_view path); 44 | -------------------------------------------------------------------------------- /loader/include/handover.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common/types.h" 4 | #include "common/attributes.h" 5 | #include "common/constants.h" 6 | #include "common/bug.h" 7 | #include "arch/handover_flags.h" 8 | #include "virtual_memory.h" 9 | 10 | /* 11 | * Generic handover info structure: 12 | * entrypoint -> address of the kernel binary entry, possibly higher half 13 | * stack -> address of the top of the kernel stack, possibly higher half 14 | * arg0, arg1 -> arguments to pass to the kernel binary entrypoint 15 | * direct_map_base -> base address in the higher half that direct maps at least 16 | * 'handover_get_minimum_map_length()' amount of 17 | * physical memory. 18 | * pt -> page table that will be switched to before handing control to kernel 19 | * flags -> flags that describe the expected system state before 'entrypoint' 20 | * is invoked, some are arch-specific. 21 | * 22 | * Page table is expected to contain at least two mappings, where both linearly 23 | * map physical ram from address zero: 24 | * 0x0000...0000 -> handover_get_minimum_map_length() 25 | * AND 26 | * direct_map_base -> handover_get_minimum_map_length() 27 | */ 28 | struct handover_info { 29 | u64 entrypoint; 30 | u64 stack; 31 | u64 arg0, arg1; 32 | u64 direct_map_base; 33 | 34 | struct page_table pt; 35 | /* 36 | * If set, unmaps the first table or handover_get_minimum_map_length() 37 | * worth of pages from the page table root, whichever one is bigger. 38 | */ 39 | #define HO_HIGHER_HALF_ONLY_BIT 0 40 | #define HO_HIGHER_HALF_ONLY (1 << HO_HIGHER_HALF_ONLY_BIT) 41 | 42 | /* 43 | * Arch-specific flags are described in /include/handover_flags.h 44 | */ 45 | u32 flags; 46 | }; 47 | 48 | u64 handover_get_minimum_map_length(u64 direct_map_base, u32 flags); 49 | u64 handover_get_max_pt_address(u64 direct_map_base, u32 flags); 50 | 51 | /* 52 | * Must be executed before calling 'kernel_handover', expects at least 53 | * the memory services to still be online 54 | */ 55 | void handover_prepare_for(struct handover_info *hi); 56 | 57 | NORETURN 58 | void kernel_handover(struct handover_info*); 59 | 60 | bool handover_is_flag_supported(u32 flag); 61 | void handover_ensure_supported_flags(u32 flags); 62 | -------------------------------------------------------------------------------- /loader/include/handover_impl.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "common/string_view.h" 5 | 6 | extern bool handover_flags_map[32]; 7 | extern struct string_view handover_flags_to_string[32]; 8 | 9 | void initialize_flags_map(void); 10 | -------------------------------------------------------------------------------- /loader/include/hyper.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common/string_view.h" 4 | #include "common/helpers.h" 5 | 6 | #define HYPER_MAJOR 0 7 | #define HYPER_MINOR 10 8 | #define HYPER_PATCH 0 9 | 10 | #define MAKE_BRAND_STRING(mj, mi, pa) SV("HyperLoader v" TO_STR(mj) "." TO_STR(mi) "." TO_STR(pa)) 11 | #define HYPER_BRAND_STRING MAKE_BRAND_STRING(HYPER_MAJOR, HYPER_MINOR, HYPER_PATCH) 12 | -------------------------------------------------------------------------------- /loader/include/services.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common/types.h" 4 | #include "common/attributes.h" 5 | #include "apm.h" 6 | 7 | enum service_provider { 8 | SERVICE_PROVIDER_INVALID, 9 | SERVICE_PROVIDER_BIOS, 10 | SERVICE_PROVIDER_UEFI 11 | }; 12 | enum service_provider services_get_provider(void); 13 | 14 | /* 15 | * Attempts to retrieve the RSDP structure location. 16 | * Returns a 16-byte aligned address of the structure if successful, NULL otherwise. 17 | */ 18 | ptr_t services_find_rsdp(void); 19 | 20 | /* 21 | * Attempts to retrieve the DTB structure location. 22 | * Returns an 8-byte aligned address of the structure if successful, NULL otherwise. 23 | */ 24 | ptr_t services_find_dtb(void); 25 | 26 | /* 27 | * Attempts to retrieve the SMBIOS entry point structure location. 28 | * Returns a 16-byte aligned address of the structure if successful, NULL otherwise. 29 | */ 30 | ptr_t services_find_smbios(void); 31 | 32 | /* 33 | * Attempts to setup the 32-bit protected-mode interface for APM if it exists. 34 | * Returns true if the interface was successfully installed, false otherwise. 35 | */ 36 | bool services_setup_apm(struct apm_info *out_info); 37 | 38 | /* 39 | * Aborts the loader execution in a platform-specific manner. 40 | * Must be used for unrecoverable errors. 41 | */ 42 | NORETURN void loader_abort(void); 43 | 44 | /* 45 | * Platform-agnostic loader entrypoint. 46 | */ 47 | NORETURN void loader_entry(void); 48 | 49 | /* 50 | * Runs all registered cleanup handlers. 51 | * All services aside from memory management & handover are assumed to be 52 | * unusable after this function returns. 53 | */ 54 | void services_cleanup(void); 55 | -------------------------------------------------------------------------------- /loader/include/services_impl.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common/attributes.h" 4 | #include "common/types.h" 5 | 6 | NORETURN 7 | void on_service_use_after_exit(const char *func); 8 | 9 | extern bool services_offline; 10 | 11 | #define SERVICE_FUNCTION() \ 12 | do { \ 13 | if (unlikely(services_offline)) \ 14 | on_service_use_after_exit(__FUNCTION__); \ 15 | } while (0) 16 | 17 | struct memory_map_entry; 18 | void mme_align_if_needed(struct memory_map_entry *me); 19 | bool mme_is_valid(struct memory_map_entry *me); 20 | void mme_insert(struct memory_map_entry *buf, struct memory_map_entry *me, 21 | size_t idx, size_t count); 22 | 23 | void mm_sort(struct memory_map_entry *buf, size_t count); 24 | 25 | 26 | #define FIXUP_UNSORTED (1 << 0) 27 | #define FIXUP_IF_DIRTY (1 << 1) 28 | #define FIXUP_OVERLAP_RESOLVE (1 << 2) 29 | #define FIXUP_OVERLAP_INTENTIONAL (1 << 3) 30 | #define FIXUP_NO_PRESERVE_LOADER_RECLAIM (1 << 4) 31 | 32 | size_t mm_fixup(struct memory_map_entry *buf, size_t count, size_t cap, u8 flags); 33 | 34 | ssize_t mm_find_first_that_contains(struct memory_map_entry *buf, u64 count, 35 | u64 value, bool allow_one_above); 36 | 37 | typedef void (*cleanup_handler)(void); 38 | 39 | #define DECLARE_CLEANUP_HANDLER(handler) \ 40 | static cleanup_handler CONCAT(handler, hook) \ 41 | CTOR_SECTION(cleanup_handlers) USED = &handler 42 | -------------------------------------------------------------------------------- /loader/include/uefi/globals.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "structures.h" 4 | 5 | extern EFI_SYSTEM_TABLE *g_st; 6 | extern EFI_HANDLE g_img; 7 | -------------------------------------------------------------------------------- /loader/include/uefi/helpers.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "structures.h" 4 | #include "common/string_view.h" 5 | 6 | #define unlikely_efi_error(ret) unlikely(EFI_ERROR(ret)) 7 | 8 | bool uefi_pool_alloc(EFI_MEMORY_TYPE type, size_t elem_size, size_t count, VOID **out); 9 | 10 | /* 11 | * The caller is responsible for freeing array with FreePool() 12 | * Count is guaranteed to be >0 if this returns true 13 | * No memory is allocated if this returns false 14 | */ 15 | bool uefi_get_protocol_handles(EFI_GUID *guid, EFI_HANDLE **array, UINTN *count); 16 | 17 | struct string_view uefi_status_to_string(EFI_STATUS sts); 18 | 19 | void *uefi_find_configuration(EFI_GUID *guid); 20 | -------------------------------------------------------------------------------- /loader/include/uefi/relocator.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "structures.h" 6 | 7 | typedef void (*relocated_cb_t)(void *user, u64 addr); 8 | 9 | struct relocation_entry { 10 | // The size field is used if 'end' is set to NULL 11 | union { 12 | struct { 13 | void *begin; 14 | void *end; 15 | }; 16 | 17 | u64 size; 18 | }; 19 | 20 | EFI_PHYSICAL_ADDRESS max_address; 21 | EFI_MEMORY_TYPE memory_type; 22 | 23 | void *user; 24 | relocated_cb_t cb; 25 | }; 26 | 27 | void relocate_entries(struct relocation_entry *entries); 28 | -------------------------------------------------------------------------------- /loader/include/video_services.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common/types.h" 4 | 5 | #define FB_FORMAT_INVALID 0 6 | #define FB_FORMAT_RGB888 1 7 | #define FB_FORMAT_BGR888 2 8 | #define FB_FORMAT_RGBX8888 3 9 | #define FB_FORMAT_XRGB8888 4 10 | 11 | static inline const char *fb_format_as_str(u16 fmt) 12 | { 13 | switch (fmt) { 14 | case FB_FORMAT_RGB888: 15 | return "rgb888"; 16 | case FB_FORMAT_BGR888: 17 | return "bgr888"; 18 | case FB_FORMAT_RGBX8888: 19 | return "rgbx8888"; 20 | case FB_FORMAT_XRGB8888: 21 | return "xrgb8888"; 22 | case FB_FORMAT_INVALID: 23 | default: 24 | return ""; 25 | } 26 | } 27 | 28 | static inline u16 fb_format_from_mask_shifts_8888(u8 r_shift, u8 g_shift, u8 b_shift, u8 x_shift, u8 bpp) 29 | { 30 | if (bpp == 24) { 31 | if (b_shift == 0 && g_shift == 8 && r_shift == 16) 32 | return FB_FORMAT_RGB888; 33 | if (r_shift == 0 && g_shift == 8 && b_shift == 16) 34 | return FB_FORMAT_BGR888; 35 | } else if (bpp == 32) { 36 | if (x_shift == 0 && b_shift == 8 && g_shift == 16 && r_shift == 24) 37 | return FB_FORMAT_RGBX8888; 38 | if (b_shift == 0 && g_shift == 8 && r_shift == 16 && x_shift == 24) 39 | return FB_FORMAT_XRGB8888; 40 | } 41 | 42 | return FB_FORMAT_INVALID; 43 | } 44 | 45 | struct video_mode { 46 | u32 width; 47 | u32 height; 48 | u16 bpp; 49 | u16 format; 50 | u32 id; 51 | }; 52 | 53 | struct resolution { 54 | u32 width; 55 | u32 height; 56 | }; 57 | 58 | enum color { 59 | COLOR_WHITE, 60 | COLOR_GRAY, 61 | COLOR_YELLOW, 62 | COLOR_RED, 63 | COLOR_BLUE, 64 | COLOR_GREEN, 65 | }; 66 | 67 | struct framebuffer { 68 | u32 width; 69 | u32 height; 70 | u32 pitch; 71 | u16 bpp; 72 | u16 format; 73 | u64 physical_address; 74 | }; 75 | 76 | /* 77 | * Number of video modes that can be queried. 78 | */ 79 | u32 vs_get_mode_count(void); 80 | 81 | /* 82 | * Retrieves information about a video mode at idx. 83 | * idx -> video mode to retrieve. 84 | * out_mode -> pointer to data that receives video mode information. 85 | */ 86 | void vs_query_mode(size_t idx, struct video_mode *out_mode); 87 | 88 | /* 89 | * Attempts to query native screen resolution. 90 | * out_resolution -> main display resolution in pixels. 91 | * Returns true if query succeeded, false otherwise. 92 | */ 93 | bool vs_query_native_resolution(struct resolution *out_resolution); 94 | 95 | /* 96 | * Sets one of the modes returned from an earlier call to list_modes(). 97 | * id -> id of the mode to be set. 98 | * out_framebuffer -> memory region and various data about the set mode. 99 | * Returns true if mode was set successfully, false otherwise. 100 | */ 101 | bool vs_set_mode(u32 id, struct framebuffer *out_framebuffer); 102 | 103 | /* 104 | * Writes string to the output device with the given color. 105 | * text -> ascii string to output to the device. 106 | * count -> number of characters to write. 107 | * color -> color of the output message. 108 | * Returns true if string was successfully written, false otherwise. 109 | */ 110 | bool vs_write_tty(const char *text, size_t count, enum color c); 111 | -------------------------------------------------------------------------------- /loader/include/virtual_memory.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common/types.h" 4 | #include "common/align.h" 5 | #include "arch/virtual_memory.h" 6 | 7 | struct page_table { 8 | void *root; 9 | void (*write_slot)(void*, u64); 10 | u64 (*read_slot)(void*); 11 | u64 max_table_address; 12 | u64 entry_address_mask; 13 | u8 table_width_shift; 14 | u8 levels; 15 | u8 entry_width; 16 | u8 base_shift; 17 | }; 18 | 19 | static inline ptr_t pt_get_root(struct page_table *pt) 20 | { 21 | return (ptr_t)pt->root; 22 | } 23 | 24 | static inline size_t page_shift(struct page_table *pt) 25 | { 26 | return pt->base_shift; 27 | } 28 | 29 | static inline size_t huge_page_shift(struct page_table *pt) 30 | { 31 | return page_shift(pt) + pt->table_width_shift; 32 | } 33 | 34 | static inline size_t huge_page_size(struct page_table *pt) 35 | { 36 | return 1ul << huge_page_shift(pt); 37 | } 38 | 39 | static inline size_t page_size(struct page_table *pt) 40 | { 41 | return 1ul << pt->base_shift; 42 | } 43 | 44 | #define HUGE_PAGE_ROUND_UP(pt, size) ALIGN_UP(size, huge_page_size(pt)) 45 | #define HUGE_PAGE_ROUND_DOWN(pt, size) ALIGN_DOWN(size, huge_page_size(pt)) 46 | 47 | void page_table_init(struct page_table *pt, enum pt_type type, 48 | u64 max_table_address); 49 | 50 | /* 51 | * Amount of virtual memory covered by a level in a page table. 52 | * E.g. 1GiB for an i686 PAE PDPT entry (lvl_idx=3 or pt->levels - 1). 53 | */ 54 | u64 pt_level_entry_virtual_coverage(struct page_table *pt, size_t lvl_idx); 55 | 56 | enum page_type { 57 | // 4K pages 58 | PAGE_TYPE_NORMAL = 0, 59 | 60 | // 2/4M pages 61 | PAGE_TYPE_HUGE = 1, 62 | }; 63 | 64 | struct page_mapping_spec { 65 | struct page_table *pt; 66 | 67 | u64 virtual_base; 68 | u64 physical_base; 69 | 70 | size_t count; 71 | enum page_type type; 72 | bool critical; 73 | }; 74 | 75 | bool map_pages(const struct page_mapping_spec*); 76 | 77 | // Copy a root table entry at src to dest table entry 78 | void map_copy_root_entry(struct page_table*, u64 src_virtual_address, 79 | u64 dest_virtual_address); 80 | 81 | u64 pt_get_root_pte_at(struct page_table *pt, u64 virtual_address); 82 | -------------------------------------------------------------------------------- /loader/include/virtual_memory_impl.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "virtual_memory.h" 4 | 5 | ptr_t pt_get_table_page(u64 max_address); 6 | 7 | u8 pt_table_width_shift_for_level(struct page_table *pt, size_t idx); 8 | -------------------------------------------------------------------------------- /loader/loader.c: -------------------------------------------------------------------------------- 1 | #include "services.h" 2 | #include "common/log.h" 3 | #include "common/string_view.h" 4 | #include "allocator.h" 5 | #include "filesystem/filesystem.h" 6 | #include "filesystem/filesystem_table.h" 7 | #include "config.h" 8 | #include "boot_protocol.h" 9 | 10 | void init_all_disks(void); 11 | void init_config(struct config *out_cfg); 12 | 13 | struct file *find_config_file(struct fs_entry **fe); 14 | void pick_loadable_entry(struct config *cfg, struct loadable_entry *le); 15 | 16 | void loader_entry(void) 17 | { 18 | struct config cfg = { 0 }; 19 | struct loadable_entry le; 20 | 21 | logger_init(); 22 | 23 | fst_init(); 24 | 25 | init_all_disks(); 26 | init_config(&cfg); 27 | 28 | pick_loadable_entry(&cfg, &le); 29 | boot(&cfg, &le); 30 | } 31 | 32 | // TODO: support chain-loading configs 33 | void init_config(struct config *out_cfg) 34 | { 35 | struct fs_entry *fe; 36 | struct file *cfg_file; 37 | char *cfg_data; 38 | struct config_source cfg_src; 39 | 40 | cfg_file = find_config_file(&fe); 41 | if (!cfg_file) 42 | oops("Couldn't find hyper.cfg anywhere on disk!\n"); 43 | 44 | fst_set_origin(fe); 45 | cfg_data = allocate_critical_bytes(cfg_file->size); 46 | 47 | if (!cfg_file->fs->read_file(cfg_file, cfg_data, 0, cfg_file->size)) 48 | oops("failed to read config file\n"); 49 | 50 | cfg_src = (struct config_source) { 51 | .text = cfg_data, 52 | .size = cfg_file->size 53 | }; 54 | 55 | cfg_file->fs->close_file(cfg_file); 56 | 57 | if (!cfg_parse(cfg_src, out_cfg)) { 58 | cfg_pretty_print_error(out_cfg); 59 | loader_abort(); 60 | } 61 | } 62 | 63 | void init_all_disks(void) 64 | { 65 | size_t disk_index; 66 | u32 disk_count; 67 | struct block_cache bc; 68 | void *buf; 69 | 70 | buf = allocate_pages(1); 71 | if (unlikely(!buf)) 72 | return; 73 | 74 | disk_count = ds_get_disk_count(); 75 | 76 | for (disk_index = 0; disk_index < disk_count; ++disk_index) { 77 | struct disk d; 78 | ds_query_disk(disk_index, &d); 79 | 80 | block_cache_init(&bc, &ds_read_blocks, d.handle, d.block_shift, 81 | buf, PAGE_SIZE >> d.block_shift); 82 | 83 | fs_detect_all(&d, &bc); 84 | } 85 | 86 | free_pages(buf, 1); 87 | } 88 | 89 | static struct string_view search_paths[] = { 90 | SV_CONSTEXPR("/hyper.cfg"), 91 | SV_CONSTEXPR("/boot/hyper.cfg"), 92 | SV_CONSTEXPR("/boot/hyper/hyper.cfg"), 93 | }; 94 | 95 | struct file *find_config_file(struct fs_entry **out_entry) 96 | { 97 | struct fs_entry *entries; 98 | size_t i, j, entry_count; 99 | entries = fst_list(&entry_count); 100 | 101 | for (i = 0; i < entry_count; ++i) { 102 | for (j = 0; j < ARRAY_SIZE(search_paths); ++j) { 103 | struct filesystem *fs = entries[i].fs; 104 | struct file *f; 105 | 106 | f = path_open(fs, search_paths[j]); 107 | if (!f) 108 | continue; 109 | 110 | *out_entry = &entries[i]; 111 | return f; 112 | } 113 | } 114 | 115 | return NULL; 116 | } 117 | 118 | #define DEFAULT_ENTRY_KEY SV("default-entry") 119 | 120 | void pick_loadable_entry(struct config *cfg, struct loadable_entry *le) 121 | { 122 | struct string_view loadable_entry_name; 123 | 124 | if (!cfg_get_global_string(cfg, DEFAULT_ENTRY_KEY, &loadable_entry_name)) { 125 | if (!cfg_first_loadable_entry(cfg, le)) 126 | oops("configuration file must contain at least one loadable entry\n"); 127 | return; 128 | } 129 | 130 | if (!cfg_get_loadable_entry(cfg, loadable_entry_name, le)) 131 | oops("no loadable entry \"%pSV\"\n", &loadable_entry_name); 132 | } 133 | -------------------------------------------------------------------------------- /loader/services_impl.c: -------------------------------------------------------------------------------- 1 | #include "common/panic.h" 2 | 3 | #include "services.h" 4 | #include "services_impl.h" 5 | #include "handover.h" 6 | #include "handover_impl.h" 7 | 8 | bool services_offline = false; 9 | 10 | void on_service_use_after_exit(const char *func) 11 | { 12 | panic("Attempted to use %s() after exit!\n", func); 13 | } 14 | 15 | CTOR_SECTION_DEFINE_ITERATOR(cleanup_handler, cleanup_handlers); 16 | 17 | void services_cleanup(void) 18 | { 19 | cleanup_handler *handler; 20 | 21 | for (handler = cleanup_handlers_begin; 22 | handler < cleanup_handlers_end; 23 | ++handler) 24 | { 25 | (*handler)(); 26 | } 27 | } 28 | 29 | static void do_detect_flags(void) 30 | { 31 | static bool flags_detected = false; 32 | 33 | if (flags_detected) 34 | return; 35 | 36 | initialize_flags_map(); 37 | handover_flags_map[HO_HIGHER_HALF_ONLY_BIT] = true; 38 | 39 | flags_detected = true; 40 | } 41 | 42 | bool handover_is_flag_supported(u32 flag) 43 | { 44 | do_detect_flags(); 45 | return handover_flags_map[__builtin_ctz(flag)]; 46 | } 47 | 48 | void handover_ensure_supported_flags(u32 flags) 49 | { 50 | size_t i; 51 | 52 | do_detect_flags(); 53 | 54 | for (i = 0; i < sizeof(handover_flags_map); ++i) { 55 | u32 value = 1u << i; 56 | 57 | if ((flags & value) != value) 58 | continue; 59 | 60 | if (!handover_flags_map[i]) 61 | oops("unsupported feature: '%pSV'\n", &handover_flags_to_string[i]); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /loader/uefi/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Not even going to attempt to abstract this away, the difference is TOO big 2 | if (HYPER_TOOLCHAIN STREQUAL "clang") 3 | target_link_options( 4 | ${LOADER_EXECUTABLE} 5 | PRIVATE 6 | /subsystem:efi_application 7 | /entry:EfiMain 8 | ) 9 | else () 10 | target_link_options( 11 | ${LOADER_EXECUTABLE} 12 | PRIVATE 13 | --subsystem 10 14 | --entry EfiMain 15 | --strip-all 16 | --script ${CMAKE_CURRENT_SOURCE_DIR}/linker.ld 17 | --orphan-handling=error 18 | -nostdlib 19 | ) 20 | endif () 21 | 22 | add_loader_c_flags( 23 | -fno-stack-protector 24 | -mno-stack-arg-probe 25 | ) 26 | 27 | target_sources( 28 | ${LOADER_EXECUTABLE} 29 | PRIVATE 30 | uefi_disk_services.c 31 | uefi_entry.c 32 | uefi_find.c 33 | uefi_helpers.c 34 | uefi_memory_services.c 35 | uefi_video_services.c 36 | relocator.c 37 | stubs.c 38 | ) 39 | -------------------------------------------------------------------------------- /loader/uefi/include/platform_config.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define PLATFORM_HAS_MEMCPY 4 | #define PLATFORM_HAS_MEMMOVE 5 | #define PLATFORM_HAS_MEMSET 6 | #define PLATFORM_WANTS_GENERIC_STRING 7 | -------------------------------------------------------------------------------- /loader/uefi/linker.ld: -------------------------------------------------------------------------------- 1 | SECTIONS 2 | { 3 | /* 4 | * The alignment-related stuff is also used in the vanilla mingw32 5 | * link script, so it's probably a good idea to keep it. 6 | */ 7 | . = SIZEOF_HEADERS; 8 | . = ALIGN(__section_alignment__); 9 | 10 | .text __image_base__ + (__section_alignment__ < 0x1000 ? . : __section_alignment__) : { 11 | *(.text .text.*) 12 | } 13 | 14 | .data ALIGN(__section_alignment__) : { 15 | *(.data) 16 | } 17 | 18 | .rdata ALIGN(__section_alignment__) : { 19 | *(.rdata) 20 | *(SORT(.rdata$*)) 21 | } 22 | 23 | .boot_protocols : { 24 | boot_protocols_begin = .; 25 | *(.boot_protocols) 26 | boot_protocols_end = .; 27 | } 28 | 29 | .filesystems : { 30 | filesystems_begin = .; 31 | *(.filesystems) 32 | filesystems_end = .; 33 | } 34 | 35 | .cleanup_handlers : { 36 | cleanup_handlers_begin = .; 37 | *(.cleanup_handlers) 38 | cleanup_handlers_end = .; 39 | } 40 | 41 | .bss ALIGN(__section_alignment__) : { 42 | *(.bss) 43 | *(COMMON) 44 | } 45 | 46 | .reloc ALIGN(__section_alignment__) : { 47 | *(.reloc) 48 | } 49 | 50 | /DISCARD/ : { 51 | *(.pdata*) 52 | *(.xdata*) 53 | *(.edata*) 54 | *(.idata*) 55 | *(.eh_frame*) 56 | *(.debug*) 57 | *(.tls*) 58 | *(.rsrc) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /loader/uefi/relocator.c: -------------------------------------------------------------------------------- 1 | #define MSG_FMT(msg) "UEFI-RELOC: " msg 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "uefi/relocator.h" 8 | 9 | #include "uefi/globals.h" 10 | #include "uefi/helpers.h" 11 | 12 | static EFI_PHYSICAL_ADDRESS last_allocation; 13 | static EFI_PHYSICAL_ADDRESS last_ceiling; 14 | static size_t last_bytes_rem; 15 | 16 | void relocate_entries(struct relocation_entry *entries) 17 | { 18 | struct relocation_entry *entry; 19 | 20 | for (entry = entries; entry->begin; ++entry) { 21 | EFI_PHYSICAL_ADDRESS addr; 22 | size_t byte_len, pages, page_bytes; 23 | bool is_relocation; 24 | 25 | is_relocation = entry->end != NULL; 26 | addr = entry->max_address; 27 | 28 | if (is_relocation) { 29 | if ((u64)entry->end < entry->max_address) { 30 | addr = (EFI_PHYSICAL_ADDRESS)entry->begin; 31 | goto run_cb; 32 | } 33 | 34 | byte_len = entry->end - entry->begin; 35 | print_info("relocating an entry at 0x%016llX below 0x%016llX (%zu bytes)\n", 36 | (u64)entry->begin, entry->max_address, byte_len); 37 | } else { 38 | byte_len = entry->size; 39 | print_info("allocating %zu bytes below 0x%016llX\n", 40 | byte_len, entry->max_address); 41 | } 42 | 43 | byte_len = ALIGN_UP(byte_len, 8); 44 | page_bytes = PAGE_ROUND_UP(byte_len); 45 | pages = page_bytes >> PAGE_SHIFT; 46 | 47 | if (byte_len > last_bytes_rem || last_ceiling > entry->max_address) { 48 | EFI_STATUS ret; 49 | 50 | ret = g_st->BootServices->AllocatePages( 51 | AllocateMaxAddress, entry->memory_type, pages, &addr 52 | ); 53 | if (unlikely_efi_error(ret)) { 54 | panic("failed to allocate %zu pages below 0x%016llX\n", 55 | pages, entry->max_address); 56 | } 57 | 58 | print_info("allocated %zu pages at 0x%016llX\n", pages, addr); 59 | 60 | last_allocation = addr + byte_len; 61 | last_ceiling = entry->max_address; 62 | last_bytes_rem = page_bytes - byte_len; 63 | } else { 64 | addr = last_allocation; 65 | last_bytes_rem -= byte_len; 66 | last_allocation += byte_len; 67 | } 68 | 69 | if (is_relocation) 70 | memcpy((void*)addr, entry->begin, byte_len); 71 | 72 | run_cb: 73 | if (entry->cb) 74 | entry->cb(entry->user, addr); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /loader/uefi/stubs.c: -------------------------------------------------------------------------------- 1 | #define MSG_FMT(msg) "UEFI-STUBS: " msg 2 | 3 | #include "common/log.h" 4 | #include "common/types.h" 5 | #include "apm.h" 6 | 7 | bool services_setup_apm(struct apm_info *out_info) 8 | { 9 | UNUSED(out_info); 10 | 11 | print_warn("APM setup is unsupported!\n"); 12 | return false; 13 | } 14 | -------------------------------------------------------------------------------- /loader/uefi/uefi_disk_services.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | void uefi_disk_services_init(void); 4 | -------------------------------------------------------------------------------- /loader/uefi/uefi_entry.c: -------------------------------------------------------------------------------- 1 | #include "common/log.h" 2 | #include "services.h" 3 | #include "uefi/structures.h" 4 | #include "uefi_memory_services.h" 5 | #include "uefi_video_services.h" 6 | #include "uefi_disk_services.h" 7 | 8 | EFI_SYSTEM_TABLE *g_st = NULL; 9 | EFI_HANDLE g_img = NULL; 10 | 11 | enum service_provider services_get_provider(void) 12 | { 13 | return SERVICE_PROVIDER_UEFI; 14 | } 15 | 16 | void loader_abort(void) 17 | { 18 | UINTN idx; 19 | EFI_INPUT_KEY key; 20 | EFI_STATUS sts; 21 | 22 | // Empty any pending keystrokes 23 | for (;;) { 24 | sts = g_st->ConIn->ReadKeyStroke(g_st->ConIn, &key); 25 | if (sts != EFI_SUCCESS) 26 | break; 27 | } 28 | 29 | if (sts == EFI_UNSUPPORTED) { 30 | print_err("Loading aborted! Exiting in 10 seconds...\n"); 31 | g_st->BootServices->Stall(10 * 1000 * 1000); 32 | } else { 33 | print_err("Loading aborted! Press any key to continue...\n"); 34 | g_st->BootServices->WaitForEvent(1, &g_st->ConIn->WaitForKey, &idx); 35 | } 36 | 37 | g_st->BootServices->Exit(g_img, EFI_ABORTED, 0, NULL); 38 | __builtin_unreachable(); 39 | } 40 | 41 | EFI_STATUS EFIAPI EfiMain( 42 | IN EFI_HANDLE ImageHandle, 43 | IN EFI_SYSTEM_TABLE *SystemTable 44 | ) 45 | { 46 | g_img = ImageHandle; 47 | g_st = SystemTable; 48 | 49 | uefi_memory_services_init(); 50 | uefi_video_services_init(); 51 | uefi_disk_services_init(); 52 | 53 | loader_entry(); 54 | } 55 | -------------------------------------------------------------------------------- /loader/uefi/uefi_find.c: -------------------------------------------------------------------------------- 1 | #define MSG_FMT(msg) "UEFI-TBL: " msg 2 | 3 | #include "common/log.h" 4 | #include "services.h" 5 | #include "uefi/helpers.h" 6 | 7 | #define EFI_ACPI_20_TABLE_GUID \ 8 | { 0x8868E871, 0xE4F1, 0x11D3, { 0xBC, 0x22, 0x00, 0x80, 0xC7, 0x3C, 0x88, 0x81 } } 9 | 10 | #define EFI_ACPI_10_TABLE_GUID \ 11 | { 0xEB9D2D30, 0x2D88, 0x11D3, { 0x9A, 0x16, 0x00, 0x90, 0x27, 0x3F, 0xC1, 0x4D } } 12 | 13 | ptr_t services_find_rsdp(void) 14 | { 15 | EFI_GUID table10_guid = EFI_ACPI_10_TABLE_GUID; 16 | EFI_GUID table20_guid = EFI_ACPI_20_TABLE_GUID; 17 | 18 | ptr_t table_addr; 19 | int table_version = 2; 20 | 21 | table_addr = (ptr_t)uefi_find_configuration(&table20_guid); 22 | if (table_addr) 23 | goto out; 24 | 25 | table_addr = (ptr_t)uefi_find_configuration(&table10_guid); 26 | table_version = 1; 27 | if (table_addr) 28 | goto out; 29 | 30 | print_warn("couldn't find RSDP, ACPI is unsupported by host(?)\n"); 31 | return table_addr; 32 | 33 | out: 34 | print_info("RSDP table v%d @0x%016llX\n", table_version, table_addr); 35 | return table_addr; 36 | } 37 | 38 | #define EFI_DTB_TABLE_GUID \ 39 | { 0xB1B621D5, 0xF19C, 0x41A5, { 0x83, 0x0B, 0xD9, 0x15, 0x2C, 0x69, 0xAA, 0xE0 } } 40 | 41 | ptr_t services_find_dtb(void) 42 | { 43 | EFI_GUID dtb_guid = EFI_DTB_TABLE_GUID; 44 | ptr_t dtb_addr; 45 | 46 | dtb_addr = (ptr_t)uefi_find_configuration(&dtb_guid); 47 | if (dtb_addr) 48 | print_info("device tree blob @0x%016llX\n", dtb_addr); 49 | 50 | return dtb_addr; 51 | } 52 | 53 | /* 54 | * On UEFI-based systems, the SMBIOS Entry Point structure can be located by 55 | * looking in the EFI Configuration Table for the SMBIOS/SMBIOS 3.x GUID 56 | */ 57 | #define SMBIOS_TABLE_GUID \ 58 | { 0xEB9D2D31, 0x2D88, 0x11D3, { 0x9A, 0x16, 0x00, 0x90, 0x27, 0x3F, 0xC1, 0x4D } } 59 | 60 | #define SMBIOS3_TABLE_GUID \ 61 | { 0xF2FD1544, 0x9794, 0x4A2C, { 0x99, 0x2E, 0xE5, 0xBB, 0xCF, 0x20, 0xE3, 0x94 } } 62 | 63 | ptr_t services_find_smbios(void) 64 | { 65 | EFI_GUID smbios_guid = SMBIOS_TABLE_GUID; 66 | EFI_GUID smbios3_guid = SMBIOS3_TABLE_GUID; 67 | 68 | ptr_t table_addr; 69 | int bitness = 64; 70 | 71 | table_addr = (ptr_t)uefi_find_configuration(&smbios3_guid); 72 | if (table_addr == 0) { 73 | table_addr = (ptr_t) uefi_find_configuration(&smbios_guid); 74 | bitness = 32; 75 | } 76 | 77 | if (table_addr) 78 | print_info("SMBIOS (%d-bit) @0x%016llX\n", bitness, table_addr); 79 | 80 | return table_addr; 81 | } 82 | -------------------------------------------------------------------------------- /loader/uefi/uefi_memory_services.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | void uefi_memory_services_init(void); 4 | -------------------------------------------------------------------------------- /loader/uefi/uefi_video_services.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | void uefi_video_services_init(void); 4 | -------------------------------------------------------------------------------- /tests/conftest.py: -------------------------------------------------------------------------------- 1 | import options 2 | import pytest 3 | 4 | def pytest_addoption(parser): 5 | options.add_base_options(parser.addoption) 6 | options.add_test_options(parser.addoption) 7 | 8 | 9 | def pytest_collection_modifyitems(config, items): 10 | has_uefi_x64, has_uefi_aa64, has_bios, has_bios_iso = ( 11 | options.check_availability(config.getoption) 12 | ) 13 | 14 | for item in items: 15 | if "uefi_x64" in item.keywords: 16 | if not has_uefi_x64: 17 | item.add_marker( 18 | pytest.mark.skip( 19 | reason="missing x64 UEFI loader or firmware" 20 | ) 21 | ) 22 | continue 23 | 24 | if "uefi_aarch64" in item.keywords: 25 | if not has_uefi_aa64: 26 | item.add_marker( 27 | pytest.mark.skip( 28 | reason="missing aa64 UEFI loader or firmware" 29 | ) 30 | ) 31 | continue 32 | 33 | if not has_bios and "fat" in item.keywords: 34 | item.add_marker(pytest.mark.skip(reason="missing hyper installer")) 35 | continue 36 | 37 | if "iso" in item.keywords: 38 | if not has_bios_iso: 39 | item.add_marker(pytest.mark.skip(reason="missing hyper iso loader")) 40 | continue 41 | 42 | if not has_bios and "hdd" in item.keywords: 43 | item.add_marker(pytest.mark.skip(reason="missing hyper installer")) 44 | -------------------------------------------------------------------------------- /tests/kernel/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | if [[ "$OSTYPE" == "darwin"* ]]; then 6 | realpath() { 7 | [[ $1 = /* ]] && echo "$1" || echo "$PWD/${1#./}" 8 | } 9 | 10 | cores=$(sysctl -n hw.physicalcpu) 11 | elif [[ "$OSTYPE" == "linux-gnu"* ]]; then 12 | cores=$(nproc) 13 | fi 14 | 15 | pushd () { command pushd "$@" > /dev/null ; } 16 | popd () { command popd "$@" > /dev/null ; } 17 | 18 | true_path="$(dirname "$(realpath "$0")")" 19 | 20 | pushd $true_path 21 | 22 | mkdir -p build 23 | pushd build 24 | 25 | cmake .. 26 | make -j$cores 27 | 28 | popd 29 | popd 30 | -------------------------------------------------------------------------------- /tests/kernel/common/log.c: -------------------------------------------------------------------------------- 1 | #include "common/log.h" 2 | #include "common/format.h" 3 | #include "test_ctl.h" 4 | 5 | void vprint(const char *msg, va_list vlist) 6 | { 7 | static char log_buf[256]; 8 | 9 | int chars; 10 | chars = vscnprintf(log_buf, sizeof(log_buf), msg, vlist); 11 | test_write_string(log_buf, chars); 12 | } 13 | 14 | void print(const char *msg, ...) 15 | { 16 | va_list vlist; 17 | va_start(vlist, msg); 18 | vprint(msg, vlist); 19 | va_end(vlist); 20 | } 21 | -------------------------------------------------------------------------------- /tests/kernel/fb_tty.c: -------------------------------------------------------------------------------- 1 | #include "fb_tty.h" 2 | #include "fb_font.h" 3 | #include "ultra_protocol.h" 4 | #include "ultra_helpers.h" 5 | #include "test_ctl.h" 6 | 7 | static void *fb_ptr = NULL; 8 | static size_t fb_pitch; 9 | static size_t fb_width; 10 | static size_t fb_height; 11 | static size_t tty_x; 12 | static size_t tty_y; 13 | static size_t rows; 14 | static size_t columns; 15 | 16 | struct ultra_framebuffer *get_fb(struct ultra_boot_context *bctx) 17 | { 18 | typedef struct ultra_framebuffer_attribute ufbattr; 19 | ufbattr *fbattr = (ufbattr*)find_attr(bctx, ULTRA_ATTRIBUTE_FRAMEBUFFER_INFO); 20 | 21 | return fbattr ? &fbattr->fb : NULL; 22 | } 23 | 24 | void fb_tty_init(struct ultra_boot_context *bctx) 25 | { 26 | struct ultra_framebuffer *fb = get_fb(bctx); 27 | u32 expected_bpp; 28 | u32 expected_pitch_min; 29 | 30 | if (!fb) { 31 | print("Couldn't find FB info, framebuffer logging won't be available\n"); 32 | return; 33 | } 34 | 35 | if (fb->width < 800 || fb->height < 600) 36 | test_fail("invalid framebuffer resolution %ux%u\n", 37 | fb->width, fb->height); 38 | 39 | switch (fb->format) { 40 | default: 41 | case ULTRA_FB_FORMAT_INVALID: 42 | test_fail("bogus framebuffer format %u\n", fb->format); 43 | 44 | case ULTRA_FB_FORMAT_XRGB8888: 45 | case ULTRA_FB_FORMAT_RGBX8888: 46 | expected_bpp = 32; 47 | break; 48 | 49 | case ULTRA_FB_FORMAT_RGB888: 50 | case ULTRA_FB_FORMAT_BGR888: 51 | expected_bpp = 24; 52 | break; 53 | } 54 | 55 | if (fb->bpp != expected_bpp) 56 | test_fail("invalid bpp %u for format %u\n", fb->bpp, fb->format); 57 | 58 | expected_pitch_min = (fb->bpp / 8) * fb->width; 59 | if (fb->pitch < expected_pitch_min) 60 | test_fail("bogus framebuffer pitch %u\n", fb->pitch); 61 | 62 | if (fb->format != ULTRA_FB_FORMAT_XRGB8888) 63 | return; 64 | if (sizeof(void*) == 4) 65 | return; 66 | 67 | fb_pitch = fb->pitch; 68 | fb_width = fb->width; 69 | fb_height = fb->height; 70 | fb_ptr = (void*)fb->physical_address; 71 | 72 | // Relocate the framebuffer physical address if we're higher half exclusive 73 | if ((ptr_t)bctx >= AMD64_LA57_DIRECT_MAP_BASE) { 74 | if ((ptr_t)bctx < AMD64_DIRECT_MAP_BASE) 75 | fb_ptr += AMD64_LA57_DIRECT_MAP_BASE; 76 | else 77 | fb_ptr += AMD64_DIRECT_MAP_BASE; 78 | } 79 | 80 | rows = fb_height / FONT_HEIGHT; 81 | columns = fb_width / FONT_WIDTH; 82 | } 83 | 84 | void fb_write_one(char c) 85 | { 86 | size_t x_initial = FONT_WIDTH * tty_x; 87 | size_t y_initial = FONT_HEIGHT * tty_y; 88 | size_t y, x; 89 | 90 | for (y = 0; y < FONT_HEIGHT; ++y) { 91 | for (x = 0; x < FONT_WIDTH; ++x) { 92 | u32 *fb_at_y = fb_ptr + (y_initial + y) * fb_pitch; 93 | 94 | bool present = fb_font[(size_t)c][y] & (1 << x); 95 | fb_at_y[x_initial + x] = 0xFFFFFFFF * present; 96 | } 97 | } 98 | } 99 | 100 | static void fb_tty_newline() 101 | { 102 | if (++tty_y >= rows) 103 | tty_y = 0; 104 | 105 | tty_x = 0; 106 | } 107 | 108 | void fb_tty_write(const char *str, size_t count) 109 | { 110 | size_t i; 111 | 112 | if (!fb_ptr) 113 | return; 114 | 115 | for (i = 0; i < count; ++i) 116 | { 117 | char c = str[i]; 118 | 119 | if (c == '\n') { 120 | fb_tty_newline(); 121 | continue; 122 | } 123 | 124 | fb_write_one(c); 125 | 126 | if (++tty_x >= columns) 127 | fb_tty_newline(); 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /tests/kernel/include/common/log.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "attributes.h" 3 | #include 4 | 5 | void print(const char *msg, ...); 6 | void vprint(const char *msg, va_list vlist); 7 | -------------------------------------------------------------------------------- /tests/kernel/include/common/string_ex.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "types.h" 4 | 5 | /* 6 | * This header contains string.h functions that are not part of the hyper 7 | * loader, and that I am not planning to add there to prevent undesired bloat. 8 | */ 9 | 10 | static inline int strcmp(const char *lhs, const char *rhs) 11 | { 12 | size_t i = 0; 13 | typedef const unsigned char *cucp; 14 | 15 | while (lhs[i] && rhs[i]) { 16 | if (lhs[i] != rhs[i]) 17 | return *(cucp)&lhs[i] - *(cucp)&rhs[i]; 18 | 19 | i++; 20 | } 21 | 22 | return *(cucp)&lhs[i] - *(cucp)&rhs[i]; 23 | } 24 | -------------------------------------------------------------------------------- /tests/kernel/include/fb_font.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "common/types.h" 3 | 4 | #define FONT_WIDTH 8 5 | #define FONT_HEIGHT 16 6 | 7 | extern u8 fb_font[256][16]; 8 | -------------------------------------------------------------------------------- /tests/kernel/include/fb_tty.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common/types.h" 4 | 5 | struct ultra_boot_context; 6 | 7 | void fb_tty_init(struct ultra_boot_context *bctx); 8 | void fb_tty_write(const char *str, size_t count); 9 | -------------------------------------------------------------------------------- /tests/kernel/include/test_ctl.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include "common/types.h" 3 | #include "common/attributes.h" 4 | #include "common/log.h" 5 | 6 | struct ultra_boot_context; 7 | void test_ctl_init(struct ultra_boot_context*); 8 | 9 | void test_write_string(const char *str, size_t count); 10 | 11 | NORETURN 12 | void test_fail(const char *reason, ...); 13 | 14 | NORETURN 15 | void test_vfail(const char *reason, va_list vlist); 16 | 17 | NORETURN 18 | void test_pass(); 19 | 20 | NORETURN 21 | static inline void test_fail_on_non_unique(const char *arg) 22 | { 23 | test_fail("encountered multiple '%s'\n", arg); 24 | } 25 | 26 | NORETURN 27 | static inline void test_fail_on_no_mandatory(const char *arg) 28 | { 29 | test_fail("missing mandatory '%s'\n", arg); 30 | } 31 | -------------------------------------------------------------------------------- /tests/kernel/include/ultra_helpers.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ultra_protocol.h" 4 | 5 | #define I686_DIRECT_MAP_BASE 0xC0000000 6 | #define AMD64_DIRECT_MAP_BASE 0xFFFF800000000000 7 | #define AMD64_LA57_DIRECT_MAP_BASE 0xFF00000000000000 8 | #define AARCH64_48BIT_DIRECT_MAP_BASE 0xFFFF000000000000 9 | #define AARCH64_52BIT_DIRECT_MAP_BASE 0xFFF0000000000000 10 | 11 | struct ultra_attribute_header *find_attr(struct ultra_boot_context *ctx, 12 | uint32_t type); 13 | -------------------------------------------------------------------------------- /tests/kernel/link_1gb_generic.ld: -------------------------------------------------------------------------------- 1 | ENTRY(main) 2 | 3 | SECTIONS 4 | { 5 | . = 0x0000000040000000; 6 | 7 | .text : 8 | { 9 | *(.text .text.*) 10 | } 11 | 12 | . = ALIGN(0x1000); 13 | 14 | .rodata : 15 | { 16 | *(.rodata .rodata.*) 17 | } 18 | 19 | . = ALIGN(0x1000); 20 | 21 | .data : 22 | { 23 | *(.data .data.*) 24 | } 25 | 26 | . = ALIGN(0x1000); 27 | 28 | .bss : 29 | { 30 | *(COMMON) 31 | *(.bss .bss.*) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /tests/kernel/link_1mb_generic.ld: -------------------------------------------------------------------------------- 1 | ENTRY(main) 2 | 3 | SECTIONS 4 | { 5 | . = 0x100000; 6 | 7 | .text : 8 | { 9 | *(.text .text.*) 10 | } 11 | 12 | .rodata : 13 | { 14 | *(.rodata .rodata.*) 15 | } 16 | 17 | .data : 18 | { 19 | *(.data .data.*) 20 | } 21 | 22 | .bss : 23 | { 24 | *(COMMON) 25 | *(.bss .bss.*) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /tests/kernel/link_generic_64bit_higher_half.ld: -------------------------------------------------------------------------------- 1 | ENTRY(main) 2 | 3 | SECTIONS 4 | { 5 | . = 0xFFFFFFFF80000000; /* MAX - 2GB */ 6 | 7 | .text : 8 | { 9 | *(.text .text.*) 10 | } 11 | 12 | . = ALIGN(0x1000); 13 | 14 | .rodata : 15 | { 16 | *(.rodata .rodata.*) 17 | } 18 | 19 | . = ALIGN(0x1000); 20 | 21 | .data : 22 | { 23 | *(.data .data.*) 24 | } 25 | 26 | . = ALIGN(0x1000); 27 | 28 | .bss : 29 | { 30 | *(COMMON) 31 | *(.bss .bss.*) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /tests/kernel/link_i686_higher_half.ld: -------------------------------------------------------------------------------- 1 | ENTRY(main) 2 | 3 | SECTIONS 4 | { 5 | . = 0xC0100000; 6 | 7 | .text : 8 | { 9 | *(.text .text.*) 10 | } 11 | 12 | .rodata : 13 | { 14 | *(.rodata .rodata.*) 15 | } 16 | 17 | .data : 18 | { 19 | *(.data .data.*) 20 | } 21 | 22 | .bss : 23 | { 24 | *(COMMON) 25 | *(.bss .bss.*) 26 | } 27 | } -------------------------------------------------------------------------------- /tests/kernel/test_ctl_aarch64.c: -------------------------------------------------------------------------------- 1 | #include "common/helpers.h" 2 | #include "common/types.h" 3 | #include "ultra_helpers.h" 4 | #include "test_ctl_impl.h" 5 | #include "test_ctl.h" 6 | #include "fb_tty.h" 7 | 8 | volatile u8 *g_qemu_uart = (u8*)0x9000000; 9 | bool g_uart_rebased = false; 10 | 11 | void arch_test_ctl_init(struct ultra_boot_context *bctx) 12 | { 13 | typedef struct ultra_platform_info_attribute upia; 14 | upia *pia = (upia*)find_attr(bctx, ULTRA_ATTRIBUTE_PLATFORM_INFO); 15 | 16 | g_qemu_uart += pia->higher_half_base; 17 | g_uart_rebased = true; 18 | } 19 | 20 | void arch_put_byte(char c) 21 | { 22 | if (g_uart_rebased) 23 | *g_qemu_uart = c; 24 | } 25 | 26 | void arch_hang_or_shutdown(void) 27 | { 28 | if (g_should_shutdown) { 29 | // Try PSCI SYSTEM_OFF method, then give up 30 | asm volatile ("movz x0, #0x0008; movk x0, #0x8400, LSL #16; hvc #0"); 31 | } 32 | for (;;) asm volatile("wfi" ::: "memory"); 33 | } 34 | -------------------------------------------------------------------------------- /tests/kernel/test_ctl_generic.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "common/attributes.h" 3 | #include "common/string_ex.h" 4 | 5 | #include "fb_tty.h" 6 | #include "test_ctl.h" 7 | #include "test_ctl_impl.h" 8 | #include "ultra_helpers.h" 9 | 10 | #define TEST_PASS_MARKER0 0xCA 11 | #define TEST_PASS_MARKER1 0xFE 12 | #define TEST_PASS_MARKER2 0xBA 13 | #define TEST_PASS_MARKER3 0xBE 14 | 15 | #define TEST_FAIL_MARKER0 0xDE 16 | #define TEST_FAIL_MARKER1 0xAD 17 | #define TEST_FAIL_MARKER2 0xBE 18 | #define TEST_FAIL_MARKER3 0xEF 19 | 20 | bool g_should_shutdown = true; 21 | 22 | void test_pass(void) 23 | { 24 | print("TEST PASS!\n"); 25 | 26 | arch_put_byte(TEST_PASS_MARKER0); 27 | arch_put_byte(TEST_PASS_MARKER1); 28 | arch_put_byte(TEST_PASS_MARKER2); 29 | arch_put_byte(TEST_PASS_MARKER3); 30 | arch_hang_or_shutdown(); 31 | } 32 | 33 | void test_vfail(const char *reason, va_list vlist) 34 | { 35 | print("TEST FAIL!\n"); 36 | vprint(reason, vlist); 37 | 38 | arch_put_byte(TEST_FAIL_MARKER0); 39 | arch_put_byte(TEST_FAIL_MARKER1); 40 | arch_put_byte(TEST_FAIL_MARKER2); 41 | arch_put_byte(TEST_FAIL_MARKER3); 42 | arch_hang_or_shutdown(); 43 | } 44 | 45 | void test_fail(const char *reason, ...) 46 | { 47 | va_list vlist; 48 | va_start(vlist, reason); 49 | test_vfail(reason, vlist); 50 | va_end(vlist); 51 | } 52 | 53 | void panic(const char *reason, ...) 54 | { 55 | va_list vlist; 56 | va_start(vlist, reason); 57 | test_vfail(reason, vlist); 58 | va_end(vlist); 59 | } 60 | 61 | void oops(const char *reason, ...) 62 | { 63 | va_list vlist; 64 | va_start(vlist, reason); 65 | test_vfail(reason, vlist); 66 | va_end(vlist); 67 | } 68 | 69 | WEAK 70 | void arch_write_string(const char *str, size_t count) 71 | { 72 | while (count--) 73 | arch_put_byte(*str++); 74 | } 75 | 76 | void test_write_string(const char *str, size_t count) 77 | { 78 | arch_write_string(str, count); 79 | fb_tty_write(str, count); 80 | } 81 | 82 | WEAK 83 | void arch_test_ctl_init(struct ultra_boot_context *bctx) 84 | { 85 | UNUSED(bctx); 86 | } 87 | 88 | void test_ctl_init(struct ultra_boot_context *bctx) 89 | { 90 | struct ultra_command_line_attribute *cmdline; 91 | 92 | cmdline = (struct ultra_command_line_attribute*)find_attr(bctx, ULTRA_ATTRIBUTE_COMMAND_LINE); 93 | if (cmdline) 94 | g_should_shutdown = strcmp(cmdline->text, "no-shutdown") != 0; 95 | 96 | arch_test_ctl_init(bctx); 97 | } 98 | -------------------------------------------------------------------------------- /tests/kernel/test_ctl_impl.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common/attributes.h" 4 | #include "common/types.h" 5 | 6 | extern bool g_should_shutdown; 7 | 8 | // Optional, should be implemented if needed 9 | void arch_test_ctl_init(struct ultra_boot_context *bctx); 10 | 11 | void arch_put_byte(char c); 12 | 13 | // Optional, arch_put_byte is called per char if not implemented 14 | void arch_write_string(const char *str, size_t count); 15 | 16 | // *MUST* shutdown to make tests pass, allowed to hang if on real HW 17 | NORETURN 18 | void arch_hang_or_shutdown(void); 19 | -------------------------------------------------------------------------------- /tests/kernel/test_ctl_x86.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "pio.h" 3 | 4 | #include "test_ctl.h" 5 | #include "test_ctl_impl.h" 6 | #include "ultra_protocol.h" 7 | #include "ultra_helpers.h" 8 | 9 | static int is_in_hypervisor_state = -1; 10 | 11 | #define HYPERVISOR_BIT (1 << 31) 12 | 13 | static bool is_in_hypervisor(void) 14 | { 15 | if (is_in_hypervisor_state == -1) { 16 | u32 a, b, c, d; 17 | 18 | asm volatile("cpuid" 19 | : "=a"(a), "=b"(b), "=c"(c), "=d"(d) 20 | : "a"(1), "c"(0)); 21 | is_in_hypervisor_state = c & HYPERVISOR_BIT; 22 | } 23 | 24 | return is_in_hypervisor_state; 25 | } 26 | 27 | static inline void e9_put_byte(char c) 28 | { 29 | out8(0xE9, c); 30 | } 31 | 32 | void arch_put_byte(char c) 33 | { 34 | if (!is_in_hypervisor()) 35 | return; 36 | 37 | e9_put_byte(c); 38 | } 39 | 40 | void arch_write_string(const char *str, size_t len) 41 | { 42 | if (!is_in_hypervisor()) 43 | return; 44 | 45 | while (len--) 46 | e9_put_byte(*str++); 47 | } 48 | 49 | // Try various methods, then give up 50 | void arch_hang_or_shutdown() 51 | { 52 | if (!is_in_hypervisor() || !g_should_shutdown) 53 | goto hang; 54 | 55 | out16(0xB004, 0x2000); 56 | out16(0x604, 0x2000); 57 | out16(0x4004, 0x3400); 58 | 59 | hang: 60 | for (;;) asm volatile("hlt" ::: "memory"); 61 | } 62 | -------------------------------------------------------------------------------- /tests/kernel/ultra_helpers.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "ultra_helpers.h" 3 | 4 | struct ultra_attribute_header *find_attr(struct ultra_boot_context *ctx, 5 | uint32_t type) 6 | { 7 | struct ultra_attribute_header *ret = ctx->attributes; 8 | size_t i; 9 | 10 | // Guaranteed to be the first attribute 11 | if (type == ULTRA_ATTRIBUTE_PLATFORM_INFO) 12 | return ret; 13 | 14 | ret = ULTRA_NEXT_ATTRIBUTE(ret); 15 | 16 | // Guaranteed to be the second attribute 17 | if (type == ULTRA_ATTRIBUTE_KERNEL_INFO) 18 | return ret; 19 | 20 | ret = ULTRA_NEXT_ATTRIBUTE(ret); 21 | 22 | for (i = 2; i < ctx->attribute_count; ++i, ret = ULTRA_NEXT_ATTRIBUTE(ret)) { 23 | if (ret->type == type) 24 | return ret; 25 | } 26 | 27 | return NULL; 28 | } 29 | -------------------------------------------------------------------------------- /tests/make_disk_image.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | import argparse 3 | import options 4 | import disk_image as di 5 | import shutil 6 | import sys 7 | from typing import Callable, Any 8 | 9 | 10 | def argparse_optgetter(obj: Any) -> Callable: 11 | def do_getopt(name): 12 | return getattr(obj, name[2:].replace("-", "_")) 13 | 14 | return do_getopt 15 | 16 | 17 | if __name__ == "__main__": 18 | parser = argparse.ArgumentParser(usage="Make a loader image") 19 | options.add_base_options(parser.add_argument) 20 | 21 | parser.add_argument("fs_type", choices=["ISO9660", "FAT12", "FAT16", "FAT32"], 22 | help="Type of file system to use for the image") 23 | parser.add_argument("out_path", type=str, help="Path to the output image") 24 | parser.add_argument("--br-type", default="MBR", choices=["MBR", "GPT"], 25 | help="Type of boot record to use for the image") 26 | 27 | kernel_types = [ 28 | "i686_lower_half", 29 | "i686_higher_half", 30 | "i686_lower_half_pae", 31 | "i686_higher_half_pae", 32 | "amd64_lower_half", 33 | "amd64_higher_half", 34 | "amd64_lower_half_5lvl", 35 | "amd64_higher_half_5lvl", 36 | "aarch64_lower_half", 37 | "aarch64_higher_half", 38 | "aarch64_lower_half_5lvl", 39 | "aarch64_higher_half_5lvl", 40 | ] 41 | parser.add_argument("--kernel-type", default="amd64_higher_half", 42 | help="Type of kernel to set as default in config", 43 | choices=kernel_types) 44 | args = parser.parse_args() 45 | 46 | getopt = argparse_optgetter(args) 47 | has_uefi_x64, has_uefi_aa64, has_bios, has_bios_br = \ 48 | options.check_availability(getopt, args.fs_type != "ISO9660", 49 | args.br_type == "GPT", False) 50 | has_uefi = has_uefi_x64 or has_uefi_aa64 51 | 52 | cfg = di.make_normal_boot_config(args.kernel_type, "no-shutdown") 53 | try: 54 | image = next(di.build(getopt, args.br_type, args.fs_type, 55 | cfg, args.out_path, False)) 56 | finally: 57 | shutil.rmtree(getopt(options.INTERM_DIR_OPT)) 58 | 59 | print(f"Created an image at {image.path}") 60 | print("The image can be booted with:") 61 | 62 | kernel_is_x86 = "aarch64" not in args.kernel_type 63 | 64 | if kernel_is_x86: 65 | if has_uefi_x64: 66 | print("- UEFI") 67 | 68 | if args.fs_type == "ISO9660": 69 | if has_bios_br: 70 | print("- BIOS as CD") 71 | 72 | if has_bios: 73 | print("- BIOS as HDD") 74 | elif has_bios and args.br_type != "GPT": 75 | print("- BIOS") 76 | elif has_uefi_aa64: 77 | print("- UEFI") 78 | -------------------------------------------------------------------------------- /tests/options.py: -------------------------------------------------------------------------------- 1 | import path_guesser 2 | 3 | KERNEL_DIR_OPT = "--kernel-dir" 4 | INTERM_DIR_OPT = "--intermediate-dir" 5 | INSTALLER_OPT = "--hyper-install-path" 6 | X64_HYPER_UEFI_OPT = "--x64-hyper-uefi-path" 7 | AA64_HYPER_UEFI_OPT = "--aa64-hyper-uefi-path" 8 | HYPER_ISO_BR_OPT = "--hyper-iso-br-path" 9 | X64_UEFI_FIRMWARE_OPT = "--x64-uefi-firmware-path" 10 | AA64_UEFI_FIRMWARE_OPT = "--aa64-uefi-firmware-path" 11 | QEMU_GUI_OPT = "--qemu-enable-gui" 12 | 13 | 14 | def add_base_options(add_opt_cb): 15 | binaries_path = path_guesser.guess_path_to_kernel_binaries() 16 | installer_path = path_guesser.guess_path_to_installer() 17 | hyper_x64_uefi_path = path_guesser.guess_path_to_hyper_uefi("amd64") 18 | hyper_aa64_uefi_path = path_guesser.guess_path_to_hyper_uefi("aarch64") 19 | hyper_iso_br_path = path_guesser.guess_path_to_hyper_iso_br() 20 | 21 | add_opt_cb(KERNEL_DIR_OPT, type=str, 22 | default=binaries_path, 23 | help="Path to the directory with kernel binaries", 24 | required=binaries_path is None) 25 | add_opt_cb(INTERM_DIR_OPT, type=str, 26 | default=path_guesser.guess_path_to_interm_dir(), 27 | help="Path to the intermediate data directory") 28 | add_opt_cb(INSTALLER_OPT, type=str, 29 | default=installer_path, 30 | help="Path to the hyper installer") 31 | add_opt_cb(X64_HYPER_UEFI_OPT, type=str, 32 | default=hyper_x64_uefi_path, 33 | help="Path to the x64 hyper UEFI binary") 34 | add_opt_cb(AA64_HYPER_UEFI_OPT, type=str, 35 | default=hyper_aa64_uefi_path, 36 | help="Path to the aa64 hyper UEFI binary") 37 | add_opt_cb(HYPER_ISO_BR_OPT, type=str, 38 | default=hyper_iso_br_path, 39 | help="Path to the hyper ISO boot record") 40 | 41 | 42 | def add_test_options(add_opt_cb): 43 | add_opt_cb(X64_UEFI_FIRMWARE_OPT, type=str, 44 | default=path_guesser.guess_path_to_uefi_firmware("amd64"), 45 | help="Path to the x64 UEFI firmware") 46 | add_opt_cb(AA64_UEFI_FIRMWARE_OPT, type=str, 47 | default=path_guesser.guess_path_to_uefi_firmware("aarch64"), 48 | help="Path to the aarch64 UEFI firmware") 49 | add_opt_cb("--qemu-enable-gui", action="store_true", 50 | help="Run QEMU with GUI") 51 | 52 | 53 | def check_availability(get_opt_cb, hdd_only=False, gpt_only=False, 54 | need_firmware=True): 55 | has_uefi_x64 = get_opt_cb(X64_HYPER_UEFI_OPT) is not None 56 | has_uefi_aa64 = get_opt_cb(AA64_HYPER_UEFI_OPT) is not None 57 | 58 | if need_firmware: 59 | if has_uefi_x64: 60 | has_uefi_x64 = get_opt_cb(X64_UEFI_FIRMWARE_OPT) is not None 61 | if has_uefi_aa64: 62 | has_uefi_aa64 = get_opt_cb(AA64_UEFI_FIRMWARE_OPT) is not None 63 | 64 | has_uefi = (has_uefi_x64 or has_uefi_aa64) 65 | has_bios = get_opt_cb(INSTALLER_OPT) is not None 66 | has_bios_iso = get_opt_cb(HYPER_ISO_BR_OPT) is not None 67 | 68 | if not has_uefi and (not has_bios or gpt_only) and \ 69 | (not has_bios_iso or hdd_only): 70 | raise FileNotFoundError("Couldn't find any viable boot options! " 71 | "Please specify paths manually.") 72 | 73 | return has_uefi_x64, has_uefi_aa64, has_bios, has_bios_iso 74 | -------------------------------------------------------------------------------- /tests/path_guesser.py: -------------------------------------------------------------------------------- 1 | import os 2 | import platform 3 | import subprocess 4 | 5 | 6 | def abs_path_to_current_dir() -> str: 7 | return os.path.dirname(os.path.abspath(__file__)) 8 | 9 | 10 | def abs_path_to_project_root() -> str: 11 | curdir = abs_path_to_current_dir() 12 | pardir = os.path.join(curdir, os.path.pardir) 13 | 14 | return os.path.abspath(pardir) 15 | 16 | 17 | def __guess_or_none(guess, validity_check=os.F_OK): 18 | return guess if os.access(guess, validity_check) else None 19 | 20 | 21 | def __guess_with_middle_parts_or_none(middle_parts, postfix, 22 | prefix=abs_path_to_project_root(), 23 | validity_check=os.F_OK): 24 | for mp in middle_parts: 25 | path = os.path.join(prefix, mp, postfix) 26 | 27 | res = __guess_or_none(path, validity_check) 28 | if res is not None: 29 | return res 30 | 31 | return None 32 | 33 | def __guess_with_prefixes_or_none(prefixes, postfix, validity_check=os.F_OK): 34 | for prefix in prefixes: 35 | path = os.path.join(prefix, postfix) 36 | 37 | res = __guess_or_none(path, validity_check) 38 | if res is not None: 39 | return res 40 | 41 | return None 42 | 43 | 44 | def guess_path_to_kernel_binaries(): 45 | guess = os.path.join(abs_path_to_current_dir(), "kernel/build") 46 | 47 | kernels = [ 48 | "kernel_i686_lower_half", 49 | "kernel_i686_higher_half", 50 | "kernel_amd64_higher_half", 51 | "kernel_amd64_lower_half", 52 | "kernel_aarch64_lower_half", 53 | "kernel_aarch64_higher_half", 54 | ] 55 | for kernel in kernels: 56 | if not os.access(os.path.join(guess, kernel), os.F_OK): 57 | return None 58 | 59 | return guess 60 | 61 | 62 | def guess_path_to_interm_dir(): 63 | return os.path.join(abs_path_to_current_dir(), "temp-data") 64 | 65 | def guess_path_to_installer(): 66 | guess = os.path.join(abs_path_to_project_root(), 67 | "installer/hyper_install") 68 | return __guess_or_none(guess, os.X_OK) 69 | 70 | 71 | def guess_path_to_hyper_uefi(arch): 72 | middle_parts = [ 73 | f"build-clang-{arch}-uefi", 74 | f"build-gcc-{arch}-uefi", 75 | ] 76 | postfix = os.path.join("loader", "hyper_uefi") 77 | 78 | return __guess_with_middle_parts_or_none(middle_parts, postfix) 79 | 80 | 81 | def guess_path_to_hyper_iso_br(): 82 | middle_parts = [ 83 | "build-clang-i686-bios", 84 | "build-gcc-i686-bios", 85 | ] 86 | postfix = os.path.join("loader", "hyper_iso_boot") 87 | 88 | return __guess_with_middle_parts_or_none(middle_parts, postfix) 89 | 90 | 91 | def guess_path_to_uefi_firmware(arch): 92 | edk2_arch = arch 93 | if edk2_arch == "amd64": 94 | edk2_arch = "x86_64" 95 | 96 | prefixes = [ 97 | "/usr", 98 | ] 99 | 100 | try: 101 | bp = subprocess.run(["brew", "--prefix", "qemu"], 102 | stdout=subprocess.PIPE, 103 | universal_newlines=True) 104 | if bp.returncode == 0: 105 | prefixes.append(bp.stdout.strip()) 106 | except FileNotFoundError: 107 | pass 108 | 109 | res = __guess_with_prefixes_or_none( 110 | prefixes, 111 | f"share/qemu/firmware/60-edk2-{edk2_arch}.json" 112 | ) 113 | if res is None: 114 | return None 115 | 116 | import json 117 | with open(res) as file: 118 | path_json = json.load(file) 119 | guess = path_json.get("mapping", {}).get("executable", {}).get("filename", {}) 120 | 121 | return __guess_or_none(guess) if guess else None 122 | -------------------------------------------------------------------------------- /tests/pytest.ini: -------------------------------------------------------------------------------- 1 | [pytest] 2 | markers = 3 | uefi_x64: mark test as running the x64 UEFI loader 4 | uefi_aarch64: mark test as running the aarch64 UEFI loader 5 | bios: mark test as running the BIOS loader 6 | fat: mark test as running on a FAT file system 7 | iso: mark test as running on an ISO9660 file system 8 | hdd: mark test as running as an HDD 9 | -------------------------------------------------------------------------------- /toolchain/toolchain_clang.cmake: -------------------------------------------------------------------------------- 1 | if (APPLE) 2 | execute_process( 3 | COMMAND 4 | brew --prefix lld 5 | OUTPUT_VARIABLE 6 | BREW_LLD_PREFIX 7 | OUTPUT_STRIP_TRAILING_WHITESPACE 8 | ) 9 | set(LLD_LINKER "${BREW_LLD_PREFIX}/bin/ld.lld") 10 | else () 11 | set(LLD_LINKER "lld") 12 | endif () 13 | 14 | # Do this because cmake attempts to link against -lc and crt0 15 | set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) 16 | 17 | if (HYPER_PLATFORM STREQUAL "uefi" AND HYPER_ARCH STREQUAL "amd64") 18 | set(HYPER_TRIPLET_PREFIX "x86_64") 19 | else () 20 | set(HYPER_TRIPLET_PREFIX ${HYPER_ARCH}) 21 | endif () 22 | 23 | if (HYPER_PLATFORM STREQUAL "uefi") 24 | set(HYPER_LLD_FLAVOR link) 25 | set(HYPER_LLD_OUT_TARGET "/out:") 26 | set(HYPER_TRIPLET_POSTFIX "pc-win32-coff") 27 | elseif (HYPER_PLATFORM STREQUAL "bios") 28 | set(HYPER_LLD_FLAVOR ld) 29 | set(HYPER_LLD_OUT_TARGET "-o ") 30 | set(HYPER_TRIPLET_POSTFIX "none-none") 31 | else () 32 | message(FATAL_ERROR "Platform ${HYPER_PLATFORM} is not supported") 33 | endif () 34 | 35 | set(HYPER_COMPILER_PREFIX "${HYPER_TRIPLET_PREFIX}-${HYPER_TRIPLET_POSTFIX}") 36 | 37 | set(CMAKE_C_LINK_EXECUTABLE 38 | " -flavor ${HYPER_LLD_FLAVOR} \ 39 | ${HYPER_LLD_OUT_TARGET} ") 40 | 41 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --target=${HYPER_COMPILER_PREFIX}") 42 | 43 | set(CMAKE_SYSTEM_NAME Linux) 44 | set(CMAKE_SYSTEM_PROCESSOR ${HYPER_ARCH}) 45 | set(CMAKE_SYSROOT "") 46 | 47 | set(CMAKE_C_COMPILER clang) 48 | set(CMAKE_LINKER ${LLD_LINKER}) 49 | -------------------------------------------------------------------------------- /toolchain/toolchain_dummy.cmake: -------------------------------------------------------------------------------- 1 | function(target_compile_options) 2 | endfunction() 3 | -------------------------------------------------------------------------------- /toolchain/toolchain_gcc.cmake: -------------------------------------------------------------------------------- 1 | # Do this because cmake attempts to link against -lc and crt0 2 | set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) 3 | 4 | if (HYPER_PLATFORM STREQUAL "uefi" AND HYPER_ARCH STREQUAL "amd64") 5 | set(HYPER_TRIPLET_PREFIX "x86_64") 6 | else () 7 | set(HYPER_TRIPLET_PREFIX ${HYPER_ARCH}) 8 | endif () 9 | 10 | if (HYPER_PLATFORM STREQUAL "uefi") 11 | set(HYPER_TRIPLET_POSTFIX "w64-mingw32") 12 | elseif (HYPER_PLATFORM STREQUAL "bios") 13 | set(HYPER_TRIPLET_POSTFIX "elf") 14 | else () 15 | message(FATAL_ERROR "Platform ${HYPER_PLATFORM} is not supported") 16 | endif () 17 | 18 | set(HYPER_COMPILER_PREFIX "${HYPER_TRIPLET_PREFIX}-${HYPER_TRIPLET_POSTFIX}") 19 | 20 | set(HYPER_TOOLS_DIR "tools_${HYPER_ARCH}_${HYPER_PLATFORM}_${HYPER_TOOLCHAIN}") 21 | set(HYPER_TOOLCHAIN_ROOT ${CMAKE_CURRENT_LIST_DIR}/${HYPER_TOOLS_DIR}) 22 | set(HYPER_TOOLCHAIN_PATH ${HYPER_TOOLCHAIN_ROOT}/bin) 23 | set(HYPER_TOOLCHAIN_PREFIX ${HYPER_TOOLCHAIN_PATH}/${HYPER_COMPILER_PREFIX}-) 24 | 25 | set(CMAKE_SYSTEM_NAME Linux) 26 | set(CMAKE_SYSTEM_PROCESSOR ${HYPER_ARCH}) 27 | set(CMAKE_SYSROOT "") 28 | 29 | set(CMAKE_C_LINK_EXECUTABLE 30 | " \ 31 | -o ") 32 | 33 | set(CMAKE_C_COMPILER ${HYPER_TOOLCHAIN_PREFIX}gcc) 34 | set(CMAKE_LINKER ${HYPER_TOOLCHAIN_PREFIX}ld) 35 | --------------------------------------------------------------------------------