├── .clang-format ├── .devcontainer ├── Dockerfile └── devcontainer.json ├── .github └── workflows │ └── codeql.yml ├── .gitignore ├── .gitmodules ├── Firefly-Kernel.code-workspace ├── LICENSE ├── Makefile ├── README.md ├── Screenshots.md ├── docs ├── firefly.bmp ├── firefly.png └── kasan.png ├── firefly ├── kernel │ ├── boot │ │ └── boot_mem.cpp │ ├── console │ │ ├── LICENSE │ │ ├── README.md │ │ ├── console.cpp │ │ ├── gterm.c │ │ ├── image.c │ │ └── term.c │ ├── drivers │ │ ├── ps2.cpp │ │ └── serial.cpp │ ├── graphics │ │ └── framebuffer.cpp │ ├── init │ │ └── init.cpp │ ├── intel64 │ │ ├── acpi │ │ │ └── acpi.cpp │ │ ├── cpu │ │ │ ├── ap │ │ │ │ └── ap.cpp │ │ │ ├── apic │ │ │ │ ├── apic.cpp │ │ │ │ └── ioapic.cpp │ │ │ └── cpu.cpp │ │ ├── gdt │ │ │ ├── gdt.asm │ │ │ ├── gdt.cpp │ │ │ └── tss.cpp │ │ ├── hpet │ │ │ └── hpet.cpp │ │ ├── int │ │ │ ├── interrupt.asm │ │ │ └── interrupt.cpp │ │ ├── paging.cpp │ │ └── pit │ │ │ └── pit.cpp │ ├── kernel.cpp │ ├── logger.cpp │ ├── memory-manager │ │ ├── primary │ │ │ └── primary_phys.cpp │ │ ├── secondary │ │ │ └── heap.cpp │ │ └── virtual │ │ │ └── virtual.cpp │ ├── stubs.cpp │ ├── tasks │ │ └── scheduler.cpp │ ├── timer │ │ └── timer.cpp │ └── trace │ │ ├── sanitizer │ │ ├── kasan.cpp │ │ ├── kasan_methods.cpp │ │ └── ubsan.cpp │ │ ├── strace.cpp │ │ └── symbols.cpp ├── libk++ │ ├── cstring.cpp │ ├── fmt.cpp │ ├── memory.cpp │ └── utils.cpp └── meson.build ├── fonts ├── unifont.bin ├── vgafont.bin └── vgafont.obj ├── include ├── cstdlib │ ├── cassert.h │ ├── cmath.cpp │ └── cmath.h ├── firefly │ ├── boot │ │ └── boot_mem.hpp │ ├── compiler │ │ └── compiler.hpp │ ├── console │ │ ├── console.hpp │ │ ├── gterm.h │ │ ├── image.h │ │ └── term.h │ ├── drivers │ │ ├── ports.hpp │ │ ├── ps2.hpp │ │ └── serial.hpp │ ├── graphics │ │ └── framebuffer.hpp │ ├── intel64 │ │ ├── acpi │ │ │ ├── acpi.hpp │ │ │ ├── acpi_rsdp.hpp │ │ │ ├── acpi_table.hpp │ │ │ └── tables │ │ │ │ ├── all.hpp │ │ │ │ ├── bgrt.hpp │ │ │ │ ├── hpet.hpp │ │ │ │ └── madt.hpp │ │ ├── cache.hpp │ │ ├── cpu │ │ │ ├── ap │ │ │ │ └── ap.hpp │ │ │ ├── apic │ │ │ │ └── apic.hpp │ │ │ └── cpu.hpp │ │ ├── gdt │ │ │ ├── gdt.hpp │ │ │ └── tss.hpp │ │ ├── hpet │ │ │ └── hpet.hpp │ │ ├── int │ │ │ └── interrupt.hpp │ │ ├── page_flags.hpp │ │ ├── page_permissions.hpp │ │ ├── paging.hpp │ │ └── pit │ │ │ └── pit.hpp │ ├── kernel.hpp │ ├── limine.hpp │ ├── logger.hpp │ ├── memory-manager │ │ ├── allocator.hpp │ │ ├── mm.hpp │ │ ├── page.hpp │ │ ├── primary │ │ │ ├── buddy.hpp │ │ │ ├── page_frame.hpp │ │ │ └── primary_phys.hpp │ │ ├── secondary │ │ │ ├── heap.hpp │ │ │ └── slab │ │ │ │ └── slab.hpp │ │ ├── stack.hpp │ │ └── virtual │ │ │ ├── virtual.hpp │ │ │ └── vspace.hpp │ ├── panic.hpp │ ├── tasks │ │ ├── context.hpp │ │ ├── scheduler.hpp │ │ └── task.hpp │ ├── timer │ │ └── timer.hpp │ └── trace │ │ ├── sanitizer │ │ └── kasan.hpp │ │ ├── strace.hpp │ │ └── symbols.hpp ├── libk++ │ ├── align.h │ ├── bits.h │ ├── cstring.hpp │ ├── fmt.hpp │ ├── memory.hpp │ └── utils.hpp └── meson.build ├── limine.cfg ├── linkage └── linker_x86_64.ld ├── meson.build ├── meson_config.txt ├── meson_options.txt └── scripts ├── geniso.sh ├── gensym.sh ├── qemu-bios-int-log.sh ├── qemu-bios.sh ├── qemu-gdb.sh ├── qemu-log.sh ├── qemu-uefi.sh └── symbol_table_x86_64.py /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | BasedOnStyle: Google 3 | AlignEscapedNewlinesLeft: 'true' 4 | AlignTrailingComments: 'true' 5 | AllowShortBlocksOnASingleLine: 'false' 6 | AllowShortFunctionsOnASingleLine: None 7 | AllowShortIfStatementsOnASingleLine: 'true' 8 | AllowShortLoopsOnASingleLine: 'false' 9 | AllowShortCaseLabelsOnASingleLine: 'false' 10 | AlwaysBreakTemplateDeclarations: 'true' 11 | AlignAfterOpenBracket: Align 12 | BinPackArguments: 'true' 13 | BinPackParameters: 'true' 14 | BreakBeforeBinaryOperators: 'false' 15 | BreakBeforeBraces: Attach 16 | BreakBeforeTernaryOperators: 'true' 17 | BreakConstructorInitializersBeforeComma: 'false' 18 | ConstructorInitializerAllOnOneLineOrOnePerLine: 'false' 19 | Cpp11BracedListStyle: 'false' 20 | ColumnLimit: 0 21 | DerivePointerAlignment: 'true' 22 | DisableFormat: 'false' 23 | IndentCaseLabels: 'true' 24 | Language: Cpp 25 | MaxEmptyLinesToKeep: '2' 26 | NamespaceIndentation: None 27 | SpaceBeforeAssignmentOperators: 'true' 28 | SpaceBeforeParens: ControlStatements 29 | SpaceInEmptyParentheses: 'false' 30 | SpacesInAngles: 'false' 31 | SpacesInCStyleCastParentheses: 'false' 32 | SpacesInContainerLiterals: 'true' 33 | SpacesInParentheses: 'false' 34 | Standard: Cpp11 35 | UseTab: false 36 | IndentWidth: 4 37 | AccessModifierOffset: -4 38 | -------------------------------------------------------------------------------- /.devcontainer/Dockerfile: -------------------------------------------------------------------------------- 1 | # See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.191.0/containers/cpp/.devcontainer/base.Dockerfile 2 | 3 | # [Choice] Debian / Ubuntu version: debian-11, debian-10, debian-9, ubuntu-20.04, ubuntu-18.04 4 | ARG VARIANT="buster" 5 | FROM mcr.microsoft.com/vscode/devcontainers/cpp:0-${VARIANT} 6 | 7 | # [Optional] Uncomment this section to install additional packages. 8 | RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ 9 | && apt-get -y install --no-install-recommends \ 10 | xorriso \ 11 | grub2-common \ 12 | clang \ 13 | nasm \ 14 | qemu-system-x86 \ 15 | grub-pc-bin \ 16 | lld 17 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | // For format details, see https://aka.ms/devcontainer.json. For config options, see the README at: 2 | // https://github.com/microsoft/vscode-dev-containers/tree/v0.191.0/containers/cpp 3 | { 4 | "name": "C++", 5 | "build": { 6 | "dockerfile": "Dockerfile", 7 | // Update 'VARIANT' to pick an Debian / Ubuntu OS version: debian-11, debian-10, debian-9, ubuntu-20.04, ubuntu-18.04 8 | "args": { "VARIANT": "ubuntu-20.04" } 9 | }, 10 | "runArgs": [ "--cap-add=SYS_PTRACE", "--security-opt", "seccomp=unconfined"], 11 | 12 | // Set *default* container specific settings.json values on container create. 13 | "settings": {}, 14 | 15 | // Add the IDs of extensions you want installed when the container is created. 16 | "extensions": [ 17 | "ms-vscode.cpptools", 18 | "ms-vscode.makefile-tools", 19 | "github.copilot", 20 | "ms-vscode.cpptools-extension-pack", 21 | "ms-vscode.cmake-tools", 22 | "eamodio.gitlens", 23 | "webfreak.debug" 24 | ], 25 | 26 | // Use 'forwardPorts' to make a list of ports inside the container available locally. 27 | // "forwardPorts": [], 28 | 29 | // Use 'postCreateCommand' to run commands after the container is created. 30 | // "postCreateCommand": "gcc -v", 31 | 32 | // Comment out connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root. 33 | "remoteUser": "vscode" 34 | } -------------------------------------------------------------------------------- /.github/workflows/codeql.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ "master" ] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: [ "master" ] 20 | schedule: 21 | - cron: '18 3 * * 6' 22 | 23 | jobs: 24 | analyze: 25 | name: Analyze 26 | runs-on: ubuntu-latest 27 | permissions: 28 | actions: read 29 | contents: read 30 | security-events: write 31 | 32 | strategy: 33 | fail-fast: false 34 | matrix: 35 | language: [ 'cpp', 'python' ] 36 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] 37 | # Use only 'java' to analyze code written in Java, Kotlin or both 38 | # Use only 'javascript' to analyze code written in JavaScript, TypeScript or both 39 | # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support 40 | 41 | steps: 42 | - name: Checkout repository 43 | uses: actions/checkout@v3 44 | 45 | # Initializes the CodeQL tools for scanning. 46 | - name: Initialize CodeQL 47 | uses: github/codeql-action/init@v2 48 | with: 49 | languages: ${{ matrix.language }} 50 | # If you wish to specify custom queries, you can do so here or in a config file. 51 | # By default, queries listed here will override any specified in a config file. 52 | # Prefix the list here with "+" to use these queries and those in the config file. 53 | 54 | # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs 55 | # queries: security-extended,security-and-quality 56 | 57 | 58 | # ℹ️ Command-line programs to run using the OS shell. 59 | # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun 60 | 61 | # If the Autobuild fails above, remove it and uncomment the following three lines. 62 | # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. 63 | - run: | 64 | echo "Installing dependencies" 65 | sudo apt install ninja-build nasm clang lld python3.11 git 66 | 67 | - run: | 68 | echo "Installing meson version 0.60.1" 69 | wget https://github.com/mesonbuild/meson/releases/download/0.60.1/meson-0.60.1.tar.gz 70 | tar -xf meson-0.60.1.tar.gz 71 | cd meson-0.60.1/ 72 | ./packaging/create_zipapp.py --outfile meson.pyz --interpreter '/usr/bin/env python3' 73 | chmod +x ./meson.pyz 74 | 75 | # TODO: Some submodules use ssh which won't work since codeQL doesn't have it setup. 76 | # - run: | 77 | # echo "Installing submodules" 78 | # git submodule init && git submodule update 79 | 80 | - run: | 81 | echo "Installing submodules the hacky way (refer to codeql.yml for details)" 82 | cd include/ 83 | git clone https://github.com/FireflyOS/cxxshim.git 84 | git clone https://github.com/FireflyOS/frigg.git 85 | 86 | - run: | 87 | echo "Build kernel" 88 | ./meson-0.60.1/meson.pyz build --cross-file meson_config.txt && cd build && ../meson-0.60.1/meson.pyz compile 89 | 90 | - name: Perform CodeQL Analysis 91 | uses: github/codeql-action/analyze@v2 92 | with: 93 | category: "/language:${{matrix.language}}" 94 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Symbol tables 5 | /parsed_x86_64.sym 6 | /scripts/parsed_x86_64.sym 7 | 8 | # Compiled Object files 9 | *.o 10 | 11 | # Precompiled Headers 12 | *.gch 13 | *.pch 14 | 15 | # Fortran module files 16 | *.mod 17 | *.smod 18 | 19 | # Compiled Static libraries 20 | *.lai 21 | *.la 22 | *.a 23 | *.lib 24 | 25 | # Executables 26 | *.elf 27 | 28 | # Binaries 29 | *.iso 30 | 31 | # Buildsystem 32 | /build/ 33 | /builddir/ 34 | .idea 35 | /builddir/ 36 | /.cache/ 37 | /compile/* 38 | 39 | # IDE 40 | /.vscode/*.log 41 | /.vscode/c_cpp_properties.json 42 | /.vscode/* -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "limine"] 2 | path = limine 3 | url = https://github.com/limine-bootloader/limine.git 4 | branch = v3.0-branch-binary 5 | [submodule "include/frigg"] 6 | path = include/frigg 7 | url = git@github.com:FireflyOS/frigg.git 8 | branch = master 9 | [submodule "include/cxxshim"] 10 | path = include/cxxshim 11 | url = git@github.com:FireflyOS/cxxshim.git 12 | branch = master -------------------------------------------------------------------------------- /Firefly-Kernel.code-workspace: -------------------------------------------------------------------------------- 1 | { 2 | "folders": [ 3 | { 4 | "path": "." 5 | } 6 | ], 7 | "settings": { 8 | "files.associations": { 9 | "*.dproj": "xml", 10 | "*.groupproj": "xml", 11 | "3ds.h": "c", 12 | "synchronization.h": "c", 13 | "gpu.h": "c", 14 | "brew_bgr.h": "c", 15 | "*.tcc": "cpp", 16 | "istream": "cpp", 17 | "ostream": "cpp", 18 | "memory": "cpp", 19 | "exception": "cpp", 20 | "initializer_list": "cpp", 21 | "new": "cpp", 22 | "type_traits": "cpp", 23 | "utility": "cpp", 24 | "gfx.h": "c", 25 | "cctype": "cpp", 26 | "clocale": "cpp", 27 | "cmath": "cpp", 28 | "cstdint": "cpp", 29 | "cstdio": "cpp", 30 | "cstdlib": "cpp", 31 | "cwchar": "cpp", 32 | "cwctype": "cpp", 33 | "iosfwd": "cpp", 34 | "iostream": "cpp", 35 | "limits": "cpp", 36 | "stdexcept": "cpp", 37 | "streambuf": "cpp", 38 | "string": "cpp", 39 | "string_view": "cpp", 40 | "system_error": "cpp", 41 | "typeinfo": "cpp" 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # Quick & dirty makefile to automate the process of typing all these commands/ 3 | # Note: This is specific to any shell that supports ';' as a subcommand separator, it is NOT portable. 4 | # See: https://lists.gnu.org/archive/html/help-make/2008-05/msg00047.html 5 | # 6 | all: 7 | ifeq ($(wildcard ./build),) 8 | make -C limine/ 9 | meson build --cross-file meson_config.txt 10 | endif 11 | cd build; \ 12 | meson compile && \ 13 | ../scripts/geniso.sh 14 | 15 | run: 16 | cd build; \ 17 | ../scripts/qemu-bios.sh 18 | 19 | uefi: 20 | cd build; \ 21 | ../scripts/qemu-uefi.sh 22 | 23 | gdb: 24 | cd build; \ 25 | ../scripts/qemu-gdb.sh 26 | 27 | info_log: 28 | cd build; \ 29 | ../scripts/qemu-bios-int-log.sh 30 | 31 | clean: 32 | ifeq ($(wildcard ./build),) 33 | $(error No build directory to clean, please run 'make all' or 'make' first) 34 | endif 35 | cd build; \ 36 | ninja clean 37 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Firefly-Kernel 2 | Kernel for FireflyOS which can be booted on UEFI and BIOS. ([Screenshots](Screenshots.md)) 3 | 4 | ## A note to the reader: 5 | Are you interested in writing a kernel but lack a "study buddy" or don't know where to get started? 6 | 7 | This project is for you! 8 | 9 | Our goal is to build an operating system where everyone can participate and learn; both newbies and professionals alike. 10 | 11 | Join us on Discord if you'd like to talk: https://discord.gg/sfsdhXs8wA (Channel: #cursed-firefly-dev) 12 | 13 | 14 | ### Clone the repo 15 | * `git clone https://github.com/FireflyOS/Firefly-Kernel --recursive` 16 | * `cd Firefly-Kernel` 17 | 18 | ## Ubuntu 19 | 20 | ```bash 21 | sudo apt install meson ninja-build nasm xorriso qemu-system-x86 clang lld ovmf #For UEFI emulation only 22 | ``` 23 | 24 | Firefly OS uses the meson build system: 25 | ```bash 26 | # Note: We invoke the meson build commands using regular Makefiles as a QoL improvement 27 | make all run # Alternatively you can use 'make all uefi' for uefi emulation 28 | 29 | # "I want to execute the build commands manually!" - Here you go: 30 | make -C limine/ 31 | meson build --cross-file meson_config.txt # You *must* use build, other scripts depend on this directory name 32 | cd build 33 | meson compile && ../scripts/geniso.sh && ../scripts/qemu-bios.sh # If meson compile is not supported you can either upgrade meson or use ninja 34 | ``` 35 | Note: It is assumed you have meson version `0.60.1` or higher, you may or may not run into problems with older versions. 36 | -------------------------------------------------------------------------------- /Screenshots.md: -------------------------------------------------------------------------------- 1 | ## Normal boot: 2 | ![Firefly OS](docs/firefly.png) 3 | 4 | ## Kasan: 5 | ![Firefly OS kasan](docs/kasan.png) 6 | -------------------------------------------------------------------------------- /docs/firefly.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FireflyOS/Firefly-Kernel/440588f7535fcfa1890bbb79c6d55af05d4c9fc9/docs/firefly.bmp -------------------------------------------------------------------------------- /docs/firefly.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FireflyOS/Firefly-Kernel/440588f7535fcfa1890bbb79c6d55af05d4c9fc9/docs/firefly.png -------------------------------------------------------------------------------- /docs/kasan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FireflyOS/Firefly-Kernel/440588f7535fcfa1890bbb79c6d55af05d4c9fc9/docs/kasan.png -------------------------------------------------------------------------------- /firefly/kernel/boot/boot_mem.cpp: -------------------------------------------------------------------------------- 1 | #include "firefly/intel64/cpu/cpu.hpp" 2 | #include "firefly/intel64/page_flags.hpp" 3 | #include "firefly/intel64/page_permissions.hpp" 4 | #include "firefly/memory-manager/primary/buddy.hpp" 5 | #include "firefly/panic.hpp" 6 | #include "libk++/align.h" 7 | 8 | namespace firefly::boot { 9 | using namespace kernel; 10 | using namespace core::paging; 11 | 12 | BuddyAllocator pageAllocator{}; 13 | 14 | // Index within the Pml (0-511) 15 | inline int64_t getPmlOffset(const uint64_t virtual_addr, const int depth) { 16 | // Dissect the virtual address by retrieving 9 bits starting at `depth - 1` 17 | return (virtual_addr >> (PAGE_SHIFT + (9 * (depth - 1)))) & 0x1FF; // We subtract 1 from idx so that we don't have to input idx 0-3, but rather 1-4 18 | } 19 | 20 | inline uint64_t *allocatePageTable(uint64_t size = PageSize::Size4K) { 21 | uint64_t *ptr = pageAllocator.alloc(4096).unpack(); 22 | 23 | if (!ptr) 24 | firefly::panic("Unable to allocate memory for a page-table"); 25 | 26 | return ptr; 27 | } 28 | 29 | void traverse_page_tables(const uint64_t virtual_addr, const uint64_t physical_addr, const int access_flags, uint64_t *pml_ptr, const PageSize page_size = PageSize::Size4K) { 30 | auto idx4 = getPmlOffset(virtual_addr, 4); 31 | auto idx3 = getPmlOffset(virtual_addr, 3); 32 | auto idx2 = getPmlOffset(virtual_addr, 2); 33 | auto idx1 = getPmlOffset(virtual_addr, 1); 34 | 35 | if (!(pml_ptr[idx4] & 1)) { 36 | uint64_t *ptr = allocatePageTable(); 37 | pml_ptr[idx4] = reinterpret_cast(ptr); 38 | pml_ptr[idx4] |= access_flags; 39 | } 40 | auto pml3 = reinterpret_cast(pml_ptr[idx4] & ~(511)); 41 | if (page_size == PageSize::Size1G) { 42 | pml3[idx3] = (physical_addr | access_flags | BIT(7)); 43 | return; 44 | } 45 | if (!(pml3[idx3] & 1)) { 46 | const auto ptr = allocatePageTable(); 47 | pml3[idx3] = reinterpret_cast(ptr); 48 | pml3[idx3] |= access_flags; 49 | } 50 | auto pml2 = reinterpret_cast(pml3[idx3] & ~(511)); 51 | if (page_size == PageSize::Size2M) { 52 | pml2[idx2] = (physical_addr | access_flags | BIT(7)); 53 | return; 54 | } 55 | 56 | if ((pml2[idx2] & 1) == 0) { 57 | const auto ptr = allocatePageTable(); 58 | pml2[idx2] = reinterpret_cast(ptr); 59 | pml2[idx2] |= access_flags; 60 | } 61 | 62 | const auto pml1 = reinterpret_cast(pml2[idx2] & ~(511)); 63 | pml1[idx1] = (physical_addr | access_flags); 64 | } 65 | 66 | void map(uint64_t virtual_addr, uint64_t physical_addr, AccessFlags access_flags, const uint64_t *pml_ptr, const PageSize page_size) { 67 | traverse_page_tables(virtual_addr, physical_addr, static_cast(access_flags), const_cast(pml_ptr), page_size); 68 | asm volatile("invlpg %0" ::"m"(virtual_addr) 69 | : "memory"); 70 | } 71 | 72 | // Map some memory for the pagelist (page.hpp) 73 | // This is required for the Physical memory manager to function. 74 | void bootMapExtraRegion(limine_memmap_response *mmap) { 75 | constexpr int required_size = 4; 76 | 77 | for (uint64_t i = 0; i < mmap->entry_count; i++) { 78 | auto e = mmap->entries[i]; 79 | if (e->type != LIMINE_MEMMAP_USABLE || e->length < MiB(required_size)) 80 | continue; 81 | 82 | pageAllocator.init(BuddyAllocator::AddressType(e->base), 22); 83 | e->base = libkern::align_up4k(e->base + MiB(required_size)); 84 | e->length -= libkern::align_down4k(MiB(required_size)); 85 | break; 86 | } 87 | 88 | // clang-format off 89 | auto cr3{ 0ul }; 90 | asm volatile("mov %%cr3, %0": "=r"(cr3)); 91 | // clang-format on 92 | 93 | if (cpuHugePages()) { 94 | map(AddressLayout::PageData, 0, AccessFlags::ReadWrite, reinterpret_cast(cr3), PageSize::Size1G); 95 | } else { 96 | for (uint32_t i = 0; i < GiB(1); i += PageSize::Size2M) { 97 | map(i + AddressLayout::PageData, i, AccessFlags::ReadWrite, reinterpret_cast(cr3), PageSize::Size2M); 98 | } 99 | } 100 | } 101 | 102 | 103 | } // namespace firefly::boot 104 | -------------------------------------------------------------------------------- /firefly/kernel/console/LICENSE: -------------------------------------------------------------------------------- 1 | BSD 2-Clause License 2 | 3 | Copyright (c) 2019, 2020, 2021, 2022, mintsuki and contributors 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | -------------------------------------------------------------------------------- /firefly/kernel/console/README.md: -------------------------------------------------------------------------------- 1 | # Port of the Limine bootloader terminal 2 | 3 | Normally this terminal is provided by the Limine bootloader and can (and should) be used by the kernel during early boot. 4 | 5 | It's an extremely fast with a complete "terminfo"/vt100 implementation. There really isn't a reason not to use this terminal. 6 | 7 | The only issue here is that it's merely an *early boot console*, and it's located in conventional, lower-half memory. 8 | 9 | Once you get to userspace you'll find it very annoying to try and map memory *around* the it considering that terminal shouldn't be in lower-half memory in the first place! 10 | 11 | That's what this port is for. 12 | You should be able to include it into your kernel and use it just fine. 13 | 14 | Please let us know if any issues arise, thank you! 15 | 16 | ## Features 17 | * Every feature that Limine terminal has is supported 18 | * Multiple terminals support 19 | 20 | ## Usage 21 | 22 | 1. First off, choose a font from fonts/ folder or create your own and load it in your os (link it directly to the kernel, load it from filesystem, as a module, etc) 23 | 24 | 2. To initialize the terminal, include `term.h` and provide some basic functions declared in the header file (Example shown below) 25 | 26 | 3. Create new term_t and run `term_init(term, arguments);` (If you set bios to false, you will not be able to use text mode) 27 | 28 | 4. To use vbe mode with framebuffer, run `term_vbe(term, arguments);` (Example shown below) 29 | 30 | 5. To use text mode, run `term_textmode(term);` 31 | 32 | Note: There also are C++ wrappers for term_t and image_t structures (cppterm_t and cppimage_t) in `source/cpp/` directory 33 | 34 | ## Example 35 | ```c 36 | #include "term.h" 37 | 38 | void *alloc_mem(size_t size) 39 | { 40 | // Allocate memory 41 | // memset() memory to zero 42 | } 43 | void free_mem(void *ptr, size_t size) 44 | { 45 | // Free memory 46 | } 47 | void *memcpy(void *dest, const void *src, size_t len) 48 | { 49 | // Memcpy 50 | } 51 | void *memset(void *dest, int ch, size_t n) 52 | { 53 | // Memset 54 | } 55 | 56 | void callback(uint64_t, uint64_t, uint64_t, uint64_t, uint64_t) 57 | { 58 | handleCallback(); 59 | } 60 | 61 | struct framebuffer_t frm = { 62 | address, // Framebuffer address 63 | width, // Framebuffer width 64 | height, // Framebuffer height 65 | pitch // Framebuffer pitch 66 | }; 67 | 68 | struct font_t font = { 69 | font_address, // Address of font file 70 | 8, // Font width 71 | 16, // Font height 72 | 1, // Character spacing 73 | 0, // Font scaling x 74 | 0 // Font scaling y 75 | }; 76 | 77 | struct style_t style = { 78 | DEFAULT_ANSI_COLOURS, // Default terminal palette 79 | DEFAULT_ANSI_BRIGHT_COLOURS, // Default terminal bright palette 80 | 0xA0000000, // Background colour 81 | 0xFFFFFF, // Foreground colour 82 | 64, // Terminal margin 83 | 0 // Terminal margin gradient 84 | }; 85 | 86 | struct image_t image; 87 | image_open(&image, bmpBackgroundAddress, size); 88 | struct background_t back = { 89 | &image, // Background. Set to NULL to disable background 90 | STRETCHED, // STRETCHED, CENTERED or TILED 91 | 0x00000000 // Terminal backdrop colour 92 | }; 93 | 94 | struct term_t term; 95 | term_init(&term, callback, bootedInBiosMode); 96 | 97 | // VBE mode 98 | // In VBE mode you can create more terminals for different framebuffers 99 | term_vbe(&term, frm, font, style, back); 100 | term_print(&term, "Hello, World!"); 101 | 102 | // Text mode 103 | term_textmode(&term); 104 | term_print(&term, "Hello, World!"); 105 | ``` 106 | 107 | Based on: https://github.com/limine-bootloader/limine 108 | -------------------------------------------------------------------------------- /firefly/kernel/console/console.cpp: -------------------------------------------------------------------------------- 1 | #include "firefly/console/console.hpp" 2 | 3 | #include 4 | #include 5 | 6 | #include "firefly/compiler/compiler.hpp" 7 | #include "firefly/console/term.h" 8 | #include "firefly/graphics/framebuffer.hpp" 9 | #include "firefly/intel64/acpi/acpi.hpp" 10 | #include "firefly/limine.hpp" 11 | #include "firefly/logger.hpp" 12 | #include "firefly/memory-manager/virtual/virtual.hpp" 13 | #include "libk++/align.h" 14 | 15 | extern uintptr_t _binary_fonts_vgafont_bin_start[]; 16 | 17 | namespace { 18 | frg::ticket_spinlock console_lock = frg::ticket_spinlock(); 19 | } 20 | 21 | namespace firefly::kernel::console { 22 | 23 | USED struct limine_framebuffer_request fb { 24 | .id = LIMINE_FRAMEBUFFER_REQUEST, .revision = 0, .response = nullptr 25 | }; 26 | 27 | // TODO: Support multiple terminals. 28 | // A simple frg::array should do the trick. 29 | // We could even have different terminal background images. 30 | // Implementation: A config file can be passed as a bootloader module and parsed here. 31 | term_t term; 32 | 33 | background_t loadTerminalSplash(); 34 | 35 | void init() { 36 | auto fb_response = fb.response; 37 | if (unlikely(!fb_response)) 38 | return; 39 | 40 | struct framebuffer_t frm = { 41 | (uintptr_t)fb_response->framebuffers[0]->address, // Framebuffer address 42 | fb_response->framebuffers[0]->width, // Framebuffer width 43 | fb_response->framebuffers[0]->height, // Framebuffer height 44 | fb_response->framebuffers[0]->pitch // Framebuffer pitch 45 | }; 46 | 47 | // TODO: Extend the fbDev class to the C++ file, not just header only. 48 | // It's best if fbDev initializes the console, there should also be a frg::vector (requires a slab allocator) 49 | // for every console / framebuffer / instance of fbDev since limine supports multiple framebuffers now 50 | fbDev.init(PhysicalAddress(frm.address), frm.width, frm.height, frm.pitch); 51 | 52 | auto font_addr = (uintptr_t)_binary_fonts_vgafont_bin_start; 53 | struct font_t font = { 54 | font_addr, // Address of font file 55 | 8, // Font width 56 | 16, // Font height 57 | 1, // Character spacing 58 | 0, // Font scaling x 59 | 0 // Font scaling y 60 | }; 61 | 62 | struct style_t style = { 63 | DEFAULT_ANSI_COLOURS, // Default terminal palette 64 | DEFAULT_ANSI_BRIGHT_COLOURS, // Default terminal bright palette 65 | 0xA0000000, // Background colour 66 | 0xAAAAAA, // Foreground colour 67 | 0, // Terminal margin 68 | 0 // Terminal margin gradient 69 | }; 70 | 71 | term_init(&term, nullptr); 72 | 73 | // Try to load the firmware bootsplash as the terminal background. 74 | // If we can't find it we default to black 75 | auto const bgrt = reinterpret_cast(core::acpi::Acpi::accessor().find("BGRT")); 76 | 77 | if (bgrt && bgrt->valid()) { 78 | // Bitmap header 79 | struct PACKED { 80 | uint16_t id; 81 | uint32_t size; 82 | } hdr; 83 | memcpy(&hdr, (const void *)bgrt->imageAddress, sizeof(decltype(hdr))); 84 | 85 | struct image_t img; 86 | image_open(&img, bgrt->imageAddress, hdr.size); 87 | struct background_t back = { 88 | &img, 89 | CENTERED, 90 | 0 91 | }; 92 | term_vbe(&term, frm, font, style, back); 93 | } else { 94 | term_vbe(&term, frm, font, style, {}); 95 | } 96 | } 97 | 98 | [[gnu::no_sanitize_address]] void write(const char *str) { 99 | console_lock.lock(); 100 | if (likely(term.initialised)) 101 | term_print(&term, str); 102 | console_lock.unlock(); 103 | } 104 | 105 | } // namespace firefly::kernel::console 106 | -------------------------------------------------------------------------------- /firefly/kernel/console/image.c: -------------------------------------------------------------------------------- 1 | #include "firefly/console/image.h" 2 | #include "firefly/console/term.h" 3 | 4 | #define DIV_ROUNDUP(A, B) \ 5 | ({ \ 6 | typeof(A) _a_ = A; \ 7 | typeof(B) _b_ = B; \ 8 | (_a_ + (_b_ - 1)) / _b_; \ 9 | }) 10 | 11 | #define ALIGN_UP(A, B) \ 12 | ({ \ 13 | typeof(A) _a__ = A; \ 14 | typeof(B) _b__ = B; \ 15 | DIV_ROUNDUP(_a__, _b__) * _b__; \ 16 | }) 17 | 18 | bool bmp_open_image(struct image_t *image, uint64_t file, uint64_t size) 19 | { 20 | struct bmp_header header; 21 | memset(&header, 0, sizeof(struct bmp_header)); 22 | memcpy(&header, (uint8_t*)file, sizeof(struct bmp_header)); 23 | 24 | char signature[2] = { 25 | (char)(header.bf_signature & 0xFF), 26 | (char)(header.bf_signature >> 8) 27 | }; 28 | if (signature[0] != 'B' || signature[1] != 'M') return false; 29 | 30 | if (header.bi_bpp % 8 != 0) return false; 31 | 32 | uint32_t bf_size; 33 | if (header.bf_offset + header.bf_size > size) 34 | bf_size = size - header.bf_offset; 35 | else bf_size = header.bf_size; 36 | 37 | image->img = alloc_mem(bf_size); 38 | memcpy(image->img, (uint8_t*)(file + header.bf_offset), bf_size); 39 | 40 | image->allocated_size = bf_size; 41 | 42 | image->x_size = header.bi_width; 43 | image->y_size = header.bi_height; 44 | image->pitch = ALIGN_UP(header.bi_width * header.bi_bpp, 32) / 8; 45 | image->bpp = header.bi_bpp; 46 | image->img_width = header.bi_width; 47 | image->img_height = header.bi_height; 48 | 49 | return true; 50 | } 51 | 52 | void image_make_centered(struct image_t *image, int frame_x_size, int frame_y_size, uint32_t back_colour) 53 | { 54 | image->type = CENTERED; 55 | 56 | image->x_displacement = frame_x_size / 2 - image->x_size / 2; 57 | image->y_displacement = frame_y_size / 2 - image->y_size / 2; 58 | image->back_colour = back_colour; 59 | } 60 | 61 | void image_make_stretched(struct image_t *image, int new_x_size, int new_y_size) 62 | { 63 | image->type = STRETCHED; 64 | 65 | image->x_size = new_x_size; 66 | image->y_size = new_y_size; 67 | } 68 | 69 | bool image_open(struct image_t *image, uint64_t file, uint64_t size) 70 | { 71 | image->type = TILED; 72 | if (bmp_open_image(image, file, size)) 73 | return true; 74 | return false; 75 | } 76 | 77 | void image_close(struct image_t *image) 78 | { 79 | free_mem(image->img, image->allocated_size); 80 | } -------------------------------------------------------------------------------- /firefly/kernel/drivers/ps2.cpp: -------------------------------------------------------------------------------- 1 | #include "firefly/drivers/ps2.hpp" 2 | 3 | #include 4 | #include 5 | 6 | #include "firefly/compiler/compiler.hpp" 7 | #include "firefly/console/console.hpp" 8 | #include "firefly/intel64/cpu/apic/apic.hpp" 9 | #include "firefly/intel64/cpu/cpu.hpp" 10 | #include "firefly/intel64/int/interrupt.hpp" 11 | 12 | namespace firefly::drivers::ps2 { 13 | using namespace firefly::kernel::io; 14 | 15 | constexpr short data_port = 0x60; 16 | constexpr short status_register = 0x64; 17 | // constexpr short command_register = 0x64; 18 | 19 | enum keys : unsigned char { 20 | backspace = 0x0E, 21 | enter = 0x1C, 22 | lshift = 0x2A, 23 | caps_lock = 0x3A, 24 | max_stdin_length = 255, 25 | }; 26 | 27 | /** 28 | * Buffer for CIN characters, limited at 255 29 | */ 30 | static frg::array stdin = {}; 31 | 32 | /** 33 | * Current stdin write index 34 | */ 35 | static size_t stdin_index = 0; 36 | 37 | /** 38 | * True if shift or caps lock is active, false if not 39 | */ 40 | bool shift_pressed = false; 41 | 42 | /** 43 | * Array for key names 44 | */ 45 | static USED frg::array sc_name = { "ERROR", "Esc", "1", "2", "3", "4", "5", "6", 46 | "7", "8", "9", "0", "-", "=", "Backspace", "Tab", "Q", "W", "E", 47 | "R", "T", "Y", "U", "I", "O", "P", "[", "]", "Enter", "Lctrl", 48 | "A", "S", "D", "F", "G", "H", "J", "K", "L", ";", "'", "`", 49 | "LShift", "\\", "Z", "X", "C", "V", "B", "N", "M", ",", ".", 50 | "/", "RShift", "Keypad *", "LAlt", "Spacebar" }; 51 | 52 | /** 53 | * Array for ascii key values 54 | */ 55 | static frg::array sc_ascii = { '?', '?', '1', '2', '3', '4', '5', '6', 56 | '7', '8', '9', '0', '-', '=', '?', '?', 'q', 'w', 'e', 'r', 't', 'y', 57 | 'u', 'i', 'o', 'p', '[', ']', '?', '?', 'a', 's', 'd', 'f', 'g', 58 | 'h', 'j', 'k', 'l', ';', '\'', '`', '?', '\\', 'z', 'x', 'c', 'v', 59 | 'b', 'n', 'm', ',', '.', '/', '?', '?', '?', ' ' }; 60 | 61 | /** 62 | * Enum used for io port statuses 63 | */ 64 | enum status : short { 65 | // 1 : data to be read 66 | out_buffer_status = 0b00000001, 67 | // 0 : data may be written 68 | in_buffer_status = 0b00000010, 69 | // 1 : passes POST 70 | system_flag = 0b00000100, 71 | // 0 : data written is data 72 | // 1 : data written is command 73 | command_data = 0b00001000, 74 | // may be keyboard lock, 75 | // most likely unused 76 | unknown_lock = 0b00010000, 77 | // may be timeout, 78 | // or second ps/2 port output buffer full 79 | unknown_timeout = 0b00100000, 80 | timeout_error = 0b01000000, 81 | parity_error = 0b10000000 82 | }; 83 | 84 | 85 | /** 86 | * Appends a character to cin and 87 | * increments stdin_index 88 | * @param c char to be appended 89 | */ 90 | void append_cin(char c) { 91 | stdin[stdin_index++] = c; 92 | } 93 | 94 | frg::optional get_scancode() { 95 | // ensure there is data to be read 96 | if (inb(status_register) & status::out_buffer_status) 97 | return inb(data_port); 98 | return NULL; 99 | } 100 | 101 | void handle_input(unsigned char scancode) { 102 | if (scancode == keys::caps_lock) { 103 | shift_pressed = !shift_pressed; 104 | return; 105 | } 106 | 107 | if (scancode & 0x80) { 108 | if ((scancode & ~(1UL << 7)) == keys::lshift) 109 | shift_pressed = !shift_pressed; 110 | } else 111 | switch (scancode) { 112 | case keys::lshift: 113 | shift_pressed = !shift_pressed; 114 | break; 115 | case keys::enter: 116 | firefly::kernel::console::write("\n"); 117 | append_cin('\n'); 118 | break; 119 | case keys::backspace: 120 | firefly::kernel::console::write("\b"); 121 | append_cin('\b'); 122 | break; 123 | default: { 124 | char ascii = sc_ascii[scancode]; 125 | if (shift_pressed) 126 | ascii -= 32; 127 | append_cin(ascii); 128 | char str[2] = { ascii, '\0' }; 129 | firefly::kernel::console::write(str); 130 | } 131 | } 132 | } 133 | 134 | static void ps2_irq_handler([[maybe_unused]] kernel::ContextRegisters*) { 135 | auto const sc = get_scancode(); 136 | if (sc.has_value()) { 137 | handle_input(sc.value()); 138 | } 139 | } 140 | 141 | bool init() { 142 | kernel::core::interrupt::registerIRQHandler(ps2_irq_handler, 1); 143 | kernel::apic::enableIRQ(1); 144 | 145 | // clear keyboard buffer 146 | while (inb(status_register) & 0x1) { 147 | (void)inb(0x60); 148 | } 149 | 150 | return (inb(status_register) & status::system_flag) ? true : false; 151 | } 152 | 153 | } // namespace firefly::drivers::ps2 154 | -------------------------------------------------------------------------------- /firefly/kernel/drivers/serial.cpp: -------------------------------------------------------------------------------- 1 | #include "firefly/drivers/serial.hpp" 2 | 3 | #include 4 | 5 | #include "firefly/drivers/ports.hpp" 6 | #include "libk++/cstring.hpp" 7 | 8 | namespace firefly::kernel::io { 9 | static frg::ticket_spinlock serial_lock = frg::ticket_spinlock(); 10 | 11 | SerialPort::SerialPort(uint16_t port, uint32_t baud_rate) 12 | : port(port), baud_divisor{ static_cast(BAUD_BASE / baud_rate) } { 13 | } 14 | 15 | bool SerialPort::is_initialized() const noexcept { 16 | return initialized; 17 | } 18 | bool SerialPort::received() const noexcept { 19 | return inb(port + 5) & 1; 20 | } 21 | char SerialPort::read_char() const noexcept { 22 | return inb(port); 23 | } 24 | 25 | bool SerialPort::is_transmit_empty() const noexcept { 26 | return inb(port + 5) & 0x20; 27 | } 28 | 29 | void SerialPort::send_char(char c) noexcept { 30 | outb(port, c); 31 | } 32 | 33 | bool SerialPort::initialize() noexcept { 34 | if (initialized) { 35 | return false; 36 | } 37 | 38 | outb(port + 1, 0); // Disable interrupts 39 | outb(port + 3, 0x80); 40 | // divisor 41 | auto low_byte = (baud_divisor & 0xFF); 42 | auto high_byte = (baud_divisor >> 8) & 0xFF; 43 | outb(port, low_byte); 44 | outb(port + 1, high_byte); 45 | // Set 8 bits, no parity, one stop bit 46 | outb(port + 3, 0x03); 47 | // Enable FIFO, clear them, with 14-byte threshold 48 | outb(port + 2, 0xC7); 49 | // IRQs enabled, RTS/DSR set 50 | outb(port + 4, 0x0B); 51 | outb(port + 4, 0x1E); 52 | // Let's test a byte 53 | outb(port, 0xAE); 54 | 55 | if (inb(port) != 0xAE) { 56 | return true; 57 | } 58 | initialized = true; 59 | return false; 60 | } 61 | 62 | void SerialPort::send_chars(const char* c, int len) noexcept { 63 | serial_lock.lock(); 64 | if (len == -1) { 65 | len = libkern::cstring::strlen(c); 66 | } 67 | 68 | for (int i = 0; i < len; ++i) { 69 | send_char(c[i]); 70 | } 71 | send_char('\0'); 72 | serial_lock.unlock(); 73 | } 74 | 75 | void SerialPort::read_string(char* buffer, int len) const noexcept { 76 | char current_char = '1'; // anything that's not '\0' 77 | int read_count = 0; 78 | while (read_count < len && ((!current_char) == '\0')) { 79 | current_char = read_char(); 80 | buffer[read_count++] = current_char; 81 | len--; 82 | } 83 | buffer[read_count] = '\0'; 84 | } 85 | 86 | } // namespace firefly::kernel::io 87 | -------------------------------------------------------------------------------- /firefly/kernel/graphics/framebuffer.cpp: -------------------------------------------------------------------------------- 1 | #include "firefly/graphics/framebuffer.hpp" 2 | 3 | namespace firefly::kernel { 4 | FramebufferDevice fbDev; 5 | } // namespace firefly::kernel -------------------------------------------------------------------------------- /firefly/kernel/init/init.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "firefly/boot/boot_mem.hpp" 5 | #include "firefly/console/console.hpp" 6 | #include "firefly/intel64/acpi/acpi.hpp" 7 | #include "firefly/intel64/cpu/ap/ap.hpp" 8 | #include "firefly/intel64/cpu/cpu.hpp" 9 | #include "firefly/kernel.hpp" 10 | #include "firefly/limine.hpp" 11 | #include "firefly/logger.hpp" 12 | #include "firefly/memory-manager/primary/primary_phys.hpp" 13 | #include "firefly/memory-manager/stack.hpp" 14 | #include "firefly/memory-manager/virtual/virtual.hpp" 15 | #include "firefly/timer/timer.hpp" 16 | #include "firefly/trace/sanitizer/kasan.hpp" 17 | 18 | constinit frg::manual_box kStack; 19 | 20 | alignas(uint16_t) static uint8_t stack[PageSize::Size4K * 2] = { 0 }; 21 | 22 | USED struct limine_memmap_request memmap { 23 | .id = LIMINE_MEMMAP_REQUEST, .revision = 0, .response = nullptr 24 | }; 25 | 26 | void bootloaderServicesInit() { 27 | using namespace firefly::kernel; 28 | const auto verify = [](auto tag) { 29 | if (unlikely(tag == NULL)) { 30 | for (;;) 31 | asm("hlt"); 32 | } 33 | return tag; 34 | }; 35 | 36 | auto tagmem = verify(memmap.response); 37 | 38 | firefly::boot::bootMapExtraRegion(tagmem); 39 | 40 | mm::Physical::init(tagmem); 41 | mm::kernelPageSpace::init(); 42 | mm::kernelHeap::init(); 43 | 44 | core::acpi::Acpi::init(); 45 | console::init(); 46 | 47 | kasan::init(); 48 | } 49 | 50 | extern "C" [[noreturn]] [[gnu::naked]] void kernel_init() { 51 | kStack.initialize(); 52 | Stack::stack stk{ 53 | .rbp = reinterpret_cast(stack), 54 | .rsp = reinterpret_cast(stack + (PageSize::Size4K * 2)) 55 | }; 56 | kStack->add(stk); 57 | 58 | // clang-format off 59 | asm volatile("mov %0, %%rbp" :: "r"(stk.rbp) : "memory"); 60 | asm volatile("mov %0, %%rsp" :: "r"(stk.rsp) : "memory"); 61 | // clang-format off 62 | 63 | bootloaderServicesInit(); 64 | 65 | firefly::kernel::initializeBootProccessor(reinterpret_cast(stack)); 66 | firefly::kernel::applicationProcessor::startAllCores(); 67 | firefly::kernel::timer::init(); 68 | 69 | firefly::kernel::kernel_main(); 70 | __builtin_unreachable(); 71 | } 72 | -------------------------------------------------------------------------------- /firefly/kernel/intel64/acpi/acpi.cpp: -------------------------------------------------------------------------------- 1 | #include "firefly/intel64/acpi/acpi.hpp" 2 | 3 | #include 4 | 5 | #include "firefly/limine.hpp" 6 | #include "firefly/logger.hpp" 7 | #include "firefly/panic.hpp" 8 | 9 | namespace firefly::kernel::core::acpi { 10 | 11 | struct limine_rsdp_request rsdp_request = { 12 | .id = LIMINE_RSDP_REQUEST, 13 | .revision = 0, 14 | .response = nullptr 15 | }; 16 | 17 | frg::manual_box acpiSingleton; 18 | 19 | Acpi& Acpi::accessor() { 20 | return *acpiSingleton; 21 | } 22 | 23 | void Acpi::init() { 24 | acpiSingleton.initialize(); 25 | auto const rsdp_response = rsdp_request.response; 26 | 27 | if (unlikely(!rsdp_response || !rsdp_response->address)) 28 | firefly::panic("Unable to obtain basic ACPI information"); 29 | 30 | // Verify the RSDP checksum 31 | auto& rsdp = acpiSingleton.get()->rsdp; 32 | rsdp = *reinterpret_cast(rsdp_response->address); 33 | rsdp.validateOrPanic(reinterpret_cast(rsdp_response->address)); 34 | 35 | // Initialize the RSDT or XSDT if available 36 | const auto& self = acpiSingleton.get(); 37 | self->useXSDT = rsdp.revision >= 2; 38 | self->divisor = self->useXSDT ? 8 : 4; 39 | 40 | if (self->useXSDT) 41 | self->xsdt.root = reinterpret_castxsdt.root)>(rsdp.xsdtAddress); 42 | else 43 | self->rsdt.root = reinterpret_castrsdt.root)>(rsdp.rsdtAddress); 44 | 45 | self->rootHeader = self->useXSDT ? self->xsdt.root->header : self->rsdt.root->header; 46 | self->entries = (self->rootHeader.length - sizeof(self->rootHeader)) / self->divisor; 47 | 48 | logLine << "Initialized: ACPI\n" 49 | << fmt::endl; 50 | } 51 | 52 | void Acpi::dumpTables() const { 53 | logLine << "Using " << (useXSDT ? "xsdt" : "rsdt") << ", number of acpi tables: " << entries << '\n' 54 | << fmt::endl; 55 | 56 | for (int i = 0; i < entries; i++) { 57 | AcpiSdt* sdt = reinterpret_cast((useXSDT ? xsdt.root->tables[i] : rsdt.root->tables[i])); 58 | 59 | // The SDT descriptors are not null-terminated. 60 | // They are, however, always 4 characters long so we can just iterate over them like this 61 | for (int j = 0; j < 4; j++) { 62 | logLine << sdt->signature[j]; 63 | } 64 | logLine << '\n' 65 | << fmt::endl; 66 | } 67 | } 68 | 69 | AcpiTable Acpi::find(frg::string_view identifier) const { 70 | assert_truth(identifier.size() == 4 && "Acpi identifier must be 4 characters!"); 71 | 72 | for (int i = 0; i < entries; i++) { 73 | AcpiSdt* sdt = reinterpret_cast((useXSDT ? xsdt.root->tables[i] : rsdt.root->tables[i])); 74 | 75 | if (sdt->compareSignature(identifier.data()) && sdt->validate()) { 76 | return AcpiTable(sdt); 77 | } 78 | } 79 | 80 | logLine << "Couldn't find table: " << identifier.data() << '\n' 81 | << fmt::endl; 82 | return nullptr; 83 | } 84 | } // namespace firefly::kernel::core::acpi 85 | -------------------------------------------------------------------------------- /firefly/kernel/intel64/cpu/ap/ap.cpp: -------------------------------------------------------------------------------- 1 | #include "firefly/intel64/cpu/ap/ap.hpp" 2 | 3 | #include 4 | #include 5 | 6 | #include "firefly/drivers/ports.hpp" 7 | #include "firefly/intel64/cpu/apic/apic.hpp" 8 | #include "firefly/intel64/cpu/cpu.hpp" 9 | #include "firefly/limine.hpp" 10 | #include "firefly/logger.hpp" 11 | #include "firefly/memory-manager/mm.hpp" 12 | #include "firefly/memory-manager/primary/primary_phys.hpp" 13 | #include "firefly/memory-manager/virtual/virtual.hpp" 14 | #include "frg/spinlock.hpp" 15 | 16 | 17 | namespace firefly::kernel::applicationProcessor { 18 | volatile struct limine_smp_request smp_request { 19 | .id = LIMINE_SMP_REQUEST, .revision = 0, .response = nullptr 20 | }; 21 | 22 | static frg::ticket_spinlock initLock; 23 | 24 | void smp_main(struct limine_smp_info* info) { 25 | auto stack = reinterpret_cast(mm::Physical::must_allocate(PageSize::Size4K * 2)); 26 | 27 | // clang-format off 28 | asm volatile("mov %0, %%rsp" ::"r"(stack+PageSize::Size4K * 2) : "memory"); 29 | asm volatile("mov %0, %%rbp" ::"r"(stack): "memory"); 30 | // clang-format on 31 | 32 | mm::kernelPageSpace::accessor().setCR3(); 33 | initializeApplicationProcessor(stack); 34 | initLock.unlock(); 35 | 36 | asm volatile("hlt"); 37 | } 38 | 39 | void startAllCores() { 40 | struct limine_smp_response* smp = smp_request.response; 41 | 42 | for (uint64_t i = 0; i < smp->cpu_count; i++) { 43 | auto cpu = smp->cpus[i]; 44 | cpu->goto_address = &smp_main; 45 | initLock.lock(); 46 | } 47 | 48 | apic::IOApic::initAll(); 49 | } 50 | 51 | } // namespace firefly::kernel::applicationProcessor 52 | -------------------------------------------------------------------------------- /firefly/kernel/intel64/cpu/apic/apic.cpp: -------------------------------------------------------------------------------- 1 | #include "firefly/intel64/cpu/apic/apic.hpp" 2 | 3 | #include 4 | 5 | #include "firefly/drivers/ports.hpp" 6 | #include "firefly/intel64/acpi/acpi.hpp" 7 | #include "firefly/intel64/cpu/cpu.hpp" 8 | #include "firefly/limine.hpp" 9 | #include "firefly/memory-manager/primary/buddy.hpp" 10 | #include "firefly/memory-manager/primary/primary_phys.hpp" 11 | #include "firefly/timer/timer.hpp" 12 | #include "libk++/bits.h" 13 | 14 | namespace firefly::kernel::apic { 15 | using core::acpi::Acpi; 16 | 17 | frg::manual_box apicSingleton; 18 | frg::manual_box apicTimerSingleton; 19 | 20 | Apic& Apic::accessor() { 21 | return *apicSingleton; 22 | } 23 | 24 | ApicTimer& ApicTimer::accessor() { 25 | return *apicTimerSingleton; 26 | } 27 | 28 | // Write to the APIC 29 | void Apic::write(uint32_t offset, uint32_t value) { 30 | auto reg = reinterpret_cast(reinterpret_cast(address) + offset); 31 | *reinterpret_cast(reg) = value; 32 | } 33 | 34 | // Read from the APIC 35 | uint32_t Apic::read(uint32_t offset) const { 36 | auto reg = reinterpret_cast(reinterpret_cast(address) + offset); 37 | return *reinterpret_cast(reg); 38 | } 39 | 40 | // Set APIC register 280h to 0 41 | // Clears the errors 42 | void Apic::clearErrors() { 43 | write(0x280, 0); 44 | } 45 | 46 | // Set destination processor for IPI 47 | void Apic::setIPIDest(uint32_t ap) { 48 | write(APIC_ICR1, (read(APIC_ICR1) & 0x00ffffff) | (ap << 24)); 49 | } 50 | 51 | // Send APIC EOI 52 | void Apic::sendEOI() { 53 | write(APIC_EOI, 0); 54 | } 55 | 56 | // Enable the APIC 57 | void Apic::enable() { 58 | write(APIC_SPURIOUS, read(APIC_SPURIOUS) | BIT(8) | 0xFF); 59 | } 60 | 61 | // Enable IRQ in LVT, 62 | // also addes base of vector so there are 63 | // no problems with the CPU exceptions (irq #1 -> LVT_BASE + 1, not int 1) 64 | void Apic::enableIRQ(uint8_t irq) { 65 | write(APIC_SPURIOUS, read(APIC_SPURIOUS) | (LVT_BASE + irq)); 66 | } 67 | 68 | uint32_t Apic::apicId() { 69 | return (read(APIC_ID) >> 24); 70 | } 71 | 72 | // call this to make sure PIC is disabled completely 73 | // We want to use IOAPIC + LAPIC! 74 | static void disable_pic() { 75 | io::outb(0xA1, 0xFF); 76 | io::outb(0x21, 0xFF); 77 | } 78 | 79 | void Apic::init() { 80 | if (!apicSingleton.valid()) apicSingleton.initialize(); 81 | auto lapic = apicSingleton.get(); 82 | disable_pic(); 83 | 84 | // mask all interrupts 85 | lapic->write(APIC_LVTT, APIC_MASKED); 86 | lapic->write(APIC_LVTTHMR, APIC_MASKED); 87 | lapic->write(APIC_LVTPC, APIC_MASKED); 88 | lapic->write(APIC_LVT1, APIC_MASKED); 89 | lapic->write(APIC_LVT0, APIC_MASKED); 90 | lapic->write(APIC_LVTERR, APIC_MASKED); 91 | 92 | lapic->write(APIC_TASK_PRIORITY, 0); // Accept all INTs & exceptions 93 | 94 | lapic->sendEOI(); 95 | 96 | lapic->enable(); 97 | asm volatile("sti"); 98 | } 99 | 100 | uint32_t ApicTimer::calibrate(uint64_t usec) { 101 | auto lapic = Apic::accessor(); 102 | lapic.write(APIC_TIMER_DIVIDER, 3); 103 | lapic.write(APIC_TIMER_INITIAL, ~0L); 104 | 105 | timer::usleep(usec); 106 | 107 | lapic.write(APIC_LVTT, APIC_MASKED); 108 | auto ticks = ~0U - lapic.read(APIC_TIMER_CURRENT); 109 | 110 | lapic.write(APIC_LVTT, (0 << 16) | (0 << 17) | 32); 111 | return ticks; 112 | } 113 | 114 | void ApicTimer::oneShotTimer(uint64_t ticks) { 115 | auto lapic = Apic::accessor(); 116 | lapic.write(APIC_TIMER_INITIAL, ticks); 117 | } 118 | 119 | void ApicTimer::init() { 120 | apicTimerSingleton.initialize(); 121 | } 122 | 123 | bool ApicTimer::isAvailable() { 124 | if (Acpi::accessor().find("APIC")) 125 | return true; 126 | 127 | return false; 128 | } 129 | 130 | } // namespace firefly::kernel::apic 131 | -------------------------------------------------------------------------------- /firefly/kernel/intel64/cpu/apic/ioapic.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "cstdlib/cassert.h" 4 | #include "firefly/drivers/ports.hpp" 5 | #include "firefly/intel64/acpi/acpi.hpp" 6 | #include "firefly/intel64/acpi/tables/madt.hpp" 7 | #include "firefly/intel64/cpu/apic/apic.hpp" 8 | #include "firefly/intel64/cpu/cpu.hpp" 9 | #include "firefly/limine.hpp" 10 | #include "firefly/logger.hpp" 11 | #include "firefly/memory-manager/allocator.hpp" 12 | #include "firefly/memory-manager/primary/buddy.hpp" 13 | #include "firefly/memory-manager/primary/primary_phys.hpp" 14 | #include "frg/array.hpp" 15 | #include "frg/manual_box.hpp" 16 | #include "libk++/bits.h" 17 | 18 | namespace firefly::kernel::apic { 19 | using core::acpi::Acpi; 20 | 21 | namespace { 22 | AcpiMadt* madt = nullptr; 23 | frg::manual_box> ioapicsSingleton; 24 | } // namespace 25 | 26 | 27 | // Write to the IOAPIC 28 | void IOApic::write(uint8_t offset, uint32_t value) { 29 | *reinterpret_cast(address) = offset; 30 | *reinterpret_cast(address + 0x10) = value; 31 | } 32 | 33 | // Read from the IOAPIC 34 | uint32_t IOApic::read(uint8_t offset) const { 35 | *reinterpret_cast(address) = offset; 36 | return *reinterpret_cast(address + 0x10); 37 | } 38 | 39 | // Check for MADT source override 40 | // or return one-to-one GSI 41 | uint8_t IOApic::getGSI(uint8_t irq) { 42 | assert_truth(madt != nullptr); 43 | auto const result = madt->enumerate(); 44 | auto const sourceOverrides = result.get<2>(); 45 | for (size_t i = 0; i < 6; i++) { 46 | auto entry = sourceOverrides[i]; 47 | if (entry != nullptr && entry->source == irq) { 48 | return entry->gsi; 49 | } 50 | } 51 | return irq; 52 | } 53 | 54 | // map IRQ to BSP (for now) 55 | void IOApic::updateIRQ(uint8_t irq) { 56 | RedirectionEntry redEnt = {}; 57 | redEnt.vector = LVT_BASE + irq; 58 | // redEnt.destination = cpu id here; 59 | redEnt.delvMode = IOAPIC_DELMODE_FIXED; 60 | redEnt.destMode = DestinationMode::Physical; 61 | 62 | write((IOAPIC_REDTBL_BASE + irq * 2), redEnt.lowerDword); 63 | write((IOAPIC_REDTBL_BASE + irq * 2 + 1), redEnt.upperDword); 64 | } 65 | 66 | void enableIRQ(uint8_t irq) { 67 | uint64_t gsi = IOApic::getGSI(irq); 68 | logLine << "IRQ #" << fmt::dec << irq << " ==> GSI #" << fmt::dec << gsi << "\n" 69 | << fmt::endl; 70 | 71 | for (size_t i = 0; i < ioapicsSingleton->size(); i++) { 72 | auto ioapic = ioapicsSingleton->data()[i]; 73 | if (gsi < ioapic.getGlobalInterruptBase()) { 74 | ioapic.updateIRQ(gsi); 75 | return; 76 | } 77 | } 78 | 79 | panic("No IOApic found for IRQ"); 80 | return; 81 | } 82 | 83 | void IOApic::initAll() { 84 | using core::acpi::Acpi; 85 | madt = reinterpret_cast(Acpi::accessor().mustFind("APIC")); 86 | ioapicsSingleton.initialize(); 87 | 88 | auto const result = madt->enumerate(); 89 | auto const ioapics = result.get<1>(); 90 | for (size_t i = 0; i < 1; i++) { 91 | auto entry = ioapics[i]; 92 | IOApic ioapic = IOApic(entry->ioApicAddress, entry->ioApicId, entry->globalInterruptBase); 93 | ioapicsSingleton->push(ioapic); 94 | } 95 | } 96 | 97 | 98 | } // namespace firefly::kernel::apic 99 | -------------------------------------------------------------------------------- /firefly/kernel/intel64/cpu/cpu.cpp: -------------------------------------------------------------------------------- 1 | #include "firefly/intel64/cpu/cpu.hpp" 2 | 3 | #include "firefly/intel64/cpu/ap/ap.hpp" 4 | #include "firefly/intel64/cpu/apic/apic.hpp" 5 | #include "firefly/intel64/int/interrupt.hpp" 6 | #include "firefly/memory-manager/allocator.hpp" 7 | #include "firefly/memory-manager/secondary/heap.hpp" 8 | #include "frg/manual_box.hpp" 9 | #include "frg/spinlock.hpp" 10 | #include "frg/vector.hpp" 11 | 12 | namespace firefly::kernel { 13 | namespace { 14 | frg::vector allCpuContexts; 15 | frg::ticket_spinlock allCpuContextsLock; 16 | } // namespace 17 | 18 | void initializeBootProccessor(uint64_t stack) { 19 | initializeThisCpu(stack); 20 | } 21 | 22 | void initializeApplicationProcessor(uint64_t stack) { 23 | initializeThisCpu(stack); 24 | } 25 | 26 | CpuData *getCpuData(size_t k) { 27 | return allCpuContexts[k]; 28 | } 29 | 30 | CpuData *getLocalCpuData() { 31 | AssemblyCpuData *cpu_data; 32 | asm volatile("mov %%gs:0, %0" 33 | : "=r"(cpu_data)); 34 | return reinterpret_cast(cpu_data); 35 | } 36 | 37 | void initializeThisCpu(uint64_t stack) { 38 | auto cpuData = new (mm::heap->allocate(sizeof(CpuData))) CpuData; 39 | cpuData->selfPointer = cpuData; 40 | 41 | allCpuContextsLock.lock(); 42 | cpuData->cpuIndex = allCpuContexts.size(); 43 | allCpuContextsLock.unlock(); 44 | 45 | allCpuContexts.push(cpuData); 46 | 47 | logLine << "initializing CPU " << fmt::dec << cpuData->cpuIndex << '\n' 48 | << fmt::endl; 49 | 50 | core::gdt::init(cpuData->gdt); 51 | core::tss::init(cpuData, stack); 52 | 53 | // Write cpu data base to GS base 54 | wrmsr(MSR::GsBase, reinterpret_cast(cpuData)); 55 | 56 | firefly::kernel::core::interrupt::init(); 57 | 58 | apic::Apic::init(); 59 | } 60 | 61 | } // namespace firefly::kernel 62 | -------------------------------------------------------------------------------- /firefly/kernel/intel64/gdt/gdt.asm: -------------------------------------------------------------------------------- 1 | bits 64 2 | 3 | global load_gdt 4 | load_gdt: 5 | lgdt [rdi] 6 | 7 | push 0x8 8 | lea rax, [rel .flush] 9 | push rax 10 | retfq 11 | .flush: 12 | mov ax, 0x10 13 | mov ds, ax 14 | mov es, ax 15 | mov ss, ax 16 | mov fs, ax 17 | mov gs, ax 18 | ret 19 | -------------------------------------------------------------------------------- /firefly/kernel/intel64/gdt/gdt.cpp: -------------------------------------------------------------------------------- 1 | #include "firefly/intel64/gdt/gdt.hpp" 2 | 3 | #include "firefly/intel64/gdt/tss.hpp" 4 | 5 | namespace firefly::kernel::core::gdt { 6 | 7 | void setGdtEntry(Gdt &cpuGdt, uint64_t selectorIdx, uint8_t flags, uint8_t access) { 8 | cpuGdt.gdtDescriptors[selectorIdx].flags = flags; 9 | cpuGdt.gdtDescriptors[selectorIdx].access = access; 10 | } 11 | 12 | void setTssEntry(Gdt &cpuGdt, uint64_t selectorIdx, uint8_t flags, uint8_t access) { 13 | cpuGdt.tssDescriptors.size = 104; 14 | cpuGdt.tssDescriptors.base0 = selectorIdx & 0xFFFF; 15 | cpuGdt.tssDescriptors.base1 = (selectorIdx >> 16) & 0xFF; 16 | cpuGdt.tssDescriptors.access = access; 17 | cpuGdt.tssDescriptors.flags = flags; 18 | cpuGdt.tssDescriptors.base2 = (selectorIdx >> 24) & 0xFF; 19 | cpuGdt.tssDescriptors.base3 = (selectorIdx >> 32); 20 | cpuGdt.tssDescriptors.reserved = 0; 21 | } 22 | 23 | uint16_t tssEntryOffset() { 24 | return GDT_MAX_ENTRIES * 8; 25 | } 26 | 27 | uint16_t gdtEntryOffset(enum SegmentSelector selector) { 28 | return selector * 8; 29 | } 30 | 31 | void init(Gdt &gdt) { 32 | setGdtEntry(gdt, SegmentSelector::null, 0, 0); 33 | setGdtEntry(gdt, SegmentSelector::kernCS, 0xA2, 0x9A); 34 | setGdtEntry(gdt, SegmentSelector::kernDS, 0xA0, 0x92); 35 | setGdtEntry(gdt, SegmentSelector::userDS, 0x00, 0xF2); 36 | setGdtEntry(gdt, SegmentSelector::userCS, 0x20, 0xFA); 37 | 38 | Gdtr gdtr; 39 | gdtr.size = (sizeof(Gdt) + GDT_MAX_ENTRIES) - 1; 40 | gdtr.base = reinterpret_cast(&gdt); 41 | 42 | load_gdt((uint64_t)&gdtr); 43 | } 44 | } // namespace firefly::kernel::core::gdt 45 | -------------------------------------------------------------------------------- /firefly/kernel/intel64/gdt/tss.cpp: -------------------------------------------------------------------------------- 1 | #include "firefly/intel64/cpu/cpu.hpp" 2 | #include "firefly/intel64/gdt/gdt.hpp" 3 | #include "firefly/logger.hpp" 4 | #include "firefly/memory-manager/primary/primary_phys.hpp" 5 | #include "firefly/panic.hpp" 6 | 7 | namespace firefly::kernel::core::tss { 8 | 9 | inline void load_tss(SegmentSelector selector) { 10 | asm("ltr %%ax\n" ::"a"(selector)); 11 | } 12 | 13 | void init(CpuData* cpuData, uint64_t stack) { 14 | Tss& tss = cpuData->tss; 15 | gdt::setTssEntry(cpuData->gdt, reinterpret_cast(&tss), 0x20, 0x89); 16 | tss.RSP0 = stack; // general 17 | tss.IST1 = reinterpret_cast(mm::Physical::must_allocate()); // double fault 18 | tss.IST2 = reinterpret_cast(mm::Physical::must_allocate()); // nmi 19 | tss.IST3 = reinterpret_cast(mm::Physical::must_allocate()); // debug 20 | tss.IST4 = reinterpret_cast(mm::Physical::must_allocate()); // mce 21 | load_tss(static_cast(gdt::tssEntryOffset())); 22 | } 23 | } // namespace firefly::kernel::core::tss 24 | -------------------------------------------------------------------------------- /firefly/kernel/intel64/hpet/hpet.cpp: -------------------------------------------------------------------------------- 1 | #include "firefly/intel64/hpet/hpet.hpp" 2 | 3 | #include "firefly/intel64/acpi/acpi.hpp" 4 | #include "firefly/intel64/cpu/apic/apic.hpp" 5 | #include "firefly/intel64/cpu/cpu.hpp" 6 | #include "firefly/intel64/int/interrupt.hpp" 7 | #include "firefly/logger.hpp" 8 | #include "firefly/memory-manager/allocator.hpp" 9 | #include "firefly/timer/timer.hpp" 10 | #include "frg/manual_box.hpp" 11 | 12 | namespace firefly::kernel::timer { 13 | using core::acpi::Acpi; 14 | 15 | namespace { 16 | frg::manual_box hpetSingleton; 17 | frg::manual_box> hpetTimers; 18 | } // namespace 19 | 20 | 21 | HPET& HPET::accessor() { 22 | return *hpetSingleton; 23 | } 24 | 25 | void HPET::write(const uint64_t offset, const uint64_t value) { 26 | auto reg = reinterpret_cast(reinterpret_cast(address) + offset); 27 | *reinterpret_cast(reg) = value; 28 | } 29 | 30 | uint64_t HPET::read(const uint64_t offset) const { 31 | auto reg = reinterpret_cast(reinterpret_cast(address) + offset); 32 | return *reinterpret_cast(reg); 33 | } 34 | 35 | void HPET::initialize() { 36 | period = (read(GENERAL_CAPS_REG) >> 32); 37 | } 38 | 39 | // Set bit 0 40 | void HPET::enable() { 41 | write(GENERAL_CONF_REG, read(GENERAL_CONF_REG) | BIT(0)); 42 | } 43 | 44 | // Clear bit 0 45 | void HPET::disable() { 46 | write(GENERAL_CONF_REG, read(GENERAL_CONF_REG) & ~BIT(0)); 47 | } 48 | 49 | void HPETTimer::setPeriodicMode() { 50 | if (!periodicSupport) panic("Timer doesn't have support for periodic interrupts"); 51 | auto cfg = HPET::accessor().read(timer_config(timerNum)); 52 | cfg |= (1 << 3) | (1 << 6); 53 | HPET::accessor().write(timer_config(timerNum), cfg); 54 | } 55 | 56 | void HPETTimer::setOneshotMode() { 57 | auto cfg = HPET::accessor().read(timer_config(timerNum)); 58 | HPET::accessor().write(timer_config(timerNum), cfg); 59 | } 60 | 61 | void HPETTimer::setIoApicOutput(uint8_t output) { 62 | auto cfg = HPET::accessor().read(timer_config(timerNum)); 63 | cfg |= (output << 9); 64 | HPET::accessor().write(timer_config(timerNum), cfg); 65 | } 66 | 67 | void HPETTimer::enable() { 68 | auto cfg = HPET::accessor().read(timer_config(timerNum)); 69 | cfg |= (1 << 2); 70 | HPET::accessor().write(timer_config(timerNum), cfg); 71 | } 72 | 73 | void HPETTimer::disable() { 74 | auto cfg = HPET::accessor().read(timer_config(timerNum)); 75 | cfg |= (0 << 2); 76 | HPET::accessor().write(timer_config(timerNum), cfg); 77 | } 78 | 79 | 80 | // Setup HPET 81 | // Includes unused things, that may be helpful later 82 | void HPET::init() { 83 | if (hpetSingleton.valid()) 84 | panic("Tried to initialize HPET twice"); 85 | auto const& hpetAcpiTable = reinterpret_cast(Acpi::accessor().mustFind("HPET")); 86 | hpetSingleton.initialize(hpetAcpiTable->address, reinterpret_cast(hpetAcpiTable->minimumClockTicks)); 87 | hpetTimers.initialize(); 88 | 89 | auto hpet = hpetSingleton.get(); 90 | hpet->write(GENERAL_CONF_REG, 0); 91 | hpet->write(MAIN_COUNTER_VALUE_REG, 0); 92 | hpet->disable(); 93 | 94 | size_t timersSize = 3; // TODO: bits 12-8 in general caps 95 | for (size_t i = 0; i < timersSize; i++) { 96 | bool periodic = (hpet->read(timer_config(i)) >> 4) & 1UL; 97 | hpetTimers->push(HPETTimer(periodic, i)); 98 | } 99 | 100 | auto hpetTimer = hpetTimers->data()[0]; 101 | hpetTimer.setPeriodicMode(); 102 | hpetTimer.enable(); 103 | 104 | hpet->initialize(); 105 | hpet->enable(); 106 | } 107 | 108 | void HPET::deinit() { 109 | auto hpet = hpetSingleton.get(); 110 | hpet->disable(); 111 | } 112 | 113 | } // namespace firefly::kernel::timer 114 | -------------------------------------------------------------------------------- /firefly/kernel/intel64/int/interrupt.asm: -------------------------------------------------------------------------------- 1 | bits 64 2 | 3 | global assign_cpu_exceptions 4 | global assign_irq_handlers 5 | 6 | extern interrupt_handler 7 | extern irq_handler 8 | extern update 9 | 10 | %macro pusha64 0 11 | push rax 12 | push rbx 13 | push rcx 14 | push rdx 15 | push rsi 16 | push rdi 17 | push rbp 18 | push r8 19 | push r9 20 | push r10 21 | push r11 22 | push r12 23 | push r13 24 | push r14 25 | push r15 26 | %endmacro 27 | 28 | %macro popa64 0 29 | pop r15 30 | pop r14 31 | pop r13 32 | pop r12 33 | pop r11 34 | pop r10 35 | pop r9 36 | pop r8 37 | pop rbp 38 | pop rdi 39 | pop rsi 40 | pop rdx 41 | pop rcx 42 | pop rbx 43 | pop rax 44 | %endmacro 45 | 46 | %macro CPU_INTR 1 47 | CPU_INTR%1: 48 | push qword 0xff 49 | push qword %1 50 | jmp interrupt_wrapper 51 | %endmacro 52 | 53 | %macro CPU_INTR_ERR 1 54 | CPU_INTR_ERR%1: 55 | ; error code will be pushed by CPU 56 | push qword %1 57 | jmp interrupt_wrapper 58 | %endmacro 59 | 60 | %macro CPU_IRQ 1 61 | CPU_IRQ%1: 62 | push qword 0xff ; dummy error code 63 | push qword %1 64 | jmp irq_wrapper 65 | %endmacro 66 | 67 | %macro register_handler 1 68 | lea rdi, [rel %1] 69 | mov rsi, 0x8 70 | mov rdx, 0x8E 71 | mov rcx, i 72 | mov r8, 0 73 | call update 74 | %endmacro 75 | 76 | ; - CPU Exceptions - 77 | %assign i 0 78 | %rep 8 79 | CPU_INTR i 80 | %assign i i+1 81 | %endrep 82 | 83 | CPU_INTR_ERR 8 84 | CPU_INTR_ERR 9 85 | CPU_INTR_ERR 10 86 | CPU_INTR_ERR 11 87 | CPU_INTR_ERR 12 88 | CPU_INTR_ERR 13 89 | CPU_INTR_ERR 14 90 | 91 | %assign i 15 92 | %rep 17 93 | CPU_INTR i 94 | %assign i i+1 95 | %endrep 96 | ; - CPU Exceptions - 97 | 98 | assign_cpu_exceptions: 99 | %assign i 0 100 | %rep 8 101 | register_handler CPU_INTR%+i 102 | %assign i i+1 103 | %endrep 104 | 105 | %rep 6 106 | register_handler CPU_INTR_ERR%+i 107 | %assign i i+1 108 | %endrep 109 | 110 | %assign i 15 111 | %rep 17 112 | register_handler CPU_INTR%+i 113 | %assign i i+1 114 | %endrep 115 | ret 116 | 117 | ; CPU exceptions 118 | interrupt_wrapper: 119 | cld 120 | pusha64 121 | ; also save SSE state when we figure that out 122 | ; note from @legendary-cookie: is saving SSE needed on a non-task switching interrupt? 123 | 124 | call interrupt_handler 125 | 126 | popa64 127 | add rsp, 16 128 | iretq 129 | 130 | ; IRQs 131 | 132 | %assign i 0x20 133 | %rep 16 134 | CPU_IRQ i 135 | %assign i i+1 136 | %endrep 137 | 138 | assign_irq_handlers: 139 | %assign i 0x20 140 | %rep 16 141 | register_handler CPU_IRQ%+i 142 | %assign i i+1 143 | %endrep 144 | ret 145 | 146 | irq_wrapper: 147 | cld 148 | pusha64 149 | 150 | mov rdi, rsp 151 | call irq_handler 152 | mov rsp, rax 153 | 154 | popa64 155 | add rsp, 16 156 | iretq 157 | -------------------------------------------------------------------------------- /firefly/kernel/intel64/int/interrupt.cpp: -------------------------------------------------------------------------------- 1 | #include "firefly/intel64/int/interrupt.hpp" 2 | 3 | #include 4 | 5 | #include "cstdlib/cassert.h" 6 | #include "firefly/drivers/ports.hpp" 7 | #include "firefly/intel64/cpu/apic/apic.hpp" 8 | #include "firefly/logger.hpp" 9 | #include "firefly/panic.hpp" 10 | #include "firefly/trace/symbols.hpp" 11 | 12 | namespace firefly::kernel::core::interrupt { 13 | struct __attribute__((packed)) idt_gate { 14 | uint16_t offset_0; 15 | uint16_t selector; 16 | uint8_t ist; 17 | uint8_t type; 18 | uint16_t offset_1; 19 | uint32_t offset_2; 20 | uint32_t rsv_1; 21 | }; 22 | 23 | static_assert(16 == sizeof(idt_gate), "idt_gate size incorrect"); 24 | 25 | extern "C" { 26 | void interrupt_handler(InterruptFrame iframe); 27 | InterruptFrame* irq_handler(InterruptFrame* iframe); 28 | void exception_handler([[maybe_unused]] InterruptFrame iframe); 29 | void interrupt_wrapper(); 30 | void exception_wrapper(); 31 | void assign_cpu_exceptions(); 32 | void assign_irq_handlers(); 33 | } 34 | 35 | static idt_gate idt[256]; 36 | 37 | static const char* exceptions[] = { 38 | "Divide by Zero", 39 | "Debug", 40 | "NMI", 41 | "Breakpoint", 42 | "Overflow", 43 | "Bound Range Exceeded", 44 | "Invalid Opcode", 45 | "Device not available", 46 | "Double Fault", 47 | "", 48 | "Invalid TSS", 49 | "Segment not present", 50 | "Stack Segment fault", 51 | "GPF", 52 | "Page Fault", 53 | "", 54 | "x87 Floating point exception", 55 | "Alignment check", 56 | "Machine check", 57 | "SIMD Floating point exception", 58 | "Virtualization Exception", 59 | "Control protection exception", 60 | "", 61 | "", 62 | "", 63 | "", 64 | "", 65 | "", 66 | "Hypervisor injection exception", 67 | "VMM Communication exception", 68 | "Security exception", 69 | "" 70 | }; 71 | 72 | namespace change { 73 | extern "C" void set_ist(uint8_t index, uint8_t ist) { 74 | idt[index].ist = ist; 75 | } 76 | extern "C" void update(void (*handler)(), uint16_t cs, uint8_t type, uint8_t index, uint8_t ist) { 77 | idt[index].offset_0 = reinterpret_cast(handler) & 0xffff; 78 | idt[index].selector = cs; 79 | idt[index].ist = ist; 80 | idt[index].type = type; 81 | idt[index].offset_1 = reinterpret_cast(handler) >> 16 & 0xffff; 82 | idt[index].offset_2 = reinterpret_cast(handler) >> 32 & 0xffffffff; 83 | idt[index].rsv_1 = 0; 84 | } 85 | } // namespace change 86 | 87 | struct __attribute__((packed)) idt_reg { 88 | /** 89 | * size of table in bytes - 1 90 | */ 91 | uint16_t limit; 92 | /** 93 | * base address of idt 94 | */ 95 | idt_gate* base; 96 | } idtr = { 97 | .limit = (sizeof(struct idt_gate) * 256) - 1, 98 | .base = idt 99 | }; 100 | 101 | void init() { 102 | assign_cpu_exceptions(); 103 | assign_irq_handlers(); 104 | 105 | // check tss.cpp for IST usage 106 | change::set_ist(8, 1); 107 | change::set_ist(2, 2); 108 | change::set_ist(1, 3); 109 | change::set_ist(18, 4); 110 | 111 | asm("lidt %0" ::"m"(idtr) 112 | : "memory"); 113 | } 114 | 115 | void interrupt_handler(InterruptFrame iframe) { 116 | logLine << "Exception: " << exceptions[iframe.int_no] << "\n" 117 | << fmt::endl; 118 | logLine << "Int#: " << iframe.int_no << "\nError code: " << iframe.err << fmt::endl; 119 | logLine << "RIP: " << fmt::hex << iframe.rip << fmt::endl; 120 | 121 | debugLine << "Rip: " << fmt::hex << iframe.rip << '\n' 122 | << "Rax: " << fmt::hex << iframe.rax << '\n' 123 | << "Rbx: " << fmt::hex << iframe.rbx << '\n' 124 | << "Rcx: " << fmt::hex << iframe.rcx << '\n' 125 | << "Rdx: " << fmt::hex << iframe.rdx << '\n' 126 | << "Rdi: " << fmt::hex << iframe.rdi << '\n' 127 | << "Rsi: " << fmt::hex << iframe.rsi << '\n' 128 | << "R8: " << fmt::hex << iframe.r8 << '\n' 129 | << "R9: " << fmt::hex << iframe.r9 << '\n' 130 | << "R10: " << fmt::hex << iframe.r10 << '\n' 131 | << "R11: " << fmt::hex << iframe.r11 << '\n' 132 | << "R12: " << fmt::hex << iframe.r12 << '\n' 133 | << "R13: " << fmt::hex << iframe.r13 << '\n' 134 | << "R14: " << fmt::hex << iframe.r14 << '\n' 135 | << "R15: " << fmt::hex << iframe.r15 << '\n' 136 | << fmt::endl; 137 | 138 | panic("interrupt"); 139 | 140 | for (;;) 141 | asm("cli\nhlt"); 142 | } 143 | 144 | using InterruptHandlerCallback = void (*)(ContextRegisters*); 145 | static frg::array irqHandlers; 146 | 147 | void registerIRQHandler(void (*handler)(ContextRegisters*), uint8_t irq) { 148 | assert_truth(irqHandlers[irq] == nullptr && "Tried to overwrite IRQ handler"); 149 | irqHandlers[irq] = handler; 150 | } 151 | 152 | void unregisterIRQHandler(uint8_t irq) { 153 | irqHandlers[irq] = nullptr; 154 | } 155 | 156 | InterruptFrame* irq_handler(InterruptFrame* stack) { 157 | uint8_t irq = stack->int_no - apic::LVT_BASE; 158 | if (irqHandlers[irq] != nullptr) { 159 | irqHandlers[irq](stack); 160 | } else { 161 | debugLine << "Unhandled IRQ received! IRQ #" << stack->int_no - apic::LVT_BASE << "\n" 162 | << fmt::endl; 163 | } 164 | apic::Apic::accessor().sendEOI(); 165 | return stack; 166 | } 167 | 168 | } // namespace firefly::kernel::core::interrupt 169 | -------------------------------------------------------------------------------- /firefly/kernel/intel64/paging.cpp: -------------------------------------------------------------------------------- 1 | #include "firefly/intel64/paging.hpp" 2 | 3 | #include "firefly/compiler/compiler.hpp" 4 | #include "firefly/intel64/cpu/cpu.hpp" 5 | #include "firefly/memory-manager/primary/page_frame.hpp" 6 | #include "firefly/memory-manager/primary/primary_phys.hpp" 7 | #include "firefly/panic.hpp" 8 | #include "libk++/bits.h" 9 | 10 | namespace firefly::kernel::core::paging { 11 | 12 | void invalidatePage(const VirtualAddress page) { 13 | asm volatile("invlpg %0" ::"m"(page) 14 | : "memory"); 15 | } 16 | 17 | void invalidatePage(const uint64_t page) { 18 | invalidatePage(reinterpret_cast(page)); 19 | } 20 | 21 | // Index within the Pml (0-511) 22 | inline int64_t getPmlOffset(const uint64_t virtual_addr, const int depth) { 23 | // Dissect the virtual address by retrieving 9 bits starting at `depth - 1` 24 | return (virtual_addr >> (PAGE_SHIFT + (9 * (depth - 1)))) & 0x1FF; // We subtract 1 from idx so that we don't have to input idx 0-3, but rather 1-4 25 | } 26 | 27 | // Number of translation levels 28 | using PageDepth = int; 29 | 30 | // Returns the number of translation levels 31 | // -> Pml3, Pml2, Pml1 32 | PageDepth depthOf(const PageSize page_size) { 33 | switch (page_size) { 34 | case PageSize::Size1G: 35 | return 3; 36 | 37 | case PageSize::Size2M: 38 | return 2; 39 | 40 | // 4k 41 | default: 42 | return 1; 43 | } 44 | 45 | __builtin_unreachable(); 46 | } 47 | 48 | void traversePageTables(const uint64_t virtual_addr, const uint64_t physical_addr, AccessFlags access_flags, Pml *pml, const PageSize page_size = PageSize::Size4K) { 49 | int depth = 4; 50 | const int target_depth = depthOf(page_size); 51 | 52 | // Stop at pml1 53 | while (depth != target_depth) { 54 | pml = pml->next(getPmlOffset(virtual_addr, depth), access_flags, page_size); 55 | depth--; 56 | } 57 | 58 | // lowest_pml_index changes depending on the number of translation levels we use. 59 | // This can vary for 4K, 2M and 1G pages 60 | auto lowest_pml_index = getPmlOffset(virtual_addr, depth); 61 | 62 | // Set the PS bit 63 | int flags = static_cast(access_flags); 64 | if (lowest_pml_index != 1) flags |= BIT(7); 65 | 66 | pml->entries[lowest_pml_index].create(physical_addr, flags, page_size); 67 | } 68 | 69 | void map(uint64_t virtual_addr, uint64_t physical_addr, AccessFlags access_flags, Pml *root, const PageSize page_size) { 70 | traversePageTables(virtual_addr, physical_addr, access_flags, root, page_size); 71 | invalidatePage(virtual_addr); 72 | } 73 | 74 | 75 | } // namespace firefly::kernel::core::paging 76 | -------------------------------------------------------------------------------- /firefly/kernel/intel64/pit/pit.cpp: -------------------------------------------------------------------------------- 1 | #include "firefly/intel64/pit/pit.hpp" 2 | 3 | #include 4 | #include 5 | 6 | #include "firefly/drivers/ports.hpp" 7 | #include "firefly/intel64/cpu/apic/apic.hpp" 8 | #include "firefly/intel64/int/interrupt.hpp" 9 | #include "firefly/logger.hpp" 10 | #include "firefly/timer/timer.hpp" 11 | 12 | namespace { 13 | constexpr const uint64_t PIT_HZ = 1193180; 14 | constexpr const uint64_t PIT_FREQUENCY = 1000; // 1ms or smth 15 | } // namespace 16 | 17 | namespace firefly::kernel { 18 | namespace timer::pit { 19 | 20 | static void timer_callback() { 21 | timer::tick(); 22 | } 23 | 24 | void init() { 25 | // core::interrupt::registerIRQHandler(timer_callback, apic::IOApic::getGSI(0)); 26 | apic::enableIRQ(0); 27 | 28 | const uint32_t divisor = PIT_HZ / PIT_FREQUENCY; 29 | 30 | // send the command byte 31 | io::outb(0x43, 0x36); 32 | 33 | // divisor needs to be sent bytewise 34 | uint8_t l = static_cast(divisor & 0xFF); 35 | uint8_t h = static_cast((divisor >> 8) & 0xFF); 36 | 37 | io::outb(0x40, l); 38 | io::outb(0x40, h); 39 | } 40 | 41 | void destroy() { 42 | core::interrupt::unregisterIRQHandler(apic::IOApic::getGSI(0)); 43 | } 44 | } // namespace timer::pit 45 | } // namespace firefly::kernel 46 | -------------------------------------------------------------------------------- /firefly/kernel/kernel.cpp: -------------------------------------------------------------------------------- 1 | #include "firefly/kernel.hpp" 2 | 3 | #include 4 | #include 5 | 6 | #include "firefly/drivers/ps2.hpp" 7 | #include "firefly/drivers/serial.hpp" 8 | #include "firefly/intel64/acpi/acpi.hpp" 9 | #include "firefly/intel64/hpet/hpet.hpp" 10 | #include "firefly/intel64/pit/pit.hpp" 11 | #include "firefly/logger.hpp" 12 | #include "firefly/memory-manager/allocator.hpp" 13 | #include "firefly/memory-manager/secondary/heap.hpp" 14 | #include "firefly/panic.hpp" 15 | #include "firefly/tasks/scheduler.hpp" 16 | #include "firefly/timer/timer.hpp" 17 | 18 | [[maybe_unused]] constexpr short MAJOR_VERSION = 0; 19 | [[maybe_unused]] constexpr short MINOR_VERSION = 0; 20 | constexpr const char *VERSION_STRING = "0.0"; 21 | 22 | namespace firefly::kernel { 23 | void log_core_firefly_contributors() { 24 | logLine << "FireflyOS\nVersion: " << VERSION_STRING << "\nContributors:"; 25 | 26 | frg::array arr = { 27 | "Lime\t ", "JohnkaS", "V01D-NULL" 28 | }; 29 | 30 | for (size_t i = 0; i < arr.max_size(); i++) { 31 | if (i % 2 == 0) { 32 | logLine << '\n' 33 | << '\t'; 34 | } 35 | logLine << " " << arr[i]; 36 | } 37 | logLine << '\n' 38 | << fmt::endl; 39 | } 40 | 41 | void loop1() { 42 | debugLine << "loop 1 \n" 43 | << fmt::endl; 44 | 45 | for (;;) { 46 | asm volatile("hlt"); 47 | } 48 | } 49 | 50 | void loop2() { 51 | debugLine << "loop 2\n" 52 | << fmt::endl; 53 | 54 | for (;;) { 55 | asm volatile("hlt"); 56 | } 57 | } 58 | 59 | void loop3() { 60 | debugLine << "loop 3\n" 61 | << fmt::endl; 62 | for (;;) { 63 | asm volatile("hlt"); 64 | } 65 | } 66 | 67 | [[noreturn]] void kernel_main() { 68 | log_core_firefly_contributors(); 69 | core::acpi::Acpi::accessor().dumpTables(); 70 | 71 | 72 | // Testing the heap with a vector 73 | frg::vector vec; 74 | vec.push(1); 75 | vec.push(2); 76 | vec.push(3); 77 | logLine << "Vec.size: " << vec.size() << ", vec.front: " << vec.front() << "\n" 78 | << fmt::endl; 79 | 80 | // Testing the heap with allocations 81 | auto ptr = mm::heap->allocate(sizeof(int)); 82 | logLine << "ptr=" << fmt::hex << reinterpret_cast(ptr) << '\n' 83 | << fmt::endl; 84 | 85 | auto ptr2 = mm::heap->allocate(sizeof(int)); 86 | logLine << "ptr=" << fmt::hex << reinterpret_cast(ptr2) << '\n' 87 | << fmt::endl; 88 | 89 | firefly::drivers::ps2::init(); 90 | tasks::Scheduler::init(); 91 | 92 | auto sp1 = reinterpret_cast(mm::Physical::must_allocate(8192)); 93 | auto sp2 = reinterpret_cast(mm::Physical::must_allocate(8192)); 94 | auto sp3 = reinterpret_cast(mm::Physical::must_allocate(8192)); 95 | tasks::Scheduler::accessor().addTask( 96 | tasks::Task(reinterpret_cast(&loop1), sp1)); 97 | tasks::Scheduler::accessor().addTask( 98 | tasks::Task(reinterpret_cast(&loop2), sp2)); 99 | tasks::Scheduler::accessor().addTask( 100 | tasks::Task(reinterpret_cast(&loop3), sp3)); 101 | 102 | timer::start(); 103 | for (;;) { 104 | // wait for scheduler to start 105 | } 106 | panic("Reached the end of the kernel"); 107 | __builtin_unreachable(); 108 | } 109 | } // namespace firefly::kernel 110 | -------------------------------------------------------------------------------- /firefly/kernel/logger.cpp: -------------------------------------------------------------------------------- 1 | #include "firefly/logger.hpp" 2 | 3 | namespace firefly { 4 | log logLine; 5 | dbg debugLine; 6 | } // namespace firefly 7 | -------------------------------------------------------------------------------- /firefly/kernel/memory-manager/primary/primary_phys.cpp: -------------------------------------------------------------------------------- 1 | #include "firefly/memory-manager/primary/primary_phys.hpp" 2 | 3 | #include "firefly/memory-manager/page.hpp" 4 | #include "firefly/memory-manager/primary/buddy.hpp" 5 | #include "frg/spinlock.hpp" 6 | 7 | // TODO: Use constinit 8 | Pagelist pagelist; 9 | BuddyManager buddy; 10 | frg::ticket_spinlock buddyLock = frg::ticket_spinlock(); 11 | 12 | namespace firefly::kernel::mm::Physical { 13 | 14 | 15 | void init(limine_memmap_response *mmap) { 16 | buddy.init(mmap); 17 | pagelist.init(mmap); 18 | logLine << "pmm: Initialized" << fmt::endl; 19 | } 20 | 21 | PhysicalAddress allocate(uint64_t size, FillMode fill) { 22 | (void)fill; 23 | return buddy.alloc(size); 24 | } 25 | 26 | PhysicalAddress must_allocate(uint64_t size, FillMode fill) { 27 | (void)fill; 28 | return buddy.must_alloc(size); 29 | } 30 | 31 | void deallocate(PhysicalAddress ptr) { 32 | buddy.free(static_cast(ptr)); 33 | } 34 | } // namespace firefly::kernel::mm::Physical 35 | -------------------------------------------------------------------------------- /firefly/kernel/memory-manager/secondary/heap.cpp: -------------------------------------------------------------------------------- 1 | #include "firefly/memory-manager/secondary/heap.hpp" 2 | 3 | #include 4 | 5 | #include "cstdlib/cmath.h" 6 | #include "firefly/logger.hpp" 7 | #include "firefly/memory-manager/primary/buddy.hpp" 8 | #include "firefly/memory-manager/secondary/slab/slab.hpp" 9 | #include "firefly/memory-manager/virtual/virtual.hpp" 10 | 11 | namespace firefly::kernel::mm { 12 | 13 | BuddyAllocator vm_buddy; 14 | 15 | // Todo: 16 | // - The slab allocator needs to resize itself (i.e. add or remove(free) slabs). 17 | // (Useful when the system is low on memory) 18 | // 19 | // - Integrate kasan into this. 20 | struct VmBackingAllocator { 21 | VirtualAddress allocate(int size) { 22 | auto ptr = vm_buddy.alloc(size).unpack(); 23 | 24 | if (ptr == nullptr) 25 | panic("vm_buddy returned a null-pointer, heap is OOM."); 26 | 27 | return VirtualAddress(ptr); 28 | } 29 | }; 30 | 31 | struct LockingMechanism { 32 | void lock() { 33 | } 34 | void unlock() { 35 | } 36 | }; 37 | 38 | 39 | // Allocatable sizes: 8, 16, 32 64, 128, 256, 512, 1024, 2048, 4096. 40 | // Allocation requests for sizes lower than the minimum, 8, will be served with the minimum size (8). 41 | using cacheType = slabCache; 42 | frg::array kernelAllocatorCaches = {}; 43 | 44 | constinit frg::manual_box heap; 45 | 46 | void kernelHeap::init() { 47 | // Map kernel heap with physical memory to back it up. 48 | for (int i = 0; i < GiB(1); i += PageSize::Size4K) { 49 | auto phys = reinterpret_cast(Physical::must_allocate()); 50 | kernelPageSpace::accessor().map(i + AddressLayout::SlabHeap, phys, AccessFlags::ReadWrite, PageSize::Size4K); 51 | } 52 | 53 | // Initialize separate buddy instance to manage heap memory (VmBackingAllocator) 54 | vm_buddy.init(reinterpret_cast(AddressLayout::SlabHeap), BuddyAllocator::largest_allowed_order); 55 | 56 | heap.initialize(); 57 | for (size_t i = 0, j = 3; i < kernelAllocatorCaches.size(); i++, j++) 58 | kernelAllocatorCaches[i].initialize(1 << j, "heap"); 59 | } 60 | 61 | VirtualAddress kernelHeap::allocate(size_t size) const { 62 | assert_truth(size >= 2 && size <= 4096 && "Invalid allocation size"); 63 | 64 | if (!slabHelper::powerOfTwo(size)) 65 | size = slabHelper::alignToSecondPower(size); 66 | 67 | auto& cache = kernelAllocatorCaches[log2(size) - 1]; 68 | return cache.allocate(); 69 | } 70 | 71 | void kernelHeap::deallocate(VirtualAddress ptr) const { 72 | if (ptr == nullptr) 73 | return; 74 | 75 | auto aligned_address = (reinterpret_cast(ptr) >> PAGE_SHIFT) << PAGE_SHIFT; 76 | auto size = pagelist.phys_to_page(aligned_address - AddressLayout::SlabHeap)->slab_size; 77 | // assert_truth(size >= 2 && size <= 4096 && "Invalid deallocation size"); 78 | if (size < 8) return; 79 | auto& cache = kernelAllocatorCaches[log2(size) - 1]; 80 | cache.deallocate(ptr); 81 | } 82 | 83 | } // namespace firefly::kernel::mm 84 | -------------------------------------------------------------------------------- /firefly/kernel/memory-manager/virtual/virtual.cpp: -------------------------------------------------------------------------------- 1 | #include "firefly/memory-manager/virtual/virtual.hpp" 2 | 3 | #include "firefly/compiler/compiler.hpp" 4 | #include "firefly/console/console.hpp" 5 | #include "firefly/intel64/cpu/cpu.hpp" 6 | #include "firefly/limine.hpp" 7 | #include "firefly/logger.hpp" 8 | #include "firefly/memory-manager/primary/buddy.hpp" 9 | #include "firefly/memory-manager/primary/primary_phys.hpp" 10 | #include "firefly/panic.hpp" 11 | #include "libk++/bits.h" 12 | 13 | namespace firefly::kernel::mm { 14 | 15 | USED struct limine_kernel_address_request kernel_address { 16 | .id = LIMINE_KERNEL_ADDRESS_REQUEST, .revision = 0, .response = nullptr 17 | }; 18 | 19 | frg::manual_box kPageSpaceSingleton{}; 20 | 21 | kernelPageSpace &kernelPageSpace::accessor() { 22 | return *kPageSpaceSingleton; 23 | } 24 | 25 | void kernelPageSpace::init() { 26 | auto pml4 = static_cast(Physical::must_allocate()); 27 | kPageSpaceSingleton.initialize(pml4); 28 | 29 | auto const &self = kPageSpaceSingleton.get(); 30 | 31 | self->mapRange(PageSize::Size4K, buddy.get_highest_address(), AccessFlags::ReadWrite, AddressLayout::Low); 32 | 33 | if (self->hugePages) { 34 | self->mapRange(0, GiB(4), AccessFlags::ReadWrite, AddressLayout::High, PageSize::Size1G); 35 | self->map(AddressLayout::PageData, 0, AccessFlags::ReadWrite, PageSize::Size1G); 36 | } else { 37 | self->mapRange(0, GiB(4), AccessFlags::ReadWrite, AddressLayout::High, PageSize::Size2M); 38 | self->mapRange(0, GiB(1), AccessFlags::ReadWrite, AddressLayout::PageData, PageSize::Size2M); 39 | } 40 | 41 | for (size_t i = kernel_address.response->physical_base, j = 0; i < kernel_address.response->physical_base + GiB(1); i += PageSize::Size4K, j += PageSize::Size4K) 42 | self->map(j + kernel_address.response->virtual_base, i, AccessFlags::ReadWrite, PageSize::Size4K); 43 | 44 | self->loadAddressSpace(); 45 | 46 | logLine << "vmm: Initialized" << fmt::endl; 47 | } 48 | } // namespace firefly::kernel::mm 49 | -------------------------------------------------------------------------------- /firefly/kernel/stubs.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "cstdlib/cassert.h" 5 | #include "firefly/memory-manager/primary/primary_phys.hpp" 6 | #include "firefly/memory-manager/secondary/heap.hpp" 7 | #include "firefly/panic.hpp" 8 | 9 | /* 10 | All kinds of stubs for functions that prevented a successful linking process reside here. 11 | */ 12 | extern "C" { 13 | int __cxa_atexit([[maybe_unused]] void (*func)(void *), [[maybe_unused]] void *arg, [[maybe_unused]] void *dso_handle) { 14 | return 0; 15 | } 16 | 17 | void *alloc_mem(size_t size) { 18 | return firefly::kernel::mm::Physical::must_allocate(size); 19 | } 20 | 21 | void free_mem(void *ptr, [[maybe_unused]] size_t size) { 22 | firefly::kernel::mm::Physical::deallocate(ptr); 23 | } 24 | 25 | void __stack_chk_fail(void) { 26 | } 27 | 28 | void FRG_INTF(log)(const char *cstring) { 29 | } 30 | 31 | void FRG_INTF(panic)(const char *cstring) { 32 | firefly::panic(cstring); 33 | } 34 | } 35 | 36 | void *operator new(size_t sz) { 37 | return firefly::kernel::mm::heap->allocate(sz); 38 | } 39 | 40 | void operator delete(void *ptr, [[gnu::unused]] size_t size) { 41 | firefly::kernel::mm::heap->deallocate(ptr); 42 | } 43 | -------------------------------------------------------------------------------- /firefly/kernel/tasks/scheduler.cpp: -------------------------------------------------------------------------------- 1 | #include "firefly/tasks/scheduler.hpp" 2 | 3 | namespace firefly::kernel::tasks { 4 | 5 | frg::manual_box schedSingleton; 6 | Scheduler& Scheduler::accessor() { 7 | return *schedSingleton; 8 | } 9 | void Scheduler::init() { 10 | schedSingleton.initialize(); 11 | } 12 | } // namespace firefly::kernel::tasks 13 | -------------------------------------------------------------------------------- /firefly/kernel/timer/timer.cpp: -------------------------------------------------------------------------------- 1 | #include "firefly/timer/timer.hpp" 2 | 3 | #include "firefly/intel64/cpu/apic/apic.hpp" 4 | #include "firefly/intel64/cpu/cpu.hpp" 5 | #include "firefly/intel64/hpet/hpet.hpp" 6 | #include "firefly/intel64/int/interrupt.hpp" 7 | #include "firefly/intel64/pit/pit.hpp" 8 | #include "firefly/logger.hpp" 9 | #include "firefly/memory-manager/allocator.hpp" 10 | #include "firefly/memory-manager/primary/primary_phys.hpp" 11 | #include "firefly/panic.hpp" 12 | #include "firefly/tasks/scheduler.hpp" 13 | 14 | namespace firefly::kernel { 15 | // TODO: rework this, probably a better solution 16 | namespace timer { 17 | 18 | namespace { 19 | // This will just get increased 20 | static volatile uint64_t ticks = 0; 21 | static volatile uint32_t ticks_20ms = 0; 22 | } // namespace 23 | 24 | void timer_irq(ContextRegisters* stack) { 25 | if (tasks::Scheduler::accessor().getTask() == nullptr) { 26 | tasks::Scheduler::accessor().schedule(); 27 | tasks::Scheduler::accessor().getTask()->load(stack); 28 | } else { 29 | tasks::Scheduler::accessor().getTask()->save(stack); 30 | tasks::Scheduler::accessor().schedule(); 31 | tasks::Scheduler::accessor().getTask()->load(stack); 32 | } 33 | start(); 34 | } 35 | 36 | void start() { 37 | apic::ApicTimer::accessor().oneShotTimer(ticks_20ms); 38 | } 39 | 40 | void init() { 41 | resetTicks(); 42 | HPET::init(); 43 | core::interrupt::registerIRQHandler(timer_irq, 0); 44 | if (apic::ApicTimer::isAvailable()) { 45 | apic::ApicTimer::init(); 46 | ticks_20ms = 200 * apic::ApicTimer::accessor().calibrate(100); 47 | } else { 48 | pit::init(); 49 | } 50 | 51 | return; 52 | // panic("No usable timer found"); 53 | } 54 | 55 | 56 | // Probably call this function in the IRQ handler, or whatever the timer fires 57 | void tick() { 58 | ticks = ticks + 1; 59 | } 60 | 61 | void resetTicks() { 62 | ticks = 0; 63 | } 64 | 65 | void msleep(uint64_t ms) { 66 | usleep(ms * 1000); 67 | } 68 | 69 | void usleep(uint64_t us) { 70 | HPET::accessor().usleep(us); 71 | } 72 | 73 | 74 | } // namespace timer 75 | } // namespace firefly::kernel 76 | -------------------------------------------------------------------------------- /firefly/kernel/trace/sanitizer/kasan.cpp: -------------------------------------------------------------------------------- 1 | #include "firefly/trace/sanitizer/kasan.hpp" 2 | 3 | #include 4 | #include 5 | 6 | #include "cstdlib/cassert.h" 7 | #include "firefly/compiler/compiler.hpp" 8 | #include "firefly/drivers/ports.hpp" 9 | #include "firefly/logger.hpp" 10 | #include "firefly/memory-manager/virtual/virtual.hpp" 11 | #include "firefly/panic.hpp" 12 | #include "libk++/bits.h" 13 | #include "libk++/cstring.hpp" 14 | #include "libk++/memory.hpp" 15 | 16 | /* 17 | Documentation is scarce, I found these resources to be helpful: 18 | - https://github.com/xairy/kernel-sanitizers/blob/asan/arch/x86/mm/asan/asan.c - This was an early test when kasan was just being integrated into the linux kernel. 19 | - https://www.starlab.io/blog/kasan-what-is-it-how-does-it-work-and-what-are-the-strange-numbers-at-the-end 20 | */ 21 | 22 | namespace { 23 | using namespace firefly::kernel; 24 | 25 | constexpr bool verboseKasan = true; 26 | constexpr bool debugKasan = false; 27 | constexpr uint8_t kasanDumpSize = 16; // Dump 16 bits / 2 bytes at a time. 28 | constexpr uint64_t kasanShadowOffset = 0xdfff'f000'0000'0000; // Turns into a canonical address in {to,from}Shadow 29 | constexpr uint8_t kasanShift = 3; 30 | constexpr uint8_t kasanAccessSize = 1 << kasanShift; // 8 31 | constexpr uint32_t kasanHighShadowSize = (GiB(4) / 8); // Temporary. Should be the size of the kernel heap (which does not exist yet) divided by 8. 32 | 33 | #if !defined(FIRELY_KASAN) 34 | [[maybe_unused]] 35 | #endif 36 | bool 37 | kasanUserAddress(VirtualAddress ptr) { 38 | return (reinterpret_cast(ptr) & (1ul << 63)) == 0; 39 | } 40 | 41 | uint8_t *toShadow(VirtualAddress ptr) { 42 | return reinterpret_cast( 43 | (reinterpret_cast(ptr) >> kasanShift) + kasanShadowOffset); 44 | } 45 | 46 | VirtualAddress fromShadow(kasan::KasanAddress shadow) { 47 | return reinterpret_cast((shadow - kasanShadowOffset) << kasanShift); 48 | } 49 | 50 | // Q&D bounds-check (temporary, should be the kernel heap). 51 | bool withinKasanMemory(kasan::KasanAddress addr) { 52 | if (addr >= AddressLayout::High && addr <= AddressLayout::High + GiB(4)) 53 | return true; 54 | 55 | return false; 56 | } 57 | 58 | #if !defined(FIRELY_KASAN) 59 | [[maybe_unused]] 60 | #endif 61 | void 62 | mapShadowMemory(VirtualAddress base, size_t length) { 63 | using namespace core::paging; 64 | 65 | auto shadow = uint64_t(toShadow(base)); 66 | if constexpr (verboseKasan) { 67 | firefly::logLine << "KASAN: Mapping kasan shadow " << firefly::fmt::hex << reinterpret_cast(base) << " at " << firefly::fmt::hex << shadow << '\n' 68 | << firefly::fmt::endl; 69 | } 70 | 71 | // TODO: Is this the right way to go about this..? 72 | // Managarm does this too when mapping kasan memory, it's just that this uses 73 | // so much memory... 74 | for (size_t i = 0; i <= length; i += PageSize::Size4K) { 75 | auto phys_addr = uint64_t(mm::Physical::allocate()); 76 | mm::kernelPageSpace::accessor().map(shadow + i, phys_addr, AccessFlags::ReadWrite, PageSize::Size4K); 77 | } 78 | } 79 | } // namespace 80 | 81 | namespace firefly::kernel::kasan { 82 | frg::init_once kasan_initialized{ false }; 83 | 84 | void init() { 85 | #if defined(FIREFLY_KASAN) 86 | // NOTE: We're using AddressLayout::High as a test here. It should just shadow memory such as the heap or any buffers. 87 | mapShadowMemory(VirtualAddress(AddressLayout::High), kasanHighShadowSize); 88 | unpoison(VirtualAddress(AddressLayout::High), kasanHighShadowSize); 89 | logLine << "KASAN: Initialized\n" 90 | << fmt::endl; 91 | kasan_initialized = true; 92 | #else 93 | logLine << "KASAN is disabled\n" 94 | << fmt::endl; 95 | #endif 96 | } 97 | 98 | [[gnu::no_sanitize_address]] void poison(VirtualAddress addr, size_t size, ShadowRegionStatus status) { 99 | #if defined(FIREFLY_KASAN) 100 | assert_truth(!kasanUserAddress(addr) && "Kasan cannot poison user pages yet!"); 101 | memset(static_cast(toShadow(addr)), static_cast(status), size >> kasanShift); 102 | #else 103 | (void)addr; 104 | (void)size; 105 | (void)status; 106 | #endif 107 | } 108 | 109 | [[gnu::no_sanitize_address]] void unpoison(VirtualAddress addr, size_t size) { 110 | #if defined(FIREFLY_KASAN) 111 | poison(addr, size, ShadowRegionStatus::Unpoisoned); 112 | #else 113 | (void)addr; 114 | (void)size; 115 | #endif 116 | } 117 | 118 | [[gnu::no_sanitize_address]] inline bool isPoisoned(KasanAddress addr, size_t size) { 119 | auto shadow = toShadow(VirtualAddress(addr)); 120 | for (size_t i = 0; i < (size >> kasanShift); i++) { 121 | if (shadow[i] == static_cast(ShadowRegionStatus::Poisoned)) 122 | return true; 123 | } 124 | 125 | return false; 126 | } 127 | 128 | [[gnu::no_sanitize_address]] void verifyAccess(KasanAddress addr, size_t size, bool write) { 129 | if (!kasan_initialized || !withinKasanMemory(addr)) 130 | return; 131 | 132 | // TODO: This should be offered by console.hpp or logger.hpp 133 | constexpr auto color_red = "\033[31m"; 134 | constexpr auto color_reset = "\033[39m"; 135 | 136 | // Dump 'len' bytes of shadow memory and highlight all bytes in a range from shadow_bytes_{start,end}. 137 | const auto dumpShadowMemory = [=](KasanAddress base, size_t len, auto shadow_bytes_start, auto shadow_bytes_end) { 138 | logLine << fmt::hex << reinterpret_cast(fromShadow(base)) << ": "; 139 | for (size_t i = 0; i < len; i++) { 140 | logLine << ((base + i) >= shadow_bytes_start && (base + i) <= shadow_bytes_end ? color_red : color_reset) 141 | << fmt::hex << (*reinterpret_cast(base + i) & 0xff) << ' '; 142 | } 143 | 144 | logLine << color_reset << '\n' 145 | << fmt::endl; 146 | }; 147 | 148 | static int recursion; 149 | if (++recursion == 1) { 150 | if (isPoisoned(addr, size)) { 151 | logLine << "\n==== " << color_red << "Kasan detected a memory access violation" << color_reset << " ====\n" 152 | << (write ? "Write to " : "Read from ") << "poisoned address " << fmt::hex << addr << " of size " << size << '\n' 153 | << fmt::endl; 154 | 155 | logLine << "Memory state around the buggy address:\n" 156 | << fmt::endl; 157 | 158 | // Shadow memory at the address `start => end` is the poisoned memory. 159 | auto shadow = uint64_t(toShadow(VirtualAddress(addr))); 160 | const KasanAddress shadow_bytes_start = shadow; 161 | const KasanAddress shadow_bytes_end = shadow + (size >> kasanShift); 162 | if constexpr (debugKasan) 163 | logLine << "KASAN-DBG: shadow_start=" << fmt::hex << shadow_bytes_start << ", shadow_end=" << fmt::hex << shadow_bytes_end << '\n' 164 | << fmt::endl; 165 | 166 | logLine << '\n' 167 | << fmt::endl; 168 | bool had_to_break = false; 169 | for (int i = 0; i < 3; i++) { 170 | if ((AddressLayout::High + (kasanDumpSize * i)) >= addr) { 171 | had_to_break = true; 172 | break; 173 | } 174 | 175 | dumpShadowMemory(KasanAddress(toShadow(VirtualAddress(AddressLayout::High + (kasanDumpSize * i)))), kasanDumpSize, shadow_bytes_start, shadow_bytes_end); 176 | } 177 | if (!had_to_break) 178 | logLine << "\n" 179 | << fmt::endl; 180 | 181 | dumpShadowMemory(shadow, kasanDumpSize, shadow_bytes_start, shadow_bytes_end); 182 | panic("kasan access violation"); 183 | } 184 | } 185 | recursion--; 186 | } 187 | } // namespace firefly::kernel::kasan 188 | -------------------------------------------------------------------------------- /firefly/kernel/trace/sanitizer/kasan_methods.cpp: -------------------------------------------------------------------------------- 1 | #include "firefly/logger.hpp" 2 | #include "firefly/memory-manager/virtual/virtual.hpp" 3 | #include "firefly/trace/sanitizer/kasan.hpp" 4 | #include "libk++/bits.h" 5 | 6 | extern "C" void __asan_loadN_noabort(void* p, size_t size) { 7 | firefly::kernel::kasan::verifyAccess(reinterpret_cast(p), size, false); 8 | } 9 | 10 | extern "C" void __asan_storeN_noabort(void* p, int size) { 11 | firefly::kernel::kasan::verifyAccess(reinterpret_cast(p), size, true); 12 | } 13 | 14 | extern "C" void __asan_load1_noabort(void* p) { 15 | firefly::kernel::kasan::verifyAccess(reinterpret_cast(p), 1, false); 16 | } 17 | extern "C" void __asan_load2_noabort(void* p) { 18 | firefly::kernel::kasan::verifyAccess(reinterpret_cast(p), 2, false); 19 | } 20 | extern "C" void __asan_load4_noabort(void* p) { 21 | firefly::kernel::kasan::verifyAccess(reinterpret_cast(p), 4, false); 22 | } 23 | extern "C" void __asan_load8_noabort(void* p) { 24 | firefly::kernel::kasan::verifyAccess(reinterpret_cast(p), 8, false); 25 | } 26 | extern "C" void __asan_load16_noabort(void* p) { 27 | firefly::kernel::kasan::verifyAccess(reinterpret_cast(p), 16, false); 28 | } 29 | extern "C" void __asan_store1_noabort(void* p) { 30 | firefly::kernel::kasan::verifyAccess(reinterpret_cast(p), 1, true); 31 | } 32 | 33 | extern "C" void __asan_store2_noabort(void* p) { 34 | firefly::kernel::kasan::verifyAccess(reinterpret_cast(p), 2, true); 35 | } 36 | 37 | extern "C" void __asan_store4_noabort(void* p) { 38 | firefly::kernel::kasan::verifyAccess(reinterpret_cast(p), 4, true); 39 | } 40 | 41 | extern "C" void __asan_store8_noabort(void* p) { 42 | firefly::kernel::kasan::verifyAccess(reinterpret_cast(p), 8, true); 43 | } 44 | extern "C" void __asan_store16_noabort(void* p) { 45 | firefly::kernel::kasan::verifyAccess(reinterpret_cast(p), 16, true); 46 | } 47 | 48 | extern "C" void __asan_handle_no_return() { 49 | // unused 50 | } 51 | 52 | extern "C" void __asan_before_dynamic_init(const char* module_name) { 53 | // unused 54 | } 55 | 56 | extern "C" void __asan_after_dynamic_init() { 57 | // unused 58 | } -------------------------------------------------------------------------------- /firefly/kernel/trace/sanitizer/ubsan.cpp: -------------------------------------------------------------------------------- 1 | 2 | 3 | /* 4 | BSD 2-Clause License 5 | Copyright (c) 2021, Abb1x 6 | All rights reserved. 7 | Redistribution and use in source and binary forms, with or without 8 | modification, are permitted provided that the following conditions are met: 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 2. Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | */ 25 | #include 26 | #include 27 | 28 | #include "firefly/logger.hpp" 29 | 30 | struct tu_source_location { 31 | const char *file; 32 | uint32_t line; 33 | uint32_t column; 34 | }; 35 | 36 | struct tu_type_descriptor { 37 | uint16_t kind; 38 | uint16_t info; 39 | char name[]; 40 | }; 41 | 42 | struct tu_overflow_data { 43 | struct tu_source_location location; 44 | struct tu_type_descriptor *type; 45 | }; 46 | 47 | struct tu_shift_out_of_bounds_data { 48 | struct tu_source_location location; 49 | struct tu_type_descriptor *left_type; 50 | struct tu_type_descriptor *right_type; 51 | }; 52 | 53 | struct tu_invalid_value_data { 54 | struct tu_source_location location; 55 | struct tu_type_descriptor *type; 56 | }; 57 | 58 | struct tu_array_out_of_bounds_data { 59 | struct tu_source_location location; 60 | struct tu_type_descriptor *array_type; 61 | struct tu_type_descriptor *index_type; 62 | }; 63 | 64 | struct tu_type_mismatch_v1_data { 65 | struct tu_source_location location; 66 | struct tu_type_descriptor *type; 67 | unsigned char log_alignment; 68 | unsigned char type_check_kind; 69 | }; 70 | 71 | struct tu_negative_vla_data { 72 | struct tu_source_location location; 73 | struct tu_type_descriptor *type; 74 | }; 75 | 76 | struct tu_nonnull_return_data { 77 | struct tu_source_location location; 78 | }; 79 | 80 | struct tu_nonnull_arg_data { 81 | struct tu_source_location location; 82 | }; 83 | 84 | struct tu_unreachable_data { 85 | struct tu_source_location location; 86 | }; 87 | 88 | struct tu_invalid_builtin_data { 89 | struct tu_source_location location; 90 | unsigned char kind; 91 | }; 92 | 93 | extern "C" { 94 | static void tu_print_location(const char *message, struct tu_source_location loc) { 95 | using namespace firefly; 96 | logLine << "tinyubsan: " << message 97 | << " at file " << loc.file 98 | << ", line " << fmt::dec << loc.line 99 | << ", column " << loc.column << '\n' 100 | << fmt::endl; 101 | } 102 | 103 | void __ubsan_handle_add_overflow(struct tu_overflow_data *data) { 104 | tu_print_location("addition overflow", data->location); 105 | } 106 | 107 | void __ubsan_handle_sub_overflow(struct tu_overflow_data *data) { 108 | tu_print_location("subtraction overflow", data->location); 109 | } 110 | 111 | void __ubsan_handle_mul_overflow(struct tu_overflow_data *data) { 112 | tu_print_location("multiplication overflow", data->location); 113 | } 114 | 115 | void __ubsan_handle_divrem_overflow(struct tu_overflow_data *data) { 116 | tu_print_location("division overflow", data->location); 117 | } 118 | 119 | void __ubsan_handle_negate_overflow(struct tu_overflow_data *data) { 120 | tu_print_location("negation overflow", data->location); 121 | } 122 | 123 | void __ubsan_handle_pointer_overflow(struct tu_overflow_data *data) { 124 | tu_print_location("pointer overflow", data->location); 125 | } 126 | 127 | void __ubsan_handle_shift_out_of_bounds(struct tu_shift_out_of_bounds_data *data) { 128 | tu_print_location("shift out of bounds", data->location); 129 | } 130 | 131 | void __ubsan_handle_load_invalid_value(struct tu_invalid_value_data *data) { 132 | tu_print_location("invalid load value", data->location); 133 | } 134 | 135 | void __ubsan_handle_out_of_bounds(struct tu_array_out_of_bounds_data *data) { 136 | tu_print_location("array out of bounds", data->location); 137 | } 138 | 139 | void __ubsan_handle_type_mismatch_v1(struct tu_type_mismatch_v1_data *data, uintptr_t ptr) { 140 | if (!ptr) { 141 | tu_print_location("use of NULL pointer", data->location); 142 | } 143 | 144 | else if (ptr & ((1 << data->log_alignment) - 1)) { 145 | tu_print_location("use of misaligned pointer", data->location); 146 | } else { 147 | tu_print_location("no space for object", data->location); 148 | } 149 | } 150 | 151 | void __ubsan_handle_vla_bound_not_positive(struct tu_negative_vla_data *data) { 152 | tu_print_location("variable-length argument is negative", data->location); 153 | } 154 | 155 | void __ubsan_handle_nonnull_return(struct tu_nonnull_return_data *data) { 156 | tu_print_location("non-null return is null", data->location); 157 | } 158 | 159 | void __ubsan_handle_nonnull_arg(struct tu_nonnull_arg_data *data) { 160 | tu_print_location("non-null argument is null", data->location); 161 | } 162 | 163 | void __ubsan_handle_builtin_unreachable(struct tu_unreachable_data *data) { 164 | tu_print_location("unreachable code reached", data->location); 165 | } 166 | 167 | void __ubsan_handle_invalid_builtin(struct tu_invalid_builtin_data *data) { 168 | tu_print_location("invalid builtin", data->location); 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /firefly/kernel/trace/strace.cpp: -------------------------------------------------------------------------------- 1 | #include "firefly/logger.hpp" 2 | #include "firefly/trace/symbols.hpp" 3 | 4 | namespace firefly::trace { 5 | 6 | struct stackframe { 7 | struct stackframe *frame; 8 | uint64_t rip; 9 | }; 10 | 11 | void trace_callstack() { 12 | logLine << "Stack trace:\n" 13 | << fmt::endl; 14 | 15 | stackframe *stkf; 16 | asm("mov %%rbp, %0" 17 | : "=r"(stkf)); 18 | 19 | for (int i = 0; i < 10; i++) { 20 | if (!backtrace(stkf->rip, i)) 21 | return; 22 | 23 | stkf = stkf->frame; 24 | } 25 | } 26 | } // namespace firefly::trace 27 | -------------------------------------------------------------------------------- /firefly/kernel/trace/symbols.cpp: -------------------------------------------------------------------------------- 1 | #include "firefly/trace/symbols.hpp" 2 | 3 | #include "firefly/logger.hpp" 4 | #include "libk++/cstring.hpp" 5 | 6 | extern "C" SymbolTablePair symbol_table[]; 7 | 8 | SymbolTablePair SymbolTable::operator[](uint64_t addr) const noexcept { 9 | return lookup(addr); 10 | } 11 | // Parse the symbol table and find the correct address of RIP 12 | SymbolTablePair SymbolTable::lookup(uint64_t addr) const noexcept { 13 | // We need the largest address that is smaller than `addr` because of the jsr instruction, otherwise the addresses are off 14 | uint64_t corrected_address = 0; 15 | uint64_t index_new = 0; 16 | 17 | for (int i = 0;; i++) { 18 | uint64_t sym_addr = symbol_table[i].addr; 19 | if (corrected_address >= addr) 20 | continue; 21 | 22 | else if (sym_addr < addr) { 23 | if (sym_addr > corrected_address) { 24 | corrected_address = sym_addr; 25 | index_new = i; 26 | } 27 | } 28 | 29 | if (sym_addr == 0xFFFFFFFFFFFFFFFF) 30 | return { corrected_address, symbol_table[index_new].name }; 31 | } 32 | } 33 | 34 | 35 | bool backtrace(uint64_t addr, int iteration) { 36 | SymbolTable table{}; 37 | auto const& [base, name] = table[addr]; 38 | 39 | firefly::logLine << "#" << iteration << " " << firefly::fmt::hex << base << " \t" << name << '\n' 40 | << firefly::fmt::endl; 41 | 42 | /* Don't trace symbols below kernel_init */ 43 | if (firefly::libkern::cstring::strcmp(name, "kernel_init") == 0) 44 | return false; 45 | 46 | return true; 47 | } 48 | -------------------------------------------------------------------------------- /firefly/libk++/cstring.cpp: -------------------------------------------------------------------------------- 1 | #include "libk++/cstring.hpp" 2 | 3 | namespace firefly::libkern::cstring { 4 | 5 | size_t strlen(const char *str) { 6 | size_t n = 0; 7 | while (*str++) 8 | n++; 9 | return n; 10 | } 11 | 12 | int strcmp(const char *s1, const char *s2) { 13 | int index = 0; 14 | while (s1[index] && s2[index] && s1[index] == s2[index]) 15 | index++; 16 | 17 | if (s1[index] == '\0' && s2[index] == '\0') 18 | return 0; 19 | else 20 | return s1[index] - s2[index]; 21 | } 22 | 23 | int strncmp(const char *str1, const char *str2, size_t n) { 24 | for (size_t i = 0; i < n && str1; i++) { 25 | if (str1[i] != str2[i]) 26 | return -1; 27 | } 28 | return 0; 29 | } 30 | 31 | char *strcpy(char *dest, const char *src) { 32 | char *ret = dest; 33 | 34 | int i = 0; 35 | do { 36 | dest[i] = src[i]; 37 | i++; 38 | } while (src[i] != '\0'); 39 | dest[i] = src[i]; 40 | 41 | return ret; 42 | } 43 | 44 | char *strchr(const char *str, int c) { 45 | for (int i = 0; str[i]; i++) 46 | if (str[i] == (char)c) 47 | return (char *)&str[i]; 48 | return nullptr; 49 | } 50 | 51 | char *strchrn(const char *str, int c, int n) { 52 | for (int i = 0; str[i] && n--; i++) 53 | if (str[i] == (char)c) 54 | return (char *)&str[i]; 55 | return nullptr; 56 | } 57 | 58 | int toupper(char c) { 59 | if (c >= 'a' && c <= 'z') 60 | return c - 32; 61 | else 62 | return c; 63 | } 64 | 65 | char *strtok(char *s, const char *delimiters) { 66 | static char *next = nullptr; 67 | 68 | if (s != nullptr) 69 | next = s; 70 | 71 | if (next == nullptr || *next == '\0') 72 | return nullptr; 73 | 74 | char *c = next; 75 | while (*c != '\0') { 76 | for (const char *comp = delimiters; *comp != '\0'; comp++) { 77 | if (*comp == *c) { 78 | char *token = next; 79 | next = c + 1; 80 | *c = '\0'; 81 | return token; 82 | } 83 | } 84 | 85 | c++; 86 | } 87 | 88 | char *token = next; 89 | next = c; 90 | return token; 91 | } 92 | } // namespace firefly::libkern::cstring -------------------------------------------------------------------------------- /firefly/libk++/fmt.cpp: -------------------------------------------------------------------------------- 1 | #include "libk++/fmt.hpp" 2 | 3 | #include 4 | 5 | #include "cstdlib/cassert.h" 6 | 7 | namespace firefly { 8 | 9 | char format::itoc(int num) { 10 | return '0' + num; 11 | } 12 | 13 | char format::itoh(int num, bool upper) { 14 | if (upper) 15 | return num - 10 + 'A'; 16 | return num - 10 + 'a'; 17 | } 18 | 19 | char* format::reverse(char* s) { 20 | char temp; 21 | int src_string_index = 0; 22 | int last_char = len(s) - 1; 23 | 24 | for (; src_string_index < last_char; src_string_index++) { 25 | temp = s[src_string_index]; // Save current character 26 | s[src_string_index] = 27 | s[last_char]; // Swap out the current char with the last char 28 | s[last_char] = temp; // Swap out last character with the current character 29 | last_char--; 30 | } 31 | 32 | s[len(s) - 1 + 1] = '\0'; 33 | 34 | return s; 35 | } 36 | 37 | void format::itoa(size_t num, char* s, int base) { 38 | size_t buffer_sz = 20; 39 | size_t counter = 0; 40 | size_t digit = 0; 41 | 42 | if (num == 0) { 43 | s[0] = '0'; 44 | s[1] = '\0'; 45 | return; 46 | } 47 | 48 | while (num != 0 && counter < buffer_sz) { 49 | digit = (num % base); 50 | if (digit > 9) 51 | s[counter++] = itoh(digit, false); 52 | else 53 | s[counter++] = itoc(digit); 54 | 55 | num /= base; 56 | } 57 | 58 | s[counter] = '\0'; 59 | reverse(s); 60 | } 61 | 62 | size_t format::len(const char* s) { 63 | size_t n = 0; 64 | 65 | while (*s++) 66 | n++; 67 | 68 | return n; 69 | } 70 | 71 | void format::do_format(char in) { 72 | appendToBuffer(in); 73 | } 74 | 75 | void format::do_format(char* in) { 76 | appendToBuffer(in); 77 | } 78 | 79 | void format::do_format(const char* in) { 80 | appendToBuffer(in); 81 | } 82 | 83 | void format::appendToBuffer(char c) { 84 | assert_truth(writer_position < BUFF_LEN && "Prevented buffer overflow"); 85 | buffer[writer_position++] = c; 86 | } 87 | 88 | void format::appendToBuffer(const char* s) { 89 | while (*s != '\0') { 90 | assert_truth(writer_position < BUFF_LEN && "Prevented buffer overflow"); 91 | buffer[writer_position++] = *s++; 92 | } 93 | } 94 | }; // namespace firefly 95 | -------------------------------------------------------------------------------- /firefly/libk++/memory.cpp: -------------------------------------------------------------------------------- 1 | #include "libk++/memory.hpp" 2 | 3 | void *memset(void *dest, int val, size_t len) { 4 | uint8_t *ptr = (uint8_t *)dest; 5 | 6 | while (len--) { 7 | *ptr++ = val; 8 | } 9 | 10 | return dest; 11 | } 12 | 13 | void *memcpy(void *dest, const void *src, size_t len) { 14 | uint8_t *_dest = (uint8_t *)dest; 15 | uint8_t *_src = (uint8_t *)src; 16 | 17 | for (; len != 0; len--) { 18 | *_dest = *_src; 19 | 20 | _dest++; 21 | _src++; 22 | } 23 | 24 | return dest; 25 | } 26 | 27 | int memcmp(const char *s1, const char *s2, int n) { 28 | int index = 0; 29 | while (s1[index] && s2[index] && s1[index] == s2[index] && n--) 30 | index++; 31 | 32 | if (n == 0) 33 | return 0; 34 | else 35 | return s1[index] - s2[index]; 36 | } -------------------------------------------------------------------------------- /firefly/libk++/utils.cpp: -------------------------------------------------------------------------------- 1 | #include "libk++/utils.hpp" 2 | 3 | #include 4 | 5 | namespace firefly::kernel::util { 6 | uint32_t rev32(uint32_t bytes) { 7 | return ((bytes & 0xFF) << 16) | (((bytes >> 8) & 0xFF) << 8) | (((bytes >> 16) & 0xFF)); 8 | } 9 | 10 | //Converts a single (B,G,R)(array) color pair into an RGB value (uint32_t) 11 | uint32_t bgr2rgb(uint32_t bytes[], int offset) { 12 | uint32_t concatenated = (bytes[offset + 0] << 16) | (bytes[offset + 1] << 8) | (bytes[offset + 2]); 13 | return rev32(concatenated); 14 | } 15 | } // namespace firefly::kernel::util -------------------------------------------------------------------------------- /firefly/meson.build: -------------------------------------------------------------------------------- 1 | kernel_cxx_flags = [ 2 | '-std=c++20', 3 | '-ffreestanding', 4 | '-g', '-O2', 5 | '-I include/', 6 | '-I include/cxxshim', 7 | '-I include/frigg', 8 | '-ffreestanding', 9 | '-fno-stack-protector', 10 | '-fno-stack-check', 11 | '-fno-pie', 12 | '-fno-pic', 13 | '-m64', 14 | '-mno-80387', 15 | '-mno-mmx', 16 | '-mno-sse', 17 | '-mno-sse2', 18 | '-mno-red-zone', 19 | '-mcmodel=kernel', 20 | '-fno-exceptions', 21 | '-fno-rtti', 22 | '-mno-3dnow', 23 | '-fno-omit-frame-pointer' 24 | ] 25 | 26 | kernel_c_flags = [ 27 | '-std=gnu11', 28 | '-ffreestanding', '-g', 29 | '-I include', 30 | '-ffreestanding', 31 | '-fno-stack-protector', 32 | '-fno-stack-check', 33 | '-fno-pie', 34 | '-fno-pic', 35 | '-m64', 36 | '-march=x86-64', 37 | '-mabi=sysv', 38 | '-mno-80387', 39 | '-mno-mmx', 40 | '-mno-sse', 41 | '-mno-sse2', 42 | '-mno-red-zone', 43 | '-mcmodel=kernel', 44 | '-fno-omit-frame-pointer' 45 | ] 46 | 47 | c_files += files( 48 | 'kernel/console/gterm.c', 'kernel/console/image.c', 49 | 'kernel/console/term.c' 50 | ) 51 | 52 | cxx_files += files('libk++/fmt.cpp', 'libk++/memory.cpp', 'libk++/utils.cpp', 'libk++/cstring.cpp') 53 | cxx_files += files( 54 | 'kernel/kernel.cpp', 'kernel/stubs.cpp', 'kernel/drivers/ps2.cpp', 'kernel/logger.cpp', 55 | 'kernel/drivers/serial.cpp', 'kernel/intel64/int/interrupt.cpp', 'kernel/memory-manager/primary/primary_phys.cpp', 56 | 'kernel/intel64/gdt/gdt.cpp', 'kernel/intel64/gdt/tss.cpp', 'kernel/init/init.cpp', 'kernel/boot/boot_mem.cpp', 57 | 'kernel/trace/strace.cpp', 'kernel/trace/symbols.cpp', 'kernel/memory-manager/virtual/virtual.cpp', 58 | 'kernel/console/console.cpp', 'kernel/intel64/acpi/acpi.cpp', 'kernel/graphics/framebuffer.cpp', 59 | 'kernel/intel64/paging.cpp', 'kernel/intel64/cpu/cpu.cpp', 'kernel/intel64/cpu/ap/ap.cpp', 60 | 'kernel/intel64/cpu/apic/apic.cpp', 'kernel/intel64/pit/pit.cpp', 61 | 'kernel/intel64/cpu/apic/ioapic.cpp', 'kernel/intel64/cpu/cpu.cpp', 'kernel/trace/sanitizer/kasan.cpp', 62 | 'kernel/memory-manager/secondary/heap.cpp', 63 | 'kernel/tasks/scheduler.cpp', 64 | 'kernel/timer/timer.cpp', 'kernel/intel64/hpet/hpet.cpp' 65 | ) 66 | asm_files += files('kernel/intel64/gdt/gdt.asm', 'kernel/intel64/int/interrupt.asm') 67 | -------------------------------------------------------------------------------- /fonts/unifont.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FireflyOS/Firefly-Kernel/440588f7535fcfa1890bbb79c6d55af05d4c9fc9/fonts/unifont.bin -------------------------------------------------------------------------------- /fonts/vgafont.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FireflyOS/Firefly-Kernel/440588f7535fcfa1890bbb79c6d55af05d4c9fc9/fonts/vgafont.bin -------------------------------------------------------------------------------- /fonts/vgafont.obj: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FireflyOS/Firefly-Kernel/440588f7535fcfa1890bbb79c6d55af05d4c9fc9/fonts/vgafont.obj -------------------------------------------------------------------------------- /include/cstdlib/cassert.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "firefly/panic.hpp" 4 | 5 | #define assert(condition) assert_truth(condition) 6 | 7 | #define assert_truth(condition) \ 8 | do { \ 9 | if (!(condition)) \ 10 | firefly::assertion_failure_panic(#condition); \ 11 | } while (0) 12 | -------------------------------------------------------------------------------- /include/cstdlib/cmath.cpp: -------------------------------------------------------------------------------- 1 | #include "cmath.h" 2 | 3 | size_t pow(size_t base, size_t exponent) { 4 | size_t temp; 5 | if (! exponent) { 6 | return 1; 7 | } 8 | temp = pow(base, exponent / 2); 9 | if ((exponent % 2) == 0) { 10 | return temp * temp; 11 | } else { 12 | if (exponent > 0) 13 | return base * temp * temp; 14 | else 15 | return (temp * temp) / base; 16 | } 17 | } 18 | 19 | // https://stackoverflow.com/questions/11376288/fast-computing-of-log2-for-64-bit-integers 20 | int log2(uint64_t value) 21 | { 22 | static const int tab64[64] = { 23 | 63, 0, 58, 1, 59, 47, 53, 2, 24 | 60, 39, 48, 27, 54, 33, 42, 3, 25 | 61, 51, 37, 40, 49, 18, 28, 20, 26 | 55, 30, 34, 11, 43, 14, 22, 4, 27 | 62, 57, 46, 52, 38, 26, 32, 41, 28 | 50, 36, 17, 19, 29, 10, 13, 21, 29 | 56, 45, 25, 31, 35, 16, 9, 12, 30 | 44, 24, 15, 8, 23, 7, 6, 5}; 31 | value |= value >> 1; 32 | value |= value >> 2; 33 | value |= value >> 4; 34 | value |= value >> 8; 35 | value |= value >> 16; 36 | value |= value >> 32; 37 | return tab64[((uint64_t)((value - (value >> 1))*0x07EDD5E59A4E28C2)) >> 58]; 38 | } -------------------------------------------------------------------------------- /include/cstdlib/cmath.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | size_t pow(size_t base, size_t exponent); 7 | int log2(uint64_t value); 8 | 9 | constexpr inline size_t constexpr_log2(size_t n) { 10 | return ((n < 2) ? 1 : 1 + constexpr_log2(n / 2)); 11 | } -------------------------------------------------------------------------------- /include/firefly/boot/boot_mem.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "firefly/limine.hpp" 4 | 5 | namespace firefly::boot { 6 | void bootMapExtraRegion(limine_memmap_response *mmap); 7 | } // namespace firefly::boot 8 | -------------------------------------------------------------------------------- /include/firefly/compiler/compiler.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define PACKED __attribute__((packed)) 4 | #define USED __attribute__((used)) 5 | #define likely(x) __builtin_expect(!!(x), 1) 6 | #define unlikely(x) __builtin_expect(!!(x), 0) 7 | -------------------------------------------------------------------------------- /include/firefly/console/console.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace firefly::kernel::console 4 | { 5 | void init(); 6 | [[gnu::no_sanitize_address]] void write(const char *str); 7 | } // namespace firefly::kernel::device::console 8 | -------------------------------------------------------------------------------- /include/firefly/console/gterm.h: -------------------------------------------------------------------------------- 1 | #ifndef GTERM_H 2 | #define GTERM_H 3 | 4 | #include "term.h" 5 | 6 | #ifdef __cplusplus 7 | extern "C" { 8 | #endif 9 | 10 | struct gterm_char 11 | { 12 | uint32_t c; 13 | uint32_t fg; 14 | uint32_t bg; 15 | }; 16 | 17 | struct gterm_queue_item 18 | { 19 | size_t x, y; 20 | struct gterm_char c; 21 | }; 22 | 23 | struct gterm_context 24 | { 25 | uint32_t text_fg; 26 | uint32_t text_bg; 27 | bool cursor_status; 28 | size_t cursor_x; 29 | size_t cursor_y; 30 | bool scroll_enabled; 31 | 32 | uint32_t saved_state_text_fg; 33 | uint32_t saved_state_text_bg; 34 | size_t saved_state_cursor_x; 35 | size_t saved_state_cursor_y; 36 | }; 37 | 38 | struct gterm_t 39 | { 40 | struct framebuffer_t framebuffer; 41 | volatile uint32_t *framebuffer_addr; 42 | 43 | struct term_t *term; 44 | 45 | size_t vga_font_width; 46 | size_t vga_font_height; 47 | size_t font_bytes; 48 | size_t glyph_width; 49 | size_t glyph_height; 50 | 51 | size_t vga_font_scale_x; 52 | size_t vga_font_scale_y; 53 | 54 | size_t offset_x, offset_y; 55 | 56 | uint8_t *vga_font_bits; 57 | size_t vga_font_bool_size; 58 | bool *vga_font_bool; 59 | 60 | uint32_t ansi_colours[8]; 61 | uint32_t ansi_bright_colours[8]; 62 | uint32_t default_fg, default_bg; 63 | 64 | struct image_t *background; 65 | 66 | size_t bg_canvas_size; 67 | uint32_t *bg_canvas; 68 | 69 | size_t rows; 70 | size_t cols; 71 | size_t margin; 72 | size_t margin_gradient; 73 | 74 | size_t grid_size; 75 | size_t queue_size; 76 | size_t map_size; 77 | 78 | struct gterm_char *grid; 79 | 80 | struct gterm_queue_item *queue; 81 | size_t queue_i; 82 | 83 | struct gterm_queue_item **map; 84 | 85 | struct gterm_context context; 86 | 87 | size_t old_cursor_x; 88 | size_t old_cursor_y; 89 | }; 90 | 91 | void gterm_save_state(struct gterm_t *gterm); 92 | void gterm_restore_state(struct gterm_t *gterm); 93 | void gterm_swap_palette(struct gterm_t *gterm); 94 | bool gterm_scroll_disable(struct gterm_t *gterm); 95 | void gterm_scroll_enable(struct gterm_t *gterm); 96 | void gterm_revscroll(struct gterm_t *gterm); 97 | void gterm_scroll(struct gterm_t *gterm); 98 | void gterm_clear(struct gterm_t *gterm, bool move); 99 | void gterm_enable_cursor(struct gterm_t *gterm); 100 | bool gterm_disable_cursor(struct gterm_t *gterm); 101 | void gterm_set_cursor_pos(struct gterm_t *gterm, size_t x, size_t y); 102 | void gterm_get_cursor_pos(struct gterm_t *gterm, size_t *x, size_t *y); 103 | void gterm_move_character(struct gterm_t *gterm, size_t new_x, size_t new_y, size_t old_x, size_t old_y); 104 | void gterm_set_text_fg(struct gterm_t *gterm, size_t fg); 105 | void gterm_set_text_bg(struct gterm_t *gterm, size_t bg); 106 | void gterm_set_text_fg_bright(struct gterm_t *gterm, size_t fg); 107 | void gterm_set_text_bg_bright(struct gterm_t *gterm, size_t bg); 108 | void gterm_set_text_fg_default(struct gterm_t *gterm); 109 | void gterm_set_text_bg_default(struct gterm_t *gterm); 110 | void gterm_double_buffer_flush(struct gterm_t *gterm); 111 | void gterm_putchar(struct gterm_t *gterm, uint8_t c); 112 | 113 | bool gterm_init(struct gterm_t *gterm, struct term_t *term, struct framebuffer_t frm, struct font_t font, struct style_t style, struct background_t back); 114 | void gterm_deinit(struct gterm_t *gterm); 115 | 116 | uint64_t gterm_context_size(struct gterm_t *gterm); 117 | void gterm_context_save(struct gterm_t *gterm, uint64_t ptr); 118 | void gterm_context_restore(struct gterm_t *gterm, uint64_t ptr); 119 | void gterm_full_refresh(struct gterm_t *gterm); 120 | 121 | #ifdef __cplusplus 122 | } 123 | #endif 124 | 125 | #endif // GTERM_H -------------------------------------------------------------------------------- /include/firefly/console/image.h: -------------------------------------------------------------------------------- 1 | #ifndef IMAGE_H 2 | #define IMAGE_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #ifdef __cplusplus 9 | extern "C" { 10 | #endif 11 | 12 | enum image_style 13 | { 14 | CENTERED, 15 | STRETCHED, 16 | TILED 17 | }; 18 | 19 | struct bmp_header 20 | { 21 | uint16_t bf_signature; 22 | uint32_t bf_size; 23 | uint32_t reserved; 24 | uint32_t bf_offset; 25 | 26 | uint32_t bi_size; 27 | uint32_t bi_width; 28 | uint32_t bi_height; 29 | uint16_t bi_planes; 30 | uint16_t bi_bpp; 31 | uint32_t bi_compression; 32 | uint32_t bi_image_size; 33 | uint32_t bi_xcount; 34 | uint32_t bi_ycount; 35 | uint32_t bi_clr_used; 36 | uint32_t bi_clr_important; 37 | uint32_t red_mask; 38 | uint32_t green_mask; 39 | uint32_t blue_mask; 40 | } __attribute__((packed)); 41 | 42 | struct image_t 43 | { 44 | size_t allocated_size; 45 | size_t x_size; 46 | size_t y_size; 47 | enum image_style type; 48 | uint8_t *img; 49 | int bpp; 50 | int pitch; 51 | size_t img_width; 52 | size_t img_height; 53 | size_t x_displacement; 54 | size_t y_displacement; 55 | uint32_t back_colour; 56 | }; 57 | 58 | bool bmp_open_image(struct image_t *image, uint64_t file, uint64_t size); 59 | void image_make_centered(struct image_t *image, int frame_x_size, int frame_y_size, uint32_t back_colour); 60 | void image_make_stretched(struct image_t *image, int new_x_size, int new_y_size); 61 | bool image_open(struct image_t *image, uint64_t file, uint64_t size); 62 | void image_close(struct image_t *image); 63 | 64 | #ifdef __cplusplus 65 | } 66 | #endif 67 | 68 | #endif // IMAGE_H -------------------------------------------------------------------------------- /include/firefly/console/term.h: -------------------------------------------------------------------------------- 1 | #ifndef TERM_H 2 | #define TERM_H 3 | 4 | #include "image.h" 5 | 6 | #ifdef __cplusplus 7 | extern "C" 8 | { 9 | #endif 10 | 11 | extern void *alloc_mem(size_t size); 12 | extern void free_mem(void *ptr, size_t size); 13 | extern void *memcpy(void *dest, const void *src, size_t len); 14 | extern void *memset(void *dest, int ch, size_t n); 15 | 16 | #define VGA_FONT_MAX 16384 17 | #define VGA_FONT_GLYPHS 256 18 | 19 | #define TERM_TABSIZE 8 20 | #define MAX_ESC_VALUES 16 21 | 22 | #define TERM_CTX_SIZE (uint64_t)(-1) 23 | #define TERM_CTX_SAVE (uint64_t)(-2) 24 | #define TERM_CTX_RESTORE (uint64_t)(-3) 25 | #define TERM_FULL_REFRESH (uint64_t)(-4) 26 | 27 | #define CHARSET_DEFAULT 0 28 | #define CHARSET_DEC_SPECIAL 1 29 | 30 | #define DEFAULT_ANSI_COLOURS \ 31 | { \ 32 | 0x00000000, 0x00AA0000, 0x0000AA00, 0x00AA5500, 0x000000AA, 0x00AA00AA, 0x0000AAAA, 0x00AAAAAA \ 33 | } 34 | #define DEFAULT_ANSI_BRIGHT_COLOURS \ 35 | { \ 36 | 0x00555555, 0x00FF5555, 0x0055FF55, 0x00FFFF55, 0x005555FF, 0x00FF55FF, 0x0055FFFF, 0x00FFFFFF \ 37 | } 38 | 39 | #define DEFAULT_BACKGROUND 0x00000000 // Black 40 | #define DEFAULT_FOREGROUND 0x00AAAAAA // Grey 41 | 42 | #define DEFAULT_MARGIN 64 43 | #define DEFAULT_MARGIN_GRADIENT 4 44 | 45 | typedef void (*callback_t)(uint64_t, uint64_t, uint64_t, uint64_t, uint64_t); 46 | typedef size_t fixedp6; 47 | 48 | static inline size_t fixedp6_to_int(fixedp6 value) 49 | { 50 | return value / 64; 51 | } 52 | static inline fixedp6 int_to_fixedp6(size_t value) 53 | { 54 | return value * 64; 55 | } 56 | 57 | enum callbacks 58 | { 59 | TERM_CB_DEC = 10, 60 | TERM_CB_BELL = 20, 61 | TERM_CB_PRIVATE_ID = 30, 62 | TERM_CB_STATUS_REPORT = 40, 63 | TERM_CB_POS_REPORT = 50, 64 | TERM_CB_KBD_LEDS = 60, 65 | TERM_CB_MODE = 70, 66 | TERM_CB_LINUX = 80 67 | }; 68 | 69 | enum term_type 70 | { 71 | NOT_READY, 72 | VBE, 73 | #if defined(__i386__) || defined(__x86_64__) 74 | TEXTMODE 75 | #endif 76 | }; 77 | 78 | struct framebuffer_t 79 | { 80 | uintptr_t address; 81 | uint64_t width; 82 | uint64_t height; 83 | uint64_t pitch; 84 | }; 85 | 86 | struct font_t 87 | { 88 | uintptr_t address; 89 | uint8_t width; 90 | uint8_t height; 91 | uint8_t spacing; 92 | uint8_t scale_x; 93 | uint8_t scale_y; 94 | }; 95 | 96 | struct style_t 97 | { 98 | uint32_t ansi_colours[8]; 99 | uint32_t ansi_bright_colours[8]; 100 | uint32_t background; 101 | uint32_t foreground; 102 | uint16_t margin; 103 | uint16_t margin_gradient; 104 | }; 105 | 106 | struct background_t 107 | { 108 | struct image_t *background; 109 | enum image_style style; 110 | uint32_t backdrop; 111 | }; 112 | 113 | struct term_context 114 | { 115 | bool control_sequence; 116 | bool csi; 117 | bool escape; 118 | bool rrr; 119 | bool discard_next; 120 | bool bold; 121 | bool reverse_video; 122 | bool dec_private; 123 | bool insert_mode; 124 | uint8_t g_select; 125 | uint8_t charsets[2]; 126 | size_t current_charset; 127 | size_t escape_offset; 128 | size_t esc_values_i; 129 | size_t saved_cursor_x; 130 | size_t saved_cursor_y; 131 | size_t current_primary; 132 | size_t scroll_top_margin; 133 | size_t scroll_bottom_margin; 134 | uint32_t esc_values[MAX_ESC_VALUES]; 135 | 136 | bool saved_state_bold; 137 | bool saved_state_reverse_video; 138 | size_t saved_state_current_charset; 139 | size_t saved_state_current_primary; 140 | }; 141 | 142 | struct gterm_t; 143 | #if defined(__i386__) || defined(__x86_64__) 144 | struct tterm_t; 145 | #endif 146 | 147 | struct term_t 148 | { 149 | struct term_context context; 150 | struct gterm_t *gterm; 151 | 152 | bool initialised; 153 | 154 | enum term_type term_backend; 155 | size_t rows, cols; 156 | bool runtime; 157 | 158 | uint64_t arg; 159 | bool autoflush; 160 | 161 | callback_t callback; 162 | }; 163 | 164 | void term_init(struct term_t *term, callback_t callback); 165 | void term_deinit(struct term_t *term); 166 | void term_reinit(struct term_t *term); 167 | void term_vbe(struct term_t *term, struct framebuffer_t frm, struct font_t font, struct style_t style, struct background_t back); 168 | #if defined(__i386__) || defined(__x86_64__) 169 | void term_textmode(struct term_t *term); 170 | #endif 171 | void term_notready(struct term_t *term); 172 | void term_putchar(struct term_t *term, uint8_t c); 173 | void term_write(struct term_t *term, uint64_t buf, uint64_t count); 174 | void term_print(struct term_t *term, const char *str); 175 | void term_sgr(struct term_t *term); 176 | void term_dec_private_parse(struct term_t *term, uint8_t c); 177 | void term_linux_private_parse(struct term_t *term); 178 | void term_mode_toggle(struct term_t *term, uint8_t c); 179 | void term_control_sequence_parse(struct term_t *term, uint8_t c); 180 | void term_escape_parse(struct term_t *term, uint8_t c); 181 | void term_raw_putchar(struct term_t *term, uint8_t c); 182 | void term_clear(struct term_t *term, bool move); 183 | void term_enable_cursor(struct term_t *term); 184 | bool term_disable_cursor(struct term_t *term); 185 | void term_set_cursor_pos(struct term_t *term, size_t x, size_t y); 186 | void term_get_cursor_pos(struct term_t *term, size_t *x, size_t *y); 187 | void term_set_text_fg(struct term_t *term, size_t fg); 188 | void term_set_text_bg(struct term_t *term, size_t bg); 189 | void term_set_text_fg_bright(struct term_t *term, size_t fg); 190 | void term_set_text_bg_bright(struct term_t *term, size_t bg); 191 | void term_set_text_fg_default(struct term_t *term); 192 | void term_set_text_bg_default(struct term_t *term); 193 | bool term_scroll_disable(struct term_t *term); 194 | void term_scroll_enable(struct term_t *term); 195 | void term_move_character(struct term_t *term, size_t new_x, size_t new_y, size_t old_x, size_t old_y); 196 | void term_scroll(struct term_t *term); 197 | void term_revscroll(struct term_t *term); 198 | void term_swap_palette(struct term_t *term); 199 | void term_save_state(struct term_t *term); 200 | void term_restore_state(struct term_t *term); 201 | void term_double_buffer_flush(struct term_t *term); 202 | uint64_t term_context_size(struct term_t *term); 203 | void term_context_save(struct term_t *term, uint64_t ptr); 204 | void term_context_restore(struct term_t *term, uint64_t ptr); 205 | void term_full_refresh(struct term_t *term); 206 | 207 | #ifdef __cplusplus 208 | } 209 | #endif 210 | 211 | #endif // TERM_H -------------------------------------------------------------------------------- /include/firefly/drivers/ports.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace firefly::kernel::io { 4 | /** 5 | * Read a byte from 16-bit port address 6 | * @param port The port to read from 7 | * @return The byte read from port 8 | */ 9 | [[nodiscard]] inline uint8_t inb(uint16_t port) { 10 | uint8_t val; 11 | asm volatile( 12 | "inb %%dx, %%al" 13 | : "=a"(val) 14 | : "d"(port)); 15 | return val; 16 | } 17 | 18 | /** 19 | * Writes a byte to 16-bit port address 20 | * @param port The port to write to 21 | * @param val The byte to write 22 | */ 23 | inline void outb(uint16_t port, uint8_t val) { 24 | asm volatile( 25 | "outb %%al, %%dx" 26 | : 27 | : "d"(port), "a"(val)); 28 | } 29 | 30 | /** 31 | * Read a word from 16-bit port address 32 | * @param port The port to read from 33 | * @return The word read from port 34 | */ 35 | [[nodiscard]] inline uint16_t inw(uint16_t port) { 36 | uint16_t val; 37 | asm volatile( 38 | "inw %%dx, %%ax" 39 | : "=a"(val) 40 | : "d"(port)); 41 | return val; 42 | } 43 | 44 | /** 45 | * Writes a word to 16-bit port address 46 | * @param port The port to write to 47 | * @param val The byte to write 48 | */ 49 | inline void outw(uint8_t port, uint16_t val) { 50 | asm volatile( 51 | "outw %%ax, %%dx" 52 | : 53 | : "d"(port), "a"(val)); 54 | } 55 | 56 | /** 57 | * Delays a small amount of time 58 | * to give devices time to process 59 | */ 60 | inline void io_pause() { 61 | // dummy write, port 0x80 unused after POST 62 | asm volatile("outb %al, $0x80"); 63 | } 64 | } // namespace firefly::kernel::io 65 | -------------------------------------------------------------------------------- /include/firefly/drivers/ps2.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | #include "firefly/drivers/ports.hpp" 6 | 7 | #include "libk++/utils.hpp" 8 | 9 | /** 10 | * The PS/2 port driver 11 | */ 12 | namespace firefly::drivers::ps2 { 13 | /** 14 | * Initializes the driver 15 | * @return true : Driver was successfully initialized 16 | * false : Driver failed to initialize 17 | */ 18 | bool init(); 19 | 20 | /** 21 | * Tries to read a scancode from the keybaord 22 | * 23 | * @return scancode if input was readable 24 | */ 25 | frg::optional get_scancode(); 26 | 27 | /** 28 | * Handles input of a scancode and forwards it to the display buffer 29 | * 30 | * @param scancode Scancode that was receieved from the keyboard 31 | * @param crs Where to print the input 32 | */ 33 | void handle_input(unsigned char scancode); 34 | } // namespace firefly::drivers::ps2 -------------------------------------------------------------------------------- /include/firefly/drivers/serial.hpp: -------------------------------------------------------------------------------- 1 | #ifndef __SERIAL_KERNEL_HPP__ 2 | #define __SERIAL_KERNEL_HPP__ 3 | 4 | #include 5 | #include 6 | 7 | namespace firefly::kernel::io { 8 | class SerialPort { 9 | public: 10 | constexpr static inline size_t BAUD_BASE = 115200; 11 | constexpr static inline uint16_t COM1 = 0x3F8; 12 | constexpr static inline uint16_t COM2 = 0x2F8; 13 | constexpr static inline uint16_t COM3 = 0x3E8; 14 | constexpr static inline uint16_t COM4 = 0x2E8; 15 | 16 | private: 17 | uint16_t port; 18 | uint16_t baud_divisor; 19 | 20 | bool initialized = false; 21 | 22 | public: 23 | SerialPort(uint16_t port, uint32_t baud_rate); 24 | 25 | bool initialize() noexcept; 26 | bool is_initialized() const noexcept; 27 | 28 | 29 | bool received() const noexcept; 30 | char read_char() const noexcept; 31 | 32 | void send_char(char c) noexcept; 33 | bool is_transmit_empty() const noexcept; 34 | 35 | void send_chars(const char* c, int len = -1) noexcept; 36 | void read_string(char* buffer, int len) const noexcept; 37 | }; 38 | 39 | } // namespace firefly::kernel::io 40 | 41 | #endif -------------------------------------------------------------------------------- /include/firefly/graphics/framebuffer.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "firefly/compiler/compiler.hpp" 6 | #include "firefly/memory-manager/mm.hpp" 7 | 8 | namespace firefly::kernel { 9 | class FramebufferDevice { 10 | public: 11 | void init(PhysicalAddress fb, uint64_t width, uint64_t height, uint64_t pitch) { 12 | this->fb = static_cast(fb); 13 | this->pitch = pitch; 14 | this->height = height; 15 | this->width = width; 16 | } 17 | 18 | private: 19 | uint32_t *fb; 20 | uint64_t width, height, pitch; 21 | }; 22 | 23 | extern FramebufferDevice fbDev; 24 | } // namespace firefly::kernel 25 | -------------------------------------------------------------------------------- /include/firefly/intel64/acpi/acpi.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "firefly/compiler/compiler.hpp" 8 | #include "firefly/intel64/acpi/acpi_rsdp.hpp" 9 | #include "firefly/intel64/acpi/acpi_table.hpp" 10 | #include "firefly/intel64/acpi/tables/all.hpp" 11 | 12 | namespace firefly::kernel::core::acpi { 13 | using AcpiTable = void*; 14 | 15 | class Acpi { 16 | private: 17 | friend class frg::manual_box; 18 | Acpi() = default; 19 | 20 | public: 21 | static void init(); 22 | static Acpi& accessor(); 23 | 24 | AcpiTable find(frg::string_view identifier) const; 25 | AcpiTable mustFind(frg::string_view identifier) const { 26 | auto const& table = find(identifier); 27 | if (!table) 28 | firefly::panic("Cannot find ACPI table!"); 29 | 30 | return table; 31 | }; 32 | 33 | void dumpTables() const; 34 | 35 | private: 36 | AcpiRsdp rsdp; 37 | AcpiSdt rootHeader; 38 | AcpiRootTable rsdt; 39 | AcpiRootTable xsdt; 40 | 41 | bool useXSDT{ false }; 42 | int8_t divisor{ 0 }; 43 | int entries{ 0 }; 44 | }; 45 | } // namespace firefly::kernel::core::acpi -------------------------------------------------------------------------------- /include/firefly/intel64/acpi/acpi_rsdp.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "firefly/compiler/compiler.hpp" 6 | #include "firefly/panic.hpp" 7 | 8 | namespace firefly::kernel::core::acpi { 9 | struct AcpiRsdp { 10 | // RSDP version 1.0 11 | char signature[8]; 12 | uint8_t checksum; 13 | char oemString[6]; 14 | uint8_t revision; 15 | uint32_t rsdtAddress; 16 | 17 | // RSDP version 2+ 18 | uint32_t length; 19 | uint64_t xsdtAddress; 20 | uint8_t extendedChecksum; 21 | uint8_t reserved[3]; 22 | 23 | inline void validateOrPanic(uint8_t *ptr) { 24 | int checksum{ 0 }; 25 | for (uint64_t i = 0; i < length; i++) 26 | checksum += *ptr++; 27 | 28 | if ((checksum & 0xff) != 0) 29 | firefly::panic("Bad RSDP checksum"); 30 | } 31 | } PACKED; 32 | } // namespace firefly::kernel::core::acpi -------------------------------------------------------------------------------- /include/firefly/intel64/acpi/acpi_table.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "firefly/compiler/compiler.hpp" 6 | #include "firefly/logger.hpp" 7 | #include "libk++/cstring.hpp" 8 | 9 | #define ACPI_LOOP_AND_LOG(info, max, str) \ 10 | firefly::logLine << info; \ 11 | for (int i = 0; i < max; i++) \ 12 | firefly::logLine << str[i]; \ 13 | firefly::logLine << '\n' \ 14 | << firefly::fmt::endl; 15 | 16 | struct AcpiSdt { 17 | char signature[4]; 18 | uint32_t length; 19 | uint8_t revision; 20 | uint8_t checksum; 21 | char oemString[6]; 22 | char oemTableId[8]; 23 | uint32_t oemRevision; 24 | char creatorId[4]; 25 | uint32_t creatorRevision; 26 | 27 | inline void logInfo() const { 28 | ACPI_LOOP_AND_LOG("Signature: ", 4, signature); 29 | ACPI_LOOP_AND_LOG("OEM: ", 6, oemString); 30 | ACPI_LOOP_AND_LOG("OEM-ID: ", 8, oemTableId); 31 | } 32 | 33 | inline bool validate() const { 34 | uint8_t sum = 0; 35 | for (uint32_t i = 0; i < this->length; i++) 36 | sum += reinterpret_cast(const_cast(this))[i]; 37 | 38 | return sum == 0; 39 | } 40 | 41 | inline bool compareSignature(const char *other) const { 42 | using firefly::libkern::cstring::strncmp; 43 | return (strncmp(signature, other, 4)) == 0; 44 | } 45 | } PACKED; 46 | 47 | struct AcpiAddress { 48 | uint8_t addressSpaceID; // 0 - System Memory 49 | // 1 - System I/O 50 | uint8_t registerBitWidth; 51 | uint8_t registerBitOffset; 52 | uint8_t reserved; 53 | uint64_t address; 54 | } PACKED; 55 | 56 | template 57 | class AcpiRootTable { 58 | public: 59 | struct PACKED { 60 | AcpiSdt header; 61 | T tables[]; // Pointers to acpi tables 62 | } * root; 63 | } PACKED; 64 | -------------------------------------------------------------------------------- /include/firefly/intel64/acpi/tables/all.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "bgrt.hpp" 4 | #include "hpet.hpp" 5 | #include "madt.hpp" 6 | -------------------------------------------------------------------------------- /include/firefly/intel64/acpi/tables/bgrt.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "firefly/intel64/acpi/acpi_table.hpp" 4 | 5 | /* 6 | Boot Graphics Resource Table 7 | https://wiki.osdev.org/BGRT 8 | */ 9 | struct AcpiBgrt { 10 | /* SDT Header */ 11 | AcpiSdt header; 12 | 13 | /* BGRT contents */ 14 | uint16_t version; 15 | uint8_t status; 16 | uint8_t imageType; 17 | uint64_t imageAddress; 18 | uint32_t xOffset; 19 | uint32_t yOffset; 20 | 21 | inline bool valid() const { 22 | return version == 1 && status == 0 && (imageType & 1) == 0; 23 | } 24 | } PACKED; -------------------------------------------------------------------------------- /include/firefly/intel64/acpi/tables/hpet.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "firefly/compiler/compiler.hpp" 6 | #include "firefly/intel64/acpi/acpi_table.hpp" 7 | 8 | // https://www.intel.com/content/dam/www/public/us/en/documents/technical-specifications/software-developers-hpet-spec-1-0a.pdf 9 | // page 30 10 | struct AcpiHpet { 11 | AcpiSdt header; 12 | 13 | uint32_t hpetId; 14 | 15 | AcpiAddress address; 16 | 17 | const uint8_t hpetNumber; 18 | 19 | uint16_t minimumClockTicks; // minimum clocks ticks without lost interruts 20 | struct { 21 | uint8_t pageProtection : 4; 22 | uint8_t oemAttributes : 4; 23 | }; 24 | } PACKED; 25 | -------------------------------------------------------------------------------- /include/firefly/intel64/acpi/tables/madt.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "firefly/compiler/compiler.hpp" 8 | #include "firefly/intel64/acpi/acpi_table.hpp" 9 | #include "firefly/logger.hpp" 10 | #include "firefly/memory-manager/allocator.hpp" 11 | #include "frg/manual_box.hpp" 12 | #include "libk++/bits.h" 13 | #include "libk++/fmt.hpp" 14 | 15 | USED const frg::array madtInterruptDevices = { 16 | "local apic", 17 | "io apic", 18 | "io apic interrupt source override", 19 | "io apic NMI source", 20 | "io apic NMI", 21 | "local apic address override" 22 | }; 23 | 24 | // Represents: MadtHeader::entryType 25 | enum MadtEntryType : uint8_t { 26 | lApic, 27 | ioApic, 28 | sourceOverride, 29 | x2Apic = 9, 30 | }; 31 | 32 | struct MadtHeader { 33 | uint8_t entryType; 34 | uint8_t recordLen; 35 | } PACKED; 36 | 37 | struct MadtEntryApic : MadtHeader { 38 | uint8_t processorId; 39 | uint8_t apicId; 40 | enum flags { 41 | // Check bit 0 first. If it's clear, check bit 1. 42 | // If neither of these bits are set, we should not 43 | // enable this CPU. 44 | processorEnabled = BIT(0), 45 | onlineCapable = BIT(1) 46 | }; 47 | } PACKED; 48 | 49 | struct MadtEntryIoApic : MadtHeader { 50 | uint8_t ioApicId; 51 | uint8_t reserved; 52 | uint32_t ioApicAddress; 53 | uint64_t globalInterruptBase; /* Global system interrupt base */ 54 | } PACKED; 55 | 56 | struct MadtSourceOverride : MadtHeader { 57 | uint8_t bus; // constant, meaning ISA 58 | uint8_t source; // Bus relative IRQ 59 | uint32_t gsi; // what GSI this will issue 60 | uint16_t flags; 61 | } PACKED; 62 | 63 | /* 64 | Multiple Apic Description Table 65 | https://wiki.osdev.org/MADT 66 | */ 67 | struct AcpiMadt { 68 | AcpiSdt header; 69 | 70 | uint32_t localApicAddress; 71 | uint32_t ignore; // Flags. Indicates whether a PIC is installed so 72 | // that an os knows if it should disable it. We do it unconditionally. 73 | char entries[]; 74 | 75 | // Find and return a tuple of every apic and io apic device reported by the MADT. 76 | // using T = frg::tuple, frg::array, frg::array>; 77 | using T = frg::tuple, frg::vector, frg::vector>; 78 | inline T enumerate() { 79 | // frg::array apics; 80 | // frg::array io_apics; 81 | // frg::array source_overrides; 82 | frg::vector apics; 83 | frg::vector io_apics; 84 | frg::vector source_overrides; 85 | // Madt entries range from 'madt_entries_start' to 'madt_entries_end' 86 | auto madt_entries_start = (uint8_t *)entries; 87 | auto madt_entries_end = reinterpret_cast(madt_entries_start) + header.length; 88 | 89 | // size_t apics_i, io_apics_i, source_overrides_i; 90 | // apics_i = 0; 91 | // io_apics_i = 0; 92 | // source_overrides_i = 0; 93 | 94 | // Iterate over all madt entries 95 | while (reinterpret_cast(madt_entries_start) < madt_entries_end) { 96 | MadtHeader *hdr = reinterpret_cast(madt_entries_start); 97 | 98 | switch (hdr->entryType) { 99 | case MadtEntryType::lApic: 100 | // apics[apics_i] = reinterpret_cast(madt_entries_start); 101 | apics.push(reinterpret_cast(madt_entries_start)); 102 | break; 103 | 104 | case MadtEntryType::ioApic: 105 | // io_apics[io_apics_i] = reinterpret_cast(madt_entries_start); 106 | io_apics.push(reinterpret_cast(madt_entries_start)); 107 | break; 108 | 109 | case MadtEntryType::sourceOverride: 110 | // source_overrides[source_overrides_i] = reinterpret_cast(madt_entries_start); 111 | // source_overrides_vec.push((MadtSourceOverride *) madt_entries_start); 112 | source_overrides.push(reinterpret_cast(madt_entries_start)); 113 | break; 114 | 115 | case MadtEntryType::x2Apic: 116 | firefly::debugLine << "x2apic\n" 117 | << firefly::fmt::endl; 118 | break; 119 | 120 | default: 121 | break; 122 | } 123 | 124 | // Move on to the next entry 125 | madt_entries_start += hdr->recordLen; 126 | } 127 | 128 | return { apics, io_apics, source_overrides }; 129 | } 130 | } PACKED; 131 | -------------------------------------------------------------------------------- /include/firefly/intel64/cache.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | static constexpr char CACHE_LINE_SIZE = 64; -------------------------------------------------------------------------------- /include/firefly/intel64/cpu/ap/ap.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace firefly::kernel::applicationProcessor { 4 | void startAllCores(); 5 | } 6 | -------------------------------------------------------------------------------- /include/firefly/intel64/cpu/apic/apic.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "firefly/intel64/acpi/acpi.hpp" 6 | #include "firefly/memory-manager/mm.hpp" 7 | #include "firefly/memory-manager/virtual/virtual.hpp" 8 | #include "firefly/memory-manager/virtual/vspace.hpp" 9 | #include "frg/manual_box.hpp" 10 | 11 | namespace firefly::kernel::apic { 12 | 13 | constexpr const uint8_t LVT_BASE = 0x20; 14 | 15 | constexpr const uint32_t APIC_ID = 0x20; 16 | constexpr const uint32_t APIC_LVR = 0x30; 17 | constexpr const uint32_t APIC_TASK_PRIORITY = 0x80; 18 | constexpr const uint32_t APIC_ICR0 = 0x300; 19 | constexpr const uint32_t APIC_ICR1 = 0x310; 20 | constexpr const uint32_t APIC_LVTT = 0x320; // Timer 21 | constexpr const uint32_t APIC_LVTTHMR = 0x330; // Thermal monitor 22 | constexpr const uint32_t APIC_LVTPC = 0x340; // Performance monitor 23 | constexpr const uint32_t APIC_LVT0 = 0x350; // Local INT #0 24 | constexpr const uint32_t APIC_LVT1 = 0x360; // Local INT #1 25 | constexpr const uint32_t APIC_LVTERR = 0x370; // Error 26 | constexpr const uint32_t APIC_TIMER_INITIAL = 0x380; 27 | constexpr const uint32_t APIC_TIMER_CURRENT = 0x390; 28 | constexpr const uint32_t APIC_TIMER_DIVIDER = 0x3E0; 29 | constexpr const uint32_t APIC_EOI = 0x0B0; 30 | constexpr const uint32_t APIC_SPURIOUS = 0xF0; 31 | 32 | constexpr const uint32_t APIC_MASKED = 0x10000; 33 | 34 | constexpr const uint8_t IOAPIC_ID = 0x00; 35 | constexpr const uint8_t IOAPIC_VER = 0x01; 36 | 37 | constexpr const uint64_t IOAPIC_MASKED = BIT(16); 38 | constexpr const uint64_t IOAPIC_LEVEL = BIT(15); 39 | constexpr const uint64_t IOAPIC_INTPOL = BIT(13); 40 | constexpr const uint64_t IOAPIC_DESTMODE = BIT(11); 41 | constexpr const uint64_t IOAPIC_DELMODE_FIXED = ((0) << 8); 42 | 43 | constexpr const uint64_t IOAPIC_REDTBL_BASE = 0x10; 44 | constexpr const uint64_t IOAPIC_REDTBL0 = 0x10; 45 | constexpr const uint64_t IOAPIC_REDTBL1 = 0x12; 46 | 47 | consteval const uint64_t IOAPIC_DEST(uint64_t dest) { 48 | return (((dest)&0xFF) << 56); 49 | } 50 | 51 | // When using this class, keep in mind that the only core to access the lapic is the core the lapic belongs to! 52 | class Apic { 53 | private: 54 | friend class frg::manual_box; 55 | 56 | protected: 57 | uint64_t address; 58 | 59 | public: 60 | Apic() { 61 | using core::acpi::Acpi; 62 | auto const& madt = reinterpret_cast(Acpi::accessor().mustFind("APIC")); 63 | this->address = madt->localApicAddress; 64 | } 65 | 66 | static void init(); 67 | 68 | // Access the Local APIC 69 | // You can ONLY access your own 70 | // APIC, so this is valid 71 | static Apic& accessor(); 72 | 73 | void setAddress(uint64_t newAddress) { 74 | this->address = newAddress; 75 | } 76 | 77 | void enable(); 78 | void enableIRQ(uint8_t irq); 79 | void write(uint32_t offset, uint32_t value); 80 | uint32_t read(uint32_t offset) const; 81 | void clearErrors(); 82 | void setIPIDest(uint32_t ap); 83 | void sendEOI(); 84 | 85 | uint32_t apicId(); 86 | }; 87 | 88 | // Enable an IRQ in both LAPIC and the correct IOAPIC 89 | void enableIRQ(uint8_t irq); 90 | 91 | class IOApic { 92 | private: 93 | uint32_t address; 94 | uint8_t ID; 95 | uint8_t ioApicVersion; 96 | uint64_t globalInterruptBase; 97 | 98 | public: 99 | IOApic(uint32_t physAddress, uint8_t id, uint64_t globalInterruptBase) { 100 | this->address = physAddress; 101 | this->globalInterruptBase = globalInterruptBase; 102 | 103 | this->ID = (read(IOAPIC_ID) >> 24) & 0xF0; 104 | this->ioApicVersion = read(IOAPIC_VER); 105 | } 106 | 107 | static void initAll(); 108 | /// Lookup IRQ -> Global IRQ mapping in the MADT table 109 | static uint8_t getGSI(uint8_t irq); 110 | 111 | enum DeliveryMode { 112 | Edge = 0, 113 | Level = 1, 114 | }; 115 | 116 | enum DestinationMode { 117 | Physical = 0, 118 | Logical = 1 119 | }; 120 | 121 | union RedirectionEntry { 122 | struct 123 | { 124 | uint64_t vector : 8; 125 | uint64_t delvMode : 3; 126 | uint64_t destMode : 1; 127 | uint64_t delvStatus : 1; 128 | uint64_t pinPolarity : 1; 129 | uint64_t remoteIRR : 1; 130 | uint64_t triggerMode : 1; 131 | uint64_t mask : 1; 132 | uint64_t reserved : 39; 133 | uint64_t destination : 8; 134 | }; 135 | struct 136 | { 137 | uint32_t lowerDword; 138 | uint32_t upperDword; 139 | }; 140 | }; 141 | 142 | void write(uint8_t offset, uint32_t value); 143 | uint32_t read(uint8_t offset) const; 144 | void updateIRQ(uint8_t irq); 145 | 146 | inline uint64_t getGlobalInterruptBase() const { 147 | return globalInterruptBase; 148 | } 149 | }; 150 | 151 | class ApicTimer { 152 | enum ApicTimerMode { 153 | OneShot = 0, 154 | Periodic = 1, 155 | TscDeadline = 2 156 | }; 157 | 158 | public: 159 | static ApicTimer& accessor(); 160 | static void init(); 161 | static bool isAvailable(); 162 | 163 | uint32_t calibrate(uint64_t usec); 164 | void oneShotTimer(uint64_t ticks); 165 | }; 166 | 167 | } // namespace firefly::kernel::apic 168 | -------------------------------------------------------------------------------- /include/firefly/intel64/cpu/cpu.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "firefly/intel64/gdt/gdt.hpp" 4 | #include "firefly/intel64/gdt/tss.hpp" 5 | #include "libk++/bits.h" 6 | 7 | namespace firefly::kernel { 8 | struct __attribute__((packed)) InterruptFrame { 9 | int64_t r15; 10 | int64_t r14; 11 | int64_t r13; 12 | int64_t r12; 13 | int64_t r11; 14 | int64_t r10; 15 | int64_t r9; 16 | int64_t r8; 17 | int64_t rdi; 18 | int64_t rsi; 19 | int64_t rbp; 20 | int64_t rdx; 21 | int64_t rcx; 22 | int64_t rbx; 23 | int64_t rax; 24 | 25 | int64_t int_no; 26 | int64_t err; 27 | 28 | int64_t rip; 29 | int64_t cs; 30 | int64_t rflags; 31 | int64_t rsp; 32 | int64_t ss; 33 | }; 34 | 35 | using ContextRegisters = InterruptFrame; 36 | 37 | union RFlags { 38 | uint64_t All; 39 | struct { 40 | int8_t carry : 1; 41 | int8_t reserved0 : 1; 42 | int8_t parity : 1; 43 | int8_t reserved1 : 1; 44 | int8_t auxiliaryCarry : 1; 45 | int8_t reserved2 : 1; 46 | int8_t zero : 1; 47 | int8_t sign : 1; 48 | int8_t trap : 1; 49 | int8_t interruptEnable : 1; 50 | int8_t direction : 1; 51 | int8_t overflow : 1; 52 | int8_t ioPriv : 2; 53 | int8_t nesterTask : 1; 54 | int8_t reserved3 : 1; 55 | int8_t resume : 1; 56 | int8_t v8086Mode : 1; 57 | int8_t alignCheckAccessControl : 1; 58 | int8_t virtualInterrupt : 1; 59 | int8_t virtualInterruptPending : 1; 60 | int8_t id : 1; 61 | int64_t reserved4 : 41; 62 | } fields PACKED; 63 | }; 64 | 65 | enum MSR : uint32_t { 66 | GsBase = 0xC0000101, 67 | }; 68 | 69 | // Per-cpu structures 70 | struct AssemblyCpuData { 71 | AssemblyCpuData *selfPointer; 72 | } PACKED; 73 | 74 | struct CpuData : public AssemblyCpuData { 75 | uint8_t cpuIndex; 76 | 77 | core::gdt::Gdt gdt; 78 | core::tss::Tss tss; 79 | } PACKED; 80 | 81 | // from managarm 82 | void initializeBootProccessor(uint64_t stack); 83 | void initializeApplicationProcessor(uint64_t stack); 84 | 85 | void initializeThisCpu(uint64_t stack); 86 | CpuData *getCpuData(size_t k); 87 | CpuData *getLocalCpuData(); 88 | 89 | // from linux kernel source 90 | inline void native_cpuid(uint32_t *eax, uint32_t *ebx, 91 | uint32_t *ecx, uint32_t *edx) { 92 | /* ecx is often an input as well as an output. */ 93 | asm volatile("cpuid" 94 | : "=a"(*eax), 95 | "=b"(*ebx), 96 | "=c"(*ecx), 97 | "=d"(*edx) 98 | : "0"(*eax), "2"(*ecx)); 99 | } 100 | 101 | inline uint64_t rdtsc(void) { 102 | uint32_t edx, eax; 103 | asm volatile("rdtsc" 104 | : "=a"(eax), "=d"(edx)); 105 | return ((uint64_t)edx << 32) | eax; 106 | } 107 | 108 | inline bool cpuHugePages() { 109 | uint32_t eax{ 1 }, ebx{ 0 }, ecx{ 0 }, edx{ 0 }; 110 | native_cpuid(&eax, &ebx, &ecx, &edx); 111 | 112 | // check if bit 26 of edx is set 113 | return (edx & BIT(26)); 114 | } 115 | 116 | inline void delay(uint64_t cycles) { 117 | uint64_t next_stop = rdtsc() + cycles; 118 | while (rdtsc() < next_stop) 119 | ; 120 | } 121 | 122 | inline void wrmsr(uint32_t index, uint64_t value) { 123 | uint32_t low = value; 124 | uint32_t high = value >> 32; 125 | asm volatile("wrmsr" ::"c"(index), "a"(low), "d"(high) 126 | : "memory"); 127 | } 128 | 129 | inline uint64_t rdmsr(uint32_t index) { 130 | uint32_t low, high; 131 | asm volatile("rdmsr" 132 | : "=a"(low), "=d"(high) 133 | : "c"(index) 134 | : "memory"); 135 | return ((uint64_t)high << 32) | (uint64_t)low; 136 | } 137 | 138 | } // namespace firefly::kernel 139 | -------------------------------------------------------------------------------- /include/firefly/intel64/gdt/gdt.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "firefly/compiler/compiler.hpp" 6 | #include "firefly/intel64/gdt/tss.hpp" 7 | 8 | static constexpr int8_t GDT_MAX_ENTRIES{ 5 }; 9 | 10 | enum SegmentSelector : int8_t { 11 | null, 12 | kernCS, 13 | kernDS, 14 | userCS, 15 | userDS, 16 | tss 17 | }; 18 | 19 | namespace firefly::kernel::core::gdt { 20 | extern "C" void load_gdt(uint64_t); 21 | 22 | struct Gdtr { 23 | uint16_t size; 24 | uint64_t base; 25 | } PACKED; 26 | 27 | struct Gdt { 28 | struct PACKED { 29 | uint16_t limit{ 0 }; 30 | uint16_t base0{ 0 }; 31 | uint8_t base1{ 0 }; 32 | uint8_t access; 33 | uint8_t flags; 34 | uint8_t base2{ 0 }; 35 | } gdtDescriptors[GDT_MAX_ENTRIES]; 36 | 37 | tss::descriptor tssDescriptors; 38 | } PACKED; 39 | 40 | void init(Gdt &gdt); 41 | void setGdtEntry(Gdt &cpuGdt, uint64_t selectorIdx, uint8_t flags, uint8_t access); 42 | void setTssEntry(Gdt &cpuGdt, uint64_t selectorIdx, uint8_t flags, uint8_t access); 43 | 44 | uint16_t tssEntryOffset(); 45 | uint16_t gdtEntryOffset(SegmentSelector selector); 46 | } // namespace firefly::kernel::core::gdt 47 | -------------------------------------------------------------------------------- /include/firefly/intel64/gdt/tss.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | namespace firefly::kernel { 9 | struct CpuData; 10 | } 11 | 12 | namespace firefly::kernel::core::tss { 13 | 14 | struct Tss { 15 | uint32_t reserved0; 16 | uint64_t RSP0; 17 | uint64_t RSP1; 18 | uint64_t RSP2; 19 | 20 | uint32_t reserved1; 21 | uint32_t reserved2; 22 | 23 | uint64_t IST1; 24 | uint64_t IST2; 25 | uint64_t IST3; 26 | uint64_t IST4; 27 | uint64_t IST5; 28 | uint64_t IST6; 29 | uint64_t IST7; 30 | 31 | uint32_t reserved3; 32 | uint32_t reserved4; 33 | 34 | uint16_t IOBP; 35 | } PACKED; 36 | 37 | // See: Intel SDM Figure 7-3. TSS Descriptor 38 | struct descriptor { 39 | uint16_t size; 40 | uint16_t base0; 41 | uint8_t base1; 42 | uint8_t access; 43 | uint8_t flags; 44 | uint8_t base2; 45 | uint32_t base3; 46 | uint32_t reserved; 47 | } PACKED; 48 | 49 | void init(CpuData* cpudata, uint64_t stack); 50 | 51 | } // namespace firefly::kernel::core::tss 52 | -------------------------------------------------------------------------------- /include/firefly/intel64/hpet/hpet.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "firefly/intel64/acpi/acpi_table.hpp" 7 | #include "firefly/logger.hpp" 8 | 9 | namespace firefly::kernel::timer { 10 | constexpr const uint8_t GENERAL_CAPS_REG = 0x0; 11 | constexpr const uint8_t GENERAL_CONF_REG = 0x10; 12 | constexpr const uint8_t GENERAL_INT_STATUS_REG = 0x20; 13 | constexpr const uint8_t MAIN_COUNTER_VALUE_REG = 0xF0; 14 | 15 | constexpr uint16_t timer_config(uint8_t n) { 16 | return (0x100 + 0x20 * n); 17 | } 18 | 19 | constexpr uint16_t timer_comparator(uint8_t n) { 20 | return (0x108 + 0x20 * n); 21 | } 22 | 23 | constexpr uint16_t timer_fsb_int_route(uint8_t n) { 24 | return (0x110 + 0x20 * n); 25 | } 26 | 27 | class HPET { 28 | private: 29 | friend class frg::manual_box; 30 | friend class HPETTimer; 31 | 32 | protected: 33 | uint64_t address; 34 | uint32_t period; 35 | uint32_t frequency; 36 | uint16_t minimumTicks; 37 | 38 | void write(const uint64_t reg, const uint64_t value); 39 | uint64_t read(const uint64_t reg) const; 40 | 41 | inline uint64_t counter() const { 42 | return read(MAIN_COUNTER_VALUE_REG); 43 | } 44 | 45 | inline uint64_t usecToTicks(const uint64_t usec) const { 46 | const uint64_t femtosecond = 1000000000; 47 | const uint64_t ticks = read(MAIN_COUNTER_VALUE_REG); 48 | return ticks + (usec * femtosecond) / period; 49 | } 50 | 51 | 52 | public: 53 | HPET(AcpiAddress& acpiAddress, uint16_t minimumClockTicks) { 54 | this->address = acpiAddress.address; 55 | this->minimumTicks = minimumClockTicks; 56 | } 57 | 58 | static HPET& accessor(); 59 | static void init(); 60 | static void deinit(); 61 | 62 | void initialize(); 63 | void enable(); 64 | void disable(); 65 | 66 | inline void usleep(uint64_t usec) const { 67 | const uint64_t required_hpet_ticks = usecToTicks(usec); 68 | while (counter() < required_hpet_ticks) 69 | asm volatile("pause" :: 70 | : "memory"); 71 | } 72 | }; 73 | 74 | class HPETTimer { 75 | private: 76 | bool periodicSupport; 77 | uint8_t timerNum; 78 | 79 | public: 80 | HPETTimer(bool periodicSupport, uint8_t timerNum) { 81 | this->periodicSupport = periodicSupport; 82 | this->timerNum = timerNum; 83 | } 84 | 85 | void enable(); 86 | void disable(); 87 | void setIoApicOutput(uint8_t output); 88 | void setOneshotMode(); 89 | void setPeriodicMode(); 90 | }; 91 | }; // namespace firefly::kernel::timer 92 | -------------------------------------------------------------------------------- /include/firefly/intel64/int/interrupt.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "firefly/intel64/cpu/cpu.hpp" 6 | 7 | namespace firefly::kernel::core::interrupt { 8 | 9 | void init(); 10 | 11 | // register IRQ handler 12 | // No need to call update() as we just write it to a table with handlers 13 | // for the actual interrupt handlers 14 | void registerIRQHandler(void (*handler)(ContextRegisters*), uint8_t irq); 15 | void unregisterIRQHandler(uint8_t irq); 16 | 17 | namespace change { 18 | extern "C" void update(void (*handler)(), uint16_t cs, uint8_t type, uint8_t index, uint8_t ist); 19 | } 20 | } // namespace firefly::kernel::core::interrupt 21 | -------------------------------------------------------------------------------- /include/firefly/intel64/page_flags.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "firefly/compiler/compiler.hpp" 7 | #include "firefly/intel64/page_permissions.hpp" 8 | #include "firefly/logger.hpp" 9 | #include "firefly/memory-manager/mm.hpp" 10 | #include "firefly/memory-manager/page.hpp" 11 | #include "firefly/memory-manager/primary/primary_phys.hpp" 12 | 13 | namespace firefly::kernel::core::paging { 14 | 15 | struct StructuredPte { 16 | uint8_t present; 17 | uint64_t address; 18 | }; 19 | 20 | union Pte { 21 | uint64_t All; 22 | 23 | struct { 24 | uint8_t present : 1; 25 | uint8_t readwrite : 1; 26 | uint8_t supervisor : 1; 27 | uint8_t writethrough : 1; 28 | uint8_t cache_disabled : 1; 29 | uint8_t accessed : 1; 30 | uint8_t dirty : 1; 31 | uint8_t pagesize : 1; 32 | uint8_t global : 1; 33 | uint8_t ignore : 3; 34 | uint64_t address : 52; 35 | } fields PACKED; 36 | 37 | // A hack to use structured binding with a filter (only select a few elements) 38 | inline StructuredPte destructure() const { 39 | return { fields.present, fields.address }; 40 | } 41 | 42 | inline void create(uint64_t addr, int flags, PageSize ps) { 43 | auto check_flags = [&](int flags, int bit) { 44 | return static_cast((flags >> bit) & 1); 45 | }; 46 | 47 | this->fields = { 48 | .present = check_flags(flags, 0), 49 | .readwrite = check_flags(flags, 1), 50 | .supervisor = check_flags(flags, 2), 51 | .writethrough = check_flags(flags, 3), 52 | .cache_disabled = check_flags(flags, 4), 53 | .accessed = 0, 54 | .dirty = 0, 55 | .pagesize = check_flags(flags, 7), // PS/PAT bit 56 | .global = check_flags(flags, 8), 57 | .ignore = 0, 58 | .address = pagelist.pfn(addr) // Page Frame Number 59 | }; 60 | } 61 | }; 62 | 63 | // Pml => Page map level 64 | // (A vague term to help define Pml{1,2,3,4} or PDPT,PDP,PD,PT) 65 | struct Pml { 66 | Pte entries[512] = {}; 67 | 68 | // Retrieve the next pml, creating it if necessary. 69 | inline Pml* next(int depth, AccessFlags flags, PageSize ps) { 70 | const auto& pte = this->entries[depth]; 71 | auto [present, address] = pte.destructure(); 72 | 73 | if (present) { 74 | return reinterpret_cast(address << PAGE_SHIFT); 75 | } 76 | 77 | uint64_t addr = reinterpret_cast(operator new(PageSize::Size4K)); 78 | if (!addr) return nullptr; 79 | 80 | this->entries[depth].create(addr, static_cast(flags), ps); 81 | return reinterpret_cast(addr); 82 | }; 83 | 84 | inline PhysicalAddress operator new(size_t sz) { 85 | return mm::Physical::allocate(sz, FillMode::ZERO); 86 | } 87 | } PACKED; 88 | } // namespace firefly::kernel::core::paging -------------------------------------------------------------------------------- /include/firefly/intel64/page_permissions.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace firefly::kernel::core::paging { 4 | enum class AccessFlags : int { 5 | Readonly = 1, 6 | ReadWrite = 3, 7 | UserReadOnly = 5, 8 | UserReadWrite = 7 9 | }; 10 | 11 | // TODO: Implement me! 12 | enum class CacheMode : int { 13 | None, 14 | Uncachable, 15 | WriteCombine, 16 | WriteThrough, 17 | WriteBack 18 | }; 19 | } // namespace firefly::kernel::core::paging -------------------------------------------------------------------------------- /include/firefly/intel64/paging.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "firefly/intel64/page_flags.hpp" 6 | #include "firefly/intel64/page_permissions.hpp" 7 | #include "firefly/limine.hpp" 8 | #include "firefly/memory-manager/mm.hpp" 9 | 10 | namespace firefly::kernel::core::paging { 11 | void invalidatePage(const VirtualAddress page); 12 | void invalidatePage(const uint64_t page); 13 | void map(uint64_t virtual_addr, uint64_t physical_addr, AccessFlags access_flags, Pml *root, const PageSize page_size); 14 | } // namespace firefly::kernel::core::paging 15 | -------------------------------------------------------------------------------- /include/firefly/intel64/pit/pit.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace firefly::kernel { 6 | namespace timer::pit { 7 | void init(); 8 | void destroy(); 9 | void sleep(uint32_t ms); 10 | 11 | uint64_t counter(); 12 | } // namespace timer::pit 13 | } // namespace firefly::kernel 14 | -------------------------------------------------------------------------------- /include/firefly/kernel.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace firefly::kernel { 4 | [[noreturn]] void kernel_main(); 5 | void log_core_firefly_contributors(); 6 | } // namespace firefly::kernel -------------------------------------------------------------------------------- /include/firefly/logger.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | #include "firefly/console/console.hpp" 8 | #include "firefly/drivers/serial.hpp" 9 | #include "libk++/fmt.hpp" 10 | 11 | namespace firefly { 12 | 13 | namespace fmt_options { 14 | struct __endl {}; 15 | struct __base { 16 | int base; 17 | }; 18 | 19 | struct __hex : __base {}; 20 | struct __dec : __base {}; 21 | struct __bin : __base {}; 22 | struct __oct : __base {}; 23 | } // namespace fmt_options 24 | 25 | namespace fmt { 26 | constexpr fmt_options::__endl endl; 27 | constexpr fmt_options::__bin bin{ 2 }; 28 | constexpr fmt_options::__oct oct{ 8 }; 29 | constexpr fmt_options::__dec dec{ 10 }; 30 | constexpr fmt_options::__hex hex{ 16 }; 31 | } // namespace fmt 32 | 33 | namespace __writer { 34 | struct fb { 35 | void operator()(const char *s) { 36 | kernel::console::write(s); 37 | } 38 | }; 39 | 40 | struct serial { 41 | void operator()(const char *s) { 42 | using namespace kernel; 43 | io::SerialPort port = io::SerialPort(io::SerialPort::COM1, io::SerialPort::BAUD_BASE); 44 | port.send_chars(s, -1); 45 | } 46 | }; 47 | }; // namespace __writer 48 | 49 | template 50 | class ostream : format { 51 | private: 52 | template 53 | void vprintf(T in) { 54 | do_format(in); 55 | } 56 | 57 | public: 58 | template 59 | ostream &operator<<(T arg) { 60 | vprintf(arg); 61 | return *this; 62 | } 63 | 64 | ostream &operator<<(fmt_options::__endl) { 65 | flush(); 66 | return *this; 67 | } 68 | 69 | // Special case: 70 | // Handles the base of a number using fmt_options::__{dec, hex, etc} 71 | template 72 | ostream &operator<<(NumberBase nb) 73 | requires std::is_class_v 74 | { 75 | base = static_cast(nb.base); 76 | return *this; 77 | } 78 | 79 | inline void flush() { 80 | writeCallback(buffer); 81 | empty_buffer(); 82 | writer_position = 0; 83 | } 84 | 85 | 86 | private: 87 | inline void empty_buffer() { 88 | for (size_t i = 0; i < writer_position; i++) 89 | buffer[i] = 0; 90 | } 91 | 92 | private: 93 | F writeCallback; 94 | }; 95 | 96 | class log : public ostream<__writer::fb> {}; 97 | class dbg : public ostream<__writer::serial> {}; 98 | 99 | extern log logLine; 100 | extern dbg debugLine; 101 | 102 | } // namespace firefly 103 | -------------------------------------------------------------------------------- /include/firefly/memory-manager/allocator.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "firefly/logger.hpp" 4 | #include "firefly/memory-manager/secondary/heap.hpp" 5 | 6 | struct Allocator { 7 | VirtualAddress allocate(size_t size) const { 8 | using namespace firefly::kernel::mm; 9 | return heap->allocate(size); 10 | } 11 | 12 | void deallocate(VirtualAddress ptr) const { 13 | using namespace firefly::kernel::mm; 14 | return heap->deallocate(ptr); 15 | } 16 | 17 | void deallocate(VirtualAddress ptr, [[maybe_unused]] long unsigned int sz) const { 18 | using namespace firefly::kernel::mm; 19 | return heap->deallocate(ptr); 20 | } 21 | 22 | void free(VirtualAddress ptr) const { 23 | // firefly::logLine << firefly::fmt::hex << (uintptr_t) ptr << "\n" << firefly::fmt::endl; 24 | deallocate(ptr); 25 | } 26 | }; 27 | -------------------------------------------------------------------------------- /include/firefly/memory-manager/mm.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | enum AddressLayout : uint64_t { 6 | // clang-format off 7 | PageData = 0xFFFF'D000'0000'0000UL, 8 | SlabHeap = 0xFFFF'E000'0000'0000UL, 9 | High = 0xFFFF'8000'0000'0000UL, 10 | Code = 0xFFFF'FFFF'8000'0000UL, 11 | Low = 0x0000'0000'0000'0000UL 12 | // clang-format on 13 | }; 14 | 15 | enum PageSize : uint32_t { 16 | Size4K = 0x00001000, 17 | Size2M = 0x00200000, 18 | Size1G = 0x40000000, 19 | }; 20 | 21 | static constexpr uint32_t PAGE_SHIFT = 12; // Lower 12 bits of a virtual address denote the offset in the page frame 22 | using PhysicalAddress = void *; 23 | using VirtualAddress = void *; 24 | 25 | enum FillMode : char { 26 | ZERO = 0, 27 | NONE = 1 // Don't fill 28 | }; 29 | -------------------------------------------------------------------------------- /include/firefly/memory-manager/page.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | #include "firefly/compiler/compiler.hpp" 11 | #include "firefly/limine.hpp" 12 | #include "firefly/memory-manager/mm.hpp" 13 | #include "libk++/bits.h" 14 | extern uintptr_t GLOB_PAGE_ARRAY[]; 15 | 16 | enum class RawPageFlags : int { 17 | None = 0, 18 | Unusable = 1, 19 | Slab = 2 20 | }; 21 | 22 | struct RawPage { 23 | RawPageFlags flags; 24 | int order; 25 | int buddy_index; 26 | int slab_size; 27 | std::atomic_int refcount; 28 | 29 | void operator=(const RawPage &other) { 30 | flags = other.flags; 31 | order = other.order; 32 | buddy_index = other.buddy_index; 33 | refcount.store(other.refcount, std::memory_order_seq_cst); 34 | } 35 | 36 | bool is_buddy_page(int min_order) const { 37 | return order >= min_order; 38 | } 39 | 40 | void reset(bool reset_refcount = true) { 41 | flags = RawPageFlags::None; 42 | order = 0; 43 | if (likely(reset_refcount)) 44 | refcount = 0; 45 | } 46 | }; 47 | 48 | class Pagelist { 49 | using Index = int; 50 | using AddressType = uint64_t; 51 | 52 | public: 53 | void init(struct limine_memmap_response *memmap_response) { 54 | for (size_t i = 0; i < memmap_response->entry_count; i++) { 55 | auto e = memmap_response->entries[i]; 56 | 57 | // Ensure the array size is not exceeded 58 | // auto const projected_index = largest_index + (e->length / 4096); 59 | // assert_truth(projected_index <= page_array_sz); 60 | 61 | for (size_t j = 0; j <= e->length; j += 4096, largest_index++) { 62 | auto const page = RawPage{ 63 | .flags = (e->type != LIMINE_MEMMAP_USABLE) ? RawPageFlags::Unusable : RawPageFlags::None 64 | }; 65 | pages[largest_index] = page; 66 | } 67 | } 68 | 69 | firefly::logLine << "RawPage size:" << firefly::fmt::dec << sizeof(RawPage) << "bytes\n"; 70 | firefly::logLine << "Pagelist overhead:" << firefly::fmt::dec << largest_index * sizeof(RawPage) << "bytes\n" 71 | << firefly::fmt::endl; 72 | 73 | // largest_index is incremented one too many times. 74 | --largest_index; 75 | } 76 | 77 | inline auto get_page(RawPage *p) const { 78 | return AddressType(pages - p); 79 | } 80 | inline auto max_page() const { 81 | return &pages[largest_index]; 82 | } 83 | inline auto min_page() const { 84 | return &pages[0]; 85 | } 86 | 87 | inline auto phys_to_page(uint64_t addr) const { 88 | return &pages[(addr >> PAGE_SHIFT) - 1]; 89 | } 90 | 91 | inline auto page_to_phys(RawPage *p) const { 92 | return get_page(p) << PAGE_SHIFT; 93 | } 94 | 95 | inline auto pfn(uint64_t addr) const { 96 | return addr >> PAGE_SHIFT; 97 | } 98 | 99 | auto operator[](Index i) const { 100 | auto const &page = phys_to_page(i); 101 | return array_operator{ .page = page, .address = page_to_phys(page) }; 102 | } 103 | 104 | private: 105 | struct array_operator { 106 | RawPage *page; 107 | AddressType address; 108 | }; 109 | 110 | RawPage *pages = (struct RawPage *)(reinterpret_cast(GLOB_PAGE_ARRAY)); 111 | Index largest_index{}; // largest index into the 'pages' array 112 | }; 113 | 114 | // Instance created in primary_phys.cpp 115 | extern Pagelist pagelist; 116 | -------------------------------------------------------------------------------- /include/firefly/memory-manager/primary/page_frame.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "firefly/logger.hpp" 4 | #include "firefly/memory-manager/mm.hpp" 5 | #include "firefly/memory-manager/primary/primary_phys.hpp" 6 | #include "libk++/bits.h" 7 | #include "libk++/memory.hpp" 8 | 9 | namespace firefly::kernel::mm { 10 | class PageFrame { 11 | public: 12 | static constexpr bool verbose = !false; 13 | 14 | template 15 | class Freelist { 16 | private: 17 | T list; 18 | 19 | public: 20 | void add(const T &block) { 21 | if (block) { 22 | memset((void *)block, 0, PageSize::Size4K); 23 | *(T *)block = list; 24 | } 25 | list = block; 26 | } 27 | 28 | T remove(FillMode fill) { 29 | T element = list; 30 | list = *(T *)list; 31 | 32 | if (fill != FillMode::NONE) 33 | memset(element, fill, PageSize::Size4K); 34 | 35 | return element; 36 | } 37 | }; 38 | Freelist freelist; 39 | 40 | void init(PhysicalAddress base, size_t length) { 41 | freelist.add(nullptr); 42 | addRange(reinterpret_cast(base), length); 43 | logLine << "page-allocator: Initialized " << fmt::endl; 44 | } 45 | 46 | PhysicalAddress allocate(FillMode fill = FillMode::ZERO) { 47 | auto ptr = freelist.remove(fill); 48 | if (!ptr) { 49 | auto fallback = Physical::allocate(4 * PageSize::Size4K); 50 | if (!fallback) 51 | return nullptr; 52 | 53 | addRange(reinterpret_cast(fallback), 4 * PageSize::Size4K); 54 | return freelist.remove(FillMode::NONE); // Physical::Allocate already cleared the memory 55 | } 56 | return ptr; 57 | } 58 | 59 | void deallocate(PhysicalAddress ptr) { 60 | if (ptr) 61 | freelist.add(ptr); 62 | }; 63 | 64 | void addRange(uint64_t base, uint64_t len) { 65 | auto pages = ((base + len - 1) - base) / PageSize::Size4K; 66 | auto top = base + pages * PageSize::Size4K; 67 | 68 | if constexpr (verbose) 69 | logLine << "base: " << fmt::hex << base << " | top: " << top << " | pages: " << pages << "\n" 70 | << fmt::endl; 71 | 72 | for (auto i = 0ul; i < (len / PageSize::Size4K); i++) { 73 | for (auto i = 0ul; i <= pages; i++) { 74 | freelist.add(reinterpret_cast(base + (i * PageSize::Size4K))); 75 | } 76 | } 77 | } 78 | }; 79 | } // namespace firefly::kernel::mm 80 | -------------------------------------------------------------------------------- /include/firefly/memory-manager/primary/primary_phys.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "firefly/memory-manager/mm.hpp" 6 | #include "firefly/limine.hpp" 7 | 8 | namespace firefly::kernel::mm::Physical { 9 | void init(limine_memmap_response *mmap); 10 | PhysicalAddress allocate(uint64_t size = 4096, FillMode fill = FillMode::ZERO); 11 | PhysicalAddress must_allocate(uint64_t size = 4096, FillMode fill = FillMode::ZERO); 12 | void deallocate(PhysicalAddress ptr); 13 | } // namespace firefly::kernel::mm::Physical -------------------------------------------------------------------------------- /include/firefly/memory-manager/secondary/heap.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "firefly/memory-manager/mm.hpp" 6 | 7 | namespace firefly::kernel::mm { 8 | class kernelHeap { 9 | private: 10 | friend class frg::manual_box; 11 | kernelHeap() = default; 12 | 13 | public: 14 | static void init(); 15 | VirtualAddress allocate(size_t size) const; 16 | void deallocate(VirtualAddress ptr) const; 17 | }; 18 | 19 | extern constinit frg::manual_box heap; 20 | 21 | } // namespace firefly::kernel::mm 22 | -------------------------------------------------------------------------------- /include/firefly/memory-manager/secondary/slab/slab.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "firefly/intel64/cache.hpp" 12 | #include "firefly/logger.hpp" 13 | #include "firefly/memory-manager/mm.hpp" 14 | #include "firefly/memory-manager/page.hpp" 15 | #include "firefly/memory-manager/primary/primary_phys.hpp" 16 | #include "libk++/bits.h" 17 | 18 | namespace firefly::kernel::mm { 19 | 20 | namespace { 21 | static constexpr bool debugSlab{ false }; 22 | static constexpr bool sanityCheckSlab{ true }; 23 | }; // namespace 24 | 25 | struct slabHelper { 26 | static constexpr bool powerOfTwo(int n) { 27 | return (n > 0) && ((n & (n - 1)) == 0); 28 | } 29 | 30 | // Compute the next highest power of 2 of 32-bit 'n' 31 | static constexpr int alignToSecondPower(int n) { 32 | n--; 33 | n |= n >> 1; 34 | n |= n >> 2; 35 | n |= n >> 4; 36 | n |= n >> 8; 37 | n |= n >> 16; 38 | return ++n; 39 | } 40 | }; 41 | 42 | /* vm = virtual memory */ 43 | template 44 | class slabCache { 45 | static constexpr int object_size{ 8 }; // sizeof(slab::object) aka sizeof(frg::intrusive_queue::hook) aka sizeof(pointer) 46 | struct slab; 47 | enum SlabType { 48 | Small, 49 | Large 50 | }; 51 | 52 | enum SlabState { 53 | full, 54 | partial, 55 | free 56 | }; 57 | 58 | public: 59 | slabCache() = default; 60 | slabCache(int sz, const frg::string_view& descriptor = "anonymous") { 61 | initialize(sz, std::move(descriptor)); 62 | } 63 | 64 | void initialize(int sz, const frg::string_view& descriptor = "anonymous") { 65 | // Minimum allocation size has to be the size of a 'struct object', otherwise the 66 | // intrusive queue will end up in trouble since it's given too little memory. 67 | if (sz < object_size) 68 | sz = object_size; 69 | 70 | if (!slabHelper::powerOfTwo(size)) 71 | size = slabHelper::alignToSecondPower(size); 72 | 73 | size = sz; 74 | slab_type = slabTypeOf(sz); 75 | this->descriptor = std::move(descriptor); 76 | createSlab(); 77 | } 78 | 79 | VirtualAddress allocate() { 80 | lock.lock(); 81 | 82 | slab* _slab = slabs[SlabState::partial].first(); 83 | VirtualAddress object; 84 | 85 | auto do_dequeue = [&]() { 86 | return (object = reinterpret_cast(_slab->object_queue.dequeue())); 87 | }; 88 | 89 | // Check if we can allocate an object from the partial slab 90 | if (!_slab) { 91 | _slab = slabs[SlabState::free].first(); 92 | 93 | if (unlikely(!_slab)) { 94 | // panic("TODO: Slab cache is OOM. Implement grow()"); 95 | grow(); 96 | _slab = slabs[SlabState::free].first(); 97 | assert_truth(_slab != nullptr && "Alloc for slab failed"); 98 | } 99 | 100 | // Move a free slab into the partial slab 101 | slabs[SlabState::free].remove(_slab); 102 | slabs[SlabState::partial].insert(_slab); 103 | 104 | _slab->slab_state = SlabState::partial; 105 | do_dequeue(); 106 | 107 | if constexpr (debugSlab) 108 | assert_truth(object != nullptr && "This should NOT happen, something went really wrong"); 109 | 110 | goto end; 111 | } 112 | 113 | // Perform the allocation 114 | do_dequeue(); 115 | 116 | // Check if this slab is full and move it 117 | // into the full slab tree if applicable 118 | if (!object || _slab->object_queue.size() == 0) { 119 | // Fix slab state 120 | slabs[SlabState::partial].remove(_slab); 121 | slabs[SlabState::full].insert(_slab); 122 | _slab->slab_state = SlabState::full; 123 | 124 | // Retry allocation 125 | lock.unlock(); 126 | allocate(); 127 | } 128 | 129 | end: 130 | lock.unlock(); 131 | return object; 132 | } 133 | 134 | void deallocate(VirtualAddress ptr) { 135 | lock.lock(); 136 | 137 | // Slabs are always aligned on 4kib boundaries. 138 | // A 4kib aligned address has it's lowest 12 bits cleared, that's what we're doing here. 139 | constexpr const int shift = PAGE_SHIFT; 140 | slab* _slab = reinterpret_cast((reinterpret_cast(ptr) >> shift) << shift); 141 | 142 | if constexpr (sanityCheckSlab) { 143 | assert_truth(_slab->object_queue.size() - 1 < static_cast(_slab->max_objects) && "Tried to free non-allocated address"); 144 | assert_truth(_slab->slab_state != SlabState::free && "Tried to free non-allocated address"); 145 | } 146 | 147 | if constexpr (debugSlab) { 148 | logLine << "Slab is located at: " 149 | << fmt::hex << reinterpret_cast(_slab) << '\n' 150 | << "Descriptor: " << this->descriptor.data() 151 | << ", state: " << fmt::dec << (int)_slab->slab_state 152 | << '\n' 153 | << fmt::endl; 154 | } 155 | 156 | // This object is most likely still in the cache, so we enqueue it at the head. 157 | // This will make sure we hand out "cache warm" objects whenever possible. 158 | _slab->object_queue.enqueue_head(reinterpret_cast(ptr)); 159 | 160 | // If this object was full, move it the the partial slab 161 | if (_slab->slab_state == SlabState::full) { 162 | auto previous_state = _slab->slab_state; 163 | _slab->slab_state = SlabState::partial; 164 | 165 | slabs[previous_state].remove(_slab); 166 | slabs[SlabState::partial].insert(_slab); 167 | } 168 | 169 | lock.unlock(); 170 | } 171 | 172 | private: 173 | void grow() { 174 | createSlab(); 175 | } 176 | 177 | void shrink(); 178 | 179 | void createSlab() { 180 | size_t alloc_sz = slab_type == SlabType::Large ? PageSize::Size2M : PageSize::Size4K; 181 | 182 | if constexpr (debugSlab) { 183 | logLine << "Start of slab creation\n"; 184 | logLine << "slab descriptor='" << descriptor.data() << "'\n"; 185 | logLine << "Creating new slab with an allocation of size '" << alloc_sz << "', object size is: " << size << '\n'; 186 | logLine << "is large slab: " << (alloc_sz == PageSize::Size2M ? "true" : "false") << '\n' 187 | << fmt::endl; 188 | } 189 | 190 | auto base = reinterpret_cast(vm_backing.allocate(alloc_sz)); 191 | slab* _slab = new (reinterpret_cast(base)) struct slab(descriptor, base, alloc_sz, size); 192 | slabs[SlabState::free].insert(_slab); 193 | 194 | if constexpr (debugSlab) 195 | logLine << "slab can hold " << _slab->max_objects << " objects\nEnd of slab creation\n" 196 | << fmt::endl; 197 | } 198 | 199 | SlabType slabTypeOf(int size) const { 200 | return size > static_cast(PageSize::Size4K / 8) ? SlabType::Large : SlabType::Small; 201 | } 202 | 203 | frg::string_view descriptor; 204 | 205 | private: 206 | struct slab { 207 | struct object : frg::default_queue_hook {}; 208 | 209 | slab(const frg::string_view& _descriptor, uintptr_t address, size_t len, size_t sz) 210 | : descriptor{ _descriptor } { 211 | // Note: 212 | // Technically this is breaking the spec which mandates the slab be placed at the _end_, but 213 | // I prefer it this way and don't see any major issues as a result of doing it like this. - V01D 214 | // 215 | // Reserve some memory for the slab structure & align it to sizeof(slab) bytes. 216 | // If we don't do this we will end up overwriting the object we just created. 217 | logLine << "base addr of slab=" << fmt::hex << address << '\n' 218 | << fmt::endl; 219 | address += sizeof(slab); 220 | address = (address + sizeof(slab) - 1) & ~(sizeof(slab) - 1); 221 | base_address = address; 222 | 223 | for (auto i = address; i <= (address + len); i += sz) { 224 | // TODO: VmBackingAllocator should do this. 225 | // Marks each 4kib page as a slab page. 226 | if (i % PageSize::Size4K == 0) { 227 | auto const& page = pagelist.phys_to_page(i - AddressLayout::SlabHeap); 228 | page->flags = RawPageFlags::Slab; 229 | page->slab_size = sz; 230 | } 231 | 232 | frg::queue_result res = object_queue.enqueue(reinterpret_cast(i)); 233 | assert_truth(res == frg::queue_result::Okay); 234 | } 235 | 236 | slab_state = SlabState::free; 237 | max_objects = object_queue.size() - 1; 238 | } 239 | 240 | frg::string_view descriptor; 241 | uintptr_t base_address; 242 | size_t max_objects; 243 | 244 | SlabState slab_state; 245 | frg::intrusive_queue object_queue; 246 | frg::rbtree_hook tree_hook; 247 | }; 248 | 249 | struct comparator { 250 | bool operator()(const slab& a, const slab& b) { 251 | return a.base_address < b.base_address; 252 | } 253 | }; 254 | 255 | int size; 256 | Lock lock; 257 | SlabType slab_type; 258 | VmBackingAllocator vm_backing; 259 | 260 | // Full, partial and free slabs 261 | alignas(CACHE_LINE_SIZE) frg::array< 262 | frg::rbtree< 263 | slab, 264 | &slab::tree_hook, 265 | comparator>, 266 | 3> slabs = {}; 267 | }; 268 | } // namespace firefly::kernel::mm 269 | -------------------------------------------------------------------------------- /include/firefly/memory-manager/stack.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "firefly/memory-manager/allocator.hpp" 8 | 9 | struct Stack { 10 | struct stack { 11 | uint64_t rbp; 12 | uint64_t rsp; 13 | }; 14 | 15 | Stack() = default; 16 | virtual ~Stack() { 17 | } 18 | 19 | [[gnu::artificial]] virtual inline void add(stack stk) = 0; 20 | [[gnu::artificial]] virtual inline stack get(int index = 0) const = 0; 21 | 22 | protected: 23 | void operator delete([[maybe_unused]] void *ptr) { 24 | } 25 | }; 26 | 27 | class kernelStack : Stack { 28 | public: 29 | inline void add(stack stk) override { 30 | stacks.push_back(stk); 31 | } 32 | 33 | inline stack get(int index = 0) const override { 34 | return stacks.front(); 35 | } 36 | 37 | inline auto operator[](int index) const { 38 | return stacks[index]; 39 | } 40 | 41 | inline int howMany() const { 42 | return stacks.size(); 43 | }; 44 | 45 | private: 46 | frg::small_vector stacks; 47 | }; 48 | 49 | class userStack : Stack { 50 | public: 51 | inline void add(stack stk) override { 52 | stacks.push_back(stk); 53 | } 54 | 55 | inline stack get(int index = 0) const override { 56 | return stacks.front(); 57 | } 58 | 59 | inline auto operator[](int index) const { 60 | return stacks[index]; 61 | } 62 | 63 | inline int howMany() const { 64 | return stacks.size(); 65 | }; 66 | 67 | private: 68 | frg::small_vector stacks; 69 | }; 70 | 71 | extern constinit frg::manual_box kStack; -------------------------------------------------------------------------------- /include/firefly/memory-manager/virtual/virtual.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | #include "firefly/logger.hpp" 9 | #include "firefly/memory-manager/virtual/vspace.hpp" 10 | 11 | namespace firefly::kernel::mm { 12 | 13 | /* Represents kernel page tables. Global singleton. */ 14 | class kernelPageSpace : public VirtualSpace { 15 | private: 16 | friend class frg::manual_box; 17 | kernelPageSpace(PhysicalAddress root) { 18 | initSpace(root); 19 | } 20 | 21 | public: 22 | static void init(); 23 | static kernelPageSpace &accessor(); 24 | 25 | VIRTUAL_SPACE_FUNC_MAP_RANGE; 26 | VIRTUAL_SPACE_FUNC_UNMAP; 27 | VIRTUAL_SPACE_FUNC_MAP; 28 | }; 29 | 30 | /* Represents user processes page tables. Private, one (or more) per task. Currently unused (no userspace) */ 31 | class userPageSpace : VirtualSpace { 32 | userPageSpace(PhysicalAddress root) { 33 | initSpace(root); 34 | } 35 | 36 | void map(T virtual_addr, T physical_addr, AccessFlags flags, PageSize page_size) const override { 37 | (void)virtual_addr; 38 | (void)physical_addr; 39 | (void)flags; 40 | (void)page_size; 41 | logLine << "userPageSpace: map() is a stub!\n" 42 | << fmt::endl; 43 | } 44 | 45 | void unmap(T virtual_addr) const override { 46 | (void)virtual_addr; 47 | logLine << "userPageSpace: map() is a stub!\n" 48 | << fmt::endl; 49 | } 50 | }; 51 | 52 | } // namespace firefly::kernel::mm 53 | -------------------------------------------------------------------------------- /include/firefly/memory-manager/virtual/vspace.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "firefly/intel64/cpu/cpu.hpp" 4 | #include "firefly/intel64/page_flags.hpp" 5 | #include "firefly/intel64/page_permissions.hpp" 6 | #include "firefly/intel64/paging.hpp" 7 | #include "firefly/logger.hpp" 8 | #include "firefly/memory-manager/primary/primary_phys.hpp" 9 | #include "firefly/panic.hpp" 10 | 11 | /* Use these macros when no custom logic is needed. It's shorter and cleaner. */ 12 | #define VIRTUAL_SPACE_FUNC_UNMAP \ 13 | void unmap(T virt) const override { \ 14 | VirtualSpace::unmap(virt); \ 15 | } 16 | 17 | #define VIRTUAL_SPACE_FUNC_MAP \ 18 | void map(T virt, T phys, AccessFlags flags, PageSize page_size) const override { \ 19 | VirtualSpace::map(virt, phys, flags, page_size); \ 20 | } 21 | 22 | #define VIRTUAL_SPACE_FUNC_MAP_RANGE \ 23 | void mapRange(T base, T len, AccessFlags flags, AddressLayout off = AddressLayout::Low, PageSize page_size = PageSize::Size4K) const override { \ 24 | VirtualSpace::mapRange(base, len, flags, off, page_size); \ 25 | } 26 | 27 | namespace firefly::kernel::mm { 28 | 29 | using core::paging::AccessFlags; 30 | using core::paging::CacheMode; 31 | 32 | class VirtualSpace { 33 | public: 34 | VirtualSpace() = default; 35 | virtual ~VirtualSpace() { 36 | Physical::deallocate(PhysicalAddress(pml4)); 37 | } 38 | 39 | void operator delete([[maybe_unused]] void* ptr) { 40 | } 41 | 42 | void setCR3() { 43 | loadAddressSpace(); 44 | } 45 | 46 | protected: 47 | using T = uint64_t; 48 | bool hugePages = false; 49 | 50 | inline void initSpace(PhysicalAddress root) { 51 | pml4 = static_cast(root); 52 | hugePages = cpuHugePages(); 53 | } 54 | 55 | virtual void map(T virtual_addr, T physical_addr, AccessFlags flags, PageSize page_size) const { 56 | core::paging::map(virtual_addr, physical_addr, flags, pml4, page_size); 57 | } 58 | 59 | virtual void mapRange(uint64_t base, uint64_t len, AccessFlags flags, AddressLayout offset = AddressLayout::Low, PageSize page_size = PageSize::Size4K) const { 60 | for (uint64_t i = base; i < (base + len); i += page_size) 61 | map(i + offset, i, flags, page_size); 62 | } 63 | 64 | virtual void unmap([[maybe_unused]] T virtual_addr) const { 65 | firefly::panic("unmap() has not been implemented"); 66 | } 67 | 68 | inline void invalidate(VirtualAddress page) const { 69 | core::paging::invalidatePage(page); 70 | } 71 | 72 | inline void invalidate(T page) const { 73 | core::paging::invalidatePage(page); 74 | } 75 | 76 | inline core::paging::Pml* root() const { 77 | return pml4; 78 | } 79 | 80 | inline void loadAddressSpace() const { 81 | asm volatile("mov %0, %%cr3" ::"r"(reinterpret_cast(pml4))); 82 | } 83 | 84 | private: 85 | // T *pml4; 86 | core::paging::Pml* pml4; 87 | }; 88 | } // namespace firefly::kernel::mm 89 | -------------------------------------------------------------------------------- /include/firefly/panic.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "firefly/logger.hpp" 4 | #include "firefly/trace/strace.hpp" 5 | 6 | namespace firefly { 7 | 8 | [[gnu::used]] [[noreturn]] static void 9 | panic(const char *msg) { 10 | logLine << "\n**** Kernel panic ****\nReason: " << msg << "\n" 11 | << fmt::endl; 12 | trace::trace_callstack(); 13 | 14 | while (1) 15 | // asm volatile("cli; hlt"); 16 | asm volatile("sti; hlt"); 17 | } 18 | 19 | [[gnu::used]] [[noreturn]] static void 20 | assertion_failure_panic(const char *msg) { 21 | logLine << "\n**** Kernel panic ****\nAssertion failed: `" << msg << "\n" 22 | << fmt::endl; 23 | trace::trace_callstack(); 24 | 25 | while (1) 26 | asm volatile("cli; hlt"); 27 | } 28 | 29 | } // namespace firefly 30 | -------------------------------------------------------------------------------- /include/firefly/tasks/context.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "firefly/intel64/cpu/cpu.hpp" 4 | #include "libk++/memory.hpp" 5 | 6 | namespace firefly::kernel::tasks { 7 | 8 | /** 9 | * Class containing everything related to task context 10 | * this includes registers, stack, cr0 and so on 11 | */ 12 | class Context { 13 | friend class Task; 14 | 15 | public: 16 | Context(uintptr_t ip, uintptr_t sp) { 17 | memset(®s, 0, sizeof(ContextRegisters)); 18 | // set entrypoint 19 | regs.rip = ip; 20 | // set rflags 21 | RFlags rflags; 22 | rflags.fields.interruptEnable = 1; 23 | rflags.fields.carry = 1; 24 | regs.rflags = rflags.All; 25 | // TODO: user mode data selectors 26 | regs.cs = core::gdt::gdtEntryOffset(SegmentSelector::kernCS); 27 | regs.ss = core::gdt::gdtEntryOffset(SegmentSelector::kernDS); 28 | // setup the stack registers 29 | // TODO: change this 30 | regs.rbp = (int64_t)sp; 31 | regs.rsp = (int64_t)sp + 4096 * 2; 32 | } 33 | 34 | inline void save(const ContextRegisters* regs) { 35 | this->regs = *regs; 36 | } 37 | inline void load(ContextRegisters* regs) { 38 | *regs = this->regs; 39 | } 40 | 41 | private: 42 | ContextRegisters regs; 43 | }; 44 | 45 | } // namespace firefly::kernel::tasks 46 | -------------------------------------------------------------------------------- /include/firefly/tasks/scheduler.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "firefly/logger.hpp" 6 | #include "firefly/memory-manager/allocator.hpp" 7 | #include "firefly/tasks/task.hpp" 8 | 9 | namespace firefly::kernel::tasks { 10 | // TODO: Abstract, multiple sched impls 11 | class Scheduler { 12 | public: 13 | static Scheduler& accessor(); 14 | static void init(); 15 | 16 | Task* getTask() const { 17 | return currentTask; 18 | } 19 | 20 | /** 21 | * Get next task 22 | */ 23 | Task* schedule() { 24 | if (++n > tasks.size()) 25 | n = 1; 26 | currentTask = &tasks[n - 1]; 27 | return currentTask; 28 | } 29 | 30 | void addTask(Task task) { 31 | tasks.push(task); 32 | } 33 | 34 | protected: 35 | frg::vector tasks; 36 | Task* currentTask; 37 | 38 | private: 39 | uint64_t n; 40 | }; 41 | 42 | } // namespace firefly::kernel::tasks 43 | -------------------------------------------------------------------------------- /include/firefly/tasks/task.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "firefly/logger.hpp" 4 | #include "firefly/memory-manager/primary/primary_phys.hpp" 5 | #include "firefly/tasks/context.hpp" 6 | 7 | namespace firefly::kernel::tasks { 8 | class Task { 9 | public: 10 | Task(uintptr_t entrypoint, uintptr_t sp) 11 | : context(entrypoint, sp) { 12 | } 13 | 14 | void load(ContextRegisters* regs) { 15 | this->context.load(regs); 16 | } 17 | 18 | void save(const ContextRegisters* regs) { 19 | this->context.save(regs); 20 | } 21 | 22 | uintptr_t getRip() { 23 | return this->context.regs.rip; 24 | } 25 | 26 | private: 27 | Context context; 28 | }; 29 | } // namespace firefly::kernel::tasks 30 | -------------------------------------------------------------------------------- /include/firefly/timer/timer.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace firefly::kernel::timer { 6 | // This MUST setup a timer, else panics 7 | void init(); 8 | void start(); 9 | 10 | void tick(); 11 | void resetTicks(); 12 | 13 | void sleep(uint64_t ms); 14 | void usleep(uint64_t us); 15 | } // namespace firefly::kernel::timer 16 | -------------------------------------------------------------------------------- /include/firefly/trace/sanitizer/kasan.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "firefly/memory-manager/mm.hpp" 7 | 8 | namespace firefly::kernel::kasan { 9 | using KasanAddress = uintptr_t; 10 | 11 | /* Each byte of the shadow region encodes the status of the address it shadows */ 12 | enum ShadowRegionStatus : int8_t { 13 | // TODO: Add more region states such as a redzone for buffer overruns 14 | Unpoisoned = 0, 15 | Poisoned = ~0 16 | }; 17 | 18 | [[gnu::no_sanitize_address]] void init(); 19 | [[gnu::no_sanitize_address]] void poison(VirtualAddress addr, size_t size, ShadowRegionStatus status = ShadowRegionStatus::Poisoned); 20 | [[gnu::no_sanitize_address]] void unpoison(VirtualAddress addr, size_t size); 21 | 22 | #if defined(FIREFLY_KASAN) 23 | [[gnu::no_sanitize_address]] void verifyAccess(KasanAddress addr, size_t size, bool write); 24 | #endif 25 | } // namespace firefly::kernel::kasan -------------------------------------------------------------------------------- /include/firefly/trace/strace.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace firefly::trace { 4 | void trace_callstack(); 5 | // void panic(const char *msg); 6 | } // namespace firefly::trace -------------------------------------------------------------------------------- /include/firefly/trace/symbols.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | struct SymbolTablePair { 6 | uint64_t addr; 7 | const char *name; 8 | }; 9 | 10 | class SymbolTable { 11 | public: 12 | SymbolTablePair lookup(uint64_t) const noexcept; 13 | SymbolTablePair operator[](uint64_t) const noexcept; 14 | }; 15 | 16 | bool backtrace(uint64_t addr, int iteration); -------------------------------------------------------------------------------- /include/libk++/align.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace firefly::libkern { 6 | template 7 | inline T align_up4k(T in) { 8 | return (in + 4096 - 1) & ~(4096 - 1); 9 | } 10 | 11 | template 12 | inline T align_down4k(T in) { 13 | return (in) & ~(4096 - 1); 14 | } 15 | } // namespace firefly::libkern 16 | -------------------------------------------------------------------------------- /include/libk++/bits.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | static constexpr int kib_shift = 10; 4 | static constexpr int mib_shift = 20; 5 | 6 | #define BIT(o) (1 << o) 7 | #define MiB(o) BIT(mib_shift) * o 8 | #define GiB(o) MiB(1024L) * o -------------------------------------------------------------------------------- /include/libk++/cstring.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace firefly::libkern::cstring { 7 | size_t strlen(const char *str); 8 | char *strcpy(char *dest, const char *src); 9 | int strcmp(const char *s1, const char *s2); 10 | int strncmp(const char *str1, const char *str2, size_t n); 11 | char *strchr(const char *str, int c); 12 | char *strchrn(const char *str, int c, int n); 13 | int toupper(char c); 14 | char *strtok(char *s, const char *delimiters); 15 | } // namespace firefly::libkern::cstring -------------------------------------------------------------------------------- /include/libk++/fmt.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | namespace firefly::libkern::fmt { 8 | int vsnprintf(char* str, size_t size, const char* fmt, va_list ap); 9 | int printf(const char* fmt, ...); 10 | void itoa(size_t num, char* str, int base); 11 | int atoi(const char* str); 12 | char* strrev(char* str); 13 | } // namespace firefly::libkern::fmt 14 | 15 | 16 | #include 17 | 18 | namespace firefly { 19 | struct format { 20 | private: 21 | static constexpr const int BUFF_LEN{ 512 }; 22 | 23 | private: 24 | char itoc(int num); 25 | char itoh(int num, bool upper); 26 | void itoa(size_t num, char* s, int base); 27 | char* reverse(char* s); 28 | size_t len(const char* s); 29 | 30 | public: 31 | format() 32 | : base{ format_base::Dec } { 33 | } 34 | 35 | template 36 | void do_format(T in) requires std::is_integral_v { 37 | char out[32]; 38 | itoa(in, out, static_cast(base)); 39 | appendToBuffer(out); 40 | } 41 | 42 | void do_format(char in); 43 | void do_format(char* in); 44 | void do_format(const char* in); 45 | 46 | void appendToBuffer(char c); 47 | void appendToBuffer(const char* s); 48 | 49 | protected: 50 | enum format_base : int { 51 | Bin = 2, 52 | Oct = 8, 53 | Dec = 10, 54 | Hex = 16 55 | }; 56 | 57 | char buffer[BUFF_LEN]{ 0 }; 58 | size_t writer_position{ 0 }; 59 | format_base base; 60 | }; 61 | 62 | } // namespace firefly -------------------------------------------------------------------------------- /include/libk++/memory.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | extern "C" void *memset(void *dest, int val, size_t len); 7 | extern "C" void *memcpy(void *dest, const void *src, size_t len); 8 | int memcmp(const char *s1, const char *s2, int n); -------------------------------------------------------------------------------- /include/libk++/utils.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace firefly::kernel { 6 | uint32_t bgr2rgb(uint32_t bytes[], int offset); 7 | uint32_t rev32(uint32_t bytes); 8 | } // namespace firefly::kernel -------------------------------------------------------------------------------- /include/meson.build: -------------------------------------------------------------------------------- 1 | cxx_files += files( 2 | 'cstdlib/cmath.cpp' 3 | ) -------------------------------------------------------------------------------- /limine.cfg: -------------------------------------------------------------------------------- 1 | TIMEOUT=4 2 | GRAPHICS=yes 3 | BACKGROUND_PATH=boot:///firefly.bmp 4 | BACKGROUND_STYLE=centered 5 | THEME_BACKGROUND=FF000000 6 | THEME_MARGIN=30 7 | 8 | :FireflyOS 9 | KASLR=no 10 | PROTOCOL=limine 11 | KERNEL_PATH=boot:///kernel_x86_64.elf 12 | -------------------------------------------------------------------------------- /linkage/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 _start to be our entry point */ 6 | ENTRY(kernel_init) 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 | } 16 | 17 | SECTIONS 18 | { 19 | /* We wanna be placed in the topmost 2GiB of the address space, for optimisations */ 20 | /* and because that is what the Limine spec mandates. */ 21 | /* Any address in this region will do, but often 0xffffffff80000000 is chosen as */ 22 | /* that is the beginning of the region. */ 23 | . = 0xffffffff80000000; 24 | PROVIDE_HIDDEN(GLOB_PAGE_ARRAY = 0xFFFFD00000000000 + 512M); 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 | .bss : { 45 | *(COMMON) 46 | *(.bss .bss.*) 47 | } :data 48 | } 49 | -------------------------------------------------------------------------------- /meson.build: -------------------------------------------------------------------------------- 1 | project('FireflyOS', ['cpp', 'c'], default_options: ['cpp_std=c++2a', 'b_asneeded=false', 'b_lundef=false', 'buildtype=plain']) 2 | 3 | if not meson.is_cross_build() 4 | error('This is not a cross build, refer to the build instructions.') 5 | endif 6 | 7 | if meson.get_compiler('cpp').get_id() != 'gcc' 8 | error('Attempted to build with a compiler that is not gcc') 9 | endif 10 | 11 | # Set by subdirs 12 | c_files = [] 13 | cxx_files = [] 14 | asm_files = [] 15 | include_dir = include_directories('include/', 'include/cxxshim/', 'include/frigg/') 16 | 17 | subdir('include/') 18 | subdir('firefly/') # kernel 19 | 20 | if get_option('kasan') 21 | message('Enabling KASAN') 22 | warning('KASAN will significantly impact performance & memory usage, only use it for debugging purposes!') 23 | kasan_flags = ['-fsanitize=kernel-address', '--asan-shadow-offset=0xdffff00000000000', '-D FIREFLY_KASAN', '--param=asan-globals=0'] 24 | # kernel_c_flags += kasan_flags 25 | kernel_cxx_flags += kasan_flags 26 | cxx_files += ['firefly/kernel/trace/sanitizer/kasan_methods.cpp'] 27 | endif 28 | 29 | if get_option('ubsan') 30 | message('Enabling UBSAN') 31 | kernel_c_flags += '-fsanitize=undefined' 32 | kernel_cxx_flags += '-fsanitize=undefined' 33 | cxx_files += 'firefly/kernel/trace/sanitizer/ubsan.cpp' 34 | endif 35 | 36 | nasm = find_program('nasm') 37 | asm_gen = generator(nasm, output: '@BASENAME@.o', arguments: ['-felf64', '@INPUT@', '-g', '-F', 'dwarf', '-o', '@OUTPUT@']) 38 | asm_obj = asm_gen.process(asm_files) 39 | 40 | executable_kwargs = { 41 | 'cpp_args': kernel_cxx_flags, 42 | 'c_args': kernel_c_flags, 43 | 'include_directories': include_dir, 44 | 'link_args': ['-Wl,-T../linkage/linker_x86_64.ld', '-nostdlib', 'kernel_x86_64.elf.p/symtable.o', '../fonts/vgafont.obj', '-static', '-Wl,-z-max-page-size=0x1000'], 45 | } 46 | 47 | # Todo: symbol table should be a kernel module rather than re-linking the kernel 48 | run_command('scripts/gensym.sh', check: true) 49 | 50 | exe = executable( 51 | 'kernel_x86_64.elf', 52 | c_files, cxx_files, asm_obj, 53 | kwargs: executable_kwargs 54 | ) 55 | 56 | custom_target( 57 | 'Relink', 58 | depends: exe, 59 | input: exe, 60 | output: 'Target Relink failed', 61 | command: ['python3', '../scripts/symbol_table_x86_64.py', 'kernel_x86_64.elf'], 62 | build_by_default: true 63 | ) 64 | -------------------------------------------------------------------------------- /meson_config.txt: -------------------------------------------------------------------------------- 1 | [target_machine] 2 | cpu_family = 'x86' 3 | system = 'None (Freestanding)' 4 | cpu = 'x86_64' 5 | endian = 'little' 6 | 7 | [binaries] 8 | c = 'gcc' 9 | cpp = 'g++' 10 | cpp_ld = 'lld' -------------------------------------------------------------------------------- /meson_options.txt: -------------------------------------------------------------------------------- 1 | option('kasan', type: 'boolean', value: false) 2 | option('ubsan', type: 'boolean', value: false) 3 | -------------------------------------------------------------------------------- /scripts/geniso.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ISO=FireflyOS_x86_64.iso 4 | 5 | cd .. 6 | rm -f $ISO 7 | rm -rf iso_root 8 | mkdir iso_root/ 9 | 10 | cp limine/limine.sys limine/limine-cd.bin limine/limine-cd-efi.bin limine.cfg docs/firefly.bmp \ 11 | build/kernel_x86_64.elf iso_root/ 12 | 13 | xorriso -as mkisofs -b limine-cd.bin \ 14 | -no-emul-boot -boot-load-size 4 -boot-info-table \ 15 | --efi-boot limine-cd-efi.bin \ 16 | -efi-boot-part --efi-boot-image --protective-msdos-label \ 17 | iso_root -o $ISO 18 | 19 | limine/limine-deploy FireflyOS_x86_64.iso 20 | rm -rf iso_root 21 | -------------------------------------------------------------------------------- /scripts/gensym.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo '#include ' > build/parsed_x86_64.sym 4 | echo 'SymbolTablePair symbol_table[] = {{0xFFFFFFFFFFFFFFFF, ""}};' >> build/parsed_x86_64.sym 5 | mkdir build/kernel_x86_64.elf.p/ 6 | clang++ -x c++ -I include/firefly/trace -m64 -c build/parsed_x86_64.sym -o build/kernel_x86_64.elf.p/symtable.o -------------------------------------------------------------------------------- /scripts/qemu-bios-int-log.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | qemu-system-x86_64 -smp 4 -M smm=off -d int -serial stdio -cpu qemu64 -m 1G -boot d -no-shutdown -no-reboot -cdrom ../FireflyOS_x86_64.iso 4 | -------------------------------------------------------------------------------- /scripts/qemu-bios.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | qemu-system-x86_64 -smp 4 -enable-kvm -cpu host -m 8G -serial stdio -boot d -no-shutdown -no-reboot -cdrom ../FireflyOS_x86_64.iso 4 | -------------------------------------------------------------------------------- /scripts/qemu-gdb.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | qemu-system-x86_64 -enable-kvm -smp 4 -M smm=off -m 256M -boot d -no-shutdown -no-reboot -cdrom ../FireflyOS_x86_64.iso -d int -S -s 4 | -------------------------------------------------------------------------------- /scripts/qemu-log.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | qemu-system-x86_64 -M smm=off -m 256M -boot d -no-shutdown -no-reboot -cdrom ../FireflyOS_x86_64.iso -d int -------------------------------------------------------------------------------- /scripts/qemu-uefi.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | qemu-system-x86_64 -smp 4 -enable-kvm -M smm=off -cpu host -m 8G -boot d -no-shutdown -serial stdio -no-reboot -bios /usr/share/ovmf/OVMF.fd -cdrom ../FireflyOS_x86_64.iso 4 | -------------------------------------------------------------------------------- /scripts/symbol_table_x86_64.py: -------------------------------------------------------------------------------- 1 | from os import popen, system 2 | import sys 3 | 4 | PARSED = "parsed_x86_64.sym" 5 | KERNEL = sys.argv[1] 6 | writer = open(PARSED, "w+") 7 | 8 | symbol_table_naked = """ 9 | #include 10 | 11 | SymbolTablePair symbol_table[] = { 12 | """ 13 | 14 | def init_writer(): 15 | writer.seek(0) 16 | writer.truncate() 17 | writer.write(symbol_table_naked) 18 | 19 | # return an entry in the symbol array: {0xdeadbeef, "function"}, 20 | def symbol_template(address, function_name): 21 | return '\t{0x' + address + ', ' + '\"' + function_name + '\"' + '},\n' 22 | 23 | def write_data(address, function_name): 24 | if function_name == "": 25 | return 26 | writer.write(symbol_template(address=address, function_name=function_name)) 27 | 28 | 29 | def destroy_writer(): 30 | writer.write("{0xFFFFFFFFFFFFFFFF, \"\"}") 31 | writer.write("};") 32 | writer.close() 33 | 34 | # Input: A single line from objdump, returns true if it's a function, otherwise it returns false since we don't need variables or function names 35 | def is_function_symbol(objdump_line): 36 | if not ".text" in objdump_line: 37 | return False 38 | return True 39 | 40 | def function_symbol_extract(objdump_line): 41 | objdump_line = str(objdump_line).split() 42 | return (str(objdump_line[0]), str(objdump_line[5:]).replace("]", "").replace("'","").replace("[", "").replace(",,", ",")) 43 | 44 | def parse_symbol_tables(unparsed_sym_table): 45 | unparsed_sym_table = unparsed_sym_table.splitlines() 46 | 47 | for x in range(len(unparsed_sym_table)-1): 48 | if not is_function_symbol(unparsed_sym_table[x]): 49 | continue 50 | else: 51 | addr, name = function_symbol_extract(unparsed_sym_table[x]) 52 | write_data(str(addr), name) 53 | 54 | 55 | def compile_sym_table(): 56 | system(f"clang++ -ffreestanding -x c++ -c ./parsed_x86_64.sym -m64 -I ../include/firefly/trace -o kernel_x86_64.elf.p/symtable.o") 57 | 58 | if __name__ == '__main__': 59 | # This might seem really stupid, and that's because it is 60 | # But for some reason the script just won't write the symbol 61 | # table correctly the first time (These issues started occurring after switching from make to meson) 62 | # I have no idea what the issue is, and I don't care enough to find an actual solution 63 | # If you figure it out, please make a PR 64 | for x in range(2): 65 | init_writer() 66 | parse_symbol_tables(popen(f"objdump -C -t {KERNEL}").read()) 67 | destroy_writer() 68 | 69 | compile_sym_table() 70 | system('ld.lld -o ./kernel_x86_64.elf -T ../linkage/linker_x86_64.ld -nostdlib -m elf_x86_64 $(find ./ -name "*.o" -type f) ../fonts/vgafont.obj') # Relink kernel with symbol tables 71 | writer = open(PARSED, "w+") 72 | 73 | print("[*] Wrote symbol table") 74 | print("[*] Compiled symbol table") 75 | print('[*] Relinked kernel') --------------------------------------------------------------------------------