├── .clang-format ├── .github └── workflows │ ├── container-build.yml │ └── main.yml ├── .gitmodules ├── CMakeLists.txt ├── Kconfig ├── LICENSE ├── README.rst ├── abort.c ├── ci ├── .gitignore ├── Dockerfile.zephyr ├── build-all.sh ├── build-cmd.sh ├── container-build.sh ├── devshell.sh ├── env.sh ├── sanitycheck.sh └── setup-sdk.sh ├── gnu-stack.ld ├── mutex-pool ├── CMakeLists.txt ├── Kconfig └── gen-mutex-pool.py ├── rust-smem.c ├── rust-smem.ld ├── rust-toolchain.toml ├── rust ├── .gitignore ├── build.sh ├── genproject.sh ├── sysroot-stage1 │ ├── Cargo.lock │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── targets │ ├── i686-unknown-zephyr.json │ ├── riscv32ima-unknown-zephyr-elf.json │ ├── riscv32imac-unknown-zephyr-elf.json │ ├── riscv32imc-unknown-zephyr-elf.json │ ├── riscv64imac-unknown-zephyr-elf.json │ ├── thumbv7em-zephyr-eabi.json │ ├── thumbv7em-zephyr-eabihf.json │ ├── thumbv7m-zephyr-eabi.json │ ├── thumbv7r-zephyr-eabi.json │ ├── thumbv7r-zephyr-eabihf.json │ ├── thumbv8m.main-zephyr-eabi.json │ ├── thumbv8m.main-zephyr-eabihf.json │ └── update-targets.sh ├── zephyr-core │ ├── .gitignore │ ├── Cargo.toml │ ├── build.rs │ ├── src │ │ ├── kobj.rs │ │ ├── lib.rs │ │ ├── memdomain.rs │ │ ├── mempool.rs │ │ ├── mutex.rs │ │ ├── mutex_alloc.rs │ │ ├── poll.rs │ │ ├── poll_signal.rs │ │ ├── semaphore.rs │ │ ├── thread.rs │ │ └── time.rs │ └── time-convert │ │ ├── Cargo.toml │ │ └── src │ │ └── lib.rs ├── zephyr-futures │ ├── Cargo.toml │ └── src │ │ ├── delay.rs │ │ └── lib.rs ├── zephyr-logger │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── zephyr-macros │ ├── .gitignore │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── zephyr-sys │ ├── .gitignore │ ├── Cargo.toml │ ├── build.rs │ ├── src │ │ └── lib.rs │ └── wrapper.h ├── zephyr-uart-buffered │ ├── Cargo.toml │ └── src │ │ ├── futures.rs │ │ └── lib.rs └── zephyr │ ├── Cargo.toml │ └── src │ ├── device.rs │ ├── eeprom.rs │ ├── lib.rs │ └── uart.rs ├── samples ├── futures │ ├── CMakeLists.txt │ ├── Cargo.toml │ ├── prj.conf │ ├── sample.yaml │ └── src │ │ ├── lib.rs │ │ └── main.c ├── no_std │ ├── .gitignore │ ├── CMakeLists.txt │ ├── Cargo.toml │ ├── README.md │ ├── prj.conf │ └── src │ │ ├── lib.rs │ │ └── main.c ├── rust-app │ ├── .gitignore │ ├── CMakeLists.txt │ ├── Cargo.toml │ ├── prj.conf │ └── src │ │ ├── lib.rs │ │ └── main.c └── serial │ ├── CMakeLists.txt │ ├── Cargo.toml │ ├── README.rst │ ├── prj.conf │ └── src │ ├── lib.rs │ └── main.c ├── scripts └── gen_syscalls.py ├── syscall-thunk-any.c ├── syscall-thunk-kernel.c ├── syscall-thunk-user.c ├── tests ├── eeprom │ ├── CMakeLists.txt │ ├── Cargo.toml │ ├── prj.conf │ ├── prj_qemu_x86.conf │ ├── src │ │ ├── lib.rs │ │ └── main.c │ └── testcase.yaml ├── posix-clock │ ├── CMakeLists.txt │ ├── Cargo.lock │ ├── Cargo.toml │ ├── prj.conf │ ├── src │ │ ├── lib.rs │ │ └── main.c │ └── testcase.yaml ├── rust │ ├── CMakeLists.txt │ ├── Cargo.lock │ ├── Cargo.toml │ ├── prj.conf │ ├── src │ │ ├── lib.rs │ │ └── main.c │ └── testcase.yaml └── semaphore │ ├── CMakeLists.txt │ ├── Cargo.lock │ ├── Cargo.toml │ ├── prj.conf │ ├── src │ ├── lib.rs │ └── main.c │ └── testcase.yaml ├── uart-buffered ├── CMakeLists.txt └── src │ ├── uart_buffered.c │ ├── uart_buffered.h │ └── uart_buffered_api.c ├── zephyr-bindgen ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE-Apache-2.0 └── src │ └── main.rs └── zephyr └── module.yml /.clang-format: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: GPL-2.0 2 | # 3 | # clang-format configuration file. Intended for clang-format >= 4. 4 | # 5 | # For more information, see: 6 | # 7 | # Documentation/process/clang-format.rst 8 | # https://clang.llvm.org/docs/ClangFormat.html 9 | # https://clang.llvm.org/docs/ClangFormatStyleOptions.html 10 | # 11 | --- 12 | AccessModifierOffset: -4 13 | AlignAfterOpenBracket: Align 14 | AlignConsecutiveAssignments: false 15 | AlignConsecutiveDeclarations: false 16 | #AlignEscapedNewlines: Left # Unknown to clang-format-4.0 17 | AlignOperands: true 18 | AlignTrailingComments: false 19 | AllowAllParametersOfDeclarationOnNextLine: false 20 | AllowShortBlocksOnASingleLine: false 21 | AllowShortCaseLabelsOnASingleLine: false 22 | AllowShortFunctionsOnASingleLine: None 23 | AllowShortIfStatementsOnASingleLine: false 24 | AllowShortLoopsOnASingleLine: false 25 | AlwaysBreakAfterDefinitionReturnType: None 26 | AlwaysBreakAfterReturnType: None 27 | AlwaysBreakBeforeMultilineStrings: false 28 | AlwaysBreakTemplateDeclarations: false 29 | BinPackArguments: true 30 | BinPackParameters: true 31 | BraceWrapping: 32 | AfterClass: false 33 | AfterControlStatement: false 34 | AfterEnum: false 35 | AfterFunction: true 36 | AfterNamespace: true 37 | AfterObjCDeclaration: false 38 | AfterStruct: false 39 | AfterUnion: false 40 | #AfterExternBlock: false # Unknown to clang-format-5.0 41 | BeforeCatch: false 42 | BeforeElse: false 43 | IndentBraces: false 44 | #SplitEmptyFunction: true # Unknown to clang-format-4.0 45 | #SplitEmptyRecord: true # Unknown to clang-format-4.0 46 | #SplitEmptyNamespace: true # Unknown to clang-format-4.0 47 | BreakBeforeBinaryOperators: None 48 | BreakBeforeBraces: Custom 49 | #BreakBeforeInheritanceComma: false # Unknown to clang-format-4.0 50 | BreakBeforeTernaryOperators: false 51 | BreakConstructorInitializersBeforeComma: false 52 | #BreakConstructorInitializers: BeforeComma # Unknown to clang-format-4.0 53 | BreakAfterJavaFieldAnnotations: false 54 | BreakStringLiterals: false 55 | ColumnLimit: 80 56 | CommentPragmas: '^ IWYU pragma:' 57 | #CompactNamespaces: false # Unknown to clang-format-4.0 58 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 59 | ConstructorInitializerIndentWidth: 8 60 | ContinuationIndentWidth: 8 61 | Cpp11BracedListStyle: false 62 | DerivePointerAlignment: false 63 | DisableFormat: false 64 | ExperimentalAutoDetectBinPacking: false 65 | #FixNamespaceComments: false # Unknown to clang-format-4.0 66 | 67 | # Taken from: 68 | # git grep -h '^#define [^[:space:]]*for_each[^[:space:]]*(' include/ \ 69 | # | sed "s,^#define \([^[:space:]]*for_each[^[:space:]]*\)(.*$, - '\1'," \ 70 | # | sort | uniq 71 | ForEachMacros: 72 | - 'FOR_EACH' 73 | - 'for_each_linux_bus' 74 | - 'for_each_linux_driver' 75 | - 'metal_bitmap_for_each_clear_bit' 76 | - 'metal_bitmap_for_each_set_bit' 77 | - 'metal_for_each_page_size_down' 78 | - 'metal_for_each_page_size_up' 79 | - 'metal_list_for_each' 80 | - 'RB_FOR_EACH' 81 | - 'RB_FOR_EACH_CONTAINER' 82 | - 'SYS_DLIST_FOR_EACH_CONTAINER' 83 | - 'SYS_DLIST_FOR_EACH_CONTAINER_SAFE' 84 | - 'SYS_DLIST_FOR_EACH_NODE' 85 | - 'SYS_DLIST_FOR_EACH_NODE_SAFE' 86 | - 'SYS_SFLIST_FOR_EACH_CONTAINER' 87 | - 'SYS_SFLIST_FOR_EACH_CONTAINER_SAFE' 88 | - 'SYS_SFLIST_FOR_EACH_NODE' 89 | - 'SYS_SFLIST_FOR_EACH_NODE_SAFE' 90 | - 'SYS_SLIST_FOR_EACH_CONTAINER' 91 | - 'SYS_SLIST_FOR_EACH_CONTAINER_SAFE' 92 | - 'SYS_SLIST_FOR_EACH_NODE' 93 | - 'SYS_SLIST_FOR_EACH_NODE_SAFE' 94 | - '_WAIT_Q_FOR_EACH' 95 | - 'Z_GENLIST_FOR_EACH_CONTAINER' 96 | - 'Z_GENLIST_FOR_EACH_CONTAINER_SAFE' 97 | - 'Z_GENLIST_FOR_EACH_NODE' 98 | - 'Z_GENLIST_FOR_EACH_NODE_SAFE' 99 | 100 | #IncludeBlocks: Preserve # Unknown to clang-format-5.0 101 | IncludeCategories: 102 | - Regex: '.*' 103 | Priority: 1 104 | IncludeIsMainRegex: '(Test)?$' 105 | IndentCaseLabels: false 106 | #IndentPPDirectives: None # Unknown to clang-format-5.0 107 | IndentWidth: 8 108 | IndentWrappedFunctionNames: false 109 | JavaScriptQuotes: Leave 110 | JavaScriptWrapImports: true 111 | KeepEmptyLinesAtTheStartOfBlocks: false 112 | MacroBlockBegin: '' 113 | MacroBlockEnd: '' 114 | MaxEmptyLinesToKeep: 1 115 | NamespaceIndentation: Inner 116 | #ObjCBinPackProtocolList: Auto # Unknown to clang-format-5.0 117 | ObjCBlockIndentWidth: 8 118 | ObjCSpaceAfterProperty: true 119 | ObjCSpaceBeforeProtocolList: true 120 | 121 | # Taken from git's rules 122 | #PenaltyBreakAssignment: 10 # Unknown to clang-format-4.0 123 | PenaltyBreakBeforeFirstCallParameter: 30 124 | PenaltyBreakComment: 10 125 | PenaltyBreakFirstLessLess: 0 126 | PenaltyBreakString: 10 127 | PenaltyExcessCharacter: 100 128 | PenaltyReturnTypeOnItsOwnLine: 60 129 | 130 | PointerAlignment: Right 131 | ReflowComments: false 132 | SortIncludes: false 133 | #SortUsingDeclarations: false # Unknown to clang-format-4.0 134 | SpaceAfterCStyleCast: false 135 | SpaceAfterTemplateKeyword: true 136 | SpaceBeforeAssignmentOperators: true 137 | #SpaceBeforeCtorInitializerColon: true # Unknown to clang-format-5.0 138 | #SpaceBeforeInheritanceColon: true # Unknown to clang-format-5.0 139 | SpaceBeforeParens: ControlStatements 140 | #SpaceBeforeRangeBasedForLoopColon: true # Unknown to clang-format-5.0 141 | SpaceInEmptyParentheses: false 142 | SpacesBeforeTrailingComments: 1 143 | SpacesInAngles: false 144 | SpacesInContainerLiterals: false 145 | SpacesInCStyleCastParentheses: false 146 | SpacesInParentheses: false 147 | SpacesInSquareBrackets: false 148 | Standard: Cpp03 149 | TabWidth: 8 150 | UseTab: Always 151 | ... 152 | -------------------------------------------------------------------------------- /.github/workflows/container-build.yml: -------------------------------------------------------------------------------- 1 | name: Container Build 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | env: 7 | REGISTRY: ghcr.io 8 | IMAGE_NAME: ${{ github.repository }} 9 | 10 | jobs: 11 | containers: 12 | strategy: 13 | matrix: 14 | zephyr_version: [3.7.0, 2.7.3, 2.3.0] 15 | runs-on: ubuntu-latest 16 | env: 17 | ZEPHYR_VERSION: ${{ matrix.zephyr_version }} 18 | RUST_VERSION: 1.75.0 19 | 20 | permissions: 21 | contents: read 22 | packages: write 23 | 24 | steps: 25 | - name: Checkout 26 | uses: actions/checkout@v4 27 | 28 | - name: Log in to the Container registry 29 | uses: docker/login-action@v3 30 | with: 31 | registry: ${{ env.REGISTRY }} 32 | username: ${{ github.actor }} 33 | password: ${{ secrets.GITHUB_TOKEN }} 34 | 35 | - name: Set up Docker Buildx 36 | uses: docker/setup-buildx-action@v3 37 | 38 | - name: Docker metadata 39 | id: meta 40 | uses: docker/metadata-action@v5 41 | with: 42 | images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} 43 | tags: type=raw,value=zephyr-rust:${{ env.ZEPHYR_VERSION }}-${{ env.RUST_VERSION }} 44 | 45 | - name: Build and push 46 | uses: docker/build-push-action@v5 47 | with: 48 | context: ./ci 49 | push: true 50 | file: ci/Dockerfile.zephyr 51 | build-args: | 52 | ZEPHYR_VERSION=${{ env.ZEPHYR_VERSION }} 53 | RUST_VERSION=${{ env.RUST_VERSION }} 54 | tags: ${{ steps.meta.outputs.tags }} 55 | labels: ${{ steps.meta.outputs.labels }} 56 | cache-from: type=gha 57 | cache-to: type=gha 58 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: 7 | master 8 | pull_request: 9 | 10 | jobs: 11 | build: 12 | strategy: 13 | fail-fast: false 14 | matrix: 15 | zephyr_version: [3.7.0, 2.7.3, 2.3.0] 16 | board: [qemu_x86, qemu_cortex_m3, qemu_cortex_r5, nucleo_l552ze_q, native_posix, qemu_riscv32, qemu_riscv64] 17 | test: [samples/rust-app, samples/no_std, samples/serial] 18 | exclude: 19 | - board: qemu_riscv32 20 | zephyr_version: 2.3.0 21 | - board: qemu_riscv64 22 | zephyr_version: 2.3.0 23 | - board: qemu_riscv32 24 | zephyr_version: 2.7.3 25 | - board: qemu_riscv64 26 | zephyr_version: 2.7.3 27 | # serial/uart does not exist on posix 28 | - board: native_posix 29 | test: samples/serial 30 | # posix has header issues on Zephyr 3.x 31 | - board: native_posix 32 | zephyr_version: 3.7.0 33 | include: 34 | - fails: false 35 | - run: false 36 | runs-on: ubuntu-latest 37 | container: 38 | image: ghcr.io/${{ github.repository }}:zephyr-rust-${{ matrix.zephyr_version }}-1.75.0 39 | credentials: 40 | username: ${{ github.actor }} 41 | password: ${{ secrets.GITHUB_TOKEN }} 42 | 43 | steps: 44 | - name: Checkout 45 | uses: actions/checkout@v4 46 | with: 47 | submodules: recursive 48 | 49 | - name: Build 50 | run: | 51 | west build -d /tmp/build -p auto -b ${{ matrix.board }} ${{ matrix.test }} 52 | 53 | - name: Run 54 | if: ${{ matrix.run }} 55 | run: | 56 | cd /tmp/build 57 | ninja run || ${{ matrix.fails }} 58 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "rust/libc"] 2 | path = rust/libc 3 | url = https://github.com/tylerwhall/libc.git 4 | shallow = true 5 | [submodule "rust/rust"] 6 | path = rust/rust 7 | url = https://github.com/tylerwhall/rust.git 8 | shallow = true 9 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | cmake_minimum_required(VERSION 3.8.2) 3 | 4 | add_subdirectory(uart-buffered) 5 | add_subdirectory(mutex-pool) 6 | 7 | # Use a clang_target known to clang so headers will be processed correctly with 8 | # bindgen. rust_target may point to a custom json target. 9 | if(${ARCH} STREQUAL "posix" OR ${ARCH} STREQUAL "x86") 10 | set(rust_target i686-unknown-zephyr) 11 | set(clang_target i686-unknown-linux-gnu) 12 | elseif(${ARCH} STREQUAL "arm") 13 | if(CONFIG_CPU_AARCH32_CORTEX_R OR CONFIG_CPU_CORTEX_R) 14 | if(CONFIG_FPU) 15 | set(rust_target thumbv7r-zephyr-eabihf) 16 | set(clang_target thumbv7r-unknown-none-eabihf) 17 | else() 18 | set(rust_target thumbv7r-zephyr-eabi) 19 | set(clang_target thumbv7r-unknown-none-eabi) 20 | endif() 21 | elseif(CONFIG_CPU_CORTEX_M4 OR CONFIG_CPU_CORTEX_M7) 22 | if(CONFIG_FPU) 23 | set(rust_target thumbv7em-zephyr-eabihf) 24 | set(clang_target thumbv7em-unknown-none-eabihf) 25 | else() 26 | set(rust_target thumbv7em-zephyr-eabi) 27 | set(clang_target thumbv7em-unknown-none-eabi) 28 | endif() 29 | elseif(CONFIG_ARMV8_M_MAINLINE) 30 | if(CONFIG_FPU) 31 | set(rust_target thumbv8m.main-zephyr-eabihf) 32 | set(clang_target thumbv8m.main-unknown-none-eabihf) 33 | else() 34 | set(rust_target thumbv8m.main-zephyr-eabi) 35 | set(clang_target thumbv8m.main-unknown-none-eabi) 36 | endif() 37 | else() 38 | set(rust_target thumbv7m-zephyr-eabi) 39 | set(clang_target thumbv7m-none-eabi) 40 | endif() 41 | elseif(${ARCH} STREQUAL "riscv") 42 | set(rust_target "riscv") 43 | 44 | if(CONFIG_64BIT) 45 | string(CONCAT rust_target ${rust_target} "64") 46 | else() 47 | string(CONCAT rust_target ${rust_target} "32") 48 | endif() 49 | 50 | set(clang_target ${rust_target}) 51 | 52 | if (CONFIG_RISCV_ISA_RV32E) 53 | string(CONCAT rust_target ${rust_target} "e") 54 | else() 55 | string(CONCAT rust_target ${rust_target} "i") 56 | endif() 57 | 58 | if (CONFIG_RISCV_ISA_EXT_M) 59 | string(CONCAT rust_target ${rust_target} "m") 60 | endif() 61 | if (CONFIG_RISCV_ISA_EXT_A) 62 | string(CONCAT rust_target ${rust_target} "a") 63 | endif() 64 | 65 | if(CONFIG_RISCV_ISA_EXT_C) 66 | string(CONCAT rust_target ${rust_target} "c") 67 | endif() 68 | 69 | string(CONCAT rust_target ${rust_target} "-unknown-zephyr-elf") 70 | else() 71 | message(FATAL_ERROR "Arch ${ARCH} not supported") 72 | endif() 73 | 74 | message("Rust target: ${rust_target}") 75 | message("Clang target: ${clang_target}") 76 | 77 | set(all_syscalls ${ZEPHYR_BINARY_DIR}/include/generated/all_syscalls.h) 78 | set(syscall_thunks ${ZEPHYR_BINARY_DIR}/include/generated/syscall_thunks.c) 79 | set(syscalls_json ${ZEPHYR_BINARY_DIR}/misc/generated/syscalls.json) 80 | add_custom_command(OUTPUT ${all_syscalls} ${syscall_thunks} 81 | # Also, some files are written to include/generated/syscalls/ 82 | COMMAND 83 | ${PYTHON_EXECUTABLE} 84 | ${CMAKE_CURRENT_SOURCE_DIR}/scripts/gen_syscalls.py 85 | --json-file ${syscalls_json} # Read this file 86 | --thunks ${syscall_thunks} 87 | --all-syscalls ${all_syscalls} 88 | WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} 89 | DEPENDS ${syscalls_json} 90 | ${CMAKE_CURRENT_SOURCE_DIR}/scripts/gen_syscalls.py 91 | ) 92 | add_custom_target(syscall_thunks_target DEPENDS 93 | ${all_syscalls} ${syscall_thunks} ${SYSCALL_LIST_H_TARGET} kernel) 94 | 95 | if(NOT ZEPHYR_BINDGEN) 96 | set(zephyr_bindgen_src_dir ${CMAKE_CURRENT_SOURCE_DIR}/zephyr-bindgen) 97 | set(zephyr_bindgen_build_dir ${CMAKE_CURRENT_BINARY_DIR}/zephyr-bindgen) 98 | set(ZEPHYR_BINDGEN ${zephyr_bindgen_build_dir}/release/zephyr-bindgen) 99 | add_custom_target(zephyr_bindgen_target DEPENDS ${ZEPHYR_BINDGEN}) 100 | set(zephyr_bindgen_target zephyr_bindgen_target) 101 | 102 | add_custom_command(OUTPUT ${ZEPHYR_BINDGEN} 103 | WORKING_DIRECTORY ${zephyr_bindgen_src_dir} 104 | DEPENDS syscall_thunks_target 105 | COMMAND 106 | cargo -v build --release --target-dir=${zephyr_bindgen_build_dir} 107 | ) 108 | endif() 109 | 110 | set(rust_src_dir ${CMAKE_CURRENT_SOURCE_DIR}/rust) 111 | set(rust_sysroot ${CMAKE_CURRENT_BINARY_DIR}/sysroot) 112 | set(rust_sysroot_build ${CMAKE_CURRENT_BINARY_DIR}/sysroot-build) 113 | set(rust_generated_project ${CMAKE_CURRENT_BINARY_DIR}/rust-app) 114 | set(rust_app_build ${CMAKE_CURRENT_BINARY_DIR}/app) 115 | set(rust_staticlib ${rust_app_build}/${rust_target}/release/librust_app.a) 116 | if(NOT DEFINED CARGO_SOURCE_DIR) 117 | set(CARGO_SOURCE_DIR ${CMAKE_SOURCE_DIR}) 118 | endif() 119 | 120 | add_custom_command(OUTPUT ${rust_generated_project}/Cargo.toml 121 | COMMAND ${rust_src_dir}/genproject.sh ${CARGO_SOURCE_DIR} ${rust_generated_project} 122 | DEPENDS ${rust_src_dir}/genproject.sh 123 | ) 124 | add_custom_target(rust_generated_project DEPENDS ${rust_generated_project}/Cargo.toml) 125 | 126 | # external_project_cflags comes from the example: zephyr/samples/application_development/external_lib/CMakeLists.txt 127 | zephyr_get_include_directories_for_lang_as_string( C includes) 128 | zephyr_get_system_include_directories_for_lang_as_string(C system_includes) 129 | zephyr_get_compile_definitions_for_lang_as_string( C definitions) 130 | # `options` is not included because many of the flags are not supported by clang 131 | # `-imacros ${AUTOCONF_H}` is needed and would have been in `options` 132 | #zephyr_get_compile_options_for_lang_as_string( C options) 133 | 134 | set(external_project_cflags 135 | "${includes} ${definitions} ${system_includes} -imacros ${AUTOCONF_H}" 136 | ) 137 | 138 | # Add the Cargo project only if CONFIG_RUST because this will alawys invoke Cargo. 139 | if (CONFIG_RUST) 140 | include(ExternalProject) 141 | 142 | ExternalProject_Add( 143 | rust_project 144 | PREFIX ${CMAKE_CURRENT_BINARY_DIR} 145 | SOURCE_DIR ${rust_src_dir} 146 | BUILD_IN_SOURCE 1 147 | BUILD_ALWAYS 1 148 | DEPENDS syscall_thunks_target ${zephyr_bindgen_target} rust_generated_project 149 | CONFIGURE_COMMAND "" 150 | BUILD_COMMAND 151 | env 152 | "ZEPHYR_BINDGEN=${ZEPHYR_BINDGEN}" 153 | "ZEPHYR_KERNEL_VERSION_NUM=${KERNEL_VERSION_NUMBER}" 154 | "CONFIG_USERSPACE=${CONFIG_USERSPACE}" 155 | "CONFIG_RUST_ALLOC_POOL=${CONFIG_RUST_ALLOC_POOL}" 156 | "CONFIG_RUST_MUTEX_POOL=${CONFIG_RUST_MUTEX_POOL}" 157 | "CONFIG_POSIX_CLOCK=${CONFIG_POSIX_CLOCK}" 158 | "TARGET_CFLAGS=${external_project_cflags} --target=${clang_target}" 159 | "SYSROOT=${rust_sysroot}" 160 | "SYSROOT_BUILD=${rust_sysroot_build}" 161 | "APP_BUILD=${rust_app_build}" 162 | "RUST_TARGET=${rust_target}" 163 | "RUST_TARGET_SPEC=${rust_src_dir}/targets/${rust_target}.json" 164 | "CARGO_MANIFEST=${rust_generated_project}/Cargo.toml" 165 | ./build.sh 166 | INSTALL_COMMAND "" 167 | BUILD_BYPRODUCTS ${rust_staticlib} 168 | ) 169 | 170 | # A regular Zephyr C library for our C code 171 | zephyr_library_named(rust_c) 172 | set(thunk_sources syscall-thunk-any.c) 173 | if(CONFIG_USERSPACE) 174 | set(thunk_sources ${thunk_sources} syscall-thunk-kernel.c syscall-thunk-user.c) 175 | endif() 176 | target_sources(rust_c PRIVATE ${thunk_sources} rust-smem.c abort.c) 177 | if(DEFINED syscall_thunk_cflags) 178 | set_source_files_properties(${thunk_sources} PROPERTIES COMPILE_FLAGS "${syscall_thunk_cflags}") 179 | endif() 180 | add_dependencies(rust_c syscall_thunks_target) 181 | 182 | # Compatibility for Zephyr 1.14 183 | if (NOT COMMAND zephyr_library_import) 184 | # Add the imported library 'library_name', located at 'library_path' to the 185 | # global list of Zephyr CMake libraries. 186 | function(zephyr_library_import library_name library_path) 187 | add_library(${library_name} STATIC IMPORTED GLOBAL) 188 | set_target_properties(${library_name} 189 | PROPERTIES IMPORTED_LOCATION 190 | ${library_path} 191 | ) 192 | zephyr_append_cmake_library(${library_name}) 193 | endfunction() 194 | endif() 195 | 196 | zephyr_library_import(rust ${rust_staticlib}) 197 | if(CONFIG_USERSPACE) 198 | set_property(TARGET zephyr_property_target 199 | APPEND PROPERTY COMPILE_OPTIONS 200 | "-l" librust_app.a "rust_std_partition") 201 | endif() 202 | endif() 203 | -------------------------------------------------------------------------------- /Kconfig: -------------------------------------------------------------------------------- 1 | menuconfig RUST 2 | bool "Rust" 3 | select THREAD_CUSTOM_DATA 4 | # Static initializers for Zephyr >=3.7 5 | select STATIC_INIT_GNU if TOOLCHAIN_SUPPORTS_STATIC_INIT_GNU 6 | # Static initializers for Zephyr 2.x 7 | select CPLUSPLUS if !TOOLCHAIN_SUPPORTS_STATIC_INIT_GNU 8 | help 9 | Rust language support. 10 | 11 | if RUST 12 | 13 | # Define this choice to use PICOLIBC_USE_MODULE on 3.7 and be compatible with 2.x 14 | # This breaks older 3.x versions because PICOLIBC_SOURCE is not a choice there 15 | # It is not possible to use 'select' with a 'choice' 16 | choice PICOLIBC_SOURCE 17 | prompt "Source of Picolibc" 18 | default PICOLIBC_USE_MODULE 19 | 20 | config PICOLIBC_USE_MODULE 21 | bool "Picolibc from module" 22 | endchoice 23 | 24 | choice RUST_GLOBAL_ALLOCATOR 25 | prompt "Rust global allocator" 26 | default RUST_ALLOC_POOL if USERSPACE 27 | help 28 | The global allocator can either use k_malloc or a dedicated sys mem 29 | pool for Rust allocations. A dedicated pool is the default for userspace 30 | because the pool must be placed in a shared memory region accessible 31 | to all Rust userspace threads. If an application wants to provide its 32 | own allocator with #[global_allocator] and avoid creating a Zephyr 33 | sys_mem_pool, select RUST_ALLOC_KMALLOC. 34 | 35 | config RUST_ALLOC_KMALLOC 36 | bool "k_malloc" 37 | help 38 | Adjust the pool size with CONFIG_HEAP_MEM_POOL_SIZE. Must be non-zero. 39 | 40 | config RUST_ALLOC_POOL 41 | bool "Dedicated memory pool" 42 | help 43 | Required for userspace. 44 | endchoice 45 | 46 | if RUST_ALLOC_POOL 47 | config RUST_HEAP_MEM_POOL_MAX_SIZE 48 | int "Rust heap memory pool block size (in bytes)" 49 | default 1024 50 | help 51 | This option specifies the max block size of the heap memory pool 52 | created specifically to act as the Rust global allocator. Must be a 53 | power of 2. A size of zero means that no heap memory pool is defined. 54 | 55 | config RUST_HEAP_MEM_POOL_MIN_SIZE 56 | int "The smallest blocks in the Rust heap memory pool (in bytes)" 57 | default 32 58 | help 59 | This option specifies the size of the smallest block partition. 60 | Option must be a power of 2 and less than or equal to the heap block 61 | size. 62 | 63 | config RUST_HEAP_MEM_POOL_NMAX 64 | int "Rust heap memory pool size (in blocks)" 65 | default 8 66 | help 67 | This option specifies the number of max block size blocks in the Rust 68 | heap. 69 | endif 70 | 71 | rsource "mutex-pool/Kconfig" 72 | 73 | endif 74 | 75 | config UART_BUFFERED 76 | bool "UART fifo buffered driver" 77 | depends on SERIAL && UART_INTERRUPT_DRIVEN 78 | select POLL 79 | help 80 | Allows wrapping the raw UART API with software RX and TX FIFOs that 81 | can be accessed from user space. Interrupt handlers move data between 82 | the hardware fifos and memory. This way, interrupt handlers do not 83 | need to be specifically written to deal with application-specific 84 | UART use cases from kernel mode. Signals are provided for a thread to 85 | be woken on read or write readiness. Behaves similarly to a UNIX 86 | file, supporting blocking and non-blocking reads and writes. 87 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | Rust on Zephyr RTOS 2 | ################### 3 | 4 | Overview 5 | ******** 6 | Zephyr_ module for building a Cargo project and linking it into a Zephyr image. 7 | Add this directory to ZEPHYR_EXTRA_MODULES to build a Cargo library project 8 | (located in the Zephyr app's source directory by default) and link it into the 9 | Zephyr app. 10 | 11 | Version Compatibility 12 | ===================== 13 | **Zephyr**: v2.3, v2.7.3, v3.7. 3.0-3.6 not supported. 14 | 15 | **Rust**: exactly 1.75.0 16 | 17 | Please use one of the above Zephyr releases before reporting issues! At the 18 | time you are reading this, Zephyr's main branch will likely not work, though it 19 | is usually one 1-2 minor changes to support a new release. The project aims to 20 | support 2.3, the LTS releases, and the latest release. 21 | 22 | Features 23 | ======== 24 | 25 | * Generated bindings for all syscalls 26 | * Safe wrappers for some Zephyr APIs (mutex, semaphore, timers, k_poll, UART) 27 | * Basic libstd port (no_std not necessary) 28 | * Heap (std::alloc) see CONFIG_RUST_ALLOC_POOL 29 | * Thread-local storage 30 | * Kernel or user-mode Rust 31 | 32 | * Rust globals and heap in a Rust-specific memory segment that can be granted to specific threads 33 | * Syscalls compile to direct C function calls when !CONFIG_USERSPACE 34 | * Note: running kernel and user-mode Rust at the same time could pose a security risk, since there is one shared global allocator 35 | 36 | * Minimal std::futures executor 37 | 38 | * Supports dynamic tasks and timers 39 | * Currently single-threaded 40 | * async/await UART example 41 | 42 | * Implemented as a Zephyr module for inclusion in existing Zephyr projects 43 | * No modifications to Zephyr source 44 | 45 | 46 | .. _Zephyr: https://github.com/zephyrproject-rtos/zephyr 47 | 48 | Building and Running 49 | ******************** 50 | 51 | Clone the repo 52 | ============== 53 | 54 | Make sure to clone the submodules recursively. This points to modified Rust libstd. 55 | 56 | .. code-block:: console 57 | 58 | git clone --recurse-submodules https://github.com/tylerwhall/zephyr-rust.git 59 | 60 | Zephyr setup 61 | ============ 62 | 63 | Refer to the Zephyr getting started guide_. This includes installing west, 64 | getting Zephyr source, and the Zephyr toolchain. Make sure you can build a C 65 | sample within Zephyr. 66 | 67 | .. _guide: https://docs.zephyrproject.org/2.5.0/getting_started/index.html 68 | 69 | See above for tested compatible Zephyr releases. Please try a release if master 70 | does not work. Due to differences in the syscall header generation, v1.14 LTS 71 | is no longer supported. 72 | See `issue 16 `_. 73 | 74 | Rust toolchain 75 | ============== 76 | 77 | The compiler version must exactly match the version of standard library 78 | included as a submodule of this project. In practice, using a different 79 | compiler version often fails to compile because of Rust internally making heavy 80 | use of unstable compiler features. 81 | 82 | The current base is stable-1.75.0. Rustup is the default workflow, and the 83 | rust-toolchain file in this repo should cause rustup to automatically install 84 | and use the right version. If not, manually install: 85 | 86 | .. code-block:: console 87 | 88 | rustup toolchain install 1.75.0 89 | 90 | If supplying your own rustc and cargo, make sure they are the version above. 91 | The build will fail if it detects a version mismatch. 92 | 93 | Also install clang from your distro. This is required by bindgen to generate 94 | syscall bindings. Else you will get this error 95 | 96 | .. code-block:: console 97 | 98 | thread 'main' panicked at 'Unable to find libclang: "couldn\'t find any valid shared libraries matching: [\'libclang.so\', \'libclang-*.so\', \'libclang.so.*\'] 99 | 100 | Build 101 | ===== 102 | 103 | .. code-block:: console 104 | 105 | west build -p auto -b samples/rust-app/ 106 | 107 | Native: 108 | 109 | .. code-block:: console 110 | 111 | west build -p auto -b native_posix samples/rust-app/ 112 | 113 | qemu_x86: 114 | 115 | .. code-block:: console 116 | 117 | west build -p auto -b qemu_x86 samples/rust-app/ 118 | 119 | ARM Cortex-M: 120 | 121 | .. code-block:: console 122 | 123 | west build -p auto -b qemu_cortex_m3 samples/rust-app/ 124 | 125 | These errors are normal. Needs investigation, but the binary is still created 126 | successfully. 127 | 128 | .. code-block:: console 129 | 130 | x86_64-zephyr-elf-objdump: DWARF error: mangled line number section (bad file number) 131 | 132 | Run (QEMU targets): 133 | 134 | .. code-block:: console 135 | 136 | cd build 137 | ninja run 138 | 139 | Sample Output 140 | ============= 141 | 142 | .. code-block:: console 143 | 144 | *** Booting Zephyr OS build zephyr-v2.2.0 *** 145 | Hello Rust println 146 | Hello from Rust kernel with direct kernel call 147 | Hello from Rust kernel with runtime-detect syscall 148 | Hello from second thread 149 | second thread: f = 1 150 | second thread: now f = 55 151 | Time InstantMs(20) 152 | Time Instant(InstantMs(20)) 153 | Locking 154 | Unlocking 155 | No device 156 | Boxed value 1 157 | main thread: f = 1 158 | main thread: now f = 2 159 | Hello from Rust userspace with forced user-mode syscall 160 | Locking 161 | Unlocking 162 | INFO app: TEST: info!() 163 | WARN app: TEST: warn!() 164 | ERROR app: TEST: error!() 165 | main thread: f = 2 166 | main thread: now f = 3 167 | Hello from Rust userspace with forced user-mode syscall 168 | Hello from Rust userspace with runtime-detect syscall 169 | Next call will crash if userspace is working. 170 | FAILED: zephyr/CMakeFiles/run 171 | 172 | Failure is from an intentional crash at the end of the sample. 173 | 174 | Testing 175 | ******* 176 | 177 | The Zephyr test runner can be used: 178 | 179 | .. code-block:: console 180 | 181 | $ZEPHYR_BASE/scripts/sanitycheck --testcase-root tests -p native_posix -N 182 | 183 | Or you can build and run the test manually: 184 | 185 | .. code-block:: console 186 | 187 | west build -p auto -b native_posix tests/rust 188 | cd build 189 | ninja run 190 | 191 | Supported Architectures 192 | *********************** 193 | 194 | * native_posix 195 | * x86 196 | * armv7m 197 | * armv7r 198 | * thumbv7em 199 | 200 | Really anything that works with Zephyr and Rust should work. Only need to 201 | define a target.json and add a case for it in CMakelists. 202 | 203 | TODO 204 | **** 205 | 206 | * Figure out how to fail tests through assertions in code 207 | * Support #[test] 208 | * Ability to build multiple independent apps 209 | * More safe bindings (e.g. GPIO) 210 | 211 | Features Not Planned to Support 212 | =============================== 213 | 214 | * std::thread. Requires thread resources to be dynamically allocated. This is 215 | possible, but not common for Zephyr. 216 | * Defining static threads in Rust. Zephyr uses many layers of 217 | architecture-specific C macros that would not be wise to try to duplicate 218 | exactly in Rust. Possibly could generate C code like in the "cpp" crate, but 219 | for now just define threads in C and point them at a Rust FFI entry point. 220 | * std::sync::{Mutex, RwLock}. Mutex should work when built without userspace 221 | support. Userspace would require (at least) CONFIG_DYNAMIC_OBJECTS. While 222 | this is possible, I don't want to require it to use libstd. May revisit. 223 | The small number of uses in libstd are patched out. 224 | 225 | License 226 | ******* 227 | 228 | Licensed under either of 229 | 230 | * Apache License, Version 2.0 http://www.apache.org/licenses/LICENSE-2.0 231 | * MIT license http://opensource.org/licenses/MIT 232 | 233 | at your option. 234 | 235 | Contribution 236 | ============ 237 | 238 | Unless you explicitly state otherwise, any contribution intentionally submitted 239 | for inclusion in the work by you, as defined in the Apache-2.0 license, shall be 240 | dual licensed as above, without any additional terms or conditions. 241 | -------------------------------------------------------------------------------- /abort.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Linaro Limited 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | #include 8 | #include 9 | #if KERNEL_VERSION_MAJOR < 3 10 | #include 11 | #else 12 | #include 13 | #endif 14 | 15 | #if KERNEL_VERSION_MAJOR <= 2 && KERNEL_VERSION_MINOR < 5 16 | void __attribute__((weak, noreturn)) abort(void) 17 | { 18 | printk("abort()\n"); 19 | k_panic(); 20 | __builtin_unreachable(); 21 | } 22 | #endif 23 | -------------------------------------------------------------------------------- /ci/.gitignore: -------------------------------------------------------------------------------- 1 | log 2 | -------------------------------------------------------------------------------- /ci/Dockerfile.zephyr: -------------------------------------------------------------------------------- 1 | FROM ubuntu:24.04 AS zephyr 2 | 3 | ARG ZEPHYR_VERSION=2.4.0 4 | # Prevent prompts configuring tzdata 5 | ENV DEBIAN_FRONTEND=noninteractive 6 | RUN ln -fs /usr/share/zoneinfo/America/New_York /etc/localtime 7 | RUN apt-get update && apt-get install -y --no-install-recommends \ 8 | git cmake ninja-build gperf bzip2 \ 9 | ccache dfu-util device-tree-compiler wget \ 10 | python3-dev python3-pip python3-setuptools python3-tk python3-wheel xz-utils file \ 11 | make gcc gcc-multilib g++-multilib libsdl2-dev 12 | 13 | RUN pip3 install --break-system-packages -U west 14 | 15 | ENV WEST_BASE=/zephyrproject 16 | ENV ZEPHYR_BASE=${WEST_BASE}/zephyr 17 | WORKDIR ${WEST_BASE} 18 | RUN git clone https://github.com/zephyrproject-rtos/zephyr.git -b v${ZEPHYR_VERSION} --filter=tree:0 ${ZEPHYR_BASE} 19 | # Needed for building as root 20 | RUN if [ "${ZEPHYR_VERSION}" = "2.3.0" ]; then \ 21 | cd ${ZEPHYR_BASE}; \ 22 | git cherry-pick -n 66d1b3ce10635dd9988898479ce8b8f6516a98df; \ 23 | fi 24 | RUN west init -l ${ZEPHYR_BASE} 25 | RUN west update --narrow --fetch-opt=--filter=tree:0 26 | 27 | RUN pip3 install --break-system-packages -r ${ZEPHYR_BASE}/scripts/requirements.txt 28 | 29 | ADD setup-sdk.sh /setup-sdk.sh 30 | RUN /setup-sdk.sh 31 | # Required for sanitycheck 32 | ENV ZEPHYR_TOOLCHAIN_VARIANT=zephyr 33 | 34 | # Rust toolchain stage 35 | FROM zephyr 36 | 37 | ARG RUST_VERSION=1.69.0 38 | ENV RUSTUP_HOME=/usr/local/rustup \ 39 | CARGO_HOME=/usr/local/cargo \ 40 | PATH=/usr/local/cargo/bin:$PATH \ 41 | RUST_VERSION=${RUST_VERSION} \ 42 | CARGO_REGISTRIES_CRATES_IO_PROTOCOL=sparse 43 | 44 | RUN apt-get install -y clang 45 | RUN set -eux; \ 46 | wget https://sh.rustup.rs -O rustup.sh; \ 47 | sh rustup.sh -y --no-modify-path --profile minimal --default-toolchain ${RUST_VERSION}; \ 48 | rm rustup.sh; \ 49 | rustup --version; \ 50 | cargo --version; \ 51 | rustc --version; 52 | RUN set -eux; \ 53 | ln -sf ${CARGO_HOME}/bin/cargo /usr/bin/cargo; \ 54 | ln -sf ${CARGO_HOME}/bin/rustup /usr/bin/rustup; \ 55 | ln -sf ${CARGO_HOME}/bin/rustc /usr/bin/rustc; 56 | -------------------------------------------------------------------------------- /ci/build-all.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | #rm -rf log 4 | 5 | ZEPHYR_VERSIONS="3.7.0 2.7.3 2.3.0" 6 | 7 | #parallel \ 8 | # -j8 \ 9 | # --results log/container \ 10 | # --resume \ 11 | # --halt now,fail=1 \ 12 | # ZEPHYR_VERSION={1} ./container-build.sh \ 13 | # ::: $ZEPHYR_VERSIONS 14 | 15 | # First build the main sample for zephyr versions 16 | parallel \ 17 | -j8 \ 18 | --results log/build \ 19 | --resume \ 20 | --halt now,fail=1 \ 21 | ZEPHYR_VERSION={1} ./build-cmd.sh west build -d /tmp/build -p auto -b {2} {3} \ 22 | ::: $ZEPHYR_VERSIONS \ 23 | ::: qemu_x86 \ 24 | ::: samples/rust-app 25 | 26 | # Full set. --resume removes duplicates from above 27 | parallel \ 28 | -j8 \ 29 | --results log/build \ 30 | --resume \ 31 | --halt now,fail=1 \ 32 | ZEPHYR_VERSION={1} ./build-cmd.sh west build -d /tmp/build -p auto -b {2} {3} \ 33 | ::: $ZEPHYR_VERSIONS \ 34 | ::: qemu_x86 qemu_cortex_m3 qemu_cortex_r5 \ 35 | ::: samples/rust-app samples/serial samples/futures 36 | 37 | # native_posix does not support UART_INTERRUPT_DRIVEN 38 | parallel \ 39 | -j8 \ 40 | --results log/build \ 41 | --resume \ 42 | --halt now,fail=1 \ 43 | ZEPHYR_VERSION={1} ./build-cmd.sh west build -d /tmp/build -p auto -b {2} {3} \ 44 | ::: $ZEPHYR_VERSIONS \ 45 | ::: native_posix \ 46 | ::: samples/rust-app samples/futures 47 | -------------------------------------------------------------------------------- /ci/build-cmd.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" 4 | . "${DIR}/env.sh" 5 | 6 | set -ex 7 | 8 | exec docker run \ 9 | -i --rm \ 10 | -v ${DIR}/..:/zephyr-rust:ro \ 11 | -w /zephyr-rust \ 12 | ${DOCKER_ARGS[@]} \ 13 | ${CONTAINER_REGISTRY}${ZEPHYR_VERSION}-${RUST_VERSION} \ 14 | "$@" 15 | -------------------------------------------------------------------------------- /ci/container-build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | 3 | DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" 4 | . "${DIR}/env.sh" 5 | 6 | set -ex 7 | 8 | docker build -f Dockerfile.zephyr \ 9 | --build-arg ZEPHYR_VERSION=${ZEPHYR_VERSION} \ 10 | --build-arg RUST_VERSION=${RUST_VERSION} \ 11 | -t ${CONTAINER_REGISTRY}${ZEPHYR_VERSION}-${RUST_VERSION} \ 12 | . 13 | -------------------------------------------------------------------------------- /ci/devshell.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" 4 | . "${DIR}/env.sh" 5 | 6 | exec docker run \ 7 | -it --rm \ 8 | -v /etc/passwd:/etc/passwd:ro \ 9 | -v /etc/group:/etc/group:ro \ 10 | -v /etc/shadow:/etc/shadow:ro \ 11 | -v ${DIR}/..:/zephyr-rust \ 12 | -w /zephyr-rust \ 13 | ${CONTAINER_REGISTRY}${ZEPHYR_VERSION}-${RUST_VERSION} \ 14 | sh -c "chown -R $USER:$GROUP /zephyrproject && \ 15 | chown -R $USER:$GROUP \$CARGO_HOME && \ 16 | mkdir -p $HOME && \ 17 | chown $USER:$GROUP $HOME && \ 18 | su -s /bin/bash $USER" 19 | -------------------------------------------------------------------------------- /ci/env.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ZEPHYR_RUST="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )/.." 4 | RUST_VERSION="${RUST_VERSION:-$( rustc --version | awk '{print $2}' )}" 5 | ZEPHYR_VERSION=${ZEPHYR_VERSION:-3.7.0} 6 | # Set CONTAINER_REGISTRY to something like "zephyr-rust:" to use local images 7 | CONTAINER_REGISTRY="zephyr-rust:" 8 | CONTAINER_REGISTRY=${CONTAINER_REGISTRY:-ghcr.io/tylerwhall/zephyr-rust:zephyr-rust-} 9 | 10 | echo RUST_VERSION=$RUST_VERSION 11 | echo ZEPHYR_VERSION=$ZEPHYR_VERSION 12 | -------------------------------------------------------------------------------- /ci/sanitycheck.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # testcase.yaml schema changed after 2.3 :( 4 | ZEPHYR_VERSION=2.3.0 5 | 6 | DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" 7 | 8 | rm -rf sanity-out 9 | DOCKER_ARGS=(-e ZEPHYR_TOOLCHAIN_VARIANT=zephyr -v ${DIR}/sanity-out:/sanity-out) 10 | 11 | . "${DIR}/build-cmd.sh" sh -c "\$ZEPHYR_BASE/scripts/sanitycheck -N -O /sanity-out/out -c --all -T /zephyr-rust/tests" 12 | -------------------------------------------------------------------------------- /ci/setup-sdk.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | if [ -z "$ZEPHYR_VERSION" ]; then 4 | echo "error: ZEPHYR_VERSION must be set" 5 | exit 1 6 | fi 7 | 8 | case $ZEPHYR_VERSION in 9 | 3.*) 10 | SDK_VERSION=0.16.1 11 | TAR_DIR=zephyr-sdk-0.16.1 12 | TAR_FILE=zephyr-sdk-0.16.1_linux-x86_64.tar.xz 13 | SDK_URL=https://github.com/zephyrproject-rtos/sdk-ng/releases/download/v0.16.1/$TAR_FILE 14 | ;; 15 | 2.7.*) 16 | SDK_VERSION=0.13.2 17 | SDK_URL=https://github.com/zephyrproject-rtos/sdk-ng/releases/download/v0.13.2/zephyr-sdk-0.13.2-linux-x86_64-setup.run 18 | ;; 19 | 2.6.0) 20 | SDK_VERSION=0.12.2 21 | SDK_URL=https://github.com/zephyrproject-rtos/sdk-ng/releases/download/v0.12.2/zephyr-sdk-0.12.2-x86_64-linux-setup.run 22 | ;; 23 | 2.5.0) 24 | SDK_VERSION=0.12.2 25 | SDK_URL=https://github.com/zephyrproject-rtos/sdk-ng/releases/download/v0.12.2/zephyr-sdk-0.12.2-x86_64-linux-setup.run 26 | ;; 27 | 2.4.0) 28 | SDK_VERSION=0.12.2 29 | SDK_URL=https://github.com/zephyrproject-rtos/sdk-ng/releases/download/v0.12.2/zephyr-sdk-0.12.2-x86_64-linux-setup.run 30 | ;; 31 | 2.3.0) 32 | SDK_VERSION=0.12.2 33 | SDK_URL=https://github.com/zephyrproject-rtos/sdk-ng/releases/download/v0.12.2/zephyr-sdk-0.12.2-x86_64-linux-setup.run 34 | ;; 35 | *) 36 | echo "Unknown zephyr version -> sdk version mapping" 37 | exit 1 38 | ;; 39 | esac 40 | 41 | if [ -n "$TAR_FILE" ]; then 42 | wget ${SDK_URL} 43 | tar xvf $TAR_FILE -C /opt 44 | rm $TAR_FILE 45 | cd /opt/$TAR_DIR 46 | ./setup.sh -h -c 47 | else 48 | wget ${SDK_URL} -O ./zephyr-sdk.run && chmod +x ./zephyr-sdk.run 49 | ./zephyr-sdk.run -- -d /opt/zephyr-sdk-${SDK_VERSION} && rm ./zephyr-sdk.run 50 | fi 51 | -------------------------------------------------------------------------------- /gnu-stack.ld: -------------------------------------------------------------------------------- 1 | SECTIONS 2 | { 3 | /DISCARD/ : { *(.note.GNU-stack) } 4 | /DISCARD/ : { *(.note.gnu-stack) } 5 | } INSERT AFTER .text; 6 | -------------------------------------------------------------------------------- /mutex-pool/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | if(CONFIG_RUST_MUTEX_POOL) 2 | 3 | zephyr_library_named(mutex_pool) 4 | 5 | set(mutex_pool_c ${CMAKE_CURRENT_BINARY_DIR}/mutex-pool.c) 6 | 7 | add_custom_command(OUTPUT ${mutex_pool_c} 8 | COMMAND 9 | ${PYTHON_EXECUTABLE} 10 | ${CMAKE_CURRENT_SOURCE_DIR}/gen-mutex-pool.py ${CONFIG_RUST_MUTEX_POOL_SIZE} > ${mutex_pool_c} 11 | DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/gen-mutex-pool.py 12 | ) 13 | add_custom_target(mutex_pool_c_target DEPENDS ${mutex_pool_c}) 14 | 15 | target_sources(mutex_pool PRIVATE ${mutex_pool_c}) 16 | add_dependencies(mutex_pool mutex_pool_c_target) 17 | 18 | endif() 19 | -------------------------------------------------------------------------------- /mutex-pool/Kconfig: -------------------------------------------------------------------------------- 1 | config RUST_MUTEX_POOL 2 | bool "Preallocate k_mutex for std::sync::Mutex" 3 | default y if USERSPACE 4 | help 5 | Allocate a fixed number of static mutexes at compile time rather than 6 | using the default allocator. Required for std::sync::Mutex to 7 | function from user space. 8 | 9 | if RUST_MUTEX_POOL 10 | config RUST_MUTEX_POOL_SIZE 11 | int "Number of preallocated k_mutex" 12 | default 8 13 | help 14 | Number of kernel mutexes available to back std::sync::Mutex. 15 | endif 16 | -------------------------------------------------------------------------------- /mutex-pool/gen-mutex-pool.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import sys 4 | 5 | count = int(sys.argv[1]) 6 | 7 | initializer = "\n".join([f'\tZ_MUTEX_INITIALIZER(rust_mutex_pool[{i}]),' for i in range(count)]) 8 | 9 | sys.stdout.write(f''' 10 | #include 11 | #if KERNEL_VERSION_MAJOR < 3 12 | #include 13 | #else 14 | #include 15 | #endif 16 | 17 | #if KERNEL_VERSION_MAJOR < 3 18 | Z_STRUCT_SECTION_ITERABLE(k_mutex, rust_mutex_pool[{count}]) = {{ 19 | #else 20 | STRUCT_SECTION_ITERABLE_ARRAY(k_mutex, rust_mutex_pool, {count}) = {{ 21 | #endif 22 | {initializer} 23 | }}; 24 | ''') 25 | -------------------------------------------------------------------------------- /rust-smem.c: -------------------------------------------------------------------------------- 1 | #include 2 | #if KERNEL_VERSION_MAJOR < 3 3 | #include 4 | #include 5 | #include 6 | #else 7 | #include 8 | #include 9 | #include 10 | #endif 11 | 12 | #ifdef CONFIG_USERSPACE 13 | struct k_mem_domain rust_std_domain; 14 | K_APPMEM_PARTITION_DEFINE(rust_std_partition); 15 | #define RUST_STD_SECTION K_APP_DMEM_SECTION(rust_std_partition) 16 | #else 17 | #define RUST_STD_SECTION .data 18 | #endif 19 | 20 | #if defined(CONFIG_RUST_ALLOC_POOL) 21 | 22 | #define RUST_STD_MEM_POOL_SIZE (WB_UP(CONFIG_RUST_HEAP_MEM_POOL_MAX_SIZE) * \ 23 | CONFIG_RUST_HEAP_MEM_POOL_NMAX) 24 | 25 | char __aligned(sizeof(void *)) Z_GENERIC_SECTION(RUST_STD_SECTION) 26 | kheap_rust_std_mem_pool[RUST_STD_MEM_POOL_SIZE]; 27 | 28 | struct k_heap Z_GENERIC_SECTION(RUST_STD_SECTION) rust_std_mem_pool; 29 | 30 | #elif CONFIG_HEAP_MEM_POOL_SIZE == 0 31 | 32 | #error CONFIG_HEAP_MEM_POOL_SIZE (k_malloc) \ 33 | must be non-zero if not using a Rust sys mem pool. 34 | 35 | #endif /* defined(CONFIG_RUST_ALLOC_POOL) */ 36 | 37 | #if defined(CONFIG_USERSPACE) || defined(CONFIG_RUST_ALLOC_POOL) 38 | 39 | /* Harmless API difference that generates a warning */ 40 | #if ZEPHYR_VERSION_CODE >= ZEPHYR_VERSION(3, 4, 0) 41 | static int rust_std_init(void) 42 | { 43 | #elif ZEPHYR_VERSION_CODE >= ZEPHYR_VERSION(2, 4, 0) 44 | static int rust_std_init(const struct device *arg) 45 | { 46 | ARG_UNUSED(arg); 47 | #else 48 | static int rust_std_init(struct device *arg) 49 | { 50 | ARG_UNUSED(arg); 51 | #endif /* ZEPHYR_VERSION_CODE >= ZEPHYR_VERSION(2, 4, 0) */ 52 | 53 | #ifdef CONFIG_USERSPACE 54 | struct k_mem_partition *rust_std_parts[] = { &rust_std_partition }; 55 | 56 | k_mem_domain_init(&rust_std_domain, 57 | ARRAY_SIZE(rust_std_parts), rust_std_parts); 58 | #endif /* CONFIG_USERSPACE */ 59 | 60 | #ifdef CONFIG_RUST_ALLOC_POOL 61 | 62 | k_heap_init(&rust_std_mem_pool, kheap_rust_std_mem_pool, 63 | RUST_STD_MEM_POOL_SIZE); 64 | 65 | #endif /* CONFIG_RUST_ALLOC_POOL */ 66 | 67 | #if defined(CONFIG_USERSPACE) && defined(CONFIG_RUST_MUTEX_POOL) 68 | extern struct k_mutex rust_mutex_pool[CONFIG_RUST_MUTEX_POOL_SIZE]; 69 | 70 | for (size_t i = 0; i < ARRAY_SIZE(rust_mutex_pool); i++) { 71 | k_object_access_all_grant(&rust_mutex_pool[i]); 72 | } 73 | #endif /* defined(CONFIG_USERSPACE) && defined(CONFIG_RUST_MUTEX_POOL) */ 74 | 75 | return 0; 76 | } 77 | 78 | SYS_INIT(rust_std_init, PRE_KERNEL_2, CONFIG_APPLICATION_INIT_PRIORITY); 79 | 80 | #endif /* defined(CONFIG_USERSPACE) || defined(CONFIG_RUST_ALLOC_POOL) */ 81 | -------------------------------------------------------------------------------- /rust-smem.ld: -------------------------------------------------------------------------------- 1 | SECTIONS 2 | { 3 | data_smem_rust_std_partition_data : { *librust_app.a:*(.data .data.*) } 4 | data_smem_rust_std_partition_bss : { *librust_app.a:*(.bss .bss.* COMMON COMMON.*) } 5 | } INSERT BEFORE .data; 6 | -------------------------------------------------------------------------------- /rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "1.75.0" 3 | profile = "minimal" 4 | -------------------------------------------------------------------------------- /rust/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | -------------------------------------------------------------------------------- /rust/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | 3 | HOST=$(rustc -vV | grep host: | cut -d ' ' -f 2) 4 | CARGO_ARGS="-v build --target=${RUST_TARGET_SPEC} --release" 5 | VERSION="1.75" 6 | CURRENT_RUSTC_VERSION=$(rustc -vV | grep ^release: | cut -d ' ' -f 2 | cut -d '.' -f '1,2') 7 | 8 | # Assert cargo version matches the certified version 9 | if [ "${CURRENT_RUSTC_VERSION}x" != "${VERSION}x" ]; then 10 | echo "Error: Current cargo version: ${CURRENT_RUSTC_VERSION}, expected: ${VERSION}" 11 | echo "If using rustup, it should be automatically installed. If not, run" 12 | echo "rustup toolchain install ${VERSION}" 13 | exit 1 14 | fi 15 | 16 | publish_sysroot() { 17 | local CLEAN_DIR_IF_CHANGED=$1 18 | shift 19 | local SYSROOT=$1 20 | shift 21 | local SYSROOT_LIB="${SYSROOT}/lib/rustlib/${RUST_TARGET}/lib" 22 | local SYSROOT_LIB_HOST="${SYSROOT}/lib/rustlib/${HOST}/lib" 23 | mkdir -p ${SYSROOT_LIB} ${SYSROOT_LIB_HOST} 24 | mkdir -p ${SYSROOT_LIB}-new ${SYSROOT_LIB_HOST}-new 25 | 26 | for src in $@; do 27 | cp -a $src/${RUST_TARGET}/release/deps/. ${SYSROOT_LIB}-new 28 | cp -a $src/release/deps/. ${SYSROOT_LIB_HOST}-new 29 | done 30 | if ! diff -qr ${SYSROOT_LIB} ${SYSROOT_LIB}-new || ! diff -qr ${SYSROOT_LIB_HOST} ${SYSROOT_LIB_HOST}-new; then 31 | rm -rf ${CLEAN_DIR_IF_CHANGED} 32 | fi 33 | rm -r ${SYSROOT_LIB} ${SYSROOT_LIB_HOST} 34 | mv ${SYSROOT_LIB}-new ${SYSROOT_LIB} 35 | mv ${SYSROOT_LIB_HOST}-new ${SYSROOT_LIB_HOST} 36 | } 37 | 38 | # Unstable features are required for building std. Also allow in the app 39 | # project for now because they're often needed for low level embedded. 40 | export RUSTC_BOOTSTRAP=1 41 | # Build std 42 | RUSTFLAGS="$RUSTFLAGS -Cembed-bitcode=yes" cargo ${CARGO_ARGS} \ 43 | --target-dir=${SYSROOT_BUILD}-stage1 \ 44 | --manifest-path=./sysroot-stage1/Cargo.toml -p std 45 | publish_sysroot ${APP_BUILD} ${SYSROOT} ${SYSROOT_BUILD}-stage1 46 | 47 | export RUSTFLAGS="${RUSTFLAGS} --sysroot ${SYSROOT}" 48 | cargo ${CARGO_ARGS} --target-dir=${APP_BUILD} --manifest-path=${CARGO_MANIFEST} 49 | -------------------------------------------------------------------------------- /rust/genproject.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | 3 | crate_dir=$1 4 | outdir=$2 5 | 6 | rm -rf $outdir 7 | mkdir -p $outdir/src 8 | # Copy a Cargo.lock if it exists 9 | cp ${crate_dir}/Cargo.lock $outdir || true 10 | 11 | echo "extern crate app;" > $outdir/src/lib.rs 12 | 13 | cat > $outdir/Cargo.toml <"] 3 | name = "sysroot" 4 | version = "0.0.0" 5 | repository = "https://github.com/tylerwhall/zephyr-rust.git" 6 | 7 | [dependencies.std] 8 | path = "../rust/library/std" 9 | 10 | [patch.crates-io] 11 | libc = { path = '../libc' } 12 | 13 | rustc-std-workspace-core = { path = '../rust/library/rustc-std-workspace-core' } 14 | rustc-std-workspace-alloc = { path = '../rust/library/rustc-std-workspace-alloc' } 15 | 16 | [profile.release] 17 | lto = true 18 | panic = "abort" 19 | debug = true 20 | -------------------------------------------------------------------------------- /rust/sysroot-stage1/src/lib.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /rust/targets/i686-unknown-zephyr.json: -------------------------------------------------------------------------------- 1 | { 2 | "arch": "x86", 3 | "cpu": "pentium4", 4 | "crt-static-respected": true, 5 | "data-layout": "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-f64:32:64-f80:32-n8:16:32-S128", 6 | "dynamic-linking": "false", 7 | "env": "gnu", 8 | "has-rpath": true, 9 | "has-thread-local": "false", 10 | "llvm-target": "i686-unknown-linux-gnu", 11 | "max-atomic-width": 64, 12 | "os": "zephyr", 13 | "position-independent-executables": "false", 14 | "pre-link-args": { 15 | "gcc": [ 16 | "-m32" 17 | ] 18 | }, 19 | "relro-level": "full", 20 | "stack-probes": { 21 | "kind": "inline-or-call", 22 | "min-llvm-version-for-inline": [ 23 | 16, 24 | 0, 25 | 0 26 | ] 27 | }, 28 | "supported-sanitizers": [ 29 | "address" 30 | ], 31 | "supported-split-debuginfo": [ 32 | "packed", 33 | "unpacked", 34 | "off" 35 | ], 36 | "target-family": "zephyr", 37 | "target-pointer-width": "32", 38 | "relocation-model": "static", 39 | "features": "-mmx,-sse,+soft-float" 40 | } 41 | -------------------------------------------------------------------------------- /rust/targets/riscv32ima-unknown-zephyr-elf.json: -------------------------------------------------------------------------------- 1 | { 2 | "arch": "riscv32", 3 | "cpu": "generic-rv32", 4 | "data-layout": "e-m:e-p:32:32-i64:64-n32-S128", 5 | "eh-frame-header": false, 6 | "emit-debug-gdb-scripts": false, 7 | "features": "+m,+a", 8 | "linker": "rust-lld", 9 | "linker-flavor": "ld.lld", 10 | "llvm-target": "riscv32", 11 | "max-atomic-width": 32, 12 | "panic-strategy": "abort", 13 | "relocation-model": "static", 14 | "target-pointer-width": "32", 15 | "target-family": "zephyr", 16 | "os": "zephyr", 17 | "position-independent-executables": "false", 18 | "dynamic-linking": "false", 19 | "has-thread-local": "false" 20 | } 21 | -------------------------------------------------------------------------------- /rust/targets/riscv32imac-unknown-zephyr-elf.json: -------------------------------------------------------------------------------- 1 | { 2 | "arch": "riscv32", 3 | "cpu": "generic-rv32", 4 | "data-layout": "e-m:e-p:32:32-i64:64-n32-S128", 5 | "eh-frame-header": false, 6 | "emit-debug-gdb-scripts": false, 7 | "features": "+m,+a,+c", 8 | "linker": "rust-lld", 9 | "linker-flavor": "ld.lld", 10 | "llvm-target": "riscv32", 11 | "max-atomic-width": 32, 12 | "panic-strategy": "abort", 13 | "relocation-model": "static", 14 | "target-pointer-width": "32", 15 | "target-family": "zephyr", 16 | "os": "zephyr", 17 | "position-independent-executables": "false", 18 | "dynamic-linking": "false", 19 | "has-thread-local": "false" 20 | } 21 | -------------------------------------------------------------------------------- /rust/targets/riscv32imc-unknown-zephyr-elf.json: -------------------------------------------------------------------------------- 1 | { 2 | "arch": "riscv32", 3 | "atomic-cas": false, 4 | "cpu": "generic-rv32", 5 | "data-layout": "e-m:e-p:32:32-i64:64-n32-S128", 6 | "eh-frame-header": false, 7 | "emit-debug-gdb-scripts": false, 8 | "features": "+m,+c", 9 | "linker": "rust-lld", 10 | "linker-flavor": "ld.lld", 11 | "llvm-target": "riscv32", 12 | "max-atomic-width": 0, 13 | "panic-strategy": "abort", 14 | "relocation-model": "static", 15 | "target-pointer-width": "32", 16 | "target-family": "zephyr", 17 | "os": "zephyr", 18 | "position-independent-executables": "false", 19 | "dynamic-linking": "false", 20 | "has-thread-local": "false" 21 | } 22 | -------------------------------------------------------------------------------- /rust/targets/riscv64imac-unknown-zephyr-elf.json: -------------------------------------------------------------------------------- 1 | { 2 | "arch": "riscv64", 3 | "code-model": "medium", 4 | "cpu": "generic-rv64", 5 | "data-layout": "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128", 6 | "eh-frame-header": false, 7 | "emit-debug-gdb-scripts": false, 8 | "features": "+m,+a,+c", 9 | "linker": "rust-lld", 10 | "linker-flavor": "ld.lld", 11 | "llvm-target": "riscv64", 12 | "max-atomic-width": 64, 13 | "panic-strategy": "abort", 14 | "relocation-model": "static", 15 | "target-pointer-width": "64", 16 | "target-family": "zephyr", 17 | "os": "zephyr", 18 | "position-independent-executables": "false", 19 | "dynamic-linking": "false", 20 | "has-thread-local": "false" 21 | } 22 | -------------------------------------------------------------------------------- /rust/targets/thumbv7em-zephyr-eabi.json: -------------------------------------------------------------------------------- 1 | { 2 | "abi": "eabi", 3 | "arch": "arm", 4 | "c-enum-min-bits": 8, 5 | "data-layout": "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64", 6 | "emit-debug-gdb-scripts": false, 7 | "frame-pointer": "always", 8 | "linker": "rust-lld", 9 | "linker-flavor": "ld.lld", 10 | "llvm-target": "thumbv7em-none-eabi", 11 | "max-atomic-width": 32, 12 | "panic-strategy": "abort", 13 | "relocation-model": "static", 14 | "target-pointer-width": "32", 15 | "target-family": "zephyr", 16 | "os": "zephyr", 17 | "position-independent-executables": "false", 18 | "dynamic-linking": "false", 19 | "has-thread-local": "false" 20 | } 21 | -------------------------------------------------------------------------------- /rust/targets/thumbv7em-zephyr-eabihf.json: -------------------------------------------------------------------------------- 1 | { 2 | "abi": "eabihf", 3 | "arch": "arm", 4 | "c-enum-min-bits": 8, 5 | "data-layout": "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64", 6 | "emit-debug-gdb-scripts": false, 7 | "features": "+vfp4,-d32,-fp64", 8 | "frame-pointer": "always", 9 | "linker": "rust-lld", 10 | "linker-flavor": "ld.lld", 11 | "llvm-target": "thumbv7em-none-eabihf", 12 | "max-atomic-width": 32, 13 | "panic-strategy": "abort", 14 | "relocation-model": "static", 15 | "target-pointer-width": "32", 16 | "target-family": "zephyr", 17 | "os": "zephyr", 18 | "position-independent-executables": "false", 19 | "dynamic-linking": "false", 20 | "has-thread-local": "false" 21 | } 22 | -------------------------------------------------------------------------------- /rust/targets/thumbv7m-zephyr-eabi.json: -------------------------------------------------------------------------------- 1 | { 2 | "abi": "eabi", 3 | "arch": "arm", 4 | "c-enum-min-bits": 8, 5 | "data-layout": "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64", 6 | "emit-debug-gdb-scripts": false, 7 | "frame-pointer": "always", 8 | "linker": "rust-lld", 9 | "linker-flavor": "ld.lld", 10 | "llvm-target": "thumbv7m-none-eabi", 11 | "max-atomic-width": 32, 12 | "panic-strategy": "abort", 13 | "relocation-model": "static", 14 | "target-pointer-width": "32", 15 | "target-family": "zephyr", 16 | "os": "zephyr", 17 | "position-independent-executables": "false", 18 | "dynamic-linking": "false", 19 | "has-thread-local": "false" 20 | } 21 | -------------------------------------------------------------------------------- /rust/targets/thumbv7r-zephyr-eabi.json: -------------------------------------------------------------------------------- 1 | { 2 | "abi": "eabi", 3 | "arch": "arm", 4 | "c-enum-min-bits": 8, 5 | "data-layout": "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64", 6 | "emit-debug-gdb-scripts": false, 7 | "linker": "rust-lld", 8 | "linker-flavor": "ld.lld", 9 | "llvm-target": "armv7r-unknown-none-eabi", 10 | "max-atomic-width": 32, 11 | "panic-strategy": "abort", 12 | "relocation-model": "static", 13 | "target-pointer-width": "32", 14 | "target-family": "zephyr", 15 | "os": "zephyr", 16 | "position-independent-executables": "false", 17 | "dynamic-linking": "false", 18 | "has-thread-local": "false", 19 | "features": ",+v7,+thumb-mode,+thumb2,+rclass" 20 | } 21 | -------------------------------------------------------------------------------- /rust/targets/thumbv7r-zephyr-eabihf.json: -------------------------------------------------------------------------------- 1 | { 2 | "abi": "eabihf", 3 | "arch": "arm", 4 | "c-enum-min-bits": 8, 5 | "data-layout": "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64", 6 | "emit-debug-gdb-scripts": false, 7 | "features": "+vfp3,-d32,-fp16,+v7,+thumb-mode,+thumb2,+rclass", 8 | "linker": "rust-lld", 9 | "linker-flavor": "ld.lld", 10 | "llvm-target": "armv7r-unknown-none-eabihf", 11 | "max-atomic-width": 32, 12 | "panic-strategy": "abort", 13 | "relocation-model": "static", 14 | "target-pointer-width": "32", 15 | "target-family": "zephyr", 16 | "os": "zephyr", 17 | "position-independent-executables": "false", 18 | "dynamic-linking": "false", 19 | "has-thread-local": "false" 20 | } 21 | -------------------------------------------------------------------------------- /rust/targets/thumbv8m.main-zephyr-eabi.json: -------------------------------------------------------------------------------- 1 | { 2 | "abi": "eabi", 3 | "arch": "arm", 4 | "c-enum-min-bits": 8, 5 | "data-layout": "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64", 6 | "emit-debug-gdb-scripts": false, 7 | "frame-pointer": "always", 8 | "linker": "rust-lld", 9 | "linker-flavor": "ld.lld", 10 | "llvm-target": "thumbv8m.main-none-eabi", 11 | "max-atomic-width": 32, 12 | "panic-strategy": "abort", 13 | "relocation-model": "static", 14 | "target-pointer-width": "32", 15 | "target-family": "zephyr", 16 | "os": "zephyr", 17 | "position-independent-executables": "false", 18 | "dynamic-linking": "false", 19 | "has-thread-local": "false" 20 | } 21 | -------------------------------------------------------------------------------- /rust/targets/thumbv8m.main-zephyr-eabihf.json: -------------------------------------------------------------------------------- 1 | { 2 | "abi": "eabihf", 3 | "arch": "arm", 4 | "c-enum-min-bits": 8, 5 | "data-layout": "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64", 6 | "emit-debug-gdb-scripts": false, 7 | "features": "+fp-armv8,-fp64,-d32", 8 | "frame-pointer": "always", 9 | "linker": "rust-lld", 10 | "linker-flavor": "ld.lld", 11 | "llvm-target": "thumbv8m.main-none-eabihf", 12 | "max-atomic-width": 32, 13 | "panic-strategy": "abort", 14 | "relocation-model": "static", 15 | "target-pointer-width": "32", 16 | "target-family": "zephyr", 17 | "os": "zephyr", 18 | "position-independent-executables": "false", 19 | "dynamic-linking": "false", 20 | "has-thread-local": "false" 21 | } 22 | -------------------------------------------------------------------------------- /rust/targets/update-targets.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | targets=( 4 | i686-unknown-zephyr 5 | riscv32imac-unknown-zephyr-elf 6 | # riscv32ima-unknown-zephyr-elf # need to update script to generically handle RISCV extensions. Manually update from above for now. 7 | riscv32imc-unknown-zephyr-elf 8 | riscv64imac-unknown-zephyr-elf 9 | thumbv7em-zephyr-eabihf 10 | thumbv7em-zephyr-eabi 11 | thumbv7m-zephyr-eabi 12 | thumbv7r-zephyr-eabihf 13 | thumbv7r-zephyr-eabi 14 | thumbv8m.main-zephyr-eabi 15 | thumbv8m.main-zephyr-eabihf 16 | ) 17 | 18 | for target in "${targets[@]}"; do 19 | # Replace "zephyr" with "none" 20 | rust_target="${target/zephyr/none}" 21 | 22 | # Special case mapping for i686-unknown-none 23 | case $rust_target in 24 | i686-unknown-none) 25 | rust_target=i686-unknown-linux-gnu 26 | extra_filter='| .["features"] = "-mmx,-sse,+soft-float"' 27 | ;; 28 | thumbv7r-*) 29 | # Rust does not have a thumbv7 target. Use armv7 and add thumb features 30 | rust_target=${rust_target/thumbv7/armv7} 31 | extra_filter='| .["features"] += ",+v7,+thumb-mode,+thumb2,+rclass"' 32 | ;; 33 | *) 34 | extra_filter="" 35 | ;; 36 | esac 37 | echo "Updating $target from $rust_target" 38 | # Set target-family and os to zephyr 39 | # Remove is-builtin, linker, linker-flavor 40 | filter='.["target-family"] = "zephyr" | 41 | .["os"] = "zephyr" | 42 | .["position-independent-executables"] = "false" | 43 | .["relocation-model"] = "static" | 44 | .["dynamic-linking"] = "false" | 45 | .["has-thread-local"] = "false" | 46 | del(.["is-builtin"])' 47 | filter="$filter $extra_filter" 48 | RUSTC_BOOTSTRAP=1 rustc --print target-spec-json -Z unstable-options --target $rust_target | 49 | jq "$filter" > $target.json 50 | done 51 | -------------------------------------------------------------------------------- /rust/zephyr-core/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | Cargo.lock 4 | -------------------------------------------------------------------------------- /rust/zephyr-core/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "zephyr-core" 3 | description = "Safe bindings to Zephyr kernel API (only using libcore)" 4 | version = "0.1.0" 5 | authors = ["Tyler Hall "] 6 | edition = "2018" 7 | 8 | [dependencies] 9 | zephyr-sys = { path = "../zephyr-sys" } 10 | alloc = { path = "../rust/library/alloc" } 11 | core = { version = '1.0.0', optional = true, package = 'rustc-std-workspace-core' } 12 | compiler_builtins = { version = '0.1.2', optional = true } 13 | derive_more = { version = '0.14.1', default-features = false, features = ['no_std'] } 14 | libc = "0.2" 15 | 16 | time-convert = { path = "./time-convert" } 17 | 18 | [features] 19 | defaults = [ 'rustc-dep-of-std' ] 20 | rustc-dep-of-std = ['core', 'compiler_builtins/rustc-dep-of-std', 'zephyr-sys/rustc-dep-of-std', 'libc/rustc-dep-of-std', 'time-convert/rustc-dep-of-std'] 21 | have_std = [] 22 | -------------------------------------------------------------------------------- /rust/zephyr-core/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | let kernel_version_str_trimmed = std::env::var("ZEPHYR_KERNEL_VERSION_NUM") 3 | .expect("ZEPHYR_KERNEL_VERSION_NUM must be set") 4 | .trim_start_matches("0x").to_owned(); 5 | let kernel_version = u32::from_str_radix(&kernel_version_str_trimmed, 16) 6 | .expect("ZEPHYR_KERNEL_VERSION_NUM must be an integer"); 7 | 8 | if kernel_version >= 0x2_05_00 { 9 | println!("cargo:rustc-cfg=zephyr250"); 10 | } 11 | if kernel_version >= 0x2_07_00 { 12 | println!("cargo:rustc-cfg=zephyr270"); 13 | } 14 | if kernel_version >= 0x3_00_00 { 15 | println!("cargo:rustc-cfg=zephyr300"); 16 | } 17 | if kernel_version >= 0x3_05_00 { 18 | println!("cargo:rustc-cfg=zephyr350"); 19 | } 20 | 21 | if std::env::var("CONFIG_USERSPACE").expect("CONFIG_USERSPACE must be set") == "y" { 22 | println!("cargo:rustc-cfg=usermode"); 23 | } 24 | if std::env::var("CONFIG_RUST_ALLOC_POOL").expect("CONFIG_RUST_ALLOC_POOL must be set") == "y" { 25 | println!("cargo:rustc-cfg=mempool"); 26 | } 27 | if std::env::var("CONFIG_RUST_MUTEX_POOL").expect("CONFIG_RUST_MUTEX_POOL must be set") == "y" { 28 | println!("cargo:rustc-cfg=mutex_pool"); 29 | } 30 | if std::env::var("CONFIG_POSIX_CLOCK").expect("CONFIG_POSIX_CLOCK must be set") == "y" { 31 | println!("cargo:rustc-cfg=clock"); 32 | } 33 | if let Ok(tls) = std::env::var("CONFIG_THREAD_LOCAL_STORAGE") { 34 | if tls == "y" { 35 | println!("cargo:rustc-cfg=tls"); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /rust/zephyr-core/src/kobj.rs: -------------------------------------------------------------------------------- 1 | use core::cell::UnsafeCell; 2 | use core::mem::MaybeUninit; 3 | use core::ops::Deref; 4 | 5 | use zephyr_sys::raw::k_objects; 6 | 7 | /// A kernel object that is usable via system calls. 8 | /// 9 | /// This implies locking is done in the kernel and this is therefore Send + Sync. e.g. mutex, 10 | /// semaphore, fifo. Implement this for the raw Zephyr C structs. 11 | pub unsafe trait KObj { 12 | const OTYPE: k_objects; 13 | 14 | // This is safe, itself, but obviously unsafe to use the mut void pointer 15 | fn as_void_ptr(&self) -> *mut libc::c_void { 16 | self as *const _ as *mut _ 17 | } 18 | } 19 | 20 | // On behalf of the 'zephyr' crate 21 | unsafe impl KObj for zephyr_sys::raw::device { 22 | const OTYPE: k_objects = zephyr_sys::raw::k_objects_K_OBJ_ANY; 23 | } 24 | 25 | pub struct StaticKObj(UnsafeCell>); 26 | 27 | unsafe impl Send for StaticKObj {} 28 | unsafe impl Sync for StaticKObj {} 29 | 30 | impl StaticKObj { 31 | // Unsafe because there must be some provision to initialize this before it is referenced and 32 | // it must be declared static because we hand out KObjRef with static lifetime. 33 | pub const unsafe fn uninit() -> Self { 34 | StaticKObj(UnsafeCell::new(MaybeUninit::uninit())) 35 | } 36 | } 37 | 38 | impl StaticKObj { 39 | pub fn as_ptr(&self) -> *const T { 40 | unsafe { (*self.0.get()).as_ptr() } 41 | } 42 | 43 | pub fn as_mut_ptr(&self) -> *mut T { 44 | unsafe { (*self.0.get()).as_mut_ptr() } 45 | } 46 | } 47 | 48 | impl Deref for StaticKObj { 49 | type Target = T; 50 | 51 | fn deref(&self) -> &T { 52 | unsafe { &*self.as_ptr() } 53 | } 54 | } 55 | 56 | #[macro_export] 57 | macro_rules! make_static_wrapper { 58 | ($k_type:ident, $k_path:path) => { 59 | /// Defines a newtype struct k_foo appropriate for static initialization that 60 | /// looks to zephyr like its own. 61 | /// 62 | /// Creating uninitialized variables in Rust requires a union which is in the 63 | /// implementation of MaybeUninit. gen_kobject_list.py ignores members of unions 64 | /// so fails to recognise that our StaticKobj contains a struct k_mutex. By using 65 | /// the same structure name, we trick gen_kobject_list.py into whitelisting the 66 | /// address of this struct as a kernel object. 67 | pub mod global { 68 | use crate::kobj::*; 69 | use core::ops::Deref; 70 | 71 | #[allow(non_camel_case_types)] 72 | pub struct $k_type(StaticKObj<$k_path>); 73 | 74 | unsafe impl KObj for $k_type { 75 | const OTYPE: zephyr_sys::raw::k_objects = <$k_path as KObj>::OTYPE; 76 | } 77 | 78 | impl $k_type { 79 | pub const unsafe fn uninit() -> Self { 80 | $k_type(StaticKObj::uninit()) 81 | } 82 | 83 | /// Get the real k_obj type. Same as deref twice. 84 | pub fn kobj(&self) -> &$k_path { 85 | self.deref().deref() 86 | } 87 | } 88 | 89 | impl Deref for $k_type { 90 | type Target = StaticKObj<$k_path>; 91 | 92 | fn deref(&self) -> &Self::Target { 93 | &self.0 94 | } 95 | } 96 | } 97 | }; 98 | } 99 | -------------------------------------------------------------------------------- /rust/zephyr-core/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(not(feature = "have_std"), no_std)] 2 | #![feature(never_type)] 3 | 4 | #[macro_use] 5 | extern crate derive_more; 6 | 7 | pub mod kobj; 8 | pub mod memdomain; 9 | pub mod mempool; 10 | pub mod mutex; 11 | pub mod mutex_alloc; 12 | pub mod poll; 13 | mod poll_signal; 14 | pub mod semaphore; 15 | pub mod thread; 16 | mod time; 17 | 18 | pub use time::*; 19 | use zephyr_sys::raw::KERNEL_VERSION_NUMBER; 20 | use core::fmt; 21 | 22 | // Set from environment from build.rs 23 | pub const CONFIG_USERSPACE: bool = cfg!(usermode); 24 | 25 | #[derive(Eq, PartialEq, Ord, PartialOrd)] 26 | pub struct KernelVersion(u32); 27 | 28 | // Zephyr kernel version number 29 | pub const KERNEL_VERSION: KernelVersion = KernelVersion(KERNEL_VERSION_NUMBER); 30 | 31 | impl fmt::Display for KernelVersion { 32 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 33 | let major = (self.0 & 0xff0000) >> 16; 34 | let minor = (self.0 & 0x00ff00) >> 8; 35 | let patch = self.0 & 0x0000ff; 36 | write!(f, "{}.{}.{}", major, minor, patch) 37 | } 38 | } 39 | 40 | // Use this mem pool for global allocs instead of kmalloc 41 | #[cfg(mempool)] 42 | crate::global_sys_mem_pool!(rust_std_mem_pool); 43 | 44 | /// Convert a negative error code to a Result 45 | pub trait NegErr { 46 | fn neg_err(&self) -> Result; 47 | } 48 | 49 | impl NegErr for i32 { 50 | fn neg_err(&self) -> Result { 51 | if *self >= 0 { 52 | Ok(*self as u32) 53 | } else { 54 | Err((-*self) as u32) 55 | } 56 | } 57 | } 58 | 59 | pub mod context { 60 | /// Kernel, user, or runtime-detect (any) 61 | pub unsafe trait Context {} 62 | 63 | pub struct Kernel; 64 | unsafe impl Context for Kernel {} 65 | 66 | pub struct User; 67 | unsafe impl Context for User {} 68 | 69 | pub struct Any; 70 | unsafe impl Context for Any {} 71 | } 72 | 73 | macro_rules! zephyr_bindings { 74 | ($context:ident, $context_struct:path) => { 75 | #[inline(always)] 76 | pub fn k_str_out_raw(s: &[u8]) { 77 | unsafe { zephyr_sys::syscalls::$context::k_str_out(s.as_ptr() as *mut _, s.len()) }; 78 | } 79 | 80 | #[inline(always)] 81 | pub fn k_str_out(s: &str) { 82 | k_str_out_raw(s.as_bytes()) 83 | } 84 | 85 | #[inline(always)] 86 | pub fn k_uptime_ticks() -> crate::Ticks { 87 | unsafe { crate::Ticks(zephyr_sys::syscalls::$context::k_uptime_ticks()) } 88 | } 89 | 90 | #[inline(always)] 91 | pub fn k_sleep(timeout: crate::Timeout) -> crate::DurationMs { 92 | unsafe { crate::DurationMs::from(zephyr_sys::syscalls::$context::k_sleep(timeout.0)) } 93 | } 94 | 95 | #[inline(always)] 96 | pub fn k_thread_custom_data_get() -> *mut u8 { 97 | unsafe { zephyr_sys::syscalls::$context::k_thread_custom_data_get() as *mut u8 } 98 | } 99 | 100 | #[inline(always)] 101 | pub fn k_thread_custom_data_set(value: *mut u8) { 102 | unsafe { zephyr_sys::syscalls::$context::k_thread_custom_data_set(value as *mut _) }; 103 | } 104 | 105 | #[cfg(clock)] 106 | #[inline(always)] 107 | pub fn clock_settime(timespec: zephyr_sys::raw::timespec) { 108 | use core::convert::TryInto; 109 | 110 | unsafe { 111 | zephyr_sys::raw::clock_settime( 112 | zephyr_sys::raw::CLOCK_REALTIME.try_into().unwrap(), 113 | ×pec, 114 | ); 115 | } 116 | } 117 | 118 | #[cfg(clock)] 119 | #[inline(always)] 120 | pub fn clock_gettime() -> zephyr_sys::raw::timespec { 121 | use core::convert::TryInto; 122 | 123 | unsafe { 124 | let mut t: zephyr_sys::raw::timespec = core::mem::zeroed(); 125 | zephyr_sys::syscalls::$context::clock_gettime( 126 | zephyr_sys::raw::CLOCK_REALTIME.try_into().unwrap(), 127 | &mut t, 128 | ); 129 | t 130 | } 131 | } 132 | 133 | impl crate::mutex::MutexSyscalls for $context_struct { 134 | unsafe fn k_mutex_init(mutex: *mut zephyr_sys::raw::k_mutex) { 135 | zephyr_sys::syscalls::$context::k_mutex_init(mutex); 136 | } 137 | 138 | unsafe fn k_mutex_lock( 139 | mutex: *mut zephyr_sys::raw::k_mutex, 140 | timeout: zephyr_sys::raw::k_timeout_t, 141 | ) -> libc::c_int { 142 | zephyr_sys::syscalls::$context::k_mutex_lock(mutex, timeout) 143 | } 144 | 145 | unsafe fn k_mutex_unlock(mutex: *mut zephyr_sys::raw::k_mutex) { 146 | // TODO: return the error from here. Ignoring now for Zephyr 2.1 compat 147 | zephyr_sys::syscalls::$context::k_mutex_unlock(mutex); 148 | } 149 | } 150 | }; 151 | } 152 | 153 | /// Functions only accessible from kernel mode 154 | pub mod kernel { 155 | use core::alloc::{GlobalAlloc, Layout}; 156 | use core::ptr; 157 | use libc::c_void; 158 | 159 | zephyr_bindings!(kernel, crate::context::Kernel); 160 | 161 | pub fn k_thread_user_mode_enter(mut f: F) -> ! 162 | where 163 | F: FnOnce() + Send + Sync, 164 | { 165 | extern "C" fn run_closure(p1: *mut c_void, _p2: *mut c_void, _p3: *mut c_void) 166 | where 167 | F: FnOnce() + Send + Sync, 168 | { 169 | let f = unsafe { ptr::read(p1 as *mut F) }; 170 | f(); 171 | } 172 | unsafe { 173 | zephyr_sys::raw::k_thread_user_mode_enter( 174 | Some(run_closure::), 175 | &mut f as *mut _ as *mut c_void, 176 | ptr::null_mut(), 177 | ptr::null_mut(), 178 | ) 179 | } 180 | } 181 | 182 | fn check_align(ptr: *mut u8, layout: Layout) -> *mut u8 { 183 | if ptr as usize & (layout.align() - 1) != 0 { 184 | unsafe { 185 | zephyr_sys::raw::printk( 186 | "Rust unsatisfied alloc alignment\n\0".as_ptr() as *const libc::c_char 187 | ); 188 | } 189 | core::ptr::null_mut() 190 | } else { 191 | ptr 192 | } 193 | } 194 | 195 | pub struct KMalloc; 196 | 197 | unsafe impl GlobalAlloc for KMalloc { 198 | #[inline] 199 | unsafe fn alloc(&self, layout: Layout) -> *mut u8 { 200 | #[cfg(not(zephyr250))] 201 | let ret = zephyr_sys::raw::k_malloc(layout.size()) as *mut _; 202 | #[cfg(zephyr250)] 203 | let ret = zephyr_sys::raw::k_aligned_alloc(layout.align(), layout.size()) as *mut _; 204 | 205 | check_align(ret, layout) 206 | } 207 | 208 | #[inline] 209 | unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { 210 | let ret = zephyr_sys::raw::k_calloc(1, layout.size()) as *mut _; 211 | check_align(ret, layout) 212 | } 213 | 214 | #[inline] 215 | unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) { 216 | zephyr_sys::raw::k_free(ptr as *mut _) 217 | } 218 | } 219 | } 220 | 221 | pub mod user { 222 | zephyr_bindings!(user, crate::context::User); 223 | } 224 | 225 | pub mod any { 226 | zephyr_bindings!(any, crate::context::Any); 227 | } 228 | -------------------------------------------------------------------------------- /rust/zephyr-core/src/memdomain.rs: -------------------------------------------------------------------------------- 1 | #[cfg(not(usermode))] 2 | use core::marker::PhantomData; 3 | 4 | pub use zephyr_sys::raw::k_mem_domain; 5 | 6 | use crate::thread::ThreadId; 7 | 8 | #[cfg(usermode)] 9 | pub struct MemDomain<'a>(&'a k_mem_domain); 10 | #[cfg(not(usermode))] 11 | pub struct MemDomain<'a>(PhantomData<&'a ()>); 12 | 13 | impl<'a> MemDomain<'a> { 14 | #[cfg(usermode)] 15 | pub unsafe fn new(domain: &'a k_mem_domain) -> Self { 16 | MemDomain(domain) 17 | } 18 | #[cfg(not(usermode))] 19 | pub fn new() -> Self { 20 | MemDomain(PhantomData) 21 | } 22 | 23 | pub fn add_thread(&self, _thread: ThreadId) { 24 | #[cfg(usermode)] 25 | C::k_mem_domain_add_thread(self.0, _thread) 26 | } 27 | } 28 | 29 | pub trait MemDomainAPI { 30 | fn k_mem_domain_add_thread(domain: &k_mem_domain, thread: ThreadId); 31 | } 32 | 33 | impl MemDomainAPI for crate::context::Kernel { 34 | fn k_mem_domain_add_thread(domain: &k_mem_domain, thread: ThreadId) { 35 | unsafe { 36 | zephyr_sys::raw::k_mem_domain_add_thread(domain as *const _ as *mut _, thread.tid()); 37 | } 38 | } 39 | } 40 | 41 | /// Get a static reference to an external mem domain 42 | #[cfg(usermode)] 43 | #[macro_export] 44 | macro_rules! static_mem_domain { 45 | ($domain:ident) => {{ 46 | extern "C" { 47 | #[no_mangle] 48 | static $domain: $crate::memdomain::k_mem_domain; 49 | } 50 | 51 | unsafe { $crate::memdomain::MemDomain::new(&$domain) } 52 | }}; 53 | } 54 | /// Get a static reference to an external mem domain 55 | #[cfg(not(usermode))] 56 | #[macro_export] 57 | macro_rules! static_mem_domain { 58 | ($domain:ident) => {{ 59 | $crate::memdomain::MemDomain::new() 60 | }}; 61 | } 62 | -------------------------------------------------------------------------------- /rust/zephyr-core/src/mempool.rs: -------------------------------------------------------------------------------- 1 | use core::alloc::{GlobalAlloc, Layout}; 2 | 3 | pub use zephyr_sys::raw::k_heap; 4 | use zephyr_sys::raw::K_NO_WAIT; 5 | 6 | pub struct MempoolAlloc(pub &'static k_heap); 7 | 8 | unsafe impl Send for MempoolAlloc {} 9 | unsafe impl Sync for MempoolAlloc {} 10 | 11 | unsafe impl GlobalAlloc for MempoolAlloc { 12 | unsafe fn alloc(&self, layout: Layout) -> *mut u8 { 13 | let kheap = self.0 as *const _ as *mut _; 14 | #[cfg(zephyr250)] 15 | { 16 | zephyr_sys::raw::k_heap_aligned_alloc(kheap, layout.align(), layout.size(), K_NO_WAIT) as *mut _ 17 | } 18 | #[cfg(not(zephyr250))] 19 | { 20 | let ret = zephyr_sys::raw::k_heap_alloc(kheap, layout.size(), K_NO_WAIT) as *mut _; 21 | if ret as usize & (layout.align() - 1) != 0 { 22 | zephyr_sys::raw::printk( 23 | "Rust unsatisfied alloc alignment\n\0".as_ptr() as *const libc::c_char 24 | ); 25 | core::ptr::null_mut() 26 | } else { 27 | ret 28 | } 29 | } 30 | } 31 | 32 | unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) { 33 | let kheap = self.0 as *const _ as *mut _; 34 | zephyr_sys::raw::k_heap_free(kheap, ptr as *mut _) 35 | } 36 | } 37 | 38 | /// Assign a Zephyr k_heap as #[global_allocator] 39 | /// 40 | /// This should be defined with K_HEAP_DEFINE and granted permission to any 41 | /// Rust threads that need to use libstd or alloc. 42 | #[macro_export] 43 | macro_rules! global_sys_mem_pool { 44 | ($pool:ident) => { 45 | extern "C" { 46 | static $pool: $crate::mempool::k_heap; 47 | } 48 | 49 | #[global_allocator] 50 | static GLOBAL: $crate::mempool::MempoolAlloc = 51 | $crate::mempool::MempoolAlloc(unsafe { &$pool }); 52 | }; 53 | } 54 | -------------------------------------------------------------------------------- /rust/zephyr-core/src/mutex.rs: -------------------------------------------------------------------------------- 1 | use core::cell::UnsafeCell; 2 | use core::marker::PhantomData; 3 | use core::ops::{Deref, DerefMut}; 4 | 5 | use zephyr_sys::raw::{k_mutex, k_objects}; 6 | 7 | use super::NegErr; 8 | use crate::kobj::*; 9 | 10 | // Declare the Zephyr struct to be a kernel object 11 | unsafe impl KObj for k_mutex { 12 | const OTYPE: k_objects = zephyr_sys::raw::k_objects_K_OBJ_MUTEX; 13 | } 14 | 15 | pub use zephyr_sys::raw::k_mutex as KMutex; 16 | 17 | crate::make_static_wrapper!(k_mutex, zephyr_sys::raw::k_mutex); 18 | 19 | /// Raw syscall API 20 | pub trait MutexSyscalls { 21 | unsafe fn k_mutex_init(mutex: *mut zephyr_sys::raw::k_mutex); 22 | unsafe fn k_mutex_lock( 23 | mutex: *mut zephyr_sys::raw::k_mutex, 24 | timeout: zephyr_sys::raw::k_timeout_t, 25 | ) -> libc::c_int; 26 | unsafe fn k_mutex_unlock(mutex: *mut zephyr_sys::raw::k_mutex); 27 | } 28 | 29 | /// Safer API implemented for the mutex kobject. 30 | /// 31 | /// Still not safe because it doesn't implement a lock guard. 32 | pub trait RawMutex { 33 | unsafe fn init(self); 34 | unsafe fn lock(self); 35 | unsafe fn unlock(self); 36 | unsafe fn try_lock(self) -> bool; 37 | } 38 | 39 | impl<'a> RawMutex for &'a KMutex { 40 | #[inline] 41 | unsafe fn init(self) { 42 | C::k_mutex_init(self as *const _ as *mut _) 43 | } 44 | 45 | unsafe fn lock(self) { 46 | C::k_mutex_lock(self as *const _ as *mut _, zephyr_sys::raw::K_FOREVER) 47 | .neg_err() 48 | .expect("mutex lock"); 49 | } 50 | 51 | unsafe fn unlock(self) { 52 | C::k_mutex_unlock(self as *const _ as *mut _); 53 | } 54 | 55 | unsafe fn try_lock(self) -> bool { 56 | match C::k_mutex_lock(self as *const _ as *mut _, zephyr_sys::raw::K_NO_WAIT).neg_err() { 57 | Ok(_) => Ok(true), 58 | Err(zephyr_sys::raw::EBUSY) => Ok(false), 59 | Err(e) => Err(e), 60 | } 61 | .expect("mutex try_lock") 62 | } 63 | } 64 | 65 | /// Safe mutex container like that in std 66 | /// 67 | /// Using this is safe, but creating it is not. Creator must ensure it is not 68 | /// possible to get a reference to the data elsewhere. Lifetime bounds ensure the 69 | /// mutex kobject lives at least as long as the data it protects. 70 | pub struct Mutex<'m, T> { 71 | mutex: &'m KMutex, 72 | data: MutexData, 73 | } 74 | 75 | impl<'m, T> Mutex<'m, T> { 76 | pub const unsafe fn new(mutex: &'m KMutex, data: T) -> Self { 77 | Mutex { 78 | mutex, 79 | data: MutexData::new(data), 80 | } 81 | } 82 | 83 | pub unsafe fn kobj(&self) -> *mut libc::c_void { 84 | self.mutex as *const _ as *mut _ 85 | } 86 | 87 | pub fn lock<'a, C: MutexSyscalls>(&'a self) -> MutexGuard<'a, T, C> { 88 | unsafe { 89 | self.mutex.lock::(); 90 | } 91 | MutexGuard { 92 | mutex: self, 93 | _syscalls: PhantomData, 94 | } 95 | } 96 | } 97 | 98 | /// Allow cloning a mutex where the data is a reference. This allows multiple references to static 99 | /// data with a static lock without wrapping those references in another Arc layer. 100 | impl<'m, 'd, T> Clone for Mutex<'m, &'d T> { 101 | fn clone(&self) -> Self { 102 | Mutex { 103 | mutex: self.mutex, 104 | data: unsafe { MutexData::new(&*self.data.0.get()) }, 105 | } 106 | } 107 | } 108 | 109 | pub struct MutexGuard<'a, T: 'a, C: MutexSyscalls> { 110 | mutex: &'a Mutex<'a, T>, 111 | _syscalls: PhantomData, 112 | } 113 | 114 | impl<'a, T: 'a, C: MutexSyscalls> Drop for MutexGuard<'a, T, C> { 115 | fn drop(&mut self) { 116 | unsafe { self.mutex.mutex.unlock::() } 117 | } 118 | } 119 | 120 | impl<'a, T: 'a, C: MutexSyscalls> Deref for MutexGuard<'a, T, C> { 121 | type Target = T; 122 | 123 | fn deref(&self) -> &T { 124 | unsafe { &*self.mutex.data.0.get() } 125 | } 126 | } 127 | 128 | impl<'a, T: 'a, C: MutexSyscalls> DerefMut for MutexGuard<'a, T, C> { 129 | fn deref_mut(&mut self) -> &mut T { 130 | unsafe { &mut *self.mutex.data.0.get() } 131 | } 132 | } 133 | 134 | pub struct MutexData(UnsafeCell); 135 | 136 | unsafe impl Sync for MutexData {} 137 | 138 | impl MutexData { 139 | pub const fn new(data: T) -> Self { 140 | MutexData(UnsafeCell::new(data)) 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /rust/zephyr-core/src/mutex_alloc.rs: -------------------------------------------------------------------------------- 1 | #[cfg(not(mutex_pool))] 2 | use alloc::boxed::Box; 3 | use core::ops::Deref; 4 | use core::ptr::NonNull; 5 | 6 | use crate::mutex::*; 7 | 8 | pub struct DynMutex(NonNull); 9 | 10 | impl DynMutex { 11 | pub fn new() -> Option { 12 | unsafe { 13 | #[cfg(not(mutex_pool))] 14 | let m = { 15 | let m = Box::into_raw(Box::new(**crate::mutex::global::k_mutex::uninit())); 16 | (*m).init::(); 17 | m 18 | }; 19 | #[cfg(mutex_pool)] 20 | let m = mutex_pool::alloc_mutex().expect("mutex pool exhausted"); 21 | 22 | Some(DynMutex(NonNull::new_unchecked(m))) 23 | } 24 | } 25 | 26 | pub fn into_raw(self) -> *mut KMutex { 27 | let ptr = self.0.as_ptr(); 28 | core::mem::forget(self); 29 | ptr 30 | } 31 | 32 | pub unsafe fn from_raw(m: *mut KMutex) -> Self { 33 | DynMutex(NonNull::new_unchecked(m)) 34 | } 35 | } 36 | 37 | impl Drop for DynMutex { 38 | fn drop(&mut self) { 39 | unsafe { 40 | #[cfg(not(mutex_pool))] 41 | Box::from_raw(self.0.as_mut()); 42 | #[cfg(mutex_pool)] 43 | mutex_pool::free_mutex(self.0.as_mut()); 44 | } 45 | } 46 | } 47 | 48 | impl Deref for DynMutex { 49 | type Target = KMutex; 50 | 51 | fn deref(&self) -> &KMutex { 52 | unsafe { self.0.as_ref() } 53 | } 54 | } 55 | 56 | #[cfg(mutex_pool)] 57 | mod mutex_pool { 58 | use crate::mutex::*; 59 | use core::sync::atomic::{AtomicU8, Ordering}; 60 | 61 | const NUM_MUTEX: usize = zephyr_sys::raw::CONFIG_RUST_MUTEX_POOL_SIZE as usize; 62 | 63 | extern "C" { 64 | #[allow(improper_ctypes)] 65 | static rust_mutex_pool: [KMutex; NUM_MUTEX]; 66 | } 67 | 68 | const NUM_USED: usize = (NUM_MUTEX + 7) / 8; 69 | /// Bitfield tracking allocated mutexes 70 | static USED: [AtomicU8; NUM_USED] = unsafe { core::mem::transmute([0u8; NUM_USED]) }; 71 | 72 | pub fn alloc_mutex() -> Option<*mut KMutex> { 73 | let mut ret = None; 74 | 75 | for (i, byte) in USED.iter().enumerate() { 76 | // Valid bits in this byte 77 | let valid = core::cmp::max(NUM_MUTEX - i * 8, 8); 78 | if byte 79 | .fetch_update(Ordering::Relaxed, Ordering::Relaxed, |val| { 80 | ret = None; 81 | for bit in 0..valid { 82 | let mask = 1 << bit; 83 | if val & mask == 0 { 84 | unsafe { 85 | ret = Some(&rust_mutex_pool[i * 8 + bit] as *const _ as *mut _) 86 | }; 87 | return Some(val | mask); 88 | } 89 | } 90 | None 91 | }) 92 | .is_ok() 93 | { 94 | break; 95 | } 96 | } 97 | 98 | ret 99 | } 100 | 101 | pub fn free_mutex(mutex: *mut KMutex) { 102 | let index = unsafe { mutex.offset_from(&rust_mutex_pool[0] as *const _) } as usize; 103 | let byte = index / 8; 104 | let bit = index % 8; 105 | USED[byte].fetch_and(!(1 << bit), Ordering::Relaxed); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /rust/zephyr-core/src/poll.rs: -------------------------------------------------------------------------------- 1 | use libc::{c_int, c_void}; 2 | use zephyr_sys::raw::{ 3 | _poll_types_bits__POLL_TYPE_SEM_AVAILABLE, _poll_types_bits__POLL_TYPE_SIGNAL, k_poll_event, 4 | k_poll_modes_K_POLL_MODE_NOTIFY_ONLY, k_timeout_t, K_POLL_STATE_NOT_READY, K_POLL_TYPE_IGNORE, 5 | }; 6 | 7 | use crate::kobj::*; 8 | use crate::semaphore::KSem; 9 | use crate::time::Timeout; 10 | use crate::NegErr; 11 | 12 | pub use crate::poll_signal::*; 13 | 14 | pub type KPollEvent = k_poll_event; 15 | 16 | pub unsafe trait PollableKobj: KObj { 17 | const POLL_TYPE: u32; 18 | } 19 | 20 | unsafe impl PollableKobj for KSem { 21 | const POLL_TYPE: u32 = 1 << (_poll_types_bits__POLL_TYPE_SEM_AVAILABLE - 1); 22 | } 23 | 24 | unsafe impl PollableKobj for KPollSignal { 25 | const POLL_TYPE: u32 = 1 << (_poll_types_bits__POLL_TYPE_SIGNAL - 1); 26 | } 27 | 28 | #[repr(u32)] 29 | pub enum PollMode { 30 | NotifyOnly = k_poll_modes_K_POLL_MODE_NOTIFY_ONLY, 31 | } 32 | 33 | pub trait PollEventFuncs { 34 | fn new() -> Self; 35 | 36 | fn init<'e, 'o: 'e, O: PollableKobj>(&'e mut self, kobj: &'o O, mode: PollMode); 37 | 38 | fn ready(&self) -> bool; 39 | 40 | fn obj(&self) -> *const c_void; 41 | } 42 | 43 | impl PollEventFuncs for KPollEvent { 44 | fn new() -> Self { 45 | unsafe { 46 | let mut event = core::mem::MaybeUninit::uninit(); 47 | zephyr_sys::raw::k_poll_event_init( 48 | event.as_mut_ptr(), 49 | K_POLL_TYPE_IGNORE, 50 | 0, 51 | 1 as *const c_void as *mut c_void, // Must not be null, but won't be touched by the kernel because type is IGNORE 52 | ); 53 | event.assume_init() 54 | } 55 | } 56 | 57 | fn init<'e, 'o: 'e, O: PollableKobj>(&'e mut self, kobj: &'o O, mode: PollMode) { 58 | unsafe { 59 | zephyr_sys::raw::k_poll_event_init( 60 | self, 61 | O::POLL_TYPE, 62 | mode as c_int, 63 | kobj.as_void_ptr(), 64 | ) 65 | } 66 | } 67 | 68 | fn ready(&self) -> bool { 69 | self.state() != K_POLL_STATE_NOT_READY 70 | } 71 | 72 | fn obj(&self) -> *const c_void { 73 | unsafe { self.__bindgen_anon_1.obj } 74 | } 75 | } 76 | 77 | pub trait PollSyscalls { 78 | fn k_poll(events: &mut [KPollEvent], timeout: k_timeout_t) -> c_int; 79 | } 80 | 81 | macro_rules! trait_impl { 82 | ($context:ident, $context_struct:path) => { 83 | impl PollSyscalls for $context_struct { 84 | fn k_poll(events: &mut [KPollEvent], timeout: k_timeout_t) -> c_int { 85 | unsafe { 86 | zephyr_sys::syscalls::$context::k_poll( 87 | events.as_mut_ptr(), 88 | events.len() as c_int, 89 | timeout, 90 | ) 91 | } 92 | } 93 | } 94 | }; 95 | } 96 | 97 | trait_impl!(kernel, crate::context::Kernel); 98 | trait_impl!(user, crate::context::User); 99 | trait_impl!(any, crate::context::Any); 100 | 101 | #[derive(Clone, Copy, Debug)] 102 | pub enum PollError { 103 | Canceled, 104 | } 105 | 106 | pub trait PollEventsFuncs { 107 | fn poll(&mut self) -> Result<(), PollError>; 108 | /// Returns true if events are ready, false if timeout. 109 | fn poll_timeout( 110 | &mut self, 111 | timeout: Option, 112 | ) -> Result; 113 | } 114 | 115 | impl PollEventsFuncs for [KPollEvent] { 116 | fn poll(&mut self) -> Result<(), PollError> { 117 | match C::k_poll(self, zephyr_sys::raw::K_FOREVER).neg_err() { 118 | Ok(_) => Ok(()), 119 | Err(zephyr_sys::raw::EINTR) => Err(PollError::Canceled), 120 | Err(zephyr_sys::raw::ENOMEM) => panic!("k_poll OOM"), 121 | Err(e) => panic!("k_poll error {}", e), 122 | } 123 | } 124 | 125 | fn poll_timeout( 126 | &mut self, 127 | timeout: Option, 128 | ) -> Result { 129 | let timeout = timeout.map(|x| x.0).unwrap_or(zephyr_sys::raw::K_FOREVER); 130 | match C::k_poll(self, timeout).neg_err() { 131 | Ok(_) => Ok(true), 132 | Err(zephyr_sys::raw::EAGAIN) => Ok(false), 133 | Err(zephyr_sys::raw::EINTR) => Err(PollError::Canceled), 134 | Err(zephyr_sys::raw::ENOMEM) => panic!("k_poll OOM"), 135 | Err(e) => panic!("k_poll error {}", e), 136 | } 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /rust/zephyr-core/src/poll_signal.rs: -------------------------------------------------------------------------------- 1 | use libc::{c_int, c_uint}; 2 | 3 | use crate::kobj::*; 4 | 5 | use zephyr_sys::raw::k_objects; 6 | pub use zephyr_sys::raw::k_poll_signal as KPollSignal; 7 | 8 | unsafe impl KObj for KPollSignal { 9 | const OTYPE: k_objects = zephyr_sys::raw::k_objects_K_OBJ_POLL_SIGNAL; 10 | } 11 | 12 | crate::make_static_wrapper!(k_poll_signal, zephyr_sys::raw::k_poll_signal); 13 | 14 | pub trait KPollSignalSyscalls { 15 | unsafe fn k_poll_signal_init(signal: &KPollSignal); 16 | fn k_poll_signal_reset(signal: &KPollSignal); 17 | fn k_poll_signal_check(signal: &KPollSignal, signaled: &mut c_uint, result: &mut c_int); 18 | fn k_poll_signal_raise(signal: &KPollSignal, result: c_int) -> c_int; 19 | } 20 | 21 | macro_rules! trait_impl { 22 | ($context:ident, $context_struct:path) => { 23 | impl KPollSignalSyscalls for $context_struct { 24 | unsafe fn k_poll_signal_init(signal: &KPollSignal) { 25 | zephyr_sys::syscalls::$context::k_poll_signal_init(signal as *const _ as *mut _) 26 | } 27 | 28 | fn k_poll_signal_reset(signal: &KPollSignal) { 29 | unsafe { 30 | zephyr_sys::syscalls::$context::k_poll_signal_reset( 31 | signal as *const _ as *mut _, 32 | ) 33 | } 34 | } 35 | 36 | fn k_poll_signal_check( 37 | signal: &KPollSignal, 38 | signaled: &mut c_uint, 39 | result: &mut c_int, 40 | ) { 41 | unsafe { 42 | zephyr_sys::syscalls::$context::k_poll_signal_check( 43 | signal as *const _ as *mut _, 44 | signaled, 45 | result, 46 | ) 47 | } 48 | } 49 | 50 | fn k_poll_signal_raise(signal: &KPollSignal, result: c_int) -> c_int { 51 | unsafe { 52 | zephyr_sys::syscalls::$context::k_poll_signal_raise( 53 | signal as *const _ as *mut _, 54 | result, 55 | ) 56 | } 57 | } 58 | } 59 | }; 60 | } 61 | 62 | trait_impl!(kernel, crate::context::Kernel); 63 | trait_impl!(user, crate::context::User); 64 | trait_impl!(any, crate::context::Any); 65 | 66 | pub trait Signal { 67 | unsafe fn init(&self); 68 | fn reset(&self); 69 | fn check(&self) -> Option; 70 | fn raise(&self, result: c_int); 71 | } 72 | 73 | impl Signal for KPollSignal { 74 | unsafe fn init(&self) { 75 | C::k_poll_signal_init(self) 76 | } 77 | 78 | fn reset(&self) { 79 | C::k_poll_signal_reset(self) 80 | } 81 | 82 | fn check(&self) -> Option { 83 | let mut signaled = 0; 84 | let mut result = 0; 85 | C::k_poll_signal_check(self, &mut signaled, &mut result); 86 | if signaled != 0 { 87 | Some(result) 88 | } else { 89 | None 90 | } 91 | } 92 | 93 | fn raise(&self, result: c_int) { 94 | C::k_poll_signal_raise(self, result); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /rust/zephyr-core/src/semaphore.rs: -------------------------------------------------------------------------------- 1 | use zephyr_sys::raw::{k_objects, k_sem, k_timeout_t}; 2 | 3 | use super::NegErr; 4 | use crate::kobj::*; 5 | use crate::time::Timeout; 6 | 7 | // Declare the Zephyr struct to be a kernel object 8 | unsafe impl KObj for k_sem { 9 | const OTYPE: k_objects = zephyr_sys::raw::k_objects_K_OBJ_SEM; 10 | } 11 | 12 | pub use zephyr_sys::raw::k_sem as KSem; 13 | 14 | crate::make_static_wrapper!(k_sem, zephyr_sys::raw::k_sem); 15 | 16 | /// Raw syscall API 17 | pub trait SemaphoreSyscalls { 18 | unsafe fn k_sem_init(sem: &k_sem, initial_count: libc::c_uint, limit: libc::c_uint); 19 | fn k_sem_take(sem: &k_sem, timeout: k_timeout_t) -> libc::c_int; 20 | fn k_sem_give(sem: &k_sem); 21 | fn k_sem_reset(sem: &k_sem); 22 | fn k_sem_count_get(sem: &k_sem) -> libc::c_uint; 23 | } 24 | 25 | macro_rules! trait_impl { 26 | ($context:ident, $context_struct:path) => { 27 | impl SemaphoreSyscalls for $context_struct { 28 | unsafe fn k_sem_init(sem: &k_sem, initial_count: libc::c_uint, limit: libc::c_uint) { 29 | zephyr_sys::syscalls::$context::k_sem_init( 30 | sem as *const _ as *mut _, 31 | initial_count, 32 | limit, 33 | ); // TODO: return the error from here. Ignoring now for Zephyr 2.1 compat 34 | } 35 | 36 | fn k_sem_take(sem: &k_sem, timeout: k_timeout_t) -> libc::c_int { 37 | unsafe { 38 | zephyr_sys::syscalls::$context::k_sem_take(sem as *const _ as *mut _, timeout) 39 | } 40 | } 41 | 42 | fn k_sem_give(sem: &k_sem) { 43 | unsafe { zephyr_sys::syscalls::$context::k_sem_give(sem as *const _ as *mut _) } 44 | } 45 | 46 | fn k_sem_reset(sem: &k_sem) { 47 | unsafe { zephyr_sys::syscalls::$context::k_sem_reset(sem as *const _ as *mut _) } 48 | } 49 | 50 | fn k_sem_count_get(sem: &k_sem) -> libc::c_uint { 51 | unsafe { 52 | zephyr_sys::syscalls::$context::k_sem_count_get(sem as *const _ as *mut _) 53 | } 54 | } 55 | } 56 | }; 57 | } 58 | 59 | trait_impl!(kernel, crate::context::Kernel); 60 | trait_impl!(user, crate::context::User); 61 | trait_impl!(any, crate::context::Any); 62 | 63 | /// Safe API implemented on the sem struct. Converts errors. 64 | pub trait Semaphore { 65 | unsafe fn init(&self, initial_count: u32, limit: u32); 66 | /// Take with infinite timeout 67 | fn take(&self); 68 | /// Take with timeout. Returns true if successful. False if timeout. 69 | fn take_timeout(&self, timeout: Timeout) -> bool; 70 | /// Take with no timeout. Returns true if successful. 71 | fn try_take(&self) -> bool; 72 | fn give(&self); 73 | fn reset(&self); 74 | fn count(&self) -> u32; 75 | } 76 | 77 | impl Semaphore for k_sem { 78 | unsafe fn init(&self, initial_count: u32, limit: u32) { 79 | C::k_sem_init(&self, initial_count, limit) 80 | } 81 | 82 | fn take(&self) { 83 | C::k_sem_take(self, zephyr_sys::raw::K_FOREVER.into()) 84 | .neg_err() 85 | .expect("sem take"); 86 | } 87 | 88 | fn take_timeout(&self, timeout: Timeout) -> bool { 89 | match C::k_sem_take(self, timeout.0).neg_err() { 90 | Ok(_) => Ok(true), 91 | Err(zephyr_sys::raw::EBUSY) => Ok(false), 92 | Err(zephyr_sys::raw::EAGAIN) => Ok(false), 93 | Err(e) => Err(e), 94 | } 95 | .expect("sem take") 96 | } 97 | 98 | fn try_take(&self) -> bool { 99 | match C::k_sem_take(self, (zephyr_sys::raw::K_NO_WAIT).into()).neg_err() { 100 | Ok(_) => Ok(true), 101 | Err(zephyr_sys::raw::EBUSY) => Ok(false), 102 | Err(e) => Err(e), 103 | } 104 | .expect("sem take") 105 | } 106 | 107 | fn give(&self) { 108 | C::k_sem_give(self) 109 | } 110 | 111 | fn reset(&self) { 112 | C::k_sem_reset(self) 113 | } 114 | 115 | fn count(&self) -> u32 { 116 | // .into() will fail to compile on platforms where uint != u32 117 | // can do a conversion if that case ever occurs 118 | C::k_sem_count_get(self).into() 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /rust/zephyr-core/src/thread.rs: -------------------------------------------------------------------------------- 1 | use core::ptr::NonNull; 2 | 3 | use crate::kobj::KObj; 4 | 5 | #[derive(Clone, Copy, Debug, PartialEq)] 6 | pub struct ThreadId(NonNull); 7 | 8 | impl ThreadId { 9 | pub fn tid(&self) -> zephyr_sys::raw::k_tid_t { 10 | self.0.as_ptr() 11 | } 12 | 13 | pub fn k_thread_suspend(&self) { 14 | C::k_thread_suspend(*self) 15 | } 16 | 17 | pub fn k_wakeup(&self) { 18 | C::k_wakeup(*self) 19 | } 20 | 21 | pub fn k_object_access_grant(&self, kobj: &K) { 22 | C::k_object_access_grant(kobj, *self) 23 | } 24 | } 25 | 26 | pub trait ThreadSyscalls { 27 | fn k_thread_suspend(thread: ThreadId); 28 | fn k_wakeup(thread: ThreadId); 29 | fn k_current_get() -> crate::thread::ThreadId; 30 | fn k_object_access_grant(kobj: &K, thread: ThreadId); 31 | } 32 | 33 | macro_rules! trait_impl { 34 | ($context:ident, $context_struct:path) => { 35 | impl ThreadSyscalls for $context_struct { 36 | fn k_thread_suspend(thread: ThreadId) { 37 | unsafe { zephyr_sys::syscalls::$context::k_thread_suspend(thread.tid()) } 38 | } 39 | 40 | fn k_wakeup(thread: ThreadId) { 41 | unsafe { zephyr_sys::syscalls::$context::k_wakeup(thread.tid()) } 42 | } 43 | 44 | /* If less than 2.7, always use the k_current_get syscall */ 45 | #[cfg(not(zephyr270))] 46 | fn k_current_get() -> crate::thread::ThreadId { 47 | ThreadId(unsafe { 48 | NonNull::new_unchecked(zephyr_sys::syscalls::$context::k_current_get()) 49 | }) 50 | } 51 | 52 | /* 2.7 and above with TLS allows pulling from TLS */ 53 | #[cfg(all(zephyr270, tls))] 54 | fn k_current_get() -> crate::thread::ThreadId { 55 | extern "C" { 56 | #[no_mangle] 57 | static z_tls_current: *mut zephyr_sys::raw::k_thread; 58 | } 59 | ThreadId(unsafe { 60 | NonNull::new_unchecked(z_tls_current) 61 | }) 62 | } 63 | 64 | /* 2.7 without TLS renames the syscall to z_current_get */ 65 | #[cfg(all(zephyr270, not(zephyr350), not(tls)))] 66 | fn k_current_get() -> crate::thread::ThreadId { 67 | ThreadId(unsafe { 68 | NonNull::new_unchecked(zephyr_sys::syscalls::$context::z_current_get()) 69 | }) 70 | } 71 | 72 | /* 3.5 renames the syscall again to k_sched_current_thread_query */ 73 | #[cfg(all(zephyr350, not(tls)))] 74 | fn k_current_get() -> crate::thread::ThreadId { 75 | ThreadId(unsafe { 76 | NonNull::new_unchecked(zephyr_sys::syscalls::$context::k_sched_current_thread_query()) 77 | }) 78 | } 79 | 80 | fn k_object_access_grant(kobj: &K, thread: ThreadId) { 81 | if !zephyr_sys::raw::RUST_CONFIG_USERSPACE { 82 | // Avoid unnecessary call to stub function 83 | return; 84 | } 85 | unsafe { 86 | zephyr_sys::syscalls::$context::k_object_access_grant( 87 | kobj.as_void_ptr(), 88 | thread.tid(), 89 | ); 90 | } 91 | } 92 | } 93 | }; 94 | } 95 | 96 | trait_impl!(kernel, crate::context::Kernel); 97 | trait_impl!(user, crate::context::User); 98 | trait_impl!(any, crate::context::Any); 99 | -------------------------------------------------------------------------------- /rust/zephyr-core/src/time.rs: -------------------------------------------------------------------------------- 1 | use core::time::Duration; 2 | 3 | use time_convert::z_tmcvt; 4 | use zephyr_sys::raw::{k_ticks_t, k_timeout_t, Z_HZ_ticks}; 5 | 6 | #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash, From)] 7 | pub struct Ticks(pub k_ticks_t); 8 | 9 | impl Ticks { 10 | pub fn checked_add(self, rhs: Self) -> Option { 11 | self.0.checked_add(rhs.0).map(Into::into) 12 | } 13 | 14 | pub fn checked_sub(self, rhs: Self) -> Option { 15 | self.0.checked_sub(rhs.0).map(Into::into) 16 | } 17 | 18 | pub fn checked_add_duration(self, other: &Duration) -> Option { 19 | let other = other.into(); 20 | self.checked_add(other) 21 | } 22 | 23 | pub fn checked_sub_duration(self, other: &Duration) -> Option { 24 | let other = other.into(); 25 | self.checked_sub(other) 26 | } 27 | 28 | pub fn as_millis(&self) -> u64 { 29 | ticks_to_ms_near(self.0 as u64) 30 | } 31 | 32 | /// Subtract two tick instants. Return a Timeout suitable for use as a 33 | /// timeout parameter for Zephyr system calls. 34 | pub fn sub_timeout(self, rhs: Self) -> Timeout { 35 | if rhs.0 > self.0 { 36 | Ticks(0).into() 37 | } else { 38 | Ticks(self.0 - rhs.0).into() 39 | } 40 | } 41 | } 42 | 43 | impl From for Ticks { 44 | #[inline(always)] 45 | fn from(val: u64) -> Self { 46 | Ticks(val as k_ticks_t) 47 | } 48 | } 49 | 50 | impl From<&Duration> for Ticks { 51 | #[inline(always)] 52 | fn from(dur: &Duration) -> Self { 53 | Ticks(secs_to_ticks(dur.as_secs()).0 + ns_to_ticks(dur.subsec_nanos().into()).0) 54 | } 55 | } 56 | 57 | impl From for Duration { 58 | #[inline(always)] 59 | fn from(ticks: Ticks) -> Self { 60 | Duration::from_nanos(ticks_to_ns_near(ticks.0 as u64)) 61 | } 62 | } 63 | 64 | #[derive(Clone, Copy, Debug)] 65 | pub struct Timeout(pub k_timeout_t); 66 | 67 | impl From for Timeout { 68 | #[inline(always)] 69 | fn from(ticks: Ticks) -> Self { 70 | Timeout(k_timeout_t { ticks: ticks.0 }) 71 | } 72 | } 73 | 74 | impl From for Ticks { 75 | #[inline(always)] 76 | fn from(timeout: Timeout) -> Self { 77 | Ticks(timeout.0.ticks) 78 | } 79 | } 80 | 81 | impl From<&Duration> for Timeout { 82 | fn from(dur: &Duration) -> Self { 83 | Ticks::from(dur).into() 84 | } 85 | } 86 | 87 | impl From for Duration { 88 | #[inline(always)] 89 | fn from(timeout: Timeout) -> Self { 90 | Ticks::from(timeout).into() 91 | } 92 | } 93 | 94 | pub const K_FOREVER: Timeout = Timeout(zephyr_sys::raw::K_FOREVER); 95 | pub const K_NO_WAIT: Timeout = Timeout(zephyr_sys::raw::K_NO_WAIT); 96 | 97 | #[allow(unused)] 98 | #[inline(always)] 99 | fn secs_to_ticks(val: u64) -> Ticks { 100 | z_tmcvt(val, 1, Z_HZ_ticks, true, true, false).into() 101 | } 102 | 103 | #[allow(unused)] 104 | #[inline(always)] 105 | fn ms_to_ticks(val: u64) -> Ticks { 106 | z_tmcvt(val, 1_000, Z_HZ_ticks, true, true, false).into() 107 | } 108 | 109 | #[allow(unused)] 110 | #[inline(always)] 111 | fn us_to_ticks(val: u64) -> Ticks { 112 | z_tmcvt(val, 1_000_000, Z_HZ_ticks, true, true, false).into() 113 | } 114 | 115 | #[allow(unused)] 116 | #[inline(always)] 117 | fn ns_to_ticks(val: u64) -> Ticks { 118 | z_tmcvt(val, 1_000_000_000, Z_HZ_ticks, true, true, false).into() 119 | } 120 | 121 | #[allow(unused)] 122 | #[inline(always)] 123 | fn ticks_to_secs_floor(val: u64) -> u64 { 124 | z_tmcvt(val, Z_HZ_ticks, 1, true, false, false) 125 | } 126 | 127 | #[allow(unused)] 128 | #[inline(always)] 129 | fn ticks_to_secs_ceil(val: u64) -> u64 { 130 | z_tmcvt(val, Z_HZ_ticks, 1, true, true, false) 131 | } 132 | 133 | #[allow(unused)] 134 | #[inline(always)] 135 | fn ticks_to_secs_near(val: u64) -> u64 { 136 | z_tmcvt(val, Z_HZ_ticks, 1, true, false, true) 137 | } 138 | 139 | #[allow(unused)] 140 | #[inline(always)] 141 | fn ticks_to_ms_floor(val: u64) -> u64 { 142 | z_tmcvt(val, Z_HZ_ticks, 1_000, true, false, false) 143 | } 144 | 145 | #[allow(unused)] 146 | #[inline(always)] 147 | fn ticks_to_ms_ceil(val: u64) -> u64 { 148 | z_tmcvt(val, Z_HZ_ticks, 1_000, true, true, false) 149 | } 150 | 151 | #[allow(unused)] 152 | #[inline(always)] 153 | fn ticks_to_ms_near(val: u64) -> u64 { 154 | z_tmcvt(val, Z_HZ_ticks, 1_000, true, false, true) 155 | } 156 | 157 | #[allow(unused)] 158 | #[inline(always)] 159 | fn ticks_to_us_floor(val: u64) -> u64 { 160 | z_tmcvt(val, Z_HZ_ticks, 1_000_000, true, false, false) 161 | } 162 | 163 | #[allow(unused)] 164 | #[inline(always)] 165 | fn ticks_to_us_ceil(val: u64) -> u64 { 166 | z_tmcvt(val, Z_HZ_ticks, 1_000_000, true, true, false) 167 | } 168 | 169 | #[allow(unused)] 170 | #[inline(always)] 171 | fn ticks_to_us_near(val: u64) -> u64 { 172 | z_tmcvt(val, Z_HZ_ticks, 1_000_000, true, false, true) 173 | } 174 | 175 | #[allow(unused)] 176 | #[inline(always)] 177 | fn ticks_to_ns_floor(val: u64) -> u64 { 178 | z_tmcvt(val, Z_HZ_ticks, 1_000_000_000, true, false, false) 179 | } 180 | 181 | #[allow(unused)] 182 | #[inline(always)] 183 | fn ticks_to_ns_ceil(val: u64) -> u64 { 184 | z_tmcvt(val, Z_HZ_ticks, 1_000_000_000, true, true, false) 185 | } 186 | 187 | #[allow(unused)] 188 | #[inline(always)] 189 | fn ticks_to_ns_near(val: u64) -> u64 { 190 | z_tmcvt(val, Z_HZ_ticks, 1_000_000_000, true, false, true) 191 | } 192 | 193 | /// 32-bit time in ms. Used for sleep return value. 194 | #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash, From, Into)] 195 | pub struct DurationMs(pub i32); 196 | -------------------------------------------------------------------------------- /rust/zephyr-core/time-convert/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "time-convert" 3 | version = "0.1.0" 4 | authors = ["Tyler Hall "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | core = { version = '1.0.0', optional = true, package = 'rustc-std-workspace-core' } 11 | compiler_builtins = { version = '0.1.2', optional = true } 12 | 13 | [features] 14 | rustc-dep-of-std = ['core', 'compiler_builtins/rustc-dep-of-std'] 15 | -------------------------------------------------------------------------------- /rust/zephyr-core/time-convert/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | 3 | /// Ported from z_tmcvt inline C code. 4 | #[inline(always)] 5 | pub fn z_tmcvt( 6 | mut t: u64, 7 | from_hz: u32, 8 | to_hz: u32, 9 | const_hz: bool, 10 | round_up: bool, 11 | round_off: bool, 12 | ) -> u64 { 13 | let mul_ratio = const_hz && (to_hz > from_hz) && ((to_hz % from_hz) == 0); 14 | let div_ratio = const_hz && (from_hz > to_hz) && ((from_hz % to_hz) == 0); 15 | 16 | if from_hz == to_hz { 17 | return t; 18 | } 19 | 20 | let mut off: u64 = 0; 21 | 22 | if !mul_ratio { 23 | let rdivisor: u32 = if div_ratio { from_hz / to_hz } else { from_hz }; 24 | let rdivisor: u64 = rdivisor.into(); 25 | 26 | if round_up { 27 | off = rdivisor - 1; 28 | } else if round_off { 29 | off = rdivisor / 2; 30 | } 31 | } 32 | 33 | /* Select (at build time!) between three different expressions for 34 | * the same mathematical relationship, each expressed with and 35 | * without truncation to 32 bits (I couldn't find a way to make 36 | * the compiler correctly guess at the 32 bit result otherwise). 37 | */ 38 | if div_ratio { 39 | t += off; 40 | t / u64::from(from_hz / to_hz) 41 | } else if mul_ratio { 42 | t * u64::from(to_hz / from_hz) 43 | } else { 44 | (t * u64::from(to_hz) + off) / u64::from(from_hz) 45 | } 46 | } 47 | 48 | #[test] 49 | fn test_z_tmcvt() { 50 | let hz_ms = 1000; 51 | let hz_s = 1; 52 | let hz_ns = 1_000_000_000; 53 | 54 | // 500ms to 100HZ ticks 55 | let hz_ticks = 100; 56 | assert_eq!(z_tmcvt(500, hz_ms, hz_ticks, true, true, false), 50); 57 | 58 | // 500ms to 1000HZ ticks 59 | let hz_ticks = 1000; 60 | assert_eq!(z_tmcvt(500, hz_ms, hz_ticks, true, true, false), 500); 61 | 62 | // 2.5s in ns to 1000HZ ticks 63 | assert_eq!(z_tmcvt(2_500_000_000, hz_ns, hz_ticks, true, true, false), 2500); 64 | 65 | // 2500 1000HZ ticks to ns 66 | assert_eq!(z_tmcvt(2500, hz_ticks, hz_ns, true, true, false), 2_500_000_000); 67 | } 68 | -------------------------------------------------------------------------------- /rust/zephyr-futures/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "zephyr-futures" 3 | version = "0.1.0" 4 | authors = ["Tyler Hall "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | zephyr = { path = "../zephyr" } 9 | log = "0.4" 10 | futures = "0.3.1" 11 | -------------------------------------------------------------------------------- /rust/zephyr-futures/src/delay.rs: -------------------------------------------------------------------------------- 1 | use std::future::Future; 2 | use std::pin::Pin; 3 | use std::task::{Context, Poll, Waker}; 4 | use std::time::{Duration, Instant}; 5 | 6 | use zephyr_core::{Ticks, Timeout}; 7 | 8 | #[derive(Debug)] 9 | pub struct Delay(Instant); 10 | 11 | impl Delay { 12 | pub fn new(dur: Duration) -> Self { 13 | Self(Instant::now() + dur) 14 | } 15 | 16 | pub fn new_at(instant: Instant) -> Self { 17 | Self(instant) 18 | } 19 | } 20 | 21 | impl Future for Delay { 22 | type Output = (); 23 | 24 | fn poll(self: Pin<&mut Self>, context: &mut Context) -> Poll { 25 | // Can we avoid a system call on every poll? 26 | if Instant::now() >= self.0 { 27 | Poll::Ready(()) 28 | } else { 29 | super::current_reactor_register_timer(self.0, context); 30 | Poll::Pending 31 | } 32 | } 33 | } 34 | 35 | /// Simple registry of tasks and their soonest expiring timer 36 | /// 37 | /// This is kept simple by storing the state of outstanding timers only in the `Delay`. Rather than 38 | /// keeping each delay registered with the reactor until it expires, only keep track of the next 39 | /// soonest deadline per task. When any deadline for a task expires, the task is polled, causing 40 | /// any relevant deadlines to be re-registered. It is not necessary for a Delay to unregister 41 | /// itself when it is dropped. A back reference to mark the delay expired is also not used. 42 | /// 43 | /// The tradeoff is slower polling. Each delay must compare itself against the system time and 44 | /// re-register on every poll if not expired. The `register` operation searches the list once per 45 | /// timer and the list size is up to the number of tasks. This scales poorly, but the assumption 46 | /// for Zephyr is that there will likely be no more than 5-10 each of tasks and timers. 47 | pub(super) struct TimerReactor { 48 | tasks: Vec<(Waker, Instant)>, 49 | } 50 | 51 | impl TimerReactor { 52 | pub fn new() -> Self { 53 | TimerReactor { tasks: Vec::new() } 54 | } 55 | 56 | pub fn register(&mut self, new_deadline: Instant, context: &mut Context) { 57 | let new_waker = context.waker(); 58 | if let Some((ref _waker, ref mut cur_deadline)) = self 59 | .tasks 60 | .iter_mut() 61 | .find(|(waker, _instant)| waker.will_wake(new_waker)) 62 | { 63 | // We already have this task registered. Update the expiration time if it's sooner. 64 | // Else we can ignore this, because the task will get woken earlier, the delay will get 65 | // polled again and this deadline will be re-registered. 66 | if *cur_deadline > new_deadline { 67 | *cur_deadline = new_deadline; 68 | } 69 | } else { 70 | // Just add the deadline for this task. 71 | self.tasks.push((new_waker.clone(), new_deadline)); 72 | } 73 | } 74 | 75 | /// Wake and remove expired timers. Return whether tasks or woken, or else how long to wait. 76 | pub fn poll(&mut self) -> TimerPoll { 77 | if self.tasks.is_empty() { 78 | return TimerPoll::Idle; 79 | } 80 | let mut ret = TimerPoll::::Idle; 81 | let now = Instant::now(); 82 | 83 | // Could use drain_filter when stable. https://github.com/rust-lang/rust/issues/43244 84 | self.tasks.retain(|(waker, deadline)| { 85 | if now >= *deadline { 86 | waker.wake_by_ref(); 87 | ret = TimerPoll::Woken; 88 | false 89 | } else { 90 | ret = match ret { 91 | TimerPoll::Delay(ref cur) if deadline < cur => TimerPoll::Delay(*deadline), 92 | TimerPoll::Delay(cur) => TimerPoll::Delay(cur), 93 | TimerPoll::Idle => TimerPoll::Delay(*deadline), 94 | TimerPoll::Woken => TimerPoll::Woken, 95 | }; 96 | true 97 | } 98 | }); 99 | ret.map(|deadline| Ticks::from(deadline).sub_timeout(Ticks::from(now))) 100 | } 101 | } 102 | 103 | pub(super) enum TimerPoll { 104 | /// No pending timers 105 | Idle, 106 | /// A task was woken due to timer expiration 107 | Woken, 108 | /// No work to do now. Contains the soonest expiring timer. 109 | Delay(T), 110 | } 111 | 112 | impl TimerPoll { 113 | pub fn map(self, f: F) -> TimerPoll 114 | where 115 | F: FnOnce(T) -> U, 116 | { 117 | match self { 118 | TimerPoll::Idle => TimerPoll::Idle, 119 | TimerPoll::Woken => TimerPoll::Woken, 120 | TimerPoll::Delay(t) => TimerPoll::Delay(f(t)), 121 | } 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /rust/zephyr-logger/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "zephyr-logger" 3 | version = "0.1.0" 4 | authors = ["Chris Redmon "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | log = "0.4" 9 | -------------------------------------------------------------------------------- /rust/zephyr-logger/src/lib.rs: -------------------------------------------------------------------------------- 1 | use log::{LevelFilter, Metadata, Record}; 2 | 3 | pub fn init(max_level: LevelFilter) { 4 | log::set_logger(&ZEPHYR_LOGGER).unwrap(); 5 | log::set_max_level(max_level); 6 | } 7 | 8 | static ZEPHYR_LOGGER: ZephyrLogger = ZephyrLogger; 9 | struct ZephyrLogger; 10 | 11 | impl log::Log for ZephyrLogger { 12 | fn enabled(&self, metadata: &Metadata) -> bool { 13 | metadata.level() <= log::max_level() 14 | } 15 | 16 | fn log(&self, record: &Record) { 17 | if self.enabled(record.metadata()) { 18 | println!("{} {}: {}", record.level(), record.target(), record.args()); 19 | } 20 | } 21 | 22 | fn flush(&self) {} 23 | } 24 | -------------------------------------------------------------------------------- /rust/zephyr-macros/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | Cargo.lock 4 | -------------------------------------------------------------------------------- /rust/zephyr-macros/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "zephyr-macros" 3 | version = "0.1.0" 4 | authors = ["Tyler Hall "] 5 | edition = "2018" 6 | 7 | [lib] 8 | proc-macro = true 9 | 10 | [dependencies] 11 | proc-macro2 = "0.4.27" 12 | quote = "0.6.12" 13 | -------------------------------------------------------------------------------- /rust/zephyr-macros/src/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate proc_macro; 2 | 3 | use proc_macro::TokenStream; 4 | use proc_macro2::{Ident, Literal, TokenTree}; 5 | use quote::quote; 6 | 7 | fn get_single_arg(item: TokenStream) -> Ident { 8 | let item = proc_macro2::TokenStream::from(item); 9 | let arg = item.into_iter().next(); 10 | let ident = if let Some(TokenTree::Ident(ident)) = arg { 11 | ident 12 | } else { 13 | panic!("k_*_define takes one identifier argument. Got {:?}", arg); 14 | }; 15 | ident 16 | } 17 | 18 | #[proc_macro] 19 | pub fn k_mutex_define(item: TokenStream) -> TokenStream { 20 | let ident = get_single_arg(item); 21 | let section = Literal::string(&format!("._k_mutex.static.{}", ident)); 22 | let ctor = Ident::new(&format!("_rust_mutex_init_{}", ident), ident.span()); 23 | let ctor_ptr = Ident::new(&format!("_ctor_rust_mutex_init_{}", ident), ident.span()); 24 | let expanded = quote! { 25 | // The static storage for the object, itself 26 | #[link_section = #section] 27 | static #ident: zephyr::mutex::global::k_mutex = unsafe { zephyr::mutex::global::k_mutex::uninit() }; 28 | 29 | // A constructor function that calls its init 30 | #[allow(non_snake_case)] 31 | extern "C" fn #ctor() { 32 | use zephyr::mutex::RawMutex; 33 | unsafe { #ident.init::() } 34 | } 35 | 36 | // Add a pointer to the constructor to .ctors table 37 | #[used] 38 | #[link_section = ".ctors"] 39 | #[allow(non_upper_case_globals)] 40 | static #ctor_ptr: extern "C" fn() = #ctor; 41 | }; 42 | 43 | expanded.into() 44 | } 45 | 46 | #[proc_macro] 47 | pub fn k_poll_signal_define(item: TokenStream) -> TokenStream { 48 | let ident = get_single_arg(item); 49 | // Using mutex section because there is not one for poll_signal. Need to 50 | // ensure this is in kernel memory. 51 | let section = Literal::string(&format!("._k_mutex.static.{}", ident)); 52 | let ctor = Ident::new(&format!("_rust_poll_signal_init_{}", ident), ident.span()); 53 | let ctor_ptr = Ident::new( 54 | &format!("_ctor_rust_poll_signal_init_{}", ident), 55 | ident.span(), 56 | ); 57 | let expanded = quote! { 58 | // The static storage for the object, itself 59 | #[link_section = #section] 60 | static #ident: zephyr::poll::global::k_poll_signal = unsafe { zephyr::poll::global::k_poll_signal::uninit() }; 61 | 62 | // A constructor function that calls its init 63 | #[allow(non_snake_case)] 64 | extern "C" fn #ctor() { 65 | use zephyr::poll::*; 66 | unsafe { #ident.init::() } 67 | } 68 | 69 | // Add a pointer to the constructor to .ctors table 70 | #[used] 71 | #[link_section = ".ctors"] 72 | #[allow(non_upper_case_globals)] 73 | static #ctor_ptr: extern "C" fn() = #ctor; 74 | }; 75 | 76 | expanded.into() 77 | } 78 | 79 | fn get_sem_args(item: TokenStream) -> Option<(Ident, Literal, Literal)> { 80 | let item = proc_macro2::TokenStream::from(item); 81 | let mut iter = item.into_iter(); 82 | 83 | if let ( 84 | Some(TokenTree::Ident(ident)), 85 | Some(TokenTree::Punct(p1)), 86 | Some(TokenTree::Literal(l1)), 87 | Some(TokenTree::Punct(p2)), 88 | Some(TokenTree::Literal(l2)), 89 | ) = ( 90 | iter.next(), 91 | iter.next(), 92 | iter.next(), 93 | iter.next(), 94 | iter.next(), 95 | ) { 96 | if p1.as_char() == ',' && p2.as_char() == ',' { 97 | return Some((ident, l1, l2)); 98 | } 99 | } 100 | None 101 | } 102 | 103 | #[proc_macro] 104 | pub fn k_sem_define(item: TokenStream) -> TokenStream { 105 | let (ident, initial, limit) = get_sem_args(item).expect("Expected 3 comma-separated arguments"); 106 | 107 | let section = Literal::string(&format!("._k_sem.static.{}", ident)); 108 | let ctor = Ident::new(&format!("_rust_sem_init_{}", ident), ident.span()); 109 | let ctor_ptr = Ident::new(&format!("_ctor_rust_sem_init_{}", ident), ident.span()); 110 | let expanded = quote! { 111 | // The static storage for the object, itself 112 | #[link_section = #section] 113 | static #ident: zephyr::semaphore::global::k_sem = unsafe { zephyr::semaphore::global::k_sem::uninit() }; 114 | 115 | // A constructor function that calls its init 116 | #[allow(non_snake_case)] 117 | extern "C" fn #ctor() { 118 | use zephyr::semaphore::*; 119 | unsafe { #ident.init::(#initial, #limit) } 120 | } 121 | 122 | // Add a pointer to the constructor to .ctors table 123 | #[used] 124 | #[link_section = ".ctors"] 125 | #[allow(non_upper_case_globals)] 126 | static #ctor_ptr: extern "C" fn() = #ctor; 127 | }; 128 | 129 | expanded.into() 130 | } 131 | -------------------------------------------------------------------------------- /rust/zephyr-sys/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | Cargo.lock 4 | -------------------------------------------------------------------------------- /rust/zephyr-sys/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "zephyr-sys" 3 | version = "0.1.0" 4 | authors = ["Tyler Hall "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | core = { version = '1.0.0', optional = true, package = 'rustc-std-workspace-core' } 9 | compiler_builtins = { version = '0.1.2', optional = true } 10 | libc = "0.2" 11 | 12 | [features] 13 | rustc-dep-of-std = ['core', 'compiler_builtins/rustc-dep-of-std', 'libc/rustc-dep-of-std'] 14 | -------------------------------------------------------------------------------- /rust/zephyr-sys/build.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | 3 | fn main() { 4 | // This build.rs just invokes a binary from a different project. We do it this way to avoid 5 | // having any build-dependencies in this project. Cargo hopelessly conflates dependencies and 6 | // build-dependencies in a way that makes it impossible to build libstd 7 | let zb = env::var("ZEPHYR_BINDGEN").expect("ZEPHYR_BINDGEN unset"); 8 | let rc = std::process::Command::new(zb) 9 | .status() 10 | .expect("cargo run failed"); 11 | assert!(rc.success()); 12 | } 13 | -------------------------------------------------------------------------------- /rust/zephyr-sys/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![allow(non_upper_case_globals)] 3 | #![allow(non_camel_case_types)] 4 | #![allow(non_snake_case)] 5 | #![allow(improper_ctypes)] // Zero size struct for k_spinlock 6 | 7 | pub mod raw { 8 | include!(concat!(env!("OUT_DIR"), "/bindings.rs")); 9 | 10 | unsafe impl Send for k_mutex {} 11 | unsafe impl Sync for k_mutex {} 12 | unsafe impl Send for k_sem {} 13 | unsafe impl Sync for k_sem {} 14 | unsafe impl Send for device {} 15 | unsafe impl Sync for device {} 16 | 17 | // Recreate what the K_FOREVER macro does 18 | pub const K_FOREVER: k_timeout_t = k_timeout_t { 19 | ticks: -1 as k_ticks_t, 20 | }; 21 | 22 | pub const K_NO_WAIT: k_timeout_t = k_timeout_t { ticks: 0 }; 23 | } 24 | 25 | pub mod syscalls { 26 | include!(concat!(env!("OUT_DIR"), "/syscalls.rs")); 27 | } 28 | -------------------------------------------------------------------------------- /rust/zephyr-sys/wrapper.h: -------------------------------------------------------------------------------- 1 | // Work around compile error on x86 with clang < 3.9.0 where __float128 is not 2 | // defined 3 | #define _GCC_MAX_ALIGN_T 4 | 5 | #include 6 | #if KERNEL_VERSION_MAJOR < 3 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #else 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #endif 19 | 20 | #ifdef CONFIG_UART_BUFFERED 21 | #include 22 | #endif 23 | 24 | #ifdef CONFIG_POSIX_CLOCK 25 | #include 26 | #endif 27 | 28 | // Create a constant we can use from Rust in all cases 29 | #ifdef CONFIG_USERSPACE 30 | const bool RUST_CONFIG_USERSPACE = true; 31 | #else 32 | const bool RUST_CONFIG_USERSPACE = false; 33 | #endif 34 | -------------------------------------------------------------------------------- /rust/zephyr-uart-buffered/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "zephyr-uart-buffered" 3 | version = "0.1.0" 4 | authors = ["Tyler Hall "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | futures = "0.3" 9 | zephyr-futures = { path = "../zephyr-futures" } 10 | -------------------------------------------------------------------------------- /rust/zephyr-uart-buffered/src/futures.rs: -------------------------------------------------------------------------------- 1 | use core::pin::Pin; 2 | use core::task::{Context, Poll}; 3 | 4 | use futures::io::{AsyncRead, AsyncWrite, Error}; 5 | 6 | use zephyr_core::context::Any as C; 7 | use zephyr_core::poll::Signal; 8 | use zephyr_futures::current_reactor_register; 9 | 10 | use super::{UartBufferedRx, UartBufferedTx}; 11 | 12 | pub struct UartBufferedRxAsync { 13 | uart: UartBufferedRx, 14 | } 15 | 16 | impl UartBufferedRxAsync { 17 | pub fn new(uart: UartBufferedRx) -> Self { 18 | UartBufferedRxAsync { uart } 19 | } 20 | } 21 | 22 | impl AsyncRead for UartBufferedRxAsync { 23 | fn poll_read( 24 | self: Pin<&mut Self>, 25 | cx: &mut Context, 26 | buf: &mut [u8], 27 | ) -> Poll> { 28 | let s = self.get_mut(); 29 | let uart = &mut s.uart; 30 | 31 | if let Some(len) = uart.read_nb(buf) { 32 | return Poll::Ready(Ok(len)); 33 | } 34 | 35 | // Need to register for readiness on the signal. We wait to clear the 36 | // signal until after the uart is not ready so that we don't make a 37 | // redundant system call before each read attempt, e.g. if the client is 38 | // reading one byte at a time and there are several buffered. 39 | // Because the signal is edge triggered, resetting here could clear an 40 | // event that happened since the poll above. So poll one more time. 41 | let signal = uart.get_signal(); 42 | signal.reset::(); 43 | current_reactor_register(signal, cx); 44 | 45 | if let Some(len) = uart.read_nb(buf) { 46 | return Poll::Ready(Ok(len)); 47 | } 48 | 49 | Poll::Pending 50 | } 51 | } 52 | 53 | pub struct UartBufferedTxAsync { 54 | uart: UartBufferedTx, 55 | } 56 | 57 | impl UartBufferedTxAsync { 58 | pub fn new(uart: UartBufferedTx) -> Self { 59 | UartBufferedTxAsync { uart } 60 | } 61 | } 62 | 63 | impl AsyncWrite for UartBufferedTxAsync { 64 | fn poll_write( 65 | self: Pin<&mut Self>, 66 | cx: &mut Context, 67 | buf: &[u8], 68 | ) -> Poll> { 69 | let s = self.get_mut(); 70 | let uart = &mut s.uart; 71 | 72 | if let Some(len) = uart.write_nb(buf) { 73 | return Poll::Ready(Ok(len)); 74 | } 75 | 76 | // Need to register for readiness on the signal. We wait to clear the 77 | // signal until after the uart is not ready so that we don't make a 78 | // redundant system call before each write attempt, e.g. if the client 79 | // is writing one byte at a time and there are several buffered. 80 | // Because the signal is edge triggered, resetting here could clear an 81 | // event that happened since the poll above. So poll one more time. 82 | let signal = uart.get_signal(); 83 | signal.reset::(); 84 | current_reactor_register(signal, cx); 85 | 86 | if let Some(len) = uart.write_nb(buf) { 87 | return Poll::Ready(Ok(len)); 88 | } 89 | 90 | Poll::Pending 91 | } 92 | 93 | fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context) -> Poll> { 94 | Poll::Ready(Ok(())) 95 | } 96 | 97 | fn poll_close(self: Pin<&mut Self>, _cx: &mut Context) -> Poll> { 98 | Poll::Ready(Ok(())) 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /rust/zephyr-uart-buffered/src/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate zephyr_core; 2 | extern crate zephyr_sys; 3 | 4 | use zephyr_core::poll::KPollSignal; 5 | use zephyr_core::NegErr; 6 | use zephyr_sys::raw::{uart_buffered_rx_handle, uart_buffered_tx_handle}; 7 | 8 | mod futures; 9 | 10 | pub use crate::futures::{UartBufferedRxAsync, UartBufferedTxAsync}; 11 | 12 | pub struct UartBufferedRx { 13 | handle: uart_buffered_rx_handle, 14 | } 15 | 16 | // Handle is not thread-safe but can be sent between threads 17 | unsafe impl Send for UartBufferedRx {} 18 | 19 | impl UartBufferedRx { 20 | /// Unsafe because this is passed from C and caller must guarantee there is 21 | /// only one instance created per handle. 22 | pub unsafe fn new(handle: uart_buffered_rx_handle) -> Self { 23 | UartBufferedRx { handle } 24 | } 25 | 26 | /// Infallible read (lower level than `std::io::Read`) 27 | pub fn read(&mut self, buf: &mut [u8]) -> usize { 28 | unsafe { 29 | zephyr_sys::raw::uart_buffered_read( 30 | &self.handle as *const _ as *mut _, 31 | buf.as_mut_ptr(), 32 | buf.len(), 33 | ) 34 | } 35 | } 36 | 37 | /// Non blocking read. Returns None if nothing read, else returns number of bytes read. 38 | pub fn read_nb(&mut self, buf: &mut [u8]) -> Option { 39 | unsafe { 40 | zephyr_sys::raw::uart_buffered_read_nb( 41 | &self.handle as *const _ as *mut _, 42 | buf.as_mut_ptr(), 43 | buf.len(), 44 | ) 45 | } 46 | .neg_err() 47 | .ok() 48 | .map(|len| len as usize) 49 | } 50 | 51 | /// Get reference to signal to wait on for non-blocking readiness. Static 52 | /// lifetime because uart buffered can only be declared statically. 53 | pub fn get_signal(&self) -> &'static KPollSignal { 54 | unsafe { &*self.handle.fifo.signal } 55 | } 56 | 57 | pub fn into_async(self) -> UartBufferedRxAsync { 58 | UartBufferedRxAsync::new(self) 59 | } 60 | } 61 | 62 | pub struct UartBufferedTx { 63 | handle: uart_buffered_tx_handle, 64 | } 65 | 66 | // Handle is not thread-safe but can be sent between threads 67 | unsafe impl Send for UartBufferedTx {} 68 | 69 | impl UartBufferedTx { 70 | /// Unsafe because this is passed from C and caller must guarantee there is 71 | /// only one instance created per handle. 72 | pub unsafe fn new(handle: uart_buffered_tx_handle) -> Self { 73 | UartBufferedTx { handle } 74 | } 75 | 76 | /// Infallible write (lower level than `std::io::Write`) 77 | pub fn write(&mut self, buf: &[u8]) { 78 | unsafe { 79 | zephyr_sys::raw::uart_buffered_write( 80 | &self.handle as *const _ as *mut _, 81 | buf.as_ptr(), 82 | buf.len(), 83 | ) 84 | } 85 | } 86 | 87 | /// Non blocking write. Returns None if nothing written, else returns number of bytes written. 88 | pub fn write_nb(&mut self, buf: &[u8]) -> Option { 89 | unsafe { 90 | zephyr_sys::raw::uart_buffered_write_nb( 91 | &self.handle as *const _ as *mut _, 92 | buf.as_ptr(), 93 | buf.len(), 94 | ) 95 | } 96 | .neg_err() 97 | .ok() 98 | .map(|len| len as usize) 99 | } 100 | 101 | /// Get reference to signal to wait on for non-blocking readiness. Static 102 | /// lifetime because uart buffered can only be declared statically. 103 | pub fn get_signal(&self) -> &'static KPollSignal { 104 | unsafe { &*self.handle.fifo.signal } 105 | } 106 | 107 | pub fn into_async(self) -> UartBufferedTxAsync { 108 | UartBufferedTxAsync::new(self) 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /rust/zephyr/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "zephyr" 3 | description = "Safe bindings to Zephyr kernel API" 4 | version = "0.1.0" 5 | authors = ["Tyler Hall "] 6 | edition = "2018" 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /rust/zephyr/src/device.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::CStr; 2 | 3 | pub use zephyr_sys::raw::device as Device; 4 | 5 | pub trait DeviceSyscalls { 6 | fn device_get_binding(device_name: &CStr) -> Option<&'static Device>; 7 | } 8 | 9 | macro_rules! trait_impl { 10 | ($context:ident, $context_struct:path) => { 11 | impl DeviceSyscalls for $context_struct { 12 | #[inline(always)] 13 | fn device_get_binding(device_name: &CStr) -> Option<&'static Device> { 14 | unsafe { 15 | // All devices are static in Zephyr, so static lifetime 16 | // Option<&T> is guaranteed to have the null pointer optimization, so we can cast 17 | // https://doc.rust-lang.org/nomicon/ffi.html#the-nullable-pointer-optimization 18 | core::mem::transmute(zephyr_sys::syscalls::$context::device_get_binding( 19 | device_name.as_ptr(), 20 | )) 21 | } 22 | } 23 | } 24 | }; 25 | } 26 | 27 | trait_impl!(kernel, crate::context::Kernel); 28 | trait_impl!(user, crate::context::User); 29 | trait_impl!(any, crate::context::Any); 30 | -------------------------------------------------------------------------------- /rust/zephyr/src/eeprom.rs: -------------------------------------------------------------------------------- 1 | use std::io; 2 | 3 | use zephyr_sys::raw::off_t; 4 | 5 | use super::NegErrno; 6 | use crate::device::Device; 7 | 8 | /// Raw syscall API 9 | pub trait EepromSyscalls { 10 | unsafe fn eeprom_read(device: *mut Device, offset: off_t, data: &mut [u8]) -> io::Result<()>; 11 | unsafe fn eeprom_write(device: *mut Device, offset: off_t, data: &[u8]) -> io::Result<()>; 12 | unsafe fn eeprom_get_size(device: *mut Device) -> usize; 13 | } 14 | 15 | macro_rules! trait_impl { 16 | ($context:ident, $context_struct:path) => { 17 | impl EepromSyscalls for $context_struct { 18 | #[inline(always)] 19 | unsafe fn eeprom_read( 20 | device: *mut Device, 21 | offset: off_t, 22 | data: &mut [u8], 23 | ) -> io::Result<()> { 24 | zephyr_sys::syscalls::$context::eeprom_read( 25 | device, 26 | offset, 27 | data.as_mut_ptr() as *mut _, 28 | data.len(), 29 | ) 30 | .zero_or_neg_errno() 31 | } 32 | 33 | #[inline(always)] 34 | unsafe fn eeprom_write( 35 | device: *mut Device, 36 | offset: off_t, 37 | data: &[u8], 38 | ) -> io::Result<()> { 39 | zephyr_sys::syscalls::$context::eeprom_write( 40 | device, 41 | offset, 42 | data.as_ptr() as *const _, 43 | data.len(), 44 | ) 45 | .zero_or_neg_errno() 46 | } 47 | 48 | #[inline(always)] 49 | unsafe fn eeprom_get_size(device: *mut Device) -> usize { 50 | zephyr_sys::syscalls::$context::eeprom_get_size(device) 51 | } 52 | } 53 | }; 54 | } 55 | 56 | trait_impl!(kernel, crate::context::Kernel); 57 | trait_impl!(user, crate::context::User); 58 | trait_impl!(any, crate::context::Any); 59 | 60 | pub struct Eeprom(&'static Device); 61 | 62 | impl Eeprom { 63 | /// # Safety 64 | /// 65 | /// Caller must ensure the device is an eeprom device 66 | pub unsafe fn new(dev: &'static Device) -> Self { 67 | Eeprom(dev) 68 | } 69 | 70 | #[inline(always)] 71 | pub fn read(&self, offset: off_t, data: &mut [u8]) -> io::Result<()> { 72 | unsafe { C::eeprom_read(self.0 as *const _ as *mut _, offset, data) } 73 | } 74 | 75 | #[inline(always)] 76 | pub fn write(&self, offset: off_t, data: &[u8]) -> io::Result<()> { 77 | unsafe { C::eeprom_write(self.0 as *const _ as *mut _, offset, data) } 78 | } 79 | 80 | #[inline(always)] 81 | pub fn size(&self) -> usize { 82 | unsafe { C::eeprom_get_size(self.0 as *const _ as *mut _) } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /rust/zephyr/src/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate zephyr_core; 2 | extern crate zephyr_sys; 3 | 4 | use std::io; 5 | 6 | pub use zephyr_core::*; 7 | pub mod device; 8 | pub mod eeprom; 9 | pub mod uart; 10 | 11 | trait NegErrno: NegErr { 12 | fn neg_errno(&self) -> io::Result; 13 | fn zero_or_neg_errno(&self) -> io::Result<()>; 14 | } 15 | 16 | impl NegErrno for i32 { 17 | fn neg_errno(&self) -> io::Result { 18 | self.neg_err() 19 | .map_err(|e| io::Error::from_raw_os_error(e as i32)) 20 | } 21 | 22 | fn zero_or_neg_errno(&self) -> io::Result<()> { 23 | self.neg_errno().map(|_| ()) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /rust/zephyr/src/uart.rs: -------------------------------------------------------------------------------- 1 | use super::NegErr; 2 | use crate::device::Device; 3 | 4 | pub trait UartSyscalls { 5 | fn uart_poll_out(device: &Device, out_char: char); 6 | 7 | fn uart_poll_in(device: &Device) -> Result, u32>; 8 | 9 | fn uart_err_check(device: &Device) -> Option; 10 | 11 | fn uart_config_get(device: &Device) -> Result; 12 | 13 | fn uart_configure(device: &Device, config: &UartConfig) -> Result<(), u32>; 14 | } 15 | 16 | macro_rules! trait_impl { 17 | ($context:ident, $context_struct:path) => { 18 | impl UartSyscalls for $context_struct { 19 | #[inline(always)] 20 | fn uart_poll_out(device: &Device, out_char: char) { 21 | unsafe { 22 | zephyr_sys::syscalls::$context::uart_poll_out( 23 | device as *const _ as *mut _, 24 | out_char as u8, 25 | ) 26 | }; 27 | } 28 | 29 | #[inline(always)] 30 | fn uart_poll_in(device: &Device) -> Result, u32> { 31 | let mut munge: u8 = 0; 32 | let rc = unsafe { 33 | zephyr_sys::syscalls::$context::uart_poll_in( 34 | device as *const _ as *mut _, 35 | &mut munge, 36 | ) 37 | } 38 | .neg_err() 39 | .map(|_| munge as char); 40 | 41 | // remap a return value of -1 from uart_poll_in() to Ok(None) 42 | match rc { 43 | Ok(c) => Ok(Some(c)), 44 | Err(1) => Ok(None), 45 | Err(e) => Err(e), 46 | } 47 | } 48 | 49 | #[inline(always)] 50 | fn uart_err_check(device: &Device) -> Option { 51 | let rc = unsafe { 52 | zephyr_sys::syscalls::$context::uart_err_check(device as *const _ as *mut _) 53 | } 54 | .neg_err(); 55 | 56 | match rc { 57 | Ok(_) => None, 58 | Err(e) => Some(e), 59 | } 60 | } 61 | 62 | #[inline(always)] 63 | fn uart_config_get(device: &Device) -> Result { 64 | let mut config = UartConfig::default(); 65 | unsafe { 66 | zephyr_sys::syscalls::$context::uart_config_get( 67 | device as *const _ as *mut _, 68 | &mut config.0, 69 | ) 70 | } 71 | .neg_err() 72 | .map(|_| config) 73 | } 74 | 75 | #[inline(always)] 76 | fn uart_configure(device: &Device, config: &UartConfig) -> Result<(), u32> { 77 | unsafe { 78 | zephyr_sys::syscalls::$context::uart_configure( 79 | device as *const _ as *mut _, 80 | &config.0, 81 | ) 82 | } 83 | .neg_err() 84 | .map(|_| ()) 85 | } 86 | } 87 | }; 88 | } 89 | 90 | pub struct UartConfig(zephyr_sys::raw::uart_config); 91 | 92 | impl UartConfig { 93 | pub fn set_flow_control_rts_cts(&mut self) { 94 | self.0.flow_ctrl = 95 | zephyr_sys::raw::uart_config_flow_control_UART_CFG_FLOW_CTRL_RTS_CTS as u8; 96 | } 97 | 98 | pub fn set_flow_control_dtr_dsr(&mut self) { 99 | self.0.flow_ctrl = 100 | zephyr_sys::raw::uart_config_flow_control_UART_CFG_FLOW_CTRL_DTR_DSR as u8; 101 | } 102 | 103 | pub fn disable_flow_control(&mut self) { 104 | self.0.flow_ctrl = zephyr_sys::raw::uart_config_flow_control_UART_CFG_FLOW_CTRL_NONE as u8; 105 | } 106 | 107 | pub fn get_baud_rate(&self) -> u32 { 108 | self.0.baudrate 109 | } 110 | 111 | pub fn set_baud_rate(&mut self, baud_rate: u32) { 112 | self.0.baudrate = baud_rate; 113 | } 114 | 115 | pub fn get_stop_bits(&self) -> u8 { 116 | self.0.stop_bits 117 | } 118 | 119 | pub fn set_stop_bits(&mut self, stop_bits: u8) { 120 | self.0.stop_bits = stop_bits 121 | } 122 | 123 | pub fn get_data_bits(&self) -> u8 { 124 | self.0.data_bits 125 | } 126 | 127 | pub fn set_data_bits(&mut self, data_bits: u8) { 128 | self.0.data_bits = data_bits 129 | } 130 | } 131 | 132 | impl Default for UartConfig { 133 | fn default() -> Self { 134 | Self(zephyr_sys::raw::uart_config { 135 | baudrate: 0, 136 | parity: 0, 137 | stop_bits: 0, 138 | data_bits: 0, 139 | flow_ctrl: 0, 140 | }) 141 | } 142 | } 143 | 144 | trait_impl!(kernel, crate::context::Kernel); 145 | trait_impl!(user, crate::context::User); 146 | trait_impl!(any, crate::context::Any); 147 | -------------------------------------------------------------------------------- /samples/futures/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | 3 | cmake_minimum_required(VERSION 3.13.1) 4 | 5 | # For this example, add ZEPHYR_RUST as a zephyr module. Not needed if added in 6 | # your environment or managed by west. 7 | get_filename_component(ZEPHYR_RUST ${CMAKE_CURRENT_SOURCE_DIR}/../.. ABSOLUTE) 8 | list(APPEND ZEPHYR_EXTRA_MODULES ${ZEPHYR_RUST}) 9 | 10 | include($ENV{ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE) 11 | project(rust) 12 | target_sources(app PRIVATE ./src/main.c) 13 | -------------------------------------------------------------------------------- /samples/futures/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "app" 3 | version = "0.1.0" 4 | authors = ["Tyler Hall "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | futures = "0.3.1" 9 | zephyr = { path = "../../rust/zephyr" } 10 | zephyr-macros = { path = "../../rust/zephyr-macros" } 11 | zephyr-futures = { path = "../../rust/zephyr-futures" } 12 | -------------------------------------------------------------------------------- /samples/futures/prj.conf: -------------------------------------------------------------------------------- 1 | CONFIG_RUST=y 2 | CONFIG_ZTEST=y 3 | CONFIG_POLL=y 4 | CONFIG_MAIN_STACK_SIZE=1024 5 | CONFIG_HEAP_MEM_POOL_SIZE=1024 6 | -------------------------------------------------------------------------------- /samples/futures/sample.yaml: -------------------------------------------------------------------------------- 1 | common: 2 | arch_whitelist: x86 arm posix 3 | tests: 4 | rust.futures: 5 | tags: rust 6 | -------------------------------------------------------------------------------- /samples/futures/src/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate libc; 2 | 3 | use libc::c_void; 4 | 5 | use futures::future; 6 | use futures::stream::StreamExt; 7 | use futures::task::LocalSpawnExt; 8 | 9 | use zephyr::semaphore::*; 10 | use zephyr_futures::{Executor, SemaphoreStream}; 11 | 12 | zephyr_macros::k_sem_define!(TEST_SEM, 0, 10); 13 | 14 | #[no_mangle] 15 | pub extern "C" fn rust_sem_thread(_a: *const c_void, _b: *const c_void, _c: *const c_void) { 16 | use zephyr::context::Kernel as C; 17 | for i in 0..10 { 18 | println!("Giving {}", i); 19 | TEST_SEM.give::(); 20 | } 21 | } 22 | 23 | zephyr_macros::k_mutex_define!(EXECUTOR_MUTEX); 24 | zephyr_macros::k_poll_signal_define!(EXECUTOR_SIGNAL); 25 | 26 | #[no_mangle] 27 | pub extern "C" fn rust_test_main() { 28 | use zephyr::context::Kernel as C; 29 | 30 | let f = SemaphoreStream::new(&TEST_SEM) 31 | .take(10) 32 | .enumerate() 33 | .for_each(|(i, _val)| { 34 | println!("Took {}", i); 35 | future::ready(()) 36 | }); 37 | let mut executor = unsafe { Executor::new(&EXECUTOR_MUTEX, &EXECUTOR_SIGNAL) }; 38 | executor.spawn_local(f).unwrap(); 39 | executor.run::(); 40 | } 41 | -------------------------------------------------------------------------------- /samples/futures/src/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #if KERNEL_VERSION_MAJOR < 3 4 | #include 5 | #else 6 | #include 7 | #endif 8 | 9 | extern void rust_test_main(void); 10 | extern void rust_sem_thread(void *, void *, void *); 11 | 12 | K_THREAD_DEFINE(sem_thread, 1024, rust_sem_thread, NULL, NULL, NULL, 13 | K_LOWEST_APPLICATION_THREAD_PRIO, 0, 0); 14 | 15 | void test_main(void) 16 | { 17 | rust_test_main(); 18 | } 19 | -------------------------------------------------------------------------------- /samples/no_std/.gitignore: -------------------------------------------------------------------------------- 1 | Cargo.lock 2 | /target 3 | -------------------------------------------------------------------------------- /samples/no_std/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | 3 | cmake_minimum_required(VERSION 3.13.1) 4 | 5 | # For this example, add ZEPHYR_RUST as a zephyr module. Not needed if added in 6 | # your environment or managed by west. 7 | get_filename_component(ZEPHYR_RUST ${CMAKE_CURRENT_SOURCE_DIR}/../.. ABSOLUTE) 8 | list(APPEND ZEPHYR_EXTRA_MODULES ${ZEPHYR_RUST}) 9 | 10 | include($ENV{ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE) 11 | project(hello_world) 12 | target_sources(app PRIVATE ./src/main.c) 13 | -------------------------------------------------------------------------------- /samples/no_std/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "app" 3 | version = "0.1.0" 4 | authors = ["Jack Greenbaum ", "Tyler Hall "] 5 | repository = "https://github.com/tylerwhall/zephyr-rust.git" 6 | edition = "2018" 7 | 8 | [dependencies] 9 | cstr = "0.2.10" 10 | log = "0.4" 11 | zephyr = { path = "../../rust/zephyr" } 12 | zephyr-macros = { path = "../../rust/zephyr-macros" } 13 | zephyr-logger = { path = "../../rust/zephyr-logger" } 14 | 15 | [features] 16 | default = [] 17 | # Enable this to add the std examples back into the sample 18 | have_std = [] -------------------------------------------------------------------------------- /samples/no_std/README.md: -------------------------------------------------------------------------------- 1 | # A no_std example for zephyr-rust 2 | 3 | This is essentially the same as `samples/rust-app`, but with the following changes to make it 4 | `#![no_std]`: 5 | - Replaced `zephyr::` with `zephyr_core::` 6 | - Replaced `println!()` with `zephyr_core::any::k_str_out(format!())` (except where println was 7 | to be called explicitly) 8 | - Used `#[cfg(feature = "have_std")]` to 'comment out' code that requires std. This makes it easy to see what parts of Rust (`println`, `std::time`, etc) and zephyr-rust (`zephyr` vs `zephyr_core`) require std. You can of course run those lines by enabling the `have_std` feature. 9 | -------------------------------------------------------------------------------- /samples/no_std/prj.conf: -------------------------------------------------------------------------------- 1 | CONFIG_RUST=y 2 | CONFIG_RUST_ALLOC_POOL=y 3 | CONFIG_SERIAL=y 4 | CONFIG_UART_NATIVE_POSIX=y 5 | CONFIG_NATIVE_UART_0_ON_OWN_PTY=y 6 | CONFIG_USERSPACE=y 7 | CONFIG_QEMU_ICOUNT=n 8 | CONFIG_LOG=y 9 | -------------------------------------------------------------------------------- /samples/no_std/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(not(feature = "have_std"), no_std)] 2 | 3 | #[macro_use] 4 | extern crate cstr; 5 | #[macro_use] 6 | extern crate log; 7 | 8 | extern crate zephyr_macros; 9 | #[cfg(feature = "have_std")] 10 | extern crate zephyr; 11 | extern crate zephyr_core; 12 | extern crate zephyr_logger; 13 | 14 | use core::ffi::c_void; 15 | use log::LevelFilter; 16 | 17 | extern crate alloc; 18 | use alloc::format; 19 | use alloc::boxed::Box; 20 | 21 | use zephyr::device::DeviceSyscalls; 22 | use zephyr_core::mutex::*; 23 | use zephyr_core::semaphore::*; 24 | use zephyr_core::thread::ThreadSyscalls; 25 | 26 | #[cfg(feature = "have_std")] 27 | use std::cell::RefCell; 28 | #[cfg(feature = "have_std")] 29 | thread_local!(static TLS: std::cell::RefCell = RefCell::new(1)); 30 | 31 | zephyr_macros::k_mutex_define!(MUTEX); 32 | zephyr_macros::k_sem_define!(TLS_SEM, 0, 1); 33 | 34 | fn mutex_test() { 35 | let data = 1u32; 36 | 37 | // Bind the static mutex to our local data. This would make more sense if 38 | // the data were static, but that requires app mem regions for user mode. 39 | let mutex = unsafe { Mutex::new(&MUTEX, &data) }; 40 | 41 | // Should allow cloning directly if the data is a reference. 42 | let _other_mutex = mutex.clone(); 43 | zephyr_core::any::k_str_out("Locking\n"); 44 | let _val = mutex.lock::(); 45 | zephyr_core::any::k_str_out("Unlocking\n"); 46 | } 47 | 48 | #[cfg(feature = "have_std")] 49 | fn std_mutex_test() { 50 | println!("std::sync::Mutex::new"); 51 | let lock = std::sync::Mutex::new(0u8); 52 | println!("std::sync::Mutex::lock"); 53 | *lock.lock().unwrap() = 1; 54 | } 55 | 56 | fn thread_join_std_mem_domain(_context: zephyr_core::context::Kernel) { 57 | use zephyr_core::context::Kernel as C; 58 | zephyr_core::static_mem_domain!(rust_std_domain).add_thread::(C::k_current_get()); 59 | } 60 | 61 | #[no_mangle] 62 | pub extern "C" fn rust_second_thread( 63 | _a: *const c_void, 64 | _b: *const c_void, 65 | _c: *const c_void, 66 | ) { 67 | thread_join_std_mem_domain(zephyr_core::context::Kernel); 68 | 69 | zephyr_core::any::k_str_out("Hello from second thread\n"); 70 | 71 | #[cfg(feature = "have_std")] 72 | TLS.with(|f| { 73 | zephyr_core::any::k_str_out(format!("second thread: f = {}\n", *f.borrow()).as_str()); 74 | assert!(*f.borrow() == 1); 75 | *f.borrow_mut() = 55; 76 | zephyr_core::any::k_str_out(format!("second thread: now f = {}\n", *f.borrow()).as_str()); 77 | assert!(*f.borrow() == 55); 78 | }); 79 | 80 | // Let thread 1 access TLS after we have already set it. Value should not be seen on thread 1 81 | TLS_SEM.give::(); 82 | } 83 | 84 | #[no_mangle] 85 | pub extern "C" fn rust_main() { 86 | use zephyr_core::context::Kernel as Context; 87 | 88 | #[cfg(feature = "have_std")] 89 | println!("Hello from Rust on Zephyr {} via println!", zephyr_core::KERNEL_VERSION); 90 | zephyr_core::kernel::k_str_out("Hello from Rust kernel with direct kernel call\n"); 91 | zephyr_core::any::k_str_out("Hello from Rust kernel with runtime-detect syscall\n"); 92 | 93 | #[cfg(feature = "have_std")] 94 | std::thread::sleep(std::time::Duration::from_millis(1)); 95 | zephyr_core::any::k_str_out(format!("Time {:?}\n", zephyr_core::any::k_uptime_ticks().as_millis()).as_str()); 96 | #[cfg(feature = "have_std")] 97 | println!("Time {:?}", std::time::Instant::now()); 98 | 99 | let current = Context::k_current_get(); 100 | current.k_object_access_grant::(&MUTEX); 101 | current.k_object_access_grant::(&TLS_SEM); 102 | mutex_test(); 103 | #[cfg(feature = "have_std")] 104 | std_mutex_test(); 105 | 106 | if let Some(_device) = Context::device_get_binding(cstr!("nonexistent")) { 107 | zephyr_core::any::k_str_out("Got device\n") 108 | } else { 109 | zephyr_core::any::k_str_out("No device\n") 110 | } 111 | 112 | { 113 | let boxed = Box::new(1u8); 114 | zephyr_core::any::k_str_out(format!("Boxed value {}\n", boxed).as_str()); 115 | } 116 | 117 | // test std::ops::{Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive} 118 | { 119 | let a: [u8; 4] = [1, 2, 3, 4]; 120 | let len = a.iter().len(); 121 | for _ in &a[0..len] {} 122 | for _ in &a[0..=(len - 1)] {} 123 | for _ in &a[..] {} 124 | for _ in &a[0..] {} 125 | for _ in &a[..len] {} 126 | for _ in &a[..=(len - 1)] {} 127 | } 128 | 129 | TLS_SEM.take::(); 130 | assert!(!TLS_SEM.try_take::()); 131 | #[cfg(feature = "have_std")] 132 | TLS.with(|f| { 133 | println!("main thread: f = {}\n", *f.borrow()); 134 | assert!(*f.borrow() == 1); 135 | *f.borrow_mut() = 2; 136 | println!("main thread: now f = {}\n", *f.borrow()); 137 | assert!(*f.borrow() == 2); 138 | }); 139 | TLS_SEM.give::(); 140 | 141 | thread_join_std_mem_domain(Context); 142 | zephyr_core::kernel::k_thread_user_mode_enter(|| { 143 | use zephyr_core::context::User as Context; 144 | 145 | zephyr_core::user::k_str_out("Hello from Rust userspace with forced user-mode syscall\n"); 146 | 147 | mutex_test(); 148 | #[cfg(feature = "have_std")] 149 | std_mutex_test(); 150 | 151 | zephyr_logger::init(LevelFilter::Info); 152 | 153 | trace!("TEST: trace!()"); 154 | debug!("TEST: debug!()"); 155 | info!("TEST: info!()"); 156 | warn!("TEST: warn!()"); 157 | error!("TEST: error!()"); 158 | 159 | assert!(TLS_SEM.try_take::()); 160 | #[cfg(feature = "have_std")] 161 | TLS.with(|f| { 162 | println!("main thread: f = {}", *f.borrow()); 163 | assert!(*f.borrow() == 2); 164 | *f.borrow_mut() = 3; 165 | println!("main thread: now f = {}", *f.borrow()); 166 | assert!(*f.borrow() == 3); 167 | }); 168 | 169 | zephyr_core::user::k_str_out("Hello from Rust userspace with forced user-mode syscall\n"); 170 | 171 | zephyr_core::any::k_str_out("Hello from Rust userspace with runtime-detect syscall\nNext call will crash if userspace is working.\n"); 172 | 173 | // This will compile, but crash if CONFIG_USERSPACE is working 174 | zephyr_core::kernel::k_str_out("Hello from Rust userspace with direct kernel call\n"); 175 | }); 176 | } 177 | -------------------------------------------------------------------------------- /samples/no_std/src/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #if KERNEL_VERSION_MAJOR < 3 3 | #include 4 | #else 5 | #include 6 | #endif 7 | 8 | #define MY_STACK_SIZE 1024 9 | #define MY_PRIORITY 5 10 | 11 | extern void rust_main(void); 12 | extern void rust_second_thread(void *, void *, void *); 13 | 14 | K_THREAD_DEFINE(my_tid, MY_STACK_SIZE, 15 | rust_second_thread, NULL, NULL, NULL, 16 | MY_PRIORITY, 0, 0); 17 | 18 | void main(void) 19 | { 20 | rust_main(); 21 | } 22 | -------------------------------------------------------------------------------- /samples/rust-app/.gitignore: -------------------------------------------------------------------------------- 1 | Cargo.lock 2 | /target 3 | -------------------------------------------------------------------------------- /samples/rust-app/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | 3 | cmake_minimum_required(VERSION 3.13.1) 4 | 5 | # For this example, add ZEPHYR_RUST as a zephyr module. Not needed if added in 6 | # your environment or managed by west. 7 | get_filename_component(ZEPHYR_RUST ${CMAKE_CURRENT_SOURCE_DIR}/../.. ABSOLUTE) 8 | list(APPEND ZEPHYR_EXTRA_MODULES ${ZEPHYR_RUST}) 9 | 10 | include($ENV{ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE) 11 | project(hello_world) 12 | target_sources(app PRIVATE ./src/main.c) 13 | -------------------------------------------------------------------------------- /samples/rust-app/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "app" 3 | version = "0.1.0" 4 | authors = ["Tyler Hall "] 5 | repository = "https://github.com/tylerwhall/zephyr-rust.git" 6 | edition = "2018" 7 | 8 | [dependencies] 9 | cstr = "0.2.10" 10 | log = "0.4" 11 | zephyr = { path = "../../rust/zephyr" } 12 | zephyr-macros = { path = "../../rust/zephyr-macros" } 13 | zephyr-logger = { path = "../../rust/zephyr-logger" } 14 | -------------------------------------------------------------------------------- /samples/rust-app/prj.conf: -------------------------------------------------------------------------------- 1 | CONFIG_RUST=y 2 | CONFIG_RUST_ALLOC_POOL=y 3 | CONFIG_SERIAL=y 4 | CONFIG_UART_NATIVE_POSIX=y 5 | CONFIG_NATIVE_UART_0_ON_OWN_PTY=y 6 | CONFIG_USERSPACE=y 7 | CONFIG_QEMU_ICOUNT=n 8 | CONFIG_LOG=y 9 | -------------------------------------------------------------------------------- /samples/rust-app/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate cstr; 3 | #[macro_use] 4 | extern crate log; 5 | 6 | extern crate zephyr_macros; 7 | extern crate zephyr; 8 | extern crate zephyr_logger; 9 | 10 | use std::cell::RefCell; 11 | use std::time::Duration; 12 | 13 | use core::ffi::c_void; 14 | use log::LevelFilter; 15 | 16 | use zephyr::device::DeviceSyscalls; 17 | use zephyr::mutex::*; 18 | use zephyr::semaphore::*; 19 | use zephyr::thread::ThreadSyscalls; 20 | 21 | thread_local!(static TLS: RefCell = RefCell::new(1)); 22 | 23 | zephyr_macros::k_mutex_define!(MUTEX); 24 | zephyr_macros::k_sem_define!(TLS_SEM, 0, 1); 25 | 26 | fn mutex_test() { 27 | let data = 1u32; 28 | 29 | // Bind the static mutex to our local data. This would make more sense if 30 | // the data were static, but that requires app mem regions for user mode. 31 | let mutex = unsafe { Mutex::new(&MUTEX, &data) }; 32 | 33 | // Should allow cloning directly if the data is a reference. 34 | let _other_mutex = mutex.clone(); 35 | zephyr::any::k_str_out("Locking\n"); 36 | let _val = mutex.lock::(); 37 | zephyr::any::k_str_out("Unlocking\n"); 38 | } 39 | 40 | fn std_mutex_test() { 41 | println!("std::sync::Mutex::new"); 42 | let lock = std::sync::Mutex::new(0u8); 43 | println!("std::sync::Mutex::lock"); 44 | *lock.lock().unwrap() = 1; 45 | } 46 | 47 | fn thread_join_std_mem_domain(_context: zephyr::context::Kernel) { 48 | use zephyr::context::Kernel as C; 49 | zephyr::static_mem_domain!(rust_std_domain).add_thread::(C::k_current_get()); 50 | } 51 | 52 | #[no_mangle] 53 | pub extern "C" fn rust_second_thread( 54 | _a: *const c_void, 55 | _b: *const c_void, 56 | _c: *const c_void, 57 | ) { 58 | thread_join_std_mem_domain(zephyr::context::Kernel); 59 | 60 | println!("Hello from second thread"); 61 | 62 | TLS.with(|f| { 63 | println!("second thread: f = {}", *f.borrow()); 64 | assert!(*f.borrow() == 1); 65 | *f.borrow_mut() = 55; 66 | println!("second thread: now f = {}", *f.borrow()); 67 | assert!(*f.borrow() == 55); 68 | }); 69 | 70 | // Let thread 1 access TLS after we have already set it. Value should not be seen on thread 1 71 | TLS_SEM.give::(); 72 | } 73 | 74 | #[no_mangle] 75 | pub extern "C" fn rust_main() { 76 | use zephyr::context::Kernel as Context; 77 | 78 | println!("Hello from Rust on Zephyr {} via println!", zephyr::KERNEL_VERSION); 79 | zephyr::kernel::k_str_out("Hello from Rust kernel with direct kernel call\n"); 80 | zephyr::any::k_str_out("Hello from Rust kernel with runtime-detect syscall\n"); 81 | 82 | std::thread::sleep(Duration::from_millis(1)); 83 | println!("Time {:?}", zephyr::any::k_uptime_ticks().as_millis()); 84 | println!("Time {:?}", std::time::Instant::now()); 85 | 86 | let current = Context::k_current_get(); 87 | current.k_object_access_grant::(&MUTEX); 88 | current.k_object_access_grant::(&TLS_SEM); 89 | mutex_test(); 90 | std_mutex_test(); 91 | 92 | if let Some(_device) = Context::device_get_binding(cstr!("nonexistent")) { 93 | println!("Got device"); 94 | } else { 95 | println!("No device"); 96 | } 97 | 98 | { 99 | let boxed = Box::new(1u8); 100 | println!("Boxed value {}", boxed); 101 | } 102 | 103 | // test std::ops::{Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive} 104 | { 105 | let a: [u8; 4] = [1, 2, 3, 4]; 106 | let len = a.iter().len(); 107 | for _ in &a[0..len] {} 108 | for _ in &a[0..=(len - 1)] {} 109 | for _ in &a[..] {} 110 | for _ in &a[0..] {} 111 | for _ in &a[..len] {} 112 | for _ in &a[..=(len - 1)] {} 113 | } 114 | 115 | TLS_SEM.take::(); 116 | assert!(!TLS_SEM.try_take::()); 117 | TLS.with(|f| { 118 | println!("main thread: f = {}", *f.borrow()); 119 | assert!(*f.borrow() == 1); 120 | *f.borrow_mut() = 2; 121 | println!("main thread: now f = {}", *f.borrow()); 122 | assert!(*f.borrow() == 2); 123 | }); 124 | TLS_SEM.give::(); 125 | 126 | thread_join_std_mem_domain(Context); 127 | zephyr::kernel::k_thread_user_mode_enter(|| { 128 | use zephyr::context::User as Context; 129 | 130 | zephyr::user::k_str_out("Hello from Rust userspace with forced user-mode syscall\n"); 131 | 132 | mutex_test(); 133 | std_mutex_test(); 134 | 135 | zephyr_logger::init(LevelFilter::Info); 136 | 137 | trace!("TEST: trace!()"); 138 | debug!("TEST: debug!()"); 139 | info!("TEST: info!()"); 140 | warn!("TEST: warn!()"); 141 | error!("TEST: error!()"); 142 | 143 | assert!(TLS_SEM.try_take::()); 144 | TLS.with(|f| { 145 | println!("main thread: f = {}", *f.borrow()); 146 | assert!(*f.borrow() == 2); 147 | *f.borrow_mut() = 3; 148 | println!("main thread: now f = {}", *f.borrow()); 149 | assert!(*f.borrow() == 3); 150 | }); 151 | 152 | zephyr::user::k_str_out("Hello from Rust userspace with forced user-mode syscall\n"); 153 | 154 | zephyr::any::k_str_out("Hello from Rust userspace with runtime-detect syscall\nNext call will crash if userspace is working.\n"); 155 | 156 | // This will compile, but crash if CONFIG_USERSPACE is working 157 | zephyr::kernel::k_str_out("Hello from Rust userspace with direct kernel call\n"); 158 | }); 159 | } 160 | -------------------------------------------------------------------------------- /samples/rust-app/src/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #if KERNEL_VERSION_MAJOR < 3 3 | #include 4 | #else 5 | #include 6 | #endif 7 | 8 | #define MY_STACK_SIZE 1024 9 | #define MY_PRIORITY 5 10 | 11 | extern void rust_main(void); 12 | extern void rust_second_thread(void *, void *, void *); 13 | 14 | K_THREAD_DEFINE(my_tid, MY_STACK_SIZE, 15 | rust_second_thread, NULL, NULL, NULL, 16 | MY_PRIORITY, 0, 0); 17 | 18 | void main(void) 19 | { 20 | rust_main(); 21 | } 22 | -------------------------------------------------------------------------------- /samples/serial/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | 3 | cmake_minimum_required(VERSION 3.13.1) 4 | 5 | # For this example, add ZEPHYR_RUST as a zephyr module. Not needed if added in 6 | # your environment or managed by west. 7 | get_filename_component(ZEPHYR_RUST ${CMAKE_CURRENT_SOURCE_DIR}/../.. ABSOLUTE) 8 | list(APPEND ZEPHYR_EXTRA_MODULES ${ZEPHYR_RUST}) 9 | 10 | list(APPEND QEMU_EXTRA_FLAGS -serial tcp:localhost:4444,server,nowait) 11 | include($ENV{ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE) 12 | project(hello_world) 13 | target_sources(app PRIVATE ./src/main.c) 14 | -------------------------------------------------------------------------------- /samples/serial/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "app" 3 | version = "0.1.0" 4 | authors = ["Tyler Hall "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | futures = "0.3.1" 9 | zephyr = { path = "../../rust/zephyr" } 10 | zephyr-macros = { path = "../../rust/zephyr-macros" } 11 | zephyr-futures = { path = "../../rust/zephyr-futures" } 12 | zephyr-uart-buffered = { path = "../../rust/zephyr-uart-buffered" } 13 | -------------------------------------------------------------------------------- /samples/serial/README.rst: -------------------------------------------------------------------------------- 1 | Build 2 | ===== 3 | 4 | .. code-block:: console 5 | 6 | mkdir -p build-x86 && cd build-x86 7 | cmake -GNinja -DBOARD=qemu_x86 .. 8 | ninja 9 | 10 | Run 11 | === 12 | 13 | .. code-block:: console 14 | ninja run 15 | 16 | CMakeLists.txt adds the following line to the qemu command: 17 | 18 | .. code-block:: console 19 | -serial tcp:localhost:4444,server,nowait 20 | 21 | You can then connect to localhost:4444 using nc to simulate the other end of 22 | the serial connection. 23 | 24 | Of course, you can change the -serial argument to any other option that qemu 25 | supports. 26 | -------------------------------------------------------------------------------- /samples/serial/prj.conf: -------------------------------------------------------------------------------- 1 | CONFIG_MAIN_STACK_SIZE=2048 2 | CONFIG_NATIVE_UART_0_ON_OWN_PTY=y 3 | CONFIG_RUST_ALLOC_POOL=y 4 | CONFIG_RUST=y 5 | CONFIG_SERIAL=y 6 | CONFIG_UART_NATIVE_POSIX=y 7 | CONFIG_UART_INTERRUPT_DRIVEN=y 8 | CONFIG_USERSPACE=y 9 | CONFIG_UART_BUFFERED=y 10 | CONFIG_QEMU_ICOUNT=n 11 | -------------------------------------------------------------------------------- /samples/serial/src/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate zephyr_sys; 2 | 3 | use std::time::Duration; 4 | 5 | use futures::io::BufReader; 6 | use futures::task::LocalSpawnExt; 7 | use futures::{AsyncBufReadExt, AsyncWriteExt, StreamExt}; 8 | 9 | use zephyr_futures::delay::Delay; 10 | use zephyr_futures::Executor; 11 | use zephyr_sys::raw::{uart_buffered_rx_handle, uart_buffered_tx_handle}; 12 | use zephyr_uart_buffered::{UartBufferedRx, UartBufferedTx}; 13 | 14 | zephyr_macros::k_mutex_define!(EXECUTOR_MUTEX); 15 | zephyr_macros::k_poll_signal_define!(EXECUTOR_SIGNAL); 16 | 17 | async fn echo(rx: R, mut tx: W) { 18 | let mut lines = rx.lines(); 19 | while let Some(line) = lines.next().await { 20 | let line = line.unwrap(); 21 | println!("got line: {}", line); 22 | println!("sleeping"); 23 | Delay::new(Duration::from_secs(1)).await; 24 | let line = line.into_bytes(); 25 | tx.write_all(&line).await.unwrap(); 26 | } 27 | } 28 | 29 | #[no_mangle] 30 | pub extern "C" fn rust_main(rx: uart_buffered_rx_handle, tx: uart_buffered_tx_handle) { 31 | use zephyr::context::Kernel as C; 32 | 33 | let rx = unsafe { UartBufferedRx::new(rx) }; 34 | let rx = BufReader::with_capacity(32, rx.into_async()); 35 | 36 | let tx = unsafe { UartBufferedTx::new(tx) }.into_async(); 37 | 38 | let mut executor = unsafe { Executor::new(&EXECUTOR_MUTEX, &EXECUTOR_SIGNAL) }; 39 | executor.spawn_local(echo(rx, tx)).unwrap(); 40 | executor.run::(); 41 | } 42 | -------------------------------------------------------------------------------- /samples/serial/src/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | extern void rust_main(struct uart_buffered_rx_handle rx, 4 | struct uart_buffered_tx_handle tx); 5 | 6 | static FIFO_DEFINE(test_uart_buffered_rx, 16); 7 | static FIFO_DEFINE(test_uart_buffered_tx, 16); 8 | UART_BUFFERED_DEFINE(test_uart, test_uart_buffered_rx, test_uart_buffered_tx); 9 | 10 | void main(void) 11 | { 12 | struct uart_buffered_rx_handle rx; 13 | struct uart_buffered_tx_handle tx; 14 | 15 | const struct device *uart = device_get_binding("UART_1"); 16 | 17 | if (!uart) { 18 | printk("Failed to get uart\n"); 19 | return; 20 | } 21 | 22 | UART_BUFFERED_INIT(&test_uart, uart); 23 | #if CONFIG_USERSPACE 24 | uart_buffered_access_grant(&test_uart, k_current_get()); 25 | #endif 26 | rx = uart_buffered_rx_handle(&test_uart); 27 | tx = uart_buffered_tx_handle(&test_uart); 28 | 29 | rust_main(rx, tx); 30 | } 31 | -------------------------------------------------------------------------------- /scripts/gen_syscalls.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # Copyright (c) 2017 Intel Corporation 4 | # 5 | # SPDX-License-Identifier: Apache-2.0 6 | 7 | """ 8 | Script to generate system call wrappers for FFI 9 | 10 | This script parses the system call metadata JSON file emitted by 11 | parse_syscalls.py to create two files: 12 | 13 | - A header that includes all the individual generated syscall headers. 14 | 15 | - A C file defining wrapper functions for every syscall that do nothing but 16 | pass through the arguments and return value. This allows creating real 17 | symbols for each syscall that can be called through FFI. 18 | """ 19 | 20 | import sys 21 | import re 22 | import argparse 23 | import os 24 | import json 25 | 26 | syscall_template = """ 27 | /* auto-generated by gen_syscalls.py, don't edit */ 28 | 29 | #include 30 | 31 | #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) 32 | #pragma GCC diagnostic push 33 | #endif 34 | 35 | #ifdef __GNUC__ 36 | #pragma GCC diagnostic ignored "-Wstrict-aliasing" 37 | #endif 38 | 39 | %s 40 | 41 | #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) 42 | #pragma GCC diagnostic pop 43 | #endif 44 | """ 45 | 46 | typename_regex = re.compile(r'(.*?)([A-Za-z0-9_]+)$') 47 | 48 | 49 | class SyscallParseException(Exception): 50 | pass 51 | 52 | 53 | def typename_split(item): 54 | if "[" in item: 55 | raise SyscallParseException( 56 | "Please pass arrays to syscalls as pointers, unable to process '%s'" % 57 | item) 58 | 59 | if "(" in item: 60 | raise SyscallParseException( 61 | "Please use typedefs for function pointers") 62 | 63 | mo = typename_regex.match(item) 64 | if not mo: 65 | raise SyscallParseException("Malformed system call invocation") 66 | 67 | m = mo.groups() 68 | return (m[0].strip(), m[1]) 69 | 70 | def wrapper_defs(func_name, func_type, args,): 71 | decl_arglist = ", ".join([" ".join(argrec) for argrec in args]) 72 | 73 | syscall_list = "extern %s z_anyctx_%s(%s);\n" % (func_type, func_name, decl_arglist) 74 | syscall_list += "#ifdef CONFIG_USERSPACE\n" 75 | syscall_list += "extern %s z_kernelctx_%s(%s);\n" % (func_type, func_name, decl_arglist) 76 | syscall_list += "extern %s z_userctx_%s(%s);\n" % (func_type, func_name, decl_arglist) 77 | syscall_list += "#endif\n" 78 | 79 | wrap = "#if defined(__ZEPHYR_SUPERVISOR__)\n" 80 | wrap += "%s z_kernelctx_%s(%s)\n" % (func_type, func_name, decl_arglist) 81 | wrap += "#elif defined(__ZEPHYR_USER__)\n" 82 | wrap += "%s z_userctx_%s(%s)\n" % (func_type, func_name, decl_arglist) 83 | wrap += "#else\n" 84 | wrap += "%s z_anyctx_%s(%s)\n" % (func_type, func_name, decl_arglist) 85 | wrap += "#endif\n" 86 | wrap += "{\n" 87 | 88 | impl_arglist = ", ".join([argrec[1] for argrec in args]) 89 | impl_call = "%s(%s)" % (func_name, impl_arglist) 90 | wrap += "\t" + "%s%s;\n" % ("return " if func_type != "void" else "", 91 | impl_call) 92 | wrap += "}\n" 93 | 94 | return wrap, syscall_list 95 | 96 | def analyze_fn(match_group): 97 | func, args = match_group 98 | 99 | try: 100 | if args == "void": 101 | args = [] 102 | else: 103 | args = [typename_split(a.strip()) for a in args.split(",")] 104 | 105 | func_type, func_name = typename_split(func) 106 | except SyscallParseException: 107 | sys.stderr.write("In declaration of %s\n" % func) 108 | raise 109 | 110 | sys_id = "K_SYSCALL_" + func_name.upper() 111 | 112 | return wrapper_defs(func_name, func_type, args) 113 | 114 | def parse_args(): 115 | global args 116 | parser = argparse.ArgumentParser( 117 | description=__doc__, 118 | formatter_class=argparse.RawDescriptionHelpFormatter) 119 | 120 | parser.add_argument("-i", "--json-file", required=True, 121 | help="Read syscall information from json file") 122 | parser.add_argument("-t", "--thunks", required=True, 123 | help="Output file for thunk definitions") 124 | parser.add_argument("-a", "--all-syscalls", required=True, 125 | help="Output file for all syscalls header") 126 | args = parser.parse_args() 127 | 128 | 129 | def main(): 130 | parse_args() 131 | 132 | with open(args.json_file, 'r') as fd: 133 | syscalls = json.load(fd) 134 | 135 | invocations = [] 136 | declarations = [] 137 | includes = set() 138 | 139 | # whitelist: syscall groups for which to generate bindings. Zephyr 140 | # generates syscalls/foo.h for each foo.h in this list. 141 | 142 | # includes: headers that app code would include to call syscalls. Those 143 | # define other necessary data types and usually include the corresponding 144 | # syscalls/foo.h. But there are odd cases like clock_gettime() on posix 145 | # where the syscall header is not included, so we must not include the 146 | # syscall header directly. 147 | whitelist = set(["kernel.h", "kobject.h", "device.h", "uart.h", "mutex.h", "errno_private.h", "eeprom.h", "time.h"]) 148 | includes = ["kernel.h", "device.h", "drivers/uart.h", "sys/mutex.h", "sys/errno_private.h", "drivers/eeprom.h", "posix/time.h"] 149 | 150 | for match_group, fn in [i[:2] for i in syscalls]: 151 | if fn not in whitelist: 152 | continue 153 | include = "syscalls/%s" % fn 154 | invocation, declaration = analyze_fn(match_group) 155 | invocations.append(invocation) 156 | declarations.append(declaration) 157 | 158 | os.makedirs(os.path.dirname(args.thunks), exist_ok=True) 159 | os.makedirs(os.path.dirname(args.all_syscalls), exist_ok=True) 160 | 161 | with open(args.all_syscalls, "w") as fp: 162 | fp.write("#ifndef ZEPHYR_ALL_SYSCALLS_H\n") 163 | fp.write("#define ZEPHYR_ALL_SYSCALLS_H\n") 164 | fp.write("#include \n") 165 | fp.write("#if KERNEL_VERSION_MAJOR < 3\n") 166 | fp.write("\n".join(["#include <%s>" % fn for fn in includes])) 167 | fp.write("\n#else\n") 168 | fp.write("\n".join(["#include " % fn for fn in includes])) 169 | fp.write("\n#endif\n") 170 | fp.write("\n") 171 | # Hack because z_sys_mutex_kernel_lock is not defined in sys/mutex.h for !USERSPACE 172 | # This is the same on all zephyr versions as it exists in include/generated 173 | fp.write("#include \n") 174 | fp.write("\n") 175 | fp.write("".join(declarations)) 176 | fp.write("\n#endif\n") 177 | 178 | thunks = syscall_template % ("\n\n".join(invocations)) 179 | with open(args.thunks, "w") as fp: 180 | fp.write(thunks) 181 | 182 | if __name__ == "__main__": 183 | main() 184 | -------------------------------------------------------------------------------- /syscall-thunk-any.c: -------------------------------------------------------------------------------- 1 | #include 2 | -------------------------------------------------------------------------------- /syscall-thunk-kernel.c: -------------------------------------------------------------------------------- 1 | #define __ZEPHYR_SUPERVISOR__ 2 | #include 3 | -------------------------------------------------------------------------------- /syscall-thunk-user.c: -------------------------------------------------------------------------------- 1 | #define __ZEPHYR_USER__ 2 | #include 3 | -------------------------------------------------------------------------------- /tests/eeprom/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | 3 | cmake_minimum_required(VERSION 3.13.1) 4 | 5 | # For this example, add ZEPHYR_RUST as a zephyr module. Not needed if added in 6 | # your environment or managed by west. 7 | get_filename_component(ZEPHYR_RUST ${CMAKE_CURRENT_SOURCE_DIR}/../.. ABSOLUTE) 8 | list(APPEND ZEPHYR_EXTRA_MODULES ${ZEPHYR_RUST}) 9 | 10 | include($ENV{ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE) 11 | project(eeprom_api) 12 | 13 | target_sources(app PRIVATE ./src/main.c) 14 | -------------------------------------------------------------------------------- /tests/eeprom/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "app" 3 | version = "0.1.0" 4 | authors = ["Tyler Hall "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | zephyr = { path = "../../rust/zephyr" } 9 | -------------------------------------------------------------------------------- /tests/eeprom/prj.conf: -------------------------------------------------------------------------------- 1 | CONFIG_ZTEST=y 2 | CONFIG_MAIN_STACK_SIZE=2048 3 | CONFIG_HEAP_MEM_POOL_SIZE=1024 4 | CONFIG_EEPROM=y 5 | CONFIG_RUST=y 6 | -------------------------------------------------------------------------------- /tests/eeprom/prj_qemu_x86.conf: -------------------------------------------------------------------------------- 1 | CONFIG_ZTEST=y 2 | CONFIG_MAIN_STACK_SIZE=2048 3 | CONFIG_HEAP_MEM_POOL_SIZE=1024 4 | CONFIG_EEPROM=y 5 | CONFIG_EEPROM_SIMULATOR=y 6 | CONFIG_RUST=y 7 | -------------------------------------------------------------------------------- /tests/eeprom/src/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate zephyr_sys; 2 | 3 | use std::convert::TryInto; 4 | use std::ffi::CStr; 5 | 6 | use zephyr::device::DeviceSyscalls; 7 | use zephyr::eeprom::Eeprom; 8 | 9 | #[no_mangle] 10 | pub extern "C" fn test_main() { 11 | use zephyr::context::Any as C; 12 | 13 | let eeprom = unsafe { 14 | let device = C::device_get_binding(CStr::from_bytes_with_nul_unchecked( 15 | zephyr_sys::raw::DT_N_S_eeprom_P_label, 16 | )) 17 | .expect("get eeprom"); 18 | Eeprom::new(device) 19 | }; 20 | 21 | let write = [1, 2, 3, 4]; 22 | let mut read = [0; 4]; 23 | 24 | let size = eeprom.size::(); 25 | println!("EEPROM size {}", size); 26 | // Out of bounds read 27 | assert_eq!( 28 | eeprom 29 | .read::(size.try_into().unwrap(), &mut read) 30 | .unwrap_err() 31 | .kind(), 32 | std::io::ErrorKind::InvalidInput 33 | ); 34 | 35 | eeprom.read::(0, &mut read).expect("read"); 36 | println!("Initial: {:?}", &read); 37 | eeprom.write::(0, &write).expect("write"); 38 | eeprom.read::(0, &mut read).expect("read"); 39 | println!("After write: {:?}", &read); 40 | assert_eq!(&read, &write); 41 | } 42 | -------------------------------------------------------------------------------- /tests/eeprom/src/main.c: -------------------------------------------------------------------------------- 1 | // Empty 2 | -------------------------------------------------------------------------------- /tests/eeprom/testcase.yaml: -------------------------------------------------------------------------------- 1 | tests: 2 | rust.eeprom: 3 | platform_whitelist: qemu_x86 4 | tags: drivers userspace 5 | -------------------------------------------------------------------------------- /tests/posix-clock/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | 3 | cmake_minimum_required(VERSION 3.13.1) 4 | 5 | # For this example, add ZEPHYR_RUST as a zephyr module. Not needed if added in 6 | # your environment or managed by west. 7 | get_filename_component(ZEPHYR_RUST ${CMAKE_CURRENT_SOURCE_DIR}/../.. ABSOLUTE) 8 | list(APPEND ZEPHYR_EXTRA_MODULES ${ZEPHYR_RUST}) 9 | 10 | include($ENV{ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE) 11 | project(rust) 12 | target_sources(app PRIVATE ./src/main.c) 13 | -------------------------------------------------------------------------------- /tests/posix-clock/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | [[package]] 4 | name = "rust" 5 | version = "0.1.0" 6 | 7 | -------------------------------------------------------------------------------- /tests/posix-clock/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "app" 3 | version = "0.1.0" 4 | authors = ["Tyler Hall "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | -------------------------------------------------------------------------------- /tests/posix-clock/prj.conf: -------------------------------------------------------------------------------- 1 | CONFIG_RUST=y 2 | CONFIG_ZTEST=y 3 | CONFIG_HEAP_MEM_POOL_SIZE=1024 4 | CONFIG_POSIX_CLOCK=y 5 | -------------------------------------------------------------------------------- /tests/posix-clock/src/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate zephyr_core; 2 | extern crate zephyr_sys; 3 | 4 | #[no_mangle] 5 | pub extern "C" fn test_main() { 6 | std::thread::sleep(std::time::Duration::from_secs(1)); 7 | 8 | let ts = zephyr_core::kernel::clock_gettime(); 9 | println!("sec: {} nsec {}", ts.tv_sec, ts.tv_nsec); 10 | zephyr_core::kernel::clock_settime(ts); 11 | } 12 | -------------------------------------------------------------------------------- /tests/posix-clock/src/main.c: -------------------------------------------------------------------------------- 1 | // Empty 2 | -------------------------------------------------------------------------------- /tests/posix-clock/testcase.yaml: -------------------------------------------------------------------------------- 1 | tests: 2 | rust.posix-clock: 3 | platform_whitelist: qemu_x86 qemu_cortex_m3 native_posix 4 | tags: rust 5 | -------------------------------------------------------------------------------- /tests/rust/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | 3 | cmake_minimum_required(VERSION 3.13.1) 4 | 5 | # For this example, add ZEPHYR_RUST as a zephyr module. Not needed if added in 6 | # your environment or managed by west. 7 | get_filename_component(ZEPHYR_RUST ${CMAKE_CURRENT_SOURCE_DIR}/../.. ABSOLUTE) 8 | list(APPEND ZEPHYR_EXTRA_MODULES ${ZEPHYR_RUST}) 9 | 10 | include($ENV{ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE) 11 | project(rust) 12 | target_sources(app PRIVATE ./src/main.c) 13 | -------------------------------------------------------------------------------- /tests/rust/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | [[package]] 4 | name = "rust" 5 | version = "0.1.0" 6 | 7 | -------------------------------------------------------------------------------- /tests/rust/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "app" 3 | version = "0.1.0" 4 | authors = ["Tyler Hall "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | -------------------------------------------------------------------------------- /tests/rust/prj.conf: -------------------------------------------------------------------------------- 1 | CONFIG_RUST=y 2 | CONFIG_ZTEST=y 3 | CONFIG_HEAP_MEM_POOL_SIZE=1024 4 | -------------------------------------------------------------------------------- /tests/rust/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[no_mangle] 2 | pub extern "C" fn test_main() { 3 | println!("Hello, world!"); 4 | } 5 | -------------------------------------------------------------------------------- /tests/rust/src/main.c: -------------------------------------------------------------------------------- 1 | // Empty 2 | -------------------------------------------------------------------------------- /tests/rust/testcase.yaml: -------------------------------------------------------------------------------- 1 | tests: 2 | rust.main: 3 | platform_whitelist: qemu_x86 qemu_cortex_m3 native_posix 4 | tags: rust 5 | -------------------------------------------------------------------------------- /tests/semaphore/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | 3 | cmake_minimum_required(VERSION 3.13.1) 4 | 5 | # For this example, add ZEPHYR_RUST as a zephyr module. Not needed if added in 6 | # your environment or managed by west. 7 | get_filename_component(ZEPHYR_RUST ${CMAKE_CURRENT_SOURCE_DIR}/../.. ABSOLUTE) 8 | list(APPEND ZEPHYR_EXTRA_MODULES ${ZEPHYR_RUST}) 9 | 10 | include($ENV{ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE) 11 | project(rust) 12 | target_sources(app PRIVATE ./src/main.c) 13 | -------------------------------------------------------------------------------- /tests/semaphore/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | [[package]] 4 | name = "app" 5 | version = "0.1.0" 6 | 7 | -------------------------------------------------------------------------------- /tests/semaphore/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "app" 3 | version = "0.1.0" 4 | authors = ["Tyler Hall "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | zephyr = { path = "../../rust/zephyr" } 9 | zephyr-macros = { path = "../../rust/zephyr-macros" } 10 | -------------------------------------------------------------------------------- /tests/semaphore/prj.conf: -------------------------------------------------------------------------------- 1 | CONFIG_RUST=y 2 | CONFIG_ZTEST=y 3 | CONFIG_HEAP_MEM_POOL_SIZE=1024 4 | -------------------------------------------------------------------------------- /tests/semaphore/src/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate libc; 2 | extern crate zephyr; 3 | extern crate zephyr_macros; 4 | 5 | use libc::c_void; 6 | use zephyr::context::Kernel as C; 7 | use zephyr::semaphore::*; 8 | 9 | zephyr_macros::k_sem_define!(TEST_SEM, 0, 10); 10 | 11 | #[no_mangle] 12 | pub extern "C" fn rust_sem_thread(_a: *const c_void, _b: *const c_void, _c: *const c_void) { 13 | for i in 0..10 { 14 | println!("Giving {}", i); 15 | TEST_SEM.give::(); 16 | } 17 | } 18 | 19 | #[no_mangle] 20 | pub extern "C" fn rust_test_main() { 21 | for i in 0..10 { 22 | println!("Taking {}", i); 23 | TEST_SEM.take::(); 24 | println!("Took {}", i); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /tests/semaphore/src/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | extern void rust_test_main(void); 4 | extern void rust_sem_thread(void *, void *, void *); 5 | 6 | K_THREAD_DEFINE(sem_thread, 1024, rust_sem_thread, NULL, NULL, NULL, 7 | K_LOWEST_APPLICATION_THREAD_PRIO, 0, 0); 8 | 9 | void test_main(void) 10 | { 11 | rust_test_main(); 12 | } 13 | -------------------------------------------------------------------------------- /tests/semaphore/testcase.yaml: -------------------------------------------------------------------------------- 1 | tests: 2 | rust.semaphore: 3 | platform_whitelist: qemu_x86 qemu_cortex_m3 native_posix 4 | tags: rust 5 | -------------------------------------------------------------------------------- /uart-buffered/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | if(CONFIG_UART_BUFFERED) 2 | zephyr_library() 3 | zephyr_library_sources(src/uart_buffered.c src/uart_buffered_api.c) 4 | endif() 5 | zephyr_include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src) 6 | -------------------------------------------------------------------------------- /uart-buffered/src/uart_buffered.c: -------------------------------------------------------------------------------- 1 | #define __ZEPHYR_SUPERVISOR__ 2 | 3 | #include 4 | 5 | #if KERNEL_VERSION_MAJOR < 3 6 | #include 7 | #else 8 | #include 9 | #endif 10 | 11 | #include "uart_buffered.h" 12 | 13 | LOG_MODULE_REGISTER(uart_buffered); 14 | 15 | /* TX interrupt handler */ 16 | static void uart_buffered_tx(struct uart_buffered_tx *uart) 17 | { 18 | struct fifo_handle *fifo = &uart->fifo; 19 | bool disable_irq = true; 20 | 21 | while (!fifo_empty(fifo)) { 22 | uint8_t c = fifo_peek(fifo); 23 | if (uart_fifo_fill(fifo->device, &c, 1) == 1) { 24 | fifo_pop(fifo); 25 | } else { 26 | /* Still want to send more (fifo not empty) */ 27 | disable_irq = false; 28 | break; 29 | } 30 | compiler_barrier(); /* Should be a CPU barrier on SMP, but no Zephyr API */ 31 | } 32 | 33 | if (disable_irq) { 34 | uart_irq_tx_disable(fifo->device); 35 | } 36 | 37 | /* Wake the writer if the fifo is half full or less */ 38 | if (fifo_used(fifo) <= fifo_capacity(fifo) / 2) { 39 | k_poll_signal_raise(fifo->signal, 0); 40 | } 41 | } 42 | 43 | void uart_buffered_rx_timeout(struct k_timer *timer) 44 | { 45 | struct k_poll_signal *signal = k_timer_user_data_get(timer); 46 | LOG_DBG("rx wake on timer"); 47 | k_poll_signal_raise(signal, 0); 48 | } 49 | 50 | /* RX interrupt handler */ 51 | static void uart_buffered_rx(struct uart_buffered_rx *uart) 52 | { 53 | struct fifo_handle *fifo = &uart->fifo; 54 | bool disable_irq = true; 55 | 56 | while (!fifo_full(fifo)) { 57 | uint8_t c; 58 | if (uart_fifo_read(fifo->device, &c, 1) == 1) { 59 | fifo_push(fifo, c); 60 | LOG_DBG("uart byte 0x%x write = %d read = %d used = %d\n", 61 | c, fifo->fifo->write, 62 | fifo->fifo->read, 63 | fifo->fifo->write - fifo->fifo->read); 64 | } else { 65 | /* Still want to receive more (fifo not full) */ 66 | disable_irq = false; 67 | break; 68 | } 69 | } 70 | 71 | if (disable_irq) { 72 | LOG_DBG("disable rx irq"); 73 | uart_irq_rx_disable(fifo->device); 74 | } 75 | 76 | if (fifo_used(fifo) >= fifo_capacity(fifo) / 2) { 77 | /* Wake reader now if more than half full */ 78 | k_timer_stop(uart->timer); 79 | LOG_DBG("rx wake"); 80 | k_poll_signal_raise(fifo->signal, 0); 81 | } else if (!fifo_empty(fifo)) { 82 | /* Make sure reader is woken eventually if any data is available */ 83 | LOG_DBG("rx timer start"); 84 | k_timer_start(uart->timer, K_MSEC(1), Z_TIMEOUT_NO_WAIT); 85 | } 86 | } 87 | 88 | /* UART IRQ handler (RX and TX) */ 89 | void uart_buffered_irq(struct device *uart, struct uart_buffered_rx *rx_fifo, 90 | struct uart_buffered_tx *tx_fifo) 91 | { 92 | while (uart_irq_update(uart) && uart_irq_is_pending(uart)) { 93 | int err = uart_err_check(uart); 94 | if (err & UART_ERROR_OVERRUN) { 95 | LOG_ERR("overrun"); 96 | } 97 | if (err & UART_ERROR_FRAMING) { 98 | LOG_ERR("framing"); 99 | } 100 | if (uart_irq_rx_ready(uart)) { 101 | __ASSERT(uart == rx_fifo->fifo.device, 102 | "mismatched uart and fifo"); 103 | uart_buffered_rx(rx_fifo); 104 | } 105 | if (uart_irq_tx_ready(uart)) { 106 | __ASSERT(uart == tx_fifo->fifo.device, 107 | "mismatched uart and fifo"); 108 | uart_buffered_tx(tx_fifo); 109 | } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /uart-buffered/src/uart_buffered.h: -------------------------------------------------------------------------------- 1 | #ifndef __UART_BUFFERED_H__ 2 | #define __UART_BUFFERED_H__ 3 | 4 | #include 5 | #if KERNEL_VERSION_MAJOR < 3 6 | #include 7 | #include 8 | #include 9 | #else 10 | #include 11 | #include 12 | #endif 13 | 14 | typedef uint16_t fifo_index_t; 15 | struct fifo { 16 | fifo_index_t write; 17 | fifo_index_t read; 18 | uint8_t buf[]; 19 | }; 20 | 21 | #define FIFO_DEFINE(name, size) \ 22 | uint8_t name[offsetof(struct fifo, buf) + (size)]; \ 23 | BUILD_ASSERT(((size) & ((size)-1)) == 0, \ 24 | "fifo size must be a power of 2") 25 | 26 | #define FIFO_CAPACITY(name) (sizeof(name) - offsetof(struct fifo, buf)) 27 | 28 | /* 29 | * Copyable set of references needed to operate on the fifo. The kernel owns 30 | * the primary copy in kernel memory which can't be modififed by un-trusted 31 | * code. This can be copied for access by user space if the thread is granted 32 | * access to the kernel objects and the fifo memory. 33 | */ 34 | struct fifo_handle { 35 | struct fifo *fifo; 36 | /* capacity - 1. Capacity must be power of 2 */ 37 | size_t capacity_mask; 38 | struct device *device; 39 | struct k_poll_signal *signal; 40 | }; 41 | 42 | /* New type to prevent mixing rx and tx functions */ 43 | struct uart_buffered_rx_handle { 44 | struct fifo_handle fifo; 45 | }; 46 | 47 | /* New type to prevent mixing rx and tx functions */ 48 | struct uart_buffered_tx_handle { 49 | struct fifo_handle fifo; 50 | }; 51 | 52 | static inline size_t fifo_capacity(struct fifo_handle *fifo) 53 | { 54 | return fifo->capacity_mask + 1; 55 | } 56 | 57 | static inline size_t fifo_used(struct fifo_handle *fifo) 58 | { 59 | return (fifo_index_t)(fifo->fifo->write - fifo->fifo->read); 60 | } 61 | 62 | static inline bool fifo_full(struct fifo_handle *fifo) 63 | { 64 | return fifo_used(fifo) >= fifo_capacity(fifo); 65 | } 66 | 67 | static inline bool fifo_empty(struct fifo_handle *fifo) 68 | { 69 | return fifo_used(fifo) == 0; 70 | } 71 | 72 | static inline void fifo_push(struct fifo_handle *fifo, uint8_t val) 73 | { 74 | __ASSERT(!fifo_full(fifo), "push to full fifo"); 75 | fifo->fifo->buf[fifo->fifo->write & fifo->capacity_mask] = val; 76 | compiler_barrier(); /* Should be a CPU barrier on SMP, but no Zephyr API */ 77 | fifo->fifo->write++; 78 | } 79 | 80 | static inline uint8_t fifo_peek(struct fifo_handle *fifo) 81 | { 82 | __ASSERT(!fifo_empty(fifo), "peek from empty fifo"); 83 | return fifo->fifo->buf[fifo->fifo->read & fifo->capacity_mask]; 84 | } 85 | 86 | static inline uint8_t fifo_pop(struct fifo_handle *fifo) 87 | { 88 | __ASSERT(!fifo_empty(fifo), "pop from empty fifo"); 89 | uint8_t ret = fifo->fifo->buf[fifo->fifo->read & fifo->capacity_mask]; 90 | compiler_barrier(); /* Should be a CPU barrier on SMP, but no Zephyr API */ 91 | fifo->fifo->read++; 92 | return ret; 93 | } 94 | 95 | /* Kernel memory storage for kobjects and the kernel's fifo handle */ 96 | struct uart_buffered_rx { 97 | struct fifo_handle fifo; 98 | struct k_poll_signal signal; 99 | struct k_timer *const timer; 100 | }; 101 | 102 | /* Kernel memory storage for kobjects and the kernel's fifo handle */ 103 | struct uart_buffered_tx { 104 | struct fifo_handle fifo; 105 | struct k_poll_signal signal; 106 | }; 107 | 108 | struct uart_buffered { 109 | struct uart_buffered_rx rx; 110 | struct uart_buffered_tx tx; 111 | }; 112 | 113 | static inline struct uart_buffered_rx_handle 114 | uart_buffered_rx_handle(struct uart_buffered *uart) 115 | { 116 | struct uart_buffered_rx_handle handle; 117 | 118 | handle.fifo = uart->rx.fifo; 119 | 120 | return handle; 121 | } 122 | 123 | static inline struct uart_buffered_tx_handle 124 | uart_buffered_tx_handle(struct uart_buffered *uart) 125 | { 126 | struct uart_buffered_tx_handle handle; 127 | 128 | handle.fifo = uart->tx.fifo; 129 | 130 | return handle; 131 | } 132 | 133 | #define FIFO_INITIALIZER(fifo_name, signal_name) \ 134 | { \ 135 | .fifo = (struct fifo *)&fifo_name, \ 136 | .capacity_mask = FIFO_CAPACITY(fifo_name) - 1, .device = NULL, \ 137 | .signal = signal_name, \ 138 | } 139 | 140 | /* 141 | * Devices can't have user data, so this function passes the static fifo 142 | * pointers for this specific fifo to the generic IRQ function. 143 | */ 144 | #define UART_FIFO_IRQ_DEFINE(name, rx, tx) \ 145 | static void name##_irq(struct device *uart) \ 146 | { \ 147 | uart_buffered_irq(uart, rx, tx); \ 148 | } 149 | 150 | #define UART_BUFFERED_DEFINE(name, rx_fifo, tx_fifo) \ 151 | K_TIMER_DEFINE(name##_timer, uart_buffered_rx_timeout, NULL); \ 152 | struct uart_buffered name = \ 153 | { .rx = \ 154 | { \ 155 | .fifo = FIFO_INITIALIZER(rx_fifo, \ 156 | &name.rx.signal), \ 157 | .timer = &name##_timer, \ 158 | }, \ 159 | .tx = { \ 160 | .fifo = FIFO_INITIALIZER(tx_fifo, &name.tx.signal), \ 161 | } }; \ 162 | UART_FIFO_IRQ_DEFINE(name, &name.rx, &name.tx) 163 | 164 | #define UART_BUFFERED_INIT(name, uart) \ 165 | uart_buffered_init(name, uart, name##_irq) 166 | 167 | /* Invoked by macros */ 168 | void uart_buffered_rx_timeout(struct k_timer *timer); 169 | void uart_buffered_irq(struct device *uart, struct uart_buffered_rx *rx_fifo, 170 | struct uart_buffered_tx *tx_fifo); 171 | 172 | /* API */ 173 | int uart_buffered_write_nb(struct uart_buffered_tx_handle *tx, const uint8_t *buf, 174 | size_t len); 175 | void uart_buffered_write(struct uart_buffered_tx_handle *tx, const uint8_t *buf, 176 | size_t len); 177 | int uart_buffered_read_nb(struct uart_buffered_rx_handle *rx, uint8_t *buf, 178 | size_t len); 179 | size_t uart_buffered_read(struct uart_buffered_rx_handle *rx, uint8_t *buf, 180 | size_t len); 181 | 182 | static inline void uart_buffered_rx_access_grant(struct uart_buffered_rx *fifo, 183 | struct k_thread *thread) 184 | { 185 | k_object_access_grant(&fifo->signal, thread); 186 | } 187 | 188 | static inline void uart_buffered_tx_access_grant(struct uart_buffered_tx *fifo, 189 | struct k_thread *thread) 190 | { 191 | k_object_access_grant(&fifo->signal, thread); 192 | } 193 | 194 | static inline void uart_buffered_access_grant(struct uart_buffered *uart, 195 | struct k_thread *thread) 196 | { 197 | uart_buffered_rx_access_grant(&uart->rx, thread); 198 | uart_buffered_tx_access_grant(&uart->tx, thread); 199 | k_object_access_grant(uart->rx.fifo.device, thread); 200 | } 201 | 202 | static inline void fifo_handle_init(struct fifo_handle *fifo, struct device *device) 203 | { 204 | fifo->device = device; 205 | k_poll_signal_init(fifo->signal); 206 | } 207 | 208 | static inline void uart_buffered_rx_init(struct uart_buffered_rx *fifo, 209 | struct device *uart) 210 | { 211 | fifo_handle_init(&fifo->fifo, uart); 212 | k_timer_user_data_set(fifo->timer, &fifo->signal); 213 | } 214 | 215 | static inline void uart_buffered_tx_init(struct uart_buffered_tx *fifo, 216 | struct device *uart) 217 | { 218 | fifo_handle_init(&fifo->fifo, uart); 219 | } 220 | 221 | static inline void uart_buffered_init(struct uart_buffered *buffered, struct device *uart, 222 | void (*irq_handler)(struct device *uart)) 223 | { 224 | uint8_t c; 225 | 226 | uart_irq_rx_disable(uart); 227 | uart_irq_tx_disable(uart); 228 | 229 | while (uart_fifo_read(uart, &c, 1)) { 230 | }; 231 | 232 | uart_buffered_rx_init(&buffered->rx, uart); 233 | uart_buffered_tx_init(&buffered->tx, uart); 234 | 235 | #if ZEPHYR_VERSION_CODE >= ZEPHYR_VERSION(2, 4, 0) 236 | uart_irq_callback_set(uart, (uart_irq_callback_user_data_t)irq_handler); 237 | #else 238 | uart_irq_callback_set(uart, irq_handler); 239 | #endif 240 | uart_irq_err_enable(uart); 241 | uart_irq_rx_enable(uart); 242 | } 243 | 244 | #endif 245 | -------------------------------------------------------------------------------- /uart-buffered/src/uart_buffered_api.c: -------------------------------------------------------------------------------- 1 | #include "uart_buffered.h" 2 | 3 | static void k_poll_signal_wait(struct k_poll_signal *signal) 4 | { 5 | struct k_poll_event event; 6 | 7 | k_poll_event_init(&event, K_POLL_TYPE_SIGNAL, K_POLL_MODE_NOTIFY_ONLY, 8 | signal); 9 | k_poll(&event, 1, K_FOREVER); 10 | k_poll_signal_reset(signal); 11 | } 12 | 13 | int uart_buffered_write_nb(struct uart_buffered_tx_handle *tx, const uint8_t *buf, 14 | size_t len) 15 | { 16 | fifo_index_t current_read; 17 | struct fifo_handle *fifo = &tx->fifo; 18 | fifo_index_t last_read = fifo->fifo->read; 19 | bool was_empty = fifo_empty(fifo); 20 | int pos = 0; 21 | 22 | compiler_barrier(); /* Should be a CPU barrier on SMP, but no Zephyr API */ 23 | 24 | while (pos < len) { 25 | if (fifo_full(fifo)) { 26 | if (pos == 0) 27 | pos = -EAGAIN; 28 | break; 29 | } 30 | fifo_push(fifo, buf[pos++]); 31 | } 32 | 33 | compiler_barrier(); /* Should be a CPU barrier on SMP, but no Zephyr API */ 34 | 35 | /* 36 | * To avoid making a syscall on every write, determine if it's possible the tx irq is disabled. 37 | * - If fifo is non-empty, we might need to enable 38 | * - If the fifo was observed empty before we added something, we need to 39 | * enable because the transition to fifo empty would have disabled it. 40 | * - If the fifo was changed by the irq handler between observations, we can't 41 | * be sure if it became empty in the handler and was disabled, so we must 42 | * enable it. 43 | */ 44 | current_read = fifo->fifo->read; 45 | if (!fifo_empty(fifo) && (was_empty || last_read != current_read)) { 46 | uart_irq_tx_enable(fifo->device); 47 | } 48 | 49 | return pos; 50 | } 51 | 52 | void uart_buffered_write(struct uart_buffered_tx_handle *tx, const uint8_t *buf, 53 | size_t len) 54 | { 55 | struct fifo_handle *fifo = &tx->fifo; 56 | 57 | while (len) { 58 | int ret = uart_buffered_write_nb(tx, buf, len); 59 | if (ret < 0) { 60 | k_poll_signal_wait(fifo->signal); 61 | continue; 62 | } 63 | buf += ret; 64 | len -= ret; 65 | } 66 | } 67 | 68 | int uart_buffered_read_nb(struct uart_buffered_rx_handle *rx, uint8_t *buf, 69 | size_t len) 70 | { 71 | fifo_index_t current_write; 72 | struct fifo_handle *fifo = &rx->fifo; 73 | fifo_index_t last_write = fifo->fifo->write; 74 | bool was_full = fifo_full(fifo); 75 | int pos = 0; 76 | 77 | compiler_barrier(); /* Should be a CPU barrier on SMP, but no Zephyr API */ 78 | 79 | while (pos < len) { 80 | if (fifo_empty(fifo)) { 81 | if (pos == 0) 82 | pos = -EAGAIN; 83 | break; 84 | } 85 | buf[pos++] = fifo_pop(fifo); 86 | } 87 | 88 | compiler_barrier(); /* Should be a CPU barrier on SMP, but no Zephyr API */ 89 | 90 | /* 91 | * To avoid making a syscall on every read, determine if it's possible the rx irq is disabled. 92 | * - If fifo is not full, we might need to enable 93 | * - If the fifo was observed full before we added something, we need to 94 | * enable because the transition to fifo full would have disabled it. 95 | * - If the fifo was changed by the irq handler between observations, we can't 96 | * be sure if it became full in the handler and was disabled, so we must 97 | * enable it. 98 | */ 99 | current_write = fifo->fifo->write; 100 | if (!fifo_full(fifo) && (was_full || last_write != current_write)) { 101 | uart_irq_rx_enable(fifo->device); 102 | } 103 | 104 | return pos; 105 | } 106 | 107 | size_t uart_buffered_read(struct uart_buffered_rx_handle *rx, uint8_t *buf, 108 | size_t len) 109 | { 110 | struct fifo_handle *fifo = &rx->fifo; 111 | uint8_t *orig_buf = buf; 112 | 113 | while (len) { 114 | int ret = uart_buffered_read_nb(rx, buf, len); 115 | if (ret < 0) { 116 | if (buf != orig_buf) { 117 | /* Return if we have anything */ 118 | break; 119 | } else { 120 | /* Wait until we have something */ 121 | k_poll_signal_wait(fifo->signal); 122 | continue; 123 | } 124 | } 125 | buf += ret; 126 | len -= ret; 127 | } 128 | 129 | return buf - orig_buf; 130 | } 131 | -------------------------------------------------------------------------------- /zephyr-bindgen/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /zephyr-bindgen/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "zephyr-bindgen" 3 | version = "0.1.0" 4 | authors = ["Steven Walter "] 5 | edition = "2018" 6 | description = "Zephyr syscall bindings generator for Rust" 7 | license = "Apache-2.0" 8 | repository = "https://github.com/tylerwhall/zephyr-rust.git" 9 | homepage = "https://github.com/tylerwhall/zephyr-rust" 10 | 11 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 12 | 13 | [dependencies] 14 | bindgen = "0.69" 15 | -------------------------------------------------------------------------------- /zephyr-bindgen/LICENSE-Apache-2.0: -------------------------------------------------------------------------------- 1 | ../LICENSE -------------------------------------------------------------------------------- /zephyr-bindgen/src/main.rs: -------------------------------------------------------------------------------- 1 | extern crate bindgen; 2 | 3 | use std::env; 4 | use std::fs::File; 5 | use std::io::Write; 6 | use std::path::PathBuf; 7 | use std::sync::{Arc, Mutex}; 8 | 9 | use bindgen::callbacks::ParseCallbacks; 10 | 11 | #[derive(Debug, Default)] 12 | struct CallbacksInner { 13 | /// List of all syscalls found from bindgen 14 | syscalls: Vec, 15 | } 16 | #[derive(Clone, Debug, Default)] 17 | struct Callbacks(Arc>); 18 | 19 | impl ParseCallbacks for Callbacks { 20 | fn item_name(&self, name: &str) -> Option { 21 | const PREFIX: &str = "z_anyctx_"; 22 | let mut inner = self.0.lock().unwrap(); 23 | if name.starts_with(PREFIX) { 24 | inner.syscalls.push(name[PREFIX.len()..].into()); 25 | } 26 | None 27 | } 28 | } 29 | 30 | fn main() { 31 | let flags = env::var("TARGET_CFLAGS").unwrap_or("".to_string()); 32 | eprintln!("cflags: {}", flags); 33 | let userspace = env::var("CONFIG_USERSPACE").expect("CONFIG_USERSPACE must be set") == "y"; 34 | eprintln!("userspace: {}", userspace); 35 | 36 | let callbacks = Callbacks::default().clone(); 37 | // The bindgen::Builder is the main entry point 38 | // to bindgen, and lets you build up options for 39 | // the resulting bindings. 40 | let bindings = bindgen::Builder::default() 41 | // The input header we would like to generate 42 | // bindings for. 43 | .header("wrapper.h") 44 | .use_core() 45 | .ctypes_prefix("libc") 46 | .parse_callbacks(Box::new(callbacks.clone())) 47 | // XXX: doesn't handle args with spaces in quotes 48 | .clang_args(flags.split(" ")) 49 | .opaque_type("z_x86_thread_stack_header") 50 | // Finish the builder and generate the bindings. 51 | .generate() 52 | // Unwrap the Result and panic on failure. 53 | .expect("Unable to generate bindings"); 54 | 55 | // Write the bindings to the $OUT_DIR/bindings.rs file. 56 | let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); 57 | bindings 58 | .write_to_file(out_path.join("bindings.rs")) 59 | .expect("Couldn't write bindings!"); 60 | 61 | // Namespace aliases to syscalls by their valid contexts 62 | let mut out = File::create(out_path.join("syscalls.rs")).unwrap(); 63 | let syscalls = callbacks.0.lock().unwrap(); 64 | 65 | writeln!(&mut out, "pub mod any {{").unwrap(); 66 | for syscall in syscalls.syscalls.iter() { 67 | writeln!( 68 | &mut out, 69 | " pub use super::super::raw::z_anyctx_{} as {};", 70 | syscall, syscall 71 | ) 72 | .unwrap(); 73 | } 74 | writeln!(&mut out, "}}").unwrap(); 75 | writeln!(&mut out, "pub mod user {{").unwrap(); 76 | if userspace { 77 | // If userspace enabled, output each userspace-context syscall here 78 | for syscall in syscalls.syscalls.iter() { 79 | writeln!( 80 | &mut out, 81 | " pub use super::super::raw::z_userctx_{} as {};", 82 | syscall, syscall 83 | ) 84 | .unwrap(); 85 | } 86 | } else { 87 | // Else, import all the kernel functions since they can be called directly 88 | writeln!(&mut out, "pub use super::any::*;").unwrap(); 89 | } 90 | writeln!(&mut out, "}}").unwrap(); 91 | writeln!(&mut out, "pub mod kernel {{").unwrap(); 92 | if userspace { 93 | // If userspace, put the kernel-specific functions in the root of the module 94 | for syscall in syscalls.syscalls.iter() { 95 | writeln!( 96 | &mut out, 97 | " pub use super::super::raw::z_kernelctx_{} as {};", 98 | syscall, syscall 99 | ) 100 | .unwrap(); 101 | } 102 | } else { 103 | // Else, import all kernel functions since they can be called directly 104 | writeln!(&mut out, "pub use super::any::*;").unwrap(); 105 | } 106 | writeln!(&mut out, "}}").unwrap(); 107 | } 108 | -------------------------------------------------------------------------------- /zephyr/module.yml: -------------------------------------------------------------------------------- 1 | build: 2 | cmake: . 3 | kconfig: Kconfig 4 | --------------------------------------------------------------------------------