os/util/user_buffer.rs
1//! 用户态缓冲区
2//!
3//! 一般来说,用户态程序通过一段位于用户地址的缓冲区与内核进行数据交换
4//! 例如,系统调用接口通常传入指向用户缓冲区的指针和长度
5//! 这个模块提供了对这类缓冲区的抽象和操作方法
6//!
7//! XXX: 目前的实现直接依赖于 RISC-V 特权级的 SUM 位来允许内核访问用户空间
8//! 未来可能需要改进为通过页表映射等方式实现更通用的用户空间访问
9
10use alloc::vec::Vec;
11use core::ptr;
12
13use crate::arch::constant::{USER_BASE, USER_TOP};
14use crate::arch::trap::SumGuard;
15
16/// 向用户空间写入数据
17/// # 参数
18/// - `user_ptr`: 指向用户空间的指针
19/// - `value`: 要写入的数据
20/// # Safety
21/// 调用者必须确保 `user_ptr` 指向的内存是有效且可写的用户空间地址。
22pub unsafe fn write_to_user<T>(user_ptr: *mut T, value: T) {
23 let _guard = SumGuard::new();
24 unsafe {
25 ptr::write_volatile(user_ptr, value);
26 }
27}
28
29/// 从用户空间读取数据
30/// # 参数
31/// - `user_ptr`: 指向用户空间的指针
32/// # 返回值
33/// - 读取到的数据
34/// # Safety
35/// 调用者必须确保 `user_ptr` 指向的内存是有效且可读的用户空间地址。
36pub unsafe fn read_from_user<T: Copy>(user_ptr: *const T) -> T {
37 let _guard = SumGuard::new();
38 unsafe { ptr::read_volatile(user_ptr) }
39}
40
41/// 用户缓冲区结构体
42pub struct UserBuffer {
43 data: *mut u8,
44 len: usize,
45}
46
47impl UserBuffer {
48 /// 创建一个新的用户缓冲区
49 /// # 参数:
50 /// - `data`: 指向用户缓冲区的指针
51 /// - `len`: 缓冲区的长度
52 pub fn new(data: *mut u8, len: usize) -> Self {
53 Self { data, len }
54 }
55
56 /// 从用户缓冲区向内核缓冲区复制数据
57 /// # Safety
58 /// - 调用方必须保证 `self.data .. self.data + self.len` 是用户空间中有效且已映射的可读内存;
59 /// - 与目标内核缓冲区不重叠(此处目标是新分配的 Vec,天然满足);
60 /// - 若无法在此处静态保证有效性,应在更高一层先做页表/范围校验。
61 pub unsafe fn copy_from_user(self) -> Vec<u8> {
62 if self.len == 0 {
63 return Vec::new();
64 }
65 let mut vec = Vec::with_capacity(self.len);
66 unsafe {
67 let _guard = SumGuard::new();
68 vec.set_len(self.len);
69 ptr::copy_nonoverlapping(self.data as *const u8, vec.as_mut_ptr(), self.len);
70 }
71 vec
72 }
73
74 /// 将内核缓冲区数据拷贝到用户缓冲区
75 /// 超过用户缓冲区长度的部分将被截断
76 /// # Safety
77 /// - 调用方必须保证 `self.data .. self.data + self.len` 是用户空间中有效且已映射的可写内存;
78 /// - 与源切片不重叠(此处源在内核内存,通常不与用户缓冲重叠)。
79 pub unsafe fn copy_to_user(self, data: &[u8]) {
80 if self.len == 0 || data.is_empty() {
81 return;
82 }
83 let n = core::cmp::min(self.len, data.len());
84 unsafe {
85 let _guard = SumGuard::new();
86 ptr::copy_nonoverlapping(data.as_ptr(), self.data, n);
87 }
88 }
89
90 /// TODO: 运行时做一次“粗略”范围校验(不保证已映射,仅做地址区间与溢出检查)
91 /// 建议在 syscall 层或结合 MemorySpace 做页表级校验。
92 pub fn range_sane(&self) -> bool {
93 unimplemented!();
94 let start = self.data as usize;
95 let end = start.checked_add(self.len).unwrap_or(usize::MAX);
96 // start < USER_BASE && end <= USER_TOP;
97 true
98 }
99
100 /// 返回用户缓冲区长度
101 #[inline]
102 pub fn len(&self) -> usize {
103 self.len
104 }
105
106 /// 判断用户缓冲区是否为空
107 #[inline]
108 pub fn is_empty(&self) -> bool {
109 self.len == 0
110 }
111}
112
113/// 验证用户空间指针是否有效
114///
115/// 检查指针是否:
116/// 1. 非空
117/// 2. 指向用户空间地址范围 [USER_BASE, USER_TOP]
118/// 3. 指针指向的内存区域不溢出用户空间
119///
120/// # 参数
121/// * `ptr` - 要验证的用户空间指针
122///
123/// # 返回值
124/// * `true` - 指针有效
125/// * `false` - 指针无效
126///
127/// # 注意
128/// 此函数仅进行地址范围检查,不验证内存是否已映射或可访问。
129/// 实际访问内存前,仍需处理可能的页错误。
130pub fn validate_user_ptr<T>(ptr: *const T) -> bool {
131 if ptr.is_null() {
132 return false;
133 }
134
135 let addr = ptr as usize;
136 let size = core::mem::size_of::<T>();
137
138 // 检查起始地址是否在用户空间范围内
139 // USER_BASE 为 0,所以只需检查上界
140 if addr > USER_TOP {
141 return false;
142 }
143
144 // 检查是否会溢出用户空间
145 if let Some(end_addr) = addr.checked_add(size) {
146 if end_addr > USER_TOP + 1 {
147 return false;
148 }
149 } else {
150 // 地址加法溢出
151 return false;
152 }
153
154 true
155}
156
157/// 验证可写的用户空间指针是否有效
158///
159/// 与 `validate_user_ptr` 功能相同,但用于可变指针。
160///
161/// # 参数
162/// * `ptr` - 要验证的可写用户空间指针
163///
164/// # 返回值
165/// * `true` - 指针有效
166/// * `false` - 指针无效
167#[inline]
168pub fn validate_user_ptr_mut<T>(ptr: *mut T) -> bool {
169 validate_user_ptr(ptr as *const T)
170}