├── .gdbinit ├── .github └── workflows │ ├── cron_report.yml │ ├── emulate.yml │ └── test_code.yml ├── .gitignore ├── .gitmodules ├── LICENSE ├── Makefile ├── README.md ├── charlotte_core ├── Cargo.lock ├── Cargo.toml ├── build.rs ├── linker │ ├── aarch64.ld │ ├── riscv64.ld │ └── x86_64.ld ├── rust-toolchain.toml └── src │ ├── acpi │ ├── bgrt.rs │ ├── fadt.rs │ ├── madt.rs │ ├── mod.rs │ ├── rsdp.rs │ ├── sdt.rs │ ├── srat.rs │ └── tables.rs │ ├── arch │ ├── aarch64 │ │ ├── memory │ │ │ └── mod.rs │ │ ├── mod.rs │ │ └── uart.rs │ ├── mod.rs │ ├── riscv64 │ │ └── mod.rs │ └── x86_64 │ │ ├── cpu │ │ ├── cpu.asm │ │ ├── cpu_intrinsics.rs │ │ └── mod.rs │ │ ├── exceptions │ │ ├── exceptions.asm │ │ ├── exceptions.rs │ │ └── mod.rs │ │ ├── gdt │ │ ├── gdt.asm │ │ ├── gdt.rs │ │ ├── mod.rs │ │ └── tss.rs │ │ ├── global.asm │ │ ├── global.rs │ │ ├── idt │ │ └── mod.rs │ │ ├── interrupts │ │ ├── apic.csv │ │ ├── apic.rs │ │ ├── apic_consts.rs │ │ ├── isa_handler.asm │ │ ├── isa_handler.rs │ │ ├── mod.rs │ │ ├── vectors.asm │ │ ├── vectors.rs │ │ ├── vectors_asm.py │ │ └── vectors_rs.py │ │ ├── memory │ │ ├── mod.rs │ │ └── page_map │ │ │ ├── mod.asm │ │ │ ├── mod.rs │ │ │ └── page_table │ │ │ ├── mod.rs │ │ │ └── page_table_entry.rs │ │ ├── mod.rs │ │ └── serial.rs │ ├── bootinfo │ └── mod.rs │ ├── framebuffer │ ├── chars.rs │ ├── colors.rs │ ├── console.rs │ ├── framebuffer.rs │ └── mod.rs │ ├── kmon.rs │ ├── logging │ ├── logger.rs │ └── mod.rs │ ├── main.rs │ └── memory │ ├── address.rs │ ├── mod.rs │ ├── pmm.rs │ └── span_printer.rs ├── limine.conf └── tools ├── deprecated └── codecheck.py ├── install-requirements.sh └── lint.sh /.gdbinit: -------------------------------------------------------------------------------- 1 | file charlotte_core/target/x86_64-unknown-none/debug/charlotte_core 2 | symbol-file charlotte_core/target/x86_64-unknown-none/debug/charlotte_core -o 0xffffffff80000000 3 | set disassembly-flavor intel 4 | target remote localhost:1234 5 | -------------------------------------------------------------------------------- /.github/workflows/cron_report.yml: -------------------------------------------------------------------------------- 1 | name: Security check 2 | on: 3 | schedule: 4 | - cron: "0 6 * * 5,2" 5 | 6 | jobs: 7 | security-audit: 8 | uses: charlotte-os/devops/.github/workflows/security_audit.yml@main 9 | secrets: inherit 10 | -------------------------------------------------------------------------------- /.github/workflows/emulate.yml: -------------------------------------------------------------------------------- 1 | name: Emulate 2 | on: 3 | workflow_call: 4 | inputs: 5 | toolchain: 6 | description: Rust toolchain 7 | required: true 8 | type: string 9 | 10 | jobs: 11 | emulate: 12 | runs-on: ubuntu-latest 13 | strategy: 14 | matrix: 15 | # TODO: Keep this updated 16 | arch: 17 | - x86_64 18 | fail-fast: false 19 | 20 | steps: 21 | - uses: actions/checkout@v4 22 | - name: Set up QEMU 23 | uses: docker/setup-qemu-action@v3 24 | - name: Install ${{ matrix.arch }}-unknown-none target 25 | uses: actions-rust-lang/setup-rust-toolchain@v1 26 | with: 27 | toolchain: ${{ inputs.toolchain }} 28 | target: ${{ matrix.arch }}-unknown-none 29 | components: clippy, rustfmt 30 | rustflags: -W warnings 31 | cache: true 32 | cache-workspaces: ./charlotte_core 33 | - name: Run Charlotte OS on ${{ matrix.arch }} emulator 34 | run: make run-${{ matrix.arch }}-debug 35 | -------------------------------------------------------------------------------- /.github/workflows/test_code.yml: -------------------------------------------------------------------------------- 1 | name: Test code 2 | on: [push, pull_request] 3 | 4 | jobs: 5 | unit-test: 6 | uses: charlotte-os/devops/.github/workflows/unit_tests.yml@main 7 | secrets: inherit 8 | with: 9 | toolchain: nightly 10 | style-check: 11 | uses: charlotte-os/devops/.github/workflows/style_check.yml@main 12 | secrets: inherit 13 | with: 14 | toolchain: nightly 15 | emulate: 16 | needs: [unit-test, style-check] 17 | uses: ./.github/workflows/emulate.yml 18 | secrets: inherit 19 | with: 20 | toolchain: nightly 21 | benchmark: 22 | needs: [emulate] 23 | uses: charlotte-os/devops/.github/workflows/benchmark.yml@main 24 | secrets: inherit 25 | with: 26 | toolchain: nightly 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iso 2 | ovmf-aarch64 3 | ovmf-riscv64 4 | ovmf-x86_64 5 | charlotte_core/target/** 6 | log.txt 7 | limine 8 | .idea 9 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "limine"] 2 | path = limine 3 | url = https://github.com/limine-bootloader/limine.git 4 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | #x86_64 2 | 3 | default: run-x86_64-debug 4 | 5 | install-requirements: 6 | chmod +x ./tools/install-requirements.sh 7 | ./tools/install-requirements.sh 8 | 9 | limine: 10 | @if [ ! -d "limine" ]; then \ 11 | git clone https://github.com/limine-bootloader/limine.git --branch=v8.x-binary --depth=1;\ 12 | fi 13 | make -C limine 14 | 15 | ovmf-x86_64: 16 | mkdir -p ovmf-x86_64 17 | cd ovmf-x86_64 && curl -o OVMF.fd https://retrage.github.io/edk2-nightly/bin/RELEASEX64_OVMF.fd 18 | 19 | build-x86_64-debug: limine 20 | cd charlotte_core && cargo build --target x86_64-unknown-none 21 | rm -rf iso_root 22 | mkdir -p iso_root 23 | cp -v charlotte_core/target/x86_64-unknown-none/debug/charlotte_core \ 24 | limine.conf limine/limine-uefi-cd.bin iso_root/ 25 | mkdir -p iso_root/EFI/BOOT 26 | cp -v limine/BOOTX64.EFI iso_root/EFI/BOOT/ 27 | xorriso -as mkisofs \ 28 | -no-emul-boot -boot-load-size 4 -boot-info-table \ 29 | --efi-boot limine-uefi-cd.bin \ 30 | -efi-boot-part --efi-boot-image --protective-msdos-label \ 31 | iso_root -o charlotte_core-x86_64-debug.iso 32 | rm -rf iso_root 33 | 34 | run-x86_64-debug: ovmf-x86_64 build-x86_64-debug 35 | qemu-system-x86_64 -enable-kvm -M q35 -cpu host -m 2G -bios ovmf-x86_64/OVMF.fd -cdrom charlotte_core-x86_64-debug.iso -boot d -serial stdio 36 | 37 | run-x86_64-debug-multicore: ovmf-x86_64 build-x86_64-debug 38 | qemu-system-x86_64 -enable-kvm -M q35 -smp 8 -cpu host -m 2G -bios ovmf-x86_64/OVMF.fd -cdrom charlotte_core-x86_64-debug.iso -boot d -serial stdio 39 | 40 | run-x86_64-debug-numa: ovmf-x86_64 build-x86_64-debug 41 | qemu-system-x86_64 -enable-kvm -M q35 -cpu host -m 8G -bios ovmf-x86_64/OVMF.fd -cdrom charlotte_core-x86_64-debug.iso -boot d -serial stdio -smp 4 -object memory-backend-ram,size=4G,id=m0 -object memory-backend-ram,size=4G,id=m1 -numa node,memdev=m0,cpus=0-1,nodeid=0 -numa node,memdev=m1,cpus=2-3,nodeid=1 42 | 43 | run-x86_64-extdb: ovmf-x86_64 build-x86_64-debug 44 | qemu-system-x86_64 -enable-kvm -s -S -M q35 -m 2G -bios ovmf-x86_64/OVMF.fd -cdrom charlotte_core-x86_64-debug.iso -boot d -serial stdio 45 | 46 | run-x86_64-log: ovmf-x86_64 build-x86_64-debug 47 | qemu-system-x86_64 -enable-kvm -M q35 -cpu host -m 12G -bios ovmf-x86_64/OVMF.fd -cdrom charlotte_core-x86_64-debug.iso -boot d -serial file:log_x86_64.txt 48 | 49 | build-x86_64-release: limine 50 | cd charlotte_core && cargo build --target x86_64-unknown-none --release 51 | rm -rf iso_root 52 | mkdir -p iso_root 53 | cp -v charlotte_core/target/x86_64-unknown-none/release/charlotte_core \ 54 | limine.conf limine/limine-uefi-cd.bin iso_root/ 55 | mkdir -p iso_root/EFI/BOOT 56 | cp -v limine/BOOTX64.EFI iso_root/EFI/BOOT/ 57 | xorriso -as mkisofs \ 58 | -no-emul-boot -boot-load-size 4 -boot-info-table \ 59 | --efi-boot limine-uefi-cd.bin \ 60 | -efi-boot-part --efi-boot-image --protective-msdos-label \ 61 | iso_root -o charlotte_core-x86_64-release.iso 62 | rm -rf iso_root 63 | 64 | run-x86_64-release: ovmf-x86_64 build-x86_64-release 65 | qemu-system-x86_64 -enable-kvm -M q35 -cpu host -m 12G -bios ovmf-x86_64/OVMF.fd -cdrom charlotte_core-x86_64-release.iso -boot d 66 | 67 | check-x86_64: 68 | cd charlotte_core && cargo check --target x86_64-unknown-none 69 | 70 | # aarch64 71 | 72 | ovmf-aarch64: 73 | mkdir -p ovmf-aarch64 74 | cd ovmf-aarch64 && curl -o OVMF.fd https://retrage.github.io/edk2-nightly/bin/RELEASEAARCH64_QEMU_EFI.fd 75 | build-aarch64-debug: limine 76 | cd charlotte_core && cargo build --target aarch64-unknown-none 77 | charlotte_core-aarch64-debug.iso: build-aarch64-debug 78 | rm -rf iso_root 79 | mkdir -p iso_root 80 | cp -v charlotte_core/target/aarch64-unknown-none/debug/charlotte_core \ 81 | limine.conf limine/limine-uefi-cd.bin iso_root/ 82 | mkdir -p iso_root/EFI/BOOT 83 | cp -v limine/BOOTAA64.EFI iso_root/EFI/BOOT/ 84 | xorriso -as mkisofs \ 85 | -no-emul-boot -boot-load-size 4 -boot-info-table \ 86 | --efi-boot limine-uefi-cd.bin \ 87 | -efi-boot-part --efi-boot-image --protective-msdos-label \ 88 | iso_root -o charlotte_core-aarch64-debug.iso 89 | rm -rf iso_root 90 | run-aarch64-debug: ovmf-aarch64 charlotte_core-aarch64-debug.iso 91 | qemu-system-aarch64 -M virt -cpu cortex-a72 -device ramfb -device qemu-xhci -device usb-kbd -m 2G -bios ovmf-aarch64/OVMF.fd -cdrom charlotte_core-aarch64-debug.iso -boot d 92 | run-aarch64-log: ovmf-aarch64 charlotte_core-aarch64-debug.iso 93 | qemu-system-aarch64 -M virt -cpu cortex-a72 -device ramfb -device qemu-xhci -device usb-kbd -m 2G -bios ovmf-aarch64/OVMF.fd -cdrom charlotte_core-aarch64-debug.iso -boot d \ 94 | -serial file:log_aarch64.txt 95 | 96 | 97 | build-aarch64-release: limine 98 | cd charlotte_core && cargo build --target aarch64-unknown-none --release 99 | charlotte_core-aarch64-release.iso: build-aarch64-release 100 | rm -rf iso_root 101 | mkdir -p iso_root 102 | cp -v charlotte_core/target/aarch64-unknown-none/release/charlotte_core \ 103 | limine.conf limine/limine-uefi-cd.bin iso_root/ 104 | mkdir -p iso_root/EFI/BOOT 105 | cp -v limine/BOOTAA64.EFI iso_root/EFI/BOOT/ 106 | xorriso -as mkisofs \ 107 | -no-emul-boot -boot-load-size 4 -boot-info-table \ 108 | --efi-boot limine-uefi-cd.bin \ 109 | -efi-boot-part --efi-boot-image --protective-msdos-label \ 110 | iso_root -o charlotte_core-aarch64-release.iso 111 | rm -rf iso_root 112 | run-aarch64-release: ovmf-aarch64 charlotte_core-aarch64-release.iso 113 | qemu-system-aarch64 -M virt -cpu cortex-a72 -device ramfb -device qemu-xhci -device usb-kbd -m 2G -bios ovmf-aarch64/OVMF.fd -cdrom charlotte_core-aarch64-release.iso -boot d 114 | 115 | # riscv64 116 | 117 | ovmf-riscv64: 118 | mkdir -p ovmf-riscv64 119 | cd ovmf-riscv64 && curl -o OVMF.fd https://retrage.github.io/edk2-nightly/bin/RELEASERISCV64_VIRT_CODE.fd && dd if=/dev/zero of=OVMF.fd bs=1 count=0 seek=33554432 120 | build-riscv64-debug: 121 | cd charlotte_core && cargo build --target riscv64gc-unknown-none-elf 122 | charlotte_core-riscv64-debug.iso: build-riscv64-debug 123 | rm -rf iso_root 124 | mkdir -p iso_root 125 | cp -v charlotte_core/target/riscv64gc-unknown-none-elf/debug/charlotte_core \ 126 | limine.conf limine/limine-uefi-cd.bin iso_root/ 127 | mkdir -p iso_root/EFI/BOOT 128 | cp -v limine/BOOTRISCV64.EFI iso_root/EFI/BOOT/ 129 | xorriso -as mkisofs \ 130 | -no-emul-boot -boot-load-size 4 -boot-info-table \ 131 | --efi-boot limine-uefi-cd.bin \ 132 | -efi-boot-part --efi-boot-image --protective-msdos-label \ 133 | iso_root -o charlotte_core-riscv64-debug.iso 134 | rm -rf iso_root 135 | run-riscv64-debug: ovmf-riscv64 charlotte_core-riscv64-debug.iso 136 | qemu-system-riscv64 -M virt -cpu rv64 \ 137 | -device ramfb -device qemu-xhci -device usb-kbd -m 2G -drive if=pflash,unit=0,format=raw,file=ovmf-riscv64/OVMF.fd \ 138 | -device virtio-scsi-pci,id=scsi -device scsi-cd,drive=cd0 -drive id=cd0,format=raw,file=charlotte_core-riscv64-debug.iso 139 | run-riscv64-debug-log: ovmf-riscv64 charlotte_core-riscv64-debug.iso 140 | qemu-system-riscv64 -M virt -cpu rv64 \ 141 | -device ramfb -device qemu-xhci -device usb-kbd -m 2G -drive if=pflash,unit=0,format=raw,file=ovmf-riscv64/OVMF.fd \ 142 | -device virtio-scsi-pci,id=scsi -device scsi-cd,drive=cd0 -drive id=cd0,format=raw,file=charlotte_core-riscv64-debug.iso \ 143 | -serial file:log_riscv64.txt 144 | 145 | build-riscv64-release: 146 | cd charlotte_core && cargo build --target riscv64gc-unknown-none-elf 147 | charlotte_core-riscv64-release.iso: build-riscv64-release 148 | rm -rf iso_root 149 | mkdir -p iso_root 150 | cp -v charlotte_core/target/riscv64gc-unknown-none-elf/release/charlotte_core \ 151 | limine.conf limine/limine-uefi-cd.bin iso_root/ 152 | mkdir -p iso_root/EFI/BOOT 153 | cp -v limine/BOOTRISCV64.EFI iso_root/EFI/BOOT/ 154 | xorriso -as mkisofs \ 155 | -no-emul-boot -boot-load-size 4 -boot-info-table \ 156 | --efi-boot limine-uefi-cd.bin \ 157 | -efi-boot-part --efi-boot-image --protective-msdos-label \ 158 | iso_root -o charlotte_core-riscv64-release.iso 159 | rm -rf iso_root 160 | run-riscv64-release: ovmf-riscv64 charlotte_core-riscv64-release.iso 161 | qemu-system-riscv64 -M virt -cpu rv64 \ 162 | -device ramfb -device qemu-xhci -device usb-kbd -m 2G -drive if=pflash,unit=0,format=raw,file=ovmf-riscv64/OVMF.fd \ 163 | -device virtio-scsi-pci,id=scsi -device scsi-cd,drive=cd0 -drive id=cd0,format=raw,file=charlotte_core-riscv64-release.iso 164 | 165 | # clean commands 166 | 167 | clean: 168 | cd charlotte_core && cargo clean 169 | rm -rf ovmf-aarch64 170 | rm -rf ovmf-riscv64 171 | rm -rf ovmf-x86_64 172 | rm -f charlotte_core-aarch64-debug.iso 173 | rm -f charlotte_core-riscv64-debug.iso 174 | rm -f charlotte_core-x86_64-debug.iso 175 | rm -f charlotte_core-aarch64-release.iso 176 | rm -f charlotte_core-riscv64-release.iso 177 | rm -f charlotte_core-x86_64-release.iso 178 | rm -f log_aarch64.txt 179 | rm -f log_riscv64.txt 180 | rm -f log_x86_64.txt 181 | 182 | distclean: clean 183 | rm -rf limine 184 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Charlotte Core 2 | 3 | ![Testing](https://github.com/charlotte-os/charlotte-core/actions/workflows/test_code.yml/badge.svg) 4 | ![Vulnerability](https://github.com/charlotte-os/charlotte-core/actions/workflows/cron_report.yml/badge.svg) 5 | 6 | ## The Kernel of CharlotteOS 7 | 8 | Charlotte Core is the kernel of CharlotteOS, a post Unix operating system. 9 | 10 | ### Status 11 | 12 | Charlotte Core is in early development. Run it at your own risk. 13 | 14 | ### Implementation Languages 15 | 16 | Charlotte Core is written in Rust and Assembly language 17 | 18 | ### Targets 19 | 20 | - x86_64-unknown-none 21 | - aarch64-unknown-none 22 | - riscv64-unknown-none-elf 23 | 24 | #### Building 25 | 26 | The following commands are used to build the debug and release versions of the kernel. 27 | 28 | ```bash 29 | make build-x86_64-debug # debug 30 | make build-x86_64-release # release 31 | ``` 32 | 33 | #### Testing 34 | 35 | ```bash 36 | make run-x86_64-debug # For testing a debug build in qemu 37 | make run-x86_64-release # For testing a release build in qemu 38 | ``` 39 | 40 | The `x86_64` portion of any of the previous commands can be replaced with `aarch64` or `riscv64` to build the kernel for the Arm and RISC-V architectures respectively however it should be noted that support for these ISAs is much less complete than for x86_64 for the time being. 41 | 42 | #### GDB debug probe 43 | 44 | Follow the steps in the `Building` section above, replacing `make run-x86_64-debug` for `run-x86_64-debugprobe`, this will start qemu, but it will appear unresponsive 45 | this is because it's waiting for gdb to connect, on linux this can be achieved by in another terminal running: 46 | 47 | ```bash 48 | gdb charlotte_core/target/x86_64-unknown-none/debug/charlotte_core 49 | ``` 50 | 51 | and once gdb is open: 52 | 53 | ```gdb 54 | (gdb) target remote localhost:1234 55 | make sure to set some breakpoints or the code will just go straight to the halt at the end of main currently 56 | (gdb) c 57 | ``` 58 | 59 | *OR* 60 | Use the .gdbinit file present in the repo, to do this you need to allow gdb to load the .gdbinit file, 61 | this can be accomplished by adding `add-auto-load-safe-path [path to the repo here]/.gdbinit` to `$HOME/.config/gdb/gdbinit`, wit this done you just need to run: 62 | 63 | ```bash 64 | # in terminal 1 65 | make run-x86_64-debugprobe 66 | # in another terminal 67 | gdb 68 | ``` 69 | 70 | if you are currently in the repo main folder you may use the snippet bellow to add the loading allow to gdbinit 71 | 72 | ```bash 73 | mkdir -p $HOME/.config/gdb/;echo "add-auto-load-safe-path $(pwd)/.gdbinit" >> $HOME/.config/gdb/gdbinit 74 | ``` 75 | 76 | further reference [Qemu GDB docs](https://qemu-project.gitlab.io/qemu/system/gdb.html) 77 | 78 | ### Documentation 79 | 80 | Detailed documentation will eventually be available in this repository's GitHub wiki and at CharlotteOS's website: https://www.charlotte-os.org/ 81 | 82 | ### Contributing 83 | 84 | Contributions are always welcome and highly appreciated. Please create a new branch for your work and submit a pull request on GitHub. Make sure to rebase all your changes on master to maintain a linear commit history avoiding merge commits to the extent possible. Feel free to grab any unassigned issues in the GitHub issue tracker for this repository. 85 | 86 | Also please join [our community on Discord](https://discord.com/invite/vE7bCCKx4X) to stay up to date with the latest on CharlotteOS development. 87 | -------------------------------------------------------------------------------- /charlotte_core/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "aarch64-cpu" 7 | version = "9.4.0" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "ac42a04a61c19fc8196dd728022a784baecc5d63d7e256c01ad1b3fbfab26287" 10 | dependencies = [ 11 | "tock-registers", 12 | ] 13 | 14 | [[package]] 15 | name = "autocfg" 16 | version = "1.1.0" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 19 | 20 | [[package]] 21 | name = "bit_field" 22 | version = "0.10.2" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61" 25 | 26 | [[package]] 27 | name = "bitflags" 28 | version = "2.4.2" 29 | source = "registry+https://github.com/rust-lang/crates.io-index" 30 | checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" 31 | 32 | [[package]] 33 | name = "cc" 34 | version = "1.0.90" 35 | source = "registry+https://github.com/rust-lang/crates.io-index" 36 | checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5" 37 | 38 | [[package]] 39 | name = "charlotte_core" 40 | version = "0.1.0" 41 | dependencies = [ 42 | "aarch64-cpu", 43 | "cc", 44 | "derive-getters", 45 | "ignore-result", 46 | "limine", 47 | "spin", 48 | "walkdir", 49 | "x86_64", 50 | ] 51 | 52 | [[package]] 53 | name = "derive-getters" 54 | version = "0.4.0" 55 | source = "registry+https://github.com/rust-lang/crates.io-index" 56 | checksum = "0a6433aac097572ea8ccc60b3f2e756c661c9aeed9225cdd4d0cb119cb7ff6ba" 57 | dependencies = [ 58 | "proc-macro2", 59 | "quote", 60 | "syn", 61 | ] 62 | 63 | [[package]] 64 | name = "ignore-result" 65 | version = "0.2.0" 66 | source = "registry+https://github.com/rust-lang/crates.io-index" 67 | checksum = "665ff4dce8edd10d490641ccb78949832f1ddbff02c584fb1f85ab888fe0e50c" 68 | 69 | [[package]] 70 | name = "limine" 71 | version = "0.2.0" 72 | source = "registry+https://github.com/rust-lang/crates.io-index" 73 | checksum = "846c87e24d089e8717a61098cba72b378e3525c46a87cf75b71352dcf668e68c" 74 | dependencies = [ 75 | "bitflags", 76 | ] 77 | 78 | [[package]] 79 | name = "lock_api" 80 | version = "0.4.11" 81 | source = "registry+https://github.com/rust-lang/crates.io-index" 82 | checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" 83 | dependencies = [ 84 | "autocfg", 85 | "scopeguard", 86 | ] 87 | 88 | [[package]] 89 | name = "proc-macro2" 90 | version = "1.0.78" 91 | source = "registry+https://github.com/rust-lang/crates.io-index" 92 | checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" 93 | dependencies = [ 94 | "unicode-ident", 95 | ] 96 | 97 | [[package]] 98 | name = "quote" 99 | version = "1.0.35" 100 | source = "registry+https://github.com/rust-lang/crates.io-index" 101 | checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" 102 | dependencies = [ 103 | "proc-macro2", 104 | ] 105 | 106 | [[package]] 107 | name = "rustversion" 108 | version = "1.0.17" 109 | source = "registry+https://github.com/rust-lang/crates.io-index" 110 | checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" 111 | 112 | [[package]] 113 | name = "same-file" 114 | version = "1.0.6" 115 | source = "registry+https://github.com/rust-lang/crates.io-index" 116 | checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" 117 | dependencies = [ 118 | "winapi-util", 119 | ] 120 | 121 | [[package]] 122 | name = "scopeguard" 123 | version = "1.2.0" 124 | source = "registry+https://github.com/rust-lang/crates.io-index" 125 | checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" 126 | 127 | [[package]] 128 | name = "spin" 129 | version = "0.9.8" 130 | source = "registry+https://github.com/rust-lang/crates.io-index" 131 | checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" 132 | dependencies = [ 133 | "lock_api", 134 | ] 135 | 136 | [[package]] 137 | name = "syn" 138 | version = "2.0.58" 139 | source = "registry+https://github.com/rust-lang/crates.io-index" 140 | checksum = "44cfb93f38070beee36b3fef7d4f5a16f27751d94b187b666a5cc5e9b0d30687" 141 | dependencies = [ 142 | "proc-macro2", 143 | "quote", 144 | "unicode-ident", 145 | ] 146 | 147 | [[package]] 148 | name = "tock-registers" 149 | version = "0.8.1" 150 | source = "registry+https://github.com/rust-lang/crates.io-index" 151 | checksum = "696941a0aee7e276a165a978b37918fd5d22c55c3d6bda197813070ca9c0f21c" 152 | 153 | [[package]] 154 | name = "unicode-ident" 155 | version = "1.0.12" 156 | source = "registry+https://github.com/rust-lang/crates.io-index" 157 | checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" 158 | 159 | [[package]] 160 | name = "volatile" 161 | version = "0.4.6" 162 | source = "registry+https://github.com/rust-lang/crates.io-index" 163 | checksum = "442887c63f2c839b346c192d047a7c87e73d0689c9157b00b53dcc27dd5ea793" 164 | 165 | [[package]] 166 | name = "walkdir" 167 | version = "2.5.0" 168 | source = "registry+https://github.com/rust-lang/crates.io-index" 169 | checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" 170 | dependencies = [ 171 | "same-file", 172 | "winapi-util", 173 | ] 174 | 175 | [[package]] 176 | name = "winapi" 177 | version = "0.3.9" 178 | source = "registry+https://github.com/rust-lang/crates.io-index" 179 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 180 | dependencies = [ 181 | "winapi-i686-pc-windows-gnu", 182 | "winapi-x86_64-pc-windows-gnu", 183 | ] 184 | 185 | [[package]] 186 | name = "winapi-i686-pc-windows-gnu" 187 | version = "0.4.0" 188 | source = "registry+https://github.com/rust-lang/crates.io-index" 189 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 190 | 191 | [[package]] 192 | name = "winapi-util" 193 | version = "0.1.6" 194 | source = "registry+https://github.com/rust-lang/crates.io-index" 195 | checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" 196 | dependencies = [ 197 | "winapi", 198 | ] 199 | 200 | [[package]] 201 | name = "winapi-x86_64-pc-windows-gnu" 202 | version = "0.4.0" 203 | source = "registry+https://github.com/rust-lang/crates.io-index" 204 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 205 | 206 | [[package]] 207 | name = "x86_64" 208 | version = "0.15.1" 209 | source = "registry+https://github.com/rust-lang/crates.io-index" 210 | checksum = "4bc79523af8abf92fb1a970c3e086c5a343f6bcc1a0eb890f575cbb3b45743df" 211 | dependencies = [ 212 | "bit_field", 213 | "bitflags", 214 | "rustversion", 215 | "volatile", 216 | ] 217 | -------------------------------------------------------------------------------- /charlotte_core/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "charlotte_core" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | [[bin]] 8 | name = "charlotte_core" 9 | test = false 10 | bench = false 11 | 12 | [build-dependencies] 13 | cc = "*" 14 | walkdir = "*" 15 | 16 | [dependencies] 17 | derive-getters = "0.4.0" 18 | ignore-result = "*" 19 | limine = "0.2.0" 20 | spin = { version = "*", features = ["ticket_mutex"] } 21 | 22 | [target.'cfg(target_arch = "x86_64")'.dependencies] 23 | x86_64 = "0.15.1" 24 | 25 | [target.'cfg(target_arch = "aarch64")'.dependencies] 26 | aarch64-cpu = "9.4.0" 27 | 28 | # Linting 29 | [lints.clippy] 30 | # Groups 31 | suspicious = { level = "deny", priority = -1 } 32 | perf = { level = "deny", priority = -1 } 33 | complexity = { level = "warn", priority = -1 } 34 | style = { level = "warn", priority = -1 } 35 | 36 | undocumented_unsafe_blocks = { level = "warn" } 37 | 38 | # Relax some lints because we don't support 32-bit targets 39 | enum_clike_unportable_variant = { level = "allow", priority = -10 } 40 | -------------------------------------------------------------------------------- /charlotte_core/build.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | 3 | fn main() { 4 | let arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap(); 5 | 6 | match arch.as_str() { 7 | "x86_64" => { 8 | // Tell cargo to pass the linker script to the linker... 9 | println!("cargo:rustc-link-arg=-Tlinker/x86_64.ld"); 10 | // ...and to re-run if it changes. 11 | println!("cargo:rerun-if-changed=linker/x86_64.ld"); 12 | } 13 | "aarch64" => { 14 | // Tell cargo to pass the linker script to the linker... 15 | println!("cargo:rustc-link-arg=-Tlinker/aarch64.ld"); 16 | // ...and to re-run if it changes. 17 | println!("cargo:rerun-if-changed=linker/aarch64.ld"); 18 | } 19 | "riscv64" => { 20 | // Tell cargo to pass the linker script to the linker... 21 | println!("cargo:rustc-link-arg=-Tlinker/riscv64.ld"); 22 | // ...and to re-run if it changes. 23 | println!("cargo:rerun-if-changed=linker/riscv64.ld"); 24 | } 25 | _ => panic!("Invalid ISA"), 26 | } 27 | 28 | println!("cargo:rerun-if-changed=asm"); 29 | } 30 | -------------------------------------------------------------------------------- /charlotte_core/linker/aarch64.ld: -------------------------------------------------------------------------------- 1 | /* Tell the linker that we want an aarch64 ELF64 output file */ 2 | OUTPUT_FORMAT(elf64-littleaarch64) 3 | OUTPUT_ARCH(aarch64) 4 | 5 | /* We want the symbol main to be our entry point */ 6 | ENTRY(main) 7 | 8 | /* Define the program headers we want so the bootloader gives us the right */ 9 | /* MMU permissions */ 10 | PHDRS 11 | { 12 | text PT_LOAD FLAGS((1 << 0) | (1 << 2)) ; /* Execute + Read */ 13 | rodata PT_LOAD FLAGS((1 << 2)) ; /* Read only */ 14 | data PT_LOAD FLAGS((1 << 1) | (1 << 2)) ; /* Write + Read */ 15 | dynamic PT_DYNAMIC FLAGS((1 << 1) | (1 << 2)) ; /* Dynamic PHDR for relocations */ 16 | } 17 | 18 | SECTIONS 19 | { 20 | /* We wanna be placed in the topmost 2GiB of the address space, for optimisations */ 21 | /* and because that is what the Limine spec mandates. */ 22 | /* Any address in this region will do, but often 0xffffffff80000000 is chosen as */ 23 | /* that is the beginning of the region. */ 24 | . = 0xffffffff80000000; 25 | 26 | .text : { 27 | *(.text .text.*) 28 | } :text 29 | 30 | /* Move to the next memory page for .rodata */ 31 | . += CONSTANT(MAXPAGESIZE); 32 | 33 | .rodata : { 34 | *(.rodata .rodata.*) 35 | } :rodata 36 | 37 | /* Move to the next memory page for .data */ 38 | . += CONSTANT(MAXPAGESIZE); 39 | 40 | .data : { 41 | *(.data .data.*) 42 | } :data 43 | 44 | /* Dynamic section for relocations, both in its own PHDR and inside data PHDR */ 45 | .dynamic : { 46 | *(.dynamic) 47 | } :data :dynamic 48 | 49 | /* NOTE: .bss needs to be the last thing mapped to :data, otherwise lots of */ 50 | /* unnecessary zeros will be written to the binary. */ 51 | /* If you need, for example, .init_array and .fini_array, those should be placed */ 52 | /* above this. */ 53 | .bss : { 54 | *(.bss .bss.*) 55 | *(COMMON) 56 | } :data 57 | 58 | /* Discard .note.* and .eh_frame since they may cause issues on some hosts. */ 59 | /DISCARD/ : { 60 | *(.eh_frame) 61 | *(.note .note.*) 62 | } 63 | } -------------------------------------------------------------------------------- /charlotte_core/linker/riscv64.ld: -------------------------------------------------------------------------------- 1 | /* Tell the linker that we want a riscv64 ELF64 output file */ 2 | OUTPUT_FORMAT(elf64-littleriscv) 3 | OUTPUT_ARCH(riscv:rv64) 4 | 5 | /* We want the symbol main to be our entry point */ 6 | ENTRY(main) 7 | 8 | /* Define the program headers we want so the bootloader gives us the right */ 9 | /* MMU permissions */ 10 | PHDRS 11 | { 12 | text PT_LOAD FLAGS((1 << 0) | (1 << 2)) ; /* Execute + Read */ 13 | rodata PT_LOAD FLAGS((1 << 2)) ; /* Read only */ 14 | data PT_LOAD FLAGS((1 << 1) | (1 << 2)) ; /* Write + Read */ 15 | dynamic PT_DYNAMIC FLAGS((1 << 1) | (1 << 2)) ; /* Dynamic PHDR for relocations */ 16 | } 17 | 18 | SECTIONS 19 | { 20 | /* We wanna be placed in the topmost 2GiB of the address space, for optimisations */ 21 | /* and because that is what the Limine spec mandates. */ 22 | /* Any address in this region will do, but often 0xffffffff80000000 is chosen as */ 23 | /* that is the beginning of the region. */ 24 | . = 0xffffffff80000000; 25 | 26 | .text : { 27 | *(.text .text.*) 28 | } :text 29 | 30 | /* Move to the next memory page for .rodata */ 31 | . += CONSTANT(MAXPAGESIZE); 32 | 33 | .rodata : { 34 | *(.rodata .rodata.*) 35 | } :rodata 36 | 37 | /* Move to the next memory page for .data */ 38 | . += CONSTANT(MAXPAGESIZE); 39 | 40 | .data : { 41 | *(.data .data.*) 42 | *(.sdata .sdata.*) 43 | } :data 44 | 45 | /* Dynamic section for relocations, both in its own PHDR and inside data PHDR */ 46 | .dynamic : { 47 | *(.dynamic) 48 | } :data :dynamic 49 | 50 | /* NOTE: .bss needs to be the last thing mapped to :data, otherwise lots of */ 51 | /* unnecessary zeros will be written to the binary. */ 52 | /* If you need, for example, .init_array and .fini_array, those should be placed */ 53 | /* above this. */ 54 | .bss : { 55 | *(.sbss .sbss.*) 56 | *(.bss .bss.*) 57 | *(COMMON) 58 | } :data 59 | 60 | /* Discard .note.* and .eh_frame since they may cause issues on some hosts. */ 61 | /DISCARD/ : { 62 | *(.eh_frame) 63 | *(.note .note.*) 64 | } 65 | } -------------------------------------------------------------------------------- /charlotte_core/linker/x86_64.ld: -------------------------------------------------------------------------------- 1 | /* Tell the linker that we want an x86_64 ELF64 output file */ 2 | OUTPUT_FORMAT(elf64-x86-64) 3 | OUTPUT_ARCH(i386:x86-64) 4 | 5 | /* We want the symbol main to be our entry point */ 6 | ENTRY(main) 7 | 8 | /* Define the program headers we want so the bootloader gives us the right */ 9 | /* MMU permissions */ 10 | PHDRS 11 | { 12 | text PT_LOAD FLAGS((1 << 0) | (1 << 2)) ; /* Execute + Read */ 13 | rodata PT_LOAD FLAGS((1 << 2)) ; /* Read only */ 14 | data PT_LOAD FLAGS((1 << 1) | (1 << 2)) ; /* Write + Read */ 15 | dynamic PT_DYNAMIC FLAGS((1 << 1) | (1 << 2)) ; /* Dynamic PHDR for relocations */ 16 | } 17 | 18 | SECTIONS 19 | { 20 | /* We wanna be placed in the topmost 2GiB of the address space, for optimisations */ 21 | /* and because that is what the Limine spec mandates. */ 22 | /* Any address in this region will do, but often 0xffffffff80000000 is chosen as */ 23 | /* that is the beginning of the region. */ 24 | . = 0xffffffff80000000; 25 | 26 | .text : { 27 | *(.text .text.*) 28 | } :text 29 | 30 | /* Move to the next memory page for .rodata */ 31 | . += CONSTANT(MAXPAGESIZE); 32 | 33 | .rodata : { 34 | *(.rodata .rodata.*) 35 | } :rodata 36 | 37 | /* Move to the next memory page for .data */ 38 | . += CONSTANT(MAXPAGESIZE); 39 | 40 | .data : { 41 | *(.data .data.*) 42 | } :data 43 | 44 | /* Dynamic section for relocations, both in its own PHDR and inside data PHDR */ 45 | .dynamic : { 46 | *(.dynamic) 47 | } :data :dynamic 48 | 49 | /* NOTE: .bss needs to be the last thing mapped to :data, otherwise lots of */ 50 | /* unnecessary zeros will be written to the binary. */ 51 | /* If you need, for example, .init_array and .fini_array, those should be placed */ 52 | /* above this. */ 53 | .bss : { 54 | *(.bss .bss.*) 55 | *(COMMON) 56 | } :data 57 | 58 | /* Discard .note.* and .eh_frame since they may cause issues on some hosts. */ 59 | /DISCARD/ : { 60 | *(.eh_frame) 61 | *(.note .note.*) 62 | } 63 | } -------------------------------------------------------------------------------- /charlotte_core/rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "nightly" 3 | targets = ["aarch64-unknown-none", "riscv64gc-unknown-none-elf", "x86_64-unknown-none"] -------------------------------------------------------------------------------- /charlotte_core/src/acpi/bgrt.rs: -------------------------------------------------------------------------------- 1 | //! Boot Graphics Record Table (BGRT) definition 2 | 3 | use core::str; 4 | 5 | use super::tables::get_table; 6 | 7 | #[repr(C)] 8 | #[derive(Copy, Clone, Debug)] 9 | pub struct Bgrt { 10 | signature: [u8; 4], 11 | length: u32, 12 | revision: u8, 13 | checksum: u8, 14 | oem_id: [u8; 6], 15 | oem_table_id: [u8; 8], 16 | oem_revision: [u8; 4], 17 | creator_id: u32, 18 | creator_revision: u8, 19 | version_id: u16, 20 | status: u8, 21 | image_type: u8, 22 | image_address: u64, 23 | x_offset: u32, 24 | y_offset: u32, 25 | } 26 | 27 | impl Bgrt { 28 | pub fn new(addr: usize) -> Option { 29 | let header = get_table(addr, *b"BGRT"); 30 | 31 | if let Some(_header) = header { 32 | let bgrt = unsafe { &*(addr as *const Bgrt) }; 33 | Some(*bgrt) 34 | } else { 35 | None 36 | } 37 | } 38 | 39 | pub fn signature(&self) -> &str { 40 | str::from_utf8(&self.signature).unwrap() 41 | } 42 | 43 | pub fn checksum(&self) -> u8 { 44 | self.checksum 45 | } 46 | 47 | pub fn oem_id(&self) -> &str { 48 | str::from_utf8(&self.oem_id).unwrap() 49 | } 50 | 51 | pub fn oem_table_id(&self) -> &str { 52 | str::from_utf8(&self.oem_table_id).unwrap() 53 | } 54 | 55 | pub fn revision(&self) -> u8 { 56 | self.revision 57 | } 58 | 59 | pub fn creator_id(&self) -> u32 { 60 | self.creator_id 61 | } 62 | 63 | pub fn creator_revision(&self) -> u8 { 64 | { 65 | self.creator_revision 66 | } 67 | } 68 | 69 | pub fn set_version(&mut self) { 70 | // version must be set to 1 71 | self.version_id = 1 72 | } 73 | 74 | pub fn version(&self) -> u16 { 75 | //can be set here too, made a seperate function for debugging 76 | self.version_id 77 | } 78 | 79 | pub fn status(&self) -> u8 { 80 | self.status 81 | } 82 | 83 | pub fn image_type(&self) -> u8 { 84 | self.image_type 85 | } 86 | 87 | pub fn image_address(&self) -> u64 { 88 | self.image_address 89 | } 90 | // x offset of the image 91 | pub fn x_offset(&self) -> u32 { 92 | self.x_offset 93 | } 94 | 95 | // y offset of the image 96 | pub fn y_offset(&self) -> u32 { 97 | self.y_offset 98 | } 99 | 100 | pub fn length(&self) -> Option { 101 | if self.revision == 0 { 102 | None 103 | } else { 104 | Some(self.length) 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /charlotte_core/src/acpi/fadt.rs: -------------------------------------------------------------------------------- 1 | //! Fixed ACPI Description Table (FADT) definition 2 | 3 | use derive_getters::Getters; 4 | 5 | use super::tables::{get_table, SDTHeader}; 6 | 7 | /// Address space types to make the meaning of the field more clear 8 | #[repr(C)] 9 | #[derive(Copy, Clone)] 10 | pub enum AddressSpace { 11 | SystemMemory = 0, 12 | SystemIO = 1, 13 | PciConfig = 2, 14 | EmbeddedController = 3, 15 | SMBus = 4, 16 | SystemCMOS = 5, 17 | PciBarTarget = 6, 18 | IPMI = 7, 19 | GeneralPurposeIO = 8, 20 | GenericSerialBus = 9, 21 | PlatformCommunicationChannel = 0xA, 22 | FunctionalFixedHardware = 0xB, 23 | SystemFirmware = 0xC, 24 | MemoryMappedIO = 0xD, 25 | PlatformResource = 0xE, 26 | Invalid = 0xF, 27 | } 28 | 29 | /// Access size types to make the meaning of the field more clear 30 | #[repr(C)] 31 | #[derive(Copy, Clone)] 32 | pub enum AccessSize { 33 | Undefined = 0, 34 | Byte = 1, 35 | Word = 2, 36 | DWord = 3, 37 | QWord = 4, 38 | } 39 | 40 | /// Generic address structure 41 | /// Used to describe registers 42 | #[repr(C)] 43 | #[derive(Copy, Clone, Debug)] 44 | pub struct GenericAddress { 45 | address_space: u8, 46 | bit_width: u8, 47 | bit_offset: u8, 48 | access_size: u8, 49 | address: u64, 50 | } 51 | 52 | impl GenericAddress { 53 | pub fn address_space(&self) -> AddressSpace { 54 | match self.address_space { 55 | 0 => AddressSpace::SystemMemory, 56 | 1 => AddressSpace::SystemIO, 57 | 2 => AddressSpace::PciConfig, 58 | 3 => AddressSpace::EmbeddedController, 59 | 4 => AddressSpace::SMBus, 60 | 5 => AddressSpace::SystemCMOS, 61 | 6 => AddressSpace::PciBarTarget, 62 | 7 => AddressSpace::IPMI, 63 | 8 => AddressSpace::GeneralPurposeIO, 64 | 9 => AddressSpace::GenericSerialBus, 65 | 0xA => AddressSpace::PlatformCommunicationChannel, 66 | 0xB => AddressSpace::FunctionalFixedHardware, 67 | 0xC => AddressSpace::SystemFirmware, 68 | 0xD => AddressSpace::MemoryMappedIO, 69 | 0xE => AddressSpace::PlatformResource, 70 | _ => AddressSpace::Invalid, 71 | } 72 | } 73 | 74 | pub fn access_size(&self) -> AccessSize { 75 | match self.access_size { 76 | 1 => AccessSize::Byte, 77 | 2 => AccessSize::Word, 78 | 3 => AccessSize::DWord, 79 | 4 => AccessSize::QWord, 80 | _ => AccessSize::Undefined, 81 | } 82 | } 83 | 84 | pub fn address(&self) -> u64 { 85 | self.address 86 | } 87 | 88 | pub fn bit_width(&self) -> u8 { 89 | self.bit_width 90 | } 91 | 92 | pub fn bit_offset(&self) -> u8 { 93 | self.bit_offset 94 | } 95 | } 96 | 97 | /// Fixed ACPI Description Table (FADT) 98 | /// The FADT is a table that provides an ACPI-compliant OS with the information it needs to 99 | /// enact power management related actions. 100 | #[repr(C)] 101 | #[derive(Copy, Clone, Getters, Debug)] 102 | pub struct Fadt { 103 | header: SDTHeader, 104 | firmware_ctrl: u32, 105 | dsdt: u32, 106 | reserved: u8, 107 | 108 | preferred_pm_profile: u8, 109 | sci_int: u16, 110 | smi_cmd: u32, 111 | acpi_enable: u8, 112 | acpi_disable: u8, 113 | s4bios_req: u8, 114 | pstate_cnt: u8, 115 | pm1a_evt_blk: u32, 116 | pm1b_evt_blk: u32, 117 | pm1a_cnt_blk: u32, 118 | pm1b_cnt_blk: u32, 119 | pm_timer_blk: u32, 120 | gpe0_blk: u32, 121 | gpe1_blk: u32, 122 | pm1_evt_len: u8, 123 | pm1_cnt_len: u8, 124 | pm_timer_len: u8, 125 | gpe0_blk_len: u8, 126 | gpe1_blk_len: u8, 127 | gpe1_base: u8, 128 | cst_cnt: u8, 129 | p_lvl2_lat: u16, 130 | p_lvl3_lat: u16, 131 | flush_size: u16, 132 | flush_stride: u16, 133 | duty_offset: u8, 134 | duty_width: u8, 135 | day_alarm: u8, 136 | month_alarm: u8, 137 | century: u8, 138 | 139 | boot_arch_flags: u16, 140 | reserved2: u8, 141 | flags: u32, 142 | 143 | reset_reg: GenericAddress, 144 | reset_value: u8, 145 | reserved3: [u8; 3], 146 | 147 | x_firmware_ctrl: u64, 148 | x_dsdt: u64, 149 | 150 | x_pm1a_evt_blk: GenericAddress, 151 | x_pm1b_evt_blk: GenericAddress, 152 | x_pm1a_cnt_blk: GenericAddress, 153 | x_pm1b_cnt_blk: GenericAddress, 154 | x_pm_timer_blk: GenericAddress, 155 | x_gpe0_blk: GenericAddress, 156 | x_gpe1_blk: GenericAddress, 157 | } 158 | 159 | impl Fadt { 160 | pub fn new(addr: usize) -> Option { 161 | let header = get_table(addr, *b"FACP"); 162 | 163 | if let Some(_header) = header { 164 | let fadt = unsafe { &*(addr as *const Fadt) }; 165 | Some(*fadt) 166 | } else { 167 | None 168 | } 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /charlotte_core/src/acpi/madt.rs: -------------------------------------------------------------------------------- 1 | //! MADT Parsing facilities 2 | use crate::acpi::tables::{get_table, SDTHeader}; 3 | use core::mem; 4 | 5 | /// The MADT 6 | #[derive(Debug, Clone, Copy)] 7 | pub struct Madt { 8 | header: SDTHeader, 9 | local_apic_addr: u32, 10 | flags: u32, 11 | addr: usize, 12 | } 13 | 14 | impl Madt { 15 | pub fn new(addr: usize) -> Madt { 16 | let header = get_table(addr, *b"APIC"); 17 | if let Some(header) = header { 18 | let local_apic_addr = unsafe { *((addr + mem::size_of::()) as *const u32) }; 19 | let flags = unsafe { *((addr + mem::size_of::() + 4) as *const u32) }; 20 | Madt { 21 | header, 22 | local_apic_addr, 23 | flags, 24 | addr, 25 | } 26 | } else { 27 | panic!("Failed to validate MADT"); 28 | } 29 | } 30 | 31 | pub fn local_apic_addr(&self) -> u32 { 32 | self.local_apic_addr 33 | } 34 | 35 | pub fn flags(&self) -> u32 { 36 | self.flags 37 | } 38 | 39 | pub fn iter(&self) -> MadtIter { 40 | MadtIter { 41 | addr: self.addr + mem::size_of::() + 8, // Skip over the header, the local APIC address and flags 42 | offset: 0, 43 | len: self.header.length() as usize - mem::size_of::() - 8, 44 | } 45 | } 46 | } 47 | 48 | /// MADT Entry Iterator 49 | pub struct MadtIter { 50 | addr: usize, 51 | offset: usize, 52 | len: usize, 53 | } 54 | 55 | impl Iterator for MadtIter { 56 | type Item = MadtEntry; 57 | 58 | fn next(&mut self) -> Option { 59 | if self.offset < self.len { 60 | let header = unsafe { &*((self.addr + self.offset) as *const MadtEntryHeader) }; 61 | 62 | let entry = match header.entry_type { 63 | 0 => MadtEntry::ProcessorLocalApic(unsafe { 64 | *((self.addr + self.offset) as *const ProcessorLocalApic) 65 | }), 66 | 1 => MadtEntry::IOApic(unsafe { *((self.addr + self.offset) as *const IoApic) }), 67 | 2 => MadtEntry::InterruptSourceOverride(unsafe { 68 | *((self.addr + self.offset) as *const InterruptSourceOverride) 69 | }), 70 | 3 => MadtEntry::NonMaskableInterruptSource(unsafe { 71 | *((self.addr + self.offset) as *const NonMaskableInterruptSource) 72 | }), 73 | 4 => MadtEntry::LocalApicNmi(unsafe { 74 | *((self.addr + self.offset) as *const LocalApicNmi) 75 | }), 76 | 5 => MadtEntry::LocalApicAddressOverride(unsafe { 77 | *((self.addr + self.offset) as *const LocalApicAddressOverride) 78 | }), 79 | _ => MadtEntry::Unknown(header.entry_type), 80 | }; 81 | self.offset += header.length as usize; 82 | Some(entry) 83 | } else { 84 | None 85 | } 86 | } 87 | } 88 | 89 | /// MADT Entries 90 | #[derive(Debug)] 91 | pub enum MadtEntry { 92 | #[allow(unused)] 93 | ProcessorLocalApic(ProcessorLocalApic), 94 | IOApic(IoApic), 95 | #[allow(unused)] 96 | InterruptSourceOverride(InterruptSourceOverride), 97 | #[allow(unused)] 98 | NonMaskableInterruptSource(NonMaskableInterruptSource), 99 | #[allow(unused)] 100 | LocalApicNmi(LocalApicNmi), 101 | #[allow(unused)] 102 | LocalApicAddressOverride(LocalApicAddressOverride), 103 | #[allow(unused)] 104 | Unknown(u8), 105 | } 106 | 107 | /// MADT Entry Header 108 | #[repr(C, packed)] 109 | #[derive(Copy, Clone, Debug)] 110 | struct MadtEntryHeader { 111 | entry_type: u8, 112 | length: u8, 113 | } 114 | 115 | /// Processor Local APIC Structure 116 | #[repr(C, packed)] 117 | #[derive(Debug, Copy, Clone)] 118 | pub struct ProcessorLocalApic { 119 | header: MadtEntryHeader, 120 | pub processor_id: u8, 121 | pub apic_id: u8, 122 | pub flags: u32, 123 | } 124 | 125 | /// IO APIC Structure 126 | #[repr(C, packed)] 127 | #[derive(Debug, Copy, Clone)] 128 | pub struct IoApic { 129 | header: MadtEntryHeader, 130 | io_apic_id: u8, 131 | reserved: u8, 132 | io_apic_addr: u32, 133 | global_system_interrupt_base: u32, 134 | } 135 | 136 | /// Interrupt Source Override Structure 137 | #[repr(C, packed)] 138 | #[derive(Debug, Copy, Clone)] 139 | pub struct InterruptSourceOverride { 140 | header: MadtEntryHeader, 141 | bus: u8, 142 | source: u8, 143 | global_system_interrupt: u32, 144 | flags: u16, 145 | } 146 | 147 | /// Non-maskable Interrupt Source Structure 148 | #[repr(C, packed)] 149 | #[derive(Debug, Copy, Clone)] 150 | pub struct NonMaskableInterruptSource { 151 | header: MadtEntryHeader, 152 | flags: u16, 153 | global_system_interrupt: u32, 154 | } 155 | 156 | /// Local APIC NMI Structure 157 | #[repr(C, packed)] 158 | #[derive(Debug, Copy, Clone)] 159 | pub struct LocalApicNmi { 160 | header: MadtEntryHeader, 161 | processor_id: u8, 162 | flags: u16, 163 | local_apic_lint: u8, 164 | } 165 | 166 | /// Local APIC Address Override Structure 167 | #[repr(C, packed)] 168 | #[derive(Debug, Copy, Clone)] 169 | pub struct LocalApicAddressOverride { 170 | header: MadtEntryHeader, 171 | reserved: u16, 172 | pub local_apic_address: u64, 173 | } 174 | -------------------------------------------------------------------------------- /charlotte_core/src/acpi/mod.rs: -------------------------------------------------------------------------------- 1 | //! # ACPI Information 2 | //! This module contains requests for information from the ACPI tables. 3 | 4 | use crate::acpi::rsdp::Rsdp; 5 | use crate::bootinfo::RSDP_REQUEST; 6 | 7 | use self::bgrt::Bgrt; 8 | use self::fadt::Fadt; 9 | use self::madt::Madt; 10 | use self::sdt::Sdt; 11 | use self::srat::Srat; 12 | 13 | pub mod bgrt; 14 | pub mod fadt; 15 | pub mod madt; 16 | pub mod rsdp; 17 | pub mod sdt; 18 | pub mod srat; 19 | pub mod tables; 20 | 21 | /// Stores the data for all the ACPI tables. 22 | #[derive(Clone, Copy)] 23 | pub struct AcpiInfo { 24 | rsdp: Rsdp, 25 | sdt: Sdt, 26 | madt: Madt, 27 | #[allow(unused)] 28 | fadt: Fadt, 29 | bgrt: Bgrt, 30 | #[allow(unused)] 31 | srat: Option, 32 | } 33 | 34 | impl AcpiInfo { 35 | /// Creates a new AcpiTables. 36 | pub fn new( 37 | rsdp: Rsdp, 38 | sdt: Sdt, 39 | madt: Madt, 40 | fadt: Fadt, 41 | bgrt: Bgrt, 42 | srat: Option, 43 | ) -> Self { 44 | Self { 45 | rsdp, 46 | sdt, 47 | madt, 48 | fadt, 49 | bgrt, 50 | srat, 51 | } 52 | } 53 | 54 | pub fn rsdp(&self) -> &Rsdp { 55 | &self.rsdp 56 | } 57 | 58 | pub fn sdt(&self) -> &Sdt { 59 | &self.sdt 60 | } 61 | 62 | pub fn madt(&self) -> &Madt { 63 | &self.madt 64 | } 65 | 66 | pub fn bgrt(&self) -> &Bgrt { 67 | &self.bgrt 68 | } 69 | } 70 | 71 | pub fn parse() -> AcpiInfo { 72 | if let Some(response) = RSDP_REQUEST.get_response() { 73 | let rsdp = Rsdp::new_from_address(response.address() as usize); 74 | let sdt = sdt::Sdt::new(&rsdp).unwrap(); 75 | 76 | let madt = Madt::new(sdt.get_table(*b"APIC").unwrap()); 77 | let fadt = Fadt::new(sdt.get_table(*b"FACP").unwrap()).unwrap(); 78 | 79 | let bgrt = Bgrt::new(sdt.get_table(*b"BGRT").unwrap()).unwrap(); 80 | 81 | let srat = if let Some(srat_addr) = sdt.get_table(*b"SRAT") { 82 | Srat::new(srat_addr) 83 | } else { 84 | None 85 | }; 86 | AcpiInfo::new(rsdp, sdt, madt, fadt, bgrt, srat) 87 | } else { 88 | panic!("Failed to obtain RSDP response."); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /charlotte_core/src/acpi/rsdp.rs: -------------------------------------------------------------------------------- 1 | use core::str; 2 | 3 | const RSDP_SIGNATURE: [u8; 8] = *b"RSD PTR "; 4 | 5 | pub const RSDP_V1_LEN: usize = 20; // Length of the RSDP for version 1 (20 bytes) 6 | 7 | /// Contains information about the RSDP (Root System Description Pointer). 8 | #[repr(C, packed)] 9 | #[derive(Copy, Clone)] 10 | pub struct Rsdp { 11 | signature: [u8; 8], 12 | checksum: u8, 13 | oem_id: [u8; 6], 14 | revision: u8, 15 | rsdt_address: u32, 16 | // Only valid on revison 2.0 and later 17 | length: u32, 18 | xsdt_address: u64, 19 | extended_checksum: u8, 20 | reserved: [u8; 3], 21 | } 22 | 23 | impl Rsdp { 24 | /// Creates a new RSDP from an address 25 | pub fn new_from_address(address: usize) -> Self { 26 | let rsdp = unsafe { &*(address as *const Rsdp) }; 27 | rsdp.validate(); 28 | *rsdp 29 | } 30 | 31 | pub fn signature(&self) -> &str { 32 | str::from_utf8(&self.signature).unwrap() 33 | } 34 | 35 | pub fn checksum(&self) -> u8 { 36 | self.checksum 37 | } 38 | 39 | pub fn oem_id(&self) -> &str { 40 | str::from_utf8(&self.oem_id).unwrap() 41 | } 42 | 43 | pub fn revision(&self) -> u8 { 44 | self.revision 45 | } 46 | 47 | pub fn rsdt_address(&self) -> u32 { 48 | self.rsdt_address 49 | } 50 | 51 | pub fn length(&self) -> Option { 52 | if self.revision == 0 { 53 | None 54 | } else { 55 | Some(self.length) 56 | } 57 | } 58 | 59 | pub fn xsdt_address(&self) -> Option { 60 | if self.revision == 0 || self.xsdt_address == 0 { 61 | None 62 | } else { 63 | Some(self.xsdt_address) 64 | } 65 | } 66 | 67 | pub fn extended_checksum(&self) -> Option { 68 | if self.revision == 0 { 69 | None 70 | } else { 71 | Some(self.extended_checksum) 72 | } 73 | } 74 | 75 | /// Validates the RSDP 76 | /// 1. Ensures the signature is valid 77 | /// 2. Ensures the OEM ID is a valid string 78 | /// 3. Ensures the checksum is valid 79 | fn validate(&self) { 80 | // Ensure the signature is valid 81 | if self.signature != RSDP_SIGNATURE { 82 | panic!("Invalid RSDP signature"); 83 | } 84 | 85 | // Ensure the OEM id is a valid string 86 | if str::from_utf8(&self.oem_id).is_err() { 87 | panic!("Invalid OEM ID"); 88 | } 89 | 90 | // Ensure the checksum is valid 91 | // Length only exists on revision 2.0 and later 92 | let length = if self.revision == 0 { 93 | RSDP_V1_LEN 94 | } else { 95 | self.length as usize 96 | }; 97 | 98 | let bytes = 99 | unsafe { core::slice::from_raw_parts(self as *const Rsdp as *const u8, length) }; 100 | let sum = bytes.iter().fold(0u8, |sum, &byte| sum.wrapping_add(byte)); 101 | 102 | if sum != 0 { 103 | panic!("Invalid RSDP checksum"); 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /charlotte_core/src/acpi/sdt.rs: -------------------------------------------------------------------------------- 1 | //! XSDT Parsing facilities 2 | 3 | use core::fmt::Write; 4 | use core::mem; 5 | 6 | use crate::logln; 7 | 8 | use super::{ 9 | rsdp::Rsdp, 10 | tables::{self, SDTHeader}, 11 | }; 12 | 13 | /// An entry in the XSDT or RSDT 14 | #[derive(Copy, Clone)] 15 | struct SdtEntry { 16 | signature: [u8; 4], 17 | addr: usize, 18 | } 19 | 20 | /// The XSDT or RSDT 21 | #[derive(Copy, Clone)] 22 | pub struct Sdt { 23 | header: SDTHeader, 24 | n_entries: usize, 25 | // TODO: when support for alloc is added, change this to a Vec 26 | sub_tables: [Option; 32], 27 | addr_width: usize, 28 | } 29 | 30 | impl Sdt { 31 | pub fn new(rsdp: &Rsdp) -> Option { 32 | let sdt_address = if rsdp.xsdt_address().is_some() { 33 | rsdp.xsdt_address().unwrap() 34 | } else { 35 | rsdp.rsdt_address() as u64 36 | }; 37 | let sdt = tables::get_table(sdt_address as usize, *b"XSDT"); 38 | if let Some(header) = sdt { 39 | let n_entries = (header.length() as usize - mem::size_of::()) / 8; 40 | let sub_tables = populate_sub_tables( 41 | (sdt_address as usize) + mem::size_of::(), 42 | n_entries, 43 | 64, 44 | ); 45 | let table = Some(Self { 46 | header, 47 | n_entries, 48 | sub_tables, 49 | addr_width: 64, 50 | }); 51 | return table; 52 | } 53 | logln!("Found XSDT but failed to validate it"); 54 | let sdt = tables::get_table(sdt_address as usize, *b"RSDT"); 55 | if let Some(header) = sdt { 56 | let n_entries = (header.length() as usize - mem::size_of::()) / 4; 57 | let sub_tables = populate_sub_tables( 58 | (sdt_address as usize) + mem::size_of::(), 59 | n_entries, 60 | 32, 61 | ); 62 | let table = Some(Self { 63 | header, 64 | n_entries, 65 | sub_tables, 66 | addr_width: 32, 67 | }); 68 | return table; 69 | } 70 | logln!("Failed to validate RSDT, bad ACPI tables, backing off."); 71 | 72 | None 73 | } 74 | 75 | /// Get the address of a table from the XSDT or RSDT 76 | pub fn get_table(&self, signature: [u8; 4]) -> Option { 77 | for i in 0..self.n_entries { 78 | if self.sub_tables[i].is_some() && self.sub_tables[i].unwrap().signature == signature { 79 | return Some(self.sub_tables[i].unwrap().addr); 80 | } 81 | } 82 | None 83 | } 84 | 85 | pub fn header(&self) -> &SDTHeader { 86 | &self.header 87 | } 88 | 89 | pub fn n_entries(&self) -> usize { 90 | self.n_entries 91 | } 92 | 93 | pub fn addr_width(&self) -> usize { 94 | self.addr_width 95 | } 96 | } 97 | 98 | /// Populates the subtables of the XSDT or RSDT 99 | fn populate_sub_tables( 100 | address: usize, 101 | n_entries: usize, 102 | addr_width: usize, 103 | ) -> [Option; 32] { 104 | let mut sub_tables: [Option; 32] = [None; 32]; 105 | for i in 0..n_entries { 106 | let mut ptr: usize = 0; 107 | // We need to grab each half independently since the XSDT uses 64-bit pointers 108 | // but the RSDT uses 32-bit pointers. 109 | 110 | // The XSDT addresses are also 4-byte aligned, so we can't treat its entries as u64 111 | // as dereferencing them would cause a misaligned access 112 | let ptr_low = unsafe { *((address + i * (addr_width / 8)) as *const u32) }; 113 | ptr |= ptr_low as usize; 114 | if addr_width == 64 { 115 | let ptr_high = unsafe { *((address + i * (addr_width / 8) + 4) as *const u32) }; 116 | ptr |= (ptr_high as usize) << 32; 117 | } 118 | let table = tables::get_table_any_sig(ptr); 119 | if let Some(header) = table { 120 | sub_tables[i] = Some(SdtEntry { 121 | signature: *header.signature_bytes(), 122 | addr: ptr, 123 | }); 124 | } 125 | } 126 | sub_tables 127 | } 128 | -------------------------------------------------------------------------------- /charlotte_core/src/acpi/srat.rs: -------------------------------------------------------------------------------- 1 | use crate::logln; 2 | 3 | use super::tables::{get_table, SDTHeader}; 4 | use core::{fmt::Write, mem, usize}; 5 | 6 | #[repr(C)] 7 | #[derive(Copy, Clone, Debug)] 8 | pub struct Srat { 9 | header: SDTHeader, 10 | length: u32, // set this to SDTHeader.length 11 | addr: usize, 12 | } 13 | 14 | impl Srat { 15 | pub fn new(addr: usize) -> Option { 16 | let header = get_table(addr, *b"SRAT"); 17 | if let Some(header) = header { 18 | Some(Srat { 19 | header, 20 | length: header.length() + 32, 21 | addr, 22 | }) 23 | } else { 24 | None 25 | } 26 | } 27 | 28 | pub fn header(&self) -> SDTHeader { 29 | self.header 30 | } 31 | 32 | pub fn iter(&self) -> SratIter { 33 | SratIter { 34 | addr: self.addr, 35 | length: self.length - 0x30, 36 | offset: 0x30, 37 | } 38 | } 39 | } 40 | 41 | impl Iterator for SratIter { 42 | type Item = SratEntry; 43 | 44 | fn next(&mut self) -> Option { 45 | if self.offset < (self.length - 1) as usize { 46 | let next_type = unsafe { *((self.addr + self.offset) as *const u8) }; 47 | let mut next_len: usize = 1; 48 | logln!("{:X}", next_type); 49 | let entry = match next_type { 50 | 0 => { 51 | next_len = mem::size_of::(); 52 | SratEntry::ProcessorLocalApic(unsafe { 53 | *((self.addr + self.offset) as *const ProcessorLocalApic) 54 | }) 55 | } 56 | 1 => { 57 | next_len = mem::size_of::(); 58 | SratEntry::MemoryAffinityStructure(unsafe { 59 | *((self.addr + self.offset) as *const MemoryAffinityStructure) 60 | }) 61 | } 62 | 2 => { 63 | next_len = mem::size_of::(); 64 | SratEntry::ProcessorLocalApicX2(unsafe { 65 | *((self.addr + self.offset) as *const ProcessorLocalApicX2) 66 | }) 67 | } 68 | _ => SratEntry::Unknown(next_type), 69 | }; 70 | self.offset += next_len; 71 | Some(entry) 72 | } else { 73 | None 74 | } 75 | } 76 | } 77 | 78 | pub struct SratIter { 79 | addr: usize, 80 | length: u32, 81 | offset: usize, 82 | } 83 | 84 | #[derive(Debug)] 85 | pub enum SratEntry { 86 | #[allow(unused)] 87 | ProcessorLocalApic(ProcessorLocalApic), 88 | #[allow(unused)] 89 | MemoryAffinityStructure(MemoryAffinityStructure), 90 | #[allow(unused)] 91 | ProcessorLocalApicX2(ProcessorLocalApicX2), 92 | #[allow(unused)] 93 | Unknown(u8), 94 | } 95 | 96 | #[derive(Copy, Clone, Debug)] 97 | #[allow(unused)] 98 | pub struct ProcessorLocalApic { 99 | // Changed to pub 100 | entry_type: u8, 101 | length: u8, 102 | p_domain: u8, 103 | apic_id: u8, 104 | flags: u32, 105 | local_sapic_eid: u8, 106 | hi_dm: [u8; 3], 107 | clock_domain: u32, 108 | } 109 | 110 | #[derive(Copy, Clone, Debug)] 111 | #[allow(unused)] 112 | pub struct MemoryAffinityStructure { 113 | // Changed to pub 114 | entry_type: u8, 115 | length: u8, 116 | p_domain: u8, 117 | reserved: [u8; 2], 118 | base_addr_low: u32, 119 | base_addr_high: u32, 120 | length_low: u32, 121 | length_high: u32, 122 | reserved_2: [u8; 4], 123 | flags: u32, 124 | reserved_3: [u8; 8], 125 | } 126 | 127 | #[derive(Copy, Clone, Debug)] 128 | #[allow(unused)] 129 | pub struct ProcessorLocalApicX2 { 130 | // Changed to pub 131 | entry_type: u8, 132 | length: u8, 133 | reserved: [u8; 2], 134 | p_domain: u32, 135 | apic_id_x2: u32, 136 | flags: u32, 137 | clock_domain: u32, 138 | reserved_2: [u8; 4], 139 | } 140 | -------------------------------------------------------------------------------- /charlotte_core/src/acpi/tables.rs: -------------------------------------------------------------------------------- 1 | //! ACPI tables handling 2 | 3 | use core::str; 4 | 5 | use core::fmt::Write; 6 | 7 | use crate::logln; 8 | 9 | #[repr(C, packed)] 10 | #[derive(Copy, Clone, Debug)] 11 | pub struct SDTHeader { 12 | signature: [u8; 4], 13 | length: u32, 14 | revision: u8, 15 | checksum: u8, 16 | oem_id: [u8; 6], 17 | oem_table_id: [u8; 8], 18 | oem_revision: u32, 19 | creator_id: u32, 20 | creator_revision: u32, 21 | } 22 | 23 | impl SDTHeader { 24 | pub fn signature(&self) -> &str { 25 | str::from_utf8(&self.signature).unwrap() 26 | } 27 | 28 | pub fn signature_bytes(&self) -> &[u8; 4] { 29 | &self.signature 30 | } 31 | 32 | pub fn length(&self) -> u32 { 33 | self.length 34 | } 35 | 36 | pub fn revision(&self) -> u8 { 37 | self.revision 38 | } 39 | 40 | pub fn checksum(&self) -> u8 { 41 | self.checksum 42 | } 43 | 44 | pub fn oem_id(&self) -> &str { 45 | str::from_utf8(&self.oem_id).unwrap() 46 | } 47 | 48 | pub fn oem_table_id(&self) -> &str { 49 | str::from_utf8(&self.oem_table_id).unwrap() 50 | } 51 | 52 | pub fn oem_revision(&self) -> u32 { 53 | self.oem_revision 54 | } 55 | 56 | pub fn creator_id(&self) -> u32 { 57 | self.creator_id 58 | } 59 | 60 | pub fn creator_revision(&self) -> u32 { 61 | self.creator_revision 62 | } 63 | } 64 | 65 | pub fn validate_checksum(data: &[u8]) -> bool { 66 | let mut sum: u8 = 0; 67 | for byte in data { 68 | sum = sum.wrapping_add(*byte); 69 | } 70 | sum == 0 71 | } 72 | 73 | pub fn get_table(address: usize, sig: [u8; 4]) -> Option { 74 | let header = unsafe { &*(address as *const SDTHeader) }; 75 | if *header.signature_bytes() == sig { 76 | logln!("Found table with signature: {}", header.signature()); 77 | if validate_checksum(unsafe { 78 | core::slice::from_raw_parts(address as *const u8, header.length() as usize) 79 | }) { 80 | logln!("Checksum is valid"); 81 | return Some(*header); 82 | } else { 83 | logln!("Checksum is invalid"); 84 | } 85 | } 86 | None 87 | } 88 | 89 | pub fn get_table_any_sig(address: usize) -> Option { 90 | let header = unsafe { &*(address as *const SDTHeader) }; 91 | logln!("Found table with signature: {}", header.signature()); 92 | if validate_checksum(unsafe { 93 | core::slice::from_raw_parts(address as *const u8, header.length() as usize) 94 | }) { 95 | logln!("Checksum is valid"); 96 | return Some(*header); 97 | } else { 98 | logln!("Checksum is invalid"); 99 | } 100 | None 101 | } 102 | -------------------------------------------------------------------------------- /charlotte_core/src/arch/aarch64/memory/mod.rs: -------------------------------------------------------------------------------- 1 | //! # Memory Management Subsystem 2 | //! The memory management subsystem is responsible for managing the direct mapping of physical 3 | //! memory in the kernel's address space, allocating and deallocating physical frames, and managing 4 | //! all virtual address spaces. 5 | 6 | use aarch64_cpu::registers::Readable; 7 | use spin::lazy::Lazy; 8 | 9 | /// The number of significant bits in a physical address on the current CPU. 10 | pub static PADDR_SIG_BITS: Lazy = Lazy::new(|| { 11 | use aarch64_cpu::registers::ID_AA64MMFR0_EL1; 12 | use aarch64_cpu::registers::ID_AA64MMFR0_EL1::PARange::*; 13 | 14 | match ID_AA64MMFR0_EL1 15 | .read_as_enum::(ID_AA64MMFR0_EL1::PARange) 16 | { 17 | Some(Value::Bits_32) => 32, 18 | Some(Value::Bits_36) => 36, 19 | Some(Value::Bits_40) => 40, 20 | Some(Value::Bits_42) => 42, 21 | Some(Value::Bits_44) => 44, 22 | Some(Value::Bits_48) => 48, 23 | Some(Value::Bits_52) => 52, 24 | _ => panic!("CPU is broken"), 25 | } 26 | }); 27 | 28 | /// The number of significant bits in a virtual address on the current CPU. 29 | pub static VADDR_SIG_BITS: Lazy = Lazy::new(|| { 30 | use aarch64_cpu::registers::ID_AA64MMFR2_EL1; 31 | 32 | match ID_AA64MMFR2_EL1.read(ID_AA64MMFR2_EL1::VARange) { 33 | 0 => 48, 34 | 1 => 52, 35 | _ => panic!("CPU is broken"), 36 | } 37 | }); 38 | -------------------------------------------------------------------------------- /charlotte_core/src/arch/aarch64/mod.rs: -------------------------------------------------------------------------------- 1 | use core::arch::asm; 2 | 3 | pub mod memory; 4 | pub mod uart; 5 | 6 | use uart::Uart; 7 | 8 | pub struct Api; 9 | 10 | /// Provide the implementation of the Api trait for the Api struct 11 | impl crate::arch::Api for Api { 12 | /// Define the logger type 13 | type DebugLogger = Uart; 14 | 15 | /// Get a new logger instance 16 | fn get_logger() -> Self::DebugLogger { 17 | Uart::new() 18 | } 19 | /// Get the number of significant physical address bits supported by the current CPU 20 | fn get_paddr_width() -> u8 { 21 | *memory::PADDR_SIG_BITS 22 | } 23 | /// Get the number of significant virtual address bits supported by the current CPU 24 | fn get_vaddr_width() -> u8 { 25 | *memory::VADDR_SIG_BITS 26 | } 27 | /// Halt the calling LP 28 | fn halt() -> ! { 29 | // TODO: + disable IRQs, when they are ready 30 | unsafe { asm!("wfi") }; 31 | loop {} 32 | } 33 | /// Kernel Panic 34 | fn panic() -> ! { 35 | Self::halt() 36 | } 37 | 38 | /// Read a byte from the specified port 39 | fn inb(_port: u16) -> u8 { 40 | todo!() 41 | } 42 | 43 | /// Write a byte to the specified port 44 | fn outb(_port: u16, _val: u8) { 45 | todo!() 46 | } 47 | /// Initialize the bootstrap processor (BSP) 48 | fn init_bsp() {} 49 | /// 50 | /// Initialize the application processors (APs) 51 | fn init_ap() { 52 | //! This routine is run by each application processor to initialize itself prior to being handed off to the scheduler. 53 | todo!() 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /charlotte_core/src/arch/aarch64/uart.rs: -------------------------------------------------------------------------------- 1 | use core::fmt::Write; 2 | 3 | pub struct Uart {} 4 | 5 | impl Uart { 6 | pub fn new() -> Self { 7 | Self {} 8 | } 9 | } 10 | 11 | // ToDo: pl011 uart when vmm is done 12 | impl Write for Uart { 13 | fn write_str(&mut self, _s: &str) -> core::fmt::Result { 14 | Ok(()) 15 | } 16 | fn write_char(&mut self, _c: char) -> core::fmt::Result { 17 | Ok(()) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /charlotte_core/src/arch/mod.rs: -------------------------------------------------------------------------------- 1 | //! # Arch 2 | //! This module provides a common interface for interacting with code that is specific to each supported 3 | //! instruction set architecture (ISA). It provides a set of traits and types that can be used to interact 4 | //! with ISA specific code in a consistent and platform independent manner. 5 | 6 | use core::fmt; 7 | use core::fmt::Write; 8 | use core::result::Result; 9 | 10 | use spin::{lazy::Lazy, mutex::TicketMutex}; 11 | 12 | use crate::framebuffer::console::CONSOLE; 13 | use crate::memory::address::{PhysicalAddress, UAddr, VirtualAddress}; 14 | 15 | #[cfg(target_arch = "aarch64")] 16 | pub mod aarch64; 17 | #[cfg(target_arch = "riscv64")] 18 | pub mod riscv64; 19 | #[cfg(target_arch = "x86_64")] 20 | pub mod x86_64; 21 | 22 | pub static LOGGER: Lazy> = Lazy::new(|| { 23 | TicketMutex::new(Logger { 24 | logger: ::get_logger(), 25 | }) 26 | }); 27 | 28 | pub trait MemoryMap { 29 | type Error; 30 | type Flags; 31 | 32 | /// Loads the page map into the logical processor. 33 | unsafe fn load(&self) -> Result<(), Self::Error>; 34 | 35 | /// Maps a page at the given virtual address. 36 | /// # Arguments 37 | /// * `vaddr` - The virtual address to map the page to 38 | /// * `paddr` - The physical base address of the page frame to be mapped 39 | /// * `flags` - The flags to apply to the page table entry 40 | fn map_page( 41 | &mut self, 42 | vaddr: VirtualAddress, 43 | paddr: PhysicalAddress, 44 | flags: Self::Flags, 45 | ) -> Result<(), Self::Error>; 46 | 47 | /// Unmaps a page from the given page map at the given virtual address. 48 | /// # Arguments 49 | /// * `vaddr` - The virtual address to unmap. 50 | /// # Returns 51 | /// Returns an error of type `Self::Error` if unmapping fails or the physical address that was 52 | /// previously mapped to the given virtual address if successful. 53 | fn unmap_page(&mut self, vaddr: VirtualAddress) -> Result; 54 | 55 | /// Maps a large page (2 MiB) at the given virtual address. 56 | /// # Arguments 57 | /// * `vaddr` - The virtual address to map. 58 | /// * `paddr` - The physical address to map. 59 | /// * `flags` - The flags to apply to the page table entry. 60 | /// # Returns 61 | /// Returns an error of type `Self::Error` if mapping fails. 62 | fn map_large_page( 63 | &mut self, 64 | vaddr: VirtualAddress, 65 | paddr: PhysicalAddress, 66 | flags: Self::Flags, 67 | ) -> Result<(), Self::Error>; 68 | 69 | /// Unmaps a large page from the given page map at the given virtual address. 70 | /// # Arguments 71 | /// * `vaddr` - The virtual address to unmap. 72 | /// # Returns 73 | /// Returns an error of type `Self::Error` if unmapping fails or the physical address that was 74 | /// previously mapped to the given virtual address if successful. 75 | fn unmap_large_page(&mut self, vaddr: VirtualAddress) -> Result; 76 | 77 | /// Maps a huge page (1 GiB) at the given virtual address. 78 | /// # Arguments 79 | /// * `vaddr` - The virtual address to map. 80 | /// * `paddr` - The physical address to map. 81 | /// * `flags` - The flags to apply to the page table entry. 82 | /// # Returns 83 | /// Returns an error of type `Self::Error` if mapping fails. 84 | fn map_huge_page( 85 | &mut self, 86 | vaddr: VirtualAddress, 87 | paddr: PhysicalAddress, 88 | flags: Self::Flags, 89 | ) -> Result<(), Self::Error>; 90 | 91 | /// Unmaps a huge page from the given page map at the given virtual address. 92 | /// # Arguments 93 | /// * `vaddr` - The virtual address to unmap. 94 | /// # Returns 95 | /// Returns an error of type `Self::Error` if unmapping fails or the physical address that was 96 | /// previously mapped to the given virtual address if successful. 97 | fn unmap_huge_page(&mut self, vaddr: VirtualAddress) -> Result; 98 | } 99 | pub enum HwTimerMode { 100 | OneShot, 101 | Recurrent, 102 | } 103 | 104 | #[derive(Debug, Copy, Clone)] 105 | pub struct PagingParams { 106 | pub page_size: UAddr, 107 | pub page_shift: UAddr, 108 | pub page_mask: UAddr, 109 | } 110 | 111 | #[derive(Debug, Copy, Clone)] 112 | pub struct IsaParams { 113 | pub paging: PagingParams, 114 | } 115 | 116 | pub trait Api { 117 | type Api: Api; 118 | type DebugLogger: Write; 119 | type Serial: Serial; 120 | 121 | /// Each ISA implementation does something specific within this function, 122 | /// you should check the relevant implementation under each ISA folder linked bellow: 123 | /// * [X86_64](x86_64::Api::isa_init) 124 | fn isa_init() -> Self; 125 | 126 | fn get_logger() -> Self::DebugLogger; 127 | fn get_serial(&self) -> Self::Serial; 128 | fn get_paddr_width() -> u8; 129 | fn get_vaddr_width() -> u8; 130 | fn validate_paddr(raw: usize) -> bool; 131 | fn validate_vaddr(raw: u64) -> bool; 132 | #[allow(unused)] 133 | fn halt() -> !; 134 | fn panic() -> !; 135 | fn inb(port: u16) -> u8; 136 | fn outb(port: u16, val: u8); 137 | #[allow(unused)] 138 | fn init_ap(&mut self); 139 | /// Sets up the ISA specific timer(s) 140 | /// ## Notes: 141 | /// * for ISAs with only one timer timer_id is ignored 142 | /// * some ISAs have timers that can't be as precise as say 10 tps, check the ISA manuals for details 143 | fn setup_isa_timer(&mut self, tps: u32, mode: HwTimerMode, timer_id: u16); 144 | fn start_isa_timers(&self); 145 | fn pause_isa_timers(&self); 146 | fn init_interrupts(&mut self); 147 | fn interrupts_enabled(&self) -> bool; 148 | #[allow(unused)] 149 | fn disable_interrupts(&mut self); 150 | #[allow(unused)] 151 | fn restore_interrupts(&mut self); 152 | #[allow(unused)] 153 | fn set_interrupt_handler(&mut self, h: fn(vector: u64), vector: u32); 154 | #[allow(unused)] 155 | fn end_of_interrupt(); 156 | } 157 | 158 | pub trait Serial { 159 | fn read_char(&mut self) -> char; 160 | fn put_char(&mut self, c: char); 161 | } 162 | 163 | /// A logger that writes to both the framebuffer console and the serial port. 164 | pub struct Logger { 165 | logger: ::DebugLogger, 166 | } 167 | 168 | impl Write for Logger { 169 | fn write_str(&mut self, s: &str) -> fmt::Result { 170 | write!(self.logger, "{}", s).unwrap(); 171 | write!(CONSOLE.lock(), "{}", s).unwrap(); 172 | Ok(()) 173 | } 174 | } 175 | 176 | #[macro_export] 177 | macro_rules! log { 178 | ($($arg:tt)*) => { 179 | $crate::arch::LOGGER.lock().write_fmt(format_args!($($arg)*)).unwrap(); 180 | }; 181 | } 182 | 183 | #[macro_export] 184 | macro_rules! logln { 185 | ($($arg:tt)*) => { 186 | $crate::arch::LOGGER.lock().write_fmt(format_args!($($arg)*)).unwrap(); 187 | $crate::arch::LOGGER.lock().write_str("\n").unwrap(); 188 | }; 189 | } 190 | 191 | #[cfg(target_arch = "x86_64")] 192 | pub type ArchApi = x86_64::Api; 193 | #[cfg(target_arch = "aarch64")] 194 | pub type ArchApi = aarch64::Api; 195 | #[cfg(target_arch = "riscv64")] 196 | pub type ArchApi = riscv64::Api; 197 | 198 | #[cfg(target_arch = "x86_64")] 199 | pub const ISA_PARAMS: IsaParams = x86_64::X86_ISA_PARAMS; 200 | #[cfg(target_arch = "aarch64")] 201 | pub static MEMORY_PARAMS: PagingParams = aarch64::ISA_MEMORY_PARAMS; 202 | #[cfg(target_arch = "riscv64")] 203 | pub static MEMORY_PARAMS: PagingParams = riscv64::ISA_MEMORY_PARAMS; 204 | -------------------------------------------------------------------------------- /charlotte_core/src/arch/riscv64/mod.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /charlotte_core/src/arch/x86_64/cpu/cpu.asm: -------------------------------------------------------------------------------- 1 | .code64 2 | 3 | .text 4 | .global asm_halt 5 | asm_halt: // Halt the processor 6 | cli 7 | hlt 8 | jmp asm_halt // Jump to asm_halt in case the processor is not halted 9 | 10 | .global asm_inb 11 | asm_inb: 12 | mov dx, di 13 | in al, dx 14 | ret 15 | 16 | .global asm_outb 17 | asm_outb: 18 | mov dx, di 19 | mov al, sil 20 | out dx, al 21 | ret 22 | 23 | .global asm_get_privilege_level 24 | asm_get_privilege_level: 25 | // this routine takes in 0 params 26 | mov eax, cs 27 | and eax, 3 28 | ret 29 | 30 | .global asm_get_vendor_string 31 | asm_get_vendor_string: 32 | mov r10, rbx //preserve rbx 33 | mov eax, 0 //eax = leaf 34 | mov ecx, 0 //ecx = subleaf 35 | cpuid 36 | mov[rdi], ebx 37 | mov[rdi+4], edx 38 | mov[rdi+8], ecx 39 | mov rbx, r10 //restore rbx 40 | ret -------------------------------------------------------------------------------- /charlotte_core/src/arch/x86_64/cpu/cpu_intrinsics.rs: -------------------------------------------------------------------------------- 1 | use crate::arch::x86_64::cpu::MSRValue; 2 | use core::arch::{asm, global_asm}; 3 | 4 | pub unsafe fn asm_read_msr(selector: u32) -> MSRValue { 5 | let mut eax: u32; 6 | let mut edx: u32; 7 | 8 | asm!( 9 | "rdmsr", 10 | in("ecx") selector, 11 | out("eax") eax, 12 | out("edx") edx, 13 | ); 14 | 15 | MSRValue { eax, edx } 16 | } 17 | 18 | pub unsafe fn asm_write_msr(selector: u32, value: MSRValue) { 19 | asm!( 20 | "wrmsr", 21 | in("ecx") selector, 22 | in("eax") value.eax, 23 | in("edx") value.edx, 24 | ); 25 | } 26 | 27 | global_asm! { 28 | include_str!("cpu.asm") 29 | } 30 | -------------------------------------------------------------------------------- /charlotte_core/src/arch/x86_64/cpu/mod.rs: -------------------------------------------------------------------------------- 1 | use core::arch::asm; 2 | use core::arch::x86_64::{__cpuid, __cpuid_count}; 3 | 4 | use spin::lazy::Lazy; 5 | 6 | use crate::arch::x86_64::cpu::cpu_intrinsics::{asm_read_msr, asm_write_msr}; 7 | 8 | mod cpu_intrinsics; 9 | 10 | /// The number of significant bits in a physical address on the current CPU. 11 | pub static PADDR_SIG_BITS: Lazy = Lazy::new(|| { 12 | let cpuid = unsafe { __cpuid_count(0x80000008, 0) }; 13 | // 0x80000008 is the highest cpuid leaf that returns the physical address width in EAX[7:0] 14 | let psig_bits = cpuid.eax & 0xFF; 15 | psig_bits as u8 16 | }); 17 | 18 | /// The number of significant bits in a virtual address on the current CPU. 19 | pub static VADDR_SIG_BITS: Lazy = Lazy::new(|| { 20 | let cpuid = unsafe { __cpuid_count(0x80000008, 0) }; 21 | // 0x80000008 is the highest cpuid leaf that returns the virtual address width in EAX[15:8] 22 | let vsig_bits = (cpuid.eax >> 8) & 0xFF; 23 | vsig_bits as u8 24 | }); 25 | 26 | pub static ARE_HUGE_PAGES_SUPPORTED: Lazy = Lazy::new(huge_pages_supported); 27 | pub static CPU_HAS_MSR: Lazy = Lazy::new(|| { 28 | let res = unsafe { __cpuid_count(0, 0) }; 29 | res.edx & 1 << 5 != 0 30 | }); 31 | 32 | extern "C" { 33 | pub fn asm_halt() -> !; 34 | pub fn asm_get_vendor_string(dest: &mut [u8; 12]); 35 | pub fn asm_get_privilege_level() -> u8; 36 | } 37 | 38 | pub struct MSRValue { 39 | pub eax: u32, 40 | pub edx: u32, 41 | } 42 | 43 | pub fn get_privilege_level() -> u8 { 44 | unsafe { asm_get_privilege_level() } 45 | } 46 | 47 | pub fn read_msr(msr: u32) -> MSRValue { 48 | if !*CPU_HAS_MSR { 49 | panic!("Processor lacks msr support and read_msr was called!"); 50 | } 51 | unsafe { asm_read_msr(msr) } 52 | } 53 | 54 | pub fn read_msr_u64(msr: u32) -> u64 { 55 | if !*CPU_HAS_MSR { 56 | panic!("Processor lacks msr support and read_msr was called!"); 57 | } 58 | let regs = unsafe { asm_read_msr(msr) }; 59 | let mut res = 0u64; 60 | res = (res | (regs.edx as u64) << 32) | regs.eax as u64; 61 | res 62 | } 63 | 64 | pub fn write_msr(msr: u32, value: MSRValue) { 65 | if !*CPU_HAS_MSR { 66 | panic!("Processor lacks msr support and write_msr was called!"); 67 | } 68 | unsafe { asm_write_msr(msr, value) }; 69 | } 70 | 71 | pub fn set_msr_bit(msr: u32, bit: u8) { 72 | let mut val = read_msr(msr); 73 | val.edx |= 1 << bit; 74 | write_msr(msr, val); 75 | } 76 | 77 | pub fn clear_msr_bit(msr: u32, bit: u8) { 78 | let mut val = read_msr(msr); 79 | val.edx &= !(1 << bit); 80 | write_msr(msr, val); 81 | } 82 | 83 | /// Test the flags of the processor to determine if the interrupts are enabled 84 | pub fn asm_are_interrupts_enabled() -> bool { 85 | let mut flags: u64; 86 | unsafe { asm!("pushf\n\tpop {}", out(reg) flags) }; 87 | (flags & 1 << 9) != 0 88 | } 89 | 90 | #[allow(unused)] 91 | pub fn irq_disable() { 92 | unsafe { 93 | asm!("cli"); 94 | }; 95 | } 96 | 97 | #[allow(unused)] 98 | pub fn irq_restore() { 99 | unsafe { 100 | asm!("sti"); 101 | }; 102 | } 103 | 104 | pub fn asm_outb(port: u16, val: u8) { 105 | unsafe { 106 | asm!( 107 | " 108 | out dx, al 109 | ", 110 | in("dx") port, 111 | in("al") val, 112 | ); 113 | } 114 | } 115 | 116 | pub fn asm_inb(port: u16) -> u8 { 117 | let val: u8; 118 | unsafe { 119 | asm!( 120 | " 121 | in al, dx 122 | ", 123 | out("al") val, 124 | in("dx") port, 125 | ); 126 | } 127 | val 128 | } 129 | 130 | /// outputs `word` to `port` 131 | pub fn asm_outw(port: u16, word: u16) { 132 | unsafe { 133 | asm!( 134 | " 135 | out dx, ax 136 | ", 137 | in("dx") port, 138 | in("ax") word, 139 | ); 140 | } 141 | } 142 | 143 | /// outputs `dword` to `port` 144 | pub fn asm_outdw(port: u16, dword: u32) { 145 | unsafe { 146 | asm!( 147 | " 148 | out dx, eax 149 | ", 150 | in("dx") port, 151 | in("eax") dword, 152 | ); 153 | } 154 | } 155 | 156 | pub fn asm_inw(port: u16) -> u16 { 157 | let word: u16; 158 | unsafe { 159 | asm!( 160 | " 161 | in ax, dx 162 | ", 163 | out("ax") word, 164 | in("dx") port, 165 | ); 166 | } 167 | word 168 | } 169 | 170 | pub fn asm_indw(port: u16) -> u32 { 171 | let dword: u32; 172 | unsafe { 173 | asm!( 174 | " 175 | in eax, dx 176 | ", 177 | out("eax") dword, 178 | in("dx") port, 179 | ); 180 | } 181 | dword 182 | } 183 | 184 | pub fn get_tsc_frequency() -> u32 { 185 | let cpuid_res = unsafe { __cpuid(0x15) }; 186 | 187 | if cpuid_res.ebx == 0 { 188 | panic!("TSC clock ratio is not enumerated!"); 189 | } 190 | 191 | cpuid_res.ebx / cpuid_res.eax 192 | } 193 | 194 | /// Determines whether the current LP supports huge pages. 195 | /// Returns `true` if huge pages are supported, `false` otherwise. 196 | fn huge_pages_supported() -> bool { 197 | let cpuid_result = unsafe { __cpuid_count(0x80000001, 0) }; 198 | let edx = cpuid_result.edx; 199 | edx & (1 << 26) != 0 200 | } 201 | -------------------------------------------------------------------------------- /charlotte_core/src/arch/x86_64/exceptions/exceptions.asm: -------------------------------------------------------------------------------- 1 | .code64 2 | 3 | .text 4 | //Handlers 5 | .extern ih_divide_by_zero 6 | .extern ih_double_fault 7 | .extern ih_general_protection_fault 8 | .extern ih_page_fault 9 | .extern ih_segment_not_present 10 | .extern ih_debug 11 | .extern ih_non_maskable_interrupt 12 | .extern ih_breakpoint 13 | .extern ih_overflow 14 | .extern ih_bound_range_exceeded 15 | .extern ih_invalid_opcode 16 | .extern ih_device_not_available 17 | .extern ih_invalid_tss 18 | .extern ih_stack_segment_fault 19 | .extern ih_reserved 20 | .extern ih_x87_floating_point 21 | .extern ih_alignment_check 22 | .extern ih_machine_check 23 | .extern ih_simd_floating_point 24 | .extern ih_virtualization 25 | .extern ih_control_protection 26 | .extern ih_hypervisor_injection 27 | .extern ih_vmm_communication 28 | .extern ih_security_exception 29 | 30 | //The actual ISRs 31 | .global isr_divide_by_zero 32 | isr_divide_by_zero: 33 | call save_regs 34 | call ih_divide_by_zero 35 | call restore_regs 36 | iretq 37 | 38 | .global isr_double_fault 39 | isr_double_fault: 40 | //Registers are not saved since this exception is an abort 41 | pop rdi //pop the error code (should always be 0) 42 | call ih_double_fault 43 | hlt //halt the core since double faults are an abort 44 | 45 | .global isr_general_protection_fault 46 | isr_general_protection_fault: 47 | call save_regs 48 | pop rdi //pop the error code 49 | mov rsi, [rip] // Save the RIP to RSI for the handler 50 | call ih_general_protection_fault 51 | hlt 52 | call restore_regs 53 | iretq 54 | 55 | .global isr_page_fault 56 | isr_page_fault: 57 | call save_regs 58 | pop rdi //pop the error code 59 | call ih_page_fault 60 | call restore_regs 61 | iretq 62 | 63 | .global isr_segment_not_present 64 | isr_segment_not_present: 65 | call save_regs 66 | pop rdi // Pop the error code into RDI for the handler 67 | call ih_segment_not_present 68 | push rdi // Push the error code back onto the stack for restoring context 69 | call restore_regs 70 | add rsp, 8 // Clean up the error code from the stack 71 | iretq 72 | 73 | .global isr_debug 74 | isr_debug: 75 | call save_regs 76 | call ih_debug 77 | call restore_regs 78 | iretq 79 | 80 | .global isr_non_maskable_interrupt 81 | isr_non_maskable_interrupt: 82 | call save_regs 83 | call ih_non_maskable_interrupt 84 | call restore_regs 85 | iretq 86 | 87 | .global isr_breakpoint 88 | isr_breakpoint: 89 | call save_regs 90 | call ih_breakpoint 91 | call restore_regs 92 | iretq 93 | 94 | 95 | .global isr_overflow 96 | isr_overflow: 97 | call save_regs 98 | call ih_overflow 99 | call restore_regs 100 | iretq 101 | 102 | .global isr_bound_range_exceeded 103 | isr_bound_range_exceeded: 104 | call save_regs 105 | call ih_bound_range_exceeded 106 | call restore_regs 107 | iretq 108 | 109 | .global isr_invalid_opcode 110 | isr_invalid_opcode: 111 | call save_regs 112 | call ih_invalid_opcode 113 | call restore_regs 114 | iretq 115 | 116 | .global isr_device_not_available 117 | isr_device_not_available: 118 | call save_regs 119 | call ih_device_not_available 120 | call restore_regs 121 | iretq 122 | 123 | .global isr_invalid_tss 124 | isr_invalid_tss: 125 | call save_regs 126 | pop rdi 127 | call ih_invalid_tss 128 | push rdi 129 | call restore_regs 130 | add rsp, 8 131 | iretq 132 | 133 | .global isr_stack_segment_fault 134 | isr_stack_segment_fault: 135 | call save_regs 136 | pop rdi 137 | call ih_stack_segment_fault 138 | push rdi 139 | call restore_regs 140 | add rsp, 8 141 | iretq 142 | 143 | .global isr_reserved 144 | isr_reserved: 145 | call save_regs 146 | // No error code to pop for this vector, as it's not used 147 | call ih_reserved 148 | call restore_regs 149 | iretq 150 | 151 | .global isr_x87_floating_point 152 | isr_x87_floating_point: 153 | call save_regs 154 | call ih_x87_floating_point 155 | call restore_regs 156 | iretq 157 | 158 | .global isr_alignment_check 159 | isr_alignment_check: 160 | call save_regs 161 | pop rdi 162 | call ih_alignment_check 163 | push rdi 164 | call restore_regs 165 | add rsp, 8 166 | iretq 167 | 168 | .global isr_machine_check 169 | isr_machine_check: 170 | // Registers are not saved since this exception is an abort 171 | // Unlike Double Fault, Machine Check does not push an error code 172 | call ih_machine_check 173 | hlt // Halt the core since machine checks indicate severe hardware issues 174 | 175 | .global isr_simd_floating_point 176 | isr_simd_floating_point: 177 | call save_regs 178 | call ih_simd_floating_point 179 | call restore_regs 180 | iretq 181 | 182 | .global isr_virtualization 183 | isr_virtualization: 184 | call save_regs 185 | call ih_virtualization 186 | call restore_regs 187 | iretq 188 | 189 | .global isr_control_protection 190 | isr_control_protection: 191 | call save_regs 192 | pop rdi 193 | call ih_control_protection 194 | push rdi 195 | call restore_regs 196 | add rsp, 8 197 | iretq 198 | 199 | .global isr_hypervisor_injection 200 | isr_hypervisor_injection: 201 | call save_regs 202 | call ih_hypervisor_injection 203 | call restore_regs 204 | iretq 205 | 206 | .global isr_vmm_communication 207 | isr_vmm_communication: 208 | call save_regs 209 | pop rdi // Pop the error code into RDI for the handler 210 | call ih_vmm_communication 211 | push rdi // Push the error code back onto the stack for correct stack alignment 212 | call restore_regs 213 | add rsp, 8 // Clean up the error code from the stack 214 | iretq 215 | 216 | .global isr_security_exception 217 | isr_security_exception: 218 | call save_regs 219 | pop rdi // Pop the error code into RDI for the handler 220 | call ih_security_exception 221 | push rdi // Push the error code back onto the stack for correct stack alignment 222 | call restore_regs 223 | add rsp, 8 // Clean up the error code from the stack 224 | iretq -------------------------------------------------------------------------------- /charlotte_core/src/arch/x86_64/exceptions/exceptions.rs: -------------------------------------------------------------------------------- 1 | //! X86_64 cpu exception handlers, this code is how it is by design, 2 | //! the calling convention for these is special, and needs to be naked in pure asm 3 | //! as a consequence. 4 | //! 5 | 6 | use core::arch::global_asm; 7 | 8 | #[no_mangle] 9 | static mut BSP_REGS: [u64; 16] = [0; 16]; 10 | 11 | global_asm! { 12 | include_str!("exceptions.asm"), 13 | } 14 | 15 | // Handlers 16 | extern "C" { 17 | fn ih_divide_by_zero(); 18 | fn ih_double_fault(); 19 | fn ih_general_protection_fault(); 20 | fn ih_page_fault(); 21 | fn ih_segment_not_present(); 22 | fn ih_debug(); 23 | fn ih_non_maskable_interrupt(); 24 | fn ih_breakpoint(); 25 | fn ih_overflow(); 26 | fn ih_bound_range_exceeded(); 27 | fn ih_invalid_opcode(); 28 | fn ih_device_not_available(); 29 | fn ih_invalid_tss(); 30 | fn ih_stack_segment_fault(); 31 | fn ih_reserved(); 32 | fn ih_x87_floating_point(); 33 | fn ih_alignment_check(); 34 | fn ih_machine_check(); 35 | fn ih_simd_floating_point(); 36 | fn ih_virtualization(); 37 | fn ih_control_protection(); 38 | fn ih_hypervisor_injection(); 39 | fn ih_vmm_communication(); 40 | fn ih_security_exception(); 41 | } 42 | -------------------------------------------------------------------------------- /charlotte_core/src/arch/x86_64/exceptions/mod.rs: -------------------------------------------------------------------------------- 1 | mod exceptions; 2 | 3 | use core::fmt::Write; 4 | 5 | use ignore_result::Ignore; 6 | 7 | use super::serial::{ComPort::COM1, SerialPort}; 8 | use crate::arch::x86_64::idt::*; 9 | 10 | use crate::arch::*; 11 | 12 | pub fn load_exceptions(idt: &mut Idt) { 13 | idt.set_gate(0, isr_divide_by_zero, 1 << 3, true, true); 14 | idt.set_gate(1, isr_debug, 1 << 3, true, false); 15 | idt.set_gate(2, isr_non_maskable_interrupt, 1 << 3, true, false); 16 | idt.set_gate(3, isr_breakpoint, 1 << 3, true, false); 17 | idt.set_gate(4, isr_overflow, 1 << 3, true, false); 18 | idt.set_gate(5, isr_bound_range_exceeded, 1 << 3, true, false); 19 | idt.set_gate(6, isr_invalid_opcode, 1 << 3, true, false); 20 | idt.set_gate(7, isr_device_not_available, 1 << 3, true, false); 21 | idt.set_gate(8, isr_double_fault, 1 << 3, true, true); 22 | idt.set_gate(10, isr_invalid_tss, 1 << 3, true, false); 23 | idt.set_gate(11, isr_segment_not_present, 1 << 3, true, true); 24 | idt.set_gate(12, isr_stack_segment_fault, 1 << 3, true, false); 25 | idt.set_gate(13, isr_general_protection_fault, 1 << 3, true, true); 26 | idt.set_gate(14, isr_page_fault, 1 << 3, true, true); 27 | idt.set_gate(15, isr_reserved, 1 << 3, true, false); 28 | idt.set_gate(16, isr_x87_floating_point, 1 << 3, true, false); 29 | idt.set_gate(17, isr_alignment_check, 1 << 3, true, false); 30 | idt.set_gate(18, isr_machine_check, 1 << 3, true, false); 31 | idt.set_gate(19, isr_simd_floating_point, 1 << 3, true, false); 32 | idt.set_gate(20, isr_virtualization, 1 << 3, true, false); 33 | idt.set_gate(21, isr_control_protection, 1 << 3, true, false); 34 | idt.set_gate(28, isr_hypervisor_injection, 1 << 3, true, false); 35 | idt.set_gate(29, isr_vmm_communication, 1 << 3, true, false); 36 | idt.set_gate(30, isr_security_exception, 1 << 3, true, false); 37 | } 38 | 39 | extern "C" { 40 | fn isr_divide_by_zero(); 41 | fn isr_debug(); 42 | fn isr_non_maskable_interrupt(); 43 | fn isr_breakpoint(); 44 | fn isr_overflow(); 45 | fn isr_bound_range_exceeded(); 46 | fn isr_invalid_opcode(); 47 | fn isr_device_not_available(); 48 | fn isr_double_fault(); 49 | fn isr_invalid_tss(); 50 | fn isr_stack_segment_fault(); 51 | fn isr_general_protection_fault(); 52 | fn isr_segment_not_present(); 53 | fn isr_page_fault(); 54 | fn isr_reserved(); 55 | fn isr_x87_floating_point(); 56 | fn isr_alignment_check(); 57 | fn isr_machine_check(); 58 | fn isr_simd_floating_point(); 59 | fn isr_virtualization(); 60 | fn isr_control_protection(); 61 | fn isr_hypervisor_injection(); 62 | fn isr_vmm_communication(); 63 | fn isr_security_exception(); 64 | } 65 | 66 | #[no_mangle] 67 | extern "C" fn ih_double_fault(_error_code: u64) { 68 | let mut logger = SerialPort::try_new(COM1).unwrap(); 69 | 70 | writeln!(&mut logger, "A double fault has occurred! Panicking!").ignore(); 71 | ArchApi::panic(); 72 | } 73 | 74 | #[no_mangle] 75 | extern "C" fn ih_divide_by_zero() { 76 | let mut logger = SerialPort::try_new(COM1).unwrap(); 77 | 78 | writeln!( 79 | &mut logger, 80 | "A divide by zero has occurred in kernelspace! Panicking!" 81 | ) 82 | .ignore(); 83 | } 84 | 85 | #[no_mangle] 86 | extern "C" fn ih_general_protection_fault(error_code: u64, rip: u64) { 87 | let mut logger = SerialPort::try_new(COM1).unwrap(); 88 | let rip_adjusted = 0xffffffff80000000 - rip; 89 | if error_code != 0 { 90 | writeln!( 91 | &mut logger, 92 | "A general protection fault has occurred in kernel space with error code {:X}! Panicking! 93 | this is usually the segment selector that caused the fault. RIP = {:X}", 94 | error_code, rip_adjusted 95 | ) 96 | .ignore(); 97 | } else { 98 | writeln!( 99 | &mut logger, 100 | "A general protection fault has occurred in kernelspace! Panicking! 101 | RIP = {:X}", 102 | rip_adjusted, 103 | ) 104 | .ignore(); 105 | } 106 | } 107 | 108 | #[no_mangle] 109 | extern "C" fn ih_page_fault(error_code: u64) { 110 | let mut logger = SerialPort::try_new(COM1).unwrap(); 111 | 112 | writeln!( 113 | &mut logger, 114 | "A page fault has occurred with error code {:32b}", 115 | error_code 116 | ) 117 | .ignore(); 118 | } 119 | 120 | #[no_mangle] 121 | extern "C" fn ih_segment_not_present(error_code: u64) { 122 | let mut logger = SerialPort::try_new(COM1).unwrap(); 123 | 124 | writeln!( 125 | &mut logger, 126 | "Segment Not Present Fault: error code {:x}", 127 | error_code 128 | ) 129 | .ignore(); 130 | } 131 | 132 | #[no_mangle] 133 | extern "C" fn ih_debug() { 134 | let mut logger = SerialPort::try_new(COM1).unwrap(); 135 | 136 | writeln!(&mut logger, "Debug Exception Occurred!").ignore(); 137 | } 138 | 139 | #[no_mangle] 140 | extern "C" fn ih_non_maskable_interrupt() { 141 | let mut logger = SerialPort::try_new(COM1).unwrap(); 142 | 143 | writeln!(&mut logger, "Non-maskable Interrupt Occurred!").ignore(); 144 | } 145 | 146 | #[no_mangle] 147 | extern "C" fn ih_breakpoint() { 148 | let mut logger = SerialPort::try_new(COM1).unwrap(); 149 | 150 | writeln!(&mut logger, "Breakpoint Exception Occurred!").ignore(); 151 | } 152 | 153 | #[no_mangle] 154 | extern "C" fn ih_overflow() { 155 | let mut logger = SerialPort::try_new(COM1).unwrap(); 156 | 157 | writeln!(&mut logger, "Overflow Exception Occurred!").ignore(); 158 | } 159 | 160 | #[no_mangle] 161 | extern "C" fn ih_bound_range_exceeded() { 162 | let mut logger = SerialPort::try_new(COM1).unwrap(); 163 | 164 | writeln!(&mut logger, "Bound Range Exceeded Exception Occurred!").ignore(); 165 | } 166 | 167 | #[no_mangle] 168 | extern "C" fn ih_invalid_opcode() { 169 | let mut logger = SerialPort::try_new(COM1).unwrap(); 170 | 171 | writeln!(&mut logger, "Invalid Opcode Exception Occurred!").ignore(); 172 | } 173 | 174 | #[no_mangle] 175 | extern "C" fn ih_device_not_available() { 176 | let mut logger = SerialPort::try_new(COM1).unwrap(); 177 | 178 | writeln!(&mut logger, "Device Not Available Exception Occurred!").ignore(); 179 | } 180 | 181 | #[no_mangle] 182 | extern "C" fn ih_invalid_tss(error_code: u64) { 183 | let mut logger = SerialPort::try_new(COM1).unwrap(); 184 | 185 | writeln!( 186 | &mut logger, 187 | "Invalid TSS Exception Occurred! Error code: {:x}", 188 | error_code 189 | ) 190 | .ignore(); 191 | } 192 | 193 | #[no_mangle] 194 | extern "C" fn ih_stack_segment_fault(error_code: u64) { 195 | let mut logger = SerialPort::try_new(COM1).unwrap(); 196 | 197 | writeln!( 198 | &mut logger, 199 | "Stack-Segment Fault Occurred! Error code: {:x}", 200 | error_code 201 | ) 202 | .ignore(); 203 | } 204 | 205 | #[no_mangle] 206 | extern "C" fn ih_reserved() { 207 | let mut logger = SerialPort::try_new(COM1).unwrap(); 208 | 209 | writeln!( 210 | &mut logger, 211 | "Unexpected Reserved Vector 15 Exception Occurred!" 212 | ) 213 | .ignore(); 214 | } 215 | 216 | #[no_mangle] 217 | extern "C" fn ih_x87_floating_point() { 218 | let mut logger = SerialPort::try_new(COM1).unwrap(); 219 | 220 | writeln!(&mut logger, "x87 Floating-Point Exception Occurred!").ignore(); 221 | } 222 | 223 | #[no_mangle] 224 | extern "C" fn ih_alignment_check(error_code: u64) { 225 | let mut logger = SerialPort::try_new(COM1).unwrap(); 226 | 227 | writeln!( 228 | &mut logger, 229 | "Alignment Check Exception Occurred! Error code: {:x}", 230 | error_code 231 | ) 232 | .ignore(); 233 | } 234 | 235 | #[no_mangle] 236 | extern "C" fn ih_machine_check() { 237 | let mut logger = SerialPort::try_new(COM1).unwrap(); 238 | 239 | writeln!( 240 | &mut logger, 241 | "Machine Check Exception Occurred! System is halted for safety." 242 | ) 243 | .ignore(); 244 | // After logging, the system might be halted by the assembly handler (ih_machine_check does not return). 245 | } 246 | 247 | #[no_mangle] 248 | extern "C" fn ih_simd_floating_point() { 249 | let mut logger = SerialPort::try_new(COM1).unwrap(); 250 | 251 | writeln!(&mut logger, "SIMD Floating-Point Exception Occurred!").ignore(); 252 | } 253 | 254 | #[no_mangle] 255 | extern "C" fn ih_virtualization() { 256 | let mut logger = SerialPort::try_new(COM1).unwrap(); 257 | 258 | writeln!(&mut logger, "Virtualization Exception Occurred!").ignore(); 259 | } 260 | 261 | #[no_mangle] 262 | extern "C" fn ih_control_protection(error_code: u64) { 263 | let mut logger = SerialPort::try_new(COM1).unwrap(); 264 | 265 | writeln!( 266 | &mut logger, 267 | "Control Protection Exception Occurred! Error code: {:x}", 268 | error_code 269 | ) 270 | .ignore(); 271 | } 272 | 273 | #[no_mangle] 274 | extern "C" fn ih_hypervisor_injection() { 275 | let mut logger = SerialPort::try_new(COM1).unwrap(); 276 | 277 | writeln!(&mut logger, "Hypervisor Injection Exception Occurred!").ignore(); 278 | } 279 | 280 | #[no_mangle] 281 | extern "C" fn ih_vmm_communication(error_code: u64) { 282 | let mut logger = SerialPort::try_new(COM1).unwrap(); 283 | 284 | writeln!( 285 | &mut logger, 286 | "VMM Communication Exception Occurred! Error code: {:x}", 287 | error_code 288 | ) 289 | .ignore(); 290 | } 291 | 292 | #[no_mangle] 293 | extern "C" fn ih_security_exception(error_code: u64) { 294 | let mut logger = SerialPort::try_new(COM1).unwrap(); 295 | 296 | writeln!( 297 | &mut logger, 298 | "Security Exception Occurred! Error code: {:x}", 299 | error_code 300 | ) 301 | .ignore(); 302 | } 303 | -------------------------------------------------------------------------------- /charlotte_core/src/arch/x86_64/gdt/gdt.asm: -------------------------------------------------------------------------------- 1 | .code64 2 | 3 | .data 4 | gdtr: 5 | .2byte 447 // Place 447 = (64 * 7) - 1 in the gdtr label 6 | .8byte 0 7 | 8 | .text 9 | .global asm_load_gdt 10 | asm_load_gdt: 11 | mov [rip + gdtr + 2], rdi 12 | lgdt [rip + gdtr] 13 | ret 14 | 15 | .global asm_reload_segment_regs 16 | asm_reload_segment_regs: 17 | mov rax, 1 //segment descriptor 1 is the kernel code segment 18 | shl rax, 3 19 | push rax 20 | lea rax, [rip + reload_cs] 21 | push rax 22 | retfq 23 | 24 | reload_cs: 25 | mov ax, 2 //segment descriptor 2 is the kernel data segment 26 | shl ax, 3 27 | mov ds, ax 28 | mov es, ax 29 | mov fs, ax 30 | mov gs, ax 31 | mov ss, ax 32 | ret 33 | 34 | .global asm_load_tss 35 | asm_load_tss: 36 | mov ax, 5 37 | shl ax, 3 38 | ltr ax 39 | ret 40 | -------------------------------------------------------------------------------- /charlotte_core/src/arch/x86_64/gdt/gdt.rs: -------------------------------------------------------------------------------- 1 | use core::arch::global_asm; 2 | 3 | global_asm! { 4 | include_str!("gdt.asm") 5 | } 6 | -------------------------------------------------------------------------------- /charlotte_core/src/arch/x86_64/gdt/mod.rs: -------------------------------------------------------------------------------- 1 | mod gdt; 2 | pub mod tss; 3 | 4 | use core::{mem::size_of, ptr}; 5 | 6 | use tss::Tss; 7 | 8 | #[repr(C, packed(1))] 9 | #[derive(Clone, Copy)] 10 | struct SegmentDescriptor { 11 | limit0: u16, 12 | base0: u16, 13 | base1: u8, 14 | access_byte: u8, 15 | limit1_flags: u8, 16 | base2: u8, 17 | } 18 | 19 | impl SegmentDescriptor { 20 | fn new() -> Self { 21 | SegmentDescriptor { 22 | limit0: 0, 23 | base0: 0, 24 | base1: 0, 25 | access_byte: 0, 26 | limit1_flags: 0, 27 | base2: 0, 28 | } 29 | } 30 | } 31 | 32 | #[repr(C, packed(1))] 33 | struct SystemSegmentDescriptor { 34 | lower: SegmentDescriptor, 35 | base3: u32, 36 | reserved: u32, 37 | } 38 | 39 | impl SystemSegmentDescriptor { 40 | fn new() -> Self { 41 | SystemSegmentDescriptor { 42 | lower: SegmentDescriptor::new(), 43 | base3: 0, 44 | reserved: 0, 45 | } 46 | } 47 | } 48 | 49 | #[repr(C, packed(1))] 50 | pub struct Gdt { 51 | segment_descs: [SegmentDescriptor; 5], 52 | tss_desc: SystemSegmentDescriptor, 53 | } 54 | 55 | impl Gdt { 56 | pub fn new(tss: &Tss) -> Self { 57 | let mut gdt = Gdt { 58 | segment_descs: [SegmentDescriptor::new(); 5], 59 | tss_desc: SystemSegmentDescriptor::new(), 60 | }; 61 | 62 | //Null descriptor 63 | gdt.set_segment_desc(0, 0, 0, 0, 0); 64 | //Kernel Mode Code Segment 65 | gdt.set_segment_desc(1, 0, 0xFFFFF, 0x9A, 0xA); 66 | //Kernel Mode Data Segment 67 | gdt.set_segment_desc(2, 0, 0xFFFFF, 0x92, 0xC); 68 | //User Mode Code Segment 69 | gdt.set_segment_desc(3, 0, 0xFFFFF, 0xFA, 0xA); 70 | //User Mode Data Segment 71 | gdt.set_segment_desc(4, 0, 0xFFFFF, 0xF2, 0xC); 72 | //Task State Segment 73 | gdt.set_tss_desc(ptr::addr_of!(*tss) as u64, size_of::() as u32); 74 | 75 | gdt 76 | } 77 | fn set_tss_desc(&mut self, base: u64, limit: u32) { 78 | self.set_segment_desc(5, (base & 0xFFFFFFFF) as u32, limit, 0x89, 0x0); 79 | self.tss_desc.base3 = ((base & 0xFFFFFFFF00000000) >> 32) as u32; 80 | self.tss_desc.reserved = 0; 81 | } 82 | fn set_segment_desc( 83 | &mut self, 84 | index: usize, 85 | base: u32, 86 | limit: u32, 87 | access_byte: u8, 88 | flags: u8, 89 | ) { 90 | let dest_sd = if index == 5 { 91 | &mut (self.tss_desc.lower) 92 | } else { 93 | &mut (self.segment_descs[index]) 94 | }; 95 | 96 | dest_sd.limit0 = (limit & 0xFFFF) as u16; 97 | dest_sd.limit1_flags = ((limit & 0xFF0000) >> 16) as u8; // Only the lower 4 bits of this field encodes limit bits 98 | 99 | dest_sd.base0 = (base & 0xFFFF) as u16; 100 | dest_sd.base1 = ((base & 0xFF0000) >> 16) as u8; 101 | dest_sd.base2 = ((base & 0xFF000000) >> 24) as u8; 102 | 103 | dest_sd.access_byte = access_byte; 104 | 105 | dest_sd.limit1_flags |= (flags & 0xFF) << 4; // The upper 4 bits of this field encodes flags 106 | } 107 | pub fn load(&self) { 108 | unsafe { 109 | asm_load_gdt(self); 110 | } 111 | } 112 | pub fn reload_segment_regs() { 113 | unsafe { 114 | asm_reload_segment_regs(); 115 | } 116 | } 117 | pub fn load_tss() { 118 | unsafe { 119 | asm_load_tss(); 120 | } 121 | } 122 | } 123 | 124 | extern "C" { 125 | fn asm_load_gdt(gdt: &Gdt); 126 | fn asm_reload_segment_regs(); 127 | fn asm_load_tss(); 128 | } 129 | -------------------------------------------------------------------------------- /charlotte_core/src/arch/x86_64/gdt/tss.rs: -------------------------------------------------------------------------------- 1 | use core::mem::size_of; 2 | 3 | #[repr(C, packed(1))] 4 | pub struct Tss { 5 | res: u32, 6 | rsp0: u64, 7 | unused: [u8; 90], 8 | iopb: u16, 9 | } 10 | 11 | impl Tss { 12 | pub fn new(rsp0: u64) -> Self { 13 | Tss { 14 | res: 0, 15 | rsp0, 16 | unused: [0u8; 90], 17 | iopb: size_of::() as u16, 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /charlotte_core/src/arch/x86_64/global.asm: -------------------------------------------------------------------------------- 1 | .code64 2 | 3 | .text 4 | .global save_regs 5 | save_regs: 6 | push r15 7 | push r14 8 | push r13 9 | push r12 10 | push r11 11 | push r10 12 | push r9 13 | push r8 14 | push rbp 15 | push rsp 16 | push rdi 17 | push rsi 18 | push rdx 19 | push rcx 20 | push rbx 21 | push rax 22 | ret 23 | 24 | .global restore_regs 25 | restore_regs: 26 | pop rax 27 | pop rbx 28 | pop rcx 29 | pop rdx 30 | pop rsi 31 | pop rdi 32 | pop rsp 33 | pop rbp 34 | pop r8 35 | pop r9 36 | pop r10 37 | pop r11 38 | pop r12 39 | pop r13 40 | pop r14 41 | pop r15 42 | ret 43 | -------------------------------------------------------------------------------- /charlotte_core/src/arch/x86_64/global.rs: -------------------------------------------------------------------------------- 1 | //! General asm utilities for x86_64 2 | //! The functions defined here are NOT supposed to be inlined and must be kept in the global_asm! block 3 | //! 4 | 5 | use core::arch::global_asm; 6 | 7 | global_asm! { 8 | include_str!("global.asm") 9 | } 10 | -------------------------------------------------------------------------------- /charlotte_core/src/arch/x86_64/idt/mod.rs: -------------------------------------------------------------------------------- 1 | use core::arch::asm; 2 | use core::ptr::addr_of; 3 | 4 | #[derive(Debug)] 5 | #[repr(C, align(16))] 6 | pub struct Idt { 7 | pub gates: [InterruptGate; 256], 8 | } 9 | 10 | impl Idt { 11 | pub const fn new() -> Self { 12 | Idt { 13 | gates: [InterruptGate::new(); 256], 14 | } 15 | } 16 | pub fn set_gate( 17 | &mut self, 18 | index: usize, 19 | isr_ptr: unsafe extern "C" fn(), 20 | segment_selector: u16, 21 | is_trap: bool, 22 | is_present: bool, 23 | ) { 24 | let gate = &mut self.gates[index]; 25 | let isr_addr = isr_ptr as u64; 26 | 27 | gate.addr0 = u16::try_from(isr_addr & 0xFFFF).unwrap(); 28 | gate.segment_selector = segment_selector; 29 | gate.reserved_ist_index = 0u8; // the IST is not used 30 | gate.flags = if is_trap { 0b1111u8 } else { 0b1110u8 }; //gate type 31 | //reserved bit 32 | gate.flags &= !(0b1u8 << 4); 33 | //privilege ring required to use gate 34 | gate.flags &= !(0b11u8 << 5); 35 | //present bit 36 | if is_present { 37 | gate.flags |= 0b1u8 << 7; 38 | } else { 39 | gate.flags &= !(0b1u8 << 7); 40 | } 41 | gate.addr1 = ((isr_addr & (0xFFFF << 16)) >> 16) as u16; 42 | gate.addr2 = ((isr_addr & (0xFFFFFFFF << 32)) >> 32) as u32; 43 | gate.reserved = 0u32; 44 | } 45 | #[allow(unused)] 46 | pub fn set_present(&mut self, index: usize) { 47 | if index < 256 { 48 | self.gates[index].flags |= 0b1u8 << 7; 49 | } 50 | } 51 | #[allow(unused)] 52 | pub fn clear_present(&mut self, index: usize) { 53 | if index < 256 { 54 | self.gates[index].flags &= !(0b1u8 << 7); 55 | } 56 | } 57 | pub fn load(&self) { 58 | let idtr = Idtr::new(128u16 * 256u16 - 1u16, addr_of!(*self) as u64); 59 | unsafe { 60 | asm_load_idt(&idtr); 61 | } 62 | } 63 | } 64 | #[derive(Clone, Copy, Debug)] 65 | #[repr(C, packed(1))] 66 | pub struct InterruptGate { 67 | addr0: u16, 68 | segment_selector: u16, 69 | reserved_ist_index: u8, 70 | flags: u8, 71 | addr1: u16, 72 | addr2: u32, 73 | reserved: u32, 74 | } 75 | 76 | impl InterruptGate { 77 | const fn new() -> Self { 78 | InterruptGate { 79 | addr0: 0u16, 80 | segment_selector: 0u16, 81 | reserved_ist_index: 0u8, 82 | flags: 0u8, 83 | addr1: 0u16, 84 | addr2: 0u32, 85 | reserved: 0u32, 86 | } 87 | } 88 | } 89 | 90 | #[repr(C, packed)] 91 | struct Idtr { 92 | size: u16, 93 | base: u64, 94 | } 95 | 96 | impl Idtr { 97 | fn new(size: u16, base: u64) -> Self { 98 | Idtr { size, base } 99 | } 100 | } 101 | 102 | unsafe fn asm_load_idt(idtr: &Idtr) { 103 | asm!("\ 104 | lidt [{}] 105 | ", in(reg) idtr); 106 | } 107 | -------------------------------------------------------------------------------- /charlotte_core/src/arch/x86_64/interrupts/apic.csv: -------------------------------------------------------------------------------- 1 | Offset,Register name,Read/Write permissions 2 | 000h - 010h,Reserved, 3 | 020h,LAPIC ID Register,Read/Write 4 | 030h,LAPIC Version Register,Read only 5 | 040h - 070h,Reserved, 6 | 080h,Task Priority Register (TPR),Read/Write 7 | 090h,Arbitration Priority Register (APR),Read only 8 | 0A0h,Processor Priority Register (PPR),Read only 9 | 0B0h,EOI register,Write only 10 | 0C0h,Remote Read Register (RRD),Read only 11 | 0D0h,Logical Destination Register,Read/Write 12 | 0E0h,Destination Format Register,Read/Write 13 | 0F0h,Spurious Interrupt Vector Register,Read/Write 14 | 100h - 170h,In-Service Register (ISR),Read only 15 | 180h - 1F0h,Trigger Mode Register (TMR),Read only 16 | 200h - 270h,Interrupt Request Register (IRR),Read only 17 | 280h,Error Status Register,Read only 18 | 290h - 2E0h,Reserved, 19 | 2F0h,LVT Corrected Machine Check Interrupt (CMCI) Register,Read/Write 20 | 300h - 310h,Interrupt Command Register (ICR),Read/Write 21 | 320h,LVT Timer Register,Read/Write 22 | 330h,LVT Thermal Sensor Register,Read/Write 23 | 340h,LVT Performance Monitoring Counters Register,Read/Write 24 | 350h,LVT LINT0 Register,Read/Write 25 | 360h,LVT LINT1 Register,Read/Write 26 | 370h,LVT Error Register,Read/Write 27 | 380h,Initial Count Register (for Timer),Read/Write 28 | 390h,Current Count Register (for Timer),Read only 29 | 3A0h - 3D0h,Reserved, 30 | 3E0h,Divide Configuration Register (for Timer),Read/Write 31 | 3F0h,Reserved 32 | -------------------------------------------------------------------------------- /charlotte_core/src/arch/x86_64/interrupts/apic.rs: -------------------------------------------------------------------------------- 1 | use core::arch::x86_64::{__cpuid, __rdtscp, _mm_lfence, _mm_pause, _rdtsc}; 2 | use core::ptr; 3 | use core::time::Duration; 4 | 5 | use crate::acpi::madt::{Madt, MadtEntry}; 6 | use crate::arch::x86_64::cpu::{irq_disable, irq_restore, read_msr, write_msr}; 7 | use crate::arch::x86_64::idt::Idt; 8 | use crate::arch::x86_64::interrupts::apic_consts::{ 9 | APIC_DISABLE, APIC_NMI, APIC_SW_ENABLE, DESTINATION_FORMAT, EOI_REGISTER, LAPIC_VERSION, 10 | LOGICAL_DESTINATION, LVT_LINT0, LVT_LINT1, LVT_PERFORMANCE_MONITORING_COUNTERS, LVT_TIMER, 11 | SPURIOUS_INTERRUPT_VECTOR, TASK_PRIORITY_TPR, TIMER_CURRENT, TIMER_DIVISOR, TIMER_INIT_COUNT, 12 | }; 13 | use crate::arch::x86_64::interrupts::isa_handler::load_handlers; 14 | use crate::arch::HwTimerMode; 15 | 16 | const FEAT_EDX_APIC: u32 = 1 << 9; 17 | const APIC_MSR: u32 = 0x1B; 18 | 19 | #[no_mangle] 20 | static mut LAPIC_REMAPPED_LOCATION: u64 = 0xFEE00000; 21 | 22 | #[no_mangle] 23 | pub extern "C" fn apic_offset() -> u64 { 24 | unsafe { LAPIC_REMAPPED_LOCATION } 25 | } 26 | 27 | #[repr(u32)] 28 | #[derive(Debug, Copy, Clone)] 29 | /// Masks bits 17 & 18 as per fig 11-8 from the intel SDM Vol 3 11.5 30 | pub enum TimerMode { 31 | // makes the bits for mode 00, so is a no op 32 | Oneshot = 0x00, 33 | Periodic = 0x01 << 17, 34 | TscDeadline = 0x02 << 17, 35 | } 36 | 37 | impl From for TimerMode { 38 | fn from(mode: HwTimerMode) -> Self { 39 | match mode { 40 | HwTimerMode::OneShot => TimerMode::Oneshot, 41 | HwTimerMode::Recurrent => TimerMode::Periodic, 42 | } 43 | } 44 | } 45 | 46 | #[repr(u32)] 47 | #[derive(Debug, Copy, Clone)] 48 | /// Consts for the timer divisor, 49 | /// the divisor takes the form of bit indexes `[3, 1, 0]` with index 2 being reserved 50 | /// ## References: 51 | /// * AMD SDM 16.4.1 52 | /// * Intel SDM Vol3 11.5.4 53 | pub enum TimerDivisor { 54 | Div1 = 0b1011, 55 | Div2 = 0b0000, 56 | Div4 = 0b0001, 57 | Div8 = 0b0010, 58 | Div16 = 0b0011, 59 | Div32 = 0b1000, 60 | Div64 = 0b1001, 61 | Div128 = 0b1010, 62 | } 63 | 64 | impl From for TimerDivisor { 65 | fn from(value: u8) -> Self { 66 | match value { 67 | 1 => TimerDivisor::Div1, 68 | 2 => TimerDivisor::Div2, 69 | 4 => TimerDivisor::Div4, 70 | 8 => TimerDivisor::Div8, 71 | 16 => TimerDivisor::Div16, 72 | 32 => TimerDivisor::Div32, 73 | 64 => TimerDivisor::Div64, 74 | 128 => TimerDivisor::Div128, 75 | _ => panic!("Invalid divisor value"), 76 | } 77 | } 78 | } 79 | 80 | pub struct Apic { 81 | base_phys_addr: usize, 82 | base_mapped_addr: Option, 83 | timer_mode: TimerMode, 84 | timer_count: u32, 85 | timer_divisor: TimerDivisor, 86 | pub tps: u64, 87 | pub lvt_max: u8, 88 | } 89 | 90 | impl Apic { 91 | pub fn new(madt: &Madt) -> Self { 92 | let addr = Self::get_apic_addr(madt); 93 | 94 | Apic { 95 | base_phys_addr: addr, 96 | base_mapped_addr: None, 97 | timer_mode: TimerMode::Oneshot, 98 | timer_divisor: TimerDivisor::Div1, 99 | timer_count: 0, 100 | tps: 0, 101 | lvt_max: 0, 102 | } 103 | } 104 | 105 | fn get_addr(&self) -> usize { 106 | self.base_mapped_addr.unwrap_or(self.base_phys_addr) 107 | } 108 | 109 | #[allow(unused)] 110 | pub fn get_apic_addr(madt: &Madt) -> usize { 111 | let mut addr = madt.local_apic_addr() as usize; 112 | let mut itr = madt.iter(); 113 | for entry in itr { 114 | if let MadtEntry::LocalApicAddressOverride(addr_o) = entry { 115 | addr = addr_o.local_apic_address as usize; 116 | } 117 | } 118 | 119 | addr 120 | } 121 | 122 | pub fn write_apic_reg(&self, offset: u32, value: u32) { 123 | let addr = (self.get_addr() + offset as usize) as *mut u32; 124 | unsafe { ptr::write_volatile(addr, value) } 125 | } 126 | 127 | pub fn read_apic_reg(&self, offset: u32) -> u32 { 128 | let addr = (self.get_addr() + offset as usize) as *const u32; 129 | unsafe { ptr::read_volatile(addr) } 130 | } 131 | 132 | pub fn init(&mut self) { 133 | irq_disable(); 134 | // If the apic is not present according to cpuid 135 | if !Apic::is_present() { 136 | panic!("APIC is not present, and is required!") 137 | } 138 | 139 | let ver_reg = self.read_apic_reg(LAPIC_VERSION); 140 | let max_lvt = (0xffu32 << 16) & ver_reg; 141 | // this is a valid downcast, by moving the max_lvt 16 bits to the right, we get the max lvt 142 | // in the lower 8 bits of the u32 and can then transmute it to an u8. 143 | self.lvt_max = ((max_lvt >> 16) + 1) as u8; 144 | 145 | // initialize the APIC to known state 146 | let base = self.get_addr(); 147 | if base != 0xFEE00000 { 148 | panic!("APIC base address is not 0xFEE00000, it is {:#X}", base); 149 | } 150 | // reset the apic to make sure it's in a known state 151 | Self::enable_apic(false); 152 | Self::enable_apic(true); 153 | 154 | self.write_apic_reg(DESTINATION_FORMAT, 0x0FFFFFFFF); 155 | let ldf = self.read_apic_reg(LOGICAL_DESTINATION) & 0x00FFFFFF; 156 | self.write_apic_reg(LOGICAL_DESTINATION, ldf); 157 | self.write_apic_reg(SPURIOUS_INTERRUPT_VECTOR, 0x27 + APIC_SW_ENABLE); 158 | self.write_apic_reg(LVT_TIMER, APIC_DISABLE); 159 | self.write_apic_reg(LVT_PERFORMANCE_MONITORING_COUNTERS, APIC_NMI); 160 | self.write_apic_reg(LVT_LINT0, APIC_DISABLE); 161 | self.write_apic_reg(LVT_LINT1, APIC_DISABLE); 162 | self.write_apic_reg(TASK_PRIORITY_TPR, 15); 163 | 164 | self.tps = self.calculate_ticks_per_second(); 165 | irq_restore(); 166 | } 167 | 168 | pub fn enable(&mut self, idt: &mut Idt) { 169 | load_handlers(idt); 170 | self.init(); 171 | } 172 | 173 | fn calculate_ticks_per_second(&self) -> u64 { 174 | let duration = Duration::from_millis(100); 175 | let ticks = Self::measure_tsc_duration(duration); 176 | Self::calculate_bus_speed(ticks, duration) 177 | } 178 | 179 | fn set_timer_counter(&self, frequency: u32) { 180 | self.write_apic_reg(TIMER_INIT_COUNT, frequency); 181 | } 182 | 183 | fn set_timer_divisor(&self, divisor: TimerDivisor) { 184 | self.write_apic_reg(TIMER_DIVISOR, divisor as u32); 185 | } 186 | 187 | pub fn setup_timer(&mut self, mode: TimerMode, frequency: u32, divisor: TimerDivisor) { 188 | self.timer_divisor = divisor; 189 | self.timer_mode = mode; 190 | self.timer_count = frequency; 191 | } 192 | 193 | fn set_lvt_timer_register(&self, mode: TimerMode, enabled: bool, vector: u8) { 194 | let mut value = vector as u32; 195 | value |= mode as u32; 196 | // if masking is desired by enabled being false 197 | // then or 1 << 16 with the mask 198 | if !enabled { 199 | value |= 1 << 16; 200 | } 201 | self.write_apic_reg(LVT_TIMER, value); 202 | } 203 | 204 | pub fn start_timer(&self) { 205 | self.set_timer_divisor(self.timer_divisor); 206 | self.set_timer_counter(self.timer_count); 207 | self.set_lvt_timer_register(self.timer_mode, true, 32); 208 | } 209 | 210 | pub fn write_eoi(&self) { 211 | self.write_apic_reg(EOI_REGISTER, 1); 212 | } 213 | 214 | pub fn get_timer_current_count(&self) -> u32 { 215 | self.read_apic_reg(TIMER_CURRENT) 216 | } 217 | } 218 | 219 | // static methods 220 | impl Apic { 221 | pub fn signal_eoi() { 222 | let base = unsafe { LAPIC_REMAPPED_LOCATION }; 223 | let addr = (base + EOI_REGISTER as u64) as *mut u32; 224 | unsafe { ptr::write_volatile(addr, 0) } 225 | } 226 | 227 | fn measure_tsc_duration(duration: Duration) -> u64 { 228 | unsafe { 229 | let sec = Duration::from_secs(1); 230 | 231 | _mm_lfence(); // Serialize 232 | let start_tsc = __rdtscp(&mut 0); 233 | _mm_lfence(); // Serialize 234 | 235 | let start_time = _rdtsc(); 236 | 237 | // Busy-wait loop for the specified duration 238 | let end_time = start_time + duration.as_nanos() as u64; 239 | while _rdtsc() < end_time { 240 | _mm_pause(); 241 | } 242 | 243 | _mm_lfence(); // Serialize 244 | let end_tsc = __rdtscp(&mut 0); 245 | _mm_lfence(); // Serialize 246 | (end_tsc - start_tsc) * sec.as_millis() as u64 247 | } 248 | } 249 | 250 | fn calculate_bus_speed(ticks: u64, duration: Duration) -> u64 { 251 | ticks / duration.as_millis() as u64 252 | } 253 | 254 | pub fn enable_apic(enable: bool) { 255 | let mut msr = read_msr(APIC_MSR); 256 | 257 | if enable { 258 | msr.eax |= 1 << 11; 259 | } else { 260 | msr.eax ^= 1 << 11; 261 | } 262 | write_msr(APIC_MSR, msr); 263 | } 264 | 265 | pub fn is_apic_enabled() -> bool { 266 | let msr = read_msr(APIC_MSR); 267 | 268 | (msr.eax & 1 << 11) != 0 269 | } 270 | 271 | pub fn is_present() -> bool { 272 | let cpuid = unsafe { __cpuid(1) }; 273 | (cpuid.edx & FEAT_EDX_APIC) == FEAT_EDX_APIC 274 | } 275 | } 276 | -------------------------------------------------------------------------------- /charlotte_core/src/arch/x86_64/interrupts/apic_consts.rs: -------------------------------------------------------------------------------- 1 | // src/arch/x86_64/interrupts/apic_consts.rs 2 | 3 | /// Read/Write 4 | pub const LAPIC_ID: u32 = 0x020; 5 | 6 | /// Read only 7 | pub const LAPIC_VERSION: u32 = 0x030; 8 | 9 | /// Read/Write 10 | pub const TASK_PRIORITY_TPR: u32 = 0x080; 11 | 12 | /// Read only 13 | pub const ARBITRATION_PRIORITY_APR: u32 = 0x090; 14 | 15 | /// Read only 16 | pub const PROCESSOR_PRIORITY_PPR: u32 = 0x0A0; 17 | 18 | /// Write only 19 | pub const EOI_REGISTER: u32 = 0x0B0; 20 | 21 | /// Read only 22 | pub const REMOTE_READ_RRD: u32 = 0x0C0; 23 | 24 | /// Read/Write 25 | pub const LOGICAL_DESTINATION: u32 = 0x0D0; 26 | 27 | /// Read/Write 28 | pub const DESTINATION_FORMAT: u32 = 0x0E0; 29 | 30 | /// Read/Write 31 | pub const SPURIOUS_INTERRUPT_VECTOR: u32 = 0x0F0; 32 | 33 | /// Read only 34 | pub const IN_SERVICE_ISR: u32 = 0x100; 35 | 36 | /// Read only 37 | pub const TRIGGER_MODE_TMR: u32 = 0x180; 38 | 39 | /// Read only 40 | pub const INTERRUPT_REQUEST_IRR: u32 = 0x200; 41 | 42 | /// Read only 43 | pub const ERROR_STATUS: u32 = 0x280; 44 | 45 | /// Read/Write 46 | pub const LVT_CORRECTED_MACHINE_CHECK_INTERRUPT_CMCI: u32 = 0x2F0; 47 | 48 | /// Read/Write 49 | pub const INTERRUPT_COMMAND_ICR: u32 = 0x300; 50 | 51 | /// Read/Write 52 | pub const LVT_TIMER: u32 = 0x320; 53 | 54 | /// Read/Write 55 | pub const LVT_THERMAL_SENSOR: u32 = 0x330; 56 | 57 | /// Read/Write 58 | pub const LVT_PERFORMANCE_MONITORING_COUNTERS: u32 = 0x340; 59 | 60 | /// Read/Write 61 | pub const LVT_LINT0: u32 = 0x350; 62 | 63 | /// Read/Write 64 | pub const LVT_LINT1: u32 = 0x360; 65 | 66 | /// Read/Write 67 | pub const LVT_ERROR: u32 = 0x370; 68 | 69 | /// Read/Write 70 | pub const TIMER_INIT_COUNT: u32 = 0x380; 71 | 72 | /// Read only 73 | pub const TIMER_CURRENT: u32 = 0x390; 74 | 75 | /// Read/Write 76 | pub const TIMER_DIVISOR: u32 = 0x3E0; 77 | 78 | pub const APIC_DISABLE: u32 = 0x10000; 79 | 80 | pub const APIC_NMI: u32 = 0x400; 81 | 82 | pub const APIC_SW_ENABLE: u32 = 0x100; 83 | -------------------------------------------------------------------------------- /charlotte_core/src/arch/x86_64/interrupts/isa_handler.asm: -------------------------------------------------------------------------------- 1 | // This is the code to handle interrupts, put the data into a struct in memory and call the rust handler 2 | .code64 3 | 4 | .text 5 | 6 | .global isr_dummy 7 | isr_dummy: // Dummyinterrupt to map to spurious interrupt 8 | iretq 9 | -------------------------------------------------------------------------------- /charlotte_core/src/arch/x86_64/interrupts/isa_handler.rs: -------------------------------------------------------------------------------- 1 | use core::arch::global_asm; 2 | 3 | use core::fmt::Write; 4 | use ignore_result::Ignore; 5 | 6 | use crate::arch::x86_64::cpu::{asm_are_interrupts_enabled, irq_disable, irq_restore}; 7 | use crate::arch::x86_64::idt::Idt; 8 | use crate::arch::x86_64::interrupts::apic::Apic; 9 | use crate::arch::x86_64::interrupts::vectors::IV_HANDLERS; 10 | use crate::arch::x86_64::serial::ComPort::COM1; 11 | use crate::arch::x86_64::serial::SerialPort; 12 | use crate::logln; 13 | 14 | fn default_handler(vector: u64) { 15 | let mut logger = SerialPort::try_new(COM1).unwrap(); 16 | 17 | writeln!( 18 | &mut logger, 19 | "vector {} called, but no handler is registered in the kernel for it", 20 | vector 21 | ) 22 | .ignore(); 23 | 24 | Apic::signal_eoi(); 25 | } 26 | 27 | static mut IV_HANDLER_FNS: [fn(vector: u64); 224] = [default_handler; 224]; 28 | 29 | extern "C" { 30 | pub(crate) fn isr_dummy(); 31 | } 32 | 33 | #[derive(Debug, Clone, Copy)] 34 | #[repr(u8)] 35 | pub enum IntIdx { 36 | Timer = 0x20, 37 | } 38 | 39 | pub fn load_handlers(idt: &mut Idt) { 40 | if asm_are_interrupts_enabled() { 41 | irq_disable(); 42 | } 43 | for (gate, h) in IV_HANDLERS.iter().enumerate() { 44 | let i = gate + 32; 45 | if i == 39 { 46 | idt.set_gate(i, isr_dummy, 1 << 3, false, true); 47 | continue; 48 | } 49 | idt.set_gate(i, *h, 1 << 3, false, true); 50 | } 51 | idt.load(); 52 | if !asm_are_interrupts_enabled() { 53 | irq_restore(); 54 | } 55 | } 56 | 57 | #[no_mangle] 58 | pub extern "C" fn isr_handler(vector: u64) { 59 | let called_by = vector - 32; 60 | unsafe { 61 | let h = IV_HANDLER_FNS[called_by as usize]; 62 | h(vector); 63 | } 64 | } 65 | 66 | pub fn register_iv_handler(h: fn(vector: u64), vector: u8) { 67 | if asm_are_interrupts_enabled() { 68 | irq_disable(); 69 | } 70 | if vector < 32 { 71 | panic!("Cannot set vector handler lower than 32"); 72 | } 73 | unsafe { 74 | let idx = (vector - 32) as usize; 75 | IV_HANDLER_FNS[idx] = h; 76 | logln!("setup handler for {}", idx); 77 | } 78 | if !asm_are_interrupts_enabled() { 79 | irq_restore(); 80 | } 81 | } 82 | 83 | global_asm! { 84 | include_str!("isa_handler.asm"), 85 | } 86 | -------------------------------------------------------------------------------- /charlotte_core/src/arch/x86_64/interrupts/mod.rs: -------------------------------------------------------------------------------- 1 | //! This module helps detect and initialize the hardware timers present in the LP 2 | //! 3 | 4 | pub mod apic; 5 | pub mod apic_consts; 6 | pub mod isa_handler; 7 | mod vectors; 8 | -------------------------------------------------------------------------------- /charlotte_core/src/arch/x86_64/interrupts/vectors.rs: -------------------------------------------------------------------------------- 1 | use core::arch::global_asm; 2 | 3 | // handlers 4 | extern "C" { 5 | fn iv_32(); 6 | fn iv_33(); 7 | fn iv_34(); 8 | fn iv_35(); 9 | fn iv_36(); 10 | fn iv_37(); 11 | fn iv_38(); 12 | fn iv_39(); 13 | fn iv_40(); 14 | fn iv_41(); 15 | fn iv_42(); 16 | fn iv_43(); 17 | fn iv_44(); 18 | fn iv_45(); 19 | fn iv_46(); 20 | fn iv_47(); 21 | fn iv_48(); 22 | fn iv_49(); 23 | fn iv_50(); 24 | fn iv_51(); 25 | fn iv_52(); 26 | fn iv_53(); 27 | fn iv_54(); 28 | fn iv_55(); 29 | fn iv_56(); 30 | fn iv_57(); 31 | fn iv_58(); 32 | fn iv_59(); 33 | fn iv_60(); 34 | fn iv_61(); 35 | fn iv_62(); 36 | fn iv_63(); 37 | fn iv_64(); 38 | fn iv_65(); 39 | fn iv_66(); 40 | fn iv_67(); 41 | fn iv_68(); 42 | fn iv_69(); 43 | fn iv_70(); 44 | fn iv_71(); 45 | fn iv_72(); 46 | fn iv_73(); 47 | fn iv_74(); 48 | fn iv_75(); 49 | fn iv_76(); 50 | fn iv_77(); 51 | fn iv_78(); 52 | fn iv_79(); 53 | fn iv_80(); 54 | fn iv_81(); 55 | fn iv_82(); 56 | fn iv_83(); 57 | fn iv_84(); 58 | fn iv_85(); 59 | fn iv_86(); 60 | fn iv_87(); 61 | fn iv_88(); 62 | fn iv_89(); 63 | fn iv_90(); 64 | fn iv_91(); 65 | fn iv_92(); 66 | fn iv_93(); 67 | fn iv_94(); 68 | fn iv_95(); 69 | fn iv_96(); 70 | fn iv_97(); 71 | fn iv_98(); 72 | fn iv_99(); 73 | fn iv_100(); 74 | fn iv_101(); 75 | fn iv_102(); 76 | fn iv_103(); 77 | fn iv_104(); 78 | fn iv_105(); 79 | fn iv_106(); 80 | fn iv_107(); 81 | fn iv_108(); 82 | fn iv_109(); 83 | fn iv_110(); 84 | fn iv_111(); 85 | fn iv_112(); 86 | fn iv_113(); 87 | fn iv_114(); 88 | fn iv_115(); 89 | fn iv_116(); 90 | fn iv_117(); 91 | fn iv_118(); 92 | fn iv_119(); 93 | fn iv_120(); 94 | fn iv_121(); 95 | fn iv_122(); 96 | fn iv_123(); 97 | fn iv_124(); 98 | fn iv_125(); 99 | fn iv_126(); 100 | fn iv_127(); 101 | fn iv_128(); 102 | fn iv_129(); 103 | fn iv_130(); 104 | fn iv_131(); 105 | fn iv_132(); 106 | fn iv_133(); 107 | fn iv_134(); 108 | fn iv_135(); 109 | fn iv_136(); 110 | fn iv_137(); 111 | fn iv_138(); 112 | fn iv_139(); 113 | fn iv_140(); 114 | fn iv_141(); 115 | fn iv_142(); 116 | fn iv_143(); 117 | fn iv_144(); 118 | fn iv_145(); 119 | fn iv_146(); 120 | fn iv_147(); 121 | fn iv_148(); 122 | fn iv_149(); 123 | fn iv_150(); 124 | fn iv_151(); 125 | fn iv_152(); 126 | fn iv_153(); 127 | fn iv_154(); 128 | fn iv_155(); 129 | fn iv_156(); 130 | fn iv_157(); 131 | fn iv_158(); 132 | fn iv_159(); 133 | fn iv_160(); 134 | fn iv_161(); 135 | fn iv_162(); 136 | fn iv_163(); 137 | fn iv_164(); 138 | fn iv_165(); 139 | fn iv_166(); 140 | fn iv_167(); 141 | fn iv_168(); 142 | fn iv_169(); 143 | fn iv_170(); 144 | fn iv_171(); 145 | fn iv_172(); 146 | fn iv_173(); 147 | fn iv_174(); 148 | fn iv_175(); 149 | fn iv_176(); 150 | fn iv_177(); 151 | fn iv_178(); 152 | fn iv_179(); 153 | fn iv_180(); 154 | fn iv_181(); 155 | fn iv_182(); 156 | fn iv_183(); 157 | fn iv_184(); 158 | fn iv_185(); 159 | fn iv_186(); 160 | fn iv_187(); 161 | fn iv_188(); 162 | fn iv_189(); 163 | fn iv_190(); 164 | fn iv_191(); 165 | fn iv_192(); 166 | fn iv_193(); 167 | fn iv_194(); 168 | fn iv_195(); 169 | fn iv_196(); 170 | fn iv_197(); 171 | fn iv_198(); 172 | fn iv_199(); 173 | fn iv_200(); 174 | fn iv_201(); 175 | fn iv_202(); 176 | fn iv_203(); 177 | fn iv_204(); 178 | fn iv_205(); 179 | fn iv_206(); 180 | fn iv_207(); 181 | fn iv_208(); 182 | fn iv_209(); 183 | fn iv_210(); 184 | fn iv_211(); 185 | fn iv_212(); 186 | fn iv_213(); 187 | fn iv_214(); 188 | fn iv_215(); 189 | fn iv_216(); 190 | fn iv_217(); 191 | fn iv_218(); 192 | fn iv_219(); 193 | fn iv_220(); 194 | fn iv_221(); 195 | fn iv_222(); 196 | fn iv_223(); 197 | fn iv_224(); 198 | fn iv_225(); 199 | fn iv_226(); 200 | fn iv_227(); 201 | fn iv_228(); 202 | fn iv_229(); 203 | fn iv_230(); 204 | fn iv_231(); 205 | fn iv_232(); 206 | fn iv_233(); 207 | fn iv_234(); 208 | fn iv_235(); 209 | fn iv_236(); 210 | fn iv_237(); 211 | fn iv_238(); 212 | fn iv_239(); 213 | fn iv_240(); 214 | fn iv_241(); 215 | fn iv_242(); 216 | fn iv_243(); 217 | fn iv_244(); 218 | fn iv_245(); 219 | fn iv_246(); 220 | fn iv_247(); 221 | fn iv_248(); 222 | fn iv_249(); 223 | fn iv_250(); 224 | fn iv_251(); 225 | fn iv_252(); 226 | fn iv_253(); 227 | fn iv_254(); 228 | fn iv_255(); 229 | 230 | } 231 | // end of handlers 232 | pub const IV_HANDLERS: [unsafe extern "C" fn(); 224] = [ 233 | iv_32, iv_33, iv_34, iv_35, iv_36, iv_37, iv_38, iv_39, iv_40, iv_41, iv_42, iv_43, iv_44, 234 | iv_45, iv_46, iv_47, iv_48, iv_49, iv_50, iv_51, iv_52, iv_53, iv_54, iv_55, iv_56, iv_57, 235 | iv_58, iv_59, iv_60, iv_61, iv_62, iv_63, iv_64, iv_65, iv_66, iv_67, iv_68, iv_69, iv_70, 236 | iv_71, iv_72, iv_73, iv_74, iv_75, iv_76, iv_77, iv_78, iv_79, iv_80, iv_81, iv_82, iv_83, 237 | iv_84, iv_85, iv_86, iv_87, iv_88, iv_89, iv_90, iv_91, iv_92, iv_93, iv_94, iv_95, iv_96, 238 | iv_97, iv_98, iv_99, iv_100, iv_101, iv_102, iv_103, iv_104, iv_105, iv_106, iv_107, iv_108, 239 | iv_109, iv_110, iv_111, iv_112, iv_113, iv_114, iv_115, iv_116, iv_117, iv_118, iv_119, iv_120, 240 | iv_121, iv_122, iv_123, iv_124, iv_125, iv_126, iv_127, iv_128, iv_129, iv_130, iv_131, iv_132, 241 | iv_133, iv_134, iv_135, iv_136, iv_137, iv_138, iv_139, iv_140, iv_141, iv_142, iv_143, iv_144, 242 | iv_145, iv_146, iv_147, iv_148, iv_149, iv_150, iv_151, iv_152, iv_153, iv_154, iv_155, iv_156, 243 | iv_157, iv_158, iv_159, iv_160, iv_161, iv_162, iv_163, iv_164, iv_165, iv_166, iv_167, iv_168, 244 | iv_169, iv_170, iv_171, iv_172, iv_173, iv_174, iv_175, iv_176, iv_177, iv_178, iv_179, iv_180, 245 | iv_181, iv_182, iv_183, iv_184, iv_185, iv_186, iv_187, iv_188, iv_189, iv_190, iv_191, iv_192, 246 | iv_193, iv_194, iv_195, iv_196, iv_197, iv_198, iv_199, iv_200, iv_201, iv_202, iv_203, iv_204, 247 | iv_205, iv_206, iv_207, iv_208, iv_209, iv_210, iv_211, iv_212, iv_213, iv_214, iv_215, iv_216, 248 | iv_217, iv_218, iv_219, iv_220, iv_221, iv_222, iv_223, iv_224, iv_225, iv_226, iv_227, iv_228, 249 | iv_229, iv_230, iv_231, iv_232, iv_233, iv_234, iv_235, iv_236, iv_237, iv_238, iv_239, iv_240, 250 | iv_241, iv_242, iv_243, iv_244, iv_245, iv_246, iv_247, iv_248, iv_249, iv_250, iv_251, iv_252, 251 | iv_253, iv_254, iv_255, 252 | ]; 253 | 254 | global_asm! { 255 | include_str!("vectors.asm"), 256 | } 257 | -------------------------------------------------------------------------------- /charlotte_core/src/arch/x86_64/interrupts/vectors_asm.py: -------------------------------------------------------------------------------- 1 | template = """ 2 | .global iv_{num} 3 | iv_{num}: 4 | call save_regs 5 | mov rdi, {num} 6 | call isr_handler 7 | call restore_regs 8 | iretq 9 | """ 10 | 11 | file_tmp = """ 12 | // From vector 32 to 255 create a handler 13 | .code64 14 | 15 | .text 16 | 17 | .extern save_regs 18 | .extern restore_regs 19 | .extern isr_handler 20 | {handlers} 21 | 22 | // end of handlers 23 | """ 24 | 25 | if __name__ == '__main__': 26 | handlers = "" 27 | 28 | for i in range(32, 256): 29 | handlers += template.format(num=i) 30 | 31 | print(file_tmp.format(handlers=handlers)) 32 | -------------------------------------------------------------------------------- /charlotte_core/src/arch/x86_64/interrupts/vectors_rs.py: -------------------------------------------------------------------------------- 1 | template = """fn iv_{num}(); 2 | """ 3 | 4 | file_tmp = """ 5 | // handlers 6 | extern "C" {{ 7 | {handlers} 8 | }} 9 | // end of handlers 10 | pub const IV_HANDLERS: [unsafe extern "C" fn(); 224] = [{refs}]; 11 | """ 12 | 13 | if __name__ == '__main__': 14 | handlers = "" 15 | refs = [] 16 | 17 | for i in range(32, 256): 18 | handlers += template.format(num=i) 19 | refs.append(f"iv_{i}") 20 | 21 | print(file_tmp.format(handlers=handlers, refs=", ".join(refs))) 22 | -------------------------------------------------------------------------------- /charlotte_core/src/arch/x86_64/memory/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod page_map; 2 | 3 | use core::arch::x86_64::__cpuid_count; 4 | 5 | use crate::memory::address::{PhysicalAddress, VirtualAddress}; 6 | use crate::memory::pmm::Error as PmmError; 7 | use spin::lazy::Lazy; 8 | 9 | /// The number of significant binary digits in a physical address 10 | pub static PADDR_SIGBITS: Lazy = Lazy::new(|| { 11 | let cpuid_result = unsafe { __cpuid_count(0x80000008, 0) }; 12 | (cpuid_result.eax & 0xFF) as u8 13 | }); 14 | 15 | /// This is the number of significant binary digits in a virtual (linear) address 16 | /// This will not be used for now however it is here to support an implementation of 17 | /// 5-level paging in the future 18 | pub static VADDR_SIGBITS: Lazy = Lazy::new(|| { 19 | let cpuid_result = unsafe { __cpuid_count(0x80000001, 0) }; 20 | if cpuid_result.ecx & (1 << 16) != 0 { 21 | 57u8 22 | } else { 23 | 48u8 24 | } 25 | }); 26 | 27 | #[derive(Debug, PartialEq, Eq)] 28 | pub enum Error { 29 | UnsupportedOperation, 30 | InvalidArgument, 31 | InvalidAddress, 32 | InvalidPAddrAlignment, 33 | InvalidVAddrAlignment, 34 | OutOfMemory, 35 | VAddrRangeUnavailable, 36 | EntryNotPresent, 37 | EntryNotTable, 38 | NoSizeBit, 39 | OpNotSupportedAtThisLevel, 40 | AlredyHasPcid, 41 | InvalidPcid, 42 | PmmError(PmmError), 43 | } 44 | 45 | impl From for Error { 46 | fn from(error: PmmError) -> Self { 47 | Error::PmmError(error) 48 | } 49 | } 50 | 51 | extern "C" { 52 | fn asm_load_page_map(paddr: PhysicalAddress); 53 | fn asm_invalidate_tlb_entry(vaddr: VirtualAddress); 54 | pub fn asm_get_cr4() -> u64; 55 | } 56 | -------------------------------------------------------------------------------- /charlotte_core/src/arch/x86_64/memory/page_map/mod.asm: -------------------------------------------------------------------------------- 1 | .code64 2 | 3 | .text 4 | .global asm_load_page_map 5 | asm_load_page_map: 6 | mov cr3, rdi 7 | ret 8 | 9 | .global asm_get_cr4 10 | asm_get_cr4: 11 | mov rax, cr4 12 | ret 13 | 14 | .global asm_get_cr3 15 | asm_get_cr3: 16 | mov rax, cr3 17 | ret 18 | 19 | .global asm_invalidate_tlb_entry 20 | asm_invalidate_tlb_entry: 21 | invlpg [rdi] 22 | ret -------------------------------------------------------------------------------- /charlotte_core/src/arch/x86_64/memory/page_map/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod page_table; 2 | 3 | use page_table::PageTable; 4 | 5 | use super::Error; 6 | 7 | use core::arch::{asm, global_asm}; 8 | use core::fmt::Write; 9 | use core::ptr::addr_of_mut; 10 | 11 | use crate::arch::x86_64::cpu::ARE_HUGE_PAGES_SUPPORTED; 12 | use crate::arch::{Api, ArchApi, MemoryMap}; 13 | use crate::logln; 14 | use crate::memory::address::VirtualAddress; 15 | use crate::memory::{address::PhysicalAddress, pmm::PHYSICAL_FRAME_ALLOCATOR}; 16 | 17 | struct Walker<'a> { 18 | page_map: &'a PageMap, 19 | pml4: Option<&'a mut PageTable>, 20 | pdpt: Option<&'a mut PageTable>, 21 | pd: Option<&'a mut PageTable>, 22 | pt: Option<&'a mut PageTable>, 23 | } 24 | 25 | impl<'a> Walker<'a> { 26 | fn new(page_map: &'a PageMap) -> Self { 27 | Self { 28 | page_map: page_map, 29 | pml4: None, 30 | pdpt: None, 31 | pd: None, 32 | pt: None, 33 | } 34 | } 35 | fn walk_cr3(&mut self) -> Result<(), Error> { 36 | unsafe { 37 | self.pml4 = Some(&mut *(<*mut PageTable>::from(self.page_map.get_pml4_paddr()))); 38 | } 39 | Ok(()) 40 | } 41 | fn walk_pml4(&mut self, vaddr: VirtualAddress, flags: u64) -> Result<(), Error> { 42 | match &mut self.pml4 { 43 | Some(pml4) => { 44 | unsafe { 45 | let pml4_ptr = addr_of_mut!(*pml4); 46 | self.pdpt = Some( 47 | &mut *((*pml4_ptr).get_or_map_table( 48 | vaddr, 49 | page_table::PageTableLevel::PML4, 50 | flags, 51 | )?), 52 | ); 53 | } 54 | Ok(()) 55 | } 56 | None => { 57 | self.walk_cr3()?; 58 | self.walk_pml4(vaddr, flags) 59 | } 60 | } 61 | } 62 | 63 | fn walk_pdpt(&mut self, vaddr: VirtualAddress, flags: u64) -> Result<(), Error> { 64 | match &mut self.pdpt { 65 | Some(pdpt) => { 66 | unsafe { 67 | let pdpt_ptr = addr_of_mut!(*pdpt); 68 | self.pd = Some( 69 | &mut *((*pdpt_ptr).get_or_map_table( 70 | vaddr, 71 | page_table::PageTableLevel::PDPT, 72 | flags, 73 | )?), 74 | ); 75 | } 76 | Ok(()) 77 | } 78 | None => { 79 | logln!("Walking PML4"); 80 | self.walk_pml4(vaddr, flags)?; 81 | self.walk_pdpt(vaddr, flags) 82 | } 83 | } 84 | } 85 | 86 | fn walk_pd(&mut self, vaddr: VirtualAddress, flags: u64) -> Result<(), Error> { 87 | match &mut self.pd { 88 | Some(pd) => { 89 | unsafe { 90 | let pd_ptr = addr_of_mut!(*pd); 91 | logln!("Obtained PD pointer: {:p}", pd_ptr); 92 | self.pd = Some( 93 | &mut *((*pd_ptr).get_or_map_table( 94 | vaddr, 95 | page_table::PageTableLevel::PD, 96 | flags, 97 | )?), 98 | ); 99 | logln!("Obtained or Mapped PD table."); 100 | } 101 | Ok(()) 102 | } 103 | None => { 104 | logln!("Walking PDPT"); 105 | self.walk_pdpt(vaddr, flags)?; 106 | self.walk_pd(vaddr, flags) 107 | } 108 | } 109 | } 110 | } 111 | 112 | #[repr(transparent)] 113 | #[derive(Debug)] 114 | pub struct PageMap { 115 | cr3: u64, 116 | } 117 | 118 | impl PageMap { 119 | pub fn try_new() -> Result { 120 | Ok(PageMap { 121 | cr3: PHYSICAL_FRAME_ALLOCATOR.lock().allocate()?.bits() as u64, 122 | }) 123 | } 124 | pub fn from_cr3(cr3: u64) -> Result { 125 | // clear the PCID bits 126 | //cr3 &= !0xFFF; 127 | 128 | if ArchApi::validate_paddr(cr3 as usize) == false { 129 | Err(Error::InvalidAddress) 130 | } else { 131 | Ok(PageMap { cr3: cr3 }) 132 | } 133 | } 134 | pub fn get_pml4_paddr(&self) -> PhysicalAddress { 135 | PhysicalAddress::from(self.cr3 & !0xFFF) 136 | } 137 | pub fn get_pcid(&self) -> u16 { 138 | (self.cr3 & 0xFFF) as u16 139 | } 140 | pub fn set_pcid(&mut self, pcid: u16) -> Result<(), Error> { 141 | if self.get_pcid() != 0 { 142 | Err(Error::AlredyHasPcid) 143 | } else { 144 | self.cr3 = (self.cr3 & !0xFFF) | pcid as u64; 145 | Ok(()) 146 | } 147 | } 148 | fn invalidate_pcid(&self) { 149 | let mut pcid = [0u64; 2]; 150 | pcid[0] = self.get_pcid() as u64; 151 | unsafe { 152 | asm! { 153 | "invpcid 1, [{pcid}]", 154 | pcid = in(reg) pcid.as_ptr(), 155 | } 156 | } 157 | } 158 | } 159 | 160 | impl MemoryMap for PageMap { 161 | type Error = Error; 162 | type Flags = u64; 163 | 164 | /// Loads the page map into the logical processor. 165 | unsafe fn load(&self) -> Result<(), Self::Error> { 166 | if self.get_pcid() != 0 { 167 | unsafe { 168 | asm! { 169 | "mov cr3, {0}", 170 | in(reg) self.cr3, 171 | } 172 | } 173 | Ok(()) 174 | } else { 175 | Err(Error::InvalidPcid) 176 | } 177 | } 178 | 179 | /// Maps a page at the given virtual address. 180 | /// # Arguments 181 | /// * `vaddr` - The virtual address to map the page to 182 | /// * `paddr` - The physical base address of the page frame to be mapped 183 | /// * `flags` - The flags to apply to the page table entry 184 | fn map_page( 185 | &mut self, 186 | vaddr: VirtualAddress, 187 | paddr: PhysicalAddress, 188 | flags: Self::Flags, 189 | ) -> Result<(), Self::Error> { 190 | if vaddr.is_aligned_to(crate::arch::ISA_PARAMS.paging.page_size) == false { 191 | Err(Error::InvalidVAddrAlignment) 192 | } else if vaddr.is_null() { 193 | Err(Error::InvalidAddress) 194 | } else { 195 | let mut walker = Walker::new(self); 196 | logln!("Walker created."); 197 | walker.walk_pd(vaddr, flags)?; 198 | logln!("Walker walked to PD."); 199 | walker.pt.unwrap().map_page( 200 | page_table::PageSize::Standard, 201 | vaddr.pt_index(), 202 | paddr, 203 | flags, 204 | )?; 205 | 206 | Ok(()) 207 | } 208 | } 209 | 210 | /// Unmaps a page from the given page map at the given virtual address. 211 | /// # Arguments 212 | /// * `vaddr` - The virtual address to unmap. 213 | /// # Returns 214 | /// Returns an error of type `Self::Error` if unmapping fails or the physical address that was 215 | /// previously mapped to the given virtual address if successful. 216 | fn unmap_page(&mut self, vaddr: VirtualAddress) -> Result { 217 | let mut walker = Walker::new(self); 218 | walker.walk_pd(vaddr, 0)?; 219 | unsafe { 220 | walker 221 | .pt 222 | .unwrap() 223 | .unmap_page(page_table::PageSize::Standard, vaddr.pt_index()) 224 | } 225 | } 226 | 227 | /// Maps a large page (2 MiB) at the given virtual address. 228 | /// # Arguments 229 | /// * `vaddr` - The virtual address to map. 230 | /// * `paddr` - The physical address to map. 231 | /// * `flags` - The flags to apply to the page table entry. 232 | /// # Returns 233 | /// Returns an error of type `Self::Error` if mapping fails. 234 | fn map_large_page( 235 | &mut self, 236 | vaddr: VirtualAddress, 237 | paddr: PhysicalAddress, 238 | flags: Self::Flags, 239 | ) -> Result<(), Self::Error> { 240 | let mut walker = Walker::new(self); 241 | walker.walk_pdpt(vaddr, flags)?; 242 | walker 243 | .pd 244 | .unwrap() 245 | .map_page(page_table::PageSize::Large, vaddr.pd_index(), paddr, flags) 246 | } 247 | 248 | /// Unmaps a large page from the given page map at the given virtual address. 249 | /// # Arguments 250 | /// * `vaddr` - The virtual address to unmap. 251 | /// # Returns 252 | /// Returns an error of type `Self::Error` if unmapping fails or the physical address that was 253 | /// previously mapped to the given virtual address if successful. 254 | fn unmap_large_page(&mut self, vaddr: VirtualAddress) -> Result { 255 | let mut walker = Walker::new(self); 256 | walker.walk_pdpt(vaddr, 0)?; 257 | unsafe { 258 | walker 259 | .pd 260 | .unwrap() 261 | .unmap_page(page_table::PageSize::Large, vaddr.pd_index()) 262 | } 263 | } 264 | 265 | /// Maps a huge page (1 GiB) at the given virtual address. 266 | /// # Arguments 267 | /// * `vaddr` - The virtual address to map. 268 | /// * `paddr` - The physical address to map. 269 | /// * `flags` - The flags to apply to the page table entry. 270 | /// # Returns 271 | /// Returns an error of type `Self::Error` if mapping fails. 272 | fn map_huge_page( 273 | &mut self, 274 | vaddr: VirtualAddress, 275 | paddr: PhysicalAddress, 276 | flags: Self::Flags, 277 | ) -> Result<(), Self::Error> { 278 | if *ARE_HUGE_PAGES_SUPPORTED == false { 279 | Err(Error::UnsupportedOperation) 280 | } else { 281 | let mut walker = Walker::new(self); 282 | walker.walk_pml4(vaddr, flags)?; 283 | walker.pdpt.unwrap().map_page( 284 | page_table::PageSize::Huge, 285 | vaddr.pdpt_index(), 286 | paddr, 287 | flags, 288 | ) 289 | } 290 | } 291 | 292 | /// Unmaps a huge page from the given page map at the given virtual address. 293 | /// # Arguments 294 | /// * `vaddr` - The virtual address to unmap. 295 | /// # Returns 296 | /// Returns an error of type `Self::Error` if unmapping fails or the physical address that was 297 | /// previously mapped to the given virtual address if successful. 298 | fn unmap_huge_page(&mut self, vaddr: VirtualAddress) -> Result { 299 | if *ARE_HUGE_PAGES_SUPPORTED == false { 300 | Err(Error::UnsupportedOperation) 301 | } else { 302 | let mut walker = Walker::new(self); 303 | walker.walk_pml4(vaddr, 0)?; 304 | unsafe { 305 | walker 306 | .pdpt 307 | .unwrap() 308 | .unmap_page(page_table::PageSize::Huge, vaddr.pdpt_index()) 309 | } 310 | } 311 | } 312 | } 313 | 314 | global_asm!(include_str!("mod.asm")); 315 | 316 | extern "C" { 317 | pub fn asm_get_cr3() -> u64; 318 | } 319 | -------------------------------------------------------------------------------- /charlotte_core/src/arch/x86_64/memory/page_map/page_table/mod.rs: -------------------------------------------------------------------------------- 1 | use page_table_entry::*; 2 | 3 | use crate::arch::x86_64::memory::*; 4 | use crate::arch::ISA_PARAMS; 5 | use crate::memory::address::*; 6 | use crate::memory::pmm::PHYSICAL_FRAME_ALLOCATOR; 7 | 8 | pub mod page_table_entry; 9 | 10 | #[derive(Debug, PartialEq, Eq)] 11 | pub enum PageSize { 12 | Standard = 0, 13 | Large = 1, 14 | Huge = 2, 15 | } 16 | 17 | #[derive(Debug, PartialEq, Eq)] 18 | pub enum PageTableLevel { 19 | PML4 = 4, 20 | PDPT = 3, 21 | PD = 2, 22 | PT = 1, 23 | } 24 | 25 | const N_PT_ENTRIES: usize = 512; 26 | const LARGE_PAGE_NFRAMES: u64 = 512; 27 | const HUGE_PAGE_NFRAMES: u64 = 512 * 512; 28 | 29 | #[repr(align(4096))] 30 | #[derive(Debug)] 31 | pub struct PageTable { 32 | table: [PageTableEntry; N_PT_ENTRIES], 33 | } 34 | 35 | impl PageTable { 36 | pub fn new() -> Self { 37 | Self { 38 | table: [PageTableEntry::new(); N_PT_ENTRIES], 39 | } 40 | } 41 | 42 | pub fn map_table(&mut self, index: usize, flags: u64) -> Result { 43 | let table_paddr = PHYSICAL_FRAME_ALLOCATOR.lock().allocate()?; 44 | self.table[index].map_table(table_paddr, flags)?; 45 | Ok(PHYSICAL_FRAME_ALLOCATOR.lock().allocate().unwrap()) 46 | } 47 | 48 | pub unsafe fn unmap_table(&mut self, index: usize) -> Result<(), Error> { 49 | if self.table[index].is_present() { 50 | if !self.table[index].is_size_bit_set() { 51 | let table_paddr = self.table[index].unmap()?; 52 | PHYSICAL_FRAME_ALLOCATOR.lock().deallocate(table_paddr)?; 53 | Ok(()) 54 | } else { 55 | Err(Error::EntryNotTable) 56 | } 57 | } else { 58 | Err(Error::EntryNotPresent) 59 | } 60 | } 61 | 62 | pub fn map_page( 63 | &mut self, 64 | size: PageSize, 65 | index: usize, 66 | paddr: PhysicalAddress, 67 | flags: u64, 68 | ) -> Result<(), Error> { 69 | match size { 70 | PageSize::Standard => { 71 | if !paddr.is_page_aligned() { 72 | return Err(Error::InvalidPAddrAlignment); 73 | } 74 | } 75 | PageSize::Large => { 76 | if !paddr.is_aligned_to(ISA_PARAMS.paging.page_size * LARGE_PAGE_NFRAMES) { 77 | return Err(Error::InvalidPAddrAlignment); 78 | } 79 | } 80 | PageSize::Huge => { 81 | if !paddr.is_aligned_to(ISA_PARAMS.paging.page_size * HUGE_PAGE_NFRAMES) { 82 | return Err(Error::InvalidPAddrAlignment); 83 | } 84 | } 85 | } 86 | self.table[index].map_page(paddr, flags, size)?; 87 | Ok(()) 88 | } 89 | 90 | pub unsafe fn unmap_page( 91 | &mut self, 92 | size: PageSize, 93 | index: usize, 94 | ) -> Result { 95 | let page_paddr = self.table[index].unmap()?; 96 | match size { 97 | PageSize::Standard => PHYSICAL_FRAME_ALLOCATOR.lock().deallocate(page_paddr)?, 98 | PageSize::Large => PHYSICAL_FRAME_ALLOCATOR 99 | .lock() 100 | .deallocate_contiguous(page_paddr, LARGE_PAGE_NFRAMES)?, 101 | PageSize::Huge => PHYSICAL_FRAME_ALLOCATOR 102 | .lock() 103 | .deallocate_contiguous(page_paddr, HUGE_PAGE_NFRAMES)?, 104 | } 105 | Ok(page_paddr) 106 | } 107 | 108 | pub fn get_or_map_table( 109 | &mut self, 110 | vaddr: VirtualAddress, 111 | level: PageTableLevel, 112 | flags: u64, 113 | ) -> Result<*mut PageTable, Error> { 114 | let index = match level { 115 | PageTableLevel::PML4 => vaddr.pml4_index(), 116 | PageTableLevel::PDPT => vaddr.pdpt_index(), 117 | PageTableLevel::PD => vaddr.pd_index(), 118 | PageTableLevel::PT => vaddr.pt_index(), 119 | }; 120 | if self.table[index].is_present() { 121 | match level { 122 | PageTableLevel::PDPT | PageTableLevel::PD => { 123 | if self.table[index].is_size_bit_set() { 124 | return Err(Error::VAddrRangeUnavailable); 125 | } 126 | } 127 | _ => {} 128 | } 129 | Ok(<*mut PageTable>::from(self.table[index].addr().unwrap())) 130 | } else { 131 | Ok(<*mut PageTable>::from(self.map_table(index, flags)?)) 132 | } 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /charlotte_core/src/arch/x86_64/memory/page_map/page_table/page_table_entry.rs: -------------------------------------------------------------------------------- 1 | use super::PageSize; 2 | 3 | use crate::arch::x86_64::memory::*; 4 | use crate::memory::address::*; 5 | 6 | static ADDR_MASK: Lazy = Lazy::new(|| { 7 | // Create a mask that will clear all bits in a PTE that are not address bits 8 | let mut mask = (1 << *PADDR_SIGBITS) - 1; 9 | mask &= !0xFFF; // Clear the lower 12 bits 10 | mask 11 | }); 12 | 13 | /// Page Table Entry Flags 14 | /// Any items prefixed with `Cc` are specific to this kernel and are not part of the x86_64 ISA 15 | pub enum PteFlags { 16 | Present = 1, 17 | Write = 1 << 1, 18 | User = 1 << 2, 19 | WriteThrough = 1 << 3, 20 | CacheDisable = 1 << 4, 21 | Accessed = 1 << 5, 22 | Dirty = 1 << 6, // Only for entries that point to pages 23 | PageSizeOrPat = 1 << 7, // PageSize for entires in the PDPT, and PD for 1GiB and 2MiB pages and PAT for 4KiB pages 24 | Global = 1 << 8, 25 | HugeAndLargePat = 1 << 12, // Only for entries in the PDPT, and PD for 1GiB and 2MiB pages 26 | CcCopyOnWrite = 1 << 52, // Only for entries that point to pages. This bit indicates that the page should be copied on write 27 | CcShared = 1 << 53, // Only for entries that point to pages. This bit indicates that the page is shared between multiple address spaces 28 | NoExecute = 1 << 63, 29 | } 30 | 31 | static FLAG_MASK: u64 = PteFlags::Present as u64 32 | | PteFlags::Write as u64 33 | | PteFlags::User as u64 34 | | PteFlags::WriteThrough as u64 35 | | PteFlags::CacheDisable as u64 36 | | PteFlags::Accessed as u64 37 | | PteFlags::Dirty as u64 38 | | PteFlags::PageSizeOrPat as u64 39 | | PteFlags::Global as u64 40 | | PteFlags::CcCopyOnWrite as u64 41 | | PteFlags::CcShared as u64 42 | | PteFlags::NoExecute as u64; 43 | 44 | static HUGE_AND_LARGE_PAGE_FLAG_MASK: u64 = FLAG_MASK | PteFlags::HugeAndLargePat as u64; 45 | 46 | #[derive(Debug, Clone, Copy)] 47 | #[repr(transparent)] 48 | pub struct PageTableEntry { 49 | entry: u64, 50 | } 51 | 52 | impl PageTableEntry { 53 | #[inline] 54 | pub fn new() -> Self { 55 | Self { entry: 0 } 56 | } 57 | 58 | #[inline] 59 | pub fn addr(&self) -> Result { 60 | if self.is_present() == false { 61 | Err(Error::EntryNotPresent) 62 | } else { 63 | Ok(PhysicalAddress::from(self.entry & *ADDR_MASK)) 64 | } 65 | } 66 | 67 | pub fn map_table(&mut self, paddr: PhysicalAddress, flags: u64) -> Result<(), Error> { 68 | if self.is_present() { 69 | Err(Error::VAddrRangeUnavailable) 70 | } else if !paddr.is_page_aligned() { 71 | Err(Error::InvalidPAddrAlignment) 72 | } else { 73 | self.entry = (paddr.bits() & *ADDR_MASK) | (flags & FLAG_MASK); 74 | Ok(()) 75 | } 76 | } 77 | 78 | pub fn map_page( 79 | &mut self, 80 | paddr: PhysicalAddress, 81 | flags: u64, 82 | size: PageSize, 83 | ) -> Result<(), Error> { 84 | if self.is_present() { 85 | Err(Error::VAddrRangeUnavailable) 86 | } else if !paddr.is_page_aligned() { 87 | Err(Error::InvalidPAddrAlignment) 88 | } else { 89 | let flag_mask = if size == PageSize::Standard { 90 | FLAG_MASK 91 | } else { 92 | HUGE_AND_LARGE_PAGE_FLAG_MASK 93 | }; 94 | self.entry = (paddr.bits() & *ADDR_MASK) | (flags & flag_mask); 95 | Ok(()) 96 | } 97 | } 98 | 99 | pub fn unmap(&mut self) -> Result { 100 | let paddr = self.addr()?; 101 | self.entry = 0; 102 | Ok(paddr) 103 | } 104 | 105 | #[inline] 106 | pub fn is_present(&self) -> bool { 107 | self.entry & PteFlags::Present as u64 != 0 108 | } 109 | 110 | #[inline] 111 | pub fn is_size_bit_set(&self) -> bool { 112 | self.entry & PteFlags::PageSizeOrPat as u64 != 0 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /charlotte_core/src/arch/x86_64/serial.rs: -------------------------------------------------------------------------------- 1 | use core::fmt::{self, Write}; 2 | 3 | use crate::arch::{Api, ArchApi, Serial}; 4 | 5 | #[allow(unused)] 6 | pub enum ComPort { 7 | COM1 = 0x3F8, 8 | COM2 = 0x2F8, 9 | COM3 = 0x3E8, 10 | COM4 = 0x2E8, 11 | COM5 = 0x5F8, 12 | COM6 = 0x4F8, 13 | COM7 = 0x5E8, 14 | COM8 = 0x4E8, 15 | } 16 | 17 | pub struct SerialPort { 18 | io_port: u16, 19 | } 20 | 21 | impl SerialPort { 22 | pub fn try_new(com_port: ComPort) -> Option { 23 | let port = SerialPort { 24 | io_port: com_port as u16, 25 | }; 26 | ArchApi::outb(port.io_port + 1, 0x00); // Disable all interrupts 27 | ArchApi::outb(port.io_port + 3, 0x80); // Enable DLAB (set baud rate divisor) 28 | ArchApi::outb(port.io_port + 0, 0x03); // Set divisor to 3 (lo byte) 38400 baud 29 | ArchApi::outb(port.io_port + 1, 0x00); // (hi byte) 30 | ArchApi::outb(port.io_port + 3, 0x03); // 8 bits, no parity, one stop bit 31 | ArchApi::outb(port.io_port + 2, 0xC7); // Enable FIFO, clear them, with 14-byte threshold 32 | ArchApi::outb(port.io_port + 4, 0x0B); // IRQs enabled, RTS/DSR set 33 | ArchApi::outb(port.io_port + 4, 0x1E); // Set in loopback mode, test the serial chip 34 | ArchApi::outb(port.io_port + 0, 0xAE); // Test serial chip (send byte 0xAE and check if serial returns same byte) 35 | 36 | if ArchApi::inb(port.io_port) != 0xAE { 37 | None 38 | } else { 39 | ArchApi::outb(port.io_port + 4, 0x0F); 40 | Some(port) 41 | } 42 | } 43 | fn is_transmit_empty(&self) -> i32 { 44 | (ArchApi::inb(self.io_port + 5) & 0x20).into() 45 | } 46 | fn received(&self) -> bool { 47 | (ArchApi::inb(self.io_port + 5) & 1) != 0 48 | } 49 | } 50 | 51 | impl Write for SerialPort { 52 | fn write_str(&mut self, s: &str) -> fmt::Result { 53 | for c in s.chars() { 54 | self.write_char(c)? 55 | } 56 | Ok(()) 57 | } 58 | fn write_char(&mut self, c: char) -> fmt::Result { 59 | while self.is_transmit_empty() == 0 {} 60 | if c.is_ascii() { 61 | if c == '\n' { 62 | ArchApi::outb(self.io_port, '\r' as u8); 63 | ArchApi::outb(self.io_port, '\n' as u8); 64 | } else { 65 | ArchApi::outb(self.io_port, c as u8); 66 | } 67 | Ok(()) 68 | } else { 69 | Err(fmt::Error) 70 | } 71 | } 72 | } 73 | 74 | impl Serial for SerialPort { 75 | fn read_char(&mut self) -> char { 76 | while !self.received() {} 77 | ArchApi::inb(self.io_port) as char 78 | } 79 | fn put_char(&mut self, c: char) { 80 | self.write_char(c).unwrap() 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /charlotte_core/src/bootinfo/mod.rs: -------------------------------------------------------------------------------- 1 | //! # Boot Information 2 | //! This module contains requests for information from the Limine boot protocol. 3 | 4 | pub use limine::memory_map; 5 | pub use limine::request::*; 6 | #[allow(unused)] 7 | pub use limine::response::*; 8 | use limine::BaseRevision; 9 | 10 | #[allow(unused)] 11 | /// Require version 1 or later of the Limine boot protocol 12 | pub static BASE_REVISION: BaseRevision = BaseRevision::new(); 13 | 14 | /// This request is used to obtain a direct mapping of physical memory 15 | /// in the kernel's address space. 16 | pub static HHDM_REQUEST: HhdmRequest = HhdmRequest::new(); 17 | /// This request is used to obtain the memory map. 18 | pub static MEMORY_MAP_REQUEST: MemoryMapRequest = MemoryMapRequest::new(); 19 | 20 | /// This request is used to obtain the framebuffer 21 | pub static FRAMEBUFFER_REQUEST: FramebufferRequest = FramebufferRequest::new(); 22 | 23 | /// This request is used to obtain RSDP data 24 | pub static RSDP_REQUEST: RsdpRequest = RsdpRequest::new(); 25 | -------------------------------------------------------------------------------- /charlotte_core/src/framebuffer/colors.rs: -------------------------------------------------------------------------------- 1 | /// Define as a non_exhaustive struct to behave as an "enum" with constant values 2 | /// Doing it this way avoids explicit type casting with Rust enums 3 | #[non_exhaustive] 4 | pub struct Color; 5 | 6 | impl Color { 7 | pub const BLACK: u32 = 0x00000000; 8 | pub const WHITE: u32 = 0xFFFFFFFF; 9 | pub const RED: u32 = 0x00FF0000; 10 | pub const GREEN: u32 = 0x0000FF00; 11 | pub const BLUE: u32 = 0x000000FF; 12 | pub const YELLOW: u32 = 0x00FFFF00; 13 | pub const MAGENTA: u32 = 0x00FF00FF; 14 | pub const CYAN: u32 = 0x0000FFFF; 15 | } 16 | 17 | #[allow(unused)] 18 | pub fn blend_colors(foreground: u32, background: u32, blend_factor: u8) -> u32 { 19 | let fg_ratio = blend_factor as u32; 20 | let bg_ratio = 255 - fg_ratio as u32; 21 | 22 | let r = (((foreground >> 16) & 0xFF) * fg_ratio + ((background >> 16) & 0xFF) * bg_ratio) / 255; 23 | let g = (((foreground >> 8) & 0xFF) * fg_ratio + ((background >> 8) & 0xFF) * bg_ratio) / 255; 24 | let b = ((foreground & 0xFF) * fg_ratio + (background & 0xFF) * bg_ratio) / 255; 25 | 26 | (r << 16) | (g << 8) | b 27 | } 28 | -------------------------------------------------------------------------------- /charlotte_core/src/framebuffer/console.rs: -------------------------------------------------------------------------------- 1 | use core::fmt; 2 | use spin::mutex::TicketMutex; 3 | 4 | use crate::framebuffer::{ 5 | chars::{FONT_HEIGHT, FONT_WIDTH}, 6 | colors::Color, 7 | framebuffer::FRAMEBUFFER, 8 | }; 9 | 10 | pub const CONSOLE_WIDTH: usize = 80; 11 | pub const CONSOLE_HEIGHT: usize = 50; 12 | 13 | pub static CONSOLE: TicketMutex = TicketMutex::new(Console::new()); 14 | 15 | /// Represents a single character on the framebuffer console 16 | #[derive(Copy, Clone)] 17 | struct ConsoleChar { 18 | character: char, 19 | color: u32, 20 | background_color: u32, 21 | } 22 | 23 | /// Buffer of characters in the console 24 | struct ConsoleBuffer { 25 | chars: [[ConsoleChar; CONSOLE_WIDTH]; CONSOLE_HEIGHT], 26 | } 27 | 28 | /// Framebuffer console 29 | pub struct Console { 30 | buffer: ConsoleBuffer, 31 | cursor_x: usize, 32 | cursor_y: usize, 33 | text_color: u32, 34 | background_color: u32, 35 | } 36 | 37 | #[allow(unused)] 38 | impl Console { 39 | /// Create a new console 40 | pub const fn new() -> Self { 41 | Self { 42 | buffer: ConsoleBuffer { 43 | chars: [[ConsoleChar { 44 | character: '\0', 45 | color: 0, 46 | background_color: 0, 47 | }; CONSOLE_WIDTH]; CONSOLE_HEIGHT], 48 | }, 49 | cursor_x: 0, 50 | cursor_y: 0, 51 | text_color: Color::WHITE, 52 | background_color: Color::BLACK, 53 | } 54 | } 55 | 56 | pub fn set_colors(&mut self, text_color: u32, background_color: u32) { 57 | self.text_color = text_color; 58 | self.background_color = background_color; 59 | } 60 | 61 | /// Write a char to the console 62 | pub fn write_char( 63 | &mut self, 64 | character: char, 65 | color: Option, 66 | background_color: Option, 67 | ) { 68 | // Write the character to the buffer 69 | match character { 70 | // Newline 71 | '\n' => { 72 | self.cursor_x = 0; 73 | self.cursor_y += 1; 74 | } 75 | // Carriage return 76 | '\r' => self.cursor_x = 0, 77 | // Tab 78 | '\t' => { 79 | for _ in 0..4 { 80 | self.write_char(' ', color, background_color); 81 | } 82 | } 83 | // Backspace 84 | '\x08' => { 85 | if self.cursor_x > 0 { 86 | self.cursor_x -= 1; 87 | self.write_char(' ', color, background_color); 88 | self.cursor_x -= 1; 89 | } 90 | } 91 | // Any other character 92 | _ => { 93 | self.buffer.chars[self.cursor_y][self.cursor_x] = ConsoleChar { 94 | character, 95 | color: color.unwrap_or(self.text_color), 96 | background_color: background_color.unwrap_or(self.background_color), 97 | }; 98 | self.cursor_x += 1; 99 | } 100 | } 101 | 102 | // If we've reached the end of the line, move to the next line 103 | if self.cursor_x >= CONSOLE_WIDTH { 104 | self.cursor_x = 0; 105 | self.cursor_y += 1; 106 | } 107 | 108 | // If we've reached the end of the console, scroll 109 | if self.cursor_y >= CONSOLE_HEIGHT { 110 | self.scroll(); 111 | } 112 | } 113 | 114 | /// Write a string to the console 115 | pub fn write_str(&mut self, string: &str, color: Option, background_color: Option) { 116 | for character in string.chars() { 117 | self.write_char(character, color, background_color); 118 | } 119 | // Flush the console to the framebuffer 120 | self.flush(); 121 | } 122 | 123 | /// Clear the console 124 | pub fn clear(&mut self) { 125 | for y in 0..CONSOLE_HEIGHT { 126 | for x in 0..CONSOLE_WIDTH { 127 | self.buffer.chars[y][x] = ConsoleChar { 128 | character: '\0', 129 | color: 0, 130 | background_color: 0, 131 | }; 132 | } 133 | } 134 | self.cursor_x = 0; 135 | self.cursor_y = 0; 136 | } 137 | 138 | /// Scroll the console 139 | fn scroll(&mut self) { 140 | // Move all lines up by one 141 | for y in 1..CONSOLE_HEIGHT { 142 | for x in 0..CONSOLE_WIDTH { 143 | self.buffer.chars[y - 1][x] = self.buffer.chars[y][x]; 144 | } 145 | } 146 | 147 | // Clear the last line 148 | for x in 0..CONSOLE_WIDTH { 149 | self.buffer.chars[CONSOLE_HEIGHT - 1][x] = ConsoleChar { 150 | character: '\0', 151 | color: 0, 152 | background_color: 0, 153 | }; 154 | } 155 | self.cursor_y = CONSOLE_HEIGHT - 1; 156 | } 157 | 158 | /// Flush the console to the framebuffer 159 | fn flush(&self) { 160 | let scale: usize = 1; 161 | for y in 0..CONSOLE_HEIGHT { 162 | for x in 0..CONSOLE_WIDTH { 163 | // Draw the character to the framebuffer 164 | FRAMEBUFFER.lock().draw_char( 165 | /* Add a 1 pixel margin between characters */ 166 | x * FONT_WIDTH * scale + 1, 167 | /* Add a 1 pixel margin between lines */ 168 | y * FONT_HEIGHT * scale + 1, 169 | self.buffer.chars[y][x].character, 170 | self.buffer.chars[y][x].color, 171 | self.buffer.chars[y][x].background_color, 172 | ); 173 | } 174 | } 175 | } 176 | 177 | pub fn clear_inner_styling(&self) { 178 | INNER_STYLE_SETTINGS.lock().clear(); 179 | } 180 | } 181 | 182 | static INNER_STYLE_SETTINGS: TicketMutex = 183 | TicketMutex::new(InnerPrintStyle::new()); 184 | 185 | /// Inner style settings for print macros 186 | struct InnerPrintStyle { 187 | text_color: Option, 188 | background_color: Option, 189 | setting_text_color: bool, 190 | setting_background_color: bool, 191 | } 192 | 193 | impl InnerPrintStyle { 194 | /// Create a 195 | const fn new() -> Self { 196 | Self { 197 | text_color: None, 198 | background_color: None, 199 | setting_text_color: false, 200 | setting_background_color: false, 201 | } 202 | } 203 | 204 | fn clear(&mut self) { 205 | self.text_color = None; 206 | self.background_color = None; 207 | self.setting_text_color = false; 208 | self.setting_background_color = false; 209 | } 210 | } 211 | 212 | impl fmt::Write for Console { 213 | fn write_str(&mut self, string: &str) -> fmt::Result { 214 | let mut reading_color_type = false; 215 | let mut styling = INNER_STYLE_SETTINGS.lock(); 216 | 217 | if styling.setting_text_color { 218 | styling.text_color = Some(u32::from_str_radix(string, 16).unwrap_or(Color::WHITE)); 219 | styling.setting_text_color = false; 220 | return Ok(()); 221 | } 222 | if styling.setting_background_color { 223 | styling.background_color = 224 | Some(u32::from_str_radix(string, 16).unwrap_or(Color::BLACK)); 225 | styling.setting_background_color = false; 226 | return Ok(()); 227 | } 228 | 229 | for character in string.chars() { 230 | if character == '[' { 231 | reading_color_type = true; 232 | continue; 233 | } 234 | if reading_color_type { 235 | if character == 'b' || character == 'B' { 236 | styling.setting_text_color = false; 237 | styling.setting_background_color = true; 238 | } else if character == 'f' || character == 'F' { 239 | styling.setting_text_color = true; 240 | styling.setting_background_color = false; 241 | } 242 | reading_color_type = false; 243 | continue; 244 | } 245 | self.write_char(character, styling.text_color, styling.background_color); 246 | } 247 | // Flush the console to the framebuffer 248 | self.flush(); 249 | Ok(()) 250 | } 251 | } 252 | 253 | #[macro_export] 254 | macro_rules! print { 255 | ($($arg:tt)*) => { 256 | CONSOLE.lock().write_fmt(format_args!($($arg)*)).unwrap(); 257 | CONSOLE.lock().clear_inner_styling(); 258 | } 259 | } 260 | 261 | #[macro_export] 262 | macro_rules! println { 263 | ($($arg:tt)*) => { 264 | CONSOLE.lock().write_fmt(format_args!($($arg)*)).unwrap(); 265 | CONSOLE.lock().write_char('\n', None, None); 266 | CONSOLE.lock().clear_inner_styling(); 267 | } 268 | } 269 | -------------------------------------------------------------------------------- /charlotte_core/src/framebuffer/framebuffer.rs: -------------------------------------------------------------------------------- 1 | use core::sync::atomic::{AtomicPtr, Ordering}; 2 | 3 | use crate::bootinfo::FRAMEBUFFER_REQUEST; 4 | use crate::framebuffer::chars::{FONT, FONT_HEIGHT, FONT_WIDTH}; 5 | // External crate for bootloader-specific functions and types. 6 | extern crate limine; 7 | use limine::framebuffer::Framebuffer; 8 | use spin::lazy::Lazy; 9 | use spin::mutex::TicketMutex; 10 | 11 | use super::console::{CONSOLE_HEIGHT, CONSOLE_WIDTH}; 12 | 13 | /// Global access to the framebuffer 14 | pub static FRAMEBUFFER: Lazy> = 15 | Lazy::new(|| TicketMutex::new(init_framebuffer().unwrap())); 16 | 17 | /// A struct representing the framebuffer information, 18 | /// including its memory address, dimensions, pixel format, etc. 19 | pub struct FrameBufferInfo { 20 | address: AtomicPtr, 21 | width: usize, 22 | height: usize, 23 | pitch: usize, 24 | bpp: usize, 25 | scale: usize, 26 | } 27 | 28 | /// including its memory address, dimensions, pixel format, etc. 29 | #[derive(Copy, Clone)] 30 | pub struct Point { 31 | pub x: isize, 32 | pub y: isize, 33 | } 34 | 35 | #[allow(unused)] 36 | impl FrameBufferInfo { 37 | /// Constructs a new `FrameBufferInfo` instance from a limine framebuffer. 38 | /// 39 | /// # Arguments 40 | /// 41 | /// * `framebuffer` - A reference to a limine `Framebuffer` struct. 42 | pub fn new(framebuffer: &Framebuffer) -> Self { 43 | let mut framebuffer = Self { 44 | address: AtomicPtr::new(framebuffer.addr() as *mut u32), 45 | width: framebuffer.width() as usize, 46 | height: framebuffer.height() as usize, 47 | pitch: framebuffer.pitch() as usize, 48 | bpp: framebuffer.bpp() as usize, 49 | scale: 1, 50 | }; 51 | 52 | /* Initialize framebuffer scale automatically */ 53 | framebuffer.calc_scale(); 54 | framebuffer 55 | } 56 | 57 | /// Draws a line between two points using Bresenham's line algorithm. 58 | /// 59 | /// # Arguments 60 | /// 61 | /// * `p0` - The start point of the line. 62 | /// * `p1` - The end point of the line. 63 | /// * `color` - The color of the line in ARGB format. 64 | pub fn draw_line(&self, p0: Point, p1: Point, color: u32) { 65 | let mut x0 = p0.x; 66 | let mut y0 = p0.y; 67 | let x1 = p1.x; 68 | let y1 = p1.y; 69 | 70 | let dx = isize::abs(x1 - x0); 71 | let dy = -isize::abs(y1 - y0); 72 | let sx = if x0 < x1 { 1 } else { -1 }; 73 | let sy = if y0 < y1 { 1 } else { -1 }; 74 | let mut err = dx + dy; // error value e_xy 75 | 76 | loop { 77 | self.draw_pixel(x0 as usize, y0 as usize, color); 78 | // Draw the current pixel 79 | if x0 == x1 && y0 == y1 { 80 | break; 81 | } 82 | let e2 = 2 * err; 83 | if e2 >= dy { 84 | // e_xy+e_x > 0 85 | err += dy; 86 | x0 += sx; 87 | } 88 | if e2 <= dx { 89 | // e_xy+e_y < 0 90 | err += dx; 91 | y0 += sy; 92 | } 93 | } 94 | } 95 | 96 | /// Clears the entire screen to a single color. 97 | /// 98 | /// # Arguments 99 | /// 100 | /// * `color` - The color to fill the screen with, in ARGB format. 101 | pub fn clear_screen(&self, color: u32) { 102 | for y in 0..self.height { 103 | for x in 0..self.width { 104 | let pixel_offset = y * self.pitch / (self.bpp / 8) + x; 105 | unsafe { 106 | *self.address.load(Ordering::Relaxed).add(pixel_offset) = color; 107 | } 108 | } 109 | } 110 | } 111 | 112 | /// Draws a single pixel at the specified location. 113 | /// 114 | /// # Arguments 115 | /// 116 | /// * `x` - The x coordinate of the pixel. 117 | /// * `y` - The y coordinate of the pixel. 118 | /// * `color` - The color of the pixel in ARGB format. 119 | 120 | pub fn draw_pixel(&self, x: usize, y: usize, color: u32) { 121 | if x < self.width && y < self.height { 122 | let pixel_offset = y * self.pitch / (self.bpp / 8) + x; 123 | unsafe { 124 | *self.address.load(Ordering::Relaxed).add(pixel_offset) = color; 125 | } 126 | } 127 | } 128 | 129 | /// Draws text starting from a specified location. 130 | /// 131 | /// # Arguments 132 | /// 133 | /// * `x` - The x coordinate of the starting point of the text. 134 | /// * `y` - The y coordinate of the starting point of the text. 135 | /// * `text` - The text to draw. 136 | /// * `color` - The color of the text in ARGB format. 137 | 138 | pub fn draw_text( 139 | &self, 140 | mut x: usize, 141 | mut y: usize, 142 | text: &str, 143 | color: u32, 144 | background_color: u32, 145 | ) { 146 | let start_x = x; // Remember the starting x position to reset to it on new lines 147 | for c in text.chars() { 148 | match c { 149 | '\n' => { 150 | y += FONT_HEIGHT + 1; 151 | x = start_x; 152 | } 153 | _ => { 154 | self.draw_char(x, y, c, color, background_color); 155 | x += FONT_WIDTH; 156 | } 157 | } 158 | } 159 | } 160 | 161 | /// Helper method to draw a single character from its bitmap. 162 | /// 163 | /// # Arguments 164 | /// 165 | /// * `x` - The x coordinate where the character should be drawn. 166 | /// * `y` - The y coordinate where the character should be drawn. 167 | /// * `bitmap` - A reference to the bitmap array representing the character. 168 | /// * `color` - The color of the character in ARGB format. 169 | pub fn draw_char(&self, x: usize, y: usize, chracter: char, color: u32, background_color: u32) { 170 | let char_int: usize = chracter as usize; 171 | let first_byte_index = char_int * 16; 172 | let mut do_draw: bool; 173 | let mut colour_buffer: u32; 174 | for by in 0..16 { 175 | for bi in 0..8 { 176 | do_draw = ((FONT[first_byte_index + by] >> (7 - bi)) & 1) != 0; 177 | if do_draw { 178 | colour_buffer = color; 179 | } else { 180 | colour_buffer = background_color; 181 | } 182 | self.draw_pixel(x + bi, y + by, colour_buffer); 183 | } 184 | } 185 | } 186 | 187 | /// Draws a rectangle at the specified location and dimensions. 188 | /// 189 | /// # Arguments 190 | /// 191 | /// * `x` - The x coordinate of the rectangle's top-left corner. 192 | /// * `y` - The y coordinate of the rectangle's top-left corner. 193 | /// * `width` - The width of the rectangle. 194 | /// * `height` - The height of the rectangle. 195 | /// * `color` - The color of the rectangle in ARGB format. 196 | pub fn draw_rect(&self, x: usize, y: usize, width: usize, height: usize, color: u32) { 197 | for row in y..y + height { 198 | for col in x..x + width { 199 | self.draw_pixel(col, row, color); 200 | } 201 | } 202 | } 203 | 204 | /// Draws a filled triangle between three points. 205 | /// 206 | /// # Arguments 207 | /// 208 | /// * `p1`, `p2`, `p3` - The vertices of the triangle. 209 | /// * `color` - The color to fill the triangle with, in ARGB format. 210 | pub fn draw_triangle(&self, p1: Point, p2: Point, p3: Point, color: u32) { 211 | // First, sort vertices by y-coordinate 212 | let mut vertices = [p1, p2, p3]; 213 | vertices.sort_unstable_by_key(|v| v.y); 214 | 215 | // Define a closure to interpolate x values for a given y 216 | let interpolate_x = |p1: Point, p2: Point, current_y: isize| -> isize { 217 | if p1.y == p2.y { 218 | return p1.x; 219 | } 220 | p1.x + (p2.x - p1.x) * (current_y - p1.y) / (p2.y - p1.y) 221 | }; 222 | 223 | // Function to fill between two edges from startY to endY 224 | let fill_between_edges = |self_ref: &Self, 225 | start_y: isize, 226 | end_y: isize, 227 | p_left: Point, 228 | p_right_start: Point, 229 | p_right_end: Point| { 230 | for y in start_y..=end_y { 231 | let x_start = interpolate_x(p_left, p_right_start, y); 232 | let x_end = interpolate_x(p_left, p_right_end, y); 233 | for x in x_start.min(x_end)..=x_start.max(x_end) { 234 | self_ref.draw_pixel(x as usize, y as usize, color); 235 | } 236 | } 237 | }; 238 | 239 | // Fill from top vertex to middle vertex 240 | fill_between_edges( 241 | self, 242 | vertices[0].y, 243 | vertices[1].y, 244 | vertices[0], 245 | vertices[1], 246 | vertices[2], 247 | ); 248 | 249 | // Fill from middle vertex to bottom vertex 250 | fill_between_edges( 251 | self, 252 | vertices[1].y, 253 | vertices[2].y, 254 | vertices[2], 255 | vertices[0], 256 | vertices[1], 257 | ); 258 | } 259 | 260 | /// Return the framebuffer scaling multiplier 261 | pub fn get_scale(&self) -> usize { 262 | self.scale 263 | } 264 | 265 | /// Automatically select scaling based on resolution 266 | /// Call this whenever resolution of the monitor changes! 267 | pub fn calc_scale(&mut self) { 268 | let scale_width = self.width / (CONSOLE_WIDTH * FONT_WIDTH); 269 | let scale_height = self.height / (CONSOLE_HEIGHT * FONT_HEIGHT); 270 | self.scale = if (scale_height > scale_width) { 271 | scale_width 272 | } else { 273 | scale_height 274 | }; 275 | } 276 | } 277 | 278 | /// Initializes the framebuffer and returns a `FrameBufferInfo` instance if successful. 279 | pub fn init_framebuffer() -> Option { 280 | if let Some(framebuffer_response) = FRAMEBUFFER_REQUEST.get_response() { 281 | if framebuffer_response.framebuffers().count() < 1 { 282 | panic!("No framebuffer returned from bootloader!"); 283 | } 284 | 285 | let framebuffer = &framebuffer_response.framebuffers().next().unwrap(); 286 | Some(FrameBufferInfo::new(framebuffer)) 287 | } else { 288 | panic!("No framebuffer returned from bootlaoder!"); 289 | } 290 | } 291 | -------------------------------------------------------------------------------- /charlotte_core/src/framebuffer/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod chars; 2 | pub mod colors; 3 | pub mod console; 4 | pub mod framebuffer; 5 | -------------------------------------------------------------------------------- /charlotte_core/src/kmon.rs: -------------------------------------------------------------------------------- 1 | use crate::arch::Serial; 2 | use crate::log; 3 | use core::fmt::Write; 4 | 5 | pub struct Kmon { 6 | pub port: T, 7 | recv_buf_pos: usize, 8 | pub recv_buf: [char; 256], 9 | } 10 | 11 | impl Kmon { 12 | fn is_ascii_printable(c: u8) -> bool { 13 | (0x20..=0x7E).contains(&c) 14 | } 15 | 16 | pub fn new(port: T) -> Self { 17 | Self { 18 | port, 19 | recv_buf_pos: 0, 20 | recv_buf: ['\0'; 256], 21 | } 22 | } 23 | 24 | fn handle_line(&mut self) { 25 | self.recv_buf[self.recv_buf_pos] = '\0'; 26 | for i in 0..self.recv_buf_pos { 27 | log!("{}", self.recv_buf[i]); 28 | } 29 | log!("\n"); 30 | self.recv_buf_pos = 0; 31 | self.print_term_begin(); 32 | } 33 | 34 | fn handle_char(&mut self, c: char) { 35 | if Self::is_ascii_printable(c as u8) { 36 | log!("{}", c); 37 | log!("_\x08"); 38 | self.recv_buf[self.recv_buf_pos] = c; 39 | self.recv_buf_pos += 1; 40 | } else if c == '\r' { 41 | /* Clear _ */ 42 | log!(" \x08\n"); 43 | if self.recv_buf_pos == 0 { 44 | self.print_term_begin(); 45 | } else { 46 | self.handle_line(); 47 | } 48 | } else if c == '\x08' || c == '\x7F' { 49 | if self.recv_buf_pos > 0 { 50 | self.recv_buf[self.recv_buf_pos] = '\0'; 51 | self.recv_buf_pos -= 1; 52 | /* Ugliest hack in the world to fix backspace... */ 53 | log!("\x7F \x08\x08 \x08_\x08"); 54 | } 55 | } else { 56 | log!("\nUnknown character: {:x}\n", c as u8); 57 | 58 | /* Reset written content */ 59 | self.recv_buf = ['\0'; 256]; 60 | self.recv_buf_pos = 0; 61 | self.print_term_begin(); 62 | } 63 | } 64 | 65 | pub fn repl_loop(&mut self) { 66 | log!("=================== Serial Prompt v1.0 =====================\n"); 67 | self.print_term_begin(); 68 | loop { 69 | let c = self.port.read_char(); 70 | self.handle_char(c); 71 | } 72 | } 73 | 74 | fn print_term_begin(&self) { 75 | log!(">>> _\x08"); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /charlotte_core/src/logging/logger.rs: -------------------------------------------------------------------------------- 1 | use core::fmt::{self, Write}; 2 | use spin::Mutex; 3 | use crate::framebuffer::framebuffer::FRAMEBUFFER; 4 | use crate::framebuffer::colors::Color; 5 | 6 | #[derive(Debug, Clone, Copy)] 7 | pub enum LogLevel { 8 | Error, 9 | Warn, 10 | Info, 11 | Debug, 12 | } 13 | 14 | pub struct Logger { 15 | level: LogLevel, 16 | } 17 | 18 | impl Logger { 19 | pub fn init(level: LogLevel) { 20 | *LOGGER.lock() = Logger { level }; 21 | } 22 | 23 | pub fn log(&self, level: LogLevel, message: &str) { 24 | if level as u8 <= self.level as u8 { 25 | let mut framebuffer = FRAMEBUFFER.lock(); 26 | writeln!(framebuffer, "[{:?}] {}", level, message).unwrap(); 27 | } 28 | } 29 | } 30 | 31 | impl Write for Logger { 32 | fn write_str(&mut self, s: &str) -> fmt::Result { 33 | let mut framebuffer = FRAMEBUFFER.lock(); 34 | framebuffer.write_str(s) 35 | } 36 | } 37 | 38 | static LOGGER: Mutex = Mutex::new(Logger { level: LogLevel::Info }); 39 | 40 | pub fn log(level: LogLevel, message: &str) { 41 | LOGGER.lock().log(level, message); 42 | } 43 | 44 | #[macro_export] 45 | macro_rules! logln { 46 | ($level:expr, $($arg:tt)*) => ({ 47 | $crate::logging::logger::log($level, &format!($($arg)*)); 48 | }) 49 | } -------------------------------------------------------------------------------- /charlotte_core/src/logging/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod logger; -------------------------------------------------------------------------------- /charlotte_core/src/main.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | #![warn(missing_copy_implementations)] 4 | 5 | use core::fmt::Write; 6 | use core::panic::PanicInfo; 7 | 8 | use arch::{Api, ArchApi, HwTimerMode}; 9 | 10 | use crate::kmon::Kmon; 11 | 12 | mod acpi; 13 | mod arch; 14 | mod bootinfo; 15 | mod framebuffer; 16 | mod kmon; 17 | mod memory; 18 | 19 | /// This is the kernel entrypoint function, 20 | /// the first thing it does is call: [isa_init](ArchApi::isa_init) 21 | /// you should check the documentation on that function for details, 22 | /// since that contains all the ISA specific initialization code. 23 | #[no_mangle] 24 | unsafe extern "C" fn main() -> ! { 25 | let mut arch_api = ArchApi::isa_init(); 26 | logln!("Bring up finished, starting kernel interactive prompt"); 27 | 28 | //This code currently causes a triple fault if allowed to run. A fix is needed! 29 | /* // Setup handle_timer function to handle interrupt vector 32 for x86_64 30 | #[cfg(target_arch = "x86_64")] 31 | arch_api.set_interrupt_handler(on_tick, 32); 32 | // Start the ISA specific timer(s) with a rate of about every 10us (1MHz) 33 | arch_api.setup_isa_timer(100_000, HwTimerMode::Recurrent, 0); 34 | arch_api.start_isa_timers(); */ 35 | let port = arch_api.get_serial(); 36 | let mut mon = Kmon::new(port); 37 | mon.repl_loop(); 38 | 39 | #[allow(clippy::empty_loop)] 40 | loop {} 41 | } 42 | 43 | #[no_mangle] 44 | /// System monotonic time, this is a global variable that is updated every time the timer ticks. 45 | static mut SYSTEM_MONOTONIC: u64 = 0; 46 | 47 | /// This function handles timer ticks, inside of it you can dispatch schedulers 48 | /// or anything else that needs to be done on a timer tick. 49 | fn on_tick(_: u64) { 50 | unsafe { 51 | SYSTEM_MONOTONIC += 10; 52 | } 53 | ArchApi::end_of_interrupt(); 54 | } 55 | 56 | #[panic_handler] 57 | fn rust_panic(_info: &PanicInfo) -> ! { 58 | logln!("A kernel panic has occurred due to a Rust runtime panic."); 59 | logln!("PanicInfo: {:?}", _info); 60 | ArchApi::panic() 61 | } 62 | -------------------------------------------------------------------------------- /charlotte_core/src/memory/address.rs: -------------------------------------------------------------------------------- 1 | use core::ops::Add; 2 | 3 | use crate::arch::{Api, ArchApi, ISA_PARAMS}; 4 | use crate::memory::pmm::DIRECT_MAP; 5 | 6 | pub const PAGE_SIZE: UAddr = ISA_PARAMS.paging.page_size; 7 | pub const PAGE_SHIFT: UAddr = ISA_PARAMS.paging.page_shift; 8 | pub const PAGE_MASK: UAddr = ISA_PARAMS.paging.page_mask; 9 | 10 | #[cfg(not(target_pointer_width = "64"))] 11 | compile_error! {"Unsupported ISA pointer width"} 12 | 13 | #[cfg(target_pointer_width = "64")] 14 | pub type UAddr = u64; 15 | 16 | pub trait MemoryAddress { 17 | type MemoryAddress: MemoryAddress; 18 | fn is_aligned(&self, alignment: UAddr) -> bool; 19 | fn is_page_aligned(&self) -> bool; 20 | fn is_vaddress() -> bool; 21 | } 22 | 23 | #[derive(Clone, Copy, Debug, PartialOrd, Ord, PartialEq, Eq)] 24 | #[repr(transparent)] 25 | pub struct PhysicalAddress(UAddr); 26 | 27 | impl PhysicalAddress { 28 | #[inline] 29 | pub const fn new(addr: UAddr) -> Self { 30 | Self(addr) 31 | } 32 | 33 | pub const fn as_usize(&self) -> usize { 34 | self.0 as usize 35 | } 36 | 37 | #[inline] 38 | pub const fn bits(&self) -> UAddr { 39 | self.0 40 | } 41 | 42 | #[inline] 43 | pub const fn pfn(&self) -> UAddr { 44 | self.bits() >> PAGE_SHIFT 45 | } 46 | 47 | #[inline] 48 | pub const fn from_pfn(pfn: UAddr) -> Self { 49 | Self::new(pfn << PAGE_SHIFT) 50 | } 51 | 52 | #[inline] 53 | /// # Safety 54 | /// This function will panic in case align == 0 or align - 1 == 0 55 | pub const fn is_aligned_to(&self, align: UAddr) -> bool { 56 | if align == 0 { 57 | panic!("Tried to test alignment to 0") 58 | } 59 | self.bits() & (align - 1) == 0 60 | } 61 | 62 | #[inline] 63 | pub const fn is_page_aligned(&self) -> bool { 64 | self.is_aligned_to(PAGE_SIZE) 65 | } 66 | 67 | #[inline] 68 | pub fn iter_frames(&self, n_frames: UAddr) -> impl Iterator { 69 | (self.bits()..(self.bits() + n_frames * PAGE_SIZE)) 70 | .step_by(PAGE_SIZE as usize) 71 | .map(PhysicalAddress::new) 72 | } 73 | 74 | pub unsafe fn as_ref(&self) -> &T { 75 | unsafe { &*(self.bits() as *const T) } 76 | } 77 | 78 | #[allow(clippy::mut_from_ref)] 79 | pub unsafe fn as_mut(&self) -> &mut T { 80 | unsafe { &mut *(self.bits() as *mut T) } 81 | } 82 | } 83 | 84 | impl MemoryAddress for PhysicalAddress { 85 | type MemoryAddress = PhysicalAddress; 86 | 87 | fn is_aligned(&self, alignment: UAddr) -> bool { 88 | self.is_aligned_to(alignment) 89 | } 90 | 91 | fn is_page_aligned(&self) -> bool { 92 | self.is_page_aligned() 93 | } 94 | 95 | fn is_vaddress() -> bool { 96 | false 97 | } 98 | } 99 | 100 | impl From for PhysicalAddress { 101 | #[inline] 102 | fn from(val: UAddr) -> Self { 103 | Self::new(val) 104 | } 105 | } 106 | 107 | impl From for usize { 108 | #[inline] 109 | fn from(addr: PhysicalAddress) -> Self { 110 | addr.as_usize() 111 | } 112 | } 113 | 114 | impl From for *const T { 115 | #[inline] 116 | #[allow(clippy::unconditional_recursion)] 117 | fn from(addr: PhysicalAddress) -> *const T { 118 | (*DIRECT_MAP + addr.0).into() 119 | } 120 | } 121 | 122 | #[allow(clippy::unconditional_recursion)] 123 | impl From for *mut T { 124 | #[inline] 125 | fn from(addr: PhysicalAddress) -> *mut T { 126 | (*DIRECT_MAP + addr.0).into() 127 | } 128 | } 129 | 130 | impl Add for PhysicalAddress { 131 | type Output = Self; 132 | 133 | #[inline] 134 | fn add(self, val: UAddr) -> Self::Output { 135 | Self::new(self.0 + val) 136 | } 137 | } 138 | 139 | #[derive(Clone, Copy, Debug, PartialOrd, Ord, PartialEq, Eq)] 140 | #[repr(transparent)] 141 | pub struct VirtualAddress(UAddr); 142 | 143 | #[derive(Debug, Clone, Copy)] 144 | pub enum VAddrError { 145 | InvalidForm(u64), 146 | InvalidAlignment(u64), 147 | } 148 | 149 | impl VirtualAddress { 150 | const NULL: VirtualAddress = VirtualAddress(0); 151 | /// Default constructor that provides a null virtual address 152 | /// (0x0 is to be used as the null virtual address in CharlotteOS) 153 | #[inline] 154 | pub fn new() -> Self { 155 | Self::default() 156 | } 157 | #[inline] 158 | pub fn bits(&self) -> UAddr { 159 | self.0 160 | } 161 | /// Check if the virtual address is null 162 | #[inline] 163 | pub fn is_null(&self) -> bool { 164 | self.0 == 0 165 | } 166 | /// Check if the virtual address is aligned to the specified alignment 167 | #[inline] 168 | pub fn is_aligned_to(&self, align: UAddr) -> bool { 169 | self.0 % align == 0 170 | } 171 | /// Get the base address of the page that the virtual address is in 172 | #[inline] 173 | pub fn get_page_base(&self) -> UAddr { 174 | self.0 & PAGE_MASK 175 | } 176 | /// Get the offset of the virtual address from the base address of the page 177 | #[inline] 178 | pub fn get_page_offset(&self) -> usize { 179 | (self.0 & 0xfff) as usize 180 | } 181 | #[inline] 182 | pub fn pml4_index(&self) -> usize { 183 | ((self.0 >> 39) & 0x1ff) as usize 184 | } 185 | #[inline] 186 | pub fn pdpt_index(&self) -> usize { 187 | ((self.0 >> 30) & 0x1ff) as usize 188 | } 189 | #[inline] 190 | pub fn pd_index(&self) -> usize { 191 | ((self.0 >> 21) & 0x1ff) as usize 192 | } 193 | #[inline] 194 | pub fn pt_index(&self) -> usize { 195 | ((self.0 >> 12) & 0x1ff) as usize 196 | } 197 | } 198 | 199 | impl MemoryAddress for VirtualAddress { 200 | type MemoryAddress = VirtualAddress; 201 | 202 | fn is_aligned(&self, alignment: UAddr) -> bool { 203 | self.is_aligned_to(alignment) 204 | } 205 | 206 | fn is_page_aligned(&self) -> bool { 207 | self.is_aligned_to(self.get_page_base()) 208 | } 209 | 210 | fn is_vaddress() -> bool { 211 | true 212 | } 213 | } 214 | 215 | impl Default for VirtualAddress { 216 | fn default() -> Self { 217 | Self::NULL 218 | } 219 | } 220 | 221 | impl TryFrom for VirtualAddress { 222 | type Error = VAddrError; 223 | 224 | fn try_from(addr: u64) -> Result { 225 | if ArchApi::validate_vaddr(addr) { 226 | Ok(Self(addr as UAddr)) 227 | } else { 228 | Err(VAddrError::InvalidForm(addr)) 229 | } 230 | } 231 | } 232 | 233 | impl From for UAddr { 234 | #[inline] 235 | fn from(addr: VirtualAddress) -> Self { 236 | addr.0 237 | } 238 | } 239 | 240 | /// VirtualAddress can be converted to a const or mut pointer. 241 | /// Safety: This conversion itself is safe, but dereferencing the resulting pointer may not be. 242 | /// This is why only converstions to raw pointers are provided because dereferencing raw pointers is considered unsafe 243 | /// and requires that invariants the compiler cannot verify are upheld by the developer. 244 | impl From for *const T { 245 | #[inline] 246 | fn from(addr: VirtualAddress) -> *const T { 247 | addr.0 as *const T 248 | } 249 | } 250 | 251 | impl From for *mut T { 252 | #[inline] 253 | fn from(addr: VirtualAddress) -> *mut T { 254 | addr.0 as *mut T 255 | } 256 | } 257 | 258 | impl Add for VirtualAddress { 259 | type Output = Self; 260 | 261 | #[inline] 262 | fn add(self, val: usize) -> Self::Output { 263 | Self(self.0 + val as UAddr) 264 | } 265 | } 266 | 267 | impl Add for VirtualAddress { 268 | type Output = Self; 269 | 270 | #[inline] 271 | fn add(self, val: UAddr) -> Self::Output { 272 | Self(self.0 + val) 273 | } 274 | } 275 | -------------------------------------------------------------------------------- /charlotte_core/src/memory/mod.rs: -------------------------------------------------------------------------------- 1 | //! # Memory Management Subsystem 2 | //! The memory management subsystem is responsible for managing the direct mapping of physical 3 | //! memory in the kernel's address space, allocating and deallocating physical frames, and managing 4 | //! all virtual address spaces. 5 | 6 | pub mod address; 7 | pub mod pmm; 8 | pub mod span_printer; 9 | -------------------------------------------------------------------------------- /charlotte_core/src/memory/pmm.rs: -------------------------------------------------------------------------------- 1 | use crate::bootinfo; 2 | use crate::memory::address::{PhysicalAddress, UAddr, VirtualAddress}; 3 | 4 | use core::slice::from_raw_parts_mut; 5 | 6 | use spin::{lazy::Lazy, mutex::Mutex}; 7 | 8 | pub static DIRECT_MAP: Lazy = Lazy::new(|| { 9 | VirtualAddress::try_from( 10 | bootinfo::HHDM_REQUEST 11 | .get_response() 12 | .expect("Limine failed to create a direct mapping of physical memory.") 13 | .offset(), 14 | ) 15 | .expect("Direct map address does not fit in a VirtualAddress") 16 | }); 17 | 18 | pub static PHYSICAL_FRAME_ALLOCATOR: Lazy> = 19 | Lazy::new(|| Mutex::new(PhysicalFrameAllocator::new())); 20 | 21 | pub struct MemoryMap { 22 | entries: &'static [&'static bootinfo::memory_map::Entry], 23 | } 24 | 25 | impl MemoryMap { 26 | pub fn get() -> MemoryMap { 27 | MemoryMap { 28 | entries: bootinfo::MEMORY_MAP_REQUEST 29 | .get_response() 30 | .expect("Limine failed to obtain a memory map.") 31 | .entries(), 32 | } 33 | } 34 | 35 | pub fn total_memory(&self) -> usize { 36 | self.entries.iter().map(|entry| entry.length).sum::() as usize 37 | } 38 | 39 | pub fn usable_memory(&self) -> UAddr { 40 | self.entries 41 | .iter() 42 | .filter(|entry| entry.entry_type == bootinfo::memory_map::EntryType::USABLE) 43 | .map(|entry| entry.length) 44 | .sum::() 45 | } 46 | 47 | fn highest_address(&self) -> UAddr { 48 | self.entries 49 | .iter() 50 | .map(|entry| entry.base + entry.length) 51 | .max() 52 | .unwrap_or(0) 53 | } 54 | 55 | pub fn iter(&self) -> core::slice::Iter<&bootinfo::memory_map::Entry> { 56 | self.entries.iter() 57 | } 58 | 59 | pub fn find_best_fit(&self, size: UAddr) -> Result<&bootinfo::memory_map::Entry, Error> { 60 | self.entries 61 | .iter() 62 | .filter(|entry| entry.entry_type == bootinfo::memory_map::EntryType::USABLE) 63 | .filter(|entry| entry.length >= size) 64 | .min_by_key(|entry| entry.length) 65 | .ok_or(Error::InsufficientContiguousMemoryAvailable) 66 | .copied() 67 | } 68 | } 69 | 70 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 71 | #[allow(unused)] 72 | pub enum Error { 73 | OutOfMemory, 74 | InsufficientMemoryAvailable, 75 | InsufficientContiguousMemoryAvailable, 76 | MemoryOvercommitted, 77 | AddressMisaligned, 78 | AddressOutOfRange, 79 | InvalidSize, 80 | InvalidAlignment, 81 | } 82 | 83 | enum RegionAvailability { 84 | Available, 85 | Unavailable(PhysicalAddress), 86 | } 87 | 88 | const FRAME_SIZE: UAddr = 4096; 89 | 90 | /// A bitmap based physical frame allocator 91 | pub struct PhysicalFrameAllocator { 92 | bitmap: &'static mut [u8], 93 | } 94 | 95 | impl PhysicalFrameAllocator { 96 | fn new() -> PhysicalFrameAllocator { 97 | let memory_map = MemoryMap::get(); 98 | let total_memory = memory_map.highest_address(); 99 | let bitmap_len = (total_memory / FRAME_SIZE).div_ceil(u8::BITS as u64); 100 | // find a region that is large enough to hold the bitmap 101 | let region = memory_map.find_best_fit(bitmap_len) 102 | .expect("Failed to find a physical memory region large enough to hold the physical frame allocator bitmap"); 103 | 104 | // Initialize bitmap and create PFA 105 | let bitmap_addr = (*DIRECT_MAP + region.base).bits() as *mut u8; 106 | let bitmap = unsafe { 107 | // clear the bitmap to mark all frames as unavailable 108 | bitmap_addr.write_bytes(0xff, bitmap_len as usize); 109 | from_raw_parts_mut(bitmap_addr, bitmap_len as usize) 110 | }; 111 | 112 | let mut pfa = PhysicalFrameAllocator { bitmap }; 113 | 114 | // clear the bits corresponding to available frames 115 | for entry in MemoryMap::get().iter() { 116 | match entry.entry_type { 117 | bootinfo::memory_map::EntryType::USABLE => { 118 | let start = PhysicalAddress::new(entry.base); 119 | let n_frames = entry.length / FRAME_SIZE; 120 | for addr in start.iter_frames(n_frames) { 121 | pfa.clear_by_address(addr); 122 | } 123 | } 124 | _ => { 125 | // for unusable regions (like BAD_MEMORY), ensure the bits are set to 1 (unavailable) 126 | let start = PhysicalAddress::new(entry.base); 127 | let n_frames = entry.length / FRAME_SIZE; 128 | for addr in start.iter_frames(n_frames) { 129 | pfa.set_by_address(addr); 130 | } 131 | } 132 | } 133 | } 134 | 135 | // set the bits corresponding to the bitmap as unavailable 136 | let bitmap_start = PhysicalAddress::new(region.base); 137 | let bitmap_frames = (region.length).div_ceil(FRAME_SIZE); 138 | for addr in bitmap_start.iter_frames(bitmap_frames) { 139 | pfa.set_by_address(addr); 140 | } 141 | 142 | pfa 143 | } 144 | 145 | #[inline] 146 | const fn frame_capacity(&self) -> UAddr { 147 | (self.bitmap.len() * 8) as UAddr 148 | } 149 | 150 | pub fn allocate(&mut self) -> Result { 151 | for (byte_index, byte) in self.bitmap.iter_mut().enumerate() { 152 | let bit_index = byte.trailing_ones() as usize; 153 | if bit_index < 8 { 154 | *byte |= 1 << bit_index; 155 | return Ok(self.index_to_address(byte_index, bit_index)); 156 | } 157 | } 158 | Err(Error::OutOfMemory) 159 | } 160 | 161 | pub fn deallocate(&mut self, frame: PhysicalAddress) -> Result<(), Error> { 162 | if !frame.is_page_aligned() { 163 | return Err(Error::AddressMisaligned); 164 | } 165 | if frame.pfn() >= self.frame_capacity() { 166 | return Err(Error::AddressOutOfRange); 167 | } 168 | self.clear_by_address(frame); 169 | Ok(()) 170 | } 171 | 172 | pub fn allocate_contiguous( 173 | &mut self, 174 | n_frames: UAddr, 175 | alignment: UAddr, 176 | ) -> Result { 177 | //validate inputs 178 | if n_frames == 0 { 179 | return Err(Error::InvalidSize); 180 | } 181 | if !alignment.is_power_of_two() { 182 | return Err(Error::AddressMisaligned); 183 | } 184 | 185 | // if the requested alignment is less than the frame size, then the alignment is the frame size 186 | let corrected_alignment = alignment.max(FRAME_SIZE); 187 | 188 | let mut base = PhysicalAddress::new(0); 189 | while base.pfn() + n_frames < self.frame_capacity() { 190 | match self.check_region(base, n_frames) { 191 | RegionAvailability::Available => { 192 | for addr in base.iter_frames(n_frames) { 193 | self.set_by_address(addr); 194 | } 195 | return Ok(base); 196 | } 197 | RegionAvailability::Unavailable(last_frame) => { 198 | // skip to the next properly aligned address after the last frame in the gap 199 | base = PhysicalAddress::new( 200 | (last_frame.bits() + FRAME_SIZE).next_multiple_of(corrected_alignment), 201 | ); 202 | } 203 | } 204 | } 205 | Err(Error::InsufficientContiguousMemoryAvailable) 206 | } 207 | 208 | pub fn deallocate_contiguous( 209 | &mut self, 210 | base: PhysicalAddress, 211 | n_frames: UAddr, 212 | ) -> Result<(), Error> { 213 | // validate inputs 214 | if n_frames == 0 { 215 | return Err(Error::InvalidSize); 216 | } 217 | if !base.is_page_aligned() { 218 | return Err(Error::AddressMisaligned); 219 | } 220 | if base.pfn() >= self.frame_capacity() || base.pfn() + n_frames >= self.frame_capacity() { 221 | return Err(Error::AddressOutOfRange); 222 | } 223 | 224 | for addr in base.iter_frames(n_frames) { 225 | self.clear_by_address(addr); 226 | } 227 | Ok(()) 228 | } 229 | 230 | fn check_region(&self, base: PhysicalAddress, n_frames: UAddr) -> RegionAvailability { 231 | // search the region in reverse order so that if a gap is found 232 | // the address of the last frame in the gap is returned 233 | // this is useful for the allocate_contiguous method 234 | // if a gap is found, the method can continue searching from after the gap 235 | for address in (base.bits()..(base + n_frames).bits()).rev() { 236 | if self.get_by_address(PhysicalAddress::from(address)) { 237 | return RegionAvailability::Unavailable(PhysicalAddress::from(address)); 238 | } 239 | } 240 | RegionAvailability::Available 241 | } 242 | 243 | fn index_to_address(&self, byte: usize, bit: usize) -> PhysicalAddress { 244 | PhysicalAddress::from_pfn((byte * 8 + bit) as UAddr) 245 | } 246 | 247 | fn address_to_index(&self, address: PhysicalAddress) -> (UAddr, UAddr) { 248 | ((address.pfn() / 8) as UAddr, (address.pfn() % 8) as UAddr) 249 | } 250 | 251 | fn get_by_address(&self, address: PhysicalAddress) -> bool { 252 | let (byte, bit) = self.address_to_index(address); 253 | self.bitmap[byte as usize] & (1 << bit) != 0 254 | } 255 | 256 | fn set_by_address(&mut self, address: PhysicalAddress) { 257 | let (byte, bit) = self.address_to_index(address); 258 | self.bitmap[byte as usize] |= 1 << bit; 259 | } 260 | 261 | fn clear_by_address(&mut self, address: PhysicalAddress) { 262 | let (byte, bit) = self.address_to_index(address); 263 | self.bitmap[byte as usize] &= !(1 << bit); 264 | } 265 | } 266 | -------------------------------------------------------------------------------- /charlotte_core/src/memory/span_printer.rs: -------------------------------------------------------------------------------- 1 | //! Print a span of memory starting at some address with some width 2 | #![allow(clippy::collapsible_else_if)] 3 | 4 | use core::fmt::Write; 5 | 6 | use crate::{log, logln}; 7 | 8 | #[derive(Clone, Copy)] 9 | pub struct MemorySpan { 10 | start: usize, 11 | width: usize, 12 | } 13 | 14 | #[derive(Clone, Copy)] 15 | pub struct MemorySpanIter { 16 | span: MemorySpan, 17 | offset: usize, 18 | } 19 | 20 | impl MemorySpan { 21 | pub fn new(start: usize, width: usize) -> Self { 22 | MemorySpan { start, width } 23 | } 24 | 25 | pub fn print_span(&self, width: usize, raw_dump: bool) { 26 | let mut iter = self.iter(); 27 | let mut idx = 0; 28 | logln!( 29 | "Printing span starting at {:X} {:X} wide", 30 | self.start, 31 | self.width 32 | ); 33 | while let Some(byte) = iter.next() { 34 | if raw_dump { 35 | if idx % width == 0 { 36 | if idx == 0 { 37 | if byte < 15 { 38 | log!("\n0{:X} ", byte); 39 | } else { 40 | log!("\n{:X} ", byte); 41 | } 42 | } else { 43 | if byte < 15 { 44 | log!("\n0{:X} ", byte); 45 | } else { 46 | log!("\n{:X} ", byte); 47 | } 48 | } 49 | } else { 50 | if byte < 15 { 51 | log!("0{:X} ", byte); 52 | } else { 53 | log!("{:X} ", byte); 54 | } 55 | } 56 | } else { 57 | if idx % width == 0 { 58 | if idx == 0 { 59 | if byte < 15 { 60 | log!("{:X} | 0{:X} ", self.start + idx, byte); 61 | } else { 62 | log!("{:X} | {:X} ", self.start + idx, byte); 63 | } 64 | } else { 65 | if byte < 15 { 66 | log!("\n{:X} | 0{:X} ", self.start + idx, byte); 67 | } else { 68 | log!("\n{:X} | {:X} ", self.start + idx, byte); 69 | } 70 | } 71 | } else { 72 | if byte < 15 { 73 | log!("0{:X} ", byte); 74 | } else { 75 | log!("{:X} ", byte); 76 | } 77 | } 78 | } 79 | idx += 1; 80 | } 81 | logln!("\nEND OF SPAN"); 82 | } 83 | 84 | pub fn iter(&self) -> MemorySpanIter { 85 | MemorySpanIter { 86 | span: *self, 87 | offset: 0, 88 | } 89 | } 90 | } 91 | 92 | impl Iterator for MemorySpanIter { 93 | type Item = u8; 94 | 95 | fn next(&mut self) -> Option { 96 | if self.offset <= self.span.width { 97 | let byte = Some(unsafe { *((self.span.start + self.offset) as *const u8) }); 98 | self.offset += 1; 99 | byte 100 | } else { 101 | None 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /limine.conf: -------------------------------------------------------------------------------- 1 | TIMEOUT: 0 2 | SERIAL: yes 3 | 4 | /CharlotteOS 5 | PROTOCOL: limine 6 | KERNEL_PATH: boot():/charlotte_core 7 | KASLR: no 8 | -------------------------------------------------------------------------------- /tools/deprecated/codecheck.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | """Code sanity checker for charlotte_core""" 4 | 5 | import os 6 | import subprocess 7 | import sys 8 | 9 | # When adding a new target, add the target to the TARGETS list 10 | # a target set as "core" will cause the script to raise an error if the target fails 11 | TARGETS = { 12 | "x86_64-unknown-none": "core", 13 | "aarch64-unknown-none": "secondary", 14 | "riscv64gc-unknown-none-elf": "secondary", 15 | } 16 | TARGET_RESULTS = {} 17 | 18 | def run_grep(lookup, filename) -> str: 19 | """Run grep on the file and return the result""" 20 | try: 21 | return subprocess.run( 22 | ["grep", "--recursive", lookup, filename], 23 | stdout=subprocess.PIPE, 24 | check=True, 25 | ).stdout.decode("utf-8") 26 | except subprocess.CalledProcessError: 27 | return "" 28 | 29 | def check_style(): 30 | """Check code style""" 31 | print("Checking code style") 32 | try: 33 | subprocess.run( 34 | [ 35 | "cargo", 36 | "fmt", 37 | "--check", 38 | "--manifest-path", 39 | "charlotte_core/Cargo.toml", 40 | ], 41 | check=True, 42 | stderr=subprocess.PIPE, 43 | stdout=subprocess.PIPE, 44 | ) 45 | except subprocess.CalledProcessError: 46 | print("style issues detected please run 'cargo fmt'") 47 | sys.exit(1) 48 | 49 | print("Code style check passed") 50 | 51 | def check_code(): 52 | """Check the project""" 53 | grep_res = run_grep("allow(unused)", "./charlotte_core/src") 54 | 55 | for target in TARGETS: 56 | print(f"Checking target: {target}") 57 | os.environ["TARGET"] = target 58 | try: 59 | subprocess.run( 60 | [ 61 | "cargo", 62 | "check", 63 | "--target", 64 | target, 65 | "--manifest-path", 66 | "charlotte_core/Cargo.toml", 67 | ], 68 | check=True, 69 | stderr=subprocess.PIPE, 70 | stdout=subprocess.PIPE, 71 | ) 72 | except subprocess.CalledProcessError: 73 | target_result = "Failed" 74 | else: 75 | target_result = "Ok" 76 | TARGET_RESULTS[target] = target_result 77 | 78 | print("\n\nResults:") 79 | print("--------") 80 | if grep_res: 81 | print( 82 | "Unused code warning supression detected! Please check them and remove if not needed." 83 | ) 84 | print("Affected files:") 85 | print(grep_res) 86 | print("Target results:") 87 | for target, result in TARGET_RESULTS.items(): 88 | print(f"{target}: {result}") 89 | 90 | if "Failed" in TARGET_RESULTS.values(): 91 | for target, result in TARGET_RESULTS.items(): 92 | if result == "Failed" and TARGETS[target] == "core": 93 | print( 94 | f"Core target {target} failed to build, please fix the build errors!" 95 | ) 96 | sys.exit(1) 97 | print( 98 | "WARN: Some non core target failed to build" 99 | ) 100 | sys.exit(0) 101 | 102 | print("All checks passed!") 103 | sys.exit(0) 104 | 105 | if __name__ == "__main__": 106 | check_style() 107 | check_code() 108 | -------------------------------------------------------------------------------- /tools/install-requirements.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | ## Functions and constants 4 | ## ----------------------- 5 | 6 | # POSIX ONLY - This script is supposed to run on nearly any distros 7 | # (well at least popular ones) 8 | 9 | # CHANGE THIS when there are new requirements 10 | # WARNING: A package might be named differently on different distributions 11 | required_packages_common="xorriso make curl" 12 | 13 | # Here are the requirements with different names on different distros 14 | required_packages_ubuntu="qemu-system" 15 | required_packages_debian="qemu-system-x86" 16 | required_packages_arch="qemu libisoburn" 17 | required_packages_fedora="qemu" 18 | required_packages_macos="qemu" 19 | 20 | # WARNING: sudo needed 21 | 22 | # Wrapper: 23 | # Print out status of executed command in a human readable way 24 | # Parameters: 25 | # $1 - Label 26 | # $2 - Command 27 | # WARNING: String indexing in POSIX is undefined 28 | wrapper() { 29 | output=$("${@:2}" 2>&1) 30 | if [ $? -eq 0 ] 31 | then 32 | printf "\033[1m[\e[32m Ok \e[0m\033[1m] %s\e[0m\n" "$1" 33 | else 34 | printf "\033[1m[\e[31mFail\e[0m\033[1m] %s\e[0m\n \033[1m\e[31mError\e[0m $output\n" "$1" 35 | return 1 36 | fi 37 | } 38 | 39 | 40 | # Install Rust and Rust nightly toolchain 41 | install_rust_nightly() { 42 | printf "[ ] Rust nightly toolchain:\n" 43 | # Test if the nightly toolchain is not already installed, if it is leave it alone 44 | cd charlotte_core || exit 1 45 | if [ "$(rustc --version | grep nightly)" = '' ]; then 46 | wrapper " Nightly not found, it will be installed now" 47 | wrapper " Download Rust installer" $(curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs >> /tmp/rust-installer.sh) 48 | wrapper " Make Rust installer executable" chmod +x /tmp/rust-installer.sh 49 | wrapper " Install Rust nightly" /tmp/rust-installer.sh -y --profile minimal 50 | wrapper " Remove Rust installer" rm /tmp/rust-installer.sh 51 | else 52 | wrapper " Found nightly rust installed on this system" rustc --version 53 | fi 54 | cd .. || exit 1 55 | } 56 | 57 | 58 | ## Main execution 59 | #---------------- 60 | 61 | CONTINUE="n" 62 | echo "This script is gonna do the following:" 63 | echo " Check if you have rust nightly installed and if not install it." 64 | echo " Install the packages you need to build and run the project. [will use sudo]" 65 | printf "Continue?[y/N]:" 66 | read -r CONTINUE 67 | 68 | if [ "$CONTINUE" != "${CONTINUE,,}" ]; then 69 | exit 1 70 | fi 71 | 72 | 73 | # Detect OS and distribution 74 | if [ "$(uname)" = "Darwin" ]; then 75 | for package in $required_packages_common; do 76 | wrapper "Install \"$package\" package" brew install "$package" 77 | done 78 | install_rust_nightly 79 | 80 | for package in $required_packages_macos; do 81 | wrapper "Install \"$package\" package" brew install "$package" 82 | done 83 | else 84 | # Check for specific Linux distributions 85 | if [ -f /etc/os-release ]; then 86 | . /etc/os-release 87 | if [ -n "$ID" ]; then 88 | case "$ID" in 89 | ubuntu) 90 | for package in $required_packages_common; do 91 | wrapper "Install \"$package\" package" sudo apt-get -y install "$package" 92 | done 93 | install_rust_nightly 94 | 95 | for package in $required_packages_ubuntu; do 96 | wrapper "Install \"$package\" package" sudo apt-get -y install "$package" 97 | done 98 | ;; 99 | debian) 100 | for package in $required_packages_common; do 101 | wrapper "Install \"$package\" package" sudo apt-get -y install "$package" 102 | done 103 | install_rust_nightly 104 | 105 | for package in $required_packages_debian; do 106 | wrapper "Install \"$package\" package" sudo apt-get -y install "$package" 107 | done 108 | ;; 109 | arch) 110 | for package in $required_packages_common; do 111 | wrapper "Install \"$package\" package" sudo pacman -S --noconfirm --needed "$package" 112 | done 113 | install_rust_nightly 114 | 115 | for package in $required_packages_arch; do 116 | wrapper "Install \"$package\" package" sudo pacman -S --noconfirm --needed "$package" 117 | done 118 | ;; 119 | fedora) 120 | for package in $required_packages_common; do 121 | wrapper "Install \"$package\" package" sudo dnf install -y "$package" 122 | done 123 | install_rust_nightly 124 | 125 | for package in $required_packages_fedora; do 126 | wrapper "Install \"$package\" package" sudo dnf install -y "$package" 127 | done 128 | ;; 129 | 130 | *) 131 | printf "\033[1m[\e[31mFail\e[0m\033[1m] Linux distribution is not supported\n" 132 | exit 1 133 | ;; 134 | esac 135 | printf "\033[1m[\e[32m Ok \e[0m\033[1m] Installation done\n" 136 | else 137 | printf "\033[1m[\e[31mFail\e[0m\033[1m] Linux distribution could not be determined\n" 138 | fi 139 | else 140 | printf "\033[1m[\e[31mFail\e[0m\033[1m] Linux distribution could not be determined: missing /etc/os-release\n" 141 | fi 142 | fi -------------------------------------------------------------------------------- /tools/lint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | cargo fmt --check --manifest-path charlotte_core/Cargo.toml 4 | 5 | # TODO: Keep targets updated 6 | cargo check --target x86_64-unknown-none --manifest-path charlotte_core/Cargo.toml 7 | # cargo clippy --target x86_64-unknown-none --manifest-path charlotte_core/Cargo.toml 8 | 9 | grep -rn '#\[allow\(.*\)\]' ./charlotte_core/src/ 10 | --------------------------------------------------------------------------------