Skip to content

Commit 8f7a050

Browse files
committed
add range functions to Mapper
1 parent d7e62d2 commit 8f7a050

File tree

1 file changed

+283
-1
lines changed
  • src/structures/paging/mapper

1 file changed

+283
-1
lines changed

src/structures/paging/mapper/mod.rs

Lines changed: 283 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,9 @@ pub use self::offset_page_table::OffsetPageTable;
77
pub use self::recursive_page_table::{InvalidPageTable, RecursivePageTable};
88

99
use crate::structures::paging::{
10+
frame::PhysFrameRange,
1011
frame_alloc::{FrameAllocator, FrameDeallocator},
11-
page::PageRangeInclusive,
12+
page::{PageRange, PageRangeInclusive},
1213
page_table::PageTableFlags,
1314
Page, PageSize, PhysFrame, Size1GiB, Size2MiB, Size4KiB,
1415
};
@@ -197,6 +198,52 @@ pub trait Mapper<S: PageSize> {
197198
}
198199
}
199200

201+
/// Maps the given range of frames to the range of virtual pages.
202+
///
203+
/// ## Safety
204+
///
205+
/// This is a convencience function that invokes [`Mapper::map_to`] internally, so
206+
/// all safety requirements of it also apply for this function.
207+
///
208+
/// ## Panics
209+
///
210+
/// This function panics if the amount of pages does not equal the amount of frames.
211+
///
212+
/// ## Errors
213+
///
214+
/// If an error occurs half-way through a [`MapperFlushRange<S>`] is returned that contains the frames that were successfully mapped.
215+
#[inline]
216+
unsafe fn map_to_range<A>(
217+
&mut self,
218+
pages: PageRange<S>,
219+
frames: PhysFrameRange<S>,
220+
flags: PageTableFlags,
221+
frame_allocator: &mut A,
222+
) -> Result<MapperFlushRange<S>, (MapToError<S>, MapperFlushRange<S>)>
223+
where
224+
Self: Sized,
225+
A: FrameAllocator<Size4KiB> + ?Sized,
226+
{
227+
assert_eq!(pages.count(), frames.count());
228+
229+
pages
230+
.zip(frames)
231+
.try_for_each(|(page, frame)| {
232+
unsafe { self.map_to(page, frame, flags, frame_allocator) }
233+
.map(|_| ())
234+
.map_err(|e| {
235+
(
236+
e,
237+
MapperFlushRange::new(PageRange {
238+
start: pages.start,
239+
end: page,
240+
}),
241+
)
242+
})
243+
})
244+
.map(|_| MapperFlushRange::new(pages))
245+
}
246+
200247
/// Creates a new mapping in the page table.
201248
///
202249
/// This function might need additional physical frames to create new page tables. These
@@ -279,11 +326,151 @@ pub trait Mapper<S: PageSize> {
279326
Self: Sized,
280327
A: FrameAllocator<Size4KiB> + ?Sized;
281328

329+
/// Maps the given range of frames to the range of virtual pages.
330+
///
331+
/// ## Safety
332+
///
333+
/// This is a convencience function that invokes [`Mapper::map_to_with_table_flags`] internally, so
334+
/// all safety requirements of it also apply for this function.
335+
///
336+
/// ## Panics
337+
///
338+
/// This function panics if the amount of pages does not equal the amount of frames.
339+
///
340+
/// ## Errors
341+
///
342+
/// If an error occurs half-way through a [`MapperFlushRange<S>`] is returned that contains the frames that were successfully mapped.
343+
unsafe fn map_to_range_with_table_flags<A>(
344+
&mut self,
345+
pages: PageRange<S>,
346+
frames: PhysFrameRange<S>,
347+
flags: PageTableFlags,
348+
parent_table_flags: PageTableFlags,
349+
frame_allocator: &mut A,
350+
) -> Result<MapperFlushRange<S>, (MapToError<S>, MapperFlushRange<S>)>
351+
where
352+
Self: Sized,
353+
A: FrameAllocator<Size4KiB> + ?Sized,
354+
{
355+
assert_eq!(pages.count(), frames.count());
356+
357+
pages
358+
.zip(frames)
359+
.try_for_each(|(page, frame)| {
360+
unsafe {
361+
self.map_to_with_table_flags(
362+
page,
363+
frame,
364+
flags,
365+
parent_table_flags,
366+
frame_allocator,
367+
)
368+
}
369+
.map(|_| ())
370+
.map_err(|e| {
371+
(
372+
e,
373+
MapperFlushRange::new(PageRange {
374+
start: pages.start,
375+
end: page,
376+
}),
377+
)
378+
})
379+
})
380+
.map(|_| MapperFlushRange::new(pages))
381+
}
382+
383+
/// Maps frames from the allocator to the given range of virtual pages.
384+
///
385+
/// ## Safety
386+
///
387+
/// This is a convencience function that invokes [`Mapper::map_to_with_table_flags`] internally, so
388+
/// all safety requirements of it also apply for this function.
389+
///
390+
/// ## Errors
391+
///
392+
/// If an error occurs half-way through a [`MapperFlushRange<S>`] is returned that contains the frames that were successfully mapped.
393+
unsafe fn map_range_with_table_flags<A>(
394+
&mut self,
395+
mut pages: PageRange<S>,
396+
flags: PageTableFlags,
397+
parent_table_flags: PageTableFlags,
398+
frame_allocator: &mut A,
399+
) -> Result<MapperFlushRange<S>, (MapToError<S>, MapperFlushRange<S>)>
400+
where
401+
Self: Sized,
402+
A: FrameAllocator<Size4KiB> + FrameAllocator<S> + ?Sized,
403+
{
404+
pages
405+
.try_for_each(|page| {
406+
let frame = frame_allocator
407+
.allocate_frame()
408+
.ok_or((MapToError::FrameAllocationFailed, page))?;
409+
410+
unsafe {
411+
self.map_to_with_table_flags(
412+
page,
413+
frame,
414+
flags,
415+
parent_table_flags,
416+
frame_allocator,
417+
)
418+
}
419+
.map(|_| ())
420+
.map_err(|e| (e, page))
421+
})
422+
.map(|_| MapperFlushRange::new(pages))
423+
.map_err(|(e, page)| {
424+
(
425+
e,
426+
MapperFlushRange::new(PageRange {
427+
start: pages.start,
428+
end: page,
429+
}),
430+
)
431+
})
432+
}
433+
282434
/// Removes a mapping from the page table and returns the frame that used to be mapped.
283435
///
284436
/// Note that no page tables or pages are deallocated.
285437
fn unmap(&mut self, page: Page<S>) -> Result<(PhysFrame<S>, MapperFlush<S>), UnmapError>;
286438

439+
/// Removes a range of mapping from the page table and deallocate the frames that used to be mapped.
440+
///
441+
/// Note that no page tables or pages are deallocated.
442+
///
443+
/// ## Errors
444+
/// If an error occurs half-way through a [`MapperFlushRange<S>`] is returned that contains the pages that were successfully unmapped.
445+
fn unmap_range<D>(
446+
&mut self,
447+
pages: PageRange<S>,
448+
deallocator: &mut D,
449+
) -> Result<MapperFlushRange<S>, (UnmapError, MapperFlushRange<S>)>
450+
where
451+
D: FrameDeallocator<S>,
452+
{
453+
pages
454+
.clone()
455+
.try_for_each(|page| {
456+
let (frame, _) = self.unmap(page).map_err(|e| {
457+
(
458+
e,
459+
MapperFlushRange::new(PageRange {
460+
start: pages.start,
461+
end: page,
462+
}),
463+
)
464+
})?;
465+
unsafe {
466+
// SAFETY: the page has been unmapped so the frame is unused
467+
deallocator.deallocate_frame(frame);
468+
}
469+
Ok(())
470+
})
471+
.map(|_| MapperFlushRange::new(pages))
472+
}
473+
287474
/// Updates the flags of an existing mapping.
288475
///
289476
/// ## Safety
@@ -299,6 +486,41 @@ pub trait Mapper<S: PageSize> {
299486
flags: PageTableFlags,
300487
) -> Result<MapperFlush<S>, FlagUpdateError>;
301488

489+
/// Updates the flags of a range of existing mappings.
490+
///
491+
/// ## Safety
492+
///
493+
/// This method is unsafe because changing the flags of a mapping
494+
/// might result in undefined behavior. For example, setting the
495+
/// `GLOBAL` and `WRITABLE` flags for a page might result in the corruption
496+
/// of values stored in that page from processes running in other address
497+
/// spaces.
498+
///
499+
/// ## Errors
500+
/// If an error occurs half-way through a [`MapperFlushRange<S>`] is returned that contains the pages that were successfully updated.
501+
unsafe fn update_flags_range(
502+
&mut self,
503+
pages: PageRange<S>,
504+
flags: PageTableFlags,
505+
) -> Result<MapperFlushRange<S>, (FlagUpdateError, MapperFlushRange<S>)> {
506+
pages
507+
.clone()
508+
.try_for_each(|page| {
509+
unsafe { self.update_flags(page, flags) }
510+
.map(|_| ())
511+
.map_err(|e| {
512+
(
513+
e,
514+
MapperFlushRange::new(PageRange {
515+
start: pages.start,
516+
end: page,
517+
}),
518+
)
519+
})
520+
})
521+
.map(|_| MapperFlushRange::new(pages))
522+
}
523+
302524
/// Set the flags of an existing page level 4 table entry
303525
///
304526
/// ## Safety
@@ -372,6 +594,31 @@ pub trait Mapper<S: PageSize> {
372594
let page = Page::containing_address(VirtAddr::new(frame.start_address().as_u64()));
373595
unsafe { self.map_to(page, frame, flags, frame_allocator) }
374596
}
597+
598+
/// Maps the given range of frames to the range of virtual pages with the same address.
599+
///
600+
/// ## Safety
601+
///
602+
/// This is a convencience function that invokes [`Mapper::map_to_range`] internally, so
603+
/// all safety requirements of it also apply for this function.
604+
#[inline]
605+
unsafe fn identity_map_range<A>(
606+
&mut self,
607+
frames: PhysFrameRange<S>,
608+
flags: PageTableFlags,
609+
frame_allocator: &mut A,
610+
) -> Result<MapperFlushRange<S>, (MapToError<S>, MapperFlushRange<S>)>
611+
where
612+
Self: Sized,
613+
A: FrameAllocator<Size4KiB> + ?Sized,
614+
S: PageSize,
615+
Self: Mapper<S>,
616+
{
617+
let start = Page::containing_address(VirtAddr::new(frames.start.start_address().as_u64()));
618+
let end = Page::containing_address(VirtAddr::new(frames.end.start_address().as_u64()));
619+
let pages = PageRange { start, end };
620+
unsafe { self.map_to_range(pages, frames, flags, frame_allocator) }
621+
}
375622
}
376623

