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