Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions src/hyperlight_guest/src/arch/aarch64/exit.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
Copyright 2025 The Hyperlight Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

// TODO(aarch64): implement VM exit mechanism (e.g. hvc instruction)

/// Trigger a VM exit sending a 32-bit value to the host on the given port.
pub(crate) unsafe fn out32(_port: u16, _val: u32) {
unimplemented!("aarch64 out32")
}
31 changes: 31 additions & 0 deletions src/hyperlight_guest/src/arch/aarch64/layout.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
Copyright 2025 The Hyperlight Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

// TODO(aarch64): these values are placeholders copied from amd64
pub const MAIN_STACK_TOP_GVA: u64 = 0xffff_ff00_0000_0000;
pub const MAIN_STACK_LIMIT_GVA: u64 = 0xffff_fe00_0000_0000;

pub fn scratch_size() -> u64 {
unimplemented!("aarch64 scratch_size")
}

pub fn scratch_base_gpa() -> u64 {
unimplemented!("aarch64 scratch_base_gpa")
}

pub fn scratch_base_gva() -> u64 {
unimplemented!("aarch64 scratch_base_gva")
}
25 changes: 25 additions & 0 deletions src/hyperlight_guest/src/arch/aarch64/prim_alloc.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
Copyright 2025 The Hyperlight Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

// TODO(aarch64): implement real aarch64 page allocator

// There are no notable architecture-specific safety considerations
// here, and the general conditions are documented in the
// architecture-independent re-export in prim_alloc.rs
#[allow(clippy::missing_safety_doc)]
pub unsafe fn alloc_phys_pages(_n: u64) -> u64 {
unimplemented!("aarch64 alloc_phys_pages")
}
60 changes: 60 additions & 0 deletions src/hyperlight_guest/src/arch/amd64/exit.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
Copyright 2025 The Hyperlight Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

use core::arch::asm;

#[cfg(feature = "trace_guest")]
use hyperlight_common::outb::OutBAction;

/// OUT function for sending a 32-bit value to the host.
/// `out32` can be called from an exception context, so we must be careful
/// with the tracing state that might be locked at that time.
/// The tracing state calls `try_lock` internally to avoid deadlocks.
/// Furthermore, the instrument macro is not used here to avoid creating spans
/// in exception contexts. Because if the trace state is already locked, trying to create a span
/// would cause a panic, which is undesirable in exception handling.
pub(crate) unsafe fn out32(port: u16, val: u32) {
#[cfg(feature = "trace_guest")]
{
if let Some((ptr, len)) = hyperlight_guest_tracing::serialized_data() {
// If tracing is enabled and there is data to send, send it along with the OUT action
unsafe {
asm!("out dx, eax",
in("dx") port,
in("eax") val,
in("r8") OutBAction::TraceBatch as u64,
in("r9") ptr,
in("r10") len,
options(preserves_flags, nomem, nostack)
)
};

// Reset the trace state after sending the batch
// This clears all existing spans/events ensuring a clean state for the next operations
// The trace state is expected to be flushed before this call
hyperlight_guest_tracing::reset();
} else {
// If tracing is not enabled, just send the value
unsafe {
asm!("out dx, eax", in("dx") port, in("eax") val, options(preserves_flags, nomem, nostack))
};
}
}
#[cfg(not(feature = "trace_guest"))]
unsafe {
asm!("out dx, eax", in("dx") port, in("eax") val, options(preserves_flags, nomem, nostack));
}
}
47 changes: 6 additions & 41 deletions src/hyperlight_guest/src/exit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,16 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

use core::arch::asm;
use core::ffi::{CStr, c_char};

use hyperlight_common::outb::OutBAction;

#[cfg_attr(target_arch = "x86_64", path = "arch/amd64/exit.rs")]
#[cfg_attr(target_arch = "x86", path = "arch/amd64/exit.rs")]
#[cfg_attr(target_arch = "aarch64", path = "arch/aarch64/exit.rs")]
mod arch;
pub(crate) use arch::out32;

/// Exits the VM with an Abort OUT action and code 0.
#[unsafe(no_mangle)]
pub extern "C" fn abort() -> ! {
Expand Down Expand Up @@ -87,46 +92,6 @@ pub(crate) fn outb(port: u16, data: &[u8]) {
}
}

