xref: /DragonOS/kernel/src/arch/x86_64/kvm/vmx/vcpu.rs (revision 83ed0ebc293d5a10245089f627f52770fd5b9dd4)
1 use super::vmcs::{
2     VMCSRegion, VmcsFields, VmxEntryCtrl, VmxPrimaryExitCtrl, VmxPrimaryProcessBasedExecuteCtrl,
3     VmxSecondaryProcessBasedExecuteCtrl,
4 };
5 use super::vmx_asm_wrapper::{vmx_vmclear, vmx_vmptrld, vmx_vmread, vmx_vmwrite, vmxoff, vmxon};
6 use crate::arch::kvm::vmx::mmu::KvmMmu;
7 use crate::arch::kvm::vmx::seg::{seg_setup, Sreg};
8 use crate::arch::kvm::vmx::{VcpuRegIndex, X86_CR0};
9 use crate::arch::mm::{LockedFrameAllocator, PageMapper};
10 use crate::arch::x86_64::mm::X86_64MMArch;
11 use crate::arch::MMArch;
12 use crate::kdebug;
13 use crate::mm::{phys_2_virt, VirtAddr};
14 use crate::mm::{MemoryManagementArch, PageTableKind};
15 use crate::syscall::SystemError;
16 use crate::virt::kvm::vcpu::Vcpu;
17 use crate::virt::kvm::vm::Vm;
18 use alloc::alloc::Global;
19 use alloc::boxed::Box;
20 use core::slice;
21 use raw_cpuid::CpuId;
22 use x86;
23 use x86::{controlregs, msr, segmentation};
24 // use crate::arch::kvm::vmx::seg::RMODE_TSS_SIZE;
25 // use crate::virt::kvm::{KVM};
26 
27 // KERNEL_ALLOCATOR
28 pub const PAGE_SIZE: usize = 0x1000;
29 pub const NR_VCPU_REGS: usize = 16;
30 
31 #[repr(C, align(4096))]
32 #[derive(Debug)]
33 pub struct VmxonRegion {
34     pub revision_id: u32,
35     pub data: [u8; PAGE_SIZE - 4],
36 }
37 
38 #[repr(C, align(4096))]
39 #[derive(Debug)]
40 pub struct MSRBitmap {
41     pub data: [u8; PAGE_SIZE],
42 }
43 
44 #[derive(Debug)]
45 pub struct VcpuData {
46     /// The virtual and physical address of the Vmxon naturally aligned 4-KByte region of memory
47     pub vmxon_region: Box<VmxonRegion>,
48     pub vmxon_region_physical_address: u64, // vmxon需要该地址
49     /// The virtual and physical address of the Vmcs naturally aligned 4-KByte region of memory
50     /// holds the complete CPU state of both the host and the guest.
51     /// includes the segment registers, GDT, IDT, TR, various MSR’s
52     /// and control field structures for handling exit and entry operations
53     pub vmcs_region: Box<VMCSRegion>,
54     pub vmcs_region_physical_address: u64, // vmptrld, vmclear需要该地址
55     pub msr_bitmap: Box<MSRBitmap>,
56     pub msr_bitmap_physical_address: u64,
57 }
58 
59 #[derive(Default, Debug)]
60 #[repr(C)]
61 pub struct VcpuContextFrame {
62     pub regs: [usize; NR_VCPU_REGS], // 通用寄存器
63     pub rip: usize,
64     pub rflags: usize,
65 }
66 
67 #[derive(Debug)]
68 #[allow(dead_code)]
69 pub enum VcpuState {
70     VcpuInv = 0,
71     VcpuPend = 1,
72     VcpuAct = 2,
73 }
74 
75 #[derive(Debug)]
76 pub struct VmxVcpu {
77     pub vcpu_id: u32,
78     pub vcpu_ctx: VcpuContextFrame, // 保存vcpu切换时的上下文,如通用寄存器等
79     pub vcpu_state: VcpuState,      // vcpu当前运行状态
80     pub mmu: KvmMmu,                // vcpu的内存管理单元
81     pub data: VcpuData,             // vcpu的数据
82     pub parent_vm: Vm,              // parent KVM
83 }
84 
85 impl VcpuData {
86     pub fn alloc() -> Result<Self, SystemError> {
87         let vmxon_region: Box<VmxonRegion> = unsafe {
88             Box::try_new_zeroed_in(Global)
89                 .expect("Try new zeroed fail!")
90                 .assume_init()
91         };
92         let vmcs_region: Box<VMCSRegion> = unsafe {
93             Box::try_new_zeroed_in(Global)
94                 .expect("Try new zeroed fail!")
95                 .assume_init()
96         };
97         let msr_bitmap: Box<MSRBitmap> = unsafe {
98             Box::try_new_zeroed_in(Global)
99                 .expect("Try new zeroed fail!")
100                 .assume_init()
101         };
102         // FIXME: virt_2_phys的转换正确性存疑
103         let vmxon_region_physical_address = {
104             let vaddr = VirtAddr::new(vmxon_region.as_ref() as *const _ as _);
105             unsafe { MMArch::virt_2_phys(vaddr).unwrap().data() as u64 }
106         };
107         let vmcs_region_physical_address = {
108             let vaddr = VirtAddr::new(vmcs_region.as_ref() as *const _ as _);
109             unsafe { MMArch::virt_2_phys(vaddr).unwrap().data() as u64 }
110         };
111         let msr_bitmap_physical_address = {
112             let vaddr = VirtAddr::new(msr_bitmap.as_ref() as *const _ as _);
113             unsafe { MMArch::virt_2_phys(vaddr).unwrap().data() as u64 }
114         };
115 
116         let mut instance = Self {
117             // Allocate a naturally aligned 4-KByte VMXON region of memory to enable VMX operation (Intel Manual: 25.11.5 VMXON Region)
118             vmxon_region,
119             vmxon_region_physical_address,
120             // Allocate a naturally aligned 4-KByte VMCS region of memory
121             vmcs_region,
122             vmcs_region_physical_address,
123             msr_bitmap,
124             msr_bitmap_physical_address,
125         };
126         // printk_color!(GREEN, BLACK, "[+] init_region\n");
127         instance.init_region()?;
128         Ok(instance)
129     }
130 
131     pub fn init_region(&mut self) -> Result<(), SystemError> {
132         // Get the Virtual Machine Control Structure revision identifier (VMCS revision ID)
133         // (Intel Manual: 25.11.5 VMXON Region)
134         let revision_id = unsafe { (msr::rdmsr(msr::IA32_VMX_BASIC) as u32) & 0x7FFF_FFFF };
135         kdebug!("[+] VMXON Region Virtual Address: {:p}", self.vmxon_region);
136         kdebug!(
137             "[+] VMXON Region Physical Addresss: 0x{:x}",
138             self.vmxon_region_physical_address
139         );
140         kdebug!("[+] VMCS Region Virtual Address: {:p}", self.vmcs_region);
141         kdebug!(
142             "[+] VMCS Region Physical Address1: 0x{:x}",
143             self.vmcs_region_physical_address
144         );
145         self.vmxon_region.revision_id = revision_id;
146         self.vmcs_region.revision_id = revision_id;
147         return Ok(());
148     }
149 }
150 
151 impl VmxVcpu {
152     pub fn new(vcpu_id: u32, parent_vm: Vm) -> Result<Self, SystemError> {
153         kdebug!("Creating processor {}", vcpu_id);
154         let instance = Self {
155             vcpu_id,
156             vcpu_ctx: VcpuContextFrame {
157                 regs: [0; NR_VCPU_REGS],
158                 rip: 0,
159                 rflags: 0,
160             },
161             vcpu_state: VcpuState::VcpuInv,
162             mmu: KvmMmu::default(),
163             data: VcpuData::alloc()?,
164             parent_vm,
165         };
166         Ok(instance)
167     }
168 
169     pub fn vmx_set_cr0(cr0: X86_CR0) -> Result<(), SystemError> {
170         let mut hw_cr0 = cr0 & !(X86_CR0::CR0_NW | X86_CR0::CR0_CD);
171         hw_cr0 |= X86_CR0::CR0_WP | X86_CR0::CR0_NE;
172 
173         vmx_vmwrite(VmcsFields::GUEST_CR0 as u32, cr0.bits() as u64)?;
174         Ok(())
175     }
176 
177     pub fn vmcs_init_guest(&self) -> Result<(), SystemError> {
178         // https://www.sandpile.org/x86/initial.htm
179         // segment field initialization
180         seg_setup(Sreg::CS as usize)?;
181         vmx_vmwrite(VmcsFields::GUEST_CS_SELECTOR as u32, 0xf000)?;
182         vmx_vmwrite(VmcsFields::GUEST_CS_BASE as u32, 0xffff0000)?;
183 
184         seg_setup(Sreg::DS as usize)?;
185         seg_setup(Sreg::ES as usize)?;
186         seg_setup(Sreg::FS as usize)?;
187         seg_setup(Sreg::GS as usize)?;
188         seg_setup(Sreg::SS as usize)?;
189 
190         vmx_vmwrite(VmcsFields::GUEST_TR_SELECTOR as u32, 0)?;
191         vmx_vmwrite(VmcsFields::GUEST_TR_BASE as u32, 0)?;
192         vmx_vmwrite(VmcsFields::GUEST_TR_LIMIT as u32, 0xffff)?;
193         vmx_vmwrite(VmcsFields::GUEST_TR_ACCESS_RIGHTS as u32, 0x008b)?;
194 
195         vmx_vmwrite(VmcsFields::GUEST_LDTR_SELECTOR as u32, 0)?;
196         vmx_vmwrite(VmcsFields::GUEST_LDTR_BASE as u32, 0)?;
197         vmx_vmwrite(VmcsFields::GUEST_LDTR_LIMIT as u32, 0xffff)?;
198         vmx_vmwrite(VmcsFields::GUEST_LDTR_ACCESS_RIGHTS as u32, 0x00082)?;
199 
200         vmx_vmwrite(VmcsFields::GUEST_RFLAGS as u32, 2)?;
201 
202         vmx_vmwrite(VmcsFields::GUEST_GDTR_BASE as u32, 0)?;
203         vmx_vmwrite(VmcsFields::GUEST_GDTR_LIMIT as u32, 0x0000_FFFF as u64)?;
204 
205         vmx_vmwrite(VmcsFields::GUEST_IDTR_BASE as u32, 0)?;
206         vmx_vmwrite(VmcsFields::GUEST_IDTR_LIMIT as u32, 0x0000_FFFF as u64)?;
207 
208         vmx_vmwrite(VmcsFields::GUEST_ACTIVITY_STATE as u32, 0)?; // State = Active
209         vmx_vmwrite(VmcsFields::GUEST_INTERRUPTIBILITY_STATE as u32, 0)?;
210         vmx_vmwrite(VmcsFields::GUEST_PENDING_DBG_EXCEPTIONS as u32, 0)?;
211 
212         vmx_vmwrite(VmcsFields::CTRL_VM_ENTRY_INTR_INFO_FIELD as u32, 0)?;
213 
214         let cr0 = X86_CR0::CR0_NW | X86_CR0::CR0_CD | X86_CR0::CR0_ET;
215         Self::vmx_set_cr0(cr0)?;
216 
217         vmx_vmwrite(VmcsFields::GUEST_CR0 as u32, cr0.bits() as u64)?;
218 
219         vmx_vmwrite(
220             VmcsFields::GUEST_SYSENTER_CS as u32,
221             vmx_vmread(VmcsFields::HOST_SYSENTER_CS as u32).unwrap(),
222         )?;
223         vmx_vmwrite(VmcsFields::GUEST_VMX_PREEMPT_TIMER_VALUE as u32, 0)?;
224 
225         vmx_vmwrite(VmcsFields::GUEST_INTR_STATUS as u32, 0)?;
226         vmx_vmwrite(VmcsFields::GUEST_PML_INDEX as u32, 0)?;
227 
228         vmx_vmwrite(VmcsFields::GUEST_VMCS_LINK_PTR as u32, u64::MAX)?;
229         vmx_vmwrite(VmcsFields::GUEST_DEBUGCTL as u32, unsafe {
230             msr::rdmsr(msr::IA32_DEBUGCTL)
231         })?;
232 
233         vmx_vmwrite(
234             VmcsFields::GUEST_SYSENTER_ESP as u32,
235             vmx_vmread(VmcsFields::HOST_SYSENTER_ESP as u32).unwrap(),
236         )?;
237         vmx_vmwrite(
238             VmcsFields::GUEST_SYSENTER_EIP as u32,
239             vmx_vmread(VmcsFields::HOST_SYSENTER_EIP as u32).unwrap(),
240         )?;
241 
242         // Self::vmx_set_cr0();
243         vmx_vmwrite(VmcsFields::GUEST_CR3 as u32, 0)?;
244         vmx_vmwrite(
245             VmcsFields::GUEST_CR4 as u32,
246             1, // enable vme
247         )?;
248         vmx_vmwrite(VmcsFields::GUEST_DR7 as u32, 0x0000_0000_0000_0400)?;
249         vmx_vmwrite(
250             VmcsFields::GUEST_RSP as u32,
251             self.vcpu_ctx.regs[VcpuRegIndex::Rsp as usize] as u64,
252         )?;
253         vmx_vmwrite(VmcsFields::GUEST_RIP as u32, self.vcpu_ctx.rip as u64)?;
254         kdebug!("vmcs init guest rip: {:#x}", self.vcpu_ctx.rip as u64);
255         kdebug!(
256             "vmcs init guest rsp: {:#x}",
257             self.vcpu_ctx.regs[VcpuRegIndex::Rsp as usize] as u64
258         );
259 
260         // vmx_vmwrite(VmcsFields::GUEST_RFLAGS as u32, x86::bits64::rflags::read().bits())?;
261         Ok(())
262     }
263 
264     #[allow(deprecated)]
265     pub fn vmcs_init_host(&self) -> Result<(), SystemError> {
266         vmx_vmwrite(VmcsFields::HOST_CR0 as u32, unsafe {
267             controlregs::cr0().bits().try_into().unwrap()
268         })?;
269         vmx_vmwrite(VmcsFields::HOST_CR3 as u32, unsafe { controlregs::cr3() })?;
270         vmx_vmwrite(VmcsFields::HOST_CR4 as u32, unsafe {
271             controlregs::cr4().bits().try_into().unwrap()
272         })?;
273         vmx_vmwrite(
274             VmcsFields::HOST_ES_SELECTOR as u32,
275             (segmentation::es().bits() & (!0x07)).into(),
276         )?;
277         vmx_vmwrite(
278             VmcsFields::HOST_CS_SELECTOR as u32,
279             (segmentation::cs().bits() & (!0x07)).into(),
280         )?;
281         vmx_vmwrite(
282             VmcsFields::HOST_SS_SELECTOR as u32,
283             (segmentation::ss().bits() & (!0x07)).into(),
284         )?;
285         vmx_vmwrite(
286             VmcsFields::HOST_DS_SELECTOR as u32,
287             (segmentation::ds().bits() & (!0x07)).into(),
288         )?;
289         vmx_vmwrite(
290             VmcsFields::HOST_FS_SELECTOR as u32,
291             (segmentation::fs().bits() & (!0x07)).into(),
292         )?;
293         vmx_vmwrite(
294             VmcsFields::HOST_GS_SELECTOR as u32,
295             (segmentation::gs().bits() & (!0x07)).into(),
296         )?;
297         vmx_vmwrite(VmcsFields::HOST_TR_SELECTOR as u32, unsafe {
298             (x86::task::tr().bits() & (!0x07)).into()
299         })?;
300         vmx_vmwrite(VmcsFields::HOST_FS_BASE as u32, unsafe {
301             msr::rdmsr(msr::IA32_FS_BASE)
302         })?;
303         vmx_vmwrite(VmcsFields::HOST_GS_BASE as u32, unsafe {
304             msr::rdmsr(msr::IA32_GS_BASE)
305         })?;
306 
307         let mut pseudo_descriptpr: x86::dtables::DescriptorTablePointer<u64> = Default::default();
308         unsafe {
309             x86::dtables::sgdt(&mut pseudo_descriptpr);
310         };
311 
312         vmx_vmwrite(
313             VmcsFields::HOST_TR_BASE as u32,
314             get_segment_base(pseudo_descriptpr.base, pseudo_descriptpr.limit, unsafe {
315                 x86::task::tr().bits().into()
316             }),
317         )?;
318         vmx_vmwrite(
319             VmcsFields::HOST_GDTR_BASE as u32,
320             pseudo_descriptpr.base.to_bits() as u64,
321         )?;
322         vmx_vmwrite(VmcsFields::HOST_IDTR_BASE as u32, unsafe {
323             let mut pseudo_descriptpr: x86::dtables::DescriptorTablePointer<u64> =
324                 Default::default();
325             x86::dtables::sidt(&mut pseudo_descriptpr);
326             pseudo_descriptpr.base.to_bits() as u64
327         })?;
328 
329         // fast entry into the kernel
330         vmx_vmwrite(VmcsFields::HOST_SYSENTER_ESP as u32, unsafe {
331             msr::rdmsr(msr::IA32_SYSENTER_ESP)
332         })?;
333         vmx_vmwrite(VmcsFields::HOST_SYSENTER_EIP as u32, unsafe {
334             msr::rdmsr(msr::IA32_SYSENTER_EIP)
335         })?;
336         vmx_vmwrite(VmcsFields::HOST_SYSENTER_CS as u32, unsafe {
337             msr::rdmsr(msr::IA32_SYSENTER_CS)
338         })?;
339 
340         // vmx_vmwrite(VmcsFields::HOST_RIP as u32, vmx_return as *const () as u64)?;
341         // kdebug!("vmcs init host rip: {:#x}", vmx_return as *const () as u64);
342 
343         Ok(())
344     }
345 
346     // Intel SDM Volume 3C Chapter 25.3 “Organization of VMCS Data”
347     pub fn vmcs_init(&self) -> Result<(), SystemError> {
348         vmx_vmwrite(VmcsFields::CTRL_PAGE_FAULT_ERR_CODE_MASK as u32, 0)?;
349         vmx_vmwrite(VmcsFields::CTRL_PAGE_FAULT_ERR_CODE_MATCH as u32, 0)?;
350         vmx_vmwrite(VmcsFields::CTRL_CR3_TARGET_COUNT as u32, 0)?;
351 
352         vmx_vmwrite(
353             VmcsFields::CTRL_PIN_BASED_VM_EXEC_CTRLS as u32,
354             adjust_vmx_pinbased_controls() as u64,
355         )?;
356 
357         vmx_vmwrite(
358             VmcsFields::CTRL_MSR_BITMAP_ADDR as u32,
359             self.data.msr_bitmap_physical_address,
360         )?;
361 
362         vmx_vmwrite(VmcsFields::CTRL_CR0_READ_SHADOW as u32, unsafe {
363             controlregs::cr0().bits().try_into().unwrap()
364         })?;
365         vmx_vmwrite(VmcsFields::CTRL_CR4_READ_SHADOW as u32, unsafe {
366             controlregs::cr4().bits().try_into().unwrap()
367         })?;
368         vmx_vmwrite(
369             VmcsFields::CTRL_VM_ENTRY_CTRLS as u32,
370             adjust_vmx_entry_controls() as u64,
371         )?;
372         vmx_vmwrite(
373             VmcsFields::CTRL_PRIMARY_VM_EXIT_CTRLS as u32,
374             adjust_vmx_exit_controls() as u64,
375         )?;
376         vmx_vmwrite(
377             VmcsFields::CTRL_PRIMARY_PROCESSOR_VM_EXEC_CTRLS as u32,
378             adjust_vmx_primary_process_exec_controls() as u64,
379         )?;
380         vmx_vmwrite(
381             VmcsFields::CTRL_SECONDARY_PROCESSOR_VM_EXEC_CTRLS as u32,
382             adjust_vmx_secondary_process_exec_controls() as u64,
383         )?;
384 
385         self.vmcs_init_host()?;
386         self.vmcs_init_guest()?;
387         Ok(())
388     }
389 
390     fn kvm_mmu_load(&mut self) -> Result<(), SystemError> {
391         kdebug!("kvm_mmu_load!");
392         // 申请并创建新的页表
393         let mapper: crate::mm::page::PageMapper<X86_64MMArch, LockedFrameAllocator> = unsafe {
394             PageMapper::create(PageTableKind::EPT, LockedFrameAllocator)
395                 .ok_or(SystemError::ENOMEM)?
396         };
397 
398         let ept_root_hpa = mapper.table().phys();
399         let set_eptp_fn = self.mmu.set_eptp.unwrap();
400         set_eptp_fn(ept_root_hpa.data() as u64)?;
401         self.mmu.root_hpa = ept_root_hpa.data() as u64;
402         kdebug!("ept_root_hpa:{:x}!", ept_root_hpa.data() as u64);
403 
404         return Ok(());
405     }
406 
407     pub fn set_regs(&mut self, regs: VcpuContextFrame) -> Result<(), SystemError> {
408         self.vcpu_ctx = regs;
409         Ok(())
410     }
411 }
412 
413 impl Vcpu for VmxVcpu {
414     /// Virtualize the CPU
415     fn virtualize_cpu(&mut self) -> Result<(), SystemError> {
416         match has_intel_vmx_support() {
417             Ok(_) => {
418                 kdebug!("[+] CPU supports Intel VMX");
419             }
420             Err(e) => {
421                 kdebug!("[-] CPU does not support Intel VMX: {:?}", e);
422                 return Err(SystemError::EOPNOTSUPP_OR_ENOTSUP);
423             }
424         };
425 
426         match enable_vmx_operation() {
427             Ok(_) => {
428                 kdebug!("[+] Enabling Virtual Machine Extensions (VMX)");
429             }
430             Err(_) => {
431                 kdebug!("[-] VMX operation is not supported on this processor.");
432                 return Err(SystemError::EOPNOTSUPP_OR_ENOTSUP);
433             }
434         }
435 
436         vmxon(self.data.vmxon_region_physical_address)?;
437         kdebug!("[+] VMXON successful!");
438         vmx_vmclear(self.data.vmcs_region_physical_address)?;
439         vmx_vmptrld(self.data.vmcs_region_physical_address)?;
440         kdebug!("[+] VMPTRLD successful!");
441         self.vmcs_init().expect("vncs_init fail");
442         kdebug!("[+] VMCS init!");
443         // kdebug!("vmcs init host rip: {:#x}", vmx_return as *const () as u64);
444         // kdebug!("vmcs init host rsp: {:#x}", x86::bits64::registers::rsp());
445         // vmx_vmwrite(VmcsFields::HOST_RSP as u32, x86::bits64::registers::rsp())?;
446         // vmx_vmwrite(VmcsFields::HOST_RIP as u32, vmx_return as *const () as u64)?;
447         // vmx_vmwrite(VmcsFields::HOST_RSP as u32,  x86::bits64::registers::rsp())?;
448         self.kvm_mmu_load()?;
449         Ok(())
450     }
451 
452     fn devirtualize_cpu(&self) -> Result<(), SystemError> {
453         vmxoff()?;
454         Ok(())
455     }
456 
457     /// Gets the index of the current logical/virtual processor
458     fn id(&self) -> u32 {
459         self.vcpu_id
460     }
461 }
462 
463 pub fn get_segment_base(gdt_base: *const u64, gdt_size: u16, segment_selector: u16) -> u64 {
464     let table = segment_selector & 0x0004; // get table indicator in selector
465     let index = (segment_selector >> 3) as usize; // get index in selector
466     if table == 0 && index == 0 {
467         return 0;
468     }
469     let descriptor_table = unsafe { slice::from_raw_parts(gdt_base, gdt_size.into()) };
470     let descriptor = descriptor_table[index];
471 
472     let base_high = (descriptor & 0xFF00_0000_0000_0000) >> 32;
473     let base_mid = (descriptor & 0x0000_00FF_0000_0000) >> 16;
474     let base_low = (descriptor & 0x0000_0000_FFFF_0000) >> 16;
475     let segment_base = (base_high | base_mid | base_low) & 0xFFFFFFFF;
476     let virtaddr = phys_2_virt(segment_base.try_into().unwrap())
477         .try_into()
478         .unwrap();
479     kdebug!(
480         "segment_base={:x}",
481         phys_2_virt(segment_base.try_into().unwrap())
482     );
483     return virtaddr;
484 }
485 
486 // FIXME: may have bug
487 // pub fn read_segment_access_rights(segement_selector: u16) -> u32{
488 //     let table = segement_selector & 0x0004; // get table indicator in selector
489 //     let index = segement_selector & 0xFFF8; // get index in selector
490 //     let mut flag: u16;
491 //     if table==0 && index==0 {
492 //         return 0;
493 //     }
494 //     unsafe{
495 //         asm!(
496 //             "lar {0:r}, rcx",
497 //             "mov {1:r}, {0:r}",
498 //             in(reg) segement_selector,
499 //             out(reg) flag,
500 //         );
501 //     }
502 //     return (flag >> 8) as u32;
503 // }
504 pub fn adjust_vmx_controls(ctl_min: u32, ctl_opt: u32, msr: u32, result: &mut u32) {
505     let vmx_msr_low: u32 = unsafe { (msr::rdmsr(msr) & 0x0000_0000_FFFF_FFFF) as u32 };
506     let vmx_msr_high: u32 = unsafe { (msr::rdmsr(msr) << 32) as u32 };
507     let mut ctl: u32 = ctl_min | ctl_opt;
508     ctl &= vmx_msr_high; /* bit == 0 in high word ==> must be zero */
509     ctl |= vmx_msr_low; /* bit == 1 in low word  ==> must be one  */
510     *result = ctl;
511 }
512 
513 pub fn adjust_vmx_entry_controls() -> u32 {
514     let mut entry_controls: u32 = 0;
515     adjust_vmx_controls(
516         VmxEntryCtrl::LOAD_DBG_CTRLS.bits(),
517         VmxEntryCtrl::IA32E_MODE_GUEST.bits(),
518         msr::IA32_VMX_ENTRY_CTLS, //Capability Reporting Register of VM-entry Controls (R/O)
519         &mut entry_controls,
520     );
521     return entry_controls;
522     // msr::IA32_VMX_TRUE_ENTRY_CTLS//Capability Reporting Register of VM-entry Flex Controls (R/O) See Table 35-2
523 }
524 
525 pub fn adjust_vmx_exit_controls() -> u32 {
526     let mut exit_controls: u32 = 0;
527     adjust_vmx_controls(
528         VmxPrimaryExitCtrl::SAVE_DBG_CTRLS.bits(),
529         VmxPrimaryExitCtrl::HOST_ADDR_SPACE_SIZE.bits(),
530         msr::IA32_VMX_EXIT_CTLS,
531         &mut exit_controls,
532     );
533     return exit_controls;
534 }
535 
536 pub fn adjust_vmx_pinbased_controls() -> u32 {
537     let mut controls: u32 = 0000_0016;
538     adjust_vmx_controls(0, 0, msr::IA32_VMX_TRUE_PINBASED_CTLS, &mut controls);
539     // kdebug!("adjust_vmx_pinbased_controls: {:x}", controls);
540     return controls;
541 }
542 
543 pub fn adjust_vmx_primary_process_exec_controls() -> u32 {
544     let mut controls: u32 = 0;
545     adjust_vmx_controls(
546         0,
547         VmxPrimaryProcessBasedExecuteCtrl::USE_MSR_BITMAPS.bits()
548             | VmxPrimaryProcessBasedExecuteCtrl::ACTIVATE_SECONDARY_CONTROLS.bits(),
549         msr::IA32_VMX_PROCBASED_CTLS,
550         &mut controls,
551     );
552     return controls;
553 }
554 
555 pub fn adjust_vmx_secondary_process_exec_controls() -> u32 {
556     let mut controls: u32 = 0;
557     adjust_vmx_controls(
558         0,
559         VmxSecondaryProcessBasedExecuteCtrl::ENABLE_RDTSCP.bits()
560             | VmxSecondaryProcessBasedExecuteCtrl::ENABLE_XSAVES_XRSTORS.bits()
561             | VmxSecondaryProcessBasedExecuteCtrl::ENABLE_INVPCID.bits()
562             | VmxSecondaryProcessBasedExecuteCtrl::ENABLE_EPT.bits()
563             | VmxSecondaryProcessBasedExecuteCtrl::UNRESTRICTED_GUEST.bits(),
564         msr::IA32_VMX_PROCBASED_CTLS2,
565         &mut controls,
566     );
567     return controls;
568 }
569 
570 /// Check to see if CPU is Intel (“GenuineIntel”).
571 /// Check processor supports for Virtual Machine Extension (VMX) technology
572 //  CPUID.1:ECX.VMX[bit 5] = 1 (Intel Manual: 24.6 Discovering Support for VMX)
573 pub fn has_intel_vmx_support() -> Result<(), SystemError> {
574     let cpuid = CpuId::new();
575     if let Some(vi) = cpuid.get_vendor_info() {
576         if vi.as_str() != "GenuineIntel" {
577             return Err(SystemError::EOPNOTSUPP_OR_ENOTSUP);
578         }
579     }
580     if let Some(fi) = cpuid.get_feature_info() {
581         if !fi.has_vmx() {
582             return Err(SystemError::EOPNOTSUPP_OR_ENOTSUP);
583         }
584     }
585     Ok(())
586 }
587 
588 /// Enables Virtual Machine Extensions
589 // - CR4.VMXE[bit 13] = 1 (Intel Manual: 24.7 Enabling and Entering VMX Operation)
590 pub fn enable_vmx_operation() -> Result<(), SystemError> {
591     let mut cr4 = unsafe { controlregs::cr4() };
592     cr4.set(controlregs::Cr4::CR4_ENABLE_VMX, true);
593     unsafe { controlregs::cr4_write(cr4) };
594 
595     set_lock_bit()?;
596     kdebug!("[+] Lock bit set via IA32_FEATURE_CONTROL");
597     set_cr0_bits();
598     kdebug!("[+] Mandatory bits in CR0 set/cleared");
599     set_cr4_bits();
600     kdebug!("[+] Mandatory bits in CR4 set/cleared");
601 
602     Ok(())
603 }
604 
605 /// Check if we need to set bits in IA32_FEATURE_CONTROL
606 // (Intel Manual: 24.7 Enabling and Entering VMX Operation)
607 fn set_lock_bit() -> Result<(), SystemError> {
608     const VMX_LOCK_BIT: u64 = 1 << 0;
609     const VMXON_OUTSIDE_SMX: u64 = 1 << 2;
610 
611     let ia32_feature_control = unsafe { msr::rdmsr(msr::IA32_FEATURE_CONTROL) };
612 
613     if (ia32_feature_control & VMX_LOCK_BIT) == 0 {
614         unsafe {
615             msr::wrmsr(
616                 msr::IA32_FEATURE_CONTROL,
617                 VMXON_OUTSIDE_SMX | VMX_LOCK_BIT | ia32_feature_control,
618             )
619         };
620     } else if (ia32_feature_control & VMXON_OUTSIDE_SMX) == 0 {
621         return Err(SystemError::EPERM);
622     }
623 
624     Ok(())
625 }
626 
627 /// Set the mandatory bits in CR0 and clear bits that are mandatory zero
628 /// (Intel Manual: 24.8 Restrictions on VMX Operation)
629 fn set_cr0_bits() {
630     let ia32_vmx_cr0_fixed0 = unsafe { msr::rdmsr(msr::IA32_VMX_CR0_FIXED0) };
631     let ia32_vmx_cr0_fixed1 = unsafe { msr::rdmsr(msr::IA32_VMX_CR0_FIXED1) };
632 
633     let mut cr0 = unsafe { controlregs::cr0() };
634 
635     cr0 |= controlregs::Cr0::from_bits_truncate(ia32_vmx_cr0_fixed0 as usize);
636     cr0 &= controlregs::Cr0::from_bits_truncate(ia32_vmx_cr0_fixed1 as usize);
637 
638     unsafe { controlregs::cr0_write(cr0) };
639 }
640 
641 /// Set the mandatory bits in CR4 and clear bits that are mandatory zero
642 /// (Intel Manual: 24.8 Restrictions on VMX Operation)
643 fn set_cr4_bits() {
644     let ia32_vmx_cr4_fixed0 = unsafe { msr::rdmsr(msr::IA32_VMX_CR4_FIXED0) };
645     let ia32_vmx_cr4_fixed1 = unsafe { msr::rdmsr(msr::IA32_VMX_CR4_FIXED1) };
646 
647     let mut cr4 = unsafe { controlregs::cr4() };
648 
649     cr4 |= controlregs::Cr4::from_bits_truncate(ia32_vmx_cr4_fixed0 as usize);
650     cr4 &= controlregs::Cr4::from_bits_truncate(ia32_vmx_cr4_fixed1 as usize);
651 
652     unsafe { controlregs::cr4_write(cr4) };
653 }
654