1use core::alloc::AllocError;
2use core::fmt;
3use core::sync::atomic::{AtomicUsize, Ordering};
4
5use align_address::Align;
6use free_list::{FreeList, PageLayout, PageRange};
7use hermit_sync::InterruptTicketMutex;
8use memory_addresses::VirtAddr;
9
10#[cfg(all(target_arch = "x86_64", feature = "hermit-entry"))]
11use crate::arch::mm::paging::PageTableEntryFlagsExt;
12use crate::arch::mm::paging::{self, HugePageSize, LargePageSize, PageSize};
13use crate::env::{self, MemmapType, StartInfo};
14use crate::mm::device_alloc::DeviceAlloc;
15use crate::mm::{PageRangeAllocator, PageRangeBox};
16use crate::page_range_ext::PageRangeExt;
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 #[cfg(feature = "hermit-entry")]
150 unsafe {
151 map_frame_range(range);
152 }
153 TOTAL_MEMORY.fetch_add(range.len().get(), Ordering::Relaxed);
154 debug!("Claimed physical memory: {range:#x?}");
155 }
156
157 let reserve = |reservation: PageRange| {
158 debug!("Memory reservation: {reservation:#x?}");
159 while let Ok(reserved) = PHYSICAL_FREE_LIST
162 .lock()
163 .allocate_with(|range| reservation.and(range))
164 {
165 debug!("Reserved {reserved:#x?}");
166 }
167 };
168
169 let kernel_start = elf_symbols::executable_start().addr();
170 let kernel_end = elf_symbols::executable_end().addr();
171 let kernel_region = PageRange::containing(kernel_start, kernel_end).unwrap();
172 reserve(kernel_region);
173
174 for module in env::start_info().modules() {
175 reserve(module.phys_frame_range());
176 }
177
178 #[cfg(feature = "hermit-entry")]
179 {
180 use crate::env::FdtStartInfo;
181
182 let fdt = env::start_info().fdt().unwrap();
183
184 for reservation in fdt.memory_reservations() {
185 let start = reservation.address().addr();
186 let end = start + reservation.size();
187 let reservation = PageRange::new(start, end).unwrap();
188 reserve(reservation);
189 }
190
191 let fdt_start = env::start_info().fdt_addr().unwrap().get();
192 let fdt_end = fdt_start + fdt.total_size();
193 let fdt_region = PageRange::containing(fdt_start, fdt_end).unwrap();
194 reserve(fdt_region);
195 }
196}
197
198unsafe fn init() {
199 if cfg!(target_arch = "x86_64") && DeviceAlloc.phys_offset() != VirtAddr::zero() {
200 let start = DeviceAlloc.phys_offset();
201 let count = DeviceAlloc.phys_offset().as_u64() / HugePageSize::SIZE;
202 let count = usize::try_from(count).unwrap();
203 paging::unmap::<HugePageSize>(start, count);
204 }
205
206 unsafe {
207 detect_from_start_info();
208 }
209}