From a4ad6da4f7413ec76daa06e735c2ccc9bd4406eb Mon Sep 17 00:00:00 2001 From: August Date: Fri, 31 Oct 2025 12:49:18 -0400 Subject: [PATCH] Initial work for hardware virtualization --- src/kernel/arch/x86_64/amd_virt.rs | 30 +++++++ src/kernel/arch/x86_64/cpuid.rs | 121 +++++++++++++++++++++++++++ src/kernel/arch/x86_64/intel_virt.rs | 17 ++++ src/kernel/arch/x86_64/mod.rs | 3 + src/kernel/main.rs | 13 ++- 5 files changed, 180 insertions(+), 4 deletions(-) create mode 100644 src/kernel/arch/x86_64/amd_virt.rs create mode 100644 src/kernel/arch/x86_64/cpuid.rs create mode 100644 src/kernel/arch/x86_64/intel_virt.rs diff --git a/src/kernel/arch/x86_64/amd_virt.rs b/src/kernel/arch/x86_64/amd_virt.rs new file mode 100644 index 0000000..b74d5f4 --- /dev/null +++ b/src/kernel/arch/x86_64/amd_virt.rs @@ -0,0 +1,30 @@ +// Copyright (c) 2025 shibedrill +// SPDX-License-Identifier: MIT + +use intbits::Bits; +use x86_64::registers::control::EferFlags; + +use crate::arch::x86_64::cpuid::*; + +pub fn amd_v_supported() -> bool { + match ProcessorVendor::try_get_current() { + Ok(vendor) => { + if vendor == ProcessorVendor::AMD { + match CPUID.get_extended_processor_and_feature_identifiers() { + Some(features) => features.has_svm(), + _ => false, + } + } else { + false + } + } + Err(_) => false, + } +} + +pub fn enable_svm() { + let mut efer = x86_64::registers::model_specific::Efer::read().bits(); + efer.set_bit(12, true); + assert!(efer.bit(12)); + unsafe { x86_64::registers::model_specific::Efer::write(EferFlags::from_bits_retain(efer)) }; +} \ No newline at end of file diff --git a/src/kernel/arch/x86_64/cpuid.rs b/src/kernel/arch/x86_64/cpuid.rs new file mode 100644 index 0000000..3d716a2 --- /dev/null +++ b/src/kernel/arch/x86_64/cpuid.rs @@ -0,0 +1,121 @@ +// Copyright (c) 2025 shibedrill +// SPDX-License-Identifier: MIT + +use lazy_static::lazy_static; +use raw_cpuid::CpuId; +use raw_cpuid::CpuIdReaderNative; + +use crate::arch::x86_64::amd_virt::amd_v_supported; +use crate::arch::x86_64::intel_virt::intel_vt_supported; + +lazy_static! { + pub static ref CPUID: CpuId = CpuId::new(); +} + +#[derive(Debug)] +pub enum VirtualizationProvider { + AmdV, + IntelVTx, + None, +} + +#[derive(PartialEq, Eq)] +pub enum ProcessorVendor { + AMD, + Intel, + Via, + Transmeta, + Cyrix, + Centaur, + Nexgen, + UMC, + SiS, + NSC, + Rise, + Vortex, + AO486, + Zaoxin, + Hygon, + Elbrus, + QEMU, + KVM, + VMware, + VirtualBox, + Xen, + HyperV, + Parallels, + QNX, +} + +impl TryFrom<&str> for ProcessorVendor { + type Error = (); + fn try_from(from: &str) -> Result { + match from { + "AuthenticAMD" | "AMDisbetter!" => Ok(Self::AMD), + "GenuineIntel" | "GenuineIotel" => Ok(Self::Intel), + "VIA VIA VIA " => Ok(Self::Via), + "GenuineTMx86" | "TransmetaCPU" => Ok(Self::Transmeta), + "CyrixInstead" => Ok(Self::Cyrix), + "CentaurHauls" => Ok(Self::Centaur), + "NexGenDriven" => Ok(Self::Nexgen), + "UMC UMC UMC " => Ok(Self::UMC), + "SiS SiS SiS " => Ok(Self::SiS), + "Geode by NSC" => Ok(Self::NSC), + "RiseRiseRise" => Ok(Self::Rise), + "Vortex86 SoC" => Ok(Self::Vortex), + "MiSTer AO486" | "GenuineAO486" => Ok(Self::AO486), + " Shanghai " => Ok(Self::Zaoxin), + "HygonGenuine" => Ok(Self::Hygon), + "E2K MACHINE " => Ok(Self::Elbrus), + "TCGTCGTCGTCG" => Ok(Self::QEMU), + " KVMKVMKVM " => Ok(Self::KVM), + "VMwareVMware" => Ok(Self::VMware), + "VBoxVBoxVBox" => Ok(Self::VirtualBox), + "XenVMMXenVMM" => Ok(Self::Xen), + "Microsoft Hv" => Ok(Self::HyperV), + " prl hyperv " | " lrpepyh vr " => Ok(Self::Parallels), + " QNXQVMBSQG " => Ok(Self::QNX), + _ => Err(()), + } + } +} + +impl ProcessorVendor { + pub fn is_hypervisor(&self) -> bool { + matches!(self, Self::QEMU + | Self::KVM + | Self::VMware + | Self::VirtualBox + | Self::HyperV + | Self::Parallels + | Self::QNX) + } + pub fn try_get_current() -> Result { + if let Some(brand_string) = CPUID.get_vendor_info() { + Self::try_from(brand_string.as_str()) + } else { + Err(()) + } + } +} + +pub fn virt_supported() -> VirtualizationProvider { + match ProcessorVendor::try_get_current() { + Err(_) => VirtualizationProvider::None, + Ok(ProcessorVendor::Intel) => { + if intel_vt_supported() { + VirtualizationProvider::IntelVTx + } else { + VirtualizationProvider::None + } + } + Ok(ProcessorVendor::AMD) => { + if amd_v_supported() { + VirtualizationProvider::AmdV + } else { + VirtualizationProvider::None + } + } + _ => VirtualizationProvider::None, + } +} diff --git a/src/kernel/arch/x86_64/intel_virt.rs b/src/kernel/arch/x86_64/intel_virt.rs new file mode 100644 index 0000000..8334c2c --- /dev/null +++ b/src/kernel/arch/x86_64/intel_virt.rs @@ -0,0 +1,17 @@ +// Copyright (c) 2025 shibedrill +// SPDX-License-Identifier: MIT + +use crate::arch::x86_64::cpuid::*; + +pub fn intel_vt_supported() -> bool { + match ProcessorVendor::try_get_current() { + Ok(vendor) => match vendor == ProcessorVendor::Intel { + true => match CPUID.get_feature_info() { + Some(features) => features.has_vmx(), + None => false, + }, + false => false, + }, + Err(_) => false, + } +} diff --git a/src/kernel/arch/x86_64/mod.rs b/src/kernel/arch/x86_64/mod.rs index 0270fd4..955dc4c 100644 --- a/src/kernel/arch/x86_64/mod.rs +++ b/src/kernel/arch/x86_64/mod.rs @@ -1,8 +1,11 @@ // Copyright (c) 2025 shibedrill // SPDX-License-Identifier: MIT +pub mod amd_virt; pub mod asm; +pub mod cpuid; pub mod gdt; +pub mod intel_virt; pub mod interrupts; pub mod paging; pub mod serial; diff --git a/src/kernel/main.rs b/src/kernel/main.rs index 54af811..e5b4838 100644 --- a/src/kernel/main.rs +++ b/src/kernel/main.rs @@ -34,6 +34,8 @@ use lazy_static::lazy_static; use limine::firmware_type::FirmwareType; use spin::mutex::Mutex; +use crate::arch::x86_64::{amd_virt, cpuid::{virt_supported, CPUID}}; + lazy_static! { pub static ref SERIAL_3F8: Mutex = Mutex::new(arch::x86_64::serial::SerialPort::new(0x3f8)); @@ -137,16 +139,19 @@ unsafe extern "C" fn main() -> ! { arch::x86_64::paging::get_mappings(); - let cpuid = raw_cpuid::CpuId::new(); - if let Some(string) = cpuid.get_processor_brand_string() { + if let Some(string) = CPUID.get_processor_brand_string() { log_info!("CPU brand string: {}", string.as_str()); } - if let Some(string) = cpuid.get_vendor_info() { + if let Some(string) = CPUID.get_vendor_info() { log_info!("CPU vendor: {}", string.as_str()); } - if let Some(string) = cpuid.get_hypervisor_info() { + if let Some(string) = CPUID.get_hypervisor_info() { log_info!("Hypervisor: {:?}", string.identify()); } + log_info!("Virtualization provider: {:?}", virt_supported()); + log_info!("AMD-V info: {}", CPUID.get_svm_info().unwrap().revision()); + amd_virt::enable_svm(); + loop { arch::asm::nop();