Skip to main content

hermit/syscalls/
mman.rs

1//! Memory management syscalls.
2//!
3//! These system calls are similar to [sys/mman.h].
4//!
5//! <div class="warning">These system calls are not very POSIX-like yet!</div>
6//!
7//! [sys/mman.h]: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/sys_mman.h.html
8
9use core::ffi::{c_int, c_void};
10
11use align_address::Align;
12use free_list::{FreeList, PageLayout, PageRange};
13use hermit_sync::SpinMutex;
14use memory_addresses::{PhysAddr, VirtAddr};
15
16use crate::arch;
17#[cfg(target_arch = "x86_64")]
18use crate::arch::mm::paging::PageTableEntryFlagsExt;
19use crate::arch::mm::paging::{BasePageSize, PageSize, PageTableEntryFlags};
20use crate::mm::{FrameAlloc, PageAlloc, PageRangeAllocator};
21
22bitflags! {
23	#[repr(transparent)]
24	#[derive(Debug, Copy, Clone, Default)]
25	pub struct MemoryProtection: u32 {
26		/// Pages may not be accessed.
27		const None = 0;
28		/// Indicates that the memory region should be readable.
29		const Read = 1 << 0;
30		/// Indicates that the memory region should be writable.
31		const Write = 1 << 1;
32		/// Indicates that the memory region should be executable.
33		const Exec = 1 << 2;
34	}
35}
36
37static PROT_NONE_FREE_LIST: SpinMutex<FreeList<16>> = SpinMutex::new(FreeList::new());
38
39/// Creates a new virtual memory mapping of the `size` specified with
40/// protection bits specified in `prot_flags`.
41#[hermit_macro::system(errno)]
42#[unsafe(no_mangle)]
43pub extern "C" fn sys_mmap(size: usize, prot_flags: MemoryProtection, ret: &mut *mut u8) -> i32 {
44	let size = size.align_up(BasePageSize::SIZE as usize);
45	let layout = PageLayout::from_size(size).unwrap();
46	let page_range = PageAlloc::allocate(layout).unwrap();
47	let virtual_address = VirtAddr::from(page_range.start());
48	if prot_flags.is_empty() {
49		*ret = virtual_address.as_mut_ptr();
50		unsafe {
51			PROT_NONE_FREE_LIST.lock().deallocate(page_range).unwrap();
52		}
53		return 0;
54	}
55	let frame_layout = PageLayout::from_size(size).unwrap();
56	let frame_range = FrameAlloc::allocate(frame_layout).unwrap();
57	let physical_address = PhysAddr::from(frame_range.start());
58
59	debug!("Mmap {physical_address:X} -> {virtual_address:X} ({size})");
60	let count = size / BasePageSize::SIZE as usize;
61	let mut flags = PageTableEntryFlags::empty();
62	flags.normal().writable();
63	if prot_flags.contains(MemoryProtection::Write) {
64		flags.writable();
65	}
66	if !prot_flags.contains(MemoryProtection::Exec) {
67		flags.execute_disable();
68	}
69
70	arch::mm::paging::map::<BasePageSize>(virtual_address, physical_address, count, flags);
71
72	*ret = virtual_address.as_mut_ptr();
73
74	0
75}
76
77/// Unmaps memory at the specified `ptr` for `size` bytes.
78#[hermit_macro::system(errno)]
79#[unsafe(no_mangle)]
80pub extern "C" fn sys_munmap(ptr: *mut u8, size: usize) -> i32 {
81	let virtual_address = VirtAddr::from_ptr(ptr);
82	let size = size.align_up(BasePageSize::SIZE as usize);
83	let page_range = PageRange::from_start_len(virtual_address.as_usize(), size).unwrap();
84
85	if PROT_NONE_FREE_LIST.lock().allocate_at(page_range).is_ok() {
86		return 0;
87	}
88
89	if let Some(physical_address) = arch::mm::paging::virtual_to_physical(virtual_address) {
90		arch::mm::paging::unmap::<BasePageSize>(
91			virtual_address,
92			size / BasePageSize::SIZE as usize,
93		);
94		debug!("Unmapping {virtual_address:X} ({size}) -> {physical_address:X}");
95
96		let frame_range =
97			PageRange::from_start_len(physical_address.as_u64() as usize, size).unwrap();
98		unsafe {
99			FrameAlloc::deallocate(frame_range);
100		}
101	}
102
103	unsafe {
104		PageAlloc::deallocate(page_range);
105	}
106
107	0
108}
109
110/// Configures the protections associated with a region of virtual memory
111/// starting at `ptr` and going to `size`.
112///
113/// Returns 0 on success and an error code on failure.
114#[hermit_macro::system(errno)]
115#[unsafe(no_mangle)]
116pub extern "C" fn sys_mprotect(ptr: *mut u8, size: usize, prot_flags: MemoryProtection) -> i32 {
117	let count = size / BasePageSize::SIZE as usize;
118	let mut flags = PageTableEntryFlags::empty();
119	flags.normal().writable();
120	if prot_flags.contains(MemoryProtection::Write) {
121		flags.writable();
122	}
123	if !prot_flags.contains(MemoryProtection::Exec) {
124		flags.execute_disable();
125	}
126
127	let virtual_address = VirtAddr::from_ptr(ptr);
128
129	debug!("Mprotect {virtual_address:X} ({size}) -> {prot_flags:?})");
130	if let Some(physical_address) = arch::mm::paging::virtual_to_physical(virtual_address) {
131		arch::mm::paging::map::<BasePageSize>(virtual_address, physical_address, count, flags);
132		0
133	} else {
134		let frame_layout = PageLayout::from_size(size).unwrap();
135		let frame_range = FrameAlloc::allocate(frame_layout).unwrap();
136		let physical_address = PhysAddr::from(frame_range.start());
137		arch::mm::paging::map::<BasePageSize>(virtual_address, physical_address, count, flags);
138		0
139	}
140}
141
142#[hermit_macro::system(errno)]
143#[unsafe(no_mangle)]
144pub extern "C" fn sys_mlock(_addr: *const c_void, _size: usize) -> i32 {
145	// Hermit does not do any swapping yet.
146	0
147}
148
149#[hermit_macro::system(errno)]
150#[unsafe(no_mangle)]
151pub extern "C" fn sys_munlock(_addr: *const c_void, _size: usize) -> i32 {
152	// Hermit does not do any swapping yet.
153	0
154}
155
156#[hermit_macro::system(errno)]
157#[unsafe(no_mangle)]
158pub extern "C" fn sys_mlockall(_flags: c_int) -> i32 {
159	// Hermit does not do any swapping yet.
160	0
161}
162
163#[hermit_macro::system(errno)]
164#[unsafe(no_mangle)]
165pub extern "C" fn sys_munlockall(_flags: c_int) -> i32 {
166	// Hermit does not do any swapping yet.
167	0
168}