Skip to content

Commit 5dead71

Browse files
committed
implement sys_mremap
1 parent 3c0253e commit 5dead71

5 files changed

Lines changed: 422 additions & 30 deletions

File tree

etc/syscalls_linux_aarch64.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,7 @@
216216
| 0xd5 (213) | readahead | (int fd, loff_t offset, size_t count) | __arm64_sys_readahead | false |
217217
| 0xd6 (214) | brk | (unsigned long brk) | __arm64_sys_brk | true |
218218
| 0xd7 (215) | munmap | (unsigned long addr, size_t len) | __arm64_sys_munmap | true |
219-
| 0xd8 (216) | mremap | (unsigned long addr, unsigned long old_len, unsigned long new_len, unsigned long flags, unsigned long new_addr) | __arm64_sys_mremap | false |
219+
| 0xd8 (216) | mremap | (unsigned long addr, unsigned long old_len, unsigned long new_len, unsigned long flags, unsigned long new_addr) | __arm64_sys_mremap | true |
220220
| 0xd9 (217) | add_key | (const char *_type, const char *_description, const void *_payload, size_t plen, key_serial_t ringid) | __arm64_sys_add_key | false |
221221
| 0xda (218) | request_key | (const char *_type, const char *_description, const char *_callout_info, key_serial_t destringid) | __arm64_sys_request_key | false |
222222
| 0xdb (219) | keyctl | (int option, unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5) | __arm64_sys_keyctl | false |

libkernel/src/memory/proc_vm/memory_map/mod.rs

Lines changed: 273 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,17 @@ pub enum AddressRequest {
3737
},
3838
}
3939

