Skip to content

Commit 9095115

Browse files
committed
amd64: move processor control structures to snapshot region
This commit changes the code that sets up all these processor control structures to allocate pages from the scratch region, map them to an arbitrary VA, and then point the processor at that VA (in order to ensure that they survive snapshotting at the same VA). It also refactors the architecture-specific initialisation code somewhat, in order to make it a bit easier to have one memory allocation shared by all the relevant tables & in preparation for future multi-architecture support. Signed-off-by: Lucy Menon <168595099+syntactically@users.noreply.github.com>
1 parent c8097f3 commit 9095115

22 files changed

Lines changed: 849 additions & 741 deletions

File tree

src/hyperlight_common/src/arch/amd64/layout.rs

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

17+
// The addresses in this file should be coordinated with
18+
// src/hyperlight_guest/src/arch/amd64/layout.rs and
19+
// src/hyperlight_guest_bin/src/arch/amd64/layout.rs
20+
1721
/// We have this the top of the page below the top of memory in order
1822
/// to make working with start/end ptrs in a few places more
1923
/// convenient (not needing to worry about overflow)

src/hyperlight_guest/src/arch/amd64/layout.rs

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

17-
pub const MAIN_STACK_TOP_GVA: usize = 0xffff_feff_ffff_f000;
17+
// The addresses in this file should be coordinated with
18+
// src/hyperlight_common/src/arch/amd64/layout.rs and
19+
// src/hyperlight_guest_bin/src/arch/amd64/layout.rs
20+
21+
pub const MAIN_STACK_TOP_GVA: u64 = 0xffff_feff_ffff_f000;
1822

