hyperlight_guest/
host_function_call.rs

1/*
2Copyright 2024 The Hyperlight Authors.
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8    http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17use alloc::format;
18use alloc::string::ToString;
19use alloc::vec::Vec;
20use core::arch;
21
22use hyperlight_common::flatbuffer_wrappers::function_call::{FunctionCall, FunctionCallType};
23use hyperlight_common::flatbuffer_wrappers::function_types::{
24    ParameterValue, ReturnType, ReturnValue,
25};
26use hyperlight_common::flatbuffer_wrappers::guest_error::ErrorCode;
27use hyperlight_common::flatbuffer_wrappers::util::get_flatbuffer_result;
28use hyperlight_common::outb::OutBAction;
29
30use crate::error::{HyperlightGuestError, Result};
31use crate::shared_input_data::try_pop_shared_input_data_into;
32use crate::shared_output_data::push_shared_output_data;
33
34/// Get a return value from a host function call.
35/// This usually requires a host function to be called first using `call_host_function_internal`.
36/// When calling `call_host_function<T>`, this function is called internally to get the return
37/// value.
38pub fn get_host_return_value<T: TryFrom<ReturnValue>>() -> Result<T> {
39    let return_value = try_pop_shared_input_data_into::<ReturnValue>()
40        .expect("Unable to deserialize a return value from host");
41    T::try_from(return_value).map_err(|_| {
42        HyperlightGuestError::new(
43            ErrorCode::GuestError,
44            format!(
45                "Host return value was not a {} as expected",
46                core::any::type_name::<T>()
47            ),
48        )
49    })
50}
51
52/// Internal function to call a host function without generic type parameters.
53/// This is used by both the Rust and C APIs to reduce code duplication.
54///
55/// This function doesn't return the host function result directly, instead it just
56/// performs the call. The result must be obtained by calling `get_host_return_value`.
57pub fn call_host_function_internal(
58    function_name: &str,
59    parameters: Option<Vec<ParameterValue>>,
60    return_type: ReturnType,
61) -> Result<()> {
62    let host_function_call = FunctionCall::new(
63        function_name.to_string(),
64        parameters,
65        FunctionCallType::Host,
66        return_type,
67    );
68
69    let host_function_call_buffer: Vec<u8> = host_function_call
70        .try_into()
71        .expect("Unable to serialize host function call");
72
73    push_shared_output_data(host_function_call_buffer)?;
74
75    outb(OutBAction::CallFunction as u16, &[0]);
76
77    Ok(())
78}
79
80/// Call a host function with the given parameters and return type.
81/// This function serializes the function call and its parameters,
82/// sends it to the host, and then retrieves the return value.
83///
84/// The return value is deserialized into the specified type `T`.
85pub fn call_host_function<T: TryFrom<ReturnValue>>(
86    function_name: &str,
87    parameters: Option<Vec<ParameterValue>>,
88    return_type: ReturnType,
89) -> Result<T> {
90    call_host_function_internal(function_name, parameters, return_type)?;
91    get_host_return_value::<T>()
92}
93
94pub fn outb(port: u16, data: &[u8]) {
95    unsafe {
96        let mut i = 0;
97        while i < data.len() {
98            let remaining = data.len() - i;
99            let chunk_len = remaining.min(3);
100            let mut chunk = [0u8; 4];
101            chunk[0] = chunk_len as u8;
102            chunk[1..1 + chunk_len].copy_from_slice(&data[i..i + chunk_len]);
103            let val = u32::from_le_bytes(chunk);
104            out32(port, val);
105            i += chunk_len;
106        }
107    }
108}
109
110pub(crate) unsafe fn out32(port: u16, val: u32) {
111    arch::asm!("out dx, eax", in("dx") port, in("eax") val, options(preserves_flags, nomem, nostack));
112}
113
114/// Prints a message using `OutBAction::DebugPrint`. It transmits bytes of a message
115/// through several VMExists and, with such, it is slower than
116/// `print_output_with_host_print`.
117///
118/// This function should be used in debug mode only. This function does not
119/// require memory to be setup to be used.
120pub fn debug_print(msg: &str) {
121    outb(OutBAction::DebugPrint as u16, msg.as_bytes());
122}
123
124/// Print a message using the host's print function.
125///
126/// This function requires memory to be setup to be used. In particular, the
127/// existence of the input and output memory regions.
128pub fn print_output_with_host_print(function_call: &FunctionCall) -> Result<Vec<u8>> {
129    if let ParameterValue::String(message) = function_call.parameters.clone().unwrap()[0].clone() {
130        let res = call_host_function::<i32>(
131            "HostPrint",
132            Some(Vec::from(&[ParameterValue::String(message.to_string())])),
133            ReturnType::Int,
134        )?;
135
136        Ok(get_flatbuffer_result(res))
137    } else {
138        Err(HyperlightGuestError::new(
139            ErrorCode::GuestError,
140            "Wrong Parameters passed to print_output_with_host_print".to_string(),
141        ))
142    }
143}