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