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
13pub fn brk(new_brk: usize) -> isize {
29 let memory_space = current_memory_space();
30 let mut space = memory_space.lock();
31
32 let current = space.current_brk().unwrap_or(0);
34
35 if new_brk == 0 {
37 return current as isize;
38 }
39
40 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 current as isize
52 }
53 }
54}
55
56pub 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 if len == 0 {
91 pr_err!("mmap: len is zero");
92 return -EINVAL as isize;
93 }
94
95 if hint.checked_add(len).is_none() {
97 pr_err!("mmap: address overflow");
98 return -EINVAL as isize;
99 }
100
101 let map_flags = MapFlags::from_bits_truncate(flags);
103 let prot_flags = ProtFlags::from_bits_truncate(prot);
104
105 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 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 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 let mmap_file = if !map_flags.contains(MapFlags::ANONYMOUS) {
125 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 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 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 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 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 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 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 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 if hint == 0 {
210 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 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 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 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 pte_flags |= UniversalPTEFlag::READABLE;
253 }
254 if prot_flags.contains(ProtFlags::EXEC) {
255 pte_flags |= UniversalPTEFlag::EXECUTABLE;
256 }
257
258 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 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 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 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
302pub fn munmap(addr: *mut c_void, len: usize) -> isize {
317 if len == 0 {
319 return 0; }
321
322 let start = addr as usize;
323
324 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
342pub fn mprotect(addr: *mut c_void, len: usize, prot: i32) -> isize {
365 let start = addr as usize;
366
367 if len == 0 {
369 return 0; }
371
372 if start % PAGE_SIZE != 0 {
374 pr_err!("mprotect: address not page-aligned: 0x{:x}", start);
375 return -EINVAL as isize;
376 }
377
378 let prot_flags = ProtFlags::from_bits_truncate(prot);
380
381 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 pte_flags |= UniversalPTEFlag::READABLE;
391 }
392 if prot_flags.contains(ProtFlags::EXEC) {
393 pte_flags |= UniversalPTEFlag::EXECUTABLE;
394 }
395
396 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}