├── .clang-format ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── codeql │ └── codeql-config.yml └── workflows │ ├── build.yml │ └── codeql.yml ├── .gitignore ├── .gitlab-ci.yml ├── .mailmap ├── CMakeLists.txt ├── LICENSE-GPL ├── LICENSE-MPL ├── Makefile ├── README.md └── src ├── atomics.h ├── bindings ├── jni │ ├── lekkit │ │ └── rvvm │ │ │ ├── DS1742.java │ │ │ ├── Framebuffer.java │ │ │ ├── GoldfishRTC.java │ │ │ ├── HIDKeyboard.java │ │ │ ├── HIDMouse.java │ │ │ ├── I2CBus.java │ │ │ ├── IGPIODevice.java │ │ │ ├── IRemovableDevice.java │ │ │ ├── MMIODevice.java │ │ │ ├── MTDFlash.java │ │ │ ├── NS16550A.java │ │ │ ├── NVMeDrive.java │ │ │ ├── PCIBus.java │ │ │ ├── PCIDevice.java │ │ │ ├── PLIC.java │ │ │ ├── RTL8169.java │ │ │ ├── RVVMMachine.java │ │ │ ├── RVVMNative.java │ │ │ ├── SiFiveGPIO.java │ │ │ └── Syscon.java │ ├── rvvm_jni.c │ └── tiny-jni.h ├── libretro │ ├── libretro.c │ ├── libretro.h │ └── rvvm_libretro.info └── macos_codesign │ ├── codesign.sh │ ├── rvvm_debug.entitlements │ └── rvvm_isolement.entitlements ├── bit_ops.h ├── blk_io.c ├── blk_io.h ├── compiler.h ├── cpu ├── fpu_lib.h ├── riscv32_interpreter.c ├── riscv64_interpreter.c ├── riscv_atomics.h ├── riscv_base.h ├── riscv_compressed.h ├── riscv_fpu.h ├── riscv_interpreter.h └── riscv_jit.h ├── devices ├── alsa.c ├── ata.c ├── ata.h ├── chardev.h ├── chardev_term.c ├── eth-oc.c ├── eth-oc.h ├── framebuffer.c ├── framebuffer.h ├── gpio-sifive.c ├── gpio-sifive.h ├── gpio_api.h ├── gui_window.c ├── gui_window.h ├── haiku_window.cpp ├── hid-keyboard.c ├── hid-mouse.c ├── hid_api.h ├── hid_dev.h ├── i2c-hid.c ├── i2c-hid.h ├── i2c-oc.c ├── i2c-oc.h ├── mtd-physmap.c ├── mtd-physmap.h ├── ns16550a.c ├── ns16550a.h ├── nvme.c ├── nvme.h ├── pci-bus.c ├── pci-bus.h ├── pci-vfio.c ├── pci-vfio.h ├── ps2-altera.c ├── ps2-altera.h ├── ps2-keyboard.c ├── ps2-mouse.c ├── riscv-aclint.c ├── riscv-aclint.h ├── riscv-aplic.c ├── riscv-aplic.h ├── riscv-imsic.c ├── riscv-imsic.h ├── riscv-plic.c ├── riscv-plic.h ├── rtc-ds1742.c ├── rtc-ds1742.h ├── rtc-goldfish.c ├── rtc-goldfish.h ├── rtl8169.c ├── rtl8169.h ├── sdl_window.c ├── sound-hda.c ├── sound-hda.h ├── syscon.c ├── syscon.h ├── tap_api.h ├── tap_linux.c ├── tap_user.c ├── usb-xhci.c ├── usb-xhci.h ├── wayland_window.c ├── wayland_window.h ├── win32window.c └── x11window_xlib.c ├── dlib.c ├── dlib.h ├── elf_load.c ├── elf_load.h ├── fdtlib.c ├── fdtlib.h ├── fpu_ops.h ├── gdbstub.c ├── gdbstub.h ├── hashmap.c ├── hashmap.h ├── main.c ├── mem_ops.h ├── networking.c ├── networking.h ├── rcu_lib.c ├── rcu_lib.h ├── ringbuf.c ├── ringbuf.h ├── riscv_cpu.c ├── riscv_cpu.h ├── riscv_csr.c ├── riscv_csr.h ├── riscv_hart.c ├── riscv_hart.h ├── riscv_mmu.c ├── riscv_mmu.h ├── riscv_priv.c ├── riscv_priv.h ├── rvjit ├── rvjit.c ├── rvjit.h ├── rvjit_arm.h ├── rvjit_arm64.h ├── rvjit_emit.c ├── rvjit_emit.h ├── rvjit_riscv.h └── rvjit_x86.h ├── rvtimer.c ├── rvtimer.h ├── rvvm.c ├── rvvm.h ├── rvvm_isolation.c ├── rvvm_isolation.h ├── rvvm_types.h ├── rvvm_user.c ├── rvvm_user.h ├── rvvmlib.h ├── spinlock.c ├── spinlock.h ├── stacktrace.c ├── stacktrace.h ├── threading.c ├── threading.h ├── utils.c ├── utils.h ├── vector.c ├── vector.h ├── vma_ops.c └── vma_ops.h /.clang-format: -------------------------------------------------------------------------------- 1 | # 2 | # Clang-format config enforcing the established RVVM codestyle. 3 | # (Obviously with minor differences, but not for the worse usually) 4 | # 5 | # Requires clang-format 16 or newer. 6 | # Each new added option should document whether it requires a similarly modern version. 7 | # 8 | # Commented out options are helpful in rare situations (Like formatting huge switches), 9 | # and otherwise introduce unwanted codestyle changes. 10 | # Those should be used with care and the code in question later covered with // clang-format off 11 | # 12 | # NOTE: This WILL mess up formatting in some rare cases, namely multi-line macros and 13 | # short case statements like keycode translations. Older clang-format versions might also 14 | # mess everything up on detecting unknown config key, so please update/disable it then. 15 | # 16 | # Please review formatting results before commits, at least for now, and use /**/ for manual 17 | # multi-line macro breaks, or cover in // clang-format off as you see fit. 18 | # 19 | 20 | # Use this config for C, C++ code 21 | Language: Cpp 22 | 23 | # 24 | # File settings 25 | # 26 | 27 | # Wrap at column width 120 28 | ColumnLimit: 120 29 | 30 | # Force LF line ending (clang-format 16) 31 | LineEnding: LF 32 | 33 | # Insert newline at EOF if missing (clang-format 16) 34 | InsertNewlineAtEOF: true 35 | 36 | # 37 | # Language settings 38 | # 39 | 40 | # Always use `type* val` pointer style 41 | PointerAlignment: Left 42 | 43 | # Align pointer qualifiers 44 | SpaceAroundPointerQualifiers: Both 45 | 46 | # Always insert a single newline between function/struct bodies (clang-format 14) 47 | SeparateDefinitionBlocks: Always 48 | 49 | # Keep up to 3 consecutive empty lines between declarations (For header structuring) 50 | MaxEmptyLinesToKeep: 3 51 | 52 | # Break before an operator on arithmetic/logic wrapping 53 | BreakBeforeBinaryOperators: true 54 | BreakBeforeTernaryOperators: true 55 | 56 | # Align arithmetic operands after wrapped operator 57 | AlignOperands: AlignAfterOperator 58 | 59 | # Set to ControlStatementsExceptControlMacros if you ever wish to remove space in vector_foreach() and the likes 60 | SpaceBeforeParens: ControlStatements 61 | 62 | # Allow single-line function declaration for empty body (Stubs) 63 | AllowShortFunctionsOnASingleLine: Empty 64 | 65 | # Never allow single-line loops 66 | AllowShortLoopsOnASingleLine: false 67 | 68 | # No spaces around elements in a braced list initializer, {0, 1, 2, 3} 69 | Cpp11BracedListStyle: true 70 | SpaceBeforeCpp11BracedList: true 71 | 72 | # 73 | # Indentation settings 74 | # 75 | 76 | # Always use 4 spaces for indentation 77 | TabWidth: 4 78 | IndentWidth: 4 79 | ContinuationIndentWidth: 4 80 | UseTab: Never 81 | 82 | # Indent switch case labels 83 | IndentCaseLabels: true 84 | 85 | # 86 | # Braces handling 87 | # 88 | 89 | # Enforce braces after control flow statements 90 | InsertBraces: true 91 | 92 | # Only break before function body braces, distantly similar to Linux 93 | BreakBeforeBraces: Custom 94 | BraceWrapping: 95 | AfterCaseLabel: false 96 | AfterClass: false 97 | AfterControlStatement: Never 98 | AfterEnum: false 99 | AfterFunction: true 100 | AfterNamespace: false 101 | AfterStruct: false 102 | AfterUnion: false 103 | AfterExternBlock: false 104 | BeforeCatch: false 105 | BeforeElse: false 106 | BeforeWhile: false 107 | IndentBraces: false 108 | 109 | # 110 | # Pretty alignment 111 | # 112 | 113 | # Table-like alignment for function declarations and function pointers 114 | AlignConsecutiveDeclarations: 115 | Enabled: true 116 | AcrossEmptyLines: false 117 | AcrossComments: false 118 | AlignFunctionPointers: true 119 | 120 | # Table-like alignment for assignments (With operator padding) 121 | AlignConsecutiveAssignments: 122 | Enabled: true 123 | AcrossEmptyLines: false 124 | AcrossComments: false 125 | AlignCompound: true 126 | PadOperators: true 127 | 128 | # Table-like alignment for definitions and comments 129 | AlignConsecutiveMacros: AcrossEmptyLinesAndComments 130 | AlignTrailingComments: true 131 | 132 | # Uniformly spread function arguments on wrapping 133 | BinPackArguments: true 134 | BinPackParameters: true 135 | 136 | # Indent function arguments to open bracket on wrapping 137 | AlignAfterOpenBracket: Align 138 | 139 | # Right-align structure fields in structure array initializer (clang-format 13) 140 | AlignArrayOfStructures: Right 141 | 142 | # 143 | # Formatting rules specifically for C++ code 144 | # 145 | 146 | # Format range-based for loops as `for (auto v : vector) {}` 147 | SpaceBeforeRangeBasedForLoopColon: true 148 | 149 | # Always split template declarations into separate lines 150 | AlwaysBreakTemplateDeclarations: true 151 | 152 | # Do not indent class access modifiers 153 | AccessModifierOffset: -4 154 | 155 | # 156 | # Possibly helpful, but not yet used options 157 | # 158 | 159 | # Allow short case labels to fit onto a single line, helps with huge translation switches 160 | # NOTE: This messes up a lot of code, disable and use occasionally, then clang-format off 161 | #AllowShortCaseLabelsOnASingleLine: true 162 | 163 | # Align short case statements (clang-format 17) 164 | #AlignConsecutiveShortCaseStatements: 165 | # Enabled: true 166 | # AcrossEmptyLines: true 167 | # AcrossComments: true 168 | # AlignCaseColons: false 169 | 170 | # Do not format macro bodies, might be useful sometimes (clang-format 18) 171 | # NOTE: This breaks AlignConsecutiveMacros as of clang-format 19 172 | #SkipMacroDefinitionBody: true 173 | 174 | # 175 | # RVVM-specific handling of attribute, foreach-like and scoped macros. 176 | # 177 | # The vector_foreach(), vector_foreach_back() macros are from `src/vector.h` 178 | # The hashmap_foreach() macro is from `src/hashmap.h` 179 | # 180 | # Scoped locking helpers are from `src/spinlock.h` and `src/rcu_lib.h` respectively 181 | # 182 | # DO_ONCE_SCOPED {} macro is from `src/utils.h` 183 | # 184 | # All other macros (attributes, CHECK_INCLUDE() and scoped helpers) are found in `src/compiler.h` 185 | # 186 | 187 | # Do not break before braces for following foreach-like macros 188 | ForEachMacros: [vector_foreach, vector_foreach_back, hashmap_foreach, scoped_spin_lock, 189 | scoped_spin_try_lock, scoped_spin_read_lock, scoped_spin_try_read_lock, 190 | scoped_spin_lock_slow, scoped_spin_read_lock_slow, scoped_rcu_reader, 191 | DO_ONCE_SCOPED, BREAKABLE_SCOPE, SCOPED_COND_NAMED. POST_COND_NAMED, 192 | SCOPED_STMT_NAMED, POST_STMT_NAMED, SCOPED_COND, POST_COND, SCOPED_STMT, 193 | POST_STMT, SCOPED_HELPER] 194 | 195 | # Template vector type 196 | TypenameMacros: [vector_t] 197 | 198 | # Do not mess up my string-handling macros! 199 | WhitespaceSensitiveMacros: [CHECK_INCLUDE, MACRO_TOSTRING, MACRO_CONCAT] 200 | 201 | # Attribute-like macros 202 | StatementAttributeLikeMacros: [forceinline, no_inline, slow_path, flatten_calls, align_cacheline, 203 | randomize_layout, safe_aliasing, warn_unused_ret, deallocate_with, 204 | TSAN_SUPPRESS, MSAN_SUPPRESS, GNU_DESTRUCTOR, GNU_CONSTRUCTOR] 205 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | ### The issue 11 | - This doesn't work right ... 12 | 13 | ### Steps to reproduce 14 | - Use this guest image ... 15 | - Run `rvvm fw_jump.bin -m 1G -k ...` 16 | - Wait till the guest boots 17 | - Run this command ... 18 | - Observe the issue 19 | 20 | ### Investigation 21 | - I stumbled upon this when ... 22 | - This worked previously 23 | - This may have been caused by a recent commit 24 | 25 | ### Workarounds 26 | - The issue doesn't manifest when we disable ... 27 | - Reverting this commit fixes it 28 | 29 | ### Suggested fix / Expected behavior 30 | - This may be fixed by ... 31 | - According to RISC-V spec ... 32 | - I think it should be done like this ... 33 | 34 | ### Additional info 35 | - Reference RVVM commit (Shown in `rvvm -h`, guest `/proc/cpuinfo`, machine name) 36 | - Host OS/Architecture (If applies) 37 | - Links to additional material 38 | 39 | Feel free to remove unused sections 40 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | ### Exact requirements 11 | - I want to improve efficiency of ... 12 | 13 | ### Suggested solution 14 | - Emulate this device ... 15 | - Switch to it in default RVVM board ... 16 | 17 | ### Expected results and side effects 18 | - The Linux guests will be using driver ... 19 | - This enables networking with Haiku guests ... 20 | - This may break some guests which ... 21 | - This could improve performance on Windows hosts 22 | 23 | ### Alternatives 24 | - Existing RVVM solution differs to this ... 25 | - Another project X solves this by ... 26 | -------------------------------------------------------------------------------- /.github/codeql/codeql-config.yml: -------------------------------------------------------------------------------- 1 | name: "CodeQL config" 2 | queries: 3 | - uses: security-extended 4 | - uses: security-and-quality 5 | query-filters: 6 | - exclude: 7 | id: cpp/path-injection 8 | - exclude: 9 | id: cpp/commented-out-code 10 | - exclude: 11 | id: cpp/long-switch 12 | - exclude: 13 | id: cpp/equality-on-floats 14 | - exclude: 15 | id: cpp/include-non-header 16 | - exclude: 17 | id: cpp/complex-block 18 | - exclude: 19 | id: cpp/trivial-switch 20 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: [ "staging" ] 7 | pull_request: 8 | branches: [ "staging" ] 9 | schedule: 10 | - cron: "0 0 * * *" 11 | 12 | env: 13 | CFLAGS: -Werror 14 | 15 | jobs: 16 | linux-x86_64-build: 17 | runs-on: ubuntu-latest 18 | steps: 19 | - name: Checkout 20 | uses: actions/checkout@v4.1.1 21 | - name: Install libX11 22 | run: sudo DEBIAN_FRONTEND=noninteractive apt-get install -yq --no-install-recommends libx11-dev libxext-dev 23 | - name: Build 24 | run: make 25 | - name: Run RISC-V Tests 26 | run: timeout 600 make test 27 | - name: Upload Artifacts 28 | uses: actions/upload-artifact@v4.3.1 29 | with: 30 | retention-days: 5 31 | name: rvvm_linux_x86_64 32 | path: | 33 | release.linux.x86_64/rvvm_x86_64 34 | release.linux.x86_64/librvvm.so 35 | release.linux.x86_64/librvvm_static.a 36 | 37 | win32-x86_64-build: 38 | runs-on: ubuntu-latest 39 | steps: 40 | - name: Checkout 41 | uses: actions/checkout@v4.1.1 42 | - name: Set up MinGW 43 | run: sudo DEBIAN_FRONTEND=noninteractive apt-get install -yq --no-install-recommends gcc-mingw-w64-x86-64-win32 44 | - name: Build 45 | run: make CC=x86_64-w64-mingw32-gcc 46 | - name: Upload Artifacts 47 | uses: actions/upload-artifact@v4.3.1 48 | with: 49 | retention-days: 5 50 | name: rvvm_win32_x86_64 51 | path: | 52 | release.windows.x86_64/rvvm_x86_64.exe 53 | release.windows.x86_64/librvvm.dll 54 | release.windows.x86_64/librvvm_static.a 55 | 56 | macos-arm64-build: 57 | runs-on: macos-latest 58 | env: 59 | SDL2_TAR_LINK: https://github.com/libsdl-org/SDL/releases/download/release-2.30.9/SDL2-2.30.9.tar.gz 60 | SDL2_NAME: SDL2-2.30.9 61 | CFLAGS: -target arm64-apple-macos11 -Werror -ISDL2-2.30.9/include 62 | steps: 63 | - name: Checkout 64 | uses: actions/checkout@v4.1.1 65 | - name: Download SDL2 headers 66 | run: wget ${{env.SDL2_TAR_LINK}} -O - | tar -xzf - && mv ${{env.SDL2_NAME}}/include src/SDL2 67 | - name: Build 68 | run: make 69 | - name: Upload Artifacts 70 | uses: actions/upload-artifact@v4.3.1 71 | with: 72 | retention-days: 5 73 | name: rvvm_macos_arm64 74 | path: | 75 | release.darwin.arm64/rvvm_arm64 76 | release.darwin.arm64/librvvm.dylib 77 | release.darwin.arm64/librvvm_static.a 78 | 79 | macos-x86_64-build: 80 | runs-on: macos-latest 81 | env: 82 | SDL2_TAR_LINK: https://github.com/libsdl-org/SDL/releases/download/release-2.30.9/SDL2-2.30.9.tar.gz 83 | SDL2_NAME: SDL2-2.30.9 84 | CFLAGS: -target x86_64-apple-macos10.12 -Werror -ISDL2-2.30.9/include 85 | steps: 86 | - name: Checkout 87 | uses: actions/checkout@v4.1.1 88 | - name: Download SDL2 headers 89 | run: wget ${{env.SDL2_TAR_LINK}} -O - | tar -xzf - && mv ${{env.SDL2_NAME}}/include src/SDL2 90 | - name: Build 91 | run: make 92 | - name: Upload Artifacts 93 | uses: actions/upload-artifact@v4.3.1 94 | with: 95 | retention-days: 5 96 | name: rvvm_macos_x86_64 97 | path: | 98 | release.darwin.x86_64/rvvm_x86_64 99 | release.darwin.x86_64/librvvm.dylib 100 | release.darwin.x86_64/librvvm_static.a 101 | -------------------------------------------------------------------------------- /.github/workflows/codeql.yml: -------------------------------------------------------------------------------- 1 | name: "CodeQL" 2 | 3 | on: 4 | push: 5 | branches: [ "staging" ] 6 | pull_request: 7 | branches: [ "staging" ] 8 | schedule: 9 | - cron: "52 7 * * 4" 10 | 11 | jobs: 12 | analyze: 13 | name: Analyze 14 | runs-on: ubuntu-latest 15 | permissions: 16 | actions: read 17 | contents: read 18 | security-events: write 19 | 20 | strategy: 21 | fail-fast: false 22 | matrix: 23 | language: [ cpp ] 24 | 25 | steps: 26 | - name: Checkout 27 | uses: actions/checkout@v3 28 | 29 | - name: Initialize CodeQL 30 | uses: github/codeql-action/init@v3 31 | with: 32 | languages: cpp 33 | config-file: ./.github/codeql/codeql-config.yml 34 | 35 | - name: Autobuild 36 | uses: github/codeql-action/autobuild@v3 37 | 38 | - name: Perform CodeQL Analysis 39 | uses: github/codeql-action/analyze@v3 40 | with: 41 | category: "/language:${{ matrix.language }}" 42 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # 2 | # To clean up the repo, run `git clean -Xdf` 3 | # 4 | 5 | # Makefile default build paths 6 | /release.*/ 7 | /debug.*/ 8 | 9 | # CMake default build path 10 | /build/ 11 | 12 | # CMake garbage if someone tries in-source build 13 | CMakeFiles/ 14 | CMakeCache.txt 15 | cmake_install.cmake 16 | Makefile* 17 | 18 | # Autogenerated compile_flags.txt for Clang infrastructure 19 | compile_flags.txt 20 | 21 | # Assets for WASM build 22 | /wasm/ 23 | 24 | # Temporary local stuff 25 | /tmp/ 26 | 27 | # Clangd cache 28 | /.cache/ 29 | 30 | # VSCode editor temp files 31 | /.vscode/ 32 | 33 | # Kate editor temp files 34 | *.kate-swp 35 | 36 | # Patches stuff 37 | /*.patch 38 | *.rej 39 | *.orig 40 | 41 | # Random stuff on Windows build host 42 | NUL 43 | 44 | # Explicitly allow our main makefile 45 | !/Makefile 46 | -------------------------------------------------------------------------------- /.mailmap: -------------------------------------------------------------------------------- 1 | 0xCatPKG <0xCatPKG@rvvm.dev> 2 | 0xCatPKG <0xCatPKG@rvvm.dev> <42083774+PacketCat@users.noreply.github.com> 3 | 0xCatPKG <0xCatPKG@rvvm.dev> <42083774+0xCatPKG@users.noreply.github.com> 4 | -------------------------------------------------------------------------------- /src/bindings/jni/lekkit/rvvm/DS1742.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 5 | */ 6 | 7 | package lekkit.rvvm; 8 | 9 | public class DS1742 extends MMIODevice { 10 | public DS1742(RVVMMachine machine) { 11 | super(machine); 12 | if (machine.isValid()) { 13 | setMMIOHandle(RVVMNative.rtc_ds1742_init_auto(machine.getPtr())); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/bindings/jni/lekkit/rvvm/Framebuffer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 5 | */ 6 | 7 | package lekkit.rvvm; 8 | 9 | import java.nio.ByteBuffer; 10 | 11 | public class Framebuffer extends MMIODevice { 12 | protected int width; 13 | protected int height; 14 | protected int bpp; 15 | protected ByteBuffer buff; 16 | 17 | public static final int BPP_R5G6B5 = 16; 18 | public static final int BPP_R8G8B8 = 24; 19 | public static final int BPP_A8R8G8B8 = 32; 20 | 21 | public Framebuffer(RVVMMachine machine, int x, int y) { 22 | this(machine, x, y, BPP_A8R8G8B8); 23 | } 24 | 25 | public Framebuffer(RVVMMachine machine, int x, int y, int bpp) { 26 | super(machine); 27 | this.width = x; 28 | this.height = y; 29 | this.bpp = bpp; 30 | this.buff = ByteBuffer.allocateDirect(x * y * (bpp / 8)); 31 | if (machine.isValid()) { 32 | ByteBuffer[] arr = { null }; 33 | setMMIOHandle(RVVMNative.framebuffer_init_auto(machine.getPtr(), arr, x, y, bpp)); 34 | buff = arr[0]; 35 | } 36 | } 37 | 38 | public ByteBuffer getBuffer() { 39 | return buff; 40 | } 41 | 42 | public int getWidth() { 43 | return width; 44 | } 45 | 46 | public int getHeight() { 47 | return height; 48 | } 49 | 50 | public int getBpp() { 51 | return bpp; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/bindings/jni/lekkit/rvvm/GoldfishRTC.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 5 | */ 6 | 7 | package lekkit.rvvm; 8 | 9 | public class GoldfishRTC extends MMIODevice { 10 | public GoldfishRTC(RVVMMachine machine) { 11 | super(machine); 12 | if (machine.isValid()) { 13 | setMMIOHandle(RVVMNative.rtc_goldfish_init_auto(machine.getPtr())); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/bindings/jni/lekkit/rvvm/HIDMouse.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 5 | */ 6 | 7 | package lekkit.rvvm; 8 | 9 | public class HIDMouse { 10 | private final RVVMMachine machine; 11 | private final long hid_mouse; 12 | 13 | public static final byte HID_BTN_NONE = 0; 14 | public static final byte HID_BTN_LEFT = 1; 15 | public static final byte HID_BTN_RIGHT = 2; 16 | public static final byte HID_BTN_MIDDLE = 4; 17 | 18 | public static final int HID_SCROLL_UP = -1; 19 | public static final int HID_SCROLL_DOWN = 1; 20 | 21 | public HIDMouse(RVVMMachine machine) { 22 | if (machine.isValid()) { 23 | this.machine = machine; 24 | this.hid_mouse = RVVMNative.hid_mouse_init_auto(machine.getPtr()); 25 | } else { 26 | this.machine = null; 27 | this.hid_mouse = 0; 28 | } 29 | } 30 | 31 | public boolean isValid() { 32 | return hid_mouse != 0; 33 | } 34 | 35 | public void move(int x, int y) { 36 | if (isValid()) { 37 | RVVMNative.hid_mouse_move(hid_mouse, x, y); 38 | } 39 | } 40 | public void place(int x, int y) { 41 | if (isValid()) { 42 | RVVMNative.hid_mouse_place(hid_mouse, x, y); 43 | } 44 | } 45 | public void resolution(int x, int y) { 46 | if (isValid()) { 47 | RVVMNative.hid_mouse_resolution(hid_mouse, x, y); 48 | } 49 | } 50 | public void press(byte btns) { 51 | if (isValid()) { 52 | RVVMNative.hid_mouse_press(hid_mouse, btns); 53 | } 54 | } 55 | public void release(byte btns) { 56 | if (isValid()) { 57 | RVVMNative.hid_mouse_release(hid_mouse, btns); 58 | } 59 | } 60 | public void scroll(int offset) { 61 | if (isValid()) { 62 | RVVMNative.hid_mouse_scroll(hid_mouse, offset); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/bindings/jni/lekkit/rvvm/I2CBus.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 5 | */ 6 | 7 | package lekkit.rvvm; 8 | 9 | public class I2CBus { 10 | public I2CBus(RVVMMachine machine) { 11 | if (machine.isValid()) { 12 | RVVMNative.i2c_bus_init_auto(machine.getPtr()); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/bindings/jni/lekkit/rvvm/IGPIODevice.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 5 | */ 6 | 7 | package lekkit.rvvm; 8 | 9 | public interface IGPIODevice { 10 | 11 | public boolean write_pins(int offset, int pins); 12 | 13 | public int read_pins(int offset); 14 | 15 | public boolean write_pins(int pins); 16 | 17 | public int read_pins(); 18 | } 19 | -------------------------------------------------------------------------------- /src/bindings/jni/lekkit/rvvm/IRemovableDevice.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 5 | */ 6 | 7 | package lekkit.rvvm; 8 | 9 | public interface IRemovableDevice { 10 | 11 | public boolean isValid(); 12 | 13 | public void remove(); 14 | } 15 | -------------------------------------------------------------------------------- /src/bindings/jni/lekkit/rvvm/MMIODevice.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 5 | */ 6 | 7 | package lekkit.rvvm; 8 | 9 | public abstract class MMIODevice implements IRemovableDevice { 10 | private final RVVMMachine machine; 11 | private long mmio_dev = 0; 12 | 13 | public MMIODevice(RVVMMachine machine) { 14 | this.machine = machine; 15 | } 16 | 17 | public RVVMMachine getMachine() { 18 | return machine; 19 | } 20 | 21 | protected void setMMIOHandle(long mmio_dev) { 22 | this.mmio_dev = mmio_dev; 23 | } 24 | 25 | public boolean isValid() { 26 | return machine.isValid() && this.mmio_dev != 0; 27 | } 28 | 29 | public synchronized void remove() { 30 | if (isValid()) { 31 | RVVMNative.remove_mmio(this.mmio_dev); 32 | mmio_dev = 0; 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/bindings/jni/lekkit/rvvm/MTDFlash.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 5 | */ 6 | 7 | package lekkit.rvvm; 8 | 9 | public class MTDFlash extends MMIODevice { 10 | public MTDFlash(RVVMMachine machine, String imagePath, boolean rw) { 11 | super(machine); 12 | if (machine.isValid()) { 13 | setMMIOHandle(RVVMNative.mtd_physmap_init_auto(machine.getPtr(), imagePath, rw)); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/bindings/jni/lekkit/rvvm/NS16550A.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 5 | */ 6 | 7 | package lekkit.rvvm; 8 | 9 | public class NS16550A extends MMIODevice { 10 | public NS16550A(RVVMMachine machine) { 11 | super(machine); 12 | if (machine.isValid()) { 13 | setMMIOHandle(RVVMNative.ns16550a_init_auto(machine.getPtr())); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/bindings/jni/lekkit/rvvm/NVMeDrive.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 5 | */ 6 | 7 | package lekkit.rvvm; 8 | 9 | public class NVMeDrive extends PCIDevice { 10 | public NVMeDrive(RVVMMachine machine, String imagePath, boolean rw) { 11 | super(machine); 12 | if (machine.isValid()) { 13 | long pci_bus = RVVMNative.get_pci_bus(machine.getPtr()); 14 | if (pci_bus != 0) { 15 | setPCIHandle(RVVMNative.nvme_init(pci_bus, imagePath, rw)); 16 | } 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/bindings/jni/lekkit/rvvm/PCIBus.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 5 | */ 6 | 7 | package lekkit.rvvm; 8 | 9 | public class PCIBus { 10 | public PCIBus(RVVMMachine machine) { 11 | if (machine.isValid()) { 12 | RVVMNative.pci_bus_init_auto(machine.getPtr()); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/bindings/jni/lekkit/rvvm/PCIDevice.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 5 | */ 6 | 7 | package lekkit.rvvm; 8 | 9 | public abstract class PCIDevice implements IRemovableDevice { 10 | private final RVVMMachine machine; 11 | private long pci_dev; 12 | 13 | public PCIDevice(RVVMMachine machine) { 14 | this.machine = machine; 15 | } 16 | 17 | public RVVMMachine getMachine() { 18 | return machine; 19 | } 20 | 21 | protected void setPCIHandle(long pci_dev) { 22 | this.pci_dev = pci_dev; 23 | } 24 | 25 | public boolean isValid() { 26 | return machine.isValid() && pci_dev != 0; 27 | } 28 | 29 | public synchronized void remove() { 30 | if (isValid()) { 31 | RVVMNative.pci_remove_device(pci_dev); 32 | pci_dev = 0; 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/bindings/jni/lekkit/rvvm/PLIC.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 5 | */ 6 | 7 | package lekkit.rvvm; 8 | 9 | public class PLIC { 10 | public PLIC(RVVMMachine machine) { 11 | if (machine.isValid()) { 12 | RVVMNative.riscv_plic_init_auto(machine.getPtr()); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/bindings/jni/lekkit/rvvm/RTL8169.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 5 | */ 6 | 7 | package lekkit.rvvm; 8 | 9 | public class RTL8169 extends PCIDevice { 10 | public RTL8169(RVVMMachine machine) { 11 | super(machine); 12 | if (machine.isValid()) { 13 | long pci_bus = RVVMNative.get_pci_bus(machine.getPtr()); 14 | if (pci_bus != 0) { 15 | long tap = RVVMNative.tap_user_open(); 16 | if (tap != 0) { 17 | setPCIHandle(RVVMNative.rtl8169_init(pci_bus,tap)); 18 | } 19 | } 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/bindings/jni/lekkit/rvvm/RVVMMachine.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 5 | */ 6 | 7 | package lekkit.rvvm; 8 | 9 | import java.nio.ByteBuffer; 10 | 11 | public class RVVMMachine { 12 | private long machine = 0; 13 | 14 | public static final int RVVM_OPT_NONE = 0x0; 15 | public static final int RVVM_OPT_RESET_PC = 0x1; //!< Physical jump address at reset, defaults to 0x80000000 16 | public static final int RVVM_OPT_DTB_ADDR = 0x2; //!< Pass DTB address if non-zero, omits FDT generation 17 | public static final int RVVM_OPT_TIME_FREQ = 0x3; //!< Machine timer frequency, 10Mhz by default 18 | public static final int RVVM_OPT_HW_IMITATE = 0x4; //!< Imitate traits or identity of physical hardware 19 | public static final int RVVM_OPT_MAX_CPU_CENT = 0x5; //!< Max CPU load % per guest/host CPUs 20 | public static final int RVVM_OPT_JIT = 0x6; //!< Enable JIT 21 | public static final int RVVM_OPT_JIT_CACHE = 0x7; //!< Amount of per-core JIT cache (In bytes) 22 | public static final int RVVM_OPT_JIT_HARVARD = 0x8; //!< No dirty code tracking, explicit ifence, slower 23 | 24 | public static final int RVVM_OPT_MEM_BASE = 0x80000001; //!< Physical RAM base address, defaults to 0x80000000 25 | public static final int RVVM_OPT_MEM_SIZE = 0x80000002; //!< Physical RAM size 26 | public static final int RVVM_OPT_HART_COUNT = 0x80000003; //!< Amount of harts 27 | 28 | public RVVMMachine(long mem_mb, int smp, String isa) { 29 | if (RVVMNative.isLoaded()) { 30 | this.machine = RVVMNative.create_machine(mem_mb << 20, smp, isa); 31 | } 32 | 33 | if (isValid()) { 34 | RVVMNative.riscv_clint_init_auto(machine); 35 | } 36 | } 37 | 38 | public boolean isValid() { 39 | return machine != 0; 40 | } 41 | 42 | public long getPtr() { 43 | return machine; 44 | } 45 | 46 | public ByteBuffer getDmaBuffer(long addr, long size) { 47 | if (isValid()) { 48 | return RVVMNative.get_dma_buf(machine, addr, size); 49 | } 50 | return null; 51 | } 52 | 53 | public void setCmdline(String cmdline) { 54 | if (isValid()) { 55 | RVVMNative.set_cmdline(machine, cmdline); 56 | } 57 | } 58 | 59 | public void appendCmdline(String cmdline) { 60 | if (isValid()) { 61 | RVVMNative.append_cmdline(machine, cmdline); 62 | } 63 | } 64 | 65 | public long getOption(int opt) { 66 | if (isValid()) { 67 | return RVVMNative.get_opt(machine, opt); 68 | } 69 | return 0; 70 | } 71 | 72 | public void setOption(int opt, long val) { 73 | if (isValid()) { 74 | RVVMNative.set_opt(machine, opt, val); 75 | } 76 | } 77 | 78 | public boolean loadBootrom(String path) { 79 | if (isValid()) { 80 | return RVVMNative.load_bootrom(machine, path); 81 | } 82 | return false; 83 | } 84 | 85 | public boolean loadKernel(String path) { 86 | if (isValid()) { 87 | return RVVMNative.load_kernel(machine, path); 88 | } 89 | return false; 90 | } 91 | 92 | public boolean loadDeviceTree(String path) { 93 | if (isValid()) { 94 | return RVVMNative.load_dtb(machine, path); 95 | } 96 | return false; 97 | } 98 | 99 | public boolean dumpDeviceTree(String path) { 100 | if (isValid()) { 101 | return RVVMNative.dump_dtb(machine, path); 102 | } 103 | return false; 104 | } 105 | 106 | public boolean start() { 107 | if (isValid()) { 108 | return RVVMNative.start_machine(machine); 109 | } 110 | return false; 111 | } 112 | 113 | public boolean reset() { 114 | if (isValid()) { 115 | return RVVMNative.reset_machine(machine, true); 116 | } 117 | return false; 118 | } 119 | 120 | public boolean poweroff() { 121 | if (isValid()) { 122 | return RVVMNative.reset_machine(machine, false); 123 | } 124 | return false; 125 | } 126 | 127 | public boolean pause() { 128 | if (isValid()) { 129 | return RVVMNative.pause_machine(machine); 130 | } 131 | return false; 132 | } 133 | 134 | public boolean isPowered() { 135 | if (isValid()) { 136 | return RVVMNative.machine_powered(machine); 137 | } 138 | return false; 139 | } 140 | 141 | // Beware to drop all references to devices owned by this machine beforehand 142 | public synchronized void free() { 143 | if (isValid()) { 144 | RVVMNative.free_machine(machine); 145 | machine = 0; 146 | } 147 | } 148 | 149 | // Should not be relied upon 150 | @Override 151 | protected synchronized void finalize() { 152 | free(); 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /src/bindings/jni/lekkit/rvvm/RVVMNative.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 5 | */ 6 | 7 | package lekkit.rvvm; 8 | 9 | import java.nio.ByteBuffer; 10 | 11 | // Regenerate C prototypes: $ javac -h . RVVMNative.java 12 | 13 | public class RVVMNative { 14 | // Do not crash the JVM if we failed to load native lib 15 | public static boolean loaded = false; 16 | 17 | private static void checkABI() { 18 | if (check_abi(9)) { 19 | loaded = true; 20 | } else { 21 | System.out.println("ERROR: Invalid librvvm ABI version! Please update your JNI bindings!"); 22 | } 23 | } 24 | 25 | // Manually load librvvm 26 | public static boolean loadLib(String path) { 27 | if (loaded) return true; 28 | try { 29 | System.load(path); 30 | checkABI(); 31 | } catch (Throwable e) { 32 | System.out.println("ERROR: Failed to load librvvm: " + e.toString()); 33 | } 34 | return loaded; 35 | } 36 | 37 | static { 38 | try { 39 | System.loadLibrary("rvvm"); 40 | checkABI(); 41 | } catch (Throwable e) { 42 | System.out.println("INFO: Failed to load system-wide librvvm: " + e.toString()); 43 | } 44 | } 45 | 46 | public static boolean isLoaded() { 47 | return loaded; 48 | } 49 | 50 | // 51 | // Common RVVM API functions 52 | // 53 | 54 | public static native boolean check_abi(int abi); 55 | 56 | /// 57 | // RVVM Machine Management API 58 | // 59 | 60 | public static native long create_machine(long mem_size, int smp, String isa); 61 | public static native void set_cmdline(long machine, String cmdline); 62 | public static native void append_cmdline(long machine, String cmdline); 63 | public static native boolean load_bootrom(long machine, String path); 64 | public static native boolean load_kernel(long machine, String path); 65 | public static native boolean load_dtb(long machine, String path); 66 | public static native boolean dump_dtb(long machine, String path); 67 | public static native long get_opt(long machine, int opt); 68 | public static native void set_opt(long machine, int opt, long val); 69 | public static native boolean start_machine(long machine); 70 | public static native boolean pause_machine(long machine); 71 | public static native boolean reset_machine(long machine, boolean reset); 72 | public static native boolean machine_running(long machine); 73 | public static native boolean machine_powered(long machine); 74 | public static native void free_machine(long machine); 75 | public static native void run_eventloop(); 76 | 77 | // 78 | // RVVM Device API 79 | // 80 | 81 | public static native ByteBuffer get_dma_buf(long machine, long addr, long size); 82 | public static native long mmio_zone_auto(long machine, long addr, long size); 83 | 84 | public static native void remove_mmio(long mmio_dev); 85 | 86 | // Get wired interrupt controller 87 | public static native long get_intc(long machine); 88 | public static native void set_intc(long machine, long intc); 89 | 90 | // Get PCI bus (root complex) 91 | public static native long get_pci_bus(long machine); 92 | public static native void set_pci_bus(long machine, long pci_bus); 93 | 94 | // Get I2C bus 95 | public static native long get_i2c_bus(long machine); 96 | public static native void set_i2c_bus(long machine, long i2c_bus); 97 | 98 | // 99 | // RVVM Devices 100 | // 101 | 102 | public static native void riscv_clint_init_auto(long machine); 103 | public static native void riscv_imsic_init_auto(long machine); 104 | 105 | // Returns wired IRQ controller handle 106 | public static native long riscv_plic_init_auto(long machine); 107 | public static native long riscv_aplic_init_auto(long machine); 108 | 109 | // Returns PCI bus handle 110 | public static native long pci_bus_init_auto(long machine); 111 | 112 | // Returns I2C bus handle 113 | public static native long i2c_bus_init_auto(long machine); 114 | 115 | // Returns TAP device handle 116 | public static native long tap_user_open(); 117 | 118 | // Returns MMIO handle 119 | public static native long syscon_init_auto(long machine); 120 | public static native long rtc_goldfish_init_auto(long machine); 121 | public static native long rtc_ds1742_init_auto(long machine); 122 | public static native long ns16550a_init_auto(long machine); 123 | public static native long gpio_sifive_init_auto(long machine, long gpio); 124 | public static native long mtd_physmap_init_auto(long machine, String image_path, boolean rw); 125 | public static native long framebuffer_init_auto(long machine, ByteBuffer[] fb, int x, int y, int bpp); 126 | 127 | // Returns PCI device handle 128 | public static native long rtl8169_init(long pci_bus, long tap); 129 | public static native long nvme_init(long pci_bus, String image_path, boolean rw); 130 | 131 | // Returns HID mouse handle 132 | public static native long hid_mouse_init_auto(long machine); 133 | 134 | // Returns HID keyboard handle 135 | public static native long hid_keyboard_init_auto(long machine); 136 | 137 | // Returns GPIO handle 138 | public static native long gpio_dev_create(); 139 | 140 | // Takes PCI device hande 141 | public static native void pci_remove_device(long dev); 142 | 143 | // Takes GPIO handle 144 | public static native void gpio_dev_free(long gpio); 145 | public static native int gpio_read_pins(long gpio, int off); 146 | public static native boolean gpio_write_pins(long gpio, int off, int pins); 147 | 148 | // Takes HID mouse handle 149 | public static native void hid_mouse_resolution(long mouse, int x, int y); 150 | public static native void hid_mouse_place(long mouse, int x, int y); 151 | public static native void hid_mouse_move(long mouse, int x, int y); 152 | public static native void hid_mouse_press(long mouse, byte btns); 153 | public static native void hid_mouse_release(long mouse, byte btns); 154 | public static native void hid_mouse_scroll(long mouse, int offset); 155 | 156 | // Takes HID keyboard handle 157 | public static native void hid_keyboard_press(long kb, byte key); 158 | public static native void hid_keyboard_release(long kb, byte key); 159 | } 160 | -------------------------------------------------------------------------------- /src/bindings/jni/lekkit/rvvm/SiFiveGPIO.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 5 | */ 6 | 7 | package lekkit.rvvm; 8 | 9 | public class SiFiveGPIO extends MMIODevice implements IGPIODevice { 10 | private long gpio_dev = 0; 11 | 12 | public SiFiveGPIO(RVVMMachine machine) { 13 | super(machine); 14 | if (machine.isValid()) { 15 | this.gpio_dev = RVVMNative.gpio_dev_create(); 16 | 17 | setMMIOHandle(RVVMNative.gpio_sifive_init_auto(getMachine().getPtr(), this.gpio_dev)); 18 | 19 | if (!this.isValid() && this.gpio_dev != 0) { 20 | RVVMNative.gpio_dev_free(gpio_dev); 21 | this.gpio_dev = 0; 22 | } 23 | } 24 | } 25 | 26 | public boolean write_pins(int offset, int pins) { 27 | if (isValid()) { 28 | return RVVMNative.gpio_write_pins(gpio_dev, offset, pins); 29 | } 30 | return false; 31 | } 32 | 33 | public int read_pins(int offset) { 34 | if (isValid()) { 35 | return RVVMNative.gpio_read_pins(gpio_dev, offset); 36 | } 37 | return 0; 38 | } 39 | 40 | public boolean write_pins(int pins) { 41 | return write_pins(0, pins); 42 | } 43 | 44 | public int read_pins() { 45 | return read_pins(0); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/bindings/jni/lekkit/rvvm/Syscon.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 5 | */ 6 | 7 | package lekkit.rvvm; 8 | 9 | public class Syscon extends MMIODevice { 10 | public Syscon(RVVMMachine machine) { 11 | super(machine); 12 | if (machine.isValid()) { 13 | setMMIOHandle(RVVMNative.syscon_init_auto(machine.getPtr())); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/bindings/libretro/rvvm_libretro.info: -------------------------------------------------------------------------------- 1 | # Software Information 2 | display_name = "RVVM" 3 | display_version = "0.6-git" 4 | authors = "LekKit" 5 | categories = "Virtual machine" 6 | license = "GPLv3+" 7 | permissions = "" 8 | supported_extensions = "rvvm" 9 | 10 | # Hardware Information 11 | manufacturer = "RVVM" 12 | systemname = "RVVM" 13 | systemid = "rvvm" 14 | 15 | # Libretro Features 16 | database = "RVVM" 17 | supports_no_game = "false" 18 | libretro_saves = "false" 19 | cheats = "false" 20 | needs_fullpath = "true" 21 | disk_control = "false" 22 | is_experimental = "true" 23 | 24 | description = "A port of the RVVM (RISC-V Virtual Machine) to libretro." 25 | -------------------------------------------------------------------------------- /src/bindings/macos_codesign/codesign.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | codesign -s - --force --options=runtime --entitlements rvvm_debug.entitlements $@ 3 | -------------------------------------------------------------------------------- /src/bindings/macos_codesign/rvvm_debug.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.cs.disable-library-validation 6 | 7 | com.apple.security.cs.allow-dyld-environment-variables 8 | 9 | com.apple.security.cs.disable-executable-page-protection 10 | 11 | com.apple.security.get-task-allow 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/bindings/macos_codesign/rvvm_isolement.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.cs.allow-dyld-environment-variables 6 | 7 | com.apple.security.get-task-allow 8 | 9 | com.apple.security.app-sandbox 10 | 11 | com.apple.security.inherit 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/cpu/fpu_lib.h: -------------------------------------------------------------------------------- 1 | /* 2 | fpu_lib.h - Floating-point handling library 3 | Copyright (C) 2024 LekKit 4 | 5 | This Source Code Form is subject to the terms of the Mozilla Public 6 | License, v. 2.0. If a copy of the MPL was not distributed with this 7 | file, You can obtain one at https://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | #ifndef RVVM_FPU_LIB_H 11 | #define RVVM_FPU_LIB_H 12 | 13 | #include "compiler.h" 14 | #include "fpu_ops.h" 15 | #include "mem_ops.h" 16 | #include "bit_ops.h" 17 | 18 | /* well... */ 19 | #if defined(_WIN32) && defined(__clang__) 20 | #define _C_COMPLEX_T 21 | typedef _Complex double _C_double_complex; 22 | typedef _Complex float _C_float_complex; 23 | typedef _Complex long double _C_ldouble_complex; 24 | #endif 25 | #include 26 | 27 | #define fpu_isnan(x) isnan(x) 28 | 29 | static forceinline bool fpu_is_snanf(float f) 30 | { 31 | uint32_t i; 32 | memcpy(&i, &f, sizeof(f)); 33 | return fpu_isnan(f) && !bit_check(i, 22); 34 | } 35 | 36 | static forceinline bool fpu_is_snand(double d) 37 | { 38 | uint64_t i; 39 | memcpy(&i, &d, sizeof(d)); 40 | return fpu_isnan(d) && !bit_check(i, 51); 41 | } 42 | 43 | static forceinline bool fpu_signbitf(float f) 44 | { 45 | uint32_t i; 46 | memcpy(&i, &f, sizeof(f)); 47 | return i >> 31; 48 | } 49 | 50 | static forceinline bool fpu_signbitd(double d) 51 | { 52 | uint64_t i; 53 | memcpy(&i, &d, sizeof(d)); 54 | return i >> 63; 55 | } 56 | 57 | static forceinline float fpu_sqrtf(float val) 58 | { 59 | float ret = sqrtf(val); 60 | if (unlikely(val < 0 && !fetestexcept(FE_INVALID))) feraiseexcept(FE_INVALID); 61 | return ret; 62 | } 63 | 64 | static forceinline double fpu_sqrtd(double val) 65 | { 66 | double ret = sqrt(val); 67 | if (unlikely(val < 0 && !fetestexcept(FE_INVALID))) feraiseexcept(FE_INVALID); 68 | return ret; 69 | } 70 | 71 | static forceinline float fpu_copysignf(float a, float b) 72 | { 73 | return copysignf(a, b); 74 | } 75 | 76 | static forceinline double fpu_copysignd(double a, double b) 77 | { 78 | return copysign(a, b); 79 | } 80 | 81 | static forceinline float fpu_copysignxf(float a, float b) 82 | { 83 | uint32_t ia, ib; 84 | memcpy(&ia, &a, sizeof(a)); 85 | memcpy(&ib, &b, sizeof(b)); 86 | ib ^= ia; 87 | memcpy(&b, &ib, sizeof(b)); 88 | return fpu_copysignf(a, b); 89 | } 90 | 91 | static forceinline double fpu_copysignxd(double a, double b) 92 | { 93 | uint64_t ia, ib; 94 | memcpy(&ia, &a, sizeof(a)); 95 | memcpy(&ib, &b, sizeof(b)); 96 | ib ^= ia; 97 | memcpy(&b, &ib, sizeof(b)); 98 | return fpu_copysignd(a, b); 99 | } 100 | 101 | static forceinline float fpu_fmaf(float a, float b, float c) 102 | { 103 | #ifdef UNDER_CE // WinCE libc doesn't have fma() 104 | return a * b + c; 105 | #else 106 | return fmaf(a, b, c); 107 | #endif 108 | } 109 | 110 | static forceinline double fpu_fmad(double a, double b, double c) 111 | { 112 | #ifdef UNDER_CE // WinCE libc doesn't have fma() 113 | return a * b + c; 114 | #else 115 | return fma(a, b, c); 116 | #endif 117 | } 118 | 119 | static forceinline float fpu_minf(float x, float y) 120 | { 121 | #if defined(__riscv_f) 122 | // On real RISC-V, fmin/fmax actually behave the way we need 123 | return fminf(x, y); 124 | #else 125 | #if defined(GNU_EXTS) 126 | // isless/isgreater are non-signalling comparisons 127 | if (isless(x, y)) { 128 | return x; 129 | } else if (likely(isless(y, x))) { 130 | return y; 131 | } 132 | #endif 133 | if (unlikely(fpu_isnan(x))) { 134 | // If one of operands is NaN, return a different operand 135 | if (fpu_is_snanf(x) || fpu_is_snanf(y)) { 136 | feraiseexcept(FE_INVALID); 137 | } 138 | return y; 139 | } else if (unlikely(fpu_isnan(y))) { 140 | if (fpu_is_snanf(x) || fpu_is_snanf(y)) { 141 | feraiseexcept(FE_INVALID); 142 | } 143 | return x; 144 | #if !defined(GNU_EXTS) 145 | } else if (x < y) { 146 | return x; 147 | } else if (y < x) { 148 | return y; 149 | #endif 150 | } else { 151 | // -0.0 is less than 0.0, but not handled by isless/isgreater 152 | return fpu_signbitf(x) ? x : y; 153 | } 154 | #endif 155 | } 156 | 157 | static forceinline float fpu_maxf(float x, float y) 158 | { 159 | #if defined(__riscv_f) 160 | return fmaxf(x, y); 161 | #else 162 | #if defined(GNU_EXTS) 163 | if (isgreater(x, y)) { 164 | return x; 165 | } else if (likely(isgreater(y, x))) { 166 | return y; 167 | } 168 | #endif 169 | if (unlikely(fpu_isnan(x))) { 170 | if (fpu_is_snanf(x) || fpu_is_snanf(y)) { 171 | feraiseexcept(FE_INVALID); 172 | } 173 | return y; 174 | } else if (unlikely(fpu_isnan(y))) { 175 | if (fpu_is_snanf(x) || fpu_is_snanf(y)) { 176 | feraiseexcept(FE_INVALID); 177 | } 178 | return x; 179 | #if !defined(GNU_EXTS) 180 | } else if (x > y) { 181 | return x; 182 | } else if (y > x) { 183 | return y; 184 | #endif 185 | } else { 186 | return fpu_signbitf(x) ? y : x; 187 | } 188 | #endif 189 | } 190 | 191 | static forceinline double fpu_mind(double x, double y) 192 | { 193 | #if defined(__riscv_d) 194 | return fmin(x, y); 195 | #else 196 | #if defined(GNU_EXTS) 197 | if (isless(x, y)) { 198 | return x; 199 | } else if (likely(isless(y, x))) { 200 | return y; 201 | } 202 | #endif 203 | if (unlikely(fpu_isnan(x))) { 204 | if (fpu_is_snand(x) || fpu_is_snand(y)) { 205 | feraiseexcept(FE_INVALID); 206 | } 207 | return y; 208 | } else if (unlikely(fpu_isnan(y))) { 209 | if (fpu_is_snand(x) || fpu_is_snand(y)) { 210 | feraiseexcept(FE_INVALID); 211 | } 212 | return x; 213 | #if !defined(GNU_EXTS) 214 | } else if (x < y) { 215 | return x; 216 | } else if (y < x) { 217 | return y; 218 | #endif 219 | } else { 220 | return fpu_signbitd(x) ? x : y; 221 | } 222 | #endif 223 | } 224 | 225 | static forceinline double fpu_maxd(double x, double y) 226 | { 227 | #if defined(__riscv_d) 228 | return fmax(x, y); 229 | #else 230 | #if defined(GNU_EXTS) 231 | if (isgreater(x, y)) { 232 | return x; 233 | } else if (likely(isgreater(y, x))) { 234 | return y; 235 | } 236 | #endif 237 | if (unlikely(fpu_isnan(x))) { 238 | if (fpu_is_snand(x) || fpu_is_snand(y)) { 239 | feraiseexcept(FE_INVALID); 240 | } 241 | return y; 242 | } else if (unlikely(fpu_isnan(y))) { 243 | if (fpu_is_snand(x) || fpu_is_snand(y)) { 244 | feraiseexcept(FE_INVALID); 245 | } 246 | return x; 247 | #if !defined(GNU_EXTS) 248 | } else if (x > y) { 249 | return x; 250 | } else if (y > x) { 251 | return y; 252 | #endif 253 | } else { 254 | return fpu_signbitd(x) ? y : x; 255 | } 256 | #endif 257 | } 258 | 259 | #endif 260 | -------------------------------------------------------------------------------- /src/cpu/riscv32_interpreter.c: -------------------------------------------------------------------------------- 1 | /* 2 | riscv_interpreter.c - RISC-V 32-bit template interpreter 3 | Copyright (C) 2024 LekKit 4 | 5 | This Source Code Form is subject to the terms of the Mozilla Public 6 | License, v. 2.0. If a copy of the MPL was not distributed with this 7 | file, You can obtain one at https://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | #define riscv_run_interpreter riscv32_run_interpreter 11 | 12 | #include "riscv_interpreter.h" 13 | -------------------------------------------------------------------------------- /src/cpu/riscv64_interpreter.c: -------------------------------------------------------------------------------- 1 | /* 2 | riscv_interpreter64.c - RISC-V 64-bit template interpreter 3 | Copyright (C) 2024 LekKit 4 | 5 | This Source Code Form is subject to the terms of the Mozilla Public 6 | License, v. 2.0. If a copy of the MPL was not distributed with this 7 | file, You can obtain one at https://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | #define RV64 11 | #define riscv_run_interpreter riscv64_run_interpreter 12 | 13 | #include "compiler.h" 14 | 15 | // The interpreter is faster on GCC with -O3 optimization for whatever reason. 16 | // This overrides the optimization level set for the whole codebase 17 | SOURCE_OPTIMIZATION_O3 18 | 19 | #include "riscv_interpreter.h" 20 | -------------------------------------------------------------------------------- /src/cpu/riscv_interpreter.h: -------------------------------------------------------------------------------- 1 | /* 2 | riscv_interpreter.h - RISC-V Template Interpreter 3 | Copyright (C) 2024 LekKit 4 | 5 | This Source Code Form is subject to the terms of the Mozilla Public 6 | License, v. 2.0. If a copy of the MPL was not distributed with this 7 | file, You can obtain one at https://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | #ifndef RISCV_INTERPRETER_H 11 | #define RISCV_INTERPRETER_H 12 | 13 | #include "rvvm.h" 14 | #include "riscv_cpu.h" 15 | #include "riscv_hart.h" 16 | #include "riscv_mmu.h" 17 | #include "riscv_jit.h" 18 | #include "compiler.h" 19 | #include "bit_ops.h" 20 | 21 | /* 22 | * Interpreter helpers 23 | */ 24 | 25 | #ifdef RV64 26 | 27 | typedef uint64_t xlen_t; 28 | typedef int64_t sxlen_t; 29 | typedef uint64_t xaddr_t; 30 | #define SHAMT_BITS 6 31 | #define DIV_OVERFLOW_RS1 ((sxlen_t)0x8000000000000000ULL) 32 | 33 | #else 34 | 35 | typedef uint32_t xlen_t; 36 | typedef int32_t sxlen_t; 37 | typedef uint32_t xaddr_t; 38 | #define SHAMT_BITS 5 39 | #define DIV_OVERFLOW_RS1 ((sxlen_t)0x80000000U) 40 | 41 | #endif 42 | 43 | static forceinline xlen_t riscv_read_reg(rvvm_hart_t* vm, regid_t reg) 44 | { 45 | return vm->registers[reg]; 46 | } 47 | 48 | static forceinline sxlen_t riscv_read_reg_s(rvvm_hart_t* vm, regid_t reg) 49 | { 50 | return vm->registers[reg]; 51 | } 52 | 53 | static forceinline void riscv_write_reg(rvvm_hart_t* vm, regid_t reg, sxlen_t data) 54 | { 55 | vm->registers[reg] = data; 56 | } 57 | 58 | // Provides entry point to riscv_emulate_insn() 59 | #include "riscv_compressed.h" 60 | 61 | /* 62 | * Optimized CPU interpreter dispatch loop. 63 | * Executes instructions until some event occurs (interrupt, trap). 64 | * 65 | * Calling this with vm->running == 0 allows single-stepping guest instructions. 66 | * 67 | * NOTE: Call riscv_restart_dispatch() after flushing TLB around PC, 68 | * otherwise the CPU loop will continue executing current page. 69 | */ 70 | 71 | TSAN_SUPPRESS void riscv_run_interpreter(rvvm_hart_t* vm) 72 | { 73 | uint32_t insn = 0; 74 | 75 | // This is similar to TLB mechanism, but persists in local variables across instructions 76 | size_t insn_ptr = 0; 77 | xlen_t insn_page = vm->registers[RISCV_REG_PC] + RISCV_PAGE_SIZE; 78 | 79 | do { 80 | const xlen_t insn_addr = vm->registers[RISCV_REG_PC]; 81 | if (likely(insn_addr - insn_page <= 0xFFC)) { 82 | // Direct instruction fetch by pointer 83 | insn = read_uint32_le_m((const void*)(size_t)(insn_ptr + insn_addr)); 84 | } else { 85 | uint32_t tmp = 0; 86 | if (likely(riscv_fetch_insn(vm, insn_addr, &tmp))) { 87 | // Update pointer to the current page in real memory 88 | // If we are executing code from MMIO, direct memory fetch fails 89 | const xlen_t vpn = vm->registers[RISCV_REG_PC] >> RISCV_PAGE_SHIFT; 90 | const rvvm_tlb_entry_t* entry = &vm->tlb[vpn & RVVM_TLB_MASK]; 91 | insn = tmp; 92 | insn_ptr = entry->ptr; 93 | insn_page = entry->e << RISCV_PAGE_SHIFT; 94 | } else { 95 | // Instruction fetch fault happened 96 | return; 97 | } 98 | } 99 | 100 | #if defined(USE_JIT) && (defined(RVJIT_NATIVE_64BIT) || !defined(RV64)) 101 | if (likely(vm->jit_compiling)) { 102 | // If we hit non-compilable instruction or cross page boundaries, current JIT block is finalized. 103 | if (vm->jit_block_ends || (vm->jit.virt_pc >> RISCV_PAGE_SHIFT) != (vm->registers[RISCV_REG_PC] >> RISCV_PAGE_SHIFT)) { 104 | riscv_jit_finalize(vm); 105 | } 106 | vm->jit_block_ends = true; 107 | } 108 | #endif 109 | 110 | // Implicitly zero x0 register each time 111 | vm->registers[RISCV_REG_ZERO] = 0; 112 | 113 | riscv_emulate_insn(vm, insn); 114 | } while (likely(vm->running)); 115 | } 116 | 117 | #endif 118 | -------------------------------------------------------------------------------- /src/devices/alsa.c: -------------------------------------------------------------------------------- 1 | /* 2 | alsa.c - ALSA sound backend 3 | Copyright (C) 2025 David Korenchuk 4 | 5 | This Source Code Form is subject to the terms of the Mozilla Public 6 | License, v. 2.0. If a copy of the MPL was not distributed with this 7 | file, You can obtain one at https://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | #ifdef USE_ALSA 11 | 12 | #include "compiler.h" 13 | #include "dlib.h" 14 | #include "sound-hda.h" 15 | #include "utils.h" 16 | 17 | 18 | #if CHECK_INCLUDE(alsa/asoundlib.h, 1) 19 | #include 20 | #endif 21 | 22 | #define ASOUND_DLIB_SYM(sym) static __typeof__(sym) *MACRO_CONCAT(sym, _dlib) = NULL; 23 | 24 | ASOUND_DLIB_SYM(snd_pcm_open) 25 | ASOUND_DLIB_SYM(snd_pcm_hw_params_malloc) 26 | ASOUND_DLIB_SYM(snd_pcm_hw_params_any) 27 | ASOUND_DLIB_SYM(snd_pcm_hw_params_set_access) 28 | ASOUND_DLIB_SYM(snd_pcm_hw_params_set_format) 29 | ASOUND_DLIB_SYM(snd_pcm_hw_params_set_channels) 30 | ASOUND_DLIB_SYM(snd_pcm_hw_params_set_rate_near) 31 | ASOUND_DLIB_SYM(snd_pcm_hw_params_set_period_size_near) 32 | ASOUND_DLIB_SYM(snd_pcm_hw_params) 33 | ASOUND_DLIB_SYM(snd_pcm_writei) 34 | ASOUND_DLIB_SYM(snd_pcm_prepare) 35 | ASOUND_DLIB_SYM(snd_pcm_drain) 36 | ASOUND_DLIB_SYM(snd_pcm_close) 37 | ASOUND_DLIB_SYM(snd_pcm_hw_params_free) 38 | 39 | #define snd_pcm_open snd_pcm_open_dlib 40 | #define snd_pcm_hw_params_malloc snd_pcm_hw_params_malloc_dlib 41 | #define snd_pcm_hw_params_any snd_pcm_hw_params_any_dlib 42 | #define snd_pcm_hw_params_set_access snd_pcm_hw_params_set_access_dlib 43 | #define snd_pcm_hw_params_set_format snd_pcm_hw_params_set_format_dlib 44 | #define snd_pcm_hw_params_set_channels snd_pcm_hw_params_set_channels_dlib 45 | #define snd_pcm_hw_params_set_rate_near snd_pcm_hw_params_set_rate_near_dlib 46 | #define snd_pcm_hw_params_set_period_size_near snd_pcm_hw_params_set_period_size_near_dlib 47 | #define snd_pcm_hw_params snd_pcm_hw_params_dlib 48 | #define snd_pcm_writei snd_pcm_writei_dlib 49 | #define snd_pcm_prepare snd_pcm_prepare_dlib 50 | #define snd_pcm_drain snd_pcm_drain_dlib 51 | #define snd_pcm_close snd_pcm_close_dlib 52 | #define snd_pcm_hw_params_free snd_pcm_hw_params_free_dlib 53 | 54 | typedef struct { 55 | snd_pcm_t *pcm_handle; 56 | } alsa_subsystem_t; 57 | 58 | static void alsa_sound_write(sound_subsystem_t *subsystem, void *data, size_t size) 59 | { 60 | alsa_subsystem_t *alsa = subsystem->sound_data; 61 | 62 | if (snd_pcm_writei(alsa->pcm_handle, data, size / 2) == -EPIPE) 63 | snd_pcm_prepare(alsa->pcm_handle); 64 | } 65 | 66 | static bool alsa_load_symbols(void) 67 | { 68 | dlib_ctx_t *libasound = dlib_open("asound", DLIB_NAME_PROBE); 69 | if (libasound == NULL) { 70 | rvvm_warn("Cannot load libasound.so"); 71 | return 0; 72 | } 73 | 74 | bool avail = 1; 75 | 76 | #define ASOUND_DLIB_RESOLVE(lib, sym) \ 77 | do { \ 78 | sym = dlib_resolve(lib, #sym); \ 79 | avail = avail && !!sym; \ 80 | } while (0) 81 | 82 | ASOUND_DLIB_RESOLVE(libasound, snd_pcm_open); 83 | ASOUND_DLIB_RESOLVE(libasound, snd_pcm_hw_params_malloc); 84 | ASOUND_DLIB_RESOLVE(libasound, snd_pcm_hw_params_any); 85 | ASOUND_DLIB_RESOLVE(libasound, snd_pcm_hw_params_set_access); 86 | ASOUND_DLIB_RESOLVE(libasound, snd_pcm_hw_params_set_format); 87 | ASOUND_DLIB_RESOLVE(libasound, snd_pcm_hw_params_set_channels); 88 | ASOUND_DLIB_RESOLVE(libasound, snd_pcm_hw_params_set_rate_near); 89 | ASOUND_DLIB_RESOLVE(libasound, snd_pcm_hw_params_set_period_size_near); 90 | ASOUND_DLIB_RESOLVE(libasound, snd_pcm_hw_params); 91 | ASOUND_DLIB_RESOLVE(libasound, snd_pcm_writei); 92 | ASOUND_DLIB_RESOLVE(libasound, snd_pcm_prepare); 93 | ASOUND_DLIB_RESOLVE(libasound, snd_pcm_drain); 94 | ASOUND_DLIB_RESOLVE(libasound, snd_pcm_close); 95 | ASOUND_DLIB_RESOLVE(libasound, snd_pcm_hw_params_free); 96 | 97 | dlib_close(libasound); 98 | 99 | return avail; 100 | } 101 | 102 | bool alsa_sound_init(sound_subsystem_t *sound) 103 | { 104 | bool libasound_avail = true; 105 | DO_ONCE(libasound_avail = alsa_load_symbols()); 106 | if (!libasound_avail) { 107 | rvvm_error("Could not load libasound.so"); 108 | return false; 109 | } 110 | 111 | alsa_subsystem_t *subsystem = safe_new_obj(alsa_subsystem_t); 112 | if (snd_pcm_open(&subsystem->pcm_handle, "default", SND_PCM_STREAM_PLAYBACK, 0) < 0) { 113 | rvvm_warn("Failed to open ALSA device"); 114 | return false; 115 | } 116 | 117 | snd_pcm_t *pcm_device = subsystem->pcm_handle; 118 | snd_pcm_hw_params_t *params = NULL; 119 | unsigned int sample_rate = 192000; 120 | int channels = 1; 121 | snd_pcm_uframes_t frames = 128; 122 | int dir = 0; 123 | 124 | snd_pcm_hw_params_malloc(¶ms); 125 | snd_pcm_hw_params_any(pcm_device, params); 126 | 127 | snd_pcm_hw_params_set_access(pcm_device, params, SND_PCM_ACCESS_RW_INTERLEAVED); 128 | snd_pcm_hw_params_set_format(pcm_device, params, SND_PCM_FORMAT_S16_LE); 129 | snd_pcm_hw_params_set_channels(pcm_device, params, channels); 130 | snd_pcm_hw_params_set_rate_near(pcm_device, params, &sample_rate, &dir); 131 | snd_pcm_hw_params_set_period_size_near(pcm_device, params, &frames, &dir); 132 | snd_pcm_hw_params(pcm_device, params); 133 | 134 | subsystem->pcm_handle = pcm_device; 135 | 136 | sound->sound_data = subsystem; 137 | sound->write = alsa_sound_write; 138 | 139 | return true; 140 | } 141 | 142 | #endif /* USE_ALSA */ 143 | -------------------------------------------------------------------------------- /src/devices/ata.h: -------------------------------------------------------------------------------- 1 | /* 2 | ata.h - IDE/ATA disk controller 3 | Copyright (C) 2021 cerg2010cerg2010 4 | LekKit 5 | 6 | This Source Code Form is subject to the terms of the Mozilla Public 7 | License, v. 2.0. If a copy of the MPL was not distributed with this 8 | file, You can obtain one at https://mozilla.org/MPL/2.0/. 9 | */ 10 | 11 | #ifndef RVVM_ATA_H 12 | #define RVVM_ATA_H 13 | 14 | #include "rvvmlib.h" 15 | #include "pci-bus.h" 16 | 17 | #define ATA_DATA_ADDR_DEFAULT 0x00200000 18 | #define ATA_CTL_ADDR_DEFAULT 0x00201000U 19 | 20 | // Attach ATA PIO at specific address 21 | PUBLIC bool ata_pio_init(rvvm_machine_t* machine, rvvm_addr_t ata_data_addr, rvvm_addr_t ata_ctl_addr, const char* image, bool rw); 22 | 23 | // Automatically attach ATA PIO to a machine 24 | PUBLIC bool ata_pio_init_auto(rvvm_machine_t* machine, const char* image, bool rw); 25 | 26 | // Attach PCI ATA UDMA (IDE) to a PCI bus 27 | PUBLIC pci_dev_t* ata_pci_init(pci_bus_t* pci_bus, const char* image, bool rw); 28 | 29 | // Automatically attach PCI ATA UDMA (IDE) to a machine 30 | PUBLIC pci_dev_t* ata_pci_init_auto(rvvm_machine_t* machine, const char* image, bool rw); 31 | 32 | // Automatically attach ATA drive to a machine 33 | PUBLIC bool ata_init_auto(rvvm_machine_t* machine, const char* image, bool rw); 34 | 35 | #endif 36 | -------------------------------------------------------------------------------- /src/devices/chardev.h: -------------------------------------------------------------------------------- 1 | /* 2 | chardev.h - Character device backend for UART 3 | Copyright (C) 2023 LekKit 4 | 宋文武 5 | 6 | This Source Code Form is subject to the terms of the Mozilla Public 7 | License, v. 2.0. If a copy of the MPL was not distributed with this 8 | file, You can obtain one at https://mozilla.org/MPL/2.0/. 9 | */ 10 | 11 | #ifndef RVVM_CHARDEV_H 12 | #define RVVM_CHARDEV_H 13 | 14 | #include "rvvmlib.h" 15 | 16 | /* 17 | * IO Device - UART (or else) owning the chardev 18 | * Chardev - Terminal, emulated VT, socket 19 | */ 20 | 21 | typedef struct rvvm_chardev chardev_t; 22 | 23 | struct rvvm_chardev { 24 | // IO Dev -> Chardev calls 25 | uint32_t (*poll)(chardev_t* dev); 26 | size_t (*read)(chardev_t* dev, void* buf, size_t nbytes); 27 | size_t (*write)(chardev_t* dev, const void* buf, size_t nbytes); 28 | 29 | // Chardev -> IO Device notifications (IRQ) 30 | void (*notify)(void* io_dev, uint32_t flags); 31 | 32 | // Common RVVM API features 33 | void (*update)(chardev_t* dev); 34 | void (*remove)(chardev_t* dev); 35 | 36 | void* data; 37 | void* io_dev; 38 | }; 39 | 40 | #define CHARDEV_RX 0x1 41 | #define CHARDEV_TX 0x2 42 | 43 | static inline uint32_t chardev_poll(chardev_t* dev) 44 | { 45 | if (dev && dev->poll) { 46 | return dev->poll(dev); 47 | } 48 | return CHARDEV_TX; 49 | } 50 | 51 | static inline size_t chardev_read(chardev_t* dev, void* buf, size_t nbytes) 52 | { 53 | if (dev && dev->read) { 54 | return dev->read(dev, buf, nbytes); 55 | } 56 | return 0; 57 | } 58 | 59 | static inline size_t chardev_write(chardev_t* dev, const void* buf, size_t nbytes) 60 | { 61 | if (dev && dev->write) { 62 | return dev->write(dev, buf, nbytes); 63 | } 64 | return nbytes; 65 | } 66 | 67 | static inline void chardev_free(chardev_t* dev) 68 | { 69 | if (dev && dev->remove) { 70 | dev->remove(dev); 71 | } 72 | } 73 | 74 | static inline void chardev_update(chardev_t* dev) 75 | { 76 | if (dev && dev->update) { 77 | dev->update(dev); 78 | } 79 | } 80 | 81 | static inline void chardev_notify(chardev_t* dev, uint32_t flags) 82 | { 83 | if (dev && dev->notify) { 84 | dev->notify(dev->io_dev, flags); 85 | } 86 | } 87 | 88 | // Built-in chardev implementations 89 | 90 | PUBLIC chardev_t* chardev_term_create(void); // stdio 91 | PUBLIC chardev_t* chardev_fd_create(int rfd, int wfd); // POSIX fd 92 | PUBLIC chardev_t* chardev_pty_create(const char* path); // POSIX pipe/pty 93 | 94 | #endif 95 | -------------------------------------------------------------------------------- /src/devices/eth-oc.h: -------------------------------------------------------------------------------- 1 | /* 2 | eth-oc.h - OpenCores Ethernet MAC controller 3 | Copyright (C) 2021 cerg2010cerg2010 4 | LekKit 5 | 6 | This Source Code Form is subject to the terms of the Mozilla Public 7 | License, v. 2.0. If a copy of the MPL was not distributed with this 8 | file, You can obtain one at https://mozilla.org/MPL/2.0/. 9 | */ 10 | 11 | #ifndef RVVM_ETH_OC_H 12 | #define RVVM_ETH_OC_H 13 | 14 | #include "rvvmlib.h" 15 | #include "tap_api.h" 16 | 17 | #define ETH_OC_ADDR_DEFAULT 0x21000000U 18 | 19 | PUBLIC rvvm_mmio_dev_t* ethoc_init(rvvm_machine_t* machine, tap_dev_t* tap, 20 | rvvm_addr_t base_addr, rvvm_intc_t* intc, rvvm_irq_t irq); 21 | 22 | PUBLIC rvvm_mmio_dev_t* ethoc_init_auto(rvvm_machine_t* machine); 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /src/devices/framebuffer.c: -------------------------------------------------------------------------------- 1 | /* 2 | framebuffer.c - Simple Framebuffer 3 | Copyright (C) 2021 LekKit 4 | 5 | This Source Code Form is subject to the terms of the Mozilla Public 6 | License, v. 2.0. If a copy of the MPL was not distributed with this 7 | file, You can obtain one at https://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | #include "framebuffer.h" 11 | #include "fdtlib.h" 12 | #include "utils.h" 13 | 14 | static void fb_remove(rvvm_mmio_dev_t* dev) 15 | { 16 | UNUSED(dev); 17 | } 18 | 19 | static rvvm_mmio_type_t fb_dev_type = { 20 | .name = "framebuffer", 21 | .remove = fb_remove, 22 | }; 23 | 24 | PUBLIC rvvm_mmio_dev_t* framebuffer_init(rvvm_machine_t* machine, rvvm_addr_t addr, const fb_ctx_t* fb) 25 | { 26 | // Map the framebuffer into physical memory 27 | rvvm_mmio_dev_t fb_mmio = { 28 | .addr = addr, 29 | .size = framebuffer_size(fb), 30 | .mapping = fb->buffer, 31 | .type = &fb_dev_type, 32 | }; 33 | 34 | rvvm_mmio_dev_t* mmio = rvvm_attach_mmio(machine, &fb_mmio); 35 | if (mmio == NULL) return mmio; 36 | 37 | #ifdef USE_FDT 38 | struct fdt_node* fb_fdt = fdt_node_create_reg("framebuffer", fb_mmio.addr); 39 | fdt_node_add_prop_reg(fb_fdt, "reg", fb_mmio.addr, fb_mmio.size); 40 | fdt_node_add_prop_str(fb_fdt, "compatible", "simple-framebuffer"); 41 | switch (fb->format) { 42 | case RGB_FMT_R5G6B5: 43 | fdt_node_add_prop_str(fb_fdt, "format", "r5g6b5"); 44 | break; 45 | case RGB_FMT_R8G8B8: 46 | fdt_node_add_prop_str(fb_fdt, "format", "r8g8b8"); 47 | break; 48 | case RGB_FMT_A8R8G8B8: 49 | fdt_node_add_prop_str(fb_fdt, "format", "a8r8g8b8"); 50 | break; 51 | case RGB_FMT_A8B8G8R8: 52 | fdt_node_add_prop_str(fb_fdt, "format", "a8b8g8r8"); 53 | break; 54 | default: 55 | rvvm_warn("Unknown RGB format in framebuffer_init()!"); 56 | break; 57 | } 58 | fdt_node_add_prop_u32(fb_fdt, "width", fb->width); 59 | fdt_node_add_prop_u32(fb_fdt, "height", fb->height); 60 | fdt_node_add_prop_u32(fb_fdt, "stride", framebuffer_stride(fb)); 61 | 62 | fdt_node_add_child(rvvm_get_fdt_soc(machine), fb_fdt); 63 | #endif 64 | 65 | return mmio; 66 | } 67 | 68 | PUBLIC rvvm_mmio_dev_t* framebuffer_init_auto(rvvm_machine_t* machine, const fb_ctx_t* fb) 69 | { 70 | rvvm_addr_t addr = rvvm_mmio_zone_auto(machine, 0x18000000, framebuffer_size(fb)); 71 | rvvm_mmio_dev_t* mmio = framebuffer_init(machine, addr, fb); 72 | if (mmio) { 73 | rvvm_append_cmdline(machine, "console=tty0"); 74 | } 75 | return mmio; 76 | } 77 | -------------------------------------------------------------------------------- /src/devices/framebuffer.h: -------------------------------------------------------------------------------- 1 | /* 2 | framebuffer.h - Framebuffer context, RGB format handling 3 | Copyright (C) 2021 LekKit 4 | 5 | This Source Code Form is subject to the terms of the Mozilla Public 6 | License, v. 2.0. If a copy of the MPL was not distributed with this 7 | file, You can obtain one at https://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | #ifndef RVVM_FRAMEBUFFER_H 11 | #define RVVM_FRAMEBUFFER_H 12 | 13 | #include "rvvmlib.h" 14 | 15 | #define RGB_FMT_INVALID 0x0 16 | #define RGB_FMT_R5G6B5 0x02 17 | #define RGB_FMT_R8G8B8 0x03 18 | #define RGB_FMT_A8R8G8B8 0x04 //!< Little-endian: BGRA, Big-endian: ARGB (Recommended) 19 | #define RGB_FMT_A8B8G8R8 0x14 //!< Little-endian: RGBA, Big-endian: ABGR 20 | 21 | //! Pixel RGB format 22 | typedef uint8_t rgb_fmt_t; 23 | 24 | //! Framebuffer context description 25 | typedef struct { 26 | void* buffer; //!< Buffer in host memory 27 | uint32_t width; //!< Width in pixels 28 | uint32_t height; //!< Height in pixels 29 | uint32_t stride; //!< Line alignment. Set to 0 if unsure. 30 | rgb_fmt_t format; //!< Pixel format 31 | } fb_ctx_t; 32 | 33 | /* 34 | * Pixel format handling 35 | */ 36 | 37 | //! Get bytes per pixel for a format 38 | static inline size_t rgb_format_bytes(rgb_fmt_t format) 39 | { 40 | switch (format) { 41 | case RGB_FMT_R5G6B5: return 2; 42 | case RGB_FMT_R8G8B8: return 3; 43 | case RGB_FMT_A8R8G8B8: return 4; 44 | case RGB_FMT_A8B8G8R8: return 4; 45 | } 46 | return 0; 47 | } 48 | 49 | //! Get bits per pixel (bpp) for a format 50 | static inline size_t rgb_format_bpp(rgb_fmt_t format) 51 | { 52 | return rgb_format_bytes(format) << 3; 53 | } 54 | 55 | //! Get pixel format from bpp 56 | static inline rgb_fmt_t rgb_format_from_bpp(size_t bpp) 57 | { 58 | switch (bpp) { 59 | case 16: return RGB_FMT_R5G6B5; 60 | case 24: return RGB_FMT_R8G8B8; 61 | // Default to ARGB when bpp = 32, this is what most guests and hosts expect 62 | case 32: return RGB_FMT_A8R8G8B8; 63 | } 64 | return RGB_FMT_INVALID; 65 | } 66 | 67 | /* 68 | * Framebuffer API 69 | */ 70 | 71 | //! Calculate effective framebuffer stride 72 | static inline size_t framebuffer_stride(const fb_ctx_t* fb) 73 | { 74 | return fb->stride ? fb->stride : fb->width * rgb_format_bytes(fb->format); 75 | } 76 | 77 | //! Calculate framebuffer region size 78 | static inline size_t framebuffer_size(const fb_ctx_t* fb) 79 | { 80 | return framebuffer_stride(fb) * fb->height; 81 | } 82 | 83 | //! \brief Attach framebuffer context to the machine. 84 | //! \warning The buffer is not freed automatically. 85 | PUBLIC rvvm_mmio_dev_t* framebuffer_init(rvvm_machine_t* machine, rvvm_addr_t addr, const fb_ctx_t* fb); 86 | 87 | PUBLIC rvvm_mmio_dev_t* framebuffer_init_auto(rvvm_machine_t* machine, const fb_ctx_t* fb); 88 | 89 | #endif 90 | -------------------------------------------------------------------------------- /src/devices/gpio-sifive.h: -------------------------------------------------------------------------------- 1 | /* 2 | gpio-sifive.h - SiFive GPIO Controller 3 | Copyright (C) 2024 LekKit 4 | 5 | This Source Code Form is subject to the terms of the Mozilla Public 6 | License, v. 2.0. If a copy of the MPL was not distributed with this 7 | file, You can obtain one at https://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | #ifndef RVVM_GPIO_SIFIVE_H 11 | #define RVVM_GPIO_SIFIVE_H 12 | 13 | #include "rvvmlib.h" 14 | #include "gpio_api.h" 15 | 16 | #define GPIO_SIFIVE_PINS 32 17 | 18 | #define GPIO_SIFIVE_ADDR_DEFAULT 0x10060000 19 | 20 | PUBLIC rvvm_mmio_dev_t* gpio_sifive_init(rvvm_machine_t* machine, rvvm_gpio_dev_t* gpio, 21 | rvvm_addr_t base_addr, rvvm_intc_t* intc, const rvvm_irq_t* irqs); 22 | 23 | PUBLIC rvvm_mmio_dev_t* gpio_sifive_init_auto(rvvm_machine_t* machine, rvvm_gpio_dev_t* gpio); 24 | 25 | #endif 26 | -------------------------------------------------------------------------------- /src/devices/gpio_api.h: -------------------------------------------------------------------------------- 1 | /* 2 | gpio_api.h - General-Purpose IO API 3 | Copyright (C) 2024 LekKit 4 | 5 | This Source Code Form is subject to the terms of the Mozilla Public 6 | License, v. 2.0. If a copy of the MPL was not distributed with this 7 | file, You can obtain one at https://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | #ifndef RVVM_GPIO_API_H 11 | #define RVVM_GPIO_API_H 12 | 13 | typedef struct rvvm_gpio_dev rvvm_gpio_dev_t; 14 | 15 | struct rvvm_gpio_dev { 16 | // IO Dev -> GPIO Dev calls 17 | bool (*pins_out)(rvvm_gpio_dev_t* dev, size_t off, uint32_t pins); 18 | 19 | // GPIO Dev -> IO Dev calls 20 | bool (*pins_in)(rvvm_gpio_dev_t* dev, size_t off, uint32_t pins); 21 | uint32_t (*pins_read)(rvvm_gpio_dev_t* dev, size_t off); 22 | 23 | // Common RVVM API features 24 | void (*update)(rvvm_gpio_dev_t* dev); 25 | void (*remove)(rvvm_gpio_dev_t* dev); 26 | 27 | void* data; 28 | void* io_dev; 29 | }; 30 | 31 | static inline bool gpio_pins_out(rvvm_gpio_dev_t* dev, size_t off, uint32_t pins) 32 | { 33 | if (dev && dev->pins_out) return dev->pins_out(dev, off, pins); 34 | return false; 35 | } 36 | 37 | static inline bool gpio_write_pins(rvvm_gpio_dev_t* dev, size_t off, uint32_t pins) 38 | { 39 | if (dev && dev->pins_in) return dev->pins_in(dev, off, pins); 40 | return false; 41 | } 42 | 43 | static inline uint32_t gpio_read_pins(rvvm_gpio_dev_t* dev, size_t off) 44 | { 45 | if (dev && dev->pins_read) return dev->pins_read(dev, off); 46 | return 0; 47 | } 48 | 49 | static inline void gpio_free(rvvm_gpio_dev_t* dev) 50 | { 51 | if (dev && dev->remove) dev->remove(dev); 52 | } 53 | 54 | static inline void gpio_update(rvvm_gpio_dev_t* dev) 55 | { 56 | if (dev && dev->update) dev->update(dev); 57 | } 58 | 59 | #endif 60 | -------------------------------------------------------------------------------- /src/devices/gui_window.h: -------------------------------------------------------------------------------- 1 | /* 2 | gui_window.h - Framebuffer GUI Window 3 | Copyright (C) 2021 LekKit 4 | 5 | This Source Code Form is subject to the terms of the Mozilla Public 6 | License, v. 2.0. If a copy of the MPL was not distributed with this 7 | file, You can obtain one at https://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | #ifndef RVVM_GUI_WINDOW_H 11 | #define RVVM_GUI_WINDOW_H 12 | 13 | #include "framebuffer.h" 14 | #include "hid_api.h" 15 | 16 | typedef struct gui_window_t gui_window_t; 17 | 18 | struct gui_window_t { 19 | void* win_data; 20 | void* data; 21 | fb_ctx_t fb; 22 | 23 | // Calls into GUI implementation 24 | void (*draw)(gui_window_t* win); 25 | void (*poll)(gui_window_t* win); 26 | void (*remove)(gui_window_t* win); 27 | void (*grab_input)(gui_window_t* win, bool grab); 28 | void (*set_title)(gui_window_t* win, const char* title); 29 | void (*set_fullscreen)(gui_window_t* win, bool fullscreen); // TODO: Borderless fullscreen 30 | 31 | // Calls from GUI implementation 32 | void (*on_close)(gui_window_t* win); 33 | void (*on_focus_lost)(gui_window_t* win); 34 | void (*on_key_press)(gui_window_t* win, hid_key_t key); 35 | void (*on_key_release)(gui_window_t* win, hid_key_t key); 36 | void (*on_mouse_press)(gui_window_t* win, hid_btns_t btns); 37 | void (*on_mouse_release)(gui_window_t* win, hid_btns_t btns); 38 | void (*on_mouse_place)(gui_window_t* win, int32_t x, int32_t y); 39 | void (*on_mouse_move)(gui_window_t* win, int32_t x, int32_t y); 40 | void (*on_mouse_scroll)(gui_window_t* win, int32_t offset); 41 | }; 42 | 43 | // Internal use 44 | bool x11_window_init(gui_window_t* win); 45 | bool wayland_window_init(gui_window_t* win); // TODO: Wayland GUI backend 46 | bool win32_window_init(gui_window_t* win); 47 | bool haiku_window_init(gui_window_t* win); 48 | bool sdl_window_init(gui_window_t* win); 49 | 50 | // Probe windowing backends and create a window. Returns false on failure. 51 | bool gui_window_init_backend(gui_window_t* win); 52 | 53 | // Attach a framebuffer & HID mouse/keyboard to the VM. Returns false on failure. 54 | PUBLIC bool gui_window_init_auto(rvvm_machine_t* machine, uint32_t width, uint32_t height); 55 | 56 | #endif 57 | -------------------------------------------------------------------------------- /src/devices/hid_dev.h: -------------------------------------------------------------------------------- 1 | /* 2 | hid_dev.h - Generic HID Device API 3 | Copyright (C) 2022 X512 4 | 5 | This Source Code Form is subject to the terms of the Mozilla Public 6 | License, v. 2.0. If a copy of the MPL was not distributed with this 7 | file, You can obtain one at https://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | #ifndef RVVM_HID_DEV_H 11 | #define RVVM_HID_DEV_H 12 | 13 | #include 14 | #include 15 | 16 | #define REPORT_TYPE_INPUT 1 17 | #define REPORT_TYPE_OUTPUT 2 18 | #define REPORT_TYPE_FEATURE 3 19 | 20 | #define HID_PROTOCOL_BOOT 0 21 | #define HID_PROTOCOL_REPORT 1 22 | 23 | #define HID_POWER_ON 0 24 | #define HID_POWER_SLEEP 1 25 | 26 | typedef struct { 27 | void* host; 28 | void* dev; 29 | 30 | // static information 31 | const uint8_t* report_desc; 32 | uint16_t report_desc_size; 33 | uint16_t max_input_size; 34 | uint16_t max_output_size; 35 | uint16_t vendor_id; 36 | uint16_t product_id; 37 | uint16_t version_id; 38 | 39 | // device -> host calls 40 | void (*input_available)(void* host, uint8_t report_id); 41 | 42 | void (*reset)(void* dev); 43 | void (*read_report )(void* dev, uint8_t report_type, uint8_t report_id, uint32_t offset, uint8_t *val); 44 | void (*write_report)(void* dev, uint8_t report_type, uint8_t report_id, uint32_t offset, uint8_t val); 45 | void (*get_idle)(void* dev, uint8_t report_id, uint16_t* idle /* milliseconds */); 46 | void (*set_idle)(void* dev, uint8_t report_id, uint16_t idle); 47 | void (*get_protocol)(void* dev, uint16_t* protocol); 48 | void (*set_protocol)(void* dev, uint16_t protocol); 49 | void (*set_power)(void* dev, uint16_t power); 50 | void (*remove)(void* dev); 51 | } hid_dev_t; 52 | 53 | #endif // RVVM_HID_DEV_H 54 | -------------------------------------------------------------------------------- /src/devices/i2c-hid.h: -------------------------------------------------------------------------------- 1 | /* 2 | i2c-hid.h - I2C HID Host Controller Interface 3 | Copyright (C) 2022 X512 4 | 5 | This Source Code Form is subject to the terms of the Mozilla Public 6 | License, v. 2.0. If a copy of the MPL was not distributed with this 7 | file, You can obtain one at https://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | #ifndef _I2C_HID_H_ 11 | #define _I2C_HID_H_ 12 | 13 | #include "hid_dev.h" 14 | #include "rvvmlib.h" 15 | 16 | PUBLIC void i2c_hid_init_auto(rvvm_machine_t* machine, hid_dev_t* hid_dev); 17 | 18 | #endif // _I2C_HID_H_ 19 | -------------------------------------------------------------------------------- /src/devices/i2c-oc.h: -------------------------------------------------------------------------------- 1 | /* 2 | i2c-oc.h - OpenCores I2C Controller 3 | Copyright (C) 2022 LekKit 4 | 5 | This Source Code Form is subject to the terms of the Mozilla Public 6 | License, v. 2.0. If a copy of the MPL was not distributed with this 7 | file, You can obtain one at https://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | #ifndef RVVM_I2C_OC_H 11 | #define RVVM_I2C_OC_H 12 | 13 | #include "rvvmlib.h" 14 | 15 | #define I2C_OC_ADDR_DEFAULT 0x10030000 16 | 17 | #define I2C_AUTO_ADDR 0x0 // Auto-pick I2C device address 18 | 19 | typedef struct { 20 | // I2C bus address 21 | uint16_t addr; 22 | // Device-specific data 23 | void* data; 24 | 25 | // Start transaction, return device availability 26 | bool (*start)(void* dev, bool is_write); 27 | // Return false on NACK or no data to read 28 | bool (*write)(void* dev, uint8_t byte); 29 | bool (*read)(void* dev, uint8_t* byte); 30 | // Stop the current transaction 31 | void (*stop)(void* dev); 32 | // Device cleanup 33 | void (*remove)(void* dev); 34 | } i2c_dev_t; 35 | 36 | PUBLIC i2c_bus_t* i2c_oc_init(rvvm_machine_t* machine, rvvm_addr_t addr, rvvm_intc_t* intc, rvvm_irq_t irq); 37 | PUBLIC i2c_bus_t* i2c_oc_init_auto(rvvm_machine_t* machine); 38 | 39 | // Returns assigned device address or zero on error 40 | PUBLIC uint16_t i2c_attach_dev(i2c_bus_t* bus, const i2c_dev_t* dev_desc); 41 | 42 | // Get I2C controller FDT node for nested device nodes 43 | PUBLIC struct fdt_node* i2c_bus_fdt_node(i2c_bus_t* bus); 44 | 45 | #endif 46 | -------------------------------------------------------------------------------- /src/devices/mtd-physmap.c: -------------------------------------------------------------------------------- 1 | /* 2 | mtd-physmap.c - Memory Technology Device Mapping 3 | Copyright (C) 2023 LekKit 4 | 5 | This Source Code Form is subject to the terms of the Mozilla Public 6 | License, v. 2.0. If a copy of the MPL was not distributed with this 7 | file, You can obtain one at https://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | #include "mtd-physmap.h" 11 | #include "blk_io.h" 12 | #include "fdtlib.h" 13 | #include "utils.h" 14 | 15 | SOURCE_OPTIMIZATION_SIZE 16 | 17 | typedef struct { 18 | blkdev_t* blk; 19 | } mtd_dev_t; 20 | 21 | static void mtd_remove(rvvm_mmio_dev_t* dev) 22 | { 23 | mtd_dev_t* mtd = dev->data; 24 | blk_close(mtd->blk); 25 | free(mtd); 26 | } 27 | 28 | static void mtd_reset(rvvm_mmio_dev_t* dev) 29 | { 30 | mtd_dev_t* mtd = dev->data; 31 | void* ptr = rvvm_get_dma_ptr(dev->machine, rvvm_get_opt(dev->machine, RVVM_OPT_MEM_BASE), blk_getsize(mtd->blk)); 32 | if (ptr) blk_read(mtd->blk, ptr, blk_getsize(mtd->blk), 0); 33 | } 34 | 35 | static rvvm_mmio_type_t mtd_type = { 36 | .name = "mtd_physmap", 37 | .remove = mtd_remove, 38 | .reset = mtd_reset, 39 | }; 40 | 41 | static bool mtd_mmio_read(rvvm_mmio_dev_t* dev, void* data, size_t offset, uint8_t size) 42 | { 43 | mtd_dev_t* mtd = dev->data; 44 | return blk_read(mtd->blk, data, size, offset) == size; 45 | } 46 | 47 | static bool mtd_mmio_write(rvvm_mmio_dev_t* dev, void* data, size_t offset, uint8_t size) 48 | { 49 | mtd_dev_t* mtd = dev->data; 50 | return blk_write(mtd->blk, data, size, offset) == size; 51 | } 52 | 53 | PUBLIC rvvm_mmio_dev_t* mtd_physmap_init_blk(rvvm_machine_t* machine, rvvm_addr_t addr, void* blk_dev) 54 | { 55 | mtd_dev_t* mtd = safe_new_obj(mtd_dev_t); 56 | mtd->blk = blk_dev; 57 | 58 | rvvm_mmio_dev_t mtd_mmio = { 59 | .addr = addr, 60 | .size = blk_getsize(mtd->blk), 61 | .min_op_size = 1, 62 | .max_op_size = 8, 63 | .read = mtd_mmio_read, 64 | .write = mtd_mmio_write, 65 | .data = mtd, 66 | .type = &mtd_type, 67 | }; 68 | rvvm_mmio_dev_t* mmio = rvvm_attach_mmio(machine, &mtd_mmio); 69 | if (mmio == NULL) return mmio; 70 | #ifdef USE_FDT 71 | struct fdt_node* mtd_fdt = fdt_node_create_reg("flash", mtd_mmio.addr); 72 | fdt_node_add_prop_reg(mtd_fdt, "reg", mtd_mmio.addr, mtd_mmio.size); 73 | fdt_node_add_prop_str(mtd_fdt, "compatible", "mtd-ram"); 74 | fdt_node_add_prop_u32(mtd_fdt, "bank-width", 0x1); 75 | fdt_node_add_prop_u32(mtd_fdt, "#address-cells", 1); 76 | fdt_node_add_prop_u32(mtd_fdt, "#size-cells", 1); 77 | { 78 | struct fdt_node* partition0 = fdt_node_create("partition@0"); 79 | uint32_t reg[2] = { 0, mtd_mmio.size, }; 80 | fdt_node_add_prop_cells(partition0, "reg", reg, 2); 81 | fdt_node_add_prop_str(partition0, "label", "firmware"); 82 | fdt_node_add_child(mtd_fdt, partition0); 83 | } 84 | fdt_node_add_child(rvvm_get_fdt_soc(machine), mtd_fdt); 85 | #endif 86 | return mmio; 87 | } 88 | 89 | PUBLIC rvvm_mmio_dev_t* mtd_physmap_init(rvvm_machine_t* machine, rvvm_addr_t addr, const char* image_path, bool rw) 90 | { 91 | blkdev_t* blk = blk_open(image_path, rw ? BLKDEV_RW : 0); 92 | if (blk == NULL) return NULL; 93 | return mtd_physmap_init_blk(machine, addr, blk); 94 | } 95 | 96 | PUBLIC rvvm_mmio_dev_t* mtd_physmap_init_auto(rvvm_machine_t* machine, const char* image_path, bool rw) 97 | { 98 | return mtd_physmap_init(machine, MTD_PHYSMAP_DEFAULT_MMIO, image_path, rw); 99 | } 100 | -------------------------------------------------------------------------------- /src/devices/mtd-physmap.h: -------------------------------------------------------------------------------- 1 | /* 2 | mtd-physmap.h - Memory Technology Device Mapping 3 | Copyright (C) 2023 LekKit 4 | 5 | This Source Code Form is subject to the terms of the Mozilla Public 6 | License, v. 2.0. If a copy of the MPL was not distributed with this 7 | file, You can obtain one at https://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | #ifndef RVVM_MTD_PHYSMAP_H 11 | #define RVVM_MTD_PHYSMAP_H 12 | 13 | #include "rvvmlib.h" 14 | 15 | /* 16 | * The main purpose of this device is to allow guests to flash 17 | * different firmware into the board memory chip 18 | */ 19 | 20 | #define MTD_PHYSMAP_DEFAULT_MMIO 0x04000000 21 | 22 | PUBLIC rvvm_mmio_dev_t* mtd_physmap_init_blk(rvvm_machine_t* machine, rvvm_addr_t addr, void* blk_dev); 23 | PUBLIC rvvm_mmio_dev_t* mtd_physmap_init(rvvm_machine_t* machine, rvvm_addr_t addr, const char* image_path, bool rw); 24 | PUBLIC rvvm_mmio_dev_t* mtd_physmap_init_auto(rvvm_machine_t* machine, const char* image_path, bool rw); 25 | 26 | #endif 27 | 28 | -------------------------------------------------------------------------------- /src/devices/ns16550a.h: -------------------------------------------------------------------------------- 1 | /* 2 | ns16550a.h - NS16550A UART 3 | Copyright (C) 2021 LekKit 4 | 5 | This Source Code Form is subject to the terms of the Mozilla Public 6 | License, v. 2.0. If a copy of the MPL was not distributed with this 7 | file, You can obtain one at https://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | #ifndef RVVM_NS16550A_H 11 | #define RVVM_NS16550A_H 12 | 13 | #include "rvvmlib.h" 14 | #include "chardev.h" 15 | 16 | #define NS16550A_ADDR_DEFAULT 0x10000000 17 | 18 | PUBLIC rvvm_mmio_dev_t* ns16550a_init(rvvm_machine_t* machine, chardev_t* chardev, 19 | rvvm_addr_t addr, rvvm_intc_t* intc, rvvm_irq_t irq); 20 | 21 | PUBLIC rvvm_mmio_dev_t* ns16550a_init_auto(rvvm_machine_t* machine, chardev_t* chardev); 22 | 23 | PUBLIC rvvm_mmio_dev_t* ns16550a_init_term_auto(rvvm_machine_t* machine); 24 | 25 | #endif 26 | -------------------------------------------------------------------------------- /src/devices/nvme.h: -------------------------------------------------------------------------------- 1 | /* 2 | nvme.h - Non-Volatile Memory Express 3 | Copyright (C) 2022 LekKit 4 | 5 | This Source Code Form is subject to the terms of the Mozilla Public 6 | License, v. 2.0. If a copy of the MPL was not distributed with this 7 | file, You can obtain one at https://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | #ifndef NVME_H 11 | #define NVME_H 12 | 13 | #include "rvvmlib.h" 14 | #include "pci-bus.h" 15 | 16 | PUBLIC pci_dev_t* nvme_init_blk(pci_bus_t* pci_bus, void* blk_dev); 17 | PUBLIC pci_dev_t* nvme_init(pci_bus_t* pci_bus, const char* image_path, bool rw); 18 | PUBLIC pci_dev_t* nvme_init_auto(rvvm_machine_t* machine, const char* image_path, bool rw); 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /src/devices/pci-vfio.h: -------------------------------------------------------------------------------- 1 | /* 2 | pci-vfio.h - VFIO PCI Passthrough 3 | Copyright (C) 2022 LekKit 4 | 5 | This Source Code Form is subject to the terms of the Mozilla Public 6 | License, v. 2.0. If a copy of the MPL was not distributed with this 7 | file, You can obtain one at https://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | #ifndef RVVM_PCI_VFIO_H 11 | #define RVVM_PCI_VFIO_H 12 | 13 | #include "pci-bus.h" 14 | 15 | PUBLIC bool pci_vfio_init_auto(rvvm_machine_t* machine, const char* pci_id); 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /src/devices/ps2-altera.c: -------------------------------------------------------------------------------- 1 | /* 2 | ps2-altera.c - Altera PS2 Controller 3 | Copyright (C) 2021 LekKit 4 | cerg2010cerg2010 5 | 6 | This Source Code Form is subject to the terms of the Mozilla Public 7 | License, v. 2.0. If a copy of the MPL was not distributed with this 8 | file, You can obtain one at https://mozilla.org/MPL/2.0/. 9 | */ 10 | 11 | #include "ps2-altera.h" 12 | #include "mem_ops.h" 13 | #include "atomics.h" 14 | #include "utils.h" 15 | #include "fdtlib.h" 16 | 17 | SOURCE_OPTIMIZATION_SIZE 18 | 19 | #define PS2_ALTERA_REG_DATA 0x0 20 | #define PS2_ALTERA_REG_CTRL 0x4 21 | 22 | #define PS2_ALTERA_CTRL_RE 0x1 // IRQ Enabled 23 | #define PS2_ALTERA_CTRL_RI 0x100 // IRQ Pending 24 | #define PS2_ALTERA_CTRL_CE 0x400 // Controller Error 25 | 26 | #define PS2_ALTERA_DATA_RVALID 0x8000 27 | 28 | #define PS2_ALTERA_REG_SIZE 0x1000 29 | 30 | typedef struct { 31 | chardev_t* chardev; 32 | 33 | // IRQ data 34 | rvvm_intc_t* intc; 35 | rvvm_irq_t irq; 36 | 37 | // Controller registers 38 | uint32_t ctrl; 39 | } ps2_altera_dev_t; 40 | 41 | static void ps2_altera_notify(void* io_dev, uint32_t flags) 42 | { 43 | ps2_altera_dev_t* ps2 = io_dev; 44 | if (flags & CHARDEV_RX) { 45 | if (atomic_or_uint32(&ps2->ctrl, PS2_ALTERA_CTRL_RI) & PS2_ALTERA_CTRL_RE) { 46 | rvvm_send_irq(ps2->intc, ps2->irq); 47 | } 48 | } 49 | } 50 | 51 | static bool ps2_altera_read(rvvm_mmio_dev_t* dev, void* data, size_t offset, uint8_t size) 52 | { 53 | ps2_altera_dev_t* ps2 = dev->data; 54 | uint32_t val = 0; 55 | UNUSED(size); 56 | 57 | switch (offset) { 58 | case PS2_ALTERA_REG_DATA: { 59 | uint8_t byte = 0; 60 | uint32_t avail = chardev_read(ps2->chardev, &byte, sizeof(byte)); 61 | val = byte | (avail ? PS2_ALTERA_DATA_RVALID : 0) | (avail << 16); 62 | break; 63 | } 64 | case PS2_ALTERA_REG_CTRL: 65 | val = atomic_load_uint32(&ps2->ctrl); 66 | break; 67 | } 68 | 69 | write_uint32_le(data, val); 70 | return true; 71 | } 72 | 73 | static bool ps2_altera_write(rvvm_mmio_dev_t* dev, void* data, size_t offset, uint8_t size) 74 | { 75 | ps2_altera_dev_t* ps2 = dev->data; 76 | uint32_t val = read_uint32_le(data); 77 | UNUSED(size); 78 | 79 | switch (offset) { 80 | case PS2_ALTERA_REG_DATA: { 81 | uint8_t byte = val; 82 | if (!chardev_write(ps2->chardev, &byte, sizeof(byte))) { 83 | atomic_or_uint32(&ps2->ctrl, PS2_ALTERA_CTRL_CE); 84 | } 85 | } 86 | break; 87 | case PS2_ALTERA_REG_CTRL: 88 | atomic_or_uint32(&ps2->ctrl, val & PS2_ALTERA_CTRL_RE); 89 | atomic_and_uint32(&ps2->ctrl, PS2_ALTERA_CTRL_RI | (val & (PS2_ALTERA_CTRL_RE | PS2_ALTERA_CTRL_CE))); 90 | break; 91 | } 92 | 93 | return true; 94 | } 95 | 96 | static void ps2_altera_update(rvvm_mmio_dev_t* dev) 97 | { 98 | ps2_altera_dev_t* ps2 = dev->data; 99 | chardev_update(ps2->chardev); 100 | } 101 | 102 | static void ps2_altera_remove(rvvm_mmio_dev_t* dev) 103 | { 104 | ps2_altera_dev_t* ps2 = dev->data; 105 | chardev_free(ps2->chardev); 106 | free(ps2); 107 | } 108 | 109 | static rvvm_mmio_type_t ps2_altera_dev_type = { 110 | .name = "ps2_altera", 111 | .update = ps2_altera_update, 112 | .remove = ps2_altera_remove, 113 | }; 114 | 115 | void ps2_altera_init(rvvm_machine_t* machine, chardev_t* chardev, rvvm_addr_t addr, rvvm_intc_t* intc, rvvm_irq_t irq) 116 | { 117 | ps2_altera_dev_t* ps2 = safe_new_obj(ps2_altera_dev_t); 118 | ps2->chardev = chardev; 119 | ps2->intc = intc; 120 | ps2->irq = irq; 121 | 122 | if (chardev) { 123 | chardev->io_dev = ps2; 124 | chardev->notify = ps2_altera_notify; 125 | } 126 | 127 | rvvm_mmio_dev_t ps2_mmio = { 128 | .addr = addr, 129 | .size = PS2_ALTERA_REG_SIZE, 130 | .data = ps2, 131 | .type = &ps2_altera_dev_type, 132 | .read = ps2_altera_read, 133 | .write = ps2_altera_write, 134 | .min_op_size = 4, 135 | .max_op_size = 4, 136 | }; 137 | if (!rvvm_attach_mmio(machine, &ps2_mmio)) { 138 | rvvm_error("Failed to attach Altera PS/2 controller!"); 139 | return; 140 | } 141 | 142 | #ifdef USE_FDT 143 | struct fdt_node* ps2_fdt = fdt_node_create_reg("ps2", ps2_mmio.addr); 144 | fdt_node_add_prop_reg(ps2_fdt, "reg", ps2_mmio.addr, ps2_mmio.size); 145 | fdt_node_add_prop_str(ps2_fdt, "compatible", "altr,ps2-1.0"); 146 | rvvm_fdt_describe_irq(ps2_fdt, intc, irq); 147 | fdt_node_add_child(rvvm_get_fdt_soc(machine), ps2_fdt); 148 | #endif 149 | } 150 | 151 | void ps2_altera_init_auto(rvvm_machine_t* machine, chardev_t* chardev) 152 | { 153 | rvvm_intc_t* intc = rvvm_get_intc(machine); 154 | rvvm_addr_t addr = rvvm_mmio_zone_auto(machine, PS2_ALTERA_ADDR_DEFAULT, PS2_ALTERA_REG_SIZE); 155 | ps2_altera_init(machine, chardev, addr, intc, rvvm_alloc_irq(intc)); 156 | } 157 | -------------------------------------------------------------------------------- /src/devices/ps2-altera.h: -------------------------------------------------------------------------------- 1 | /* 2 | ps2-altera.h - Altera PS2 Controller 3 | Copyright (C) 2021 LekKit 4 | cerg2010cerg2010 5 | 6 | This Source Code Form is subject to the terms of the Mozilla Public 7 | License, v. 2.0. If a copy of the MPL was not distributed with this 8 | file, You can obtain one at https://mozilla.org/MPL/2.0/. 9 | */ 10 | 11 | #ifndef RVVM_PS2_ALTERA_H 12 | #define RVVM_PS2_ALTERA_H 13 | 14 | #include "rvvmlib.h" 15 | #include "chardev.h" 16 | 17 | #define PS2_ALTERA_ADDR_DEFAULT 0x20000000U 18 | 19 | void ps2_altera_init(rvvm_machine_t* machine, chardev_t* chardev, rvvm_addr_t addr, rvvm_intc_t* intc, rvvm_irq_t irq); 20 | 21 | void ps2_altera_init_auto(rvvm_machine_t* machine, chardev_t* chardev); 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /src/devices/riscv-aclint.c: -------------------------------------------------------------------------------- 1 | /* 2 | riscv-aclint.c - RISC-V Advanced Core Local Interruptor 3 | Copyright (C) 2021 LekKit 4 | 5 | This Source Code Form is subject to the terms of the Mozilla Public 6 | License, v. 2.0. If a copy of the MPL was not distributed with this 7 | file, You can obtain one at https://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | #include "riscv-aclint.h" 11 | #include "riscv_hart.h" 12 | #include "mem_ops.h" 13 | #include "bit_ops.h" 14 | 15 | #define CLINT_MMIO_SIZE 0x10000 16 | #define ACLINT_MSWI_SIZE 0x4000 17 | #define ACLINT_MTIMER_SIZE 0x8000 18 | 19 | static rvvm_mmio_type_t aclint_mswi_dev_type = { 20 | .name = "riscv_aclint_mswi", 21 | }; 22 | 23 | static rvvm_mmio_type_t aclint_mtimer_dev_type = { 24 | .name = "riscv_aclint_mtimer", 25 | }; 26 | 27 | static bool aclint_mswi_read(rvvm_mmio_dev_t* device, void* data, size_t offset, uint8_t size) 28 | { 29 | size_t hartid = offset >> 2; 30 | UNUSED(size); 31 | 32 | if (hartid < vector_size(device->machine->harts)) { 33 | rvvm_hart_t* vm = vector_at(device->machine->harts, hartid); 34 | write_uint32_le_m(data, (riscv_interrupts_raised(vm) >> RISCV_INTERRUPT_MSOFTWARE) & 1); 35 | return true; 36 | } 37 | 38 | return false; 39 | } 40 | 41 | static bool aclint_mswi_write(rvvm_mmio_dev_t* device, void* data, size_t offset, uint8_t size) 42 | { 43 | size_t hartid = offset >> 2; 44 | UNUSED(size); 45 | 46 | if (hartid < vector_size(device->machine->harts)) { 47 | rvvm_hart_t* vm = vector_at(device->machine->harts, hartid); 48 | if (read_uint32_le_m(data) & 1) { 49 | riscv_interrupt(vm, RISCV_INTERRUPT_MSOFTWARE); 50 | } else { 51 | riscv_interrupt_clear(vm, RISCV_INTERRUPT_MSOFTWARE); 52 | } 53 | return true; 54 | } 55 | 56 | return false; 57 | } 58 | 59 | static bool aclint_mtimer_read(rvvm_mmio_dev_t* device, void* data, size_t offset, uint8_t size) 60 | { 61 | size_t hartid = offset >> 3; 62 | UNUSED(size); 63 | 64 | if (offset == 0x7FF8) { 65 | write_uint64_le_m(data, rvtimer_get(&device->machine->timer)); 66 | return true; 67 | } 68 | 69 | if (hartid < vector_size(device->machine->harts)) { 70 | rvvm_hart_t* vm = vector_at(device->machine->harts, hartid); 71 | write_uint64_le_m(data, rvtimecmp_get(&vm->mtimecmp)); 72 | return true; 73 | } 74 | 75 | return false; 76 | } 77 | 78 | static bool aclint_mtimer_write(rvvm_mmio_dev_t* device, void* data, size_t offset, uint8_t size) 79 | { 80 | size_t hartid = offset >> 3; 81 | UNUSED(size); 82 | 83 | if (offset == 0x7FF8) { 84 | rvtimer_rebase(&device->machine->timer, read_uint64_le_m(data)); 85 | return true; 86 | } 87 | 88 | if (hartid < vector_size(device->machine->harts)) { 89 | rvvm_hart_t* vm = vector_at(device->machine->harts, hartid); 90 | rvtimecmp_set(&vm->mtimecmp, read_uint64_le_m(data)); 91 | if (rvtimecmp_pending(&vm->mtimecmp)) { 92 | riscv_interrupt(vm, RISCV_INTERRUPT_MTIMER); 93 | } else { 94 | riscv_interrupt_clear(vm, RISCV_INTERRUPT_MTIMER); 95 | } 96 | return true; 97 | } 98 | 99 | return false; 100 | } 101 | 102 | PUBLIC void riscv_clint_init(rvvm_machine_t* machine, rvvm_addr_t addr) 103 | { 104 | rvvm_mmio_dev_t aclint_mswi = { 105 | .addr = addr, 106 | .size = ACLINT_MSWI_SIZE, 107 | .type = &aclint_mswi_dev_type, 108 | .read = aclint_mswi_read, 109 | .write = aclint_mswi_write, 110 | .min_op_size = 4, 111 | .max_op_size = 4, 112 | }; 113 | 114 | rvvm_mmio_dev_t aclint_mtimer = { 115 | .addr = addr + ACLINT_MSWI_SIZE, 116 | .size = ACLINT_MTIMER_SIZE, 117 | .type = &aclint_mtimer_dev_type, 118 | .read = aclint_mtimer_read, 119 | .write = aclint_mtimer_write, 120 | .min_op_size = 8, 121 | .max_op_size = 8, 122 | }; 123 | 124 | if (!rvvm_attach_mmio(machine, &aclint_mswi) || !rvvm_attach_mmio(machine, &aclint_mtimer)) { 125 | rvvm_error("Failed to attach RISC-V ACLINT!"); 126 | return; 127 | } 128 | 129 | #ifdef USE_FDT 130 | struct fdt_node* cpus = fdt_node_find(rvvm_get_fdt_root(machine), "cpus"); 131 | vector_t(uint32_t) irq_ext = {0}; 132 | 133 | vector_foreach(machine->harts, i) { 134 | struct fdt_node* cpu = fdt_node_find_reg(cpus, "cpu", i); 135 | struct fdt_node* cpu_irq = fdt_node_find(cpu, "interrupt-controller"); 136 | uint32_t irq_phandle = fdt_node_get_phandle(cpu_irq); 137 | 138 | if (irq_phandle) { 139 | vector_push_back(irq_ext, irq_phandle); 140 | vector_push_back(irq_ext, RISCV_INTERRUPT_MSOFTWARE); 141 | vector_push_back(irq_ext, irq_phandle); 142 | vector_push_back(irq_ext, RISCV_INTERRUPT_MTIMER); 143 | } else { 144 | rvvm_warn("Missing /cpus/cpu/interrupt-controller node in FDT!"); 145 | } 146 | } 147 | 148 | struct fdt_node* clint = fdt_node_create_reg("clint", addr); 149 | fdt_node_add_prop_reg(clint, "reg", addr, CLINT_MMIO_SIZE); 150 | fdt_node_add_prop(clint, "compatible", "sifive,clint0\0riscv,clint0", 27); 151 | fdt_node_add_prop_cells(clint, "interrupts-extended", vector_buffer(irq_ext), vector_size(irq_ext)); 152 | fdt_node_add_child(rvvm_get_fdt_soc(machine), clint); 153 | vector_free(irq_ext); 154 | #endif 155 | } 156 | 157 | PUBLIC void riscv_clint_init_auto(rvvm_machine_t* machine) 158 | { 159 | rvvm_addr_t addr = rvvm_mmio_zone_auto(machine, CLINT_ADDR_DEFAULT, CLINT_MMIO_SIZE); 160 | riscv_clint_init(machine, addr); 161 | } 162 | -------------------------------------------------------------------------------- /src/devices/riscv-aclint.h: -------------------------------------------------------------------------------- 1 | /* 2 | riscv-aclint.h - RISC-V Advanced Core Local Interruptor 3 | Copyright (C) 2021 LekKit 4 | 5 | This Source Code Form is subject to the terms of the Mozilla Public 6 | License, v. 2.0. If a copy of the MPL was not distributed with this 7 | file, You can obtain one at https://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | #ifndef RVVM_ACLINT_H 11 | #define RVVM_ACLINT_H 12 | 13 | #include "rvvmlib.h" 14 | 15 | #define CLINT_ADDR_DEFAULT 0x2000000U 16 | 17 | PUBLIC void riscv_clint_init(rvvm_machine_t* machine, rvvm_addr_t addr); 18 | 19 | PUBLIC void riscv_clint_init_auto(rvvm_machine_t* machine); 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /src/devices/riscv-aplic.h: -------------------------------------------------------------------------------- 1 | /* 2 | riscv-aplic.h - RISC-V Advanced Platform-Level Interrupt Controller 3 | Copyright (C) 2024 LekKit 4 | 5 | This Source Code Form is subject to the terms of the Mozilla Public 6 | License, v. 2.0. If a copy of the MPL was not distributed with this 7 | file, You can obtain one at https://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | #ifndef RVVM_APLIC_H 11 | #define RVVM_APLIC_H 12 | 13 | #include "rvvmlib.h" 14 | 15 | #define APLIC_M_ADDR_DEFAULT 0x0C000000U 16 | #define APLIC_S_ADDR_DEFAULT 0x0D000000U 17 | 18 | PUBLIC rvvm_intc_t* riscv_aplic_init(rvvm_machine_t* machine, rvvm_addr_t m_addr, rvvm_addr_t s_addr); 19 | 20 | PUBLIC rvvm_intc_t* riscv_aplic_init_auto(rvvm_machine_t* machine); 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /src/devices/riscv-imsic.c: -------------------------------------------------------------------------------- 1 | /* 2 | riscv-imsic.c - RISC-V Incoming Message-Signaled Interrupt Controller 3 | Copyright (C) 2024 LekKit 4 | 5 | This Source Code Form is subject to the terms of the Mozilla Public 6 | License, v. 2.0. If a copy of the MPL was not distributed with this 7 | file, You can obtain one at https://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | #include "riscv-imsic.h" 11 | #include "riscv_hart.h" 12 | #include "mem_ops.h" 13 | #include "bit_ops.h" 14 | 15 | #define IMSIC_REG_SETEIPNUM_LE 0x00 16 | #define IMSIC_REG_SETEIPNUM_BE 0x04 17 | 18 | typedef struct { 19 | bool smode; 20 | } imsic_ctx_t; 21 | 22 | static bool imsic_mmio_write(rvvm_mmio_dev_t* dev, void* data, size_t offset, uint8_t size) 23 | { 24 | imsic_ctx_t* imsic = dev->data; 25 | size_t hartid = offset >> 12; 26 | UNUSED(size); 27 | 28 | if (hartid < vector_size(dev->machine->harts)) { 29 | rvvm_hart_t* hart = vector_at(dev->machine->harts, hartid); 30 | switch (offset & 0xFFC) { 31 | case IMSIC_REG_SETEIPNUM_LE: { 32 | riscv_send_aia_irq(hart, imsic->smode, read_uint32_le(data)); 33 | break; 34 | } 35 | case IMSIC_REG_SETEIPNUM_BE: { 36 | riscv_send_aia_irq(hart, imsic->smode, read_uint32_be_m(data)); 37 | break; 38 | } 39 | } 40 | } 41 | 42 | return true; 43 | } 44 | 45 | static rvvm_mmio_type_t imsic_dev_type = { 46 | .name = "riscv_imsic", 47 | }; 48 | 49 | PUBLIC void riscv_imsic_init(rvvm_machine_t* machine, rvvm_addr_t addr, bool smode) 50 | { 51 | if (rvvm_machine_running(machine)) { 52 | rvvm_error("Can't enable AIA on already running machine!"); 53 | return; 54 | } 55 | 56 | vector_foreach(machine->harts, i) { 57 | riscv_hart_aia_init(vector_at(machine->harts, i)); 58 | } 59 | 60 | rvvm_append_isa_string(machine, "_smaia_ssaia"); 61 | 62 | imsic_ctx_t* imsic = safe_new_obj(imsic_ctx_t); 63 | 64 | rvvm_mmio_dev_t imsic_mmio = { 65 | .addr = addr, 66 | .size = vector_size(machine->harts) << 12, 67 | .data = imsic, 68 | .min_op_size = 4, 69 | .max_op_size = 4, 70 | .read = rvvm_mmio_none, 71 | .write = imsic_mmio_write, 72 | .type = &imsic_dev_type, 73 | }; 74 | 75 | imsic->smode = smode; 76 | 77 | if (!rvvm_attach_msi_target(machine, &imsic_mmio)) { 78 | rvvm_error("Failed to attach RISC-V IMSIC!"); 79 | return; 80 | } 81 | 82 | #ifdef USE_FDT 83 | struct fdt_node* imsic_fdt = fdt_node_create_reg(smode ? "imsics_s" : "imsics_m", imsic_mmio.addr); 84 | struct fdt_node* cpus = fdt_node_find(rvvm_get_fdt_root(machine), "cpus"); 85 | vector_t(uint32_t) irq_ext = {0}; 86 | 87 | fdt_node_add_prop_reg(imsic_fdt, "reg", imsic_mmio.addr, imsic_mmio.size); 88 | fdt_node_add_prop_str(imsic_fdt, "compatible", "riscv,imsics"); 89 | fdt_node_add_prop(imsic_fdt, "interrupt-controller", NULL, 0); 90 | fdt_node_add_prop_u32(imsic_fdt, "#interrupt-cells", 0); 91 | fdt_node_add_prop(imsic_fdt, "msi-controller", NULL, 0); 92 | fdt_node_add_prop_u32(imsic_fdt, "#msi-cells", 0); 93 | fdt_node_add_prop_u32(imsic_fdt, "riscv,num-ids", RVVM_AIA_IRQ_LIMIT - 1); 94 | 95 | vector_foreach(machine->harts, i) { 96 | struct fdt_node* cpu = fdt_node_find_reg(cpus, "cpu", i); 97 | struct fdt_node* cpu_irq = fdt_node_find(cpu, "interrupt-controller"); 98 | 99 | if (cpu_irq) { 100 | vector_push_back(irq_ext, fdt_node_get_phandle(cpu_irq)); 101 | vector_push_back(irq_ext, smode ? RISCV_INTERRUPT_SEXTERNAL : RISCV_INTERRUPT_MEXTERNAL); 102 | } else { 103 | rvvm_warn("Missing CPU IRQ nodes in FDT!"); 104 | } 105 | } 106 | 107 | fdt_node_add_prop_cells(imsic_fdt, "interrupts-extended", vector_buffer(irq_ext), vector_size(irq_ext)); 108 | fdt_node_add_child(rvvm_get_fdt_soc(machine), imsic_fdt); 109 | vector_free(irq_ext); 110 | #endif 111 | } 112 | 113 | PUBLIC void riscv_imsic_init_auto(rvvm_machine_t* machine) 114 | { 115 | size_t imsic_size = vector_size(machine->harts) << 12; 116 | rvvm_addr_t m_addr = rvvm_mmio_zone_auto(machine, IMSIC_M_ADDR_DEFAULT, imsic_size); 117 | rvvm_addr_t s_addr = rvvm_mmio_zone_auto(machine, IMSIC_S_ADDR_DEFAULT, imsic_size); 118 | riscv_imsic_init(machine, m_addr, false); 119 | riscv_imsic_init(machine, s_addr, true); 120 | } 121 | -------------------------------------------------------------------------------- /src/devices/riscv-imsic.h: -------------------------------------------------------------------------------- 1 | /* 2 | riscv-imsic.h - RISC-V Incoming Message-Signaled Interrupt Controller 3 | Copyright (C) 2024 LekKit 4 | 5 | This Source Code Form is subject to the terms of the Mozilla Public 6 | License, v. 2.0. If a copy of the MPL was not distributed with this 7 | file, You can obtain one at https://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | #ifndef RVVM_IMSIC_H 11 | #define RVVM_IMSIC_H 12 | 13 | #include "rvvmlib.h" 14 | 15 | #define IMSIC_M_ADDR_DEFAULT 0x24000000U 16 | #define IMSIC_S_ADDR_DEFAULT 0x28000000U 17 | 18 | PUBLIC void riscv_imsic_init(rvvm_machine_t* machine, rvvm_addr_t addr, bool smode); 19 | 20 | PUBLIC void riscv_imsic_init_auto(rvvm_machine_t* machine); 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /src/devices/riscv-plic.h: -------------------------------------------------------------------------------- 1 | /* 2 | riscv-plic.c - RISC-V Platform-Level Interrupt Controller 3 | Copyright (C) 2023 LekKit 4 | 2021 cerg2010cerg2010 5 | 6 | This Source Code Form is subject to the terms of the Mozilla Public 7 | License, v. 2.0. If a copy of the MPL was not distributed with this 8 | file, You can obtain one at https://mozilla.org/MPL/2.0/. 9 | */ 10 | 11 | #ifndef RVVM_PLIC_H 12 | #define RVVM_PLIC_H 13 | 14 | #include "rvvmlib.h" 15 | 16 | #define PLIC_ADDR_DEFAULT 0xC000000U 17 | 18 | PUBLIC rvvm_intc_t* riscv_plic_init(rvvm_machine_t* machine, rvvm_addr_t addr); 19 | PUBLIC rvvm_intc_t* riscv_plic_init_auto(rvvm_machine_t* machine); 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /src/devices/rtc-ds1742.c: -------------------------------------------------------------------------------- 1 | /* 2 | rtc-ds7142.c - Dallas DS1742 Real-time Clock 3 | Copyright (C) 2023 LekKit 4 | 5 | This Source Code Form is subject to the terms of the Mozilla Public 6 | License, v. 2.0. If a copy of the MPL was not distributed with this 7 | file, You can obtain one at https://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | #include "rtc-ds1742.h" 11 | #include "fdtlib.h" 12 | #include "mem_ops.h" 13 | #include "rvtimer.h" 14 | #include "spinlock.h" 15 | #include "utils.h" 16 | 17 | SOURCE_OPTIMIZATION_SIZE 18 | 19 | #define DS1742_REG_CTL_CENT 0x0 // Control, Century 20 | #define DS1742_REG_SECONDS 0x1 // Seconds [0, 59] 21 | #define DS1742_REG_MINUTES 0x2 // Minutes [0, 59] 22 | #define DS1742_REG_HOURS 0x3 // Hours [0, 23] 23 | #define DS1742_REG_DAY 0x4 // Day of week [1, 7] 24 | #define DS1742_REG_DATE 0x5 // Day of month [1, 31] 25 | #define DS1742_REG_MONTH 0x6 // Month [1, 12] 26 | #define DS1742_REG_YEAR 0x7 // Year [0, 99] 27 | 28 | #define DS1742_MMIO_SIZE 0x8 29 | 30 | #define DS1742_DAY_BATT 0x80 // Battery OK 31 | #define DS1742_CTL_READ 0x40 // Lock registers for read 32 | #define DS1742_CTL_MASK 0xC0 // Mask of control registers 33 | 34 | typedef struct { 35 | spinlock_t lock; 36 | uint8_t ctl; 37 | uint8_t regs[DS1742_MMIO_SIZE]; 38 | } ds1742_dev_t; 39 | 40 | static inline uint8_t bcd_conv_u8(uint8_t val) 41 | { 42 | return (val % 10) | ((val / 10) << 4); 43 | } 44 | 45 | static inline uint64_t div_time_units(uint64_t* units, uint32_t div) 46 | { 47 | uint64_t rem = *units % div; 48 | *units = *units / div; 49 | return rem; 50 | } 51 | 52 | void rtc_ds1742_update_regs(ds1742_dev_t* rtc) 53 | { 54 | uint64_t tmp = rvtimer_unixtime(); 55 | uint32_t sec = div_time_units(&tmp, 60); // Seconds [0, 59] 56 | uint32_t min = div_time_units(&tmp, 60); // Minutes [0, 59] 57 | uint32_t hour = div_time_units(&tmp, 24); // Hours [0, 59] 58 | uint64_t days = tmp + 719468; // Days since 01.03.0000 59 | uint32_t wday = (days + 3) % 7; // Day of week [0, 6] 60 | uint64_t era = days / 146097; // Era 61 | uint32_t doe = days - (era * 146097); // Day of era [0, 146096] 62 | uint32_t yoe = (doe - doe / 1460) / 365; // Year of era [0, 399] 63 | uint64_t year = yoe + (era * 400); // Year 64 | uint32_t doy = doe - ((365 * yoe) + (yoe / 4) - (yoe / 100)); // Day of year [0, 365] 65 | uint32_t mmf = ((5 * doy) + 2) / 153; // Month [0, 11] [Mar, Feb] 66 | uint32_t mday = doy - (((153 * mmf) + 2) / 5) + 1; // Day of month [1, 31] 67 | unsigned mon = mmf + (mmf < 10 ? 3 : -9); // Month [1, 12] [Jan, Dec] 68 | 69 | rtc->regs[DS1742_REG_CTL_CENT] = bcd_conv_u8(year / 100); 70 | rtc->regs[DS1742_REG_SECONDS] = bcd_conv_u8(sec); 71 | rtc->regs[DS1742_REG_MINUTES] = bcd_conv_u8(min); 72 | rtc->regs[DS1742_REG_HOURS] = bcd_conv_u8(hour); 73 | rtc->regs[DS1742_REG_DATE] = bcd_conv_u8(mday); 74 | rtc->regs[DS1742_REG_DAY] = bcd_conv_u8(wday); 75 | rtc->regs[DS1742_REG_MONTH] = bcd_conv_u8(mon); 76 | rtc->regs[DS1742_REG_YEAR] = bcd_conv_u8(year % 100); 77 | } 78 | 79 | static bool rtc_ds1742_mmio_read(rvvm_mmio_dev_t* dev, void* data, size_t offset, uint8_t size) 80 | { 81 | ds1742_dev_t* rtc = dev->data; 82 | UNUSED(size); 83 | 84 | scoped_spin_lock (&rtc->lock) { 85 | uint8_t reg = rtc->regs[offset]; 86 | switch (offset) { 87 | case DS1742_REG_CTL_CENT: 88 | reg |= rtc->ctl; 89 | break; 90 | case DS1742_REG_DAY: 91 | reg |= DS1742_DAY_BATT; 92 | break; 93 | } 94 | write_uint8(data, reg); 95 | } 96 | 97 | return true; 98 | } 99 | 100 | static bool rtc_ds1742_mmio_write(rvvm_mmio_dev_t* dev, void* data, size_t offset, uint8_t size) 101 | { 102 | ds1742_dev_t* rtc = dev->data; 103 | UNUSED(size); 104 | 105 | if (offset == DS1742_REG_CTL_CENT) { 106 | uint8_t ctl = read_uint8(data) & DS1742_CTL_MASK; 107 | scoped_spin_lock (&rtc->lock) { 108 | if (!(rtc->ctl & DS1742_CTL_READ) && (ctl & DS1742_CTL_READ)) { 109 | rtc_ds1742_update_regs(rtc); 110 | } 111 | rtc->ctl = ctl; 112 | } 113 | } 114 | 115 | return true; 116 | } 117 | 118 | static rvvm_mmio_type_t rtc_ds1742_dev_type = { 119 | .name = "rtc_ds1742", 120 | }; 121 | 122 | PUBLIC rvvm_mmio_dev_t* rtc_ds1742_init(rvvm_machine_t* machine, rvvm_addr_t base_addr) 123 | { 124 | ds1742_dev_t* rtc = safe_new_obj(ds1742_dev_t); 125 | rvvm_mmio_dev_t rtc_ds1742 = { 126 | .addr = base_addr, 127 | .size = DS1742_MMIO_SIZE, 128 | .data = rtc, 129 | .type = &rtc_ds1742_dev_type, 130 | .read = rtc_ds1742_mmio_read, 131 | .write = rtc_ds1742_mmio_write, 132 | .min_op_size = 1, 133 | .max_op_size = 1, 134 | }; 135 | rtc_ds1742_update_regs(rtc); 136 | rvvm_mmio_dev_t* mmio = rvvm_attach_mmio(machine, &rtc_ds1742); 137 | if (mmio == NULL) { 138 | return mmio; 139 | } 140 | #ifdef USE_FDT 141 | struct fdt_node* rtc_fdt = fdt_node_create_reg("rtc", base_addr); 142 | fdt_node_add_prop_reg(rtc_fdt, "reg", base_addr, DS1742_MMIO_SIZE); 143 | fdt_node_add_prop_str(rtc_fdt, "compatible", "maxim,ds1742"); 144 | fdt_node_add_child(rvvm_get_fdt_soc(machine), rtc_fdt); 145 | #endif 146 | return mmio; 147 | } 148 | 149 | PUBLIC rvvm_mmio_dev_t* rtc_ds1742_init_auto(rvvm_machine_t* machine) 150 | { 151 | rvvm_addr_t addr = rvvm_mmio_zone_auto(machine, RTC_DS1742_DEFAULT_MMIO, DS1742_MMIO_SIZE); 152 | return rtc_ds1742_init(machine, addr); 153 | } 154 | -------------------------------------------------------------------------------- /src/devices/rtc-ds1742.h: -------------------------------------------------------------------------------- 1 | /* 2 | rtc-ds7142.h - Dallas DS1742 Real-time Clock 3 | Copyright (C) 2023 LekKit 4 | 5 | This Source Code Form is subject to the terms of the Mozilla Public 6 | License, v. 2.0. If a copy of the MPL was not distributed with this 7 | file, You can obtain one at https://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | #ifndef RVVM_RTC_DS1742_H 11 | #define RVVM_RTC_DS1742_H 12 | 13 | #include "rvvmlib.h" 14 | 15 | #define RTC_DS1742_DEFAULT_MMIO 0x101000 16 | 17 | PUBLIC rvvm_mmio_dev_t* rtc_ds1742_init(rvvm_machine_t* machine, rvvm_addr_t base_addr); 18 | PUBLIC rvvm_mmio_dev_t* rtc_ds1742_init_auto(rvvm_machine_t* machine); 19 | 20 | #endif 21 | 22 | -------------------------------------------------------------------------------- /src/devices/rtc-goldfish.c: -------------------------------------------------------------------------------- 1 | /* 2 | rtc-goldfish.c - Goldfish Real-time Clock 3 | Copyright (C) 2021 LekKit 4 | 5 | This Source Code Form is subject to the terms of the Mozilla Public 6 | License, v. 2.0. If a copy of the MPL was not distributed with this 7 | file, You can obtain one at https://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | #include "rtc-goldfish.h" 11 | #include "fdtlib.h" 12 | #include "mem_ops.h" 13 | #include "rvtimer.h" 14 | #include "utils.h" 15 | 16 | SOURCE_OPTIMIZATION_SIZE 17 | 18 | #define RTC_TIME_LOW 0x0 19 | #define RTC_TIME_HIGH 0x4 20 | #define RTC_ALARM_LOW 0x8 21 | #define RTC_ALARM_HIGH 0xC 22 | #define RTC_IRQ_ENABLED 0x10 23 | #define RTC_ALARM_CLEAR 0x14 24 | #define RTC_ALARM_STATUS 0x18 25 | #define RTC_IRQ_CLEAR 0x1C 26 | 27 | #define RTC_GOLDFISH_REG_SIZE 0x1000 28 | 29 | typedef struct { 30 | rvvm_intc_t* intc; 31 | rvvm_irq_t irq; 32 | 33 | uint32_t time_low; 34 | uint32_t time_high; 35 | uint32_t alarm_low; 36 | uint32_t alarm_high; 37 | uint32_t alarm_enabled; 38 | uint32_t irq_enabled; 39 | } rtc_goldfish_dev_t; 40 | 41 | static void rtc_goldfish_update(rtc_goldfish_dev_t* rtc) 42 | { 43 | uint64_t timer64 = rvtimer_unixtime() * 1000000000ULL; 44 | atomic_store_uint32_relax(&rtc->time_low, timer64); 45 | atomic_store_uint32_relax(&rtc->time_high, timer64 >> 32); 46 | 47 | if (atomic_load_uint32_relax(&rtc->alarm_enabled) && atomic_load_uint32_relax(&rtc->irq_enabled)) { 48 | uint64_t alarm64 = atomic_load_uint32_relax(&rtc->alarm_low); 49 | alarm64 |= (((uint64_t)atomic_load_uint32_relax(&rtc->alarm_high)) << 32); 50 | if (timer64 >= alarm64) { 51 | rvvm_raise_irq(rtc->intc, rtc->irq); 52 | } 53 | } else { 54 | rvvm_lower_irq(rtc->intc, rtc->irq); 55 | } 56 | } 57 | 58 | static bool rtc_goldfish_mmio_read(rvvm_mmio_dev_t* dev, void* data, size_t offset, uint8_t size) 59 | { 60 | rtc_goldfish_dev_t* rtc = dev->data; 61 | uint32_t val = 0; 62 | UNUSED(size); 63 | 64 | switch (offset) { 65 | case RTC_TIME_LOW: 66 | rtc_goldfish_update(rtc); 67 | val = atomic_load_uint32_relax(&rtc->time_low); 68 | break; 69 | case RTC_TIME_HIGH: 70 | val = atomic_load_uint32_relax(&rtc->time_high); 71 | break; 72 | case RTC_ALARM_LOW: 73 | val = atomic_load_uint32_relax(&rtc->alarm_low); 74 | break; 75 | case RTC_ALARM_HIGH: 76 | val = atomic_load_uint32_relax(&rtc->alarm_high); 77 | break; 78 | case RTC_IRQ_ENABLED: 79 | val = atomic_load_uint32_relax(&rtc->irq_enabled); 80 | break; 81 | case RTC_ALARM_STATUS: 82 | val = atomic_load_uint32_relax(&rtc->alarm_enabled); 83 | break; 84 | } 85 | 86 | write_uint32_le(data, val); 87 | return true; 88 | } 89 | 90 | static bool rtc_goldfish_mmio_write(rvvm_mmio_dev_t* dev, void* data, size_t offset, uint8_t size) 91 | { 92 | rtc_goldfish_dev_t* rtc = dev->data; 93 | uint32_t val = read_uint32_le(data); 94 | UNUSED(size); 95 | 96 | switch (offset) { 97 | case RTC_ALARM_LOW: 98 | atomic_store_uint32_relax(&rtc->alarm_enabled, true); 99 | atomic_store_uint32_relax(&rtc->alarm_low, val); 100 | rtc_goldfish_update(rtc); 101 | break; 102 | case RTC_ALARM_HIGH: 103 | atomic_store_uint32_relax(&rtc->alarm_high, val); 104 | break; 105 | case RTC_IRQ_ENABLED: 106 | atomic_store_uint32_relax(&rtc->irq_enabled, val & 1); 107 | rtc_goldfish_update(rtc); 108 | break; 109 | case RTC_ALARM_CLEAR: 110 | atomic_store_uint32_relax(&rtc->alarm_enabled, false); 111 | rtc_goldfish_update(rtc); 112 | break; 113 | case RTC_IRQ_CLEAR: 114 | rtc_goldfish_update(rtc); 115 | break; 116 | } 117 | 118 | return true; 119 | } 120 | 121 | static rvvm_mmio_type_t rtc_goldfish_dev_type = { 122 | .name = "rtc_goldfish", 123 | }; 124 | 125 | PUBLIC rvvm_mmio_dev_t* rtc_goldfish_init(rvvm_machine_t* machine, rvvm_addr_t addr, rvvm_intc_t* intc, rvvm_irq_t irq) 126 | { 127 | rtc_goldfish_dev_t* rtc = safe_new_obj(rtc_goldfish_dev_t); 128 | rtc->intc = intc; 129 | rtc->irq = irq; 130 | 131 | rvvm_mmio_dev_t rtc_mmio = { 132 | .addr = addr, 133 | .size = RTC_GOLDFISH_REG_SIZE, 134 | .data = rtc, 135 | .type = &rtc_goldfish_dev_type, 136 | .read = rtc_goldfish_mmio_read, 137 | .write = rtc_goldfish_mmio_write, 138 | .min_op_size = 4, 139 | .max_op_size = 4, 140 | }; 141 | rvvm_mmio_dev_t* mmio = rvvm_attach_mmio(machine, &rtc_mmio); 142 | if (mmio == NULL) { 143 | return mmio; 144 | } 145 | #ifdef USE_FDT 146 | struct fdt_node* rtc_fdt = fdt_node_create_reg("rtc", rtc_mmio.addr); 147 | fdt_node_add_prop_reg(rtc_fdt, "reg", rtc_mmio.addr, rtc_mmio.size); 148 | fdt_node_add_prop_str(rtc_fdt, "compatible", "google,goldfish-rtc"); 149 | rvvm_fdt_describe_irq(rtc_fdt, intc, irq); 150 | fdt_node_add_child(rvvm_get_fdt_soc(machine), rtc_fdt); 151 | #endif 152 | return mmio; 153 | } 154 | 155 | PUBLIC rvvm_mmio_dev_t* rtc_goldfish_init_auto(rvvm_machine_t* machine) 156 | { 157 | rvvm_intc_t* intc = rvvm_get_intc(machine); 158 | rvvm_addr_t addr = rvvm_mmio_zone_auto(machine, RTC_GOLDFISH_ADDR_DEFAULT, RTC_GOLDFISH_REG_SIZE); 159 | return rtc_goldfish_init(machine, addr, intc, rvvm_alloc_irq(intc)); 160 | } 161 | -------------------------------------------------------------------------------- /src/devices/rtc-goldfish.h: -------------------------------------------------------------------------------- 1 | /* 2 | rtc-goldfish.h - Goldfish Real-time Clock 3 | Copyright (C) 2021 LekKit 4 | 5 | This Source Code Form is subject to the terms of the Mozilla Public 6 | License, v. 2.0. If a copy of the MPL was not distributed with this 7 | file, You can obtain one at https://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | #ifndef RVVM_RTC_GOLDFISH_H 11 | #define RVVM_RTC_GOLDFISH_H 12 | 13 | #include "rvvmlib.h" 14 | 15 | #define RTC_GOLDFISH_ADDR_DEFAULT 0x101000 16 | 17 | PUBLIC rvvm_mmio_dev_t* rtc_goldfish_init(rvvm_machine_t* machine, rvvm_addr_t addr, 18 | rvvm_intc_t* intc, rvvm_irq_t irq); 19 | 20 | PUBLIC rvvm_mmio_dev_t* rtc_goldfish_init_auto(rvvm_machine_t* machine); 21 | 22 | #endif 23 | 24 | -------------------------------------------------------------------------------- /src/devices/rtl8169.h: -------------------------------------------------------------------------------- 1 | /* 2 | rtl8169.h - Realtek RTL8169 NIC 3 | Copyright (C) 2022 LekKit 4 | 5 | This Source Code Form is subject to the terms of the Mozilla Public 6 | License, v. 2.0. If a copy of the MPL was not distributed with this 7 | file, You can obtain one at https://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | #ifndef RVVM_RTL8169_H 11 | #define RVVM_RTL8169_H 12 | 13 | #include "pci-bus.h" 14 | #include "tap_api.h" 15 | 16 | PUBLIC pci_dev_t* rtl8169_init(pci_bus_t* pci_bus, tap_dev_t* tap); 17 | PUBLIC pci_dev_t* rtl8169_init_auto(rvvm_machine_t* machine); 18 | 19 | #endif 20 | -------------------------------------------------------------------------------- /src/devices/sound-hda.h: -------------------------------------------------------------------------------- 1 | /* 2 | sound-hda.h - HD Audio 3 | Copyright (C) 2025 David Korenchuk 4 | 5 | This Source Code Form is subject to the terms of the Mozilla Public 6 | License, v. 2.0. If a copy of the MPL was not distributed with this 7 | file, You can obtain one at https://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | #ifndef SOUND_HDA_H 11 | #define SOUND_HDA_H 12 | 13 | #include "rvvmlib.h" 14 | #include "pci-bus.h" 15 | 16 | typedef struct sound_subsystem_t sound_subsystem_t; 17 | 18 | struct sound_subsystem_t { 19 | void *sound_data; 20 | void (*write)(sound_subsystem_t *subsystem, void *data, size_t size); 21 | }; 22 | 23 | // Internal use 24 | bool alsa_sound_init(sound_subsystem_t *sound); 25 | 26 | PUBLIC pci_dev_t* sound_hda_init(pci_bus_t* pci_bus); 27 | PUBLIC pci_dev_t* sound_hda_init_auto(rvvm_machine_t* machine); 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /src/devices/syscon.c: -------------------------------------------------------------------------------- 1 | /* 2 | syscon.c - Poweroff/reset syscon device 3 | Copyright (C) 2021 LekKit 4 | 5 | This Source Code Form is subject to the terms of the Mozilla Public 6 | License, v. 2.0. If a copy of the MPL was not distributed with this 7 | file, You can obtain one at https://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | #include "syscon.h" 11 | #include "mem_ops.h" 12 | #include "fdtlib.h" 13 | 14 | #define SYSCON_POWEROFF 0x5555 15 | #define SYSCON_RESET 0x7777 16 | 17 | static bool syscon_mmio_write(rvvm_mmio_dev_t* dev, void* data, size_t offset, uint8_t size) 18 | { 19 | UNUSED(size); 20 | if (offset == 0) { 21 | switch(read_uint16_le_m(data)) { 22 | case SYSCON_POWEROFF: 23 | case SYSCON_RESET: 24 | rvvm_reset_machine(dev->machine, read_uint16_le_m(data) == SYSCON_RESET); 25 | break; 26 | default: 27 | break; 28 | } 29 | } 30 | return true; 31 | } 32 | 33 | static rvvm_mmio_type_t syscon_dev_type = { 34 | .name = "syscon", 35 | }; 36 | 37 | PUBLIC rvvm_mmio_dev_t* syscon_init(rvvm_machine_t* machine, rvvm_addr_t base_addr) 38 | { 39 | rvvm_mmio_dev_t syscon = { 40 | .addr = base_addr, 41 | .size = 0x1000, 42 | .read = rvvm_mmio_none, 43 | .write = syscon_mmio_write, 44 | .min_op_size = 2, 45 | .max_op_size = 2, 46 | .type = &syscon_dev_type, 47 | }; 48 | rvvm_mmio_dev_t* mmio = rvvm_attach_mmio(machine, &syscon); 49 | if (mmio == NULL) return mmio; 50 | #ifdef USE_FDT 51 | struct fdt_node* test = fdt_node_create_reg("test", base_addr); 52 | fdt_node_add_prop_reg(test, "reg", base_addr, 0x1000); 53 | fdt_node_add_prop(test, "compatible", "sifive,test1\0sifive,test0\0syscon\0", 33); 54 | fdt_node_add_child(rvvm_get_fdt_soc(machine), test); 55 | 56 | struct fdt_node* poweroff = fdt_node_create("poweroff"); 57 | fdt_node_add_prop_str(poweroff, "compatible", "syscon-poweroff"); 58 | fdt_node_add_prop_u32(poweroff, "value", SYSCON_POWEROFF); 59 | fdt_node_add_prop_u32(poweroff, "offset", 0); 60 | fdt_node_add_prop_u32(poweroff, "regmap", fdt_node_get_phandle(test)); 61 | fdt_node_add_child(rvvm_get_fdt_root(machine), poweroff); 62 | 63 | struct fdt_node* reboot = fdt_node_create("reboot"); 64 | fdt_node_add_prop_str(reboot, "compatible", "syscon-reboot"); 65 | fdt_node_add_prop_u32(reboot, "value", SYSCON_RESET); 66 | fdt_node_add_prop_u32(reboot, "offset", 0); 67 | fdt_node_add_prop_u32(reboot, "regmap", fdt_node_get_phandle(test)); 68 | fdt_node_add_child(rvvm_get_fdt_root(machine), reboot); 69 | #endif 70 | return mmio; 71 | } 72 | 73 | PUBLIC rvvm_mmio_dev_t* syscon_init_auto(rvvm_machine_t* machine) 74 | { 75 | rvvm_addr_t addr = rvvm_mmio_zone_auto(machine, SYSCON_DEFAULT_MMIO, 0x1000); 76 | return syscon_init(machine, addr); 77 | } 78 | -------------------------------------------------------------------------------- /src/devices/syscon.h: -------------------------------------------------------------------------------- 1 | /* 2 | syscon.h - Poweroff/reset syscon device 3 | Copyright (C) 2021 LekKit 4 | 5 | This Source Code Form is subject to the terms of the Mozilla Public 6 | License, v. 2.0. If a copy of the MPL was not distributed with this 7 | file, You can obtain one at https://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | #ifndef RVVM_SYSCON_H 11 | #define RVVM_SYSCON_H 12 | 13 | #include "rvvmlib.h" 14 | 15 | #define SYSCON_DEFAULT_MMIO 0x100000 16 | 17 | PUBLIC rvvm_mmio_dev_t* syscon_init(rvvm_machine_t* machine, rvvm_addr_t base_addr); 18 | PUBLIC rvvm_mmio_dev_t* syscon_init_auto(rvvm_machine_t* machine); 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /src/devices/tap_api.h: -------------------------------------------------------------------------------- 1 | /* 2 | tap_api.h - TAP Networking API 3 | Copyright (C) 2021 LekKit 4 | 5 | This Source Code Form is subject to the terms of the Mozilla Public 6 | License, v. 2.0. If a copy of the MPL was not distributed with this 7 | file, You can obtain one at https://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | #ifndef RVVM_TAP_H 11 | #define RVVM_TAP_H 12 | 13 | #include "rvvmlib.h" 14 | 15 | // Maximum size for an Ethernet II header + payload 16 | #define TAP_FRAME_SIZE 1514 17 | 18 | typedef struct { 19 | // Network card specific context 20 | void* net_dev; 21 | // Feed received Ethernet frame to the NIC (Without CRC) 22 | bool (*feed_rx)(void* net_dev, const void* data, size_t size); 23 | } tap_net_dev_t; 24 | 25 | typedef struct tap_dev tap_dev_t; 26 | 27 | // Create TAP interface 28 | PUBLIC tap_dev_t* tap_open(void); 29 | 30 | // Attach to the NIC 31 | PUBLIC void tap_attach(tap_dev_t* tap, const tap_net_dev_t* net_dev); 32 | 33 | // Send Ethernet frame (Without CRC) 34 | PUBLIC bool tap_send(tap_dev_t* tap, const void* data, size_t size); 35 | 36 | // Set/get interface MAC address 37 | PUBLIC bool tap_get_mac(tap_dev_t* tap, uint8_t mac[6]); 38 | PUBLIC bool tap_set_mac(tap_dev_t* tap, const uint8_t mac[6]); 39 | 40 | // Forward ports from host address into guest network 41 | // By default forwards to guest DHCP address 42 | // Format: "tcp/2022=22"; "[::1]:2022=22"; "127.0.0.1:2022=192.168.0.101:22" 43 | PUBLIC bool tap_portfwd(tap_dev_t* tap, const char* fwd); 44 | 45 | // Set the host interface addr for this TAP interface 46 | PUBLIC bool tap_ifaddr(tap_dev_t* tap, const char* addr); 47 | 48 | // Shut down the interface 49 | PUBLIC void tap_close(tap_dev_t* tap); 50 | 51 | #endif 52 | -------------------------------------------------------------------------------- /src/devices/tap_linux.c: -------------------------------------------------------------------------------- 1 | /* 2 | tap_linux.c - Linux TUN/TAP Networking 3 | Copyright (C) 2021 LekKit 4 | cerg2010cerg2010 5 | 6 | This Source Code Form is subject to the terms of the Mozilla Public 7 | License, v. 2.0. If a copy of the MPL was not distributed with this 8 | file, You can obtain one at https://mozilla.org/MPL/2.0/. 9 | */ 10 | 11 | #include "tap_api.h" 12 | #include "threading.h" 13 | #include "utils.h" 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | /* 27 | * Linux TUN/TAP networking manual by cerg2010cerg2010 (circa 2021) 28 | * In the guest: 29 | ip addr add 192.168.2.1/24 dev enp0s1 30 | ip link set enp0s1 up 31 | ip route add default dev enp0s1 32 | ip route del default 33 | ip route add default via 192.168.2.2 34 | echo 'nameserver 1.1.1.1' > /etc/resolv.conf 35 | * Workaround TX checksum failure: 36 | ethtool -K enp0s1 tx off 37 | * On the host (replace wlan0 with your host NIC ifname): 38 | sudo sysctl net.ipv4.ip_forward=1 39 | sudo iptables -t nat -A POSTROUTING -o wlan0 -j MASQUERADE 40 | sudo ip addr add 192.168.2.2/24 dev tap0 41 | */ 42 | 43 | struct tap_dev { 44 | tap_net_dev_t net; 45 | thread_ctx_t* thread; 46 | int fd; 47 | int shut[2]; 48 | char name[IFNAMSIZ]; 49 | }; 50 | 51 | static void* tap_thread(void* arg) 52 | { 53 | tap_dev_t* tap = (tap_dev_t*)arg; 54 | uint8_t buffer[TAP_FRAME_SIZE]; 55 | int ret = 0; 56 | struct pollfd pfds[2] = { 57 | { 58 | .fd = tap->fd, 59 | .events = POLLIN, 60 | }, 61 | { 62 | .fd = tap->shut[0], 63 | .events = POLLIN | POLLHUP, 64 | } 65 | }; 66 | while (true) { 67 | // Poll events 68 | poll(pfds, 2, -1); 69 | // Check for shutdown notification 70 | if (pfds[1].revents) break; 71 | // We received a packet 72 | if (pfds[0].revents & POLLIN) { 73 | ret = read(tap->fd, buffer, sizeof(buffer)); 74 | if (ret > 0) { 75 | tap->net.feed_rx(tap->net.net_dev, buffer, ret); 76 | } 77 | } 78 | } 79 | return arg; 80 | } 81 | 82 | tap_dev_t* tap_open(void) 83 | { 84 | tap_dev_t* tap = safe_new_obj(tap_dev_t); 85 | // Open TUN 86 | tap->fd = open("/dev/net/tun", O_RDWR); 87 | if (tap->fd < 0) { 88 | rvvm_error("Failed to open /dev/net/tun: %s", strerror(errno)); 89 | free(tap); 90 | return NULL; 91 | } 92 | // Assign ifname, set TAP mode 93 | struct ifreq ifr = {0}; 94 | rvvm_strlcpy(ifr.ifr_name, "tap0", sizeof(ifr.ifr_name)); 95 | ifr.ifr_flags = IFF_TAP | IFF_NO_PI; 96 | if (ioctl(tap->fd, TUNSETIFF, &ifr) < 0) { 97 | rvvm_error("ioctl(TUNSETIFF) failed: %s", strerror(errno)); 98 | close(tap->fd); 99 | free(tap); 100 | return NULL; 101 | } 102 | // TAP may be assigned a different name 103 | rvvm_strlcpy(tap->name, ifr.ifr_name, sizeof(tap->name)); 104 | 105 | // Create shutdown pipe 106 | if (pipe(tap->shut) < 0) { 107 | rvvm_error("pipe() failed: %s", strerror(errno)); 108 | close(tap->fd); 109 | free(tap); 110 | return NULL; 111 | } 112 | 113 | // Set the interface up 114 | int sock = socket(AF_INET, SOCK_DGRAM, 0); 115 | ioctl(sock, SIOCGIFFLAGS, &ifr); 116 | ifr.ifr_flags |= IFF_UP; 117 | ioctl(sock, SIOCSIFFLAGS, &ifr); 118 | close(sock); 119 | 120 | return tap; 121 | } 122 | 123 | void tap_attach(tap_dev_t* tap, const tap_net_dev_t* net_dev) 124 | { 125 | if (tap->net.feed_rx == NULL) { 126 | tap->net = *net_dev; 127 | // Run TAP thread 128 | tap->thread = thread_create(tap_thread, tap); 129 | } 130 | } 131 | 132 | bool tap_send(tap_dev_t* tap, const void* data, size_t size) 133 | { 134 | return write(tap->fd, data, size) >= 0; 135 | } 136 | 137 | bool tap_get_mac(tap_dev_t* tap, uint8_t mac[6]) 138 | { 139 | struct ifreq ifr = {0}; 140 | rvvm_strlcpy(ifr.ifr_name, tap->name, sizeof(ifr.ifr_name)); 141 | if (ioctl(tap->fd, SIOCGIFHWADDR, &ifr) < 0) return false; 142 | if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) { 143 | return false; 144 | } 145 | memcpy(mac, ifr.ifr_hwaddr.sa_data, 6); 146 | return true; 147 | } 148 | 149 | bool tap_set_mac(tap_dev_t* tap, const uint8_t mac[6]) 150 | { 151 | struct ifreq ifr = {0}; 152 | rvvm_strlcpy(ifr.ifr_name, tap->name, sizeof(ifr.ifr_name)); 153 | ifr.ifr_hwaddr.sa_family = ARPHRD_ETHER; 154 | memcpy(ifr.ifr_hwaddr.sa_data, mac, 6); 155 | return ioctl(tap->fd, SIOCSIFHWADDR, &ifr) >= 0; 156 | } 157 | 158 | bool tap_portfwd(tap_dev_t* tap, const char* fwd) 159 | { 160 | UNUSED(tap); UNUSED(fwd); 161 | return false; 162 | } 163 | 164 | void tap_close(tap_dev_t* tap) 165 | { 166 | // Shut down the TAP thread 167 | close(tap->shut[1]); 168 | thread_join(tap->thread); 169 | 170 | // Cleanup 171 | close(tap->fd); 172 | close(tap->shut[0]); 173 | free(tap); 174 | } 175 | -------------------------------------------------------------------------------- /src/devices/usb-xhci.h: -------------------------------------------------------------------------------- 1 | /* 2 | usb-xhci.h - USB Extensible Host Controller Interface 3 | Copyright (C) 2024 LekKit 4 | 5 | This Source Code Form is subject to the terms of the Mozilla Public 6 | License, v. 2.0. If a copy of the MPL was not distributed with this 7 | file, You can obtain one at https://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | #ifndef RVVM_USB_XHCI_H 11 | #define RVVM_USB_XHCI_H 12 | 13 | #include "pci-bus.h" 14 | 15 | PUBLIC pci_dev_t* usb_xhci_init(pci_bus_t* pci_bus); 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /src/dlib.c: -------------------------------------------------------------------------------- 1 | /* 2 | dlib.c - Dynamic library loader 3 | Copyright (C) 2023 LekKit 4 | 0xCatPKG <0xCatPKG@rvvm.dev> 5 | 0xCatPKG 6 | 7 | This Source Code Form is subject to the terms of the Mozilla Public 8 | License, v. 2.0. If a copy of the MPL was not distributed with this 9 | file, You can obtain one at https://mozilla.org/MPL/2.0/. 10 | */ 11 | 12 | #include "dlib.h" 13 | 14 | #if !defined(USE_NO_DLIB) && defined(_WIN32) && !defined(UNDER_CE) 15 | #include 16 | 17 | #define DLIB_WIN32_IMPL 1 18 | 19 | #elif !defined(USE_NO_DLIB) && !defined(__EMSCRIPTEN__) \ 20 | && (defined(__unix__) || defined(__APPLE__) || defined(__HAIKU__)) 21 | #include 22 | 23 | #define DLIB_POSIX_IMPL 1 24 | 25 | #if defined(__COSMOPOLITAN__) 26 | // Support Cosmopolitan libc & foreign ABI (MSABI, etc) via cosmo_dltramp() 27 | #define dlopen(lib, flags) cosmo_dlopen(lib, flags) 28 | #define dlsym(lib, symbol) cosmo_dltramp(cosmo_dlsym(lib, symbol)) 29 | #define dlclose(lib) cosmo_dlclose(lib) 30 | #endif 31 | 32 | #endif 33 | 34 | // RVVM internal headers come after system headers because of safe_free() 35 | #include "utils.h" 36 | 37 | SOURCE_OPTIMIZATION_SIZE 38 | 39 | struct dlib_ctx { 40 | #if defined(DLIB_WIN32_IMPL) 41 | HMODULE handle; 42 | #elif defined(DLIB_POSIX_IMPL) 43 | void* handle; 44 | #endif 45 | uint32_t flags; 46 | }; 47 | 48 | #if defined(DLIB_WIN32_IMPL) || defined(DLIB_POSIX_IMPL) 49 | 50 | static dlib_ctx_t* dlib_open_internal(const char* lib_name, uint32_t flags) 51 | { 52 | #if defined(DLIB_WIN32_IMPL) 53 | wchar_t u16_name[256] = {0}; 54 | MultiByteToWideChar(CP_UTF8, 0, lib_name, -1, u16_name, STATIC_ARRAY_SIZE(u16_name)); 55 | 56 | // Try to get module from already loaded modules 57 | HMODULE handle = GetModuleHandleW(u16_name); 58 | if (handle) { 59 | // Prevent unloading an existing module 60 | flags &= ~DLIB_MAY_UNLOAD; 61 | } else { 62 | handle = LoadLibraryW(u16_name); 63 | } 64 | 65 | if (handle) { 66 | dlib_ctx_t* lib = safe_new_obj(dlib_ctx_t); 67 | lib->handle = handle; 68 | lib->flags = flags; 69 | return lib; 70 | } 71 | #elif defined(DLIB_POSIX_IMPL) 72 | void* handle = dlopen(lib_name, RTLD_LAZY | RTLD_LOCAL); 73 | if (handle) { 74 | dlib_ctx_t* lib = safe_new_obj(dlib_ctx_t); 75 | lib->handle = handle; 76 | lib->flags = flags; 77 | return lib; 78 | } 79 | #else 80 | UNUSED(lib_name); 81 | UNUSED(flags); 82 | #endif 83 | 84 | return NULL; 85 | } 86 | 87 | static dlib_ctx_t* dlib_open_named(const char* prefix, const char* lib_name, const char* suffix, uint32_t flags) 88 | { 89 | char name[256] = {0}; 90 | size_t off = rvvm_strlcpy(name, prefix, sizeof(name)); 91 | off += rvvm_strlcpy(name + off, lib_name, sizeof(name) - off); 92 | rvvm_strlcpy(name + off, suffix, sizeof(name) - off); 93 | return dlib_open_internal(name, flags); 94 | } 95 | 96 | #define DLIB_PROBE_INTERNAL(lib_name, flags) \ 97 | do { \ 98 | dlib_ctx_t* lib = dlib_open_internal(lib_name, flags); \ 99 | if (lib) { \ 100 | return lib; \ 101 | } \ 102 | } while (0) 103 | 104 | #define DLIB_PROBE_NAMED(prefix, lib_name, suffix, flags) \ 105 | do { \ 106 | dlib_ctx_t* lib = dlib_open_named(prefix, lib_name, suffix, flags); \ 107 | if (lib) { \ 108 | return lib; \ 109 | } \ 110 | } while (0) 111 | 112 | #endif 113 | 114 | dlib_ctx_t* dlib_open(const char* lib_name, uint32_t flags) 115 | { 116 | #if defined(DLIB_WIN32_IMPL) || defined(DLIB_POSIX_IMPL) 117 | if (lib_name) { 118 | DLIB_PROBE_INTERNAL(lib_name, flags); 119 | 120 | if ((flags & DLIB_NAME_PROBE) && !rvvm_strfind(lib_name, "/")) { 121 | DLIB_PROBE_NAMED("lib", lib_name, ".so", flags); 122 | DLIB_PROBE_NAMED("lib", lib_name, ".dll", flags); 123 | DLIB_PROBE_NAMED("lib", lib_name, ".dylib", flags); 124 | DLIB_PROBE_NAMED("", lib_name, ".so", flags); 125 | DLIB_PROBE_NAMED("", lib_name, ".dll", flags); 126 | DLIB_PROBE_NAMED("", lib_name, ".dylib", flags); 127 | } 128 | } 129 | #else 130 | UNUSED(lib_name); 131 | UNUSED(flags); 132 | #endif 133 | 134 | return NULL; 135 | } 136 | 137 | void dlib_close(dlib_ctx_t* lib) 138 | { 139 | // Silently ignore load error 140 | if (lib && (lib->flags & DLIB_MAY_UNLOAD)) { 141 | rvvm_info("Unloading a library"); 142 | #if defined(DLIB_WIN32_IMPL) 143 | FreeLibrary(lib->handle); 144 | #elif defined(DLIB_POSIX_IMPL) 145 | dlclose(lib->handle); 146 | #endif 147 | } 148 | free(lib); 149 | } 150 | 151 | void* dlib_resolve(dlib_ctx_t* lib, const char* symbol_name) 152 | { 153 | void* ret = NULL; 154 | UNUSED(symbol_name); 155 | if (lib) { 156 | #if defined(DLIB_WIN32_IMPL) 157 | ret = (void*)GetProcAddress(lib->handle, symbol_name); 158 | #elif defined(DLIB_POSIX_IMPL) 159 | ret = (void*)dlsym(lib->handle, symbol_name); 160 | #endif 161 | } 162 | return ret; 163 | } 164 | 165 | void* dlib_get_symbol(const char* lib_name, const char* symbol_name) 166 | { 167 | if (lib_name) { 168 | dlib_ctx_t* lib = dlib_open(lib_name, 0); 169 | void* symbol = dlib_resolve(lib, symbol_name); 170 | dlib_close(lib); 171 | return symbol; 172 | } 173 | #if defined(DLIB_POSIX_IMPL) && defined(RTLD_DEFAULT) 174 | // Resolve a global symbol 175 | return (void*)dlsym(RTLD_DEFAULT, symbol_name); 176 | #else 177 | return NULL; 178 | #endif 179 | } 180 | -------------------------------------------------------------------------------- /src/dlib.h: -------------------------------------------------------------------------------- 1 | /* 2 | dlib.h - Dynamic library loader 3 | Copyright (C) 2023 LekKit 4 | 0xCatPKG <0xCatPKG@rvvm.dev> 5 | 0xCatPKG 6 | 7 | This Source Code Form is subject to the terms of the Mozilla Public 8 | License, v. 2.0. If a copy of the MPL was not distributed with this 9 | file, You can obtain one at https://mozilla.org/MPL/2.0/. 10 | */ 11 | 12 | #ifndef RVVM_DLIB_H 13 | #define RVVM_DLIB_H 14 | 15 | #include 16 | #include 17 | 18 | #define DLIB_NAME_PROBE 0x1 // Probe various A.so, libA.so variations 19 | #define DLIB_MAY_UNLOAD 0x2 // Allow dlib_close() to actually unload the library 20 | 21 | typedef struct dlib_ctx dlib_ctx_t; 22 | 23 | // Load a library by name 24 | dlib_ctx_t* dlib_open(const char* lib_name, uint32_t flags); 25 | 26 | // Drop the library handle. unload the library if DLIB_MAY_UNLOAD was set 27 | void dlib_close(dlib_ctx_t* lib); 28 | 29 | // Resolve public library symbols 30 | void* dlib_resolve(dlib_ctx_t* lib, const char* symbol_name); 31 | 32 | // Get symbol from a specific lib, or a global symbol if lib_name == NULL 33 | void* dlib_get_symbol(const char* lib_name, const char* symbol_name); 34 | 35 | #endif 36 | -------------------------------------------------------------------------------- /src/elf_load.c: -------------------------------------------------------------------------------- 1 | /* 2 | elf_load.c - ELF Loader 3 | Copyright (C) 2023 LekKit 4 | 2021 cerg2010cerg2010 5 | 6 | This Source Code Form is subject to the terms of the Mozilla Public 7 | License, v. 2.0. If a copy of the MPL was not distributed with this 8 | file, You can obtain one at https://mozilla.org/MPL/2.0/. 9 | */ 10 | 11 | #include "elf_load.h" 12 | #include "mem_ops.h" 13 | #include "utils.h" 14 | #include "vma_ops.h" 15 | 16 | #define ELF_ET_NONE 0x0 17 | #define ELF_ET_REL 0x1 18 | #define ELF_ET_EXEC 0x2 19 | #define ELF_ET_DYN 0x3 20 | 21 | #define ELF_PT_NULL 0x0 22 | #define ELF_PT_LOAD 0x1 23 | #define ELF_PT_DYNAMIC 0x2 24 | #define ELF_PT_INTERP 0x3 25 | #define ELF_PT_NOTE 0x4 26 | #define ELF_PT_SHLIB 0x5 27 | #define ELF_PT_PHDR 0x6 28 | #define ELF_PT_TLS 0x7 29 | 30 | #define ELF_PF_X 0x1 31 | #define ELF_PF_W 0x2 32 | #define ELF_PF_R 0x4 33 | 34 | // TODO: Handling >64k PHENTs 35 | #define ELF_PN_XNUM 0xFFFF 36 | 37 | #define WRAP_ERR(cond, error) \ 38 | if (!(cond)) { \ 39 | rvvm_error(error); \ 40 | return false; \ 41 | } 42 | 43 | SOURCE_OPTIMIZATION_SIZE 44 | 45 | bool elf_load_file(rvfile_t* file, elf_desc_t* elf) 46 | { 47 | uint8_t tmp[64] = {0}; 48 | WRAP_ERR(file && elf, "Invalid arguments to elf_load_file()"); 49 | WRAP_ERR(rvread(file, tmp, 64, 0) == 64, "Failed to read ELF header"); 50 | WRAP_ERR(read_uint32_le_m(tmp) == 0x464c457F, "Not an ELF file"); 51 | WRAP_ERR(tmp[4] == 1 || tmp[4] == 2, "Invalid ELF class"); 52 | WRAP_ERR(tmp[5] == 1, "Not a little-endian ELF"); 53 | 54 | // Parse ELF header 55 | bool objcopy = !!elf->base; 56 | bool class64 = (tmp[4] == 2); 57 | uint16_t elf_type = read_uint16_le_m(tmp + 16); 58 | uint64_t elf_entry = class64 ? read_uint64_le_m(tmp + 24) : read_uint32_le_m(tmp + 24); 59 | uint64_t elf_phoff = class64 ? read_uint64_le_m(tmp + 32) : read_uint32_le_m(tmp + 28); 60 | //uint64_t elf_shoff = class64 ? read_uint64_le_m(tmp + 40) : read_uint32_le_m(tmp + 32); 61 | size_t elf_phnsz = class64 ? 56 : 32; 62 | size_t elf_phnum = read_uint16_le_m(tmp + (class64 ? 56 : 44)); 63 | 64 | elf->entry = elf_entry; 65 | elf->interp_path = NULL; 66 | elf->phdr = 0; 67 | elf->phnum = elf_phnum; 68 | 69 | // Determine lowest / highest virtual address, PHDR address 70 | uint64_t elf_loaddr = (uint64_t)-1; 71 | uint64_t elf_hiaddr = 0; 72 | for (size_t i=0; i elf_hiaddr) elf_hiaddr = p_vaddr + p_memsz; 81 | } 82 | if (p_type == ELF_PT_PHDR) elf->phdr = p_vaddr; 83 | } 84 | if (elf_loaddr == (uint64_t)-1) elf_loaddr = 0; // No ELF segments 85 | 86 | // Relocate pointers 87 | if (objcopy) { 88 | if (elf->entry) elf->entry -= elf_loaddr; 89 | if (elf->phdr) elf->phdr -= elf_loaddr; 90 | } else { 91 | // Userland ELF loading 92 | elf->buf_size = elf_hiaddr - elf_loaddr; 93 | if (elf_type == ELF_ET_DYN) { 94 | // Dynamic (PIC) ELF, relocate it 95 | elf->base = vma_alloc(NULL, elf->buf_size, VMA_RDWR); 96 | WRAP_ERR(elf->base, "Failed to allocate dynamic ELF VMA"); 97 | } else { 98 | // Non-relocatable ELF at fixed address 99 | elf->base = vma_alloc((void*)(size_t)elf_loaddr, elf->buf_size, VMA_RDWR | VMA_FIXED); 100 | WRAP_ERR(elf->base, "Failed to map fixed ELF VMA, address collision?"); 101 | } 102 | if (elf->entry) elf->entry += (size_t)elf->base - elf_loaddr; 103 | if (elf->phdr) elf->phdr += (size_t)elf->base - elf_loaddr; 104 | } 105 | 106 | for (size_t i=0; ibase) + (p_vaddr - elf_loaddr); 119 | WRAP_ERR(p_vaddr + p_memsz <= elf_loaddr + elf->buf_size, "ELF segment does not fit in memory"); 120 | WRAP_ERR(rvread(file, vaddr, p_fsize, p_offset) == p_fsize, "Failed to read ELF segment"); 121 | } 122 | if (p_type == ELF_PT_INTERP && !objcopy && !elf->interp_path) { 123 | // Get ELF interpreter path 124 | WRAP_ERR(p_fsize < 1024, "ELF interpreter path is too long"); 125 | elf->interp_path = safe_new_arr(char, p_fsize + 1); 126 | WRAP_ERR(rvread(file, elf->interp_path, p_fsize, p_offset) == p_fsize, "Failed to read ELF interp_path"); 127 | } 128 | } 129 | 130 | return true; 131 | } 132 | 133 | bool bin_objcopy(rvfile_t* file, void* buffer, size_t size, bool try_elf) 134 | { 135 | uint8_t mag[4] = {0}; 136 | if (try_elf && rvread(file, mag, 4, 0) == 4 && read_uint32_le_m(mag) == 0x464c457F) { 137 | elf_desc_t elf = { 138 | .base = buffer, 139 | .buf_size = size, 140 | }; 141 | if (elf_load_file(file, &elf)) return true; 142 | } 143 | return rvread(file, buffer, size, 0); 144 | } 145 | -------------------------------------------------------------------------------- /src/elf_load.h: -------------------------------------------------------------------------------- 1 | /* 2 | elf_load.h - ELF Loader 3 | Copyright (C) 2023 LekKit 4 | 2021 cerg2010cerg2010 5 | 6 | This Source Code Form is subject to the terms of the Mozilla Public 7 | License, v. 2.0. If a copy of the MPL was not distributed with this 8 | file, You can obtain one at https://mozilla.org/MPL/2.0/. 9 | */ 10 | 11 | #ifndef ELF_LOAD_H 12 | #define ELF_LOAD_H 13 | 14 | #include "blk_io.h" 15 | 16 | typedef struct { 17 | // Pass a buffer for objcopy, NULL for userland loading 18 | // Receive base ELF address for userland 19 | void* base; 20 | // Objcopy buffer size 21 | size_t buf_size; 22 | 23 | // Various loaded ELF info 24 | size_t entry; 25 | char* interp_path; 26 | size_t phdr; 27 | size_t phnum; 28 | } elf_desc_t; 29 | 30 | bool elf_load_file(rvfile_t* file, elf_desc_t* elf); 31 | 32 | bool bin_objcopy(rvfile_t* file, void* buffer, size_t size, bool try_elf); 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /src/fdtlib.h: -------------------------------------------------------------------------------- 1 | /* 2 | fdtlib.h - Flattened Device Tree Library 3 | Copyright (C) 2021 cerg2010cerg2010 4 | LekKit 5 | 6 | This Source Code Form is subject to the terms of the Mozilla Public 7 | License, v. 2.0. If a copy of the MPL was not distributed with this 8 | file, You can obtain one at https://mozilla.org/MPL/2.0/. 9 | */ 10 | 11 | #ifndef RVVM_FDTLIB_H 12 | #define RVVM_FDTLIB_H 13 | 14 | #include "rvvmlib.h" 15 | 16 | struct fdt_node; 17 | 18 | /* 19 | * Node handling 20 | */ 21 | 22 | // Create a fdt node, root node should have name = NULL 23 | PUBLIC struct fdt_node* fdt_node_create(const char* name); 24 | 25 | // Create a fdt node with address, like device@10000 26 | PUBLIC struct fdt_node* fdt_node_create_reg(const char* name, uint64_t addr); 27 | 28 | // Attach child node 29 | PUBLIC void fdt_node_add_child(struct fdt_node* node, struct fdt_node* child); 30 | 31 | // Lookup for child node by name (returns NULL on failure) 32 | PUBLIC struct fdt_node* fdt_node_find(struct fdt_node* node, const char* name); 33 | 34 | // Lookup for child node by name + addr, like device@10000 (returns NULL on failure) 35 | PUBLIC struct fdt_node* fdt_node_find_reg(struct fdt_node* node, const char* name, uint64_t addr); 36 | 37 | // Lookup for any reg child node by name, like device@* (returns NULL on failure) 38 | PUBLIC struct fdt_node* fdt_node_find_reg_any(struct fdt_node* node, const char *name); 39 | 40 | // Get child node phandle. Allocates phandles transparently, all node hierarchy should be attached! 41 | PUBLIC uint32_t fdt_node_get_phandle(struct fdt_node* node); 42 | 43 | /* 44 | * Property handling 45 | */ 46 | 47 | // Add arbitary byte buffer property 48 | PUBLIC void fdt_node_add_prop(struct fdt_node* node, const char* name, const void* data, uint32_t len); 49 | 50 | // Add single-cell property 51 | PUBLIC void fdt_node_add_prop_u32(struct fdt_node* node, const char* name, uint32_t val); 52 | 53 | // Add double-cell property 54 | PUBLIC void fdt_node_add_prop_u64(struct fdt_node* node, const char* name, uint64_t val); 55 | 56 | // Add multi-cell property 57 | PUBLIC void fdt_node_add_prop_cells(struct fdt_node* node, const char* name, uint32_t* cells, uint32_t count); 58 | 59 | // Add string property 60 | PUBLIC void fdt_node_add_prop_str(struct fdt_node* node, const char* name, const char* val); 61 | 62 | // Add register range property (addr cells: 2, size cells: 2) 63 | PUBLIC void fdt_node_add_prop_reg(struct fdt_node* node, const char* name, uint64_t begin, uint64_t size); 64 | 65 | // Returns underlying property data 66 | PUBLIC void* fdt_node_get_prop_data(struct fdt_node* node, const char* name); 67 | 68 | // Returns underlying property size 69 | PUBLIC size_t fdt_node_get_prop_size(struct fdt_node* node, const char* name); 70 | 71 | // Delete property 72 | PUBLIC bool fdt_node_del_prop(struct fdt_node* node, const char* name); 73 | 74 | /* 75 | * Serialization, cleanup 76 | */ 77 | 78 | // Recursively free a node and it's child nodes 79 | PUBLIC void fdt_node_free(struct fdt_node* node); 80 | 81 | // Returns required buffer size for serializing 82 | PUBLIC size_t fdt_size(struct fdt_node* node); 83 | 84 | // Serialize DTB into buffer, returns 0 when there's insufficient space 85 | // Returns required buffer size when buffer == NULL 86 | PUBLIC size_t fdt_serialize(struct fdt_node* node, void* buffer, size_t size, uint32_t boot_cpuid); 87 | 88 | #endif 89 | -------------------------------------------------------------------------------- /src/fpu_ops.h: -------------------------------------------------------------------------------- 1 | /* 2 | fpu_ops.h - FPU Rounding/Exceptions non-fenv fallback 3 | Copyright (C) 2021 LekKit 4 | 5 | This Source Code Form is subject to the terms of the Mozilla Public 6 | License, v. 2.0. If a copy of the MPL was not distributed with this 7 | file, You can obtain one at https://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | #ifndef LEKKIT_FPU_OPS_H 11 | #define LEKKIT_FPU_OPS_H 12 | 13 | #include "compiler.h" 14 | 15 | #if !defined(USE_NO_FENV) && CHECK_INCLUDE(fenv.h, 1) 16 | // Target has 17 | #include 18 | #endif 19 | 20 | #if defined(FE_ALL_EXCEPT) 21 | 22 | // Fix rounding misoptimizations even when -frounding-math is not present 23 | #if CLANG_CHECK_VER(12, 0) 24 | #pragma float_control(precise) 25 | #pragma STDC FENV_ACCESS ON 26 | #elif defined(_MSC_VER) 27 | #pragma fenv_access(on) 28 | #endif 29 | 30 | #else 31 | 32 | // Some targets (Emscripten, Windows CE) explicitly lack the ability to manipulate host FPU modes. 33 | // These shims are provided to build & run on these targets. 34 | #define HOST_NO_FENV 1 35 | 36 | static forceinline int fpu_dummy_op_internal(void) 37 | { 38 | return 0; 39 | } 40 | 41 | static forceinline int fpu_dummy_op_internal_i(int i) 42 | { 43 | UNUSED(i); 44 | return 0; 45 | } 46 | 47 | #define feclearexcept(i) fpu_dummy_op_internal_i(i) 48 | #define feraiseexcept(i) fpu_dummy_op_internal_i(i) 49 | #define fetestexcept(i) fpu_dummy_op_internal_i(i) 50 | 51 | #define fegetround() fpu_dummy_op_internal() 52 | #define fesetround(i) fpu_dummy_op_internal_i(i) 53 | 54 | #endif 55 | 56 | #ifndef FE_DIVBYZERO 57 | #define FE_DIVBYZERO 0 58 | #endif 59 | 60 | #ifndef FE_INEXACT 61 | #define FE_INEXACT 0 62 | #endif 63 | 64 | #ifndef FE_INVALID 65 | #define FE_INVALID 0 66 | #endif 67 | 68 | #ifndef FE_OVERFLOW 69 | #define FE_OVERFLOW 0 70 | #endif 71 | 72 | #ifndef FE_UNDERFLOW 73 | #define FE_UNDERFLOW 0 74 | #endif 75 | 76 | #ifndef FE_ALL_EXCEPT 77 | #define FE_ALL_EXCEPT 0 78 | #endif 79 | 80 | #ifndef FE_DOWNWARD 81 | #define FE_DOWNWARD 0 82 | #endif 83 | 84 | #ifndef FE_TONEAREST 85 | #define FE_TONEAREST 0 86 | #endif 87 | 88 | #ifndef FE_TOWARDZERO 89 | #define FE_TOWARDZERO 0 90 | #endif 91 | 92 | #ifndef FE_UPWARD 93 | #define FE_UPWARD 0 94 | #endif 95 | 96 | #ifndef FE_DFL_ENV 97 | #define FE_DFL_ENV 0 98 | #endif 99 | 100 | #endif 101 | -------------------------------------------------------------------------------- /src/gdbstub.h: -------------------------------------------------------------------------------- 1 | /* 2 | gdbstub.h - GDB Debugging Stub 3 | Copyright (C) 2024 LekKit 4 | 5 | This Source Code Form is subject to the terms of the Mozilla Public 6 | License, v. 2.0. If a copy of the MPL was not distributed with this 7 | file, You can obtain one at https://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | #ifndef RVVM_GDBSTUB_H 11 | #define RVVM_GDBSTUB_H 12 | 13 | #include "rvvmlib.h" 14 | 15 | typedef struct gdb_server gdb_server_t; 16 | 17 | PUBLIC bool gdbstub_init(rvvm_machine_t* machine, const char* bind); 18 | 19 | bool gdbstub_halt(gdb_server_t* server); 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /src/hashmap.c: -------------------------------------------------------------------------------- 1 | /* 2 | hashmap.c - Open-addressing hashmap implementation 3 | Copyright (C) 2021 LekKit 4 | 5 | This Source Code Form is subject to the terms of the Mozilla Public 6 | License, v. 2.0. If a copy of the MPL was not distributed with this 7 | file, You can obtain one at https://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | #include "hashmap.h" 11 | #include "bit_ops.h" 12 | #include "compiler.h" 13 | #include "mem_ops.h" 14 | #include "utils.h" 15 | 16 | slow_path void hashmap_grow_internal(hashmap_t* map, size_t key, size_t val) 17 | { 18 | hashmap_resize(map, (map->size + 1) << 1); 19 | hashmap_put(map, key, val); 20 | } 21 | 22 | static size_t hashmap_calc_shrink(hashmap_t* map) 23 | { 24 | if (unlikely(map->entries && map->entry_balance > map->entries)) { 25 | return map->size / (map->entry_balance / map->entries); 26 | } 27 | return map->size; 28 | } 29 | 30 | slow_path void hashmap_shrink_internal(hashmap_t* map) 31 | { 32 | size_t size = hashmap_calc_shrink(map); 33 | if (unlikely(size < map->size)) { 34 | hashmap_resize(map, size); 35 | } 36 | } 37 | 38 | slow_path void hashmap_rebalance_internal(hashmap_t* map, size_t index) 39 | { 40 | size_t j = index, k; 41 | while (true) { 42 | map->buckets[index].val = 0; 43 | do { 44 | j = (j + 1) & map->size; 45 | if (!map->buckets[j].val) { 46 | return; 47 | } 48 | k = hashmap_hash(map->buckets[j].key) & map->size; 49 | } while ((index <= j) ? (index < k && k <= j) : (index < k || k <= j)); 50 | 51 | map->buckets[index] = map->buckets[j]; 52 | index = j; 53 | } 54 | } 55 | 56 | void hashmap_init(hashmap_t* map, size_t size) 57 | { 58 | if (!size) { 59 | size = 16; 60 | } 61 | map->entries = 0; 62 | map->entry_balance = 0; 63 | map->size = bit_next_pow2(size) - 1; 64 | map->buckets = safe_new_arr(hashmap_bucket_t, map->size + 1); 65 | } 66 | 67 | void hashmap_destroy(hashmap_t* map) 68 | { 69 | free(map->buckets); 70 | memset(map, 0, sizeof(hashmap_t)); 71 | } 72 | 73 | void hashmap_resize(hashmap_t* map, size_t size) 74 | { 75 | hashmap_t tmp; 76 | hashmap_init(&tmp, size); 77 | hashmap_foreach (map, k, v) { 78 | hashmap_put(&tmp, k, v); 79 | } 80 | free(map->buckets); 81 | map->buckets = tmp.buckets; 82 | map->size = tmp.size; 83 | map->entry_balance = map->entries; 84 | } 85 | 86 | void hashmap_clear(hashmap_t* map) 87 | { 88 | size_t size = bit_next_pow2(hashmap_calc_shrink(map)) - 1; 89 | if (size < map->size) { 90 | map->size = size; 91 | map->buckets = safe_realloc(map->buckets, (map->size + 1) * sizeof(hashmap_bucket_t)); 92 | map->entry_balance = map->entries; 93 | } 94 | memset(map->buckets, 0, (map->size + 1) * sizeof(hashmap_bucket_t)); 95 | map->entries = 0; 96 | } 97 | -------------------------------------------------------------------------------- /src/hashmap.h: -------------------------------------------------------------------------------- 1 | /* 2 | hashmap.h - Open-addressing hashmap implementation 3 | Copyright (C) 2021 LekKit 4 | 5 | This Source Code Form is subject to the terms of the Mozilla Public 6 | License, v. 2.0. If a copy of the MPL was not distributed with this 7 | file, You can obtain one at https://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | #ifndef LEKKIT_HASHMAP_H 11 | #define LEKKIT_HASHMAP_H 12 | 13 | #include "compiler.h" 14 | 15 | #include 16 | #include 17 | 18 | // This is the worst-case scenario lookup complexity, only 1/256 of entries may reach this point at all. 19 | // Setting the value lower may improve worst-case scenario by a slight margin, but increases memory consumption by 20 | // orders of magnitude. 21 | #define HASHMAP_MAX_PROBES 256 22 | 23 | typedef struct randomize_layout { 24 | size_t key; 25 | size_t val; 26 | } hashmap_bucket_t; 27 | 28 | // Bucket with val=0 is treated as unused bucket to reduce memory usage (no additional flag), size is actually a bitmask 29 | // holding lowest 1s to represent size encoding space. 30 | typedef struct randomize_layout { 31 | hashmap_bucket_t* buckets; 32 | size_t size; 33 | size_t entries; 34 | size_t entry_balance; 35 | } hashmap_t; 36 | 37 | // Internal hashmap implementation paths 38 | slow_path void hashmap_grow_internal(hashmap_t* map, size_t key, size_t val); 39 | slow_path void hashmap_shrink_internal(hashmap_t* map); 40 | slow_path void hashmap_rebalance_internal(hashmap_t* map, size_t index); 41 | 42 | // Hint the expected amount of entries on map creation 43 | void hashmap_init(hashmap_t* map, size_t size); 44 | void hashmap_destroy(hashmap_t* map); 45 | void hashmap_resize(hashmap_t* map, size_t size); 46 | void hashmap_clear(hashmap_t* map); 47 | 48 | static inline size_t hashmap_used_mem(hashmap_t* map) 49 | { 50 | return (map->size + 1) * sizeof(hashmap_bucket_t); 51 | } 52 | 53 | #define hashmap_foreach(map, k, v) \ 54 | for (size_t k, v, MACRO_IDENT(hashmap_iter) = 0; /**/ \ 55 | k = ((map)->buckets[MACRO_IDENT(hashmap_iter) & (map)->size].key), /**/ \ 56 | v = ((map)->buckets[MACRO_IDENT(hashmap_iter) & (map)->size].val), /**/ \ 57 | MACRO_IDENT(hashmap_iter) <= (map)->size; /**/ \ 58 | ++MACRO_IDENT(hashmap_iter)) /**/ \ 59 | if (v) 60 | 61 | static forceinline size_t hashmap_hash(size_t k) 62 | { 63 | k ^= k << 21; 64 | k ^= k >> 17; 65 | #if (SIZE_MAX > 0xFFFFFFFF) 66 | k ^= k >> 35; 67 | k ^= k >> 51; 68 | #endif 69 | return k; 70 | } 71 | 72 | static inline void hashmap_put(hashmap_t* map, size_t key, size_t val) 73 | { 74 | size_t hash = hashmap_hash(key); 75 | for (size_t i = 0; i < HASHMAP_MAX_PROBES; ++i) { 76 | size_t index = (hash + i) & map->size; 77 | 78 | if (likely(map->buckets[index].key == key)) { 79 | // The key is already used, change value 80 | map->buckets[index].val = val; 81 | 82 | if (!val) { 83 | // Value = 0 means we should clear a bucket 84 | // Rebalance colliding trailing entries 85 | hashmap_rebalance_internal(map, index); 86 | map->entries--; 87 | } 88 | return; 89 | } else if (likely(!map->buckets[index].val && val)) { 90 | // Empty bucket found, the key is unused 91 | map->entries++; 92 | map->buckets[index].key = key; 93 | map->buckets[index].val = val; 94 | return; 95 | } 96 | } 97 | // Near-key space is polluted with colliding entries, reallocate and rehash 98 | // Puts the new entry as well to simplify the inlined function 99 | if (unlikely(val)) { 100 | hashmap_grow_internal(map, key, val); 101 | } 102 | } 103 | 104 | static forceinline size_t hashmap_get(const hashmap_t* map, size_t key) 105 | { 106 | size_t hash = hashmap_hash(key); 107 | for (size_t i = 0; i < HASHMAP_MAX_PROBES; ++i) { 108 | size_t index = (hash + i) & map->size; 109 | if (likely(map->buckets[index].key == key || !map->buckets[index].val)) { 110 | return map->buckets[index].val; 111 | } 112 | } 113 | return 0; 114 | } 115 | 116 | static inline void hashmap_remove(hashmap_t* map, size_t key) 117 | { 118 | // Treat value zero as removed key 119 | hashmap_put(map, key, 0); 120 | if (unlikely(map->entries < map->entry_balance && map->entries > 256)) { 121 | hashmap_shrink_internal(map); 122 | } 123 | } 124 | 125 | #endif 126 | -------------------------------------------------------------------------------- /src/networking.h: -------------------------------------------------------------------------------- 1 | /* 2 | networking.h - Network sockets (IPv4/IPv6), Event polling 3 | Copyright (C) 2021 LekKit 4 | 5 | This Source Code Form is subject to the terms of the Mozilla Public 6 | License, v. 2.0. If a copy of the MPL was not distributed with this 7 | file, You can obtain one at https://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | #ifndef LEKKIT_NETWORKING_H 11 | #define LEKKIT_NETWORKING_H 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | typedef struct net_sock net_sock_t; 18 | 19 | typedef struct { 20 | // Address family (IPv4/IPv6) 21 | uint16_t type; 22 | 23 | // Port (In host byte order) 24 | uint16_t port; 25 | 26 | // For IPv4: ip[0].ip[1].ip[2].ip[3] 27 | uint8_t ip[16]; 28 | } net_addr_t; 29 | 30 | #define NET_TYPE_IPV4 0x0 31 | #define NET_TYPE_IPV6 0x1 32 | #define NET_PORT_ANY 0 33 | 34 | extern const net_addr_t net_ipv4_any_addr; 35 | extern const net_addr_t net_ipv6_any_addr; 36 | extern const net_addr_t net_ipv4_local_addr; 37 | extern const net_addr_t net_ipv6_local_addr; 38 | 39 | // Passed to listen/bind as a shorthand (Picks any free port) 40 | #define NET_IPV4_ANY (&net_ipv4_any_addr) 41 | #define NET_IPV4_LOCAL (&net_ipv4_local_addr) 42 | #define NET_IPV6_ANY (&net_ipv6_any_addr) 43 | #define NET_IPV6_LOCAL (&net_ipv6_local_addr) 44 | 45 | #define NET_ERR_NONE 0 46 | #define NET_ERR_UNKNOWN (-1) 47 | #define NET_ERR_BLOCK (-2) 48 | #define NET_ERR_DISCONNECT (-3) 49 | #define NET_ERR_RESET (-4) 50 | 51 | // Parses IPv6 address string, returns parsed length or 0 on failure 52 | size_t net_parse_ipv6(net_addr_t* addr, const char* str); 53 | 54 | // Parses IPv4 address string, returns parsed length or 0 on failure 55 | size_t net_parse_ipv4(net_addr_t* addr, const char* str); 56 | 57 | // Parses string with IPv4/IPv6 and/or port, returns parsed length or 0 on failure 58 | size_t net_parse_addr(net_addr_t* addr, const char* str); 59 | 60 | // TCP Sockets 61 | 62 | net_sock_t* net_tcp_listen(const net_addr_t* addr); 63 | net_sock_t* net_tcp_accept(net_sock_t* listener); 64 | net_sock_t* net_tcp_connect(const net_addr_t* dst, const net_addr_t* src, bool block); 65 | bool net_tcp_sockpair(net_sock_t* pair[2]); 66 | bool net_tcp_status(net_sock_t* sock); // Connected & not yet closed on both sides 67 | bool net_tcp_shutdown(net_sock_t* sock); // Send EOF (FIN), only recv() works afterwards 68 | 69 | int32_t net_tcp_send(net_sock_t* sock, const void* buffer, size_t size); 70 | int32_t net_tcp_recv(net_sock_t* sock, void* buffer, size_t size); 71 | 72 | // UDP Sockets 73 | 74 | net_sock_t* net_udp_bind(const net_addr_t* addr); 75 | 76 | size_t net_udp_send(net_sock_t* sock, const void* buffer, size_t size, const net_addr_t* addr); 77 | int32_t net_udp_recv(net_sock_t* sock, void* buffer, size_t size, net_addr_t* addr); 78 | 79 | // Generic socket operations 80 | 81 | net_addr_t* net_sock_addr(net_sock_t* sock); 82 | uint16_t net_sock_port(net_sock_t* sock); 83 | bool net_sock_set_blocking(net_sock_t* sock, bool block); 84 | void net_sock_close(net_sock_t* sock); 85 | 86 | // Socket event polling 87 | 88 | typedef struct net_poll net_poll_t; 89 | 90 | typedef struct { 91 | uint32_t flags; 92 | void* data; 93 | } net_event_t; 94 | 95 | // Incoming connection, data received or peer disconnected 96 | // Implicitly watched on all added sockets 97 | #define NET_POLL_RECV 0x1 98 | 99 | // Transmission is possible or outbound connect finished 100 | // Check connection success with net_tcp_status() afterwards 101 | #define NET_POLL_SEND 0x2 102 | 103 | #define NET_POLL_INF ((uint32_t)-1) 104 | 105 | net_poll_t* net_poll_create(void); 106 | 107 | bool net_poll_add(net_poll_t* poll, net_sock_t* sock, const net_event_t* event); 108 | bool net_poll_mod(net_poll_t* poll, net_sock_t* sock, const net_event_t* event); 109 | bool net_poll_remove(net_poll_t* poll, net_sock_t* sock); 110 | 111 | size_t net_poll_wait(net_poll_t* poll, net_event_t* events, size_t size, uint32_t wait_ms); 112 | 113 | void net_poll_close(net_poll_t* poll); 114 | 115 | #endif 116 | -------------------------------------------------------------------------------- /src/rcu_lib.h: -------------------------------------------------------------------------------- 1 | /* 2 | rcu_lib.h - Read-copy-update (RCU) 3 | Copyright (C) 2024 LekKit 4 | 5 | This Source Code Form is subject to the terms of the Mozilla Public 6 | License, v. 2.0. If a copy of the MPL was not distributed with this 7 | file, You can obtain one at https://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | #ifndef RVVM_RCU_LIB_H 11 | #define RVVM_RCU_LIB_H 12 | 13 | #include "atomics.h" 14 | 15 | #if defined(GNU_EXTS) 16 | #define RCU_THREAD_LOCAL __thread 17 | #elif defined(_MSC_VER) 18 | #define RCU_THREAD_LOCAL __declspec(thread) 19 | #else 20 | // No thread locals available, fallback to shared reader state 21 | #define RCU_THREAD_LOCAL 22 | #define RCU_DUMMY_SHARED_STATE 23 | #endif 24 | 25 | #define RCU_INTERNAL_STATE_GEN 0x80000000U 26 | 27 | #define RCU_INTERNAL_STATE_READERS(state) ((uint16_t)(state)) 28 | 29 | extern RCU_THREAD_LOCAL uint32_t rcu_internal_reader_state; 30 | 31 | extern uint32_t rcu_internal_writer_waiting; 32 | 33 | #if !defined(RCU_DUMMY_SHARED_STATE) 34 | 35 | extern uint32_t rcu_internal_writer_gen; 36 | 37 | extern uint32_t rcu_has_remote_barriers; 38 | 39 | #endif 40 | 41 | slow_path void rcu_internal_wake_writer(uint32_t state); 42 | 43 | /* 44 | * RCU API 45 | */ 46 | 47 | // Register rcu reader thread, MUST be called before rcu_read_lock() 48 | void rcu_register_thread(void); 49 | 50 | // Deregister rcu reader thread, MUST be called before thread termination 51 | void rcu_deregister_thread(void); 52 | 53 | // Wait for next rcu grace period, all pointers previously replaced may be freed afterwards 54 | void rcu_synchronize(void); 55 | 56 | // Enter rcu reader section 57 | static forceinline void rcu_read_lock(void) 58 | { 59 | uint32_t* self = &rcu_internal_reader_state; 60 | atomic_compiler_barrier(); 61 | #if defined(RCU_DUMMY_SHARED_STATE) 62 | atomic_add_uint32(self, 1); 63 | #else 64 | uint32_t state = atomic_load_uint32_ex(self, ATOMIC_RELAXED); 65 | if (likely(!RCU_INTERNAL_STATE_READERS(state))) { 66 | state = atomic_load_uint32_ex(&rcu_internal_writer_gen, ATOMIC_RELAXED); 67 | atomic_store_uint32_ex(self, state, ATOMIC_RELAXED); 68 | atomic_compiler_barrier(); 69 | if (unlikely(!atomic_load_uint32_ex(&rcu_has_remote_barriers, ATOMIC_RELAXED))) { 70 | // Perform a StoreLoad barrier if remote barriers are not supported. 71 | // This prevents rcu_dereference() reordering before rcu reader section. 72 | atomic_fence_ex(ATOMIC_SEQ_CST); 73 | } 74 | } else { 75 | atomic_store_uint32_ex(self, state + 1, ATOMIC_RELAXED); 76 | } 77 | #endif 78 | } 79 | 80 | // Exit rcu reader section 81 | static forceinline void rcu_read_unlock(void) 82 | { 83 | uint32_t* self = &rcu_internal_reader_state; 84 | atomic_compiler_barrier(); 85 | #if defined(RCU_DUMMY_SHARED_STATE) 86 | uint32_t state = atomic_sub_uint32(self, 1); 87 | #else 88 | uint32_t state = atomic_load_uint32_ex(self, ATOMIC_RELAXED); 89 | 90 | #if defined(__SANITIZE_THREAD__) || defined(__x86_64__) 91 | atomic_store_uint32_ex(self, state - 1, ATOMIC_RELEASE); 92 | #else 93 | atomic_compiler_barrier(); 94 | if (unlikely(!atomic_load_uint32_ex(&rcu_has_remote_barriers, ATOMIC_RELAXED))) { 95 | // Perform a LoadStore barrier if remote barriers are not supported. 96 | // This prevents rcu_dereference() reordering after rcu reader section. 97 | atomic_fence_ex(ATOMIC_RELEASE); 98 | } 99 | atomic_store_uint32_ex(self, state - 1, ATOMIC_RELAXED); 100 | #endif 101 | 102 | #endif 103 | 104 | // Wake up RCU writer thread if needed 105 | if (unlikely(atomic_load_uint32_ex(&rcu_internal_writer_waiting, ATOMIC_RELAXED))) { 106 | rcu_internal_wake_writer(state); 107 | } 108 | } 109 | 110 | // Assign a pointer from rcu writer thread 111 | #define rcu_assign_pointer(rcu_ptr, val) atomic_store_pointer(&rcu_ptr, val) 112 | 113 | // Swap a pointer from rcu writer thread 114 | #define rcu_swap_pointer(rcu_ptr, val) atomic_swap_pointer(&rcu_ptr, val) 115 | 116 | // Dereference a pointer from rcu reader thread 117 | #define rcu_dereference(rcu_ptr) atomic_load_pointer(&rcu_ptr) 118 | 119 | #ifdef USE_INFRASTRUCTURE_TESTS 120 | 121 | // Run an rcu torture test to check correctness of implementation 122 | void rcu_torture_test(uint32_t reader_threads, uint32_t secs); 123 | 124 | #endif 125 | 126 | #endif 127 | -------------------------------------------------------------------------------- /src/ringbuf.c: -------------------------------------------------------------------------------- 1 | /* 2 | ringbuf.с - FIFO Ring buffer 3 | Copyright (C) 2021 cerg2010cerg2010 4 | LekKit 5 | 6 | This Source Code Form is subject to the terms of the Mozilla Public 7 | License, v. 2.0. If a copy of the MPL was not distributed with this 8 | file, You can obtain one at https://mozilla.org/MPL/2.0/. 9 | */ 10 | 11 | #include "ringbuf.h" 12 | #include "utils.h" 13 | #include "mem_ops.h" 14 | 15 | SOURCE_OPTIMIZATION_SIZE 16 | 17 | void ringbuf_create(ringbuf_t* rb, size_t size) 18 | { 19 | rb->data = safe_new_arr(uint8_t, size); 20 | rb->size = size; 21 | rb->start = 0; 22 | rb->consumed = 0; 23 | } 24 | 25 | void ringbuf_destroy(ringbuf_t* rb) 26 | { 27 | rb->size = 0; 28 | rb->start = 0; 29 | rb->consumed = 0; 30 | free(rb->data); 31 | } 32 | 33 | size_t ringbuf_space(ringbuf_t* rb) 34 | { 35 | return rb->size - rb->consumed; 36 | } 37 | 38 | size_t ringbuf_avail(ringbuf_t* rb) 39 | { 40 | return rb->consumed; 41 | } 42 | 43 | size_t ringbuf_skip(ringbuf_t* rb, size_t len) 44 | { 45 | size_t skip = EVAL_MIN(len, rb->consumed); 46 | rb->consumed -= skip; 47 | return skip; 48 | } 49 | 50 | static inline size_t ringbuf_get_read_start(ringbuf_t* rb) 51 | { 52 | return rb->consumed > rb->start 53 | ? (rb->size - rb->consumed + rb->start) 54 | : (rb->start - rb->consumed); 55 | } 56 | 57 | size_t ringbuf_peek(ringbuf_t* rb, void* data, size_t len) 58 | { 59 | size_t start = ringbuf_get_read_start(rb); 60 | size_t ret = EVAL_MIN(rb->consumed, len); 61 | size_t lhalf_len = EVAL_MIN(rb->size - start, ret); 62 | memcpy(data, ((uint8_t*)rb->data) + start, lhalf_len); 63 | if (ret > lhalf_len) { 64 | size_t rhalf_len = ret - lhalf_len; 65 | memcpy(((uint8_t*)data) + lhalf_len, rb->data, rhalf_len); 66 | } 67 | return ret; 68 | } 69 | 70 | size_t ringbuf_read(ringbuf_t* rb, void* data, size_t len) 71 | { 72 | size_t ret = ringbuf_peek(rb, data, len); 73 | ringbuf_skip(rb, ret); 74 | return ret; 75 | } 76 | 77 | size_t ringbuf_write(ringbuf_t* rb, const void* data, size_t len) 78 | { 79 | size_t ret = EVAL_MIN(rb->size - rb->consumed, len); 80 | size_t lhalf_len = EVAL_MIN(rb->size - rb->start, ret); 81 | memcpy(((uint8_t*)rb->data) + rb->start, data, lhalf_len); 82 | if (ret > lhalf_len) { 83 | size_t rhalf_len = ret - lhalf_len; 84 | memcpy(rb->data, ((const uint8_t*)data) + lhalf_len, rhalf_len); 85 | rb->start = rhalf_len; 86 | } else { 87 | rb->start += ret; 88 | } 89 | rb->consumed += ret; 90 | return ret; 91 | } 92 | 93 | bool ringbuf_get(ringbuf_t* rb, void* data, size_t len) 94 | { 95 | if (len <= ringbuf_avail(rb)) { 96 | ringbuf_read(rb, data, len); 97 | return true; 98 | } 99 | return false; 100 | } 101 | 102 | bool ringbuf_put(ringbuf_t* rb, const void* data, size_t len) 103 | { 104 | if (len <= ringbuf_space(rb)) { 105 | ringbuf_write(rb, data, len); 106 | return true; 107 | } 108 | DO_ONCE(rvvm_info("Overflow in ring %p! (size: %u, consumed: %u, len: %u)", 109 | (void*)rb, (uint32_t)rb->size, (uint32_t)rb->consumed, (uint32_t)len)); 110 | return false; 111 | } 112 | -------------------------------------------------------------------------------- /src/ringbuf.h: -------------------------------------------------------------------------------- 1 | /* 2 | ringbuf.h - FIFO Ring buffer 3 | Copyright (C) 2021 cerg2010cerg2010 4 | LekKit 5 | 6 | This Source Code Form is subject to the terms of the Mozilla Public 7 | License, v. 2.0. If a copy of the MPL was not distributed with this 8 | file, You can obtain one at https://mozilla.org/MPL/2.0/. 9 | */ 10 | 11 | #ifndef RVVM_RINGBUF_H 12 | #define RVVM_RINGBUF_H 13 | 14 | #include 15 | #include 16 | #include 17 | 18 | typedef struct ringbuf { 19 | void* data; 20 | size_t size; 21 | size_t start; 22 | size_t consumed; 23 | } ringbuf_t; 24 | 25 | void ringbuf_create(ringbuf_t* rb, size_t size); 26 | void ringbuf_destroy(ringbuf_t* rb); 27 | 28 | size_t ringbuf_space(ringbuf_t* rb); 29 | size_t ringbuf_avail(ringbuf_t* rb); 30 | 31 | // Serial operation (Returns actual amount of read/written bytes) 32 | size_t ringbuf_read(ringbuf_t* rb, void* data, size_t len); 33 | size_t ringbuf_peek(ringbuf_t* rb, void* data, size_t len); 34 | size_t ringbuf_skip(ringbuf_t* rb, size_t len); 35 | size_t ringbuf_write(ringbuf_t* rb, const void* data, size_t len); 36 | 37 | // Error out instead of partial operation 38 | bool ringbuf_get(ringbuf_t* rb, void* data, size_t len); 39 | bool ringbuf_put(ringbuf_t* rb, const void* data, size_t len); 40 | 41 | static inline bool ringbuf_put_u8(ringbuf_t* rb, uint8_t x) { return ringbuf_put(rb, &x, sizeof(x)); } 42 | static inline bool ringbuf_put_u16(ringbuf_t* rb, uint16_t x) { return ringbuf_put(rb, &x, sizeof(x)); } 43 | static inline bool ringbuf_put_u32(ringbuf_t* rb, uint32_t x) { return ringbuf_put(rb, &x, sizeof(x)); } 44 | static inline bool ringbuf_put_u64(ringbuf_t* rb, uint64_t x) { return ringbuf_put(rb, &x, sizeof(x)); } 45 | 46 | static inline bool ringbuf_get_u8(ringbuf_t* rb, uint8_t* x) { return ringbuf_get(rb, x, sizeof(*x)); } 47 | static inline bool ringbuf_get_u16(ringbuf_t* rb, uint16_t* x) { return ringbuf_get(rb, x, sizeof(*x)); } 48 | static inline bool ringbuf_get_u32(ringbuf_t* rb, uint32_t* x) { return ringbuf_get(rb, x, sizeof(*x)); } 49 | static inline bool ringbuf_get_u64(ringbuf_t* rb, uint64_t* x) { return ringbuf_get(rb, x, sizeof(*x)); } 50 | 51 | #endif 52 | -------------------------------------------------------------------------------- /src/riscv_cpu.c: -------------------------------------------------------------------------------- 1 | /* 2 | riscv_cpu.c - RISC-V CPU Interfaces 3 | Copyright (C) 2021 LekKit 4 | 5 | This Source Code Form is subject to the terms of the Mozilla Public 6 | License, v. 2.0. If a copy of the MPL was not distributed with this 7 | file, You can obtain one at https://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | #include "riscv_cpu.h" 11 | #include "riscv_hart.h" 12 | 13 | void riscv_run_till_event(rvvm_hart_t* vm) 14 | { 15 | if (vm->rv64) { 16 | #ifdef USE_RV64 17 | riscv64_run_interpreter(vm); 18 | #endif 19 | } else { 20 | #ifdef USE_RV32 21 | riscv32_run_interpreter(vm); 22 | #endif 23 | } 24 | } 25 | 26 | slow_path void riscv_illegal_insn(rvvm_hart_t* vm, const uint32_t insn) 27 | { 28 | riscv_trap(vm, RISCV_TRAP_ILL_INSN, insn); 29 | } 30 | 31 | void riscv_jit_flush_cache(rvvm_hart_t* vm) 32 | { 33 | #ifdef USE_JIT 34 | if (vm->jit_enabled) { 35 | riscv_jit_discard(vm); 36 | riscv_jit_tlb_flush(vm); 37 | rvjit_flush_cache(&vm->jit); 38 | } 39 | #else 40 | UNUSED(vm); 41 | #endif 42 | } 43 | 44 | #ifdef USE_JIT 45 | 46 | void riscv_jit_mark_dirty_mem(rvvm_machine_t* machine, rvvm_addr_t addr, size_t size) 47 | { 48 | vector_foreach(machine->harts, i) { 49 | rvjit_mark_dirty_mem(&vector_at(machine->harts, i)->jit, addr, size); 50 | } 51 | } 52 | 53 | /* 54 | * When returning from recompiled blocks, hart state 55 | * stays consistent, thus allowing to switch between 56 | * interpret-trace-compile and trace-execute states 57 | */ 58 | 59 | static inline void riscv_jit_tlb_put(rvvm_hart_t* vm, rvvm_addr_t vaddr, rvjit_func_t block) 60 | { 61 | rvvm_addr_t entry = (vaddr >> 1) & RVVM_TLB_MASK; 62 | vm->jtlb[entry].pc = vaddr; 63 | vm->jtlb[entry].block = block; 64 | } 65 | 66 | static bool riscv_jit_lookup(rvvm_hart_t* vm) 67 | { 68 | // Translate virtual PC into physical, JIT operates on phys_pc 69 | rvvm_addr_t virt_pc = vm->registers[RISCV_REG_PC]; 70 | rvvm_addr_t phys_pc = 0; 71 | // Lookup in the hashmap, cache virt_pc->block in JTLB 72 | if (riscv_virt_translate_e(vm, virt_pc, &phys_pc)) { 73 | rvjit_func_t block = rvjit_block_lookup(&vm->jit, phys_pc); 74 | if (block) { 75 | riscv_jit_tlb_put(vm, virt_pc, block); 76 | block(vm); 77 | return true; 78 | } 79 | 80 | // No valid block compiled for this location, 81 | // init a new one and enable JIT compiler 82 | rvjit_block_init(&vm->jit); 83 | vm->jit.pc_off = 0; 84 | vm->jit.virt_pc = virt_pc; 85 | vm->jit.phys_pc = phys_pc; 86 | 87 | // Von Neumann icache: Flush JTLB upon hiting a dirty block 88 | riscv_jit_tlb_flush(vm); 89 | 90 | vm->jit_compiling = true; 91 | vm->jit_block_ends = false; 92 | } 93 | return false; 94 | } 95 | 96 | static forceinline bool riscv_jtlb_lookup(rvvm_hart_t* vm) 97 | { 98 | // Try to find & execute a block 99 | rvvm_addr_t pc = vm->registers[RISCV_REG_PC]; 100 | rvvm_addr_t entry = (pc >> 1) & RVVM_TLB_MASK; 101 | rvvm_addr_t tpc = vm->jtlb[entry].pc; 102 | if (likely(pc == tpc)) { 103 | vm->jtlb[entry].block(vm); 104 | return true; 105 | } else { 106 | return false; 107 | } 108 | } 109 | 110 | slow_path bool riscv_jit_tlb_lookup(rvvm_hart_t* vm) 111 | { 112 | if (likely(vm->jit_enabled)) { 113 | if (likely(riscv_jtlb_lookup(vm))) { 114 | #ifndef RVJIT_NATIVE_LINKER 115 | // Try to execute more blocks if they aren't linked 116 | for (size_t i=0; i<10 && riscv_jtlb_lookup(vm); ++i); 117 | #endif 118 | return true; 119 | } else { 120 | return riscv_jit_lookup(vm); 121 | } 122 | } 123 | return false; 124 | } 125 | 126 | slow_path void riscv_jit_finalize(rvvm_hart_t* vm) 127 | { 128 | if (rvjit_block_nonempty(&vm->jit)) { 129 | rvjit_func_t block = rvjit_block_finalize(&vm->jit); 130 | 131 | if (block) { 132 | riscv_jit_tlb_put(vm, vm->jit.virt_pc, block); 133 | } else { 134 | // Our cache is full, flush it 135 | riscv_jit_tlb_flush(vm); 136 | rvjit_flush_cache(&vm->jit); 137 | } 138 | } 139 | 140 | vm->jit_compiling = false; 141 | } 142 | 143 | #endif 144 | -------------------------------------------------------------------------------- /src/riscv_cpu.h: -------------------------------------------------------------------------------- 1 | /* 2 | riscv_cpu.h - RISC-V CPU Interfaces 3 | Copyright (C) 2021 LekKit 4 | 5 | This Source Code Form is subject to the terms of the Mozilla Public 6 | License, v. 2.0. If a copy of the MPL was not distributed with this 7 | file, You can obtain one at https://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | #ifndef RISCV_CPU_H 11 | #define RISCV_CPU_H 12 | 13 | #include "rvvm.h" 14 | #include "riscv_mmu.h" 15 | 16 | // Run the vCPU in current thread 17 | void riscv_run_till_event(rvvm_hart_t* vm); 18 | 19 | // Trap the vCPU on illegal instruction 20 | slow_path void riscv_illegal_insn(rvvm_hart_t* vm, const uint32_t insn); 21 | 22 | // Internal interpreter ISA switching 23 | void riscv32_run_interpreter(rvvm_hart_t* vm); 24 | void riscv64_run_interpreter(rvvm_hart_t* vm); 25 | 26 | /* 27 | * JIT infrastructure 28 | */ 29 | 30 | // Flush the JIT cache (Kinda like instruction cache) 31 | void riscv_jit_flush_cache(rvvm_hart_t* vm); 32 | 33 | // Discard the currently JITed block 34 | static forceinline void riscv_jit_discard(rvvm_hart_t* vm) 35 | { 36 | #ifdef USE_JIT 37 | vm->jit_compiling = false; 38 | #else 39 | UNUSED(vm); 40 | #endif 41 | } 42 | 43 | // Finish the currently JITed block 44 | static forceinline void riscv_jit_compile(rvvm_hart_t* vm) 45 | { 46 | #ifdef USE_JIT 47 | vm->jit_block_ends = true; 48 | #else 49 | UNUSED(vm); 50 | #endif 51 | } 52 | 53 | // Mark the physical memory as dirty (Overwritten) 54 | #ifdef USE_JIT 55 | void riscv_jit_mark_dirty_mem(rvvm_machine_t* machine, rvvm_addr_t addr, size_t size); 56 | #else 57 | static forceinline void riscv_jit_mark_dirty_mem(rvvm_machine_t* machine, rvvm_addr_t addr, size_t size) { 58 | UNUSED(machine); 59 | UNUSED(addr); 60 | UNUSED(size); 61 | } 62 | #endif 63 | 64 | /* 65 | * Interpreter JIT glue 66 | */ 67 | 68 | slow_path bool riscv_jit_tlb_lookup(rvvm_hart_t* vm); 69 | 70 | slow_path void riscv_jit_finalize(rvvm_hart_t* vm); 71 | 72 | #endif 73 | -------------------------------------------------------------------------------- /src/riscv_hart.h: -------------------------------------------------------------------------------- 1 | /* 2 | riscv_hart.h - RISC-V Hardware Thread 3 | Copyright (C) 2021 LekKit 4 | 5 | This Source Code Form is subject to the terms of the Mozilla Public 6 | License, v. 2.0. If a copy of the MPL was not distributed with this 7 | file, You can obtain one at https://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | #ifndef RISCV_HART_H 11 | #define RISCV_HART_H 12 | 13 | #include "rvvm.h" 14 | 15 | /* 16 | * Hart context creation, disposal 17 | */ 18 | 19 | // Create hart context 20 | rvvm_hart_t* riscv_hart_init(rvvm_machine_t* machine); 21 | 22 | // Prepare harts before spawning any of them 23 | void riscv_hart_prepare(rvvm_hart_t* vm); 24 | 25 | // Initialize RISC-V AIA support 26 | void riscv_hart_aia_init(rvvm_hart_t* vm); 27 | 28 | // Free hart context 29 | void riscv_hart_free(rvvm_hart_t* vm); 30 | 31 | /* 32 | * Hart operations, may be called on any thread 33 | */ 34 | 35 | // Makes vCPU return from riscv_run_till_event() to check for IRQs, 36 | // or after flushing pages overlapping PC (optimization quirk) 37 | void riscv_restart_dispatch(rvvm_hart_t* vm); 38 | 39 | // Signals interrupt to the hart 40 | void riscv_interrupt(rvvm_hart_t* vm, bitcnt_t irq); 41 | 42 | // Clears interrupt in IP csr of the hart 43 | void riscv_interrupt_clear(rvvm_hart_t* vm, bitcnt_t irq); 44 | 45 | // Sends an AIA (MSI) IRQ to the hart at M/S mode 46 | void riscv_send_aia_irq(rvvm_hart_t* vm, bool smode, uint32_t irq); 47 | 48 | // Hart interrupts that are raised externally 49 | static inline uint32_t riscv_interrupts_raised(rvvm_hart_t* vm) 50 | { 51 | return atomic_load_uint32_relax(&vm->pending_irqs); 52 | } 53 | 54 | // Signal the vCPU to check for timer interrupts 55 | void riscv_hart_check_timer(rvvm_hart_t* vm); 56 | 57 | // Preempt the hart vCPU thread from consuming CPU for preempt_ms 58 | void riscv_hart_preempt(rvvm_hart_t* vm, uint32_t preempt_ms); 59 | 60 | /* 61 | * Hart operations, may be called ONLY on hart thread 62 | */ 63 | 64 | // Hart interrupts that are pending & enabled by ie CSR 65 | static inline uint32_t riscv_interrupts_pending(rvvm_hart_t* vm) 66 | { 67 | return (riscv_interrupts_raised(vm) | vm->csr.ip) & vm->csr.ie; 68 | } 69 | 70 | // Update AIA state after write to AIA CSRs 71 | void riscv_update_aia_state(rvvm_hart_t* vm, bool smode); 72 | 73 | // Get/Claim highest-priority AIA (MSI) IRQ at M/S mode 74 | uint32_t riscv_get_aia_irq(rvvm_hart_t* vm, bool smode, bool claim); 75 | 76 | // Check interrupts after writing to ie/ip/status CSRs, or after sret/mret 77 | void riscv_hart_check_interrupts(rvvm_hart_t* vm); 78 | 79 | // Correctly applies side-effects of switching privileges 80 | void riscv_switch_priv(rvvm_hart_t* vm, uint8_t priv_mode); 81 | 82 | // Correctly applies side-effects of switching XLEN 83 | void riscv_update_xlen(rvvm_hart_t* vm); 84 | 85 | // Traps the hart. Should be the last operation before returning to dispatch. 86 | void riscv_trap(rvvm_hart_t* vm, bitcnt_t cause, rvvm_uxlen_t tval); 87 | 88 | // Execute a breakpoint. Reports each breakpoint to GDB stub once 89 | void riscv_breakpoint(rvvm_hart_t* vm); 90 | 91 | /* 92 | * Running the hart 93 | */ 94 | 95 | // Executes the machine hart in a current thread 96 | // Returns upon receiving EXT_EVENT_PAUSE 97 | void riscv_hart_run(rvvm_hart_t* vm); 98 | 99 | // Execute a userland thread context in current thread 100 | // Returns trap cause upon any CPU trap 101 | rvvm_addr_t riscv_hart_run_userland(rvvm_hart_t* vm); 102 | 103 | // Spawns hart vCPU thread, returns immediately 104 | void riscv_hart_spawn(rvvm_hart_t *vm); 105 | 106 | // Requests the hart to be paused as soon as possible 107 | void riscv_hart_queue_pause(rvvm_hart_t* vm); 108 | 109 | // Pauses hart in a consistent state, terminates vCPU thread 110 | void riscv_hart_pause(rvvm_hart_t* vm); 111 | 112 | #endif 113 | -------------------------------------------------------------------------------- /src/riscv_priv.h: -------------------------------------------------------------------------------- 1 | /* 2 | riscv_priv.h - RISC-V Privileged 3 | Copyright (C) 2021 LekKit 4 | 5 | This Source Code Form is subject to the terms of the Mozilla Public 6 | License, v. 2.0. If a copy of the MPL was not distributed with this 7 | file, You can obtain one at https://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | #ifndef RISCV_PRIV_H 11 | #define RISCV_PRIV_H 12 | 13 | #include "rvvm.h" 14 | 15 | slow_path void riscv_emulate_opc_system(rvvm_hart_t* vm, const uint32_t insn); 16 | slow_path void riscv_emulate_opc_misc_mem(rvvm_hart_t* vm, const uint32_t insn); 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /src/rvtimer.h: -------------------------------------------------------------------------------- 1 | /* 2 | rvtimer.h - Timers, sleep functions 3 | Copyright (C) 2021 LekKit 4 | 5 | This Source Code Form is subject to the terms of the Mozilla Public 6 | License, v. 2.0. If a copy of the MPL was not distributed with this 7 | file, You can obtain one at https://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | #ifndef RVVM_RVTIMER_H 11 | #define RVVM_RVTIMER_H 12 | 13 | #include "compiler.h" 14 | #include "rvvm_types.h" 15 | #include 16 | 17 | typedef struct { 18 | // Those fields are internal use only 19 | uint64_t begin; 20 | uint64_t freq; 21 | } rvtimer_t; 22 | 23 | typedef struct { 24 | // Those fields are internal use only 25 | uint64_t timecmp; 26 | rvtimer_t* timer; 27 | } rvtimecmp_t; 28 | 29 | // Convert between frequencies without overflow 30 | static inline uint64_t rvtimer_convert_freq(uint64_t clk, uint64_t src_freq, uint64_t dst_freq) 31 | { 32 | #ifdef INT128_SUPPORT 33 | // Fast path when no overflow (Just mul + div on x86_64 with the overflow check) 34 | if (likely(!((uint128_t)clk * (uint128_t)dst_freq >> 64))) { 35 | return clk * dst_freq / src_freq; 36 | } 37 | #endif 38 | uint64_t freq_rem = clk % src_freq; 39 | return (clk / src_freq * dst_freq) + (freq_rem * dst_freq / src_freq); 40 | } 41 | 42 | // Get monotonic clocksource with the specified frequency 43 | uint64_t rvtimer_clocksource(uint64_t freq); 44 | 45 | // Get UNIX timestamp (Seconds since 1 Jan 1970) 46 | uint64_t rvtimer_unixtime(void); 47 | 48 | /* 49 | * Timer 50 | */ 51 | 52 | // Initialize the timer and the clocksource 53 | void rvtimer_init(rvtimer_t* timer, uint64_t freq); 54 | 55 | // Get timer frequency 56 | uint64_t rvtimer_freq(const rvtimer_t* timer); 57 | 58 | // Get current timer value 59 | uint64_t rvtimer_get(const rvtimer_t* timer); 60 | 61 | // Rebase the clocksource by time field 62 | void rvtimer_rebase(rvtimer_t* timer, uint64_t time); 63 | 64 | /* 65 | * Timer comparators 66 | */ 67 | 68 | // Init timer comparator 69 | void rvtimecmp_init(rvtimecmp_t* cmp, rvtimer_t* timer); 70 | 71 | // Set comparator timestamp 72 | void rvtimecmp_set(rvtimecmp_t* cmp, uint64_t timecmp); 73 | 74 | // Get comparator timestamp 75 | uint64_t rvtimecmp_get(const rvtimecmp_t* cmp); 76 | 77 | // Check if we have a pending timer interrupt. Updates on it's own 78 | bool rvtimecmp_pending(const rvtimecmp_t* cmp); 79 | 80 | // Get delay until the timer interrupt (In timer frequency) 81 | uint64_t rvtimecmp_delay(const rvtimecmp_t* cmp); 82 | 83 | // Get delay until the timer interrupt (In nanoseconds) 84 | uint64_t rvtimecmp_delay_ns(const rvtimecmp_t* cmp); 85 | 86 | /* 87 | * Sleep 88 | */ 89 | 90 | // Set expected sleep latency (Internal use) 91 | void sleep_low_latency(bool enable); 92 | 93 | // Sleep for N ms 94 | void sleep_ms(uint32_t ms); 95 | 96 | #endif 97 | -------------------------------------------------------------------------------- /src/rvvm_isolation.h: -------------------------------------------------------------------------------- 1 | /* 2 | rvvm_isolation.h - Process & thread isolation 3 | Copyright (C) 2024 LekKit 4 | 5 | This Source Code Form is subject to the terms of the Mozilla Public 6 | License, v. 2.0. If a copy of the MPL was not distributed with this 7 | file, You can obtain one at https://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | #ifndef RVVM_ISOLATION_H 11 | #define RVVM_ISOLATION_H 12 | 13 | #include "rvvmlib.h" 14 | 15 | /* 16 | * Restrict the calling thread from: 17 | * - Accessing the filesystem 18 | * - Accessing PID namespace, killing processes 19 | * - Accessing IPC namespace 20 | * - Forking, executing new programs 21 | * 22 | * Additionally, all capabilities of the calling are dropped, 23 | * and suid privilege escalation is no longer possible. 24 | * 25 | * This is expected to be applied to all RVVM-owned threads 26 | * (vCPU, threadpool, event dispatch thread) without affecting 27 | * the process as a whole. 28 | */ 29 | void rvvm_restrict_this_thread(void); 30 | 31 | /* 32 | * Apply same restrictions as cap_restrict_this_thread() to the whole process. 33 | * 34 | * Additionally, drop to nobody if we're root. 35 | * 36 | * NOTE: We can't implicitly enforce this in librvvm as we never know 37 | * when it's safe to do so. It's up to the API user to decide. 38 | */ 39 | PUBLIC void rvvm_restrict_process(void); 40 | 41 | /* 42 | * Possible TODO for further librvvm isolation: Implement process-wide filesystem restrictions 43 | * - Read-only access to /etc, /usr, ... etc system dirs 44 | * - Read-only access to any hidden .file in $HOME (Prevent messing with .bashrc, .profile etc) 45 | * - Read-only access to ~/.local/bin, ~/.local/lib 46 | * - No access to ~/.gnupg, ~/.ssh, ~/.pki, other critical user data like crypto wallets, browser profiles and such 47 | * 48 | * This in theory could be applied to any process which uses librvvm, with an opt-out mechanism 49 | * 50 | * Easily doable through OpenBSD pledge, however Linux Landlock is per-thread only, 51 | * which significantly complicates the implementation. 52 | */ 53 | 54 | #endif 55 | -------------------------------------------------------------------------------- /src/rvvm_types.h: -------------------------------------------------------------------------------- 1 | /* 2 | rvvm_types.c - RVVM integer types 3 | Copyright (C) 2021 LekKit 4 | 5 | This Source Code Form is subject to the terms of the Mozilla Public 6 | License, v. 2.0. If a copy of the MPL was not distributed with this 7 | file, You can obtain one at https://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | #ifndef RVVM_TYPES_H 11 | #define RVVM_TYPES_H 12 | 13 | #include "compiler.h" 14 | 15 | #include 16 | #include 17 | #include 18 | 19 | #if CHECK_INCLUDE(inttypes.h, 1) 20 | #include 21 | #endif 22 | 23 | // Fix for MSVCRT printf specifier on MinGW 24 | #if defined(_WIN32) 25 | #undef PRIx32 26 | #define PRIx32 "I32x" 27 | 28 | #undef PRIx64 29 | #define PRIx64 "I64x" 30 | 31 | #undef PRId32 32 | #define PRId32 "I32d" 33 | 34 | #undef PRId64 35 | #define PRId64 "I64d" 36 | 37 | #undef PRIu32 38 | #define PRIu32 "I32u" 39 | 40 | #undef PRIu64 41 | #define PRIu64 "I64u" 42 | #endif 43 | 44 | #ifndef PRIx32 45 | #define PRIx32 "x" 46 | #endif 47 | 48 | #ifndef PRIx64 49 | #define PRIx64 "llx" 50 | #endif 51 | 52 | #ifndef PRId32 53 | #define PRId32 "d" 54 | #endif 55 | 56 | #ifndef PRId64 57 | #define PRId64 "lld" 58 | #endif 59 | 60 | #ifndef PRIu32 61 | #define PRIu32 "u" 62 | #endif 63 | 64 | #ifndef PRIu64 65 | #define PRIu64 "llu" 66 | #endif 67 | 68 | #ifdef __SIZEOF_INT128__ 69 | #define INT128_SUPPORT 1 70 | typedef unsigned __int128 uint128_t; 71 | typedef __int128 int128_t; 72 | #endif 73 | 74 | typedef uint8_t regid_t; // Register index 75 | typedef uint8_t bitcnt_t; // Bits count 76 | 77 | #endif 78 | -------------------------------------------------------------------------------- /src/rvvm_user.h: -------------------------------------------------------------------------------- 1 | /* 2 | rvvm_user.h - RVVM Linux binary emulator 3 | Copyright (C) 2024 LekKit 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #ifndef RVVM_USER_H 20 | #define RVVM_USER_H 21 | 22 | // Just call this like main(), envp may be NULL 23 | int rvvm_user_linux(int argc, char** argv, char** envp); 24 | 25 | #endif 26 | -------------------------------------------------------------------------------- /src/spinlock.h: -------------------------------------------------------------------------------- 1 | /* 2 | spinlock.h - Fast hybrid reader/writer lock 3 | Copyright (C) 2021 LekKit 4 | 5 | This Source Code Form is subject to the terms of the Mozilla Public 6 | License, v. 2.0. If a copy of the MPL was not distributed with this 7 | file, You can obtain one at https://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | #ifndef LEKKIT_SPINLOCK_H 11 | #define LEKKIT_SPINLOCK_H 12 | 13 | #include "atomics.h" 14 | #include "compiler.h" 15 | #include 16 | 17 | /* 18 | * Lock flags meaning: 19 | * 0x00000000: Quiescent state (No one holds the lock) 20 | * 0x00000001: Writer holds the lock 21 | * 0x7FFFFFFE: Reader count 22 | * 0x80000000: There are waiters 23 | */ 24 | 25 | // Static initializer 26 | #define SPINLOCK_INIT ZERO_INIT 27 | 28 | typedef struct { 29 | uint32_t flag; 30 | #if defined(USE_SPINLOCK_DEBUG) 31 | const char* location; 32 | #endif 33 | } spinlock_t; 34 | 35 | // Slow path for kernel wait / wake on contended lock, and lock misuse detection 36 | slow_path void spin_lock_wait(spinlock_t* lock, const char* location, bool slow); 37 | slow_path void spin_lock_wake(spinlock_t* lock, uint32_t prev); 38 | 39 | slow_path void spin_read_lock_wait(spinlock_t* lock, const char* location, bool slow); 40 | slow_path void spin_read_lock_wake(spinlock_t* lock, uint32_t prev); 41 | 42 | // Initialize a lock 43 | static inline void spin_init(spinlock_t* lock) 44 | { 45 | lock->flag = 0x0; 46 | #if defined(USE_SPINLOCK_DEBUG) 47 | lock->location = NULL; 48 | #endif 49 | } 50 | 51 | /* 52 | * Internal lock debugging macros 53 | */ 54 | #if defined(USE_SPINLOCK_DEBUG) 55 | #define SPINLOCK_DEBUG_LOCATION SOURCE_LINE 56 | #define SPINLOCK_MARK_LOCATION(lock, location) \ 57 | do { \ 58 | lock->location = location; \ 59 | } while (0) 60 | #else 61 | #define SPINLOCK_DEBUG_LOCATION NULL 62 | #define SPINLOCK_MARK_LOCATION(lock, location) \ 63 | do { \ 64 | UNUSED(lock); \ 65 | UNUSED(location); \ 66 | } while (0) 67 | #endif 68 | 69 | /* 70 | * Writer locking 71 | */ 72 | 73 | static forceinline bool spin_try_lock_internal(spinlock_t* lock, const char* location) 74 | { 75 | if (likely(atomic_cas_uint32_ex(&lock->flag, 0x0, 0x1, false, ATOMIC_ACQUIRE, ATOMIC_RELAXED))) { 76 | SPINLOCK_MARK_LOCATION(lock, location); 77 | return true; 78 | } 79 | return false; 80 | } 81 | 82 | static forceinline void spin_lock_internal(spinlock_t* lock, const char* location, bool slow) 83 | { 84 | // Use weaker CAS in fast path 85 | if (likely(atomic_cas_uint32_ex(&lock->flag, 0x0, 0x1, true, ATOMIC_ACQUIRE, ATOMIC_RELAXED))) { 86 | SPINLOCK_MARK_LOCATION(lock, location); 87 | } else { 88 | spin_lock_wait(lock, location, slow); 89 | } 90 | } 91 | 92 | // Try to claim the writer lock 93 | #define spin_try_lock(lock) spin_try_lock_internal(lock, SPINLOCK_DEBUG_LOCATION) 94 | 95 | // Perform writer locking on small, bounded critical section 96 | // Reports a deadlock upon waiting for too long 97 | #define spin_lock(lock) spin_lock_internal(lock, SPINLOCK_DEBUG_LOCATION, false) 98 | 99 | // Perform writer locking around heavy operation, wait indefinitely 100 | #define spin_lock_slow(lock) spin_lock_internal(lock, SPINLOCK_DEBUG_LOCATION, true) 101 | 102 | // Release the writer lock 103 | static forceinline void spin_unlock(spinlock_t* lock) 104 | { 105 | uint32_t prev = atomic_swap_uint32_ex(&lock->flag, 0x0, ATOMIC_RELEASE); 106 | if (unlikely(prev != 0x1)) { 107 | // Waiters are present, or invalid usage detected (Not locked / Locked as a reader) 108 | spin_lock_wake(lock, prev); 109 | } 110 | } 111 | 112 | /* 113 | * Reader locking 114 | */ 115 | 116 | // Try to claim the reader lock 117 | static forceinline bool spin_try_read_lock(spinlock_t* lock) 118 | { 119 | uint32_t prev; 120 | do { 121 | prev = atomic_load_uint32_relax(&lock->flag); 122 | if (unlikely(prev & 0x80000001U)) { 123 | // Writer owns the lock, writer waiters are present or too much readers (sic!) 124 | return false; 125 | } 126 | } while (!atomic_cas_uint32_ex(&lock->flag, prev, prev + 0x2, true, ATOMIC_ACQUIRE, ATOMIC_RELAXED)); 127 | return true; 128 | } 129 | 130 | static forceinline void spin_read_lock_internal(spinlock_t* lock, const char* location, bool slow) 131 | { 132 | if (unlikely(!spin_try_read_lock(lock))) { 133 | spin_read_lock_wait(lock, location, slow); 134 | } 135 | } 136 | 137 | // Perform reader locking on small, bounded critical section 138 | // Reports a deadlock upon waiting for too long 139 | #define spin_read_lock(lock) spin_read_lock_internal(lock, SPINLOCK_DEBUG_LOCATION, false) 140 | 141 | // Perform reader locking around heavy operation, wait indefinitely 142 | #define spin_read_lock_slow(lock) spin_read_lock_internal(lock, SPINLOCK_DEBUG_LOCATION, true) 143 | 144 | // Release the reader lock 145 | static forceinline void spin_read_unlock(spinlock_t* lock) 146 | { 147 | uint32_t prev = atomic_sub_uint32_ex(&lock->flag, 0x2, ATOMIC_RELEASE); 148 | if (unlikely(((int32_t)prev) < 0x2)) { 149 | // Waiters are present, or invalid usage detected (Not locked / Locked as a writer) 150 | spin_read_lock_wake(lock, prev); 151 | } 152 | } 153 | 154 | /* 155 | * Scoped locking helpers, may be exited via break 156 | * 157 | * scoped_spin_lock(&lock) { 158 | * do_something_under_lock(); 159 | * 160 | * if (need_exit_under_lock()) { 161 | * break; 162 | * } 163 | * 164 | * do_something_under_lock(); 165 | * } 166 | * 167 | * scoped_spin_try_lock(&lock) { 168 | * do_something_under_lock(); 169 | * } else { 170 | * do_something_when_locking_failed(); 171 | * } 172 | */ 173 | 174 | #define scoped_spin_lock(lock) SCOPED_HELPER (spin_lock(lock), spin_unlock(lock)) 175 | #define scoped_spin_try_lock(lock) POST_COND (spin_try_lock(lock), spin_unlock(lock)) 176 | 177 | #define scoped_spin_read_lock(lock) SCOPED_HELPER (spin_read_lock(lock), spin_read_unlock(lock)) 178 | #define scoped_spin_try_read_lock(lock) POST_COND (spin_try_read_lock(lock), spin_read_unlock(lock)) 179 | 180 | #define scoped_spin_lock_slow(lock) SCOPED_HELPER (spin_lock_slow(lock), spin_unlock(lock)) 181 | #define scoped_spin_read_lock_slow(lock) SCOPED_HELPER (spin_read_lock_slow(lock), spin_read_unlock(lock)) 182 | 183 | #endif 184 | -------------------------------------------------------------------------------- /src/stacktrace.c: -------------------------------------------------------------------------------- 1 | /* 2 | stacktrace.c - Stacktrace (Using dynamically loaded libbacktrace) 3 | Copyright (C) 2024 LekKit 4 | 5 | This Source Code Form is subject to the terms of the Mozilla Public 6 | License, v. 2.0. If a copy of the MPL was not distributed with this 7 | file, You can obtain one at https://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | #include "stacktrace.h" 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | #if (defined(__unix__) || defined(__APPLE__)) && !defined(NO_STACKTRACE) && !defined(__SANITIZE_ADDRESS__) 17 | #include 18 | #include 19 | #ifndef SIGBUS 20 | #define SIGBUS 10 21 | #endif 22 | #ifdef SA_SIGINFO 23 | #define STACKTRACE_SIGACTION_IMPL 24 | #endif 25 | #endif 26 | 27 | // RVVM internal headers come after system headers because of safe_free() 28 | #include "compiler.h" 29 | #include "utils.h" 30 | #include "dlib.h" 31 | 32 | SOURCE_OPTIMIZATION_SIZE 33 | 34 | /* 35 | * libbacktrace boilerplace 36 | */ 37 | 38 | struct backtrace_state; 39 | 40 | typedef void (*backtrace_error_callback)(void* data, const char* msg, int errnum); 41 | typedef int (*backtrace_full_callback)(void* data, uintptr_t pc, const char* filename, int lineno, const char* function); 42 | 43 | static struct backtrace_state* (*backtrace_create_state)(const char* filename, int threaded, 44 | backtrace_error_callback error_callback, void *data) = NULL; 45 | static int (*backtrace_full)(struct backtrace_state *state, int skip, backtrace_full_callback callback, 46 | backtrace_error_callback error_callback, void *data) = NULL; 47 | static void (*backtrace_print)(struct backtrace_state *state, int skip, FILE* file) = NULL; 48 | 49 | static struct backtrace_state* bt_state = NULL; 50 | 51 | static void backtrace_dummy_error(void* data, const char* msg, int errnum) 52 | { 53 | UNUSED(data); UNUSED(msg); UNUSED(errnum); 54 | } 55 | 56 | static int backtrace_dummy_callback(void* data, uintptr_t pc, const char* filename, int lineno, const char* function) 57 | { 58 | UNUSED(data); UNUSED(pc); UNUSED(filename); UNUSED(lineno); UNUSED(function); 59 | return 0; 60 | } 61 | 62 | /* 63 | * Fatal signal stacktraces 64 | */ 65 | 66 | #ifdef STACKTRACE_SIGACTION_IMPL 67 | 68 | static void signal_handler(int sig) 69 | { 70 | switch (sig) { 71 | case SIGSEGV: 72 | rvvm_warn("Fatal signal: Segmentation fault!"); 73 | break; 74 | case SIGBUS: 75 | rvvm_warn("Fatal signal: Bus fault - Misaligned access or mapped IO error!"); 76 | break; 77 | case SIGILL: 78 | rvvm_warn("Fatal signal: Illegal instruction!"); 79 | break; 80 | case SIGFPE: 81 | rvvm_warn("Fatal signal: Division by zero!"); 82 | break; 83 | default: 84 | rvvm_warn("Fatal signal %d", sig); 85 | break; 86 | } 87 | stacktrace_print(); 88 | full_deinit(); 89 | _Exit(-sig); 90 | } 91 | 92 | static void set_signal_handler(int sig) 93 | { 94 | struct sigaction sa_old = {0}; 95 | struct sigaction sa = { 96 | .sa_handler = signal_handler, 97 | }; 98 | sigaction(sig, NULL, &sa_old); 99 | if (!(sa_old.sa_flags & SA_SIGINFO)) { 100 | void* prev = sa_old.sa_handler; 101 | if (prev == NULL || prev == (void*)SIG_IGN || prev == (void*)SIG_DFL) { 102 | // Signal not used 103 | sigaction(sig, &sa, NULL); 104 | } 105 | } 106 | } 107 | 108 | #endif 109 | 110 | static void backtrace_init_once(void) 111 | { 112 | if (rvvm_has_arg("no_stacktrace")) { 113 | return; 114 | } 115 | 116 | dlib_ctx_t* libbt = dlib_open("backtrace", DLIB_NAME_PROBE); 117 | 118 | backtrace_create_state = dlib_resolve(libbt, "backtrace_create_state"); 119 | backtrace_full = dlib_resolve(libbt, "backtrace_full"); 120 | backtrace_print = dlib_resolve(libbt, "backtrace_print"); 121 | 122 | dlib_close(libbt); 123 | 124 | if (backtrace_create_state) { 125 | bt_state = backtrace_create_state(NULL, true, backtrace_dummy_error, NULL); 126 | } 127 | if (backtrace_full && bt_state) { 128 | // Preload backtracing data, isolation is enabled later on 129 | backtrace_full(bt_state, 0, backtrace_dummy_callback, backtrace_dummy_error, NULL); 130 | } 131 | 132 | #ifdef STACKTRACE_SIGACTION_IMPL 133 | set_signal_handler(SIGSEGV); 134 | set_signal_handler(SIGBUS); 135 | set_signal_handler(SIGILL); 136 | set_signal_handler(SIGFPE); 137 | #endif 138 | } 139 | 140 | void stacktrace_init(void) 141 | { 142 | DO_ONCE(backtrace_init_once()); 143 | } 144 | 145 | void stacktrace_print(void) 146 | { 147 | stacktrace_init(); 148 | if (backtrace_print && bt_state) { 149 | rvvm_warn("Stacktrace:"); 150 | backtrace_print(bt_state, 0, stderr); 151 | } else { 152 | DO_ONCE(rvvm_warn("Please install libbacktrace to obtain debug stacktraces")); 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /src/stacktrace.h: -------------------------------------------------------------------------------- 1 | /* 2 | stacktrace.h - Stacktrace 3 | Copyright (C) 2024 LekKit 4 | 5 | This Source Code Form is subject to the terms of the Mozilla Public 6 | License, v. 2.0. If a copy of the MPL was not distributed with this 7 | file, You can obtain one at https://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | #ifndef RVVM_STACKTRACE_H 11 | #define RVVM_STACKTRACE_H 12 | 13 | void stacktrace_init(void); 14 | 15 | void stacktrace_print(void); 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /src/threading.h: -------------------------------------------------------------------------------- 1 | /* 2 | threading.h - Threading, Futexes, Conditional variables, Threadpool 3 | Copyright (C) 2021 LekKit 4 | 5 | This Source Code Form is subject to the terms of the Mozilla Public 6 | License, v. 2.0. If a copy of the MPL was not distributed with this 7 | file, You can obtain one at https://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | #ifndef LEKKIT_THREADING_H 11 | #define LEKKIT_THREADING_H 12 | 13 | #include 14 | #include 15 | 16 | /* 17 | * Threads 18 | */ 19 | 20 | typedef struct thread_ctx thread_ctx_t; 21 | 22 | typedef void* (*thread_func_t)(void*); 23 | 24 | thread_ctx_t* thread_create_ex(thread_func_t func, void* arg, uint32_t stack_size); 25 | thread_ctx_t* thread_create(thread_func_t func, void* arg); 26 | bool thread_join(thread_ctx_t* thread); 27 | bool thread_detach(thread_ctx_t* thread); // NOTE: Detaching is not safe in libraries 28 | 29 | /* 30 | * Futexes (Possibly emulated) 31 | * 32 | * Work only within current process bounds. 33 | * Please prefer a conditional variable if you value precise timeout timing. 34 | */ 35 | 36 | #define THREAD_FUTEX_INFINITE ((uint64_t)-1) 37 | 38 | bool thread_futex_wait(void* ptr, uint32_t val, uint64_t timeout_ns); 39 | void thread_futex_wake(void* ptr, uint32_t num); 40 | 41 | /* 42 | * Conditional variables (More like events) 43 | */ 44 | 45 | // No condvar_wait() timeout 46 | #define CONDVAR_INFINITE ((uint64_t)-1) 47 | 48 | typedef struct cond_var cond_var_t; 49 | 50 | cond_var_t* condvar_create(void); 51 | bool condvar_wait_ns(cond_var_t* cond, uint64_t timeout_ns); 52 | bool condvar_wait(cond_var_t* cond, uint64_t timeout_ms); 53 | bool condvar_wake(cond_var_t* cond); 54 | bool condvar_wake_all(cond_var_t* cond); 55 | uint32_t condvar_waiters(cond_var_t* cond); 56 | void condvar_free(cond_var_t* cond); 57 | 58 | /* 59 | * Shared threadpool 60 | */ 61 | 62 | #define THREAD_MAX_VA_ARGS 8 63 | 64 | typedef void* (*thread_func_va_t)(void**); 65 | 66 | // Execute task in threadpool 67 | void thread_create_task(thread_func_t func, void* arg); 68 | void thread_create_task_va(thread_func_va_t func, void** args, unsigned arg_count); 69 | 70 | #endif 71 | -------------------------------------------------------------------------------- /src/vector.c: -------------------------------------------------------------------------------- 1 | /* 2 | vector.c - Vector Container 3 | Copyright (C) 2021 LekKit 4 | 5 | This Source Code Form is subject to the terms of the Mozilla Public 6 | License, v. 2.0. If a copy of the MPL was not distributed with this 7 | file, You can obtain one at https://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | #include "vector.h" 11 | #include "utils.h" 12 | 13 | SOURCE_OPTIMIZATION_SIZE 14 | 15 | typedef vector_t(void) safe_aliasing vector_punned_t; 16 | 17 | // Grow factor: 1.5 (Better memory reusage), initial capacity: 2 18 | slow_path void vector_grow_internal(void* vec, size_t elem_size, size_t pos) 19 | { 20 | vector_punned_t* vector = vec; 21 | if (!vector->size) { 22 | // Allocate the vector buffer 23 | vector->size = 2; 24 | vector->data = safe_calloc(elem_size, vector->size); 25 | } 26 | if (pos >= vector->size) { 27 | size_t new_size = vector->size; 28 | while (pos >= new_size) { 29 | new_size += (new_size >> 1); 30 | } 31 | vector->data = safe_realloc(vector->data, new_size * elem_size); 32 | vector->size = new_size; 33 | } 34 | } 35 | 36 | static void vector_move_elem_internal(vector_punned_t* vector, size_t elem_size, size_t pos, bool erase) 37 | { 38 | size_t move_count = (vector->count - pos) * elem_size; 39 | void* move_from = ((uint8_t*)vector->data) + ((pos + (erase ? 1 : 0)) * elem_size); 40 | void* move_to = ((uint8_t*)vector->data) + ((pos + (erase ? 0 : 1)) * elem_size); 41 | memmove(move_to, move_from, move_count); 42 | } 43 | 44 | void vector_emplace_internal(void* vec, size_t elem_size, size_t pos) 45 | { 46 | vector_punned_t* vector = vec; 47 | if (pos < vector->count) { 48 | vector_move_elem_internal(vector, elem_size, pos, false); 49 | vector->count++; 50 | } else { 51 | size_t new_count = pos + 1; 52 | if (unlikely(pos >= vector->size)) { 53 | vector_grow_internal(vec, elem_size, pos); 54 | } 55 | void* old_end = ((uint8_t*)vector->data) + (vector->count * elem_size); 56 | memset(old_end, 0, (new_count - vector->count) * elem_size); 57 | vector->count = new_count; 58 | } 59 | } 60 | 61 | void vector_erase_internal(void* vec, size_t elem_size, size_t pos) 62 | { 63 | vector_punned_t* vector = vec; 64 | if (pos < vector->count) { 65 | vector->count--; 66 | vector_move_elem_internal(vector, elem_size, pos, true); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/vector.h: -------------------------------------------------------------------------------- 1 | /* 2 | vector.h - Vector Container 3 | Copyright (C) 2021 LekKit 4 | 5 | This Source Code Form is subject to the terms of the Mozilla Public 6 | License, v. 2.0. If a copy of the MPL was not distributed with this 7 | file, You can obtain one at https://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | #ifndef LEKKIT_VECTOR_H 11 | #define LEKKIT_VECTOR_H 12 | 13 | #include "compiler.h" 14 | #include 15 | #include 16 | 17 | #define vector_t(vec_elem_type) struct { vec_elem_type* data; size_t size; size_t count; } 18 | 19 | // Grow the internal vector buffer to fit element at pos, does not initialize memory 20 | slow_path void vector_grow_internal(void* vec, size_t elem_size, size_t pos); 21 | 22 | // Emplace new element at pos, zeroing the new element and every preceeding one 23 | void vector_emplace_internal(void* vec, size_t elem_size, size_t pos); 24 | 25 | // Erase element at pos, moving the trailing elements into it's place 26 | void vector_erase_internal(void* vec, size_t elem_size, size_t pos); 27 | 28 | // Initialize vector by zeroing it's fields 29 | #define vector_init(vec) \ 30 | do { \ 31 | (vec).data = NULL; \ 32 | (vec).size = 0; \ 33 | (vec).count = 0; \ 34 | } while(0) 35 | 36 | // Free vector buffer 37 | // May be called multiple times, the vector is empty yet reusable afterwards 38 | // Semantically identical to clear(), but also frees memory 39 | #define vector_free(vec) \ 40 | do { \ 41 | free((vec).data); \ 42 | (vec).data = NULL; \ 43 | (vec).size = 0; \ 44 | (vec).count = 0; \ 45 | } while(0) 46 | 47 | // Empty the vector 48 | #define vector_clear(vec) do { (vec).count = 0; } while(0) 49 | 50 | // Get element count 51 | #define vector_size(vec) (vec).count 52 | 53 | // Get underlying buffer capacity 54 | #define vector_capacity(vec) (vec).size 55 | 56 | // Dereference element at specific position 57 | #define vector_at(vec, pos) (vec).data[pos] 58 | 59 | // Get vector elements buffer 60 | #define vector_buffer(vec) (vec).data 61 | 62 | // Resize the vector, zeroing any newly alocated elements 63 | #define vector_resize(vec, size) \ 64 | do { \ 65 | if (unlikely((size) > (vec).count)) { \ 66 | vector_emplace_internal(&(vec), sizeof(*(vec).data), (size) - 1); \ 67 | } \ 68 | (vec).count = (size); \ 69 | } while(0) 70 | 71 | // Put element at specific position, overwriting previous element there if any 72 | #define vector_put(vec, pos, val) \ 73 | do { \ 74 | if (unlikely(pos >= (vec).count)) { \ 75 | vector_emplace_internal(&(vec), sizeof(*(vec).data), pos); \ 76 | } \ 77 | (vec).data[pos] = val; \ 78 | } while(0) 79 | 80 | // Insert new element at the end of the vector 81 | #define vector_push_back(vec, val) \ 82 | do { \ 83 | if (unlikely((vec).count >= (vec).size)) { \ 84 | vector_grow_internal(&(vec), sizeof(*(vec).data), (vec).count); \ 85 | } \ 86 | (vec).data[(vec).count++] = val; \ 87 | } while(0) 88 | 89 | // Insert element at specific position, move trailing elements forward 90 | #define vector_insert(vec, pos, val) \ 91 | do { \ 92 | vector_emplace_internal(&(vec), sizeof(*(vec).data), pos); \ 93 | (vec).data[pos] = val; \ 94 | } while(0) 95 | 96 | // Emplace new element at the end of the vector 97 | #define vector_emplace_back(vec) \ 98 | do { \ 99 | if (unlikely((vec).count >= (vec).size)) { \ 100 | vector_grow_internal(&(vec), sizeof(*(vec).data), (vec).count); \ 101 | } \ 102 | memset(&(vec).data[(vec).count++], 0, sizeof(*(vec).data)); \ 103 | } while(0) 104 | 105 | // Emplace new element at specific position, move trailing elements forward 106 | #define vector_emplace(vec, pos) vector_emplace_internal(&(vec), sizeof(*(vec).data), pos) 107 | 108 | // Erase element at specific posision, move trailing elements backward 109 | #define vector_erase(vec, pos) vector_erase_internal(&(vec), sizeof(*(vec).data), pos) 110 | 111 | // Iterates the vector in forward order 112 | // Be sure to break loop after vector_erase(), since it invalidates forward iterators 113 | #define vector_foreach(vec, iter) \ 114 | for (size_t iter=0; iter<(vec).count; ++iter) 115 | 116 | // Iterates the vector in reversed order, which is safe for vector_erase() 117 | #define vector_foreach_back(vec, iter) \ 118 | for (size_t iter=(vec).count; iter--;) 119 | 120 | #endif 121 | -------------------------------------------------------------------------------- /src/vma_ops.h: -------------------------------------------------------------------------------- 1 | /* 2 | vma_ops.h - Virtual memory area operations 3 | Copyright (C) 2023 LekKit 4 | 5 | This Source Code Form is subject to the terms of the Mozilla Public 6 | License, v. 2.0. If a copy of the MPL was not distributed with this 7 | file, You can obtain one at https://mozilla.org/MPL/2.0/. 8 | */ 9 | 10 | #ifndef LEKKIT_VMA_OPS_H 11 | #define LEKKIT_VMA_OPS_H 12 | 13 | #include "blk_io.h" 14 | 15 | #define VMA_NONE 0x0 16 | #define VMA_EXEC 0x1 17 | #define VMA_WRITE 0x2 18 | #define VMA_READ 0x4 19 | #define VMA_RDWR (VMA_READ | VMA_WRITE) 20 | #define VMA_RDEX (VMA_READ | VMA_EXEC) 21 | #define VMA_RWX (VMA_READ | VMA_WRITE | VMA_EXEC) 22 | 23 | #define VMA_SHARED 0x08 // Shared file mapping, private by default 24 | #define VMA_FIXED 0x10 // Fixed mapping address (Non-destructive), pretty picky to use 25 | #define VMA_THP 0x20 // Transparent hugepages 26 | #define VMA_KSM 0x40 // Kernel same-page merging 27 | 28 | /* 29 | * Misc memory helpers 30 | */ 31 | 32 | // Get host page size 33 | size_t vma_page_size(void); 34 | 35 | // Create anonymous memory-backed FD (POSIX only!) 36 | int vma_anon_memfd(size_t size); 37 | 38 | // Broadcast a global memory barrier on all running threads. May fail on some host systems. 39 | bool vma_broadcast_membarrier(void); 40 | 41 | /* 42 | * VMA allocations & file mapping 43 | */ 44 | 45 | // Allocate anonymous VMA, force needed address using VMA_FIXED 46 | void* vma_alloc(void* addr, size_t size, uint32_t flags); 47 | 48 | // Map file into memory, acts like vma_alloc() when file == NULL 49 | void* vma_mmap(void* addr, size_t size, uint32_t flags, rvfile_t* file, uint64_t offset); 50 | 51 | // Create separate RW/exec VMAs (For W^X JIT) 52 | bool vma_multi_mmap(void** rw, void** exec, size_t size); 53 | 54 | // Resize anon VMA, pass VMA_FIXED to make sure it stays in place 55 | void* vma_remap(void* addr, size_t old_size, size_t new_size, uint32_t flags); 56 | 57 | /* 58 | * VMA operations 59 | */ 60 | 61 | // Change VMA protection flags 62 | bool vma_protect(void* addr, size_t size, uint32_t flags); 63 | 64 | // Synchronise writes to shared file mapping 65 | bool vma_sync(void* addr, size_t size); 66 | 67 | // Hint to free (zero-fill) underlying memory, VMA is still intact 68 | bool vma_clean(void* addr, size_t size, bool lazy); 69 | 70 | // Hint to pageout memory, data is kept intact 71 | bool vma_pageout(void* addr, size_t size, bool lazy); 72 | 73 | // Unmap the VMA 74 | bool vma_free(void* addr, size_t size); 75 | 76 | #endif 77 | --------------------------------------------------------------------------------