├── .clang-format ├── .clang-tidy ├── .github └── workflows │ ├── build.yaml │ └── guidelines.yaml ├── .gitignore ├── .gitmodules ├── LICENSE ├── Makefile ├── README.md ├── common ├── compiler.cmake └── include │ ├── attributes.h │ ├── errno.h │ ├── hal │ ├── gpio.h │ ├── i2c.h │ └── spi.h │ ├── meta.h │ ├── signal.h │ ├── sys │ └── wait.h │ ├── syscall.h │ └── syscall_defs.inc ├── docs ├── README.md ├── kernel │ ├── README.md │ ├── filesystems.md │ ├── hal.md │ ├── isr.md │ ├── memory.md │ ├── process.md │ └── scheduler.md ├── project_requirements.md └── project_structure.md ├── files ├── CMakeLists.txt ├── Makefile ├── lib │ ├── CMakeLists.txt │ ├── badge │ │ └── CMakeLists.txt │ ├── crt │ │ ├── CMakeLists.txt │ │ ├── start_amd64.S │ │ └── start_riscv.S │ └── syscall │ │ ├── CMakeLists.txt │ │ └── src │ │ ├── gpio.c │ │ ├── i2c.c │ │ ├── spi.c │ │ └── syscall.c └── sbin │ ├── CMakeLists.txt │ └── init │ ├── CMakeLists.txt │ └── main.c ├── kernel ├── CMakeLists.txt ├── Makefile ├── cpu │ ├── amd64 │ │ ├── CMakeLists.txt │ │ ├── include │ │ │ └── cpu │ │ │ │ ├── arch_cpulocal.h │ │ │ │ ├── hardware.h │ │ │ │ ├── interrupt.h │ │ │ │ ├── isr.h │ │ │ │ ├── isr_ctx.h │ │ │ │ ├── mmu.h │ │ │ │ ├── priv_level.h │ │ │ │ ├── regs.h │ │ │ │ ├── segmentation.h │ │ │ │ ├── x86_cpuid.h │ │ │ │ ├── x86_cr.h │ │ │ │ ├── x86_ioport.h │ │ │ │ └── x86_msr.h │ │ └── src │ │ │ ├── backtrace.c │ │ │ ├── entrypoint.S │ │ │ ├── interrupt.c │ │ │ ├── isr.S │ │ │ ├── isr.c │ │ │ ├── isr_ctx.S │ │ │ ├── isr_ctx.c │ │ │ ├── mmu.c │ │ │ ├── panic.c │ │ │ ├── scheduler.c │ │ │ ├── syscall.S │ │ │ ├── syscall.c │ │ │ ├── time.c │ │ │ └── usercopy.c │ ├── riscv │ │ ├── CMakeLists.txt │ │ ├── include │ │ │ └── cpu │ │ │ │ ├── arch_cpulocal.h │ │ │ │ ├── hardware.h │ │ │ │ ├── interrupt.h │ │ │ │ ├── interrupt │ │ │ │ ├── riscv_intc.h │ │ │ │ └── riscv_plic.h │ │ │ │ ├── isr.h │ │ │ │ ├── isr_ctx.h │ │ │ │ ├── mmu.h │ │ │ │ ├── regs.h │ │ │ │ ├── riscv.h │ │ │ │ ├── riscv_pmp.h │ │ │ │ └── riscv_sbi.h │ │ └── src │ │ │ ├── backtrace.c │ │ │ ├── cpu1_entrypoint.S │ │ │ ├── entrypoint.S │ │ │ ├── interrupt.c │ │ │ ├── interrupt │ │ │ ├── riscv_intc.c │ │ │ └── riscv_plic.c │ │ │ ├── isr.S │ │ │ ├── isr.c │ │ │ ├── isr_ctx.S │ │ │ ├── isr_ctx.c │ │ │ ├── memprotect │ │ │ ├── mmu.c │ │ │ └── riscv_pmp.c │ │ │ ├── panic.c │ │ │ ├── sbi_time.c │ │ │ ├── scheduler.c │ │ │ ├── syscall.S │ │ │ └── usercopy.c │ ├── riscv32 │ │ └── CMakeLists.txt │ └── riscv64 │ │ └── CMakeLists.txt ├── include │ ├── backtrace.h │ ├── badgelib │ │ ├── badge_format_str.h │ │ ├── fifo.h │ │ ├── hwtimer.h │ │ ├── log.h │ │ ├── malloc.h │ │ ├── mutex.h │ │ ├── num_to_str.h │ │ ├── panic.h │ │ ├── rawprint.h │ │ ├── semaphore.h │ │ ├── spinlock.h │ │ ├── static-buddy.h │ │ ├── time.h │ │ └── time_private.h │ ├── blockdevice.h │ ├── blockdevice │ │ ├── blkdev_impl.h │ │ ├── blkdev_internal.h │ │ └── blkdev_ram.h │ ├── cpulocal.h │ ├── filesystem.h │ ├── filesystem │ │ ├── fs_fat.h │ │ ├── fs_fat_types.h │ │ ├── fs_ramfs.h │ │ ├── fs_ramfs_types.h │ │ ├── syscall_impl.h │ │ ├── vfs_fifo.h │ │ ├── vfs_internal.h │ │ ├── vfs_types.h │ │ └── vfs_vtable.h │ ├── housekeeping.h │ ├── interrupt.h │ ├── isr_ctx.h │ ├── loading │ │ └── kbelf_pre.h │ ├── memprotect.h │ ├── page_alloc.h │ ├── process │ │ ├── internal.h │ │ ├── process.h │ │ ├── sighandler.h │ │ ├── syscall_impl.h │ │ └── types.h │ ├── scheduler │ │ ├── cpu.h │ │ ├── isr.h │ │ ├── scheduler.h │ │ ├── types.h │ │ └── waitlist.h │ ├── smp.h │ ├── stdlib.h │ ├── syscall_util.h │ ├── todo.h │ └── usercopy.h ├── port │ ├── esp32c6 │ │ ├── CMakeLists.txt │ │ ├── bootloader.bin │ │ ├── gdbinit │ │ ├── include │ │ │ ├── port │ │ │ │ ├── clkconfig.h │ │ │ │ ├── hardware.h │ │ │ │ ├── hardware_allocation.h │ │ │ │ ├── interrupt.h │ │ │ │ ├── memprotect.h │ │ │ │ ├── pmu.h │ │ │ │ ├── pmu_init.h │ │ │ │ └── port.h │ │ │ ├── sdkconfig.h │ │ │ └── soc │ │ │ │ ├── io_mux_struct.h │ │ │ │ ├── plic_struct.h │ │ │ │ └── soc_types.h │ │ ├── linker.ld │ │ ├── partition-table.bin │ │ ├── port.mk │ │ └── src │ │ │ ├── clkconfig.c │ │ │ ├── hal │ │ │ ├── i2c.c │ │ │ └── spi.c │ │ │ ├── interrupt.c │ │ │ ├── memprotect.c │ │ │ ├── pmu_init.c │ │ │ ├── port.c │ │ │ └── smp.c │ ├── esp32p4 │ │ ├── CMakeLists.txt │ │ ├── bootloader.bin │ │ ├── include │ │ │ ├── port │ │ │ │ ├── hardware.h │ │ │ │ ├── hardware_allocation.h │ │ │ │ ├── interrupt.h │ │ │ │ ├── memprotect.h │ │ │ │ ├── pmu_init.h │ │ │ │ └── port.h │ │ │ ├── sdkconfig.h │ │ │ └── soc │ │ │ │ ├── clic_struct.h │ │ │ │ └── soc_types.h │ │ ├── linker.ld │ │ ├── partition-table.bin │ │ ├── port.mk │ │ └── src │ │ │ ├── interrupt.c │ │ │ ├── memprotect.c │ │ │ ├── pmu_init.c │ │ │ ├── port.c │ │ │ └── smp.c │ ├── esp_common │ │ ├── CMakeLists.txt │ │ ├── include │ │ │ ├── esp_assert.h │ │ │ ├── esp_attr.h │ │ │ ├── esp_bit_defs.h │ │ │ ├── esp_intmtx.h │ │ │ ├── esp_rom_sys.h │ │ │ ├── hal │ │ │ │ ├── assert.h │ │ │ │ ├── log.h │ │ │ │ ├── misc.h │ │ │ │ └── regi2c_ctrl.h │ │ │ ├── port │ │ │ │ ├── smp.h │ │ │ │ └── time.h │ │ │ └── time.h │ │ ├── port.mk │ │ └── src │ │ │ ├── gpio.c │ │ │ ├── hwtimer.c │ │ │ ├── i2c.c │ │ │ └── time.c │ └── generic │ │ ├── CMakeLists.txt │ │ ├── gdbinit │ │ ├── include │ │ ├── driver.h │ │ ├── driver │ │ │ ├── ata.h │ │ │ ├── ata │ │ │ │ ├── ahci.h │ │ │ │ └── ahci │ │ │ │ │ ├── command.h │ │ │ │ │ ├── fis.h │ │ │ │ │ ├── generic_host_ctrl.h │ │ │ │ │ ├── pci_cap.h │ │ │ │ │ └── port.h │ │ │ ├── pcie.h │ │ │ └── pcie │ │ │ │ ├── bar.h │ │ │ │ ├── class.h │ │ │ │ └── confspace.h │ │ └── port │ │ │ ├── dtb.h │ │ │ ├── dtparse.h │ │ │ ├── hardware.h │ │ │ ├── hardware_allocation.h │ │ │ ├── interrupt.h │ │ │ ├── limine_req.h │ │ │ ├── memprotect.h │ │ │ ├── port.h │ │ │ ├── smp.h │ │ │ └── time.h │ │ ├── limine.conf │ │ ├── linker_amd64.ld │ │ ├── linker_riscv64.ld │ │ ├── port.mk │ │ ├── port_amd64.mk │ │ ├── port_riscv64.mk │ │ └── src │ │ ├── driver │ │ ├── ata │ │ │ ├── blkdev_ata.c │ │ │ └── sata_ahci_pcie.c │ │ └── pcie.c │ │ ├── dtb.c │ │ ├── dtparse.c │ │ ├── hal │ │ ├── gpio.c │ │ ├── i2c.c │ │ └── spi.c │ │ ├── memprotect.c │ │ ├── port.c │ │ ├── smp.c │ │ └── uacpi_kernel_api.c └── src │ ├── badgelib │ ├── badge_format_str.c │ ├── fifo.c │ ├── log.c │ ├── mutex.c │ ├── num_to_str.c │ ├── panic.c │ ├── rawprint.c │ ├── semaphore.c │ └── spinlock.c │ ├── blockdevice │ ├── blkdev_ram.c │ └── blockdevice.c │ ├── filesystem │ ├── filesystem.c │ ├── fs_fat.c │ ├── fs_ramfs.c │ ├── syscall_impl.c │ ├── vfs_fifo.c │ └── vfs_internal.c │ ├── freestanding │ ├── int_routines.c │ └── string.c │ ├── hal │ └── syscall_impl.c │ ├── housekeeping.c │ ├── interrupt.c │ ├── main.c │ ├── malloc │ ├── bitops.h │ ├── build.sh │ ├── compiler.h │ ├── debug.h │ ├── main.c │ ├── malloc.c │ ├── slab-alloc.c │ ├── spinlock.h │ └── static-buddy.c │ ├── page_alloc.c │ ├── process │ ├── kbelfx.c │ ├── proc_memmap.c │ ├── process.c │ ├── sighandler.c │ ├── syscall_impl.c │ └── syscall_util.c │ ├── scheduler │ ├── scheduler.c │ └── waitlist.c │ ├── syscall.c │ └── time.c ├── shell.nix └── tools ├── address-filter.py ├── bin2c.py ├── config.py ├── pack-image.py ├── pagetable.py └── ramfs-gen.py /.github/workflows/build.yaml: -------------------------------------------------------------------------------- 1 | name: CMake 2 | 3 | on: 4 | push: 5 | branches: ['main'] 6 | pull_request: 7 | branches: ['main'] 8 | 9 | env: 10 | CMAKE_GENERATOR: Ninja 11 | 12 | jobs: 13 | build: 14 | strategy: 15 | matrix: 16 | # Build with all four build types: 17 | build_type: ['Debug', 'Release', 'RelWithDebInfo', 'MinSizeRel'] 18 | 19 | runs-on: ubuntu-22.04 20 | 21 | steps: 22 | - uses: actions/checkout@v3 23 | 24 | - name: Fetch submodules 25 | run: git submodule update --init 26 | 27 | - name: Install ninja 28 | run: sudo apt-get install -y ninja-build 29 | 30 | - name: Fetch & install RISC-V GNU Compiler Toolchain 31 | run: | 32 | curl -Lo riscv32-glibc-ubuntu-22.04-nightly.tar.gz 'https://github.com/riscv-collab/riscv-gnu-toolchain/releases/download/2023.06.09/riscv32-glibc-ubuntu-22.04-nightly-2023.06.09-nightly.tar.gz' 33 | tar -xf riscv32-glibc-ubuntu-22.04-nightly.tar.gz 34 | echo "${PWD}/riscv/bin" >> $GITHUB_PATH 35 | export PATH=$PATH:${PWD}/riscv/bin 36 | make hh24_defconfig 37 | 38 | - name: Build 39 | run: CMAKE_BUILD_TYPE=${{matrix.build_type}} make clean build 40 | 41 | - name: Build Statistics 42 | run: | 43 | riscv32-unknown-linux-gnu-size -B kernel/firmware/badger-os.elf 44 | 45 | - name: Upload 46 | uses: actions/upload-artifact@v3 47 | with: 48 | name: Firmware 49 | path: | 50 | kernel/firmware/badger-os.elf 51 | kernel/firmware/badger-os.bin 52 | -------------------------------------------------------------------------------- /.github/workflows/guidelines.yaml: -------------------------------------------------------------------------------- 1 | name: Code Check 2 | 3 | on: 4 | push: 5 | branches: ['main'] 6 | pull_request: 7 | branches: ['main'] 8 | 9 | jobs: 10 | validate-format: 11 | runs-on: ubuntu-22.04 12 | 13 | steps: 14 | - uses: actions/checkout@v3 15 | 16 | - name: Fetch submodules 17 | run: git submodule update --init 18 | 19 | - name: Install clang-format 20 | run: | 21 | sudo apt-get update -y 22 | 23 | # see https://apt.llvm.org/ 24 | wget https://apt.llvm.org/llvm.sh 25 | chmod +x llvm.sh 26 | sudo ./llvm.sh 18 27 | 28 | sudo apt-get install -y clang-format-18 29 | 30 | - name: Fetch & install RISC-V GNU Compiler Toolchain 31 | run: | 32 | curl -Lo riscv32-glibc-ubuntu-22.04-nightly.tar.gz 'https://github.com/riscv-collab/riscv-gnu-toolchain/releases/download/2023.06.09/riscv32-glibc-ubuntu-22.04-nightly-2023.06.09-nightly.tar.gz' 33 | tar -xf riscv32-glibc-ubuntu-22.04-nightly.tar.gz 34 | echo "${PWD}/riscv/bin" >> $GITHUB_PATH 35 | export PATH=$PATH:${PWD}/riscv/bin 36 | make hh24_defconfig cmake-configure 37 | 38 | - name: Run clang-format 39 | run: | 40 | make clang-format-check 41 | 42 | 43 | static-analysis: 44 | runs-on: ubuntu-22.04 45 | 46 | steps: 47 | - uses: actions/checkout@v3 48 | 49 | - name: Fetch submodules 50 | run: git submodule update --init 51 | 52 | - name: Install clang-tidy 53 | run: | 54 | sudo apt-get update -y 55 | 56 | # see https://apt.llvm.org/ 57 | wget https://apt.llvm.org/llvm.sh 58 | chmod +x llvm.sh 59 | sudo ./llvm.sh 18 60 | 61 | sudo apt-get install -y clang-tidy-18 62 | 63 | - name: Fetch & install RISC-V GNU Compiler Toolchain 64 | run: | 65 | curl -Lo riscv32-glibc-ubuntu-22.04-nightly.tar.gz 'https://github.com/riscv-collab/riscv-gnu-toolchain/releases/download/2023.06.09/riscv32-glibc-ubuntu-22.04-nightly-2023.06.09-nightly.tar.gz' 66 | tar -xf riscv32-glibc-ubuntu-22.04-nightly.tar.gz 67 | echo "${PWD}/riscv/bin" >> $GITHUB_PATH 68 | export PATH=$PATH:${PWD}/riscv/bin 69 | make hh24_defconfig cmake-configure 70 | 71 | - name: Run clang-tidy 72 | run: | 73 | make clang-tidy-check 74 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__/ 2 | build/ 3 | .vscode/ 4 | .ccache/ 5 | .cache/ 6 | firmware/ 7 | app/ 8 | root/ 9 | .venv/ 10 | .config 11 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "lib/kbelf"] 2 | path = kernel/lib/kbelf 3 | url = https://github.com/robotman2412/KiloElfLoader 4 | [submodule "kernel/port/esp_common/esp-idf"] 5 | path = kernel/port/esp_common/esp-idf 6 | url = https://github.com/espressif/esp-idf 7 | [submodule "kernel/lib/opensbi"] 8 | path = kernel/lib/opensbi 9 | url = https://github.com/riscv-software-src/opensbi 10 | [submodule "kernel/lib/u-boot"] 11 | path = kernel/lib/u-boot 12 | url = https://github.com/u-boot/u-boot 13 | [submodule "kernel/lib/badgelib"] 14 | path = common/badgelib 15 | url = https://github.com/badgeteam/badgelib 16 | [submodule "kernel/lib/uacpi"] 17 | path = kernel/lib/uacpi 18 | url = https://github.com/ultraos/uacpi 19 | [submodule "kernel/lib/limine"] 20 | path = kernel/lib/limine 21 | url = https://github.com/limine-bootloader/limine 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Badge.team 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | # SPDX-License-Identifier: MIT 3 | 4 | MAKEFLAGS += --silent 5 | SHELL := /usr/bin/env bash 6 | 7 | .PHONY: all 8 | all: build 9 | 10 | .PHONY: config 11 | configure: config 12 | config: 13 | ./tools/config.py 14 | git submodule update --init 15 | $(MAKE) -C kernel _on_config 16 | 17 | .PHONY: hh24_defconfig 18 | hh24_defconfig: 19 | ./tools/config.py --target esp32c6 --use-default 20 | $(MAKE) -C kernel _on_config 21 | 22 | .PHONY: why2025_defconfig 23 | why2025_defconfig: 24 | ./tools/config.py --target esp32p4 --use-default 25 | $(MAKE) -C kernel _on_config 26 | 27 | .PHONY: unmatched_defconfig 28 | unmatched_defconfig: 29 | ./tools/config.py --target generic --use-default --vec_spec none 30 | $(MAKE) -C kernel _on_config 31 | 32 | .PHONY: build 33 | build: 34 | $(MAKE) -C files build 35 | $(MAKE) -C kernel build 36 | 37 | .PHONY: clean 38 | clean: 39 | $(MAKE) -C files clean 40 | $(MAKE) -C kernel clean 41 | 42 | .PHONY: cmake-configure 43 | cmake-configure: 44 | $(MAKE) -C files build 45 | $(MAKE) -C kernel cmake-configure 46 | 47 | .PHONY: openocd 48 | openocd: 49 | $(MAKE) -C kernel openocd 50 | 51 | .PHONY: gdb 52 | gdb: 53 | $(MAKE) -C kernel gdb 54 | 55 | .PHONY: qemu 56 | qemu: 57 | $(MAKE) -C files build 58 | $(MAKE) -C kernel qemu 59 | 60 | .PHONY: flash 61 | flash: 62 | $(MAKE) -C files build 63 | $(MAKE) -C kernel flash 64 | 65 | .PHONY: image 66 | image: 67 | $(MAKE) -C files build 68 | $(MAKE) -C kernel image 69 | 70 | .PHONY: burn 71 | burn: 72 | $(MAKE) -C files build 73 | $(MAKE) -C kernel burn 74 | 75 | .PHONY: monitor 76 | monitor: 77 | $(MAKE) -C kernel monitor 78 | 79 | .PHONY: clang-format-check 80 | clang-format-check: 81 | $(MAKE) -C kernel clang-format-check 82 | 83 | .PHONY: clang-tidy-check 84 | clang-tidy-check: 85 | $(MAKE) -C kernel clang-tidy-check 86 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BadgerOS 2 | 3 | BadgerOS is a hobby operating system. 4 | 5 | ## Index 6 | - [Contributing](#contributing) 7 | - [Prerequisites](#prerequisites) 8 | - [Build system](#build-system) 9 | - [Credits](#credits) 10 | 11 | 12 | 13 | # [Documentation](./docs/README.md) 14 | 15 | 16 | 17 | # Contributing 18 | We are an open-source project, so we can always use more hands! 19 | If you're new to this project and want to help, message us: 20 | - [RobotMan2412](https://t.me/robotman2412) on telegram 21 | - [Badge.team](https://t.me/+StQpEWyhnb96Y88p) telegram group 22 | 23 | After that, see [Project structure](./docs/project_structure.md) for reference about how this project works. 24 | 25 | 26 | 27 | # Prerequisites 28 | To be able build the project, we need to install tools and a compiler required for RISCV architecture. 29 | 30 | ### On ubuntu, run: 31 | ```sh 32 | sudo apt install -y gcc-riscv64-linux-gnu build-essential git picocom cmake python3 33 | ``` 34 | Above command installs all the tools and compiler listed below. 35 | 36 | ### Tools: 37 | - git 38 | - build-essential 39 | - cmake 40 | - python3 41 | - picocom 42 | 43 | ### RISCV compiler: 44 | - A RISC-V toolchain, one of: 45 | - [BadgerOS buildroot](https://github.com/badgeteam/mch2025-badgeros-buildroot), preferably riscv64 46 | - `gcc-riscv64-linux-gnu` (ubuntu) / `riscv64-gnu-toolchain-glibc-bin` (arch) 47 | 48 | 49 | ## For RISC-V PC port 50 | If you don't know what this is, you don't need this. All of: 51 | - mtools 52 | - swig 53 | - gptfdisk 54 | 55 | 56 | 57 | # Build system 58 | The build system is based on Makefiles and CMake. 59 | The following commands can be used to perform relevant actions: 60 | 61 | To select target platform, choose one of: 62 | - `make config` (manual configuration) 63 | - `make hh24_defconfig` (HackerHotel 2024 badge) 64 | - `make why2025_defconfig` (WHY2025 badge) 65 | - `make unmatched_defconfig` (RISC-V PC port) 66 | 67 | 68 | **Navigate to the project directory:** `cd /path/to/BadgerOS` 69 | 70 | **1. To build, run:** `make build` 71 | 72 | **2. To remove build files, run:** `make clean` 73 | 74 | **3. To flash to an ESP, run:** `make flash` 75 | 76 | **4. To open picocom, run:** `make monitor` 77 | 78 | **5. To build, flash and open picocom, run:** `make` or `make all` 79 | 80 | To check code style: `make clang-format-check` (code formatting) and `make clang-tidy-check` (programming guidelines) 81 | 82 | Build artifacts will be put into the `kernel/firmware` folder once the project was successfully built. 83 | -------------------------------------------------------------------------------- /common/compiler.cmake: -------------------------------------------------------------------------------- 1 | 2 | # SPDX-License-Identifier: MIT 3 | 4 | cmake_minimum_required(VERSION 3.10.0) 5 | 6 | # Determine the compiler prefix 7 | set(CMAKE_C_COMPILER "${CONFIG_COMPILER}") 8 | set(BADGER_OBJCOPY "${CONFIG_TC_PREFIX}objcopy") 9 | set(BADGER_OBJDUMP "${CONFIG_TC_PREFIX}objdump") 10 | 11 | # Determine arch options 12 | if("${CONFIG_CPU}" STREQUAL "riscv32") 13 | set(target_arch_prefix rv32) 14 | set(target_abi ilp32) 15 | elseif("${CONFIG_CPU}" STREQUAL "riscv64") 16 | set(target_arch_prefix rv64) 17 | set(target_abi lp64) 18 | else() 19 | set(target_arch_prefix amd64) 20 | set(target_abi sysv) 21 | endif() 22 | 23 | if("${CONFIG_CPU}" STREQUAL "amd64") 24 | set(target_arch "x86-64") 25 | else() 26 | if("${CONFIG_FLOAT_SPEC}" STREQUAL "single") 27 | set(target_float_frac f) 28 | elseif("${CONFIG_FLOAT_SPEC}" STREQUAL "double") 29 | set(target_float_frac fd) 30 | else() 31 | set(target_float_frac) 32 | endif() 33 | 34 | set(target_arch "${target_arch_prefix}ima${target_float_frac}c_zicsr_zifencei") 35 | endif() -------------------------------------------------------------------------------- /common/include/attributes.h: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #pragma once 5 | 6 | // this file provides convenience macros for the attributes provided by gcc: 7 | // https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html 8 | 9 | #ifdef BADGEROS_KERNEL 10 | #include 11 | #ifdef CONFIG_TARGET_generic 12 | // Function that must be in RAM for XIP targets. 13 | #define RAMFUNC 14 | #else 15 | // Function that must be in RAM for XIP targets. 16 | #define RAMFUNC __attribute__((section(".ramtext"))) 17 | #endif 18 | #endif 19 | 20 | // Disable address sanitization for a function. 21 | #define NOASAN __attribute__((no_sanitize("address"))) 22 | 23 | // Declares that a function cannot return. 24 | #define NORETURN __attribute__((noreturn)) 25 | 26 | // Declares that a function has no observable side effects and does not mutate its parameters. 27 | #define PURE __attribute__((pure)) 28 | 29 | // Declares that a function has no observable side effects, does not mutate its parameters and does not read memory. 30 | #define CONST __attribute__((const)) 31 | 32 | // Declares that a function is not called very often and can be size-optimized even in fast builds. 33 | #define COLD __attribute__((cold)) 34 | 35 | // Declares that a function is called very often and should be speed-optimized even in small builds. 36 | #define HOT __attribute__((hot)) 37 | 38 | // Declares that a function call must be inlined whenever possible. 39 | #define FORCEINLINE __attribute__((always_inline)) 40 | 41 | // Declares that a symbol will be placed in a section called `name` 42 | #define SECTION(name) __attribute__((section(name))) 43 | 44 | // Declares that a symbol will be aligned to `alignment` 45 | #define ALIGNED_TO(alignment) __attribute__((aligned(alignment))) 46 | 47 | // Packed struct (don't add padding to align fields). 48 | #define PACKED __attribute__((packed)) 49 | 50 | // Function written in assembly. 51 | #define NAKED __attribute__((naked)) 52 | 53 | // Volatile variables. 54 | #define VOLATILE volatile 55 | 56 | // Struct is little-endian. 57 | #define LITTLE_ENDIAN __attribute__((scalar_storage_order("little-endian"))) 58 | 59 | // Struct is big-endian. 60 | #define BIG_ENDIAN __attribute__((scalar_storage_order("bog-endian"))) 61 | -------------------------------------------------------------------------------- /common/include/hal/gpio.h: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #pragma once 5 | 6 | #include "attributes.h" 7 | #include "badge_err.h" 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | typedef enum { 14 | // high-impedance; no digital or analog functions 15 | IO_MODE_HIGH_Z, 16 | // digital output pin; io_write 17 | IO_MODE_OUTPUT, 18 | // digital input pin; optionally with pull resistors; io_read 19 | IO_MODE_INPUT, 20 | // PWM-based output pin; io_pwm_write 21 | IO_MODE_PWM, 22 | // DAC-based output pin; io_dac_write 23 | IO_MODE_DAC, 24 | // ADC-based input pin; optionally with pull resistors; io_adc_read 25 | IO_MODE_ADC, 26 | } io_mode_t; 27 | 28 | typedef enum { 29 | // no internal pull resistors; floating 30 | IO_PULL_NONE, 31 | // internal pull-up resistor; disconnected pins will read VCC 32 | IO_PULL_UP, 33 | // internal pull-down resistor; disconnected pins will read 0v 34 | IO_PULL_DOWN, 35 | } io_pull_t; 36 | 37 | 38 | // Returns the amount of GPIO pins present. 39 | // Cannot produce an error. 40 | int io_count() CONST; 41 | // Sets the mode of GPIO pin `pin` to `mode`. 42 | void io_mode(badge_err_t *ec, int pin, io_mode_t mode); 43 | // Get the mode of GPIO pin `pin`. 44 | io_mode_t io_getmode(badge_err_t *ec, int pin); 45 | // Sets the pull resistor behaviour of GPIO pin `pin` to `dir`. 46 | void io_pull(badge_err_t *ec, int pin, io_pull_t dir); 47 | // Get the pull resistor behaviour of GPIO pin `pin`. 48 | io_pull_t io_getpull(badge_err_t *ec, int pin); 49 | // Writes level to GPIO pin pin. 50 | void io_write(badge_err_t *ec, int pin, bool level); 51 | // Reads logic level value from GPIO pin `pin`. 52 | // Returns false on error. 53 | bool io_read(badge_err_t *ec, int pin); 54 | // Determine whether GPIO `pin` is claimed by a peripheral. 55 | // Returns false on error. 56 | bool io_is_peripheral(badge_err_t *ec, int pin); 57 | -------------------------------------------------------------------------------- /common/include/hal/spi.h: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #pragma once 5 | 6 | #include "attributes.h" 7 | #include "badge_err.h" 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | // Returns the amount of SPI peripherals present. 14 | // Cannot produce an error. 15 | int spi_count() CONST; 16 | 17 | // Initialises SPI peripheral spi_num in controller mode with SCLK pin `sclk_pin`, MOSI pin `mosi_pin`, MISO pin 18 | // `miso_pin`, SS pin `ss_pin` and clock speed/bitrate bitrate. The modes of the SCLK, MOSI, MISO and SS pins are 19 | // changed automatically. This function may be used again to change the settings on an initialised SPI peripheral in 20 | // controller mode. 21 | void spi_controller_init( 22 | badge_err_t *ec, int spi_num, int sclk_pin, int mosi_pin, int miso_pin, int ss_pin, int32_t bitrate 23 | ); 24 | // De-initialises SPI peripheral. 25 | void spi_deinit(badge_err_t *ec, int spi_num); 26 | // Reads len bytes into buffer buf from SPI device. 27 | // This function blocks until the entire transaction is completed. 28 | void spi_controller_read(badge_err_t *ec, int spi_num, void *buf, size_t len); 29 | // Writes len bytes from buffer buf to SPI device. 30 | // This function blocks until the entire transaction is completed. 31 | void spi_controller_write(badge_err_t *ec, int spi_num, void const *buf, size_t len); 32 | // Writes len bytes from buffer buf to SPI device, then reads len bytes into buffer buf from SPI device. 33 | // Write and read happen simultaneously if the full-duplex flag fdx is set. 34 | // This function blocks until the entire transaction is completed. 35 | void spi_controller_transfer(badge_err_t *ec, int spi_num, void *buf, size_t len, bool fdx); 36 | -------------------------------------------------------------------------------- /common/include/meta.h: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | // A meta-programming macro to obtain a pointer to `Container` from 11 | // a pointer to `field_name`. 12 | #define field_parent_ptr(Container, field_name, pointer_to_field) \ 13 | ((Container *)(((char *)(pointer_to_field)) - offsetof(Container, field_name))) 14 | 15 | // Takes whatever `value` is and returns it as a string literal 16 | #define comptime_stringify(value) #value 17 | 18 | // Returns the inner `value` of the given macro as a string. Can be used to 19 | // convert things like numbers or `__LINE__` to string form. 20 | #define convert_macro_to_string(value) comptime_stringify(value) 21 | 22 | // Concatenate two keywords. 23 | #define concat_keywords(a, b) a##b 24 | -------------------------------------------------------------------------------- /common/include/signal.h: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #pragma once 5 | 6 | #define SIGHUP 1 7 | #define SIGINT 2 8 | #define SIGQUIT 3 9 | #define SIGILL 4 10 | #define SIGTRAP 5 11 | #define SIGABRT 6 12 | #define SIGBUS 7 13 | #define SIGFPE 8 14 | #define SIGKILL 9 15 | #define SIGUSR1 10 16 | #define SIGSEGV 11 17 | #define SIGUSR2 12 18 | #define SIGPIPE 13 19 | #define SIGALRM 14 20 | #define SIGTERM 15 21 | #define SIGSTKFLT 16 22 | #define SIGCHLD 17 23 | #define SIGCONT 18 24 | #define SIGSTOP 19 25 | #define SIGTSTP 20 26 | #define SIGTTIN 21 27 | #define SIGTTOU 22 28 | #define SIGURG 23 29 | #define SIGXCPU 24 30 | #define SIGXFSZ 25 31 | #define SIGVTALRM 26 32 | #define SIGPROF 27 33 | #define SIGWINCH 28 34 | #define SIGIO 29 35 | #define SIGPWR 30 36 | #define SIGSYS 31 37 | 38 | #define SIG_COUNT 32 39 | 40 | 41 | #ifdef BADGEROS_KERNEL 42 | 43 | // Ignore this signal. 44 | #define SIG_IGN ((size_t)0) 45 | // Assign the default action to this signal. 46 | #define SIG_DFL SIZE_MAX 47 | // Bitmask of signals that kill the process by default. 48 | #define SIG_DFL_KILL_MASK \ 49 | ((1llu << SIGHUP) | (1llu << SIGSEGV) | (1llu << SIGILL) | (1llu << SIGTRAP) | (1llu << SIGTERM) | \ 50 | (1llu << SIGKILL) | (1llu << SIGABRT) | (1llu << SIGSYS)) 51 | // Signal name table. 52 | extern char const *signames[SIG_COUNT]; 53 | 54 | #else 55 | 56 | // Ignore this signal. 57 | #define SIG_IGN ((void *)0) 58 | // Assign the default action to this signal. 59 | #define SIG_DFL ((void *)-1) 60 | 61 | // Signal handler. 62 | typedef void (*sighandler_t)(int signum); 63 | 64 | // Set the handler or action for a particular signal. 65 | sighandler_t signal(int signum, sighandler_t handler); 66 | 67 | #endif 68 | -------------------------------------------------------------------------------- /common/include/sys/wait.h: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #pragma once 5 | 6 | // Don't block waiting. 7 | #define WNOHANG 0x00000001 8 | // Match children that have stopped but not been traced. 9 | #define WUNTRACED 0x00000002 10 | // Report continued child. 11 | #define WCONTINUED 0x00000008 12 | // Don't reap, just poll status. 13 | #define WNOWAIT 0x01000000 14 | 15 | // Create status for child exited normally. 16 | #define W_EXITED(ret) ((ret) << 8) 17 | // Create status for child exited by signal. 18 | #define W_SIGNALLED(sig) (((sig) << 8) | 0x40) 19 | // Create status for child that was suspended by a signal. 20 | #define W_STOPCODE(sig) (((sig) << 8) | 0x20) 21 | // Child was continued. 22 | #define W_CONTINUED 0x10 23 | // Child dumped core. 24 | #define WCOREFLAG 0x80 25 | 26 | // What the exit code was. 27 | #define WEXITSTATUS(status) (((status) & 0xff00) >> 8) 28 | // What the terminating signal was. 29 | #define WTERMSIG(status) WEXITSTATUS(status) 30 | // What signal suspended the child. 31 | #define WSTOPSIG(status) WEXITSTATUS(status) 32 | 33 | // Whether the child dumped core. 34 | #define WCOREDUMP(status) ((status) & WCOREFLAG) 35 | // Whether the child exited normally (by means of `SYSCALL_PROC_EXIT`). 36 | #define WIFEXITED(status) (((status) & 0xff) == 0) 37 | // Whether the child was killed by a signal. 38 | #define WIFSIGNALED(status) ((status) & 0x40) 39 | // Whether the child was suspended by a signal. 40 | #define WIFSTOPPED(status) ((status) & 0x20) 41 | // Whether the child was resumed by `SIGCONT`. 42 | #define WIFCONTINUED(status) ((status) & 0x10) 43 | 44 | // Match any process for `waitpid`. 45 | #define WAIT_ANY (-1) 46 | // Match processes in current process group for `waitpid`. 47 | #define WAIT_MYPGRP 0 48 | 49 | #ifndef BADGEROS_KERNEL 50 | // Wait for a child with a matching PID to die: 51 | // - If `pid` is -1, match any child. 52 | // - If `pid` is 0, match any child in the process group. 53 | // - If `pid` < -1, match any child in the process group `-pid`. 54 | // - If `pid` > 0, match the child with the specified PID. 55 | // 56 | // With certain conditions: 57 | // - If `options & WNOHANG`, returns 0 for children that aren't dead. 58 | // - If `options & WUNTRACED`, also match children that have stopped but not been traced. 59 | // - If a child is traced and stopped, it is always matched. 60 | // - If `options & WCONTINUED`, also match children that were resumed by `SIGCONT`. 61 | // 62 | // Stores the child's status in `wstatus` and returns the PID, or -1 on error. 63 | int waitpid(int pid, int *wstatus, int options); 64 | 65 | // Wait for any child to die, store the status in `wstatus` and return the PID. 66 | // Alias for `waitpid(-1, wstatus, 0)`. 67 | static inline int wait(int *wstatus) { 68 | return waitpid(-1, wstatus, 0); 69 | } 70 | #endif 71 | -------------------------------------------------------------------------------- /common/include/syscall.h: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #pragma once 5 | 6 | #ifdef __ASSEMBLER__ 7 | 8 | // Assembly enum. 9 | #define SYSCALL_DEF(num, enum, name, returns, ...) .equ enum, num 10 | #include "syscall_defs.inc" 11 | 12 | #else 13 | 14 | #include "hal/gpio.h" 15 | 16 | #include 17 | #include 18 | #include 19 | 20 | #ifdef BADGEROS_KERNEL 21 | #include "filesystem.h" 22 | #include "scheduler/scheduler.h" 23 | 24 | #define SYSCALL_FLAG_EXISTS 0x01 25 | #define SYSCALL_FLAG_FAST 0x02 26 | 27 | typedef struct { 28 | void (*funcptr)(); 29 | uint8_t retwidth; 30 | uint8_t flags; 31 | } syscall_info_t; 32 | 33 | syscall_info_t syscall_info(int no); 34 | 35 | #else 36 | typedef int tid_t; 37 | typedef struct stat stat_t; 38 | 39 | // Process is running or waiting for syscalls. 40 | #define PROC_RUNNING 0x00000001 41 | // Process is waiting for threads to exit. 42 | #define PROC_EXITING 0x00000002 43 | // Process has fully exited. 44 | #define PROC_EXITED 0x00000004 45 | 46 | // Open for read-only. 47 | #define OFLAGS_READONLY 0x00000001 48 | // Open for write-only. 49 | #define OFLAGS_WRITEONLY 0x00000002 50 | // Open for read and write. 51 | #define OFLAGS_READWRITE 0x00000003 52 | // Seek to the end on opening. 53 | #define OFLAGS_APPEND 0x00000004 54 | // Truncate on opening. 55 | #define OFLAGS_TRUNCATE 0x00000008 56 | // Create if it doesn't exist on opening. 57 | #define OFLAGS_CREATE 0x00000010 58 | // Error if it exists on opening. 59 | #define OFLAGS_EXCLUSIVE 0x00000020 60 | // Do not inherit to child process. 61 | #define OFLAGS_CLOEXEC 0x00000040 62 | // Open a directory instead of a file. 63 | #define OFLAGS_DIRECTORY 0x00000080 64 | // Do not block on sockets, FIFOs, TTYs, etc. 65 | #define OFLAGS_NONBLOCK 0x00000100 66 | 67 | #define MEMFLAGS_R 0x00000001 68 | #define MEMFLAGS_W 0x00000002 69 | #define MEMFLAGS_RW 0x00000003 70 | #define MEMFLAGS_X 0x00000004 71 | #define MEMFLAGS_RX 0x00000005 72 | #define MEMFLAGS_WX 0x00000006 73 | #define MEMFLAGS_RWX 0x00000007 74 | #endif 75 | 76 | #include 77 | #include 78 | #include 79 | 80 | // C enum. 81 | typedef enum { 82 | #define SYSCALL_DEF(num, enum, name, returns, ...) enum = num, 83 | #include "syscall_defs.inc" 84 | } syscall_t; 85 | 86 | // C declarations. 87 | #define SYSCALL_DEF(num, enum, name, returns, ...) returns name(__VA_ARGS__); 88 | #include "syscall_defs.inc" 89 | 90 | #endif 91 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # BadgerOS documentation 2 | 3 | 7 | 8 | ## For contributors 9 | For information about the structure of the project, including building, tools and folder structure, see [Project structure](./project_structure.md). 10 | 11 | ## For BadgerOS kernel developers 12 | For information about the internal structure and API of the BadgerOS kernel, see [Kernel documentation](./kernel/README.md). 13 | 14 | 20 | For project plan and requirements, see [Project requirements](./project_requirements.md). 21 | -------------------------------------------------------------------------------- /docs/kernel/hal.md: -------------------------------------------------------------------------------- 1 | # Hardware abstraction layer 2 | The hardware abstraction layer, or HAL for short, provides a generic interface to various hardware functions, including GPIO, I²C, SPI, FLASH, etc. 3 | Other components can then use the HAL to perform the same operations on different hardware without the need for porting them. 4 | Take filesystems for example, where one type of filesystem can be stored on almost any kind of media without the need for explicit support from the filesystem driver. 5 | 6 | Currently responsible: Julian (RobotMan2412). 7 | 8 | ## Index 9 | - [Scope](#scope) 10 | - [Dependents](#dependents) 11 | - [Dependencies](#dependencies) 12 | - [Data types](#data-types) 13 | - [API](#api) 14 | 15 | 16 | # Scope 17 | The HAL needs to provide an abstract interface that can perform all actions that BagderOS needs it to. There will abstractions for: 18 | - GPIO 19 | - Digital I/O 20 | - PWM outputs 21 | - ADC 22 | - DAC 23 | - I²C 24 | - UART 25 | - SPI 26 | - I²S 27 | - FLASH 28 | 29 | ## GPIO scope 30 | The GPIOs need to be able to function as digital I/Os and all alternate functions provided by the hardware for which there is a driver (I²C, UART, SPI, etc.). The GPIO API also needs to be able to tell whether a pin is currently in use by one of such alternate functions. At least one GPIO input pin needs to support interrupts. 31 | 32 | ## I²C scope 33 | The I²C HAL needs to support master, slave, 10-bit addressing and interrupts. 34 | 35 | ## UART scope 36 | The UART HAL needs to support baudrate configuration and interrupts. 37 | 38 | ## SPI scope 39 | The SPI HAL needs to support DMA, one-wire SPI, two-wire SPI and interrupts. 40 | 41 | ## I²S scope 42 | TODO: Depends on sound architecture 43 | 44 | ## FLASH scope 45 | The FLASH HAL needs to support FLASH MMUs (if present), reading, erasing and writing. 46 | 47 | 48 | # Dependents 49 | ## [Filesystems](./filesystems.md) 50 | The block device drivers from the filesystems component depend on the HAL to abstract communications with physical storage media. 51 | 52 | 53 | # Dependencies 54 | The HAL does not depend on other components. 55 | 56 | 57 | # Data types 58 | TODO. 59 | 60 | 61 | # API 62 | TODO. 63 | -------------------------------------------------------------------------------- /docs/kernel/memory.md: -------------------------------------------------------------------------------- 1 | # Memory management 2 | All the memory available to the system is handled centrally by the memory manager. The memory manager is responsible for accounting the available memory and distributing said memory between processes, kernel-side caches, kernel stacks and kernel data structures. 3 | Memory management also includes the configuration of virtual memory or memory protection, whichever is applicable. 4 | 5 | Currently responsible: Hein-Pieter van Braam (HP). 6 | 7 | ## Index 8 | - [Scope](#scope) 9 | - [Dependents](#dependents) 10 | - [Dependencies](#dependencies) 11 | - [Data types](#data-types) 12 | - [API](#api) 13 | 14 | 15 | # Scope 16 | Memory management is in charge of allocating and protecting all the memory available to the system. Various systems in the kernel and applications can request memory for various purposes. The memory allocator decides how much memory at what addresses to assign to applications or the kernel. This information is then relayed to memory protection for the application or kernel respectively. 17 | 18 | 19 | # Dependents 20 | ## [Process management](./process.md) 21 | The process management component relays requests for memory from the processes to the memory management component. 22 | 23 | 24 | # Dependencies 25 | The memory allocator does not depend on other components. 26 | 27 | 28 | # Data types 29 | TODO. 30 | 31 | 32 | # API 33 | TODO. 34 | -------------------------------------------------------------------------------- /docs/kernel/process.md: -------------------------------------------------------------------------------- 1 | # Document title 2 | Description 3 | 4 | Currently responsible: Name (Nickname). 5 | 6 | ## Index 7 | - [Scope](#scope) 8 | - [Dependents](#dependents) 9 | - [Dependencies](#dependencies) 10 | - [Data types](#data-types) 11 | - [API](#api) 12 | 13 | 14 | # Scope 15 | Scope 16 | 17 | 18 | # Dependents 19 | 20 | 21 | 22 | # Dependencies 23 | 24 | 25 | 26 | # Data types 27 | TODO. 28 | 29 | 30 | # API 31 | TODO. 32 | -------------------------------------------------------------------------------- /docs/kernel/scheduler.md: -------------------------------------------------------------------------------- 1 | # Document title 2 | Description 3 | 4 | Currently responsible: Name (Nickname). 5 | 6 | ## Index 7 | - [Scope](#scope) 8 | - [Dependents](#dependents) 9 | - [Dependencies](#dependencies) 10 | - [Data types](#data-types) 11 | - [API](#api) 12 | 13 | 14 | # Scope 15 | Scope 16 | 17 | 18 | # Dependents 19 | 20 | 21 | 22 | # Dependencies 23 | 24 | 25 | 26 | # Data types 27 | TODO. 28 | 29 | 30 | # API 31 | TODO. 32 | -------------------------------------------------------------------------------- /files/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | # SPDX-License-Identifier: MIT 3 | 4 | cmake_minimum_required(VERSION 3.10.0) 5 | 6 | set(CMAKE_EXPORT_COMPILE_COMMANDS true) 7 | include(../.config/config.cmake) 8 | include(../common/compiler.cmake) 9 | 10 | set(badge_cflags 11 | -ffreestanding 12 | -fpie -pie 13 | -march=${target_arch} # Selects the target CPU. 14 | -mabi=${target_abi} # Selects target ABI 15 | -nodefaultlibs -nostdlib # Do not link any default libraries like libgcc or libc. 16 | -O2 # Optimize the code. 17 | -Werror=return-type # Error when a function doesn't return a value, but declares to do so. 18 | -Werror=implicit-fallthrough 19 | -Wall -Wextra # Ramp up warning level. 20 | -std=gnu11 # We use the C11 standard 21 | -DBADGEROS # Tell the code we're building for the userspace 22 | -fno-omit-frame-pointer # Always use frame pointer 23 | -fno-stack-protector 24 | -fno-exceptions 25 | -ggdb 26 | ) 27 | 28 | project(root C ASM) 29 | 30 | set(badge_include 31 | ${CMAKE_CURRENT_LIST_DIR}/../common/badgelib/include 32 | ${CMAKE_CURRENT_LIST_DIR}/../common/include 33 | ${CMAKE_CURRENT_LIST_DIR}/../.config 34 | ) 35 | set(badge_libs crt badgelib) 36 | macro(badgeros_executable exec installdir) 37 | add_executable(${exec}) 38 | target_compile_options(${exec} PRIVATE ${badge_cflags} -ffunction-sections) 39 | target_link_options(${exec} PRIVATE ${badge_cflags} -pie -Wl,--gc-sections -nostartfiles) 40 | target_include_directories(${exec} PRIVATE ${badge_include}) 41 | target_link_libraries(${exec} PRIVATE ${badge_libs}) 42 | install(TARGETS ${exec} RUNTIME DESTINATION ${installdir}) 43 | endmacro() 44 | 45 | add_subdirectory(../common/badgelib badgelib) 46 | add_subdirectory(lib) 47 | add_subdirectory(sbin) 48 | -------------------------------------------------------------------------------- /files/Makefile: -------------------------------------------------------------------------------- 1 | 2 | # SPDX-License-Identifier: MIT 3 | 4 | MAKEFLAGS += --silent 5 | SHELL := /usr/bin/env bash 6 | BUILDDIR ?= $(shell pwd)/build 7 | OUTPUT ?= $(shell pwd)/root 8 | 9 | .PHONY: all clean build 10 | 11 | all: build 12 | 13 | build: 14 | mkdir -p '$(BUILDDIR)' 15 | mkdir -p '$(OUTPUT)' 16 | cmake -B '$(BUILDDIR)' 17 | cmake --build '$(BUILDDIR)' 18 | cmake --install '$(BUILDDIR)' --prefix '$(OUTPUT)' 19 | 20 | clean: 21 | rm -rf '$(BUILDDIR)' '$(OUTPUT)' 22 | -------------------------------------------------------------------------------- /files/lib/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | # SPDX-License-Identifier: MIT 3 | 4 | cmake_minimum_required(VERSION 3.10.0) 5 | 6 | add_subdirectory(crt) 7 | add_subdirectory(syscall) 8 | add_subdirectory(badge) 9 | -------------------------------------------------------------------------------- /files/lib/badge/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | # SPDX-License-Identifier: MIT 3 | 4 | cmake_minimum_required(VERSION 3.10.0) 5 | -------------------------------------------------------------------------------- /files/lib/crt/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | # SPDX-License-Identifier: MIT 3 | 4 | cmake_minimum_required(VERSION 3.10.0) 5 | 6 | if(${CONFIG_CPU} STREQUAL "amd64") 7 | add_library(crt start_amd64.S) 8 | else() 9 | add_library(crt start_riscv.S) 10 | endif() 11 | target_compile_options(crt PRIVATE ${badge_cflags} -ffunction-sections) 12 | target_link_options(crt PRIVATE ${badge_cflags} -Wl,--gc-sections -nostartfiles) 13 | target_include_directories(crt PRIVATE ${badge_include}) 14 | target_link_libraries(crt PUBLIC syscall) 15 | -------------------------------------------------------------------------------- /files/lib/crt/start_amd64.S: -------------------------------------------------------------------------------- 1 | 2 | # SPDX-License-Identifier: MIT 3 | 4 | #include "syscall.h" 5 | 6 | .global main 7 | .intel_syntax 8 | 9 | 10 | 11 | .global _start 12 | .text 13 | .p2align 1 14 | _start: 15 | lea %rsp, stack_top[%rip] 16 | xor %ebp, %ebp 17 | 18 | # Set signal handler return trampoline. 19 | xor %edi, %edi 20 | lea %rsi, sigret_trampoline[%rip] 21 | call syscall_proc_sighandler 22 | 23 | # TODO: argc and argv. 24 | 25 | # Call main function. 26 | call main 27 | 28 | # Run exit syscall if main returns. 29 | mov %rdi, %rax 30 | jmp syscall_proc_exit 31 | 32 | 33 | 34 | sigret_trampoline: 35 | jmp syscall_proc_sigret 36 | 37 | 38 | 39 | # Main stack. 40 | .section ".bss" 41 | stack_bottom: 42 | .zero 1024 43 | stack_top: 44 | -------------------------------------------------------------------------------- /files/lib/crt/start_riscv.S: -------------------------------------------------------------------------------- 1 | 2 | # SPDX-License-Identifier: MIT 3 | 4 | #include "syscall.h" 5 | 6 | .global __global_pointer$ 7 | .global main 8 | 9 | 10 | 11 | .global _start 12 | .text 13 | .p2align 1 14 | _start: 15 | # Set stack and global pointer. 16 | .option push 17 | .option norelax 18 | la sp, stack_top 19 | la gp, __global_pointer$ 20 | .option pop 21 | 22 | # Set signal handler return trampoline. 23 | li a0, 0 24 | la a1, sigret_trampoline 25 | jal syscall_proc_sighandler 26 | 27 | # TODO: argc and argv. 28 | 29 | # Call main function. 30 | jal main 31 | 32 | # Run exit syscall if main returns. 33 | li a7, SYSCALL_PROC_EXIT 34 | ecall 35 | 36 | 37 | 38 | sigret_trampoline: 39 | j syscall_proc_sigret 40 | 41 | 42 | 43 | # Main stack. 44 | .section ".bss" 45 | stack_bottom: 46 | .zero 1024 47 | stack_top: 48 | -------------------------------------------------------------------------------- /files/lib/syscall/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | # SPDX-License-Identifier: MIT 3 | 4 | cmake_minimum_required(VERSION 3.10.0) 5 | 6 | add_library(syscall 7 | src/gpio.c 8 | src/i2c.c 9 | src/spi.c 10 | src/syscall.c 11 | ) 12 | target_compile_options(syscall PRIVATE ${badge_cflags} -ffunction-sections) 13 | target_link_options(syscall PRIVATE ${badge_cflags} -Wl,--gc-sections -nostartfiles) 14 | target_include_directories(syscall PRIVATE ${badge_include}) 15 | -------------------------------------------------------------------------------- /files/lib/syscall/src/gpio.c: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #include "hal/gpio.h" 5 | 6 | #include "syscall.h" 7 | 8 | // Returns the amount of GPIO pins present. 9 | // Cannot produce an error. 10 | int io_count() { 11 | return syscall_io_count(); 12 | } 13 | 14 | // Sets the mode of GPIO pin `pin` to `mode`. 15 | void io_mode(badge_err_t *ec, int pin, io_mode_t mode) { 16 | syscall_io_mode(ec, pin, mode); 17 | } 18 | 19 | // Get the mode of GPIO pin `pin`. 20 | io_mode_t io_getmode(badge_err_t *ec, int pin) { 21 | return syscall_io_getmode(ec, pin); 22 | } 23 | 24 | // Sets the pull resistor behaviour of GPIO pin `pin` to `dir`. 25 | void io_pull(badge_err_t *ec, int pin, io_pull_t dir) { 26 | syscall_io_pull(ec, pin, dir); 27 | } 28 | 29 | // Get the pull resistor behaviour of GPIO pin `pin`. 30 | io_pull_t io_getpull(badge_err_t *ec, int pin) { 31 | return syscall_io_getpull(ec, pin); 32 | } 33 | 34 | // Writes level to GPIO pin pin. 35 | void io_write(badge_err_t *ec, int pin, bool level) { 36 | syscall_io_write(ec, pin, level); 37 | } 38 | 39 | // Reads logic level value from GPIO pin `pin`. 40 | // Returns false on error. 41 | bool io_read(badge_err_t *ec, int pin) { 42 | return syscall_io_read(ec, pin); 43 | } 44 | 45 | // Determine whether GPIO `pin` is claimed by a peripheral. 46 | // Returns false on error. 47 | bool io_is_peripheral(badge_err_t *ec, int pin) { 48 | return syscall_io_is_peripheral(ec, pin); 49 | } 50 | -------------------------------------------------------------------------------- /files/lib/syscall/src/i2c.c: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #include "hal/i2c.h" 5 | 6 | #include "syscall.h" 7 | 8 | // Returns the amount of I²C peripherals present. 9 | // Cannot produce an error. 10 | int i2c_count() { 11 | return syscall_i2c_count(); 12 | } 13 | 14 | // Initialises I²C peripheral i2c_num in slave mode with SDA pin `sda_pin`, SCL pin `scl_pin` and clock speed/bitrate 15 | // bitrate. When initialised as an I²C master, the modes of the SDA and SCL pins are changed automatically. This 16 | // function may be used again to change the settings on an initialised I²C peripheral in master mode. 17 | void i2c_master_init(badge_err_t *ec, int i2c_num, int sda_pin, int scl_pin, int32_t bitrate) { 18 | return syscall_i2c_master_init(ec, i2c_num, sda_pin, scl_pin, bitrate); 19 | } 20 | 21 | // De-initialises I²C peripheral i2c_num in master mode. 22 | void i2c_master_deinit(badge_err_t *ec, int i2c_num) { 23 | return syscall_i2c_master_deinit(ec, i2c_num); 24 | } 25 | 26 | // Reads len bytes into buffer buf from I²C slave with ID slave_id. 27 | // This function blocks until the entire transaction is completed and returns the number of acknowledged read bytes. 28 | size_t i2c_master_read_from(badge_err_t *ec, int i2c_num, int slave_id, void *buf, size_t len) { 29 | return syscall_i2c_master_read_from(ec, i2c_num, slave_id, buf, len); 30 | } 31 | 32 | // Writes len bytes from buffer buf to I²C slave with ID slave_id. 33 | // This function blocks until the entire transaction is completed and returns the number of acknowledged written bytes. 34 | size_t i2c_master_write_to(badge_err_t *ec, int i2c_num, int slave_id, void const *buf, size_t len) { 35 | return syscall_i2c_master_write_to(ec, i2c_num, slave_id, buf, len); 36 | } 37 | -------------------------------------------------------------------------------- /files/lib/syscall/src/spi.c: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #include "hal/spi.h" 5 | 6 | #include "syscall.h" 7 | 8 | // Returns the amount of SPI peripherals present. 9 | // Cannot produce an error. 10 | int spi_count() { 11 | return syscall_spi_count(); 12 | } 13 | 14 | // Initialises SPI peripheral spi_num in controller mode with SCLK pin `sclk_pin`, MOSI pin `mosi_pin`, MISO pin 15 | // `miso_pin`, SS pin `ss_pin` and clock speed/bitrate bitrate. The modes of the SCLK, MOSI, MISO and SS pins are 16 | // changed automatically. This function may be used again to change the settings on an initialised SPI peripheral in 17 | // controller mode. 18 | void spi_controller_init( 19 | badge_err_t *ec, int spi_num, int sclk_pin, int mosi_pin, int miso_pin, int ss_pin, int32_t bitrate 20 | ) { 21 | syscall_spi_controller_init(ec, spi_num, sclk_pin, mosi_pin, miso_pin, ss_pin, bitrate); 22 | } 23 | 24 | // De-initialises SPI peripheral. 25 | void spi_deinit(badge_err_t *ec, int spi_num) { 26 | syscall_spi_deinit(ec, spi_num); 27 | } 28 | 29 | // Reads len bytes into buffer buf from SPI device. 30 | // This function blocks until the entire transaction is completed. 31 | void spi_controller_read(badge_err_t *ec, int spi_num, void *buf, size_t len) { 32 | syscall_spi_controller_read(ec, spi_num, buf, len); 33 | } 34 | 35 | // Writes len bytes from buffer buf to SPI device. 36 | // This function blocks until the entire transaction is completed. 37 | void spi_controller_write(badge_err_t *ec, int spi_num, void const *buf, size_t len) { 38 | syscall_spi_controller_write(ec, spi_num, buf, len); 39 | } 40 | 41 | // Writes len bytes from buffer buf to SPI device, then reads len bytes into buffer buf from SPI device. 42 | // Write and read happen simultaneously if the full-duplex flag fdx is set. 43 | // This function blocks until the entire transaction is completed. 44 | void spi_controller_transfer(badge_err_t *ec, int spi_num, void *buf, size_t len, bool fdx) { 45 | syscall_spi_controller_transfer(ec, spi_num, buf, len, fdx); 46 | } 47 | -------------------------------------------------------------------------------- /files/lib/syscall/src/syscall.c: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #include "syscall.h" 5 | 6 | // These syscalls *do* actually take parameters, but GCC doesn't realise this. 7 | #pragma GCC diagnostic ignored "-Wunused-parameter" 8 | 9 | #ifdef __riscv 10 | #define SYSCALL_DEF(num, enum, name, returns, ...) \ 11 | returns name(__VA_ARGS__) __attribute__((naked)); \ 12 | returns name(__VA_ARGS__) { \ 13 | asm("li a7, " #num "; ecall; ret"); \ 14 | } 15 | #elif defined(__x86_64__) 16 | #define SYSCALL_DEF(num, enum, name, returns, ...) \ 17 | returns name(__VA_ARGS__) __attribute__((naked)); \ 18 | returns name(__VA_ARGS__) { \ 19 | asm("mov $" #num ", %rax; mov %rcx, %r10; syscall; ret"); \ 20 | } 21 | #endif 22 | 23 | #include "syscall_defs.inc" 24 | -------------------------------------------------------------------------------- /files/sbin/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | # SPDX-License-Identifier: MIT 3 | 4 | cmake_minimum_required(VERSION 3.10.0) 5 | 6 | add_subdirectory(init) 7 | -------------------------------------------------------------------------------- /files/sbin/init/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | # SPDX-License-Identifier: MIT 3 | 4 | cmake_minimum_required(VERSION 3.10.0) 5 | 6 | badgeros_executable(init sbin) 7 | target_sources(init PRIVATE 8 | main.c 9 | ) 10 | -------------------------------------------------------------------------------- /files/sbin/init/main.c: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #include "signal.h" 5 | #include "syscall.h" 6 | 7 | 8 | 9 | size_t strlen(char const *cstr) { 10 | char const *pre = cstr; 11 | while (*cstr) cstr++; 12 | return cstr - pre; 13 | } 14 | 15 | void print(char const *cstr) { 16 | syscall_temp_write(cstr, strlen(cstr)); 17 | } 18 | 19 | void putd(uint64_t value, int decimals) { 20 | char tmp[20]; 21 | 22 | int i; 23 | for (i = 0; i < 20; i++) { 24 | tmp[19 - i] = '0' + value % 10; 25 | value /= 10; 26 | if (!value) { 27 | break; 28 | } 29 | } 30 | 31 | if (decimals < i) { 32 | decimals = i; 33 | } else if (decimals > 20) { 34 | decimals = 20; 35 | } 36 | 37 | syscall_temp_write(tmp + 20 - decimals, decimals); 38 | } 39 | 40 | char const hextab[] = "0123456789ABCDEF"; 41 | 42 | int main() { 43 | syscall_proc_sighandler(SIGHUP, SIG_IGN); 44 | 45 | print("Hi, Ther.\n"); 46 | 47 | // int ec = syscall_fs_mkfifo(-1, "/myfifo", 7); 48 | // print("mkfifo returned "); 49 | // putd(ec, 1); 50 | // print("\n"); 51 | 52 | // int fd = syscall_fs_open(-1, "/myfifo", 7, OFLAGS_READWRITE | OFLAGS_NONBLOCK); 53 | // print("open returned "); 54 | // putd(fd, 1); 55 | // print("\n"); 56 | 57 | long pipes[2]; 58 | int ec = syscall_fs_pipe(pipes, 0); 59 | print("pipe returned "); 60 | putd(ec, 1); 61 | print("\n"); 62 | 63 | ec = syscall_fs_write(pipes[1], "mydata", 6); 64 | print("write returned "); 65 | putd(ec, 1); 66 | print("\n"); 67 | 68 | char mybuf[6]; 69 | int count = syscall_fs_read(pipes[0], mybuf, 6); 70 | print("read returned "); 71 | putd(count, 1); 72 | print("\n"); 73 | 74 | print("Read data:\n"); 75 | syscall_temp_write(mybuf, count); 76 | print("\n"); 77 | 78 | syscall_sys_shutdown(false); 79 | 80 | return 0; 81 | } 82 | -------------------------------------------------------------------------------- /kernel/Makefile: -------------------------------------------------------------------------------- 1 | 2 | # SPDX-License-Identifier: MIT 3 | 4 | CONFIG_PATH ?= ../.config/config.mk 5 | include $(CONFIG_PATH) 6 | 7 | MAKEFLAGS += --silent 8 | SHELL := /usr/bin/env bash 9 | OUTPUT ?= $(shell pwd)/firmware 10 | BUILDDIR ?= build 11 | BADGER_RAMFS_ROOT ?= ../files/root 12 | CROSS_COMPILE = $(CONFIG_TC_PREFIX) 13 | GDB ?= $(CROSS_COMPILE)gdb 14 | CLANG_FORMAT ?= clang-format-18 15 | CLANG_TIDY ?= clang-tidy-18 16 | 17 | include port/$(CONFIG_TARGET)/port.mk 18 | 19 | .PHONY: cmake-configure 20 | cmake-configure: $(BUILDDIR)/fs_root.c 21 | mkdir -p '$(BUILDDIR)' 22 | cmake -B '$(BUILDDIR)' "-DCMAKE_BUILD_TYPE=$${CMAKE_BUILD_TYPE:-Debug}" 23 | 24 | $(BUILDDIR)/fs_root.c: $(shell find '$(BADGER_RAMFS_ROOT)' 2>/dev/null) 25 | mkdir -p '$(BUILDDIR)' 26 | ../tools/ramfs-gen.py '$(BADGER_RAMFS_ROOT)' '$(BUILDDIR)/fs_root.c' init_ramfs 27 | 28 | .PHONY: _on_config 29 | _on_config: _port_on_config 30 | git submodule update --init lib/kbelf 31 | 32 | .PHONY: build 33 | build: cmake-configure 34 | cmake --build '$(BUILDDIR)' 35 | cmake --install '$(BUILDDIR)' --prefix '$(OUTPUT)' 36 | 37 | .PHONY: clean 38 | clean: 39 | rm -rf "$(BUILDDIR)" 40 | 41 | .PHONY: clang-format-check 42 | clang-format-check: cmake-configure 43 | echo "clang-format check the following files:" 44 | jq -r '.[].file' build/compile_commands.json | grep '\.[ch]$$' 45 | echo "analysis results:" 46 | $(CLANG_FORMAT) --dry-run $(shell jq -r '.[].file' build/compile_commands.json | grep '\.[ch]$$') 47 | 48 | .PHONY: clang-tidy-check 49 | clang-tidy-check: cmake-configure 50 | echo "clang-tidy check the following files:" 51 | jq -r '.[].file' build/compile_commands.json | grep '\.[ch]$$' 52 | echo "analysis results:" 53 | $(CLANG_TIDY) -p build $(shell jq -r '.[].file' build/compile_commands.json | grep '\.[ch]$$') \ 54 | --checks=-*widening*multiplication*,-*insecureAPI* \ 55 | --warnings-as-errors="*" 56 | 57 | .PHONY: gdb 58 | gdb: 59 | $(GDB) -x port/$(CONFIG_TARGET)/gdbinit build/badger-os.elf 60 | -------------------------------------------------------------------------------- /kernel/cpu/amd64/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | # SPDX-License-Identifier: MIT 3 | 4 | cmake_minimum_required(VERSION 3.10.0) 5 | 6 | set(cpu_src 7 | ${CMAKE_CURRENT_LIST_DIR}/src/entrypoint.S 8 | ${CMAKE_CURRENT_LIST_DIR}/src/isr.S 9 | ${CMAKE_CURRENT_LIST_DIR}/src/isr_ctx.S 10 | ${CMAKE_CURRENT_LIST_DIR}/src/syscall.S 11 | 12 | ${CMAKE_CURRENT_LIST_DIR}/src/backtrace.c 13 | ${CMAKE_CURRENT_LIST_DIR}/src/interrupt.c 14 | ${CMAKE_CURRENT_LIST_DIR}/src/isr.c 15 | ${CMAKE_CURRENT_LIST_DIR}/src/isr_ctx.c 16 | ${CMAKE_CURRENT_LIST_DIR}/src/panic.c 17 | ${CMAKE_CURRENT_LIST_DIR}/src/scheduler.c 18 | ${CMAKE_CURRENT_LIST_DIR}/src/syscall.c 19 | ${CMAKE_CURRENT_LIST_DIR}/src/time.c 20 | ${CMAKE_CURRENT_LIST_DIR}/src/usercopy.c 21 | ${CMAKE_CURRENT_LIST_DIR}/src/mmu.c 22 | ) 23 | set(cpu_include ${CMAKE_CURRENT_LIST_DIR}/include) 24 | set(cpu_link) 25 | set(cpu_flags -masm=intel -mgeneral-regs-only -mno-red-zone) 26 | set(kbelf_target x86) 27 | -------------------------------------------------------------------------------- /kernel/cpu/amd64/include/cpu/arch_cpulocal.h: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #pragma once 5 | 6 | // Arch-specific CPU-local data. 7 | typedef struct { 8 | // Pointer to this CPU's TSS struct. 9 | void *tss; 10 | } arch_cpulocal_t; 11 | -------------------------------------------------------------------------------- /kernel/cpu/amd64/include/cpu/hardware.h: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #pragma once 5 | 6 | #ifndef __ASSEMBLER__ 7 | #include 8 | #include 9 | #endif 10 | 11 | 12 | 13 | /* ==== CPU INFO ==== */ 14 | 15 | // Page size for memory protections. 16 | #define MEMMAP_PAGE_SIZE 4096 17 | -------------------------------------------------------------------------------- /kernel/cpu/amd64/include/cpu/isr.h: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #pragma once 5 | 6 | #include "cpu/regs.h" 7 | #include "isr_ctx.h" 8 | 9 | #ifndef __ASSEMBLER__ 10 | #include 11 | #include 12 | #include 13 | #endif 14 | 15 | #ifdef __ASSEMBLER__ 16 | // clang-format off 17 | 18 | // Called from ASM on interrupt. 19 | .global amd64_interrupt_handler 20 | // Called from ASM on trap. 21 | .global amd64_trap_handler 22 | 23 | // clang-format on 24 | #else 25 | // Callback from ASM to platform-specific interrupt handler. 26 | extern void amd64_interrupt_handler(); 27 | // ASM system call wrapper function. 28 | extern void amd64_syscall_wrapper(); 29 | // Callback from ASM on non-syscall trap. 30 | extern void amd64_trap_handler(size_t trapno, size_t error_code); 31 | 32 | // Explicit context switch from kernel. 33 | // Interrupts must be disabled on entry and will be re-enabled on exit. 34 | // If the context switch target is not set, this is a NOP. 35 | extern void isr_context_switch(); 36 | // Pause the CPU briefly. 37 | static inline void isr_pause() { 38 | // TODO. 39 | } 40 | 41 | #endif 42 | -------------------------------------------------------------------------------- /kernel/cpu/amd64/include/cpu/priv_level.h: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #pragma once 5 | 6 | // Privilege level bitmask. 7 | #define PRIV_LEVEL_MASK 3llu 8 | // Privilege level in which the kernel runs. 9 | #define PRIV_KERNEL 0llu 10 | // Privilege level in which user code runs. 11 | #define PRIV_USER 3llu 12 | -------------------------------------------------------------------------------- /kernel/cpu/amd64/include/cpu/segmentation.h: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #pragma once 5 | 6 | #ifndef __ASSEMBLER__ 7 | #include "attributes.h" 8 | 9 | #include 10 | 11 | 12 | 13 | // GDT flag: long mode. 14 | #define GDT_FLAG_L (1llu << 53) 15 | // GDT access: accessed bit, set to 1 when segment has been accessed. 16 | #define GDT_ACCESS_A (1llu << 40) 17 | // GDT access: read/write, read-enable for code, write-enable for data. 18 | #define GDT_ACCESS_RW (1llu << 41) 19 | /* DC access bit omitted - BadgerOS will never want to set it to 1. */ 20 | // GDT access: executable. 21 | #define GDT_ACCESS_E (1llu << 43) 22 | // GDT access: is NOT a system descriptor. 23 | #define GDT_ACCESS_S (1llu << 44) 24 | // GDT access: privilege level bitmask. 25 | #define GDT_ACCESS_DPL_MASK (3llu << 45) 26 | // GDT access: privilege level base bit. 27 | #define GDT_ACCESS_DPL_BASE 45 28 | // GDT access: present. 29 | #define GDT_ACCESS_P (1llu << 47) 30 | // Format base address for GDT. 31 | #define GDT_BASE(value) ((((value) << 16) & 0x000000ffffff0000) | (((value) << 32) & 0xff00000000000000)) 32 | // Format limit for GDT. 33 | #define GDT_LIMIT(value) ((value) & 0xffff) 34 | #endif 35 | 36 | // Format a segment value without address. 37 | #define FORMAT_SEGMENT(segno, use_local, privilege) (((segno) << 3) | ((use_local) << 2) | (privilege)) 38 | 39 | // Segment number to use for kernel code. 40 | #define SEGNO_KCODE 1 41 | // Segment number to use for kernel data. 42 | #define SEGNO_KDATA 2 43 | // Segment number to use for user code. 44 | #define SEGNO_UCODE 3 45 | // Segment number to use for user data. 46 | #define SEGNO_UDATA 4 47 | 48 | // Byte size of the TSS. 49 | #define TSS_SIZE 0x68 50 | // Offset of ring 0 stack in the TSS. 51 | #define TSS_STACK 0x04 52 | -------------------------------------------------------------------------------- /kernel/cpu/amd64/include/cpu/x86_cpuid.h: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #pragma once 5 | 6 | #include 7 | 8 | 9 | 10 | // CPUID info. 11 | typedef struct { 12 | uint32_t eax; 13 | uint32_t ebx; 14 | uint32_t ecx; 15 | uint32_t edx; 16 | } cpuid_t; 17 | 18 | // Get CPU information. 19 | static inline cpuid_t cpuid(uint32_t index) __attribute__((const)); 20 | static inline cpuid_t cpuid(uint32_t index) { 21 | register uint32_t eax asm("eax") = 0; 22 | register uint32_t ebx asm("ebx") = 0; 23 | register uint32_t ecx asm("ecx") = 0; 24 | register uint32_t edx asm("edx") = 0; 25 | asm("cpuid" : "+r"(eax), "=r"(ebx), "=r"(ecx), "=r"(edx)); 26 | return (cpuid_t){eax, ebx, ecx, edx}; 27 | } 28 | -------------------------------------------------------------------------------- /kernel/cpu/amd64/include/cpu/x86_ioport.h: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #include 5 | 6 | 7 | 8 | // Output a byte to an I/O port 9 | __attribute__((always_inline)) static inline void outb(uint16_t port, uint8_t value) { 10 | asm volatile("out dx, al" : : "d"(port), "a"(value)); 11 | } 12 | 13 | // Input a byte from an I/O port 14 | __attribute__((always_inline)) static inline uint8_t inb(uint16_t port) { 15 | uint8_t value; 16 | asm volatile("in al, dx" : "=a"(value) : "d"(port)); 17 | return value; 18 | } 19 | 20 | 21 | 22 | // Output a word to an I/O port 23 | __attribute__((always_inline)) static inline void outw(uint16_t port, uint16_t value) { 24 | asm volatile("out dx, ax" : : "d"(port), "a"(value)); 25 | } 26 | 27 | // Input a word from an I/O port 28 | __attribute__((always_inline)) static inline uint16_t inw(uint16_t port) { 29 | uint8_t value; 30 | asm volatile("in ax, dx" : "=a"(value) : "d"(port)); 31 | return value; 32 | } 33 | 34 | 35 | 36 | // Output a dword to an I/O port 37 | __attribute__((always_inline)) static inline void outd(uint16_t port, uint32_t value) { 38 | asm volatile("out dx, eax" : : "d"(port), "a"(value)); 39 | } 40 | 41 | // Input a dword from an I/O port 42 | __attribute__((always_inline)) static inline uint32_t ind(uint16_t port) { 43 | uint8_t value; 44 | asm volatile("in eax, dx" : "=a"(value) : "d"(port)); 45 | return value; 46 | } 47 | -------------------------------------------------------------------------------- /kernel/cpu/amd64/include/cpu/x86_msr.h: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #pragma once 5 | 6 | #ifndef __ASSEMBLER__ 7 | #include "attributes.h" 8 | 9 | #include 10 | #endif 11 | 12 | 13 | 14 | #ifndef __ASSEMBLER__ 15 | // Format of EFER MSR. 16 | typedef union { 17 | struct { 18 | // System call extensions. 19 | uint64_t sce : 1; 20 | // Reserved. 21 | uint64_t : 7; 22 | // Long mode enable. 23 | uint64_t lme : 1; 24 | // Long mode active. 25 | uint64_t lma : 1; 26 | // No-execute enable. 27 | uint64_t nxe : 1; 28 | // Secure virtual machine enable. 29 | uint64_t svme : 1; 30 | // Fast FXSAVE/FXSTOR. 31 | uint64_t ffxsr : 1; 32 | // Translation cache extension. 33 | uint64_t tce : 1; 34 | }; 35 | uint64_t val; 36 | } msr_efer_t; 37 | #endif 38 | 39 | 40 | 41 | // Address of FSBASE MSR; base address of `fs` segment. 42 | #define MSR_FSBASE 0xc0000100 43 | // Address of GSBASE MSR; base address of `gs` segment. 44 | // Swapped with KGSBASE using the `swapgs` instruction. 45 | #define MSR_GSBASE 0xc0000101 46 | // Address of KGSBASE MSR; temporary value for kernel `gs` segment. 47 | // Swapped with GSBASE using the `swapgs` instruction. 48 | #define MSR_KGSBASE 0xc0000102 49 | // Address of EFER MSR; extended feature enable register. 50 | #define MSR_EFER 0xc0000080 51 | // Address of the STAR MSR; CS/SS for user/kernel. 52 | #define MSR_STAR 0xc0000081 53 | // Adress of the LSTAR MSR; entry point for system calls. 54 | #define MSR_LSTAR 0xc0000082 55 | // Address of the FMASK MSR; flags to clear when entering kernel. 56 | #define MSR_FMASK 0xc0000084 57 | 58 | 59 | 60 | #ifndef __ASSEMBLER__ 61 | // Read an MSR. 62 | FORCEINLINE static inline uint64_t msr_read(uint32_t address) { 63 | uint32_t addr = address; 64 | uint32_t lo; 65 | uint32_t hi; 66 | asm("rdmsr" : "=a"(lo), "=d"(hi) : "c"(addr) : "memory"); 67 | return ((uint64_t)hi << 32) | lo; 68 | } 69 | 70 | // Write an MSR. 71 | FORCEINLINE static inline void msr_write(uint32_t address, uint64_t value) { 72 | uint32_t addr = address; 73 | uint32_t lo = value; 74 | uint32_t hi = value >> 32; 75 | asm volatile("wrmsr" ::"a"(lo), "d"(hi), "c"(addr) : "memory"); 76 | } 77 | #endif 78 | -------------------------------------------------------------------------------- /kernel/cpu/amd64/src/backtrace.c: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #include "backtrace.h" 5 | 6 | #include "attributes.h" 7 | #include "isr_ctx.h" 8 | #include "rawprint.h" 9 | 10 | #include 11 | 12 | #ifndef BACKTRACE_DEPTH 13 | #define BACKTRACE_DEPTH 32 14 | #endif 15 | 16 | 17 | 18 | // Given stack frame pointer, perform backtrace. 19 | void backtrace_from_ptr(void *frame_pointer) { 20 | rawprint("**** BEGIN BACKRTACE ****\n"); 21 | // Prev RBP offset: 0 words 22 | // Prev RIP offset: 1 word 23 | atomic_thread_fence(memory_order_acquire); 24 | size_t *fp = frame_pointer; 25 | for (int i = 0; i < BACKTRACE_DEPTH; i++) { 26 | size_t ra; 27 | if ((size_t)fp < 0x1000 || isr_noexc_copy_size(&ra, fp + 1)) { 28 | break; 29 | } 30 | rawprint("0x"); 31 | rawprinthex(ra, sizeof(size_t) * 2); 32 | rawputc('\n'); 33 | if ((size_t)fp < 0x1000 || isr_noexc_copy_size((size_t *)&fp, fp)) { 34 | break; 35 | } 36 | } 37 | rawprint("**** END BACKRTACE ****\n"); 38 | } 39 | 40 | // Perform backtrace as called. 41 | void backtrace() NAKED; 42 | void backtrace() { 43 | // clang-format off 44 | asm volatile( 45 | "mov %rdi, %rbp;" 46 | "jmp backtrace_from_ptr;" 47 | ); 48 | // clang-format on 49 | } 50 | -------------------------------------------------------------------------------- /kernel/cpu/amd64/src/entrypoint.S: -------------------------------------------------------------------------------- 1 | 2 | # SPDX-License-Identifier: MIT 3 | 4 | #include 5 | 6 | # This is the C entrypoint, which will be called when basic CRT is set up. 7 | .global basic_runtime_init 8 | 9 | 10 | 11 | # Entrypoint from the bootloader. 12 | .text 13 | .global _start 14 | .type _start, %function 15 | .cfi_startproc 16 | _start: 17 | .intel_syntax 18 | .cfi_undefined %rsp 19 | .cfi_undefined %rbp 20 | xor %rbp, %rbp 21 | jmp basic_runtime_init 22 | .cfi_endproc 23 | -------------------------------------------------------------------------------- /kernel/cpu/amd64/src/isr_ctx.S: -------------------------------------------------------------------------------- 1 | 2 | # SPDX-License-Identifier: MIT 3 | 4 | #include "cpu/regs.h" 5 | .global kernel_reg_dump_arr 6 | .intel_syntax 7 | 8 | 9 | 10 | # Print a register dump of the current registers. 11 | .global kernel_cur_regs_dump 12 | .type kernel_cur_regs_dump, %function 13 | kernel_cur_regs_dump: 14 | ret 15 | 16 | 17 | 18 | # Calls the actual trapping code in `isr_noexc_run`. 19 | .global _isr_noexc_run_int 20 | .type _isr_noexc_run_int, %function 21 | _isr_noexc_run_int: 22 | // Save all callee-save regs. 23 | mov [%rsi+cpu_regs_t_rbx], %rbx 24 | mov [%rsi+cpu_regs_t_rsp], %rsp 25 | mov [%rsi+cpu_regs_t_rbp], %rbp 26 | mov [%rsi+cpu_regs_t_r12], %r12 27 | mov [%rsi+cpu_regs_t_r13], %r13 28 | mov [%rsi+cpu_regs_t_r14], %r14 29 | mov [%rsi+cpu_regs_t_r15], %r15 30 | mov %rax, [%rsp] 31 | mov [%rsi+cpu_regs_t_rip], %rax 32 | // Jump to implementation. 33 | jmp %rdx 34 | -------------------------------------------------------------------------------- /kernel/cpu/amd64/src/panic.c: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #include "panic.h" 5 | 6 | void cpu_panic_poweroff() { 7 | asm volatile("cli"); 8 | while (1) asm volatile("hlt"); 9 | } 10 | -------------------------------------------------------------------------------- /kernel/cpu/amd64/src/syscall.c: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #include "cpu/x86_msr.h" 5 | #include "isr_ctx.h" 6 | #include "scheduler/types.h" 7 | 8 | 9 | 10 | // Helper function that swaps from user to kernel ISR context on syscall. 11 | void amd64_syscall_raise() { 12 | isr_ctx_t *cur = isr_ctx_get(); 13 | isr_ctx_swap(&cur->thread->kernel_isr_ctx); 14 | } 15 | 16 | // Helper function that swaps from kernel ISR context to user ISR context on syscall return. 17 | void amd64_syscall_lower() { 18 | isr_ctx_t *cur = isr_ctx_get(); 19 | isr_ctx_swap(&cur->thread->user_isr_ctx); 20 | } 21 | -------------------------------------------------------------------------------- /kernel/cpu/amd64/src/usercopy.c: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #include "usercopy.h" 5 | 6 | #include "assertions.h" 7 | #include "badge_strings.h" 8 | #include "cpu/mmu.h" 9 | #include "interrupt.h" 10 | #include "isr_ctx.h" 11 | #include "memprotect.h" 12 | #include "process/internal.h" 13 | 14 | // TODO: Convert to page fault intercepting memcpy. 15 | // TODO: Migrate into generic process API. 16 | 17 | 18 | 19 | // Determine string length in memory a user owns. 20 | // Returns -1 if the user doesn't have access to any byte in the string. 21 | ptrdiff_t strlen_from_user_raw(process_t *process, size_t user_vaddr, ptrdiff_t max_len) { 22 | if (!max_len) { 23 | return 0; 24 | } 25 | ptrdiff_t len = 0; 26 | 27 | // Check first page permissions. 28 | if (!(proc_map_contains_raw(process, user_vaddr, 1) & MEMPROTECT_FLAG_R)) { 29 | return -1; 30 | } 31 | 32 | // String length loop. 33 | mpu_ctx_t *old_mpu = isr_ctx_get()->mpu_ctx; 34 | memprotect_swap(&process->memmap.mpu_ctx); 35 | mmu_enable_sum(); 36 | while (len < max_len && *(char const *)user_vaddr) { 37 | len++; 38 | user_vaddr++; 39 | if (user_vaddr % MEMMAP_PAGE_SIZE == 0) { 40 | // Check further page permissions. 41 | mmu_disable_sum(); 42 | if (!(proc_map_contains_raw(process, user_vaddr, 1) & MEMPROTECT_FLAG_R)) { 43 | memprotect_swap(old_mpu); 44 | return -1; 45 | } 46 | mmu_enable_sum(); 47 | } 48 | } 49 | mmu_disable_sum(); 50 | 51 | memprotect_swap(old_mpu); 52 | return len; 53 | } 54 | 55 | // Copy bytes from user to kernel. 56 | // Returns whether the user has access to all of these bytes. 57 | // If the user doesn't have access, no copy is performed. 58 | bool copy_from_user_raw(process_t *process, void *kernel_vaddr, size_t user_vaddr, size_t len) { 59 | if (!proc_map_contains_raw(process, user_vaddr, len)) { 60 | return false; 61 | } 62 | mpu_ctx_t *old_mpu = isr_ctx_get()->mpu_ctx; 63 | memprotect_swap(&process->memmap.mpu_ctx); 64 | mmu_enable_sum(); 65 | mem_copy(kernel_vaddr, (void *)user_vaddr, len); 66 | mmu_disable_sum(); 67 | memprotect_swap(old_mpu); 68 | return true; 69 | } 70 | 71 | // Copy from kernel to user. 72 | // Returns whether the user has access to all of these bytes. 73 | // If the user doesn't have access, no copy is performed. 74 | bool copy_to_user_raw(process_t *process, size_t user_vaddr, void *kernel_vaddr0, size_t len) { 75 | if (!proc_map_contains_raw(process, user_vaddr, len)) { 76 | return false; 77 | } 78 | mpu_ctx_t *old_mpu = isr_ctx_get()->mpu_ctx; 79 | memprotect_swap(&process->memmap.mpu_ctx); 80 | mmu_enable_sum(); 81 | mem_copy((void *)user_vaddr, kernel_vaddr0, len); 82 | mmu_disable_sum(); 83 | memprotect_swap(old_mpu); 84 | return true; 85 | } 86 | -------------------------------------------------------------------------------- /kernel/cpu/riscv/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | # SPDX-License-Identifier: MIT 3 | 4 | cmake_minimum_required(VERSION 3.10.0) 5 | 6 | set(cpu_src 7 | ${CMAKE_CURRENT_LIST_DIR}/src/entrypoint.S 8 | ${CMAKE_CURRENT_LIST_DIR}/src/isr.S 9 | ${CMAKE_CURRENT_LIST_DIR}/src/isr_ctx.S 10 | ${CMAKE_CURRENT_LIST_DIR}/src/syscall.S 11 | 12 | ${CMAKE_CURRENT_LIST_DIR}/src/backtrace.c 13 | ${CMAKE_CURRENT_LIST_DIR}/src/isr.c 14 | ${CMAKE_CURRENT_LIST_DIR}/src/isr_ctx.c 15 | ${CMAKE_CURRENT_LIST_DIR}/src/panic.c 16 | ${CMAKE_CURRENT_LIST_DIR}/src/scheduler.c 17 | ${CMAKE_CURRENT_LIST_DIR}/src/usercopy.c 18 | ) 19 | set(cpu_include ${CMAKE_CURRENT_LIST_DIR}/include) 20 | set(cpu_link) 21 | set(cpu_flags) 22 | set(kbelf_target riscv) 23 | 24 | if(DEFINED cpu_multicore) 25 | set(cpu_src ${cpu_src} ${CMAKE_CURRENT_LIST_DIR}/src/cpu1_entrypoint.S) 26 | endif() 27 | if(DEFINED cpu_riscv_enable_riscv_intc) 28 | set(cpu_src ${cpu_src} ${CMAKE_CURRENT_LIST_DIR}/src/interrupt/riscv_intc.c) 29 | set(cpu_src ${cpu_src} ${CMAKE_CURRENT_LIST_DIR}/src/interrupt.c) 30 | endif() 31 | if(DEFINED cpu_riscv_enable_riscv_plic) 32 | set(cpu_src ${cpu_src} ${CMAKE_CURRENT_LIST_DIR}/src/interrupt/riscv_plic.c) 33 | set(cpu_src ${cpu_src} ${CMAKE_CURRENT_LIST_DIR}/src/interrupt.c) 34 | endif() 35 | if(DEFINED cpu_riscv_enable_pmp) 36 | set(cpu_src ${cpu_src} ${CMAKE_CURRENT_LIST_DIR}/src/memprotect/riscv_pmp.c) 37 | endif() 38 | if(DEFINED cpu_riscv_enable_mmu) 39 | set(cpu_src ${cpu_src} ${CMAKE_CURRENT_LIST_DIR}/src/memprotect/mmu.c) 40 | endif() 41 | if(DEFINED cpu_riscv_enable_sbi_time) 42 | set(cpu_src ${cpu_src} ${CMAKE_CURRENT_LIST_DIR}/src/sbi_time.c) 43 | add_definitions(-DCPU_RISCV_ENABLE_SBI_TIME) 44 | endif() 45 | -------------------------------------------------------------------------------- /kernel/cpu/riscv/include/cpu/arch_cpulocal.h: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #pragma once 5 | 6 | // Arch-specific CPU-local data. 7 | typedef struct { 8 | } arch_cpulocal_t; 9 | -------------------------------------------------------------------------------- /kernel/cpu/riscv/include/cpu/hardware.h: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #pragma once 5 | 6 | #ifndef __ASSEMBLER__ 7 | #include 8 | #include 9 | #endif 10 | 11 | 12 | 13 | /* ==== CPU INFO ==== */ 14 | 15 | // Kernel runs in M-mode instead of S-mode. 16 | #define RISCV_M_MODE_KERNEL 0 17 | // Number of PMP regions supported by the CPU. 18 | #define RISCV_PMP_REGION_COUNT 16 19 | 20 | // Number of interrupt channels (excluding trap handler) in the vector table. 21 | #define RISCV_VT_INT_COUNT 31 22 | // Number of padding words in the vector table. 23 | #define RISCV_VT_PADDING 0 24 | // Bitmask for interrupt cause. 25 | #define RISCV_VT_ICAUSE_MASK 31 26 | // Bitmask for trap cause. 27 | #define RISCV_VT_TCAUSE_MASK 31 28 | -------------------------------------------------------------------------------- /kernel/cpu/riscv/include/cpu/interrupt.h: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #pragma once 5 | 6 | #include "cpu/regs.h" 7 | #include "cpu/riscv.h" 8 | 9 | #include 10 | 11 | // Enable interrupts if a condition is met. 12 | static inline void irq_enable_if(bool enable) { 13 | long mask = enable << CSR_STATUS_IE_BIT; 14 | asm volatile("csrs " CSR_STATUS_STR ", %0" ::"ri"(mask)); 15 | } 16 | 17 | // Disable interrupts if a condition is met. 18 | static inline void irq_disable_if(bool disable) { 19 | long mask = disable << CSR_STATUS_IE_BIT; 20 | asm volatile("csrc " CSR_STATUS_STR ", %0" ::"ri"(mask)); 21 | } 22 | 23 | // Enable interrupts. 24 | static inline void irq_enable() { 25 | long mask = 1 << CSR_STATUS_IE_BIT; 26 | asm volatile("csrs " CSR_STATUS_STR ", %0" ::"ri"(mask)); 27 | } 28 | 29 | // Disable interrupts. 30 | // Returns whether interrupts were enabled. 31 | static inline bool irq_disable() { 32 | long mask = 1 << CSR_STATUS_IE_BIT; 33 | asm volatile("csrrc %0, " CSR_STATUS_STR ", %0" : "+r"(mask)); 34 | return (mask >> CSR_STATUS_IE_BIT) & 1; 35 | } 36 | 37 | // Query whether interrupts are enabled in this CPU. 38 | static inline bool irq_is_enabled() { 39 | long mask; 40 | asm("csrr %0, " CSR_STATUS_STR : "=r"(mask)); 41 | return (mask >> CSR_STATUS_IE_BIT) & 1; 42 | } 43 | -------------------------------------------------------------------------------- /kernel/cpu/riscv/include/cpu/interrupt/riscv_intc.h: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | 11 | 12 | // Interrupt handler for the INTC to forward external interrupts to. 13 | extern void (*intc_ext_irq_handler)(); 14 | -------------------------------------------------------------------------------- /kernel/cpu/riscv/include/cpu/interrupt/riscv_plic.h: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | 11 | 12 | // Maximum number of PLIC contexts. 13 | #define PLIC_MAX_CTX_COUNT 15872 14 | // Address space size of the PLIC. 15 | #define PLIC_MEM_SIZE 0x4000000 16 | 17 | // Offset for interrupt priorities. 18 | #define PLIC_PRIO_OFF 0x000000 19 | // Offset for interrupt pending bits. 20 | #define PLIC_PENDING_OFF 0x001000 21 | // Offset for interrupt enable bits. 22 | #define PLIC_ENABLE_OFF(ctx) (0x002000 + (ctx) * 0x80) 23 | // Offset for priority threshold. 24 | #define PLIC_THRESH_OFF(ctx) (0x200000 + (ctx) * 0x1000) 25 | // Offset for claim/complete. 26 | #define PLIC_CLAIM_OFF(ctx) (0x200004 + (ctx) * 0x1000) 27 | -------------------------------------------------------------------------------- /kernel/cpu/riscv/include/cpu/isr.h: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #pragma once 5 | 6 | #include "cpu/regs.h" 7 | #include "cpu/riscv.h" 8 | #include "isr_ctx.h" 9 | 10 | #ifndef __ASSEMBLER__ 11 | #include 12 | #include 13 | #include 14 | #endif 15 | 16 | #ifdef __ASSEMBLER__ 17 | // clang-format off 18 | 19 | // Interrupt vector table implemented in ASM. 20 | .global riscv_interrupt_vector_table 21 | // Called from ASM on interrupt. 22 | .global riscv_interrupt_handler 23 | // Called from ASM on trap. 24 | .global riscv_trap_handler 25 | 26 | // clang-format on 27 | #else 28 | // Interrupt vector table implemented in ASM. 29 | extern uint32_t const riscv_interrupt_vector_table[32]; 30 | // Callback from ASM to platform-specific interrupt handler. 31 | extern void riscv_interrupt_handler(); 32 | // ASM system call wrapper function. 33 | extern void riscv_syscall_wrapper(); 34 | // Callback from ASM on non-syscall trap. 35 | extern void riscv_trap_handler(); 36 | 37 | // Explicit context switch from kernel. 38 | // Interrupts must be disabled on entry and will be re-enabled on exit. 39 | // If the context switch target is not set, this is a NOP. 40 | extern void isr_context_switch(); 41 | // Pause the CPU briefly. 42 | static inline void isr_pause() { 43 | // RISC-V Zihintpause instruction. 44 | // This is a fence with PRED=W and SUCC=none. 45 | asm(".word 0x0100000f"); 46 | } 47 | 48 | #endif 49 | -------------------------------------------------------------------------------- /kernel/cpu/riscv/include/cpu/riscv.h: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #pragma once 5 | 6 | // Undef x86 macros because intellisense doesn't recognise RISC-V. 7 | #ifndef __riscv_xlen 8 | #ifdef __x86_64__ 9 | #define __riscv_xlen 64 10 | #else 11 | #define __riscv_xlen 32 12 | #endif 13 | #endif 14 | #undef __x86_64__ 15 | #undef __i386__ 16 | #ifndef __riscv 17 | #define __riscv 18 | #endif 19 | 20 | 21 | 22 | /* ==== RISC-V MSTATUS DEFINITION ==== */ 23 | 24 | #define RISCV_STATUS_SIE_BIT 1 25 | #define RISCV_STATUS_MIE_BIT 3 26 | #define RISCV_STATUS_SPIE_BIT 5 27 | #define RISCV_STATUS_UBE_BIT 6 28 | #define RISCV_STATUS_MPIE_BIT 7 29 | #define RISCV_STATUS_SPP_BIT 8 30 | #define RISCV_STATUS_VS_BASE_BIT 9 // ,10 31 | #define RISCV_STATUS_MPP_BASE_BIT 11 // ,12 32 | #define RISCV_STATUS_FS_BASE_BIT 13 // ,14 33 | #define RISCV_STATUS_XS_BASE_BIT 15 // ,16 34 | #define RISCV_STATUS_MPRV_BIT 17 35 | #define RISCV_STATUS_SUM_BIT 18 36 | #define RISCV_STATUS_MXR_BIT 19 37 | #define RISCV_STATUS_TVM_BIT 20 38 | #define RISCV_STATUS_TW_BIT 21 39 | #define RISCV_STATUS_TSR_BIT 22 40 | #define RISCV_STATUS_SR_BIT 31 41 | 42 | 43 | 44 | /* ==== RISC-V INTERRUPT LIST ==== */ 45 | #define RISCV_INT_SUPERVISOR_SOFT 1 46 | #define RISCV_INT_MACHINE_SOFT 3 47 | #define RISCV_INT_SUPERVISOR_TIMER 5 48 | #define RISCV_INT_MACHINE_TIMER 7 49 | #define RISCV_INT_SUPERVISOR_EXT 9 50 | #define RISCV_INT_MACHINE_EXT 11 51 | 52 | 53 | 54 | /* ==== RISC-V TRAP LIST ==== */ 55 | 56 | // Instruction access misaligned. 57 | #define RISCV_TRAP_IALIGN 0x00 58 | // Instruction access fault. 59 | #define RISCV_TRAP_IACCESS 0x01 60 | // Illegal instruction. 61 | #define RISCV_TRAP_IILLEGAL 0x02 62 | // Trace / breakpoint trap. 63 | #define RISCV_TRAP_EBREAK 0x03 64 | // Load access misaligned. 65 | #define RISCV_TRAP_LALIGN 0x04 66 | // Load access fault. 67 | #define RISCV_TRAP_LACCESS 0x05 68 | // Store / AMO access misaligned. 69 | #define RISCV_TRAP_SALIGN 0x06 70 | // Store / AMO access fault. 71 | #define RISCV_TRAP_SACCESS 0x07 72 | // ECALL from U-mode. 73 | #define RISCV_TRAP_U_ECALL 0x08 74 | // ECALL from S-mode. 75 | #define RISCV_TRAP_S_ECALL 0x09 76 | // ECALL from M-mode. 77 | #define RISCV_TRAP_M_ECALL 0x0B 78 | // Instruction page fault. 79 | #define RISCV_TRAP_IPAGE 0x0C 80 | // Load page fault. 81 | #define RISCV_TRAP_LPAGE 0x0D 82 | // Store / AMO page fault. 83 | #define RISCV_TRAP_SPAGE 0x0F 84 | -------------------------------------------------------------------------------- /kernel/cpu/riscv/src/backtrace.c: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #include "backtrace.h" 5 | 6 | #include "attributes.h" 7 | #include "isr_ctx.h" 8 | #include "rawprint.h" 9 | 10 | #include 11 | 12 | #ifndef BACKTRACE_DEPTH 13 | #define BACKTRACE_DEPTH 32 14 | #endif 15 | 16 | 17 | 18 | // Given stack frame pointer, perform backtrace. 19 | void backtrace_from_ptr(void *frame_pointer) { 20 | rawprint("**** BEGIN BACKRTACE ****\n"); 21 | // Prev FP offset: -2 words 22 | // Prev RA offset: -1 word 23 | atomic_thread_fence(memory_order_acquire); 24 | size_t *fp = frame_pointer; 25 | for (int i = 0; i < BACKTRACE_DEPTH; i++) { 26 | size_t ra; 27 | if ((size_t)fp < 0x1000 || isr_noexc_copy_size(&ra, fp - 1)) { 28 | break; 29 | } 30 | rawprint("0x"); 31 | rawprinthex(ra, sizeof(size_t) * 2); 32 | rawputc('\n'); 33 | if ((size_t)fp < 0x1000 || isr_noexc_copy_size((size_t *)&fp, fp - 2)) { 34 | break; 35 | } 36 | } 37 | rawprint("**** END BACKRTACE ****\n"); 38 | } 39 | 40 | // Perform backtrace as called. 41 | void backtrace() NAKED; 42 | #if __riscv_xlen == 64 43 | void backtrace() { 44 | asm volatile("addi sp, sp, -16"); 45 | asm volatile("sd ra, 8(sp)"); 46 | asm volatile("sd s0, 0(sp)"); 47 | asm volatile("addi s0, sp, 16"); 48 | asm volatile("mv a0, s0"); 49 | asm volatile("jal backtrace_from_ptr"); 50 | asm volatile("ld ra, 8(sp)"); 51 | asm volatile("ld s0, 0(sp)"); 52 | asm volatile("addi sp, sp, 16"); 53 | asm volatile("ret"); 54 | } 55 | #else 56 | void backtrace() { 57 | asm volatile("addi sp, sp, -16"); 58 | asm volatile("sw ra, 12(sp)"); 59 | asm volatile("sw s0, 8(sp)"); 60 | asm volatile("addi s0, sp, 16"); 61 | asm volatile("mv a0, s0"); 62 | asm volatile("jal backtrace_from_ptr"); 63 | asm volatile("lw ra, 12(sp)"); 64 | asm volatile("lw s0, 8(sp)"); 65 | asm volatile("addi sp, sp, 16"); 66 | asm volatile("ret"); 67 | } 68 | #endif 69 | -------------------------------------------------------------------------------- /kernel/cpu/riscv/src/cpu1_entrypoint.S: -------------------------------------------------------------------------------- 1 | 2 | # SPDX-License-Identifier: MIT 3 | 4 | #include 5 | 6 | # The global pointer is used for relaxation of `.data` accesses. 7 | .global __global_pointer$ 8 | 9 | 10 | 11 | #if __riscv_xlen == 64 12 | #define ST_REG sd 13 | #define LD_REG ld 14 | #else 15 | #define ST_REG sw 16 | #define LD_REG lw 17 | #endif 18 | 19 | 20 | 21 | # Entrypoint from the bootloader. 22 | .text 23 | .align 2 24 | .global _cpu1_start 25 | .type _cpu1_start, %function 26 | _cpu1_start: 27 | # Set up registers. 28 | .option push 29 | .option norelax 30 | la gp, __global_pointer$ 31 | .option pop 32 | mv tp, x0 33 | LD_REG sp, cpu1_temp_stack 34 | 35 | # Jump to the C entrypoint. 36 | jal cpu1_init 37 | # This function isn't allowed to return. 38 | ebreak 39 | -------------------------------------------------------------------------------- /kernel/cpu/riscv/src/entrypoint.S: -------------------------------------------------------------------------------- 1 | 2 | # SPDX-License-Identifier: MIT 3 | 4 | #include 5 | 6 | # This is the C entrypoint, which will be called when basic CRT is set up. 7 | .global basic_runtime_init 8 | # The global pointer is used for relaxation of `.data` accesses. 9 | .global __global_pointer$ 10 | # First address in `.bss` (inclusive). 11 | .global __start_bss 12 | # Last address in `.bss` (exclusive). 13 | .global __stop_bss 14 | # First address in `.init_array` (inclusive). 15 | .global __start_init_array 16 | # Last address in `.init_array` (exclusive). 17 | .global __stop_init_array 18 | 19 | 20 | 21 | # Entrypoint from the bootloader. 22 | .text 23 | .align 2 24 | .global _start 25 | .type _start, %function 26 | .cfi_startproc 27 | _start: 28 | .cfi_undefined %sp 29 | .cfi_undefined %fp 30 | .cfi_undefined %ra 31 | # Set up registers. 32 | .option push 33 | .option norelax 34 | la gp, __global_pointer$ 35 | .option pop 36 | mv tp, x0 37 | 38 | # Zero out .bss section. 39 | #ifndef CONFIG_TARGET_generic 40 | la a0, __start_bss 41 | la a1, __stop_bss 42 | beq a0, a1, .bssinit_skip 43 | .bssinit_loop: 44 | sw x0, 0(a0) 45 | addi a0, a0, 4 46 | bne a0, a1, .bssinit_loop 47 | .bssinit_skip: 48 | #endif 49 | 50 | # Jump to the C entrypoint. 51 | jal basic_runtime_init 52 | # This function isn't allowed to return. 53 | ebreak 54 | .cfi_endproc 55 | -------------------------------------------------------------------------------- /kernel/cpu/riscv/src/interrupt.c: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #include "interrupt.h" 5 | 6 | #include "cpu/isr.h" 7 | #include "cpulocal.h" 8 | 9 | 10 | 11 | // Initialise interrupt drivers for this CPU. 12 | void irq_init(isr_ctx_t *tmp_ctx) { 13 | // Install interrupt handler. 14 | asm volatile("csrw sstatus, 0"); 15 | asm volatile("csrw stvec, %0" ::"r"(riscv_interrupt_vector_table)); 16 | asm volatile("csrw sscratch, %0" ::"r"(tmp_ctx)); 17 | 18 | // Disable all internal interrupts. 19 | asm volatile("csrw sie, 0"); 20 | } 21 | -------------------------------------------------------------------------------- /kernel/cpu/riscv/src/interrupt/riscv_intc.c: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #include "cpu/riscv.h" 5 | #include "interrupt.h" 6 | #include "log.h" 7 | #include "panic.h" 8 | 9 | #ifdef CPU_RISCV_ENABLE_SBI_TIME 10 | // Called by the interrupt handler when the CPU-local timer fires. 11 | void riscv_sbi_timer_interrupt(); 12 | #endif 13 | 14 | // Interrupt handler for the INTC to forward external interrupts to. 15 | void (*intc_ext_irq_handler)(); 16 | 17 | void riscv_interrupt_handler() { 18 | long cause; 19 | asm("csrr %0, " CSR_CAUSE_STR : "=r"(cause)); 20 | 21 | long int_no = cause & RISCV_VT_ICAUSE_MASK; 22 | 23 | if (int_no == RISCV_INT_SUPERVISOR_EXT) { 24 | intc_ext_irq_handler(); 25 | #ifdef CPU_RISCV_ENABLE_SBI_TIME 26 | } else if (int_no == RISCV_INT_SUPERVISOR_TIMER) { 27 | asm("csrc sie, %0" ::"r"(1 << RISCV_INT_SUPERVISOR_TIMER)); 28 | riscv_sbi_timer_interrupt(); 29 | #endif 30 | } else { 31 | logkf_from_isr(LOG_FATAL, "Unhandled interrupt 0x%{long;x}", cause); 32 | panic_abort(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /kernel/cpu/riscv/src/panic.c: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #include "panic.h" 5 | 6 | #include "cpu/regs.h" 7 | 8 | void cpu_panic_poweroff() { 9 | asm volatile("csrci " CSR_STATUS_STR ", %0" ::"ri"(1 << CSR_STATUS_IE_BIT)); 10 | while (1) asm volatile("wfi"); 11 | } 12 | -------------------------------------------------------------------------------- /kernel/cpu/riscv/src/syscall.S: -------------------------------------------------------------------------------- 1 | 2 | # SPDX-License-Identifier: MIT 3 | 4 | 5 | 6 | .text 7 | .align 2 8 | .global riscv_syscall_wrapper 9 | # Called directly from `sched_raise_from_isr`; overwrites callee-save registers. 10 | riscv_syscall_wrapper: 11 | # Save arguments. 12 | mv s1, a0 13 | mv s2, a1 14 | mv s3, a2 15 | mv s4, a3 16 | mv s5, a4 17 | mv s6, a5 18 | mv s7, a6 19 | # Get syscall information. 20 | mv a0, a7 21 | jal syscall_info 22 | # If it doesn't exist, go to sigsys handler. 23 | bnez a0, .gotosys 24 | j proc_sigsys_handler 25 | # Run the system call. 26 | .gotosys: 27 | mv s8, a0 28 | mv s9, a1 29 | mv a0, s1 30 | mv a1, s2 31 | mv a2, s3 32 | mv a3, s4 33 | mv a4, s5 34 | mv a5, s6 35 | mv a6, s7 36 | jalr s8 37 | # Truncate return value. 38 | andi t0, s9, 255 39 | li t1, __riscv_xlen/8 40 | bgt t0, t1, .args2 41 | bnez t0, .args1 42 | li a0, 0 43 | .args1: 44 | li a1, 0 45 | .args2: 46 | # Return from system call. 47 | j syscall_return 48 | -------------------------------------------------------------------------------- /kernel/cpu/riscv32/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include(cpu/riscv/CMakeLists.txt) -------------------------------------------------------------------------------- /kernel/cpu/riscv64/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include(cpu/riscv/CMakeLists.txt) -------------------------------------------------------------------------------- /kernel/include/backtrace.h: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #pragma once 5 | 6 | // Given stack frame pointer, perform backtrace. 7 | void backtrace_from_ptr(void *frame_pointer); 8 | // Perform backtrace as called. 9 | void backtrace(); 10 | -------------------------------------------------------------------------------- /kernel/include/badgelib/badge_format_str.h: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | // Callback function used by format_str. 12 | typedef bool (*format_str_cb_t)(char const *msg, size_t len, void *cookie); 13 | 14 | // Format a string and output characters via callback. 15 | // 16 | // Format string types: 17 | // %% - Print a literal % character. 18 | // %{cs} - Print a C-String; alias for `%{char;c;nul}`. 19 | // %{ls} - Print a pointer/length string; alias for `%{char;c;array}`. 20 | // %{<;multiple><;joiner>} - General format string; type and multiple are optional. 21 | // 22 | // Format specifiers: 23 | // d - Print decimal. 24 | // x - Print unsigned hexadecimal. 25 | // o - Print unsigned octal. 26 | // q - Print unsigned octal. 27 | // c - Print a single character. 28 | // Hexadecimal is uppercase if the specifier is uppercase and vice versa. 29 | // 30 | // Type speciciers: 31 | // (omitted) - signed int 32 | // ullong / llong - unsigned / signed long long 33 | // ulong / long - unsigned / signed long 34 | // uint / int - unsigned / signed int 35 | // ushort / short - unsigned / signed short int 36 | // uchar / char - unsigned / signed char 37 | // u64 / i64 - uint64_t / int64_t 38 | // u32 / i32 - uint32_t / int32_t 39 | // u16 / i16 - uint16_t / int16_t 40 | // u8 / i8 - uint8_t / int8_t 41 | // size - size_t 42 | // ptrdiff - ptrdiff_t 43 | // 44 | // Multiples specifiers: 45 | // (omitted) - Single occurrance; values are passed directly. 46 | // nul / null - NULL-terminated array of values; values are passed by pointer to null-terminated array. 47 | // arr / array - Pointer+length array of values; values are passed by array pointer followed by array length. 48 | // 49 | // Joiner specifiers: 50 | // Jointer specifiers specify a string to put between array elements. 51 | // They can contain any character except '}'. 52 | // 53 | // If one of the format specifiers is malformed, the remaining text is output verbatim. 54 | bool format_str_va(char const *msg, size_t length, format_str_cb_t callback, void *cookie, va_list vararg); 55 | -------------------------------------------------------------------------------- /kernel/include/badgelib/fifo.h: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | 11 | 12 | // A generic thread-safe nonblocking FIFO safe for multi-read, multi-write use. 13 | // To achieve the multi-read, multi-write safety, the FIFO has a "reservation" and "index" each for read and write. 14 | // The reservation in atomically updated to reserve data in the buffer to read or write. 15 | // When the read or write is finished, the index is updated in an ACAS loop. 16 | typedef struct fifo fifo_t; 17 | 18 | // Create a new FIFO. 19 | fifo_t *fifo_create(size_t ent_size, size_t capacity); 20 | // Destroy a FIFO. 21 | void fifo_destroy(fifo_t *fifo); 22 | 23 | // Send multiple elements to a FIFO. 24 | // Returns the actual amount of elements sent. 25 | size_t fifo_send_n(fifo_t *fifo, void const *data, size_t max_send); 26 | // Receive an element from a FIFO. 27 | // Returns the actual amount of elements received.. 28 | size_t fifo_recv_n(fifo_t *fifo, void *data, size_t max_recv); 29 | 30 | // Atomically test how much data can be sent. 31 | size_t fifo_max_send(fifo_t *fifo); 32 | // Atomically test how much data can be received. 33 | size_t fifo_max_recv(fifo_t *fifo); 34 | 35 | // Try to send a single element to a FIFO. 36 | static inline bool fifo_send_1(fifo_t *fifo, void const *data) { 37 | return fifo_send_n(fifo, data, 1); 38 | } 39 | // Try to receive a single element from a FIFO. 40 | static inline bool fifo_recv_1(fifo_t *fifo, void *data) { 41 | return fifo_recv_n(fifo, data, 1); 42 | } 43 | -------------------------------------------------------------------------------- /kernel/include/badgelib/hwtimer.h: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #pragma once 5 | 6 | #include "attributes.h" 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #define FREQUENCY_HZ_MIN INT32_MIN 13 | #define FREQUENCY_HZ_MAX INT32_MAX 14 | #define TIMER_VALUE_MIN INT64_MIN 15 | #define TIMER_VALUE_MAX INT64_MAX 16 | 17 | typedef int32_t frequency_hz_t; 18 | typedef int64_t timer_value_t; 19 | 20 | // Initialise timer and watchdog subsystem. 21 | void timer_init(); 22 | // Get the number of hardware timers. 23 | int timer_count() CONST; 24 | // Get the IRQ number for a timer. 25 | int timer_get_irq(int timerno); 26 | // Set the counting frequency of a hardware timer. 27 | void timer_set_freq(int timerno, frequency_hz_t frequency); 28 | 29 | // Configure and enable timer alarm. 30 | void timer_alarm_config(int timerno, timer_value_t threshold, bool reset_on_alarm); 31 | // Disable timer alarm. 32 | void timer_alarm_disable(int timerno); 33 | // Get the current value of timer. 34 | timer_value_t timer_value_get(int timerno); 35 | // Set the current value of timer. 36 | void timer_value_set(int timerno, timer_value_t value); 37 | // Enable the timer counting. 38 | void timer_start(int timerno); 39 | // Disable the timer counting. 40 | void timer_stop(int timerno); 41 | 42 | // Check whether timer has interrupts enabled. 43 | bool timer_int_enabled(int timerno); 44 | // Enable / disable timer interrupts. 45 | void timer_int_enable(int timerno, bool enable); 46 | // Check whether timer interrupt had fired. 47 | bool timer_int_pending(int timerno); 48 | // Clear timer interrupt. 49 | void timer_int_clear(int timerno); 50 | -------------------------------------------------------------------------------- /kernel/include/badgelib/log.h: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #pragma once 5 | 6 | #include "mutex.h" 7 | 8 | #include 9 | 10 | 11 | 12 | // Number of microseconds before log mutex times out. 13 | #define LOG_MUTEX_TIMEOUT 500000 14 | 15 | typedef enum { 16 | LOG_FATAL, 17 | LOG_ERROR, 18 | LOG_WARN, 19 | LOG_INFO, 20 | LOG_DEBUG, 21 | } log_level_t; 22 | 23 | extern mutex_t log_mtx; 24 | 25 | // Print an unformatted message. 26 | void logk(log_level_t level, char const *msg); 27 | // Print a formatted message according to format_str. 28 | void logkf(log_level_t level, char const *msg, ...); 29 | // Print a hexdump (usually for debug purposes). 30 | void logk_hexdump(log_level_t level, char const *msg, void const *data, size_t size); 31 | // Print a hexdump, override the address shown (usually for debug purposes). 32 | void logk_hexdump_vaddr(log_level_t level, char const *msg, void const *data, size_t size, size_t vaddr); 33 | 34 | // Print an unformatted message from an interrupt. 35 | // Only use this function in emergencies. 36 | void logk_from_isr(log_level_t level, char const *msg); 37 | // Print a formatted message according to format_str from an interrupt. 38 | // Only use this function in emergencies. 39 | void logkf_from_isr(log_level_t level, char const *msg, ...); 40 | // Print a hexdump (usually for debug purposes) from an interrupt. 41 | // Only use this function in emergencies. 42 | void logk_hexdump_from_isr(log_level_t level, char const *msg, void const *data, size_t size); 43 | // Print a hexdump, override the address shown (usually for debug purposes) from an interrupt. 44 | // Only use this function in emergencies. 45 | void logk_hexdump_vaddr_from_isr(log_level_t level, char const *msg, void const *data, size_t size, size_t vaddr); 46 | -------------------------------------------------------------------------------- /kernel/include/badgelib/malloc.h: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #include 5 | 6 | void *malloc(size_t size); 7 | void free(void *ptr); 8 | void *calloc(size_t nmemb, size_t size); 9 | void *realloc(void *ptr, size_t size); 10 | void *reallocarray(void *ptr, size_t nmemb, size_t size); 11 | 12 | void kernel_heap_init(); 13 | -------------------------------------------------------------------------------- /kernel/include/badgelib/mutex.h: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #pragma once 5 | 6 | #include "scheduler/waitlist.h" 7 | #include "time.h" 8 | 9 | #include 10 | #include 11 | 12 | // A mutex. 13 | typedef struct { 14 | // Mutex allows sharing. 15 | bool is_shared; 16 | // Share count and/or is locked. 17 | atomic_int shares; 18 | // List of threads waiting for this mutex. 19 | waitlist_t waiting_list; 20 | } mutex_t; 21 | 22 | 23 | #define MUTEX_FAST_LOOPS 256 24 | #define MUTEX_T_INIT {0, 0, WAITLIST_T_INIT} 25 | #define MUTEX_T_INIT_SHARED {1, 0, WAITLIST_T_INIT} 26 | 27 | 28 | 29 | // Recommended way to create a mutex at run-time. 30 | void mutex_init(mutex_t *mutex, bool shared); 31 | // Clean up the mutex. 32 | void mutex_destroy(mutex_t *mutex); 33 | 34 | // Try to acquire `mutex` within `max_wait_us` microseconds. 35 | // If `max_wait_us` is too long or negative, do not use the timeout. 36 | // Returns true if the mutex was successully acquired. 37 | bool mutex_acquire(mutex_t *mutex, timestamp_us_t max_wait_us); 38 | // Release `mutex`, if it was initially acquired by this thread. 39 | // Returns true if the mutex was successfully released. 40 | bool mutex_release(mutex_t *mutex); 41 | 42 | // Try to acquire a share in `mutex` within `max_wait_us` microseconds. 43 | // If `max_wait_us` is too long or negative, do not use the timeout. 44 | // Returns true if the share was successfully acquired. 45 | bool mutex_acquire_shared(mutex_t *mutex, timestamp_us_t max_wait_us); 46 | // Release `mutex`, if it was initially acquired by this thread. 47 | // Returns true if the mutex was successfully released. 48 | bool mutex_release_shared(mutex_t *mutex); 49 | -------------------------------------------------------------------------------- /kernel/include/badgelib/num_to_str.h: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | // Table used to convert to hexadecimal. 11 | // The contents are 0-9 and uppercase A-F. 12 | // To obtain lowercase, bitwise OR with 0x20. 13 | extern char const hexadecimal_table[16]; 14 | 15 | // Exactly 16-bit binary to decimal into a 5-char buffer. 16 | void num_uint16_to_str(uint16_t val, char outbuf[5]); 17 | // Exactly 32-bit binary to decimal into a 10-char buffer. 18 | void num_uint32_to_str(uint32_t val, char outbuf[10]); 19 | // Exactly 64-bit binary to decimal into a 20-char buffer. 20 | void num_uint64_to_str(uint64_t val, char outbuf[20]); 21 | // Up to 64-bit binary to decimal into a 20-char buffer. 22 | // Pads the buffer with zeroes. 23 | // Returns the amount of non-zero digits, which may be 0. 24 | size_t num_uint_to_str(uint64_t val, char outbuf[20]); 25 | 26 | // Generic signed binary to decimal. 27 | // Places a NULL terminator at the end. 28 | // Returns the length of the generated string. 29 | size_t int_to_cstr(int64_t val, char *outbuf, size_t size); 30 | // Generic unsigned binary to decimal. 31 | // Places a NULL terminator at the end. 32 | // Returns the length of the generated string. 33 | size_t uint_to_cstr(uint64_t val, char *outbuf, size_t size); 34 | 35 | // Generic signed binary to decimal. 36 | // Places a NULL terminator if the buffer is not filled entirely. 37 | // Returns the length of the generated string. 38 | size_t int_to_cstr_packed(int64_t val, char *outbuf, size_t size); 39 | // Generic unsigned binary to decimal. 40 | // Places a NULL terminator if the buffer is not filled entirely. 41 | // Returns the length of the generated string. 42 | size_t uint_to_cstr_packed(uint64_t val, char *outbuf, size_t size); 43 | -------------------------------------------------------------------------------- /kernel/include/badgelib/panic.h: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #pragma once 5 | 6 | #include "attributes.h" 7 | 8 | // Try to atomically claim the panic flag. 9 | // Only ever call this if a subsequent call to `panic_abort_unchecked` or `panic_poweroff_unchecked` is imminent. 10 | void claim_panic(); 11 | 12 | // Like `panic_abort`, but does not check the panic flag. 13 | void panic_abort_unchecked() NORETURN; 14 | // Like `panic_poweroff`, but does not check the panic flag. 15 | void panic_poweroff_unchecked() NORETURN; 16 | 17 | // Call this function when and only when the kernel has encountered a fatal error. 18 | // Prints register dump for current kernel context and jumps to `panic_poweroff`. 19 | void panic_abort() NORETURN; 20 | // Call this function when and only when the kernel has encountered a fatal error. 21 | // Immediately power off or reset the system. 22 | void panic_poweroff() NORETURN; 23 | // Check for a panic and immediately halt if it has happened. 24 | void check_for_panic(); 25 | -------------------------------------------------------------------------------- /kernel/include/badgelib/rawprint.h: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | 9 | // Simple printer with specified length. 10 | void rawprint_substr(char const *msg, size_t length); 11 | // Simple printer. 12 | void rawprint(char const *msg); 13 | // Simple printer. 14 | void rawputc(char msg); 15 | // Bin 2 hex printer. 16 | void rawprinthex(uint64_t val, int digits); 17 | // Bin 2 dec printer. 18 | void rawprintudec(uint64_t val, int digits); 19 | // Bin 2 dec printer. 20 | void rawprintdec(int64_t val, int digits); 21 | // Current uptime printer for logging. 22 | void rawprintuptime(); 23 | -------------------------------------------------------------------------------- /kernel/include/badgelib/semaphore.h: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #pragma once 5 | 6 | #include "scheduler/waitlist.h" 7 | #include "time.h" 8 | 9 | #include 10 | #include 11 | 12 | // Semaphore. 13 | typedef struct { 14 | // Number of times posted. 15 | atomic_int available; 16 | // Threads waiting for the semaphore to be posted. 17 | waitlist_t waiting_list; 18 | } sem_t; 19 | 20 | #define SEM_FAST_LOOPS 256 21 | #define SEM_T_INIT {0, WAITLIST_T_INIT} 22 | 23 | // Initialize a new semaphore. 24 | void sem_init(sem_t *sem); 25 | // Clean up a semaphore. 26 | void sem_destroy(sem_t *sem); 27 | 28 | // Reset the semaphore count. 29 | void sem_reset(sem_t *sem); 30 | // Post the semaphore once. 31 | void sem_post(sem_t *sem); 32 | // Await the semaphore. 33 | bool sem_await(sem_t *sem, timestamp_us_t timeout); 34 | -------------------------------------------------------------------------------- /kernel/include/badgelib/spinlock.h: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #pragma once 5 | 6 | #include 7 | 8 | 9 | 10 | typedef atomic_int spinlock_t; 11 | 12 | #define SPINLOCK_T_INIT 0 13 | #define SPINLOCK_T_INIT_SHARED 1 14 | 15 | 16 | 17 | // Take the spinlock exclusively. 18 | void spinlock_take(spinlock_t *lock); 19 | // Release the spinlock exclusively. 20 | void spinlock_release(spinlock_t *lock); 21 | // Take the spinlock shared. 22 | void spinlock_take_shared(spinlock_t *lock); 23 | // Release the spinlock shared. 24 | void spinlock_release_shared(spinlock_t *lock); 25 | -------------------------------------------------------------------------------- /kernel/include/badgelib/static-buddy.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | #pragma once 4 | #ifndef BADGEROS_KERNEL 5 | #define _GNU_SOURCE 6 | #endif 7 | 8 | #include "config.h" 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | #define PAGE_SIZE 4096 15 | #ifdef CONFIG_TARGET_generic 16 | #define MAX_MEMORY_POOLS 16 17 | #else 18 | #define MAX_MEMORY_POOLS 4 19 | #endif 20 | #define MAX_SLAB_SIZE 256 21 | 22 | #define ALIGN_UP(x, y) (void *)(((size_t)(x) + (y - 1)) & ~(y - 1)) 23 | #define ALIGN_DOWN(x, y) (void *)((size_t)(x) & ~(y - 1)) 24 | 25 | #define ALIGN_PAGE_UP(x) ALIGN_UP(x, PAGE_SIZE) 26 | #define ALIGN_PAGE_DOWN(x) ALIGN_DOWN(x, PAGE_SIZE) 27 | 28 | enum block_type { BLOCK_TYPE_FREE, BLOCK_TYPE_USER, BLOCK_TYPE_PAGE, BLOCK_TYPE_SLAB, BLOCK_TYPE_ERROR }; 29 | enum slab_type { SLAB_TYPE_SLAB }; 30 | 31 | void init_pool(void *mem_start, void *mem_end, uint32_t flags); 32 | void init_kernel_slabs(); 33 | void print_allocator(); 34 | 35 | void *slab_allocate(size_t size, enum slab_type type, uint32_t flags); 36 | void slab_deallocate(void *ptr); 37 | size_t slab_get_size(void *ptr); 38 | 39 | void *buddy_allocate(size_t size, enum block_type type, uint32_t flags); 40 | void *buddy_reallocate(void *ptr, size_t size); 41 | void buddy_deallocate(void *ptr); 42 | void buddy_split_allocated(void *ptr); 43 | enum block_type buddy_get_type(void *ptr); 44 | size_t buddy_get_size(void *ptr); 45 | 46 | typedef struct buddy_block { 47 | uint8_t pid; 48 | uint8_t order; 49 | bool in_list; 50 | bool is_waste; 51 | enum block_type type; 52 | struct buddy_block *next; 53 | struct buddy_block *prev; 54 | } buddy_block_t; 55 | 56 | typedef struct { 57 | uint32_t flags; 58 | void *start; 59 | void *end; 60 | void *pages_start; 61 | void *pages_end; 62 | size_t pages; 63 | size_t free_pages; 64 | uint8_t max_order; 65 | uint8_t max_order_free; 66 | uint32_t max_order_waste; 67 | buddy_block_t waste_list; 68 | buddy_block_t *free_lists; 69 | buddy_block_t *blocks; 70 | } memory_pool_t; 71 | 72 | extern uint8_t memory_pool_num; 73 | extern memory_pool_t memory_pools[MAX_MEMORY_POOLS]; 74 | -------------------------------------------------------------------------------- /kernel/include/badgelib/time.h: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #pragma once 5 | 6 | #include "port/time.h" 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #define TIMESTAMP_US_MIN INT64_MIN 13 | #define TIMESTAMP_US_MAX INT64_MAX 14 | 15 | typedef int64_t timestamp_us_t; 16 | 17 | // Timer callback function. 18 | typedef void (*timer_fn_t)(void *cookie); 19 | 20 | // Timer callback. 21 | typedef struct { 22 | // Timer task no. 23 | int64_t taskno; 24 | // Timestamp to run callback at. 25 | timestamp_us_t timestamp; 26 | // Timer callback function. 27 | timer_fn_t callback; 28 | // Cookie for timer callback function. 29 | void *cookie; 30 | } timertask_t; 31 | 32 | // Sets the alarm time when the next callback switch should occur. 33 | void time_set_next_task_switch(timestamp_us_t timestamp); 34 | // Attach a callback to a timer interrupt. 35 | // The callback with the lowest timestamp is likeliest, but not guaranteed, to run first. 36 | // Returns whether the task was successfully added. 37 | bool time_add_async_task(timertask_t *task); 38 | // Cancel a callback created with `time_add_async_task`. 39 | bool time_cancel_async_task(int64_t taskno); 40 | // Get current time in microseconds. 41 | timestamp_us_t time_us(); 42 | -------------------------------------------------------------------------------- /kernel/include/badgelib/time_private.h: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #pragma once 5 | 6 | #include "time.h" 7 | 8 | // Time CPU-local data. 9 | typedef struct { 10 | // Whether the timer is set for preemption instead of timer callback. 11 | bool timer_is_preempt; 12 | // Next time to preempt at. 13 | timestamp_us_t preempt_time; 14 | } time_cpulocal_t; 15 | 16 | 17 | 18 | // Set the CPU's timer to a certain timestamp. 19 | void time_set_cpu_timer(timestamp_us_t timestamp); 20 | // Clear the CPU's timer. 21 | void time_clear_cpu_timer(); 22 | // Generic timer init after timer-specific init. 23 | void time_init_generic(); 24 | // Callback from timer-specific code when the CPU timer fires. 25 | void time_cpu_timer_isr(); 26 | -------------------------------------------------------------------------------- /kernel/include/blockdevice/blkdev_impl.h: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #include "blockdevice.h" 5 | 6 | // Clean up driver-specific parts. 7 | typedef void (*blkdev_destroy_t)(blkdev_t *dev); 8 | // Connect to the block device if required. 9 | typedef void (*blkdev_open_t)(badge_err_t *ec, blkdev_t *dev); 10 | // Disconnect from the block device. 11 | // Writes will already have been flushed to disk. 12 | typedef void (*blkdev_close_t)(badge_err_t *ec, blkdev_t *dev); 13 | // Query whether a block is erased. 14 | typedef bool (*blkdev_is_erased_t)(badge_err_t *ec, blkdev_t *dev, blksize_t block); 15 | // Erase a block. 16 | typedef void (*blkdev_erase_t)(badge_err_t *ec, blkdev_t *dev, blksize_t block); 17 | // Erase if required and write a block. 18 | typedef void (*blkdev_write_t)(badge_err_t *ec, blkdev_t *dev, blksize_t block, uint8_t const *writebuf); 19 | // Read a block. 20 | typedef void (*blkdev_read_t)(badge_err_t *ec, blkdev_t *dev, blksize_t block, uint8_t *readbuf); 21 | // Partially write a block. 22 | // This is very likely to cause a read-modify-write operation. 23 | typedef void (*blkdev_write_partial_t)( 24 | badge_err_t *ec, 25 | blkdev_t *dev, 26 | blksize_t block, 27 | size_t subblock_offset, 28 | uint8_t const *writebuf, 29 | size_t writebuf_len 30 | ); 31 | // Partially read a block. 32 | // This may use read caching if the device doesn't support partial read. 33 | typedef void (*blkdev_read_partial_t)( 34 | badge_err_t *ec, blkdev_t *dev, blksize_t block, size_t subblock_offset, uint8_t *readbuf, size_t readbuf_len 35 | ); 36 | 37 | // Block device virtual function table. 38 | typedef struct blkdev_vtable blkdev_vtable_t; 39 | struct blkdev_vtable { 40 | blkdev_destroy_t destroy; 41 | blkdev_open_t open; 42 | blkdev_close_t close; 43 | blkdev_is_erased_t is_erased; 44 | blkdev_erase_t erase; 45 | blkdev_write_t write; 46 | blkdev_read_t read; 47 | blkdev_write_partial_t write_partial; 48 | blkdev_read_partial_t read_partial; 49 | }; 50 | 51 | // Create a new block device with a vtable and a cookie. 52 | blkdev_t *blkdev_impl_create(badge_err_t *ec, blkdev_vtable_t const *vtable, void *cookie); 53 | // Get a block device's cookie. 54 | void *blkdev_impl_get_cookie(blkdev_t const *blkdev); 55 | // Set read-only flag. 56 | void blkdev_impl_set_readonly(blkdev_t *blkdev, bool readonly); 57 | // Set block count. 58 | void blkdev_impl_set_size(blkdev_t *blkdev, blksize_t blocks); 59 | // Set block size. 60 | void blkdev_impl_set_block_size(blkdev_t *blkdev, blksize_t block_size); 61 | -------------------------------------------------------------------------------- /kernel/include/blockdevice/blkdev_ram.h: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #include "blockdevice.h" 5 | 6 | // Create a new RAM block device. 7 | blkdev_t *blkdev_ram_create(badge_err_t *ec, void *memory, size_t block_count, size_t block_size, bool readonly); 8 | -------------------------------------------------------------------------------- /kernel/include/cpulocal.h: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #pragma once 5 | 6 | #include "cpu/arch_cpulocal.h" 7 | #include "time_private.h" 8 | 9 | #include 10 | 11 | 12 | 13 | // CPU-local scheduler data. 14 | typedef struct sched_cpulocal_t sched_cpulocal_t; 15 | 16 | // CPU-local data. 17 | typedef struct cpulocal_t { 18 | // Current CPU ID. 19 | size_t cpuid; 20 | // Current SMP CPU inder. 21 | int cpu; 22 | // ISR stack top. 23 | size_t isr_stack_top; 24 | // ISR stack bottom. 25 | size_t isr_stack_bottom; 26 | // CPU-local scheduler data. 27 | sched_cpulocal_t *sched; 28 | // CPU-local timer data. 29 | time_cpulocal_t time; 30 | // Arch-specific CPU-local data. 31 | arch_cpulocal_t arch; 32 | } cpulocal_t; 33 | 34 | // Per-CPU CPU-local data. 35 | extern cpulocal_t *cpulocal; 36 | 37 | #include "scheduler/types.h" 38 | -------------------------------------------------------------------------------- /kernel/include/filesystem/fs_ramfs_types.h: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #pragma once 5 | 6 | #include "assertions.h" 7 | #include "attributes.h" 8 | #include "filesystem.h" 9 | #include "mutex.h" 10 | 11 | #include 12 | 13 | // Maximum name length of RAMFS. 14 | #define VFS_RAMFS_NAME_MAX 255 15 | #if FILESYSTEM_NAME_MAX < VFS_RAMFS_NAME_MAX 16 | #undef VFS_RAMFS_NAME_MAX 17 | #define VFS_RAMFS_NAME_MAX FILESYSTEM_NAME_MAX 18 | #endif 19 | 20 | // Bit position of file type in mode field. 21 | #define VFS_RAMFS_MODE_BIT 12 22 | // Bit mask of file type in mode field. 23 | #define VFS_RAMFS_MODE_MASK 0xf000 24 | 25 | 26 | 27 | /* ==== In-memory structures ==== */ 28 | 29 | // File data storage. 30 | typedef struct { 31 | // Data buffer length. 32 | size_t len; 33 | // Data buffer capacity. 34 | size_t cap; 35 | // Data buffer. 36 | char *buf; 37 | // Inode number. 38 | inode_t inode; 39 | // File type and protection. 40 | uint16_t mode; 41 | // Number of hard links. 42 | size_t links; 43 | // Whether the file handle is open. 44 | bool open; 45 | // Owner user ID. 46 | int uid; 47 | // Owner group ID. 48 | int gid; 49 | } fs_ramfs_inode_t; 50 | 51 | // RAMFS directory entry. 52 | typedef struct { 53 | // Entry size. 54 | size_t size; 55 | // Inode number. 56 | inode_t inode; 57 | // Name length. 58 | size_t name_len; 59 | // Filename. 60 | char name[VFS_RAMFS_NAME_MAX + 1]; 61 | } fs_ramfs_dirent_t; 62 | 63 | // RAM filesystem file / directory handle. 64 | // This handle is shared between multiple holders of the same file. 65 | typedef fs_ramfs_inode_t *fs_ramfs_file_t; 66 | 67 | // Mounted RAM filesystem. 68 | typedef struct { 69 | // RAM limit for the entire filesystem. 70 | size_t ram_limit; 71 | // RAM usage. 72 | atomic_size_t ram_usage; 73 | // Inode table, indices 0 and 1 are unused. 74 | fs_ramfs_inode_t *inode_list; 75 | // Inode table usage map. 76 | bool *inode_usage; 77 | // Number of allocated inodes. 78 | size_t inode_list_len; 79 | // THE RAMFS mutex. 80 | // Acquired shared for all read-only operations. 81 | // Acquired exclusive for any write operation. 82 | mutex_t mtx; 83 | } fs_ramfs_t; 84 | -------------------------------------------------------------------------------- /kernel/include/filesystem/syscall_impl.h: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #include "filesystem.h" 5 | #include "syscall.h" 6 | -------------------------------------------------------------------------------- /kernel/include/filesystem/vfs_fifo.h: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #pragma once 5 | 6 | #include "fifo.h" 7 | #include "filesystem/vfs_internal.h" 8 | #include "scheduler/waitlist.h" 9 | #include "spinlock.h" 10 | 11 | // VFS information used to manage FIFOs. 12 | typedef struct vfs_fifo_obj vfs_fifo_obj_t; 13 | 14 | // VFS information used to manage FIFOs. 15 | struct vfs_fifo_obj { 16 | // FIFO data storage. 17 | fifo_t *buffer; 18 | // Spinlock that guards the data storage. 19 | spinlock_t buffer_lock; 20 | // Number of readers. 21 | atomic_int read_count; 22 | // Number of writers. 23 | atomic_int write_count; 24 | // Threads blocked on read. 25 | waitlist_t read_blocked; 26 | // Threads blocked on write. 27 | waitlist_t write_blocked; 28 | }; 29 | 30 | // Create a FIFO object. 31 | vfs_fifo_obj_t *vfs_fifo_create(); 32 | // Destroy a FIFO object. 33 | void vfs_fifo_destroy(vfs_fifo_obj_t *fobj); 34 | 35 | // Handle a file open for a FIFO. 36 | void vfs_fifo_open(badge_err_t *ec, vfs_fifo_obj_t *fobj, bool nonblock, bool read, bool write); 37 | // Handle a file close for a FIFO. 38 | void vfs_fifo_close(vfs_fifo_obj_t *fobj, bool had_read, bool had_write); 39 | // Handle a file read for a FIFO. 40 | // WARNING: May sporadically return 0 in a blocking multi-read scenario. 41 | // Raises ECAUSE_PIPE_CLOSED if `enforce_open` is true and the write end is closed. 42 | fileoff_t vfs_fifo_read(badge_err_t *ec, vfs_fifo_obj_t *fobj, bool nonblock, uint8_t *readbuf, fileoff_t readlen); 43 | // Handle a file write for a FIFO. 44 | // Raises ECAUSE_PIPE_CLOSED if `enforce_open` is true and the read end is closed. 45 | fileoff_t vfs_fifo_write( 46 | badge_err_t *ec, vfs_fifo_obj_t *fobj, bool nonblock, bool enforce_open, uint8_t const *writebuf, fileoff_t writelen 47 | ); 48 | -------------------------------------------------------------------------------- /kernel/include/housekeeping.h: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #pragma once 5 | 6 | #include "time.h" 7 | 8 | typedef void (*hk_task_t)(int taskno, void *arg); 9 | 10 | // Initialize the housekeeping system. 11 | void hk_init(); 12 | 13 | // Add a one-time task with optional timestamp to the queue. 14 | // This task will be run in the "housekeeping" task. 15 | // Returns the task number. 16 | int hk_add_once(timestamp_us_t time, hk_task_t task, void *arg); 17 | // Add a repeating task with optional start timestamp to the queue. 18 | // This task will be run in the "housekeeping" task. 19 | // Returns the task number. 20 | int hk_add_repeated(timestamp_us_t time, timestamp_us_t interval, hk_task_t task, void *arg); 21 | // Cancel a housekeeping task. 22 | void hk_cancel(int taskno); 23 | 24 | // Variant of `hk_add_once` that does not use the mutex. 25 | // WARNING: Only use before the scheduler has started! 26 | int hk_add_once_presched(timestamp_us_t time, hk_task_t task, void *arg); 27 | // Variant of `hk_add_repeated` that does not use the mutex. 28 | // WARNING: Only use before the scheduler has started! 29 | int hk_add_repeated_presched(timestamp_us_t time, timestamp_us_t interval, hk_task_t task, void *arg); 30 | -------------------------------------------------------------------------------- /kernel/include/interrupt.h: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #pragma once 5 | 6 | #include "port/interrupt.h" 7 | 8 | typedef struct isr_ctx_t isr_ctx_t; 9 | // Interrupt service routine functions. 10 | typedef void (*isr_t)(int irq, void *cookie); 11 | // Installed ISR (opaque struct). 12 | typedef struct isr_entry isr_entry_t; 13 | // Reference to installed ISR. 14 | typedef isr_entry_t *isr_handle_t; 15 | 16 | // Initialise interrupt drivers for this CPU. 17 | void irq_init(isr_ctx_t *tmp_ctx); 18 | 19 | // Enable the IRQ. 20 | void irq_ch_enable(int irq); 21 | // Disable the IRQ. 22 | void irq_ch_disable(int irq); 23 | // Query whether the IRQ is enabled. 24 | bool irq_ch_is_enabled(int irq); 25 | // Add an ISR to a certain IRQ. 26 | isr_handle_t isr_install(int irq, isr_t isr_func, void *cookie); 27 | // Remove an ISR. 28 | void isr_remove(isr_handle_t handle); 29 | 30 | // Enable interrupts if a condition is met. 31 | static inline void irq_enable_if(bool enable); 32 | // Disable interrupts if a condition is met. 33 | static inline void irq_disable_if(bool disable); 34 | // Enable interrupts. 35 | static inline void irq_enable(); 36 | // Disable interrupts. 37 | // Returns whether interrupts were enabled. 38 | static inline bool irq_disable(); 39 | // Query whether interrupts are enabled on this CPU. 40 | static inline bool irq_is_enabled(); 41 | -------------------------------------------------------------------------------- /kernel/include/isr_ctx.h: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | typedef struct isr_ctx_t isr_ctx_t; 11 | 12 | // Get the current ISR context. 13 | static inline isr_ctx_t *isr_ctx_get(); 14 | // Get the outstanding context switch target, if any. 15 | static inline isr_ctx_t *isr_ctx_switch_get(); 16 | // Set the context switch target to switch to before exiting the trap/interrupt handler. 17 | static inline void isr_ctx_switch_set(isr_ctx_t *switch_to); 18 | 19 | // Print a register dump given isr_ctx_t. 20 | void isr_ctx_dump(isr_ctx_t const *ctx); 21 | // Print a register dump of the current registers. 22 | void kernel_cur_regs_dump(); 23 | 24 | #include "cpu/isr_ctx.h" 25 | 26 | // Function to run with `isr_noexc_run`. 27 | typedef void (*isr_noexc_t)(void *cookie); 28 | // Trap handler to run with `isr_noexc_run`. 29 | typedef void (*isr_catch_t)(void *cookie, cpu_regs_t *regs_ptr); 30 | // Run a restricted function and catch exceptions. 31 | // The code will run with interrupts disabled. 32 | // All traps will cause the optional `trap_handler` to be called and `code` to terminate early. 33 | // This should only be used for debug or ISA detection purposes. 34 | // Returns whether a trap occurred. 35 | bool isr_noexc_run(isr_noexc_t code, isr_catch_t trap_handler, void *cookie); 36 | 37 | // Try to copy memory from src to dest. 38 | // Returns whether an access trap occurred. 39 | bool isr_noexc_mem_copy(void *dest, void const *src, size_t len); 40 | // Try to copy uint8_t from src to dest. 41 | // Returns whether an access trap occurred. 42 | bool isr_noexc_copy_u8(uint8_t *dest, uint8_t const *src); 43 | // Try to copy uint16_t from src to dest. 44 | // Returns whether an access trap occurred. 45 | bool isr_noexc_copy_u16(uint16_t *dest, uint16_t const *src); 46 | // Try to copy uint32_t from src to dest. 47 | // Returns whether an access trap occurred. 48 | bool isr_noexc_copy_u32(uint32_t *dest, uint32_t const *src); 49 | // Try to copy uint64_t from src to dest. 50 | // Returns whether an access trap occurred. 51 | bool isr_noexc_copy_u64(uint64_t *dest, uint64_t const *src); 52 | 53 | // Try to copy size_t from src to dest. 54 | // Returns whether an access trap occurred. 55 | static inline bool isr_noexc_copy_size(size_t *dest, size_t const *src) { 56 | #if __SIZE_WIDTH__ == 64 57 | return isr_noexc_copy_u64(dest, src); 58 | #else 59 | return isr_noexc_copy_u32(dest, src); 60 | #endif 61 | } 62 | -------------------------------------------------------------------------------- /kernel/include/loading/kbelf_pre.h: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #pragma once 5 | 6 | #include "log.h" 7 | 8 | #define KBELF_DEBUG 9 | 10 | #ifndef KBELF_FMT_CSTR 11 | #define KBELF_FMT_CSTR "%{cs}" 12 | #endif 13 | 14 | #ifndef KBELF_FMT_DEC 15 | #define KBELF_FMT_DEC "%{d}" 16 | #endif 17 | 18 | #ifndef KBELF_FMT_SIZE 19 | #define KBELF_FMT_SIZE "%{size;d}" 20 | #endif 21 | 22 | #ifndef KBELF_FMT_BYTE 23 | #define KBELF_FMT_BYTE "%{u8;x}" 24 | #endif 25 | 26 | #ifndef KBELF_LOGGER 27 | #define KBELF_LOGGER(lv, fmt, ...) logkf(lv, fmt __VA_OPT__(, ) __VA_ARGS__) 28 | #endif 29 | 30 | #ifndef KBELF_LOG_LEVEL_D 31 | #define KBELF_LOG_LEVEL_D LOG_DEBUG 32 | #endif 33 | 34 | #ifndef KBELF_LOG_LEVEL_I 35 | #define KBELF_LOG_LEVEL_I LOG_INFO 36 | #endif 37 | 38 | #ifndef KBELF_LOG_LEVEL_W 39 | #define KBELF_LOG_LEVEL_W LOG_WARN 40 | #endif 41 | 42 | #ifndef KBELF_LOG_LEVEL_E 43 | #define KBELF_LOG_LEVEL_E LOG_ERROR 44 | #endif -------------------------------------------------------------------------------- /kernel/include/memprotect.h: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #define MEMPROTECT_FLAG_R 0x00000001 11 | #define MEMPROTECT_FLAG_W 0x00000002 12 | #define MEMPROTECT_FLAG_RW 0x00000003 13 | #define MEMPROTECT_FLAG_X 0x00000004 14 | #define MEMPROTECT_FLAG_RX 0x00000005 15 | #define MEMPROTECT_FLAG_WX 0x00000006 16 | #define MEMPROTECT_FLAG_RWX 0x00000007 17 | // Global mapping; visible in all address spaces. 18 | #define MEMPROTECT_FLAG_GLOBAL 0x00000010 19 | // Kernel mapping; accessible to kernel mode instead of user mode. 20 | #define MEMPROTECT_FLAG_KERNEL 0x00000020 21 | // I/O mapping; if possible, mark region as I/O. 22 | #define MEMPROTECT_FLAG_IO 0x00000040 23 | // Non-cachable; if possible, mark region as non-cacheable. 24 | #define MEMPROTECT_FLAG_NC 0x00000080 25 | 26 | #include "port/hardware_allocation.h" 27 | #include "port/memprotect.h" 28 | #include "process/types.h" 29 | 30 | 31 | #if MEMMAP_VMEM 32 | // For systems with VMEM: global MMU context. 33 | extern mpu_ctx_t mpu_global_ctx; 34 | // Virtual to physical lookup results. 35 | typedef struct { 36 | // Memory protection flags. 37 | uint32_t flags; 38 | // Physical address. 39 | size_t paddr; 40 | // Page base vaddr. 41 | size_t page_vaddr; 42 | // Page base paddr. 43 | size_t page_paddr; 44 | // Page size. 45 | size_t page_size; 46 | } virt2phys_t; 47 | // Lookup virtual address to physical address. 48 | virt2phys_t memprotect_virt2phys(mpu_ctx_t *ctx, size_t vaddr); 49 | // Allocare a kernel virtual address to a certain physical address. 50 | // Does not map anything to this location. 51 | size_t memprotect_alloc_vaddr(size_t len); 52 | // Free a virtual address range allocated with `memprotect_alloc_vaddr`. 53 | void memprotect_free_vaddr(size_t vaddr); 54 | #endif 55 | 56 | // Initialise memory protection driver. 57 | void memprotect_early_init(); 58 | // Initialise memory protection driver. 59 | void memprotect_postheap_init(); 60 | // Initialise memory protection driver. 61 | void memprotect_init(); 62 | // Create a memory protection context. 63 | void memprotect_create(mpu_ctx_t *ctx); 64 | // Clean up a memory protection context. 65 | void memprotect_destroy(mpu_ctx_t *ctx); 66 | // Add a memory protection region for user memory. 67 | bool memprotect_u(proc_memmap_t *new_mm, mpu_ctx_t *ctx, size_t vaddr, size_t paddr, size_t length, uint32_t flags); 68 | // Add a memory protection region for kernel memory. 69 | bool memprotect_k(size_t vaddr, size_t paddr, size_t length, uint32_t flags); 70 | // Commit pending memory protections, if any. 71 | void memprotect_commit(mpu_ctx_t *ctx); 72 | // Swap in memory protections for the current thread. 73 | void memprotect_swap_from_isr(); 74 | // Swap in memory protections for a given context. 75 | void memprotect_swap(mpu_ctx_t *ctx); 76 | -------------------------------------------------------------------------------- /kernel/include/page_alloc.h: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | 11 | 12 | // Allocate pages of physical memory. 13 | // Uses physical page numbers (paddr / MEMMAP_PAGE_SIZE). 14 | size_t phys_page_alloc(size_t page_count, bool for_user); 15 | // Returns how large a physical allocation actually is. 16 | // Uses physical page numbers (paddr / MEMMAP_PAGE_SIZE). 17 | size_t phys_page_size(size_t ppn); 18 | // Free pages of physical memory. 19 | // Uses physical page numbers (paddr / MEMMAP_PAGE_SIZE). 20 | void phys_page_free(size_t ppn); 21 | // Split a physical page allocation into two in the allocator. 22 | // Uses physical page numbers (paddr / MEMMAP_PAGE_SIZE). 23 | void phys_page_split(size_t ppn); 24 | -------------------------------------------------------------------------------- /kernel/include/process/process.h: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #pragma once 5 | 6 | #include "scheduler/scheduler.h" 7 | 8 | 9 | 10 | // Process is running or waiting for syscalls. 11 | #define PROC_RUNNING 0x00000001 12 | // Process is waiting for threads to exit. 13 | #define PROC_EXITING 0x00000002 14 | // Process has fully exited. 15 | #define PROC_EXITED 0x00000004 16 | // Process has signals pending. 17 | #define PROC_SIGPEND 0x00000008 18 | // Process is pre-start. 19 | #define PROC_PRESTART 0x00000010 20 | // Process has a state change not acknowledged. 21 | #define PROC_STATECHG 0x00000020 22 | 23 | 24 | 25 | // A process and all of its resources. 26 | typedef struct process_t process_t; 27 | // Globally unique process ID. 28 | typedef int pid_t; 29 | 30 | // Send a signal to all running processes in the system except the init process. 31 | void proc_signal_all(int signal); 32 | // Whether any non-init processes are currently running. 33 | bool proc_has_noninit(); 34 | 35 | // Create a new, empty process. 36 | pid_t proc_create(badge_err_t *ec, pid_t parent, char const *binary, int argc, char const *const *argv); 37 | // Delete a process only if it hasn't been started yet. 38 | bool proc_delete_prestart(pid_t pid); 39 | // Delete a process and release any resources it had. 40 | void proc_delete(pid_t pid); 41 | // Get the process' flags. 42 | uint32_t proc_getflags(badge_err_t *ec, pid_t pid); 43 | // Get the PID of the current process, if any. 44 | pid_t proc_current_pid(); 45 | // Load an executable and start a prepared process. 46 | void proc_start(badge_err_t *ec, pid_t pid); 47 | 48 | // Allocate more memory to a process. 49 | // Returns actual virtual address on success, 0 on failure. 50 | size_t proc_map(badge_err_t *ec, pid_t pid, size_t vaddr_req, size_t min_size, size_t min_align, int flags); 51 | // Release memory allocated to a process. 52 | // The given span should not fall outside an area mapped with `proc_map_raw`. 53 | void proc_unmap(badge_err_t *ec, pid_t pid, size_t vaddr, size_t len); 54 | // Whether the process owns this range of memory. 55 | // Returns the lowest common denominator of the access bits bitwise or 8. 56 | int proc_map_contains(badge_err_t *ec, pid_t pid, size_t base, size_t size); 57 | 58 | // Raise a signal to a process, which may be the current process. 59 | void proc_raise_signal(badge_err_t *ec, pid_t pid, int signum); 60 | 61 | // Check whether a process is a parent to another. 62 | bool proc_is_parent(pid_t parent, pid_t child); 63 | -------------------------------------------------------------------------------- /kernel/include/process/sighandler.h: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #pragma once 5 | 6 | #include "attributes.h" 7 | 8 | #include 9 | 10 | // Kernel side of the signal handler. 11 | // Called in the kernel side of a used thread when a signal might be queued. 12 | void proc_signal_handler() NORETURN; 13 | // Raises a segmentation fault to the current thread. 14 | // Called in the kernel side of a used thread when hardware detects a segmentation fault. 15 | void proc_sigsegv_handler(size_t vaddr) NORETURN; 16 | // Raises an illegal instruction fault to the current thread. 17 | // Called in the kernel side of a used thread when hardware detects an illegal instruction fault. 18 | void proc_sigill_handler() NORETURN; 19 | // Raises a trace/breakpoint trap to the current thread. 20 | // Called in the kernel side of a used thread when hardware detects a trace/breakpoint trap. 21 | void proc_sigtrap_handler() NORETURN; 22 | // Raises an invalid system call to the current thread. 23 | // Called in the kernel side of a used thread when a system call does not exist. 24 | void proc_sigsys_handler() NORETURN; 25 | -------------------------------------------------------------------------------- /kernel/include/process/syscall_impl.h: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #pragma once 5 | 6 | #include "syscall.h" 7 | 8 | // Sycall: Exit the process; exit code can be read by parent process. 9 | void syscall_self_exit(int code); 10 | -------------------------------------------------------------------------------- /kernel/include/scheduler/cpu.h: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #pragma once 5 | 6 | #include "attributes.h" 7 | #include "badge_err.h" 8 | #include "scheduler/scheduler.h" 9 | #include "scheduler/types.h" 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | 16 | // Requests the scheduler to prepare a switch from userland to kernel for a user thread. 17 | // If `syscall` is true, copies argument registers to the kernel thread. 18 | // The kernel thread will start at `entry_point`. 19 | void sched_raise_from_isr(sched_thread_t *thread, bool syscall, void *entry_point); 20 | 21 | // Requests the scheduler to prepare a switch from kernel to userland for a user thread. 22 | // Resumes the userland thread where it left off. 23 | void sched_lower_from_isr(); 24 | 25 | // Check whether the current thread is in a signal handler. 26 | // Returns signal number, or 0 if not in a signal handler. 27 | bool sched_is_sighandler(); 28 | 29 | // Enters a signal handler in the current thread. 30 | // Returns false if there isn't enough resources to do so. 31 | bool sched_signal_enter(size_t handler_vaddr, size_t return_vaddr, int signum); 32 | 33 | // Exits a signal handler in the current thread. 34 | // Returns false if the process cannot be resumed. 35 | bool sched_signal_exit(); 36 | 37 | // Prepares a context to be invoked as a kernel thread. 38 | void sched_prepare_kernel_entry(sched_thread_t *thread, void *entry_point, void *arg); 39 | 40 | // Prepares a pair of contexts to be invoked as a userland thread. 41 | // Kernel-side in these threads is always started by an ISR and the entry point is given at that time. 42 | void sched_prepare_user_entry(sched_thread_t *thread, size_t entry_point, size_t arg); 43 | 44 | // Run arch-specific task switch code before `isr_context_switch`. 45 | // Called after the scheduler decides what thread to switch to. 46 | void sched_arch_task_switch(sched_thread_t *next); 47 | -------------------------------------------------------------------------------- /kernel/include/scheduler/isr.h: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #pragma once 5 | 6 | #include "scheduler/scheduler.h" 7 | 8 | 9 | 10 | // Remove the current thread from the runqueue from this CPU. 11 | // Interrupts must be disabled. 12 | sched_thread_t *thread_dequeue_self(); 13 | 14 | // Requests the scheduler to prepare a switch from inside an interrupt routine. 15 | void sched_request_switch_from_isr(); 16 | 17 | // Get a new blocking ticket; to block the thread, run `thread_yield()`. 18 | uint64_t thread_block(); 19 | 20 | // Unblock a thread. 21 | bool thread_unblock(tid_t thread, uint64_t ticket); 22 | -------------------------------------------------------------------------------- /kernel/include/scheduler/waitlist.h: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #pragma once 5 | 6 | #include "list.h" 7 | #include "scheduler/scheduler.h" 8 | #include "spinlock.h" 9 | 10 | 11 | 12 | // Thread waitilig list entry. 13 | typedef struct { 14 | // Linked list node. 15 | dlist_node_t node; 16 | // Whether this entry is still in the list. 17 | bool in_list; 18 | // Thread to resume. 19 | tid_t thread; 20 | // Blocking ticket to resume. 21 | uint64_t ticket; 22 | } waitlist_ent_t; 23 | 24 | // Thread waiting list object. 25 | typedef struct { 26 | // Waiting list spinlock. 27 | spinlock_t lock; 28 | // List of blocked threads. 29 | dlist_t list; 30 | } waitlist_t; 31 | 32 | // Create a new (empty) waiting list. 33 | #define WAITLIST_T_INIT {SPINLOCK_T_INIT, DLIST_EMPTY} 34 | 35 | 36 | 37 | // Block on a waiting list, or until 38 | // Runs `double_check(cookie)` and unblocks if false to prevent race conditions. 39 | void waitlist_block(waitlist_t *list, timestamp_us_t timeout, bool (*double_check)(void *), void *cookie); 40 | // Try to resume a thread blocked on the waiting list. 41 | void waitlist_notify(waitlist_t *list); 42 | -------------------------------------------------------------------------------- /kernel/include/smp.h: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #pragma once 5 | 6 | #include "port/smp.h" 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | 13 | 14 | // Number of detected usable CPU cores. 15 | // Never changes after smp_init. 16 | extern int smp_count; 17 | 18 | // The the SMP CPU index of the calling CPU. 19 | int smp_cur_cpu(); 20 | // Get the SMP CPU index from the CPU ID value. 21 | int smp_get_cpu(size_t cpuid); 22 | // Get the CPU ID value from the SMP CPU index. 23 | size_t smp_get_cpuid(int cpu); 24 | // Power on another CPU. 25 | bool smp_poweron(int cpu, void *entrypoint, void *stack); 26 | // Power off this CPU. 27 | bool smp_poweroff(); 28 | // Pause this CPU, if supported. 29 | bool smp_pause(); 30 | // Resume another CPU, if supported. 31 | bool smp_resume(int cpu); 32 | // Whether a CPU can be powered off at runtime. 33 | bool smp_can_poweroff(int cpu); 34 | -------------------------------------------------------------------------------- /kernel/include/stdlib.h: -------------------------------------------------------------------------------- 1 | 2 | // Dummy stdlib.h 3 | 4 | #pragma once 5 | -------------------------------------------------------------------------------- /kernel/include/todo.h: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #pragma once 5 | 6 | #include "log.h" 7 | #include "panic.h" 8 | 9 | 10 | 11 | #define TODO() \ 12 | ({ \ 13 | logkf(LOG_FATAL, "TODO in function %{cs} at %{cs}:%{d}", __FUNCTION__, __FILE_NAME__, __LINE__); \ 14 | panic_abort(); \ 15 | __builtin_unreachable(); \ 16 | }) 17 | -------------------------------------------------------------------------------- /kernel/include/usercopy.h: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #pragma once 5 | 6 | #include "process/process.h" 7 | 8 | // Determine string length in memory a user owns. 9 | // Returns -1 if the user doesn't have access to any byte in the string. 10 | ptrdiff_t strlen_from_user_raw(process_t *process, size_t user_vaddr, ptrdiff_t max_len); 11 | 12 | // Copy bytes from user to kernel. 13 | // Returns whether the user has access to all of these bytes. 14 | // If the user doesn't have access, no copy is performed. 15 | bool copy_from_user_raw(process_t *process, void *kernel_vaddr, size_t user_vaddr, size_t len); 16 | 17 | // Copy from kernel to user. 18 | // Returns whether the user has access to all of these bytes. 19 | // If the user doesn't have access, no copy is performed. 20 | bool copy_to_user_raw(process_t *process, size_t user_vaddr, void *const kernel_vaddr, size_t len); 21 | 22 | // Determine string length in memory a user owns. 23 | // Returns -1 if the user doesn't have access to any byte in the string. 24 | ptrdiff_t strlen_from_user(pid_t pid, size_t user_vaddr, ptrdiff_t max_len); 25 | 26 | // Copy bytes from user to kernel. 27 | // Returns whether the user has access to all of these bytes. 28 | // If the user doesn't have access, no copy is performed. 29 | bool copy_from_user(pid_t pid, void *kernel_vaddr, size_t user_vaddr, size_t len); 30 | 31 | // Copy from kernel to user. 32 | // Returns whether the user has access to all of these bytes. 33 | // If the user doesn't have access, no copy is performed. 34 | bool copy_to_user(pid_t pid, size_t user_vaddr, void *const kernel_vaddr, size_t len); 35 | -------------------------------------------------------------------------------- /kernel/port/esp32c6/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | # SPDX-License-Identifier: MIT 3 | 4 | cmake_minimum_required(VERSION 3.10.0) 5 | 6 | set(cpu_riscv_enable_pmp true) 7 | 8 | set(port_src 9 | # ${CMAKE_CURRENT_LIST_DIR}/src/hal/gpio.c 10 | # ${CMAKE_CURRENT_LIST_DIR}/src/hal/i2c.c 11 | ${CMAKE_CURRENT_LIST_DIR}/src/hal/spi.c 12 | 13 | ${CMAKE_CURRENT_LIST_DIR}/src/clkconfig.c 14 | ${CMAKE_CURRENT_LIST_DIR}/src/interrupt.c 15 | ${CMAKE_CURRENT_LIST_DIR}/src/memprotect.c 16 | ${CMAKE_CURRENT_LIST_DIR}/src/pmu_init.c 17 | ${CMAKE_CURRENT_LIST_DIR}/src/port.c 18 | ${CMAKE_CURRENT_LIST_DIR}/src/smp.c 19 | ) 20 | set(port_include 21 | ${CMAKE_CURRENT_LIST_DIR}/include 22 | ) 23 | set(port_link 24 | -T${CMAKE_CURRENT_LIST_DIR}/linker.ld 25 | -L${CMAKE_CURRENT_LIST_DIR}/../esp_common/esp-idf/components/soc/esp32c6/ld/ 26 | ) 27 | set(port_flags 28 | -static -fno-pic 29 | ) 30 | 31 | include(port/esp_common/CMakeLists.txt) 32 | set_target_properties(${target} PROPERTIES LINK_DEPENDS ${CMAKE_CURRENT_LIST_DIR}/linker.ld) 33 | -------------------------------------------------------------------------------- /kernel/port/esp32c6/bootloader.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/badgeteam/BadgerOS/6eaf51d5336d5f7531431b39c83404a1c3202430/kernel/port/esp32c6/bootloader.bin -------------------------------------------------------------------------------- /kernel/port/esp32c6/gdbinit: -------------------------------------------------------------------------------- 1 | target extended-remote :3333 2 | set remote hardware-watchpoint-limit 2 3 | 4 | mem 0x20000000 0x30000000 rw 5 | mem 0x40000000 0x40050000 ro 6 | mem 0x40800000 0x40880000 rw 7 | mem 0x42000000 0x43000000 ro 8 | mem 0x50000000 0x50004000 rw 9 | mem 0x60000000 0x600d0000 rw 10 | 11 | define reset 12 | mon reset halt 13 | maintenance flush register-cache 14 | thb basic_runtime_init 15 | c 16 | end 17 | 18 | reset 19 | -------------------------------------------------------------------------------- /kernel/port/esp32c6/include/port/clkconfig.h: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | 9 | // Configure I2C0 clock. 10 | void clkconfig_i2c0(uint32_t freq_hz, bool enable, bool reset); 11 | // Configure SPI2 clock. 12 | void clkconfig_spi2(uint32_t freq_hz, bool enable, bool reset); 13 | // Configure UART0 clock. 14 | void clkconfig_uart0(uint32_t freq_hz, bool enable, bool reset); 15 | -------------------------------------------------------------------------------- /kernel/port/esp32c6/include/port/hardware_allocation.h: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #pragma once 5 | 6 | 7 | 8 | /* ==== Interrupts ==== */ 9 | 10 | // Kernel runs in M-mode instead of S-mode. 11 | #define RISCV_M_MODE_KERNEL 1 12 | // Interrupt channel used for timer alarms. 13 | #define INT_CHANNEL_TIMER_ALARM 16 14 | // Interrupt channel used for watchdog alarms. 15 | #define INT_CHANNEL_WATCHDOG_ALARM 17 16 | // Interrupt channel used for UART. 17 | #define INT_CHANNEL_UART 18 18 | // Interrupt channel used for I²C. 19 | #define INT_CHANNEL_I2C 19 20 | // Interrupt channel used for SPI. 21 | #define INT_CHANNEL_SPI 20 22 | 23 | // Default priority for timer alarm interrupts. 24 | #define INT_PRIO_TIMER_ALARM 11 25 | // Default priority for timer watchdog interrupts. 26 | #define INT_PRIO_WATCHDOG_ALARM 15 27 | // Interrupt channel used for UART interrupts. 28 | #define INT_PRIO_UART 14 29 | // Interrupt channel used for I²C interrupts. 30 | #define INT_PRIO_I2C 13 31 | // Interrupt channel used for SPI interrupts. 32 | #define INT_PRIO_SPI 12 33 | 34 | 35 | 36 | /* ==== Timers ==== */ 37 | 38 | // Number of usable hardware timers. 39 | #define TIMER_COUNT 4 40 | // Timer used for system timekeeping. 41 | #define TIMER_SYSTICK_NUM 0 42 | // System tick rate in Hz. 43 | #define TIMER_SYSTICK_RATE 1000000 44 | 45 | 46 | 47 | /* ==== Memory protection regions ==== */ 48 | 49 | // Region size used for nullpointer protection regions. 50 | #define PMP_SIZE_NULLPTR 0x10000000 51 | // Region size used for ROM write protection. 52 | #define PMP_SIZE_ROM_WP 0x00080000 53 | // Region size used for FLASH write protection. 54 | #define PMP_SIZE_FLASH_WP 0x01000000 55 | 56 | // Region base used for ROM write protection. 57 | #define PMP_BASE_ROM_WP 0x40000000 58 | // Region base used for ROM write protection. 59 | #define PMP_BASE_FLASH_WP 0x42000000 60 | 61 | // NAPOT PMP entry for lower nullpointer protection. 62 | #define PMP_ENTRY_NULLPTR_LOW_NAPOT 12 63 | // NAPOT PMP entry for upper nullpointer protection. 64 | #define PMP_ENTRY_NULLPTR_HIGH_NAPOT 13 65 | // NAPOT PMP entry for ROM write protection 66 | #define PMP_ENTRY_ROM_WP_NAPOT 14 67 | // NAPOT PMP entry for FLASH write protection 68 | #define PMP_ENTRY_FLASH_WP_NAPOT 15 69 | // NAPOT PMP entry for U-mode global permissions. 70 | #define PMP_ENTRY_USER_GLOBAL_NAPOT 11 71 | 72 | // Kernel supports virtual memory. 73 | #define MEMMAP_VMEM 0 74 | // Page size for memory protections. 75 | #define MEMMAP_PAGE_SIZE 4096 76 | // Maximum number of mapped regions per process. 77 | #define PROC_MEMMAP_MAX_REGIONS 8 78 | // Lowest numbered PMP to use for process memory maps, must be a multiple of 4. 79 | #define PROC_RISCV_PMP_START 0 80 | // Number of PMPs to use for process memory maps, must be a multiple of 4. 81 | #define PROC_RISCV_PMP_COUNT 8 82 | -------------------------------------------------------------------------------- /kernel/port/esp32c6/include/port/interrupt.h: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #pragma once 5 | 6 | #include "cpu/interrupt.h" 7 | 8 | #include 9 | 10 | #define EXT_IRQ_COUNT ETS_MAX_INTR_SOURCE 11 | 12 | // Set the external interrupt signal for CPU timer IRQs. 13 | void set_cpu_timer_irq(int cpu, int timer_irq); 14 | -------------------------------------------------------------------------------- /kernel/port/esp32c6/include/port/memprotect.h: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #pragma once 5 | 6 | #include "cpu/riscv_pmp.h" 7 | 8 | typedef riscv_pmp_ctx_t mpu_ctx_t; 9 | -------------------------------------------------------------------------------- /kernel/port/esp32c6/include/port/pmu_init.h: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #pragma once 5 | 6 | // Initialise the power management unit. 7 | void pmu_init(); 8 | -------------------------------------------------------------------------------- /kernel/port/esp32c6/include/port/port.h: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | // Early hardware initialization. 11 | void port_early_init(); 12 | // Post-heap hardware initialization. 13 | void port_postheap_init(); 14 | // Full hardware initialization. 15 | void port_init(); 16 | // Power off. 17 | void port_poweroff(bool restart); 18 | // Send a single character to the log output. 19 | void port_putc(char msg); 20 | 21 | // Fence data and instruction memory for executable mapping. 22 | static inline void port_fencei() { 23 | asm("fence rw,rw"); 24 | asm("fence.i"); 25 | } 26 | -------------------------------------------------------------------------------- /kernel/port/esp32c6/include/sdkconfig.h: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #define CONFIG_IDF_TARGET_ESP32C6 1 5 | -------------------------------------------------------------------------------- /kernel/port/esp32c6/include/soc/io_mux_struct.h: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #pragma once 5 | 6 | #include 7 | 8 | // I2S output clock selection. 9 | typedef union { 10 | struct { 11 | uint32_t out1 : 5; 12 | uint32_t out2 : 5; 13 | uint32_t out3 : 5; 14 | uint32_t : 17; 15 | }; 16 | uint32_t val; 17 | } io_mux_clk_t; 18 | 19 | typedef union { 20 | struct { 21 | uint32_t mcu_oe : 1; 22 | uint32_t slp_sel : 1; 23 | uint32_t mcu_wpd : 1; 24 | uint32_t mcu_wpu : 1; 25 | uint32_t mcu_ie : 1; 26 | uint32_t mcu_drv : 2; 27 | uint32_t fun_wpd : 1; 28 | uint32_t fun_wpu : 1; 29 | uint32_t fun_ie : 1; 30 | uint32_t fun_drv : 2; 31 | uint32_t mcu_sel : 3; 32 | uint32_t filter_en : 1; 33 | }; 34 | uint32_t val; 35 | } io_mux_gpio_t; 36 | 37 | typedef struct io_mux_t { 38 | io_mux_clk_t volatile clk; 39 | io_mux_gpio_t volatile gpio[31]; 40 | } io_mux_t; 41 | 42 | extern io_mux_t IO_MUX; 43 | -------------------------------------------------------------------------------- /kernel/port/esp32c6/include/soc/plic_struct.h: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #pragma once 5 | 6 | #include "soc/soc.h" 7 | 8 | typedef struct { 9 | // Interrupt enable. 10 | uint32_t volatile int_en; 11 | // Interrupt type. 12 | uint32_t volatile int_type; 13 | // Interrupt clear. 14 | uint32_t volatile int_clear; 15 | // EMIP status register. 16 | uint32_t volatile int_pending; 17 | 18 | // Interrupt priorities. 19 | uint32_t volatile int_pri[32]; 20 | } esp_plic_t; 21 | 22 | extern esp_plic_t PLIC_MX; 23 | extern esp_plic_t PLIC_UX; 24 | -------------------------------------------------------------------------------- /kernel/port/esp32c6/include/soc/soc_types.h: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #pragma once 5 | 6 | typedef enum { 7 | PMU_HP_MODE_ACTIVE, 8 | PMU_HP_MODE_MODEM, 9 | PMU_HP_MODE_SLEEP, 10 | } pmu_hp_mode_t; 11 | 12 | typedef enum { 13 | PMU_HP_SYSCLK_XTAL, 14 | PMU_HP_SYSCLK_PLL, 15 | PMU_HP_SYSCLK_FOSC, 16 | } pmu_hp_sysclk_src_t; 17 | 18 | typedef enum { 19 | PMU_HP_ICG_MODEM_CODE_SLEEP, 20 | PMU_HP_ICG_MODEM_CODE_MODEM, 21 | PMU_HP_ICG_MODEM_CODE_ACTIVE, 22 | } pmu_hp_icg_modem_mode_t; 23 | 24 | typedef enum { 25 | PMU_HP_PD_TOP, 26 | PMU_HP_PD_HP_AON, 27 | PMU_HP_PD_CPU, 28 | PMU_HP_PD_RESERVED, 29 | PMU_HP_PD_WIFI, 30 | } pmu_hp_power_domain_t; 31 | 32 | #define HP_CALI_DBIAS 25 33 | 34 | #define PMU_HP_RETENTION_REGDMA_CONFIG(dir, entry) ((((dir) << 2) | (entry & 3)) & 7) 35 | -------------------------------------------------------------------------------- /kernel/port/esp32c6/partition-table.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/badgeteam/BadgerOS/6eaf51d5336d5f7531431b39c83404a1c3202430/kernel/port/esp32c6/partition-table.bin -------------------------------------------------------------------------------- /kernel/port/esp32c6/port.mk: -------------------------------------------------------------------------------- 1 | 2 | include port/esp_common/port.mk 3 | 4 | PORT ?= /dev/ttyACM0 5 | 6 | .PHONY: openocd 7 | openocd: 8 | openocd -c 'set ESP_RTOS "none"' -f board/esp32c6-builtin.cfg 9 | 10 | .PHONY: flash 11 | flash: build 12 | ../.venv/bin/python -m esptool -b 921600 --port "$(PORT)" \ 13 | write_flash --flash_mode dio --flash_freq 80m --flash_size 2MB \ 14 | 0x0 port/esp32c6/bootloader.bin \ 15 | 0x10000 "$(OUTPUT)/badger-os.bin" \ 16 | 0x8000 port/esp32c6/partition-table.bin 17 | 18 | .PHONY: monitor 19 | monitor: 20 | echo -e "\033[1mType ^A^X to exit.\033[0m" 21 | picocom -q -b 115200 '$(PORT)' \ 22 | | ../tools/address-filter.py -A $(CROSS_COMPILE)addr2line '$(OUTPUT)/badger-os.elf'; echo -e '\033[0m' 23 | -------------------------------------------------------------------------------- /kernel/port/esp32c6/src/smp.c: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #include "smp.h" 5 | 6 | 7 | 8 | // Number of detected usable CPU cores. 9 | // Never changes after smp_init. 10 | int smp_count = 1; 11 | 12 | // The the SMP CPU index of the calling CPU. 13 | int smp_cur_cpu() { 14 | return 0; 15 | } 16 | 17 | // Get the SMP CPU index from the CPU ID value. 18 | int smp_get_cpu(size_t cpuid) { 19 | (void)cpuid; 20 | return 0; 21 | } 22 | 23 | // Get the CPU ID value from the SMP CPU index. 24 | size_t smp_get_cpuid(int cpu) { 25 | (void)cpu; 26 | return 0; 27 | } 28 | 29 | // Power on another CPU. 30 | bool smp_poweron(int cpu, void *entrypoint, void *stack) { 31 | (void)cpu; 32 | (void)entrypoint; 33 | (void)stack; 34 | return false; 35 | } 36 | 37 | // Power off this CPU. 38 | bool smp_poweroff() { 39 | return false; 40 | } 41 | 42 | // Pause this CPU, if supported. 43 | bool smp_pause() { 44 | return false; 45 | } 46 | 47 | // Resume another CPU, if supported. 48 | bool smp_resume(int cpu) { 49 | (void)cpu; 50 | return false; 51 | } 52 | 53 | // Whether a CPU can be powered off at runtime. 54 | bool smp_can_poweroff(int cpu) { 55 | (void)cpu; 56 | return false; 57 | } 58 | -------------------------------------------------------------------------------- /kernel/port/esp32p4/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | # SPDX-License-Identifier: MIT 3 | 4 | cmake_minimum_required(VERSION 3.10.0) 5 | 6 | set(cpu_riscv_enable_pmp true) 7 | set(cpu_multicore true) 8 | 9 | set(port_src 10 | ${CMAKE_CURRENT_LIST_DIR}/src/interrupt.c 11 | ${CMAKE_CURRENT_LIST_DIR}/src/memprotect.c 12 | ${CMAKE_CURRENT_LIST_DIR}/src/pmu_init.c 13 | ${CMAKE_CURRENT_LIST_DIR}/src/port.c 14 | ${CMAKE_CURRENT_LIST_DIR}/src/smp.c 15 | ) 16 | set(port_include 17 | ${CMAKE_CURRENT_LIST_DIR}/include 18 | ) 19 | set(port_link 20 | -T${CMAKE_CURRENT_LIST_DIR}/linker.ld 21 | -L${CMAKE_CURRENT_LIST_DIR}/../esp_common/esp-idf/components/soc/esp32p4/ld/ 22 | -L${CMAKE_CURRENT_LIST_DIR}/../esp_common/esp-idf/components/esp_rom/esp32p4/ld/ 23 | ) 24 | set(port_flags 25 | -static -fno-pic 26 | ) 27 | 28 | include(port/esp_common/CMakeLists.txt) 29 | set_target_properties(${target} PROPERTIES LINK_DEPENDS ${CMAKE_CURRENT_LIST_DIR}/linker.ld) 30 | -------------------------------------------------------------------------------- /kernel/port/esp32p4/bootloader.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/badgeteam/BadgerOS/6eaf51d5336d5f7531431b39c83404a1c3202430/kernel/port/esp32p4/bootloader.bin -------------------------------------------------------------------------------- /kernel/port/esp32p4/include/port/hardware.h: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #pragma once 5 | 6 | 7 | 8 | /* ==== CPU INFO ==== */ 9 | 10 | // Kernel runs in M-mode instead of S-mode. 11 | #define RISCV_M_MODE_KERNEL 1 12 | // Number of PMP regions supported by the CPU. 13 | #define RISCV_PMP_REGION_COUNT 16 14 | // Enable use of CLIC interrupt controller. 15 | #define RISCV_USE_CLIC 16 | // Number of CLIC interrupt priority levels. 17 | #define RISCV_CLIC_MAX_PRIO 7 18 | // MTVT CSR index. 19 | #define CSR_MTVT 0x307 20 | // MTVT CSR name. 21 | #define CSR_MTVT_STR "0x307" 22 | // MINTSTATUS CSR index. 23 | // As per CLIC specs, mintstatus CSR should be at 0xFB1, however esp32p4 implements it at 0x346 24 | #define CSR_MINTSTATUS 0x346 25 | // MINTSTATUS CSR name. 26 | #define CSR_MINTSTATUS_STR "0x346" 27 | 28 | // Number of interrupt channels (excluding trap handler) in the vector table. 29 | #define RISCV_VT_INT_COUNT 47 30 | // Number of padding words in the vector table. 31 | #define RISCV_VT_PADDING 16 32 | // Bitmask for interrupt cause. 33 | #define RISCV_VT_ICAUSE_MASK 63 34 | // Bitmask for trap cause. 35 | #define RISCV_VT_TCAUSE_MASK 31 36 | -------------------------------------------------------------------------------- /kernel/port/esp32p4/include/port/hardware_allocation.h: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #pragma once 5 | 6 | 7 | 8 | /* ==== Interrupts ==== */ 9 | 10 | // Default priority for timer alarm interrupts. 11 | #define INT_PRIO_TIMER_ALARM 11 12 | // Default priority for timer watchdog interrupts. 13 | #define INT_PRIO_WATCHDOG_ALARM 15 14 | // Interrupt channel used for UART interrupts. 15 | #define INT_PRIO_UART 14 16 | // Interrupt channel used for I²C interrupts. 17 | #define INT_PRIO_I2C 13 18 | // Interrupt channel used for SPI interrupts. 19 | #define INT_PRIO_SPI 12 20 | 21 | 22 | 23 | /* ==== Timers ==== */ 24 | 25 | // Number of usable hardware timers. 26 | #define TIMER_COUNT 2 27 | // Timer used for system timekeeping. 28 | #define TIMER_SYSTICK_NUM 0 29 | // System tick rate in Hz. 30 | #define TIMER_SYSTICK_RATE 1000000 31 | 32 | 33 | 34 | /* ==== Memory protection regions ==== */ 35 | 36 | // Region size used for nullpointer protection regions. 37 | #define PMP_SIZE_NULLPTR 0x10000000 38 | // Region size used for ROM write protection. 39 | #define PMP_SIZE_ROM_WP 0x00020000 40 | // Region size used for FLASH write protection. 41 | #define PMP_SIZE_FLASH_WP 0x04000000 42 | 43 | // Region base used for ROM write protection. 44 | #define PMP_BASE_ROM_WP 0x4FC00000 45 | // Region base used for ROM write protection. 46 | #define PMP_BASE_FLASH_WP 0x40000000 47 | 48 | // NAPOT PMP entry for lower nullpointer protection. 49 | #define PMP_ENTRY_NULLPTR_LOW_NAPOT 12 50 | // NAPOT PMP entry for upper nullpointer protection. 51 | #define PMP_ENTRY_NULLPTR_HIGH_NAPOT 13 52 | // NAPOT PMP entry for ROM write protection 53 | #define PMP_ENTRY_ROM_WP_NAPOT 14 54 | // NAPOT PMP entry for FLASH write protection 55 | #define PMP_ENTRY_FLASH_WP_NAPOT 15 56 | // NAPOT PMP entry for U-mode global permissions. 57 | #define PMP_ENTRY_USER_GLOBAL_NAPOT 11 58 | 59 | // Kernel supports virtual memory. 60 | #define MEMMAP_VMEM 0 61 | // Page size for memory protections. 62 | #define MEMMAP_PAGE_SIZE 4096 63 | // Maximum number of mapped regions per process. 64 | #define PROC_MEMMAP_MAX_REGIONS 8 65 | // Lowest numbered PMP to use for process memory maps, must be a multiple of 4. 66 | #define PROC_RISCV_PMP_START 0 67 | // Number of PMPs to use for process memory maps, must be a multiple of 4. 68 | #define PROC_RISCV_PMP_COUNT 8 69 | -------------------------------------------------------------------------------- /kernel/port/esp32p4/include/port/interrupt.h: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #pragma once 5 | 6 | #include "cpu/interrupt.h" 7 | #include "soc/interrupts.h" 8 | 9 | #define EXT_IRQ_COUNT ETS_MAX_INTR_SOURCE 10 | 11 | // Set the external interrupt signal for CPU timer IRQs. 12 | void set_cpu_timer_irq(int cpu, int timer_irq); 13 | -------------------------------------------------------------------------------- /kernel/port/esp32p4/include/port/memprotect.h: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #pragma once 5 | 6 | #include "cpu/riscv_pmp.h" 7 | 8 | typedef riscv_pmp_ctx_t mpu_ctx_t; 9 | -------------------------------------------------------------------------------- /kernel/port/esp32p4/include/port/pmu_init.h: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #pragma once 5 | 6 | // Initialise the power management unit. 7 | void pmu_init(); 8 | -------------------------------------------------------------------------------- /kernel/port/esp32p4/include/port/port.h: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | // Early hardware initialization. 11 | void port_early_init(); 12 | // Post-heap hardware initialization. 13 | void port_postheap_init(); 14 | // Full hardware initialization. 15 | void port_init(); 16 | // Power off. 17 | void port_poweroff(bool restart); 18 | // Send a single character to the log output. 19 | void port_putc(char msg); 20 | // Fence data and instruction memory for executable mapping. 21 | void port_fencei(); 22 | -------------------------------------------------------------------------------- /kernel/port/esp32p4/include/sdkconfig.h: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #define CONFIG_IDF_TARGET_ESP32P4 1 5 | -------------------------------------------------------------------------------- /kernel/port/esp32p4/include/soc/clic_struct.h: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #pragma once 5 | 6 | typedef union { 7 | struct { 8 | // Whether `Smclicshv` is supported. 9 | uint32_t nvbits : 1; 10 | // Number of bits used to represent interrupt priority. 11 | uint32_t nlbits : 4; 12 | // Number of privilege modes supported. 13 | // When 0, only M-mode has interrupts. 14 | uint32_t nmbits : 2; 15 | uint32_t : 25; 16 | }; 17 | // Packed value. 18 | uint32_t val; 19 | } clic_dev_int_config_reg_t; 20 | 21 | typedef union { 22 | struct { 23 | // Actual number of interrupt channels. 24 | uint32_t num_int : 13; 25 | // CLIC hardware version. 26 | uint32_t version : 8; 27 | // Number of bits used to represent interrupt priority. 28 | uint32_t ctlbits : 4; 29 | uint32_t : 7; 30 | }; 31 | // Packed value. 32 | uint32_t val; 33 | } clic_dev_int_info_reg_t; 34 | 35 | typedef union { 36 | struct { 37 | uint32_t thresh : 8; 38 | uint32_t : 24; 39 | }; 40 | // Packed value. 41 | uint32_t val; 42 | } clic_dev_int_thresh_reg_t; 43 | 44 | typedef struct { 45 | clic_dev_int_config_reg_t volatile int_config; 46 | clic_dev_int_info_reg_t volatile int_info; 47 | clic_dev_int_thresh_reg_t volatile int_thresh; 48 | } clic_dev_t; 49 | 50 | 51 | 52 | typedef union { 53 | struct { 54 | // Interrupt is pending. 55 | uint32_t pending : 1; 56 | uint32_t : 7; 57 | // Interrupt is enabled. 58 | uint32_t enable : 1; 59 | uint32_t : 7; 60 | // Use interrupt vectoring for this IRQ. 61 | uint32_t attr_shv : 1; 62 | // Whether the interrupt is edge-triggered. 63 | uint32_t attr_trig : 1; 64 | uint32_t : 4; 65 | // Which privilege mode receives this interrupt. 66 | uint32_t attr_mode : 2; 67 | // Interrupt priority. 68 | uint32_t ctl : 8; 69 | }; 70 | // Packed value. 71 | uint32_t val; 72 | } clic_int_ctl_reg_t; 73 | 74 | typedef union { 75 | clic_int_ctl_reg_t volatile irq_ctl[48]; 76 | } clic_ctl_dev_t; 77 | -------------------------------------------------------------------------------- /kernel/port/esp32p4/include/soc/soc_types.h: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #pragma once 5 | 6 | typedef enum { 7 | PMU_HP_MODE_ACTIVE, 8 | PMU_HP_MODE_MODEM, 9 | PMU_HP_MODE_SLEEP, 10 | } pmu_hp_mode_t; 11 | -------------------------------------------------------------------------------- /kernel/port/esp32p4/partition-table.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/badgeteam/BadgerOS/6eaf51d5336d5f7531431b39c83404a1c3202430/kernel/port/esp32p4/partition-table.bin -------------------------------------------------------------------------------- /kernel/port/esp32p4/port.mk: -------------------------------------------------------------------------------- 1 | 2 | include port/esp_common/port.mk 3 | 4 | PORT ?= /dev/ttyACM0 5 | 6 | .PHONY: openocd 7 | openocd: 8 | openocd -c 'set ESP_RTOS "none"' -f board/esp32p4-builtin.cfg 9 | 10 | .PHONY: flash 11 | flash: build 12 | ../.venv/bin/python -m esptool -b 921600 --port "$(PORT)" --no-stub \ 13 | write_flash --flash_mode dio --flash_freq 80m --flash_size 2MB \ 14 | 0x2000 port/esp32p4/bootloader.bin \ 15 | 0x10000 "$(OUTPUT)/badger-os.bin" \ 16 | 0x8000 port/esp32p4/partition-table.bin 17 | 18 | .PHONY: monitor 19 | monitor: 20 | echo -e "\033[1mType ^A^X to exit.\033[0m" 21 | picocom -q -b 115200 '$(PORT)' \ 22 | | ../tools/address-filter.py -A $(CROSS_COMPILE)addr2line '$(OUTPUT)/badger-os.elf'; echo -e '\033[0m' 23 | -------------------------------------------------------------------------------- /kernel/port/esp_common/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | # SPDX-License-Identifier: MIT 3 | 4 | cmake_minimum_required(VERSION 3.10.0) 5 | 6 | set(port_src ${port_src} 7 | ${CMAKE_CURRENT_LIST_DIR}/src/gpio.c 8 | ${CMAKE_CURRENT_LIST_DIR}/src/i2c.c 9 | ${CMAKE_CURRENT_LIST_DIR}/src/hwtimer.c 10 | ${CMAKE_CURRENT_LIST_DIR}/src/time.c 11 | ) 12 | set(port_include ${port_include} 13 | ${CMAKE_CURRENT_LIST_DIR}/include/ 14 | ) 15 | set(port_system_include ${port_system_include} 16 | ${CMAKE_CURRENT_LIST_DIR}/esp-idf/components/soc/${CONFIG_TARGET}/include/ 17 | ${CMAKE_CURRENT_LIST_DIR}/esp-idf/components/soc/include/ 18 | ${CMAKE_CURRENT_LIST_DIR}/esp-idf/components/hal/${CONFIG_TARGET}/include/ 19 | ${CMAKE_CURRENT_LIST_DIR}/esp-idf/components/hal/include/ 20 | ${CMAKE_CURRENT_LIST_DIR}/esp-idf/components/esp_rom/${CONFIG_TARGET}/include/ 21 | ${CMAKE_CURRENT_LIST_DIR}/esp-idf/components/esp_rom/${CONFIG_TARGET}/include/${CONFIG_TARGET}/ 22 | ${CMAKE_CURRENT_LIST_DIR}/esp-idf/components/esp_rom/include/ 23 | ) 24 | set(port_link ${port_link} 25 | -L${CMAKE_CURRENT_LIST_DIR}/esp-idf/components/soc/${CONFIG_TARGET}/ld/ 26 | -L${CMAKE_CURRENT_LIST_DIR}/esp-idf/components/esp_rom/${CONFIG_TARGET}/ld/ 27 | ) 28 | set(create_esp32_bin true) 29 | -------------------------------------------------------------------------------- /kernel/port/esp_common/include/esp_assert.h: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | // Port of esp_assert.h 4 | 5 | #pragma once 6 | 7 | #include "assertions.h" 8 | 9 | #define assert assert_dev_drop 10 | 11 | #define ESP_STATIC_ASSERT(...) _Static_assert(__VA_ARGS__) 12 | -------------------------------------------------------------------------------- /kernel/port/esp_common/include/esp_attr.h: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | // Port of esp_attr.h 4 | 5 | #pragma once 6 | 7 | #define IRAM_ATTR 8 | #define FORCE_INLINE_ATTR static inline 9 | -------------------------------------------------------------------------------- /kernel/port/esp_common/include/esp_bit_defs.h: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | // Port of esp_bit_defs.h 4 | 5 | #pragma once 6 | 7 | #define BIT0 0x1 8 | #define BIT1 0x2 9 | #define BIT2 0x4 10 | #define BIT3 0x8 11 | #define BIT4 0x10 12 | #define BIT5 0x20 13 | #define BIT6 0x40 14 | #define BIT7 0x80 15 | #define BIT8 0x100 16 | #define BIT9 0x200 17 | #define BIT10 0x400 18 | #define BIT11 0x800 19 | #define BIT12 0x1000 20 | #define BIT13 0x2000 21 | #define BIT14 0x4000 22 | #define BIT15 0x8000 23 | #define BIT16 0x10000 24 | #define BIT17 0x20000 25 | #define BIT18 0x40000 26 | #define BIT19 0x80000 27 | #define BIT20 0x100000 28 | #define BIT21 0x200000 29 | #define BIT22 0x400000 30 | #define BIT23 0x800000 31 | #define BIT24 0x1000000 32 | #define BIT25 0x2000000 33 | #define BIT26 0x4000000 34 | #define BIT27 0x8000000 35 | #define BIT28 0x10000000 36 | #define BIT29 0x20000000 37 | #define BIT30 0x40000000 38 | #define BIT31 0x80000000 39 | #define BIT32 0x100000000 40 | #define BIT33 0x200000000 41 | #define BIT34 0x400000000 42 | #define BIT35 0x800000000 43 | #define BIT36 0x1000000000 44 | #define BIT37 0x2000000000 45 | #define BIT38 0x4000000000 46 | #define BIT39 0x8000000000 47 | #define BIT40 0x10000000000 48 | #define BIT41 0x20000000000 49 | #define BIT42 0x40000000000 50 | #define BIT43 0x80000000000 51 | #define BIT44 0x100000000000 52 | #define BIT45 0x200000000000 53 | #define BIT46 0x400000000000 54 | #define BIT47 0x800000000000 55 | #define BIT48 0x1000000000000 56 | #define BIT49 0x2000000000000 57 | #define BIT50 0x4000000000000 58 | #define BIT51 0x8000000000000 59 | #define BIT52 0x10000000000000 60 | #define BIT53 0x20000000000000 61 | #define BIT54 0x40000000000000 62 | #define BIT55 0x80000000000000 63 | #define BIT56 0x100000000000000 64 | #define BIT57 0x200000000000000 65 | #define BIT58 0x400000000000000 66 | #define BIT59 0x800000000000000 67 | #define BIT60 0x1000000000000000 68 | #define BIT61 0x2000000000000000 69 | #define BIT62 0x4000000000000000 70 | #define BIT63 0x8000000000000000 71 | 72 | #define BIT(x) (1UL << (x)) 73 | #define BIT64(x) (1ULL << (x)) 74 | -------------------------------------------------------------------------------- /kernel/port/esp_common/include/esp_intmtx.h: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #pragma once 5 | 6 | #include "soc/interrupts.h" 7 | 8 | #include 9 | 10 | // Interrupt map register. 11 | typedef union { 12 | struct { 13 | // Mapped interrupt signal. 14 | uint32_t map : 6; 15 | // Reserved. 16 | uint32_t : 26; 17 | }; 18 | // Packed value. 19 | uint32_t val; 20 | } intmtx_map_t; 21 | 22 | // Clock gate register. 23 | typedef union { 24 | struct { 25 | // Clock enabled. 26 | uint32_t clk_en : 1; 27 | // Reserved. 28 | uint32_t : 31; 29 | }; 30 | // Packed value. 31 | uint32_t val; 32 | } intmtx_clk_t; 33 | 34 | // Interrupt matrix. 35 | typedef struct { 36 | // Interrupt map. 37 | intmtx_map_t volatile map[ETS_MAX_INTR_SOURCE]; 38 | // Interrupts pending. 39 | uint32_t volatile pending[(ETS_MAX_INTR_SOURCE + 31) / 32]; 40 | // Clock gate register. 41 | intmtx_clk_t volatile clock; 42 | } intmtx_t; -------------------------------------------------------------------------------- /kernel/port/esp_common/include/esp_rom_sys.h: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | // Port of esp_rom_sys.h 4 | 5 | #pragma once 6 | -------------------------------------------------------------------------------- /kernel/port/esp_common/include/hal/assert.h: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | // Port of hal/assert.h 4 | 5 | #pragma once 6 | 7 | #include "assertions.h" 8 | #include "panic.h" 9 | 10 | #define HAL_ASSERT(__e) assert_dev_drop(__e) 11 | extern void abort(); 12 | -------------------------------------------------------------------------------- /kernel/port/esp_common/include/hal/log.h: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | // Port of hal/log.h 4 | 5 | #pragma once 6 | -------------------------------------------------------------------------------- /kernel/port/esp_common/include/port/smp.h: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #pragma once 5 | -------------------------------------------------------------------------------- /kernel/port/esp_common/include/port/time.h: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #pragma once 5 | 6 | // Initialise timer and watchdog subsystem. 7 | void time_init_dtb(); 8 | -------------------------------------------------------------------------------- /kernel/port/esp_common/include/time.h: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #pragma once 5 | 6 | // Initialise timer and watchdog subsystem. 7 | void time_init(); 8 | -------------------------------------------------------------------------------- /kernel/port/esp_common/port.mk: -------------------------------------------------------------------------------- 1 | 2 | .PHONY: _port_on_config 3 | _port_on_config: 4 | python -m venv ../.venv 5 | ../.venv/bin/pip install esptool 6 | git submodule update --init port/esp_common/esp-idf 7 | -------------------------------------------------------------------------------- /kernel/port/esp_common/src/time.c: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #include "time.h" 5 | 6 | #include "arrays.h" 7 | #include "assertions.h" 8 | #include "config.h" 9 | #include "hwtimer.h" 10 | #include "interrupt.h" 11 | #include "mutex.h" 12 | #include "scheduler/isr.h" 13 | #include "smp.h" 14 | #include "time_private.h" 15 | 16 | #ifdef CONFIG_TARGET_esp32p4 17 | // Timer used for task list items. 18 | #define TIMER_COUNT 2 19 | #else 20 | // Timer used for task list items. 21 | #define TIMER_COUNT 1 22 | #endif 23 | 24 | 25 | 26 | // Callback to the timer driver for when a timer alarm fires. 27 | void timer_isr_timer_alarm() { 28 | int cpu = smp_cur_cpu(); 29 | timer_alarm_disable(cpu); 30 | timer_int_clear(cpu); 31 | time_cpu_timer_isr(); 32 | } 33 | 34 | 35 | 36 | // Set the CPU's timer to a certain timestamp. 37 | void time_set_cpu_timer(timestamp_us_t timestamp) { 38 | timer_alarm_config(smp_cur_cpu(), timestamp, false); 39 | } 40 | 41 | // Clear the CPU's timer. 42 | void time_clear_cpu_timer() { 43 | int cpu = smp_cur_cpu(); 44 | timer_alarm_disable(cpu); 45 | timer_int_clear(cpu); 46 | } 47 | 48 | 49 | 50 | // Initialise timer and watchdog subsystem. 51 | void time_init() { 52 | timer_init(); 53 | 54 | // Configure system timers. 55 | for (int i = 0; i <= TIMER_COUNT; i++) { 56 | timer_stop(i); 57 | timer_value_set(i, 0); 58 | timer_alarm_disable(i); 59 | timer_int_clear(i); 60 | timer_int_enable(i, true); 61 | timer_set_freq(i, 1000000); 62 | } 63 | 64 | // Assign timer IRQs. 65 | for (int i = 0; i < TIMER_COUNT; i++) { 66 | set_cpu_timer_irq(i, timer_get_irq(i)); 67 | } 68 | 69 | // Start timers at close to the same time. 70 | for (int i = 0; i <= TIMER_COUNT; i++) { 71 | timer_start(i); 72 | } 73 | 74 | time_init_generic(); 75 | } 76 | 77 | // Get current time in microseconds. 78 | timestamp_us_t time_us() { 79 | return timer_value_get(smp_cur_cpu()); 80 | } 81 | -------------------------------------------------------------------------------- /kernel/port/generic/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | # SPDX-License-Identifier: MIT 3 | 4 | cmake_minimum_required(VERSION 3.10.0) 5 | 6 | set(cpu_riscv_enable_riscv_intc true) 7 | set(cpu_riscv_enable_riscv_plic true) 8 | set(cpu_riscv_enable_sbi_time true) 9 | set(cpu_riscv_enable_mmu true) 10 | 11 | set(port_src 12 | ${CMAKE_CURRENT_LIST_DIR}/src/driver/ata/sata_ahci_pcie.c 13 | ${CMAKE_CURRENT_LIST_DIR}/src/driver/ata/blkdev_ata.c 14 | ${CMAKE_CURRENT_LIST_DIR}/src/driver/pcie.c 15 | ${CMAKE_CURRENT_LIST_DIR}/src/hal/gpio.c 16 | ${CMAKE_CURRENT_LIST_DIR}/src/hal/i2c.c 17 | ${CMAKE_CURRENT_LIST_DIR}/src/hal/spi.c 18 | ${CMAKE_CURRENT_LIST_DIR}/src/memprotect.c 19 | ${CMAKE_CURRENT_LIST_DIR}/src/port.c 20 | ${CMAKE_CURRENT_LIST_DIR}/src/smp.c 21 | ) 22 | set(port_include 23 | ${CMAKE_CURRENT_LIST_DIR}/include 24 | lib/limine 25 | ) 26 | set(port_link 27 | -T${CMAKE_CURRENT_LIST_DIR}/linker_${CONFIG_CPU}.ld 28 | -L${CMAKE_CURRENT_LIST_DIR} 29 | ) 30 | set(port_flags) 31 | set_target_properties(${target} PROPERTIES LINK_DEPENDS ${CMAKE_CURRENT_LIST_DIR}/linker.ld) 32 | 33 | if(${CONFIG_CPU} STREQUAL "amd64") 34 | include(lib/uacpi/uacpi.cmake) 35 | set(port_src ${port_src} ${UACPI_SOURCES}) 36 | set(port_include ${port_include} ${UACPI_INCLUDES}) 37 | 38 | set(port_flags ${port_flags} -DPORT_ENABLE_ACPI) 39 | set(port_src ${port_src} 40 | ${CMAKE_CURRENT_LIST_DIR}/src/uacpi_kernel_api.c 41 | ) 42 | else() 43 | set(port_flags ${port_flags} -DPORT_ENABLE_DTB) 44 | set(port_src ${port_src} 45 | ${CMAKE_CURRENT_LIST_DIR}/src/dtb.c 46 | ${CMAKE_CURRENT_LIST_DIR}/src/dtparse.c 47 | ) 48 | endif() 49 | -------------------------------------------------------------------------------- /kernel/port/generic/gdbinit: -------------------------------------------------------------------------------- 1 | file firmware/badger-os.elf 2 | target remote :1234 3 | 4 | define reset 5 | mon system_reset 6 | maintenance flush register-cache 7 | thb basic_runtime_init 8 | c 9 | end 10 | 11 | mon system_reset 12 | maintenance flush register-cache 13 | thb basic_runtime_init 14 | b panic_poweroff_unchecked 15 | c 16 | -------------------------------------------------------------------------------- /kernel/port/generic/include/driver.h: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #pragma once 5 | 6 | 7 | 8 | #include "driver/pcie.h" 9 | #ifdef PORT_ENABLE_DTB 10 | #include "port/dtb.h" 11 | #endif 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | 18 | 19 | #ifdef PORT_ENABLE_DTB 20 | // Init function for devices detected from DTB. 21 | typedef void (*driver_dtb_init_t)(dtb_handle_t *dtb, dtb_node_t *node, uint32_t addr_cells, uint32_t size_cells); 22 | #endif 23 | // Init function for devices detected from PCI / PCIe. 24 | typedef void (*driver_pci_init_t)(pci_addr_t addr); 25 | 26 | // Supported device driver types. 27 | typedef enum { 28 | DRIVER_TYPE_DTB = 1, 29 | DRIVER_TYPE_PCI = 2, 30 | } driver_type_t; 31 | 32 | // Generic driver information. 33 | typedef struct { 34 | // Driver type. 35 | driver_type_t type; 36 | union { 37 | struct { 38 | // PCI device class code. 39 | pci_class_t pci_class; 40 | // Init from PCI / PCIe. 41 | driver_pci_init_t pci_init; 42 | }; 43 | #ifdef PORT_ENABLE_DTB 44 | struct { 45 | // Number of DTB compatible keywords. 46 | size_t dtb_supports_len; 47 | // Supported DTB compatible keywords. 48 | char const *const *const dtb_supports; 49 | // Init from DTB. 50 | driver_dtb_init_t dtb_init; 51 | }; 52 | #endif 53 | }; 54 | } driver_t; 55 | 56 | // Start of driver list. 57 | extern driver_t const start_drivers[] asm("__start_drivers"); 58 | // End of driver list. 59 | extern driver_t const stop_drivers[] asm("__stop_drivers"); 60 | 61 | // Define a new driver. 62 | #define DRIVER_DECL(name) __attribute((section(".drivers"))) driver_t const name 63 | -------------------------------------------------------------------------------- /kernel/port/generic/include/driver/ata.h: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #pragma once 5 | 6 | #include "driver/pcie.h" 7 | 8 | 9 | 10 | // Send a single ATA command synchronously. 11 | typedef void (*ata_cmd_sync_t)(); 12 | 13 | // ATA virtual function table. 14 | typedef struct { 15 | ata_cmd_sync_t cmd_sync; 16 | } ata_vtable_t; 17 | 18 | // ATA device handle. 19 | typedef struct { 20 | // ATA virtual function table. 21 | ata_vtable_t const *vtable; 22 | } ata_handle_t; 23 | -------------------------------------------------------------------------------- /kernel/port/generic/include/driver/ata/ahci.h: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #pragma once 5 | 6 | #include "driver/ata.h" 7 | #include "driver/ata/ahci/command.h" 8 | #include "driver/ata/ahci/generic_host_ctrl.h" 9 | #include "driver/ata/ahci/pci_cap.h" 10 | #include "driver/ata/ahci/port.h" 11 | 12 | // All HBA BAR registers - not meant to be constructed. 13 | typedef struct { 14 | // Generic host control. 15 | ahci_bar_ghc_t generic_host_ctrl; 16 | // Reserved. 17 | uint8_t _reserved0[52]; 18 | // Reserved for NVMHCI. 19 | uint8_t _reserved1[64]; 20 | // Vendor-specific registers. 21 | uint8_t _reserved2[96]; 22 | // Port control registers. 23 | ahci_bar_port_t ports[32]; 24 | } ahci_bar_t; 25 | _Static_assert(offsetof(ahci_bar_t, ports) == 0x100, "Offset of ports in ahci_bar_t must be 0x100"); 26 | 27 | // SATA controller handle. 28 | typedef struct { 29 | // Base class. 30 | ata_handle_t base; 31 | // PCI address. 32 | pci_addr_t addr; 33 | // PCI BAR handle. 34 | pci_bar_handle_t bar; 35 | // Number of ports. 36 | int n_ports; 37 | // Enabled ports (ports with drives in them). 38 | uint32_t ports_enabled; 39 | // Pointer to HBA BAR registers. 40 | ahci_bar_t *regs; 41 | // Memory allocated for the command lists. 42 | size_t cmd_paddr; 43 | // Memory allocated for the FIS. 44 | size_t fis_paddr; 45 | } sata_handle_t; 46 | -------------------------------------------------------------------------------- /kernel/port/generic/include/driver/ata/ahci/command.h: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #pragma once 5 | 6 | #include "attributes.h" 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | 13 | 14 | // Command header flags. 15 | typedef union { 16 | struct { 17 | // Command FIS length in DWORDS. 18 | uint16_t cmd_fis_len : 5; 19 | // PIO setup FIS shall indicate ATAPI command. 20 | uint16_t is_atapi : 1; 21 | // Write to storage medium. 22 | uint16_t is_write : 1; 23 | // PRDs are prefetchable. 24 | uint16_t prefetch : 1; 25 | // Command is part of a reset sequence. 26 | uint16_t is_reset : 1; 27 | // TODO: Something about BIST and tests. 28 | uint16_t is_bist : 1; 29 | // Clear the busy flag on R_OK. 30 | uint16_t clr_bsy_ok : 1; 31 | // Reserved. 32 | uint16_t : 1; 33 | // Port multiplier port. 34 | uint16_t pmul_port : 4; 35 | }; 36 | uint16_t val; 37 | } achi_cmdflag_t; 38 | 39 | // Command header entry. 40 | typedef struct { 41 | // Command header flags. 42 | VOLATILE achi_cmdflag_t flags; 43 | // Physical Region Descriptor Table Length. 44 | VOLATILE uint16_t prd_table_len; 45 | // PRD byte count; transferred byte count. 46 | VOLATILE uint32_t prd_transfer_len; 47 | // Command table base address. 48 | VOLATILE uint64_t cmdtab_paddr; 49 | // Reserved. 50 | uint32_t : 32; 51 | uint32_t : 32; 52 | uint32_t : 32; 53 | uint32_t : 32; 54 | } ahci_cmdhdr_t; 55 | -------------------------------------------------------------------------------- /kernel/port/generic/include/driver/ata/ahci/pci_cap.h: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | 11 | 12 | // Identifies a PCI capability as a SATA capability. 13 | #define AHCI_PCI_SATA_CAP 0x12 14 | 15 | // Serial ATA capability register 0 - not meant to be constructed. 16 | typedef union { 17 | struct { 18 | // Capability ID; must be 0x12. 19 | uint32_t cap_id : 8; 20 | // Next capability. 21 | uint32_t next_cap : 8; 22 | // Minor revision; must be 0x0. 23 | uint32_t min_rev : 4; 24 | // Major revision; must be 0x1. 25 | uint32_t maj_rev : 4; 26 | // Reserved. 27 | uint32_t : 8; 28 | }; 29 | uint32_t val; 30 | } satacr0_t; 31 | 32 | // Serial ATA capability register 1 - not meant to be constructed. 33 | typedef union { 34 | struct { 35 | // BAR location; select BAR to use for index-data pair, or set to 0xF for DWORDS directly following SATACR1. 36 | uint32_t barloc : 4; 37 | // BAR offset; offset, in DWORD granularity, of index-data pair into the BAR. 38 | uint32_t barofst : 20; 39 | // Reserved. 40 | uint32_t : 0; 41 | }; 42 | uint32_t val; 43 | } satacr1_t; 44 | 45 | // AHCI Serial ATA capability - not meant to be constructed. 46 | typedef struct { 47 | satacr0_t satacr0; 48 | satacr1_t satacr1; 49 | } ahci_pci_sata_cap_t; 50 | -------------------------------------------------------------------------------- /kernel/port/generic/include/driver/pcie/bar.h: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #pragma once 5 | 6 | #include "attributes.h" 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | 13 | 14 | // BAR flag: Is I/O BAR. 15 | #define BAR_FLAG_IO 1 16 | // BAR flag: Is a 64-bit memory BAR. 17 | #define BAR_FLAG_64BIT 4 18 | // I/O BAR address mask. 19 | #define BAR_IO_ADDR_MASK 0xfffffffc 20 | // 32-bit memory BAR address mask. 21 | #define BAR_MEM32_ADDR_MASK 0xfffffff0 22 | // 64-bit memory BAR address mask. 23 | #define BAR_MEM64_ADDR_MASK 0xfffffffffffffff0 24 | // Memory BAR flag: Is prefetchable. 25 | #define BAR_FLAG_PREFETCH 8 26 | 27 | // PCI base address register. 28 | typedef uint32_t pci_bar_t; 29 | // 64-bit PCI base address register. 30 | typedef uint64_t ALIGNED_TO(4) pci_bar64_t; 31 | 32 | 33 | 34 | // PCI address space types; the `type` field in `pci_bar_pattr_t`. 35 | typedef enum { 36 | // PCIe ECAM configuration space. 37 | PCI_ASPACE_ECAM = 0, 38 | // I/O space. 39 | PCI_ASPACE_IO = 1, 40 | // 32-bit memory space. 41 | PCI_ASPACE_MEM32 = 2, 42 | // 64-bit memory space. 43 | PCI_ASPACE_MEM64 = 3, 44 | } pci_aspace_t; 45 | 46 | // Attributes for PCI BAR physical address. 47 | typedef union { 48 | struct { 49 | // Register number; zero for #ranges. 50 | uint32_t regno : 8; 51 | // Function number; zero for #ranges. 52 | uint32_t func : 3; 53 | // Device number; zero for #ranges. 54 | uint32_t dev : 5; 55 | // Bus number; zero for #ranges. 56 | uint32_t bus : 8; 57 | // Address space type. 58 | uint32_t type : 2; 59 | // Reserved. 60 | uint32_t : 3; 61 | // Aliased / small. 62 | uint32_t t : 1; 63 | // Prefetchable. 64 | uint32_t prefetch : 1; 65 | // Not relocatable. 66 | uint32_t fixed : 1; 67 | }; 68 | uint32_t val; 69 | } pci_bar_pattr_t; 70 | 71 | // PCI BAR physical address. 72 | typedef struct ALIGNED_TO(4) { 73 | pci_bar_pattr_t attr; 74 | uint32_t addr_hi; 75 | uint32_t addr_lo; 76 | } pci_paddr_t; 77 | -------------------------------------------------------------------------------- /kernel/port/generic/include/port/dtparse.h: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #pragma once 5 | 6 | #include "port/dtb.h" 7 | 8 | 9 | 10 | // Parse the DTB and add found devices. 11 | void dtparse(void *dtb_ptr); 12 | 13 | // Dump the DTB. 14 | void dtdump(void *dtb_ptr); 15 | -------------------------------------------------------------------------------- /kernel/port/generic/include/port/hardware.h: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #pragma once 5 | 6 | #include "cpu/hardware.h" 7 | -------------------------------------------------------------------------------- /kernel/port/generic/include/port/hardware_allocation.h: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #pragma once 5 | 6 | 7 | 8 | /* ==== Memory protection regions ==== */ 9 | 10 | // Kernel supports virtual memory. 11 | #define MEMMAP_VMEM 1 12 | // Page size for memory protections. 13 | #define MEMMAP_PAGE_SIZE 4096 14 | -------------------------------------------------------------------------------- /kernel/port/generic/include/port/interrupt.h: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #pragma once 5 | 6 | #include "cpu/interrupt.h" 7 | -------------------------------------------------------------------------------- /kernel/port/generic/include/port/limine_req.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/badgeteam/BadgerOS/6eaf51d5336d5f7531431b39c83404a1c3202430/kernel/port/generic/include/port/limine_req.h -------------------------------------------------------------------------------- /kernel/port/generic/include/port/memprotect.h: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #pragma once 5 | 6 | #include "list.h" 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | typedef struct mpu_ctx_t { 13 | // Linked list node. 14 | dlist_node_t node; 15 | // Page table root physical page number. 16 | size_t root_ppn; 17 | } mpu_ctx_t; 18 | 19 | // HHDM length in pages. 20 | extern size_t memprotect_hhdm_pages; 21 | // Kernel virtual page number. 22 | extern size_t memprotect_kernel_vpn; 23 | // Kernel physical page number. 24 | extern size_t memprotect_kernel_ppn; 25 | // Kernel length in pages. 26 | extern size_t memprotect_kernel_pages; 27 | -------------------------------------------------------------------------------- /kernel/port/generic/include/port/port.h: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | // Early hardware initialization. 11 | void port_early_init(); 12 | // Post-heap hardware initialization. 13 | void port_postheap_init(); 14 | // Reclaim bootloader memory. 15 | void port_reclaim_mem(); 16 | // Full hardware initialization. 17 | void port_init(); 18 | // Power off. 19 | void port_poweroff(bool restart); 20 | // Send a single character to the log output. 21 | void port_putc(char msg); 22 | 23 | // Fence data and instruction memory for executable mapping. 24 | static inline void port_fencei() { 25 | #ifdef __riscv 26 | asm("fence rw,rw"); 27 | asm("fence.i"); 28 | #elif defined(__x86_64__) 29 | // TODO: Figure out which fence to use. 30 | #endif 31 | } 32 | -------------------------------------------------------------------------------- /kernel/port/generic/include/port/smp.h: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #pragma once 5 | 6 | #include "port/dtb.h" 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #ifdef PORT_ENABLE_DTB 13 | // Initialise the SMP subsystem. 14 | void smp_init_dtb(dtb_handle_t *dtb); 15 | #endif 16 | -------------------------------------------------------------------------------- /kernel/port/generic/include/port/time.h: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #pragma once 5 | 6 | #include "port/dtb.h" 7 | 8 | // Initialise timer using the DTB. 9 | void time_init_dtb(dtb_handle_t *dtb); 10 | // Early timer init before ACPI (but not DTB). 11 | void time_init_before_acpi(); 12 | // Initialise timer using ACPI. 13 | void time_init_acpi(); 14 | -------------------------------------------------------------------------------- /kernel/port/generic/limine.conf: -------------------------------------------------------------------------------- 1 | timeout: 0 2 | 3 | /BadgerOS 4 | protocol: limine 5 | kaslr: no 6 | kernel_path: boot():/boot/badger-os.elf 7 | -------------------------------------------------------------------------------- /kernel/port/generic/linker_amd64.ld: -------------------------------------------------------------------------------- 1 | OUTPUT_FORMAT(elf64-x86-64) 2 | OUTPUT_ARCH(i386:x86-64) 3 | 4 | ENTRY(_start) 5 | 6 | PHDRS 7 | { 8 | text PT_LOAD FLAGS(0x05); /* Execute + Read */ 9 | rodata PT_LOAD FLAGS(0x04); /* Read only */ 10 | data PT_LOAD FLAGS(0x06); /* Write + Read */ 11 | dynamic PT_DYNAMIC FLAGS(0x06); /* Dynamic PHDR for relocations */ 12 | } 13 | 14 | SECTIONS 15 | { 16 | . = 0xffffffff80000000; 17 | __start_badgeros_kernel = .; 18 | 19 | __start_text = .; 20 | .text : { 21 | *(.text .text.*) 22 | } :text 23 | . = ALIGN(256); 24 | .interrupt_vector_table : { 25 | *(.interrupt_vector_table) 26 | } :text 27 | . = ALIGN(4096); 28 | __stop_text = .; 29 | 30 | __start_rodata = .; 31 | .rodata : { 32 | *(.rodata .rodata.* .srodata .srodata.*) 33 | . = ALIGN(16); 34 | __start_drivers = .; 35 | KEEP(*(.drivers .drivers.*)) 36 | __stop_drivers = .; 37 | } :rodata 38 | .dynstr : { *(.dynstr) } :rodata 39 | .dynsym : { *(.dynsym) } :rodata 40 | .rela.dyn : { *(.rela.dyn) } :rodata 41 | . = ALIGN(4096); 42 | __stop_rodata = .; 43 | 44 | __start_data = .; 45 | .dynamic : { *(.dynamic) } :data :dynamic 46 | .got : { *(.got) } :data 47 | .got.plt : { *(.got.plt) } :data 48 | .data : { 49 | /* Limine requests. */ 50 | KEEP(*(.requests_start)) 51 | KEEP(*(.requests)) 52 | KEEP(*(.requests_end)) 53 | *(.data .data.*) 54 | *(.sdata .sdata.*) 55 | } :data 56 | PROVIDE(__global_pointer$ = .); 57 | 58 | /* Add zero-initialized memory last to avoid padding. */ 59 | .bss : { 60 | *(.sbss .sbss.*) 61 | *(.bss .bss.*) 62 | *(COMMON) 63 | } :data 64 | . = ALIGN(4096); 65 | __stop_data = .; 66 | 67 | __stop_badgeros_kernel = .; 68 | 69 | /* Discard useless sections. */ 70 | /DISCARD/ : { 71 | *(.eh_frame*) 72 | *(.note .note.*) 73 | *(.interp) 74 | } 75 | } -------------------------------------------------------------------------------- /kernel/port/generic/linker_riscv64.ld: -------------------------------------------------------------------------------- 1 | OUTPUT_FORMAT(elf64-littleriscv) 2 | OUTPUT_ARCH(riscv) 3 | 4 | ENTRY(_start) 5 | 6 | PHDRS 7 | { 8 | text PT_LOAD FLAGS(0x05); /* Execute + Read */ 9 | rodata PT_LOAD FLAGS(0x04); /* Read only */ 10 | data PT_LOAD FLAGS(0x06); /* Write + Read */ 11 | dynamic PT_DYNAMIC FLAGS(0x06); /* Dynamic PHDR for relocations */ 12 | } 13 | 14 | SECTIONS 15 | { 16 | . = 0xffffffff80000000; 17 | __start_badgeros_kernel = .; 18 | 19 | __start_text = .; 20 | .text : { 21 | *(.text .text.*) 22 | } :text 23 | . = ALIGN(256); 24 | .interrupt_vector_table : { 25 | *(.interrupt_vector_table) 26 | } :text 27 | . = ALIGN(4096); 28 | __stop_text = .; 29 | 30 | __start_rodata = .; 31 | .rodata : { 32 | *(.rodata .rodata.* .srodata .srodata.*) 33 | . = ALIGN(16); 34 | __start_drivers = .; 35 | KEEP(*(.drivers .drivers.*)) 36 | __stop_drivers = .; 37 | __start_fsdrivers = .; 38 | KEEP(*(.fsdrivers .fsdrivers.*)) 39 | __stop_fsdrivers = .; 40 | } :rodata 41 | .dynstr : { *(.dynstr) } :rodata 42 | .dynsym : { *(.dynsym) } :rodata 43 | .rela.dyn : { *(.rela.dyn) } :rodata 44 | . = ALIGN(4096); 45 | __stop_rodata = .; 46 | 47 | __start_data = .; 48 | .dynamic : { *(.dynamic) } :data :dynamic 49 | .got : { *(.got) } :data 50 | .got.plt : { *(.got.plt) } :data 51 | .data : { 52 | /* Limine requests. */ 53 | KEEP(*(.requests_start)) 54 | KEEP(*(.requests)) 55 | KEEP(*(.requests_end)) 56 | *(.data .data.*) 57 | *(.sdata .sdata.*) 58 | } :data 59 | PROVIDE(__global_pointer$ = .); 60 | 61 | /* Add zero-initialized memory last to avoid padding. */ 62 | .bss : { 63 | *(.sbss .sbss.*) 64 | *(.bss .bss.*) 65 | *(COMMON) 66 | } :data 67 | . = ALIGN(4096); 68 | __stop_data = .; 69 | 70 | __stop_badgeros_kernel = .; 71 | 72 | /* Discard useless sections. */ 73 | /DISCARD/ : { 74 | *(.eh_frame*) 75 | *(.note .note.*) 76 | *(.interp) 77 | } 78 | } -------------------------------------------------------------------------------- /kernel/port/generic/port.mk: -------------------------------------------------------------------------------- 1 | 2 | PORT ?= /dev/ttyUSB1 3 | DRIVE ?= /dev/null # If user doesn't specify, burn /dev/null instead of anything potentially important 4 | 5 | include port/generic/port_$(CONFIG_CPU).mk 6 | 7 | .PHONY: _port_on_config 8 | _port_on_config: _cpu_on_config 9 | git submodule update --init lib/limine 10 | git submodule update --init lib/uacpi 11 | 12 | image: build $(OUTPUT)/image.hdd 13 | 14 | burn: image 15 | sudo dd if=$(OUTPUT)/image.hdd of=$(DRIVE) bs=1M oflag=sync conv=nocreat 16 | 17 | .PHONY: monitor 18 | monitor: 19 | echo -e "\033[1mType ^A^X to exit.\033[0m" 20 | picocom -q -b 115200 '$(PORT)' \ 21 | | ../tools/address-filter.py -A $(CROSS_COMPILE)addr2line '$(OUTPUT)/badger-os.elf'; echo -e '\033[0m' 22 | -------------------------------------------------------------------------------- /kernel/port/generic/port_amd64.mk: -------------------------------------------------------------------------------- 1 | 2 | QEMU_CPU_OPTS = 3 | QEMU ?= qemu-system-x86_64 4 | 5 | .PHONY: _cpu_on_config 6 | _cpu_on_config: 7 | 8 | 9 | $(OUTPUT)/image.hdd: port/generic/limine.conf $(OUTPUT)/badger-os.elf 10 | # Create boot filesystem 11 | echo Create EFI filesystem 12 | rm -rf $(BUILDDIR)/image.dir 13 | mkdir -p $(BUILDDIR)/image.dir/EFI/BOOT/ 14 | mkdir -p $(BUILDDIR)/image.dir/boot/ 15 | make -C lib/limine 16 | cp lib/limine/BOOTX64.EFI $(BUILDDIR)/image.dir/EFI/BOOT/ 17 | cp lib/limine/limine-bios.sys $(BUILDDIR)/image.dir/boot/ 18 | cp port/generic/limine.conf $(BUILDDIR)/image.dir/boot/ 19 | cp $(OUTPUT)/badger-os.elf $(BUILDDIR)/image.dir/boot/ 20 | 21 | # Format FAT filesystem 22 | echo Create FAT filesystem blob 23 | rm -f $(BUILDDIR)/image_bootfs.bin 24 | dd if=/dev/zero bs=1M count=4 of=$(BUILDDIR)/image_bootfs.bin 25 | mformat -i $(BUILDDIR)/image_bootfs.bin 26 | mcopy -s -i $(BUILDDIR)/image_bootfs.bin $(BUILDDIR)/image.dir/* ::/ 27 | 28 | # Create image 29 | echo Create image 30 | rm -f $(OUTPUT)/image.hdd 31 | dd if=/dev/zero bs=1M count=64 of=$(OUTPUT)/image.hdd 32 | # 4M /boot, remainder /root 33 | echo pre sgdisk 34 | sgdisk -a 1 \ 35 | --new=3:34:8225 --change-name=3:boot --typecode=3:0x0700 \ 36 | --new=4:8226:-0 --change-name=4:root --typecode=4:0x8300 \ 37 | $(OUTPUT)/image.hdd 38 | 39 | # Install Limine BIOS bootloader 40 | ./lib/limine/limine bios-install $(OUTPUT)/image.hdd 41 | 42 | # Copy data onto partitions 43 | echo Copy data onto partitions 44 | dd if=$(BUILDDIR)/image_bootfs.bin bs=512 seek=34 of=$(OUTPUT)/image.hdd conv=notrunc 45 | 46 | .PHONY: clean-image 47 | clean-image: 48 | 49 | .PHONY: qemu 50 | qemu-debug: image 51 | $(QEMU) -s -S \ 52 | -d int -no-reboot -no-shutdown \ 53 | -smp 1 -m 4G -cpu max,tsc-frequency=1000000000 \ 54 | -device pcie-root-port,bus=pci.0,id=pcisw0 \ 55 | -device qemu-xhci,bus=pcisw0 -device usb-kbd \ 56 | -device virtio-scsi-pci,id=scsi \ 57 | -drive id=hd0,format=raw,file=$(OUTPUT)/image.hdd \ 58 | -serial mon:stdio -display none \ 59 | | ../tools/address-filter.py -L -A $(CROSS_COMPILE)addr2line $(OUTPUT)/badger-os.elf 60 | 61 | .PHONY: qemu 62 | qemu: image 63 | $(QEMU) -s \ 64 | -smp 1 -m 4G -cpu max,tsc-frequency=1000000000 \ 65 | -device pcie-root-port,bus=pci.0,id=pcisw0 \ 66 | -device qemu-xhci,bus=pcisw0 -device usb-kbd \ 67 | -device virtio-scsi-pci,id=scsi \ 68 | -drive id=hd0,format=raw,file=$(OUTPUT)/image.hdd \ 69 | -serial mon:stdio -display none \ 70 | | ../tools/address-filter.py -L -A $(CROSS_COMPILE)addr2line $(OUTPUT)/badger-os.elf 71 | -------------------------------------------------------------------------------- /kernel/port/generic/src/driver/ata/blkdev_ata.c: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #include "blockdevice/blkdev_impl.h" 5 | #include "driver/ata.h" 6 | -------------------------------------------------------------------------------- /kernel/port/generic/src/hal/gpio.c: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #include "hal/gpio.h" 5 | 6 | #pragma GCC diagnostic ignored "-Wunused-parameter" 7 | 8 | 9 | 10 | // Returns the amount of GPIO pins present. 11 | // Cannot produce an error. 12 | int io_count() { 13 | return 0; 14 | } 15 | 16 | // Sets the mode of GPIO pin `pin` to `mode`. 17 | void io_mode(badge_err_t *ec, int pin, io_mode_t mode) { 18 | badge_err_set(ec, ELOC_GPIO, ECAUSE_UNSUPPORTED); 19 | } 20 | 21 | // Get the mode of GPIO pin `pin`. 22 | io_mode_t io_getmode(badge_err_t *ec, int pin) { 23 | badge_err_set(ec, ELOC_GPIO, ECAUSE_UNSUPPORTED); 24 | return IO_MODE_HIGH_Z; 25 | } 26 | 27 | // Sets the pull resistor behaviour of GPIO pin `pin` to `dir`. 28 | void io_pull(badge_err_t *ec, int pin, io_pull_t dir) { 29 | badge_err_set(ec, ELOC_GPIO, ECAUSE_UNSUPPORTED); 30 | } 31 | 32 | // Get the pull resistor behaviour of GPIO pin `pin`. 33 | io_pull_t io_getpull(badge_err_t *ec, int pin) { 34 | badge_err_set(ec, ELOC_GPIO, ECAUSE_UNSUPPORTED); 35 | return IO_PULL_NONE; 36 | } 37 | 38 | // Writes level to GPIO pin pin. 39 | void io_write(badge_err_t *ec, int pin, bool level) { 40 | badge_err_set(ec, ELOC_GPIO, ECAUSE_UNSUPPORTED); 41 | } 42 | 43 | // Reads logic level value from GPIO pin `pin`. 44 | // Returns false on error. 45 | bool io_read(badge_err_t *ec, int pin) { 46 | badge_err_set(ec, ELOC_GPIO, ECAUSE_UNSUPPORTED); 47 | return false; 48 | } 49 | 50 | // Determine whether GPIO `pin` is claimed by a peripheral. 51 | // Returns false on error. 52 | bool io_is_peripheral(badge_err_t *ec, int pin) { 53 | badge_err_set(ec, ELOC_GPIO, ECAUSE_UNSUPPORTED); 54 | return false; 55 | } 56 | -------------------------------------------------------------------------------- /kernel/port/generic/src/hal/i2c.c: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #include "hal/i2c.h" 5 | 6 | #pragma GCC diagnostic ignored "-Wunused-parameter" 7 | 8 | 9 | 10 | // Returns the amount of I²C peripherals present. 11 | // Cannot produce an error. 12 | int i2c_count() { 13 | return 0; 14 | } 15 | 16 | 17 | // Initialises I²C peripheral i2c_num in slave mode with SDA pin `sda_pin`, SCL pin `scl_pin` and clock speed/bitrate 18 | // bitrate. When initialised as an I²C master, the modes of the SDA and SCL pins are changed automatically. This 19 | // function may be used again to change the settings on an initialised I²C peripheral in master mode. 20 | void i2c_master_init(badge_err_t *ec, int i2c_num, int sda_pin, int scl_pin, int32_t bitrate) { 21 | badge_err_set(ec, ELOC_I2C, ECAUSE_UNSUPPORTED); 22 | } 23 | 24 | // De-initialises I²C peripheral i2c_num in master mode. 25 | void i2c_master_deinit(badge_err_t *ec, int i2c_num) { 26 | badge_err_set(ec, ELOC_I2C, ECAUSE_UNSUPPORTED); 27 | } 28 | 29 | // Reads len bytes into buffer buf from I²C slave with ID slave_id. 30 | // This function blocks until the entire transaction is completed and returns the number of acknowledged read bytes. 31 | size_t i2c_master_read_from(badge_err_t *ec, int i2c_num, int slave_id, void *raw_ptr, size_t len) { 32 | badge_err_set(ec, ELOC_I2C, ECAUSE_UNSUPPORTED); 33 | return 0; 34 | } 35 | 36 | // Writes len bytes from buffer buf to I²C slave with ID slave_id. 37 | // This function blocks until the entire transaction is completed and returns the number of acknowledged written bytes. 38 | size_t i2c_master_write_to(badge_err_t *ec, int i2c_num, int slave_id, void const *raw_ptr, size_t len) { 39 | badge_err_set(ec, ELOC_I2C, ECAUSE_UNSUPPORTED); 40 | return 0; 41 | } 42 | -------------------------------------------------------------------------------- /kernel/port/generic/src/hal/spi.c: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #include "hal/spi.h" 5 | 6 | #pragma GCC diagnostic ignored "-Wunused-parameter" 7 | 8 | 9 | 10 | int spi_count() { 11 | return 0; 12 | } 13 | 14 | void spi_controller_init( 15 | badge_err_t *ec, int spi_num, int sclk_pin, int mosi_pin, int miso_pin, int ss_pin, int32_t bitrate 16 | ) { 17 | badge_err_set(ec, ELOC_SPI, ECAUSE_UNSUPPORTED); 18 | } 19 | 20 | void spi_controller_read(badge_err_t *ec, int spi_num, void *buf, size_t len) { 21 | badge_err_set(ec, ELOC_SPI, ECAUSE_UNSUPPORTED); 22 | } 23 | 24 | void spi_controller_write(badge_err_t *ec, int spi_num, void const *buf, size_t len) { 25 | badge_err_set(ec, ELOC_SPI, ECAUSE_UNSUPPORTED); 26 | } 27 | 28 | void spi_controller_transfer(badge_err_t *ec, int spi_num, void *buf, size_t len, bool fdx) { 29 | badge_err_set(ec, ELOC_SPI, ECAUSE_UNSUPPORTED); 30 | } 31 | -------------------------------------------------------------------------------- /kernel/src/badgelib/rawprint.c: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #include "rawprint.h" 5 | 6 | #include "num_to_str.h" 7 | #include "port/port.h" 8 | #include "time.h" 9 | 10 | #include 11 | 12 | char const hextab[] = "0123456789ABCDEF"; 13 | 14 | // Simple printer with specified length. 15 | void rawprint_substr(char const *msg, size_t length) { 16 | while (length--) { 17 | rawputc(*msg); 18 | msg++; 19 | } 20 | } 21 | 22 | // Simple printer. 23 | void rawprint(char const *msg) { 24 | for (; *msg; msg++) rawputc(*msg); 25 | } 26 | 27 | // Simple printer. 28 | void rawputc(char msg) { 29 | static char prev = 0; 30 | if (msg == '\r') { 31 | port_putc('\r'); 32 | port_putc('\n'); 33 | } else if (msg == '\n') { 34 | if (prev != '\r') { 35 | port_putc('\r'); 36 | port_putc('\n'); 37 | } 38 | } else { 39 | port_putc(msg); 40 | } 41 | prev = msg; 42 | } 43 | 44 | // Bin 2 hex printer. 45 | void rawprinthex(uint64_t val, int digits) { 46 | for (; digits > 0; digits--) { 47 | rawputc(hextab[(val >> (digits * 4 - 4)) & 15]); 48 | } 49 | } 50 | 51 | // Bin 2 dec printer. 52 | void rawprintudec(uint64_t val, int digits) { 53 | char buf[20]; 54 | size_t buf_digits = uint_to_cstr_packed(val, buf, sizeof(buf)); 55 | if (digits < (int)buf_digits) 56 | digits = (int)buf_digits; 57 | else if (digits > (int)sizeof(buf)) 58 | digits = sizeof(buf); 59 | rawprint_substr(buf, digits); 60 | } 61 | 62 | // Bin 2 dec printer. 63 | void rawprintdec(int64_t val, int digits) { 64 | if (val < 0) { 65 | rawputc('-'); 66 | val = -val; 67 | } 68 | rawprintudec(val, digits); 69 | } 70 | 71 | // Current uptime printer for logging. 72 | void rawprintuptime() { 73 | char buf[20]; 74 | size_t digits = num_uint_to_str(time_us() / 1000, buf); 75 | if (digits < 8) 76 | digits = 8; 77 | 78 | rawputc('['); 79 | rawprint_substr(buf + 20 - digits, digits - 3); 80 | rawputc('.'); 81 | rawprint_substr(buf + 17, 3); 82 | rawputc(']'); 83 | } 84 | -------------------------------------------------------------------------------- /kernel/src/badgelib/semaphore.c: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #include "semaphore.h" 5 | 6 | #include "assertions.h" 7 | #include "scheduler/waitlist.h" 8 | 9 | 10 | 11 | // Initialize a new semaphore. 12 | void sem_init(sem_t *sem) { 13 | *sem = (sem_t)SEM_T_INIT; 14 | atomic_thread_fence(memory_order_release); 15 | } 16 | 17 | // Clean up a semaphore. 18 | void sem_destroy(sem_t *sem) { 19 | assert_dev_drop(!sem->waiting_list.list.len); 20 | } 21 | 22 | // Reset the semaphore count. 23 | void sem_reset(sem_t *sem) { 24 | atomic_store_explicit(&sem->available, 0, memory_order_relaxed); 25 | } 26 | 27 | // Post the semaphore once. 28 | void sem_post(sem_t *sem) { 29 | assert_dev_keep(atomic_fetch_add(&sem->available, 1) < __INT_MAX__); 30 | waitlist_notify(&sem->waiting_list); 31 | } 32 | 33 | static bool sem_block_double_check(void *cookie) { 34 | sem_t *sem = cookie; 35 | return atomic_load_explicit(&sem->available, memory_order_relaxed) == 0; 36 | } 37 | 38 | // Block on a semaphore. 39 | static void sem_block(sem_t *sem, timestamp_us_t timeout) { 40 | waitlist_block(&sem->waiting_list, timeout, sem_block_double_check, sem); 41 | } 42 | 43 | // Await the semaphore. 44 | bool sem_await(sem_t *sem, timestamp_us_t timeout) { 45 | // Compute timeout. 46 | timestamp_us_t now = time_us(); 47 | if (timeout < 0 || timeout - TIMESTAMP_US_MAX + now >= 0) { 48 | timeout = TIMESTAMP_US_MAX; 49 | } else { 50 | timeout += now; 51 | } 52 | 53 | int cur = atomic_load_explicit(&sem->available, memory_order_relaxed); 54 | 55 | // Fast path. 56 | for (int i = SEM_FAST_LOOPS; i >= 0; i--) { 57 | assert_dev_drop(cur >= 0); 58 | if (!cur) { 59 | cur = atomic_load_explicit(&sem->available, memory_order_relaxed); 60 | } else if (atomic_compare_exchange_weak_explicit( 61 | &sem->available, 62 | &cur, 63 | cur - 1, 64 | memory_order_acquire, 65 | memory_order_relaxed 66 | )) { 67 | return true; 68 | } 69 | } 70 | 71 | // Slow path. 72 | while (time_us() < timeout) { 73 | assert_dev_drop(cur >= 0); 74 | if (!cur) { 75 | sem_block(sem, timeout); 76 | cur = atomic_load_explicit(&sem->available, memory_order_relaxed); 77 | 78 | } else if (atomic_compare_exchange_weak_explicit( 79 | &sem->available, 80 | &cur, 81 | cur - 1, 82 | memory_order_acquire, 83 | memory_order_relaxed 84 | )) { 85 | return true; 86 | } 87 | } 88 | 89 | return false; 90 | } 91 | -------------------------------------------------------------------------------- /kernel/src/badgelib/spinlock.c: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #include "spinlock.h" 5 | 6 | #include "assertions.h" 7 | #include "interrupt.h" 8 | 9 | #define SPINLOCK_EXCL_MAGIC (-__INT_MAX__ - 1) 10 | 11 | 12 | 13 | // Take the spinlock exclusively. 14 | void spinlock_take(spinlock_t *lock) { 15 | assert_dev_drop(!irq_is_enabled()); 16 | int cur = atomic_load_explicit(lock, memory_order_acquire); 17 | int next; 18 | do { 19 | cur &= 1; 20 | next = cur | SPINLOCK_EXCL_MAGIC; 21 | } while (!atomic_compare_exchange_weak_explicit(lock, &cur, next, memory_order_acquire, memory_order_relaxed)); 22 | } 23 | 24 | // Release the spinlock exclusively. 25 | void spinlock_release(spinlock_t *lock) { 26 | assert_dev_drop(!irq_is_enabled()); 27 | int res = atomic_fetch_and_explicit(lock, 1, memory_order_release); 28 | assert_dev_drop(res < 0); 29 | } 30 | 31 | // Take the spinlock shared. 32 | void spinlock_take_shared(spinlock_t *lock) { 33 | assert_dev_drop(!irq_is_enabled()); 34 | assert_dev_drop(atomic_load_explicit(lock, memory_order_relaxed) & 1); 35 | int cur = atomic_load_explicit(lock, memory_order_acquire); 36 | int next; 37 | do { 38 | if (cur < 0) { 39 | cur = 1; 40 | } 41 | next = cur + 2; 42 | } while (!atomic_compare_exchange_weak_explicit(lock, &cur, next, memory_order_acquire, memory_order_relaxed)); 43 | } 44 | 45 | // Release the spinlock shared. 46 | void spinlock_release_shared(spinlock_t *lock) { 47 | assert_dev_drop(!irq_is_enabled()); 48 | int res = atomic_fetch_sub_explicit(lock, 2, memory_order_release); 49 | assert_dev_drop(res >= 2); 50 | } 51 | -------------------------------------------------------------------------------- /kernel/src/malloc/build.sh: -------------------------------------------------------------------------------- 1 | set -e 2 | 3 | defines="-DBADGEROS_MALLOC_STANDALONE -DBADGEROS_MALLOC_DEBUG_LEVEL=3" 4 | sources="main.c static-buddy.c slab-alloc.c" 5 | 6 | echo "64-bit" 7 | gcc -m64 -g3 -Wall -Wextra ${defines} ${sources} -o main64 8 | echo "32-bit" 9 | gcc -m32 -g3 -Wall -Wextra ${defines} ${sources} -o main32 10 | echo "64-bit softbit" 11 | gcc -DSOFTBIT -g3 -Wall -Wextra ${defines} ${sources} -o main64-softbit 12 | echo "32-bit softbit" 13 | gcc -DSOFTBIT -m32 -g3 -Wall -Wextra ${defines} ${sources} -o main32-softbit 14 | 15 | echo "wrapper" 16 | gcc -std=gnu17 -g3 -Wall -Wextra -DPRELOAD ${sources} ${defines} malloc.c -Wl,--wrap,malloc -Wl,--wrap,free -Wl,--wrap,calloc -Wl,--wrap,realloc -Wl,--wrap,reallocarray -Wl,--wrap,aligned_alloc -Wl,--wrap,posix_memalign -fpic -shared -o malloc.so 17 | 18 | #riscv64-linux-gnu-gcc -g3 -Wall -Wextra ${defines} ${sources} -o mainrv64 19 | #riscv64-linux-gnu-gcc -march=rv32imac_zicsr_zifencei -g3 -Wall -Wextra ${defines} ${sources} -o mainrv32 20 | -------------------------------------------------------------------------------- /kernel/src/malloc/compiler.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | __attribute__((always_inline)) static inline void intr_pause() { 4 | #if (defined(__x86_64__) || defined(__i386__)) 5 | __builtin_ia32_pause(); 6 | #elif (defined(__arm__)) 7 | __asm__ volatile("yield"); 8 | #elif (defined(__aarch64__)) 9 | __asm__ volatile("yield"); 10 | #elif (defined(__riscv)) 11 | // On some versions of GCC the riscv_pause() builtin doesn't work 12 | // on the badge hardware 13 | __asm__ volatile("nop"); 14 | __asm__ volatile("nop"); 15 | __asm__ volatile("nop"); 16 | __asm__ volatile("nop"); 17 | 18 | #else 19 | // For architectures where there's no known pause instruction, a no-op. 20 | #endif 21 | } 22 | -------------------------------------------------------------------------------- /kernel/src/malloc/spinlock.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | #pragma once 4 | 5 | #include "compiler.h" 6 | 7 | #include 8 | #include 9 | 10 | #define SPIN_LOCK_LOCK(x) \ 11 | do { \ 12 | int __spin_backoff = 1; \ 13 | while (atomic_flag_test_and_set_explicit(&x, memory_order_acquire)) { \ 14 | for (int __spin_i = 0; __spin_i < __spin_backoff; ++__spin_i) intr_pause(); \ 15 | if (__spin_backoff < 16) /* limit backoff to 16 pauses */ \ 16 | __spin_backoff <<= 1; \ 17 | } \ 18 | } while (0) 19 | 20 | #define SPIN_LOCK_TRY_LOCK(x) !atomic_flag_test_and_set_explicit(&x, memory_order_acquire) 21 | #define SPIN_LOCK_UNLOCK(x) atomic_flag_clear_explicit(&x, memory_order_release) 22 | #define DELAY(x) \ 23 | do { \ 24 | for (size_t i = 0; i < x; ++i) { \ 25 | intr_pause(); \ 26 | } \ 27 | } while (0) 28 | -------------------------------------------------------------------------------- /kernel/src/page_alloc.c: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #include "page_alloc.h" 5 | 6 | #include "badge_strings.h" 7 | #include "port/hardware_allocation.h" 8 | #include "static-buddy.h" 9 | #if MEMMAP_VMEM 10 | #include "cpu/mmu.h" 11 | #endif 12 | 13 | 14 | 15 | // Allocate pages of physical memory. 16 | // Uses physical page numbers (paddr / MEMMAP_PAGE_SIZE). 17 | size_t phys_page_alloc(size_t page_count, bool for_user) { 18 | void *mem = buddy_allocate(page_count * MEMMAP_PAGE_SIZE, for_user ? BLOCK_TYPE_USER : BLOCK_TYPE_PAGE, 0); 19 | if (!mem) { 20 | return 0; 21 | } 22 | mem_set(mem, 0, page_count * MEMMAP_PAGE_SIZE); 23 | #if MEMMAP_VMEM 24 | return ((size_t)mem - mmu_hhdm_vaddr) / MEMMAP_PAGE_SIZE; 25 | #else 26 | return (size_t)mem / MEMMAP_PAGE_SIZE; 27 | #endif 28 | } 29 | 30 | // Returns how large a physical allocation actually is. 31 | // Uses physical page numbers (paddr / MEMMAP_PAGE_SIZE). 32 | size_t phys_page_size(size_t ppn) { 33 | #if MEMMAP_VMEM 34 | return buddy_get_size((void *)(ppn * MEMMAP_PAGE_SIZE + mmu_hhdm_vaddr)) / MEMMAP_PAGE_SIZE; 35 | #else 36 | return buddy_get_size((void *)(ppn * MEMMAP_PAGE_SIZE)) / MEMMAP_PAGE_SIZE; 37 | #endif 38 | } 39 | 40 | // Free pages of physical memory. 41 | // Uses physical page numbers (paddr / MEMMAP_PAGE_SIZE). 42 | void phys_page_free(size_t ppn) { 43 | #if MEMMAP_VMEM 44 | buddy_deallocate((void *)(ppn * MEMMAP_PAGE_SIZE + mmu_hhdm_vaddr)); 45 | #else 46 | buddy_deallocate((void *)(ppn * MEMMAP_PAGE_SIZE)); 47 | #endif 48 | } 49 | 50 | // Split a physical page allocation into two in the allocator. 51 | // Uses physical page numbers (paddr / MEMMAP_PAGE_SIZE). 52 | void phys_page_split(size_t ppn) { 53 | #if MEMMAP_VMEM 54 | buddy_split_allocated((void *)(ppn * MEMMAP_PAGE_SIZE + mmu_hhdm_vaddr)); 55 | #else 56 | buddy_split_allocated((void *)(ppn * MEMMAP_PAGE_SIZE)); 57 | #endif 58 | } 59 | -------------------------------------------------------------------------------- /kernel/src/process/syscall_util.c: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #include "syscall_util.h" 5 | 6 | #include "process/internal.h" 7 | #include "process/sighandler.h" 8 | #include "process/types.h" 9 | 10 | 11 | // Assert that a condition is true, or raise SIGSEGV and don't return. 12 | void sigsys_assert(bool condition) { 13 | if (!condition) { 14 | proc_sigsys_handler(); 15 | } 16 | } 17 | 18 | // Assert that a condition is true, or raise SIGSEGV and don't return. 19 | void sigsegv_assert(bool condition, size_t vaddr) { 20 | if (!condition) { 21 | proc_sigsegv_handler(vaddr); 22 | } 23 | } 24 | 25 | // Checks whether the process has permission for a range of memory. 26 | bool sysutil_memperm(void const *ptr, size_t len, uint32_t flags) { 27 | return (proc_map_contains_raw(proc_current(), (size_t)ptr, len) & flags) == flags; 28 | } 29 | 30 | // If the process does not have access, raise SIGSEGV and don't return. 31 | void sysutil_memassert(void const *ptr, size_t len, uint32_t flags) { 32 | if (!sysutil_memperm(ptr, len, flags)) { 33 | proc_sigsegv_handler((size_t)ptr); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /kernel/src/scheduler/waitlist.c: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | 4 | #include "scheduler/waitlist.h" 5 | 6 | #include "assertions.h" 7 | #include "cpu/interrupt.h" 8 | #include "list.h" 9 | #include "scheduler/isr.h" 10 | #include "scheduler/scheduler.h" 11 | #include "spinlock.h" 12 | #include "time.h" 13 | 14 | 15 | 16 | // Resume a blocked thread by means of a timer. 17 | static void timer_resume(void *arg) { 18 | waitlist_ent_t *ent = arg; 19 | thread_unblock(ent->thread, ent->ticket); 20 | } 21 | 22 | // Block on a waiting list. 23 | // Runs `double_check(cookie)` and unblocks if false to prevent race conditions. 24 | void waitlist_block(waitlist_t *list, timestamp_us_t timeout, bool (*double_check)(void *), void *cookie) { 25 | bool ie = irq_disable(); 26 | 27 | waitlist_ent_t ent = { 28 | .node = DLIST_NODE_EMPTY, 29 | .in_list = true, 30 | .thread = sched_current_tid(), 31 | .ticket = thread_block(), 32 | }; 33 | timertask_t task = { 34 | .callback = timer_resume, 35 | .cookie = &ent, 36 | .timestamp = timeout, 37 | }; 38 | 39 | spinlock_take(&list->lock); 40 | dlist_append(&list->list, &ent.node); 41 | spinlock_release(&list->lock); 42 | 43 | if (timeout < TIMESTAMP_US_MAX) { 44 | time_add_async_task(&task); 45 | } 46 | 47 | if (!double_check(cookie)) { 48 | thread_unblock(ent.thread, ent.ticket); 49 | irq_enable(); 50 | } else { 51 | thread_yield(); 52 | } 53 | 54 | if (timeout < TIMESTAMP_US_MAX) { 55 | time_cancel_async_task(task.taskno); 56 | } 57 | 58 | assert_dev_keep(irq_disable()); 59 | spinlock_take(&list->lock); 60 | if (ent.in_list) { 61 | dlist_remove(&list->list, &ent.node); 62 | } else { 63 | assert_dev_drop(!dlist_contains(&list->list, &ent.node)); 64 | } 65 | spinlock_release(&list->lock); 66 | irq_enable_if(ie); 67 | } 68 | 69 | // Try to resume a thread blocked on the waiting list. 70 | void waitlist_notify(waitlist_t *list) { 71 | bool ie = irq_disable(); 72 | spinlock_take(&list->lock); 73 | 74 | while (1) { 75 | waitlist_ent_t *ent = (waitlist_ent_t *)dlist_pop_front(&list->list); 76 | if (!ent) 77 | break; 78 | ent->in_list = false; 79 | if (thread_unblock(ent->thread, ent->ticket)) 80 | break; 81 | } 82 | 83 | spinlock_release(&list->lock); 84 | irq_enable_if(ie); 85 | } 86 | -------------------------------------------------------------------------------- /shell.nix: -------------------------------------------------------------------------------- 1 | let 2 | nixpkgs_rev = "1f7e3343e3de9e8d05d8316262a95409a390c83b"; # master on 2024-03-20 3 | 4 | # TODO: change to mirrexagon's repo after merges of PR 42 and 44 5 | nixpkgs-esp-dev_rev = "b3594b490ed1ffb40e5dd20ea7074b73ae34ea27"; # main on 2024-03-20 6 | nixpkgs-esp-dev_sha256 = "sha256:0fqvk9va6rqd9pmjd82pmikzc30d174jcgmvfyacg875qgrxjii8"; 7 | nixpkgs-esp-dev_src = (builtins.fetchTarball { 8 | url = "https://github.com/cyber-murmel/nixpkgs-esp-dev/archive/${nixpkgs-esp-dev_rev}.tar.gz"; 9 | sha256 = nixpkgs-esp-dev_sha256; 10 | }); 11 | in 12 | 13 | { pkgs ? import (builtins.fetchTarball "https://github.com/NixOS/nixpkgs/archive/${nixpkgs_rev}.tar.gz") { 14 | overlays = [ 15 | (import "${nixpkgs-esp-dev_src}/overlay.nix") 16 | ]; 17 | } 18 | }: 19 | 20 | with pkgs; 21 | let 22 | riscv32-unknown-linux-gnu = stdenv.mkDerivation rec { 23 | name = "riscv32-unknown-linux-gnu"; 24 | version = "13.2.0"; 25 | 26 | release = "2024.03.01"; 27 | ubuntu_version = "22.04"; 28 | 29 | src = (builtins.fetchTarball "https://github.com/riscv-collab/riscv-gnu-toolchain/releases/download/${release}/riscv32-glibc-ubuntu-${ubuntu_version}-gcc-nightly-${release}-nightly.tar.gz"); 30 | 31 | buildInputs = [ 32 | autoPatchelfHook 33 | 34 | zlib 35 | glib 36 | gmp 37 | zstd 38 | mpfr 39 | libmpc 40 | lzma 41 | expat 42 | python310 43 | ncurses 44 | ]; 45 | 46 | installPhase = '' 47 | runHook preInstall 48 | 49 | cp -r . $out 50 | 51 | runHook postInstall 52 | ''; 53 | }; 54 | in 55 | mkShell { 56 | buildInputs = [ 57 | riscv32-unknown-linux-gnu 58 | cmake 59 | esptool 60 | picocom 61 | esp-idf-esp32c6 62 | ]; 63 | } 64 | -------------------------------------------------------------------------------- /tools/address-filter.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # SPDX-License-Identifier: MIT 4 | 5 | import sys, subprocess, os 6 | from argparse import * 7 | 8 | assert __name__ == "__main__" 9 | 10 | parser = ArgumentParser(description="Reads stdin for kernel addresses and injects their linenumbers") 11 | parser.add_argument("-L", "--long", "--64bit", action="store_true", help="Use 64-bit addresses") 12 | parser.add_argument("-A", "--addr2line", action="store", help="addr2line program") 13 | parser.add_argument("file", action="store", help="Executable file to read symbols from") 14 | args = parser.parse_args() 15 | 16 | hextab = "0123456789abcdef" 17 | buf = '' 18 | hexits = 16 if args.long else 8 19 | 20 | state = -2 21 | 22 | prefix = os.path.realpath('.') or "" 23 | 24 | def addr2line(): 25 | global buf 26 | cmd = [ 27 | args.addr2line, '-e', args.file, buf 28 | ] 29 | process = subprocess.Popen(cmd, stdout=subprocess.PIPE) 30 | result = process.stdout.read() 31 | process.wait() 32 | string = result.decode().strip() 33 | if process.returncode: return 34 | if string == '??:0': return 35 | if string.endswith('?'): return 36 | sys.stdout.write(" (") 37 | if string.startswith(prefix): 38 | sys.stdout.write(os.path.relpath(string)) 39 | else: 40 | sys.stdout.write(string) 41 | sys.stdout.write(")") 42 | 43 | while True: 44 | try: 45 | msg_raw = sys.stdin.buffer.read(1) 46 | except (KeyboardInterrupt, EOFError): 47 | break 48 | if not len(msg_raw): break 49 | msg = chr(msg_raw[0]) 50 | for char in msg: 51 | if state == -2 and char == '0': 52 | state = -1 53 | sys.stdout.write(char) 54 | elif state == -1 and char in 'xX': 55 | state = 0 56 | buf = '' 57 | sys.stdout.write(char) 58 | elif state == hexits: 59 | if char.lower() not in hextab: 60 | addr2line() 61 | sys.stdout.write(char) 62 | state = -2 63 | elif char.lower() in hextab: 64 | buf += char 65 | state += 1 66 | sys.stdout.write(char) 67 | else: 68 | state = -2 69 | sys.stdout.write(char) 70 | sys.stdout.flush() 71 | -------------------------------------------------------------------------------- /tools/bin2c.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import sys 4 | 5 | if len(sys.argv) != 4: 6 | print("Usage: bin2c.py [infile] [outfile] [name]") 7 | exit(1) 8 | 9 | infd = open(sys.argv[1], "rb") 10 | outfd = open(sys.argv[2], "w") 11 | name = sys.argv[3] 12 | 13 | data = infd.read() 14 | 15 | outfd.write("// WARNING: This is a generated file, do not edit it!\n") 16 | outfd.write("// clang-format off\n") 17 | outfd.write("#include \n") 18 | outfd.write("#include \n") 19 | outfd.write("uint8_t const {}[] = {{\n".format(name)) 20 | for byte in data: 21 | outfd.write("0x{:02x},".format(byte)) 22 | outfd.write("\n};\n") 23 | outfd.write("size_t const {}_len = {};\n".format(name, len(data))) 24 | 25 | infd.close() 26 | outfd.flush() 27 | outfd.close() 28 | -------------------------------------------------------------------------------- /tools/pack-image.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # SPDX-License-Identifier: MIT 3 | 4 | import os, argparse, shutil 5 | from pathlib import Path 6 | 7 | def patchElf(fd): 8 | # Determine the length of the file. 9 | fd.seek(0, 2) 10 | raw_size = fd.tell() 11 | 12 | # Append padding bytes. 13 | padd_size = ((raw_size + 15) & ~15) - raw_size 14 | if padd_size == 0: 15 | fd.write(b"\0" * 15) 16 | elif padd_size > 1: 17 | fd.write(b"\0" * (padd_size - 1)) 18 | 19 | # Initialise checksum. 20 | xsum_state = 0xEF 21 | 22 | fd.seek(1, os.SEEK_SET) 23 | seg_num = fd.read(1)[0] 24 | 25 | 26 | def readword(): 27 | raw = fd.read(4) 28 | return raw[0] + (raw[1] << 8) + (raw[2] << 16) + (raw[3] << 24) 29 | 30 | 31 | # Compute checksum. 32 | fd.seek(24, os.SEEK_SET) 33 | for _ in range(seg_num): 34 | seg_laddr = readword() 35 | seg_len = readword() 36 | for _ in range(seg_len): 37 | xsum_state ^= fd.read(1)[0] 38 | 39 | # Append checksum. 40 | fd.seek(0, 2) 41 | fd.write(bytes([xsum_state])) 42 | 43 | def main(): 44 | parser = argparse.ArgumentParser() 45 | 46 | parser.add_argument("input", type=Path) 47 | parser.add_argument("output", type=Path) 48 | 49 | args = parser.parse_args() 50 | 51 | input_file: Path = args.input 52 | output_file: Path = args.output 53 | 54 | try: 55 | shutil.copyfile(input_file, output_file) 56 | 57 | # Open the generated file for appending a checksum. 58 | with output_file.open("ab+") as fd: 59 | patchElf(fd) 60 | 61 | except: 62 | try: 63 | output_file.unlink() 64 | except: 65 | pass 66 | raise 67 | 68 | if __name__ == "__main__": 69 | main() -------------------------------------------------------------------------------- /tools/ramfs-gen.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import sys, os 4 | 5 | if len(sys.argv) != 4: 6 | print("Usage: ramfs-gen.py [indir] [outfile] [name]") 7 | exit(1) 8 | 9 | outfd = open(sys.argv[2], "w") 10 | 11 | outfd.write("// WARNING: This is a generated file, do not edit it!\n") 12 | outfd.write("// clang-format off\n") 13 | outfd.write("// NOLINTBEGIN\n") 14 | outfd.write("#include \n") 15 | outfd.write("#include \n") 16 | outfd.write("#include \"filesystem.h\"\n") 17 | outfd.write("#include \"badge_err.h\"\n") 18 | outfd.write("#include \"assertions.h\"\n") 19 | 20 | roms="" 21 | dirs="" 22 | files="" 23 | file_count=0 24 | 25 | 26 | 27 | def escape(raw: str): 28 | return raw\ 29 | .replace('\\', '\\\\')\ 30 | .replace('\r', '\\r')\ 31 | .replace('\n', '\\n')\ 32 | .replace('"', '\\"') 33 | 34 | 35 | def add_rom(path, virtpath, name): 36 | global roms, files 37 | infd = open(path, "rb") 38 | data = infd.read() 39 | roms += "uint8_t const {}[] = {{\n ".format(name) 40 | for byte in data: 41 | roms += "0x{:02x},".format(byte) 42 | roms += "\n};\n" 43 | roms += "fileoff_t const {}_len = {};\n".format(name, len(data)) 44 | files += " fd = fs_open(&ec, FILE_NONE, \"{}\", {}, OFLAGS_CREATE | OFLAGS_WRITEONLY);\n".format(escape(virtpath), len(virtpath)) 45 | files += " badge_err_assert_dev(&ec);\n" 46 | files += " len = fs_write(&ec, fd, {}, {}_len);\n".format(name, name) 47 | files += " badge_err_assert_dev(&ec);\n" 48 | files += " assert_dev_drop(len == {}_len);\n".format(name) 49 | files += " fs_close(&ec, fd);\n" 50 | files += " badge_err_assert_dev(&ec);\n" 51 | infd.close() 52 | 53 | 54 | def add_dir(path, virtpath): 55 | global dirs, file_count 56 | for filename in os.listdir(path): 57 | if os.path.isdir(path + "/" + filename): 58 | dirs += " fs_mkdir(&ec, FILE_NONE, \"{}\", {});\n".format(escape(virtpath + "/" + filename), len(virtpath) + 1 + len(filename)) 59 | dirs += " badge_err_assert_dev(&ec);\n" 60 | add_dir(path + "/" + filename, virtpath + "/" + filename) 61 | else: 62 | add_rom(path + "/" + filename, virtpath + "/" + filename, "filerom_{}".format(file_count)) 63 | file_count += 1 64 | 65 | 66 | add_dir(sys.argv[1], "") 67 | outfd.write(roms) 68 | outfd.write("void {}() {{\n".format(sys.argv[3])) 69 | outfd.write(" badge_err_t ec = {0};\n") 70 | outfd.write(" file_t fd;\n") 71 | outfd.write(" fileoff_t len;\n") 72 | outfd.write(dirs) 73 | outfd.write(files) 74 | outfd.write(" (void)len;\n") 75 | outfd.write("}\n") 76 | outfd.write("// NOLINTEND\n") 77 | outfd.flush() 78 | outfd.close() 79 | --------------------------------------------------------------------------------