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::normal();
61	flags
62		.set_writable(prot_flags.contains(MemoryProtection::Write))
63		.set_execute(prot_flags.contains(MemoryProtection::Exec));
64
65	paging::map::<BasePageSize>(virtual_address, physical_address, count, flags);
66
67	*ret = virtual_address.as_mut_ptr();
68
69	0
70}
71
72/// Unmaps memory at the specified `ptr` for `size` bytes.
73#[hermit_macro::system(errno)]
74#[unsafe(no_mangle)]
75pub extern "C" fn sys_munmap(ptr: *mut u8, size: usize) -> i32 {
76	let virtual_address = VirtAddr::from_ptr(ptr);
77	let size = size.align_up(BasePageSize::SIZE as usize);
78	let page_range = PageRange::from_start_len(virtual_address.as_usize(), size).unwrap();
79
80	if PROT_NONE_FREE_LIST.lock().allocate_at(page_range).is_ok() {
81		return 0;
82	}
83
84	if let Some(physical_address) = paging::virtual_to_physical(virtual_address) {
85		paging::unmap::<BasePageSize>(virtual_address, size / BasePageSize::SIZE as usize);
86		debug!("Unmapping {virtual_address:X} ({size}) -> {physical_address:X}");
87
88		let frame_range =
89			PageRange::from_start_len(physical_address.as_u64() as usize, size).unwrap();
90		unsafe {
91			FrameAlloc::deallocate(frame_range);
92		}
93	}
94
95	unsafe {
96		PageAlloc::deallocate(page_range);
97	}
98
99	0
100}
101
102/// Configures the protections associated with a region of virtual memory
103/// starting at `ptr` and going to `size`.
104///
105/// Returns 0 on success and an error code on failure.
106#[hermit_macro::system(errno)]
107#[unsafe(no_mangle)]
108pub extern "C" fn sys_mprotect(ptr: *mut u8, size: usize, prot_flags: MemoryProtection) -> i32 {
109	let count = size / BasePageSize::SIZE as usize;
110	let mut flags = PageTableEntryFlags::normal();
111	flags
112		.set_writable(prot_flags.contains(MemoryProtection::Write))
113		.set_execute(prot_flags.contains(MemoryProtection::Exec));
114
115	let virtual_address = VirtAddr::from_ptr(ptr);
116
117	debug!("Mprotect {virtual_address:X} ({size}) -> {prot_flags:?})");
118	if let Some(physical_address) = paging::virtual_to_physical(virtual_address) {
119		paging::map::<BasePageSize>(virtual_address, physical_address, count, flags);
120		0
121	} else {
122		let frame_layout = PageLayout::from_size(size).unwrap();
123		let frame_range = FrameAlloc::allocate(frame_layout).unwrap();
124		let physical_address = PhysAddr::from(frame_range.start());
125		paging::map::<BasePageSize>(virtual_address, physical_address, count, flags);
126		0
127	}
128}
129
130#[hermit_macro::system(errno)]
131#[unsafe(no_mangle)]
132pub extern "C" fn sys_mlock(_addr: *const c_void, _size: usize) -> i32 {
133	// Hermit does not do any swapping yet.
134	0
135}
136
137#[hermit_macro::system(errno)]
138#[unsafe(no_mangle)]
139pub extern "C" fn sys_munlock(_addr: *const c_void, _size: usize) -> i32 {
140	// Hermit does not do any swapping yet.
141	0
142}
143
144#[hermit_macro::system(errno)]
145#[unsafe(no_mangle)]
146pub extern "C" fn sys_mlockall(_flags: c_int) -> i32 {
147	// Hermit does not do any swapping yet.
148	0
149}
150
151#[hermit_macro::system(errno)]
152#[unsafe(no_mangle)]
153pub extern "C" fn sys_munlockall(_flags: c_int) -> i32 {
154	// Hermit does not do any swapping yet.
155	0
156}