nub_host_kvm/func/host_functions.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
17//! Host-function registration API.
18//!
19//! Replaces the upstream parameter-tuple polymorphic machinery
20//! (`ParameterTuple`/`SupportedReturnType`/`HostFunction<Output, Args>`)
21//! with a single byte-slice signature. Each host function takes the
22//! raw `Request.payload` bytes from the guest and returns the
23//! response payload bytes. Typed encode/decode (if anything) is the
24//! caller's job — same shape as `#[guest_function]` on the guest
25//! side.
26
27use crate::Result;
28use crate::new_error;
29use crate::sandbox::UninitializedSandbox;
30use crate::sandbox::host_funcs::HostFn;
31
32/// A sandbox on which host functions can be registered.
33pub trait Registerable {
34 /// Register `func` to be invoked by the guest under the given
35 /// `fn_id`. Overwrites any prior entry for that id.
36 fn register_host_function(&mut self, fn_id: u32, func: HostFn) -> Result<()>;
37}
38
39impl Registerable for UninitializedSandbox {
40 fn register_host_function(&mut self, fn_id: u32, func: HostFn) -> Result<()> {
41 let mut hfs = self
42 .host_funcs
43 .try_lock()
44 .map_err(|e| new_error!("Error locking at {}:{}: {}", file!(), line!(), e))?;
45 hfs.register_host_function(fn_id, func)
46 }
47}
48
49/// Allow registering host functions on an already-evolved
50/// [`crate::MultiUseSandbox`].
51///
52/// The primary entry point for host-function registration is the
53/// `UninitializedSandbox` impl above — that's the lifecycle phase
54/// where the guest hasn't yet been allowed to issue host calls.
55/// There are, however, cases where a `MultiUseSandbox` is obtained
56/// without traversing the `Uninitialized → evolve()` path:
57///
58/// - Sandboxes loaded from a persisted snapshot.
59/// - Any future API that yields a `MultiUseSandbox` directly.
60///
61/// In those cases the caller never had a chance to call
62/// `register_host_function` on an `UninitializedSandbox`, so we
63/// expose the same trait implementation here for late registration.
64/// The guest's dispatcher resolves by `fn_id` at call time, so
65/// inserting into the registry after `evolve()` is semantically safe
66/// as long as the first host-function invocation happens after
67/// registration completes.
68impl Registerable for crate::MultiUseSandbox {
69 fn register_host_function(&mut self, fn_id: u32, func: HostFn) -> Result<()> {
70 let mut hfs = self
71 .host_funcs
72 .try_lock()
73 .map_err(|e| new_error!("Error locking at {}:{}: {}", file!(), line!(), e))?;
74 hfs.register_host_function(fn_id, func)
75 }
76}
77
78pub(crate) fn register_host_function(
79 sandbox: &mut UninitializedSandbox,
80 fn_id: u32,
81 func: HostFn,
82) -> Result<()> {
83 sandbox
84 .host_funcs
85 .try_lock()
86 .map_err(|e| new_error!("Error locking at {}:{}: {}", file!(), line!(), e))?
87 .register_host_function(fn_id, func)?;
88 Ok(())
89}