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
//! Hypercalls for project 2.
//!
//! Hypercall is a software trap from the guest operating system to hypervisor, similar to the syscall from the application to kernel.
//! You can simply think hypercall as a "syscall" of the hypervisor and guest OS.
//!
//! In x86_64, guest OS can requests hypercall through the special instruction "vmcall".
//! When guest OS executes "vmcall" instruction, it first vmexits to the hypervisor.
//! After that, hypercall reads the registers and resolve the requested hypercall according to the its own abi for hypercall.
//! And the hypervisor serves the requests and pass the control back to the guest OS through the "vmresume" instruction.
//!
//! Both the project now and the project afterwards, you will use the following abis for hypercall.
//! %rax holds the hypercall number.
//! %rdi, %rsi, %rdx, %r10, %r9, %r8 are the first and second arguments, and so on.
//!
//! ## Hypercall interface
//! The core interface of hypercall is [`HypercallAbi`] and [`Hypercall`] traits.
//! When the vcpu executes the vmcall, it traps into the vmexit handler of the host operating system.
//! Then the vmexit control infrastructure of the kev forwards the given request to the [`Controller`] for the hypercall.
//! When the [`Controller`] found that the given request is a hypercall, it probes the CPU state and resolve the
//! information of the request through the [`Hypercall::resolve`]. After that the [`Controller`] passes the decoded
//! hypercall request to the [`HypercallAbi::handle`]. The [`HypercallAbi::handle`] then finally handles the given requests.
//!
//! ## Tasks
//! For this part, you are required to implement two hypercalls: the first halts the current vCPU, while the second prints a string to the console.
//! The detailed Application Binary Interface (ABI) for each hypercall can be founded in the [`Hypercall`] code section.
//! When you write to the console, you **MUST** proxy the console output through the [`PrinterProxy`].
//! Otherwise, grading script may be failed.
//!
//! [`HypercallAbi`]: crate::vmexit::hypercall::HypercallAbi
//! [`HypercallAbi::handle`]: crate::vmexit::hypercall::HypercallAbi::handle
//! [`Hypercall`]: crate::vmexit::hypercall::Hypercall
//! [`Hypercall::resolve`]: crate::vmexit::hypercall::Hypercall::resolve
//! [`Controller`]: crate::vmexit::hypercall::Controller
//! [`VmexitController`]: kev::vmexits::VmexitController
//! [`ExitReason`]: kev::vmcs::ExitReason
//! [`VmOps`]: kev::vm::VmOps
//!
use crate::{vmexit::hypercall, PrinterProxy};
use core::fmt::Write;
use kev::{
    vcpu::{GenericVCpuState, VmexitResult},
    vm::Gva,
    Probe, VmError,
};

/// Hypercall context.
pub struct HypercallCtx;
impl hypercall::HypercallAbi for HypercallCtx {
    type Call = Hypercall;

    fn handle<P: Probe>(
        &mut self,
        hc: Self::Call,
        p: &mut P,
        GenericVCpuState { vmcs, vm, .. }: &mut GenericVCpuState,
    ) -> Result<VmexitResult, VmError> {
        // Hint:
        //   - You can delegate exit request to the kev by returning `VmexitResult::Exited(0)`.
        //     You MUST not exit thread with [`keos::thread::with_current`] (Possibly leads deadlock.)
        //   - You can request vm to be exited by using trait [`kev::vm::VmOp`].
        //   - You can get &str through `core::str::from_utf8` and `core::slice::from_raw_parts`.
        //   - You MUST use write!(&mut crate::PrinterProxy, "{}", b) when writing to buffer.
        todo!()
    }
}

/// Supported hypercalls.
#[derive(Debug)]
pub enum Hypercall {
    /// Halt the vcpu with exitcode `code`.
    ///
    /// rax = 0.
    HaltCpu {
        /// Exit code. Provides on rdi.
        code: usize,
    },
    /// Print the message to the console.
    ///
    /// rax = 1.
    Print {
        /// Buffer to print. Provides on rdi.
        buf: Gva,
        /// Size of buffer to print. Provides on rsi.
        size: usize,
    },
}

impl hypercall::Hypercall for Hypercall {
    fn resolve(GenericVCpuState { gprs, .. }: &mut GenericVCpuState) -> Option<Self> {
        todo!()
    }
}