1pub(crate) mod device_alloc;
44mod mapped_page_box;
45mod page_range_alloc;
46mod physicalmem;
47mod virtualmem;
48
49use core::alloc::Layout;
50use core::mem::MaybeUninit;
51
52use align_address::Align;
53use free_list::{PageLayout, PageRange};
54use hermit_sync::RawInterruptSpinMutex;
55pub use memory_addresses::{PhysAddr, VirtAddr};
56#[cfg(target_os = "none")]
57use talc::TalcLock;
58#[cfg(target_os = "none")]
59use talc::source::Manual;
60
61pub use self::mapped_page_box::MappedPageBox;
62pub use self::page_range_alloc::{PageRangeAllocator, PageRangeBox};
63pub use self::physicalmem::{FrameAlloc, FrameBox};
64pub use self::virtualmem::{PageAlloc, PageBox};
65use crate::arch;
66#[cfg(any(target_arch = "x86_64", target_arch = "riscv64"))]
67use crate::arch::mm::paging::HugePageSize;
68pub use crate::arch::mm::paging::virtual_to_physical;
69use crate::arch::mm::paging::{BasePageSize, LargePageSize, PageSize};
70
71#[cfg(target_os = "none")]
72#[global_allocator]
73pub(crate) static ALLOCATOR: TalcLock<RawInterruptSpinMutex, Manual> = TalcLock::new(Manual);
74
75#[cfg(target_os = "none")]
76pub(crate) fn claim_initial_heap() {
77 #[repr(C, align(0x1000))]
78 struct InitialHeap([MaybeUninit<u8>; 0x1000]);
79
80 debug_assert_eq!(
81 Layout::new::<InitialHeap>(),
82 Layout::from_size_align(0x1000, 0x1000).unwrap()
83 );
84
85 static mut INITIAL_HEAP: InitialHeap = InitialHeap([MaybeUninit::uninit(); _]);
86
87 let base = (&raw mut INITIAL_HEAP).cast::<u8>();
88 let size = size_of::<InitialHeap>();
89 unsafe {
90 ALLOCATOR.lock().claim(base, size).unwrap();
91 }
92}
93
94#[cfg(target_os = "none")]
95pub(crate) fn init() {
96 use arch::mm::paging;
97
98 #[cfg(not(target_arch = "riscv64"))]
99 unsafe {
100 paging::init();
101 }
102 unsafe {
103 FrameAlloc::init();
104 }
105 #[cfg(target_arch = "x86_64")]
106 unsafe {
107 paging::log_page_tables();
108 }
109 unsafe {
110 PageAlloc::init();
111 }
112 #[cfg(target_arch = "riscv64")]
113 unsafe {
114 paging::enable_page_table();
115 }
116
117 let total_mem = physicalmem::total_memory_size();
118 info!("Total memory size: {} MiB", total_mem >> 20);
119
120 let npages = total_mem / BasePageSize::SIZE as usize;
123 let npage_div = BasePageSize::SIZE as usize / align_of::<usize>();
124 let npage_3tables = npages / npage_div + 1;
125 let npage_2tables = npage_3tables / npage_div + 1;
126 let npage_1tables = npage_2tables / npage_div + 1;
127 let min_mem = (npage_3tables + npage_2tables + npage_1tables) * BasePageSize::SIZE as usize
128 + 2 * LargePageSize::SIZE as usize;
129 #[cfg(any(target_arch = "x86_64", target_arch = "riscv64"))]
130 let has_1gib_pages = arch::kernel::processor::supports_1gib_pages();
131 let has_2mib_pages = arch::kernel::processor::supports_2mib_pages();
132
133 info!("Minimum memory size: {} MiB", min_mem >> 20);
134 let avail_mem = total_mem
135 .checked_sub(min_mem)
136 .unwrap_or_else(|| panic!("Not enough memory available!"))
137 .align_down(LargePageSize::SIZE as usize);
138
139 let mut map_addr;
140 let mut map_size;
141 let heap_start_addr;
142
143 #[cfg(feature = "common-os")]
144 {
145 info!("Using Hermit as common OS!");
146
147 let reserve: usize = (avail_mem * 75) / 100;
149 let reserve = core::cmp::min(reserve, 0x0400_0000);
151
152 let virt_size: usize = reserve.align_down(LargePageSize::SIZE as usize);
153 let layout = PageLayout::from_size_align(virt_size, LargePageSize::SIZE as usize).unwrap();
154 let page_range = PageAlloc::allocate(layout).unwrap();
155 let virt_addr = VirtAddr::from(page_range.start());
156 heap_start_addr = virt_addr;
157
158 info!(
159 "Heap: size {} MB, start address {:p}",
160 virt_size >> 20,
161 virt_addr
162 );
163
164 #[cfg(any(target_arch = "x86_64", target_arch = "riscv64"))]
165 if has_1gib_pages && virt_size > HugePageSize::SIZE as usize {
166 let npages = (virt_addr.align_up(HugePageSize::SIZE) - virt_addr) as usize
168 / LargePageSize::SIZE as usize;
169 if let Err(n) = paging::map_heap::<LargePageSize>(virt_addr, npages) {
170 map_addr = virt_addr + n as u64 * LargePageSize::SIZE;
171 map_size = virt_size - (map_addr - virt_addr) as usize;
172 } else {
173 map_addr = virt_addr.align_up(HugePageSize::SIZE);
174 map_size = virt_size - (map_addr - virt_addr) as usize;
175 }
176 } else {
177 map_addr = virt_addr;
178 map_size = virt_size;
179 }
180
181 #[cfg(not(any(target_arch = "x86_64", target_arch = "riscv64")))]
182 {
183 map_addr = virt_addr;
184 map_size = virt_size;
185 }
186 }
187
188 #[cfg(not(feature = "common-os"))]
189 {
190 #[cfg(not(feature = "mman"))]
192 let stack_reserve: usize = (avail_mem * 10) / 100;
193
194 #[cfg(not(feature = "mman"))]
199 let virt_size: usize = (avail_mem - stack_reserve).align_down(LargePageSize::SIZE as usize);
200 #[cfg(feature = "mman")]
201 let virt_size: usize = ((avail_mem * 75) / 100).align_down(LargePageSize::SIZE as usize);
202
203 let layout = PageLayout::from_size_align(virt_size, LargePageSize::SIZE as usize).unwrap();
204 let page_range = PageAlloc::allocate(layout).unwrap();
205 let virt_addr = VirtAddr::from(page_range.start());
206 heap_start_addr = virt_addr;
207
208 info!(
209 "Heap: size {} MB, start address {:p}",
210 virt_size >> 20,
211 virt_addr
212 );
213
214 #[cfg(any(target_arch = "x86_64", target_arch = "riscv64"))]
215 if has_1gib_pages && virt_size > HugePageSize::SIZE as usize {
216 let npages = (virt_addr.align_up(HugePageSize::SIZE) - virt_addr) / LargePageSize::SIZE;
218 if let Err(n) = paging::map_heap::<LargePageSize>(virt_addr, npages as usize) {
219 map_addr = virt_addr + n as u64 * LargePageSize::SIZE;
220 map_size = virt_size - (map_addr - virt_addr) as usize;
221 } else {
222 map_addr = virt_addr.align_up(HugePageSize::SIZE);
223 map_size = virt_size - (map_addr - virt_addr) as usize;
224 }
225 } else {
226 map_addr = virt_addr;
227 map_size = virt_size;
228 }
229
230 #[cfg(not(any(target_arch = "x86_64", target_arch = "riscv64")))]
231 {
232 map_addr = virt_addr;
233 map_size = virt_size;
234 }
235 }
236
237 #[cfg(any(target_arch = "x86_64", target_arch = "riscv64"))]
238 if has_1gib_pages
239 && map_size > HugePageSize::SIZE as usize
240 && map_addr.is_aligned_to(HugePageSize::SIZE)
241 {
242 let size = map_size.align_down(HugePageSize::SIZE as usize);
243 if let Err(num_pages) =
244 paging::map_heap::<HugePageSize>(map_addr, size / HugePageSize::SIZE as usize)
245 {
246 map_size -= num_pages * HugePageSize::SIZE as usize;
247 map_addr += num_pages as u64 * HugePageSize::SIZE;
248 } else {
249 map_size -= size;
250 map_addr += size;
251 }
252 }
253
254 if has_2mib_pages
255 && map_size > LargePageSize::SIZE as usize
256 && map_addr.is_aligned_to(LargePageSize::SIZE)
257 {
258 let size = map_size.align_down(LargePageSize::SIZE as usize);
259 if let Err(num_pages) =
260 paging::map_heap::<LargePageSize>(map_addr, size / LargePageSize::SIZE as usize)
261 {
262 map_size -= num_pages * LargePageSize::SIZE as usize;
263 map_addr += num_pages as u64 * LargePageSize::SIZE;
264 } else {
265 map_size -= size;
266 map_addr += size;
267 }
268 }
269
270 if map_size > BasePageSize::SIZE as usize && map_addr.is_aligned_to(BasePageSize::SIZE) {
271 let size = map_size.align_down(BasePageSize::SIZE as usize);
272 if let Err(num_pages) =
273 paging::map_heap::<BasePageSize>(map_addr, size / BasePageSize::SIZE as usize)
274 {
275 map_size -= num_pages * BasePageSize::SIZE as usize;
276 map_addr += num_pages as u64 * BasePageSize::SIZE;
277 } else {
278 map_size -= size;
279 map_addr += size;
280 }
281 }
282
283 let heap_end_addr = map_addr;
284
285 let size = heap_end_addr.as_usize() - heap_start_addr.as_usize();
286 unsafe {
287 ALLOCATOR
288 .lock()
289 .claim(heap_start_addr.as_mut_ptr(), size)
290 .unwrap();
291 }
292
293 info!("Heap is located at {heap_start_addr:p}..{heap_end_addr:p} ({map_size} Bytes unmapped)");
294}
295
296pub(crate) fn print_information() {
297 info!("{FrameAlloc}");
298 info!("{PageAlloc}");
299}
300
301#[cfg(feature = "pci")]
303pub(crate) fn map(
304 physical_address: PhysAddr,
305 size: usize,
306 writable: bool,
307 no_execution: bool,
308 no_cache: bool,
309) -> VirtAddr {
310 use crate::arch::mm::paging::PageTableEntryFlags;
311 #[cfg(target_arch = "x86_64")]
312 use crate::arch::mm::paging::PageTableEntryFlagsExt;
313
314 let size = size.align_up(BasePageSize::SIZE as usize);
315 let count = size / BasePageSize::SIZE as usize;
316
317 let mut flags = if no_cache {
318 PageTableEntryFlags::device()
319 } else {
320 PageTableEntryFlags::normal()
321 };
322 flags.set_writable(writable).set_execute(!no_execution);
323
324 let layout = PageLayout::from_size(size).unwrap();
325 let page_range = PageAlloc::allocate(layout).unwrap();
326 let virtual_address = VirtAddr::from(page_range.start());
327 arch::mm::paging::map::<BasePageSize>(virtual_address, physical_address, count, flags);
328
329 virtual_address
330}
331
332#[allow(dead_code)]
333pub(crate) fn unmap(virtual_address: VirtAddr, size: usize) {
335 let size = size.align_up(BasePageSize::SIZE as usize);
336
337 if virtual_to_physical(virtual_address).is_some() {
338 arch::mm::paging::unmap::<BasePageSize>(
339 virtual_address,
340 size / BasePageSize::SIZE as usize,
341 );
342
343 let range = PageRange::from_start_len(virtual_address.as_usize(), size).unwrap();
344 unsafe {
345 PageAlloc::deallocate(range);
346 }
347 } else {
348 panic!("No page table entry for virtual address {virtual_address:p}");
349 }
350}