377624
/// This type represents a page whose mapping has changed in the page table.
@@ -405,6 +652,41 @@ impl<S: PageSize> MapperFlush<S> {
405652
pub fn ignore(self) {}
406653
}
407654

655+
/// This type represents a range of pages whose mappings have changed in the page table.
656+
///
657+
/// The old mappings might be still cached in the translation lookaside buffer (TLB), so they need
658+
/// to be flushed from the TLB before they're accessed. This type is returned from a function that
659+
/// changed the mappings of a range of pages to ensure that the TLB flush is not forgotten.
660+
#[derive(Debug)]
661+
#[must_use = "Page Table changes must be flushed or ignored."]
662+
pub struct MapperFlushRange<S: PageSize>(PageRange<S>);
663+
664+
impl<S: PageSize> MapperFlushRange<S> {
665+
/// Create a new flush promise
666+
#[inline]
667+
fn new(pages: PageRange<S>) -> Self {
668+
MapperFlushRange(pages)
669+
}
670+
671+
/// Flush the page from the TLB to ensure that the newest mapping is used.
672+
#[cfg(feature = "instructions")]
673+
#[inline]
674+
pub fn flush(self) {
675+
for page in self.0 {
676+
crate::instructions::tlb::flush(page.start_address())
677+
}
678+
}
679+
680+
/// Don't flush the TLB and silence the “must be used” warning.
681+
#[inline]
682+
pub fn ignore(self) {}
683+
684+
/// Get the range of changed pages.
685+
pub fn pages(&self) -> PageRange<S> {
686+
self.0
687+
}
688+
}
689+
408690
/// This type represents a change of a page table requiring a complete TLB flush
409691
///
410692
/// The old mapping might be still cached in the translation lookaside buffer (TLB), so it needs

0 commit comments

Comments
 (0)