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

Commit

Permalink
vmclear and ptrload works, time to launch vm
Browse files Browse the repository at this point in the history
  • Loading branch information
memN0ps committed Feb 17, 2024
1 parent 8604cd8 commit 6844000
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 210 deletions.
2 changes: 1 addition & 1 deletion driver/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ fn main(_image_handle: Handle, mut system_table: SystemTable<Boot>) -> Status {

info!("The hypervisor has been installed successfully!");

//system_table.boot_services().stall(10_000_000);
system_table.boot_services().stall(20_000_000);

Status::SUCCESS
}
Expand Down
208 changes: 40 additions & 168 deletions hypervisor/src/intel/segmentation.rs
Original file line number Diff line number Diff line change
@@ -1,177 +1,49 @@
//! This module provides utilities and structures to manage segment descriptors
//! in the GDT (Global Descriptor Table) and LDT (Local Descriptor Table).
//! It handles the extraction, representation, and manipulation of segment descriptors.
//!
//! Credits to rCore OS for providing an accessible and comprehensible implementation of segmentation:
//! https://github.com/rcore-os/RVM1.5/blob/main/src/arch/x86_64/segmentation.rs
use core::arch::asm;
use x86::bits64::rflags::RFlags;
use x86::segmentation::SegmentSelector;