1923
pub fn scratch_size() -> u64 {
2024
let addr = crate::layout::scratch_size_gva();
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
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 super::machine::ExceptionInfo;
18+
19+
#[repr(C)]
20+
/// Saved context, pushed onto the stack by exception entry code
21+
pub struct Context {
22+
/// in order: ds, gs, fs, es
23+
pub segments: [u64; 4],
24+
pub fxsave: [u8; 512],
25+
/// no `rsp`, since the processor saved it
26+
/// `rax` is at the top, `r15` the bottom
27+
pub gprs: [u64; 15],
28+
_padding: u64,
29+
}
30+
const _: () = assert!(size_of::<Context>() == 32 + 512 + 120 + 8);
31+
// The combination of the ExceptionInfo (pushed by the CPU) and the
32+
// register Context that we save to the stack must be 16byte aligned
33+
// before calling the hl_exception_handler as specified in the x86-64
34+
// ELF System V psABI specification, Section 3.2.2:
35+
//
36+
// https://gitlab.com/x86-psABIs/x86-64-ABI/-/jobs/artifacts/master/raw/x86-64-ABI/abi.pdf?job=build
37+
const _: () = assert!((size_of::<Context>() + size_of::<ExceptionInfo>()).is_multiple_of(16));
38+
39+
// Defines `context_save` and `context_restore`
40+
macro_rules! save {
41+
() => {
42+
concat!(
43+
// Save general-purpose registers
44+
" sub rsp, 8\n",
45+
" push rax\n",
46+
" push rbx\n",
47+
" push rcx\n",
48+
" push rdx\n",
49+
" push rsi\n",
50+
" push rdi\n",
51+
" push rbp\n",
52+
" push r8\n",
53+
" push r9\n",
54+
" push r10\n",
55+
" push r11\n",
56+
" push r12\n",
57+
" push r13\n",
58+
" push r14\n",
59+
" push r15\n",
60+
// Save floating-point/SSE registers
61+
// TODO: Don't do this unconditionally: get the exn
62+
// handlers compiled without sse
63+
// TODO: Check if we ever generate code with ymm/zmm in
64+
// the handlers and save/restore those as well
65+
" sub rsp, 512\n",
66+
" mov rax, rsp\n",
67+
" fxsave [rax]\n",
68+
// Save the rest of the segment registers
69+
" mov rax, es\n",
70+
" push rax\n",
71+
" mov rax, fs\n",
72+
" push rax\n",
73+
" mov rax, gs\n",
74+
" push rax\n",
75+
" mov rax, ds\n",
76+
" push rax\n",
77+
)
78+
};
79+
}
80+
pub(super) use save;
81+
82+
macro_rules! restore {
83+
() => {
84+
concat!(
85+
// Restore most segment registers
86+
" pop rax\n",
87+
" mov ds, rax\n",
88+
" pop rax\n",
89+
" mov gs, rax\n",
90+
" pop rax\n",
91+
" mov fs, rax\n",
92+
" pop rax\n",
93+
" mov es, rax\n",
94+
// Restore floating-point/SSE registers
95+
" mov rax, rsp\n",
96+
" fxrstor [rax]\n",
97+
" add rsp, 512\n",
98+
// Restore general-purpose registers
99+
" pop r15\n",
100+
" pop r14\n",
101+
" pop r13\n",
102+
" pop r12\n",
103+
" pop r11\n",
104+
" pop r10\n",
105+
" pop r9\n",
106+
" pop r8\n",
107+
" pop rbp\n",
108+
" pop rdi\n",
109+
" pop rsi\n",
110+
" pop rdx\n",
111+
" pop rcx\n",
112+
" pop rbx\n",
113+
" pop rax\n",
114+
" add rsp, 8\n",
115+
)
116+
};
117+
}
118+
pub(super) use restore;
Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
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+
// Note: this code takes reference from
18+
// https://github.com/nanvix/nanvix/blob/dev/src/kernel/src/hal/arch/x86/hooks.S
19+
20+
use core::arch::{asm, global_asm};
21+
22+
use hyperlight_common::outb::Exception;
23+
24+
use super::super::context;
25+
use super::super::machine::{IDT, IdtEntry, IdtPointer, ProcCtrl};
26+
27+
unsafe extern "C" {
28+
// Exception handlers
29+
fn _do_excp0();
30+
fn _do_excp1();
31+
fn _do_excp2();
32+
fn _do_excp3();
33+
fn _do_excp4();
34+
fn _do_excp5();
35+
fn _do_excp6();
36+
fn _do_excp7();
37+
fn _do_excp8();
38+
fn _do_excp9();
39+
fn _do_excp10();
40+
fn _do_excp11();
41+
fn _do_excp12();
42+
fn _do_excp13();
43+
fn _do_excp14();
44+
fn _do_excp15();
45+
fn _do_excp16();
46+
fn _do_excp17();
47+
fn _do_excp18();
48+
fn _do_excp19();
49+
fn _do_excp20();
50+
fn _do_excp30();
51+
}
52+
53+
// Macro to generate exception handlers
54+
// that satisfy the `extern`s at the top of the file.
55+
//
56+
// - Example output from this macro for generate_excp!(0) call:
57+
// ```assembly
58+
// .global _do_excp0
59+
// _do_excp0:
60+
// context_save!()
61+
// mov rsi, 0
62+
// mov rdx, 0
63+
// jmp _do_excp_common
64+
// ```
65+
macro_rules! generate_excp {
66+
($num:expr) => {
67+
concat!(
68+
".global _do_excp",
69+
stringify!($num),
70+
"\n",
71+
"_do_excp",
72+
stringify!($num),
73+
":\n",
74+
context::save!(),
75+
// rsi is the exception number.
76+
" mov rsi, ",
77+
stringify!($num),
78+
"\n",
79+
// rdx is only used for pagefault exception and
80+
// contains the address that caused the pagefault.
81+
" mov rdx, 0\n",
82+
" jmp _do_excp_common\n"
83+
)
84+
};
85+
($num:expr, pusherrcode) => {
86+
concat!(
87+
".global _do_excp",
88+
stringify!($num),
89+
"\n",
90+
"_do_excp",
91+
stringify!($num),
92+
":\n",
93+
// Some exceptions push an error code onto the stack.
94+
// For the ones that don't, we push a 0 to keep the
95+
// stack aligned.
96+
" push 0\n",
97+
context::save!(),
98+
// rsi is the exception number.
99+
" mov rsi, ",
100+
stringify!($num),
101+
"\n",
102+
// rdx is only used for pagefault exception and
103+
// contains the address that caused the pagefault.
104+
" mov rdx, 0\n",
105+
" jmp _do_excp_common\n"
106+
)
107+
};
108+
($num:expr, pagefault) => {
109+
concat!(
110+
".global _do_excp",
111+
stringify!($num),
112+
"\n",
113+
"_do_excp",
114+
stringify!($num),
115+
":\n",
116+
context::save!(),
117+
" mov rsi, ",
118+
stringify!($num),
119+
"\n",
120+
// In a page fault exception, the cr2 register
121+
// contains the address that caused the page fault.
122+
" mov rdx, cr2\n",
123+
" jmp _do_excp_common\n"
124+
)
125+
};
126+
}
127+
128+
// Generates exception handlers
129+
macro_rules! generate_exceptions {
130+
() => {
131+
concat!(
132+
// Common exception handler
133+
".global _do_excp_common\n",
134+
"_do_excp_common:\n",
135+
// the first argument to the Rust handler points to the
136+
// bottom of the context struct, which happens to be the
137+
// stack pointer just before it was called.
138+
" mov rdi, rsp\n",
139+
" call {hl_exception_handler}\n",
140+
context::restore!(),
141+
" add rsp, 8\n", // error code
142+
" iretq\n", // iretq is used to return from exception in x86_64
143+
generate_excp!(0, pusherrcode),
144+
generate_excp!(1, pusherrcode),
145+
generate_excp!(2, pusherrcode),
146+
generate_excp!(3, pusherrcode),
147+
generate_excp!(4, pusherrcode),
148+
generate_excp!(5, pusherrcode),
149+
generate_excp!(6, pusherrcode),
150+
generate_excp!(7, pusherrcode),
151+
generate_excp!(8),
152+
generate_excp!(9, pusherrcode),
153+
generate_excp!(10),
154+
generate_excp!(11),
155+
generate_excp!(12),
156+
generate_excp!(13),
157+
generate_excp!(14, pagefault),
158+
generate_excp!(15, pusherrcode),
159+
generate_excp!(16, pusherrcode),
160+
generate_excp!(17),
161+
generate_excp!(18, pusherrcode),
162+
generate_excp!(19, pusherrcode),
163+
generate_excp!(20, pusherrcode),
164+
generate_excp!(30),
165+
)
166+
};
167+
}
168+
169+
// Output the assembly code
170+
global_asm!(
171+
generate_exceptions!(),
172+
hl_exception_handler = sym super::handle::hl_exception_handler,
173+
);
174+
175+
pub(in super::super) fn init_idt(pc: *mut ProcCtrl) {
176+
let idt = unsafe { &raw mut (*pc).idt };
177+
let set_idt_entry = |idx, handler: unsafe extern "C" fn()| {
178+
let handler_addr = handler as *const () as u64;
179+
unsafe {
180+
(&raw mut (*idt).entries[idx as usize]).write_volatile(IdtEntry::new(handler_addr));
181+
}
182+
};
183+
set_idt_entry(Exception::DivideByZero, _do_excp0); // Divide by zero
184+
set_idt_entry(Exception::Debug, _do_excp1); // Debug
185+
set_idt_entry(Exception::NonMaskableInterrupt, _do_excp2); // Non-maskable interrupt
186+
set_idt_entry(Exception::Breakpoint, _do_excp3); // Breakpoint
187+
set_idt_entry(Exception::Overflow, _do_excp4); // Overflow
188+
set_idt_entry(Exception::BoundRangeExceeded, _do_excp5); // Bound Range Exceeded
189+
set_idt_entry(Exception::InvalidOpcode, _do_excp6); // Invalid Opcode
190+
set_idt_entry(Exception::DeviceNotAvailable, _do_excp7); // Device Not Available
191+
set_idt_entry(Exception::DoubleFault, _do_excp8); // Double Fault
192+
set_idt_entry(Exception::CoprocessorSegmentOverrun, _do_excp9); // Coprocessor Segment Overrun
193+
set_idt_entry(Exception::InvalidTSS, _do_excp10); // Invalid TSS
194+
set_idt_entry(Exception::SegmentNotPresent, _do_excp11); // Segment Not Present
195+
set_idt_entry(Exception::StackSegmentFault, _do_excp12); // Stack-Segment Fault
196+
set_idt_entry(Exception::GeneralProtectionFault, _do_excp13); // General Protection Fault
197+
set_idt_entry(Exception::PageFault, _do_excp14); // Page Fault
198+
set_idt_entry(Exception::Reserved, _do_excp15); // Reserved
199+
set_idt_entry(Exception::X87FloatingPointException, _do_excp16); // x87 Floating-Point Exception
200+
set_idt_entry(Exception::AlignmentCheck, _do_excp17); // Alignment Check
201+
set_idt_entry(Exception::MachineCheck, _do_excp18); // Machine Check
202+
set_idt_entry(Exception::SIMDFloatingPointException, _do_excp19); // SIMD Floating-Point Exception
203+
set_idt_entry(Exception::VirtualizationException, _do_excp20); // Virtualization Exception
204+
set_idt_entry(Exception::SecurityException, _do_excp30); // Security Exception
205+
206+
let idtr = IdtPointer {
207+
limit: (core::mem::size_of::<IDT>() - 1) as u16,
208+
base: idt as u64,
209+
};
210+
unsafe {
211+
asm!(
212+
"lidt [{}]",
213+
in(reg) &idtr,
214+
options(readonly, nostack, preserves_flags)
215+
);
216+
}
217+
}

0 commit comments

Comments
 (0)