From 0709f980afbc7ac326c2c2a909f360375a3cc7ba Mon Sep 17 00:00:00 2001 From: David Gonzalez Martin Date: Sun, 9 Jul 2023 11:24:30 -0600 Subject: [PATCH] first commit --- .gitattributes | 9 + .github/FUNDING.yml | 3 + .github/workflows/lightning.yml | 44 + .gitignore | 9 + README.md | 119 ++ bochsrc | 17 + build.zig | 743 ++++++++ config/default.json | 9 + config/gdb_script | 1 + config/image_config.json | 11 + config/qemu.json | 31 + resources/FiraSans-Regular.otf | Bin 0 -> 357996 bytes resources/zap-light16.psf | Bin 0 -> 5312 bytes src/bootloader.zig | 796 ++++++++ src/bootloader/arch.zig | 2 + src/bootloader/arch/x86.zig | 0 src/bootloader/arch/x86/64/smp_trampoline.S | 135 ++ src/bootloader/arch/x86_64.zig | 150 ++ src/bootloader/bios.zig | 649 +++++++ src/bootloader/limine.zig | 369 ++++ src/bootloader/limine/LICENSE.md | 9 + .../limine/arch/x86_64/linker_script.ld | 37 + .../limine/installables/BOOTAA64.EFI | Bin 0 -> 159744 bytes .../limine/installables/BOOTIA32.EFI | Bin 0 -> 208896 bytes .../limine/installables/BOOTX64.EFI | Bin 0 -> 184320 bytes src/bootloader/limine/installables/LICENSE.md | 9 + src/bootloader/limine/installables/Makefile | 45 + src/bootloader/limine/installables/install-sh | 541 ++++++ .../limine/installables/limine-cd-efi.bin | Bin 0 -> 1474560 bytes .../limine/installables/limine-cd.bin | Bin 0 -> 24576 bytes .../limine/installables/limine-deploy.c | 920 +++++++++ .../limine/installables/limine-pxe.bin | Bin 0 -> 21523 bytes .../limine/installables/limine-version.c | 7 + src/bootloader/limine/installables/limine.cfg | 13 + src/bootloader/limine/installables/limine.h | 466 +++++ src/bootloader/limine/installables/limine.sys | Bin 0 -> 159436 bytes src/bootloader/limine/installer.zig | 1676 +++++++++++++++++ src/bootloader/limine/main.zig | 332 ++++ src/bootloader/limine/test.zig | 0 src/bootloader/rise/bios/linker_script.ld | 27 + src/bootloader/rise/bios/main.zig | 275 +++ src/bootloader/rise/bios/test.zig | 0 src/bootloader/rise/bios/unreal_mode.S | 113 ++ src/bootloader/rise/uefi/main.zig | 436 +++++ src/bootloader/rise/uefi/test.zig | 0 src/bootloader/todo/draw_context.zig | 48 + src/bootloader/todo/font.zig | 51 + src/bootloader/todo/smp.zig | 136 ++ src/bootloader/uefi.zig | 88 + src/common.zig | 502 +++++ src/cpu.zig | 435 +++++ src/cpu/arch.zig | 13 + src/cpu/arch/x86/64/init.zig | 1492 +++++++++++++++ src/cpu/arch/x86/64/linker_script.ld | 39 + src/cpu/arch/x86/64/syscall.zig | 281 +++ src/cpu/arch/x86_64.zig | 265 +++ src/cpu/capabilities.zig | 419 +++++ src/cpu/main.zig | 37 + src/cpu/test.zig | 21 + src/cpu/test_runner.zig | 38 + src/host.zig | 74 + src/host/disk_image_builder.zig | 250 +++ src/host/disk_image_builder/boot_disk.zig | 405 ++++ src/host/disk_image_builder/main.zig | 302 +++ src/host/disk_image_builder/test.zig | 224 +++ src/host/runner/main.zig | 350 ++++ src/host/test.zig | 6 + src/lib.zig | 1080 +++++++++++ src/lib/arch.zig | 34 + src/lib/arch/x86.zig | 6 + src/lib/arch/x86/64/registers.zig | 343 ++++ src/lib/arch/x86/common.zig | 59 + src/lib/arch/x86_64.zig | 34 + src/lib/config.zig | 6 + src/lib/crc32.zig | 13 + src/lib/disk.zig | 123 ++ src/lib/extern_enum_array.zig | 231 +++ src/lib/filesystem.zig | 18 + src/lib/filesystem/fat32.zig | 1297 +++++++++++++ src/lib/graphics.zig | 184 ++ src/lib/nls.zig | 21 + src/lib/nls/ascii.zig | 125 ++ src/lib/partition_table.zig | 11 + src/lib/partition_table/gpt.zig | 416 ++++ src/lib/partition_table/mbr.zig | 357 ++++ src/lib/psf1.zig | 13 + src/privileged.zig | 114 ++ src/privileged/acpi.zig | 247 +++ src/privileged/arch.zig | 22 + src/privileged/arch/x86.zig | 11 + src/privileged/arch/x86/32/io.zig | 14 + src/privileged/arch/x86/64/apic.zig | 148 ++ src/privileged/arch/x86/64/io.zig | 44 + src/privileged/arch/x86/64/paging.zig | 1088 +++++++++++ src/privileged/arch/x86/64/registers.zig | 373 ++++ src/privileged/arch/x86/common.zig | 65 + src/privileged/arch/x86_64.zig | 227 +++ src/rise.zig | 23 + src/rise/arch.zig | 7 + src/rise/arch/x64_64.zig | 155 ++ src/rise/capabilities.zig | 381 ++++ src/rise/syscall.zig | 117 ++ src/user.zig | 142 ++ src/user/arch.zig | 16 + src/user/arch/x86_64.zig | 116 ++ src/user/arch/x86_64/linker_script.ld | 24 + src/user/capabilities.zig | 18 + src/user/core_state.zig | 27 + src/user/libc.zig | 7 + src/user/mmu_aware_virtual_address_space.zig | 62 + src/user/physical_map.zig | 25 + src/user/physical_memory_region.zig | 81 + src/user/process.zig | 31 + src/user/programs/device_manager/main.zig | 18 + src/user/programs/device_manager/module.json | 4 + src/user/programs/device_manager/test.zig | 0 src/user/programs/init/main.zig | 25 + src/user/programs/init/module.json | 4 + src/user/programs/init/test.zig | 0 src/user/slot_allocator.zig | 28 + src/user/thread.zig | 172 ++ src/user/virtual_address_space.zig | 60 + tools/format_loopback_fat32.sh | 4 + tools/loopback_end.sh | 4 + tools/loopback_mount.sh | 6 + tools/loopback_start.sh | 6 + 126 files changed, 21766 insertions(+) create mode 100644 .gitattributes create mode 100644 .github/FUNDING.yml create mode 100644 .github/workflows/lightning.yml create mode 100644 .gitignore create mode 100644 README.md create mode 100644 bochsrc create mode 100644 build.zig create mode 100644 config/default.json create mode 100644 config/gdb_script create mode 100644 config/image_config.json create mode 100644 config/qemu.json create mode 100644 resources/FiraSans-Regular.otf create mode 100644 resources/zap-light16.psf create mode 100644 src/bootloader.zig create mode 100644 src/bootloader/arch.zig create mode 100644 src/bootloader/arch/x86.zig create mode 100644 src/bootloader/arch/x86/64/smp_trampoline.S create mode 100644 src/bootloader/arch/x86_64.zig create mode 100644 src/bootloader/bios.zig create mode 100644 src/bootloader/limine.zig create mode 100644 src/bootloader/limine/LICENSE.md create mode 100644 src/bootloader/limine/arch/x86_64/linker_script.ld create mode 100755 src/bootloader/limine/installables/BOOTAA64.EFI create mode 100755 src/bootloader/limine/installables/BOOTIA32.EFI create mode 100755 src/bootloader/limine/installables/BOOTX64.EFI create mode 100644 src/bootloader/limine/installables/LICENSE.md create mode 100644 src/bootloader/limine/installables/Makefile create mode 100755 src/bootloader/limine/installables/install-sh create mode 100644 src/bootloader/limine/installables/limine-cd-efi.bin create mode 100644 src/bootloader/limine/installables/limine-cd.bin create mode 100644 src/bootloader/limine/installables/limine-deploy.c create mode 100644 src/bootloader/limine/installables/limine-pxe.bin create mode 100644 src/bootloader/limine/installables/limine-version.c create mode 100644 src/bootloader/limine/installables/limine.cfg create mode 100644 src/bootloader/limine/installables/limine.h create mode 100755 src/bootloader/limine/installables/limine.sys create mode 100644 src/bootloader/limine/installer.zig create mode 100644 src/bootloader/limine/main.zig create mode 100644 src/bootloader/limine/test.zig create mode 100644 src/bootloader/rise/bios/linker_script.ld create mode 100644 src/bootloader/rise/bios/main.zig create mode 100644 src/bootloader/rise/bios/test.zig create mode 100644 src/bootloader/rise/bios/unreal_mode.S create mode 100644 src/bootloader/rise/uefi/main.zig create mode 100644 src/bootloader/rise/uefi/test.zig create mode 100644 src/bootloader/todo/draw_context.zig create mode 100644 src/bootloader/todo/font.zig create mode 100644 src/bootloader/todo/smp.zig create mode 100644 src/bootloader/uefi.zig create mode 100644 src/common.zig create mode 100644 src/cpu.zig create mode 100644 src/cpu/arch.zig create mode 100644 src/cpu/arch/x86/64/init.zig create mode 100644 src/cpu/arch/x86/64/linker_script.ld create mode 100644 src/cpu/arch/x86/64/syscall.zig create mode 100644 src/cpu/arch/x86_64.zig create mode 100644 src/cpu/capabilities.zig create mode 100644 src/cpu/main.zig create mode 100644 src/cpu/test.zig create mode 100644 src/cpu/test_runner.zig create mode 100644 src/host.zig create mode 100644 src/host/disk_image_builder.zig create mode 100644 src/host/disk_image_builder/boot_disk.zig create mode 100644 src/host/disk_image_builder/main.zig create mode 100644 src/host/disk_image_builder/test.zig create mode 100644 src/host/runner/main.zig create mode 100644 src/host/test.zig create mode 100644 src/lib.zig create mode 100644 src/lib/arch.zig create mode 100644 src/lib/arch/x86.zig create mode 100644 src/lib/arch/x86/64/registers.zig create mode 100644 src/lib/arch/x86/common.zig create mode 100644 src/lib/arch/x86_64.zig create mode 100644 src/lib/config.zig create mode 100644 src/lib/crc32.zig create mode 100644 src/lib/disk.zig create mode 100644 src/lib/extern_enum_array.zig create mode 100644 src/lib/filesystem.zig create mode 100644 src/lib/filesystem/fat32.zig create mode 100644 src/lib/graphics.zig create mode 100644 src/lib/nls.zig create mode 100644 src/lib/nls/ascii.zig create mode 100644 src/lib/partition_table.zig create mode 100644 src/lib/partition_table/gpt.zig create mode 100644 src/lib/partition_table/mbr.zig create mode 100644 src/lib/psf1.zig create mode 100644 src/privileged.zig create mode 100644 src/privileged/acpi.zig create mode 100644 src/privileged/arch.zig create mode 100644 src/privileged/arch/x86.zig create mode 100644 src/privileged/arch/x86/32/io.zig create mode 100644 src/privileged/arch/x86/64/apic.zig create mode 100644 src/privileged/arch/x86/64/io.zig create mode 100644 src/privileged/arch/x86/64/paging.zig create mode 100644 src/privileged/arch/x86/64/registers.zig create mode 100644 src/privileged/arch/x86/common.zig create mode 100644 src/privileged/arch/x86_64.zig create mode 100644 src/rise.zig create mode 100644 src/rise/arch.zig create mode 100644 src/rise/arch/x64_64.zig create mode 100644 src/rise/capabilities.zig create mode 100644 src/rise/syscall.zig create mode 100644 src/user.zig create mode 100644 src/user/arch.zig create mode 100644 src/user/arch/x86_64.zig create mode 100644 src/user/arch/x86_64/linker_script.ld create mode 100644 src/user/capabilities.zig create mode 100644 src/user/core_state.zig create mode 100644 src/user/libc.zig create mode 100644 src/user/mmu_aware_virtual_address_space.zig create mode 100644 src/user/physical_map.zig create mode 100644 src/user/physical_memory_region.zig create mode 100644 src/user/process.zig create mode 100644 src/user/programs/device_manager/main.zig create mode 100644 src/user/programs/device_manager/module.json create mode 100644 src/user/programs/device_manager/test.zig create mode 100644 src/user/programs/init/main.zig create mode 100644 src/user/programs/init/module.json create mode 100644 src/user/programs/init/test.zig create mode 100644 src/user/slot_allocator.zig create mode 100644 src/user/thread.zig create mode 100644 src/user/virtual_address_space.zig create mode 100755 tools/format_loopback_fat32.sh create mode 100755 tools/loopback_end.sh create mode 100755 tools/loopback_mount.sh create mode 100755 tools/loopback_start.sh diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..4589729 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,9 @@ +# Default behavior, if core.autocrlf is unset. +* text=auto + +# Files to be converted to native line endings on checkout. +*.cpp text +*.h text + +# Text files to always have LF (unix) line endings on checkout. +*.zig text eol=lf diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..c44cc56 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,3 @@ +# These are supported funding model platforms + +github: [davidgm94] diff --git a/.github/workflows/lightning.yml b/.github/workflows/lightning.yml new file mode 100644 index 0000000..cc6f4ec --- /dev/null +++ b/.github/workflows/lightning.yml @@ -0,0 +1,44 @@ +name: Lightning + +on: + pull_request: + paths: + - ".github/workflows/lightning.yml" + - "**.zig" + push: + paths: + - ".github/workflows/lightning.yml" + - "**.zig" + branches: + - main + schedule: + - cron: "0 0 * * *" +concurrency: + # Cancels pending runs when a PR gets updated. + group: ${{ github.head_ref || github.run_id }}-${{ github.actor }} + cancel-in-progress: true + +jobs: + build: + strategy: + matrix: + os: [ + ubuntu-latest, + macos-latest, + windows-latest, + ] + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v3 + - uses: davidgm94/setup-qemu@v3 + - uses: goto-bus-stop/setup-zig@v2 + with: + version: master + + - name: Zig environment variables + run: zig env + - name: Build test executables + run: zig build all_tests -Dci --verbose + - name: Test with QEMU + run: zig build test_all -Dci --verbose + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e0f2feb --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +zig-cache/* +zig-out/* +*.png +logfile +debug_disk +.gdb_history +bochsout.txt +*.hdd +loopback_device diff --git a/README.md b/README.md new file mode 100644 index 0000000..a5948b5 --- /dev/null +++ b/README.md @@ -0,0 +1,119 @@ +# Rise: an attempt to write a better operating system + +![Build status](https://img.shields.io/github/actions/workflow/status/davidgm94/rise/lightning.yml?branch=main) + +An experiment of an operating system for modern 64-bit architectures which focuses on building robust, fast and usable system software and learning how to do it along the way. + +The current plan is to explore the idea of the multikernel exposed in the Barrelfish and Arrakis papers (very roughly simplified, an exokernel per core). Hopefully this academic model proves worthy, resulting in a big improvement in multiple aspects. If not, a hybrid kernel model with high-performance async syscalls will be used. + +The operating system design for developers aims at fast iteration times, so there are no external dependencies aside from the execution environment and the compiler. + +Currently only the Limine bootloader and a custom one are supported. Both only support BIOS and UEFI for the x86_64 architecture and both implement SMP trampoline, modules, memory map and framebuffer, among other features. + +The disk image creation is currently raw (not ISO format) made by a handmade written-in-Zig FAT32 driver which needs severe improvements and bug fixing, but it does the job for now. + +For each run, Github CI currently compiles all build and test artifacts and tests all the guest (only x86_64 for now) and host executables. Guest testing is done through QEMU TCG. + +## High-level design goals + +- Multikernel model, which would grant more safety and speed. +- Try to supress interpreted/JIT language uses in every field as much as possible, preferring compiled type-safe native languages instead and then favoring speed, robustness and safety. +- Make everything go reasonably fast (as fast as possible). +- Usable desktop, for both basic and developer purposes. +- Sandboxed execution of programs by default. +- New library and executable format for modern times, which aims at performance and scalability and admits both static and dynamic linking, preferring static. +- Prefer typed commmunication as opposed to strings, for example in program arguments and configuration files. +- Clean up shells, move away from current ones as much as possible: make it type-safe and compiled, commands are function calls from libraries instead of executables, etc. +- Promote open-source driver code (especially for GPUs, since these drivers being close-source is hurting the computing space) and simplified drivers through ISA/DMA. +- (far away in the future) Think of a way to substitute browser's Javascript for native compiled code. + +## External dependencies to compile and run the code (executables your machine should have in the PATH environment variable) + +* The Zig compiler (master) - This is required to compile and run the code. Apart from the operating system being written in Zig, Zig is used as a build system, so no platform-specific scripting language is needed. The easiest way to get it is to download the master binary at the website. +* QEMU - to load and execute the operating system in a virtual environment +* GDB - only for debugging + +## Internal dependencies + +* STB TTF + +## Build and run instructions + +- To build for the default target options (located in config/default.json): `zig build` +- To build and run for the default target options: `zig build run` +- To build and debug for the default target options: `zig build debug` +- To build and test for the default target options: `zig build test` +- To build and debug the tests for the default target options: `zig build test_debug` +- To build all host and guest normal artifacts: `zig build all` +- To build all host and guest test artifacts: `zig build all_tests` +- To build and run all host and guest tests: `zig build test_all` +- To run any other specialized step, please consult the steps listed in `zig build --help` + +## Target architectures: + +- [x] x86_64 +- [ ] RISC-V 64 +- [ ] aarch64 + +Currently only x86_64 is supported, although aarch64 and RISC-V 64 are planned for implementation. + +## Target execution environments + +- [x] Real hardware. BIG DISCLAIMER: Support on real hardware is really primitive as it has been implemented recently. Only the UEFI boot protocol is tested and should only be tried/tested if you know what you are doing. Moreover, since currently there is no CI for real hardware, due to the diversity of the x86-64 platform and the lack of testing, real hardware might not work as emulated ones do. + +### Emulators/Hypervisors + +#### QEMU + - [x] KVM + - [ ] XEN + - [ ] HAX + - [ ] HVF + - [ ] NVMM + - [ ] WHPX + - [x] TCG + +##### Degree of QEMU emulation supported right now: + +- Linux + +* [x] Run +* [x] Debug + +- Windows + +* [x] Run +* [ ] Debug + +- MacOS + +* [x] Run +* [x] Debug + +#### Other execution environments + +- [ ] Bochs +- [ ] VMWare +- [ ] VirtualBox + +## Next tasks to be done + +### General + +* Implement the CPU driver according to the `multikernel` model. + +## Inspirations and acknowledgements + +- Linux kernel: https://kernel.org +- Barrelfish and Arrakis: +* https://barrelfish.org/ +* https://arrakis.cs.washington.edu/ +- Essence: https://gitlab.com/nakst/essence/ +- Limine bootloader: https://github.com/limine-bootloader/limine +- Florence: https://github.com/FlorenceOS/Florence +- Managarm: https://github.com/managarm/managarm +- Jonathan Blow ideas on how an operating system should be: +* https://youtu.be/k0uE_chSnV8 +* https://youtu.be/xXSIs4aTqhI +- Casey Muratori's lecture: https://youtu.be/kZRE7HIO3vk +- Zig Discord channel: https://discord.gg/gxsFFjE +- OSDev Discord channel: https://discord.gg/RnCtsqD diff --git a/bochsrc b/bochsrc new file mode 100644 index 0000000..3d7d9dd --- /dev/null +++ b/bochsrc @@ -0,0 +1,17 @@ +cpu: count=2, reset_on_triple_fault=0 + +display_library: x, options="gui_debug" + +megs: 512 + +clock: sync=realtime, time0=local + +ata0-master: type=disk, path="zig-cache/rise_Debug_x86_64_rise_bios_normal_exe.hdd", mode=flat + +boot: c + +log: ./bochsout.txt + +mouse: enabled=0 + +magic_break: enabled=1 diff --git a/build.zig b/build.zig new file mode 100644 index 0000000..f3a07b2 --- /dev/null +++ b/build.zig @@ -0,0 +1,743 @@ +const std = @import("std"); +const common = @import("src/common.zig"); +const os = common.os; + +// Build types +const Build = std.Build; +const CompileStep = std.Build.CompileStep; +const FileSource = std.Build.FileSource; +const Module = std.Build.Module; +const ModuleDependency = std.Build.ModuleDependency; +const OptionsStep = std.Build.OptionsStep; +const RunStep = std.Build.RunStep; +const Step = std.Build.Step; + +const assert = std.debug.assert; +const Bootloader = common.Bootloader; +const Configuration = common.Configuration; +const Cpu = common.Cpu; +const CrossTarget = common.CrossTarget; +const DiskType = common.DiskType; +const ExecutionType = common.ExecutionType; +const ExecutionEnvironment = common.ExecutionEnvironment; +const FilesystemType = common.FilesystemType; +const OptimizeMode = common.OptimizeMode; +const QEMUOptions = common.QEMUOptions; +const RiseProgram = common.RiseProgram; +const Suffix = common.Suffix; +const Target = common.Target; + +const Error = error{ + not_implemented, + architecture_not_supported, + failed_to_run, +}; + +const source_root_dir = "src"; +const user_program_dir_path = "src/user/programs"; + +var ci = false; +var debug_user = false; +var debug_loader = false; +var modules = Modules{}; +var b: *Build = undefined; +var build_steps: *BuildSteps = undefined; +var default_configuration: Configuration = undefined; +var user_modules: []const common.Module = undefined; +var options = Options{}; + +pub fn build(b_arg: *Build) !void { + b = b_arg; + ci = b.option(bool, "ci", "CI mode") orelse false; + debug_user = b.option(bool, "debug_user", "Debug user program") orelse false; + debug_loader = b.option(bool, "debug_loader", "Debug loader program") orelse false; + const default_cfg_override = b.option([]const u8, "default", "Default configuration JSON file") orelse "config/default.json"; + modules = blk: { + var mods = Modules{}; + inline for (comptime common.enumValues(ModuleID)) |module_id| { + mods.modules.set(module_id, b.createModule(.{ + .source_file = FileSource.relative(switch (module_id) { + .limine_installer => "src/bootloader/limine/installer.zig", + else => switch (module_id) { + .bios, .uefi, .limine => "src/bootloader", + else => "src", + } ++ "/" ++ @tagName(module_id) ++ ".zig", + }), + })); + } + + try mods.setDependencies(.lib, &.{}); + try mods.setDependencies(.host, &.{.lib}); + try mods.setDependencies(.bootloader, &.{ .lib, .privileged }); + try mods.setDependencies(.bios, &.{ .lib, .privileged }); + try mods.setDependencies(.limine, &.{ .lib, .privileged }); + try mods.setDependencies(.uefi, &.{ .lib, .privileged }); + try mods.setDependencies(.limine_installer, &.{ .lib, .privileged }); + try mods.setDependencies(.privileged, &.{ .lib, .bootloader }); + try mods.setDependencies(.cpu, &.{ .privileged, .lib, .bootloader, .rise }); + try mods.setDependencies(.rise, &.{.lib}); + try mods.setDependencies(.user, &.{ .lib, .rise }); + + break :blk mods; + }; + + options = blk: { + var opts = Options{}; + opts.createOption(.bootloader); + opts.createOption(.cpu); + opts.createOption(.user); + opts.createOption(.host); + break :blk opts; + }; + + default_configuration = blk: { + const default_json_file = try std.fs.cwd().readFileAlloc(b.allocator, default_cfg_override, common.maxInt(usize)); + const parsed_cfg = try std.json.parseFromSlice(Configuration, b.allocator, default_json_file, .{}); + const cfg = parsed_cfg.value; + + const optimize_mode = b.option( + std.builtin.Mode, + "optimize", + "Prioritize performance, safety, or binary size (-O flag)", + ) orelse cfg.optimize_mode; + + break :blk Configuration{ + .architecture = b.standardTargetOptions(.{ .default_target = .{ .cpu_arch = cfg.architecture } }).getCpuArch(), + .bootloader = cfg.bootloader, + .boot_protocol = cfg.boot_protocol, + .execution_environment = cfg.execution_environment, + .optimize_mode = optimize_mode, + .execution_type = cfg.execution_type, + .executable_kind = .exe, + }; + }; + + build_steps = try b.allocator.create(BuildSteps); + build_steps.* = .{ + .build_all = b.step("all", "Build all the artifacts"), + .build_all_tests = b.step("all_tests", "Build all the artifacts related to tests"), + .run = b.step("run", "Run the operating system through an emulator"), + .debug = b.step("debug", "Debug the operating system through an emulator"), + .test_run = b.step("test", "Run unit tests"), + .test_debug = b.step("test_debug", "Debug unit tests"), + .test_all = b.step("test_all", "Run all unit tests"), + .test_host = b.step("test_host", "Run host unit tests"), + }; + + const disk_image_builder_modules = &.{ .lib, .host, .bootloader, .limine_installer, .bios }; + const disk_image_root_path = "src/host/disk_image_builder"; + const disk_image_builder = blk: { + const exe = try addCompileStep(.{ + .kind = .exe, + .name = "disk_image_builder", + .root_project_path = disk_image_root_path, + .modules = disk_image_builder_modules, + }); + + b.default_step.dependOn(&exe.step); + + break :blk exe; + }; + + const runner = blk: { + const exe = try addCompileStep(.{ + .kind = .exe, + .name = "runner", + .root_project_path = "src/host/runner", + .modules = &.{ .lib, .host }, + }); + + b.default_step.dependOn(&exe.step); + + break :blk exe; + }; + + const native_tests = [_]struct { + name: []const u8, + root_project_path: []const u8, + modules: []const ModuleID, + c: ?C = null, + + const C = struct { + include_paths: []const []const u8, + source_files: []const SourceFile, + link_libc: bool, + link_libcpp: bool, + + const SourceFile = struct { + path: []const u8, + flags: []const []const u8, + }; + }; + }{ + .{ + .name = "host_native_test", + .root_project_path = "src/host", + .modules = &.{ .lib, .host }, + }, + .{ + .name = "disk_image_builder_native_test", + .root_project_path = disk_image_root_path, + .modules = disk_image_builder_modules, + .c = .{ + .include_paths = &.{"src/bootloader/limine/installables"}, + .source_files = &.{ + .{ + .path = "src/bootloader/limine/installables/limine-deploy.c", + .flags = &.{}, + }, + }, + .link_libc = true, + .link_libcpp = false, + }, + }, + }; + + const native_test_optimize_mode = .ReleaseFast; + for (native_tests) |native_test| { + const test_name = try std.mem.concat(b.allocator, u8, &.{ native_test.name, "_", @tagName(native_test_optimize_mode) }); + const test_exe = try addCompileStep(.{ + .name = test_name, + .root_project_path = native_test.root_project_path, + .optimize_mode = native_test_optimize_mode, + .modules = native_test.modules, + .kind = .@"test", + }); + + if (native_test.c) |c| { + for (c.include_paths) |include_path| { + test_exe.addIncludePath(include_path); + } + + for (c.source_files) |source_file| { + test_exe.addCSourceFile(source_file.path, source_file.flags); + } + + if (c.link_libc) { + test_exe.linkLibC(); + } + + if (c.link_libcpp) { + test_exe.linkLibCpp(); + } + } + + const run_test_step = b.addRunArtifact(test_exe); + //run_test_step.condition = .always; + build_steps.test_all.dependOn(&run_test_step.step); + build_steps.test_host.dependOn(&run_test_step.step); + } + + { + var user_module_list = std.ArrayList(common.Module).init(b.allocator); + var user_program_dir = try std.fs.cwd().openIterableDir(user_program_dir_path, .{ .access_sub_paths = true }); + defer user_program_dir.close(); + + var user_program_iterator = user_program_dir.iterate(); + + while (try user_program_iterator.next()) |entry| { + const dir_name = entry.name; + const file_path = try std.mem.concat(b.allocator, u8, &.{ dir_name, "/module.json" }); + const file = try user_program_dir.dir.readFileAlloc(b.allocator, file_path, common.maxInt(usize)); + const parsed_user_program = try std.json.parseFromSlice(common.UserProgram, b.allocator, file, .{}); + const user_program = parsed_user_program.value; + try user_module_list.append(.{ + .program = user_program, + .name = b.dupe(dir_name), // we have to dupe here otherwise Windows CI fails + }); + } + + user_modules = user_module_list.items; + } + + const executable_kinds = [2]CompileStep.Kind{ .exe, .@"test" }; + + for (common.enumValues(OptimizeMode)) |optimize_mode| { + for (common.supported_architectures, 0..) |architecture, architecture_index| { + const user_target = try getTarget(architecture, .user); + + for (executable_kinds) |executable_kind| { + const is_test = executable_kind == .@"test"; + const cpu_driver_path = "src/cpu"; + const target = try getTarget(architecture, .privileged); + const cpu_driver = try addCompileStep(.{ + .kind = executable_kind, + .name = "cpu_driver", + .root_project_path = cpu_driver_path, + .target = target, + .optimize_mode = optimize_mode, + .modules = &.{ .lib, .bootloader, .privileged, .cpu, .rise }, + }); + + cpu_driver.force_pic = true; + cpu_driver.disable_stack_probing = true; + cpu_driver.stack_protector = false; + cpu_driver.strip = false; + cpu_driver.red_zone = false; + cpu_driver.omit_frame_pointer = false; + + cpu_driver.code_model = switch (architecture) { + .x86_64 => .kernel, + .riscv64 => .medium, + .aarch64 => .small, + else => return Error.architecture_not_supported, + }; + + const cpu_driver_linker_script_path = FileSource.relative(try std.mem.concat(b.allocator, u8, &.{ cpu_driver_path, "/arch/", switch (architecture) { + .x86_64 => "x86/64", + .x86 => "x86/32", + else => @tagName(architecture), + }, "/linker_script.ld" })); + + cpu_driver.setLinkerScriptPath(cpu_driver_linker_script_path); + + var user_module_list = try std.ArrayList(*CompileStep).initCapacity(b.allocator, user_modules.len); + const user_architecture_source_path = try std.mem.concat(b.allocator, u8, &.{ "src/user/arch/", @tagName(architecture), "/" }); + const user_linker_script_path = FileSource.relative(try std.mem.concat(b.allocator, u8, &.{ user_architecture_source_path, "linker_script.ld" })); + for (user_modules) |module| { + const user_module = try addCompileStep(.{ + .kind = executable_kind, + .name = module.name, + .root_project_path = try std.mem.concat(b.allocator, u8, &.{ user_program_dir_path, "/", module.name }), + .target = user_target, + .optimize_mode = optimize_mode, + .modules = &.{ .lib, .user, .rise }, + }); + user_module.strip = false; + + user_module.setLinkerScriptPath(user_linker_script_path); + + user_module_list.appendAssumeCapacity(user_module); + } + + const bootloaders = common.architecture_bootloader_map[architecture_index]; + for (bootloaders) |bootloader_struct| { + const bootloader = bootloader_struct.id; + for (bootloader_struct.protocols) |boot_protocol| { + const rise_loader_path = "src/bootloader/rise/"; + const limine_loader_path = "src/bootloader/limine/"; + const bootloader_name = "loader"; + const bootloader_modules = [_]ModuleID{ .lib, .bootloader, .privileged }; + + const bootloader_compile_step = switch (bootloader) { + .rise => switch (boot_protocol) { + .bios => switch (architecture) { + .x86_64 => blk: { + const bootloader_path = rise_loader_path ++ "bios"; + const executable = try addCompileStep(.{ + .kind = executable_kind, + .name = bootloader_name, + .root_project_path = bootloader_path, + .target = try getTarget(.x86, .privileged), + .optimize_mode = .ReleaseSmall, + .modules = &(bootloader_modules ++ .{.bios}), + }); + + executable.strip = true; + + executable.addAssemblyFile("src/bootloader/arch/x86/64/smp_trampoline.S"); + executable.addAssemblyFile(bootloader_path ++ "/unreal_mode.S"); + executable.setLinkerScriptPath(FileSource.relative(bootloader_path ++ "/linker_script.ld")); + executable.code_model = .small; + + break :blk executable; + }, + else => return Error.architecture_not_supported, + }, + .uefi => blk: { + const bootloader_path = rise_loader_path ++ "uefi"; + const executable = try addCompileStep(.{ + .kind = executable_kind, + .name = bootloader_name, + .root_project_path = bootloader_path, + .target = .{ + .cpu_arch = architecture, + .os_tag = .uefi, + .abi = .msvc, + }, + .optimize_mode = .ReleaseSafe, + .modules = &(bootloader_modules ++ .{.uefi}), + }); + + executable.strip = true; + + switch (architecture) { + .x86_64 => executable.addAssemblyFile("src/bootloader/arch/x86/64/smp_trampoline.S"), + else => {}, + } + + break :blk executable; + }, + }, + .limine => blk: { + const bootloader_path = limine_loader_path; + const executable = try addCompileStep(.{ + .kind = executable_kind, + .name = bootloader_name, + .root_project_path = bootloader_path, + .target = target, + .optimize_mode = .ReleaseSafe, + .modules = &(bootloader_modules ++ .{.limine}), + }); + + executable.force_pic = true; + executable.omit_frame_pointer = false; + executable.want_lto = false; + executable.strip = false; + + executable.code_model = cpu_driver.code_model; + + executable.setLinkerScriptPath(FileSource.relative(try common.concat(b.allocator, u8, &.{ limine_loader_path ++ "arch/", @tagName(architecture), "/linker_script.ld" }))); + + break :blk executable; + }, + }; + + bootloader_compile_step.disable_stack_probing = true; + bootloader_compile_step.stack_protector = false; + bootloader_compile_step.red_zone = false; + + if (architecture == default_configuration.architecture and bootloader == default_configuration.bootloader and boot_protocol == default_configuration.boot_protocol and optimize_mode == default_configuration.optimize_mode and !is_test) { + addObjdump(bootloader_compile_step, bootloader_name); + addFileSize(bootloader_compile_step, bootloader_name); + } + + const execution_environments: []const ExecutionEnvironment = switch (bootloader) { + .rise, .limine => switch (boot_protocol) { + .bios => switch (architecture) { + .x86_64 => &.{.qemu}, + else => return Error.architecture_not_supported, + }, + .uefi => &.{.qemu}, + }, + }; + + const execution_types: []const ExecutionType = + switch (common.canVirtualizeWithQEMU(architecture, ci)) { + true => &.{ .emulated, .accelerated }, + false => &.{.emulated}, + }; + + for (execution_types) |execution_type| { + for (execution_environments) |execution_environment| { + const configuration = Configuration{ + .architecture = architecture, + .bootloader = bootloader, + .boot_protocol = boot_protocol, + .optimize_mode = optimize_mode, + .execution_environment = execution_environment, + .execution_type = execution_type, + .executable_kind = executable_kind, + }; + + var disk_argument_parser = common.ArgumentParser.DiskImageBuilder{}; + const disk_image_builder_run = b.addRunArtifact(disk_image_builder); + const disk_image_path = disk_image_builder_run.addOutputFileArg("disk.hdd"); + + while (disk_argument_parser.next()) |argument_type| switch (argument_type) { + .configuration => inline for (common.fields(Configuration)) |field| disk_image_builder_run.addArg(@tagName(@field(configuration, field.name))), + .image_configuration_path => disk_image_builder_run.addArg(common.ImageConfig.default_path), + .disk_image_path => { + // Must be first + assert(@intFromEnum(argument_type) == 0); + }, + .bootloader => { + disk_image_builder_run.addArtifactArg(bootloader_compile_step); + }, + .cpu => disk_image_builder_run.addArtifactArg(cpu_driver), + .user_programs => for (user_module_list.items) |user_module| disk_image_builder_run.addArtifactArg(user_module), + }; + + const user_init = user_module_list.items[0]; + + const runner_run = try newRunnerRunArtifact(.{ + .configuration = configuration, + .disk_image_path = disk_image_path, + .cpu_driver = cpu_driver, + .loader = bootloader_compile_step, + .user_init = user_init, + .runner = runner, + .qemu_options = .{ + .is_debug = false, + .is_test = is_test, + }, + }); + const runner_debug = try newRunnerRunArtifact(.{ + .configuration = configuration, + .disk_image_path = disk_image_path, + .cpu_driver = cpu_driver, + .loader = bootloader_compile_step, + .user_init = user_init, + .runner = runner, + .qemu_options = .{ + .is_debug = true, + .is_test = is_test, + }, + }); + + if (is_test) { + build_steps.test_all.dependOn(&runner_run.step); + } + + if (architecture == default_configuration.architecture and bootloader == default_configuration.bootloader and boot_protocol == default_configuration.boot_protocol and optimize_mode == default_configuration.optimize_mode and execution_environment == default_configuration.execution_environment and execution_type == default_configuration.execution_type) { + if (is_test) { + build_steps.test_run.dependOn(&runner_run.step); + build_steps.test_debug.dependOn(&runner_debug.step); + } else { + build_steps.run.dependOn(&runner_run.step); + build_steps.debug.dependOn(&runner_debug.step); + + b.default_step.dependOn(&bootloader_compile_step.step); + + b.default_step.dependOn(&cpu_driver.step); + + for (user_module_list.items) |user_module| { + b.default_step.dependOn(&user_module.step); + } + + const artifacts: []const *CompileStep = &.{ cpu_driver, user_init }; + const artifact_names: []const []const u8 = &.{ "cpu", "init" }; + + inline for (artifact_names, 0..) |artifact_name, index| { + const artifact = artifacts[index]; + addObjdump(artifact, artifact_name); + addFileSize(artifact, artifact_name); + } + } + } + } + } + } + } + } + } + } + + if (os == .linux) { + const generate_command = b.addSystemCommand(&.{ "dot", "-Tpng" }); + generate_command.addFileSourceArg(FileSource.relative("capabilities.dot")); + generate_command.addArg("-o"); + const png = generate_command.addOutputFileArg("capabilities.png"); + + const visualize_command = b.addSystemCommand(&.{"xdg-open"}); + visualize_command.addFileSourceArg(png); + + const dot_command = b.step("dot", "TEMPORARY: (developers only) Generate a graph and visualize it"); + dot_command.dependOn(&visualize_command.step); + } +} + +const Options = struct { + arr: std.EnumArray(RiseProgram, *OptionsStep) = std.EnumArray(RiseProgram, *OptionsStep).initUndefined(), + + pub fn createOption(options_struct: *Options, rise_program: RiseProgram) void { + const new_options = b.addOptions(); + new_options.addOption(RiseProgram, "program_type", rise_program); + options_struct.arr.set(rise_program, new_options); + } +}; + +const BuildSteps = struct { + build_all: *Step, + build_all_tests: *Step, + debug: *Step, + run: *Step, + test_run: *Step, + test_debug: *Step, + test_all: *Step, + test_host: *Step, +}; + +fn addObjdump(artifact: *CompileStep, comptime name: []const u8) void { + switch (os) { + .linux, .macos => { + const objdump = b.addSystemCommand(&.{ "objdump", "-dxS", "-Mintel" }); + objdump.addArtifactArg(artifact); + const objdump_step = b.step("objdump_" ++ name, "Objdump " ++ name); + objdump_step.dependOn(&objdump.step); + }, + else => {}, + } +} + +fn addFileSize(artifact: *CompileStep, comptime name: []const u8) void { + switch (os) { + .linux, .macos => { + const file_size = b.addSystemCommand(switch (os) { + .linux => &.{ "stat", "-c", "%s" }, + .macos => &.{ "wc", "-c" }, + else => unreachable, + }); + file_size.addArtifactArg(artifact); + + const file_size_step = b.step("file_size_" ++ name, "Get the file size of " ++ name); + file_size_step.dependOn(&file_size.step); + }, + else => {}, + } +} + +fn newRunnerRunArtifact(arguments: struct { + configuration: Configuration, + disk_image_path: FileSource, + loader: *CompileStep, + runner: *CompileStep, + cpu_driver: *CompileStep, + user_init: *CompileStep, + qemu_options: QEMUOptions, +}) !*RunStep { + const runner = b.addRunArtifact(arguments.runner); + var argument_parser = common.ArgumentParser.Runner{}; + while (argument_parser.next()) |argument_type| switch (argument_type) { + .configuration => inline for (common.fields(Configuration)) |field| runner.addArg(@tagName(@field(arguments.configuration, field.name))), + .image_configuration_path => runner.addArg(common.ImageConfig.default_path), + .cpu_driver => runner.addArtifactArg(arguments.cpu_driver), + .loader_path => runner.addArtifactArg(arguments.loader), + .init => runner.addArtifactArg(arguments.user_init), + .disk_image_path => runner.addFileSourceArg(arguments.disk_image_path), + .qemu_options => inline for (common.fields(QEMUOptions)) |field| runner.addArg(if (@field(arguments.qemu_options, field.name)) "true" else "false"), + .ci => runner.addArg(if (ci) "true" else "false"), + .debug_user => runner.addArg(if (debug_user) "true" else "false"), + .debug_loader => runner.addArg(if (debug_loader) "true" else "false"), + }; + + return runner; +} + +const ExecutableDescriptor = struct { + kind: CompileStep.Kind, + name: []const u8, + root_project_path: []const u8, + target: CrossTarget = .{}, + optimize_mode: OptimizeMode = .Debug, + modules: []const ModuleID, +}; + +fn addCompileStep(executable_descriptor: ExecutableDescriptor) !*CompileStep { + const main_file = try std.mem.concat(b.allocator, u8, &.{ executable_descriptor.root_project_path, "/main.zig" }); + const compile_step = switch (executable_descriptor.kind) { + .exe => blk: { + const executable = b.addExecutable(.{ + .name = executable_descriptor.name, + .root_source_file = FileSource.relative(main_file), + .target = executable_descriptor.target, + .optimize = executable_descriptor.optimize_mode, + }); + + build_steps.build_all.dependOn(&executable.step); + + break :blk executable; + }, + .@"test" => blk: { + const test_file = FileSource.relative(try std.mem.concat(b.allocator, u8, &.{ executable_descriptor.root_project_path, "/test.zig" })); + const test_exe = b.addTest(.{ + .name = executable_descriptor.name, + .root_source_file = test_file, + .target = executable_descriptor.target, + .optimize = executable_descriptor.optimize_mode, + .test_runner = if (executable_descriptor.target.os_tag) |_| main_file else null, + }); + + build_steps.build_all_tests.dependOn(&test_exe.step); + + break :blk test_exe; + }, + else => return Error.not_implemented, + }; + + compile_step.link_gc_sections = true; + + if (executable_descriptor.target.getOs().tag == .freestanding) { + compile_step.entry_symbol_name = "_start"; + } + + compile_step.setMainPkgPath(source_root_dir); + + for (executable_descriptor.modules) |module| { + modules.addModule(compile_step, module); + } + + return compile_step; +} + +const ModuleID = enum { + /// This module has typical common stuff used everywhere + lib, + /// This module contains code that is used by host programs when building and trying to run the OS + host, + /// This module contains code related to the bootloaders + bootloader, + bios, + uefi, + limine, + limine_installer, + /// This module contains code that is used by Rise privileged programs + privileged, + /// This module contains code that is unique to Rise CPU drivers + cpu, + /// This module contains code that is used by userspace programs + user, + /// This module contains code that is interacting between userspace and cpu in Rise + rise, +}; + +pub const Modules = struct { + modules: std.EnumArray(ModuleID, *Module) = std.EnumArray(ModuleID, *Module).initUndefined(), + dependencies: std.EnumArray(ModuleID, []const ModuleDependency) = std.EnumArray(ModuleID, []const ModuleDependency).initUndefined(), + + fn addModule(mods: Modules, compile_step: *CompileStep, module_id: ModuleID) void { + compile_step.addModule(@tagName(module_id), mods.modules.get(module_id)); + } + + fn setDependencies(mods: Modules, module_id: ModuleID, dependencies: []const ModuleID) !void { + const module = mods.modules.get(module_id); + try module.dependencies.put(@tagName(module_id), module); + + for (dependencies) |dependency_id| { + const dependency_module = mods.modules.get(dependency_id); + try module.dependencies.put(@tagName(dependency_id), dependency_module); + } + } +}; + +fn getTarget(asked_arch: Cpu.Arch, execution_mode: common.TraditionalExecutionMode) Error!CrossTarget { + var enabled_features = Cpu.Feature.Set.empty; + var disabled_features = Cpu.Feature.Set.empty; + + if (execution_mode == .privileged) { + switch (asked_arch) { + .x86, .x86_64 => { + // disable FPU + const Feature = Target.x86.Feature; + disabled_features.addFeature(@intFromEnum(Feature.x87)); + disabled_features.addFeature(@intFromEnum(Feature.mmx)); + disabled_features.addFeature(@intFromEnum(Feature.sse)); + disabled_features.addFeature(@intFromEnum(Feature.sse2)); + disabled_features.addFeature(@intFromEnum(Feature.avx)); + disabled_features.addFeature(@intFromEnum(Feature.avx2)); + disabled_features.addFeature(@intFromEnum(Feature.avx512f)); + + enabled_features.addFeature(@intFromEnum(Feature.soft_float)); + }, + else => return Error.architecture_not_supported, + } + } + + return CrossTarget{ + .cpu_arch = asked_arch, + .cpu_model = switch (common.cpu.arch) { + .x86 => .determined_by_cpu_arch, + .x86_64 => if (execution_mode == .privileged) .determined_by_cpu_arch else + // zig fmt off + .determined_by_cpu_arch, + // .determined_by_cpu_arch, + // TODO: this causes some problems: https://github.com/ziglang/zig/issues/15524 + //.{ .explicit = &common.Target.x86.cpu.x86_64_v3 }, + else => .determined_by_cpu_arch, + }, + .os_tag = .freestanding, + .abi = .none, + .cpu_features_add = enabled_features, + .cpu_features_sub = disabled_features, + }; +} diff --git a/config/default.json b/config/default.json new file mode 100644 index 0000000..12994fc --- /dev/null +++ b/config/default.json @@ -0,0 +1,9 @@ +{ + "architecture": "x86_64", + "bootloader": "rise", + "boot_protocol": "bios", + "execution_environment": "qemu", + "optimize_mode": "Debug", + "execution_type": "emulated", + "executable_kind": "exe" +} diff --git a/config/gdb_script b/config/gdb_script new file mode 100644 index 0000000..ae307e5 --- /dev/null +++ b/config/gdb_script @@ -0,0 +1 @@ +set can-use-hw-watchpoints 1 diff --git a/config/image_config.json b/config/image_config.json new file mode 100644 index 0000000..bb3aef3 --- /dev/null +++ b/config/image_config.json @@ -0,0 +1,11 @@ +{ + "sector_count": 131072, + "sector_size": 512, + "image_name": "rise", + "partition_table": "gpt", + "partition": { + "name": "ESP", + "filesystem": "fat32", + "first_lba": 2048 + } +} diff --git a/config/qemu.json b/config/qemu.json new file mode 100644 index 0000000..7dac633 --- /dev/null +++ b/config/qemu.json @@ -0,0 +1,31 @@ +{ + "memory": { + "amount": "2048", + "unit": "megabyte" + }, + "virtualize": false, + "vga": "std", + "smp": 2, + "debugcon": "stdio", + "log": { + "file": null, + "guest_errors": true, + "assembly": false, + "interrupts": true + }, + "trace": [ + "nvme", + "pci", + "ide", + "ata", + "ahci", + "sata", + "apic_report_irq_delivered", + "apic_reset_irq_delivered", + "apic_get_irq_delivered", + "apic_local_deliver", + "apic_deliver_irq", + "apic_mem_readl", + "apic_mem_writel" + ] +} diff --git a/resources/FiraSans-Regular.otf b/resources/FiraSans-Regular.otf new file mode 100644 index 0000000000000000000000000000000000000000..98ef98c8db44a9cb8632228989fa65fdd183a06b GIT binary patch literal 357996 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 vr`+auQE%X=cp?obfAhPt&nvfLcW&2 + exit 1;; + esac + shift;; + + -o) chowncmd="$chownprog $2" + shift;; + + -p) cpprog="$cpprog -p";; + + -s) stripcmd=$stripprog;; + + -S) backupsuffix="$2" + shift;; + + -t) + is_target_a_directory=always + dst_arg=$2 + # Protect names problematic for 'test' and other utilities. + case $dst_arg in + -* | [=\(\)!]) dst_arg=./$dst_arg;; + esac + shift;; + + -T) is_target_a_directory=never;; + + --version) echo "$0 $scriptversion"; exit $?;; + + --) shift + break;; + + -*) echo "$0: invalid option: $1" >&2 + exit 1;; + + *) break;; + esac + shift +done + +# We allow the use of options -d and -T together, by making -d +# take the precedence; this is for compatibility with GNU install. + +if test -n "$dir_arg"; then + if test -n "$dst_arg"; then + echo "$0: target directory not allowed when installing a directory." >&2 + exit 1 + fi +fi + +if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then + # When -d is used, all remaining arguments are directories to create. + # When -t is used, the destination is already specified. + # Otherwise, the last argument is the destination. Remove it from $@. + for arg + do + if test -n "$dst_arg"; then + # $@ is not empty: it contains at least $arg. + set fnord "$@" "$dst_arg" + shift # fnord + fi + shift # arg + dst_arg=$arg + # Protect names problematic for 'test' and other utilities. + case $dst_arg in + -* | [=\(\)!]) dst_arg=./$dst_arg;; + esac + done +fi + +if test $# -eq 0; then + if test -z "$dir_arg"; then + echo "$0: no input file specified." >&2 + exit 1 + fi + # It's OK to call 'install-sh -d' without argument. + # This can happen when creating conditional directories. + exit 0 +fi + +if test -z "$dir_arg"; then + if test $# -gt 1 || test "$is_target_a_directory" = always; then + if test ! -d "$dst_arg"; then + echo "$0: $dst_arg: Is not a directory." >&2 + exit 1 + fi + fi +fi + +if test -z "$dir_arg"; then + do_exit='(exit $ret); exit $ret' + trap "ret=129; $do_exit" 1 + trap "ret=130; $do_exit" 2 + trap "ret=141; $do_exit" 13 + trap "ret=143; $do_exit" 15 + + # Set umask so as not to create temps with too-generous modes. + # However, 'strip' requires both read and write access to temps. + case $mode in + # Optimize common cases. + *644) cp_umask=133;; + *755) cp_umask=22;; + + *[0-7]) + if test -z "$stripcmd"; then + u_plus_rw= + else + u_plus_rw='% 200' + fi + cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;; + *) + if test -z "$stripcmd"; then + u_plus_rw= + else + u_plus_rw=,u+rw + fi + cp_umask=$mode$u_plus_rw;; + esac +fi + +for src +do + # Protect names problematic for 'test' and other utilities. + case $src in + -* | [=\(\)!]) src=./$src;; + esac + + if test -n "$dir_arg"; then + dst=$src + dstdir=$dst + test -d "$dstdir" + dstdir_status=$? + # Don't chown directories that already exist. + if test $dstdir_status = 0; then + chowncmd="" + fi + else + + # Waiting for this to be detected by the "$cpprog $src $dsttmp" command + # might cause directories to be created, which would be especially bad + # if $src (and thus $dsttmp) contains '*'. + if test ! -f "$src" && test ! -d "$src"; then + echo "$0: $src does not exist." >&2 + exit 1 + fi + + if test -z "$dst_arg"; then + echo "$0: no destination specified." >&2 + exit 1 + fi + dst=$dst_arg + + # If destination is a directory, append the input filename. + if test -d "$dst"; then + if test "$is_target_a_directory" = never; then + echo "$0: $dst_arg: Is a directory" >&2 + exit 1 + fi + dstdir=$dst + dstbase=`basename "$src"` + case $dst in + */) dst=$dst$dstbase;; + *) dst=$dst/$dstbase;; + esac + dstdir_status=0 + else + dstdir=`dirname "$dst"` + test -d "$dstdir" + dstdir_status=$? + fi + fi + + case $dstdir in + */) dstdirslash=$dstdir;; + *) dstdirslash=$dstdir/;; + esac + + obsolete_mkdir_used=false + + if test $dstdir_status != 0; then + case $posix_mkdir in + '') + # With -d, create the new directory with the user-specified mode. + # Otherwise, rely on $mkdir_umask. + if test -n "$dir_arg"; then + mkdir_mode=-m$mode + else + mkdir_mode= + fi + + posix_mkdir=false + # The $RANDOM variable is not portable (e.g., dash). Use it + # here however when possible just to lower collision chance. + tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$ + + trap ' + ret=$? + rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir" 2>/dev/null + exit $ret + ' 0 + + # Because "mkdir -p" follows existing symlinks and we likely work + # directly in world-writeable /tmp, make sure that the '$tmpdir' + # directory is successfully created first before we actually test + # 'mkdir -p'. + if (umask $mkdir_umask && + $mkdirprog $mkdir_mode "$tmpdir" && + exec $mkdirprog $mkdir_mode -p -- "$tmpdir/a/b") >/dev/null 2>&1 + then + if test -z "$dir_arg" || { + # Check for POSIX incompatibilities with -m. + # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or + # other-writable bit of parent directory when it shouldn't. + # FreeBSD 6.1 mkdir -m -p sets mode of existing directory. + test_tmpdir="$tmpdir/a" + ls_ld_tmpdir=`ls -ld "$test_tmpdir"` + case $ls_ld_tmpdir in + d????-?r-*) different_mode=700;; + d????-?--*) different_mode=755;; + *) false;; + esac && + $mkdirprog -m$different_mode -p -- "$test_tmpdir" && { + ls_ld_tmpdir_1=`ls -ld "$test_tmpdir"` + test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1" + } + } + then posix_mkdir=: + fi + rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir" + else + # Remove any dirs left behind by ancient mkdir implementations. + rmdir ./$mkdir_mode ./-p ./-- "$tmpdir" 2>/dev/null + fi + trap '' 0;; + esac + + if + $posix_mkdir && ( + umask $mkdir_umask && + $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir" + ) + then : + else + + # mkdir does not conform to POSIX, + # or it failed possibly due to a race condition. Create the + # directory the slow way, step by step, checking for races as we go. + + case $dstdir in + /*) prefix='/';; + [-=\(\)!]*) prefix='./';; + *) prefix='';; + esac + + oIFS=$IFS + IFS=/ + set -f + set fnord $dstdir + shift + set +f + IFS=$oIFS + + prefixes= + + for d + do + test X"$d" = X && continue + + prefix=$prefix$d + if test -d "$prefix"; then + prefixes= + else + if $posix_mkdir; then + (umask $mkdir_umask && + $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break + # Don't fail if two instances are running concurrently. + test -d "$prefix" || exit 1 + else + case $prefix in + *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;; + *) qprefix=$prefix;; + esac + prefixes="$prefixes '$qprefix'" + fi + fi + prefix=$prefix/ + done + + if test -n "$prefixes"; then + # Don't fail if two instances are running concurrently. + (umask $mkdir_umask && + eval "\$doit_exec \$mkdirprog $prefixes") || + test -d "$dstdir" || exit 1 + obsolete_mkdir_used=true + fi + fi + fi + + if test -n "$dir_arg"; then + { test -z "$chowncmd" || $doit $chowncmd "$dst"; } && + { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } && + { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false || + test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1 + else + + # Make a couple of temp file names in the proper directory. + dsttmp=${dstdirslash}_inst.$$_ + rmtmp=${dstdirslash}_rm.$$_ + + # Trap to clean up those temp files at exit. + trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0 + + # Copy the file name to the temp name. + (umask $cp_umask && + { test -z "$stripcmd" || { + # Create $dsttmp read-write so that cp doesn't create it read-only, + # which would cause strip to fail. + if test -z "$doit"; then + : >"$dsttmp" # No need to fork-exec 'touch'. + else + $doit touch "$dsttmp" + fi + } + } && + $doit_exec $cpprog "$src" "$dsttmp") && + + # and set any options; do chmod last to preserve setuid bits. + # + # If any of these fail, we abort the whole thing. If we want to + # ignore errors from any of these, just make sure not to ignore + # errors from the above "$doit $cpprog $src $dsttmp" command. + # + { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } && + { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } && + { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } && + { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } && + + # If -C, don't bother to copy if it wouldn't change the file. + if $copy_on_change && + old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` && + new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` && + set -f && + set X $old && old=:$2:$4:$5:$6 && + set X $new && new=:$2:$4:$5:$6 && + set +f && + test "$old" = "$new" && + $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1 + then + rm -f "$dsttmp" + else + # If $backupsuffix is set, and the file being installed + # already exists, attempt a backup. Don't worry if it fails, + # e.g., if mv doesn't support -f. + if test -n "$backupsuffix" && test -f "$dst"; then + $doit $mvcmd -f "$dst" "$dst$backupsuffix" 2>/dev/null + fi + + # Rename the file to the real destination. + $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null || + + # The rename failed, perhaps because mv can't rename something else + # to itself, or perhaps because mv is so ancient that it does not + # support -f. + { + # Now remove or move aside any old file at destination location. + # We try this two ways since rm can't unlink itself on some + # systems and the destination file might be busy for other + # reasons. In this case, the final cleanup might fail but the new + # file should still install successfully. + { + test ! -f "$dst" || + $doit $rmcmd "$dst" 2>/dev/null || + { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null && + { $doit $rmcmd "$rmtmp" 2>/dev/null; :; } + } || + { echo "$0: cannot unlink or rename $dst" >&2 + (exit 1); exit 1 + } + } && + + # Now rename the file to the real destination. + $doit $mvcmd "$dsttmp" "$dst" + } + fi || exit 1 + + trap '' 0 + fi +done + +# Local variables: +# eval: (add-hook 'before-save-hook 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-time-zone: "UTC0" +# time-stamp-end: "; # UTC" +# End: diff --git a/src/bootloader/limine/installables/limine-cd-efi.bin b/src/bootloader/limine/installables/limine-cd-efi.bin new file mode 100644 index 0000000000000000000000000000000000000000..3595310e26d9f2ea890f5bffeb12b7f39ece9d8c GIT binary patch literal 1474560 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 z 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 z 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 +#include +#include +#include +#include +#include +#include +#include + +static bool quiet = false; + +static int set_pos(FILE *stream, uint64_t pos) { + if (sizeof(long) >= 8) { + return fseek(stream, (long)pos, SEEK_SET); + } + + long jump_size = (LONG_MAX / 2) + 1; + long last_jump = pos % jump_size; + uint64_t jumps = pos / jump_size; + + rewind(stream); + + for (uint64_t i = 0; i < jumps; i++) { + if (fseek(stream, jump_size, SEEK_CUR) != 0) { + return -1; + } + } + if (fseek(stream, last_jump, SEEK_CUR) != 0) { + return -1; + } + + return 0; +} + +#define DIV_ROUNDUP(a, b) (((a) + ((b) - 1)) / (b)) + +struct gpt_table_header { + // the head + char signature[8]; + uint32_t revision; + uint32_t header_size; + uint32_t crc32; + uint32_t _reserved0; + + // the partitioning info + uint64_t my_lba; + uint64_t alternate_lba; + uint64_t first_usable_lba; + uint64_t last_usable_lba; + + // the guid + uint64_t disk_guid[2]; + + // entries related + uint64_t partition_entry_lba; + uint32_t number_of_partition_entries; + uint32_t size_of_partition_entry; + uint32_t partition_entry_array_crc32; +}; + +struct gpt_entry { + uint64_t partition_type_guid[2]; + + uint64_t unique_partition_guid[2]; + + uint64_t starting_lba; + uint64_t ending_lba; + + uint64_t attributes; + + uint16_t partition_name[36]; +}; + +// This table from https://web.mit.edu/freebsd/head/sys/libkern/crc32.c +static const uint32_t crc32_table[] = { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, + 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, + 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, + 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, + 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, + 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, + 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, + 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, + 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, + 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, + 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, + 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, + 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, + 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, + 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, + 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, + 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, + 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, + 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, + 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, + 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, + 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d +}; + +static uint32_t crc32(void *_stream, size_t len) { + uint8_t *stream = _stream; + uint32_t ret = 0xffffffff; + + for (size_t i = 0; i < len; i++) { + ret = (ret >> 8) ^ crc32_table[(ret ^ stream[i]) & 0xff]; + } + + ret ^= 0xffffffff; + return ret; +} + +static bool bigendian = false; + +static uint16_t endswap16(uint16_t value) { + uint16_t ret = 0; + ret |= (value >> 8) & 0x00ff; + ret |= (value << 8) & 0xff00; + return ret; +} + +static uint32_t endswap32(uint32_t value) { + uint32_t ret = 0; + ret |= (value >> 24) & 0x000000ff; + ret |= (value >> 8) & 0x0000ff00; + ret |= (value << 8) & 0x00ff0000; + ret |= (value << 24) & 0xff000000; + return ret; +} + +static uint64_t endswap64(uint64_t value) { + uint64_t ret = 0; + ret |= (value >> 56) & 0x00000000000000ff; + ret |= (value >> 40) & 0x000000000000ff00; + ret |= (value >> 24) & 0x0000000000ff0000; + ret |= (value >> 8) & 0x00000000ff000000; + ret |= (value << 8) & 0x000000ff00000000; + ret |= (value << 24) & 0x0000ff0000000000; + ret |= (value << 40) & 0x00ff000000000000; + ret |= (value << 56) & 0xff00000000000000; + return ret; +} + +#define ENDSWAP(VALUE) (bigendian ? ( \ + sizeof(VALUE) == 1 ? (VALUE) : \ + sizeof(VALUE) == 2 ? endswap16(VALUE) : \ + sizeof(VALUE) == 4 ? endswap32(VALUE) : \ + sizeof(VALUE) == 8 ? endswap64(VALUE) : (abort(), 1) \ +) : (VALUE)) + +static enum { + CACHE_CLEAN, + CACHE_DIRTY +} cache_state; +static uint64_t cached_block; +static uint8_t *cache = NULL; +static FILE *device = NULL; +static size_t block_size; + +static bool device_init(void) { + size_t guesses[] = { 512, 2048, 4096 }; + + for (size_t i = 0; i < sizeof(guesses) / sizeof(size_t); i++) { + void *tmp = realloc(cache, guesses[i]); + if (tmp == NULL) { + perror("ERROR"); + return false; + } + cache = tmp; + + rewind(device); + + size_t ret = fread(cache, guesses[i], 1, device); + if (ret != 1) { + continue; + } + + block_size = guesses[i]; + + if (!quiet) { + fprintf(stderr, "Physical block size of %zu bytes.\n", block_size); + } + + cache_state = CACHE_CLEAN; + cached_block = 0; + return true; + } + + fprintf(stderr, "ERROR: Couldn't determine block size of device.\n"); + return false; +} + +static bool device_flush_cache(void) { + if (cache_state == CACHE_CLEAN) + return true; + + if (set_pos(device, cached_block * block_size) != 0) { + perror("ERROR"); + return false; + } + + size_t ret = fwrite(cache, block_size, 1, device); + if (ret != 1) { + perror("ERROR"); + return false; + } + + cache_state = CACHE_CLEAN; + return true; +} + +static bool device_cache_block(uint64_t block) { + if (cached_block == block) + return true; + + if (cache_state == CACHE_DIRTY) { + if (!device_flush_cache()) + return false; + } + + if (set_pos(device, block * block_size) != 0) { + perror("ERROR"); + return false; + } + + size_t ret = fread(cache, block_size, 1, device); + if (ret != 1) { + perror("ERROR"); + return false; + } + + cached_block = block; + + return true; +} + +struct undeploy_data { + void *data; + uint64_t loc; + uint64_t count; +}; + +#define UNDEPLOY_DATA_MAX 256 + +static bool undeploying = false; +static struct undeploy_data undeploy_data[UNDEPLOY_DATA_MAX]; +static struct undeploy_data undeploy_data_rev[UNDEPLOY_DATA_MAX]; +static uint64_t undeploy_data_i = 0; +static const char *undeploy_file = NULL; + +static void reverse_undeploy_data(void) { + for (size_t i = 0, j = undeploy_data_i - 1; i < undeploy_data_i; i++, j--) { + undeploy_data_rev[j] = undeploy_data[i]; + } + + memcpy(undeploy_data, undeploy_data_rev, undeploy_data_i * sizeof(struct undeploy_data)); +} + +static void free_undeploy_data(void) { + for (size_t i = 0; i < undeploy_data_i; i++) { + free(undeploy_data[i].data); + } +} + +static bool store_undeploy_data(const char *filename) { + if (!quiet) { + fprintf(stderr, "Storing undeploy data to file: `%s`...\n", filename); + } + + FILE *udfile = fopen(filename, "wb"); + if (udfile == NULL) { + goto error; + } + + if (fwrite(&undeploy_data_i, sizeof(uint64_t), 1, udfile) != 1) { + goto error; + } + + for (size_t i = 0; i < undeploy_data_i; i++) { + if (fwrite(&undeploy_data[i].loc, sizeof(uint64_t), 1, udfile) != 1) { + goto error; + } + if (fwrite(&undeploy_data[i].count, sizeof(uint64_t), 1, udfile) != 1) { + goto error; + } + if (fwrite(undeploy_data[i].data, undeploy_data[i].count, 1, udfile) != 1) { + goto error; + } + } + + fclose(udfile); + return true; + +error: + perror("ERROR"); + if (udfile != NULL) { + fclose(udfile); + } + return false; +} + +static bool load_undeploy_data(const char *filename) { + if (!quiet) { + fprintf(stderr, "Loading undeploy data from file: `%s`...\n", filename); + } + + FILE *udfile = fopen(filename, "rb"); + if (udfile == NULL) { + goto error; + } + + if (fread(&undeploy_data_i, sizeof(uint64_t), 1, udfile) != 1) { + goto error; + } + + for (size_t i = 0; i < undeploy_data_i; i++) { + if (fread(&undeploy_data[i].loc, sizeof(uint64_t), 1, udfile) != 1) { + goto error; + } + if (fread(&undeploy_data[i].count, sizeof(uint64_t), 1, udfile) != 1) { + goto error; + } + undeploy_data[i].data = malloc(undeploy_data[i].count); + if (undeploy_data[i].data == NULL) { + goto error; + } + if (fread(undeploy_data[i].data, undeploy_data[i].count, 1, udfile) != 1) { + goto error; + } + } + + fclose(udfile); + return true; + +error: + perror("ERROR"); + if (udfile != NULL) { + fclose(udfile); + } + return false; +} + +static bool _device_read(void *_buffer, uint64_t loc, size_t count) { + uint8_t *buffer = _buffer; + uint64_t progress = 0; + while (progress < count) { + uint64_t block = (loc + progress) / block_size; + + if (!device_cache_block(block)) { + fprintf(stderr, "ERROR: Read error.\n"); + return false; + } + + uint64_t chunk = count - progress; + uint64_t offset = (loc + progress) % block_size; + if (chunk > block_size - offset) + chunk = block_size - offset; + + memcpy(buffer + progress, &cache[offset], chunk); + progress += chunk; + } + + return true; +} + +static bool _device_write(const void *_buffer, uint64_t loc, size_t count) { + if (undeploying) { + goto skip_save; + } + + if (undeploy_data_i >= UNDEPLOY_DATA_MAX) { + fprintf(stderr, "Internal error: Too many undeploy data entries!\n"); + return false; + } + + struct undeploy_data *ud = &undeploy_data[undeploy_data_i]; + + ud->data = malloc(count); + if (ud->data == NULL) { + fprintf(stderr, "ERROR: Memory allocation failure.\n"); + return false; + } + + if (!_device_read(ud->data, loc, count)) { + fprintf(stderr, "ERROR: Device read failure.\n"); + return false; + } + + ud->loc = loc; + ud->count = count; + +skip_save:; + const uint8_t *buffer = _buffer; + uint64_t progress = 0; + while (progress < count) { + uint64_t block = (loc + progress) / block_size; + + if (!device_cache_block(block)) { + fprintf(stderr, "ERROR: Write error.\n"); + return false; + } + + uint64_t chunk = count - progress; + uint64_t offset = (loc + progress) % block_size; + if (chunk > block_size - offset) + chunk = block_size - offset; + + memcpy(&cache[offset], buffer + progress, chunk); + cache_state = CACHE_DIRTY; + progress += chunk; + } + + if (!undeploying) { + undeploy_data_i++; + } + return true; +} + +static void undeploy(void) { + undeploying = true; + + cache_state = CACHE_CLEAN; + cached_block = (uint64_t)-1; + + for (size_t i = 0; i < undeploy_data_i; i++) { + struct undeploy_data *ud = &undeploy_data[i]; + bool retry = false; + while (!_device_write(ud->data, ud->loc, ud->count)) { + if (retry) { + fprintf(stderr, "ERROR: Undeploy data index %zu failed to write. Undeploy may be incomplete!\n", i); + break; + } + if (!quiet) { + fprintf(stderr, "Warning: Undeploy data index %zu failed to write, retrying...\n", i); + } + if (!device_flush_cache()) { + fprintf(stderr, "ERROR: Device cache flush failure. Undeploy may be incomplete!\n"); + } + cache_state = CACHE_CLEAN; + cached_block = (uint64_t)-1; + retry = true; + } + } + + if (!device_flush_cache()) { + fprintf(stderr, "ERROR: Device cache flush failure. Undeploy may be incomplete!\n"); + } + + if (!quiet) { + fprintf(stderr, "Undeploy data restored successfully. Limine undeployed!\n"); + } +} + +#define device_read(BUFFER, LOC, COUNT) \ + do { \ + if (!_device_read(BUFFER, LOC, COUNT)) \ + goto cleanup; \ + } while (0) + +#define device_write(BUFFER, LOC, COUNT) \ + do { \ + if (!_device_write(BUFFER, LOC, COUNT)) \ + goto cleanup; \ + } while (0) + +static void usage(const char *name) { + printf("Usage: %s [GPT partition index]\n", name); + printf("\n"); + printf(" --force-mbr Force MBR detection to work even if the\n"); + printf(" safety checks fail (DANGEROUS!)\n"); + printf("\n"); + printf(" --undeploy Reverse the entire deployment procedure\n"); + printf("\n"); + printf(" --undeploy-data-file=\n"); + printf(" Set the input (for --undeploy) or output file\n"); + printf(" name of the file which contains undeploy data\n"); + printf("\n"); + printf(" --quiet Do not print verbose diagnostic messages\n"); + printf("\n"); + printf(" --help | -h Display this help message\n"); + printf("\n"); +#ifdef IS_WINDOWS + system("pause"); +#endif +} + +int deploy(const char* device_path, const uint8_t* bootloader_img, size_t bootloader_file_size) { + int ok = EXIT_FAILURE; + int force_mbr = 0; + bool undeploy_mode = false; + uint8_t orig_mbr[70], timestamp[6]; + const char *part_ndx = NULL; + + uint32_t endcheck = 0x12345678; + uint8_t endbyte = *((uint8_t *)&endcheck); + bigendian = endbyte == 0x12; + + if ((device = fopen(device_path, "r+b")) == NULL) { // + fprintf(stderr, "ERROR: No device specified\n"); + return EXIT_FAILURE; + } + + if (!device_init()) { + goto undeploy_mode_cleanup; + } + + if (undeploy_mode) { + if (undeploy_file == NULL) { + fprintf(stderr, "ERROR: Undeploy mode set but no --undeploy-data-file=... passed.\n"); + goto undeploy_mode_cleanup; + } + + if (!load_undeploy_data(undeploy_file)) { + goto undeploy_mode_cleanup; + } + + undeploy(); + + ok = EXIT_SUCCESS; + goto undeploy_mode_cleanup; + } + + // Probe for GPT and logical block size + int gpt = 0; + struct gpt_table_header gpt_header; + uint64_t lb_guesses[] = { 512, 4096 }; + uint64_t lb_size = 0; + for (size_t i = 0; i < sizeof(lb_guesses) / sizeof(uint64_t); i++) { + device_read(&gpt_header, lb_guesses[i], sizeof(struct gpt_table_header)); + if (!strncmp(gpt_header.signature, "EFI PART", 8)) { + lb_size = lb_guesses[i]; + if (!force_mbr) { + gpt = 1; + if (!quiet) { + fprintf(stderr, "Deploying to GPT. Logical block size of %" PRIu64 " bytes.\n", + lb_guesses[i]); + } + } else { + fprintf(stderr, "ERROR: Device has a valid GPT, refusing to force MBR.\n"); + goto cleanup; + } + break; + } + } + + struct gpt_table_header secondary_gpt_header; + if (gpt) { + if (!quiet) { + fprintf(stderr, "Secondary header at LBA 0x%" PRIx64 ".\n", + ENDSWAP(gpt_header.alternate_lba)); + } + device_read(&secondary_gpt_header, lb_size * ENDSWAP(gpt_header.alternate_lba), + sizeof(struct gpt_table_header)); + if (!strncmp(secondary_gpt_header.signature, "EFI PART", 8)) { + if (!quiet) { + fprintf(stderr, "Secondary header valid.\n"); + } + } else { + fprintf(stderr, "ERROR: Secondary header not valid, aborting.\n"); + goto cleanup; + } + } + + int mbr = 0; + if (gpt == 0) { + // Do all sanity checks on MBR + mbr = 1; + + uint8_t hint8 = 0; + uint16_t hint16 = 0; + + bool any_active = false; + + device_read(&hint8, 446, sizeof(uint8_t)); + if (hint8 != 0x00 && hint8 != 0x80) { + if (!force_mbr) { + mbr = 0; + } else { + hint8 = hint8 & 0x80 ? 0x80 : 0x00; + device_write(&hint8, 446, sizeof(uint8_t)); + } + } + any_active = any_active ? any_active : (hint8 & 0x80) != 0; + device_read(&hint8, 462, sizeof(uint8_t)); + if (hint8 != 0x00 && hint8 != 0x80) { + if (!force_mbr) { + mbr = 0; + } else { + hint8 = hint8 & 0x80 ? 0x80 : 0x00; + device_write(&hint8, 462, sizeof(uint8_t)); + } + } + any_active = any_active ? any_active : (hint8 & 0x80) != 0; + device_read(&hint8, 478, sizeof(uint8_t)); + if (hint8 != 0x00 && hint8 != 0x80) { + if (!force_mbr) { + mbr = 0; + } else { + hint8 = hint8 & 0x80 ? 0x80 : 0x00; + device_write(&hint8, 478, sizeof(uint8_t)); + } + } + any_active = any_active ? any_active : (hint8 & 0x80) != 0; + device_read(&hint8, 494, sizeof(uint8_t)); + if (hint8 != 0x00 && hint8 != 0x80) { + if (!force_mbr) { + mbr = 0; + } else { + hint8 = hint8 & 0x80 ? 0x80 : 0x00; + device_write(&hint8, 494, sizeof(uint8_t)); + } + } + any_active = any_active ? any_active : (hint8 & 0x80) != 0; + + char hintc[64]; + device_read(hintc, 4, 8); + if (memcmp(hintc, "_ECH_FS_", 8) == 0) { + if (!force_mbr) { + mbr = 0; + } else { + memset(hintc, 0, 8); + device_write(hintc, 4, 8); + } + } + device_read(hintc, 3, 4); + if (memcmp(hintc, "NTFS", 4) == 0) { + if (!force_mbr) { + mbr = 0; + } else { + memset(hintc, 0, 4); + device_write(hintc, 3, 4); + } + } + device_read(hintc, 54, 3); + if (memcmp(hintc, "FAT", 3) == 0) { + if (!force_mbr) { + mbr = 0; + } else { + memset(hintc, 0, 5); + device_write(hintc, 54, 5); + } + } + device_read(hintc, 82, 3); + if (memcmp(hintc, "FAT", 3) == 0) { + if (!force_mbr) { + mbr = 0; + } else { + memset(hintc, 0, 5); + device_write(hintc, 82, 5); + } + } + device_read(hintc, 3, 5); + if (memcmp(hintc, "FAT32", 5) == 0) { + if (!force_mbr) { + mbr = 0; + } else { + memset(hintc, 0, 5); + device_write(hintc, 3, 5); + } + } + device_read(&hint16, 1080, sizeof(uint16_t)); + hint16 = ENDSWAP(hint16); + if (hint16 == 0xef53) { + if (!force_mbr) { + mbr = 0; + } else { + hint16 = 0; + hint16 = ENDSWAP(hint16); + device_write(&hint16, 1080, sizeof(uint16_t)); + } + } + + if (mbr && !any_active) { + if (!quiet) { + fprintf(stderr, "No active partition found, some systems may not boot.\n"); + fprintf(stderr, "Setting partition 1 as active to work around the issue...\n"); + } + hint8 = 0x80; + device_write(&hint8, 446, sizeof(uint8_t)); + } + } + + if (gpt == 0 && mbr == 0) { + fprintf(stderr, "ERROR: Could not determine if the device has a valid partition table.\n"); + fprintf(stderr, " Please ensure the device has a valid MBR or GPT.\n"); + fprintf(stderr, " Alternatively, pass `--force-mbr` to override these checks.\n"); + fprintf(stderr, " **ONLY DO THIS AT YOUR OWN RISK, DATA LOSS MAY OCCUR!**\n"); + goto cleanup; + } + + size_t stage2_size = bootloader_file_size - 512; + + size_t stage2_sects = DIV_ROUNDUP(stage2_size, 512); + + uint16_t stage2_size_a = (stage2_sects / 2) * 512 + (stage2_sects % 2 ? 512 : 0); + uint16_t stage2_size_b = (stage2_sects / 2) * 512; + + // Default split of stage2 for MBR (consecutive in post MBR gap) + uint64_t stage2_loc_a = 512; + uint64_t stage2_loc_b = stage2_loc_a + stage2_size_a; + + if (gpt) { + if (part_ndx != NULL) { + uint32_t partition_num; + sscanf(part_ndx, "%" SCNu32, &partition_num); + partition_num--; + if (partition_num > ENDSWAP(gpt_header.number_of_partition_entries)) { + fprintf(stderr, "ERROR: Partition number is too large.\n"); + goto cleanup; + } + + struct gpt_entry gpt_entry; + device_read(&gpt_entry, + (ENDSWAP(gpt_header.partition_entry_lba) * lb_size) + + (partition_num * ENDSWAP(gpt_header.size_of_partition_entry)), + sizeof(struct gpt_entry)); + + if (gpt_entry.unique_partition_guid[0] == 0 && + gpt_entry.unique_partition_guid[1] == 0) { + fprintf(stderr, "ERROR: No such partition.\n"); + goto cleanup; + } + + if (!quiet) { + fprintf(stderr, "GPT partition specified. Deploying there instead of embedding.\n"); + } + + stage2_loc_a = ENDSWAP(gpt_entry.starting_lba) * lb_size; + stage2_loc_b = stage2_loc_a + stage2_size_a; + if (stage2_loc_b & (lb_size - 1)) + stage2_loc_b = (stage2_loc_b + lb_size) & ~(lb_size - 1); + } else { + if (!quiet) { + fprintf(stderr, "GPT partition NOT specified. Attempting GPT embedding.\n"); + } + + int64_t max_partition_entry_used = -1; + for (int64_t i = 0; i < (int64_t)ENDSWAP(gpt_header.number_of_partition_entries); i++) { + struct gpt_entry gpt_entry; + device_read(&gpt_entry, + (ENDSWAP(gpt_header.partition_entry_lba) * lb_size) + + (i * ENDSWAP(gpt_header.size_of_partition_entry)), + sizeof(struct gpt_entry)); + + if (gpt_entry.unique_partition_guid[0] != 0 || + gpt_entry.unique_partition_guid[1] != 0) { + if (i > max_partition_entry_used) + max_partition_entry_used = i; + } + } + + stage2_loc_a = (ENDSWAP(gpt_header.partition_entry_lba) + 32) * lb_size; + stage2_loc_a -= stage2_size_a; + stage2_loc_a &= ~(lb_size - 1); + stage2_loc_b = (ENDSWAP(secondary_gpt_header.partition_entry_lba) + 32) * lb_size; + stage2_loc_b -= stage2_size_b; + stage2_loc_b &= ~(lb_size - 1); + + size_t partition_entries_per_lb = + lb_size / ENDSWAP(gpt_header.size_of_partition_entry); + size_t new_partition_array_lba_size = + stage2_loc_a / lb_size - ENDSWAP(gpt_header.partition_entry_lba); + size_t new_partition_entry_count = + new_partition_array_lba_size * partition_entries_per_lb; + + if ((int64_t)new_partition_entry_count <= max_partition_entry_used) { + fprintf(stderr, "ERROR: Cannot embed because there are too many used partition entries.\n"); + goto cleanup; + } + + if (!quiet) { + fprintf(stderr, "New maximum count of partition entries: %zu.\n", new_partition_entry_count); + } + + // Zero out unused partitions + void *empty = calloc(1, ENDSWAP(gpt_header.size_of_partition_entry)); + for (size_t i = max_partition_entry_used + 1; i < new_partition_entry_count; i++) { + device_write(empty, + ENDSWAP(gpt_header.partition_entry_lba) * lb_size + i * ENDSWAP(gpt_header.size_of_partition_entry), + ENDSWAP(gpt_header.size_of_partition_entry)); + } + for (size_t i = max_partition_entry_used + 1; i < new_partition_entry_count; i++) { + device_write(empty, + ENDSWAP(secondary_gpt_header.partition_entry_lba) * lb_size + i * ENDSWAP(secondary_gpt_header.size_of_partition_entry), + ENDSWAP(secondary_gpt_header.size_of_partition_entry)); + } + free(empty); + + uint8_t *partition_array = + malloc(new_partition_entry_count * ENDSWAP(gpt_header.size_of_partition_entry)); + if (partition_array == NULL) { + perror("ERROR"); + goto cleanup; + } + + device_read(partition_array, + ENDSWAP(gpt_header.partition_entry_lba) * lb_size, + new_partition_entry_count * ENDSWAP(gpt_header.size_of_partition_entry)); + + uint32_t crc32_partition_array = + crc32(partition_array, + new_partition_entry_count * ENDSWAP(gpt_header.size_of_partition_entry)); + + free(partition_array); + + gpt_header.partition_entry_array_crc32 = ENDSWAP(crc32_partition_array); + gpt_header.number_of_partition_entries = ENDSWAP(new_partition_entry_count); + gpt_header.crc32 = 0; + gpt_header.crc32 = crc32(&gpt_header, 92); + gpt_header.crc32 = ENDSWAP(gpt_header.crc32); + device_write(&gpt_header, + lb_size, + sizeof(struct gpt_table_header)); + + secondary_gpt_header.partition_entry_array_crc32 = ENDSWAP(crc32_partition_array); + secondary_gpt_header.number_of_partition_entries = + ENDSWAP(new_partition_entry_count); + secondary_gpt_header.crc32 = 0; + secondary_gpt_header.crc32 = crc32(&secondary_gpt_header, 92); + secondary_gpt_header.crc32 = ENDSWAP(secondary_gpt_header.crc32); + device_write(&secondary_gpt_header, + lb_size * ENDSWAP(gpt_header.alternate_lba), + sizeof(struct gpt_table_header)); + } + } else { + if (!quiet) { + fprintf(stderr, "Deploying to MBR.\n"); + } + } + + if (!quiet) { + fprintf(stderr, "Stage 2 to be located at 0x%" PRIx64 " and 0x%" PRIx64 ".\n", + stage2_loc_a, stage2_loc_b); + } + + // Save original timestamp + device_read(timestamp, 218, 6); + + // Save the original partition table of the device + device_read(orig_mbr, 440, 70); + + // Write the bootsector from the bootloader to the device + device_write(&bootloader_img[0], 0, 512); + + // Write the rest of stage 2 to the device + device_write(&bootloader_img[512], stage2_loc_a, stage2_size_a); + device_write(&bootloader_img[512 + stage2_size_a], + stage2_loc_b, stage2_size - stage2_size_a); + + // Hardcode in the bootsector the location of stage 2 halves + stage2_size_a = ENDSWAP(stage2_size_a); + device_write(&stage2_size_a, 0x1a4 + 0, sizeof(uint16_t)); + stage2_size_b = ENDSWAP(stage2_size_b); + device_write(&stage2_size_b, 0x1a4 + 2, sizeof(uint16_t)); + stage2_loc_a = ENDSWAP(stage2_loc_a); + device_write(&stage2_loc_a, 0x1a4 + 4, sizeof(uint64_t)); + stage2_loc_b = ENDSWAP(stage2_loc_b); + device_write(&stage2_loc_b, 0x1a4 + 12, sizeof(uint64_t)); + + // Write back timestamp + device_write(timestamp, 218, 6); + + // Write back the saved partition table to the device + device_write(orig_mbr, 440, 70); + + if (!device_flush_cache()) + goto cleanup; + + if (!quiet) { + fprintf(stderr, "Reminder: Remember to copy the limine.sys file in either\n" + " the root, /boot, /limine, or /boot/limine directories of\n" + " one of the partitions on the device, or boot will fail!\n"); + + fprintf(stderr, "Limine deployed successfully!\n"); + } + + ok = EXIT_SUCCESS; + +cleanup: + reverse_undeploy_data(); + if (ok != EXIT_SUCCESS) { + // If we failed, attempt to reverse deploy process + undeploy(); + } else if (undeploy_file != NULL) { + store_undeploy_data(undeploy_file); + } +undeploy_mode_cleanup: + free_undeploy_data(); + if (cache) + free(cache); + if (device != NULL) + fclose(device); + + return ok; +} diff --git a/src/bootloader/limine/installables/limine-pxe.bin b/src/bootloader/limine/installables/limine-pxe.bin new file mode 100644 index 0000000000000000000000000000000000000000..8d9dae2306a09b379b3cc39200074a8e479958dd GIT binary patch literal 21523 zcmZ6x1yoc)|3AD-Be5u5(nvQ*cXuO5mvkeY3&_$9(j^@d64D*gND0zNNq4^&f6wzj z&w1yZy?5ptpZV6z+`GHC_j|~`AP{?J%zR8|%tR~57XrpJ^o5XjLcZ^SAe5bZw7w{y zCM*yL^iN_nW+!IC1{}2h1W?qidSpQ$tECsnpmsL^r2_;)1~unsfIw%3JNZx~5I#sB zAU{3JrjP&MJz*Sl&(_&759l;MUxId@uV-bu-hj}^zoxAdDM^hE8;EqH#)?#=#yKlA zGVIZb)TBm%%{_Af8rE4EY{IDL|1ZgR<$-WkxQ`y~3werm!+d%;E4&AE0Wn`d?Z0qE zZfFrQPYlvekoL}fIQC9JVnjz9(%?po1IQ6P0dmhDD^MT^w1W?r1?@!#0s}MgF}IY1_3=t zgDyPuj?BX|+WEz04}u#eq(L~^=A7@r#WLFME!<@fiaP@AY9AYRK|jhjT9l9~w&*Z}K~Rzm@}bkLg{*re|Sphpb@LOyv2T&bh=o?!X_?+)xC1)!4v zi6Dn|Uq$Po@ZGo(M2mr8lFzvGu!ZR@)PEYVuK&Tlfp%m4!@j$P{qh}x_MpHnx(?uA zb^x-*<1^>~lJo~260-jskViO3h$3JX^brgR9Y+9xnpS}%S`J7eyaR}xaPH@T-O>c0 zG!Q_emUjX*?Ew7fZ~{Qb5*8&H4uFWdAF+2@!b1LUAQT6X+>e{C|H+a6N3IFD19DAq z|EQqS*!1=b50_2p(F19tv#qpKZ_h-DlC_&-f(&51$a$ zHN64bMO}c6KI{Jf=J-#6TtC3NefyxDz#NePAasiikOd04YY!196Y6N$J75gdD7=RR zP=h2um6Ag{&!S~f_^#aWqK&|Sp@1tQ)7z-G&+@R9|8e9B+KKhg+?#vYpzjZ87Yb~m zYX{*uUeK=lf4C2%kWbn`o&a0~=pQoZBQTZ*#sdh{&$~!a9~dZxXHTVps3SnvA?*hM zxj%#p8xcshwEG3L8=n2?pH(+-0epZ9BzzZc_|XRcG0YwWtdE`N8DYQw$FhrPU6g+z zzPW=f_>S(w!G6HHx_058;{@$_cxF)}LmH~XfxS39gb4&p#y1xo0EP|17TlA88sm*l zqJE~@VDAJ!r~0|TAfcZCW1+48iuef<5(~M1BIiLT>_4 zK>JJp8y)EU6GH>;O~}t=-2Wv%Yd1Y#fa0g)>-KvKnD#H7d}lx+ZNY$XKq85On%qFu z2g0@gKjAuiuEBq5zLP+Rso5`;f!g|WHA`^_51b6s2Y@m)efP5nz$Sq0(%{kfSt&Z|-;xG)afBU=|8gNAFYce7_Q}{gp}y2iAV^YYw4~_6J_Lq9y^s8#DvCs) z4(fg0*Z=MG&&L#OEK(PpZ`zMF5;Ia7fv?LA`+3y}|93-mKmisD>VP!Xv(Z3afbG)| z5ci#p^haUulzm>eR}&Q1(?>9W6#8j^_y2%ZVY9ID9TGsV`QIrRfCYktKA-ph;=)BO z^xO}?iqs*9mIuRB2*TlZhX9Ju^G*l}Sp~WRVa9-Okl4Ow^7B>*`Scy=Lff9nKs8hX z16U8@6A(G}%Vq!$3sTVKf(2=a!vcyGcC$kQED%^f>;|^Clk}{3O&Z_}+lBQB1U(}J zI02D*KyU$0*<_agQ`iTfAdOMXiog)u6)8gi>t@u!;hPMQf;1)(r=tMW+0aXWf~y7g z&S%4!;37!q!%5&o`TE_D0CDg!Ookwmi~667H?Y&4=Y1IZ?0Er$O975f}Vf~ z2-*k7H{iDkegtv~X_Ry#|1az_B=j#iut#zI_viwX17`d?y8d|%nDP|*IoEqe2?E;4GgIoJIKt+_%}E*cT<*o=?7i$0MZCjh>wT%*7JCXbJOWA_rdq)4T6N zpWs{&LKodtpif|TWJ{ zIfPz3>fMyV& z42B0IfRVr`V017Rm;g);W&?|Yjj>+70-+$IBE3LFLqLbefWrhs{)2@CprHVGFF~aBmj~CX@JZ?t{^C=0MrB80Kox;8wWJ9NR)rRz`;lRVd!h< z)Lyq?v6-Gr_u(bk)Xyi+GN_do+2DtO8v2pqj2kJx$fM%~(Kh?n_XVfclQ=jwF2??L z@bFu7XleORK*Rju> zoF6R5M)iY1v0kCOSw?zf62-;34vXUyJVdw;Gb|H=J4uNHjBxm}w9fb+(KBC>x->9YeNS z)LiLPmRFU=PBz_wZ0m@_$@3@F3~YJ9xi$j#rw481Yd}Yq1%~8fsmonOXGMo*1a=$; z#~N#3F*{t2M`vOahv5vtNwNGF_yK8Oy-=fqC#jCZ8}#;C=4QSXw&PsOM`r64#gOkN zn%t6xrKUub9sMHh4E;7u`zpy}y0eP5vmy(USH&K>0hWH<3#FfmR{{*QTyv^zAlR3? z24rQKYV#Ues6sh=IgVTNEy9)E)k9BZ>uwjx1MeL49^P_mu}8Ug#CTn-mtRyHdd*zf z*cv{jzmXco&lA*U2@)gh=c7Nyn&?7!eVhI$m8-wh!`A&$scqR{eCd)E%U@uVS9bN$ zp6wg_Q;Ls6PEOvvV@veYk)H_NppC|cR>u=TS>I33i@Oszzll~6iQN2bxen>^0;4M? zyx@F)%f%-an^X?XAHIyuDsM&_Bd$iVnnYZWqSH)&JB-%-c|VtHSE-_>W0IxZbV5HZ zGS6f9wJLSCZ~SX|+ny)(r<{NY$r#8T@|0@Z5pw^&7|9CbdQ)k*gjQv^1OqJg(X#xwBXZMT?4?}E zp0sfR!&f!Z`9TEQJH$0v^Cu?rT_11`6qpGo{XzS$s~B zUB%$$-YU$M)Y8OXiCthpt%$ekcKjWObR=!%wJ%rWhF5a?`+#)KYgE>{H?qoe^WRNr zQ#b1^UM}0l9~r>BOKC8=&-hcw-V}{Y-1QAu?x{qdJI&xvI^AFDvl zR>e)E{-$<&H|cHCiUZB0et-EU-Dtb0h~As}T>PfW;MEa8e!UJ`w{F#~1a%!dc_v2Oa=ibe({?oov{@Gp z@pK-+a{VQDsaVs^N@Ov(ir_9yR=bdNJ-TW}< zYu9EHc(k@T zKt;QNlBOJ)VG!)Z^R@_wv#*?Uv>9wj<#CUNf+91KpW9H|IKUDE)ro zUdLTZ0)A))(@%|k)Mce0!t}d{WnUjlSu(uweo1nQ{+B#Lcg~G|)!*pag9c_2c9NgY zvFVMkV+9n}TJ~}U1O4L>@f(qbH~URgp@CLEiS%K|oozqCzDIpn;sJUt%t)%V1jc#d zi96{*_MPMUug>astR<~!Lo=y!eA9cw}4|d$<-!?KxQ~yNSvwNuZ~|z ze_K_ik*-d1NhBEG>6-TbrS)x?6GWMV51yv3PMugzde7&s?wH z;2Nm2v>Y7Aafeu?+3i3Ld`-=`oahkE`c%PzE;g=u`7gtgf=U;>N(PzM(FCG zTbkVb;pHL1gPW_CAuXBLSBc5TS6G;T^a~*2ULVeX_m2Bb2qc_)vBXyJM>xDl!Ykq_ z$q;jo3s@#L@0tlhg4dS>;kW7)elI@t)t6T#a^O$D#ntQcyM5)|uEorK?%&XbI>~1^rBIB& zd4*h}G6!nVolmPx%Nx|05@uh>UA4S<<1EJP+-);~u>aZsBW7Y|HPFfhqKjdpo>03r zPCLm>7WGlFU{Yl+Dd|-S86lGi=X;4^`TnOB2rreWS3if7Z=awN5mS|QCz*2d7lkC;XR>X@0IZ!>MCj9 zC#zyv_y)VclobhuUsbauwAsTyGT&=Ew=7k>SJ&cDst;SVP{;QYMr=D7EGV>+j_~*E zr}t=)uhg7N|G7i~v97APGq4EgEkfyf_^@Km^coW{_O?l0C34kaGRMEZmNi15lo&lm zl}nl#>(6c$+-wEJ=@ad%>OGIhnhy^-e(b8e6*w`s(qefQCxc3LA-8yI(w$ypClYp; zlP2R94#-Cz#!Xe1Bv?Fei@z?{Y?J5f%ME%M};7QqrkPY}#34WE{2-ulBuIqI{x|hpq1~>?ez4jcNs;Ptjx6 z9kd3knM?nyOs;yl2Apy@*}Nz#d7Thd&k^!gqBVxa0i3ar)iD>V#i_8bRq+(_er(Hv zMBls}yDa7C!O#`H0+%(r?KX_HIt|~eLQP?^G-e^fRiAT*9`{2f4=6*X$$n@*8(BuS zVFu!*dNHOz~(dpcn7Dmm5 z2wxP@1bGf&yuv+#OMbIWVbN5YKRtJ22f{D$Y2M4ikz?2<3J^%37eCUg`Za9&z(3}m zg}P=fmMr9$veAR9KRvk-9Y6K6szBczkv54~tIC0W;7A`tG4cpv)0v44$Z}@1(cS%E zH_BtKa-H%mR=#Ra=Fbq~qe8;=8+=ji{F?G09%EUFZQ%7HOm)U9DePq3x2qCv6VEGH zZ*qV?4-@fjkG?{Lrt*cL-i^5Vs^R*UfC0W#!OBgiFtrKl7cy7o2_-cKSR=-zpKi9r33xDD-Xl5j${9+H!!c%v&y3}E&9cq26 zZg|Q3I1trVj}axG5ry{h7R@eU#N(Fo(im|f*J;#^1&QHGeV!SY<|69~eBO{kCy3dZ ztdL?dsmpZguhV!Y=ZDfQqTRfyruQvL;G82XY*}vXMBEWaq{_4YUdBS*L;B`$nQ!Rr zGSicu+WU8|12a>9R!c`uBjxMddO%Na)?_Ka4G+^C?O|0PwuP=tT9gp=H_U&H)gAk# z$XuTo7LZg(>nA!ape|UDHk6X%88W*}Nf+@XSD^M<2+4&Bg>Eyz-GnWVK4zc+!eDPWK#TVLzY`VRKl$r@8M8vftsC;=-V|gRAJP^_!kJWL|g&Af@ zL_Kgj#FqwtXObsAY8Rt$qJ*a_)flx|nAG)3`E!Zn%2L=*pH_%CxjNbk`LYj9qK=+$ z1nv=L`d{9mu>{13zS-KL8HaWWl9!p5YqCpULyTm`x`V@*FxSRCi@NBbLos&FRYL)M5i9pGh!b`YP#Dto}Oq#bLjQW+HNO-yF zQQ+K+yIhg6vq}atkx%Epc@=ldci_zD{uU3_{D$3GnQE@#<7;B5>X9XD2R7Mas_9(` zJre4z;IIE~M|_f9I+OTXY5lcX?bq-tc#y5XoKNeesX!0)Now!Q_JyB8hz4dCQXl3j z`EJF%aRxj;U53k@`iYI6G|0U;Kop-X`{;gVviW||ER{eyTjh*pW-X1i%VC;L(};Bp zqBps$qa4PrzbEHhkvDdNvi5*wltYtBK1KPK0e^sFDv@c0N_v23@2^pvUm?qyo_5hf z1L3UY%_}86a?>-7k#=^=MR}}(CD}FEuKfnqmtPgY=u_c1Hq-?v1U|%+C5@i=u+lIq z=htcVoJ8Wdm90KHSXH8-?Z<89fmwcN#!f8X2u~+}{!zR^jnKzk^N;TvX-a~-gz-XJ8NtlAn%il% zIW=zQU%zI|7QVFGVlXgcYnr1uEE1V4grdaE?WoN4|3b0h7&OJRy*L=#9xAB zr-({h6tfhTjl0Z4Ccq|uVhQ9 z;M1HcnsIri{Fn0B4pl2O{ofI!(IxLU;dGqen^3T+dB4tf3|z^mH1-V_C`^YpXp(UD zwmvhVmq3ngN-D~$oJRK><7CV9*m06r*Tj}AQqD?)uI+L7i5^u&`)sZ?eY&aX-2ELU zyepORCGX<_SW{FE!BZO>oQuGtQ7ouUSxde;Q5w=8Vxy>=S zm^gX-z{lLcjBVHoK{T$#`qIrcbTxvE=rS5CaoC%JL`G2HJ$WIhb`Jkn1nnE!BkK&JF)qnfquEPj(Kz}mI(jl| zP$z^xy1_FL85~;nP0Sl@lj~ zw*^xOl~9vcdYHBp=Fvjw)-0B_PCB~Kccx4(B2vW+F%8y{Jp}K!5XqmiZt^5e6O@`l z6uv8kI#8zAgfWyPV7wd@L6d7TqUgH9Jw>N46?{Eddw56Dx-XG+hIFELbrBX&t{AYD zAfl1?pn24A*z)p`+5aLmprtM$<)e+&G!~mamB|u z6#Vt%;^#cZdS#pf87c5|7V*k+S*|(Z&}vlh%QG%3E5}z|NaIZ_2t(fWZI8Wa+x!kg zm4L69lnvcS#7Z@{qkO%_L2W(rnlK?uUT&d6&Di0EK)%Nt(*99{sIhBIiymrNxo~m5 z3G*qVeJs>);Z#5LyY%E-zmC%|z>_%xMbL-!2V2>Nd1y*Sv<0TlkttzpFe; zO!;)!b6HqYXCO#t*6NeWczZ2!%_&LaR9moo1qqLYdbcNg3NJfnvlr@ESg8|)=*N?8 zNL#xnFT$kx1ETwBnR~>s;C*#rCkVS=5L_2`G9ZGbZc66R>a zus4(^-zh}};6^7;tcgT?c!wkRdFw8DHva_O*}h0lBC=K|A|tS9Lz1Cbaifazi`)^< z_OP1#n@Gg8rwfZvuZ@;LJ_m9$&Xd=LO{bw!;ILTF%p;^&K`LHqn$r!wu2HY=3gzf< ziProq48-61@!}KU*=b2FZ`Ln$#tBWwdzZ6RYg&P-`yIZ$39uKj-aH>+#HHd z!saK8AeD=yth{g&PHMCEa1!|?HU46KG~vX)&R4JcIv=EnnGw8==w`X(W*M_O!YhN! zBfV|;!;y};CY8`LYp#mP%t+l(vTpU6A}w#-S<+pSsW@J0(?RUt3vp)V;MhZoh~!5Kb4hO{e`=7$spgs zVQqG^jktlkBsaxg79TNh+Nr8*;C7Nrv+3Fn|Il5ZA9ButaYc?4;W4MO2-uHymI zlgS%>xC~BuhnkVPEQPZ%8NFp;_0-x6u3zrr)oB%ISK&YFKreH!>X%KCzI*Z0@z7K0 z@1WHc@39u4w1KyK0~Y<9mB^Vf*|az#c5vE~ z@hl0?v4IU;aY*u}V4Mzj>9GEqh7mM)w&XzNFzwoMbF>h)E}H?%wpJG$5xd)#rUC2aKej4ofi$(6dKTf<~h;fHw*P zxKLI~F;X&e0wwi9!>4b?uKpa=`*xj=ZX`g6K>rRDbc`!IVb6|lYc94Mu?@=HM!C`{ikX_QM8&lT-%Dt)fA?}b_>3$+yU+)BSYNARY9 z%7G1wsC|VvWm7Fxn6p>y1HiFOoiKyXg!=El%gU#HQ|r|0N4d43>1Ldtx|kA;i)O8+ zxATmTW*n4mXnyzR{h~O^?uIzQij|H3v=zCzSL?k4wPH!#%Szt z-@+4bY;G4Lm(E34I~Pp3>0bzkIe<;ScD_thgofe`%}e_cg%Ctbv$iEP5VuYdI(Xzf znikYxP|Ql32?$9eid-O8q#n<_Tpi!UQ%UqNK%e^2><`_UuCGX4w6_^YKlZQl-2q$R z+cef>RW`D4eB`qXr*+)2NSR~S!){DH(8)n;wE?8D-=95?@E>s`u+GBA<5Bo zYNB{Fg7`$tX27W>Hk~uFc=${l=lBeM^@nFiE1My##^?Cl%5~>_u*kNc!Sn(d4(~my zWmm;L0*;-A3E{Dy+yQd+E5&e>th3OqnQeD}0*Shy*0J6+RXy@Q*0mOk+)_qFAz^Hh zg8Vp5nk?y~<+ZMUdtoo@9LqM=!q8ZY9`IIC@HX-0YKo=(WlA6o z(iJ+74Y^4|F`e&Rpa1-=P>jq}j3wA0p3=qpb!V9Y zpRvCA^chbPdVbhRODZ~Iso+5VmMDSfT0CKhw(^@Z!jD%^aZ7b&`v%Ot zQn}a@^uDXzx;#5~tEXTNuJK|mh-|z6&HU!-jT7>!XGBrxU#MrF&oB1%<{c7yl{wsqTZDrJkGUU>@V+k$<^vz!c+{**;g~>vlS-LDDWc;sX0w;d(AL#|ZB-SyA z?$UziK}Kjtnw(jYfP5Ww+5Wlug*}T|@)3E%H3k}A`kh-zK zVP+E(U;Qs8h-6yLq1?_hM^vdT*%%5<{f4-=Jx1qyvB5UW;|F?tHf0kcKlsFVjCy0w z`?TBHY<^L0^X}?4{286ZV>6Zje!1|073Zbn5r+E8b;HTJJv4URxX`+^bW3T-1b>oZ z4#+KwMKp0)RHm9#(n|fjENbj+jL`G-!iNp@EIK3v*;I2U_-a)@&HP%M946j_QN<52 zqf-f?(QwhVf{Q!GE}O%cfv9mXq#9om7lswA-7u#&NJvgLzkaDX80AXiDeA11_U^UT zjcK4Wo*PuhH3s$Ae$`|1mcC1(&xth4Vmw(5%kw=)-6Z$MUtyP+F&6&Dj}~9Y&|8%Y z#ZtG_y*EM=%BV^VzimW2~D`qQjdwQ9YwC49>`Vk`)ImnBDul8~5qRFq@N{%$IZHi@NLMu+5neaHht{oKSm= z2kpj267U(QWUHz{h9t_4!JoGpswT=B-?@ZhpqG}mXaTQzeo=muJ6ddY_iL3z+#xAh zmKN;i_kocVAs=2YIarI}7?UQd%NXlj*anhx??uF9gG!wbC9{~?cl~07#Rp_euYuU#wszRooBde*sc@D+2qc`c>1;CxtLC01{*7#xVy(n&zD0^jAR{xchy=&Kbq z-O0YVddXt#;RS1&aiw$L;?QcVoEPZ(T)G9*MoHv1Z7*zpe#pc9RMpH|z)S!SeDA!f zk9pQPLtv1P*~)BtOgkZU{0RbQQX%EPsV3%1k5du%*jsD&X35`H8$3hiTRCOIF@+*ppAG5s90)<+WX zRh{GY+aK5xOiop5p1Mb&h;}u~>#-8+9iM--8FrB)PTPqz|G{=xd$YY+qZ+G&78Voq zNiC{2GMtztg+aOp=8W~!3yN<3~DTsz^z;?XoBU1q5GJJw>6xe1o%^rxwRyxT3 z@hg7Rlf^b$hRE-w@d%%PnwqRn^Y1Mpdac;upGs5si39ivhJzhA(xa!W$?!G_*^=3F zJVlb#*9fdv{k*FRibk^P>%sWRllLY;fuGAUrG#%I+mFQS8nHN+>Ncqx83`#%-f%5p zR&Vly{w`4NBs13uTBIkWy9p>X{Z4^(M>3EOot;rpl)^6SZDnRndgni+P;~=uc{&dy zv@d-#(zdrh`eI^MOT1P+yp0o{LfNbE$f#SB@K8^;YP1^HF-ZZfP;_vp!88+^e(!R2 z+I;zW3^H6iY{ul{!b)WhsK{OaxD8j+%-Q@qcKlc$$-`lY0~s$)`lejtX93Dva^?Hr zKS%x$6rO6SVT&*K?76|X2b&?=<>7iWaIFiCWQ%Dr3h9WwKWQ|r(td5BNylemR^8H+ zl>Xh|#ZUk9Np4IoLeVWEFmR(ZZ>K#4meYM;laQY(SK86kPEF!B{^Q5)Do1ImIyi5V zQR*ppGb`S(8QDj{QMt3a=dIJ#o9Y6di<^@4pPziDZ=NP|@Xj{%v_2~B&7%xXP~Gh3 zmzMJoR@^EoU4%&QXl4i!W~Z+Xxf4wpja?uvxQm}`Ny&Q0t^JPWw`PMYq zAy0NKfOZqyE?|Gcw9Q13`)BzWC8$J+vG}w3qw`_hB2&N>4Mv!)daCDCO%4Nl<*(7k z>-F!^9Q(eaU{Y<3fj6AP(hn<Nq<%c#&K_Auj@BiZw!!_@+pnZ|SSGo50^;fKz2 z+S%YNWXaK(w9O^ zLcE)M0}1s{tGZq>_$s+d?383m_f#24cY^st;|I#KPAw_XYah9S+|Ff87KW^;iY}d6 zKm24DmO-(vzW!K4X{jzn?9_L_yW;xUa~>_fhAIzr35|DT zUL!XiYo7M!QNah@(XeqC{)#H^0P{npLg;|&YD=`?q?`25pD5X2EUZRbdaU(HG7O`# z4Z|y^mb%UYt}EZJ>=$o8Mj^ZL*`(PutR!SvkHOW5i%JS3M*rwy>{&IPKQpTaL&M6i z`zd5(L(QM0zTKy=3wjoITM(X`YpUIz!r9i{kfCz&P z-*I*Dfox2oX0;fLYQ->z*+r8|a!dAp58sMexWsz3E-@8*x0+MDYvDywti44w)v#Sj zgJ@&q05o-Vw5P?f z@8V7n&CP2@gUo?)!F$R;ycj46VJFIlrskt#G)QU|GuCm7?+MVU7%rrYaJFOF;Vt3%+I z4v%-Ea!gX#j)LDEcHinp?RKal9?hQS-0)0cSUy_I<7>C`Wot*Uexn#p)m~pI-wf7& z71USUZDm*U;r?s5$bb^nW9piMrg!1M+Qm1wtXF3q>q;Mqmpv26&KYVkFy0yvve50f z3?SD8#DYXJsM}!uH;{E7Zyg2Y zZ5YMuZ}C=REN3KgVHNMLM(zZSzg{?#Y?*ClDuH(lmUgn=`!WgB7_YeLyT;;+!;T{8 zf{eDh7cv>i602P|C10@*Pw{YJ$aVW1@of1)ZXVNC7yMuG|GHhpBSTs4pFo+I1*<_x z%Ig!BJC!gV)ZRHY88CdC5Lo2H(B2*5)|zK%E`yZc{NM^E?8_eLY*@gj`7;I>H1P1w!Ai=TKmpEg>hHg0KJisnPZ3v+kQeni38&;=aF`U%^8289e1}JSuX0a zp|t?z@T}1Y(sqMt*Vz$ekv;>2h%??B(YeYOk96M)y?}-P2rd=H)*bnMu}37XVbqJ| z{sI#-;>c?5CgDOKW`kjBQTQpyvRV86tKSds+hT-7>!P#_%yI#NB8kk7AH9L5j6RVUD5dB^MgX7F29`{c`h zt=K|{jlU%uDQV0?wVk|pEAhBCsn=V#lJyoFv&Egm-NH!dGL_zZzIU1rp^+l_n=T=R zAx=>+al#yj&jjjcF>Z4&MJHIEC&NCB<4T% zarCG)1eiAQ`0kM{&b}FnImBxu-|j_@`N)xRi9vp|sI=u(NM#=6F_Oo%yiX>^!e^(9 zc>VE{Ls5H+&n{P5RUH#|j+yBULs?wywa52Wrf7+p!RdjJ6r8m_-mvn=!2Su2E3qE) zZVx$Yuwd4h?I#LRT+a?Cv!ArlX}2wek{4uIG**$Zek$pb?TldBvYwOhw08LsTYQmz ziYC&>>?>#0@h!GlXkHi@+-tduSJx0+hnHi?cd>Lw#ar%f%3U5YY+C08_c6$Q=QEyc z4FVzCDm}+;^aRu!gtDJ~$2Y>);tXNCDzLlc*KNatafID37DFw~9ZQ zcMf4i7d2*%ne^TX6wCkBJa1$2Ese(v;(!7q=B*6xwUC1cFLog`Tv#V%?9&+f5nLnx z2h9l^*U?iu$KzvWUh~IXM4I?as@|W}+N&ANaR}!wGmt4+3@&OtC+J(=s@|tua5P_XUN3Crh#r7+&0r(>TaT zAqRn0AZR6%KQ1G2?T7Dv_E#aZ9XrNK$$&Pff5IkS4Zp$oAcsbcn#&$0?69lL@-1|; zsridv;W|3jLKc_OXe@5Dv+2^}1pIlOhRmn{lFI&7IPRmqm$#-Y0r;^=_fe5pa5X~z zl{@KpL2BOJ&PyV0x1-~nkSp;KVpL}zy|g-l#KY2a1Ff$S z-^#k6Jl0&Dx17VWk+i|)Aqr7KT+5BTuh%OcvAJ|To1jXmE z;Z0N$h>ea4<&x5-IfS>-=}>9ab6INIa;cUY7=M0wL;t~j#ZNl6@rqIABUz8Q<-Qg2 z1oz^~$?78hh!I0-t6z{I{58J8`cJKkbHr#yPlStQWK|mB^Fu`R*W_HK*vg!r2;Z@w zzp=gs{Cwmxj2>dLv`)+XY~L`?aftE3?#EiJV21L!BBI#04E^;)y8bdGJEnT_DdC8u zC4!ig36>)>vw?tl{{UF+s$*Z})RJAKZ(0>O(MQyVSG2~QmaLe~5ajU`rKo(9;pWtX z7t3LSl12wpGwJKi27H`N1~SbC4&GK=Orgm8ue(L*{c^1Sgj9=b$sWr2==c03mRIWS`K9` z>ppGL8qKDPE3Tf5s>Dz9krFceep7t?SIIfj#tt%cLZmnNO^%P_N*aECo$}qsC}X?> ztm0mOOlPVq@2o>T3y~iyZG$^B@HIJ$1ERznY%q}?do+-r7@J$QFb3+2<$D|a8=iOe z_(5nv9}fcaZ<)wo3;v;VlPw;cS34jHc3Sc2??nF`gd28>k` zao^)d$DGg8>+MmMnXHjUJrogBI7>tm$ZaTvrdq$Z#rbj0J&7vA_y))>Xd+{CgN&%) zwJ=It*2gues}-yeG2mx1_(eOqqfb3G@IS-bK8$PDY!4Nrm$qGhEG>n1VhW`|cVFVc zci0d5_88PI#$fFvx)y55{6nCyZ*={)Y5N{lj4`hQ>p_AbVQ+&R^B>#4XA5;3XPaeU zZ3R{#L*u`kxpnoex)|~ipT;V|`EOK8h^Cz}EQw^uq3uoaIcMyv+^K7q0!_p_?A3gO z2GUcM1v!fiYF?>G90K>&Xd1{O>6U1ZW4_B~Z7{D&6}i%xP*gMntlk-~`I=K)eLNe;0kR;R>Zli4X_%$a%e<;Qg`(XW6V{rT{c31p8sdsH-4_%59>=#p1$hb!qv8i zd#+{8XJbJq8o;BPN47?!m%)Zk&a^C}N|{N@5Xle;lHsCLm$Qm^o2m%;pL&JK(J9wj zaWX81pM8S;0T~qOdgKi3H;E`&5qJ{VjrK}Ac=$ubgHo zcPY*O#6r}Rp9H$icWJiNNpxv6_GR0@R71ACvd=vci3nQJ$~rS zLt0-^m*DRR!1bQt*77TnXI(a!k+Gi>M&RVkC8xl!fO9A&F(t`XReIYgqFD0$6=Y;Js*6rKxx>xe+ zLiMqwZF@-B(k?Up-tELC#h=DE#~Q6$gC^BjAl|M;18 z7FMkD9v)jeTp^C*YH5j_Igo#a1aN)gG+@j8bb)82_p2g`h(ucO>j*l{Nse2+U)&l; zt~pZF#1*}qr;J^P5H1tZCZq<9rF&5?u3g6pqS^dmCbx5gliPURc%$1o9BIZgk6mLr zts;#tvV77ov@?EbO=IcMa(rtIf#;}zah98(p_iM@rhTof*8hS}`;jaQDkz2QHLQpF zCFJ!B2_fN$dVUBmU;MV zyPILzr7Fiyssg}&vv9fW)TD?{l;H;KlNkF^F-~ilJ8HNC#F{hwnXqJcVlVbuPVPm9 zOvUiHKkF_uOPH@0M~xyxHf|0N>|RD#?wnT~slB>39H~Zv&+wzkgUyKf-tzwuKt9SI)Hu{;YY}m|LpYlSzbYI5_JR zc(YVeCIjDTKP}j5N}=s0hkPgvrNlKb&pUbz3Pb_EcOQT<*Sf#!g}%onePk zr2^~s9Lfu=P_PK!Ooi5I6Tk`28#?!m)?o43Vmz2CbMfK(ze z=*S-fmL>4goVmf@Q)~-+!R^;qPJc;sC7s2;E%0avznOz?TD|b5yDS!+I2OJI)vw0? zFR@fgneRt(%UA6*b8GKd(_6!6*ZcH3%)Y&wd}a7@&qaO2mMYTpff~_|KPhu;s27-ad%t&VaHKUc|B{`4^-1yRt;4yN&P7uMzQMuuv37;eo#d zTH}F^XG8i%_1*y4ZWpyMtM^7)TubJ3c&a{mVj`C=W$$6>oBC@XfzmgcRybwY<`<|K zUTYzGdc*6G;?9xrmV-=sIgZx@jKCk<*oXy1ghmFj(zYJ3N8Z1ghOQBK3on@{^H?Od z^0dyFuw;|J`HQ{`JU5)W9?y{#_Aut4GSB8nh=#U2c*KWsg;xyn`4?A;o@n4=Twf{@ zd7+T{($z^e>cdR5G1`Z$X{y=E6}mt7+gM`wc3oF{^M6C}BZ>%q-vh`3~;`>IDkx7;K!wjsh!B{KQzu{~}<1=eWW7Un_BD#lr??hQHm`-49@ ziN~Y3t;_eQ*x&8O`lo;V!QP}1psSv^-Im|qBHb58-?;O5JScbXD#p9*<-ZfirpzY) zt69=;YDHjuyH`qnEh1se_qn|?Xfc~}HslXtQB0NiWRO=CFAAK!h}Nd%J!`At>-!Hw zeJd)|(fOQ)j4ZLS?T=!Q>9_jM7Og~-jwRr<%UhX3nvTc+FE9|#@Aj64o=vLjANVxV z3tT{QjtTs5{ezp(7n;2Q-9qU8=p0p>9=Ja(z#Veq>ypp{!@k_&8T6Wfpo)(;mH{zt z2Kao4nPaYhP#i;RGN5Wi# z+GsZZMr01Ya!l|<4qRch>-3f}J>EuR8@@&$oMw71xB^?EdUD!&J{Qh0J(s|X8av2Y zSAc&IayM1ab>C#yMSc`LvZA+<>WQCzH{cH{^&p`0Eqs@@A5wS>&ki;U{wa`S@)Hc_ z=&SVh2t!_ny_AMtz74Pl(ch^h7ZRFkP3^5;6Hn_m@G!C;vgXz+fHLu)(nK$>gtI5@*uew36S@I~qa`io#h0imLe zAzsq&9MR*Yyh0@QB=tsj$_G;R4xyq0kKXW|N~3nm*q*$0A*X}s$?Iq|dQn~=ybvbf zzoH!|BrUGf{7l1jMz?JVg2vcrNIsWV_>BlK=$I0!0DfhR29aM!2q{2&~ zIOXj)#HX%_`K8GcY@!q zhsq~|<&#RIcXY}()SFisbw#BPXF*9xfwQQrRJYbvxb_*HwVd&*U3ZGV;^1^O=+~zGR)Qw9KhnSLiIXZzw7))ID8> zMSIc4LMA!Iyi8{=Tw795w4vbXl0s%F)RmMKtSht+11~GxSWr^5PPZ~Uo0(C{my{$h zg`1so9@ZI4okh+qx*`W1!(O^jGm4JAdT&qM`Vn+xntU71x^Uf{Ha z`m8I0ft+RbEyFvQ3O67OBv7`N%D@3F9a!81A_$=aH0#O=6+|E|z+P0Uo6R__&hm$K zS%n4bsLXhKVF=%bf^ra(t$=qp3)jtJ(vnxAuvb_fV05}Cr@3`%vlTdi3kP3bUS8ynXPLQHQ zH+^#ylMEx4KFB*LcN>PtwbW^+ssO56w60KBpbNHfzyyQTW--Z5Kwgdr6+-jvFhw&G zbRg04&4o-F)o-1%Oou9n6S}@oH+L2jlod`gl_C>0b*e5~sSW8`uxct+WfW{I)NLu_ zb!*GYivZ!E&=9xNR;aUsENAMdNa0JVpQ!`=8!BZ)71GWk;2kET49o}T1W=TNwiPLg z3FIS0v8%$3MQaOx8WqJ1ohwU`-O0neJE$r%W)P)Am$EWz`2)$zS3ZzxT#kg<3lYPt zAvRlBO68Y4ClPokEez^)NavQBH_p=)tXqf39@b5p?wH182b}_TE(bU~gVH*lQIrs7 zZN%OOveN`*zKE!YSks+Q@6rC>QFK!2H2 zQJkQUGZn10q5q=JaF{&|B@7hlVO=)Ab}dN73Z4oyvJ~bCB=%e(*qSUfTuz6%0VQB;836klcT6`R zBvDcV)e0`~AVp4W=hRJK2j0V3=t!6eu4_{%)$|QT>upY*v*4LR(EQT%h4xudA*Ht> zO(3^thN%Bi{HwDUKuhZL3)g`xrvl?Z^7>M0si69HJCCMzrzZ{h#97Q^h4#|IlG&!R zbv&4UP%R5|&lGM^^bs(X;uGi;KZ{wFX3Pq@irnO^70XtngCLRJAx@*Pbgg|$xiX64 zGr&4#f+_P>D>#WQs3T7o+Q2}gn8!hUR`BPbwQG4hEF(-xYGR^Zamdic&ab5i{vA_M zLlqDw3S^G8pfDgi6a^rsuG(1!lU%Zn0T)X(8`K#bdV!M~gOQpAy6%V3B(lI_U{cpH zE1}bdg3>KI<3g}KbZp=NrUustToE3aUJmQQR3MeQRdaIIO6HbORzCi?&Xm0337zHf zt%Y{x zr$7BEV@4OK7{}&?3x=%!iiW0?lsO7-t$@nRWh)D;L$|327tcb6v#3-gs!EGN8?Z^NG^eSqTWX_Q2ZCB#6q(ey2LT`1BG!AvIyuNjw8|!N&}rr{Gf#pGx?=2A_A~vlBjU__V;M9X@{eF#iJH;G=_2B7BnIV}{RD z@F|B+C463k&%5y12_H9nTHwVr6>C95p9X(M z|HqObtjJ+!4&YejMb3OjNnv4menIKF{AYP6mX+p%E5UWh!Qg*f@_)3M^>mTbp`Xu8 z{1HvpI-V=Emof8yMAMS8vU28`AJK3ftnN(BkE9cp&gLJ{6pfGh^hY$rIr!I)Xoqw5 z*^g*v-;j?@ul$In8_L!>n6H0C!ve?Jq9VrkBU(Dv7L+o6Xb5E3)>@f?TaULUdD|^W za4vr`2m$A_p1FUp9AqX@fuQGk=GJ2fp=%HlMELO_1eLY4j2Rf-3+7NK+=3K3%9;4V zVj1s*@N~U{nIQ~8qkv`(f^gpE4QTBl;bwq13Z40|F6CRxnV&%sAJLwetwRbS zUfv!G3vzS%_I5zMe<&=7{QIFWr0(WW08)ehrOPcqkU|Z>hQ#E|f4jgflsog+1J&#K zqIFE=OJMSjd>VP?Z+P0y7;6{?zG(}rha!f=SY|Y@-ol2}Pj69hJa-4!>VTN9u!L#6 zqh@VcnSC9j`uXkfr;ExQ`AYIS6DlE9%6FDgl}P)!;*_8WP^J)y>x$MFIYaf^0RTaT zvy3ldCcg&k0m6c(9e~|gn2(DeWBUd0V8^yq@U&5ReC;g+mgW~R&;C+*g8p&8QXV%m zF>eR^ptB7|(#!z_7L-C}SN>}hTM#LIc9dg_ci1aBy3a)OTqkX1pk;hkVBc#ixHeJ&|1WhVUwg@G#PgmB;$DADYU+5B5< z0%614d<=`3lI^IOb_kF2LE#y3uo#M(9fgc{D3s~lj)HYOLlq_8LDM4#^Vxp{p=$vQ z0xc?CSGbv3{(+)uPeU*dDpJSOe zz30#Hv=###=4pJST3Zu(Lc>yjgyJs499hAR@5YA!wE3nX00q@60I?Q0=gt{I!{2Vf zkYZ3wSQa-snc5v8Fh!p6_n}3U)BFEqn6;3yfT5M(c7r`1f+>LGJGx3sN`&K9Q6XVF zl&NHg_xfIYv375;MD4N^Hzg+T4}!ox9fkRr(tbJixKeE|*c1Yl1E_q%1`uLlA+z^5C3pxmEw@ARom?{CP~^Ho&ZMJ)=5v8w>)ELTDR%JG>m$<$u=} zRy>Tb?FCdDLdi~kB8|Ckhw5n*emisyjmvL`&ZUw1?a+BNcE26^5RK+Ht>578{@ z4)A=MSlt0$Ky$4-zzb>mbqCl$GqO9hERiN@cYyWGECA<&W8S!~XfCtj92P*|u~6m3 c0w1h$oTsH=9Uj6d|I^?90|M#hQV??h03Ibzs{jB1 literal 0 HcmV?d00001 diff --git a/src/bootloader/limine/installables/limine-version.c b/src/bootloader/limine/installables/limine-version.c new file mode 100644 index 0000000..9980fad --- /dev/null +++ b/src/bootloader/limine/installables/limine-version.c @@ -0,0 +1,7 @@ +#include + +#define LIMINE_VERSION "4.20230120.0" + +int main(void) { + puts(LIMINE_VERSION); +} diff --git a/src/bootloader/limine/installables/limine.cfg b/src/bootloader/limine/installables/limine.cfg new file mode 100644 index 0000000..e28f06d --- /dev/null +++ b/src/bootloader/limine/installables/limine.cfg @@ -0,0 +1,13 @@ +# Timeout in seconds that Limine will use before automatically booting. +TIMEOUT=0 + +# The entry name that will be displayed in the boot menu +:Rise + +# Change the protocol line depending on the used protocol. +PROTOCOL=limine + +# Path to the kernel to boot. boot:/// represents the partition on which limine.cfg is located. +KERNEL_PATH=boot:///CPUDRIV + +DEFAULT_ENTRY=0 diff --git a/src/bootloader/limine/installables/limine.h b/src/bootloader/limine/installables/limine.h new file mode 100644 index 0000000..365de87 --- /dev/null +++ b/src/bootloader/limine/installables/limine.h @@ -0,0 +1,466 @@ +/* BSD Zero Clause License */ + +/* Copyright (C) 2022 mintsuki and contributors. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _LIMINE_H +#define _LIMINE_H 1 + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/* Misc */ + +#ifdef LIMINE_NO_POINTERS +# define LIMINE_PTR(TYPE) uint64_t +#else +# define LIMINE_PTR(TYPE) TYPE +#endif + +#define LIMINE_COMMON_MAGIC 0xc7b1dd30df4c8b88, 0x0a82e883a194f07b + +struct limine_uuid { + uint32_t a; + uint16_t b; + uint16_t c; + uint8_t d[8]; +}; + +#define LIMINE_MEDIA_TYPE_GENERIC 0 +#define LIMINE_MEDIA_TYPE_OPTICAL 1 +#define LIMINE_MEDIA_TYPE_TFTP 2 + +struct limine_file { + uint64_t revision; + LIMINE_PTR(void *) address; + uint64_t size; + LIMINE_PTR(char *) path; + LIMINE_PTR(char *) cmdline; + uint32_t media_type; + uint32_t unused; + uint32_t tftp_ip; + uint32_t tftp_port; + uint32_t partition_index; + uint32_t mbr_disk_id; + struct limine_uuid gpt_disk_uuid; + struct limine_uuid gpt_part_uuid; + struct limine_uuid part_uuid; +}; + +/* Boot info */ + +#define LIMINE_BOOTLOADER_INFO_REQUEST { LIMINE_COMMON_MAGIC, 0xf55038d8e2a1202f, 0x279426fcf5f59740 } + +struct limine_bootloader_info_response { + uint64_t revision; + LIMINE_PTR(char *) name; + LIMINE_PTR(char *) version; +}; + +struct limine_bootloader_info_request { + uint64_t id[4]; + uint64_t revision; + LIMINE_PTR(struct limine_bootloader_info_response *) response; +}; + +/* Stack size */ + +#define LIMINE_STACK_SIZE_REQUEST { LIMINE_COMMON_MAGIC, 0x224ef0460a8e8926, 0xe1cb0fc25f46ea3d } + +struct limine_stack_size_response { + uint64_t revision; +}; + +struct limine_stack_size_request { + uint64_t id[4]; + uint64_t revision; + LIMINE_PTR(struct limine_stack_size_response *) response; + uint64_t stack_size; +}; + +/* HHDM */ + +#define LIMINE_HHDM_REQUEST { LIMINE_COMMON_MAGIC, 0x48dcf1cb8ad2b852, 0x63984e959a98244b } + +struct limine_hhdm_response { + uint64_t revision; + uint64_t offset; +}; + +struct limine_hhdm_request { + uint64_t id[4]; + uint64_t revision; + LIMINE_PTR(struct limine_hhdm_response *) response; +}; + +/* Framebuffer */ + +#define LIMINE_FRAMEBUFFER_REQUEST { LIMINE_COMMON_MAGIC, 0x9d5827dcd881dd75, 0xa3148604f6fab11b } + +#define LIMINE_FRAMEBUFFER_RGB 1 + +struct limine_video_mode { + uint64_t pitch; + uint64_t width; + uint64_t height; + uint16_t bpp; + uint8_t memory_model; + uint8_t red_mask_size; + uint8_t red_mask_shift; + uint8_t green_mask_size; + uint8_t green_mask_shift; + uint8_t blue_mask_size; + uint8_t blue_mask_shift; +}; + +struct limine_framebuffer { + LIMINE_PTR(void *) address; + uint64_t width; + uint64_t height; + uint64_t pitch; + uint16_t bpp; + uint8_t memory_model; + uint8_t red_mask_size; + uint8_t red_mask_shift; + uint8_t green_mask_size; + uint8_t green_mask_shift; + uint8_t blue_mask_size; + uint8_t blue_mask_shift; + uint8_t unused[7]; + uint64_t edid_size; + LIMINE_PTR(void *) edid; + /* Response revision 1 */ + uint64_t mode_count; + LIMINE_PTR(struct limine_video_mode **) modes; +}; + +struct limine_framebuffer_response { + uint64_t revision; + uint64_t framebuffer_count; + LIMINE_PTR(struct limine_framebuffer **) framebuffers; +}; + +struct limine_framebuffer_request { + uint64_t id[4]; + uint64_t revision; + LIMINE_PTR(struct limine_framebuffer_response *) response; +}; + +/* Terminal */ + +#define LIMINE_TERMINAL_REQUEST { LIMINE_COMMON_MAGIC, 0xc8ac59310c2b0844, 0xa68d0c7265d38878 } + +#define LIMINE_TERMINAL_CB_DEC 10 +#define LIMINE_TERMINAL_CB_BELL 20 +#define LIMINE_TERMINAL_CB_PRIVATE_ID 30 +#define LIMINE_TERMINAL_CB_STATUS_REPORT 40 +#define LIMINE_TERMINAL_CB_POS_REPORT 50 +#define LIMINE_TERMINAL_CB_KBD_LEDS 60 +#define LIMINE_TERMINAL_CB_MODE 70 +#define LIMINE_TERMINAL_CB_LINUX 80 + +#define LIMINE_TERMINAL_CTX_SIZE ((uint64_t)(-1)) +#define LIMINE_TERMINAL_CTX_SAVE ((uint64_t)(-2)) +#define LIMINE_TERMINAL_CTX_RESTORE ((uint64_t)(-3)) +#define LIMINE_TERMINAL_FULL_REFRESH ((uint64_t)(-4)) + +/* Response revision 1 */ +#define LIMINE_TERMINAL_OOB_OUTPUT_GET ((uint64_t)(-10)) +#define LIMINE_TERMINAL_OOB_OUTPUT_SET ((uint64_t)(-11)) + +#define LIMINE_TERMINAL_OOB_OUTPUT_OCRNL (1 << 0) +#define LIMINE_TERMINAL_OOB_OUTPUT_OFDEL (1 << 1) +#define LIMINE_TERMINAL_OOB_OUTPUT_OFILL (1 << 2) +#define LIMINE_TERMINAL_OOB_OUTPUT_OLCUC (1 << 3) +#define LIMINE_TERMINAL_OOB_OUTPUT_ONLCR (1 << 4) +#define LIMINE_TERMINAL_OOB_OUTPUT_ONLRET (1 << 5) +#define LIMINE_TERMINAL_OOB_OUTPUT_ONOCR (1 << 6) +#define LIMINE_TERMINAL_OOB_OUTPUT_OPOST (1 << 7) + +struct limine_terminal; + +typedef void (*limine_terminal_write)(struct limine_terminal *, const char *, uint64_t); +typedef void (*limine_terminal_callback)(struct limine_terminal *, uint64_t, uint64_t, uint64_t, uint64_t); + +struct limine_terminal { + uint64_t columns; + uint64_t rows; + LIMINE_PTR(struct limine_framebuffer *) framebuffer; +}; + +struct limine_terminal_response { + uint64_t revision; + uint64_t terminal_count; + LIMINE_PTR(struct limine_terminal **) terminals; + LIMINE_PTR(limine_terminal_write) write; +}; + +struct limine_terminal_request { + uint64_t id[4]; + uint64_t revision; + LIMINE_PTR(struct limine_terminal_response *) response; + LIMINE_PTR(limine_terminal_callback) callback; +}; + +/* 5-level paging */ + +#define LIMINE_5_LEVEL_PAGING_REQUEST { LIMINE_COMMON_MAGIC, 0x94469551da9b3192, 0xebe5e86db7382888 } + +struct limine_5_level_paging_response { + uint64_t revision; +}; + +struct limine_5_level_paging_request { + uint64_t id[4]; + uint64_t revision; + LIMINE_PTR(struct limine_5_level_paging_response *) response; +}; + +/* SMP */ + +#define LIMINE_SMP_REQUEST { LIMINE_COMMON_MAGIC, 0x95a67b819a1b857e, 0xa0b61b723b6a73e0 } + +struct limine_smp_info; + +typedef void (*limine_goto_address)(struct limine_smp_info *); + +#if defined (__x86_64__) || defined (__i386__) + +#define LIMINE_SMP_X2APIC (1 << 0) + +struct limine_smp_info { + uint32_t processor_id; + uint32_t lapic_id; + uint64_t reserved; + LIMINE_PTR(limine_goto_address) goto_address; + uint64_t extra_argument; +}; + +struct limine_smp_response { + uint64_t revision; + uint32_t flags; + uint32_t bsp_lapic_id; + uint64_t cpu_count; + LIMINE_PTR(struct limine_smp_info **) cpus; +}; + +#elif defined (__aarch64__) + +struct limine_smp_info { + uint32_t processor_id; + uint32_t gic_iface_no; + uint64_t mpidr; + uint64_t reserved; + LIMINE_PTR(limine_goto_address) goto_address; + uint64_t extra_argument; +}; + +struct limine_smp_response { + uint64_t revision; + uint32_t flags; + uint64_t bsp_mpidr; + uint64_t cpu_count; + LIMINE_PTR(struct limine_smp_info **) cpus; +}; + +#else +#error Unknown architecture +#endif + +struct limine_smp_request { + uint64_t id[4]; + uint64_t revision; + LIMINE_PTR(struct limine_smp_response *) response; + uint64_t flags; +}; + +/* Memory map */ + +#define LIMINE_MEMMAP_REQUEST { LIMINE_COMMON_MAGIC, 0x67cf3d9d378a806f, 0xe304acdfc50c3c62 } + +#define LIMINE_MEMMAP_USABLE 0 +#define LIMINE_MEMMAP_RESERVED 1 +#define LIMINE_MEMMAP_ACPI_RECLAIMABLE 2 +#define LIMINE_MEMMAP_ACPI_NVS 3 +#define LIMINE_MEMMAP_BAD_MEMORY 4 +#define LIMINE_MEMMAP_BOOTLOADER_RECLAIMABLE 5 +#define LIMINE_MEMMAP_KERNEL_AND_MODULES 6 +#define LIMINE_MEMMAP_FRAMEBUFFER 7 + +struct limine_memmap_entry { + uint64_t base; + uint64_t length; + uint64_t type; +}; + +struct limine_memmap_response { + uint64_t revision; + uint64_t entry_count; + LIMINE_PTR(struct limine_memmap_entry **) entries; +}; + +struct limine_memmap_request { + uint64_t id[4]; + uint64_t revision; + LIMINE_PTR(struct limine_memmap_response *) response; +}; + +/* Entry point */ + +#define LIMINE_ENTRY_POINT_REQUEST { LIMINE_COMMON_MAGIC, 0x13d86c035a1cd3e1, 0x2b0caa89d8f3026a } + +typedef void (*limine_entry_point)(void); + +struct limine_entry_point_response { + uint64_t revision; +}; + +struct limine_entry_point_request { + uint64_t id[4]; + uint64_t revision; + LIMINE_PTR(struct limine_entry_point_response *) response; + LIMINE_PTR(limine_entry_point) entry; +}; + +/* Kernel File */ + +#define LIMINE_KERNEL_FILE_REQUEST { LIMINE_COMMON_MAGIC, 0xad97e90e83f1ed67, 0x31eb5d1c5ff23b69 } + +struct limine_kernel_file_response { + uint64_t revision; + LIMINE_PTR(struct limine_file *) kernel_file; +}; + +struct limine_kernel_file_request { + uint64_t id[4]; + uint64_t revision; + LIMINE_PTR(struct limine_kernel_file_response *) response; +}; + +/* Module */ + +#define LIMINE_MODULE_REQUEST { LIMINE_COMMON_MAGIC, 0x3e7e279702be32af, 0xca1c4f3bd1280cee } + +struct limine_module_response { + uint64_t revision; + uint64_t module_count; + LIMINE_PTR(struct limine_file **) modules; +}; + +struct limine_module_request { + uint64_t id[4]; + uint64_t revision; + LIMINE_PTR(struct limine_module_response *) response; +}; + +/* RSDP */ + +#define LIMINE_RSDP_REQUEST { LIMINE_COMMON_MAGIC, 0xc5e77b6b397e7b43, 0x27637845accdcf3c } + +struct limine_rsdp_response { + uint64_t revision; + LIMINE_PTR(void *) address; +}; + +struct limine_rsdp_request { + uint64_t id[4]; + uint64_t revision; + LIMINE_PTR(struct limine_rsdp_response *) response; +}; + +/* SMBIOS */ + +#define LIMINE_SMBIOS_REQUEST { LIMINE_COMMON_MAGIC, 0x9e9046f11e095391, 0xaa4a520fefbde5ee } + +struct limine_smbios_response { + uint64_t revision; + LIMINE_PTR(void *) entry_32; + LIMINE_PTR(void *) entry_64; +}; + +struct limine_smbios_request { + uint64_t id[4]; + uint64_t revision; + LIMINE_PTR(struct limine_smbios_response *) response; +}; + +/* EFI system table */ + +#define LIMINE_EFI_SYSTEM_TABLE_REQUEST { LIMINE_COMMON_MAGIC, 0x5ceba5163eaaf6d6, 0x0a6981610cf65fcc } + +struct limine_efi_system_table_response { + uint64_t revision; + LIMINE_PTR(void *) address; +}; + +struct limine_efi_system_table_request { + uint64_t id[4]; + uint64_t revision; + LIMINE_PTR(struct limine_efi_system_table_response *) response; +}; + +/* Boot time */ + +#define LIMINE_BOOT_TIME_REQUEST { LIMINE_COMMON_MAGIC, 0x502746e184c088aa, 0xfbc5ec83e6327893 } + +struct limine_boot_time_response { + uint64_t revision; + int64_t boot_time; +}; + +struct limine_boot_time_request { + uint64_t id[4]; + uint64_t revision; + LIMINE_PTR(struct limine_boot_time_response *) response; +}; + +/* Kernel address */ + +#define LIMINE_KERNEL_ADDRESS_REQUEST { LIMINE_COMMON_MAGIC, 0x71ba76863cc55f63, 0xb2644a48c516a487 } + +struct limine_kernel_address_response { + uint64_t revision; + uint64_t physical_base; + uint64_t virtual_base; +}; + +struct limine_kernel_address_request { + uint64_t id[4]; + uint64_t revision; + LIMINE_PTR(struct limine_kernel_address_response *) response; +}; + +/* Device Tree Blob */ + +#define LIMINE_DTB_REQUEST { LIMINE_COMMON_MAGIC, 0xb40ddb48fb54bac7, 0x545081493f81ffb7 } + +struct limine_dtb_response { + uint64_t revision; + LIMINE_PTR(void *) dtb_ptr; +}; + +struct limine_dtb_request { + uint64_t id[4]; + uint64_t revision; + LIMINE_PTR(struct limine_dtb_response *) response; +}; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/bootloader/limine/installables/limine.sys b/src/bootloader/limine/installables/limine.sys new file mode 100755 index 0000000000000000000000000000000000000000..aa3cb4a23ed33d38e100d70e33bc47ae32ff2a17 GIT binary patch literal 159436 zcma%k4P2B}_Wv_5=!l~aCKV{vSlCLLGHT|ewjc~(i47uPn@FVKKngT7k1rKAIE-<5 zdR%LJ-*vZswYzP*`_s1a{e>5QfaWT`tdhEgN%he1t5N#{V(a|B=RPwE+W)`n$C-KV z+qvhSd+xdCoO|xWkBN1ATfYkvw!g6~T&U`#$BQ3`x|=t;1>xJf@D(F$GYNvQzHNQo z`u6qe`qs{z(SmR=^e=~f<>@jMH(PrH;my`Q)D#qJFFVlpqo|81HQ0;Xg7E3KsDAB5 zQ(gQO@y@#9ZPh3%6>OiH>#jk+Tq@Xw=ROSx3!e%X9uVF?1%Unj{boXOV9+1k8z%%N z{MjwvY(f#c`Oj`aXpW%gO?Z;$$LpIT{)i{~C+Z^pUPV8{{=t84`zL;~bMtb(74TpX zeiF5BN1<#0R!xJ>RS8XlzM(H)l~8QC<&SRHgyO8?c^~T%>;8UA@h^%Wb9I{W^k3gp z)!gl?+9rhk{g(Iju1IRBccbMKeN&4?-7SBwqMzIUQ6-uh-Jj~jI~%{PsfrPbcbbIt zr`I>GKeyhu{#bs&qQ_-EF2={`o{3Hc!8JN?KkyI&_X81pQ>I$lBM6>-#zu2pBOh_U zuRwUe1b;jz>TbJdEvEJgn%+2iafe^P)OXhN3jeun27WlHpF;f`JL6pNtyk-+b!suY+vJ&AEZ;rOBmPNzXYd`x z8;kRc3yK#JZHpf-UO;6g&*i@hAY`a+yr+1nvLt?7v8_0pW}`T_IIlRT_&2u?65kfz z*IID0j|XUweToO{X9U&jzP+**R0nTg<2|B!I?Qio%x2xt z%s8?7Pn5Do%b8K~v(e(FXZ7Z8^HEmmXOgNr#B#buG?WT?Ea%KTCbi|Ncg-ihjABim zXH7ckCOKuC*)Kmb-h5<8y;yx3eKVwm$HV1%(w@#exXFc_SA@x`8$j!@|r+$*H^>#$u1JIfv~SNdgZ z47j>$ksElZK#)Uawi%Kv5l51tXG`XWcf^- zbw!p`F%>BwX4LuROhb59JaAHvWTKy(F06Ik6|@~e7*CgCSnFtOoHi}u+NZAs0-JTd zFms(}-}prZkLNeqxBZi(ta|t+K?qIoLZ$MJ(uJ5T^<}^WK=b~Obms0fS+Bf*4ii&f zf9Z}=p>$!EEiWJ$)sBA!0-k*&&6O_&G+H0c8-XxU=DO}i9UlWbFV*=Hsw&?W1f3w= zjuFZ^1~$tmTie(y1IUkQU=QnASO~p^9K8@qsj_5HDlZf_4+ig_&Mwtu+xLL5dAVw_ z*1Nc6@cYk;-WQO`OAl$K8KF|^1uC{9H`x4ht-{X+R!9%E|4u753@jeU+iy2uQkY+n z{+6AUU8>Kusb_!WB-lm;Hv1T`Z?5|CoFEy*>hs9nrpH&dExR-v?Q#Q>LH$dx5f#|% zE83}1{TgzIwD3Dy^D}$m-m7V0k5&W|EVBP-*1&SCa>{nvwaO(Tw&>ulSoxh*SKYj2Y!W3KvEAE%;X-DTd-GK?(4 zz%umApeS?DzBI~~rOwp)fPkwOfrC5<>!0c5G(Ah!%V`FdY^Y5$)+8HK*ke(0S~N?J zmc0wnm|44&-*M3x5NChEz97+f`3_buZu%4$9*{qIf-0RCHyzd;;R`d7Jv>GBEpruc&COtGshf43`6%8CS_k;>Mb2?x4YAro?ee2Mm-XU{OT#etDLMuj zVKsQ_Qkk_)N%@gkkDDQB>)Z7aBniy>vr40~Y(T)4tIoTmapM+iyQf!l4e|6wh~7S$ zN$WvY*`?&^wt@J<_E>x=yED-L#Xrk~7@tX^r8kLC0y({@cp!>;sggMWeAufr8AMyJ z4G(O01tl>!?=t?NvfZmF-|M39Pv+x$4|V2RWDdyr$Iac$FTU41gk7pXJEUqqi77BG zrZ&i%L3U7o*&7^!w}Tc8MiGHgNCuhnrn&ptxI}<>MEcVJutbrc1UpRw>)uUJg>66% ztl`H_Ayyv(k}@X&3zs zA+To?nG@{Og-%$?wC0o-y~m zH7QBi>OV(QeLc37;muY*0c0u5uv-iXrp$FUDfXt6`GRjSa`=LkjNl=;vP-reBo?SX ziAErtHr8}cKrecCpdw%0ol(ReZ6SVQUzks`^NMsPyEH1>mYbzs>E;vSUZWSp>R-`t zGL5nhD}W7>ll3f3FDDyVnxQt?Sd(Tm{D6XAL(iqCFQEeMsZ4*1E9qSmPSM{V#oJonFEjd z=70`q_NFw@AsGU8BXMg@8d`1a*J`lV3VMK6S0{%<-(hJB*xZG33dZq-oPzbdg!NYK zBhhxMDo@ z3wVW<11k(mn|?n*b$nv3^M%X$%|XeZm-`N7W0X^PZ}KqSUG#_UXYA-MFW^|FJ~Nd1 zGI;B>Lf3HKB+b&~IH>{q@Y*&a598qWgV;GykT|vZ26-k2IoMKRkRqooFiH^?Ta?S- zY1Yq{V6TP7oqM(E&5(}r{k+j23)O5w%^y&c?^U%{1FLtR73);JxTQ{4r|8{63ojA3 zWEx89{|>&XSBEOmf)f4mxFdD#dV^M6n`sdD)UyNbhTpmSB34Ft>i*=rosxTl$^Aaj zUEk`dYc(Hz*nKHdOlb&hj9de4!G`Se(2^<+I!B5n{NZ<9lEv=C!tZ!VokokLcU`j z!dMil_krLc%D^I!D~(tN;ug_!X`<^EVB*~`#Xt(L9m1!ka}*e;Y0yMvkuKeLEAmUV z{9N_-k(`fNqwb>n=M%;1k3gN$F#G0nq(x@%LUbxtH&cGNeH+hDyq3R7%iqQGW3J^_ zQ@-B*5ziMw`K}7UXob3zr*?4+Xh3(PzbRa6ZM&K`K}iW%{H}5CX%k&{HHA-1@!gi< zel{>sc|0KXnCmPT#aHT@^x=Z8(bF8})1{}^ri0=w-u#2t5Zoq(R9vvdkZ|Y$_>TSVGt4I1&7cMWE#So5N8COCwC&l75BA z^Ci}!r+2LD4ms0bb?J&I2rDCNs^&z63&||g-^8DUbYH8uCmkEflh04^^v+u|*VCIV zR=-4$dUM3;4*sw}^e!MN==U@k zO8ScB``IbbO15@2@d^TK>fBGeQNN*e?gtvoFHiN<=>a@fy&38|kYEj-1|sbGBUFAw zSrB;G{oJ6yU93&&^q!Wje%HM_2!)v(k5RR^D>hxt8yt;Jog*@BCdZ;x`kLkVSu0^7 zb#>}#ELJ}+6JO{CiyC;zGqkyYx`*24rrwT&RcxIvuoZYfXTIQk(oYAQhhW2Hf}qwgYoey zH-!*=iK-+^oxX@|Q~(^bQ45_jfaP|cca=q1W9Qw-y{;w^PeitR=4|vTxJqTo@9lOy zZVu&0kGN+(%!_7;dtM;aJiVh`BR##5(oLnq)IVsGx;fH!>*mqEkv8>Bjcu{#-c-x0 zLsHw+=e26-mMhX9J^K>5cyrHu+eBTO-kJ72--fEucdnhfu&MJ_5xI?>T>axHwU`4+ z{=U+2r9!s)*h9SZDngOc&zi03u;H-Hpirp)=ptppSQ<}t@6+mTi$tj}Vwiv*W>tB; zziRb3R-NIPhLAlR2Z?L0Lp28d!rIQPXP_0c)**gZAv=jx&-_?e<|cDPT2OO z4o1SndMt?$i-O2!^{hcxujm=Olu+-kPpp}9TiYX^<{{AEwowC1FISRd{p=*>z_Hg; zX%JS$rTA`_b)1V=TG?SGL&rpIN)vsPn^JFUyAO&;Eq+1XyOhi}WzJBYd~0yY-lRBm z>;Y}X*6Nb0>!c2OaF8K9`{Md-DV{D9JAn<2hS+a>*5gf96-#zVt7{PbZfz@M7M#e5CeCqx*v8Le4quU|a1GlMteSwu%SLF+xAueJF!zMDfM9L@`a|Pq!xSfV>KTobm%zS2(T@D|12anmxpTwL2b# z<7SrhrF^UJfxjhg!wTVbF|tr~tFlJNEC5jR8gH@T`F24_lD?1!`zCS$78H%ur(IoF z=jco2Vd~>8-XbQ+_a$GBS4XZ*(m~pqxsod_;?~VBif+>wu z^`-LE;40ew;^8nsOg$`*urxRyVv-;F^$GR`G%a@7JtJ079@j}da&)Yv!SRjm5^G4f zFfYqyUxmF{{el}U(AfMOvTwM}J|ARMn|VHA@Us0Y%Gjc1@Y&%w5{5>_8pW)=O%Ysv zyiH5G8rtF%ENNFXRFx=o>lQ)a`C6{$Quy`-7|xCd(7Bj;0QP9=fh_eg4qP9Qdf{}^ zIRl9J6ZkF#tlRip-50EA^m)y2iY`f6lB%}FgZQL!WYed~y zmLt-6-w5+j%K_)mgd@6!rZ8dFZ0XV>ttA>7!TYsgwPU8WOEtDjUGZ%&)m z?mBPYKdoKdSg)iG36SGaQgVd9=xrDCt=$|$A6B&PV4XZLynqm*ayd>PVs zq6YDPagTlynuvSi%*v|0N`?uQ#FUo)wbLw@T*uK}TmSCJ$*FNGMu_2=ArKVdS+MG>z{bWWb=`f!em3@PGgYhE;`%w@n2Dc_-lOU3$6~yH!sU zi2^BB++sD@)TE1?BYm;<&%tY*ub_L+6`eFfU5ourEydTn?_ojsBHp1NZ=E#Qeh;>6 zUzvN=FhQEhn&hMy`;q6K`944k(lEO~?0(b#>AyxKqy41jfSwEIk9r?W0JY5*az(SlwYQxa z6D~AG(6`S1djQB%do+`eH36|Cdj(c%o_eld-gx^DT+2V)FE7TfZ>M@YujRqX1ng>p z9T1MXZ3oL>&d!Z9*AJa*k}}Num7AlSf{@Uq$(+a?a2PjmoA75=1(f?tTpg-`B*YFiF*s zK&yDizrwrTH@Pmew&ESV;ooUY?4J22PRgH~>xy?=4*!mRE`@)$+09d%jl60oGhEAT zF5c0HDnpBR{22aSQv?CfapqP33_j8N(Mb{DGoOLlcY*P*G50jNG6rHnPQt-U^Gux^ z0@1S__{llZvJ~~saZC$l;TF4q5_U}eYAk+EK?J@_B}#rradUW-P|ThU!=XW^zL-5S z$kQ$s!?DDi29|A9DtD0%l&2RD^biEHv3kK!om5x;3 zI>(uAPZ+T1PB-YJyI`7{yTOE`a1;eeByM^*IxBELAz)K?P2q6KpFE8(W%~*UW60kx zuvKYdp{lDFpzJ~%=BVjDs;+;R?0d!gBN+@$*rN#x?{grRVr^mdp0hC0s_Ru44Q@qq z)@XTkJa3=5E@>~wK}sCXur!BB5}Z%aJD~v6c$JND0G6#z*05{l-Zmiv<-4cQq_9u! z#E;CoWZdOk+|04Loz%yX>T6$e8gzT(QB5a$O98sHLN#dEvqo0g&ZfcBSP{i4rm%`c z*=r>&9b7$Q}bI0<#3PK^<1P%K|<=EUQZp{*n3VWLtapAXG9T^IC6d&%Kf8gEmq zZ`jnMW2Ttgq7~#Y3H~7N=1|Wmmh%ctkLA(%)_9FzJ;+bDu5ns?B~MHVU=pe|hiP@#0)PgjQWDoWwnhv*uC(&FVWiG$kY4b1BS`YCfIf=?%C> z{bchQHG#A^Kxi5lgpdsYZVdngUVV6q=aO#aUL0e~mF@5zq*n#V121}S$N8FXnEkac zf&Mg(zP@T7$*MHRvDQUX(lBisLO{VdMLJGXm8;%4QJbf}JZr;QW0 z`s-Drr`?ardV38p%~iXhGVwiHnh{tLr51lqeL!!|;C_T*_PBP8$Igv!y=UJz=&bg3 z?Crjh_Rq0>_>6WFz+hvK);5!up=E_^qN=V{;9@9ea8lfttJZ7k4z?$g!o@s9K?Bc& z&sy|e0G9Gb^6>jo;jRCK`}zmr903Ok#m#A9m|9@3XXml$?4>iwF}+Yio>up0RFTsL z(TXs7&icpn$_sVT$|of0OD7RA|G~EdBM0F5+9+b(h# zus3P_+t*|M2*4{r0E@yDFUgyL6lGKY!Zk|6K&WHY`}#u-2UL&M-&&fey6)CEs!6sU zzgo!wfOA1IT zN8y|CeD>HP#l_2x!c-5w= z&>+A?pkrV~ocb)Ug)1Hcf8_Hm_wVj%(5ZGU0zr;^RKsugRdZ)VObP!+B`)kyc)1q{3;?~UetbpXV zseA6_gu;Sp*EJSTw{{^0C9}lszY=?_m_uH~c$EZ_67)VnCkY$ozofgmO!+PlSh!%q;3rrXb6t7}` zAdqYCg(hFBQy)XZJ_g%>?<>B3D3svl2u?;!L0Eoce&_o!+#1O5Ow=C8a?Ceot9Cwt zdc+Skh2^Tlc%ORK?4CYR(r4%S3{8V4vfiX@n|c|2V7h`qORf=lcJiELtC@U5Hu@65 zILqO4Lmdc@IHc%ARZ9&=$}=dKat&px46MS)hXdQJ2l`_!j6e57g6kId8icjniiszA zGW9Or#5XuMkQ)P*2&^!|U{deUvWbVHwMWeuhPvyXCb({eff}9>6_8>udq!1v4*|gu zKz`tf=%58_r85Bc&jpvo{k%?)S4K$%=Kb846P_0*7nnwTTJ@!X@n&U}>auLv^RjZ) zi=kz<=^dl>E5S_dQ&rDZyHepb87$LyCbpkY`6U9HM;`WquDvXHzS* z{=_X_%0T>;>z0|Mv5D2y=&Y)Vssn(pN%r=k0uHckYUdalG=j$TvT^~L^=F5gkIoA? z4RACOXBi*ASA+RPbZ3;rVxaH9R+zXL( z2o_OeNb>z}6Bdhf21-+Y7kTzy23Lt&MtYATQVy81%nor&iixCwE}=o0ANjebPygH` zWlFf`yiS>89^+meu5%4?uMP{7;@po<2mqxke>9B+gy7QjC@aNmi`>|B+ z5x2}AI>ysC!8O>^H(eUyi*-LY^v>z7o0O+@HLncI#R9f8AqK0B)i+i1B!nT6PEn`~ z6kYmVaK2oL;+AkZvm>EihoDj4y;YZVAf~wXC?VZabyk4CYHR>;Sh~h-ptax|0)q!S zDw6K&2|U=0gI?3&P}zNcZyr!ByA|spc9Ukj_BvsEv1Uj?PuXO?;N?&kcp*otu?S zqM2s*p_4Q|#45sFt_{621DipqkwlhPYfS+qRr`p?In&d`FVM3J18c%g_5rU05d*WT zIVn2ruZZqTidl&tiWEtYYfeYcoa!1^ zIZ=qE2}p6Y-sfS#2NKn{PH-H#`HkAZts+Y8yq%AXXL`D(z`|8B52{h4L?+n~*jk{Wl#Qf+IdNN0oCridn9a_0{AKm-EjQvqe!gb8nJgJxCbkBD_^a z1c$u)3?Tp+f35fh~MJYL(i`g@y>c5Y|Pqa^u zg>{rIGMP^+Fn-TtkNPL5$9EjQ=hBR}k`CBbwU6d%=`U`xz1`8GIL7Pga{Co(acPJi}=N{0N zniT0E41em^rkXVfT9P(_3qH?P`0S)Q0_7B1(7z8l1Pb+%t8T?ap$l!ubashV868x* z8cQ=&gZAvH>_W$HY#)Xitoh>a#jQDCGusrz7-zQEpN$%FfX-!YQ&_Vv0*g5ZA+##Y zUU6%MQAveY_-IczOP|3Wod$PDU8*)voBA4L6m1^qN$lCE=9K8UpHOgbj1xa{`ZSgj z*PLQPf3)e4cMCH|6Q!vNr+wdt_Rnd^#IeU=aZ55ZsK<;5q*J`RKv6r9oFW_VxRW{} z>tUI=HFuQfdA$jz+0qagHPQ9$Q9~L?W4we20SzI{e)Tip5g4U1j&wJD4uRL9UeRKG z*d^8sc7_Wj8z1gTY2nSH>%R^|3~Gn^BClxhC5c5g z#I1QiHgCMR_0e(lXCcmW5eEGLYgWHL!ey4{GW5hRX>|GidJs0h6;XCd!Emr3oKc7B zzmBNYKR}UvJE>0QLB;q0%k1)08im?94-92F9b)x5l(;X6qL)_g`$=%0a+;Vl0%62s zIAx&S*9!4>h+A5e=-Eo@OBZK`qoMf190HuLvPLBp01+b?{SwPRuDo~~kym-;X8PF+ za}`l|9kr>?M%JW0*qbi>AM;T-Z))|~HK`*aV4)9^F3Oo*waHl3$7`x@ZbqZp-+x7upvP%KeLA(%C#Jy_-|CC&wyHYUy;{~@>}wEwD!u_% z`ml7UVy(yC*rlHNnwpdRe+~omMYt~+T+`r;O9u0J8cd!>)AY<%(R)9J1|~Ci&&|$L z=RqCEKyX6QJ(u~b_7P`uc88|-JTJpmSvnNpZR%9MbsD$9TMjYhcRx3nXx4;%LCBEK z(~^O=(sy&IiCYi`b&yscVND30074s-)Q3!^Ccck<%eO;#d{Nd(hKaVcJY8_gcFC*Z zU-txz+rP(dAX-~Ifv7#-L->nU|ByqTfN3X`Qc<)*)e3WzT8=r2acIM$!0-VQ{Fz;J zhNEuANI*o~r6Cr&gTJRo}mr7r=u>=k9)Qz;y>jUiq2+BUBDGe_CsvX6cd6 z@u5*5v}QB_RpiJSQF0cHS=#yGd4)Mj>OR~T=rx+};<;3-0)}I1eS4TKup&wAxP?=@ zG$U92Onba~jz(wY2%f~jFWlc!G)~7`w0ItzL5W+FjpCNs!^KT-b#|HeW829&!!p~$ zDbA>4WDep0REPzTeO{LO5|=zUD?QGR-7B@j^FO*0(Yjr@1JQGq)g#>3+L6!#@dAMv zdjuQ_#WtaF2eDJ^0n6vCjczEM6lZ-drsDA=WVf{qQT?*9##4U<1QNH*0wVS1{o%7r zILZ{a&Vxib(DSA4NVv6QB)}oEuCiU%mz$ksn+LK&A!2_A$K?QtcBpQ;&qiFp04?X~ zN<)me){M#b>@)S-9Vx~f!I;?bTz7kVqa?Ug*kEz%)9B~vI3*hHk1*T3yntCX11=&C zd{1)%ZAy+)^c!Fclu?mo^O8^MbruI~NWB^i4aW`1^)q z?cMbz5o*b63u(8>H;UF~wr&BQmGujND7%0-j4*W~XMTfSfRd%I;xJ(?`LosS+=;2r z@Ybyv0c|5ZgVYT~JW1arVUM!<^PmNZ+*CCx(6BCKtG@$m;75DL$6zsbv8u6+I{J*d zf-6XEL@p$dxHWvN;72%?@HUi|Tc{jY68!3yKpw@AbpQ0Nm`Qe|^d!fN3SYq?8w%xh zX9(oF`Sg9Bol=*<`M}mafv%xjY~z)#YWz8M5T8jgFW^VkTBOHO_*%M&mU+6qlOoye z6!E0q*QU{y$UxnV)w&DP`c^Gv4sKxFtk8cL@0Bix;=94I{McXBT^0#8>Kzp$fd(Q0(a)BmSz+)M9GIN)6se!aXAh zQs;Oqb?o|Tp85waM6vqMz;V6l5KN+;uXP6!n&Fp^%~5L~iQCJk{)e+`XO5LCdAs&&65C#97Tyzyz0(i=Wwu)9Ips>@GKuc2{-?uE*re zGqTh!zIT35{nvwVgGo|H98fY}0skb;vt?-uf5U#hloabLauQDTaNB?jpnN}a#7(Q> zacv6`G@C9!#6W#(E*!n%NQEXGEd_FndQF zg80IPQ$ju0mKB(+UTWcJdGv^3zffTZG3 zaD(F>zBd}lW`>|`47&J0csq{TpaI%DU=FLXS}o;Q*t~GJ0UM6EMQ`x*PITRgUHbN# zDyo%Sb%~0lvs{B2s{4!-+XKExIY)(k^eHqk^L{L!S8*nB2A0z{z=smj#_~y3)P0Jp z8#yYwq`XS#sSd*1+KwF}-#`7;1;|4D3HVR+P}n9G$FYFaj;l3PnN-PAcvRe2EhO-^ zqzy{4sSM1H1sX_?Bs8`5j6&LWJS2z63Ah}N1A`G;@nOd$!>zEk#A0Cm1IOYQ2zzy&q9 zD+HZG<@DWb{~HN`amTq3;FrQUb7A8oPmp&bR=uY;OspQl9}Lp?pgB4avR`vbLf?(o z!7BkwIMeyg+jWrN@Scu_>op`jqCKni4~j4R3boi_h-W2v92l@Ed9kpKgpduap{g7p zT&Das4fL{;!Hh<7KS-ERg>OO#?~v47_4cELKMK0C)g>A(N@hDYayVIF{L)l6YQS^V zRIMC=G_WRF)Ah#altq}MHjVQX`3c{UIb9eWwJ271+h}llE zPvt7wTQCIqL%!k6iX(!nV}lqYnDzNMc>CT%by^(nHQmmnE+q|)hJ)}KpbM~qMD=?z*=zTmVk-sa6TlbFTf>F`6KEAH@14-rB3V5hUw}_)$l{% zQS|E|nDpc#oUHckqXERgfatsfz__VcZ}B_tlhYSMb&Jca8XXNxJHZvg$=CZW~+8X3%m%2QN z435oZ4N8j!1!uMO--PL!M;yQscvg%4iXuHXDN5N)8+%|gZM(EV1r=j;3~~Wl({w{RG2fga z14qj1e?U1V3(m{`(ZYIy`|DOM6>L}4MT!(1uKfk5Kubaj9x)IUT^ZH8F$9ufThpd# ziq+jv)#O*BvBc$1V(`tL*0T}vt8qNT#50U63zDNwyXp8!UDZBy=c$UOnPHH(K&(#Uqrs}pv#ArgUV+26 zV3${KNP4=GSk69{*&kKk6&%%#28+V}`9j=O*#Sp>rXL$9 zj{5uLN;nMYKDD*$UQ+zqV@VL8AKYg~O{5RwoV*oA0yxHUNt|^;Ol@U3b>gf`tnxUJ z>`uDVAdQf5TRH9xv;cp0nm|`vmZsDCZ3tw|Q^)e#97K$O1gFzK`IdvzyFoJ}FJcLM zXQKh@@iZF|bHMUH#!YkRyn~>(r-ivn$yY)=1)oHpWz@>ZWKV<9(jxu9>d8M(!3Te8Ji0KGO+thFBg43yC;M&j$ zw?AT{#Oee1rJkmb0%Z^g<_Puo9rZ^rtN(~9vlabp+4w@jr?7K8r-1347xb1->B;j; z6o)T-0aj5=U@Og0u6hQl7$mCv0wv|W+WNc}Nyv}_yTtZ_N5r+~xJXapQrZ4JED0QM@1%W3P(Q*(X76Z6 z)&={AU*g9XjQ@4deB%^w6utkzH+I{qMlyM-&e0LnJMW=LzW;4}v-40*8azi4se`@9 za+s@+u}2(FQfLfLs<>93gxEpiyH zZ?T+KQtILHO2i~8Sq7|Js9iXI*U$4jS09Bl6uNb+Oo7q3kL4W1LSRyz=Ubh)b{CLi z)?F-Tn|uphRK@nV#-KaNV5cuKj%bvVdu1~P-Fz%R{UdxKBYNN&u(YxZqjOa8S4~)p z)3NPXu`Jr5r^J2^y-|F*!F0jhr;~1-jtc}=Vn25bM;&%$v~*lt8vuU6Y&6H66GYA; z@~a)rM(nmYPo{e908xDcUVJS2N`yhxu{C{yR7rC~d)Z{gza8Ez*ZeAt&P zKST@s0=rVxL@X!&LXW5c&oOWqSZ~Y~{u6FhN8;TIld#wSLLd7Xd)PRpm(4QZoC+{z zp$kicxYi5NORM$w0FOPwNF92fI}oLloXU&?@~v`#eh>9vIk5*d$iWD*tu)95M#og$ ziIMqg)fFA#eF}Ix#-Xt?=YRoB3N$>&0?f=F(>JHn<}KjW1*qtY8}hjn1F1Qi-0u)K zXffqLaC8|-({b%*?Fym~CcPeYT7j-Kj9Ux-&H@r%P|E!<3Gv&Y=#vzEJ*g5j81e$; zbU8EWj&+n=p_fUj(uo94D-m$eE~gX2SdNek25YIY=gGM<>r-gQB$3aHvvS%YlEqo) zQ#{SsHT;$~31J&u=!F!8=thpEV*L&5r4tu_&r`S?f}P=7iZx-5@Mem(B#-rY%yig? z-W-fZNlql&B1m7^bs(zh3Q+@>nh}k>3fC-@=y{O&7T3ursYHo8jN?_jHbizB!DsU1 za6CS)ADQ1ZdErU@5{KE#!GMl8>!XULRr<}JXGCr0OD^w1Otj-&%Ev(`LGsmXdd7oJH}^t1g8R$#5Vm zm|%z8vvVp*>2W(}lZYC#^Lv6ZYUjO_IdbPODATyJmog)E>Y;vTL96#;TgTRpFd77q zwA&YwAE>ICcCdI%`CQRE86@bZn^SnhHQ@-8V39oG>{9fcOh~=I( zV8xEiGGd8ijeCwlJv5*_%Q7}6Ys(zyMG4zA1c!)wkBD0j)Sn%Q-~n`pjqn2R6?zSN zEwG%|w#7@%`TwSIa7@vhbn>ow^D(A`9{J;W3-B-+0hfoFmPt49wOrpItx*c&vjF8R z*;5xYeHcxXxOORIAe=Z?H$eorUm`ZpbL(z9$+8`0d^ z788oSu0Mo98U&3)P^g)E_Q{$?Q3!7)_Eb{;IS$Ogd4zA4L8U@6u0ZWB;Z^U(s#=Lq=M+4hjh{}`V|wKo z);1b~+p1RHRs? z=&!!M2nJ9borNG+3LZ`fVREScFL0{0j=$5ID06M5A0;@7vjr}7Hd@O0r6MO)2%Ar;RJ7) zq1VtR4RTkG7hDhHiW%N602tPImkY=~Ed9Yn#I+=f|u7%DCJ8mcUp?U3tMHKWr& zkfpXz8|=}tv`?9T0rPuC{FU{43Ny|*&hlH>F~@}Ih*>%{I;X`MZ-H|&_LMV@J-`a| z3I5p6;2ar=0DdSSVeDaj=bu3kcpfY`1EMQlEwLBGUo{wVzL)N@!2UcHd%=MlBCRxF z>p=`R;ZiIdzGqk@o2B`(n>E=%KzbV?)BB*vo8TgORx!0Ll-22g(Op zSXG$qeoL#k_I1c$)V5kGob6#7GK z;UOb??vO#5rK1<8VCJXrnZI_ROP@$ORe~jgAQu)C!e9kx@ z?yx0iX2VBJQ609@o7IHPBr1&E*5f*9621LF**hOEU(`11&!JF}xBnK_lm^X;os>4) z3pF`a*#wibDUIDvn~Z0lW?6#&wz)~os-Zig#q334;j(a{xms9+AN0Szz7w%2FA7g0 ziDNM7Bwk4Px*M;kYvg0e%iVsZl6HRcjw_Y886ZEnSyzlpg3-mYuBWw_>F`@DM;1ey zd$lg~;tz^2|466qMz=T&dPbp}D-_=R312|Q=CpWSJZ$S+jCJNFK33HJrB-`R$CCHM zq`u96{d!68sg*zJTzwr+u=-%$kx;37e;8}|`iWrne}}TwWI%%=4!7qxx;Dwjf~n(f z*XY0$Zyc0B`_DDHS^tNE$%FrL^YF$%Bm5Dr_$YZ!eDuZl3Kliy;T@I+Xx=D6Uev`8 zSZhJ!^))R$EAMIGJFL=8+) zZeHm)uml)IA0P2)26WsE$b9I`F&Ez}jo~F3C>oscX#v!}5U%w5qqz?4aZOGgg&}X) z5K8OR$N}jvwGRw`tvp=)c0gLMo*IyjP!9}94^ruN^tJYb)ep4v0^Yqr{WVWs?SF_` z+rK;DQ{qPWKRxm(s)h^JzqeI;-dcvZ$P>awwu1^qVIO^X=x}wvf|N5jf}K0 zyfl$ssMLdp#?Dv4pq4(E@c0#XH+%edz{Zqrww#1NNDv$mmi!J!8CSi0%drV=ofV6u03 za4brGSlp?!My(#vli%*i=@5GIJ8&a_=mp}1AYuG)^5u81R_{@1yyc>E5UypN^4*F% z@K=e|O|R_$vAS`#+0F36pw9oo$Sud5*m~Oe6<~4qQO|`t@ODi^Q?wzvrN?1leF+z0 z8*%c|>ytpN9Ym~z0~jlLelHwrugyH85v+mr9{U<8ylj)5f*QbckDa4(l?V!wA%5i8 zH$7hijU$M$86*w2G&>{jwYH8%lOwDtp(plJ0`NJ(X+QQoD18O{$|+A}n{Xq^f(#W0 zZXyEFyN04bEOrD0m}uz%0s4Bn@vfwc14%#;mc3vTOj0}NmN$t8C&jf4zvAu&&Mh6_ zmOgHFfm@_@heP9*r$OM_%nr^l32n!|1apEN;MT}*AA-1-GbketfeOQ}9QzI_5QrGc zHw4#pdnEWnG>(B6;EcvGPy^dFjyW@sWA<~7*$-|xfOr&*Nv@7KA4>@O9sIvh@E#yi zc7o+H{30M#PmgajyM)zpb<%r)4^8@c=I=RgKHW+FEOx=tD6aiAA8?E3{2kEdq+9re zUvUg!7hps*0xm}7dj@LC)hX`)W3WD&@^+fsjv)=qf9#teE1bf?8qcLEZ>K5m7}9p^ zYZ~VXY1i~_3XsWOC^=n4OC|_u$@7WWb93(%Os2=jR^&$tkB?noy6+*= z*ai8f2k~t(%^2%6jT^fv;h{(&wPZz!ljh^e)uu#YZsu$uM40l5#pO$if_O<~7*hF) zA_tMwROE1!JDjHCa;a=dq)-y1Bt}59x4dv^$zox`go#3;dD7%5_fLIbnx*i`#Y>8c z1#8(7qLs^0xVUJ7Ntj5}HgQ=AP2mLRYNs$e`%%l(sfiDQZl($c<`3jrQNB!CUWB=J zE_ReufU3eQ&|o>IrC?1PIM=B~PfAOL#8pBNZJIRzb`>o**$bT}smv)=RFpeFC{uA! zp$k-Wn#Qh*6q3kn95R3IeDh zTHwGGjY}|rq7|!(gf!0ICRe$MSkQzCU0P(CJb}<7S`im>DhiQf#+a}Qs5HcD!J;u# zlu@{%$h5j#GA%BzC_!h}h(@qo_9Bx5v>b2ZB*l+bKHda|?N4O_72?hkj2)9vj^#sh z0uUA8wh|f^3d(qnwyug+lq@d#d1R!}f39*%94-kf$jMn*uz{3LQ_B2#bM8%^GymRH z>l{MNQA7|X^jot_O9=W1V*j*+HQ|q^dwZ z$Ofi$KBX}sW^aNil@>mpi1#N?(u4`;u?HiC(T`6yFSjMnot=WG2d0~skA6IH`Gep+ zjY9oL)Q_f5N+pmnvr|pWOPtFKU5o7#A~A-t73dt~DO?uhzkX{uB*{`avG~LmuE6AT z5tLq3hLy7n_?Nph#tF)}%)-TXl3!dH4pas01_9ET>|OhDsm=_hv-^a#yNd?$x^$^-{FvGsf8rp zHAxzhi4%lJiX3G{%O+-)FOjhHgKSx7da7u(#*bL@VC`~DV3{;QSdeC&7ZeqF$@AvU zo}2!lDUBq3KcP`nw%De{OIa!|hLBiIJo03b9Sby4cogI-hCBz?E|whF zMue2q#6+_ukkP~;EheG!lY@#B`Y|9)6lR%*42A*ih!g8I=3&KPQkMwx(P(*L*=mz@8rB}B%>e;0Cb&n?j&SeT3hW1C zuy=5=YD=CsU${z?`Hwzo%1oa7m?`(sIXRiu>@jd0&5ZcAXMGoQTKmWO4BN3=60IQ}wz@=+`q0O_%=_&!o(4Iz{vU32AQxmb)3bA+f+X^CuSrFP!Rg{;I zXepOyhtn`fUIymlDv7p0Nt+472fEM%{0IGM4)7odH$lhAL{J!g?7`o6g1?kU<)lkd zbJD#KUO-^T6(gANNyH}!pHKJ0>w?d__QkHl-{&y;Njwg`@Uh|Z z1U?n`xbfM5&)fKH#-|RSR(#s=@#7;PN+Aj#6F!OfB;jMj=Lvi&@Nwg_0iU<=*^Eye zKCSq)6NqAmOg7j}sh4BxDK}JB1`OZKu*!fJ6qgjdUS1(Q z^&eDRg56!H`41`*p4d7M{0CKO@d@;|T>cGxCeR$vAA9+C^=QuMufhDgdXD7 z$p!UvsZ$ttEw7|Z%Nlr|*uD=S606e)`gYnD_*~L!i zd_~KI<{L^DmzO)12>M@M59rB~a%X{NypBg9Or-)>InBC|h9u21lE@W`kX%x-6k;e? zz7ZKvDqQ7KiE#S{3=fzUKIw$TRu!IBXpb9SrOdK|65;9Bv?sKW zdtG~6CB(cDY(sK)@l#xr7Isl)VHqsD3OIO5oI#-c*$IuXNO%j+z@(xA6869OGa0rH zp#jf8a&g6K;VbR2jGpPD9%V1D5a{9_Jr=^MLb6FqmeKQ|U-P16MPQenVBDHcST1N?gLSjYt?{)6`sWJ0NWm0;iW_eQ zWZS{K75@3#VD4h52Owxk*^;7F!kqU&2ypt7NET@PB@_hnc>m+!5h7)!fb?)-+56bf zot&jKzF2oHgLC08Db1_i_9r|oCIv@$k{-#D6ly|Gw6F{x5b4XwM%JLy-S|)juTHn* zLm3*V7m-t3=$bsK9}a)LnnxI8U&OY!$|clp3RUI!WPI2^iQxG1ZzfsI3sH`UJwsf)mK?1>QkUU9dgJ_k~bOLw9c|N&WFT zno!>l18o}r3t1c&&ND*HKJ)?uu>ksQ0q;i`{6CN;MawZBEu(;2H$n=somfb&5+EK_ z!-Zkc^7x9P_C$dnkwM)DllLLUl}}YL{rEwx*ipDLRHXu070Z_wfNhF|?MPro`(e|1 zeNKUEbw!b&{$w@~hT;pO!T)!RfkvQ_zPMcY%b}kd6){KaVGV(@qLqAHtA)BFL9B~l zatOAg!4wJX7m+BOlnDNT7f`4RrZne(u;3VBPmVvqjYo``1jeh{BQ_o31MP7+98yBV zwdVdBK_)hAp!jAlj zx|6tGetq4^TvNY3cZ%Tt2?+OdZT|YYQ@MVBeeMHXN4cSwvpM1Ke7qsob!-A^QPtYu%7NjhkOL zWLvmJc768bL~f+rkZl$wz;-KuU|zALWU?^#BqhM_l&ET>gcK}te#TS5GCG74_&=O3 z;{{Znu6S9Bmn%*7E*)&%90VcZE)-8th{eKo9!1~{AiBvFt$65mNT~_eHzsHWK3g-- zBZ$o&x_b(v8cz%8343X?XCXc1P~H=GLKdEu@F&W%(-W^pSDHS+qg*)-S9)$iXh}o` zvPw<#{=Y9c7QSrcHoGZ1CnrZ}cDwjf08gvvDL{G8@h8f2<0-)FRr4pxtDz@e??wI; zK;+hV+N-Qx&omopRTde3XGppc@cWJH{9<5=ZPp@MZAs9-+|EWU`ngamGJ(;wzR zfb90)pbUq4-lHhK$rpjxESS~ ziLl*i=;RR@p}{!_Sf0V&FjD@8DEh{2mA?9KBC5W@4BU~Y%@}o4m5S0l1ULkwX*PBJ z=ZH+nM5q(4Dj9u`HoFOefvE2$DC8L7(Sua~BTCkSA?&24+o{za zv_>!${W-zgb1eC{xaoZT*9NvZ1NpfDL{=l7i2e{7tD1wH;KkqI z3mkl-_#dLA2qGOqUc-S`GEzd-?Oq6@OyX@of6mfG-FT`IfAqU-1BPicKf z{nC}m4&TUYf73E49lcswU4O(&ZFGnz$CaK72(cEo#D&W*5f(W1t@910mt(|D(qQ1P z#_+cUY>Cs=`3AYqAq2@UGpbv&aJZJBt`KU4+x`fZ1*}1Jc*g`P;)dN8t)e-Mx1x&u zYw546MG@OFyS`TVb+u?-Zmbn{U9E_psMYGbt`h#f8eXdM5+49!{I(c%czfP8yyr|B zOi(y2Jy(X{f(F7ocF?r@?$PE)qaC1%n|i`Or)j3&EjIpD<%U>LvrH(C`5hj^^S zF*^PBvl^HIc#R~o@bXbC$G4F@;zN7gWlH}x9xaZ1i)&7_h{qv;w?&w{aj}kWCea#s zA9*&!AO?;hR+;$mg^!UVzd&%sO)n5Utb6?7DO^;3or;?EVJ2j133)3mV&)3dhtjm1 zkv1gDuWKM)ClH=GBhHQY)PEVKOnI4Y{R=AP29AYR*)}ABOmo#PJkUQR0v}Q(lDOZV z;S zJzgmeBIoA77AnIv#JK>%%ZIdXGeTPl#GB@OMY!1T2c7#9%5Aax744K4QTX$clxf| zNNB8o8{gtOxG!*T0Jrli@sbg|08DS_t;G%yZfA%%IjJol185-Cs^f2Iz}`P=obBzX^K_w~fs5Cnz6Xo5u3y2yslTGUzgWY zPb|6$*9+M*i0$cMvk)+%vRP4~pzaO*=!Y2At%S1tJE9_9*0Krj&)5FauKWTYF+|A= zd}&(orWoIoL~#v|*Le58(6Qh0hq#yVf&mu&J0Q*G|4y&W_?k&UgVa zE+61VD!rcPHKL?4Bgr#khl$>3;Qa+u0&FN2*A849&*TX|5_)>Ogm)&=z71}?V9FKU z`~poK{cC=KSc3jtqtUh9FGMdbcun3+I4g7UcOUW6#thtEMd*>3en8&vHuAmQ$}a+9 z#upIc>7TP2>VUmLz%|AV0tNq4;1U*MieUL_)?sEzjB(vpDwTXH|@3Z|3~}lJMKR|>Ys5X?OMnE z%Q5~9)OQWB*}y6qeVX0WD<&*3urxS6w14CO+g`5u4KE*vJE5S-4+J0*iTH+K=@r+( za|2NuCBI43w;U76`30pk27lU(FYf79qqy!B|dG zn%W@PW)U-!NBZk{x#Wj$FI^XExP>&|ikt4)7nF_re&xcpGbLD^@dKiaj(% zD?l#qEP&_G%`g+V(`M+!M zKahH{yiFx8qsJx^TvxmV@i#V|p*JPZd<<^f$~%R+Kj0P3W>A+#1}xA;%zhBSna^ce zJ>S9PC2Ht?&Xo0_$#X^Q(U|ohCN?xVir7TQ*91svCwl%AgCe|3L2Vo+bhgqXI9tdG z`ixJJ{v%Zgar(T72;|c* z!3g_PP&+Vil5X4Co z+~J$~ES^DtO|q9E5ZHcL*fGh*Cdd!ICO^hOz6kY+s|}Y@@lhSwdSN=G-O%f`<=z0L z0uWaRr9#?Pu^De5xHGNY5Zudi7f>1&@cAWnL*WLNw{i>R-w^T%n|De<@k=Ny&!T}? zXoq?VaW;E^h&5z6gH)%I>dw(f7Mlhm6buTm*#`5a@FQV<7r0k?91eVecxKASms5QQ zd`9*Xk85MAi@+Eqj5aFPw_{0Q=;m2PHa&;n8Cypfo=s*-CQ1~~gxDkD8FYd0+3(rV z@x>3Qh2;?f&3-;#u>=B_VD_y`TKpE~D`LPxjz8gV#UDHP0Qq@>5P(zh9a;-fD)jza z&SCDs1*mulXhnLdux#V8sGreLQe3Ddj!b66pU{~RTc)4}t4GzWo!eN$ZX=^NlFZ`f z9a2)g1((Apxw4*J)TyCrSq`2 z3Y!k>TQVG!(u9^ZWBHY<36)^n(U-jj(@xB8N)?!498d}>(E}483aRwwZij+oClE=f zTd3hwH&4fW20_S1V(9V9@G6QEe0`AF${{zf*;UVe8~9rCP45p;yBazP^Z2q{f1JhJ zDZRc6zXPjG^n3b7X5a8tN@8`%>`T75%j`?3o=O?=Jv$F71lYZg#A5?ajJo_5!BQ64 zdLI_Vb@cQueVO&{dc*~0nI1br=upXQRFbyT!8BhO9HKkkH=mXm^9t<13^D;(PK1!U zywE6>`sI0$sehZy7*%bOiv3dQ+rH9Gq#JhJsoWD!Tn(h8!#Dt#E{pUjiow2ckwscu zZ<5PQ@*3y~)f<5qGJJW19WqjL(NaPfVh1bZa42vvv@{BT2tAiVRk)k%t?9J#S;0i3 z=VM?GNkx7WRCN~_rNw@uWcKrdp$HW;)(?~|mo__~ZZD2S`5JU&LR)B>fe6a0*BXg} zRbjC@FK4d>LycwkU2372y;bK9$nR2TBxs4vTp{PKwPfXPg^uQ0ST8vRUo>3D9O-7H z8-=<8qAPh}=;aBIEnx9(z9b2c-9nFIvesK>h`@8mH*+g$_Z6*$N+6Ch6k)TRgd7Gr zjVgs5oVUz~kh1*dHiMkb61(N6ErDm}ePmzjJy@3X zbcL>r!jsoci&|BCfh{PAZ))qvSsDf?+8OH@(B$b$l~(yJmgBAUc&@w#!=P`02>~1)7@lUR zaug?^w*lE`La@RmnF^OhDpC^Z;~>L&0=KxUP~+O8>lxc`K9`AHm`gvFe@RdV5Qf`f zO{1@}q;B^P`pyk@XK07y?R;3;^c}MKSMqimM)~(_@In~8PW=ax`9%jW&$g$cU1oJi z2STsNY(ptOM;(NPhRp$-8Qg09&>9(10kl0jFvO=x-H`2v^v9&r(Ekl9KSE9DY{A#c zVT@fxtM^f&PA{W$neCCGbSUC(9jzmX*!BvdgePJ12hg--YzXEdPO!1eRVkoJw=tTu zh-ebq(GpDxGn&-JXcG7?qF&hXf|c_@Ni06Qc&f!%6H!y{XB>Gv?Y9@$#R5{d>AUSkP_5L%8+EP*JG zmf&XzRI!$@o+Z#IvIKUhU&f+QeL_0_pH930w(D$5Aw0GY*`f8(LOu;)H-qi!gvZFB zF&<@al-_tK+W|%>)+QltSeu4lVRNrxTROdhY7aZ=lebw|`psB;4cn+cpaH;b@Ip{) zIT}ihK_yl^5o(2zSo)MSwe<)wDDvZkgG{gKIP6jslVv9GPhg0Doq-pD&w-X5oPgj& z4=FgqC_f}@{uQCT&j`)|ygxLNh|whq1?=0JidT%l?2~^@^FMTLv_aG!e(v55dI|s2 z-8Pv1k&X)DB2bySgLGu2bHlb$@je4t9D@mWqY0-CS8D~-HVe6DK`W%0Ps8{yj>s5! z>5%#>Ulull819kpG7&u)1`a|_`8C`rtuTuHL|;6#f%rOo*{yyC`VzG~ruo+l@o)a0)y;)VzkQ z(oye6|MvDJd%gq3*sEF{qjar~1c1N+bDk)jO2fklbl;Jx3%w;YUXTsI*g_{f)z)K?wQ!K5Ksb&9-uKT>RaAy9z~9|$#>1_)*9;e-5;H%{l@rT!43 z1sG|qL`MQ9Z(mzQGCGpvy?AnEA>N<_o%q?5z-Plkw|Q43=-jumpL*KBMzaKX<0{LYoG;hx9HU^XbV*Wa3;VH=e<<$s^J)39H_!yb(~N~ zGeeBr0_PyKYDMl8>Rv$##ze3F8Yr{vDO5fHuqS`60qkx7Q*4jCa~hd#WLE^>f?1`aEzqA|BMvJs1@xh+h8{nFJOh) z6lqXUCiV|Fp|v*mR5KJN4+Y=}!_I{&6t#8Uo$cO>3Buz9aoI5)rk91<8t8+zI?Q^x zsHRZAn_My@f|vB{D-zo2Q7uO%?<&}Ash!LKWCGe1QXhhjA|5z-u$Tw8^I$0t?%~0Y zdEnx~13XyH12+#I;=yVb)OabtU$CZ@M^N2#ilM#uH(am&^%7Ye>2Cv(t1pVXO1*^V zZCR;9MgT|G3}t_YcN}GJKoNsbwiY9eG3c(s7<{by@#I})MpwrWl;%&LE6txy6~Zb~ zStG2B3pKPBiIFS}hfte?QpL!FLaiO+px%I=*Z{IYp)n*Hfnf}OB1Q^@$30-rF^tRW z<1ija>+eXVxvXA z&X3<#he;oFq$7rCf?i#Z_pOdZ;3p#6es$Gg==gqwVyWKcCpdm4m>CVJOK>1HNsP!P;)B-1G?_u@DS=~DT?CNb*LHszjapMvNapmfCGK z)6hR4)b=2Q7;y=8Q;>q)jcA(yM|DVSb;Rq{5l95N8&;p!)#@1btyt+RXcl{C36CFQ zgs86eF%TCl_>Mw-8}7YO*nC_2ijh+LRNUL2lJrtLdZ+#o>kc7J|8b5qQ;=^E($o!O zNVAw$fHmsfctYmW*{M2RLO`e`f<@13wC5MK=U4f2T>bqr({T;5XqfnIqX8+&9U_E*tY~RlR6b|2Byd!yd5$@ zbjv2krU+|PE*eFwhhC@J5Dmte@JZ0Y88!z!;0c(>7!(VE1Poh@V@ah1Fl>%A%AwIL zjm8HZ89Z?&4>-f-$i_N~d1R+Vxn@0nB}ZTkUr=yXby09e{UHT!sdrFtOeJe;{vB!&1)fD_yokYRk)FcYtQAbhmcXb#Aht&w?kAJt?OF@%L{%-wS)z2w-SN$giht%T~ ze4zfFf;M$O1>4mi1@EiP6uhTyqo7s&GX+8Q4GQ+D8!7mU`T_-w>T?uqQ-4K4hl=G( z=Rc@cQ?OSh*EaqGDjiGt+tmjs*rVP{L8p2b1xM5}3ihi`3Qnr?C^)V=B&eTJ zU{Yyj`$wopDTq@KQXr^%DKM+MC>W`dERKJe+DJjXx|srlN?TIDQQbhnD3$E|`$wx! zQ}8?WNeb4fbrigyK1#tjbtMI_sLLsMQ~fCgudB4%_xGxIQgB*bOu@g^VhTQ0^C?JD zZ=v9VN@p1US5+$oo7L+m*rZ-V!5b>^YyQ{NaTNSg9ZkWnRI-EXe@fL+;8pv8=Kcni z$iM$(HAKO0R9Z0ozg7Q1!AB}Y>U91(6>_#Z|9Z8J0#OZ6P^%I{>VHgqi-NT((OUoG z%r=4l3HA5%RHM=%guh-T^5>USi2|QmL&1j7qy8sDtNe0kg@0qH!vAdO=l(y0mieC! z-RFNM^do;y=q~@SLwERJ3N7(J7h2@64lVFMADZufF_iEBW60q@7rNR1RcJOX3RkDn zBH_vR4-Z}ES3}qOPlc}bE1}8$&qEXaU7;)dq0kur7opMqe}+c*KM4)Ybsh-^n4~s!7#?WnJRXkY4zkUcJAS@29#&N$9#B?DZ%0Z z(-1A^4;t1|8S*x29e4p@^O30s^hew&)(@AZVkvg=wHPqYv0bd_CT;;+mWAp+fr#?_ zLe9sLVYmY@Co)~?ora!*k1P8+7gxaZ+JY50s5~VnOl`v&rY-jaU`5MCc-@=`PvKu1 znu4rVg%t|E3Qc4HrG|5WR+yr|q%l)JA%GGH#NJ^-?MT-Dc%kkDCC50zc066S+@-5| zNQ55*7;5I@O$@!do_)uMFG$Q%@7KBrlW)XnU{Q^6g4hI*>m;5qfO@`yNLE$1r_ml7 zLt`$0*|Y^#7^UG-fI~$6E~<;3?BHgKGiTJ-0p<*ssKbE{xZPT1mlx&Q4!~SYaD@qH zm-B5cEA!!q8wZIwflzj|900?DM%(tFCfJ0N+NQFv7ujts_;k;T(S&MiaxpjwwgW5U z@pXO+fs)TN8XoC_`a2qP4pz8HpaqGcRW2fgR%_^WRd*I+oGT}P)A+Gh6`vp%K}(%O zU!VuTMTcEnRj6|(TR-H0??#nut{QW)?kKGITv}yUX*bI8gKyWY5uTS=BA%CF$mi-; zl)(At&nX(?7mwqKk9mw?bgIo%Yz!@E{#}L^lS5Xp>u_)fFA*kmmMzc&UpzkKi%3Nw zKFBj8tS?PL$xgysO?Z47+8{jz0Y_@IRB(jkwbV8}tGN6V&G@ z_)z^71rfELf-Pz_1-Y(6m zHH(6s>P!mWRzNoKZ(m@K4o1!8-L4yjWv#{0hMqT$!E{ zvy2{7{roX%8rZ;rQM>|AL%II^0j|FfjDD0_4{(F($4G|J#E#JS2L^6G(m>GYNYlZG ze9*yS!Dr~K??X5hFNgR2Kh!+-s ztnzNpM_E;2&+)Pn^`F?_)Ahs?RB-NZ3&q|EE8UT_x}}ffpktK<;$5u{i`JkpzUT83 zn+FIJ1|5Uh`$f``$WxuH3M1C^Lp+uv-!@&k5L(yjfV8qBQ|!gIYCaCIF5z|UZuecp z0jQ7Dd?;6Z&X2_l!-dCRqpI#51I83`jE`nc++ngnR=9{gUVF)bhPY`MU6f zBdL#j#S7`4)nYTw{7ur_Z12O=)_FoxCu{3s{Gx*^2elJW*+m6V|L|%oC(2ZudJrRz zq<`@wB5%y!)j%!+tV%96v|Q|9)aQH!09$ztHDF<8(<`WwTH*PANE%eFnuBzoa~);< zKFgZjv`)(^)GbA9P-e7|cueBO=At&AvroKmo$xquuK+hP!l?Ex;0EqPe0S(=d!-A4 zxBxgr#_1QoQzTx9=TbX3azkdJ?h@e@<%Qb?%6m0CP!fBuh2tcCA1voS;lwTa;wKiv z0cYT2Q$owg#lO0JQ5oc9BF{X&kNW*{v@uw~kj=XWI7#yt3=QEU*hH$Qv0!vr0OC;>niVWyyU}kgNT7l=JZ723;ZuMmT3flIewRAMprNKv zv`X>a9cP1&3CNhtr6Z`KcbwLPV&CwU<5^Ez7P2Cq`8C6z^?)m#8BOHx#6WNKHN%c- zdQI?YiOlrXV&A5H%OJsyKUNuaZk&%iMRN%Kk9z5g$>Tw$1!pR$7&5Uhm9135lXM~G zTUae#xJqD4q;IXI|8%7Kf<=EOQca-44UDMS_{BK(qAtJ|c0+u^Ds)qT1|9J4(6Dw3-E?m@f>JP>SPKZjr6+9Su& zGdhZ;mDab`+kq3%3(ghD@Kj56-z^!4JlpES-0hE4_xDFgk;=Cv6_Sf$|L}?>c%i50 z4=Zl88_d5eS@BH7nu}9M~GHg zcmz%e@K>T-$3GT(4He&)PDD>f8yevF*$O6fiE@aSfJ^A){3Xi83RXU{SN?YKRp8H1EeZACjjsLqP~4Qx++swV+?;T>}p{yTFpd5w@gvDu3i9>VvUQ zS>!EnFps`i=({cAUDJP!bingP>s(SdN=Bh^l4+sxApdTT7u^tQ@epy{{D(O5hBazA z3}SNlRChm**SL_5|XMvXimcl)Z~{|L^p%i!*dFnd=LUF8^!M8S^} zXLK#1LgN(g#Watx0GHgZIlqn5l_?Qa2r5L2ULtb1rs6!KOi>3?>GH~T^mR)8U?7z! zfa{iLDfRt<)P=x)*FSMEQl`8*ka~{tuEoh_nX+~u^#rBXK252Q45ZSjf$N4>DD~c0 zYTe7=B1s^wD(srY26kTTU2Ox+xV&}FB}&GFv98o0afu3@19o0p74|Xu>nmO7R5qh5 zV4Ssw;PU|TMgBsxIpSHjOc%PIHf%$(dk1GH27Dm9URm~Is?kiqyBe>c9GChiYlE7I z$9f`NwEu&oim!Z~wFhGpm`t_9pQBLQf!38M5Bv{(fIYZSy8#I}(03{yR0fmY=Nf->d$C|ukyr2Y{{sds(`#w<_q-lD^G zI_B)M5nu+DI^Og-#AY);Jqz%PSE8Ok8UX^{))pc)R1nNthlc2ul!d;BKKy%%)(3ZD ziL&WO(e~Hb0Y#y)TnSCk@|P`%%%WB+uRctf%u*{E2^DII#FE%4(P03I>76Z6PVip) ziq}O}q`=2u<5H@|{BARf9Xh|TsS&o<-tNWcPNkLuPznYu@dl`I{X}7|F|4Ec8 z_WnSqvmlGBeG}e~WbcibQ$pQu@R~Zc79rej&Rx&o0twRQ6nR04yd+f&T*8$w_)hY} zwnW*znldCuR;DQ9w7dlt4HO@+cP4^EWTi#fH%(8Fz_zH0{N)rFBP+~G*NeP@0%S8s zR>1GwvtUe+P0lw%Z()gY4J*XhI$SToGHLZLq9%;w$#X42<3h7D@}Oyv^6C%SM?~%* z9;eR6I$PqJyqOUr@BQR5>17soQ~Y|Ia)L;?5GNR24_bl8AK($Tk{?Ug!_(`IKe4nt z%6=chveOn?OUZ|Y7hjjsU6S@Cn6+M~QYB5xxfYU>vQ4dGbQ>4SbALxe^ z*N{Ei4S@hd4zA75LB45WYfePxwdvf&gz5{G-|{X>f$BhnNaMlC4hrGI3EhZH=%}@-Qs)8JDasrx(nDM?zMPU}kHbeN+a(n0heOAv|WkgR|i zQ&I<0@scB|V1f6LC#o_5?e@IDKq|Frk{}>SZx(3N)iY{Y#uDXjmcs7nz945Nf%1wt*nl)^4C!L@vclvO03v_7*u}2 zFs=Lx-&TH^Ya5k6Un~D+hT%~du>NC)X#Ll({Ko!^-AT$Ao~xWe=?eLwWVtXUq0epJ zPM2GLh?}3n?)f3hb_{k@^%p82V&FLVFY*xCgQd~7f5lX5B-(SF3z}Tdfmu-AyqkJN zV-z6|AKdo}?zSOlvYlp>kQ4UnHAP|w-G;_To8N#HGbgJYF1?ypQNa?$amDiJZ76-`J0*6#PemGf({c`-N?tTru@Xnb>n7JYI%Q&4oo!EfS zaqR3OizC>28PQR|AQ(@t>Cf7%k|PxItpNGKa>lncAYGg@6FG3^CE`KqOE@iT9Tol} zPUpGSJEspJ4p#2NF2yrmXdE{m70w=KM1GIScBP3bo+FRzLN4o>k>gI$SGn>yg`O{r z9M?_HGug-A6BB1fE!%y>reY9h=&Z@#vj%4i6T1=VK3F@i%Skp@%<^mWp@IdH(Pwd+5aj z7V`~w5E|pD&3~XKORTrScoSaR(cWOZ88Kwi1?!v`CqxjVBL%Wo5M)Xv%$5OTADJzK zI?R~J>ntBQD8wiq34<|P26Ydx*y}o1C#{2+_jYWz7zq<vE92L^*f|&;A#d{j5++jEI(<+U&g<*=e(y zkL*j7m$dA$(m%p0E&J`1ol5^3(s=3a%S-?BKz3q~S?Rxzm7aHb>4bP%=>#H_?ylq| zPaImZd#RR<;D=J(MZCRde=r0Z?pazk0xp&I9bVc#Et`))VXu3%mW?2fMW6Odb@Yna ziKqh)gc$5UG+SGY^sXWQ4tIV1^;`oSDt-Uu!{m7mXS1__ZcbwG66s?Z{g@S<4F{bqpAtylZ)>VGN|Sli~n4gHa*=jhFjg zEN`Bd75WAec%i$sjJ(jN2Qt>O5^G{5t{+sQ`{%soym|uW4|z#z29@M4)-pUvgJS1x zx@&L=)3po@RKgTq!W=C_ahGkcd!&}(8Eqiq&6srb7mT+cM&>kv@kxlW!HG{sjDg&> zg@b(T^$g?^lzQD`mRZQZ$v?^%P+p>vTsLC#M?({ut__WQJmqpKTX`;4{YzAp>*lw4 z*5+7NR-jXPQY(;};kua^S`_%(%X8hQ<)S7A<5TcUf3AOFEvNj;V)?IiD#c-HA}t;h zq@YvzIfN+i_!50oro6y*WJYlgE5m&y6fCa?+2I9xLs#LPWEZUiYysw!iE84MiDIr# zG)mHg6Bb5E02*+sPNiiD2Mtj(*QZ#{sb%e#=US)bB1)&VLa3#^fU848^cP~ET#KFM zMHJ*z6wF~zbM)zZu1o0j66J6#<*vvqS{x#?Xvv7oqK+v)U_P77A&2Wzt<-{OsoP2c zWFfDOMrxrliZ{pk4Q2fG9My?EkR1;F^iMM=a+@Soe*x{Ta|2hffBy~Zwd+aZ#?j%E z*A3tdJi7zj5nplF5&&qSBF>|Ca6{B3Y^H&T1fq6JsxRtMOT{!+S&1tXCl1OSKIXx^ zO~kzf^Io$cP`&thm*kyYo4}PYK_KORUgyx9fE8%E{R`G^Jj{`TXdm4(ME=5?ei#R= zSn29|ny6$UPt!DejX75r@}RSsx2a6IAGty!T<3^SeVswe==%NdscLp&_T|(l8YFTw zcy}=ah!lif8qggy+|PrzDR4P9)2B<6t+DT}_Z36o%J0N*EK=?%WFQi&{HB$|#HzOw zDn<`2Aciv}1OxZU8))o>&G9t&(8GNx4a4tL7OiF7ekm1C@*KiQYxA@@gmdh!WWsKt zu$S`~(qTAYzLE-=deOQYvS>;bm}lUc#)9nTm{FqK$CHU4D=AZMADoHQtVk;u>2{vJ zftn-FqKd(E;%@k~R>xV9wF0I?q~a!b5%cQ~Z*sda#Hf=!;zp@Np)p{MU`6zmEK*+4 zQi_{!5Kks(wd< zV*$?!XD?ZKO_h7duNPFE*F&>8GK)Ao4s@a_PM-OEyPh+;NdVvt)7s=~3oaLPo8c_!&3ysj)JgslV`3y8Ij=|e4Zfnsbn5U@zREJWK zGa(%K%vAjuPHc1uy*ROwV5z4a5(0)&1%9{Y3UxS~il|4iNeRXi2 zNX143K8AMzIPxe4>0)@6K0W|xI6P#KR*3BuMjJ6Q8h#^zQ;fMMNL=;0nQK%C79-bZp zOcW|UGu`)LWOu=53(POUHbL`TF>jYK3mS6-pN)jx@Z+{01x$M|ozHG)!^_1@0(=}p z4yUbh@A<<9LPF0*gJ&IJw#xvY-k2W1Bv1Rm{)=fXhjG0%4dSE#S@HAw(^(FqkaIu= zj&;l97>>auuyiD#BuhIv?Cdevq5px;0Xz`|BhgvP%^MGW{NH2w|RkWeJLAT(Zp zni3piL!zghoTZ-{1igI;ZG&JZd!Q8*mW)qe6zsOVCVsc4lF~ComBe!h(UoQcv|Tai zq!Q02$v}57U-nU#rhtNNU`-FevrJxl;EQDG44)wRtnGbJl$4G}yRl!7hQk)YF(A0m zcnOvf^d~VnfMLfl{AhP8X2xgHnbC_b1U@pM7c}4?4q=)c`_N`SGajO`E<{^Fec)KN z>OH{S_bjFQd`|pAn&s_?$B&)uYZ1>TnXO<;T*MB!80W;8ENc9sg^^h_X(F>|H#c-y zzl0PQ*~?Uk(;v>^1!AVJ~M7TbqLMY>SLE9MeAA ztk(8Kk0tqjT`(#H7nHzAVV&KT` zgCiMKm}|I;ec1bxmK=$b)h!@8;V}y$$w~$8JwA+e7>g9n5~i`cfl$gI+@L6~I-v4o zhSUx|A=kk}2|DzomDiu;B%Rqd$O~6uU~zE)TnJuH&%Hb+=y@+F^C2jkk&17hA#b0s zxx5?1yck}we*+e2pb=7EKm{2m)mM`=LVq6A;a1P*>b*o6s3(swvH+i?Ae>l6EB z>Oz-b!Yi*+v+EWuG1ec_g`obx9s}^-bG-5sjd;RCDl8$$=xAc$!1b_1Z z1BQd_Ux0MNG5aZAqJ-6gG;t?NkbknB_pJdHy9l(N=lLY`G;85ej4~DzU*UAEX{5T( zpuNjy{0m-!OXvzEYYn9KiFsW{26NitqK(oSd_T>ZE;BAS%W10o0dIC?2ZR8LuCpdG z<8q!Z18f77hok+4{Reo+El7i%i~WpVwHb#^ZALMMWNwnr zIUMIn^V;RPX8A#je3?2Iwp+Ta zQj9oGyMepPFrSfG<^blyv1tr%K~&%={kRcFW71x!MW}TV-j;?LQx=&;dj zho9n@i-W)NZs}+xT;hhm8FO2a6Bhi4b7?()7>eDSJ@9MSh0{H7AqI2@+`?wGBu_(& zDEJ$sPR53`aPDsAFomsSRFaXeV+O3g#%<8;Hk5Z2*!n#CRU6?p_4H4K+hAr$LD{2# zE0Fg1j6RMrtkYu}8XT;%FnzR>b8*y>&Pr9DLyQ1cp;Bm6gAqA@8n{fn6XCK1xAdE# zS_~P+@?Gdctg(nN6B?bYwa{jdpuw}L74Y4CS%dT1vLL>5r=IcD&|ojH^?F)V-0$(W zpGGrdBcz0`h}MDjDn{yEEr~a8AoP$rrWL0124@w*Wo@`}6?#$m_wyZwV&Hc1Ap_PZ zGW7^~6HJEa%;P2U5>8JmOp{JZdodR~0mrODqt8BF$mx_$qveqsq>GpY%7dtIa7Xlg zl5tDE?X2fr^*%J4mxr1K5t}a{KdVkFpNd&NUBNNTHy6>VUW0GGHXEYy8Dh5?vtiK2 zU}{*N!}cBLrv`+l^3FnUrx#xFN|mV}2>G2-cp4Vb`~c@BR>H;cS)FGpm>1jrl|~48 zrbvg+cR>a?2=93R)b}RtS>#l9W&zL7&^}@(Zi?htwBNCvU0F%YTCx7rV4IE1(Ti1 z*}PZ<+`MN)jxUFme^PiUSowEv_GDH*8o}C&17B3)32TB4!uDa<qb zAJP`T<%uSGjB{cy!|Fkv`T{+Y3{K@CEeCrxIu+NTEOE#Z?^H^oS?C!xE=nM0(u_pRE`hIG7L$AQ`sHO zLeCc5Ne{|GolJBpuSB!Z^H`_y+@LHbBqcc&F`9**$2pbCL0J-zbOns6M6)D7WHGUU zz*-!x&lN`Gh!@T7RAGDW1E&~6f&XRvbN(O4 zpT^)#UPyHGF;MGx3fj&P07Ll%PvN7F(Oxx>BZ?3|iDseE2F{cX%0fxNs@!N6j5ey7 zH7Ey-KSuKEXbx?##|+A%;X}m1`drTj8z}Q7f2PK=B#h+oXcld-_YKOT;ls9Q78-1z z%pV73iH?6PslFgO{yfRVo@x9CWr>b|G|PV;f1c}G$A9qs74El98`5bZF};JeDyEvs zaD;BPCtLUbbpO55@CUHFGB-G?FDwU7;Q1sNKOG-Ye9$E~$0zTc47`FagFKG%RA2Z7 zo>(1^mdF|EA5n{7{b`&3qoVB+S| z5c1c6ZRVGgtw(-(g!OL^3=z$(?&JMyji*lNx4AD3nlH5L)_LXkObyt^NEfO}xOz>( znU2q;e(^KCoc(_NU*mK)yl0~7tw*HyOiv=87%+NTWj{r?wf7Awt)U@S9)0j1%FDpn z7%T5R^+SOZwbJ{D*ZYW>o>ht|C@*7l*?DO)d=DC1F;)T&r0I5SkF0OK!q zo7fd7Ml4Y>rUi4;jC2LcZ$SC=XSM(z>+uSHGwy)Fi}-HD>?{^W%*kTs5W9!PdJ*%o z*rixZhixQJGsa>j#2Q#Wb1Y^-Y&}a$ip5e9^Ru+HSXz24Ha!;0h{a~cVwtg6b}VK` zteKV9MXkrU1mlk*Mq60EQ$+-(tNj3UB`{29YqEu(lX>J$RjOPpHsCxQbK2~G&8k2+uZ#*&g{iOMy+bU6=* z94B^}W$$~KdDz(Ftwo9FZ(|+mqEBPOm;bpZAXQpVz$>$!Z2dmH;l5;!=^PrmtG*T$`+W|WY@-t}uC0|~r(}^DBusUZBmQ@+3kHly5w0i#A3W9l; zbgXNvnSm}7tc2p|Dq7zM-=jW$Q+;G!D4lstM<4(D+J@rEIgTIPaXK2h6Ophk0c&Vc-925T*h{Sc%__mZ%l=f7epj(PE_?oz6y{ zgB|c@Gzeygpxf@6`aEu~!daBT$kHJl!p;_U898PmKQs#<&o&oUE?m=ogX{s8-5h&Q z_6Se)iruN)%YxJ?Hjk7}iyb(t-!TFJw*@Ec;R10LnNXu?z%*{EN4kMY%HYz?v=GWk zPW2W5`LY*uGsx7ZWjKmoo)yH2#$-m#$&O(UP!Z0ZLVw-r_@x@XUS#kDZ&mhl~5xoE2JgKi_?-p-KJg? zPw4a)&>N{UA&dj?#-(Y)Yd>@gmqCj?*57Vat3m6d??|=)fK{1t+g{jXA4SKSbktZo zgI?kAu`UAmGvq3>3dW|4B!l6<*oxU61g0;=F5(9g^8-FuD^bNNa_8}yMtMsO&> zf!+b-39Y79N*0eGL*y-jd>O8yBX8v*ru>9wF|c$k3k?X*QbbvDqgg0j%R-~Yv(TN2 za#b`7rE6JUi)EopU!{K)>$Tx6O4qV%ie({=SotuTh0?VwbZ^WnYDP>o;B;k3XVF!_ zo;v#i|GN6}&PKZm@uOAI3J3^V1=K0ZLS03cvS^meI!jrotH?4ln&q<2QWokevLr^c zT-I62LS03cFCNz5aam_63w5<@iFyTyFIznD1Nt;%3H>jgxqNOAWnnR(pTRcK{)_Mb z_xfVHFLu5I@;K)ZPMaXYMNE*jC)#!jH?-r8ob8XSG`ao(%?;&$ZlwN``j*-0O2=WY zvXG}hy4>}nSMfYI`uu<^AE&;GHTs<7%7vDPa#i#>#idY*iP7hok(C*)iT&P(B^rH16dR~RSzR-B(@v98F)_D5WyH+zj zkiX%wnU2^X6y$Nb4npp}OxZRlE!$NJS)VfH^wdh!e|?)tkzsZ{|GXGyZ{ISLxF>Q)zEm&wH>WoAnk!UJd#drXvlZ?vPTPz z*_mQwl=}?itcQD7n<67TBg9Cm`(rVZ;Qj~h#dc3Ui|=+y`y1dGs4>M-pOPV6%y-!O zE4~cF=D8g#9E~fLs2U528nOHv4V{qc3)RRKVYCfb9lD6jAjs!dzkJC>=x>=m_d6=oZ;6PlajoFITdG^ISs!|O5c?h$SVw4|{tKDqQMXeC z^*Yr45=GVTpdjmq;v9OwL?BxzzL{jl3gRvS)PnH=KP7oioj(DI32P+Ep4pNO41RrKL)7Y7@%rA1AvNn@*%NNF?T4S?uq8`q(ccElFOZN z3S*E*5p<|B{CTtWRqiM3w$6&LQ~`~qQnVrI)e3Ve$O(VCB|ssi;o0K!FpRQCLgbsN~sYs57d!86GwUZ`@9hX;t--=hTa!U&=ESw!kB z1@=%^E3L5vM6jErUj^i{@|N;nC?(;#b~cqv5@BY!k;VXs@@Xicx|QmMKn|-PIhFng zSVo-Q3ypEL&1k$(OLH9;jkf)QV?XY~9Jp{8Q~{|`P9&J)E=Z|Q%E;&6&ZTU%&7LlG zJU)j64nxEjwc_&>HcdBByV0oc3@T6dQ~Ma2I+b7W{B;oY_k7wKUxe&>YmasJJE#zo zTiJnt=KNy3@Xn#vI(LZ%ygj^7^h4~O?WVrl9HG+9*A#QSh$gnQInp)pasFx!0^PE^iHt@#5D^gbGy?OPG?oX=uiT48L>k3e1u z1cosft?`k6;S9-D5Ap}~nF}|;LYHO3WN!Q60k=$62J(>O+nJ|F{W=!hGK+_T*Vd+0P=f(>f-QM7w*R&jX65u6VG zZ!Lxp2ZqzyTwnU7_rf&Xi0IqkOiC$HeuSaGw17B`l#?56#I3PLl)}`hUiv_291F8F zGYK7xIrdOEtNaj}a?tw-fv{z22R!qzrX0+%dmjNZ-KqBgMb#UDqqt?kZ6z-0=i;zB z^i$SpkOFBgBs5OJJn@Tc&7uvlY|d z@R8iWfPj%~ugDRrx^(WbY$^0M{g8&z^L-jd+^2VGW9wc6(2=d*F4%BTZvHpxleg;2 zX;Mz1@JvpTYbLgY5O*wBB9%@Bnn9SLPy%urq#QdifW>ZKEOkcvST_qrgi^h$M*^I+ z?cn4v_lzTH&W6A=oKfFL5^4Idg90e~M5ohuFe#W}&(9n(oucIWS5Gf8=8QD0 zKE{2#H#>8V=P2gm0b~U&YYXk;N;uqa3sb)tL=o@>gasRo5I1m}#NH8ZLw+cT7_d%;{s_Jn{UiOeWk%QUAk@1=853=c`^xR0{4w32 zSuQ{1V3lX5v2sJ#3S0BX%-IT%ZMWmceTCS2vpW&$-JapwQ;`sgXJ13lZ3uVG_7gtke`p&;7-i}l5sR1 zujvgzMh>lHeNvx7493m%o}b?*K04-R&r0-dw%B`%@Yq?rL0Y!X{jjig0gP$485X3= zxucWyVUm4g=W&NzC*t#mSF5<^Ag%@N}UX%($0ZbSRB0h4;IbW7+J~ zPTy#ujGaU8Lj%DlX~)}L^hC0pRZ1u$HX}fV<;4>qRdF1F8Im91fpBbS7q+(BF3=T> zZc^!?%AbRE6Sj5+{%JIncgiy-Ag@t6FP=|GIO4vZA|^!M7JG&#Fz*cTwssRQH7vU) zm_u7))j`5gtFMjMw=eX|ftQG(`cMrfVFF|*o59@VwTa>6k$L+(i|&J+2zEgx-ttiHcK(*<)d21ZML0PdHI|7o(sCg!;n2XE`=wtFU!M~>wMW0sjIm*5E+wa9!Zj=*yJLcQ92>E z!ZI8cSgvH1n3U&(M?9lcvGms>AJ^{ zM$ci5hC-`~WAup2iepS5;3GJ29s*5957_)Be<>Q#DpAV$?E&W3HL>~qCNALE{Km!C zNoWJo3S?BDM=HEQ;GFI(V)u!UjJa8;JBKKI&|=$UXw?I^Z@m#_Tf{!#`s;WDFBrns zoJ=rrq#?MG^5#I`aFd(^DMY`VlPcxCtp~n#5^`Z()i5_SlC|8BcYMD41~F6b`AU8$ zLoS?2Qex$uwv*Bcp@8H8Z4iYmI4R_wfR8NnNhrW*VRc{yzJSPug`1Gf*-ru#cbL^vO8?>ye($mp!a+P=`;9E zk&AAy$ct}CLUpie0=Ph$Q0fqmMk?Ncs2bD_h*iH#oUmc8zSRcVBt||VPa|f}(0f7{ zZuh5nl2^~L$V+D=Rg+MSS2`vhi&X4~87Ar2q|#Fr*AXFyBz~?X-9W;ZkX6>6r;p7$ zl~vUy9V5%*ain7jIr7yR>0_5>Ob5P0*lYrX2#;4{B3)Jj3_yYTS{yfhZ0RXm6-qol z;h0cU26(9NU`6Nfs0!PTz$1nOHvSI*A8n@v#}OeH38z7F0CN;!SZzojTV$9{qS-b#0Tm+Fg3DgpA=5$6>48VMN<1!ZDg9MwgC@Q z4^KG2Eb}ET0hj@7<;2~L^2)_&l0G;WS*VNYRAM2-L7F8rVLGrkUdoUjG6LIf1PK>= zGv&Mtp@ta`*?Max{K(K|jVxh8o)E*sQ18?hJ=E@_(v&36*kvcbHBSZ5PPeEYq&{yj96M_rmk#o!Gz4=X9Hrhr`vL8r%x)HohH<* zq$U)n8782e(3)`l9k3n6n!R@2M2^$+E%V%0i>tz*PsFjU z$(kYc??5CnX%(%AM|5IoyDoH8>KzGJ8?}E%F;Z2h&-iaD08ZGP%Auufxxv;}gFZh` zpjviOCYAIJ1_l8$e=Ig2iB7OjW>Rk&Ll{lrH$`PoxLb`P-804-ASgcOLi<-1_k4ehiL%Q8Eyx56d!|`MZ z=I%+Njzpk=1N;I5*Je9VX%n_q0&~tADuFqmsmguC5rU#h$K<7;scjlbJqipG#9;s^ zcA{JJx+l1xi88Ca?lMu|y?{Ymvx|w>#jm6Qm{EsZ| zqWH)+SbQnPGjXJhJWh&h%VsXc@5Ui4(laTZh%;iur&FAGO~g|vPJAZfNr*>BD9Z0y zw8a(L^-6y_Lunx1_$Jx~MAz6l+TSwMyr9e8hiFy(6;kkUpZL%i19#QPZwg69KT7%T z6IWd~MR@E2p(N}}*%>+H2lx0?*y@~qA03uep(tDo^+0S6a=+2P9ARsVZD-}3bYi`= z;5vwQ8VXXx?BROPCt^SkzJ)kZnD+o+Sy7?=P1NHkZw>jdgOeG;5?^g$Y*SMqwxU`F z&&klIfsYJuTuru6oJ!9RSY}uh0fWs;^y|?)cMBo7CM3{}mg+(euqM)FL^2i>pZTwp z3Iv9uGbw3gFqdq*qQ{^mOJ~5Eh(#VFEgfMB=8`>pp6;HJ#M9j)fOR7n*4c5yqE!sc zBmaluo0Of}vK-w%Z&;4*XMl7u%56=u|$9^-k$qhm{96#r5&z#0mcDOKR# zwEf#q1biOdRM+9hy9WEWHTQ{Yh@T@lc`^V~kSyPpBF#5Sad>g#O~PX)MsSCsoUpYF zYsNwMBO0#7B)561G<+)svSGRd*)ZLKY|2n%qe4Q5F2^)iN(|FdIi}Hs`xZ>&y3v3? zFpY}iX!cj@hcU4phHasW!O)`hp5a^9V0=3?7~huOlq@g432e7TzC~Irn5CtHi5Bvu zH>D$eI?0LLC@mhHBrP3n0kU;b-C$7B?>%HRvjwmewys3q&l*;u?>Fgj8lH=9i*A~p zm3vd_q@tU!7`}vDkg3H{Gnj0=GU@vw?weA9Yloc5!{r*TDSGg@Lft&VCwRQI$adTj zoI-XBO2qy#4Ek?_Xu+vTP?M>-nzErLY@(^MvCyI_J#ND>^(m=k!X814qxYr9r%PFj1OoG%UU;d2H@YDTqpmgn){Aj3eMfgAni+jR*=&&KNQum%b?% zH_G`b($1-Sec2=6rXxQU7ZgIyF{Y{vf*)R@e4ke$=e>zCq~UVjc9@)!i(91IFyBkt zz?p77BeqTnd|?njy(FznmdcW(f)wetRKGq{P2Ir!8G;~K=5RG}1;z&>0}B4-*Uw{M zP6y;$Q}oKV_P~H%p}yH?Y=#s7(`puqdt+r{x%kAyx`mEUQNF@G5cpotmA66vXAJww zslbyO;X%j<)jDSANNSCJFZNIJtQM>g=5MWTwR{)N>KMcw;J(Ca3s2Zn(X0H9m+XEJ zmdkqY^aQOqMMNozsMR>Ky8jkfGWD&{SA?uRBvSvhuW$z$q%?0o2h@Obn0=5;@fEg; zeLWRF@tJq{Ru{I1-VyspxyOqAYXoLZgc-|G>561KiMFV?VvKC>#*gZt*jb8YQtT^= zVLKYx-a|1^t;qJ%6q`t~FvTqR5rh90BV$%xxBUzr;OM0rP1yb=y*&f`+x`W`6pH5nuB<(M?LDWcH4NYvKeul<25eP!&CW1*QzKLLr!@VfIP9s8sD_1VoQ)sL& z4z*%sFH!VYF=T*sh7vl;eU2?jI5Ic48Z)_IjSmOxrQpjTszmqcAoL}!^*D@F(#O-M zrUke_jDn{@>W>QJTw-~R9Kq=nT0?MYr`T)}dnXEyldA&=DD6U5;DNozGdUD!06&R? zyRd!kLMNAg+IaK*up{`+59b0bUZ-{GIK=G0W3Frvtj?Gt{f-N>U4mS;BssV|Ev`b zb4B!IF*D+sK>A|1JwFY{Ss<@$g2TG z^3go(2IpAqeQv?O4hO3W+c_#j3gKsEra6(mTJfS zXwmRUy?J<{t<`h3KIJB@v4=1S?Sls4+xqYg&>-R5ARt_ zjk%yiDdFv!8Yb7OcC_}tuDIgm*j@%+2iF5R=d;s2TSw)unJUNHZ+&m1_xWfIGhN9L zO{k?E);A%WN^F3Zr{~-9e1sSOxw=^2caCL!XUcu2SZ`bH@w%@gUO^7bS~7XTwfqYV zIG?7~>=`>4LcamA!eu;Lv`D>NzY(pPXDe=p7eOZVTxiQXgiD^{Q!XDJc-zUj?Cz3k2s-uY*EHL=duvgfaL{%1T}tn+lstlH5gZoF~d#p5)_GDhj^bh$VgGe_26Gne$o;KG2RfdyPtp;2nbxyZd@&|+QIvHniH{hvAS~yf%R*{!0<1Q4RwiblsNf_VI97EEA{`*<;cN9& zV!*~t$rhiQ&{k=-RVg^l6hyi<0(+>Zf6P=4-I}Igks~wt%E(+o$F$mJp)LW`oM+?~ zb;zI%SUz+#vfLQSP`a2-6U#~r0RaG~J*Fngrw`Cx3Vuuh`#FTbu%pR`1fvQA0~i*~ zu`BPKI+5~Vm`9rK!>bg znuEdIAm6P5>GU{&qs9FSxJQ%YtP67j)0`seMqHx8Xw9Z?=ivqvP)$zB-j)*wEJ4x3+-!d{k32xo7#=vAbKMmUL3_~^OH8ccs>xr?@Wi2qpuycvILJ2*_vkWd%sQo#H z_G{%%VdWO`a_#h7KzUrl~F4%T5U`%MIIeUWHTmdh{$Or8Q*nS3wib>xn(Qb|S$_FWN zFB?&^L^(B*uL&eI&Ur~O~ci-w4dDKC^+xO$>KA+a8~n7v#HB-wG29g zO^d+xV(hY9sO#C53SbI(`*E-#jhG4a;AbweE=AjyOkJ>(?lzrD4*Wsq_XDW4Z#*^` zY9nw343j4234PM(icg^c3lVOdzrT(zwZ$%a_firo03<>#?wW??dX7)>oWf>uEKY|v zwdaG#G6(M(qUoa{wW=wiyy^Fr`>AoTAhDS24TlAgRny5497fQ zZfm>eD>}i7<%^a{@Fw%AkTYA%J_rpx@bKj(w>$9Cwjj_2zk(CVF>brJFJZ-}6x-qL zi(hF*p4}mCKDB{TU!>GMNd04I7pjDi$H0b~)d%w)K2IBKLhR7&6o*1$a#eU-2pv%+cRGRsovp$H&kDSUD?GaXb=(y_-+GE~%Jb&U; zo{B!>5=|>ttA(A6$Mi+C_Gqo^;w{ix&c>VY4$LoW&)ish(146+4QF`^Qlro3cnd7i z=db=hv>*zR9Tx^c`T)7o;0P6 zrJZO{AO7jVzu?NQx}{lvKY0V!w+mZ$2h_Nq?tTPILqJW$87NPg+J>6W-M>3qX!M-b z?~ngW4K=FxtDo++KvPD)f17uqUU&W@eHd$AyzYxTKNAn`CldpM5eoREWbVe1##?)_ zit3do|EALoU|Uq48Tu>L-I?fyTy6nP^10JYE*dW~#uk~+h+FLWOa2_pO*8-G(}ZOR zO@+JPKZ7u-?#-XPi_ntw>YiGJDHUgbo{cbhTJJOc2-9YNb=%7bQ%~P`<=qIUuURyD zA;R>Inos|PaORV)_}?MSnDW5Q(-3CAaa5dwF!TQR=UzmZJK=9#cOtYueAT04>pqlg z)2NL??Qm*1)cd`7ni{m76huPKAb*XR&X;R+mBDQH6y9{GwiH4~1_y6xMUpwKy2Xn; zwE9yz(zdMu-N`CSGqMf;Bx9PmIL)cU&tjbq>NUQ_X)c8X`t|)&FIHcy#>W+RMd^i< zk|yV(n2Yji6b_kTDjTNLE-T#@U}aC}#rGPGZYh9&&l%OAFI1m4e3Y73M?7Ali?fz&(H zPO})DzzKGmS$Z<<93G|Gv@lP_WnWqnQfbvkYA;WPv_e`6QfV`ZR9sC`3Ah4GOGB!e zrm& zgW-;D>3sDOnlE6_t?kx}Y>hSv8+T$WbV`26B)#8D#iQ_t%+mX0pbHkc5_{z;KXLT_ zL|jWBvbMsEQ7^3YY&>m&F*9LP7}jQ_x(u`gY%G_ts!|dUP!$)fJ8^}-u~RQG8^OZH z4{(_Lyj;iHdV4xdy6cb0h0`UNDnf;=iNv_W*uGSU)0c?8MJjz;o|`1q(Yak>P^#J_ zFDSD1$tw!2hpSK1G_CHTNlN{r$&QVS6dp&@U&?E?et`XeH3-l18!w=TCr#LdV8hmxc~ z;&~a&O@69WXQn>x1luXqS+owmCbh})7Nd*Hoz@f8r-|}Z_YmEY+Tw|P;iMl1&khnr zlS*-c8n)tutKHg8OiK%T*$WW>+~i{@)1~)yRQ^Tzq3P25MrwQBYl*#v@^-1bmd5cl zYpeBWw2Muwi^b1iuK{y!*l?7$CDN6y6{geLt#IVX``5tx*B`d6w8dYbHq9#&9T*z_TWi0yPKoY z0cE_PRME>gIHChIZJ|v7lkj4Jf)LsYR8e;(j0WhGHzoh??^*ktgtp$fpL_p&K4kB+ z_u6Yc>sinHde&OiYYN@X9l-K3!&-8{H<%)HcumPgZ?$Ak;s_*sCcbueaeV!=#onfS z1a$QtQSKg`tdb*)T4Jwvh*IaE0BCm+CE{x@MLawBgxP&n`b(FAVMM^XF|zoMwh#?IC)#jTAWc zl{?|r&hS=LsLuUWQ!4UhJ*zG15M+ z#G~VE*Hp5fY4rq_>=x+xbbRfuh__+^mBGBCSAW(s ze$8CBuXYKnxSsT+1fph#ze2p8*pK8^H(Gm zcY4{AAIH3MC`BUz3f=cLL z=g(UU=I5>Tw~DnBdnLV~6HajnN-k*gQLB$G9q^uwuRThacE`vQK!$uI>1E6L#;{B?A}*E$tL?}Eb$!rY9I7gSUc5g$iUbuLR#Y; zWvi|EE8&c+0P!1_gRNO@`OZ}@cRgd@*B2vXC%#B_lzP_g^(4BXnXdTSvyu4PPL5pM zP`DaZ2<8r!8qg6?wV~u3dc~jBlIS3%G69S1z=@NfB?uIh>_ZrLdE32y$v0{eU2VNj zmB5HAjC&gomTs-(Nrds-Ajzy;y2Pc#es*GAdm8Ro=>%L$4)qf9L88asqcsQ#5LZj(rhu;!ucCZXRY^UrF|RLM7K!kc``yYATO58YftPQ zpK52@%(bTg_)@HJwd8}}m@eHa=|G=XH*gg_r_qr;LeSx2uMthNFC-OV5~h36uWlE1 z+QD@{l7#MTgX)*X*LEY91d~6X-Ovh~B`jdT(yT;9VRcjKtaT`78!4}Gxj(NhL2Yf4 z?-0nKWH{r`+s7^>N8mXieID_%uh3LAd#;tP7C(ugx0EmzJ59de-3G`XXHH z*mOCOXbmNys&2vC%}}B_v=goXUa^Fl3})2RR*|-MzRP7BcLPH4wj>8aI$HigCOW^D71bI-)KoY|z^8@varB%Jm&x*Pv z*hO(dePLE+qi4@cx5{Yrt5+t@d6)coEe!v>7JvSp2nydJS5^yT@w~rU-vT=7|47W+ zYqG-CogK}a(oL28jUW|_njx&T>N}lBvoVds^vWEj3F``WEEg&U(}K7Kq(nSxjRelZ z)ut=v?I>L#HBmT!Rp|;N8N>n-6^*vm-;@+&M@{ zAX`e!Do{_iV2L5*(P)14TIiHumE^=eGiTYvIdxLR=Hw6g2ta#E(86`(?yWtKtWYDQ z`1)Ci5Y)iD^`*8OY+-W&5726*E26rAl2?mSFi6EalF2=i$(WF6av0mU&k`4e;KR_z zE<(K!vNKX4WZ9?e36&${P6^eV`Mt*AZC3rZ0W-lZNepn(j?9$lb`k3y*?SIg@t`1{ zwV}}IVv(Nk8|jd4IN_T#Nigy&MG-3%{yHh%P$GT_4o48exeK=e%pL>M1Ndo$D@?Yj zHj`~e$dCdYu`+QA;smq1iaq1%`1)?dn^-B2UsNDn3le+LNsbzt_W+b52rW!9W-Bxu z?x$M4v!z?bFNLwGqFpw0gaCv)Pr1o5pprllBN-CM#Ix&@Y;9 zgCsF8<%9HNR-kBlB0N4=vafJcIkh4YOPeR?25EU7o9YZpu-ERz0^eQyn)<+qYzqwm zRO?j7e&idqZt&-|MqpW}Yk5g`q1o=GcJvD}7$!XoeeT=Pb3bYP%dHQA#8<=`1)hTo~btt4xKcQ2p!Rl%`k5{41&P` zNTIEOfi!V$cOP(OB5QJS_6LL9)$;Nbj*|K&YL&0HV(0E zW^E}1q18J|4Z(%x$dxwBX=2lQ=|MF01o{5ZBT-JhJ0Zd<&R-Zvar5z zlj+AN2WwF#%_VnE``S=wy30QHEknM<-BQbC#F9YKR0e zjrK>sVb#g_T5CEMdw|e@Qvt~3gQc@p7Os#vQ)(`+Xf$V?*wx=;I)LDwvqSUN5&qRm zDFJ!YQUe!bg)2;WiCRr~p=d*DebzT)1Z-nLoW+m89XnOb`46%b_ehcViwXKc^AQS3 zAtSKxGX?ibf_sHV2a08waxUp8+$v>Lnw6#@DZz<6^K?vC3suizI}_% z>Y!@EP`d#3JRVOKpBgJjV&Ti}m7m9epOCs(j>Uy2I0Z@I=PSr&p(n(JX+h`>=?z`v zB2QVT1PHD<>tR+K+R2~SRLY?dp?+<1<`!oS$o}5vn^;5|fY?(6$`oSg_fs2*g+MCj zkl?lY%>S#6=9A9hZy&HD}2 zYhaEDirC+DCm)1DW?s5HfmPD!i~<<~7W=ZLSL@N<^9cvFg3O#$oZl`Ns$GGMp+J+9 z96Gv2je$y+Q!&{h|+6FA}FeDxQ_vIur|aOx~3NKW#E$c0hz?x#O#$6WEe` zy)*>%y(n|wMUho|q6ZF^r{h8higO4U)TTlR&c0mZop*d+XovX`C@!6qRxFS>C4@xC za6R~#g?oNnIAW!4H-4)btgx%x{g7JMK$Kzd1h3xWCK~&7B}k8rc<30UELJ>b@)Lm5 z@%3GZt}O~ATqKY{rKxU-V%fH#DX4)ry9@Ic7kkz!4tAM`arz+7OP+T;EKAea*yBxg z3jtQ5Rc9p(!O|YG9W?3A^I(b|jSHR56)r@lcX1<)+ap+I4Vbh#kF`o9 zubLpz9&ziS<+cs9qGHZQJd16}DDXB3C?hI|;uEDf{T8)@uu4};gv$nOk)1`2p|MW% zq#xnuTfhV&g&J_2&O(a!Mi zkqm?^`u+K9WE83}%&M3)cu@!cFGS65(5I;)>x}r)c3u#dG&L3=@+{g&|LB9}MOKasbHZa`Ppauu@s_ zC}b9xu4lV?`BO=r;T*1z8HOTv7MMIOB7)FXS`mAQZ;K2j{26mR_d+X2kZcz%R1AJk z#fTLZZY@{+3aC!tMowQ(2}45&bc=Lt8h8|dg}Fjqo6dy_m=QrJJn!v>85H9|z8;7e zk(n%z@oB#1QPCNr43(`1U96(U>|!2kFh&B1feA{5<$>&oqg4$)0rS$;;{=(xTcGj; zsH7qreW4PmD!!uDK`jXP1&QbBtYdv9;mqGMd8z>SvIt9JnH*@sSlOhJFU`u(7#j1m z(bDY3i?T^W3?2X*dFsSS_0ANozDH1|c9d>1?T?IVlb^H0hTGXHA(>*uicFX1BfUfn zY!0vq(+UL2p2DnR%(0-2alCU*slXoIku4}m8CY9#aUby2O!GHEVBfQrV%7oua0I>>@XgX3+AGy-cLIvm1swft7H58(^ zNEXIjk3ua66Bp5#iXK77`7k$_BvR4MoVYo(-A;=>t6*{A3S*mDCrWK**UcQD*iIyNvjez7 zwJ4>HE)Z}ht;_res!-pdn)=Z{uxMyaBSdZVWlIv;AzE&zEjduQNlu|0tJQL@&9z|W z3nz-{Y(fAqg((s^s9&xt&Xrsu2k9l}U=>h9>FOwd1!kM3W(n=D$K)^vONt%|Dz)FL zKrW(wk4#MPucMgr7)1Maj7j4>{spS$!n(Rm&oe;W%7q!6%FXZ3u6{lhA)V{fY@&%KG(Pd3AJgipoQ?a z-lZFq{zcFwasf=jlzB@!H&U{8ot1jp0!2vER$2XcVo;W2D27ZiR*`bac}Hx{AC@#F zzRn0Cp3|6C*r!m_Air*dwZO2Z5D&-5iy$(^$V&)Fif)=P%n^Kkh$I9wZGbke!*`Md zMS3&W)JjswYI7;)Zzye+f^t4OU_w0@>LD7*2tjo-uSds+OjXDz*h0Rq4 zB_6-2d>)(GEf2`%`S!?S+QV82K}>iWUX~hR;7<_+UMUh~FNqx`+g%jQWV2KgQk6bm zM5Yn@QxgSGu}Vh?u`cbI)r5 zrW-CqqB3bA=-1|be)@CkfWprx}YhiYpY8UCobOY(i#`os0 zF+R)tsKoK$GiJLJ>f)S(Y1)0rUIONl}UgUAB?CVX9LS{2qmlJsgtbEWI(dE znqA$AfRyb!a$-PJK=ayI2w{TN0)fP`5zU~jB?VR^712Jd$&BLOW&cgTH=ciUiaFMr^t`Hp;ZY|?) zsb%@N5GTGABSmL1Y>vx`AB@r+_01!r7qD0lXgw2yPxgaSOC=PQ+Ejpv=QSGgja1~~ zlv~aCv{6THxp{3xS80gpD#9UPRmnYMJ8aK;em%r)iRgvW)!ZkMsPQi(YSI_&KflRI zSCa%7l*lqoRN1Wh5Mi(mzYMgR6<`ThLhBGa;EZ z=mcyF0ycG`l#^OlOMjPcGUZJ?NfzGGTztUIR#`9=#s@u`;2r|tTB}8xDkq~YJf-;4D$-vUp#Z(}hHFa5H zC4L{oYPHL2HLP}+*AFCwR7A|8P>ZKf^Q?V^nn6IYHfdhfiKl%z@nq`16H~@H`^Y3S zfw0*FvX_{+iwblBE=R99n9>FQd;vRN4NoME!VSX#`rp$*(5 z(Y(R*ESL-vmccX8HN+f5n-eHMQSxj~&u%E)WcYyOFknx+fQ#mWPb#u_QYl(U*&wts zFNIDUV4`hZ_$jpkg==y)vx?CuB8U)G8wuk!=BSY(h)b68Z*>SP9sn zP%}Ba*4YS+X;~sOgbZlUW_lH1b!II1#h0<^T2guQlTb(D^KIfgS+S>~49tBQvh#M7 zY%APUp}H_+Z7NQ65mG$t3@^nmFcR^-TpkGij^!S}bnRj~EYTd(t;{J~a-fJ`5Wj?E zTSN^Cy2;;ojGI7C)1#@$?RF2XsKNjM3TAA zSPbK@G+^dk7$UIjP`cPJUrEV6&l0veuW6SvKuvCFBg&~KLd@b|$q^TGGt1UY4J_MT zIq~MmSTw9@$UXVs0AT`heH6hZ*h)$~WUY-%w*zL7Oq~+5%1z=j zS%t)1D~T)bo2iAIYY3gL5ihL?6Bn2b3Pl2w(5GZ@;q$K|0oxy(VOTPVSw?AQJIY}IF(T+y7+5KD#`Uin={ zt(JHpiE7hAstwlym}Q=ZFozSEEiRv!F#%w3NZt>l9tPc7okYdqC#zH}B{7vV4~!v1 ztj^X(*0SWm(!Q6;H3;3>(4R+vTRx)LT2?H93}}=kBo=BlyMQ1;n0-U2RfmGFgJTbjHw{{w$x zewv(j0<9+g5U58Gs0tZbN=q(egf(DZmaY1bH4w&%h!LnFvj!As4PBVQAOUuOf&`%* zV8*uQ1qG630tkH&C79m|`yL9VMvLoH1cB zO`95kyJTv1nTS9@5zmrxMXBvkhPBC8vt@S^9aA^~7Q4>|A3bHzF+CN=ija7uvyc+( zCW9bZ=gkl;AjC=*q2g|X!B>O{iBb|cr)BwT-3a8039wJ(de-GingKA524w(Tq}SaL zHgKW`L;^%92B)onlQjtF!BYmOM$2Z15qd6Z4MxDVG7*8&3S(v%025R{Z(Mv4X~m)k z3{E++5XOr(eXW4rHaG62mKEA^Hk<#-855GmP@@C~;37%2L`VFk zfJ06`!^iE!Xb)JNJ|Ee>7_o(F(%QQUx5`Y(B}fp53KNH!2Vi(s<(>G@fCs~yBtVc5 zc^f-&);r_mYv{^iwHj|K085^@>O3DtFPs80S9Y!D#ZW9KhooYik(8Ayi*n7ovXJZi zR@ou)i!iKQ&W$a0Q`i*&&cNUpuViGH$j?Pyb77I~%AXh-(O!=yWA>`8Z}S?lSB;La zSCw4gJa3B?f`FVxAUy!r>oSv7(jpXBPASM$gv-OseJ5jAi0S4GQIuGe6vKiu7)%y~ za~b5XNLuDH@m4rhB^l8A+NnVr%!}0)6RXZ|BvaIoDh-cixe|nC&Fv(8CCt9v;ii%B z3zZ8c6riW}3dFg!IYOt40F07-thD`grk5vWN+5vbM(G;U;iwUM}1=w~7au(AWQ zL!1zhpVDb^wO_d1B0W&9x^S&>vjt{lLGJWS(h5gVJ^`szVXaKTzzYHC#pHCmh&I8j zV}P0VE!HLl$~mBDtIfE}=q38N9u`twDdJu&ITPA) z0xZe^D&b76TFy(Si7IBs6cINlRMJYS3VGVX$lJm#Ci8Qm3Bt{ps;f{)m;s1X2Ho`3 zZlFC%hA=8n9Hn&eFy!joEAgedS25kly;7WssAztXj!HNi5WaSCmJ&QIlCEUe=4@6= zO>#iDptD0G8=G~ljO+?ku0X(KYXw@}h}2*fMs$#uq`44jCyrIW=t>srDYIDdQ6x$& zl}TPzZYEa_iY9bI1gm<7V5&Z@FpiVY7AZuaUdVX`8Y(BO%&%%pvu0X(*2@H~;|7S# z>;N8{aKX4{dU7y9C^r%1?CbMlY{7f^+<>#NscNis{2-p$e#U6GW;b7(%*x=oi7nKW zvh4J=BiLok4=C+kQv^;j<-FDnIj{8u5de}v2baj2mEPss$bfBpJ_Ug=saW-XI5YrE zh%{mVm=Gas^(+P?m{=)=Cp`2a3~B@P7GNaI*i*7A|#rkKBq#7sIkOMLICVR2kty zt#*-gzz8_0r9@|ygt;9q#?X>zAXP}=08MgkA&_B9wM4pMAY)5N3T~Fju;aqU(*XN* zOf8NSGeK*)_(l{K)pm=jKo*x+?Q|Z~^A9>Si|2BJLF;3W;3O&4Brk3Wq>gXy=KJx) zsgxCPa=h&aF@D|#x8U9@W=`Msl;1JFwg_$}2G%ydHa4Dp4{BrHokcMJ_}a4Z>^RGt zedBA3$Imk*1h{5;=M~~ksGVmVcXr4;n z76Of*F?PHqEIG~?Upr>J4XIblaX({yqlxh|CX5%~i+;z~jvMa+q_yL`854XPfblaX zk2ePKjY-GXP8{zhZEGibGbS(Hp$#zHmRCc!gR%XubRM?tt;YtQ*CcheJ-d5qXAd?P zb?4i&=?D8Vf0%RO3*yZ?`J1_^m@@_n8}5GHJ+uCI=Yr?=4hnY?dRco)l@V_P_P69C_6=l3ows153(w(UosdlG#5&-ym{eU*Ex{ndr9y}a)_yiFWskl7qkg-*K3ze;W0GO0z+QzP_6s?2Y-FE;3lN0(d`X<0HVlGxcdCbwHo zYirBoQI{2(d=*b_S$bZFGB>=}w=exxZ58wAUah5j$d?=t>Lcwfv87-kI51?zODpzAL?+ziSRexZA*8>vOu>$X)wE z-L-L7{7cH-45l?)<(ze?xfxxR(<{475C^)d-9C7J8i&IYj@g_=eR+y?RV1lOk7)V zBvR0sUdK(OHn{HwF-d+WO_k(l*KnPq&EWH6jP{Sp4-ot1)IBMRXXB|N_Pur^(mCx- z!$v>4&dA+{sO7#l6=t>#TY4d+_ni%+y3*xpoPR?%RkQugw14*h9YtL|lvp^69Fn@r zNa{Ty#4Qz<(G!QwUd5vvL5&$oP zRbz#3s-rE@lKAE?8%Bmcoi-}se^u|F`~`AWn=5ww#}(ebj^o3#lha=JwdZd74j4); z@E@L*y?bEN^QpS@!z0+!>c>3wYpPVPDBbb<;n}8V7BC8vI_@8GIH%;#mjyz6^YYjl zzGx|n-Qw3x>-aV0zv0R+{#CZ<8+}ukp8t*{QS{o5zS)_MVSTrVA{M)8=yaW{9r0&K0+tiD*KU9aV*B&i z{^;5Pv#}_#vw0wX+xg7E>?dafUVP?ZHd&tiZ03CV)8+R2RJLfd)qcBY+^a*D^R%ZM z(VsTAd^HZO?dUhIWp2D^f3FuC0vLOC3&+Jqc6_hB^Jg($CY{s2?jO~fW|#36`}wNY zti4y2kH3?kc$i=%U#P zUiQ|3rQ7s$(&^Npr5$&3xAeUJK45##>iho__^PsH-+*P8Zc`_La`zA@-FUU9)wO7T z{KD~SX?Yiqb9zsEq+LcEYTtcNd6je{>$li$YQ}G`SAI_26ASt=ajLbmXinZr1IIV7 zx=MPvWz{HMa)!X`yt*aTWfJtS|2vaGg2ZX;LHP=KumdkVZ=wwo?`kvgY6lL>{x~l_-k41DF8H_qaJHeIh9}Ye1>@;B#VI0P%|&XQ z%ekwOW?j`Ut0}J|^HQ9&wT>Xlkw(w+zbDHn9<>XLg9Yo%g1R@;cy4y;-EChR@~Qh7kKQoj|*+DlRg!xXA9}B14Qz5CJ;&2qx_9;3AY)U(Z+^{GoxC-g*pR zN2FH9pZtNqz9KWPzo75Y5SUL__m8a&-haxFPRRK2LI?5#40L8YAj=dh5Xhg5KfdA> zzV}MPfCE3c3<1kuv0+O$_@AymIW}7~qI24aw~U}yhXBW$JA#9jMii;HJQAJo zh|Q}M2Z~r5GlWPw?fMA+^vxE zdv`0Oh|qfViZC|Ho9)y4aap11A)?W!CP(iRd56r%@yV6RJvx5(O&e_}wWBtEeP_)( z;e^+te?hIDbu|xi!t2q0d%oP3{GFx_9ZzgAo)8CRoAus!;&0bFyt~6|<4rq-)6LBt zl%=2TTsT?dU)BKZr<}7Jb|K#MU6p*?IL)!rHVQ{NZcjd~(l=V^e1}u7VLFxkA;+=@ zC6|}{2qk-{$<=u%tg~GufB!SVny>SnlzcH?^7*jjuZ7sHt|SHa%&+@7YH=ii+MwY>JiIZ!x$36O@qYR?vYUd}d0k=k_jXmE z9-TW&s^PEdIchLDnWMXdGtSNJd!rgiJlDIz_|6!eNM`(T4n39B`ZSNT#Mb7Y2oHV@ zWfn)=4a@UvjeUE$t_Y>oK(1+D88tk8V%+WnTZnjfa z<<=13J2`~+BnS4yzw&WmKdN)e;D}}Eaoxk% zsy~Q7z9l`8+xX*6=_;iY0|_kr)kc8`vbt zl$`Qyiv9K{hL^sWmEtvM7Ft_s-r~F5fSSs#nlTO#8xpT3)>RWqN|PR5O>tLEC@0+z zc{PYhG5oLxI0n&nS@K47)kLk9&CM@RKcYZ#Deffeqd(BW29N$&y6REeFQo5-eE0`` z4*gI^B=O-HyV#uQqd)jBeez?erbjoOXCI;bw zgnGMKGmT!$?_lTY5Cw& z`Nqj5SVDEdu-wyzru0o^I?$BR-z1JItP6g>*#-@Mew5Cjk}JQtMZpy7vGix(eaX8h88lf<(PVTvKOP8we4o>s!fJz0xM#iJ=OYVS z-OGuOk%+Yu)VP=SoS5vNKu>Oj>VgDHlNM$8b`GCOh?H!M7eU+Dg2@kpI=fQ40S4orCJ82z0~|i;Ihr-*L741GRYDlVcz_ zeR`Ms6zE48+?<}?H^Ei$c2|Yh!I>c-Kl9wc)4EQ2*3OH*Wl<*SV*^a8Jb-|H@!4ELLAuxGFO@ z9Tb_zn|~-SnBdMNU5P%-rjB#y#JfJBW@1g~)4E{4S>91v9M7vO?3jDKx~4AJoPSlG z{JF3Y3Fc5qat~a}{d6hV2a5|*qjeI?q8MHJ(7N1+lqZ*P+Bl`&sW1NEj#s^!d#|%D z_*_VR--d}OatTH1bus4G4CRoiH@;21h-YqubspQttv_L9U9jT2Y9b1RM>*;xy09SG z#=vDcnV>EhzFP0fCp8{VbqJ=<$9n%E&v(ct#e;zIj^C^Q`m_KnQ42RqI9ee1`6~N9 z{`l}qoQZnVJkGmlC?psZ-+Wc@tCi5y(0|T;wEgcDAx+*XG;=stEO>vT4I?NmO0IKQ zc&obDS(`j8W9dm7rC42XzRCch;FWdBKVuz%j8mu^uPYfTCO8{x=nz0Ll1lHcs1H79 zzoOYMpL>LZT3#hiT9;0~`B3d0K||%zfwR2NJ`(%<$!An@=|p=TPhO;PTKF0*WTQ8G zcU4aCXB7FIfOBUoyKIySMUKRq-vK%!I@yf^Ws^9b=Pf+&8Y(BU4ZLZqr$FgI;yG=n zPrnPaOMG~}GrUd(NXti%UAkvQ2*SGnn5%$`z>iai#-JcAQ74gSs49mQv zhq3gjNDJHVcl=>kwl>!HM$SB|jPm?02rIKKdRxN;|1H^%3}nt<^}rixuy`l*6&9J+ zcl6%OlFC>?>QL`3oU)L6erI2&PaEv~KUo0vUQg$ftq5c=S&hyItjX%8Vdei6Cw*n? zL(fqn@#ZP0QT2nmC)Nc$4;evZ&Q&3_8YbEKI%Sl|a?}A^?3ZVkSGLkt?d^Be1zWzN zvc*jZfOoqh3)tZQ153~tfAf0fiyqCFAAsyBqVk7ai-5C$t@Q5!+qHBx$^CuY&(*kl zJ!kj50pzEy@K;&$z&in?ia;thMA&kFB`{IL7&EM{Gse9woy5m!iX10}6S@cGZlV}rm-^v*gUU&B4 z$l`7=17u}_Ke-iQn4I=qF+nt(|g_!Gl39h|jVIT}5KC0I2gJ2O&D&!alL?@re;7%K)v6XHMFC7fhB-U@Rp?fg(V zdgyoONV}$Z$T80j-$2Ega`l0K+=|u^1_jw=cFXY< zW9n`f@@wy)mN9TZ*EiIAJ7Zm^bfvc#EfmC?w(yJ2#j;b3)VpqpjpT`UOA)7AOz^%? z%;*+-pDP1^TJM%}E)(h1+)PKs9pLn*`0rBe9KQDNkT!3N{Hmf%dmIeVJXs#GzRxA^ z{rEqP+H80?pkk6E%4s<`Sj8O{wHtaA3Iqs_y?0YlZtS zaX+au{^emRd)_Izf8-e>9g6|+D;yqz8}b8Qb6XW@c#lCt&$^<;S5u&CT5(ij5zLo) zGmB#xa-n(K{6f6hP8WfK2};)Itg}q}NW@7|S9Cm5wZP->^X#%WEqK$*JEvC^4|yMH z=k(pq>2Dj>yBH5;PV(N3hT?Z%9pNPW%~0jeJ*Ub!*b70?^hpSzw25jzDcE-}5t+>L<%@ zb~;>ra0kn6`4q(9E8y*!x7Whu6T~he^rK(P6Z+>=YeQufKolXw%An|& z^eG~;D|$1iEa01Hu-L{ZnrScbrrjI`)ib#2WR$YPTPnEaaHpG%4OE)(cSZp}%7WXT z5Ma@qtGT3dnmb>#KKU%>MDRs(kYg0BO8yRU7JMrIAewBG19oTl;PbKMQ#$Z*M(%e} z@_<~7iSBprgF^H-C_xV&#C8+&M9yB@MG`e9da(Hf52Du|fnq0Id$GCQ=McE+BRaP@ z-t-v#q%+aTk!v{^Ri~gQXE;`SpDH>fHpwoU+*gtt_QIzyI#*5J@cJC0VB)|B7=(K+AB2y7H9rXFpfH+PpuzQSd)ce$=dQT#+sUls z>mOmevVsZC8aY}Q{4<_12rXPvIdM)s2jm8`f2{`+ixr`AD@uM?_I4y##FxM_mL5so z>xh_wh2=q=tAbH3_a@?4>9zXr&4_hX8N+ysio2@JeMfXkCF&nFObD-Lz3_W(_`6nD z=)Je&9KD_q6J_#$21c3m0jJcgQGq*)Ii_b7cgZixhcF9s-(tR)3+d5k3Ci|{aoJ^7 zD7)Og%HCBu11n)paPQY0!>C^O3MH=E;*W2~3DFroka|Elj+gwD@jFn0ZmLdS&45Fzs#A#`sSQfQcjs{cq{bOvv~ zS@FJ9=_^>Lk%kW@4>%(KCt}%%)i{7itZI<6GjEJ7lqSE?R3J2gP;I|L%C;*wJUL&@ zovLPwqpq2*8qU(PYD_w-nlh?+nb?bw)?hVQ5tRiEe@nwxhF^#lKq9E98qtDW5AUzw zek5{!)WQ*R=bm;|@jz!MQTX=Zgg1k4ylxAlU$+Iuqe``^1>MIkZ{e=XTA;w~Bh@2uIL95sbKPhF&Q*m9Y5Xm!6+yAEvcL#aT{xxo1J-cW#R=p zoLu^&tlVEx9`%-7)9=l_=A=Kj9OYS#MCNF$x!1hRZxr!0f9i@}L!AXRu*;0h_VP)G z7Iz}BixA#1p9V79ugLu7T;yo&9RQxJ4Sr}HL%DxY91bYaaHtk^1fx741CRH(TNK9! z^-`tEqKW^ z%AZ~UkKf~*^**#2ij7c`bPqq0E0F8lKd1ZKB?uxxC-;LMY2F{roLn%(8T|n#XPSt> z8U4n;*mqy$J2|5(9;4O&@jo?E=*|1yZ;a@i(T%*Kg?m)83(SlSeeGcwB(?RyX}SJ8 z%9k#w96RHl#dG4#I}8n#CjBU07M{X-ioR2PyDJWUI83SIrIF3;Z3Q4KUftnEE1{k; zQ^>&?!qO0xw>`T-A5za1VLcJo>_e^|$&V}0tFQ>JyNYsj*WK=2E_jPmm>I!W@y*>G zL5w+pTV*6veOoOsTVU^QQg0q8Pp)!y$^$6HF3`weWO!aO zO@D)_A-qSljduL;rB_iFQT*QFF05dMfN@TJu+63l%I74nRYdv8e>Rlc`#HfzrjQr_ z?@VW=mB0(l2(O0p+oc48E~cJfkO>)`qucOvKA^Ziq4L`P z%#i{Wsk@^-jQp3^2S0HUagn>99t%j#?P`2tyt+Sh#uNn^d!G?rSG1CM!pn!5QgJ`* ztkwG9!uvvsi#L4|vxy!S&ZwX-wZSacg4M=3zp>!hr>!!tYbx~lb3a;nZRgV8=}g{i zn-H+{_qxw_-EJ=HCjKFv2##S{(#NHpDo#&d2;ueT$L0hNxi)wSR|D^7ffvcf$V>i| z*C(4{yWr2?vf-)^CJ#an{SGznQS6;}0WlJi{tts9V*IR2> zy4I%is>ti2SKp;qm{=H-4MpDL<*M>475p{b2RMJ(l23k#IQPPWIl&VP4NDY$Q}Pmq zVQnx1%7M6Ku$`tK^kf>A>YqZ~6^f z#!*QX$MSl!;n|s$6YuqAzfm_;+;EaH8iboS-Rn8K;Ug{6-y{B?*)sjTy3T5uK1SEu zTBcvC>+F{4f30h6%k=9uyu0su6wW6O7@2iWA5n;{Fp8YsiGNhwr)3QwwamVXEUmS= zn|=kai;D|3ysKq;JoxjkY{KyB?ANNG0OM7Z`tEiOHaz?#V`%ENPX~k$&juPN!eBQ z`K^2*9rB#qjo#%(Wp%+AR#Siir$eSOHAh3ERdTxto7&*mdMioHyH7!OqT9n=-9L>$ zwWY~_M?=*HwHC(})vo9uoL5m7JY%Iek9~B*aAsA3<8c>)+lKl=9*%~Rr)em-R_O}0 zj)!WK*(H@r$o3>ixOw{>w3$-Q-^bFNb6uIy->cm*+_K|np zRXM(ej|Z-g@_yiYl|toALy9NMxvvXeF~CIRANQ}+Bfo@Wnwto_a!mVU6+FmErR%O% z<6en>rP}weR^dp|&)iLR=Fg)3+@D2jnx9J@iu8Y9ANJjp-0T$guTWbSs}A-tmkWZw zuaCQitCar1MM(t5eI5sna-86~Oi#i6?`z{lI{v+z7|ZnHs}&?qCw9iacXk`kN5`AT z@gP~{0`QI0@4!mD00L!%6>vP`l0^ik0s8G+|Frlh)se(O?W#4O9#?G6>nNW94W*!4!6%r3E-&W=NA z?`6(ivH1Eng6jnu`P`ec9g68SD0EBN9JE&^`xRSwyVVjk%7y=$%c6Yz0@17$?FgN+ z(H%&uGLhE669s7|k=dB~%1U798a|r8`j=i(SwZc=$=evTUrToV6;InER-oi+6{bj8 zLBl&1#)1M256MicqtJ@zz}u-~ypN1sxF~qZ^mP;ybYtZ@Y9XuuuY1ociaHZA<L^Sh_4f(rJ+aeq+fy$csQ6f4`5>+6?v_bKmGP!DwfPes zg;<^`ysxK}MOzOSCytHm;*f)yif6nZ6Xa%KpyGHdSt+)!K0W z6cHV7dJ#X9u!cykg-`x`6QbBX_?pX0Dk$A7ZSK8oyL7;*Mvau~+o?X~!9Cn*+Q zf^T&Qca;lkgAeUAEH)1`yvZMeNr$7jS0DA?CKiQ(nvN=ND4At)wJr6lr9DH(Gt&|M zNWAF=K{&~OpzO&P-+Sk3mZ{elC$^0|oqEZyuXsB8<+AUyI`4tIvb!qFyOcU8HAp=2 z7dDBBDvvk+4Fw((-#RMk{V~5?C!YRB;(tEh7dN5={u%M6eHRh=*Z2>WJ$d_z8g|_B z=M*FEF983Xif29({I-w1f*vY=x@FA7#}jAm^O1*BJ9_`8J~O;LJuP-!QC|`Bbyi;i z5lUBI;-l-?7xl9G78hlI=<0j0<-jMYPv5G(g`g|WIWM1ckJvf+zpW#@2P zht@g#RxL+Mw#eI7Dqvl(=F4mKK7=>B7|h;I13qUeNYQw; zMT8ptE2T{QtEE`H+A?nj)TEIa6rHaxNs`1}^AO(A_S~a=BMiR358>N=>cttaf^Wrb zD?j1j`$*-=Zv&q`Gx&;w59i>M)Npj~;B$0;+Ze<{PB!GF*$Kut55v@l9-h?qra@_z zlfPBO)cnbh`qRsv{`5n?|2dlnJy4uDIIXmZUOg&*YVzJ+urJ|Ls)m=_ zNqO7!b2s3f!QYP&1H_xViMBcP_*VyZquipmFikymod1yt-jy%EtFYkv{rnliAB9DgnSBM` zm1Ah~`(yc&xhbGTfp;kN=43$?frC*F)ZzN^eZA`%{wI~-U)S(++-;zVt{Q1JF6B?v zA%8JNaLBU{51nwyr*s)~etHe%2eZo1HjnLabY$IU(*mOvZ=CmR=1_sRNUlmzn1a?- zqplUO;lXYqWsyVD#UB~#-9M(cll;kruClkQWVy1HU9xIQTIQH+n}ioo`9(97+t_caDf)a~83EL|M0A zRwv7ohM?WpOy^kEJG`Kx^3f)!&-f}9z=>YGxGH^kLU}ZZc?)7R$^WHSaGE*7eZ55>F zDXhJn_boLR;of1lL~5HVe{Q#brQ1%s#cyq@?6O;2*QUyDyB)(YUD)>D4O97lJ5003 zdJi~#j3f2=P?r9vrM*eARR1JT8|I^1?k;=srJDEFvO@{F<@CtsQ@=&GJe4{5^NZ|B ze0TZN|McMdf0sD(w7wmAAk__Peayk5j~fEGo-b} zu6SZVQO9=Tb^Nt_kIaDq_syKTIfK}pdU@^X%W9ufH>BcgT*;yBwktV|`tz{<7dGt> zSmhPH8rJq9Rd$BOub%-)W~2W_U0kY&%su1O6@fEmvYR&}{^`F`Tgr3BFPTX*A=a{Z z(>i1`4QYC?DDj+QZm+0vsQK7BWzxQQlh&Ic27Tf}A1`}i=|ewU!CrE=6+iQE^IN~2 zI6v}8>WF_^#WRcFx|yw^{g0PFbNv2=HzqEQ+@0#cgUK9^zyuF2j0{{a?ieucpyQ)O zSYtyBG%Sp0#*o!6AHm8eP9o>(BE16gBDe+_PawJBW;#=peK6)|OZ0U|Q2Mk*nJ414 zvG3Wd?3OFM+0m()uF*F#2DJOiAH=5~`(UbONtGgPE)Mk6$eaq=s?ZER^)8fum~=oB(=M$cx)bz+@# zp~Abpxa*cOjTgU)y)Mq8y1ks@-ag5@TPh;wFL#4vaeSCj1UZ<`3oW-m8g?#?)#hB7 ziiKBLJ}&dEe^&R3?1funT~J7B3{%Ie)F%Ii&LY1~T8<>+yBh@@Zlx?!*iKAycOMGPNU}M%5J}-eo&H* zxZc=(>2?#Nhs1?tH(ulQzx9^r;ve^7XlmYMw3_h0wm@2E=EO4pYj?T3V*hJ*y1OF( zYjfRQ%>P=gyNmi?o2k2^iJ7kQTT{<~G(>jaTS9ngsY3V~4K)*(IEM91H!GMr^(S}x zm_mL-cZ|(=XLc8r?9W^nCS6$&pSle!PP;Jatheo&UH3o5#E6MV;QSC0l{piBdZ(|w z;O)LDTno!Q#cOy>9@>IQCs*IJ_aCrf>WUK=uwh>E>nff)wm$kBY#8L) z-%l?3dE%mdiwz_H_?s@LhSb`EatQn@;ZFk7F@8Q6{zPav{{4|<{Y#2yk|VnGyxhmn z7bSnI1b~9zBfv-cX44ruB0=;dN>+4R>Fw=%Yg?2b=+ja<22x9-WHXZcA{FILR?rjy z_2zzm=9^#kW|qE8fqs6U3KylQsXTY*h}{JKpTRpp!cKP<%+ir z=Lw~IwZ5T^C`3W;4Z0Im^^W6C?z1gua?}G;woXUKBa4`s>Gdv-2LO3!W|h&mr#S*XN9U`(>*|bN;66@zCEqRnz*NSNWUq zJ8u1v^EdzS+pT}Z-&8&3Zy+wYe+zXY% zJiq$Y%8#DhRs?e(0#jESKAnHmS3cwi>T0{XU03XVyex7`T4=Vc?wq5oZsTmnn;tqj zr04JPzBJtzwKRvKEK5`x5anA<&tQJ41nBSLhuzu>s(}GpshVhAi*<`lEP@;kCir6%b$mU)b*^F!x z;oLUXtGhgm*)$1&oa}k>Tkn1N=BI!1{-=k5LHwryVel<_eT_f0j0ug0iW6sP@`OLN z;+Y#~S8$9V!L29%P&8(L;;?-zco3%*Pr*PW9#xX*HSlz+xT#TVSnv^gEM3! zVi{zjv$llC7*Zh+6eBfn=S;>XXBi2J)USsK!`*@X2DeuDD;*5vbMM*8EDCI@zCfkyBJe zab55WUQ*75-;ZToeE!T2P!PqzR|x^|j_QkQGTRqTV)i_4N2(7@s0^+Qf$ealZJ`N( zDbEZHd)le6vmUBvuVh57FS>dM*l_F}js@-}(F=2J(QT<-8X*q|ZT>(`mU#QI(qNYQ zVsD$6WO`X-FG2-jT;bJ@^&TA4Ti-jt+~<#&#bbUl{m3QD90=z>PZqP)>9dJ;*Riu_ z{x$tYa<|^pRWnB7)@Np-W*kd5$9gpr`o8Et6iq);M8-F-1-n0;IK;&IVADUk@u``d zex!=!X4#qr+1b+;7L!`_OVcz(=||?;^V;m}JMH;a_uQXe4(g|MWgAbBICJ%VHY&_6 z#LY%fa1%RP=l|5)R=)(<$x9dRScxc z2U2ANsbc142Ci4wYv6iCz=9n&icrS9-hL57KMPKxZfGOijlk9uvfqcTuW1LhLsFWa z`>-(xoKckg2hw^N#HShwIHz&kdS z{+E7e*oGg!wc(AKF47aDH~r}yB-QoxY99s={e8Ad3Gh$=*=(p#r-oye7>ZR8~JCa#lvcc_uOTvuk$NHXilZ?f(`>n793 zsT-gQ_IuDnbd)+eYZ)ESB`dY6m_=TycB9I{h@|r5sT%fEbF8W7T$A(uEElB+X zZmlek6c45#yUJfT3;DJ4aL%1IO;9zMU{7XLZSbGfhMU9yK73Vycg7Iha4TzP!n`UW zW$5~uZ!nYy`AmB;%jP3pHG}ol%>lKwtOs>fT7{{0&Dnn3eKWkBp7E7;s^cZaG)71E zX17d~T}~s>NWWrR1WED>T%LD}S1qbMWEo4@oZu^_OcVqd`aD=*AH<-f;`(4!?rTuG zBt+?hvOj?GLFk%PtvOmPGa6bwd$+J)bA=g-dPlA!Ad>y3H1}tT%;+mBRep9QYOo^p z8L3h4A!QMic@N1|o2bd_eb;t-Qx(sO(Ur*@WkgUT{*&FJ?%P>rw_mG3kD%k5Um#Jt zD&E|LIZo+vTjJ6mK~wRoC>Y7yEl-nx!=O>5Jd2SQ8dTz#<7`c5vYgDPrq}OF#x0v^LhqpVJ_!S$aFu8!znEIqy57Z|6Ad%pn&l*HPSMFAY z%yAFu-I9>%@b!4NBIE<%&-5wrXamL90Ji~CsVfv$(l#)0?-FtI59 zcn8z>%F=`_=hg=QrF{2(tl^ml>7Ku z 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 literal 0 HcmV?d00001 diff --git a/src/bootloader/limine/installer.zig b/src/bootloader/limine/installer.zig new file mode 100644 index 0000000..8df1de2 --- /dev/null +++ b/src/bootloader/limine/installer.zig @@ -0,0 +1,1676 @@ +const std = @import("std"); +const assert = std.debug.assert; +const builtin = @import("builtin"); +const log = std.log; + +comptime { + assert(@import("builtin").cpu.arch.endian() == .Little); +} + +const InstallerError = error{ + not_64_bit, + unable_to_get_arguments, + image_file_not_found, + unable_to_get_file_size, + unable_to_allocate_memory_for_file, + unable_to_read_file_into_memory, + secondary_GPT_header_invalid, + invalid_partition_table, +}; + +const GPT = struct { + const Header = extern struct { + signature: u64 align(4), + revision: u32, + header_size: u32, + CRC32: u32, + _reserved0: u32, + + LBA: u64 align(4), + alternate_LBA: u64 align(4), + first_usable_LBA: u64 align(4), + last_usable_LBA: u64 align(4), + + disk_GUID_0: [2]u64 align(4), + + partition_entry_LBA: u64 align(4), + partition_entry_count: u32, + partition_entry_size: u32, + partition_entry_array_CRC32: u32, + + comptime { + const expected_size = @sizeOf([8]u8) + (4 * @sizeOf(u32)) + (4 * @sizeOf(u64)) + (2 * @sizeOf(u64)) + @sizeOf(u64) + (3 * @sizeOf(u32)); + std.debug.assert(expected_size == 92); + std.debug.assert(@sizeOf(Header) == expected_size); + } + }; + + const Entry = extern struct { + partition_type_guid0: u64, + partition_type_guid1: u64, + unique_partition_guid0: u64, + unique_partition_guid1: u64, + starting_LBA: u64, + ending_LBA: u64, + attributes: u64, + partition_name: [36]u16, + + comptime { + const expected_size = @sizeOf([2]u64) + (2 * @sizeOf(u64)) + (3 * @sizeOf(u64)) + (36 * @sizeOf(u16)); + std.debug.assert(expected_size == 128); + std.debug.assert(@sizeOf(Entry) == expected_size); + } + }; +}; + +var format_buffer: [8192]u8 = undefined; +fn print(comptime format: []const u8, args: anytype) void { + const format_buffer_slice = std.fmt.bufPrint(&format_buffer, format, args) catch @panic("Unable to format stdout buffer\n"); + stderr(format_buffer_slice); +} + +fn stderr(bytes: []const u8) void { + _ = std.io.getStdErr().write(bytes) catch @panic("Unable to write to stdout\n"); +} + +fn print_error_and_exit(e: InstallerError) InstallerError { + print("An error occurred: {}\n", .{e}); + return e; +} + +const gpt_header_signature = @as(*align(1) const u64, @ptrCast("EFI PART")).*; + +fn div_roundup(a: u64, b: u64) u64 { + return (((a) + ((b) - 1)) / (b)); +} + +fn crc32(bytes: []const u8) u32 { + var result: u32 = std.math.maxInt(u32); + for (bytes) |byte| { + result = (result >> 8) ^ crc32_table[@as(u8, @truncate(result ^ byte))]; + } + + result ^= std.math.maxInt(u32); + return result; +} + +const crc32_table = [_]u32{ 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d }; + +pub const hdd = [_]u8{ + 0xeb, 0x3c, 0x90, 0x4c, 0x49, 0x4d, 0x49, 0x4e, 0x45, 0x20, 0x20, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4c, 0x49, 0x4d, 0x49, 0x4e, + 0x45, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfa, 0xfc, + 0xea, 0x45, 0x7c, 0x00, 0x00, 0x31, 0xf6, 0x8e, 0xde, 0x8e, 0xc6, 0x8e, 0xd6, 0xbc, 0x00, 0x7c, + 0xfb, 0x80, 0xfa, 0x80, 0x0f, 0x82, 0x10, 0x01, 0x80, 0xfa, 0x8f, 0x0f, 0x87, 0x08, 0x01, 0xb4, + 0x41, 0xbb, 0xaa, 0x55, 0xcd, 0x13, 0x0f, 0x82, 0xfc, 0x00, 0x81, 0xfb, 0x55, 0xaa, 0x0f, 0x85, + 0xf3, 0x00, 0x68, 0x00, 0x70, 0x07, 0xbf, 0xa8, 0x7d, 0x66, 0x8b, 0x05, 0x66, 0x8b, 0x6d, 0x04, + 0x31, 0xdb, 0x66, 0x31, 0xc9, 0x8b, 0x4d, 0xfc, 0xe8, 0x55, 0x00, 0x0f, 0x82, 0xd5, 0x00, 0x66, + 0x8b, 0x45, 0x08, 0x66, 0x8b, 0x6d, 0x0c, 0x01, 0xcb, 0x8b, 0x4d, 0xfe, 0xe8, 0x41, 0x00, 0x0f, + 0x82, 0xc0, 0x00, 0x0f, 0x01, 0x16, 0x4d, 0x7d, 0xfa, 0x0f, 0x20, 0xc0, 0x0f, 0xba, 0xe8, 0x00, + 0x0f, 0x22, 0xc0, 0xea, 0x79, 0x7d, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x60, 0xbe, 0x00, 0x80, 0xc7, 0x04, 0x10, 0x00, 0xc7, 0x44, 0x02, 0x01, 0x00, 0x89, 0x5c, 0x04, + 0x8c, 0x44, 0x06, 0x52, 0x56, 0x66, 0x50, 0x66, 0x55, 0xb4, 0x48, 0xbe, 0x10, 0x80, 0xc7, 0x04, + 0x1e, 0x00, 0xcd, 0x13, 0x72, 0x45, 0x8b, 0x6c, 0x18, 0x89, 0xc8, 0x66, 0xc1, 0xe9, 0x10, 0x89, + 0xca, 0x31, 0xc9, 0xf7, 0xf5, 0x85, 0xd2, 0x0f, 0x95, 0xc1, 0x01, 0xc1, 0x66, 0x5a, 0x66, 0x58, + 0x5e, 0x66, 0xf7, 0xf5, 0x66, 0x89, 0x44, 0x08, 0x66, 0xc7, 0x44, 0x0c, 0x00, 0x00, 0x00, 0x00, + 0x5a, 0xb4, 0x42, 0xf8, 0xcd, 0x13, 0x72, 0x13, 0x01, 0x6c, 0x04, 0x66, 0x31, 0xdb, 0x66, 0xff, + 0x44, 0x08, 0x0f, 0x90, 0xc3, 0x66, 0x01, 0x5c, 0x0c, 0xe2, 0xe6, 0x61, 0xc3, 0x17, 0x00, 0x4b, + 0x7d, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x9a, 0xcf, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, + 0x92, 0xcf, 0x00, 0x46, 0x46, 0x46, 0x46, 0x46, 0x81, 0xc6, 0x30, 0x4f, 0x68, 0x00, 0xb8, 0x07, + 0x26, 0x89, 0x36, 0x00, 0x00, 0xfb, 0xf4, 0xeb, 0xfd, 0xb8, 0x10, 0x00, 0x00, 0x00, 0x8e, 0xd8, + 0x8e, 0xc0, 0x8e, 0xe0, 0x8e, 0xe8, 0x8e, 0xd0, 0x81, 0xe2, 0xff, 0x00, 0x00, 0x00, 0x6a, 0x00, + 0x52, 0x68, 0xc3, 0x48, 0x00, 0x00, 0x68, 0xe0, 0x0a, 0x07, 0x00, 0xe8, 0x60, 0x82, 0x06, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0xaa, + 0xfc, 0x30, 0xc0, 0xbf, 0xd8, 0x0a, 0x07, 0x00, 0xb9, 0xd8, 0x0a, 0x07, 0x00, 0x81, 0xe9, 0xd8, + 0x0a, 0x07, 0x00, 0xf3, 0xaa, 0xe9, 0x7a, 0x00, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, + 0x56, 0x57, 0x8b, 0x44, 0x24, 0x0c, 0x89, 0xc7, 0x8b, 0x74, 0x24, 0x10, 0x8b, 0x4c, 0x24, 0x14, + 0xf3, 0xa4, 0x5f, 0x5e, 0xc3, 0x57, 0x8b, 0x54, 0x24, 0x08, 0x89, 0xd7, 0x8b, 0x44, 0x24, 0x0c, + 0x8b, 0x4c, 0x24, 0x10, 0xf3, 0xaa, 0x89, 0xd0, 0x5f, 0xc3, 0x56, 0x57, 0x8b, 0x44, 0x24, 0x0c, + 0x89, 0xc7, 0x8b, 0x74, 0x24, 0x10, 0x8b, 0x4c, 0x24, 0x14, 0x39, 0xf7, 0x77, 0x04, 0xf3, 0xa4, + 0xeb, 0x0c, 0x8d, 0x7c, 0x0f, 0xff, 0x8d, 0x74, 0x0e, 0xff, 0xfd, 0xf3, 0xa4, 0xfc, 0x5f, 0x5e, + 0xc3, 0x56, 0x57, 0x8b, 0x7c, 0x24, 0x0c, 0x8b, 0x74, 0x24, 0x10, 0x8b, 0x4c, 0x24, 0x14, 0xf3, + 0xa6, 0x74, 0x0b, 0x8a, 0x47, 0xff, 0x2a, 0x46, 0xff, 0x0f, 0xbe, 0xc0, 0xeb, 0x02, 0x31, 0xc0, + 0x5f, 0x5e, 0xc3, 0xcc, 0x57, 0x56, 0x0f, 0xb6, 0x74, 0x24, 0x14, 0x8b, 0x7c, 0x24, 0x18, 0xff, + 0x74, 0x24, 0x10, 0xff, 0x74, 0x24, 0x10, 0x68, 0x00, 0x80, 0x00, 0x00, 0xe8, 0x17, 0x00, 0x00, + 0x00, 0x83, 0xc4, 0x0c, 0xbc, 0x00, 0x7c, 0x00, 0x00, 0x31, 0xed, 0x57, 0x56, 0x6a, 0x00, 0x68, + 0x00, 0x80, 0x00, 0x00, 0xc3, 0xcc, 0xcc, 0xcc, 0x55, 0x53, 0x57, 0x56, 0x8b, 0x54, 0x24, 0x1c, + 0xb8, 0xfd, 0xff, 0xff, 0xff, 0x83, 0xfa, 0x12, 0x0f, 0x82, 0xa2, 0x00, 0x00, 0x00, 0x8b, 0x4c, + 0x24, 0x18, 0x80, 0x39, 0x1f, 0x0f, 0x85, 0x95, 0x00, 0x00, 0x00, 0x80, 0x79, 0x01, 0x8b, 0x0f, + 0x85, 0x8b, 0x00, 0x00, 0x00, 0x80, 0x79, 0x02, 0x08, 0x0f, 0x85, 0x81, 0x00, 0x00, 0x00, 0x0f, + 0xb6, 0x59, 0x03, 0x83, 0xfb, 0x1f, 0x77, 0x78, 0x8d, 0x71, 0x0a, 0xf6, 0xc3, 0x04, 0x74, 0x0e, + 0x0f, 0xb6, 0x3e, 0x8d, 0x6a, 0xf4, 0x39, 0xfd, 0x72, 0x66, 0x8d, 0x74, 0x37, 0x02, 0xf6, 0xc3, + 0x08, 0x75, 0x04, 0x89, 0xf7, 0xeb, 0x13, 0x89, 0xf5, 0x29, 0xcd, 0x39, 0xd5, 0x73, 0x51, 0x8d, + 0x7e, 0x01, 0x45, 0x80, 0x3e, 0x00, 0x89, 0xfe, 0x75, 0xf1, 0xf6, 0xc3, 0x10, 0x75, 0x04, 0x89, + 0xfd, 0xeb, 0x13, 0x89, 0xfe, 0x29, 0xce, 0x39, 0xd6, 0x73, 0x35, 0x8d, 0x6f, 0x01, 0x46, 0x80, + 0x3f, 0x00, 0x89, 0xef, 0x75, 0xf1, 0x83, 0xe3, 0x02, 0x01, 0xeb, 0x01, 0xd1, 0x29, 0xd9, 0x83, + 0xf9, 0x08, 0x7c, 0x1c, 0x83, 0xc1, 0xf8, 0x51, 0x53, 0xff, 0x74, 0x24, 0x1c, 0xe8, 0x16, 0x00, + 0x00, 0x00, 0x83, 0xc4, 0x0c, 0x31, 0xc9, 0x85, 0xc0, 0x0f, 0x94, 0xc1, 0x8d, 0x44, 0x49, 0xfd, + 0x5e, 0x5f, 0x5b, 0x5d, 0xc3, 0xcc, 0xcc, 0xcc, 0x55, 0x53, 0x57, 0x56, 0x81, 0xec, 0x40, 0x06, + 0x00, 0x00, 0x8b, 0x84, 0x24, 0x54, 0x06, 0x00, 0x00, 0x8b, 0x8c, 0x24, 0x58, 0x06, 0x00, 0x00, + 0x8d, 0x54, 0x24, 0x38, 0x89, 0x4a, 0xe4, 0x03, 0x8c, 0x24, 0x5c, 0x06, 0x00, 0x00, 0x89, 0x4a, + 0xe8, 0x31, 0xc9, 0x89, 0x4a, 0xec, 0x89, 0x4a, 0xf0, 0x89, 0x4a, 0xf4, 0x89, 0x42, 0xfc, 0x89, + 0x42, 0xf8, 0x8b, 0x4c, 0x24, 0x28, 0x85, 0xc9, 0x7e, 0x04, 0x89, 0xca, 0xeb, 0x38, 0x8b, 0x74, + 0x24, 0x1c, 0x8b, 0x44, 0x24, 0x20, 0x39, 0xc6, 0x74, 0x14, 0x8d, 0x56, 0x01, 0x89, 0x54, 0x24, + 0x1c, 0x0f, 0xb6, 0x36, 0xd3, 0xe6, 0x09, 0x74, 0x24, 0x24, 0x89, 0xd6, 0xeb, 0x0a, 0xc7, 0x44, + 0x24, 0x2c, 0x01, 0x00, 0x00, 0x00, 0x89, 0xc6, 0x8d, 0x51, 0x08, 0x89, 0x54, 0x24, 0x28, 0x83, + 0xf9, 0xf9, 0x89, 0xd1, 0x7c, 0xd0, 0x8b, 0x74, 0x24, 0x24, 0x89, 0x74, 0x24, 0x14, 0xd1, 0xee, + 0x89, 0x74, 0x24, 0x24, 0x8d, 0x5a, 0xff, 0x89, 0x5c, 0x24, 0x28, 0x83, 0xfb, 0x01, 0x77, 0x45, + 0x8b, 0x4c, 0x24, 0x1c, 0x8b, 0x7c, 0x24, 0x20, 0x83, 0xc2, 0xf7, 0x8d, 0x5a, 0x08, 0x39, 0xf9, + 0x74, 0x18, 0x8d, 0x41, 0x01, 0x89, 0x44, 0x24, 0x1c, 0x0f, 0xb6, 0x29, 0x89, 0xd9, 0xd3, 0xe5, + 0x09, 0xee, 0x89, 0x74, 0x24, 0x24, 0x89, 0xc1, 0xeb, 0x0a, 0xc7, 0x44, 0x24, 0x2c, 0x01, 0x00, + 0x00, 0x00, 0x89, 0xf9, 0x83, 0xc2, 0x10, 0x89, 0x54, 0x24, 0x28, 0x83, 0xfb, 0xfa, 0x89, 0xda, + 0x7c, 0xc9, 0x83, 0xc3, 0x08, 0x89, 0xf5, 0xc1, 0xed, 0x02, 0x89, 0x6c, 0x24, 0x24, 0x8d, 0x53, + 0xfe, 0x89, 0x54, 0x24, 0x28, 0xb8, 0xfd, 0xff, 0xff, 0xff, 0x83, 0xe6, 0x03, 0x0f, 0x84, 0xeb, + 0x00, 0x00, 0x00, 0x83, 0xfe, 0x02, 0x0f, 0x84, 0x4c, 0x01, 0x00, 0x00, 0x83, 0xfe, 0x01, 0x0f, + 0x85, 0xcf, 0x04, 0x00, 0x00, 0xb8, 0xe0, 0xff, 0xff, 0xff, 0x66, 0xc7, 0x44, 0x04, 0x58, 0x00, + 0x00, 0x83, 0xc0, 0x02, 0x75, 0xf4, 0xc7, 0x44, 0x24, 0x46, 0x18, 0x00, 0x98, 0x00, 0x66, 0xc7, + 0x44, 0x24, 0x4a, 0x70, 0x00, 0xb8, 0xe8, 0xff, 0xff, 0xff, 0x8d, 0x88, 0x18, 0x01, 0x00, 0x00, + 0x66, 0x89, 0x8c, 0x44, 0x88, 0x00, 0x00, 0x00, 0x40, 0x75, 0xef, 0x31, 0xc0, 0x66, 0x89, 0x84, + 0x44, 0x88, 0x00, 0x00, 0x00, 0x40, 0x3d, 0x90, 0x00, 0x00, 0x00, 0x75, 0xf0, 0xb8, 0xf8, 0xff, + 0xff, 0xff, 0x8d, 0x88, 0x20, 0x01, 0x00, 0x00, 0x66, 0x89, 0x8c, 0x44, 0xb8, 0x01, 0x00, 0x00, + 0x40, 0x75, 0xef, 0xb8, 0x90, 0xff, 0xff, 0xff, 0x8d, 0x88, 0x00, 0x01, 0x00, 0x00, 0x66, 0x89, + 0x8c, 0x44, 0x98, 0x02, 0x00, 0x00, 0x40, 0x75, 0xef, 0xc7, 0x84, 0x24, 0x98, 0x02, 0x00, 0x00, + 0x1d, 0x01, 0x00, 0x00, 0xb8, 0xe0, 0xff, 0xff, 0xff, 0x66, 0xc7, 0x84, 0x04, 0xbc, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x83, 0xc0, 0x02, 0x75, 0xf1, 0x66, 0xc7, 0x84, 0x24, 0xa6, 0x02, 0x00, 0x00, + 0x20, 0x00, 0x31, 0xc0, 0x66, 0x89, 0x84, 0x44, 0xbc, 0x02, 0x00, 0x00, 0x40, 0x83, 0xf8, 0x20, + 0x75, 0xf2, 0xc7, 0x84, 0x24, 0xfc, 0x04, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x8d, 0x4c, 0x24, + 0x1c, 0x8d, 0x54, 0x24, 0x38, 0x8d, 0x84, 0x24, 0x9c, 0x02, 0x00, 0x00, 0x50, 0xe8, 0x66, 0x04, + 0x00, 0x00, 0x83, 0xc4, 0x04, 0x85, 0xc0, 0x74, 0x5f, 0xe9, 0xf6, 0x03, 0x00, 0x00, 0x8b, 0x4c, + 0x24, 0x1c, 0x8b, 0x54, 0x24, 0x20, 0x29, 0xca, 0x83, 0xfa, 0x04, 0x0f, 0x8c, 0xe3, 0x03, 0x00, + 0x00, 0x0f, 0xb7, 0x19, 0x0f, 0xb7, 0x51, 0x02, 0x81, 0xf2, 0xff, 0xff, 0x00, 0x00, 0x39, 0xd3, + 0x0f, 0x85, 0xce, 0x03, 0x00, 0x00, 0x83, 0xc1, 0x04, 0x89, 0x4c, 0x24, 0x1c, 0x85, 0xdb, 0x74, + 0x1d, 0x8b, 0x4c, 0x24, 0x1c, 0x8b, 0x54, 0x24, 0x34, 0x8d, 0x71, 0x01, 0x8d, 0x7a, 0x01, 0x4b, + 0x89, 0x74, 0x24, 0x1c, 0x8a, 0x09, 0x89, 0x7c, 0x24, 0x34, 0x88, 0x0a, 0x75, 0xe3, 0x31, 0xc0, + 0x89, 0x44, 0x24, 0x24, 0x89, 0x44, 0x24, 0x28, 0xf6, 0x44, 0x24, 0x14, 0x01, 0x0f, 0x84, 0xef, + 0xfd, 0xff, 0xff, 0xe9, 0x6d, 0x03, 0x00, 0x00, 0x83, 0xfa, 0x04, 0x77, 0x45, 0x8b, 0x4c, 0x24, + 0x1c, 0x8b, 0x74, 0x24, 0x20, 0x83, 0xc3, 0xf6, 0x8d, 0x53, 0x08, 0x39, 0xf1, 0x74, 0x18, 0x8d, + 0x79, 0x01, 0x89, 0x7c, 0x24, 0x1c, 0x0f, 0xb6, 0x01, 0x89, 0xd1, 0xd3, 0xe0, 0x09, 0xc5, 0x89, + 0x6c, 0x24, 0x24, 0x89, 0xf9, 0xeb, 0x0a, 0xc7, 0x44, 0x24, 0x2c, 0x01, 0x00, 0x00, 0x00, 0x89, + 0xf1, 0x83, 0xc3, 0x10, 0x89, 0x5c, 0x24, 0x28, 0x83, 0xfa, 0xfd, 0x89, 0xd3, 0x7c, 0xc9, 0x83, + 0xc2, 0x08, 0x89, 0xe9, 0xc1, 0xe9, 0x05, 0x89, 0x4c, 0x24, 0x24, 0x8d, 0x42, 0xfb, 0x89, 0x44, + 0x24, 0x28, 0x83, 0xf8, 0x04, 0x77, 0x55, 0x89, 0x6c, 0x24, 0x08, 0x89, 0xcd, 0x8b, 0x4c, 0x24, + 0x1c, 0x8b, 0x74, 0x24, 0x20, 0x83, 0xc2, 0xf3, 0x8d, 0x42, 0x08, 0x39, 0xf1, 0x74, 0x1c, 0x8d, + 0x79, 0x01, 0x89, 0x7c, 0x24, 0x1c, 0x0f, 0xb6, 0x19, 0x89, 0xc1, 0xd3, 0xe3, 0x89, 0xe9, 0x09, + 0xd9, 0x89, 0xcd, 0x89, 0x4c, 0x24, 0x24, 0x89, 0xf9, 0xeb, 0x0a, 0xc7, 0x44, 0x24, 0x2c, 0x01, + 0x00, 0x00, 0x00, 0x89, 0xf1, 0x83, 0xc2, 0x10, 0x89, 0x54, 0x24, 0x28, 0x83, 0xf8, 0xfd, 0x89, + 0xc2, 0x7c, 0xc5, 0x83, 0xc0, 0x08, 0x89, 0xe9, 0x8b, 0x6c, 0x24, 0x08, 0x83, 0xe5, 0x1f, 0x89, + 0x4c, 0x24, 0x04, 0x89, 0xca, 0xc1, 0xea, 0x05, 0x89, 0x54, 0x24, 0x24, 0x8d, 0x48, 0xfb, 0x89, + 0x4c, 0x24, 0x28, 0x83, 0xf9, 0x03, 0x77, 0x43, 0x8b, 0x7c, 0x24, 0x1c, 0x8b, 0x74, 0x24, 0x20, + 0x83, 0xc0, 0xf3, 0x8d, 0x48, 0x08, 0x39, 0xf7, 0x74, 0x16, 0x8d, 0x5f, 0x01, 0x89, 0x5c, 0x24, + 0x1c, 0x0f, 0xb6, 0x3f, 0xd3, 0xe7, 0x09, 0xfa, 0x89, 0x54, 0x24, 0x24, 0x89, 0xdf, 0xeb, 0x0a, + 0xc7, 0x44, 0x24, 0x2c, 0x01, 0x00, 0x00, 0x00, 0x89, 0xf7, 0x83, 0xc0, 0x10, 0x89, 0x44, 0x24, + 0x28, 0x83, 0xf9, 0xfc, 0x89, 0xc8, 0x7c, 0xcb, 0x83, 0xc1, 0x08, 0x89, 0xd0, 0xc1, 0xe8, 0x04, + 0x89, 0x44, 0x24, 0x24, 0x83, 0xc1, 0xfc, 0x89, 0x4c, 0x24, 0x28, 0x83, 0xfd, 0x1d, 0x0f, 0x87, + 0x56, 0x02, 0x00, 0x00, 0x8b, 0x74, 0x24, 0x04, 0x83, 0xe6, 0x1f, 0x83, 0xfe, 0x1d, 0x0f, 0x87, + 0x46, 0x02, 0x00, 0x00, 0xc7, 0x04, 0x24, 0xfd, 0xff, 0xff, 0xff, 0x81, 0xc5, 0x01, 0x01, 0x00, + 0x00, 0x89, 0x6c, 0x24, 0x08, 0x46, 0x89, 0x74, 0x24, 0x04, 0x83, 0xe2, 0x0f, 0xbe, 0xed, 0xff, + 0xff, 0xff, 0xc6, 0x84, 0x34, 0x13, 0x05, 0x00, 0x00, 0x00, 0x46, 0x75, 0xf5, 0x83, 0xc2, 0x03, + 0x31, 0xff, 0x8b, 0x74, 0x24, 0x20, 0x83, 0xf9, 0x02, 0x7f, 0x39, 0x8b, 0x6c, 0x24, 0x1c, 0x39, + 0xf5, 0x74, 0x17, 0x8d, 0x5d, 0x01, 0x89, 0x5c, 0x24, 0x1c, 0x0f, 0xb6, 0x6d, 0x00, 0xd3, 0xe5, + 0x09, 0xe8, 0x89, 0x44, 0x24, 0x24, 0x89, 0xdd, 0xeb, 0x0a, 0xc7, 0x44, 0x24, 0x2c, 0x01, 0x00, + 0x00, 0x00, 0x89, 0xf5, 0x8d, 0x59, 0x08, 0x89, 0x5c, 0x24, 0x28, 0x83, 0xf9, 0xfb, 0x89, 0xd9, + 0x7c, 0xcd, 0xeb, 0x02, 0x89, 0xcb, 0x89, 0xc1, 0xc1, 0xe9, 0x03, 0x89, 0x4c, 0x24, 0x24, 0x83, + 0xc3, 0xfd, 0x89, 0x5c, 0x24, 0x28, 0x24, 0x07, 0x0f, 0xb6, 0xaf, 0x88, 0x0a, 0x07, 0x00, 0x88, + 0x84, 0x2c, 0x00, 0x05, 0x00, 0x00, 0x39, 0xd7, 0x8d, 0x7f, 0x01, 0x89, 0xc8, 0x89, 0xd9, 0x75, + 0x95, 0x8d, 0x4c, 0x24, 0x38, 0x8d, 0x94, 0x24, 0x00, 0x05, 0x00, 0x00, 0x6a, 0x13, 0xe8, 0x82, + 0x03, 0x00, 0x00, 0x83, 0xc4, 0x04, 0x85, 0xc0, 0x0f, 0x85, 0x87, 0x01, 0x00, 0x00, 0x83, 0xbc, + 0x24, 0x98, 0x02, 0x00, 0x00, 0xff, 0x0f, 0x84, 0x85, 0x01, 0x00, 0x00, 0x8b, 0x5c, 0x24, 0x08, + 0x8b, 0x44, 0x24, 0x04, 0x8d, 0x2c, 0x18, 0x31, 0xff, 0x89, 0x6c, 0x24, 0x18, 0x8d, 0x4c, 0x24, + 0x1c, 0x8d, 0x54, 0x24, 0x38, 0xe8, 0xf8, 0x02, 0x00, 0x00, 0x3b, 0x84, 0x24, 0x98, 0x02, 0x00, + 0x00, 0x0f, 0x8f, 0x5a, 0x01, 0x00, 0x00, 0x89, 0xc1, 0x83, 0xf8, 0x12, 0x74, 0x35, 0x83, 0xf9, + 0x11, 0x74, 0x47, 0x83, 0xf9, 0x10, 0x0f, 0x85, 0xc7, 0x00, 0x00, 0x00, 0x85, 0xff, 0x0f, 0x84, + 0x3d, 0x01, 0x00, 0x00, 0xc7, 0x44, 0x24, 0x10, 0x03, 0x00, 0x00, 0x00, 0x8d, 0x84, 0x24, 0x00, + 0x05, 0x00, 0x00, 0x0f, 0xb6, 0x44, 0x07, 0xff, 0x89, 0x44, 0x24, 0x0c, 0xb8, 0x02, 0x00, 0x00, + 0x00, 0xeb, 0x2c, 0xc7, 0x44, 0x24, 0x0c, 0x00, 0x00, 0x00, 0x00, 0xc7, 0x44, 0x24, 0x10, 0x0b, + 0x00, 0x00, 0x00, 0xb8, 0x07, 0x00, 0x00, 0x00, 0xeb, 0x15, 0xc7, 0x44, 0x24, 0x0c, 0x00, 0x00, + 0x00, 0x00, 0xc7, 0x44, 0x24, 0x10, 0x03, 0x00, 0x00, 0x00, 0xb8, 0x03, 0x00, 0x00, 0x00, 0x8b, + 0x54, 0x24, 0x28, 0x39, 0xc2, 0x7d, 0x37, 0x8b, 0x5c, 0x24, 0x1c, 0x8b, 0x74, 0x24, 0x20, 0x39, + 0xf3, 0x74, 0x16, 0x8d, 0x6b, 0x01, 0x89, 0x6c, 0x24, 0x1c, 0x0f, 0xb6, 0x1b, 0x89, 0xd1, 0xd3, + 0xe3, 0x09, 0x5c, 0x24, 0x24, 0x89, 0xeb, 0xeb, 0x0a, 0xc7, 0x44, 0x24, 0x2c, 0x01, 0x00, 0x00, + 0x00, 0x89, 0xf3, 0x83, 0xc2, 0x08, 0x89, 0x54, 0x24, 0x28, 0x39, 0xc2, 0x7c, 0xd1, 0x8b, 0x6c, + 0x24, 0x24, 0x31, 0xf6, 0x4e, 0x89, 0xc1, 0xd3, 0xe6, 0xf7, 0xd6, 0x21, 0xee, 0x03, 0x74, 0x24, + 0x10, 0xd3, 0xed, 0x89, 0x6c, 0x24, 0x24, 0x29, 0xc2, 0x89, 0x54, 0x24, 0x28, 0x8b, 0x6c, 0x24, + 0x18, 0x89, 0xe9, 0x29, 0xf9, 0x39, 0xce, 0x8b, 0x5c, 0x24, 0x08, 0x8b, 0x4c, 0x24, 0x0c, 0x76, + 0x0b, 0xeb, 0x7e, 0x39, 0xfd, 0xbe, 0x01, 0x00, 0x00, 0x00, 0x74, 0x75, 0x88, 0x8c, 0x3c, 0x00, + 0x05, 0x00, 0x00, 0x47, 0x4e, 0x75, 0xf5, 0x39, 0xfd, 0x0f, 0x87, 0xee, 0xfe, 0xff, 0xff, 0x80, + 0xbc, 0x24, 0x00, 0x06, 0x00, 0x00, 0x00, 0x74, 0x58, 0x8d, 0x4c, 0x24, 0x38, 0x8d, 0x94, 0x24, + 0x00, 0x05, 0x00, 0x00, 0x53, 0xe8, 0x2b, 0x02, 0x00, 0x00, 0x83, 0xc4, 0x04, 0x85, 0xc0, 0x75, + 0x34, 0x8d, 0x94, 0x1c, 0x00, 0x05, 0x00, 0x00, 0x8d, 0x8c, 0x24, 0x9c, 0x02, 0x00, 0x00, 0xff, + 0x74, 0x24, 0x04, 0xe8, 0x0d, 0x02, 0x00, 0x00, 0x83, 0xc4, 0x04, 0x85, 0xc0, 0x0f, 0x84, 0x0a, + 0xfc, 0xff, 0xff, 0xeb, 0x1f, 0x31, 0xc0, 0x83, 0x7c, 0x24, 0x2c, 0x00, 0x0f, 0x94, 0xc0, 0x8d, + 0x44, 0x40, 0xfd, 0xeb, 0x0f, 0x89, 0x04, 0x24, 0xeb, 0x07, 0xc7, 0x04, 0x24, 0xfd, 0xff, 0xff, + 0xff, 0x8b, 0x04, 0x24, 0x81, 0xc4, 0x40, 0x06, 0x00, 0x00, 0x5e, 0x5f, 0x5b, 0x5d, 0xc3, 0x55, + 0x53, 0x57, 0x56, 0x89, 0xce, 0x8b, 0x59, 0x0c, 0x39, 0xd3, 0x7d, 0x2f, 0x8b, 0x0e, 0x8b, 0x46, + 0x04, 0x39, 0xc1, 0x74, 0x13, 0x8d, 0x79, 0x01, 0x89, 0x3e, 0x0f, 0xb6, 0x29, 0x89, 0xd9, 0xd3, + 0xe5, 0x09, 0x6e, 0x08, 0x89, 0xf9, 0xeb, 0x09, 0xc7, 0x46, 0x10, 0x01, 0x00, 0x00, 0x00, 0x89, + 0xc1, 0x83, 0xc3, 0x08, 0x89, 0x5e, 0x0c, 0x39, 0xd3, 0x7c, 0xd6, 0x8b, 0x7e, 0x08, 0x31, 0xc0, + 0x48, 0x89, 0xd1, 0xd3, 0xe0, 0xf7, 0xd0, 0x21, 0xf8, 0xd3, 0xef, 0x89, 0x7e, 0x08, 0x29, 0xd3, + 0x89, 0x5e, 0x0c, 0x5e, 0x5f, 0x5b, 0x5d, 0xc3, 0x55, 0x53, 0x57, 0x56, 0x50, 0x89, 0xd6, 0x89, + 0xcf, 0xe8, 0x1c, 0x01, 0x00, 0x00, 0xb9, 0xfd, 0xff, 0xff, 0xff, 0x83, 0x7f, 0x10, 0x00, 0x0f, + 0x85, 0xff, 0x00, 0x00, 0x00, 0x8b, 0x6c, 0x24, 0x18, 0x89, 0x34, 0x24, 0x3d, 0xff, 0x00, 0x00, + 0x00, 0x7f, 0x10, 0x8b, 0x57, 0x18, 0x8d, 0x4a, 0x01, 0x89, 0x4f, 0x18, 0x88, 0x02, 0xe9, 0xc9, + 0x00, 0x00, 0x00, 0x3d, 0x00, 0x01, 0x00, 0x00, 0x0f, 0x84, 0xe0, 0x00, 0x00, 0x00, 0x3b, 0x86, + 0x60, 0x02, 0x00, 0x00, 0x0f, 0x8f, 0xc5, 0x00, 0x00, 0x00, 0x3d, 0x1d, 0x01, 0x00, 0x00, 0x0f, + 0x87, 0xba, 0x00, 0x00, 0x00, 0x83, 0xbd, 0x60, 0x02, 0x00, 0x00, 0xff, 0x0f, 0x84, 0xad, 0x00, + 0x00, 0x00, 0x89, 0xc3, 0x05, 0xff, 0xfe, 0xff, 0xff, 0x31, 0xf6, 0xb9, 0xff, 0x00, 0x00, 0x10, + 0x0f, 0xa3, 0xc1, 0x72, 0x10, 0x0f, 0xb6, 0x90, 0x10, 0x0a, 0x07, 0x00, 0x89, 0xf9, 0xe8, 0x1c, + 0xff, 0xff, 0xff, 0x89, 0xc6, 0x89, 0xf9, 0x89, 0xea, 0xe8, 0x94, 0x00, 0x00, 0x00, 0x83, 0xf8, + 0x1d, 0x7f, 0x7c, 0x89, 0xe9, 0x89, 0xc5, 0x3b, 0x81, 0x60, 0x02, 0x00, 0x00, 0x7f, 0x70, 0x31, + 0xc0, 0x83, 0xfd, 0x04, 0x72, 0x0e, 0x0f, 0xb6, 0x95, 0x2e, 0x0a, 0x07, 0x00, 0x89, 0xf9, 0xe8, + 0xeb, 0xfe, 0xff, 0xff, 0x0f, 0xb7, 0x8c, 0x2d, 0x4c, 0x0a, 0x07, 0x00, 0x01, 0xc1, 0x8b, 0x47, + 0x18, 0x89, 0xc2, 0x2b, 0x57, 0x14, 0x39, 0xd1, 0x7f, 0x45, 0x0f, 0xb7, 0x94, 0x1b, 0x9a, 0x08, + 0x07, 0x00, 0x01, 0xd6, 0x85, 0xf6, 0x7e, 0x18, 0xf7, 0xd9, 0x31, 0xc0, 0x8b, 0x57, 0x18, 0x8d, + 0x2c, 0x02, 0x8a, 0x1c, 0x29, 0x88, 0x1c, 0x02, 0x40, 0x39, 0xc6, 0x75, 0xef, 0x8b, 0x47, 0x18, + 0x01, 0xf0, 0x89, 0x47, 0x18, 0x8b, 0x34, 0x24, 0x8b, 0x6c, 0x24, 0x18, 0x89, 0xf9, 0x89, 0xf2, + 0xe8, 0x1d, 0x00, 0x00, 0x00, 0x83, 0x7f, 0x10, 0x00, 0x0f, 0x84, 0x0d, 0xff, 0xff, 0xff, 0xb9, + 0xfd, 0xff, 0xff, 0xff, 0x89, 0xc8, 0x83, 0xc4, 0x04, 0x5e, 0x5f, 0x5b, 0x5d, 0xc3, 0x31, 0xc9, + 0xeb, 0xf2, 0x55, 0x53, 0x57, 0x56, 0x50, 0x89, 0xd5, 0x89, 0xcb, 0x31, 0xd2, 0x42, 0xe8, 0x6c, + 0xfe, 0xff, 0xff, 0x89, 0xc7, 0x0f, 0xb7, 0x45, 0x02, 0x31, 0xf6, 0x39, 0xc7, 0x89, 0x2c, 0x24, + 0x7c, 0x21, 0x83, 0xc5, 0x04, 0x31, 0xf6, 0x01, 0xc6, 0x29, 0xc7, 0x89, 0xd9, 0x31, 0xd2, 0x42, + 0xe8, 0x4a, 0xfe, 0xff, 0xff, 0x8d, 0x3c, 0x78, 0x0f, 0xb7, 0x45, 0x00, 0x83, 0xc5, 0x02, 0x39, + 0xc7, 0x7d, 0xe4, 0x01, 0xfe, 0x8b, 0x04, 0x24, 0x0f, 0xb7, 0x44, 0x70, 0x20, 0x83, 0xc4, 0x04, + 0x5e, 0x5f, 0x5b, 0x5d, 0xc3, 0x55, 0x53, 0x57, 0x56, 0x83, 0xec, 0x20, 0x8b, 0x7c, 0x24, 0x34, + 0xb8, 0xe0, 0xff, 0xff, 0xff, 0x66, 0xc7, 0x44, 0x01, 0x20, 0x00, 0x00, 0x83, 0xc0, 0x02, 0x75, + 0xf4, 0xc7, 0x81, 0x60, 0x02, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x85, 0xff, 0x74, 0x1b, 0x31, + 0xc0, 0x80, 0x3c, 0x02, 0x00, 0x74, 0x0e, 0x89, 0x81, 0x60, 0x02, 0x00, 0x00, 0x0f, 0xb6, 0x34, + 0x02, 0x66, 0xff, 0x04, 0x71, 0x40, 0x39, 0xc7, 0x75, 0xe7, 0x31, 0xff, 0x31, 0xdb, 0x43, 0xbd, + 0xe0, 0xff, 0xff, 0xff, 0xb8, 0xfd, 0xff, 0xff, 0xff, 0x0f, 0xb7, 0x74, 0x29, 0x20, 0x29, 0xf3, + 0x72, 0x66, 0x01, 0xdb, 0x66, 0x89, 0x7c, 0x2c, 0x20, 0x01, 0xf7, 0x83, 0xc5, 0x02, 0x75, 0xe9, + 0x83, 0xff, 0x02, 0x72, 0x04, 0x85, 0xdb, 0x75, 0x4f, 0x83, 0xff, 0x01, 0x75, 0x07, 0x66, 0x83, + 0x79, 0x02, 0x01, 0x75, 0x43, 0x8b, 0x5c, 0x24, 0x34, 0x85, 0xdb, 0x74, 0x23, 0x31, 0xc0, 0x0f, + 0xb6, 0x34, 0x02, 0x85, 0xf6, 0x74, 0x14, 0x0f, 0xb7, 0x1c, 0x74, 0x8d, 0x6b, 0x01, 0x66, 0x89, + 0x2c, 0x74, 0x66, 0x89, 0x44, 0x59, 0x20, 0x8b, 0x5c, 0x24, 0x34, 0x40, 0x39, 0xc3, 0x75, 0xdf, + 0x31, 0xc0, 0x83, 0xff, 0x01, 0x75, 0x11, 0x66, 0xc7, 0x41, 0x02, 0x02, 0x00, 0x8b, 0x91, 0x60, + 0x02, 0x00, 0x00, 0x42, 0x66, 0x89, 0x51, 0x22, 0x83, 0xc4, 0x20, 0x5e, 0x5f, 0x5b, 0x5d, 0xc3, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, + 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, 0x00, 0x7f, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x04, 0x04, 0x05, 0x05, 0x06, 0x06, 0x07, 0x07, + 0x08, 0x08, 0x09, 0x09, 0x0a, 0x0a, 0x0b, 0x0b, 0x0c, 0x0c, 0x0d, 0x0d, 0x01, 0x00, 0x02, 0x00, + 0x03, 0x00, 0x04, 0x00, 0x05, 0x00, 0x07, 0x00, 0x09, 0x00, 0x0d, 0x00, 0x11, 0x00, 0x19, 0x00, + 0x21, 0x00, 0x31, 0x00, 0x41, 0x00, 0x61, 0x00, 0x81, 0x00, 0xc1, 0x00, 0x01, 0x01, 0x81, 0x01, + 0x01, 0x02, 0x01, 0x03, 0x01, 0x04, 0x01, 0x06, 0x01, 0x08, 0x01, 0x0c, 0x01, 0x10, 0x01, 0x18, + 0x01, 0x20, 0x01, 0x30, 0x01, 0x40, 0x01, 0x60, 0x10, 0x11, 0x12, 0x00, 0x08, 0x07, 0x09, 0x06, + 0x0a, 0x05, 0x0b, 0x04, 0x0c, 0x03, 0x0d, 0x02, 0x0e, 0x01, 0x0f, 0x00, 0x03, 0x00, 0x04, 0x00, + 0x05, 0x00, 0x06, 0x00, 0x07, 0x00, 0x08, 0x00, 0x09, 0x00, 0x0a, 0x00, 0x0b, 0x00, 0x0d, 0x00, + 0x0f, 0x00, 0x11, 0x00, 0x13, 0x00, 0x17, 0x00, 0x1b, 0x00, 0x1f, 0x00, 0x23, 0x00, 0x2b, 0x00, + 0x33, 0x00, 0x3b, 0x00, 0x43, 0x00, 0x53, 0x00, 0x63, 0x00, 0x73, 0x00, 0x83, 0x00, 0xa3, 0x00, + 0xc3, 0x00, 0xe3, 0x00, 0x02, 0x01, 0x00, 0x00, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0x8d, 0x7d, 0x0d, 0x5c, 0x53, 0x57, + 0xb2, 0xf8, 0xbd, 0x10, 0x30, 0x62, 0xf4, 0x46, 0xc5, 0x16, 0x94, 0x6a, 0xb4, 0xd8, 0x95, 0xd6, + 0x2a, 0x51, 0xab, 0xa6, 0x6a, 0x17, 0x84, 0x00, 0xb6, 0xc6, 0x06, 0x90, 0x60, 0xb7, 0x22, 0xa5, + 0x4a, 0x0c, 0x14, 0x81, 0x26, 0xb9, 0x7e, 0xb4, 0xc5, 0x82, 0x97, 0xb4, 0x1c, 0x6e, 0x6f, 0xb7, + 0xdb, 0x8f, 0xdd, 0x6e, 0x77, 0x7d, 0xdb, 0x6e, 0xbb, 0xbb, 0xdd, 0xee, 0xee, 0xd3, 0xdd, 0x6e, + 0xfd, 0xfe, 0x48, 0xc0, 0x07, 0x88, 0x56, 0x51, 0x5c, 0x8d, 0x4a, 0x2b, 0x56, 0xda, 0x5e, 0x1a, + 0x5e, 0x1b, 0xb5, 0x8f, 0x2f, 0x2d, 0xf9, 0xcf, 0x9c, 0x9b, 0x08, 0xe8, 0xbe, 0xff, 0xab, 0xbf, + 0x5f, 0xc8, 0x3d, 0x73, 0xce, 0x99, 0x33, 0x33, 0x67, 0x66, 0xce, 0xcc, 0x39, 0xe7, 0xc6, 0x9b, + 0x89, 0xee, 0xc3, 0x2d, 0xdf, 0x86, 0x31, 0x7b, 0xdf, 0x2e, 0x09, 0x67, 0xaa, 0x3a, 0xf1, 0xf1, + 0xfa, 0x47, 0x1c, 0x3b, 0x61, 0x7b, 0x25, 0xc3, 0x7c, 0x73, 0x2f, 0xfc, 0x89, 0x61, 0xf6, 0xe8, + 0x18, 0x86, 0x79, 0xd5, 0xfb, 0xaa, 0xfb, 0xd5, 0xf6, 0x57, 0xe5, 0x57, 0x5b, 0x3a, 0x73, 0x26, + 0x31, 0xcc, 0xf1, 0xd0, 0xbf, 0x1c, 0xf2, 0xa5, 0xd0, 0xa5, 0xb6, 0x36, 0x1a, 0x7b, 0x03, 0xe1, + 0x8d, 0xc6, 0x7e, 0x68, 0xc9, 0x48, 0xc6, 0x5e, 0xd2, 0xdc, 0x7f, 0x93, 0x63, 0x63, 0x8a, 0xd5, + 0xb6, 0x42, 0x40, 0x71, 0x6c, 0x8f, 0x76, 0x48, 0x7f, 0x4e, 0xe7, 0x8e, 0x1f, 0xe0, 0xa6, 0xb9, + 0xad, 0x7b, 0xaa, 0xa1, 0xae, 0x98, 0x31, 0x1f, 0xd3, 0xbb, 0x07, 0x6b, 0x6f, 0x14, 0x33, 0x36, + 0xe6, 0x85, 0x63, 0x01, 0x66, 0xf5, 0x33, 0xe1, 0xcc, 0x02, 0xe6, 0xc3, 0x0b, 0xcc, 0xb0, 0x7f, + 0xbf, 0x9d, 0x65, 0x0d, 0x44, 0xfe, 0xa9, 0x72, 0xfb, 0xac, 0xc0, 0xac, 0xbf, 0x54, 0x8a, 0x13, + 0x90, 0x32, 0x32, 0xe1, 0x2f, 0x80, 0xa7, 0x31, 0x02, 0x9f, 0x77, 0xc0, 0x93, 0xb8, 0x32, 0x5e, + 0xd5, 0x18, 0xf1, 0xa7, 0x4a, 0xa5, 0x03, 0xc7, 0x46, 0x7c, 0x5a, 0x85, 0x5f, 0xa3, 0xff, 0x45, + 0xbf, 0xee, 0xf9, 0x0c, 0xbe, 0xb2, 0x2d, 0xb9, 0x39, 0xdf, 0x30, 0xf0, 0xa0, 0x66, 0xfe, 0x2d, + 0x69, 0xdf, 0x4c, 0x84, 0xba, 0x61, 0x54, 0x35, 0x44, 0x5e, 0xa9, 0x62, 0x1a, 0x22, 0xbf, 0xc6, + 0x3f, 0xbe, 0x2a, 0xc6, 0x6a, 0xb8, 0xeb, 0x4f, 0x95, 0xce, 0x59, 0xd6, 0x4c, 0x6b, 0xd6, 0x3f, + 0xd9, 0x44, 0xf7, 0xa7, 0x77, 0x59, 0x7f, 0x66, 0x7d, 0xc2, 0xd9, 0xa9, 0x87, 0xa7, 0xdf, 0x7f, + 0x5d, 0x55, 0x73, 0x9f, 0xaf, 0x6a, 0x0f, 0x13, 0xf6, 0xe9, 0x5d, 0x1f, 0xaa, 0x9c, 0x51, 0xd8, + 0x53, 0x55, 0x09, 0x3d, 0x9f, 0xea, 0x9f, 0x6f, 0xe5, 0xd8, 0xbb, 0x3e, 0xad, 0xc2, 0xaf, 0xc9, + 0xff, 0xaa, 0xe2, 0x74, 0xf5, 0x95, 0xc7, 0x58, 0x6e, 0x5a, 0xfd, 0x37, 0x35, 0x55, 0x31, 0x8c, + 0x75, 0xbf, 0x8e, 0x79, 0xf5, 0xe2, 0xab, 0xf5, 0xaf, 0x5e, 0x79, 0xd5, 0xf7, 0x6a, 0x6b, 0x5e, + 0xfe, 0x9a, 0x27, 0x45, 0xca, 0x17, 0xa1, 0x3c, 0x56, 0x2e, 0x81, 0x41, 0x19, 0xc6, 0x39, 0x29, + 0xf0, 0x10, 0xd0, 0xc1, 0x04, 0x1e, 0xfa, 0x9a, 0xfe, 0x45, 0xa0, 0x5c, 0xdc, 0xc3, 0x30, 0xc2, + 0x11, 0x4d, 0xbd, 0xde, 0x5d, 0xcf, 0xdc, 0xf6, 0x2f, 0x10, 0x7e, 0x3b, 0xe4, 0x38, 0x48, 0xe0, + 0xfe, 0x6d, 0x30, 0xb5, 0x03, 0xff, 0x96, 0x7b, 0x6e, 0x9f, 0x9f, 0xa1, 0xfc, 0x6f, 0xbb, 0x8d, + 0xff, 0xef, 0x7d, 0x3f, 0x28, 0xc8, 0x6a, 0x53, 0xe3, 0x55, 0xbf, 0xff, 0x18, 0xaa, 0xc5, 0xd4, + 0x78, 0xf5, 0xfb, 0xf1, 0x02, 0x7d, 0xd0, 0xbc, 0x3f, 0x43, 0xa0, 0xa2, 0x9e, 0x49, 0xbf, 0x46, + 0xcf, 0xa3, 0x5f, 0xf7, 0x2c, 0x16, 0x82, 0xa2, 0x7e, 0x66, 0xdb, 0xff, 0x2e, 0x6a, 0x97, 0x32, + 0x54, 0xcb, 0x7c, 0x2b, 0xb9, 0x4f, 0x27, 0xcc, 0xb7, 0x8a, 0xf7, 0xcd, 0x10, 0xb8, 0x3f, 0x73, + 0xef, 0x8d, 0x98, 0x62, 0xdd, 0x6e, 0xcd, 0xb3, 0xe6, 0x5b, 0xd7, 0xa0, 0x6c, 0xad, 0x4f, 0x5a, + 0x57, 0x61, 0x9d, 0x4e, 0xb8, 0xf1, 0x29, 0xd3, 0x3f, 0xd8, 0x36, 0x5e, 0xb0, 0xae, 0x97, 0xd6, + 0xc5, 0xcf, 0xb0, 0x9a, 0xad, 0xd9, 0x38, 0x1b, 0x56, 0x8b, 0x35, 0xd7, 0x9a, 0x63, 0xfd, 0xed, + 0xe4, 0x48, 0xee, 0x5d, 0xee, 0x43, 0xa5, 0x07, 0x15, 0xfb, 0x4c, 0xfa, 0x35, 0x79, 0x9e, 0x00, + 0x03, 0x6b, 0x58, 0x1c, 0x78, 0x60, 0x1b, 0x48, 0x1d, 0x74, 0x7b, 0x90, 0x26, 0x94, 0x7a, 0xfd, + 0x6b, 0xc3, 0xff, 0x31, 0xff, 0x9f, 0x7f, 0x8a, 0x44, 0x8e, 0x0f, 0xfb, 0x07, 0xd2, 0x50, 0xbd, + 0xef, 0x07, 0xc6, 0xeb, 0x41, 0x1c, 0xdf, 0x2b, 0xe2, 0xb8, 0xa9, 0x88, 0x43, 0x55, 0xad, 0x88, + 0x43, 0x2a, 0x89, 0x8f, 0xfe, 0x66, 0xab, 0xf0, 0xbf, 0x8b, 0xe4, 0x6d, 0x41, 0x11, 0x49, 0xa8, + 0xea, 0x46, 0x60, 0xa3, 0x2a, 0xb0, 0x51, 0x1d, 0xd8, 0xc8, 0x04, 0x26, 0xfb, 0x05, 0xe1, 0x48, + 0x64, 0xff, 0x2c, 0x64, 0xe9, 0x7b, 0x01, 0xbf, 0x26, 0xdf, 0x14, 0x06, 0x35, 0xe9, 0xb4, 0x70, + 0xbb, 0x26, 0x4d, 0x0f, 0x04, 0x18, 0x66, 0x08, 0x63, 0xff, 0x3f, 0x3e, 0xc0, 0x96, 0xb3, 0x73, + 0x2d, 0x42, 0xd7, 0x4c, 0x31, 0x4f, 0x5d, 0xf9, 0xfc, 0x68, 0x86, 0x73, 0x1d, 0x66, 0x61, 0x7e, + 0x4d, 0x31, 0x3b, 0xd9, 0x51, 0xa6, 0x38, 0xae, 0xfa, 0xcf, 0x58, 0x32, 0x46, 0x8b, 0x26, 0xad, + 0xf8, 0x7c, 0x34, 0xb7, 0xe3, 0x79, 0x95, 0xbe, 0x5b, 0x7f, 0x9a, 0x54, 0xf8, 0x09, 0x2f, 0xb3, + 0x27, 0x48, 0x4e, 0xd7, 0xd8, 0xba, 0x62, 0x26, 0x97, 0xe4, 0xb4, 0x65, 0x11, 0xde, 0x6b, 0x91, + 0xef, 0x5d, 0x89, 0x7a, 0xa9, 0x25, 0x0d, 0xc2, 0xf3, 0x3a, 0x96, 0x1f, 0x27, 0xa6, 0xcc, 0xd0, + 0xfb, 0xc5, 0xc7, 0x66, 0xea, 0x4f, 0x8d, 0xf4, 0x70, 0xd5, 0x17, 0xd1, 0x70, 0xf9, 0x0e, 0x92, + 0xd3, 0xde, 0x98, 0x82, 0x0a, 0xce, 0x08, 0xcf, 0xc7, 0x33, 0xfc, 0x78, 0x31, 0x05, 0xf0, 0xa6, + 0xa8, 0xcc, 0xf2, 0xe4, 0x0c, 0xec, 0xac, 0x22, 0x29, 0xf1, 0xe2, 0x63, 0xf3, 0x88, 0xa9, 0x05, + 0x2a, 0xc4, 0xec, 0x44, 0x92, 0x73, 0x46, 0xda, 0x19, 0x00, 0x2a, 0x5c, 0x6e, 0x6e, 0x99, 0x9f, + 0xf4, 0x78, 0x02, 0x23, 0x3d, 0x03, 0x53, 0x2c, 0xb9, 0x99, 0x59, 0xf2, 0x27, 0xd9, 0xca, 0x68, + 0xa7, 0xb8, 0x1d, 0x7e, 0x62, 0x6c, 0xea, 0xf9, 0x9a, 0xbd, 0x46, 0x4c, 0xc7, 0xb9, 0x1d, 0x7d, + 0xec, 0x39, 0xd1, 0x74, 0x26, 0xc1, 0x23, 0x1a, 0x5b, 0x62, 0x7b, 0xf5, 0xdd, 0x23, 0x9b, 0x38, + 0x57, 0x3b, 0x0c, 0x27, 0x3e, 0xaf, 0x12, 0x1f, 0x53, 0x89, 0x79, 0xed, 0xdc, 0x8e, 0xf3, 0xa2, + 0xb1, 0xa3, 0xe7, 0x32, 0x7b, 0x01, 0x98, 0x0e, 0x37, 0x36, 0x8d, 0xcf, 0x39, 0x9e, 0x9b, 0x65, + 0x0e, 0x38, 0xe2, 0xb3, 0xe5, 0xf3, 0x31, 0x88, 0x32, 0xda, 0xe5, 0x76, 0x4e, 0x14, 0x7a, 0x59, + 0xae, 0xfa, 0x43, 0x24, 0xb3, 0x31, 0x20, 0x9c, 0x0d, 0x90, 0xde, 0x91, 0x7e, 0xfe, 0x70, 0xe7, + 0xaf, 0x00, 0x00, 0xe4, 0xb3, 0x88, 0x0f, 0x78, 0x71, 0xcc, 0x10, 0x73, 0xda, 0x49, 0xf6, 0x4c, + 0xb1, 0xc2, 0x2f, 0x1a, 0x63, 0xc8, 0x75, 0xd2, 0xf0, 0x00, 0x2f, 0x8b, 0xa6, 0xb8, 0x49, 0xa6, + 0x2e, 0xe2, 0x25, 0xad, 0x3d, 0x5f, 0x73, 0x3b, 0x2e, 0xb1, 0xad, 0x62, 0x8e, 0x37, 0xa1, 0x4e, + 0x34, 0xb6, 0xc5, 0x7a, 0xa1, 0x5d, 0xc2, 0xb9, 0xfd, 0xc8, 0x7c, 0x6c, 0xbd, 0x61, 0x80, 0xf4, + 0x72, 0x4b, 0x1b, 0x0c, 0xe7, 0xb9, 0xa5, 0x03, 0x22, 0x2f, 0x73, 0xa9, 0x50, 0x02, 0xc2, 0x34, + 0xac, 0x5f, 0x34, 0xa9, 0xc3, 0x33, 0xe3, 0x81, 0x26, 0xf9, 0x94, 0x09, 0xc6, 0xc9, 0xe9, 0x02, + 0x5b, 0x67, 0x07, 0xa0, 0xef, 0x58, 0xa4, 0x78, 0x11, 0x1f, 0x43, 0x5a, 0x26, 0x19, 0xe3, 0x94, + 0x69, 0xe1, 0xb6, 0x3d, 0x3b, 0x10, 0x08, 0xec, 0x64, 0x7d, 0x61, 0x7a, 0xb7, 0x70, 0x64, 0xe6, + 0x9a, 0xfc, 0x27, 0xf3, 0xea, 0x6d, 0x7b, 0x3e, 0x43, 0x2f, 0x2b, 0x9f, 0x1c, 0xc7, 0x30, 0xa1, + 0x09, 0x5e, 0x2d, 0x55, 0xbc, 0x5d, 0xbc, 0x1a, 0x26, 0x4b, 0x7e, 0xdd, 0x44, 0x9d, 0x87, 0xfe, + 0xe8, 0xc7, 0xac, 0xbe, 0x3b, 0x4d, 0x54, 0x49, 0x1f, 0x5e, 0x81, 0x89, 0xf1, 0x78, 0xfa, 0xa6, + 0x14, 0x33, 0xc5, 0xab, 0x33, 0xcd, 0xb9, 0x01, 0x5e, 0x23, 0x77, 0xfc, 0x10, 0x08, 0x08, 0x47, + 0xd0, 0x9b, 0x4b, 0x5f, 0x30, 0xd0, 0xe9, 0xd5, 0x68, 0xda, 0x09, 0x04, 0xa3, 0xd5, 0xbb, 0xbb, + 0xeb, 0x59, 0xca, 0x03, 0xb9, 0xca, 0xbb, 0x7d, 0x09, 0x55, 0x15, 0xef, 0x32, 0x0c, 0xcb, 0xf0, + 0x93, 0x81, 0x43, 0x31, 0x47, 0x4d, 0x96, 0x6a, 0x44, 0xa3, 0x97, 0x2c, 0x55, 0x83, 0xf8, 0x61, + 0x1a, 0xc8, 0xa3, 0x2a, 0x12, 0x16, 0x22, 0x6f, 0x35, 0x25, 0x2f, 0x44, 0x52, 0xaa, 0xc8, 0xab, + 0xa5, 0x8a, 0x6b, 0xd6, 0xc6, 0x11, 0xa0, 0xfb, 0x17, 0xb3, 0x8b, 0xd9, 0x6c, 0xdb, 0x21, 0x10, + 0x6c, 0xae, 0x45, 0xb6, 0x2a, 0xa3, 0x73, 0xbb, 0x46, 0x7c, 0xf8, 0x22, 0xe7, 0x7a, 0x19, 0x80, + 0xb4, 0xf6, 0x84, 0x52, 0x9b, 0x11, 0xaa, 0x35, 0x5e, 0xc3, 0x6a, 0x1b, 0x7b, 0xab, 0xfb, 0xe7, + 0x4a, 0x83, 0x9f, 0x0c, 0x6f, 0x90, 0x1e, 0xea, 0xff, 0xad, 0x52, 0xad, 0x1e, 0x5e, 0x3d, 0x83, + 0xf6, 0xdf, 0xab, 0x46, 0x86, 0xfc, 0x12, 0xff, 0x8f, 0xec, 0xcc, 0xec, 0x62, 0x95, 0xc5, 0x2c, + 0x5f, 0xbe, 0x49, 0x9b, 0xed, 0xc1, 0x0a, 0xb3, 0x6d, 0x2a, 0xa8, 0xaf, 0x45, 0x7e, 0x7d, 0x79, + 0x50, 0x0c, 0x5c, 0xf5, 0x55, 0x00, 0xef, 0x51, 0xc1, 0x9f, 0x6c, 0x73, 0x76, 0x71, 0x38, 0xf4, + 0xb3, 0x04, 0x78, 0xb5, 0xfc, 0xd7, 0x60, 0x27, 0x15, 0xed, 0xb4, 0xb1, 0x1d, 0x3b, 0xe5, 0x0d, + 0x76, 0x3a, 0xc0, 0xe0, 0x58, 0x7b, 0xc2, 0x83, 0xdd, 0xe6, 0x87, 0xba, 0xf1, 0xc1, 0x6e, 0xe1, + 0xb4, 0xdb, 0xb2, 0x36, 0xec, 0x76, 0xdf, 0x60, 0x37, 0x17, 0xb6, 0x57, 0x2a, 0xb3, 0x8b, 0xb3, + 0x42, 0x9d, 0x92, 0xfe, 0x4d, 0xa7, 0xab, 0x8f, 0x85, 0xe6, 0x69, 0x25, 0x8c, 0x13, 0x71, 0x3b, + 0x79, 0xa3, 0x83, 0x5d, 0x22, 0x68, 0x17, 0xe3, 0x25, 0xec, 0x72, 0xe8, 0x56, 0x97, 0xa9, 0xd9, + 0xc5, 0x61, 0xd9, 0xb6, 0x85, 0x40, 0x7b, 0x2e, 0x36, 0x6e, 0xbf, 0xa1, 0xc8, 0x69, 0xb7, 0xf1, + 0xda, 0x92, 0xec, 0xef, 0xc0, 0x9f, 0xbd, 0x55, 0x4f, 0xbc, 0xc2, 0x91, 0xd4, 0xd0, 0x24, 0xe6, + 0x5a, 0xcc, 0x62, 0x85, 0x5a, 0xdf, 0x9d, 0x2b, 0xdf, 0x3f, 0x80, 0x2d, 0x55, 0xd5, 0x60, 0x36, + 0x7a, 0xb7, 0xc4, 0x7f, 0x6f, 0x2e, 0x56, 0x99, 0x6d, 0x7b, 0x40, 0xae, 0x96, 0x5c, 0xf9, 0x1d, + 0x05, 0x8b, 0x38, 0x9f, 0xf8, 0xa1, 0xc9, 0x9a, 0xfc, 0xd0, 0xfc, 0x57, 0x75, 0x45, 0x43, 0x03, + 0xc9, 0xd5, 0x0e, 0x7d, 0x51, 0x31, 0xcd, 0x72, 0xdf, 0xa3, 0x8a, 0x62, 0xba, 0x3f, 0x66, 0xc5, + 0xc5, 0x2e, 0xaa, 0x95, 0x03, 0x60, 0xeb, 0xa8, 0x95, 0x96, 0x5c, 0xa5, 0xa1, 0x19, 0x75, 0x33, + 0xe3, 0xc6, 0x50, 0xdd, 0x0c, 0x56, 0xc8, 0x5d, 0x63, 0x43, 0x6c, 0x44, 0x87, 0xf4, 0x13, 0x29, + 0x60, 0xf8, 0x8f, 0x3b, 0x4b, 0xe1, 0xbb, 0xea, 0xa0, 0x3c, 0x80, 0x7e, 0x91, 0x05, 0x17, 0xb7, + 0x06, 0xca, 0xe4, 0xc6, 0x61, 0x50, 0x83, 0x80, 0x68, 0xd4, 0x1a, 0x5c, 0x89, 0xf0, 0xc0, 0xbd, + 0xd4, 0x01, 0x7c, 0xef, 0xc5, 0x10, 0x42, 0x7f, 0x5a, 0x72, 0x2d, 0x06, 0x50, 0x26, 0x18, 0x60, + 0xa1, 0x42, 0x93, 0xe8, 0x9a, 0x81, 0x6d, 0xc0, 0xcf, 0x5c, 0x12, 0xf3, 0xb4, 0xe8, 0x69, 0x44, + 0x69, 0x26, 0x85, 0x9c, 0x24, 0x9f, 0x7b, 0xbe, 0x1a, 0xc1, 0x36, 0x8c, 0x3d, 0xa5, 0x77, 0x9b, + 0xf7, 0x60, 0x77, 0x73, 0xa6, 0x45, 0xe9, 0x6f, 0x16, 0x79, 0x8d, 0x45, 0x7e, 0xa7, 0x9f, 0x52, + 0x2b, 0x19, 0x0f, 0xe8, 0x8f, 0x92, 0x0c, 0x0d, 0xc9, 0x50, 0x93, 0x0c, 0x15, 0x51, 0x4b, 0xd2, + 0x72, 0x68, 0x52, 0xac, 0x35, 0x67, 0xca, 0x4b, 0x1e, 0xbd, 0x35, 0xd1, 0x1c, 0xd0, 0x20, 0x46, + 0x82, 0xfd, 0x13, 0x56, 0x4c, 0x53, 0x91, 0x64, 0x95, 0x98, 0xa6, 0x26, 0xc9, 0xea, 0xda, 0x34, + 0x4d, 0x4d, 0xb2, 0x26, 0x85, 0x3c, 0xa1, 0x15, 0xd3, 0xa2, 0x49, 0x72, 0xb4, 0xe8, 0x5a, 0x0d, + 0x9d, 0x89, 0xb1, 0x4b, 0xfa, 0x1d, 0x75, 0x8d, 0x01, 0x6e, 0xd9, 0x25, 0x72, 0x09, 0x5d, 0xe3, + 0x8d, 0x29, 0xc4, 0xc3, 0xed, 0x38, 0x06, 0x14, 0x36, 0x8b, 0x9f, 0x3c, 0x85, 0x8d, 0xae, 0x02, + 0x8d, 0xec, 0x29, 0x6a, 0x98, 0x89, 0xe4, 0xd1, 0x79, 0xa2, 0x6b, 0x1d, 0x40, 0x1f, 0x30, 0x76, + 0x89, 0x92, 0x0d, 0x1e, 0x62, 0xaf, 0x0a, 0x6e, 0x56, 0x38, 0xc5, 0x40, 0x23, 0x6e, 0x87, 0x17, + 0xba, 0x5d, 0x00, 0xa3, 0x64, 0x9b, 0x49, 0xda, 0x42, 0x62, 0x59, 0x0c, 0xce, 0x88, 0xa4, 0x01, + 0xe5, 0x6d, 0x60, 0x0d, 0xa6, 0xc9, 0x48, 0xa4, 0xba, 0x1a, 0x88, 0x7c, 0x2e, 0x1c, 0xd7, 0x04, + 0x99, 0xa4, 0x99, 0xc1, 0x8d, 0x92, 0xb4, 0xe5, 0x68, 0xf7, 0xa6, 0x76, 0xb2, 0x22, 0x83, 0xa4, + 0xa5, 0xee, 0x64, 0x3b, 0xf3, 0xa0, 0xfa, 0xf0, 0x00, 0xca, 0x16, 0xd9, 0xe7, 0x6e, 0x06, 0x55, + 0x83, 0xab, 0x16, 0xb0, 0x5f, 0x9e, 0x56, 0xb8, 0xa1, 0xe2, 0x5e, 0x59, 0x1f, 0x06, 0x33, 0x97, + 0xc2, 0x12, 0xf0, 0x11, 0x46, 0xb5, 0xe0, 0x4e, 0x25, 0x46, 0xaf, 0x94, 0x72, 0x93, 0x18, 0xcf, + 0xe8, 0xc1, 0xd3, 0x7a, 0xda, 0x55, 0x11, 0xe8, 0x09, 0xf4, 0x47, 0x33, 0x8b, 0xb5, 0x99, 0x66, + 0x20, 0xc0, 0x6c, 0x91, 0xb3, 0xfa, 0x42, 0x56, 0xdb, 0x2e, 0xf4, 0x72, 0xce, 0x91, 0x42, 0x6f, + 0x04, 0xe7, 0xea, 0xc7, 0x99, 0x34, 0x1e, 0x27, 0x15, 0x5d, 0x52, 0x2e, 0xbb, 0x07, 0xe7, 0x7c, + 0x6f, 0x2c, 0x86, 0xa3, 0x07, 0x33, 0x60, 0xfc, 0xeb, 0x7f, 0x11, 0x23, 0x09, 0x9d, 0x09, 0x94, + 0xa5, 0x2b, 0x89, 0x3e, 0xa8, 0x89, 0x2b, 0x15, 0x1e, 0x50, 0xa2, 0x2e, 0x6c, 0xa4, 0x0f, 0x00, + 0x3d, 0xe4, 0xad, 0xe5, 0xb4, 0x32, 0x9a, 0xb8, 0xcc, 0x28, 0xb3, 0x83, 0xe5, 0x54, 0x35, 0x3a, + 0x88, 0xab, 0x84, 0x3e, 0xc8, 0xe4, 0xe0, 0x66, 0x84, 0xbb, 0x9c, 0xf8, 0xf7, 0x93, 0x95, 0xf0, + 0xf7, 0x10, 0x0e, 0xe6, 0x5c, 0xa3, 0x0f, 0x88, 0x79, 0x67, 0xf4, 0xdd, 0xc4, 0xcf, 0x7d, 0xd0, + 0x3b, 0x92, 0xf4, 0x79, 0x2e, 0x8f, 0xac, 0x3a, 0x8a, 0x6e, 0x0a, 0x94, 0x16, 0xa6, 0x36, 0xa8, + 0x9b, 0x41, 0x75, 0x90, 0x7f, 0xde, 0x1b, 0x64, 0xc1, 0xd5, 0x81, 0x06, 0x03, 0x5c, 0x44, 0x00, + 0x17, 0xbc, 0x5a, 0xa4, 0xba, 0xa9, 0xef, 0x1e, 0xe4, 0x8b, 0x03, 0x14, 0x8f, 0xf1, 0x7f, 0x1e, + 0x86, 0xf6, 0xd0, 0xff, 0x86, 0x76, 0xa1, 0x82, 0xb6, 0xf2, 0x20, 0x62, 0x65, 0xf6, 0x04, 0xa8, + 0x6a, 0xf8, 0x61, 0xa5, 0xe0, 0xaa, 0x4f, 0xe0, 0x3a, 0x45, 0x9b, 0x91, 0x70, 0x91, 0x4a, 0x80, + 0xa4, 0xa8, 0x44, 0x2a, 0x01, 0x92, 0xa2, 0xae, 0xa5, 0x12, 0xa8, 0x49, 0x01, 0x87, 0xdd, 0x42, + 0x52, 0xb4, 0xa2, 0xc2, 0x7c, 0x4a, 0x74, 0xf8, 0x41, 0x64, 0x7b, 0xfc, 0x27, 0x28, 0x85, 0x70, + 0x4a, 0x9b, 0x70, 0x96, 0x21, 0xcf, 0x27, 0x12, 0x07, 0xa8, 0x4f, 0xd7, 0x00, 0xb6, 0x59, 0xd8, + 0x98, 0xb2, 0x98, 0xa1, 0xc8, 0x57, 0xd2, 0x3e, 0x31, 0xd9, 0xa0, 0xe0, 0x66, 0xf9, 0x27, 0x71, + 0x41, 0x2d, 0x71, 0xc2, 0x6a, 0xd0, 0x04, 0xeb, 0x1e, 0x5d, 0x0d, 0x8e, 0xd0, 0xd5, 0xe0, 0x80, + 0x68, 0x72, 0x0f, 0x59, 0x0d, 0x6a, 0x52, 0x92, 0xb2, 0xe5, 0xcf, 0x26, 0xd1, 0x35, 0x1f, 0x8c, + 0x76, 0x4c, 0xa3, 0xd1, 0x4f, 0xd7, 0x94, 0x94, 0x12, 0xa8, 0x1f, 0x19, 0x2c, 0x61, 0x33, 0x5b, + 0x43, 0xca, 0x4a, 0xc6, 0x37, 0x02, 0x20, 0x3f, 0x28, 0x4a, 0x05, 0x3a, 0x24, 0x56, 0x74, 0x89, + 0xc6, 0xe3, 0x28, 0xaa, 0xea, 0x73, 0xb8, 0x44, 0xf7, 0x72, 0x0a, 0xaf, 0xe9, 0x42, 0x40, 0xc5, + 0xb9, 0xf6, 0x41, 0x43, 0x6a, 0xdd, 0x9d, 0x5a, 0xd4, 0x0f, 0x6f, 0x48, 0x9f, 0xc8, 0x25, 0xfd, + 0x45, 0x72, 0x55, 0xe2, 0xdb, 0xb2, 0x8b, 0xb5, 0xd9, 0x66, 0x4b, 0xa6, 0x3c, 0xd0, 0xad, 0x48, + 0x6e, 0xab, 0x8a, 0xe1, 0xaa, 0xdf, 0x40, 0x76, 0x4c, 0x1a, 0x91, 0x05, 0x0b, 0x20, 0x91, 0x62, + 0xb2, 0x8a, 0xa4, 0xa9, 0xc4, 0x64, 0x35, 0x49, 0x53, 0xd7, 0x26, 0x6b, 0x6a, 0xd2, 0x34, 0xe9, + 0x64, 0xab, 0x56, 0x4c, 0x8e, 0x26, 0x69, 0xd1, 0xa8, 0x19, 0x6b, 0xe6, 0x91, 0xb4, 0x44, 0xd4, + 0x8c, 0x35, 0x8b, 0xd1, 0x58, 0x56, 0xc4, 0x28, 0xb3, 0x01, 0x96, 0x72, 0xef, 0xa4, 0x90, 0x0c, + 0xee, 0x13, 0xa9, 0x80, 0xd1, 0x50, 0x14, 0x91, 0x83, 0xad, 0x28, 0xfa, 0x28, 0xd1, 0xa9, 0xa0, + 0x16, 0xf3, 0x31, 0x5b, 0xb3, 0x26, 0xc9, 0x22, 0xb7, 0x4e, 0xa4, 0x92, 0xd0, 0x07, 0x40, 0x16, + 0x63, 0x49, 0x1a, 0x0a, 0x41, 0x03, 0x2b, 0xbe, 0x2f, 0x53, 0x1f, 0x78, 0xdc, 0xb7, 0x1c, 0xe4, + 0x90, 0x66, 0x6b, 0x48, 0x03, 0x39, 0x24, 0x83, 0x83, 0xdd, 0x8e, 0xda, 0x5a, 0x93, 0x06, 0xbd, + 0xb6, 0x0f, 0xe9, 0x35, 0x42, 0xe9, 0x15, 0x16, 0x6a, 0xcb, 0x8a, 0xe1, 0x10, 0xef, 0x90, 0xb4, + 0x55, 0x64, 0xc5, 0x6a, 0x31, 0x05, 0xf8, 0x78, 0x4a, 0x4c, 0x01, 0x7b, 0x5e, 0x47, 0x7a, 0xab, + 0x8e, 0xa0, 0xeb, 0x1d, 0x74, 0xe3, 0xc2, 0x92, 0x33, 0x90, 0x03, 0x32, 0xce, 0x1c, 0xea, 0xce, + 0xdf, 0xc3, 0x84, 0x50, 0xd4, 0xec, 0xac, 0xdc, 0x92, 0x04, 0xd9, 0x87, 0xe0, 0x49, 0x2d, 0xd6, + 0xe6, 0x66, 0xca, 0x6f, 0x1b, 0x83, 0x4e, 0x8a, 0x56, 0x3b, 0x67, 0xd1, 0x06, 0x2b, 0x19, 0x67, + 0x8c, 0xe0, 0x59, 0x45, 0x1b, 0x6c, 0x1a, 0xde, 0x60, 0x74, 0xda, 0xa2, 0x87, 0x10, 0xab, 0xfd, + 0x13, 0xbd, 0xdb, 0x17, 0x2e, 0xaa, 0x76, 0x06, 0x3d, 0x7f, 0xae, 0xe5, 0x3d, 0x04, 0x03, 0xbd, + 0xf3, 0xe8, 0x68, 0xe2, 0x68, 0x3a, 0x5c, 0xf4, 0xdf, 0x2b, 0x9f, 0xb3, 0xc1, 0x70, 0xb9, 0x01, + 0x7b, 0x89, 0xfc, 0xdc, 0x28, 0x2a, 0x41, 0x97, 0x5b, 0xa9, 0x74, 0x8e, 0xa1, 0x5d, 0xd2, 0x0c, + 0x0d, 0xf6, 0x73, 0x0a, 0xb2, 0xbf, 0x0f, 0x2e, 0x23, 0xe2, 0x68, 0x8a, 0xee, 0x28, 0x08, 0x3b, + 0x47, 0x0b, 0xca, 0x51, 0x9b, 0xa7, 0x16, 0x97, 0x50, 0x94, 0x23, 0x0c, 0xe5, 0x6a, 0x3e, 0x6a, + 0xe1, 0x2a, 0x0d, 0x1f, 0x61, 0x30, 0x6b, 0x9d, 0x6a, 0xa1, 0x51, 0xb5, 0x8c, 0x87, 0x7c, 0x2d, + 0xc4, 0xf7, 0x60, 0xd7, 0x09, 0xe0, 0x1d, 0xc5, 0x87, 0x68, 0xa7, 0x48, 0x03, 0xb6, 0x6c, 0x80, + 0x96, 0xdf, 0x43, 0x4b, 0xda, 0xae, 0xd6, 0xa8, 0x26, 0x9e, 0xca, 0xcb, 0x9c, 0x5b, 0x56, 0x71, + 0xbb, 0xdc, 0xac, 0x5b, 0x52, 0x55, 0x32, 0x4d, 0xb4, 0x86, 0xdb, 0x65, 0x52, 0x17, 0x79, 0x3e, + 0x05, 0xbd, 0xf1, 0xc8, 0xa3, 0xa4, 0x68, 0x46, 0x8a, 0x7e, 0x7d, 0xc6, 0x29, 0x77, 0xbb, 0x4a, + 0xad, 0x54, 0xd7, 0x9a, 0xd4, 0xdc, 0x21, 0x0f, 0x69, 0xae, 0xac, 0xfb, 0x5d, 0x65, 0x7f, 0xc4, + 0xa6, 0x08, 0xc1, 0xfd, 0x67, 0xdf, 0x64, 0x2c, 0x1e, 0x0e, 0x16, 0x8f, 0xfa, 0xb8, 0x4a, 0x4f, + 0x8b, 0xe0, 0x6e, 0xd1, 0x9f, 0x7e, 0xb4, 0xb2, 0x2f, 0x8a, 0x4b, 0xa9, 0x1b, 0x0c, 0x8e, 0x50, + 0x34, 0x46, 0x2d, 0x10, 0xe6, 0xe9, 0x9d, 0x02, 0xd6, 0xac, 0x0f, 0x70, 0x87, 0x26, 0x12, 0xaf, + 0xea, 0x77, 0x8b, 0xb1, 0x67, 0xfd, 0x9f, 0x7d, 0x31, 0x50, 0x38, 0xac, 0x14, 0x8e, 0xfa, 0x46, + 0x41, 0xa1, 0x65, 0xf1, 0xc8, 0x4d, 0xb3, 0x84, 0xfa, 0x16, 0xe1, 0x46, 0xc0, 0x79, 0x1f, 0xf1, + 0x43, 0x6c, 0x0e, 0xd1, 0x61, 0xcf, 0x65, 0x6e, 0x07, 0xef, 0x67, 0xcf, 0x62, 0x50, 0xdc, 0x43, + 0x2e, 0x78, 0xfa, 0xa7, 0xb0, 0xde, 0xb1, 0xe7, 0x20, 0xa2, 0x5c, 0x4a, 0x1a, 0x7c, 0x7f, 0x03, + 0xff, 0x0e, 0xf3, 0x10, 0x46, 0xb4, 0xd0, 0xbc, 0x1f, 0x04, 0x3e, 0x24, 0x3a, 0x13, 0x2b, 0x34, + 0x60, 0x06, 0xdc, 0x2e, 0x4e, 0xe8, 0x9b, 0xed, 0xbc, 0x0b, 0x28, 0x71, 0x1d, 0xe5, 0x63, 0xc0, + 0x0b, 0x6f, 0x1b, 0x49, 0xa7, 0xa6, 0x73, 0x22, 0x28, 0x50, 0x43, 0xe4, 0x6c, 0x69, 0x0d, 0x9b, + 0xee, 0xbb, 0x1b, 0xe0, 0xc5, 0x0a, 0xdc, 0x22, 0xff, 0x21, 0x4a, 0x89, 0xe2, 0xeb, 0xd9, 0xeb, + 0x95, 0x3f, 0x9d, 0xcd, 0x55, 0x7f, 0x0c, 0xd2, 0x29, 0x0e, 0xb3, 0xed, 0xbc, 0x82, 0x51, 0x64, + 0x46, 0xd4, 0xad, 0x95, 0xae, 0x11, 0xe1, 0xe1, 0xb6, 0x3d, 0x14, 0x9e, 0x38, 0x08, 0xdf, 0xa1, + 0xc0, 0x0f, 0x50, 0x78, 0xcc, 0x20, 0xbc, 0x1a, 0xe1, 0x2a, 0xdb, 0xc7, 0x14, 0xce, 0x84, 0xe0, + 0xce, 0x72, 0x68, 0xdb, 0xd2, 0x86, 0xb0, 0xae, 0x91, 0x21, 0xd8, 0x7d, 0x30, 0xde, 0x3c, 0x1a, + 0xb5, 0x7a, 0x6f, 0xc1, 0x66, 0x49, 0x29, 0x01, 0x83, 0xdf, 0x39, 0xa3, 0x72, 0xe1, 0x6c, 0xe7, + 0xbd, 0x0d, 0xe1, 0xb3, 0x53, 0x7c, 0xf7, 0xa4, 0x54, 0x3e, 0x1f, 0x98, 0x2d, 0x3d, 0x19, 0xe0, + 0x7b, 0xf4, 0x6e, 0xc3, 0x75, 0xee, 0x4d, 0x77, 0x83, 0x2a, 0x9c, 0x61, 0xeb, 0x85, 0xc6, 0x30, + 0x1f, 0x9b, 0x9e, 0x5e, 0x9b, 0x0e, 0x12, 0x6f, 0x02, 0xe5, 0x98, 0x0a, 0x12, 0xe0, 0xaa, 0x31, + 0x44, 0xa8, 0x09, 0x4f, 0xf1, 0x7d, 0x7d, 0x47, 0x2f, 0xb6, 0x9e, 0xa2, 0xe6, 0x52, 0xeb, 0xb1, + 0x86, 0x33, 0xd6, 0x37, 0x04, 0x4d, 0x0b, 0x73, 0x2b, 0x58, 0x21, 0xd5, 0x92, 0x49, 0x43, 0x4c, + 0x37, 0x33, 0xcd, 0x32, 0x06, 0x5d, 0x40, 0xcd, 0xe0, 0x1c, 0x27, 0xa2, 0x33, 0xab, 0x50, 0x4b, + 0x79, 0xfe, 0x46, 0x9a, 0x8e, 0xc1, 0xd0, 0xdc, 0xae, 0xf4, 0x01, 0x64, 0xf7, 0x8b, 0x30, 0xf4, + 0x6a, 0xd3, 0x9d, 0x53, 0x44, 0x93, 0xbf, 0xaa, 0x6f, 0x80, 0x63, 0x98, 0x4d, 0x51, 0x35, 0x55, + 0xcb, 0xb5, 0xe1, 0x4c, 0x32, 0x31, 0xf9, 0x1b, 0xe8, 0x13, 0x93, 0xee, 0x6b, 0xe1, 0x0e, 0xa5, + 0xc3, 0x42, 0xf2, 0xf4, 0x8b, 0xb3, 0x84, 0xde, 0xac, 0xad, 0x2b, 0x84, 0xde, 0x6c, 0xae, 0xfa, + 0x17, 0xd4, 0x21, 0xe6, 0x70, 0xd5, 0x1d, 0xf4, 0x61, 0x15, 0xe7, 0x5a, 0x85, 0xbe, 0xcf, 0x2f, + 0x34, 0xa8, 0x45, 0x2d, 0x39, 0x1f, 0x28, 0x57, 0xf9, 0x62, 0xa4, 0x8c, 0xd7, 0x84, 0x3e, 0xf5, + 0xa6, 0x25, 0x81, 0x78, 0xc9, 0x0d, 0x12, 0x15, 0xef, 0x02, 0x95, 0x27, 0xe7, 0x21, 0x65, 0xb8, + 0xa9, 0xa2, 0xd3, 0xd7, 0xf9, 0x6c, 0x00, 0x17, 0xa8, 0x7b, 0xb9, 0xea, 0x9d, 0x14, 0x49, 0x2a, + 0xe7, 0x1a, 0x7f, 0x3b, 0x92, 0x29, 0x42, 0xef, 0x5a, 0xae, 0xda, 0xca, 0x62, 0xfd, 0x3a, 0xce, + 0x75, 0x9d, 0x09, 0xe1, 0x69, 0x41, 0x95, 0x3d, 0x6f, 0x96, 0xe3, 0xc3, 0x15, 0x5c, 0xd3, 0xd1, + 0xe3, 0x8d, 0x91, 0xd2, 0xd4, 0xae, 0xa3, 0x5c, 0x75, 0x05, 0x26, 0x94, 0x1b, 0x55, 0xae, 0x6e, + 0x31, 0xc7, 0xef, 0x5c, 0x00, 0x21, 0xcc, 0xfe, 0x00, 0x30, 0x57, 0xd5, 0x8f, 0x7f, 0x9d, 0x71, + 0x2b, 0x6a, 0x59, 0xe9, 0x67, 0x6c, 0xcd, 0x36, 0x64, 0x4f, 0xf8, 0x96, 0xb5, 0x8f, 0x4a, 0xae, + 0xea, 0x47, 0xe6, 0xc9, 0x05, 0xfe, 0x4b, 0x92, 0xe7, 0x27, 0x17, 0x40, 0x56, 0xa2, 0xb1, 0xab, + 0x81, 0x36, 0x60, 0x48, 0x43, 0xe7, 0x51, 0x5c, 0x8d, 0x86, 0x50, 0xd5, 0x79, 0x94, 0x86, 0xf1, + 0xe2, 0x7c, 0xc1, 0xad, 0x02, 0xe4, 0x16, 0x79, 0x82, 0x5a, 0xd1, 0xc7, 0x3a, 0xa1, 0x0e, 0x05, + 0xfb, 0xea, 0x29, 0xac, 0x3f, 0x25, 0xad, 0x0a, 0x3c, 0x5a, 0xf9, 0x42, 0x64, 0xe0, 0x5e, 0xe2, + 0xe5, 0x3b, 0x5d, 0x17, 0x45, 0xa3, 0xdf, 0xf9, 0xc0, 0x3e, 0x24, 0x61, 0x09, 0xa5, 0x63, 0x62, + 0xed, 0x64, 0xc9, 0xcc, 0xd6, 0xbc, 0x4d, 0xc9, 0xe8, 0x64, 0xed, 0x51, 0x69, 0x4b, 0x28, 0x15, + 0x2d, 0xfc, 0x57, 0x24, 0xc7, 0x4f, 0x5a, 0x90, 0x0a, 0xbe, 0xab, 0x53, 0x45, 0x93, 0x63, 0x3f, + 0xad, 0xe3, 0x5e, 0xee, 0x06, 0xcc, 0x0d, 0x95, 0xd8, 0xe5, 0xa7, 0x49, 0x9d, 0x5f, 0xa1, 0x34, + 0xc6, 0x80, 0x34, 0x90, 0xe7, 0xbf, 0xe3, 0x26, 0xca, 0xd8, 0xea, 0xd3, 0xd0, 0x96, 0xab, 0x3e, + 0x09, 0x85, 0x64, 0x3a, 0x0c, 0x57, 0x8d, 0x51, 0x6a, 0xcd, 0x6b, 0xd8, 0x25, 0xa9, 0x76, 0x6c, + 0x72, 0xf5, 0x69, 0xfe, 0xeb, 0xce, 0xbf, 0x31, 0x43, 0xa7, 0xa3, 0x2e, 0x4c, 0x11, 0xe1, 0x17, + 0x3f, 0x60, 0x18, 0xa2, 0x8c, 0xb4, 0x49, 0x53, 0x3b, 0xa6, 0xa6, 0x86, 0x76, 0x02, 0xc7, 0x00, + 0x2d, 0x7d, 0x2f, 0xd6, 0x8e, 0xa9, 0x3e, 0x0a, 0xd5, 0x40, 0x16, 0x57, 0xfd, 0x6b, 0x40, 0x90, + 0x16, 0xc4, 0x8f, 0x79, 0x43, 0xb0, 0x69, 0xed, 0x98, 0xb4, 0xea, 0xa3, 0xfc, 0xd7, 0xbe, 0xe7, + 0x48, 0x03, 0xf1, 0x7e, 0x3c, 0x63, 0x5f, 0x3f, 0x4e, 0x0a, 0xea, 0x16, 0x65, 0x78, 0x59, 0xcd, + 0xaf, 0xa9, 0x6e, 0xd5, 0xbe, 0x83, 0x69, 0xfa, 0x52, 0x5e, 0xf6, 0x2d, 0xfe, 0xc7, 0x8c, 0xbd, + 0xb4, 0x0d, 0x8c, 0x4a, 0x9b, 0x8c, 0x0b, 0x11, 0xfa, 0x0b, 0x6c, 0x92, 0xcc, 0x77, 0xfa, 0x22, + 0xf6, 0x60, 0x05, 0xd0, 0xa0, 0x70, 0xcd, 0x74, 0x2e, 0x06, 0x2a, 0xf7, 0x52, 0xd8, 0x2d, 0x15, + 0x25, 0xf5, 0x9d, 0xf7, 0x01, 0xf4, 0xb6, 0x96, 0x28, 0x3c, 0x0e, 0xc0, 0x95, 0x4b, 0x96, 0x61, + 0xd1, 0x59, 0x2c, 0xf2, 0x7e, 0x57, 0xb7, 0xf3, 0x49, 0x7d, 0x08, 0xc2, 0x3f, 0x56, 0xb9, 0xe4, + 0x51, 0x5a, 0xb5, 0x94, 0x3b, 0xf4, 0x32, 0x9d, 0x86, 0xde, 0x49, 0xce, 0xf1, 0x42, 0x6f, 0x14, + 0x3f, 0xae, 0x78, 0xb4, 0xfc, 0x47, 0x65, 0x52, 0x77, 0x46, 0xf9, 0xee, 0xde, 0x39, 0xc9, 0x37, + 0xde, 0x2c, 0xff, 0x5a, 0x51, 0x5b, 0x58, 0x73, 0xc7, 0xd5, 0xd2, 0xe6, 0xe0, 0xa9, 0xcd, 0xf2, + 0x8b, 0x4a, 0xb3, 0x74, 0xc3, 0x00, 0xff, 0x27, 0xe1, 0x48, 0x22, 0xb5, 0xca, 0xca, 0x25, 0xef, + 0xea, 0x10, 0xaf, 0xee, 0xbd, 0x85, 0xdf, 0xd1, 0x65, 0xa8, 0x1a, 0xc6, 0xb6, 0xd8, 0xb0, 0x8f, + 0x59, 0xce, 0xda, 0x4c, 0x2d, 0xb4, 0x93, 0x86, 0x7f, 0x94, 0xa4, 0x26, 0x7d, 0x40, 0xca, 0x3b, + 0x52, 0x3c, 0xa3, 0x98, 0xc9, 0x96, 0x9f, 0x78, 0x84, 0xd6, 0xde, 0x22, 0x28, 0x8a, 0xff, 0x49, + 0xa3, 0x51, 0x1e, 0x3d, 0x06, 0x72, 0x2c, 0x08, 0x28, 0xe4, 0x7f, 0x75, 0x61, 0x30, 0xa1, 0x29, + 0x8e, 0xc7, 0xb6, 0x0f, 0x2a, 0x6d, 0xf7, 0x44, 0x41, 0xad, 0x2f, 0x62, 0x34, 0x33, 0x06, 0xd9, + 0x97, 0x69, 0xbb, 0xbf, 0x29, 0xed, 0x28, 0x55, 0x9d, 0xe1, 0x30, 0x52, 0xc8, 0x1d, 0xc4, 0x81, + 0xc7, 0xd5, 0xbb, 0xa1, 0x19, 0x81, 0xf8, 0xc2, 0xd8, 0x4e, 0x8c, 0x6d, 0x10, 0x15, 0x93, 0x96, + 0x91, 0x7e, 0x67, 0x01, 0x39, 0x07, 0x22, 0x25, 0x7e, 0x30, 0x2c, 0x7d, 0x83, 0xbe, 0x31, 0xa1, + 0x31, 0xb6, 0x61, 0xff, 0x38, 0x86, 0x86, 0x83, 0x51, 0x90, 0x62, 0x3d, 0x91, 0xa4, 0x6c, 0x84, + 0xe4, 0x74, 0x11, 0x0f, 0xa9, 0x63, 0x3d, 0x92, 0x8a, 0x90, 0xbe, 0x04, 0x4f, 0xa5, 0x27, 0xb1, + 0x66, 0xf9, 0x3d, 0xde, 0xc7, 0x84, 0xc6, 0x91, 0x10, 0xbf, 0xed, 0xc1, 0x0e, 0x86, 0x5e, 0x1a, + 0x24, 0xc7, 0xfa, 0xc9, 0x39, 0x98, 0x03, 0xfb, 0x61, 0xa1, 0x42, 0xcd, 0x6c, 0x8e, 0x4b, 0xf1, + 0x4d, 0x16, 0x59, 0x45, 0xb7, 0x8c, 0xe4, 0xa4, 0x94, 0xc1, 0x12, 0x4e, 0x99, 0xa8, 0x44, 0xdf, + 0xdc, 0x86, 0xd4, 0x7b, 0xbc, 0x0f, 0x42, 0x20, 0x52, 0x0b, 0xdf, 0x20, 0xde, 0x7b, 0x25, 0x93, + 0x57, 0x5a, 0x3e, 0x8a, 0x15, 0xef, 0x0e, 0x9a, 0xec, 0x38, 0xc9, 0xce, 0x92, 0x05, 0x8a, 0xc5, + 0xd6, 0xb2, 0xc9, 0xd5, 0x6e, 0xbe, 0x43, 0x1c, 0x11, 0x9c, 0x66, 0xe1, 0x48, 0xdc, 0xb0, 0x84, + 0x5f, 0x47, 0xce, 0xc2, 0x82, 0x86, 0x2c, 0x0e, 0xe1, 0xcf, 0x78, 0xa6, 0xc1, 0x28, 0x83, 0xc9, + 0x8e, 0x6c, 0x71, 0xae, 0x06, 0x26, 0xa5, 0x8a, 0x4e, 0xe0, 0x03, 0x19, 0xcb, 0x82, 0xa0, 0x28, + 0x59, 0x61, 0xcc, 0x03, 0x3c, 0x69, 0x08, 0xb9, 0x9e, 0x70, 0xac, 0xf2, 0x58, 0x62, 0x4d, 0xfe, + 0xc0, 0xe3, 0x7b, 0x47, 0x22, 0x33, 0x57, 0x69, 0x10, 0x3e, 0x89, 0xef, 0x22, 0x0d, 0xf6, 0x66, + 0x50, 0x66, 0x60, 0x82, 0xd2, 0x94, 0x00, 0xc6, 0x4b, 0xc6, 0x2a, 0xe6, 0x5b, 0x3b, 0x25, 0xbd, + 0xfa, 0x22, 0xff, 0xa5, 0x6f, 0x42, 0x88, 0xc3, 0x31, 0xb4, 0x32, 0xc8, 0x9f, 0xc8, 0xde, 0x22, + 0x55, 0x37, 0x8c, 0xd4, 0x38, 0x2a, 0xf1, 0x3b, 0xa8, 0xa5, 0xa4, 0xf6, 0x02, 0xa9, 0x05, 0x92, + 0xb1, 0x93, 0x9c, 0x25, 0x37, 0x84, 0x2b, 0x5c, 0xad, 0x78, 0x06, 0x16, 0xa0, 0x9a, 0x8c, 0x01, + 0xa0, 0x9b, 0xfb, 0xa0, 0x3f, 0xce, 0xf3, 0xad, 0x2a, 0x63, 0x3f, 0x87, 0xf4, 0xdd, 0x38, 0x4c, + 0xe9, 0xab, 0xe8, 0x02, 0x64, 0xd7, 0xec, 0xa7, 0xc9, 0xb1, 0xdd, 0x89, 0xfb, 0x68, 0x62, 0x54, + 0xe1, 0x17, 0x17, 0x54, 0x0d, 0x50, 0x5a, 0x1f, 0x95, 0x56, 0x80, 0xb8, 0x6b, 0x0e, 0x51, 0x5a, + 0xf7, 0x3d, 0xfe, 0x19, 0x5a, 0xdf, 0x17, 0xe2, 0xdd, 0xbe, 0x05, 0xff, 0x48, 0xdc, 0xf3, 0x43, + 0xb0, 0xad, 0x69, 0xb0, 0xb9, 0x51, 0xda, 0xca, 0x12, 0x03, 0x00, 0x6b, 0x7e, 0x49, 0x7b, 0xbc, + 0xf6, 0x62, 0x3b, 0xc3, 0x24, 0xf1, 0x97, 0xc4, 0x11, 0xbe, 0xd9, 0xd4, 0x81, 0x05, 0x5d, 0x63, + 0xf5, 0x45, 0x67, 0xd7, 0xad, 0x39, 0xfa, 0x42, 0x7a, 0x14, 0xc7, 0x78, 0x87, 0xf6, 0x88, 0x49, + 0xf2, 0x7d, 0x41, 0xcd, 0xf1, 0xb6, 0x49, 0x0a, 0xed, 0x05, 0x42, 0xd6, 0xaa, 0x76, 0x75, 0xf3, + 0x61, 0xe4, 0x5b, 0xdb, 0x63, 0x97, 0x18, 0x46, 0x9e, 0x4f, 0xf3, 0x2e, 0xd5, 0xe1, 0x98, 0x2f, + 0xc1, 0x6e, 0xd3, 0x30, 0x54, 0x5f, 0x2f, 0xce, 0x0f, 0x5a, 0xd1, 0xde, 0x57, 0x4e, 0x86, 0x31, + 0x5c, 0xea, 0x49, 0x71, 0xac, 0xa1, 0xce, 0xf1, 0x08, 0xe1, 0xfd, 0x10, 0x5f, 0x9f, 0x25, 0x97, + 0x20, 0xd2, 0x22, 0x37, 0x2a, 0x17, 0xc7, 0x32, 0xd2, 0x93, 0x2c, 0xdf, 0x23, 0x46, 0xc7, 0x1a, + 0xea, 0xec, 0x5f, 0xb3, 0x7d, 0xa4, 0x2e, 0xe1, 0x5a, 0x56, 0xa6, 0xd9, 0xb6, 0x19, 0xd6, 0x66, + 0xf9, 0x12, 0x4d, 0x5a, 0x20, 0x62, 0xf3, 0x53, 0xcc, 0xbe, 0x31, 0x66, 0xdb, 0x4a, 0x10, 0xa3, + 0xdc, 0x48, 0xe1, 0x48, 0xc1, 0xeb, 0xb6, 0x2f, 0x11, 0xb0, 0x4b, 0x01, 0x0c, 0xd2, 0x88, 0x14, + 0x6a, 0x24, 0xa3, 0x96, 0x18, 0x7b, 0x1b, 0x22, 0xa8, 0xb7, 0x78, 0x17, 0x89, 0xa9, 0x86, 0x68, + 0x71, 0x1e, 0x98, 0xb4, 0x33, 0x1a, 0x42, 0x15, 0xad, 0xbc, 0x7b, 0x57, 0x10, 0xae, 0x40, 0x85, + 0xbe, 0xf0, 0xbd, 0xab, 0x80, 0x9d, 0x7d, 0x0b, 0x60, 0x70, 0x2e, 0xb5, 0x19, 0xf2, 0xd1, 0xd4, + 0xe6, 0x4c, 0xf9, 0x09, 0x8a, 0x5c, 0x15, 0xe0, 0x7b, 0x2d, 0xb2, 0x55, 0x19, 0xc8, 0x16, 0x86, + 0xe4, 0x25, 0x29, 0x15, 0xe0, 0x82, 0xbf, 0x52, 0xf6, 0x36, 0x42, 0x7e, 0x63, 0x4a, 0x25, 0x98, + 0x8c, 0x33, 0xd6, 0x96, 0x7b, 0x19, 0x5a, 0x4d, 0x54, 0x5a, 0xc9, 0x13, 0x3f, 0xa7, 0xfb, 0x6d, + 0x6f, 0x1c, 0x60, 0x19, 0x9b, 0x1b, 0x9e, 0x65, 0x36, 0x58, 0x51, 0x25, 0x07, 0x02, 0x48, 0x33, + 0x4d, 0x72, 0x21, 0x6f, 0xcd, 0xe7, 0x5e, 0x77, 0x53, 0x25, 0xc3, 0xb8, 0x20, 0xa3, 0x47, 0xe8, + 0x8b, 0xe0, 0x5e, 0xf7, 0x08, 0xbd, 0x3a, 0x88, 0x23, 0x82, 0x41, 0x23, 0xe4, 0xfe, 0x52, 0xe6, + 0xef, 0xa4, 0xe4, 0x76, 0xa1, 0x7f, 0x22, 0x97, 0xe2, 0x19, 0x84, 0x1d, 0x96, 0x92, 0x75, 0x43, + 0x60, 0xd9, 0x98, 0xaa, 0x18, 0xd5, 0xb5, 0xb1, 0xfa, 0xd3, 0x30, 0xd7, 0x51, 0x35, 0x71, 0xda, + 0xda, 0xd5, 0x63, 0xd9, 0xa5, 0xbe, 0x6b, 0x0d, 0x2a, 0x2d, 0x13, 0x54, 0x64, 0x0b, 0x6e, 0xdb, + 0x1a, 0x61, 0xd9, 0x85, 0x08, 0x8e, 0xd7, 0xe8, 0x4f, 0xd7, 0xc6, 0xdd, 0x05, 0x6d, 0x35, 0xd0, + 0x76, 0xa9, 0xe1, 0x14, 0x7f, 0xd5, 0x37, 0x56, 0x7f, 0xda, 0xd0, 0xec, 0x18, 0x95, 0x70, 0x8a, + 0xad, 0x6b, 0x08, 0x63, 0x96, 0x2e, 0xe3, 0xfb, 0xd6, 0x84, 0x42, 0x63, 0x40, 0x9e, 0xa3, 0xae, + 0x0d, 0xab, 0x9d, 0xba, 0xb0, 0x9d, 0x1f, 0xa7, 0xef, 0x4e, 0x03, 0xd3, 0x8f, 0xa9, 0x55, 0xcd, + 0xa9, 0x8d, 0xd7, 0xa7, 0x2d, 0x6c, 0x77, 0x5e, 0xd5, 0x1f, 0x5d, 0xd8, 0xce, 0x6d, 0xf7, 0x48, + 0xa9, 0x23, 0x03, 0x98, 0x70, 0xac, 0xb9, 0x15, 0x36, 0xd2, 0x7e, 0xdc, 0xa1, 0x30, 0xee, 0x50, + 0xac, 0x54, 0x7e, 0x58, 0x18, 0x98, 0x28, 0x95, 0xeb, 0xb8, 0x14, 0xbf, 0xf4, 0xfc, 0x61, 0x21, + 0x30, 0x51, 0x7a, 0x1e, 0x9e, 0x6f, 0x80, 0xdf, 0x9b, 0xad, 0x60, 0x7c, 0x88, 0x3b, 0xa4, 0x9a, + 0x03, 0x5d, 0xb8, 0x43, 0x71, 0x7a, 0x69, 0xf3, 0x61, 0x29, 0x43, 0x07, 0x8d, 0xb8, 0x94, 0xa6, + 0xa1, 0xad, 0xd3, 0x0c, 0x7d, 0xce, 0x33, 0x30, 0x9c, 0x77, 0xe8, 0x70, 0x43, 0xe2, 0x54, 0x13, + 0x44, 0xa6, 0x4e, 0x9d, 0x98, 0x83, 0xe1, 0xea, 0x32, 0x7d, 0x40, 0xef, 0xae, 0x8d, 0x7b, 0xe4, + 0xe1, 0xb8, 0x87, 0xf9, 0xb1, 0xc0, 0xe9, 0xdd, 0x86, 0x3e, 0xe9, 0x45, 0x96, 0xff, 0x6f, 0xdf, + 0x18, 0x48, 0x1b, 0xa2, 0xb8, 0xed, 0x6e, 0x58, 0x15, 0xa4, 0x54, 0x26, 0x10, 0xea, 0xaf, 0x77, + 0x67, 0x80, 0x68, 0x2b, 0x5f, 0x60, 0x59, 0x46, 0x4a, 0x62, 0xf9, 0xee, 0x21, 0xd1, 0x37, 0xba, + 0xe2, 0x40, 0x71, 0x94, 0x64, 0xf4, 0x43, 0x70, 0x6b, 0xfb, 0x9e, 0x7a, 0x6c, 0xb0, 0x43, 0xc3, + 0x09, 0xe7, 0xd2, 0x4a, 0x03, 0xc3, 0xbd, 0x59, 0x2f, 0x04, 0xc2, 0xb9, 0xd7, 0x1b, 0xab, 0x6f, + 0xf0, 0x73, 0x0e, 0x05, 0xd0, 0xd6, 0x1b, 0xa8, 0x2f, 0x8a, 0x3d, 0x6b, 0x9f, 0x96, 0x5c, 0x93, + 0xba, 0xa4, 0x0b, 0x13, 0x51, 0x72, 0x82, 0x3f, 0x28, 0x19, 0xbb, 0x8a, 0x55, 0x74, 0xd7, 0x2a, + 0x72, 0x61, 0x70, 0x77, 0xcb, 0x17, 0x0e, 0xe3, 0x0e, 0x8f, 0xb6, 0xe5, 0x84, 0x05, 0x60, 0xb2, + 0x10, 0xa5, 0xc2, 0x34, 0xc9, 0xf7, 0xcc, 0x07, 0x05, 0x72, 0xcd, 0x82, 0x3f, 0xef, 0xe3, 0xd3, + 0x03, 0xa0, 0x75, 0x3d, 0x0d, 0xe8, 0x0a, 0xf8, 0x58, 0x6e, 0x97, 0x51, 0x6d, 0x96, 0x93, 0xae, + 0x53, 0xe5, 0xf2, 0xc5, 0xda, 0x3e, 0x07, 0x5d, 0xeb, 0x7c, 0x13, 0x23, 0xb4, 0x01, 0x96, 0x1f, + 0x2b, 0x6f, 0x53, 0xe3, 0xd9, 0x10, 0xfc, 0x79, 0xff, 0x03, 0xd0, 0x55, 0x5f, 0xc4, 0x7b, 0xf8, + 0xe5, 0x72, 0xf3, 0x23, 0x6c, 0xf7, 0xc3, 0x92, 0xed, 0x2b, 0xad, 0xdc, 0x3c, 0x9a, 0xe1, 0x97, + 0x90, 0x06, 0x31, 0x29, 0x06, 0xa0, 0x3d, 0xc2, 0xd6, 0xb8, 0xc0, 0x0b, 0x0f, 0x41, 0x7e, 0xa9, + 0xbf, 0xc8, 0xed, 0x4a, 0xd3, 0x64, 0x07, 0x36, 0x02, 0xf2, 0x3f, 0x5e, 0xa3, 0xbc, 0x82, 0x99, + 0x6b, 0x88, 0x47, 0x5e, 0xc6, 0x20, 0x5d, 0xbc, 0x36, 0x3d, 0x65, 0xd1, 0xd6, 0xb8, 0xad, 0xe7, + 0x7d, 0x23, 0x00, 0xf6, 0x10, 0x3d, 0x9c, 0xfa, 0x10, 0xad, 0x81, 0x1f, 0x6d, 0x63, 0x50, 0xdd, + 0xc7, 0xfd, 0xcf, 0x50, 0x1b, 0xe1, 0x35, 0xb6, 0x73, 0xca, 0x06, 0xf4, 0x8a, 0x9b, 0x81, 0x80, + 0xfc, 0x20, 0xa4, 0x00, 0xb2, 0xd7, 0x17, 0xda, 0x8a, 0x26, 0x27, 0x6b, 0xef, 0x69, 0x9b, 0x1a, + 0xce, 0x34, 0x44, 0xe0, 0x5f, 0xd6, 0xf6, 0x07, 0x40, 0x90, 0x29, 0xf7, 0x28, 0x29, 0x1f, 0x69, + 0x00, 0xc2, 0x92, 0x6c, 0xe5, 0x97, 0x30, 0x7a, 0xbf, 0x32, 0x04, 0x36, 0xd3, 0xf6, 0x3d, 0x8d, + 0xf2, 0x4f, 0x0e, 0x81, 0xc5, 0xd8, 0x54, 0xed, 0x08, 0xdb, 0x3f, 0x08, 0xe3, 0xaa, 0x05, 0x0c, + 0x79, 0x82, 0x03, 0x20, 0x8d, 0xec, 0x9e, 0xe3, 0xa5, 0x90, 0xd7, 0xad, 0x88, 0xd3, 0x7b, 0x46, + 0xad, 0xd0, 0xf1, 0x19, 0xfa, 0xa3, 0xd2, 0x04, 0xad, 0x83, 0x61, 0x12, 0x5a, 0x0e, 0x6b, 0x21, + 0x64, 0x8d, 0x3d, 0x9a, 0x69, 0x2e, 0x66, 0xb2, 0x70, 0x57, 0x59, 0x83, 0x58, 0x62, 0x2c, 0x72, + 0x3f, 0x45, 0xa7, 0x2a, 0x8e, 0xb6, 0x69, 0x8b, 0xc3, 0x18, 0x5b, 0xe5, 0x05, 0x20, 0xff, 0x9d, + 0xf9, 0xa1, 0x44, 0x62, 0x92, 0x4d, 0xb8, 0x84, 0x4b, 0xba, 0x4d, 0x85, 0x6e, 0x22, 0x96, 0x6a, + 0x86, 0x2a, 0xa8, 0x92, 0x0d, 0x11, 0xc8, 0x3f, 0x0b, 0x49, 0xb8, 0x5f, 0x21, 0xc1, 0xf7, 0x15, + 0xf2, 0x0c, 0x29, 0xa9, 0x3c, 0x1e, 0xc8, 0xb2, 0xee, 0xfb, 0x21, 0xbc, 0xeb, 0x43, 0x9d, 0xf3, + 0x06, 0xf1, 0x5a, 0xf7, 0xf5, 0x86, 0x7f, 0xab, 0x4c, 0x7f, 0xe5, 0x92, 0x3f, 0x53, 0xa1, 0xfd, + 0x54, 0xef, 0xb6, 0xee, 0xbb, 0x11, 0xfe, 0x2d, 0x7e, 0xf5, 0x85, 0x7f, 0xbb, 0xb3, 0x92, 0x96, + 0x76, 0x6a, 0x68, 0xd3, 0x10, 0x30, 0x5c, 0x01, 0x36, 0x5a, 0xf7, 0xf5, 0xc3, 0xd7, 0x28, 0xeb, + 0xbe, 0x9b, 0xe1, 0xdf, 0x36, 0x44, 0x20, 0x02, 0x96, 0x22, 0x93, 0xff, 0x02, 0x7a, 0xa2, 0x8c, + 0xc3, 0xf2, 0x11, 0xa0, 0x67, 0x3e, 0x35, 0xf6, 0xef, 0x02, 0xc5, 0x1f, 0x54, 0xee, 0x85, 0x62, + 0x45, 0x0c, 0xb8, 0x05, 0x31, 0x29, 0x9a, 0xdb, 0xd1, 0xb8, 0x84, 0xad, 0x04, 0x87, 0x28, 0xec, + 0xc5, 0xb0, 0x33, 0x27, 0x5a, 0xe4, 0xb5, 0xef, 0x9d, 0x99, 0x4a, 0x95, 0xe6, 0x2e, 0x1b, 0x83, + 0x67, 0x91, 0xad, 0xf2, 0x97, 0xd3, 0x31, 0xfe, 0x06, 0x2e, 0xdf, 0xc7, 0x1a, 0x29, 0xaf, 0xc3, + 0xda, 0x18, 0xae, 0x65, 0xac, 0xe4, 0xf9, 0x30, 0xe2, 0xf1, 0x74, 0xaa, 0xac, 0xe4, 0xb1, 0x48, + 0xa1, 0x9d, 0xb3, 0x92, 0x14, 0x15, 0x71, 0xa8, 0x49, 0xb6, 0x46, 0xaa, 0x38, 0x80, 0x31, 0x50, + 0xae, 0x9c, 0xf0, 0x10, 0x15, 0x5a, 0x63, 0x7a, 0x3c, 0xb3, 0x14, 0x03, 0x65, 0xb5, 0xc8, 0x90, + 0x74, 0xc8, 0x5b, 0x85, 0x76, 0x0d, 0x49, 0x8f, 0xf6, 0xf8, 0xa0, 0x6b, 0x7e, 0x64, 0x6e, 0x6e, + 0xf1, 0x78, 0xf9, 0x9d, 0x2b, 0x54, 0xe9, 0x0e, 0x41, 0xb4, 0xcb, 0x74, 0xa7, 0xab, 0x59, 0xfe, + 0x5e, 0x48, 0x4d, 0xf5, 0xdd, 0x78, 0x0c, 0x23, 0x9a, 0x62, 0xd0, 0xa3, 0xec, 0x78, 0x34, 0x3a, + 0x33, 0xf0, 0x10, 0x12, 0x60, 0x96, 0x0f, 0xcc, 0xa3, 0x78, 0x71, 0x3f, 0x79, 0xa1, 0x72, 0xde, + 0xb1, 0xf1, 0x32, 0x55, 0xb7, 0x4b, 0x7d, 0xa1, 0x60, 0xaa, 0xaa, 0xab, 0x17, 0x50, 0xd9, 0x98, + 0x38, 0x98, 0xa3, 0x86, 0x07, 0xe9, 0x7c, 0xbe, 0x8f, 0xf9, 0xbd, 0xe4, 0xba, 0x89, 0x09, 0x43, + 0x03, 0xac, 0xd1, 0x48, 0x85, 0xeb, 0x05, 0x10, 0x97, 0x64, 0x6c, 0x22, 0x1e, 0xe1, 0x32, 0x24, + 0x69, 0xd5, 0x1e, 0x19, 0x92, 0x86, 0x4a, 0x49, 0xd9, 0xac, 0x51, 0x60, 0x6e, 0x0a, 0x3b, 0xa2, + 0x77, 0x27, 0x1d, 0xc6, 0xcd, 0x5d, 0x8c, 0xce, 0xf8, 0x0e, 0x65, 0xc3, 0x24, 0x89, 0xab, 0x3e, + 0xa7, 0xa2, 0x61, 0x2d, 0xf2, 0x2b, 0x6d, 0x8f, 0x83, 0x5e, 0xd9, 0xf2, 0x5c, 0x85, 0xba, 0x46, + 0xba, 0xdb, 0xc6, 0x64, 0x40, 0xfd, 0xc1, 0x79, 0x38, 0x29, 0xe4, 0x93, 0x69, 0xb8, 0x9c, 0xd3, + 0x21, 0x09, 0xdd, 0x72, 0xb6, 0x36, 0x52, 0x6a, 0x26, 0x63, 0xf8, 0x38, 0x5e, 0xfe, 0xe5, 0x17, + 0x54, 0x06, 0xdd, 0xae, 0x78, 0xa8, 0x62, 0x39, 0x57, 0x31, 0xa0, 0x2e, 0x2e, 0x97, 0xd5, 0x0f, + 0x06, 0xf3, 0x69, 0x52, 0xd1, 0x4e, 0x16, 0xea, 0xbb, 0x49, 0xb9, 0x96, 0x94, 0xcf, 0x23, 0xe5, + 0x89, 0xa2, 0x4b, 0x43, 0x77, 0x92, 0xb4, 0x74, 0xff, 0x6d, 0x21, 0x79, 0x6c, 0x71, 0x63, 0x0a, + 0x52, 0x10, 0x10, 0x97, 0xd0, 0x19, 0x0c, 0xf0, 0xd1, 0x74, 0x06, 0xe5, 0xe9, 0xf1, 0x0a, 0x86, + 0x46, 0x3a, 0x7f, 0x8d, 0xc6, 0x26, 0x2d, 0xc3, 0x42, 0x10, 0x03, 0x7c, 0x59, 0x89, 0xf1, 0x04, + 0xe9, 0xa5, 0x93, 0x67, 0x3c, 0x4e, 0xf8, 0x33, 0x84, 0x6f, 0x29, 0x8e, 0xb7, 0x48, 0xfc, 0xdb, + 0x16, 0x79, 0xc3, 0x5c, 0x85, 0x0b, 0xe3, 0x01, 0x9c, 0x3b, 0x92, 0xd7, 0x25, 0x86, 0x13, 0xe3, + 0x4e, 0xd1, 0x58, 0x4d, 0x8c, 0x7f, 0x15, 0x8d, 0x95, 0xd0, 0xe1, 0x3f, 0x8a, 0x67, 0x58, 0x14, + 0x49, 0x99, 0xe5, 0xfb, 0x95, 0xd6, 0xa2, 0xf1, 0x0c, 0x71, 0x6d, 0xa6, 0x49, 0x43, 0x8b, 0xb2, + 0x57, 0x8a, 0x9b, 0x80, 0xa6, 0xe3, 0x44, 0x2a, 0xa7, 0x4c, 0xe3, 0xae, 0x22, 0xa5, 0x49, 0xef, + 0x36, 0xe7, 0xca, 0x51, 0x4a, 0x27, 0x8b, 0x05, 0x98, 0xff, 0xf5, 0x65, 0x85, 0x79, 0xe3, 0xbb, + 0xc8, 0x39, 0x6e, 0x20, 0x63, 0x33, 0xf1, 0x1e, 0xa4, 0xb8, 0xf2, 0x85, 0x45, 0x01, 0x86, 0x8f, + 0x78, 0x9c, 0xef, 0x06, 0x8f, 0x3c, 0x23, 0x38, 0x24, 0x25, 0xf2, 0xe3, 0x39, 0xc1, 0xcd, 0x79, + 0xba, 0x4b, 0xab, 0x0c, 0x02, 0x93, 0x69, 0x3a, 0x2e, 0x2a, 0x1b, 0xb5, 0xc6, 0x16, 0x91, 0x92, + 0x03, 0x31, 0x26, 0x1d, 0xd6, 0x86, 0x9e, 0x3a, 0x5b, 0xfe, 0xc5, 0x9c, 0xc1, 0x81, 0x75, 0x43, + 0x07, 0xbe, 0x0a, 0xfa, 0xb7, 0x17, 0x1b, 0x52, 0x1b, 0x10, 0xf3, 0x64, 0x88, 0x91, 0x2b, 0x5f, + 0x50, 0x83, 0xe7, 0x8d, 0x58, 0x06, 0xa3, 0x1f, 0x35, 0x9c, 0xe4, 0xd2, 0x4f, 0x92, 0x15, 0xd1, + 0xc5, 0xac, 0xfe, 0x68, 0x66, 0x26, 0x2c, 0x0c, 0x1f, 0xd1, 0xcd, 0xed, 0x68, 0xf0, 0x3b, 0xae, + 0x4f, 0xa0, 0x6f, 0xd5, 0xd6, 0x68, 0xc5, 0x9a, 0xbc, 0x50, 0x08, 0x4d, 0x03, 0xe9, 0xe5, 0xc7, + 0x29, 0x13, 0xf1, 0xdb, 0x69, 0x8a, 0x22, 0x22, 0x9c, 0xe4, 0xc9, 0x8d, 0x94, 0x13, 0x3a, 0x19, + 0x8a, 0x1d, 0x49, 0x8f, 0x22, 0xad, 0xf5, 0x74, 0x3e, 0x94, 0xbd, 0x69, 0x77, 0x70, 0x3f, 0xda, + 0x45, 0xcf, 0x0a, 0xe2, 0xcd, 0x92, 0xf1, 0x6d, 0xb3, 0x7c, 0x5a, 0xaf, 0xf8, 0xa2, 0x00, 0x4c, + 0x0e, 0x9b, 0x82, 0xe7, 0x05, 0x74, 0x66, 0xdc, 0x74, 0x66, 0x8e, 0xe0, 0xcc, 0x38, 0x47, 0x07, + 0x36, 0x46, 0xe7, 0x66, 0xcb, 0x6f, 0x28, 0x2d, 0xb1, 0x97, 0x19, 0x78, 0x9d, 0xd7, 0xae, 0x2c, + 0x63, 0x79, 0xef, 0x0a, 0x57, 0xd8, 0x9a, 0x35, 0x1a, 0xb1, 0x42, 0x26, 0xbd, 0xe4, 0x2a, 0x84, + 0x60, 0x5c, 0x6a, 0x03, 0x49, 0x56, 0x83, 0x65, 0xfa, 0x59, 0x81, 0x92, 0xcd, 0x80, 0x73, 0xa5, + 0xe6, 0x6f, 0xf4, 0xca, 0x5f, 0x4e, 0x1d, 0x24, 0x5b, 0x34, 0x7a, 0xd9, 0x06, 0x88, 0xf3, 0xd8, + 0x4b, 0xa4, 0x42, 0xd6, 0x9f, 0xd6, 0x1f, 0x4d, 0x06, 0x1f, 0x43, 0x1a, 0x49, 0x3d, 0x48, 0x8a, + 0x54, 0x78, 0xc5, 0xc5, 0x6f, 0x75, 0xe0, 0x76, 0x42, 0x1a, 0xf5, 0x2c, 0x88, 0x80, 0x7b, 0x79, + 0x04, 0xee, 0x1b, 0xe4, 0xbd, 0x46, 0x72, 0x5e, 0x21, 0xa6, 0x1a, 0x6b, 0x88, 0x69, 0x2b, 0x39, + 0x98, 0x06, 0x0f, 0x54, 0xd0, 0x43, 0xd9, 0x1f, 0xc2, 0xfc, 0x45, 0xb2, 0x9d, 0x32, 0x4f, 0x77, + 0x3b, 0x39, 0x3d, 0x04, 0xe0, 0x3d, 0x17, 0x48, 0xce, 0x9b, 0xd4, 0xd6, 0x70, 0xf6, 0x9b, 0x13, + 0x87, 0xaa, 0xa8, 0x68, 0xec, 0x02, 0xf7, 0x72, 0x9b, 0x20, 0xe8, 0x14, 0x2f, 0xb9, 0x74, 0x6b, + 0x8a, 0xf9, 0x02, 0xf6, 0x20, 0x62, 0x13, 0xde, 0x42, 0xcc, 0x0c, 0x7b, 0x43, 0xb8, 0x91, 0x64, + 0xff, 0x4f, 0xc0, 0xed, 0x11, 0xf9, 0xb6, 0x9e, 0xcf, 0x45, 0xe3, 0x9b, 0x82, 0x97, 0x61, 0x4f, + 0x8c, 0x6d, 0x11, 0x73, 0xbc, 0x86, 0xb3, 0xe4, 0x14, 0xb7, 0xf4, 0x04, 0x31, 0xb5, 0x41, 0x6d, + 0xde, 0x6b, 0xb1, 0xe7, 0x45, 0x53, 0x0d, 0xb7, 0xf4, 0xa4, 0xc1, 0xcb, 0xa5, 0x5c, 0xe3, 0x52, + 0xf9, 0x36, 0x6e, 0xa9, 0x57, 0xcc, 0x79, 0x65, 0xa9, 0xd0, 0x3f, 0x92, 0xf4, 0x00, 0xf7, 0x1c, + 0xb5, 0x65, 0x5f, 0x1c, 0x2c, 0xd1, 0x07, 0xa1, 0x18, 0x98, 0x6f, 0xb6, 0x3d, 0x88, 0xe1, 0x73, + 0xcf, 0x7f, 0xd3, 0xd1, 0x95, 0xe3, 0xc5, 0x15, 0x2a, 0x70, 0x5a, 0xee, 0x54, 0x8b, 0x59, 0x2e, + 0xa4, 0xd6, 0x8e, 0x9b, 0xc4, 0xaa, 0x86, 0xb4, 0x24, 0x56, 0xd9, 0x73, 0xa5, 0xfb, 0x91, 0x52, + 0x26, 0x4b, 0x26, 0xe0, 0x03, 0x99, 0x57, 0x83, 0xf6, 0x9f, 0x10, 0xb4, 0xde, 0x62, 0x06, 0xba, + 0x79, 0x3a, 0x28, 0x36, 0x29, 0x23, 0x5c, 0xe8, 0x0b, 0xb3, 0xa7, 0xea, 0x2f, 0xa6, 0x08, 0xbd, + 0x01, 0xe7, 0xc4, 0x7f, 0xdb, 0x7d, 0x71, 0x4d, 0x20, 0x2d, 0x0e, 0x30, 0x34, 0xcc, 0x08, 0x62, + 0xc8, 0x06, 0x04, 0x95, 0x0a, 0x02, 0x70, 0x6e, 0x9e, 0xf0, 0x14, 0xa1, 0x8f, 0xdd, 0x74, 0x58, + 0x84, 0x84, 0xad, 0xa2, 0x1d, 0xd2, 0xfc, 0xf4, 0xaa, 0x00, 0xee, 0xb9, 0x73, 0xae, 0xc9, 0x37, + 0x80, 0x91, 0xd1, 0xb6, 0x5f, 0xe2, 0x02, 0xb7, 0x90, 0x92, 0xaf, 0xaa, 0x3a, 0x82, 0xfe, 0x74, + 0x98, 0xb3, 0xb5, 0xc3, 0xd2, 0x47, 0xe3, 0x71, 0x18, 0x62, 0xad, 0x32, 0x44, 0x43, 0xd2, 0x68, + 0x65, 0x19, 0x82, 0x45, 0x66, 0x86, 0xc4, 0x9f, 0xc1, 0xa9, 0xb2, 0xc8, 0xd3, 0x66, 0x2b, 0xf3, + 0x94, 0x16, 0x6f, 0xb6, 0x30, 0x0c, 0x4c, 0xc9, 0x44, 0xf9, 0xa3, 0xcf, 0x28, 0x15, 0xdc, 0xee, + 0xb4, 0xf8, 0x25, 0x2b, 0x00, 0xc8, 0xf3, 0xdc, 0x6e, 0x23, 0x6e, 0xd3, 0x73, 0xbb, 0xf9, 0xef, + 0xd9, 0x86, 0xe2, 0x48, 0x9b, 0x89, 0x9e, 0x66, 0xde, 0x3b, 0x3b, 0xb8, 0xec, 0xf2, 0x4f, 0x42, + 0xdb, 0xc8, 0x25, 0x0c, 0xd8, 0xd5, 0xc6, 0x5c, 0x31, 0x6d, 0x06, 0xb7, 0xdb, 0xef, 0x91, 0x35, + 0x42, 0xbb, 0x1f, 0x1a, 0xab, 0x6c, 0x35, 0x5f, 0x60, 0xe3, 0xee, 0x59, 0xa1, 0xc6, 0x8b, 0x03, + 0x1b, 0xb5, 0xb2, 0xf5, 0xf3, 0x60, 0x4c, 0x92, 0x41, 0xf7, 0x11, 0x46, 0xdb, 0xe2, 0x20, 0x58, + 0x90, 0x6d, 0x34, 0xf9, 0x56, 0x09, 0x47, 0x66, 0x40, 0xcc, 0x6a, 0x6b, 0x87, 0xb5, 0xdd, 0x37, + 0xde, 0xf6, 0x39, 0x70, 0xe4, 0xd3, 0xd8, 0x74, 0x5f, 0xd0, 0x85, 0x3d, 0x5a, 0xe1, 0xef, 0xf5, + 0xeb, 0x41, 0xfe, 0xe8, 0x72, 0xb2, 0x19, 0xb7, 0x7a, 0x78, 0x8d, 0xab, 0xdb, 0xf9, 0xa0, 0x45, + 0xf6, 0x28, 0xe1, 0x0e, 0x2c, 0x4f, 0x3a, 0x89, 0xba, 0x79, 0x70, 0x05, 0x75, 0xff, 0xa3, 0xe4, + 0x2d, 0xe0, 0x0a, 0xae, 0xa1, 0xf5, 0x6f, 0x47, 0xb8, 0x2f, 0x55, 0xef, 0x96, 0x3e, 0xa1, 0x7b, + 0xfd, 0x91, 0x24, 0x4d, 0x4d, 0xd2, 0x54, 0xd6, 0xc6, 0xb4, 0x30, 0x98, 0x1b, 0x3f, 0xae, 0x2a, + 0x57, 0x61, 0x81, 0x39, 0x8c, 0x67, 0x92, 0x53, 0x7b, 0x21, 0x08, 0x79, 0x56, 0x3e, 0xa4, 0x48, + 0x45, 0x4c, 0x8b, 0x44, 0x8b, 0xd0, 0x08, 0x97, 0xfd, 0x53, 0x7b, 0xc5, 0xd5, 0x6c, 0xb4, 0xf4, + 0x09, 0x7a, 0xfa, 0xe2, 0x39, 0x28, 0xcc, 0x7f, 0x3d, 0x18, 0x5c, 0xfb, 0x00, 0x81, 0xf0, 0x15, + 0x48, 0xd0, 0x6d, 0x31, 0x17, 0x6b, 0x64, 0x41, 0xe9, 0x8a, 0xe7, 0x00, 0x9c, 0xeb, 0x09, 0x18, + 0x9f, 0xdb, 0xed, 0x1a, 0xa7, 0x9c, 0x03, 0x29, 0xe3, 0xdb, 0x5e, 0xa5, 0xdb, 0x05, 0x16, 0xf9, + 0xcd, 0x20, 0x82, 0x35, 0x61, 0x92, 0x0b, 0x0d, 0x2e, 0xc0, 0x6b, 0xcd, 0xf2, 0x5f, 0x69, 0x44, + 0xa9, 0x1e, 0x8e, 0x75, 0xba, 0xfc, 0xe0, 0x50, 0xac, 0xe1, 0xb8, 0x15, 0x65, 0xf4, 0xb7, 0x14, + 0x27, 0xca, 0xce, 0xfb, 0x14, 0x8d, 0x6a, 0x10, 0x8d, 0xcd, 0xfa, 0x00, 0xd9, 0xaa, 0x23, 0x69, + 0x71, 0x3b, 0xd9, 0x9a, 0xc8, 0x9a, 0xb4, 0x78, 0xb2, 0x66, 0x06, 0xb0, 0x38, 0xd3, 0x08, 0xf6, + 0x08, 0x78, 0xdf, 0x56, 0xc2, 0x23, 0x5c, 0x34, 0xf3, 0xba, 0xa4, 0x55, 0x61, 0xd9, 0xb2, 0x21, + 0xd8, 0x35, 0x4d, 0xdd, 0xc0, 0xcc, 0x16, 0xd3, 0xd4, 0x49, 0x01, 0x5e, 0xa6, 0x24, 0x7c, 0x3d, + 0x33, 0x48, 0x97, 0x66, 0x4f, 0x2d, 0x9e, 0xe0, 0x6d, 0x7f, 0x01, 0x8f, 0xb8, 0xc0, 0x89, 0x5d, + 0x99, 0x19, 0x5c, 0x5b, 0xba, 0x48, 0x4a, 0x98, 0xb5, 0x51, 0xc0, 0x5a, 0xc6, 0x08, 0xfa, 0x6a, + 0x25, 0xc2, 0x2b, 0xd8, 0xd2, 0xf5, 0xd2, 0x40, 0x90, 0x8d, 0x2f, 0xaf, 0x29, 0x6c, 0x78, 0x91, + 0x8d, 0x2b, 0xc8, 0x46, 0xb6, 0xb9, 0x58, 0x27, 0x5b, 0xdb, 0x42, 0x21, 0xed, 0x74, 0x10, 0x0b, + 0xc5, 0x6b, 0x5b, 0x84, 0xca, 0xf0, 0xe4, 0x37, 0xb4, 0x7d, 0x71, 0xa2, 0x45, 0x5e, 0x35, 0x5d, + 0xd9, 0xd7, 0xbe, 0x87, 0xa5, 0xb2, 0x53, 0x2b, 0xc7, 0xab, 0x66, 0x59, 0x8e, 0x53, 0xe8, 0x35, + 0xfa, 0x03, 0x1b, 0xe3, 0xe4, 0xe7, 0xa7, 0x07, 0xa9, 0xd7, 0x8a, 0x69, 0x71, 0xa3, 0xd2, 0x74, + 0x5c, 0xf5, 0x3e, 0x86, 0x6e, 0x01, 0x62, 0xb0, 0xc0, 0x11, 0x93, 0x4c, 0x83, 0x85, 0x16, 0xc9, + 0xd8, 0x46, 0x7d, 0x1a, 0x84, 0x18, 0x8d, 0x46, 0x2f, 0xbd, 0x92, 0xb6, 0x1b, 0xbc, 0xd2, 0x19, + 0x6e, 0x77, 0x9f, 0xfe, 0x62, 0xa3, 0xb1, 0x0d, 0x21, 0xd6, 0x46, 0x63, 0x3b, 0x3d, 0x46, 0x05, + 0x3f, 0xf5, 0x05, 0xac, 0x97, 0xf0, 0xd5, 0x11, 0xe0, 0xcf, 0xe4, 0x16, 0x4f, 0x93, 0x77, 0x5d, + 0x0c, 0xd2, 0xcb, 0xb9, 0x7e, 0x83, 0xe8, 0xd3, 0xb4, 0xac, 0x97, 0xdb, 0x6d, 0x6a, 0xcf, 0x0c, + 0xf0, 0x7e, 0xb3, 0x7c, 0xe8, 0x01, 0x65, 0x17, 0x6a, 0xb7, 0xb1, 0x9d, 0xad, 0xb7, 0x2e, 0x32, + 0x76, 0x39, 0x66, 0x88, 0x26, 0x6f, 0xe5, 0x65, 0x96, 0xd7, 0x2d, 0x5a, 0x13, 0xb7, 0x97, 0xee, + 0x38, 0xac, 0xd0, 0x39, 0xc6, 0x9b, 0x6d, 0xb8, 0x35, 0x20, 0x6f, 0xea, 0xa4, 0x3c, 0xee, 0x64, + 0xc1, 0x95, 0x43, 0x3d, 0xdd, 0xfe, 0x99, 0x04, 0x94, 0x6f, 0x73, 0xd2, 0xd0, 0xa7, 0xcd, 0x8a, + 0x1b, 0xf1, 0x41, 0x6a, 0xc7, 0x70, 0xbb, 0x8f, 0x9a, 0x33, 0x8b, 0xa7, 0xca, 0xf3, 0x42, 0x14, + 0xf0, 0xf3, 0x03, 0xe0, 0xcb, 0x78, 0xbf, 0xbc, 0x30, 0x5e, 0x89, 0xb1, 0x7b, 0xf0, 0xf4, 0x68, + 0x73, 0xe8, 0xf4, 0xc8, 0xa6, 0x6b, 0xa3, 0xb6, 0x62, 0xf5, 0x07, 0x02, 0x16, 0x5b, 0x2f, 0x66, + 0xc1, 0xac, 0xfc, 0x04, 0x14, 0x6c, 0x53, 0x61, 0x19, 0xf0, 0x7d, 0x49, 0xed, 0x07, 0xaf, 0x12, + 0x4c, 0x88, 0x08, 0x9d, 0xf3, 0x85, 0xe5, 0xd5, 0xe7, 0x75, 0x7e, 0x18, 0x43, 0xb3, 0x81, 0xbc, + 0xce, 0xc4, 0x98, 0x50, 0x5a, 0x20, 0xe6, 0x69, 0xb2, 0x65, 0xdb, 0x35, 0x45, 0x5f, 0x1a, 0x84, + 0x86, 0x30, 0x8b, 0x3c, 0x3a, 0x14, 0xb8, 0x54, 0x2e, 0x9a, 0xcd, 0xab, 0xb2, 0x73, 0x7d, 0xea, + 0x86, 0x11, 0xb3, 0x49, 0x6f, 0x52, 0xb6, 0x59, 0xbe, 0xe1, 0xa7, 0x5c, 0x05, 0x2f, 0x2d, 0x39, + 0xc7, 0xe6, 0xda, 0x70, 0xe7, 0x24, 0x5b, 0x36, 0xd1, 0x05, 0x59, 0xe3, 0x4b, 0x04, 0xc5, 0xd9, + 0x30, 0x26, 0x78, 0x8a, 0xc4, 0xc7, 0x41, 0x09, 0x4f, 0x54, 0x95, 0xd2, 0x18, 0x28, 0x09, 0x91, + 0xc1, 0x92, 0x73, 0x14, 0xd9, 0xac, 0x26, 0xe5, 0x1a, 0xca, 0x0e, 0xe4, 0x66, 0x3d, 0x34, 0xfb, + 0xe5, 0xd5, 0x95, 0x8f, 0x30, 0xce, 0xbb, 0x2a, 0xb7, 0xb2, 0x0c, 0x7f, 0x37, 0x4c, 0x3c, 0xfa, + 0x93, 0xf9, 0xf7, 0xd2, 0x2e, 0xbe, 0x11, 0x96, 0x80, 0x25, 0x06, 0xf7, 0x0c, 0x36, 0x6a, 0x20, + 0x61, 0x92, 0xa7, 0x28, 0x60, 0xd4, 0xa1, 0x68, 0xe5, 0x71, 0x4d, 0x28, 0xc1, 0xaf, 0x5c, 0xc8, + 0x38, 0xef, 0x86, 0x04, 0x35, 0x3c, 0x43, 0x1b, 0xe0, 0x63, 0x32, 0x31, 0x09, 0x64, 0xef, 0x0f, + 0x1e, 0x01, 0xe4, 0x05, 0x9e, 0x8a, 0x0e, 0x32, 0x6e, 0xc4, 0x33, 0x80, 0xca, 0x9f, 0x32, 0x4e, + 0x43, 0xe5, 0x8b, 0x2c, 0x23, 0x6e, 0xd2, 0xf2, 0xf9, 0xfa, 0xa3, 0x74, 0x2f, 0x27, 0x3c, 0x37, + 0x4e, 0xcc, 0xd7, 0x8d, 0x3d, 0x56, 0xf5, 0x05, 0x03, 0xfc, 0x66, 0x66, 0x16, 0xb3, 0x99, 0xe6, + 0xec, 0xac, 0x4c, 0x8b, 0x9c, 0x4c, 0x8f, 0x18, 0xe3, 0x1b, 0xd2, 0x59, 0x16, 0xda, 0xfb, 0x66, + 0x9b, 0x03, 0x9b, 0xe2, 0xe4, 0xff, 0x99, 0x16, 0x4c, 0x83, 0xf4, 0xee, 0xc0, 0x26, 0x1d, 0x40, + 0xcc, 0x66, 0x4b, 0x6e, 0x20, 0x37, 0x1a, 0x92, 0x1a, 0xf8, 0x42, 0x92, 0xc9, 0x26, 0xad, 0xb5, + 0x71, 0x04, 0xcb, 0x12, 0xff, 0x90, 0xdc, 0xb3, 0xaa, 0xeb, 0x5d, 0x35, 0x9e, 0xcb, 0x43, 0xbe, + 0x2b, 0xf1, 0x67, 0x73, 0x8b, 0x23, 0x72, 0x6d, 0x18, 0xc4, 0x58, 0xb2, 0xe5, 0xf6, 0xf3, 0xf4, + 0x74, 0xb4, 0x21, 0x2d, 0x82, 0xb1, 0x79, 0x2f, 0xa3, 0x8b, 0xbd, 0xfc, 0xdd, 0x2d, 0x27, 0xf7, + 0x27, 0x70, 0x07, 0xe0, 0x78, 0x8a, 0xa6, 0x85, 0xec, 0x83, 0x78, 0xc5, 0x7b, 0xda, 0x31, 0xe2, + 0xb9, 0xe8, 0x1c, 0x6d, 0x08, 0xe7, 0xaa, 0xf1, 0xa6, 0x94, 0xf8, 0xa4, 0xc6, 0xf7, 0x5d, 0xb1, + 0x56, 0x4e, 0x09, 0xb6, 0xaa, 0x07, 0xa9, 0x90, 0xf0, 0x43, 0x34, 0x04, 0x0c, 0xe4, 0xda, 0x18, + 0x35, 0xde, 0xc7, 0x91, 0xa4, 0x95, 0x3d, 0xc0, 0x9d, 0x59, 0x2e, 0x3f, 0x1f, 0x3a, 0x1e, 0x47, + 0x80, 0xd0, 0xcb, 0x3a, 0xef, 0x5a, 0x82, 0x11, 0x1b, 0x57, 0x5d, 0x8d, 0xc1, 0x55, 0x03, 0x36, + 0x07, 0x04, 0xbe, 0x06, 0xd1, 0xd5, 0xdf, 0x43, 0x0f, 0xb0, 0xcd, 0x72, 0xe7, 0xd4, 0xe0, 0xa5, + 0x31, 0x95, 0x28, 0x5d, 0x03, 0xa0, 0xe7, 0xf2, 0x28, 0xfd, 0xe9, 0xac, 0x80, 0x43, 0x9d, 0x95, + 0x69, 0x46, 0xc5, 0x8b, 0x53, 0x70, 0xbe, 0x87, 0x94, 0x91, 0x14, 0x0d, 0xa1, 0x24, 0xe2, 0xe1, + 0x16, 0x10, 0x1c, 0xf3, 0x38, 0x08, 0x7c, 0xb6, 0xf4, 0x22, 0xeb, 0xec, 0x11, 0x53, 0xf0, 0xb4, + 0x40, 0x74, 0xa8, 0x25, 0x3a, 0x36, 0xb5, 0x4d, 0x86, 0x49, 0xe7, 0x76, 0x3d, 0x1e, 0x70, 0x1d, + 0x75, 0x46, 0xe1, 0x21, 0x58, 0x44, 0x8d, 0x3a, 0xc9, 0xf7, 0xdf, 0x0d, 0x78, 0x87, 0xad, 0x0d, + 0x82, 0xa6, 0xbc, 0x2e, 0xdc, 0xd9, 0x7f, 0x1d, 0xdd, 0xc5, 0xae, 0xf1, 0xae, 0xd3, 0x5c, 0xf5, + 0x9f, 0xd1, 0x8e, 0x4c, 0x1d, 0xdc, 0xae, 0x14, 0x1d, 0x7a, 0x03, 0x36, 0xa1, 0x4e, 0x5a, 0x19, + 0xf5, 0xb9, 0xd0, 0xaf, 0x7a, 0x61, 0x8e, 0x34, 0x2f, 0x5c, 0xb8, 0xca, 0x4a, 0xcb, 0xf5, 0x53, + 0x2b, 0xb7, 0x84, 0xb3, 0xce, 0xc8, 0xca, 0x2d, 0x61, 0x61, 0x7c, 0x5c, 0xa5, 0x61, 0x05, 0x1f, + 0x55, 0xb9, 0x85, 0x35, 0x71, 0xd5, 0xef, 0x23, 0x97, 0xbb, 0x9e, 0x0d, 0x4b, 0xb8, 0xc6, 0x5e, + 0x15, 0xfa, 0xc3, 0x5f, 0xf4, 0xea, 0x8f, 0x82, 0x6a, 0xde, 0xcf, 0xed, 0x5a, 0x39, 0x6a, 0xaa, + 0xd0, 0x3f, 0xcb, 0x39, 0x42, 0xe8, 0x5f, 0xc4, 0x8f, 0xf4, 0xc5, 0x56, 0xbe, 0x30, 0x6a, 0xda, + 0x22, 0xe7, 0x38, 0xe9, 0x59, 0xb6, 0xe6, 0xcd, 0xd1, 0x16, 0x30, 0x60, 0x43, 0x03, 0xb9, 0xca, + 0x7b, 0x89, 0xa7, 0xa1, 0x9a, 0x16, 0x19, 0x85, 0x7c, 0x49, 0xc2, 0x02, 0x48, 0x74, 0xfd, 0xb7, + 0xc1, 0xf9, 0x72, 0x26, 0x72, 0xbb, 0xc2, 0x45, 0x53, 0x47, 0x82, 0x87, 0x45, 0x2d, 0x7f, 0xa0, + 0xaa, 0x8f, 0x05, 0x61, 0x72, 0xdb, 0x4e, 0x23, 0xd1, 0x4d, 0x55, 0x97, 0x99, 0xde, 0x40, 0x60, + 0x7a, 0x60, 0x04, 0xc3, 0xb0, 0xf5, 0x7b, 0x30, 0x09, 0xf3, 0x8d, 0x84, 0x84, 0x07, 0x62, 0x54, + 0xe8, 0x82, 0xf7, 0xe0, 0x5c, 0x98, 0x64, 0xf8, 0x56, 0x20, 0x89, 0x42, 0xc3, 0x0d, 0xae, 0x3a, + 0x0f, 0x63, 0x28, 0x4f, 0x84, 0x25, 0x53, 0x72, 0x59, 0x68, 0x02, 0x90, 0xf0, 0x13, 0xaa, 0xd7, + 0x0d, 0xd5, 0x0f, 0x51, 0x32, 0x2a, 0x97, 0x60, 0xf6, 0x1b, 0xa4, 0x86, 0xa3, 0xe1, 0xf9, 0x2d, + 0x9a, 0xb6, 0x43, 0x44, 0xd1, 0xb9, 0x1e, 0x11, 0x54, 0xc8, 0x8c, 0x73, 0x25, 0x78, 0x40, 0x71, + 0x55, 0x98, 0x58, 0x1e, 0x55, 0xf9, 0x62, 0x80, 0x71, 0x16, 0x74, 0x83, 0x57, 0x73, 0x8e, 0x09, + 0xf0, 0x6d, 0xe0, 0x77, 0xe4, 0xfa, 0x29, 0xc1, 0x43, 0xc5, 0x70, 0x1d, 0x9d, 0x65, 0xcf, 0x95, + 0x51, 0x10, 0x78, 0x5b, 0x32, 0xb3, 0xd1, 0x35, 0xe3, 0xec, 0x3e, 0x72, 0x8e, 0xce, 0x2e, 0x3a, + 0xb7, 0x76, 0x65, 0xb0, 0xce, 0x0c, 0xbc, 0x4e, 0xa4, 0x41, 0x97, 0xe5, 0x52, 0x7a, 0xe3, 0x85, + 0x92, 0xaa, 0x23, 0xa8, 0xe3, 0x54, 0xe9, 0x71, 0xe2, 0x57, 0xa9, 0x48, 0x39, 0xd8, 0x09, 0xac, + 0x64, 0x1f, 0x4d, 0x09, 0xba, 0x18, 0x48, 0xab, 0x1a, 0x93, 0xa2, 0xd3, 0xfe, 0xc6, 0x30, 0x8d, + 0x49, 0x31, 0x9b, 0xfe, 0x86, 0x5e, 0x57, 0x4d, 0xd2, 0xe3, 0x1a, 0xd3, 0xe9, 0x4d, 0x45, 0x54, + 0xdb, 0x74, 0x95, 0x6f, 0x97, 0x2d, 0x42, 0xd9, 0xd7, 0xd8, 0xe8, 0xa3, 0x89, 0xa6, 0x68, 0x8a, + 0xc6, 0xac, 0xd6, 0xac, 0x15, 0xc3, 0xc4, 0x2c, 0x95, 0xe7, 0x8b, 0x51, 0xe1, 0x39, 0x5a, 0xe1, + 0x14, 0x13, 0xe0, 0xe3, 0xd0, 0xde, 0xb3, 0xc0, 0xe0, 0x03, 0x89, 0xf2, 0x3b, 0x67, 0x29, 0x8d, + 0x21, 0xa7, 0x00, 0xb4, 0x95, 0x6b, 0xe5, 0x4e, 0xe5, 0x36, 0x0d, 0x05, 0x0a, 0x5d, 0xf3, 0x20, + 0xc3, 0x95, 0x4c, 0xc7, 0x65, 0x2d, 0x75, 0xc5, 0x7a, 0xb7, 0xeb, 0x28, 0x1f, 0x0e, 0xb9, 0x94, + 0x70, 0x64, 0xde, 0xa0, 0x91, 0xa6, 0xc2, 0xb4, 0x90, 0x13, 0x64, 0x2c, 0x04, 0x14, 0xd2, 0xce, + 0x1f, 0x70, 0x09, 0xc3, 0x60, 0xc8, 0x6c, 0xce, 0xcc, 0x92, 0x17, 0x2a, 0x23, 0x48, 0xae, 0xaf, + 0x00, 0x5e, 0x1c, 0x4e, 0x6f, 0x82, 0x99, 0xe5, 0xb6, 0xae, 0xd0, 0xaa, 0xb7, 0x58, 0x72, 0x85, + 0x0d, 0x0c, 0xa9, 0xa9, 0xbb, 0x55, 0x33, 0x4d, 0x72, 0x7d, 0x8c, 0x7d, 0x22, 0xe8, 0x55, 0x30, + 0xb3, 0xfc, 0xe1, 0xad, 0x1a, 0x35, 0x84, 0x96, 0x9d, 0x66, 0x65, 0x31, 0xf4, 0xfc, 0x80, 0x8b, + 0x21, 0x84, 0x67, 0x11, 0xdc, 0x6e, 0x69, 0x3f, 0x14, 0x60, 0x1d, 0x93, 0x52, 0x47, 0x60, 0x5a, + 0xf5, 0x6b, 0x80, 0x1c, 0xc2, 0xea, 0x8a, 0x8e, 0x9e, 0x00, 0xa9, 0x83, 0xc6, 0xf5, 0x50, 0x02, + 0x27, 0x1b, 0x29, 0xba, 0x5a, 0x68, 0xcb, 0xa3, 0xdc, 0xee, 0x83, 0xb8, 0x7b, 0xe5, 0x0a, 0x88, + 0xdb, 0xcf, 0xd0, 0x96, 0x10, 0x0b, 0xb4, 0x73, 0xa9, 0x37, 0x6a, 0xb7, 0xbb, 0xa1, 0x58, 0x93, + 0x77, 0x9d, 0xdb, 0x75, 0x91, 0xdb, 0x71, 0x03, 0xe2, 0x6c, 0xb6, 0x99, 0xed, 0x4f, 0x68, 0xe1, + 0x76, 0x1d, 0x3c, 0x08, 0x15, 0xfa, 0xd3, 0x3d, 0x3d, 0x4b, 0xbe, 0xc7, 0x0d, 0xfb, 0x09, 0xc4, + 0xdb, 0x98, 0xa6, 0xd2, 0xe0, 0xc5, 0x4b, 0x63, 0x17, 0xbd, 0x8f, 0x6c, 0xba, 0xee, 0x9b, 0xb5, + 0xe4, 0xfb, 0x00, 0x7d, 0x22, 0xde, 0x4d, 0x5a, 0xa8, 0xd6, 0x0e, 0x56, 0xfb, 0xc6, 0x41, 0x19, + 0xa7, 0xed, 0x1f, 0x2c, 0x1e, 0x82, 0xe7, 0xb4, 0x93, 0x1c, 0x59, 0xcc, 0xe9, 0xb0, 0x12, 0x8b, + 0x86, 0xf4, 0xd7, 0x58, 0xc6, 0x88, 0x79, 0x6d, 0x56, 0xb2, 0x46, 0x5b, 0xb3, 0x62, 0x9c, 0xf8, + 0xd6, 0x71, 0xa4, 0xc8, 0x12, 0x2d, 0x9a, 0x64, 0xb2, 0x22, 0x46, 0x3c, 0xd8, 0x86, 0xc5, 0xad, + 0x10, 0xc4, 0xc4, 0x41, 0x02, 0x00, 0x1d, 0x66, 0x70, 0x3b, 0x3c, 0xac, 0x17, 0x72, 0xef, 0xb4, + 0x99, 0x30, 0x1f, 0x40, 0x12, 0xdf, 0x51, 0x59, 0xd1, 0xc5, 0x90, 0xb4, 0x44, 0xa7, 0x56, 0xca, + 0xd9, 0x69, 0x25, 0xcf, 0x4d, 0xf4, 0x7c, 0xa7, 0x85, 0xaf, 0x68, 0x9f, 0x0a, 0x0c, 0xeb, 0x34, + 0x59, 0xa3, 0xd3, 0x5f, 0x94, 0x20, 0xf9, 0xbd, 0x0a, 0x6b, 0xd4, 0x67, 0xa1, 0x15, 0x87, 0xae, + 0xb9, 0x38, 0xa3, 0x2d, 0x18, 0x03, 0x7a, 0xab, 0x8e, 0xe0, 0x5c, 0x0e, 0xf3, 0xc0, 0xd5, 0x18, + 0x5d, 0xa2, 0x0e, 0xbc, 0x2d, 0x6b, 0xf0, 0x26, 0xd0, 0x45, 0xf4, 0xb0, 0xaf, 0x20, 0x30, 0x0f, + 0xa2, 0xaf, 0xc7, 0x21, 0x03, 0x56, 0xfc, 0x54, 0xb7, 0x68, 0xfc, 0xad, 0xfe, 0x28, 0x68, 0xbf, + 0xd0, 0xab, 0xe3, 0x41, 0xf7, 0x0e, 0x48, 0xd2, 0x66, 0xba, 0xb7, 0x90, 0x3c, 0xd1, 0x23, 0x03, + 0x1d, 0xc9, 0xd1, 0x78, 0x82, 0x92, 0x73, 0x5c, 0x52, 0x2d, 0xd2, 0x77, 0x73, 0xbb, 0x34, 0x89, + 0xe8, 0xb8, 0x38, 0xf0, 0x60, 0xcf, 0xa4, 0x55, 0x0d, 0x44, 0x00, 0x3e, 0xfe, 0x1b, 0x5f, 0x85, + 0xc5, 0x2c, 0xb9, 0x1c, 0xf4, 0xca, 0x9c, 0xe3, 0xde, 0x90, 0xd5, 0x62, 0x99, 0x81, 0xe1, 0xc5, + 0x1c, 0x7f, 0xb0, 0x52, 0x32, 0x1e, 0x37, 0xcb, 0x13, 0x43, 0x2c, 0x70, 0xae, 0x51, 0x78, 0x59, + 0xef, 0x85, 0x05, 0x6c, 0xb1, 0x4e, 0xe2, 0x8f, 0x43, 0xe2, 0xbf, 0x99, 0xda, 0xbd, 0x5e, 0xc1, + 0x20, 0x05, 0xd3, 0x6c, 0x3f, 0xb9, 0xe6, 0x13, 0x6e, 0xe1, 0x27, 0xe7, 0xe4, 0xb1, 0x50, 0x8f, + 0x57, 0x0b, 0x86, 0x8e, 0x81, 0x87, 0x40, 0xa1, 0x51, 0x2c, 0xf2, 0x6e, 0x46, 0xb1, 0x52, 0xca, + 0x32, 0x9e, 0x5d, 0x82, 0x55, 0x46, 0x07, 0x83, 0xae, 0xfa, 0xe2, 0x0c, 0x59, 0xad, 0x3c, 0x0b, + 0x15, 0x5d, 0x30, 0xf0, 0xdb, 0x7b, 0x47, 0xa3, 0x89, 0x34, 0x5e, 0xff, 0x03, 0xb7, 0x3b, 0xe7, + 0x2b, 0x62, 0x9e, 0xc7, 0x8f, 0x13, 0x4d, 0xed, 0x9e, 0xcb, 0xda, 0x91, 0xcd, 0x78, 0x2e, 0x6b, + 0x9e, 0x47, 0x1a, 0x7d, 0x11, 0x60, 0xcc, 0x39, 0x7e, 0x98, 0x51, 0x48, 0x7b, 0xb9, 0xdd, 0xe5, + 0x1a, 0x29, 0x55, 0x8f, 0xca, 0xd4, 0x4d, 0xd2, 0x17, 0x93, 0xc7, 0x17, 0x92, 0x5e, 0xc8, 0xbf, + 0x14, 0x46, 0xcd, 0xf2, 0x2f, 0x95, 0x03, 0x6a, 0x92, 0x9e, 0x44, 0x9e, 0xd7, 0x36, 0xa6, 0x44, + 0xaf, 0xf8, 0x04, 0x2f, 0xf7, 0xc6, 0x38, 0xe0, 0x4b, 0x4c, 0x5f, 0x48, 0x52, 0xe2, 0x82, 0x17, + 0x95, 0xd1, 0xfc, 0x61, 0x81, 0x80, 0x99, 0xab, 0x0e, 0x45, 0x3e, 0xfa, 0x8b, 0xbe, 0xef, 0x42, + 0xb3, 0xb7, 0x10, 0x33, 0xdc, 0xb3, 0xe4, 0x24, 0xb7, 0x2b, 0x19, 0x22, 0xa8, 0x27, 0x34, 0xdc, + 0x0e, 0xaf, 0xe4, 0x3a, 0x00, 0xec, 0xd9, 0x22, 0xe8, 0x95, 0x2d, 0xb3, 0x3c, 0x65, 0x9a, 0x92, + 0x9a, 0x74, 0x3b, 0x4b, 0xb8, 0xdd, 0x96, 0x89, 0xc2, 0x8b, 0x2a, 0x1d, 0xc9, 0xf3, 0xf3, 0x23, + 0x21, 0xa9, 0x89, 0xf6, 0xb4, 0x6b, 0x47, 0xd6, 0x61, 0xc4, 0xd8, 0x67, 0x96, 0x23, 0xc2, 0x83, + 0x61, 0x12, 0x57, 0xfd, 0x1c, 0x26, 0x11, 0x7c, 0x9b, 0x98, 0xe7, 0xe7, 0x76, 0x7c, 0x0e, 0x0e, + 0xaf, 0x43, 0x1e, 0x50, 0xee, 0xfa, 0x90, 0x3e, 0xd2, 0x08, 0xea, 0xac, 0x77, 0x9b, 0xb3, 0xe1, + 0x43, 0x77, 0x52, 0x9a, 0x68, 0xb7, 0x68, 0xcf, 0x57, 0x61, 0x96, 0x00, 0xdf, 0x21, 0xbf, 0x32, + 0x51, 0x89, 0x54, 0x92, 0xb8, 0xdd, 0xe9, 0x33, 0xc0, 0x6a, 0xa5, 0xd4, 0xf0, 0x00, 0x5e, 0xdb, + 0xee, 0xb9, 0x0e, 0x39, 0x34, 0x10, 0x97, 0x2d, 0x7f, 0xac, 0xa0, 0xe2, 0x76, 0xe7, 0x02, 0xb1, + 0xb9, 0x33, 0xb9, 0xdd, 0x8f, 0xcf, 0xf0, 0x5c, 0x06, 0x77, 0x6e, 0xc9, 0x32, 0x07, 0x16, 0x90, + 0x46, 0xf9, 0xab, 0x53, 0xd4, 0xa3, 0x08, 0x37, 0x20, 0x4e, 0xfc, 0x82, 0xe6, 0xef, 0x7e, 0x0f, + 0x08, 0x36, 0xaf, 0x45, 0xca, 0x65, 0xa5, 0xfc, 0x31, 0xa0, 0x82, 0x15, 0xed, 0xe4, 0x84, 0xe7, + 0xab, 0x88, 0x5a, 0xd5, 0x02, 0xbc, 0xd9, 0xb7, 0x1f, 0xdb, 0x98, 0x8e, 0x4b, 0x9a, 0x05, 0xc4, + 0xd4, 0x25, 0x54, 0x68, 0x98, 0xda, 0xe5, 0x0b, 0x46, 0x91, 0x9c, 0x0e, 0xae, 0xfa, 0x25, 0xa8, + 0xa9, 0xec, 0xe3, 0x38, 0x17, 0x9a, 0xe6, 0x87, 0x49, 0xce, 0x49, 0x8a, 0x4c, 0x74, 0x8a, 0x80, + 0x80, 0xe3, 0xa9, 0xa1, 0xe8, 0xbe, 0x96, 0x89, 0x9f, 0xc2, 0xed, 0x6a, 0x92, 0xa2, 0x97, 0x49, + 0xa9, 0xbf, 0xb8, 0xbe, 0x24, 0x1c, 0x7d, 0xcf, 0xcb, 0xcb, 0xe9, 0xe0, 0x32, 0xa9, 0x97, 0x52, + 0x01, 0xa1, 0xd1, 0x2b, 0x45, 0xff, 0x82, 0xe4, 0x9d, 0x91, 0x5e, 0xb9, 0x07, 0x7b, 0x4b, 0x6f, + 0x4e, 0xc0, 0xaf, 0xc3, 0x37, 0x70, 0xf7, 0x27, 0xaf, 0xa3, 0x36, 0xf5, 0xf9, 0xa8, 0x9a, 0xd4, + 0x87, 0x7b, 0xd3, 0xf9, 0xff, 0xd1, 0x9f, 0x16, 0xf3, 0xe4, 0x5a, 0x55, 0x76, 0x4d, 0xea, 0xd8, + 0x08, 0xc8, 0xd0, 0x23, 0xf9, 0xeb, 0xa2, 0xa9, 0xab, 0x36, 0x19, 0xac, 0xfa, 0x4c, 0x4d, 0xf5, + 0x84, 0x46, 0xe8, 0x54, 0x9b, 0x3c, 0x19, 0x9e, 0x9a, 0xe0, 0xc9, 0x75, 0x1a, 0xd2, 0x5f, 0xce, + 0xf5, 0x05, 0xde, 0x17, 0xc5, 0x41, 0x2b, 0x0f, 0x44, 0x20, 0x56, 0x1d, 0xaf, 0x16, 0xdc, 0x01, + 0xfb, 0x55, 0xbd, 0x3b, 0x43, 0x7a, 0x0b, 0x01, 0x0d, 0xa9, 0x5a, 0x36, 0xb4, 0xb0, 0xed, 0xcd, + 0x79, 0x07, 0x5a, 0x4f, 0x79, 0x07, 0x8f, 0x92, 0x9a, 0xc0, 0xcd, 0x67, 0x05, 0x4e, 0x29, 0x66, + 0x52, 0xfd, 0x13, 0x7a, 0x25, 0xc0, 0x5b, 0xab, 0xee, 0xf6, 0xa8, 0x39, 0x97, 0x84, 0x48, 0x21, + 0x31, 0x84, 0xce, 0x11, 0xc7, 0x75, 0x49, 0x7c, 0x2f, 0x04, 0x7b, 0xb5, 0xb8, 0xd1, 0xbf, 0x4a, + 0x1f, 0xf8, 0x3b, 0x0b, 0xf9, 0x82, 0xbe, 0x3b, 0x6d, 0xf1, 0x2c, 0xe7, 0x2c, 0x21, 0xa0, 0xe6, + 0xde, 0xf0, 0x08, 0x81, 0xa8, 0x4d, 0x2b, 0xa7, 0x99, 0x64, 0xfe, 0x71, 0xee, 0x90, 0xdb, 0x2c, + 0x7f, 0xf7, 0x25, 0x0d, 0x86, 0x6b, 0x52, 0x97, 0x78, 0xd2, 0xb1, 0x9f, 0x2a, 0x31, 0xad, 0xda, + 0xcd, 0xb7, 0xf8, 0xe2, 0xba, 0x8d, 0x32, 0xeb, 0x7c, 0x40, 0x01, 0x1d, 0x56, 0x53, 0xc7, 0x27, + 0x33, 0xca, 0x46, 0xfd, 0xdf, 0x8b, 0x47, 0x49, 0x46, 0x8f, 0x19, 0x17, 0xcf, 0xb6, 0xaf, 0x42, + 0xbe, 0xfc, 0x05, 0x60, 0xd0, 0x17, 0x51, 0xd9, 0xa7, 0x76, 0x2e, 0x14, 0x4d, 0xc7, 0x93, 0x41, + 0x79, 0x84, 0x3a, 0x9d, 0x50, 0xaf, 0x5b, 0x64, 0x6a, 0xe1, 0x5c, 0x0f, 0x0d, 0xa0, 0xf0, 0xfc, + 0xfa, 0xee, 0x15, 0xd9, 0xb9, 0x72, 0x56, 0x8c, 0xa2, 0x39, 0xe1, 0x50, 0x24, 0xfe, 0xaa, 0x23, + 0x0b, 0x43, 0xea, 0x0e, 0x91, 0xde, 0x8b, 0x31, 0xa1, 0x8d, 0x88, 0x51, 0x80, 0xde, 0x2c, 0xcf, + 0x9f, 0xa2, 0x9c, 0x70, 0x8f, 0x42, 0x03, 0xc9, 0xf3, 0x57, 0xbe, 0x30, 0x02, 0x65, 0xd6, 0x00, + 0xeb, 0x02, 0x93, 0xc1, 0x5f, 0x45, 0x93, 0x59, 0xa8, 0xef, 0xf6, 0x4d, 0x28, 0xd6, 0xd1, 0x0c, + 0x42, 0x2d, 0x47, 0x2a, 0xed, 0xf5, 0xdd, 0xd0, 0x18, 0x09, 0x7a, 0x4d, 0x4a, 0x7d, 0x44, 0x57, + 0xac, 0xa3, 0xeb, 0xf8, 0x37, 0x93, 0x07, 0xeb, 0x3a, 0x9d, 0x43, 0x8e, 0xc4, 0x35, 0xa0, 0x4f, + 0xe2, 0x16, 0x95, 0xde, 0x2d, 0x04, 0x34, 0x7b, 0xbf, 0xc3, 0x3b, 0x0d, 0xc6, 0x26, 0x21, 0xa0, + 0x3d, 0xf4, 0x5d, 0x00, 0x1f, 0xfd, 0x23, 0x4f, 0x08, 0x01, 0xdd, 0x7e, 0x08, 0x4a, 0x39, 0xce, + 0x08, 0xa1, 0x56, 0x98, 0xbd, 0xd8, 0xd0, 0x6a, 0xb7, 0x4a, 0xe0, 0x87, 0xb4, 0xa0, 0xf0, 0x01, + 0x74, 0xfc, 0xa6, 0x2e, 0x3c, 0xc3, 0x91, 0x77, 0x52, 0x4f, 0xa6, 0x02, 0xb3, 0x4f, 0x17, 0x1a, + 0x54, 0xd8, 0x54, 0x65, 0xb8, 0xb0, 0xf1, 0xb2, 0x45, 0xfe, 0xf0, 0xee, 0x5b, 0xa1, 0xa9, 0x0c, + 0x0e, 0xcc, 0x15, 0x70, 0x4e, 0xc1, 0x9b, 0x57, 0x7e, 0xa2, 0xfa, 0x58, 0x44, 0x37, 0xa2, 0x74, + 0x7f, 0x56, 0xe9, 0x9e, 0x66, 0xe8, 0xe1, 0xaf, 0x50, 0xd6, 0xf0, 0x22, 0xb4, 0x26, 0xe4, 0xc3, + 0x31, 0x08, 0xc8, 0xd0, 0x8a, 0x99, 0x49, 0xca, 0xca, 0x1f, 0xe0, 0xa3, 0x21, 0x1d, 0xc5, 0x70, + 0x7f, 0x36, 0x75, 0x6e, 0xd1, 0xc1, 0x33, 0x35, 0x68, 0x55, 0x0e, 0x89, 0x63, 0xaa, 0xa7, 0x3d, + 0xcc, 0x1c, 0xd8, 0x98, 0x24, 0x77, 0xdc, 0xa5, 0xa4, 0x0e, 0x19, 0x16, 0xf9, 0xfc, 0x5d, 0x43, + 0x52, 0x87, 0xe0, 0x59, 0x79, 0x8e, 0x17, 0x4f, 0xa0, 0x4d, 0x31, 0x22, 0x1f, 0x4d, 0xfc, 0x23, + 0x9b, 0x94, 0xfb, 0x3c, 0xa2, 0xc9, 0xcf, 0xed, 0x4e, 0xd6, 0x70, 0xbb, 0x96, 0x8d, 0xe1, 0x76, + 0x34, 0x81, 0x74, 0xf4, 0x47, 0x61, 0xd9, 0xc9, 0x69, 0x23, 0xa6, 0xf6, 0x70, 0xbc, 0x67, 0xd4, + 0x31, 0x3e, 0x47, 0x0b, 0x6c, 0x83, 0x5b, 0x00, 0xb1, 0x67, 0x65, 0xca, 0xbf, 0x98, 0xa6, 0x9c, + 0xa0, 0x1b, 0xcf, 0x90, 0xd6, 0x07, 0xf8, 0x76, 0xb1, 0x22, 0x66, 0x52, 0x45, 0x1b, 0x70, 0xc5, + 0xed, 0x38, 0xdf, 0x73, 0x99, 0x78, 0xd8, 0x0b, 0xa2, 0xb1, 0x23, 0xa1, 0x09, 0xdc, 0x0b, 0x38, + 0xcc, 0xd8, 0x53, 0xa0, 0x91, 0x10, 0xd8, 0x26, 0xd4, 0xef, 0xa3, 0x67, 0x59, 0xcd, 0x86, 0xcf, + 0x89, 0x17, 0xdf, 0x50, 0x38, 0xc7, 0x2d, 0xfd, 0x9c, 0x4b, 0x85, 0xc7, 0x73, 0xe0, 0xbb, 0x8d, + 0x5e, 0xd1, 0x74, 0x46, 0xd4, 0xd4, 0x08, 0x9e, 0x01, 0x10, 0x10, 0xb7, 0x6b, 0x29, 0x10, 0xe2, + 0xd1, 0x1f, 0x0d, 0x5f, 0xaa, 0xe3, 0x5e, 0xf7, 0x70, 0xbb, 0xb3, 0xc0, 0xa3, 0x34, 0xf7, 0x7c, + 0xc1, 0x9e, 0x0a, 0x37, 0x76, 0x8c, 0x07, 0x74, 0x39, 0x6a, 0x98, 0x6c, 0xb6, 0xdf, 0x92, 0x9d, + 0x69, 0xce, 0x02, 0xb1, 0x42, 0x40, 0x44, 0x3e, 0x05, 0x8d, 0xcb, 0xc1, 0x2b, 0xc3, 0xe0, 0x62, + 0xd8, 0xf3, 0x63, 0xaf, 0x01, 0x83, 0x86, 0xab, 0xf8, 0x4a, 0x43, 0x0c, 0xb7, 0x4d, 0x8f, 0x81, + 0xe1, 0xb0, 0xb3, 0x78, 0xbc, 0x63, 0x2e, 0x26, 0xab, 0x60, 0xc5, 0x73, 0xde, 0x27, 0xf4, 0x6a, + 0xf9, 0x54, 0xbd, 0x9b, 0x8c, 0xe0, 0x76, 0x3f, 0x0b, 0xe3, 0x3c, 0x1b, 0x27, 0x45, 0x5b, 0xcc, + 0xc5, 0x61, 0xe6, 0xac, 0xdc, 0x80, 0x5e, 0xd6, 0x7f, 0x8a, 0x5d, 0xe3, 0x7c, 0xcf, 0x52, 0x01, + 0xed, 0x48, 0x8e, 0x93, 0x54, 0xaf, 0x41, 0x2e, 0x50, 0xac, 0xca, 0x32, 0x63, 0xf5, 0xb8, 0x4f, + 0x95, 0xbb, 0x9e, 0xeb, 0xc3, 0x39, 0xdf, 0x63, 0x88, 0x43, 0xe2, 0xaf, 0x59, 0x1b, 0x23, 0x31, + 0xc2, 0x51, 0x70, 0x91, 0x73, 0xa4, 0xf5, 0x94, 0x8f, 0x6d, 0x65, 0xaf, 0x23, 0xca, 0x6c, 0xa8, + 0xb6, 0x40, 0xb7, 0x8b, 0xc7, 0x83, 0x97, 0xe0, 0xe9, 0xa6, 0x8c, 0x6a, 0x3a, 0xbd, 0xc8, 0xd2, + 0xdf, 0x5d, 0xc7, 0x72, 0x46, 0x0f, 0xbd, 0xa7, 0x4d, 0x2f, 0xb5, 0x0f, 0x39, 0x3f, 0x07, 0xa5, + 0x85, 0x64, 0xca, 0xb6, 0x0e, 0xfc, 0xb7, 0xfc, 0xd8, 0x84, 0xe0, 0x46, 0x09, 0x89, 0x11, 0xdc, + 0x2a, 0x4c, 0x79, 0x30, 0xa4, 0x82, 0xc4, 0x47, 0x85, 0x37, 0xf9, 0xe5, 0x17, 0x43, 0xb8, 0xd3, + 0x16, 0xd3, 0x0b, 0xf6, 0x7c, 0xba, 0xb0, 0xd5, 0xcc, 0x38, 0x1f, 0x14, 0xd3, 0xd6, 0xfd, 0x79, + 0x2c, 0xe4, 0x48, 0x4e, 0xad, 0xd9, 0x66, 0x86, 0x88, 0x53, 0xfe, 0x07, 0xcd, 0x62, 0xd4, 0xbe, + 0x9f, 0x70, 0xfb, 0xda, 0xb5, 0x8e, 0xd1, 0xb6, 0xe7, 0xf1, 0xf0, 0x79, 0x3b, 0x05, 0xaa, 0xac, + 0xc2, 0xd6, 0x47, 0xc2, 0xf9, 0xe9, 0xb6, 0x5f, 0xe3, 0x61, 0x39, 0x51, 0x60, 0x74, 0x74, 0x8b, + 0x2c, 0x44, 0x2b, 0x19, 0x61, 0x6f, 0x48, 0x57, 0x6b, 0x57, 0xc4, 0xed, 0xc1, 0xb1, 0x71, 0x45, + 0xe4, 0xfe, 0x50, 0xd7, 0xda, 0xde, 0xed, 0xd1, 0x71, 0x10, 0x96, 0xfc, 0x72, 0x75, 0x18, 0x8d, + 0x35, 0x38, 0x63, 0x23, 0x79, 0x69, 0x15, 0x5e, 0xcd, 0x7e, 0x95, 0xde, 0x19, 0xbb, 0x56, 0xcc, + 0x14, 0x87, 0xc9, 0xb3, 0x95, 0x28, 0xc0, 0xc6, 0x40, 0x90, 0x25, 0x6f, 0x8e, 0x0e, 0x25, 0x7c, + 0xd9, 0xf2, 0x5e, 0xba, 0x89, 0xa9, 0xca, 0x70, 0xb9, 0xb7, 0x8e, 0x85, 0x68, 0x23, 0x3c, 0x03, + 0xc3, 0x0d, 0x15, 0x44, 0xaf, 0xdf, 0x26, 0xc1, 0x1a, 0x65, 0xf4, 0xe7, 0x42, 0x14, 0xc4, 0x4d, + 0x52, 0x22, 0x04, 0x88, 0xb0, 0xae, 0x65, 0xcb, 0xbf, 0x0b, 0x0b, 0xde, 0xd3, 0xe1, 0xaa, 0xff, + 0x89, 0x2b, 0xf9, 0xf5, 0xaa, 0x7a, 0x2f, 0x0e, 0x74, 0x1e, 0x87, 0x0a, 0xf0, 0xb2, 0x5c, 0xaf, + 0x84, 0x03, 0xdc, 0xee, 0xf0, 0xe9, 0x8c, 0x9f, 0x61, 0x96, 0x30, 0xef, 0x82, 0x58, 0xee, 0xc2, + 0xfa, 0x5c, 0x8c, 0xe9, 0xbf, 0x50, 0x05, 0xef, 0xd6, 0xf2, 0x9f, 0xf9, 0x36, 0xd2, 0x0d, 0x66, + 0xfe, 0x69, 0x4a, 0x17, 0x56, 0xbe, 0x39, 0x9e, 0x56, 0x8a, 0x2f, 0xb5, 0x21, 0xca, 0x97, 0x56, + 0xe2, 0xdf, 0xab, 0xe4, 0x82, 0x5c, 0x12, 0x81, 0xc5, 0xa7, 0xc2, 0x68, 0x6c, 0xb1, 0x67, 0xfc, + 0xb0, 0x88, 0x3f, 0xc7, 0x4d, 0x23, 0xfe, 0x97, 0xe1, 0x4b, 0x54, 0x7a, 0xa4, 0xc7, 0xe1, 0xf5, + 0xb5, 0x74, 0x9d, 0x12, 0xf3, 0x77, 0xe2, 0x4d, 0x76, 0x1b, 0xde, 0x78, 0x92, 0x1f, 0xf4, 0x2a, + 0xb2, 0x0d, 0x0d, 0x37, 0x5d, 0x19, 0x2e, 0x28, 0xeb, 0xbb, 0xc7, 0x07, 0xb3, 0x8d, 0x4e, 0x69, + 0x60, 0xd0, 0x7f, 0x3d, 0xa5, 0xdc, 0xf0, 0xc3, 0x97, 0x87, 0xb8, 0xea, 0x77, 0x70, 0x6d, 0x58, + 0x6a, 0x86, 0xf0, 0xcd, 0xe5, 0x26, 0xa6, 0x33, 0x10, 0xa5, 0x38, 0x23, 0xbb, 0x97, 0xae, 0xab, + 0xe4, 0x47, 0x36, 0x1a, 0x3b, 0x68, 0xe4, 0x1a, 0xcf, 0xed, 0xde, 0x16, 0x86, 0x44, 0x78, 0x96, + 0x59, 0x5d, 0x4d, 0xdc, 0x9b, 0x1e, 0x2b, 0xac, 0x29, 0x6f, 0xd5, 0x59, 0x85, 0xde, 0xa9, 0xdc, + 0x1b, 0x6e, 0x5d, 0x93, 0xae, 0x05, 0x2c, 0x35, 0xf8, 0x3e, 0x12, 0x2f, 0xa3, 0x23, 0x75, 0xcc, + 0x04, 0x53, 0x87, 0x55, 0x5f, 0xce, 0x95, 0xff, 0x38, 0x39, 0x78, 0x0b, 0xa8, 0x0d, 0xdf, 0x88, + 0x3a, 0x01, 0x29, 0x51, 0xcf, 0x57, 0x6c, 0x73, 0x42, 0x23, 0xa9, 0xf0, 0xc6, 0xe6, 0xc8, 0xb5, + 0x8f, 0xc5, 0x1d, 0xa6, 0x53, 0x7f, 0x91, 0xfb, 0xc3, 0x8d, 0xd6, 0xaf, 0xf7, 0x60, 0x32, 0x3d, + 0x54, 0x09, 0x2e, 0xd1, 0x9d, 0x23, 0xce, 0xd8, 0x07, 0xfa, 0x00, 0xaa, 0xad, 0xef, 0xde, 0x8b, + 0xe9, 0xa0, 0xa1, 0x2f, 0xf6, 0x73, 0x6e, 0x69, 0x2b, 0xb7, 0xb4, 0x11, 0x02, 0x30, 0xc6, 0xa9, + 0xa1, 0xee, 0x67, 0xd9, 0x6a, 0xf0, 0x3b, 0x78, 0x21, 0xbb, 0x0b, 0x4f, 0xc4, 0x2a, 0x2b, 0x3a, + 0x18, 0xe7, 0x13, 0xa2, 0xa9, 0x4d, 0xe4, 0xdb, 0xb9, 0x0f, 0xae, 0x46, 0x7a, 0xbe, 0x8a, 0x64, + 0xfd, 0x22, 0x2f, 0x8f, 0x6d, 0x96, 0x4c, 0x6f, 0x82, 0xfe, 0x24, 0x65, 0x99, 0x33, 0x15, 0xc3, + 0x7f, 0xed, 0x68, 0xf0, 0x4d, 0x88, 0x37, 0xc5, 0x24, 0x35, 0x7d, 0xdb, 0x80, 0x3a, 0xa0, 0x3c, + 0x3f, 0x40, 0xb8, 0x1d, 0x9b, 0xe3, 0x59, 0xbc, 0x98, 0xca, 0xed, 0xb8, 0x06, 0x2e, 0x06, 0x1c, + 0xc8, 0x59, 0xb6, 0x69, 0xec, 0x80, 0x2f, 0xeb, 0x16, 0xe2, 0x08, 0x08, 0x37, 0x86, 0x21, 0xd6, + 0xdd, 0x42, 0xbc, 0x60, 0x10, 0x71, 0x06, 0x20, 0x3e, 0x4f, 0x7a, 0xc1, 0xb7, 0x35, 0x92, 0x53, + 0xec, 0x79, 0x14, 0xd1, 0x30, 0x9c, 0xbd, 0x63, 0x4f, 0x80, 0xfb, 0x2a, 0x66, 0x6c, 0x48, 0xba, + 0xc5, 0x9c, 0x19, 0x98, 0x2b, 0xf7, 0x35, 0x29, 0xc1, 0xce, 0x91, 0xa7, 0x86, 0xef, 0x8a, 0x28, + 0x81, 0x5f, 0xe5, 0xc2, 0xd9, 0x9c, 0x2b, 0x1f, 0x93, 0xaf, 0x73, 0xe4, 0x44, 0x12, 0x31, 0xca, + 0xd2, 0x36, 0x34, 0x0d, 0x49, 0xc2, 0x7b, 0xef, 0x36, 0xe5, 0x7d, 0x0f, 0xf9, 0xfe, 0x98, 0xe0, + 0xd9, 0x7e, 0x12, 0x3a, 0xda, 0xfa, 0xe0, 0x16, 0x1f, 0x08, 0xde, 0xc6, 0xd0, 0x08, 0xd0, 0xd6, + 0x01, 0x51, 0x83, 0x1c, 0xab, 0xb4, 0x92, 0xde, 0xc0, 0x4d, 0x0d, 0xfd, 0x51, 0x58, 0x70, 0xb8, + 0x5d, 0x2a, 0xb5, 0xd0, 0x3b, 0xdb, 0x39, 0x0d, 0x56, 0xe9, 0xc9, 0x35, 0x55, 0xd8, 0x2a, 0xb9, + 0xaa, 0x0f, 0xab, 0xf9, 0xcb, 0x34, 0x82, 0x19, 0xc5, 0x9e, 0xc7, 0xc0, 0xb6, 0xd9, 0x17, 0x25, + 0xe6, 0xc8, 0x90, 0x1f, 0x1d, 0x83, 0x48, 0x4c, 0x7f, 0x14, 0xcc, 0xe7, 0x2d, 0x0f, 0xa4, 0x31, + 0x90, 0x39, 0xd1, 0x57, 0x5c, 0xc0, 0x37, 0x93, 0x3c, 0x0c, 0x15, 0xd9, 0x63, 0x2c, 0x04, 0xc2, + 0x32, 0xe9, 0x93, 0xde, 0x42, 0xf2, 0x64, 0x3b, 0xd2, 0x6d, 0x6c, 0x87, 0x15, 0x22, 0xcf, 0xbf, + 0xe8, 0xad, 0x55, 0x78, 0x8e, 0xf2, 0xba, 0x1b, 0xf4, 0x8f, 0x13, 0xac, 0x8a, 0x71, 0xb4, 0x06, + 0xf8, 0xf6, 0x5c, 0x89, 0xde, 0xe1, 0xc7, 0x50, 0xb2, 0x58, 0x6d, 0x96, 0x2b, 0xe9, 0x56, 0x5d, + 0x0c, 0xb7, 0x2b, 0x2d, 0x32, 0xc9, 0x2c, 0x13, 0x6d, 0xd0, 0x5c, 0x94, 0x32, 0x0d, 0x37, 0x33, + 0xef, 0x56, 0xb6, 0x6a, 0xf3, 0x3a, 0xa4, 0x6c, 0x35, 0xc2, 0x49, 0x1f, 0xa0, 0x09, 0xf0, 0x5e, + 0x69, 0x3b, 0x22, 0xca, 0xd6, 0x5f, 0xcc, 0x06, 0xe7, 0xb6, 0x50, 0xc1, 0xf3, 0xef, 0x22, 0xa6, + 0x5c, 0x2a, 0x0f, 0x1a, 0x33, 0x91, 0x7a, 0x05, 0x71, 0xae, 0x7c, 0x16, 0x1d, 0xeb, 0x79, 0x0c, + 0xa3, 0x2e, 0x3a, 0xef, 0x02, 0x97, 0x08, 0x6b, 0x75, 0x07, 0x5b, 0xa7, 0x77, 0x8b, 0x15, 0x5e, + 0x8c, 0x0a, 0xde, 0xc6, 0xa3, 0x61, 0x63, 0x1b, 0x8b, 0xaf, 0x35, 0xb8, 0x3e, 0xa4, 0x7b, 0x2d, + 0x5d, 0xca, 0x24, 0x40, 0x03, 0xd2, 0x0f, 0xde, 0x63, 0xbe, 0xfc, 0x12, 0xdd, 0xfd, 0x03, 0xe7, + 0x41, 0x19, 0x1a, 0xe2, 0x40, 0x26, 0x2a, 0x2d, 0x49, 0x7f, 0x80, 0x3f, 0x93, 0x2d, 0x6f, 0x67, + 0x42, 0x4e, 0xe4, 0x5f, 0xbe, 0xf9, 0x4b, 0x98, 0x24, 0x68, 0x30, 0x4d, 0x74, 0xe1, 0xe9, 0x21, + 0x5d, 0x99, 0xf9, 0x76, 0xf9, 0x51, 0x8e, 0xb6, 0xa0, 0x5b, 0x21, 0x30, 0x38, 0x84, 0x9c, 0xbe, + 0x09, 0xb6, 0x7f, 0xa0, 0x13, 0x98, 0x71, 0x86, 0x3a, 0x01, 0xfa, 0x52, 0x18, 0x55, 0x2e, 0x58, + 0x38, 0x71, 0x27, 0xc8, 0xb5, 0x9d, 0xbe, 0xd1, 0x11, 0xc6, 0x55, 0x8f, 0xa4, 0x0f, 0x6a, 0x7e, + 0x66, 0x30, 0x1d, 0x1d, 0x8e, 0xd9, 0xaf, 0x6c, 0x4a, 0x42, 0xbc, 0x1b, 0xcf, 0x56, 0x1d, 0x79, + 0x37, 0x74, 0xa8, 0xe2, 0xbb, 0x6a, 0x63, 0xdb, 0xe8, 0xee, 0xe9, 0xa4, 0x8b, 0x83, 0xde, 0x23, + 0x5a, 0xd8, 0xa2, 0x7a, 0x98, 0x7b, 0x19, 0x5f, 0x4c, 0x20, 0x27, 0xc1, 0x3d, 0x4b, 0xc9, 0x33, + 0x40, 0x01, 0x1b, 0x92, 0xd7, 0x32, 0x7b, 0xd0, 0x09, 0x99, 0x41, 0x35, 0x64, 0x79, 0x4c, 0x70, + 0x89, 0xa1, 0x6f, 0xd3, 0x40, 0x5c, 0x76, 0x2e, 0x08, 0xa8, 0xc7, 0xed, 0x07, 0x1e, 0x26, 0x54, + 0xa2, 0x8b, 0x90, 0x26, 0x1b, 0x9c, 0x84, 0x45, 0x9e, 0x7c, 0x9a, 0x46, 0x85, 0x10, 0x1c, 0x64, + 0xcb, 0xeb, 0x14, 0x5a, 0x24, 0x53, 0x3b, 0x2c, 0xe4, 0x16, 0x39, 0xfc, 0x07, 0x65, 0xf3, 0xb5, + 0x5e, 0x41, 0x6e, 0x91, 0x53, 0x95, 0xfa, 0xea, 0x8b, 0xce, 0x7b, 0x49, 0x1f, 0x34, 0x41, 0x0f, + 0xdd, 0x2e, 0x4f, 0xbd, 0xa1, 0xac, 0x45, 0x9c, 0xed, 0xa1, 0x2b, 0x20, 0x90, 0xbb, 0x5b, 0x95, + 0x1b, 0x10, 0x17, 0x31, 0x22, 0x8a, 0x1e, 0x76, 0x87, 0x6e, 0xa6, 0xbe, 0xbb, 0x7b, 0xe9, 0x34, + 0x98, 0xae, 0xb3, 0x2c, 0x35, 0x9f, 0x63, 0x62, 0xd5, 0x2a, 0xaa, 0x90, 0xb2, 0xf8, 0x77, 0xba, + 0xe0, 0x2c, 0x8d, 0x43, 0xb1, 0xc8, 0xd3, 0xc7, 0x84, 0x96, 0x14, 0xe1, 0xc5, 0x38, 0x86, 0xab, + 0xfe, 0x80, 0xc6, 0xd0, 0x32, 0xf7, 0x41, 0xc3, 0x64, 0xc2, 0x77, 0x49, 0x69, 0x01, 0x3c, 0x9d, + 0xce, 0x6b, 0x87, 0x21, 0x40, 0x15, 0x78, 0xbf, 0x70, 0x63, 0xd4, 0x26, 0x8d, 0x98, 0xfa, 0xbb, + 0x19, 0x44, 0xf5, 0x1f, 0x9d, 0x25, 0x68, 0x8b, 0x8f, 0x7d, 0x4f, 0x9a, 0x44, 0xbe, 0x2b, 0x01, + 0x26, 0xaa, 0xc3, 0xf1, 0x88, 0x98, 0xbe, 0x4a, 0xcc, 0x6b, 0x17, 0x3f, 0xc6, 0x21, 0xb8, 0x1d, + 0xfe, 0x9e, 0xf7, 0x71, 0x50, 0xcf, 0xe5, 0x30, 0xd6, 0x83, 0xa1, 0x86, 0x1f, 0x02, 0x1e, 0x49, + 0xf5, 0x12, 0xf8, 0x0f, 0x15, 0x6e, 0x11, 0xce, 0x95, 0xfb, 0x8f, 0x28, 0xef, 0xa7, 0xe5, 0x75, + 0x74, 0x4e, 0xc1, 0x77, 0x8e, 0x30, 0x55, 0xf4, 0x18, 0xfc, 0x8e, 0x07, 0xc5, 0xf4, 0xd5, 0x78, + 0x30, 0xb6, 0x3b, 0x84, 0xe7, 0x4f, 0xc3, 0xf0, 0x74, 0x29, 0x18, 0x24, 0xe3, 0x19, 0x48, 0x99, + 0x7c, 0xcf, 0xe3, 0x88, 0xe1, 0x90, 0x5e, 0x89, 0xe9, 0x4f, 0x89, 0xfb, 0x95, 0x1e, 0xbd, 0xca, + 0xc8, 0x90, 0x19, 0xc2, 0x0a, 0xdb, 0x84, 0x9b, 0x07, 0x5d, 0x60, 0x9b, 0xc0, 0x71, 0xb8, 0xb1, + 0x65, 0xec, 0x80, 0xfe, 0x68, 0xe6, 0x5e, 0x7c, 0x49, 0x30, 0x13, 0xd2, 0x65, 0x63, 0x13, 0xe8, + 0x06, 0xb8, 0xb9, 0x2e, 0x79, 0x43, 0x90, 0x18, 0x63, 0x93, 0x28, 0x2a, 0x58, 0x9a, 0x14, 0x2c, + 0x62, 0x85, 0xec, 0xf9, 0x3a, 0x8c, 0x6d, 0x1c, 0xdb, 0x8c, 0xaf, 0x99, 0xd1, 0xb7, 0x0b, 0xb3, + 0x72, 0xe9, 0xe0, 0x73, 0xe5, 0x85, 0x4a, 0x27, 0xc8, 0x2e, 0x73, 0x44, 0x7c, 0xef, 0x07, 0xf8, + 0x7b, 0x1b, 0x48, 0x81, 0x30, 0x16, 0x82, 0x67, 0xa3, 0x77, 0xd1, 0xaa, 0x38, 0xc7, 0x4a, 0xd1, + 0x78, 0x06, 0x98, 0xc1, 0xf8, 0xee, 0xe7, 0x21, 0xbc, 0x94, 0x1f, 0xd6, 0x3f, 0xb6, 0x19, 0x5c, + 0x2c, 0x7b, 0x95, 0xb2, 0x03, 0xfe, 0x70, 0x81, 0xdc, 0x59, 0x8f, 0x37, 0x83, 0x65, 0x74, 0x89, + 0x0d, 0xaa, 0x94, 0x65, 0xfc, 0x01, 0xc0, 0x18, 0x9e, 0x77, 0x5c, 0xa4, 0xc2, 0x4f, 0x59, 0x94, + 0x1f, 0xc7, 0x6d, 0xdb, 0x82, 0x47, 0x9d, 0xe1, 0x50, 0xc4, 0x3b, 0x18, 0x33, 0x07, 0x5f, 0xa8, + 0xc0, 0xdb, 0x5f, 0x5a, 0x7a, 0xb5, 0x44, 0x2b, 0xed, 0xc4, 0x85, 0x3d, 0xb0, 0x13, 0x97, 0x5d, + 0x5c, 0xa0, 0x82, 0x81, 0xb1, 0x3c, 0x95, 0x06, 0x13, 0x31, 0x6b, 0x86, 0xc5, 0xc4, 0x74, 0x71, + 0x06, 0x1f, 0x38, 0x5e, 0x7c, 0xf5, 0x7b, 0x96, 0x0a, 0x39, 0xd3, 0x2c, 0xff, 0xd7, 0xa8, 0xa1, + 0xeb, 0xeb, 0xee, 0x51, 0x43, 0x22, 0x64, 0x70, 0xcf, 0x18, 0x48, 0xe1, 0xe1, 0xda, 0x5f, 0xe9, + 0x71, 0x0e, 0x96, 0xcc, 0x34, 0xac, 0xb2, 0x60, 0xdc, 0x7d, 0x5f, 0x7d, 0x28, 0xb0, 0x5a, 0xa8, + 0x04, 0x56, 0xf7, 0x41, 0xa8, 0x26, 0xbe, 0x15, 0x8d, 0xde, 0xe2, 0x93, 0x18, 0xf4, 0x04, 0xcf, + 0xaa, 0xc8, 0x58, 0xf1, 0xad, 0x38, 0x0a, 0xd0, 0x51, 0x80, 0x86, 0x64, 0x82, 0xe7, 0x70, 0x2b, + 0x6f, 0x3a, 0xbe, 0xe9, 0xae, 0x3a, 0x82, 0x38, 0xd7, 0x04, 0xdf, 0x59, 0x09, 0x8e, 0xd7, 0x2d, + 0x1d, 0xfc, 0x70, 0x00, 0x8f, 0x3a, 0xb0, 0x64, 0xa1, 0xe3, 0xd1, 0x37, 0x25, 0x77, 0xd5, 0x05, + 0xc7, 0x4b, 0x0f, 0x8e, 0x37, 0x47, 0x38, 0xf8, 0x3d, 0x6e, 0x77, 0x38, 0x13, 0xa4, 0x83, 0x88, + 0x3e, 0x57, 0x96, 0xce, 0x87, 0xce, 0x0c, 0xef, 0x02, 0xb7, 0x3a, 0x67, 0x54, 0xd0, 0x6a, 0x73, + 0xcd, 0xf2, 0x6c, 0x6f, 0xf0, 0xf0, 0x2d, 0x38, 0x64, 0xfe, 0xa0, 0x05, 0x25, 0x91, 0x9c, 0x23, + 0xc4, 0xd4, 0x24, 0x56, 0xe0, 0x9b, 0x2d, 0x90, 0x04, 0xf4, 0x72, 0xd5, 0x0f, 0xd2, 0x48, 0x0d, + 0x34, 0x24, 0x5a, 0x70, 0x83, 0x37, 0x38, 0xae, 0xc4, 0xff, 0xa0, 0x60, 0xe1, 0xc6, 0x23, 0x60, + 0xff, 0xa4, 0x65, 0x3c, 0xc4, 0x2d, 0xc6, 0x76, 0x71, 0x3f, 0xd5, 0xbd, 0x0a, 0xbf, 0xf8, 0x1b, + 0x9c, 0x6f, 0x30, 0x8c, 0xec, 0x5c, 0x4c, 0x09, 0xc8, 0x49, 0x79, 0xeb, 0x5d, 0x4a, 0x9c, 0x50, + 0x8f, 0x91, 0x42, 0xc5, 0x81, 0x84, 0x01, 0xd1, 0xa8, 0x9d, 0x04, 0x2a, 0x0a, 0xf9, 0xcf, 0x25, + 0x6e, 0x47, 0x45, 0x07, 0x81, 0x00, 0xdf, 0x0f, 0x0b, 0xa8, 0xd1, 0xcb, 0xf6, 0x8b, 0x15, 0x6d, + 0xca, 0xfb, 0xd4, 0xc6, 0xae, 0x07, 0x30, 0xf0, 0x6f, 0x8f, 0x45, 0x47, 0x00, 0xe3, 0x24, 0xd4, + 0x89, 0xd0, 0x12, 0xca, 0x27, 0x0d, 0x67, 0x49, 0x0b, 0x24, 0x09, 0xa0, 0x31, 0x86, 0x3e, 0x6e, + 0xe9, 0x59, 0x2e, 0xb5, 0x85, 0xe4, 0xb8, 0xb9, 0xa5, 0x7d, 0xa4, 0xe2, 0x0c, 0x10, 0xd9, 0x9d, + 0x34, 0x4d, 0xcd, 0x8f, 0x15, 0x8d, 0x71, 0x62, 0xdc, 0xdb, 0xd0, 0x03, 0xf4, 0xb3, 0x53, 0x47, + 0xcd, 0x3b, 0x06, 0x4f, 0xeb, 0xf7, 0x88, 0x15, 0xfe, 0x5c, 0x79, 0x7a, 0x54, 0x48, 0x18, 0x01, + 0xfe, 0xb8, 0x59, 0xfe, 0x4c, 0x1b, 0x3c, 0xe0, 0x8a, 0x5c, 0x12, 0x75, 0x1d, 0xcf, 0xa3, 0x97, + 0xa1, 0xfb, 0xe8, 0x11, 0x1a, 0x35, 0x74, 0xef, 0xf0, 0x9f, 0x84, 0xf7, 0x42, 0xcc, 0x1b, 0x09, + 0x71, 0x7b, 0x5a, 0x98, 0xb3, 0x18, 0x93, 0xee, 0xa9, 0xf4, 0x5d, 0x41, 0xd2, 0x6f, 0x98, 0xb8, + 0x29, 0x2a, 0x59, 0xa8, 0xd3, 0x18, 0x9a, 0xf8, 0xef, 0x89, 0x07, 0xf7, 0xe7, 0xf1, 0xf2, 0x81, + 0xa4, 0x59, 0x26, 0xa6, 0xfe, 0xbc, 0x17, 0x22, 0x52, 0xbe, 0x83, 0xdb, 0x31, 0x00, 0x5e, 0xbb, + 0xe7, 0x0a, 0x72, 0x26, 0x73, 0xbb, 0x97, 0xff, 0xfc, 0x26, 0xb7, 0xe3, 0x18, 0x7b, 0xca, 0x22, + 0xf2, 0xde, 0x6c, 0x31, 0xef, 0x9f, 0x99, 0xa8, 0xa9, 0x7b, 0xe4, 0xaf, 0xdd, 0xa1, 0x98, 0x9f, + 0x52, 0xe0, 0x7c, 0xad, 0xb3, 0x81, 0xa1, 0x2f, 0xa0, 0x57, 0x37, 0x33, 0xca, 0x48, 0x8b, 0x26, + 0x82, 0x64, 0x84, 0x4b, 0x8c, 0x3d, 0x34, 0xde, 0xb7, 0x38, 0x1e, 0xe2, 0xac, 0xfe, 0x13, 0x34, + 0x59, 0x26, 0xa9, 0x96, 0x89, 0x9a, 0x97, 0x13, 0x8e, 0x41, 0x9e, 0x23, 0x5c, 0x00, 0x4a, 0x97, + 0xbf, 0xac, 0x32, 0x1c, 0xc3, 0x27, 0xe1, 0xe7, 0x50, 0x1d, 0xbe, 0xfa, 0x65, 0xc8, 0x8a, 0x62, + 0x02, 0xbb, 0x57, 0x51, 0xd5, 0x6e, 0x53, 0x2b, 0x6b, 0xc2, 0xcb, 0xd4, 0xd0, 0x0f, 0x2b, 0xf6, + 0x79, 0x03, 0x88, 0x3c, 0x17, 0x6e, 0xec, 0x1a, 0x0f, 0x2e, 0x08, 0x34, 0x80, 0x3f, 0xc0, 0x5e, + 0x85, 0xc5, 0x4b, 0xcc, 0x73, 0xc3, 0x3c, 0x42, 0xd8, 0x13, 0x03, 0x61, 0x4f, 0x92, 0x1b, 0x5f, + 0xef, 0xa4, 0x49, 0x94, 0x98, 0xd3, 0x32, 0x3e, 0xe7, 0x0c, 0x28, 0x09, 0x26, 0x52, 0xa0, 0x2e, + 0xb1, 0xbd, 0xa0, 0x23, 0xdc, 0xb6, 0x2b, 0xd4, 0xa5, 0x27, 0x29, 0x67, 0x75, 0xdf, 0x7d, 0x46, + 0x8f, 0xb7, 0xf7, 0x2b, 0x47, 0x76, 0x1f, 0xc3, 0x42, 0x66, 0xeb, 0x68, 0x07, 0xd0, 0x35, 0xdb, + 0xdf, 0xf1, 0xe0, 0xdb, 0x17, 0xcc, 0x5f, 0x2a, 0x97, 0xbc, 0x7d, 0x81, 0x5e, 0x03, 0xf8, 0x17, + 0x5d, 0xe2, 0xd4, 0x55, 0xf5, 0xf4, 0x05, 0x8d, 0x2b, 0x78, 0x04, 0xf5, 0xde, 0x07, 0x50, 0x25, + 0x3e, 0xf4, 0xee, 0x05, 0x0c, 0x1e, 0x20, 0x34, 0xa9, 0x13, 0x4e, 0x33, 0x87, 0x19, 0x58, 0x2e, + 0x0c, 0x27, 0x69, 0xf8, 0x18, 0x7b, 0xca, 0xfe, 0x34, 0x5e, 0x8d, 0xc9, 0x2c, 0x66, 0x69, 0x98, + 0x9c, 0x99, 0x6d, 0xb6, 0xd8, 0x32, 0xee, 0x0d, 0x67, 0x6c, 0xcb, 0xe1, 0x8f, 0x3c, 0x9b, 0x4e, + 0xf4, 0x4c, 0x05, 0x41, 0xb5, 0x9b, 0x5c, 0xe2, 0x93, 0x28, 0xc6, 0xaa, 0x06, 0x6c, 0x2c, 0xb4, + 0x20, 0xd2, 0x3e, 0x8a, 0x94, 0xdc, 0x18, 0x8e, 0x96, 0xd0, 0x2e, 0xef, 0x63, 0x63, 0xc7, 0x7f, + 0xd8, 0x7e, 0x7f, 0x89, 0xb2, 0xe0, 0x6f, 0xc5, 0xad, 0xd2, 0xdc, 0x6c, 0x8b, 0x7c, 0x98, 0xae, + 0x20, 0x1a, 0x76, 0x09, 0xb6, 0x12, 0x26, 0x60, 0x33, 0x66, 0x2f, 0x0e, 0xb9, 0x0f, 0x07, 0xcf, + 0x96, 0x4d, 0x8c, 0xa2, 0x61, 0xca, 0xb9, 0x97, 0xed, 0x0a, 0xde, 0x86, 0x3c, 0x44, 0x5f, 0xed, + 0xd2, 0xd5, 0x9a, 0xe2, 0xb9, 0x5d, 0x39, 0x33, 0xb8, 0x5d, 0x47, 0xb9, 0x5d, 0x6e, 0x7a, 0x68, + 0x34, 0x2c, 0x85, 0x07, 0xeb, 0x1e, 0xc2, 0x40, 0x29, 0x7d, 0x9f, 0x68, 0xe6, 0xa0, 0x81, 0xae, + 0x22, 0x39, 0x4d, 0x62, 0x98, 0xfe, 0xf4, 0xa3, 0x60, 0x82, 0x18, 0xd4, 0x77, 0x70, 0xd5, 0x89, + 0x98, 0x3e, 0x65, 0xaa, 0xc1, 0x72, 0xc1, 0x30, 0x8d, 0x7e, 0x49, 0x95, 0x25, 0xbc, 0xe0, 0xd1, + 0xb2, 0xca, 0x5b, 0xc7, 0xd2, 0x3c, 0x48, 0x1a, 0x8f, 0x90, 0x9c, 0xe3, 0xb8, 0xc3, 0xc0, 0xbb, + 0x85, 0x6f, 0x58, 0x6e, 0xdb, 0x01, 0x7a, 0xad, 0xec, 0x00, 0xc9, 0xd9, 0x23, 0xde, 0x85, 0x2f, + 0xaa, 0xc2, 0xf2, 0xb8, 0x55, 0xcd, 0x9e, 0x23, 0x90, 0xa7, 0xaf, 0xd1, 0x8c, 0xad, 0x17, 0x93, + 0x7b, 0x45, 0x3d, 0x5e, 0xba, 0xf9, 0xa7, 0xf8, 0xec, 0x4d, 0x71, 0x8b, 0x6a, 0x6c, 0x0f, 0xc9, + 0xf1, 0x1a, 0x5a, 0xc8, 0xb5, 0x49, 0x39, 0x32, 0xf7, 0x46, 0x03, 0xd8, 0xe2, 0x22, 0x58, 0x76, + 0x8c, 0x60, 0x16, 0xed, 0x10, 0xa7, 0xc4, 0x7e, 0x0e, 0x71, 0x23, 0xf7, 0x7a, 0x9d, 0xee, 0x5a, + 0x65, 0x3f, 0xcb, 0xdf, 0xbd, 0xc8, 0xe8, 0x25, 0x03, 0xb1, 0x9f, 0xdb, 0xc7, 0x40, 0x1b, 0x32, + 0x30, 0x89, 0x3f, 0xc3, 0x6d, 0x0b, 0x00, 0x7d, 0x41, 0x30, 0x74, 0xaf, 0x3e, 0x0d, 0x09, 0x0a, + 0xde, 0xfc, 0x6a, 0x17, 0xf9, 0x36, 0x58, 0x6d, 0x17, 0x81, 0x0b, 0x81, 0x76, 0xed, 0x80, 0xa2, + 0xfa, 0x2c, 0x1f, 0x89, 0xd9, 0x7e, 0x85, 0x2c, 0xf2, 0x6e, 0xb2, 0x55, 0x45, 0x22, 0x21, 0x94, + 0xc6, 0xdf, 0x31, 0x68, 0x8f, 0xed, 0x25, 0x16, 0x35, 0x49, 0x03, 0x67, 0xbf, 0x47, 0x34, 0x1e, + 0x10, 0x3c, 0x31, 0x19, 0x9c, 0x4b, 0xaf, 0xbc, 0x5d, 0x2b, 0xae, 0xd0, 0x90, 0xba, 0x91, 0xcd, + 0xce, 0x71, 0xf4, 0x9a, 0xb4, 0x6d, 0x09, 0x4e, 0x2c, 0x69, 0x06, 0xcd, 0x77, 0x4c, 0x05, 0x96, + 0x33, 0xa8, 0x40, 0x92, 0x20, 0x6a, 0x22, 0x3d, 0xd2, 0xbc, 0xba, 0xbd, 0x90, 0x7d, 0x33, 0xd7, + 0xff, 0x40, 0x4e, 0x89, 0x39, 0xc7, 0x1f, 0xf5, 0x25, 0x8b, 0x8f, 0xa0, 0x81, 0x5f, 0x23, 0x03, + 0x55, 0x0d, 0xa8, 0x72, 0xe2, 0xcf, 0x54, 0x24, 0xaf, 0x4b, 0x68, 0x05, 0x0d, 0x03, 0xd5, 0x9b, + 0x3a, 0x40, 0xbf, 0xc2, 0x8d, 0xf2, 0x78, 0x53, 0x17, 0x99, 0x03, 0x6b, 0xfd, 0xcf, 0x54, 0xb1, + 0xe7, 0xc9, 0xa3, 0x9a, 0xa9, 0xbd, 0xf8, 0x76, 0xa9, 0xa9, 0x03, 0x70, 0x2c, 0x85, 0x21, 0x0c, + 0x75, 0xdc, 0x36, 0x17, 0x6e, 0xeb, 0x81, 0xb5, 0xfe, 0x81, 0xbe, 0x41, 0x2a, 0x69, 0x70, 0x40, + 0xe1, 0x85, 0x66, 0x2d, 0xcb, 0x3b, 0xa4, 0xc5, 0xcd, 0xe2, 0xdd, 0x62, 0xbe, 0xea, 0x10, 0x8e, + 0x60, 0x38, 0x4b, 0x2f, 0xed, 0x92, 0x3c, 0x19, 0xc4, 0xb6, 0x49, 0x2d, 0xe6, 0x6b, 0xec, 0xf3, + 0xd9, 0xb3, 0xe3, 0xf3, 0xe4, 0xaa, 0x01, 0x56, 0x4b, 0x2f, 0x3d, 0x5e, 0x60, 0xec, 0x33, 0xa0, + 0xb3, 0xb4, 0xbc, 0x59, 0x0d, 0x4a, 0x8a, 0x87, 0xce, 0xad, 0x01, 0xa2, 0x27, 0x4f, 0xa8, 0x1a, + 0xd3, 0x71, 0x0d, 0x66, 0x1a, 0x47, 0x20, 0x83, 0xbe, 0xb8, 0x91, 0x9f, 0xf3, 0x31, 0xe0, 0x35, + 0x97, 0xe1, 0xf6, 0x3b, 0xb8, 0x1b, 0xe8, 0x31, 0xaf, 0x39, 0xc8, 0x5d, 0x46, 0xd2, 0x22, 0xa3, + 0x9f, 0xdb, 0xb6, 0x51, 0x79, 0x97, 0x06, 0x32, 0x3b, 0xfc, 0x69, 0x0b, 0x53, 0x07, 0x3f, 0x9a, + 0x66, 0x0b, 0xfe, 0xce, 0x7c, 0xba, 0x90, 0xe1, 0xde, 0xb9, 0xe9, 0x0c, 0x7c, 0xd7, 0x81, 0x7c, + 0x24, 0x95, 0x07, 0x43, 0x09, 0x64, 0x06, 0x77, 0x81, 0x72, 0xba, 0x1c, 0x26, 0x7c, 0xc7, 0x5a, + 0x2b, 0x96, 0xab, 0x44, 0xf4, 0xaa, 0x78, 0x22, 0x61, 0xec, 0x12, 0x39, 0xf0, 0xbf, 0x62, 0x3a, + 0x2a, 0x8c, 0xe1, 0x14, 0xfa, 0x60, 0x0f, 0xee, 0xe1, 0x5c, 0xc5, 0xcd, 0x9d, 0xa5, 0xc7, 0x70, + 0xdf, 0x0c, 0x3c, 0xf2, 0x55, 0x6e, 0x69, 0x4e, 0x3b, 0xb8, 0x64, 0x6e, 0xe9, 0xd5, 0x14, 0xa1, + 0x31, 0x06, 0x51, 0x7a, 0xf9, 0x06, 0xf0, 0xe6, 0x38, 0x0b, 0x10, 0x28, 0x44, 0xd7, 0x53, 0x2a, + 0x25, 0xe3, 0x6f, 0x21, 0x5f, 0x3a, 0x7b, 0xfd, 0x0f, 0xb4, 0x44, 0x20, 0x0f, 0x6b, 0x1b, 0x52, + 0x22, 0x0d, 0x30, 0x47, 0xe7, 0x61, 0xd8, 0x24, 0xc1, 0x78, 0x26, 0x66, 0x11, 0xe4, 0x87, 0xae, + 0x1c, 0x3c, 0xd1, 0x4b, 0x4e, 0xc4, 0xdc, 0x22, 0x28, 0x5f, 0xd1, 0xd9, 0xac, 0xad, 0x1a, 0x40, + 0x69, 0x38, 0x23, 0xf0, 0x36, 0xf2, 0x66, 0xc9, 0xcc, 0x4a, 0xd1, 0x59, 0xe0, 0xf3, 0x0d, 0xce, + 0x73, 0x5a, 0xbe, 0x48, 0x8a, 0x3b, 0x29, 0x3a, 0xd4, 0x98, 0xb5, 0x3a, 0x34, 0xa0, 0x67, 0xe2, + 0xdc, 0x70, 0x78, 0x7c, 0x52, 0x35, 0x3e, 0xaf, 0x6b, 0xee, 0xea, 0x73, 0xaa, 0xb9, 0xf3, 0xce, + 0x81, 0x08, 0x53, 0x21, 0xbb, 0x0b, 0x77, 0x9e, 0x53, 0x8b, 0x79, 0x5d, 0xe3, 0x57, 0x9f, 0xd3, + 0x48, 0x2b, 0x4f, 0xaa, 0x61, 0x96, 0xc9, 0x1c, 0xc9, 0x1c, 0x06, 0x5e, 0xdb, 0x70, 0xc1, 0x71, + 0x4f, 0x42, 0xbd, 0x50, 0x3f, 0x20, 0xe6, 0xb4, 0x4b, 0xd1, 0xcd, 0xd2, 0x73, 0xb2, 0x42, 0x1f, + 0x10, 0x2a, 0xd4, 0xc5, 0x3c, 0xc6, 0x7f, 0x9b, 0x21, 0x9a, 0x5a, 0x20, 0x2a, 0x31, 0xf9, 0x93, + 0x08, 0x9d, 0x8a, 0x16, 0x43, 0x13, 0xb7, 0x6d, 0x4d, 0xe8, 0x16, 0xa2, 0x9f, 0xa8, 0x85, 0x23, + 0xab, 0x14, 0x07, 0xb0, 0x55, 0x39, 0xc6, 0xdb, 0x71, 0x72, 0x48, 0xac, 0x4d, 0x6f, 0xf7, 0x70, + 0xd5, 0xad, 0xe8, 0xf5, 0x2a, 0xd4, 0x42, 0xa3, 0x56, 0xcc, 0xf7, 0x8b, 0xb9, 0xdf, 0x8b, 0x8f, + 0xdf, 0x14, 0xd3, 0x7b, 0x41, 0xdc, 0xac, 0x17, 0x57, 0x48, 0x40, 0xd3, 0x34, 0xb6, 0x45, 0xe4, + 0x94, 0xf7, 0xa6, 0x20, 0x2a, 0x25, 0x39, 0x1d, 0x2f, 0x4e, 0x92, 0x32, 0x03, 0x42, 0xbf, 0x6a, + 0x53, 0xc9, 0xfe, 0x67, 0xc0, 0xa1, 0x06, 0xe2, 0xdf, 0x52, 0x43, 0x3a, 0xbf, 0x7f, 0x15, 0x7a, + 0xd5, 0x35, 0x55, 0x7d, 0xa8, 0x4a, 0x2f, 0xde, 0x5d, 0xd5, 0x47, 0x65, 0xb3, 0x48, 0x29, 0xf3, + 0xc9, 0xfb, 0x33, 0xf0, 0xd2, 0xd1, 0x4f, 0xab, 0xfa, 0xc2, 0x10, 0xfc, 0x00, 0x54, 0xeb, 0x00, + 0x7c, 0xff, 0xfe, 0xf9, 0xe8, 0xa5, 0x67, 0xec, 0x5f, 0x86, 0x2e, 0x6a, 0xea, 0xfe, 0xef, 0x10, + 0xc7, 0xc4, 0xfd, 0x0f, 0x20, 0x70, 0xfc, 0xfe, 0x9f, 0x21, 0x50, 0xb3, 0xbf, 0x97, 0xde, 0x53, + 0xda, 0xaf, 0xba, 0x82, 0x0e, 0x36, 0xc0, 0xfb, 0x71, 0x3b, 0x96, 0x5e, 0xa3, 0xe9, 0x80, 0xf8, + 0xdc, 0xb6, 0x1a, 0xf7, 0x81, 0xaa, 0x1a, 0xd1, 0xf1, 0xc7, 0xc3, 0x94, 0xaf, 0xe0, 0x5c, 0x73, + 0xe9, 0x9e, 0x5a, 0xf4, 0x60, 0x98, 0xc6, 0xab, 0x07, 0x9d, 0x23, 0xa4, 0x1c, 0xb3, 0x69, 0x44, + 0xaf, 0x7a, 0x0f, 0x8b, 0x24, 0xb2, 0x21, 0x02, 0xfd, 0x3f, 0xbb, 0x07, 0x5b, 0x0c, 0xd9, 0x8f, + 0x8c, 0x16, 0x96, 0x98, 0xb3, 0xc3, 0xf1, 0x30, 0xfe, 0x8f, 0x74, 0xb5, 0xdd, 0xb3, 0x12, 0x4a, + 0xe2, 0x3d, 0xb4, 0x8f, 0xa9, 0xbd, 0xea, 0x06, 0x9e, 0x46, 0x72, 0xd5, 0x7e, 0xba, 0xde, 0x7e, + 0x46, 0x7f, 0x12, 0xea, 0xcd, 0x65, 0x88, 0x62, 0x88, 0x0e, 0x89, 0xcf, 0xa9, 0xc4, 0x28, 0xd1, + 0xae, 0x66, 0x4f, 0xa0, 0x02, 0xd8, 0x35, 0x63, 0x07, 0x08, 0x44, 0xf3, 0xcf, 0xa1, 0x6f, 0xc4, + 0x8d, 0xe6, 0x7d, 0x63, 0xc0, 0xa4, 0xac, 0xe2, 0x38, 0xee, 0xf0, 0x69, 0x6e, 0xf7, 0x05, 0xcf, + 0x15, 0x55, 0x55, 0x3f, 0xf3, 0x6e, 0x20, 0xb0, 0x0f, 0x7f, 0x34, 0x87, 0x33, 0x5d, 0x30, 0x9c, + 0x87, 0xf5, 0x16, 0x5f, 0x5a, 0xda, 0xac, 0x8c, 0x1a, 0x1c, 0x46, 0x3c, 0xb0, 0xcc, 0x0c, 0x25, + 0xf1, 0x9f, 0x74, 0xb4, 0x7d, 0x90, 0xe1, 0x32, 0xa0, 0x64, 0x06, 0x8c, 0xd0, 0xf7, 0x29, 0x49, + 0x78, 0x47, 0x6c, 0xbf, 0x3d, 0x15, 0x2c, 0x1a, 0xe4, 0x0f, 0xb1, 0x0b, 0x2e, 0xc2, 0xdb, 0x5a, + 0x90, 0xbe, 0x57, 0x68, 0x0f, 0x54, 0xb9, 0x67, 0xd5, 0xa0, 0xc0, 0xe3, 0x33, 0x35, 0x60, 0xe2, + 0x60, 0xe3, 0xc2, 0x69, 0x88, 0x06, 0xd5, 0x78, 0xa1, 0x31, 0x53, 0xd3, 0x98, 0xac, 0x98, 0x39, + 0x8b, 0xab, 0xd1, 0x5e, 0x85, 0xc0, 0x91, 0xdc, 0xe1, 0x53, 0xdc, 0xee, 0x66, 0xcf, 0xe5, 0xe1, + 0x04, 0x36, 0xef, 0xa3, 0x2b, 0xd6, 0x35, 0x3a, 0x2a, 0xc4, 0x35, 0x30, 0x6a, 0xbe, 0xe1, 0xaa, + 0xb2, 0x7e, 0xf5, 0x39, 0x56, 0xfe, 0x1f, 0x9d, 0x83, 0x3c, 0x45, 0x07, 0x79, 0xca, 0x5a, 0x09, + 0xa5, 0x84, 0x1e, 0xf1, 0x9f, 0x59, 0xab, 0xe0, 0x61, 0x12, 0xef, 0x67, 0x4f, 0x12, 0x05, 0x88, + 0x6f, 0xe2, 0x2b, 0x50, 0x5f, 0xa4, 0xd2, 0x29, 0x85, 0xd0, 0x2f, 0x08, 0xce, 0x92, 0x05, 0x77, + 0xcc, 0xa2, 0xd1, 0x38, 0x4d, 0xdc, 0xb6, 0xf5, 0xf4, 0x17, 0x05, 0x06, 0xe7, 0xd9, 0x22, 0x67, + 0x28, 0x2f, 0xf2, 0xd0, 0xec, 0x6e, 0x2f, 0xfa, 0xb2, 0x7d, 0xbf, 0x7a, 0x21, 0x9c, 0x99, 0xda, + 0xcc, 0x36, 0x5b, 0x2c, 0xc5, 0xac, 0xc5, 0x6c, 0xc9, 0xb2, 0x98, 0x87, 0xac, 0x75, 0xdb, 0xe8, + 0x56, 0xdd, 0xcc, 0xa1, 0x18, 0x22, 0x15, 0x0c, 0x8a, 0x86, 0x30, 0x21, 0x95, 0xb2, 0xcd, 0x55, + 0x22, 0x8a, 0x82, 0xe3, 0xca, 0xa1, 0x38, 0x84, 0xf4, 0x94, 0x06, 0xa2, 0xa6, 0x3a, 0x12, 0xbc, + 0xe4, 0x4e, 0xbb, 0xf0, 0x7a, 0xd1, 0xa8, 0x89, 0x40, 0xcb, 0x99, 0x8e, 0x04, 0xe0, 0xf5, 0xd1, + 0xd3, 0x99, 0x59, 0x59, 0x59, 0x99, 0x59, 0xe6, 0xac, 0xe1, 0x2b, 0xad, 0x2a, 0x2c, 0xb8, 0xd2, + 0xd2, 0xc5, 0x1a, 0xb0, 0x87, 0x2b, 0xd8, 0x95, 0xdc, 0x94, 0x1e, 0x1a, 0xdc, 0x33, 0xe4, 0x75, + 0xda, 0xe0, 0xa6, 0x1c, 0x2e, 0xd7, 0x77, 0xbe, 0x66, 0x1b, 0xa3, 0xbc, 0x06, 0x23, 0xa5, 0xaa, + 0xf1, 0x88, 0x12, 0x12, 0xb1, 0xab, 0xb7, 0x62, 0x1c, 0xfc, 0xab, 0xe8, 0xbd, 0x67, 0xd9, 0x66, + 0x7b, 0x42, 0x0e, 0xae, 0x4e, 0xd2, 0xbc, 0xff, 0x4a, 0x45, 0x41, 0x3f, 0xc2, 0xf2, 0x3f, 0x13, + 0xd3, 0xfc, 0xa2, 0xe5, 0x7b, 0x71, 0x2b, 0x38, 0x80, 0x36, 0xb6, 0x51, 0x5c, 0x73, 0x73, 0x6c, + 0xab, 0xeb, 0xe2, 0xd6, 0x51, 0xfa, 0x80, 0xeb, 0x34, 0x9e, 0x79, 0x31, 0x2f, 0x2e, 0xc4, 0xd3, + 0xa9, 0x8a, 0x8e, 0x07, 0x2a, 0xfc, 0xc2, 0x45, 0x06, 0x13, 0x64, 0x6d, 0x76, 0x2e, 0xd8, 0x03, + 0xf8, 0x6c, 0xf9, 0xca, 0x48, 0xdc, 0xae, 0x6f, 0x87, 0x50, 0x3b, 0xa1, 0x31, 0xb6, 0x15, 0x34, + 0x6c, 0x51, 0x45, 0x1b, 0xf1, 0x4e, 0x02, 0xc7, 0x6d, 0x94, 0x2b, 0xc6, 0x09, 0x0d, 0xf2, 0xb2, + 0x2d, 0xbf, 0xb1, 0x35, 0x7f, 0x41, 0xb9, 0x9b, 0x7e, 0x2c, 0x10, 0x48, 0xe8, 0x8f, 0xf5, 0xea, + 0xbb, 0xf1, 0xfc, 0x3f, 0xc9, 0x62, 0x41, 0x16, 0x35, 0x99, 0x59, 0xd9, 0xb9, 0x43, 0x64, 0xe2, + 0xa7, 0x8c, 0xcd, 0x04, 0x8b, 0xb7, 0xe4, 0xca, 0xc7, 0xe9, 0xf6, 0x8a, 0x66, 0xe8, 0xf4, 0xac, + 0xef, 0x51, 0x12, 0xf8, 0x5e, 0xcc, 0xaf, 0x06, 0x03, 0x9d, 0x77, 0x07, 0x5f, 0x6a, 0x81, 0xa9, + 0x31, 0x69, 0x20, 0x2c, 0x31, 0xab, 0x42, 0xbf, 0xb6, 0xa0, 0xc5, 0x28, 0xb9, 0x96, 0xfe, 0x9a, + 0x91, 0x56, 0x68, 0xd0, 0x52, 0xfb, 0x09, 0x6e, 0x5c, 0xa0, 0x00, 0x36, 0x88, 0x5b, 0xfd, 0xe2, + 0x9a, 0xef, 0x0d, 0xfd, 0xca, 0x6b, 0x83, 0x5e, 0x31, 0xad, 0xd7, 0xbe, 0x80, 0xd4, 0xb1, 0xfd, + 0xe3, 0xd7, 0xdc, 0xac, 0xea, 0x47, 0x93, 0x02, 0xb6, 0xed, 0xf3, 0xc0, 0x3b, 0x27, 0xdc, 0xa8, + 0xaa, 0x47, 0x03, 0xc0, 0x6d, 0xd3, 0x3c, 0xbc, 0x62, 0x21, 0x56, 0x74, 0x49, 0x07, 0xee, 0x46, + 0x83, 0x22, 0x15, 0x5d, 0x64, 0xa1, 0xef, 0x1e, 0x00, 0x80, 0x08, 0xa1, 0x9a, 0x16, 0xc7, 0x83, + 0x2d, 0xb2, 0x75, 0x58, 0xc4, 0x83, 0x5b, 0x15, 0x1d, 0x58, 0x68, 0x88, 0x59, 0xc6, 0xd7, 0xe0, + 0xbb, 0x52, 0xca, 0xdb, 0x31, 0x75, 0xaa, 0x5b, 0x3f, 0x5f, 0x04, 0x09, 0x50, 0x1c, 0x9e, 0x85, + 0x18, 0x35, 0xa2, 0x16, 0x7b, 0x34, 0xe1, 0x86, 0xea, 0x7f, 0xe3, 0x65, 0x00, 0xe3, 0xcc, 0x69, + 0xc6, 0x44, 0xce, 0x75, 0x19, 0xe3, 0x6a, 0x93, 0x16, 0x32, 0xdc, 0xf1, 0x7c, 0x34, 0xf8, 0x17, + 0xd7, 0x69, 0x88, 0x5d, 0x9d, 0x4f, 0x89, 0xc6, 0x78, 0xc9, 0xa1, 0x85, 0xac, 0x4b, 0x65, 0x88, + 0xe4, 0x97, 0x83, 0xb3, 0x56, 0x18, 0x02, 0x3e, 0xd8, 0x5e, 0x71, 0xc5, 0xcd, 0xb1, 0x10, 0x39, + 0x6b, 0x0d, 0xfd, 0x62, 0x4e, 0x34, 0x39, 0x17, 0x7b, 0x89, 0x7b, 0xbd, 0xde, 0x60, 0xd4, 0xc6, + 0x36, 0x73, 0x6f, 0x34, 0x2e, 0x32, 0xca, 0x93, 0x60, 0x21, 0x7d, 0xdd, 0xad, 0xee, 0x55, 0x7b, + 0xf9, 0xc8, 0xca, 0x8a, 0x99, 0x8c, 0xf3, 0xd1, 0x6a, 0x37, 0xe0, 0xc4, 0xb5, 0xce, 0x18, 0xef, + 0x74, 0x02, 0xb1, 0x8f, 0xf2, 0x7f, 0xac, 0xac, 0x98, 0xc1, 0x38, 0xa1, 0x3a, 0x91, 0x71, 0x96, + 0xe0, 0x5f, 0x58, 0x45, 0x9c, 0x93, 0x25, 0x20, 0xf0, 0x7c, 0x80, 0xd7, 0x0d, 0x8d, 0x25, 0x65, + 0x8b, 0x62, 0x32, 0xbe, 0xfb, 0xf4, 0x6e, 0x9f, 0x01, 0x5a, 0x00, 0xaa, 0x3b, 0x1b, 0xe9, 0x95, + 0x46, 0xf8, 0x82, 0x5b, 0x05, 0xe0, 0xc0, 0x7d, 0x2d, 0xf9, 0x35, 0xe5, 0xb6, 0xa9, 0x38, 0x82, + 0x44, 0xee, 0x64, 0x41, 0x9b, 0x94, 0xa9, 0x7d, 0x50, 0xb9, 0xa5, 0xda, 0xd5, 0x34, 0xb8, 0x84, + 0xcd, 0xc3, 0x78, 0xa0, 0x42, 0x4b, 0xfa, 0xc2, 0x21, 0xd9, 0xcd, 0x8b, 0x26, 0xde, 0xf1, 0x46, + 0x0d, 0x81, 0xbc, 0x3f, 0x0c, 0xcf, 0xb5, 0xdc, 0x98, 0x95, 0x55, 0x8b, 0xf4, 0xa0, 0xb6, 0x83, + 0xbe, 0x65, 0x8a, 0xeb, 0xb6, 0xb1, 0x9d, 0x98, 0x8e, 0x48, 0x8b, 0x9b, 0xc4, 0x11, 0x10, 0xff, + 0x10, 0xbe, 0x85, 0x6d, 0x10, 0x1f, 0x57, 0x41, 0x1c, 0x44, 0xf2, 0x8e, 0x8f, 0x3d, 0xb6, 0x08, + 0x26, 0xa7, 0x79, 0x52, 0x8e, 0xc6, 0xfe, 0x13, 0x03, 0x0f, 0xe1, 0xd5, 0x99, 0xd8, 0x0b, 0xf6, + 0x7b, 0x60, 0xd6, 0x1e, 0x85, 0x90, 0x16, 0xf4, 0x1a, 0x42, 0xd9, 0x60, 0x84, 0x03, 0xfa, 0x9c, + 0xd1, 0xa9, 0x46, 0xcc, 0xbc, 0x37, 0xd4, 0x07, 0xd6, 0x68, 0x91, 0x3f, 0x22, 0xad, 0xbc, 0x06, + 0xf3, 0xdb, 0xc6, 0xbd, 0x61, 0xfc, 0x4e, 0xa4, 0x28, 0x48, 0x5e, 0x13, 0x69, 0xb5, 0xcf, 0x5a, + 0xc4, 0x7b, 0xc9, 0x85, 0x49, 0x39, 0x4d, 0x8e, 0x78, 0x83, 0x9f, 0x34, 0xc7, 0x5e, 0x70, 0xc4, + 0x91, 0x05, 0x24, 0x5f, 0x95, 0xe0, 0x8f, 0x3d, 0x1f, 0x6e, 0x6c, 0x19, 0x6f, 0x3a, 0x8e, 0x01, + 0x48, 0x18, 0x79, 0x54, 0xd5, 0xf9, 0x17, 0xea, 0x4c, 0xbd, 0x86, 0x0a, 0x35, 0x7d, 0xc5, 0xab, + 0x29, 0xf6, 0x1a, 0xf7, 0x46, 0x9d, 0x3a, 0xe7, 0x3b, 0xfe, 0x5e, 0x03, 0xe4, 0x9f, 0x17, 0x62, + 0xaf, 0xd9, 0x27, 0xc2, 0xaa, 0x8f, 0x0c, 0x1b, 0x8f, 0x03, 0xbb, 0x09, 0x7d, 0xb1, 0x7e, 0xec, + 0x1b, 0x45, 0x96, 0xaa, 0x7c, 0x76, 0x20, 0x66, 0x12, 0xbe, 0x25, 0xd7, 0xe1, 0x58, 0x2f, 0x9a, + 0xbc, 0x86, 0x26, 0xe2, 0xc5, 0x5a, 0xbf, 0x63, 0xb5, 0x58, 0xd1, 0x12, 0x5e, 0x11, 0xea, 0x72, + 0x52, 0xcc, 0x6b, 0x23, 0x8b, 0x62, 0xfd, 0x24, 0x05, 0x7d, 0x32, 0xf8, 0x1d, 0xc7, 0x6a, 0x72, + 0x9d, 0x9c, 0x00, 0x2e, 0xf1, 0x1e, 0x01, 0xf0, 0x61, 0x3a, 0x22, 0x2e, 0x6f, 0xd6, 0x92, 0xe5, + 0x75, 0x5a, 0x88, 0xbe, 0xc8, 0x0b, 0x75, 0x2a, 0x08, 0xdf, 0x88, 0xa6, 0x2e, 0xe1, 0x44, 0xec, + 0x0d, 0xb2, 0xba, 0x4e, 0x43, 0x9c, 0x75, 0x6a, 0x7c, 0xf9, 0xda, 0xcc, 0x42, 0xf0, 0x01, 0x3d, + 0x8c, 0x32, 0xa4, 0xed, 0x7e, 0x43, 0x0b, 0xb7, 0xed, 0x57, 0x78, 0xb6, 0x9a, 0xe3, 0xaf, 0xea, + 0xc7, 0x1f, 0xe9, 0xc1, 0x24, 0x3e, 0x2f, 0x7a, 0xe3, 0x98, 0x90, 0xbb, 0x9c, 0xd3, 0x88, 0x97, + 0x5c, 0x20, 0x93, 0x93, 0x34, 0x59, 0x22, 0xc4, 0xee, 0xa9, 0x27, 0xb4, 0xf8, 0x7b, 0x3b, 0xa9, + 0x27, 0x54, 0x68, 0x14, 0xaa, 0x13, 0x64, 0xf5, 0x09, 0x0d, 0x79, 0xe1, 0x84, 0x5a, 0x5a, 0xca, + 0xe2, 0x0f, 0xa7, 0xc0, 0xcc, 0xcf, 0xbb, 0xed, 0x9c, 0x29, 0x49, 0x32, 0x1e, 0xc7, 0x2b, 0xd6, + 0x66, 0xf9, 0x30, 0x1b, 0x3c, 0x3f, 0x3d, 0xcc, 0xc8, 0x10, 0x98, 0xe5, 0xfd, 0x13, 0xcc, 0x5b, + 0x27, 0xd3, 0x83, 0xe1, 0x18, 0x7a, 0x3e, 0xdc, 0x61, 0x4e, 0x36, 0x65, 0x93, 0xbc, 0x36, 0xbc, + 0xd5, 0x61, 0x2e, 0x9e, 0x20, 0x27, 0xec, 0x0c, 0xde, 0x89, 0x3f, 0xc3, 0xf2, 0x73, 0xf0, 0xd7, + 0x1f, 0xa4, 0xc3, 0x2b, 0x9f, 0x09, 0x2d, 0xd6, 0x9f, 0x43, 0x40, 0x85, 0xbb, 0xfd, 0x77, 0x91, + 0x86, 0x34, 0x88, 0xac, 0xc8, 0x39, 0x7e, 0xa7, 0xad, 0x40, 0xf1, 0x53, 0x15, 0x0d, 0xf8, 0xf6, + 0x4c, 0x1a, 0x79, 0x08, 0x1d, 0x7c, 0x28, 0xb3, 0x44, 0xb7, 0x5c, 0x89, 0x61, 0x56, 0x0d, 0xbe, + 0xc9, 0x69, 0x25, 0xaf, 0x30, 0xab, 0x4b, 0xc2, 0x99, 0xc6, 0x6a, 0x66, 0x0d, 0x7c, 0xc1, 0xf8, + 0xaf, 0x7a, 0x3a, 0xb5, 0x08, 0x7d, 0x1a, 0x8a, 0x82, 0x5b, 0xc5, 0x9f, 0xe3, 0xd8, 0x7b, 0x7e, + 0x55, 0xc9, 0x30, 0x41, 0x5e, 0x72, 0x2d, 0x7b, 0x07, 0x2a, 0x70, 0x6d, 0xd4, 0xef, 0x1b, 0xa8, + 0xd0, 0xc2, 0xf7, 0xc3, 0x3b, 0x59, 0xab, 0x61, 0x80, 0x8f, 0xb6, 0x8a, 0x6c, 0x4f, 0x8b, 0x95, + 0xb0, 0xf0, 0x6d, 0x15, 0xa3, 0xac, 0x10, 0xcd, 0xbd, 0xe5, 0x0e, 0xed, 0x8e, 0xe0, 0x0d, 0xf3, + 0x8f, 0x59, 0xf9, 0x20, 0x0c, 0x5b, 0xed, 0xe6, 0x9f, 0x93, 0xf0, 0xcd, 0x15, 0xbc, 0xde, 0xfc, + 0x09, 0x13, 0xba, 0x6c, 0xce, 0xc6, 0xd3, 0xcb, 0xe6, 0x13, 0xe4, 0x25, 0x3b, 0x28, 0xaf, 0xd0, + 0xfa, 0xe7, 0x4a, 0xeb, 0xe5, 0x1d, 0xeb, 0x3e, 0x0c, 0xe3, 0xfb, 0x77, 0xfe, 0xed, 0xab, 0x75, + 0xc1, 0xa7, 0x16, 0xfa, 0xc4, 0x3a, 0xfb, 0x3b, 0x9e, 0x22, 0x9e, 0x20, 0xec, 0x54, 0xa8, 0xb6, + 0xf2, 0x68, 0x18, 0x69, 0xfa, 0xea, 0xa9, 0x20, 0xf8, 0x3f, 0x43, 0x60, 0x39, 0x99, 0xbe, 0xad, + 0x81, 0xdb, 0x9e, 0x33, 0xd6, 0x84, 0xe6, 0x05, 0xa6, 0x8e, 0xae, 0x42, 0xd1, 0x81, 0x79, 0xae, + 0x38, 0x08, 0x02, 0x6d, 0x3f, 0x55, 0xe4, 0x66, 0xfc, 0x2f, 0xbc, 0x4c, 0x1e, 0xfc, 0x67, 0xc9, + 0xc5, 0xdf, 0x4f, 0x24, 0x8d, 0xa2, 0x33, 0x5e, 0x2b, 0x2e, 0x8f, 0x8f, 0xbe, 0xfe, 0x41, 0xfe, + 0x9a, 0xfa, 0x5c, 0x71, 0x65, 0xbc, 0x1a, 0x22, 0x6f, 0xa8, 0x01, 0x98, 0xf6, 0xfa, 0x47, 0xa4, + 0x25, 0xbf, 0xfe, 0xf6, 0x96, 0x86, 0x9e, 0x4d, 0xaa, 0xeb, 0x1f, 0xf8, 0x34, 0xd2, 0x0b, 0x5c, + 0x40, 0x72, 0x8e, 0x09, 0xfc, 0x70, 0xfd, 0x83, 0x9b, 0xd0, 0x17, 0x9a, 0xbd, 0x00, 0xdd, 0x42, + 0xe8, 0xfe, 0xe8, 0x1c, 0x55, 0x9b, 0x1e, 0xb8, 0x3f, 0x2d, 0xc0, 0x1d, 0x72, 0xe3, 0xeb, 0x92, + 0xd0, 0x02, 0xc3, 0xbe, 0xdc, 0xe2, 0x19, 0xf4, 0x9a, 0x80, 0xa0, 0x8a, 0x8f, 0x38, 0x96, 0x01, + 0x31, 0x6e, 0x06, 0xf8, 0x7c, 0x88, 0x99, 0x63, 0xae, 0x7f, 0x50, 0x1c, 0x43, 0x6b, 0x32, 0xa0, + 0x6a, 0x44, 0xc6, 0xb1, 0xfc, 0x35, 0xc8, 0xcd, 0xe0, 0xbf, 0x9b, 0x7a, 0x37, 0xfe, 0x94, 0x05, + 0xb0, 0x31, 0x11, 0x58, 0x7e, 0xed, 0xb5, 0x50, 0x99, 0x95, 0xa3, 0x86, 0x95, 0xc3, 0xe4, 0xfe, + 0x81, 0xa1, 0xe5, 0x70, 0xf9, 0x9b, 0x61, 0x65, 0x95, 0x7c, 0x61, 0x58, 0x39, 0x42, 0x6e, 0x1e, + 0x56, 0x8e, 0x94, 0xf7, 0x0d, 0x2b, 0x8f, 0x90, 0x3f, 0x52, 0xca, 0xab, 0x68, 0x51, 0x2d, 0xff, + 0x86, 0x16, 0x6f, 0xd5, 0x8f, 0x94, 0x6b, 0x87, 0xd6, 0x47, 0xc9, 0xcf, 0x07, 0xeb, 0x95, 0xf2, + 0x28, 0xf9, 0x99, 0x61, 0x65, 0x8d, 0xfc, 0xe4, 0xb0, 0xf2, 0x68, 0xf9, 0xb1, 0x61, 0xe5, 0x31, + 0xf2, 0xa2, 0xe1, 0xf8, 0x39, 0xfa, 0xbb, 0x66, 0x83, 0x65, 0xad, 0x3c, 0x71, 0xe8, 0x78, 0x63, + 0xe5, 0x51, 0xc3, 0xdb, 0x8f, 0x93, 0xfb, 0x7f, 0x18, 0xda, 0x7e, 0xbc, 0xfc, 0xcd, 0xb0, 0x72, + 0xb4, 0x7c, 0x61, 0x58, 0x79, 0x82, 0xdc, 0x3c, 0xac, 0x7c, 0x97, 0xbc, 0x6f, 0x58, 0xf9, 0x6e, + 0xf9, 0xa3, 0x61, 0xe5, 0x18, 0xf9, 0x9d, 0x61, 0xe5, 0x58, 0xb9, 0x76, 0x58, 0x79, 0xa2, 0xfc, + 0xdc, 0xb0, 0xf2, 0x24, 0xb9, 0x78, 0x58, 0x39, 0x4e, 0xfe, 0xd9, 0xb0, 0xf2, 0x3d, 0xf2, 0xa3, + 0x3f, 0x0c, 0xe1, 0x67, 0xb2, 0xbc, 0xe8, 0x87, 0x61, 0xfc, 0x4c, 0x91, 0xef, 0x07, 0x40, 0x8e, + 0xfe, 0x28, 0xda, 0x68, 0x36, 0xfe, 0x46, 0x64, 0x0e, 0xbe, 0x72, 0x4c, 0xf0, 0x44, 0x0e, 0x5c, + 0x61, 0xb4, 0xeb, 0xf4, 0x96, 0x31, 0x3d, 0xde, 0x46, 0x63, 0x1b, 0xbe, 0x46, 0x20, 0x9c, 0x66, + 0x7a, 0x2e, 0xb8, 0x02, 0x5b, 0xa2, 0x7a, 0x72, 0xda, 0x7a, 0x3e, 0x17, 0xce, 0x31, 0x3d, 0x97, + 0xc0, 0x8b, 0x1a, 0x3b, 0xc8, 0x59, 0x57, 0x80, 0x7f, 0x10, 0x72, 0x37, 0x55, 0xcf, 0x75, 0xdf, + 0x64, 0xd7, 0x45, 0x7e, 0x14, 0x3d, 0xb2, 0xa2, 0xb7, 0x91, 0xea, 0x89, 0x9f, 0x7e, 0x37, 0xe0, + 0x0f, 0x2a, 0xf5, 0x20, 0xa0, 0xf3, 0x2d, 0x7c, 0x97, 0xb6, 0x1f, 0x52, 0x78, 0x0c, 0xcf, 0x0e, + 0x36, 0x0a, 0xfe, 0x29, 0x90, 0x63, 0xf2, 0x13, 0x0c, 0xe7, 0xe8, 0xab, 0xf4, 0xf6, 0x08, 0x43, + 0x5e, 0x87, 0xbd, 0x94, 0x86, 0x5e, 0xbe, 0x0d, 0xb0, 0x08, 0xd1, 0xdf, 0xa9, 0x25, 0x17, 0x12, + 0x9a, 0x6a, 0x4d, 0xed, 0xad, 0x5f, 0x13, 0x4f, 0xeb, 0x37, 0xf0, 0x30, 0xb2, 0x1f, 0xb2, 0xe6, + 0xd6, 0x2b, 0xe4, 0x5a, 0xb0, 0x4c, 0xf2, 0xce, 0x40, 0xae, 0xdb, 0xfa, 0x15, 0x14, 0x7d, 0x23, + 0xaf, 0x13, 0x6f, 0x0f, 0xac, 0x3e, 0x67, 0xf1, 0xa5, 0xa5, 0x9e, 0xc2, 0x33, 0xc4, 0xe8, 0x35, + 0x9c, 0xb5, 0x4f, 0x80, 0x56, 0x40, 0x43, 0x6b, 0x3b, 0xf1, 0x40, 0x9a, 0x6b, 0xf0, 0x38, 0x38, + 0xc3, 0x59, 0x7e, 0x94, 0x94, 0x1f, 0xf0, 0x45, 0xea, 0x03, 0xfa, 0x8b, 0x60, 0x44, 0x01, 0xa1, + 0xa2, 0x8d, 0x21, 0x5e, 0xd2, 0xef, 0x1c, 0xd1, 0xe3, 0xa5, 0xbc, 0x42, 0x24, 0xf6, 0x24, 0xf5, + 0x46, 0x77, 0xca, 0x07, 0x52, 0xf6, 0x0e, 0xa1, 0xa2, 0x03, 0x8f, 0xde, 0xf0, 0x9d, 0x4a, 0xa3, + 0x76, 0xcb, 0xd8, 0x9e, 0xbc, 0x76, 0x21, 0xa7, 0x83, 0xe9, 0xc9, 0xeb, 0x68, 0x34, 0x7a, 0x51, + 0x5e, 0x20, 0xbc, 0x10, 0x26, 0xe0, 0xbf, 0xa2, 0x1d, 0x28, 0x24, 0x2d, 0xae, 0xd3, 0xfc, 0x4c, + 0xc3, 0x75, 0x47, 0x24, 0xe9, 0x25, 0x17, 0x7c, 0x77, 0xbb, 0xba, 0x07, 0x85, 0xd5, 0x8d, 0x3f, + 0xdb, 0x41, 0xbf, 0x7b, 0xe1, 0xd3, 0xa2, 0x3f, 0xdd, 0xf9, 0x7b, 0x94, 0x54, 0xab, 0x03, 0x96, + 0x9e, 0x76, 0xfa, 0x0a, 0x30, 0x77, 0xf0, 0xb4, 0x70, 0x6d, 0x0a, 0xc9, 0x69, 0xe7, 0xa3, 0x0d, + 0x5e, 0xbb, 0xca, 0xd0, 0x63, 0x57, 0x93, 0xfe, 0x84, 0x6b, 0xb1, 0xf5, 0x90, 0x7c, 0xf5, 0xfa, + 0x78, 0x10, 0xd6, 0x3e, 0x14, 0x56, 0x42, 0x33, 0x4a, 0x04, 0x96, 0xc8, 0x6b, 0xad, 0xed, 0xb5, + 0xa6, 0x36, 0x45, 0x60, 0x75, 0xa4, 0xb7, 0xf5, 0x2b, 0x28, 0x11, 0x58, 0xa1, 0x2f, 0x40, 0x18, + 0x40, 0xa1, 0xad, 0x57, 0xb0, 0x5e, 0xc6, 0x7a, 0x10, 0xd9, 0x19, 0xd2, 0xda, 0xfa, 0x75, 0xcf, + 0x57, 0x40, 0xc6, 0x29, 0x43, 0xab, 0x3d, 0x92, 0x1f, 0x63, 0x68, 0x74, 0x44, 0x3d, 0x60, 0x6c, + 0x99, 0x04, 0x5d, 0x4e, 0x91, 0x86, 0x84, 0x9e, 0xd8, 0x63, 0x88, 0xe0, 0x73, 0xc4, 0xd3, 0xde, + 0xfa, 0x1d, 0x48, 0x7a, 0x00, 0xd6, 0x95, 0x0b, 0x42, 0x85, 0x97, 0xb9, 0x53, 0x64, 0x41, 0x81, + 0xc5, 0x61, 0x40, 0x0a, 0x02, 0xc2, 0xdf, 0x1a, 0xd2, 0xe2, 0x6f, 0x2c, 0x9c, 0x02, 0x01, 0xcc, + 0x36, 0xf4, 0x80, 0x00, 0xfa, 0x7b, 0xba, 0x7d, 0x93, 0xc9, 0xf5, 0xe1, 0x22, 0xc0, 0x1b, 0x96, + 0xbd, 0xa0, 0x26, 0x30, 0x49, 0xed, 0x54, 0x5b, 0xf0, 0xbe, 0xa0, 0xe1, 0x1c, 0xb7, 0x8d, 0x50, + 0xfe, 0xeb, 0xa8, 0xb6, 0x74, 0x80, 0xb6, 0xf4, 0xef, 0x0d, 0x6a, 0x0b, 0xdf, 0x6e, 0xb7, 0x2b, + 0xda, 0xf2, 0x2c, 0xde, 0x10, 0x33, 0x75, 0x50, 0x85, 0x49, 0xf0, 0xb6, 0x7e, 0x01, 0xca, 0x09, + 0xea, 0x40, 0xce, 0x8b, 0x79, 0xed, 0x23, 0x4f, 0x81, 0x4a, 0x43, 0x1d, 0x6a, 0x08, 0x84, 0x30, + 0x64, 0xa0, 0xf5, 0x5b, 0x28, 0x81, 0x80, 0xbe, 0xa6, 0x1a, 0x73, 0x03, 0x35, 0xa6, 0x2d, 0xa4, + 0x31, 0x5e, 0xc8, 0x04, 0x14, 0x8d, 0x81, 0x30, 0xb0, 0x5d, 0xd1, 0x98, 0xb6, 0xe1, 0x1a, 0x73, + 0x94, 0x6a, 0xcc, 0x51, 0xd0, 0x96, 0x66, 0xe1, 0x48, 0xdc, 0x6d, 0x1c, 0xe7, 0x69, 0xf0, 0x77, + 0x3b, 0x79, 0x35, 0x06, 0x6a, 0x79, 0xed, 0x2e, 0x37, 0x3f, 0xcb, 0x70, 0xc3, 0xa1, 0x46, 0x51, + 0xf5, 0xf4, 0xf8, 0x26, 0x91, 0x3e, 0xb0, 0x96, 0x5b, 0x1c, 0xf7, 0x00, 0x3d, 0x38, 0xe9, 0x57, + 0x89, 0xbf, 0xe7, 0xaa, 0xfe, 0x62, 0x27, 0xee, 0x67, 0x91, 0x6b, 0x86, 0x7a, 0x6e, 0xdb, 0x6f, + 0x28, 0xc3, 0x4d, 0xc2, 0xd5, 0x29, 0x10, 0xcd, 0xf1, 0x77, 0xd1, 0x09, 0x1f, 0xb0, 0x8f, 0x24, + 0xd7, 0x20, 0x89, 0xa8, 0x07, 0xbc, 0xc0, 0x95, 0xef, 0x39, 0x08, 0x53, 0x6e, 0x4d, 0x39, 0xfe, + 0xec, 0x2a, 0xe9, 0xc7, 0x29, 0x6f, 0xc7, 0xc9, 0xed, 0x80, 0x29, 0xf7, 0xb7, 0x7e, 0x1d, 0x54, + 0x85, 0x0b, 0xa4, 0xc2, 0x4b, 0xa1, 0x38, 0xe5, 0xed, 0x38, 0xe5, 0x1d, 0x23, 0xbd, 0xad, 0x5f, + 0x21, 0xd7, 0xa7, 0xc0, 0x2e, 0x81, 0xe7, 0x06, 0xd2, 0x6a, 0x38, 0x35, 0x38, 0xeb, 0xde, 0x49, + 0xd0, 0xab, 0x15, 0xa0, 0x68, 0x77, 0x30, 0xf5, 0x5e, 0x52, 0xd7, 0xfa, 0x05, 0xf6, 0xff, 0xae, + 0x55, 0x06, 0xeb, 0xab, 0x27, 0x2d, 0xf8, 0xc2, 0xb0, 0xc2, 0x3a, 0x83, 0xa7, 0x24, 0x4c, 0x34, + 0x86, 0xe7, 0x78, 0xa5, 0x7e, 0x45, 0x0e, 0xb3, 0xbc, 0x3e, 0xfe, 0xcf, 0xfb, 0xff, 0x95, 0xb7, + 0xf2, 0xa3, 0x35, 0x9d, 0x62, 0xd2, 0x86, 0xfc, 0xb3, 0xf5, 0x26, 0xb1, 0x0e, 0xe7, 0x8b, 0x0d, + 0xfd, 0x38, 0xad, 0xf6, 0xb6, 0xdf, 0xaa, 0xc5, 0x03, 0xf1, 0x77, 0x82, 0xdf, 0xaf, 0x87, 0xca, + 0x27, 0x83, 0xe5, 0x93, 0x4a, 0xa3, 0x77, 0x74, 0xca, 0x37, 0xd4, 0x9b, 0x57, 0x19, 0x75, 0x45, + 0xa5, 0x0e, 0x67, 0x41, 0x49, 0x49, 0x81, 0xb3, 0xa8, 0xac, 0x54, 0xb7, 0xd6, 0x56, 0xb8, 0xf6, + 0x19, 0x9d, 0xb5, 0xa0, 0xa8, 0xa4, 0x70, 0x1d, 0x93, 0xed, 0x2c, 0x58, 0xfb, 0xcc, 0x83, 0x8e, + 0xc2, 0xf5, 0x1b, 0x0a, 0x4b, 0x9d, 0x00, 0xe4, 0x4b, 0x9c, 0x4c, 0xf6, 0x32, 0x53, 0xaa, 0xae, + 0x70, 0xf3, 0xda, 0xc2, 0x72, 0x6c, 0xcf, 0xe4, 0x1b, 0x53, 0x32, 0xf2, 0xd3, 0xb2, 0xf3, 0x99, + 0x94, 0x32, 0xbe, 0x64, 0x9d, 0xae, 0xb4, 0xcc, 0xa9, 0x5b, 0x57, 0xe8, 0x2c, 0xb4, 0x6f, 0x28, + 0x2a, 0x2d, 0xd4, 0x3d, 0x5d, 0x86, 0x45, 0x7b, 0xd1, 0xc6, 0x42, 0x26, 0x39, 0xc5, 0xbc, 0x4c, + 0x67, 0x2f, 0x5c, 0x5b, 0x52, 0x50, 0xb4, 0xa1, 0xe0, 0xe9, 0x92, 0x42, 0x66, 0x29, 0xd4, 0x95, + 0x94, 0x15, 0xac, 0x2b, 0xb4, 0x0f, 0x03, 0x2f, 0x2b, 0xdd, 0x58, 0x50, 0x52, 0xb4, 0x4e, 0xb7, + 0x32, 0x3b, 0x9b, 0x99, 0x51, 0xca, 0x97, 0x94, 0x24, 0x30, 0x85, 0x9b, 0x9d, 0x73, 0x1e, 0xd6, + 0x19, 0x4b, 0x9d, 0x45, 0xce, 0x2d, 0xba, 0x22, 0x07, 0x1d, 0xc3, 0x5e, 0xb8, 0x9e, 0x2f, 0x29, + 0xb0, 0xeb, 0xac, 0x40, 0x27, 0x00, 0xec, 0x3a, 0xc7, 0x96, 0x0d, 0x25, 0x45, 0xa5, 0xcf, 0x44, + 0x05, 0x5b, 0x9b, 0x0b, 0xec, 0x4e, 0x5d, 0x99, 0x55, 0x57, 0x5e, 0xe0, 0xb4, 0x85, 0xfa, 0xac, + 0x2b, 0x82, 0x81, 0x9c, 0x65, 0xf6, 0x2d, 0xc3, 0x3b, 0x98, 0x0a, 0x37, 0x20, 0x0c, 0x24, 0x50, + 0xb6, 0x96, 0xca, 0xc0, 0x01, 0x2d, 0x1d, 0x58, 0xdc, 0x04, 0x22, 0x08, 0xa1, 0x03, 0x34, 0xeb, + 0xca, 0x0a, 0x15, 0x44, 0x20, 0x2e, 0xc0, 0x5e, 0x54, 0xaa, 0x9b, 0xcd, 0x38, 0xad, 0xce, 0xf2, + 0x87, 0x75, 0x59, 0x85, 0x05, 0xeb, 0xa8, 0xcc, 0x78, 0x7b, 0x61, 0x08, 0xdf, 0x86, 0x82, 0x72, + 0x10, 0x93, 0xad, 0x80, 0x77, 0x38, 0x0b, 0xd7, 0xcd, 0x62, 0xd2, 0x92, 0x57, 0xa2, 0xb0, 0x57, + 0x58, 0x1e, 0x60, 0x74, 0xba, 0x27, 0xa7, 0x97, 0xe7, 0x45, 0x31, 0x4f, 0x4e, 0x5f, 0xa5, 0x7b, + 0xf0, 0x11, 0xdd, 0xf4, 0x55, 0x79, 0xba, 0x87, 0xe1, 0xaf, 0x4e, 0xb7, 0x78, 0xba, 0x43, 0x37, + 0x63, 0xfa, 0xe6, 0x84, 0x47, 0xa2, 0x98, 0xd4, 0xa2, 0x8d, 0x45, 0x0e, 0x9c, 0x8f, 0xa7, 0xb7, + 0xe8, 0x12, 0x99, 0x15, 0xa6, 0x65, 0xb7, 0x04, 0x53, 0x56, 0xbe, 0xb6, 0x6c, 0x5d, 0x61, 0x90, + 0x2e, 0xbe, 0x14, 0x58, 0x2a, 0xdb, 0x58, 0x68, 0x47, 0xd9, 0xe9, 0x0a, 0xed, 0xf6, 0x32, 0xbb, + 0x43, 0x67, 0x2d, 0xe3, 0x4b, 0xd7, 0x45, 0x31, 0x45, 0xc1, 0x0e, 0xd0, 0x12, 0x67, 0x6f, 0x43, + 0xc1, 0xfa, 0xa2, 0xb5, 0xcc, 0xac, 0x59, 0xb3, 0x99, 0x44, 0xfd, 0x9c, 0xb9, 0xf3, 0x1e, 0x9a, + 0xbf, 0x60, 0xa1, 0xa1, 0xe0, 0xe9, 0xb5, 0xeb, 0x0a, 0xad, 0x8c, 0xb1, 0x74, 0x1d, 0xca, 0xca, + 0x69, 0x2f, 0x58, 0x5b, 0x38, 0x4b, 0xc7, 0xcc, 0xc6, 0x69, 0x9b, 0x5d, 0x52, 0x84, 0x53, 0x38, + 0xcb, 0xb1, 0xc5, 0xc1, 0x2c, 0xcb, 0x7e, 0xdc, 0x30, 0x7f, 0x7e, 0xe2, 0xc3, 0xc0, 0xbc, 0xae, + 0xdc, 0x0e, 0x13, 0x05, 0xec, 0x6d, 0x2c, 0x2b, 0xe1, 0x37, 0x14, 0xc2, 0x5c, 0x3b, 0xd6, 0xda, + 0x8b, 0xca, 0x41, 0xaa, 0xcc, 0x52, 0x90, 0xc1, 0x06, 0xca, 0x3b, 0x63, 0x4c, 0x1b, 0x3e, 0xd7, + 0xa9, 0x85, 0x4f, 0xf3, 0xeb, 0x99, 0xc4, 0xcd, 0x4c, 0x90, 0x6c, 0x9c, 0x35, 0x40, 0xec, 0x2c, + 0xdc, 0xa0, 0xb3, 0x15, 0x38, 0x80, 0x0b, 0x07, 0x5f, 0x5e, 0x5e, 0x66, 0x07, 0x41, 0xe9, 0xac, + 0x85, 0x05, 0x4e, 0x90, 0xa2, 0x43, 0x37, 0x7d, 0x73, 0x14, 0x93, 0x0c, 0x23, 0x96, 0x3e, 0xc8, + 0x3b, 0x28, 0x77, 0x1b, 0x86, 0xc8, 0xb5, 0xd4, 0x69, 0xa7, 0xaa, 0x00, 0xaa, 0x5b, 0xb4, 0xae, + 0x50, 0x57, 0xa0, 0x0b, 0xb6, 0x71, 0xc0, 0x04, 0x83, 0xd4, 0x66, 0x31, 0xc9, 0x4e, 0x40, 0x5e, + 0x8e, 0x08, 0x15, 0x42, 0xf3, 0xed, 0x30, 0x45, 0x33, 0x12, 0x74, 0x20, 0xd1, 0xf2, 0xcd, 0x85, + 0x4c, 0x1a, 0xd5, 0x6f, 0x9d, 0xb3, 0x4c, 0x87, 0x2a, 0x88, 0x13, 0xba, 0xbe, 0x50, 0x37, 0x77, + 0x16, 0x13, 0x94, 0x15, 0x4e, 0x32, 0x95, 0x22, 0x13, 0x35, 0x75, 0xaa, 0x2e, 0x5b, 0xa9, 0x0d, + 0xe9, 0x5a, 0xb0, 0x6a, 0x2a, 0x56, 0x65, 0x14, 0x6c, 0x2c, 0xd4, 0x6d, 0x29, 0xe3, 0x75, 0x6b, + 0xcb, 0xca, 0x8b, 0x00, 0xe1, 0xa0, 0xd0, 0x10, 0xb7, 0xd3, 0x56, 0xa8, 0xb3, 0x83, 0x2c, 0x67, + 0xea, 0xa8, 0x48, 0xe1, 0x4b, 0xa9, 0x9f, 0xa9, 0x03, 0xfd, 0x1b, 0x2a, 0x65, 0x44, 0x15, 0xd2, + 0xce, 0x22, 0xe0, 0x1d, 0x26, 0xa3, 0x0c, 0xcc, 0x07, 0xe7, 0x04, 0x50, 0x94, 0x83, 0xb6, 0x15, + 0x29, 0x8a, 0x09, 0xe4, 0x23, 0x44, 0xb1, 0xab, 0xc2, 0x8d, 0x45, 0x6b, 0x0b, 0x7f, 0x1a, 0x15, + 0xc5, 0x0c, 0x9d, 0x2b, 0x4b, 0x91, 0xdd, 0xc9, 0xc3, 0xbc, 0x3b, 0xa8, 0x2a, 0x33, 0x66, 0x10, + 0xa5, 0x43, 0x97, 0xb2, 0x32, 0x6b, 0xf9, 0x03, 0xc9, 0xcb, 0x57, 0x3e, 0x90, 0x6a, 0x5c, 0x8e, + 0x84, 0xd9, 0x0b, 0x11, 0xc3, 0xac, 0x21, 0x36, 0x5b, 0x58, 0x4a, 0xe5, 0x97, 0x3c, 0x27, 0x11, + 0x78, 0x28, 0x2d, 0x0c, 0xea, 0x75, 0x48, 0xe7, 0x96, 0x99, 0x37, 0xce, 0xd3, 0x15, 0xac, 0x5b, + 0x87, 0xc8, 0x1e, 0xd6, 0x4d, 0x9b, 0xee, 0x98, 0xc6, 0x64, 0x07, 0x9d, 0x02, 0x76, 0x2e, 0x07, + 0x38, 0x3c, 0x83, 0xd2, 0x3a, 0x9e, 0x51, 0x94, 0x10, 0x66, 0x6f, 0x96, 0x2e, 0x15, 0x0d, 0x1f, + 0x9e, 0x50, 0xf7, 0xe7, 0xce, 0x51, 0x9c, 0x09, 0xd5, 0xb1, 0x87, 0xa3, 0x98, 0x49, 0x4f, 0xce, + 0xd5, 0x6f, 0x30, 0x27, 0xaf, 0x58, 0x96, 0x02, 0x4f, 0x0b, 0x16, 0xe9, 0x37, 0x4c, 0x7a, 0x32, + 0x71, 0xc3, 0xc3, 0xba, 0x10, 0x2b, 0x43, 0x39, 0x1a, 0x22, 0xd3, 0xa7, 0xf9, 0x22, 0xa0, 0x77, + 0x59, 0xaa, 0x6e, 0x43, 0x91, 0x63, 0x43, 0x81, 0x73, 0xad, 0x6d, 0x56, 0x14, 0xf0, 0x50, 0xba, + 0x11, 0x06, 0x07, 0x66, 0x0b, 0x4a, 0x42, 0x3a, 0x32, 0x68, 0xcb, 0x21, 0x4f, 0x96, 0x4a, 0x85, + 0x45, 0xa9, 0x2d, 0xd8, 0x08, 0x20, 0xaa, 0x96, 0xe9, 0x85, 0xa5, 0x60, 0x39, 0x25, 0x40, 0x7f, + 0x99, 0x53, 0x51, 0x9c, 0xa0, 0x8f, 0x33, 0x15, 0xac, 0xb5, 0xa1, 0xff, 0xa2, 0xce, 0x70, 0x98, + 0x3d, 0x0c, 0x25, 0x0c, 0x14, 0xe9, 0x61, 0x5d, 0x36, 0xbf, 0x76, 0x2d, 0x48, 0xc5, 0x0a, 0x9e, + 0x0a, 0x34, 0xb2, 0x14, 0xe6, 0x09, 0x24, 0xf6, 0x5c, 0x21, 0xd8, 0x9f, 0x22, 0xc2, 0x41, 0x4d, + 0x2b, 0x2b, 0x2f, 0x2c, 0x55, 0x74, 0x68, 0xfa, 0x66, 0x54, 0x80, 0xa7, 0x41, 0xf1, 0xca, 0x41, + 0x24, 0x85, 0xe0, 0x50, 0xa0, 0x07, 0x93, 0xa3, 0xa8, 0x70, 0x56, 0xb2, 0x89, 0x59, 0xb1, 0x32, + 0x2d, 0x1b, 0xcc, 0x25, 0x31, 0x88, 0x23, 0xbb, 0xd0, 0x0e, 0x26, 0x8e, 0xfa, 0x0e, 0x62, 0x5e, + 0x57, 0x54, 0xba, 0x9e, 0x2a, 0x01, 0x45, 0x05, 0x4e, 0x08, 0x24, 0x51, 0x52, 0x02, 0xd5, 0x0a, + 0x2e, 0xf0, 0x20, 0x45, 0x4e, 0x6c, 0xe7, 0xd4, 0x4d, 0x5f, 0x07, 0xde, 0xc3, 0x59, 0xe8, 0x48, + 0x98, 0x09, 0xae, 0x6b, 0x53, 0x29, 0xd5, 0xf4, 0x0d, 0x45, 0xeb, 0x6d, 0x4e, 0x9d, 0xb3, 0xe0, + 0x99, 0x42, 0xd0, 0xfc, 0xd2, 0xf5, 0x85, 0xf6, 0x59, 0x51, 0x21, 0xa5, 0xb7, 0x22, 0x4d, 0x20, + 0xb7, 0x67, 0x86, 0xa8, 0xff, 0x52, 0xfc, 0xab, 0xb3, 0x17, 0x40, 0x4b, 0xea, 0xf3, 0x0b, 0xd7, + 0x81, 0x2c, 0xa7, 0x02, 0xe3, 0x40, 0xf2, 0xfa, 0x52, 0x6a, 0xa9, 0xa0, 0xfa, 0x76, 0x3b, 0x8f, + 0xa6, 0x76, 0xa7, 0x27, 0x0d, 0xf9, 0xc4, 0x59, 0xcc, 0x63, 0x85, 0xf6, 0xd2, 0xc2, 0x92, 0xd9, + 0xa6, 0xb2, 0x75, 0x3c, 0x98, 0x7e, 0x50, 0x2d, 0x0b, 0x74, 0xcf, 0x14, 0x6e, 0x51, 0xf4, 0x11, + 0x30, 0x95, 0xe2, 0x13, 0xe8, 0x14, 0x3f, 0x8b, 0x59, 0x95, 0x66, 0xcc, 0x0a, 0xba, 0x8a, 0xdc, + 0xe4, 0xac, 0x15, 0xcb, 0x56, 0xa4, 0x83, 0x20, 0x91, 0xdd, 0x21, 0x4e, 0xa3, 0xb0, 0x74, 0xad, + 0x7d, 0x4b, 0xb9, 0x32, 0x8a, 0xe2, 0x33, 0xc0, 0x3a, 0x66, 0x82, 0x29, 0xf3, 0x56, 0x2b, 0x38, + 0x89, 0x2d, 0xa8, 0x23, 0x4f, 0x17, 0xda, 0xc0, 0x40, 0xa3, 0x98, 0xc7, 0x41, 0x7c, 0x56, 0xf0, + 0xe7, 0xa0, 0xb5, 0x6b, 0x79, 0x3b, 0x2c, 0x23, 0x4c, 0x4a, 0x6a, 0x62, 0xa2, 0x5e, 0x71, 0xc8, + 0xd0, 0xc5, 0xce, 0xaf, 0xa5, 0x08, 0xfe, 0x1d, 0x53, 0x51, 0x43, 0x15, 0x10, 0x27, 0x0a, 0x64, + 0x8e, 0xb5, 0xa0, 0x30, 0x20, 0x3b, 0x14, 0x05, 0x20, 0xa7, 0xae, 0xda, 0x59, 0x06, 0x93, 0x5c, + 0xb2, 0x8e, 0x01, 0x17, 0x4b, 0x35, 0x1b, 0xd4, 0x19, 0x1c, 0x7a, 0x81, 0x93, 0x1a, 0x83, 0x91, + 0x9a, 0x05, 0xba, 0xee, 0x87, 0xd1, 0x24, 0xb2, 0xc0, 0x62, 0x60, 0x52, 0xd7, 0x31, 0x2b, 0xa1, + 0xd3, 0x86, 0x82, 0xd2, 0x2d, 0x3a, 0xe3, 0x42, 0xb0, 0x3d, 0x74, 0x6c, 0xe0, 0x00, 0xa6, 0x06, + 0xd7, 0x08, 0x5c, 0x11, 0x1e, 0x98, 0x5e, 0x0e, 0xeb, 0xc1, 0x54, 0x20, 0x95, 0xba, 0x55, 0x73, + 0x72, 0xd6, 0x4a, 0xe6, 0x96, 0x50, 0x56, 0x3e, 0xfe, 0xb8, 0xce, 0x94, 0xbc, 0xe2, 0x09, 0x9d, + 0xe5, 0xf1, 0xe5, 0x39, 0x26, 0x63, 0xf6, 0x54, 0x46, 0x99, 0x41, 0x65, 0x6e, 0x41, 0x4d, 0x0a, + 0x4a, 0x83, 0xfe, 0x9f, 0xc9, 0x80, 0x99, 0xbf, 0xcd, 0x3e, 0xca, 0xec, 0x0f, 0xeb, 0x1e, 0xe7, + 0xe9, 0x3a, 0x19, 0x74, 0xdc, 0x74, 0x95, 0x5e, 0x61, 0xc9, 0x66, 0xd2, 0xec, 0x05, 0x1b, 0xc0, + 0x69, 0x5b, 0xad, 0x85, 0x76, 0xe6, 0xa7, 0x3f, 0xfd, 0x29, 0x63, 0x46, 0x17, 0xa8, 0x18, 0xc7, + 0xe6, 0x85, 0x0b, 0x86, 0xac, 0xff, 0x8a, 0x86, 0xa6, 0x94, 0x94, 0x39, 0x0a, 0x6f, 0xad, 0x80, + 0xca, 0xcc, 0x65, 0x2b, 0x0b, 0xac, 0x43, 0xb7, 0xa9, 0x08, 0x17, 0xcf, 0x42, 0x87, 0xb3, 0xa8, + 0x54, 0xd1, 0x0a, 0x5c, 0x94, 0x1d, 0xc3, 0x68, 0x9c, 0x9f, 0x08, 0xb6, 0x56, 0x60, 0x1f, 0xb6, + 0x14, 0x44, 0x31, 0x4b, 0xc1, 0x69, 0x3f, 0x53, 0x5e, 0x56, 0x84, 0xae, 0xa5, 0x8c, 0x47, 0xe3, + 0x50, 0x28, 0x48, 0x2e, 0x81, 0x79, 0xa2, 0xde, 0x47, 0x31, 0x50, 0xfc, 0x17, 0xa6, 0xc4, 0x42, + 0xb3, 0x18, 0xba, 0xc4, 0x31, 0xf4, 0x03, 0x85, 0x71, 0x10, 0xe3, 0x98, 0xde, 0x50, 0x3e, 0xb6, + 0xe0, 0xf7, 0x7b, 0x6f, 0x0c, 0xc2, 0xb0, 0x9e, 0x0d, 0x76, 0xc7, 0xd8, 0x4b, 0x1d, 0x0c, 0xa9, + 0x70, 0x2b, 0x08, 0xa3, 0xa4, 0x44, 0xf8, 0x24, 0xc1, 0xe7, 0xd8, 0x51, 0x86, 0xb1, 0xc2, 0xe7, + 0x7d, 0xf8, 0x7c, 0x04, 0x9f, 0xbf, 0xc3, 0x67, 0x73, 0x1b, 0xc3, 0xa4, 0x7d, 0xc6, 0x30, 0x2f, + 0xc1, 0x77, 0xc5, 0x15, 0x86, 0x69, 0xb8, 0x0c, 0xf1, 0x56, 0x3b, 0xc3, 0xd4, 0x42, 0xb9, 0xe9, + 0x12, 0xc3, 0xd4, 0x00, 0x0c, 0x0f, 0x9e, 0x9e, 0xbd, 0xc8, 0x30, 0xb1, 0x50, 0x1e, 0xb8, 0xc0, + 0x30, 0x9f, 0xc1, 0xb7, 0x3a, 0x08, 0x1f, 0x0f, 0x9f, 0xb7, 0xe0, 0xd3, 0x0d, 0xb0, 0x71, 0xd0, + 0xe6, 0xef, 0x9f, 0x2b, 0xf0, 0xff, 0xeb, 0x73, 0x12, 0xc6, 0x49, 0x3c, 0x03, 0x74, 0xc1, 0xc7, + 0x0c, 0x9f, 0xa7, 0xe0, 0x53, 0x0e, 0x9f, 0x4a, 0xf8, 0xbc, 0x06, 0x9f, 0x77, 0xe1, 0xb3, 0x13, + 0x3e, 0x6e, 0xf8, 0xb4, 0xc0, 0xa7, 0x1d, 0x3e, 0x7e, 0xf8, 0x30, 0xff, 0x02, 0xde, 0xe0, 0xa3, + 0x83, 0x4f, 0x22, 0x7c, 0x92, 0xe0, 0x63, 0x86, 0xcf, 0x53, 0xf0, 0x29, 0x87, 0x4f, 0x25, 0x7c, + 0x5e, 0x83, 0xcf, 0xbb, 0xf0, 0xd9, 0x09, 0x1f, 0x37, 0x7c, 0x5a, 0xe0, 0xd3, 0x0e, 0x1f, 0x3f, + 0xde, 0x70, 0x38, 0x0b, 0xfd, 0xe1, 0xa3, 0x3b, 0x8b, 0x57, 0x59, 0x19, 0x26, 0x9f, 0xc6, 0x41, + 0xcc, 0xbd, 0xb7, 0x1e, 0x67, 0xd9, 0x0b, 0xd1, 0xc9, 0xe4, 0xaf, 0x75, 0x30, 0x49, 0x00, 0x74, + 0x94, 0x17, 0x95, 0xf2, 0xe5, 0x0c, 0x6e, 0x5f, 0x15, 0xad, 0x73, 0x32, 0xf8, 0x5f, 0xa6, 0xac, + 0x87, 0x6f, 0xfc, 0xaf, 0x47, 0x60, 0x4a, 0xf3, 0x13, 0x17, 0xe6, 0x3b, 0x8b, 0xd6, 0x3e, 0xe3, + 0xc8, 0x5f, 0x0b, 0x4e, 0x06, 0x22, 0x46, 0xe6, 0x2f, 0x83, 0x15, 0x6b, 0x41, 0x43, 0x9f, 0x06, + 0x7f, 0x46, 0xff, 0xc7, 0x92, 0x20, 0xac, 0xc8, 0x61, 0x67, 0x0e, 0xe0, 0x58, 0xe5, 0x45, 0xce, + 0x7c, 0x47, 0x49, 0x61, 0x61, 0x79, 0x7e, 0x41, 0xe9, 0xba, 0xfc, 0x67, 0x79, 0x28, 0x96, 0x95, + 0xe6, 0x83, 0x2b, 0xc1, 0x75, 0xc8, 0xc1, 0xe0, 0x7f, 0x5c, 0xf2, 0x7f, 0xb4, 0x99, 0xf5, 0x74, + 0x91, 0xd3, 0xa1, 0x9f, 0xcf, 0x4c, 0xfc, 0x11, 0x4d, 0xd7, 0x3a, 0x9e, 0x2b, 0xb4, 0x97, 0x31, + 0xf3, 0x7f, 0x44, 0xd3, 0x92, 0xb2, 0xb2, 0x72, 0xe6, 0x99, 0x1f, 0xd1, 0x70, 0x1d, 0xac, 0xee, + 0x4c, 0xcd, 0x8f, 0xa4, 0x13, 0x96, 0xce, 0xe6, 0x1f, 0xd1, 0x14, 0x8d, 0x8f, 0xf9, 0xf4, 0x47, + 0x34, 0xc4, 0x39, 0xf8, 0xd7, 0x8f, 0x68, 0x87, 0x73, 0xf6, 0xd9, 0x8f, 0x68, 0x67, 0xdf, 0x90, + 0x8f, 0x4d, 0xaf, 0xfc, 0x88, 0xa6, 0x1b, 0xca, 0xd6, 0x39, 0x98, 0xaf, 0x7f, 0x44, 0xc3, 0x02, + 0xc7, 0xda, 0xa2, 0x22, 0xc6, 0xf7, 0x23, 0x5a, 0x3a, 0xd6, 0x16, 0x94, 0x32, 0x7e, 0x68, 0x08, + 0x64, 0xd8, 0xd6, 0x5a, 0x99, 0x81, 0x5b, 0x8f, 0xb7, 0x26, 0x79, 0xdb, 0x2d, 0x48, 0x70, 0x2e, + 0x7f, 0x32, 0x08, 0x01, 0xe7, 0xb3, 0x9e, 0xb9, 0x7f, 0xb0, 0x1c, 0x64, 0x26, 0x51, 0x81, 0xa0, + 0xe7, 0x79, 0xe6, 0xd6, 0x63, 0x08, 0xa1, 0x6b, 0x10, 0x12, 0x44, 0xf8, 0xf1, 0x20, 0x04, 0x95, + 0xb5, 0xb4, 0x8c, 0x19, 0x18, 0xde, 0x0b, 0xe6, 0x50, 0x27, 0xdc, 0x82, 0x14, 0x3a, 0xca, 0x99, + 0xf8, 0xc1, 0x62, 0x19, 0xef, 0x84, 0xf0, 0x74, 0xbd, 0x83, 0x99, 0x21, 0x0c, 0x41, 0xa3, 0x80, + 0x66, 0x0e, 0x82, 0x70, 0xde, 0xe6, 0x0d, 0x69, 0x01, 0xc5, 0xc5, 0x83, 0xc5, 0x20, 0xe1, 0x66, + 0x80, 0x38, 0x0a, 0x9d, 0xf9, 0xb0, 0x2e, 0xe5, 0x5b, 0xcb, 0x99, 0x9f, 0x41, 0x11, 0x1f, 0xd1, + 0x9e, 0x98, 0xad, 0x43, 0x0a, 0x21, 0x5e, 0xde, 0x1e, 0x0a, 0x0b, 0x72, 0x73, 0xfa, 0xf6, 0x76, + 0x40, 0xbd, 0x7f, 0x28, 0x2c, 0x88, 0xfc, 0xfb, 0xa1, 0x30, 0xa4, 0xee, 0xe6, 0x50, 0x00, 0x52, + 0x83, 0xff, 0xa5, 0xcb, 0x2d, 0x40, 0x90, 0x42, 0x0d, 0xc0, 0x86, 0xc4, 0xe4, 0xcc, 0x7f, 0xbb, + 0xc0, 0x2d, 0x94, 0x3b, 0xf3, 0xd7, 0x03, 0xd5, 0xeb, 0xf9, 0xa2, 0x75, 0x4c, 0xe5, 0x4b, 0x60, + 0xf2, 0x8e, 0x7c, 0x1a, 0x79, 0xe6, 0x6f, 0x78, 0xda, 0xce, 0x18, 0x6b, 0x18, 0x06, 0xbe, 0x69, + 0x0b, 0xac, 0x87, 0x22, 0x86, 0xc4, 0x58, 0x66, 0x34, 0xaf, 0xde, 0xc2, 0x86, 0xd5, 0x4f, 0x6f, + 0x51, 0x70, 0x3c, 0x77, 0x07, 0xd8, 0xea, 0x80, 0x98, 0xaf, 0xb0, 0x84, 0x69, 0xbc, 0xa3, 0x66, + 0x6d, 0x59, 0x99, 0x7d, 0x1d, 0xa3, 0xfa, 0xf9, 0xed, 0xf0, 0xa7, 0x8b, 0xca, 0x1c, 0xf9, 0x4a, + 0xf2, 0x3a, 0x13, 0x2a, 0x21, 0x55, 0xca, 0x77, 0x96, 0x51, 0x95, 0x48, 0xfb, 0xb9, 0xe2, 0x94, + 0xa0, 0x08, 0x50, 0xa6, 0x10, 0x8a, 0xeb, 0x8a, 0xd6, 0x17, 0x39, 0x43, 0xf5, 0xdb, 0x01, 0x00, + 0x41, 0x85, 0xb3, 0x8c, 0x2f, 0x62, 0x26, 0xbf, 0x06, 0xec, 0x01, 0xc2, 0x82, 0xa7, 0x1d, 0x80, + 0xdd, 0x59, 0x98, 0x8f, 0x8b, 0x1f, 0x63, 0xfb, 0x05, 0xf0, 0x60, 0xc7, 0xb6, 0x55, 0xf0, 0xb4, + 0x51, 0x79, 0x7c, 0xed, 0x2d, 0x04, 0x96, 0xe6, 0x17, 0x31, 0xcf, 0xfe, 0x52, 0x79, 0x82, 0xfe, + 0x71, 0xbf, 0x52, 0x1e, 0x37, 0x33, 0x31, 0x6f, 0x07, 0xfb, 0xa0, 0xb3, 0x0d, 0x46, 0xd2, 0xcc, + 0x01, 0x04, 0x16, 0x94, 0x42, 0x66, 0x57, 0xfe, 0x6b, 0x14, 0x5b, 0x10, 0x29, 0x7d, 0x76, 0x94, + 0x63, 0x8b, 0x77, 0xe0, 0x19, 0x28, 0x29, 0x2f, 0x07, 0x0f, 0xfb, 0x9f, 0xf4, 0x19, 0x13, 0x5b, + 0x3b, 0x53, 0xf7, 0x6b, 0x4a, 0xe4, 0xda, 0xf2, 0x2d, 0xcc, 0x97, 0xca, 0x63, 0x29, 0x3e, 0xe3, + 0xb5, 0x73, 0x04, 0x6f, 0x28, 0x67, 0x72, 0x82, 0x8f, 0x05, 0x90, 0x55, 0x41, 0xb1, 0xe1, 0x9d, + 0x60, 0x2b, 0x78, 0x8e, 0xfc, 0x0d, 0x7d, 0x2e, 0x29, 0x2c, 0x65, 0x26, 0xfd, 0x06, 0x85, 0x81, + 0x2a, 0xe7, 0x84, 0x70, 0xe0, 0x15, 0x28, 0xd1, 0xec, 0x8c, 0xd9, 0xfc, 0x5b, 0x6c, 0x02, 0x61, + 0xc3, 0xdc, 0x7c, 0x0c, 0x8b, 0x99, 0x92, 0xed, 0xa8, 0x99, 0x76, 0x08, 0x8f, 0xf3, 0x41, 0xe1, + 0x19, 0x71, 0xb0, 0x18, 0x8a, 0x9a, 0x1d, 0x85, 0x8c, 0x77, 0x28, 0x94, 0xe9, 0xdd, 0x8e, 0x82, + 0x75, 0x3c, 0x43, 0x15, 0x25, 0xdf, 0x41, 0xd3, 0x23, 0x07, 0xf3, 0xd9, 0x7f, 0x04, 0xa1, 0x6b, + 0x01, 0x0c, 0x02, 0x2d, 0x2a, 0x5d, 0x57, 0xb8, 0x99, 0x59, 0xfe, 0x81, 0xa2, 0x6a, 0x4f, 0x43, + 0x31, 0x5f, 0x99, 0x48, 0x26, 0x3f, 0x08, 0xa3, 0xe3, 0xcf, 0xfc, 0x03, 0x43, 0x83, 0x95, 0x7c, + 0x8c, 0xc3, 0x99, 0x92, 0x0f, 0x61, 0x3d, 0x77, 0xd0, 0xa9, 0x56, 0xf4, 0xe2, 0xa5, 0x41, 0x00, + 0xd5, 0xa0, 0x9f, 0x63, 0x99, 0x36, 0xbd, 0xe7, 0xcf, 0xf0, 0xb4, 0x16, 0x03, 0x1c, 0xe6, 0x69, + 0x7c, 0xa4, 0x5a, 0x5b, 0x13, 0x7a, 0x42, 0xc3, 0x52, 0x7f, 0x84, 0xe2, 0x2e, 0xc3, 0x1c, 0x59, + 0xc1, 0x8e, 0xef, 0xc6, 0x87, 0x00, 0xb4, 0xf9, 0xa6, 0x21, 0x00, 0x05, 0xd5, 0x2b, 0x00, 0xb1, + 0x16, 0x38, 0xe7, 0xce, 0x19, 0x42, 0xc3, 0xdf, 0x6e, 0xc1, 0x90, 0x62, 0x50, 0x4c, 0x58, 0x16, + 0x37, 0x3b, 0x99, 0xda, 0x1d, 0x21, 0x30, 0x45, 0x9e, 0xf1, 0xf7, 0xa1, 0x45, 0x94, 0xd3, 0xfb, + 0xff, 0x64, 0x98, 0xb5, 0x90, 0xb2, 0x80, 0xa1, 0x95, 0xe0, 0x76, 0x83, 0x3d, 0x1f, 0x82, 0x29, + 0x80, 0xe3, 0x3b, 0xba, 0x4a, 0x4b, 0x4a, 0x84, 0xe3, 0x56, 0x51, 0x21, 0x61, 0x3b, 0x94, 0xa9, + 0x64, 0x87, 0xf7, 0xfa, 0x70, 0xd7, 0x6d, 0x60, 0xab, 0xbd, 0x6c, 0x43, 0x3e, 0xa4, 0xdd, 0xcc, + 0xaa, 0xdd, 0x4a, 0x02, 0xaf, 0xd0, 0xb1, 0x76, 0x6f, 0xb0, 0x44, 0xed, 0xb2, 0x14, 0x37, 0x25, + 0x26, 0xef, 0x0f, 0x82, 0xc0, 0x3e, 0x1d, 0x85, 0xf9, 0x98, 0xd2, 0x82, 0x36, 0xbe, 0x77, 0x10, + 0x26, 0x55, 0x09, 0x06, 0x15, 0x0b, 0xc1, 0xa6, 0x4f, 0x1d, 0x02, 0x9a, 0x95, 0x09, 0xa4, 0xd1, + 0x28, 0x1d, 0x00, 0xdf, 0x30, 0xa2, 0xfd, 0x29, 0xbd, 0x2f, 0x87, 0x4a, 0x0a, 0xb9, 0x87, 0xdd, + 0x43, 0xc6, 0xa3, 0x73, 0x34, 0xcb, 0x33, 0x04, 0xa2, 0x88, 0xf0, 0xf7, 0x1e, 0xd4, 0x46, 0x18, + 0x40, 0x41, 0x11, 0x73, 0x04, 0x46, 0x81, 0x04, 0x31, 0x1f, 0xe2, 0x5b, 0x65, 0x1c, 0x66, 0xc4, + 0x7f, 0x81, 0x37, 0x29, 0xdc, 0x00, 0xc3, 0x05, 0x07, 0xa6, 0xd9, 0x0d, 0x93, 0x02, 0x60, 0x47, + 0x01, 0x6a, 0x22, 0x10, 0x1e, 0x0c, 0xc3, 0x99, 0xd1, 0x47, 0x43, 0x26, 0xa7, 0xf4, 0x60, 0x6e, + 0x1e, 0x55, 0xac, 0x39, 0x58, 0x7c, 0xb0, 0x19, 0x47, 0x2b, 0xba, 0x55, 0x7e, 0xf2, 0xb8, 0x52, + 0x6d, 0x2f, 0xd8, 0x14, 0x02, 0x95, 0x03, 0xa8, 0x7c, 0xc3, 0x06, 0x10, 0x62, 0x61, 0x21, 0xb3, + 0xf7, 0x38, 0x25, 0x78, 0x08, 0x35, 0x2d, 0xb7, 0x43, 0xf2, 0x9d, 0x5b, 0xca, 0x0b, 0x19, 0xf9, + 0xdf, 0x82, 0xe1, 0x11, 0x02, 0x5f, 0x48, 0x1a, 0xfc, 0x9f, 0x02, 0x0f, 0xc8, 0x81, 0x22, 0x7a, + 0x6b, 0x19, 0xf3, 0xd7, 0x13, 0xff, 0x8e, 0x2b, 0x54, 0x8e, 0x96, 0x93, 0x0a, 0x01, 0xa5, 0x85, + 0x9b, 0xf2, 0x15, 0xcb, 0x74, 0x9f, 0x0a, 0x52, 0x5d, 0x08, 0x29, 0x07, 0x63, 0x3e, 0x1d, 0x2c, + 0xa1, 0x73, 0x7e, 0x05, 0x0a, 0x05, 0x73, 0x40, 0x47, 0x69, 0x5c, 0xed, 0x0e, 0x96, 0x94, 0xcd, + 0x01, 0x66, 0x55, 0x2b, 0x12, 0x85, 0x81, 0x3f, 0x53, 0xd9, 0x4a, 0x87, 0x43, 0x87, 0xf1, 0x96, + 0xf2, 0x08, 0x8b, 0x0e, 0xf3, 0x91, 0xf2, 0xb8, 0xa1, 0x0c, 0x5c, 0x66, 0xdd, 0xe0, 0xf3, 0xac, + 0xb5, 0x65, 0xe5, 0xe0, 0x4e, 0xc1, 0x67, 0x6d, 0x2a, 0xb0, 0x43, 0x00, 0x70, 0x62, 0x48, 0x15, + 0x8d, 0x84, 0x4e, 0x05, 0xb1, 0x81, 0x63, 0xf9, 0xf6, 0xd6, 0xe3, 0xac, 0xc2, 0x67, 0xf9, 0x82, + 0x12, 0xc6, 0x3f, 0x08, 0xa0, 0x6d, 0xaf, 0xdf, 0x1a, 0x79, 0xee, 0x1c, 0x67, 0xd9, 0xfc, 0x79, + 0x4c, 0xe0, 0x36, 0xc0, 0xac, 0x72, 0x3d, 0xa3, 0x3a, 0x73, 0x1b, 0x0c, 0x02, 0x8f, 0x42, 0x68, + 0x1b, 0x7b, 0x3b, 0xbc, 0x1c, 0xd6, 0xe5, 0x7f, 0xd7, 0x16, 0x56, 0x3c, 0x8c, 0xb0, 0xed, 0x05, + 0x54, 0x1b, 0x42, 0xc9, 0x4e, 0x7e, 0x22, 0x8d, 0xb9, 0x6f, 0x87, 0xea, 0x69, 0x14, 0x7e, 0x3b, + 0x74, 0x0e, 0x8d, 0xcb, 0x6f, 0x87, 0xce, 0xa5, 0x91, 0xfa, 0xed, 0xd0, 0x79, 0x34, 0x76, 0xbf, + 0x1d, 0xfa, 0x10, 0x8d, 0xe6, 0x6f, 0x87, 0xce, 0xa7, 0xf1, 0xfd, 0xed, 0xd0, 0x05, 0x34, 0xe2, + 0xbf, 0x1d, 0xba, 0x90, 0xe6, 0x00, 0xb7, 0x43, 0x0d, 0x34, 0x2b, 0xb8, 0x83, 0x8b, 0x44, 0x9a, + 0x28, 0xdc, 0x01, 0xd6, 0xd3, 0xdc, 0xe1, 0x0e, 0xf0, 0x1c, 0x9a, 0x4e, 0xdc, 0x01, 0x9e, 0x4b, + 0x33, 0x8c, 0x3b, 0xc0, 0xf3, 0x68, 0xd2, 0x71, 0x07, 0xf8, 0x21, 0x9a, 0x87, 0xdc, 0x01, 0x9e, + 0x4f, 0x53, 0x93, 0x3b, 0xc0, 0x0b, 0x68, 0xb6, 0x72, 0x07, 0x78, 0x21, 0x4d, 0x60, 0xee, 0x00, + 0x1b, 0x68, 0x4e, 0x73, 0xc7, 0xac, 0x24, 0xd2, 0x34, 0xe7, 0x0e, 0xb0, 0x9e, 0x66, 0x3e, 0x77, + 0x80, 0xe7, 0xd0, 0x64, 0xe8, 0x0e, 0xf0, 0x5c, 0x9a, 0x1f, 0xdd, 0x01, 0x9e, 0x47, 0x53, 0xa6, + 0x3b, 0xc0, 0x0f, 0xd1, 0x2c, 0xea, 0x0e, 0xf0, 0x7c, 0x9a, 0x58, 0xdd, 0x01, 0x5e, 0x40, 0x73, + 0xad, 0x3b, 0xc0, 0x0b, 0x69, 0xfa, 0x75, 0x07, 0xd8, 0x40, 0x33, 0xb2, 0x3b, 0xb4, 0x2c, 0x91, + 0x26, 0x69, 0x77, 0x80, 0xf5, 0xcc, 0x2c, 0x00, 0xe7, 0x83, 0x63, 0xde, 0xb8, 0xae, 0x68, 0x2e, + 0xb3, 0xe2, 0x1c, 0x16, 0x40, 0xdf, 0xb1, 0x50, 0xe5, 0xc5, 0x02, 0x1f, 0xac, 0x72, 0x9c, 0xa7, + 0xa5, 0x60, 0x1d, 0x1e, 0xc2, 0x29, 0xff, 0xd3, 0xdf, 0xff, 0x03, 0x02, 0xe9, 0xe6, 0x52, 0x10, + 0x73, 0x00, 0x00, +}; + +pub fn install(device: []u8, force_mbr: bool, partition_number: ?u32) !void { + if (@sizeOf(usize) != @sizeOf(u64)) return print_error_and_exit(InstallerError.not_64_bit); + + // Point to the second block where the GPT header might be + var do_gpt = false; + var gpt_header: *GPT.Header = undefined; + const lb_guesses = [_]u64{ 512, 4096 }; + var lb_size: u64 = 0; + + for (lb_guesses) |guess| { + gpt_header = @as(*GPT.Header, @ptrCast(@alignCast(&device[guess]))); + if (gpt_header.signature == gpt_header_signature) { + lb_size = guess; + do_gpt = !force_mbr; + if (force_mbr) { + gpt_header.* = std.mem.zeroes(GPT.Header); + } + break; + } + } + + const secondary_GPT_header: *GPT.Header = @ptrCast(@alignCast(&device[lb_size * gpt_header.alternate_LBA])); + if (do_gpt) { + //print("Installing to GPT. Logical block size of {}\nSecondary header at LBA 0x{x}\n", .{lb_size, gpt_header.alternate_LBA}); + if (secondary_GPT_header.signature != gpt_header_signature) { + return print_error_and_exit(InstallerError.secondary_GPT_header_invalid); + } + + //stdout_write("Secondary header valid\n"); + } else { + var mbr = true; + + // Do MBR sanity checks + + { + const hint = @as(*u8, @ptrCast(&device[446])); + if (hint.* != 0 and hint.* != 0x80) { + if (!force_mbr) mbr = false else hint.* = if (hint.* & 0x80 != 0) 0x80 else 0; + } + } + + { + const hint = @as(*u8, @ptrCast(&device[462])); + if (hint.* != 0 and hint.* != 0x80) { + if (!force_mbr) mbr = false else hint.* = if (hint.* & 0x80 != 0) 0x80 else 0; + } + } + + { + const hint = @as(*u8, @ptrCast(&device[478])); + if (hint.* != 0 and hint.* != 0x80) { + if (!force_mbr) mbr = false else hint.* = if (hint.* & 0x80 != 0) 0x80 else 0; + } + } + + { + const hint = @as(*u8, @ptrCast(&device[494])); + if (hint.* != 0 and hint.* != 0x80) { + if (!force_mbr) mbr = false else hint.* = if (hint.* & 0x80 != 0) 0x80 else 0; + } + } + + { + const hint = @as(*[8]u8, @ptrCast(&device[4])); + if (std.mem.eql(u8, hint, "_ECH_FS_")) { + if (!force_mbr) mbr = false else hint.* = std.mem.zeroes([8]u8); + } + } + + { + const hint = @as(*[4]u8, @ptrCast(&device[3])); + if (std.mem.eql(u8, hint, "NTFS")) { + if (!force_mbr) mbr = false else hint.* = std.mem.zeroes([4]u8); + } + } + + { + const hint = @as(*[5]u8, @ptrCast(&device[54])); + if (std.mem.eql(u8, hint[0..3], "FAT")) { + if (!force_mbr) mbr = false else hint.* = std.mem.zeroes([5]u8); + } + } + + { + const hint = @as(*[5]u8, @ptrCast(&device[82])); + if (std.mem.eql(u8, hint[0..3], "FAT")) { + if (!force_mbr) mbr = false else hint.* = std.mem.zeroes([5]u8); + } + } + + { + const hint = @as(*[5]u8, @ptrCast(&device[3])); + if (std.mem.eql(u8, hint, "FAT32")) { + if (!force_mbr) mbr = false else hint.* = std.mem.zeroes([5]u8); + } + } + + { + const hint = @as(*align(1) u16, @ptrCast(&device[1080])); + if (hint.* == 0xef53) { + if (!force_mbr) mbr = false else hint.* = 0; + } + } + + if (!mbr) return print_error_and_exit(InstallerError.invalid_partition_table); + } + + const stage2_size = hdd.len - 512; + const stage2_sections = div_roundup(stage2_size, 512); + var stage2_size_a = @as(u16, @intCast((stage2_sections / 2) * 512 + @as(u64, if (stage2_sections % 2 != 0) 512 else 0))); + const stage2_size_b = @as(u16, @intCast((stage2_sections / 2) * 512)); + + var stage2_loc_a: u64 = 512; + var stage2_loc_b = stage2_loc_a + stage2_size_a; + + if (do_gpt) { + if (partition_number != null) { + return error.TODO; + } else { + const partition_entry_count = gpt_header.partition_entry_count; + if (partition_entry_count == 0) return error.TODO; + const partition_entry_base_address = @intFromPtr(device.ptr) + (gpt_header.partition_entry_LBA * lb_size); + var partition_entry_address = partition_entry_base_address; + + var max_partition_entry_used: u64 = 0; + + var partition_entry_i: u64 = 0; + while (partition_entry_i < partition_entry_count) : (partition_entry_i += 1) { + const partition_entry = @as(*GPT.Entry, @ptrFromInt(partition_entry_address)); + defer partition_entry_address += gpt_header.partition_entry_size; + + if (partition_entry.unique_partition_guid0 != 0 or partition_entry.unique_partition_guid1 != 0) { + if (partition_entry_i > max_partition_entry_used) max_partition_entry_used = partition_entry_i; + } + } + + stage2_loc_a = (gpt_header.partition_entry_LBA + 32) * lb_size; + stage2_loc_a -= stage2_size_a; + stage2_loc_a &= ~(lb_size - 1); + + stage2_loc_b = (secondary_GPT_header.partition_entry_LBA + 32) * lb_size; + stage2_loc_b -= stage2_size_b; + stage2_loc_b &= ~(lb_size - 1); + + const partition_entry_per_lb_count = lb_size / gpt_header.partition_entry_size; + const new_partition_array_lba_size = stage2_loc_a / lb_size - gpt_header.partition_entry_LBA; + const new_partition_entry_count = new_partition_array_lba_size * partition_entry_per_lb_count; + + if (new_partition_entry_count <= max_partition_entry_used) { + return error.TODO; + } + + //print("New maximum count of partition entries: {}\n", .{new_partition_entry_count}); + var partition_i: usize = max_partition_entry_used + 1; + while (partition_i < new_partition_entry_count) : (partition_i += 1) { + const entry_offset = partition_i * gpt_header.partition_entry_size; + const primary_entry_base = gpt_header.partition_entry_LBA * lb_size + entry_offset; + const secondary_entry_base = secondary_GPT_header.partition_entry_LBA * lb_size + entry_offset; + @memset(device[primary_entry_base .. primary_entry_base + gpt_header.partition_entry_size], 0); + @memset(device[secondary_entry_base .. secondary_entry_base + gpt_header.partition_entry_size], 0); + } + + assert(gpt_header.partition_entry_count * @sizeOf(GPT.Entry) == gpt_header.partition_entry_count * gpt_header.partition_entry_size); + assert(secondary_GPT_header.partition_entry_count * @sizeOf(GPT.Entry) == secondary_GPT_header.partition_entry_count * secondary_GPT_header.partition_entry_size); + + gpt_header.partition_entry_array_CRC32 = crc32(@as([*]u8, @ptrFromInt(partition_entry_base_address))[0 .. new_partition_entry_count * gpt_header.partition_entry_size]); + gpt_header.partition_entry_count = @as(u32, @intCast(new_partition_entry_count)); + gpt_header.CRC32 = 0; + gpt_header.CRC32 = crc32(std.mem.asBytes(gpt_header)); + + secondary_GPT_header.partition_entry_array_CRC32 = gpt_header.partition_entry_array_CRC32; + secondary_GPT_header.partition_entry_count = @as(u32, @intCast(new_partition_entry_count)); + secondary_GPT_header.CRC32 = 0; + secondary_GPT_header.CRC32 = crc32(std.mem.asBytes(secondary_GPT_header)); + } + } else { + //stdout_write("Installing to MBR\n"); + } + + const original_timestamp = @as(*[6]u8, @ptrCast(&device[218])).*; + const original_partition_table = @as(*[70]u8, @ptrCast(&device[440])).*; + @memcpy(device[0..512], hdd[0..512]); + + { + const dst = device[stage2_loc_a .. stage2_loc_a + stage2_size_a]; + const src = hdd[512 .. 512 + stage2_size_a]; + @memcpy(dst, src); + } + + { + const size_left = stage2_size - stage2_size_a; + const dst = device[stage2_loc_b .. stage2_loc_b + size_left]; + const src = hdd[512 + stage2_size_a .. 512 + stage2_size_a + size_left]; + @memcpy(dst, src); + } + + @as(*align(1) u16, @ptrCast(&device[0x1a4 + 0])).* = stage2_size_a; + @as(*align(1) u16, @ptrCast(&device[0x1a4 + 2])).* = stage2_size_b; + @as(*align(1) u64, @ptrCast(&device[0x1a4 + 4])).* = stage2_loc_a; + @as(*align(1) u64, @ptrCast(&device[0x1a4 + 12])).* = stage2_loc_b; + + @as(*[6]u8, @ptrCast(&device[218])).* = original_timestamp; + @as(*[70]u8, @ptrCast(&device[440])).* = original_partition_table; +} diff --git a/src/bootloader/limine/main.zig b/src/bootloader/limine/main.zig new file mode 100644 index 0000000..88536a1 --- /dev/null +++ b/src/bootloader/limine/main.zig @@ -0,0 +1,332 @@ +const lib = @import("lib"); +const assert = lib.assert; +const log = lib.log.scoped(.LIMINE); + +const bootloader = @import("bootloader"); + +const limine = @import("limine"); + +const privileged = @import("privileged"); +const ACPI = privileged.ACPI; +const Mapping = privileged.Mapping; +const PageAllocator = privileged.PageAllocator; +const PhysicalAddress = lib.PhysicalAddress; +const PhysicalMemoryRegion = lib.PhysicalMemoryRegion; +const VirtualAddress = lib.VirtualAddress; +const stopCPU = privileged.arch.stopCPU; +const paging = privileged.arch.x86_64.paging; + +const writer = privileged.E9Writer{ .context = {} }; + +const Request = extern struct { + information: limine.BootloaderInfo.Request = .{ .revision = 0 }, + hhdm: limine.HHDM.Request = .{ .revision = 0 }, + framebuffer: limine.Framebuffer.Request = .{ .revision = 0 }, + smp: limine.SMPInfoRequest = .{ .revision = 0, .flags = .{ .x2apic = false } }, + memory_map: limine.MemoryMap.Request = .{ .revision = 0 }, + modules: limine.Module.Request = .{ .revision = 0 }, + rsdp: limine.RSDP.Request = .{ .revision = 0 }, + smbios: limine.SMBIOS.Request = .{ .revision = 0 }, + efi_system_table: limine.EFISystemTable.Request = .{ .revision = 0 }, + kernel_address: limine.KernelAddress.Request = .{ .revision = 0 }, +}; + +var request = Request{}; + +comptime { + @export(request, .{ .linkage = .Strong, .name = "request" }); +} + +pub fn panic(message: []const u8, _: ?*lib.StackTrace, _: ?usize) noreturn { + privileged.arch.disableInterrupts(); + + writer.writeAll("[PANIC] ") catch {}; + writer.writeAll(message) catch {}; + writer.writeByte('\n') catch {}; + + privileged.shutdown(.failure); +} + +pub const std_options = struct { + pub const log_level = lib.std.log.Level.debug; + + pub fn logFn(comptime level: lib.std.log.Level, comptime scope: @TypeOf(.EnumLiteral), comptime format: []const u8, args: anytype) void { + _ = level; + // _ = level; + writer.writeByte('[') catch stopCPU(); + writer.writeAll(@tagName(scope)) catch stopCPU(); + writer.writeAll("] ") catch stopCPU(); + lib.format(writer, format, args) catch stopCPU(); + writer.writeByte('\n') catch stopCPU(); + } +}; + +const Filesystem = struct { + modules: []const limine.File, + + pub fn deinitialize(filesystem: *Filesystem) !void { + _ = filesystem; + } + + pub fn readFile(filesystem: *Filesystem, file_path: []const u8, file_buffer: []u8) ![]const u8 { + const module = try filesystem.getModule(file_path); + assert(file_buffer.len >= module.size); + @memcpy(file_buffer[0..module.size], module.getContent()); + return file_buffer; + } + + pub fn sneakFile(filesystem: *Filesystem, file_path: []const u8, size: usize) ![]const u8 { + _ = size; + const file = try filesystem.getModule(file_path); + return file.getContent(); + } + + fn getModule(filesystem: *Filesystem, file_path: []const u8) !*const limine.File { + for (filesystem.modules) |*module| { + const path = module.path[0..lib.length(module.path)]; + if (lib.equal(u8, file_path, path)) { + return module; + } + } + + return Error.file_not_found; + } + + pub fn getFileSize(filesystem: *Filesystem, file_path: []const u8) !u32 { + const file = try filesystem.getModule(file_path); + return @as(u32, @intCast(file.size)); + } + + pub fn getSectorSize(filesystem: *Filesystem) u16 { + _ = filesystem; + return lib.default_sector_size; + } +}; + +const MemoryMap = struct { + entries: []const limine.MemoryMap.Entry, + index: usize = 0, + + pub fn getEntryCount(memory_map: *const MemoryMap) u32 { + return @as(u32, @intCast(memory_map.entries.len)); + } + + pub fn next(memory_map: *MemoryMap) !?bootloader.MemoryMapEntry { + if (memory_map.index < memory_map.entries.len) { + const entry = memory_map.entries[memory_map.index]; + memory_map.index += 1; + + return .{ + .region = entry.region, + .type = switch (entry.type) { + .usable => .usable, + .framebuffer, .kernel_and_modules, .bootloader_reclaimable, .reserved, .acpi_reclaimable, .acpi_nvs => .reserved, + .bad_memory => @panic("Bad memory"), + }, + }; + } + + return null; + } +}; + +const Initialization = struct { + framebuffer: bootloader.Framebuffer, + memory_map: MemoryMap, + filesystem: Filesystem, + architecture: switch (lib.cpu.arch) { + .x86_64 => struct { + rsdp: *ACPI.RSDP.Descriptor1, + }, + else => @compileError("Architecture not supported"), + }, + + early_initialized: bool = false, + framebuffer_initialized: bool = false, + memory_map_initialized: bool = false, + filesystem_initialized: bool = false, + + pub fn ensureLoaderIsMapped(init: *Initialization, minimal_paging: privileged.arch.paging.Specific, page_allocator: PageAllocator, bootloader_information: *bootloader.Information) !void { + const Section = enum { + text, + rodata, + data, + }; + const physical_offset = request.kernel_address.response.?.physical_address; + const virtual_offset = request.kernel_address.response.?.virtual_address; + + inline for (comptime lib.enumValues(Section)) |section| { + const section_name = @tagName(section); + const section_start = @intFromPtr(@extern(*const u8, .{ .name = section_name ++ "_section_start" })); + const section_end = @intFromPtr(@extern(*const u8, .{ .name = section_name ++ "_section_end" })); + + const offset = section_start - virtual_offset; + const physical_address = PhysicalAddress.new(physical_offset + offset); + const virtual_address = VirtualAddress.new(section_start); + const size = section_end - section_start; + + log.debug("Trying to map {s}: 0x{x} -> 0x{x} for 0x{x} bytes...", .{ section_name, virtual_address.value(), physical_address.value(), size }); + + if (section == .text) { + const address = @intFromPtr(&ensureLoaderIsMapped); + assert(address >= section_start and address <= section_end); + } + + try minimal_paging.map(physical_address, virtual_address, size, switch (section) { + .text => .{ .write = false, .execute = true }, + .rodata => .{ .write = false, .execute = false }, + .data => .{ .write = true, .execute = false }, + }, page_allocator); + log.debug("Mapped {s}...", .{section_name}); + } + + _ = init; + _ = bootloader_information; + } + + pub fn ensureStackIsMapped(init: *Initialization, minimal_paging: paging.Specific, page_allocator: PageAllocator) !void { + _ = init; + const rsp = switch (lib.cpu.arch) { + .x86_64 => asm volatile ( + \\mov %rsp, %[result] + : [result] "=r" (-> u64), + ), + .aarch64 => @panic("TODO ensureStackIsMapped"), + else => @compileError("Architecture not supported"), + }; + + const memory_map = request.memory_map.response.?; + const memory_map_entries = memory_map.entries.*[0..memory_map.entry_count]; + for (memory_map_entries) |entry| { + if (entry.type == .bootloader_reclaimable) { + if (entry.region.address.toHigherHalfVirtualAddress().value() < rsp and entry.region.address.offset(entry.region.size).toHigherHalfVirtualAddress().value() > rsp) { + try minimal_paging.map(entry.region.address, entry.region.address.toHigherHalfVirtualAddress(), entry.region.size, .{ .write = true, .execute = false }, page_allocator); + break; + } + } + } else @panic("Can't find memory map region for RSP"); + } + + pub fn getCPUCount(init: *Initialization) !u32 { + return switch (lib.cpu.arch) { + .x86_64 => blk: { + const rsdp = init.architecture.rsdp; + const madt_header = try rsdp.findTable(.APIC); + const madt = @as(*align(1) const ACPI.MADT, @ptrCast(madt_header)); + const cpu_count = madt.getCPUCount(); + break :blk cpu_count; + }, + else => @compileError("Architecture not supported"), + }; + } + + pub fn getRSDPAddress(init: *Initialization) usize { + return @intFromPtr(init.architecture.rsdp); + } + + pub fn deinitializeMemoryMap(init: *Initialization) !void { + init.memory_map.index = 0; + } + + fn initialize(init: *Initialization) !void { + init.* = .{ + .framebuffer = blk: { + if (request.framebuffer.response) |response| { + const framebuffers = response.framebuffers.*; + if (response.framebuffer_count > 0) { + const framebuffer = framebuffers[0]; + break :blk .{ + .address = framebuffer.address, + .pitch = @as(u32, @intCast(framebuffer.pitch)), + .width = @as(u32, @intCast(framebuffer.width)), + .height = @as(u32, @intCast(framebuffer.height)), + .bpp = framebuffer.bpp, + .red_mask = .{ + .shift = framebuffer.red_mask_shift, + .size = framebuffer.red_mask_size, + }, + .green_mask = .{ + .shift = framebuffer.green_mask_shift, + .size = framebuffer.green_mask_size, + }, + .blue_mask = .{ + .shift = framebuffer.blue_mask_shift, + .size = framebuffer.blue_mask_size, + }, + .memory_model = framebuffer.memory_model, + }; + } + } + + return Error.framebuffer_not_found; + }, + .memory_map = blk: { + if (request.memory_map.response) |response| { + if (response.entry_count > 0) { + const entries = response.entries.*[0..response.entry_count]; + break :blk .{ + .entries = entries, + }; + } + } + + return Error.memory_map_not_found; + }, + .filesystem = blk: { + if (request.modules.response) |response| { + if (response.module_count > 0) { + const modules = response.modules.*[0..response.module_count]; + break :blk .{ + .modules = modules, + }; + } + } + return Error.filesystem_not_found; + }, + .architecture = switch (lib.cpu.arch) { + .x86_64 => .{ + .rsdp = blk: { + if (request.rsdp.response) |response| { + break :blk @as(?*ACPI.RSDP.Descriptor1, @ptrFromInt(response.address)) orelse return Error.rsdp_not_found; + } + + return Error.rsdp_not_found; + }, + }, + else => @compileError("Architecture not supported"), + }, + }; + + init.early_initialized = true; + init.framebuffer_initialized = true; + init.filesystem_initialized = true; + init.memory_map_initialized = true; + } +}; + +const Error = error{ + not_implemented, + file_not_found, + framebuffer_not_found, + memory_map_not_found, + filesystem_not_found, + rsdp_not_found, + protocol_not_found, +}; + +var initialization: Initialization = undefined; + +export fn _start() callconv(.C) noreturn { + main() catch |err| @panic(@errorName(err)); +} + +pub fn main() !noreturn { + log.debug("Hello Limine!", .{}); + const limine_protocol: bootloader.Protocol = if (request.efi_system_table.response != null) .uefi else if (request.smbios.response != null) .bios else return Error.protocol_not_found; + + try initialization.initialize(); + + switch (limine_protocol) { + inline else => |protocol| try bootloader.Information.initialize(&initialization, .limine, protocol), + } +} diff --git a/src/bootloader/limine/test.zig b/src/bootloader/limine/test.zig new file mode 100644 index 0000000..e69de29 diff --git a/src/bootloader/rise/bios/linker_script.ld b/src/bootloader/rise/bios/linker_script.ld new file mode 100644 index 0000000..c0837c7 --- /dev/null +++ b/src/bootloader/rise/bios/linker_script.ld @@ -0,0 +1,27 @@ +PHDRS { + none PT_NULL FLAGS(0); + text PT_LOAD FLAGS((1 << 2) | (1 << 0) /* Readable | Executable */); + rodata PT_LOAD FLAGS((1 << 2) /* Readable */); + data PT_LOAD FLAGS((1 << 2) | (1 << 1) /* Readable | Writeable */); +} + +SECTIONS { + . = 0x1000; + loader_start = .; + .text . : { + *(.smp_trampoline*) + *(.realmode*) + *(.text*) + }:text + . = ALIGN(0x10); + .rodata . : { + *(.rodata*) + }:rodata + . = ALIGN(0x10); + .data . : { + *(.data*) + *(.bss*) + }:data + . = ALIGN(0x10); + loader_end = .; +} diff --git a/src/bootloader/rise/bios/main.zig b/src/bootloader/rise/bios/main.zig new file mode 100644 index 0000000..5202703 --- /dev/null +++ b/src/bootloader/rise/bios/main.zig @@ -0,0 +1,275 @@ +const lib = @import("lib"); +const Allocator = lib.Allocator; +const assert = lib.assert; +const log = lib.log; +const privileged = @import("privileged"); +const ACPI = privileged.ACPI; +const MemoryManager = privileged.MemoryManager; +const PhysicalHeap = privileged.PhyicalHeap; +const writer = privileged.writer; + +const stopCPU = privileged.arch.stopCPU; +const GDT = privileged.arch.x86_64.GDT; +const Mapping = privileged.Mapping; +const PageAllocator = privileged.PageAllocator; +const PhysicalAddress = lib.PhysicalAddress; +const VirtualAddress = lib.VirtualAddress; +const PhysicalMemoryRegion = lib.PhysicalMemoryRegion; +const VirtualMemoryRegion = lib.VirtualMemoryRegion; + +const bootloader = @import("bootloader"); +const bios = @import("bios"); + +extern const loader_start: u8; +extern const loader_end: u8; + +const FATAllocator = extern struct { + buffer: [0x2000]u8 = undefined, + allocated: usize = 0, + allocator: Allocator = .{ + .callbacks = .{ + .allocate = allocate, + }, + }, + + pub fn allocate(allocator: *Allocator, size: u64, alignment: u64) Allocator.Allocate.Error!Allocator.Allocate.Result { + const fat = @fieldParentPtr(FATAllocator, "allocator", allocator); + const aligned_allocated = lib.alignForward(usize, fat.allocated, @as(usize, @intCast(alignment))); + if (aligned_allocated + size > fat.buffer.len) @panic("no alloc"); + fat.allocated = aligned_allocated; + const result = Allocator.Allocate.Result{ + .address = @intFromPtr(&fat.buffer) + fat.allocated, + .size = size, + }; + fat.allocated += @as(usize, @intCast(size)); + return result; + } +}; + +pub const std_options = struct { + pub const log_level = lib.std.log.Level.debug; + + pub fn logFn(comptime level: lib.std.log.Level, comptime scope: @TypeOf(.EnumLiteral), comptime format: []const u8, args: anytype) void { + _ = args; + _ = format; + _ = scope; + _ = level; + // _ = level; + // writer.writeByte('[') catch stopCPU(); + // writer.writeAll(@tagName(scope)) catch stopCPU(); + // writer.writeAll("] ") catch stopCPU(); + // lib.format(writer, format, args) catch stopCPU(); + // writer.writeByte('\n') catch stopCPU(); + } +}; + +pub fn panic(message: []const u8, _: ?*lib.StackTrace, _: ?usize) noreturn { + privileged.arch.disableInterrupts(); + writer.writeAll("[PANIC] ") catch stopCPU(); + writer.writeAll(message) catch stopCPU(); + writer.writeByte('\n') catch stopCPU(); + + privileged.shutdown(.failure); +} + +const Filesystem = extern struct { + fat_allocator: FATAllocator = .{}, + fat_cache: lib.Filesystem.FAT32.Cache, + disk: bios.Disk = .{}, + cache_index: usize = 0, + + pub fn deinitialize(filesystem: *Filesystem) !void { + filesystem.fat_allocator.allocated = filesystem.cache_index; + } + + pub fn readFile(filesystem: *Filesystem, file_path: []const u8, file_buffer: []u8) ![]const u8 { + log.debug("File {s} read started", .{file_path}); + assert(filesystem.fat_allocator.allocated <= filesystem.fat_allocator.buffer.len); + const file = try filesystem.fat_cache.readFileToBuffer(file_path, file_buffer); + log.debug("File read succeeded", .{}); + return file; + } + + pub fn sneakFile(filesystem: *Filesystem, file_path: []const u8, size: usize) ![]const u8 { + log.debug("File {s} read started", .{file_path}); + const file = try filesystem.fat_cache.readFileToCache(file_path, size); + log.debug("File read succeeded", .{}); + return file; + } + + pub fn getFileSize(filesystem: *Filesystem, file_path: []const u8) !u32 { + const file_size = try filesystem.fat_cache.getFileSize(file_path); + filesystem.fat_allocator.allocated = filesystem.cache_index; + return file_size; + } + + pub fn getSectorSize(filesystem: *Filesystem) u16 { + return filesystem.disk.disk.sector_size; + } +}; + +const MemoryMap = extern struct { + iterator: bios.E820Iterator, + entry_count: u32, + + pub fn getEntryCount(memory_map: *const MemoryMap) u32 { + return memory_map.entry_count; + } + + pub fn next(memory_map: *MemoryMap) !?bootloader.MemoryMapEntry { + if (memory_map.iterator.next()) |bios_entry| { + return .{ + .region = bios_entry.toPhysicalMemoryRegion(), + .type = switch (bios_entry.type) { + .usable => if (bios_entry.isUsable()) .usable else .reserved, + .bad_memory => .bad_memory, + else => .reserved, + }, + }; + } + + return null; + } +}; + +const Initialization = struct { + filesystem: Filesystem, + memory_map: MemoryMap, + framebuffer: bootloader.Framebuffer, + architecture: switch (lib.cpu.arch) { + .x86, .x86_64 => struct { + rsdp: u32, + }, + else => @compileError("Architecture not supported"), + }, + early_initialized: bool = false, + framebuffer_initialized: bool = false, + memory_map_initialized: bool = false, + filesystem_initialized: bool = false, + + pub fn getCPUCount(init: *Initialization) !u32 { + return switch (lib.cpu.arch) { + .x86, .x86_64 => blk: { + const rsdp = @as(*ACPI.RSDP.Descriptor1, @ptrFromInt(init.architecture.rsdp)); + const madt_header = try rsdp.findTable(.APIC); + const madt = @as(*align(1) const ACPI.MADT, @ptrCast(madt_header)); + break :blk madt.getCPUCount(); + }, + else => @compileError("Architecture not supported"), + }; + } + + pub fn getRSDPAddress(init: *Initialization) u32 { + return init.architecture.rsdp; + } + + pub fn deinitializeMemoryMap(init: *Initialization) !void { + init.memory_map.iterator = bios.E820Iterator{}; + } + + pub fn ensureLoaderIsMapped(init: *Initialization, paging: privileged.arch.paging.Specific, page_allocator: PageAllocator, bootloader_information: *bootloader.Information) !void { + _ = init; + _ = bootloader_information; + const loader_physical_start = PhysicalAddress.new(lib.alignBackward(usize, @intFromPtr(&loader_start), lib.arch.valid_page_sizes[0])); + const loader_size = lib.alignForward(u64, @intFromPtr(&loader_end) - @intFromPtr(&loader_start) + @intFromPtr(&loader_start) - loader_physical_start.value(), lib.arch.valid_page_sizes[0]); + // Not caring about safety here + try paging.map(loader_physical_start, loader_physical_start.toIdentityMappedVirtualAddress(), lib.alignForward(u64, loader_size, lib.arch.valid_page_sizes[0]), .{ .write = true, .execute = true }, page_allocator); + } + + pub fn ensureStackIsMapped(init: *Initialization, paging: privileged.arch.paging.Specific, page_allocator: PageAllocator) !void { + _ = init; + const loader_stack_size = bios.stack_size; + const loader_stack = PhysicalAddress.new(lib.alignForward(u32, bios.stack_top, lib.arch.valid_page_sizes[0]) - loader_stack_size); + try paging.map(loader_stack, loader_stack.toIdentityMappedVirtualAddress(), loader_stack_size, .{ .write = true, .execute = false }, page_allocator); + } + + pub fn initialize(init: *Initialization) !void { + // assert(!init.filesystem.initialized); + // defer init.filesystem.initialized = true; + init.* = .{ + .filesystem = .{ + .fat_cache = undefined, + }, + .memory_map = .{ + .iterator = .{}, + .entry_count = bios.getMemoryMapEntryCount(), + }, + .architecture = switch (lib.cpu.arch) { + .x86, .x86_64 => .{ + .rsdp = @intFromPtr(try bios.findRSDP()), + }, + else => @compileError("Architecture not supported"), + }, + .framebuffer = blk: { + var vbe_info: bios.VBE.Information = undefined; + + const edid_info = bios.VBE.getEDIDInfo() catch @panic("No EDID"); + const edid_width = edid_info.getWidth(); + const edid_height = edid_info.getHeight(); + const edid_bpp = 32; + const preferred_resolution = if (edid_width != 0 and edid_height != 0) .{ .x = edid_width, .y = edid_height } else @panic("No EDID"); + _ = preferred_resolution; + bios.VBE.getControllerInformation(&vbe_info) catch @panic("No VBE information"); + + if (!lib.equal(u8, &vbe_info.signature, "VESA")) { + @panic("VESA signature"); + } + + if (vbe_info.version_major != 3 and vbe_info.version_minor != 0) { + @panic("VESA version"); + } + + const edid_video_mode = vbe_info.getVideoMode(bios.VBE.Mode.defaultIsValid, edid_width, edid_height, edid_bpp) orelse @panic("No video mode"); + const framebuffer_region = PhysicalMemoryRegion.fromRaw(.{ + .raw_address = edid_video_mode.framebuffer_address, + .size = edid_video_mode.linear_bytes_per_scanline * edid_video_mode.resolution_y, + }); + + const framebuffer = .{ + .address = framebuffer_region.address.value(), + .pitch = edid_video_mode.linear_bytes_per_scanline, + .width = edid_video_mode.resolution_x, + .height = edid_video_mode.resolution_y, + .bpp = edid_video_mode.bpp, + .red_mask = .{ + .shift = edid_video_mode.linear_red_mask_shift, + .size = edid_video_mode.linear_red_mask_size, + }, + .green_mask = .{ + .shift = edid_video_mode.linear_green_mask_shift, + .size = edid_video_mode.linear_green_mask_size, + }, + .blue_mask = .{ + .shift = edid_video_mode.linear_blue_mask_shift, + .size = edid_video_mode.linear_blue_mask_size, + }, + .memory_model = 0x06, + }; + + break :blk framebuffer; + }, + }; + + const gpt_cache = try lib.PartitionTable.GPT.Partition.Cache.fromPartitionIndex(&init.filesystem.disk.disk, 0, &init.filesystem.fat_allocator.allocator); + init.filesystem.fat_cache = try lib.Filesystem.FAT32.Cache.fromGPTPartitionCache(&init.filesystem.fat_allocator.allocator, gpt_cache); + init.filesystem.cache_index = init.filesystem.fat_allocator.allocated; + try init.deinitializeMemoryMap(); + + init.memory_map_initialized = true; + init.filesystem_initialized = true; + init.framebuffer_initialized = true; + + init.early_initialized = true; + } +}; + +var initialization: Initialization = undefined; + +export fn _start() callconv(.C) noreturn { + bios.A20Enable() catch @panic("A20 is not enabled"); + + initialization.initialize() catch |err| @panic(@errorName(err)); + bootloader.Information.initialize(&initialization, .rise, .bios) catch |err| { + @panic(@errorName(err)); + }; +} diff --git a/src/bootloader/rise/bios/test.zig b/src/bootloader/rise/bios/test.zig new file mode 100644 index 0000000..e69de29 diff --git a/src/bootloader/rise/bios/unreal_mode.S b/src/bootloader/rise/bios/unreal_mode.S new file mode 100644 index 0000000..5bebc1c --- /dev/null +++ b/src/bootloader/rise/bios/unreal_mode.S @@ -0,0 +1,113 @@ +.code32 +.align 0x10 +.global interrupt +interrupt: +movb 4(%esp), %al +movb %al, (.interrupt_number) + +movl 8(%esp), %eax +movl %eax, (.out_registers) + +movl 12(%esp), %eax +movl %eax, (.in_registers) + +sgdt [.protected_mode_gdt] +sidt [.protected_mode_idt] +lidt [.real_mode_idt] + +push %ebx +push %esi +push %edi +push %ebp + +jmp $0x08, $.bits16 + +.code16 +.bits16: +mov $0x10, %ax +mov %ax, %ds +mov %ax, %es +mov %ax, %fs +mov %ax, %gs +mov %ax, %ss +mov %cr0, %eax +and $0xfe, %al +mov %eax, %cr0 +jmp $0x00, $.cs_zero +.cs_zero: +xor %ax, %ax +mov %ax, %ss +mov %esp, %ss:(.esp) +mov %ss:(.in_registers), %esp +pop %gs +pop %fs +pop %es +pop %ds +popfd +pop %ebp +pop %edi +pop %esi +pop %edx +pop %ecx +pop %ebx +pop %eax +mov %ss:(.esp), %esp +sti +.byte 0xcd +.interrupt_number: .byte 0 +cli + +mov %esp, %ss:(.esp) +mov %ss:(.out_registers), %esp +lea 0x28(%esp), %esp +push %eax +push %ebx +push %ecx +push %edx +push %esi +push %ebp +pushfd +push %ds +push %es +push %fs +push %gs +mov %ss:(.esp), %esp + +lgdtl %ss:(.protected_mode_gdt) +lidtl %ss:(.protected_mode_idt) + +mov %cr0, %eax +or $0x1, %al +mov %eax, %cr0 +jmp $0x18, $.bits32 + +err: +cli +hlt + +.bits32: +.code32 +mov $0x20, %eax +mov %eax, %ds +mov %eax, %es +mov %eax, %fs +mov %eax, %gs +mov %eax, %ss + +pop %ebp +pop %edi +pop %esi +pop %ebx + + +ret + +.align 0x10 +.esp: .long 0 +.out_registers: .long 0 +.in_registers: .long 0 +.protected_mode_gdt: .quad 0 +.protected_mode_idt: .quad 0 +.real_mode_idt: + .word 0x3ff + .long 0 diff --git a/src/bootloader/rise/uefi/main.zig b/src/bootloader/rise/uefi/main.zig new file mode 100644 index 0000000..eec4563 --- /dev/null +++ b/src/bootloader/rise/uefi/main.zig @@ -0,0 +1,436 @@ +const lib = @import("lib"); +const assert = lib.assert; +const config = lib.config; +const Allocator = lib.Allocator; +const ELF = lib.ELF(64); +const log = lib.log.scoped(.uefi); +const PhysicalAddress = lib.PhysicalAddress; +const PhysicalMemoryRegion = lib.PhysicalMemoryRegion; +const VirtualAddress = lib.VirtualAddress; +const VirtualMemoryRegion = lib.VirtualMemoryRegion; + +const bootloader = @import("bootloader"); +const uefi = @import("uefi"); +const BootloaderInformation = uefi.BootloaderInformation; +const BootServices = uefi.BootServices; +const ConfigurationTable = uefi.ConfigurationTable; +const FileProtocol = uefi.FileProtocol; +const Handle = uefi.Handle; +const LoadedImageProtocol = uefi.LoadedImageProtocol; +const LoadKernelFunction = uefi.LoadKernelFunction; +const MemoryCategory = uefi.MemoryCategory; +const MemoryDescriptor = uefi.MemoryDescriptor; +const ProgramSegment = uefi.ProgramSegment; +const Protocol = uefi.Protocol; +const page_table_estimated_size = uefi.page_table_estimated_size; +const SimpleFilesystemProtocol = uefi.SimpleFilesystemProtocol; +const SystemTable = uefi.SystemTable; + +const privileged = @import("privileged"); +const ACPI = privileged.ACPI; +const PageAllocator = privileged.PageAllocator; +pub const writer = privileged.writer; + +const CPU = privileged.arch.CPU; +const GDT = privileged.arch.x86_64.GDT; +const paging = privileged.arch.paging; + +const Stage = enum { + boot_services, + after_boot_services, + trampoline, +}; + +pub const std_options = struct { + pub const log_level = lib.std.log.Level.debug; + + pub fn logFn(comptime level: lib.std.log.Level, comptime scope: @TypeOf(.EnumLiteral), comptime format: []const u8, args: anytype) void { + const scope_prefix = if (scope == .default) ": " else "(" ++ @tagName(scope) ++ "): "; + const prefix = "[" ++ @tagName(level) ++ "] " ++ scope_prefix; + switch (lib.cpu.arch) { + .x86_64 => { + lib.format(writer, prefix ++ format ++ "\n", args) catch {}; + }, + else => @compileError("Unsupported CPU architecture"), + } + } +}; + +const practical_memory_map_descriptor_size = 0x30; +const practical_memory_map_descriptor_count = 256; + +pub fn panic(message: []const u8, _: ?*lib.StackTrace, _: ?usize) noreturn { + writer.writeAll("[uefi] [PANIC] ") catch {}; + writer.writeAll(message) catch {}; + writer.writeAll("\r\n") catch {}; + + privileged.shutdown(.failure); +} + +const Filesystem = extern struct { + root: *FileProtocol, + buffer: [0x200 * 10]u8 = undefined, + + pub fn deinitialize(filesystem: *Filesystem) !void { + _ = filesystem; + } + + pub fn readFile(filesystem: *Filesystem, file_path: []const u8, file_buffer: []u8) ![]const u8 { + const file = try filesystem.openFile(file_path); + var size: u64 = file_buffer.len; + try uefi.Try(file.handle.read(&size, file_buffer.ptr)); + if (file_buffer.len < size) @panic("readFileFast"); + return file_buffer[0..size]; + } + + pub fn sneakFile(filesystem: *Filesystem, file_path: []const u8, size: usize) ![]const u8 { + _ = size; + + const file = try filesystem.readFile(file_path, &filesystem.buffer); + return file; + } + + const FileDescriptor = struct { + handle: *FileProtocol, + path_size: u32, + }; + + pub fn getFileSize(filesystem: *Filesystem, file_path: []const u8) !u32 { + const file = try filesystem.openFile(file_path); + log.debug("File size", .{}); + var file_info_buffer: [@sizeOf(uefi.FileInfo) + 0x100]u8 align(@alignOf(uefi.FileInfo)) = undefined; + var file_info_size = file_info_buffer.len; + try uefi.Try(file.handle.getInfo(&uefi.FileInfo.guid, &file_info_size, &file_info_buffer)); + if (file_info_buffer.len < file_info_size) @panic("getFileSize"); + const file_info = @as(*uefi.FileInfo, @ptrCast(&file_info_buffer)); + return @as(u32, @intCast(file_info.file_size)); + } + + fn openFile(filesystem: *Filesystem, file_path: []const u8) !FileDescriptor { + const init = @fieldParentPtr(Initialization, "filesystem", filesystem); + if (init.exited_boot_services) { + return Error.boot_services_exited; + } + + log.debug("opening file: {s}", .{file_path}); + var file: *FileProtocol = undefined; + var path_buffer: [256:0]u16 = undefined; + const length = try lib.unicode.utf8ToUtf16Le(&path_buffer, file_path); + path_buffer[length] = 0; + const path = path_buffer[0..length :0]; + const uefi_path = if (path[0] == '/') path[1..] else path; + log.debug("uefi path: {any}", .{uefi_path}); + + try uefi.Try(filesystem.root.open(&file, uefi_path, FileProtocol.efi_file_mode_read, 0)); + log.debug("Opened", .{}); + + const result = FileDescriptor{ + .handle = file, + .path_size = @as(u32, @intCast(path.len * @sizeOf(u16))), + }; + + log.debug("opened file: {s}", .{file_path}); + + return result; + } + + pub fn getSectorSize(filesystem: *Filesystem) u16 { + _ = filesystem; + return lib.default_sector_size; + } +}; + +const MemoryMap = extern struct { + size: usize = buffer_len, + key: usize, + descriptor_size: usize, + descriptor_version: u32, + buffer: [buffer_len]u8 align(@alignOf(MemoryDescriptor)) = undefined, + offset: usize = 0, + entry_count: u32, + + const buffer_len = practical_memory_map_descriptor_size * practical_memory_map_descriptor_count; + + pub fn getEntryCount(memory_map: *const MemoryMap) u32 { + return memory_map.entry_count; + } + + pub fn next(memory_map: *MemoryMap) !?bootloader.MemoryMapEntry { + if (memory_map.offset < memory_map.size) { + const entry = @as(*MemoryDescriptor, @ptrCast(@alignCast(@alignOf(MemoryDescriptor), memory_map.buffer[memory_map.offset..].ptr))).*; + memory_map.offset += memory_map.descriptor_size; + const result = bootloader.MemoryMapEntry{ + .region = PhysicalMemoryRegion.new(.{ + .address = PhysicalAddress.new(entry.physical_start), + .size = entry.number_of_pages << uefi.page_shifter, + }), + .type = switch (entry.type) { + .ReservedMemoryType, .LoaderCode, .LoaderData, .BootServicesCode, .BootServicesData, .RuntimeServicesCode, .RuntimeServicesData, .ACPIReclaimMemory, .ACPIMemoryNVS, .MemoryMappedIO, .MemoryMappedIOPortSpace, .PalCode, .PersistentMemory => .reserved, + .ConventionalMemory => .usable, + .UnusableMemory => .bad_memory, + else => @panic("Unknown type"), + }, + }; + + return result; + } + + return null; + } + + fn getHostRegion(memory_map: *MemoryMap, length_size_tuples: bootloader.LengthSizeTuples) !PhysicalMemoryRegion { + var memory: []align(uefi.page_size) u8 = undefined; + const memory_size = length_size_tuples.getAlignedTotalSize(); + try uefi.Try(memory_map.boot_services.allocatePages(.AllocateAnyPages, .LoaderData, memory_size >> uefi.page_shifter, &memory.ptr)); + memory.len = memory_size; + @memset(memory, 0); + + return PhysicalMemoryRegion.fromByteSlice(.{ .slice = memory }); + } +}; + +const Initialization = struct { + filesystem: Filesystem, + framebuffer: bootloader.Framebuffer, + architecture: switch (lib.cpu.arch) { + .x86_64 => struct { + rsdp: *ACPI.RSDP.Descriptor1, + }, + else => @compileError("Architecture not supported"), + }, + boot_services: *uefi.BootServices, + handle: uefi.Handle, + // system_table: *uefi.SystemTable, + early_initialized: bool = false, + filesystem_initialized: bool = false, + memory_map_initialized: bool = false, + framebuffer_initialized: bool = false, + exited_boot_services: bool = false, + memory_map: MemoryMap, + + pub fn getRSDPAddress(init: *Initialization) u32 { + return @as(u32, @intCast(@intFromPtr(init.architecture.rsdp))); + } + + pub fn getCPUCount(init: *Initialization) !u32 { + return switch (lib.cpu.arch) { + .x86_64 => blk: { + const madt_header = try init.architecture.rsdp.findTable(.APIC); + const madt = @as(*align(1) const ACPI.MADT, @ptrCast(madt_header)); + break :blk madt.getCPUCount(); + }, + else => @compileError("Architecture not supported"), + }; + } + + pub fn initialize(init: *Initialization) !void { + defer init.early_initialized = true; + + const system_table = uefi.getSystemTable(); + const handle = uefi.getHandle(); + const boot_services = system_table.boot_services orelse @panic("boot services"); + const out = system_table.con_out orelse @panic("con out"); + try uefi.Try(out.reset(true)); + try uefi.Try(out.clearScreen()); + + init.* = .{ + .memory_map = .{ + .key = 0, + .descriptor_size = 0, + .descriptor_version = 0, + .entry_count = 0, + }, + .filesystem = .{ + .root = blk: { + const loaded_image = try Protocol.open(LoadedImageProtocol, boot_services, handle); + const filesystem_protocol = try Protocol.open(SimpleFilesystemProtocol, boot_services, loaded_image.device_handle orelse @panic("No device handle")); + + var root: *FileProtocol = undefined; + try uefi.Try(filesystem_protocol.openVolume(&root)); + break :blk root; + }, + }, + .framebuffer = blk: { + log.debug("Locating GOP", .{}); + const gop = try Protocol.locate(uefi.GraphicsOutputProtocol, boot_services); + log.debug("Located GOP", .{}); + + const pixel_format_info: struct { + red_color_mask: bootloader.Framebuffer.ColorMask, + blue_color_mask: bootloader.Framebuffer.ColorMask, + green_color_mask: bootloader.Framebuffer.ColorMask, + bpp: u8, + } = switch (gop.mode.info.pixel_format) { + .PixelRedGreenBlueReserved8BitPerColor => .{ + .red_color_mask = .{ .size = 8, .shift = 0 }, + .green_color_mask = .{ .size = 8, .shift = 8 }, + .blue_color_mask = .{ .size = 8, .shift = 16 }, + .bpp = 32, + }, + .PixelBlueGreenRedReserved8BitPerColor => .{ + .red_color_mask = .{ .size = 8, .shift = 16 }, + .green_color_mask = .{ .size = 8, .shift = 8 }, + .blue_color_mask = .{ .size = 8, .shift = 0 }, + .bpp = 32, + }, + .PixelBitMask, .PixelBltOnly => @panic("Unsupported pixel format"), + .PixelFormatMax => @panic("Corrupted pixel format"), + }; + + break :blk bootloader.Framebuffer{ + .address = gop.mode.frame_buffer_base, + .pitch = @divExact(gop.mode.info.pixels_per_scan_line * pixel_format_info.bpp, @bitSizeOf(u8)), + .width = gop.mode.info.horizontal_resolution, + .height = gop.mode.info.vertical_resolution, + .bpp = pixel_format_info.bpp, + .red_mask = pixel_format_info.red_color_mask, + .green_mask = pixel_format_info.green_color_mask, + .blue_mask = pixel_format_info.blue_color_mask, + .memory_model = 0x06, + }; + }, + .boot_services = boot_services, + .handle = handle, + .architecture = switch (lib.cpu.arch) { + .x86_64 => .{ + .rsdp = for (system_table.configuration_table[0..system_table.number_of_table_entries]) |configuration_table| { + if (configuration_table.vendor_guid.eql(ConfigurationTable.acpi_20_table_guid)) { + break @as(*ACPI.RSDP.Descriptor1, @ptrCast(@alignCast(@alignOf(ACPI.RSDP.Descriptor1), configuration_table.vendor_table))); + } + } else return Error.rsdp_not_found, + }, + else => @compileError("Architecture not supported"), + }, + }; + + log.debug("Memory map size: {}", .{init.memory_map.size}); + _ = boot_services.getMemoryMap(&init.memory_map.size, @as([*]MemoryDescriptor, @ptrCast(&init.memory_map.buffer)), &init.memory_map.key, &init.memory_map.descriptor_size, &init.memory_map.descriptor_version); + init.memory_map.entry_count = @as(u32, @intCast(@divExact(init.memory_map.size, init.memory_map.descriptor_size))); + assert(init.memory_map.entry_count > 0); + + init.filesystem_initialized = true; + init.memory_map_initialized = true; + init.framebuffer_initialized = true; + } + + pub fn deinitializeMemoryMap(init: *Initialization) !void { + if (!init.exited_boot_services) { + // Add the region for the bootloader information + init.memory_map.size += init.memory_map.descriptor_size; + const expected_memory_map_size = init.memory_map.size; + const expected_memory_map_descriptor_size = init.memory_map.descriptor_size; + const expected_memory_map_descriptor_version = init.memory_map.descriptor_version; + + log.debug("Getting memory map before exiting boot services...", .{}); + + blk: while (init.memory_map.size < MemoryMap.buffer_len) : (init.memory_map.size += init.memory_map.descriptor_size) { + uefi.Try(init.boot_services.getMemoryMap(&init.memory_map.size, @as([*]MemoryDescriptor, @ptrCast(&init.memory_map.buffer)), &init.memory_map.key, &init.memory_map.descriptor_size, &init.memory_map.descriptor_version)) catch continue; + init.exited_boot_services = true; + break :blk; + } else { + @panic("Cannot satisfy memory map requirements"); + } + + if (expected_memory_map_size != init.memory_map.size) { + log.warn("Old memory map size: {}. New memory map size: {}", .{ expected_memory_map_size, init.memory_map.size }); + } + if (expected_memory_map_descriptor_size != init.memory_map.descriptor_size) { + @panic("Descriptor size change"); + } + if (expected_memory_map_descriptor_version != init.memory_map.descriptor_version) { + @panic("Descriptor version change"); + } + const real_memory_map_entry_count = @divExact(init.memory_map.size, init.memory_map.descriptor_size); + const expected_memory_map_entry_count = @divExact(expected_memory_map_size, expected_memory_map_descriptor_size); + const diff = @as(i16, @intCast(expected_memory_map_entry_count)) - @as(i16, @intCast(real_memory_map_entry_count)); + if (diff < 0) { + @panic("Memory map entry count diff < 0"); + } + + // bootloader_information.configuration.memory_map_diff = @intCast(u8, diff); + + log.debug("Exiting boot services...", .{}); + try uefi.Try(init.boot_services.exitBootServices(init.handle, init.memory_map.key)); + log.debug("Exited boot services...", .{}); + + privileged.arch.disableInterrupts(); + } + + init.memory_map.offset = 0; + } + + pub fn ensureLoaderIsMapped(init: *Initialization, minimal_paging: paging.Specific, page_allocator: PageAllocator, bootloader_information: *bootloader.Information) !void { + _ = bootloader_information; + // Actually mapping the whole uefi executable so we don't have random problems with code being dereferenced by the trampoline + switch (lib.cpu.arch) { + .x86_64 => { + const trampoline_code_start = @intFromPtr(&bootloader.arch.x86_64.jumpToKernel); + + try init.deinitializeMemoryMap(); + while (try init.memory_map.next()) |entry| { + if (entry.region.address.value() < trampoline_code_start and trampoline_code_start < entry.region.address.offset(entry.region.size).value()) { + const code_physical_region = entry.region; + const code_virtual_address = code_physical_region.address.toIdentityMappedVirtualAddress(); + try minimal_paging.map(code_physical_region.address, code_virtual_address, code_physical_region.size, .{ .write = false, .execute = true }, page_allocator); + return; + } + } + }, + else => @compileError("Architecture not supported"), + } + + return Error.map_failed; + } + + pub fn ensureStackIsMapped(init: *Initialization, minimal_paging: paging.Specific, page_allocator: PageAllocator) !void { + const rsp = asm volatile ( + \\mov %rsp, %[rsp] + : [rsp] "=r" (-> u64), + ); + + while (try init.memory_map.next()) |entry| { + if (entry.region.address.value() < rsp and rsp < entry.region.address.offset(entry.region.size).value()) { + const rsp_region_physical_address = entry.region.address; + const rsp_region_virtual_address = rsp_region_physical_address.toIdentityMappedVirtualAddress(); + if (entry.region.size == 0) return Error.region_empty; + try minimal_paging.map(rsp_region_physical_address, rsp_region_virtual_address, entry.region.size, .{ .write = true, .execute = false }, page_allocator); + return; + } + } + + return Error.map_failed; + } +}; + +var initialization: Initialization = undefined; + +const Error = error{ + map_failed, + region_empty, + rsdp_not_found, + boot_services_exited, +}; + +pub fn main() noreturn { + // var filesystem: Filesystem = .{ + // .boot_services = boot_services, + // .handle = handle, + // .root = undefined, + // }; + // var mmap: MemoryMap = .{ + // .boot_services = boot_services, + // .handle = handle, + // }; + // var fb = Framebuffer{ + // .boot_services = boot_services, + // }; + // var vas = VAS{ + // .mmap = &mmap, + // }; + initialization.initialize() catch |err| { + @panic(@errorName(err)); + }; + bootloader.Information.initialize(&initialization, .rise, .uefi) catch |err| { + @panic(@errorName(err)); + }; +} diff --git a/src/bootloader/rise/uefi/test.zig b/src/bootloader/rise/uefi/test.zig new file mode 100644 index 0000000..e69de29 diff --git a/src/bootloader/todo/draw_context.zig b/src/bootloader/todo/draw_context.zig new file mode 100644 index 0000000..5b03423 --- /dev/null +++ b/src/bootloader/todo/draw_context.zig @@ -0,0 +1,48 @@ +pub const DrawContext = extern struct { + x: u32 = 0, + y: u32 = 0, + color: u32 = 0xff_ff_ff_ff, + reserved: u32 = 0, + + pub const Error = error{}; + pub const Writer = lib.Writer(*DrawContext, DrawContext.Error, DrawContext.write); + + pub fn write(draw_context: *DrawContext, bytes: []const u8) DrawContext.Error!usize { + const bootloader_information = @fieldParentPtr(Information, "draw_context", draw_context); + const color = draw_context.color; + for (bytes) |byte| { + if (byte != '\n') { + bootloader_information.font.draw(&bootloader_information.font, &bootloader_information.framebuffer, byte, color, draw_context.x, draw_context.y); + if (draw_context.x + 8 < bootloader_information.framebuffer.width) { + draw_context.x += @bitSizeOf(u8); + continue; + } + } + + if (draw_context.y < bootloader_information.framebuffer.width) { + draw_context.y += bootloader_information.font.character_size; + draw_context.x = 0; + } else { + asm volatile ( + \\cli + \\hlt + ); + } + } + + return bytes.len; + } + + pub inline fn clearScreen(draw_context: *DrawContext, color: u32) void { + const bootloader_information = @fieldParentPtr(Information, "draw_context", draw_context); + const pixels_per_scanline = @divExact(bootloader_information.framebuffer.pitch, @divExact(bootloader_information.framebuffer.bpp, @bitSizeOf(u8))); + const framebuffer_pixels = @as([*]u32, @ptrFromInt(bootloader_information.framebuffer.address))[0 .. pixels_per_scanline * bootloader_information.framebuffer.height]; + var y: u32 = 0; + while (y < bootloader_information.framebuffer.height) : (y += 1) { + const line = framebuffer_pixels[y * pixels_per_scanline .. y * pixels_per_scanline + pixels_per_scanline]; + for (line) |*pixel| { + pixel.* = color; + } + } + } +}; diff --git a/src/bootloader/todo/font.zig b/src/bootloader/todo/font.zig new file mode 100644 index 0000000..5e179bb --- /dev/null +++ b/src/bootloader/todo/font.zig @@ -0,0 +1,51 @@ +pub const Font = extern struct { + file: PhysicalMemoryRegion align(8), // so 32-bit doesn't whine + glyph_buffer_size: u32, + character_size: u8, + draw: *const fn (font: *const Font, framebuffer: *const Framebuffer, character: u8, color: u32, offset_x: u32, offset_y: u32) void, + + pub fn fromPSF1(file: []const u8) !Font { + const header = @as(*const lib.PSF1.Header, @ptrCast(file.ptr)); + if (!lib.equal(u8, &header.magic, &lib.PSF1.Header.magic)) { + return lib.PSF1.Error.invalid_magic; + } + + const glyph_buffer_size = @as(u32, header.character_size) * (lib.maxInt(u8) + 1) * (1 + @intFromBool(header.mode == 1)); + + return .{ + .file = PhysicalMemoryRegion.new(PhysicalAddress.new(@intFromPtr(file.ptr)), file.len), + .glyph_buffer_size = glyph_buffer_size, + .character_size = header.character_size, + .draw = drawPSF1, + }; + } + + fn drawPSF1(font: *const Font, framebuffer: *const Framebuffer, character: u8, color: u32, offset_x: u32, offset_y: u32) void { + const bootloader_information = @fieldParentPtr(Information, "framebuffer", framebuffer); + const glyph_buffer_virtual_region = if (bootloader_information.stage == .trampoline) font.file.toHigherHalfVirtualAddress() else font.file.toIdentityMappedVirtualAddress(); + const glyph_buffer = glyph_buffer_virtual_region.access(u8)[@sizeOf(lib.PSF1.Header)..][0..font.glyph_buffer_size]; + const glyph_offset = @as(usize, character) * font.character_size; + const glyph = glyph_buffer[glyph_offset .. glyph_offset + font.character_size]; + + var glyph_index: usize = 0; + _ = glyph_index; + + const pixels_per_scanline = @divExact(framebuffer.pitch, @divExact(framebuffer.bpp, @bitSizeOf(u8))); + const fb = @as([*]u32, @ptrFromInt(framebuffer.address))[0 .. pixels_per_scanline * framebuffer.height]; + var y = offset_y; + + for (glyph) |byte| { + const base_index = y * pixels_per_scanline + offset_x; + if (byte & 1 << 7 != 0) fb[base_index + 0] = color; + if (byte & 1 << 6 != 0) fb[base_index + 1] = color; + if (byte & 1 << 5 != 0) fb[base_index + 2] = color; + if (byte & 1 << 4 != 0) fb[base_index + 3] = color; + if (byte & 1 << 3 != 0) fb[base_index + 4] = color; + if (byte & 1 << 2 != 0) fb[base_index + 5] = color; + if (byte & 1 << 1 != 0) fb[base_index + 6] = color; + if (byte & 1 << 0 != 0) fb[base_index + 7] = color; + + y += 1; + } + } +}; diff --git a/src/bootloader/todo/smp.zig b/src/bootloader/todo/smp.zig new file mode 100644 index 0000000..ec9d34e --- /dev/null +++ b/src/bootloader/todo/smp.zig @@ -0,0 +1,136 @@ +// TODO: legacy stuff; refactor when SMP is implemented + +// pub fn initializeSMP(bootloader_information: *Information, madt: *const ACPI.MADT) void { +// if (bootloader_information.bootloader != .rise) @panic("Protocol not supported"); +// +// const smp_records = bootloader_information.getSlice(.smps); +// +// switch (lib.cpu.arch) { +// .x86, .x86_64 => { +// const cr3 = bootloader_information.virtual_address_space.arch.cr3; +// if (@bitCast(u64, cr3) > lib.maxInt(u32)) { +// lib.log.err("CR3: 0x{x}, {}", .{ @bitCast(u64, cr3), cr3 }); +// @panic("CR3 overflow"); +// } +// +// const cpuid = lib.arch.x86_64.cpuid; +// const lapicWrite = privileged.arch.x86_64.APIC.lapicWrite; +// +// if (cpuid(1).edx & (1 << 9) == 0) { +// @panic("No APIC detected"); +// } +// +// var iterator = madt.getIterator(); +// var smp_index: usize = 0; +// +// const smp_trampoline_physical_address = PhysicalAddress.new(@ptrToInt(&arch.x86_64.smp_trampoline)); +// // Sanity checks +// const trampoline_argument_symbol = @extern(*SMP.Trampoline.Argument, .{ .name = "smp_trampoline_arg_start" }); +// const smp_core_booted_symbol = @extern(*bool, .{ .name = "smp_core_booted" }); +// const trampoline_argument_start = @ptrToInt(trampoline_argument_symbol); +// const trampoline_argument_offset = @intCast(u32, trampoline_argument_start - smp_trampoline_physical_address.value()); +// const smp_core_booted_offset = @intCast(u32, @ptrToInt(smp_core_booted_symbol) - smp_trampoline_physical_address.value()); +// if (!lib.isAligned(trampoline_argument_start, @alignOf(SMP.Trampoline.Argument))) @panic("SMP trampoline argument alignment must match"); +// const trampoline_argument_end = @ptrToInt(@extern(*u8, .{ .name = "smp_trampoline_arg_end" })); +// const trampoline_argument_size = trampoline_argument_end - trampoline_argument_start; +// if (trampoline_argument_size != @sizeOf(SMP.Trampoline.Argument)) { +// @panic("SMP trampoline argument size must match"); +// } +// +// const smp_trampoline_size = @ptrToInt(@extern(*u8, .{ .name = "smp_trampoline_end" })) - smp_trampoline_physical_address.value(); +// if (smp_trampoline_size > lib.arch.valid_page_sizes[0]) { +// @panic("SMP trampoline too big"); +// } +// +// const smp_trampoline = @intCast(u32, switch (lib.cpu.arch) { +// .x86 => smp_trampoline_physical_address.toIdentityMappedVirtualAddress().value(), +// .x86_64 => blk: { +// const page_counters = bootloader_information.getPageCounters(); +// for (bootloader_information.getMemoryMapEntries(), 0..) |memory_map_entry, index| { +// if (memory_map_entry.type == .usable and memory_map_entry.region.address.value() < lib.mb and lib.isAligned(memory_map_entry.region.address.value(), lib.arch.valid_page_sizes[0])) { +// const page_counter = &page_counters[index]; +// const offset = page_counter.* * lib.arch.valid_page_sizes[0]; +// if (offset < memory_map_entry.region.size) { +// page_counter.* += 1; +// const smp_trampoline_buffer_region = memory_map_entry.region.offset(offset).toIdentityMappedVirtualAddress(); +// +// privileged.arch.x86_64.paging.setMappingFlags(&bootloader_information.virtual_address_space, smp_trampoline_buffer_region.address.value(), .{ +// .write = true, +// .execute = true, +// .global = true, +// }) catch @panic("can't set smp trampoline flags"); +// +// const smp_trampoline_buffer = smp_trampoline_buffer_region.access(u8); +// const smp_trampoline_region = PhysicalMemoryRegion.new(smp_trampoline_physical_address, smp_trampoline_size); +// const smp_trampoline_source = smp_trampoline_region.toIdentityMappedVirtualAddress().access(u8); +// +// @memcpy(smp_trampoline_buffer, smp_trampoline_source); +// break :blk smp_trampoline_buffer_region.address.value(); +// } +// } +// } +// +// @panic("No suitable region found for SMP trampoline"); +// }, +// else => @compileError("Architecture not supported"), +// }); +// +// const trampoline_argument = @intToPtr(*SMP.Trampoline.Argument, smp_trampoline + trampoline_argument_offset); +// trampoline_argument.* = .{ +// .hhdm = bootloader_information.higher_half, +// .cr3 = @intCast(u32, @bitCast(u64, cr3)), +// .gdt_descriptor = undefined, +// .gdt = .{}, +// }; +// +// trampoline_argument.gdt_descriptor = trampoline_argument.gdt.getDescriptor(); +// +// const smp_core_booted = @intToPtr(*bool, smp_trampoline + smp_core_booted_offset); +// +// while (iterator.next()) |entry| { +// switch (entry.type) { +// .LAPIC => { +// const lapic_entry = @fieldParentPtr(ACPI.MADT.LAPIC, "record", entry); +// const lapic_id = @as(u32, lapic_entry.APIC_ID); +// smp_records[smp_index] = .{ +// .acpi_id = lapic_entry.ACPI_processor_UID, +// .lapic_id = lapic_id, +// .entry_point = 0, +// .argument = 0, +// }; +// +// if (lapic_entry.APIC_ID == bootloader_information.smp.bsp_lapic_id) { +// smp_index += 1; +// continue; +// } +// +// lapicWrite(.icr_high, lapic_id << 24); +// lapicWrite(.icr_low, 0x4500); +// +// arch.x86_64.delay(10_000_000); +// +// const icr_low = (smp_trampoline >> 12) | 0x4600; +// lapicWrite(.icr_high, lapic_id << 24); +// lapicWrite(.icr_low, icr_low); +// +// for (0..100) |_| { +// if (@cmpxchgStrong(bool, smp_core_booted, true, false, .SeqCst, .SeqCst) == null) { +// lib.log.debug("Booted core #{}", .{lapic_id}); +// break; +// } +// +// arch.x86_64.delay(10_000_000); +// } else @panic("SMP not booted"); +// }, +// .x2APIC => @panic("x2APIC"), +// else => { +// lib.log.warn("Unhandled {s} entry", .{@tagName(entry.type)}); +// }, +// } +// } +// +// lib.log.debug("Enabled all cores!", .{}); +// }, +// else => @compileError("Architecture not supported"), +// } +// } diff --git a/src/bootloader/uefi.zig b/src/bootloader/uefi.zig new file mode 100644 index 0000000..2e1e46d --- /dev/null +++ b/src/bootloader/uefi.zig @@ -0,0 +1,88 @@ +const lib = @import("lib"); +const alignForward = lib.alignForward; +const assert = lib.assert; +const CustomAllocator = lib.CustomAllocator; +const log = lib.log.scoped(.UEFI); +const uefi = lib.uefi; + +pub const BootServices = uefi.tables.BootServices; +pub const ConfigurationTable = uefi.tables.ConfigurationTable; +pub const Error = Status.EfiError; +pub const FileInfo = uefi.protocols.FileInfo; +pub const FileProtocol = uefi.protocols.FileProtocol; +pub const GraphicsOutputProtocol = uefi.protocols.GraphicsOutputProtocol; +pub const LoadedImageProtocol = uefi.protocols.LoadedImageProtocol; +pub const Handle = uefi.Handle; +pub const MemoryDescriptor = uefi.tables.MemoryDescriptor; +pub const SimpleFilesystemProtocol = uefi.protocols.SimpleFileSystemProtocol; +pub const Status = uefi.Status; +pub const SystemTable = uefi.tables.SystemTable; +pub const Try = Status.err; + +const str16 = lib.std.unicode.utf8ToUtf16LeStringLiteral; + +pub const page_size = 0x1000; +pub const page_shifter = lib.arch.page_shifter(page_size); + +const privileged = @import("privileged"); +const PhysicalAddress = privileged.PhysicalAddress; +const VirtualAddress = privileged.VirtualAddress; +const VirtualMemoryRegion = privileged.VirtualMemoryRegion; +const stopCPU = privileged.arch.stopCPU; + +pub fn panic(comptime format: []const u8, arguments: anytype) noreturn { + privileged.arch.disableInterrupts(); + lib.log.scoped(.PANIC).err(format, arguments); + privileged.arch.stopCPU(); +} + +pub fn result(src: lib.SourceLocation, status: Status) void { + Try(status) catch |err| { + panic("UEFI error {} at {s}:{}:{} in function {s}", .{ err, src.file, src.line, src.column, src.fn_name }); + }; +} + +pub inline fn getSystemTable() *SystemTable { + return uefi.system_table; +} + +pub inline fn getHandle() Handle { + return uefi.handle; +} + +pub const Protocol = struct { + pub fn locate(comptime ProtocolT: type, boot_services: *BootServices) !*ProtocolT { + var pointer_buffer: ?*anyopaque = null; + try Try(boot_services.locateProtocol(&ProtocolT.guid, null, &pointer_buffer)); + return cast(ProtocolT, pointer_buffer); + } + + pub fn handle(comptime ProtocolT: type, boot_services: *BootServices, efi_handle: Handle) !*ProtocolT { + var interface_buffer: ?*anyopaque = null; + try Try(boot_services.handleProtocol(efi_handle, &ProtocolT.guid, &interface_buffer)); + return cast(ProtocolT, interface_buffer); + } + + pub fn open(comptime ProtocolT: type, boot_services: *BootServices, efi_handle: Handle) !*ProtocolT { + var interface_buffer: ?*anyopaque = null; + try Try(boot_services.openProtocol(efi_handle, &ProtocolT.guid, &interface_buffer, efi_handle, null, .{ .get_protocol = true })); + return cast(ProtocolT, interface_buffer); + } + + fn cast(comptime ProtocolT: type, ptr: ?*anyopaque) *ProtocolT { + return @as(*ProtocolT, @ptrCast(@alignCast(@alignOf(ProtocolT), ptr))); + } +}; + +pub const ProgramSegment = extern struct { + physical: u64, + virtual: u64, + size: u32, + file_offset: u32, + mappings: extern struct { + write: bool, + execute: bool, + }, +}; + +//pub const LoadKernelFunction = fn (bootloader_information: *BootloaderInformation, kernel_start_address: u64, cr3: privileged.arch.x86_64.registers.cr3, stack: u64, gdt_descriptor: *privileged.arch.x86_64.GDT.Descriptor) callconv(.SysV) noreturn; diff --git a/src/common.zig b/src/common.zig new file mode 100644 index 0000000..62d63d7 --- /dev/null +++ b/src/common.zig @@ -0,0 +1,502 @@ +const compiler_builtin = @import("builtin"); +pub const cpu = compiler_builtin.cpu; +pub const os = compiler_builtin.os.tag; +pub const build_mode = compiler_builtin.mode; +pub const is_test = compiler_builtin.is_test; + +pub const kb = 1024; +pub const mb = kb * 1024; +pub const gb = mb * 1024; +pub const tb = gb * 1024; + +pub const SizeUnit = enum(u64) { + byte = 1, + kilobyte = 1024, + megabyte = 1024 * 1024, + gigabyte = 1024 * 1024 * 1024, + terabyte = 1024 * 1024 * 1024 * 1024, +}; + +pub const std = @import("std"); +pub const Target = std.Target; +pub const Cpu = Target.Cpu; +pub const CrossTarget = std.zig.CrossTarget; + +pub const log = std.log; + +pub const Atomic = std.atomic.Atomic; + +pub const Reader = std.io.Reader; +pub const Writer = std.io.Writer; + +pub const FixedBufferStream = std.io.FixedBufferStream; +pub const fixedBufferStream = std.io.fixedBufferStream; + +pub fn assert(ok: bool) void { + if (!ok) { + if (@inComptime()) { + @compileError("Assert failed!"); + } else { + @panic("Assert failed!"); + } + } +} + +pub const deflate = std.compress.deflate; + +const debug = std.debug; +pub const print = debug.print; +pub const StackIterator = debug.StackIterator; +pub const dwarf = std.dwarf; +pub const ModuleDebugInfo = std.debug.ModuleDebugInfo; + +pub const elf = std.elf; + +const fmt = std.fmt; +pub const format = std.fmt.format; +pub const FormatOptions = fmt.FormatOptions; +pub const bufPrint = fmt.bufPrint; +pub const allocPrint = fmt.allocPrint; +pub const comptimePrint = fmt.comptimePrint; +pub const parseUnsigned = fmt.parseUnsigned; + +const heap = std.heap; +pub const FixedBufferAllocator = heap.FixedBufferAllocator; + +pub const json = std.json; + +const mem = std.mem; +pub const ZigAllocator = mem.Allocator; +pub const equal = mem.eql; +pub const length = mem.len; +pub const startsWith = mem.startsWith; +pub const endsWith = mem.endsWith; +pub const indexOf = mem.indexOf; +// Ideal for small inputs +pub const indexOfPosLinear = mem.indexOfPosLinear; +pub const lastIndexOf = mem.lastIndexOf; +pub const asBytes = mem.asBytes; +pub const readIntBig = mem.readIntBig; +pub const readIntSliceBig = mem.readIntSliceBig; +pub const concat = mem.concat; +pub const sliceAsBytes = mem.sliceAsBytes; +pub const bytesAsSlice = mem.bytesAsSlice; +pub const alignForward = mem.alignForward; +pub const alignBackward = mem.alignBackward; +pub const isAligned = mem.isAligned; +pub const isAlignedGeneric = mem.isAlignedGeneric; +pub const reverse = mem.reverse; +pub const tokenize = mem.tokenize; +pub const containsAtLeast = mem.containsAtLeast; +pub const sliceTo = mem.sliceTo; +pub const swap = mem.swap; + +pub const random = std.rand; + +pub const testing = std.testing; + +pub const sort = std.sort; + +pub fn fieldSize(comptime T: type, field_name: []const u8) comptime_int { + var foo: T = undefined; + return @sizeOf(@TypeOf(@field(foo, field_name))); +} + +const DiffError = error{ + diff, +}; + +pub fn diff(file1: []const u8, file2: []const u8) !void { + assert(file1.len == file2.len); + var different_bytes: u64 = 0; + for (file1, 0..) |byte1, index| { + const byte2 = file2[index]; + const is_different_byte = byte1 != byte2; + different_bytes += @intFromBool(is_different_byte); + if (is_different_byte) { + log.debug("Byte [0x{x}] is different: 0x{x} != 0x{x}", .{ index, byte1, byte2 }); + } + } + + if (different_bytes != 0) { + log.debug("Total different bytes: 0x{x}", .{different_bytes}); + return DiffError.diff; + } +} + +pub fn zeroes(comptime T: type) T { + var result: T = undefined; + const slice = asBytes(&result); + @memset(slice, 0); + return result; +} + +const ascii = std.ascii; +pub const upperString = ascii.upperString; +pub const isUpper = ascii.isUpper; +pub const isAlphabetic = ascii.isAlphabetic; + +const std_builtin = std.builtin; +pub const AtomicRmwOp = std_builtin.AtomicRmwOp; +pub const AtomicOrder = std_builtin.AtomicOrder; +pub const Type = std_builtin.Type; +pub const StackTrace = std_builtin.StackTrace; +pub const SourceLocation = std_builtin.SourceLocation; + +pub fn FieldType(comptime T: type, comptime name: []const u8) type { + return @TypeOf(@field(@as(T, undefined), name)); +} + +// META PROGRAMMING +pub const AutoEnumArray = std.enums.EnumArray; +pub const fields = std.meta.fields; +pub const IntType = std.meta.Int; +pub const enumFromInt = std.meta.enumFromInt; +pub const stringToEnum = std.meta.stringToEnum; +pub const Tag = std.meta.Tag; + +const math = std.math; +pub const maxInt = math.maxInt; +pub const min = math.min; +pub const divCeil = math.divCeil; +pub const clamp = math.clamp; +pub const isPowerOfTwo = math.isPowerOfTwo; +pub const mul = math.mul; +pub const cast = math.cast; + +pub const unicode = std.unicode; + +pub const uefi = std.os.uefi; + +pub const DiskType = enum(u32) { + virtio = 0, + nvme = 1, + ahci = 2, + ide = 3, + memory = 4, + bios = 5, + + pub const count = enumCount(@This()); +}; + +pub const FilesystemType = enum(u32) { + rise = 0, + ext2 = 1, + fat32 = 2, + + pub const count = enumCount(@This()); +}; + +pub fn enumFields(comptime E: type) []const Type.EnumField { + return @typeInfo(E).Enum.fields; +} + +pub const enumValues = std.enums.values; + +pub fn enumCount(comptime E: type) usize { + return enumFields(E).len; +} + +pub const PartitionTableType = enum { + mbr, + gpt, +}; + +pub const supported_architectures = [_]Cpu.Arch{ + .x86_64, + //.aarch64, + //.riscv64, +}; + +pub fn architectureIndex(comptime arch: Cpu.Arch) comptime_int { + inline for (supported_architectures, 0..) |architecture, index| { + if (arch == architecture) return index; + } + + @compileError("Architecture not found"); +} + +pub const architecture_bootloader_map = blk: { + var array: [supported_architectures.len][]const ArchitectureBootloader = undefined; + + array[architectureIndex(.x86_64)] = &.{ + .{ + .id = .rise, + .protocols = &.{ .bios, .uefi }, + }, + .{ + .id = .limine, + .protocols = &.{ .bios, .uefi }, + }, + }; + + // array[architectureIndex(.aarch64)] = &.{ + // .{ + // .id = .rise, + // .protocols = &.{.uefi}, + // }, + // .{ + // .id = .limine, + // .protocols = &.{.uefi}, + // }, + // }; + + // array[architectureIndex(.riscv64)] = &.{ + // .{ + // .id = .rise, + // .protocols = &.{.uefi}, + // }, + // }; + + break :blk array; +}; + +pub const Bootloader = enum(u32) { + rise, + limine, + + pub const Protocol = enum(u32) { + bios, + uefi, + }; +}; + +pub const ArchitectureBootloader = struct { + id: Bootloader, + protocols: []const Bootloader.Protocol, +}; + +pub const TraditionalExecutionMode = enum(u1) { + privileged = 0, + user = 1, +}; + +pub const ExecutionEnvironment = enum { + qemu, +}; + +pub const ImageConfig = struct { + image_name: []const u8, + sector_count: u64, + sector_size: u16, + partition_table: PartitionTableType, + partition: PartitionConfig, + + pub const default_path = "config/image_config.json"; + + pub fn get(allocator: ZigAllocator, path: []const u8) !ImageConfig { + const image_config_file = try std.fs.cwd().readFileAlloc(allocator, path, maxInt(usize)); + const parsed_image_configuration = try std.json.parseFromSlice(ImageConfig, allocator, image_config_file, .{}); + return parsed_image_configuration.value; + } +}; + +pub const PartitionConfig = struct { + name: []const u8, + filesystem: FilesystemType, + first_lba: u64, +}; + +pub const QEMU = extern struct { + pub const isa_debug_exit = ISADebugExit{}; + + pub const ISADebugExit = extern struct { + io_base: u8 = 0xf4, + io_size: u8 = @sizeOf(u32), + }; + + pub const ExitCode = enum(u32) { + success = 0x10, + failure = 0x11, + _, + }; +}; + +pub const OptimizeMode = std.builtin.OptimizeMode; + +pub const Configuration = struct { + architecture: Cpu.Arch, + bootloader: Bootloader, + boot_protocol: Bootloader.Protocol, + execution_environment: ExecutionEnvironment, + optimize_mode: OptimizeMode, + execution_type: ExecutionType, + executable_kind: std.Build.CompileStep.Kind, +}; + +pub const QEMUOptions = packed struct { + is_test: bool, + is_debug: bool, +}; + +pub const ExecutionType = enum { + emulated, + accelerated, +}; + +pub const Suffix = enum { + bootloader, + cpu_driver, + image, + complete, + + pub fn fromConfiguration(suffix: Suffix, allocator: ZigAllocator, configuration: Configuration, prefix: ?[]const u8) ![]const u8 { + const cpu_driver_suffix = [_][]const u8{ + @tagName(configuration.optimize_mode), + "_", + @tagName(configuration.architecture), + "_", + @tagName(configuration.executable_kind), + }; + + const bootloader_suffix = [_][]const u8{ + @tagName(configuration.architecture), + "_", + @tagName(configuration.bootloader), + "_", + @tagName(configuration.boot_protocol), + }; + + const image_suffix = [_][]const u8{ + @tagName(configuration.optimize_mode), + "_", + } ++ bootloader_suffix ++ [_][]const u8{ + "_", + @tagName(configuration.executable_kind), + }; + + const complete_suffix = image_suffix ++ [_][]const u8{ + "_", + @tagName(configuration.execution_type), + "_", + @tagName(configuration.execution_environment), + }; + + return try std.mem.concat(allocator, u8, &switch (suffix) { + .cpu_driver => if (prefix) |pf| [1][]const u8{pf} ++ cpu_driver_suffix else cpu_driver_suffix, + .bootloader => if (prefix) |pf| [1][]const u8{pf} ++ bootloader_suffix else bootloader_suffix, + .image => if (prefix) |pf| [1][]const u8{pf} ++ image_suffix else image_suffix, + .complete => if (prefix) |pf| [1][]const u8{pf} ++ complete_suffix else complete_suffix, + }); + } +}; + +pub const Module = struct { + program: UserProgram, + name: []const u8, +}; +pub const UserProgram = struct { + kind: Kind, + dependencies: []const Dependency, + + pub const Kind = enum { + zig_exe, + }; + + pub const Dependency = struct { + foo: u64 = 0, + }; +}; + +pub const RiseProgram = enum { + bootloader, + cpu, + user, + host, +}; + +pub fn canVirtualizeWithQEMU(architecture: Cpu.Arch, ci: bool) bool { + if (architecture != cpu.arch) return false; + if (ci) return false; + + return switch (os) { + .linux => blk: { + const uname = std.os.uname(); + const release = &uname.release; + break :blk !containsAtLeast(u8, release, 1, "WSL") and !containsAtLeast(u8, release, 1, "microsoft"); + }, + else => false, + }; +} + +pub const default_cpu_name = "/cpu"; +pub const default_init_file = "/init"; + +pub const ArgumentParser = struct { + pub const null_specifier = "-"; + + pub const DiskImageBuilder = struct { + argument_index: usize = 0, + + pub const ArgumentType = enum { + disk_image_path, + configuration, + image_configuration_path, + bootloader, + cpu, + user_programs, + }; + + pub const Result = struct { + bootloader: []const u8, + disk_image_path: []const u8, + image_configuration_path: []const u8, + cpu: []const u8, + user_programs: []const []const u8, + configuration: Configuration, + }; + + pub fn next(argument_parser: *ArgumentParser.DiskImageBuilder) ?ArgumentType { + if (argument_parser.argument_index < enumCount(ArgumentType)) { + const result: ArgumentType = @enumFromInt(argument_parser.argument_index); + argument_parser.argument_index += 1; + return result; + } + + return null; + } + }; + + pub const Runner = struct { + argument_index: usize = 0, + + pub fn next(argument_parser: *ArgumentParser.Runner) ?ArgumentType { + if (argument_parser.argument_index < enumCount(ArgumentType)) { + const result: ArgumentType = @enumFromInt(argument_parser.argument_index); + argument_parser.argument_index += 1; + return result; + } + + return null; + } + + pub const ArgumentType = enum { + configuration, + disk_image_path, + image_configuration_path, + cpu_driver, + loader_path, + qemu_options, + ci, + debug_user, + debug_loader, + init, + }; + + pub const Result = struct { + configuration: Configuration, + disk_image_path: []const u8, + image_configuration_path: []const u8, + cpu_driver: []const u8, + loader_path: []const u8, + qemu_options: QEMUOptions, + ci: bool, + debug_user: bool, + debug_loader: bool, + init: []const u8, + }; + }; +}; + +pub const default_disk_size = 64 * 1024 * 1024; +pub const default_sector_size = 0x200; diff --git a/src/cpu.zig b/src/cpu.zig new file mode 100644 index 0000000..5b15f49 --- /dev/null +++ b/src/cpu.zig @@ -0,0 +1,435 @@ +const lib = @import("lib"); +const Allocator = lib.Allocator; +const assert = lib.assert; +const log = lib.log; + +const bootloader = @import("bootloader"); + +const privileged = @import("privileged"); +const CPUPageTables = privileged.arch.CPUPageTables; +const Mapping = privileged.Mapping; +const PageAllocatorInterface = privileged.PageAllocator; +const PhysicalAddress = lib.PhysicalAddress; +const PhysicalAddressSpace = lib.PhysicalAddressSpace; +const PhysicalMemoryRegion = lib.PhysicalMemoryRegion; +const stopCPU = privileged.arch.stopCPU; +const VirtualAddress = privileged.VirtualAddress; +const VirtualMemoryRegion = privileged.VirtualMemoryRegion; + +const rise = @import("rise"); + +pub const test_runner = @import("cpu/test_runner.zig"); +pub const arch = @import("cpu/arch.zig"); +pub const capabilities = @import("cpu/capabilities.zig"); + +pub export var stack: [0x8000]u8 align(0x1000) = undefined; +pub export var page_allocator = PageAllocator{ + .head = null, + .list_allocator = .{ + .u = .{ + .primitive = .{ + .backing_4k_page = undefined, + .allocated = 0, + }, + }, + .primitive = true, + }, +}; + +pub var bundle: []const u8 = &.{}; +pub var bundle_files: []const u8 = &.{}; + +pub export var user_scheduler: *UserScheduler = undefined; +pub export var driver: *align(lib.arch.valid_page_sizes[0]) Driver = undefined; +pub export var page_tables: CPUPageTables = undefined; +pub var file: []align(lib.default_sector_size) const u8 = undefined; +pub export var core_id: u32 = 0; +pub export var bsp = false; +var panic_lock = lib.Spinlock.released; + +/// This data structure holds the information needed to run a core +pub const Driver = extern struct { + init_root_capability: capabilities.RootDescriptor, + valid: bool, + padding: [padding_byte_count]u8 = .{0} ** padding_byte_count, + const padding_byte_count = lib.arch.valid_page_sizes[0] - @sizeOf(bool) - @sizeOf(capabilities.RootDescriptor); + + pub inline fn getRootCapability(drv: *Driver) *capabilities.Root { + return drv.init_root_capability.value; + } + + comptime { + // @compileLog(@sizeOf(Driver)); + assert(lib.isAligned(@sizeOf(Driver), lib.arch.valid_page_sizes[0])); + } +}; + +/// This data structure holds the information needed to run a program in a core (cpu side) +pub const UserScheduler = extern struct { + capability_root_node: capabilities.Root, + common: *rise.UserScheduler, + padding: [padding_byte_count]u8 = .{0} ** padding_byte_count, + + const total_size = @sizeOf(capabilities.Root) + @sizeOf(*rise.UserScheduler); + const aligned_size = lib.alignForward(usize, total_size, lib.arch.valid_page_sizes[0]); + const padding_byte_count = aligned_size - total_size; + + comptime { + if (padding_byte_count == 0 and @hasField(UserScheduler, "padding")) { + @compileError("remove padding because it is not necessary"); + } + } +}; + +const print_stack_trace = false; +var panic_count: usize = 0; + +inline fn panicPrologue(comptime format: []const u8, arguments: anytype) !void { + panic_count += 1; + privileged.arch.disableInterrupts(); + if (panic_count == 1) panic_lock.acquire(); + + try writer.writeAll(lib.Color.get(.bold)); + try writer.writeAll(lib.Color.get(.red)); + try writer.writeAll("[CPU DRIVER] [PANIC] "); + try writer.writeAll(lib.Color.get(.reset)); + try writer.print(format, arguments); + try writer.writeByte('\n'); +} + +inline fn panicEpilogue() noreturn { + if (panic_count == 1) panic_lock.release(); + + shutdown(.failure); +} + +// inline fn printStackTrace(maybe_stack_trace: ?*lib.StackTrace) !void { +// if (maybe_stack_trace) |stack_trace| { +// var debug_info = try getDebugInformation(); +// try writer.writeAll("Stack trace:\n"); +// var frame_index: usize = 0; +// var frames_left: usize = @min(stack_trace.index, stack_trace.instruction_addresses.len); +// +// while (frames_left != 0) : ({ +// frames_left -= 1; +// frame_index = (frame_index + 1) % stack_trace.instruction_addresses.len; +// }) { +// const return_address = stack_trace.instruction_addresses[frame_index]; +// try writer.print("[{}] ", .{frame_index}); +// try printSourceAtAddress(&debug_info, return_address); +// } +// } else { +// try writer.writeAll("Stack trace not available\n"); +// } +// } + +// inline fn printStackTraceFromStackIterator(return_address: usize, frame_address: usize) !void { +// var debug_info = try getDebugInformation(); +// var stack_iterator = lib.StackIterator.init(return_address, frame_address); +// var frame_index: usize = 0; +// try writer.writeAll("Stack trace:\n"); +// +// try printSourceAtAddress(&debug_info, return_address); +// while (stack_iterator.next()) |address| : (frame_index += 1) { +// try writer.print("[{}] ", .{frame_index}); +// try printSourceAtAddress(&debug_info, address); +// } +// } + +// fn printSourceAtAddress(debug_info: *lib.ModuleDebugInfo, address: usize) !void { +// if (debug_info.findCompileUnit(address)) |compile_unit| { +// const symbol = .{ +// .symbol_name = debug_info.getSymbolName(address) orelse "???", +// .compile_unit_name = compile_unit.die.getAttrString(debug_info, lib.dwarf.AT.name, debug_info.debug_str, compile_unit.*) catch "???", +// .line_info = debug_info.getLineNumberInfo(heap_allocator.toZig(), compile_unit.*, address) catch null, +// }; +// try writer.print("0x{x}: {s}!{s} {s}:{}:{}\n", .{ address, symbol.symbol_name, symbol.compile_unit_name, symbol.line_info.?.file_name, symbol.line_info.?.line, symbol.line_info.?.column }); +// } else |err| { +// return err; +// } +// } + +pub fn panicWithStackTrace(stack_trace: ?*lib.StackTrace, comptime format: []const u8, arguments: anytype) noreturn { + _ = stack_trace; + panicPrologue(format, arguments) catch {}; + // if (print_stack_trace) printStackTrace(stack_trace) catch {}; + panicEpilogue(); +} + +pub fn panicFromInstructionPointerAndFramePointer(return_address: usize, frame_address: usize, comptime format: []const u8, arguments: anytype) noreturn { + _ = frame_address; + _ = return_address; + panicPrologue(format, arguments) catch {}; + //if (print_stack_trace) printStackTraceFromStackIterator(return_address, frame_address) catch {}; + panicEpilogue(); +} + +pub fn panic(comptime format: []const u8, arguments: anytype) noreturn { + @call(.always_inline, panicFromInstructionPointerAndFramePointer, .{ @returnAddress(), @frameAddress(), format, arguments }); +} + +pub var syscall_count: usize = 0; + +pub inline fn shutdown(exit_code: lib.QEMU.ExitCode) noreturn { + log.debug("Printing stats...", .{}); + log.debug("Syscall count: {}", .{syscall_count}); + + privileged.shutdown(exit_code); +} + +pub const PageAllocator = extern struct { + head: ?*Entry, + list_allocator: ListAllocator, + total_allocated_size: u32 = 0, + + fn getPageAllocatorInterface(pa: *PageAllocator) PageAllocatorInterface { + return .{ + .allocate = callbackAllocate, + .context = pa, + .context_type = .cpu, + }; + } + + fn callbackAllocate(context: ?*anyopaque, size: u64, alignment: u64, options: PageAllocatorInterface.AllocateOptions) Allocator.Allocate.Error!PhysicalMemoryRegion { + _ = options; + const pa = @as(?*PageAllocator, @ptrCast(@alignCast(context))) orelse return Allocator.Allocate.Error.OutOfMemory; + const result = try pa.allocate(size, alignment); + return result; + } + + pub fn allocate(pa: *PageAllocator, size: u64, alignment: u64) Allocator.Allocate.Error!PhysicalMemoryRegion { + if (pa.head == null) { + @panic("head null"); + } + + const allocation = blk: { + var ptr = pa.head; + while (ptr) |entry| : (ptr = entry.next) { + if (lib.isAligned(entry.region.address.value(), alignment) and entry.region.size > size) { + const result = PhysicalMemoryRegion{ + .address = entry.region.address, + .size = size, + }; + entry.region.address = entry.region.address.offset(size); + entry.region.size -= size; + + pa.total_allocated_size += @as(u32, @intCast(size)); + // log.debug("Allocated 0x{x}", .{size}); + + break :blk result; + } + } + + ptr = pa.head; + + while (ptr) |entry| : (ptr = entry.next) { + const aligned_address = lib.alignForward(entry.region.address.value(), alignment); + const top = entry.region.top().value(); + if (aligned_address < top and top - aligned_address > size) { + // log.debug("Found region which we should be splitting: (0x{x}, 0x{x})", .{ entry.region.address.value(), entry.region.size }); + // log.debug("User asked for 0x{x} bytes with alignment 0x{x}", .{ size, alignment }); + // Split the addresses to obtain the desired result + const first_region_size = aligned_address - entry.region.address.value(); + const first_region_address = entry.region.address; + const first_region_next = entry.next; + + const second_region_address = aligned_address + size; + const second_region_size = top - aligned_address + size; + + const result = PhysicalMemoryRegion{ + .address = PhysicalAddress.new(aligned_address), + .size = size, + }; + + // log.debug("\nFirst region: (Address: 0x{x}. Size: 0x{x}).\nRegion in the middle (allocated): (Address: 0x{x}. Size: 0x{x}).\nSecond region: (Address: 0x{x}. Size: 0x{x})", .{ first_region_address, first_region_size, result.address.value(), result.size, second_region_address, second_region_size }); + + const new_entry = pa.list_allocator.get(); + entry.* = .{ + .region = .{ + .address = first_region_address, + .size = first_region_size, + }, + .next = new_entry, + }; + + new_entry.* = .{ + .region = .{ + .address = PhysicalAddress.new(second_region_address), + .size = second_region_size, + }, + .next = first_region_next, + }; + // log.debug("First entry: (Address: 0x{x}. Size: 0x{x})", .{ entry.region.address.value(), entry.region.size }); + // log.debug("Second entry: (Address: 0x{x}. Size: 0x{x})", .{ new_entry.region.address.value(), new_entry.region.size }); + + // pa.total_allocated_size += @intCast(u32, size); + // log.debug("Allocated 0x{x}", .{size}); + + break :blk result; + } + } + + log.err("Allocate error. Size: 0x{x}. Alignment: 0x{x}. Total allocated size: 0x{x}", .{ size, alignment, pa.total_allocated_size }); + return Allocator.Allocate.Error.OutOfMemory; + }; + + //log.debug("Physical allocation: 0x{x}, 0x{x}", .{ allocation.address.value(), allocation.size }); + + @memset(allocation.toHigherHalfVirtualAddress().access(u8), 0); + + return allocation; + } + + pub inline fn fromBSP(bootloader_information: *bootloader.Information) InitializationError!PageAllocator { + const memory_map_entries = bootloader_information.getMemoryMapEntries(); + const page_counters = bootloader_information.getPageCounters(); + + var total_size: usize = 0; + const page_shifter = lib.arch.page_shifter(lib.arch.valid_page_sizes[0]); + + for (memory_map_entries, page_counters) |entry, page_counter| { + if (entry.type != .usable or !lib.isAligned(entry.region.size, lib.arch.valid_page_sizes[0]) or entry.region.address.value() < lib.mb) { + continue; + } + + total_size += entry.region.size - (page_counter << page_shifter); + } + + const cpu_count = bootloader_information.smp.cpu_count; + const total_memory_to_take = total_size / cpu_count; + + // Look for a 4K page to host the memory map + const backing_4k_page = for (memory_map_entries, page_counters) |entry, *page_counter| { + const occupied_size = page_counter.* << page_shifter; + const entry_size_left = entry.region.size - occupied_size; + if (entry_size_left != 0) { + if (entry.type != .usable or !lib.isAligned(entry.region.size, lib.arch.valid_page_sizes[0]) or entry.region.address.value() < lib.mb) continue; + + assert(lib.isAligned(entry_size_left, lib.arch.valid_page_sizes[0])); + page_counter.* += 1; + break entry.region.address.offset(occupied_size); + } + } else return InitializationError.bootstrap_region_not_found; + + var memory_taken: usize = 0; + var backing_4k_page_memory_allocated: usize = 0; + + var last_entry: ?*Entry = null; + var first_entry: ?*Entry = null; + + for (memory_map_entries, page_counters) |entry, *page_counter| { + if (entry.type != .usable or !lib.isAligned(entry.region.size, lib.arch.valid_page_sizes[0]) or entry.region.address.value() < lib.mb) continue; + + const occupied_size = page_counter.* << page_shifter; + + if (occupied_size < entry.region.size) { + const entry_size_left = entry.region.size - occupied_size; + + var memory_taken_from_region: usize = 0; + while (memory_taken + memory_taken_from_region < total_memory_to_take) { + if (memory_taken_from_region == entry_size_left) break; + + const size_to_take = @min(2 * lib.mb, entry_size_left); + memory_taken_from_region += size_to_take; + } + + memory_taken += memory_taken_from_region; + + page_counter.* += @as(u32, @intCast(memory_taken_from_region >> page_shifter)); + const region_descriptor = .{ + .address = entry.region.offset(occupied_size).address, + .size = memory_taken_from_region, + }; + + if (backing_4k_page_memory_allocated >= lib.arch.valid_page_sizes[0]) return InitializationError.memory_exceeded; + const entry_address = backing_4k_page.offset(backing_4k_page_memory_allocated); + const new_entry = entry_address.toHigherHalfVirtualAddress().access(*Entry); + backing_4k_page_memory_allocated += @sizeOf(Entry); + + new_entry.* = .{ + .region = .{ + .address = region_descriptor.address, + .size = region_descriptor.size, + }, + .next = null, + }; + + if (last_entry) |e| { + e.next = new_entry; + } else { + first_entry = new_entry; + } + + last_entry = new_entry; + + if (memory_taken >= total_memory_to_take) break; + } + } + + const result = .{ + .head = first_entry, + .list_allocator = .{ + .u = .{ + .primitive = .{ + .backing_4k_page = backing_4k_page, + .allocated = backing_4k_page_memory_allocated, + }, + }, + .primitive = true, + }, + }; + + return result; + } + + const ListAllocator = extern struct { + u: extern union { + primitive: extern struct { + backing_4k_page: PhysicalAddress, + allocated: u64, + }, + normal: extern struct { + foo: u64, + }, + }, + primitive: bool, + + pub fn get(list_allocator: *ListAllocator) *Entry { + switch (list_allocator.primitive) { + true => { + if (list_allocator.u.primitive.allocated < 0x1000) { + const result = list_allocator.u.primitive.backing_4k_page.offset(list_allocator.u.primitive.allocated).toHigherHalfVirtualAddress().access(*Entry); + list_allocator.u.primitive.backing_4k_page = list_allocator.u.primitive.backing_4k_page.offset(@sizeOf(Entry)); + return result; + } else { + @panic("reached limit"); + } + }, + false => { + @panic("not primitive allocator not implemented"); + }, + } + } + }; + + pub const Entry = extern struct { + region: PhysicalMemoryRegion, + next: ?*Entry, + }; + + const InitializationError = error{ + bootstrap_region_not_found, + memory_exceeded, + }; +}; + +// fn getDebugInformation() !lib.ModuleDebugInfo { +// const debug_info = lib.getDebugInformation(heap_allocator.toZig(), file) catch |err| { +// try writer.print("Failed to get debug information: {}", .{err}); +// return err; +// }; +// +// return debug_info; +// } + +pub const writer = privileged.E9Writer{ .context = {} }; diff --git a/src/cpu/arch.zig b/src/cpu/arch.zig new file mode 100644 index 0000000..2f1010c --- /dev/null +++ b/src/cpu/arch.zig @@ -0,0 +1,13 @@ +const lib = @import("lib"); + +pub const current = switch (lib.cpu.arch) { + .x86_64 => x86_64, + else => @compileError("Architecture not supported"), +}; + +pub const x86_64 = @import("arch/x86_64.zig"); +pub usingnamespace current; + +pub const entryPoint = current.entryPoint; +pub const virtualAddressSpaceallocatePages = current.virtualAddressSpaceallocatePages; +pub const root_page_table_type = current.root_page_table_entry; diff --git a/src/cpu/arch/x86/64/init.zig b/src/cpu/arch/x86/64/init.zig new file mode 100644 index 0000000..aa37a3d --- /dev/null +++ b/src/cpu/arch/x86/64/init.zig @@ -0,0 +1,1492 @@ +const bootloader = @import("bootloader"); +const cpu = @import("cpu"); +const lib = @import("lib"); +const privileged = @import("privileged"); +const rise = @import("rise"); + +const Allocator = lib.Allocator; +const assert = lib.assert; +const ELF = lib.ELF(64); +const log = lib.log.scoped(.INIT); +const PhysicalAddress = lib.PhysicalAddress; +const PhysicalMemoryRegion = lib.PhysicalMemoryRegion; +const VirtualAddress = lib.VirtualAddress; +const VirtualMemoryRegion = lib.VirtualMemoryRegion; + +const panic = cpu.panic; +const x86_64 = cpu.arch.current; + +const paging = privileged.arch.paging; + +const APIC = privileged.arch.x86_64.APIC; +const cr0 = privileged.arch.x86_64.registers.cr0; +const cr3 = privileged.arch.x86_64.registers.cr3; +const cr4 = privileged.arch.x86_64.registers.cr4; +const XCR0 = privileged.arch.x86_64.registers.XCR0; +const IA32_APIC_BASE = privileged.arch.x86_64.registers.IA32_APIC_BASE; +const IA32_EFER = privileged.arch.x86_64.registers.IA32_EFER; +const IA32_FS_BASE = privileged.arch.x86_64.registers.IA32_FS_BASE; +const IA32_FSTAR = privileged.arch.x86_64.registers.IA32_FSTAR; +const IA32_FMASK = privileged.arch.x86_64.registers.IA32_FMASK; +const IA32_LSTAR = privileged.arch.x86_64.registers.IA32_LSTAR; +const IA32_STAR = privileged.arch.x86_64.registers.IA32_STAR; + +const user_scheduler_memory_start_virtual_address = VirtualAddress.new(0x200_000); +const user_scheduler_virtual_address = user_scheduler_memory_start_virtual_address; + +pub fn entryPoint() callconv(.Naked) noreturn { + asm volatile ( + \\lea stack(%rip), %rsp + \\add %[stack_len], %rsp + \\pushq $0 + \\mov %rsp, %rbp + \\jmp *%[main] + : + : [stack_len] "i" (cpu.stack.len), + [main] "{rax}" (&main), + : "rsp", "rbp" + ); + + unreachable; +} + +const InitializationError = error{ + feature_requested_and_not_available, + no_files, + cpu_file_not_found, + init_file_not_found, +}; + +noinline fn main(bootloader_information: *bootloader.Information) callconv(.C) noreturn { + log.info("Initializing...\n\n\t[BUILD MODE] {s}\n\t[BOOTLOADER] {s}\n\t[BOOT PROTOCOL] {s}\n", .{ @tagName(lib.build_mode), @tagName(bootloader_information.bootloader), @tagName(bootloader_information.protocol) }); + archInitialize(bootloader_information) catch |err| { + cpu.panicWithStackTrace(@errorReturnTrace(), "Failed to initialize CPU: {}", .{err}); + }; +} + +fn archInitialize(bootloader_information: *bootloader.Information) !noreturn { + // bootloader_information.draw_context.clearScreen(0xffff7f50); + // Do an integrity check so that the bootloader information is in perfect state and there is no weird memory behavior. + // This is mainly due to the transition from a 32-bit bootloader to a 64-bit CPU driver in the x86-64 architecture. + try bootloader_information.checkIntegrity(); + // Informing the bootloader information struct that we have reached the CPU driver and any bootloader + // functionality is not available anymore + bootloader_information.stage = .cpu; + // Check that the bootloader has loaded some files as the CPU driver needs them to go forward + cpu.bundle = bootloader_information.getSlice(.bundle); + if (cpu.bundle.len == 0) return InitializationError.no_files; + cpu.bundle_files = bootloader_information.getSlice(.file_list); + if (cpu.bundle_files.len == 0) return InitializationError.no_files; + + const cpuid = lib.arch.x86_64.cpuid; + if (x86_64.pcid) { + if (cpuid(1).ecx & (1 << 17) == 0) return InitializationError.feature_requested_and_not_available; + } + + if (x86_64.invariant_tsc) { + if (cpuid(0x80000007).edx & (1 << 8) == 0) return InitializationError.feature_requested_and_not_available; + } + + // Initialize GDT + const gdt_descriptor = x86_64.GDT.Descriptor{ + .limit = @sizeOf(x86_64.GDT) - 1, + .address = @intFromPtr(&gdt), + }; + + asm volatile ( + \\lgdt %[gdt] + \\mov %[ds], %rax + \\movq %rax, %ds + \\movq %rax, %es + \\movq %rax, %fs + \\movq %rax, %gs + \\movq %rax, %ss + \\pushq %[cs] + \\lea 1f(%rip), %rax + \\pushq %rax + \\lretq + \\1: + : + : [gdt] "*p" (&gdt_descriptor), + [ds] "i" (x86_64.data_64), + [cs] "i" (x86_64.code_64), + : "memory" + ); + + const tss_address = @intFromPtr(&tss); + gdt.tss_descriptor = .{ + .limit_low = @as(u16, @truncate(@sizeOf(x86_64.TSS))), + .base_low = @as(u16, @truncate(tss_address)), + .base_mid_low = @as(u8, @truncate(tss_address >> 16)), + .access = .{ + .type = .tss_available, + .dpl = 0, + .present = true, + }, + .attributes = .{ + .limit = @as(u4, @truncate(@sizeOf(x86_64.TSS) >> 16)), + .available_for_system_software = false, + .granularity = false, + }, + .base_mid_high = @as(u8, @truncate(tss_address >> 24)), + .base_high = @as(u32, @truncate(tss_address >> 32)), + }; + + tss.rsp[0] = @intFromPtr(&interrupt_stack) + interrupt_stack.len; + asm volatile ( + \\ltr %[tss_selector] + : + : [tss_selector] "r" (@as(u16, x86_64.tss_selector)), + : "memory" + ); + + // Initialize IDT + + for (&idt.descriptors, interrupt_handlers, 0..) |*descriptor, interrupt_handler, i| { + const interrupt_address = @intFromPtr(interrupt_handler); + descriptor.* = .{ + .offset_low = @as(u16, @truncate(interrupt_address)), + .segment_selector = x86_64.code_64, + .flags = .{ + .ist = 0, + .type = if (i < 32) .trap_gate else .interrupt_gate, // TODO: I think this is not correct + .dpl = 0, + .present = true, + }, + .offset_mid = @as(u16, @truncate(interrupt_address >> 16)), + .offset_high = @as(u32, @truncate(interrupt_address >> 32)), + }; + } + + const idt_descriptor = x86_64.IDT.Descriptor{ + .limit = @sizeOf(x86_64.IDT) - 1, + .address = @intFromPtr(&idt), + }; + + asm volatile ( + \\lidt %[idt_descriptor] + : + : [idt_descriptor] "*p" (&idt_descriptor), + : "memory" + ); + + // Mask PIC + privileged.arch.io.write(u8, 0xa1, 0xff); + privileged.arch.io.write(u8, 0x21, 0xff); + + asm volatile ("sti" ::: "memory"); + + const star = IA32_STAR{ + .kernel_cs = x86_64.code_64, + .user_cs_anchor = x86_64.data_64, + }; + + comptime { + assert(x86_64.data_64 == star.kernel_cs + 8); + assert(star.user_cs_anchor == x86_64.user_data_64 - 8); + assert(star.user_cs_anchor == x86_64.user_code_64 - 16); + } + + star.write(); + + IA32_LSTAR.write(@intFromPtr(&cpu.arch.x86_64.syscall.entryPoint)); + const syscall_mask = privileged.arch.x86_64.registers.syscall_mask; + IA32_FMASK.write(syscall_mask); + + // Enable syscall extensions + var efer = IA32_EFER.read(); + efer.SCE = true; + efer.write(); + + // TODO: AVX + + const avx_xsave_cpuid = cpuid(1); + const avx_support = avx_xsave_cpuid.ecx & (1 << 28) != 0; + const xsave_support = avx_xsave_cpuid.ecx & (1 << 26) != 0; + const avx2_support = cpuid(7).ebx & (1 << 5) != 0; + log.debug("AVX: {}. AVX2: {}. XSAVE: {}. Can't enable them yet", .{ avx_support, avx2_support, xsave_support }); + + comptime { + assert(lib.arch.valid_page_sizes[0] == 0x1000); + } + + var my_cr4 = cr4.read(); + my_cr4.OSFXSR = true; + my_cr4.OSXMMEXCPT = true; + //my_cr4.OSXSAVE = true; + my_cr4.page_global_enable = true; + my_cr4.performance_monitoring_counter_enable = true; + my_cr4.write(); + + var my_cr0 = cr0.read(); + my_cr0.monitor_coprocessor = true; + my_cr0.emulation = false; + my_cr0.numeric_error = true; + my_cr0.task_switched = false; + my_cr0.write(); + + // The bootloader already mapped APIC, so it's not necessary to map it here + var ia32_apic_base = IA32_APIC_BASE.read(); + cpu.bsp = ia32_apic_base.bsp; + ia32_apic_base.global_enable = true; + + const spurious_vector: u8 = 0xFF; + APIC.write(.spurious, @as(u32, 0x100) | spurious_vector); + + const tpr = APIC.TaskPriorityRegister{}; + tpr.write(); + + const lvt_timer = APIC.LVTTimer{}; + lvt_timer.write(); + + ia32_apic_base.write(); + + x86_64.ticks_per_ms = APIC.calibrateTimer(); + + cpu.core_id = APIC.read(.id); + + asm volatile ( + \\fninit + // TODO: figure out why this crashes with KVM + //\\ldmxcsr %[mxcsr] + :: //[mxcsr] "m" (@as(u32, 0x1f80)), + : "memory"); + + // Write user TLS base address + IA32_FS_BASE.write(user_scheduler_virtual_address.value()); + + // TODO: configure PAT + + try initialize(bootloader_information); +} + +fn initialize(bootloader_information: *bootloader.Information) !noreturn { + const memory_map_entries = bootloader_information.getMemoryMapEntries(); + const page_counters = bootloader_information.getPageCounters(); + + var free_size: usize = 0; + var free_region_count: usize = 0; + + for (memory_map_entries, page_counters) |mmap_entry, page_counter| { + if (mmap_entry.type == .usable) { + const free_region = mmap_entry.getFreeRegion(page_counter); + free_size += free_region.size; + free_region_count += @intFromBool(free_region.size > 0); + } + } + + const total_to_allocate = @sizeOf(cpu.Driver) + @sizeOf(cpu.capabilities.Root) + lib.arch.valid_page_sizes[0]; + + const total_physical: struct { + region: PhysicalMemoryRegion, + free_size: u64, + index: usize, + } = for (memory_map_entries, page_counters, 0..) |mmap_entry, page_counter, index| { + if (mmap_entry.type == .usable) { + const free_region = mmap_entry.getFreeRegion(page_counter); + if (free_region.size >= total_to_allocate) { + break .{ + .region = PhysicalMemoryRegion.new(.{ + .address = free_region.address, + .size = total_to_allocate, + }), + .free_size = free_region.size - total_to_allocate, + .index = index, + }; + } + } + } else @panic("Total physical region not found"); + + var offset: usize = 0; + + cpu.driver = total_physical.region.offset(offset).address.toHigherHalfVirtualAddress().access(*align(lib.arch.valid_page_sizes[0]) cpu.Driver); + offset += @sizeOf(cpu.Driver); + + const root_capability = total_physical.region.offset(offset).address.toHigherHalfVirtualAddress().access(*cpu.capabilities.Root); + offset += @sizeOf(cpu.capabilities.Root); + + var heap_offset: usize = 0; + const heap_region = total_physical.region.offset(offset); + assert(heap_region.size == lib.arch.valid_page_sizes[0]); + const host_free_ram = heap_region.offset(heap_offset).address.toHigherHalfVirtualAddress().access(*cpu.capabilities.RAM.Region); + host_free_ram.* = .{ + .region = PhysicalMemoryRegion.new(.{ + .address = total_physical.region.offset(total_to_allocate).address, + .size = total_physical.free_size, + }), + }; + heap_offset += @sizeOf(cpu.capabilities.RAM.Region); + const privileged_cpu_memory = heap_region.offset(heap_offset).address.toHigherHalfVirtualAddress().access(*cpu.capabilities.RAM.Region); + privileged_cpu_memory.* = .{ + .region = total_physical.region, + }; + + heap_offset += @sizeOf(cpu.capabilities.RAM); + + var previous_free_ram = host_free_ram; + for (memory_map_entries, page_counters, 0..) |memory_map_entry, page_counter, index| { + if (index == total_physical.index) continue; + + if (memory_map_entry.type == .usable) { + const region = memory_map_entry.getFreeRegion(page_counter); + if (region.size > 0) { + const new_free_ram = heap_region.offset(heap_offset).address.toHigherHalfVirtualAddress().access(*cpu.capabilities.RAM.Region); + heap_offset += @sizeOf(cpu.capabilities.RAM.Region); + new_free_ram.* = .{ + .region = region, + }; + previous_free_ram.next = new_free_ram; + previous_free_ram = new_free_ram; + } + } + } + + root_capability.* = .{ + .static = .{ + .cpu = true, + .boot = true, + .process = true, + }, + .dynamic = .{ + .io = .{ + .debug = true, + }, + .ram = .{ + .lists = blk: { + var lists = [1]?*cpu.capabilities.RAM.Region{null} ** lib.arch.reverse_valid_page_sizes.len; + var free_ram_iterator: ?*cpu.capabilities.RAM.Region = host_free_ram; + while (free_ram_iterator) |free_ram| { + comptime assert(lib.arch.reverse_valid_page_sizes.len == 3); + const next = free_ram.next; + + if (free_ram.region.size >= lib.arch.reverse_valid_page_sizes[0]) { + const previous_first = lists[0]; + lists[0] = free_ram; + free_ram.next = previous_first; + } else if (free_ram.region.size >= lib.arch.reverse_valid_page_sizes[1]) { + const previous_first = lists[1]; + lists[1] = free_ram; + free_ram.next = previous_first; + } else if (free_ram.region.size >= lib.arch.reverse_valid_page_sizes[2]) { + const previous_first = lists[2]; + lists[2] = free_ram; + free_ram.next = previous_first; + } else unreachable; + + free_ram_iterator = next; + } + + break :blk lists; + }, + }, + .cpu_memory = .{ + .flags = .{ + .allocate = true, + }, + }, + .page_table = .{}, + }, + .scheduler = .{ + .memory = undefined, + }, + .heap = cpu.capabilities.Root.Heap.new(heap_region, heap_offset), + }; + + cpu.driver.* = .{ + .valid = true, + .init_root_capability = .{ + .value = root_capability, + }, + }; + + switch (cpu.bsp) { + true => { + const init_module_descriptor = try bootloader_information.getFileDescriptor("init"); + try spawnInitBSP(init_module_descriptor.content, bootloader_information.cpu_page_tables); + }, + false => @panic("Implement APP"), + } +} + +export var interrupt_stack: [0x1000]u8 align(lib.arch.stack_alignment) = undefined; +export var gdt = x86_64.GDT{}; +export var tss = x86_64.TSS{}; +export var idt = x86_64.IDT{}; +export var user_stack: u64 = 0; + +comptime { + assert(rise.arch.user_code_selector == x86_64.user_code_selector); + assert(rise.arch.user_data_selector == x86_64.user_data_selector); +} + +pub fn InterruptHandler(comptime interrupt_number: u64, comptime has_error_code: bool) fn () callconv(.Naked) noreturn { + return struct { + fn handler() callconv(.Naked) noreturn { + asm volatile ( + \\endbr64 + ::: "memory"); + + if (x86_64.smap) { + // TODO: Investigate why this is Exception #6 + asm volatile ( + \\clac + ::: "memory"); + } + + asm volatile ( + \\cld + ::: "memory"); + + if (!has_error_code) { + asm volatile ("pushq $0" ::: "memory"); + } + + asm volatile ( + \\push %rdi + \\push %rsi + \\push %rdx + \\push %rcx + \\push %rax + \\push %r8 + \\push %r9 + \\push %r10 + \\push %r11 + \\push %rbx + \\push %rbp + \\push %r12 + \\push %r13 + \\push %r14 + \\push %r15 + \\mov %rsp, %rdi + \\mov %[interrupt_number], %rsi + \\call interruptHandler + \\pop %r15 + \\pop %r14 + \\pop %r13 + \\pop %r12 + \\pop %rbp + \\pop %rbx + \\pop %r11 + \\pop %r10 + \\pop %r9 + \\pop %r8 + \\pop %rax + \\pop %rcx + \\pop %rdx + \\pop %rsi + \\pop %rdi + : + : [interrupt_number] "i" (interrupt_number), + : "memory" + ); + + if (!has_error_code) { + asm volatile ( + \\add $0x8, %rsp + ::: "memory"); + } + + asm volatile ( + \\iretq + \\int3 + ::: "memory"); + + unreachable; + } + }.handler; +} + +const Interrupt = enum(u5) { + DE = 0x00, + DB = 0x01, + NMI = 0x02, + BP = 0x03, + OF = 0x04, + BR = 0x05, + UD = 0x06, + NM = 0x07, + DF = 0x08, + CSO = 0x09, // Not used anymore + TS = 0x0A, + NP = 0x0B, + SS = 0x0C, + GP = 0x0D, + PF = 0x0E, + MF = 0x10, + AC = 0x11, + MC = 0x12, + XM = 0x13, + VE = 0x14, + CP = 0x15, + _, +}; +const interrupt_handlers = [256]*const fn () callconv(.Naked) noreturn{ + InterruptHandler(@intFromEnum(Interrupt.DE), false), + InterruptHandler(@intFromEnum(Interrupt.DB), false), + InterruptHandler(@intFromEnum(Interrupt.NMI), false), + InterruptHandler(@intFromEnum(Interrupt.BP), false), + InterruptHandler(@intFromEnum(Interrupt.OF), false), + InterruptHandler(@intFromEnum(Interrupt.BR), false), + InterruptHandler(@intFromEnum(Interrupt.UD), false), + InterruptHandler(@intFromEnum(Interrupt.NM), false), + InterruptHandler(@intFromEnum(Interrupt.DF), true), + InterruptHandler(@intFromEnum(Interrupt.CSO), false), + InterruptHandler(@intFromEnum(Interrupt.TS), true), + InterruptHandler(@intFromEnum(Interrupt.NP), true), + InterruptHandler(@intFromEnum(Interrupt.SS), true), + InterruptHandler(@intFromEnum(Interrupt.GP), true), + InterruptHandler(@intFromEnum(Interrupt.PF), true), + InterruptHandler(0x0f, false), + InterruptHandler(@intFromEnum(Interrupt.MF), false), + InterruptHandler(@intFromEnum(Interrupt.AC), true), + InterruptHandler(@intFromEnum(Interrupt.MC), false), + InterruptHandler(@intFromEnum(Interrupt.XM), false), + InterruptHandler(@intFromEnum(Interrupt.VE), false), + InterruptHandler(@intFromEnum(Interrupt.CP), true), + InterruptHandler(0x16, false), + InterruptHandler(0x17, false), + InterruptHandler(0x18, false), + InterruptHandler(0x19, false), + InterruptHandler(0x1a, false), + InterruptHandler(0x1b, false), + InterruptHandler(0x1c, false), + InterruptHandler(0x1d, false), + InterruptHandler(0x1e, false), + InterruptHandler(0x1f, false), + InterruptHandler(0x20, false), + InterruptHandler(0x21, false), + InterruptHandler(0x22, false), + InterruptHandler(0x23, false), + InterruptHandler(0x24, false), + InterruptHandler(0x25, false), + InterruptHandler(0x26, false), + InterruptHandler(0x27, false), + InterruptHandler(0x28, false), + InterruptHandler(0x29, false), + InterruptHandler(0x2a, false), + InterruptHandler(0x2b, false), + InterruptHandler(0x2c, false), + InterruptHandler(0x2d, false), + InterruptHandler(0x2e, false), + InterruptHandler(0x2f, false), + InterruptHandler(0x30, false), + InterruptHandler(0x31, false), + InterruptHandler(0x32, false), + InterruptHandler(0x33, false), + InterruptHandler(0x34, false), + InterruptHandler(0x35, false), + InterruptHandler(0x36, false), + InterruptHandler(0x37, false), + InterruptHandler(0x38, false), + InterruptHandler(0x39, false), + InterruptHandler(0x3a, false), + InterruptHandler(0x3b, false), + InterruptHandler(0x3c, false), + InterruptHandler(0x3d, false), + InterruptHandler(0x3e, false), + InterruptHandler(0x3f, false), + InterruptHandler(0x40, false), + InterruptHandler(0x41, false), + InterruptHandler(0x42, false), + InterruptHandler(0x43, false), + InterruptHandler(0x44, false), + InterruptHandler(0x45, false), + InterruptHandler(0x46, false), + InterruptHandler(0x47, false), + InterruptHandler(0x48, false), + InterruptHandler(0x49, false), + InterruptHandler(0x4a, false), + InterruptHandler(0x4b, false), + InterruptHandler(0x4c, false), + InterruptHandler(0x4d, false), + InterruptHandler(0x4e, false), + InterruptHandler(0x4f, false), + InterruptHandler(0x50, false), + InterruptHandler(0x51, false), + InterruptHandler(0x52, false), + InterruptHandler(0x53, false), + InterruptHandler(0x54, false), + InterruptHandler(0x55, false), + InterruptHandler(0x56, false), + InterruptHandler(0x57, false), + InterruptHandler(0x58, false), + InterruptHandler(0x59, false), + InterruptHandler(0x5a, false), + InterruptHandler(0x5b, false), + InterruptHandler(0x5c, false), + InterruptHandler(0x5d, false), + InterruptHandler(0x5e, false), + InterruptHandler(0x5f, false), + InterruptHandler(0x60, false), + InterruptHandler(0x61, false), + InterruptHandler(0x62, false), + InterruptHandler(0x63, false), + InterruptHandler(0x64, false), + InterruptHandler(0x65, false), + InterruptHandler(0x66, false), + InterruptHandler(0x67, false), + InterruptHandler(0x68, false), + InterruptHandler(0x69, false), + InterruptHandler(0x6a, false), + InterruptHandler(0x6b, false), + InterruptHandler(0x6c, false), + InterruptHandler(0x6d, false), + InterruptHandler(0x6e, false), + InterruptHandler(0x6f, false), + InterruptHandler(0x70, false), + InterruptHandler(0x71, false), + InterruptHandler(0x72, false), + InterruptHandler(0x73, false), + InterruptHandler(0x74, false), + InterruptHandler(0x75, false), + InterruptHandler(0x76, false), + InterruptHandler(0x77, false), + InterruptHandler(0x78, false), + InterruptHandler(0x79, false), + InterruptHandler(0x7a, false), + InterruptHandler(0x7b, false), + InterruptHandler(0x7c, false), + InterruptHandler(0x7d, false), + InterruptHandler(0x7e, false), + InterruptHandler(0x7f, false), + InterruptHandler(0x80, false), + InterruptHandler(0x81, false), + InterruptHandler(0x82, false), + InterruptHandler(0x83, false), + InterruptHandler(0x84, false), + InterruptHandler(0x85, false), + InterruptHandler(0x86, false), + InterruptHandler(0x87, false), + InterruptHandler(0x88, false), + InterruptHandler(0x89, false), + InterruptHandler(0x8a, false), + InterruptHandler(0x8b, false), + InterruptHandler(0x8c, false), + InterruptHandler(0x8d, false), + InterruptHandler(0x8e, false), + InterruptHandler(0x8f, false), + InterruptHandler(0x90, false), + InterruptHandler(0x91, false), + InterruptHandler(0x92, false), + InterruptHandler(0x93, false), + InterruptHandler(0x94, false), + InterruptHandler(0x95, false), + InterruptHandler(0x96, false), + InterruptHandler(0x97, false), + InterruptHandler(0x98, false), + InterruptHandler(0x99, false), + InterruptHandler(0x9a, false), + InterruptHandler(0x9b, false), + InterruptHandler(0x9c, false), + InterruptHandler(0x9d, false), + InterruptHandler(0x9e, false), + InterruptHandler(0x9f, false), + InterruptHandler(0xa0, false), + InterruptHandler(0xa1, false), + InterruptHandler(0xa2, false), + InterruptHandler(0xa3, false), + InterruptHandler(0xa4, false), + InterruptHandler(0xa5, false), + InterruptHandler(0xa6, false), + InterruptHandler(0xa7, false), + InterruptHandler(0xa8, false), + InterruptHandler(0xa9, false), + InterruptHandler(0xaa, false), + InterruptHandler(0xab, false), + InterruptHandler(0xac, false), + InterruptHandler(0xad, false), + InterruptHandler(0xae, false), + InterruptHandler(0xaf, false), + InterruptHandler(0xb0, false), + InterruptHandler(0xb1, false), + InterruptHandler(0xb2, false), + InterruptHandler(0xb3, false), + InterruptHandler(0xb4, false), + InterruptHandler(0xb5, false), + InterruptHandler(0xb6, false), + InterruptHandler(0xb7, false), + InterruptHandler(0xb8, false), + InterruptHandler(0xb9, false), + InterruptHandler(0xba, false), + InterruptHandler(0xbb, false), + InterruptHandler(0xbc, false), + InterruptHandler(0xbd, false), + InterruptHandler(0xbe, false), + InterruptHandler(0xbf, false), + InterruptHandler(0xc0, false), + InterruptHandler(0xc1, false), + InterruptHandler(0xc2, false), + InterruptHandler(0xc3, false), + InterruptHandler(0xc4, false), + InterruptHandler(0xc5, false), + InterruptHandler(0xc6, false), + InterruptHandler(0xc7, false), + InterruptHandler(0xc8, false), + InterruptHandler(0xc9, false), + InterruptHandler(0xca, false), + InterruptHandler(0xcb, false), + InterruptHandler(0xcc, false), + InterruptHandler(0xcd, false), + InterruptHandler(0xce, false), + InterruptHandler(0xcf, false), + InterruptHandler(0xd0, false), + InterruptHandler(0xd1, false), + InterruptHandler(0xd2, false), + InterruptHandler(0xd3, false), + InterruptHandler(0xd4, false), + InterruptHandler(0xd5, false), + InterruptHandler(0xd6, false), + InterruptHandler(0xd7, false), + InterruptHandler(0xd8, false), + InterruptHandler(0xd9, false), + InterruptHandler(0xda, false), + InterruptHandler(0xdb, false), + InterruptHandler(0xdc, false), + InterruptHandler(0xdd, false), + InterruptHandler(0xde, false), + InterruptHandler(0xdf, false), + InterruptHandler(0xe0, false), + InterruptHandler(0xe1, false), + InterruptHandler(0xe2, false), + InterruptHandler(0xe3, false), + InterruptHandler(0xe4, false), + InterruptHandler(0xe5, false), + InterruptHandler(0xe6, false), + InterruptHandler(0xe7, false), + InterruptHandler(0xe8, false), + InterruptHandler(0xe9, false), + InterruptHandler(0xea, false), + InterruptHandler(0xeb, false), + InterruptHandler(0xec, false), + InterruptHandler(0xed, false), + InterruptHandler(0xee, false), + InterruptHandler(0xef, false), + InterruptHandler(0xf0, false), + InterruptHandler(0xf1, false), + InterruptHandler(0xf2, false), + InterruptHandler(0xf3, false), + InterruptHandler(0xf4, false), + InterruptHandler(0xf5, false), + InterruptHandler(0xf6, false), + InterruptHandler(0xf7, false), + InterruptHandler(0xf8, false), + InterruptHandler(0xf9, false), + InterruptHandler(0xfa, false), + InterruptHandler(0xfb, false), + InterruptHandler(0xfc, false), + InterruptHandler(0xfd, false), + InterruptHandler(0xfe, false), + InterruptHandler(0xff, false), +}; + +const BSPEarlyAllocator = extern struct { + base: PhysicalAddress, + size: usize, + offset: usize, + allocator: Allocator = .{ + .callbacks = .{ + .allocate = callbackAllocate, + }, + }, + heap_first: ?*BSPHeapEntry = null, + + const BSPHeapEntry = extern struct { + virtual_memory_region: VirtualMemoryRegion, + offset: usize = 0, + next: ?*BSPHeapEntry = null, + + // pub fn create(heap: *BSPHeapEntry, comptime T: type) !*T { + // _ = heap; + // @panic("TODO: create"); + // } + + pub fn allocateBytes(heap: *BSPHeapEntry, size: u64, alignment: u64) ![]u8 { + assert(alignment < lib.arch.valid_page_sizes[0]); + assert(heap.virtual_memory_region.size > size); + if (!lib.isAligned(heap.virtual_memory_region.address.value(), alignment)) { + const misalignment = lib.alignForward(usize, heap.virtual_memory_region.address.value(), alignment) - heap.virtual_memory_region.address.value(); + _ = heap.virtual_memory_region.takeSlice(misalignment); + } + + return heap.virtual_memory_region.takeByteSlice(size); + } + }; + + pub fn createPageAligned(allocator: *BSPEarlyAllocator, comptime T: type) AllocatorError!*align(lib.arch.valid_page_sizes[0]) T { + return @as(*align(lib.arch.valid_page_sizes[0]) T, @ptrCast(try allocator.allocateBytes(@sizeOf(T), lib.arch.valid_page_sizes[0]))); + } + + pub fn allocateBytes(allocator: *BSPEarlyAllocator, size: u64, alignment: u64) AllocatorError![]align(lib.arch.valid_page_sizes[0]) u8 { + if (!lib.isAligned(size, lib.arch.valid_page_sizes[0])) return AllocatorError.bad_alignment; + if (allocator.offset + size > allocator.size) return AllocatorError.out_of_memory; + + // TODO: don't trash memory + if (!lib.isAligned(allocator.base.offset(allocator.offset).value(), alignment)) { + const aligned = lib.alignForward(usize, allocator.base.offset(allocator.offset).value(), alignment); + allocator.offset += aligned - allocator.base.offset(allocator.offset).value(); + } + + const physical_address = allocator.base.offset(allocator.offset); + allocator.offset += size; + const slice = physical_address.toHigherHalfVirtualAddress().access([*]align(lib.arch.valid_page_sizes[0]) u8)[0..size]; + @memset(slice, 0); + + return slice; + } + + pub fn callbackAllocate(allocator: *Allocator, size: u64, alignment: u64) Allocator.Allocate.Error!Allocator.Allocate.Result { + const early_allocator = @fieldParentPtr(BSPEarlyAllocator, "allocator", allocator); + if (alignment == lib.arch.valid_page_sizes[0] or size % lib.arch.valid_page_sizes[0] == 0) { + const result = early_allocator.allocateBytes(size, alignment) catch return Allocator.Allocate.Error.OutOfMemory; + return .{ + .address = @intFromPtr(result.ptr), + .size = result.len, + }; + } else if (alignment > lib.arch.valid_page_sizes[0]) { + @panic("WTF"); + } else { + assert(size < lib.arch.valid_page_sizes[0]); + const heap_entry_allocation = early_allocator.allocateBytes(lib.arch.valid_page_sizes[0], lib.arch.valid_page_sizes[0]) catch return Allocator.Allocate.Error.OutOfMemory; + const heap_entry_region = VirtualMemoryRegion.fromByteSlice(.{ + .slice = heap_entry_allocation, + }); + const heap_entry = try early_allocator.addHeapRegion(heap_entry_region); + const result = try heap_entry.allocateBytes(size, alignment); + return .{ + .address = @intFromPtr(result.ptr), + .size = result.len, + }; + } + } + + inline fn addHeapRegion(early_allocator: *BSPEarlyAllocator, region: VirtualMemoryRegion) !*BSPHeapEntry { + const heap_entry = region.address.access(*BSPHeapEntry); + const offset = @sizeOf(BSPHeapEntry); + heap_entry.* = .{ + .offset = offset, + .virtual_memory_region = region.offset(offset), + .next = early_allocator.heap_first, + }; + + early_allocator.heap_first = heap_entry; + + return heap_entry; + } + const AllocatorError = error{ + out_of_memory, + bad_alignment, + }; +}; + +const half_page_table_entry_count = @divExact(paging.page_table_entry_count, 2); + +fn spawnInitBSP(init_file: []const u8, cpu_page_tables: paging.CPUPageTables) !noreturn { + const spawn_init = try spawnInitCommon(cpu_page_tables); + const init_scheduler = spawn_init.scheduler; + const page_table_regions = spawn_init.page_table_regions; + + // TODO: make this the right one + const address_space = page_table_regions.getAddressSpace(); + const init_elf = try ELF.Parser.init(init_file); + const entry_point = init_elf.getEntryPoint(); + const program_headers = init_elf.getProgramHeaders(); + const scheduler_common = init_scheduler.common; + + for (program_headers) |program_header| { + if (program_header.type == .load) { + const aligned_size = lib.alignForward(usize, program_header.size_in_memory, lib.arch.valid_page_sizes[0]); + const segment_virtual_address = VirtualAddress.new(program_header.virtual_address); + const indexed_virtual_address = @as(paging.IndexedVirtualAddress, @bitCast(program_header.virtual_address)); + _ = indexed_virtual_address; + const segment_flags = .{ + .execute_disable = !program_header.flags.executable, + .write = program_header.flags.writable, + .user = true, + }; + + const segment_physical_region = try cpu.driver.getRootCapability().allocatePages(aligned_size); + try page_table_regions.map(segment_virtual_address, segment_physical_region.address, segment_physical_region.size, segment_flags); + + const src = init_file[program_header.offset..][0..program_header.size_in_file]; + const dst = segment_physical_region.toHigherHalfVirtualAddress().access(u8)[0..program_header.size_in_file]; + @memcpy(dst, src); + } + } + + // Once all page tables are set up, copy lower half of the address space to the cpu page table + const cpu_pml4 = page_table_regions.getCpuPML4(); + const user_pml4 = page_table_regions.getUserPML4(); + @memcpy(cpu_pml4[0..half_page_table_entry_count], user_pml4[0..half_page_table_entry_count]); + + cpu.user_scheduler = init_scheduler; + address_space.cr3.write(); + + scheduler_common.self = scheduler_common; + + const scheduler_common_arch = scheduler_common.architectureSpecific(); + + // Set arguments + + // First argument + scheduler_common_arch.disabled_save_area.registers.rdi = user_scheduler_virtual_address.value(); + // Second argument + const is_init = true; + scheduler_common_arch.disabled_save_area.registers.rsi = @intFromBool(is_init); + + scheduler_common_arch.disabled_save_area.registers.rip = entry_point; // Set entry point + scheduler_common_arch.disabled_save_area.registers.rsp = user_scheduler_virtual_address.offset(@offsetOf(rise.UserScheduler, "setup_stack")).value() + scheduler_common_arch.generic.setup_stack.len; + scheduler_common.setup_stack_lock.value = true; + scheduler_common_arch.disabled_save_area.registers.rflags = .{ .IF = true }; // Set RFLAGS + + scheduler_common_arch.disabled_save_area.fpu.fcw = 0x037f; // Set FPU + scheduler_common_arch.disabled_save_area.fpu.mxcsr = 0x1f80; + + scheduler_common_arch.disabled_save_area.contextSwitch(); +} + +const UserMemory = extern struct { + root: PhysicalMemoryRegion, + pdpt: PhysicalMemoryRegion, + pdt: PhysicalMemoryRegion, + pt: PhysicalMemoryRegion, +}; + +const PageTableRegions = extern struct { + regions: [region_count]PhysicalMemoryRegion, + total: PhysicalMemoryRegion, + base_virtual_address: VirtualAddress, + + fn mapQuick(page_table_regions: PageTableRegions, virtual_address: VirtualAddress, physical_address: PhysicalAddress, size: usize, flags: paging.MemoryFlags) void { + const ptes = page_table_regions.getPageTables(.{ .index = .pt }); + // log.debug("PTE base: 0x{x}", .{@ptrToInt(ptes.ptr)}); + assert(lib.isAligned(size, lib.arch.valid_page_sizes[0])); + const indexed = @as(paging.IndexedVirtualAddress, @bitCast(virtual_address.value())); + const base_indexed = @as(paging.IndexedVirtualAddress, @bitCast(page_table_regions.base_virtual_address.value())); + const physical_base = physical_address.value(); + var physical_iterator = physical_base; + const physical_top = physical_base + size; + const pd_offset_index = indexed.PD - base_indexed.PD; + // log.debug("PD index: {}. PD offset index: {}", .{ indexed.PD, pd_offset_index }); + var index = @as(usize, pd_offset_index) * paging.page_table_entry_count + indexed.PT; + // log.debug("Virtual address: 0x{x}. Size: 0x{x}. Index: {}. PD: {}. PT: {}", .{ virtual_address.value(), size, index, indexed.PD, indexed.PT }); + + while (physical_iterator < physical_top) : ({ + physical_iterator += lib.arch.valid_page_sizes[0]; + index += 1; + }) { + ptes[index] = paging.getPageEntry(paging.PTE, physical_iterator, flags); + } + } + + fn map(page_table_regions: PageTableRegions, virtual_address: VirtualAddress, physical_address: PhysicalAddress, size: usize, flags: paging.MemoryFlags) !void { + // log.debug("Mapping 0x{x} -> 0x{x} for 0x{x} bytes", .{ virtual_address.value(), physical_address.value(), size }); + assert(page_table_regions.regions[@intFromEnum(Index.pml4)].size == 2 * lib.arch.valid_page_sizes[0]); + assert(page_table_regions.regions[@intFromEnum(Index.pdp)].size == lib.arch.valid_page_sizes[0]); + assert(page_table_regions.regions[@intFromEnum(Index.pd)].size == lib.arch.valid_page_sizes[0]); + + page_table_regions.mapQuick(virtual_address, physical_address, size, flags); + + const address_space = page_table_regions.getAddressSpace(); + const virtual_address_top = virtual_address.offset(size).value(); + var index: usize = 0; + + while (virtual_address.offset(index * lib.arch.valid_page_sizes[0]).value() < virtual_address_top) : (index += 1) { + const offset = index * lib.arch.valid_page_sizes[0]; + const expected_pa = physical_address.offset(offset); + const va = virtual_address.offset(offset); + + const translated_physical_address = address_space.translateAddress(va, flags) catch |err| { + panic("Mapping of 0x{x} failed: {}", .{ va.value(), err }); + }; + + if (translated_physical_address.value() != expected_pa.value()) { + @panic("Mapping failed"); + } + } + } + + const region_count = lib.enumCount(Index); + const Index = enum(u2) { + pml4, + pdp, + pd, + pt, + }; + + const sizes = blk: { + const shifter = lib.arch.page_shifter(lib.arch.valid_page_sizes[0]); + var result: [region_count]comptime_int = undefined; + + for (&result, entry_count_array) |*size, entry_count| { + size.* = @divExact(entry_count, paging.page_table_entry_count) << shifter; + } + + break :blk result; + }; + + const total_size = blk: { + var result: comptime_int = 0; + + for (sizes) |size| { + result += size; + } + + break :blk result; + }; + + const entry_count_array = blk: { + var result: [region_count]comptime_int = undefined; + + result[@intFromEnum(Index.pml4)] = 2 * paging.page_table_entry_count; + result[@intFromEnum(Index.pdp)] = init_vas_pdpe_count; + result[@intFromEnum(Index.pd)] = init_vas_pde_count; + result[@intFromEnum(Index.pt)] = init_vas_pte_count; + + break :blk result; + }; + + const EntryType = blk: { + var result: [region_count]type = undefined; + result[@intFromEnum(Index.pml4)] = paging.PML4TE; + result[@intFromEnum(Index.pdp)] = paging.PDPTE; + result[@intFromEnum(Index.pd)] = paging.PDTE; + result[@intFromEnum(Index.pt)] = paging.PTE; + break :blk result; + }; + + const init_vas_size = 128 * lib.mb; + const init_vas_page_count = @divExact(init_vas_size, lib.arch.valid_page_sizes[0]); + + const init_vas_pte_count = init_vas_page_count; + const init_vas_pde_count = lib.alignForward(usize, @divExact(init_vas_pte_count, paging.page_table_entry_count), paging.page_table_entry_count); + const init_vas_pdpe_count = lib.alignForward(usize, @divExact(init_vas_pde_count, paging.page_table_entry_count), paging.page_table_entry_count); + + const AccessOptions = packed struct { + index: Index, + user: bool = true, + }; + + pub inline fn getPhysicalRegion(regions: PageTableRegions, comptime options: AccessOptions) PhysicalMemoryRegion { + const index = @intFromEnum(options.index); + const result = regions.regions[index].offset(switch (index) { + 0 => switch (options.user) { + true => paging.page_table_size, + false => 0, + }, + else => 0, + }); + + return switch (index) { + 0 => PhysicalMemoryRegion.new(.{ .address = result.address, .size = paging.page_table_size }), + else => result, + }; + } + + pub inline fn getPageTables(regions: PageTableRegions, comptime options: AccessOptions) []EntryType[@intFromEnum(options.index)] { + return regions.getPhysicalRegion(options).toHigherHalfVirtualAddress().access(EntryType[@intFromEnum(options.index)]); + } + + pub inline fn getAddressSpace(regions: PageTableRegions) paging.Specific { + const address_space = paging.Specific{ .cr3 = cr3.fromAddress(regions.getPhysicalRegion(.{ .index = .pml4, .user = true }).address) }; + return address_space; + } + + pub inline fn getPrivilegedAddressSpace(regions: PageTableRegions) paging.Specific { + const address_space = paging.Specific{ .cr3 = cr3.fromAddress(regions.getPhysicalRegion(.{ .index = .pml4, .user = false }).address) }; + return address_space; + } + + pub inline fn getCpuPML4(regions: PageTableRegions) *paging.PML4Table { + return regions.getPageTables(.{ .index = .pml4, .user = false })[0..paging.page_table_entry_count]; + } + + pub inline fn getUserPML4(regions: PageTableRegions) *paging.PML4Table { + return regions.getPageTables(.{ .index = .pml4, .user = true })[0..paging.page_table_entry_count]; + } +}; + +const SpawnInitCommonResult = extern struct { + page_table_regions: PageTableRegions, + scheduler: *cpu.UserScheduler, +}; + +const scheduler_memory_size = 1 << 19; +const dispatch_count = x86_64.IDT.entry_count; +var once: bool = false; + +fn spawnInitCommon(cpu_page_tables: paging.CPUPageTables) !SpawnInitCommonResult { + assert(!once); + once = true; + // TODO: delete in the future + assert(cpu.bsp); + cpu.driver.valid = true; + + const allocation: extern struct { + page_table_regions: PageTableRegions, + cpu_page_table_physical_region: PhysicalMemoryRegion, + } = blk: { + const page_table_regions_total_size = PageTableRegions.total_size; + const cpu_page_table_size = (paging.Level.count - 1) * paging.page_table_size; + const allocation_size = page_table_regions_total_size + cpu_page_table_size; + const allocation_alignment = 2 * paging.page_table_alignment; + const total_region = try cpu.driver.getRootCapability().allocatePageCustomAlignment(allocation_size, allocation_alignment); + //log.debug("Total region: (0x{x}, 0x{x})", .{ total_region.address.value(), total_region.top().value() }); + var region_slicer = total_region; + var page_table_regions = PageTableRegions{ + .regions = undefined, + .total = total_region, + .base_virtual_address = user_scheduler_virtual_address, + }; + + inline for (&page_table_regions.regions, 0..) |*region, index| { + region.* = region_slicer.takeSlice(PageTableRegions.sizes[index]); + } + + assert(lib.isAligned(page_table_regions.regions[0].address.value(), 2 * paging.page_table_alignment)); + + assert(region_slicer.size == cpu_page_table_size); + + const cpu_page_table_physical_region = region_slicer; + + break :blk .{ + .page_table_regions = page_table_regions, + .cpu_page_table_physical_region = cpu_page_table_physical_region, + }; + }; + + const page_table_regions = allocation.page_table_regions; + const cpu_page_table_physical_region = allocation.cpu_page_table_physical_region; + + const indexed_start = @as(paging.IndexedVirtualAddress, @bitCast(user_scheduler_virtual_address.value())); + const indexed_end = @as(paging.IndexedVirtualAddress, @bitCast(user_scheduler_virtual_address.offset(PageTableRegions.init_vas_size).value())); + // log.debug("Indexed start: {}", .{indexed_start}); + // log.debug("Indexed end: {}", .{indexed_end}); + page_table_regions.getPageTables(.{ + .index = .pml4, + .user = true, + })[indexed_start.PML4] = .{ + .present = true, + .write = true, + .user = true, + .address = paging.packAddress(paging.PML4TE, page_table_regions.getPhysicalRegion(.{ .index = .pdp }).address.value()), + }; + + page_table_regions.getPageTables(.{ .index = .pdp })[indexed_start.PDP] = .{ + .present = true, + .write = true, + .user = true, + .address = paging.packAddress(paging.PDPTE, page_table_regions.getPhysicalRegion(.{ .index = .pd }).address.value()), + }; + + const pdes = page_table_regions.getPageTables(.{ .index = .pd }); + // log.debug("PDE count: {}", .{pdes.len}); + //log.debug("PTE base: 0x{x}. PTE count: {}", .{ page_table_regions.get(.{ .index = .pt }).address.value(), page_table_regions.getPageTables(.{ .index = .pt }).len }); + + for (pdes[indexed_start.PD .. indexed_start.PD + indexed_end.PD], 0..) |*pde, pde_offset| { + const pte_index = paging.page_table_entry_count * pde_offset; + const pte_offset = pte_index * paging.page_table_entry_size; + const pte_address = page_table_regions.getPhysicalRegion(.{ .index = .pt }).offset(pte_offset).address.value(); + // log.debug("Linking PDE[{}] 0x{x} with PTE base address: 0x{x} (pte index: {}. pte offset: 0x{x})", .{ pde_offset, @ptrToInt(pde), pte_address, pte_index, pte_offset }); + pde.* = paging.PDTE{ + .present = true, + .write = true, + .user = true, + .address = paging.packAddress(paging.PDTE, pte_address), + }; + } + + const scheduler_memory_physical_region = try cpu.driver.getRootCapability().allocatePages(scheduler_memory_size); + const scheduler_memory_map_flags = .{ + .present = true, + .write = true, + .user = true, + .execute_disable = true, + }; + + try page_table_regions.map(user_scheduler_memory_start_virtual_address, scheduler_memory_physical_region.address, scheduler_memory_physical_region.size, scheduler_memory_map_flags); + + const root_page_tables = [2]PhysicalMemoryRegion{ + page_table_regions.getPhysicalRegion(.{ .index = .pml4, .user = false }), + page_table_regions.getPhysicalRegion(.{ .index = .pml4, .user = true }), + }; + // log.debug("Root page tables: {any}", .{root_page_tables}); + assert(root_page_tables[0].size == lib.arch.valid_page_sizes[0]); + + // Map CPU driver into the CPU page table + var cpu_page_table_physical_region_iterator = cpu_page_table_physical_region; + // log.debug("CPU page table physical region: 0x{x} - 0x{x}", .{ cpu_page_table_physical_region.address.value(), cpu_page_table_physical_region.top().value() }); + + const cpu_pte_count = paging.page_table_entry_count - paging.CPUPageTables.left_ptables; + const cpu_ptes = cpu_page_tables.p_table.toHigherHalfVirtualAddress().access(*paging.PTable)[0..cpu_pte_count]; + const user_mapped_cpu_pte_offset = (paging.Level.count - 2) * paging.page_table_size; + // log.debug("[OFFSET] 0x{x}", .{user_mapped_cpu_pte_offset}); + const user_mapped_cpu_ptes = cpu_page_table_physical_region.offset(user_mapped_cpu_pte_offset).toHigherHalfVirtualAddress().access(paging.PTE)[0..cpu_pte_count]; + @memcpy(user_mapped_cpu_ptes, cpu_ptes); + + const user_root_page_table_region = root_page_tables[1]; + const RootPageTableEntryType = paging.EntryTypeMap(lib.arch.valid_page_sizes[1])[@intFromEnum(x86_64.root_page_table_entry)]; + user_root_page_table_region.toHigherHalfVirtualAddress().access(paging.PML4TE)[paging.CPUPageTables.pml4_index] = paging.PML4TE{ + .present = true, + .write = true, + .execute_disable = false, + .address = paging.packAddress(RootPageTableEntryType, cpu_page_table_physical_region.offset(0).address.value()), + }; + + const current_address_space = paging.Specific{ .cr3 = cr3.read() }; + const src_half = (try current_address_space.getPML4TableUnchecked())[half_page_table_entry_count..][0..half_page_table_entry_count]; + @memcpy(root_page_tables[0].toHigherHalfVirtualAddress().access(paging.PML4TE)[half_page_table_entry_count..][0..half_page_table_entry_count], src_half); + + const pdp = cpu_page_table_physical_region_iterator.takeSlice(paging.page_table_size); + const pd = cpu_page_table_physical_region_iterator.takeSlice(paging.page_table_size); + const pt = cpu_page_table_physical_region_iterator.takeSlice(paging.page_table_size); + assert(cpu_page_table_physical_region_iterator.size == 0); + + const pdp_table = pdp.toHigherHalfVirtualAddress().access(paging.PDPTE); + // log.debug("pdp index: {}. pdp table: 0x{x}", .{ paging.CPUPageTables.pdp_index, @ptrToInt(pdp_table.ptr) }); + pdp_table[paging.CPUPageTables.pdp_index] = paging.PDPTE{ + .present = true, + .write = true, + .execute_disable = false, + .address = paging.packAddress(paging.PDPTE, pd.address.value()), + }; + + const pd_table = pd.toHigherHalfVirtualAddress().access(paging.PDTE); + pd_table[paging.CPUPageTables.pd_index] = paging.PDTE{ + .present = true, + .write = true, + .execute_disable = false, + .address = paging.packAddress(paging.PDTE, pt.address.value()), + }; + + const supporting_page_table_size = PageTableRegions.total_size; + _ = supporting_page_table_size; + const indexed_base = @as(paging.IndexedVirtualAddress, @bitCast(page_table_regions.total.address.toHigherHalfVirtualAddress().value())); + const indexed_top = @as(paging.IndexedVirtualAddress, @bitCast(page_table_regions.total.top().toHigherHalfVirtualAddress().value())); + const diff = @as(u64, @bitCast(indexed_top)) - @as(u64, @bitCast(indexed_base)); + // log.debug("Mapping 0x{x} - 0x{x} to higher half", .{ page_table_regions.total.address.value(), page_table_regions.total.top().value() }); + // log.debug("supporting_page_table_size: {}", .{supporting_page_table_size}); + // log.debug("\nBASE: {}\n\nTOP: {}\n\n", .{ indexed_base, indexed_top }); + + assert(indexed_base.PML4 == indexed_top.PML4); + assert(indexed_base.PDP == indexed_top.PDP); + const ptable_count = indexed_top.PD - indexed_base.PD + 1; + + const cpu_indexed_base = @as(paging.IndexedVirtualAddress, @bitCast(cpu_page_table_physical_region.toHigherHalfVirtualAddress().address.value())); + const cpu_indexed_top = @as(paging.IndexedVirtualAddress, @bitCast(cpu_page_table_physical_region.toHigherHalfVirtualAddress().top().value())); + const cpu_diff = @as(u64, @bitCast(cpu_indexed_top)) - @as(u64, @bitCast(cpu_indexed_base)); + // log.debug("\nCPU BASE: {}\n\nCPU TOP: {}\n\n", .{ cpu_indexed_base, cpu_indexed_top }); + assert(cpu_indexed_base.PML4 == cpu_indexed_top.PML4); + assert(cpu_indexed_base.PDP == cpu_indexed_top.PDP); + assert(cpu_indexed_base.PDP == indexed_base.PDP); + assert(cpu_indexed_base.PD == cpu_indexed_top.PD); + assert(cpu_indexed_base.PT < cpu_indexed_top.PT); + assert(cpu_indexed_base.PML4 == indexed_base.PML4); + assert(cpu_indexed_base.PDP == indexed_base.PDP); + const cpu_ptable_count = cpu_indexed_top.PD - cpu_indexed_base.PD + 1; + assert(cpu_ptable_count <= ptable_count); + + const support_pdp_table_count = 1; + const support_pd_table_count = 1; + const min = @min(@as(u64, @bitCast(indexed_base)), @as(u64, @bitCast(cpu_indexed_base))); + const max = @max(@as(u64, @bitCast(indexed_top)), @as(u64, @bitCast(cpu_indexed_top))); + const min_indexed = @as(paging.IndexedVirtualAddress, @bitCast(min)); + const general_diff = max - min; + const pte_count = @divExact(general_diff, lib.arch.valid_page_sizes[0]); + const support_p_table_count = 1 + pte_count / paging.page_table_entry_count + @intFromBool(@as(usize, paging.page_table_entry_count) - min_indexed.PT < pte_count); + // log.debug("Support p table count: {}", .{support_p_table_count}); + // log.debug("indexed base: 0x{x}. top: 0x{x}", .{ @bitCast(u64, indexed_base), @bitCast(u64, indexed_top) }); + // log.debug("cpu indexed base: 0x{x}. top: 0x{x}", .{ @bitCast(u64, cpu_indexed_base), @bitCast(u64, cpu_indexed_top) }); + + const support_page_table_count = @as(usize, support_pdp_table_count + support_pd_table_count + support_p_table_count); + const support_page_table_physical_region = try cpu.driver.getRootCapability().allocatePages(support_page_table_count * paging.page_table_size); + // log.debug("Support page tables: 0x{x} - 0x{x}", .{ support_page_table_physical_region.address.value(), support_page_table_physical_region.top().value() }); + // log.debug("PD table count: {}. P table count: {}", .{ support_pd_table_count, support_p_table_count }); + + const support_pdp_offset = 0; + const support_pd_offset = support_pdp_table_count * paging.page_table_size; + const support_pt_offset = support_pd_offset + support_pd_table_count * paging.page_table_size; + + const support_pml4 = page_table_regions.getPageTables(.{ .user = true, .index = .pml4 }); + const support_pdp_region = support_page_table_physical_region.offset(support_pdp_offset); + const support_pd_region = support_page_table_physical_region.offset(support_pd_offset); + const support_pt_region = support_page_table_physical_region.offset(support_pt_offset); + + assert(!support_pml4[indexed_base.PML4].present); + assert(support_pdp_table_count == 1); + + support_pml4[indexed_base.PML4] = paging.PML4TE{ + .present = true, + .write = true, + .address = paging.packAddress(paging.PML4TE, support_pdp_region.address.value()), + }; + + const support_pdp = support_pdp_region.toHigherHalfVirtualAddress().access(paging.PDPTE); + assert(!support_pdp[indexed_base.PDP].present); + assert(support_pd_table_count == 1); + + support_pdp[indexed_base.PDP] = paging.PDPTE{ + .present = true, + .write = true, + .address = paging.packAddress(paging.PDPTE, support_pd_region.address.value()), + }; + + const support_pd = support_pd_region.toHigherHalfVirtualAddress().access(paging.PDTE); + assert(!support_pd[indexed_base.PD].present); + assert(indexed_base.PD <= cpu_indexed_base.PD); + + for (0..support_p_table_count) |i| { + const pd_index = indexed_base.PD + i; + const p_table_physical_region = support_pt_region.offset(i * paging.page_table_size); + support_pd[pd_index] = paging.PDTE{ + .present = true, + .write = true, + .address = paging.packAddress(paging.PDTE, p_table_physical_region.address.value()), + }; + } + + const support_ptes = support_pt_region.toHigherHalfVirtualAddress().access(paging.PTE); + for (0..@divExact(diff, lib.arch.valid_page_sizes[0])) |page_index| { + support_ptes[indexed_base.PT + page_index] = paging.getPageEntry(paging.PTE, page_table_regions.total.offset(page_index * lib.arch.valid_page_sizes[0]).address.value(), .{ + .present = true, + .write = true, + }); + } + + for (0..@divExact(cpu_diff, lib.arch.valid_page_sizes[0])) |page_index| { + support_ptes[cpu_indexed_base.PT + page_index] = paging.getPageEntry(paging.PTE, cpu_page_table_physical_region.offset(page_index * lib.arch.valid_page_sizes[0]).address.value(), .{ + .present = true, + .write = true, + }); + } + + { + const privileged_stack_physical_region = try cpu.driver.getRootCapability().allocatePages(x86_64.capability_address_space_stack_size); + const indexed_privileged_stack = @as(paging.IndexedVirtualAddress, @bitCast(x86_64.capability_address_space_stack_address.value())); + const stack_last_page = x86_64.capability_address_space_stack_address.offset(x86_64.capability_address_space_stack_size - lib.arch.valid_page_sizes[0]); + const indexed_privileged_stack_last_page = @as(paging.IndexedVirtualAddress, @bitCast(stack_last_page.value())); + assert(indexed_privileged_stack.PD == indexed_privileged_stack_last_page.PD); + assert(indexed_privileged_stack.PT < indexed_privileged_stack_last_page.PT); + + const pml4te = &page_table_regions.getPageTables(.{ .index = .pml4, .user = false })[indexed_privileged_stack.PML4]; + assert(pml4te.present); + + const pdpte = &(try paging.accessPageTable(PhysicalAddress.new(paging.unpackAddress(pml4te)), *paging.PDPTable))[indexed_privileged_stack.PDP]; + assert(!pdpte.present); + const pd_table_physical_region = try cpu.driver.getRootCapability().allocatePages(paging.page_table_size); + pdpte.* = paging.PDPTE{ + .present = true, + .write = true, + .address = paging.packAddress(paging.PDTE, pd_table_physical_region.address.value()), + }; + + const pdte = &(try paging.accessPageTable(PhysicalAddress.new(paging.unpackAddress(pdpte)), *paging.PDTable))[indexed_privileged_stack.PD]; + assert(!pdte.present); + const p_table_physical_region = try cpu.driver.getRootCapability().allocatePages(paging.page_table_size); + pdte.* = paging.PDTE{ + .present = true, + .write = true, + .address = paging.packAddress(paging.PDTE, p_table_physical_region.address.value()), + }; + + const p_table = try paging.accessPageTable(PhysicalAddress.new(paging.unpackAddress(pdte)), *paging.PTable); + for (p_table[indexed_privileged_stack.PT .. @as(usize, indexed_privileged_stack_last_page.PT) + 1], 0..) |*pte, index| { + const physical_address = privileged_stack_physical_region.offset(index * paging.page_table_size).address; + pte.* = paging.getPageEntry(paging.PTE, physical_address.value(), .{ + .present = true, + .write = true, + }); + } + } + + const init_cpu_scheduler_physical_region = try cpu.driver.getRootCapability().allocatePages(@sizeOf(cpu.UserScheduler)); + const init_cpu_scheduler_virtual_region = init_cpu_scheduler_physical_region.toHigherHalfVirtualAddress(); + const init_cpu_scheduler = init_cpu_scheduler_virtual_region.address.access(*cpu.UserScheduler); + // log.debug("Init scheduler: 0x{x}", .{init_cpu_scheduler_virtual_region.address.value()}); + const cpu_scheduler_indexed = @as(paging.IndexedVirtualAddress, @bitCast(init_cpu_scheduler_virtual_region.address.value())); + // log.debug("CPU scheduler indexed: {}", .{cpu_scheduler_indexed}); + + assert(cpu_scheduler_indexed.PML4 == cpu_indexed_base.PML4); + + const scheduler_pml4te = &page_table_regions.getPageTables(.{ .index = .pml4, .user = true })[cpu_scheduler_indexed.PML4]; + assert(scheduler_pml4te.present); + + const scheduler_pdpte = &(try paging.accessPageTable(PhysicalAddress.new(paging.unpackAddress(scheduler_pml4te)), *paging.PDPTable))[cpu_scheduler_indexed.PDP]; + + // Sanity checks + + const scheduler_pdte = blk: { + const pdp_is_inside = cpu_scheduler_indexed.PDP >= cpu_indexed_base.PDP and cpu_scheduler_indexed.PDP <= cpu_indexed_top.PDP; + // log.debug("PDP inside: {}", .{pdp_is_inside}); + assert(scheduler_pdpte.present == pdp_is_inside); + + if (!scheduler_pdpte.present) { + const pdte_allocation = try cpu.driver.getRootCapability().allocatePages(paging.page_table_size); + scheduler_pdpte.* = .{ + .present = true, + .write = true, + .address = paging.packAddress(@TypeOf(scheduler_pdpte.*), pdte_allocation.address.value()), + }; + } + + break :blk &(try paging.accessPageTable(PhysicalAddress.new(paging.unpackAddress(scheduler_pdpte)), *paging.PDTable))[cpu_scheduler_indexed.PD]; + }; + + const scheduler_pte = blk: { + const is_inside_cpu_page_table_limits = cpu_scheduler_indexed.PD >= cpu_indexed_base.PD and cpu_scheduler_indexed.PD <= cpu_indexed_top.PD; + assert(is_inside_cpu_page_table_limits == scheduler_pdte.present); + if (!scheduler_pdte.present) { + const pte_allocation = try cpu.driver.getRootCapability().allocatePages(paging.page_table_size); + scheduler_pdte.* = .{ + .present = true, + .write = true, + .address = paging.packAddress(@TypeOf(scheduler_pdte.*), pte_allocation.address.value()), + }; + } + + break :blk &(try paging.accessPageTable(PhysicalAddress.new(paging.unpackAddress(scheduler_pdte)), *paging.PTable))[cpu_scheduler_indexed.PT]; + }; + + scheduler_pte.* = paging.getPageEntry(paging.PTE, init_cpu_scheduler_physical_region.address.value(), .{ + .present = true, + .write = true, + }); + + init_cpu_scheduler.* = cpu.UserScheduler{ + .common = user_scheduler_virtual_address.access(*rise.UserScheduler), + .capability_root_node = cpu.capabilities.Root{ + .static = .{ + .cpu = true, + .boot = true, + .process = true, + }, + .dynamic = .{ + .io = .{ + .debug = true, + }, + .ram = cpu.driver.getRootCapability().dynamic.ram, + .cpu_memory = .{ + .flags = .{ + .allocate = true, + }, + }, + .page_table = .{}, + }, + .scheduler = .{ + .handle = init_cpu_scheduler, + .memory = scheduler_memory_physical_region, + }, + }, + }; + + const higher_half_scheduler_common = scheduler_memory_physical_region.address.toHigherHalfVirtualAddress().access(*rise.UserScheduler); + // log.debug("Higher half: 0x{x}", .{@ptrToInt(higher_half_scheduler_common)}); + higher_half_scheduler_common.disabled = true; + higher_half_scheduler_common.core_id = cpu.core_id; + + // log.debug("cpu scheduler: 0x{x}", .{@ptrToInt(init_cpu_scheduler)}); + + return SpawnInitCommonResult{ + .page_table_regions = page_table_regions, + .scheduler = init_cpu_scheduler, + }; +} diff --git a/src/cpu/arch/x86/64/linker_script.ld b/src/cpu/arch/x86/64/linker_script.ld new file mode 100644 index 0000000..69c7cdc --- /dev/null +++ b/src/cpu/arch/x86/64/linker_script.ld @@ -0,0 +1,39 @@ +PHDRS { + none PT_NULL FLAGS(0); + text PT_LOAD FLAGS((1 << 2) | (1 << 0) /* Readable | Executable */); + rodata PT_LOAD FLAGS((1 << 2) /* Readable */); + data PT_LOAD FLAGS((1 << 2) | (1 << 1) /* Readable | Writeable */); +} + +SECTIONS { + . = 0xFFFFFFFF80000000; + PROVIDE(cpu_driver_start = .); + + + PROVIDE(text_section_start = .); + .text . : { + *(.text*) + }:text + + . = ALIGN(4K); + PROVIDE(text_section_end = .); + + PROVIDE(rodata_section_start = .); + .rodata . : { + *(.rodata*) + }:rodata + + . = ALIGN(4K); + PROVIDE(rodata_section_end = .); + + PROVIDE(data_section_start = .); + .data . : { + *(.data*) + *(.bss*) + *(.got*) + }:data + + . = ALIGN(4K); + PROVIDE(data_section_end = .); + PROVIDE(cpu_driver_end = .); +} diff --git a/src/cpu/arch/x86/64/syscall.zig b/src/cpu/arch/x86/64/syscall.zig new file mode 100644 index 0000000..354281b --- /dev/null +++ b/src/cpu/arch/x86/64/syscall.zig @@ -0,0 +1,281 @@ +const cpu = @import("cpu"); +const lib = @import("lib"); +const log = lib.log; +const privileged = @import("privileged"); +const rise = @import("rise"); + +const assert = lib.assert; + +const cr3 = privileged.arch.x86_64.registers.cr3; + +const cr3_user_page_table_mask = 1 << @bitOffsetOf(cr3, "address"); +const cr3_user_page_table_and_pcid_mask = cr3_user_page_table_mask | pcid_mask; +const pcid_bit = 11; +const pcid_mask = 1 << pcid_bit; + +/// SYSCALL documentation +/// ABI: +/// - RAX: System call options (number for Linux) +/// - RCX: Return address +/// - R11: Saved rflags +/// - RDI: argument 0 +/// - RSI: argument 1 +/// - RDX: argument 2 +/// - R10: argument 3 +/// - R8: argument 4 +/// - R9: argument 5 +fn riseSyscall(comptime Syscall: type, raw_arguments: rise.syscall.Arguments) Syscall.ErrorSet.Error!Syscall.Result { + cpu.syscall_count += 1; + comptime assert(Syscall == rise.capabilities.Syscall(Syscall.capability, Syscall.command)); + const capability: rise.capabilities.Type = Syscall.capability; + const command: rise.capabilities.Command(capability) = Syscall.command; + const arguments = try Syscall.toArguments(raw_arguments); + + return if (cpu.user_scheduler.capability_root_node.hasPermissions(capability, command)) switch (capability) { + .io => switch (command) { + .copy, .mint, .retype, .delete, .revoke, .create => unreachable, + .log => blk: { + const message = arguments; + cpu.writer.writeAll(message) catch unreachable; + comptime assert(Syscall.Result == usize); + break :blk message.len; + }, + }, + .cpu => switch (command) { + .copy, .mint, .retype, .delete, .revoke, .create => unreachable, + .get_core_id => cpu.core_id, + .shutdown => cpu.shutdown(.success), + .get_command_buffer => { + const command_buffer = arguments; + _ = command_buffer; + @panic("TODO: get_command_buffer"); + }, + }, + .cpu_memory => switch (command) { + .allocate => blk: { + comptime assert(@TypeOf(arguments) == usize); + const size = arguments; + const physical_region = try cpu.user_scheduler.capability_root_node.allocatePages(size); + try cpu.user_scheduler.capability_root_node.allocateCPUMemory(physical_region, .{ .privileged = false }); + break :blk physical_region.address; + }, + else => @panic(@tagName(command)), + }, + .ram => unreachable, + .boot => switch (command) { + .get_bundle_size => cpu.bundle.len, + .get_bundle_file_list_size => cpu.bundle_files.len, + else => @panic(@tagName(command)), + }, + .process => switch (command) { + .exit => switch (arguments) { + true => cpu.shutdown(.success), + false => cpu.panic("User process panicked", .{}), + }, + else => @panic(@tagName(command)), + }, + .page_table => @panic("TODO: page_table"), + } else error.forbidden; +} + +export fn syscall(registers: *const Registers) callconv(.C) rise.syscall.Result { + const options = @as(rise.syscall.Options, @bitCast(registers.syscall_number)); + const arguments = rise.syscall.Arguments{ registers.rdi, registers.rsi, registers.rdx, registers.r10, registers.r8, registers.r9 }; + + return switch (options.general.convention) { + .rise => switch (options.rise.type) { + inline else => |capability| switch (@as(rise.capabilities.Command(capability), @enumFromInt(options.rise.command))) { + inline else => |command| blk: { + const Syscall = rise.capabilities.Syscall(capability, command); + const result: Syscall.Result = riseSyscall(Syscall, arguments) catch |err| break :blk Syscall.errorToRaw(err); + break :blk Syscall.resultToRaw(result); + }, + }, + }, + .linux => @panic("linux syscall"), + }; +} + +/// SYSCALL documentation +/// ABI: +/// - RAX: System call number +/// - RCX: Return address +/// - R11: Saved rflags +/// - RDI: argument 0 +/// - RSI: argument 1 +/// - RDX: argument 2 +/// - R10: argument 3 +/// - R8: argument 4 +/// - R9: argument 5 +pub fn entryPoint() callconv(.Naked) void { + asm volatile ( + \\endbr64 + \\swapgs + \\movq %rsp, user_stack(%rip) + ); + + if (cpu.arch.x86_64.kpti) { + asm volatile ( + \\mov %cr3, %rsp + ::: "memory"); + + if (cpu.arch.pcid) { + @compileError("pcid support not yet implemented"); + } + + asm volatile ( + \\andq %[mask], %rsp + \\mov %rsp, %cr3 + : + : [mask] "i" (~@as(u64, cr3_user_page_table_and_pcid_mask)), + : "memory" + ); + } + + // Safe stack + asm volatile ("movabsq %[capability_address_space_stack_top], %rsp" + : + : [capability_address_space_stack_top] "i" (cpu.arch.x86_64.capability_address_space_stack_top), + : "memory", "rsp" + ); + + asm volatile ( + \\pushq %[user_ds] + \\pushq (user_stack) + \\pushq %r11 + \\pushq %[user_cs] + \\pushq %rcx + \\pushq %rax + : + : [user_ds] "i" (cpu.arch.x86_64.user_data_selector), + [user_cs] "i" (cpu.arch.x86_64.user_code_selector), + : "memory" + ); + + // Push and clear registers + asm volatile ( + // Push + \\pushq %rdi + \\pushq %rsi + \\pushq %rdx + \\pushq %rcx + \\pushq %rax + \\pushq %r8 + \\pushq %r9 + \\pushq %r10 + \\pushq %r11 + \\pushq %rbx + \\pushq %rbp + \\pushq %r12 + \\pushq %r13 + \\pushq %r14 + \\pushq %r15 + // Clear + \\xorl %esi, %esi + \\xorl %edx, %edx + \\xorl %ecx, %ecx + \\xorl %r8d, %r8d + \\xorl %r9d, %r9d + \\xorl %r10d, %r10d + \\xorl %r11d, %r11d + \\xorl %ebx, %ebx + \\xorl %ebp, %ebp + \\xorl %r12d, %r12d + \\xorl %r13d, %r13d + \\xorl %r14d, %r14d + \\xorl %r15d, %r15d + ::: "memory"); + + // Pass arguments + asm volatile ( + \\mov %rsp, %rdi + \\mov %rax, %rsi + ::: "memory"); + + // TODO: more security stuff + asm volatile ( + \\call syscall + ::: "memory"); + + // TODO: more security stuff + + // Pop registers + asm volatile ( + \\popq %r15 + \\popq %r14 + \\popq %r13 + \\popq %r12 + \\popq %rbp + \\popq %rbx + \\popq %r11 + \\popq %r10 + \\popq %r9 + \\popq %r8 + \\popq %rcx + // RAX + \\popq %rcx + // RDX + \\popq %rsi + \\popq %rsi + \\popq %rdi + ::: "memory"); + + if (cpu.arch.x86_64.kpti) { + // Restore CR3 + asm volatile ( + \\mov %cr3, %rsp + ::: "memory"); + + if (cpu.arch.x86_64.pcid) { + @compileError("PCID not supported yet"); + } + + asm volatile ( + \\orq %[user_cr3_mask], %rsp + \\mov %rsp, %cr3 + : + : [user_cr3_mask] "i" (cr3_user_page_table_mask), + : "memory" + ); + } + + // Restore RSP + asm volatile ( + \\mov user_stack(%rip), %rsp + ::: "memory"); + + asm volatile ( + \\swapgs + \\sysretq + ::: "memory"); + + asm volatile ( + \\int3 + ::: "memory"); + + unreachable; +} + +pub const Registers = extern struct { + r15: u64, + r14: u64, + r13: u64, + r12: u64, + rbp: u64, + rbx: u64, + r11: u64, + r10: u64, + r9: u64, + r8: u64, + rax: u64, + rcx: u64, + rdx: u64, + rsi: u64, + rdi: u64, + syscall_number: u64, + rip: u64, + cs: u64, + rflags: u64, + rsp: u64, + ss: u64, +}; diff --git a/src/cpu/arch/x86_64.zig b/src/cpu/arch/x86_64.zig new file mode 100644 index 0000000..90044dd --- /dev/null +++ b/src/cpu/arch/x86_64.zig @@ -0,0 +1,265 @@ +const lib = @import("lib"); +const Allocator = lib.Allocator; +const assert = lib.assert; +const ELF = lib.ELF(64); +const log = lib.log; +const Spinlock = lib.Spinlock; +const bootloader = @import("bootloader"); +const privileged = @import("privileged"); +const panic = cpu.panic; +const PageAllocator = cpu.PageAllocator; +const x86_64 = privileged.arch.x86_64; +const APIC = x86_64.APIC; +const paging = x86_64.paging; +const cr0 = x86_64.registers.cr0; +const cr3 = x86_64.registers.cr3; +const cr4 = x86_64.registers.cr4; +const PhysicalAddress = lib.PhysicalAddress; +const PhysicalMemoryRegion = lib.PhysicalMemoryRegion; +const VirtualAddress = lib.VirtualAddress; +const VirtualMemoryRegion = lib.VirtualMemoryRegion; + +const cpu = @import("cpu"); +const Heap = cpu.Heap; + +const init = @import("./x86/64/init.zig"); +pub const syscall = @import("./x86/64/syscall.zig"); +pub const entryPoint = init.entryPoint; + +const rise = @import("rise"); + +var writer_lock: Spinlock = .released; + +pub const Registers = extern struct { + r15: u64, + r14: u64, + r13: u64, + r12: u64, + rbp: u64, + rbx: u64, + r11: u64, + r10: u64, + r9: u64, + r8: u64, + rax: u64, + rcx: u64, + rdx: u64, + rsi: u64, + rdi: u64, + syscall_number_or_error_code: u64, + rip: u64, + cs: u64, + rflags: lib.arch.x86_64.registers.RFLAGS, + rsp: u64, + ss: u64, +}; + +const interrupt_kind: u32 = 0; + +export fn interruptHandler(regs: *const InterruptRegisters, interrupt_number: u8) void { + switch (interrupt_number) { + local_timer_vector => { + APIC.write(.eoi, 0); + nextTimer(10); + }, + else => cpu.panicFromInstructionPointerAndFramePointer(regs.rip, regs.rbp, "Exception: 0x{x}", .{interrupt_number}), + } +} + +const InterruptRegisters = extern struct { + r15: u64, + r14: u64, + r13: u64, + r12: u64, + rbp: u64, + rbx: u64, + r11: u64, + r10: u64, + r9: u64, + r8: u64, + rax: u64, + rcx: u64, + rdx: u64, + rsi: u64, + rdi: u64, + error_code: u64, + rip: u64, + cs: u64, + rflags: u64, + rsp: u64, + ss: u64, +}; + +const local_timer_vector = 0xef; +pub export var ticks_per_ms: privileged.arch.x86_64.TicksPerMS = undefined; +pub inline fn nextTimer(ms: u32) void { + APIC.write(.lvt_timer, local_timer_vector | (1 << 17)); + APIC.write(.timer_initcnt, ticks_per_ms.lapic * ms); +} +pub const kpti = true; +pub const pcid = false; +pub const smap = false; +pub const invariant_tsc = false; +pub const capability_address_space_size = 1 * lib.gb; +pub const capability_address_space_start = capability_address_space_stack_top - capability_address_space_size; +pub const capability_address_space_stack_top = 0xffff_ffff_8000_0000; +pub const capability_address_space_stack_size = privileged.default_stack_size; +pub const capability_address_space_stack_alignment = lib.arch.valid_page_sizes[0]; +pub const capability_address_space_stack_address = VirtualAddress.new(capability_address_space_stack_top - capability_address_space_stack_size); +pub const code_64 = @offsetOf(GDT, "code_64"); +pub const data_64 = @offsetOf(GDT, "data_64"); +pub const user_code_64 = @offsetOf(GDT, "user_code_64"); +pub const user_data_64 = @offsetOf(GDT, "user_data_64"); +pub const tss_selector = @offsetOf(GDT, "tss_descriptor"); +pub const user_code_selector = user_code_64 | user_dpl; +pub const user_data_selector = user_data_64 | user_dpl; +pub const user_dpl = 3; + +pub const GDT = extern struct { + null: Entry = GDT.Entry.null_entry, // 0x00 + code_16: Entry = GDT.Entry.code_16, // 0x08 + data_16: Entry = GDT.Entry.data_16, // 0x10 + code_32: Entry = GDT.Entry.code_32, // 0x18 + data_32: Entry = GDT.Entry.data_32, // 0x20 + code_64: u64 = 0x00A09A0000000000, // 0x28 + data_64: u64 = 0x0000920000000000, // 0x30 + user_data_64: u64 = @as(u64, 0x0000920000000000) | (3 << 45), //GDT.Entry.user_data_64, // 0x38 + user_code_64: u64 = @as(u64, 0x00A09A0000000000) | (3 << 45), //GDT.Entry.user_code_64, // 0x40 + tss_descriptor: TSS.Descriptor = undefined, // 0x48 + + const Entry = privileged.arch.x86_64.GDT.Entry; + + pub const Descriptor = privileged.arch.x86_64.GDT.Descriptor; + + comptime { + const entry_count = 9; + const target_size = entry_count * @sizeOf(Entry) + @sizeOf(TSS.Descriptor); + + assert(@sizeOf(GDT) == target_size); + assert(@offsetOf(GDT, "code_64") == 0x28); + assert(@offsetOf(GDT, "data_64") == 0x30); + assert(@offsetOf(GDT, "user_data_64") == 0x38); + assert(@offsetOf(GDT, "user_code_64") == 0x40); + assert(@offsetOf(GDT, "tss_descriptor") == entry_count * @sizeOf(Entry)); + } + + pub fn getDescriptor(global_descriptor_table: *const GDT) GDT.Descriptor { + return .{ + .limit = @sizeOf(GDT) - 1, + .address = @intFromPtr(global_descriptor_table), + }; + } +}; + +pub const SystemSegmentDescriptor = extern struct { + const Type = enum(u4) { + ldt = 0b0010, + tss_available = 0b1001, + tss_busy = 0b1011, + call_gate = 0b1100, + interrupt_gate = 0b1110, + trap_gate = 0b1111, + }; +}; + +pub const TSS = extern struct { + reserved0: u32 = 0, + rsp: [3]u64 align(4) = [3]u64{ 0, 0, 0 }, + reserved1: u64 align(4) = 0, + IST: [7]u64 align(4) = [7]u64{ 0, 0, 0, 0, 0, 0, 0 }, + reserved3: u64 align(4) = 0, + reserved4: u16 = 0, + IO_map_base_address: u16 = 104, + + comptime { + assert(@sizeOf(TSS) == 104); + } + + pub const Descriptor = extern struct { + limit_low: u16, + base_low: u16, + base_mid_low: u8, + access: Access, + attributes: Attributes, + base_mid_high: u8, + base_high: u32, + reserved: u32 = 0, + + pub const Access = packed struct(u8) { + type: SystemSegmentDescriptor.Type, + reserved: u1 = 0, + dpl: u2, + present: bool, + }; + + pub const Attributes = packed struct(u8) { + limit: u4, + available_for_system_software: bool, + reserved: u2 = 0, + granularity: bool, + }; + + comptime { + assert(@sizeOf(TSS.Descriptor) == 0x10); + } + }; + + pub fn getDescriptor(tss_struct: *const TSS, offset: u64) Descriptor { + const address = @intFromPtr(tss_struct) + offset; + return Descriptor{ + .low = .{ + .limit_low = @as(u16, @truncate(@sizeOf(TSS) - 1)), + .base_low = @as(u16, @truncate(address)), + .base_low_mid = @as(u8, @truncate(address >> 16)), + .type = 0b1001, + .descriptor_privilege_level = 0, + .present = 1, + .limit_high = 0, + .available_for_system_software = 0, + .granularity = 0, + .base_mid = @as(u8, @truncate(address >> 24)), + }, + .base_high = @as(u32, @truncate(address >> 32)), + }; + } +}; + +pub const IDT = extern struct { + descriptors: [entry_count]GateDescriptor = undefined, + pub const Descriptor = privileged.arch.x86_64.SegmentDescriptor; + pub const GateDescriptor = extern struct { + offset_low: u16, + segment_selector: u16, + flags: packed struct(u16) { + ist: u3, + reserved: u5 = 0, + type: SystemSegmentDescriptor.Type, + reserved1: u1 = 0, + dpl: u2, + present: bool, + }, + offset_mid: u16, + offset_high: u32, + reserved: u32 = 0, + + comptime { + assert(@sizeOf(@This()) == 0x10); + } + }; + pub const entry_count = 256; +}; + +pub inline fn writerStart() void { + writer_lock.acquire(); +} + +pub inline fn writerEnd() void { + writer_lock.release(); +} + +pub const PageTableEntry = paging.Level; +pub const root_page_table_entry = @as(cpu.arch.PageTableEntry, @enumFromInt(0)); + +pub const IOMap = extern struct { + debug: bool, +}; diff --git a/src/cpu/capabilities.zig b/src/cpu/capabilities.zig new file mode 100644 index 0000000..ae8ea8f --- /dev/null +++ b/src/cpu/capabilities.zig @@ -0,0 +1,419 @@ +const lib = @import("lib"); +const assert = lib.assert; +const Allocator = lib.Allocator; +const enumCount = lib.enumCount; +const log = lib.log.scoped(.capabilities); + +const privileged = @import("privileged"); +const PhysicalAddress = lib.PhysicalAddress; +const PhysicalMemoryRegion = lib.PhysicalMemoryRegion; +const rise = @import("rise"); +const cpu = @import("cpu"); + +pub const RootDescriptor = extern struct { + value: *Root, +}; + +pub const Static = enum { + cpu, + boot, + process, + + pub const count = lib.enumCount(@This()); + + pub const Bitmap = @Type(.{ + .Struct = blk: { + const full_bit_size = @max(@as(comptime_int, 1 << 3), @as(u8, @sizeOf(Static)) << 3); + break :blk .{ + .layout = .Packed, + .backing_integer = lib.IntType(.unsigned, full_bit_size), + .fields = fields: { + var fields: []const lib.Type.StructField = &.{}; + inline for (lib.enumFields(Static)) |static_field| { + fields = fields ++ [1]lib.Type.StructField{.{ + .name = static_field.name, + .type = bool, + .default_value = null, + .is_comptime = false, + .alignment = 0, + }}; + } + + assert(Static.count > 0); + assert(@sizeOf(Static) > 0 or Static.count == 1); + + const padding_type = lib.IntType(.unsigned, full_bit_size - Static.count); + + fields = fields ++ [1]lib.Type.StructField{.{ + .name = "reserved", + .type = padding_type, + .default_value = &@as(padding_type, 0), + .is_comptime = false, + .alignment = 0, + }}; + break :fields fields; + }, + .decls = &.{}, + .is_tuple = false, + }; + }, + }); +}; + +pub const Dynamic = enum { + io, + ram, // Barrelfish equivalent: RAM (no PhysAddr) + cpu_memory, // Barrelfish equivalent: Frame + page_table, // Barrelfish equivalent: VNode + // irq_table, + // device_memory, + // scheduler, + + pub const Map = extern struct { + io: IO, + ram: RAM, + cpu_memory: CPUMemory, + page_table: PageTables, + + comptime { + inline for (lib.fields(Dynamic.Map), lib.fields(Dynamic)) |struct_field, enum_field| { + assert(lib.equal(u8, enum_field.name, struct_field.name)); + } + } + }; +}; + +pub const RAM = extern struct { + lists: [lib.arch.reverse_valid_page_sizes.len]?*Region = .{null} ** lib.arch.valid_page_sizes.len, + + const AllocateError = error{ + OutOfMemory, + }; + + inline fn getListIndex(size: usize) usize { + inline for (lib.arch.reverse_valid_page_sizes, 0..) |reverse_page_size, reverse_index| { + if (size >= reverse_page_size) return reverse_index; + } + + unreachable; + } + + pub const Region = extern struct { + region: PhysicalMemoryRegion, + next: ?*@This() = null, + + const UnalignedAllocationResult = extern struct { + wasted: PhysicalMemoryRegion, + allocated: PhysicalMemoryRegion, + }; + + inline fn allocateUnaligned(free_ram: *Region, size: usize, alignment: usize) ?UnalignedAllocationResult { + const aligned_region_address = lib.alignForward(usize, free_ram.region.address.value(), alignment); + const wasted_space = aligned_region_address - free_ram.region.address.value(); + if (free_ram.region.size >= wasted_space + size) { + const wasted_region = free_ram.region.takeSlice(wasted_space); + const allocated_region = free_ram.region.takeSlice(size); + return UnalignedAllocationResult{ + .wasted = wasted_region, + .allocated = allocated_region, + }; + } + + return null; + } + }; +}; + +pub const CPUMemory = extern struct { + privileged: RAM = .{}, + user: RAM = .{}, + flags: Flags, + + const Flags = packed struct(u64) { + allocate: bool, + reserved: u63 = 0, + }; +}; + +pub const PageTables = extern struct { + foo: u32 = 0, +}; + +pub const IO = extern struct { + debug: bool, +}; + +pub const Scheduler = extern struct { + handle: ?*cpu.UserScheduler = null, + memory: PhysicalMemoryRegion, +}; + +comptime { + assert(enumCount(Dynamic) + enumCount(Static) == enumCount(rise.capabilities.Type)); +} + +pub const Root = extern struct { + static: Static.Bitmap, + dynamic: Dynamic.Map, + scheduler: Scheduler, + heap: Heap = .{}, + padding: [padding_byte_count]u8 = .{0} ** padding_byte_count, + + const max_alignment = @max(@alignOf(Static.Bitmap), @alignOf(Dynamic.Map), @alignOf(Scheduler), @alignOf(Heap)); + const total_size = lib.alignForward(usize, @sizeOf(Static.Bitmap) + @sizeOf(Dynamic.Map) + @sizeOf(Scheduler) + @sizeOf(Heap), max_alignment); + const page_aligned_size = lib.alignForward(usize, total_size, lib.arch.valid_page_sizes[0]); + const padding_byte_count = page_aligned_size - total_size; + + comptime { + assert(@sizeOf(Root) % lib.arch.valid_page_sizes[0] == 0); + } + + pub fn copy(root: *Root, other: *Root) void { + other.static = root.static; + // TODO: + other.dynamic = root.dynamic; + } + + pub inline fn hasPermissions(root: *const Root, comptime capability_type: rise.capabilities.Type, command: rise.capabilities.Command(capability_type)) bool { + return switch (capability_type) { + // static capabilities + inline .cpu, + .boot, + .process, + => |static_capability| @field(root.static, @tagName(static_capability)), + // dynamic capabilities + .io => switch (command) { + .copy, .mint, .retype, .delete, .revoke, .create => unreachable, + .log => root.dynamic.io.debug, + }, + .cpu_memory => root.dynamic.cpu_memory.flags.allocate, + .ram => unreachable, + .page_table => unreachable, + }; + } + + pub const AllocateError = error{ + OutOfMemory, + }; + + // Fast path + pub fn allocatePages(root: *Root, size: usize) AllocateError!PhysicalMemoryRegion { + assert(size != 0); + assert(lib.isAligned(size, lib.arch.valid_page_sizes[0])); + var index = RAM.getListIndex(size); + + const result = blk: { + while (true) : (index -= 1) { + const list = root.dynamic.ram.lists[index]; + var iterator = list; + + while (iterator) |free_ram| : (iterator = free_ram.next) { + if (free_ram.region.size >= size) { + if (free_ram.region.size >= size) { + const result = free_ram.region.takeSlice(size); + break :blk result; + } else { + @panic("TODO: cnsume all reigon"); + } + } + } + + if (index == 0) break; + } + + return error.OutOfMemory; + }; + + @memset(result.toHigherHalfVirtualAddress().access(u8), 0); + + return result; + } + + // Slow uncommon path. Use cases: + // 1. CR3 switch. This is assumed to be privileged, so this function assumes privileged use of the memory + pub fn allocatePageCustomAlignment(root: *Root, size: usize, alignment: usize) AllocateError!PhysicalMemoryRegion { + assert(alignment > lib.arch.valid_page_sizes[0] and alignment < lib.arch.valid_page_sizes[1]); + + comptime assert(lib.arch.valid_page_sizes.len == 3); + var index = RAM.getListIndex(size); + + while (true) : (index -= 1) { + if (root.dynamic.ram.lists[index]) |smallest_region_list| { + var iterator: ?*cpu.capabilities.RAM.Region = smallest_region_list; + while (iterator) |free_ram| : (iterator = free_ram.next) { + if (lib.isAligned(free_ram.region.address.value(), alignment)) { + if (free_ram.region.size >= size) { + const allocated_region = free_ram.region.takeSlice(size); + return allocated_region; + } + } else if (free_ram.allocateUnaligned(size, alignment)) |unaligned_allocation| { + try root.addRegion(&root.dynamic.ram, unaligned_allocation.wasted); + return unaligned_allocation.allocated; + } + } + } + + if (index == 0) break; + } + + return AllocateError.OutOfMemory; + } + + fn allocateSingle(root: *Root, comptime T: type) AllocateError!*T { + var iterator = root.heap.first; + while (iterator) |heap_region| : (iterator = heap_region.next) { + if (heap_region.alignmentFits(@alignOf(T))) { + if (heap_region.sizeFits(@sizeOf(T))) { + const allocated_region = heap_region.takeRegion(@sizeOf(T)); + const result = &allocated_region.toHigherHalfVirtualAddress().access(T)[0]; + return result; + } + } else { + @panic("ELSE"); + } + } + + const physical_region = try root.allocatePages(lib.arch.valid_page_sizes[0]); + const heap_region = physical_region.toHigherHalfVirtualAddress().address.access(*Heap.Region); + const first = root.heap.first; + heap_region.* = .{ + .descriptor = physical_region.offset(@sizeOf(Heap.Region)), + .allocated_size = @sizeOf(Heap.Region), + .next = first, + }; + + root.heap.first = heap_region; + + return try root.allocateSingle(T); + } + + fn allocateMany(root: *Root, comptime T: type, count: usize) AllocateError![]T { + _ = count; + _ = root; + + @panic("TODO many"); + } + + fn addRegion(root: *Root, ram: *RAM, physical_region: PhysicalMemoryRegion) !void { + const index = RAM.getListIndex(physical_region.size); + const new_region = try root.allocateSingle(RAM.Region); + new_region.* = RAM.Region{ + .region = physical_region, + .next = root.dynamic.ram.lists[index], + }; + + ram.lists[index] = new_region; + } + + pub const AllocateCPUMemoryOptions = packed struct { + privileged: bool, + }; + + pub fn allocateCPUMemory(root: *Root, physical_region: PhysicalMemoryRegion, options: AllocateCPUMemoryOptions) !void { + const ram_region = switch (options.privileged) { + true => &root.dynamic.cpu_memory.privileged, + false => &root.dynamic.cpu_memory.user, + }; + + try root.addRegion(ram_region, physical_region); + } + + pub const Heap = extern struct { + first: ?*Region = null, + + const AllocateError = error{ + OutOfMemory, + }; + + pub fn new(physical_region: PhysicalMemoryRegion, previous_allocated_size: usize) Heap { + const allocated_size = previous_allocated_size + @sizeOf(Region); + assert(physical_region.size > allocated_size); + const region = physical_region.offset(previous_allocated_size).address.toHigherHalfVirtualAddress().access(*Region); + region.* = .{ + .descriptor = physical_region, + .allocated_size = allocated_size, + }; + return Heap{ + .first = region, + }; + } + + fn create(heap: *Heap, comptime T: type) Heap.AllocateError!*T { + const result = try heap.allocate(T, 1); + return &result[0]; + } + + fn allocate(heap: *Heap, comptime T: type, count: usize) Heap.AllocateError![]T { + var iterator = heap.first; + while (iterator) |heap_region| { + const allocation = heap_region.allocate(T, count) catch continue; + return allocation; + } + @panic("TODO: allocate"); + } + + const Region = extern struct { + descriptor: PhysicalMemoryRegion, + allocated_size: usize, + next: ?*Region = null, + + inline fn getFreeRegion(region: Region) PhysicalMemoryRegion { + const free_region = region.descriptor.offset(region.allocated_size); + assert(free_region.size > 0); + return free_region; + } + + const AllocateError = error{ + OutOfMemory, + }; + + fn takeRegion(region: *Region, size: usize) PhysicalMemoryRegion { + var free_region = region.getFreeRegion(); + assert(free_region.size >= size); + const allocated_region = free_region.takeSlice(size); + region.allocated_size += size; + return allocated_region; + } + + fn allocate(region: *Region, comptime T: type, count: usize) Region.AllocateError![]T { + const free_region = region.getFreeRegion(); + _ = free_region; + _ = count; + @panic("TODO: region allocate"); + } + + fn create(region: *Region, comptime T: type) Region.AllocateError!*T { + const result = try region.allocate(T, 1); + return &result[0]; + } + + inline fn canAllocateDirectly(region: Region, size: usize, alignment: usize) bool { + const alignment_fits = region.alignmentFits(alignment); + const size_fits = region.sizeFits(size); + return alignment_fits and size_fits; + } + + inline fn canAllocateSplitting(region: Region, size: usize, alignment: usize) bool { + const free_region = region.getFreeRegion(); + const aligned_region_address = lib.alignForward(usize, free_region.address.value(), alignment); + const wasted_space = aligned_region_address - free_region.address.value(); + log.warn("Wasted space: {} bytes", .{wasted_space}); + _ = size; + @panic("TODO: canAllocateSplitting"); + } + + inline fn sizeFits(region: Region, size: usize) bool { + return region.descriptor.size - region.allocated_size >= size; + } + + inline fn alignmentFits(region: Region, alignment: usize) bool { + const result = lib.isAligned(region.getFreeRegion().address.value(), alignment); + return result; + } + }; + }; +}; + +pub const RootPageTableEntry = extern struct { + address: PhysicalAddress, +}; diff --git a/src/cpu/main.zig b/src/cpu/main.zig new file mode 100644 index 0000000..7d7d9f2 --- /dev/null +++ b/src/cpu/main.zig @@ -0,0 +1,37 @@ +const lib = @import("lib"); +const assert = lib.assert; +const log = lib.log; + +const privileged = @import("privileged"); +const stopCPU = privileged.arch.stopCPU; + +const cpu = @import("cpu"); + +var lock: lib.Spinlock = .released; + +pub const std_options = struct { + pub fn logFn(comptime level: lib.log.Level, comptime scope: @TypeOf(.EnumLiteral), comptime format: []const u8, args: anytype) void { + lock.acquire(); + cpu.writer.writeAll("[CPU DRIVER] ") catch unreachable; + cpu.writer.writeByte('[') catch unreachable; + cpu.writer.writeAll(@tagName(scope)) catch unreachable; + cpu.writer.writeAll("] ") catch unreachable; + cpu.writer.writeByte('[') catch unreachable; + cpu.writer.writeAll(@tagName(level)) catch unreachable; + cpu.writer.writeAll("] ") catch unreachable; + lib.format(cpu.writer, format, args) catch unreachable; + cpu.writer.writeByte('\n') catch unreachable; + + lock.release(); + } + + pub const log_level = lib.log.Level.debug; +}; + +pub fn panic(message: []const u8, _: ?*lib.StackTrace, _: ?usize) noreturn { + @call(.always_inline, cpu.panic, .{ "{s}", .{message} }); +} + +comptime { + @export(cpu.arch.entryPoint, .{ .name = "_start", .linkage = .Strong }); +} diff --git a/src/cpu/test.zig b/src/cpu/test.zig new file mode 100644 index 0000000..c079be4 --- /dev/null +++ b/src/cpu/test.zig @@ -0,0 +1,21 @@ +const lib = @import("lib"); +const log = lib.log.scoped(.TEST); +const privileged = @import("privileged"); +const writer = privileged.writer; + +test "Hello kernel" { + lib.testing.log_level = .debug; + log.debug("Hello kernel test", .{}); +} + +pub const std_options = struct { + pub fn logFn(comptime level: lib.log.Level, comptime scope: @TypeOf(.EnumLiteral), comptime format: []const u8, args: anytype) void { + _ = level; + writer.writeAll("[CPU DRIVER] ") catch unreachable; + writer.writeByte('[') catch unreachable; + writer.writeAll(@tagName(scope)) catch unreachable; + writer.writeAll("] ") catch unreachable; + lib.format(writer, format, args) catch unreachable; + writer.writeByte('\n') catch unreachable; + } +}; diff --git a/src/cpu/test_runner.zig b/src/cpu/test_runner.zig new file mode 100644 index 0000000..b80a220 --- /dev/null +++ b/src/cpu/test_runner.zig @@ -0,0 +1,38 @@ +const lib = @import("lib"); +const assert = lib.assert; +const log = lib.log.scoped(.TEST); +const privileged = @import("privileged"); +const QEMU = lib.QEMU; + +const cpu = @import("cpu"); + +const RunAllTestResult = error{ + failure, +}; + +pub fn runAllTests() RunAllTestResult!void { + comptime assert(lib.is_test); + const test_functions = @import("builtin").test_functions; + var failed_test_count: usize = 0; + for (test_functions) |test_function| { + test_function.func() catch |err| { + log.err("Test failed: {}", .{err}); + failed_test_count += 1; + }; + } + + const test_count = test_functions.len; + assert(QEMU.isa_debug_exit.io_size == @sizeOf(u32)); + const exit_code = switch (failed_test_count) { + 0 => blk: { + log.info("All {} tests passed.", .{test_count}); + break :blk .success; + }, + else => blk: { + log.info("Run {} tests. Failed {}.", .{ test_count, failed_test_count }); + break :blk .failure; + }, + }; + + cpu.shutdown(exit_code); +} diff --git a/src/host.zig b/src/host.zig new file mode 100644 index 0000000..5ca1104 --- /dev/null +++ b/src/host.zig @@ -0,0 +1,74 @@ +const lib = @import("lib"); + +comptime { + if (lib.os == .freestanding) @compileError("Host file included in non-host target"); +} + +const std = @import("std"); +pub const ChildProcess = std.ChildProcess; + +pub const posix = std.os; +pub const sync = std.os.sync; + +pub const fs = std.fs; +pub const cwd = fs.cwd; +pub const Dir = fs.Dir; +pub const basename = fs.path.basename; +pub const dirname = fs.path.dirname; + +const io = std.io; +pub const getStdOut = std.io.getStdOut; + +const heap = std.heap; +pub const ArenaAllocator = heap.ArenaAllocator; +pub const page_allocator = heap.page_allocator; + +pub const ArrayList = std.ArrayList; +pub const ArrayListAligned = std.ArrayListAligned; + +pub const time = std.time; + +// Build imports +pub const build = std.build; + +pub fn allocateZeroMemory(bytes: u64) ![]align(0x1000) u8 { + switch (lib.os) { + .windows => { + const windows = std.os.windows; + return @as([*]align(0x1000) u8, @ptrCast(@alignCast(try windows.VirtualAlloc(null, bytes, windows.MEM_RESERVE | windows.MEM_COMMIT, windows.PAGE_READWRITE))))[0..bytes]; + }, + // Assume all systems are POSIX + else => { + const mmap = std.os.mmap; + const PROT = std.os.PROT; + const MAP = std.os.MAP; + return try mmap(null, bytes, PROT.READ | PROT.WRITE, MAP.PRIVATE | MAP.ANONYMOUS, -1, 0); + }, + .freestanding => @compileError("Not implemented yet"), + } +} + +pub const ExecutionError = error{failed}; +pub fn spawnProcess(arguments: []const []const u8, allocator: lib.ZigAllocator) !void { + var process = ChildProcess.init(arguments, allocator); + process.stdout_behavior = .Ignore; + const execution_result = try process.spawnAndWait(); + + switch (execution_result) { + .Exited => |exit_code| { + switch (exit_code) { + 0 => {}, + else => return ExecutionError.failed, + } + }, + .Signal => |signal_code| { + _ = signal_code; + unreachable; + }, + .Stopped, .Unknown => unreachable, + } +} + +pub const panic = std.debug.panic; + +pub const allocateArguments = std.process.argsAlloc; diff --git a/src/host/disk_image_builder.zig b/src/host/disk_image_builder.zig new file mode 100644 index 0000000..6813aea --- /dev/null +++ b/src/host/disk_image_builder.zig @@ -0,0 +1,250 @@ +const lib = @import("lib"); +const assert = lib.assert; +const FAT32 = lib.Filesystem.FAT32; +const PartitionTable = lib.PartitionTable; +const GPT = PartitionTable.GPT; +const MBR = PartitionTable.MBR; +const host = @import("host"); + +pub const ImageDescription = struct { + partition_name: []const u8, + partition_start_lba: u64, + disk_sector_count: u64, + disk_sector_size: u64, + partition_filesystem: lib.FilesystemType, +}; + +pub extern fn deploy(device_path: [*:0]const u8, limine_hdd_ptr: [*]const u8, limine_hdd_len: usize) callconv(.C) c_int; + +const Disk = lib.Disk; +pub const DiskImage = extern struct { + disk: Disk, + buffer_ptr: [*]u8, + + pub fn write(disk: *Disk, bytes: []const u8, sector_offset: u64, commit_memory_to_disk: bool) Disk.WriteError!void { + const need_write = !(disk.type == .memory and !commit_memory_to_disk); + if (need_write) { + const disk_image = @fieldParentPtr(DiskImage, "disk", disk); + assert(disk_image.disk.disk_size > 0); + //assert(disk.disk.partition_count == 1); + assert(bytes.len > 0); + //assert(disk.disk.disk_size == disk.buffer.items.len); + const byte_offset = sector_offset * disk_image.disk.sector_size; + + if (byte_offset + bytes.len > disk_image.disk.disk_size) return Disk.WriteError.disk_size_overflow; + + @memcpy(disk_image.getBuffer()[byte_offset .. byte_offset + bytes.len], bytes); + } + } + + pub fn read(disk: *Disk, sector_count: u64, sector_offset: u64, provided_buffer: ?[]const u8) Disk.ReadError!Disk.ReadResult { + assert(provided_buffer == null); + const disk_image = @fieldParentPtr(DiskImage, "disk", disk); + assert(disk_image.disk.disk_size > 0); + assert(sector_count > 0); + //assert(disk.disk.disk_size == disk.buffer.items.len); + const byte_count = sector_count * disk_image.disk.sector_size; + const byte_offset = sector_offset * disk_image.disk.sector_size; + if (byte_offset + byte_count > disk.disk_size) { + return Disk.ReadError.read_error; + } + return .{ + .buffer = disk_image.getBuffer()[byte_offset .. byte_offset + byte_count].ptr, + .sector_count = sector_count, + }; + } + + pub fn readCache(disk: *Disk, sector_count: u64, sector_offset: u64) Disk.ReadError!Disk.ReadResult { + _ = sector_count; + _ = sector_offset; + _ = disk; + return error.read_error; + } + + pub fn fromZero(sector_count: usize, sector_size: u16) !DiskImage { + const disk_bytes = try host.allocateZeroMemory(sector_count * sector_size); + var disk_image = DiskImage{ + .disk = .{ + .type = .memory, + .callbacks = .{ + .read = DiskImage.read, + .write = DiskImage.write, + .readCache = DiskImage.readCache, + }, + .disk_size = disk_bytes.len, + .sector_size = sector_size, + .cache_size = 0, + }, + .buffer_ptr = disk_bytes.ptr, + }; + + return disk_image; + } + + pub fn createFAT(disk_image: *DiskImage, comptime image: ImageDescription, original_gpt_cache: ?GPT.Partition.Cache) !GPT.Partition.Cache { + const gpt_cache = try GPT.create(&disk_image.disk, if (original_gpt_cache) |o_gpt_cache| o_gpt_cache.gpt.header else null); + const partition_name_u16 = lib.unicode.utf8ToUtf16LeStringLiteral(image.partition_name); + const gpt_partition_cache = try gpt_cache.addPartition(image.partition_filesystem, partition_name_u16, image.partition_start_lba, gpt_cache.header.last_usable_lba, if (original_gpt_cache) |o_gpt_cache| o_gpt_cache.partition else null); + + return gpt_partition_cache; + } + + pub fn fromFile(file_path: []const u8, sector_size: u16, allocator: lib.ZigAllocator) !DiskImage { + const disk_memory = try host.cwd().readFileAlloc(allocator, file_path, lib.maxInt(usize)); + + var disk_image = DiskImage{ + .disk = .{ + .type = .memory, + .callbacks = .{ + .read = DiskImage.read, + .write = DiskImage.write, + .readCache = DiskImage.readCache, + }, + .disk_size = disk_memory.len, + .sector_size = sector_size, + .cache_size = 0, + }, + .buffer_ptr = disk_memory.ptr, + }; + + return disk_image; + } + + const File = struct { + handle: lib.File, + size: usize, + }; + + pub inline fn getBuffer(disk_image: DiskImage) []u8 { + return disk_image.buffer_ptr[0..disk_image.disk.disk_size]; + } +}; + +pub fn format(disk: *Disk, partition_range: Disk.PartitionRange, copy_mbr: ?*const MBR.Partition) !FAT32.Cache { + if (disk.type != .memory) @panic("disk is not memory"); + const fat_partition_mbr_lba = partition_range.first_lba; + const fat_partition_mbr = try disk.readTypedSectors(MBR.Partition, fat_partition_mbr_lba, null, .{}); + + const sectors_per_track = 32; + const total_sector_count_32 = @as(u32, @intCast(lib.alignBackward(u64, partition_range.last_lba - partition_range.first_lba, sectors_per_track))); + const fat_count = FAT32.count; + + var cluster_size: u8 = 1; + const max_cluster_size = 128; + var fat_data_sector_count: u32 = undefined; + var fat_length_32: u32 = undefined; + var cluster_count_32: u32 = undefined; + + while (true) { + assert(cluster_size > 0); + fat_data_sector_count = total_sector_count_32 - lib.alignForward(u32, FAT32.default_reserved_sector_count, cluster_size); + cluster_count_32 = (fat_data_sector_count * disk.sector_size + fat_count * 8) / (cluster_size * disk.sector_size + fat_count * 4); + fat_length_32 = lib.alignForward(u32, cdiv((cluster_count_32 + 2) * 4, disk.sector_size), cluster_size); + cluster_count_32 = (fat_data_sector_count - fat_count * fat_length_32) / cluster_size; + const max_cluster_size_32 = @min(fat_length_32 * disk.sector_size / 4, FAT32.getMaxCluster(.fat32)); + if (cluster_count_32 > max_cluster_size_32) { + cluster_count_32 = 0; + } + if (cluster_count_32 != 0 and cluster_count_32 < FAT32.getMinCluster(.fat32)) { + cluster_count_32 = 0; + } + + if (cluster_count_32 != 0) break; + + cluster_size <<= 1; + + const keep_going = cluster_size != 0 and cluster_size <= max_cluster_size; + if (!keep_going) break; + @panic("unexpected fat32 bug"); + } + + var root_directory_entries: u64 = 0; + _ = root_directory_entries; + + const reserved_sector_count = lib.alignForward(u16, FAT32.default_reserved_sector_count, cluster_size); + + fat_partition_mbr.* = MBR.Partition{ + .bpb = .{ + .dos3_31 = .{ + .dos2_0 = .{ + .jmp_code = .{ 0xeb, 0x58, 0x90 }, + .oem_identifier = "mkfs.fat".*, + .sector_size = disk.sector_size, + .cluster_sector_count = cluster_size, + .reserved_sector_count = reserved_sector_count, + .fat_count = fat_count, + .root_entry_count = 0, + .total_sector_count_16 = 0, + .media_descriptor = 0xf8, + .fat_sector_count_16 = 0, + }, + .physical_sectors_per_track = sectors_per_track, + .disk_head_count = 8, + .hidden_sector_count = @as(u32, @intCast(partition_range.first_lba)), + .total_sector_count_32 = total_sector_count_32, + }, + .fat_sector_count_32 = fat_length_32, + .drive_description = 0, + .version = .{ 0, 0 }, + .root_directory_cluster_offset = FAT32.starting_cluster, + .fs_info_sector = FAT32.default_fs_info_sector, + .backup_boot_record_sector = FAT32.default_backup_boot_record_sector, + .drive_number = 0x80, + .extended_boot_signature = 0x29, + .serial_number = if (copy_mbr) |copy_partition_mbr| copy_partition_mbr.bpb.serial_number else @truncate(@as(u64, @intCast(host.time.microTimestamp()))), + .volume_label = "NO NAME ".*, + .filesystem_type = "FAT32 ".*, + }, + .code = [_]u8{ + 0xe, 0x1f, 0xbe, 0x77, 0x7c, 0xac, 0x22, 0xc0, 0x74, 0xb, 0x56, 0xb4, 0xe, 0xbb, 0x7, 0x0, 0xcd, 0x10, 0x5e, 0xeb, 0xf0, 0x32, 0xe4, 0xcd, 0x16, 0xcd, 0x19, 0xeb, 0xfe, 0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x61, 0x20, 0x62, 0x6f, 0x6f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x64, 0x69, 0x73, 0x6b, 0x2e, 0x20, 0x20, 0x50, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x20, 0x69, 0x6e, 0x73, 0x65, 0x72, 0x74, 0x20, 0x61, 0x20, 0x62, 0x6f, 0x6f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x66, 0x6c, 0x6f, 0x70, 0x70, 0x79, 0x20, 0x61, 0x6e, 0x64, 0xd, 0xa, 0x70, 0x72, 0x65, 0x73, 0x73, 0x20, 0x61, 0x6e, 0x79, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x74, 0x6f, 0x20, 0x74, 0x72, 0x79, 0x20, 0x61, 0x67, 0x61, 0x69, 0x6e, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0xd, 0xa, + } ++ [1]u8{0} ** 227, + // This should be zero + .partitions = lib.zeroes([4]MBR.LegacyPartition), + }; + + try disk.writeTypedSectors(MBR.Partition, fat_partition_mbr, fat_partition_mbr_lba, false); + + const backup_boot_record_sector = partition_range.first_lba + fat_partition_mbr.bpb.backup_boot_record_sector; + const backup_boot_record = try disk.readTypedSectors(MBR.Partition, backup_boot_record_sector, null, .{}); + backup_boot_record.* = fat_partition_mbr.*; + try disk.writeTypedSectors(MBR.Partition, backup_boot_record, backup_boot_record_sector, false); + + const fs_info_lba = partition_range.first_lba + fat_partition_mbr.bpb.fs_info_sector; + const fs_info = try disk.readTypedSectors(FAT32.FSInfo, fs_info_lba, null, .{}); + fs_info.* = .{ + .lead_signature = 0x41615252, + .signature = 0x61417272, + .free_cluster_count = cluster_count_32, + .last_allocated_cluster = 0, + .trail_signature = 0xaa550000, + }; + try disk.writeTypedSectors(FAT32.FSInfo, fs_info, fs_info_lba, false); + + const cache = FAT32.Cache{ + .disk = disk, + .partition_range = partition_range, + .mbr = fat_partition_mbr, + .fs_info = fs_info, + .allocator = null, + }; + + // TODO: write this properly + + try cache.registerCluster(0, FAT32.Entry.reserved_and_should_not_be_used_eof, null); + try cache.registerCluster(1, FAT32.Entry.allocated_and_eof, null); + try cache.registerCluster(2, FAT32.Entry.reserved_and_should_not_be_used_eof, null); + + cache.fs_info.last_allocated_cluster = 2; + cache.fs_info.free_cluster_count = cluster_count_32 - 1; + + const backup_fs_info_lba = backup_boot_record_sector + backup_boot_record.bpb.fs_info_sector; + const backup_fs_info = try disk.readTypedSectors(FAT32.FSInfo, backup_fs_info_lba, null, .{}); + backup_fs_info.* = fs_info.*; + try disk.writeTypedSectors(FAT32.FSInfo, backup_fs_info, backup_fs_info_lba, false); + + return cache; +} + +fn cdiv(a: u32, b: u32) u32 { + return (a + b - 1) / b; +} diff --git a/src/host/disk_image_builder/boot_disk.zig b/src/host/disk_image_builder/boot_disk.zig new file mode 100644 index 0000000..55823b1 --- /dev/null +++ b/src/host/disk_image_builder/boot_disk.zig @@ -0,0 +1,405 @@ +const bootloader = @import("bootloader"); +const bios = @import("bios"); +const uefi = @import("uefi"); +const host = @import("host"); +const lib = @import("lib"); +const assert = lib.assert; +const MBR = lib.PartitionTable.MBR; + +pub const BootDisk = extern struct { + bpb: MBR.BIOSParameterBlock.DOS7_1_79, + code: [code_byte_count]u8, + gdt: GDT, + gdt_descriptor: GDT.Descriptor, + dap: MBR.DAP align(2), + partitions: [4]MBR.LegacyPartition align(2), + signature: [2]u8 = [_]u8{ 0x55, 0xaa }, + + const code_byte_count = 0x10d; + + const GDT = bootloader.arch.x86_64.GDT; + + const hlt = [_]u8{0xf4}; + const clc = [_]u8{0xf8}; + const cli = [_]u8{0xfa}; + const sti = [_]u8{0xfb}; + const cld = [_]u8{0xfc}; + + const xor = 0x31; + const xor_si_si_16 = [_]u8{ xor, 0xf6 }; + const push_ds = [_]u8{0x1e}; + const mov_ds_si = [_]u8{ 0x8e, 0xde }; + const mov_es_si = [_]u8{ 0x8e, 0xc6 }; + const mov_ss_si = [_]u8{ 0x8e, 0xd6 }; + const mov_sp_stack_top = [_]u8{0xbc} ++ lib.asBytes(&bios.stack_top).*; + const mov_bx_0xaa55 = [_]u8{ 0xbb, 0xaa, 0x55 }; + const cmp_bx_0xaa55 = [_]u8{ 0x81, 0xfb, 0x55, 0xaa }; + + const jc = 0x72; + const jne = 0x75; + + const mov_eax_cr0 = [_]u8{ 0x0f, 0x20, 0xc0 }; + const mov_cr0_eax = [_]u8{ 0x0f, 0x22, 0xc0 }; + + const code_32 = @offsetOf(GDT, "code_32"); + const data_32 = @offsetOf(GDT, "data_32"); + + const reload_data_segments_32 = [_]u8{ + 0xb8, data_32, 0x00, 0x00, 0x00, // mov eax, 0x10 + 0x8e, 0xd8, // mov ds, ax + 0x8e, 0xc0, // mov es, ax + 0x8e, 0xe0, // mov fs, ax + 0x8e, 0xe8, // mov gs, ax + 0x8e, 0xd0, // mov ss, ax + }; + const xor_eax_eax = [_]u8{ xor, 0xc8 }; + const xor_ebx_ebx = [_]u8{ xor, 0xdb }; + const nop = [_]u8{0x90}; + const rep_movsb = [_]u8{ 0xf3, 0xa4 }; + + fn or_ax(imm8: u8) [4]u8 { + return .{ 0x66, 0x83, 0xc8, imm8 }; + } + + fn int(interrupt_number: u8) [2]u8 { + return .{ 0xcd, interrupt_number }; + } + + fn mov_cx(imm16: u16) [3]u8 { + const imm_bytes = lib.asBytes(&imm16); + return .{ 0xb9, imm_bytes[0], imm_bytes[1] }; + } + + fn mov_di(imm16: u16) [3]u8 { + const imm_bytes = lib.asBytes(&imm16); + return .{ 0xbf, imm_bytes[0], imm_bytes[1] }; + } + + fn mov_si(imm16: u16) [3]u8 { + const imm_bytes = lib.asBytes(&imm16); + return .{ 0xbe, imm_bytes[0], imm_bytes[1] }; + } + + fn mov_ah(imm8: u8) [2]u8 { + return .{ 0xb4, imm8 }; + } + + pub fn fill(mbr: *BootDisk, allocator: lib.ZigAllocator, dap: MBR.DAP) !void { + // Hardcoded jmp to end of FAT32 BPB + const jmp_to_end_of_bpb = .{ 0xeb, @sizeOf(MBR.BIOSParameterBlock.DOS7_1_79) - 2 }; + mbr.bpb.dos3_31.dos2_0.jmp_code = jmp_to_end_of_bpb ++ nop; + mbr.dap = dap; + mbr.gdt = .{}; + mbr.gdt_descriptor = .{ + .limit = @sizeOf(GDT) - 1, + .address = bios.mbr_offset + @offsetOf(BootDisk, "gdt"), + }; + var assembler = Assembler{ + .boot_disk = mbr, + .patches = host.ArrayList(Patch).init(allocator), + .labels = host.ArrayList(Label.Offset).init(allocator), + }; + defer assembler.patch(); + + // 16-bit + assembler.addInstruction(&cli); + assembler.addInstruction(&xor_si_si_16); + assembler.addInstruction(&mov_ds_si); + assembler.addInstruction(&mov_es_si); + assembler.addInstruction(&mov_ss_si); + assembler.addInstruction(&mov_sp_stack_top); + assembler.addInstruction(&mov_si(0x7c00)); + assembler.addInstruction(&mov_di(bios.mbr_offset)); + assembler.addInstruction(&mov_cx(lib.default_sector_size)); + assembler.addInstruction(&cld); + assembler.addInstruction(&rep_movsb); + try assembler.far_jmp_16(0x0, .reload_cs_16); + + try assembler.add_instruction_with_label(&sti, .reload_cs_16); + assembler.addInstruction(&mov_ah(0x41)); + assembler.addInstruction(&mov_bx_0xaa55); + assembler.addInstruction(&int(0x13)); + try assembler.jcc(jc, .error16); + assembler.addInstruction(&cmp_bx_0xaa55); + try assembler.jcc(jne, .error16); + try assembler.add_instruction_with_label(&mov_ah(0x42), .read_sectors); + try assembler.mov_si(.dap); + assembler.addInstruction(&clc); + assembler.addInstruction(&int(0x13)); + + try assembler.jcc(jc, .error16); + // Save real mode + try assembler.lgdt_16(.gdt_descriptor); + assembler.addInstruction(&cli); + assembler.addInstruction(&mov_eax_cr0); + assembler.addInstruction(&or_ax(1)); + assembler.addInstruction(&mov_cr0_eax); + try assembler.far_jmp_16(code_32, .protected_mode); + + try assembler.add_instruction_with_label(&cli, .error16); + assembler.addInstruction(&hlt); + + // 32-bit + try assembler.add_instruction_with_label(&reload_data_segments_32, .protected_mode); + assembler.addInstruction(&xor_eax_eax); + assembler.addInstruction(&xor_ebx_ebx); + + assembler.addInstruction(&[_]u8{0xbe} ++ lib.asBytes(&@as(u32, 0x600))); + assembler.addInstruction(&[_]u8{0xbf} ++ lib.asBytes(&@as(u32, 0x10000))); + const aligned_file_size = @as(u32, dap.sector_count * lib.default_sector_size); + assembler.addInstruction(&[_]u8{0xb9} ++ lib.asBytes(&aligned_file_size)); + assembler.addInstruction(&cld); + assembler.addInstruction(&[_]u8{ 0xf3, 0xa4 }); + + // mov ebp, 0x10000 + assembler.addInstruction(&[_]u8{0xbd} ++ lib.asBytes(&@as(u32, 0x10000))); + + //b0: 66 8b 5d 2a mov bx,WORD PTR [rbp+0x2a] // BX: Program header size + assembler.addInstruction(&.{ 0x66, 0x8b, 0x5d, 0x2a }); + //b4: 66 8b 45 2c mov ax,WORD PTR [rbp+0x2c] // AX: Program header count + assembler.addInstruction(&.{ 0x66, 0x8b, 0x45, 0x2c }); + //b8: 8b 55 1c mov edx,DWORD PTR [rbp+0x1c] // EDX: Program header offset + assembler.addInstruction(&.{ 0x8b, 0x55, 0x1c }); + //bb: 01 ea add edx,ebp // EDX: program header base address + assembler.addInstruction(&.{ 0x01, 0xea }); + //bd: 83 3a 01 cmp DWORD PTR [rdx],0x1 // [EDX]: Program header type. Compare if it is PT_LOAD + try assembler.add_instruction_with_label(&.{ 0x83, 0x3a, 0x01 }, .elf_loader_loop); + //c0: 75 0d jne 0xcf // Continue if not PT_LOAD + try assembler.jcc(jne, .elf_loader_loop_continue); + //c2: 89 ee mov esi,ebp // ESI: ELF base address + assembler.addInstruction(&.{ 0x89, 0xee }); + //c4: 03 72 04 add esi,DWORD PTR [rdx+0x4] // ESI: program segment address, source of the memcpy + + assembler.addInstruction(&.{ 0x03, 0x72, 0x04 }); + //c7: 8b 7a 0c mov edi,DWORD PTR [rdx+0xc] // EDI: program segment physical address, destination of the memcpy + assembler.addInstruction(&.{ 0x8b, 0x7a, 0x0c }); + //ca: 8b 4a 10 mov ecx,DWORD PTR [rdx+0x10] // ECX: program header file size, bytes to memcpy + assembler.addInstruction(&.{ 0x8b, 0x4a, 0x10 }); + //cd: f3 a4 rep movs BYTE PTR es:[rdi],BYTE PTR ds:[rsi] + assembler.addInstruction(&.{ 0xf3, 0xa4 }); + //cf: 01 da add edx,ebx + try assembler.add_instruction_with_label(&.{ 0x01, 0xda }, .elf_loader_loop_continue); + //d1: 48 dec eax + assembler.addInstruction(&.{0x48}); + // jnz loop + const jnz = jne; + try assembler.jcc(jnz, .elf_loader_loop); + //d5: 8b 5d 18 mov ebx,DWORD PTR [rbp+0x18] + assembler.addInstruction(&.{ 0x8b, 0x5d, 0x18 }); + + // EXPERIMENT: stack to a higher address + assembler.addInstruction(.{@as(u8, 0xbd)} ++ lib.asBytes(&bios.loader_stack_top)); + + //d8: ff e3 jmp rbx + assembler.addInstruction(&.{ 0xff, 0xe3 }); + // log.debug("MBR code length: 0x{x}/0x{x}", .{ assembler.code_index, assembler.boot_disk.code.len }); + } + + const Label = enum { + reload_cs_16, + error16, + read_sectors, + dap, + dap_pointer, + gdt_descriptor, + protected_mode, + elf_loader_loop, + elf_loader_loop_continue, + + const Offset = struct { + label: Label, + offset: u8, + }; + }; + + const Patch = struct { + label: Label, + label_size: u8, + label_offset: u8, + // For relative labels, instruction len to compute RIP-relative address + // For absolute labels, offset in which to introduce a 8-bit absolute offset + label_type: enum { + relative, + absolute, + }, + label_section: enum { + code, + data, + }, + instruction_starting_offset: u8, + instruction_len: u8, + }; + + pub const Assembler = struct { + boot_disk: *BootDisk, + code_index: u8 = 0, + patches: host.ArrayList(Patch), + labels: host.ArrayList(Label.Offset), + + pub inline fn addInstruction(assembler: *Assembler, instruction_bytes: []const u8) void { + assert(assembler.code_index + instruction_bytes.len <= assembler.boot_disk.code.len); + // lib.print("[0x{x:0>4}] ", .{bios.mbr_offset + @offsetOf(BootDisk, "code") + assembler.code_index}); + // for (instruction_bytes) |byte| { + // lib.print("{x:0>2} ", .{byte}); + // } + // lib.print("\n", .{}); + @memcpy(assembler.boot_disk.code[assembler.code_index .. assembler.code_index + instruction_bytes.len], instruction_bytes); + assembler.code_index += @as(u8, @intCast(instruction_bytes.len)); + } + + pub fn add_instruction_with_label(assembler: *Assembler, instruction_bytes: []const u8, label: Label) !void { + try assembler.labels.append(.{ .label = label, .offset = assembler.code_index }); + assembler.addInstruction(instruction_bytes); + } + + pub fn far_jmp_16(assembler: *Assembler, segment: u16, label: Label) !void { + const segment_bytes = lib.asBytes(&segment); + const offset_bytes = lib.asBytes(&bios.mbr_offset); + const instruction_bytes = [_]u8{ 0xea, offset_bytes[0], offset_bytes[1], segment_bytes[0], segment_bytes[1] }; + try assembler.patches.append(.{ + .label = label, + .label_size = @sizeOf(u16), + .label_offset = 1, + .label_type = .absolute, + .label_section = .code, + .instruction_starting_offset = assembler.code_index, + .instruction_len = instruction_bytes.len, + }); + assembler.addInstruction(&instruction_bytes); + } + + pub fn jcc(assembler: *Assembler, jmp_opcode: u8, label: Label) !void { + const instruction_bytes = [_]u8{ jmp_opcode, 0x00 }; + try assembler.patches.append(.{ + .label = label, + .label_size = @sizeOf(u8), + .label_offset = 1, + .label_type = .relative, + .label_section = .code, + .instruction_starting_offset = assembler.code_index, + .instruction_len = instruction_bytes.len, + }); + assembler.addInstruction(&instruction_bytes); + } + + pub fn mov_si(assembler: *Assembler, label: Label) !void { + const instruction_bytes = [_]u8{ 0xbe, 0x00, 0x00 }; + try assembler.patches.append(.{ + .label = label, + .label_size = @sizeOf(u16), + .label_offset = 1, + .label_type = .absolute, + .label_section = .data, + .instruction_starting_offset = assembler.code_index, + .instruction_len = instruction_bytes.len, + }); + assembler.addInstruction(&instruction_bytes); + } + + pub fn lgdt_16(assembler: *Assembler, label: Label) !void { + const instruction_bytes = [_]u8{ 0x0f, 0x01, 0x16, 0x00, 0x00 }; + try assembler.patches.append(.{ + .label = label, + .label_size = @sizeOf(u16), + .label_offset = 3, + .label_type = .absolute, + .label_section = .data, + .instruction_starting_offset = assembler.code_index, + .instruction_len = instruction_bytes.len, + }); + assembler.addInstruction(&instruction_bytes); + } + + pub fn mov_ebp_dword_ptr(assembler: *Assembler, label: Label) !void { + const instruction_bytes = [_]u8{ 0x8b, 0x2d, 0x00, 0x00, 0x00, 0x00 }; + try assembler.patches.append(.{ + .label = label, + .label_size = @sizeOf(u16), + .label_offset = 2, + .label_type = .absolute, + .label_section = .data, + .instruction_starting_offset = assembler.code_index, + .instruction_len = instruction_bytes.len, + }); + assembler.addInstruction(&instruction_bytes); + } + + pub fn patch(assembler: *Assembler) void { + var patched: usize = 0; + + next_patch: for (assembler.patches.items) |patch_descriptor| { + const index = patch_descriptor.instruction_starting_offset + patch_descriptor.label_offset; + // log.debug("Trying to patch instruction. Section: {s}. Label: {s}. Label size: {}. Label type: {s}", .{ @tagName(patch_descriptor.label_section), @tagName(patch_descriptor.label), patch_descriptor.label_size, @tagName(patch_descriptor.label_type) }); + switch (patch_descriptor.label_section) { + .code => for (assembler.labels.items) |label_descriptor| { + if (patch_descriptor.label == label_descriptor.label) { + switch (patch_descriptor.label_type) { + .absolute => { + assert(patch_descriptor.label_size == @sizeOf(u16)); + @as(*align(1) u16, @ptrCast(&assembler.boot_disk.code[index])).* = bios.mbr_offset + @offsetOf(BootDisk, "code") + label_descriptor.offset; + }, + .relative => { + assert(patch_descriptor.label_size == @sizeOf(u8)); + assert(patch_descriptor.label_section == .code); + const computed_after_instruction_offset = patch_descriptor.instruction_starting_offset + patch_descriptor.instruction_len; + const operand_a = @as(isize, @intCast(label_descriptor.offset)); + const operand_b = @as(isize, @intCast(computed_after_instruction_offset)); + const diff = @as(u8, @bitCast(@as(i8, @intCast(operand_a - operand_b)))); + @as(*align(1) u8, @ptrCast(&assembler.boot_disk.code[index])).* = diff; + }, + } + + // const instruction_start = bios.mbr_offset + @offsetOf(BootDisk, "code") + patch_descriptor.instruction_starting_offset; + // lib.print("[0x{x:0>4}] ", .{instruction_start}); + // const instruction_bytes = assembler.boot_disk.code[patch_descriptor.instruction_starting_offset .. patch_descriptor.instruction_starting_offset + patch_descriptor.instruction_len]; + // for (instruction_bytes) |byte| { + // lib.print("{x:0>2} ", .{byte}); + // } + // lib.print("\n", .{}); + patched += 1; + continue :next_patch; + } + }, + .data => { + // log.debug("Data: {s}", .{@tagName(patch_descriptor.label)}); + const dap_offset = @offsetOf(BootDisk, "dap"); + // log.debug("DAP offset: 0x{x}", .{dap_offset}); + switch (patch_descriptor.label_type) { + .absolute => { + assert(patch_descriptor.label_size == @sizeOf(u16)); + const ptr = bios.mbr_offset + @as(u16, switch (patch_descriptor.label) { + .dap => dap_offset, + .gdt_descriptor => @offsetOf(BootDisk, "gdt_descriptor"), + .dap_pointer => dap_offset + @offsetOf(MBR.DAP, "offset"), + else => @panic("unreachable tag"), + }); + // log.debug("Ptr patched: 0x{x}", .{ptr}); + @as(*align(1) u16, @ptrCast(&assembler.boot_disk.code[index])).* = ptr; + }, + .relative => @panic("unreachable relative"), + } + + // log.debug("Patched instruction:", .{}); + // const instruction_start = bios.mbr_offset + @offsetOf(BootDisk, "code") + patch_descriptor.instruction_starting_offset; + // lib.print("[0x{x:0>4}] ", .{instruction_start}); + // const instruction_bytes = assembler.boot_disk.code[patch_descriptor.instruction_starting_offset .. patch_descriptor.instruction_starting_offset + patch_descriptor.instruction_len]; + // for (instruction_bytes) |byte| { + // lib.print("{x:0>2} ", .{byte}); + // } + // lib.print("\n", .{}); + + patched += 1; + continue :next_patch; + }, + } + + // log.debug("Patch count: {}. Patched count: {}", .{ assembler.patches.items.len, patched }); + assert(patched == assembler.patches.items.len); + } + } + }; + + comptime { + assert(@sizeOf(@This()) == lib.default_sector_size); + } +}; diff --git a/src/host/disk_image_builder/main.zig b/src/host/disk_image_builder/main.zig new file mode 100644 index 0000000..4a9aca1 --- /dev/null +++ b/src/host/disk_image_builder/main.zig @@ -0,0 +1,302 @@ +const host = @import("host"); +const lib = @import("lib"); +const bios = @import("bios"); +const limine_installer = @import("limine_installer"); + +const assert = lib.assert; +const log = lib.log.scoped(.DiskImageBuilder); + +const Disk = lib.Disk; +const GPT = lib.PartitionTable.GPT; +const MBR = lib.PartitionTable.MBR; +const FAT32 = lib.Filesystem.FAT32; + +const max_file_length = lib.maxInt(usize); + +const Configuration = lib.Configuration; + +const disk_image_builder = @import("../disk_image_builder.zig"); +const ImageDescription = disk_image_builder.ImageDescription; +const DiskImage = disk_image_builder.DiskImage; +const format = disk_image_builder.format; + +const BootDisk = @import("boot_disk.zig").BootDisk; + +const dap_file_read = 0x600; +const file_copy_offset = 0x10000; + +const Error = error{ + configuration_wrong_argument, + configuration_not_found, + cpu_not_found, + bootloader_path_not_found, + user_programs_not_found, + image_configuration_path_not_found, + disk_image_path_not_found, + wrong_arguments, + not_implemented, +}; + +fn readFileAbsolute(allocator: *lib.Allocator.Wrapped, absolute_file_path: []const u8) ![]const u8 { + return try ((try host.fs.openFileAbsolute(absolute_file_path, .{})).readToEndAlloc(allocator.zigUnwrap(), max_file_length)); +} + +fn readFileAbsoluteToArrayList(array_list: *host.ArrayList(u8), absolute_file_path: []const u8) !void { + const file = try host.fs.openFileAbsolute(absolute_file_path, .{}); + try file.reader().readAllArrayList(array_list, lib.maxInt(usize)); +} + +fn addFileToBundle(file: host.fs.File, file_list: *host.ArrayList(u8), name: []const u8, file_contents: *host.ArrayList(u8)) !void { + try file_contents.appendNTimes(0, lib.alignForward(usize, file_contents.items.len, 0x10) - file_contents.items.len); + const offset = file_contents.items.len; + try file.reader().readAllArrayList(file_contents, lib.maxInt(usize)); + const stat = try file.stat(); + try file_list.writer().writeIntLittle(u32, @as(u32, @intCast(offset))); + try file_list.writer().writeIntLittle(u32, @as(u32, @intCast(stat.size))); + try file_list.writer().writeIntLittle(u8, @as(u8, @intCast(name.len))); + try file_list.appendSlice(name); +} + +pub fn main() anyerror!void { + var arena_allocator = host.ArenaAllocator.init(host.page_allocator); + defer arena_allocator.deinit(); + var wrapped_allocator = lib.Allocator.wrap(arena_allocator.allocator()); + + const arguments = (try host.allocateArguments(wrapped_allocator.zigUnwrap()))[1..]; + + const arguments_result: lib.ArgumentParser.DiskImageBuilder.Result = blk: { + var argument_parser = lib.ArgumentParser.DiskImageBuilder{}; + var argument_configuration: ?Configuration = null; + var argument_bootloader: ?[]const u8 = null; + var argument_cpu: ?[]const u8 = null; + var argument_user_programs: ?[]const []const u8 = null; + var argument_image_configuration_path: ?[]const u8 = null; + var argument_disk_image_path: ?[]const u8 = null; + var argument_index: usize = 0; + + while (argument_parser.next()) |argument_type| switch (argument_type) { + .disk_image_path => { + assert(@intFromEnum(argument_type) == 0); + argument_disk_image_path = arguments[argument_index]; + argument_index += 1; + }, + .configuration => { + argument_configuration = undefined; + const configuration = &argument_configuration.?; + inline for (lib.fields(Configuration)) |field| { + @field(configuration, field.name) = lib.stringToEnum(field.type, arguments[argument_index]) orelse return Error.configuration_wrong_argument; + argument_index += 1; + } + }, + .image_configuration_path => { + argument_image_configuration_path = arguments[argument_index]; + argument_index += 1; + }, + .bootloader => { + const argument = arguments[argument_index]; + argument_index += 1; + if (!lib.equal(u8, argument, "-")) { + argument_bootloader = argument; + } + }, + .cpu => { + argument_cpu = arguments[argument_index]; + argument_index += 1; + }, + .user_programs => { + argument_user_programs = arguments[argument_index..]; + argument_index += argument_user_programs.?.len; + }, + }; + + assert(argument_index == arguments.len); + break :blk .{ + .configuration = argument_configuration orelse return Error.configuration_not_found, + .disk_image_path = argument_disk_image_path orelse return Error.disk_image_path_not_found, + .image_configuration_path = argument_image_configuration_path orelse return Error.image_configuration_path_not_found, + .bootloader = argument_bootloader orelse return Error.bootloader_path_not_found, + .cpu = argument_cpu orelse return Error.cpu_not_found, + .user_programs = argument_user_programs orelse return Error.user_programs_not_found, + }; + }; + + const configuration = arguments_result.configuration; + + // TODO: use a format with hex support + const image_config = try lib.ImageConfig.get(wrapped_allocator.zigUnwrap(), arguments_result.image_configuration_path); + var disk_image = try DiskImage.fromZero(image_config.sector_count, image_config.sector_size); + const disk = &disk_image.disk; + const gpt_cache = try GPT.create(disk, null); + var partition_name_buffer: [256]u16 = undefined; + const partition_name = blk: { + const partition_index = try lib.unicode.utf8ToUtf16Le(&partition_name_buffer, image_config.partition.name); + break :blk partition_name_buffer[0..partition_index]; + }; + + switch (image_config.partition.filesystem) { + .fat32 => { + const filesystem = .fat32; + const gpt_partition_cache = try gpt_cache.addPartition(filesystem, partition_name, image_config.partition.first_lba, gpt_cache.header.last_usable_lba, null); + const fat_partition_cache = try format(gpt_cache.disk, .{ + .first_lba = gpt_partition_cache.partition.first_lba, + .last_lba = gpt_partition_cache.partition.last_lba, + }, null); + + var bundle_file_list = host.ArrayList(u8).init(wrapped_allocator.zigUnwrap()); + var uncompressed = host.ArrayList(u8).init(wrapped_allocator.zigUnwrap()); + // Uncompressed bundle size + try bundle_file_list.writer().writeIntLittle(u32, 0); + // Compressed bundle size + try bundle_file_list.writer().writeIntLittle(u32, 0); + // (cpu + programs + font) Bundle file count + try bundle_file_list.writer().writeIntLittle(u32, @as(u32, @intCast(1 + arguments_result.user_programs.len + 1))); + + const cpu_path = arguments_result.cpu; + const cpu_file = try host.fs.openFileAbsolute(cpu_path, .{}); + const cpu_name = host.basename(cpu_path); + try addFileToBundle(cpu_file, &bundle_file_list, cpu_name, &uncompressed); + + for (arguments_result.user_programs) |user_program| { + const file = try host.fs.openFileAbsolute(user_program, .{}); + const name = host.basename(user_program); + try addFileToBundle(file, &bundle_file_list, name, &uncompressed); + } + + const font_file = try host.cwd().openFile("resources/zap-light16.psf", .{}); + try addFileToBundle(font_file, &bundle_file_list, "font", &uncompressed); + + var compressed = host.ArrayList(u8).init(wrapped_allocator.zigUnwrap()); + var compressor = try lib.deflate.compressor(wrapped_allocator.zigUnwrap(), compressed.writer(), .{ .level = .best_compression }); + try compressor.writer().writeAll(uncompressed.items); + try compressor.close(); + + // Wait until here because reallocations can happen in the ArrayList + const bundle_sizes = @as(*align(1) [2]u32, @ptrCast(&bundle_file_list.items[0])); + bundle_sizes[0] = @as(u32, @intCast(uncompressed.items.len)); + bundle_sizes[1] = @as(u32, @intCast(compressed.items.len)); + + // { + // var stream = lib.fixedBufferStream(compressed.items); + // var decompressor = try lib.deflate.decompressor(wrapped_allocator.zigUnwrap(), stream.reader(), null); + // var decompressed = host.ArrayList(u8).init(wrapped_allocator.zigUnwrap()); + // try decompressor.reader().readAllArrayList(&decompressed, lib.maxInt(usize)); + // log.debug("DECOMPRESSED AFTER:", .{}); + // if (decompressor.close()) |err| return err; + // + // for (decompressed.items[0..20], uncompressed.items[0..20]) |byte, before| { + // assert(byte == before); + // log.debug("Byte: 0x{x}", .{byte}); + // } + // } + + try fat_partition_cache.makeNewFile("/files", bundle_file_list.items, wrapped_allocator.unwrap(), null, 0); + try fat_partition_cache.makeNewFile("/bundle", compressed.items, wrapped_allocator.unwrap(), null, 0); + + const loader_file_path = arguments_result.bootloader; + const loader_file = try readFileAbsolute(&wrapped_allocator, loader_file_path); + + switch (configuration.bootloader) { + .limine => { + // log.debug("Installing Limine HDD", .{}); + try limine_installer.install(disk_image.getBuffer(), false, null); + // log.debug("Ended installing Limine HDD", .{}); + const limine_installable_path = "src/bootloader/limine/installables"; + const limine_installable_dir = try host.cwd().openDir(limine_installable_path, .{}); + const loader_fat_path = try lib.concat(wrapped_allocator.zigUnwrap(), u8, &.{ "/", host.basename(loader_file_path) }); + try fat_partition_cache.makeNewFile(loader_fat_path, loader_file, wrapped_allocator.unwrap(), null, 0); + + const limine_cfg = blk: { + var limine_cfg_generator = LimineCFG{ + .buffer = host.ArrayList(u8).init(wrapped_allocator.zigUnwrap()), + }; + try limine_cfg_generator.addField("TIMEOUT", "0"); + try limine_cfg_generator.addEntryName("Rise"); + try limine_cfg_generator.addField("PROTOCOL", "limine"); + try limine_cfg_generator.addField("DEFAULT_ENTRY", "0"); + try limine_cfg_generator.addField("KERNEL_PATH", try lib.concat(wrapped_allocator.zigUnwrap(), u8, &.{ "boot:///", loader_fat_path })); + + try limine_cfg_generator.addField("MODULE_PATH", "boot:////bundle"); + try limine_cfg_generator.addField("MODULE_PATH", "boot:////files"); + break :blk limine_cfg_generator.buffer.items; + }; + + try fat_partition_cache.makeNewFile("/limine.cfg", limine_cfg, wrapped_allocator.unwrap(), null, @as(u64, @intCast(host.time.milliTimestamp()))); + const limine_sys = try limine_installable_dir.readFileAlloc(wrapped_allocator.zigUnwrap(), "limine.sys", max_file_length); + try fat_partition_cache.makeNewFile("/limine.sys", limine_sys, wrapped_allocator.unwrap(), null, @as(u64, @intCast(host.time.milliTimestamp()))); + + switch (configuration.architecture) { + .x86_64 => { + try fat_partition_cache.makeNewDirectory("/EFI", wrapped_allocator.unwrap(), null, @as(u64, @intCast(host.time.milliTimestamp()))); + try fat_partition_cache.makeNewDirectory("/EFI/BOOT", wrapped_allocator.unwrap(), null, @as(u64, @intCast(host.time.milliTimestamp()))); + try fat_partition_cache.makeNewFile("/EFI/BOOT/BOOTX64.EFI", try limine_installable_dir.readFileAlloc(wrapped_allocator.zigUnwrap(), "BOOTX64.EFI", max_file_length), wrapped_allocator.unwrap(), null, @as(u64, @intCast(host.time.milliTimestamp()))); + }, + else => unreachable, + } + }, + .rise => switch (configuration.boot_protocol) { + .bios => { + const partition_first_usable_lba = gpt_partition_cache.gpt.header.first_usable_lba; + assert((fat_partition_cache.partition_range.first_lba - partition_first_usable_lba) * disk.sector_size > lib.alignForward(usize, loader_file.len, disk.sector_size)); + try disk.writeSlice(u8, loader_file, partition_first_usable_lba, true); + + // Build our own assembler + const boot_disk_mbr_lba = 0; + const boot_disk_mbr = try disk.readTypedSectors(BootDisk, boot_disk_mbr_lba, null, .{}); + // const dap_offset = @offsetOf(BootDisk, "dap"); + // _ = dap_offset; + // lib.log.debug("DAP offset: 0x{x}", .{dap_offset}); + const aligned_file_size = lib.alignForward(usize, loader_file.len, disk.sector_size); + const text_section_guess = lib.alignBackward(u32, @as(*align(1) const u32, @ptrCast(&loader_file[0x18])).*, 0x1000); + if (lib.maxInt(u32) - text_section_guess < aligned_file_size) @panic("unexpected size"); + const dap_top = bios.stack_top - bios.stack_size; + if (aligned_file_size > dap_top) host.panic("File size: 0x{x} bytes", .{aligned_file_size}); + // log.debug("DAP top: 0x{x}. Aligned file size: 0x{x}", .{ dap_top, aligned_file_size }); + const dap = MBR.DAP{ + .sector_count = @as(u16, @intCast(@divExact(aligned_file_size, disk.sector_size))), + .offset = dap_file_read, + .segment = 0x0, + .lba = partition_first_usable_lba, + }; + + if (dap_top - dap.offset < aligned_file_size) { + @panic("unable to fit file read from disk"); + } + + // if (dap.offset - bios.loader_start < aligned_file_size) { + // @panic("unable to fit loaded executable in memory"); + // } + + try boot_disk_mbr.fill(wrapped_allocator.zigUnwrap(), dap); + try disk.writeTypedSectors(BootDisk, boot_disk_mbr, boot_disk_mbr_lba, false); + }, + .uefi => { + try fat_partition_cache.makeNewDirectory("/EFI", wrapped_allocator.unwrap(), null, 0); + try fat_partition_cache.makeNewDirectory("/EFI/BOOT", wrapped_allocator.unwrap(), null, 0); + try fat_partition_cache.makeNewFile("/EFI/BOOT/BOOTX64.EFI", loader_file, wrapped_allocator.unwrap(), null, 0); + }, + }, + } + }, + else => @panic("Filesystem not supported"), + } + + const image_file = try host.fs.createFileAbsolute(arguments_result.disk_image_path, .{}); + try image_file.writeAll(disk_image.getBuffer()); +} + +const LimineCFG = struct { + buffer: host.ArrayList(u8), + + pub fn addField(limine_cfg: *LimineCFG, field_name: []const u8, field_value: []const u8) !void { + try limine_cfg.buffer.appendSlice(field_name); + try limine_cfg.buffer.append('='); + try limine_cfg.buffer.appendSlice(field_value); + try limine_cfg.buffer.append('\n'); + } + + pub fn addEntryName(limine_cfg: *LimineCFG, entry_name: []const u8) !void { + try limine_cfg.buffer.append(':'); + try limine_cfg.buffer.appendSlice(entry_name); + try limine_cfg.buffer.append('\n'); + } +}; diff --git a/src/host/disk_image_builder/test.zig b/src/host/disk_image_builder/test.zig new file mode 100644 index 0000000..fb88212 --- /dev/null +++ b/src/host/disk_image_builder/test.zig @@ -0,0 +1,224 @@ +const lib = @import("lib"); +const assert = lib.assert; +const log = lib.log.scoped(.DISK_IMAGE_BUILDER); +const FAT32 = lib.Filesystem.FAT32; +const GPT = lib.PartitionTable.GPT; +const host = @import("host"); +const limine_installer = @import("limine_installer"); + +const disk_image_builder = @import("../disk_image_builder.zig"); +const ImageDescription = disk_image_builder.ImageDescription; +const DiskImage = disk_image_builder.DiskImage; + +const LoopbackDevice = struct { + name: []const u8, + mount_dir: ?[]const u8 = null, + + fn start(loopback_device: *LoopbackDevice, allocator: lib.ZigAllocator, image_path: []const u8) !void { + try host.spawnProcess(&.{ "./tools/loopback_start.sh", image_path, loopback_device.name }, allocator); + } + + fn end(loopback_device: *LoopbackDevice, allocator: lib.ZigAllocator) !void { + loopback_device.mount_dir = null; + try host.spawnProcess(&.{ "./tools/loopback_end.sh", loopback_device.name }, allocator); + try host.cwd().deleteFile(loopback_device.name); + } + + fn mount(loopback_device: *LoopbackDevice, allocator: lib.ZigAllocator, mount_dir: []const u8) !MountedPartition { + try host.cwd().makePath(mount_dir); + try host.spawnProcess(&.{ "./tools/loopback_mount.sh", loopback_device.name, mount_dir }, allocator); + loopback_device.mount_dir = mount_dir; + + return MountedPartition{ + .loopback_device = loopback_device.*, + }; + } +}; + +const MountedPartition = struct { + loopback_device: LoopbackDevice, + + fn mkdir(partition: MountedPartition, allocator: lib.ZigAllocator, dir: []const u8) !void { + try host.spawnProcess(&.{ "sudo", "mkdir", "-p", try partition.join_with_root(allocator, dir) }, allocator); + } + + fn join_with_root(partition: MountedPartition, allocator: lib.ZigAllocator, path: []const u8) ![]const u8 { + const mount_dir = partition.get_mount_dir(); + const slices_to_join: []const []const u8 = if (path[0] == '/') &.{ mount_dir, path } else &.{ mount_dir, "/", path }; + const joint_path = try lib.concat(allocator, u8, slices_to_join); + return joint_path; + } + + pub fn get_mount_dir(partition: MountedPartition) []const u8 { + const mount_dir = partition.loopback_device.mount_dir orelse @panic("get_mount_dir"); + return mount_dir; + } + + fn copy_file(partition: MountedPartition, allocator: lib.ZigAllocator, file_path: []const u8, file_content: []const u8) !void { + // TODO: make this work for Windows? + const last_slash_index = lib.lastIndexOf(u8, file_path, "/") orelse @panic("fat32: copy file last slash"); + const file_name = host.basename(file_path); + assert(file_name.len > 0); + try host.cwd().writeFile(file_name, file_content); + const dir = file_path[0..if (last_slash_index == 0) 1 else last_slash_index]; + const destination_dir = try partition.join_with_root(allocator, dir); + const mkdir_process_args = &.{ "sudo", "mkdir", "-p", destination_dir }; + try host.spawnProcess(mkdir_process_args, allocator); + const copy_process_args = &.{ "sudo", "cp", "-v", file_name, destination_dir }; + try host.spawnProcess(copy_process_args, allocator); + try host.cwd().deleteFile(file_name); + } + + fn end(partition: *MountedPartition, allocator: lib.ZigAllocator) !void { + const mount_dir = partition.loopback_device.mount_dir orelse @panic("mount partition end"); + host.sync(); + try host.spawnProcess(&.{ "sudo", "umount", mount_dir }, allocator); + host.spawnProcess(&.{ "sudo", "rm", "-rf", mount_dir }, allocator) catch |err| { + switch (err) { + host.ExecutionError.failed => {}, + else => return err, + } + }; + } +}; + +const ShellImage = struct { + path: []const u8, + description: ImageDescription, + + fn createFAT(image: ShellImage, allocator: lib.ZigAllocator) !void { + const megabytes = @divExact(image.description.disk_sector_count * image.description.disk_sector_size, lib.mb); + try host.spawnProcess(&.{ "dd", "if=/dev/zero", "bs=1M", "count=0", try lib.allocPrint(allocator, "seek={d}", .{megabytes}), try lib.allocPrint(allocator, "of={s}", .{image.path}) }, allocator); + + try host.spawnProcess(&.{ "parted", "-s", image.path, "mklabel", "gpt" }, allocator); + try host.spawnProcess(&.{ "parted", "-s", image.path, "mkpart", image.description.partition_name, @tagName(image.description.partition_filesystem), try lib.allocPrint(allocator, "{d}s", .{image.description.partition_start_lba}), "100%" }, allocator); + try host.spawnProcess(&.{ "parted", "-s", image.path, "set", "1", "esp", "on" }, allocator); + } + + fn toDiskImage(image: ShellImage, allocator: lib.ZigAllocator) !DiskImage { + return try DiskImage.fromFile(image.path, @as(u16, @intCast(image.description.disk_sector_size)), allocator); + } + + fn delete(image: ShellImage) !void { + try host.cwd().deleteFile(image.path); + } +}; + +const File = struct { + path: []const u8, + content: []const u8, +}; + +const limine_directories = [_][]const u8{ + "/EFI", "/EFI/BOOT", +}; + +const limine_files = [_]File{ + .{ .path = "/limine.cfg", .content = @embedFile("../../bootloader/limine/installables/limine.cfg") }, + .{ .path = "/limine.sys", .content = @embedFile("../../bootloader/limine/installables/limine.sys") }, +}; + +test "Limine barebones" { + lib.testing.log_level = .debug; + + // + // Using an arena allocator because it doesn't care about memory leaks + var arena_allocator = host.ArenaAllocator.init(host.page_allocator); + defer arena_allocator.deinit(); + + var wrapped_allocator = lib.Allocator.wrap(arena_allocator.allocator()); + + const deploy_limine = true; + + switch (lib.os) { + .linux => { + const image = ImageDescription{ + .partition_start_lba = 0x800, + .disk_sector_count = 131072, + .disk_sector_size = lib.default_sector_size, + .partition_name = "ESP", + .partition_filesystem = .fat32, + }; + + const test_path = "zig-cache/test_original.hdd"; + const test_image = ShellImage{ + .path = test_path, + .description = image, + }; + test_image.delete() catch {}; + + try test_image.createFAT(wrapped_allocator.zigUnwrap()); + if (deploy_limine and disk_image_builder.deploy(test_path, &limine_installer.hdd, limine_installer.hdd.len) != 0) { + @panic("asjdkajsd"); + } + + var loopback_device = LoopbackDevice{ .name = "loopback_device" }; + try loopback_device.start(wrapped_allocator.zigUnwrap(), test_path); + + log.debug("Formatting", .{}); + try host.spawnProcess(&.{ "./tools/format_loopback_fat32.sh", loopback_device.name }, wrapped_allocator.zigUnwrap()); + + const mount_dir = "image_mount"; + + var partition = try loopback_device.mount(wrapped_allocator.zigUnwrap(), mount_dir); + + for (limine_directories) |directory| { + try partition.mkdir(wrapped_allocator.zigUnwrap(), directory); + } + + for (limine_files) |file| { + try partition.copy_file(wrapped_allocator.zigUnwrap(), file.path, file.content); + } + + try partition.end(wrapped_allocator.zigUnwrap()); + try loopback_device.end(wrapped_allocator.zigUnwrap()); + + var original_disk_image = try test_image.toDiskImage(wrapped_allocator.zigUnwrap()); + const original_gpt_cache = try GPT.Partition.Cache.fromPartitionIndex(&original_disk_image.disk, 0, wrapped_allocator.unwrap()); + const original_fat_cache = try FAT32.Cache.fromGPTPartitionCache(wrapped_allocator.unwrap(), original_gpt_cache); + + var disk_image = try DiskImage.fromZero(image.disk_sector_count, image.disk_sector_size); + const gpt_partition_cache = try disk_image.createFAT(image, original_gpt_cache); + + const original_buffer = original_disk_image.getBuffer(); + const my_buffer = disk_image.getBuffer(); + + if (deploy_limine) { + try limine_installer.install(my_buffer, false, null); + } + + const fat_partition_cache = try disk_image_builder.format(gpt_partition_cache.gpt.disk, .{ + .first_lba = gpt_partition_cache.partition.first_lba, + .last_lba = gpt_partition_cache.partition.last_lba, + }, original_fat_cache.mbr); + + for (limine_directories) |directory| { + log.debug("Creating directory: {s}", .{directory}); + try fat_partition_cache.makeNewDirectory(directory, null, original_fat_cache, @as(u64, @intCast(host.time.milliTimestamp()))); + } + + for (limine_files) |file| { + log.debug("Creating file: {s}", .{file.path}); + try fat_partition_cache.makeNewFile(file.path, file.content, wrapped_allocator.unwrap(), original_fat_cache, @as(u64, @intCast(host.time.milliTimestamp()))); + } + + var diff_count: u32 = 0; + for (my_buffer, 0..) |mb, i| { + const ob = original_buffer[i]; + const diff = ob != mb; + if (diff) { + log.debug("[0x{x}] Diff. Expected: 0x{x}. Actual: 0x{x}", .{ i, ob, mb }); + } + diff_count += @intFromBool(diff); + } + + if (diff_count > 0) { + log.err("Diff count: {}", .{diff_count}); + } + try lib.testing.expectEqualSlices(u8, original_buffer, my_buffer); + + try test_image.delete(); + }, + else => {}, + } +} diff --git a/src/host/runner/main.zig b/src/host/runner/main.zig new file mode 100644 index 0000000..1e16588 --- /dev/null +++ b/src/host/runner/main.zig @@ -0,0 +1,350 @@ +const lib = @import("lib"); +const assert = lib.assert; +const log = lib.log.scoped(.RUNNER); +const host = @import("host"); +const Configuration = lib.Configuration; + +const Error = error{ + wrong_argument_count, + disk_image_path_not_found, + cpu_driver_not_found, + loader_path_not_found, + qemu_options_not_found, + configuration_not_found, + configuration_wrong_argument, + ci_not_found, + debug_user_not_found, + debug_loader_not_found, + init_not_found, + image_configuration_path_not_found, + qemu_error, + not_implemented, + architecture_not_supported, + execution_environment_not_supported, +}; + +pub fn main() anyerror!void { + const max_file_length = lib.maxInt(usize); + var arena_allocator = host.ArenaAllocator.init(host.page_allocator); + defer arena_allocator.deinit(); + var wrapped_allocator = lib.Allocator.wrap(arena_allocator.allocator()); + + const arguments_result: lib.ArgumentParser.Runner.Result = blk: { + const arguments = (try host.allocateArguments(wrapped_allocator.zigUnwrap()))[1..]; + + var argument_parser = lib.ArgumentParser.Runner{}; + var argument_disk_image_path: ?[]const u8 = null; + var argument_cpu_driver_path: ?[]const u8 = null; + var argument_loader_path: ?[]const u8 = null; + var argument_qemu_options: ?lib.QEMUOptions = null; + var argument_configuration: ?Configuration = null; + var argument_image_configuration_path: ?[]const u8 = null; + var argument_ci: ?bool = null; + var argument_debug_user: ?bool = null; + var argument_debug_loader: ?bool = null; + var argument_init_path: ?[]const u8 = null; + var argument_index: usize = 0; + + while (argument_parser.next()) |argument_type| switch (argument_type) { + .disk_image_path => { + argument_disk_image_path = arguments[argument_index]; + argument_index += 1; + }, + .cpu_driver => { + argument_cpu_driver_path = arguments[argument_index]; + argument_index += 1; + }, + .loader_path => { + argument_loader_path = arguments[argument_index]; + argument_index += 1; + }, + .qemu_options => { + const boolean_argument_strings = [2][]const u8{ arguments[argument_index], arguments[argument_index + 1] }; + argument_index += 2; + + argument_qemu_options = undefined; + inline for (lib.fields(lib.QEMUOptions), 0..) |field, field_index| { + @field(argument_qemu_options.?, field.name) = if (lib.equal(u8, boolean_argument_strings[field_index], "true")) true else if (lib.equal(u8, boolean_argument_strings[field_index], "false")) false else return Error.qemu_options_not_found; + } + }, + .configuration => { + argument_configuration = undefined; + const configuration = &argument_configuration.?; + inline for (lib.fields(Configuration)) |field| { + @field(configuration, field.name) = lib.stringToEnum(field.type, arguments[argument_index]) orelse return Error.configuration_wrong_argument; + argument_index += 1; + } + }, + .image_configuration_path => { + argument_image_configuration_path = arguments[argument_index]; + argument_index += 1; + }, + .ci => { + argument_ci = if (lib.equal(u8, arguments[argument_index], "true")) true else if (lib.equal(u8, arguments[argument_index], "false")) false else return Error.ci_not_found; + argument_index += 1; + }, + .debug_user => { + argument_debug_user = if (lib.equal(u8, arguments[argument_index], "true")) true else if (lib.equal(u8, arguments[argument_index], "false")) false else return Error.debug_user_not_found; + argument_index += 1; + }, + .debug_loader => { + argument_debug_loader = if (lib.equal(u8, arguments[argument_index], "true")) true else if (lib.equal(u8, arguments[argument_index], "false")) false else return Error.debug_loader_not_found; + argument_index += 1; + }, + .init => { + argument_init_path = arguments[argument_index]; + argument_index += 1; + }, + }; + + if (argument_index != arguments.len) return Error.wrong_argument_count; + + break :blk .{ + .disk_image_path = argument_disk_image_path orelse return Error.disk_image_path_not_found, + .cpu_driver = argument_cpu_driver_path orelse return Error.cpu_driver_not_found, + .loader_path = argument_loader_path orelse return Error.loader_path_not_found, + .qemu_options = argument_qemu_options orelse return Error.qemu_options_not_found, + .configuration = argument_configuration orelse return Error.configuration_not_found, + .image_configuration_path = argument_image_configuration_path orelse return Error.image_configuration_path_not_found, + .ci = argument_ci orelse return Error.ci_not_found, + .debug_user = argument_debug_user orelse return Error.debug_user_not_found, + .debug_loader = argument_debug_loader orelse return Error.debug_loader_not_found, + .init = argument_init_path orelse return Error.init_not_found, + }; + }; + + switch (arguments_result.configuration.execution_environment) { + .qemu => { + const qemu_options = arguments_result.qemu_options; + + const config_file = try host.cwd().readFileAlloc(wrapped_allocator.zigUnwrap(), "config/qemu.json", max_file_length); + const parsed_arguments = try lib.json.parseFromSlice(Arguments, wrapped_allocator.zigUnwrap(), config_file, .{}); + const arguments = parsed_arguments.value; + + var argument_list = host.ArrayList([]const u8).init(wrapped_allocator.zigUnwrap()); + + try argument_list.append(try lib.concat(wrapped_allocator.zigUnwrap(), u8, &.{ "qemu-system-", @tagName(arguments_result.configuration.architecture) })); + + if (qemu_options.is_test and !qemu_options.is_debug) { + try argument_list.appendSlice(&.{ "-device", try lib.allocPrint(wrapped_allocator.zigUnwrap(), "isa-debug-exit,iobase=0x{x:0>2},iosize=0x{x:0>2}", .{ lib.QEMU.isa_debug_exit.io_base, lib.QEMU.isa_debug_exit.io_size }) }); + } + + switch (arguments_result.configuration.boot_protocol) { + .uefi => try argument_list.appendSlice(&.{ "-bios", "tools/OVMF_CODE-pure-efi.fd" }), + else => {}, + } + + const image_config = try lib.ImageConfig.get(wrapped_allocator.zigUnwrap(), arguments_result.image_configuration_path); + _ = image_config; + const disk_image_path = arguments_result.disk_image_path; + try argument_list.appendSlice(&.{ "-drive", try lib.allocPrint(wrapped_allocator.zigUnwrap(), "file={s},index=0,media=disk,format=raw", .{disk_image_path}) }); + + try argument_list.append("-no-reboot"); + + if (!qemu_options.is_test) { + try argument_list.append("-no-shutdown"); + } + + if (arguments_result.ci) { + try argument_list.appendSlice(&.{ "-display", "none" }); + } + + //if (arguments.vga) |vga| { + //try argument_list.append("-vga"); + //try argument_list.append(@tagName(vga)); + //} + + if (arguments.smp) |smp| { + try argument_list.append("-smp"); + const smp_string = try lib.allocPrint(wrapped_allocator.zigUnwrap(), "{}", .{smp}); + try argument_list.append(smp_string); + } + + if (arguments.debugcon) |debugcon| { + try argument_list.append("-debugcon"); + try argument_list.append(@tagName(debugcon)); + } + + if (arguments.memory) |memory| { + try argument_list.append("-m"); + if (arguments_result.ci) { + try argument_list.append("1G"); + } else { + const memory_argument = try lib.allocPrint(wrapped_allocator.zigUnwrap(), "{}{c}", .{ memory.amount, @as(u8, switch (memory.unit) { + .kilobyte => 'K', + .megabyte => 'M', + .gigabyte => 'G', + else => @panic("Unit too big"), + }) }); + try argument_list.append(memory_argument); + } + } + + if (lib.canVirtualizeWithQEMU(arguments_result.configuration.architecture, arguments_result.ci) and (arguments_result.configuration.execution_type == .accelerated or (arguments.virtualize orelse false))) { + try argument_list.appendSlice(&.{ + "-accel", + switch (lib.os) { + .windows => "whpx", + .linux => "kvm", + .macos => "hvf", + else => @compileError("OS not supported"), + }, + "-cpu", + "host", + }); + } else { + switch (arguments_result.configuration.architecture) { + .x86_64 => try argument_list.appendSlice(&.{ "-cpu", "max" }), + else => return Error.architecture_not_supported, + } + + if (arguments.trace) |tracees| { + for (tracees) |tracee| { + const tracee_slice = try lib.allocPrint(wrapped_allocator.zigUnwrap(), "-{s}*", .{tracee}); + try argument_list.append("-trace"); + try argument_list.append(tracee_slice); + } + } + + if (!arguments_result.ci) { + if (arguments.log) |log_configuration| { + var log_what = host.ArrayList(u8).init(wrapped_allocator.zigUnwrap()); + + if (log_configuration.guest_errors) try log_what.appendSlice("guest_errors,"); + if (log_configuration.interrupts) try log_what.appendSlice("int,"); + if (!arguments_result.ci and log_configuration.assembly) try log_what.appendSlice("in_asm,"); + + if (log_what.items.len > 0) { + // Delete the last comma + _ = log_what.pop(); + + try argument_list.append("-d"); + try argument_list.append(log_what.items); + + if (log_configuration.interrupts) { + try argument_list.appendSlice(&.{ "-machine", "smm=off" }); + } + } + + if (log_configuration.file) |log_file| { + try argument_list.append("-D"); + try argument_list.append(log_file); + } + } + } + } + + if (qemu_options.is_debug) { + try argument_list.append("-s"); + if (!(arguments_result.configuration.execution_type == .accelerated or (arguments.virtualize orelse false))) { + try argument_list.append("-S"); + } + + // GF2, when not found in the PATH, can give problems + const use_gf = switch (lib.os) { + .macos => false, + .linux => false, + else => false, + }; + + var command_line_gdb = host.ArrayList([]const u8).init(wrapped_allocator.zigUnwrap()); + if (use_gf) { + try command_line_gdb.append("gf2"); + } else { + try command_line_gdb.append("kitty"); + try command_line_gdb.append(switch (lib.os) { + .linux => "gdb", + .macos => "x86_64-elf-gdb", + else => "gdb", + }); + } + + try command_line_gdb.appendSlice(&.{ "-ex", switch (arguments_result.configuration.architecture) { + .x86_64 => "set disassembly-flavor intel\n", + else => return Error.architecture_not_supported, + } }); + + try command_line_gdb.appendSlice(&.{ "-ex", "target remote localhost:1234" }); + if (arguments_result.debug_user) { + assert(!arguments_result.debug_loader); + try command_line_gdb.appendSlice(&.{ "-ex", try lib.allocPrint(wrapped_allocator.zigUnwrap(), "symbol-file {s}", .{arguments_result.init}) }); + } else if (arguments_result.debug_loader) { + assert(!arguments_result.debug_user); + try command_line_gdb.appendSlice(&.{ "-ex", try lib.allocPrint(wrapped_allocator.zigUnwrap(), "symbol-file {s}", .{arguments_result.loader_path}) }); + } else { + try command_line_gdb.appendSlice(&.{ "-ex", try lib.allocPrint(wrapped_allocator.zigUnwrap(), "symbol-file {s}", .{arguments_result.cpu_driver}) }); + } + + const gdb_script_file = try host.cwd().openFile("config/gdb_script", .{}); + var gdb_script_reader = gdb_script_file.reader(); + while (try gdb_script_reader.readUntilDelimiterOrEofAlloc(wrapped_allocator.zigUnwrap(), '\n', max_file_length)) |gdb_script_line| { + try command_line_gdb.appendSlice(&.{ "-ex", gdb_script_line }); + } + + const debugger_process_arguments = switch (lib.os) { + .linux, .macos => command_line_gdb.items, + else => return Error.not_implemented, + }; + + var debugger_process = host.ChildProcess.init(debugger_process_arguments, wrapped_allocator.zigUnwrap()); + try debugger_process.spawn(); + } + + var process = host.ChildProcess.init(argument_list.items, wrapped_allocator.zigUnwrap()); + const result = try process.spawnAndWait(); + + if (result == .Exited) { + const exit_code = result.Exited; + if (exit_code & 1 != 0) { + const mask = lib.maxInt(@TypeOf(exit_code)) - 1; + const masked_exit_code = exit_code & mask; + + if (masked_exit_code != 0) { + const qemu_exit_code = @as(lib.QEMU.ExitCode, @enumFromInt(masked_exit_code >> 1)); + + switch (qemu_exit_code) { + .success => return log.info("Success!", .{}), + .failure => log.err("QEMU exited with failure code 0x{x}", .{exit_code}), + _ => log.err("Totally unexpected value", .{}), + } + } else log.err("QEMU exited with unexpected code: {}. Masked: {}", .{ exit_code, masked_exit_code }); + } else log.err("QEMU exited with unexpected code: {}", .{exit_code}); + } else { + log.err("QEMU was {s}", .{@tagName(result)}); + } + + return Error.qemu_error; + }, + } +} + +const Arguments = struct { + const VGA = enum { + std, + cirrus, + vmware, + qxl, + xenfb, + tcx, + cg3, + virtio, + none, + }; + memory: ?struct { + amount: u64, + unit: lib.SizeUnit, + }, + virtualize: ?bool, + vga: ?VGA, + smp: ?usize, + debugcon: ?enum { + stdio, + }, + log: ?struct { + file: ?[]const u8, + guest_errors: bool, + assembly: bool, + interrupts: bool, + }, + trace: ?[]const []const u8, +}; diff --git a/src/host/test.zig b/src/host/test.zig new file mode 100644 index 0000000..e39b05a --- /dev/null +++ b/src/host/test.zig @@ -0,0 +1,6 @@ +const lib = @import("lib"); +const host = @import("host"); +test "Host tests" { + _ = lib; + _ = host; +} diff --git a/src/lib.zig b/src/lib.zig new file mode 100644 index 0000000..7dbcc0a --- /dev/null +++ b/src/lib.zig @@ -0,0 +1,1080 @@ +const common = @import("common.zig"); +pub usingnamespace common; + +pub const arch = @import("lib/arch.zig"); +/// This is done so the allocator can respect allocating from different address spaces +pub const config = @import("lib/config.zig"); +pub const CRC32 = @import("lib/crc32.zig"); +const disk_file = @import("lib/disk.zig"); +pub const Disk = disk_file.Disk; +pub const Filesystem = @import("lib/filesystem.zig"); +pub const NLS = @import("lib/nls.zig"); +pub const PartitionTable = @import("lib/partition_table.zig"); +pub const PSF1 = @import("lib/psf1.zig"); +pub const Spinlock = arch.Spinlock; + +const extern_enum_array = @import("lib/extern_enum_array.zig"); +pub const EnumArray = extern_enum_array.EnumArray; + +pub fn memcpy(noalias destination: []u8, noalias source: []const u8) void { + @setRuntimeSafety(false); + // Using this as the Zig implementation is really slow (at least in x86 with soft_float enabled + // if (common.cpu.arch == .x86 or common.cpu.arch == .x86_64 and common.Target.x86.featureSetHas(common.cpu.features, .soft_float)) { + const bytes_left = switch (common.cpu.arch) { + .x86 => asm volatile ( + \\rep movsb + : [ret] "={ecx}" (-> usize), + : [dest] "{edi}" (destination.ptr), + [src] "{esi}" (source.ptr), + [len] "{ecx}" (source.len), + ), + .x86_64 => asm volatile ( + \\rep movsb + : [ret] "={rcx}" (-> usize), + : [dest] "{rdi}" (destination.ptr), + [src] "{rsi}" (source.ptr), + [len] "{rcx}" (source.len), + ), + else => @compileError("Unreachable"), + }; + + common.assert(bytes_left == 0); + // } else { + // @memcpy(destination, source); + // } +} + +// pub fn memset(comptime T: type, slice: []T, elem: T) void { +// @setRuntimeSafety(false); +// +// const bytes_left = switch (T) { +// u8 => switch (common.cpu.arch) { +// .x86 => asm volatile ( +// \\rep stosb +// : [ret] "={ecx}" (-> usize), +// : [slice] "{edi}" (slice.ptr), +// [len] "{ecx}" (slice.len), +// [element] "{al}" (elem), +// ), +// .x86_64 => asm volatile ( +// \\rep movsb +// : [ret] "={rcx}" (-> usize), +// : [slice] "{rdi}" (slice.ptr), +// [len] "{rcx}" (slice.len), +// [element] "{al}" (elem), +// ), +// else => @compileError("Unsupported OS"), +// }, +// else => @compileError("Type " ++ @typeName(T) ++ " not supported"), +// }; +// +// common.assert(bytes_left == 0); +// } + +pub fn EnumStruct(comptime Enum: type, comptime Value: type) type { + const EnumFields = common.enumFields(Enum); + const MyEnumStruct = @Type(.{ + .Struct = .{ + .layout = .Extern, + .fields = &blk: { + var arr = [1]common.Type.StructField{undefined} ** EnumFields.len; + inline for (EnumFields) |EnumValue| { + arr[EnumValue.value] = .{ + .name = EnumValue.name, + .type = Value, + .default_value = null, + .is_comptime = false, + .alignment = @alignOf(Value), + }; + } + break :blk arr; + }, + .decls = &.{}, + .is_tuple = false, + }, + }); + const MyEnumArray = EnumArray(Enum, Value); + const Union = extern union { + fields: Struct, + array: Array, + + pub const Struct = MyEnumStruct; + pub const Array = MyEnumArray; + }; + + common.assert(@sizeOf(Union.Struct) == @sizeOf(Union.Array)); + common.assert(@sizeOf(Union.Array) == @sizeOf(Union)); + + return Union; +} + +pub const DirectoryTokenizer = struct { + string: []const u8, + index: usize = 0, + given_count: usize = 0, + total_count: usize, + + pub fn init(string: []const u8) DirectoryTokenizer { + common.assert(string.len > 0); + var count: usize = 0; + + if (string[0] == '/') { + for (string) |ch| { + count += @intFromBool(ch == '/'); + } + } else unreachable; + + return .{ .string = string, .total_count = count + 1 }; + } + + pub fn next(tokenizer: *DirectoryTokenizer) ?[]const u8 { + if (tokenizer.index == 0) { + const is_root_dir = tokenizer.string[0] == '/'; + if (is_root_dir) { + tokenizer.index += 1; + tokenizer.given_count += 1; + return "/"; + } else unreachable; + } else { + const original_index = tokenizer.index; + if (original_index < tokenizer.string.len) { + for (tokenizer.string[original_index..]) |char| { + if (char == '/') { + const result = tokenizer.string[original_index..tokenizer.index]; + tokenizer.given_count += 1; + tokenizer.index += 1; + return result; + } + + tokenizer.index += 1; + } + + tokenizer.given_count += 1; + + return tokenizer.string[original_index..]; + } else { + common.assert(original_index == tokenizer.string.len); + common.assert(tokenizer.given_count == tokenizer.total_count); + return null; + } + } + } + + pub fn is_last(tokenizer: DirectoryTokenizer) bool { + return tokenizer.given_count == tokenizer.total_count; + } + + test "directory tokenizer" { + common.log.err("ajskdjsa", .{}); + if (common.os != .freestanding) { + const TestCase = struct { + path: []const u8, + expected_result: []const []const u8, + }; + + const test_cases = [_]TestCase{ + .{ .path = "/EFI", .expected_result = &.{ "/", "EFI" } }, + .{ .path = "/abc/def/a", .expected_result = &.{ "/", "abc", "def", "a" } }, + }; + + inline for (test_cases) |case| { + var dir_tokenizer = DirectoryTokenizer.init(case.path); + var results: [case.expected_result.len][]const u8 = undefined; + var result_count: usize = 0; + + while (dir_tokenizer.next()) |dir| { + try common.testing.expect(result_count < results.len); + try common.testing.expectEqualStrings(case.expected_result[result_count], dir); + results[result_count] = dir; + result_count += 1; + } + + try common.testing.expectEqual(case.expected_result.len, result_count); + } + } + } +}; + +pub inline fn ptrAdd(comptime T: type, ptr: *T, element_offset: usize) *T { + return @as(*T, @ptrFromInt(@intFromPtr(ptr) + @sizeOf(T) * element_offset)); +} + +pub inline fn maybePtrAdd(comptime T: type, ptr: ?*T, element_offset: usize) ?*T { + return @as(*T, @ptrFromInt(@intFromPtr(ptr) + @sizeOf(T) * element_offset)); +} + +pub inline fn ptrSub(comptime T: type, ptr: *T, element_offset: usize) *T { + return @as(*T, @ptrFromInt(@intFromPtr(ptr) - @sizeOf(T) * element_offset)); +} + +pub inline fn maybePtrSub(comptime T: type, ptr: ?*T, element_offset: usize) ?*T { + return @as(*T, @ptrFromInt(@intFromPtr(ptr) - @sizeOf(T) * element_offset)); +} + +test { + common.log.err("test not taken into the test suite"); + _ = DirectoryTokenizer; + _ = Filesystem; + _ = PartitionTable; +} + +pub const Allocator = extern struct { + callbacks: Callbacks align(8), + + pub const Allocate = struct { + pub const Result = extern struct { + address: u64, + size: u64, + + pub fn toBytes(result: Result) []u8 { + return @as([*]u8, @ptrFromInt(result.address))[0..result.size]; + } + }; + pub const Fn = fn (allocator: *Allocator, size: u64, alignment: u64) Error!Result; + pub const Error = error{ + OutOfMemory, + }; + }; + + /// Necessary to do this hack + const Callbacks = switch (common.cpu.arch) { + .x86 => extern struct { + allocate: *const Allocate.Fn, + allocate_padding: u32 = 0, + }, + .x86_64, .aarch64, .riscv64 => extern struct { + allocate: *const Allocate.Fn, + }, + else => @compileError("Architecture not supported"), + }; + + pub inline fn allocateBytes(allocator: *Allocator, size: u64, alignment: u64) Allocate.Error!Allocate.Result { + return try allocator.callbacks.allocate(allocator, size, alignment); + } + + pub inline fn allocate(allocator: *Allocator, comptime T: type, len: usize) Allocate.Error![]T { + const size = @sizeOf(T) * len; + const alignment = @alignOf(T); + const allocation_result = try allocator.callbacks.allocate(allocator, size, alignment); + const result = @as([*]T, @ptrFromInt(safeArchitectureCast(allocation_result.address)))[0..len]; + return result; + } + + pub inline fn create(allocator: *Allocator, comptime T: type) Allocate.Error!*T { + const result = try allocator.allocate(T, 1); + return &result[0]; + } + + pub fn wrap(zig_allocator: common.ZigAllocator) Wrapped { + return .{ + .allocator = .{ + .callbacks = .{ + .allocate = Wrapped.wrappedCallbackAllocate, + }, + }, + .zig = .{ + .ptr = zig_allocator.ptr, + .vtable = zig_allocator.vtable, + }, + }; + } + + pub fn zigUnwrap(allocator: *Allocator) common.ZigAllocator { + return .{ + .ptr = allocator, + .vtable = &zig_vtable, + }; + } + + pub const zig_vtable = .{ + .alloc = zigAllocate, + .resize = zigResize, + .free = zigFree, + }; + + pub fn zigAllocate(context: *anyopaque, size: usize, ptr_align: u8, return_address: usize) ?[*]u8 { + _ = context; + _ = size; + _ = ptr_align; + _ = return_address; + return null; + } + + pub fn zigResize(context: *anyopaque, buffer: []u8, buffer_alignment: u8, new_length: usize, return_address: usize) bool { + _ = context; + _ = buffer; + _ = buffer_alignment; + _ = new_length; + _ = return_address; + return false; + } + + pub fn zigFree(context: *anyopaque, buffer: []u8, buffer_alignment: u8, return_address: usize) void { + _ = context; + _ = buffer; + _ = buffer_alignment; + _ = return_address; + } + + pub const Wrapped = extern struct { + allocator: Allocator, + zig: extern struct { + ptr: *anyopaque, + vtable: *const common.ZigAllocator.VTable, + }, + + pub fn unwrap(wrapped_allocator: *Wrapped) *Allocator { + return &wrapped_allocator.allocator; + } + + pub fn zigUnwrap(wrapped_allocator: *Wrapped) common.ZigAllocator { + return .{ + .ptr = wrapped_allocator.zig.ptr, + .vtable = wrapped_allocator.zig.vtable, + }; + } + + pub fn wrappedCallbackAllocate(allocator: *Allocator, size: u64, alignment: u64) Allocator.Allocate.Error!Allocator.Allocate.Result { + const wrapped_allocator = @fieldParentPtr(Wrapped, "allocator", allocator); + const zig_allocator = wrapped_allocator.zigUnwrap(); + if (alignment > common.maxInt(u8)) { + @panic("alignment supported by Zig is less than asked"); + } + const zig_result = zig_allocator.vtable.alloc(zig_allocator.ptr, size, @as(u8, @intCast(alignment)), @returnAddress()); + return .{ + .address = @intFromPtr(zig_result), + .size = size, + }; + } + }; +}; + +pub fn ELF(comptime bits: comptime_int) type { + const is_64 = switch (bits) { + 32 => @compileError("ELF file is not supported"), + 64 => true, + else => @compileError("ELF is not supported for those bits"), + }; + + return struct { + const Address = if (is_64) u64 else u32; + + pub const Parser = struct { + file_header: *const FileHeader, + + pub fn init(file: []const u8) Error!Parser { + if (file.len < @sizeOf(FileHeader)) { + return Error.not_long_enough; + } + + const file_header: *const FileHeader = @ptrCast(@alignCast(&file[0])); + try file_header.validate(); + + return Parser{ + .file_header = file_header, + }; + } + + pub fn getEntryPoint(parser: *const Parser) Address { + return parser.file_header.entry_point; + } + + pub fn getProgramHeaders(parser: *const Parser) []const ProgramHeader { + const program_headers = @as([*]const ProgramHeader, @ptrFromInt(@intFromPtr(parser.file_header) + @as(usize, @intCast(parser.file_header.program_header_offset))))[0..parser.file_header.program_header_entry_count]; + return program_headers; + } + + pub fn getSectionHeaders(parser: *const Parser) []const SectionHeader { + const section_headers = @as([*]const SectionHeader, @ptrFromInt(@intFromPtr(parser.file_header) + @as(usize, @intCast(parser.file_header.section_header_offset))))[0..parser.file_header.section_header_entry_count]; + return section_headers; + } + + pub const Error = error{ + not_long_enough, + invalid_magic, + invalid_signature, + invalid_bits, + weird_program_header_size, + weird_section_header_size, + }; + }; + + pub const FileHeader = switch (is_64) { + true => extern struct { + // e_ident + magic: u8 = magic, + elf_id: [3]u8 = elf_signature.*, + bit_count: Bits = .b64, + endianness: Endianness = .little, + header_version: u8 = 1, + os_abi: ABI = .SystemV, + abi_version: u8 = 0, + padding: [7]u8 = [_]u8{0} ** 7, + object_type: ObjectFileType = .executable, // e_type + machine: Machine = .AMD64, + version: u32 = 1, + entry_point: u64, + program_header_offset: u64 = 0x40, + section_header_offset: u64, + flags: u32 = 0, + header_size: u16 = 0x40, + program_header_size: u16 = @sizeOf(ProgramHeader), + program_header_entry_count: u16 = 1, + section_header_size: u16 = @sizeOf(SectionHeader), + section_header_entry_count: u16, + name_section_header_index: u16, + + const magic = 0x7f; + const elf_signature = "ELF"; + const Bits = enum(u8) { + b32 = 1, + b64 = 2, + }; + + const Endianness = enum(u8) { + little = 1, + big = 2, + }; + + const ABI = enum(u8) { + SystemV = 0, + }; + + pub const ObjectFileType = enum(u16) { + none = 0, + relocatable = 1, + executable = 2, + dynamic = 3, + core = 4, + lo_os = 0xfe00, + hi_os = 0xfeff, + lo_proc = 0xff00, + hi_proc = 0xffff, + }; + + pub const Machine = enum(u16) { + AMD64 = 0x3e, + }; + + pub fn validate(file_header: *const FileHeader) Parser.Error!void { + if (file_header.magic != FileHeader.magic) { + return Parser.Error.invalid_magic; + } + + if (!common.equal(u8, &file_header.elf_id, FileHeader.elf_signature)) { + return Parser.Error.invalid_signature; + } + + switch (file_header.bit_count) { + .b32 => if (bits != 32) return Parser.Error.invalid_bits, + .b64 => if (bits != 64) return Parser.Error.invalid_bits, + } + + if (file_header.program_header_size != @sizeOf(ProgramHeader)) { + return Parser.Error.weird_program_header_size; + } + + if (file_header.section_header_size != @sizeOf(SectionHeader)) { + return Parser.Error.weird_section_header_size; + } + } + }, + false => @compileError("Not yet supported"), + }; + + pub const ProgramHeader = switch (is_64) { + true => extern struct { + type: Type = .load, + flags: Flags, //= @enumToInt(Flags.readable) | @enumToInt(Flags.executable), + offset: u64, + virtual_address: u64, + physical_address: u64, + size_in_file: u64, + size_in_memory: u64, + alignment: u64 = 0, + + const Type = enum(u32) { + null = 0, + load = 1, + dynamic = 2, + interpreter = 3, + note = 4, + shlib = 5, // reserved + program_header = 6, + tls = 7, + lo_os = 0x60000000, + gnu_eh_frame = 0x6474e550, + gnu_stack = 0x6474e551, + hi_os = 0x6fffffff, + lo_proc = 0x70000000, + hi_proc = 0x7fffffff, + _, + }; + + const Flags = packed struct { + executable: bool, + writable: bool, + readable: bool, + reserved: u29, + + comptime { + common.assert(@sizeOf(Flags) == @sizeOf(u32)); + } + }; + }, + false => @compileError("Not yet supported"), + }; + pub const SectionHeader = switch (is_64) { + true => extern struct { + name_offset: u32, + type: u32, + flags: u64, + address: u64, + offset: u64, + size: u64, + // section index + link: u32, + info: u32, + alignment: u64, + entry_size: u64, + + // type + const ID = enum(u32) { + null = 0, + program_data = 1, + symbol_table = 2, + string_table = 3, + relocation_entries_addends = 4, + symbol_hash_table = 5, + dynamic_linking_info = 6, + notes = 7, + program_space_no_data = 8, + relocation_entries = 9, + reserved = 10, + dynamic_linker_symbol_table = 11, + array_of_constructors = 14, + array_of_destructors = 15, + array_of_pre_constructors = 16, + section_group = 17, + extended_section_indices = 18, + number_of_defined_types = 19, + start_os_specific = 0x60000000, + }; + + const Flag = enum(u64) { + writable = 0x01, + alloc = 0x02, + executable = 0x04, + mergeable = 0x10, + contains_null_terminated_strings = 0x20, + info_link = 0x40, + link_order = 0x80, + os_non_conforming = 0x100, + section_group = 0x200, + tls = 0x400, + mask_os = 0x0ff00000, + mask_processor = 0xf0000000, + ordered = 0x4000000, + exclude = 0x8000000, + }; + }, + false => @compileError("Not yet supported"), + }; + }; +} + +pub inline fn safeArchitectureCast(value: anytype) usize { + return switch (@sizeOf(@TypeOf(value)) > @sizeOf(usize)) { + true => if (value <= common.maxInt(usize)) @as(usize, @truncate(value)) else { + common.log.err("PANIC: virtual address is longer than usize: 0x{x}", .{value}); + @panic("safeArchitectureCast"); + }, + false => value, + }; +} + +pub const DereferenceError = error{ + address_bigger_than_usize, +}; + +pub inline fn tryDereferenceAddress(value: anytype) DereferenceError!usize { + common.assert(@sizeOf(@TypeOf(value)) > @sizeOf(usize)); + return if (value <= common.maxInt(usize)) @as(usize, @truncate(value)) else return DereferenceError.address_bigger_than_usize; +} + +pub fn enumAddNames(comptime enum_fields: []const common.Type.EnumField, comptime names: []const []const u8) []const common.Type.EnumField { + comptime var result = enum_fields; + const previous_last_value = if (enum_fields.len > 0) enum_fields[enum_fields.len - 1].value else 0; + + inline for (names, 0..) |name, value_start| { + const value = value_start + previous_last_value; + result = result ++ .{.{ + .name = name, + .value = value, + }}; + } + + return result; +} + +pub fn ErrorSet(comptime error_names: []const []const u8, comptime predefined_fields: []const common.Type.EnumField) type { + comptime var error_fields: []const common.Type.Error = &.{}; + comptime var enum_items: []const common.Type.EnumField = predefined_fields; + comptime var enum_value = enum_items[enum_items.len - 1].value + 1; + + inline for (error_names) |error_name| { + enum_items = enum_items ++ [1]common.Type.EnumField{ + .{ + .name = error_name, + .value = enum_value, + }, + }; + + enum_value += 1; + } + + inline for (enum_items) |item| { + error_fields = error_fields ++ [1]common.Type.Error{ + .{ + .name = item.name, + }, + }; + } + + const EnumType = @Type(common.Type{ + .Enum = .{ + .tag_type = u16, + .fields = enum_items, + .decls = &.{}, + .is_exhaustive = true, + }, + }); + + const ErrorType = @Type(common.Type{ + .ErrorSet = error_fields, + }); + + return struct { + pub const Error = ErrorType; + pub const Enum = EnumType; + }; +} + +pub fn getDebugInformation(allocator: common.ZigAllocator, elf_file: []align(common.default_sector_size) const u8) !common.ModuleDebugInfo { + const elf = common.elf; + var module_debug_info: common.ModuleDebugInfo = undefined; + _ = module_debug_info; + const hdr = @as(*const elf.Ehdr, @ptrCast(&elf_file[0])); + if (!common.equal(u8, hdr.e_ident[0..4], elf.MAGIC)) return error.InvalidElfMagic; + if (hdr.e_ident[elf.EI_VERSION] != 1) return error.InvalidElfVersion; + + const endian = .Little; + + const shoff = hdr.e_shoff; + const str_section_off = shoff + @as(u64, hdr.e_shentsize) * @as(u64, hdr.e_shstrndx); + const str_shdr = @as( + *const elf.Shdr, + @ptrCast(@alignCast(&elf_file[common.cast(usize, str_section_off) orelse return error.Overflow])), + ); + const header_strings = elf_file[str_shdr.sh_offset .. str_shdr.sh_offset + str_shdr.sh_size]; + const shdrs = @as( + [*]const elf.Shdr, + @ptrCast(@alignCast(&elf_file[shoff])), + )[0..hdr.e_shnum]; + + var opt_debug_info: ?[]const u8 = null; + var opt_debug_abbrev: ?[]const u8 = null; + var opt_debug_str: ?[]const u8 = null; + var opt_debug_str_offsets: ?[]const u8 = null; + var opt_debug_line: ?[]const u8 = null; + var opt_debug_line_str: ?[]const u8 = null; + var opt_debug_ranges: ?[]const u8 = null; + var opt_debug_loclists: ?[]const u8 = null; + var opt_debug_rnglists: ?[]const u8 = null; + var opt_debug_addr: ?[]const u8 = null; + var opt_debug_names: ?[]const u8 = null; + var opt_debug_frame: ?[]const u8 = null; + + for (shdrs) |*shdr| { + if (shdr.sh_type == elf.SHT_NULL) continue; + + const name = common.sliceTo(header_strings[shdr.sh_name..], 0); + if (common.equal(u8, name, ".debug_info")) { + opt_debug_info = try chopSlice(elf_file, shdr.sh_offset, shdr.sh_size); + } else if (common.equal(u8, name, ".debug_abbrev")) { + opt_debug_abbrev = try chopSlice(elf_file, shdr.sh_offset, shdr.sh_size); + } else if (common.equal(u8, name, ".debug_str")) { + opt_debug_str = try chopSlice(elf_file, shdr.sh_offset, shdr.sh_size); + } else if (common.equal(u8, name, ".debug_str_offsets")) { + opt_debug_str_offsets = try chopSlice(elf_file, shdr.sh_offset, shdr.sh_size); + } else if (common.equal(u8, name, ".debug_line")) { + opt_debug_line = try chopSlice(elf_file, shdr.sh_offset, shdr.sh_size); + } else if (common.equal(u8, name, ".debug_line_str")) { + opt_debug_line_str = try chopSlice(elf_file, shdr.sh_offset, shdr.sh_size); + } else if (common.equal(u8, name, ".debug_ranges")) { + opt_debug_ranges = try chopSlice(elf_file, shdr.sh_offset, shdr.sh_size); + } else if (common.equal(u8, name, ".debug_loclists")) { + opt_debug_loclists = try chopSlice(elf_file, shdr.sh_offset, shdr.sh_size); + } else if (common.equal(u8, name, ".debug_rnglists")) { + opt_debug_rnglists = try chopSlice(elf_file, shdr.sh_offset, shdr.sh_size); + } else if (common.equal(u8, name, ".debug_addr")) { + opt_debug_addr = try chopSlice(elf_file, shdr.sh_offset, shdr.sh_size); + } else if (common.equal(u8, name, ".debug_names")) { + opt_debug_names = try chopSlice(elf_file, shdr.sh_offset, shdr.sh_size); + } else if (common.equal(u8, name, ".debug_frame")) { + opt_debug_frame = try chopSlice(elf_file, shdr.sh_offset, shdr.sh_size); + } + } + + var di = common.dwarf.DwarfInfo{ + .endian = endian, + .debug_info = opt_debug_info orelse return error.MissingDebugInfo, + .debug_abbrev = opt_debug_abbrev orelse return error.MissingDebugInfo, + .debug_str = opt_debug_str orelse return error.MissingDebugInfo, + .debug_str_offsets = opt_debug_str_offsets, + .debug_line = opt_debug_line orelse return error.MissingDebugInfo, + .debug_line_str = opt_debug_line_str, + .debug_ranges = opt_debug_ranges, + .debug_loclists = opt_debug_loclists, + .debug_rnglists = opt_debug_rnglists, + .debug_addr = opt_debug_addr, + .debug_names = opt_debug_names, + .debug_frame = opt_debug_frame, + }; + + try common.dwarf.openDwarfDebugInfo(&di, allocator); + return di; +} + +fn chopSlice(ptr: []const u8, offset: u64, size: u64) error{Overflow}![]const u8 { + const start = common.cast(usize, offset) orelse return error.Overflow; + const end = start + (common.cast(usize, size) orelse return error.Overflow); + return ptr[start..end]; +} + +pub fn RegionInterface(comptime Region: type) type { + const type_info = @typeInfo(Region); + common.assert(type_info == .Struct); + common.assert(type_info.Struct.layout == .Extern); + common.assert(type_info.Struct.fields.len == 2); + const fields = type_info.Struct.fields; + common.assert(common.equal(u8, fields[0].name, "address")); + common.assert(common.equal(u8, fields[1].name, "size")); + const Addr = fields[0].type; + const AddrT = getAddrT(Addr); + + return struct { + pub inline fn new(info: struct { + address: Addr, + size: AddrT, + }) Region { + return Region{ + .address = info.address, + .size = info.size, + }; + } + pub inline fn fromRaw(info: struct { + raw_address: AddrT, + size: AddrT, + }) Region { + const address = Addr.new(info.raw_address); + return new(.{ + .address = address, + .size = info.size, + }); + } + + pub inline fn fromAllocation(info: struct { + allocation: Allocator.Allocate.Result, + }) Region { + return new(.{ + .address = addressToAddrT(info.allocation.address), + .size = info.allocation.size, + }); + } + + inline fn addressToAddrT(address: AddrT) Addr { + return if (Region == PhysicalMemoryRegion and address >= config.cpu_driver_higher_half_address) VirtualAddress.new(address).toPhysicalAddress() else Addr.new(address); + } + + pub inline fn fromByteSlice(info: struct { + slice: []const u8, + }) Region { + return new(.{ + .address = addressToAddrT(@intFromPtr(info.slice.ptr)), + .size = info.slice.len, + }); + } + + pub inline fn fromAnytype(any: anytype, info: struct {}) Region { + _ = info; + common.assert(@typeInfo(@TypeOf(any)) == .Pointer); + return Region{ + .address = VirtualAddress.new(@intFromPtr(any)), + .size = @sizeOf(@TypeOf(any.*)), + }; + } + + pub inline fn offset(region: Region, asked_offset: AddrT) Region { + const address = region.address.offset(asked_offset); + const size = region.size - asked_offset; + return Region{ + .address = address, + .size = size, + }; + } + + pub inline fn addOffset(region: *Region, asked_offset: AddrT) void { + region.* = region.offset(asked_offset); + } + + pub inline fn top(region: Region) Addr { + return region.address.offset(region.size); + } + + pub fn shrinked(region: Region, size: AddrT) Region { + common.assert(size <= region.size); + const result = Region{ + .address = region.address, + .size = size, + }; + + return result; + } + + pub inline fn takeSlice(region: *Region, size: AddrT) Region { + common.assert(size <= region.size); + const result = Region{ + .address = region.address, + .size = size, + }; + region.* = region.offset(size); + + return result; + } + + pub inline fn split(region: Region, comptime count: comptime_int) [count]Region { + const region_size = @divExact(region.size, count); + var result: [count]Region = undefined; + var address = region.address; + var region_offset: u64 = 0; + inline for (&result) |*split_region| { + split_region.* = Region{ + .address = address.offset(region_offset), + .size = region_size, + }; + region_offset += region_size; + } + + return result; + } + }; +} + +pub const PhysicalMemoryRegion = extern struct { + address: PhysicalAddress, + size: u64, + + pub usingnamespace RegionInterface(@This()); // This is so cool + + pub inline fn toIdentityMappedVirtualAddress(physical_memory_region: PhysicalMemoryRegion) VirtualMemoryRegion { + return .{ + .address = physical_memory_region.address.toIdentityMappedVirtualAddress(), + .size = physical_memory_region.size, + }; + } + + pub inline fn toHigherHalfVirtualAddress(physical_memory_region: PhysicalMemoryRegion) VirtualMemoryRegion { + return .{ + .address = physical_memory_region.address.toHigherHalfVirtualAddress(), + .size = physical_memory_region.size, + }; + } +}; + +pub const VirtualMemoryRegion = extern struct { + address: VirtualAddress, + size: u64, + + pub usingnamespace RegionInterface(@This()); + + pub inline fn access(virtual_memory_region: VirtualMemoryRegion, comptime T: type) []T { + const slice_len = @divExact(virtual_memory_region.size, @sizeOf(T)); + const result = virtual_memory_region.address.access([*]T)[0..safeArchitectureCast(slice_len)]; + return result; + } + + pub inline fn takeByteSlice(virtual_memory_region: *VirtualMemoryRegion, size: u64) []u8 { + return virtual_memory_region.takeSlice(size).access(u8); + } + + pub inline fn toPhysicalAddress(virtual_memory_region: VirtualMemoryRegion, info: struct {}) PhysicalMemoryRegion { + _ = info; + return PhysicalMemoryRegion{ + .address = virtual_memory_region.address.toPhysicalAddress(), + .size = virtual_memory_region.size, + }; + } +}; + +fn getAddrT(comptime AddressEnum: type) type { + const type_info = @typeInfo(AddressEnum); + common.assert(type_info == .Enum); + const AddrT = type_info.Enum.tag_type; + common.assert(switch (common.cpu.arch) { + .x86 => @sizeOf(AddrT) == 2 * @sizeOf(usize), + else => @sizeOf(AddrT) == @sizeOf(usize), + }); + + return AddrT; +} + +pub fn AddressInterface(comptime AddressEnum: type) type { + const Addr = AddressEnum; + const AddrT = getAddrT(AddressEnum); + + const Result = struct { + pub inline fn newNoChecks(addr: AddrT) Addr { + return @as(Addr, @enumFromInt(addr)); + } + + pub inline fn invalid() Addr { + return newNoChecks(0); + } + + pub inline fn value(addr: Addr) AddrT { + return @intFromEnum(addr); + } + + pub inline fn offset(addr: Addr, asked_offset: AddrT) Addr { + return newNoChecks(addr.value() + asked_offset); + } + + pub inline fn negativeOffset(addr: Addr, asked_offset: AddrT) Addr { + return newNoChecks(addr.value() - asked_offset); + } + + pub inline fn addOffset(addr: *Addr, asked_offset: AddrT) void { + addr.* = addr.offset(asked_offset); + } + + pub inline fn subOffset(addr: *Addr, asked_offset: AddrT) void { + addr.* = addr.negativeOffset(asked_offset); + } + + pub inline fn isAligned(addr: Addr, alignment: u64) bool { + const alignment_mask = alignment - 1; + return addr.value() & alignment_mask == 0; + } + }; + + return Result; +} + +pub const PhysicalAddress = enum(u64) { + null = 0, + _, + const PA = @This(); + + pub usingnamespace AddressInterface(@This()); + + pub inline fn new(address: u64) PA { + if (address >= config.cpu_driver_higher_half_address) @panic("Trying to write a higher half virtual address value into a physical address"); + return @as(PA, @enumFromInt(address)); + } + + pub inline fn toIdentityMappedVirtualAddress(physical_address: PA) VirtualAddress { + return VirtualAddress.new(physical_address.value()); + } + + pub inline fn toHigherHalfVirtualAddress(physical_address: PA) VirtualAddress { + return physical_address.toIdentityMappedVirtualAddress().offset(config.cpu_driver_higher_half_address); + } +}; + +pub const VirtualAddress = enum(u64) { + null = 0, + _, + + pub usingnamespace AddressInterface(@This()); + + pub inline fn new(address: anytype) VirtualAddress { + const T = @TypeOf(address); + return @as(VirtualAddress, @enumFromInt(switch (T) { + usize, u64, comptime_int => address, + else => switch (@typeInfo(T)) { + .Fn => @intFromPtr(&address), + .Pointer => @intFromPtr(address), + else => { + @compileLog(T); + @compileError("HA!"); + }, + }, + })); + } + + pub inline fn access(virtual_address: VirtualAddress, comptime Ptr: type) Ptr { + return @as(Ptr, @ptrFromInt(safeArchitectureCast(virtual_address.value()))); + } + + pub inline fn isValid(virtual_address: VirtualAddress) bool { + _ = virtual_address; + return true; + } + + pub inline fn toPhysicalAddress(virtual_address: VirtualAddress) PhysicalAddress { + common.assert(virtual_address.value() >= config.cpu_driver_higher_half_address); + return @as(PhysicalAddress, @enumFromInt(virtual_address.value() - config.cpu_driver_higher_half_address)); + } + + pub inline fn toGuaranteedPhysicalAddress(virtual_address: VirtualAddress) PhysicalAddress { + common.assert(virtual_address.value() < config.cpu_driver_higher_half_address); + return PhysicalAddress.new(virtual_address.value()); + } +}; + +pub const Color = enum { + black, + red, + green, + yellow, + blue, + magenta, + cyan, + white, + bright_black, + bright_red, + bright_green, + bright_yellow, + bright_blue, + bright_magenta, + bright_cyan, + bright_white, + dim, + bold, + reset, + + pub fn get(color: Color) []const u8 { + return switch (color) { + .black => "\x1b[30m", + .red => "\x1b[31m", + .green => "\x1b[32m", + .yellow => "\x1b[33m", + .blue => "\x1b[34m", + .magenta => "\x1b[35m", + .cyan => "\x1b[36m", + .white => "\x1b[37m", + .bright_black => "\x1b[90m", + .bright_red => "\x1b[91m", + .bright_green => "\x1b[92m", + .bright_yellow => "\x1b[93m", + .bright_blue => "\x1b[94m", + .bright_magenta => "\x1b[95m", + .bright_cyan => "\x1b[96m", + .bright_white => "\x1b[97m", + .bold => "\x1b[1m", + .dim => "\x1b[2m", + .reset => "\x1b[0m", + }; + } +}; diff --git a/src/lib/arch.zig b/src/lib/arch.zig new file mode 100644 index 0000000..7030f7b --- /dev/null +++ b/src/lib/arch.zig @@ -0,0 +1,34 @@ +comptime { + const os = @import("builtin").os.tag; + switch (os) { + .uefi, .freestanding => {}, + else => @compileError("This file is not to be compiled with this OS"), + } +} + +pub const current = switch (@import("builtin").cpu.arch) { + .x86 => x86, + .x86_64 => x86_64, + else => @compileError("Architecture not supported"), +}; + +pub const x86 = @import("arch/x86.zig"); +pub const x86_64 = @import("arch/x86_64.zig"); + +pub const default_page_size = current.default_page_size; +pub const reasonable_page_size = current.reasonable_page_size; + +pub const valid_page_sizes = current.valid_page_sizes; +pub const reverse_valid_page_sizes = current.reverse_valid_page_sizes; + +pub fn page_shifter(comptime asked_page_size: comptime_int) comptime_int { + return @ctz(@as(u32, asked_page_size)); +} + +pub fn page_mask(comptime asked_page_size: comptime_int) comptime_int { + return asked_page_size - 1; +} + +pub const Spinlock = current.Spinlock; + +pub const stack_alignment = current.stack_alignment; diff --git a/src/lib/arch/x86.zig b/src/lib/arch/x86.zig new file mode 100644 index 0000000..eed3e71 --- /dev/null +++ b/src/lib/arch/x86.zig @@ -0,0 +1,6 @@ +const x86 = @import("x86/common.zig"); +pub usingnamespace x86; + +pub const default_page_size = valid_page_sizes[0]; +pub const reasonable_page_size = valid_page_sizes[1]; +pub const valid_page_sizes = [2]comptime_int{ 0x1000, 0x1000 * 0x200 }; diff --git a/src/lib/arch/x86/64/registers.zig b/src/lib/arch/x86/64/registers.zig new file mode 100644 index 0000000..e05d7a6 --- /dev/null +++ b/src/lib/arch/x86/64/registers.zig @@ -0,0 +1,343 @@ +const lib = @import("lib"); +const assert = lib.assert; + +pub const RFLAGS = packed struct(u64) { + CF: bool = false, + reserved0: bool = true, + PF: bool = false, + reserved1: bool = false, + AF: bool = false, + reserved2: bool = false, + ZF: bool = false, + SF: bool = false, + TF: bool = false, + IF: bool = false, + DF: bool = false, + OF: bool = false, + IOPL: u2 = 0, + NT: bool = false, + reserved3: bool = false, + RF: bool = false, + VM: bool = false, + AC: bool = false, + VIF: bool = false, + VIP: bool = false, + ID: bool = false, + reserved4: u10 = 0, + reserved5: u32 = 0, + + comptime { + assert(@sizeOf(RFLAGS) == @sizeOf(u64)); + assert(@bitSizeOf(RFLAGS) == @bitSizeOf(u64)); + } + + pub inline fn read() RFLAGS { + return asm volatile ( + \\pushfq + \\pop %[flags] + : [flags] "=r" (-> RFLAGS), + : + : "memory" + ); + } + + pub fn user(rflags: RFLAGS) RFLAGS { + return RFLAGS{ + .IF = true, + .CF = rflags.CF, + .PF = rflags.PF, + .AF = rflags.AF, + .ZF = rflags.ZF, + .SF = rflags.SF, + .DF = rflags.DF, + .OF = rflags.OF, + }; + } +}; + +pub const SimpleRegister = enum { + rax, + rbx, + rcx, + rdx, + rbp, + rsp, + rsi, + rdi, + r8, + r9, + r10, + r11, + r12, + r13, + r14, + r15, + + gs, + cs, + + dr0, + dr1, + dr2, + dr3, + dr4, + dr5, + dr6, + dr7, + + cr2, + cr8, +}; + +pub fn SimpleR64(comptime Register: SimpleRegister) type { + return struct { + pub inline fn read() u64 { + return switch (Register) { + .rax => asm volatile ("mov %rax, %[result]" + : [result] "=r" (-> u64), + ), + .rbx => asm volatile ("mov %rbx, %[result]" + : [result] "=r" (-> u64), + ), + .rcx => asm volatile ("mov %rcx, %[result]" + : [result] "=r" (-> u64), + ), + .rdx => asm volatile ("mov %rdx, %[result]" + : [result] "=r" (-> u64), + ), + .rbp => asm volatile ("mov %rbp, %[result]" + : [result] "=r" (-> u64), + ), + .rsp => asm volatile ("mov %rsp, %[result]" + : [result] "=r" (-> u64), + ), + .rsi => asm volatile ("mov %rsi, %[result]" + : [result] "=r" (-> u64), + ), + .rdi => asm volatile ("mov %rdi, %[result]" + : [result] "=r" (-> u64), + ), + .r8 => asm volatile ("mov %r8, %[result]" + : [result] "=r" (-> u64), + ), + .r9 => asm volatile ("mov %r9, %[result]" + : [result] "=r" (-> u64), + ), + .r10 => asm volatile ("mov %r10, %[result]" + : [result] "=r" (-> u64), + ), + .r11 => asm volatile ("mov %r11, %[result]" + : [result] "=r" (-> u64), + ), + .r12 => asm volatile ("mov %r12, %[result]" + : [result] "=r" (-> u64), + ), + .r13 => asm volatile ("mov %r13, %[result]" + : [result] "=r" (-> u64), + ), + .r14 => asm volatile ("mov %r14, %[result]" + : [result] "=r" (-> u64), + ), + .r15 => asm volatile ("mov %r15, %[result]" + : [result] "=r" (-> u64), + ), + .gs => asm volatile ("mov %gs, %[result]" + : [result] "=r" (-> u64), + : + : "memory" + ), + .cs => asm volatile ("mov %cs, %[result]" + : [result] "=r" (-> u64), + : + : "memory" + ), + .dr0 => asm volatile ("mov %dr0, %[result]" + : [result] "=r" (-> u64), + ), + .dr1 => asm volatile ("mov %dr1, %[result]" + : [result] "=r" (-> u64), + ), + .dr2 => asm volatile ("mov %dr2, %[result]" + : [result] "=r" (-> u64), + ), + .dr3 => asm volatile ("mov %dr3, %[result]" + : [result] "=r" (-> u64), + ), + .dr4 => asm volatile ("mov %dr4, %[result]" + : [result] "=r" (-> u64), + ), + .dr5 => asm volatile ("mov %dr5, %[result]" + : [result] "=r" (-> u64), + ), + .dr6 => asm volatile ("mov %dr6, %[result]" + : [result] "=r" (-> u64), + ), + .dr7 => asm volatile ("mov %dr7, %[result]" + : [result] "=r" (-> u64), + ), + .cr2 => asm volatile ("mov %cr2, %[result]" + : [result] "=r" (-> u64), + : + : "memory" + ), + .cr8 => asm volatile ("mov %cr8, %[result]" + : [result] "=r" (-> u64), + : + : "memory" + ), + }; + } + + pub inline fn write(value: u64) void { + switch (Register) { + .rax => asm volatile ("mov %[in], %rax" + : + : [in] "r" (value), + ), + .rbx => asm volatile ("mov %[in], %rbx" + : + : [in] "r" (value), + ), + .rcx => asm volatile ("mov %[in], %rcx" + : + : [in] "r" (value), + ), + .rdx => asm volatile ("mov %[in], %rdx" + : + : [in] "r" (value), + ), + .rbp => asm volatile ("mov %[in], %rbp" + : + : [in] "r" (value), + ), + .rsp => asm volatile ("mov %[in], %rsp" + : + : [in] "r" (value), + ), + .rsi => asm volatile ("mov %[in], %rsi" + : + : [in] "r" (value), + ), + .rdi => asm volatile ("mov %[in], %rdi" + : + : [in] "r" (value), + ), + .r8 => asm volatile ("mov %[in], %r8" + : + : [in] "r" (value), + ), + .r9 => asm volatile ("mov %[in], %r9" + : + : [in] "r" (value), + ), + .r10 => asm volatile ("mov %[in], %r10" + : + : [in] "r" (value), + ), + .r11 => asm volatile ("mov %[in], %r11" + : + : [in] "r" (value), + ), + .r12 => asm volatile ("mov %[in], %r12" + : + : [in] "r" (value), + ), + .r13 => asm volatile ("mov %[in], %r13" + : + : [in] "r" (value), + ), + .r14 => asm volatile ("mov %[in], %r14" + : + : [in] "r" (value), + ), + .r15 => asm volatile ("mov %[in], %r15" + : + : [in] "r" (value), + ), + .gs => asm volatile ("mov %[in], %gs" + : + : [in] "r" (value), + : "memory" + ), + .cs => asm volatile ("mov %[in], %cs" + : + : [in] "r" (value), + : "memory" + ), + .dr0 => asm volatile ("mov %[in], %dr0" + : + : [in] "r" (value), + ), + .dr1 => asm volatile ("mov %[in], %dr1" + : + : [in] "r" (value), + ), + .dr2 => asm volatile ("mov %[in], %dr2" + : + : [in] "r" (value), + ), + .dr3 => asm volatile ("mov %[in], %dr3" + : + : [in] "r" (value), + ), + .dr4 => asm volatile ("mov %[in], %dr4" + : + : [in] "r" (value), + ), + .dr5 => asm volatile ("mov %[in], %dr5" + : + : [in] "r" (value), + ), + .dr6 => asm volatile ("mov %[in], %dr6" + : + : [in] "r" (value), + ), + .dr7 => asm volatile ("mov %[in], %dr7" + : + : [in] "r" (value), + ), + .cr2 => asm volatile ("mov %[in], %cr2" + : + : [in] "r" (value), + : "memory" + ), + .cr8 => asm volatile ("mov %[in], %cr8" + : + : [in] "r" (value), + : "memory" + ), + } + } + }; +} + +pub const ComplexRegister = enum { cr0, cr3, cr4 }; + +pub const rax = SimpleR64(.rax); +pub const rbx = SimpleR64(.rbx); +pub const rcx = SimpleR64(.rcx); +pub const rdx = SimpleR64(.rdx); +pub const rbp = SimpleR64(.rbp); +pub const rsp = SimpleR64(.rsp); +pub const rsi = SimpleR64(.rsi); +pub const rdi = SimpleR64(.rdi); +pub const r8 = SimpleR64(.r8); +pub const r9 = SimpleR64(.r9); +pub const r10 = SimpleR64(.r10); +pub const r11 = SimpleR64(.r11); +pub const r12 = SimpleR64(.r12); +pub const r13 = SimpleR64(.r13); +pub const r14 = SimpleR64(.r14); +pub const r15 = SimpleR64(.r15); + +pub const gs = SimpleR64(.gs); +pub const cs = SimpleR64(.cs); + +pub const dr0 = SimpleR64(.dr0); +pub const dr1 = SimpleR64(.dr1); +pub const dr2 = SimpleR64(.dr2); +pub const dr3 = SimpleR64(.dr3); +pub const dr4 = SimpleR64(.dr4); +pub const dr5 = SimpleR64(.dr5); +pub const dr6 = SimpleR64(.dr6); +pub const dr7 = SimpleR64(.dr7); diff --git a/src/lib/arch/x86/common.zig b/src/lib/arch/x86/common.zig new file mode 100644 index 0000000..e4b9e61 --- /dev/null +++ b/src/lib/arch/x86/common.zig @@ -0,0 +1,59 @@ +pub const CPUID = extern struct { + eax: u32, + ebx: u32, + edx: u32, + ecx: u32, +}; + +pub inline fn cpuid(leaf: u32) CPUID { + var eax: u32 = undefined; + var ebx: u32 = undefined; + var edx: u32 = undefined; + var ecx: u32 = undefined; + + asm volatile ( + \\cpuid + : [eax] "={eax}" (eax), + [ebx] "={ebx}" (ebx), + [edx] "={edx}" (edx), + [ecx] "={ecx}" (ecx), + : [leaf] "{eax}" (leaf), + ); + + return CPUID{ + .eax = eax, + .ebx = ebx, + .edx = edx, + .ecx = ecx, + }; +} + +pub const Spinlock = enum(u8) { + released = 0, + acquired = 1, + + pub inline fn acquire(spinlock: *volatile Spinlock) void { + asm volatile ( + \\0: + \\xchgb %[value], %[spinlock] + \\test %[value], %[value] + \\jz 2f + // If not acquire, go to spinloop + \\1: + \\pause + \\cmp %[value], %[spinlock] + // Retry + \\jne 0b + \\jmp 1b + \\2: + : + : [spinlock] "*p" (spinlock), + [value] "r" (Spinlock.acquired), + : "memory" + ); + } + + pub inline fn release(spinlock: *volatile Spinlock) void { + @atomicStore(Spinlock, spinlock, .released, .Release); + } +}; diff --git a/src/lib/arch/x86_64.zig b/src/lib/arch/x86_64.zig new file mode 100644 index 0000000..ebd1b05 --- /dev/null +++ b/src/lib/arch/x86_64.zig @@ -0,0 +1,34 @@ +const lib = @import("lib"); +const x86 = @import("x86/common.zig"); +pub usingnamespace x86; + +pub const valid_page_sizes = [3]comptime_int{ 0x1000, 0x1000 * 0x200, 0x1000 * 0x200 * 0x200 }; +pub const reverse_valid_page_sizes = blk: { + var reverse = valid_page_sizes; + lib.reverse(@TypeOf(valid_page_sizes[0]), &reverse); + // var reverse_u64: [valid_page_sizes.len]u64 = undefined; + // for (reverse, &reverse_u64) |r_el, *ru64_el| { + // ru64_el.* = r_el; + // } + + break :blk reverse; +}; +pub const default_page_size = valid_page_sizes[0]; +pub const reasonable_page_size = valid_page_sizes[1]; + +pub const registers = @import("x86/64/registers.zig"); + +pub inline fn readTimestamp() u64 { + var edx: u32 = undefined; + var eax: u32 = undefined; + + asm volatile ( + \\rdtsc + : [eax] "={eax}" (eax), + [edx] "={edx}" (edx), + ); + + return @as(u64, edx) << 32 | eax; +} + +pub const stack_alignment = 0x10; diff --git a/src/lib/config.zig b/src/lib/config.zig new file mode 100644 index 0000000..e5f6180 --- /dev/null +++ b/src/lib/config.zig @@ -0,0 +1,6 @@ +pub const cpu_driver_higher_half_address = 0xffff_8000_0000_0000; +pub const cpu_driver_start = 0xffff_ffff_8000_0000; +pub const real_hardware = false; +pub const safe_slow = false; +pub const timeslicing = true; +pub const enable_smp = false; diff --git a/src/lib/crc32.zig b/src/lib/crc32.zig new file mode 100644 index 0000000..2049cc4 --- /dev/null +++ b/src/lib/crc32.zig @@ -0,0 +1,13 @@ +const lib = @import("lib"); + +pub fn compute(bytes: []const u8) u32 { + var result: u32 = lib.maxInt(u32); + for (bytes) |byte| { + result = (result >> 8) ^ table[@as(u8, @truncate(result ^ byte))]; + } + + result ^= lib.maxInt(u32); + return result; +} + +const table = [_]u32{ 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d }; diff --git a/src/lib/disk.zig b/src/lib/disk.zig new file mode 100644 index 0000000..9904191 --- /dev/null +++ b/src/lib/disk.zig @@ -0,0 +1,123 @@ +const lib = @import("lib"); + +const FAT32 = lib.Filesystem.FAT32; +const GPT = lib.PartitionTable.GPT; +const MBR = lib.PartitionTable.MBR; + +const ArrayListAligned = lib.ArrayListAligned; +const assert = lib.assert; +const asBytes = lib.asBytes; +const log = lib.log.scoped(.Disk); +const sliceAsBytes = lib.sliceAsBytes; + +pub const Disk = extern struct { + type: Type, + disk_size: u64, + partition_sizes: [GPT.default_max_partition_count]u64 = [1]u64{0} ** GPT.default_max_partition_count, + cache_size: u16, + sector_size: u16, + callbacks: Callbacks, + + pub const Type = lib.DiskType; + + pub const ReadFn = fn (disk: *Disk, sector_count: u64, sector_offset: u64, provided_buffer: ?[]u8) ReadError!ReadResult; + pub const ReadError = error{ + read_error, + }; + pub const ReadResult = extern struct { + sector_count: u64, + buffer: [*]u8, + }; + + pub const ReadCacheFn = fn (disk: *Disk, sector_count: u64, sector_offset: u64) ReadError!ReadResult; + + pub const WriteFn = fn (disk: *Disk, bytes: []const u8, sector_offset: u64, commit_memory_to_disk: bool) WriteError!void; + pub const WriteError = error{ + not_supported, + disk_size_overflow, + }; + + pub const Callbacks = extern struct { + read: *const ReadFn, + write: *const WriteFn, + readCache: *const ReadCacheFn, + }; + + pub inline fn getProvidedBuffer(disk: *Disk, comptime T: type, count: usize, allocator: ?*lib.Allocator, force: bool) !?[]u8 { + if ((disk.type == .memory and force) or (disk.type != .memory)) { + if (allocator) |alloc| { + const size = @sizeOf(T) * count; + const alignment = @alignOf(T); + const result = try alloc.allocateBytes(size, alignment); + const slice = @as([*]u8, @ptrFromInt(@as(usize, @intCast(result.address))))[0..@as(usize, @intCast(result.size))]; + if (slice.len != size) @panic("WTQSAD/jasD"); + return slice; + } + } + + return null; + } + + const AdvancedReadOptions = packed struct(u8) { + force: bool = false, + reserved: u7 = 0, + }; + + pub fn readTypedSectors(disk: *Disk, comptime T: type, sector_offset: u64, allocator: ?*lib.Allocator, options: AdvancedReadOptions) !*T { + const sector_count = @divExact(@sizeOf(T), disk.sector_size); + const provided_buffer = try disk.getProvidedBuffer(T, 1, allocator, options.force); + const read_result = try disk.callbacks.read(disk, sector_count, sector_offset, provided_buffer); + if (read_result.sector_count != sector_count) @panic("Sector count mismatch"); + // Don't need to write back since it's a memory disk + const result: *T = @ptrCast(@alignCast(read_result.buffer)); + return result; + } + + pub inline fn writeTypedSectors(disk: *Disk, comptime T: type, content: *T, sector_offset: u64, commit_memory_to_disk: bool) !void { + try disk.callbacks.write(disk, asBytes(content), sector_offset, commit_memory_to_disk); + } + + pub inline fn readSlice(disk: *Disk, comptime T: type, len: usize, sector_offset: u64, allocator: ?*lib.Allocator, options: AdvancedReadOptions) ![]T { + const element_count_per_sector = @divExact(disk.sector_size, @sizeOf(T)); + const sector_count = @divExact(len, element_count_per_sector); + const provided_buffer = try disk.getProvidedBuffer(T, len, allocator, options.force); + const read_result = try disk.callbacks.read(disk, sector_count, sector_offset, provided_buffer); + if (read_result.sector_count != sector_count) @panic("read_slice: sector count mismatch"); + const result = @as([*]T, @ptrCast(@alignCast(read_result.buffer)))[0..len]; + return result; + } + + pub inline fn writeSlice(disk: *Disk, comptime T: type, slice: []const T, sector_offset: u64, commit_memory_to_disk: bool) !void { + const byte_slice = sliceAsBytes(slice); + try disk.callbacks.write(disk, byte_slice, sector_offset, commit_memory_to_disk); + } + + pub fn verify(disk: *Disk) !void { + const mbr = try disk.read_typed_sectors(MBR.Struct, 0); + try mbr.verify(disk); + unreachable; + } + + pub const Work = struct { + sector_offset: u64, + sector_count: u64, + operation: Operation, + }; + + pub const Operation = enum(u1) { + read = 0, + write = 1, + + // This is used by NVMe and AHCI, so it is needed to match these values + comptime { + assert(@bitSizeOf(Operation) == @bitSizeOf(u1)); + assert(@intFromEnum(Operation.read) == 0); + assert(@intFromEnum(Operation.write) == 1); + } + }; + + pub const PartitionRange = extern struct { + first_lba: u64, + last_lba: u64, + }; +}; diff --git a/src/lib/extern_enum_array.zig b/src/lib/extern_enum_array.zig new file mode 100644 index 0000000..06d53de --- /dev/null +++ b/src/lib/extern_enum_array.zig @@ -0,0 +1,231 @@ +const lib = @import("lib"); +// ENUM ARRAY +pub fn IndexedArray(comptime I: type, comptime V: type, comptime Ext: fn (type) type) type { + comptime ensureIndexer(I); + return extern struct { + const Self = @This(); + + pub usingnamespace Ext(Self); + + /// The index mapping for this map + pub const Indexer = I; + /// The key type used to index this map + pub const Key = Indexer.Key; + /// The value type stored in this map + pub const Value = V; + /// The number of possible keys in the map + pub const len = Indexer.count; + + values: [Indexer.count]Value, + + pub fn initUndefined() Self { + return Self{ .values = undefined }; + } + + pub fn initFill(v: Value) Self { + var self: Self = undefined; + lib.set(Value, &self.values, v); + return self; + } + + /// Returns the value in the array associated with a key. + pub fn get(self: Self, key: Key) Value { + return self.values[Indexer.indexOf(key)]; + } + + /// Returns a pointer to the slot in the array associated with a key. + pub fn getPtr(self: *Self, key: Key) *Value { + return &self.values[Indexer.indexOf(key)]; + } + + /// Returns a const pointer to the slot in the array associated with a key. + pub fn getPtrConst(self: *const Self, key: Key) *const Value { + return &self.values[Indexer.indexOf(key)]; + } + + /// Sets the value in the slot associated with a key. + pub fn set(self: *Self, key: Key, value: Value) void { + self.values[Indexer.indexOf(key)] = value; + } + + /// Iterates over the items in the array, in index order. + pub fn iterator(self: *Self) Iterator { + return .{ + .values = &self.values, + }; + } + + /// An entry in the array. + pub const Entry = extern struct { + /// The key associated with this entry. + /// Modifying this key will not change the array. + key: Key, + + /// A pointer to the value in the array associated + /// with this key. Modifications through this + /// pointer will modify the underlying data. + value: *Value, + }; + + pub const Iterator = extern struct { + index: usize = 0, + values: *[Indexer.count]Value, + + pub fn next(self: *Iterator) ?Entry { + const index = self.index; + if (index < Indexer.count) { + self.index += 1; + return Entry{ + .key = Indexer.keyForIndex(index), + .value = &self.values[index], + }; + } + return null; + } + }; + }; +} + +pub fn ensureIndexer(comptime T: type) void { + comptime { + if (!@hasDecl(T, "Key")) @compileError("Indexer must have decl Key: type."); + if (@TypeOf(T.Key) != type) @compileError("Indexer.Key must be a type."); + if (!@hasDecl(T, "count")) @compileError("Indexer must have decl count: usize."); + if (@TypeOf(T.count) != usize) @compileError("Indexer.count must be a usize."); + if (!@hasDecl(T, "indexOf")) @compileError("Indexer.indexOf must be a fn(Key)usize."); + if (@TypeOf(T.indexOf) != fn (T.Key) usize) @compileError("Indexer must have decl indexOf: fn(Key)usize."); + if (!@hasDecl(T, "keyForIndex")) @compileError("Indexer must have decl keyForIndex: fn(usize)Key."); + if (@TypeOf(T.keyForIndex) != fn (usize) T.Key) @compileError("Indexer.keyForIndex must be a fn(usize)Key."); + } +} + +pub fn EnumArray(comptime E: type, comptime V: type) type { + const mixin = extern struct { + fn EnumArrayExt(comptime Self: type) type { + const Indexer = Self.Indexer; + return extern struct { + /// Initializes all values in the enum array + pub fn init(init_values: EnumFieldStruct(E, V, @as(?V, null))) Self { + return initDefault(@as(?V, null), init_values); + } + + /// Initializes values in the enum array, with the specified default. + pub fn initDefault(comptime default: ?V, init_values: EnumFieldStruct(E, V, default)) Self { + var result = Self{ .values = undefined }; + comptime var i: usize = 0; + inline while (i < Self.len) : (i += 1) { + const key = comptime Indexer.keyForIndex(i); + const tag = @tagName(key); + result.values[i] = @field(init_values, tag); + } + return result; + } + }; + } + }; + return IndexedArray(EnumIndexer(E), V, mixin.EnumArrayExt); +} +const EnumField = lib.Type.EnumField; +pub fn EnumIndexer(comptime E: type) type { + if (!@typeInfo(E).Enum.is_exhaustive) { + @compileError("Cannot create an enum indexer for a non-exhaustive enum."); + } + + const const_fields = lib.fields(E); + comptime var fields = const_fields[0..const_fields.len].*; + const fields_len = fields.len; + if (fields_len == 0) { + return extern struct { + pub const Key = E; + pub const count: usize = 0; + pub fn indexOf(e: E) usize { + _ = e; + unreachable; + } + pub fn keyForIndex(i: usize) E { + _ = i; + unreachable; + } + }; + } + const SortContext = struct { + fields: []EnumField, + + pub fn lessThan(comptime ctx: @This(), comptime a: usize, comptime b: usize) bool { + return ctx.fields[a].value < ctx.fields[b].value; + } + + pub fn swap(comptime ctx: @This(), comptime a: usize, comptime b: usize) void { + return lib.swap(EnumField, &ctx.fields[a], &ctx.fields[b]); + } + }; + lib.sort.insertionContext(0, fields_len, SortContext{ .fields = &fields }); + + const min = fields[0].value; + const max = fields[fields.len - 1].value; + if (max - min == fields.len - 1) { + return extern struct { + pub const Key = E; + pub const count = fields_len; + pub fn indexOf(e: E) usize { + return @as(usize, @intCast(@intFromEnum(e) - min)); + } + pub fn keyForIndex(i: usize) E { + // TODO fix addition semantics. This calculation + // gives up some safety to avoid artificially limiting + // the range of signed enum values to max_isize. + const enum_value = if (min < 0) @as(isize, @bitCast(i)) +% min else i + min; + return @as(E, @enumFromInt(@as(lib.Tag(E), @intCast(enum_value)))); + } + }; + } + + const keys = valuesFromFields(E, &fields); + + return extern struct { + pub const Key = E; + pub const count = fields_len; + pub fn indexOf(e: E) usize { + for (keys, 0..) |k, i| { + if (k == e) return i; + } + unreachable; + } + pub fn keyForIndex(i: usize) E { + return keys[i]; + } + }; +} +pub fn EnumFieldStruct(comptime E: type, comptime Data: type, comptime field_default: ?Data) type { + const StructField = lib.builtin.Type.StructField; + var fields: []const StructField = &[_]StructField{}; + for (lib.fields(E)) |field| { + fields = fields ++ &[_]StructField{.{ + .name = field.name, + .type = Data, + .default_value = if (field_default) |d| @as(?*const anyopaque, @ptrCast(&d)) else null, + .is_comptime = false, + .alignment = if (@sizeOf(Data) > 0) @alignOf(Data) else 0, + }}; + } + return @Type(.{ .Struct = .{ + .layout = .Extern, + .fields = fields, + .decls = &.{}, + .is_tuple = false, + } }); +} + +fn ascByValue(ctx: void, comptime a: EnumField, comptime b: EnumField) bool { + _ = ctx; + return a.value < b.value; +} +pub fn valuesFromFields(comptime E: type, comptime fields: []const EnumField) []const E { + comptime { + var result: [fields.len]E = undefined; + for (fields, 0..) |f, i| { + result[i] = @field(E, f.name); + } + return &result; + } +} diff --git a/src/lib/filesystem.zig b/src/lib/filesystem.zig new file mode 100644 index 0000000..f56bebd --- /dev/null +++ b/src/lib/filesystem.zig @@ -0,0 +1,18 @@ +const lib = @import("lib"); +pub const FAT32 = @import("filesystem/fat32.zig"); + +pub const Type = lib.FilesystemType; + +pub const ReadError = error{ + unsupported, + failed, +}; + +pub const WriteError = error{ + unsupported, + failed, +}; + +test { + _ = FAT32; +} diff --git a/src/lib/filesystem/fat32.zig b/src/lib/filesystem/fat32.zig new file mode 100644 index 0000000..ab426a2 --- /dev/null +++ b/src/lib/filesystem/fat32.zig @@ -0,0 +1,1297 @@ +const FAT32 = @This(); + +const lib = @import("lib"); +const log = lib.log; +const kb = lib.kb; +const mb = lib.mb; +const gb = lib.gb; +const assert = lib.assert; + +const Disk = lib.Disk; +const GPT = lib.PartitionTable.GPT; +const MBR = lib.PartitionTable.MBR; +const NLS = lib.NLS; + +pub const count = 2; +pub const volumes_lba = GPT.reserved_partition_size / GPT.max_block_size / 2; +pub const minimum_partition_size = 33 * mb; +pub const maximum_partition_size = 32 * gb; +pub const last_cluster = 0xffff_ffff; +pub const starting_cluster = 2; +pub const default_fs_info_sector = 1; +pub const default_backup_boot_record_sector = 6; +pub const default_reserved_sector_count = 32; + +const NameCase = packed struct(u8) { + reserved: u3 = 0, + base: Case = .upper, + extension: Case = .upper, + reserved1: u3 = 0, +}; +const Case = enum(u1) { + upper = 0, + lower = 1, +}; + +pub const FSInfo = extern struct { + lead_signature: u32 = 0x41617272, + reserved: [480]u8 = [1]u8{0} ** 480, + signature: u32 = 0x61417272, + free_cluster_count: u32, + last_allocated_cluster: u32, + reserved1: [12]u8 = [1]u8{0} ** 12, + trail_signature: u32 = 0xaa550000, + + pub fn format(fsinfo: *const FSInfo, comptime _: []const u8, _: lib.FormatOptions, writer: anytype) @TypeOf(writer).Error!void { + try lib.format(writer, "FSInfo:\n", .{}); + try lib.format(writer, "\tLead signature: 0x{x}\n", .{fsinfo.lead_signature}); + try lib.format(writer, "\tOther signature: 0x{x}\n", .{fsinfo.signature}); + try lib.format(writer, "\tFree cluster count: {}\n", .{fsinfo.free_cluster_count}); + try lib.format(writer, "\tLast allocated cluster: {}\n", .{fsinfo.last_allocated_cluster}); + try lib.format(writer, "\tTrail signature: 0x{x}\n", .{fsinfo.trail_signature}); + } +}; + +pub fn isFilesystem(file: []const u8) bool { + const magic = "FAT32 "; + return lib.equal(u8, file[0x52..], magic); +} + +pub fn isBootRecord(file: []const u8) bool { + const magic = [_]u8{ 0x55, 0xAA }; + const magic_alternative = [_]u8{ 'M', 'S', 'W', 'I', 'N', '4', '.', '1' }; + if (!lib.equal(u8, file[0x1fe..], magic)) return false; + if (!lib.equal(u8, file[0x3fe..], magic)) return false; + if (!lib.equal(u8, file[0x5fe..], magic)) return false; + if (!lib.equal(u8, file[0x03..], magic_alternative)) return false; + return true; +} + +pub fn getClusterSize(size: u64) u16 { + if (size <= 64 * mb) return lib.default_sector_size; + if (size <= 128 * mb) return 1 * kb; + if (size <= 256 * mb) return 2 * kb; + if (size <= 8 * gb) return 8 * kb; + if (size <= 16 * gb) return 16 * kb; + + return 32 * kb; +} + +pub const Date = packed struct(u16) { + day: u5, + month: u4, + year: u7, + + pub fn new(day: u5, month: u4, year: u12) Date { + return Date{ + .day = day, + .month = month, + .year = @as(u7, @intCast(year - 1980)), + }; + } +}; + +pub const Time = packed struct(u16) { + seconds_2_factor: u5, + minutes: u6, + hours: u5, + + pub fn new(seconds: u6, minutes: u6, hours: u5) Time { + return Time{ + .seconds_2_factor = @as(u5, @intCast(seconds / 2)), + .minutes = minutes, + .hours = hours, + }; + } +}; + +const max_base_len = 8; +const max_extension_len = 3; +const short_name_len = max_base_len + max_extension_len; +const long_name_max_characters = 255; + +pub const DirectoryEntry = extern struct { + name: [short_name_len]u8, + attributes: Attributes, + case: NameCase, + creation_time_tenth: u8, + creation_time: Time, + creation_date: Date, + last_access_date: Date, + first_cluster_high: u16, + last_write_time: Time, + last_write_date: Date, + first_cluster_low: u16, + file_size: u32, + + pub const Sector = [per_sector]@This(); + pub const per_sector = @divExact(lib.default_sector_size, @sizeOf(@This())); + + pub const Chain = extern struct { + previous: ?*DirectoryEntry = null, + next: ?*DirectoryEntry = null, + current: *DirectoryEntry, + }; + + pub fn format(entry: *const DirectoryEntry, comptime _: []const u8, _: lib.FormatOptions, writer: anytype) @TypeOf(writer).Error!void { + try lib.format(writer, "Directory entry:\n", .{}); + try lib.format(writer, "\tName: {s}\n", .{entry.name}); + try lib.format(writer, "\tAttributes: {}\n", .{entry.attributes}); + try lib.format(writer, "\tCreation time tenth: {}\n", .{entry.creation_time_tenth}); + try lib.format(writer, "\tCreation time: {}\n", .{entry.creation_time}); + try lib.format(writer, "\tCreation date: {}\n", .{entry.creation_date}); + try lib.format(writer, "\tLast access date: {}\n", .{entry.last_access_date}); + try lib.format(writer, "\tLast write time: {}\n", .{entry.last_write_time}); + try lib.format(writer, "\tLast write date: {}\n", .{entry.last_write_date}); + const first_cluster = @as(u32, entry.first_cluster_high) << 16 | entry.first_cluster_low; + try lib.format(writer, "\tFirst cluster: 0x{x}\n", .{first_cluster}); + try lib.format(writer, "\tFile size: 0x{x}\n", .{entry.file_size}); + } + + pub fn isFree(entry: DirectoryEntry) bool { + const first_char = entry.name[0]; + assert(first_char != 0x20); + return switch (first_char) { + 0, 0xe5, ' ' => true, + else => false, + }; + } + + pub fn setFirstCluster(entry: *DirectoryEntry, cluster: u32) void { + entry.first_cluster_low = @as(u16, @truncate(cluster)); + entry.first_cluster_high = @as(u16, @truncate(cluster >> 16)); + } + + pub fn getFirstCluster(entry: *DirectoryEntry) u32 { + return @as(u32, entry.first_cluster_high) << 16 | entry.first_cluster_low; + } + + comptime { + assert(@sizeOf(@This()) == 32); + } +}; + +pub const Attributes = packed struct(u8) { + read_only: bool = false, + hidden: bool = false, + system: bool = false, + volume_id: bool = false, + directory: bool = false, + archive: bool = false, + reserved: u2 = 0, + + pub fn hasLongName(attributes: Attributes) bool { + return attributes.read_only and attributes.hidden and attributes.system and attributes.volume_id; + } +}; + +pub const LongNameEntry = extern struct { + sequence_number: packed struct(u8) { + number: u5, + first_physical_entry: u1 = 0, + last_logical: bool, + reserved: u1 = 0, + }, + chars_0_4: [5]u16 align(1), + attributes: Attributes, + reserved: u8 = 0, + checksum: u8, + chars_5_10: [6]u16 align(1), + first_cluster: u16 align(1), + chars_11_12: [2]u16 align(1), + + pub const Sector = [per_sector]@This(); + pub const per_sector = @divExact(lib.default_sector_size, @sizeOf(@This())); + + pub fn isLast(entry: LongNameEntry) bool { + return entry.sequence_number.last_logical; + } + + fn getCharacters(entry: LongNameEntry) [13]u16 { + return entry.chars_0_4 ++ entry.chars_5_10 ++ entry.chars_11_12; + } + + fn isFree(entry: LongNameEntry) bool { + const first_char = entry.chars_0_4[0]; + assert(first_char != 0x20); + return switch (first_char) { + 0, 0xe5, ' ' => true, + else => false, + }; + } +}; + +pub const Entry = packed struct(u32) { + next_cluster: u28, + reserved: u4 = 0, + + pub const Sector = [per_sector]FAT32.Entry; + const per_sector = @divExact(lib.default_sector_size, @sizeOf(FAT32.Entry)); + + pub fn isFree(entry: Entry) bool { + return entry.next_cluster == value_free; + } + + pub fn isAllocating(entry: Entry) bool { + return entry.next_cluster == value_allocated_and_eof or (entry.next_cluster >= value_allocated_start and entry.next_cluster < value_reserved_and_should_not_be_used_end); + } + + pub fn getType(entry: Entry, max_valid_cluster_number: u32) Type { + return switch (entry.value) { + value_free => .free, + value_bad_cluster => .bad_cluster, + value_reserved_and_should_not_be_used_eof_start...value_reserved_and_should_not_be_used_eof_end => .reserved_and_should_not_be_used_eof, + value_allocated_and_eof => .allocated_and_eof, + else => if (entry.value >= value_allocated_start and entry.value <= @as(u28, @intCast(max_valid_cluster_number))) .allocated else if (entry.value >= @as(u28, @intCast(max_valid_cluster_number)) + 1 and entry.value <= value_reserved_and_should_not_be_used_end) .reserved_and_should_not_be_used else @panic("fat32: getType unexpected error"), + }; + } + + fn getEntry(t: Type) Entry { + return Entry{ + .next_cluster = switch (t) { + .free => value_free, + .allocated => value_allocated_start, + .reserved_and_should_not_be_used => value_reserved_and_should_not_be_used_end, + .bad_cluster => value_bad_cluster, + .reserved_and_should_not_be_used_eof => value_reserved_and_should_not_be_used_eof_start, + .allocated_and_eof => value_allocated_and_eof, + }, + }; + } + + pub const free = getEntry(.free); + pub const allocated = getEntry(.allocated); + pub const reserved_and_should_not_be_used = getEntry(.reserved_and_should_not_be_used); + pub const bad_cluster = getEntry(.bad_cluster); + pub const reserved_and_should_not_be_used_eof = getEntry(.reserved_and_should_not_be_used_eof); + pub const allocated_and_eof = getEntry(.allocated_and_eof); + + const value_free = 0; + const value_allocated_start = 2; + const value_reserved_and_should_not_be_used_end = 0xfff_fff6; + const value_bad_cluster = 0xfff_fff7; + const value_reserved_and_should_not_be_used_eof_start = 0xfff_fff8; + const value_reserved_and_should_not_be_used_eof_end = 0xfff_fffe; + const value_allocated_and_eof = 0xfff_ffff; + + pub const Type = enum { + free, + allocated, + reserved_and_should_not_be_used, + bad_cluster, + reserved_and_should_not_be_used_eof, + allocated_and_eof, + }; +}; + +pub fn getMinCluster(comptime filesystem: lib.FilesystemType) comptime_int { + return switch (filesystem) { + .fat32 => 65525, + else => @compileError("Filesystem not supported"), + }; +} + +pub fn getMaxCluster(comptime filesystem: lib.FilesystemType) comptime_int { + return switch (filesystem) { + .fat32 => 268435446, + else => @compileError("Filesystem not supported"), + }; +} + +const dot_entry_name: [short_name_len]u8 = ".".* ++ ([1]u8{' '} ** 10); +const dot_dot_entry_name: [short_name_len]u8 = "..".* ++ ([1]u8{' '} ** 9); + +pub const NameConfiguration = packed struct(u8) { + display: Display, + create: Create, + reserved: u5 = 0, + + const Create = enum(u1) { + windows_95 = 0, + windows_nt = 1, + }; + const Display = enum(u2) { + lower = 0, + windows_95 = 1, + windows_nt = 2, + }; +}; + +const lower = NameConfiguration{ .display = .lower, .create = .windows_95 }; +const windows_95 = NameConfiguration{ .display = .windows_95, .create = .windows_95 }; +const windows_nt = NameConfiguration{ .display = .windows_nt, .create = .windows_nt }; +const mixed = NameConfiguration{ .display = .windows_nt, .create = .windows_95 }; + +pub const Cache = extern struct { + disk: *Disk, + partition_range: Disk.PartitionRange, + mbr: *MBR.Partition, + fs_info: *FSInfo, + name_configuration: NameConfiguration = mixed, + allocator: ?*lib.Allocator, + + fn get_backup_boot_record_sector(cache: Cache) u64 { + return cache.partition_range.first_lba + cache.mbr.bpb.backup_boot_record_sector; + } + + pub fn readFile(cache: Cache, allocator: ?*lib.Allocator, file_path: []const u8) ![]u8 { + const directory_entry_result = try cache.getDirectoryEntry(file_path, null); + const directory_entry = directory_entry_result.directory_entry; + const first_cluster = directory_entry.getFirstCluster(); + const file_size = directory_entry.file_size; + const aligned_file_size = lib.alignForward(file_size, cache.disk.sector_size); + const lba = cache.clusterToSector(first_cluster); + const result = try cache.disk.readSlice(u8, aligned_file_size, lba, allocator, .{}); + return result[0..file_size]; + } + + pub fn readFileToBuffer(cache: Cache, file_path: []const u8, file_buffer: []u8) ![]u8 { + const directory_entry_result = try cache.getDirectoryEntry(file_path, null); + const directory_entry = directory_entry_result.directory_entry; + const first_cluster = directory_entry.getFirstCluster(); + const file_size = directory_entry.file_size; + const aligned_file_size = lib.alignForward(usize, file_size, cache.disk.sector_size); + const lba = cache.clusterToSector(first_cluster); + + log.debug("Start disk callback", .{}); + + const result = try cache.disk.callbacks.read(cache.disk, @divExact(aligned_file_size, cache.disk.sector_size), lba, file_buffer); + log.debug("End disk callback", .{}); + return result.buffer[0..file_size]; + } + + pub fn readFileToCache(cache: Cache, file_path: []const u8, size: usize) ![]const u8 { + const directory_entry_result = try cache.getDirectoryEntry(file_path, null); + const directory_entry = directory_entry_result.directory_entry; + const first_cluster = directory_entry.getFirstCluster(); + const file_size = directory_entry.file_size; + const lba = cache.clusterToSector(first_cluster); + + const read_size = @min(file_size, size); + const aligned_read_size = lib.alignForward(usize, read_size, cache.disk.sector_size); + + const result = try cache.disk.callbacks.readCache(cache.disk, @divExact(aligned_read_size, cache.disk.sector_size), lba); + const result_slice = result.buffer[0..read_size]; + return result_slice; + } + + pub fn getFileSize(cache: Cache, file_path: []const u8) !u32 { + const directory_entry_result = try cache.getDirectoryEntry(file_path, null); + return directory_entry_result.directory_entry.file_size; + } + + pub fn fromGPTPartitionCache(allocator: *lib.Allocator, gpt_partition_cache: GPT.Partition.Cache) !FAT32.Cache { + const partition_range = Disk.PartitionRange{ + .first_lba = gpt_partition_cache.partition.first_lba, + .last_lba = gpt_partition_cache.partition.last_lba, + }; + const disk = gpt_partition_cache.gpt.disk; + + const partition_mbr = try disk.readTypedSectors(MBR.Partition, partition_range.first_lba, allocator, .{}); + assert(partition_mbr.bpb.dos3_31.dos2_0.cluster_sector_count == 1); + const fs_info_sector = partition_range.first_lba + partition_mbr.bpb.fs_info_sector; + const fs_info = try disk.readTypedSectors(FAT32.FSInfo, fs_info_sector, allocator, .{}); + + return .{ + .disk = disk, + .partition_range = partition_range, + .mbr = partition_mbr, + .fs_info = fs_info, + .allocator = allocator, + }; + } + + pub fn reserveDirectoryEntries(cache: Cache, cluster: u32, entry_count: usize) !ReserveDirectoryEntries { + const root_cluster = cache.get_root_cluster(); + const root_cluster_lba = cache.get_data_lba(); + const cluster_directory_entry_offset_lba = cache.getClusterSectorCount() * (cluster - root_cluster); + const cluster_directory_entry_lba = root_cluster_lba + cluster_directory_entry_offset_lba; + const cluster_sector_count = cache.getClusterSectorCount(); + assert(cluster_sector_count == 1); + + // TODO: what to do when there's more than one cluster per directory? + const top_cluster_lba = cluster_directory_entry_lba + cluster_sector_count; + var cluster_lba = cluster_directory_entry_lba; + + while (cluster_lba < top_cluster_lba) : (cluster_lba += 1) { + const fat_directory_entries = try cache.disk.readTypedSectors(DirectoryEntry.Sector, cluster_lba); + + for (fat_directory_entries, 0..) |*entry, entry_index| { + if (entry.is_free()) { + const free_entries_in_sector = fat_directory_entries.len - entry_index; + assert(entry_count <= free_entries_in_sector); + return .{ + .cluster_lba = cluster_lba, + .first_entry_index = entry_index, + }; + } + } + } + + return ReserveDirectoryEntries.Error.no_free_space; + } + + const ReserveDirectoryEntries = extern struct { + cluster_lba: u64, + first_entry_index: usize, + + const Error = error{ + no_free_space, + }; + }; + + pub const GetError = error{ + not_found, + entry_already_exist, + }; + + fn getDirectoryEntryCluster(cache: Cache, dir: []const u8) !u32 { + if (lib.equal(u8, dir, "/")) { + return cache.getRootCluster(); + } else { + const containing_dir_entry = try cache.getDirectoryEntry(dir, null); + return containing_dir_entry.directory_entry.getFirstCluster(); + } + } + + pub fn makeNewDirectory(cache: Cache, absolute_path: []const u8, allocator: ?*lib.Allocator, copy_cache: ?FAT32.Cache, miliseconds: u64) !void { + const copy_entry: ?*DirectoryEntry = if (copy_cache) |my_copy_cache| (try my_copy_cache.getDirectoryEntry(absolute_path, null)).directory_entry else null; + const last_slash_index = lib.lastIndexOf(u8, absolute_path, "/") orelse @panic("there must be a slash"); + const containing_dir = absolute_path[0..if (last_slash_index == 0) 1 else last_slash_index]; + const containing_dir_cluster = try cache.getDirectoryEntryCluster(containing_dir); + const content_cluster = try cache.allocateNewDirectory(containing_dir_cluster, allocator, copy_cache); + const last_element = absolute_path[last_slash_index + 1 ..]; + try cache.addEntry(.{ .name = last_element, .is_dir = true, .content_cluster = content_cluster, .containing_cluster = containing_dir_cluster }, allocator, copy_entry, miliseconds); + } + + pub fn makeNewFile(cache: Cache, file_path: []const u8, file_content: []const u8, allocator: ?*lib.Allocator, copy_cache: ?FAT32.Cache, milliseconds: u64) !void { + const copy_entry: ?*DirectoryEntry = if (copy_cache) |my_copy_cache| (try my_copy_cache.getDirectoryEntry(file_path, null)).directory_entry else null; + const last_slash_index = lib.lastIndexOf(u8, file_path, "/") orelse @panic("there must be a slash"); + const containing_dir = file_path[0..if (last_slash_index == 0) 1 else last_slash_index]; + const containing_dir_cluster = try cache.getDirectoryEntryCluster(containing_dir); + const content_cluster = try cache.allocateNewFile(file_content, allocator); + const file_name = file_path[last_slash_index + 1 ..]; + try cache.addEntry(.{ .name = file_name, .size = @as(u32, @intCast(file_content.len)), .is_dir = false, .content_cluster = content_cluster, .containing_cluster = containing_dir_cluster }, allocator, copy_entry, milliseconds); + } + + fn allocateNewFile(cache: Cache, file_content: []const u8, maybe_allocator: ?*lib.Allocator) !u32 { + assert(file_content.len > 0); + const cluster_byte_count = cache.getClusterSectorCount() * cache.disk.sector_size; + const aligned_file_size = lib.alignForward(usize, file_content.len, cluster_byte_count); + const cluster_count = @divExact(aligned_file_size, cluster_byte_count); + // log.debug("Need to allocate {} clusters for file", .{cluster_count}); + const allocator = maybe_allocator orelse @panic("We need an allocator"); + const clusters = blk: { + const alloc_result = try allocator.allocateBytes(@sizeOf(u32) * cluster_count, @alignOf(u32)); + break :blk @as([*]u32, @ptrFromInt(alloc_result.address))[0..cluster_count]; + }; + try cache.allocateClusters(clusters, allocator); + + for (clusters, 0..) |cluster, cluster_index| { + const cluster_byte_offset = cluster_byte_count * cluster_index; + const slice_start = cluster_byte_offset; + const slice_end = cluster_byte_offset + cluster_byte_count; + const slice = file_content[slice_start..if (slice_end > file_content.len) file_content.len else slice_end]; + const lba = cache.clusterToSector(cluster); + try cache.disk.writeSlice(u8, slice, lba, true); + } + + return clusters[0]; + } + + const Size = struct { + len: u16, + size: u16, + }; + + fn translateToUnicode(name: []const u8, buffer: []u16) !Size { + // Using always UTF8 + const len = try lib.unicode.utf8ToUtf16Le(buffer, name); + var size = len; + if (size % character_count_per_long_entry != 0) { + buffer[size] = 0; + size += 1; + const remainder = size % character_count_per_long_entry; + if (remainder != 0) { + const characters_to_fill = character_count_per_long_entry - remainder; + + for (buffer[size .. size + characters_to_fill]) |*wide_char| { + wide_char.* = lib.maxInt(u16); + } + + size += characters_to_fill; + } + } + + return .{ .len = @as(u16, @intCast(len)), .size = @as(u16, @intCast(size)) }; + } + + const BadChar = error{ + bad_value, + last_character_space, + }; + + fn checkBadCharacters(string: []u16) !void { + for (string) |wchar| { + if (wchar < 0x20 or wchar == '*' or wchar == '?' or wchar == '<' or wchar == '>' or wchar == '|' or wchar == '"' or wchar == ':' or wchar == '/' or wchar == '\\') return BadChar.bad_value; + } + + if (string[string.len - 1] == ' ') return BadChar.last_character_space; + } + + fn isSkipCharacter(wchar: u16) bool { + return wchar == '.' or wchar == ' '; + } + + fn isReplaceCharacter(wchar: u16) bool { + return wchar == '[' or wchar == ']' or wchar == ';' or wchar == ',' or wchar == '+' or wchar == '='; + } + + const ShortNameInfo = packed struct(u8) { + len: u5 = 0, + lower: bool = true, + upper: bool = true, + valid: bool = true, + }; + + fn toShortNameCharacter(nls: *const NLS.Table, wchar: u16, char_buffer: []u8) !ShortNameInfo { + var is_lower = true; + var is_upper = true; + var is_valid = true; + + if (isSkipCharacter(wchar)) @panic("short names must not contain skip characters"); + if (isReplaceCharacter(wchar)) @panic("short names must not contain replace characters"); + + try nls.unicode_to_character(wchar, char_buffer); + + // TODO: + const len = 1; + if (len == 0) { + @panic("nls: character length 0"); + } else if (len == 1) { + const previous = char_buffer[0]; + + if (previous >= 0x7f) @panic("nls: character value is too high"); + + char_buffer[0] = nls.to_upper(previous); + if (lib.isAlphabetic(char_buffer[0])) { + if (char_buffer[0] == previous) { + is_lower = false; + } else { + is_upper = false; + } + } + } else @panic("nls: unexpected length"); + + return ShortNameInfo{ + .len = @as(u5, @intCast(len)), + .lower = is_lower, + .upper = is_upper, + .valid = is_valid, + }; + } + + const ShortNameResult = extern struct { + name: [short_name_len]u8, + case: NameCase, + }; + + fn createShortName(cache: Cache, nls: *const NLS.Table, long_name: []u16, cluster: u32, short_name_result: *ShortNameResult, allocator: ?*lib.Allocator) !bool { + var is_short_name = true; + const end = lib.ptrAdd(u16, &long_name[0], long_name.len); + var extension_start: ?*u16 = end; + var size: usize = 0; + + while (true) { + extension_start = lib.maybePtrSub(u16, extension_start, 1); + if (@intFromPtr(extension_start) < @intFromPtr(&long_name[0])) break; + + if (extension_start.?.* == '.') { + if (extension_start == lib.ptrSub(u16, end, 1)) { + size = long_name.len; + extension_start = null; + } + + break; + } + } + + if (extension_start == lib.ptrSub(u16, &long_name[0], 1)) { + size = long_name.len; + extension_start = null; + } else if (extension_start) |ext_start| { + const extension_start_index = @divExact(@intFromPtr(ext_start) - @intFromPtr(&long_name[0]), @sizeOf(u16)); + const index = blk: { + const slice = long_name[0..extension_start_index]; + + for (slice, 0..) |wchar, index| { + if (!isSkipCharacter(wchar)) break :blk index; + } + + break :blk slice.len; + }; + + if (index != extension_start_index) { + size = extension_start_index; + extension_start = lib.maybePtrAdd(u16, extension_start, 1); + } else { + size = long_name.len; + extension_start = null; + } + } + + var numtail_base_len: usize = 6; + var numtail2_base_len: usize = 2; + + var char_buffer: [NLS.max_charset_size]u8 = undefined; + var base: [9]u8 = undefined; + var long_name_index: usize = 0; + var base_len: usize = 0; + var pointer_it: usize = 0; + var base_info = ShortNameInfo{}; + var extension_info = ShortNameInfo{}; + + while (long_name_index < size) : (long_name_index += 1) { + const wchar = long_name[long_name_index]; + // TODO: chl + // TODO: shortname_info + base_info = try toShortNameCharacter(nls, wchar, &char_buffer); + + const chl = 1; + if (chl == 0) continue; + + if (base_len < 2 and (base_len + chl) > 2) { + numtail2_base_len = base_len; + } + + if (base_len < 6 and (base_len + chl) > 6) { + numtail_base_len = base_len; + } + + var char_index: usize = 0; + while (char_index < chl) : ({ + char_index += 1; + }) { + const char = char_buffer[char_index]; + base[pointer_it] = char; + pointer_it += 1; + base_len += 1; + if (base_len >= 8) break; + } + + if (base_len >= 8) { + if ((char_index < chl - 1) or (long_name_index + 1) < size) { + is_short_name = false; + } + break; + } + } + + if (base_len == 0) @panic("fat32: base length is 0"); + + var extension_len: usize = 0; + var extension: [4]u8 = undefined; + if (extension_start) |ext_start| { + const extension_start_index = @divExact(@intFromPtr(ext_start) - @intFromPtr(&long_name[0]), @sizeOf(u16)); + const extension_slice = long_name[extension_start_index..]; + var extension_index: usize = 0; + for (extension_slice, 0..) |extension_u16, extension_pointer_index| { + extension_info = toShortNameCharacter(nls, extension_u16, &char_buffer) catch continue; + + if (extension_len + extension_info.len > 3) { + is_short_name = false; + break; + } + + for (char_buffer[0..extension_info.len]) |ch| { + extension[extension_index] = ch; + extension_index += 1; + extension_len += 1; + } + + if (extension_len >= 3) { + if (extension_pointer_index + extension_start_index + 1 != long_name.len) { + is_short_name = false; + } + + break; + } + } + } + + extension[extension_len] = 0; + base[base_len] = 0; + + if (base[0] == 0xe5) base[0] = 0x05; + + short_name_result.* = ShortNameResult{ + .name = blk: { + var name = [1]u8{' '} ** short_name_len; + @memcpy(name[0..base_len], base[0..base_len]); + @memcpy(name[max_base_len .. max_base_len + extension_len], extension[0..extension_len]); + break :blk name; + }, + .case = .{ .base = .upper, .extension = .upper }, + }; + + if (is_short_name and base_info.valid and extension_info.valid) { + if (try cache.exists(&short_name_result.name, cluster, allocator)) @panic("fat32: entry with such name already exists"); + const result = switch (cache.name_configuration.create) { + .windows_95 => base_info.upper and extension_info.upper, + .windows_nt => @panic("fat32: unsupported name configuration"), + }; + return result; + } + + @panic("fat32: cannot create shortname"); + } + + pub fn scan(cache: Cache, name: []const u8, cluster: u32, allocator: ?*lib.Allocator) !?*DirectoryEntry { + var iterator = DirectoryEntryIterator(DirectoryEntry).init(cluster); + + while (try iterator.next(cache, allocator)) |entry| { + if (lib.equal(u8, &entry.name, name)) { + return entry; + } + } + + return null; + } + + pub fn exists(cache: Cache, name: []const u8, cluster: u32, allocator: ?*lib.Allocator) !bool { + return (try cache.scan(name, cluster, allocator)) != null; + } + + const GenericEntry = struct { + long_name_entries: []LongNameEntry = &.{}, + normal_entry: DirectoryEntry, + + pub fn isExtended(entry: GenericEntry) bool { + return entry.long_name_entries.len != 0; + } + + pub fn getSlots(entry: GenericEntry) usize { + return entry.long_name_entries.len + 1; + } + }; + + pub fn buildSlots(cache: Cache, entry_setup: EntrySetup, maybe_allocator: ?*lib.Allocator, copy_entry: ?*DirectoryEntry) !void { + var long_name_array = [1]u16{0} ** (long_name_max_characters + 2); + const size = try translateToUnicode(entry_setup.name, &long_name_array); + const long_name = long_name_array[0..size.len]; + try checkBadCharacters(long_name); + + var short_name_result: ShortNameResult = undefined; + const can_get_away_with_short_name = try cache.createShortName(&NLS.ascii.table, long_name, entry_setup.content_cluster, &short_name_result, maybe_allocator); + // TODO: timestamp + var entry = GenericEntry{ + .normal_entry = DirectoryEntry{ + .name = short_name_result.name, + .attributes = .{ + .directory = entry_setup.is_dir, + .archive = !entry_setup.is_dir, + }, + .case = short_name_result.case, + .creation_time_tenth = if (copy_entry) |e| e.creation_time_tenth else 0, + .creation_time = if (copy_entry) |e| e.creation_time else .{ + .seconds_2_factor = 0, + .minutes = 0, + .hours = 0, + }, + .creation_date = if (copy_entry) |e| e.creation_date else .{ + .day = 0, + .month = 0, + .year = 0, + }, + .last_access_date = if (copy_entry) |e| e.last_access_date else .{ + .day = 0, + .month = 0, + .year = 0, + }, + .first_cluster_high = @as(u16, @truncate(entry_setup.content_cluster >> 16)), + .last_write_time = if (copy_entry) |e| e.last_write_time else .{ + .seconds_2_factor = 0, + .minutes = 0, + .hours = 0, + }, + .last_write_date = if (copy_entry) |e| e.last_write_date else .{ + .day = 0, + .month = 0, + .year = 0, + }, + .first_cluster_low = @as(u16, @truncate(entry_setup.content_cluster)), + .file_size = entry_setup.size, + }, + }; + + if (!can_get_away_with_short_name) { + const checksum = shortNameCheckSum(&short_name_result.name); + + const long_slot_count = @as(u5, @intCast(size.size / character_count_per_long_entry)); + entry.long_name_entries = blk: { + const allocator = maybe_allocator orelse @panic("fat32: allocator not provided"); + const alloc_result = try allocator.allocateBytes(@as(usize, @intCast(@sizeOf(LongNameEntry))) * long_slot_count, @alignOf(LongNameEntry)); + break :blk @as([*]LongNameEntry, @ptrFromInt(alloc_result.address))[0..long_slot_count]; + }; + var reverse_index = long_slot_count; + + for (entry.long_name_entries) |*long_name_entry| { + const offset = (reverse_index - 1) * character_count_per_long_entry; + long_name_entry.* = .{ + .sequence_number = .{ + .number = reverse_index, + .last_logical = reverse_index == long_slot_count, + }, + .chars_0_4 = long_name_array[offset .. offset + 6][0..5].*, + .attributes = .{ + .read_only = true, + .hidden = true, + .system = true, + .volume_id = true, + }, + .checksum = checksum, + .chars_5_10 = long_name_array[offset + 5 .. offset + 11][0..6].*, + .first_cluster = 0, + .chars_11_12 = long_name_array[offset + 11 .. offset + 13][0..2].*, + }; + } + } + + const total_slots = entry.getSlots(); + var free_slots: usize = 0; + var entry_iterator = DirectoryEntryIterator(DirectoryEntry).init(entry_setup.containing_cluster); + var current_cluster: u32 = 0; + + while (try entry_iterator.next(cache, maybe_allocator)) |cluster_entry| { + if (cluster_entry.isFree()) { + if (free_slots == 0) current_cluster = @as(u32, @intCast(entry_iterator.cluster)); + free_slots += 1; + + if (free_slots == total_slots) { + const last_current_cluster = @as(u32, @intCast(entry_iterator.cluster)); + assert(last_current_cluster == current_cluster); + const element_offset = @divExact(@intFromPtr(cluster_entry) - @intFromPtr(&entry_iterator.cluster_entries[0]), @sizeOf(DirectoryEntry)); + const entry_start_index = element_offset - (free_slots - 1); + + var entry_index = entry_start_index; + for (entry.long_name_entries) |*long_name_entry| { + entry_iterator.cluster_entries[entry_index] = @as(DirectoryEntry, @bitCast(long_name_entry.*)); + entry_index += 1; + } + + entry_iterator.cluster_entries[entry_index] = entry.normal_entry; + + try cache.disk.writeSlice(DirectoryEntry, entry_iterator.cluster_entries, entry_iterator.getCurrentLBA(cache), false); + + return; + } + } else { + free_slots = 0; + } + } + + @panic("fat32: cannot build slots"); + } + + const EntrySetup = struct { + name: []const u8, + size: u32 = 0, + content_cluster: u32, + containing_cluster: u32, + is_dir: bool, + }; + + pub fn addEntry(cache: Cache, entry_setup: EntrySetup, maybe_allocator: ?*lib.Allocator, copy_entry: ?*DirectoryEntry, miliseconds: u64) !void { + _ = miliseconds; + + // TODO: + if (entry_setup.name[entry_setup.name.len - 1] == '.') @panic("todo: unexpected trailing dot"); + + try cache.buildSlots(entry_setup, maybe_allocator, copy_entry); + } + + pub fn shortNameCheckSum(name: []const u8) u8 { + var result = name[0]; + + result = (result << 7) + (result >> 1) +% name[1]; + result = (result << 7) + (result >> 1) +% name[2]; + result = (result << 7) + (result >> 1) +% name[3]; + result = (result << 7) + (result >> 1) +% name[4]; + result = (result << 7) + (result >> 1) +% name[5]; + result = (result << 7) + (result >> 1) +% name[6]; + result = (result << 7) + (result >> 1) +% name[7]; + result = (result << 7) + (result >> 1) +% name[8]; + result = (result << 7) + (result >> 1) +% name[9]; + result = (result << 7) + (result >> 1) +% name[10]; + + return result; + } + + pub fn allocateNewDirectory(cache: Cache, containing_cluster: u32, allocator: ?*lib.Allocator, copy_cache: ?FAT32.Cache) !u32 { + var clusters = [1]u32{0}; + try cache.allocateClusters(&clusters, allocator); + const cluster = clusters[0]; + const lba = cache.clusterToSector(cluster); + const fat_directory_entries = try cache.disk.readTypedSectors(FAT32.DirectoryEntry.Sector, lba, allocator, .{}); + + var copy_entry: ?*FAT32.DirectoryEntry = null; + if (copy_cache) |cp_cache| { + const entries = try cp_cache.disk.readTypedSectors(FAT32.DirectoryEntry.Sector, cp_cache.clusterToSector(cluster), allocator, .{}); + copy_entry = &entries[0]; + } + const attributes = Attributes{ + .read_only = false, + .hidden = false, + .system = false, + .volume_id = false, + .directory = true, + .archive = false, + }; + + const date = .{ + .day = 0, + .month = 0, + .year = 0, + }; + const time = .{ + .seconds_2_factor = 0, + .minutes = 0, + .hours = 0, + }; + fat_directory_entries[0] = FAT32.DirectoryEntry{ + .name = dot_entry_name, + .attributes = attributes, + .case = .{}, + .creation_time_tenth = if (copy_entry) |ce| ce.creation_time_tenth else 0, + .creation_time = if (copy_entry) |ce| ce.creation_time else time, + .creation_date = if (copy_entry) |ce| ce.creation_date else date, + .first_cluster_high = @as(u16, @truncate(cluster >> 16)), + .first_cluster_low = @as(u16, @truncate(cluster)), + .last_access_date = if (copy_entry) |ce| ce.last_access_date else date, + .last_write_time = if (copy_entry) |ce| ce.last_write_time else time, + .last_write_date = if (copy_entry) |ce| ce.last_write_date else date, + .file_size = 0, + }; + // Copy the values and only modify the necessary ones + fat_directory_entries[1] = fat_directory_entries[0]; + fat_directory_entries[1].name = dot_dot_entry_name; + // TODO: Fix this + fat_directory_entries[1].setFirstCluster(if (containing_cluster == cache.getRootCluster()) 0 else containing_cluster); + // if (copy_entry) |cp_entry| { + // const copy_cluster = cp_entry.get_first_cluster(); + // const dot_entry_cluster = fat_directory_entries[0].get_first_cluster(); + // const dot_dot_entry_cluster = fat_directory_entries[1].get_first_cluster(); + // } + + // TODO: zero initialize the unused part of the cluster + try cache.disk.writeTypedSectors(FAT32.DirectoryEntry.Sector, fat_directory_entries, lba, false); + + return cluster; + } + + pub inline fn clusterToSector(cache: Cache, cluster: u32) u64 { + return (@as(u64, cluster) - cache.getRootCluster()) * cache.getClusterSectorCount() + cache.getDataLBA(); + } + + pub fn registerCluster(cache: Cache, cluster: u32, entry: Entry, allocator: ?*lib.Allocator) !void { + const fat_lba = cache.getFATLBA(); + const fat_entry_count = cache.mbr.bpb.dos3_31.dos2_0.fat_count; + const fat_entry_sector_count = cache.mbr.bpb.fat_sector_count_32; + + if (entry.isAllocating()) { + cache.fs_info.last_allocated_cluster = cluster; + cache.fs_info.free_cluster_count -= 1; + } + + // Actually allocate FAT entry + + var fat_index: u8 = 0; + + const fat_entry_sector_index = cluster % FAT32.Entry.per_sector; + + const cluster_offset = cluster * @sizeOf(u32) / cache.disk.sector_size; + while (fat_index < fat_entry_count) : (fat_index += 1) { + const fat_entry_lba = fat_lba + (fat_index * fat_entry_sector_count) + cluster_offset; + const fat_entry_sector = try cache.disk.readTypedSectors(FAT32.Entry.Sector, fat_entry_lba, allocator, .{}); + fat_entry_sector[fat_entry_sector_index] = entry; + try cache.disk.writeTypedSectors(FAT32.Entry.Sector, fat_entry_sector, fat_entry_lba, false); + } + } + + pub fn allocateClusters(cache: Cache, clusters: []u32, maybe_allocator: ?*lib.Allocator) !void { + var fat_entry_iterator = try FATEntryIterator.init(cache, maybe_allocator); + var cluster_index: usize = 0; + + var previous_cluster: ?u32 = null; + + while (try fat_entry_iterator.next(cache, maybe_allocator)) |cluster| { + const entry = &fat_entry_iterator.entries[cluster % Entry.per_sector]; + if (entry.isFree()) { + if (previous_cluster) |pc| { + if (pc != cluster - 1) { + // log.debug("PC: 0x{x}. CC: 0x{x}", .{ pc, cluster }); + @panic("allocateClusters: unreachable"); + } + } + const should_return = cluster_index == clusters.len - 1; + try cache.registerCluster(cluster, if (should_return) Entry.allocated_and_eof else Entry{ + .next_cluster = @as(u28, @intCast(cluster + 1)), + }, maybe_allocator); + clusters[cluster_index] = cluster; + cluster_index += 1; + + if (should_return) { + // const first_cluster = @intCast(u32, cluster - clusters.len + 1); + // log.debug("First cluster: 0x{x}. Last cluster: 0x{x}", .{ first_cluster, cluster }); + // log.debug("Allocated cluster range: {}-{}. LBA range: 0x{x}-0x{x}", .{ first_cluster, cluster, cache.clusterToSector(first_cluster), cache.clusterToSector(cluster) }); + return; + } + + previous_cluster = cluster; + } else if (cluster_index > 0) { + @panic("cluster index unreachable"); + } + } + + @panic("fat32: allocateClusters"); + } + + pub fn getDirectoryEntry(cache: Cache, absolute_path: []const u8, copy_cache: ?Cache) !EntryResult(DirectoryEntry) { + const fat_lba = cache.partition_range.first_lba + cache.mbr.bpb.dos3_31.dos2_0.reserved_sector_count; + const root_cluster = cache.mbr.bpb.root_directory_cluster_offset; + const data_lba = fat_lba + (cache.mbr.bpb.fat_sector_count_32 * cache.mbr.bpb.dos3_31.dos2_0.fat_count); + + const root_cluster_sector = data_lba; + var upper_cluster = root_cluster; + var dir_tokenizer = lib.DirectoryTokenizer.init(absolute_path); + var directories: usize = 0; + + const first_dir = dir_tokenizer.next() orelse @panic("fat32: there must be at least one directory in the path"); + assert(lib.equal(u8, first_dir, "/")); + + entry_loop: while (dir_tokenizer.next()) |entry_name| : (directories += 1) { + const is_last = dir_tokenizer.is_last(); + + const copy_entry: ?*FAT32.DirectoryEntry = blk: { + if (copy_cache) |cc| { + const name = absolute_path[0..dir_tokenizer.index]; + const entry_result = try cc.getDirectoryEntry(name, null); + break :blk entry_result.directory_entry; + } else break :blk null; + }; + _ = copy_entry; + + const normalized_name = packString(entry_name, .{ + .len = short_name_len, + .fill_with = ' ', + .upper = true, + }); + + while (true) : (upper_cluster += 1) { + const cluster_sector_offset = root_cluster_sector + cache.getClusterSectorCount() * (upper_cluster - root_cluster); + const directory_entries_in_cluster = try cache.disk.readTypedSectors(DirectoryEntry.Sector, cluster_sector_offset, cache.allocator, .{}); + + var entry_index: usize = 0; + while (entry_index < directory_entries_in_cluster.len) : ({ + entry_index += 1; + }) { + const directory_entry = &directory_entries_in_cluster[entry_index]; + const is_empty = directory_entry.name[0] == 0; + const is_unused = directory_entry.name[0] == 0xe5; + const is_long_name = directory_entry.attributes.hasLongName(); + + // At this point all entries in the given directory have been checked, so it's safe to say the directory doesn't contain the wanted entry + if (is_empty) { + return GetError.not_found; + } else { + if (is_unused) { + @panic("fat32: unused entry found"); + } else if (is_long_name) { + const long_name_entry = @as(*FAT32.LongNameEntry, @ptrCast(directory_entry)); + const original_starting_index = entry_index; + + if (long_name_entry.isLast()) { + entry_index += 1; + assert(entry_index < directory_entries_in_cluster.len); + const long_name_u16 = long_name_entry.getCharacters(); + var arr: [long_name_u16.len]u8 = [1]u8{0} ** long_name_u16.len; + const long_name_u8 = blk: { + for (long_name_u16, 0..) |u16_ch, index| { + if (u16_ch == 0) { + break :blk arr[0..index]; + } else if (u16_ch <= lib.maxInt(u8)) { + arr[index] = @as(u8, @intCast(u16_ch)); + } else { + @panic("fat32: u16 unreachable"); + } + } + + @panic("long_name_u8 unreachable"); + }; + + // TODO: compare long name entry + if (lib.equal(u8, long_name_u8, entry_name)) { + const normal_entry = &directory_entries_in_cluster[entry_index]; + if (is_last) { + return .{ .cluster = upper_cluster, .entry_starting_index = @as(u32, @intCast(original_starting_index)), .directory_entry = normal_entry }; + } else { + upper_cluster = normal_entry.getFirstCluster(); + continue :entry_loop; + } + } + } else { + @panic("fat32: not last entry"); + } + } else { + if (lib.equal(u8, &directory_entry.name, &normalized_name)) { + if (is_last) { + return .{ .cluster = upper_cluster, .entry_starting_index = @as(u32, @intCast(entry_index)), .directory_entry = directory_entry }; + } else { + upper_cluster = directory_entry.getFirstCluster(); + continue :entry_loop; + } + } + } + } + } + + return GetError.not_found; + } + } + + @panic("fat32: unable to get directory entry"); + } + + pub inline fn getFATLBA(cache: Cache) u64 { + const fat_lba = cache.partition_range.first_lba + cache.mbr.bpb.dos3_31.dos2_0.reserved_sector_count; + return fat_lba; + } + + pub inline fn getDataLBA(cache: Cache) u64 { + const data_lba = cache.getFATLBA() + (cache.mbr.bpb.fat_sector_count_32 * cache.mbr.bpb.dos3_31.dos2_0.fat_count); + return data_lba; + } + + pub inline fn getRootCluster(cache: Cache) u32 { + const root_cluster = cache.mbr.bpb.root_directory_cluster_offset; + return root_cluster; + } + + pub inline fn getClusterSectorCount(cache: Cache) u32 { + return cache.mbr.bpb.dos3_31.dos2_0.cluster_sector_count; + } +}; + +const PackStringOptions = packed struct(u64) { + fill_with: u8, + len: u8, + upper: bool, + reserved: u47 = 0, +}; + +pub inline fn packString(name: []const u8, comptime options: PackStringOptions) [options.len]u8 { + var result = [1]u8{options.fill_with} ** options.len; + if (name.len > 0) { + if (options.upper) { + _ = lib.upperString(&result, name); + } else { + @memcpy(&result, name); + } + } + + return result; +} + +const character_count_per_long_entry = 13; + +fn EntryResult(comptime EntryType: type) type { + return extern struct { + entry_starting_index: usize, + directory_entry: *EntryType, + cluster: u32, + }; +} + +// Sadly we have to wrap shell commands into scripts because of shell redirection usages + +const FATEntryIterator = struct { + entries: []FAT32.Entry = &.{}, + cluster: u32, + + fn init(cache: Cache, allocator: ?*lib.Allocator) !FATEntryIterator { + const cluster = cache.fs_info.last_allocated_cluster + 1; + assert(cache.disk.sector_size == @sizeOf(FAT32.Entry.Sector)); + const lba_offset = cache.getFATLBA() + (cluster / FAT32.Entry.per_sector); + + return .{ + .entries = try cache.disk.readTypedSectors(FAT32.Entry.Sector, lba_offset, allocator, .{}), + .cluster = cluster, + }; + } + + fn next(iterator: *FATEntryIterator, cache: Cache, allocator: ?*lib.Allocator) !?u32 { + var cluster_count: usize = starting_cluster; + // TODO: replace with proper variable + const max_clusters = 100000; + if (cache.disk.sector_size != @sizeOf(FAT32.Entry.Sector)) @panic("Unexpected disk sector size"); + + while (cluster_count < max_clusters) { + if (cluster_count >= max_clusters) cluster_count = starting_cluster; + + if (iterator.cluster != 0 and iterator.cluster % iterator.entries.len == 0) { + const lba_offset = cache.getFATLBA() + (iterator.cluster / FAT32.Entry.per_sector); + iterator.entries = try cache.disk.readTypedSectors(FAT32.Entry.Sector, lba_offset, allocator, .{}); + } + + const result = iterator.cluster; + iterator.cluster += 1; + return result; + } + + @panic("fat32: entry iterator unreachable"); + } +}; + +fn DirectoryEntryIterator(comptime EntryType: type) type { + assert(EntryType == DirectoryEntry or EntryType == LongNameEntry); + + return struct { + cluster_entries: []EntryType = &.{}, + cluster_it: u32 = 0, + cluster: u32, + cluster_fetched: bool = false, + + const Iterator = @This(); + + pub fn init(cluster: u32) Iterator { + return Iterator{ + .cluster = cluster, + }; + } + + pub fn getCurrentLBA(iterator: *Iterator, cache: Cache) u64 { + const cluster_lba = cache.clusterToSector(iterator.cluster); + return cluster_lba; + } + + pub fn next(iterator: *Iterator, cache: Cache, allocator: ?*lib.Allocator) !?*EntryType { + if (iterator.cluster_fetched) iterator.cluster_it += 1; + + const cluster_sector_count = cache.getClusterSectorCount(); + const cluster_entry_count = @divExact(cluster_sector_count * cache.disk.sector_size, @sizeOf(EntryType)); + assert(iterator.cluster_it <= cluster_entry_count); + if (iterator.cluster_it == cluster_entry_count) return null; // TODO: Should we early return like this? + + if (!iterator.cluster_fetched or iterator.cluster_it == cluster_entry_count) { + if (iterator.cluster_it == cluster_entry_count) iterator.cluster += 1; + + const cluster_lba = cache.clusterToSector(iterator.cluster); + iterator.cluster_entries = try cache.disk.readSlice(EntryType, cluster_entry_count, cluster_lba, allocator, .{}); + iterator.cluster_it = 0; + iterator.cluster_fetched = true; + } + + return &iterator.cluster_entries[iterator.cluster_it]; + } + }; +} diff --git a/src/lib/graphics.zig b/src/lib/graphics.zig new file mode 100644 index 0000000..545a437 --- /dev/null +++ b/src/lib/graphics.zig @@ -0,0 +1,184 @@ +pub const Driver = @This(); + +const lib = @import("lib"); +const assert = lib.assert; +const log = lib.log.scoped(.Graphics); + +pub const Rectangle = @import("graphics/rectangle.zig"); +pub const Rect = Rectangle.Rectangle; + +const Type = enum(u64) { + limine = 0, + virtio = 1, + sdl_software_renderer_prototype = 2, +}; + +const UpdateScreenFunction = fn (graphics: *Driver, drawing_area: DrawingArea, destination: Point) void; + +type: Type, +frontbuffer: Framebuffer, +backbuffer: DrawingArea, +callback_update_screen: *const UpdateScreenFunction, + +pub const DrawingArea = struct { + bytes: [*]u8 = undefined, + width: u32 = 0, + height: u32 = 0, + stride: u32 = 0, +}; + +pub const Point = struct { + x: u32 = 0, + y: u32 = 0, +}; + +pub const Framebuffer = struct { + area: DrawingArea = .{}, + modified_region: Rect = Rectangle.zero(), + + pub fn get_pixel_count(framebuffer: Framebuffer) u32 { + return framebuffer.area.width * framebuffer.area.height; + } + + pub fn get_pointer(framebuffer: Framebuffer) [*]u32 { + return @as([*]u32, @ptrCast(@alignCast(@alignOf(u32), framebuffer.area.bytes))); + } + + pub fn resize(framebuffer: *Framebuffer, allocator: lib.CustomAllocator, width: u32, height: u32) bool { + // TODO: copy old bytes + // TODO: free old bytes + if (width == 0 or height == 0) return false; + + const old_width = framebuffer.area.width; + const old_height = framebuffer.area.height; + + if (width == old_width and height == old_height) return true; + + // TODO: stop hardcoding the 4 + const new_buffer_memory = allocator.allocate_bytes(width * height * 4, 0x1000) catch unreachable; + framebuffer.area = DrawingArea{ + .bytes = @as([*]u8, @ptrFromInt(new_buffer_memory.address)), + .width = width, + .height = height, + .stride = width * 4, + }; + + // Clear it with white to debug it + framebuffer.fill(0); + + return true; + } + + pub fn fill(framebuffer: *Framebuffer, color: u32) void { + assert(@divExact(framebuffer.area.stride, framebuffer.area.width) == @sizeOf(u32)); + + for (@as([*]u32, @ptrCast(@alignCast(@alignOf(u32), framebuffer.area.bytes)))[0..framebuffer.get_pixel_count()]) |*pixel| { + pixel.* = color; + } + } + + pub fn copy(framebuffer: *Framebuffer, source: *Framebuffer, destination_point: Point, source_region: Rect, add_to_modified_region: bool) void { + const destination_region = Rectangle.from_point_and_rectangle(destination_point, source_region); + + const surface_clip = Rectangle.from_area(framebuffer.area); + + if (add_to_modified_region) { + framebuffer.update_modified_region(destination_region); + } + + const source_ptr = @as([*]u32, @ptrCast(@alignCast(@alignOf(u32), source.area.bytes + source.area.stride * Rectangle.top(source_region) + 4 * Rectangle.left(source_region)))); + framebuffer.draw_bitmap(surface_clip, destination_region, source_ptr, source.area.stride, .opaque_mode); + } + + fn update_modified_region(framebuffer: *Framebuffer, destination_region: Rect) void { + framebuffer.modified_region = Rectangle.bounding(destination_region, framebuffer.modified_region); + framebuffer.modified_region = Rectangle.clip(framebuffer.modified_region, Rectangle.from_area(framebuffer.area)).intersection; + } + + pub fn draw(framebuffer: *Framebuffer, source: *Framebuffer, destination_region: Rect, source_offset: Point, alpha: DrawBitmapMode) void { + const surface_clip = Rectangle.from_area(framebuffer.area); + framebuffer.update_modified_region(destination_region); + const source_ptr = @as([*]u32, @ptrCast(@alignCast(@alignOf(u32), source.area.bytes + source.area.stride * source_offset.y + 4 * source_offset.x))); + framebuffer.draw_bitmap(surface_clip, destination_region, source_ptr, source.area.stride, alpha); + } + + pub fn draw_bitmap(framebuffer: *Framebuffer, clip_area: Rect, region: Rect, source_ptr: [*]const u32, asked_source_stride: u32, mode: DrawBitmapMode) void { + const result = Rectangle.clip(region, clip_area); + if (result.clip) { + const bounds = result.intersection; + const source_stride = asked_source_stride / @sizeOf(u32); + const stride = framebuffer.area.stride / @sizeOf(u32); + const line_start_index = Rectangle.top(bounds) * stride + Rectangle.left(bounds); + var line_start = @as([*]u32, @ptrCast(@alignCast(@alignOf(u32), framebuffer.area.bytes))) + line_start_index; + const source_line_start_index = Rectangle.left(bounds) - Rectangle.left(region) + source_stride * (Rectangle.top(bounds) - Rectangle.top(region)); + var source_line_start = source_ptr + source_line_start_index; + + var i: u64 = 0; + const bounds_width = Rectangle.width(bounds); + const bounds_height = Rectangle.height(bounds); + + while (i < bounds_height) : ({ + i += 1; + line_start += stride; + source_line_start += source_stride; + }) { + var destination = line_start; + var source = source_line_start; + + var j = bounds_width; + if (@intFromEnum(mode) == 0xff) { + while (true) { + blend_pixel(&destination[0], source[0]); + destination += 1; + source += 1; + j -= 1; + if (j == 0) break; + } + } else if (@intFromEnum(mode) <= 0xff) { + @panic("todo: mode <= 0xff"); + } else if (mode == .xor) { + @panic("todo: mode xor"); + } else if (mode == .opaque_mode) { + // todo: refactor + while (j > 0) : ({ + destination += 1; + source += 1; + j -= 1; + }) { + destination[0] = 0xff_00_00_00 | source[0]; + } + } + } + } + } +}; + +// TODO: full alpha +fn blend_pixel(destination_pixel: *u32, modified: u32) void { + if (modified & 0xff_00_00_00 == 0xff_00_00_00) { + destination_pixel.* = modified; + return; + } else if (modified & 0xff_00_00_00 == 0) { + return; + } + + const original = destination_pixel.*; + + const m1 = (modified & 0xff_00_00_00) >> 24; + const m2 = 255 - m1; + const a = 0xff_00_00_00; + + const r2 = m2 * (original & 0x00FF00FF); + const g2 = m2 * (original & 0x0000FF00); + const r1 = m1 * (modified & 0x00FF00FF); + const g1 = m1 * (modified & 0x0000FF00); + const result = a | (0x0000FF00 & ((g1 + g2) >> 8)) | (0x00FF00FF & ((r1 + r2) >> 8)); + destination_pixel.* = result; +} + +pub const DrawBitmapMode = enum(u16) { + blend = 0, + xor = 0xfffe, + opaque_mode = 0xffff, + _, +}; diff --git a/src/lib/nls.zig b/src/lib/nls.zig new file mode 100644 index 0000000..bd70eec --- /dev/null +++ b/src/lib/nls.zig @@ -0,0 +1,21 @@ +pub const Error = error{ + name_too_long, + bad_value, +}; + +pub const max_charset_size = 6; + +pub const Table = struct { + character_set: []const u8, + unicode_to_character: *const fn (wchar: u16, char_string: []u8) Error!void, + character_to_unicode: *const fn (char_string: []u8) Error!u16, + character_set_to_lower: []const u8, + character_set_to_upper: []const u8, + + pub fn to_upper(table: *const Table, char: u8) u8 { + const possible_result = table.character_set_to_upper[char]; + return if (possible_result != 0) possible_result else char; + } +}; + +pub const ascii = @import("nls/ascii.zig"); diff --git a/src/lib/nls/ascii.zig b/src/lib/nls/ascii.zig new file mode 100644 index 0000000..c24e3ba --- /dev/null +++ b/src/lib/nls/ascii.zig @@ -0,0 +1,125 @@ +const lib = @import("lib"); +const NLS = lib.NLS; + +const charset_to_unicode: [256]u16 = [128]u16{ + 0x0000, 0x0001, 0x0002, 0x0003, + 0x0004, 0x0005, 0x0006, 0x0007, + 0x0008, 0x0009, 0x000a, 0x000b, + 0x000c, 0x000d, 0x000e, 0x000f, + 0x0010, 0x0011, 0x0012, 0x0013, + 0x0014, 0x0015, 0x0016, 0x0017, + 0x0018, 0x0019, 0x001a, 0x001b, + 0x001c, 0x001d, 0x001e, 0x001f, + 0x0020, 0x0021, 0x0022, 0x0023, + 0x0024, 0x0025, 0x0026, 0x0027, + 0x0028, 0x0029, 0x002a, 0x002b, + 0x002c, 0x002d, 0x002e, 0x002f, + 0x0030, 0x0031, 0x0032, 0x0033, + 0x0034, 0x0035, 0x0036, 0x0037, + 0x0038, 0x0039, 0x003a, 0x003b, + 0x003c, 0x003d, 0x003e, 0x003f, + 0x0040, 0x0041, 0x0042, 0x0043, + 0x0044, 0x0045, 0x0046, 0x0047, + 0x0048, 0x0049, 0x004a, 0x004b, + 0x004c, 0x004d, 0x004e, 0x004f, + 0x0050, 0x0051, 0x0052, 0x0053, + 0x0054, 0x0055, 0x0056, 0x0057, + 0x0058, 0x0059, 0x005a, 0x005b, + 0x005c, 0x005d, 0x005e, 0x005f, + 0x0060, 0x0061, 0x0062, 0x0063, + 0x0064, 0x0065, 0x0066, 0x0067, + 0x0068, 0x0069, 0x006a, 0x006b, + 0x006c, 0x006d, 0x006e, 0x006f, + 0x0070, 0x0071, 0x0072, 0x0073, + 0x0074, 0x0075, 0x0076, 0x0077, + 0x0078, 0x0079, 0x007a, 0x007b, + 0x007c, 0x007d, 0x007e, 0x007f, +} ++ [1]u16{0} ** 128; + +const page00: [256]u8 = [128]u8{ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, + 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, + 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, +} ++ [1]u8{0} ** 128; + +const page_unicode_to_charset = [_]*const [256]u8{ + &page00, +}; + +const charset_to_lower: [256]u8 = [128]u8{ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, + 0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, + 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, +} ++ [1]u8{0} ** 128; + +const charset_to_upper: [256]u8 = [128]u8{ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, + 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, + 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, + 0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, +} ++ [1]u8{0} ** 128; + +fn unicode_to_character(wchar: u16, char_string: []u8) NLS.Error!void { + //const unsigned char *uni2charset; + const wchar_low = @as(u8, @truncate(wchar)); + const wchar_high = @as(u8, @truncate(wchar >> 8)); + + if (char_string.len == 0) return NLS.Error.name_too_long; + + const unicode_to_charset = page_unicode_to_charset[wchar_high]; + const possible_out = unicode_to_charset[wchar_low]; + if (possible_out == 0) return NLS.Error.bad_value; + char_string[0] = possible_out; +} + +fn character_to_unicode(char_string: []const u8) NLS.Error!u16 { + const unicode = charset_to_unicode[char_string[0]]; + if (unicode == 0) return NLS.Error.bad_value; + return unicode; +} + +pub const table = NLS.Table{ + .character_set = "ascii", + .unicode_to_character = unicode_to_character, + .character_to_unicode = character_to_unicode, + .character_set_to_lower = &charset_to_lower, + .character_set_to_upper = &charset_to_upper, +}; diff --git a/src/lib/partition_table.zig b/src/lib/partition_table.zig new file mode 100644 index 0000000..0f5cd34 --- /dev/null +++ b/src/lib/partition_table.zig @@ -0,0 +1,11 @@ +const lib = @import("lib"); + +pub const GPT = @import("partition_table/gpt.zig"); +pub const MBR = @import("partition_table/mbr.zig"); + +test { + _ = GPT; + _ = MBR; +} + +pub const Type = lib.PartitionTableType; diff --git a/src/lib/partition_table/gpt.zig b/src/lib/partition_table/gpt.zig new file mode 100644 index 0000000..c65cc54 --- /dev/null +++ b/src/lib/partition_table/gpt.zig @@ -0,0 +1,416 @@ +const GPT = @This(); + +const lib = @import("lib"); +const assert = lib.assert; +const kb = lib.kb; +const mb = lib.mb; +const gb = lib.gb; +const CRC32 = lib.CRC32; +const Disk = lib.Disk; +const Filesystem = lib.Filesystem; +const FAT32 = Filesystem.FAT32; +const log = lib.log.scoped(.GPT); +const MBR = lib.PartitionTable.MBR; +const GUID = lib.uefi.Guid; +const Allocator = lib.Allocator; + +pub const default_max_partition_count = 128; +pub const min_block_size = lib.default_sector_size; +pub const max_block_size = 0x1000; + +pub const Header = extern struct { + signature: [8]u8 = "EFI PART".*, + revision: [4]u8 = .{ 0, 0, 1, 0 }, + header_size: u32 = @sizeOf(Header), + header_crc32: u32 = 0, + reserved: u32 = 0, + header_lba: u64, + backup_lba: u64, + first_usable_lba: u64, + last_usable_lba: u64, + disk_guid: GUID, + partition_array_lba: u64, + partition_entry_count: u32, + partition_entry_size: u32 = @sizeOf(Partition), + partition_array_crc32: u32, + reserved1: [420]u8 = [1]u8{0} ** 420, + + pub fn updateCrc32(header: *Header) void { + header.header_crc32 = 0; + header.header_crc32 = CRC32.compute(lib.asBytes(header)[0..header.header_size]); + } + + pub fn getPartititonCountInSector(header: *const Header, disk: *const Disk) u32 { + return @divExact(disk.sector_size, header.partition_entry_size); + } + + pub fn format(header: *const Header, comptime _: []const u8, _: lib.FormatOptions, writer: anytype) @TypeOf(writer).Error!void { + try lib.format(writer, "GPT header:\n", .{}); + try lib.format(writer, "\tSignature: {s}\n", .{header.signature}); + try lib.format(writer, "\tRevision: {any}\n", .{header.revision}); + try lib.format(writer, "\tHeader size: {}\n", .{header.header_size}); + try lib.format(writer, "\tHeader CRC32: 0x{x}\n", .{header.header_crc32}); + try lib.format(writer, "\tHeader LBA: 0x{x}\n", .{header.header_lba}); + try lib.format(writer, "\tAlternate header LBA: 0x{x}\n", .{header.backup_lba}); + try lib.format(writer, "\tFirst usable LBA: 0x{x}\n", .{header.first_usable_lba}); + try lib.format(writer, "\tLast usable LBA: 0x{x}\n", .{header.last_usable_lba}); + try lib.format(writer, "\tDisk GUID: {}\n", .{header.disk_guid}); + try lib.format(writer, "\tPartition array LBA: 0x{x}\n", .{header.partition_array_lba}); + try lib.format(writer, "\tPartition entry count: {}\n", .{header.partition_entry_count}); + try lib.format(writer, "\tPartition entry size: {}\n", .{header.partition_entry_size}); + try lib.format(writer, "\tPartition array CRC32: 0x{x}\n", .{header.partition_array_crc32}); + } + + pub fn compare(header: *const Header, other: *align(1) const Header) void { + log.debug("{}", .{header}); + log.debug("{}", .{other}); + + if (!lib.equal(u8, &header.signature, &other.signature)) { + log.debug("Signature mismatch: {s}, {s}", .{ header.signature, other.signature }); + } + if (!lib.equal(u8, &header.revision, &other.revision)) { + log.debug("Revision mismatch: {any}, {any}", .{ header.revision, other.revision }); + } + if (header.header_size != other.header_size) { + log.debug("Header size mismatch: {}, {}", .{ header.header_size, other.header_size }); + } + if (header.header_crc32 != other.header_crc32) { + log.debug("Header CRC32 mismatch: {}, {}", .{ header.header_crc32, other.header_crc32 }); + } + if (header.header_lba != other.header_lba) { + log.debug("Header LBA mismatch: {}, {}", .{ header.header_lba, other.header_lba }); + } + if (header.backup_lba != other.backup_lba) { + log.debug("Backup LBA mismatch: {}, {}", .{ header.backup_lba, other.backup_lba }); + } + if (header.first_usable_lba != other.first_usable_lba) { + log.debug("First usable LBA mismatch: {}, {}", .{ header.first_usable_lba, other.first_usable_lba }); + } + if (header.last_usable_lba != other.last_usable_lba) { + log.debug("Last usable LBA mismatch: {}, {}", .{ header.last_usable_lba, other.last_usable_lba }); + } + if (!header.disk_guid.eql(other.disk_guid)) { + log.debug("Disk GUID mismatch: {}, {}", .{ header.disk_guid, other.disk_guid }); + } + if (header.partition_array_lba != other.partition_array_lba) { + log.debug("Partition array LBA mismatch: {}, {}", .{ header.partition_array_lba, other.partition_array_lba }); + } + if (header.partition_entry_count != other.partition_entry_count) { + log.debug("Partition entry count mismatch: {}, {}", .{ header.partition_entry_count, other.partition_entry_count }); + } + if (header.partition_entry_size != other.partition_entry_size) { + log.debug("Partition entry size mismatch: {}, {}", .{ header.partition_entry_size, other.partition_entry_size }); + } + if (header.partition_array_crc32 != other.partition_array_crc32) { + log.debug("Partition array CRC32 mismatch: {}, {}", .{ header.partition_array_crc32, other.partition_array_crc32 }); + } + } + + pub const Cache = extern struct { + mbr: *MBR.Partition, + header: *GPT.Header, + disk: *Disk, + gpt: *GPT.Partition, + + pub fn getFreePartitionSlot(cache: Cache) !*GPT.Partition { + assert(cache.header.partition_entry_size == @sizeOf(GPT.Partition)); + // TODO: undo hack + + return cache.gpt; + + // for (cache.partition_entries[0..cache.header.partition_entry_count]) |*partition_entry| { + // if (partition_entry.first_lba == 0 and partition_entry.last_lba == 0) { + // return partition_entry; + // } + // } + + //@panic("todo: get_free_partition_slot"); + } + + pub fn getPartitionIndex(cache: Cache, partition: *GPT.Partition, partition_entries: []GPT.Partition) u32 { + assert(cache.header.partition_entry_size == @sizeOf(GPT.Partition)); + return @divExact(@as(u32, @intCast(@intFromPtr(partition) - @intFromPtr(partition_entries.ptr))), cache.header.partition_entry_size); + } + + pub fn getPartitionSector(cache: Cache, partition: *GPT.Partition, partition_entries: []GPT.Partition) u32 { + return getPartitionIndex(cache, partition, partition_entries) / cache.header.getPartititonCountInSector(cache.disk); + } + + pub fn getPartitionEntries(cache: Cache, allocator: ?*lib.Allocator) ![]GPT.Partition { + const partition_entries = try cache.disk.readSlice(GPT.Partition, cache.header.partition_entry_count, cache.header.partition_array_lba, allocator, .{}); + return partition_entries; + } + + pub inline fn updatePartitionEntry(cache: Cache, partition: *GPT.Partition, new_value: GPT.Partition) !void { + if (cache.disk.type != .memory) @panic("Disk is not memory"); + assert(cache.header.partition_entry_size == @sizeOf(GPT.Partition)); + const partition_entries = try cache.getPartitionEntries(null); + const partition_entry_bytes = lib.sliceAsBytes(partition_entries); + partition.* = new_value; + cache.header.partition_array_crc32 = CRC32.compute(partition_entry_bytes); + cache.header.updateCrc32(); + + const backup_gpt_header = try cache.disk.readTypedSectors(GPT.Header, cache.header.backup_lba, null, .{}); + backup_gpt_header.partition_array_crc32 = cache.header.partition_array_crc32; + backup_gpt_header.updateCrc32(); + + const partition_entry_sector_offset = cache.getPartitionSector(partition, partition_entries); + const partition_entry_byte_offset = partition_entry_sector_offset * cache.disk.sector_size; + // Only commit to disk the modified sector + const partition_entry_modified_sector_bytes = partition_entry_bytes[partition_entry_byte_offset .. partition_entry_byte_offset + cache.disk.sector_size]; + try cache.disk.writeSlice(u8, partition_entry_modified_sector_bytes, cache.header.partition_array_lba + partition_entry_sector_offset, false); + // Force write because for memory disk we only hold a pointer to the main partition entry array + try cache.disk.writeSlice(u8, partition_entry_modified_sector_bytes, backup_gpt_header.partition_array_lba + partition_entry_sector_offset, true); + try cache.disk.writeTypedSectors(GPT.Header, cache.header, cache.header.header_lba, false); + try cache.disk.writeTypedSectors(GPT.Header, backup_gpt_header, backup_gpt_header.header_lba, false); + } + + pub fn addPartition(cache: Cache, comptime filesystem: lib.Filesystem.Type, partition_name: []const u16, lba_start: u64, lba_end: u64, gpt_partition: ?*const GPT.Partition) !GPT.Partition.Cache { + // TODO: check if we are not overwriting a partition + // TODO: check filesystem specific stuff + const new_partition_entry = try cache.getFreePartitionSlot(); + try updatePartitionEntry(cache, new_partition_entry, GPT.Partition{ + .partition_type_guid = switch (filesystem) { + .fat32 => efi_guid, + else => @panic("unexpected filesystem"), + }, + .unique_partition_guid = if (gpt_partition) |gpt_part| gpt_part.unique_partition_guid else getRandomGuid(), + .first_lba = lba_start, + .last_lba = lba_end, + .attributes = .{}, + .partition_name = blk: { + var name = [1]u16{0} ** 36; + @memcpy(name[0..partition_name.len], partition_name); + break :blk name; + }, + }); + + return .{ + .gpt = cache, + .partition = new_partition_entry, + }; + } + + pub fn load(disk: *Disk, allocator: ?*Allocator) !GPT.Header.Cache { + _ = allocator; + _ = disk; + } + }; + + comptime { + assert(@sizeOf(Header) == lib.default_sector_size); + } + + pub fn get(disk: *Disk) !*GPT.Header { + return try disk.readTypedSectors(GPT.Header, 1); + } + + pub fn getBackup(gpt_header: *GPT.Header, disk: *Disk) !*GPT.Header { + return try disk.readTypedSectors(GPT.Header, gpt_header.backup_lba); + } +}; + +var prng = lib.random.DefaultPrng.init(0); +pub fn getRandomGuid() GUID { + const random_array = blk: { + var arr: [16]u8 = undefined; + const random = prng.random(); + random.bytes(&arr); + break :blk arr; + }; + var guid = GUID{ + .time_low = (@as(u32, random_array[0]) << 24) | (@as(u32, random_array[1]) << 16) | (@as(u32, random_array[2]) << 8) | random_array[3], + .time_mid = (@as(u16, random_array[4]) << 8) | random_array[5], + .time_high_and_version = (@as(u16, random_array[6]) << 8) | random_array[7], + .clock_seq_high_and_reserved = random_array[8], + .clock_seq_low = random_array[9], + .node = .{ random_array[10], random_array[11], random_array[12], random_array[13], random_array[14], random_array[15] }, + }; + + guid.clock_seq_high_and_reserved = (2 << 6) | (guid.clock_seq_high_and_reserved >> 2); + guid.time_high_and_version = (4 << 12) | (guid.time_high_and_version >> 4); + + return guid; +} + +pub const efi_system_partition_guid = GUID{ .time_low = 0xC12A7328, .time_mid = 0xF81F, .time_hi_and_version = 0x11D2, .clock_seq_hi_and_reserved = 0xBA, .clock_seq_low = 0x4B, .node = [_]u8{ 0x00, 0xA0, 0xC9, 0x3E, 0xC9, 0x3B } }; +pub const microsoft_basic_data_partition_guid = GUID{ .time_low = 0xEBD0A0A2, .time_mid = 0xB9E5, .time_hi_and_version = 0x4433, .clock_seq_hi_and_reserved = 0x87, .clock_seq_low = 0xC0, .node = [_]u8{ 0x68, 0xB6, 0xB7, 0x26, 0x99, 0xC7 } }; + +pub const Partition = extern struct { + partition_type_guid: GUID, + unique_partition_guid: GUID, + first_lba: u64, + last_lba: u64, + attributes: Attributes, + partition_name: [36]u16, + + pub const per_sector = @divExact(lib.default_sector_size, @sizeOf(Partition)); + + pub const Cache = extern struct { + gpt: GPT.Header.Cache, + partition: *GPT.Partition, + + pub fn fromPartitionIndex(disk: *Disk, partition_index: usize, allocator: ?*lib.Allocator) !GPT.Partition.Cache { + const mbr_lba = MBR.default_lba; + const mbr = try disk.readTypedSectors(MBR.Partition, mbr_lba, allocator, .{}); + const primary_gpt_header_lba = mbr_lba + 1; + const gpt_header = try disk.readTypedSectors(GPT.Header, primary_gpt_header_lba, allocator, .{}); + if (gpt_header.partition_entry_count == 0) @panic("No GPT partition entries"); + assert(gpt_header.partition_entry_size == @sizeOf(GPT.Partition)); + // TODO: undo hack + if (partition_index < gpt_header.partition_entry_count) { + if (partition_index != 0) @panic("Unsupported partition index"); + const partition_entries_first_sector = try disk.readSlice(GPT.Partition, GPT.Partition.per_sector, gpt_header.partition_array_lba, allocator, .{}); + const partition_entry = &partition_entries_first_sector[0]; + + return .{ + .gpt = .{ + .mbr = mbr, + .header = gpt_header, + .disk = disk, + .gpt = partition_entry, + }, + .partition = partition_entry, + }; + } + + @panic("todo: fromPartitionIndex"); + } + }; + + pub const Attributes = packed struct(u64) { + required_partition: bool = false, + no_block_io_protocol: bool = false, + legacy_bios_bootable: bool = false, + reserved: u45 = 0, + guid_reserved: u16 = 0, + }; + + pub fn compare(partition: *const Partition, other: *align(1) const Partition) void { + log.debug("{}", .{partition}); + if (partition.first_lba != other.first_lba) { + log.debug("First LBA mismatch: 0x{x}, 0x{x}", .{ partition.first_lba, other.first_lba }); + } + if (partition.last_lba != other.last_lba) { + log.debug("Last LBA mismatch: 0x{x}, 0x{x}", .{ partition.last_lba, other.last_lba }); + } + for (partition.partition_name, 0..) |partition_char, char_index| { + const other_char = other.partition_name[char_index]; + if (partition_char != other_char) { + log.debug("Char is different: {u}(0x{x}), {u}(0x{x})", .{ partition_char, partition_char, other_char, other_char }); + } + } + } + + pub fn format(partition: *const Partition, comptime _: []const u8, _: lib.FormatOptions, writer: anytype) @TypeOf(writer).Error!void { + try lib.format(writer, "GPT partition:\n", .{}); + try lib.format(writer, "\tPartition type GUID: {}\n", .{partition.partition_type_guid}); + try lib.format(writer, "\tUnique partition GUID: {}\n", .{partition.unique_partition_guid}); + try lib.format(writer, "\tFirst LBA: 0x{x}\n", .{partition.first_lba}); + try lib.format(writer, "\tLast LBA: 0x{x}\n", .{partition.last_lba}); + try lib.format(writer, "\tAttributes: {}\n", .{partition.attributes}); + try lib.format(writer, "\tPartition name: {}\n", .{lib.std.unicode.fmtUtf16le(&partition.partition_name)}); + } +}; + +pub fn create(disk: *Disk, copy_gpt_header: ?*const Header) !GPT.Header.Cache { + if (disk.type != .memory) @panic("gpt: creation is only supported for memory disks"); + // 1. Create MBR fake partition + const mbr_lba = MBR.default_lba; + const mbr = try disk.readTypedSectors(MBR.Partition, mbr_lba, null, .{}); + const first_lba = mbr_lba + 1; + const primary_header_lba = first_lba; + mbr.partitions[0] = MBR.LegacyPartition{ + .boot_indicator = 0, + .starting_chs = lib.default_sector_size, + .os_type = 0xee, + .ending_chs = 0xff_ff_ff, + .first_lba = first_lba, + .size_in_lba = @as(u32, @intCast(@divExact(disk.disk_size, disk.sector_size) - 1)), + }; + mbr.signature = .{ 0x55, 0xaa }; + try disk.writeTypedSectors(MBR.Partition, mbr, mbr_lba, false); + + // 2. Write GPT header + const partition_count = default_max_partition_count; + const partition_array_sector_count = @divExact(@sizeOf(Partition) * partition_count, disk.sector_size); + // TODO: properly compute header LBA + const gpt_header = try disk.readTypedSectors(GPT.Header, first_lba, null, .{}); + const secondary_header_lba = mbr.partitions[0].size_in_lba; + const partition_array_lba_start = first_lba + 1; + const partition_entries = try disk.readSlice(GPT.Partition, partition_count, partition_array_lba_start, null, .{}); + gpt_header.* = GPT.Header{ + .signature = "EFI PART".*, + .revision = .{ 0, 0, 1, 0 }, + .header_size = @offsetOf(GPT.Header, "reserved1"), + .header_crc32 = 0, // TODO + .header_lba = primary_header_lba, + .backup_lba = secondary_header_lba, + .first_usable_lba = partition_array_lba_start + partition_array_sector_count, + .last_usable_lba = secondary_header_lba - primary_header_lba - partition_array_sector_count, + .disk_guid = if (copy_gpt_header) |gpth| gpth.disk_guid else getRandomGuid(), + .partition_array_lba = partition_array_lba_start, + .partition_entry_count = partition_count, + .partition_array_crc32 = CRC32.compute(lib.sliceAsBytes(partition_entries)), + }; + + gpt_header.updateCrc32(); + try disk.writeTypedSectors(GPT.Header, gpt_header, primary_header_lba, false); + + var backup_gpt_header = gpt_header.*; + backup_gpt_header.partition_array_lba = secondary_header_lba - primary_header_lba - partition_array_sector_count + 1; + backup_gpt_header.header_lba = gpt_header.backup_lba; + backup_gpt_header.backup_lba = gpt_header.header_lba; + backup_gpt_header.updateCrc32(); + try disk.writeTypedSectors(GPT.Header, &backup_gpt_header, secondary_header_lba, true); + + return .{ + .mbr = mbr, + .header = gpt_header, + .disk = disk, + .gpt = &partition_entries[0], + }; +} + +const efi_guid = GUID{ + .time_low = 0xC12A7328, + .time_mid = 0xF81F, + .time_high_and_version = 0x11D2, + .clock_seq_high_and_reserved = 0xBA, + .clock_seq_low = 0x4B, + //00A0C93EC93B + .node = .{ 0x00, 0xa0, 0xc9, 0x3e, 0xc9, 0x3b }, +}; + +const limine_disk_guid = GUID{ + .time_low = 0xD2CB8A76, + .time_mid = 0xACB3, + .time_high_and_version = 0x4D4D, + .clock_seq_high_and_reserved = 0x93, + .clock_seq_low = 0x55, + .node = .{ 0xAC, 0xAE, 0xA4, 0x6B, 0x46, 0x92 }, +}; + +const limine_unique_partition_guid = GUID{ + .time_low = 0x26D6E02E, + .time_mid = 0xEED8, + .time_high_and_version = 0x4802, + .clock_seq_high_and_reserved = 0xba, + .clock_seq_low = 0xa2, + .node = .{ 0xE5, 0xAA, 0x43, 0x7F, 0xC2, 0xC5 }, +}; + +const FilesystemCacheTypes = blk: { + var types: [Filesystem.Type.count]type = undefined; + types[@intFromEnum(Filesystem.Type.rise)] = void; + types[@intFromEnum(Filesystem.Type.ext2)] = void; + types[@intFromEnum(Filesystem.Type.fat32)] = FAT32.Cache; + + break :blk types; +}; + +test "gpt size" { + comptime { + assert(@sizeOf(Header) == 0x200); + } +} diff --git a/src/lib/partition_table/mbr.zig b/src/lib/partition_table/mbr.zig new file mode 100644 index 0000000..d97e49e --- /dev/null +++ b/src/lib/partition_table/mbr.zig @@ -0,0 +1,357 @@ +const MBR = @This(); + +const lib = @import("lib"); +const assert = lib.assert; +const log = lib.log.scoped(.MBR); +const Disk = lib.Disk.Descriptor; +const GPT = lib.PartitionTable.GPT; +const FAT32 = lib.Filesystem.FAT32; + +pub const default_lba = 0; + +pub const BIOSParameterBlock = extern struct { + pub const DOS2_0 = extern struct { + jmp_code: [3]u8 = .{ 0xeb, 0x58, 0x90 }, + oem_identifier: [8]u8, + sector_size: u16 align(1), + cluster_sector_count: u8, + reserved_sector_count: u16, + fat_count: u8, + root_entry_count: u16 align(1), // only for FAT12 and FAT16 + total_sector_count_16: u16 align(1), + media_descriptor: u8, + fat_sector_count_16: u16, + + comptime { + assert(@sizeOf(@This()) == 24); + } + + fn compare(bpb_2_0: *const DOS2_0, other: *align(1) const DOS2_0) void { + if (!lib.equal(u8, &bpb_2_0.jmp_code, &other.jmp_code)) log.debug("Jump code differs: {any}, {any}", .{ bpb_2_0.jmp_code, other.jmp_code }); + if (!lib.equal(u8, &bpb_2_0.oem_identifier, &other.oem_identifier)) log.debug("OEM identifier differs: {any}, {any}", .{ bpb_2_0.oem_identifier, other.oem_identifier }); + if (bpb_2_0.sector_size != other.sector_size) log.debug("Sector size differs: {}, {}", .{ bpb_2_0.sector_size, other.sector_size }); + if (bpb_2_0.cluster_sector_count != other.cluster_sector_count) log.debug("Cluster sector count differs: {}, {}", .{ bpb_2_0.cluster_sector_count, other.cluster_sector_count }); + if (bpb_2_0.reserved_sector_count != other.reserved_sector_count) log.debug("Reserved sector count differs: {}, {}", .{ bpb_2_0.reserved_sector_count, other.reserved_sector_count }); + if (bpb_2_0.fat_count != other.fat_count) log.debug("FAT count differs: {}, {}", .{ bpb_2_0.fat_count, other.fat_count }); + if (bpb_2_0.root_entry_count != other.root_entry_count) log.debug("Root entry count differs: {}, {}", .{ bpb_2_0.root_entry_count, other.root_entry_count }); + if (bpb_2_0.total_sector_count_16 != other.total_sector_count_16) log.debug("Total sector count(16) differs: {}, {}", .{ bpb_2_0.total_sector_count_16, other.total_sector_count_16 }); + if (bpb_2_0.media_descriptor != other.media_descriptor) log.debug("Media descriptor differs: {}, {}", .{ bpb_2_0.media_descriptor, other.media_descriptor }); + if (bpb_2_0.fat_sector_count_16 != other.fat_sector_count_16) log.debug("FAT sector count (16) differs: {}, {}", .{ bpb_2_0.fat_sector_count_16, other.fat_sector_count_16 }); + } + }; + + pub const DOS3_31 = extern struct { + dos2_0: DOS2_0, + physical_sectors_per_track: u16, + disk_head_count: u16, + hidden_sector_count: u32, + total_sector_count_32: u32, + + fn compare(bpb_3_31: *align(1) const DOS3_31, other: *align(1) const DOS3_31) void { + bpb_3_31.dos2_0.compare(&other.dos2_0); + + if (bpb_3_31.physical_sectors_per_track != other.physical_sectors_per_track) log.debug("Physical sectors per track differs: {}, {}", .{ bpb_3_31.physical_sectors_per_track, other.physical_sectors_per_track }); + if (bpb_3_31.disk_head_count != other.disk_head_count) log.debug("Disk head count differs: {}, {}", .{ bpb_3_31.disk_head_count, other.disk_head_count }); + if (bpb_3_31.hidden_sector_count != other.hidden_sector_count) log.debug("Hidden sector count differs: {}, {}", .{ bpb_3_31.hidden_sector_count, other.hidden_sector_count }); + if (bpb_3_31.total_sector_count_32 != other.total_sector_count_32) log.debug("Total sector count differs: {}, {}", .{ bpb_3_31.total_sector_count_32, other.total_sector_count_32 }); + } + + comptime { + assert(@sizeOf(@This()) == 36); + } + }; + + pub const DOS7_1_79 = extern struct { + dos3_31: DOS3_31 align(1), + fat_sector_count_32: u32 align(1), + drive_description: u16 align(1), + version: [2]u8 align(1), + root_directory_cluster_offset: u32 align(1), + fs_info_sector: u16 align(1), + backup_boot_record_sector: u16 align(1), + reserved: [12]u8 = [1]u8{0} ** 12, + drive_number: u8, + reserved1: u8 = 0, + extended_boot_signature: u8, + serial_number: u32 align(1), + volume_label: [11]u8, + filesystem_type: [8]u8, + + pub fn get_free_cluster_count(bpb: *const DOS7_1_79) u32 { + const total_sector_count = bpb.dos3_31.total_sector_count_32; + const reserved_sector_count = bpb.dos3_31.dos2_0.reserved_sector_count; + const sector_count_per_fat = bpb.fat_sector_count_32; + const fat_count = bpb.dos3_31.dos2_0.fat_count; + return total_sector_count - reserved_sector_count - (sector_count_per_fat * fat_count); + } + + fn compare(this: *const DOS7_1_79, other: *align(1) const DOS7_1_79) void { + this.dos3_31.compare(&other.dos3_31); + + if (this.fat_sector_count_32 != other.fat_sector_count_32) log.debug("FAT sector count (32) differs: {}, {}", .{ this.fat_sector_count_32, other.fat_sector_count_32 }); + if (this.drive_description != other.drive_description) log.debug("Drive description differs: {}, {}", .{ this.drive_description, other.drive_description }); + if (!lib.equal(u8, &this.version, &other.version)) log.debug("Version differs: {any}, {any}", .{ this.version, other.version }); + if (this.root_directory_cluster_offset != other.root_directory_cluster_offset) log.debug("Root directory cluster differs: {}, {}", .{ this.root_directory_cluster_offset, other.root_directory_cluster_offset }); + if (this.fs_info_sector != other.fs_info_sector) log.debug("FS info differs: {}, {}", .{ this.fs_info_sector, other.fs_info_sector }); + if (this.backup_boot_record_sector != other.backup_boot_record_sector) log.debug("Backup boot record sector differs: {}, {}", .{ this.backup_boot_record_sector, other.backup_boot_record_sector }); + if (this.drive_number != other.drive_number) log.debug("Drive number differs: {}, {}", .{ this.drive_number, other.drive_number }); + if (this.extended_boot_signature != other.extended_boot_signature) log.debug("Extended boot signature differs: {}, {}", .{ this.extended_boot_signature, other.extended_boot_signature }); + if (this.serial_number != other.serial_number) log.debug("Serial number differs: 0x{x}, 0x{x}", .{ this.serial_number, other.serial_number }); + if (!lib.equal(u8, &this.volume_label, &other.volume_label)) log.debug("Volume label differs: {s}, {s}", .{ this.volume_label, other.volume_label }); + if (!lib.equal(u8, &this.filesystem_type, &other.filesystem_type)) log.debug("Filesystem type differs: {s}, {s}", .{ this.filesystem_type, other.filesystem_type }); + } + + comptime { + assert(@sizeOf(@This()) == 90); + } + }; +}; + +pub const LegacyPartition = packed struct(u128) { + boot_indicator: u8, + starting_chs: u24, + os_type: u8, + ending_chs: u24, + first_lba: u32, + size_in_lba: u32, + + comptime { + assert(@sizeOf(@This()) == 0x10); + } +}; + +pub const VerificationError = error{ + jmp_code, + sector_size, + cluster_sector_count, + reserved_sector_count, + fat_count, + root_entry_count, + total_sector_count_16, + media_type, + fat_sector_count_16, + hidden_sector_count, + total_sector_count_32, + fat_sector_count_32, + fat_version, + root_directory_cluster_offset, + fs_info_sector, + backup_boot_record_sector, + filesystem_type, +}; + +pub const Partition = extern struct { + bpb: BIOSParameterBlock.DOS7_1_79, + code: [356]u8, + partitions: [4]LegacyPartition align(2), + signature: [2]u8 = [_]u8{ 0x55, 0xaa }, + + comptime { + assert(@sizeOf(@This()) == lib.default_sector_size); + } + + pub fn compare(mbr: *Partition, other: *MBR.Partition) void { + log.debug("Comparing MBRs...", .{}); + mbr.bpb.compare(&other.bpb); + + if (!lib.equal(u8, &mbr.code, &other.code)) { + @panic("mbr: code does not match"); + } + + for (mbr.partitions, 0..) |this_partition, partition_i| { + const other_partition = other.partitions[partition_i]; + + if (this_partition.boot_indicator != other_partition.boot_indicator) log.debug("Mismatch: {}, .{}", .{ this_partition.boot_indicator, other_partition.boot_indicator }); + if (this_partition.starting_chs != other_partition.starting_chs) log.debug("Mismatch: {}, .{}", .{ this_partition.starting_chs, other_partition.starting_chs }); + if (this_partition.os_type != other_partition.os_type) log.debug("Mismatch: {}, .{}", .{ this_partition.os_type, other_partition.os_type }); + if (this_partition.ending_chs != other_partition.ending_chs) log.debug("Mismatch: {}, .{}", .{ this_partition.ending_chs, other_partition.ending_chs }); + if (this_partition.first_lba != other_partition.first_lba) log.debug("Mismatch: {}, .{}", .{ this_partition.first_lba, other_partition.first_lba }); + if (this_partition.size_in_lba != other_partition.size_in_lba) log.debug("Mismatch: {}, .{}", .{ this_partition.size_in_lba, other_partition.size_in_lba }); + } + } + + pub fn verify(mbr: *const MBR.Partition, disk: *Disk) VerificationError!void { + const bpb_2_0 = mbr.bpb.dos3_31.dos2_0; + const jmp_code = bpb_2_0.jmp_code; + const is_allowed_jmp_code = (jmp_code[0] == 0xeb and jmp_code[2] == 0x90) or jmp_code[0] == 0xe9; + log.debug("Checking jump code: [0x{x}, 0x{x}, 0x{x}]", .{ jmp_code[0], jmp_code[1], jmp_code[2] }); + if (!is_allowed_jmp_code) { + return VerificationError.jmp_code; + } + + const sector_size = bpb_2_0.sector_size; + log.debug("Checking sector size: 0x{x}", .{sector_size}); + if (sector_size != lib.default_sector_size) { + log.warn("Sector size different than 0x{x}: 0x{x}", .{ lib.default_sector_size, sector_size }); + return VerificationError.sector_size; + } + + if (sector_size != lib.default_sector_size and sector_size != 0x400 and sector_size != 0x800 and sector_size != 0x1000) { + return VerificationError.sector_size; + } + + const cluster_sector_count = bpb_2_0.cluster_sector_count; + log.debug("Checking cluster sector count: {}", .{cluster_sector_count}); + if (cluster_sector_count != 1 and cluster_sector_count != 2 and cluster_sector_count != 4 and cluster_sector_count != 8 and cluster_sector_count != 16 and cluster_sector_count != 32 and cluster_sector_count != 64 and cluster_sector_count != 128) { + return VerificationError.cluster_sector_count; + } + + const reserved_sector_count = bpb_2_0.reserved_sector_count; + log.debug("Checking reserved sector count: {}", .{cluster_sector_count}); + // TODO: 32 is recommended, not mandatory + if (reserved_sector_count != 32) { + return VerificationError.cluster_sector_count; + } + + const fat_count = bpb_2_0.fat_count; + log.debug("Checking FAT count: {}", .{fat_count}); + if (fat_count != 2) { + return VerificationError.fat_count; + } + + const root_entry_count = bpb_2_0.root_entry_count; + log.debug("Checking root entry count: {}", .{root_entry_count}); + if (root_entry_count != 0) { + return VerificationError.root_entry_count; + } + + const total_sector_count_16 = bpb_2_0.total_sector_count_16; + log.debug("Checking total sector count (16): {}", .{total_sector_count_16}); + if (total_sector_count_16 != 0) { + return VerificationError.total_sector_count_16; + } + + const media_type = bpb_2_0.media_descriptor; + log.debug("Checking media type: 0x{x}", .{media_type}); + if (media_type != 0xf8 and media_type != 0xf0) { + log.warn("Not a standard media type: 0x{x}", .{media_type}); + } + + if (media_type != 0xf0 and media_type != 0xf8 and media_type != 0xf9 and media_type != 0xfa and media_type != 0xfb and media_type != 0xfc and media_type != 0xfd and media_type != 0xfe and media_type != 0xff) { + return VerificationError.media_type; + } + + const fat_sector_count_16 = bpb_2_0.fat_sector_count_16; + log.debug("Checking FAT sector count (16): {}", .{fat_sector_count_16}); + if (fat_sector_count_16 != 0) { + return VerificationError.fat_sector_count_16; + } + + const bpb_3_31 = mbr.bpb.dos3_31; + + const hidden_sector_count = bpb_3_31.hidden_sector_count; + log.debug("Checking hidden sector count: {}", .{hidden_sector_count}); + if (hidden_sector_count != 0) { + return VerificationError.hidden_sector_count; + } + + const total_sector_count_32 = bpb_3_31.total_sector_count_32; + log.debug("Checking total sector count (32): {}", .{total_sector_count_32}); + if (total_sector_count_32 != @divExact(disk.disk_size, disk.sector_size)) { + return VerificationError.total_sector_count_32; + } + + const fat_sector_count_32 = mbr.bpb.fat_sector_count_32; + log.debug("Checking FAT sector count (32): {}", .{fat_sector_count_32}); + if (fat_sector_count_32 == 0) { + return VerificationError.fat_sector_count_32; + } + + const fat_version = mbr.bpb.version; + log.debug("Checking FAT version: {}.{}", .{ fat_version[0], fat_version[1] }); + if (fat_version[0] != 0 or fat_version[1] != 0) { + return VerificationError.fat_version; + } + + const root_directory_cluster_offset = mbr.bpb.root_directory_cluster_offset; + log.debug("Checking root directory cluster offset: {}", .{root_directory_cluster_offset}); + if (root_directory_cluster_offset != 2) { + return VerificationError.root_directory_cluster_offset; + } + + const fs_info_sector = mbr.bpb.fs_info_sector; + log.debug("Checking FSInfo sector: {}", .{fs_info_sector}); + if (fs_info_sector != 1) { + return VerificationError.fs_info_sector; + } + + const backup_boot_record_sector = mbr.bpb.backup_boot_record_sector; + log.debug("Checking backup boot record sector: {}", .{backup_boot_record_sector}); + if (backup_boot_record_sector != 6) { + return VerificationError.backup_boot_record_sector; + } + + const filesystem_type = mbr.bpb.filesystem_type; + log.debug("Checking filesystem type...", .{}); + if (!lib.equal(u8, &filesystem_type, "FAT32 ")) { + return VerificationError.filesystem_type; + } + + @panic("mbr: unexpected verification error"); + } + + pub fn format(mbr: *const MBR.Partition, comptime _: []const u8, _: lib.FormatOptions, writer: anytype) @TypeOf(writer).Error!void { + try lib.format(writer, "MBR:\n", .{}); + const bpb_2_0 = mbr.bpb.dos3_31.dos2_0; + try lib.format(writer, "\tJump code: [0x{x}, 0x{x}, 0x{x}]\n", .{ bpb_2_0.jmp_code[0], bpb_2_0.jmp_code[1], bpb_2_0.jmp_code[2] }); + try lib.format(writer, "\tOEM identifier: {s}\n", .{bpb_2_0.oem_identifier}); + try lib.format(writer, "\tSector size: {}\n", .{bpb_2_0.sector_size}); + try lib.format(writer, "\tCluster sector count: {}\n", .{bpb_2_0.cluster_sector_count}); + try lib.format(writer, "\tReserved sector count: {}\n", .{bpb_2_0.reserved_sector_count}); + try lib.format(writer, "\tFAT count: {}\n", .{bpb_2_0.fat_count}); + try lib.format(writer, "\tRoot entry count: {}\n", .{bpb_2_0.root_entry_count}); + try lib.format(writer, "\tTotal sector count(16): {}\n", .{bpb_2_0.total_sector_count_16}); + try lib.format(writer, "\tMedia descriptor: {}\n", .{bpb_2_0.media_descriptor}); + try lib.format(writer, "\tFAT sector count (16): {}\n", .{bpb_2_0.fat_sector_count_16}); + + const bpb_3_31 = mbr.bpb.dos3_31; + try lib.format(writer, "\tPhysical sectors per track: {}\n", .{bpb_3_31.physical_sectors_per_track}); + try lib.format(writer, "\tDisk head count: {}\n", .{bpb_3_31.disk_head_count}); + try lib.format(writer, "\tHidden sector count: {}\n", .{bpb_3_31.hidden_sector_count}); + try lib.format(writer, "\tTotal sector count: {}\n", .{bpb_3_31.total_sector_count_32}); + + const bpb_7_1_79 = mbr.bpb; + + try lib.format(writer, "\tFAT sector count (32): {}\n", .{bpb_7_1_79.fat_sector_count_32}); + try lib.format(writer, "\tDrive description: {}\n", .{bpb_7_1_79.drive_description}); + try lib.format(writer, "\tVersion: {}.{}\n", .{ bpb_7_1_79.version[0], bpb_7_1_79.version[1] }); + try lib.format(writer, "\tRoot directory cluster offset: {}\n", .{bpb_7_1_79.root_directory_cluster_offset}); + try lib.format(writer, "\tFS info sector: {}\n", .{bpb_7_1_79.fs_info_sector}); + try lib.format(writer, "\tBackup boot record sector: {}\n", .{bpb_7_1_79.backup_boot_record_sector}); + try lib.format(writer, "\tDrive number: {}\n", .{bpb_7_1_79.drive_number}); + try lib.format(writer, "\tExtended boot signature: {}\n", .{bpb_7_1_79.extended_boot_signature}); + try lib.format(writer, "\tSerial number: {}\n", .{bpb_7_1_79.serial_number}); + try lib.format(writer, "\tVolume label: {s}\n", .{bpb_7_1_79.volume_label}); + try lib.format(writer, "\tFilesystem type: {s}\n", .{bpb_7_1_79.filesystem_type}); + + try lib.format(writer, "\nCode:\n", .{}); + for (mbr.code) |code_byte| { + try lib.format(writer, "0x{x}, ", .{code_byte}); + } + + try lib.format(writer, "\n\nPartitions:\n", .{}); + for (mbr.partitions, 0..) |partition, partition_index| { + if (partition.size_in_lba != 0) { + try lib.format(writer, "[{}]\n", .{partition_index}); + try lib.format(writer, "\tBoot indicator: 0x{x}\n", .{partition.boot_indicator}); + try lib.format(writer, "\tStarting CHS: 0x{x}\n", .{partition.starting_chs}); + try lib.format(writer, "\tOS type: 0x{x}\n", .{partition.os_type}); + try lib.format(writer, "\tEnding CHS: 0x{x}\n", .{partition.ending_chs}); + try lib.format(writer, "\tFirst LBA: 0x{x}\n", .{partition.first_lba}); + try lib.format(writer, "\tSize in LBA: 0x{x}\n", .{partition.size_in_lba}); + } + } + } +}; + +pub const DAP = extern struct { + size: u16 = 0x10, + sector_count: u16, + offset: u16, + segment: u16, + lba: u64, + + comptime { + assert(@sizeOf(DAP) == 0x10); + } +}; diff --git a/src/lib/psf1.zig b/src/lib/psf1.zig new file mode 100644 index 0000000..005a27d --- /dev/null +++ b/src/lib/psf1.zig @@ -0,0 +1,13 @@ +const lib = @import("lib"); + +pub const Header = extern struct { + magic: [2]u8, + mode: u8, + character_size: u8, + + pub const magic = .{ 0x36, 0x04 }; +}; + +pub const Error = error{ + invalid_magic, +}; diff --git a/src/privileged.zig b/src/privileged.zig new file mode 100644 index 0000000..b73372e --- /dev/null +++ b/src/privileged.zig @@ -0,0 +1,114 @@ +// This package provides of privileged data structures and routines to both kernel and bootloaders, for now + +const lib = @import("lib"); +// const PhysicalAddress = lib.PhysicalAddress; +// const VirtualAddress = lib.VirtualAddress; +// const PhysicalMemoryRegion = lib.PhysicalMemoryRegion; +// const VirtualMemoryRegion = lib.VirtualMemoryRegion; + +const assert = lib.assert; +const log = lib.log; +const maxInt = lib.maxInt; +const Allocator = lib.Allocator; + +const bootloader = @import("bootloader"); + +pub const ACPI = @import("privileged/acpi.zig"); +pub const arch = @import("privileged/arch.zig"); + +pub const writer = E9Writer{ .context = {} }; + +pub const E9WriterError = error{}; +pub const E9Writer = lib.Writer(void, E9WriterError, writeToE9); + +fn writeToE9(_: void, bytes: []const u8) E9WriterError!usize { + return arch.io.writeBytes(0xe9, bytes); +} + +pub const default_stack_size = 0x4000; + +pub const ResourceOwner = enum(u2) { + bootloader = 0, + kernel = 1, + user = 2, +}; + +const panic_logger = lib.log.scoped(.PANIC); + +inline fn exitFromQEMU(exit_code: lib.QEMU.ExitCode) noreturn { + comptime assert(@sizeOf(lib.QEMU.ExitCode) == @sizeOf(u32)); + arch.io.write(u32, lib.QEMU.isa_debug_exit.io_base, @intFromEnum(exit_code)); + + arch.stopCPU(); +} + +pub inline fn shutdown(exit_code: lib.QEMU.ExitCode) noreturn { + if (lib.is_test) { + exitFromQEMU(exit_code); + } else { + arch.stopCPU(); + } +} + +pub const Mapping = extern struct { + physical: lib.PhysicalAddress = lib.PhysicalAddress.invalid(), + virtual: lib.VirtualAddress = .null, + size: u64 = 0, + flags: Flags = .{}, + reserved: u32 = 0, + + pub const Flags = packed struct(u32) { + write: bool = false, + cache_disable: bool = false, + global: bool = false, + execute: bool = false, + user: bool = false, + secret: bool = false, + reserved: u26 = 0, + + pub inline fn empty() Flags { + return .{}; + } + + pub inline fn toArchitectureSpecific(flags: Flags) arch.paging.MemoryFlags { + return arch.paging.newFlags(flags); + } + }; +}; + +pub const PageAllocator = struct { + allocate: *const fn (context: ?*anyopaque, size: u64, alignment: u64, allocate_options: AllocateOptions) Allocator.Allocate.Error!lib.PhysicalMemoryRegion, + context: ?*anyopaque, + context_type: ContextType, + reserved: u32 = 0, + + pub const AllocatePageTablesOptions = packed struct { + count: u16 = 1, + level: arch.paging.Level, + user: bool, + }; + + pub inline fn allocatePageTable(page_allocator: PageAllocator, options: AllocatePageTablesOptions) !lib.PhysicalMemoryRegion { + const result = try page_allocator.allocate(page_allocator.context, arch.paging.page_table_size, arch.paging.page_table_alignment, .{ + .count = options.count, + .level = options.level, + .level_valid = true, + .user = options.user, + }); + return result; + } + + pub const AllocateOptions = packed struct { + count: u16 = 1, + space_waste_allowed_to_guarantee_alignment: u8 = 0, + level: arch.paging.Level = undefined, + level_valid: bool = false, + user: bool = false, + }; + + const ContextType = enum(u32) { + invalid = 0, + bootloader = 1, + cpu = 2, + }; +}; diff --git a/src/privileged/acpi.zig b/src/privileged/acpi.zig new file mode 100644 index 0000000..0a78ae4 --- /dev/null +++ b/src/privileged/acpi.zig @@ -0,0 +1,247 @@ +const lib = @import("lib"); +const assert = lib.assert; +const log = lib.log; + +pub const RSDP = extern struct { + pub const Descriptor1 = extern struct { + signature: [8]u8, + checksum: u8, + OEM_ID: [6]u8, + revision: u8, + RSDT_address: u32, + + comptime { + assert(@sizeOf(Descriptor1) == 20); + } + + const RSDPError = error{ + version_corrupted, + table_not_found, + xsdt_32_bit, + }; + + pub fn findTable(rsdp: *RSDP.Descriptor1, table_signature: Signature) !*align(1) const Header { + switch (switch (rsdp.revision) { + 0 => false, + 2 => true, + else => return RSDPError.version_corrupted, + }) { + inline else => |is_xsdt| { + if (is_xsdt and lib.cpu.arch == .x86) return RSDPError.xsdt_32_bit; + + const root_table_address = switch (is_xsdt) { + false => rsdp.RSDT_address, + true => @fieldParentPtr(RSDP.Descriptor2, "descriptor1", rsdp).XSDT_address, + }; + + const root_table_header = @as(*align(1) Header, @ptrFromInt(root_table_address)); + const EntryType = switch (is_xsdt) { + false => u32, + true => u64, + }; + + const entry_count = @divExact(root_table_header.length - @sizeOf(Header), @sizeOf(EntryType)); + const entries = @as([*]align(1) const EntryType, @ptrFromInt(@intFromPtr(root_table_header) + @sizeOf(Header)))[0..entry_count]; + for (entries) |entry| { + const table_header = @as(*align(1) const Header, @ptrFromInt(entry)); + if (table_signature == table_header.signature) { + return table_header; + } + } + + return RSDPError.table_not_found; + }, + } + } + }; + + pub const Descriptor2 = extern struct { + descriptor1: Descriptor1, + length: u32, + XSDT_address: u64 align(4), + cheksum: u8, + reserved: [3]u8, + + comptime { + assert(@alignOf(Descriptor1) == 4); + assert(@alignOf(Descriptor2) == 4); + assert(@sizeOf(Descriptor2) == 36); + } + }; +}; + +const Signature = enum(u32) { + APIC = @as(*align(1) const u32, @ptrCast("APIC")).*, + FACP = @as(*align(1) const u32, @ptrCast("FACP")).*, + HPET = @as(*align(1) const u32, @ptrCast("HPET")).*, + MCFG = @as(*align(1) const u32, @ptrCast("MCFG")).*, + WAET = @as(*align(1) const u32, @ptrCast("WAET")).*, + BGRT = @as(*align(1) const u32, @ptrCast("BGRT")).*, + _, +}; + +pub const Header = extern struct { + signature: Signature, + length: u32, + revision: u8, + checksum: u8, + OEM_ID: [6]u8, + OEM_table_ID: [8]u8, + OEM_revision: u32, + creator_ID: u32, + creator_revision: u32, + + comptime { + assert(@sizeOf(@This()) == 0x24); + } +}; + +pub const MADT = extern struct { + header: Header, + LAPIC_address: u32, + flags: MADTFlags, + + pub const MADTFlags = packed struct(u32) { + pcat_compatibility: bool, + reserved: u31 = 0, + }; + + comptime { + assert(@sizeOf(@This()) == 0x2c); + } + + pub fn getIterator(madt: *align(1) const MADT) Iterator { + return .{ + .madt = madt, + }; + } + + pub fn getCPUCount(madt: *align(1) const MADT) u32 { + var cpu_count: u32 = 0; + var iterator = madt.getIterator(); + while (iterator.next()) |entry| { + cpu_count += switch (entry.type) { + .LAPIC => blk: { + const lapic_entry = @fieldParentPtr(LAPIC, "record", entry); + break :blk @intFromBool((lapic_entry.flags.enabled and !lapic_entry.flags.online_capable) or (lapic_entry.flags.online_capable and !lapic_entry.flags.enabled)); + }, + .x2APIC => @panic("x2apic not implemented"), + else => continue, + }; + } + + return cpu_count; + } + + pub const Record = extern struct { + type: Type, + length: u8, + + const Type = enum(u8) { + LAPIC = 0, + IO_APIC = 1, + ISO = 2, + NMI_source = 3, + LAPIC_NMI = 4, + LAPIC_address_override = 5, + IO_SAPIC = 6, + LSAPIC = 7, + platform_interrupt_sources = 8, + x2APIC = 9, + x2APIC_NMI = 0xa, + GIC_CPU_interface = 0xb, + GIC_distributor = 0xc, + GIC_MSI_frame = 0xd, + GIC_redistributor = 0xe, + GIC_interrupt_translation_service = 0xf, + }; + }; + + pub const Iterator = extern struct { + madt: *align(1) const MADT, + index: usize = 0, + offset: usize = @sizeOf(MADT), + + pub fn next(iterator: *Iterator) ?*const Record { + if (iterator.offset < iterator.madt.header.length) { + const record = @as(*const Record, @ptrFromInt(@intFromPtr(iterator.madt) + iterator.offset)); + iterator.offset += record.length; + return record; + } + + return null; + } + }; + + pub const LAPIC = extern struct { + record: Record, + ACPI_processor_UID: u8, + APIC_ID: u8, + flags: Flags, + + const Flags = packed struct(u32) { + enabled: bool, + online_capable: bool, + reserved: u30 = 0, + }; + }; + + const IO_APIC = extern struct { + record: Record, + IO_APIC_ID: u8, + reserved: u8, + IO_APIC_address: u32, + global_system_interrupt_base: u32, + + comptime { + assert(@sizeOf(@This()) == @sizeOf(u64) + @sizeOf(u32)); + } + }; + + const InterruptSourceOverride = extern struct { + record: Record, + bus: u8, + source: u8, + global_system_interrupt: u32 align(2), + flags: u16 align(2), + + comptime { + assert(@sizeOf(@This()) == @sizeOf(u64) + @sizeOf(u16)); + } + }; + + const LAPIC_NMI = extern struct { + record: Record, + ACPI_processor_UID: u8, + flags: u16 align(1), + LAPIC_lint: u8, + + comptime { + assert(@sizeOf(@This()) == @sizeOf(u32) + @sizeOf(u16)); + } + }; +}; + +const MCFG = extern struct { + header: Header, + reserved: u64, + + fn getConfigurations(mcfg: *align(1) MCFG) []Configuration { + const entry_count = (mcfg.header.length - @sizeOf(MCFG)) / @sizeOf(Configuration); + const configuration_base = @intFromPtr(mcfg) + @sizeOf(MCFG); + return @as([*]Configuration, @ptrFromInt(configuration_base))[0..entry_count]; + } + + comptime { + assert(@sizeOf(MCFG) == @sizeOf(Header) + @sizeOf(u64)); + assert(@sizeOf(Configuration) == 0x10); + } + + const Configuration = extern struct { + base_address: u64, + segment_group_number: u16, + start_bus: u8, + end_bus: u8, + reserved: u32, + }; +}; diff --git a/src/privileged/arch.zig b/src/privileged/arch.zig new file mode 100644 index 0000000..fee6004 --- /dev/null +++ b/src/privileged/arch.zig @@ -0,0 +1,22 @@ +const lib = @import("lib"); +const privileged = @import("privileged"); + +pub const x86 = @import("arch/x86.zig"); +pub const x86_64 = @import("arch/x86_64.zig"); + +pub const current = switch (lib.cpu.arch) { + .x86 => x86, + .x86_64 => x86_64, + else => @compileError("Architecture not supported"), +}; + +pub const CPUPageTables = current.paging.CPUPageTables; +pub const stopCPU = current.stopCPU; +pub const paging = current.paging; +pub const Registers = current.Registers; +pub const disableInterrupts = current.disableInterrupts; + +pub const dispatch_count = current.dispatch_count; +pub var max_physical_address_bit: u6 = 40; + +pub const io = current.io; diff --git a/src/privileged/arch/x86.zig b/src/privileged/arch/x86.zig new file mode 100644 index 0000000..9b8587c --- /dev/null +++ b/src/privileged/arch/x86.zig @@ -0,0 +1,11 @@ +const lib = @import("lib"); +const assert = lib.assert; +const privileged = @import("privileged"); + +const x86 = @import("x86/common.zig"); +pub usingnamespace x86; + +pub const io = @import("x86/32/io.zig"); + +/// Use x86_64 paging for VirtualAddressSpace +pub const paging = privileged.arch.x86_64.paging; diff --git a/src/privileged/arch/x86/32/io.zig b/src/privileged/arch/x86/32/io.zig new file mode 100644 index 0000000..3d4aa08 --- /dev/null +++ b/src/privileged/arch/x86/32/io.zig @@ -0,0 +1,14 @@ +const privileged = @import("privileged"); +pub inline fn writeBytes(port: u16, bytes: []const u8) usize { + const bytes_left = asm volatile ( + \\rep outsb + : [ret] "={ecx}" (-> usize), + : [dest] "{dx}" (port), + [src] "{esi}" (bytes.ptr), + [len] "{ecx}" (bytes.len), + : "esi", "ecx" + ); + return bytes.len - bytes_left; +} +pub const read = privileged.arch.x86.read; +pub const write = privileged.arch.x86.write; diff --git a/src/privileged/arch/x86/64/apic.zig b/src/privileged/arch/x86/64/apic.zig new file mode 100644 index 0000000..f47e4a9 --- /dev/null +++ b/src/privileged/arch/x86/64/apic.zig @@ -0,0 +1,148 @@ +const APIC = @This(); + +const lib = @import("lib"); +const assert = lib.assert; +const log = lib.log.scoped(.APIC); +const cpuid = lib.arch.x86_64.cpuid; +const maxInt = lib.maxInt; + +const privileged = @import("privileged"); +const VirtualAddress = privileged.arch.VirtualAddress; + +const arch = privileged.arch; +const x86_64 = privileged.arch.x86_64; +const IA32_APIC_BASE = x86_64.registers.IA32_APIC_BASE; +const io = x86_64.io; + +const ID = packed struct(u32) { + reserved: u24, + apic_id: u8, + + pub inline fn read() ID { + return APIC.read(.id); + } +}; + +pub const TaskPriorityRegister = packed struct(u32) { + subclass: u4 = 0, + class: u4 = 0, + reserved: u24 = 0, + + pub inline fn write(tpr: TaskPriorityRegister) void { + APIC.write(.tpr, @as(u32, @bitCast(tpr))); + } +}; + +pub const LVTTimer = packed struct(u32) { + vector: u8 = 0xfa, + reserved: u4 = 0, + delivery_status: bool = false, + reserved1: u3 = 0, + mask: bool = true, + mode: Mode = .oneshot, + reserved2: u13 = 0, + + const Mode = enum(u2) { + oneshot = 0, + periodic = 1, + tsc_deadline = 2, + }; + + pub inline fn write(timer: LVTTimer) void { + APIC.write(.lvt_timer, @as(u32, @bitCast(timer))); + } +}; + +const DivideConfigurationRegister = packed struct(u32) { + divide: Divide = .by_1, + reserved1: u28 = 0, + + // Divide[bit 2] is always 0 + const Divide = enum(u4) { + by_2 = 0b0000, + by_4 = 0b0001, + by_8 = 0b0010, + by_16 = 0b0011, + by_32 = 0b1000, + by_64 = 0b1001, + by_128 = 0b1010, + by_1 = 0b1011, + }; + + inline fn read() DivideConfigurationRegister { + return APIC.read(.timer_div); + } + + inline fn write(dcr: DivideConfigurationRegister) void { + APIC.write(.timer_div, @as(u32, @bitCast(dcr))); + } +}; + +pub inline fn access(register: Register) *volatile u32 { + const physical_address = IA32_APIC_BASE.read().getAddress(); + const virtual_address = switch (lib.cpu.arch) { + .x86 => physical_address.toIdentityMappedVirtualAddress(), + .x86_64 => switch (lib.os) { + .freestanding => physical_address.toHigherHalfVirtualAddress(), + .uefi => physical_address.toIdentityMappedVirtualAddress(), + else => @compileError("Operating system not supported"), + }, + else => @compileError("Architecture not supported"), + }; + + return virtual_address.offset(@intFromEnum(register)).access(*volatile u32); +} + +pub inline fn read(register: Register) u32 { + return access(register).*; +} + +pub inline fn write(register: Register, value: u32) void { + access(register).* = value; +} + +pub const Register = enum(u32) { + id = 0x20, + version = 0x30, + tpr = 0x80, + apr = 0x90, + ppr = 0xa0, + eoi = 0xB0, + spurious = 0xF0, + error_status_register = 0x280, + icr_low = 0x300, + icr_high = 0x310, + lvt_timer = 0x320, + timer_div = 0x3e0, + timer_initcnt = 0x380, + timer_current_count = 0x390, +}; + +pub fn calibrateTimer() privileged.arch.x86_64.TicksPerMS { + //calibrate_timer_with_rtc(apic_base); + const timer_calibration_start = lib.arch.x86_64.readTimestamp(); + var times_i: u64 = 0; + const times = 8; + + APIC.write(.timer_initcnt, lib.maxInt(u32)); + + while (times_i < times) : (times_i += 1) { + io.write(u8, io.Ports.PIT_command, 0x30); + io.write(u8, io.Ports.PIT_data, 0xa9); + io.write(u8, io.Ports.PIT_data, 0x04); + + while (true) { + io.write(u8, io.Ports.PIT_command, 0xe2); + if (io.read(u8, io.Ports.PIT_data) & (1 << 7) != 0) break; + } + } + + const ticks_per_ms = (maxInt(u32) - read(.timer_current_count)) >> 4; + const timer_calibration_end = lib.arch.x86_64.readTimestamp(); + const timestamp_ticks_per_ms = @as(u32, @intCast((timer_calibration_end - timer_calibration_start) >> 3)); + + return .{ + .tsc = timestamp_ticks_per_ms, + .lapic = ticks_per_ms, + }; +} diff --git a/src/privileged/arch/x86/64/io.zig b/src/privileged/arch/x86/64/io.zig new file mode 100644 index 0000000..c9d9b8b --- /dev/null +++ b/src/privileged/arch/x86/64/io.zig @@ -0,0 +1,44 @@ +const common = @import("common"); +const log = common.log.scoped(.IO); + +const privileged = @import("privileged"); + +pub const Ports = struct { + pub const DMA1 = 0x0000; + pub const PIC1 = 0x0020; + pub const Cyrix_MSR = 0x0022; + pub const PIT_data = 0x0040; + pub const PIT_command = 0x0043; + pub const PS2 = 0x0060; + pub const CMOS_RTC = 0x0070; + pub const DMA_page_registers = 0x0080; + pub const A20 = 0x0092; + pub const PIC2 = 0x00a0; + pub const DMA2 = 0x00c0; + pub const E9_hack = 0x00e9; + pub const ATA2 = 0x0170; + pub const ATA1 = 0x01f0; + pub const parallel_port = 0x0278; + pub const serial2 = 0x02f8; + pub const IBM_VGA = 0x03b0; + pub const floppy = 0x03f0; + pub const serial1 = 0x03f8; + pub const PCI_config = 0x0cf8; + pub const PCI_data = 0x0cfc; +}; + +pub inline fn writeBytes(port: u16, bytes: []const u8) usize { + const bytes_left = asm volatile ( + \\rep outsb + : [ret] "={rcx}" (-> usize), + : [dest] "{dx}" (port), + [src] "{rsi}" (bytes.ptr), + [len] "{rcx}" (bytes.len), + : "rsi", "rcx" + ); + + return bytes.len - bytes_left; +} + +pub const read = privileged.arch.x86_64.read; +pub const write = privileged.arch.x86_64.write; diff --git a/src/privileged/arch/x86/64/paging.zig b/src/privileged/arch/x86/64/paging.zig new file mode 100644 index 0000000..fe00fed --- /dev/null +++ b/src/privileged/arch/x86/64/paging.zig @@ -0,0 +1,1088 @@ +const lib = @import("lib"); +const alignForward = lib.alignForward; +const alignBackward = lib.alignBackward; +const isAligned = lib.isAligned; +const isAlignedGeneric = lib.isAlignedGeneric; +const assert = lib.assert; +const enumCount = lib.enumCount; +const log = lib.log.scoped(.VAS); +const zeroes = lib.zeroes; +const Allocator = lib.Allocator; + +const privileged = @import("privileged"); +const Heap = privileged.Heap; +const PageAllocator = privileged.PageAllocator; + +const valid_page_sizes = lib.arch.x86_64.valid_page_sizes; +const reverse_valid_page_sizes = lib.arch.x86_64.reverse_valid_page_sizes; + +const x86_64 = privileged.arch.x86_64; +const cr3 = x86_64.registers.cr3; +const PhysicalAddress = lib.PhysicalAddress; +const VirtualAddress = lib.VirtualAddress; +const PhysicalMemoryRegion = lib.PhysicalMemoryRegion; +const PhysicalAddressSpace = lib.PhysicalAddressSpace; +const Mapping = privileged.Mapping; + +const bootloader = @import("bootloader"); + +const page_table_level_count = 4; +pub const page_table_mask = page_table_entry_count - 1; + +pub fn entryCount(comptime level: Level, limit: u64) u10 { + const index = baseFromVirtualAddress(level, limit - 1); + const result = @as(u10, index) + 1; + // @compileLog(limit, index, result); + return result; +} + +// Comptime test +comptime { + const va = 134217728; + const indices = computeIndices(va); + const pml4_index = baseFromVirtualAddress(.PML4, va); + const pdp_index = baseFromVirtualAddress(.PDP, va); + const pd_index = baseFromVirtualAddress(.PD, va); + const pt_index = baseFromVirtualAddress(.PT, va); + assert(pml4_index == indices[@intFromEnum(Level.PML4)]); + assert(pdp_index == indices[@intFromEnum(Level.PDP)]); + assert(pd_index == indices[@intFromEnum(Level.PD)]); + assert(pt_index == indices[@intFromEnum(Level.PT)]); +} + +const max_level_possible = 5; +pub const IndexedVirtualAddress = packed struct(u64) { + page_offset: u12 = 0, + PT: u9 = 0, + PD: u9 = 0, + PDP: u9 = 0, + PML4: u9 = 0, + _: u16 = 0, + + pub fn toVirtualAddress(indexed_virtual_address: IndexedVirtualAddress) VirtualAddress { + const raw = @as(u64, @bitCast(indexed_virtual_address)); + if (indexed_virtual_address.PML4 & 0x100 != 0) { + return VirtualAddress.new(raw | 0xffff_0000_0000_0000); + } else { + return VirtualAddress.new(raw); + } + } +}; + +pub fn baseFromVirtualAddress(comptime level: Level, virtual_address: u64) u9 { + const indexed = @as(IndexedVirtualAddress, @bitCast(virtual_address)); + return @field(indexed, @tagName(level)); +} + +pub const CPUPageTables = extern struct { + pml4_table: PhysicalAddress, + pdp_table: PhysicalAddress, + pd_table: PhysicalAddress, + p_table: PhysicalAddress, + + const base = 0xffff_ffff_8000_0000; + const top = base + pte_count * lib.arch.valid_page_sizes[0]; + const pte_count = page_table_entry_count - left_ptables; + pub const left_ptables = 4; + pub const pml4_index = 0x1ff; + pub const pdp_index = 0x1fe; + pub const pd_index = 0; + const allocated_table_count = + 1 + // PML4 + 1 + // PDP + 1 + // PD + 1; // PT + const allocated_size = allocated_table_count * 0x1000; + + const page_table_base = top; + + comptime { + assert(top + (left_ptables * lib.arch.valid_page_sizes[0]) == base + lib.arch.valid_page_sizes[1]); + } + + pub fn initialize(page_allocator: PageAllocator) !CPUPageTables { + const page_table_allocation = try page_allocator.allocate(page_allocator.context, allocated_size, lib.arch.valid_page_sizes[0], .{}); + + const page_tables = CPUPageTables{ + .pml4_table = page_table_allocation.address, + .pdp_table = page_table_allocation.address.offset(0x1000), + .pd_table = page_table_allocation.address.offset(0x2000), + .p_table = page_table_allocation.address.offset(0x3000), + }; + + page_tables.pml4_table.toIdentityMappedVirtualAddress().access(*PML4Table)[pml4_index] = PML4TE{ + .present = true, + .write = true, + .address = packAddress(PML4TE, page_tables.pdp_table.value()), + }; + + page_tables.pdp_table.toIdentityMappedVirtualAddress().access(*PDPTable)[pdp_index] = PDPTE{ + .present = true, + .write = true, + .address = packAddress(PDPTE, page_tables.pd_table.value()), + }; + + page_tables.pd_table.toIdentityMappedVirtualAddress().access(*PDTable)[pd_index] = PDTE{ + .present = true, + .write = true, + .address = packAddress(PDTE, page_tables.p_table.value()), + }; + + const p_table = page_tables.p_table.toIdentityMappedVirtualAddress().access(*PTable); + p_table[0x200 - 4] = .{ + .present = true, + .write = true, + .address = packAddress(PTE, page_tables.pml4_table.value()), + }; + p_table[0x200 - 3] = .{ + .present = true, + .write = true, + .address = packAddress(PTE, page_tables.pdp_table.value()), + }; + p_table[0x200 - 2] = .{ + .present = true, + .write = true, + .address = packAddress(PTE, page_tables.pd_table.value()), + }; + p_table[0x200 - 1] = .{ + .present = true, + .write = true, + .address = packAddress(PTE, page_tables.p_table.value()), + }; + + return page_tables; + } + + pub const MapError = error{ + lower_limit_exceeded, + upper_limit_exceeded, + }; + + pub fn map(cpu_page_tables: CPUPageTables, asked_physical_address: PhysicalAddress, asked_virtual_address: VirtualAddress, size: u64, general_flags: Mapping.Flags) CPUPageTables.MapError!void { + if (asked_virtual_address.value() < base) return CPUPageTables.MapError.lower_limit_exceeded; + if (asked_virtual_address.offset(size).value() > top) return CPUPageTables.MapError.upper_limit_exceeded; + + const flags = general_flags.toArchitectureSpecific(); + const indices = computeIndices(asked_virtual_address.value()); + const index = indices[indices.len - 1]; + const iteration_count = @as(u32, @intCast(size >> lib.arch.page_shifter(lib.arch.valid_page_sizes[0]))); + const p_table = cpu_page_tables.p_table.toIdentityMappedVirtualAddress().access(*PTable); + const p_table_slice = p_table[index .. index + iteration_count]; + + var physical_address = asked_physical_address.value(); + + for (p_table_slice) |*pte| { + pte.* = @as(PTE, @bitCast(getPageEntry(PTE, physical_address, flags))); + physical_address += 0x1000; + } + } +}; + +pub const Specific = extern struct { + cr3: cr3 align(8), + + pub inline fn makeCurrent(specific: Specific) void { + specific.getUserCr3().write(); + } + + pub fn fromPageTables(cpu_page_tables: CPUPageTables) Specific { + return .{ + .cr3 = cr3.fromAddress(cpu_page_tables.pml4_table), + }; + } + + pub noinline fn map(specific: Specific, asked_physical_address: PhysicalAddress, asked_virtual_address: VirtualAddress, size: u64, general_flags: Mapping.Flags, page_allocator: PageAllocator) !void { + const flags = general_flags.toArchitectureSpecific(); + const top_virtual_address = asked_virtual_address.offset(size); + + inline for (reverse_valid_page_sizes, 0..) |reverse_page_size, reverse_page_index| { + if (size >= reverse_page_size) { + const is_smallest_page_size = reverse_page_index == reverse_valid_page_sizes.len - 1; + + if (is_smallest_page_size) { + var virtual_address = asked_virtual_address.value(); + var physical_address = asked_physical_address.value(); + + while (virtual_address < top_virtual_address.value()) : ({ + physical_address += reverse_page_size; + virtual_address += reverse_page_size; + }) { + try specific.map4KPage(physical_address, virtual_address, flags, page_allocator); + } + + return; + } else { + const aligned_page_address = alignForward(u64, asked_virtual_address.value(), reverse_page_size); + const prologue_misalignment = aligned_page_address - asked_virtual_address.value(); + const aligned_size_left = size - prologue_misalignment; + + if (aligned_size_left >= reverse_page_size) { + if (prologue_misalignment != 0) { + try specific.map(asked_physical_address, asked_virtual_address, prologue_misalignment, general_flags, page_allocator); + } + + const virtual_address = VirtualAddress.new(aligned_page_address); + const physical_address = asked_physical_address.offset(prologue_misalignment); + const this_page_top_physical_address = PhysicalAddress.new(alignBackward(u64, physical_address.offset(aligned_size_left).value(), reverse_page_size)); + const this_page_top_virtual_address = VirtualAddress.new(alignBackward(u64, virtual_address.offset(aligned_size_left).value(), reverse_page_size)); + const this_huge_page_size = this_page_top_virtual_address.value() - virtual_address.value(); + try specific.mapGeneric(physical_address, virtual_address, this_huge_page_size, reverse_page_size, flags, page_allocator); + + const epilogue_misalignment = top_virtual_address.value() - this_page_top_virtual_address.value(); + + if (epilogue_misalignment != 0) { + const epilogue_physical_address = this_page_top_physical_address; + const epilogue_virtual_address = this_page_top_virtual_address; + + try specific.map(epilogue_physical_address, epilogue_virtual_address, epilogue_misalignment, general_flags, page_allocator); + } + + return; + } + } + } + } + + return MapError.no_region_found; + } + + fn mapGeneric(specific: Specific, asked_physical_address: PhysicalAddress, asked_virtual_address: VirtualAddress, size: u64, comptime asked_page_size: comptime_int, flags: MemoryFlags, page_allocator: PageAllocator) !void { + if (!isAlignedGeneric(u64, asked_physical_address.value(), asked_page_size)) { + //log.debug("PA: {}. Page size: 0x{x}", .{ asked_physical_address, asked_page_size }); + @panic("Misaligned physical address in mapGeneric"); + } + if (!isAlignedGeneric(u64, asked_virtual_address.value(), asked_page_size)) { + @panic("Misaligned virtual address in mapGeneric"); + } + if (!isAlignedGeneric(u64, size, asked_page_size)) { + //log.debug("Asked size: 0x{x}. Asked page size: 0x{x}", .{ size, asked_page_size }); + @panic("Misaligned size in mapGeneric"); + } + + var virtual_address = asked_virtual_address.value(); + var physical_address = asked_physical_address.value(); + const top_virtual_address = asked_virtual_address.offset(size).value(); + + // TODO: batch better + switch (asked_page_size) { + // 1 GB + lib.arch.valid_page_sizes[0] * page_table_entry_count * page_table_entry_count => { + while (virtual_address < top_virtual_address) : ({ + physical_address += asked_page_size; + virtual_address += asked_page_size; + }) { + try specific.map1GBPage(physical_address, virtual_address, flags, page_allocator); + } + }, + // 2 MB + lib.arch.valid_page_sizes[0] * page_table_entry_count => { + while (virtual_address < top_virtual_address) : ({ + physical_address += asked_page_size; + virtual_address += asked_page_size; + }) { + try specific.map2MBPage(physical_address, virtual_address, flags, page_allocator); + } + }, + // Smallest: 4 KB + lib.arch.valid_page_sizes[0] => { + while (virtual_address < top_virtual_address) : ({ + physical_address += asked_page_size; + virtual_address += asked_page_size; + }) { + try specific.map4KPage(physical_address, virtual_address, flags, page_allocator); + } + }, + else => @compileError("Invalid reverse valid page size"), + } + } + + fn map1GBPage(specific: Specific, physical_address: u64, virtual_address: u64, flags: MemoryFlags, page_allocator: PageAllocator) !void { + const indices = computeIndices(virtual_address); + + const pml4_table = try getPML4Table(specific.cr3); + const pdp_table = try getPDPTable(pml4_table, indices, flags, page_allocator); + try mapPageTable1GB(pdp_table, indices, physical_address, flags); + } + + fn map2MBPage(specific: Specific, physical_address: u64, virtual_address: u64, flags: MemoryFlags, page_allocator: PageAllocator) !void { + const indices = computeIndices(virtual_address); + + const pml4_table = try getPML4Table(specific.cr3); + const pdp_table = try getPDPTable(pml4_table, indices, flags, page_allocator); + const pd_table = try getPDTable(pdp_table, indices, flags, page_allocator); + + mapPageTable2MB(pd_table, indices, physical_address, flags) catch |err| { + log.err("Virtual address: 0x{x}. Physical address: 0x{x}", .{ virtual_address, physical_address }); + return err; + }; + } + + fn map4KPage(specific: Specific, physical_address: u64, virtual_address: u64, flags: MemoryFlags, page_allocator: PageAllocator) !void { + const indices = computeIndices(virtual_address); + + const pml4_table = try getPML4Table(specific.cr3); + const pdp_table = try getPDPTable(pml4_table, indices, flags, page_allocator); + const pd_table = try getPDTable(pdp_table, indices, flags, page_allocator); + const p_table = try getPTable(pd_table, indices, flags, page_allocator); + try mapPageTable4KB(p_table, indices, physical_address, flags); + } + + pub inline fn switchTo(specific: *Specific, execution_mode: lib.TraditionalExecutionMode) void { + const mask = ~@as(u64, 1 << 12); + const masked_cr3 = (@as(u64, @bitCast(specific.cr3)) & mask); + const privileged_or = (@as(u64, @intFromEnum(execution_mode)) << 12); + const new_cr3 = @as(cr3, @bitCast(masked_cr3 | privileged_or)); + specific.cr3 = new_cr3; + } + + pub inline fn copyHigherHalfCommon(cpu_specific: Specific, pml4_physical_address: PhysicalAddress) void { + const cpu_side_pml4_table = pml4_physical_address.toHigherHalfVirtualAddress().access(*PML4Table); + const privileged_cpu_pml4_table = try getPML4Table(cpu_specific.cr3); + for (cpu_side_pml4_table[0x100..], privileged_cpu_pml4_table[0x100..]) |*pml4_entry, cpu_pml4_entry| { + pml4_entry.* = cpu_pml4_entry; + } + } + + pub fn copyHigherHalfPrivileged(cpu_specific: Specific, pml4_physical_address: PhysicalAddress) void { + cpu_specific.copyHigherHalfCommon(pml4_physical_address); + } + + pub fn copyHigherHalfUser(cpu_specific: Specific, pml4_physical_address: PhysicalAddress, page_allocator: *PageAllocator) !void { + cpu_specific.copyHigherHalfCommon(pml4_physical_address); + + const pml4_table = pml4_physical_address.toHigherHalfVirtualAddress().access(*PML4Table); + const pml4_entry = pml4_table[0x1ff]; + const pml4_entry_address = PhysicalAddress.new(unpackAddress(pml4_entry)); + const pdp_table = pml4_entry_address.toHigherHalfVirtualAddress().access(*PDPTable); + const new_pdp_table_allocation = try page_allocator.allocate(0x1000, 0x1000); + const new_pdp_table = new_pdp_table_allocation.toHigherHalfVirtualAddress().access(PDPTE); + @memcpy(new_pdp_table, pdp_table); + new_pdp_table[0x1fd] = @as(PDPTE, @bitCast(@as(u64, 0))); + } + + pub const TranslateError = error{ + pml4_entry_not_present, + pml4_entry_address_null, + pdp_entry_not_present, + pdp_entry_address_null, + pd_entry_not_present, + pd_entry_address_null, + pt_entry_not_present, + pt_entry_address_null, + flags_not_respected, + }; + + pub fn translateAddress(specific: Specific, virtual_address: VirtualAddress, flags: MemoryFlags) !PhysicalAddress { + const indices = computeIndices(virtual_address.value()); + const is_desired = virtual_address.value() == 0xffff_ffff_8001_f000; + + const pml4_table = try getPML4Table(specific.cr3); + // if (is_desired) { + // _ = try specific.translateAddress(VirtualAddress.new(@ptrToInt(pml4_table)), .{}); + // } + + //log.debug("pml4 table: 0x{x}", .{@ptrToInt(pml4_table)}); + const pml4_index = indices[@intFromEnum(Level.PML4)]; + const pml4_entry = pml4_table[pml4_index]; + if (!pml4_entry.present) { + log.err("Virtual address: 0x{x}.\nPML4 index: {}.\nValue: {}\n", .{ virtual_address.value(), pml4_index, pml4_entry }); + return TranslateError.pml4_entry_not_present; + } + + if (pml4_entry.execute_disable and !flags.execute_disable) { + return TranslateError.flags_not_respected; + } + + const pml4_entry_address = PhysicalAddress.new(unpackAddress(pml4_entry)); + if (pml4_entry_address.value() == 0) { + return TranslateError.pml4_entry_address_null; + } + + const pdp_table = try getPDPTable(pml4_table, indices, undefined, null); + if (is_desired) { + _ = try specific.translateAddress(VirtualAddress.new(@intFromPtr(pdp_table)), .{}); + } + //log.debug("pdp table: 0x{x}", .{@ptrToInt(pdp_table)}); + const pdp_index = indices[@intFromEnum(Level.PDP)]; + const pdp_entry = &pdp_table[pdp_index]; + if (!pdp_entry.present) { + log.err("PDP index {} not present in PDP table 0x{x}", .{ pdp_index, @intFromPtr(pdp_table) }); + return TranslateError.pdp_entry_not_present; + } + + if (pdp_entry.execute_disable and !flags.execute_disable) { + return TranslateError.flags_not_respected; + } + + if (pdp_entry.page_size) { + const pdp_entry_1gb = @as(PDPTE_1GB, @bitCast(pdp_entry.*)); + const entry_address_value = unpackAddress(pdp_entry_1gb); + const physical_address = PhysicalAddress.new(entry_address_value); + if (lib.isAlignedGeneric(u64, virtual_address.value(), lib.gb)) { + return physical_address; + } else { + @panic("unaligned 1gb"); + } + } + + const pdp_entry_address = PhysicalAddress.new(unpackAddress(pdp_entry.*)); + if (pdp_entry_address.value() == 0) { + return TranslateError.pdp_entry_address_null; + } + + const pd_table = try accessPageTable(pdp_entry_address, *PDTable); + if (is_desired) { + _ = try specific.translateAddress(VirtualAddress.new(@intFromPtr(pd_table)), .{}); + } + //log.debug("pd table: 0x{x}", .{@ptrToInt(pd_table)}); + const pd_index = indices[@intFromEnum(Level.PD)]; + const pd_entry = &pd_table[pd_index]; + if (!pd_entry.present) { + log.err("PD index: {}", .{pd_index}); + log.err("PD entry: 0x{x}", .{@intFromPtr(pd_entry)}); + return TranslateError.pd_entry_not_present; + } + + if (pd_entry.execute_disable and !flags.execute_disable) { + return TranslateError.flags_not_respected; + } + + if (pd_entry.page_size) { + const pd_entry_2mb = @as(PDTE_2MB, @bitCast(pd_entry.*)); + const entry_address_value = unpackAddress(pd_entry_2mb); + const physical_address = PhysicalAddress.new(entry_address_value); + if (lib.isAlignedGeneric(u64, virtual_address.value(), 2 * lib.mb)) { + return physical_address; + } else { + @panic("unaligned 2mb"); + } + } + + const pd_entry_address = PhysicalAddress.new(unpackAddress(pd_entry)); + if (pd_entry_address.value() == 0) { + return TranslateError.pd_entry_address_null; + } + + const p_table = try accessPageTable(pd_entry_address, *PTable); + if (is_desired) { + _ = try specific.translateAddress(VirtualAddress.new(@intFromPtr(p_table)), .{}); + } + // log.debug("p table: 0x{x}", .{@ptrToInt(p_table)}); + const pt_index = indices[@intFromEnum(Level.PT)]; + const pt_entry = &p_table[pt_index]; + if (!pt_entry.present) { + log.err("Virtual address 0x{x} not mapped", .{virtual_address.value()}); + log.err("Indices: {any}", .{indices}); + log.err("PTE: 0x{x}", .{@intFromPtr(pt_entry)}); + log.err("PDE: 0x{x}", .{@intFromPtr(pd_entry)}); + log.err("PDPE: 0x{x}", .{@intFromPtr(pdp_entry)}); + return TranslateError.pt_entry_not_present; + } + + if (pt_entry.execute_disable and !flags.execute_disable) { + return TranslateError.flags_not_respected; + } + + const pt_entry_address = PhysicalAddress.new(unpackAddress(pt_entry.*)); + if (pt_entry_address.value() == 0) { + return TranslateError.pt_entry_address_null; + } + + return pt_entry_address; + } + + pub fn setMappingFlags(specific: Specific, virtual_address: u64, flags: Mapping.Flags) !void { + const indices = computeIndices(virtual_address); + + const vas_cr3 = specific.cr3; + + const pml4_physical_address = vas_cr3.getAddress(); + + const pml4_table = try accessPageTable(pml4_physical_address, *PML4Table); + const pml4_entry = pml4_table[indices[@intFromEnum(Level.PML4)]]; + if (!pml4_entry.present) { + return TranslateError.pml4_entry_not_present; + } + + const pml4_entry_address = PhysicalAddress.new(unpackAddress(pml4_entry)); + if (pml4_entry_address.value() == 0) { + return TranslateError.pml4_entry_address_null; + } + + const pdp_table = try accessPageTable(pml4_entry_address, *PDPTable); + const pdp_entry = pdp_table[indices[@intFromEnum(Level.PDP)]]; + if (!pdp_entry.present) { + return TranslateError.pdp_entry_not_present; + } + + const pdp_entry_address = PhysicalAddress.new(unpackAddress(pdp_entry)); + if (pdp_entry_address.value() == 0) { + return TranslateError.pdp_entry_address_null; + } + + const pd_table = try accessPageTable(pdp_entry_address, *PDTable); + const pd_entry = pd_table[indices[@intFromEnum(Level.PD)]]; + if (!pd_entry.present) { + return TranslateError.pd_entry_not_present; + } + + const pd_entry_address = PhysicalAddress.new(unpackAddress(pd_entry)); + if (pd_entry_address.value() == 0) { + return TranslateError.pd_entry_address_null; + } + + const pt_table = try accessPageTable(pd_entry_address, *PTable); + const pt_entry = &pt_table[indices[@intFromEnum(Level.PT)]]; + if (!pt_entry.present) { + return TranslateError.pd_entry_not_present; + } + + pt_entry.write = flags.write; + pt_entry.user = flags.user; + pt_entry.page_level_cache_disable = flags.cache_disable; + pt_entry.global = flags.global; + pt_entry.execute_disable = !flags.execute; + } + + pub fn debugMemoryMap(specific: Specific) !void { + log.debug("[START] Memory map dump 0x{x}\n", .{specific.cr3.getAddress().value()}); + + const pml4 = try specific.getCpuPML4Table(); + + for (pml4, 0..) |*pml4te, pml4_index| { + if (pml4te.present) { + const pdp_table = try accessPageTable(PhysicalAddress.new(unpackAddress(pml4te.*)), *PDPTable); + + for (pdp_table, 0..) |*pdpte, pdp_index| { + if (pdpte.present) { + if (pdpte.page_size) { + continue; + } + + const pd_table = try accessPageTable(PhysicalAddress.new(unpackAddress(pdpte.*)), *PDTable); + + for (pd_table, 0..) |*pdte, pd_index| { + if (pdte.present) { + if (pdte.page_size) @panic("bbbb"); + + const p_table = try accessPageTable(PhysicalAddress.new(unpackAddress(pdte.*)), *PTable); + + for (p_table, 0..) |*pte, pt_index| { + if (pte.present) { + const indexed_virtual_address = IndexedVirtualAddress{ + .PML4 = @as(u9, @intCast(pml4_index)), + .PDP = @as(u9, @intCast(pdp_index)), + .PD = @as(u9, @intCast(pd_index)), + .PT = @as(u9, @intCast(pt_index)), + }; + + const virtual_address = indexed_virtual_address.toVirtualAddress(); + const physical_address = unpackAddress(pte.*); + log.debug("0x{x} -> 0x{x}", .{ virtual_address.value(), physical_address }); + } + } + } + } + } + } + } + } + + log.debug("[END] Memory map dump", .{}); + } + + inline fn getUserCr3(specific: Specific) cr3 { + assert(@as(u64, @bitCast(specific.cr3)) & page_table_size == 0); + return @as(cr3, @bitCast(@as(u64, @bitCast(specific.cr3)) | page_table_size)); + } + + pub inline fn getCpuPML4Table(specific: Specific) !*PML4Table { + assert(@as(u64, @bitCast(specific.cr3)) & page_table_size == 0); + return try specific.getPML4TableUnchecked(); + } + pub inline fn getUserPML4Table(specific: Specific) !*PML4Table { + return try getPML4Table(specific.getUserCr3()); + } + + pub inline fn getPML4TableUnchecked(specific: Specific) !*PML4Table { + return try getPML4Table(specific.cr3); + } +}; + +const Indices = [enumCount(Level)]u16; + +const MapError = error{ + already_present_4kb, + already_present_2mb, + already_present_1gb, + validation_failed, + no_region_found, +}; + +pub const Error = error{ + invalid_physical, + invalid_virtual, + invalid_size, + unaligned_physical, + unaligned_virtual, + unaligned_size, +}; + +pub fn accessPageTable(physical_address: PhysicalAddress, comptime Pointer: type) !Pointer { + const virtual_address = switch (lib.cpu.arch) { + .x86 => physical_address.toIdentityMappedVirtualAddress(), + .x86_64 => switch (lib.os) { + .freestanding => physical_address.toHigherHalfVirtualAddress(), + .uefi => physical_address.toIdentityMappedVirtualAddress(), + else => @compileError("OS not supported"), + }, + else => @compileError("Architecture not supported"), + }; + + return switch (lib.cpu.arch) { + .x86 => @as(Pointer, @ptrFromInt(try lib.tryDereferenceAddress(virtual_address.value()))), + else => virtual_address.access(Pointer), + }; +} + +fn getPML4Table(cr3r: cr3) !*PML4Table { + const pml4_table = try accessPageTable(cr3r.getAddress(), *PML4Table); + return pml4_table; +} + +fn getPDPTable(pml4_table: *PML4Table, indices: Indices, flags: MemoryFlags, maybe_page_allocator: ?PageAllocator) !*PDPTable { + const index = indices[@intFromEnum(Level.PML4)]; + const entry_pointer = &pml4_table[index]; + + const table_physical_address = physical_address_blk: { + const entry_value = entry_pointer.*; + if (entry_value.present) { + const entry_address = unpackAddress(entry_value); + break :physical_address_blk PhysicalAddress.new(entry_address); + } else { + if (maybe_page_allocator) |page_allocator| { + const entry_allocation = try page_allocator.allocatePageTable(.{ + .level = .PDP, + .user = flags.user, + }); + + entry_pointer.* = PML4TE{ + .present = true, + .write = true, + .user = flags.user, + .address = packAddress(PML4TE, entry_allocation.address.value()), + }; + + break :physical_address_blk entry_allocation.address; + } else { + return Allocator.Allocate.Error.OutOfMemory; + } + } + }; + + return try accessPageTable(table_physical_address, *PDPTable); +} + +pub inline fn getPageEntry(comptime Entry: type, physical_address: u64, flags: MemoryFlags) Entry { + return if (@hasDecl(Entry, "page_size") and flags.page_size) Entry{ + .present = true, + .write = flags.write, + .user = flags.user, + .page_level_cache_disable = flags.cache_disable, + .global = flags.global, + .pat = flags.pat, + .address = packAddress(Entry, physical_address), + .execute_disable = flags.execute_disable, + .page_size = flags.page_size, + } else Entry{ + .present = true, + .write = flags.write, + .user = flags.user, + .page_level_cache_disable = flags.cache_disable, + .global = flags.global, + .pat = flags.pat, + .address = packAddress(Entry, physical_address), + .execute_disable = flags.execute_disable, + }; +} + +fn mapPageTable1GB(pdp_table: *PDPTable, indices: Indices, physical_address: u64, flags: MemoryFlags) MapError!void { + const entry_index = indices[@intFromEnum(Level.PDP)]; + const entry_pointer = &pdp_table[entry_index]; + + if (entry_pointer.present) return MapError.already_present_1gb; + + assert(isAlignedGeneric(u64, physical_address, valid_page_sizes[2])); + + entry_pointer.* = @as(PDPTE, @bitCast(getPageEntry(PDPTE_1GB, physical_address, flags))); +} + +fn mapPageTable2MB(pd_table: *PDTable, indices: Indices, physical_address: u64, flags: MemoryFlags) !void { + const entry_index = indices[@intFromEnum(Level.PD)]; + const entry_pointer = &pd_table[entry_index]; + const entry_value = entry_pointer.*; + + if (entry_value.present) { + log.err("Already mapped to: 0x{x}", .{unpackAddress(entry_value)}); + return MapError.already_present_2mb; + } + + assert(isAlignedGeneric(u64, physical_address, valid_page_sizes[1])); + + entry_pointer.* = @as(PDTE, @bitCast(getPageEntry(PDTE_2MB, physical_address, flags))); +} + +fn mapPageTable4KB(p_table: *PTable, indices: Indices, physical_address: u64, flags: MemoryFlags) !void { + const entry_index = indices[@intFromEnum(Level.PT)]; + const entry_pointer = &p_table[entry_index]; + + if (entry_pointer.present) { + log.err("Trying to map to 0x{x}. Already mapped to 0x{x}", .{ physical_address, unpackAddress(entry_pointer.*) }); + return MapError.already_present_4kb; + } + + assert(isAlignedGeneric(u64, physical_address, valid_page_sizes[0])); + + entry_pointer.* = @as(PTE, @bitCast(getPageEntry(PTE, physical_address, flags))); +} + +const ToImplementError = error{ + page_size, +}; + +fn getPDTable(pdp_table: *PDPTable, indices: Indices, flags: MemoryFlags, page_allocator: PageAllocator) !*PDTable { + const entry_index = indices[@intFromEnum(Level.PDP)]; + const entry_pointer = &pdp_table[entry_index]; + + const table_physical_address = physical_address_blk: { + const entry_value = entry_pointer.*; + if (entry_value.present) { + // The address is mapped with a 1GB page + if (entry_value.page_size) { + return ToImplementError.page_size; + } else break :physical_address_blk PhysicalAddress.new(unpackAddress(entry_value)); + } else { + const entry_allocation = try page_allocator.allocatePageTable(.{ + .level = .PD, + .user = flags.user, + }); + + entry_pointer.* = PDPTE{ + .present = true, + .write = true, + .user = flags.user, + .address = packAddress(PDPTE, entry_allocation.address.value()), + }; + + break :physical_address_blk entry_allocation.address; + } + }; + + return try accessPageTable(table_physical_address, *PDTable); +} + +fn getPTable(pd_table: *PDTable, indices: Indices, flags: MemoryFlags, page_allocator: PageAllocator) !*PTable { + const entry_pointer = &pd_table[indices[@intFromEnum(Level.PD)]]; + const table_physical_address = physical_address_blk: { + const entry_value = entry_pointer.*; + if (entry_value.present) { + // The address is mapped with a 2MB page + if (entry_value.page_size) { + return ToImplementError.page_size; + } else break :physical_address_blk PhysicalAddress.new(unpackAddress(entry_value)); + } else { + const entry_allocation = try page_allocator.allocatePageTable(.{ .level = .PT, .user = flags.user }); + + entry_pointer.* = PDTE{ + .present = true, + .write = true, + .user = flags.user, + .address = packAddress(PDTE, entry_allocation.address.value()), + }; + + break :physical_address_blk entry_allocation.address; + } + }; + + return try accessPageTable(table_physical_address, *PTable); +} + +const half_entry_count = (@sizeOf(PML4Table) / @sizeOf(PML4TE)) / 2; + +const needed_physical_memory_for_bootstrapping_cpu_driver_address_space = @sizeOf(PML4Table) + @sizeOf(PDPTable) * 256; + +pub fn computeIndices(virtual_address: u64) Indices { + var indices: Indices = undefined; + var va = virtual_address; + va = va >> 12; + indices[3] = @as(u9, @truncate(va)); + va = va >> 9; + indices[2] = @as(u9, @truncate(va)); + va = va >> 9; + indices[1] = @as(u9, @truncate(va)); + va = va >> 9; + indices[0] = @as(u9, @truncate(va)); + + return indices; +} + +pub inline fn newFlags(general_flags: Mapping.Flags) MemoryFlags { + return MemoryFlags{ + .write = general_flags.write, + .user = general_flags.user, + .cache_disable = general_flags.cache_disable, + .global = general_flags.global, + .execute_disable = !general_flags.execute, + }; +} + +pub const MemoryFlags = packed struct(u64) { + present: bool = true, + write: bool = false, + user: bool = false, + write_through: bool = false, + cache_disable: bool = false, + dirty: bool = false, + global: bool = false, + pat: bool = false, + page_size: bool = false, + reserved: u54 = 0, + execute_disable: bool = false, + + comptime { + assert(@sizeOf(u64) == @sizeOf(MemoryFlags)); + } +}; + +const address_mask: u64 = 0x0000_00ff_ffff_f000; + +pub const Level = Level4; + +pub const Level4 = enum(u2) { + PML4 = 0, + PDP = 1, + PD = 2, + PT = 3, + + pub const count = lib.enumCount(@This()); +}; + +pub const Level5 = enum(u3) {}; + +pub fn EntryTypeMapSize(comptime page_size: comptime_int) usize { + return switch (Level) { + Level4 => switch (page_size) { + lib.arch.valid_page_sizes[0] => 4, + lib.arch.valid_page_sizes[1] => 3, + lib.arch.valid_page_sizes[2] => 2, + else => @compileError("Unknown page size"), + }, + Level5 => @compileError("TODO"), + else => @compileError("unreachable"), + }; +} + +pub fn EntryTypeMap(comptime page_size: comptime_int) [EntryTypeMapSize(page_size)]type { + const map_size = EntryTypeMapSize(page_size); + const Result = [map_size]type; + var result: Result = undefined; + switch (Level) { + Level4, Level5 => { + if (@hasField(Level, "pml5")) { + @compileError("TODO: type_map[@enumToInt(Level.PML5)] ="); + } + + result[@intFromEnum(Level.PML4)] = PML4TE; + + if (page_size == lib.arch.valid_page_sizes[2]) { + assert(map_size == 2 + @intFromBool(Level == Level5)); + result[@intFromEnum(Level.PDP)] = PDPTE_1GB; + } else { + result[@intFromEnum(Level.PDP)] = PDPTE; + + if (page_size == lib.arch.valid_page_sizes[1]) { + assert(map_size == @as(usize, 3) + @intFromBool(Level == Level5)); + result[@intFromEnum(Level.PD)] = PDTE_2MB; + } else { + assert(page_size == lib.arch.valid_page_sizes[0]); + + result[@intFromEnum(Level.PD)] = PDTE; + result[@intFromEnum(Level.PT)] = PTE; + } + } + }, + else => @compileError("Unexpected level type"), + } + + return result; +} + +pub inline fn unpackAddress(entry: anytype) u64 { + const T = @TypeOf(entry); + const RealType = switch (@typeInfo(T)) { + .Pointer => |pointer| pointer.child, + else => T, + }; + const address_offset = @bitOffsetOf(RealType, "address"); + return @as(u64, entry.address) << address_offset; +} + +fn AddressType(comptime T: type) type { + var a: T = undefined; + return @TypeOf(@field(a, "address")); +} + +pub fn packAddress(comptime T: type, physical_address: u64) AddressType(T) { + assert(physical_address < lib.config.cpu_driver_higher_half_address); + const address_offset = @bitOffsetOf(T, "address"); + return @as(AddressType(T), @intCast(physical_address >> address_offset)); +} + +pub const PML4TE = packed struct(u64) { + present: bool = false, + write: bool = false, + user: bool = false, + page_level_write_through: bool = false, + page_level_cache_disable: bool = false, + accessed: bool = false, + reserved0: u5 = 0, + hlat_restart: bool = false, + address: u28, + reserved1: u23 = 0, + execute_disable: bool = false, + + comptime { + assert(@sizeOf(@This()) == @sizeOf(u64)); + assert(@bitSizeOf(@This()) == @bitSizeOf(u64)); + } +}; + +pub const PDPTE = packed struct(u64) { + present: bool = false, + write: bool = false, + user: bool = false, + page_level_write_through: bool = false, + page_level_cache_disable: bool = false, + accessed: bool = false, + reserved0: u1 = 0, + page_size: bool = false, + reserved1: u3 = 0, + hlat_restart: bool = false, + address: u28, + reserved2: u23 = 0, + execute_disable: bool = false, + + comptime { + assert(@sizeOf(@This()) == @sizeOf(u64)); + assert(@bitSizeOf(@This()) == @bitSizeOf(u64)); + } +}; + +pub const PDPTE_1GB = packed struct(u64) { + present: bool = false, + write: bool = false, + user: bool = false, + page_level_write_through: bool = false, + page_level_cache_disable: bool = false, + accessed: bool = false, + dirty: bool = false, + page_size: bool = true, + global: bool = false, + reserved: u2 = 0, + hlat_restart: bool = false, + pat: bool = false, + address: u29, + reserved2: u10 = 0, + ignored: u7 = 0, + protection_key: u4 = 0, + execute_disable: bool = false, + + comptime { + assert(@sizeOf(@This()) == @sizeOf(u64)); + assert(@bitSizeOf(@This()) == @bitSizeOf(u64)); + } +}; + +pub const PDTE = packed struct(u64) { + present: bool = false, + write: bool = false, + user: bool = false, + page_level_write_through: bool = false, + page_level_cache_disable: bool = false, + accessed: bool = false, + reserved0: u1 = 0, + page_size: bool = false, + reserved1: u3 = 0, + hlat_restart: bool = false, + address: u28, + reserved2: u23 = 0, + execute_disable: bool = false, + + comptime { + assert(@sizeOf(@This()) == @sizeOf(u64)); + assert(@bitSizeOf(@This()) == @bitSizeOf(u64)); + } +}; + +pub const PDTE_2MB = packed struct(u64) { + present: bool = false, + write: bool = false, + user: bool = false, + page_level_write_through: bool = false, + page_level_cache_disable: bool = false, + accessed: bool = false, + dirty: bool = false, + page_size: bool = true, + global: bool = false, + ignored: u2 = 0, + hlat_restart: bool = false, + pat: bool = false, + reserved: u8 = 0, + address: u21, + reserved2: u10 = 0, + ignored2: u7 = 0, + protection_key: u4 = 0, + execute_disable: bool = false, + + comptime { + assert(@sizeOf(@This()) == @sizeOf(u64)); + assert(@bitSizeOf(@This()) == @bitSizeOf(u64)); + } +}; + +pub const PTE = packed struct(u64) { + present: bool = false, + write: bool = false, + user: bool = false, + page_level_write_through: bool = false, + page_level_cache_disable: bool = false, + accessed: bool = false, + dirty: bool = false, + pat: bool = false, + global: bool = false, + reserved1: u2 = 0, + hlat_restart: bool = false, + address: u28, + reserved2: u19 = 0, + protection_key: u4 = 0, + execute_disable: bool = false, + + comptime { + assert(@sizeOf(@This()) == @sizeOf(u64)); + assert(@bitSizeOf(@This()) == @bitSizeOf(u64)); + } +}; + +pub const PML4Table = [page_table_entry_count]PML4TE; +pub const PDPTable = [page_table_entry_count]PDPTE; +pub const PDTable = [page_table_entry_count]PDTE; +pub const PTable = [page_table_entry_count]PTE; +pub const page_table_entry_size = @sizeOf(u64); +pub const page_table_size = lib.arch.valid_page_sizes[0]; +pub const page_table_entry_count = @divExact(page_table_size, page_table_entry_size); +pub const page_table_alignment = page_table_size; + +comptime { + assert(page_table_alignment == page_table_size); + assert(page_table_size == lib.arch.valid_page_sizes[0]); +} diff --git a/src/privileged/arch/x86/64/registers.zig b/src/privileged/arch/x86/64/registers.zig new file mode 100644 index 0000000..0343113 --- /dev/null +++ b/src/privileged/arch/x86/64/registers.zig @@ -0,0 +1,373 @@ +const lib = @import("lib"); +const assert = lib.assert; +const SimpleR64 = lib.arch.x86_64.registers.SimpleR64; +const RFLAGS = lib.arch.x86_64.registers.RFLAGS; + +const privileged = @import("privileged"); +const PhysicalAddress = lib.PhysicalAddress; +const VirtualAddress = lib.VirtualAddress; +const PhysicalMemoryRegion = lib.PhysicalMemoryRegion; +const VirtualMemoryRegion = lib.VirtualMemoryRegion; +const PhysicalAddressSpace = lib.PhysicalAddressSpace; + +pub const cr3 = packed struct(u64) { + reserved0: u3 = 0, + /// Page-level Write-Through (bit 3 of CR3) — Controls the memory type used to access the first paging + /// structure of the current paging-structure hierarchy. See Section 4.9, “Paging and Memory Typing”. This bit + /// is not used if paging is disabled, with PAE paging, or with 4-level paging or 5-level paging if CR4.PCIDE=1. + PWT: bool = false, + + /// Page-level Cache Disable (bit 4 of CR3) — Controls the memory type used to access the first paging + /// structure of the current paging-structure hierarchy. See Section 4.9, “Paging and Memory Typing”. This bit + /// is not used if paging is disabled, with PAE paging, or with 4-level paging1 or 5-level paging if CR4.PCIDE=1. + PCD: bool = false, + reserved1: u7 = 0, + address: u52 = 0, // get this to be 32-bit compatible + + comptime { + assert(@sizeOf(cr3) == @sizeOf(u64)); + assert(@bitSizeOf(cr3) == @bitSizeOf(u64)); + } + + pub fn fromAddress(physical_address: PhysicalAddress) cr3 { + const PackedAddressType = blk: { + var foo_cr3: cr3 = undefined; + break :blk @TypeOf(@field(foo_cr3, "address")); + }; + + return .{ + .address = @as(PackedAddressType, @intCast(physical_address.value() >> @bitOffsetOf(cr3, "address"))), + }; + } + + pub inline fn read() cr3 { + return asm volatile ("mov %cr3, %[result]" + : [result] "=r" (-> cr3), + ); + } + + pub inline fn write(value: cr3) void { + asm volatile ("mov %[in], %cr3" + : + : [in] "r" (value), + ); + } + + pub inline fn equal(self: cr3, other: cr3) bool { + const self_int = @as(usize, @bitCast(self)); + const other_int = @as(usize, @bitCast(other)); + return self_int == other_int; + } + + pub inline fn getAddress(self: cr3) PhysicalAddress { + return PhysicalAddress.new(@as(u64, self.address) << @bitOffsetOf(cr3, "address")); + } +}; + +/// Contains system control flags that control operating mode and states of the processor. +pub const cr0 = packed struct(u64) { + protected_mode_enable: bool = true, + monitor_coprocessor: bool = false, + emulation: bool = false, + task_switched: bool = false, + extension_type: bool = false, + numeric_error: bool = false, + reserved: u10 = 0, + write_protect: bool = true, + reserved1: u1 = 0, + alignment_mask: bool = false, + reserved2: u10 = 0, + not_write_through: bool = false, + cache_disable: bool = false, + paging: bool = true, + upper_32_bits: u32 = 0, + + pub inline fn read() cr0 { + return asm volatile ("mov %cr0, %[result]" + : [result] "=r" (-> cr0), + ); + } + + pub inline fn write(cr0r: cr0) void { + asm volatile ( + \\mov %[cr0], %cr0 + : + : [cr0] "r" (cr0r), + ); + } +}; + +/// Contains the page-fault linear address (the linear address that caused a page fault). +pub const cr2 = SimpleR64(.cr2); + +/// WARNING: this data structure is only set to be used for 40-bit max physical address bit +/// Contains a group of flags that enable several architectural extensions, and indicate operating system or +/// executive support for specific processor capabilities. Bits CR4[63:32] can only be used for IA-32e mode only +/// features that are enabled after entering 64-bit mode. Bits CR4[63:32] do not have any effect outside of IA-32e +/// mode. +pub const cr4 = packed struct(u64) { + vme: bool = false, + pvi: bool = false, + timestamp_disable: bool = false, + debugging_extensions: bool = false, + page_size_extensions: bool = false, + physical_address_extensions: bool = true, + machine_check_enable: bool = false, + page_global_enable: bool = true, + performance_monitoring_counter_enable: bool = true, + OSFXSR: bool = true, + OSXMMEXCPT: bool = false, + user_mode_instruction: bool = false, + linear_addresses_57_bit: bool = false, + vmx_enable: bool = false, + smx_enable: bool = false, + fs_gs_base_enable: bool = false, + pcid_enable: bool = false, + OSXSAVE: bool = false, + key_locker_enable: bool = false, + supervisor_mode_execution_prevention_enable: bool = false, + supervisor_mode_access_prevention_enable: bool = false, + protection_key_user_mode_enable: bool = false, + control_flow_enforcement_technology: bool = false, + protection_key_supervisor_mode_enable: bool = false, + reserved: u40 = 0, + + pub fn read() cr4 { + return asm volatile ( + \\mov %cr4, %[result] + : [result] "=r" (-> cr4), + ); + } + + pub fn write(cr4_register: cr4) void { + asm volatile ( + \\mov %[cr4], %cr4 + : + : [cr4] "r" (cr4_register), + ); + } +}; + +/// Provides read and write access to the Task Priority Register (TPR). It specifies the priority threshold +/// value that operating systems use to control the priority class of external interrupts allowed to interrupt the +/// processor. This register is available only in 64-bit mode. However, interrupt filtering continues to apply in +/// compatibility mode. +pub const cr8 = SimpleR64(.cr8); + +pub const IA32_LSTAR = SimpleMSR(0xC0000082); +pub const IA32_FMASK = SimpleMSR(0xC0000084); +pub const syscall_mask = (1 << @bitOffsetOf(RFLAGS, "CF")) | + (1 << @bitOffsetOf(RFLAGS, "PF")) | + (1 << @bitOffsetOf(RFLAGS, "AF")) | + (1 << @bitOffsetOf(RFLAGS, "ZF")) | + (1 << @bitOffsetOf(RFLAGS, "SF")) | + (1 << @bitOffsetOf(RFLAGS, "TF")) | + (1 << @bitOffsetOf(RFLAGS, "IF")) | + (1 << @bitOffsetOf(RFLAGS, "DF")) | + (1 << @bitOffsetOf(RFLAGS, "OF")) | + (1 << @bitOffsetOf(RFLAGS, "IOPL")) | + (1 << @bitOffsetOf(RFLAGS, "NT")) | + (1 << @bitOffsetOf(RFLAGS, "RF")) | + (1 << @bitOffsetOf(RFLAGS, "AC")) | + (1 << @bitOffsetOf(RFLAGS, "ID")); + +pub const IA32_FS_BASE = SimpleMSR(0xC0000100); +pub const IA32_GS_BASE = SimpleMSR(0xC0000101); +pub const IA32_KERNEL_GS_BASE = SimpleMSR(0xC0000102); + +pub fn SimpleMSR(comptime msr: u32) type { + return struct { + pub inline fn read() u64 { + var low: u32 = undefined; + var high: u32 = undefined; + + asm volatile ("rdmsr" + : [_] "={eax}" (low), + [_] "={edx}" (high), + : [_] "{ecx}" (msr), + ); + return (@as(u64, high) << 32) | low; + } + + pub inline fn write(value: u64) void { + const low = @as(u32, @truncate(value)); + const high = @as(u32, @truncate(value >> 32)); + + asm volatile ("wrmsr" + : + : [_] "{eax}" (low), + [_] "{edx}" (high), + [_] "{ecx}" (msr), + ); + } + }; +} + +pub const MemoryType = enum(u8) { + uncacheable = 0, + write_combining = 1, + reserved0 = 2, + reserved1 = 3, + write_through = 4, + write_protected = 5, + write_back = 6, + uncached = 7, +}; + +pub const IA32_PAT = extern struct { + page_attributes: [8]MemoryType, + + const MSR = SimpleMSR(0x277); + + pub fn read() IA32_PAT { + return @as(IA32_PAT, @bitCast(MSR.read())); + } + + pub fn write(pat: IA32_PAT) void { + MSR.write(@as(u64, @bitCast(pat))); + } +}; + +pub const IA32_EFER = packed struct(u64) { + /// Syscall Enable - syscall, sysret + SCE: bool = false, + reserved0: u7 = 0, + /// Long Mode Enable + LME: bool = false, + reserved1: bool = false, + /// Long Mode Active + LMA: bool = false, + /// Enables page access restriction by preventing instruction fetches from PAE pages with the XD bit set + NXE: bool = false, + SVME: bool = false, + LMSLE: bool = false, + FFXSR: bool = false, + TCE: bool = false, + reserved2: u48 = 0, + + comptime { + assert(@sizeOf(u64) == @sizeOf(IA32_EFER)); + } + + pub const MSR = SimpleMSR(0xC0000080); + + pub fn read() IA32_EFER { + const result = MSR.read(); + const typed_result = @as(IA32_EFER, @bitCast(result)); + return typed_result; + } + + pub fn write(typed_value: IA32_EFER) void { + const value = @as(u64, @bitCast(typed_value)); + MSR.write(value); + } +}; + +pub const IA32_STAR = packed struct(u64) { + reserved: u32 = 0, + kernel_cs: u16 = 0, + user_cs_anchor: u16 = 0, + + pub const MSR = SimpleMSR(0xC0000081); + + pub fn read() @This() { + const result = MSR.read(); + const typed_result = @as(@This(), @bitCast(result)); + return typed_result; + } + + pub fn write(typed_value: @This()) void { + const value = @as(u64, @bitCast(typed_value)); + MSR.write(value); + } +}; + +pub const IA32_APIC_BASE = packed struct(u64) { + reserved0: u8 = 0, + bsp: bool = false, + reserved1: u1 = 0, + extended: bool = false, + global_enable: bool = false, + address: u24, + reserved2: u28 = 0, + + pub const MSR = SimpleMSR(0x0000001B); + + pub inline fn read() IA32_APIC_BASE { + const result = MSR.read(); + const typed_result = @as(IA32_APIC_BASE, @bitCast(result)); + return typed_result; + } + + pub inline fn write(typed_value: IA32_APIC_BASE) void { + const value = @as(u64, @bitCast(typed_value)); + MSR.write(value); + } + + pub inline fn getAddress(ia32_apic_base: IA32_APIC_BASE) PhysicalAddress { + return PhysicalAddress.new(@as(u64, ia32_apic_base.address) << @bitOffsetOf(IA32_APIC_BASE, "address")); + } +}; + +pub const XCR0 = packed struct(u64) { + X87: bool = true, + SSE: bool = true, + AVX: bool = false, + BNDREG: bool = false, + BNDCSR: bool = false, + opmask: bool = false, + ZMM_hi256: bool = false, + Hi16_ZMM: bool = false, + _: bool = false, + PKRU: bool = false, + reserved: u7 = 0, + AMX_TILECFG: bool = false, + AMX_TILEDATA: bool = false, + reserved1: u45 = 0, + + pub inline fn read() XCR0 { + var eax: u32 = undefined; + var edx: u32 = undefined; + + asm volatile ( + \\xgetbv + : [eax] "={eax}" (eax), + [edx] "={edx}" (edx), + : [ecx] "i" (@as(u32, 0)), + ); + + const xcr0 = @as(XCR0, @bitCast(@as(u64, edx) << 32 | eax)); + return xcr0; + } + + pub inline fn write(xcr0: XCR0) void { + const bitcasted_xcr0 = @as(u64, @bitCast(xcr0)); + const eax = @as(u32, @truncate(bitcasted_xcr0)); + const edx = @as(u32, @truncate(bitcasted_xcr0 >> 32)); + + asm volatile ( + \\xsetbv + : + : [eax] "{eax}" (eax), + [edx] "{edx}" (edx), + [ecx] "{edx}" (@as(u32, 0)), + ); + } +}; + +pub const FSBASE = struct { + pub inline fn write(value: u64) void { + asm volatile ( + \\wrfsbase + : + : [value] "r" (value), + ); + } + + pub inline fn read() u64 { + return asm volatile ( + \\wrfsbase + : [value] "r" (-> u64), + ); + } +}; diff --git a/src/privileged/arch/x86/common.zig b/src/privileged/arch/x86/common.zig new file mode 100644 index 0000000..60c4d51 --- /dev/null +++ b/src/privileged/arch/x86/common.zig @@ -0,0 +1,65 @@ +const lib = @import("lib"); +const assert = lib.assert; + +pub const SegmentDescriptor = extern struct { + limit: u16, + address: u64 align(2), + + comptime { + assert(@sizeOf(@This()) == 10); + } +}; + +pub inline fn stopCPU() noreturn { + while (true) { + asm volatile ( + \\cli + \\hlt + \\pause + ::: "memory"); + } +} + +pub inline fn disableInterrupts() void { + asm volatile ("cli" ::: "memory"); +} + +pub inline fn read(comptime T: type, port: u16) T { + return switch (T) { + u8 => asm volatile ("inb %[port], %[result]" + : [result] "={al}" (-> u8), + : [port] "N{dx}" (port), + ), + u16 => asm volatile ("inw %[port], %[result]" + : [result] "={ax}" (-> u16), + : [port] "N{dx}" (port), + ), + u32 => asm volatile ("inl %[port], %[result]" + : [result] "={eax}" (-> u32), + : [port] "N{dx}" (port), + ), + + else => unreachable, + }; +} + +pub inline fn write(comptime T: type, port: u16, value: T) void { + switch (T) { + u8 => asm volatile ("outb %[value], %[port]" + : + : [value] "{al}" (value), + [port] "N{dx}" (port), + ), + u16 => asm volatile ("outw %[value], %[port]" + : + : [value] "{ax}" (value), + [port] "N{dx}" (port), + ), + u32 => asm volatile ("outl %[value], %[port]" + : + : [value] "{eax}" (value), + [port] "N{dx}" (port), + ), + else => unreachable, + } +} diff --git a/src/privileged/arch/x86_64.zig b/src/privileged/arch/x86_64.zig new file mode 100644 index 0000000..92d24bc --- /dev/null +++ b/src/privileged/arch/x86_64.zig @@ -0,0 +1,227 @@ +const x86 = @import("x86/common.zig"); +pub usingnamespace x86; + +const lib = @import("lib"); +const assert = lib.assert; +const cpuid = lib.arch.x86_64.CPUID; + +const privileged = @import("privileged"); + +pub const APIC = @import("x86/64/apic.zig"); +pub const io = @import("x86/64/io.zig"); +pub const paging = @import("x86/64/paging.zig"); +pub const registers = @import("x86/64/registers.zig"); + +pub const valid_page_sizes = privileged.arch.valid_page_sizes; +pub const page_size = valid_page_sizes[0]; +pub const reasonable_page_size = valid_page_sizes[1]; + +pub fn page_shifter(comptime asked_page_size: comptime_int) comptime_int { + return @ctz(@as(u32, asked_page_size)); +} + +/// Returns the maximum number bits a physical address is allowed to have in this CPU +pub inline fn get_max_physical_address_bit() u6 { + return @as(u6, @truncate(cpuid(0x80000008).eax)); +} + +pub const GDT = extern struct { + pub const Entry = packed struct(u64) { + limit_low: u16, + base_low: u16, + base_mid: u8, + access: packed struct(u8) { + accessed: bool, + read_write: bool, + direction_conforming: bool, + executable: bool, + code_data_segment: bool, + dpl: u2, + present: bool, + }, + limit_high: u4, + reserved: u1 = 0, + long_mode: bool, + size_flag: bool, + granularity: bool, + base_high: u8 = 0, + + pub const null_entry = Entry{ + .limit_low = 0, + .base_low = 0, + .base_mid = 0, + .access = .{ + .accessed = false, + .read_write = false, + .direction_conforming = false, + .executable = false, + .code_data_segment = false, + .dpl = 0, + .present = false, + }, + .limit_high = 0, + .long_mode = false, + .size_flag = false, + .granularity = false, + }; + + pub const code_16 = Entry{ + .limit_low = 0xffff, + .base_low = 0, + .base_mid = 0, + .access = .{ + .accessed = false, + .read_write = true, + .direction_conforming = false, + .executable = true, + .code_data_segment = true, + .dpl = 0, + .present = true, + }, + .limit_high = 0, + .long_mode = false, + .size_flag = false, + .granularity = false, + }; + + pub const data_16 = Entry{ + .limit_low = 0xffff, + .base_low = 0, + .base_mid = 0, + .access = .{ + .accessed = false, + .read_write = true, + .direction_conforming = false, + .executable = false, + .code_data_segment = true, + .dpl = 0, + .present = true, + }, + .limit_high = 0, + .long_mode = false, + .size_flag = false, + .granularity = false, + }; + + pub const code_32 = Entry{ + .limit_low = 0xffff, + .base_low = 0, + .base_mid = 0, + .access = .{ + .accessed = false, + .read_write = true, + .direction_conforming = false, + .executable = true, + .code_data_segment = true, + .dpl = 0, + .present = true, + }, + .limit_high = 0xf, + .long_mode = false, + .size_flag = true, + .granularity = true, + }; + + pub const data_32 = Entry{ + .limit_low = 0xffff, + .base_low = 0, + .base_mid = 0, + .access = .{ + .accessed = false, + .read_write = true, + .direction_conforming = false, + .executable = false, + .code_data_segment = true, + .dpl = 0, + .present = true, + }, + .limit_high = 0xf, + .long_mode = false, + .size_flag = true, + .granularity = true, + }; + + pub const code_64 = Entry{ + .limit_low = 0xffff, + .base_low = 0, + .base_mid = 0, + .access = .{ + .accessed = false, + .read_write = true, + .direction_conforming = false, + .executable = true, + .code_data_segment = true, + .dpl = 0, + .present = true, + }, + .limit_high = 0xf, + .long_mode = true, + .size_flag = false, + .granularity = false, + }; + + pub const data_64 = Entry{ + .limit_low = 0xffff, + .base_low = 0, + .base_mid = 0, + .access = .{ + .accessed = false, + .read_write = true, + .direction_conforming = false, + .executable = false, + .code_data_segment = true, + .dpl = 0, + .present = true, + }, + .limit_high = 0xf, + .long_mode = false, + .size_flag = false, + .granularity = false, + }; + + pub const user_data_64 = Entry{ + .limit_low = 0xffff, + .base_low = 0, + .base_mid = 0, + .access = .{ + .accessed = false, + .read_write = true, + .direction_conforming = false, + .executable = false, + .code_data_segment = true, + .dpl = 3, + .present = true, + }, + .limit_high = 0xf, + .long_mode = false, + .size_flag = false, + .granularity = true, + }; + + pub const user_code_64 = Entry{ + .limit_low = 0xffff, + .base_low = 0, + .base_mid = 0, + .access = .{ + .accessed = false, + .read_write = true, + .direction_conforming = false, + .executable = true, + .code_data_segment = true, + .dpl = 3, + .present = true, + }, + .limit_high = 0xf, + .long_mode = true, + .size_flag = false, + .granularity = true, + }; + }; + + pub const Descriptor = x86.SegmentDescriptor; +}; + +pub const TicksPerMS = extern struct { + tsc: u32, + lapic: u32, +}; diff --git a/src/rise.zig b/src/rise.zig new file mode 100644 index 0000000..5ac47dc --- /dev/null +++ b/src/rise.zig @@ -0,0 +1,23 @@ +const lib = @import("lib"); + +pub const arch = @import("rise/arch.zig"); +pub const capabilities = @import("rise/capabilities.zig"); +pub const syscall = @import("rise/syscall.zig"); + +/// This struct is the shared part that the user and the cpu see +pub const UserScheduler = extern struct { + self: *UserScheduler, + disabled: bool, + has_work: bool, + core_id: u32, + setup_stack: [lib.arch.valid_page_sizes[0]]u8 align(lib.arch.stack_alignment), + setup_stack_lock: lib.Atomic(bool), + + pub inline fn architectureSpecific(user_scheduler: *UserScheduler) *arch.UserScheduler { + return @fieldParentPtr(arch.UserScheduler, "generic", user_scheduler); + } +}; + +pub const CommandBuffer = struct { + foo: u32, +}; diff --git a/src/rise/arch.zig b/src/rise/arch.zig new file mode 100644 index 0000000..c82ed6d --- /dev/null +++ b/src/rise/arch.zig @@ -0,0 +1,7 @@ +const lib = @import("lib"); +pub usingnamespace switch (lib.cpu.arch) { + .x86_64 => x86_64, + else => @compileError("Architecture not supported"), +}; + +const x86_64 = @import("arch/x64_64.zig"); diff --git a/src/rise/arch/x64_64.zig b/src/rise/arch/x64_64.zig new file mode 100644 index 0000000..f407251 --- /dev/null +++ b/src/rise/arch/x64_64.zig @@ -0,0 +1,155 @@ +const lib = @import("lib"); +const assert = lib.assert; +const rise = @import("rise"); + +pub const UserScheduler = extern struct { + generic: rise.UserScheduler, + disabled_save_area: RegisterArena, +}; + +pub const RegisterArena = extern struct { + fpu: FPU align(lib.arch.stack_alignment), + registers: rise.arch.Registers, + + pub fn contextSwitch(register_arena: *align(lib.arch.stack_alignment) const RegisterArena) noreturn { + assert(lib.isAligned(@intFromPtr(register_arena), lib.arch.stack_alignment)); + //lib.log.debug("ASDASD: {}", .{register_arena}); + register_arena.fpu.load(); + register_arena.registers.restore(); + } +}; + +pub const Registers = extern struct { + r15: u64, + r14: u64, + r13: u64, + r12: u64, + rbp: u64, + rbx: u64, + r11: u64, + r10: u64, + r9: u64, + r8: u64, + rax: u64, + rcx: u64, + rdx: u64, + rsi: u64, + rdi: u64, + rip: u64, + rflags: lib.arch.x86_64.registers.RFLAGS, + rsp: u64, + + pub fn restore(registers: *const Registers) noreturn { + const fmt = lib.comptimePrint; + asm volatile (fmt( + "mov {}(%[registers]), %r15\n\t" ++ + "mov {}(%[registers]), %r14\n\t" ++ + "mov {}(%[registers]), %r13\n\t" ++ + "mov {}(%[registers]), %r12\n\t" ++ + "mov {}(%[registers]), %rbp\n\t" ++ + "mov {}(%[registers]), %rbx\n\t" ++ + "mov {}(%[registers]), %r11\n\t" ++ + "mov {}(%[registers]), %r10\n\t" ++ + "mov {}(%[registers]), %r9\n\t" ++ + "mov {}(%[registers]), %r8\n\t" ++ + "mov {}(%[registers]), %rax\n\t" ++ + "mov {}(%[registers]), %rcx\n\t" ++ + "mov {}(%[registers]), %rdx\n\t" ++ + "mov {}(%[registers]), %rsi\n\t" ++ + "pushq %[ss]\n\t" ++ + "pushq {}(%[registers])\n\t" ++ + "pushq {}(%[registers])\n\t" ++ + "pushq %[cs]\n\t" ++ + "pushq {}(%[registers])\n\t" ++ + "mov {}(%[registers]), %rdi\n\t" ++ + "iretq\n\t" ++ + "1: jmp 1b", + + .{ + @offsetOf(Registers, "r15"), + @offsetOf(Registers, "r14"), + @offsetOf(Registers, "r13"), + @offsetOf(Registers, "r12"), + @offsetOf(Registers, "rbp"), + @offsetOf(Registers, "rbx"), + @offsetOf(Registers, "r11"), + @offsetOf(Registers, "r10"), + @offsetOf(Registers, "r9"), + @offsetOf(Registers, "r8"), + @offsetOf(Registers, "rax"), + @offsetOf(Registers, "rcx"), + @offsetOf(Registers, "rdx"), + @offsetOf(Registers, "rsi"), + @offsetOf(Registers, "rsp"), + @offsetOf(Registers, "rflags"), + @offsetOf(Registers, "rip"), + @offsetOf(Registers, "rdi"), + }, + ) + : + : [registers] "{rdi}" (registers), + [ss] "i" (rise.arch.user_data_selector), + [cs] "i" (rise.arch.user_code_selector), + : "memory" + ); + + unreachable; + } +}; + +pub const FPU = extern struct { + fcw: u16, + fsw: u16, + ftw: u8, + reserved: u8 = 0, + fop: u16, + fpu_ip1: u32, + fpu_ip2: u16, + reserved0: u16 = 0, + fpu_dp1: u32, + fpu_dp2: u16, + reserved1: u16 = 0, + mxcsr: u32, + mxcsr_mask: u32, + st: [8][2]u64, + xmm: [16][2]u64, + reserved2: [12]u64 = .{0} ** 12, + + pub inline fn load(fpu: *align(lib.arch.stack_alignment) const FPU) void { + assert(@intFromPtr(fpu) % lib.arch.stack_alignment == 0); + asm volatile ( + \\fxrstor %[fpu] + : + : [fpu] "*p" (fpu), + : "memory" + ); + } +}; + +pub const user_code_selector = 0x43; +pub const user_data_selector = 0x3b; + +pub inline fn syscall(options: rise.syscall.Options, arguments: rise.syscall.Arguments) rise.syscall.Result { + var first: rise.syscall.Result.Rise.First = undefined; + var second: rise.syscall.Result.Rise.Second = undefined; + asm volatile ( + \\syscall + : [rax] "={rax}" (first), + [rdx] "={rdx}" (second), + : [options] "{rax}" (options), + [arg0] "{rdi}" (arguments[0]), + [arg1] "{rsi}" (arguments[1]), + [arg2] "{rdx}" (arguments[2]), + [arg3] "{r10}" (arguments[3]), + [arg4] "{r8}" (arguments[4]), + [arg5] "{r9}" (arguments[5]), + : "rcx", "r11", "rsp", "memory" + ); + + return .{ + .rise = .{ + .first = first, + .second = second, + }, + }; +} diff --git a/src/rise/capabilities.zig b/src/rise/capabilities.zig new file mode 100644 index 0000000..8b42ae0 --- /dev/null +++ b/src/rise/capabilities.zig @@ -0,0 +1,381 @@ +const lib = @import("lib"); +const assert = lib.assert; +const PhysicalAddress = lib.PhysicalAddress; + +const rise = @import("rise"); +const syscall = rise.syscall; + +const Capabilities = @This(); + +pub const Type = enum(u8) { + io, // primitive + cpu, // primitive + ram, // primitive + cpu_memory, // non-primitive Barrelfish: frame + boot, + process, // Temporarily available + page_table, // Barrelfish: vnode + // TODO: device_memory, // primitive + // scheduler, + // irq_table, + + // _, + + pub const Type = u8; + + pub const Mappable = enum { + cpu_memory, + page_table, + + pub inline fn toCapability(mappable: Mappable) Capabilities.Type { + return switch (mappable) { + inline else => |mappable_cap| @field(Capabilities.Type, @tagName(mappable_cap)), + }; + } + }; +}; + +pub const Subtype = u16; +pub const AllTypes = Type; + +pub fn CommandBuilder(comptime list: []const []const u8) type { + const capability_base_command_list = .{ + "copy", + "mint", + "retype", + "delete", + "revoke", + "create", + } ++ list; + const enum_fields = lib.enumAddNames(&.{}, capability_base_command_list); + + // TODO: make this non-exhaustive enums + // PROBLEM: https://github.com/ziglang/zig/issues/12250 + // Currently waiting on this since this will enable some comptime magic + const result = @Type(.{ + .Enum = .{ + .tag_type = Subtype, + .fields = enum_fields, + .decls = &.{}, + .is_exhaustive = true, + }, + }); + return result; +} + +/// Takes some names and integers. Then values are added to the Command enum for an specific capability +/// The number is an offset of the fields with respect to the base command enum fields +pub fn Command(comptime capability: Type) type { + const extra_command_list = switch (capability) { + .io => .{ + "log", + }, + .cpu => .{ + "get_core_id", + "shutdown", + "get_command_buffer", + }, + .ram => [_][]const u8{}, + .cpu_memory => .{ + "allocate", + }, + .boot => .{ + "get_bundle_size", + "get_bundle_file_list_size", + }, + .process => .{ + "exit", + }, + .page_table => [_][]const u8{}, + }; + + return CommandBuilder(&extra_command_list); +} + +const success = 0; +const first_valid_error = success + 1; + +pub fn ErrorSet(comptime error_names: []const []const u8) type { + return lib.ErrorSet(error_names, &.{ + .{ + .name = "forbidden", + .value = first_valid_error + 0, + }, + .{ + .name = "corrupted_input", + .value = first_valid_error + 1, + }, + .{ + .name = "invalid_input", + .value = first_valid_error + 2, + }, + }); +} + +const raw_argument_count = @typeInfo(syscall.Arguments).Array.len; + +pub fn Syscall(comptime capability_type: Type, comptime command_type: Command(capability_type)) type { + const Types = switch (capability_type) { + .io => switch (command_type) { + .copy, .mint, .retype, .delete, .revoke, .create => struct { + pub const ErrorSet = Capabilities.ErrorSet(&.{}); + pub const Result = void; + pub const Arguments = void; + }, + .log => struct { + pub const ErrorSet = Capabilities.ErrorSet(&.{}); + pub const Result = usize; + pub const Arguments = []const u8; + + inline fn toResult(raw_result: syscall.Result.Rise) Result { + return raw_result.second; + } + + inline fn resultToRaw(result: Result) syscall.Result { + return syscall.Result{ + .rise = .{ + .first = .{}, + .second = result, + }, + }; + } + + inline fn argumentsToRaw(arguments: Arguments) syscall.Arguments { + const result = [2]usize{ @intFromPtr(arguments.ptr), arguments.len }; + return result ++ .{0} ** (raw_argument_count - result.len); + } + + inline fn toArguments(raw_arguments: syscall.Arguments) !Arguments { + const message_ptr = @as(?[*]const u8, @ptrFromInt(raw_arguments[0])) orelse return error.invalid_input; + const message_len = raw_arguments[1]; + if (message_len == 0) return error.invalid_input; + const message = message_ptr[0..message_len]; + return message; + } + }, + }, + .cpu => switch (command_type) { + .copy, .mint, .retype, .delete, .revoke, .create => struct { + pub const ErrorSet = Capabilities.ErrorSet(&.{}); + pub const Result = void; + pub const Arguments = void; + }, + .get_core_id => struct { + pub const ErrorSet = Capabilities.ErrorSet(&.{}); + pub const Result = u32; + pub const Arguments = void; + + inline fn toResult(raw_result: syscall.Result.Rise) Result { + return @as(Result, @intCast(raw_result.second)); + } + + inline fn resultToRaw(result: Result) syscall.Result { + return syscall.Result{ + .rise = .{ + .first = .{}, + .second = result, + }, + }; + } + }, + .shutdown => struct { + pub const ErrorSet = Capabilities.ErrorSet(&.{}); + pub const Result = noreturn; + pub const Arguments = void; + + pub const toResult = @compileError("noreturn unexpectedly returned"); + }, + .get_command_buffer => struct { + pub const ErrorSet = Capabilities.ErrorSet(&.{}); + pub const Result = noreturn; + pub const Arguments = *rise.CommandBuffer; + + pub const toResult = @compileError("noreturn unexpectedly returned"); + + inline fn toArguments(raw_arguments: syscall.Arguments) !Arguments { + const ptr = @as(?*rise.CommandBuffer, @ptrFromInt(raw_arguments[0])) orelse return error.invalid_input; + return ptr; + } + + inline fn argumentsToRaw(arguments: Arguments) syscall.Arguments { + const result = [1]usize{@intFromPtr(arguments)}; + return result ++ .{0} ** (raw_argument_count - result.len); + } + }, + }, + .ram => struct { + pub const ErrorSet = Capabilities.ErrorSet(&.{}); + pub const Result = void; + pub const Arguments = void; + }, + .cpu_memory => struct { + pub const ErrorSet = Capabilities.ErrorSet(&.{ + "OutOfMemory", + }); + pub const Result = PhysicalAddress; + pub const Arguments = usize; + + inline fn toResult(raw_result: syscall.Result.Rise) Result { + return PhysicalAddress.new(raw_result.second); + } + + inline fn resultToRaw(result: Result) syscall.Result { + return syscall.Result{ + .rise = .{ + .first = .{}, + .second = result.value(), + }, + }; + } + + inline fn toArguments(raw_arguments: syscall.Arguments) !Arguments { + const size = raw_arguments[0]; + return size; + } + + inline fn argumentsToRaw(arguments: Arguments) syscall.Arguments { + const result = [1]usize{arguments}; + return result ++ .{0} ** (raw_argument_count - result.len); + } + }, + .boot => switch (command_type) { + .get_bundle_file_list_size, .get_bundle_size => struct { + pub const ErrorSet = Capabilities.ErrorSet(&.{ + "buffer_too_small", + }); + pub const Result = usize; + pub const Arguments = void; + + inline fn resultToRaw(result: Result) syscall.Result { + return syscall.Result{ + .rise = .{ + .first = .{}, + .second = result, + }, + }; + } + + inline fn toResult(raw_result: syscall.Result.Rise) Result { + return raw_result.second; + } + }, + else => struct { + pub const ErrorSet = Capabilities.ErrorSet(&.{ + "buffer_too_small", + }); + pub const Result = void; + pub const Arguments = void; + }, + }, + .process => switch (command_type) { + .exit => struct { + pub const ErrorSet = Capabilities.ErrorSet(&.{}); + pub const Result = noreturn; + pub const Arguments = bool; + + inline fn toArguments(raw_arguments: syscall.Arguments) !Arguments { + const result = raw_arguments[0] != 0; + return result; + } + inline fn argumentsToRaw(arguments: Arguments) syscall.Arguments { + const result = [1]usize{@intFromBool(arguments)}; + return result ++ .{0} ** (raw_argument_count - result.len); + } + }, + else => struct { + pub const ErrorSet = Capabilities.ErrorSet(&.{}); + pub const Result = void; + pub const Arguments = void; + }, + }, + .page_table => switch (command_type) { + else => struct { + pub const ErrorSet = Capabilities.ErrorSet(&.{}); + pub const Result = void; + pub const Arguments = void; + }, + }, + // else => @compileError("TODO: " ++ @tagName(capability)), + }; + + return struct { + pub const ErrorSet = Types.ErrorSet; + pub const Result = Types.Result; + pub const Arguments = Types.Arguments; + pub const toResult = Types.toResult; + pub const toArguments = if (Arguments != void) + Types.toArguments + else + struct { + fn lambda(raw_arguments: syscall.Arguments) error{}!void { + _ = raw_arguments; + return {}; + } + }.lambda; + pub const capability = capability_type; + pub const command = command_type; + + pub inline fn resultToRaw(result: Result) syscall.Result { + return if (@hasDecl(Types, "resultToRaw")) blk: { + comptime assert(Result != void and Result != noreturn); + break :blk Types.resultToRaw(result); + } else blk: { + if (Result != void) { + @compileError("expected void type, got " ++ @typeName(Result) ++ ". You forgot to implement a resultToRaw function" ++ " for (" ++ @tagName(capability) ++ ", " ++ @tagName(command) ++ ")."); + } + + break :blk syscall.Result{ + .rise = .{ + .first = .{}, + .second = 0, + }, + }; + }; + } + + pub inline fn errorToRaw(err: @This().ErrorSet.Error) syscall.Result { + const error_enum = switch (err) { + inline else => |comptime_error| @field(@This().ErrorSet.Enum, @errorName(comptime_error)), + }; + return syscall.Result{ + .rise = .{ + .first = .{ + .@"error" = @intFromEnum(error_enum), + }, + .second = 0, + }, + }; + } + + /// This is not meant to be called in the CPU driver + pub fn blocking(arguments: Arguments) @This().ErrorSet.Error!Result { + const raw_arguments = if (Arguments != void) Types.argumentsToRaw(arguments) else [1]usize{0} ** raw_argument_count; + // TODO: make this more reliable and robust? + const options = rise.syscall.Options{ + .rise = .{ + .type = capability, + .command = @intFromEnum(command), + }, + }; + + const raw_result = rise.arch.syscall(options, raw_arguments); + + const raw_error_value = raw_result.rise.first.@"error"; + comptime { + assert(!@hasField(@This().ErrorSet.Enum, "ok")); + assert(!@hasField(@This().ErrorSet.Enum, "success")); + assert(lib.enumFields(@This().ErrorSet.Enum)[0].value == first_valid_error); + } + + return switch (raw_error_value) { + success => switch (Result) { + noreturn => unreachable, + else => toResult(raw_result.rise), + }, + else => switch (@as(@This().ErrorSet.Enum, @enumFromInt(raw_error_value))) { + inline else => |comptime_error_enum| @field(@This().ErrorSet.Error, @tagName(comptime_error_enum)), + }, + }; + } + }; +} diff --git a/src/rise/syscall.zig b/src/rise/syscall.zig new file mode 100644 index 0000000..55da669 --- /dev/null +++ b/src/rise/syscall.zig @@ -0,0 +1,117 @@ +const lib = @import("lib"); +const assert = lib.assert; +const log = lib.log.scoped(.Syscall); + +const rise = @import("rise"); +const capabilities = rise.capabilities; + +pub const argument_count = 6; +pub const Arguments = [argument_count]usize; + +pub const Convention = enum(u1) { + linux = 0, + rise = 1, +}; + +pub const Options = extern union { + general: General, + rise: Rise, + linux: Linux, + + pub const General = packed struct(u64) { + number: Number, + convention: Convention, + + pub const Number = lib.IntType(.unsigned, union_space_bits); + + comptime { + assertSize(@This()); + } + + pub inline fn getNumberInteger(general: General, comptime convention: Convention) NumberIntegerType(convention) { + const options_integer = @as(u64, @bitCast(general)); + return @as(NumberIntegerType(convention), @truncate(options_integer)); + } + + pub fn NumberIntegerType(comptime convention: Convention) type { + return switch (convention) { + .rise => Rise.IDInteger, + .linux => u64, + }; + } + }; + + pub const Rise = packed struct(u64) { + type: capabilities.Type, + command: capabilities.Subtype, + reserved: lib.IntType(.unsigned, @bitSizeOf(u64) - @bitSizeOf(capabilities.Type) - @bitSizeOf(capabilities.Subtype) - @bitSizeOf(Convention)) = 0, + convention: Convention = .rise, + + comptime { + Options.assertSize(@This()); + } + + const IDInteger = u16; + pub const ID = enum(IDInteger) { + qemu_exit = 0, + print = 1, + }; + }; + + pub const Linux = enum(u64) { + _, + comptime { + Options.assertSize(@This()); + } + }; + + pub const union_space_bits = @bitSizeOf(u64) - @bitSizeOf(Convention); + + fn assertSize(comptime T: type) void { + assert(@sizeOf(T) == @sizeOf(u64)); + assert(@bitSizeOf(T) == @bitSizeOf(u64)); + } + + comptime { + assertSize(@This()); + } +}; + +pub const Result = extern union { + general: General, + rise: Rise, + linux: Linux, + + pub const General = extern struct { + first: packed struct(u64) { + argument: u63, + convention: Convention, + }, + second: u64, + }; + + pub const Rise = extern struct { + first: First, + second: Second, + + pub const First = packed struct(u64) { + padding1: u32 = 0, + @"error": u16 = 0, + padding2: u8 = 0, + padding3: u7 = 0, + convention: Convention = .rise, + }; + + pub const Second = u64; + }; + + pub const Linux = extern struct { + result: u64, + reserved: u64 = 0, + }; + + fn assertSize(comptime T: type) void { + assert(@sizeOf(T) == @sizeOf(u64)); + assert(@bitSizeOf(T) == @bitSizeOf(u64)); + } +}; diff --git a/src/user.zig b/src/user.zig new file mode 100644 index 0000000..09f7c6b --- /dev/null +++ b/src/user.zig @@ -0,0 +1,142 @@ +const lib = @import("lib"); +const log = lib.log; +const assert = lib.assert; +const ExecutionMode = lib.Syscall.ExecutionMode; + +const rise = @import("rise"); +const capabilities = rise.capabilities; +pub const Syscall = rise.capabilities.Syscall; + +pub const arch = @import("user/arch.zig"); +const core_state = @import("user/core_state.zig"); +pub const CoreState = core_state.CoreState; +pub const PinnedState = core_state.PinnedState; +pub const libc = @import("user/libc.zig"); +pub const thread = @import("user/thread.zig"); +pub const process = @import("user/process.zig"); +const vas = @import("user/virtual_address_space.zig"); +const VirtualAddress = lib.VirtualAddress; +pub const VirtualAddressSpace = vas.VirtualAddressSpace; +pub const MMUAwareVirtualAddressSpace = vas.MMUAwareVirtualAddressSpace; + +pub const PhysicalMap = @import("user/physical_map.zig").PhysicalMap; +pub const PhysicalMemoryRegion = @import("user/physical_memory_region.zig").PhysicalMemoryRegion; +pub const SlotAllocator = @import("user/slot_allocator.zig").SlotAllocator; + +comptime { + @export(arch._start, .{ .linkage = .Strong, .name = "_start" }); +} + +pub const writer = lib.Writer(void, Writer.Error, Writer.write){ .context = {} }; +const Writer = extern struct { + const syscall = Syscall(.io, .log); + const Error = Writer.syscall.ErrorSet.Error; + + fn write(_: void, bytes: []const u8) Error!usize { + const result = try Writer.syscall.blocking(bytes); + return result; + } +}; + +pub const std_options = struct { + pub fn logFn(comptime level: lib.std.log.Level, comptime scope: @TypeOf(.EnumLiteral), comptime format: []const u8, args: anytype) void { + lib.format(writer, format, args) catch unreachable; + writer.writeByte('\n') catch unreachable; + _ = scope; + _ = level; + } +}; + +pub fn zigPanic(message: []const u8, _: ?*lib.StackTrace, _: ?usize) noreturn { + @call(.always_inline, panic, .{ "{s}", .{message} }); +} + +pub fn panic(comptime format: []const u8, arguments: anytype) noreturn { + lib.log.scoped(.PANIC).err(format, arguments); + while (true) { + Syscall(.process, .exit).blocking(false) catch |err| log.err("Exit failed: {}", .{err}); + } +} + +pub const Scheduler = extern struct { + time_slice: u32, + core_id: u32, + core_state: CoreState, +}; + +pub inline fn currentScheduler() *Scheduler { + return arch.currentScheduler(); +} + +fn schedulerInitDisabled(scheduler: *arch.Scheduler) void { + // Architecture-specific initialization + scheduler.generic.time_slice = 1; + // TODO: capabilities +} + +pub var is_init = false; +pub var command_buffer: rise.CommandBuffer = undefined; + +pub export fn start(scheduler: *arch.Scheduler, arg_init: bool) callconv(.C) noreturn { + assert(arg_init); + is_init = arg_init; + if (is_init) { + assert(scheduler.common.generic.setup_stack_lock.load(.Monotonic)); + } + assert(scheduler.common.generic.disabled); + scheduler.initDisabled(); + @panic("TWTQWD"); + // command_buffer = Syscall(.cpu, .get_command_buffer).blocking(&command_buffer) catch @panic("Unable to get command buffer"); +} + +// export fn riseInitializeDisabled(scheduler: *arch.Scheduler, arg_init: bool) callconv(.C) noreturn { +// // TODO: delete when this code is unnecessary. In the meanwhile it counts as a sanity check +// assert(arg_init); +// is_init = arg_init; +// schedulerInitDisabled(scheduler); +// thread.initDisabled(scheduler); +// } + +// Barrelfish: vregion +pub const VirtualMemoryRegion = extern struct { + virtual_address_space: *VirtualAddressSpace, + physical_region: *PhysicalMemoryRegion, + offset: usize, + size: usize, + address: VirtualAddress, + flags: Flags, + next: ?*VirtualMemoryRegion = null, + + pub const Flags = packed struct(u8) { + read: bool = false, + write: bool = false, + execute: bool = false, + cache_disabled: bool = false, + preferred_page_size: u2 = 0, + write_combining: bool = false, + reserved: u1 = 0, + }; +}; + +pub const MoreCore = extern struct { + const InitializationError = error{ + invalid_page_size, + }; + + pub fn init(page_size: usize) InitializationError!void { + blk: inline for (lib.arch.valid_page_sizes) |valid_page_size| { + if (valid_page_size == page_size) break :blk; + } else { + return InitializationError.invalid_page_size; + } + + const morecore_state = process.getMoreCoreState(); + morecore_state.mmu_state = try MMUAwareVirtualAddressSpace.initAligned(SlotAllocator.getDefault(), lib.arch.valid_page_sizes[1], lib.arch.valid_page_sizes[0], .{ .read = true, .write = true }); + + @panic("TODO: MoreCore.init"); + } + + pub const State = extern struct { + mmu_state: MMUAwareVirtualAddressSpace, + }; +}; diff --git a/src/user/arch.zig b/src/user/arch.zig new file mode 100644 index 0000000..1b9aaec --- /dev/null +++ b/src/user/arch.zig @@ -0,0 +1,16 @@ +const lib = @import("lib"); + +comptime { + if (lib.os != .freestanding) @compileError("OS not supported"); +} + +pub const x86_64 = @import("arch/x86_64.zig"); + +const current = switch (lib.cpu.arch) { + .x86_64 => x86_64, + else => @compileError("Architecture not supported"), +}; + +pub usingnamespace current; + +pub const _start = current._start; diff --git a/src/user/arch/x86_64.zig b/src/user/arch/x86_64.zig new file mode 100644 index 0000000..6ea46cd --- /dev/null +++ b/src/user/arch/x86_64.zig @@ -0,0 +1,116 @@ +const lib = @import("lib"); +const log = lib.log; +const assert = lib.assert; +const rise = @import("rise"); +const user = @import("user"); + +const FPU = rise.arch.FPU; +const Registers = rise.arch.Registers; +const RegisterArena = rise.arch.RegisterArena; + +const VirtualAddress = lib.VirtualAddress; + +const PhysicalMemoryRegion = user.PhysicalMemoryRegion; +const PhysicalMap = user.PhysicalMap; +const SlotAllocator = user.SlotAllocator; +const Thread = user.Thread; +const VirtualAddressSpace = user.VirtualAddressSpace; + +pub const Scheduler = extern struct { + common: rise.arch.UserScheduler, + generic: user.Scheduler, + + pub fn initDisabled(scheduler: *Scheduler) void { + _ = scheduler; + // TODO: + // *set entry points? + // *set tls registers? + } + + pub noinline fn restore(scheduler: *Scheduler, register_arena: *const RegisterArena) noreturn { + assert(scheduler.common.generic.disabled); + assert(scheduler.common.generic.has_work); + + assert(register_arena.registers.rip > lib.arch.valid_page_sizes[0]); + assert(register_arena.registers.rflags.IF and register_arena.registers.rflags.reserved0); + + register_arena.contextSwitch(); + } +}; + +// CRT0 +pub fn _start() callconv(.Naked) noreturn { + asm volatile ( + \\push %rbp + \\jmp *%[startFunction] + : + : [startFunction] "r" (user.start), + ); + + unreachable; +} + +pub inline fn setInitialState(register_arena: *RegisterArena, entry: VirtualAddress, stack_virtual_address: VirtualAddress, arguments: rise.syscall.Arguments) void { + assert(stack_virtual_address.value() > lib.arch.valid_page_sizes[0]); + assert(lib.isAligned(stack_virtual_address.value(), lib.arch.stack_alignment)); + var stack_address = stack_virtual_address; + // x86_64 ABI + stack_address.subOffset(@sizeOf(usize)); + + register_arena.registers.rip = entry.value(); + register_arena.registers.rsp = stack_address.value(); + register_arena.registers.rflags = .{ .IF = true }; + register_arena.registers.rdi = arguments[0]; + register_arena.registers.rsi = arguments[1]; + register_arena.registers.rdx = arguments[2]; + register_arena.registers.rcx = arguments[3]; + register_arena.registers.r8 = arguments[4]; + register_arena.registers.r9 = arguments[5]; + + register_arena.fpu = lib.zeroes(FPU); + // register_arena.fpu.fcw = 0x037f; + register_arena.fpu.fcw = 0x1f80; +} + +pub inline fn maybeCurrentScheduler() ?*user.Scheduler { + return asm volatile ( + \\mov %fs:0, %[user_scheduler] + : [user_scheduler] "=r" (-> ?*user.Scheduler), + : + : "memory" + ); +} + +pub inline fn currentScheduler() *user.Scheduler { + const result = maybeCurrentScheduler().?; + return result; +} + +/// This is an interface to user.PhysicalMap, providing the architecture-specific functionality +pub const PhysicalMapInterface = struct { + pub fn determineAddress(physical_map: *PhysicalMap, physical_memory_region: PhysicalMemoryRegion, alignment: usize) !VirtualAddress { + _ = physical_memory_region; + _ = alignment; + assert(physical_map.virtual_address_space.regions != null); + log.debug("PMap: 0x{x}", .{@intFromPtr(physical_map.virtual_address_space.regions)}); + log.debug("PMap: {?}", .{physical_map.virtual_address_space.regions}); + @panic("TODO: PhysicalMapInterface.determineAddress"); + } + + pub fn initializeCurrent(physical_map: *PhysicalMap) !void { + _ = physical_map; + log.warn("TODO: PhysicalMapInterface.initializeCurrent", .{}); + } + + pub fn init(virtual_address_space: *VirtualAddressSpace, page_level: u3, slot_allocator: *SlotAllocator) !PhysicalMap { + var result = PhysicalMap{ + .virtual_address_space = virtual_address_space, + .slot_allocator = slot_allocator, + }; + _ = page_level; + + try result.initPageTableManagement(); + + @panic("TODO: PhysicalMap.init"); + } +}; diff --git a/src/user/arch/x86_64/linker_script.ld b/src/user/arch/x86_64/linker_script.ld new file mode 100644 index 0000000..e820441 --- /dev/null +++ b/src/user/arch/x86_64/linker_script.ld @@ -0,0 +1,24 @@ +PHDRS { + none PT_NULL FLAGS(0); + text PT_LOAD FLAGS((1 << 2) | (1 << 0) /* Readable | Executable */); + rodata PT_LOAD FLAGS((1 << 2) /* Readable */); + data PT_LOAD FLAGS((1 << 2) | (1 << 1) /* Readable | Writeable */); +} + +SECTIONS { + . = 0x600000; + . = ALIGN(4K); + .text . : { + *(.text*) + }:text + . = ALIGN(4K); + .rodata . : { + *(.rodata*) + }:rodata + . = ALIGN(4K); + .data . : { + *(.data*) + *(.bss*) + }:data + . = ALIGN(4K); +} diff --git a/src/user/capabilities.zig b/src/user/capabilities.zig new file mode 100644 index 0000000..b44f92a --- /dev/null +++ b/src/user/capabilities.zig @@ -0,0 +1,18 @@ +const lib = @import("lib"); +const assert = lib.assert; +const rise = @import("rise"); + +// TODO: ref +pub fn frameCreate(ref: usize, bytes: usize) !usize { + return mappableCapabilityCreate(ref, .cpu_memory, bytes); +} + +fn mappableCapabilityCreate(ref: usize, mappable_capability: rise.capabilities.Type.Mappable, bytes: usize) !usize { + _ = mappable_capability; + _ = ref; + assert(bytes > 0); +} + +fn ramDescendantCreate(ref: usize, ) !usize { + _ = ref; +} diff --git a/src/user/core_state.zig b/src/user/core_state.zig new file mode 100644 index 0000000..705e7df --- /dev/null +++ b/src/user/core_state.zig @@ -0,0 +1,27 @@ +const user = @import("user"); +const MoreCore = user.MoreCore; +const PhysicalMap = user.PhysicalMap; +const PhysicalMemoryRegion = user.PhysicalMemoryRegion; +const SlotAllocator = user.SlotAllocator; +const VirtualAddressSpace = user.VirtualAddressSpace; +const VirtualMemoryRegion = user.VirtualMemoryRegion; + +pub const PagingState = extern struct { + virtual_address_space: VirtualAddressSpace, + physical_map: PhysicalMap, +}; + +pub const PinnedState = extern struct { + physical_memory_region: PhysicalMemoryRegion.Pinned, + virtual_memory_region: VirtualMemoryRegion, + offset: usize, + // TODO: lists +}; + +pub const CoreState = extern struct { + paging: PagingState, + slot_allocator: SlotAllocator.State, + virtual_address_space: VirtualAddressSpace.State, + pinned: PinnedState, + more_core: MoreCore.State, +}; diff --git a/src/user/libc.zig b/src/user/libc.zig new file mode 100644 index 0000000..cf6889f --- /dev/null +++ b/src/user/libc.zig @@ -0,0 +1,7 @@ +const user = @import("user"); + +pub export fn malloc(size: usize) ?*anyopaque { + const morecore_state = user.process.getMoreCoreState(); + const result = morecore_state.mmu_state.map(size) catch return null; + return result.ptr; +} diff --git a/src/user/mmu_aware_virtual_address_space.zig b/src/user/mmu_aware_virtual_address_space.zig new file mode 100644 index 0000000..6ea5e7e --- /dev/null +++ b/src/user/mmu_aware_virtual_address_space.zig @@ -0,0 +1,62 @@ +const lib = @import("lib"); +const assert = lib.assert; +const log = lib.log.scoped(.MMUAwareVirtualAddressSpace); + +const user = @import("user"); +const PhysicalMemoryRegion = user.PhysicalMemoryRegion; +const SlotAllocator = user.SlotAllocator; +const VirtualMemoryRegion = user.VirtualMemoryRegion; + +pub const MMUAwareVirtualAddressSpace = extern struct { + size: usize, + alignment: usize, + consumed: usize = 0, + /// This is a index into the architecture-specific page sizes + page_size: u8, + slot_allocator: *SlotAllocator, + physical_memory_region: PhysicalMemoryRegion.Anonymous, + virtual_memory_region: VirtualMemoryRegion, + // struct vregion vregion; ///< Needs just one vregion + // struct memobj_anon memobj; ///< Needs just one memobj + // lvaddr_t offset; ///< Offset of free space in anon + // lvaddr_t mapoffset; ///< Offset into the anon that has been mapped in + + pub fn init(size: usize) !MMUAwareVirtualAddressSpace { + const slot_allocator = SlotAllocator.getDefault(); + const alignment = lib.arch.valid_page_sizes[0]; + return initAligned(slot_allocator, size, alignment, .{ .write = true }); + } + + pub fn initAligned(slot_allocator: *SlotAllocator, size: usize, alignment: usize, flags: VirtualMemoryRegion.Flags) !MMUAwareVirtualAddressSpace { + assert(flags.preferred_page_size < lib.arch.valid_page_sizes.len); + var result = MMUAwareVirtualAddressSpace{ + .size = size, + .alignment = alignment, + .page_size = flags.preferred_page_size, + .slot_allocator = slot_allocator, + .physical_memory_region = try PhysicalMemoryRegion.Anonymous.new(size), + .virtual_memory_region = undefined, + }; + // TODO: fix this API + result.virtual_memory_region = try user.process.getVirtualAddressSpace().mapAligned(result.physical_memory_region.getGeneric().*, 0, size, alignment, flags); + + // TODO: create memobj + // TODO: map memobj into vregion + + @panic("TODO: MMUAwareVirtualAddressSpace.initAligned"); + } + + const Error = error{ + alignment, + }; + + pub fn map(virtual_address_space: *MMUAwareVirtualAddressSpace, size: usize) ![]u8 { + if (!lib.isAligned(size, lib.arch.valid_page_sizes[0])) { + return error.alignment; + } + _ = virtual_address_space; + log.warn("[map] TODO: slot allocation", .{}); + //virtual_address_space.slot_allocator.allocate(); + @panic("TODO: MMUAwareVirtualAddressSpace.map"); + } +}; diff --git a/src/user/physical_map.zig b/src/user/physical_map.zig new file mode 100644 index 0000000..516e50d --- /dev/null +++ b/src/user/physical_map.zig @@ -0,0 +1,25 @@ +const lib = @import("lib"); +const log = lib.log.scoped(.PhysicalMap); + +const user = @import("user"); +const SlotAllocator = user.SlotAllocator; +const VirtualAddressSpace = user.VirtualAddressSpace; + +pub const PhysicalMap = extern struct { + virtual_address_space: *VirtualAddressSpace, + slot_allocator: *SlotAllocator, + + pub usingnamespace user.arch.PhysicalMapInterface; + + pub fn initPageTableManagement(physical_map: *PhysicalMap) !void { + const current_physical_map = user.process.getPhysicalMap(); + log.debug("CURR: 0x{x}. PHYS: 0x{x}", .{ @intFromPtr(current_physical_map), @intFromPtr(physical_map) }); + if (current_physical_map == physical_map) { + @panic("TODO: if"); + } else { + log.warn("TODO: slab_init", .{}); + _ = user.libc.malloc(lib.arch.valid_page_sizes[0]); + @panic("TODO: else"); + } + } +}; diff --git a/src/user/physical_memory_region.zig b/src/user/physical_memory_region.zig new file mode 100644 index 0000000..65129aa --- /dev/null +++ b/src/user/physical_memory_region.zig @@ -0,0 +1,81 @@ +const lib = @import("lib"); +const assert = lib.assert; +const log = lib.log.scoped(.PhysicalMemoryRegion); + +// Barrelfish: memobj +pub const PhysicalMemoryRegion = extern struct { + size: usize, + type: Type, + + pub const Type = enum(u8) { + anonymous = 0, + one_frame = 1, + pinned = 3, + //one_frame_lazy, + //one_frame_one_map, + // vfs, + // fixed, + // numa, + // append, + + fn map(t: Type) type { + return switch (t) { + .anonymous => Anonymous, + .one_frame => OneFrame, + .pinned => Pinned, + }; + } + }; + + pub const Anonymous = extern struct { + region: PhysicalMemoryRegion, + + pub usingnamespace Interface(@This()); + + pub fn new(size: usize) !Anonymous { + const result = Anonymous{ + .region = .{ + .size = size, + .type = .anonymous, + }, + }; + + log.warn("[Anonymous.new] TODO: initialize memory", .{}); + + return result; + } + }; + + pub const OneFrame = extern struct { + pub usingnamespace Interface(@This()); + }; + + pub const Pinned = extern struct { + region: PhysicalMemoryRegion, + pub usingnamespace Interface(@This()); + + pub fn new(size: usize) !Pinned { + const result = Pinned{ + .region = .{ + .size = size, + .type = .pinned, + }, + }; + + log.warn("[Pinned.new] TODO: initialize memory", .{}); + + return result; + } + }; + + fn Interface(comptime PhysicalMemoryRegionType: type) type { + assert(@hasField(PhysicalMemoryRegionType, "region")); + assert(@TypeOf(@field(@as(PhysicalMemoryRegionType, undefined), "region")) == PhysicalMemoryRegion); + + return extern struct { + pub inline fn getGeneric(r: *PhysicalMemoryRegionType) *PhysicalMemoryRegion { + return &r.region; + } + }; + } +}; diff --git a/src/user/process.zig b/src/user/process.zig new file mode 100644 index 0000000..60b9d72 --- /dev/null +++ b/src/user/process.zig @@ -0,0 +1,31 @@ +const user = @import("user"); +const currentScheduler = user.currentScheduler; +const MoreCore = user.MoreCore; +const PhysicalMap = user.PhysicalMap; +const PinnedState = user.PinnedState; +const SlotAllocator = user.SlotAllocator; +const VirtualAddressSpace = user.VirtualAddressSpace; + +pub inline fn getVirtualAddressSpace() *VirtualAddressSpace { + return ¤tScheduler().core_state.paging.virtual_address_space; +} + +pub inline fn getPhysicalMap() *PhysicalMap { + return ¤tScheduler().core_state.paging.physical_map; +} + +pub inline fn getSlotAllocatorState() *SlotAllocator.State { + return ¤tScheduler().core_state.slot_allocator; +} + +pub inline fn getSlotAllocator() *SlotAllocator { + return ¤tScheduler().core_state.slot_allocator.default_allocator.allocator; +} + +pub inline fn getPinnedState() *PinnedState { + return ¤tScheduler().core_state.pinned; +} + +pub inline fn getMoreCoreState() *MoreCore.State { + return ¤tScheduler().core_state.more_core; +} diff --git a/src/user/programs/device_manager/main.zig b/src/user/programs/device_manager/main.zig new file mode 100644 index 0000000..f855590 --- /dev/null +++ b/src/user/programs/device_manager/main.zig @@ -0,0 +1,18 @@ +const lib = @import("lib"); +const log = lib.log; +const user = @import("user"); +const Syscall = user.Syscall; + +pub const panic = user.zigPanic; +pub const std_options = user.std_options; + +export var core_id: u32 = 0; + +pub fn main() !noreturn { + core_id = try Syscall(.cpu, .get_core_id).blocking({}); + user.currentScheduler().core_id = core_id; + log.debug("Hello world! User space initialization from core #{}", .{core_id}); + const allocation = try Syscall(.cpu_memory, .allocate).blocking(0x1000); + log.debug("Look allocation successful at 0x{x}", .{allocation.value()}); + try Syscall(.cpu, .shutdown).blocking({}); +} diff --git a/src/user/programs/device_manager/module.json b/src/user/programs/device_manager/module.json new file mode 100644 index 0000000..2a8c06a --- /dev/null +++ b/src/user/programs/device_manager/module.json @@ -0,0 +1,4 @@ +{ + "kind": "zig_exe", + "dependencies": [] +} diff --git a/src/user/programs/device_manager/test.zig b/src/user/programs/device_manager/test.zig new file mode 100644 index 0000000..e69de29 diff --git a/src/user/programs/init/main.zig b/src/user/programs/init/main.zig new file mode 100644 index 0000000..19e6781 --- /dev/null +++ b/src/user/programs/init/main.zig @@ -0,0 +1,25 @@ +const lib = @import("lib"); +const assert = lib.assert; +const log = lib.log; +const user = @import("user"); +const syscall = user.Syscall; + +pub const panic = user.zigPanic; +pub const std_options = user.std_options; + +export var core_id: u32 = 0; + +pub fn main() !noreturn { + // core_id = try syscall(.cpu, .get_core_id).blocking({}); + // user.currentScheduler().core_id = core_id; + // log.debug("Hello world! User space initialization from core #{}", .{core_id}); + // const bundle_file_list_size = try syscall(.boot, .get_bundle_file_list_size).blocking({}); + // log.debug("Bundle file list size: {}", .{bundle_file_list_size}); + // const bundle_size = try syscall(.boot, .get_bundle_size).blocking({}); + // log.debug("Bundle size: {}", .{bundle_size}); + // assert(bundle_size > 0); + // const aligned_bundle_size = lib.alignForward(usize, bundle_size, lib.arch.valid_page_sizes[0]); + // const bundle_allocation = try syscall(.cpu_memory, .allocate).blocking(aligned_bundle_size); + // log.debug("Look allocation successful at 0x{x}", .{bundle_allocation.value()}); + try syscall(.cpu, .shutdown).blocking({}); +} diff --git a/src/user/programs/init/module.json b/src/user/programs/init/module.json new file mode 100644 index 0000000..2a8c06a --- /dev/null +++ b/src/user/programs/init/module.json @@ -0,0 +1,4 @@ +{ + "kind": "zig_exe", + "dependencies": [] +} diff --git a/src/user/programs/init/test.zig b/src/user/programs/init/test.zig new file mode 100644 index 0000000..e69de29 diff --git a/src/user/slot_allocator.zig b/src/user/slot_allocator.zig new file mode 100644 index 0000000..dcd102f --- /dev/null +++ b/src/user/slot_allocator.zig @@ -0,0 +1,28 @@ +const log = @import("lib").log; +const user = @import("user"); + +pub const SlotAllocator = extern struct { + foo: u32 = 0, + + /// This function is inlined because it's only called once + pub inline fn init() !void { + log.warn("TODO: implement the whole SlotAllocator.init", .{}); + const state = user.process.getSlotAllocatorState(); + const default_allocator = state.default_allocator; + _ = default_allocator; + } + + pub fn getDefault() *SlotAllocator { + const process_slot_allocator_state = user.process.getSlotAllocatorState(); + return &process_slot_allocator_state.default_allocator.allocator; + } + + pub const State = extern struct { + default_allocator: MultiSlotAllocator, + }; +}; + +pub const MultiSlotAllocator = extern struct { + allocator: SlotAllocator, + // TODO: +}; diff --git a/src/user/thread.zig b/src/user/thread.zig new file mode 100644 index 0000000..1da6c4c --- /dev/null +++ b/src/user/thread.zig @@ -0,0 +1,172 @@ +const lib = @import("lib"); +const log = lib.log.scoped(.thread); +const user = @import("user"); +const rise = @import("rise"); + +const MoreCore = user.MoreCore; +const MMUAwareVirtualAddressSpace = user.MMUAwareVirtualAddressSpace; +const SlotAllocator = user.SlotAllocator; +const VirtualAddress = lib.VirtualAddress; +const VirtualAddressSpace = user.VirtualAddressSpace; + +const max_thread_count = 256; + +pub const Thread = extern struct { + self: *Thread, + previous: ?*Thread, + next: ?*Thread, + stack: [*]u8, + stack_top: [*]align(lib.arch.stack_alignment) u8, + register_arena: rise.arch.RegisterArena align(lib.arch.stack_alignment), + core_id: u32, + + pub fn init(thread: *Thread, scheduler: *user.arch.Scheduler) void { + thread.self = thread; + thread.previous = null; + thread.next = null; + thread.core_id = scheduler.generic.core_id; + } +}; + +pub const Mutex = extern struct { + locked: bool = false, + + pub inline fn internalLock(mutex: *volatile Mutex) void { + mutex.locked = true; + } +}; + +var static_stack: [0x10000]u8 align(lib.arch.stack_alignment) = undefined; +var static_thread: Thread = undefined; +var static_thread_lock = Mutex{}; + +pub fn initDisabled(scheduler: *user.arch.Scheduler) noreturn { + const thread = &static_thread; + static_thread_lock.internalLock(); + thread.stack = &static_stack; + thread.stack_top = static_stack[static_stack.len..]; + thread.init(scheduler); + + // TODO: use RAX as parameter? + + user.arch.setInitialState(&thread.register_arena, VirtualAddress.new(bootstrapThread), VirtualAddress.new(thread.stack_top), .{0} ** 6); + + scheduler.common.generic.has_work = true; + + scheduler.restore(&thread.register_arena); +} + +const SpawnDomainParams = extern struct {}; + +// TODO: +const Foo = struct {}; + +pub var slab_allocator: Foo = undefined; +pub var slab_virtual_address_space: user.MMUAwareVirtualAddressSpace = undefined; + +fn initThread(parameters: *SpawnDomainParams) !void { + _ = parameters; + // TODO: + // - waitset + // - ram alloc init + + try VirtualAddressSpace.initializeCurrent(); + + try SlotAllocator.init(); + + if (false) { + // TODO: + log.warn("TODO: handle the case where spawn domain parameters exist", .{}); + } else if (user.is_init) { + log.warn("Known init page table layout. Take advantage of that!", .{}); + } + + if (user.is_init) { + log.debug("More core init start", .{}); + try MoreCore.init(lib.arch.valid_page_sizes[0]); + log.debug("More core init end", .{}); + log.warn("TODO: implement memory initialization -> morecore_init()", .{}); + } else { + @panic("TODO: not init userspace binary"); + } + + log.warn("TODO: Should we do LMP endpoints?", .{}); + + if (!user.is_init) { + @panic("TODO: not init user binaries"); + } +} + +fn bootstrapThread(parameters: *SpawnDomainParams) callconv(.C) noreturn { + + // TODO: Do we have TLS data? + // tls_block_init_base = params->tls_init_base; + // tls_block_init_len = params->tls_init_len; + // tls_block_total_len = params->tls_total_len; + + initThread(parameters) catch |err| user.panic("initThread failed: {}", .{err}); + // // Allocate storage region for real threads + // size_t blocksize = sizeof(struct thread) + tls_block_total_len + THREAD_ALIGNMENT; + // err = vspace_mmu_aware_init(&thread_slabs_vm, MAX_THREADS * blocksize); + + // TODO: make this declaration value assignment complete + const block_size = @sizeOf(Thread); + slab_virtual_address_space = try MMUAwareVirtualAddressSpace.init(max_thread_count * block_size); + // if (err_is_fail(err)) { + // USER_PANIC_ERR(err, "vspace_mmu_aware_init for thread region failed\n"); + // } + // // XXX: do this nicer, but we need struct threads to be in Vspace < 4GB so + // // we can set the thread segment register. -SG, 2017-02-28. + // // We can't use the assertion yet, as the init domain has it's thread + // // slabs above 4G. + // //assert(vregion_get_base_addr(&thread_slabs_vm.vregion) + vregion_get_size(&thread_slabs_vm.vregion) < 1ul << 32); + // slab_init(&thread_slabs, blocksize, refill_thread_slabs); + + if (user.is_init) { + // No allocation path + mainThread(parameters); + } else { + // Do allocations + while (true) {} + } +} + +fn mainThread(parameters: ?*anyopaque) noreturn { + // TODO: parameters + _ = parameters; + const root = @import("root"); + if (@hasDecl(root, "main")) { + const result = switch (@typeInfo(@typeInfo(@TypeOf(root.main)).Fn.return_type.?)) { + .NoReturn => root.main(), + .Void => blk: { + root.main(); + break :blk 0; + }, + .Int => root.main(), + .ErrorUnion => blk: { + const result = root.main() catch { + // TODO: log + break :blk 1; + }; + + switch (@typeInfo(@TypeOf(result))) { + .Void => break :blk 0, + .Int => break :blk result, + else => @compileError("Unexpected return type"), + } + }, + else => @compileError("Unexpected return type"), + }; + _ = result; + @panic("ASdasd"); + } else { + const result = _main(); + _ = result; + } +} + +export fn _main() i32 { + // global constructors + // array + return 0; +} diff --git a/src/user/virtual_address_space.zig b/src/user/virtual_address_space.zig new file mode 100644 index 0000000..daea64a --- /dev/null +++ b/src/user/virtual_address_space.zig @@ -0,0 +1,60 @@ +const lib = @import("lib"); +const log = lib.log; + +const user = @import("user"); +const PhysicalMap = user.PhysicalMap; +const PhysicalMemoryRegion = user.PhysicalMemoryRegion; +const VirtualMemoryRegion = user.VirtualMemoryRegion; + +pub const MMUAwareVirtualAddressSpace = @import("mmu_aware_virtual_address_space.zig").MMUAwareVirtualAddressSpace; + +pub const VirtualAddressSpace = extern struct { + physical_map: *PhysicalMap, + // TODO: layout + regions: ?*VirtualMemoryRegion = null, + + /// The function is inlined because it's only called once + pub inline fn initializeCurrent() !void { + log.debug("VirtualAddressSpace.initializeCurrent", .{}); + const virtual_address_space = user.process.getVirtualAddressSpace(); + const physical_map = user.process.getPhysicalMap(); + virtual_address_space.physical_map = physical_map; + + const root_page_level = 0; + physical_map.* = try PhysicalMap.init(virtual_address_space, root_page_level, user.process.getSlotAllocator()); + // This should be an inline call as this the only time this function is called + try physical_map.initializeCurrent(); + + try virtual_address_space.pinnedInit(); + + log.warn("TODO: VirtualAddressSpace.initializeCurrent is incomplete!", .{}); + } + + pub inline fn pinnedInit(virtual_address_space: *VirtualAddressSpace) !void { + const pinned_state = user.process.getPinnedState(); + const pinned_size = 128 * lib.mb; + pinned_state.physical_memory_region = try PhysicalMemoryRegion.Pinned.new(pinned_size); + + pinned_state.virtual_memory_region = try virtual_address_space.map(pinned_state.physical_memory_region.getGeneric().*, 0, pinned_size, .{ .write = true }); + log.warn("TODO: VirtualAddressSpace.pinnedInit", .{}); + } + + pub inline fn map(virtual_address_space: *VirtualAddressSpace, physical_memory_region: PhysicalMemoryRegion, offset: usize, size: usize, flags: VirtualMemoryRegion.Flags) !VirtualMemoryRegion { + const alignment = lib.arch.valid_page_sizes[0]; + return virtual_address_space.mapAligned(physical_memory_region, offset, size, alignment, flags); + } + + pub fn mapAligned(virtual_address_space: *VirtualAddressSpace, physical_memory_region: PhysicalMemoryRegion, offset: usize, size: usize, alignment: usize, flags: VirtualMemoryRegion.Flags) !VirtualMemoryRegion { + const virtual_address = try virtual_address_space.physical_map.determineAddress(physical_memory_region, alignment); + _ = virtual_address; + _ = offset; + _ = size; + _ = flags; + @panic("TODO: VirtualAddressSpace.mapAligned"); + } + + pub const State = extern struct { + virtual_address_space: VirtualAddressSpace, + physical_map: PhysicalMap, + }; +}; diff --git a/tools/format_loopback_fat32.sh b/tools/format_loopback_fat32.sh new file mode 100755 index 0000000..461dff7 --- /dev/null +++ b/tools/format_loopback_fat32.sh @@ -0,0 +1,4 @@ +#!/bin/sh +# $1 Loopback device +set -e +sudo mkfs.fat -F 32 `cat $1`p1 1>2 diff --git a/tools/loopback_end.sh b/tools/loopback_end.sh new file mode 100755 index 0000000..a729ed9 --- /dev/null +++ b/tools/loopback_end.sh @@ -0,0 +1,4 @@ +#!/bin/sh +# $1 = Loopback device +set -e +sudo losetup -d `cat $1` diff --git a/tools/loopback_mount.sh b/tools/loopback_mount.sh new file mode 100755 index 0000000..4060318 --- /dev/null +++ b/tools/loopback_mount.sh @@ -0,0 +1,6 @@ +#!/bin/sh +# $1 = Loopback device +# $2 = Mount directory +set -e +#echo "Mounting loopback device $1 in directory $2..." +sudo mount `cat $1`p1 $2 diff --git a/tools/loopback_start.sh b/tools/loopback_start.sh new file mode 100755 index 0000000..efc2327 --- /dev/null +++ b/tools/loopback_start.sh @@ -0,0 +1,6 @@ +#!/bin/sh +# $1 = Disk image +# $2 = Loopback device +set -e +#echo "Starting loopback device $2 with image $1..." +sudo losetup -Pf --show $1 > $2

    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 z1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd z0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwA zz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEj zFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r z3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@ z0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VK zfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5 zV8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM z7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b* z1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd z0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwA zz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEj zFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r z3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@ z0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VK zfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5 zV8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM z7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b* z1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd z0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwA zz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEj zFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r z3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@ z0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7&r_B D<%we- literal 0 HcmV?d00001 diff --git a/src/bootloader/limine/installables/limine-cd.bin b/src/bootloader/limine/installables/limine-cd.bin new file mode 100644 index 0000000000000000000000000000000000000000..2de50bff8124a2cd51f3cb0ed6dcbb5d41e3a809 GIT binary patch literal 24576 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%0DpV*zT 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

yL05P_V|dJQhhv$Vb83Xg-B9|F*`At8okWj_8Kd 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 z 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%0D1GP3O=dnd&G<5X`2K4Vo8ZlT}7 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%0A-st=4 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 E1Ey2bjsO4v literal 0 HcmV?d00001 diff --git a/resources/zap-light16.psf b/resources/zap-light16.psf new file mode 100644 index 0000000000000000000000000000000000000000..047bd1b0be57f5165922969887fd5c6b3ede3304 GIT binary patch literal 5312 zcmZvfYj|8m8ONumYuWPTX}2NR#ja$!-P22gWqTn_!x}b+CGLjWrV9ZrErg^*Q7#pg zR#-M)dH5vt6W^#dDpo?=ZroX2GPqlk>dUR5~-O%TA{=x~wy4)o?nKPI)+}R{f?) z&a6~suyM9AC~c84#mtiKRJwcR*kaFX>CDQSms*V8lP)g2tIeL4q?CK8LP3I@JEhY_ zrOv~1*m$S+7`IpwMUvC_SREWZSj}YmCMF)R`sEuYw%fO4UaQ5L8DRXa#0)TglbE^g zmO;_qLbYAD-a9RwH8UC72ui9H1m*J9AmEiG`Oqk8bjb695`%DqWmr+{tR247Qoq>c zQQyoDP9<9q>!_Z6+RAC>Ni@Ccm~jf&AhJh{Kke44)!~H;Yi>r@jg4(gr(eHj&8Ur+ z#WTD5s$g`pXrjBBvR0L%z@&6KB}JNHo0Q3D=aR1V<@#!^3CBDO&Kf-^X+>^ErLAXF z!Shn4O|7ks`3c7A(frcG>}o=hTJtB{TbEr-{Jk7=Z`VVqR98=-Qfa1Aru#}Y%jI&z zlf|*|a~e(fNH?PnxZXekizj(MF#JX(l zrhdGi+0i7^oNKRhKld`Tq)NrjM^LGbPfm_kE6!h zY;1JY`P05lZhh?7BtqOjvG15E%{K*NZ&WLcGPb+(WVLKXo2<6M$z6OplF#RRdZP71 z*J=|J)6-G^0?vn-bX#BCaVA+U`$e{&o6np*Et2PqgEl87mGV3z2W?*@mkCSvb;Qr5l~yHw)5cNLR5TS)5lTaua| zfkb^oLeom`>T0!FC2g}!vO8olnSaJX`|qn)VZ#_7a-0d$y7d0PRX8S=tT05BJG{L3d@Z8+tN9Jri znZ!hr9h)d%=;rG}Zb#B!C6(3XR{Q8da}gOUbvy27itUQb?66nH@I9oV^GT+L$)da6 zjz4j}GsSVaQDWc0iv04mv=|tD>#K4TYqM1rXj4>YpjO+)dddC5e(&55+{1iYmD}z= z{6bf^{Wg*JDCA;ii1g_WNBMG>6cwMgGS*T3*xnH@j12DC*`LUMe?Iva`MKugagHKu zJ%=GG3Zq&*9m&P*%`CMBr_ZELCLEV$dacEko=lxd=%rEp*zW4Pc%eF#hAbgoaP&Bd~d(3fjzmC>_0{`JKzojRLxW-c5~Tua z+kL>4q;oR9_U{GPKRMFI?)n#N*S}axeRtyo4nH zgg)-itn~X=^@rh66^6&)%kWY7ID85A&kIAX!*DqiA-_BfSMuBpLy*(GdVuE}^%3ty z?o?-xU`HVjABKIfr`&y-Z*XuA`P1zdgQ(N^?#?;UICF+Ft25fIqpX7ur(cSt2 zeWmWkx>gk^zf@K9g{UIF>tH=>fQ@h&48SHBgv~Id2JpU|w-UTrl^NYIZ&yGWMj(JI zp#oRID7*!#P=hhJTJDgphil+kn1JixdUzYW9d^JCuv5LdCJcA+ zcB6XIJMPV2!Hl9N)f3(^?^!Ram+8xN7~V|T6imZzxCLh59q>-L72XB!hTGtFxC8El zJ@6j53*HOwgZIPTuow2h2jCvK7aD2>GjtzsP4$Au%IS;pCwz7wC0~QD!#ChbcuMs% z3&(l;CVUH?hG*be_%?h8o`VzcU6o4#7cq3?5fcGn!AS=gB{f{tSE;?xW7< z)ltsZ7f@eRtMpU(b?k~J<%cOb0#864eGTk`ub`FRqc7Btvtk~->hfn|q1-oJo+hrg73m%ogB(qBP7 z?e~+<_#4P?^)DyC&A*!bet2+s7=8#o2>anz^z_&88~82!4t@`RfIq^Y;Lq?E_$&Mk m{thp}Kj5G6GQ0x+f|Kwn{2NZeY4{KP7ybul;59f4A^aazON1E! literal 0 HcmV?d00001 diff --git a/src/bootloader.zig b/src/bootloader.zig new file mode 100644 index 0000000..ea7e009 --- /dev/null +++ b/src/bootloader.zig @@ -0,0 +1,796 @@ +const bootloader = @This(); + +pub const arch = @import("bootloader/arch.zig"); + +const lib = @import("lib"); +const Allocator = lib.Allocator; +const assert = lib.assert; +//const Allocator = lib.Allocator; +pub const Protocol = lib.Bootloader.Protocol; + +const privileged = @import("privileged"); +const ACPI = privileged.ACPI; +const CPUPageTables = privileged.arch.CPUPageTables; +const PageAllocator = privileged.PageAllocator; +const PhysicalAddress = lib.PhysicalAddress; +const VirtualAddress = lib.VirtualAddress; +const PhysicalMemoryRegion = lib.PhysicalMemoryRegion; +pub const paging = privileged.arch.paging; + +pub const Version = extern struct { + patch: u8, + minor: u16, + major: u8, +}; + +pub const CompactDate = packed struct(u16) { + year: u7, + month: u4, + day: u5, +}; + +const file_alignment = lib.arch.valid_page_sizes[0]; +const last_struct_offset = @offsetOf(Information, "slices"); + +pub const Information = extern struct { + entry_point: u64 align(8), + higher_half: u64 align(8), + total_size: u32, + last_struct_offset: u32 = last_struct_offset, + version: Version, + protocol: lib.Bootloader.Protocol, + bootloader: lib.Bootloader, + stage: Stage, + configuration: packed struct(u32) { + memory_map_diff: u8, + reserved: u24 = 0, + }, + cpu_driver_mappings: CPUDriverMappings, + framebuffer: Framebuffer, + //draw_context: DrawContext, + //font: Font, + smp: SMP.Information, + architecture: Architecture, + cpu_page_tables: CPUPageTables, + slices: lib.EnumStruct(Slice.Name, Slice), + + pub const Architecture = switch (lib.cpu.arch) { + .x86, .x86_64 => extern struct { + rsdp_address: u64, + }, + .aarch64 => extern struct { + foo: u64 = 0, + }, + .riscv64 => extern struct { + foo: u64 = 0, + }, + else => @compileError("Architecture not supported"), + }; + + pub const Slice = extern struct { + offset: u32 = 0, + size: u32 = 0, + len: u32 = 0, + alignment: u32 = 1, + + pub const Name = enum { + bootloader_information, // The main struct + memory_map_entries, + page_counters, + smps, + file_list, + bundle, + }; + + pub const count = lib.enumCount(Name); + + pub const TypeMap = blk: { + var arr: [Slice.count]type = undefined; + arr[@intFromEnum(Slice.Name.bootloader_information)] = Information; + arr[@intFromEnum(Slice.Name.bundle)] = u8; + arr[@intFromEnum(Slice.Name.file_list)] = u8; + arr[@intFromEnum(Slice.Name.memory_map_entries)] = MemoryMapEntry; + arr[@intFromEnum(Slice.Name.page_counters)] = u32; + arr[@intFromEnum(Slice.Name.smps)] = SMP; + break :blk arr; + }; + + pub inline fn dereference(slice: Slice, comptime slice_name: Slice.Name, bootloader_information: *const Information) []Slice.TypeMap[@intFromEnum(slice_name)] { + const Type = Slice.TypeMap[@intFromEnum(slice_name)]; + const address = @intFromPtr(bootloader_information) + slice.offset; + return @as([*]Type, @ptrFromInt(address))[0..slice.len]; + } + }; + + pub const SMP = extern struct { + acpi_id: u32, + lapic_id: u32, + entry_point: u64, + argument: u64, + + pub const Information = switch (lib.cpu.arch) { + .x86, .x86_64 => extern struct { + cpu_count: u32, + bsp_lapic_id: u32, + }, + .aarch64 => extern struct { + cpu_count: u32, + }, + .riscv64 => extern struct { + cpu_count: u32, + }, + else => @compileError("Architecture not supported"), + }; + + pub const Trampoline = extern struct { + comptime { + assert(lib.cpu.arch == .x86 or lib.cpu.arch == .x86_64); + } + + pub const Argument = switch (lib.cpu.arch) { + .x86, .x86_64 => extern struct { + hhdm: u64 align(8), + cr3: u32, + reserved: u16 = 0, + gdt_descriptor: arch.x86_64.GDT.Descriptor, + gdt: arch.x86_64.GDT, + + comptime { + assert(@sizeOf(Argument) == 24 + @sizeOf(arch.x86_64.GDT)); + } + }, + else => {}, + }; + }; + }; + + fn initializeMemoryMap(bootloader_information: *bootloader.Information, init: anytype) !usize { + try init.deinitializeMemoryMap(); + + const memory_map_entries = bootloader_information.getSlice(.memory_map_entries); + var entry_index: usize = 0; + while (try init.memory_map.next()) |entry| : (entry_index += 1) { + memory_map_entries[entry_index] = entry; + } + + return entry_index; + } + + pub fn initialize(initialization: anytype, comptime bootloader_tag: lib.Bootloader, comptime protocol: Protocol) !noreturn { + assert(@typeInfo(@TypeOf(initialization)) == .Pointer); + assert(initialization.early_initialized); + lib.log.info("Booting with bootloader {s} and boot protocol {s}", .{ @tagName(bootloader_tag), @tagName(protocol) }); + + assert(initialization.framebuffer_initialized); + assert(initialization.memory_map_initialized); + assert(initialization.filesystem_initialized); + + const sector_size = initialization.filesystem.getSectorSize(); + + const file_list_file_size = try initialization.filesystem.getFileSize("/files"); + const file_list_peek = try initialization.filesystem.sneakFile("/files", file_list_file_size); + assert(file_list_peek.len > 0); + var stream = lib.fixedBufferStream(file_list_peek); + const file_list_reader = stream.reader(); + const bundle_uncompressed_size = try file_list_reader.readIntLittle(u32); + assert(bundle_uncompressed_size > 0); + const bundle_compressed_size = try file_list_reader.readIntLittle(u32); + assert(bundle_compressed_size > 0); + const bundle_file_count = try file_list_reader.readIntLittle(u32); + assert(bundle_file_count > 0); + + const decompressor_state_allocation_size = 300 * lib.kb; + + const memory_map_entry_count = initialization.memory_map.getEntryCount(); + const cpu_count = try initialization.getCPUCount(); + + const length_size_tuples = bootloader.LengthSizeTuples.new(.{ + .bootloader_information = .{ + .length = 1, + .alignment = @alignOf(bootloader.Information), + }, + .memory_map_entries = .{ + .length = memory_map_entry_count, + .alignment = @alignOf(bootloader.MemoryMapEntry), + }, + .smps = .{ + .length = cpu_count, + .alignment = @max(@sizeOf(u64), @alignOf(bootloader.Information.SMP.Information)), + }, + .page_counters = .{ + .length = memory_map_entry_count, + .alignment = @alignOf(u32), + }, + .file_list = .{ + .length = file_list_file_size, + .alignment = 1, + }, + .bundle = .{ + .length = bundle_uncompressed_size, + .alignment = file_alignment, + }, + }); + + const extra_sizes = [2]usize{ decompressor_state_allocation_size, bundle_compressed_size }; + const aligned_extra_sizes = blk: { + var result: [extra_sizes.len]usize = undefined; + inline for (extra_sizes, &result) |extra_size, *element| { + element.* = lib.alignForward(usize, extra_size, sector_size); + } + + break :blk result; + }; + + const total_aligned_extra_size = blk: { + var result: usize = 0; + inline for (aligned_extra_sizes) |size| { + result += size; + } + + break :blk result; + }; + + var early_mmap_index: usize = 0; + const length_size_tuples_size = length_size_tuples.getAlignedTotalSize(); + const total_allocation_size = length_size_tuples_size + total_aligned_extra_size; + const total_allocation = blk: while (try initialization.memory_map.next()) |entry| : (early_mmap_index += 1) { + if (entry.type == .usable) { + if (entry.region.size >= total_allocation_size) { + break :blk .{ + .index = early_mmap_index, + .region = entry.region, + }; + } + } + } else { + return error.OutOfMemory; + }; + + const bootloader_information = total_allocation.region.address.toIdentityMappedVirtualAddress().access(*bootloader.Information); + bootloader_information.* = bootloader.Information{ + .protocol = protocol, + .bootloader = bootloader_tag, + .version = .{ .major = 0, .minor = 1, .patch = 0 }, + .total_size = length_size_tuples.total_size, + .entry_point = 0, + .higher_half = lib.config.cpu_driver_higher_half_address, + .stage = .early, + .configuration = .{ + .memory_map_diff = 0, + }, + .framebuffer = initialization.framebuffer, + // .draw_context = .{}, + // .font = undefined, + .cpu_driver_mappings = .{}, + .cpu_page_tables = undefined, + .smp = switch (lib.cpu.arch) { + .x86, .x86_64 => .{ + .cpu_count = cpu_count, + .bsp_lapic_id = @as(*volatile u32, @ptrFromInt(0x0FEE00020)).*, + }, + else => @compileError("Architecture not supported"), + }, + .slices = length_size_tuples.createSlices(), + .architecture = switch (lib.cpu.arch) { + .x86, .x86_64 => .{ + .rsdp_address = initialization.getRSDPAddress(), + }, + else => @compileError("Architecture not supported"), + }, + }; + + const page_counters = bootloader_information.getSlice(.page_counters); + @memset(page_counters, 0); + + // Make sure pages are allocated to host the bootloader information and fetch memory entries from firmware (only non-UEFI) + if (bootloader_tag != .rise or protocol != .uefi) { + page_counters[total_allocation.index] = bootloader_information.getAlignedTotalSize() >> lib.arch.page_shifter(lib.arch.valid_page_sizes[0]); + + const new_memory_map_entry_count = try bootloader_information.initializeMemoryMap(initialization); + + if (new_memory_map_entry_count != memory_map_entry_count) @panic("Memory map entry count mismatch"); + } + + const file_list = bootloader_information.getSlice(.file_list); + if (file_list_peek.len == file_list_file_size) { + lib.memcpy(file_list, file_list_peek); + } else { + @panic("Not able to fit in the cache"); + } + + try initialization.filesystem.deinitialize(); + + const bootloader_information_total_aligned_size = bootloader_information.getAlignedTotalSize(); + const extra_allocation_region = total_allocation.region.offset(bootloader_information_total_aligned_size).shrinked(total_aligned_extra_size); + const decompressor_state_buffer = extra_allocation_region.toIdentityMappedVirtualAddress().access(u8)[0..decompressor_state_allocation_size]; + const compressed_bundle_buffer = extra_allocation_region.offset(decompressor_state_allocation_size).toIdentityMappedVirtualAddress().access(u8)[0..lib.alignForward(usize, bundle_compressed_size, sector_size)]; + const compressed_bundle = try initialization.filesystem.readFile("/bundle", compressed_bundle_buffer); + assert(compressed_bundle.len > 0); + + if (bootloader_tag == .rise and protocol == .uefi) { + // Check if the memory map entry count matches here is not useful because probably it's going to be less as exiting boot services seems + // like making some deallocations + const new_memory_map_entry_count = @as(u32, @intCast(try bootloader_information.initializeMemoryMap(initialization))); + if (new_memory_map_entry_count > memory_map_entry_count) { + return Error.unexpected_memory_map_entry_count; + } + bootloader_information.configuration.memory_map_diff = @as(u8, @intCast(memory_map_entry_count - new_memory_map_entry_count)); + } + + // Check if the host entry still corresponds to the same index + const memory_map_entries = bootloader_information.getMemoryMapEntries(); + const expected_host_region = memory_map_entries[total_allocation.index].region; + assert(expected_host_region.address.value() == total_allocation.region.address.value()); + assert(expected_host_region.size == total_allocation.region.size); + + var compressed_bundle_stream = lib.fixedBufferStream(compressed_bundle); + const decompressed_bundle = bootloader_information.getSlice(.bundle); + assert(decompressed_bundle.len != 0); + var decompressor_state_allocator = lib.FixedBufferAllocator.init(decompressor_state_buffer); + var decompressor = try lib.deflate.decompressor(decompressor_state_allocator.allocator(), compressed_bundle_stream.reader(), null); + const bytes = try decompressor.reader().readAll(decompressed_bundle); + assert(bytes == bundle_uncompressed_size); + if (decompressor.close()) |err| { + return err; + } + + // Empty region as this is no longer needed. Region was not marked as allocated so no need + // to unmark it + const free_slice = extra_allocation_region.toIdentityMappedVirtualAddress().access(u8); + @memset(free_slice, 0); + + const page_allocator = PageAllocator{ + .allocate = Information.callbackAllocatePages, + .context = bootloader_information, + .context_type = .bootloader, + }; + bootloader_information.cpu_page_tables = try CPUPageTables.initialize(page_allocator); + + const minimal_paging = privileged.arch.paging.Specific.fromPageTables(bootloader_information.cpu_page_tables); + + const cpu_file_descriptor = try bootloader_information.getFileDescriptor("cpu_driver"); + var elf_parser = try lib.ELF(64).Parser.init(cpu_file_descriptor.content); + const program_headers = elf_parser.getProgramHeaders(); + + for (program_headers) |*ph| { + switch (ph.type) { + .load => { + if (ph.size_in_memory == 0) continue; + + if (!ph.flags.readable) { + @panic("ELF program segment is marked as non-readable"); + } + + if (ph.size_in_file != ph.size_in_memory) { + @panic("ELF program segment file size is smaller than memory size"); + } + + const aligned_size = lib.alignForward(u64, ph.size_in_memory, lib.arch.valid_page_sizes[0]); + const physical_allocation = try bootloader_information.allocatePages(aligned_size, lib.arch.valid_page_sizes[0], .{}); + const physical_address = physical_allocation.address; + const virtual_address = VirtualAddress.new(ph.virtual_address); + const flags = Mapping.Flags{ .write = ph.flags.writable, .execute = ph.flags.executable }; + + switch (ph.flags.executable) { + true => switch (ph.flags.writable) { + true => @panic("Text section is not supposed to be writable"), + false => { + bootloader_information.cpu_driver_mappings.text = .{ + .physical = physical_address, + .virtual = virtual_address, + .size = ph.size_in_memory, + .flags = flags, + }; + }, + }, + false => switch (ph.flags.writable) { + true => bootloader_information.cpu_driver_mappings.data = .{ + .physical = physical_address, + .virtual = virtual_address, + .size = ph.size_in_memory, + .flags = flags, + }, + false => bootloader_information.cpu_driver_mappings.rodata = .{ + .physical = physical_address, + .virtual = virtual_address, + .size = ph.size_in_memory, + .flags = flags, + }, + }, + } + + // log.debug("Started mapping kernel section", .{}); + try bootloader_information.cpu_page_tables.map(physical_address, virtual_address, aligned_size, flags); + // log.debug("Ended mapping kernel section", .{}); + + const dst_slice = physical_address.toIdentityMappedVirtualAddress().access([*]u8)[0..lib.safeArchitectureCast(ph.size_in_memory)]; + const src_slice = cpu_file_descriptor.content[lib.safeArchitectureCast(ph.offset)..][0..lib.safeArchitectureCast(ph.size_in_file)]; + // log.debug("Src slice: [0x{x}, 0x{x}]. Dst slice: [0x{x}, 0x{x}]", .{ @ptrToInt(src_slice.ptr), @ptrToInt(src_slice.ptr) + src_slice.len, @ptrToInt(dst_slice.ptr), @ptrToInt(dst_slice.ptr) + dst_slice.len }); + if (!(dst_slice.len >= src_slice.len)) { + @panic("bios: segment allocated memory must be equal or greater than especified"); + } + + lib.memcpy(dst_slice, src_slice); + }, + else => { + //log.warn("Unhandled PH {s}", .{@tagName(ph.type)}); + }, + } + } + + //for (bootloader_information.getMemoryMapEntries()[0..memory_map_entry_count]) |entry| { + for (bootloader_information.getMemoryMapEntries()) |entry| { + if (entry.type == .usable) { + try minimal_paging.map(entry.region.address, entry.region.address.toHigherHalfVirtualAddress(), lib.alignForward(u64, entry.region.size, lib.arch.valid_page_sizes[0]), .{ .write = true, .execute = false }, page_allocator); + } + } + + try minimal_paging.map(total_allocation.region.address, total_allocation.region.address.toIdentityMappedVirtualAddress(), bootloader_information.getAlignedTotalSize(), .{ .write = true, .execute = false }, page_allocator); + try initialization.ensureLoaderIsMapped(minimal_paging, page_allocator, bootloader_information); + + const framebuffer_physical_address = PhysicalAddress.new(if (bootloader_information.bootloader == .limine) bootloader_information.framebuffer.address - lib.config.cpu_driver_higher_half_address else bootloader_information.framebuffer.address); + try minimal_paging.map(framebuffer_physical_address, framebuffer_physical_address.toHigherHalfVirtualAddress(), lib.alignForward(u64, bootloader_information.framebuffer.getSize(), lib.arch.valid_page_sizes[0]), .{ .write = true, .execute = false }, page_allocator); + bootloader_information.framebuffer.address = framebuffer_physical_address.toHigherHalfVirtualAddress().value(); + + try initialization.ensureStackIsMapped(minimal_paging, page_allocator); + + switch (lib.cpu.arch) { + .x86, .x86_64 => { + const apic_base_physical_address = privileged.arch.x86_64.registers.IA32_APIC_BASE.read().getAddress(); + try minimal_paging.map(apic_base_physical_address, apic_base_physical_address.toHigherHalfVirtualAddress(), lib.arch.valid_page_sizes[0], .{ + .write = true, + .cache_disable = true, + .global = true, + }, page_allocator); + }, + else => @compileError("Not supported"), + } + + // bootloader_information.initializeSMP(madt); + + bootloader_information.entry_point = elf_parser.getEntryPoint(); + + if (bootloader_information.entry_point != 0) { + lib.log.info("Jumping to kernel...", .{}); + bootloader.arch.x86_64.jumpToKernel(bootloader_information, minimal_paging); + } else return Error.no_entry_point_found; + } + + pub const Error = error{ + file_not_found, + filesystem_initialization_failed, + unexpected_memory_map_entry_count, + no_entry_point_found, + }; + + pub fn getAlignedTotalSize(information: *Information) u32 { + if (information.total_size == 0) @panic("Information.getAlignedTotalSize"); + return lib.alignForward(u32, information.total_size, lib.arch.valid_page_sizes[0]); + } + + pub inline fn getSlice(information: *const Information, comptime offset_name: Slice.Name) []Slice.TypeMap[@intFromEnum(offset_name)] { + const slice_offset = information.slices.array.values[@intFromEnum(offset_name)]; + return slice_offset.dereference(offset_name, information); + } + + pub fn getMemoryMapEntryCount(information: *Information) usize { + return information.getSlice(.memory_map_entries).len - information.configuration.memory_map_diff; + } + + pub fn getMemoryMapEntries(information: *Information) []MemoryMapEntry { + return information.getSlice(.memory_map_entries)[0..information.getMemoryMapEntryCount()]; + } + + pub fn getPageCounters(information: *Information) []u32 { + return information.getSlice(.page_counters)[0..information.getMemoryMapEntryCount()]; + } + + pub const IntegrityError = error{ + bad_slice_alignment, + bad_slice_size, + bad_total_size, + bad_struct_offset, + }; + + pub fn checkIntegrity(information: *const Information) !void { + if (information.last_struct_offset != last_struct_offset) { + return IntegrityError.bad_struct_offset; + } + + const original_total_size = information.total_size; + var total_size: u32 = 0; + inline for (Information.Slice.TypeMap, 0..) |T, index| { + const slice = information.slices.array.values[index]; + + if (slice.alignment < @alignOf(T)) { + return IntegrityError.bad_slice_alignment; + } + + if (slice.len * @sizeOf(T) != slice.size) { + return IntegrityError.bad_slice_size; + } + + total_size = lib.alignForward(u32, total_size, slice.alignment); + total_size += lib.alignForward(u32, slice.size, slice.alignment); + } + + if (total_size != original_total_size) { + return IntegrityError.bad_total_size; + } + } + + pub fn allocatePages(bootloader_information: *Information, size: u64, alignment: u64, options: PageAllocator.AllocateOptions) Allocator.Allocate.Error!PhysicalMemoryRegion { + const allocation = blk: { + if (bootloader_information.stage != .cpu) { + if (size & lib.arch.page_mask(lib.arch.valid_page_sizes[0]) != 0) return Allocator.Allocate.Error.OutOfMemory; + if (alignment & lib.arch.page_mask(lib.arch.valid_page_sizes[0]) != 0) return Allocator.Allocate.Error.OutOfMemory; + + const four_kb_pages = @as(u32, @intCast(@divExact(size, lib.arch.valid_page_sizes[0]))); + + const entries = bootloader_information.getMemoryMapEntries(); + const page_counters = bootloader_information.getPageCounters(); + + for (entries, 0..) |entry, entry_index| { + const busy_size = @as(u64, page_counters[entry_index]) * lib.arch.valid_page_sizes[0]; + const size_left = entry.region.size - busy_size; + const target_address = entry.region.address.offset(busy_size); + + if (entry.type == .usable and target_address.value() <= lib.maxInt(usize) and size_left > size and entry.region.address.value() != 0) { + if (entry.region.address.isAligned(alignment)) { + const result = PhysicalMemoryRegion.new(.{ + .address = target_address, + .size = size, + }); + + @memset(@as([*]u8, @ptrFromInt(lib.safeArchitectureCast(result.address.value())))[0..lib.safeArchitectureCast(result.size)], 0); + + page_counters[entry_index] += four_kb_pages; + + break :blk result; + } + } + } + + if (options.space_waste_allowed_to_guarantee_alignment > 0) { + for (entries, 0..) |entry, entry_index| { + const busy_size = @as(u64, page_counters[entry_index]) * lib.arch.valid_page_sizes[0]; + const size_left = entry.region.size - busy_size; + const target_address = entry.region.address.offset(busy_size); + + if (entry.type == .usable and target_address.value() <= lib.maxInt(usize) and size_left > size and entry.region.address.value() != 0) { + const aligned_address = lib.alignForward(u64, target_address.value(), alignment); + const difference = aligned_address - target_address.value(); + const allowed_quota = alignment / options.space_waste_allowed_to_guarantee_alignment; + + if (aligned_address + size < entry.region.address.offset(entry.region.size).value() and difference <= allowed_quota) { + const result = PhysicalMemoryRegion.new(.{ + .address = PhysicalAddress.new(aligned_address), + .size = size, + }); + + @memset(@as([*]u8, @ptrFromInt(lib.safeArchitectureCast(result.address.value())))[0..lib.safeArchitectureCast(result.size)], 0); + page_counters[entry_index] += @as(u32, @intCast(difference + size)) >> lib.arch.page_shifter(lib.arch.valid_page_sizes[0]); + + break :blk result; + } + } + } + } + } + + return Allocator.Allocate.Error.OutOfMemory; + }; + + return allocation; + } + + pub fn callbackAllocatePages(context: ?*anyopaque, size: u64, alignment: u64, options: PageAllocator.AllocateOptions) Allocator.Allocate.Error!PhysicalMemoryRegion { + const bootloader_information = @as(*Information, @ptrCast(@alignCast(context))); + return try bootloader_information.allocatePages(size, alignment, options); + } + + pub fn heapAllocate(bootloader_information: *Information, size: u64, alignment: u64) !Allocator.Allocate.Result { + if (bootloader_information.stage != .cpu) { + for (&bootloader_information.heap.regions) |*region| { + if (region.size > size) { + const result = .{ + .address = region.address.value(), + .size = size, + }; + region.size -= size; + region.address.addOffset(size); + return result; + } + } + const size_to_page_allocate = lib.alignForward(u64, size, lib.arch.valid_page_sizes[0]); + for (&bootloader_information.heap.regions) |*region| { + if (region.size == 0) { + const allocated_region = try bootloader_information.page_allocator.allocateBytes(size_to_page_allocate, lib.arch.valid_page_sizes[0]); + region.* = .{ + .address = PhysicalAddress.new(allocated_region.address), + .size = allocated_region.size, + }; + const result = .{ + .address = region.address.value(), + .size = size, + }; + region.address.addOffset(size); + region.size -= size; + return result; + } + } + + _ = alignment; + } + + return Allocator.Allocate.Error.OutOfMemory; + } + + pub fn getFileDescriptor(bootloader_information: *bootloader.Information, wanted_file_name: []const u8) !FileDescriptor { + const file_list = bootloader_information.getSlice(.file_list); + + var index: usize = 0; + index += 2 * @sizeOf(u32); + const file_count = @as(*align(1) const u32, @ptrCast(&file_list[index])).*; + index += @sizeOf(u32); + var file_index: u32 = 0; + + while (file_index < file_count) : (file_index += 1) { + const file_name_len_offset = 2 * @sizeOf(u32); + const file_name_len = file_list[index + file_name_len_offset]; + const file_name_offset = file_name_len_offset + @sizeOf(u8); + const file_name = file_list[index + file_name_offset ..][0..file_name_len]; + + if (lib.equal(u8, wanted_file_name, file_name)) { + const file_offset = @as(*align(1) const u32, @ptrCast(&file_list[index + 0])).*; + const file_size = @as(*align(1) const u32, @ptrCast(&file_list[index + @sizeOf(u32)])).*; + const bundle = bootloader_information.getSlice(.bundle); + const file_content = bundle[file_offset..][0..file_size]; + + return FileDescriptor{ + .name = file_name, + .content = file_content, + }; + } + + const offset_to_add = file_name_offset + file_name.len; + index += offset_to_add; + } + + return Error.file_not_found; + } +}; + +pub const FileDescriptor = struct { + name: []const u8, + content: []const u8, +}; + +pub const CPUDriverMappings = extern struct { + text: Mapping = .{}, + data: Mapping = .{}, + rodata: Mapping = .{}, +}; + +const Mapping = privileged.Mapping; + +pub const MemoryMapEntry = extern struct { + region: PhysicalMemoryRegion align(8), + type: Type align(8), + + const Type = enum(u64) { + usable = 0, + reserved = 1, + bad_memory = 2, + }; + + pub fn getFreeRegion(mmap_entry: MemoryMapEntry, page_counter: u32) PhysicalMemoryRegion { + return mmap_entry.region.offset(page_counter << lib.arch.page_shifter(lib.arch.valid_page_sizes[0])); + } + + comptime { + assert(@sizeOf(MemoryMapEntry) == @sizeOf(u64) * 3); + } +}; + +pub const Framebuffer = extern struct { + address: u64, + pitch: u32, + width: u32, + height: u32, + bpp: u16, + red_mask: ColorMask, + green_mask: ColorMask, + blue_mask: ColorMask, + memory_model: u8, + reserved: u8 = 0, + + pub const ColorMask = extern struct { + size: u8 = 0, + shift: u8 = 0, + }; + + pub const VideoMode = extern struct { + foo: u32 = 0, + }; + + pub inline fn getSize(framebuffer: Framebuffer) u32 { + return framebuffer.pitch * framebuffer.height; + } +}; + +pub const LengthSizeTuples = extern struct { + tuples: Tuples, + total_size: u32 = 0, + + const Tuples = lib.EnumStruct(Information.Slice.Name, Tuple); + + const count = Information.Slice.count; + + pub const Tuple = extern struct { + length: u32, + alignment: u32, + size: u32 = 0, + reserved: u32 = 0, + }; + + pub fn new(fields: Tuples.Struct) LengthSizeTuples { + var tuples = LengthSizeTuples{ + .tuples = .{ + .fields = fields, + }, + }; + + var total_size: u32 = 0; + + inline for (Information.Slice.TypeMap, 0..) |T, index| { + const tuple = &tuples.tuples.array.values[index]; + const size = tuple.length * @sizeOf(T); + tuple.alignment = if (tuple.alignment < @alignOf(T)) @alignOf(T) else tuple.alignment; + total_size = lib.alignForward(u32, total_size, tuple.alignment); + total_size += lib.alignForward(u32, size, tuple.alignment); + tuple.size = size; + } + + tuples.total_size = total_size; + + return tuples; + } + + pub fn createSlices(tuples: LengthSizeTuples) lib.EnumStruct(Information.Slice.Name, Information.Slice) { + var slices = lib.zeroes(lib.EnumStruct(Information.Slice.Name, Information.Slice)); + var allocated_size: u32 = 0; + + for (&slices.array.values, 0..) |*slice, index| { + const tuple = tuples.tuples.array.values[index]; + const length = tuple.length; + const size = lib.alignForward(u32, tuple.size, tuple.alignment); + + allocated_size = lib.alignForward(u32, allocated_size, tuple.alignment); + slice.* = .{ + .offset = allocated_size, + .len = length, + .size = tuple.size, + .alignment = tuple.alignment, + }; + + allocated_size += size; + } + + if (allocated_size != tuples.total_size) @panic("Extra allocation size must match bootloader allocated extra size"); + + return slices; + } + + pub fn getAlignedTotalSize(tuples: LengthSizeTuples) u32 { + if (tuples.total_size == 0) @panic("LengthSizeTuples.getAlignedTotalSize"); + return lib.alignForward(u32, tuples.total_size, lib.arch.valid_page_sizes[0]); + } +}; + +pub const Stage = enum(u32) { + early = 0, + only_graphics = 1, + trampoline = 2, + cpu = 3, +}; diff --git a/src/bootloader/arch.zig b/src/bootloader/arch.zig new file mode 100644 index 0000000..a0630e2 --- /dev/null +++ b/src/bootloader/arch.zig @@ -0,0 +1,2 @@ +pub const x86 = @import("arch/x86.zig"); +pub const x86_64 = @import("arch/x86_64.zig"); diff --git a/src/bootloader/arch/x86.zig b/src/bootloader/arch/x86.zig new file mode 100644 index 0000000..e69de29 diff --git a/src/bootloader/arch/x86/64/smp_trampoline.S b/src/bootloader/arch/x86/64/smp_trampoline.S new file mode 100644 index 0000000..d5cc010 --- /dev/null +++ b/src/bootloader/arch/x86/64/smp_trampoline.S @@ -0,0 +1,135 @@ +.section .smp_trampoline +.align 0x1000 + +.global smp_trampoline +.global smp_trampoline_arg_start +.global smp_trampoline_arg_end +.global smp_gdt_descriptor +.global smp_core_booted +.global smp_trampoline_end + +.code16 +smp_trampoline: +cli +cld + +mov %cs, %ebx +shl $0x4, %ebx + +lidtl %cs:(invalid_idt - smp_trampoline) +lgdtl %cs:(smp_gdt_descriptor - smp_trampoline) +leal (protected_mode - smp_trampoline)(%ebx), %eax +movl %eax, %cs:(far_jump_offset - smp_trampoline) +movl $0x11, %eax +movl %eax, %cr0 +mov %cs:(gdt32_ds - smp_trampoline), %eax +ljmpl *%cs:(far_jump - smp_trampoline) + +far_jump: +far_jump_offset: .long 0 +gdt32_cs: .long 0x18 +gdt32_ds: .long 0x20 + +.code32 +protected_mode: +movw %ax, %ds +movw %ax, %es +movw %ax, %fs +movw %ax, %gs +movw %ax, %ss +xorl %eax, %eax +lldtw %ax +xorl %eax, %eax +movl %eax, %cr4 + +// TODO: Change +// always no x2apic +leal (temporal_stack_top - smp_trampoline)(%ebx), %esp + +// Long mode activation + +// In CR4 +mov %cr4, %eax +bts $0x5, %eax +mov %eax, %cr4 + +// In EFER: +mov $0xc0000080, %ecx +mov $0x900, %eax +xor %edx, %edx +wrmsr + +// Setup CR3 +mov (arg_cr3 - smp_trampoline)(%ebx), %eax +mov %eax, %cr3 + +mov %cr0, %eax +bts $31, %eax +mov %eax, %cr0 + +leal (bits64 - smp_trampoline)(%ebx), %eax +push $0x28 +push %eax +lretl + +.code64 +bits64: +mov $0x30, %rax +mov %rax, %ds +mov %rax, %es +mov %rax, %fs +mov %rax, %gs +mov %rax, %ss + +mov %ebx, %ebx + +// Enable NXE +mov $0xc0000080, %ecx +rdmsr +bts $11, %eax +wrmsr + +// Enable write protect +mov %cr0, %rax +bts $16, %rax +mov %rax, %cr0 + +// TODO: before park +mov $1, %al +lock xchgb (smp_core_booted - smp_trampoline)(%rbx), %al +xor %rax, %rax +cli +hlt + +.align 16 +temporal_stack: +.fill 128, 1, 0 +temporal_stack_top: + +invalid_idt: +.quad 0 +.quad 0 + +.align 16 +smp_trampoline_arg_start: +arg_hhdm: +.quad 0 +arg_cr3: +.long 0 +reserved: .word 0 +smp_gdt_descriptor: +.limit: .word 0 +.address: .quad 0 +smp_gdt: +smp_gdt_null: .quad 0 +smp_gdt_code_16: .quad 0 +smp_gdt_data_16: .quad 0 +smp_gdt_code_32: .quad 0 +smp_gdt_data_32: .quad 0 +smp_gdt_code_64: .quad 0 +smp_gdt_data_64: .quad 0 +smp_trampoline_arg_end: + +smp_core_booted: .byte 0 + +smp_trampoline_end: diff --git a/src/bootloader/arch/x86_64.zig b/src/bootloader/arch/x86_64.zig new file mode 100644 index 0000000..bbe6e20 --- /dev/null +++ b/src/bootloader/arch/x86_64.zig @@ -0,0 +1,150 @@ +const lib = @import("lib"); +const assert = lib.assert; +const bootloader = @import("bootloader"); +const privileged = @import("privileged"); +const paging = privileged.arch.paging; +const x86_64 = privileged.arch.x86_64; + +pub const GDT = extern struct { + null_entry: Entry = Entry.null_entry, + // 0x08 + code_16: Entry = Entry.code_16, + // 0x10 + data_16: Entry = Entry.data_16, + // 0x18 + code_32: Entry = Entry.code_32, + // 0x20 + data_32: Entry = Entry.data_32, + // 0x28 + code_64: Entry = Entry.code_64, + // 0x30 + data_64: Entry = Entry.data_64, + + pub const Entry = x86_64.GDT.Entry; + pub const Descriptor = x86_64.GDT.Descriptor; + + pub fn getDescriptor(gdt: *const GDT) GDT.Descriptor { + return .{ + .limit = @sizeOf(GDT) - 1, + .address = @intFromPtr(gdt), + }; + } +}; + +const code_segment_selector = @offsetOf(GDT, "code_64"); +const data_segment_selector = @offsetOf(GDT, "data_64"); +const entry_point_offset = @offsetOf(bootloader.Information, "entry_point"); +const higher_half_offset = @offsetOf(bootloader.Information, "higher_half"); + +pub fn jumpToKernel(bootloader_information_arg: *bootloader.Information, minimal_paging: paging.Specific) noreturn { + if (@intFromPtr(bootloader_information_arg) >= lib.config.cpu_driver_higher_half_address) { + // Error + privileged.arch.stopCPU(); + } + + // Enable long mode and certain important bits + var efer = privileged.arch.x86_64.registers.IA32_EFER.read(); + efer.LME = true; + efer.NXE = true; + efer.SCE = true; + efer.write(); + + minimal_paging.cr3.write(); + + if (lib.cpu.arch == .x86) { + // Enable PAE + var cr4 = asm volatile ( + \\mov %cr4, %[cr4] + : [cr4] "=r" (-> u32), + : + : "memory" + ); + cr4 |= (1 << 5); + asm volatile ( + \\mov %[cr4], %cr4 + : + : [cr4] "r" (cr4), + : "memory" + ); + + // Enable paging + var cr0 = asm volatile ( + \\mov %cr0, %[cr0] + : [cr0] "=r" (-> u32), + : + : "memory" + ); + cr0 |= (1 << 31); + asm volatile ( + \\mov %[cr0], %cr0 + : + : [cr0] "r" (cr0), + : "memory" + ); + + asm volatile ( + \\jmp %[code_segment_selector], $bits64 + \\.code64 + \\bits64: + \\mov %[data_segment_selector], %ds + \\mov %[data_segment_selector], %es + \\mov %[data_segment_selector], %fs + \\mov %[data_segment_selector], %gs + \\mov %[data_segment_selector], %ss + : + : [code_segment_selector] "i" (code_segment_selector), + [data_segment_selector] "r" (data_segment_selector), + : "memory" + ); + } + + switch (lib.cpu.arch) { + .x86_64 => { + const bootloader_information = @as(*bootloader.Information, @ptrFromInt(@intFromPtr(bootloader_information_arg) + lib.config.cpu_driver_higher_half_address)); + const entry_point = bootloader_information.entry_point; + asm volatile ( + \\.code64 + \\jmp *%[entry_point] + \\cli + \\hlt + : + : [entry_point] "r" (entry_point), + [bootloader_information] "{rdi}" (bootloader_information), + : "memory" + ); + }, + .x86 => asm volatile ( + \\mov %edi, %eax + \\add %[higher_half_offset], %eax + \\.byte 0x48 + \\add (%eax), %edi + \\.byte 0x48 + \\mov %edi, %eax + \\.byte 0x48 + \\mov %edi, %eax + \\add %[entry_point_offset], %eax + \\.byte 0x48 + \\mov (%eax), %eax + \\jmp *%eax + \\cli + \\hlt + : + : [bootloader_information] "{edi}" (bootloader_information_arg), + [higher_half_offset] "i" (higher_half_offset), + [slice_offset] "i" (@offsetOf(bootloader.Information.Slice, "offset")), + [slice_size_slide] "i" (@offsetOf(bootloader.Information.Slice, "size") - @offsetOf(bootloader.Information.Slice, "offset")), + [entry_point_offset] "i" (entry_point_offset), + : "memory" + ), + else => @compileError("Architecture not supported"), + } + + unreachable; +} + +pub inline fn delay(cycles: u64) void { + const next_stop = lib.arch.x86_64.readTimestamp() + cycles; + while (lib.arch.x86_64.readTimestamp() < next_stop) {} +} + +pub extern fn smp_trampoline() align(0x1000) callconv(.Naked) noreturn; diff --git a/src/bootloader/bios.zig b/src/bootloader/bios.zig new file mode 100644 index 0000000..964f2d6 --- /dev/null +++ b/src/bootloader/bios.zig @@ -0,0 +1,649 @@ +const lib = @import("lib"); +const assert = lib.assert; +const bootloader = @import("bootloader"); + +const privileged = @import("privileged"); +const ACPI = privileged.ACPI; +const x86_64 = privileged.arch.x86_64; +const PhysicalAddress = lib.PhysicalAddress; +const VirtualAddress = lib.VirtualAddress; +const PhysicalMemoryRegion = lib.PhysicalMemoryRegion; +const VirtualMemoryRegion = lib.VirtualMemoryRegion; + +inline fn segment(value: u32) u16 { + return @as(u16, @intCast(value & 0xffff0)) >> 4; +} + +inline fn offset(value: u32) u16 { + return @as(u16, @truncate(value & 0xf >> 0)); +} + +pub const loader_stack_top: u32 = 0x20000; +pub const stack_top: u16 = mbr_offset; +pub const mbr_offset: u16 = 0xfe00; +pub const stack_size: u16 = 0x2000; + +pub const loader_start = 0x1000; + +pub const Disk = extern struct { + disk: lib.Disk = .{ + .disk_size = lib.default_disk_size, + .sector_size = lib.default_sector_size, + .callbacks = .{ + .read = read, + .write = write, + .readCache = readCache, + }, + .type = .bios, + .cache_size = buffer_len, + }, + + var buffer = [1]u8{0} ** buffer_len; + const buffer_len = lib.default_sector_size * 0x10; + + pub fn read(disk: *lib.Disk, sector_count: u64, sector_offset: u64, maybe_provided_buffer: ?[]u8) lib.Disk.ReadError!lib.Disk.ReadResult { + if (sector_count > lib.maxInt(u16)) @panic("too many sectors"); + + const buffer_sectors = @divExact(buffer.len, disk.sector_size); + if (maybe_provided_buffer == null) { + if (sector_count > buffer_sectors) { + return error.read_error; + } + } + + const disk_buffer_address = @intFromPtr(&buffer); + if (disk_buffer_address > lib.maxInt(u16)) @panic("address too high"); + + var sectors_left = sector_count; + while (sectors_left > 0) { + const sectors_to_read = @as(u16, @intCast(@min(sectors_left, buffer_sectors))); + + const lba_offset = sector_count - sectors_left; + sectors_left -= sectors_to_read; + const lba = sector_offset + lba_offset; + + const dap = DAP{ + .sector_count = sectors_to_read, + .offset = @as(u16, @intCast(disk_buffer_address)), + .segment = 0, + .lba = lba, + }; + lib.log.debug("DAP: {}", .{dap}); + + const dap_address = @intFromPtr(&dap); + lib.log.debug("DAP address: 0x{x}", .{dap_address}); + const dap_offset = offset(dap_address); + const dap_segment = segment(dap_address); + var registers = Registers{ + .eax = 0x4200, + .edx = 0x80, + .esi = dap_offset, + .ds = dap_segment, + }; + + lib.log.debug("Start int", .{}); + interrupt(0x13, ®isters, ®isters); + lib.log.debug("End int", .{}); + + if (registers.eflags.flags.carry_flag) return error.read_error; + + const provided_buffer_offset = lba_offset * disk.sector_size; + const bytes_to_copy = sectors_to_read * disk.sector_size; + const src_slice = buffer[0..bytes_to_copy]; + + if (maybe_provided_buffer) |provided_buffer| { + lib.log.debug("A", .{}); + const dst_slice = provided_buffer[@as(usize, @intCast(provided_buffer_offset))..][0..bytes_to_copy]; + + // TODO: report Zig that this codegen is so bad that we have to use rep movsb instead to make it go fast + // Tasks: + // - Find out the root issue: is it only soft float? is it 32-bit soft_float? is it 32-bit soft_float ReleaseSmall? + // - Report the issue with data to back the facts + const use_rep_movsb = true; + if (use_rep_movsb) { + lib.memcpy(dst_slice, src_slice); + const bytes_left = asm volatile ( + \\rep movsb + : [ret] "={ecx}" (-> usize), + : [dest] "{edi}" (dst_slice.ptr), + [src] "{esi}" (src_slice.ptr), + [len] "{ecx}" (src_slice.len), + ); + assert(bytes_left == 0); + } else { + @memcpy(dst_slice, src_slice); + } + } else { + lib.log.debug("B", .{}); + } + } + + const result = lib.Disk.ReadResult{ + .sector_count = sector_count, + .buffer = (maybe_provided_buffer orelse &buffer).ptr, + }; + + return result; + } + + pub fn readCache(disk: *lib.Disk, asked_sector_count: u64, sector_offset: u64) lib.Disk.ReadError!lib.Disk.ReadResult { + const max_sector_count = @divExact(disk.cache_size, disk.sector_size); + const sector_count = if (asked_sector_count > max_sector_count) max_sector_count else asked_sector_count; + return try read(disk, sector_count, sector_offset, null); + } + + pub fn write(disk: *lib.Disk, bytes: []const u8, sector_offset: u64, commit_memory_to_disk: bool) lib.Disk.WriteError!void { + _ = disk; + _ = bytes; + _ = sector_offset; + _ = commit_memory_to_disk; + return lib.Disk.WriteError.not_supported; + } +}; + +extern fn interrupt(number: u8, out_regs: *Registers, in_regs: *const Registers) linksection(".realmode") callconv(.C) void; + +const DAP = lib.PartitionTable.MBR.DAP; + +const Registers = extern struct { + gs: u16 = 0, + fs: u16 = 0, + es: u16 = 0, + ds: u16 = 0, + eflags: packed struct(u32) { + flags: packed struct(u16) { + carry_flag: bool = false, + reserved: u1 = 1, + parity_flag: bool = false, + reserved1: u1 = 0, + adjust_flag: bool = false, + reserved2: u1 = 0, + zero_flag: bool = false, + sign_flag: bool = false, + trap_flag: bool = false, + interrupt_enabled_flag: bool = false, + direction_flag: bool = false, + overflow_flag: bool = false, + io_privilege_level: u2 = 0, + nested_task_flag: bool = false, + mode_flag: bool = false, + } = .{}, + extended: packed struct(u16) { + resume_flag: bool = false, + virtual_8086_mode: bool = false, + alignment_smap_check: bool = false, + virtual_interrupt_flag: bool = false, + virtual_interrupt_pending: bool = false, + cpuid: bool = false, + reserved: u8 = 0, + aes_key_schedule: bool = false, + reserved1: bool = false, + } = .{}, + } = .{}, + ebp: u32 = 0, + edi: u32 = 0, + esi: u32 = 0, + edx: u32 = 0, + ecx: u32 = 0, + ebx: u32 = 0, + eax: u32 = 0, +}; + +fn A20IsEnabled() bool { + const address = 0x7dfe; + const address_with_offset = address + 0x100000; + if (@as(*volatile u16, @ptrFromInt(address)).* != @as(*volatile u16, @ptrFromInt(address_with_offset)).*) { + return true; + } + + @as(*volatile u16, @ptrFromInt(address)).* = ~(@as(*volatile u16, @ptrFromInt(address)).*); + + if (@as(*volatile u16, @ptrFromInt(address)).* != @as(*volatile u16, @ptrFromInt(address_with_offset)).*) { + return true; + } + + return false; +} + +const A20Error = error{a20_not_enabled}; + +pub fn A20Enable() A20Error!void { + if (!A20IsEnabled()) { + return A20Error.a20_not_enabled; + } +} + +pub const MemoryMapEntry = extern struct { + address: PhysicalAddress, + size: u64, + type: Type, + unused: u32 = 0, + + pub inline fn isUsable(entry: MemoryMapEntry) bool { + return entry.type == .usable and entry.address.value() >= lib.mb; + } + + pub inline fn toPhysicalMemoryRegion(entry: MemoryMapEntry) PhysicalMemoryRegion { + return PhysicalMemoryRegion.new(.{ + .address = entry.address, + .size = entry.size, + }); + } + + const Type = enum(u32) { + usable = 1, + reserved = 2, + acpi_reclaimable = 3, + acpi_nvs = 4, + bad_memory = 5, + }; +}; + +var memory_map_entries: [max_memory_entry_count]MemoryMapEntry = undefined; +const max_memory_entry_count = 32; + +pub const E820Iterator = extern struct { + registers: Registers = Registers{}, + index: usize = 0, + + pub fn next(iterator: *E820Iterator) ?MemoryMapEntry { + var memory_map_entry: MemoryMapEntry = undefined; + + comptime assert(@sizeOf(MemoryMapEntry) == 24); + iterator.registers.eax = 0xe820; + iterator.registers.ecx = @sizeOf(MemoryMapEntry); + iterator.registers.edx = 0x534d4150; + iterator.registers.edi = @intFromPtr(&memory_map_entry); + + interrupt(0x15, &iterator.registers, &iterator.registers); + + if (!iterator.registers.eflags.flags.carry_flag and iterator.registers.ebx != 0) { + iterator.index += 1; + return memory_map_entry; + } else { + return null; + } + } +}; + +pub fn getMemoryMapEntryCount() u32 { + var entry_count: u32 = 0; + var iterator = E820Iterator{}; + + while (iterator.next()) |_| { + entry_count += 1; + } + + return entry_count; +} + +const SuitableEntry = extern struct { + region: PhysicalMemoryRegion(.local), + index: u32, +}; + +pub fn fetchMemoryEntries(memory_map: []bootloader.MemoryMapEntry) void { + var iterator = E820Iterator{}; + while (iterator.next()) |entry| { + memory_map[entry.index] = .{ + .region = entry.descriptor.region, + .type = switch (entry.descriptor.type) { + .usable => if (entry.descriptor.isUsable()) .usable else .reserved, + .bad_memory => .bad_memory, + else => .reserved, + }, + }; + } + + if (iterator.index != memory_map.len) { + @panic("memory map entry mismatch"); + //privileged.panic("Memory map entries don't match. Got {}. Expected: {}", .{ iterator.index, memory_map.len }); + } +} + +const FindRSDPResult = union(enum) { + descriptor1: *ACPI.RSDP.Descriptor1, + descriptor2: *ACPI.RSDP.Descriptor2, +}; + +fn wrapSumBytes(bytes: []const u8) u8 { + var result: u8 = 0; + for (bytes) |byte| { + result +%= byte; + } + return result; +} + +pub fn getEBDAAddress() u32 { + const expected_EBDA_base = 0x80000; + const expected_EBDA_top = 0xa0000; + + const base = @as(u32, @as(*u16, @ptrFromInt(0x40e)).*) << 4; + + if (base < expected_EBDA_base or base > expected_EBDA_top) { + return expected_EBDA_base; + } else { + return base; + } +} + +const FindRSDP = error{ + not_found, + checksum_failed, +}; + +pub fn findRSDP() FindRSDP!*ACPI.RSDP.Descriptor1 { + const ebda_address = getEBDAAddress(); + const main_bios_area_base_address = 0xe0000; + const RSDP_PTR = "RSD PTR ".*; + + const pointers = [2]u32{ ebda_address, main_bios_area_base_address }; + const limits = [2]u32{ ebda_address + @as(u32, @intCast(@intFromEnum(lib.SizeUnit.kilobyte))), @as(u32, @intCast(@intFromEnum(lib.SizeUnit.megabyte))) }; + + for (pointers, 0..) |pointer, index| { + var ptr = pointer; + const limit = limits[index]; + + while (ptr < limit) : (ptr += 16) { + const rsdp_descriptor = @as(*ACPI.RSDP.Descriptor1, @ptrFromInt(ptr)); + + if (lib.equal(u8, &rsdp_descriptor.signature, &RSDP_PTR)) { + switch (rsdp_descriptor.revision) { + 0 => { + if (wrapSumBytes(lib.asBytes(rsdp_descriptor)) == 0) { + return rsdp_descriptor; + } else { + return FindRSDP.checksum_failed; + } + }, + 2 => { + const rsdp_descriptor2 = @fieldParentPtr(ACPI.RSDP.Descriptor2, "descriptor1", rsdp_descriptor); + if (wrapSumBytes(lib.asBytes(rsdp_descriptor2)) == 0) { + return &rsdp_descriptor2.descriptor1; + } else { + return FindRSDP.checksum_failed; + } + }, + else => unreachable, + } + } + } + } + + return FindRSDP.not_found; +} + +pub const RealModePointer = extern struct { + offset: u16, + segment: u16, + + pub inline fn desegment(real_mode_pointer: RealModePointer, comptime Ptr: type) Ptr { + return @as(Ptr, @ptrFromInt((@as(u32, real_mode_pointer.segment) << 4) + real_mode_pointer.offset)); + } +}; + +pub const VBE = extern struct { + pub const Information = extern struct { + signature: [4]u8, + version_minor: u8, + version_major: u8, + OEM: RealModePointer, + capabitilies: [4]u8, + video_modes: RealModePointer, + video_memory_blocks: u16, + OEM_software_revision: u16, + OEM_vendor: RealModePointer, + OEM_product_name: RealModePointer, + OEM_product_revision: RealModePointer, + reserved: [222]u8, + OEM_data: [256]u8, + + pub const Capabilities = packed struct(u32) { + dac_switchable: bool, + controller_not_vga_compatible: bool, + ramdac_blank: bool, + hardware_stereoscopic_signaling: bool, + VESA_EVC_stereo_signaling: bool, + reserved: u27 = 0, + }; + + comptime { + assert(@sizeOf(Information) == lib.default_sector_size); + } + + pub fn getVideoMode(vbe_info: *const VBE.Information, comptime isValidVideoMode: fn (mode: *const Mode) bool, desired_width: u16, desired_height: u16, edid_bpp: u8) ?Mode { + const video_modes = vbe_info.video_modes.desegment([*]const u16); + for (video_modes[0..lib.maxInt(usize)]) |video_mode_number| { + if (video_mode_number == 0xffff) break; + var registers = Registers{}; + var mode: VBE.Mode = undefined; + + registers.ecx = video_mode_number; + registers.edi = @intFromPtr(&mode); + + VBEinterrupt(.get_mode_information, ®isters) catch continue; + + if (isValidVideoMode(&mode) and mode.resolution_x == desired_width and mode.resolution_y == desired_height and mode.bpp == edid_bpp) { + // lib.log.debug("Video mode setting", .{}); + setVideoMode(video_mode_number) catch continue; + // lib.log.debug("Video mode set", .{}); + return mode; + } + } + + return null; + } + }; + + pub const Mode = extern struct { + mode_attributes: Attributes, + wina_attributes: u8, + winb_attributes: u8, + win_granularity: u16, + win_size: u16, + wina_segment: u16, + winb_segment: u16, + win_far_pointer: u32 align(2), + bytes_per_scanline: u16, + + resolution_x: u16, + resolution_y: u16, + character_size_x: u8, + character_size_y: u8, + plane_count: u8, + bpp: u8, + bank_count: u8, + memory_model: MemoryModel, + bank_size: u8, + image_count: u8, + reserved: u8 = 0, + + red_mask_size: u8, + red_mask_shift: u8, + green_mask_size: u8, + green_mask_shift: u8, + blue_mask_size: u8, + blue_mask_shift: u8, + reserved_mask_size: u8, + reserved_mask_shift: u8, + direct_color_info: u8, + + framebuffer_address: u32 align(2), + reserved_arr: [6]u8, + + linear_bytes_per_scanline: u16, + banked_image_count: u8, + linear_image_count: u8, + linear_red_mask_size: u8, + linear_red_mask_shift: u8, + linear_green_mask_size: u8, + linear_green_mask_shift: u8, + linear_blue_mask_size: u8, + linear_blue_mask_shift: u8, + linear_reserved_mask_size: u8, + linear_reserved_mask_shift: u8, + max_pixel_clock: u32 align(2), + + reserved0: [189]u8, + + comptime { + assert(@sizeOf(Mode) == 0x100); + } + + pub const MemoryModel = enum(u8) { + text_mode = 0x00, + cga_graphics = 0x01, + hercules_graphics = 0x02, + planar = 0x03, + packed_pixel = 0x04, + non_chain_4_256_color = 0x05, + direct_color = 0x06, + yuv = 0x07, + _, + }; + + pub const Attributes = packed struct(u16) { + mode_supported_by_hardware: bool, + reserved: u1 = 0, + TTY_output_function_supported_by_BIOS: bool, + color: bool, + graphics: bool, + vga_incompatible: bool, + vga_incompatible_window_mode: bool, + linear_framebuffer: bool, + double_scan_mode: bool, + interlaced_mode: bool, + hardware_triple_buffering: bool, + hardware_stereoscopic_display: bool, + dual_display_start_address: bool, + reserved0: u3 = 0, + }; + + pub const Number = packed struct(u16) { + number: u8, + is_VESA: bool, + reserved: u2 = 0, + refresh_rate_control_select: bool, + reserved0: u2 = 0, + linear_flat_frame_buffer_select: bool, + preserve_display_memory_select: bool, + }; + + pub fn defaultIsValid(mode: *const VBE.Mode) bool { + return mode.memory_model == .direct_color and mode.mode_attributes.linear_framebuffer; + } + }; + + const ReturnValue = enum(u8) { + successful = 0, + failure = 1, + not_supported_in_hardware = 2, + invalid_in_current_video_mode = 3, + }; + + const Call = enum(u8) { + get_controller_information = 0x00, + get_mode_information = 0x01, + set_mode_information = 0x02, + get_edid_information = 0x15, + }; + + const interrupt_number = 0x10; + const vbe_code = 0x4f; + + pub fn VBEinterrupt(call: Call, registers: *Registers) !void { + const source_ax = @as(u16, vbe_code << 8) | @intFromEnum(call); + registers.eax = source_ax; + interrupt(interrupt_number, registers, registers); + + const ax = @as(u16, @truncate(registers.eax)); + const al = @as(u8, @truncate(ax)); + const is_supported = al == vbe_code; + if (!is_supported) return Error.not_supported; + + const ah = @as(u8, @truncate(ax >> 8)); + if (ah > 3) @panic("Return value too high"); + const return_value = @as(ReturnValue, @enumFromInt(ah)); + return switch (return_value) { + .failure => Error.failure, + .not_supported_in_hardware => Error.not_supported_in_hardware, + .invalid_in_current_video_mode => Error.invalid_in_current_video_mode, + .successful => {}, + }; + } + + pub fn getControllerInformation(vbe_info: *VBE.Information) VBE.Error!void { + var registers = Registers{}; + + registers.edi = @intFromPtr(vbe_info); + try VBEinterrupt(.get_controller_information, ®isters); + } + + pub const Error = error{ + bad_signature, + unsupported_version, + not_supported, + failure, + not_supported_in_hardware, + invalid_in_current_video_mode, + }; + + const EDID = extern struct { + padding: [8]u8, + manufacturer_id_be: u16 align(1), + edid_id_code: u16 align(1), + serial_number: u32 align(1), + man_week: u8, + man_year: u8, + edid_version: u8, + edid_revision: u8, + video_input_type: u8, + max_horizontal_size: u8, + max_vertical_size: u8, + gamma_factor: u8, + dpms_flags: u8, + chroma_info: [10]u8, + est_timings1: u8, + est_timings2: u8, + man_res_timing: u8, + std_timing_id: [8]u16 align(1), + det_timing_desc1: [18]u8, + det_timing_desc2: [18]u8, + det_timing_desc3: [18]u8, + det_timing_desc4: [18]u8, + unused: u8, + checksum: u8, + + comptime { + assert(@sizeOf(EDID) == 0x80); + } + + pub fn getWidth(edid: *const EDID) u16 { + return edid.det_timing_desc1[2] + (@as(u16, edid.det_timing_desc1[4] & 0xf0) << 4); + } + + pub fn getHeight(edid: *const EDID) u16 { + return edid.det_timing_desc1[5] + (@as(u16, edid.det_timing_desc1[7] & 0xf0) << 4); + } + }; + + pub fn getEDIDInfo() VBE.Error!EDID { + var edid_info: EDID = undefined; + + var registers = Registers{}; + registers.ds = segment(@intFromPtr(&edid_info)); + registers.es = registers.ds; + registers.edi = offset(@intFromPtr(&edid_info)); + registers.ebx = 1; + + try VBEinterrupt(.get_edid_information, ®isters); + + return edid_info; + } + + pub fn setVideoMode(video_mode_number: u16) VBE.Error!void { + var registers = Registers{}; + registers.ebx = @as(u32, video_mode_number) | (1 << 14); + try VBEinterrupt(.set_mode_information, ®isters); + } +}; diff --git a/src/bootloader/limine.zig b/src/bootloader/limine.zig new file mode 100644 index 0000000..08e29d0 --- /dev/null +++ b/src/bootloader/limine.zig @@ -0,0 +1,369 @@ +const lib = @import("lib"); +const PhysicalMemoryRegion = lib.PhysicalMemoryRegion; + +const ID = [4]u64; + +fn requestID(c: u64, d: u64) ID { + return .{ 0xc7b1dd30df4c8b88, 0x0a82e883a194f07b, c, d }; +} + +pub const UUID = extern struct { + a: u32, + b: u16, + c: u16, + d: [8]u8, +}; + +pub const File = extern struct { + revision: u64, + address: u64, + size: u64, + path: [*:0]const u8, + command_line: [*:0]const u8, + media_type: MediaType, + unused: u32, + tftp_ip: u32, + tftp_port: u32, + partition_index: u32, + mbr_disk_id: u32, + gpt_disk_uuid: UUID, + gpt_part_uuid: UUID, + part_uuid: UUID, + + pub const MediaType = enum(u32) { + generic = 0, + optical = 1, + tftp = 2, + }; + + pub inline fn getPath(file: *const File) []const u8 { + const path = file.path[0..lib.length(file.path)]; + return path; + } + + pub inline fn getContent(file: *const File) []const u8 { + const content = @as([*]const u8, @ptrFromInt(file.address))[0..file.size]; + return content; + } +}; + +pub const BootloaderInfo = extern struct { + pub const Request = extern struct { + id: ID = requestID(0xf55038d8e2a1202f, 0x279426fcf5f59740), + revision: u64, + response: ?*const Response = null, + }; + + pub const Response = extern struct { + revision: u64, + name: [*:0]const u8, + version: [*:0]const u8, + }; +}; + +pub const StackSize = extern struct { + pub const Request = extern struct { + id: ID = requestID(0x224ef0460a8e8926, 0xe1cb0fc25f46ea3d), + revision: u64, + response: ?*const Response = null, + stack_size: u64, + }; + + pub const Response = extern struct { + revision: u64, + }; +}; + +pub const HHDM = extern struct { + pub const Request = extern struct { + id: ID = requestID(0x48dcf1cb8ad2b852, 0x63984e959a98244b), + revision: u64, + response: ?*const Response = null, + }; + + pub const Response = extern struct { + revision: u64, + offset: u64, + }; +}; + +pub const VideoMode = extern struct { + pitch: u64, + width: u64, + height: u64, + bpp: u16, + memory_model: u8, + red_mask_size: u8, + red_mask_shift: u8, + green_mask_size: u8, + green_mask_shift: u8, + blue_mask_size: u8, + blue_mask_shift: u8, +}; + +pub const Framebuffer = extern struct { + address: u64, + width: u64, + height: u64, + pitch: u64, + bpp: u16, + memory_model: u8, + red_mask_size: u8, + red_mask_shift: u8, + green_mask_size: u8, + green_mask_shift: u8, + blue_mask_size: u8, + blue_mask_shift: u8, + unused: [7]u8, + edid_size: u64, + edid: u64, + mode_count: u64, + modes: [*]const *const VideoMode, + + pub const Request = extern struct { + id: ID = requestID(0x9d5827dcd881dd75, 0xa3148604f6fab11b), + revision: u64, + response: ?*const Response = null, + }; + + pub const Response = extern struct { + revision: u64, + framebuffer_count: u64, + framebuffers: *const [*]const Framebuffer, + }; +}; + +pub const Terminal = extern struct { + columns: u64, + rows: u64, + framebuffer: ?*Framebuffer, + + pub const Request = extern struct { + id: ID = requestID(0xc8ac59310c2b0844, 0xa68d0c7265d38878), + revision: u64, + response: ?*const Response = null, + callback: ?*const Callback, + }; + + pub const Response = extern struct { + revision: u64, + terminal_count: u64, + terminals: ?*const [*]Terminal, + write: ?*const Write, + }; + + pub const Write = fn (*Terminal, [*:0]const u8, u64) callconv(.C) void; + pub const Callback = fn (*Terminal, u64, u64, u64, u64) callconv(.C) void; +}; + +pub const Paging5Level = extern struct { + pub const Request = extern struct { + id: ID = requestID(0x94469551da9b3192, 0xebe5e86db7382888), + revision: u64, + response: ?*const Response = null, + }; + + pub const Response = extern struct { + revision: u64, + }; +}; + +const SMPInfoGoToAddress = fn (*SMPInfo) callconv(.C) noreturn; + +pub const SMPInfoRequest = extern struct { + id: ID = requestID(0x95a67b819a1b857e, 0xa0b61b723b6a73e0), + revision: u64, + response: ?*const SMPInfo.Response = null, + flags: packed struct(u64) { + x2apic: bool, + reserved: u63 = 0, + }, +}; + +pub const SMPInfo = switch (@import("builtin").cpu.arch) { + .x86_64 => extern struct { + processor_id: u32, + lapic_id: u32, + reserved: u64, + goto_address: ?*const SMPInfoGoToAddress, + extra_argument: u64, + + pub const Response = extern struct { + revision: u64, + flags: u32, + bsp_lapic_id: u32, + cpu_count: u64, + cpus: ?*const [*]SMPInfo, + }; + }, + .aarch64 => extern struct { + processor_id: u32, + gic_iface_no: u32, + mpidr: u64, + reserved: u64, + goto_address: ?*const SMPInfoGoToAddress, + extra_argument: u64, + + pub const Request = SMPInfoRequest; + + pub const Response = extern struct { + revision: u64, + flags: u32, + bsp_mpidr: u64, + cpu_count: u64, + cpus: ?*const [*]const SMPInfo, + }; + }, + else => @compileError("Architecture not supported"), +}; + +pub const MemoryMap = extern struct { + pub const Entry = extern struct { + region: PhysicalMemoryRegion, + type: Type, + + const Type = enum(u64) { + usable = 0, + reserved = 1, + acpi_reclaimable = 2, + acpi_nvs = 3, + bad_memory = 4, + bootloader_reclaimable = 5, + kernel_and_modules = 6, + framebuffer = 7, + }; + }; + + pub const Request = extern struct { + id: ID = requestID(0x67cf3d9d378a806f, 0xe304acdfc50c3c62), + revision: u64, + response: ?*const Response = null, + }; + + pub const Response = extern struct { + revision: u64, + entry_count: u64, + entries: *const [*]const Entry, + }; +}; + +pub const EntryPoint = extern struct { + pub const Function = fn () callconv(.C) noreturn; + + pub const Request = extern struct { + id: ID = requestID(0x13d86c035a1cd3e1, 0x2b0caa89d8f3026a), + revision: u64, + response: ?*const Response = null, + entry_point: *const Function, + }; + + pub const Response = extern struct { + revision: u64, + }; +}; + +pub const KernelFile = extern struct { + pub const Request = extern struct { + id: ID = requestID(0xad97e90e83f1ed67, 0x31eb5d1c5ff23b69), + revision: u64, + response: ?*const Response = null, + }; + + pub const Response = extern struct { + revision: u64, + file: ?*const File, + }; +}; + +pub const Module = extern struct { + pub const Request = extern struct { + id: ID = requestID(0x3e7e279702be32af, 0xca1c4f3bd1280cee), + revision: u64, + response: ?*const Response = null, + }; + + pub const Response = extern struct { + revision: u64, + module_count: u64, + modules: *const [*]const File, + }; +}; + +pub const RSDP = extern struct { + pub const Request = extern struct { + id: ID = requestID(0xc5e77b6b397e7b43, 0x27637845accdcf3c), + revision: u64, + response: ?*const Response = null, + }; + + pub const Response = extern struct { + revision: u64, + address: u64, + }; +}; + +pub const SMBIOS = extern struct { + pub const Request = extern struct { + id: ID = requestID(0x9e9046f11e095391, 0xaa4a520fefbde5ee), + revision: u64, + response: ?*const Response = null, + }; + + pub const Response = extern struct { + revision: u64, + entry_32: u64, + entry_64: u64, + }; +}; + +pub const EFISystemTable = extern struct { + pub const Request = extern struct { + id: ID = requestID(0x5ceba5163eaaf6d6, 0x0a6981610cf65fcc), + revision: u64, + response: ?*const Response = null, + }; + + pub const Response = extern struct { + revision: u64, + address: u64, + }; +}; + +pub const BootTime = extern struct { + pub const Request = extern struct { + id: ID = requestID(0x502746e184c088aa, 0xfbc5ec83e6327893), + revision: u64, + response: ?*const Response = null, + }; + + pub const Response = extern struct { + revision: u64, + boot_time: i64, + }; +}; + +pub const KernelAddress = extern struct { + pub const Request = extern struct { + id: ID = requestID(0x71ba76863cc55f63, 0xb2644a48c516a487), + revision: u64, + response: ?*const Response = null, + }; + + pub const Response = extern struct { + revision: u64, + physical_address: u64, + virtual_address: u64, + }; +}; + +pub const DTB = extern struct { + pub const Request = extern struct { + id: ID = requestID(0xb40ddb48fb54bac7, 0x545081493f81ffb7), + revision: u64, + response: ?*const Response = null, + }; + pub const Response = extern struct { + revision: u64, + address: u64, + }; +}; diff --git a/src/bootloader/limine/LICENSE.md b/src/bootloader/limine/LICENSE.md new file mode 100644 index 0000000..f971aed --- /dev/null +++ b/src/bootloader/limine/LICENSE.md @@ -0,0 +1,9 @@ +Copyright 2019, 2020, 2021, 2022 mintsuki and contributors. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/src/bootloader/limine/arch/x86_64/linker_script.ld b/src/bootloader/limine/arch/x86_64/linker_script.ld new file mode 100644 index 0000000..f92a6ba --- /dev/null +++ b/src/bootloader/limine/arch/x86_64/linker_script.ld @@ -0,0 +1,37 @@ +PHDRS { + none PT_NULL FLAGS(0); + text PT_LOAD FLAGS((1 << 2) | (1 << 0) /* Readable | Executable */); + rodata PT_LOAD FLAGS((1 << 2) /* Readable */); + data PT_LOAD FLAGS((1 << 2) | (1 << 1) /* Readable | Writeable */); +} + +SECTIONS { + /* Start here so there is no conflict with the CPU driver */ + . = 0xFFFFFFFFF0000000; + + PROVIDE(text_section_start = .); + .text . : { + *(.text*) + }:text + + . = ALIGN(4K); + PROVIDE(text_section_end = .); + + PROVIDE(rodata_section_start = .); + .rodata . : { + *(.rodata*) + }:rodata + + . = ALIGN(4K); + PROVIDE(rodata_section_end = .); + + PROVIDE(data_section_start = .); + .data . : { + *(.data*) + *(.bss*) + *(.got*) + }:data + + . = ALIGN(4K); + PROVIDE(data_section_end = .); +} diff --git a/src/bootloader/limine/installables/BOOTAA64.EFI b/src/bootloader/limine/installables/BOOTAA64.EFI new file mode 100755 index 0000000000000000000000000000000000000000..5bae1c888847251afe7f2368101e0fda2bab5c90 GIT binary patch literal 159744 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 literal 0 HcmV?d00001 diff --git a/src/bootloader/limine/installables/BOOTIA32.EFI b/src/bootloader/limine/installables/BOOTIA32.EFI new file mode 100755 index 0000000000000000000000000000000000000000..6ff719f561406280ffa9431e5940ea5f65443f2b GIT binary patch literal 208896 zcmeFaePC3@^*6q|SzrSTH%QbdsYXQ$ib|BWY=ausY!WQd4VuJSuxPbKN-Kn2pneH% zxVw;Xycz`GT8q@$)>_*vq5%XU3pAS`LO=@9rW!Dod%LLyup}VO{e8~N-DHE>Z-3A8 z{ArY(w=-wXoH=vm%ZXPP$KM!58sFMj{?PYB5)E`DQ#?X~o`FWG3H z_Vy)r%zj`&&b-P8e^hzTPjY^6&rg5)U?At-`*SLTKh1gIr#S_;-JbK42k*Q8n&HET zj&dzI58%o39Q0aW` zckt(8{zJqBhs}-TYXbK#4iFe<68MvDO4b~kZPqml?!AZL9;5gi3zpo0fBDx`-aq%j zAJ_zL8_Ezk$xZpox7obc+;>ml9>fVgpzcXG11(2fpIKR~+~c94KQSnL)Q1bm=ssyp!#yZaDslX;$x8b8edLcE6sD z*zbKW(hwY^+YP&|dB<6}kpK3Ia+$fhp$wV$Ot-qjnY%ytWUiSQ#IIL(xcGO@sHa9P z`WJt+X4ZwKX^)wQd0twT3rUF&YX|)CQ53l+Vg`prP6mhMHr)OF@6Fs1F*n+6wou@B zG^MPw=8Myu@X9yApbmOi7>GIaZMPz3LsWW`e}3 zoY|c~XqLIuole#$0&{GB{hg2OHg%^vVzQY!3w8xEwdH=m&B>|o>o3T(gg-9gh+XmJ zcOcbfdl|kv$@enF%^+VH#m#~bQSjX-d=xjEd;)H+@V#u~C1ZHi*hq8W0`BV?G!LLAH{{>GX-2&_$aQ1d;;!K;WKUA zH$Gwpoc#QRBX>V9nZScx?5WjAj5HwDmKe10Wsu3sCQ`!kT1sd}!Uc&zn~4Y>%;Z%W zyrhdg`yA5H=#l+MJ|{8g4HN4~l(6y{B(Un8_>EUL;TH)edv-jn7os`?6T| z1PV6sl1V7!c3x7zN;-H+J`YZ2Q8y*kWYBN7Qg zUuY+gNlcluY<3n8s|3fs6{U4&5RK;Kt*xFwTlM{aBxFn5CI0e zc)bM7b%J4puqrA>Q;&hP#xX&(BTxi zr(HLMun1nNz3IxbExo4s2FXC;O2Jf%cfhLDb*HTpmCe4+D|2{`D=J5Z>Z*Q)d!sD5;j&H1*E^fV>d{2<~oWe3?F^akAYZ>Ou#_=O=4IJ zKQMrC)K{0kD1fq|qs}$!DGkbj1Bpm?C;WUlWmf8z6E-mHCUn1G*8q;849->)9<=2X zgpj$HK~o`?@*-BLdxWxknkwJX)Cnifm~8ngsFbFLJ|%xt_@n)*3Zx{X+;kR`Y&I7`qBmAwxpD+BRtu%F(@V5(p zNccNsd=KT-g}?nY`8$O_|KH^Ak}$&msqn8A{vHW0{AYx}#r|)=+rSIdJ&pWM_{;T8 z=RkILI?MIw2;pI9oUy`80H>4VeHN8(!+5{ljqyI{;>$@gJj-_zlOJorZ{_WLX@OQ@ zLIwwimJYTBZjOso%BPju*;54MIUO9$1bDf=&nb$Nn7B;dzQD=$CDOHGQ$q2lgzT4f z@U0~Dd<~VS-41Nz@w6(-e@SFKwidt2@|E~yRk~7N^0@M9Bt)Z$`h_3GI%xzBGOI(+^0Ii5D~Aw5s}+HsPF9q`m#?LmHKNqUDWYIc4CE~M0c#>P=xJPNuwXlTpY345 zHjrg+VZD5tVCCc$uq0n*GdozB&YvP~YLoNT$#3NFnQcnsE*K;jusne`TuP)w1iBD# z@?g6XxdGusZK%p^3uMMuQ1j7WdETM3+l;tl`L@>*9LU!;xpIceJ8gWWpm%W!iD`jy#4DLZ{+?8-^ zlNZL0I?^_gKq__H)6hZ_Rm^v8=j()wX6N(FHF@U;&*68haphIGg6Z%$^D3MHFJCFT z_d3y&?zXdMg=b7=vwC_m*d`qTVKGZN$*1W#=a@w%Ui!Y%O9KvwWTE z4!0)`EQ)$L@e-!rGEr^!#z-@p*~)x+;2bsPN}Oxuln#G{Dr|ojwoDJk%5h}Hob;%~ z*M+}kKG~glQ0EhuM^2(JCGrB2s5e8q)C*=oH=_4L2~K<}FE@BOIxlI&z*DZg`L5u3 zeL-j5e01zCB|J@ZbmS&R5}l~|6M~DCc2)&l>S7xx$g4VFc5BWjn6DEG3wcQ)WiIDs zZI=Iz8=nr2f?T zGLk@)muqfzA|Ww@FB8rEDiH))*+CTOWkR9Cr)56DCfBlQwNA}h%S&3*q*`}wiswr` zTB5UJ^zKep3_U8E^*C!RqNeIz5ahQMO!Yjn7UeALB;3c9x_#QjClQ7McJYu9pkS-L zi+Jtah(&;x_;She=_pTKl6EEsnId1=l+{+AZRx*hY*g9_RJM_ed@x z5~{j0J5>N>|04NA|C&-z%%3GHmAV})u6|-ymVJX7qBNhq%g8d?1wmYPF`_hQzLpvJ zlv=XHl`2rIRrWTv2a?O(n<0B;ZGm(WJh5INhgADMw!_{d1`jd1D8j@-YolO$3x)(C zZo!8z;trHEF^&j1y9jeUk_t=Cl)fOzSzs1rTO*oI4t@|V439oc0%d>25C~aX&&%4` z9qZ9w;e!-P`RV#n`N@YZz@H%wfjO0z(^v?sWo_#APto^E(D%+jtP+?-xgviJW*A4Q zDao&d$!6n1`I9&cWebXO_a~y1btE76kbGe9yYI2rcZQIs0e!LozR$*~YUsg0cn-zr316 ziB+|0lOIO6QzviCQ6lq%DCvYT*h<~Ls>4&T)x{C;=}M&3lBrG^kNymWFq!0P#aVKd zlnFULa4~Z5TG6{|#BZ$}OTH-m0MS+3N|Gms<(f9(Z=v{_cKr5_GRgduhl@ONlwzVR zE^^Ajor|2ThOi9CFZ6G*uvhP_ZaBx01_jWDRtWviAk6p4_UG$~wOkd$f0v>@L!r%t zC~xyx_!V_I{nmBc8#xz^l({alA3Y{8NV|oG8}~xY_R=^poR8$+b+VaJKGnraUSnmi zu#za7`I=Vt3N`V}Hmr0#Gl;Ak7xYVHKj;XI=PKm{7ED2>XRteJT)e`?A9k}PHBN{K zSEqX)vFhXvwm^(;rh2j9PHo~sUa&GFuV8~qD_H4bzE&)!D3RMpzZ9%=Bi^Y* zrclV&l?Opweyi|z%7IE*eiH@NEUV(A=uic1;szG%(3Y&kGMTQ_@k}JA@iA0liO$!% zMbYiFvQpQS#>a~IPQEGw9;NOev^h@~OC=pj-FbZdG(_8vv63zJ<5;4}ZN|99s=5+m z25`5wR#HKOUde$eo|vM(XpJ);Qrq}+%^4x~v62Ivk0+EdT56-&!~y#*zIg(oVxKsa zy3@R*LvwGU#Hy||^+iF{s)=x*`I)Z?1o#d>fv(S?l==qd+sJ*p6Iomp_42hNGzDaM zjw$MF@B@oT8|`~|*%qZf^SViy69Py0`fOyyf^KVWLw_~G`;~&vIPCSw>+>NP63Ej9 zA{M-;)E$F>U#xy+1_$$*O+0%&fB1}Qn!#PXs)Zn|sn2MC|9gft;%y`1Lde zv-m0tDG=P1(mN^}Z6KSi2kh}|_ zE%)n7NYbRhiXs@NBDhM3nMx?)q5}X6`cQG|P$ya{Io`?nDf;`Ry$ z_W*y47>&guSMAN7ci5=~6_Ms`gfRexJ+Q&~R@pRf+4phx<9sW%DZ{FpXvmMzkSFX1 znR^3k^zs@)>e*Gfn~Q?j-oy)h&01jPPZ1MX3kE+dTKIFDbIra&SPy%L#z`m>Ie&r$ zW8a0xz2>f;D0 zb%*TR<+PN$SJJTEg`!|D)5xv4r;`Gc)8P|PVe`?k+l30lh}?#$GW9fg-iLm{y0BlT zr3=z3C7oEUxegrJO0CFec4^M<@lxop6)s-t;`7}sumU=4MW4YFcanBRUO5Yv?Uh_i z2%d4k%y+{?G&ke}pShucezA6ph++{jtv})=5kciz5U+>`QczYzR78+MvLe=s2x?Lh zAvPu|gx8X!r2LF0TLLQpVoIVce-1g=LAITj&}i%tvqEM0a>OFYLrKjPB(S|=ooi(# z{Anx4;8&^ZN!v7*lB(LdSc>GEGDQ517`)RqQI%L#3;F_hox7jCo3cXPhn^I>E|f84 zjWjD^J6!ZzzG6J(pLqZs$ll7J)=Ju8f5CKmdIAz+|A3J0lS%~(j)=iJ$V@CHKThJR zIbnHV?$8nUk)cc)4AQuvPwDLDPm>l&T#*!5qURYmqF4*sehNg$^Df)Y-c{7;Pz#a$ z8wpDQDz#*H8f`hvY%$%e4$hAHG^@o|0X__*p?*3a&w!FqtL+W&herFcT0OAF1I4`opvw@wmK0N z&v%pa!+3s%&DNNI4kEIxh#ZQz)E>_tLlN=(vC{5kYZMzQ!a)e%UD!`3#t+G+X)eXtas97z3o{oxpr8ns*|@ zIDiRWSK{ipHyhf~7CY)zHpH1XGw#iz5(aQ&3I>Gm!@v@HXt*tygSClA#<(6?_7dRG zi)#*t4*xrJDhnbKygb|xyrj`P+}7w#2Uom9K(*K6h!;nD8@)NUL{zCi!*9WgtP=hO zp*XD`X{bIOf>(*quQuNV<*YJWJ$jQ?klw4g^WQnjdSX2n5A$XXdnl9Pe{U9MmZ)I~ z7JX)dYn!qzMU$VRIlk?Jn4;y9G@YuB+5U){m?SUCJ_b0e_Y(Ddj4Lvl}7I-*Rm6#D69C~P&EqGs}cbH8zhXlT-nxmD-A0S{4xUUaVYh^gcA9y$bOR&{uphpn%`3*&4@vJ*^Q=AHru8r zP<}rXAAJIOiHY9_gVh%Ejbi}z?0N&gwhOj{b;`2aA^tr4Nc2iDu*GbT{?YgvV!#N^ z*Xh@MZT{)z6V!XLpGOs6QG{}NcyrFT0pCiqr;>IPR!66p)ix=S50O3{TGFf~QHF7g zHRIfdlhvXN1k*4_#b^LC)L$xqULzpd4|W@|VK@)pGC5sYaRsR6-tlTr1_m(1HhSH5 zK5bdCy;*ZJ1`iS`tW8s3u`Vk%mIEmCkPFNrecI@CL1v4A8O}86_@#4)bncYS4C&00 z&VACEEuFd2nJ1lqbQViziEx&M$iZKHSy*~d+<5YVUi=$kHzH?DGq<_736@&TG{+_2 zm9Yg;oAPo7w9-{pEByp%Y=`=e`Ns9Yl5D5KEo3}G&4^(+*~p?XsGUJ0 zwdzc)?z`3aN2*z@tjHaV`0(a4P;&NGvz;W-`EHFjhg5{|tIt3*MC|C_lKqrep3+Z` zP*H;TSFW{)KV@u$jH~9uQh=233?QjCNtY;*xABW6w8y<;;e$8bZcIjcqj#j;cn8Ky+$ zz>^fgv4D*iEE57{ddC}Cl(+fhvKo|=0g*0ggU#IKFO_0%Nwnmk(dnW_I>#26$d{41 zXK7_^9#&8~0aIFD>X-RCkecLtW?rz3bp_w$-Z7yij!W!H#DzMl@xQ3%?To1z)=yxVF((Qxe%fmp?M0H`OL04*nLa+FLSzyvAVCZ#~7^y z=4&ekDD}_8x2UfU7yNZ!FUzC@#U@R!JXQqZ-c2DRrK%8I3g zDXeCEOA0#7jFL9)Ew(pd&k*F?;%QkpjJ1qwBIC#oQ%7>jc+o8du(wSL;KhI)D1iSI zJ?N44304Pq*w}y=2<@@SIW}8*ObOFX ze*kiutX~D}?g|r25~LE?Y2yMQ554gc3WVx8k2*I{jg~@8WEdyW28fd}pAupHa^OUp zjHp>wOPM!#l^?QYv>OLGXUkR#&m^|&8R41CmOTfL&@nf3V=7xOa@BZ8C>unkA2P4P zMK0zY!$mIUoy0{h=AGO}K@Xuzp+I0voFrlI8?`oz1rdv69T_lc0fAU9X-jxhAb)W- zWCG`hI`h2wN<>Xf>P$sKY<3wxru4pE*+yi2u2)`kR<9Jq-Uwo077Vr9jO*czJD^lB z)<`oJgPjOL2FZ-|!p>~;!i?=R+HxoYW~_G%L0DSV8mZ#m2{Lk`bfg*UoeXmUoi(2# z`Leb6W!{3gRCox)mpzJK`lAZiWkW|P*w1E{pgjC9(~M>ryCB?9PdmuDz}Q00*NoT6 zImh@DIp-U{BWIMcmYg%jv*dhckTnzSG&nigMwpyWj4E=vjfLcN84r?k!kA6YapN9x z)*E+`^PX`#IophB7j2+rW`ETl5WM zI5~eY29tBpFd^{KW}}Ckjm9x@HW>dPXR|@JPxOG%M$S9NU&v`P_K>sHh?DcC(Ll}? zV-q=z#;fGSjX#jH+jx~Z z+#vdraV|NJ86(Jf+!#X66NZhPkZ}sa9<4Qwlk*!RLC&)Vjo{Ir8-F9`uyK%_h_RQP zwMG*;su3e6Y>)vSU2d!=XQe?(GrGd~4LOe(YsgtBL>pUnk0=PbH>Ds}r|IpGn*j zeLhhb{e8k4{WNi7^k0c@(_nLH4h=rR>CqvHE2Bo@ifBjT(x{%eDEg1Yh0*rJdC^4T zoao1i5z!A5L!%!h21k!39MOMT0}u9E%FU;Uc!slV7#y&GOw&2z@ngc?sL-#F;|z3Z zq=AMOIok_Ah;$Z5mf5|?+&^%nY7f=4E8#+kXim%5{FXGfx0pXdqkl2; z7JHhMW#2|Gn98wM?P5)$^UyZQdASXd{bKGK7<3CekL|;t$t$K|S#j_!tYR8$1#t$; z(^A5>CRTTEjv1LLNKrU?W2a}=LMJ437i$_PP)*PGYzLlQ3rEmi z>Cyr;Wx8kYqCr4VxIM{l8b&RCJCyJegoF?_!b3t>LCIBZd17j?;1a=!&$qBhtvmui zi-AHfb8kK!lf{<4H(!0I*cP}*h=|PIDcJ~k=nNqvfW#T8e4_(3lQN}*Tj5X1MCMx3 zaJ4cSG$pip_{-$&Gku?YFoNl$eM)3QiR@E0N!W1xB3s z&}Yz1IXjbCS&Y1sVbO_^3?jho*v%gnrzKjOHRqWYX%4!vOhHkb=qK*Y$FGe)G#;l8 zo>&VnD=&zcN|;8xVpf<>sxk5l1lT^~X*k5)%86#>1j)kz8qauz;JN`q{YYwT0le5kUlhb2Vk#o#gNKVvvkeqjo z+2m|9?jh$r<1TUqm|^6^jB(^_H!dY-y)l}c6GkRE z$Bp6Sd}s_N=Q+bX4(GJd181FG99zfy&lwyBajEg1W4ulr>}&MjQHUBnb-q(LV7?Ph zf=DI`W7ZlrT!}fk4LELiO|q#2cR`P0nYz>4CrQ)i*hd~1kFoi9=29gNi@IrDj?2bOrSbHH%sqvbMnw=Lw2%2*DFq)HJ|En&k+p;|j}2oS1jXOrE_c zb-~*(1I#C^(Tr5=`)KPtUuW>3s7yx%c)+?@BZfRF;j9-`HE5w02vyQt06fTj2c0q6`ujLTJ`@5ghK!>~#JxUWDa=c#)hV6^S_# zftgRY0!B_45ja2@gaV`50S7N0Z#+aCC~-AT!O8t8lhhu}W~X9tGar2~+#Fa8cxoe% zPIe*~oM;3=n*WG;dWf=O4`D>*{F6e9Gcfxv9Jjb zPpP@G0g1-TGNo~ff^sMKm3vrZ4Ak_OyAv@e*Z58!p|(^)n`MUF{tOLPh6|G!6lCZx zVV%rCEoaqii7KcnhRRW`PY>Rp0gR4*)=>g?(f z!Nyy%lnYRbWM8`|#A$?42pD^`FM(#;Xd>gsGNC_*)po}8vvW{8QVtIiH2-hW5{s@T zGD|0I{4EG0T7w?rK3V;Ntf#6buG9R_QKoN*Op`Z0XJt|%SOg+r7?{Uuc&Gp){%#9kC-Bv36HFSqj4netst~VEupfBfC){ z_(?#nBxIz`L^mLT3}jG2LVBsQP)7*5l={LEh%Pr*HxeTg-vs~x2j|1nn4e)!WLPnQ z@5>k}04D_;dHGr-m%LQ1FFJA=;tvo2zT7IgMGsM9Y(3lgVnVCj~+XdrA@gED2m`>yKkk-uT{59h}q`!p9-Pvr<>L3xz+ zc(wV}YVRe=GAH@hW%M31t9vu-$4%B$>Di_MZSKj$kek{2p6(^%H21gITd~B&_P6$Z z<_W*ok!PAqI^b!CM|rFPz~5mXc)F_&-C!FDqoC0$=J!ab?wy6Gjj(BKidb9I9y`=V zEWmO(%|^AyLi@00f6BiQ08ebM?!9ROJWn=ikJ)U!i0{R#MwLI%9?QY`V6e6FP9)gL zdrxJFhNJ!D&9C%ok97&}(8_NCUC(ep;j1l^G{%Bo7$plXP z;q-RWI2kA=GUh=8tos zb2RmEnQ8jx*!AH{`*tEEUdg}e&SwCgWTN?=BRNBf`$VL58o;_Rr{R=tNvBq(Yr(c$ z6WbrIFM9X{+;EC-_>`Mz(7%yh{C$8sE{$V}eeM`E8my4JU&A75ZV22*pBn z!YDv#>R(`1AilXjp4L!s$!3;_U)LZ1cM;#@6Yq`@nl_V>d3o9yr(ZdvWo0}IwjeusW(F`{N?)SzSM9PS0{?L; z`1Aqbc706-;c^p8zlEf7Lu{ymV9dIF*z9Fq*#hNrhQeIe4@{vf?i*;vq`rrJCz|l`5s7aTL&`e!F$gB^ zi2I%c4OnA`G4{>GNGr%yBBY|+oS7J^aBPy!J6 z^(p@lRN^3XRl7|IlQJL)-Ue6POs&u#P6!CCRCRQW z5_uMBR3X}TnKMzpw^nFH){6=^Wb<3Id3lZ+JA?BUn9n$;mqLth5**1wP4$TubWw)j zMY+B}a?4zp?Kc-?=p8Uc1;2iB|cBD`KcEAn&R)j+)pW<9)1&+}*7MZq?#}>GqB)L={dJ`4qX0hrR zMlX}jvykiAvk6r(FW9%>dYlhqb}9CMyHNlq#eTuXWJB7c_T-oXPWVy55t@2+G2Fqj z!o-g27u#&OgfHn;J0~P-d>tsph!|RWKV-;Gov0)$Y9CgTt%MVheD1?dn+(2mu2Q#j zM(%z~-(wDf6Gp789p|q~^pP?2NSt6TWo0eXb@LC@gc&%luynB*Z1=eB&_oyX zpqP9@2B~8jw+)i&5G%MbK#P5J@6N5 zh)jqG>^=;mZ&EGd7@RxReJ1NkZwkI{khO#gq7ay{ZEQ`5JY}uyDdGefpCwsWVPDLb zJsKDNpn+T2!S17UY8;nJT8PjC7vP|6$GAgV0dKpY(Q}IRC|(%mow(QJJ*5;NbV67W z?bwJeaix_mkre85GZt4FAXd0#EUa7{)Ei0nM)JzCaJqH0FIzUOvg})swWzR{)SVNo z-n9n?78c5NGkBIYkd$||b4F5rj_-HawBR#yX3W7=(92LR3Wb6|a5mX%82a7%3QP@f zfK%JN=|`qH#{~=NFX)^?9R$9a1jf)LfbWvP0_;x`jK#2s-W;4*U9}d+5p98CiE)-U zco9r-EV_tWHLa`|eoRpgFh7QvT)neprOpfqwAihjAbFY_zK5Ajb~c zx1!ZR=i6rl<=9w1{?fxrBFz~DjJ`h5D*)yIhbNIu=R1yJWVVD>Hu#R;a67CE<;ez8wqJBy`)qw1XhG{$ zxpGfd=$8un(h1c&TJvE^k9abh<}E0Fuq*-Bp6GJ2G{2=lNO`F4oB_b3NM8)3+MCY+ zABk~=UV_cXfl`enf49C46GYNja2iXLgHT$t}$&v z7Z^l#4z!(CbcXpF2Amg_HsztzPwy4ys`Qp1n~&n%`vZ@`GU^}ZSnCN`}k zRAo4sFlC{Di!>aJ54zZ39wAzB*OEtwR)#?)LbS5N zSxUXQFIQ7|ANOrxg|oS!k`>NPdy}XfRT4@Q&7$tJv8`hC5#te$&_xbB!~!DY;%X@o z%_BrVE1U=WxXG`70K3aFq-jSN3Z0kjWM1rnwJfS|N@vs;|Z5w#nu0RWMtizE#& z6p~Mb+=>I#?35BdL8bY+Qn*11h}!gX+_<2}YTF30gPL~`nm0I{i+aR;T|_BG{~FPO zHHd(KnesYbEkX!j(Flj?I}>^IUP))o@@J5vTyKA$s^hdUVCWmlmXoYVMO(AH0scmD za}OF7mDEY9!asmip)@6Y7d%BIx{)8Dz-?5hI2f^s#tCFd5&sCD8jui}|9T=i>Y*&( z05I)IYVp}2l!TRU*2wnib-xty$+ZrGs@n_GoAq@6rX8W!--p;6NA7|ipbXQ?EqOs% z<@yPnmq8DR`)Cyk)7cj3?3PXwIhy7Kjc7??vaZT~gU#KR%9Z-$quB~eq(@Qy1|(Y|o%lAvi-;AkAlj}yN#emB96fkN6fx}a*YL}|F1CVH827r_ ziY@Tu!XT{PNh3_EC6YS+=zyZ-soyj^Z0hFDKR}BG7Km|;t2#o7k(T*VTbQ?=k{6V5 zcQmw#sJ8_!u8t8G=44cq>VLxt88HOOjDfKNj_9=oc2viROab@$K)B%VxHHRo38cHO#(Mml8rRDH~GLr3jJu$zu<5exHM;io{5zk zn(8-N*GkY~OJ`UD9~?esLb-nYVS<1u!@5$$gz(Ad+<ne3XAq4^qMs9glr3h`x) z6w!i+;W7f(5^+>0xDIbJED=Ev*ikyfQR;G( zraMZ9I!cE*O5Kjq;f~S~j#9-@n&Bt~{5g)&QI66~N9nna((@dp=Q~QX9HkdHN-uPj zj&_u0J4(OiD80y0da1Fc1Jj54-D9mOLY#fqai!%;laQGAZ0cog0HiX4V6 z<0=^=Sj7oU#499f87y&zH9&qnRymY{CT;!DyUB3sG6RkbDCXi^C>LBj$F=w}ojUtT zq6DLh5?KXoi6MQLvpUtC6H|n#uRp7ueedyH_h)K#%-x>+$GyMur$}i zKEioP6;VhRyfE|4%$t|L;9k+Bo%SBIUP&8t`fX2H+9-I4761z-{a^7b_=KDmeBpaI zNqN3^C;EtZVI@v|-DWM`Tat?9I4P;ukffeWj0F9N13UF#U?Ee@UzHwT~UZ{!TDTVWy$6|nSL zp#q?q*rX&Ow7Em$B&Zt>eJY}fxeuR3+1{KFS7X=w*=s)9jCJ?bPmjL^UA5rce;-d$ zwtsb!`ET@7HW-whD=3pK_$BoH69NhP-oM78&&?*m6c{K<0s-lhBDI)DkXG ziPJU8%}uPNEo!&=PU0NNpTa!6J@iBau3ZFvQ{y`ks?xFiz;RA#N!OS}I<4oz8yz#p zi9#y$@8e*q7=3a7um}n(aA69o1;Rdl{}v64XK4b1^U-k=&-6>jC{0x#0QUveGq0uw zaG&&@z#0>6dmA_5C;6CW-+EqkCm9^ccKCh_T|lK77^#%6^Yz37PAz{v=g=)?Db5qn zIs=BAIF1iO)*2Gu;GCvsSeQ)02d@4PIrPgerk1JRNd*hJU>e;kmoqpy;zC)j7xST+ zT>-y(qdoW?b|`(HvSF3$6`CgUY&bPoZ{s=e1iy*3tI+0=@Y(`b{!l%g6C4w&>a+zf z$}4dNXg8h#AXW2BLrqF7Igr5;MYL@ImXDToTWW*A4=^4+BrFNU?00qn8ACj!G|%G)9^LIxT{K#_s2gfW1DMS}h_j z>(|?ARN@wF3AUNI6<6X;}OD$uAl|z<6tEW~DVu2`wYM)w0WI!1-XR&gSfkWF)OH%Py zq~H(SYe9-3{67NKXv6SKbOOp&%$JTB{d5&3EN1Disk9dEp;uO08U+EEwn z7`ejYu}vcB~>;-1XH8 z>~A9?6?@O+k{F4-t{;50z`o`z?9CQ}_+=5ihYf>-=zAKsRdk$Cw(~_-5 z^e#%l7`V;?TT9q3PsQf9z)Ey9*)R+2#s2nE$DpTz9Q4t#@KK+NtK;Dl&8SX*Pqxn| zum(i?thqtdoHau8j-q*ZxtXHj2C;+p>gk9e*0VNrtKusO@V+-2X z2QAS0_0>tVM31JPkELaSHv4SAA148c+_*Xszf>hn{Q<;SWHBZS3PH)&{rc_?Xwg6n zeJt+R|A}LmH0=@jD)c|0sS#8+3p9aIuw1KL!OP-FPK6TZOU)*^Mw*R$0MgXvq%!(h zUwNZwQ_Qb7U1qU}bfTtaL6Zo={AU9`p9Cbt77Fb9j1ZWhsg+dqay`EVLk62+6?4yi)fjjr)DiI9Ho?lvH`OregECB(2 z7S}&{i?ZSR|3*Tu_~cToFSL;gReZEd7Ov1A02H1eQ5O?e^*lkD6TwnZJm$ycdbung zC7vrq>lOib@QP3m0k&RiO(;aOgZVq`y>i4r6n$d#S6bCnNi38)mE;01^)Yoa!hU`5e9?|forEBaB#2{f z!&U1^EH{A++Xc-^aTi}jxp1o#+nAkvGhrIno!g8C)DOaHgEUHr+-4~@75X+A4V&{g zHiFir0%2nkQ*Olm!ip6naZW|Ua>n%oS-@U(v*>lW7vOsh265mvSXX6qR=8aEr;Adp zh>i;VCK+{HS2rqGg!D8?Aoiy?|uvx1_ z7NZ&SsYwlNky5EY+_OThW+b@5Tatp6|5hY0;use8|U0=BJE|8G1c!|{$r{lD<6 zxV=}YM`Rry9q_f|l}lc>(cV=3cN_31^*htMxZrH;Ls#{OIPqdj$7*pW7Pw1xVy2Q- zwp(p*PEmw$XzYM}8W*xl!pem$if2bOPGf|&N(?FMX~+ZF+D0Oq=nqX4)u&rV;>yh{ zX7DBR^|^>NgCR2*mU;lE#c;2v7Wen z>_ya0J>`GH4_EQ-W8z&VwI^#+mWXJQYtW5io!X9vo3NzMV6AnOd!A9)aiHlZORpAjaJfGA;hhSSEoGlPeCH!k-vSd%^ zoa&BLd?lbN+TGo~-+o%Ddy*tA?MWeEWxMg3PD>hI6oZ7}H8{H3)tVGC2nmUhZ=(>c z-4HE0@vusW7M4Y`SeKBiLcIT3wo{1HXOJN}D+)P#3tJHOQ%e2&N*!MK!0YV2*ub!V zpwyS`RO(tFR(LwCQHl{>Z7l1kKAIFRIujc^?5aL2B&&&`vu@1uHEH>Fq3@J}7{pZF zP7;Pu$259TWAKZ*wE7$D1!1F1n?Ig&(UVL1da~wz6D-Ez5WEk!F)@-?wG^qoRwr>f z{8MDZD@ok9`>MrbrKZeVj7_)K&>1#fpBN~=OoeN*u7P2wNAYHM0q1NaRN zu&Smc=cG%j82^{;z6v+fV@e&~YryI2Q+Zyea`PTOl2#>zF$0~-cEt279P2qo9W_7M zQIDbf5-;BY$LzQsiOz{4PvNw+Qr|6}r^WNr=$>G+=&bfr^qAVru4G63GdgN;W9*2l z`iS5q_`H)ks`C-hmGAMrCg1-wy1+pX-kTG9XLV&QX=Oql=vmTKjYIN)`ka@fy5q~J z1}`MxBoB6690I)`D0S~D^(Tqy_aOw(E&D(f5WhYbbwhw#MiKQNdU zb3#JIuvO;Mufo==uMLaXDSudW#gdR)315diSfr&>@|M*{0|o|X=r;Yc<)m{maC;EX zt@^qa;Bh{@=-SSM=$ehla`v$CmW1zav3Fr$q35#c>HA}8s7x7TLA-NS(yC>y#5tlA zV&G-RVg`l@C~3Eg4Yl{84}B;24+C>TQ&f$kvOp%5$byh)@O(fW@fia==23MZx#8Sb zKRP-}^^+CA`OOagjLY=kYE^L3I0jMj;Z1~7o=6u-aR{rIKJ3tL z_3x6MP^9SH>@;3ZMGv<#=g)8U%+!Mi6Jdh4@RFXIFO>(zN(eYK?@AurQp>a0g=mvo zgC`Bxrx+Y24O~P`u7^%!hwuue5O-|!{PK@j!Q(>Kr{Ij4=7+4e@F{LSKZD`1*7@0Z zSe?($!AqHZY8IJGr_*pK4i*!==aWwIO=r?*0X%6J?I2k%gRH+nvfe&ebJJ!iH0J@b zh9Kn@TQN}QtG)wHEKOop8fVbjl<*1?byP}>Jmz|_zE;wXBR_uqoUjyhUxx=@Z#lI9 zEe7=j4bXVDi0jxJg4#r{u%x_+E~e1@qdU#lfF23~fb1WD;v~K%|InPQOISkN*(rJ~ zDA;079E?`i9iU1ebjo2N4Vuik33n;ef*nPkQ^7wQ6Q~nVog1kWVD_+&MqL~`gh8e` zHBoK(soPQeI4e+3Hnr`ri{)Z4IrF$=R5OC+O+w7outUq6WnTGF<4fE#u3da z+YP(vpj)X0y&Xg^p1(mV7xQd(@G5Lyiq^`T=K{$X;ukDLO&07U*+=UHo6>>}L@$ll zK!p(?D9N@k9J{J`4BHDsb$41-dtR|q%O9`Y+(VXsm-#JT)vAZ7v;mdC5VWq)(;a-% zpe4&B4^XP2Vxk6%y^_QYOXp97wFWt||NDdb0J>SR8UM}Z1VgfR* zD>-|DCL9-2p74EwmaNjp_261y8O~QKg*~hb+FmJ)aV|`ulCDLALsg&Hf>*$f_Uyqe z=n{QVR(CtD*EiyH$x(aP$X(O@`bD>+hwXGh&*RpXn7(j1sLvIn}vunLH*ZE_mp497PN#W-*|PP)4EC%S~|w2tZqmgPqr<&Qhz1O`{*T6UWt3_!16q} zuYyZM*qOFU%TD4*DX)>r^YoX874@Huu?0`yT>e&EczFQ`kh`IsZjsestad9~^AZ1u zjCWZv4!^#*53;5ZGbe&e^XvCoaLFhqwkJ~nVUZHxAiwTO0$Ndn{rVLtfOx5n0EhVX zj3l5HmG0O7jfD$IWKAKC^b_DvzkXE4x{^`D@E&Rk;2;Tz1A<$VfL7E9te2z!T2)m1 z`jbgOD=NdUFG~R&A~BBi>vNNUR@5lJ{@oNn*uz9)reD7~31~%~>(?iw0H#a8^ZfdS zh?NcLDon?F(#e-PWs9PDpzF{;1vd@_lvh3N4qT#aEf|^Q$0_^w6^tau!Jw1d?STX6 zG_C^A(4xda97IYd>+fuNdh)klDo?9`>i=Axrl9#sfdQnXNE2FmvW!GEk^%*Z__~bj zlO~8xW@Yn zPXX)~v2YU5k{rn5{1iYd76S3TBw)Yf6sG|8i`aEZKudBUix;N=_RCXxUu+EySSID^ z#Bjk1S27Cn)GnjIikiZHdD?~8|5JH-40->9Jjwl0EP7#7p;<)Xix?Piy|X4Cvj{el z&Xdxa%hW%1DE7%kxrZJ&bUzz17bpKS*A-Ux&V#8D{3sqOKsNH<{r$dEK`Hw0?&`*m ze(kV@wiW}-78uQ+BKYdlG|k}4smP_b^GV`SA0_vJrs&}(-hktDS)47zSG#Jl=*Hfu{t(^v;q=e;CD zJEX>(uvIrwU{ecD>-Le*hmSJ+2YeH-Y$@<~eQwuT zQ}5UfJOV)DHXM{?-1asW)p&;RJB2?-_+7$3LHOOm?-%|I_!-X>zgglpTcpkssW~EM zjEI>hV#bP?@f5QvL@}!-iI~aaH(&e~h~HxIJ5Buh#qS;X)p)JIb0_?9wFmV3!Q{C5 zDSWh07FVaCy6`~DX}BRzI#*P%G4v85?OdG;%MJ@~N@N$pd_6WWa9n}skN2DYEI`NA zSwKw)usLM;L^|yr045vz9ulL*qq9(f8anT&@qFQr3V-cA6i??fHQpusbZ|lAIrmaL zwT{5Zi^phXpK8G_+*zIP1a25C88oNi)MQM&?#+Flvg)TA; z^vODTNMH2HPyO={hR;=3eB$cDFRC5n*JpyTP6c16&iai25rhW*dP)J8G{5hx>*81E(WWG;vZRVuK zI<4eWoGbA=X{}W>Q$7<8Uq!6T6OWuOaN(2*h);}3X-N^IO|j=KqoNgW6B98wdQO$Y zQ&X~X5QHzOpQYeo`QN_=^~2F~AzmKwxvzy+;lhMxBcWcHg9MT-9P%1pQEo{NTTkp{ zn~AQ0ecfuWzTsmCVV7q~qax3BJVd#ez5kS`Xx~15{{>I>rLa#*5noBfaP}g8r{02# zlv0A@zE5)~o&-OYmlQVFQ#sg9!LJ0*uy{e`C3VY>s5DV^JX(%7AT{;KRN8Hl`!80f z!x@@0Cy)3aQ@N+)YpW)G2YIpM^`48M-IgE|JibrW(UbBC-%s#%5>CtF z)dbwGX&8p1>bd*34MicI_yxEhNJnKWC*$?hHVh5)Oz_4Uq@@W$(Su#3cq7Mee1K8% ztOP`t8jUo1ZYB}JxjJV}!I)ufz3+=D^6lGqqtLpyI+LuMFN|e3Px!bHrkt%V6PF_ zrKw<)W`VuZ2S!GM{@Gkn1xXvFSzysVum<=HAj4sEi!1a%nB-duSEvD|3$DB*QGOv; zk~~2-tWGUCzrSRvIe|@s3$VLV!Or4Jk|zMWJ{9aNt`HdU1Yqt|u(P;AV8j!Eee~lm z=L&%lPb$h2cQM#v7F%#8P3V2TlIp_6hf32{62W+2PyAAGX)9mBulDG9op2!QIAYk$ zrj5x0BeS)FFhKgI+PN zSDk^(xC;s#bdjkmy(jo&Xvvsi^hS&5i2Z6ohL!qhV}{jW;phHO2Pj4K{{bV~e-W1F zA6pPE!Po3kFN^$x#q!e+8!&G5mgwH`6VMT<~*)fJqC0>Bj z57=vt!U z=bFKG-Jc8v%wUJ^O#;(tHM5>{Dt*D}DiQ5A>sO2DS`po;)04H31o}iwI=r}h2E|yE zH%VDW`)z%n!=UvejO{KK3msM(bUe!3V8=Ff$kt?WU8I+|9*DgS#4w<>OdG!JGV**=iIN*)< zQ!v2clNK=RzMr(~q`W1C?V~3lY*lV7DJb=ddg|H$&KP9i=5iKJ3oR4Jaa_i2kif>l z*J_3KqQsSmpCjYT^^xM}mDApp+>0T~fkcUPgV&*(u2sUv;GsiFuBAAO>cBxfEqPc; z3Dc;qsTq`#hLi>UDMv&~W-{fw{V97z$}mcyi^4bcr_gqbrapo_3xVzOWJ+K>-eJK> zEK$1B7hBA_@Lg5Z+jA^%TgY>5;Fd<)wTS|DY|23P*RrP2&&+G_VbtKiVBirvmJ}PE zcALFPjXM&f@bOB-CWa-m)nw!sDfJat0^CKfEB><=^NnH?xeA&>i_L3sf(ncKqB&&7 zBdjYwnU`Dw((=t94$e?%UCQd1y(zJ!Ph(K~^_go|tQH24R^nzQJ-EJrH6H3T$+jkZ zvb|I8&m3Px#t+#zI9NWjL*4F3WD=nD*BH*53!}~E#q;KP*hdMsz%WvKE)SfCw~By& z(fc(Lq9}2IvM5`}jE3zUzI z#oU<1ibG3An3n}Vrb7NP1YLY)hbB=^P5DTb{ZKW!8(B_o0S2FjKd&Ym&vbQoJ$RT_ z54PJ685@vb5tnX}e^XJi9>!>NSMI?FpKv1x?7_*Y1JY@c&Ti>6k%M;-@O{rcgW$ui zA@(7h5T^!@3+Pn#TLadxnB6ZRj~s+~5a=HK8=p>c*d)Wp-ZdE)J? z?-E5|r{9V10EhRw5DKQ|3?46381ti7hR7aON9 z#Fm>Py-;5}UCRRoyH29T`rv>@qHgBjr(9 z@egnd@2e#HjPV}A9CtSUSAJ8kcmp(Af*9}q0B3HLVtjLSpb+BBM?e1$-`6jK{j85o zfOOSN`$p5PkQ#^}_B+2o3M*e1GS~na{0QY18XfrM_WzBcMCWkW;*!qXlVEP&*hek- zMf^RXytpGf_Q~hC+lrd7-pbuy%-u}t1iOs!f`ia2RPqRz1}IsnchU2TEd+s&+P)?w zNqp(9Uw^!e)Ef7BFcdTFUMWCFpgrhgl~m3Dsuxh1m&h_vQ0{*8h=Jl-o6;ZXgy;+R z_xA{^Kd{rOJwo<}L&^R?yUq)DUlK%ecN; z_84;zlTx#zP@VlXdlw-zWMa@ZV!IVj6+mT-2fOicq=IiWU5)H$$8R*@aTK5SsNf1t zn`mXt_Jb+5fzU6#&rfb;~sy`FM zi})U5P3E`o0o2^W@c!UO_AcXu;O&21$TEPRRY)KHz^oM9M&j20n)bl8sVMvLWu)T6 zBw>&gXq-P2jSUT+xLF7R#+@YB#DNWCHXd@2DR_KL_6d@8!;kp0k)S~(6ItAtfb=AT zt9wbSps2uK5hayMV;a``@Hx=keU}Y`7jqT20ICz~TQ*+>B0s1!|r#WVRA}x7- zV}SjQrC1%Dqz4dQtbsMXSscTpM`E@qzDP8hoN=2w$3wc}azS#}pcP zaFJ!+-%!7WIe6Dj>^_w1-~I&=m4){*L~dt=J{ENWEWKbPpBj>=HX@^$Nc5>%;YrRX z#w0oL0!~}uz6%!x;b2Z{>N7nhw~bHRC&J5qn4B7xI2 z70#+R{~rDij^}23_mcq>{?<=L7h`iqRUnlt%n`eoz^KYbB}uS&NU+4BCMPIb#F733 zWT~zBw?w>{Gcg#J<1Yxv$q)Ls#lA=Jp+_^baBcqkr_DCRuLa^~GbowrzM@+l8V!5@ zq0WAaRaVC@&xqa|zuXX=+A^bRaVpvP^rr&%#3Jws9Li4D`YvLt(!0I;KdSq0HMjo6 zAIf~PV1P0Fakh>J^_RO&Z*0k6I!;{QQ)eR_CepVIN>anD6Z1?~GB>YG7BFGH2zAJ_ zik-Q1mXyN@!1ep5na_Jhf1)rE@IutoD<=1NwCJR1=rkeK^X+59H|7H z{k6$=R(h|HkK&VYotgb={MwW?U$FqyjZG8Ja-0;^}-{d=Z5y^P|yaJ<~0bmo&JE8jrd6(Jg=v)k``6PFe z(;QzQfd(WEii2Rqlrd;Q!jSS?HGCcmpC2F9{v^-d<3o>)v(qC5bn^Sfz2jFJqBqNu zQ%R)wl^L`}n0Y?ot!5EL@*tLrGFCT_r}+B7sXcH zKR5JsApMr`i&NrbcSi-Qvc;)$8xxBIWe1|u8=o4V;dI5`!ru~@K~{h4lGB-*!Bcp9 zW<-BuQTWB2_?5e(=bGP$U2u9PL8$SW@w*dG;GdblM{jx}v!`mD%f~-vR+qlM?jZ>9?ez5C>z`Tv94Q&EnB|06CIY%I+wgxgkSuW`tE*&f#TQhj%F`OUC%Gd-i(Hw!;#k; zpByPW~KfEW0~4 z9TC51_%mF5>>=_Kp66^6kAnr3L*a^1Sdj;n=db;KuO{@+qnqM>W#7Sh0zYrEt>@A{dar_#Kc-1VT5_O~4OrJS{0u)fH zOEq4(1D78KE7bsiBWcO6e%w%>TiNX_sqiQDB_?Hg`!zhJ&V}%cB}}SRlghleJT+Q& z)y(Rf{;by8SslCAIaS{HO>pq@0Sc~Obm|9&zXSBl94`|`09sJ?}?kB$naL4`|fMfSx zvAbVlz_%*-a|0xHzi-VF)gPvQQsrRdr|*uHEvb%O(*D&9Htr4S?z?UM5Ynrq{)uzx zjMR_F;$i=3TzrR9RUO_`wNyA3U!*86G0xb~_!H!_ih)WpM}zc};h+r1D!{Oy^T#U? zTOgDVg66jvE_GYu(4ceYKZDriXh%c#cr4G2>%zk--l4sKk^5|qTWn(jY31=h~#gyEd8gEHgaS1n>v?H^`;jPTR@tGR}u{F;5N8-N6H;&N=7DV+D}wcywVQ|L5^P1U;H8VA1jJ!jzMU|DGvr5J);7p2QS{#3-K?V=6m#z{qNTWV7@K14G;)zIuqjSMyDGwEI*-1&hQ;~Gk>H@pZ)OM&}Z})C+DrndD1|FZ<#brcFP{I3!S-k&2MlOA6`GJap` zZ*cZFmv5{Y5j>O^ieQb8=HFP^XG^NOHG3c7_?xmKGKjnCsM3l^p;!KMW3aBhIy&23 zGwOcs=W{NpN*h|sk(qBY=$an|@67#NX4$2gr`8T2LxWGD(hl5MNztTmQ-@YulNG2r zM8q@R&lZi$so{(&BDf)$`j~;efqz#%vIc+odT=d? z>8CQC%ul5foUu&A@8?iTZSn&{me=I!n)~mVHPk}5{3BhYSYEt)fnkB>fp?TTg^0R( z%Oq@^;Qvc~esKQDck{fjfR$kq;pPkiac)g9=4_B4XL!#8caf+NzYNm<3pY8OFyB*Eib^Rj`u8+;6(=2c zxK$i~Y;&tHy!Qr1eKn`BTipaFam~!+G(`#;4&juj=~xutXg-ZDo5)7Ya5$$t5Uk%DUvO(6_Wzuk z4tMFT*~Dhuhi}be?lh7C*iQYRb1Hsnd`+iQw+mvA`~c6&Z){u?*k7h`v}vlYx8RmQ zELB#2Jo>gQxTkN)cB8lCtsgfdoR6O#UvqeV!-#W!R^8y5kBeW{4Y?KBvti6Si0c16 zH@9+denX#gerDI2dohHAPv_6hjpolSYk<2CQslj|f9IB&?2 z!hfZrK;>Y3L2wGq+BiD(aG=m#8YqfiwM=-;>xT7)+YT}h+D`>SAlD;stc@F~H67mb zM&}PRgKP(GvaFlq=ce7b^ybWHPIym$dD-cgwTzkP*Kj^lQ+`p$;EQoJF|F94Xy{^R z0BXtz=$!v*4^Y?NY&Q@j zz30BnoXdfJ(yBU>w@v3v|C#6qAK*BiBvEBC?_rP_)=nrzr4TPxZUhA2EPX_@`}e$+ zVwu@`!;yl3rQIv%;UFutH906Sb!lI<=n$yU>b%PIo86arl4Ta)=d=n0VK;h@)(GvCub*}Q={cK@k#Vy&8;dm-+VXc1yUH3(A zk3TgEf0Fw5?EE{OYp?NTeAZK$&ZYP<7)At!k0zdOV01<~J%eC(0 z|GYz68G_Nu`1Qd)lY7OSjO-ph$^E>~IS{PlgeWKcleu-rBd_1Fw4~TXt7lb|%=;*^ zkWvFjZ$~fV+F6Mze800o*lYh7aPkh)Wffdr5h+&Hhx+8K z`hIt5WL}^@=U}`8j1plT8)qT)@ZV7gd!TlC^6&5SCZ0Wqt`+Ph*_6S&!&69Z^Go)7cz<4;WvI~^qA4zq) zm4&cXwzOuixB>klsI$i_cY?5+kd#Y3NeLq=`~rvfpP+&x^QJZunQ_Ca=x_e|0!ekl zpY#8cOdVe8|FsQw{P05muaA6@zl#08q^S;n#Q#f5>Tn)^O~~IoU;eJ)unN*(lFjjr zn?3m(ove=^5k6Rb{1v`hVVmHab57zmNbOYqP@UjK`OcKy#fBX|692|0ZbL~ow46s$ zHMeB+>K|}-+1y8(-!Ojp7XAitFM_%^PoS>(#vXLhKKTn)E&l{3L4%75;tK)@ruhB< zdJug$oP6j-#8DNq5S2Kj>7< z;Q9>wi~z1;ex833Je*-6w(2VaeS_>8CceTxgi z#HxJSE|~@a{BYN&!ruzNFCwl)j95f`ng=7V3lSx6ltky@-{-}5g?#rSOM-z|GT!MU zAGNPRzR#AXj{jH4XKlp;18L+du*mlo$ah~F`9AUScOl zm3kC@O9Pv1mm@YqiL8IqZ^Agv=+B!05?PFk&d+yO%r9{3v)x(FiW{?>=#3$k=SJtx z=K5SbL++=Y72!N58m8pF82o2ZPzK6nX!V8T`xz9BWA$UfZ?bxWaU8a<;PIvN=gzN~ zUpQ;&d?e@Ja-)ZNk3cC%3)x!+^9sSdGquT25{_UeFOm}$ZeBbY^Oi2m2K%I43fs^v zXPyc_Cwvpnf4FRRu%0AStm!h*Le4p`^w`XUBF*@(z_UAG?&pn7?JUa-R?N@OWxafL z=uz9BRo`(AQ+k%0*$;w)jmzK7B8*8LwLs2Yej zj0R2;KzyK3#CNayui#Z&j^lAp?OZX`@@2_4_>r42As8{-{0{H7<1U;u?lRT+_Q%zz;ilix za}7iE_8FxMq$)dQ=%|&>g@Ij^bUNPLv2MC=C3b}O$fmtH`oZ{RYL~R|7LekNVWj~4 z+NE=7XZyDTv=K@#+AQ)tlZlqUYa*;|w#Ad(zb4`AOyZTu| zSEt6`N>umYak;prGLiLK;_2%CNsogD|3-YNKi*wyc8uf7(n&IteS6Km)URF~Cw zl?59Z97-S!;8rS)(Pr%v#ejVo3;M5-K_k_?SkE;27NyL&hv@> zxkunZMCZw+>B~(HL2-!w@8}04a>RnF%u+0e00BfX zkf=MfjCO7trQh>=BEA&2H4jleih31CjaO*AhDsrW6GWm2Y-JLw-R0z|f0E8Jp}#;u z^_WniIy-*(mhhiE2`zvK1%OM4;E6vRUd`jNkEC8;SkjpB0gRR}vIp)s?U?+kd2`l- z(dW%;Kspe61b?bOQM@voDfl-Y1ARVJEk=x8Fx@**DDTWpb5itsE0g>BI3HfZ;X_3RLfz0S3g)wg)?1?2)ky zov1cB!!=M-8XD}_tnrfS! zgVJ2R;rJYZ1`T@Z`#_d$x+%R)YyP(v|E?brS1&L|^*B>n4pNg*O zpu=FJU{zgK*&X}fI9wxsQ@QM;=e=$UdTM0v_@L{m5LLfQa%kK*-^%SCmL&JIKCf?xBJT*f+rq-Klup-sJ3{ zAO4-J!1?^V*zj}WIsfNNis#@>%h$oFz(*IU8d12p5s+KUpgPm(pWYXJB;K1H>39V! z!h}`P|2k8!%BiQY-YCrI$;{xYvD_zrS=R3y4KKmpHcFGsDvF#YQ9;5>F2sBD)j0E* zA9*1`U`^Kz$^^v-|5ye>i}$BU+EaKw-w8N>oS9gHOgWBI+Yo87*?{%E-Xj#Q;2Bco z>L{N#NP}%-#mCSa1Fks`QtuIM?_@3OpEvAlOR#n znYb@t+?TUfWrpJ_h8CsoLgclv^r`1ZT8R$Uz73&E&Bult2xI=B~~AW)Ue zIScbg3}S-}%#u{-UUOqvP5;be;pZw~=EmJTI&*Yw7M(=B3y~hxq6j*5W zJ(=ZHoD9J^b!&mMv=`4sukl>Orv~CHGih+)H$}yl{d3>`;QaeY+W&OE9(}}w`KLHP z<;0xwl#<_lT@=NM`(jp&FWh~lMPK9}8z$%k8Kw&DDvXPvr$#+FlB>;AX#OLly8NE| zKqir&&$)N01m3|r>IdCVm7QIC1k|6LvhAEhdF;qcXFxVCxH%)Soct?y-p!h}vyTThv0wOI*iTO{y3#H#R_ zyRUpjyGu?%Y+7>0EBZ>@J`o?lX`FEXCm=y>UY6Ww-a@Ao@*;Pc@3`$OCyCjgNJaZ= z_7BiCHiR-_AHu6wulcRCKLfI#r+$Ui`UPEFWG^O%&tb!6Bo`CSxZXZo^)Tw~o=O@} z6L*f~5h|x`r&PpC4@rRL%TyFU@>R><*HgE;pSG+{RiAE^MX`Dh z(mnZsOzLBf29AbXJ8yNXiV~SEhDBK9;NRYgmS6H{#{36DY3{HxYfUosEqlx4F{ydW zHoRxt+swGtcHBpLE2^)(XSm_vc|55R!+Sd12eL>(xb#RUaA0~@xak&c?FTf=-~&a5 z|Jpa(e|suslJ?4K-Dh}eOix`Y+i!p|vtK;+tnps3qsG3G{Z61O-Z2;-`yzdHyi>mu zBii2+)tBOzZy;zS*lp@CCyVVp`km~(W#36G8jN4LW#ZtlH-!KDYl%gLzvlPP%3oMX zXlQ2S4YD`A!gt>DWN3z8d^m>_O+ScT=*i5^cpra)X((68Ny6wyk%*J2QTcoYH!*0I zGCj+Vk^=Cd$Qxw`)`sUC4VWZll86`I7rUI!VPU=ur{kk!R{rVjrGv2#Pfpj_$cc@a zr0d-Ir$v#f(!uCxr2>r-WafnOOZ={Ms#v)rH}E zM-$(CEk1rr;+uTGT6QhA@VZx-5qRJ4P$2M5MhL%J5J{|iEm2*_GqgvV{BTv-1GHeM`tL(^rA%_`@Ts9mcIOu;m2wLJc9(Jwob7VnwgHOQ;E8; z(#FFEDiuR4VBdk1m1*qK?eAp-BYb)(_7{=m_9I5ZG$tsl9S zhOnJqADx2M@(PA-#(X|C$jqzggW!c6nsuXVKjRFTsQr+0g%LPhj1`O@`smCPM1nN| z13{yn8Sb{G*}1~B1BuL8n)DE%zu`ZERiZ$?;fqpc$@-y2l1m6Y$3zelq+r%Sk{j6k zWY{?oZasY4(Rbo+O>xdehtqUzpQpTa^n*HL4vE~k_`XC%Pk-zezn1=20y;E^73P6d zx>cWfU!o$ve=$FnJwVUb@Qb%Nr>MlxczOC0Z|mD@jhAnDGK+g&Po1_P*46Lm>H)6) zW2zgLa~((Y_jk3Ae?P>T*&FXB!E!cWOFXY{UX0a^_wz^Mg+u!Hl|KDjLj~sKN=n(T z!klhe=A2kQUOOB2=6W-NQS8h^s7~WAbnwJ@Z85S0r)l#bN*yYUH>j&0myN%m*U29_ z7oyi)I`f;p%XRg0;B_27#$}qmYmIJ{+ zKdQBjqsP2(TwgXevU=n4j4pj`J>QvxAo?J!!S%#{Hmv{Sd&i1(TrrE$G+kFyM7nf_ z|Kxvy*l^i5E}grso!u@s-pX9a6!M%Y=qqOaZrm*IGSpYoM#&4Mm3C3hc!)j^wNB06 zW#r8*PnH{xf0m*kzD6F7Z!wDPh@reFiQX?AP$2d*cWLpM`*iUy=aXl2Wmet5 z+W&C#ig{5@JHL==F`p=SUH>fd>1x{7bdrfqxzYMFgj?c@s9_G=QQ7XCnt7R|y{yjR zEa(0LXED~*5J~GK#=OXjC>pJ_2%(OF0x+O{GcdRtMUgE1{$ZD?CEzhM`<7j7>86=?yA+WUL`nF8lt+z3P5!R-gk__&Ekx8T^8*=tI6 zpJej`{UliItxbLb2V5F1bscx)6}$DvXFbeB+gJsq_Hgx*@bPe&%`k)zl2A8y7{V3j>OfHkKp?^7o6YYx1aoH`oMA$WZn2qO$iqn z#Wj|hqKe9l`Z2!$x5Im4Lth>Hr%!NZyZ5`_qZs0q&UbF>9M1`yOyqs)tEYG0zz_R7 z0$N zT4sg|aeQ@F@}>v19)hk(C!n8Z@|O7c&EY?J(B6ZWWeIct_VB0mcRb3!)PQKIq4u`> zLN$LSTphwS1+#SO7A8q~&9CVT1+#Rn))dUrL0VFrad>#tyKy6_cJ9>i*rD-x?a7QU zY8HwDKB8+B@^4R$e8IoAT-T24+Cci+LR}lsweIw_Pw3hiUE7(yMwunf;?-Pe2`zyW z%`aQiGW_@W5#U>(+J%;!+T=X*?o2|iS?%tk)c`>)XAC%EA4tS2zi0{TH-5&!Q%Na* z0n%@LRGYSxa<8-U+4_sOfG2_Ekq^t|aNU2NJ~TA8end6n{6`Oc1}tsJPEexlQC(Af z_F{oyJ`SbX(${`yuk}_G<^AE?(;wqAyZZHg=lLvX{+B;|h0px?|8Ss*&!ROK{>K77 z3-1`)bdArscmLwSzvpxIxw}66MLy>}yZi&o_$)s9+}r=m=YkE7-tgCamfrUG!aMk^ z_@`6x+xRU1+P+0s`CK~l-v>X>XZ1I3e$@7cRi)=v6zR4VFFgxZm7OxS z-K>hDtoVwehcfu*;fw^;?-DDD%$P~zN3AFd1$yRCxMn?LtoZ|fYs@D_4r_73KFy0b z)T@^`m*4Z6PV<_XW6qn`zs5PQi6^PDV~RZ5muQS9cX;df7pEiYky&QHf_n{Q9PeW+ zMcz?SCT3B9sWAQuEyG(2@RVn4oKKiIWE3r0omiT=`enUjP8SvYmL-1%E;#JjEn3e7&0ko)yt6BDH$X+G6rD!f!6?5WV zIiwjd47uw<&b~1X&*1B_oPEF4%PWuPjL~8%@FuU! z=_e8p>7nz@7+s%Q&(Ut8JKQ#!O*e>U`FPPgl>v`Enanw&S6qppNQGMm0#5S^eh;@E z3pn*#+-5V^hi1FAp}=XknuO5-y|5?eh!(jh+i5Q3?f}~KE_YF$(_Exg_Bsu1?gL9B zm)$3-BPZ6M6D6;|C_2{q#1Eu}jebZR0?jgVjCfF_*Et<-y}}CL3}Id=CH0&6&b~pj zuw8*M_nQUIz9Icp-^?i}k8h+GY-$uK>et&(XP6DYu?yk$HkzefsXR}F2s4mDks{Gu~6+7F? zH3v{{t^#-Z-MYPz z^JIUuo?um7SQ6w04Y{?|&gKFh3LJH7mpYrJUgew&l7(EeGl%m?$GxG)A_s-wUAWi> z!fpGroo(u@hZ;b&JZy@ZzMP{RhKk^s*$Y0O54Ro&IGe?TAztAt=Np2g`_#3$4I6W= z7{si#vki1QoKdM-;g~krGHly8rxJnHbi12H>k%pR7G|+gu^<9%{gKYdCC;6+US%yu zLey<&km=$+_>hwr@o28keehu?p}uSwF*!#x!mb=Jqzzh9O*~p*mjz%tf z+f%pI?2YND=hkd@>$ZRc`-Hc-`@}=dCarc%M{_M|yIU1?noId5hm%C@k!$n-bn7>P z81zvKzJ(b(Z%0D7R+Rn*; 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 literal 0 HcmV?d00001 diff --git a/src/bootloader/limine/installables/BOOTX64.EFI b/src/bootloader/limine/installables/BOOTX64.EFI new file mode 100755 index 0000000000000000000000000000000000000000..a2d161e46df3943ba249baaf2ced1c97cdd2a831 GIT binary patch literal 184320 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