/// OUT function for sending a 32-bit value to the host.
/// `out32` can be called from an exception context, so we must be careful
/// with the tracing state that might be locked at that time.
/// The tracing state calls `try_lock` internally to avoid deadlocks.
/// Furthermore, the instrument macro is not used here to avoid creating spans
/// in exception contexts. Because if the trace state is already locked, trying to create a span
/// would cause a panic, which is undesirable in exception handling.
pub(crate) unsafe fn out32(port: u16, val: u32) {
#[cfg(all(feature = "trace_guest", target_arch = "x86_64"))]
{
if let Some((ptr, len)) = hyperlight_guest_tracing::serialized_data() {
// If tracing is enabled and there is data to send, send it along with the OUT action
unsafe {
asm!("out dx, eax",
in("dx") port,
in("eax") val,
in("r8") OutBAction::TraceBatch as u64,
in("r9") ptr,
in("r10") len,
options(preserves_flags, nomem, nostack)
)
};

// Reset the trace state after sending the batch
// This clears all existing spans/events ensuring a clean state for the next operations
// The trace state is expected to be flushed before this call
hyperlight_guest_tracing::reset();
} else {
// If tracing is not enabled, just send the value
unsafe {
asm!("out dx, eax", in("dx") port, in("eax") val, options(preserves_flags, nomem, nostack))
};
}
}
#[cfg(not(all(feature = "trace_guest", target_arch = "x86_64")))]
unsafe {
asm!("out dx, eax", in("dx") port, in("eax") val, options(preserves_flags, nomem, nostack));
}
}

/// Prints a message using `OutBAction::DebugPrint`. It transmits bytes of a message
/// through several VMExists and, with such, it is slower than
/// `print_output_with_host_print`.
Expand Down
1 change: 1 addition & 0 deletions src/hyperlight_guest/src/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ limitations under the License.

#[cfg_attr(target_arch = "x86_64", path = "arch/amd64/layout.rs")]
#[cfg_attr(target_arch = "x86", path = "arch/i686/layout.rs")]
#[cfg_attr(target_arch = "aarch64", path = "arch/aarch64/layout.rs")]
mod arch;

pub use arch::{MAIN_STACK_LIMIT_GVA, MAIN_STACK_TOP_GVA};
Expand Down
1 change: 1 addition & 0 deletions src/hyperlight_guest/src/prim_alloc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ limitations under the License.

#[cfg_attr(target_arch = "x86_64", path = "arch/amd64/prim_alloc.rs")]
#[cfg_attr(target_arch = "x86", path = "arch/i686/prim_alloc.rs")]
#[cfg_attr(target_arch = "aarch64", path = "arch/aarch64/prim_alloc.rs")]
mod arch;

