Skip to content

Commit 0fb8373

Browse files
committed
add range functions to Mapper
1 parent 0ad7789 commit 0fb8373

File tree

1 file changed

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

1 file changed

+277
-1
lines changed

src/structures/paging/mapper/mod.rs

Lines changed: 277 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
};
@@ -195,6 +196,52 @@ pub trait Mapper<S: PageSize> {
195196
self.map_to_with_table_flags(page, frame, flags, parent_table_flags, frame_allocator)
196197
}
197198

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

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

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

483+
/// Updates the flags of a range of existing mappings.
484+
///
485+
/// ## Safety
486+
///
487+
/// This method is unsafe because changing the flags of a mapping
488+
/// might result in undefined behavior. For example, setting the
489+
/// `GLOBAL` and `WRITABLE` flags for a page might result in the corruption
490+
/// of values stored in that page from processes running in other address
491+
/// spaces.
492+
///
493+
/// ## Errors
494+
/// If an error occurs half-way through a [`MapperFlushRange<S>`] is returned that contains the pages that were successfully updated.
495+
unsafe fn update_flags_range(
496+
&mut self,
497+
pages: PageRange<S>,
498+
flags: PageTableFlags,
499+
) -> Result<MapperFlushRange<S>, (FlagUpdateError, MapperFlushRange<S>)> {
500+
pages
501+
.clone()
502+
.try_for_each(|page| {
503+
self.update_flags(page, flags).map(|_| ()).map_err(|e| {
504+
(
505+
e,
506+
MapperFlushRange::new(PageRange {
507+
start: pages.start,
508+
end: page,
509+
}),
510+
)
511+
})
512+
})
513+
.map(|_| MapperFlushRange::new(pages))
514+
}
515+
300516
/// Set the flags of an existing page level 4 table entry
301517
///
302518
/// ## Safety
@@ -370,6 +586,31 @@ pub trait Mapper<S: PageSize> {
370586
let page = Page::containing_address(VirtAddr::new(frame.start_address().as_u64()));
371587
self.map_to(page, frame, flags, frame_allocator)
372588
}
589+
590+
/// Maps the given range of frames to the range of virtual pages with the same address.
591+
///
592+
/// ## Safety
593+
///
594+
/// This is a convencience function that invokes [`Mapper::map_to_range`] internally, so
595+
/// all safety requirements of it also apply for this function.
596+
#[inline]
597+
unsafe fn identity_map_range<A>(
598+
&mut self,
599+
frames: PhysFrameRange<S>,
600+
flags: PageTableFlags,
601+
frame_allocator: &mut A,
602+
) -> Result<MapperFlushRange<S>, (MapToError<S>, MapperFlushRange<S>)>
603+
where
604+
Self: Sized,
605+
A: FrameAllocator<Size4KiB> + ?Sized,
606+
S: PageSize,
607+
Self: Mapper<S>,
608+
{
609+
let start = Page::containing_address(VirtAddr::new(frames.start.start_address().as_u64()));
610+
let end = Page::containing_address(VirtAddr::new(frames.end.start_address().as_u64()));
611+
let pages = PageRange { start, end };
612+
self.map_to_range(pages, frames, flags, frame_allocator)
613+
}
373614
}
374615

375616
/// This type represents a page whose mapping has changed in the page table.
@@ -403,6 +644,41 @@ impl<S: PageSize> MapperFlush<S> {
403644
pub fn ignore(self) {}
404645
}
405646

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

0 commit comments

Comments
 (0)