Skip to main content

kernel/mm/
state.rs

1// SPDX-License-Identifier: GPL-3.0-only
2//! Memory management subcategory for [`KState`]
3//!
4//! Authors: MarioS271
5
6use crate::lib::addr::VirtAddr;
7use crate::lib::sync::irq_mutex::IrqMutex;
8use crate::lib::sync::unchecked_cell::UncheckedCell;
9use crate::mm::pmm::Pmm;
10use crate::mm::vmm::address_space::AddressSpace;
11use core::sync::atomic::{AtomicU64, Ordering};
12
13#[cfg(feature = "debug-checks")] use crate::lib::panic::kernel_panic;
14#[cfg(feature = "debug-checks")] use crate::lib::panic_codes::PanicCode;
15
16// TODO: write-hot locks must not share a cache line with read-mostly fields
17
18/// Holds memory management state such as slab allocators and virtual memory areas
19#[repr(align(64))]
20pub struct Mm {
21    hhdm_offset: AtomicU64,
22    pmm: UncheckedCell<IrqMutex<Pmm>>,
23    kernel_addr_space: UncheckedCell<IrqMutex<AddressSpace>>,
24    next_kernel_stack: AtomicU64
25}
26
27impl Mm {
28    /// Constructor; initializes all values zeroed or uninited
29    pub const fn new() -> Self {
30        Self {
31            hhdm_offset: AtomicU64::new(0),
32            pmm: UncheckedCell::new(),
33            kernel_addr_space: UncheckedCell::new(),
34            next_kernel_stack: AtomicU64::new(0)
35        }
36    }
37
38    /// Initialize `Mm`'s default values
39    pub fn init(&self) {
40        self.next_kernel_stack.store(super::layout::KERNEL_STACKS_BASE.as_u64(), Ordering::Release);
41    }
42
43    /// Sets the HHDM offset
44    pub fn set_hhdm_offset(&self, offset: u64) {
45        self.hhdm_offset.store(offset, Ordering::Release);
46    }
47
48    /// Move the given [`Pmm`] into `Mm::pmm`
49    ///
50    /// # Safety
51    /// The caller must guarantee the following:
52    /// - That this method has never been called before and will never be called again
53    /// - That at the time of calling this method, no references or pointers to this data exist
54    /// - While this method is being called, no other CPU is working with the given data
55    pub unsafe fn init_pmm(&self, pmm: Pmm) {
56        unsafe { self.pmm.init(IrqMutex::new(pmm)); }
57    }
58
59    /// Move the given [`AddressSpace`] into `Mm::kernel_addr_space`
60    ///
61    /// # Safety
62    /// The caller must guarantee the following:
63    /// - That this method has never been called before and will never be called again
64    /// - That at the time of calling this method, no references or pointers to this data exist
65    /// - While this method is being called, no other CPU is working with the given data
66    pub unsafe fn init_kernel_addr_space(&self, kernel_addr_space: AddressSpace) {
67        unsafe { self.kernel_addr_space.init(IrqMutex::new(kernel_addr_space)); }
68    }
69
70    /// Getter for the hhdm offset
71    pub fn hhdm_offset(&self) -> u64 {
72        let hhdm = self.hhdm_offset.load(Ordering::Acquire);
73
74        #[cfg(feature = "debug-checks")]
75        if hhdm == 0 {
76            kernel_panic(
77                PanicCode::UninitializedAccess,
78                "Attempted to access KSTATE.mm.hhdm_offset before it was initialized"
79            )
80        }
81
82        hhdm
83    }
84
85    /// Getter for `Mm::pmm`
86    ///
87    /// # Safety
88    /// The caller must guarantee the following:
89    /// - That this value's `init` method has already been called before
90    /// - That at this method's entire execution time, no mutable references or pointers to this data
91    ///   exist or will exist
92    pub unsafe fn pmm(&self) -> &IrqMutex<Pmm> {
93        unsafe { self.pmm.get() }
94    }
95
96    /// Getter for `Mm::kernel_address_space`
97    ///
98    /// # Safety
99    /// The caller must guarantee the following:
100    /// - That this value's `init` method has already been called before
101    /// - That at this method's entire execution time, no mutable references or pointers to this data
102    ///   exist or will exist
103    pub unsafe fn kernel_addr_space(&self) -> &IrqMutex<AddressSpace> {
104        unsafe { self.kernel_addr_space.get() }
105    }
106    
107    /// Get the starting address of the next kernel stack and increment it
108    pub fn alloc_kernel_stack_slot(&self) -> VirtAddr {
109        use super::layout::*;
110
111        let addr = self.next_kernel_stack.fetch_add(KERNEL_STACK_SLOT_SIZE, Ordering::Relaxed);
112        let addr_virt = VirtAddr::new(addr);
113
114        #[cfg(feature = "debug-checks")]
115        {
116            if addr_virt < KERNEL_STACKS_BASE {
117                kernel_panic(
118                    PanicCode::UninitializedAccess,
119                    "Attempted to get the next kernel stack address before Mm::init was called"
120                );
121            }
122
123            if addr_virt > KERNEL_STACKS_BASE + KERNEL_STACKS_SIZE - KERNEL_STACK_SLOT_SIZE {
124                kernel_panic(
125                    PanicCode::OutOfVirtualMemory,
126                    "Could not allocate a kernel stack, out of virtual memory for kernel stacks (somehow)"
127                );
128            }
129        }
130
131        addr_virt
132    }
133}