diff --git a/.cargo/config.toml b/.cargo/config.toml index c0f9ac118d4717888a3399818f1012311d023e2e..553cec20f95a6b998e2fa1c8a04c43b9a55cf79e 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,25 +1,64 @@ -[target.x86_64-pc-windows-msvc] +# Mircrosoft Visual Studio 目标配置(ARM平台不需要) +[target.x86_64-pc-windows-msvc.arch] rustflags = [ + # +crt_static Indicates a static connection to the C runtime library + # -crt_static Indicates dynamic connection to the C runtime library "-C", - "target-feature=-crt-static", # "-C",'link_arg=/ENTRY:main', + "target-feature=-crt-static", + + # Program entry point function is main + # "-C",'link_arg=/ENTRY:main', + + # On MSVC platform, printf is implemented as macro by default, + # Therefore, this library is needed to import the printf function "-C", "link_arg=legacy_stdio_definitions.lib", + + # VC runtime library (dynamic connection) + # Reference: https://docs.microsoft.com/en-us/cpp/c-runtime-library/crt-library-features?view=msvc-170 "-C", "link_arg=vcruntime.lib", "-C", "link_arg=ucrt.lib", "-C", - "link_arg=msvcrt.lib", # "-C","link_arg=libvcruntime.lib" # "-C","link_arg=libucrt.lib", # "-C","link_arg=libcmt.lib", + "link_arg=msvcrt.lib", + # VC runtime library (static connection) + # "-C","link_arg=libvcruntime.lib" + # "-C","link_arg=libucrt.lib", + # "-C","link_arg=libcmt.lib", + + # Designated as win10 system. Some functions of the system below win10 cannot be used. "--cfg", "WIN10", ] + +[nightly-x86_64-unknown-linux-gnu] +rustflags = [ + # Designated as win10 system. Some functions of the system below win10 cannot be used. + "--cfg", + "WIN10", +] + +[x86_64-unknown-linux-gnu] +rustflags = [ + # Designated as win10 system. Some functions of the system below win10 cannot be used. + "--cfg", + "WIN10", +] + + [target.thumbv7m-none-eabi] rustflags = [ + # Set the target system as Huawei liteos_ m "--cfg", "liteos", "--cfg", - 'target_os="liteos"', # "--crate-type", # 'staticlib', + 'target_os="liteos_m"', + # Specify LiteOS location + "--cfg", + 'LOS_PATH="../../stm32/LiteOS"', ] -# [build] -# # Always compile for the instruction set of the STM32F1 -# target = "thumbv7m-none-eabi" + +[build] +# Always compile for the instruction set of the STM32F1 +target = "thumbv7m-none-eabi" diff --git a/.gitignore b/.gitignore index 4fffb2f89cbd8f2169ce9914bd16bd43785bb368..502d4f0ed10e4580a3977f8f1c079f7c6f7ecb5c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /target /Cargo.lock +src/liteos/config.rs diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000000000000000000000000000000000000..513cbeb9d40d194855d83ea90af0123f31f55eba --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "rust-analyzer.checkOnSave.allTargets": false +} \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index caae4ffc49d71bd27bda6b4e1b8ace4d6a510b60..7d7c1dfc2548415c457fae8a8cb178a3cbf896ef 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,9 +7,9 @@ version = "0.1.0" crate-type = ["rlib"] [dependencies] -cfg-if = "1.0.0" -utils = { path = "../utils" } +utils = { git = "https://gitee.com/iot-ua/utils.git", branch = "develop" } +cfg-if = "1.0.0" [target.'cfg(unix)'.dependencies] libc = { version = "0.2.126", default-features = false } @@ -27,4 +27,24 @@ windows-sys = { version = "0.36.1", features = [ "Win32_System_SystemServices", ] } -[target.'cfg(liteos)'.dependencies] +[build-dependencies] +regex = "1.5.6" + + +[[example]] +name = "app" +crate-type = ["staticlib"] + +[[example]] +name = "led" +crate-type = ["staticlib"] + +[profile.release] +opt-level = 3 +debug = true +debug-assertions = false +overflow-checks = false +lto = true +incremental = false +codegen-units = 1 +rpath = false diff --git a/README.md b/README.md index 9a9225013125d998d18f860568395c98112d0921..ef390dba14a3ac304b887bb5bfa58f08e90a3cd4 100644 --- a/README.md +++ b/README.md @@ -3,3 +3,11 @@ #### 介绍 操作系统级抽象,用来提供底层功能支持,例如线程,互斥锁,内存分配器等。 +#### [Arch 源代码目录说明](./doc/code_info.md) + +文档主要对代码目录组织结构进行简要说明。 + +#### [编译介绍和开发工具](./doc/build_and_tools.md) + +文档主要介绍嵌入式平台LiteOS_M下编译环境搭建,及配置方法。对于Windows,Linux等大型系统,直接使用Rust nightly常规编译即可。 + diff --git a/build.rs b/build.rs new file mode 100644 index 0000000000000000000000000000000000000000..7430808cecd4fe1d31ba60c1d13726de1d8f5d9f --- /dev/null +++ b/build.rs @@ -0,0 +1,63 @@ +use regex::Regex; +use std::{ + env, + fs::File, + io::{BufRead, BufReader, Write}, + path::PathBuf, + str::FromStr, +}; +fn main() { + let target_os = env::var("CARGO_CFG_LITEOS").is_ok(); + // Decode liteos configuration information and automatically generate configuration files. + if target_os { + let pwd = env::var("PWD").unwrap(); + let pwd = PathBuf::from_str(&pwd).unwrap(); + let manifest_path = env::var("CARGO_MANIFEST_DIR").unwrap(); + let manifest_path = PathBuf::from_str(&manifest_path).unwrap(); + + let los_path = env::var("CARGO_CFG_LOS_PATH").unwrap(); + let los_path = pwd.join(&los_path); + + let fs = File::options() + .read(true) + .open(los_path.join(".config")) + .unwrap(); + let fs = BufReader::new(fs); + println!("{}",manifest_path.join("src/liteos/config.rs").to_str().unwrap()); + let mut fcfg = File::options() + .write(true) + .create(true) + .truncate(true) + .open(manifest_path.join("src/liteos/config.rs")) + .unwrap(); + let is_empty_line = Regex::new(r"^ *?#|^ *?$").unwrap(); + let kv = Regex::new(r#"^ *?(\w+) *?= *?(.*)$"#).unwrap(); + + unsafe { + write!(&mut fcfg,"//The following content is automatically generated by the compiler.\n//please do not modify it.\n").unwrap_unchecked() + }; + + for line in fs.lines() { + if let Ok(s) = line { + if is_empty_line.is_match(&s) { + continue; + } + let mc = kv.captures(&s).unwrap(); + let key = mc.get(1).unwrap().as_str(); + let value = mc.get(2).unwrap().as_str(); + if value.parse::().is_ok() { + write!(&mut fcfg, "pub const {}: usize = {}usize;\n", key, value).unwrap(); + } else if value == "y" { + println!("cargo:rustc-cfg={}", key); + } else if value.len() > 0 { + println!("cargo:rustc_cfg='{}={}'", key, value); + } + } + } + println!( + "cargo:rerun-if-changed={}", + los_path.join(".config").to_str().unwrap() + ); + } + println!("cargo:rerun-if-changed=build.rs"); +} diff --git a/doc/build_and_tools.md b/doc/build_and_tools.md new file mode 100644 index 0000000000000000000000000000000000000000..69c1d12dec23d5eaf767211a1d335256f0c26896 --- /dev/null +++ b/doc/build_and_tools.md @@ -0,0 +1,186 @@ +### 目录 +- [配置&编译框架简介](#1) +- [配置文件说明](#2) +- [将应用添加到LiteOS系统](#3) +- [LiteOS 配置并运行Example](#3) + +

