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::string::ToString;
18use alloc::vec::Vec;
19use core::arch::global_asm;
20
21use hyperlight_common::flatbuffer_wrappers::function_call::{FunctionCall, FunctionCallType};
22use hyperlight_common::flatbuffer_wrappers::function_types::{
23    ParameterValue, ReturnType, ReturnValue,
24};
25use hyperlight_common::flatbuffer_wrappers::guest_error::ErrorCode;
26use hyperlight_common::flatbuffer_wrappers::util::get_flatbuffer_result_from_int;
27use hyperlight_common::mem::RunMode;
28
29use crate::error::{HyperlightGuestError, Result};
30use crate::host_error::check_for_host_error;
31use crate::host_functions::validate_host_function_call;
32use crate::shared_input_data::try_pop_shared_input_data_into;
33use crate::shared_output_data::push_shared_output_data;
34use crate::{OUTB_PTR, OUTB_PTR_WITH_CONTEXT, P_PEB, RUNNING_MODE};
35
36pub enum OutBAction {
37    Log = 99,
38    CallFunction = 101,
39    Abort = 102,
40}
41
42pub fn get_host_value_return_as_void() -> Result<()> {
43    let return_value = try_pop_shared_input_data_into::<ReturnValue>()
44        .expect("Unable to deserialize a return value from host");
45    if let ReturnValue::Void = return_value {
46        Ok(())
47    } else {
48        Err(HyperlightGuestError::new(
49            ErrorCode::GuestError,
50            "Host return value was not void as expected".to_string(),
51        ))
52    }
53}
54
55pub fn get_host_value_return_as_int() -> Result<i32> {
56    let return_value = try_pop_shared_input_data_into::<ReturnValue>()
57        .expect("Unable to deserialize return value from host");
58
59    // check that return value is an int and return
60    if let ReturnValue::Int(i) = return_value {
61        Ok(i)
62    } else {
63        Err(HyperlightGuestError::new(
64            ErrorCode::GuestError,
65            "Host return value was not an int as expected".to_string(),
66        ))
67    }
68}
69
70pub fn get_host_value_return_as_uint() -> Result<u32> {
71    let return_value = try_pop_shared_input_data_into::<ReturnValue>()
72        .expect("Unable to deserialize return value from host");
73
74    // check that return value is an int and return
75    if let ReturnValue::UInt(ui) = return_value {
76        Ok(ui)
77    } else {
78        Err(HyperlightGuestError::new(
79            ErrorCode::GuestError,
80            "Host return value was not a uint as expected".to_string(),
81        ))
82    }
83}
84
85pub fn get_host_value_return_as_long() -> Result<i64> {
86    let return_value = try_pop_shared_input_data_into::<ReturnValue>()
87        .expect("Unable to deserialize return value from host");
88
89    // check that return value is an int and return
90    if let ReturnValue::Long(l) = return_value {
91        Ok(l)
92    } else {
93        Err(HyperlightGuestError::new(
94            ErrorCode::GuestError,
95            "Host return value was not a long as expected".to_string(),
96        ))
97    }
98}
99
100pub fn get_host_value_return_as_ulong() -> Result<u64> {
101    let return_value = try_pop_shared_input_data_into::<ReturnValue>()
102        .expect("Unable to deserialize return value from host");
103
104    // check that return value is an int and return
105    if let ReturnValue::ULong(ul) = return_value {
106        Ok(ul)
107    } else {
108        Err(HyperlightGuestError::new(
109            ErrorCode::GuestError,
110            "Host return value was not a ulong as expected".to_string(),
111        ))
112    }
113}
114
115// TODO: Make this generic, return a Result<T, ErrorCode>
116
117pub fn get_host_value_return_as_vecbytes() -> Result<Vec<u8>> {
118    let return_value = try_pop_shared_input_data_into::<ReturnValue>()
119        .expect("Unable to deserialize return value from host");
120
121    // check that return value is an Vec<u8> and return
122    if let ReturnValue::VecBytes(v) = return_value {
123        Ok(v)
124    } else {
125        Err(HyperlightGuestError::new(
126            ErrorCode::GuestError,
127            "Host return value was not an VecBytes as expected".to_string(),
128        ))
129    }
130}
131
132// TODO: Make this generic, return a Result<T, ErrorCode> this should allow callers to call this function and get the result type they expect
133// without having to do the conversion themselves
134
135pub fn call_host_function(
136    function_name: &str,
137    parameters: Option<Vec<ParameterValue>>,
138    return_type: ReturnType,
139) -> Result<()> {
140    let host_function_call = FunctionCall::new(
141        function_name.to_string(),
142        parameters,
143        FunctionCallType::Host,
144        return_type,
145    );
146
147    validate_host_function_call(&host_function_call)?;
148
149    let host_function_call_buffer: Vec<u8> = host_function_call
150        .try_into()
151        .expect("Unable to serialize host function call");
152
153    push_shared_output_data(host_function_call_buffer)?;
154
155    outb(OutBAction::CallFunction as u16, 0);
156
157    Ok(())
158}
159
160pub fn outb(port: u16, value: u8) {
161    unsafe {
162        match RUNNING_MODE {
163            RunMode::Hypervisor => {
164                hloutb(port, value);
165            }
166            RunMode::InProcessLinux | RunMode::InProcessWindows => {
167                if let Some(outb_func) = OUTB_PTR_WITH_CONTEXT {
168                    if let Some(peb_ptr) = P_PEB {
169                        outb_func((*peb_ptr).pOutbContext, port, value);
170                    }
171                } else if let Some(outb_func) = OUTB_PTR {
172                    outb_func(port, value);
173                } else {
174                    panic!("Tried to call outb without hypervisor and without outb function ptrs");
175                }
176            }
177            _ => {
178                panic!("Tried to call outb in invalid runmode");
179            }
180        }
181
182        check_for_host_error();
183    }
184}
185
186extern "win64" {
187    fn hloutb(port: u16, value: u8);
188}
189
190pub fn print_output_as_guest_function(function_call: &FunctionCall) -> Result<Vec<u8>> {
191    if let ParameterValue::String(message) = function_call.parameters.clone().unwrap()[0].clone() {
192        call_host_function(
193            "HostPrint",
194            Some(Vec::from(&[ParameterValue::String(message.to_string())])),
195            ReturnType::Int,
196        )?;
197        let res_i = get_host_value_return_as_int()?;
198        Ok(get_flatbuffer_result_from_int(res_i))
199    } else {
200        Err(HyperlightGuestError::new(
201            ErrorCode::GuestError,
202            "Wrong Parameters passed to print_output_as_guest_function".to_string(),
203        ))
204    }
205}
206
207// port: RCX(cx), value: RDX(dl)
208global_asm!(
209    ".global hloutb
210        hloutb:
211            xor rax, rax
212            mov al, dl
213            mov dx, cx
214            out dx, al
215            ret"
216);