os/kernel/syscall/
mm.rs

1use core::ffi::c_void;
2
3use crate::config::PAGE_SIZE;
4use crate::kernel::{current_memory_space, current_task};
5use crate::mm::address::{PageNum, UsizeConvert, Vaddr, Vpn, VpnRange};
6use crate::mm::memory_space::MmapFile;
7use crate::mm::memory_space::mapping_area::AreaType;
8use crate::mm::page_table::UniversalPTEFlag;
9use crate::uapi::errno::{EACCES, EBADF, EEXIST, EINVAL, EIO, ENOMEM, EOPNOTSUPP};
10use crate::uapi::mm::{MAP_FAILED, MapFlags, ProtFlags};
11use crate::{pr_err, pr_warn};
12
13/// brk - 改变数据段的结束地址(堆顶)
14///
15/// # 参数
16/// - `new_brk`: 新的堆顶地址
17///   - 如果为 0,返回当前 brk 值(查询模式)
18///   - 如果非 0,尝试将堆顶设置为该地址
19///
20/// # 返回值
21/// - 成功: 返回新的 brk 值
22/// - 失败: 返回当前 brk 值(Linux 语义:brk 失败时返回旧值)
23///
24/// # 注意
25/// - 如果 new_brk 小于堆起始地址,失败并返回当前 brk
26/// - 如果 new_brk 超过最大堆大小限制,失败并返回当前 brk
27/// - 如果 new_brk 与栈或其他区域重叠,失败并返回当前 brk
28pub fn brk(new_brk: usize) -> isize {
29    let memory_space = current_memory_space();
30    let mut space = memory_space.lock();
31
32    // 获取当前的 brk 值
33    let current = space.current_brk().unwrap_or(0);
34
35    // 如果 new_brk 为 0,返回当前 brk(查询模式)
36    if new_brk == 0 {
37        return current as isize;
38    }
39
40    // 尝试设置新的 brk
41    match space.brk(new_brk) {
42        Ok(addr) => addr as isize,
43        Err(e) => {
44            pr_err!(
45                "brk failed: {:?}, new_brk=0x{:x}, current=0x{:x}",
46                e,
47                new_brk,
48                current
49            );
50            // Linux 语义:失败时返回当前 brk
51            current as isize
52        }
53    }
54}
55
56/// mmap - 将文件或设备映射到内存
57///
58/// # 参数
59/// - `addr`: 建议的映射起始地址
60///   - 如果为 NULL (0),由内核选择地址
61///   - 如果非 NULL,内核会尝试在该地址附近创建映射
62///   - 如果指定了 MAP_FIXED,则必须使用该地址(覆盖现有映射)
63///   - 如果指定了 MAP_FIXED_NOREPLACE,则必须使用该地址(不覆盖现有映射)
64/// - `len`: 映射的长度(字节)
65/// - `prot`: 内存保护标志(PROT_READ | PROT_WRITE | PROT_EXEC)
66/// - `flags`: 映射标志(MAP_SHARED | MAP_PRIVATE | MAP_ANONYMOUS 等)
67/// - `fd`: 文件描述符(目前仅支持匿名映射,必须为 -1)
68/// - `offset`: 文件内偏移量(目前仅支持匿名映射,必须为 0)
69///
70/// # 返回值
71/// - 成功: 返回映射区域的起始地址
72/// - 失败: 返回 MAP_FAILED (-1)
73///
74/// # 支持的特性
75/// - ✅ MAP_ANONYMOUS - 匿名映射
76/// - ✅ MAP_PRIVATE / MAP_SHARED - 私有/共享映射
77/// - ✅ MAP_FIXED - 固定地址映射(覆盖现有)
78/// - ✅ MAP_FIXED_NOREPLACE - 固定地址映射(不覆盖)
79/// - ✅ 地址 hint 机制
80///
81/// # 当前限制
82/// - ❌ 文件映射(需要 VFS 支持)
83/// - ❌ MAP_POPULATE(预分配,当前默认立即分配)
84/// - ❌ MAP_NORESERVE(延迟分配,当前默认立即分配)
85/// - ❌ 大页 (MAP_HUGETLB)
86pub fn mmap(addr: *mut c_void, len: usize, prot: i32, flags: i32, fd: i32, offset: i64) -> isize {
87    let hint = addr as usize;
88
89    // 参数验证
90    if len == 0 {
91        pr_err!("mmap: len is zero");
92        return -EINVAL as isize;
93    }
94
95    // 溢出检查
96    if hint.checked_add(len).is_none() {
97        pr_err!("mmap: address overflow");
98        return -EINVAL as isize;
99    }
100
101    // 解析和验证标志
102    let map_flags = MapFlags::from_bits_truncate(flags);
103    let prot_flags = ProtFlags::from_bits_truncate(prot);
104
105    // 检查 MAP_SHARED / MAP_PRIVATE(必须有且仅有一个)
106    if !map_flags.is_valid() {
107        pr_err!("mmap: must specify exactly one of MAP_SHARED or MAP_PRIVATE");
108        return -EINVAL as isize;
109    }
110
111    // 检查 MAP_FIXED 和 MAP_FIXED_NOREPLACE 互斥
112    if map_flags.contains(MapFlags::FIXED) && map_flags.contains(MapFlags::FIXED_NOREPLACE) {
113        pr_err!("mmap: MAP_FIXED and MAP_FIXED_NOREPLACE are mutually exclusive");
114        return -EINVAL as isize;
115    }
116
117    // 检查 MAP_FIXED 的地址对齐
118    if map_flags.contains(MapFlags::FIXED) && hint & (PAGE_SIZE - 1) != 0 {
119        pr_err!("mmap: MAP_FIXED requires page-aligned address");
120        return -EINVAL as isize;
121    }
122
123    // 创建 MmapFile(如果是文件映射)
124    let mmap_file = if !map_flags.contains(MapFlags::ANONYMOUS) {
125        // 文件映射:验证文件描述符和偏移量
126        if offset < 0 || (offset as usize) % PAGE_SIZE != 0 {
127            pr_err!("mmap: file offset must be non-negative and page-aligned");
128            return -EINVAL as isize;
129        }
130
131        // 获取文件对象
132        let task = current_task();
133        let file = match task.lock().fd_table.get(fd as usize) {
134            Ok(f) => f,
135            Err(_) => {
136                pr_err!("mmap: invalid file descriptor {}", fd);
137                return -EBADF as isize;
138            }
139        };
140
141        // 权限检查
142        if prot_flags.contains(ProtFlags::READ) && !file.readable() {
143            pr_err!("mmap: file not readable but PROT_READ requested");
144            return -EACCES as isize;
145        }
146        if prot_flags.contains(ProtFlags::WRITE)
147            && map_flags.contains(MapFlags::SHARED)
148            && !file.writable()
149        {
150            pr_err!("mmap: file not writable but PROT_WRITE + MAP_SHARED requested");
151            return -EACCES as isize;
152        }
153
154        Some(MmapFile {
155            file,
156            offset: offset as usize,
157            len,
158            prot: prot_flags,
159            flags: map_flags,
160        })
161    } else {
162        // 匿名映射验证
163        if fd != -1 {
164            pr_err!("mmap: anonymous mapping requires fd == -1");
165            return -EINVAL as isize;
166        }
167        if offset != 0 {
168            pr_err!("mmap: anonymous mapping requires offset == 0");
169            return -EINVAL as isize;
170        }
171        None
172    };
173
174    // 确定映射地址
175    let memory_space = current_memory_space();
176    let mut space = memory_space.lock();
177
178    let start_addr = if map_flags.contains(MapFlags::FIXED) {
179        // MAP_FIXED: 强制使用指定地址,覆盖现有映射
180        match space.munmap(hint, len) {
181            Ok(_) => hint,
182            Err(e) => {
183                pr_err!("mmap: MAP_FIXED munmap failed: {:?}", e);
184                return -EINVAL as isize;
185            }
186        }
187    } else if map_flags.contains(MapFlags::FIXED_NOREPLACE) {
188        // MAP_FIXED_NOREPLACE: 强制使用指定地址,不覆盖
189        if hint & (PAGE_SIZE - 1) != 0 {
190            pr_err!("mmap: MAP_FIXED_NOREPLACE requires page-aligned address");
191            return -EINVAL as isize;
192        }
193
194        let start_vpn = Vpn::from_addr_floor(Vaddr::from_usize(hint));
195        let end_vpn = Vpn::from_addr_ceil(Vaddr::from_usize(hint + len));
196        let range = VpnRange::new(start_vpn, end_vpn);
197
198        // 检查是否与现有区域重叠
199        let has_overlap = space.areas().iter().any(|a| a.vpn_range().overlaps(&range));
200
201        if has_overlap {
202            pr_err!("mmap: MAP_FIXED_NOREPLACE address already mapped");
203            return -EEXIST as isize;
204        }
205
206        hint
207    } else {
208        // 正常分配(使用 hint)
209        if hint == 0 {
210            // hint == 0: 内核选择地址
211            match space.find_free_region(len, PAGE_SIZE) {
212                Some(addr) => addr,
213                None => {
214                    pr_err!("mmap: out of memory");
215                    return -ENOMEM as isize;
216                }
217            }
218        } else {
219            // hint != 0: 尝试使用 hint,失败则内核选择
220            let aligned_hint = hint & !(PAGE_SIZE - 1);
221
222            let start_vpn = Vpn::from_addr_floor(Vaddr::from_usize(aligned_hint));
223            let end_vpn = Vpn::from_addr_ceil(Vaddr::from_usize(aligned_hint + len));
224            let range = VpnRange::new(start_vpn, end_vpn);
225
226            let hint_available = !space.areas().iter().any(|a| a.vpn_range().overlaps(&range));
227
228            if hint_available {
229                aligned_hint
230            } else {
231                // hint 不可用,内核选择
232                match space.find_free_region(len, PAGE_SIZE) {
233                    Some(addr) => addr,
234                    None => {
235                        pr_err!("mmap: out of memory");
236                        return -ENOMEM as isize;
237                    }
238                }
239            }
240        }
241    };
242
243    // 转换权限标志
244    let mut pte_flags = UniversalPTEFlag::USER_ACCESSIBLE | UniversalPTEFlag::VALID;
245
246    if prot_flags.contains(ProtFlags::READ) {
247        pte_flags |= UniversalPTEFlag::READABLE;
248    }
249    if prot_flags.contains(ProtFlags::WRITE) {
250        pte_flags |= UniversalPTEFlag::WRITEABLE;
251        // RISC-V 特性:写权限需要读权限
252        pte_flags |= UniversalPTEFlag::READABLE;
253    }
254    if prot_flags.contains(ProtFlags::EXEC) {
255        pte_flags |= UniversalPTEFlag::EXECUTABLE;
256    }
257
258    // 创建映射
259    let start_vpn = Vpn::from_addr_floor(Vaddr::from_usize(start_addr));
260    let end_vpn = Vpn::from_addr_ceil(Vaddr::from_usize(start_addr + len));
261    let vpn_range = VpnRange::new(start_vpn, end_vpn);
262
263    // 插入映射区域
264    if let Err(e) =
265        space.insert_framed_area(vpn_range, AreaType::UserMmap, pte_flags, None, mmap_file)
266    {
267        pr_err!(
268            "mmap failed to insert area: {:?}, addr=0x{:x}, len=0x{:x}, prot=0x{:x}, flags=0x{:x}",
269            e,
270            hint,
271            len,
272            prot,
273            flags
274        );
275        return MAP_FAILED;
276    }
277
278    // 如果是文件映射,立即加载数据
279    if let Some(area) = space.areas_mut().last_mut() {
280        if let Err(e) = area.load_from_file() {
281            pr_err!(
282                "mmap failed to load file data: {:?}, addr=0x{:x}, len=0x{:x}, fd={}",
283                e,
284                start_addr,
285                len,
286                fd
287            );
288            // 加载失败,清理已创建的映射
289            if let Err(unmap_err) = space.munmap(start_addr, len) {
290                pr_warn!(
291                    "mmap: failed to clean up mapping on load error: {:?}",
292                    unmap_err
293                );
294            }
295            return -EIO as isize;
296        }
297    }
298
299    start_addr as isize
300}
301
302/// munmap - 解除内存映射
303///
304/// # 参数
305/// - `addr`: 要解除映射的起始地址
306/// - `len`: 要解除映射的长度(字节)
307///
308/// # 返回值
309/// - 成功: 返回 0
310/// - 失败: 返回 -errno
311///
312/// # 注意
313/// - 如果 addr 未对齐到页边界,会向下对齐
314/// - 如果范围跨越多个映射区域,会部分解除每个区域
315/// - 如果地址未映射,返回成功(幂等操作)
316pub fn munmap(addr: *mut c_void, len: usize) -> isize {
317    // 参数验证
318    if len == 0 {
319        return 0; // POSIX: len=0 是合法的,什么都不做
320    }
321
322    let start = addr as usize;
323
324    // 获取内存空间并执行解除映射
325    let memory_space = current_memory_space();
326    let mut space = memory_space.lock();
327
328    match space.munmap(start, len) {
329        Ok(()) => 0,
330        Err(e) => {
331            pr_err!(
332                "munmap failed: {:?}, addr=0x{:x}, len=0x{:x}",
333                e,
334                start,
335                len
336            );
337            -EINVAL as isize
338        }
339    }
340}
341
342/// mprotect - 修改内存区域的保护权限
343///
344/// # 参数
345/// - `addr`: 要修改权限的起始地址(必须页对齐)
346/// - `len`: 要修改权限的长度(字节)
347/// - `prot`: 新的保护标志(PROT_READ | PROT_WRITE | PROT_EXEC | PROT_NONE)
348///
349/// # 返回值
350/// - 成功: 返回 0
351/// - 失败: 返回 -errno
352///
353/// # 注意
354/// - 地址必须页对齐,否则返回 EINVAL
355/// - 范围必须完全在现有映射区域内,否则返回 ENOMEM
356/// - 只能修改通过 mmap 或 brk 创建的用户空间映射
357///
358/// # 支持的特性
359/// - ✅ PROT_NONE - 不可访问
360/// - ✅ PROT_READ - 可读
361/// - ✅ PROT_WRITE - 可写(自动包含可读,RISC-V 特性)
362/// - ✅ PROT_EXEC - 可执行
363/// - ✅ 跨多个映射区域的权限修改
364pub fn mprotect(addr: *mut c_void, len: usize, prot: i32) -> isize {
365    let start = addr as usize;
366
367    // 参数验证
368    if len == 0 {
369        return 0; // len=0 是合法的,什么都不做
370    }
371
372    // 检查地址对齐
373    if start % PAGE_SIZE != 0 {
374        pr_err!("mprotect: address not page-aligned: 0x{:x}", start);
375        return -EINVAL as isize;
376    }
377
378    // 解析保护标志
379    let prot_flags = ProtFlags::from_bits_truncate(prot);
380
381    // 转换为页表标志
382    let mut pte_flags = UniversalPTEFlag::USER_ACCESSIBLE | UniversalPTEFlag::VALID;
383
384    if prot_flags.contains(ProtFlags::READ) {
385        pte_flags |= UniversalPTEFlag::READABLE;
386    }
387    if prot_flags.contains(ProtFlags::WRITE) {
388        pte_flags |= UniversalPTEFlag::WRITEABLE;
389        // RISC-V 特性:写权限需要读权限
390        pte_flags |= UniversalPTEFlag::READABLE;
391    }
392    if prot_flags.contains(ProtFlags::EXEC) {
393        pte_flags |= UniversalPTEFlag::EXECUTABLE;
394    }
395
396    // PROT_NONE: 不添加任何权限标志(只保留 VALID 和 USER_ACCESSIBLE)
397    // 注意:RISC-V 中,如果没有 R/W/X 权限,访问会触发页面故障
398
399    // 获取内存空间并执行权限修改
400    let memory_space = current_memory_space();
401    let mut space = memory_space.lock();
402
403    match space.mprotect(start, len, pte_flags) {
404        Ok(()) => 0,
405        Err(e) => {
406            pr_err!(
407                "mprotect failed: {:?}, addr=0x{:x}, len=0x{:x}, prot=0x{:x}",
408                e,
409                start,
410                len,
411                prot
412            );
413            -ENOMEM as isize
414        }
415    }
416}