Skip to content

Commit f3decbe

Browse files
committed
add range functions to Mapper
1 parent 33e8c3b commit f3decbe

File tree

1 file changed

+278
-2
lines changed
  • src/structures/paging/mapper

1 file changed

+278
-2
lines changed

src/structures/paging/mapper/mod.rs

Lines changed: 278 additions & 2 deletions
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_alloc::FrameAllocator, page_table::PageTableFlags, Page, PageSize, PhysFrame, Size1GiB,
11-
Size2MiB, Size4KiB,
10+
frame::PhysFrameRange, frame_alloc::FrameAllocator, page::PageRange,
11+
page_table::PageTableFlags, FrameDeallocator, Page, PageSize, PhysFrame, Size1GiB, Size2MiB,
12+
Size4KiB,
1213
};
1314
use crate::{PhysAddr, VirtAddr};
1415

@@ -193,6 +194,52 @@ pub trait Mapper<S: PageSize> {
193194
self.map_to_with_table_flags(page, frame, flags, parent_table_flags, frame_allocator)
194195
}
195196

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

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

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

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

373614
/// This type represents a page whose mapping has changed in the page table.
@@ -398,6 +639,41 @@ impl<S: PageSize> MapperFlush<S> {
398639
pub fn ignore(self) {}
399640
}
400641

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

0 commit comments

Comments
 (0)