Skip to main content

hyperlight_guest_bin/
host_comm.rs

1/*
2Copyright 2025  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::{String, ToString};
18use alloc::vec::Vec;
19use core::ffi::{CStr, c_char};
20use core::mem;
21
22use hyperlight_common::flatbuffer_wrappers::function_call::FunctionCall;
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::func::{ParameterTuple, SupportedReturnType};
29use hyperlight_guest::error::{HyperlightGuestError, Result};
30
31const BUFFER_SIZE: usize = 1000;
32static mut MESSAGE_BUFFER: Vec<u8> = Vec::new();
33
34use crate::GUEST_HANDLE;
35
36pub fn call_host_function<T>(
37    function_name: &str,
38    parameters: Option<Vec<ParameterValue>>,
39    return_type: ReturnType,
40) -> Result<T>
41where
42    T: TryFrom<ReturnValue>,
43{
44    let handle = unsafe { GUEST_HANDLE };
45    handle.call_host_function::<T>(function_name, parameters, return_type)
46}
47
48pub fn call_host<T>(function_name: impl AsRef<str>, args: impl ParameterTuple) -> Result<T>
49where
50    T: SupportedReturnType + TryFrom<ReturnValue>,
51{
52    call_host_function::<T>(function_name.as_ref(), Some(args.into_value()), T::TYPE)
53}
54
55pub fn call_host_function_without_returning_result(
56    function_name: &str,
57    parameters: Option<Vec<ParameterValue>>,
58    return_type: ReturnType,
59) -> Result<()> {
60    let handle = unsafe { GUEST_HANDLE };
61    handle.call_host_function_without_returning_result(function_name, parameters, return_type)
62}
63
64pub fn get_host_return_value_raw() -> Result<ReturnValue> {
65    let handle = unsafe { GUEST_HANDLE };
66    handle.get_host_return_raw()
67}
68
69pub fn get_host_return_value<T: TryFrom<ReturnValue>>() -> Result<T> {
70    let handle = unsafe { GUEST_HANDLE };
71    handle.get_host_return_value::<T>()
72}
73
74pub fn read_n_bytes_from_user_memory(num: u64) -> Result<Vec<u8>> {
75    let handle = unsafe { GUEST_HANDLE };
76    handle.read_n_bytes_from_user_memory(num)
77}
78
79/// Print a message using the host's print function.
80///
81/// This function requires memory to be setup to be used. In particular, the
82/// existence of the input and output memory regions.
83pub fn print_output_with_host_print(function_call: FunctionCall) -> Result<Vec<u8>> {
84    let handle = unsafe { GUEST_HANDLE };
85    if let ParameterValue::String(message) = function_call.parameters.unwrap().remove(0) {
86        let res = handle.call_host_function::<i32>(
87            "HostPrint",
88            Some(Vec::from(&[ParameterValue::String(message)])),
89            ReturnType::Int,
90        )?;
91
92        Ok(get_flatbuffer_result(res))
93    } else {
94        Err(HyperlightGuestError::new(
95            ErrorCode::GuestError,
96            "Wrong Parameters passed to print_output_with_host_print".to_string(),
97        ))
98    }
99}
100
101/// Exposes a C API to allow the guest to print a string
102///
103/// # Safety
104/// This function is not thread safe
105#[unsafe(no_mangle)]
106#[allow(static_mut_refs)]
107pub unsafe extern "C" fn _putchar(c: c_char) {
108    let handle = unsafe { GUEST_HANDLE };
109    let char = c as u8;
110    let message_buffer = unsafe { &mut MESSAGE_BUFFER };
111
112    // Extend buffer capacity if it's empty (like `with_capacity` in lazy_static).
113    // TODO: replace above Vec::new() with Vec::with_capacity once it's stable in const contexts.
114    if message_buffer.capacity() == 0 {
115        message_buffer.reserve(BUFFER_SIZE);
116    }
117
118    message_buffer.push(char);
119
120    if message_buffer.len() == BUFFER_SIZE || char == b'\0' {
121        let str = if char == b'\0' {
122            CStr::from_bytes_until_nul(message_buffer)
123                .expect("No null byte in buffer")
124                .to_string_lossy()
125                .into_owned()
126        } else {
127            String::from_utf8(mem::take(message_buffer))
128                .expect("Failed to convert buffer to string")
129        };
130
131        // HostPrint returns an i32, but we don't care about the return value
132        let _ = handle
133            .call_host_function::<i32>(
134                "HostPrint",
135                Some(Vec::from(&[ParameterValue::String(str)])),
136                ReturnType::Int,
137            )
138            .expect("Failed to call HostPrint");
139
140        // Clear the buffer after sending
141        message_buffer.clear();
142    }
143}