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};
64use crate::arch;
65#[cfg(any(target_arch = "x86_64", target_arch = "riscv64"))]
66use crate::arch::mm::paging::HugePageSize;
67pub use crate::arch::mm::paging::virtual_to_physical;
68use crate::arch::mm::paging::{BasePageSize, LargePageSize, PageSize};
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 min_mem = (npage_3tables + npage_2tables + npage_1tables) * BasePageSize::SIZE as usize
139 + 2 * LargePageSize::SIZE as usize;
140 #[cfg(any(target_arch = "x86_64", target_arch = "riscv64"))]
141 let has_1gib_pages = arch::kernel::processor::supports_1gib_pages();
142 let has_2mib_pages = arch::kernel::processor::supports_2mib_pages();
143
144 info!("Minimum memory size: {} MiB", min_mem >> 20);
145 let avail_mem = total_mem
146 .checked_sub(min_mem)
147 .unwrap_or_else(|| panic!("Not enough memory available!"))
148 .align_down(LargePageSize::SIZE as usize);
149
150 let mut map_addr;
151 let mut map_size;
152 let heap_start_addr;
153
154 #[cfg(feature = "common-os")]
155 {
156 info!("Using Hermit as common OS!");
157
158 let reserve: usize = (avail_mem * 75) / 100;
160 let reserve = core::cmp::min(reserve, 0x0400_0000);
162
163 let virt_size: usize = reserve.align_down(LargePageSize::SIZE as usize);
164 let layout = PageLayout::from_size_align(virt_size, LargePageSize::SIZE as usize).unwrap();
165 let page_range = PageAlloc::allocate(layout).unwrap();
166 let virt_addr = VirtAddr::from(page_range.start());
167 heap_start_addr = virt_addr;
168
169 info!(
170 "Heap: size {} MB, start address {:p}",
171 virt_size >> 20,
172 virt_addr
173 );
174
175 #[cfg(any(target_arch = "x86_64", target_arch = "riscv64"))]
176 if has_1gib_pages && virt_size > HugePageSize::SIZE as usize {
177 let npages = (virt_addr.align_up(HugePageSize::SIZE) - virt_addr) as usize
179 / LargePageSize::SIZE as usize;
180 if let Err(n) = paging::map_heap::<LargePageSize>(virt_addr, npages) {
181 map_addr = virt_addr + n as u64 * LargePageSize::SIZE;
182 map_size = virt_size - (map_addr - virt_addr) as usize;
183 } else {
184 map_addr = virt_addr.align_up(HugePageSize::SIZE);
185 map_size = virt_size - (map_addr - virt_addr) as usize;
186 }
187 } else {
188 map_addr = virt_addr;
189 map_size = virt_size;
190 }
191
192 #[cfg(not(any(target_arch = "x86_64", target_arch = "riscv64")))]
193 {
194 map_addr = virt_addr;
195 map_size = virt_size;
196 }
197 }
198
199 #[cfg(not(feature = "common-os"))]
200 {
201 #[cfg(not(feature = "mman"))]
203 let stack_reserve: usize = (avail_mem * 10) / 100;
204
205 #[cfg(not(feature = "mman"))]
210 let virt_size: usize = (avail_mem - stack_reserve).align_down(LargePageSize::SIZE as usize);
211 #[cfg(feature = "mman")]
212 let virt_size: usize = ((avail_mem * 75) / 100).align_down(LargePageSize::SIZE as usize);
213
214 let layout = PageLayout::from_size_align(virt_size, LargePageSize::SIZE as usize).unwrap();
215 let page_range = PageAlloc::allocate(layout).unwrap();
216 let virt_addr = VirtAddr::from(page_range.start());
217 heap_start_addr = virt_addr;
218
219 info!(
220 "Heap: size {} MB, start address {:p}",
221 virt_size >> 20,
222 virt_addr
223 );
224
225 #[cfg(any(target_arch = "x86_64", target_arch = "riscv64"))]
226 if has_1gib_pages && virt_size > HugePageSize::SIZE as usize {
227 let npages = (virt_addr.align_up(HugePageSize::SIZE) - virt_addr) / LargePageSize::SIZE;
229 if let Err(n) = paging::map_heap::<LargePageSize>(virt_addr, npages as usize) {
230 map_addr = virt_addr + n as u64 * LargePageSize::SIZE;
231 map_size = virt_size - (map_addr - virt_addr) as usize;
232 } else {
233 map_addr = virt_addr.align_up(HugePageSize::SIZE);
234 map_size = virt_size - (map_addr - virt_addr) as usize;
235 }
236 } else {
237 map_addr = virt_addr;
238 map_size = virt_size;
239 }
240
241 #[cfg(not(any(target_arch = "x86_64", target_arch = "riscv64")))]
242 {
243 map_addr = virt_addr;
244 map_size = virt_size;
245 }
246 }
247
248 #[cfg(any(target_arch = "x86_64", target_arch = "riscv64"))]
249 if has_1gib_pages
250 && map_size > HugePageSize::SIZE as usize
251 && map_addr.is_aligned_to(HugePageSize::SIZE)
252 {
253 let size = map_size.align_down(HugePageSize::SIZE as usize);
254 if let Err(num_pages) =
255 paging::map_heap::<HugePageSize>(map_addr, size / HugePageSize::SIZE as usize)
256 {
257 map_size -= num_pages * HugePageSize::SIZE as usize;
258 map_addr += num_pages as u64 * HugePageSize::SIZE;
259 } else {
260 map_size -= size;
261 map_addr += size;
262 }
263 }
264
265 if has_2mib_pages
266 && map_size > LargePageSize::SIZE as usize
267 && map_addr.is_aligned_to(LargePageSize::SIZE)
268 {
269 let size = map_size.align_down(LargePageSize::SIZE as usize);
270 if let Err(num_pages) =
271 paging::map_heap::<LargePageSize>(map_addr, size / LargePageSize::SIZE as usize)
272 {
273 map_size -= num_pages * LargePageSize::SIZE as usize;
274 map_addr += num_pages as u64 * LargePageSize::SIZE;
275 } else {
276 map_size -= size;
277 map_addr += size;
278 }
279 }
280
281 if map_size > BasePageSize::SIZE as usize && map_addr.is_aligned_to(BasePageSize::SIZE) {
282 let size = map_size.align_down(BasePageSize::SIZE as usize);
283 if let Err(num_pages) =
284 paging::map_heap::<BasePageSize>(map_addr, size / BasePageSize::SIZE as usize)
285 {
286 map_size -= num_pages * BasePageSize::SIZE as usize;
287 map_addr += num_pages as u64 * BasePageSize::SIZE;
288 } else {
289 map_size -= size;
290 map_addr += size;
291 }
292 }
293
294 let heap_end_addr = map_addr;
295
296 let size = heap_end_addr.as_usize() - heap_start_addr.as_usize();
297 unsafe {
298 ALLOCATOR
299 .lock()
300 .claim(heap_start_addr.as_mut_ptr(), size)
301 .unwrap();
302 }
303
304 info!("Heap is located at {heap_start_addr:p}..{heap_end_addr:p} ({map_size} Bytes unmapped)");
305}
306
307pub(crate) fn print_information() {
308 info!("{FrameAlloc}");
309 info!("{PageAlloc}");
310}
311
312#[cfg(feature = "pci")]
314pub(crate) fn map(
315 physical_address: PhysAddr,
316 size: usize,
317 writable: bool,
318 no_execution: bool,
319 no_cache: bool,
320) -> VirtAddr {
321 use crate::arch::mm::paging::PageTableEntryFlags;
322 #[cfg(target_arch = "x86_64")]
323 use crate::arch::mm::paging::PageTableEntryFlagsExt;
324
325 let size = size.align_up(BasePageSize::SIZE as usize);
326 let count = size / BasePageSize::SIZE as usize;
327
328 let mut flags = PageTableEntryFlags::empty();
329 flags.normal();
330 if writable {
331 flags.writable();
332 }
333 if no_execution {
334 flags.execute_disable();
335 }
336 if no_cache {
337 flags.device();
338 }
339
340 let layout = PageLayout::from_size(size).unwrap();
341 let page_range = PageAlloc::allocate(layout).unwrap();
342 let virtual_address = VirtAddr::from(page_range.start());
343 arch::mm::paging::map::<BasePageSize>(virtual_address, physical_address, count, flags);
344
345 virtual_address
346}
347
348#[allow(dead_code)]
349pub(crate) fn unmap(virtual_address: VirtAddr, size: usize) {
351 let size = size.align_up(BasePageSize::SIZE as usize);
352
353 if virtual_to_physical(virtual_address).is_some() {
354 arch::mm::paging::unmap::<BasePageSize>(
355 virtual_address,
356 size / BasePageSize::SIZE as usize,
357 );
358
359 let range = PageRange::from_start_len(virtual_address.as_usize(), size).unwrap();
360 unsafe {
361 PageAlloc::deallocate(range);
362 }
363 } else {
364 panic!("No page table entry for virtual address {virtual_address:p}");
365 }
366}