diff --git a/.gitmodules b/.gitmodules index 8598f9fb..086fcbc4 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "rt-thread"] path = rt-thread - url = https://github.com/RT-Thread/rt-thread.git + url = git@github.com:RT-Thread/rt-thread.git diff --git a/machines/qemu-virt-riscv64/.config b/machines/qemu-virt-riscv64/.config index 67de44ee..7f4df395 100644 --- a/machines/qemu-virt-riscv64/.config +++ b/machines/qemu-virt-riscv64/.config @@ -155,7 +155,6 @@ CONFIG_RT_DEBUGING_ASSERT=y CONFIG_RT_DEBUGING_COLOR=y CONFIG_RT_DEBUGING_CONTEXT=y # CONFIG_RT_DEBUGING_AUTO_INIT is not set -# CONFIG_RT_DEBUGING_PAGE_LEAK is not set # CONFIG_RT_USING_CI_ACTION is not set # @@ -173,7 +172,6 @@ CONFIG_RT_USING_SIGNALS=y # # Memory Management # -CONFIG_RT_PAGE_MAX_ORDER=11 CONFIG_RT_USING_MEMPOOL=y # CONFIG_RT_USING_SMALL_MEM is not set CONFIG_RT_USING_SLAB=y @@ -501,6 +499,8 @@ CONFIG_RT_LWIP_USING_PING=y CONFIG_RT_USING_UTEST=y CONFIG_UTEST_THR_STACK_SIZE=4096 CONFIG_UTEST_THR_PRIORITY=20 +# CONFIG_RT_UTEST_USING_AUTO_RUN is not set +# CONFIG_RT_UTEST_USING_ALL_CASES is not set # CONFIG_RT_USING_VAR_EXPORT is not set CONFIG_RT_USING_RESOURCE_ID=y CONFIG_RT_USING_ADT=y @@ -513,6 +513,22 @@ CONFIG_RT_USING_ADT_REF=y # CONFIG_RT_USING_VBUS is not set +# +# Memory management +# +CONFIG_RT_PAGE_AFFINITY_BLOCK_SIZE=0x1000 +CONFIG_RT_PAGE_MAX_ORDER=11 +# CONFIG_RT_USING_MEMBLOCK is not set + +# +# Debugging +# +# CONFIG_RT_DEBUGGING_ALIASING is not set +# CONFIG_RT_DEBUGING_PAGE_LEAK is not set +# CONFIG_RT_DEBUGGING_PAGE_POISON is not set +# end of Debugging +# end of Memory management + # # Using USB legacy version # @@ -549,6 +565,7 @@ CONFIG_RT_USING_ADT_REF=y # CONFIG_PKG_USING_FREEMODBUS is not set # CONFIG_PKG_USING_NANOPB is not set # CONFIG_PKG_USING_WIFI_HOST_DRIVER is not set +# CONFIG_PKG_USING_ESP_HOSTED is not set # # Wi-Fi @@ -655,6 +672,8 @@ CONFIG_RT_USING_ADT_REF=y # CONFIG_PKG_USING_LHC_MODBUS is not set # CONFIG_PKG_USING_QMODBUS is not set # CONFIG_PKG_USING_PNET is not set +# CONFIG_PKG_USING_OPENER is not set +# CONFIG_PKG_USING_FREEMQTT is not set # end of IoT - internet of things # @@ -744,6 +763,7 @@ CONFIG_RT_USING_ADT_REF=y # tools packages # # CONFIG_PKG_USING_CMBACKTRACE is not set +# CONFIG_PKG_USING_MCOREDUMP is not set # CONFIG_PKG_USING_EASYFLASH is not set # CONFIG_PKG_USING_EASYLOGGER is not set # CONFIG_PKG_USING_SYSTEMVIEW is not set @@ -788,6 +808,8 @@ CONFIG_RT_USING_ADT_REF=y # CONFIG_PKG_USING_VOFA_PLUS is not set # CONFIG_PKG_USING_ZDEBUG is not set # CONFIG_PKG_USING_RVBACKTRACE is not set +# CONFIG_PKG_USING_HPATCHLITE is not set +# CONFIG_PKG_USING_THREAD_METRIC is not set # end of tools packages # @@ -816,7 +838,6 @@ CONFIG_RT_USING_ADT_REF=y # # CONFIG_PKG_USING_CMSIS_5 is not set # CONFIG_PKG_USING_CMSIS_CORE is not set -# CONFIG_PKG_USING_CMSIS_DSP is not set # CONFIG_PKG_USING_CMSIS_NN is not set # CONFIG_PKG_USING_CMSIS_RTOS1 is not set # CONFIG_PKG_USING_CMSIS_RTOS2 is not set @@ -849,7 +870,12 @@ CONFIG_RT_USING_ADT_REF=y # CONFIG_PKG_USING_LITTLEFS is not set # CONFIG_PKG_USING_DFS_JFFS2 is not set # CONFIG_PKG_USING_DFS_UFFS is not set -# CONFIG_PKG_USING_LWEXT4 is not set +CONFIG_PKG_USING_LWEXT4=y +CONFIG_PKG_LWEXT4_PATH="/packages/system/lwext4" +CONFIG_RT_USING_DFS_LWEXT4=y +CONFIG_PKG_USING_LWEXT4_LATEST_VERSION=y +# CONFIG_PKG_USING_LWEXT4_V110 is not set +CONFIG_PKG_LWEXT4_VER="latest" # CONFIG_PKG_USING_THREAD_POOL is not set # CONFIG_PKG_USING_ROBOTS is not set # CONFIG_PKG_USING_EV is not set @@ -868,7 +894,6 @@ CONFIG_RT_USING_ADT_REF=y # CONFIG_PKG_USING_ARM_2D is not set # CONFIG_PKG_USING_MCUBOOT is not set # CONFIG_PKG_USING_TINYUSB is not set -# CONFIG_PKG_USING_CHERRYUSB is not set # CONFIG_PKG_USING_KMULTI_RTIMER is not set # CONFIG_PKG_USING_TFDB is not set # CONFIG_PKG_USING_QPC is not set @@ -880,8 +905,10 @@ CONFIG_RT_USING_ADT_REF=y # CONFIG_PKG_USING_SFDB is not set # CONFIG_PKG_USING_RTP is not set # CONFIG_PKG_USING_REB is not set +# CONFIG_PKG_USING_RMP is not set # CONFIG_PKG_USING_R_RHEALSTONE is not set # CONFIG_PKG_USING_HEARTBEAT is not set +# CONFIG_PKG_USING_MICRO_ROS_RTTHREAD_PACKAGE is not set # end of system packages # @@ -895,12 +922,44 @@ CONFIG_RT_USING_ADT_REF=y # # STM32 HAL & SDK Drivers # +# CONFIG_PKG_USING_STM32F0_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32F0_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32F1_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32F1_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32F2_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32F2_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32F3_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32F3_CMSIS_DRIVER is not set # CONFIG_PKG_USING_STM32F4_HAL_DRIVER is not set # CONFIG_PKG_USING_STM32F4_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32F7_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32F7_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32G0_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32G0_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32G4_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32G4_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32H5_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32H5_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32H7_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32H7_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32H7RS_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32H7RS_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32L0_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32L0_CMSIS_DRIVER is not set # CONFIG_PKG_USING_STM32L4_HAL_DRIVER is not set # CONFIG_PKG_USING_STM32L4_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32L5_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32L5_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32U5_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32U5_CMSIS_DRIVER is not set # CONFIG_PKG_USING_STM32WB55_SDK is not set # CONFIG_PKG_USING_STM32_SDIO is not set +# CONFIG_PKG_USING_STM32WL_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32WL_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32WB_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32WB_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32MP1_M4_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32MP1_M4_CMSIS_DRIVER is not set # end of STM32 HAL & SDK Drivers # @@ -932,7 +991,87 @@ CONFIG_RT_USING_ADT_REF=y # CONFIG_PKG_USING_NRF5X_SDK is not set # CONFIG_PKG_USING_NRFX is not set # CONFIG_PKG_USING_NUCLEI_SDK is not set +# CONFIG_PKG_USING_RASPBERRYPI_PICO_RP2350_SDK is not set # CONFIG_PKG_USING_RASPBERRYPI_PICO_SDK is not set +# CONFIG_PKG_USING_MM32 is not set + +# +# WCH HAL & SDK Drivers +# +# CONFIG_PKG_USING_CH32V20x_SDK is not set +# CONFIG_PKG_USING_CH32V307_SDK is not set +# end of WCH HAL & SDK Drivers + +# +# AT32 HAL & SDK Drivers +# +# CONFIG_PKG_USING_AT32A403A_HAL_DRIVER is not set +# CONFIG_PKG_USING_AT32A403A_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_AT32A423_HAL_DRIVER is not set +# CONFIG_PKG_USING_AT32A423_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_AT32F45x_HAL_DRIVER is not set +# CONFIG_PKG_USING_AT32F45x_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_AT32F402_405_HAL_DRIVER is not set +# CONFIG_PKG_USING_AT32F402_405_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_AT32F403A_407_HAL_DRIVER is not set +# CONFIG_PKG_USING_AT32F403A_407_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_AT32F413_HAL_DRIVER is not set +# CONFIG_PKG_USING_AT32F413_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_AT32F415_HAL_DRIVER is not set +# CONFIG_PKG_USING_AT32F415_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_AT32F421_HAL_DRIVER is not set +# CONFIG_PKG_USING_AT32F421_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_AT32F423_HAL_DRIVER is not set +# CONFIG_PKG_USING_AT32F423_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_AT32F425_HAL_DRIVER is not set +# CONFIG_PKG_USING_AT32F425_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_AT32F435_437_HAL_DRIVER is not set +# CONFIG_PKG_USING_AT32F435_437_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_AT32M412_416_HAL_DRIVER is not set +# CONFIG_PKG_USING_AT32M412_416_CMSIS_DRIVER is not set +# end of AT32 HAL & SDK Drivers + +# +# HC32 DDL Drivers +# +# CONFIG_PKG_USING_HC32F3_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_HC32F3_SERIES_DRIVER is not set +# CONFIG_PKG_USING_HC32F4_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_HC32F4_SERIES_DRIVER is not set +# end of HC32 DDL Drivers + +# +# NXP HAL & SDK Drivers +# +# CONFIG_PKG_USING_NXP_MCX_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_NXP_MCX_SERIES_DRIVER is not set +# CONFIG_PKG_USING_NXP_LPC_DRIVER is not set +# CONFIG_PKG_USING_NXP_LPC55S_DRIVER is not set +# CONFIG_PKG_USING_NXP_IMX6SX_DRIVER is not set +# CONFIG_PKG_USING_NXP_IMX6UL_DRIVER is not set +# CONFIG_PKG_USING_NXP_IMXRT_DRIVER is not set +# end of NXP HAL & SDK Drivers + +# +# NUVOTON Drivers +# +# CONFIG_PKG_USING_NUVOTON_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_NUVOTON_SERIES_DRIVER is not set +# CONFIG_PKG_USING_NUVOTON_ARM926_LIB is not set +# end of NUVOTON Drivers + +# +# GD32 Drivers +# +# CONFIG_PKG_USING_GD32_ARM_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_GD32_ARM_SERIES_DRIVER is not set +# end of GD32 Drivers + +# +# HPMicro SDK +# +# CONFIG_PKG_USING_HPM_SDK is not set +# end of HPMicro SDK # end of HAL & SDK Drivers # @@ -970,12 +1109,14 @@ CONFIG_RT_USING_ADT_REF=y # CONFIG_PKG_USING_BMI088 is not set # CONFIG_PKG_USING_HMC5883 is not set # CONFIG_PKG_USING_MAX6675 is not set +# CONFIG_PKG_USING_MAX31855 is not set # CONFIG_PKG_USING_TMP1075 is not set # CONFIG_PKG_USING_SR04 is not set # CONFIG_PKG_USING_CCS811 is not set # CONFIG_PKG_USING_PMSXX is not set # CONFIG_PKG_USING_RT3020 is not set # CONFIG_PKG_USING_MLX90632 is not set +# CONFIG_PKG_USING_MLX90382 is not set # CONFIG_PKG_USING_MLX90393 is not set # CONFIG_PKG_USING_MLX90392 is not set # CONFIG_PKG_USING_MLX90394 is not set @@ -1004,6 +1145,9 @@ CONFIG_RT_USING_ADT_REF=y # CONFIG_PKG_USING_ICM20608 is not set # CONFIG_PKG_USING_PAJ7620 is not set # CONFIG_PKG_USING_STHS34PF80 is not set +# CONFIG_PKG_USING_P3T1755 is not set +# CONFIG_PKG_USING_QMI8658 is not set +# CONFIG_PKG_USING_ICM20948 is not set # end of sensors drivers # @@ -1095,6 +1239,11 @@ CONFIG_RT_USING_ADT_REF=y # CONFIG_PKG_USING_BT_MX02 is not set # CONFIG_PKG_USING_GC9A01 is not set # CONFIG_PKG_USING_IK485 is not set +# CONFIG_PKG_USING_SERVO is not set +# CONFIG_PKG_USING_SEAN_WS2812B is not set +# CONFIG_PKG_USING_IC74HC165 is not set +# CONFIG_PKG_USING_IST8310 is not set +# CONFIG_PKG_USING_ST7789_SPI is not set # CONFIG_PKG_USING_SPI_TOOLS is not set # end of peripheral libraries and drivers @@ -1111,6 +1260,7 @@ CONFIG_RT_USING_ADT_REF=y # CONFIG_PKG_USING_QUEST is not set # CONFIG_PKG_USING_NAXOS is not set # CONFIG_PKG_USING_R_TINYMAIX is not set +# CONFIG_PKG_USING_LLMCHAT is not set # end of AI packages # @@ -1181,6 +1331,7 @@ CONFIG_RT_USING_ADT_REF=y # CONFIG_PKG_USING_KI is not set # CONFIG_PKG_USING_ARMv7M_DWT is not set # CONFIG_PKG_USING_CRCLIB is not set +# CONFIG_PKG_USING_LIBCRC is not set # CONFIG_PKG_USING_LWGPS is not set # CONFIG_PKG_USING_STATE_MACHINE is not set # CONFIG_PKG_USING_DESIGN_PATTERN is not set @@ -1191,6 +1342,7 @@ CONFIG_RT_USING_ADT_REF=y # CONFIG_PKG_USING_SOEM is not set # CONFIG_PKG_USING_QPARAM is not set # CONFIG_PKG_USING_CorevMCU_CLI is not set +# CONFIG_PKG_USING_DRMP is not set # end of miscellaneous packages # @@ -1441,6 +1593,19 @@ CONFIG_BSP_USING_VIRTIO_GPU=y CONFIG_BSP_USING_VIRTIO_INPUT=y # end of RISC-V QEMU virt64 configs +CONFIG_RT_USING_RUST=y +# CONFIG_RUST_DEBUG_BUILD is not set +CONFIG_RUST_EXAMPLE_HELLO=y +CONFIG_RUST_EXAMPLE_MEMORY=y +CONFIG_RUST_EXAMPLE_STRING=y +CONFIG_RUST_EXAMPLE_PRINTF=y +CONFIG_RUST_EXAMPLE_THREAD=y +CONFIG_RUST_EXAMPLE_MUTEX=y +CONFIG_RUST_EXAMPLE_SEM=y +CONFIG_RUST_EXAMPLE_MQ=y +CONFIG_RUST_EXAMPLE_VEC=y +CONFIG_RUST_EXAMPLE_DL=y +CONFIG_RUST_INIT_COMPONENT=y CONFIG_BOARD_QEMU_VIRT_RV64=y CONFIG_ENABLE_FPU=y # CONFIG_ENABLE_VECTOR is not set diff --git a/machines/qemu-virt-riscv64/Kconfig b/machines/qemu-virt-riscv64/Kconfig index 98a4e778..61cfc28c 100644 --- a/machines/qemu-virt-riscv64/Kconfig +++ b/machines/qemu-virt-riscv64/Kconfig @@ -9,6 +9,7 @@ PKGS_DIR := packages source "$(RTT_DIR)/Kconfig" osource "$PKGS_DIR/Kconfig" rsource "driver/Kconfig" +rsource "rust/Kconfig" config BOARD_QEMU_VIRT_RV64 bool diff --git a/machines/qemu-virt-riscv64/rtconfig.h b/machines/qemu-virt-riscv64/rtconfig.h index 221ee5eb..acc05991 100644 --- a/machines/qemu-virt-riscv64/rtconfig.h +++ b/machines/qemu-virt-riscv64/rtconfig.h @@ -109,7 +109,6 @@ /* Memory Management */ -#define RT_PAGE_MAX_ORDER 11 #define RT_USING_MEMPOOL #define RT_USING_SLAB #define RT_USING_SLAB_AS_HEAP @@ -331,6 +330,16 @@ #define RT_USING_ADT_REF /* end of Utilities */ +/* Memory management */ + +#define RT_PAGE_AFFINITY_BLOCK_SIZE 0x1000 +#define RT_PAGE_MAX_ORDER 11 + +/* Debugging */ + +/* end of Debugging */ +/* end of Memory management */ + /* Using USB legacy version */ /* end of Using USB legacy version */ @@ -420,6 +429,9 @@ /* Micrium: Micrium software products porting for RT-Thread */ /* end of Micrium: Micrium software products porting for RT-Thread */ +#define PKG_USING_LWEXT4 +#define RT_USING_DFS_LWEXT4 +#define PKG_USING_LWEXT4_LATEST_VERSION /* end of system packages */ /* peripheral libraries and drivers */ @@ -437,6 +449,34 @@ /* Kendryte SDK */ /* end of Kendryte SDK */ + +/* WCH HAL & SDK Drivers */ + +/* end of WCH HAL & SDK Drivers */ + +/* AT32 HAL & SDK Drivers */ + +/* end of AT32 HAL & SDK Drivers */ + +/* HC32 DDL Drivers */ + +/* end of HC32 DDL Drivers */ + +/* NXP HAL & SDK Drivers */ + +/* end of NXP HAL & SDK Drivers */ + +/* NUVOTON Drivers */ + +/* end of NUVOTON Drivers */ + +/* GD32 Drivers */ + +/* end of GD32 Drivers */ + +/* HPMicro SDK */ + +/* end of HPMicro SDK */ /* end of HAL & SDK Drivers */ /* sensors drivers */ @@ -526,6 +566,18 @@ #define BSP_USING_VIRTIO_GPU #define BSP_USING_VIRTIO_INPUT /* end of RISC-V QEMU virt64 configs */ +#define RT_USING_RUST +#define RUST_EXAMPLE_HELLO +#define RUST_EXAMPLE_MEMORY +#define RUST_EXAMPLE_STRING +#define RUST_EXAMPLE_PRINTF +#define RUST_EXAMPLE_THREAD +#define RUST_EXAMPLE_MUTEX +#define RUST_EXAMPLE_SEM +#define RUST_EXAMPLE_MQ +#define RUST_EXAMPLE_VEC +#define RUST_EXAMPLE_DL +#define RUST_INIT_COMPONENT #define BOARD_QEMU_VIRT_RV64 #define ENABLE_FPU #define __STACKSIZE__ 16384 diff --git a/machines/qemu-virt-riscv64/rust/.gitignore b/machines/qemu-virt-riscv64/rust/.gitignore new file mode 100644 index 00000000..5f4fb259 --- /dev/null +++ b/machines/qemu-virt-riscv64/rust/.gitignore @@ -0,0 +1,37 @@ +# Rust build artifacts (now in build/rust) +/target/ +**/*.rs.bk +*.pdb + +# Cargo lock file (optional - uncomment if you want to exclude it) +# Cargo.lock + +# Build directories +/build/ +*.o +*.a +*.so +*.dylib +*.dll + +# IDE specific files +.idea/ +.vscode/ +*.swp +*.swo +*~ +.DS_Store + +# Debug files +*.dSYM/ +*.su +*.idb +*.pdb + +# Backup files +*.bak +*.tmp +*.temp + +# Log files +*.log \ No newline at end of file diff --git a/machines/qemu-virt-riscv64/rust/Cargo.lock b/machines/qemu-virt-riscv64/rust/Cargo.lock new file mode 100644 index 00000000..e09e0c01 --- /dev/null +++ b/machines/qemu-virt-riscv64/rust/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "rt-rust" +version = "0.1.0" diff --git a/machines/qemu-virt-riscv64/rust/Cargo.toml b/machines/qemu-virt-riscv64/rust/Cargo.toml new file mode 100644 index 00000000..b28bf74e --- /dev/null +++ b/machines/qemu-virt-riscv64/rust/Cargo.toml @@ -0,0 +1,37 @@ +[package] +name = "rt-rust" +version = "0.1.0" +edition = "2021" + +[lib] +name = "rt_rust" +crate-type = ["staticlib"] + +[dependencies] + +[features] +default = [] +smp = [] +example_hello = [] + +example_memory = [] +example_string = [] +example_printf = [] +example_thread = [] +example_mutex = [] +example_sem = [] +example_mq = [] + + +example_dl = [] +example_vec = [] +rt_using_dlopen = [] + +[profile.release] +lto = true +opt-level = "z" +codegen-units = 1 +panic = "abort" + +[profile.dev] +panic = "abort" \ No newline at end of file diff --git a/machines/qemu-virt-riscv64/rust/Kconfig b/machines/qemu-virt-riscv64/rust/Kconfig new file mode 100644 index 00000000..04227807 --- /dev/null +++ b/machines/qemu-virt-riscv64/rust/Kconfig @@ -0,0 +1,82 @@ +menuconfig RT_USING_RUST + bool "Enable Rust component support" + default n + help + Enable Rust programming language support for RT-Thread. + This allows you to write RT-Thread components using Rust. + +if RT_USING_RUST + + config RUST_DEBUG_BUILD + bool "Build Rust code in debug mode" + default n + help + Build Rust code with debug symbols and without optimizations. + This increases binary size but helps with debugging. + + config RUST_EXAMPLE_HELLO + bool "Enable hello example" + default y + help + Enable the basic hello world example function. + config RUST_EXAMPLE_MEMORY + bool "Enable memory operations examples" + default y + help + Enable memory allocation and operation examples. + + config RUST_EXAMPLE_STRING + bool "Enable string operations examples" + default y + help + Enable string manipulation examples. + + config RUST_EXAMPLE_PRINTF + bool "Enable printf examples" + default y + help + Enable formatted output examples. + + config RUST_EXAMPLE_THREAD + bool "Enable RT-Thread threading examples" + default y + help + Enable RT-Thread threading API examples. + + config RUST_EXAMPLE_MUTEX + bool "Enable mutex examples" + default y + help + Enable mutex examples. + + config RUST_EXAMPLE_SEM + bool "Enable semaphore examples" + default y + help + Enable semaphore examples. + + config RUST_EXAMPLE_MQ + bool "Enable message queue examples" + default y + help + Enable message queue examples. + + config RUST_EXAMPLE_VEC + bool "Enable vector examples" + default y + help + Enable vector examples. + + config RUST_EXAMPLE_DL + bool "Enable libdl examples" + default y + help + Enable libdl examples. + + config RUST_INIT_COMPONENT + bool "Auto-initialize Rust component" + default y + help + Automatically initialize Rust component during RT-Thread startup. + +endif \ No newline at end of file diff --git a/machines/qemu-virt-riscv64/rust/README.md b/machines/qemu-virt-riscv64/rust/README.md new file mode 100644 index 00000000..4025016a --- /dev/null +++ b/machines/qemu-virt-riscv64/rust/README.md @@ -0,0 +1,168 @@ +# RT-Thread Rust Component + +A general-purpose Rust component for RT-Thread RTOS, supporting automatic multi-architecture detection. + +## Features + +- **Multi-architecture support**: Automatically detects ARM, AArch64, and RISC-V target architectures +- **Zero configuration**: No manual platform setup required +- **Modular design**: Core modules and example code are clearly separated +- **RT-Thread integration**: Full access to RT-Thread kernel APIs + +## Project Structure + +``` +rust/ +├── Cargo.toml # Rust project configuration +├── src/ +│ ├── lib.rs # Main library entry point +│ ├── libc.rs # Standard C library bindings +│ ├── librt.rs # RT-Thread kernel API bindings +│ ├── init.rs # Component initialization +│ └── examples/ # Example demos +│ ├── hello.rs # Basic hello world +│ ├── printf_demo.rs # Printf formatting +│ ├── string_demo.rs # String operations +│ ├── memory_demo.rs # Memory management +│ ├── vec_demo.rs # Vec implementation +│ ├── thread_demo.rs # RT-Thread threads +│ └── dlmodule_demo.rs # Dynamic module loading +├── rust_cmd.c # MSH command registration +├── SConscript # Build script with auto-detection +├── Kconfig # Configuration options +``` + +## Quick Start + +### Prerequisites + +1. **Install Rust** +```bash +curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh +``` + +2. **Add target platforms** (according to your architecture) +```bash +# RISC-V64 (soft-float) +rustup target add riscv64imac-unknown-none-elf + +# ARM Cortex-M4 +rustup target add thumbv7em-none-eabi + +# For other targets, add the corresponding Rust target based on your toolchain/ABI +``` + +### Build + +```bash +# Enable Rust component in menuconfig +scons --menuconfig +# Navigate to: Rust Component Support → Enable + +# For dynamic modules, enable dynamic module config and file system: +# 1. RT-Thread online packages → system packages: enable lwext4 file system +# 2. RT-Thread Components → C/C++ and POSIX layer +# → POSIX (Portable Operating System Interface) layer +# → Enable dynamic module APIs, dlopen()/dlsym()/dlclose() etc + +# Build +scons + +# Clean +scons -c +``` + +## Supported Architectures + +| Architecture | Target | Auto Detection | +|--------------|--------|---------------| +| Cortex-M3 | thumbv7m-none-eabi | ✓ | +| Cortex-M4/M7 | thumbv7em-none-eabi | ✓ | +| Cortex-M4F/M7F | thumbv7em-none-eabihf | ✓ | +| ARMv7-A | armv7a-none-eabi | ✓ | +| AArch64 | aarch64-unknown-none | ✓ | +| RISC-V32 | riscv32ima[f]c-unknown-none-elf | ✓ | +| RISC-V64 | riscv64[gc/imac]-unknown-none-elf | ✓ | + +The build system automatically detects the correct target from RT-Thread configuration. + +## MSH Commands + +| Command | Description | +|---------|-------------| +| `rust_hello` | Print greeting message | +| `rust_add ` | Add two numbers | +| `rust_mul ` | Multiply two numbers | +| `rust_strlen ` | Calculate string length | +| `rust_printf_demo` | Printf formatting demo | +| `rust_memory_demo` | Memory operation demo | +| `rust_thread_demo` | Thread demo | +| `rust_vec_demo` | Vec container demo | +| `rust_dl_demo` | Dynamic module loading demo | + +## Configuration Options + +Configure via `menuconfig`: + +- `RT_USING_RUST` - Enable/disable Rust component +- `RUST_DEBUG_BUILD` - Build with debug symbols +- `RUST_EXAMPLE_*` - Enable specific examples +- `RUST_INIT_COMPONENT` - Auto-initialize at startup + +## Technical Details + +- **No-std**: Embedded-friendly `#![no_std]` environment +- **FFI**: Seamless C/Rust interoperability +- **Static linking**: Generates `.a` library files +- **Memory safety**: Compile-time guarantees from Rust +- **Zero cost**: Performance equivalent to C + +## Extending Components + +To add new features: + +1. Create a module in `src/` +2. Add an example in `src/examples/` +3. Register the command in `rust_cmd.c` + +## Application Scenarios + +Rust components are especially suitable for: + +- **Safety-critical code**: Leverage Rust's memory safety guarantees +- **Complex algorithms**: Utilize Rust's advanced abstractions +- **Device drivers**: Type-safe hardware abstraction +- **Network protocol stacks**: Safe packet processing +- **Cryptography libraries**: Secure implementations preventing memory leaks + +## Troubleshooting + +### Link Errors + +If you encounter "can't link double-float modules with soft-float modules" error: +- The build system should automatically detect the correct ABI +- Check if the compiler's `-mabi` flag matches the Rust target + +### Target Not Installed + +If prompted that the target is not installed: +```bash +rustup target add +``` + +### Detection Failure + +If target architecture cannot be detected: +- Check if RT-Thread configuration is correct +- Review compiler flags in rtconfig.py + +## License + +Apache-2.0 + +## References + +- [Rust Embedded Programming Book](https://docs.rust-embedded.org/) +- [RT-Thread Documentation Center](https://www.rt-thread.org/document/site/) +- [Rust FFI Programming](https://doc.rust-lang.org/nomicon/ffi.html) +- [RISC-V Specification](https://riscv.org/technical/specifications/) \ No newline at end of file diff --git a/machines/qemu-virt-riscv64/rust/README_cn.md b/machines/qemu-virt-riscv64/rust/README_cn.md new file mode 100644 index 00000000..190eda5e --- /dev/null +++ b/machines/qemu-virt-riscv64/rust/README_cn.md @@ -0,0 +1,168 @@ +# RT-Thread Rust 组件 + +RT-Thread RTOS 的通用 Rust 组件,支持多架构自动检测。 + +## 特性 + +- **多架构支持**:自动检测 ARM、AArch64 和 RISC-V 目标架构 +- **零配置**:无需手动配置目标平台 +- **模块化设计**:核心模块与示例代码清晰分离 +- **RT-Thread 集成**:完整访问 RT-Thread 内核 API + +## 项目结构 + +``` +rust/ +├── Cargo.toml # Rust 项目配置 +├── src/ +│ ├── lib.rs # 主库入口点 +│ ├── libc.rs # 标准 C 库绑定 +│ ├── librt.rs # RT-Thread 内核 API 绑定 +│ ├── init.rs # 组件初始化 +│ └── examples/ # 示例演示 +│ ├── hello.rs # 基础 hello world +│ ├── printf_demo.rs # Printf 格式化 +│ ├── string_demo.rs # 字符串操作 +│ ├── memory_demo.rs # 内存管理 +│ ├── vec_demo.rs # vec实现 +│ ├── thread_demo.rs # RT-Thread 线程 +│ └── dlmodule_demo.rs # 动态模块加载 +├── rust_cmd.c # MSH 命令注册 +├── SConscript # 带自动检测的构建脚本 +├── Kconfig # 配置选项 +``` + +## 快速开始 + +### 前置要求 + +1. **安装 Rust** +```bash +curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh +``` + +2. **添加目标平台**(根据您的架构) +```bash +# RISC-V64(软浮点) +rustup target add riscv64imac-unknown-none-elf + +# ARM Cortex-M4 +rustup target add thumbv7em-none-eabi + +# 其他目标请根据实际工具链/ABI 添加对应的 Rust target +``` + +### 构建 + +```bash +# 在 menuconfig 中启用 Rust 组件 +scons --menuconfig +# 导航至:Rust Component Support → Enable + +# 动态模块需要打开加载动态模块配置和文件系统: +# 1. RT-Thread online packages → system packages 打开lwext4文件系统 +# 2. RT-Thread Components → C/C++ and POSIX layer +# → POSIX (Portable Operating System Interface) layer +# → Enable dynamic module APIs, dlopen()/dlsym()/dlclose() etc + +# 构建 +scons + +# 清理 +scons -c +``` + +## 支持的架构 + +| 架构 | 目标 | 自动检测 | +|------|------|----------| +| Cortex-M3 | thumbv7m-none-eabi | ✓ | +| Cortex-M4/M7 | thumbv7em-none-eabi | ✓ | +| Cortex-M4F/M7F | thumbv7em-none-eabihf | ✓ | +| ARMv7-A | armv7a-none-eabi | ✓ | +| AArch64 | aarch64-unknown-none | ✓ | +| RISC-V32 | riscv32ima[f]c-unknown-none-elf | ✓ | +| RISC-V64 | riscv64[gc/imac]-unknown-none-elf | ✓ | + +构建系统会自动从 RT-Thread 配置中检测正确的目标。 + +## MSH 命令 + +| 命令 | 描述 | +|------|------| +| `rust_hello` | 打印问候信息 | +| `rust_add ` | 两数相加 | +| `rust_mul ` | 两数相乘 | +| `rust_strlen ` | 计算字符串长度 | +| `rust_printf_demo` | Printf 格式化演示 | +| `rust_memory_demo` | 内存操作演示 | +| `rust_thread_demo` | 线程演示 | +| `rust_vec_demo` | vec容器演示| +| `rust_dl_demo` | 动态模块加载演示| + +## 配置选项 + +通过 `menuconfig` 配置: + +- `RT_USING_RUST` - 启用/禁用 Rust 组件 +- `RUST_DEBUG_BUILD` - 使用调试符号构建 +- `RUST_EXAMPLE_*` - 启用特定示例 +- `RUST_INIT_COMPONENT` - 启动时自动初始化 + +## 技术细节 + +- **No-std**:嵌入式友好的 `#![no_std]` 环境 +- **FFI**:无缝的 C/Rust 互操作性 +- **静态链接**:生成 `.a` 库文件 +- **内存安全**:Rust 的编译时保证 +- **零成本**:性能等同于 C + +## 扩展组件 + +通过以下方式添加新功能: + +1. 在 `src/` 中创建模块 +2. 在 `src/examples/` 中添加示例 +3. 在 `rust_cmd.c` 中注册命令 + +## 应用场景 + +Rust 组件特别适合以下场景: + +- **安全关键代码**:利用 Rust 的内存安全保证 +- **复杂算法**:利用 Rust 的高级抽象能力 +- **设备驱动**:类型安全的硬件抽象 +- **网络协议栈**:安全的数据包处理 +- **加密库**:防止内存泄露的安全实现 + +## 故障排除 + +### 链接错误 + +如果遇到 "can't link double-float modules with soft-float modules" 错误: +- 构建系统应该自动检测正确的 ABI +- 检查编译器的 `-mabi` 标志是否与 Rust 目标匹配 + +### 目标未安装 + +如果提示目标未安装: +```bash +rustup target add <目标名称> +``` + +### 检测失败 + +如果无法检测目标架构: +- 检查 RT-Thread 配置是否正确 +- 查看 rtconfig.py 中的编译器标志 + +## 许可证 + +Apache-2.0 + +## 参考资料 + +- [Rust 嵌入式编程手册](https://docs.rust-embedded.org/) +- [RT-Thread 文档中心](https://www.rt-thread.org/document/site/) +- [Rust FFI 编程](https://doc.rust-lang.org/nomicon/ffi.html) +- [RISC-V 规范](https://riscv.org/technical/specifications/) \ No newline at end of file diff --git a/machines/qemu-virt-riscv64/rust/SConscript b/machines/qemu-virt-riscv64/rust/SConscript new file mode 100644 index 00000000..48fd8ac8 --- /dev/null +++ b/machines/qemu-virt-riscv64/rust/SConscript @@ -0,0 +1,79 @@ +import os +import sys +from building import * + +cwd = GetCurrentDir() + +# Import helper utilities +sys.path.append(os.path.join(cwd, 'tools')) +from build_support import ( + detect_rust_target, + make_rustflags, + collect_features, + verify_rust_toolchain, + ensure_rust_target_installed, + cargo_build_staticlib, + clean_rust_build, +) + + +def _has(sym: str) -> bool: + try: + return bool(GetDepend([sym])) + except Exception: + return bool(GetDepend(sym)) + + +# Not enabled? Return empty group early +if not _has('RT_USING_RUST'): + group = [] + Return('group') + + +# Source files – MSH command glue +src = ['rust_cmd.c'] +LIBS = [] +LIBPATH = [] + +if GetOption('clean'): + # Clean Rust artifacts if toolchain present + if verify_rust_toolchain(): + clean_rust_build(Dir('#').abspath) + else: + print('Warning: Rust toolchain not found, skipping Rust clean') +else: + if verify_rust_toolchain(): + import rtconfig + + target = detect_rust_target(_has, rtconfig) + if not target: + print('Error: Unable to detect Rust target; please check configuration') + else: + print(f'Detected Rust target: {target}') + + # Optional hint if target missing + ensure_rust_target_installed(target) + + # Build mode and features + debug = bool(_has('RUST_DEBUG_BUILD')) + features = collect_features(_has) + + rustflags = make_rustflags(rtconfig, target) + rust_lib = cargo_build_staticlib( + rust_dir=cwd, target=target, features=features, debug=debug, rustflags=rustflags + ) + + if rust_lib: + LIBS = ['rt_rust'] + LIBPATH = [os.path.dirname(rust_lib)] + print('Rust library linked successfully') + else: + print('Warning: Failed to build Rust library') + else: + print('Warning: Rust toolchain not found') + print('Please install Rust from https://rustup.rs') + +# Define component group for SCons +group = DefineGroup('rust', src, depend=['RT_USING_RUST'], LIBS=LIBS, LIBPATH=LIBPATH) + +Return('group') \ No newline at end of file diff --git a/machines/qemu-virt-riscv64/rust/rust_cmd.c b/machines/qemu-virt-riscv64/rust/rust_cmd.c new file mode 100644 index 00000000..b8fe373d --- /dev/null +++ b/machines/qemu-virt-riscv64/rust/rust_cmd.c @@ -0,0 +1,522 @@ +/* + * Copyright (c) 2006-2024, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2025-09-20 RT-Thread First version + * + * Description: Rust component MSH command registration + * Provides access interface to Rust modular APIs + */ + +#include +#include +#include +#include + +/* ============== Rust function declarations ============== */ + +/* Initialization */ +extern int rust_init(void); + +/* hello module */ +#ifdef RUST_EXAMPLE_HELLO +extern void rust_hello(void); +extern void rust_hello_with_name(const char *name); +extern void rust_hello_rust_style(void); +#endif + +/* printf_demo module */ +#ifdef RUST_EXAMPLE_PRINTF +extern void rust_printf_demo(void); +extern int rust_sprintf_demo(void); +#endif + +/* string_demo module */ +#ifdef RUST_EXAMPLE_STRING +extern size_t rust_strlen_demo(const char *s); +extern int rust_strcmp_demo(const char *s1, const char *s2); +extern void rust_strcpy_demo(void); +extern void rust_strcat_demo(void); +extern int rust_strstr_demo(const char *haystack, const char *needle); +extern void rust_string_demo_all(void); +#endif + +/* memory_demo module */ +#ifdef RUST_EXAMPLE_MEMORY +extern int rust_add(int a, int b); +extern int rust_multiply(int a, int b); +extern int rust_memcpy_test(void *dest, const void *src, size_t size); +extern void rust_memset_demo(void); +extern void rust_memcmp_demo(void); +extern void rust_malloc_demo(void); +extern void rust_rt_malloc_demo(void); +extern void rust_memory_demo_all(void); +#endif + +/* thread_demo module */ +#ifdef RUST_EXAMPLE_THREAD +extern void rust_thread_create_demo(void); +extern void rust_thread_self_demo(void); +extern void rust_thread_sleep_demo(void); +extern void rust_thread_wrapper_demo(void); +extern void rust_thread_concurrent_demo(void); +extern void rust_thread_demo_all(void); +#endif + +/* mutex_demo module */ +#ifdef RUST_EXAMPLE_MUTEX +extern void rust_mutex_basic_demo(void); +extern void rust_mutex_named_demo(void); +extern void rust_mutex_trylock_demo(void); +extern void rust_mutex_atomic_demo(void); +extern void rust_mutex_concurrent_demo(void); +extern void rust_mutex_types_demo(void); +extern void rust_mutex_demo_all(void); +#endif + +/* sem_demo module */ +#ifdef RUST_EXAMPLE_SEM +extern void rust_semaphore_basic_demo(void); +extern void rust_semaphore_producer_consumer_demo(void); +extern void rust_semaphore_multi_thread_demo(void); +extern void rust_semaphore_demo_all(void); +#endif + +/* mq_demo module */ +#ifdef RUST_EXAMPLE_MQ +extern void rust_mq_demo(void); +#endif + +/* vec_demo module */ +#ifdef RUST_EXAMPLE_VEC +extern void rust_vec_demo(void); +extern void rust_vec_demo_all(void); +#endif + +/* dl_demo module */ +#ifdef RUST_EXAMPLE_DL +extern void rust_dl_open_demo(void); +extern void rust_dl_sym_demo(void); +extern void rust_dl_call_demo(void); +extern void rust_dl_error_demo(void); +extern void rust_dl_demo_all(void); +#endif +/* ============== MSH command implementation ============== */ + +/* Basic command: hello */ +#ifdef RUST_EXAMPLE_HELLO +static int cmd_rust_hello(int argc, char **argv) +{ + if (argc == 1) + { + rust_hello(); + } + else if (argc == 2) + { + rust_hello_with_name(argv[1]); + } + else + { + printf("Usage: rust_hello [name]\n"); + } + return 0; +} +MSH_CMD_EXPORT_ALIAS(cmd_rust_hello, rust_hello, Rust hello command); +#endif + +/* Arithmetic command: add */ +#ifdef RUST_EXAMPLE_MEMORY +static int cmd_rust_add(int argc, char **argv) +{ + if (argc < 3) + { + printf("Usage: rust_add \n"); + printf("Example: rust_add 100 200\n"); + return -1; + } + + int a = atoi(argv[1]); + int b = atoi(argv[2]); + int result = rust_add(a, b); + printf("%d + %d = %d\n", a, b, result); + return 0; +} +MSH_CMD_EXPORT_ALIAS(cmd_rust_add, rust_add, Add two numbers using Rust); + +/* Arithmetic command: multiply */ +static int cmd_rust_mul(int argc, char **argv) +{ + if (argc < 3) + { + printf("Usage: rust_mul \n"); + printf("Example: rust_mul 10 20\n"); + return -1; + } + + int a = atoi(argv[1]); + int b = atoi(argv[2]); + int result = rust_multiply(a, b); + printf("%d * %d = %d\n", a, b, result); + return 0; +} +MSH_CMD_EXPORT_ALIAS(cmd_rust_mul, rust_mul, Multiply two numbers using Rust); +#endif /* RUST_EXAMPLE_MEMORY */ + +/* String command: strlen */ +#ifdef RUST_EXAMPLE_STRING +static int cmd_rust_strlen(int argc, char **argv) +{ + if (argc < 2) + { + printf("Usage: rust_strlen \n"); + return -1; + } + + size_t len = rust_strlen_demo(argv[1]); + printf("String '%s' length: %zu\n", argv[1], len); + return 0; +} +MSH_CMD_EXPORT_ALIAS(cmd_rust_strlen, rust_strlen, Calculate string length); + +/* String command: strcmp */ +static int cmd_rust_strcmp(int argc, char **argv) +{ + if (argc < 3) + { + printf("Usage: rust_strcmp \n"); + return -1; + } + + int result = rust_strcmp_demo(argv[1], argv[2]); + printf("strcmp('%s', '%s') = %d\n", argv[1], argv[2], result); + return 0; +} +MSH_CMD_EXPORT_ALIAS(cmd_rust_strcmp, rust_strcmp, Compare two strings); + +/* String demonstration */ +static int cmd_rust_string(int argc, char **argv) +{ + rust_string_demo_all(); + return 0; +} +MSH_CMD_EXPORT_ALIAS(cmd_rust_string, rust_string, Demonstrate string operations); +#endif /* RUST_EXAMPLE_STRING */ + +/* Memory demonstration */ +#ifdef RUST_EXAMPLE_MEMORY +static int cmd_rust_memory(int argc, char **argv) +{ + rust_memory_demo_all(); + return 0; +} +MSH_CMD_EXPORT_ALIAS(cmd_rust_memory, rust_memory, Demonstrate memory operations); +#endif + +/* Vector demonstration */ +#ifdef RUST_EXAMPLE_VEC +static int cmd_rust_vec(int argc, char **argv) +{ + rust_vec_demo(); + return 0; +} +MSH_CMD_EXPORT_ALIAS(cmd_rust_vec, rust_vec, Demonstrate vector operations); +#endif + +/* Thread demonstration */ +#ifdef RUST_EXAMPLE_THREAD +static int cmd_rust_thread(int argc, char **argv) +{ + if (argc == 1) + { + rust_thread_demo_all(); + } + else if (strcmp(argv[1], "create") == 0) + { + rust_thread_create_demo(); + } + else if (strcmp(argv[1], "self") == 0) + { + rust_thread_self_demo(); + } + else if (strcmp(argv[1], "sleep") == 0) + { + rust_thread_sleep_demo(); + } + else if (strcmp(argv[1], "wrapper") == 0) + { + rust_thread_wrapper_demo(); + } + else if (strcmp(argv[1], "concurrent") == 0) + { + rust_thread_concurrent_demo(); + } + else + { + printf("Usage: rust_thread [create|self|sleep|wrapper|concurrent]\n"); + printf(" Without arguments: run all demos\n"); + printf(" create - demonstrate thread creation\n"); + printf(" self - show current thread info\n"); + printf(" sleep - demonstrate thread sleep\n"); + printf(" wrapper - demonstrate Rust thread wrapper\n"); + printf(" concurrent - demonstrate multiple threads concurrent execution\n"); + } + return 0; +} +MSH_CMD_EXPORT_ALIAS(cmd_rust_thread, rust_thread, RT-Thread operations demo); +#endif + +/* Mutex demonstration */ +#ifdef RUST_EXAMPLE_MUTEX +static int cmd_rust_mutex(int argc, char **argv) +{ + if (argc == 1) + { + rust_mutex_demo_all(); + } + else if (strcmp(argv[1], "basic") == 0) + { + rust_mutex_basic_demo(); + } + else if (strcmp(argv[1], "named") == 0) + { + rust_mutex_named_demo(); + } + else if (strcmp(argv[1], "trylock") == 0) + { + rust_mutex_trylock_demo(); + } + else if (strcmp(argv[1], "atomic") == 0) + { + rust_mutex_atomic_demo(); + } + else if (strcmp(argv[1], "concurrent") == 0) + { + rust_mutex_concurrent_demo(); + } + else if (strcmp(argv[1], "types") == 0) + { + rust_mutex_types_demo(); + } + else + { + printf("Usage: rust_mutex [basic|named|trylock|atomic|concurrent|types]\n"); + printf(" basic - Basic mutex creation and lock/unlock\n"); + printf(" named - Named mutex example\n"); + printf(" trylock - Try lock with timeout example\n"); + printf(" atomic - Atomic mutex for interrupt context\n"); + printf(" concurrent - Multi-threaded mutex contention\n"); + printf(" types - Sleep vs Atomic mutex comparison\n"); + printf(" (no args) - Run all mutex demos\n"); + } + return 0; +} +MSH_CMD_EXPORT_ALIAS(cmd_rust_mutex, rust_mutex, RT-Thread mutex operations demo); +#endif + +/* sem_demo module */ +#ifdef RUST_EXAMPLE_SEM +static int cmd_rust_sem(int argc, char **argv) +{ + if (argc == 1) + { + rust_semaphore_demo_all(); + } + else if (strcmp(argv[1], "basic") == 0) + { + rust_semaphore_basic_demo(); + } + else if (strcmp(argv[1], "producer_consumer") == 0) + { + rust_semaphore_producer_consumer_demo(); + } + else if (strcmp(argv[1], "multi_thread") == 0) + { + rust_semaphore_multi_thread_demo(); + } + else + { + printf("Usage: rust_sem [basic|producer_consumer|multi_thread]\n"); + printf(" basic - Basic semaphore creation and wait/signal\n"); + printf(" producer_consumer - Producer-Consumer semaphore demo\n"); + printf(" multi_thread - Multiple threads synchronization demo\n"); + printf(" (no args) - Run all semaphore demos\n"); + } + return 0; +} +MSH_CMD_EXPORT_ALIAS(cmd_rust_sem, rust_sem, RT-Thread semaphore operations demo); +#endif +#ifdef RUST_EXAMPLE_MQ +static int cmd_rust_mq(int argc, char **argv) +{ + rust_mq_demo(); + return 0; +} +MSH_CMD_EXPORT_ALIAS(cmd_rust_mq, rust_mq, RT-Thread message queue operations demo); +#endif +/* dl_demo module */ +#ifdef RUST_EXAMPLE_DL +static int cmd_rust_dl(int argc, char **argv) +{ + if (argc == 1) + { + rust_dl_demo_all(); + } + else if (strcmp(argv[1], "open") == 0) + { + rust_dl_open_demo(); + } + else if (strcmp(argv[1], "sym") == 0) + { + rust_dl_sym_demo(); + } + else if (strcmp(argv[1], "call") == 0) + { + rust_dl_call_demo(); + } + else if (strcmp(argv[1], "error") == 0) + { + rust_dl_error_demo(); + } + else + { + printf("Usage: rust_dl [open|sym|call|error]\n"); + printf(" Without arguments: run all demos\n"); + printf(" open - demonstrate dlopen/dlclose\n"); + printf(" sym - demonstrate dlsym symbol resolution\n"); + printf(" call - demonstrate function calls through dlsym\n"); + printf(" error - demonstrate error handling\n"); + } + return 0; +} +MSH_CMD_EXPORT_ALIAS(cmd_rust_dl, rust_dl, Demonstrate libdl operations); +#endif +/* Printf demonstration */ +#ifdef RUST_EXAMPLE_PRINTF +static int cmd_rust_printf(int argc, char **argv) +{ + rust_printf_demo(); + rust_sprintf_demo(); + return 0; +} +MSH_CMD_EXPORT_ALIAS(cmd_rust_printf, rust_printf, Demonstrate printf operations); +#endif + +/* Comprehensive test command */ +static int cmd_rust_test(int argc, char **argv) +{ + printf("\n=== Rust Component Test Suite ===\n"); + + /* 1. Hello test */ + printf("\n1. Hello Test:\n"); + #ifdef RUST_EXAMPLE_HELLO + rust_hello(); + rust_hello_rust_style(); + #else + printf(" (hello example disabled)\n"); + #endif + + /* 2. Printf test */ + printf("\n2. Printf Test:\n"); + #ifdef RUST_EXAMPLE_PRINTF + rust_printf_demo(); + #else + printf(" (printf example disabled)\n"); + #endif + + /* 3. String test */ + printf("\n3. String Test:\n"); + #ifdef RUST_EXAMPLE_STRING + const char *test_str = "RT-Thread"; + printf(" strlen(\"%s\") = %zu\n", test_str, rust_strlen_demo(test_str)); + #else + printf(" (string example disabled)\n"); + #endif + + /* 4. Arithmetic test */ + printf("\n4. Arithmetic Test:\n"); + #ifdef RUST_EXAMPLE_MEMORY + printf(" 42 + 58 = %d\n", rust_add(42, 58)); + printf(" 10 * 20 = %d\n", rust_multiply(10, 20)); + #else + printf(" (memory/arithmetic example disabled)\n"); + #endif + + /* 5. Memory test */ + printf("\n5. Memory Test:\n"); + #ifdef RUST_EXAMPLE_MEMORY + char src[] = "Hello"; + char dest[10]; + if (rust_memcpy_test(dest, src, strlen(src) + 1)) + { + printf(" memcpy passed: '%s'\n", dest); + } + #else + printf(" (memory example disabled)\n"); + #endif + + printf("\n=== All tests completed ===\n"); + return 0; +} +MSH_CMD_EXPORT_ALIAS(cmd_rust_test, rust_test, Run Rust component test suite); + +/* Help command */ +static int cmd_rust_help(int argc, char **argv) +{ + printf("\nRust Component Commands:\n"); + #ifdef RUST_EXAMPLE_HELLO + printf(" rust_hello [name] - Say hello\n"); + #endif + #ifdef RUST_EXAMPLE_MEMORY + printf(" rust_add - Add two numbers\n"); + printf(" rust_mul - Multiply two numbers\n"); + printf(" rust_memory - Memory operations demo\n"); + #endif + #ifdef RUST_EXAMPLE_STRING + printf(" rust_strlen - Get string length\n"); + printf(" rust_strcmp - Compare strings\n"); + printf(" rust_string - String operations demo\n"); + #endif + #ifdef RUST_EXAMPLE_PRINTF + printf(" rust_printf - Printf operations demo\n"); + #endif + #ifdef RUST_EXAMPLE_THREAD + printf(" rust_thread [opt] - Thread operations demo\n"); + #endif + #ifdef RUST_EXAMPLE_MUTEX + printf(" rust_mutex [opt] - Mutex operations demo\n"); + #endif + #ifdef RUST_EXAMPLE_SEM + printf(" rust_sem [opt] - Semaphore operations demo\n"); + #endif + #ifdef RUST_EXAMPLE_MQ + printf(" rust_mq - Message queue operations demo\n"); + #endif + printf(" rust_test - Run test suite\n"); + printf(" rust_help - Show this help\n"); + #ifdef RUST_EXAMPLE_VEC + printf(" rust_vec - Vector operations demo\n"); + #endif + #ifdef RUST_EXAMPLE_DL + printf(" rust_dl - Dynamic library operations demo\n"); + #endif + return 0; +} +MSH_CMD_EXPORT_ALIAS(cmd_rust_help, rust_help, Show Rust component help); + +/* Component initialization */ +static int rust_component_init(void) +{ + int ret = rust_init(); + if (ret == 0) + { + printf("Rust component initialized (modular version)\n"); + printf("Use 'rust_help' to see available commands\n"); + } + return ret; +} +#ifdef RUST_INIT_COMPONENT +INIT_APP_EXPORT(rust_component_init); +#endif diff --git a/machines/qemu-virt-riscv64/rust/src/api/base.rs b/machines/qemu-virt-riscv64/rust/src/api/base.rs new file mode 100644 index 00000000..7ebb98e9 --- /dev/null +++ b/machines/qemu-virt-riscv64/rust/src/api/base.rs @@ -0,0 +1,92 @@ +use crate::bindings::*; + +// Error type from rt-thread `c code` +#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub enum RttCResult { + Ok = RT_EOK as isize, + Error = -(RT_ERROR as isize), + TimeOut = -(RT_ETIMEOUT as isize), + Full = -(RT_EFULL as isize), + Empty = -(RT_EEMPTY as isize), + NoMem = -(RT_ENOMEM as isize), + NoSys = -(RT_ENOSYS as isize), + Busy = -(RT_EBUSY as isize), + IO = -(RT_EIO as isize), + INTR = -(RT_EINTR as isize), + INVAL = -(RT_EINVAL as isize), + + NotValidCode = -100, +} +// NOTE Match to `enum Error` +impl From for RttCResult { + fn from(a: i32) -> Self { + let ret = match a { + 0 => RttCResult::Ok, + -1 => RttCResult::Error, + -2 => RttCResult::TimeOut, + -3 => RttCResult::Full, + -4 => RttCResult::Empty, + -5 => RttCResult::NoMem, + -6 => RttCResult::NoSys, + -7 => RttCResult::Busy, + -8 => RttCResult::IO, + -9 => RttCResult::INTR, + -10 => RttCResult::INVAL, + _ => RttCResult::NotValidCode, + }; + ret + } +} + +impl From for RttCResult { + fn from(a: i64) -> Self { + let ret = match a { + 0 => RttCResult::Ok, + -1 => RttCResult::Error, + -2 => RttCResult::TimeOut, + -3 => RttCResult::Full, + -4 => RttCResult::Empty, + -5 => RttCResult::NoMem, + -6 => RttCResult::NoSys, + -7 => RttCResult::Busy, + -8 => RttCResult::IO, + -9 => RttCResult::INTR, + -10 => RttCResult::INVAL, + _ => RttCResult::NotValidCode, + }; + ret + } +} + +impl From for RttCResult { + fn from(a: u64) -> Self { + // Convert u64 to i64 for processing + // rt_mq_recv returns rt_base_t which can be either positive (success with data size) + // or negative (error code) + let signed_val = a as i64; + let ret = match signed_val { + 0 => RttCResult::Ok, + -1 => RttCResult::Error, + -2 => RttCResult::TimeOut, + -3 => RttCResult::Full, + -4 => RttCResult::Empty, + -5 => RttCResult::NoMem, + -6 => RttCResult::NoSys, + -7 => RttCResult::Busy, + -8 => RttCResult::IO, + -9 => RttCResult::INTR, + -10 => RttCResult::INVAL, + _ if signed_val > 0 => RttCResult::Ok, // Positive values indicate success with data size + _ => RttCResult::NotValidCode, + }; + ret + } +} + +pub fn is_eok(val: RttCResult) -> bool { + if let RttCResult::Ok = val { + true + } else { + false + } +} \ No newline at end of file diff --git a/machines/qemu-virt-riscv64/rust/src/api/interrupt.rs b/machines/qemu-virt-riscv64/rust/src/api/interrupt.rs new file mode 100644 index 00000000..1d26b7a5 --- /dev/null +++ b/machines/qemu-virt-riscv64/rust/src/api/interrupt.rs @@ -0,0 +1,60 @@ +use crate::bindings::*; + +#[cfg(not(feature = "smp"))] +unsafe fn irq_close() -> rt_base_t { + rt_hw_interrupt_disable() +} +#[cfg(feature = "smp")] +unsafe fn irq_close() -> rt_base_t { + rt_cpus_lock() +} + +#[cfg(not(feature = "smp"))] +unsafe fn irq_open(f: rt_base_t) { + rt_hw_interrupt_enable(f) +} +#[cfg(feature = "smp")] +unsafe fn irq_open(f: rt_base_t) { + rt_cpus_unlock(f) +} + +pub fn no_irq(f: F) -> T +where + F: FnOnce() -> T, +{ + let level; + let out; + unsafe { level = irq_close() } + out = f(); + unsafe { irq_open(level) } + out +} + +#[derive(Debug, Copy, Clone)] +pub struct InterruptFlag(rt_base_t); + +pub const INTERRUPT_FLAG_INIT: InterruptFlag = InterruptFlag { 0: 0 as _ }; + +pub fn interrupt_disable() -> InterruptFlag { + unsafe { InterruptFlag(irq_close()) } +} + +pub fn interrupt_enable(f: InterruptFlag) { + unsafe { irq_open(f.0) } +} + +pub fn interrupt_enter() { + unsafe { + rt_interrupt_enter(); + } +} + +pub fn interrupt_leave() { + unsafe { + rt_interrupt_leave(); + } +} + +pub fn is_irq_context() -> bool { + unsafe { rt_interrupt_get_nest() != 0 } +} \ No newline at end of file diff --git a/machines/qemu-virt-riscv64/rust/src/api/mem.rs b/machines/qemu-virt-riscv64/rust/src/api/mem.rs new file mode 100644 index 00000000..e0ad2199 --- /dev/null +++ b/machines/qemu-virt-riscv64/rust/src/api/mem.rs @@ -0,0 +1,215 @@ +/* + * Copyright (c) 2006-2024, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2025-01-XX foxglove Safe memory management API + */ + +use crate::bindings::*; +use core::ffi::c_void; + +/// Safe memory allocation +/// +/// # Arguments +/// * `bytes` - Number of bytes to allocate +/// +/// # Returns +/// * `Some(ptr)` - Valid pointer to allocated memory +/// * `None` - Allocation failed +pub fn mem_alloc(bytes: usize) -> Option<*mut c_void> { + if bytes == 0 { + return None; + } + + let ptr = unsafe { rt_malloc(bytes as rt_size_t) }; + if ptr.is_null() { + None + } else { + Some(ptr) + } +} + +/// Safe memory allocation with zero initialization +/// +/// # Arguments +/// * `count` - Number of elements +/// * `size` - Size of each element in bytes +/// +/// # Returns +/// * `Some(ptr)` - Valid pointer to zero-initialized memory +/// * `None` - Allocation failed +pub fn mem_calloc(count: usize, size: usize) -> Option<*mut c_void> { + if count == 0 || size == 0 { + return None; + } + + let ptr = unsafe { rt_calloc(count as rt_size_t, size as rt_size_t) }; + if ptr.is_null() { + None + } else { + Some(ptr) + } +} + +/// Safe aligned memory allocation +/// +/// # Arguments +/// * `bytes` - Number of bytes to allocate +/// * `align` - Alignment requirement (must be power of 2) +/// +/// # Returns +/// * `Some(ptr)` - Valid aligned pointer to allocated memory +/// * `None` - Allocation failed or invalid alignment +pub fn mem_alloc_aligned(bytes: usize, align: usize) -> Option<*mut c_void> { + if bytes == 0 || align == 0 || !align.is_power_of_two() { + return None; + } + + let ptr = unsafe { rt_malloc_align(bytes as rt_size_t, align as rt_size_t) }; + if ptr.is_null() { + None + } else { + Some(ptr) + } +} + +/// Safe memory reallocation +/// +/// # Arguments +/// * `ptr` - Existing pointer (can be null) +/// * `new_size` - New size in bytes +/// +/// # Returns +/// * `Some(ptr)` - Valid pointer to reallocated memory +/// * `None` - Reallocation failed +pub fn mem_realloc(ptr: *mut c_void, new_size: usize) -> Option<*mut c_void> { + if new_size == 0 { + if !ptr.is_null() { + mem_free(ptr); + } + return None; + } + + let new_ptr = unsafe { rt_realloc(ptr, new_size as rt_size_t) }; + if new_ptr.is_null() { + None + } else { + Some(new_ptr) + } +} + +/// Safe memory deallocation +/// +/// # Arguments +/// * `ptr` - Pointer to memory to free (null pointers are safely ignored) +pub fn mem_free(ptr: *mut c_void) { + if !ptr.is_null() { + unsafe { rt_free(ptr) }; + } +} + +/// Safe aligned memory deallocation +/// +/// # Arguments +/// * `ptr` - Pointer to aligned memory to free (null pointers are safely ignored) +pub fn mem_free_aligned(ptr: *mut c_void) { + if !ptr.is_null() { + unsafe { rt_free_align(ptr) }; + } +} + +/// Unsafe raw memory allocation (for compatibility) +/// +/// # Safety +/// Caller must ensure proper error handling and memory management +pub unsafe fn mem_alloc_raw(bytes: usize) -> *mut c_void { + rt_malloc(bytes as rt_size_t) +} + +/// Memory allocation result type for better error handling +pub type MemResult = Result; + +/// Memory allocation error types +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum MemError { + /// Out of memory + OutOfMemory, + /// Invalid size (zero or too large) + InvalidSize, + /// Invalid alignment (not power of 2) + InvalidAlignment, + /// Null pointer passed where valid pointer expected + NullPointer, +} + +/// Safe typed memory allocation +/// +/// # Type Parameters +/// * `T` - Type to allocate +/// +/// # Returns +/// * `Ok(ptr)` - Valid pointer to allocated memory for type T +/// * `Err(error)` - Allocation error +pub fn mem_alloc_typed() -> MemResult<*mut T> { + let size = core::mem::size_of::(); + if size == 0 { + return Err(MemError::InvalidSize); + } + + match mem_alloc(size) { + Some(ptr) => Ok(ptr as *mut T), + None => Err(MemError::OutOfMemory), + } +} + +/// Safe typed array allocation +/// +/// # Type Parameters +/// * `T` - Element type +/// +/// # Arguments +/// * `count` - Number of elements +/// +/// # Returns +/// * `Ok(ptr)` - Valid pointer to allocated array +/// * `Err(error)` - Allocation error +pub fn mem_alloc_array(count: usize) -> MemResult<*mut T> { + if count == 0 { + return Err(MemError::InvalidSize); + } + + let size = core::mem::size_of::() + .checked_mul(count) + .ok_or(MemError::InvalidSize)?; + + match mem_alloc(size) { + Some(ptr) => Ok(ptr as *mut T), + None => Err(MemError::OutOfMemory), + } +} + +/// Safe typed array allocation with zero initialization +/// +/// # Type Parameters +/// * `T` - Element type +/// +/// # Arguments +/// * `count` - Number of elements +/// +/// # Returns +/// * `Ok(ptr)` - Valid pointer to zero-initialized array +/// * `Err(error)` - Allocation error +pub fn mem_calloc_array(count: usize) -> MemResult<*mut T> { + if count == 0 { + return Err(MemError::InvalidSize); + } + + let size = core::mem::size_of::(); + match mem_calloc(count, size) { + Some(ptr) => Ok(ptr as *mut T), + None => Err(MemError::OutOfMemory), + } +} \ No newline at end of file diff --git a/machines/qemu-virt-riscv64/rust/src/api/mod.rs b/machines/qemu-virt-riscv64/rust/src/api/mod.rs new file mode 100644 index 00000000..468ed668 --- /dev/null +++ b/machines/qemu-virt-riscv64/rust/src/api/mod.rs @@ -0,0 +1,17 @@ +pub mod base; +pub mod interrupt; +pub mod mem; +pub mod thread; +pub mod mutex; +pub mod sem; +pub mod queue; + + + +pub use base::*; +pub use interrupt::*; +pub use mem::*; +pub use thread::*; +pub use mutex::*; +pub use sem::*; +pub use queue::*; diff --git a/machines/qemu-virt-riscv64/rust/src/api/mutex.rs b/machines/qemu-virt-riscv64/rust/src/api/mutex.rs new file mode 100644 index 00000000..e22e083a --- /dev/null +++ b/machines/qemu-virt-riscv64/rust/src/api/mutex.rs @@ -0,0 +1,39 @@ +use crate::api::RttCResult; +use crate::bindings::*; +use core::ptr; +use alloc::ffi::CString; + +pub type APIRawMutex = rt_mutex_t; + +#[inline] +pub fn mutex_create(name: &str) -> Option { + let s = CString::new(name).unwrap(); + let raw; + unsafe { + raw = rt_mutex_create(s.as_ptr(), 1); + } + if raw == ptr::null_mut() { + None + } else { + Some(raw) + } +} + +#[inline] +pub fn mutex_delete(handle: APIRawMutex) { + unsafe { + rt_mutex_delete(handle); + } +} + +#[inline] +pub fn mutex_take(handle: APIRawMutex, tick: isize) -> RttCResult { + unsafe { rt_mutex_take(handle, tick as _).into() } +} + +#[inline] +pub fn mutex_release(handle: APIRawMutex) { + unsafe { + rt_mutex_release(handle); + } +} \ No newline at end of file diff --git a/machines/qemu-virt-riscv64/rust/src/api/queue.rs b/machines/qemu-virt-riscv64/rust/src/api/queue.rs new file mode 100644 index 00000000..f88fb19e --- /dev/null +++ b/machines/qemu-virt-riscv64/rust/src/api/queue.rs @@ -0,0 +1,46 @@ +use crate::api::RttCResult; +use core::ffi::c_void; +use crate::bindings::*; +use core::ptr; +use alloc::ffi::CString; + +pub type APIRawQueue = rt_mq_t; + +#[inline] +pub(crate) fn queue_create(name: &str, len: u32, message_size: u32) -> Option { + let s = CString::new(name).unwrap(); + let raw; + unsafe { raw = rt_mq_create(s.as_ptr(), message_size.into(), len.into(), 0) } + if raw == ptr::null_mut() { + None + } else { + Some(raw) + } +} + +#[inline] +pub(crate) fn queue_send_wait( + handle: APIRawQueue, + msg: *const c_void, + msg_size: u32, + tick: i32, +) -> RttCResult { + unsafe { rt_mq_send_wait(handle, msg, msg_size.into(), tick).into() } +} + +#[inline] +pub(crate) fn queue_receive_wait( + handle: APIRawQueue, + msg: *mut c_void, + msg_size: u32, + tick: i32, +) -> RttCResult { + unsafe { rt_mq_recv(handle, msg, msg_size.into(), tick).into() } +} + +#[inline] +pub(crate) fn queue_delete(handle: APIRawQueue) { + unsafe { + rt_mq_delete(handle); + } +} \ No newline at end of file diff --git a/machines/qemu-virt-riscv64/rust/src/api/sem.rs b/machines/qemu-virt-riscv64/rust/src/api/sem.rs new file mode 100644 index 00000000..da5f3d54 --- /dev/null +++ b/machines/qemu-virt-riscv64/rust/src/api/sem.rs @@ -0,0 +1,42 @@ +use crate::api::RttCResult; +use crate::bindings::*; +use core::ptr; +use alloc::ffi::CString; + +pub type APIRawSem = rt_sem_t; + +#[inline] +pub(crate) fn semaphore_create(name: &str) -> Option { + let s = CString::new(name).unwrap(); + let raw; + unsafe { + raw = rt_sem_create(s.as_ptr(), 0, 0); + } + return if raw == ptr::null_mut() { + None + } else { + Some(raw) + }; +} + +#[inline] +pub(crate) fn semaphore_try_take(handle: APIRawSem) -> RttCResult { + unsafe { rt_sem_trytake(handle).into() } +} + +#[inline] +pub(crate) fn semaphore_take(handle: APIRawSem, tick: u32) -> RttCResult { + unsafe { rt_sem_take(handle, tick).into() } +} + +#[inline] +pub(crate) fn semaphore_release(handle: APIRawSem) -> RttCResult { + unsafe { rt_sem_release(handle).into() } +} + +#[inline] +pub(crate) fn semaphore_delete(handle: APIRawSem) { + unsafe { + let _ = rt_sem_delete(handle); + } +} \ No newline at end of file diff --git a/machines/qemu-virt-riscv64/rust/src/api/thread.rs b/machines/qemu-virt-riscv64/rust/src/api/thread.rs new file mode 100644 index 00000000..00600473 --- /dev/null +++ b/machines/qemu-virt-riscv64/rust/src/api/thread.rs @@ -0,0 +1,99 @@ +use super::base::*; +use crate::bindings::*; +use core::ptr; +use core::ffi::c_void; +use alloc::ffi::CString; + +// Thread handle, defined by c code +pub type APIRawThread = rt_thread_t; + +// Thread entry function, Defined by rtt c code +pub type ThreadEntry = extern "C" fn(parameter: *mut c_void); + +// Create a new thread +// Return None:OOM Some():thread handle +#[inline] +pub fn thread_create( + name: &str, + entry: ThreadEntry, + param: *mut c_void, + stack_size: u32, + priority: u8, + tick: u32, +) -> Option { + let name = CString::new(name).unwrap(); + let raw; + unsafe { + raw = rt_thread_create( + name.as_ptr(), + entry, + param, + stack_size as crate::bindings::librt::rt_size_t, + priority, + tick, + ); + } + if raw == ptr::null_mut() { + None + } else { + Some(raw) + } +} + +// Delete a thread from system +#[inline] +pub fn thread_delete(th: APIRawThread) -> RttCResult { + unsafe { rt_thread_delete(th).into() } +} + +// Get current thread +#[inline] +pub fn thread_self() -> Option { + let ret; + unsafe { + ret = rt_thread_self(); + } + if ret == ptr::null_mut() { + None + } else { + Some(ret) + } +} + +// Startup a thread +#[inline] +pub fn thread_startup(th: APIRawThread) -> RttCResult { + unsafe { rt_thread_startup(th).into() } +} + +// Thread have a sleep +#[inline] +pub fn thread_delay(ticks: usize) -> RttCResult { + unsafe { rt_thread_delay(ticks as _).into() } +} + +// Thread have a ms sleep +#[inline] +pub fn thread_m_delay(ms: i32) -> RttCResult { + unsafe { rt_thread_mdelay(ms as _).into() } +} + +// Yield +#[inline] +pub fn thread_yield() { + unsafe { + rt_thread_yield(); + } +} + +// Suspend a thread +#[inline] +pub fn thread_suspend(th: APIRawThread) -> RttCResult { + unsafe { rt_thread_suspend(th).into() } +} + +// Resume a thread +#[inline] +pub fn thread_resume(th: APIRawThread) -> RttCResult { + unsafe { rt_thread_resume(th).into() } +} diff --git a/machines/qemu-virt-riscv64/rust/src/bindings/libc.rs b/machines/qemu-virt-riscv64/rust/src/bindings/libc.rs new file mode 100644 index 00000000..bb6350f2 --- /dev/null +++ b/machines/qemu-virt-riscv64/rust/src/bindings/libc.rs @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2006-2024, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author foxglove + * 2025-09-15 foxglove 1.0 version + */ + +//! C standard library API bindings +//! +//! Provides Rust wrappers for standard C library functions + +pub use core::ffi::{c_char, c_int, c_void, c_uint, c_long}; + +// ============== stdio functions ============== +extern "C" { + pub fn printf(fmt: *const u8, ...) -> c_int; + pub fn sprintf(str: *mut c_char, fmt: *const c_char, ...) -> c_int; + pub fn snprintf(str: *mut c_char, size: usize, fmt: *const c_char, ...) -> c_int; + pub fn puts(s: *const c_char) -> c_int; + pub fn putchar(c: c_int) -> c_int; +} + +// ============== string functions ============== +extern "C" { + pub fn strlen(s: *const c_char) -> usize; + pub fn strcmp(s1: *const c_char, s2: *const c_char) -> c_int; + pub fn strncmp(s1: *const c_char, s2: *const c_char, n: usize) -> c_int; + pub fn strcpy(dest: *mut c_char, src: *const c_char) -> *mut c_char; + pub fn strncpy(dest: *mut c_char, src: *const c_char, n: usize) -> *mut c_char; + pub fn strcat(dest: *mut c_char, src: *const c_char) -> *mut c_char; + pub fn strncat(dest: *mut c_char, src: *const c_char, n: usize) -> *mut c_char; + pub fn strchr(s: *const c_char, c: c_int) -> *mut c_char; + pub fn strstr(haystack: *const c_char, needle: *const c_char) -> *mut c_char; +} + +// ============== memory functions ============== +extern "C" { + pub fn memcpy(dest: *mut c_void, src: *const c_void, n: usize) -> *mut c_void; + pub fn memmove(dest: *mut c_void, src: *const c_void, n: usize) -> *mut c_void; + pub fn memset(s: *mut c_void, c: c_int, n: usize) -> *mut c_void; + pub fn memcmp(s1: *const c_void, s2: *const c_void, n: usize) -> c_int; + pub fn memchr(s: *const c_void, c: c_int, n: usize) -> *mut c_void; +} + +// ============== dynamic memory management ============== +extern "C" { + pub fn malloc(size: usize) -> *mut c_void; + pub fn calloc(nmemb: usize, size: usize) -> *mut c_void; + pub fn realloc(ptr: *mut c_void, size: usize) -> *mut c_void; + pub fn free(ptr: *mut c_void); +} + +// ============== math functions ============== +extern "C" { + pub fn abs(n: c_int) -> c_int; + pub fn labs(n: c_long) -> c_long; + pub fn rand() -> c_int; + pub fn srand(seed: c_uint); +} + +// ============== conversion functions ============== +extern "C" { + pub fn atoi(nptr: *const c_char) -> c_int; + pub fn atol(nptr: *const c_char) -> c_long; + pub fn strtol(nptr: *const c_char, endptr: *mut *mut c_char, base: c_int) -> c_long; +} + +// ============== Rust-friendly wrappers ============== + +/// Safe string length calculation +pub fn safe_strlen(s: *const c_char) -> usize { + if s.is_null() { + 0 + } else { + unsafe { strlen(s) } + } +} + +/// Safe memory allocation +pub fn safe_malloc(size: usize) -> Option<*mut c_void> { + if size == 0 { + None + } else { + let ptr = unsafe { malloc(size) }; + if ptr.is_null() { + None + } else { + Some(ptr) + } + } +} + +/// Safe memory deallocation +pub fn safe_free(ptr: *mut c_void) { + if !ptr.is_null() { + unsafe { free(ptr) } + } +} + +/// Print string (Rust style) +pub fn print_str(s: &str) { + for byte in s.bytes() { + unsafe { putchar(byte as c_int) }; + } +} + +/// Print null-terminated string +pub fn print_cstr(s: &[u8]) { + unsafe { printf(s.as_ptr()) }; +} \ No newline at end of file diff --git a/machines/qemu-virt-riscv64/rust/src/bindings/libdl.rs b/machines/qemu-virt-riscv64/rust/src/bindings/libdl.rs new file mode 100644 index 00000000..7f4a6a1c --- /dev/null +++ b/machines/qemu-virt-riscv64/rust/src/bindings/libdl.rs @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2006-2024, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2025-09-23 foxglove libdl bindings + */ + +//! POSIX libdl API bindings for RT-Thread +//! Provides Rust FFI for dlopen/dlsym/dlclose/dlerror. + +use core::ffi::{c_char, c_int, c_void}; + +// RT-Thread libdl typically supports these flags; define minimally used ones +pub const RTLD_LAZY: c_int = 0x0001; // Lazy function call binding +pub const RTLD_NOW: c_int = 0x0002; // Immediate function call binding +pub const RTLD_GLOBAL: c_int = 0x0100; // Make symbols globally available +pub const RTLD_LOCAL: c_int = 0; // Default local + +#[cfg(feature = "rt_using_dlopen")] +extern "C" { + pub fn dlopen(filename: *const c_char, flag: c_int) -> *mut c_void; + pub fn dlsym(handle: *mut c_void, symbol: *const c_char) -> *mut c_void; + pub fn dlclose(handle: *mut c_void) -> c_int; + pub fn dlerror() -> *const c_char; +} + +// Stub implementations when dynamic library support is not available +#[cfg(not(feature = "rt_using_dlopen"))] +pub unsafe fn dlopen(_filename: *const c_char, _flag: c_int) -> *mut c_void { + core::ptr::null_mut() +} + +#[cfg(not(feature = "rt_using_dlopen"))] +pub unsafe fn dlsym(_handle: *mut c_void, _symbol: *const c_char) -> *mut c_void { + core::ptr::null_mut() +} + +#[cfg(not(feature = "rt_using_dlopen"))] +pub unsafe fn dlclose(_handle: *mut c_void) -> c_int { + -1 +} + +#[cfg(not(feature = "rt_using_dlopen"))] +pub unsafe fn dlerror() -> *const c_char { + b"Dynamic library support not available\0".as_ptr() as *const c_char +} + +/// Return last error as pointer (C string). Caller ensures non-null before use. +pub fn last_error_ptr() -> *const c_char { + unsafe { dlerror() } +} + +// ============== Rust-friendly safe wrappers ============== + +/// Dynamic library handle wrapper for safe resource management +pub struct DlHandle { + handle: *mut c_void, +} + +impl DlHandle { + /// Open a dynamic library safely + pub fn open(filename: &[u8], flags: c_int) -> Result { + // Use the safe_dlopen function for consistency + safe_dlopen(filename, flags) + } + + /// Get symbol from the dynamic library safely + pub fn get_symbol(&self, symbol: &[u8]) -> Result<*mut c_void, &'static str> { + // Use the safe_dlsym function for consistency + safe_dlsym(self.handle, symbol) + } + + /// Get a function pointer from the dynamic library + /// This is a convenience method that combines get_symbol with transmute + pub fn get_function(&self, symbol: &[u8]) -> Result { + let sym = self.get_symbol(symbol)?; + Ok(unsafe { core::mem::transmute_copy(&sym) }) + } + + /// Try to call a function with no arguments + /// Returns the function pointer if found, None if not found + pub fn try_call_no_args(&self, symbol: &[u8]) -> Result { + self.get_function(symbol) + } + + /// Try to call a main-style function + /// Returns the function pointer if found + pub fn try_call_main(&self, symbol: &[u8]) -> Result c_int, &'static str> { + self.get_function(symbol) + } + + /// Get the raw handle (for compatibility with existing code) + pub fn raw_handle(&self) -> *mut c_void { + self.handle + } +} + +impl Drop for DlHandle { + fn drop(&mut self) { + if !self.handle.is_null() { + // Use safe_dlclose for consistency, but ignore the result in Drop + let _ = safe_dlclose(self.handle); + } + } +} + +/// Safe dlopen wrapper +pub fn safe_dlopen(filename: &[u8], flags: c_int) -> Result { + let handle = unsafe { dlopen(filename.as_ptr() as *const c_char, flags) }; + + if handle.is_null() { + // For embedded environment, use static error message + Err("Failed to open dynamic library") + } else { + Ok(DlHandle { handle }) + } +} + +/// Safe dlsym wrapper (requires valid handle) +pub fn safe_dlsym(handle: *mut c_void, symbol: &[u8]) -> Result<*mut c_void, &'static str> { + if handle.is_null() { + return Err("Invalid handle"); + } + + let sym = unsafe { dlsym(handle, symbol.as_ptr() as *const c_char) }; + + if sym.is_null() { + // For embedded environment, use static error message + Err("Failed to find symbol in dynamic library") + } else { + Ok(sym) + } +} + +/// Safe dlclose wrapper +pub fn safe_dlclose(handle: *mut c_void) -> Result<(), c_int> { + if handle.is_null() { + return Err(-1); + } + + let result = unsafe { dlclose(handle) }; + if result == 0 { + Ok(()) + } else { + Err(result) + } +} + +/// Safe dlerror wrapper - returns error message as static string +pub fn safe_dlerror() -> Option<&'static str> { + let error_ptr = unsafe { dlerror() }; + if error_ptr.is_null() { + None + } else { + // For embedded environment, return generic error message + Some("Dynamic library error occurred") + } +} diff --git a/machines/qemu-virt-riscv64/rust/src/bindings/librt.rs b/machines/qemu-virt-riscv64/rust/src/bindings/librt.rs new file mode 100644 index 00000000..3b4c45a2 --- /dev/null +++ b/machines/qemu-virt-riscv64/rust/src/bindings/librt.rs @@ -0,0 +1,222 @@ +/* + * Copyright (c) 2006-2024, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author foxglove + * 2025-09-15 foxglove 1.0 version + */ + +//! RT-Thread OS API bindings +//! +//! Provides Rust wrappers for RT-Thread kernel and device driver APIs + +use core::ffi::{c_char, c_int, c_void, c_uint, c_ulong}; + +// RT-Thread basic type definitions +pub type rt_base_t = c_ulong; +pub type rt_ubase_t = c_ulong; +pub type rt_tick_t = c_uint; +pub type rt_size_t = c_ulong; +pub type rt_err_t = c_int; +pub type rt_thread_t = *mut c_void; +pub type rt_sem_t = *mut c_void; +pub type rt_mutex_t = *mut c_void; +pub type rt_device_t = *mut c_void; +pub type rt_mq_t = *mut c_void; + +// RT-Thread error codes +pub const RT_EOK: rt_err_t = 0; +pub const RT_ERROR: rt_err_t = 1; +pub const RT_ETIMEOUT: rt_err_t = 2; +pub const RT_EFULL: rt_err_t = 3; +pub const RT_EEMPTY: rt_err_t = 4; +pub const RT_ENOMEM: rt_err_t = 5; +pub const RT_ENOSYS: rt_err_t = 6; +pub const RT_EBUSY: rt_err_t = 7; +pub const RT_EIO: rt_err_t = 8; +pub const RT_EINTR: rt_err_t = 9; +pub const RT_EINVAL: rt_err_t = 10; + +// ============== Kernel object management ============== +extern "C" { + pub fn rt_object_get_type(object: *mut c_void) -> u8; + pub fn rt_object_find(name: *const c_char, object_type: u8) -> *mut c_void; +} + +// ============== Thread management ============== +extern "C" { + pub fn rt_thread_create( + name: *const c_char, + entry: extern "C" fn(*mut c_void), + parameter: *mut c_void, + stack_size: rt_size_t, + priority: u8, + tick: u32, + ) -> rt_thread_t; + + pub fn rt_thread_delete(thread: rt_thread_t) -> rt_err_t; + pub fn rt_thread_startup(thread: rt_thread_t) -> rt_err_t; + pub fn rt_thread_self() -> rt_thread_t; + pub fn rt_thread_yield() -> rt_err_t; + pub fn rt_thread_delay(tick: rt_tick_t) -> rt_err_t; + pub fn rt_thread_mdelay(ms: c_int) -> rt_err_t; + pub fn rt_thread_suspend(thread: rt_thread_t) -> rt_err_t; + pub fn rt_thread_resume(thread: rt_thread_t) -> rt_err_t; +} + +// ============== Semaphore management ============== +extern "C" { + pub fn rt_sem_create(name: *const c_char, value: u32, flag: u8) -> rt_sem_t; + pub fn rt_sem_delete(sem: rt_sem_t) -> rt_err_t; + pub fn rt_sem_take(sem: rt_sem_t, time: rt_tick_t) -> rt_err_t; + pub fn rt_sem_trytake(sem: rt_sem_t) -> rt_err_t; + pub fn rt_sem_release(sem: rt_sem_t) -> rt_err_t; +} + +// ============== Mutex management ============== +extern "C" { + pub fn rt_mutex_create(name: *const c_char, flag: u8) -> rt_mutex_t; + pub fn rt_mutex_delete(mutex: rt_mutex_t) -> rt_err_t; + pub fn rt_mutex_take(mutex: rt_mutex_t, time: rt_tick_t) -> rt_err_t; + pub fn rt_mutex_release(mutex: rt_mutex_t) -> rt_err_t; +} +extern "C" { + pub fn rt_mq_create(name: *const c_char, msg_size: rt_size_t, max_msgs: rt_size_t, flag: u8) -> rt_mq_t; + pub fn rt_mq_send(mq: rt_mq_t, buffer: *const c_void, size: rt_size_t) -> rt_err_t; + pub fn rt_mq_send_wait(mq: rt_mq_t, buffer: *const c_void, size: rt_size_t, timeout: c_int) -> rt_err_t; + pub fn rt_mq_recv(mq: rt_mq_t, buffer: *mut c_void, size: rt_size_t, timeout: c_int) -> rt_base_t; + pub fn rt_mq_delete(mq: rt_mq_t) -> rt_err_t; + pub fn rt_mq_detach(mq: rt_mq_t) -> rt_err_t; +} +// ============== Memory management ============== +extern "C" { + pub fn rt_malloc(size: rt_size_t) -> *mut c_void; + pub fn rt_free(ptr: *mut c_void); + pub fn rt_realloc(ptr: *mut c_void, newsize: rt_size_t) -> *mut c_void; + pub fn rt_calloc(count: rt_size_t, size: rt_size_t) -> *mut c_void; + pub fn rt_malloc_align(size: rt_size_t, align: rt_size_t) -> *mut c_void; + pub fn rt_free_align(ptr: *mut c_void); +} + +// ============== Device management ============== +extern "C" { + pub fn rt_device_find(name: *const c_char) -> rt_device_t; + pub fn rt_device_open(dev: rt_device_t, oflag: u16) -> rt_err_t; + pub fn rt_device_close(dev: rt_device_t) -> rt_err_t; + pub fn rt_device_read( + dev: rt_device_t, + pos: c_ulong, + buffer: *mut c_void, + size: rt_size_t, + ) -> rt_size_t; + pub fn rt_device_write( + dev: rt_device_t, + pos: c_ulong, + buffer: *const c_void, + size: rt_size_t, + ) -> rt_size_t; + pub fn rt_device_control(dev: rt_device_t, cmd: c_int, arg: *mut c_void) -> rt_err_t; +} + +// ============== System tick ============== +extern "C" { + pub fn rt_tick_get() -> rt_tick_t; + pub fn rt_tick_from_millisecond(ms: c_int) -> rt_tick_t; +} + +// ============== Debug output ============== +extern "C" { + pub fn rt_kprintf(fmt: *const u8, ...) -> c_int; + pub fn rt_kputs(str: *const u8) -> c_int; +} + +// ============== Interrupt management ============== +extern "C" { + pub fn rt_hw_interrupt_disable() -> rt_base_t; + pub fn rt_hw_interrupt_enable(level: rt_base_t); + pub fn rt_cpus_lock() -> rt_base_t; + pub fn rt_cpus_unlock(level: rt_base_t); + pub fn rt_interrupt_enter(); + pub fn rt_interrupt_leave(); + pub fn rt_interrupt_get_nest() -> u8; +} + +// ============== Rust-friendly wrappers ============== + +/// RT-Thread thread handle wrapper +pub struct Thread { + handle: rt_thread_t, +} + +impl Thread { + /// Create new thread + pub fn create( + name: &[u8], + entry: extern "C" fn(*mut c_void), + param: *mut c_void, + stack_size: usize, + priority: u8, + tick: u32, + ) -> Option { + let handle = unsafe { + rt_thread_create( + name.as_ptr() as *const c_char, + entry, + param, + stack_size as rt_size_t, + priority, + tick, + ) + }; + + if handle.is_null() { + None + } else { + Some(Thread { handle }) + } + } + + /// Start thread + pub fn startup(&self) -> Result<(), rt_err_t> { + let ret = unsafe { rt_thread_startup(self.handle) }; + if ret == RT_EOK { + Ok(()) + } else { + Err(ret) + } + } +} + +impl Drop for Thread { + fn drop(&mut self) { + unsafe { rt_thread_delete(self.handle) }; + } +} + +/// Current thread sleep +pub fn thread_sleep_ms(ms: u32) { + unsafe { rt_thread_mdelay(ms as c_int) }; +} + +/// Safe RT-Thread memory allocation +pub fn rt_safe_malloc(size: usize) -> Option<*mut c_void> { + if size == 0 { + None + } else { + let ptr = unsafe { rt_malloc(size as rt_size_t) }; + if ptr.is_null() { + None + } else { + Some(ptr) + } + } +} + +/// Safe RT-Thread memory deallocation +pub fn rt_safe_free(ptr: *mut c_void) { + if !ptr.is_null() { + unsafe { rt_free(ptr) } + } +} \ No newline at end of file diff --git a/machines/qemu-virt-riscv64/rust/src/bindings/mod.rs b/machines/qemu-virt-riscv64/rust/src/bindings/mod.rs new file mode 100644 index 00000000..e86323a2 --- /dev/null +++ b/machines/qemu-virt-riscv64/rust/src/bindings/mod.rs @@ -0,0 +1,7 @@ +pub mod libc; +pub mod librt; +pub mod libdl; + + pub use libc::*; + pub use librt::*; + pub use libdl::*; \ No newline at end of file diff --git a/machines/qemu-virt-riscv64/rust/src/examples/dlmodule_demo.rs b/machines/qemu-virt-riscv64/rust/src/examples/dlmodule_demo.rs new file mode 100644 index 00000000..6f05ab11 --- /dev/null +++ b/machines/qemu-virt-riscv64/rust/src/examples/dlmodule_demo.rs @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2006-2024, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2025-09-25 foxglove test dynamic loading operations + */ + +type NoArgFn = extern "C" fn(); +type AddFn = extern "C" fn(libc::c_int, libc::c_int) -> libc::c_int; +type MainFn = extern "C" fn(libc::c_int, *mut *mut libc::c_char) -> libc::c_int; + +/// Basic dlopen test using safe wrapper +#[no_mangle] +pub extern "C" fn rust_dl_open_demo() { + unsafe { + libc::printf(b"\n=== dlopen Demo ===\n\0".as_ptr()); + + let path = b"/hello.mo\0"; + match libdl::DlHandle::open(path, libdl::RTLD_NOW | libdl::RTLD_GLOBAL) { + Ok(handle) => { + libc::printf(b"dlopen ok: %p\n\0".as_ptr(), handle.raw_handle()); + libc::printf(b"Module loaded successfully\n\0".as_ptr()); + // Handle will be automatically closed when it goes out of scope + } + Err(error) => { + libc::printf(b"dlopen failed: \0".as_ptr()); + libc::print_str(&error); + libc::printf(b"\n\0".as_ptr()); + } + } + } +} + +/// dlsym symbol resolution test using safe wrapper +#[no_mangle] +pub extern "C" fn rust_dl_sym_demo() { + unsafe { + libc::printf(b"\n=== dlsym Demo ===\n\0".as_ptr()); + + let path = b"/hello.mo\0"; + match libdl::DlHandle::open(path, libdl::RTLD_NOW | libdl::RTLD_GLOBAL) { + Ok(handle) => { + // Try to resolve main symbol + let sym_name = b"main\0"; + match handle.get_symbol(sym_name) { + Ok(sym) => { + libc::printf(b"Found symbol 'main' at %p\n\0".as_ptr(), sym); + } + Err(error) => { + libc::printf(b"dlsym main failed: \0".as_ptr()); + libc::print_str(&error); + libc::printf(b"\n\0".as_ptr()); + } + } + + // Try to resolve non-existent symbol + let bad_sym = b"nonexistent_symbol\0"; + match handle.get_symbol(bad_sym) { + Ok(_) => { + libc::printf(b"Unexpected: found nonexistent symbol\n\0".as_ptr()); + } + Err(error) => { + libc::printf(b"dlsym nonexistent_symbol failed (expected): \0".as_ptr()); + libc::print_str(&error); + libc::printf(b"\n\0".as_ptr()); + } + } + // Handle will be automatically closed when it goes out of scope + } + Err(error) => { + libc::printf(b"dlopen failed: \0".as_ptr()); + libc::print_str(&error); + libc::printf(b"\n\0".as_ptr()); + } + } + } +} + +/// Function call test through dlsym using safe wrapper +#[no_mangle] +pub extern "C" fn rust_dl_call_demo() { + unsafe { + libc::printf(b"\n=== Function Call Demo ===\n\0".as_ptr()); + + let path = b"/hello.mo\0"; + match libdl::DlHandle::open(path, libdl::RTLD_NOW | libdl::RTLD_GLOBAL) { + Ok(handle) => { + // Call main function - simplified with convenience method + let sym_name = b"main\0"; + match handle.try_call_main(sym_name) { + Ok(main_fn) => { + libc::printf(b"Calling module main()...\n\0".as_ptr()); + let rc = main_fn(0, core::ptr::null_mut()); + libc::printf(b"module main() returned %d\n\0".as_ptr(), rc); + } + Err(error) => { + libc::printf(b"dlsym main failed: \0".as_ptr()); + libc::print_str(&error); + libc::printf(b"\n\0".as_ptr()); + } + } + // Handle will be automatically closed when it goes out of scope + } + Err(error) => { + libc::printf(b"dlopen failed: \0".as_ptr()); + libc::print_str(&error); + libc::printf(b"\n\0".as_ptr()); + } + } + } +} + +/// Error handling test using safe wrapper +#[no_mangle] +pub extern "C" fn rust_dl_error_demo() { + unsafe { + libc::printf(b"\n=== Error Handling Demo ===\n\0".as_ptr()); + + // Try to open non-existent module + let bad_path = b"/nonexistent.mo\0"; + match libdl::DlHandle::open(bad_path, libdl::RTLD_NOW) { + Ok(_handle) => { + libc::printf(b"Unexpected: loaded nonexistent module\n\0".as_ptr()); + // Handle will be automatically closed when it goes out of scope + } + Err(error) => { + libc::printf(b"dlopen nonexistent module failed (expected): \0".as_ptr()); + libc::print_str(&error); + libc::printf(b"\n\0".as_ptr()); + } + } + + // Try to open valid module with invalid flags + let path = b"/hello.mo\0"; + match libdl::DlHandle::open(path, 0xFFFF) { // Invalid flags + Ok(_handle) => { + libc::printf(b"dlopen with invalid flags succeeded\n\0".as_ptr()); + // Handle will be automatically closed when it goes out of scope + } + Err(error) => { + libc::printf(b"dlopen with invalid flags failed: \0".as_ptr()); + libc::print_str(&error); + libc::printf(b"\n\0".as_ptr()); + } + } + } +} + +/// Comprehensive dl operations demonstration +#[no_mangle] +pub extern "C" fn rust_dl_demo_all() { + rust_dl_open_demo(); + rust_dl_sym_demo(); + rust_dl_call_demo(); + rust_dl_error_demo(); +} + + diff --git a/machines/qemu-virt-riscv64/rust/src/examples/hello.rs b/machines/qemu-virt-riscv64/rust/src/examples/hello.rs new file mode 100644 index 00000000..44eff4a6 --- /dev/null +++ b/machines/qemu-virt-riscv64/rust/src/examples/hello.rs @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2006-2024, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2025-09-15 foxglove 1.0 version + */ + +// Hello example - demonstrates basic output functionality + +/// Simple hello function +#[no_mangle] +pub extern "C" fn rust_hello() { + println!("Hello from Rust!"); +} + +/// Hello function with name parameter +#[no_mangle] +pub extern "C" fn rust_hello_with_name(name: *const libc::c_char) { + if name.is_null() { + rust_hello(); + } else { + unsafe { + let name_str = core::ffi::CStr::from_ptr(name); + if let Ok(name_str) = name_str.to_str() { + println!("Hello, {}!", name_str); + } else { + println!("Hello, [invalid UTF-8]!"); + } + } + } +} + +/// Print using Rust-style string +#[no_mangle] +pub extern "C" fn rust_hello_rust_style() { + libc::print_str("Hello from Rust (Rust style)!\n"); +} \ No newline at end of file diff --git a/machines/qemu-virt-riscv64/rust/src/examples/memory_demo.rs b/machines/qemu-virt-riscv64/rust/src/examples/memory_demo.rs new file mode 100644 index 00000000..6a36ca14 --- /dev/null +++ b/machines/qemu-virt-riscv64/rust/src/examples/memory_demo.rs @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2006-2024, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2025-09-20 foxglove test memory operations + */ + +// Memory operation examples - demonstrates memory allocation, copy, set and other operations + +/// Basic addition function (for testing) +#[no_mangle] +pub extern "C" fn rust_add(a: i32, b: i32) -> i32 { + a + b +} + +/// Multiplication function (for testing) +#[no_mangle] +pub extern "C" fn rust_multiply(a: i32, b: i32) -> i32 { + a * b +} + +/// Memory copy test +#[no_mangle] +pub extern "C" fn rust_memcpy_test(dest: *mut u8, src: *const u8, size: usize) -> bool { + if dest.is_null() || src.is_null() || size == 0 { + return false; + } + unsafe { + libc::memcpy(dest as *mut c_void, src as *const c_void, size); + } + true +} + +/// Memory set example +#[no_mangle] +pub extern "C" fn rust_memset_demo() { + let mut buffer: [u8; 32] = [0; 32]; + + unsafe { + // 设置前16字节为'A' + libc::memset(buffer.as_mut_ptr() as *mut c_void, b'A' as i32, 16); + + // 设置后16字节为'B' + let second_half = buffer.as_mut_ptr().add(16) as *mut c_void; + libc::memset(second_half, b'B' as i32, 16); + + // 打印结果 + print!("Buffer after memset: "); + for i in 0..32 { + print!("{}", buffer[i] as char); + } + println!(); + } +} + +/// Memory comparison example +#[no_mangle] +pub extern "C" fn rust_memcmp_demo() { + let buf1 = [1u8, 2, 3, 4, 5]; + let buf2 = [1u8, 2, 3, 4, 5]; + let buf3 = [1u8, 2, 3, 4, 6]; + + unsafe { + let result1 = libc::memcmp( + buf1.as_ptr() as *const c_void, + buf2.as_ptr() as *const c_void, + 5, + ); + println!("memcmp(buf1, buf2) = {} (should be 0)", result1); + + let result2 = libc::memcmp( + buf1.as_ptr() as *const c_void, + buf3.as_ptr() as *const c_void, + 5, + ); + println!("memcmp(buf1, buf3) = {} (should be negative)", result2); + } +} + +/// Standard C library malloc/free example +#[no_mangle] +pub extern "C" fn rust_malloc_demo() { + println!("\n=== Standard C malloc/free Demo ==="); + + unsafe { + // 分配内存 + let size = 100; + let ptr = libc::malloc(size); + + if ptr.is_null() { + println!("malloc failed!"); + return; + } + + println!("Allocated {} bytes at {:p}", size, ptr); + + // 使用内存 + libc::memset(ptr, 0x42, size); + + // 释放内存 + libc::free(ptr); + println!("Memory freed"); + } +} + +/// RT-Thread memory allocation example +#[no_mangle] +pub extern "C" fn rust_rt_malloc_demo() { + println!("\n=== RT-Thread malloc/free Demo ==="); + + unsafe { + // 使用RT-Thread的内存分配 + let size = 64; + if let Some(ptr) = librt::rt_safe_malloc(size) { + println!("RT-Thread allocated {} bytes at {:p}", size, ptr); + + // 使用内存 + let buffer = ptr as *mut u8; + for i in 0..size { + *buffer.add(i) = (i as u8) & 0xFF; + } + + // 验证数据 + let mut sum = 0u32; + for i in 0..size { + sum += *buffer.add(i) as u32; + } + println!("Data sum: {}", sum); + + // 释放内存 + librt::rt_safe_free(ptr); + println!("RT-Thread memory freed"); + } else { + println!("RT-Thread malloc failed!"); + } + } +} + +/// Comprehensive memory operations demonstration +#[no_mangle] +pub extern "C" fn rust_memory_demo_all() { + rust_memset_demo(); + rust_memcmp_demo(); + rust_malloc_demo(); + rust_rt_malloc_demo(); +} diff --git a/machines/qemu-virt-riscv64/rust/src/examples/mq_demo.rs b/machines/qemu-virt-riscv64/rust/src/examples/mq_demo.rs new file mode 100644 index 00000000..6b1e19d5 --- /dev/null +++ b/machines/qemu-virt-riscv64/rust/src/examples/mq_demo.rs @@ -0,0 +1,151 @@ +/// Test message structure +#[derive(Debug, Clone, PartialEq)] +struct TestMessage { + id: u32, + data: [u8; 16], + timestamp: u32, +} + +impl TestMessage { + fn new(id: u32, data: &str) -> Self { + let mut msg_data = [0u8; 16]; + let bytes = data.as_bytes(); + let len = bytes.len().min(16); + msg_data[..len].copy_from_slice(&bytes[..len]); + + Self { + id, + data: msg_data, + timestamp: 0, // In real scenario, would use system tick + } + } +} + +/// Test basic queue operations +fn test_basic_queue_operations() { + println!("=== Testing Basic Queue Operations ==="); + + // Create a queue for u32 values + let queue = Queue::::new(5).expect("Failed to create queue"); + + // Test try_send (non-blocking) + println!("Testing try_send..."); + for i in 0..5 { + match queue.try_send(i * 10) { + Ok(()) => println!("Successfully sent: {}", i * 10), + Err((err, value)) => println!("Failed to send {}: {:?}", value, err), + } + } + + // Try to send one more (should fail - queue full) + match queue.try_send(999) { + Ok(()) => println!("Unexpected success sending to full queue"), + Err((err, value)) => println!("Expected failure sending {} to full queue: {:?}", value, err), + } + + // Test try_recv (non-blocking) + println!("Testing try_recv..."); + for i in 0..5 { + match queue.try_recv() { + Ok(value) => println!("Successfully received: {}", value), + Err(err) => println!("Failed to receive: {:?}", err), + } + } + + // Try to receive one more (should fail - queue empty) + match queue.try_recv() { + Ok(value) => println!("Unexpected success receiving from empty queue: {}", value), + Err(err) => println!("Expected failure receiving from empty queue: {:?}", err), + } + + println!("Basic queue operations test completed.\n"); +} + +/// Test queue with timeout operations +fn test_timeout_operations() { + println!("=== Testing Timeout Operations ==="); + + let queue = Queue::::new_with_name("timeout_queue", 3) + .expect("Failed to create named queue"); + + // Test send with timeout + println!("Testing send with timeout..."); + for i in 0..3 { + match queue.send(i, 100) { // 100 tick timeout + Ok(()) => println!("Successfully sent with timeout: {}", i), + Err((err, value)) => println!("Failed to send {} with timeout: {:?}", value, err), + } + } + + // Test recv with timeout + println!("Testing recv with timeout..."); + for i in 0..3 { + match queue.recv(100) { // 100 tick timeout + Ok(value) => println!("Successfully received with timeout: {}", value), + Err(err) => println!("Failed to receive with timeout: {:?}", err), + } + } + + // Test timeout on empty queue + println!("Testing timeout on empty queue..."); + match queue.recv(50) { // Short timeout + Ok(value) => println!("Unexpected success: {}", value), + Err(err) => println!("Expected timeout error: {:?}", err), + } + + println!("Timeout operations test completed.\n"); +} + +/// Test queue capacity and edge cases +fn test_queue_edge_cases() { + println!("=== Testing Queue Edge Cases ==="); + + // Test with minimum capacity + println!("Testing queue with capacity 1..."); + let small_queue = Queue::::new(1).expect("Failed to create small queue"); + + // Fill the queue + match small_queue.try_send(42) { + Ok(()) => println!("Successfully filled single-item queue"), + Err((err, value)) => println!("Failed to fill queue: {:?}", err), + } + + // Try to overfill + match small_queue.try_send(99) { + Ok(()) => println!("Unexpected success overfilling queue"), + Err((err, value)) => println!("Expected failure overfilling queue: {:?}", err), + } + + // Empty the queue + match small_queue.try_recv() { + Ok(value) => println!("Successfully emptied queue, got: {}", value), + Err(err) => println!("Failed to empty queue: {:?}", err), + } + + // Try to over-empty + match small_queue.try_recv() { + Ok(value) => println!("Unexpected success over-emptying queue: {}", value), + Err(err) => println!("Expected failure over-emptying queue: {:?}", err), + } + + println!("Edge cases test completed.\n"); +} + +/// Main demo function +pub fn mq_demo() { + println!("Starting Message Queue Demo"); + println!("============================"); + + test_basic_queue_operations(); + test_timeout_operations(); + test_queue_edge_cases(); + + println!("Message Queue Demo completed successfully!"); + println!("All Queue interfaces have been tested."); +} + +/// Entry point for the message queue demo +#[no_mangle] +pub extern "C" fn rust_mq_demo() { + mq_demo(); +} \ No newline at end of file diff --git a/machines/qemu-virt-riscv64/rust/src/examples/mutex_demo.rs b/machines/qemu-virt-riscv64/rust/src/examples/mutex_demo.rs new file mode 100644 index 00000000..8f9e9473 --- /dev/null +++ b/machines/qemu-virt-riscv64/rust/src/examples/mutex_demo.rs @@ -0,0 +1,294 @@ +/* + * Copyright (c) 2006-2024, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2024-09-25 foxglove test mutex operations + */ + +// RT-Thread mutex operation examples - demonstrates safe Rust mutex interface + +use crate::mutex::Mutex; +use crate::thread::Thread; +use alloc::string::String; +use alloc::format; +use alloc::sync::Arc; +use alloc::vec::Vec; + +/// Basic mutex creation and lock/unlock example +#[no_mangle] +pub extern "C" fn rust_mutex_basic_demo() { + println!("\n=== Rust Mutex Basic Demo ==="); + + // 创建一个基础 mutex + match Mutex::new(42i32) { + Ok(mutex) => { + println!("Mutex created successfully with initial value: 42"); + + // 获取锁并修改值 + match mutex.lock() { + Ok(mut guard) => { + println!("Lock acquired, current value: {}", *guard); + *guard = 100; + println!("Value modified to: {}", *guard); + // guard 在这里自动释放锁 + } + Err(e) => { + println!("Failed to acquire lock: {:?}", e); + } + } + + // 再次获取锁验证值 + match mutex.lock() { + Ok(guard) => { + println!("Lock acquired again, value is now: {}", *guard); + } + Err(e) => { + println!("Failed to acquire lock again: {:?}", e); + } + } + } + Err(e) => { + println!("Failed to create mutex: {:?}", e); + } + } +} + +/// Named mutex example +#[no_mangle] +pub extern "C" fn rust_mutex_named_demo() { + println!("\n=== Named Mutex Demo ==="); + + // 创建一个命名的 mutex + match Mutex::new_with_name(String::from("Hello Mutex"), "demo_mutex") { + Ok(mutex) => { + println!("Named mutex 'demo_mutex' created successfully"); + + match mutex.lock() { + Ok(mut guard) => { + println!("Current string: {}", *guard); + guard.push_str(" - Modified!"); + println!("Modified string: {}", *guard); + } + Err(e) => { + println!("Failed to acquire lock: {:?}", e); + } + } + } + Err(e) => { + println!("Failed to create named mutex: {:?}", e); + } + } +} + +/// Try lock with timeout example +#[no_mangle] +pub extern "C" fn rust_mutex_trylock_demo() { + println!("\n=== Mutex Try Lock Demo ==="); + + match Mutex::new(0u32) { + Ok(mutex) => { + println!("Mutex created for try_lock demo"); + + // 立即尝试获取锁(不等待) + match mutex.try_lock(0) { + Ok(mut guard) => { + println!("Lock acquired immediately, value: {}", *guard); + *guard += 1; + println!("Value incremented to: {}", *guard); + } + Err(e) => { + println!("Failed to acquire lock immediately: {:?}", e); + } + } + + // 尝试获取锁,最多等待100ms + match mutex.try_lock(100) { + Ok(mut guard) => { + println!("Lock acquired with 100ms timeout, value: {}", *guard); + *guard += 10; + println!("Value incremented to: {}", *guard); + } + Err(e) => { + println!("Failed to acquire lock within 100ms: {:?}", e); + } + } + } + Err(e) => { + println!("Failed to create mutex: {:?}", e); + } + } +} + +/// Mutex demonstration with immediate locking +#[no_mangle] +pub extern "C" fn rust_mutex_atomic_demo() { + println!("\n=== Mutex Immediate Lock Demo ==="); + + // 创建 mutex 并立即锁定 + match Mutex::new(0i32) { + Ok(mutex) => { + println!("Mutex created successfully"); + + match mutex.lock() { + Ok(mut guard) => { + println!("Lock acquired, value: {}", *guard); + *guard = 999; + println!("Value set to: {}", *guard); + } + Err(e) => { + println!("Failed to acquire lock: {:?}", e); + } + } + } + Err(e) => { + println!("Failed to create mutex: {:?}", e); + } + } +} + +/// Multi-threaded mutex contention example +#[no_mangle] +pub extern "C" fn rust_mutex_concurrent_demo() { + println!("\n=== Concurrent Mutex Demo ==="); + + // 创建共享的 mutex + match Mutex::new_with_name(0i32, "shared_counter") { + Ok(shared_mutex) => { + let mutex_arc = Arc::new(shared_mutex); + println!("Shared mutex created for concurrent access"); + + let mut threads = Vec::new(); + + // 创建多个线程来竞争同一个 mutex + for i in 0..3 { + let mutex_clone = Arc::clone(&mutex_arc); + + match Thread::spawn( + format!("mutex_worker_{}", i), + 2048, + 20 + i as u8, + 10, + move || { + println!("[Worker {}] Starting mutex operations", i); + + for step in 0..3 { + // 尝试获取锁 + match mutex_clone.lock() { + Ok(mut guard) => { + let old_value = *guard; + println!("[Worker {}] Step {}: Got lock, value = {}", i, step + 1, old_value); + + // 模拟一些工作 + Thread::ms_delay(50); + + *guard += 1; + println!("[Worker {}] Step {}: Incremented value to {}", i, step + 1, *guard); + + // guard 在这里自动释放锁 + } + Err(e) => { + println!("[Worker {}] Step {}: Failed to get lock: {:?}", i, step + 1, e); + } + } + + // 短暂休眠后继续 + Thread::ms_delay(30); + } + + println!("[Worker {}] Completed all mutex operations", i); + } + ) { + Ok(thread) => { + threads.push(thread); + println!("Worker thread {} created successfully", i); + } + Err(e) => { + println!("Failed to create worker thread {}: {:?}", i, e); + } + } + } + + println!("All worker threads created, waiting for completion..."); + + // 等待所有线程完成 + Thread::ms_delay(1500); + + // 检查最终值 + match mutex_arc.lock() { + Ok(guard) => { + println!("Final counter value: {} (expected: 9)", *guard); + } + Err(e) => { + println!("Failed to check final value: {:?}", e); + } + } + + println!("Concurrent mutex demo completed"); + } + Err(e) => { + println!("Failed to create shared mutex: {:?}", e); + } + } +} + +/// Different mutex types demonstration +#[no_mangle] +pub extern "C" fn rust_mutex_types_demo() { + println!("\n=== Mutex Types Demo ==="); + + // String Mutex + println!("--- String Mutex ---"); + match Mutex::new_with_name(String::from("Hello Mutex"), "string_mutex") { + Ok(string_mutex) => { + println!("String mutex created successfully"); + match string_mutex.lock() { + Ok(mut guard) => { + println!("String mutex locked: {}", *guard); + guard.push_str(" [Modified]"); + println!("String mutex data: {}", *guard); + } + Err(e) => { + println!("Failed to lock string mutex: {:?}", e); + } + } + } + Err(e) => { + println!("Failed to create string mutex: {:?}", e); + } + } + + // Numeric Mutex + println!("--- Numeric Mutex ---"); + match Mutex::new_with_name(12345u64, "numeric_mutex") { + Ok(numeric_mutex) => { + println!("Numeric mutex created successfully"); + match numeric_mutex.try_lock(100) { + Ok(mut guard) => { + println!("Numeric mutex locked: {}", *guard); + *guard *= 2; + println!("Numeric mutex data: {}", *guard); + } + Err(e) => { + println!("Failed to lock numeric mutex: {:?}", e); + } + } + } + Err(e) => { + println!("Failed to create numeric mutex: {:?}", e); + } + } +} + +/// Comprehensive mutex operations demonstration +#[no_mangle] +pub extern "C" fn rust_mutex_demo_all() { + rust_mutex_basic_demo(); + rust_mutex_named_demo(); + rust_mutex_trylock_demo(); + rust_mutex_atomic_demo(); + rust_mutex_types_demo(); + rust_mutex_concurrent_demo(); +} \ No newline at end of file diff --git a/machines/qemu-virt-riscv64/rust/src/examples/printf_demo.rs b/machines/qemu-virt-riscv64/rust/src/examples/printf_demo.rs new file mode 100644 index 00000000..8fe30b9e --- /dev/null +++ b/machines/qemu-virt-riscv64/rust/src/examples/printf_demo.rs @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2006-2024, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2025-09-20 foxglove test printf operations + */ + +// Printf examples - demonstrates formatted output functionality + +/// Demonstrate various printf formats +#[no_mangle] +pub extern "C" fn rust_printf_demo() { + // 基本输出 + println!("=== Printf Demo ==="); + + // 整数格式化 + let num: i32 = 42; + println!("Integer: {}", num); + println!("Hex: 0x{:x}", num); + println!("Octal: {:o}", num); + + // 浮点数(注意:需要软浮点支持) + // println!("Float: {}", 3.14159); + + // 字符和字符串 + let ch = 'R'; + println!("Character: {}", ch); + println!("String: {}", "RT-Thread"); + + // 指针 + let ptr = &num as *const i32; + println!("Pointer: {:p}", ptr); +} + +/// Using format! for string formatting +#[no_mangle] +pub extern "C" fn rust_sprintf_demo() -> i32 { + use alloc::format; + + // 使用 Rust 的 format! 宏进行字符串格式化 + let formatted_string = format!("Formatted: num={}, str={}", 100, "test"); + + println!("Buffer content: {}", formatted_string); + + // 返回格式化字符串的长度 + formatted_string.len() as i32 +} diff --git a/machines/qemu-virt-riscv64/rust/src/examples/sem_demo.rs b/machines/qemu-virt-riscv64/rust/src/examples/sem_demo.rs new file mode 100644 index 00000000..4c6676d8 --- /dev/null +++ b/machines/qemu-virt-riscv64/rust/src/examples/sem_demo.rs @@ -0,0 +1,256 @@ +/* + * Copyright (c) 2006-2024, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2025-01-15 foxglove semaphore demo + */ + +/// Basic semaphore operations demo +#[no_mangle] +pub extern "C" fn rust_semaphore_basic_demo() { + println!("\n=== Basic Semaphore Demo ==="); + + // Create a semaphore + match Semaphore::new_with_name("test_sem") { + Ok(sem) => { + println!("Semaphore created successfully"); + + // Test try_take (should fail initially as semaphore starts with 0) + match sem.try_take() { + Ok(_) => println!("try_take: SUCCESS (unexpected)"), + Err(e) => println!("try_take: FAILED as expected - {:?}", e), + } + + // Release semaphore to make it available + sem.release(); + println!("Semaphore released"); + + // Now try_take should succeed + match sem.try_take() { + Ok(_) => println!("try_take: SUCCESS after release"), + Err(e) => println!("try_take: FAILED - {:?}", e), + } + + // Release again for next test + sem.release(); + + // Test take with timeout + match sem.take(100) { + Ok(_) => println!("take(100): SUCCESS"), + Err(e) => println!("take(100): FAILED - {:?}", e), + } + + println!("Basic semaphore demo completed!"); + } + Err(e) => { + println!("Failed to create semaphore: {:?}", e); + } + } +} + +/// Producer-Consumer demo using semaphore +#[no_mangle] +pub extern "C" fn rust_semaphore_producer_consumer_demo() { + println!("\n=== Producer-Consumer Semaphore Demo ==="); + + // Create semaphores for synchronization + let empty_sem = match Semaphore::new_with_name("empty") { + Ok(sem) => { + // Initialize with buffer size (3 slots) + sem.release(); + sem.release(); + sem.release(); + Arc::new(sem) + } + Err(e) => { + println!("Failed to create empty semaphore: {:?}", e); + return; + } + }; + + let full_sem = match Semaphore::new_with_name("full") { + Ok(sem) => Arc::new(sem), + Err(e) => { + println!("Failed to create full semaphore: {:?}", e); + return; + } + }; + + let mutex_sem = match Semaphore::new_with_name("mutex") { + Ok(sem) => { + sem.release(); // Initialize as available + Arc::new(sem) + } + Err(e) => { + println!("Failed to create mutex semaphore: {:?}", e); + return; + } + }; + + // Shared buffer counter (simulated) + static mut BUFFER_COUNT: i32 = 0; + + // Producer thread + let empty_sem_producer = Arc::clone(&empty_sem); + let full_sem_producer = Arc::clone(&full_sem); + let mutex_sem_producer = Arc::clone(&mutex_sem); + + let producer = Thread::spawn( + String::from("producer"), + 2048, + 10, + 20, + move || { + for i in 1..=5 { + // Wait for empty slot + if let Err(e) = empty_sem_producer.take_wait_forever() { + println!("[Producer] Failed to wait for empty slot: {:?}", e); + continue; + } + + // Enter critical section + if let Err(e) = mutex_sem_producer.take_wait_forever() { + println!("[Producer] Failed to acquire mutex: {:?}", e); + continue; + } + + // Produce item + unsafe { + BUFFER_COUNT += 1; + println!("[Producer] Produced item {}, buffer count: {}", i, BUFFER_COUNT); + } + + // Exit critical section + mutex_sem_producer.release(); + + // Signal full slot + full_sem_producer.release(); + + Thread::ms_delay(200); + } + println!("[Producer] Finished producing"); + } + ); + + // Consumer thread + let empty_sem_consumer = Arc::clone(&empty_sem); + let full_sem_consumer = Arc::clone(&full_sem); + let mutex_sem_consumer = Arc::clone(&mutex_sem); + + let consumer = Thread::spawn( + String::from("consumer"), + 2048, + 10, + 20, + move || { + for i in 1..=5 { + // Wait for full slot + if let Err(e) = full_sem_consumer.take_wait_forever() { + println!("[Consumer] Failed to wait for full slot: {:?}", e); + continue; + } + + // Enter critical section + if let Err(e) = mutex_sem_consumer.take_wait_forever() { + println!("[Consumer] Failed to acquire mutex: {:?}", e); + continue; + } + + // Consume item + unsafe { + BUFFER_COUNT -= 1; + println!("[Consumer] Consumed item {}, buffer count: {}", i, BUFFER_COUNT); + } + + // Exit critical section + mutex_sem_consumer.release(); + + // Signal empty slot + empty_sem_consumer.release(); + + Thread::ms_delay(300); + } + println!("[Consumer] Finished consuming"); + } + ); + + // Wait for threads to complete + Thread::ms_delay(3000); + + println!("Producer-Consumer demo completed!"); +} + +/// Multiple threads synchronization demo +#[no_mangle] +pub extern "C" fn rust_semaphore_multi_thread_demo() { + println!("\n=== Multi-Thread Semaphore Demo ==="); + + // Create a semaphore with limited resources (2 permits) + let resource_sem = match Semaphore::new_with_name("resource") { + Ok(sem) => { + // Initialize with 2 permits + sem.release(); + sem.release(); + Arc::new(sem) + } + Err(e) => { + println!("Failed to create resource semaphore: {:?}", e); + return; + } + }; + + // Create multiple worker threads + for worker_id in 1..=4 { + let sem_clone = Arc::clone(&resource_sem); + + let _worker = Thread::spawn( + format!("worker_{}", worker_id), + 2048, + 12, + 20, + move || { + println!("[Worker {}] Trying to acquire resource...", worker_id); + + match sem_clone.take_wait_forever() { + Ok(_) => { + println!("[Worker {}] Acquired resource, working...", worker_id); + + // Simulate work + Thread::ms_delay(500); + + println!("[Worker {}] Releasing resource", worker_id); + sem_clone.release(); + } + Err(e) => { + println!("[Worker {}] Failed to acquire resource: {:?}", worker_id, e); + } + } + } + ); + } + + // Wait for all workers to complete + Thread::ms_delay(3000); + + println!("Multi-thread semaphore demo completed!"); +} + +/// Comprehensive semaphore test +#[no_mangle] +pub extern "C" fn rust_semaphore_demo_all() { + println!("\n=== Comprehensive Semaphore Demo ==="); + + // Run all demos + rust_semaphore_basic_demo(); + Thread::ms_delay(500); + + rust_semaphore_producer_consumer_demo(); + Thread::ms_delay(500); + + rust_semaphore_multi_thread_demo(); + + println!("\n=== All Semaphore Demos Completed ==="); +} \ No newline at end of file diff --git a/machines/qemu-virt-riscv64/rust/src/examples/string_demo.rs b/machines/qemu-virt-riscv64/rust/src/examples/string_demo.rs new file mode 100644 index 00000000..e6c14a91 --- /dev/null +++ b/machines/qemu-virt-riscv64/rust/src/examples/string_demo.rs @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2006-2024, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2025-09-20 foxglove test string operations + */ + +// String operation examples - demonstrates string-related function usage + +/// String length calculation example +#[no_mangle] +pub extern "C" fn rust_strlen_demo(s: *const c_char) -> usize { + libc::safe_strlen(s) +} + +/// String comparison example +#[no_mangle] +pub extern "C" fn rust_strcmp_demo(s1: *const c_char, s2: *const c_char) -> i32 { + if s1.is_null() || s2.is_null() { + return -1; + } + + unsafe { libc::strcmp(s1, s2) } +} + +/// String copy example +#[no_mangle] +pub extern "C" fn rust_strcpy_demo() { + let mut dest: [u8; 64] = [0; 64]; + let src = b"Hello, RT-Thread!\0"; + + unsafe { + libc::strcpy(dest.as_mut_ptr() as *mut c_char, src.as_ptr() as *const c_char); + let dest_str = core::ffi::CStr::from_ptr(dest.as_ptr() as *const c_char); + if let Ok(s) = dest_str.to_str() { + println!("Copied string: {}", s); + } else { + println!("Copied string: [invalid UTF-8]"); + } + } +} + +/// String concatenation example +#[no_mangle] +pub extern "C" fn rust_strcat_demo() { + let mut buffer: [u8; 128] = [0; 128]; + + // 初始化第一部分 + let part1 = b"Hello, \0"; + unsafe { + libc::strcpy(buffer.as_mut_ptr() as *mut c_char, part1.as_ptr() as *const c_char); + } + + // 连接第二部分 + let part2 = b"RT-Thread!\0"; + unsafe { + libc::strcat(buffer.as_mut_ptr() as *mut c_char, part2.as_ptr() as *const c_char); + let buffer_str = core::ffi::CStr::from_ptr(buffer.as_ptr() as *const c_char); + if let Ok(s) = buffer_str.to_str() { + println!("Concatenated string: {}", s); + } else { + println!("Concatenated string: [invalid UTF-8]"); + } + } +} + +/// String search example +#[no_mangle] +pub extern "C" fn rust_strstr_demo(haystack: *const c_char, needle: *const c_char) -> bool { + if haystack.is_null() || needle.is_null() { + return false; + } + + let result = unsafe { libc::strstr(haystack, needle) }; + !result.is_null() +} + +/// Comprehensive string operations demonstration +#[no_mangle] +pub extern "C" fn rust_string_demo_all() { + println!("\n=== String Operations Demo ==="); + + unsafe { + // 长度计算 + let test_str = b"RT-Thread\0"; + let len = libc::strlen(test_str.as_ptr() as *const c_char); + let test_str_rust = core::ffi::CStr::from_ptr(test_str.as_ptr() as *const c_char); + if let Ok(s) = test_str_rust.to_str() { + println!("Length of '{}': {}", s, len); + } else { + println!("Length of [invalid UTF-8]: {}", len); + } + + // 字符串比较 + let str1 = b"abc\0"; + let str2 = b"abd\0"; + let cmp = libc::strcmp(str1.as_ptr() as *const c_char, str2.as_ptr() as *const c_char); + let str1_rust = core::ffi::CStr::from_ptr(str1.as_ptr() as *const c_char); + let str2_rust = core::ffi::CStr::from_ptr(str2.as_ptr() as *const c_char); + if let (Ok(s1), Ok(s2)) = (str1_rust.to_str(), str2_rust.to_str()) { + println!("strcmp('{}', '{}') = {}", s1, s2, cmp); + } else { + println!("strcmp([invalid UTF-8], [invalid UTF-8]) = {}", cmp); + } + + // 字符查找 + let ch = b'd' as i32; + let found = libc::strchr(str2.as_ptr() as *const c_char, ch); + if !found.is_null() { + if let Ok(s2) = str2_rust.to_str() { + println!("Found '{}' in '{}'", ch as u8 as char, s2); + } else { + println!("Found '{}' in [invalid UTF-8]", ch as u8 as char); + } + } + } +} diff --git a/machines/qemu-virt-riscv64/rust/src/examples/thread_demo.rs b/machines/qemu-virt-riscv64/rust/src/examples/thread_demo.rs new file mode 100644 index 00000000..3815a2a6 --- /dev/null +++ b/machines/qemu-virt-riscv64/rust/src/examples/thread_demo.rs @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2006-2024, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 202-09-25 foxglove test thread operations + */ + +// RT-Thread thread operation examples - demonstrates safe Rust thread interface + +use crate::thread::Thread; +use crate::println; +use alloc::string::String; +use alloc::format; + +/// Thread creation example using Thread::spawn +#[no_mangle] +pub extern "C" fn rust_thread_create_demo() { + println!("\n=== Rust Thread Create Demo ==="); + + // 使用Thread::spawn创建线程 + match Thread::spawn( + String::from("rust_thread"), + 2048, // Stack size + 20, // Priority + 10, // Time slice + move || { + println!("[Thread 1] Started"); + + // 执行一些任务 + for i in 0..3 { + println!("[Thread 1] Working... step {}", i + 1); + Thread::ms_delay(100); // 休眠100ms + } + + println!("[Thread 1] Finished"); + } + ) { + Ok(_thread) => { + println!("Thread created and started successfully"); + + // 主线程等待一段时间 + Thread::ms_delay(500); + } + Err(e) => { + println!("Failed to create thread: {:?}", e); + } + } +} + +/// Current thread operations example +#[no_mangle] +pub extern "C" fn rust_thread_self_demo() { + println!("\n=== Current Thread Operations ==="); + + // 线程让出CPU + println!("Yielding CPU..."); + Thread::r#yield(); + println!("Resumed after yield"); +} + +/// Thread sleep example using Thread delay methods +#[no_mangle] +pub extern "C" fn rust_thread_sleep_demo() { + println!("\n=== Thread Sleep Demo ==="); + + println!("Sleeping for 1 second..."); + Thread::ms_delay(1000); + println!("Woke up after 1 second"); + + // 使用tick方式休眠 + println!("Sleeping for 50 ticks..."); + Thread::delay(50); + println!("Woke up after 50 ticks"); +} + +/// Using ThreadBuilder API +#[no_mangle] +pub extern "C" fn rust_thread_wrapper_demo() { + println!("\n=== ThreadBuilder Demo ==="); + + // 使用ThreadBuilder创建线程 + match Thread::new() + .name("rust_builder") + .stack_size(4096) + .priority(15) + .ticks(20) + .start(move || { + println!("[Thread 2] Started with ThreadBuilder"); + + for i in 0..5 { + println!("[Thread 2] Builder task step {}", i + 1); + Thread::ms_delay(80); + } + + println!("[Thread 2] ThreadBuilder task completed"); + }) { + Ok(_thread) => { + println!("Thread created using ThreadBuilder successfully"); + + // 等待线程完成 + Thread::ms_delay(600); + } + Err(e) => { + println!("Failed to create thread using ThreadBuilder: {:?}", e); + } + } +} + +/// Multiple threads concurrent execution demo +#[no_mangle] +pub extern "C" fn rust_thread_concurrent_demo() { + println!("\n=== Concurrent Threads Demo ==="); + + let mut threads = alloc::vec::Vec::new(); + + // 创建多个并发线程 + for i in 0..3 { + match Thread::spawn( + format!("worker_{}", i), + 2048, + 20 + i as u8, // 不同优先级 + 10, + move || { + println!("[Worker {}] Starting concurrent task", i); + + for step in 0..4 { + println!("[Worker {}] Step {} executing", i, step + 1); + Thread::ms_delay(150 + i * 50); // 不同的延时 + } + + println!("[Worker {}] Concurrent task completed", i); + } + ) { + Ok(thread) => { + threads.push(thread); + println!("Worker thread {} created successfully", i); + } + Err(e) => { + println!("Failed to create worker thread {}: {:?}", i, e); + } + } + } + + println!("All worker threads created, waiting for completion..."); + + // 等待所有线程完成 + Thread::ms_delay(2000); + + println!("Concurrent demo completed"); +} + +/// Comprehensive thread operations demonstration +#[no_mangle] +pub extern "C" fn rust_thread_demo_all() { + rust_thread_self_demo(); + rust_thread_sleep_demo(); + rust_thread_create_demo(); + rust_thread_wrapper_demo(); + rust_thread_concurrent_demo(); +} diff --git a/machines/qemu-virt-riscv64/rust/src/examples/vec_demo.rs b/machines/qemu-virt-riscv64/rust/src/examples/vec_demo.rs new file mode 100644 index 00000000..755c7181 --- /dev/null +++ b/machines/qemu-virt-riscv64/rust/src/examples/vec_demo.rs @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2006-2024, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2025-09-25 foxglove test vec operations with GlobalAllocator + */ + +// Imports are handled by the parent module + +#[no_mangle] +pub extern "C" fn rust_vec_demo() { + println!("\n=== Standard Vec Demo with GlobalAllocator ==="); + + // Create a Vec with initial capacity + let mut v: Vec = Vec::with_capacity(2); + println!("Created Vec with capacity: {}", v.capacity()); + + // Push elements + for i in 1..=5u32 { + v.push(i); + println!("push {} -> ok", i); + } + + println!("len={} cap={}", v.len(), v.capacity()); + + // Print all elements + for (index, &value) in v.iter().enumerate() { + println!("v[{}]={}", index, value); + } + + // Test pop operation + println!("\nTesting pop operations:"); + while let Some(value) = v.pop() { + println!("popped: {}, remaining len: {}", value, v.len()); + } + + // Test Vec methods + println!("\nTesting Vec methods:"); + v.extend_from_slice(&[10, 20, 30, 40, 50]); + println!("After extend_from_slice: len={}", v.len()); + + // Test indexing + if let Some(&value) = v.get(2) { + println!("v[2] = {}", value); + } + + // Test clear + v.clear(); + println!("After clear: len={}, cap={}", v.len(), v.capacity()); + + println!("Vec demo completed!"); +} + +#[no_mangle] +pub extern "C" fn rust_vec_advanced_demo() { + println!("\n=== Advanced Vec Operations Demo ==="); + + // Test Vec (if we had String support) + let mut numbers: Vec = Vec::new(); + + // Test reserve + numbers.reserve(10); + println!("After reserve(10): cap={}", numbers.capacity()); + + // Fill with data + for i in 0..10 { + numbers.push(i * i); + } + + // Test retain + numbers.retain(|&x| x % 2 == 0); + println!("After retain (even numbers only): len={}", numbers.len()); + + for (i, &num) in numbers.iter().enumerate() { + println!("numbers[{}] = {}", i, num); + } + + // Test insert and remove + numbers.insert(0, 999); + println!("After insert(0, 999): first element = {}", numbers[0]); + + let removed = numbers.remove(0); + println!("Removed element: {}", removed); + + // Test shrink_to_fit + numbers.shrink_to_fit(); + println!("After shrink_to_fit: len={}, cap={}", numbers.len(), numbers.capacity()); + + println!("Advanced Vec demo completed!"); +} diff --git a/machines/qemu-virt-riscv64/rust/src/init.rs b/machines/qemu-virt-riscv64/rust/src/init.rs new file mode 100644 index 00000000..df56fef5 --- /dev/null +++ b/machines/qemu-virt-riscv64/rust/src/init.rs @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2006-2024, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2025-09-15 foxglove 1.0 version + */ + +//! Rust component initialization module +//! +//! Handles the initialization of the Rust component within RT-Thread + +use crate::bindings::libc; +use crate::{print, println}; + +/// Component initialization function +/// This function is called during RT-Thread system initialization +#[no_mangle] +pub extern "C" fn rust_init() -> i32 { + // Test the print! and println! macros + println!("Rust component initialized!"); + println!("Testing Rust println! macro"); + print!("Testing Rust print! macro: "); + println!("Number test: {}", 42); + println!("String test: {}", "RT-Thread with Rust"); + print!("Multiple "); + print!("print! "); + println!("calls"); + + 0 +} \ No newline at end of file diff --git a/machines/qemu-virt-riscv64/rust/src/lib.rs b/machines/qemu-virt-riscv64/rust/src/lib.rs new file mode 100644 index 00000000..7e93bb0f --- /dev/null +++ b/machines/qemu-virt-riscv64/rust/src/lib.rs @@ -0,0 +1,189 @@ +/* + * Copyright (c) 2006-2024, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author foxglove + * 2025-09-15 foxglove 1.0 version + * 2025-09-25 foxglove 1.1 version + */ + +#![no_std] +#![allow(non_camel_case_types)] +#[warn(unused_imports)] + +pub mod bindings; +pub mod api; +pub mod malloc; +pub mod init; +pub mod out; +pub mod puts; +pub mod thread; +pub mod mutex; +pub mod sem; +pub mod queue; +pub mod time; +extern crate alloc; +use core::panic::PanicInfo; +use core::ffi::c_void; +use crate::malloc::RttAlloc; +use crate::bindings::libc; +/// Global allocator instance +#[global_allocator] +static ALLOCATOR: RttAlloc = RttAlloc; + +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub enum RTTError { + ThreadStartupErr, + MutexTakeTimeout, + SemaphoreTakeTimeout, + QueueSendTimeout, + QueueReceiveTimeout, + OutOfMemory, + + DeviceNotFound, + DeviceOpenFailed, + DeviceCloseFailed, + DeviceReadFailed, + DeviceWriteFailed, + DeviceTransFailed, + DeviceConfigFailed, + DeviceSetRxCallBackFailed, + DeviceSetTxCallBackFailed, + + FuncUnDefine, +} + +pub type RTResult = Result; + +// Example modules are gated by Cargo features so they can be toggled from Kconfig +// Each module re-exports its `#[no_mangle]` extern functions when enabled +#[cfg(feature = "example_hello")] +mod example_hello { + use crate::bindings::libc; + use crate::bindings::librt; + use crate::{print, println}; + // use core::ffi::{c_char, c_void}; + include!("examples/hello.rs"); +} +#[cfg(feature = "example_hello")] +pub use example_hello::*; + +#[cfg(feature = "example_printf")] +mod example_printf { + use crate::bindings::libc; + use crate::bindings::librt; + use core::ffi::{c_char, c_void}; + use crate::{print, println}; + include!("examples/printf_demo.rs"); +} +#[cfg(feature = "example_printf")] +pub use example_printf::*; + +#[cfg(feature = "example_string")] +mod example_string { + use crate::bindings::libc; + use crate::bindings::librt; + use core::ffi::{c_char, c_void}; + use crate::{print, println}; + include!("examples/string_demo.rs"); +} +#[cfg(feature = "example_string")] +pub use example_string::*; + +#[cfg(feature = "example_memory")] +mod example_memory { + use crate::bindings::libc; + use crate::bindings::librt; + use core::ffi::{c_char, c_void}; + use crate::{print, println}; + include!("examples/memory_demo.rs"); +} +#[cfg(feature = "example_memory")] +pub use example_memory::*; + +#[cfg(feature = "example_thread")] +mod example_thread { + use crate::bindings::libc; + use crate::bindings::librt; + use core::ffi::{c_char, c_void}; + include!("examples/thread_demo.rs"); +} +#[cfg(feature = "example_thread")] +pub use example_thread::*; + +#[cfg(feature = "example_mutex")] +mod example_mutex { + use crate::bindings::libc; + use crate::bindings::librt; + use core::ffi::{c_char, c_void}; + use crate::{print, println}; + include!("examples/mutex_demo.rs"); +} +#[cfg(feature = "example_mutex")] +pub use example_mutex::*; + +#[cfg(feature = "example_sem")] +mod example_sem { + use crate::bindings::libc; + use crate::bindings::librt; + use core::ffi::{c_char, c_void}; + use crate::{print, println}; + use crate::sem::Semaphore; + use crate::thread::Thread; + use alloc::format; + use alloc::sync::Arc; + use alloc::string::String; + include!("examples/sem_demo.rs"); +} +#[cfg(feature = "example_sem")] +pub use example_sem::*; + +#[cfg(feature = "example_vec")] +mod example_vec { + use crate::bindings::libc; + use crate::bindings::librt; + use core::{mem, ptr}; + use core::ffi::{c_char, c_void}; + use core::alloc::{GlobalAlloc, Layout}; + use alloc::vec::Vec; + use crate::{print, println}; + include!("examples/vec_demo.rs"); +} +#[cfg(feature = "example_vec")] +pub use example_vec::*; + +#[cfg(feature = "example_dl")] +mod example_dl { + use crate::bindings::libc; + use crate::bindings::libdl; + use core::ffi::{c_char, c_void, c_int}; + include!("examples/dlmodule_demo.rs"); +} +#[cfg(feature = "example_dl")] +pub use example_dl::*; + +#[cfg(feature = "example_mq")] +mod example_mq { + use crate::bindings::libc; + use crate::bindings::librt; + use core::ffi::{c_char, c_void}; + use crate::{print, println}; + use crate::queue::Queue; + use crate::time::sleep; + include!("examples/mq_demo.rs"); +} +#[cfg(feature = "example_mq")] +pub use example_mq::*; + +// Re-export initialization function +pub use init::rust_init; + +// Panic handler +#[panic_handler] +#[inline(never)] +fn panic(info: &core::panic::PanicInfo) -> ! { + print!("{:}", info); + loop {} +} diff --git a/machines/qemu-virt-riscv64/rust/src/malloc.rs b/machines/qemu-virt-riscv64/rust/src/malloc.rs new file mode 100644 index 00000000..3961ad7c --- /dev/null +++ b/machines/qemu-virt-riscv64/rust/src/malloc.rs @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2006-2024, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2025-01-XX foxglove RT-Thread GlobalAlloc implementation + */ + +use crate::api::mem::{mem_alloc, mem_alloc_aligned, mem_free, mem_free_aligned, mem_realloc}; +use core::alloc::{GlobalAlloc, Layout}; +use core::ptr; +use core::ffi::c_void; + +/// RT-Thread global allocator implementation +/// +/// This allocator provides a safe interface to RT-Thread's memory management +/// system, supporting both regular and aligned allocations. +pub struct RttAlloc; + +unsafe impl GlobalAlloc for RttAlloc { + /// Allocate memory with the given layout + /// + /// # Safety + /// This function is unsafe as required by the GlobalAlloc trait. + /// The caller must ensure proper usage according to GlobalAlloc requirements. + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + let size = layout.size(); + let align = layout.align(); + + // Handle zero-sized allocations + if size == 0 { + return ptr::null_mut(); + } + + // Use aligned allocation if alignment is greater than default + // RT-Thread's default alignment is typically 4 or 8 bytes + if align > 8 { + match mem_alloc_aligned(size, align) { + Some(ptr) => ptr as *mut u8, + None => ptr::null_mut(), + } + } else { + match mem_alloc(size) { + Some(ptr) => ptr as *mut u8, + None => ptr::null_mut(), + } + } + } + + /// Deallocate memory at the given pointer with the given layout + /// + /// # Safety + /// This function is unsafe as required by the GlobalAlloc trait. + /// The caller must ensure the pointer was allocated by this allocator + /// and the layout matches the original allocation. + unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { + if ptr.is_null() { + return; + } + + let align = layout.align(); + + // Use aligned deallocation if the original allocation was aligned + if align > 8 { + mem_free_aligned(ptr as *mut c_void); + } else { + mem_free(ptr as *mut c_void); + } + } + + /// Reallocate memory + /// + /// # Safety + /// This function is unsafe as required by the GlobalAlloc trait. + /// The caller must ensure proper usage according to GlobalAlloc requirements. + unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { + // Handle zero-sized new allocation + if new_size == 0 { + self.dealloc(ptr, layout); + return ptr::null_mut(); + } + + // Handle null pointer (equivalent to alloc) + if ptr.is_null() { + let new_layout = Layout::from_size_align_unchecked(new_size, layout.align()); + return self.alloc(new_layout); + } + + // For aligned allocations, we need to handle realloc manually + // since RT-Thread's rt_realloc may not preserve alignment + let align = layout.align(); + if align > 8 { + // Allocate new aligned memory + let new_ptr = match mem_alloc_aligned(new_size, align) { + Some(ptr) => ptr as *mut u8, + None => return ptr::null_mut(), + }; + + // Copy data from old to new + let copy_size = core::cmp::min(layout.size(), new_size); + ptr::copy_nonoverlapping(ptr, new_ptr, copy_size); + + // Free old memory + mem_free_aligned(ptr as *mut c_void); + + new_ptr + } else { + // Use RT-Thread's realloc for regular allocations + match mem_realloc(ptr as *mut c_void, new_size) { + Some(new_ptr) => new_ptr as *mut u8, + None => ptr::null_mut(), + } + } + } +} \ No newline at end of file diff --git a/machines/qemu-virt-riscv64/rust/src/mutex.rs b/machines/qemu-virt-riscv64/rust/src/mutex.rs new file mode 100644 index 00000000..cf5ea534 --- /dev/null +++ b/machines/qemu-virt-riscv64/rust/src/mutex.rs @@ -0,0 +1,212 @@ +use crate::api::*; +use crate::RTTError; +use alloc::fmt; +pub use alloc::sync::{Arc, Weak}; +use core::cell::UnsafeCell; +use core::ops::{Deref, DerefMut}; +use core::sync::atomic::*; + +const RT_WAITING_FOREVER: isize = -1; + +unsafe impl Send for Mutex {} +unsafe impl Sync for Mutex {} + +pub struct Mutex +where + M: RawMutexOps + Sized, +{ + mutex: M, + data: UnsafeCell, +} + +// Impl for Default Mutex +impl Mutex { + pub fn new(t: T) -> Result { + Ok(Mutex { + mutex: SleepMutex::create("Unnamed")?, + data: UnsafeCell::new(t), + }) + } + + pub fn new_with_name(t: T, name: &str) -> Result { + Ok(Mutex { + mutex: SleepMutex::create(name)?, + data: UnsafeCell::new(t), + }) + } + + pub fn try_lock(&self, max_wait: isize) -> Result, RTTError> { + self.mutex.take(max_wait)?; + Ok(MutexGuard { + __mutex: &self.mutex, + __data: &self.data, + }) + } + + pub fn lock(&self) -> Result, RTTError> { + self.mutex.take(RT_WAITING_FOREVER)?; + Ok(MutexGuard { + __mutex: &self.mutex, + __data: &self.data, + }) + } +} + +// Impl for all Mutex +impl Mutex { + pub fn spec_new(t: T) -> Result { + Ok(Mutex { + mutex: M::create("Unnamed")?, + data: UnsafeCell::new(t), + }) + } + + pub fn spec_new_with_name(t: T, name: &str) -> Result { + Ok(Mutex { + mutex: M::create(name)?, + data: UnsafeCell::new(t), + }) + } + + pub fn spec_try_lock(&self, max_wait: isize) -> Result, RTTError> { + self.mutex.take(max_wait)?; + Ok(MutexGuard { + __mutex: &self.mutex, + __data: &self.data, + }) + } + + pub fn spec_lock(&self) -> Result, RTTError> { + self.mutex.take(RT_WAITING_FOREVER)?; + Ok(MutexGuard { + __mutex: &self.mutex, + __data: &self.data, + }) + } +} + +impl fmt::Debug for Mutex { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "Mutex address: {:?}", self.mutex) + } +} + +pub struct MutexGuard<'a, M: RawMutexOps + 'a, T: ?Sized + 'a> { + __mutex: &'a M, + __data: &'a UnsafeCell, +} + +impl<'mutex, M: RawMutexOps, T: ?Sized> Deref for MutexGuard<'mutex, M, T> { + type Target = T; + fn deref(&self) -> &T { + unsafe { &*self.__data.get() } + } +} + +impl<'mutex, M: RawMutexOps, T: ?Sized> DerefMut for MutexGuard<'mutex, M, T> { + fn deref_mut(&mut self) -> &mut T { + unsafe { &mut *self.__data.get() } + } +} + +impl<'a, M: RawMutexOps, T: ?Sized> Drop for MutexGuard<'a, M, T> { + fn drop(&mut self) { + self.__mutex.release(); + } +} + +pub type CommonMutex = SleepMutex; +pub struct SleepMutex(APIRawMutex); + +impl fmt::Debug for SleepMutex { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{:?}", self.0) + } +} + +pub struct AtomicMutex(UnsafeCell, AtomicBool); + +pub trait RawMutexOps: Sized { + fn create(name: &str) -> Result; + fn take(&self, max_wait: isize) -> Result<(), RTTError>; + fn release(&self); + fn drop(&mut self); +} + +impl RawMutexOps for AtomicMutex { + fn create(_name: &str) -> Result { + Ok(AtomicMutex { + 0: UnsafeCell::new(INTERRUPT_FLAG_INIT), + 1: AtomicBool::new(false), + }) + } + + fn take(&self, max_wait: isize) -> Result<(), RTTError> { + let f; + unsafe { + f = self.0.get(); + *f = interrupt_disable(); + } + let ret = if max_wait == RT_WAITING_FOREVER { + while self + .1 + .compare_exchange_weak(false, true, Ordering::Acquire, Ordering::Relaxed) + .is_err() + { + // Wait until the lock looks unlocked before retrying + while self.1.load(Ordering::Relaxed) {} + } + Ok(()) + } else { + if self + .1 + .compare_exchange_weak(false, true, Ordering::Acquire, Ordering::Relaxed) + .is_ok() + { + Ok(()) + } else { + unsafe { + interrupt_enable(*f); + } + Err(RTTError::MutexTakeTimeout) + } + }; + + return ret; + } + + fn release(&self) { + self.1.store(false, Ordering::Release); + let f; + unsafe { + f = self.0.get(); + interrupt_enable(*f); + } + } + + fn drop(&mut self) {} +} + +impl RawMutexOps for SleepMutex { + fn create(name: &str) -> Result { + mutex_create(name) + .ok_or(RTTError::OutOfMemory) + .map(|m| SleepMutex(m)) + } + + fn take(&self, max_wait: isize) -> Result<(), RTTError> { + let ret = mutex_take(self.0, max_wait); + if !is_eok(ret) { + return Err(RTTError::MutexTakeTimeout); + } + Ok(()) + } + + fn release(&self) { + mutex_release(self.0); + } + + fn drop(&mut self) { + mutex_delete(self.0); + } +} \ No newline at end of file diff --git a/machines/qemu-virt-riscv64/rust/src/out.rs b/machines/qemu-virt-riscv64/rust/src/out.rs new file mode 100644 index 00000000..4b8fb552 --- /dev/null +++ b/machines/qemu-virt-riscv64/rust/src/out.rs @@ -0,0 +1,41 @@ +//! Provide the output function of debugging serial port +use crate::puts::puts; +use crate::bindings::librt::rt_kputs; +use core::fmt::{self, Write}; + +struct StdOut; + +impl fmt::Write for StdOut { + fn write_str(&mut self, s: &str) -> fmt::Result { + fn rtt_kputs(s: *const u8) { + unsafe { + rt_kputs(s); + } + } + puts(s, rtt_kputs); + Ok(()) + } +} + +pub fn _print(args: fmt::Arguments) { + unsafe { + StdOut.write_fmt(args).unwrap_unchecked(); + } +} + +#[macro_export] +macro_rules! print { + ($($arg:tt)*) => ({ + $crate::out::_print(format_args!($($arg)*)); + }); +} + +#[macro_export] +macro_rules! println { + () => ({ + $crate::out::_print(format_args!("\n")); + }); + ($($arg:tt)*) => ({ + $crate::out::_print(format_args!("{}\n", format_args!($($arg)*))); + }); +} \ No newline at end of file diff --git a/machines/qemu-virt-riscv64/rust/src/puts.rs b/machines/qemu-virt-riscv64/rust/src/puts.rs new file mode 100644 index 00000000..46f0f294 --- /dev/null +++ b/machines/qemu-virt-riscv64/rust/src/puts.rs @@ -0,0 +1,19 @@ +use core::cmp::min; + +fn up_cast(a: usize, b: usize) -> usize { + let r = a / b; + return if a % b == 0 { r } else { r + 1 }; +} + +pub(crate) fn puts(str: &str, kp: fn(s: *const u8)) { + let str = str.as_bytes(); + let mut buf = [0 as u8; 129]; + for i in 0..up_cast(str.len(), 128) { + let end = min(128, str.len() - i * 128); + for j in 0..end { + buf[j] = str[(j + i * 128) as usize]; + } + buf[end] = 0; + kp(buf.as_ptr()) + } +} \ No newline at end of file diff --git a/machines/qemu-virt-riscv64/rust/src/queue.rs b/machines/qemu-virt-riscv64/rust/src/queue.rs new file mode 100644 index 00000000..d3a8fcae --- /dev/null +++ b/machines/qemu-virt-riscv64/rust/src/queue.rs @@ -0,0 +1,99 @@ +//! Passing information between threads + +use crate::api::*; +use crate::RTTError; +use core::cell::UnsafeCell; +use core::marker::PhantomData; +use core::mem::{size_of, MaybeUninit}; +use core::ffi::c_void; + +const RT_WAITING_FOREVER: isize = -1; + +unsafe impl Send for Queue where T: Send {} +unsafe impl Sync for Queue where T: Send {} + +#[derive(Debug)] +pub struct Queue { + queue: APIRawQueue, + item_type: PhantomData>, +} + +impl Queue { + pub fn new(max_size: usize) -> Result, RTTError> { + queue_create("Unnamed", max_size as _, size_of::() as _) + .ok_or(RTTError::OutOfMemory) + .map(|m| Queue { + queue: m, + item_type: PhantomData, + }) + } + + pub fn new_with_name(name: &str, max_size: usize) -> Result, RTTError> { + queue_create(name, max_size as _, size_of::() as _) + .ok_or(RTTError::OutOfMemory) + .map(|m| Queue { + queue: m, + item_type: PhantomData, + }) + } + + pub fn try_send(&self, item: T) -> Result<(), (RTTError, T)> { + self._send(item, 0) + } + + pub fn send(&self, item: T, max_wait: i32) -> Result<(), (RTTError, T)> { + self._send(item, max_wait) + } + + pub fn send_wait_forever(&self, item: T) -> Result<(), (RTTError, T)> { + self._send(item, RT_WAITING_FOREVER as _) + } + + fn _send(&self, item: T, max_wait: i32) -> Result<(), (RTTError, T)> { + let inner = MaybeUninit::new(item); + let ret = queue_send_wait( + self.queue, + inner.as_ptr() as *const c_void, + size_of::() as _, + max_wait, + ); + return if !is_eok(ret) { + unsafe { Err((RTTError::QueueSendTimeout, inner.assume_init())) } + } else { + Ok(()) + }; + } + + pub fn try_recv(&self) -> Result { + self._receive(0) + } + + pub fn recv(&self, max_wait: i32) -> Result { + self._receive(max_wait) + } + + pub fn recv_wait_forever(&self) -> Result { + self._receive(RT_WAITING_FOREVER as _) + } + + fn _receive(&self, max_wait: i32) -> Result { + let mut inner = MaybeUninit::::uninit(); + let ret = queue_receive_wait( + self.queue, + inner.as_mut_ptr() as *mut c_void, + size_of::() as _, + max_wait, + ); + return if is_eok(ret) { + Ok(unsafe { inner.assume_init() }) + } else { + Err(RTTError::QueueReceiveTimeout) + }; + } +} + +impl Drop for Queue { + fn drop(&mut self) { + queue_delete(self.queue); + } +} \ No newline at end of file diff --git a/machines/qemu-virt-riscv64/rust/src/sem.rs b/machines/qemu-virt-riscv64/rust/src/sem.rs new file mode 100644 index 00000000..ec901b2d --- /dev/null +++ b/machines/qemu-virt-riscv64/rust/src/sem.rs @@ -0,0 +1,67 @@ +#![allow(dead_code)] + +use crate::api::*; +use crate::RTTError; +use core::cell::UnsafeCell; +use core::marker::PhantomData; + +const RT_WAITING_FOREVER: i32 = -1; + +unsafe impl Send for Semaphore {} +unsafe impl Sync for Semaphore {} + +pub struct Semaphore { + data: PhantomData<*const UnsafeCell>, + sem: APIRawSem +} + +impl Semaphore { + pub fn new() -> Result { + Self::new_with_name("unknown") + } + + pub fn new_with_name(name: &str) -> Result { + semaphore_create(name) + .ok_or(RTTError::OutOfMemory) + .map(|m| Semaphore { + data: Default::default(), + sem: m + }) + } + + pub fn try_take(&self) -> Result<(), RTTError> { + let m = semaphore_try_take(self.sem); + if !is_eok(m) { + return Err(RTTError::SemaphoreTakeTimeout); + } + Ok(()) + } + + pub fn take_wait_forever(&self) -> Result<(), RTTError> { + let ret = semaphore_take(self.sem, RT_WAITING_FOREVER as u32); + + if !is_eok(ret) { + return Err(RTTError::SemaphoreTakeTimeout); + } + + Ok(()) + } + + pub fn take(&self, max_wait: i32) -> Result<(), RTTError> { + let ret = semaphore_take(self.sem, max_wait as u32); + + if !is_eok(ret) { + return Err(RTTError::SemaphoreTakeTimeout); + } + + Ok(()) + } + + pub fn release(&self) { + semaphore_release(self.sem); + } + + fn drop(&mut self) { + semaphore_delete(self.sem) + } +} \ No newline at end of file diff --git a/machines/qemu-virt-riscv64/rust/src/thread.rs b/machines/qemu-virt-riscv64/rust/src/thread.rs new file mode 100644 index 00000000..b9e7b397 --- /dev/null +++ b/machines/qemu-virt-riscv64/rust/src/thread.rs @@ -0,0 +1,157 @@ +use crate::alloc::boxed::Box; +use crate::api::*; +use crate::{RTResult, RTTError}; +use alloc::string::String; +use core::mem; +use core::ffi::c_void; + +#[derive(Debug)] +pub struct Thread(APIRawThread); + +impl Thread { + /// Delay some ticks: ticks + /// The clock cycle will depend on the configuration of the system + pub fn delay(tick: usize) { + let _ = thread_delay(tick); + } + + /// Delay some millisecond + pub fn ms_delay(ms: usize) { + let _ = thread_m_delay(ms as i32); + } + + pub fn new() -> ThreadBuilder { + ThreadBuilder { + th_name: "uname".into(), + th_stack_size: 4096, + th_priority: 10, + th_ticks: 10, + } + } + + pub fn r#yield() { + thread_yield(); + } + + /// # Note + /// The system has the function of automatically reclaiming the end thread. + /// If a thread is very short, it is likely to end before you do delete. + /// At this time, the handle is invalid. + /// If you try to delete it, an assertion will be generated. + /// So make sure that the thread you want to delete is not finished. + /// That's why the drop function is not implemented to delete threads. + pub fn delete_thread(th: Self) { + let _ = thread_delete(th.0); + } + + /// # Note + /// Please read the `Note` of `fn delete_thread` + pub fn delete(&self) { + let _ = thread_delete(self.0); + } + + unsafe fn spawn_inner( + name: String, + stack_size: u32, + priority: u8, + ticks: u32, + func: Box, + ) -> Result { + let func = Box::new(func); + let param = &*func as *const _ as *mut _; + + extern "C" fn thread_func(param: *mut c_void) { + unsafe { + let run = Box::from_raw(param as *mut Box); + run(); + } + } + + let th_handle = thread_create( + name.as_ref(), + thread_func, + param, + stack_size, + priority, + ticks, + ) + .ok_or(RTTError::OutOfMemory)?; + + let ret = match Self::_startup(th_handle) { + Ok(_) => { + mem::forget(func); + Ok(Thread(th_handle)) + } + Err(e) => Err(e), + }; + + return ret; + } + + fn _startup(th: APIRawThread) -> Result<(), RTTError> { + let ret = thread_startup(th); + return if is_eok(ret) { + Ok(()) + } else { + Err(RTTError::ThreadStartupErr) + }; + } + + pub fn spawn( + name: String, + stack_size: u32, + priority: u8, + ticks: u32, + func: F, + ) -> RTResult + where + F: FnOnce() -> () + Send + 'static, + { + unsafe { Self::spawn_inner(name, stack_size, priority, ticks, Box::new(func)) } + } +} + +unsafe impl Send for Thread {} + +pub struct ThreadBuilder { + th_name: String, + th_stack_size: u32, + th_priority: u8, + th_ticks: u32, +} + +impl ThreadBuilder { + pub fn name(&mut self, name: &str) -> &mut Self { + self.th_name = name.into(); + self + } + + pub fn stack_size(&mut self, stack_size: u32) -> &mut Self { + self.th_stack_size = stack_size; + self + } + + pub fn priority(&mut self, priority: u8) -> &mut Self { + self.th_priority = priority; + self + } + + pub fn ticks(&mut self, ticks: u32) -> &mut Self { + self.th_ticks = ticks; + self + } + + pub fn start(&self, func: F) -> RTResult + where + F: FnOnce() -> (), + F: Send + 'static, + { + Thread::spawn( + self.th_name.clone(), + self.th_stack_size, + self.th_priority, + self.th_ticks, + func, + ) + } +} \ No newline at end of file diff --git a/machines/qemu-virt-riscv64/rust/src/time.rs b/machines/qemu-virt-riscv64/rust/src/time.rs new file mode 100644 index 00000000..2b607ba9 --- /dev/null +++ b/machines/qemu-virt-riscv64/rust/src/time.rs @@ -0,0 +1,21 @@ +use crate::api::*; +use core::time::Duration; + +pub fn sleep(time: Duration) { + let mut time = time.as_millis(); + const MAX_DELAY: u128 = i32::MAX as u128; + const MAX_DELAY_P1: u128 = i32::MAX as u128 + 1; + loop { + match time { + 1..=MAX_DELAY => { + let _ = thread_m_delay(time as i32); + return; + } + 0 => return, + MAX_DELAY_P1..=u128::MAX => { + let _ = thread_m_delay(i32::MAX); + time -= i32::MAX as u128; + } + } + } +} \ No newline at end of file diff --git a/machines/qemu-virt-riscv64/rust/tools/build_support.py b/machines/qemu-virt-riscv64/rust/tools/build_support.py new file mode 100644 index 00000000..658a59ab --- /dev/null +++ b/machines/qemu-virt-riscv64/rust/tools/build_support.py @@ -0,0 +1,236 @@ +import os +import subprocess + + +def _parse_cflags(cflags: str): + info = { + "march": None, + "mabi": None, + "rv_bits": None, # 32 or 64 + "has_f": False, + "has_d": False, + } + + if not cflags: + return info + + parts = cflags.split() + for flag in parts: + if flag.startswith("-march="): + info["march"] = flag.split("=", 1)[1] + if "rv32" in info["march"]: + info["rv_bits"] = 32 + elif "rv64" in info["march"]: + info["rv_bits"] = 64 + # crude feature detection + m = info["march"] + if m: + info["has_f"] = ("f" in m) + info["has_d"] = ("d" in m) + elif flag.startswith("-mabi="): + info["mabi"] = flag.split("=", 1)[1] + if info["mabi"] in ("ilp32d", "ilp32f", "lp64d", "lp64f"): + # floating-point ABI implies FPU availability + info["has_f"] = True + info["has_d"] = info["mabi"].endswith("d") + + return info + + +def detect_rust_target(has, rtconfig): + """ + Decide the Rust target triple based on RT-Thread Kconfig and rtconfig.*. + `has` is a callable: has("SYMBOL") -> bool + """ + # ARM Cortex-M + if has("ARCH_ARM"): + # FPU hints from flags/macros + cflags = getattr(rtconfig, "CFLAGS", "") + hard_float = "-mfloat-abi=hard" in cflags or has("ARCH_ARM_FPU") or has("ARCH_FPU_VFP") + + if has("ARCH_ARM_CORTEX_M3"): + return "thumbv7m-none-eabi" + if has("ARCH_ARM_CORTEX_M4") or has("ARCH_ARM_CORTEX_M7"): + return "thumbv7em-none-eabihf" if hard_float else "thumbv7em-none-eabi" + if has("ARCH_ARM_CORTEX_M33"): + # v8m.main + return "thumbv8m.main-none-eabi" + if has("ARCH_ARM_CORTEX_A"): + return "armv7a-none-eabi" + + # AArch64 + if has("ARCH_AARCH64") or has("ARCH_ARMV8") or has("ARCH_ARM64"): + if has("ARCH_CPU_FLOAT_ABI_SOFT"): + return "aarch64-unknown-none-softfloat" + return "aarch64-unknown-none" + + # RISC-V + if has("ARCH_RISCV32") or has("ARCH_RISCV64"): + cflags = getattr(rtconfig, "CFLAGS", "") + info = _parse_cflags(cflags) + + # fallback to Kconfig hint if march not present + rv_bits = info["rv_bits"] or (32 if has("ARCH_RISCV32") else 64) + + # ABI must carry f/d to actually use hard-float calling convention + abi = info["mabi"] or "" + abi_has_fp = abi.endswith("f") or abi.endswith("d") + has_fpu = has("ARCH_RISCV_FPU") or has("ENABLE_FPU") or info["has_f"] or info["has_d"] + + if rv_bits == 32: + # Only pick *f* target when ABI uses hard-float; otherwise use soft-float even if core has F/D + return "riscv32imafc-unknown-none-elf" if abi_has_fp else "riscv32imac-unknown-none-elf" + else: + # rv64: prefer gc (includes fd) only when ABI uses hard-float + return "riscv64gc-unknown-none-elf" if abi_has_fp else "riscv64imac-unknown-none-elf" + + # Fallback by ARCH string or CFLAGS + arch = getattr(rtconfig, "ARCH", None) + if arch: + arch_l = arch.lower() + if "aarch64" in arch_l: + return "aarch64-unknown-none" + if "arm" == arch_l or "armv7" in arch_l: + return "armv7a-none-eabi" + if "riscv32" in arch_l: + return "riscv32imac-unknown-none-elf" + if "riscv64" in arch_l or "risc-v" in arch_l: + # Many BSPs use "risc-v" token; assume 64-bit for virt64 + return "riscv64imac-unknown-none-elf" + + # Parse CFLAGS for hints + cflags = getattr(rtconfig, "CFLAGS", "") + if "-mcpu=cortex-m3" in cflags: + return "thumbv7m-none-eabi" + if "-mcpu=cortex-m4" in cflags or "-mcpu=cortex-m7" in cflags: + if "-mfpu=" in cflags and "-mfloat-abi=hard" in cflags: + return "thumbv7em-none-eabihf" + return "thumbv7em-none-eabi" + if "-march=rv32" in cflags: + return "riscv32imafc-unknown-none-elf" if ("f" in cflags or "d" in cflags) else "riscv32imac-unknown-none-elf" + if "-march=rv64" in cflags: + if ("-mabi=lp64d" in cflags) or ("-mabi=lp64f" in cflags) or ("f" in cflags) or ("d" in cflags): + return "riscv64gc-unknown-none-elf" + return "riscv64imac-unknown-none-elf" + + return None + + +def make_rustflags(rtconfig, target: str): + rustflags = [ + "-C", "opt-level=z", + "-C", "panic=abort", + "-C", "relocation-model=static", + ] + + if "riscv" in target: + rustflags += [ + "-C", "code-model=medium", + "-C", "link-dead-code", + ] + # propagate march/mabi for consistency (use link-arg for staticlib builds – harmless) + cflags = getattr(rtconfig, "CFLAGS", "") + for flag in cflags.split(): + if flag.startswith("-march=") or flag.startswith("-mabi="): + rustflags += ["-C", f"link-arg={flag}"] + + if "thumb" in target or "aarch64" in target: + rustflags += ["-C", "link-arg=-nostartfiles"] + + return " ".join(rustflags) + + +def collect_features(has): + feats = [] + if has("RUST_EXAMPLE_HELLO"): + feats.append("example_hello") + if has("RUST_EXAMPLE_MEMORY"): + feats.append("example_memory") + if has("RUST_EXAMPLE_STRING"): + feats.append("example_string") + if has("RUST_EXAMPLE_PRINTF"): + feats.append("example_printf") + if has("RUST_EXAMPLE_THREAD"): + feats.append("example_thread") + if has("RUST_EXAMPLE_MUTEX"): + feats.append("example_mutex") + if has("RUST_EXAMPLE_SEM"): + feats.append("example_sem") + if has("RUST_EXAMPLE_MQ"): + feats.append("example_mq") + if has("RT_USING_SMP"): + feats.append("smp") + if has("RUST_EXAMPLE_VEC"): + feats.append("example_vec") + if has("RUST_EXAMPLE_DL"): + feats.append("example_dl") + return feats + + +def verify_rust_toolchain(): + try: + r1 = subprocess.run(["rustc", "--version"], capture_output=True, text=True) + r2 = subprocess.run(["cargo", "--version"], capture_output=True, text=True) + return r1.returncode == 0 and r2.returncode == 0 + except Exception: + return False + + +def ensure_rust_target_installed(target: str): + try: + result = subprocess.run(["rustup", "target", "list", "--installed"], capture_output=True, text=True) + if result.returncode == 0 and target in result.stdout: + return True + print(f"Rust target '{target}' is not installed.") + print(f"Please install it with: rustup target add {target}") + except Exception: + print("Warning: Failed to check rustup target list (rustup missing?)") + return False + + +def cargo_build_staticlib(rust_dir: str, target: str, features, debug: bool, rustflags: str = None): + build_root = os.path.join(os.path.abspath(os.path.join(rust_dir, os.pardir)), "build", "rust") + target_dir = os.path.join(build_root, "target") + os.makedirs(build_root, exist_ok=True) + + env = os.environ.copy() + if rustflags: + prev = env.get("RUSTFLAGS", "").strip() + env["RUSTFLAGS"] = (prev + " " + rustflags).strip() if prev else rustflags + env["CARGO_TARGET_DIR"] = target_dir + + cmd = ["cargo", "build", "--target", target, "--manifest-path", os.path.join(rust_dir, "Cargo.toml")] + if not debug: + cmd.insert(2, "--release") + if features: + cmd += ["--no-default-features", "--features", ",".join(features)] + + print("Building Rust component (cargo)…") + res = subprocess.run(cmd, cwd=rust_dir, env=env, capture_output=True, text=True) + if res.returncode != 0: + print("Warning: Rust build failed") + if res.stderr: + print(res.stderr) + return None + + mode = "debug" if debug else "release" + lib_path = os.path.join(target_dir, target, mode, "librt_rust.a") + if os.path.exists(lib_path): + print("Rust component built successfully") + return lib_path + print("Warning: Library not found at expected location") + return None + + +def clean_rust_build(bsp_root: str): + build_dir = os.path.join(bsp_root, "build", "rust") + if os.path.exists(build_dir): + print("Cleaning Rust build artifacts…") + import shutil + try: + shutil.rmtree(build_dir) + print("Rust build artifacts cleaned") + except Exception as e: + print(f"Warning: Failed to clean Rust artifacts: {e}") + else: + print("No Rust build artifacts to clean") \ No newline at end of file diff --git "a/tutorials/QEMURISCV64-\347\216\257\345\242\203\351\205\215\347\275\256\346\220\255\345\273\272.assets/image-20250320175150469.png" "b/tutorials/QEMURISCV64-\347\216\257\345\242\203\351\205\215\347\275\256\346\220\255\345\273\272.assets/image-20250320175150469.png" new file mode 100644 index 00000000..ec8c8030 Binary files /dev/null and "b/tutorials/QEMURISCV64-\347\216\257\345\242\203\351\205\215\347\275\256\346\220\255\345\273\272.assets/image-20250320175150469.png" differ diff --git "a/tutorials/QEMURISCV64-\347\216\257\345\242\203\351\205\215\347\275\256\346\220\255\345\273\272.assets/image-20250320175511370.png" "b/tutorials/QEMURISCV64-\347\216\257\345\242\203\351\205\215\347\275\256\346\220\255\345\273\272.assets/image-20250320175511370.png" new file mode 100644 index 00000000..f2ce8888 Binary files /dev/null and "b/tutorials/QEMURISCV64-\347\216\257\345\242\203\351\205\215\347\275\256\346\220\255\345\273\272.assets/image-20250320175511370.png" differ diff --git "a/tutorials/QEMURISCV64-\347\216\257\345\242\203\351\205\215\347\275\256\346\220\255\345\273\272.assets/image-20250320175600428.png" "b/tutorials/QEMURISCV64-\347\216\257\345\242\203\351\205\215\347\275\256\346\220\255\345\273\272.assets/image-20250320175600428.png" new file mode 100644 index 00000000..eef33890 Binary files /dev/null and "b/tutorials/QEMURISCV64-\347\216\257\345\242\203\351\205\215\347\275\256\346\220\255\345\273\272.assets/image-20250320175600428.png" differ diff --git "a/tutorials/QEMURISCV64-\347\216\257\345\242\203\351\205\215\347\275\256\346\220\255\345\273\272.assets/image-20250320180052572.png" "b/tutorials/QEMURISCV64-\347\216\257\345\242\203\351\205\215\347\275\256\346\220\255\345\273\272.assets/image-20250320180052572.png" new file mode 100644 index 00000000..8cc1ac09 Binary files /dev/null and "b/tutorials/QEMURISCV64-\347\216\257\345\242\203\351\205\215\347\275\256\346\220\255\345\273\272.assets/image-20250320180052572.png" differ diff --git "a/tutorials/QEMURISCV64-\347\216\257\345\242\203\351\205\215\347\275\256\346\220\255\345\273\272.assets/image-20250320180327389.png" "b/tutorials/QEMURISCV64-\347\216\257\345\242\203\351\205\215\347\275\256\346\220\255\345\273\272.assets/image-20250320180327389.png" new file mode 100644 index 00000000..d55aa1fd Binary files /dev/null and "b/tutorials/QEMURISCV64-\347\216\257\345\242\203\351\205\215\347\275\256\346\220\255\345\273\272.assets/image-20250320180327389.png" differ diff --git "a/tutorials/QEMURISCV64-\347\216\257\345\242\203\351\205\215\347\275\256\346\220\255\345\273\272.assets/image-20250321093516364.png" "b/tutorials/QEMURISCV64-\347\216\257\345\242\203\351\205\215\347\275\256\346\220\255\345\273\272.assets/image-20250321093516364.png" new file mode 100644 index 00000000..7c4dbe6a Binary files /dev/null and "b/tutorials/QEMURISCV64-\347\216\257\345\242\203\351\205\215\347\275\256\346\220\255\345\273\272.assets/image-20250321093516364.png" differ diff --git "a/tutorials/QEMURISCV64-\347\216\257\345\242\203\351\205\215\347\275\256\346\220\255\345\273\272.assets/image-20250321105730494.png" "b/tutorials/QEMURISCV64-\347\216\257\345\242\203\351\205\215\347\275\256\346\220\255\345\273\272.assets/image-20250321105730494.png" new file mode 100644 index 00000000..352dcf48 Binary files /dev/null and "b/tutorials/QEMURISCV64-\347\216\257\345\242\203\351\205\215\347\275\256\346\220\255\345\273\272.assets/image-20250321105730494.png" differ diff --git "a/tutorials/QEMURISCV64-\347\216\257\345\242\203\351\205\215\347\275\256\346\220\255\345\273\272.assets/image-20250322151217123.png" "b/tutorials/QEMURISCV64-\347\216\257\345\242\203\351\205\215\347\275\256\346\220\255\345\273\272.assets/image-20250322151217123.png" new file mode 100644 index 00000000..79947b25 Binary files /dev/null and "b/tutorials/QEMURISCV64-\347\216\257\345\242\203\351\205\215\347\275\256\346\220\255\345\273\272.assets/image-20250322151217123.png" differ diff --git "a/tutorials/QEMURISCV64-\347\216\257\345\242\203\351\205\215\347\275\256\346\220\255\345\273\272.assets/image-20250322151339894.png" "b/tutorials/QEMURISCV64-\347\216\257\345\242\203\351\205\215\347\275\256\346\220\255\345\273\272.assets/image-20250322151339894.png" new file mode 100644 index 00000000..04084bd4 Binary files /dev/null and "b/tutorials/QEMURISCV64-\347\216\257\345\242\203\351\205\215\347\275\256\346\220\255\345\273\272.assets/image-20250322151339894.png" differ diff --git "a/tutorials/QEMURISCV64-\347\216\257\345\242\203\351\205\215\347\275\256\346\220\255\345\273\272.assets/image-20250322151348431.png" "b/tutorials/QEMURISCV64-\347\216\257\345\242\203\351\205\215\347\275\256\346\220\255\345\273\272.assets/image-20250322151348431.png" new file mode 100644 index 00000000..8ea137c1 Binary files /dev/null and "b/tutorials/QEMURISCV64-\347\216\257\345\242\203\351\205\215\347\275\256\346\220\255\345\273\272.assets/image-20250322151348431.png" differ diff --git "a/tutorials/QEMURISCV64-\347\216\257\345\242\203\351\205\215\347\275\256\346\220\255\345\273\272.md" "b/tutorials/QEMURISCV64-\347\216\257\345\242\203\351\205\215\347\275\256\346\220\255\345\273\272.md" new file mode 100644 index 00000000..a64b66f5 --- /dev/null +++ "b/tutorials/QEMURISCV64-\347\216\257\345\242\203\351\205\215\347\275\256\346\220\255\345\273\272.md" @@ -0,0 +1,417 @@ +# QEMU/RISCV64 环境配置搭建快速上手 + +## 安装环境 + +VMware Workstation Pro + +Ubuntu版本:24.06 + +QEMU版本:8.2.2(最低需要6.2.0) + +网络部分:建议在搭建前提前配置好代理,主要是git和wget + +建议执行以下ubuntu的软件安装,确保已经有git等工具 + +``` +sudo apt-get update +sudo apt-get install git build-essential cmake +``` + +## 配置RT-Thread ENV环境 + +### ENV工具简介 +RT-Thread推出的开发辅助工具env,是针对基于RT-Thread操作系统的项目工程提供编译构建环境、图形化系统配置以及软件包管理三大核心功能。 + +其内置的menuconfig提供了简单易用的配置剪裁工具 + +用户可以轻松对内核、组件和软件包进行自由裁剪,从而以搭积木的方式灵活构建系统。 + +这一机制与Linux中的menuconfig源码配置方式类似,极大地简化了系统的定制化过程,提升了开发效率。 + +### 下载安装 + +``` +wget https://raw.githubusercontent.com/RT-Thread/env/master/install_ubuntu.sh +chmod 777 install_ubuntu.sh +./install_ubuntu.sh +sudo apt install scons python3-kconfiglib python3-tqdm +``` + +安装完成后需要激活当前终端的环境变量, + +> 注意:激活的环境变量只对当前终端有效 + +``` +source ~/.env/env.sh +``` + +> 如果需要在开启终端的时候自动激活,可以将这条命令放到~/.bashrc里,这样开启新终端的时候就会自动输入这条命令 +> +> ENV仓库地址:[RT-Thread/env: Python Scripts for RT-Thread/ENV](https://github.com/RT-Thread/env) + +## 第一步:拉取仓库并配置工具链 + +### 介绍仓库 + +qemu-edu仓库主要用于板卡的适配工作,通过将RT-Thread内核以子模块的形式集成到项目中,实现了对不同板卡的支持与功能扩展。 + +### 拉取仓库 + +``` +cd ~ +git clone https://github.com/oscomp/RT-Thread +cd qemu-edu +git submodule update --init --force --recursive +``` + +git clone用于拉取仓库 + +git submodule用于拉取RT-Thread内核仓库 + +### 配置工具链 + +下载对应的Smart工具链压缩包(此处的工具链仅供演示) + +来到拉取的仓库下的toolchains目录下,找到对应riscv的工具链,解压到/opt目录 + +``` +cd toolchains/qemu-virt-riscv64 +sudo tar zxvf riscv64-linux-musleabi_for_x86_64-pc-linux-gnu_latest.tar.bz2 -C /opt +``` + +配置临时环境变量 + +``` +export RTT_EXEC_PATH=/opt/riscv64gc-linux-musleabi_for_x86_64-pc-linux-gnu/bin +export RTT_CC_PREFIX=riscv64-unknown-linux-musl- +export PATH=/opt/riscv64gc-linux-musleabi_for_x86_64-pc-linux-gnu/bin:$PATH +``` + +验证下是否配置成功 + +``` +riscv64-unknown-linux-musl-gcc --version +``` + +![image-20250321105730494](./QEMURISCV64-环境配置搭建.assets/image-20250321105730494.png) + +### 配置menuconfig组件和软件包 + +#### RT-Thread Smart配置 + +为了让内核能够支持运行挂载镜像上的脚本,这里需要开启RT-Thread Smart功能 + +在终端输入scons --menuconfig打开图形化配置界面 + +``` +source ~/.env/env.sh +scons --menuconfig +``` + +上下键控制移动,空格确认打开,回车进入子菜单(如果有的话),ESC键返回 + +找到RT-Thread Kernel选项进入可以看到对应的Enable RT-Thread Smart,按下空格看见有个星号即可 + +![image-20250320175150469](./QEMURISCV64-环境配置搭建.assets/image-20250320175150469.png) + +按下ESC返回,直到退出的最后一步会询问你是否保存,按下Y即可保存 + +#### 文件系统配置 + +为了能支持ext4镜像,还需要打开对应的软件包,跟上述配置一样的操作 + +如图第一排的是路径,按照该路径来到当前菜单 + +随后选择lwext4软件包即可打开该软件包 + +![image-20250320175511370](./QEMURISCV64-环境配置搭建.assets/image-20250320175511370.png) + +按下ESC返回,最后一步会询问你是否保存,按下Y即可保存 + +![image-20250320175600428](./QEMURISCV64-环境配置搭建.assets/image-20250320175600428.png) + +由于软件包是需要通过外部仓库的形式下载的,所以用到下面的命令下载对应仓库 + +``` +pkgs --update +``` + +这样就算是配置好了,随后编译 + +``` +scons -j12 +``` + +编译完成后会在目录下生成一个rtthread.bin文件,这里就是等下要用到的内核文件 + +## 第二步:安装QEMU并运行QEMU-RISCV模拟器 + +打开终端输入下面的命令安装高版本的QEMU + +``` +sudo apt update +sudo apt install qemu-system-misc +``` + +安装完毕后可以看一下版本 + +``` +qemu-system-riscv64 --version +``` + +![image-20250321093516364](./QEMURISCV64-环境配置搭建.assets/image-20250321093516364.png) + +### 运行QEMU-RISCV模拟器 + +来到qemu-virt-riscv64目录下,输入运行命令即可将rtthread跑起来 + +``` +qemu-system-riscv64 -nographic -machine virt -m 256M -kernel rtthread.bin +``` + +在当前目录下有一个脚本"run.sh",里面默认配置各种硬件设备,包括存储设备、网络设备和串行设备,从而实现虚拟机与外部环境的交互。 + +``` +./run.sh +``` + +同样输入命令运行即可调用刚刚生成的bin文件启动qemu + +![image-20250320180052572](./QEMURISCV64-环境配置搭建.assets/image-20250320180052572.png) + +### 挂载fat格式镜像 + +当第一次使用run.sh启动qemu时,会在当前目录下自动生成空的sd.bin的fat格式镜像文件 + +可以尝试对其进行挂载 + +``` +mount virtio-blk0 / elm +``` + +![image-20250320180327389](./QEMURISCV64-环境配置搭建.assets/image-20250320180327389.png) + +- 如何退出 qemu,CTRL + a 组合键按下后,松开,再按一下 x 键 + +# 第三步:尝试编译评测案例并挂载ext4镜像 + +对[赛题评测样例]([oscomp/testsuits-for-oskernel at pre-2025](https://github.com/oscomp/testsuits-for-oskernel/tree/pre-2025))进行编译测试 + +首先拉取仓库到本地 + +``` +git clone -b pre-2025 https://github.com/oscomp/testsuits-for-oskernel.git +``` + +## 修改工具链 + +通过修改Makefile来调用 RT-Thread Smart的工具链 + +分别修改以下文件: + +### Makefile: + +``` +DOCKER ?= docker.educg.net/cg/os-contest:20250226 + +all: sdcard + +build-all: build-rv + +build-rv: + make -f Makefile.sub clean + mkdir -p sdcard/riscv/musl + make -f Makefile.sub PREFIX=riscv64-unknown-linux-musl- DESTDIR=sdcard/riscv/musl + cp /opt/riscv64gc-linux-musleabi_for_x86_64-pc-linux-gnu/riscv64-unknown-linux-musl/lib/libc.so sdcard/riscv/musl/lib + sed -E -i 's/#### OS COMP TEST GROUP ([^ ]+) ([^ ]+) ####/#### OS COMP TEST GROUP \1 \2-musl ####/g' sdcard/riscv/musl/*_testcode.sh + +sdcard: build-all .PHONY + dd if=/dev/zero of=sdcard-rv.img count=128 bs=1M + mkfs.ext4 sdcard-rv.img + mkdir -p mnt + sudo mount sdcard-rv.img mnt + sudo cp -rL sdcard/riscv/* mnt + sudo umount mnt + +clean: + make -f Makefile.sub clean + rm -rf sdcard/riscv/* + rm -rf sdcard/loongarch/* + +docker: + docker run --rm -it -v .:/code --entrypoint bash -w /code --privileged $(DOCKER) + + +.PHONY: + +``` + +### basic/user/CMakeLists.txt: + +``` +cmake_minimum_required(VERSION 3.13) + +project(user) +enable_language(ASM) +set(CMAKE_OSX_DEPLOYMENT_TARGET "") + +# Path +if (${ARCH} STREQUAL riscv32 OR ${ARCH} STREQUAL riscv64) + set(ARCH_DIR lib/arch/riscv) +else() + set(ARCH_DIR lib/arch/${ARCH}) +endif() + +set(ASM_DIR asm) +set(BIN_DIR bin) + +# Toolchain +set(PREFIX ${ARCH}-unknown-linux-musl-) +# riscv64-unknown-elf- + +if (${ARCH} STREQUAL riscv32) + set(CMAKE_C_FLAGS "-march=rv32imac -mabi=ilp32 -mcmodel=medany") +elseif (${ARCH} STREQUAL riscv64) + set(CMAKE_C_FLAGS "-march=rv64imac -mabi=lp64 -mcmodel=medany") +else() + message("Unsupported arch: ${ARCH}") +endif () + +set(CMAKE_ASM_COMPILER ${PREFIX}gcc) +set(CMAKE_C_COMPILER ${PREFIX}gcc) +set(CMAKE_OBJCOPY ${PREFIX}objcopy) +set(CMAKE_OBJDUMP ${PREFIX}objdump) +set(CMAKE_RANLIB ${PREFIX}ranlib) +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-builtin -nostdinc -fno-stack-protector -ggdb -Wall") +set(CMAKE_ASM_FLAGS ${CMAKE_C_FLAGS}) +set(CMAKE_C_LINK_FLAGS "${LINK_FLAGS} -nostdlib -T ${CMAKE_CURRENT_SOURCE_DIR}/${ARCH_DIR}/user.ld") + +# Library +aux_source_directory(lib LIBS) +#set(LIBS ${ARCH_DIR}/crt.S ${LIBS}) +set(LIBS ${ARCH_DIR}/crt.S ${ARCH_DIR}/clone.s ${LIBS}) +add_library(ulib ${LIBS} syscall_ids) +include_directories(include/) +target_include_directories(ulib PRIVATE ${ARCH_DIR}) + +# Execuatble +set(ENTRY "0x1000") +#aux_source_directory(src SRCS) +aux_source_directory(src/oscomp SRCS) +set(EXECUTABLE_OUTPUT_PATH ${ARCH}) +foreach(PATH ${SRCS}) + get_filename_component(NAME ${PATH} NAME_WE) + + if ("${CHAPTER}" STREQUAL 2) + math(EXPR ENTRY "0x80400000" OUTPUT_FORMAT HEXADECIMAL) + endif () + + if ("${CHAPTER}" MATCHES 3_*) + if(${NAME} STREQUAL ch4_mmap0) + break () + elseif (${NAME} STREQUAL ch2_exit OR ${NAME} STREQUAL ch3_1_yield0 OR ${NAME} STREQUAL ch3_2_stride0) + math(EXPR ENTRY "0x80400000" OUTPUT_FORMAT HEXADECIMAL) + elseif (${NAME} STREQUAL ch3t_deadloop) + math(EXPR ENTRY "0x80500000" OUTPUT_FORMAT HEXADECIMAL) + else () + math(EXPR ENTRY "${ENTRY} + 0x20000" OUTPUT_FORMAT HEXADECIMAL) + endif () + endif () + + add_executable(${NAME} ${PATH}) + target_link_libraries(${NAME} ulib) + target_link_options(${NAME} PRIVATE -Ttext ${ENTRY}) + + add_custom_command( + TARGET ${NAME} + POST_BUILD + COMMAND mkdir -p ${ASM_DIR} + COMMAND ${CMAKE_OBJDUMP} ARGS -d -S $ > ${ASM_DIR}/${NAME}.asm + ) + add_custom_command( + TARGET ${NAME} + POST_BUILD + COMMAND mkdir -p ${BIN_DIR} + COMMAND ${CMAKE_OBJCOPY} ARGS -O binary $ ${BIN_DIR}/${NAME}.bin --set-section-flags .bss=alloc,load,contents + ) +endforeach() + +add_custom_command( + OUTPUT syscall_ids.h + COMMAND cp ${CMAKE_SOURCE_DIR}/${ARCH_DIR}/syscall_ids.h.in ${CMAKE_SOURCE_DIR}/lib/syscall_ids.h + COMMAND sed ARGS -n -e s/__NR_/SYS_/p + < ${CMAKE_SOURCE_DIR}/${ARCH_DIR}/syscall_ids.h.in + >> ${CMAKE_SOURCE_DIR}/lib/syscall_ids.h +) +``` + +由于我这新开了一个终端所以还需要配置下临时环境变量 + +``` +export RTT_EXEC_PATH=/opt/riscv64gc-linux-musleabi_for_x86_64-pc-linux-gnu/bin +export RTT_CC_PREFIX=riscv64-unknown-linux-musl- +export PATH=/opt/riscv64gc-linux-musleabi_for_x86_64-pc-linux-gnu/bin:$PATH +``` + +这时输入make即可自动编译 + +最后生成的sdcard-rv.img移动到qemu-virt-riscv64目录下即可 + +运行模拟器并且挂载ext4镜像 + +``` +./run sdcard-rv.img +``` + +跑起来后手动挂载一下 + +``` +mount virtio-blk0 / ext +``` + +![image-20250322151339894](./QEMURISCV64-环境配置搭建.assets/image-20250322151339894.png) + +![image-20250322151348431](./QEMURISCV64-环境配置搭建.assets/image-20250322151348431.png) + +# 第五步:QEMU-LoongsonLab环境搭建与配置 + +## 下载仓库 + +``` +git clone https://github.com/LoongsonLab/rt-thread.git +``` + +## 工具链安装 + +``` +wget https://github.com/LoongsonLab/oscomp-toolchains-for-oskernel/releases/download/loongarch64-cross-toolchains/loongarch64-musl-gcc-nightly-2025-3-15.tar.gz +sudo tar zxf loongarch64-musl-gcc-nightly-2025-3-15.tar.gz -C /opt/ +``` + +## 配置环境变量 + +``` +export RTT_EXEC_PATH=/opt/riscv64gc-linux-musleabi_for_x86_64-pc-linux-gnu/bin +export RTT_CC_PREFIX=riscv64-unknown-linux-musl- +export PATH=/opt/riscv64gc-linux-musleabi_for_x86_64-pc-linux-gnu/bin:$PATH +``` + +## 环境搭建 + +``` +cd bsp/qemu-virt64-loongarch +scons --menuconfig (无需设置,进入后在退出保存一下相当于初始化) +source ~/.env/env.sh +pkgs --update +scons -j16 +``` + +## 运行 + +输入qemu的命令运行或者运行目录下的'run.sh' 但是注意要将run.sh里面的-m 256M改为-m 1024M + +``` +qemu-system-loongarch64 -nographic -machine virt -cpu la464 -m 1024M -kernel rtthread.elf +``` + +![image-20250322151217123](./QEMURISCV64-环境配置搭建.assets/image-20250322151217123.png)