40+
/// Describes where an `mremap` operation may place the remapped VMA.
41+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
42+
pub enum RemapDestination {
43+
/// Resize in place only.
44+
InPlaceOnly,
45+
/// Resize in place if possible, otherwise move to any free region.
46+
MayMove,
47+
/// Move the mapping to exactly this address.
48+
Fixed(VA),
49+
}
50+
4051
impl<AS: UserAddressSpace> MemoryMap<AS> {
4152
/// Creates a new, empty address space.
4253
pub fn new() -> Result<Self> {
@@ -243,6 +254,257 @@ impl<AS: UserAddressSpace> MemoryMap<AS> {
243254
Err(KernelError::NoMemory)
244255
}
245256

257+
/// Remaps an existing mapping
258+
pub fn mremap(
259+
&mut self,
260+
old_addr: VA,
261+
old_len: usize,
262+
new_len: usize,
263+
destination: RemapDestination,
264+
) -> Result<(VA, Vec<PageFrame>)> {
265+
if !old_addr.is_page_aligned() || old_len == 0 || new_len == 0 {
266+
return Err(KernelError::InvalidValue);
267+
}
268+
269+
let old_len = Self::align_len(old_len);
270+
let new_len = Self::align_len(new_len);
271+
let old_region = VirtMemoryRegion::new(old_addr, old_len);
272+
273+
let source_vma = self.find_vma(old_addr).cloned().ok_or(KernelError::Fault)?;
274+
275+
if old_region.end_address() > source_vma.region.end_address() {
276+
return Err(KernelError::Fault);
277+
}
278+
279+
if let RemapDestination::Fixed(new_addr) = destination {
280+
if !new_addr.is_page_aligned() {
281+
return Err(KernelError::InvalidValue);
282+
}
283+
284+
let new_region = VirtMemoryRegion::new(new_addr, new_len);
285+
286+
if new_region.overlaps(old_region) || new_region.overlaps(source_vma.region) {
287+
return Err(KernelError::InvalidValue);
288+
}
289+
}
290+
291+
if old_len == new_len && !matches!(destination, RemapDestination::Fixed(_)) {
292+
return Ok((old_addr, Vec::new()));
293+
}
294+
295+
if let RemapDestination::Fixed(new_addr) = destination {
296+
return self.move_selected_mapping(
297+
source_vma,
298+
old_region,
299+
VirtMemoryRegion::new(new_addr, new_len),
300+
true,
301+
);
302+
}
303+
304+
if new_len <= old_len {
305+
return self.shrink_in_place(source_vma, old_region, new_len);
306+
}
307+
308+
if self.can_expand_in_place(&source_vma, old_region, new_len) {
309+
return self.expand_in_place(source_vma, old_region, new_len);
310+
}
311+
312+
let new_region = match destination {
313+
RemapDestination::InPlaceOnly => return Err(KernelError::NoMemory),
314+
RemapDestination::MayMove => self
315+
.find_free_region(new_len)
316+
.ok_or(KernelError::NoMemory)?,
317+
RemapDestination::Fixed(_) => unreachable!(),
318+
};
319+
320+
self.move_selected_mapping(
321+
source_vma,
322+
old_region,
323+
new_region,
324+
matches!(destination, RemapDestination::Fixed(_)),
325+
)
326+
}
327+
328+
fn align_len(len: usize) -> usize {
329+
if len & PAGE_MASK != 0 {
330+
(len & !PAGE_MASK) + PAGE_SIZE
331+
} else {
332+
len
333+
}
334+
}
335+
336+
fn can_expand_in_place(
337+
&self,
338+
source_vma: &VMArea,
339+
old_region: VirtMemoryRegion,
340+
new_len: usize,
341+
) -> bool {
342+
let new_end = old_region.start_address().add_bytes(new_len);
343+
344+
if new_end <= source_vma.region.end_address() {
345+
return true;
346+
}
347+
348+
self.is_region_free(VirtMemoryRegion::from_start_end_address(
349+
source_vma.region.end_address(),
350+
new_end,
351+
))
352+
}
353+
354+
fn expand_in_place(
355+
&mut self,
356+
source_vma: VMArea,
357+
old_region: VirtMemoryRegion,
358+
new_len: usize,
359+
) -> Result<(VA, Vec<PageFrame>)> {
360+
let new_end = old_region.start_address().add_bytes(new_len);
361+
362+
if new_end <= source_vma.region.end_address() {
363+
return Ok((old_region.start_address(), Vec::new()));
364+
}
365+
366+
self.vmas
367+
.remove(&source_vma.region.start_address())
368+
.unwrap();
369+
370+
let mut expanded_vma = source_vma;
371+
expanded_vma.region =
372+
VirtMemoryRegion::from_start_end_address(expanded_vma.region.start_address(), new_end);
373+
374+
self.merge_vma(expanded_vma);
375+
376+
Ok((old_region.start_address(), Vec::new()))
377+
}
378+
379+
fn shrink_in_place(
380+
&mut self,
381+
source_vma: VMArea,
382+
old_region: VirtMemoryRegion,
383+
new_len: usize,
384+
) -> Result<(VA, Vec<PageFrame>)> {
385+
let new_region = VirtMemoryRegion::new(old_region.start_address(), new_len);
386+
let removed_region = VirtMemoryRegion::from_start_end_address(
387+
new_region.end_address(),
388+
old_region.end_address(),
389+
);
390+
391+
let freed_pages = self.address_space.unmap_range(removed_region)?;
392+
393+
self.vmas
394+
.remove(&source_vma.region.start_address())
395+
.unwrap();
396+
397+
if source_vma.region.start_address() < old_region.start_address() {
398+
self.merge_vma(
399+
source_vma.shrink_to(VirtMemoryRegion::from_start_end_address(
400+
source_vma.region.start_address(),
401+
old_region.start_address(),
402+
)),
403+
);
404+
}
405+
406+
self.merge_vma(source_vma.shrink_to(new_region));
407+
408+
if old_region.end_address() < source_vma.region.end_address() {
409+
self.merge_vma(
410+
source_vma.shrink_to(VirtMemoryRegion::from_start_end_address(
411+
old_region.end_address(),
412+
source_vma.region.end_address(),
413+
)),
414+
);
415+
}
416+
417+
Ok((old_region.start_address(), freed_pages))
418+
}
419+
420+
fn relocate_vma(vma: VMArea, new_region: VirtMemoryRegion) -> VMArea {
421+
let mut moved_vma = vma;
422+
moved_vma.region = new_region;
423+
424+
if let VMAreaKind::File(mapping) = &mut moved_vma.kind {
425+
mapping.len = core::cmp::min(mapping.len, new_region.size() as u64);
426+
}
427+
428+
moved_vma
429+
}
430+
431+
fn move_selected_mapping(
432+
&mut self,
433+
source_vma: VMArea,
434+
old_region: VirtMemoryRegion,
435+
new_region: VirtMemoryRegion,
436+
clobber_target: bool,
437+
) -> Result<(VA, Vec<PageFrame>)> {
438+
let mut freed_pages = Vec::new();
439+
440+
if clobber_target {
441+
freed_pages.append(&mut self.unmap_region(new_region, None)?);
442+
}
443+
444+
let preserved_len = core::cmp::min(old_region.size(), new_region.size());
445+
let mut newly_mapped = Vec::new();
446+
447+
if preserved_len != 0 {
448+
let preserved_old = VirtMemoryRegion::new(old_region.start_address(), preserved_len);
449+
let preserved_new = VirtMemoryRegion::new(new_region.start_address(), preserved_len);
450+
451+
for (old_page, new_page) in preserved_old.iter_pages().zip(preserved_new.iter_pages()) {
452+
if let Some(page_info) = self.address_space.translate(old_page) {
453+
if let Err(err) =
454+
self.address_space
455+
.map_page(page_info.pfn, new_page, page_info.perms)
456+
{
457+
for mapped_page in newly_mapped {
458+
let _ = self.address_space.unmap(mapped_page);
459+
}
460+
461+
return Err(err);
462+
}
463+
464+
newly_mapped.push(new_page);
465+
}
466+
}
467+
468+
let _ = self.address_space.unmap_range(preserved_old)?;
469+
}
470+
471+
if old_region.size() > preserved_len {
472+
freed_pages.append(&mut self.address_space.unmap_range(
473+
VirtMemoryRegion::from_start_end_address(
474+
old_region.start_address().add_bytes(preserved_len),
475+
old_region.end_address(),
476+
),
477+
)?);
478+
}
479+
480+
self.vmas
481+
.remove(&source_vma.region.start_address())
482+
.unwrap();
483+
484+
if source_vma.region.start_address() < old_region.start_address() {
485+
self.merge_vma(
486+
source_vma.shrink_to(VirtMemoryRegion::from_start_end_address(
487+
source_vma.region.start_address(),
488+
old_region.start_address(),
489+
)),
490+
);
491+
}
492+
493+
if old_region.end_address() < source_vma.region.end_address() {
494+
self.merge_vma(
495+
source_vma.shrink_to(VirtMemoryRegion::from_start_end_address(
496+
old_region.end_address(),
497+
source_vma.region.end_address(),
498+
)),
499+
);
500+
}
501+
502+
let selected_vma = source_vma.shrink_to(old_region);
503+
self.merge_vma(Self::relocate_vma(selected_vma, new_region));
504+
505+
Ok((new_region.start_address(), freed_pages))
506+
}
507+
246508
/// Checks if a given virtual memory region is completely free.
247509
fn is_region_free(&self, region: VirtMemoryRegion) -> bool {
248510
// Find the VMA that might overlap with the start of our desired region.
@@ -304,9 +566,12 @@ impl<AS: UserAddressSpace> MemoryMap<AS> {
304566

305567
/// Inserts a new VMA, handling overlaps and merging it with neighbors if
306568
/// possible.
307-
pub(super) fn insert_and_merge(&mut self, mut vma: VMArea) {
569+
pub(super) fn insert_and_merge(&mut self, vma: VMArea) {
308570
let _ = self.unmap_region(vma.region, Some(vma.clone()));
571+
self.merge_vma(vma);
572+
}
309573

574+
fn merge_vma(&mut self, mut vma: VMArea) {
310575
// Try to merge with next VMA.
311576
if let Some(next_vma) = self.vmas.get(&vma.region.end_address())
312577
&& vma.can_merge_with(next_vma)
@@ -320,24 +585,21 @@ impl<AS: UserAddressSpace> MemoryMap<AS> {
320585
.unwrap() // Should not fail, as we just got this VMA.
321586
.region;
322587
vma.region.expand_by(next_vma_region.size());
323-
// `vma` now represents the merged region of [new, next].
324588
}
325589

326590
// Try to merge with the previous VMA.
327591
if let Some((_key, prev_vma)) = self
328592
.vmas
329593
.range_mut(..vma.region.start_address())
330594
.next_back()
331-
{
332595
// Check if it's contiguous and compatible.
333-
if prev_vma.region.end_address() == vma.region.start_address()
334-
&& prev_vma.can_merge_with(&vma)
335-
{
336-
// The VMAs are mergeable. Expand the previous VMA to absorb the
337-
// new one's region.
338-
prev_vma.region.expand_by(vma.region.size());
339-
return;
340-
}
596+
&& prev_vma.region.end_address() == vma.region.start_address()
597+
&& prev_vma.can_merge_with(&vma)
598+
{
599+
// The VMAs are mergeable. Expand the previous VMA to absorb the
600+
// new one's region.
601+
prev_vma.region.expand_by(vma.region.size());
602+
return;
341603
}
342604

343605
// If we didn't merge into a previous VMA, insert the new (and possibly

src/arch/arm64/exceptions/syscall.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ use crate::{
5454
memory::{
5555
brk::sys_brk,
5656
mincore::sys_mincore,
57-
mmap::{sys_mmap, sys_mprotect, sys_munmap},
57+
mmap::{sys_mmap, sys_mprotect, sys_mremap, sys_munmap},
5858
process_vm::sys_process_vm_readv,
5959
},
6060
net::syscalls::{
@@ -674,6 +674,17 @@ pub async fn handle_syscall(mut ctx: ProcessCtx) {
674674
.await
675675
.map_err(|e| match e {}),
676676
0xd7 => sys_munmap(&ctx, VA::from_value(arg1 as usize), arg2 as _).await,
677+
0xd8 => {
678+
sys_mremap(
679+
&ctx,
680+
VA::from_value(arg1 as usize),
681+
arg2 as _,
682+
arg3 as _,
683+
arg4,
684+
VA::from_value(arg5 as usize),
685+
)
686+
.await
687+
}
677688
0xdc => {
678689
sys_clone(
679690
&ctx,
@@ -820,6 +831,7 @@ pub async fn handle_syscall(mut ctx: ProcessCtx) {
820831
ctx.task().ctx.user().elr_el1
821832
),
822833
};
834+
// log::info!("0x{nr:x} returned {res:?} at PC 0x{:x}", ctx.task().ctx.user().elr_el1);
823835

824836
let ret_val = match res {
825837
Ok(v) => v as isize,

0 commit comments

Comments
 (0)