/// Allocate n contiguous physical pages and return the physical
Expand Down
134 changes: 71 additions & 63 deletions src/hyperlight_guest_bin/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,85 +42,93 @@ fn copy_includes<P: AsRef<Path>, Q: AsRef<Path> + std::fmt::Debug>(include_dir:
fn cargo_main() {
println!("cargo:rerun-if-changed=third_party");

let mut cfg = cc::Build::new();
let target = env::var("TARGET").expect("cargo TARGET not set");
let is_x86_64 = target.starts_with("x86_64");

if cfg!(feature = "printf") {
cfg.include("third_party/printf")
.file("third_party/printf/printf.c");
}
// Skip C/musl compilation on aarch64 since no musl support yet
if is_x86_64 {
let mut cfg = cc::Build::new();

if cfg!(feature = "printf") {
cfg.include("third_party/printf")
.file("third_party/printf/printf.c");
}

if cfg!(feature = "libc") {
let entries = glob::glob("third_party/musl/**/*.[cs]") // .c and .s files
.expect("glob pattern should be valid")
.filter_map(Result::ok);
cfg.files(entries);
if cfg!(feature = "libc") {
let entries = glob::glob("third_party/musl/**/*.[cs]") // .c and .s files
.expect("glob pattern should be valid")
.filter_map(Result::ok);
cfg.files(entries);

cfg.include("third_party/musl/src/include")
.include("third_party/musl/include")
.include("third_party/musl/src/internal")
.include("third_party/musl/arch/generic")
.include("third_party/musl/arch/x86_64");
}
cfg.include("third_party/musl/src/include")
.include("third_party/musl/include")
.include("third_party/musl/src/internal")
.include("third_party/musl/arch/generic")
.include("third_party/musl/arch/x86_64");
}

if cfg!(any(feature = "printf", feature = "libc")) {
cfg.define("HYPERLIGHT", None); // used in certain musl files for conditional compilation
if cfg!(any(feature = "printf", feature = "libc")) {
cfg.define("HYPERLIGHT", None); // used in certain musl files for conditional compilation

// silence compiler warnings
cfg.flag("-Wno-unused-command-line-argument") // including .s files makes clang believe arguments are unused
.flag("-Wno-sign-compare")
.flag("-Wno-bitwise-op-parentheses")
.flag("-Wno-unknown-pragmas")
.flag("-Wno-shift-op-parentheses")
.flag("-Wno-logical-op-parentheses")
.flag("-Wno-unused-but-set-variable")
.flag("-Wno-unused-parameter")
.flag("-Wno-string-plus-int");
// silence compiler warnings
cfg.flag("-Wno-unused-command-line-argument") // including .s files makes clang believe arguments are unused
.flag("-Wno-sign-compare")
.flag("-Wno-bitwise-op-parentheses")
.flag("-Wno-unknown-pragmas")
.flag("-Wno-shift-op-parentheses")
.flag("-Wno-logical-op-parentheses")
.flag("-Wno-unused-but-set-variable")
.flag("-Wno-unused-parameter")
.flag("-Wno-string-plus-int");

cfg.flag("-fPIC");
// This is a terrible hack, because
// - we need stack clash protection, because we have put the
// stack right smack in the middle of everything in the guest
// - clang refuses to do stack clash protection unless it is
// required by a target ABI (Windows, MacOS) or the target is
// is Linux or FreeBSD (see Clang.cpp RenderSCPOptions
// https://github.com/llvm/llvm-project/blob/1bb52e9/clang/lib/Driver/ToolChains/Clang.cpp#L3724).
// Hopefully a flag to force stack clash protection on generic
// targets will eventually show up.
cfg.flag("--target=x86_64-unknown-linux-none");
cfg.flag("-fPIC");
// This is a terrible hack, because
// - we need stack clash protection, because we have put the
// stack right smack in the middle of everything in the guest
// - clang refuses to do stack clash protection unless it is
// required by a target ABI (Windows, MacOS) or the target is
// is Linux or FreeBSD (see Clang.cpp RenderSCPOptions
// https://github.com/llvm/llvm-project/blob/1bb52e9/clang/lib/Driver/ToolChains/Clang.cpp#L3724).
// Hopefully a flag to force stack clash protection on generic
// targets will eventually show up.
cfg.flag("--target=x86_64-unknown-linux-none");

// We don't use a different stack for all interrupts, so there
// can be no red zone
cfg.flag("-mno-red-zone");
// We don't use a different stack for all interrupts, so there
// can be no red zone
cfg.flag("-mno-red-zone");

// We don't support stack protectors at the moment, but Arch Linux clang
// auto-enables them for -linux platforms, so explicitly disable them.
cfg.flag("-fno-stack-protector");
cfg.flag("-fstack-clash-protection");
cfg.flag("-mstack-probe-size=4096");
cfg.compiler(
env::var("HYPERLIGHT_GUEST_clang")
.as_deref()
.unwrap_or("clang"),
);
// We don't support stack protectors at the moment, but Arch Linux clang
// auto-enables them for -linux platforms, so explicitly disable them.
cfg.flag("-fno-stack-protector");
cfg.flag("-fstack-clash-protection");
cfg.flag("-mstack-probe-size=4096");
cfg.compiler(
env::var("HYPERLIGHT_GUEST_clang")
.as_deref()
.unwrap_or("clang"),
);

if cfg!(windows) {
unsafe { env::set_var("AR_x86_64_unknown_none", "llvm-ar") };
if cfg!(windows) {
unsafe { env::set_var("AR_x86_64_unknown_none", "llvm-ar") };
}
cfg.compile("hyperlight_guest_bin");
}
cfg.compile("hyperlight_guest_bin");
}

let out_dir = env::var("OUT_DIR").expect("cargo OUT_DIR not set");
let include_dir = PathBuf::from(&out_dir).join("include");
fs::create_dir_all(&include_dir)
.unwrap_or_else(|e| panic!("Could not create include dir {:?}: {}", &include_dir, e));
if cfg!(feature = "printf") {
copy_includes(&include_dir, "third_party/printf/");
}
if cfg!(feature = "libc") {
copy_includes(&include_dir, "third_party/musl/include");
copy_includes(&include_dir, "third_party/musl/arch/generic");
copy_includes(&include_dir, "third_party/musl/arch/x86_64");
copy_includes(&include_dir, "third_party/musl/src/internal");
if is_x86_64 {
if cfg!(feature = "printf") {
copy_includes(&include_dir, "third_party/printf/");
}
if cfg!(feature = "libc") {
copy_includes(&include_dir, "third_party/musl/include");
copy_includes(&include_dir, "third_party/musl/arch/generic");
copy_includes(&include_dir, "third_party/musl/arch/x86_64");
copy_includes(&include_dir, "third_party/musl/src/internal");
}
}
/* do not canonicalize: clang has trouble with UNC paths */
let include_str = include_dir
Expand Down
Loading