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