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/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/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.toml b/machines/qemu-virt-riscv64/rust/Cargo.toml new file mode 100644 index 00000000..c881d3ae --- /dev/null +++ b/machines/qemu-virt-riscv64/rust/Cargo.toml @@ -0,0 +1,30 @@ +[package] +name = "rt-rust" +version = "0.1.0" +edition = "2021" + +[lib] +name = "rt_rust" +crate-type = ["staticlib"] + +[dependencies] + +[features] +default = [] +example_hello = [] + +example_memory = [] +example_string = [] +example_printf = [] +example_thread = [] +example_dl = [] +example_vec = [] + +[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..f2451832 --- /dev/null +++ b/machines/qemu-virt-riscv64/rust/Kconfig @@ -0,0 +1,64 @@ +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_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..b64f3c74 --- /dev/null +++ b/machines/qemu-virt-riscv64/rust/rust_cmd.c @@ -0,0 +1,394 @@ +/* + * 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_demo_all(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 + { + printf("Usage: rust_thread [create|self|sleep|wrapper]\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"); + } + return 0; +} +MSH_CMD_EXPORT_ALIAS(cmd_rust_thread, rust_thread, RT-Thread 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 + 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/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..6a44349e --- /dev/null +++ b/machines/qemu-virt-riscv64/rust/src/examples/hello.rs @@ -0,0 +1,39 @@ +/* + * 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() { + unsafe { + libc::printf(b"Hello from Rust!\n\0".as_ptr()); + } +} + +/// 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 { + libc::printf(b"Hello, \0".as_ptr()); + libc::printf(name as *const u8); + libc::printf(b"!\n\0".as_ptr()); + } + } +} + +/// 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..d688ed2a --- /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); + + // 打印结果 + libc::printf(b"Buffer after memset: \0".as_ptr()); + for i in 0..32 { + libc::putchar(buffer[i] as i32); + } + libc::printf(b"\n\0".as_ptr()); + } +} + +/// 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, + ); + libc::printf(b"memcmp(buf1, buf2) = %d (should be 0)\n\0".as_ptr(), result1); + + let result2 = libc::memcmp( + buf1.as_ptr() as *const c_void, + buf3.as_ptr() as *const c_void, + 5, + ); + libc::printf(b"memcmp(buf1, buf3) = %d (should be negative)\n\0".as_ptr(), result2); + } +} + +/// Standard C library malloc/free example +#[no_mangle] +pub extern "C" fn rust_malloc_demo() { + unsafe { + libc::printf(b"\n=== Standard C malloc/free Demo ===\n\0".as_ptr()); + + // 分配内存 + let size = 100; + let ptr = libc::malloc(size); + + if ptr.is_null() { + libc::printf(b"malloc failed!\n\0".as_ptr()); + return; + } + + libc::printf(b"Allocated %zu bytes at %p\n\0".as_ptr(), size, ptr); + + // 使用内存 + libc::memset(ptr, 0x42, size); + + // 释放内存 + libc::free(ptr); + libc::printf(b"Memory freed\n\0".as_ptr()); + } +} + +/// RT-Thread memory allocation example +#[no_mangle] +pub extern "C" fn rust_rt_malloc_demo() { + unsafe { + libc::printf(b"\n=== RT-Thread malloc/free Demo ===\n\0".as_ptr()); + + // 使用RT-Thread的内存分配 + let size = 64; + if let Some(ptr) = librt::rt_safe_malloc(size) { + libc::printf(b"RT-Thread allocated %zu bytes at %p\n\0".as_ptr(), 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; + } + libc::printf(b"Data sum: %u\n\0".as_ptr(), sum); + + // 释放内存 + librt::rt_safe_free(ptr); + libc::printf(b"RT-Thread memory freed\n\0".as_ptr()); + } else { + libc::printf(b"RT-Thread malloc failed!\n\0".as_ptr()); + } + } +} + +/// 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/printf_demo.rs b/machines/qemu-virt-riscv64/rust/src/examples/printf_demo.rs new file mode 100644 index 00000000..3a1a0bbb --- /dev/null +++ b/machines/qemu-virt-riscv64/rust/src/examples/printf_demo.rs @@ -0,0 +1,58 @@ +/* + * 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() { + unsafe { + // 基本输出 + libc::printf(b"=== Printf Demo ===\n\0".as_ptr()); + + // 整数格式化 + let num: i32 = 42; + libc::printf(b"Integer: %d\n\0".as_ptr(), num); + libc::printf(b"Hex: 0x%x\n\0".as_ptr(), num); + libc::printf(b"Octal: %o\n\0".as_ptr(), num); + + // 浮点数(注意:需要软浮点支持) + // libc::printf(b"Float: %f\n\0".as_ptr(), 3.14159); + + // 字符和字符串 + let ch = b'R'; + libc::printf(b"Character: %c\n\0".as_ptr(), ch as i32); + libc::printf(b"String: %s\n\0".as_ptr(), b"RT-Thread\0".as_ptr()); + + // 指针 + let ptr = &num as *const i32; + libc::printf(b"Pointer: %p\n\0".as_ptr(), ptr); + } +} + +/// Using sprintf for string formatting +#[no_mangle] +pub extern "C" fn rust_sprintf_demo() -> i32 { + let mut buffer: [u8; 128] = [0; 128]; + let result = unsafe { + libc::sprintf( + buffer.as_mut_ptr() as *mut c_char, + b"Formatted: num=%d, str=%s\0".as_ptr() as *const c_char, + 100, + b"test\0".as_ptr(), + ) + }; + + unsafe { + libc::printf(b"Buffer content: %s\n\0".as_ptr(), buffer.as_ptr()); + } + + result +} 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..70bbe6ad --- /dev/null +++ b/machines/qemu-virt-riscv64/rust/src/examples/string_demo.rs @@ -0,0 +1,96 @@ +/* + * 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); + libc::printf(b"Copied string: %s\n\0".as_ptr(), dest.as_ptr()); + } +} + +/// 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); + libc::printf(b"Concatenated string: %s\n\0".as_ptr(), buffer.as_ptr()); + } +} + +/// 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() { + unsafe { + libc::printf(b"\n=== String Operations Demo ===\n\0".as_ptr()); + + // 长度计算 + let test_str = b"RT-Thread\0"; + let len = libc::strlen(test_str.as_ptr() as *const c_char); + libc::printf(b"Length of '%s': %zu\n\0".as_ptr(), test_str.as_ptr(), 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); + libc::printf(b"strcmp('%s', '%s') = %d\n\0".as_ptr(), + str1.as_ptr(), str2.as_ptr(), cmp); + + // 字符查找 + let ch = b'd' as i32; + let found = libc::strchr(str2.as_ptr() as *const c_char, ch); + if !found.is_null() { + libc::printf(b"Found '%c' in '%s'\n\0".as_ptr(), ch, str2.as_ptr()); + } + } +} 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..50a3b289 --- /dev/null +++ b/machines/qemu-virt-riscv64/rust/src/examples/thread_demo.rs @@ -0,0 +1,142 @@ +/* + * 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 thread creation, synchronization and other operations + +/// Thread entry function +extern "C" fn thread_entry(param: *mut c_void) { + let id = param as usize; + unsafe { + libc::printf(b"[Thread %d] Started\n\0".as_ptr(), id); + + // 执行一些任务 + for i in 0..3 { + libc::printf(b"[Thread %d] Working... step %d\n\0".as_ptr(), id, i + 1); + librt::rt_thread_mdelay(100); // 休眠100ms + } + + libc::printf(b"[Thread %d] Finished\n\0".as_ptr(), id); + } +} + +/// Thread creation example +#[no_mangle] +pub extern "C" fn rust_thread_create_demo() { + unsafe { + libc::printf(b"\n=== RT-Thread Create Demo ===\n\0".as_ptr()); + + // 创建线程 + let thread = librt::rt_thread_create( + b"rust_thread\0".as_ptr() as *const libc::c_char, + thread_entry, + 1 as *mut c_void, // Pass thread ID as parameter + 2048, // Stack size + 20, // Priority + 10, // Time slice + ); + + if thread.is_null() { + libc::printf(b"Failed to create thread!\n\0".as_ptr()); + return; + } + + libc::printf(b"Thread created successfully\n\0".as_ptr()); + + // 启动线程 + let ret = librt::rt_thread_startup(thread); + if ret == librt::RT_EOK { + libc::printf(b"Thread started successfully\n\0".as_ptr()); + } else { + libc::printf(b"Failed to start thread: %d\n\0".as_ptr(), ret); + } + + // 主线程等待一段时间 + librt::rt_thread_mdelay(500); + } +} + +/// Current thread information example +#[no_mangle] +pub extern "C" fn rust_thread_self_demo() { + unsafe { + libc::printf(b"\n=== Current Thread Info ===\n\0".as_ptr()); + + let current = librt::rt_thread_self(); + if !current.is_null() { + libc::printf(b"Current thread handle: %p\n\0".as_ptr(), current); + + // 获取当前tick + let tick = librt::rt_tick_get(); + libc::printf(b"Current system tick: %u\n\0".as_ptr(), tick); + + // 线程让出CPU + libc::printf(b"Yielding CPU...\n\0".as_ptr()); + librt::rt_thread_yield(); + libc::printf(b"Resumed after yield\n\0".as_ptr()); + } + } +} + +/// Thread sleep example +#[no_mangle] +pub extern "C" fn rust_thread_sleep_demo() { + unsafe { + libc::printf(b"\n=== Thread Sleep Demo ===\n\0".as_ptr()); + + libc::printf(b"Sleeping for 1 second...\n\0".as_ptr()); + librt::rt_thread_mdelay(1000); + libc::printf(b"Woke up after 1 second\n\0".as_ptr()); + + // 使用tick方式休眠 + let ticks = librt::rt_tick_from_millisecond(500); + libc::printf(b"Sleeping for %u ticks (500ms)...\n\0".as_ptr(), ticks); + librt::rt_thread_delay(ticks); + libc::printf(b"Woke up after 500ms\n\0".as_ptr()); + } +} + +/// Using Rust-wrapped thread API +#[no_mangle] +pub extern "C" fn rust_thread_wrapper_demo() { + unsafe { + libc::printf(b"\n=== Thread Wrapper Demo ===\n\0".as_ptr()); + + if let Some(thread) = librt::Thread::create( + b"rust_wrap\0", + thread_entry, + 2 as *mut c_void, + 2048, + 20, + 10, + ) { + libc::printf(b"Thread created using Rust wrapper\n\0".as_ptr()); + + if thread.startup().is_ok() { + libc::printf(b"Thread started successfully\n\0".as_ptr()); + } else { + libc::printf(b"Failed to start thread\n\0".as_ptr()); + } + + // 等待线程完成 + librt::thread_sleep_ms(500); + } else { + libc::printf(b"Failed to create thread using wrapper\n\0".as_ptr()); + } + } +} + +/// 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(); +} 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..cf187bb3 --- /dev/null +++ b/machines/qemu-virt-riscv64/rust/src/examples/vec_demo.rs @@ -0,0 +1,104 @@ +/* + * 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 + */ +/// A minimal vector backed by C malloc/realloc/free and memcpy. +/// Only supports `T: Copy` and no drop semantics for elements. +struct CVec { + ptr: *mut T, + len: usize, + cap: usize, +} + +impl CVec { + fn new() -> Self { + CVec { ptr: core::ptr::null_mut(), len: 0, cap: 0 } + } + + fn with_capacity(cap: usize) -> Self { + if cap == 0 { + return Self::new(); + } + let bytes = cap.saturating_mul(mem::size_of::()); + let raw = unsafe { libc::malloc(bytes) } as *mut T; + if raw.is_null() { + CVec { ptr: core::ptr::null_mut(), len: 0, cap: 0 } + } else { + CVec { ptr: raw, len: 0, cap } + } + } + + fn len(&self) -> usize { self.len } + fn capacity(&self) -> usize { self.cap } + fn as_ptr(&self) -> *const T { self.ptr as *const T } + fn as_mut_ptr(&mut self) -> *mut T { self.ptr } + + fn grow(&mut self) -> bool { + let new_cap = if self.cap == 0 { 4 } else { self.cap.saturating_mul(2) }; + let new_bytes = new_cap.saturating_mul(mem::size_of::()); + + let new_ptr = if self.ptr.is_null() { + unsafe { libc::malloc(new_bytes) as *mut T } + } else { + unsafe { libc::realloc(self.ptr as *mut c_void, new_bytes) as *mut T } + }; + + if new_ptr.is_null() { + return false; + } + self.ptr = new_ptr; + self.cap = new_cap; + true + } + + fn push(&mut self, value: T) -> bool { + if self.len == self.cap { + if !self.grow() { return false; } + } + unsafe { ptr::write(self.ptr.add(self.len), value); } + self.len += 1; + true + } + + fn pop(&mut self) -> Option { + if self.len == 0 { return None; } + self.len -= 1; + let value = unsafe { ptr::read(self.ptr.add(self.len)) }; + Some(value) + } + + fn get(&self, index: usize) -> Option { + if index >= self.len { return None; } + Some(unsafe { ptr::read(self.ptr.add(index)) }) + } +} + +impl Drop for CVec { + fn drop(&mut self) { + if !self.ptr.is_null() { + unsafe { libc::free(self.ptr as *mut c_void); } + } + self.ptr = core::ptr::null_mut(); + self.len = 0; + self.cap = 0; + } +} +#[no_mangle] +pub extern "C" fn rust_vec_demo() { + unsafe { libc::printf(b"\n=== CVec Demo ===\n\0".as_ptr()); } + let mut v: CVec = CVec::with_capacity(2); + for i in 1..=5u32 { + let ok = v.push(i); + unsafe { libc::printf(b"push %u -> %s\n\0".as_ptr(), i, if ok { b"ok\0".as_ptr() } else { b"fail\0".as_ptr() }); } + } + unsafe { libc::printf(b"len=%zu cap=%zu\n\0".as_ptr(), v.len(), v.capacity()); } + for i in 0..v.len() { + let val = v.get(i).unwrap_or(0); + unsafe { libc::printf(b"v[%zu]=%u\n\0".as_ptr(), i, val); } + } +} 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..e3cea820 --- /dev/null +++ b/machines/qemu-virt-riscv64/rust/src/init.rs @@ -0,0 +1,25 @@ +/* + * 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::libc; + +/// Component initialization function +/// This function is called during RT-Thread system initialization +#[no_mangle] +pub extern "C" fn rust_init() -> i32 { + unsafe { + libc::printf(b"Rust component initialized\n\0".as_ptr()); + } + 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..128592d2 --- /dev/null +++ b/machines/qemu-virt-riscv64/rust/src/lib.rs @@ -0,0 +1,107 @@ +/* + * 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)] + +use core::panic::PanicInfo; +use core::ffi::{c_void, c_char}; + +// Core modules +pub mod libc; +pub mod librt; +pub mod libdl; +pub mod init; + +// 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::libc; + use crate::librt; + 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::libc; + use crate::librt; + use core::ffi::{c_char, c_void}; + include!("examples/printf_demo.rs"); +} +#[cfg(feature = "example_printf")] +pub use example_printf::*; + +#[cfg(feature = "example_string")] +mod example_string { + use crate::libc; + use crate::librt; + use core::ffi::{c_char, c_void}; + include!("examples/string_demo.rs"); +} +#[cfg(feature = "example_string")] +pub use example_string::*; + +#[cfg(feature = "example_memory")] +mod example_memory { + use crate::libc; + use crate::librt; + use core::ffi::{c_char, c_void}; + include!("examples/memory_demo.rs"); +} +#[cfg(feature = "example_memory")] +pub use example_memory::*; + +#[cfg(feature = "example_thread")] +mod example_thread { + use crate::libc; + use crate::librt; + use core::ffi::{c_char, c_void}; + include!("examples/thread_demo.rs"); +} +#[cfg(feature = "example_thread")] +pub use example_thread::*; + +#[cfg(feature = "example_vec")] +mod example_vec { + use crate::libc; + use crate::librt; + use core::{mem, ptr}; + use core::ffi::{c_char, c_void}; + include!("examples/vec_demo.rs"); +} +#[cfg(feature = "example_vec")] +pub use example_vec::*; + +#[cfg(feature = "example_dl")] +mod example_dl { + use crate::libc; + use crate::libdl; + use core::ffi::{c_char, c_void, c_int}; + include!("examples/dlmodule_demo.rs"); +} +#[cfg(feature = "example_dl")] +pub use example_dl::*; + +// Re-export initialization function +pub use init::rust_init; + +// Panic handler +#[panic_handler] +fn panic(_info: &PanicInfo) -> ! { + unsafe { + libc::printf(b"Rust panic occurred!\n\0".as_ptr()); + } + loop {} +} diff --git a/machines/qemu-virt-riscv64/rust/src/libc.rs b/machines/qemu-virt-riscv64/rust/src/libc.rs new file mode 100644 index 00000000..bb6350f2 --- /dev/null +++ b/machines/qemu-virt-riscv64/rust/src/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/libdl.rs b/machines/qemu-virt-riscv64/rust/src/libdl.rs new file mode 100644 index 00000000..a3734c00 --- /dev/null +++ b/machines/qemu-virt-riscv64/rust/src/libdl.rs @@ -0,0 +1,139 @@ +/* + * 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 + +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; +} + +/// 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/librt.rs b/machines/qemu-virt-riscv64/rust/src/librt.rs new file mode 100644 index 00000000..109ef226 --- /dev/null +++ b/machines/qemu-virt-riscv64/rust/src/librt.rs @@ -0,0 +1,202 @@ +/* + * 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; + +// 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; +} + +// ============== 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; +} + +// ============== 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/tools/build_support.py b/machines/qemu-virt-riscv64/rust/tools/build_support.py new file mode 100644 index 00000000..87f1a5ab --- /dev/null +++ b/machines/qemu-virt-riscv64/rust/tools/build_support.py @@ -0,0 +1,228 @@ +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_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)