Skip to main content

hermit/mm/
mod.rs

1//! Memory management.
2//!
3//! This is an overview of Hermit's memory layout:
4//!
5//! - `DeviceAlloc.device_offset` is 0 if `!cfg!(careful)`
6//! - User space virtual memory is only used if `!cfg!(feature = "common-os")`
7//! - On x86-64, PCI BARs, I/O APICs, and local APICs may be in `0xc0000000..0xffffffff`, which could be inside of `MEM`.
8//!
9//! ```text
10//!                               Virtual address
11//!                                    space
12//!
13//!                                 ...┌───┬──► 00000000
14//!           Physical address   ...   │   │
15//!                space      ...      │   │ Identity map
16//!                        ...         │   │
17//!    00000000 ◄──┬───┐...         ...├───┼──► mem_size
18//!                │   │   ...   ...   │   │
19//!     FrameAlloc │MEM│      ...      │   │ Unused
20//!                │   │   ...   ...   │   │
21//!    mem_size ◄──┼───┤...         ...├───┼──► DeviceAlloc.phys_offset
22//!                │   │   ...         │   │
23//!                │   │      ...      │   │ DeviceAlloc
24//!                │   │         ...   │   │
25//!          Empty │   │            ...├───┼──► DeviceAlloc.phys_offset + mem_size
26//!                │   │               │   │
27//!                │   │               │   │
28//!                │   │               │   │ Unused
29//!     Unknown ◄──┼───┤               │   │
30//!                │   │               │   │
31//!            PCI │   │               ├───┼──► kernel_virt_start
32//!                │   │               │   │
33//!     Unknown ◄──┼───┤               │   │ PageAlloc
34//!                │   │               │   │
35//!                │   │               ├───┼──► kernel_virt_end
36//!                │   │               │   │
37//!          Empty │   │               │   │
38//!                │   │               │   │ User space
39//!                │   │               │   │
40//!                │   │               │   │
41//! ```
42
43pub(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	// we reserve physical memory for the required page tables
119	// In worst case, we use page size of BasePageSize::SIZE
120	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		// we reserve at least 75% of the memory for the user space
146		let reserve: usize = (avail_mem * 75) / 100;
147		// 64 MB is enough as kernel heap
148		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			// Mount large pages to the next huge page boundary
165			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		// we reserve 10% of the memory for stack allocations
189		#[cfg(not(feature = "mman"))]
190		let stack_reserve: usize = (avail_mem * 10) / 100;
191
192		// At first, we map only a small part into the heap.
193		// Afterwards, we already use the heap and map the rest into
194		// the virtual address space.
195
196		#[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			// Mount large pages to the next huge page boundary
215			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/// Maps a given physical address and size in virtual space and returns address.
300#[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)]
336/// Unmaps virtual address, without 'freeing' physical memory it is mapped to!
337pub(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}