1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
// Copyright 2016 Philipp Oppermann. See the README.md
// file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use core::mem::ManuallyDrop;
use log::error;
use super::{
AllocatedPages, AllocatedFrames, PageTable, MappedPages, VirtualAddress,
table::{Table, Level1},
};
use pte_flags::PteFlagsArch;
use kernel_config::memory::{TEMPORARY_PAGE_VIRT_ADDR, PAGE_SIZE};
use owned_borrowed_trait::Owned;
/// A page that can be temporarily mapped to the recursive page table frame,
/// used for purposes of editing a top-level (P4) page table itself.
///
/// See how recursive paging works: <https://wiki.osdev.org/Page_Tables#Recursive_mapping>
///
/// # Note
/// Instead of just dropping a `TemporaryPage` object,
/// it should be cleaned up (reclaimed) using [`TemporaryPage::unmap_into_parts()`].
pub struct TemporaryPage {
mapped_page: MappedPages,
/// `ManuallyDrop` is required here in order to prevent this `AllocatedFrames`
/// from being dropped twice: once when unmapping the above `mapped_page`, and
/// once when dropping this `TemporaryPage`.
/// This is because the `AllocatedFrames` object here is the same one that is
/// mapped by the above `mapped_page`.
frame: ManuallyDrop<AllocatedFrames>,
}
impl TemporaryPage {
/// Creates a new [`TemporaryPage`] and maps it to the given page table `frame`
/// in the active table.
///
/// # Arguments
/// * `page`: the optional page that will be used for the temporary mapping.
/// If `None`, a new page will be allocated.
/// * `frame`: the single frame containing the page table that we want to modify,
/// which will be mapped to this [`TemporaryPage`].
/// * `page_table`: the currently active [`PageTable`].
pub fn create_and_map_table_frame(
mut page: Option<AllocatedPages>,
frame: AllocatedFrames,
page_table: &mut PageTable,
) -> Result<TemporaryPage, &'static str> {
let mut vaddr = VirtualAddress::new_canonical(TEMPORARY_PAGE_VIRT_ADDR);
while page.is_none() && vaddr.value() != 0 {
page = page_allocator::allocate_pages_at(vaddr, 1).ok();
vaddr -= PAGE_SIZE;
}
let (mapped_page, frame) = page_table.internal_map_to(
page.ok_or("Couldn't allocate a new Page for the temporary P4 table frame")?,
Owned(frame),
PteFlagsArch::new().valid(true).writable(true),
)?;
Ok(TemporaryPage {
mapped_page,
frame: ManuallyDrop::new(frame),
})
}
/// Invokes the given closure `f` with a mutable reference to the root P4 page table
/// `Table` and `AllocatedFrame` held in this `TemporaryPage`.
pub fn with_table_and_frame<F, R>(
&mut self,
f: F,
) -> Result<R, &'static str>
where F: FnOnce(&mut Table<Level1>, &AllocatedFrames) -> R
{
let res = f(
self.mapped_page.as_type_mut(0)?,
&self.frame,
);
Ok(res)
}
/// Call this to clean up a `TemporaryPage` instead of just letting it be dropped.
///
/// A simple wrapper around [`MappedPages::unmap_into_parts()`].
pub fn unmap_into_parts(mut self, page_table: &mut PageTable) -> Result<(AllocatedPages, Option<AllocatedFrames>), &'static str> {
let mp = core::mem::replace(&mut self.mapped_page, MappedPages::empty());
mp.unmap_into_parts(page_table).map_err(|e_mp| {
error!("TemporaryPage::unmap_into_parts(): failed to unmap internal {:?}", e_mp);
"BUG: TemporaryPage::unmap_into_parts(): failed to unmap internal MappedPages into its parts."
})
}
}