Skip to content

Commit 8d808d4

Browse files
committed
add range functions to Mapper
1 parent 9a0d5ff commit 8d808d4

File tree

1 file changed

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

1 file changed

+278
-4
lines changed

src/structures/paging/mapper/mod.rs

Lines changed: 278 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +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, FrameDeallocator},
11-
page::PageRangeInclusive,
12-
page_table::PageTableFlags,
13-
Page, PageSize, PhysFrame, Size1GiB, Size2MiB, Size4KiB,
10+
frame::PhysFrameRange, frame_alloc::FrameAllocator, page::PageRange,
11+
page_table::PageTableFlags, FrameDeallocator, Page, PageSize, PhysFrame, Size1GiB, Size2MiB,
12+
Size4KiB,
1413
};
1514
use crate::{PhysAddr, VirtAddr};
1615

@@ -195,6 +194,52 @@ pub trait Mapper<S: PageSize> {
195194
self.map_to_with_table_flags(page, frame, flags, parent_table_flags, frame_allocator)
196195
}
197196

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+
198243
/// Creates a new mapping in the page table.
199244
///
200245
/// This function might need additional physical frames to create new page tables. These
@@ -277,11 +322,147 @@ pub trait Mapper<S: PageSize> {
277322
Self: Sized,
278323
A: FrameAllocator<Size4KiB> + ?Sized;
279324

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+
280426
/// Removes a mapping from the page table and returns the frame that used to be mapped.
281427
///
282428
/// Note that no page tables or pages are deallocated.
283429
fn unmap(&mut self, page: Page<S>) -> Result<(PhysFrame<S>, MapperFlush<S>), UnmapError>;
284430

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+
285466
/// Updates the flags of an existing mapping.
286467
///
287468
/// ## Safety
@@ -297,6 +478,39 @@ pub trait Mapper<S: PageSize> {
297478
flags: PageTableFlags,
298479
) -> Result<MapperFlush<S>, FlagUpdateError>;
299480

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+
300514
/// Set the flags of an existing page level 4 table entry
301515
///
302516
/// ## Safety
@@ -370,6 +584,31 @@ pub trait Mapper<S: PageSize> {
370584
let page = Page::containing_address(VirtAddr::new(frame.start_address().as_u64()));
371585
self.map_to(page, frame, flags, frame_allocator)
372586
}
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+
}
373612
}
374613

375614
/// This type represents a page whose mapping has changed in the page table.
@@ -403,6 +642,41 @@ impl<S: PageSize> MapperFlush<S> {
403642
pub fn ignore(self) {}
404643
}
405644

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

0 commit comments

Comments
 (0)