Skip to content
This repository was archived by the owner on Sep 1, 2024. It is now read-only.

Commit a40665e

Browse files
committed
vmclear and ptrld success
1 parent b4be714 commit a40665e

16 files changed

+1315
-237
lines changed

driver/src/main.rs

+32-2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
#![feature(new_uninit)]
12
#![no_main]
23
#![no_std]
34

@@ -6,8 +7,17 @@ extern crate alloc;
67
use {
78
log::*,
89
uefi::prelude::*,
9-
hypervisor::{vmm::is_hypervisor_present, intel::capture::{capture_registers, GuestRegisters}},
10+
hypervisor::{
11+
vmm::is_hypervisor_present,
12+
intel::{
13+
capture::{capture_registers, GuestRegisters},
14+
ept::paging::{AccessType, Ept},
15+
shared_data::SharedData,
16+
},
17+
error::HypervisorError
18+
},
1019
crate::virtualize::virtualize_system,
20+
alloc::boxed::Box,
1121
};
1222

1323
pub mod virtualize;
@@ -21,6 +31,11 @@ fn main(_image_handle: Handle, mut system_table: SystemTable<Boot>) -> Status {
2131

2232
info!("The Matrix is an illusion");
2333

34+
let mut shared_data = match setup_ept() {
35+
Ok(shared_data) => shared_data,
36+
Err(e) => panic!("Failed to setup EPT: {:?}", e),
37+
};
38+
2439
// Capture the register values to be used as an initial state of the VM.
2540
let mut regs = GuestRegisters::default();
2641
unsafe { capture_registers(&mut regs) }
@@ -30,7 +45,7 @@ fn main(_image_handle: Handle, mut system_table: SystemTable<Boot>) -> Status {
3045
// installing the hypervisor.
3146
if !is_hypervisor_present() {
3247
debug!("Virtualizing the system");
33-
virtualize_system(&regs, &system_table);
48+
virtualize_system(&regs, &mut shared_data, &system_table);
3449
}
3550

3651
info!("The hypervisor has been installed successfully!");
@@ -39,3 +54,18 @@ fn main(_image_handle: Handle, mut system_table: SystemTable<Boot>) -> Status {
3954

4055
Status::SUCCESS
4156
}
57+
58+
pub fn setup_ept() -> Result<Box<SharedData>, HypervisorError> {
59+
let mut primary_ept: Box<Ept> = unsafe { Box::new_zeroed().assume_init() };
60+
let mut secondary_ept: Box<Ept> = unsafe { Box::new_zeroed().assume_init() };
61+
62+
debug!("Creating Primary EPT");
63+
primary_ept.identity_2mb(AccessType::READ_WRITE_EXECUTE)?;
64+
65+
debug!("Creating Secondary EPT");
66+
secondary_ept.identity_2mb(AccessType::READ_WRITE_EXECUTE)?;
67+
68+
let shared_data = SharedData::new(primary_ept, secondary_ept);
69+
70+
shared_data
71+
}

driver/src/virtualize.rs

+13-9
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,22 @@ use {
33
core::{alloc::Layout, arch::global_asm},
44
hypervisor::{
55
intel::{
6-
capture::GuestRegisters, page::Page
6+
capture::GuestRegisters, page::Page,
7+
ept::paging::{Ept, AccessType},
8+
shared_data::SharedData,
79
},
8-
vmm::start_hypervisor
10+
vmm::start_hypervisor,
11+
error::HypervisorError,
912
},
1013
log::debug,
1114
uefi::{
1215
proto::loaded_image::LoadedImage,
1316
table::{Boot, SystemTable},
1417
},
18+
alloc::boxed::Box,
1519
};
1620

17-
pub fn virtualize_system(regs: &GuestRegisters, system_table: &SystemTable<Boot>) {
21+
pub fn virtualize_system(guest_registers: &GuestRegisters, shared_data: &mut SharedData, system_table: &SystemTable<Boot>) {
1822
let boot_service = system_table.boot_services();
1923

2024
// Open the loaded image protocol to get the current image base and image size.
@@ -55,23 +59,23 @@ pub fn virtualize_system(regs: &GuestRegisters, system_table: &SystemTable<Boot>
5559
let stack_base = stack as u64 + layout.size() as u64 - 0x10;
5660
debug!("Stack range: {:#x?}", stack_base..stack as u64);
5761

58-
unsafe { switch_stack(regs, start_hypervisor as usize, stack_base) };
62+
unsafe { switch_stack(guest_registers, shared_data, start_hypervisor as usize, stack_base) };
5963
}
6064

6165
extern "efiapi" {
6266
/// Jumps to the landing code with the new stack pointer.
63-
fn switch_stack(regs: &GuestRegisters, landing_code: usize, stack_base: u64) -> !;
67+
fn switch_stack(guest_registers: &GuestRegisters, shared_data: &mut SharedData, landing_code: usize, stack_base: u64) -> !;
6468
}
6569

6670
global_asm!(r#"
6771
// The module containing the `switch_stack` function.
6872
6973
// Jumps to the landing code with the new stack pointer.
7074
//
71-
// fn switch_stack(regs: &GuestRegisters, landing_code: usize, stack_base: u64) -> !
75+
// fn switch_stack(guest_registers: &GuestRegisters, shared_data: &mut SharedData, landing_code: usize, stack_base: u64) -> !
7276
.global switch_stack
7377
switch_stack:
7478
xchg bx, bx
75-
mov rsp, r8
76-
jmp rdx
77-
"#);
79+
mov rsp, r9
80+
jmp r8
81+
"#);

hypervisor/Cargo.toml

+2-1
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,5 @@ static_assertions = "1.1.0" # https://crates.io/crates/static_assertions
2424
log = "0.4.20" # https://crates.io/crates/log
2525
com_logger = "0.1.1" # https://crates.io/crates/com_logger
2626
iced-x86 = { version = "1.20.0", default-features = false, features = ["no_std", "decoder", "block_encoder", "instr_info", "no_d3now", "no_evex", "no_vex", "no_xop"] } # https://crates.io/crates/iced-x86
27-
bstr = { version = "1.9.0", default-features = false}
27+
bstr = { version = "1.9.0", default-features = false}
28+
derivative = { version = "2.2.0", features = ["use_core"]} # https://crates.io/crates/derivative

hypervisor/src/error.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1-
use alloc::ffi::NulError;
2-
use thiserror_no_std::Error;
1+
use {
2+
alloc::ffi::NulError,
3+
thiserror_no_std::Error,
4+
};
35

46
#[derive(Error, Debug)]
57
pub enum HypervisorError {

hypervisor/src/intel/descriptor.rs

+117-86
Original file line numberDiff line numberDiff line change
@@ -1,110 +1,115 @@
1-
//! This module defines and manages the descriptor tables (GDT and IDT) for both the host and guest.
2-
//! It provides utilities to capture, initialize, and manage these tables.
3-
41
use {
5-
crate::{
6-
error::HypervisorError,
2+
x86::{
3+
dtables::DescriptorTablePointer,
4+
segmentation::{
5+
cs, BuildDescriptor, CodeSegmentType, Descriptor, DescriptorBuilder, GateDescriptorBuilder,
6+
SegmentDescriptorBuilder, SegmentSelector,
7+
},
78
},
89
alloc::{boxed::Box, vec::Vec},
9-
x86::dtables::DescriptorTablePointer,
10+
crate::intel::support::sgdt,
1011
};
11-
use crate::intel::support::{sgdt, sidt};
12-
13-
/// Represents the descriptor tables (GDT and IDT) for the host.
14-
/// Contains the GDT and IDT along with their respective register pointers.
15-
#[repr(C, align(4096))]
16-
pub struct DescriptorTables {
17-
/// Global Descriptor Table (GDT) for the host.
18-
/// Reference: Intel® 64 and IA-32 Architectures Software Developer's Manual: 3.5.1 Segment Descriptor Tables
19-
pub global_descriptor_table: Vec<u64>,
20-
21-
/// GDTR holds the address and size of the GDT.
22-
/// Reference: Intel® 64 and IA-32 Architectures Software Developer's Manual: 2.4.1 Global Descriptor Table Register (GDTR)
23-
pub gdtr: DescriptorTablePointer<u64>,
2412

25-
/// Interrupt Descriptor Table (IDT) for the host.
26-
/// Reference: Intel® 64 and IA-32 Architectures Software Developer's Manual: 6.10 INTERRUPT DESCRIPTOR TABLE (IDT)
27-
pub interrupt_descriptor_table: Vec<u64>,
28-
29-
/// IDTR holds the address and size of the IDT.
30-
/// Reference: Intel® 64 and IA-32 Architectures Software Developer's Manual: 2.4.3 IDTR Interrupt Descriptor Table Register
31-
pub idtr: DescriptorTablePointer<u64>,
13+
// UEFI does not set TSS in the GDT. This is incompatible to be both as VM and
14+
// hypervisor states. This struct supports creating a new GDT that does contain
15+
// the TSS.
16+
//
17+
// See: 27.2.3 Checks on Host Segment and Descriptor-Table Registers
18+
// See: 27.3.1.2 Checks on Guest Segment Registers
19+
pub struct Descriptors {
20+
gdt: Vec<u64>,
21+
pub gdtr: DescriptorTablePointer<u64>,
22+
pub cs: SegmentSelector,
23+
pub tr: SegmentSelector,
24+
pub tss: TaskStateSegment,
3225
}
26+
impl Default for Descriptors {
27+
fn default() -> Self {
28+
Self {
29+
gdt: Vec::new(),
30+
gdtr: DescriptorTablePointer::<u64>::default(),
31+
cs: SegmentSelector::from_raw(0),
32+
tr: SegmentSelector::from_raw(0),
33+
tss: TaskStateSegment::default(),
34+
}
35+
}
36+
}
37+
impl Descriptors {
38+
/// Creates a new GDT with TSS based on the current GDT.
39+
pub fn new_from_current() -> Self {
40+
log::debug!("Creating a new GDT with TSS for guest");
3341

34-
impl DescriptorTables {
35-
/// Captures the currently loaded GDT and IDT for the guest.
36-
pub fn initialize_for_guest(
37-
descriptor_tables: &mut Box<DescriptorTables>,
38-
) -> Result<(), HypervisorError> {
39-
log::trace!("Capturing current Global Descriptor Table (GDT) and Interrupt Descriptor Table (IDT) for guest");
40-
41-
// Capture the current GDT and IDT.
42-
descriptor_tables.gdtr = sgdt();
43-
descriptor_tables.idtr = sidt();
44-
45-
// Note: We don't need to create new tables for the guest;
46-
// we just capture the current ones.
42+
// Get the current GDT.
43+
let current_gdtr = sgdt();
44+
let current_gdt = unsafe {
45+
core::slice::from_raw_parts(
46+
current_gdtr.base.cast::<u64>(),
47+
usize::from(current_gdtr.limit + 1) / 8,
48+
)
49+
};
4750

48-
log::trace!("Captured GDT and IDT for guest successfully!");
51+
// Copy the current GDT.
52+
let mut descriptors = Self {
53+
gdt: current_gdt.to_vec(),
54+
..Default::default()
55+
};
4956

50-
Ok(())
51-
}
57+
// Append the TSS descriptor. Push extra 0 as it is 16 bytes.
58+
// See: 3.5.2 Segment Descriptor Tables in IA-32e Mode
59+
let tr_index = descriptors.gdt.len() as u16;
60+
descriptors
61+
.gdt
62+
.push(Self::task_segment_descriptor(&descriptors.tss).as_u64());
63+
descriptors.gdt.push(0);
5264

53-
/// Initializes and returns the descriptor tables (GDT and IDT) for the host.
54-
pub fn initialize_for_host(
55-
descriptor_tables: &mut Box<DescriptorTables>,
56-
) -> Result<(), HypervisorError> {
57-
log::trace!("Initializing descriptor tables for host");
65+
descriptors.gdtr = DescriptorTablePointer::new_from_slice(&descriptors.gdt);
66+
descriptors.cs = cs();
67+
descriptors.tr = SegmentSelector::new(tr_index, x86::Ring::Ring0);
5868

59-
descriptor_tables.copy_current_gdt();
60-
descriptor_tables.copy_current_idt();
69+
log::debug!("New GDT with TSS created for guest successfully!");
6170

62-
log::trace!("Initialized descriptor tables for host");
63-
Ok(())
71+
descriptors
6472
}
6573

66-
/// Copies the current GDT.
67-
fn copy_current_gdt(&mut self) {
68-
log::trace!("Copying current GDT");
74+
/// Creates a new GDT with TSS from scratch for the host.
75+
pub fn new_for_host() -> Self {
76+
log::debug!("Creating a new GDT with TSS for host");
6977

70-
// Get the current GDTR
71-
let current_gdtr = sgdt();
78+
let mut descriptors = Self::default();
7279

73-
// Create a slice from the current GDT entries.
74-
let current_gdt = Self::from_pointer(&current_gdtr);
80+
descriptors.gdt.push(0);
81+
descriptors
82+
.gdt
83+
.push(Self::code_segment_descriptor().as_u64());
84+
descriptors
85+
.gdt
86+
.push(Self::task_segment_descriptor(&descriptors.tss).as_u64());
87+
descriptors.gdt.push(0);
7588

76-
// Create a new GDT from the slice.
77-
let new_gdt = current_gdt.to_vec();
89+
descriptors.gdtr = DescriptorTablePointer::new_from_slice(&descriptors.gdt);
90+
descriptors.cs = SegmentSelector::new(1, x86::Ring::Ring0);
91+
descriptors.tr = SegmentSelector::new(2, x86::Ring::Ring0);
7892

79-
// Create a new GDTR from the new GDT.
80-
let new_gdtr = DescriptorTablePointer::new_from_slice(new_gdt.as_slice());
93+
log::debug!("New GDT with TSS created for host successfully!");
8194

82-
// Store the new GDT in the DescriptorTables structure
83-
self.global_descriptor_table = new_gdt;
84-
self.gdtr = new_gdtr;
85-
log::trace!("Copied current GDT");
95+
descriptors
8696
}
8797

88-
/// Copies the current IDT.
89-
fn copy_current_idt(&mut self) {
90-
log::trace!("Copying current IDT");
91-
92-
// Get the current IDTR
93-
let current_idtr = sidt();
94-
95-
// Create a slice from the current IDT entries.
96-
let current_idt = Self::from_pointer(&current_idtr);
97-
98-
// Create a new IDT from the slice.
99-
let new_idt = current_idt.to_vec();
100-
101-
// Create a new IDTR from the new IDT.
102-
let new_idtr = DescriptorTablePointer::new_from_slice(new_idt.as_slice());
98+
/// Builds a segment descriptor from the task state segment.
99+
fn task_segment_descriptor(tss: &TaskStateSegment) -> Descriptor {
100+
<DescriptorBuilder as GateDescriptorBuilder<u32>>::tss_descriptor(tss.base, tss.limit, true)
101+
.present()
102+
.dpl(x86::Ring::Ring0)
103+
.finish()
104+
}
103105

104-
// Store the new IDT in the DescriptorTables structure
105-
self.interrupt_descriptor_table = new_idt;
106-
self.idtr = new_idtr; // Use the same IDTR as it points to the correct base and limit
107-
log::trace!("Copied current IDT");
106+
fn code_segment_descriptor() -> Descriptor {
107+
DescriptorBuilder::code_descriptor(0, u32::MAX, CodeSegmentType::ExecuteAccessed)
108+
.present()
109+
.dpl(x86::Ring::Ring0)
110+
.limit_granularity_4kb()
111+
.l()
112+
.finish()
108113
}
109114

110115
/// Gets the table as a slice from the pointer.
@@ -116,4 +121,30 @@ impl DescriptorTables {
116121
)
117122
}
118123
}
119-
}
124+
}
125+
126+
#[derive(derivative::Derivative)]
127+
#[derivative(Debug)]
128+
pub struct TaskStateSegment {
129+
pub base: u64,
130+
pub limit: u64,
131+
pub ar: u32,
132+
#[allow(dead_code)]
133+
#[derivative(Debug = "ignore")]
134+
segment: Box<TaskStateSegmentRaw>,
135+
}
136+
impl Default for TaskStateSegment {
137+
fn default() -> Self {
138+
let segment = Box::new(TaskStateSegmentRaw([0; 104]));
139+
Self {
140+
base: segment.as_ref() as *const _ as u64,
141+
limit: core::mem::size_of_val(segment.as_ref()) as u64 - 1,
142+
ar: 0x8b00,
143+
segment,
144+
}
145+
}
146+
}
147+
148+
/// See: Figure 8-11. 64-Bit TSS Format
149+
150+
struct TaskStateSegmentRaw([u8; 104]);

0 commit comments

Comments
 (0)