Skip to main content

kernel/mm/vmm/
address_space.rs

1// SPDX-License-Identifier: GPL-3.0-only
2//! Stores the page pointer and VMAs for a process or the kernel
3//!
4//! Authors: MarioS271
5
6use crate::lib::addr::VirtAddr;
7use crate::lib::panic::kernel_panic;
8use crate::lib::panic_codes::PanicCode;
9use crate::mm::pmm::FRAME_SIZE;
10use crate::mm::vmm::boot_mapping::BootMappings;
11use crate::mm::vmm::traits::VmmPaging;
12use crate::mm::vmm::vma::Vma;
13use crate::mm::vmm::{Vmm, VmmError, VmmResult};
14use crate::state::kstate::KSTATE;
15use alloc::collections::BTreeSet;
16
17/// Type to represent the memory of a process or the kernel by holding a pointer to its page tables and VMAs
18pub struct AddressSpace {
19    page_ptr: VirtAddr,
20    vmas: BTreeSet<Vma>,
21}
22
23impl AddressSpace {
24    /// Creates a new `AddressSpace` instance which contains the given page table pointer and no VMAs
25    pub fn empty(page_ptr: VirtAddr) -> Self {
26        Self {
27            page_ptr,
28            vmas: BTreeSet::new()
29        }
30    }
31
32    /// Create a new address space with a zeroed root page, and copy the kernel mappings into the
33    /// higher half
34    ///
35    /// # Safety
36    /// The caller must guarantee that boot stage 2 has already been completed
37    pub unsafe fn new_user_addr_space() -> Self {
38        let frame = unsafe { KSTATE.mm.pmm() }.lock().alloc_frame().unwrap_or_else(
39            || kernel_panic(
40                PanicCode::OutOfMemory,
41                "Could not allocate a root page for the a address space, out of memory"
42            )
43        );
44
45        let page = VirtAddr::from_phys(frame);
46        // Safety: page is a valid frame; also, this assumes that one root page is exactly 4 KiB
47        // (or one FRAME_SIZE) large, if it is not, this code will silently zero the wrong amount
48        // of memory!
49        unsafe {
50            core::ptr::write_bytes(page.as_mut_ptr::<u8>(), 0u8, FRAME_SIZE as usize);
51            Vmm::clone_kernel_mappings(page);
52        }
53
54        AddressSpace::empty(page)
55    }
56
57    /// Getter for `AddressSpace::page_ptr`
58    pub fn page_ptr(&self) -> VirtAddr {
59        self.page_ptr
60    }
61
62    /// Getter for `AddressSpace::vmas`
63    pub fn vmas(&self) -> &BTreeSet<Vma> {
64        &self.vmas
65    }
66
67    /// Initializes Kernel VMAs on self
68    /// Do not call this ever, unless you are initializing the kernel's address space
69    pub fn setup_kernel_vmas(&mut self, boot_mappings: &BootMappings) -> VmmResult {
70        for mapping in boot_mappings {
71            self.insert_vma(
72                Vma {
73                    start_addr: mapping.virt,
74                    end_addr: mapping.virt + mapping.size,
75                    flags: mapping.flags,
76                }
77            )?;
78        }
79
80        Ok(())
81    }
82
83    /// Add a VMA to the `AddressSpace`
84    pub fn insert_vma(&mut self, vma: Vma) -> VmmResult {
85        for vma_other in &self.vmas {
86            if vma_other.overlaps(&vma) {
87                return Err(VmmError::VmaOverlap);
88            }
89        }
90        self.vmas.insert(vma);
91        Ok(())
92    }
93
94    /// Remove a VMA from the `AddressSpace`
95    /// Returns whether the element to remove existed and could be removed or not
96    pub fn remove_vma(&mut self, vma_start_addr: VirtAddr) -> Option<Vma> {
97        self.vmas.take(&vma_start_addr)
98    }
99
100    /// Search for a VMA in the `AddressSpace` which applies VMA flags to the given `VirtAddr`
101    /// Returns a reference to the VMA wrapped in an option incase it is not found
102    pub fn find_vma(&self, addr: VirtAddr) -> Option<&Vma> {
103        self.vmas.range(..=addr).next_back().filter(|vma| vma.contains(addr))
104    }
105
106    /// Search for a VMA-free range of `size` bytes in `[from, to)`
107    /// Returns the lowest address where such a range fits, or [`None`] if such a
108    /// range couldn't be found between VMAs
109    pub fn find_free_range(&self, from: VirtAddr, to: VirtAddr, size: u64) -> Option<VirtAddr> {
110        let mut candidate = from;
111
112        for vma in self.vmas.range(from..) {
113            if vma.start_addr >= to {
114                break;
115            }
116
117            if vma.start_addr.as_u64() - candidate.as_u64() >= size {
118                return Some(candidate);
119            }
120
121            if vma.end_addr > candidate {
122                candidate = vma.end_addr;
123            }
124        }
125
126        if to.as_u64() - candidate.as_u64() >= size {
127            Some(candidate)
128        } else {
129            None
130        }
131    }
132}