Skip to main content

strat9_kernel/memory/
address_space.rs

1//! Per-process address spaces for Strat9-OS.
2//!
3//! Each task owns an `AddressSpace` backed by a PML4 page table.
4//! Kernel tasks share a single kernel address space. User tasks get a fresh
5//! PML4 with the kernel half (entries 256..512) cloned from the kernel's table.
6//!
7//! PML4 is the Page Map Level 4, the top-level page table in x86_64's 4-level paging scheme. It
8//! contains 512 entries, each covering a 512 GiB region of the virtual address space. By cloning the
9//! kernel half of the PML4, each user address space automatically shares the kernel mappings without needing to duplicate them.
10//! Source : https://wiki.osdev.org/Memory_Management
11//!
12//!
13//! x86_64 virtual address space layout:
14//!   - PML4[0..256]   => user space (per-process, zeroed for new AS)
15//!   - PML4[256..512] => kernel space (shared, cloned from kernel L4)
16//!
17
18use 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/// Flags describing permissions for a virtual memory region.
43#[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    /// Convert to x86_64 page table flags.
53    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/// Type/purpose of a virtual memory region.
69#[derive(Debug, Clone, Copy, PartialEq, Eq)]
70pub enum VmaType {
71    /// Zero-filled anonymous memory (heap, mmap).
72    Anonymous,
73    /// Stack region (grows downward).
74    Stack,
75    /// Code/text segment (typically RX).
76    Code,
77    /// Kernel-internal mapping.
78    Kernel,
79}
80
81/// Supported page sizes for VMAs.
82#[derive(Debug, Clone, Copy, PartialEq, Eq)]
83pub enum VmaPageSize {
84    /// Standard 4 KiB page.
85    Small,
86    /// Huge 2 MiB page.
87    Huge,
88}
89
90impl VmaPageSize {
91    /// Performs the bytes operation.
92    pub fn bytes(self) -> u64 {
93        match self {
94            VmaPageSize::Small => 4096,
95            VmaPageSize::Huge => 2 * 1024 * 1024,
96        }
97    }
98}
99
100/// A tracked virtual memory region within an address space.
101#[derive(Debug, Clone)]
102pub struct VirtualMemoryRegion {
103    /// Start virtual address (page-aligned).
104    pub start: u64,
105    /// Number of pages in this region (size depends on `page_size`).
106    pub page_count: usize,
107    /// Access permissions.
108    pub flags: VmaFlags,
109    /// Purpose of this region.
110    pub vma_type: VmaType,
111    /// Size of each page in this region.
112    pub page_size: VmaPageSize,
113}
114
115/// An effective mapping currently installed in the page tables.
116#[derive(Debug, Clone, Copy, PartialEq, Eq)]
117pub struct EffectiveMapping {
118    /// Start virtual address of the mapping.
119    pub start: u64,
120    /// Internal capability identifier associated with this mapping.
121    pub cap_id: CapId,
122    /// Physical block currently backing this mapping.
123    pub handle: BlockHandle,
124    /// Hardware page-table flags currently installed for this mapping.
125    pub flags: PageTableFlags,
126    /// Page size of the mapping.
127    pub page_size: VmaPageSize,
128}
129
130/// A per-process address space backed by a PML4 page table.
131///
132/// Kernel tasks share a single `AddressSpace` (the kernel AS).
133/// User tasks each get their own, with kernel entries (PML4[256..512]) cloned
134/// so that the kernel is always mapped regardless of which AS is active.
135///
136/// # Lock ordering
137///
138/// `regions` and `effective_mappings` are `SpinLock<BTreeMap<...>>`.
139/// Both BTreeMap insert/remove may allocate from the heap allocator.
140/// This is safe because:
141///
142///   1. **No IRQ-reachable path.** These locks are only taken from process
143///      context (syscall handlers, fork, munmap, page-fault). No interrupt
144///      handler or softirq acquires either lock, so IRQ-disabling is not
145///      required and cannot deadlock with the heap allocator.
146///
147///   2. **Heap lock order is consistent.** The implicit heap lock is always
148///      innermost (address_space => heap), never outermost. No other lock is
149///      acquired between the SpinLock hold and the potential heap alloc,
150///      so no ABBA cycle exists.
151///
152///   3. **Bounded contention.** Each lock protects a per-process map; only
153///      one task at a time operates on a given address space, so contention
154///      is low and hold times are short.
155///
156/// If an IRQ or NMI path ever needs to query these maps in the future,
157/// the `SpinLock` must be replaced with a lock-free snapshot or a `Mutex`
158/// (sleepable, IRQ-safe via `spin::Mutex`).
159pub struct AddressSpace {
160    /// Physical address of the PML4 table (loaded into CR3).
161    cr3_phys: PhysAddr,
162    /// Virtual address of the PML4 table (via HHDM, for reading/modifying).
163    l4_table_virt: VirtAddr,
164    /// Whether this is the kernel address space (never freed).
165    is_kernel: bool,
166    /// Tracked virtual memory regions (key = start address).
167    regions: SpinLock<BTreeMap<u64, VirtualMemoryRegion>>,
168    /// Tracked effective mappings (key = mapping start address).
169    effective_mappings: SpinLock<BTreeMap<u64, EffectiveMapping>>,
170    /// Process identifier owning this address space, when bound to a process.
171    owner_pid: AtomicU32,
172}
173
174// SAFETY: AddressSpace is protected by the scheduler lock and per-task ownership.
175// The PML4 table is accessed through HHDM virtual addresses which are valid on all CPUs.
176unsafe impl Send for AddressSpace {}
177unsafe impl Sync for AddressSpace {}
178
179impl AddressSpace {
180    /// Create the kernel address space by wrapping the current (boot) CR3.
181    ///
182    /// # Safety
183    /// Must be called exactly once, during single-threaded init, after paging is initialized.
184    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    /// Create a new user address space with the kernel half cloned.
205    ///
206    /// Allocates a fresh PML4 frame, zeroes it, then copies entries 256..512
207    /// from the kernel PML4. This shares the kernel's L3/L2/L1 subtrees so
208    /// kernel mapping changes propagate automatically.
209    pub fn new_user() -> Result<Self, &'static str> {
210        // Allocate a frame for the new PML4 table.
211        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        // Zero the entire table first (clears user-half entries 0..256).
219        // SAFETY: new_l4_virt points to a freshly allocated, HHDM-mapped frame.
220        unsafe {
221            core::ptr::write_bytes(new_l4_virt.as_mut_ptr::<u8>(), 0, 4096);
222        }
223
224        // Clone kernel entries (PML4[256..512]) from the kernel's L4 table.
225        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        // SAFETY: Both pointers are valid HHDM-mapped page tables. We only read
229        // from the kernel table and write to the freshly allocated table.
230        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        // ---------- LAPIC low-half mapping (HHDM=0 workaround) ----------
239        //
240        // When Limine provides a non-zero HHDM offset the LAPIC is mapped in
241        // PML4[256..512] (kernel half) and is already shared above.
242        //
243        // When HHDM=0 the LAPIC is identity-mapped at its physical address
244        // (0xFEE00000) in the low half (PML4[0]).  Every Ring-0 interrupt
245        // handler calls apic::eoi() which writes to this address.  If the
246        // handler fires while a user CR3 is active the write faults because
247        // PML4[0] is absent in the user page tables.
248        //
249        // Fix: map just the LAPIC 4KiB MMIO page into every new user AS using
250        // a fresh private L3/L2/L1 hierarchy (no sharing with the kernel's
251        // page table subtrees at the LAPIC virtual address).
252        {
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                // Only needed when LAPIC is in the low half.
257                if lapic_virt < 0xFFFF_8000_0000_0000 {
258                    let phys_offset = VirtAddr::new(crate::memory::hhdm_offset());
259                    // SAFETY: new_l4_virt is the freshly allocated user PML4.
260                    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                    // Use map_to_with_table_flags to avoid USER_ACCESSIBLE on
271                    // intermediate tables so user code cannot reach LAPIC MMIO.
272                    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    /// Registers an effective mapping in the address space tracking table.
304    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    /// Removes an effective mapping from the address space tracking table.
370    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    /// Updates the hardware flags recorded for an effective mapping.
386    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    /// Returns the effective mapping that starts exactly at `start`.
396    pub fn effective_mapping_by_start(&self, start: u64) -> Option<EffectiveMapping> {
397        self.effective_mappings.lock().get(&start).copied()
398    }
399
400    /// Unmaps the effective mapping that starts at `start`.
401    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    /// Returns the effective mapping covering `addr`, if any.
409    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    /// Binds this address space to the given process identifier.
422    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    /// Returns the owning process identifier, if one has been assigned.
450    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    /// Construct a temporary `OffsetPageTable` mapper for this address space.
458    ///
459    /// # Safety
460    /// The caller must ensure exclusive access to the page tables (e.g. via
461    /// the scheduler lock or single-threaded context).
462    pub(crate) unsafe fn mapper(&self) -> OffsetPageTable<'_> {
463        let phys_offset = VirtAddr::new(crate::memory::hhdm_offset());
464        // SAFETY: l4_table_virt is the HHDM-mapped address of our PML4.
465        // The caller guarantees exclusive access.
466        unsafe {
467            OffsetPageTable::new(
468                &mut *self.l4_table_virt.as_mut_ptr::<PageTable>(),
469                phys_offset,
470            )
471        }
472    }
473
474    /// Reserve a contiguous region of virtual pages without allocating physical frames.
475    ///
476    /// The pages will be mapped lazily during page faults (Demand Paging).
477    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        // Reject overlapping VMAs
499        {
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        // Enforce per-silo memory quota (best effort; non-silo tasks are ignored).
511        crate::silo::charge_current_task_memory(len).map_err(|_| "Silo memory quota exceeded")?;
512
513        // Track the region, attempting to merge with previous.
514        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    /// Handle a page fault by checking if the address falls within a reserved VMA.
561    ///
562    /// If it does, allocates a physical frame and maps it.
563    pub fn handle_fault(&self, fault_addr: u64) -> Result<(), &'static str> {
564        use x86_64::structures::paging::mapper::MapToError;
565
566        // 1. Find the VMA covering this address
567        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        // Align fault address to the page size used by this VMA.
579        let page_bytes = vma.page_size.bytes();
580        let page_addr = fault_addr & !(page_bytes - 1);
581
582        // 2. Only Anonymous/Stack regions support demand paging for now
583        match vma.vma_type {
584            VmaType::Anonymous | VmaType::Stack | VmaType::Code => {}
585            _ => return Err("VMA type does not support demand paging"),
586        }
587
588        // 3. If already mapped (race/re-fault), treat as handled.
589        if self.translate(VirtAddr::new(page_addr)).is_some() {
590            return Ok(());
591        }
592
593        // 4. Allocate and map a single page of the required size.
594        //
595        // IMPORTANT: `allocate_frame` (order-0) now goes through
596        // `FrameAllocOptions::new()` which zeroes by default.  For order > 0
597        // (huge pages) we still need a manual zero via the HHDM.
598        //
599        // The zero MUST go through phys_to_virt (HHDM), NOT through the user
600        // virtual address, because the user address space is not necessarily
601        // the currently active CR3.  Writing through `page_addr as *mut u8`
602        // would either write into a different process's memory or fault.
603        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                // SAFETY: phys_to_virt gives a valid HHDM pointer for this
615                // frame; we have exclusive ownership from the buddy allocator.
616                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        // SAFETY: We own the address space.
631        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        // Initialize COW refcount.
724        //
725        // Order-0 frames come from FrameAllocOptions which stamps refcount=1
726        // via CAS(REFCOUNT_UNUSED → 1) : the frame is already "sole owner".
727        // Huge pages (order > 0) are raw-allocated with REFCOUNT_UNUSED still
728        // in the metadata; initialise explicitly to 1 here.
729        //
730        // Do NOT call frame_inc_ref for fresh allocations: that would push the
731        // count to 2, breaking the COW semantics (refcount==1 means sole owner).
732        // frame_inc_ref is correct only when sharing an existing frame (fork).
733        if order != 0 {
734            crate::memory::cow::handle_init_ref(resolve_handle(frame.start_address));
735        }
736
737        Ok(())
738    }
739
740    /// Map a contiguous region of pages backed by newly allocated physical frames.
741    ///
742    /// Frames are allocated from the buddy allocator and zero-filled.
743    /// The region is tracked in the VMA list.
744    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        // Reject overlapping VMAs early
766        {
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        // Enforce per-silo memory quota for eagerly mapped regions.
778        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        // SAFETY: we have logical ownership of this address space.
784        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            // Allocate a physical frame of appropriate size.
793            //
794            // order-0 frames go through FrameAllocOptions (zeroed + metadata
795            // stamped).  order > 0 (huge pages) are zeroed manually via HHDM.
796            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            // Map the page.
816            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                // Free frame for this page that failed to map.
859                crate::sync::with_irqs_disabled(|token| {
860                    crate::memory::free_phys_contiguous(token, frame, order);
861                });
862
863                // Roll back already mapped pages to keep state consistent.
864                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            // Initialize COW refcount (same logic as demand_page above).
895            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        // Track the region
963        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    /// Maps shared frames.
994    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    /// Maps shared physical blocks with optional stable mapping identities.
1005    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    /// Maps shared frames with optional stable mapping identities.
1185    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    /// Unmap a previously mapped region and free the backing frames.
1209    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        // SAFETY: We have logical ownership of this address space.
1217        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            // COW-aware refcount decrement: free only when last mapping disappears.
1244            let _ = self.unregister_effective_mapping(page_addr);
1245        }
1246
1247        // Remove from VMA tracking.
1248        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    /// Find a free virtual address range of `n_pages` pages of `page_size` starting at or after `hint`.
1282    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; // USER_SPACE_END
1294
1295        // Round hint up to a page boundary
1296        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            // A gap exists before this VMA : candidate fits.
1306            if candidate.saturating_add(length) <= vma_start {
1307                break;
1308            }
1309
1310            // Candidate overlaps this VMA; skip past it.
1311            if vma_end > candidate {
1312                candidate = (vma_end.saturating_add(page_bytes - 1)) & !(page_bytes - 1);
1313            }
1314        }
1315
1316        // Final bounds check.
1317        if candidate.checked_add(length)? <= upper_limit {
1318            Some(candidate)
1319        } else {
1320            None
1321        }
1322    }
1323
1324    /// Return true if any tracked VMA overlaps `[addr, addr + len)`.
1325    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    /// Return the tracked VMA that starts exactly at `start`.
1339    pub fn region_by_start(&self, start: u64) -> Option<VirtualMemoryRegion> {
1340        let regions = self.regions.lock();
1341        regions.get(&start).cloned()
1342    }
1343
1344    /// Returns true if any page in `[addr, addr + len)` is currently mapped.
1345    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    /// Performs the protect range operation.
1371    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    /// Unmaps range.
1518    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        // Pre-validate huge-page overlaps: partial unmap of 2MiB mappings is
1527        // not supported yet. Callers must unmap on huge-page boundaries.
1528        {
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        // Process regions one by one to avoid heap allocation (Vec)
1548        let mut released_bytes = 0u64;
1549        loop {
1550            // Find the first overlapping region
1551            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; // No more overlapping regions
1564            };
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            // 1. Hardware unmap
1572            // SAFETY: Logical ownership of address space.
1573            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                // Lazy VMAs can contain unfaulted pages (no PTE). In that case
1578                // there is nothing to unmap in hardware; just update VMA metadata.
1579                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            // 2. Update tracking: remove and re-insert fragments
1612            {
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    /// Translate a virtual address to its mapped physical address.
1652    pub fn translate(&self, vaddr: VirtAddr) -> Option<PhysAddr> {
1653        // SAFETY: Read-only access to the page tables.
1654        let mapper = unsafe { self.mapper() };
1655        mapper.translate_addr(vaddr)
1656    }
1657
1658    /// Translate a virtual address to the current block handle and page-table flags.
1659    pub fn translate_to_handle(&self, vaddr: VirtAddr) -> Option<(BlockHandle, PageTableFlags)> {
1660        // SAFETY: Read-only access to the page tables.
1661        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    /// Get the physical address of this address space's PML4 table.
1672    pub fn cr3(&self) -> PhysAddr {
1673        self.cr3_phys
1674    }
1675
1676    /// Switch the CPU to this address space by writing CR3.
1677    ///
1678    /// Skips the write if CR3 already points to this address space (avoids
1679    /// unnecessary TLB flush).
1680    ///
1681    /// # Safety
1682    /// The caller must ensure this address space's page tables are valid and
1683    /// that the kernel half is correctly mapped.
1684    pub unsafe fn switch_to(&self) {
1685        let (current_frame, _) = Cr3::read();
1686        if current_frame.start_address() == self.cr3_phys {
1687            return; // Already active : skip to avoid TLB flush.
1688        }
1689
1690        // SAFETY: cr3_phys points to a valid, 4KiB-aligned PML4 table with
1691        // the kernel half correctly populated.
1692        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    /// Whether this is the kernel address space.
1702    pub fn is_kernel(&self) -> bool {
1703        self.is_kernel
1704    }
1705
1706    /// Check if this address space has any user-space memory mappings.
1707    pub fn has_user_mappings(&self) -> bool {
1708        if self.is_kernel {
1709            return false;
1710        }
1711        let regions = self.regions.lock();
1712        // Check for any non-kernel mappings.
1713        regions.values().any(|vma| vma.vma_type != VmaType::Kernel)
1714    }
1715
1716    fn teardown_effective_mapping_for_drop(&self, mapping: EffectiveMapping) {
1717        // SAFETY: the address space is being torn down; any remaining user mapping
1718        // must be detached from ownership tracking before page-table reclamation.
1719        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    /// Unmap all tracked user regions (best-effort).
1774    ///
1775    /// This frees user frames and clears the VMA list. Kernel mappings are untouched.
1776    /// Does not allocate memory.
1777    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, &region);
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    /// Performs the clone cow operation.
1822    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                // Register VMA in child.
1848                {
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                // Map in child. We map it as WRITABLE first to ensure intermediate
1904                // page tables (PDPT, PD) are created with WRITABLE bit set.
1905                // If we mapped directly as COW (Read-only), some Mapper implementations
1906                // might create Read-Only intermediate tables, blocking future COW resolution.
1907                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                    // Now downgrade to the actual COW flags (which may be Read-Only).
1935                    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    /// Releases user page tables.
2065    fn free_user_page_tables(&self) {
2066        if self.is_kernel {
2067            return;
2068        }
2069
2070        // SAFETY: We have logical ownership of this address space during drop.
2071        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    /// Performs the drop operation.
2093    fn drop(&mut self) {
2094        if self.is_kernel {
2095            return; // Never free the kernel address space.
2096        }
2097
2098        log::trace!("AddressSpace::drop begin CR3={:#x}", self.cr3_phys.as_u64());
2099
2100        // Best-effort cleanup of user mappings.
2101        self.unmap_all_user_regions();
2102        #[cfg(not(feature = "selftest"))]
2103        self.free_user_page_tables();
2104        #[cfg(feature = "selftest")]
2105        {
2106            // Runtime selftests create/destroy many temporary address spaces and
2107            // currently expose instability in recursive page-table teardown.
2108            // Keep tests deterministic by skipping deep PT reclaim in this mode.
2109            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        // Free the PML4 frame itself.
2116        // NOTE: Recursive freeing of intermediate page tables (L3/L2/L1) that
2117        // belong exclusively to the user half is deferred to P2.
2118        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
2133// ---------------------------------------------------------------------------
2134// Page table cleanup helpers (user half only)
2135// ---------------------------------------------------------------------------
2136
2137/// Releases frame.
2138fn 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
2147/// Releases l1 table.
2148fn free_l1_table(frame: X86PhysFrame<Size4KiB>) {
2149    let l1_virt = VirtAddr::new(crate::memory::phys_to_virt(frame.start_address().as_u64()));
2150    // SAFETY: l1_virt points to a valid page table frame in HHDM.
2151    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            // Mapped frames are already freed via unmap_all_user_regions.
2155            entry.set_unused();
2156        }
2157    }
2158    free_frame(frame.start_address());
2159}
2160
2161/// Releases l2 table.
2162fn 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            // 2 MiB pages are not expected in user space today.
2171            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
2182/// Releases l3 table.
2183fn 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            // 1 GiB pages are not expected in user space today.
2192            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
2203// ---------------------------------------------------------------------------
2204// Kernel address space singleton
2205// ---------------------------------------------------------------------------
2206
2207static KERNEL_ADDRESS_SPACE: Once<Arc<AddressSpace>> = Once::new();
2208
2209/// Initialize the kernel address space singleton.
2210///
2211/// Must be called once during boot, after paging is initialized, before the
2212/// scheduler creates any tasks.
2213///
2214/// # Safety
2215/// Must be called in single-threaded init context.
2216pub unsafe fn init_kernel_address_space() {
2217    KERNEL_ADDRESS_SPACE.call_once(|| {
2218        // SAFETY: Called once, single-threaded, paging initialized.
2219        Arc::new(unsafe { AddressSpace::new_kernel() })
2220    });
2221}
2222
2223/// Get a reference to the kernel address space.
2224///
2225/// Panics if called before `init_kernel_address_space()`.
2226pub fn kernel_address_space() -> &'static Arc<AddressSpace> {
2227    KERNEL_ADDRESS_SPACE
2228        .get()
2229        .expect("Kernel address space not initialized")
2230}