Skip to content

Commit a5056bf

Browse files
authored
Merge pull request raspberrypi#653 from wedsonaf/vma
rust: introduce `mm` module and vma abstraction for use with `mmap`
2 parents caa5a6f + 6df5d55 commit a5056bf

File tree

5 files changed

+178
-41
lines changed

5 files changed

+178
-41
lines changed

drivers/android/process.rs

Lines changed: 17 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,13 @@
22

33
use core::{convert::TryFrom, mem::take, ops::Range};
44
use kernel::{
5-
bindings, c_types,
5+
bindings,
66
cred::Credential,
77
file::File,
88
file_operations::{FileOperations, IoctlCommand, IoctlHandler, PollTable},
99
io_buffer::{IoBufferReader, IoBufferWriter},
1010
linked_list::List,
11+
mm,
1112
pages::Pages,
1213
prelude::*,
1314
rbtree::RBTree,
@@ -577,32 +578,29 @@ impl Process {
577578
}
578579
}
579580

580-
fn create_mapping(&self, vma: &mut bindings::vm_area_struct) -> Result {
581-
let size = core::cmp::min(
582-
(vma.vm_end - vma.vm_start) as usize,
583-
bindings::SZ_4M as usize,
584-
);
585-
let page_count = size >> bindings::PAGE_SHIFT;
581+
fn create_mapping(&self, vma: &mut mm::virt::Area) -> Result {
582+
let size = core::cmp::min(vma.end() - vma.start(), bindings::SZ_4M as usize);
583+
let page_count = size / kernel::PAGE_SIZE;
586584

587585
// Allocate and map all pages.
588586
//
589587
// N.B. If we fail halfway through mapping these pages, the kernel will unmap them.
590588
let mut pages = Vec::new();
591589
pages.try_reserve_exact(page_count)?;
592-
let mut address = vma.vm_start as usize;
590+
let mut address = vma.start();
593591
for _ in 0..page_count {
594592
let page = Pages::<0>::new()?;
595-
page.insert_page(vma, address)?;
593+
vma.insert_page(address, &page)?;
596594
pages.try_push(page)?;
597-
address += 1 << bindings::PAGE_SHIFT;
595+
address += kernel::PAGE_SIZE;
598596
}
599597

600598
let ref_pages = Ref::try_from(pages)?;
601599

602600
// Save pages for later.
603601
let mut inner = self.inner.lock();
604602
match &inner.mapping {
605-
None => inner.mapping = Some(Mapping::new(vma.vm_start as _, size, ref_pages)?),
603+
None => inner.mapping = Some(Mapping::new(vma.start(), size, ref_pages)?),
606604
Some(_) => return Err(Error::EBUSY),
607605
}
608606
Ok(())
@@ -905,26 +903,25 @@ impl FileOperations for Process {
905903
cmd.dispatch::<Self>(this, file)
906904
}
907905

908-
fn mmap(
909-
this: RefBorrow<'_, Process>,
910-
_file: &File,
911-
vma: &mut bindings::vm_area_struct,
912-
) -> Result {
906+
fn mmap(this: RefBorrow<'_, Process>, _file: &File, vma: &mut mm::virt::Area) -> Result {
913907
// We don't allow mmap to be used in a different process.
914908
if !Task::current().group_leader().eq(&this.task) {
915909
return Err(Error::EINVAL);
916910
}
917911

918-
if vma.vm_start == 0 {
912+
if vma.start() == 0 {
919913
return Err(Error::EINVAL);
920914
}
921915

922-
if (vma.vm_flags & (bindings::VM_WRITE as c_types::c_ulong)) != 0 {
916+
let mut flags = vma.flags();
917+
use mm::virt::flags::*;
918+
if flags & WRITE != 0 {
923919
return Err(Error::EPERM);
924920
}
925921

926-
vma.vm_flags |= (bindings::VM_DONTCOPY | bindings::VM_MIXEDMAP) as c_types::c_ulong;
927-
vma.vm_flags &= !(bindings::VM_MAYWRITE as c_types::c_ulong);
922+
flags |= DONTCOPY | MIXEDMAP;
923+
flags &= !MAYWRITE;
924+
vma.set_flags(flags);
928925

929926
// TODO: Set ops. We need to learn when the user unmaps so that we can stop using it.
930927
this.create_mapping(vma)

rust/kernel/file_operations.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use crate::{
1010
file::{File, FileRef},
1111
io_buffer::{IoBufferReader, IoBufferWriter},
1212
iov_iter::IovIter,
13+
mm,
1314
sync::CondVar,
1415
types::PointerWrapper,
1516
user_ptr::{UserSlicePtr, UserSlicePtrReader, UserSlicePtrWriter},
@@ -288,7 +289,14 @@ impl<A: FileOpenAdapter<T::OpenData>, T: FileOperations> FileOperationsVtable<A,
288289
// references to `file` have been released, so we know it can't be called while this
289290
// function is running.
290291
let f = unsafe { T::Wrapper::borrow((*file).private_data) };
291-
T::mmap(f, unsafe { &FileRef::from_ptr(file) }, unsafe { &mut *vma })?;
292+
293+
// SAFETY: The C API guarantees that `vma` is valid for the duration of this call.
294+
// `area` only lives within this call, so it is guaranteed to be valid.
295+
let mut area = unsafe { mm::virt::Area::from_ptr(vma) };
296+
297+
// SAFETY: The C API guarantees that `file` is valid for the duration of this call,
298+
// which is longer than the lifetime of the file reference.
299+
T::mmap(f, unsafe { &FileRef::from_ptr(file) }, &mut area)?;
292300
Ok(0)
293301
}
294302
}
@@ -707,7 +715,7 @@ pub trait FileOperations {
707715
fn mmap(
708716
_this: <Self::Wrapper as PointerWrapper>::Borrowed<'_>,
709717
_file: &File,
710-
_vma: &mut bindings::vm_area_struct,
718+
_vma: &mut mm::virt::Area,
711719
) -> Result {
712720
Err(Error::EINVAL)
713721
}

rust/kernel/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ pub mod file_operations;
5757
pub mod gpio;
5858
pub mod irq;
5959
pub mod miscdev;
60+
pub mod mm;
6061
pub mod pages;
6162
pub mod power;
6263
pub mod revocable;

rust/kernel/mm.rs

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
//! Memory management.
4+
//!
5+
//! C header: [`include/linux/mm.h`](../../../../include/linux/mm.h)
6+
7+
use crate::{bindings, pages, to_result, Result};
8+
9+
/// Virtual memory.
10+
pub mod virt {
11+
use super::*;
12+
13+
/// A wrapper for the kernel's `struct vm_area_struct`.
14+
///
15+
/// It represents an area of virtual memory.
16+
///
17+
/// # Invariants
18+
///
19+
/// `vma` is always non-null and valid.
20+
pub struct Area {
21+
vma: *mut bindings::vm_area_struct,
22+
}
23+
24+
impl Area {
25+
/// Creates a new instance of a virtual memory area.
26+
///
27+
/// # Safety
28+
///
29+
/// Callers must ensure that `vma` is non-null and valid for the duration of the new area's
30+
/// lifetime.
31+
pub(crate) unsafe fn from_ptr(vma: *mut bindings::vm_area_struct) -> Self {
32+
// INVARIANTS: The safety requirements guarantee the invariants.
33+
Self { vma }
34+
}
35+
36+
/// Returns the flags associated with the virtual memory area.
37+
///
38+
/// The possible flags are a combination of the constants in [`flags`].
39+
pub fn flags(&self) -> usize {
40+
// SAFETY: `self.vma` is valid by the type invariants.
41+
unsafe { (*self.vma).vm_flags as _ }
42+
}
43+
44+
/// Sets the flags associated with the virtual memory area.
45+
///
46+
/// The possible flags are a combination of the constants in [`flags`].
47+
pub fn set_flags(&mut self, flags: usize) {
48+
// SAFETY: `self.vma` is valid by the type invariants.
49+
unsafe { (*self.vma).vm_flags = flags as _ };
50+
}
51+
52+
/// Returns the start address of the virtual memory area.
53+
pub fn start(&self) -> usize {
54+
// SAFETY: `self.vma` is valid by the type invariants.
55+
unsafe { (*self.vma).vm_start as _ }
56+
}
57+
58+
/// Returns the end address of the virtual memory area.
59+
pub fn end(&self) -> usize {
60+
// SAFETY: `self.vma` is valid by the type invariants.
61+
unsafe { (*self.vma).vm_end as _ }
62+
}
63+
64+
/// Maps a single page at the given address within the virtual memory area.
65+
pub fn insert_page(&mut self, address: usize, page: &pages::Pages<0>) -> Result {
66+
// SAFETY: The page is guaranteed to be order 0 by the type system. The range of
67+
// `address` is already checked by `vm_insert_page`. `self.vma` and `page.pages` are
68+
// guaranteed by their repective type invariants to be valid.
69+
to_result(|| unsafe { bindings::vm_insert_page(self.vma, address as _, page.pages) })
70+
}
71+
}
72+
73+
/// Container for [`Area`] flags.
74+
pub mod flags {
75+
use crate::bindings;
76+
77+
/// No flags are set.
78+
pub const NONE: usize = bindings::VM_NONE as _;
79+
80+
/// Mapping allows reads.
81+
pub const READ: usize = bindings::VM_READ as _;
82+
83+
/// Mapping allows writes.
84+
pub const WRITE: usize = bindings::VM_WRITE as _;
85+
86+
/// Mapping allows execution.
87+
pub const EXEC: usize = bindings::VM_EXEC as _;
88+
89+
/// Mapping is shared.
90+
pub const SHARED: usize = bindings::VM_SHARED as _;
91+
92+
/// Mapping may be updated to allow reads.
93+
pub const MAYREAD: usize = bindings::VM_MAYREAD as _;
94+
95+
/// Mapping may be updated to allow writes.
96+
pub const MAYWRITE: usize = bindings::VM_MAYWRITE as _;
97+
98+
/// Mapping may be updated to allow execution.
99+
pub const MAYEXEC: usize = bindings::VM_MAYEXEC as _;
100+
101+
/// Mapping may be updated to be shared.
102+
pub const MAYSHARE: usize = bindings::VM_MAYSHARE as _;
103+
104+
/// Do not copy this vma on fork.
105+
pub const DONTCOPY: usize = bindings::VM_DONTCOPY as _;
106+
107+
/// Cannot expand with mremap().
108+
pub const DONTEXPAND: usize = bindings::VM_DONTEXPAND as _;
109+
110+
/// Lock the pages covered when they are faulted in.
111+
pub const LOCKONFAULT: usize = bindings::VM_LOCKONFAULT as _;
112+
113+
/// Is a VM accounted object.
114+
pub const ACCOUNT: usize = bindings::VM_ACCOUNT as _;
115+
116+
/// should the VM suppress accounting.
117+
pub const NORESERVE: usize = bindings::VM_NORESERVE as _;
118+
119+
/// Huge TLB Page VM.
120+
pub const HUGETLB: usize = bindings::VM_HUGETLB as _;
121+
122+
/// Synchronous page faults.
123+
pub const SYNC: usize = bindings::VM_SYNC as _;
124+
125+
/// Architecture-specific flag.
126+
pub const ARCH_1: usize = bindings::VM_ARCH_1 as _;
127+
128+
/// Wipe VMA contents in child..
129+
pub const WIPEONFORK: usize = bindings::VM_WIPEONFORK as _;
130+
131+
/// Do not include in the core dump.
132+
pub const DONTDUMP: usize = bindings::VM_DONTDUMP as _;
133+
134+
/// Not soft dirty clean area.
135+
pub const SOFTDIRTY: usize = bindings::VM_SOFTDIRTY as _;
136+
137+
/// Can contain "struct page" and pure PFN pages.
138+
pub const MIXEDMAP: usize = bindings::VM_MIXEDMAP as _;
139+
140+
/// MADV_HUGEPAGE marked this vma.
141+
pub const HUGEPAGE: usize = bindings::VM_HUGEPAGE as _;
142+
143+
/// MADV_NOHUGEPAGE marked this vma.
144+
pub const NOHUGEPAGE: usize = bindings::VM_NOHUGEPAGE as _;
145+
146+
/// KSM may merge identical pages.
147+
pub const MERGEABLE: usize = bindings::VM_MERGEABLE as _;
148+
}
149+
}

rust/kernel/pages.rs

Lines changed: 1 addition & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use core::{marker::PhantomData, ptr};
1919
///
2020
/// The pointer `Pages::pages` is valid and points to 2^ORDER pages.
2121
pub struct Pages<const ORDER: u32> {
22-
pages: *mut bindings::page,
22+
pub(crate) pages: *mut bindings::page,
2323
}
2424

2525
impl<const ORDER: u32> Pages<ORDER> {
@@ -40,24 +40,6 @@ impl<const ORDER: u32> Pages<ORDER> {
4040
Ok(Self { pages })
4141
}
4242

43-
/// Maps a single page at the given address in the given VM area.
44-
///
45-
/// This is only meant to be used by pages of order 0.
46-
pub fn insert_page(&self, vma: &mut bindings::vm_area_struct, address: usize) -> Result {
47-
if ORDER != 0 {
48-
return Err(Error::EINVAL);
49-
}
50-
51-
// SAFETY: We check above that the allocation is of order 0. The range of `address` is
52-
// already checked by `vm_insert_page`.
53-
let ret = unsafe { bindings::vm_insert_page(vma, address as _, self.pages) };
54-
if ret != 0 {
55-
Err(Error::from_kernel_errno(ret))
56-
} else {
57-
Ok(())
58-
}
59-
}
60-
6143
/// Copies data from the given [`UserSlicePtrReader`] into the pages.
6244
pub fn copy_into_page(
6345
&self,

0 commit comments

Comments
 (0)