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 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163
//! Memory utility functions and types
//!
//! Consists of wrapper types representing different kinds of memory locations. The following diagram describes the conversions between them:
//!
//! ```text
//! ┌──────────────────┐ ┌───────────────┐
//! │MachineFrameNumber│◀─────────▶│PageFrameNumber│
//! └──────────────────┘ └───────────────┘
//! ▲ ▲ ▲
//! │ │ ┌─────────┐ │
//! │ └──────│PageEntry│───┐ │
//! │ └─────────┘ │ │
//! │ ▼ ▼
//! │ ┌──────────────┐ ┌──────────────┐
//! └────────────────────────▶│VirtualAddress│◀─────▶│MachineAddress│
//! └──────────────┘ └──────────────┘
//! ▲ ▲
//! │ │
//! │ │
//! ▼ │
//! ┌───────────────┐ │
//! │PhysicalAddress│◀─────────────┘
//! └───────────────┘
//! ```
use {
crate::{
hypercall,
platform::consts::{L1_PAGETABLE_SHIFT, PAGE_SIZE},
DOMID_SELF,
},
bitflags::bitflags,
core::convert::TryInto,
log::warn,
xen_sys::{
__HYPERVISOR_memory_op, __HYPERVISOR_mmu_update, __HYPERVISOR_update_va_mapping,
mmu_update_t,
},
};
pub mod page_table;
mod wrappers;
pub use wrappers::*;
/// Pointer to the beginning of the machine frame number list
///
/// Initialized with null pointer, this is probably really bad and **must** be set to the value of the `mfn_list` field of the start info structure before being used.
static mut MFN_LIST: *mut usize = core::ptr::null_mut();
/// MFN_LIST must be initialized before converting between PageFrameNumber and MachineFrameNumber
pub(crate) fn init_mfn_list(mfn_list_addr: usize) {
unsafe { MFN_LIST = mfn_list_addr as *mut usize }
}
/// Gives a page frame number after rounding the given address to the next page frame boundary
pub fn pfn_up(phys: PhysicalAddress) -> PageFrameNumber {
// no pointer arithmetic here, only usage of PFN_UP in mini-os is by passing the result of `to_phys` which casts to unsigned long
PageFrameNumber((phys.0 + PAGE_SIZE - 1) >> L1_PAGETABLE_SHIFT)
}
/// Memory operation commands
enum Command {
/// Returns the maximum machine frame number of mapped RAM in this system
MaximumRamPage = 2,
/// Returns the current memory reservation in pages of the specified domain
CurrentReservation = 3,
/// Returns the maximum memory reservation in pages of the specified domain
MaximumReservation = 4,
}
/// Perform memory operation
unsafe fn memory_op(cmd: Command, arg: u64) -> Result<u64, hypercall::Error> {
hypercall!(__HYPERVISOR_memory_op, cmd as u64, arg)
}
/// Gets the current number of reserved pages for the current domain
pub fn get_current_pages() -> Result<usize, hypercall::Error> {
unsafe {
memory_op(
Command::CurrentReservation,
(&DOMID_SELF) as *const _ as u64,
)
}
.map(|n| n.try_into().expect("Failed to convert u64 to usize"))
}
/// Gets the maximum number of reserved pages for the current domain
pub fn get_max_pages() -> Result<usize, hypercall::Error> {
unsafe {
memory_op(
Command::MaximumReservation,
(&DOMID_SELF) as *const _ as u64,
)
}
.map(|n| n.try_into().expect("Failed to convert u64 to usize"))
}
/// Gets the maximum machine frame number of mapped RAM in this system
pub fn get_max_machine_frame_number() -> MachineFrameNumber {
let mfn = unsafe { memory_op(Command::MaximumRamPage, 0) }
.expect("maximum_ram_page memory operation can never fail");
MachineFrameNumber(mfn.try_into().expect("Failed to convert u64 to usize"))
}
/// Updates an entry in a page table
pub fn hypervisor_mmu_update(reqs: &[mmu_update_t]) -> Result<(), hypercall::Error> {
let mut success_count = 0;
unsafe {
hypercall!(
__HYPERVISOR_mmu_update,
reqs.as_ptr() as u64,
reqs.len() as u64,
(&mut success_count) as *mut _ as u64,
DOMID_SELF
)
}?;
if success_count != reqs.len() {
warn!(
"MMU update had different number of successes to number of requests: {} != {}",
success_count,
reqs.len(),
)
}
Ok(())
}
bitflags! {
/// Flags for `update_va_mapping`
pub struct TLBFlushFlags: u64 {
/// No flushing at all.
const NONE = 0b000;
/// Flush entire TLB(s).
const FLUSH = 0b001;
/// Flush only one entry.
const INVLPG = 0b011;
/// Flush subset of TLBs.
const MULTI = 0b000;
/// Flush local TLB.
const LOCAL = 0b000;
/// Flush all TLBs.
const ALL = 0b100;
}}
/// Update virtual address mapping
pub fn update_va_mapping(
va: VirtualAddress,
page: PageEntry,
flags: TLBFlushFlags,
) -> Result<(), hypercall::Error> {
unsafe {
hypercall!(
__HYPERVISOR_update_va_mapping,
va.0 as u64,
page.0 as u64,
flags.bits()
)
}?;
Ok(())
}