Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[interrupt-handlers] adds exception handling to the hyperlight guest #250

Merged
merged 5 commits into from
Feb 24, 2025
Merged
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
6 changes: 6 additions & 0 deletions src/hyperlight_guest/src/entrypoint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,12 @@ use hyperlight_common::mem::{HyperlightPEB, RunMode};
use log::LevelFilter;
use spin::Once;

use crate::gdt::load_gdt;
use crate::guest_error::reset_error;
use crate::guest_function_call::dispatch_function;
use crate::guest_logger::init_logger;
use crate::host_function_call::{outb, OutBAction};
use crate::idtr::load_idt;
use crate::{
__security_cookie, HEAP_ALLOCATOR, MIN_STACK_ADDRESS, OS_PAGE_SIZE, OUTB_PTR,
OUTB_PTR_WITH_CONTEXT, P_PEB, RUNNING_MODE,
Expand Down Expand Up @@ -104,6 +106,10 @@ pub extern "win64" fn entrypoint(peb_address: u64, seed: u64, ops: u64, max_log_
// It also means that should we change the layout of the struct in the future, we
// don't have to change the assembly code.
MIN_STACK_ADDRESS = (*peb_ptr).gueststackData.minUserStackAddress;

// Setup GDT and IDT
load_gdt();
load_idt();
}
RunMode::InProcessLinux | RunMode::InProcessWindows => {
RUNNING_MODE = (*peb_ptr).runMode;
Expand Down
97 changes: 97 additions & 0 deletions src/hyperlight_guest/src/gdt.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/*
Copyright 2024 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;
use core::ptr::addr_of;

/// Entry in the Global Descriptor Table (GDT)
/// For reference, see page 3-10 Vol. 3A of Intel 64 and IA-32
/// Architectures Software Developer's Manual, figure 3-8
/// (https://i.imgur.com/1i9xUmx.png).
/// From the bottom, we have:
/// - segment limit 15..0 = limit_low
/// - base address 31..16 = base_low
/// - base 23..16 = base_middle
/// - p dpl s type 15..8 = access
/// - p d/b l avl seg. limit 23..16 = flags_limit
/// - base 31..24 = base_high
#[repr(C, align(8))]
pub struct GdtEntry {
limit_low: u16,
base_low: u16,
base_middle: u8,
access: u8,
flags_limit: u8,
base_high: u8,
}

impl GdtEntry {
/// Creates a new GDT entry.
pub const fn new(base: u32, limit: u32, access: u8, flags: u8) -> Self {
Self {
base_low: (base & 0xffff) as u16,
base_middle: ((base >> 16) & 0xff) as u8,
base_high: ((base >> 24) & 0xff) as u8,
limit_low: (limit & 0xffff) as u16,
flags_limit: (((limit >> 16) & 0x0f) as u8) | ((flags & 0x0f) << 4),
access,
}
}
}

// Global Descriptor Table (GDT)
// For reference, see page 2-3 Vol. 3A of Intel 64 and IA-32
// Architectures Software Developer's Manual.
static mut GDT: [GdtEntry; 3] = [
// Null descriptor
GdtEntry::new(0, 0, 0, 0),
// Kernel Code Segment (0x08)
GdtEntry::new(0, 0, 0x9A, 0xA),
// Kernel Data Segment (0x10)
GdtEntry::new(0, 0, 0x92, 0xC),
];

/// GDTR (GDT pointer)
#[repr(C, packed)]
struct GdtPointer {
size: u16,
base: u64,
}

/// Load the GDT
pub unsafe fn load_gdt() {
let gdt_ptr = GdtPointer {
size: (core::mem::size_of::<[GdtEntry; 3]>() - 1) as u16,
base: addr_of!(GDT) as *const _ as u64,
};

asm!(
"lgdt [{0}]",
"mov ax, 0x10", // Load data segment registers
"mov ds, ax",
"mov es, ax",
"mov fs, ax",
"mov gs, ax",
"mov ss, ax",
"push 0x08", // Push CS (kernel code segment)
"lea rax, [2f + rip]", // Load the next instruction's address
"push rax", // Push address onto stack
"retfq", // Far return to update CS
"2:", // Label for continued execution
in(reg) &gdt_ptr,
options(nostack, preserves_flags)
);
}
102 changes: 102 additions & 0 deletions src/hyperlight_guest/src/idt.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/*
Copyright 2024 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 crate::interrupt_entry::{
_do_excp0, _do_excp1, _do_excp10, _do_excp11, _do_excp12, _do_excp13, _do_excp14, _do_excp15,
_do_excp16, _do_excp17, _do_excp18, _do_excp19, _do_excp2, _do_excp20, _do_excp3, _do_excp30,
_do_excp4, _do_excp5, _do_excp6, _do_excp7, _do_excp8, _do_excp9,
};

// An entry in the Interrupt Descriptor Table (IDT)
// For reference, see page 7-20 Vol. 3A of Intel 64 and IA-32
// Architectures Software Developer's Manual, figure 7-8
// (i.e., https://i.imgur.com/N4rEjHj.png).
// From the bottom, we have:
// - offset 15..0 = offset_low
// - segment selector 31..16 = selector
// - 000 0 0 Interrupt Stack Table 7..0 = interrupt_stack_table_offset
// - p dpl 0 type 15..8 = type_attr
// - offset 31..16 = offset_mid
// - offset 63..32 = offset_high
// - reserved 31..0 = zero
#[repr(C, align(16))]
pub(crate) struct IdtEntry {
offset_low: u16, // Lower 16 bits of handler address
selector: u16, // code segment selector in GDT
interrupt_stack_table_offset: u8, // Interrupt Stack Table offset
type_attr: u8, // Gate type and flags
offset_mid: u16, // Middle 16 bits of handler address
offset_high: u32, // High 32 bits of handler address
zero: u32, // Reserved (always 0)
}

impl IdtEntry {
fn new(handler: u64) -> Self {
Self {
offset_low: (handler & 0xFFFF) as u16,
selector: 0x08, // Kernel Code Segment
interrupt_stack_table_offset: 0, // No interrupt stack table used
type_attr: 0x8E,
// 0x8E = 10001110b
// 1 00 0 1110
// 1 = Present
// 00 = Descriptor Privilege Level (0)
// 0 = Storage Segment (0)
// 1110 = Gate Type (0b1110 = 14 = 0xE)
// 0xE means it's an interrupt gate
offset_mid: ((handler >> 16) & 0xFFFF) as u16,
offset_high: ((handler >> 32) & 0xFFFFFFFF) as u32,
zero: 0,
}
}
}

// The IDT is an array of 256 IDT entries
// (for reference, see page 7-9 Vol. 3A of Intel 64 and IA-32
// Architectures Software Developer's Manual).
pub(crate) static mut IDT: [IdtEntry; 256] = unsafe { core::mem::zeroed() };

pub(crate) fn init_idt() {
set_idt_entry(0, _do_excp0); // Divide by zero
set_idt_entry(1, _do_excp1); // Debug
set_idt_entry(2, _do_excp2); // Non-maskable interrupt
set_idt_entry(3, _do_excp3); // Breakpoint
set_idt_entry(4, _do_excp4); // Overflow
set_idt_entry(5, _do_excp5); // Bound Range Exceeded
set_idt_entry(6, _do_excp6); // Invalid Opcode
set_idt_entry(7, _do_excp7); // Device Not Available
set_idt_entry(8, _do_excp8); // Double Fault
set_idt_entry(9, _do_excp9); // Coprocessor Segment Overrun
set_idt_entry(10, _do_excp10); // Invalid TSS
set_idt_entry(11, _do_excp11); // Segment Not Present
set_idt_entry(12, _do_excp12); // Stack-Segment Fault
set_idt_entry(13, _do_excp13); // General Protection Fault
set_idt_entry(14, _do_excp14); // Page Fault
set_idt_entry(15, _do_excp15); // Reserved
set_idt_entry(16, _do_excp16); // x87 Floating-Point Exception
set_idt_entry(17, _do_excp17); // Alignment Check
set_idt_entry(18, _do_excp18); // Machine Check
set_idt_entry(19, _do_excp19); // SIMD Floating-Point Exception
set_idt_entry(20, _do_excp20); // Virtualization Exception
set_idt_entry(30, _do_excp30); // Security Exception
}

fn set_idt_entry(index: usize, handler: unsafe extern "sysv64" fn()) {
let handler_addr = handler as *const () as u64;
unsafe {
IDT[index] = IdtEntry::new(handler_addr);
}
}
32 changes: 32 additions & 0 deletions src/hyperlight_guest/src/idtr.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
use core::ptr::addr_of;

use crate::idt::{init_idt, IdtEntry, IDT};

#[repr(C, packed)]
pub struct Idtr {
pub limit: u16,
pub base: u64,
}

static mut IDTR: Idtr = Idtr { limit: 0, base: 0 };

impl Idtr {
pub unsafe fn init(&mut self, base: u64, size: u16) {
self.limit = size - 1;
self.base = base;
}

pub unsafe fn load(&self) {
core::arch::asm!("lidt [{}]", in(reg) self, options(readonly, nostack, preserves_flags));
}
}

pub(crate) unsafe fn load_idt() {
init_idt();

let idt_size = 256 * size_of::<IdtEntry>();
let expected_base = addr_of!(IDT) as *const _ as u64;

IDTR.init(expected_base, idt_size as u16);
IDTR.load();
}
Loading
Loading