1use alloc::vec::Vec;
2use core::alloc::AllocError;
3use core::fmt;
4use core::sync::atomic::{AtomicUsize, Ordering};
5
6use align_address::Align;
7use free_list::{FreeList, PageLayout, PageRange};
8use hermit_sync::InterruptTicketMutex;
9use memory_addresses::VirtAddr;
10
11#[cfg(all(target_arch = "x86_64", feature = "hermit-entry"))]
12use crate::arch::mm::paging::PageTableEntryFlagsExt;
13use crate::arch::mm::paging::{self, HugePageSize, LargePageSize, PageSize};
14use crate::env::{self, MemmapType, StartInfo};
15use crate::mm::device_alloc::DeviceAlloc;
16use crate::mm::{PageRangeAllocator, PageRangeBox};
17
18static PHYSICAL_FREE_LIST: InterruptTicketMutex<FreeList<16>> =
19 InterruptTicketMutex::new(FreeList::new());
20pub static TOTAL_MEMORY: AtomicUsize = AtomicUsize::new(0);
21
22pub struct FrameAlloc;
23
24impl PageRangeAllocator for FrameAlloc {
25 unsafe fn init() {
26 unsafe {
27 init();
28 }
29 }
30
31 fn allocate(layout: PageLayout) -> Result<PageRange, AllocError> {
32 PHYSICAL_FREE_LIST
33 .lock()
34 .allocate(layout)
35 .map_err(|_| AllocError)
36 }
37
38 fn allocate_at(range: PageRange) -> Result<(), AllocError> {
39 PHYSICAL_FREE_LIST
40 .lock()
41 .allocate_at(range)
42 .map_err(|_| AllocError)
43 }
44
45 unsafe fn deallocate(range: PageRange) {
46 unsafe {
47 PHYSICAL_FREE_LIST.lock().deallocate(range).unwrap();
48 }
49 }
50}
51
52impl fmt::Display for FrameAlloc {
53 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
54 let free_list = PHYSICAL_FREE_LIST.lock();
55 write!(f, "FrameAlloc free list:\n{free_list}")
56 }
57}
58
59pub type FrameBox = PageRangeBox<FrameAlloc>;
60
61pub fn total_memory_size() -> usize {
62 TOTAL_MEMORY.load(Ordering::Relaxed)
63}
64
65#[cfg(feature = "hermit-entry")]
66pub unsafe fn map_frame_range(frame_range: PageRange) {
67 use memory_addresses::PhysAddr;
68
69 use crate::arch::mm::paging::PageTableEntryFlags;
70
71 cfg_select! {
72 target_arch = "aarch64" => {
73 type IdentityPageSize = paging::BasePageSize;
74 }
75 target_arch = "riscv64" => {
76 type IdentityPageSize = HugePageSize;
77 }
78 target_arch = "x86_64" => {
79 type IdentityPageSize = LargePageSize;
80 }
81 }
82
83 let start = frame_range
84 .start()
85 .align_down(IdentityPageSize::SIZE.try_into().unwrap());
86 let end = frame_range
87 .end()
88 .align_up(IdentityPageSize::SIZE.try_into().unwrap());
89
90 (start..end)
91 .step_by(IdentityPageSize::SIZE.try_into().unwrap())
92 .map(|addr| PhysAddr::new(addr.try_into().unwrap()))
93 .for_each(paging::identity_map::<IdentityPageSize>);
94
95 if DeviceAlloc.phys_offset() != VirtAddr::zero() {
97 let flags = {
98 let mut flags = PageTableEntryFlags::empty();
99 flags.normal().writable().execute_disable();
100 flags
101 };
102 (start..end)
103 .step_by(IdentityPageSize::SIZE.try_into().unwrap())
104 .for_each(|addr| {
105 let phys_addr = PhysAddr::new(addr.try_into().unwrap());
106 let virt_addr = VirtAddr::from_ptr(DeviceAlloc.ptr_from::<()>(phys_addr));
107 paging::map::<IdentityPageSize>(virt_addr, phys_addr, 1, flags);
108 });
109 }
110}
111
112unsafe fn detect_from_start_info() {
113 for memmap_entry in env::start_info().memmap() {
114 if memmap_entry.ty != MemmapType::Ram {
115 continue;
116 }
117
118 let mut start_addr = memmap_entry.phys_addr;
119 let mut end_addr = start_addr + memmap_entry.len;
120
121 start_addr = start_addr.max(LargePageSize::SIZE as usize);
128
129 #[cfg(all(target_arch = "x86_64", feature = "hermit-entry"))]
130 if paging::is_recursive() {
131 start_addr = start_addr.max(elf_symbols::executable_end().addr());
132 }
133
134 if cfg!(target_arch = "aarch64") || cfg!(target_arch = "riscv64") {
135 start_addr = start_addr.max(elf_symbols::executable_end().addr());
136 }
137
138 start_addr = start_addr.align_up(0x1000);
139 end_addr = end_addr.align_down(0x1000);
140
141 if start_addr > end_addr {
142 continue;
143 }
144
145 let range = PageRange::new(start_addr, end_addr).unwrap();
146 unsafe {
147 FrameAlloc::deallocate(range);
148 }
149 }
150
151 let reserve = |reservation: PageRange| {
152 debug!("Memory reservation: {reservation:#x?}");
153 while let Ok(reserved) = PHYSICAL_FREE_LIST
156 .lock()
157 .allocate_with(|range| reservation.and(range))
158 {
159 debug!("Reserved {reserved:#x?}");
160 }
161 };
162
163 let kernel_start = elf_symbols::executable_start().addr();
164 let kernel_end = elf_symbols::executable_end().addr();
165 let kernel_region = PageRange::containing(kernel_start, kernel_end).unwrap();
166 reserve(kernel_region);
167
168 for module in env::start_info().modules() {
169 reserve(module.phys_frame_range());
170 }
171
172 #[cfg(feature = "hermit-entry")]
173 {
174 use crate::env::FdtStartInfo;
175
176 let fdt = env::start_info().fdt().unwrap();
177
178 for reservation in fdt.memory_reservations() {
179 let start = reservation.address().addr();
180 let end = start + reservation.size();
181 let reservation = PageRange::new(start, end).unwrap();
182 reserve(reservation);
183 }
184
185 let fdt_start = env::start_info().fdt_addr().unwrap().get();
186 let fdt_end = fdt_start + fdt.total_size();
187 let fdt_region = PageRange::containing(fdt_start, fdt_end).unwrap();
188 reserve(fdt_region);
189 }
190
191 let frame_ranges = PHYSICAL_FREE_LIST.lock().iter().collect::<Vec<_>>();
192
193 for frame_range in frame_ranges {
194 #[cfg(feature = "hermit-entry")]
195 unsafe {
196 map_frame_range(frame_range);
197 }
198 debug!("Claimed physical memory: {frame_range:#x?}");
199 }
200
201 TOTAL_MEMORY.store(PHYSICAL_FREE_LIST.lock().free_space(), Ordering::Relaxed);
202}
203
204unsafe fn init() {
205 if cfg!(target_arch = "x86_64") && DeviceAlloc.phys_offset() != VirtAddr::zero() {
206 let start = DeviceAlloc.phys_offset();
207 let count = DeviceAlloc.phys_offset().as_u64() / HugePageSize::SIZE;
208 let count = usize::try_from(count).unwrap();
209 paging::unmap::<HugePageSize>(start, count);
210 }
211
212 unsafe {
213 detect_from_start_info();
214 }
215}