编译框架简介

+ +对于嵌入式平台构建应用的方式有两种方案:第一种方案是由Rust来编译构建应用,并生成可下载到硬件平台的二进行文件。第二种方案是Rust负责将应用编译为静态库,再由嵌入式系统连接并构建为可下载到硬件平台的二进制文件。这里我们选择的是第二种方案。这种方案可以更好的将系统移植与应用开发解耦。使应用开发人员只需要使用标准的Rust构建工具[cargo],加极少量的配置,就可以生成应用。 + +

配置文件说明

+ +平台抽象层使用标准的Rust构建工具进行配置。配置文件主要由 [.cargo](../.cargo) 文件夹下的[config](../.cargo/config.toml)文件,根文件夹下的[Cargo.toml](../Cargo.toml),[build.rs](../build.rs)的设置来完成. + +下面以 [野火_STM32F103指南者](https://embedfire.com/products/) 为例,详细说明一下配置方法. + +- [.cargo/config.toml](../.cargo/config.toml) 详细内容如下: + +``` +# 嵌入式ARM Cortex-M3 目标设置 +[target.thumbv7m-none-eabi] +rustflags = [ + "--cfg", # 配置目标系统架构为LiteOS + "liteos", + + "--cfg", # 配置目标系统详细架构为Liteos-M + 'target_os="liteos_m"', + + "--cfg", # 指定LiteOS系统Top文件夹位置 + 'LOS_PATH="../../stm32/LiteOS"', +] + +[build] +# 指定默认编译目标为 thumbv7m-none-eabi +target = "thumbv7m-none-eabi" + +``` + +对于嵌入式交叉编译来说,Cargo config 主要指定系统的类别及 Top 文件夹位置。 + +- [Cargo.toml](../Cargo.toml) 详细说内容如下: + +``` +[package] +edition = "2021" +name = "arch" +version = "0.1.0" + +[lib] +crate-type = ["rlib"] + +[dependencies] +cfg-if = "1.0.0" +utils = { git = "https://gitee.com/iot-ua/utils.git" } + +# 构建时-依赖正规则库 +[build-dependencies] +regex = "1.5.6" + + +[[example]] # 示例 app 配置 +name="app" +crate-type=["staticlib"] # 将示例生成为静态库 + +[[example]] +name = "led" # 示例 led 配置 +crate-type = ["staticlib"] # 将示例生成为静态库 + +[profile.release] # 编译信息 +opt-level = 2 # 优化层,在嵌入式系统上不启用优化将导致大量的栈空间使用,所以这里优化为2 +debug = true # 输出Debug 信息 +debug-assertions = false # 是否允许debug-assert 功能。这里不启用 +overflow-checks = false # 数据计算溢出检查是否启用,如果启用,出现溢出时将导致异常 +lto = true # 连接时优化 +incremental = false # 是否增量编译 +codegen-units = 1 # 代码生成并行库,值越少,越有利于优化,但编译速度越慢 +rpath = false # 调试信息使用相对路径,这里选择false. +``` +Cargo.toml 需要特别注意的是生成目标需要调整为静态库,以及优化级别的选择,优化级别为零时,将导致全部函数调用变更为非内联(inline),因此会导致很深的函数调用.也就意味着需要很大的运行时堆栈空间.因此, 不建议使用零级别优化. + +"debug = true" 会输出调试信息,如果需要使用 jtag 或 swd 接口进行调试的话,必须设置为 true. + + +- [build.rs](../build.rs) 详细说内容如下: + +``` +fn main() { + let target_os = env::var("CARGO_CFG_LITEOS").is_ok(); + //解码LiteOS 配置信息,并自动生成配置文件。 + if target_os { + //取得 LiteOS Top 文件夹 + let los_path = env::var("CARGO_CFG_LOS_PATH").unwrap(); + let los_path = PathBuf::from_str(&los_path).unwrap(); + + //打开 LiteOS 配置文件 + let fs = File::options() + .read(true) + .open(los_path.join(".config")) + .unwrap(); + let fs = BufReader::new(fs); + let mut fcfg = File::options() + .write(true) + .create(true) + .truncate(true) + .open("./src/liteos/libc/config.rs") + .unwrap(); + unsafe { + write!(&mut fcfg,"//The following content is automatically generated by the compiler.\n//please do not modify it.\n").unwrap_unchecked() + }; + + let is_empty_line = Regex::new(r"^ *?#|^ *?$").unwrap(); + let kv = Regex::new(r#"^ *?(\w+) *?= *?(.*)$"#).unwrap(); + + // 分析配置文件内容,并生成Rust常量配置文件及feature配置信息。 + for line in fs.lines() { + if let Ok(s) = line { + if is_empty_line.is_match(&s) { + continue; + } + let mc = kv.captures(&s).unwrap(); + let key = mc.get(1).unwrap().as_str(); + let value = mc.get(2).unwrap().as_str(); + if value.parse::().is_ok() { + write!(&mut fcfg, "pub const {}: usize = {}usize;\n", key, value).unwrap(); + } else if value == "y" { + println!("cargo:rustc-cfg={}", key); + } else if value.len() > 0 { + println!("cargo:rustc_cfg='{}={}'", key, value); + } + } + } + // 当 LiteOS 配置文件发生变化时重新构建。 + println!( + "cargo:rerun-if-changed={}", + los_path.join(".config").to_str().unwrap() + ); + } + // 当 build.rs 文件发生变化时重新构建 + println!("cargo:rerun-if-changed=build.rs"); +} + +``` + +build.rs 构建脚本主要用于分析 LiteOS 配置信息,并生成 Rust 可使用的配置信息。这个脚本输出的内容在两个地方 ,一个是在 [src/liteos/libc/config.rs](../src/liteos/libc/config.rs),另一个在[target/thumbv7m-none-eabi/release/build/arch.../output](../target/thumbv7m-none-eabi/release/build/arch-b3f29851170e2ade/output). + +可通过查看这两个输出文件来分析编译器是否正常运行。 + +

将App添加到LiteOS系统

+ +- 修改 [LiteOS/build/mk/los_config.mk](https://gitee.com/LiteOS/LiteOS/blob/master/build/mk/los_config.mk)以下位置: + +``` +40a41,42 +> ## 将应用添加到静态库列表 ## +> LITEOS_BASELIB :=-lled +125c127,129 +< WARNING_AS_ERROR := -Wall -Werror +--- +> WARNING_AS_ERROR := -Wall -Werror +> ## 新版 GCC 编译器会将数据下标访问警告转变为错误,所以这里需要修改检查等级 +> WARNING_AS_ERROR += -Werror=array-parameter=0 +302a307,308 +> ## 将输出路径添加到 C 静态库搜索路径 ## +> LITEOS_LD_PATH += -L"/home/shuaihu/project/rust/opcua/arch/target/thumbv7m-none-eabi/release/examples" +``` +这里主要目标是将 Rust 编译的静态库连接到 LiteOS 应用。 + +- 注释掉 [LiteOS/targets/STM32F103_FIRE_Arbitrary/Src/user_task.c](https://gitee.com/LiteOS/LiteOS/blob/master/targets/STM32F103_FIRE_Arbitrary/Src/user_task.c) 全部代码,并添加以下内容。 + +``` +void led_toggle(void) +{ + HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_5); +} +``` +这里主要是实现 [野火_STM32F103指南者](https://embedfire.com/products/) 开发板的 LED 指示灯闪烁。 + +

LiteOS 配置并运行Example

+ +首先参考以下内容,将LiteOS系统编译为bin文件. + +- [LiteOS-搭建Linux编译环境](https://gitee.com/LiteOS/LiteOS/blob/master/doc/LiteOS_Build_and_IDE.md#4-1) 搭建 LiteOS 编译开发环境. + +- [Linux下编译流程](https://gitee.com/LiteOS/LiteOS/blob/master/doc/LiteOS_Build_and_IDE.md#linux%E4%B8%8B%E7%BC%96%E8%AF%91%E6%B5%81%E7%A8%8B) 配置并编译LiteOS. + +然后通过 JLink 或 ST-Link 将编译好的 bin 文件下载到开发板.即可以看到开发板的 LED 指示灯以 1 秒为间隔闪烁. 至此,所有工作完成. 可以开始快乐的用 Rust 构建你的嵌入式应用了. \ No newline at end of file diff --git a/doc/code_info.md b/doc/code_info.md new file mode 100644 index 0000000000000000000000000000000000000000..87e7a82873c1e53bca6025faf7c8abdaaa2e611f --- /dev/null +++ b/doc/code_info.md @@ -0,0 +1,18 @@ +### 平台架构层目录结构说明 + +关于代码树中各个目录存放的源代码的相关内容简介如下: + +| 一级目录 | 二级目录 | 三级目录 | 说明 | +| ---------- | ----------------------- | --------------------- | -------------------------- | +| .cargo | | | Cargo 编译配置文件 | +|doc | | | 文档 | +|src | | | 源码 | +| |common | |平台无关部分代码| +| |liteos | |华为LiteOS_M 相关实现| +| | |libc |编译时生成的 Lite_OS 配置信息| +| |unix | |类Unix系统相关实现| +| | |locks |Mutex,parker,rwlock...| +| | |malloc |内存分配单元测试| +| | |os |系统功能单元测试| +| | |thread |线程功能单元测试| +| | |utils |timespec等辅助代码| \ No newline at end of file diff --git a/examples/app.rs b/examples/app.rs new file mode 100644 index 0000000000000000000000000000000000000000..695e21028e2e492c3ba862325e4a72eed23a2856 --- /dev/null +++ b/examples/app.rs @@ -0,0 +1,26 @@ +#![no_std] +#![feature(default_alloc_error_handler)] +extern crate alloc; +use alloc::boxed::Box; +use arch::{ + os::printf, + thread::{self, Thread}, +}; +#[no_mangle] +extern "C" fn app_init() { + let _ = unsafe { + Thread::new( + 800, + Box::new(|| loop { + printf("test\n\0".as_ptr().cast()); + thread::Thread::sleep(1000); + }), + ) + }; +} + +#[cfg(not(test))] +#[panic_handler] +pub fn painc_handler(_info: &core::panic::PanicInfo<'_>) -> ! { + loop {} +} diff --git a/examples/led.rs b/examples/led.rs new file mode 100644 index 0000000000000000000000000000000000000000..43af96f1eaa013872535d68fea97d1c79143b5ba --- /dev/null +++ b/examples/led.rs @@ -0,0 +1,29 @@ +#![no_std] +#![feature(default_alloc_error_handler)] +extern crate alloc; +use alloc::boxed::Box; +use arch::{ + thread::{self, Thread}, +}; + +extern "C" { + fn led_toggle(); +} +#[no_mangle] +extern "C" fn app_init() { + let _ = unsafe { + Thread::new( + 800, + Box::new(|| loop { + led_toggle(); + thread::Thread::sleep(1000); + }), + ) + }; +} + +#[cfg(not(test))] +#[panic_handler] +pub fn painc_handler(_info: &core::panic::PanicInfo<'_>) -> ! { + loop {} +} diff --git a/src/common/malloc.rs b/src/common/malloc.rs index 5bddd1e43fb5e5a987cd7fec74813050b69afafd..acdcd7bdac07b130611ebc9e51e7807e1b74502a 100644 --- a/src/common/malloc.rs +++ b/src/common/malloc.rs @@ -36,28 +36,24 @@ pub const MIN_ALIGN: usize = 16; )))] pub const MIN_ALIGN: usize = 4; -cfg_if::cfg_if! { - if #[cfg(not(liteos))]{ - use core::{ - alloc::{GlobalAlloc, Layout}, - cmp, ptr, - }; - pub unsafe fn realloc_fallback( - alloc: &System, - ptr: *mut u8, - old_layout: Layout, - new_size: usize, - ) -> *mut u8 { - // Docs for GlobalAlloc::realloc require this to be valid: - let new_layout = Layout::from_size_align_unchecked(new_size, old_layout.align()); +use core::{ + alloc::{GlobalAlloc, Layout}, + cmp, ptr, +}; +pub unsafe fn realloc_fallback( + alloc: &System, + ptr: *mut u8, + old_layout: Layout, + new_size: usize, +) -> *mut u8 { + // Docs for GlobalAlloc::realloc require this to be valid: + let new_layout = Layout::from_size_align_unchecked(new_size, old_layout.align()); - let new_ptr = GlobalAlloc::alloc(alloc, new_layout); - if !new_ptr.is_null() { - let size = cmp::min(old_layout.size(), new_size); - ptr::copy_nonoverlapping(ptr, new_ptr, size); - GlobalAlloc::dealloc(alloc, ptr, old_layout); - } - new_ptr - } + let new_ptr = GlobalAlloc::alloc(alloc, new_layout); + if !new_ptr.is_null() { + let size = cmp::min(old_layout.size(), new_size); + ptr::copy_nonoverlapping(ptr, new_ptr, size); + GlobalAlloc::dealloc(alloc, ptr, old_layout); } + new_ptr } diff --git a/src/lib.rs b/src/lib.rs index 75f5d5c065a6e90efc17a11a36deb96d3e4f1c15..1a9b19f05604d37a7242af1451e38d3525cd5681 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -24,7 +24,8 @@ cfg_if! { mod windows; }else if #[cfg(liteos)]{ mod liteos; - use liteos::os; + pub use liteos::os; pub use liteos::thread; } } + diff --git a/src/liteos/libc.rs b/src/liteos/libc.rs new file mode 100644 index 0000000000000000000000000000000000000000..935ba129c462665a8e184299557f3b6790f5fdbc --- /dev/null +++ b/src/liteos/libc.rs @@ -0,0 +1,142 @@ +#[path = "./config.rs"] +mod config; +pub type size_t = usize; +pub mod heap { + #[repr(C)] + pub struct LOS_MEM_POOL_STATUS { + uwTotalUsedSize: u32, + uwTotalFreeSize: u32, + uwMaxFreeNodeSize: u32, + uwUsedNodeNum: u32, + uwFreeNodeNum: u32, + #[cfg(LOSCFG_MEM_TASK_STAT)] + uwUsageWaterLine: u32, + } + extern "C" { + // 初始化和删除内存池 + // LOS_MemInit 初始化一块指定的动态内存池,大小为size + pub fn LOS_MemInit(poll: *mut u8, size: u32) -> u32; + // LOS_MemDeInit 删除指定内存池,仅打开LOSCFG_MEM_MUL_POOL时有效 + pub fn LOS_MemDeInit(poll: *mut u8) -> u32; + + // 申请、释放动态内存 + // LOS_MemAlloc 从指定动态内存池中申请size长度的内存 + pub fn LOS_MemAlloc(pool: *mut u8, size: u32) -> *mut u8; + // LOS_MemFree 释放已申请的内存 + pub fn LOS_MemFree(poll: *mut u8, ptr: *mut u8) -> u32; + // LOS_MemRealloc 按size大小重新分配内存块,并将原内存块内容拷贝到新内存块。如果新内存块申请成功,则释放原内存块 + pub fn LOS_MemRealloc(pool: *mut u8, ptr: *mut u8, size: u32) -> *mut u8; + // LOS_MemAllocAlign 从指定动态内存池中申请长度为size且地址按boundary字节对齐的内存 + pub fn LOS_MemAllocAlign(pool: *mut u8, size: u32, boundary: u32) -> *mut u8; + + // 获取内存池信息 + // LOS_MemPoolSizeGet 获取指定动态内存池的总大小 + pub fn LOS_MemPoolSizeGet(pool: *const u8) -> u32; + // LOS_MemTotalUsedGet 获取指定动态内存池的总使用量大小 + pub fn LOS_MemTotalUsedGet(poll: *mut u8) -> u32; + // LOS_MemInfoGet 获取指定内存池的内存结构信息,包括空闲内存大小、已使用内存大小、空闲内存块数量、已使用的内存块数量、最大的空闲内存块大小 + pub fn LOS_MemInfoGet(pool: *mut u8, poolStatus: *mut LOS_MEM_POOL_STATUS) -> u32; + // LOS_MemPoolList 打印系统中已初始化的所有内存池,包括内存池的起始地址、内存池大小、空闲内存总大小、已使用内存总大小、最大的空闲内存块大小、空闲内存块数量、已使用的内存块数量。仅打开LOSCFG_MEM_MUL_POOL时有效 + pub fn LOS_MemPoolList() -> u32; + + // 获取内存块信息 + // LOS_MemFreeBlksGet 获取指定内存池的空闲内存块数量 + pub fn LOS_MemFreeBlksGet(pool: *mut u8) -> u32; + // LOS_MemUsedBlksGet 获取指定内存池已使用的内存块数量 + pub fn LOS_MemUsedBlksGet(pool: *mut u8) -> u32; + // LOS_MemTaskIdGet 获取申请了指定内存块的任务ID + pub fn LOS_MemTaskIdGet(ptr: *const u8) -> u32; + // LOS_MemLastUsedGet 获取内存池最后一个已使用内存块的结束地址 + pub fn LOS_MemLastUsedGet(pool: *mut u8) -> u32; + // LOS_MemNodeSizeCheck 获取指定内存块的总大小和可用大小,仅打开LOSCFG_BASE_MEM_NODE_SIZE_CHECK时有效 + pub fn LOS_MemNodeSizeCheck( + pool: *mut u8, + ptr: *mut u8, + totalSize: *mut u32, + availSize: *mut u32, + ) -> u32; + // LOS_MemFreeNodeShow 打印指定内存池的空闲内存块的大小及数量 + pub fn LOS_MemFreeNodeShow(pool: *mut u8) -> u32; + + // 检查指定内存池的完整性 + // LOS_MemIntegrityCheck 对指定内存池做完整性检查,仅打开LOSCFG_BASE_MEM_NODE_INTEGRITY_CHECK时有效 + pub fn LOS_MemIntegrityCheck(pool: *mut u8) -> u32; + + // 设置、获取内存检查级别,仅打开LOSCFG_BASE_MEM_NODE_SIZE_CHECK时有效 + // LOS_MemCheckLevelSet 设置内存检查级别 + pub fn LOS_MemCheckLevelSet(checkLevel: u8) -> u32; + // LOS_MemCheckLevelGet 获取内存检查级别 + pub fn LOS_MemCheckLevelGet() -> u8; + + // 为指定模块申请、释放动态内存,仅打开LOSCFG_MEM_MUL_MODULE时有效 + // LOS_MemMalloc 从指定动态内存池分配size长度的内存给指定模块,并纳入模块 + pub fn LOS_MemMalloc(pool: *mut u8, size: u32, moduleId: u32) -> *mut u8; + // LOS_MemMfree 释放已经申请的内存块,并纳入模块统计 + pub fn LOS_MemMfree(pool: *mut u8, ptr: *mut u8, moduleId: u32) -> u32; + // LOS_MemMallocAlign 从指定动态内存池中申请长度为size且地址按boundary字节对齐的内存给指定模块,并纳入模块统计 + pub fn LOS_MemMallocAlign( + pool: *mut u8, + size: u32, + boundary: u32, + moduleId: u32, + ) -> *mut u8; + // LOS_MemMrealloc 按size大小重新分配内存块给指定模块,并将原内存块内容拷贝到新内存块,同时纳入模块统计。如果新内存块申请成功,则释放原内存块 + pub fn LOS_MemMrealloc(pool: *mut u8, ptr: *mut u8, size: u32, moduleId: u32) -> *mut u8; + + // 获取指定模块的内存使用量 + // LOS_MemMusedGet 获取指定模块的内存使用量,仅打开LOSCFG_MEM_MUL_MODULE时有效 + pub fn LOS_MemMusedGet(mouduleId: u32) -> u32; + } +} +pub mod sys { + use super::size_t; + + extern "C" { + pub fn abort() -> !; + pub fn __errno_location() -> *const i32; + pub fn strerror_r(err: i32, buf: *mut u8, buflen: size_t) -> i32; + pub fn strnlen(cs: *const u8, maxlen: size_t) -> size_t; + } +} +pub mod pthread { + use core::ffi::c_void; + + use super::size_t; + + pub type pthread_t = isize; + pub const PR_SET_NAME: i32 = 15i32; + pub const MIN_STACK_SIZE: usize = super::config::LOSCFG_BASE_CORE_TSK_MIN_STACK_SIZE; + pub const DEFAULT_STACK_SIZE: usize = super::config::LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE; + #[repr(C)] + pub struct pthread_attr_t { + __size: [u32; 15], + #[cfg(LOSCFG_KERNEL_SMP)] + __size1: [usize; 128 / core::mem::size_of::()], + } + extern "C" { + pub fn pthread_attr_init(attr: *mut pthread_attr_t) -> i32; + pub fn pthread_attr_destroy(attr: *mut pthread_attr_t) -> i32; + pub fn pthread_attr_setstacksize(attr: *mut pthread_attr_t, stackSize: size_t) -> i32; + pub fn pthread_create( + thread: *mut pthread_t, + attr: *const pthread_attr_t, + f: extern "C" fn(*mut core::ffi::c_void) -> *mut core::ffi::c_void, + arg: *mut c_void, + ) -> i32; + + pub fn pthread_join(thread: pthread_t, retVal: *const *const c_void) -> i32; + pub fn pthread_detach(thread: pthread_t) -> i32; + pub fn sched_yield() -> i32; + pub fn prctl(op: i32, ...) -> i32; + } +} +pub mod timer { + extern "C" { + pub fn LOS_MS2Tick(millisec: u32) -> u32; + } +} +pub mod task { + extern "C" { + pub fn LOS_TaskDelay(tick: u32) -> u32; + } +} diff --git a/src/liteos/malloc.rs b/src/liteos/malloc.rs index 10a9cfea907b12e7b06118af6853763421e6ab4d..19c78cc8bbd640e37425be2c927c65e73da32055 100644 --- a/src/liteos/malloc.rs +++ b/src/liteos/malloc.rs @@ -1,37 +1,37 @@ +use super::libc::heap::*; +use crate::common::malloc::{realloc_fallback, System, MIN_ALIGN}; use core::alloc::{GlobalAlloc, Layout}; -use crate::{ - common::malloc::{System, MIN_ALIGN}, - os::{LOS_MemAllocAlign, LOS_MemFree, LOS_MemInit, LOS_MemRealloc, LOS_NOK}, -}; - -use core::ptr; -static POOL: [u8; 1024 * 64] = [0; 1024 * 64]; +extern "C" { + #[link_name = "__los_heap_addr_start__"] + static mut POOL: u8; +} unsafe impl GlobalAlloc for System { #[inline] unsafe fn alloc(&self, layout: Layout) -> *mut u8 { - if LOS_MemInit(POOL.as_ptr(), 1024 * 64) == LOS_NOK { - panic!("Memory initialization error.") + // Liteos requires that the length of memory allocation must be the 4-byte alignment + let size = layout.size() as u32; + let size = (size + (4 - 1)) & (-4i32 as u32); + let ptr = if layout.align() <= MIN_ALIGN { + LOS_MemAlloc(&mut POOL, size) + } else { + LOS_MemAllocAlign(&mut POOL, size, layout.align() as u32) }; - LOS_MemAllocAlign(POOL.as_ptr(), layout.size(), MIN_ALIGN) - } - - #[inline] - unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { - let ptr = self.alloc(layout); - if !ptr.is_null() { - ptr::write_bytes(ptr, 0, layout.size()); - } ptr } - #[inline] unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) { - LOS_MemFree(POOL.as_ptr(), ptr); + LOS_MemFree(&mut POOL, ptr); } #[inline] - unsafe fn realloc(&self, ptr: *mut u8, _layout: Layout, new_size: usize) -> *mut u8 { - LOS_MemRealloc(POOL.as_ptr(), ptr, new_size) + unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { + let size = new_size as u32; + let size = (size + (4 - 1)) & (-4i32 as u32); + if layout.align() <= MIN_ALIGN { + LOS_MemRealloc(&mut POOL, ptr, size) + } else { + realloc_fallback(self, ptr, layout, new_size) + } } } diff --git a/src/liteos/mod.rs b/src/liteos/mod.rs index 4c82ad27f29413809502a7b17c7f65f136182560..5bc3e593912faff961daae92ba4c7eabc915cf8b 100644 --- a/src/liteos/mod.rs +++ b/src/liteos/mod.rs @@ -1,3 +1,5 @@ +#[allow(unused, non_camel_case_types, non_snake_case)] +mod libc; mod malloc; -pub(crate) mod os; +pub mod os; pub mod thread; diff --git a/src/liteos/os.rs b/src/liteos/os.rs index d583d694cb58c47e4a176f0d3a1c79e9c32b39cc..51b1f87deb11f3244179f4c96ca6150a9b0679c6 100644 --- a/src/liteos/os.rs +++ b/src/liteos/os.rs @@ -1,23 +1,33 @@ -use alloc::string::{String, ToString}; - -pub const LOS_NOK: u32 = 1; -pub const LOS_OK: u32 = 0; +use core::str::from_utf8_unchecked; -extern "C" { - pub fn LOS_MemInit(pool: *const u8, size: u32) -> ErrorType; - pub fn LOS_MemAllocAlign(pool: *const u8, size: usize, boundary: usize) -> *mut u8; - pub fn LOS_MemFree(pool: *const u8, ptr: *const u8) -> ErrorType; - pub fn LOS_MemRealloc(pool: *const u8, ptr: *const u8, size: usize) -> *mut u8; +use super::libc::sys::{self, *}; +use alloc::string::{String, ToString}; +#[inline(always)] +pub fn errno() -> i32 { + unsafe { *__errno_location() as i32 } } -pub type ErrorType = u32; -pub fn errno() -> ErrorType { - 0 +pub fn error_msg(errno: i32) -> String { + let mut buf = [0u8; 256]; + unsafe { + if strerror_r(errno, buf.as_mut_ptr(), buf.len()) < 0 { + panic!("strerror_r failure"); + } + let len = strnlen(buf.as_mut_ptr(), buf.len()); + from_utf8_unchecked(&buf[0..len]).to_string() + } } -pub fn error_msg(_no: ErrorType) -> String { - "0".to_string() +extern "C" { + pub fn printf(fmt: *const i8, ...) -> i32; +} +#[inline(always)] +pub(crate) fn page_size() -> usize { + 4 } pub(crate) fn get_tick_count() -> u32 { 222 } -#[cfg(target_arch = "v7m")] -fn test_shuaihu() {} +#[allow(dead_code)] +#[inline] +pub fn abort() -> ! { + unsafe { sys::abort() }; +} diff --git a/src/liteos/thread.rs b/src/liteos/thread.rs index 0ce5e12ca63a29e618cd210b7e868bbd57e71e87..4c3d8d09a87fa08a6d36de2851330e0bd79921a2 100644 --- a/src/liteos/thread.rs +++ b/src/liteos/thread.rs @@ -1,4 +1,80 @@ -pub struct Thread(); +#[cfg(test)] +mod tests; +use crate::{ + error::{Error, Result}, + os::{self, errno, error_msg}, +}; +use alloc::boxed::Box; +use core::{cmp, mem, ptr}; +pub const DEFAULT_STACK_SIZE: usize = super::libc::pthread::DEFAULT_STACK_SIZE; +use super::libc::{pthread::*, task::LOS_TaskDelay, timer::LOS_MS2Tick}; + +pub struct Thread { + id: pthread_t, +} + impl Thread { - pub fn sleep(_ms: u32) {} + pub unsafe fn new(stack: usize, p: Box) -> Result { + let p = Box::into_raw(box p); + let mut native: pthread_t = mem::MaybeUninit::uninit().assume_init(); + let mut attr: pthread_attr_t = mem::MaybeUninit::uninit().assume_init(); + let rtn = pthread_attr_init(&mut attr); + debug_assert_eq!(rtn, 0); + let mut attr = utils::Defer(attr, |f| { + let rtn = pthread_attr_destroy(f); + debug_assert_eq!(rtn, 0); + }); + let stack_size = cmp::max(stack, MIN_STACK_SIZE); + + let page_size = os::page_size(); + let stack_size = (stack_size + page_size - 1) & (-(page_size as isize - 1) as usize - 1); + let rtn = pthread_attr_setstacksize(&mut *attr, stack_size); + debug_assert_eq!(rtn, 0); + + let ret = pthread_create(&mut native, &*attr, thread_start, p as *mut _); + + return if ret != 0 { + // The thread failed to start and as a result p was not consumed. Therefore, it is + // safe to reconstruct the box so that it gets deallocated. + drop(Box::from_raw(p)); + Err(Error::from_raw_os_error(ret)) + } else { + Ok(Thread { id: native }) + }; + + extern "C" fn thread_start(main: *mut core::ffi::c_void) -> *mut core::ffi::c_void { + unsafe { + // Finally, let's run some code. + Box::from_raw(main as *mut Box)(); + } + ptr::null_mut() + } + } + #[inline] + pub fn yield_now() { + let ret = unsafe { sched_yield() }; + debug_assert_eq!(ret, 0); + } + #[inline] + pub fn set_name(_name: &str) {} + pub fn join(self) { + unsafe { + let ret = pthread_join(self.id, ptr::null_mut()); + mem::forget(self); + debug_assert!(ret == 0, "failed to join thread: {}", error_msg(errno())); + } + } + pub fn sleep(ms: u32) { + unsafe { + let ms = LOS_MS2Tick(ms); + LOS_TaskDelay(ms); + } + } +} + +impl Drop for Thread { + fn drop(&mut self) { + let ret = unsafe { pthread_detach(self.id) }; + debug_assert_eq!(ret, 0); + } } diff --git a/src/liteos/thread/tests.rs b/src/liteos/thread/tests.rs new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/src/unix/locks/futex_rwlock/tests.rs b/src/unix/locks/futex_rwlock/tests.rs index c86c6d14b76d484b168dec90c892a847d4c2c034..f414cc4d17c3ab93d43a03200e1821c9fc411346 100644 --- a/src/unix/locks/futex_rwlock/tests.rs +++ b/src/unix/locks/futex_rwlock/tests.rs @@ -30,6 +30,6 @@ fn test_rwlock_multiple_read() { let abc = rw.try_write(); assert_eq!(abc, true); let abc = rw.try_read(); - debug_assert_eq!(abc, false); + assert_eq!(abc, false); } } diff --git a/src/unix/os.rs b/src/unix/os.rs index 23eaf7608ded7c3f77b5070bbdd0c18d70c7e06a..df5e650357f589b1a43dac775aa680df1169ed36 100644 --- a/src/unix/os.rs +++ b/src/unix/os.rs @@ -29,6 +29,7 @@ pub(crate) fn page_size() -> usize { pub fn abort() -> ! { unsafe { libc::abort() }; } +pub use libc::printf; pub(crate) fn get_tick_count() -> u32 { libc::timespec::now(libc::CLOCK_MONOTONIC).to_millisec() } diff --git a/src/unix/thread.rs b/src/unix/thread.rs index 9f345946a58ff641eb065bffb3f94ea87974a866..ff3dd233815100b398f40f4f44986ef26218bb67 100644 --- a/src/unix/thread.rs +++ b/src/unix/thread.rs @@ -1,5 +1,5 @@ #[cfg(test)] -mod test; +mod tests; use crate::{ error::{Error, Result}, os::{self, __pthread_get_minstack, errno, error_msg}, @@ -17,18 +17,20 @@ pub struct Thread { impl Thread { pub unsafe fn new(stack: usize, p: Box) -> Result { let p = Box::into_raw(box p); - let mut native: libc::pthread_t = mem::zeroed(); - let mut attr: libc::pthread_attr_t = mem::zeroed(); - - debug_assert_eq!(libc::pthread_attr_init(&mut attr), 0); + let mut native: libc::pthread_t = mem::MaybeUninit::uninit().assume_init(); + let mut attr: libc::pthread_attr_t = mem::MaybeUninit::uninit().assume_init(); + let rtn = libc::pthread_attr_init(&mut attr); + debug_assert_eq!(rtn, 0); let mut attr = utils::Defer(attr, |f| { - debug_assert_eq!(libc::pthread_attr_destroy(f), 0); + let rtn = libc::pthread_attr_destroy(f); + debug_assert_eq!(rtn, 0); }); let stack_size = cmp::max(stack, min_stack_size(&attr)); let page_size = os::page_size(); let stack_size = (stack_size + page_size - 1) & (-(page_size as isize - 1) as usize - 1); - debug_assert_eq!(libc::pthread_attr_setstacksize(&mut *attr, stack_size), 0); + let rtn = libc::pthread_attr_setstacksize(&mut *attr, stack_size); + debug_assert_eq!(rtn, 0); let ret = libc::pthread_create(&mut native, &*attr, thread_start, p as *mut _); diff --git a/src/unix/thread/test.rs b/src/unix/thread/tests.rs similarity index 100% rename from src/unix/thread/test.rs rename to src/unix/thread/tests.rs diff --git a/src/unix/utils/timespec.rs b/src/unix/utils/timespec.rs index feb96cdb73aa545e9de4b0372e59964e3b217db3..3710be0c04f4aff1b9e69092cbdb4bf9f7f1a1a0 100644 --- a/src/unix/utils/timespec.rs +++ b/src/unix/utils/timespec.rs @@ -23,7 +23,8 @@ impl TimeSpec for libc::timespec { fn now(id: libc::clockid_t) -> Self { let mut tp = MaybeUninit::uninit(); unsafe { - debug_assert_eq!(libc::clock_gettime(id, tp.as_mut_ptr()), 0); + let rtn =libc::clock_gettime(id, tp.as_mut_ptr()); + debug_assert_eq!(rtn, 0); tp.assume_init() } }