Skip to content

Commit f8b2641

Browse files
ludfjigjsturtevant
andauthored
feat: enable guest compilation for aarch64 (#1297)
* refactor: move out32 into arch-specific exit module Extract the out32 VM exit function from exit.rs into arch/amd64/exit.rs. exit.rs now delegates to the arch module via cfg_attr, keeping arch-independent code (outb, abort, debug_print) in the shared file. Signed-off-by: Ludvig Liljenberg <4257730+ludfjig@users.noreply.github.com> * refactor: gate invariant_tsc module on x86_64 The invariant_tsc module uses core::arch::x86_64 intrinsics (CPUID, RDTSC) which are not available on other architectures. Signed-off-by: Ludvig Liljenberg <4257730+ludfjig@users.noreply.github.com> * refactor: gate x86_64-specific code in hyperlight-guest-bin - Gate paging and exception modules on target_arch = x86_64 - Gate ProfiledLockedHeap (uses x86 out asm) on x86_64 - Skip musl/printf C compilation in build.rs on non-x86_64 Signed-off-by: Ludvig Liljenberg <4257730+ludfjig@users.noreply.github.com> * feat: add aarch64 arch stubs for guest crates Add aarch64 arch modules with unimplemented!() stubs: - hyperlight-guest: layout, prim_alloc, exit modules - hyperlight-guest-bin: dispatch and entrypoint stubs Update cfg_attr paths in layout.rs and prim_alloc.rs to include aarch64. All stubs panic at runtime — no aarch64 guest functionality is implemented yet. Signed-off-by: Ludvig Liljenberg <4257730+ludfjig@users.noreply.github.com> --------- Signed-off-by: Ludvig Liljenberg <4257730+ludfjig@users.noreply.github.com> Co-authored-by: James Sturtevant <jsturtevant@gmail.com>
1 parent c171901 commit f8b2641

File tree

11 files changed

+261
-110
lines changed

11 files changed

+261
-110
lines changed
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/*
2+
Copyright 2025 The Hyperlight Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
// TODO(aarch64): implement VM exit mechanism (e.g. hvc instruction)
18+
19+
/// Trigger a VM exit sending a 32-bit value to the host on the given port.
20+
pub(crate) unsafe fn out32(_port: u16, _val: u32) {
21+
unimplemented!("aarch64 out32")
22+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*
2+
Copyright 2025 The Hyperlight Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
// TODO(aarch64): these values are placeholders copied from amd64
18+
pub const MAIN_STACK_TOP_GVA: u64 = 0xffff_ff00_0000_0000;
19+
pub const MAIN_STACK_LIMIT_GVA: u64 = 0xffff_fe00_0000_0000;
20+
21+
pub fn scratch_size() -> u64 {
22+
unimplemented!("aarch64 scratch_size")
23+
}
24+
25+
pub fn scratch_base_gpa() -> u64 {
26+
unimplemented!("aarch64 scratch_base_gpa")
27+
}
28+
29+
pub fn scratch_base_gva() -> u64 {
30+
unimplemented!("aarch64 scratch_base_gva")
31+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/*
2+
Copyright 2025 The Hyperlight Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
// TODO(aarch64): implement real aarch64 page allocator
18+
19+
// There are no notable architecture-specific safety considerations
20+
// here, and the general conditions are documented in the
21+
// architecture-independent re-export in prim_alloc.rs
22+
#[allow(clippy::missing_safety_doc)]
23+
pub unsafe fn alloc_phys_pages(_n: u64) -> u64 {
24+
unimplemented!("aarch64 alloc_phys_pages")
25+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/*
2+
Copyright 2025 The Hyperlight Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
use core::arch::asm;
18+
19+
#[cfg(feature = "trace_guest")]
20+
use hyperlight_common::outb::OutBAction;
21+
22+
/// OUT function for sending a 32-bit value to the host.
23+
/// `out32` can be called from an exception context, so we must be careful
24+
/// with the tracing state that might be locked at that time.
25+
/// The tracing state calls `try_lock` internally to avoid deadlocks.
26+
/// Furthermore, the instrument macro is not used here to avoid creating spans
27+
/// in exception contexts. Because if the trace state is already locked, trying to create a span
28+
/// would cause a panic, which is undesirable in exception handling.
29+
pub(crate) unsafe fn out32(port: u16, val: u32) {
30+
#[cfg(feature = "trace_guest")]
31+
{
32+
if let Some((ptr, len)) = hyperlight_guest_tracing::serialized_data() {
33+
// If tracing is enabled and there is data to send, send it along with the OUT action
34+
unsafe {
35+
asm!("out dx, eax",
36+
in("dx") port,
37+
in("eax") val,
38+
in("r8") OutBAction::TraceBatch as u64,
39+
in("r9") ptr,
40+
in("r10") len,
41+
options(preserves_flags, nomem, nostack)
42+
)
43+
};
44+
45+
// Reset the trace state after sending the batch
46+
// This clears all existing spans/events ensuring a clean state for the next operations
47+
// The trace state is expected to be flushed before this call
48+
hyperlight_guest_tracing::reset();
49+
} else {
50+
// If tracing is not enabled, just send the value
51+
unsafe {
52+
asm!("out dx, eax", in("dx") port, in("eax") val, options(preserves_flags, nomem, nostack))
53+
};
54+
}
55+
}
56+
#[cfg(not(feature = "trace_guest"))]
57+
unsafe {
58+
asm!("out dx, eax", in("dx") port, in("eax") val, options(preserves_flags, nomem, nostack));
59+
}
60+
}

src/hyperlight_guest/src/exit.rs

Lines changed: 6 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,16 @@ See the License for the specific language governing permissions and
1414
limitations under the License.
1515
*/
1616

17-
use core::arch::asm;
1817
use core::ffi::{CStr, c_char};
1918

2019
use hyperlight_common::outb::OutBAction;
2120

21+
#[cfg_attr(target_arch = "x86_64", path = "arch/amd64/exit.rs")]
22+
#[cfg_attr(target_arch = "x86", path = "arch/amd64/exit.rs")]
23+
#[cfg_attr(target_arch = "aarch64", path = "arch/aarch64/exit.rs")]
24+
mod arch;
25+
pub(crate) use arch::out32;
26+
2227
/// Exits the VM with an Abort OUT action and code 0.
2328
#[unsafe(no_mangle)]
2429
pub extern "C" fn abort() -> ! {
@@ -87,46 +92,6 @@ pub(crate) fn outb(port: u16, data: &[u8]) {
8792
}
8893
}
8994

90-
/// OUT function for sending a 32-bit value to the host.
91-
/// `out32` can be called from an exception context, so we must be careful
92-
/// with the tracing state that might be locked at that time.
93-
/// The tracing state calls `try_lock` internally to avoid deadlocks.
94-
/// Furthermore, the instrument macro is not used here to avoid creating spans
95-
/// in exception contexts. Because if the trace state is already locked, trying to create a span
96-
/// would cause a panic, which is undesirable in exception handling.
97-
pub(crate) unsafe fn out32(port: u16, val: u32) {
98-
#[cfg(all(feature = "trace_guest", target_arch = "x86_64"))]
99-
{
100-
if let Some((ptr, len)) = hyperlight_guest_tracing::serialized_data() {
101-
// If tracing is enabled and there is data to send, send it along with the OUT action
102-
unsafe {
103-
asm!("out dx, eax",
104-
in("dx") port,
105-
in("eax") val,
106-
in("r8") OutBAction::TraceBatch as u64,
107-
in("r9") ptr,
108-
in("r10") len,
109-
options(preserves_flags, nomem, nostack)
110-
)
111-
};
112-
113-
// Reset the trace state after sending the batch
114-
// This clears all existing spans/events ensuring a clean state for the next operations
115-
// The trace state is expected to be flushed before this call
116-
hyperlight_guest_tracing::reset();
117-
} else {
118-
// If tracing is not enabled, just send the value
119-
unsafe {
120-
asm!("out dx, eax", in("dx") port, in("eax") val, options(preserves_flags, nomem, nostack))
121-
};
122-
}
123-
}
124-
#[cfg(not(all(feature = "trace_guest", target_arch = "x86_64")))]
125-
unsafe {
126-
asm!("out dx, eax", in("dx") port, in("eax") val, options(preserves_flags, nomem, nostack));
127-
}
128-
}
129-
13095
/// Prints a message using `OutBAction::DebugPrint`. It transmits bytes of a message
13196
/// through several VMExists and, with such, it is slower than
13297
/// `print_output_with_host_print`.

src/hyperlight_guest/src/layout.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ limitations under the License.
1616

1717
#[cfg_attr(target_arch = "x86_64", path = "arch/amd64/layout.rs")]
1818
#[cfg_attr(target_arch = "x86", path = "arch/i686/layout.rs")]
19+
#[cfg_attr(target_arch = "aarch64", path = "arch/aarch64/layout.rs")]
1920
mod arch;
2021

2122
pub use arch::{MAIN_STACK_LIMIT_GVA, MAIN_STACK_TOP_GVA};

src/hyperlight_guest/src/prim_alloc.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ limitations under the License.
1616

1717
#[cfg_attr(target_arch = "x86_64", path = "arch/amd64/prim_alloc.rs")]
1818
#[cfg_attr(target_arch = "x86", path = "arch/i686/prim_alloc.rs")]
19+
#[cfg_attr(target_arch = "aarch64", path = "arch/aarch64/prim_alloc.rs")]
1920
mod arch;
2021

2122
/// Allocate n contiguous physical pages and return the physical

src/hyperlight_guest_bin/build.rs

Lines changed: 71 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -42,85 +42,93 @@ fn copy_includes<P: AsRef<Path>, Q: AsRef<Path> + std::fmt::Debug>(include_dir:
4242
fn cargo_main() {
4343
println!("cargo:rerun-if-changed=third_party");
4444

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

47-
if cfg!(feature = "printf") {
48-
cfg.include("third_party/printf")
49-
.file("third_party/printf/printf.c");
50-
}
48+
// Skip C/musl compilation on aarch64 since no musl support yet
49+
if is_x86_64 {
50+
let mut cfg = cc::Build::new();
51+
52+
if cfg!(feature = "printf") {
53+
cfg.include("third_party/printf")
54+
.file("third_party/printf/printf.c");
55+
}
5156

52-
if cfg!(feature = "libc") {
53-
let entries = glob::glob("third_party/musl/**/*.[cs]") // .c and .s files
54-
.expect("glob pattern should be valid")
55-
.filter_map(Result::ok);
56-
cfg.files(entries);
57+
if cfg!(feature = "libc") {
58+
let entries = glob::glob("third_party/musl/**/*.[cs]") // .c and .s files
59+
.expect("glob pattern should be valid")
60+
.filter_map(Result::ok);
61+
cfg.files(entries);
5762

58-
cfg.include("third_party/musl/src/include")
59-
.include("third_party/musl/include")
60-
.include("third_party/musl/src/internal")
61-
.include("third_party/musl/arch/generic")
62-
.include("third_party/musl/arch/x86_64");
63-
}
63+
cfg.include("third_party/musl/src/include")
64+
.include("third_party/musl/include")
65+
.include("third_party/musl/src/internal")
66+
.include("third_party/musl/arch/generic")
67+
.include("third_party/musl/arch/x86_64");
68+
}
6469

65-
if cfg!(any(feature = "printf", feature = "libc")) {
66-
cfg.define("HYPERLIGHT", None); // used in certain musl files for conditional compilation
70+
if cfg!(any(feature = "printf", feature = "libc")) {
71+
cfg.define("HYPERLIGHT", None); // used in certain musl files for conditional compilation
6772

68-
// silence compiler warnings
69-
cfg.flag("-Wno-unused-command-line-argument") // including .s files makes clang believe arguments are unused
70-
.flag("-Wno-sign-compare")
71-
.flag("-Wno-bitwise-op-parentheses")
72-
.flag("-Wno-unknown-pragmas")
73-
.flag("-Wno-shift-op-parentheses")
74-
.flag("-Wno-logical-op-parentheses")
75-
.flag("-Wno-unused-but-set-variable")
76-
.flag("-Wno-unused-parameter")
77-
.flag("-Wno-string-plus-int");
73+
// silence compiler warnings
74+
cfg.flag("-Wno-unused-command-line-argument") // including .s files makes clang believe arguments are unused
75+
.flag("-Wno-sign-compare")
76+
.flag("-Wno-bitwise-op-parentheses")
77+
.flag("-Wno-unknown-pragmas")
78+
.flag("-Wno-shift-op-parentheses")
79+
.flag("-Wno-logical-op-parentheses")
80+
.flag("-Wno-unused-but-set-variable")
81+
.flag("-Wno-unused-parameter")
82+
.flag("-Wno-string-plus-int");
7883

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

91-
// We don't use a different stack for all interrupts, so there
92-
// can be no red zone
93-
cfg.flag("-mno-red-zone");
96+
// We don't use a different stack for all interrupts, so there
97+
// can be no red zone
98+
cfg.flag("-mno-red-zone");
9499

95-
// We don't support stack protectors at the moment, but Arch Linux clang
96-
// auto-enables them for -linux platforms, so explicitly disable them.
97-
cfg.flag("-fno-stack-protector");
98-
cfg.flag("-fstack-clash-protection");
99-
cfg.flag("-mstack-probe-size=4096");
100-
cfg.compiler(
101-
env::var("HYPERLIGHT_GUEST_clang")
102-
.as_deref()
103-
.unwrap_or("clang"),
104-
);
100+
// We don't support stack protectors at the moment, but Arch Linux clang
101+
// auto-enables them for -linux platforms, so explicitly disable them.
102+
cfg.flag("-fno-stack-protector");
103+
cfg.flag("-fstack-clash-protection");
104+
cfg.flag("-mstack-probe-size=4096");
105+
cfg.compiler(
106+
env::var("HYPERLIGHT_GUEST_clang")
107+
.as_deref()
108+
.unwrap_or("clang"),
109+
);
105110

106-
if cfg!(windows) {
107-
unsafe { env::set_var("AR_x86_64_unknown_none", "llvm-ar") };
111+
if cfg!(windows) {
112+
unsafe { env::set_var("AR_x86_64_unknown_none", "llvm-ar") };
113+
}
114+
cfg.compile("hyperlight_guest_bin");
108115
}
109-
cfg.compile("hyperlight_guest_bin");
110116
}
111117

112118
let out_dir = env::var("OUT_DIR").expect("cargo OUT_DIR not set");
113119
let include_dir = PathBuf::from(&out_dir).join("include");
114120
fs::create_dir_all(&include_dir)
115121
.unwrap_or_else(|e| panic!("Could not create include dir {:?}: {}", &include_dir, e));
116-
if cfg!(feature = "printf") {
117-
copy_includes(&include_dir, "third_party/printf/");
118-
}
119-
if cfg!(feature = "libc") {
120-
copy_includes(&include_dir, "third_party/musl/include");
121-
copy_includes(&include_dir, "third_party/musl/arch/generic");
122-
copy_includes(&include_dir, "third_party/musl/arch/x86_64");
123-
copy_includes(&include_dir, "third_party/musl/src/internal");
122+
if is_x86_64 {
123+
if cfg!(feature = "printf") {
124+
copy_includes(&include_dir, "third_party/printf/");
125+
}
126+
if cfg!(feature = "libc") {
127+
copy_includes(&include_dir, "third_party/musl/include");
128+
copy_includes(&include_dir, "third_party/musl/arch/generic");
129+
copy_includes(&include_dir, "third_party/musl/arch/x86_64");
130+
copy_includes(&include_dir, "third_party/musl/src/internal");
131+
}
124132
}
125133
/* do not canonicalize: clang has trouble with UNC paths */
126134
let include_str = include_dir

0 commit comments

Comments
 (0)