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 = PageTableEntryFlags::normal();
98 (start..end)
99 .step_by(IdentityPageSize::SIZE.try_into().unwrap())
100 .for_each(|addr| {
101 let phys_addr = PhysAddr::new(addr.try_into().unwrap());
102 let virt_addr = VirtAddr::from_ptr(DeviceAlloc.ptr_from::<()>(phys_addr));
103 paging::map::<IdentityPageSize>(virt_addr, phys_addr, 1, flags);
104 });
105 }
106}
107
108unsafe fn detect_from_start_info() {
109 for memmap_entry in env::start_info().memmap() {
110 if memmap_entry.ty != MemmapType::Ram {
111 continue;
112 }
113
114 let mut start_addr = memmap_entry.phys_addr;
115 let mut end_addr = start_addr + memmap_entry.len;
116
117 start_addr = start_addr.max(LargePageSize::SIZE as usize);
124
125 #[cfg(all(target_arch = "x86_64", feature = "hermit-entry"))]
126 if paging::is_recursive() {
127 start_addr = start_addr.max(elf_symbols::executable_end().addr());
128 }
129
130 if cfg!(target_arch = "aarch64") || cfg!(target_arch = "riscv64") {
131 start_addr = start_addr.max(elf_symbols::executable_end().addr());
132 }
133
134 start_addr = start_addr.align_up(0x1000);
135 end_addr = end_addr.align_down(0x1000);
136
137 if start_addr > end_addr {
138 continue;
139 }
140
141 let range = PageRange::new(start_addr, end_addr).unwrap();
142 unsafe {
143 FrameAlloc::deallocate(range);
144 }
145 }
146
147 let reserve = |reservation: PageRange| {
148 debug!("Memory reservation: {reservation:#x?}");
149 while let Ok(reserved) = PHYSICAL_FREE_LIST
152 .lock()
153 .allocate_with(|range| reservation.and(range))
154 {
155 debug!("Reserved {reserved:#x?}");
156 }
157 };
158
159 let kernel_start = elf_symbols::executable_start().addr();
160 let kernel_end = elf_symbols::executable_end().addr();
161 let kernel_region = PageRange::containing(kernel_start, kernel_end).unwrap();
162 reserve(kernel_region);
163
164 for module in env::start_info().modules() {
165 reserve(module.phys_frame_range());
166 }
167
168 #[cfg(feature = "hermit-entry")]
169 {
170 use crate::env::FdtStartInfo;
171
172 let fdt = env::start_info().fdt().unwrap();
173
174 for reservation in fdt.memory_reservations() {
175 let start = reservation.address().addr();
176 let end = start + reservation.size();
177 let reservation = PageRange::new(start, end).unwrap();
178 reserve(reservation);
179 }
180
181 let fdt_start = env::start_info().fdt_addr().unwrap().get();
182 let fdt_end = fdt_start + fdt.total_size();
183 let fdt_region = PageRange::containing(fdt_start, fdt_end).unwrap();
184 reserve(fdt_region);
185 }
186
187 let frame_ranges = PHYSICAL_FREE_LIST.lock().iter().collect::<Vec<_>>();
188
189 for frame_range in frame_ranges {
190 #[cfg(feature = "hermit-entry")]
191 unsafe {
192 map_frame_range(frame_range);
193 }
194 debug!("Claimed physical memory: {frame_range:#x?}");
195 }
196
197 TOTAL_MEMORY.store(PHYSICAL_FREE_LIST.lock().free_space(), Ordering::Relaxed);
198}
199
200unsafe fn init() {
201 if cfg!(target_arch = "x86_64") && DeviceAlloc.phys_offset() != VirtAddr::zero() {
202 let start = DeviceAlloc.phys_offset();
203 let count = DeviceAlloc.phys_offset().as_u64() / HugePageSize::SIZE;
204 let count = usize::try_from(count).unwrap();
205 paging::unmap::<HugePageSize>(start, count);
206 }
207
208 unsafe {
209 detect_from_start_info();
210 }
211}