use {
crate::intel::descriptor::Descriptors,
bit_field::BitField,
bitflags::bitflags,
x86::{dtables::DescriptorTablePointer, segmentation::SegmentSelector},
x86_64::structures::gdt::DescriptorFlags,
};
pub fn access_rights_from_native(access_rights: u32) -> u32 {
const VMX_SEGMENT_ACCESS_RIGHTS_UNUSABLE_FLAG: u32 = 1 << 16;

bitflags! {
/// Access rights for VMCS guest register states.
///
/// Reference: Intel® 64 and IA-32 Architectures Software Developer's Manual: 24.4.1 Guest Register State
/// and Table 24-2. Format of Access Rights.
pub struct SegmentAccessRights: u32 {
/// Accessed flag.
/// Reference: Intel® 64 and IA-32 Architectures Software Developer's Manual: 3.4.5.1 Code- and Data-Segment Descriptor Types
const ACCESSED = 1 << 0;

/// Readable (for code segments) or Writable (for data segments).
/// Reference: Intel® 64 and IA-32 Architectures Software Developer's Manual: 3.4.5.1 Code- and Data-Segment Descriptor Types
const RW = 1 << 1;

/// Conforming bit for code segments.
/// Reference: Intel® 64 and IA-32 Architectures Software Developer's Manual: 3.4.5.1 Code- and Data-Segment Descriptor Types
const CONFORMING = 1 << 2;

/// Executable bit. Must be set for code segments.
/// Reference: Intel® 64 and IA-32 Architectures Software Developer's Manual: 3.4.5.1 Code- and Data-Segment Descriptor Types
const EXECUTABLE = 1 << 3;

/// Descriptor type (0 = system; 1 = code or data).
/// Reference: Intel® 64 and IA-32 Architectures Software Developer's Manual: 3.4.5 Segment Descriptors
const CODE_DATA = 1 << 4;

/// Segment present.
/// Reference: Intel® 64 and IA-32 Architectures Software Developer's Manual: 3.4.5 Segment Descriptors
const PRESENT = 1 << 7;

/// Long mode active (for CS only).
/// Reference: Intel® 64 and IA-32 Architectures Software Developer's Manual: 3.4.5.1 Code- and Data-Segment Descriptor Types
const LONG_MODE = 1 << 13;

/// Default operation size (0 = 16-bit segment; 1 = 32-bit segment).
/// Reference: Intel® 64 and IA-32 Architectures Software Developer's Manual: 3.4.5 Segment Descriptors
const DB = 1 << 14;

/// Granularity.
/// Reference: Intel® 64 and IA-32 Architectures Software Developer's Manual: 3.4.5 Segment Descriptors
const GRANULARITY = 1 << 15;

/// Segment unusable (0 = usable; 1 = unusable).
/// Reference: Intel® 64 and IA-32 Architectures Software Developer's Manual: 24.4.1 Guest Register State
const UNUSABLE = 1 << 16;

/// Privilege level mask (bits 5-6).
/// Reference: Intel® 64 and IA-32 Architectures Software Developer's Manual: 3.4.5 Segment Descriptors
const DPL_MASK = 3 << 5;
if access_rights == 0 {
return VMX_SEGMENT_ACCESS_RIGHTS_UNUSABLE_FLAG;
}
}

impl SegmentAccessRights {
/// Constructs `SegmentAccessRights` from a segment descriptor.
///
/// The access rights are extracted from bits 40-55 of the segment descriptor.
/// Only bits 8-15 and 0-7 within this range are directly related to access rights.
///
/// # Arguments
///
/// * `desc` - The segment descriptor from which to extract access rights.
///
/// # Returns
///
/// A `SegmentAccessRights` instance representing the extracted access rights.
pub fn from_descriptor(desc: u64) -> Self {
// Extract bits 40-55 from the descriptor
let access_bits = desc.get_bits(40..56) as u32;

// Mask out the unwanted bits to get only the relevant access rights
let relevant_bits = access_bits & 0xf0ff;

Self::from_bits_truncate(relevant_bits)
}
(access_rights >> 8) & 0b1111_0000_1111_1111
}

/// Represents the details of a segment descriptor in the GDT or LDT.
/// Segment descriptors are used to define the characteristics of a segment.
///
/// Reference: Intel® 64 and IA-32 Architectures Software Developer's Manual: 3.4.5 Segment Descriptors
/// and Figure 3-8. Segment Descriptor
#[repr(C, align(16))]
pub struct SegmentDescriptor {
/// Selector provides an index into the GDT or LDT, pointing to the segment descriptor.
pub selector: SegmentSelector,
/// The starting address of the segment.
pub base_address: u64,
/// The size of the segment. The ending address is calculated as base_address + segment_limit.
pub segment_limit: u32,
/// Flags detailing the properties of the segment.
pub access_rights: SegmentAccessRights,
//
pub(crate) fn lsl(selector: SegmentSelector) -> u32 {
let flags: u64;
let mut limit: u64;
unsafe {
asm!(
"lsl {}, {}",
"pushfq",
"pop {}",
out(reg) limit,
in(reg) u64::from(selector.bits()),
lateout(reg) flags
);
};
assert!(RFlags::from_raw(flags).contains(RFlags::FLAGS_ZF));
limit as u32
}

impl SegmentDescriptor {
/// Returns an invalid `SegmentDescriptor`.
/// This is useful to represent a non-present or non-configured segment.
pub const fn invalid() -> Self {
Self {
selector: SegmentSelector::empty(),
base_address: 0,
segment_limit: 0,
access_rights: SegmentAccessRights::UNUSABLE,
}
}

/// Constructs a `SegmentDescriptor` from a given segment selector and a pointer to the GDT.
///
/// The method uses the segment selector to index into the GDT and retrieve the associated segment descriptor.
/// It then extracts the base address, segment limit, and access rights from the descriptor.
///
/// # Arguments
///
/// * `selector` - A segment selector that provides an index into the GDT.
/// * `gdtr` - A pointer to the GDT.
pub fn from_selector(selector: SegmentSelector, gdtr: &DescriptorTablePointer<u64>) -> Self {
// Index into the GDT using the selector's index value.
let index = selector.index() as usize;
let table = Descriptors::from_pointer(gdtr);

// Fetch the descriptor entry from the GDT.
let entry_value = table[index];

// Convert the entry value into descriptor flags.
let entry = DescriptorFlags::from_bits_truncate(entry_value);

// If the segment is present in memory, extract its properties.
if entry.contains(DescriptorFlags::PRESENT) {
// Extract base address from the descriptor.
let base_low = entry_value.get_bits(16..40);
let base_high = entry_value.get_bits(56..64) << 24;
let mut base_address = base_low | base_high;

// Extract segment limit from the descriptor.
let segment_limit_low = entry_value.get_bits(0..16);
let segment_limit_high = entry_value.get_bits(48..52) << 12;
let mut segment_limit = segment_limit_low | segment_limit_high;

// For non-user segments (like TSS), the base address can span two GDT entries.
// If this is the case, fetch the high 32 bits of the base address from the next GDT entry.
if !entry.contains(DescriptorFlags::USER_SEGMENT) {
let high = table[index + 1];
base_address += high << 32;
}

// If the granularity flag is set, the segment limit is scaled by a factor of 4096.
if entry.contains(DescriptorFlags::GRANULARITY) {
segment_limit = (segment_limit << 12) | 0xfff;
}

// Construct and return the `SegmentDescriptor`.
Self {
selector,
base_address,
segment_limit: segment_limit as _,
access_rights: SegmentAccessRights::from_descriptor(entry_value),
}
} else {
// If the segment is not present, return an invalid descriptor.
Self::invalid()
}
}
/// LAR-Load Access Rights Byte
pub(crate) fn lar(selector: SegmentSelector) -> u32 {
let flags: u64;
let mut access_rights: u64;
unsafe {
asm!(
"lar {}, {}",
"pushfq",
"pop {}",
out(reg) access_rights,
in(reg) u64::from(selector.bits()),
lateout(reg) flags
);
};
assert!(RFlags::from_raw(flags).contains(RFlags::FLAGS_ZF));
access_rights as u32
}
70 changes: 29 additions & 41 deletions hypervisor/src/intel/vmcs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ use {
paging::BASE_PAGE_SIZE,
rflags,
},
controlregs, dtables, msr,
segmentation::{cs, ds, es, fs, gs, SegmentSelector, ss},
msr,
segmentation::{cs, ds, es, fs, gs, ss},
vmx::vmcs,
debugregs::dr7,
},
Expand All @@ -22,12 +22,12 @@ use {
controls::{adjust_vmx_controls, VmxControl},
descriptor::Descriptors,
paging::PageTables,
segmentation::SegmentDescriptor,
shared_data::SharedData,
support::{rdmsr, sidt, vmread, vmwrite},
support::{rdmsr, sidt, vmread, vmwrite, cr0, cr3},
invvpid::{invvpid_single_context, VPID_TAG},
invept::invept_single_context,
page::Page,
segmentation::{access_rights_from_native, lar, lsl},
},
},
};
Expand Down Expand Up @@ -71,8 +71,8 @@ impl Vmcs {

let idtr = sidt();

unsafe { vmwrite(vmcs::guest::CR0, controlregs::cr0().bits() as u64) };
unsafe { vmwrite(vmcs::guest::CR3, controlregs::cr3()) };
vmwrite(vmcs::guest::CR0, cr0().bits() as u64);
vmwrite(vmcs::guest::CR3, cr3());
vmwrite(vmcs::guest::CR4, Cr4::read_raw());

vmwrite(vmcs::guest::DR7, unsafe { dr7().0 as u64 });
Expand All @@ -88,48 +88,38 @@ impl Vmcs {
vmwrite(vmcs::guest::FS_SELECTOR, fs().bits());
vmwrite(vmcs::guest::GS_SELECTOR, gs().bits());

vmwrite(vmcs::guest::LDTR_SELECTOR, 0u16); // this is not 0 in Hypervisor-101-in-Rust but is in Hello-VT-rp
vmwrite(vmcs::guest::LDTR_SELECTOR, 0u16);
vmwrite(vmcs::guest::TR_SELECTOR, guest_descriptor.tr.bits());

unsafe { vmwrite(vmcs::guest::FS_BASE, msr::rdmsr(msr::IA32_FS_BASE)) };
unsafe { vmwrite(vmcs::guest::GS_BASE, msr::rdmsr(msr::IA32_GS_BASE)) };
unsafe { vmwrite(vmcs::guest::LDTR_BASE, SegmentDescriptor::from_selector(SegmentSelector::from_raw(dtables::ldtr().bits()), &guest_descriptor.gdtr).base_address) };
// All segment base registers are assumed to be zero, except that of TR.
vmwrite(vmcs::guest::TR_BASE, guest_descriptor.tss.base);

vmwrite(vmcs::guest::CS_LIMIT, SegmentDescriptor::from_selector(SegmentSelector::from_raw(ss().bits()), &guest_descriptor.gdtr).segment_limit);
vmwrite(vmcs::guest::SS_LIMIT, SegmentDescriptor::from_selector(SegmentSelector::from_raw(ss().bits()), &guest_descriptor.gdtr).segment_limit);
vmwrite(vmcs::guest::DS_LIMIT, SegmentDescriptor::from_selector(SegmentSelector::from_raw(ds().bits()), &guest_descriptor.gdtr).segment_limit);
vmwrite(vmcs::guest::ES_LIMIT, SegmentDescriptor::from_selector(SegmentSelector::from_raw(es().bits()), &guest_descriptor.gdtr).segment_limit);
vmwrite(vmcs::guest::FS_LIMIT, SegmentDescriptor::from_selector(SegmentSelector::from_raw(fs().bits()), &guest_descriptor.gdtr).segment_limit);
vmwrite(vmcs::guest::GS_LIMIT, SegmentDescriptor::from_selector(SegmentSelector::from_raw(gs().bits()), &guest_descriptor.gdtr).segment_limit);

vmwrite(vmcs::guest::LDTR_LIMIT, 0u32); // this is not 0 in Hypervisor-101-in-Rust but is in Hello-VT-rp
vmwrite(vmcs::guest::CS_LIMIT, lsl(ss()));
vmwrite(vmcs::guest::SS_LIMIT, lsl(ss()));
vmwrite(vmcs::guest::DS_LIMIT, lsl(ds()));
vmwrite(vmcs::guest::ES_LIMIT, lsl(es()));
vmwrite(vmcs::guest::FS_LIMIT, lsl(fs()));
vmwrite(vmcs::guest::GS_LIMIT, lsl(gs()));
vmwrite(vmcs::guest::LDTR_LIMIT, 0u32);
vmwrite(vmcs::guest::TR_LIMIT, guest_descriptor.tr.bits());

vmwrite(vmcs::guest::CS_ACCESS_RIGHTS, SegmentDescriptor::from_selector(SegmentSelector::from_raw(cs().bits()), &guest_descriptor.gdtr).access_rights.bits());
vmwrite(vmcs::guest::SS_ACCESS_RIGHTS, SegmentDescriptor::from_selector(SegmentSelector::from_raw(ss().bits()), &guest_descriptor.gdtr).access_rights.bits());
vmwrite(vmcs::guest::DS_ACCESS_RIGHTS, SegmentDescriptor::from_selector(SegmentSelector::from_raw(ds().bits()), &guest_descriptor.gdtr).access_rights.bits());
vmwrite(vmcs::guest::ES_ACCESS_RIGHTS, SegmentDescriptor::from_selector(SegmentSelector::from_raw(es().bits()), &guest_descriptor.gdtr).access_rights.bits());
vmwrite(vmcs::guest::FS_ACCESS_RIGHTS, SegmentDescriptor::from_selector(SegmentSelector::from_raw(fs().bits()), &guest_descriptor.gdtr).access_rights.bits());
vmwrite(vmcs::guest::GS_ACCESS_RIGHTS, SegmentDescriptor::from_selector(SegmentSelector::from_raw(gs().bits()), &guest_descriptor.gdtr).access_rights.bits());

// https://github.com/tandasat/Hello-VT-rp/blob/main/hypervisor/src/intel_vt/vm.rs#L93-L97
vmwrite(vmcs::guest::LDTR_ACCESS_RIGHTS, SegmentDescriptor::from_selector(SegmentSelector::from_raw(0), &guest_descriptor.gdtr).access_rights.bits());
vmwrite(vmcs::guest::TR_ACCESS_RIGHTS, SegmentDescriptor::from_selector(SegmentSelector::from_raw(guest_descriptor.tss.ar as u16), &guest_descriptor.gdtr).access_rights.bits());
vmwrite(vmcs::guest::CS_ACCESS_RIGHTS, access_rights_from_native(lar(cs())) as u64);
vmwrite(vmcs::guest::SS_ACCESS_RIGHTS, access_rights_from_native(lar(ss())) as u64);
vmwrite(vmcs::guest::DS_ACCESS_RIGHTS, access_rights_from_native(lar(ds())) as u64);
vmwrite(vmcs::guest::ES_ACCESS_RIGHTS, access_rights_from_native(lar(es())) as u64);
vmwrite(vmcs::guest::FS_ACCESS_RIGHTS, access_rights_from_native(lar(fs())) as u64);
vmwrite(vmcs::guest::GS_ACCESS_RIGHTS, access_rights_from_native(lar(gs())) as u64);
vmwrite(vmcs::guest::LDTR_ACCESS_RIGHTS, access_rights_from_native(0u32));
vmwrite(vmcs::guest::TR_ACCESS_RIGHTS, access_rights_from_native(guest_descriptor.tss.ar));

vmwrite(vmcs::guest::GDTR_BASE, guest_descriptor.gdtr.base as u64);
vmwrite(vmcs::guest::IDTR_BASE, idtr.base as u64);

vmwrite(vmcs::guest::GDTR_LIMIT, guest_descriptor.gdtr.limit as u64);
vmwrite(vmcs::guest::IDTR_LIMIT, idtr.limit as u64);

unsafe {
vmwrite(vmcs::guest::IA32_DEBUGCTL_FULL, msr::rdmsr(msr::IA32_DEBUGCTL));
vmwrite(vmcs::guest::IA32_SYSENTER_CS, msr::rdmsr(msr::IA32_SYSENTER_CS));
vmwrite(vmcs::guest::IA32_SYSENTER_ESP, msr::rdmsr(msr::IA32_SYSENTER_ESP));
vmwrite(vmcs::guest::IA32_SYSENTER_EIP, msr::rdmsr(msr::IA32_SYSENTER_EIP));
vmwrite(vmcs::guest::LINK_PTR_FULL, u64::MAX);
}
vmwrite(vmcs::guest::LINK_PTR_FULL, u64::MAX);


// Note: VMCS does not manage all registers; some require manual intervention for saving and loading.
// This includes general-purpose registers and xmm registers, which must be explicitly preserved and restored by the software.
Expand Down Expand Up @@ -185,9 +175,9 @@ impl Vmcs {

let pml4_pa = host_paging.get_pml4_pa()?;

unsafe { vmwrite(vmcs::host::CR0, controlregs::cr0().bits() as u64) };
vmwrite(vmcs::host::CR0, cr0().bits() as u64);
vmwrite(vmcs::host::CR3, pml4_pa);
unsafe { vmwrite(vmcs::host::CR4, controlregs::cr4().bits() as u64) };
vmwrite(vmcs::host::CR4, Cr4::read_raw());

vmwrite(vmcs::host::CS_SELECTOR, host_descriptor.cs.bits());
vmwrite(vmcs::host::TR_SELECTOR, host_descriptor.tr.bits());
Expand Down Expand Up @@ -231,10 +221,8 @@ impl Vmcs {
vmwrite(vmcs::control::VMEXIT_CONTROLS, adjust_vmx_controls(VmxControl::VmExit, EXIT_CTL));
vmwrite(vmcs::control::PINBASED_EXEC_CONTROLS, adjust_vmx_controls(VmxControl::PinBased, PINBASED_CTL));

unsafe {
vmwrite(vmcs::control::CR0_READ_SHADOW, controlregs::cr0().bits() as u64);
vmwrite(vmcs::control::CR4_READ_SHADOW, controlregs::cr4().bits() as u64);
};
vmwrite(vmcs::control::CR0_READ_SHADOW, cr0().bits() as u64);
vmwrite(vmcs::control::CR4_READ_SHADOW, Cr4::read_raw());

vmwrite(vmcs::control::MSR_BITMAPS_ADDR_FULL, msr_bitmap.as_ref() as *const _ as u64);
//vmwrite(vmcs::control::EXCEPTION_BITMAP, 1u64 << (ExceptionInterrupt::Breakpoint as u32));
Expand Down

0 comments on commit 6844000

Please sign in to comment.