starry_api/imp/mm/
mmap.rs1use alloc::vec;
2use axerrno::{LinuxError, LinuxResult};
3use axhal::paging::{MappingFlags, PageSize};
4use axtask::{TaskExtRef, current};
5use linux_raw_sys::general::*;
6use memory_addr::{MemoryAddr, VirtAddr, VirtAddrRange, align_up_4k};
7
8use crate::file::{File, FileLike};
9
10bitflags::bitflags! {
11 #[derive(Debug)]
15 struct MmapProt: u32 {
16 const READ = PROT_READ;
18 const WRITE = PROT_WRITE;
20 const EXEC = PROT_EXEC;
22 const GROWDOWN = PROT_GROWSDOWN;
24 const GROWSUP = PROT_GROWSUP;
26 }
27}
28
29impl From<MmapProt> for MappingFlags {
30 fn from(value: MmapProt) -> Self {
31 let mut flags = MappingFlags::USER;
32 if value.contains(MmapProt::READ) {
33 flags |= MappingFlags::READ;
34 }
35 if value.contains(MmapProt::WRITE) {
36 flags |= MappingFlags::WRITE;
37 }
38 if value.contains(MmapProt::EXEC) {
39 flags |= MappingFlags::EXECUTE;
40 }
41 flags
42 }
43}
44
45bitflags::bitflags! {
46 #[derive(Debug)]
50 struct MmapFlags: u32 {
51 const SHARED = MAP_SHARED;
53 const PRIVATE = MAP_PRIVATE;
55 const FIXED = MAP_FIXED;
57 const ANONYMOUS = MAP_ANONYMOUS;
59 const NORESERVE = MAP_NORESERVE;
61 const STACK = MAP_STACK;
63 const HUGE = MAP_HUGETLB;
65 const HUGE_1GB = MAP_HUGETLB | MAP_HUGE_1GB;
67 }
68}
69
70pub fn sys_mmap(
71 addr: usize,
72 length: usize,
73 prot: u32,
74 flags: u32,
75 fd: i32,
76 offset: isize,
77) -> LinuxResult<isize> {
78 let curr = current();
79 let process_data = curr.task_ext().process_data();
80 let mut aspace = process_data.aspace.lock();
81 let permission_flags = MmapProt::from_bits_truncate(prot);
82 let map_flags = MmapFlags::from_bits_truncate(flags);
85 if map_flags.contains(MmapFlags::PRIVATE | MmapFlags::SHARED) {
86 return Err(LinuxError::EINVAL);
87 }
88
89 info!(
90 "sys_mmap: addr: {:x?}, length: {:x?}, prot: {:?}, flags: {:?}, fd: {:?}, offset: {:?}",
91 addr, length, permission_flags, map_flags, fd, offset
92 );
93
94 let page_size = if map_flags.contains(MmapFlags::HUGE_1GB) {
95 PageSize::Size1G
96 } else if map_flags.contains(MmapFlags::HUGE) {
97 PageSize::Size2M
98 } else {
99 PageSize::Size4K
100 };
101
102 let start = addr.align_down(page_size);
103 let end = (addr + length).align_up(page_size);
104 let aligned_length = end - start;
105 debug!(
106 "start: {:x?}, end: {:x?}, aligned_length: {:x?}",
107 start, end, aligned_length
108 );
109
110 let start_addr = if map_flags.contains(MmapFlags::FIXED) {
111 if start == 0 {
112 return Err(LinuxError::EINVAL);
113 }
114 let dst_addr = VirtAddr::from(start);
115 aspace.unmap(dst_addr, aligned_length)?;
116 dst_addr
117 } else {
118 aspace
119 .find_free_area(
120 VirtAddr::from(start),
121 aligned_length,
122 VirtAddrRange::new(aspace.base(), aspace.end()),
123 page_size,
124 )
125 .or(aspace.find_free_area(
126 aspace.base(),
127 aligned_length,
128 VirtAddrRange::new(aspace.base(), aspace.end()),
129 page_size,
130 ))
131 .ok_or(LinuxError::ENOMEM)?
132 };
133
134 let populate = if fd == -1 {
135 false
136 } else {
137 !map_flags.contains(MmapFlags::ANONYMOUS)
138 };
139
140 aspace.map_alloc(
141 start_addr,
142 aligned_length,
143 permission_flags.into(),
144 populate,
145 page_size,
146 )?;
147
148 if populate {
149 let file = File::from_fd(fd)?;
150 let file = file.inner();
151 let file_size = file.get_attr()?.size() as usize;
152 if offset < 0 || offset as usize >= file_size {
153 return Err(LinuxError::EINVAL);
154 }
155 let offset = offset as usize;
156 let length = core::cmp::min(length, file_size - offset);
157 let mut buf = vec![0u8; length];
158 file.read_at(offset as u64, &mut buf)?;
159 aspace.write(start_addr, page_size, &buf)?;
160 }
161 Ok(start_addr.as_usize() as _)
162}
163
164pub fn sys_munmap(addr: usize, length: usize) -> LinuxResult<isize> {
165 let curr = current();
166 let process_data = curr.task_ext().process_data();
167 let mut aspace = process_data.aspace.lock();
168 let length = align_up_4k(length);
169 let start_addr = VirtAddr::from(addr);
170 aspace.unmap(start_addr, length)?;
171 axhal::arch::flush_tlb(None);
172 Ok(0)
173}
174
175pub fn sys_mprotect(addr: usize, length: usize, prot: u32) -> LinuxResult<isize> {
176 let Some(permission_flags) = MmapProt::from_bits(prot) else {
178 return Err(LinuxError::EINVAL);
179 };
180 if permission_flags.contains(MmapProt::GROWDOWN | MmapProt::GROWSUP) {
181 return Err(LinuxError::EINVAL);
182 }
183
184 let curr = current();
185 let process_data = curr.task_ext().process_data();
186 let mut aspace = process_data.aspace.lock();
187 let length = align_up_4k(length);
188 let start_addr = VirtAddr::from(addr);
189 aspace.protect(start_addr, length, permission_flags.into())?;
190
191 Ok(0)
192}