1use crate::{
11 arch::xshim::VirtAddr,
12 memory::address_space::{VmaFlags, VmaType},
13 process::current_task_clone,
14 syscall::error::SyscallError,
15};
16use core::sync::atomic::Ordering;
17use strat9_abi::data::MemoryRegionInfo as MemoryRegionInfoAbi;
18
19pub const BRK_BASE: u64 = 0x0000_0000_2000_0000; pub const MMAP_BASE: u64 = 0x0000_0000_6000_0000; const USER_SPACE_END: u64 = crate::memory::userslice::USER_SPACE_END;
31
32const PROT_READ: u32 = 1 << 0;
37const PROT_WRITE: u32 = 1 << 1;
38const PROT_EXEC: u32 = 1 << 2;
39
40const MAP_SHARED: u32 = 1 << 0;
45const MAP_PRIVATE: u32 = 1 << 1;
46const MAP_FIXED: u32 = 1 << 4;
47const MAP_ANONYMOUS: u32 = 1 << 5;
48const MAP_HUGETLB: u32 = 1 << 11; const MAP_FIXED_NOREPLACE: u32 = 1 << 20; const MREMAP_MAYMOVE: u64 = 1 << 0;
52
53#[inline]
59fn page_align_up(addr: u64) -> u64 {
60 (addr.wrapping_add(4095)) & !4095u64
61}
62
63#[inline]
65fn huge_page_align_up(addr: u64) -> u64 {
66 (addr.wrapping_add((2 * 1024 * 1024) - 1)) & !((2 * 1024 * 1024) - 1)
67}
68
69fn prot_to_vma_flags(prot: u32) -> VmaFlags {
71 VmaFlags {
72 readable: prot & PROT_READ != 0,
73 writable: prot & PROT_WRITE != 0,
74 executable: prot & PROT_EXEC != 0,
75 user_accessible: true,
76 }
77}
78
79fn vma_flags_to_prot(flags: VmaFlags) -> u32 {
81 (if flags.readable { PROT_READ } else { 0 })
82 | (if flags.writable { PROT_WRITE } else { 0 })
83 | (if flags.executable { PROT_EXEC } else { 0 })
84}
85
86pub fn sys_mmap(
98 addr: u64,
99 len: u64,
100 prot: u32,
101 flags: u32,
102 fd_raw: u64,
103 offset: u64,
104) -> Result<u64, SyscallError> {
105 if len == 0 {
107 return Err(SyscallError::InvalidArgument);
108 }
109
110 let known_flags =
111 MAP_SHARED | MAP_PRIVATE | MAP_FIXED | MAP_ANONYMOUS | MAP_HUGETLB | MAP_FIXED_NOREPLACE;
112 if flags & !known_flags != 0 {
113 return Err(SyscallError::InvalidArgument);
114 }
115
116 let is_huge = flags & MAP_HUGETLB != 0;
117 let page_size = if is_huge {
118 crate::memory::address_space::VmaPageSize::Huge
119 } else {
120 crate::memory::address_space::VmaPageSize::Small
121 };
122 let page_bytes = page_size.bytes();
123
124 if flags & MAP_ANONYMOUS == 0 {
126 let fd = fd_raw as u32;
127 let file_offset = offset;
128
129 let is_private = flags & MAP_PRIVATE != 0;
130 let is_shared = flags & MAP_SHARED != 0;
131 if is_private == is_shared {
132 return Err(SyscallError::InvalidArgument);
133 }
134 if !is_private {
135 log::warn!("sys_mmap: file-backed MAP_SHARED not yet supported");
136 return Err(SyscallError::NotImplemented);
137 }
138 if prot & !(PROT_READ | PROT_WRITE | PROT_EXEC) != 0 {
139 return Err(SyscallError::InvalidArgument);
140 }
141
142 let len_aligned = if is_huge {
143 huge_page_align_up(len)
144 } else {
145 page_align_up(len)
146 };
147 if len_aligned == 0 {
148 return Err(SyscallError::InvalidArgument);
149 }
150 let n_pages = (len_aligned / page_bytes) as usize;
151
152 let task = current_task_clone().ok_or(SyscallError::Fault)?;
153 let open_file = {
154 let fd_table = unsafe { &*task.process.fd_table.get() };
155 fd_table.get(fd)?
156 };
157 let addr_space = task.process.address_space_arc();
158
159 let target = if flags & MAP_FIXED != 0 {
160 if addr % page_bytes != 0 || addr == 0 {
161 return Err(SyscallError::InvalidArgument);
162 }
163 if addr.saturating_add(len_aligned) > USER_SPACE_END {
164 return Err(SyscallError::InvalidArgument);
165 }
166 if flags & MAP_FIXED_NOREPLACE != 0 {
167 if addr_space.has_mapping_in_range(addr, len_aligned) {
168 return Err(SyscallError::AlreadyExists);
169 }
170 } else {
171 addr_space
172 .unmap_range(addr, len_aligned)
173 .map_err(|_| SyscallError::InvalidArgument)?;
174 }
175 addr
176 } else {
177 let hint = if addr != 0 {
178 addr
179 } else {
180 task.process.mmap_hint.load(Ordering::Relaxed)
181 };
182 addr_space
183 .find_free_vma_range(hint, n_pages, page_size)
184 .or_else(|| {
185 addr_space.find_free_vma_range(crate::kaslr::mmap_base(), n_pages, page_size)
186 })
187 .ok_or(SyscallError::OutOfMemory)?
188 };
189
190 let vma_flags = prot_to_vma_flags(prot);
191 addr_space
192 .map_region(target, n_pages, vma_flags, VmaType::Anonymous, page_size)
193 .map_err(|_| SyscallError::OutOfMemory)?;
194
195 let read_len = len as usize;
197 let mut kbuf = [0u8; 4096];
198 let mut file_off = file_offset;
199 let mut dst_off = 0usize;
200 while dst_off < read_len {
201 let chunk = core::cmp::min(4096, read_len - dst_off);
202 let n = open_file.pread(file_off, &mut kbuf[..chunk]).unwrap_or(0);
203 if n == 0 {
204 break;
205 }
206 let mut written = 0;
207 while written < n {
208 let vaddr = target + (dst_off + written) as u64;
209 let page_off = (vaddr & 0xFFF) as usize;
210 let to_write = core::cmp::min(n - written, 4096 - page_off);
211 let phys = addr_space
212 .translate(VirtAddr::new(vaddr))
213 .ok_or(SyscallError::Fault)?;
214 let hhdm_ptr = crate::memory::phys_to_virt(phys.as_u64()) as *mut u8;
215 unsafe {
216 core::ptr::copy_nonoverlapping(kbuf.as_ptr().add(written), hhdm_ptr, to_write);
217 }
218 written += to_write;
219 }
220 file_off += n as u64;
221 dst_off += n;
222 }
223
224 if flags & MAP_FIXED == 0 {
225 let new_hint = target.saturating_add(len_aligned);
226 let _ = task
227 .process
228 .mmap_hint
229 .fetch_max(new_hint, Ordering::Relaxed);
230 }
231
232 log::trace!(
233 "sys_mmap: file-backed {:#x}..{:#x} (fd={}, off={:#x})",
234 target,
235 target + len_aligned,
236 fd,
237 file_offset,
238 );
239 return Ok(target);
240 }
241
242 let is_private = flags & MAP_PRIVATE != 0;
243 let is_shared = flags & MAP_SHARED != 0;
244 if is_private == is_shared {
246 return Err(SyscallError::InvalidArgument);
247 }
248
249 if offset != 0 {
251 return Err(SyscallError::InvalidArgument);
252 }
253
254 if prot & !(PROT_READ | PROT_WRITE | PROT_EXEC) != 0 {
256 return Err(SyscallError::InvalidArgument);
257 }
258
259 let len_aligned = if is_huge {
261 huge_page_align_up(len)
262 } else {
263 page_align_up(len)
264 };
265 if len_aligned == 0 {
266 return Err(SyscallError::InvalidArgument);
268 }
269 let n_pages = (len_aligned / page_bytes) as usize;
270
271 let task = current_task_clone().ok_or(SyscallError::Fault)?;
273 let addr_space = task.process.address_space_arc();
274
275 let target = if flags & MAP_FIXED != 0 {
276 if addr % page_bytes != 0 || addr == 0 {
278 return Err(SyscallError::InvalidArgument);
279 }
280 if addr.saturating_add(len_aligned) > USER_SPACE_END {
281 return Err(SyscallError::InvalidArgument);
282 }
283 if flags & MAP_FIXED_NOREPLACE != 0 {
284 if addr_space.has_mapping_in_range(addr, len_aligned) {
286 return Err(SyscallError::AlreadyExists);
287 }
288 } else {
289 addr_space
291 .unmap_range(addr, len_aligned)
292 .map_err(|_| SyscallError::InvalidArgument)?;
293 }
294 addr
295 } else {
296 let hint = if addr != 0 {
298 addr
299 } else {
300 task.process.mmap_hint.load(Ordering::Relaxed)
301 };
302
303 addr_space
305 .find_free_vma_range(hint, n_pages, page_size)
306 .or_else(|| {
307 addr_space.find_free_vma_range(crate::kaslr::mmap_base(), n_pages, page_size)
308 })
309 .ok_or(SyscallError::OutOfMemory)?
310 };
311
312 let vma_flags = prot_to_vma_flags(prot);
314 addr_space
315 .reserve_region(target, n_pages, vma_flags, VmaType::Anonymous, page_size)
316 .map_err(|_| SyscallError::OutOfMemory)?;
317
318 if flags & MAP_FIXED == 0 {
320 let new_hint = target.saturating_add(len_aligned);
321 let _ = task
323 .process
324 .mmap_hint
325 .fetch_max(new_hint, Ordering::Relaxed);
326 }
327
328 log::trace!(
329 "sys_mmap: mapped {:#x}..{:#x} ({} pages, prot={:#x}, flags={:#x})",
330 target,
331 target + len_aligned,
332 n_pages,
333 prot,
334 flags,
335 );
336
337 Ok(target)
338}
339
340pub fn sys_munmap(addr: u64, len: u64) -> Result<u64, SyscallError> {
350 if addr == 0 || addr & 0xFFF != 0 {
351 return Err(SyscallError::InvalidArgument);
352 }
353 if len == 0 {
354 return Err(SyscallError::InvalidArgument);
355 }
356
357 let len_aligned = page_align_up(len);
358 if len_aligned == 0 {
359 return Err(SyscallError::InvalidArgument);
360 }
361 if addr.saturating_add(len_aligned) > USER_SPACE_END {
362 return Err(SyscallError::InvalidArgument);
363 }
364
365 let task = current_task_clone().ok_or(SyscallError::Fault)?;
366 task.process
367 .address_space_arc()
368 .unmap_range(addr, len_aligned)
369 .map_err(|_| SyscallError::InvalidArgument)?;
370
371 log::trace!(
372 "sys_munmap: unmapped {:#x}..{:#x}",
373 addr,
374 addr + len_aligned
375 );
376
377 Ok(0)
378}
379
380pub fn sys_mremap(
388 old_addr: u64,
389 old_size: u64,
390 new_size: u64,
391 flags: u64,
392) -> Result<u64, SyscallError> {
393 if old_size == 0 || new_size == 0 {
394 return Err(SyscallError::InvalidArgument);
395 }
396 if flags & !MREMAP_MAYMOVE != 0 {
397 return Err(SyscallError::InvalidArgument);
398 }
399
400 let task = current_task_clone().ok_or(SyscallError::Fault)?;
401 let addr_space = task.process.address_space_arc();
402 let vma = addr_space
403 .region_by_start(old_addr)
404 .ok_or(SyscallError::Fault)?;
405
406 let page_bytes = vma.page_size.bytes();
407 if old_addr % page_bytes != 0 {
408 return Err(SyscallError::InvalidArgument);
409 }
410
411 let old_len_aligned = if vma.page_size == crate::memory::address_space::VmaPageSize::Huge {
412 huge_page_align_up(old_size)
413 } else {
414 page_align_up(old_size)
415 };
416 let new_len_aligned = if vma.page_size == crate::memory::address_space::VmaPageSize::Huge {
417 huge_page_align_up(new_size)
418 } else {
419 page_align_up(new_size)
420 };
421 if old_len_aligned == 0 || new_len_aligned == 0 {
422 return Err(SyscallError::InvalidArgument);
423 }
424
425 let tracked_len = (vma.page_count as u64)
426 .checked_mul(page_bytes)
427 .ok_or(SyscallError::InvalidArgument)?;
428 if old_len_aligned != tracked_len {
429 return Err(SyscallError::InvalidArgument);
430 }
431
432 if new_len_aligned == old_len_aligned {
433 return Ok(old_addr);
434 }
435
436 if new_len_aligned < old_len_aligned {
437 let tail_addr = old_addr
438 .checked_add(new_len_aligned)
439 .ok_or(SyscallError::InvalidArgument)?;
440 let tail_len = old_len_aligned - new_len_aligned;
441 addr_space
442 .unmap_range(tail_addr, tail_len)
443 .map_err(|_| SyscallError::InvalidArgument)?;
444 return Ok(old_addr);
445 }
446
447 let grow_len = new_len_aligned - old_len_aligned;
448 let grow_start = old_addr
449 .checked_add(old_len_aligned)
450 .ok_or(SyscallError::InvalidArgument)?;
451
452 if !addr_space.has_mapping_in_range(grow_start, grow_len) {
453 let grow_pages = (grow_len / page_bytes) as usize;
454 addr_space
455 .reserve_region(
456 grow_start,
457 grow_pages,
458 vma.flags,
459 vma.vma_type,
460 vma.page_size,
461 )
462 .map_err(|_| SyscallError::OutOfMemory)?;
463 return Ok(old_addr);
464 }
465
466 if flags & MREMAP_MAYMOVE == 0 {
467 return Err(SyscallError::OutOfMemory);
468 }
469
470 let has_present_pages = addr_space
471 .any_mapped_in_range(old_addr, old_len_aligned, vma.page_size)
472 .map_err(|_| SyscallError::InvalidArgument)?;
473 if has_present_pages {
474 return Err(SyscallError::OutOfMemory);
475 }
476
477 let new_pages = (new_len_aligned / page_bytes) as usize;
478 let new_addr = addr_space
479 .find_free_vma_range(crate::kaslr::mmap_base(), new_pages, vma.page_size)
480 .ok_or(SyscallError::OutOfMemory)?;
481
482 addr_space
483 .unmap_range(old_addr, old_len_aligned)
484 .map_err(|_| SyscallError::InvalidArgument)?;
485 addr_space
486 .reserve_region(new_addr, new_pages, vma.flags, vma.vma_type, vma.page_size)
487 .map_err(|_| SyscallError::OutOfMemory)?;
488 Ok(new_addr)
489}
490
491pub fn sys_mprotect(addr: u64, len: u64, prot: u64) -> Result<u64, SyscallError> {
493 if len == 0 || addr == 0 || addr & 0xFFF != 0 {
494 return Err(SyscallError::InvalidArgument);
495 }
496 let prot_u32 = u32::try_from(prot).map_err(|_| SyscallError::InvalidArgument)?;
497 if prot_u32 & !(PROT_READ | PROT_WRITE | PROT_EXEC) != 0 {
498 return Err(SyscallError::InvalidArgument);
499 }
500
501 let len_aligned = page_align_up(len);
502 if len_aligned == 0 {
503 return Err(SyscallError::InvalidArgument);
504 }
505 if addr.saturating_add(len_aligned) > USER_SPACE_END {
506 return Err(SyscallError::InvalidArgument);
507 }
508
509 let task = current_task_clone().ok_or(SyscallError::Fault)?;
510 let addr_space = task.process.address_space_arc();
511 let flags = prot_to_vma_flags(prot_u32);
512
513 addr_space
514 .protect_range(addr, len_aligned, flags)
515 .map_err(|_| SyscallError::InvalidArgument)?;
516
517 Ok(0)
518}
519
520pub fn sys_mem_region_export(addr: u64) -> Result<u64, SyscallError> {
522 let task = current_task_clone().ok_or(SyscallError::Fault)?;
523 let address_space = task.process.address_space_arc();
524 let handle_cap = crate::capability::CapId::new();
525 let resource_id = crate::memory::memory_region_registry()
526 .export_region(&address_space, addr, handle_cap)
527 .map_err(|error| match error {
528 crate::memory::RegionCapError::InvalidRegion
529 | crate::memory::RegionCapError::IncompleteRegion
530 | crate::memory::RegionCapError::InvalidAddress => SyscallError::InvalidArgument,
531 crate::memory::RegionCapError::PermissionDenied => SyscallError::PermissionDenied,
532 crate::memory::RegionCapError::OutOfMemory => SyscallError::OutOfMemory,
533 crate::memory::RegionCapError::InconsistentState => SyscallError::IoError,
534 crate::memory::RegionCapError::NotFound => SyscallError::NotFound,
535 })?;
536
537 let cap = crate::capability::Capability {
538 id: handle_cap,
539 resource_type: crate::capability::ResourceType::MemoryRegion,
540 permissions: crate::capability::CapPermissions {
541 read: true,
542 write: true,
543 execute: true,
544 grant: true,
545 revoke: true,
546 },
547 resource: resource_id as usize,
548 badge: handle_cap.as_u64(),
549 };
550 let cap_id = unsafe { (&mut *task.process.capabilities.get()).insert(cap) };
551 Ok(cap_id.as_u64())
552}
553
554pub fn sys_mem_region_map(handle: u64, addr_hint: u64, out_ptr: u64) -> Result<u64, SyscallError> {
556 crate::silo::enforce_cap_for_current_task(handle)?;
557 if out_ptr == 0 {
558 return Err(SyscallError::Fault);
559 }
560
561 let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
562 let caps = unsafe { &*task.process.capabilities.get() };
563 let cap = caps
564 .get(crate::capability::CapId::from_raw(handle))
565 .ok_or(SyscallError::BadHandle)?;
566 if cap.resource_type != crate::capability::ResourceType::MemoryRegion {
567 return Err(SyscallError::BadHandle);
568 }
569
570 let requested_flags = VmaFlags {
571 readable: cap.permissions.read,
572 writable: cap.permissions.write,
573 executable: cap.permissions.execute,
574 user_accessible: true,
575 };
576 let address_space = task.process.address_space_arc();
577 let (base, size) = crate::memory::memory_region_registry()
578 .map_region(
579 cap.resource as u64,
580 &address_space,
581 addr_hint,
582 requested_flags,
583 )
584 .map_err(|error| match error {
585 crate::memory::RegionCapError::NotFound => SyscallError::NotFound,
586 crate::memory::RegionCapError::InvalidRegion
587 | crate::memory::RegionCapError::IncompleteRegion
588 | crate::memory::RegionCapError::InvalidAddress => SyscallError::InvalidArgument,
589 crate::memory::RegionCapError::PermissionDenied => SyscallError::PermissionDenied,
590 crate::memory::RegionCapError::OutOfMemory => SyscallError::OutOfMemory,
591 crate::memory::RegionCapError::InconsistentState => SyscallError::IoError,
592 })?;
593
594 let user = crate::memory::UserSliceWrite::new(out_ptr, core::mem::size_of::<u64>())?;
595 user.copy_from(&base.to_ne_bytes());
596 Ok(size)
597}
598
599pub fn sys_mem_region_info(handle: u64, out_ptr: u64) -> Result<u64, SyscallError> {
601 crate::silo::enforce_cap_for_current_task(handle)?;
602 if out_ptr == 0 {
603 return Err(SyscallError::Fault);
604 }
605
606 let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
607 let caps = unsafe { &*task.process.capabilities.get() };
608 let cap = caps
609 .get(crate::capability::CapId::from_raw(handle))
610 .ok_or(SyscallError::BadHandle)?;
611 if cap.resource_type != crate::capability::ResourceType::MemoryRegion {
612 return Err(SyscallError::BadHandle);
613 }
614
615 let info = crate::memory::memory_region_registry()
616 .info(cap.resource as u64)
617 .ok_or(SyscallError::NotFound)?;
618 let abi = MemoryRegionInfoAbi {
619 size: info.size,
620 page_size: info.page_size.bytes(),
621 flags: vma_flags_to_prot(info.flags),
622 _reserved: 0,
623 };
624 let user =
625 crate::memory::UserSliceWrite::new(out_ptr, core::mem::size_of::<MemoryRegionInfoAbi>())?;
626 let bytes = unsafe {
627 core::slice::from_raw_parts(
628 &abi as *const MemoryRegionInfoAbi as *const u8,
629 core::mem::size_of::<MemoryRegionInfoAbi>(),
630 )
631 };
632 user.copy_from(bytes);
633 Ok(0)
634}
635
636pub fn sys_brk(addr: u64) -> Result<u64, SyscallError> {
654 let task = current_task_clone().ok_or(SyscallError::Fault)?;
655
656 let current_brk = {
660 let raw = task.process.brk.load(Ordering::Relaxed);
661 if raw == 0 {
662 task.process.brk.store(BRK_BASE, Ordering::Relaxed);
663 BRK_BASE
664 } else {
665 raw
666 }
667 };
668
669 if addr == 0 {
671 return Ok(current_brk);
672 }
673
674 if addr < BRK_BASE || addr >= USER_SPACE_END {
677 return Ok(current_brk); }
679
680 let old_page_end = page_align_up(current_brk);
685 let new_page_end = page_align_up(addr);
686
687 if new_page_end > old_page_end {
688 let n_pages = ((new_page_end - old_page_end) / 4096) as usize;
690 let vma_flags = VmaFlags {
691 readable: true,
692 writable: true,
693 executable: false,
694 user_accessible: true,
695 };
696 if task
697 .process
698 .address_space_arc()
699 .reserve_region(
700 old_page_end,
701 n_pages,
702 vma_flags,
703 VmaType::Anonymous,
704 crate::memory::address_space::VmaPageSize::Small,
705 )
706 .is_err()
707 {
708 return Ok(current_brk);
710 }
711 log::trace!(
712 "sys_brk: grow {:#x}..{:#x} ({} pages, lazy)",
713 old_page_end,
714 new_page_end,
715 n_pages,
716 );
717 } else if new_page_end < old_page_end {
718 let len = old_page_end - new_page_end;
720 if task
721 .process
722 .address_space_arc()
723 .unmap_range(new_page_end, len)
724 .is_err()
725 {
726 return Ok(current_brk);
727 }
728 log::trace!(
729 "sys_brk: shrink {:#x}..{:#x} (-{} pages)",
730 new_page_end,
731 old_page_end,
732 len / 4096,
733 );
734 }
735 task.process.brk.store(addr, Ordering::Relaxed);
740 Ok(addr)
741}