1use alloc::{collections::BTreeMap, sync::Arc, vec::Vec};
19use core::sync::atomic::{AtomicU32, Ordering};
20
21use spin::Once;
22use x86_64::{
23 registers::control::{Cr3, Cr3Flags},
24 structures::paging::{
25 mapper::TranslateResult, Mapper, OffsetPageTable, Page, PageTable, PageTableFlags,
26 PhysFrame as X86PhysFrame, Size2MiB, Size4KiB, Translate,
27 },
28 PhysAddr, VirtAddr,
29};
30
31use crate::{
32 capability::CapId,
33 memory::{
34 allocate_mapping_cap_id, mapping_index, paging::BuddyFrameAllocator, release_owned_block,
35 resolve_handle, try_register_mapping_identity, unregister_mapping_identity, BlockHandle,
36 MappingRef,
37 },
38 process::task::Pid,
39 sync::SpinLock,
40};
41
42#[derive(Debug, Clone, Copy, PartialEq, Eq)]
44pub struct VmaFlags {
45 pub readable: bool,
46 pub writable: bool,
47 pub executable: bool,
48 pub user_accessible: bool,
49}
50
51impl VmaFlags {
52 pub fn to_page_flags(self) -> PageTableFlags {
54 let mut flags = PageTableFlags::PRESENT;
55 if self.writable {
56 flags |= PageTableFlags::WRITABLE;
57 }
58 if !self.executable {
59 flags |= PageTableFlags::NO_EXECUTE;
60 }
61 if self.user_accessible {
62 flags |= PageTableFlags::USER_ACCESSIBLE;
63 }
64 flags
65 }
66}
67
68#[derive(Debug, Clone, Copy, PartialEq, Eq)]
70pub enum VmaType {
71 Anonymous,
73 Stack,
75 Code,
77 Kernel,
79}
80
81#[derive(Debug, Clone, Copy, PartialEq, Eq)]
83pub enum VmaPageSize {
84 Small,
86 Huge,
88}
89
90impl VmaPageSize {
91 pub fn bytes(self) -> u64 {
93 match self {
94 VmaPageSize::Small => 4096,
95 VmaPageSize::Huge => 2 * 1024 * 1024,
96 }
97 }
98}
99
100#[derive(Debug, Clone)]
102pub struct VirtualMemoryRegion {
103 pub start: u64,
105 pub page_count: usize,
107 pub flags: VmaFlags,
109 pub vma_type: VmaType,
111 pub page_size: VmaPageSize,
113}
114
115#[derive(Debug, Clone, Copy, PartialEq, Eq)]
117pub struct EffectiveMapping {
118 pub start: u64,
120 pub cap_id: CapId,
122 pub handle: BlockHandle,
124 pub flags: PageTableFlags,
126 pub page_size: VmaPageSize,
128}
129
130pub struct AddressSpace {
160 cr3_phys: PhysAddr,
162 l4_table_virt: VirtAddr,
164 is_kernel: bool,
166 regions: SpinLock<BTreeMap<u64, VirtualMemoryRegion>>,
168 effective_mappings: SpinLock<BTreeMap<u64, EffectiveMapping>>,
170 owner_pid: AtomicU32,
172}
173
174unsafe impl Send for AddressSpace {}
177unsafe impl Sync for AddressSpace {}
178
179impl AddressSpace {
180 pub unsafe fn new_kernel() -> Self {
185 let (level_4_frame, _flags) = Cr3::read();
186 let cr3_phys = level_4_frame.start_address();
187 let l4_table_virt = VirtAddr::new(crate::memory::phys_to_virt(cr3_phys.as_u64()));
188
189 log::info!(
190 "Kernel address space initialized: CR3={:#x}",
191 cr3_phys.as_u64()
192 );
193
194 AddressSpace {
195 cr3_phys,
196 l4_table_virt,
197 is_kernel: true,
198 regions: SpinLock::new(BTreeMap::new()),
199 effective_mappings: SpinLock::new(BTreeMap::new()),
200 owner_pid: AtomicU32::new(0),
201 }
202 }
203
204 pub fn new_user() -> Result<Self, &'static str> {
210 let new_l4_phys =
212 crate::sync::with_irqs_disabled(|token| crate::memory::allocate_frame(token))
213 .map_err(|_| "Failed to allocate PML4 frame")?
214 .start_address;
215
216 let new_l4_virt = VirtAddr::new(crate::memory::phys_to_virt(new_l4_phys.as_u64()));
217
218 unsafe {
221 core::ptr::write_bytes(new_l4_virt.as_mut_ptr::<u8>(), 0, 4096);
222 }
223
224 let kernel_l4_phys = crate::memory::paging::kernel_l4_phys();
226 let kernel_l4_virt = VirtAddr::new(crate::memory::phys_to_virt(kernel_l4_phys.as_u64()));
227
228 unsafe {
231 let kernel_l4 = &*(kernel_l4_virt.as_ptr::<PageTable>());
232 let new_l4 = &mut *(new_l4_virt.as_mut_ptr::<PageTable>());
233 for i in 256..512 {
234 new_l4[i] = kernel_l4[i].clone();
235 }
236 }
237
238 {
253 let lapic_phys = crate::arch::x86_64::apic::lapic_phys();
254 if lapic_phys != 0 {
255 let lapic_virt = crate::memory::phys_to_virt(lapic_phys);
256 if lapic_virt < 0xFFFF_8000_0000_0000 {
258 let phys_offset = VirtAddr::new(crate::memory::hhdm_offset());
259 let l4 = unsafe { &mut *new_l4_virt.as_mut_ptr::<PageTable>() };
261 let mut mapper = unsafe { OffsetPageTable::new(l4, phys_offset) };
262 let mut buddy = crate::memory::paging::BuddyFrameAllocator;
263 let mmio_flags = PageTableFlags::PRESENT
264 | PageTableFlags::WRITABLE
265 | PageTableFlags::NO_CACHE;
266 let lapic_page =
267 Page::<Size4KiB>::containing_address(VirtAddr::new(lapic_virt));
268 let lapic_frame =
269 X86PhysFrame::<Size4KiB>::containing_address(PhysAddr::new(lapic_phys));
270 match unsafe { mapper.map_to(lapic_page, lapic_frame, mmio_flags, &mut buddy) }
273 {
274 Ok(flush) => flush.flush(),
275 Err(e) => {
276 crate::serial_println!(
277 "[as] WARN: failed to map LAPIC ({:#x}) in user AS: {:?}",
278 lapic_phys,
279 e
280 );
281 }
282 }
283 }
284 }
285 }
286
287 log::debug!(
288 "User address space created: CR3={:#x} (kernel entries cloned from {:#x})",
289 new_l4_phys.as_u64(),
290 kernel_l4_phys.as_u64()
291 );
292
293 Ok(AddressSpace {
294 cr3_phys: new_l4_phys,
295 l4_table_virt: new_l4_virt,
296 is_kernel: false,
297 regions: SpinLock::new(BTreeMap::new()),
298 effective_mappings: SpinLock::new(BTreeMap::new()),
299 owner_pid: AtomicU32::new(0),
300 })
301 }
302
303 pub fn register_effective_mapping(
305 &self,
306 mapping: EffectiveMapping,
307 ) -> Result<(), &'static str> {
308 let previous_at_start = self.effective_mapping_by_start(mapping.start);
309 if let Some(previous) = previous_at_start {
310 if previous.handle == mapping.handle && previous.cap_id == mapping.cap_id {
311 self.effective_mappings
312 .lock()
313 .insert(mapping.start, mapping);
314 if let Some(pid) = self.owner_pid() {
315 mapping_index().unregister(mapping.cap_id, pid, VirtAddr::new(mapping.start));
316 mapping_index().register(
317 mapping.cap_id,
318 MappingRef {
319 pid,
320 vaddr: VirtAddr::new(mapping.start),
321 page_size: mapping.page_size,
322 },
323 );
324 }
325 return Ok(());
326 }
327 }
328
329 if let Err(error) = try_register_mapping_identity(mapping.handle, mapping.cap_id) {
330 if error != crate::memory::OwnerError::CapAlreadyPresent {
331 log::warn!(
332 "memory: failed to register effective mapping identity cap={} block={:#x}/{} vaddr={:#x}: {:?}",
333 mapping.cap_id.as_u64(),
334 mapping.handle.base.as_u64(),
335 mapping.handle.order,
336 mapping.start,
337 error
338 );
339 return Err("Failed to register effective mapping identity");
340 }
341 }
342
343 let replaced = self
344 .effective_mappings
345 .lock()
346 .insert(mapping.start, mapping);
347 if let Some(previous) = replaced {
348 if let Some(block) = unregister_mapping_identity(previous.handle, previous.cap_id) {
349 release_owned_block(block);
350 }
351 if let Some(pid) = self.owner_pid() {
352 mapping_index().unregister(previous.cap_id, pid, VirtAddr::new(previous.start));
353 }
354 }
355
356 if let Some(pid) = self.owner_pid() {
357 mapping_index().register(
358 mapping.cap_id,
359 MappingRef {
360 pid,
361 vaddr: VirtAddr::new(mapping.start),
362 page_size: mapping.page_size,
363 },
364 );
365 }
366 Ok(())
367 }
368
369 pub fn unregister_effective_mapping(&self, start: u64) -> Option<EffectiveMapping> {
371 let mapping = self.effective_mappings.lock().remove(&start);
372 if let Some(mapping) = mapping {
373 if let Some(block) = unregister_mapping_identity(mapping.handle, mapping.cap_id) {
374 release_owned_block(block);
375 }
376 if let Some(pid) = self.owner_pid() {
377 mapping_index().unregister(mapping.cap_id, pid, VirtAddr::new(mapping.start));
378 }
379 Some(mapping)
380 } else {
381 None
382 }
383 }
384
385 pub fn update_effective_mapping_flags(&self, start: u64, flags: PageTableFlags) -> bool {
387 if let Some(mapping) = self.effective_mappings.lock().get_mut(&start) {
388 mapping.flags = flags;
389 true
390 } else {
391 false
392 }
393 }
394
395 pub fn effective_mapping_by_start(&self, start: u64) -> Option<EffectiveMapping> {
397 self.effective_mappings.lock().get(&start).copied()
398 }
399
400 pub fn unmap_effective_mapping(&self, start: u64) -> Result<(), &'static str> {
402 let mapping = self
403 .effective_mapping_by_start(start)
404 .ok_or("Mapping not found")?;
405 self.unmap_range(start, mapping.page_size.bytes())
406 }
407
408 pub fn effective_mapping_containing(&self, addr: u64) -> Option<EffectiveMapping> {
410 let mappings = self.effective_mappings.lock();
411 if let Some(mapping) = mappings.get(&(addr & !(VmaPageSize::Small.bytes() - 1))) {
412 if mapping.page_size == VmaPageSize::Small {
413 return Some(*mapping);
414 }
415 }
416 mappings
417 .get(&(addr & !(VmaPageSize::Huge.bytes() - 1)))
418 .copied()
419 }
420
421 pub fn set_owner_pid(&self, pid: Pid) {
423 let previous = self.owner_pid.swap(pid, Ordering::Relaxed);
424 let mappings: Vec<EffectiveMapping> = {
425 let guard = self.effective_mappings.lock();
426 guard.values().copied().collect()
427 };
428
429 if previous != 0 && previous != pid {
430 for mapping in mappings.iter().copied() {
431 mapping_index().unregister(mapping.cap_id, previous, VirtAddr::new(mapping.start));
432 }
433 }
434
435 if pid != 0 {
436 for mapping in mappings {
437 mapping_index().register(
438 mapping.cap_id,
439 MappingRef {
440 pid,
441 vaddr: VirtAddr::new(mapping.start),
442 page_size: mapping.page_size,
443 },
444 );
445 }
446 }
447 }
448
449 pub fn owner_pid(&self) -> Option<Pid> {
451 match self.owner_pid.load(Ordering::Relaxed) {
452 0 => None,
453 pid => Some(pid),
454 }
455 }
456
457 pub(crate) unsafe fn mapper(&self) -> OffsetPageTable<'_> {
463 let phys_offset = VirtAddr::new(crate::memory::hhdm_offset());
464 unsafe {
467 OffsetPageTable::new(
468 &mut *self.l4_table_virt.as_mut_ptr::<PageTable>(),
469 phys_offset,
470 )
471 }
472 }
473
474 pub fn reserve_region(
478 &self,
479 start: u64,
480 page_count: usize,
481 flags: VmaFlags,
482 vma_type: VmaType,
483 page_size: VmaPageSize,
484 ) -> Result<(), &'static str> {
485 let page_bytes = page_size.bytes();
486 if page_count == 0 || start % page_bytes != 0 {
487 return Err("Invalid region arguments");
488 }
489 let len = (page_count as u64)
490 .checked_mul(page_bytes)
491 .ok_or("Region length overflow")?;
492 let end = start.checked_add(len).ok_or("Region end overflow")?;
493 const USER_SPACE_END: u64 = 0x0000_8000_0000_0000;
494 if end > USER_SPACE_END {
495 return Err("Region out of user-space range");
496 }
497
498 {
500 let regions = self.regions.lock();
501 if regions.iter().any(|(&vma_start, vma)| {
502 let vma_end = vma_start
503 .saturating_add((vma.page_count as u64).saturating_mul(vma.page_size.bytes()));
504 vma_start < end && vma_end > start
505 }) {
506 return Err("Region overlaps existing mapping");
507 }
508 }
509
510 crate::silo::charge_current_task_memory(len).map_err(|_| "Silo memory quota exceeded")?;
512
513 let mut regions = self.regions.lock();
515 let mut merged = false;
516
517 if let Some((&prev_start, prev_vma)) = regions.range(..start).next_back() {
518 let prev_end = prev_start + (prev_vma.page_count as u64) * prev_vma.page_size.bytes();
519 if prev_end == start
520 && prev_vma.flags == flags
521 && prev_vma.vma_type == vma_type
522 && prev_vma.page_size == page_size
523 {
524 let new_count = prev_vma
525 .page_count
526 .checked_add(page_count)
527 .ok_or("Region page_count overflow")?;
528 let updated_vma = VirtualMemoryRegion {
529 start: prev_start,
530 page_count: new_count,
531 flags,
532 vma_type,
533 page_size,
534 };
535 regions.insert(prev_start, updated_vma);
536 merged = true;
537 }
538 }
539
540 if !merged {
541 let region = VirtualMemoryRegion {
542 start,
543 page_count,
544 flags,
545 vma_type,
546 page_size,
547 };
548 regions.insert(start, region);
549 }
550
551 log::trace!(
552 "Reserved lazy region: {:#x} ({} pages, size={:?})",
553 start,
554 page_count,
555 page_size
556 );
557 Ok(())
558 }
559
560 pub fn handle_fault(&self, fault_addr: u64) -> Result<(), &'static str> {
564 use x86_64::structures::paging::mapper::MapToError;
565
566 let vma = {
568 let regions = self.regions.lock();
569 let mut iter = regions.range(..=fault_addr);
570 let (&start, vma) = iter.next_back().ok_or("No VMA found for address")?;
571 let end = start + (vma.page_count as u64) * vma.page_size.bytes();
572 if fault_addr >= end {
573 return Err("Address outside VMA bounds");
574 }
575 vma.clone()
576 };
577
578 let page_bytes = vma.page_size.bytes();
580 let page_addr = fault_addr & !(page_bytes - 1);
581
582 match vma.vma_type {
584 VmaType::Anonymous | VmaType::Stack | VmaType::Code => {}
585 _ => return Err("VMA type does not support demand paging"),
586 }
587
588 if self.translate(VirtAddr::new(page_addr)).is_some() {
590 return Ok(());
591 }
592
593 let mut frame_allocator = crate::memory::paging::BuddyFrameAllocator;
604 let order = match vma.page_size {
605 VmaPageSize::Small => 0,
606 VmaPageSize::Huge => 9,
607 };
608
609 let frame = crate::sync::with_irqs_disabled(|token| {
610 if order == 0 {
611 crate::memory::allocate_frame(token)
612 } else {
613 let f = crate::memory::allocate_phys_contiguous(token, order)?;
614 unsafe {
617 core::ptr::write_bytes(
618 crate::memory::phys_to_virt(f.start_address.as_u64()) as *mut u8,
619 0,
620 page_bytes as usize,
621 );
622 }
623 Ok(f)
624 }
625 })
626 .map_err(|_| "OOM during demand paging")?;
627
628 let mut page_flags = vma.flags.to_page_flags();
629
630 unsafe {
632 let mut mapper = self.mapper();
633 match vma.page_size {
634 VmaPageSize::Small => {
635 let page =
636 Page::<Size4KiB>::from_start_address(VirtAddr::new(page_addr)).unwrap();
637 let phys_frame =
638 x86_64::structures::paging::PhysFrame::<Size4KiB>::containing_address(
639 frame.start_address,
640 );
641 match mapper.map_to(page, phys_frame, page_flags, &mut frame_allocator) {
642 Ok(flush) => {
643 flush.flush();
644 }
645 Err(MapToError::PageAlreadyMapped(_)) => {
646 crate::sync::with_irqs_disabled(|token| {
647 crate::memory::free_phys_contiguous(token, frame, order);
648 });
649 return Ok(());
650 }
651 Err(_) => {
652 crate::sync::with_irqs_disabled(|token| {
653 crate::memory::free_phys_contiguous(token, frame, order);
654 });
655 return Err("Failed to map demand page (4K)");
656 }
657 }
658 }
659 VmaPageSize::Huge => {
660 let page =
661 Page::<Size2MiB>::from_start_address(VirtAddr::new(page_addr)).unwrap();
662 let phys_frame =
663 x86_64::structures::paging::PhysFrame::<Size2MiB>::containing_address(
664 frame.start_address,
665 );
666 page_flags |= PageTableFlags::HUGE_PAGE;
667 match mapper.map_to(page, phys_frame, page_flags, &mut frame_allocator) {
668 Ok(flush) => {
669 flush.flush();
670 }
671 Err(MapToError::PageAlreadyMapped(_)) => {
672 crate::sync::with_irqs_disabled(|token| {
673 crate::memory::free_phys_contiguous(token, frame, order);
674 });
675 return Ok(());
676 }
677 Err(_) => {
678 crate::sync::with_irqs_disabled(|token| {
679 crate::memory::free_phys_contiguous(token, frame, order);
680 });
681 return Err("Failed to map demand page (2M)");
682 }
683 }
684 }
685 }
686 }
687
688 if self
689 .register_effective_mapping(EffectiveMapping {
690 start: page_addr,
691 cap_id: allocate_mapping_cap_id(),
692 handle: resolve_handle(frame.start_address),
693 flags: page_flags,
694 page_size: vma.page_size,
695 })
696 .is_err()
697 {
698 unsafe {
699 let mut mapper = self.mapper();
700 match vma.page_size {
701 VmaPageSize::Small => {
702 let page =
703 Page::<Size4KiB>::from_start_address(VirtAddr::new(page_addr)).unwrap();
704 if let Ok((_, flush)) = mapper.unmap(page) {
705 flush.flush();
706 }
707 }
708 VmaPageSize::Huge => {
709 let page =
710 Page::<Size2MiB>::from_start_address(VirtAddr::new(page_addr)).unwrap();
711 if let Ok((_, flush)) = mapper.unmap(page) {
712 flush.flush();
713 }
714 }
715 }
716 }
717 crate::sync::with_irqs_disabled(|token| {
718 crate::memory::free_phys_contiguous(token, frame, order);
719 });
720 return Err("Failed to track demand page mapping");
721 }
722
723 if order != 0 {
734 crate::memory::cow::handle_init_ref(resolve_handle(frame.start_address));
735 }
736
737 Ok(())
738 }
739
740 pub fn map_region(
745 &self,
746 start: u64,
747 page_count: usize,
748 flags: VmaFlags,
749 vma_type: VmaType,
750 page_size: VmaPageSize,
751 ) -> Result<(), &'static str> {
752 let page_bytes = page_size.bytes();
753 if page_count == 0 || start % page_bytes != 0 {
754 return Err("Invalid region arguments");
755 }
756 let len = (page_count as u64)
757 .checked_mul(page_bytes)
758 .ok_or("Region length overflow")?;
759 let end = start.checked_add(len).ok_or("Region end overflow")?;
760 const USER_SPACE_END: u64 = 0x0000_8000_0000_0000;
761 if end > USER_SPACE_END {
762 return Err("Region out of user-space range");
763 }
764
765 {
767 let regions = self.regions.lock();
768 if regions.iter().any(|(&vma_start, vma)| {
769 let vma_end = vma_start
770 .saturating_add((vma.page_count as u64).saturating_mul(vma.page_size.bytes()));
771 vma_start < end && vma_end > start
772 }) {
773 return Err("Region overlaps existing mapping");
774 }
775 }
776
777 crate::silo::charge_current_task_memory(len).map_err(|_| "Silo memory quota exceeded")?;
779
780 let page_flags = flags.to_page_flags();
781 let mut frame_allocator = BuddyFrameAllocator;
782
783 let mut mapper = unsafe { self.mapper() };
785 let mut mapped_pages = 0usize;
786
787 for i in 0..page_count {
788 let page_addr = start
789 .checked_add((i as u64).saturating_mul(page_bytes))
790 .ok_or("Page address overflow")?;
791
792 let order = match page_size {
797 VmaPageSize::Small => 0,
798 VmaPageSize::Huge => 9,
799 };
800
801 let frame = crate::sync::with_irqs_disabled(|token| {
802 if order == 0 {
803 crate::memory::allocate_frame(token)
804 } else {
805 let f = crate::memory::allocate_phys_contiguous(token, order)?;
806 unsafe {
807 let virt = crate::memory::phys_to_virt(f.start_address.as_u64());
808 core::ptr::write_bytes(virt as *mut u8, 0, page_bytes as usize);
809 }
810 Ok(f)
811 }
812 })
813 .map_err(|_| "Failed to allocate frame")?;
814
815 let map_ok = match page_size {
817 VmaPageSize::Small => {
818 use x86_64::structures::paging::Size4KiB;
819 let page = Page::<Size4KiB>::from_start_address(VirtAddr::new(page_addr))
820 .map_err(|_| "Map 4K: invalid page address")?;
821 let phys_frame =
822 x86_64::structures::paging::PhysFrame::<Size4KiB>::containing_address(
823 frame.start_address,
824 );
825 unsafe {
826 mapper
827 .map_to(page, phys_frame, page_flags, &mut frame_allocator)
828 .map(|flush| flush.flush())
829 .is_ok()
830 }
831 }
832 VmaPageSize::Huge => {
833 use x86_64::structures::paging::Size2MiB;
834 let page = Page::<Size2MiB>::from_start_address(VirtAddr::new(page_addr))
835 .map_err(|_| "Map 2M: invalid page address")?;
836 let phys_frame =
837 x86_64::structures::paging::PhysFrame::<Size2MiB>::containing_address(
838 frame.start_address,
839 );
840 let mut huge_flags = page_flags;
841 huge_flags |= PageTableFlags::HUGE_PAGE;
842 unsafe {
843 mapper
844 .map_to(page, phys_frame, huge_flags, &mut frame_allocator)
845 .map(|flush| flush.flush())
846 .is_ok()
847 }
848 }
849 };
850
851 if !map_ok {
852 log::error!(
853 "map_region: map_to failed at page {} vaddr={:#x} size={:?}",
854 i,
855 page_addr,
856 page_size
857 );
858 crate::sync::with_irqs_disabled(|token| {
860 crate::memory::free_phys_contiguous(token, frame, order);
861 });
862
863 for j in (0..mapped_pages).rev() {
865 let rb_addr = start + (j as u64) * page_bytes;
866 match page_size {
867 VmaPageSize::Small => {
868 use x86_64::structures::paging::Size4KiB;
869 let rb_page =
870 Page::<Size4KiB>::from_start_address(VirtAddr::new(rb_addr))
871 .map_err(|_| "Rollback: invalid 4K page address")?;
872 if let Ok((_, rb_flush)) = mapper.unmap(rb_page) {
873 rb_flush.flush();
874 let _ = self.unregister_effective_mapping(rb_addr);
875 }
876 }
877 VmaPageSize::Huge => {
878 use x86_64::structures::paging::Size2MiB;
879 let rb_page =
880 Page::<Size2MiB>::from_start_address(VirtAddr::new(rb_addr))
881 .map_err(|_| "Rollback: invalid 2M page address")?;
882 if let Ok((_, rb_flush)) = mapper.unmap(rb_page) {
883 rb_flush.flush();
884 let _ = self.unregister_effective_mapping(rb_addr);
885 }
886 }
887 }
888 }
889
890 crate::silo::release_current_task_memory(len);
891 return Err("Failed to map page");
892 }
893
894 let effective_flags = match page_size {
896 VmaPageSize::Small => page_flags,
897 VmaPageSize::Huge => page_flags | PageTableFlags::HUGE_PAGE,
898 };
899 if self
900 .register_effective_mapping(EffectiveMapping {
901 start: page_addr,
902 cap_id: allocate_mapping_cap_id(),
903 handle: resolve_handle(frame.start_address),
904 flags: effective_flags,
905 page_size,
906 })
907 .is_err()
908 {
909 match page_size {
910 VmaPageSize::Small => {
911 use x86_64::structures::paging::Size4KiB;
912 let page = Page::<Size4KiB>::from_start_address(VirtAddr::new(page_addr))
913 .map_err(|_| "Rollback: invalid 4K page address")?;
914 if let Ok((_, flush)) = mapper.unmap(page) {
915 flush.flush();
916 }
917 }
918 VmaPageSize::Huge => {
919 use x86_64::structures::paging::Size2MiB;
920 let page = Page::<Size2MiB>::from_start_address(VirtAddr::new(page_addr))
921 .map_err(|_| "Rollback: invalid 2M page address")?;
922 if let Ok((_, flush)) = mapper.unmap(page) {
923 flush.flush();
924 }
925 }
926 }
927 crate::sync::with_irqs_disabled(|token| {
928 crate::memory::free_phys_contiguous(token, frame, order);
929 });
930 for j in (0..mapped_pages).rev() {
931 let rb_addr = start + (j as u64) * page_bytes;
932 match page_size {
933 VmaPageSize::Small => {
934 use x86_64::structures::paging::Size4KiB;
935 let rb_page =
936 Page::<Size4KiB>::from_start_address(VirtAddr::new(rb_addr))
937 .map_err(|_| "Rollback: invalid 4K page address")?;
938 if let Ok((_, rb_flush)) = mapper.unmap(rb_page) {
939 rb_flush.flush();
940 let _ = self.unregister_effective_mapping(rb_addr);
941 }
942 }
943 VmaPageSize::Huge => {
944 use x86_64::structures::paging::Size2MiB;
945 let rb_page =
946 Page::<Size2MiB>::from_start_address(VirtAddr::new(rb_addr))
947 .map_err(|_| "Rollback: invalid 2M page address")?;
948 if let Ok((_, rb_flush)) = mapper.unmap(rb_page) {
949 rb_flush.flush();
950 let _ = self.unregister_effective_mapping(rb_addr);
951 }
952 }
953 }
954 }
955 crate::silo::release_current_task_memory(len);
956 return Err("Failed to track mapped region page");
957 }
958
959 mapped_pages += 1;
960 }
961
962 let mut regions = self.regions.lock();
964 let region = VirtualMemoryRegion {
965 start,
966 page_count,
967 flags,
968 vma_type,
969 page_size,
970 };
971 regions.insert(start, region);
972
973 let end = start + (page_count as u64) * page_bytes;
974 crate::trace_mem!(
975 crate::trace::category::MEM_MAP,
976 crate::trace::TraceKind::MemMap,
977 page_size.bytes(),
978 crate::trace::TraceTaskCtx {
979 task_id: 0,
980 pid: 0,
981 tid: 0,
982 cr3: self.cr3_phys.as_u64(),
983 },
984 0,
985 start,
986 end,
987 page_count as u64
988 );
989
990 Ok(())
991 }
992
993 pub fn map_shared_frames(
995 &self,
996 start: u64,
997 frame_phys_addrs: &[u64],
998 flags: VmaFlags,
999 vma_type: VmaType,
1000 ) -> Result<(), &'static str> {
1001 self.map_shared_frames_with_cap_ids(start, frame_phys_addrs, None, flags, vma_type)
1002 }
1003
1004 pub fn map_shared_handles_with_cap_ids(
1006 &self,
1007 start: u64,
1008 handles: &[BlockHandle],
1009 mapping_cap_ids: Option<&[CapId]>,
1010 flags: VmaFlags,
1011 vma_type: VmaType,
1012 page_size: VmaPageSize,
1013 ) -> Result<(), &'static str> {
1014 let page_count = handles.len();
1015 let page_bytes = page_size.bytes();
1016 if page_count == 0 || start % page_bytes != 0 {
1017 return Err("Invalid shared region arguments");
1018 }
1019 if mapping_cap_ids.is_some_and(|cap_ids| cap_ids.len() != page_count) {
1020 return Err("Shared mapping identity count mismatch");
1021 }
1022 let len = (page_count as u64)
1023 .checked_mul(page_bytes)
1024 .ok_or("Shared region length overflow")?;
1025 let end = start.checked_add(len).ok_or("Shared region end overflow")?;
1026 const USER_SPACE_END: u64 = 0x0000_8000_0000_0000;
1027 if end > USER_SPACE_END {
1028 return Err("Shared region out of user-space range");
1029 }
1030
1031 {
1032 let regions = self.regions.lock();
1033 if regions.iter().any(|(&vma_start, vma)| {
1034 let vma_end = vma_start
1035 .saturating_add((vma.page_count as u64).saturating_mul(vma.page_size.bytes()));
1036 vma_start < end && vma_end > start
1037 }) {
1038 return Err("Shared region overlaps existing mapping");
1039 }
1040 }
1041
1042 let mut page_flags = flags.to_page_flags();
1043 if page_size == VmaPageSize::Huge {
1044 page_flags |= PageTableFlags::HUGE_PAGE;
1045 }
1046 let mut frame_allocator = BuddyFrameAllocator;
1047 let mut mapper = unsafe { self.mapper() };
1048 let mut mapped_pages = 0usize;
1049
1050 for (index, handle) in handles.iter().copied().enumerate() {
1051 let page_addr = start
1052 .checked_add((index as u64) * page_bytes)
1053 .ok_or("Shared page address overflow")?;
1054
1055 let map_ok = match page_size {
1056 VmaPageSize::Small => {
1057 let page = Page::<Size4KiB>::from_start_address(VirtAddr::new(page_addr))
1058 .map_err(|_| "Map shared: invalid 4K page address")?;
1059 let frame = X86PhysFrame::<Size4KiB>::containing_address(handle.base);
1060 unsafe {
1061 mapper
1062 .map_to(page, frame, page_flags, &mut frame_allocator)
1063 .map(|flush| flush.flush())
1064 .is_ok()
1065 }
1066 }
1067 VmaPageSize::Huge => {
1068 let page = Page::<Size2MiB>::from_start_address(VirtAddr::new(page_addr))
1069 .map_err(|_| "Map shared: invalid 2M page address")?;
1070 let frame = X86PhysFrame::<Size2MiB>::containing_address(handle.base);
1071 unsafe {
1072 mapper
1073 .map_to(page, frame, page_flags, &mut frame_allocator)
1074 .map(|flush| flush.flush())
1075 .is_ok()
1076 }
1077 }
1078 };
1079
1080 if !map_ok {
1081 for rollback in (0..mapped_pages).rev() {
1082 let rb_addr = start + (rollback as u64) * page_bytes;
1083 match page_size {
1084 VmaPageSize::Small => {
1085 if let Ok(rb_page) =
1086 Page::<Size4KiB>::from_start_address(VirtAddr::new(rb_addr))
1087 {
1088 if let Ok((_, rb_flush)) = mapper.unmap(rb_page) {
1089 rb_flush.flush();
1090 let _ = self.unregister_effective_mapping(rb_addr);
1091 }
1092 }
1093 }
1094 VmaPageSize::Huge => {
1095 if let Ok(rb_page) =
1096 Page::<Size2MiB>::from_start_address(VirtAddr::new(rb_addr))
1097 {
1098 if let Ok((_, rb_flush)) = mapper.unmap(rb_page) {
1099 rb_flush.flush();
1100 let _ = self.unregister_effective_mapping(rb_addr);
1101 }
1102 }
1103 }
1104 }
1105 }
1106 return Err("Failed to map shared page");
1107 }
1108
1109 if self
1110 .register_effective_mapping(EffectiveMapping {
1111 start: page_addr,
1112 cap_id: mapping_cap_ids
1113 .and_then(|cap_ids| cap_ids.get(index).copied())
1114 .unwrap_or_else(allocate_mapping_cap_id),
1115 handle,
1116 flags: page_flags,
1117 page_size,
1118 })
1119 .is_err()
1120 {
1121 match page_size {
1122 VmaPageSize::Small => {
1123 if let Ok(page) =
1124 Page::<Size4KiB>::from_start_address(VirtAddr::new(page_addr))
1125 {
1126 if let Ok((_, flush)) = mapper.unmap(page) {
1127 flush.flush();
1128 }
1129 }
1130 }
1131 VmaPageSize::Huge => {
1132 if let Ok(page) =
1133 Page::<Size2MiB>::from_start_address(VirtAddr::new(page_addr))
1134 {
1135 if let Ok((_, flush)) = mapper.unmap(page) {
1136 flush.flush();
1137 }
1138 }
1139 }
1140 }
1141 for rollback in (0..mapped_pages).rev() {
1142 let rb_addr = start + (rollback as u64) * page_bytes;
1143 match page_size {
1144 VmaPageSize::Small => {
1145 if let Ok(rb_page) =
1146 Page::<Size4KiB>::from_start_address(VirtAddr::new(rb_addr))
1147 {
1148 if let Ok((_, rb_flush)) = mapper.unmap(rb_page) {
1149 rb_flush.flush();
1150 let _ = self.unregister_effective_mapping(rb_addr);
1151 }
1152 }
1153 }
1154 VmaPageSize::Huge => {
1155 if let Ok(rb_page) =
1156 Page::<Size2MiB>::from_start_address(VirtAddr::new(rb_addr))
1157 {
1158 if let Ok((_, rb_flush)) = mapper.unmap(rb_page) {
1159 rb_flush.flush();
1160 let _ = self.unregister_effective_mapping(rb_addr);
1161 }
1162 }
1163 }
1164 }
1165 }
1166 return Err("Failed to track shared mapping");
1167 }
1168 mapped_pages += 1;
1169 }
1170
1171 self.regions.lock().insert(
1172 start,
1173 VirtualMemoryRegion {
1174 start,
1175 page_count,
1176 flags,
1177 vma_type,
1178 page_size,
1179 },
1180 );
1181 Ok(())
1182 }
1183
1184 pub fn map_shared_frames_with_cap_ids(
1186 &self,
1187 start: u64,
1188 frame_phys_addrs: &[u64],
1189 mapping_cap_ids: Option<&[CapId]>,
1190 flags: VmaFlags,
1191 vma_type: VmaType,
1192 ) -> Result<(), &'static str> {
1193 let handles = frame_phys_addrs
1194 .iter()
1195 .copied()
1196 .map(|phys_addr| resolve_handle(PhysAddr::new(phys_addr)))
1197 .collect::<Vec<_>>();
1198 self.map_shared_handles_with_cap_ids(
1199 start,
1200 &handles,
1201 mapping_cap_ids,
1202 flags,
1203 vma_type,
1204 VmaPageSize::Small,
1205 )
1206 }
1207
1208 pub fn unmap_region(
1210 &self,
1211 start: u64,
1212 page_count: usize,
1213 page_size: VmaPageSize,
1214 ) -> Result<(), &'static str> {
1215 let page_bytes = page_size.bytes();
1216 let mut mapper = unsafe { self.mapper() };
1218
1219 for i in 0..page_count {
1220 let page_addr = start + (i as u64) * page_bytes;
1221
1222 let _frame_addr = match page_size {
1223 VmaPageSize::Small => {
1224 use x86_64::structures::paging::Size4KiB;
1225 let page = Page::<Size4KiB>::from_start_address(VirtAddr::new(page_addr))
1226 .map_err(|_| "Failed to unmap: invalid 4K page address")?;
1227 let (frame, flush) =
1228 mapper.unmap(page).map_err(|_| "Failed to unmap 4K page")?;
1229 flush.flush();
1230 frame.start_address()
1231 }
1232 VmaPageSize::Huge => {
1233 use x86_64::structures::paging::Size2MiB;
1234 let page = Page::<Size2MiB>::from_start_address(VirtAddr::new(page_addr))
1235 .map_err(|_| "Failed to unmap: invalid 2M page address")?;
1236 let (frame, flush) =
1237 mapper.unmap(page).map_err(|_| "Failed to unmap 2M page")?;
1238 flush.flush();
1239 frame.start_address()
1240 }
1241 };
1242
1243 let _ = self.unregister_effective_mapping(page_addr);
1245 }
1246
1247 self.regions.lock().remove(&start);
1249
1250 log::trace!(
1251 "Unmapped region: {:#x}..{:#x} ({} pages, size={:?})",
1252 start,
1253 start + (page_count as u64) * page_bytes,
1254 page_count,
1255 page_size
1256 );
1257
1258 let end = start + (page_count as u64) * page_bytes;
1259 crate::trace_mem!(
1260 crate::trace::category::MEM_UNMAP,
1261 crate::trace::TraceKind::MemUnmap,
1262 page_size.bytes(),
1263 crate::trace::TraceTaskCtx {
1264 task_id: 0,
1265 pid: 0,
1266 tid: 0,
1267 cr3: self.cr3_phys.as_u64(),
1268 },
1269 0,
1270 start,
1271 end,
1272 page_count as u64
1273 );
1274
1275 let released = (page_count as u64).saturating_mul(page_bytes);
1276 crate::silo::release_current_task_memory(released);
1277
1278 Ok(())
1279 }
1280
1281 pub fn find_free_vma_range(
1283 &self,
1284 hint: u64,
1285 n_pages: usize,
1286 page_size: VmaPageSize,
1287 ) -> Option<u64> {
1288 if n_pages == 0 {
1289 return None;
1290 }
1291 let page_bytes = page_size.bytes();
1292 let length = (n_pages as u64).checked_mul(page_bytes)?;
1293 let upper_limit: u64 = 0x0000_8000_0000_0000; let mut candidate = (hint.saturating_add(page_bytes - 1)) & !(page_bytes - 1);
1297 if candidate == 0 {
1298 candidate = page_bytes;
1299 }
1300
1301 let regions = self.regions.lock();
1302 for (&vma_start, vma) in regions.iter() {
1303 let vma_end = vma_start + vma.page_count as u64 * vma.page_size.bytes();
1304
1305 if candidate.saturating_add(length) <= vma_start {
1307 break;
1308 }
1309
1310 if vma_end > candidate {
1312 candidate = (vma_end.saturating_add(page_bytes - 1)) & !(page_bytes - 1);
1313 }
1314 }
1315
1316 if candidate.checked_add(length)? <= upper_limit {
1318 Some(candidate)
1319 } else {
1320 None
1321 }
1322 }
1323
1324 pub fn has_mapping_in_range(&self, addr: u64, len: u64) -> bool {
1326 let end = match addr.checked_add(len) {
1327 Some(v) => v,
1328 None => return true,
1329 };
1330 let regions = self.regions.lock();
1331 regions.iter().any(|(&vma_start, vma)| {
1332 let vma_end = vma_start
1333 .saturating_add((vma.page_count as u64).saturating_mul(vma.page_size.bytes()));
1334 vma_start < end && vma_end > addr
1335 })
1336 }
1337
1338 pub fn region_by_start(&self, start: u64) -> Option<VirtualMemoryRegion> {
1340 let regions = self.regions.lock();
1341 regions.get(&start).cloned()
1342 }
1343
1344 pub fn any_mapped_in_range(
1346 &self,
1347 addr: u64,
1348 len: u64,
1349 page_size: VmaPageSize,
1350 ) -> Result<bool, &'static str> {
1351 if len == 0 {
1352 return Ok(false);
1353 }
1354 let end = addr
1355 .checked_add(len)
1356 .ok_or("any_mapped_in_range: address overflow")?;
1357 let step = page_size.bytes();
1358 let mut cur = addr;
1359 while cur < end {
1360 if self.translate(VirtAddr::new(cur)).is_some() {
1361 return Ok(true);
1362 }
1363 cur = cur
1364 .checked_add(step)
1365 .ok_or("any_mapped_in_range: loop overflow")?;
1366 }
1367 Ok(false)
1368 }
1369
1370 pub fn protect_range(&self, addr: u64, len: u64, flags: VmaFlags) -> Result<(), &'static str> {
1372 if len == 0 {
1373 return Ok(());
1374 }
1375 let end = addr
1376 .checked_add(len)
1377 .ok_or("protect_range: address overflow")?;
1378 let mut cursor = addr;
1379
1380 {
1381 let regions = self.regions.lock();
1382 for (&vma_start, vma) in regions.iter() {
1383 let vma_end = vma_start + vma.page_count as u64 * vma.page_size.bytes();
1384 if vma_start >= end || vma_end <= addr {
1385 continue;
1386 }
1387 if vma.page_size == VmaPageSize::Huge {
1388 let range_start = core::cmp::max(vma_start, addr);
1389 let range_end = core::cmp::min(vma_end, end);
1390 if range_start % vma.page_size.bytes() != 0
1391 || range_end % vma.page_size.bytes() != 0
1392 {
1393 return Err(
1394 "protect_range: partial mprotect of 2MiB pages is not supported",
1395 );
1396 }
1397 }
1398 }
1399 }
1400
1401 let mut touched = false;
1402 while cursor < end {
1403 let region_info = {
1404 let regions = self.regions.lock();
1405 regions
1406 .iter()
1407 .find(|(&vma_start, vma)| {
1408 let vma_end = vma_start + vma.page_count as u64 * vma.page_size.bytes();
1409 vma_start < end && vma_end > cursor
1410 })
1411 .map(|(&k, v)| (k, v.clone()))
1412 };
1413
1414 let Some((vma_start, vma)) = region_info else {
1415 break;
1416 };
1417 touched = true;
1418
1419 let vma_end = vma_start + vma.page_count as u64 * vma.page_size.bytes();
1420 let range_start = core::cmp::max(vma_start, cursor);
1421 let range_end = core::cmp::min(vma_end, end);
1422 let page_bytes = vma.page_size.bytes();
1423 let new_pt_flags = flags.to_page_flags();
1424
1425 let mut mapper = unsafe { self.mapper() };
1426 let mut page_addr = range_start;
1427 while page_addr < range_end {
1428 if mapper.translate_addr(VirtAddr::new(page_addr)).is_none() {
1429 page_addr += page_bytes;
1430 continue;
1431 }
1432 unsafe {
1433 match vma.page_size {
1434 VmaPageSize::Small => {
1435 let page =
1436 Page::<Size4KiB>::from_start_address(VirtAddr::new(page_addr))
1437 .map_err(|_| "protect_range: invalid 4K page address")?;
1438 mapper
1439 .update_flags(page, new_pt_flags)
1440 .map(|f| f.ignore())
1441 .map_err(|_| "protect_range: update 4K flags failed")?;
1442 let _ = self.update_effective_mapping_flags(page_addr, new_pt_flags);
1443 }
1444 VmaPageSize::Huge => {
1445 let mut huge_flags = new_pt_flags;
1446 huge_flags |= PageTableFlags::HUGE_PAGE;
1447 let page =
1448 Page::<Size2MiB>::from_start_address(VirtAddr::new(page_addr))
1449 .map_err(|_| "protect_range: invalid 2M page address")?;
1450 mapper
1451 .update_flags(page, huge_flags)
1452 .map(|f| f.ignore())
1453 .map_err(|_| "protect_range: update 2M flags failed")?;
1454 let _ = self.update_effective_mapping_flags(page_addr, huge_flags);
1455 }
1456 }
1457 }
1458 page_addr += page_bytes;
1459 }
1460
1461 {
1462 let mut regions = self.regions.lock();
1463 regions.remove(&vma_start);
1464
1465 if range_start > vma_start {
1466 let leading_pages = ((range_start - vma_start) / page_bytes) as usize;
1467 regions.insert(
1468 vma_start,
1469 VirtualMemoryRegion {
1470 start: vma_start,
1471 page_count: leading_pages,
1472 flags: vma.flags,
1473 vma_type: vma.vma_type,
1474 page_size: vma.page_size,
1475 },
1476 );
1477 }
1478
1479 let middle_pages = ((range_end - range_start) / page_bytes) as usize;
1480 if middle_pages > 0 {
1481 regions.insert(
1482 range_start,
1483 VirtualMemoryRegion {
1484 start: range_start,
1485 page_count: middle_pages,
1486 flags,
1487 vma_type: vma.vma_type,
1488 page_size: vma.page_size,
1489 },
1490 );
1491 }
1492
1493 if range_end < vma_end {
1494 let trailing_pages = ((vma_end - range_end) / page_bytes) as usize;
1495 regions.insert(
1496 range_end,
1497 VirtualMemoryRegion {
1498 start: range_end,
1499 page_count: trailing_pages,
1500 flags: vma.flags,
1501 vma_type: vma.vma_type,
1502 page_size: vma.page_size,
1503 },
1504 );
1505 }
1506 }
1507
1508 cursor = range_end;
1509 }
1510
1511 if !touched {
1512 return Err("protect_range: no mapped region in range");
1513 }
1514 Ok(())
1515 }
1516
1517 pub fn unmap_range(&self, addr: u64, len: u64) -> Result<(), &'static str> {
1519 if len == 0 {
1520 return Ok(());
1521 }
1522 let end = addr
1523 .checked_add(len)
1524 .ok_or("unmap_range: address overflow")?;
1525
1526 {
1529 let regions = self.regions.lock();
1530 for (&vma_start, vma) in regions.iter() {
1531 let vma_end = vma_start + vma.page_count as u64 * vma.page_size.bytes();
1532 if vma_start >= end || vma_end <= addr {
1533 continue;
1534 }
1535 if vma.page_size == VmaPageSize::Huge {
1536 let range_start = core::cmp::max(vma_start, addr);
1537 let range_end = core::cmp::min(vma_end, end);
1538 if range_start % vma.page_size.bytes() != 0
1539 || range_end % vma.page_size.bytes() != 0
1540 {
1541 return Err("unmap_range: partial unmap of 2MiB pages is not supported");
1542 }
1543 }
1544 }
1545 }
1546
1547 let mut released_bytes = 0u64;
1549 loop {
1550 let region_info = {
1552 let regions = self.regions.lock();
1553 regions
1554 .iter()
1555 .find(|(&vma_start, vma)| {
1556 let vma_end = vma_start + vma.page_count as u64 * vma.page_size.bytes();
1557 vma_start < end && vma_end > addr
1558 })
1559 .map(|(&k, v)| (k, v.clone()))
1560 };
1561
1562 let Some((vma_start, vma)) = region_info else {
1563 break; };
1565
1566 let vma_end = vma_start + vma.page_count as u64 * vma.page_size.bytes();
1567 let range_start = core::cmp::max(vma_start, addr);
1568 let range_end = core::cmp::min(vma_end, end);
1569 released_bytes = released_bytes.saturating_add(range_end.saturating_sub(range_start));
1570
1571 let mut mapper = unsafe { self.mapper() };
1574 let mut page_addr = range_start;
1575 let page_bytes = vma.page_size.bytes();
1576 while page_addr < range_end {
1577 if mapper.translate_addr(VirtAddr::new(page_addr)).is_none() {
1580 page_addr += page_bytes;
1581 continue;
1582 }
1583
1584 let _frame_addr = match vma.page_size {
1585 VmaPageSize::Small => {
1586 use x86_64::structures::paging::Size4KiB;
1587 let page = Page::<Size4KiB>::from_start_address(VirtAddr::new(page_addr))
1588 .map_err(|_| "unmap_range: invalid 4K page address")?;
1589 let (frame, flush) = mapper
1590 .unmap(page)
1591 .map_err(|_| "unmap_range: unmap 4K failed")?;
1592 flush.flush();
1593 frame.start_address()
1594 }
1595 VmaPageSize::Huge => {
1596 use x86_64::structures::paging::Size2MiB;
1597 let page = Page::<Size2MiB>::from_start_address(VirtAddr::new(page_addr))
1598 .map_err(|_| "unmap_range: invalid 2M page address")?;
1599 let (frame, flush) = mapper
1600 .unmap(page)
1601 .map_err(|_| "unmap_range: unmap 2M failed")?;
1602 flush.flush();
1603 frame.start_address()
1604 }
1605 };
1606
1607 let _ = self.unregister_effective_mapping(page_addr);
1608 page_addr += page_bytes;
1609 }
1610
1611 {
1613 let mut regions = self.regions.lock();
1614 regions.remove(&vma_start);
1615
1616 if range_start > vma_start {
1617 let leading_pages =
1618 ((range_start - vma_start) / vma.page_size.bytes()) as usize;
1619 regions.insert(
1620 vma_start,
1621 VirtualMemoryRegion {
1622 start: vma_start,
1623 page_count: leading_pages,
1624 flags: vma.flags,
1625 vma_type: vma.vma_type,
1626 page_size: vma.page_size,
1627 },
1628 );
1629 }
1630
1631 if range_end < vma_end {
1632 let trailing_pages = ((vma_end - range_end) / vma.page_size.bytes()) as usize;
1633 regions.insert(
1634 range_end,
1635 VirtualMemoryRegion {
1636 start: range_end,
1637 page_count: trailing_pages,
1638 flags: vma.flags,
1639 vma_type: vma.vma_type,
1640 page_size: vma.page_size,
1641 },
1642 );
1643 }
1644 }
1645 }
1646
1647 crate::silo::release_current_task_memory(released_bytes);
1648 Ok(())
1649 }
1650
1651 pub fn translate(&self, vaddr: VirtAddr) -> Option<PhysAddr> {
1653 let mapper = unsafe { self.mapper() };
1655 mapper.translate_addr(vaddr)
1656 }
1657
1658 pub fn translate_to_handle(&self, vaddr: VirtAddr) -> Option<(BlockHandle, PageTableFlags)> {
1660 let mapper = unsafe { self.mapper() };
1662 let translated = mapper.translate(vaddr);
1663 match translated {
1664 TranslateResult::Mapped { frame, flags, .. } => {
1665 Some((resolve_handle(frame.start_address()), flags))
1666 }
1667 TranslateResult::NotMapped | TranslateResult::InvalidFrameAddress(_) => None,
1668 }
1669 }
1670
1671 pub fn cr3(&self) -> PhysAddr {
1673 self.cr3_phys
1674 }
1675
1676 pub unsafe fn switch_to(&self) {
1685 let (current_frame, _) = Cr3::read();
1686 if current_frame.start_address() == self.cr3_phys {
1687 return; }
1689
1690 unsafe {
1693 let frame =
1694 X86PhysFrame::from_start_address(self.cr3_phys).expect("CR3 address not aligned");
1695 crate::e9_println!("C");
1696 Cr3::write(frame, Cr3Flags::empty());
1697 crate::e9_println!("c");
1698 }
1699 }
1700
1701 pub fn is_kernel(&self) -> bool {
1703 self.is_kernel
1704 }
1705
1706 pub fn has_user_mappings(&self) -> bool {
1708 if self.is_kernel {
1709 return false;
1710 }
1711 let regions = self.regions.lock();
1712 regions.values().any(|vma| vma.vma_type != VmaType::Kernel)
1714 }
1715
1716 fn teardown_effective_mapping_for_drop(&self, mapping: EffectiveMapping) {
1717 unsafe {
1720 let mut mapper = self.mapper();
1721 if mapper
1722 .translate_addr(VirtAddr::new(mapping.start))
1723 .is_some()
1724 {
1725 match mapping.page_size {
1726 VmaPageSize::Small => {
1727 let page =
1728 Page::<Size4KiB>::from_start_address(VirtAddr::new(mapping.start))
1729 .unwrap();
1730 if let Err(error) = mapper.unmap(page) {
1731 log::warn!(
1732 "memory: drop cleanup failed to unmap 4K page at {:#x}: {:?}",
1733 mapping.start,
1734 error
1735 );
1736 }
1737 }
1738 VmaPageSize::Huge => {
1739 let page =
1740 Page::<Size2MiB>::from_start_address(VirtAddr::new(mapping.start))
1741 .unwrap();
1742 if let Err(error) = mapper.unmap(page) {
1743 log::warn!(
1744 "memory: drop cleanup failed to unmap 2M page at {:#x}: {:?}",
1745 mapping.start,
1746 error
1747 );
1748 }
1749 }
1750 }
1751 }
1752 }
1753
1754 let _ = self.unregister_effective_mapping(mapping.start);
1755 }
1756
1757 fn teardown_region_for_drop(&self, start: u64, region: &VirtualMemoryRegion) {
1758 let len = (region.page_count as u64).saturating_mul(region.page_size.bytes());
1759 let mut page_addr = start;
1760 let page_bytes = region.page_size.bytes();
1761
1762 while page_addr < start.saturating_add(len) {
1763 if let Some(mapping) = self.effective_mapping_by_start(page_addr) {
1764 self.teardown_effective_mapping_for_drop(mapping);
1765 }
1766 page_addr += page_bytes;
1767 }
1768
1769 let _ = self.regions.lock().remove(&start);
1770 crate::silo::release_current_task_memory(len);
1771 }
1772
1773 pub fn unmap_all_user_regions(&self) {
1778 if self.is_kernel {
1779 return;
1780 }
1781
1782 loop {
1783 let first = {
1784 let guard = self.regions.lock();
1785 guard
1786 .iter()
1787 .next()
1788 .map(|(&start, region)| (start, region.clone()))
1789 };
1790
1791 let Some((start, region)) = first else {
1792 break;
1793 };
1794
1795 let len = (region.page_count as u64).saturating_mul(region.page_size.bytes());
1796 if self.unmap_range(region.start, len).is_err() {
1797 log::warn!(
1798 "memory: unmap_all_user_regions fallback cleanup for {:#x}..{:#x}",
1799 start,
1800 start.saturating_add(len)
1801 );
1802 self.teardown_region_for_drop(start, ®ion);
1803 }
1804 }
1805
1806 let residual_mappings: Vec<EffectiveMapping> = {
1807 let guard = self.effective_mappings.lock();
1808 guard.values().copied().collect()
1809 };
1810 for mapping in residual_mappings {
1811 log::warn!(
1812 "memory: drop cleanup removing orphan effective mapping at {:#x} cap={}",
1813 mapping.start,
1814 mapping.cap_id.as_u64()
1815 );
1816 self.teardown_effective_mapping_for_drop(mapping);
1817 crate::silo::release_current_task_memory(mapping.page_size.bytes());
1818 }
1819 }
1820
1821 pub fn clone_cow(&self) -> Result<Arc<AddressSpace>, &'static str> {
1823 if self.is_kernel {
1824 return Err("Cannot fork kernel address space");
1825 }
1826
1827 let child = Arc::new(AddressSpace::new_user()?);
1828
1829 let regions: Vec<VirtualMemoryRegion> = {
1830 let guard = self.regions.lock();
1831 guard.values().cloned().collect()
1832 };
1833 let effective_mappings: Vec<EffectiveMapping> = {
1834 let guard = self.effective_mappings.lock();
1835 guard.values().copied().collect()
1836 };
1837
1838 let mut tlb_flush_needed = false;
1839 let mut processed_pages = Vec::new();
1840
1841 let res: Result<(), &'static str> = (|| {
1842 let mut parent_mapper = unsafe { self.mapper() };
1843 let mut child_mapper = unsafe { child.mapper() };
1844 let mut frame_allocator = BuddyFrameAllocator;
1845
1846 for region in regions.iter() {
1847 {
1849 let mut child_regions = child.regions.lock();
1850 child_regions.insert(region.start, region.clone());
1851 }
1852 }
1853
1854 for mapping in effective_mappings.iter().copied() {
1855 let vaddr = VirtAddr::new(mapping.start);
1856 let phys_frame_addr = mapping.handle.base;
1857 let mut new_flags = mapping.flags;
1858 let is_writable = mapping.flags.contains(PageTableFlags::WRITABLE);
1859 const COW_BIT: PageTableFlags = PageTableFlags::BIT_9;
1860
1861 if is_writable {
1862 new_flags.remove(PageTableFlags::WRITABLE);
1863 new_flags.insert(COW_BIT);
1864
1865 unsafe {
1866 let res: Result<(), &'static str> = match mapping.page_size {
1867 VmaPageSize::Small => parent_mapper
1868 .update_flags(
1869 Page::<Size4KiB>::from_start_address(vaddr).unwrap(),
1870 new_flags,
1871 )
1872 .map(|f| f.ignore())
1873 .map_err(|_| "Failed to update parent 4K flags"),
1874 VmaPageSize::Huge => parent_mapper
1875 .update_flags(
1876 Page::<Size2MiB>::from_start_address(vaddr).unwrap(),
1877 new_flags,
1878 )
1879 .map(|f| f.ignore())
1880 .map_err(|_| "Failed to update parent 2M flags"),
1881 };
1882 if let Err(e) = res {
1883 return Err(e);
1884 }
1885 }
1886 let _ = self.update_effective_mapping_flags(vaddr.as_u64(), new_flags);
1887 tlb_flush_needed = true;
1888 processed_pages.push((vaddr.as_u64(), mapping.flags, mapping.page_size));
1889 }
1890
1891 let handle = mapping.handle;
1892 crate::memory::cow::handle_inc_ref(handle).map_err(|error| {
1893 log::warn!(
1894 "clone_cow: failed to pin source handle {:#x}/{} for vaddr={:#x}: {:?}",
1895 handle.base.as_u64(),
1896 handle.order,
1897 vaddr.as_u64(),
1898 error
1899 );
1900 "Failed to pin source COW frame"
1901 })?;
1902
1903 let map_flags = new_flags | PageTableFlags::WRITABLE;
1908
1909 unsafe {
1910 let map_res: Result<(), &'static str> = match mapping.page_size {
1911 VmaPageSize::Small => {
1912 let page = Page::<Size4KiB>::from_start_address(vaddr).unwrap();
1913 let frame = x86_64::structures::paging::PhysFrame::<Size4KiB>::containing_address(phys_frame_addr);
1914 child_mapper
1915 .map_to(page, frame, map_flags, &mut frame_allocator)
1916 .map(|f| f.ignore())
1917 .map_err(|_| "Failed to map 4K in child")
1918 }
1919 VmaPageSize::Huge => {
1920 let page = Page::<Size2MiB>::from_start_address(vaddr).unwrap();
1921 let frame = x86_64::structures::paging::PhysFrame::<Size2MiB>::containing_address(phys_frame_addr);
1922 child_mapper
1923 .map_to(page, frame, map_flags, &mut frame_allocator)
1924 .map(|f| f.ignore())
1925 .map_err(|_| "Failed to map 2M in child")
1926 }
1927 };
1928
1929 if let Err(e) = map_res {
1930 crate::memory::cow::handle_dec_ref(handle);
1931 return Err(e);
1932 }
1933
1934 if !new_flags.contains(PageTableFlags::WRITABLE) {
1936 let downgrade_res: Result<(), &'static str> = match mapping.page_size {
1937 VmaPageSize::Small => {
1938 let page = Page::<Size4KiB>::from_start_address(vaddr).unwrap();
1939 child_mapper
1940 .update_flags(page, new_flags)
1941 .map(|f| f.ignore())
1942 .map_err(|_| "Failed to update child 4K flags")
1943 }
1944 VmaPageSize::Huge => {
1945 let page = Page::<Size2MiB>::from_start_address(vaddr).unwrap();
1946 child_mapper
1947 .update_flags(page, new_flags)
1948 .map(|f| f.ignore())
1949 .map_err(|_| "Failed to update child 2M flags")
1950 }
1951 };
1952 if let Err(e) = downgrade_res {
1953 let unmapped = match mapping.page_size {
1954 VmaPageSize::Small => {
1955 let page = Page::<Size4KiB>::from_start_address(vaddr).unwrap();
1956 child_mapper.unmap(page).map(|(_, f)| f.ignore()).is_ok()
1957 }
1958 VmaPageSize::Huge => {
1959 let page = Page::<Size2MiB>::from_start_address(vaddr).unwrap();
1960 child_mapper.unmap(page).map(|(_, f)| f.ignore()).is_ok()
1961 }
1962 };
1963 if unmapped {
1964 crate::memory::cow::handle_dec_ref(handle);
1965 }
1966 return Err(e);
1967 }
1968 }
1969 }
1970
1971 if child
1972 .register_effective_mapping(EffectiveMapping {
1973 start: vaddr.as_u64(),
1974 cap_id: allocate_mapping_cap_id(),
1975 handle,
1976 flags: new_flags,
1977 page_size: mapping.page_size,
1978 })
1979 .is_err()
1980 {
1981 match mapping.page_size {
1982 VmaPageSize::Small => {
1983 let page = Page::<Size4KiB>::from_start_address(vaddr).unwrap();
1984 if let Ok((_, flush)) = child_mapper.unmap(page) {
1985 flush.ignore();
1986 }
1987 }
1988 VmaPageSize::Huge => {
1989 let page = Page::<Size2MiB>::from_start_address(vaddr).unwrap();
1990 if let Ok((_, flush)) = child_mapper.unmap(page) {
1991 flush.ignore();
1992 }
1993 }
1994 }
1995 crate::memory::cow::handle_dec_ref(handle);
1996 return Err("Failed to track child COW mapping");
1997 }
1998
1999 crate::memory::cow::handle_dec_ref(handle);
2000 }
2001 Ok(())
2002 })();
2003
2004 let tlb_flush_range = if tlb_flush_needed && !processed_pages.is_empty() {
2005 let mut range_start = u64::MAX;
2006 let mut range_end = 0u64;
2007 for (vaddr, _, page_size) in &processed_pages {
2008 range_start = range_start.min(*vaddr);
2009 range_end = range_end.max(vaddr.saturating_add(page_size.bytes()));
2010 }
2011 if range_start < range_end {
2012 Some((range_start, range_end))
2013 } else {
2014 None
2015 }
2016 } else {
2017 None
2018 };
2019
2020 if let Err(e) = res {
2021 log::error!("clone_cow error: {}. Rolling back...", e);
2022 let mut parent_mapper = unsafe { self.mapper() };
2023 for &(vaddr, original_flags, page_size) in processed_pages.iter().rev() {
2024 if original_flags.contains(PageTableFlags::WRITABLE) {
2025 unsafe {
2026 match page_size {
2027 VmaPageSize::Small => {
2028 let _ = parent_mapper.update_flags(
2029 Page::<Size4KiB>::from_start_address(VirtAddr::new(vaddr))
2030 .unwrap(),
2031 original_flags,
2032 );
2033 }
2034 VmaPageSize::Huge => {
2035 let _ = parent_mapper.update_flags(
2036 Page::<Size2MiB>::from_start_address(VirtAddr::new(vaddr))
2037 .unwrap(),
2038 original_flags,
2039 );
2040 }
2041 };
2042 }
2043 let _ = self.update_effective_mapping_flags(vaddr, original_flags);
2044 }
2045 }
2046 if let Some((range_start, range_end)) = tlb_flush_range {
2047 crate::arch::x86_64::tlb::shootdown_range(
2048 VirtAddr::new(range_start),
2049 VirtAddr::new(range_end),
2050 );
2051 }
2052 return Err(e);
2053 }
2054
2055 if let Some((range_start, range_end)) = tlb_flush_range {
2056 crate::arch::x86_64::tlb::shootdown_range(
2057 VirtAddr::new(range_start),
2058 VirtAddr::new(range_end),
2059 );
2060 }
2061 Ok(child)
2062 }
2063
2064 fn free_user_page_tables(&self) {
2066 if self.is_kernel {
2067 return;
2068 }
2069
2070 let l4 = unsafe { &mut *self.l4_table_virt.as_mut_ptr::<PageTable>() };
2072
2073 for i in 0..256 {
2074 if !l4[i].flags().contains(PageTableFlags::PRESENT) {
2075 continue;
2076 }
2077 let l3_frame = match l4[i].frame() {
2078 Ok(f) => f,
2079 Err(_) => {
2080 l4[i].set_unused();
2081 continue;
2082 }
2083 };
2084
2085 free_l3_table(l3_frame);
2086 l4[i].set_unused();
2087 }
2088 }
2089}
2090
2091impl Drop for AddressSpace {
2092 fn drop(&mut self) {
2094 if self.is_kernel {
2095 return; }
2097
2098 log::trace!("AddressSpace::drop begin CR3={:#x}", self.cr3_phys.as_u64());
2099
2100 self.unmap_all_user_regions();
2102 #[cfg(not(feature = "selftest"))]
2103 self.free_user_page_tables();
2104 #[cfg(feature = "selftest")]
2105 {
2106 log::trace!(
2110 "AddressSpace::drop selftest mode: skipping deep page-table free for CR3={:#x}",
2111 self.cr3_phys.as_u64()
2112 );
2113 }
2114
2115 let phys_frame = crate::memory::PhysFrame {
2119 start_address: self.cr3_phys,
2120 };
2121 crate::sync::with_irqs_disabled(|token| {
2122 crate::memory::free_frame(token, phys_frame);
2123 });
2124
2125 log::trace!("AddressSpace::drop end CR3={:#x}", self.cr3_phys.as_u64());
2126 log::debug!(
2127 "User address space dropped: CR3={:#x}",
2128 self.cr3_phys.as_u64()
2129 );
2130 }
2131}
2132
2133fn free_frame(phys: PhysAddr) {
2139 let phys_frame = crate::memory::PhysFrame {
2140 start_address: phys,
2141 };
2142 crate::sync::with_irqs_disabled(|token| {
2143 crate::memory::free_frame(token, phys_frame);
2144 });
2145}
2146
2147fn free_l1_table(frame: X86PhysFrame<Size4KiB>) {
2149 let l1_virt = VirtAddr::new(crate::memory::phys_to_virt(frame.start_address().as_u64()));
2150 let l1 = unsafe { &mut *l1_virt.as_mut_ptr::<PageTable>() };
2152 for entry in l1.iter_mut() {
2153 if entry.flags().contains(PageTableFlags::PRESENT) {
2154 entry.set_unused();
2156 }
2157 }
2158 free_frame(frame.start_address());
2159}
2160
2161fn free_l2_table(frame: X86PhysFrame<Size4KiB>) {
2163 let l2_virt = VirtAddr::new(crate::memory::phys_to_virt(frame.start_address().as_u64()));
2164 let l2 = unsafe { &mut *l2_virt.as_mut_ptr::<PageTable>() };
2165 for entry in l2.iter_mut() {
2166 if !entry.flags().contains(PageTableFlags::PRESENT) {
2167 continue;
2168 }
2169 if entry.flags().contains(PageTableFlags::HUGE_PAGE) {
2170 entry.set_unused();
2172 continue;
2173 }
2174 if let Ok(l1_frame) = entry.frame() {
2175 free_l1_table(l1_frame);
2176 }
2177 entry.set_unused();
2178 }
2179 free_frame(frame.start_address());
2180}
2181
2182fn free_l3_table(frame: X86PhysFrame<Size4KiB>) {
2184 let l3_virt = VirtAddr::new(crate::memory::phys_to_virt(frame.start_address().as_u64()));
2185 let l3 = unsafe { &mut *l3_virt.as_mut_ptr::<PageTable>() };
2186 for entry in l3.iter_mut() {
2187 if !entry.flags().contains(PageTableFlags::PRESENT) {
2188 continue;
2189 }
2190 if entry.flags().contains(PageTableFlags::HUGE_PAGE) {
2191 entry.set_unused();
2193 continue;
2194 }
2195 if let Ok(l2_frame) = entry.frame() {
2196 free_l2_table(l2_frame);
2197 }
2198 entry.set_unused();
2199 }
2200 free_frame(frame.start_address());
2201}
2202
2203static KERNEL_ADDRESS_SPACE: Once<Arc<AddressSpace>> = Once::new();
2208
2209pub unsafe fn init_kernel_address_space() {
2217 KERNEL_ADDRESS_SPACE.call_once(|| {
2218 Arc::new(unsafe { AddressSpace::new_kernel() })
2220 });
2221}
2222
2223pub fn kernel_address_space() -> &'static Arc<AddressSpace> {
2227 KERNEL_ADDRESS_SPACE
2228 .get()
2229 .expect("Kernel address space not initialized")
2230}