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(())
}