Skip to content

Commit 88f882f

Browse files
committed
Mapper: Add map_to_with_table_flags method
Add a new map_to_with_table_flags method with an additional parent_table_flags argument to the flags of the parent table entries. map_to is kept without API changes as a convenience method. It automatically infers the parent table flags from the flags of the mapping and then calls into map_to_with_table_flags. To ensure that flags like NO_CACHE are not automatically set, which have a different meaning on higher level tables, a hardcoded mask of "safe" flags (PRESENT | WRITABLE | USER_ACCESSIBLE) is used. To keep the recursive page table working, PRESENT and WRITABLE flags for parent entries are also added.
1 parent 2de442d commit 88f882f

File tree

4 files changed

+257
-46
lines changed

4 files changed

+257
-46
lines changed

src/structures/paging/mapper/mapped_page_table.rs

+47-25
Original file line numberDiff line numberDiff line change
@@ -44,15 +44,18 @@ impl<'a, P: PhysToVirt> MappedPageTable<'a, P> {
4444
page: Page<Size1GiB>,
4545
frame: PhysFrame<Size1GiB>,
4646
flags: PageTableFlags,
47+
parent_table_flags: PageTableFlags,
4748
allocator: &mut A,
4849
) -> Result<MapperFlush<Size1GiB>, MapToError<Size1GiB>>
4950
where
5051
A: FrameAllocator<Size4KiB>,
5152
{
5253
let p4 = &mut self.level_4_table;
53-
let p3 = self
54-
.page_table_walker
55-
.create_next_table(&mut p4[page.p4_index()], allocator)?;
54+
let p3 = self.page_table_walker.create_next_table(
55+
&mut p4[page.p4_index()],
56+
parent_table_flags,
57+
allocator,
58+
)?;
5659

5760
if !p3[page.p3_index()].is_unused() {
5861
return Err(MapToError::PageAlreadyMapped(frame));
@@ -69,18 +72,23 @@ impl<'a, P: PhysToVirt> MappedPageTable<'a, P> {
6972
page: Page<Size2MiB>,
7073
frame: PhysFrame<Size2MiB>,
7174
flags: PageTableFlags,
75+
parent_table_flags: PageTableFlags,
7276
allocator: &mut A,
7377
) -> Result<MapperFlush<Size2MiB>, MapToError<Size2MiB>>
7478
where
7579
A: FrameAllocator<Size4KiB>,
7680
{
7781
let p4 = &mut self.level_4_table;
78-
let p3 = self
79-
.page_table_walker
80-
.create_next_table(&mut p4[page.p4_index()], allocator)?;
81-
let p2 = self
82-
.page_table_walker
83-
.create_next_table(&mut p3[page.p3_index()], allocator)?;
82+
let p3 = self.page_table_walker.create_next_table(
83+
&mut p4[page.p4_index()],
84+
parent_table_flags,
85+
allocator,
86+
)?;
87+
let p2 = self.page_table_walker.create_next_table(
88+
&mut p3[page.p3_index()],
89+
parent_table_flags,
90+
allocator,
91+
)?;
8492

8593
if !p2[page.p2_index()].is_unused() {
8694
return Err(MapToError::PageAlreadyMapped(frame));
@@ -97,21 +105,28 @@ impl<'a, P: PhysToVirt> MappedPageTable<'a, P> {
97105
page: Page<Size4KiB>,
98106
frame: PhysFrame<Size4KiB>,
99107
flags: PageTableFlags,
108+
parent_table_flags: PageTableFlags,
100109
allocator: &mut A,
101110
) -> Result<MapperFlush<Size4KiB>, MapToError<Size4KiB>>
102111
where
103112
A: FrameAllocator<Size4KiB>,
104113
{
105114
let p4 = &mut self.level_4_table;
106-
let p3 = self
107-
.page_table_walker
108-
.create_next_table(&mut p4[page.p4_index()], allocator)?;
109-
let p2 = self
110-
.page_table_walker
111-
.create_next_table(&mut p3[page.p3_index()], allocator)?;
112-
let p1 = self
113-
.page_table_walker
114-
.create_next_table(&mut p2[page.p2_index()], allocator)?;
115+
let p3 = self.page_table_walker.create_next_table(
116+
&mut p4[page.p4_index()],
117+
parent_table_flags,
118+
allocator,
119+
)?;
120+
let p2 = self.page_table_walker.create_next_table(
121+
&mut p3[page.p3_index()],
122+
parent_table_flags,
123+
allocator,
124+
)?;
125+
let p1 = self.page_table_walker.create_next_table(
126+
&mut p2[page.p2_index()],
127+
parent_table_flags,
128+
allocator,
129+
)?;
115130

116131
if !p1[page.p1_index()].is_unused() {
117132
return Err(MapToError::PageAlreadyMapped(frame));
@@ -124,17 +139,18 @@ impl<'a, P: PhysToVirt> MappedPageTable<'a, P> {
124139

125140
impl<'a, P: PhysToVirt> Mapper<Size1GiB> for MappedPageTable<'a, P> {
126141
#[inline]
127-
unsafe fn map_to<A>(
142+
unsafe fn map_to_with_table_flags<A>(
128143
&mut self,
129144
page: Page<Size1GiB>,
130145
frame: PhysFrame<Size1GiB>,
131146
flags: PageTableFlags,
147+
parent_table_flags: PageTableFlags,
132148
allocator: &mut A,
133149
) -> Result<MapperFlush<Size1GiB>, MapToError<Size1GiB>>
134150
where
135151
A: FrameAllocator<Size4KiB>,
136152
{
137-
self.map_to_1gib(page, frame, flags, allocator)
153+
self.map_to_1gib(page, frame, flags, parent_table_flags, allocator)
138154
}
139155

140156
fn unmap(
@@ -198,17 +214,18 @@ impl<'a, P: PhysToVirt> Mapper<Size1GiB> for MappedPageTable<'a, P> {
198214

199215
impl<'a, P: PhysToVirt> Mapper<Size2MiB> for MappedPageTable<'a, P> {
200216
#[inline]
201-
unsafe fn map_to<A>(
217+
unsafe fn map_to_with_table_flags<A>(
202218
&mut self,
203219
page: Page<Size2MiB>,
204220
frame: PhysFrame<Size2MiB>,
205221
flags: PageTableFlags,
222+
parent_table_flags: PageTableFlags,
206223
allocator: &mut A,
207224
) -> Result<MapperFlush<Size2MiB>, MapToError<Size2MiB>>
208225
where
209226
A: FrameAllocator<Size4KiB>,
210227
{
211-
self.map_to_2mib(page, frame, flags, allocator)
228+
self.map_to_2mib(page, frame, flags, parent_table_flags, allocator)
212229
}
213230

214231
fn unmap(
@@ -280,17 +297,18 @@ impl<'a, P: PhysToVirt> Mapper<Size2MiB> for MappedPageTable<'a, P> {
280297

281298
impl<'a, P: PhysToVirt> Mapper<Size4KiB> for MappedPageTable<'a, P> {
282299
#[inline]
283-
unsafe fn map_to<A>(
300+
unsafe fn map_to_with_table_flags<A>(
284301
&mut self,
285302
page: Page<Size4KiB>,
286303
frame: PhysFrame<Size4KiB>,
287304
flags: PageTableFlags,
305+
parent_table_flags: PageTableFlags,
288306
allocator: &mut A,
289307
) -> Result<MapperFlush<Size4KiB>, MapToError<Size4KiB>>
290308
where
291309
A: FrameAllocator<Size4KiB>,
292310
{
293-
self.map_to_4kib(page, frame, flags, allocator)
311+
self.map_to_4kib(page, frame, flags, parent_table_flags, allocator)
294312
}
295313

296314
fn unmap(
@@ -461,6 +479,7 @@ impl<P: PhysToVirt> PageTableWalker<P> {
461479
fn create_next_table<'b, A>(
462480
&self,
463481
entry: &'b mut PageTableEntry,
482+
insert_flags: PageTableFlags,
464483
allocator: &mut A,
465484
) -> Result<&'b mut PageTable, PageTableCreateError>
466485
where
@@ -470,12 +489,15 @@ impl<P: PhysToVirt> PageTableWalker<P> {
470489

471490
if entry.is_unused() {
472491
if let Some(frame) = allocator.allocate_frame() {
473-
entry.set_frame(frame, PageTableFlags::PRESENT | PageTableFlags::WRITABLE);
492+
entry.set_frame(frame, insert_flags);
474493
created = true;
475494
} else {
476495
return Err(PageTableCreateError::FrameAllocationFailed);
477496
}
478497
} else {
498+
if !insert_flags.is_empty() && !entry.flags().contains(insert_flags) {
499+
entry.set_flags(entry.flags() | insert_flags);
500+
}
479501
created = false;
480502
}
481503

src/structures/paging/mapper/mod.rs

+121
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,13 @@ pub trait Mapper<S: PageSize> {
8383
/// This function might need additional physical frames to create new page tables. These
8484
/// frames are allocated from the `allocator` argument. At most three frames are required.
8585
///
86+
/// Parent page table entries are automatically updated with `PRESENT | WRITABLE | USER_ACCESSIBLE`
87+
/// if present in the `PageTableFlags`. Depending on the used mapper implementation
88+
/// the `PRESENT` and `WRITABLE` flags might be set for parent tables,
89+
/// even if they are not set in `PageTableFlags`.
90+
///
91+
/// The `map_to_with_table_flags` method gives explicit control over the parent page table flags.
92+
///
8693
/// ## Safety
8794
///
8895
/// Creating page table mappings is a fundamentally unsafe operation because
@@ -111,13 +118,127 @@ pub trait Mapper<S: PageSize> {
111118
/// the same in all address spaces, otherwise undefined behavior can occur
112119
/// because of TLB races. It's worth noting that all the above requirements
113120
/// also apply to shared mappings, including the aliasing requirements.
121+
///
122+
/// # Examples
123+
///
124+
/// Create a USER_ACCESSIBLE mapping:
125+
///
126+
/// ```
127+
/// # use x86_64::structures::paging::{
128+
/// # Mapper, Page, PhysFrame, FrameAllocator,
129+
/// # Size4KiB, OffsetPageTable, page_table::PageTableFlags
130+
/// # };
131+
/// # unsafe fn test(mapper: &mut OffsetPageTable, frame_allocator: &mut impl FrameAllocator<Size4KiB>,
132+
/// # page: Page<Size4KiB>, frame: PhysFrame) {
133+
/// mapper
134+
/// .map_to(
135+
/// page,
136+
/// frame,
137+
/// PageTableFlags::PRESENT
138+
/// | PageTableFlags::WRITABLE
139+
/// | PageTableFlags::USER_ACCESSIBLE,
140+
/// frame_allocator,
141+
/// )
142+
/// .unwrap()
143+
/// .flush();
144+
/// # }
145+
/// ```
146+
#[inline]
114147
unsafe fn map_to<A>(
115148
&mut self,
116149
page: Page<S>,
117150
frame: PhysFrame<S>,
118151
flags: PageTableFlags,
119152
frame_allocator: &mut A,
120153
) -> Result<MapperFlush<S>, MapToError<S>>
154+
where
155+
Self: Sized,
156+
A: FrameAllocator<Size4KiB>,
157+
{
158+
let parent_table_flags = flags
159+
& (PageTableFlags::PRESENT
160+
| PageTableFlags::WRITABLE
161+
| PageTableFlags::USER_ACCESSIBLE);
162+
163+
self.map_to_with_table_flags(page, frame, flags, parent_table_flags, frame_allocator)
164+
}
165+
166+
/// Creates a new mapping in the page table.
167+
///
168+
/// This function might need additional physical frames to create new page tables. These
169+
/// frames are allocated from the `allocator` argument. At most three frames are required.
170+
///
171+
/// The flags of the parent table(s) can be explicitly specified. Those flags are used for
172+
/// newly created table entries, and for existing entries the flags are added.
173+
///
174+
/// Depending on the used mapper implementation, the `PRESENT` and `WRITABLE` flags might
175+
/// be set for parent tables, even if they are not specified in `parent_table_flags`.
176+
///
177+
/// ## Safety
178+
///
179+
/// Creating page table mappings is a fundamentally unsafe operation because
180+
/// there are various ways to break memory safety through it. For example,
181+
/// re-mapping an in-use page to a different frame changes and invalidates
182+
/// all values stored in that page, resulting in undefined behavior on the
183+
/// next use.
184+
///
185+
/// The caller must ensure that no undefined behavior or memory safety
186+
/// violations can occur through the new mapping. Among other things, the
187+
/// caller must prevent the following:
188+
///
189+
/// - Aliasing of `&mut` references, i.e. two `&mut` references that point to
190+
/// the same physical address. This is undefined behavior in Rust.
191+
/// - This can be ensured by mapping each page to an individual physical
192+
/// frame that is not mapped anywhere else.
193+
/// - Creating uninitalized or invalid values: Rust requires that all values
194+
/// have a correct memory layout. For example, a `bool` must be either a 0
195+
/// or a 1 in memory, but not a 3 or 4. An exception is the `MaybeUninit`
196+
/// wrapper type, which abstracts over possibly uninitialized memory.
197+
/// - This is only a problem when re-mapping pages to different physical
198+
/// frames. Mapping a page that is not in use yet is fine.
199+
///
200+
/// Special care must be taken when sharing pages with other address spaces,
201+
/// e.g. by setting the `GLOBAL` flag. For example, a global mapping must be
202+
/// the same in all address spaces, otherwise undefined behavior can occur
203+
/// because of TLB races. It's worth noting that all the above requirements
204+
/// also apply to shared mappings, including the aliasing requirements.
205+
///
206+
/// # Examples
207+
///
208+
/// Create USER_ACCESSIBLE | NO_EXECUTE | NO_CACHE mapping and update
209+
/// the top hierarchy only with USER_ACCESSIBLE:
210+
///
211+
/// ```
212+
/// # use x86_64::structures::paging::{
213+
/// # Mapper, PhysFrame, Page, FrameAllocator,
214+
/// # Size4KiB, OffsetPageTable, page_table::PageTableFlags
215+
/// # };
216+
/// # unsafe fn test(mapper: &mut OffsetPageTable, frame_allocator: &mut impl FrameAllocator<Size4KiB>,
217+
/// # page: Page<Size4KiB>, frame: PhysFrame) {
218+
/// mapper
219+
/// .map_to_with_table_flags(
220+
/// page,
221+
/// frame,
222+
/// PageTableFlags::PRESENT
223+
/// | PageTableFlags::WRITABLE
224+
/// | PageTableFlags::USER_ACCESSIBLE
225+
/// | PageTableFlags::NO_EXECUTE
226+
/// | PageTableFlags::NO_CACHE,
227+
/// PageTableFlags::USER_ACCESSIBLE,
228+
/// frame_allocator,
229+
/// )
230+
/// .unwrap()
231+
/// .flush();
232+
/// # }
233+
/// ```
234+
unsafe fn map_to_with_table_flags<A>(
235+
&mut self,
236+
page: Page<S>,
237+
frame: PhysFrame<S>,
238+
flags: PageTableFlags,
239+
parent_table_flags: PageTableFlags,
240+
frame_allocator: &mut A,
241+
) -> Result<MapperFlush<S>, MapToError<S>>
121242
where
122243
Self: Sized,
123244
A: FrameAllocator<Size4KiB>;

src/structures/paging/mapper/offset_page_table.rs

+15-7
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
#![cfg(target_arch = "x86_64")]
22

3-
use crate::structures::paging::{frame::PhysFrame, mapper::*, page_table::PageTable};
3+
use crate::structures::paging::{
4+
frame::PhysFrame, mapper::*, page_table::PageTable, Page, PageTableFlags,
5+
};
46

57
/// A Mapper implementation that requires that the complete physically memory is mapped at some
68
/// offset in the virtual address space.
@@ -54,17 +56,19 @@ impl PhysToVirt for PhysOffset {
5456

5557
impl<'a> Mapper<Size1GiB> for OffsetPageTable<'a> {
5658
#[inline]
57-
unsafe fn map_to<A>(
59+
unsafe fn map_to_with_table_flags<A>(
5860
&mut self,
5961
page: Page<Size1GiB>,
6062
frame: PhysFrame<Size1GiB>,
6163
flags: PageTableFlags,
64+
parent_table_flags: PageTableFlags,
6265
allocator: &mut A,
6366
) -> Result<MapperFlush<Size1GiB>, MapToError<Size1GiB>>
6467
where
6568
A: FrameAllocator<Size4KiB>,
6669
{
67-
self.inner.map_to(page, frame, flags, allocator)
70+
self.inner
71+
.map_to_with_table_flags(page, frame, flags, parent_table_flags, allocator)
6872
}
6973

7074
#[inline]
@@ -92,17 +96,19 @@ impl<'a> Mapper<Size1GiB> for OffsetPageTable<'a> {
9296

9397
impl<'a> Mapper<Size2MiB> for OffsetPageTable<'a> {
9498
#[inline]
95-
unsafe fn map_to<A>(
99+
unsafe fn map_to_with_table_flags<A>(
96100
&mut self,
97101
page: Page<Size2MiB>,
98102
frame: PhysFrame<Size2MiB>,
99103
flags: PageTableFlags,
104+
parent_table_flags: PageTableFlags,
100105
allocator: &mut A,
101106
) -> Result<MapperFlush<Size2MiB>, MapToError<Size2MiB>>
102107
where
103108
A: FrameAllocator<Size4KiB>,
104109
{
105-
self.inner.map_to(page, frame, flags, allocator)
110+
self.inner
111+
.map_to_with_table_flags(page, frame, flags, parent_table_flags, allocator)
106112
}
107113

108114
#[inline]
@@ -130,17 +136,19 @@ impl<'a> Mapper<Size2MiB> for OffsetPageTable<'a> {
130136

131137
impl<'a> Mapper<Size4KiB> for OffsetPageTable<'a> {
132138
#[inline]
133-
unsafe fn map_to<A>(
139+
unsafe fn map_to_with_table_flags<A>(
134140
&mut self,
135141
page: Page<Size4KiB>,
136142
frame: PhysFrame<Size4KiB>,
137143
flags: PageTableFlags,
144+
parent_table_flags: PageTableFlags,
138145
allocator: &mut A,
139146
) -> Result<MapperFlush<Size4KiB>, MapToError<Size4KiB>>
140147
where
141148
A: FrameAllocator<Size4KiB>,
142149
{
143-
self.inner.map_to(page, frame, flags, allocator)
150+
self.inner
151+
.map_to_with_table_flags(page, frame, flags, parent_table_flags, allocator)
144152
}
145153

146154
#[inline]

0 commit comments

Comments
 (0)