exit.rs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426
  1. use bitfield_struct::bitfield;
  2. use system_error::SystemError;
  3. use x86::vmx::vmcs::{guest, ro};
  4. use crate::{
  5. arch::vm::asm::{IntrInfo, VmxAsm},
  6. virt::vm::kvm_host::{vcpu::VirtCpu, Vm},
  7. };
  8. use super::{ept::EptViolationExitQual, vmx_info, PageFaultErr};
  9. extern crate num_traits;
  10. #[bitfield(u32)]
  11. pub struct VmxExitReason {
  12. pub basic: u16,
  13. pub reserved16: bool,
  14. pub reserved17: bool,
  15. pub reserved18: bool,
  16. pub reserved19: bool,
  17. pub reserved20: bool,
  18. pub reserved21: bool,
  19. pub reserved22: bool,
  20. pub reserved23: bool,
  21. pub reserved24: bool,
  22. pub reserved25: bool,
  23. pub bus_lock_detected: bool,
  24. pub enclave_mode: bool,
  25. pub smi_pending_mtf: bool,
  26. pub smi_from_vmx_root: bool,
  27. pub reserved30: bool,
  28. pub failed_vmentry: bool,
  29. }
  30. //#define VMX_EXIT_REASONS
  31. #[derive(FromPrimitive, PartialEq, Clone, Copy)]
  32. #[allow(non_camel_case_types)]
  33. pub enum VmxExitReasonBasic {
  34. EXCEPTION_OR_NMI = 0,
  35. EXTERNAL_INTERRUPT = 1,
  36. TRIPLE_FAULT = 2,
  37. INIT_SIGNAL = 3,
  38. SIPI = 4,
  39. IO_SMI = 5,
  40. OTHER_SMI = 6,
  41. INTERRUPT_WINDOW = 7,
  42. NMI_WINDOW = 8,
  43. TASK_SWITCH = 9,
  44. CPUID = 10,
  45. GETSEC = 11,
  46. HLT = 12,
  47. INVD = 13,
  48. INVLPG = 14,
  49. RDPMC = 15,
  50. RDTSC = 16,
  51. RSM = 17,
  52. VMCALL = 18,
  53. VMCLEAR = 19,
  54. VMLAUNCH = 20,
  55. VMPTRLD = 21,
  56. VMPTRST = 22,
  57. VMREAD = 23,
  58. VMRESUME = 24,
  59. VMWRITE = 25,
  60. VMXOFF = 26,
  61. VMXON = 27,
  62. CR_ACCESS = 28,
  63. DR_ACCESS = 29,
  64. IO_INSTRUCTION = 30,
  65. RDMSR = 31,
  66. WRMSR = 32,
  67. VM_ENTRY_FAILURE_INVALID_GUEST_STATE = 33,
  68. VM_ENTRY_FAILURE_MSR_LOADING = 34,
  69. MWAIT = 36,
  70. MONITOR_TRAP_FLAG = 37,
  71. MONITOR = 39,
  72. PAUSE = 40,
  73. VM_ENTRY_FAILURE_MACHINE_CHECK_EVENT = 41,
  74. TPR_BELOW_THRESHOLD = 43,
  75. APIC_ACCESS = 44,
  76. VIRTUALIZED_EOI = 45, // "EOI_INDUCED"
  77. ACCESS_GDTR_OR_IDTR = 46,
  78. ACCESS_LDTR_OR_TR = 47,
  79. EPT_VIOLATION = 48,
  80. EPT_MISCONFIG = 49,
  81. INVEPT = 50,
  82. RDTSCP = 51,
  83. VMX_PREEMPTION_TIMER_EXPIRED = 52,
  84. INVVPID = 53,
  85. WBINVD = 54,
  86. XSETBV = 55,
  87. APIC_WRITE = 56,
  88. RDRAND = 57,
  89. INVPCID = 58,
  90. VMFUNC = 59,
  91. ENCLS = 60,
  92. RDSEED = 61,
  93. PML_FULL = 62,
  94. XSAVES = 63,
  95. XRSTORS = 64,
  96. UMWAIT = 67,
  97. TPAUSE = 68,
  98. BUS_LOCK = 74,
  99. NOTIFY = 75,
  100. UNKNOWN,
  101. }
  102. impl From<u16> for VmxExitReasonBasic {
  103. fn from(num: u16) -> Self {
  104. match num {
  105. 0 => VmxExitReasonBasic::EXCEPTION_OR_NMI,
  106. 1 => VmxExitReasonBasic::EXTERNAL_INTERRUPT,
  107. 2 => VmxExitReasonBasic::TRIPLE_FAULT,
  108. 3 => VmxExitReasonBasic::INIT_SIGNAL,
  109. 4 => VmxExitReasonBasic::SIPI,
  110. 5 => VmxExitReasonBasic::IO_SMI,
  111. 6 => VmxExitReasonBasic::OTHER_SMI,
  112. 7 => VmxExitReasonBasic::INTERRUPT_WINDOW,
  113. 8 => VmxExitReasonBasic::NMI_WINDOW,
  114. 9 => VmxExitReasonBasic::TASK_SWITCH,
  115. 10 => VmxExitReasonBasic::CPUID,
  116. 11 => VmxExitReasonBasic::GETSEC,
  117. 12 => VmxExitReasonBasic::HLT,
  118. 13 => VmxExitReasonBasic::INVD,
  119. 14 => VmxExitReasonBasic::INVLPG,
  120. 15 => VmxExitReasonBasic::RDPMC,
  121. 16 => VmxExitReasonBasic::RDTSC,
  122. 17 => VmxExitReasonBasic::RSM,
  123. 18 => VmxExitReasonBasic::VMCALL,
  124. 19 => VmxExitReasonBasic::VMCLEAR,
  125. 20 => VmxExitReasonBasic::VMLAUNCH,
  126. 21 => VmxExitReasonBasic::VMPTRLD,
  127. 22 => VmxExitReasonBasic::VMPTRST,
  128. 23 => VmxExitReasonBasic::VMREAD,
  129. 24 => VmxExitReasonBasic::VMRESUME,
  130. 25 => VmxExitReasonBasic::VMWRITE,
  131. 26 => VmxExitReasonBasic::VMXOFF,
  132. 27 => VmxExitReasonBasic::VMXON,
  133. 28 => VmxExitReasonBasic::CR_ACCESS,
  134. 29 => VmxExitReasonBasic::DR_ACCESS,
  135. 30 => VmxExitReasonBasic::IO_INSTRUCTION,
  136. 31 => VmxExitReasonBasic::RDMSR,
  137. 32 => VmxExitReasonBasic::WRMSR,
  138. 33 => VmxExitReasonBasic::VM_ENTRY_FAILURE_INVALID_GUEST_STATE,
  139. 34 => VmxExitReasonBasic::VM_ENTRY_FAILURE_MSR_LOADING,
  140. 36 => VmxExitReasonBasic::MWAIT,
  141. 37 => VmxExitReasonBasic::MONITOR_TRAP_FLAG,
  142. 39 => VmxExitReasonBasic::MONITOR,
  143. 40 => VmxExitReasonBasic::PAUSE,
  144. 41 => VmxExitReasonBasic::VM_ENTRY_FAILURE_MACHINE_CHECK_EVENT,
  145. 43 => VmxExitReasonBasic::TPR_BELOW_THRESHOLD,
  146. 44 => VmxExitReasonBasic::APIC_ACCESS,
  147. 45 => VmxExitReasonBasic::VIRTUALIZED_EOI,
  148. 46 => VmxExitReasonBasic::ACCESS_GDTR_OR_IDTR,
  149. 47 => VmxExitReasonBasic::ACCESS_LDTR_OR_TR,
  150. 48 => VmxExitReasonBasic::EPT_VIOLATION,
  151. 49 => VmxExitReasonBasic::EPT_MISCONFIG,
  152. 50 => VmxExitReasonBasic::INVEPT,
  153. 51 => VmxExitReasonBasic::RDTSCP,
  154. 52 => VmxExitReasonBasic::VMX_PREEMPTION_TIMER_EXPIRED,
  155. 53 => VmxExitReasonBasic::INVVPID,
  156. 54 => VmxExitReasonBasic::WBINVD,
  157. 55 => VmxExitReasonBasic::XSETBV,
  158. 56 => VmxExitReasonBasic::APIC_WRITE,
  159. 57 => VmxExitReasonBasic::RDRAND,
  160. 58 => VmxExitReasonBasic::INVPCID,
  161. 59 => VmxExitReasonBasic::VMFUNC,
  162. 60 => VmxExitReasonBasic::ENCLS,
  163. 61 => VmxExitReasonBasic::RDSEED,
  164. 62 => VmxExitReasonBasic::PML_FULL,
  165. 63 => VmxExitReasonBasic::XSAVES,
  166. 64 => VmxExitReasonBasic::XRSTORS,
  167. 67 => VmxExitReasonBasic::UMWAIT,
  168. 68 => VmxExitReasonBasic::TPAUSE,
  169. 74 => VmxExitReasonBasic::BUS_LOCK,
  170. 75 => VmxExitReasonBasic::NOTIFY,
  171. _ => VmxExitReasonBasic::UNKNOWN,
  172. }
  173. }
  174. }
  175. #[derive(Debug, PartialEq)]
  176. #[allow(dead_code)]
  177. pub enum ExitFastpathCompletion {
  178. None,
  179. ReenterGuest,
  180. ExitHandled,
  181. }
  182. pub struct VmxExitHandlers {}
  183. // //name 代表暂时不懂含义的(name linux=name DragonOS)
  184. // ExceptionNmi = VmxExitReasonBasic::EXCEPTION_OR_NMI as isize,
  185. // ExternalInterrupt = VmxExitReasonBasic::EXTERNAL_INTERRUPT as isize,
  186. // TripleFault = VmxExitReasonBasic::TRIPLE_FAULT as isize,
  187. // NmiWindow = VmxExitReasonBasic::NMI_WINDOW as isize,
  188. // IoInstruction = VmxExitReasonBasic::IO_INSTRUCTION as isize,
  189. // CrAccess = VmxExitReasonBasic::CR_ACCESS as isize,
  190. // DrAccess = VmxExitReasonBasic::DR_ACCESS as isize,
  191. // Cpuid = VmxExitReasonBasic::CPUID as isize,
  192. // MsrRead = VmxExitReasonBasic::RDMSR as isize,
  193. // MsrWrite = VmxExitReasonBasic::WRMSR as isize,
  194. // InterruptWindow = VmxExitReasonBasic::INTERRUPT_WINDOW as isize,
  195. // Hlt = VmxExitReasonBasic::HLT as isize,
  196. // Invd = VmxExitReasonBasic::INVD as isize,
  197. // Invlpg = VmxExitReasonBasic::INVLPG as isize,
  198. // Rdpmc = VmxExitReasonBasic::RDPMC as isize,
  199. // Vmcall = VmxExitReasonBasic::VMCALL as isize,
  200. // Vmclear = VmxExitReasonBasic::VMCLEAR as isize,
  201. // Vmlaunch = VmxExitReasonBasic::VMLAUNCH as isize,
  202. // Vmptrld = VmxExitReasonBasic::VMPTRLD as isize,
  203. // Vmptrst = VmxExitReasonBasic::VMPTRST as isize,
  204. // Vmread = VmxExitReasonBasic::VMREAD as isize,
  205. // Vmresume = VmxExitReasonBasic::VMRESUME as isize,
  206. // Vmwrite = VmxExitReasonBasic::VMWRITE as isize,
  207. // Vmoff = VmxExitReasonBasic::VMXOFF as isize,
  208. // Vmon = VmxExitReasonBasic::VMXON as isize,
  209. // TprBelowThreshold = VmxExitReasonBasic::TPR_BELOW_THRESHOLD as isize,
  210. // ApicAccess = VmxExitReasonBasic::APIC_ACCESS as isize,
  211. // ApicWrite = VmxExitReasonBasic::APIC_WRITE as isize,
  212. // EoiInduced = VmxExitReasonBasic::VIRTUALIZED_EOI as isize, //name
  213. // Wbinvd = VmxExitReasonBasic::WBINVD as isize,
  214. // Xsetbv = VmxExitReasonBasic::XSETBV as isize,
  215. // TaskSwitch = VmxExitReasonBasic::TASK_SWITCH as isize,
  216. // MceDuringVmentry = VmxExitReasonBasic::VM_ENTRY_FAILURE_MACHINE_CHECK_EVENT as isize, //name
  217. // GdtrIdtr = VmxExitReasonBasic::ACCESS_GDTR_OR_IDTR as isize,
  218. // LdtrTr = VmxExitReasonBasic::ACCESS_LDTR_OR_TR as isize,
  219. // EptViolation = VmxExitReasonBasic::EPT_VIOLATION as isize,
  220. // EptMisconfig = VmxExitReasonBasic::EPT_MISCONFIG as isize,
  221. // PauseInstruction = VmxExitReasonBasic::PAUSE as isize,
  222. // MwaitInstruction = VmxExitReasonBasic::MWAIT as isize,
  223. // MonitorTrapFlag = VmxExitReasonBasic::MONITOR_TRAP_FLAG as isize,
  224. // MonitorInstruction = VmxExitReasonBasic::MONITOR as isize,
  225. // Invept = VmxExitReasonBasic::INVEPT as isize,
  226. // Invvpid = VmxExitReasonBasic::INVVPID as isize,
  227. // Rdrand = VmxExitReasonBasic::RDRAND as isize,
  228. // Rdseed = VmxExitReasonBasic::RDSEED as isize,
  229. // PmlFull = VmxExitReasonBasic::PML_FULL as isize,
  230. // Invpcid = VmxExitReasonBasic::INVPCID as isize,
  231. // Vmfunc = VmxExitReasonBasic::VMFUNC as isize,
  232. // PreemptionTimer = VmxExitReasonBasic::VMX_PREEMPTION_TIMER_EXPIRED as isize,
  233. // Encls = VmxExitReasonBasic::ENCLS as isize,
  234. // BusLock = VmxExitReasonBasic::BUS_LOCK as isize,
  235. // Notify = VmxExitReasonBasic::NOTIFY as isize,
  236. // Unknown,
  237. impl VmxExitHandlers {
  238. #[inline(never)]
  239. pub fn try_handle_exit(
  240. vcpu: &mut VirtCpu,
  241. vm: &Vm,
  242. basic: VmxExitReasonBasic,
  243. ) -> Option<Result<i32, SystemError>> {
  244. // let exit_reason = vmx_vmread(VmcsFields::VMEXIT_EXIT_REASON as u32).unwrap() as u32;
  245. // let exit_basic_reason = exit_reason & 0x0000_ffff;
  246. // let guest_rip = vmx_vmread(VmcsFields::GUEST_RIP as u32).unwrap();
  247. // let _guest_rflags = vmx_vmread(VmcsFields::GUEST_RFLAGS as u32).unwrap();
  248. match basic {
  249. VmxExitReasonBasic::IO_INSTRUCTION => {
  250. return Some(Self::handle_io(vcpu));
  251. }
  252. VmxExitReasonBasic::EPT_VIOLATION => {
  253. let r = Some(Self::handle_ept_violation(vcpu, vm));
  254. debug();
  255. r
  256. }
  257. VmxExitReasonBasic::EXTERNAL_INTERRUPT => {
  258. return Some(Self::handle_external_interrupt(vcpu));
  259. }
  260. VmxExitReasonBasic::EXCEPTION_OR_NMI => {
  261. todo!()
  262. }
  263. _ => None,
  264. }
  265. }
  266. fn handle_io(_vcpu: &mut VirtCpu) -> Result<i32, SystemError> {
  267. todo!();
  268. }
  269. fn handle_external_interrupt(vcpu: &mut VirtCpu) -> Result<i32, SystemError> {
  270. vcpu.stat.irq_exits += 1;
  271. Ok(1)
  272. }
  273. fn handle_ept_violation(vcpu: &mut VirtCpu, vm: &Vm) -> Result<i32, SystemError> {
  274. let exit_qualification = vcpu.get_exit_qual(); //0x184
  275. // EPT 违规发生在从 NMI 执行 iret 时,
  276. // 在下一次 VM 进入之前必须设置 "blocked by NMI" 位。
  277. // 有一些错误可能会导致该位未被设置:
  278. // AAK134, BY25。
  279. let vmx = vcpu.vmx();
  280. if vmx.idt_vectoring_info.bits() & IntrInfo::INTR_INFO_VALID_MASK.bits() != 0
  281. && vmx_info().enable_vnmi
  282. && exit_qualification & IntrInfo::INTR_INFO_UNBLOCK_NMI.bits() as u64 != 0
  283. {
  284. VmxAsm::vmx_vmwrite(guest::INTERRUPTIBILITY_STATE, 0x8); //GUEST_INTR_STATE_NMI
  285. }
  286. let gpa = VmxAsm::vmx_vmread(ro::GUEST_PHYSICAL_ADDR_FULL);
  287. //let exit_qualification = VmxAsm::vmx_vmread(ro::EXIT_QUALIFICATION);
  288. // trace_kvm_page_fault(vcpu, gpa, exit_qualification);//
  289. // 根据故障类型确定错误代码
  290. let mut error_code = if exit_qualification & (EptViolationExitQual::ACC_READ.bits()) != 0 {
  291. //debug!("error_code::ACC_READ");
  292. PageFaultErr::PFERR_USER.bits()
  293. } else {
  294. 0
  295. };
  296. error_code |= if exit_qualification & (EptViolationExitQual::ACC_WRITE.bits()) != 0 {
  297. //debug!("error_code::ACC_WRITE");
  298. PageFaultErr::PFERR_WRITE.bits()
  299. } else {
  300. 0
  301. };
  302. error_code |= if exit_qualification & (EptViolationExitQual::ACC_INSTR.bits()) != 0 {
  303. //actice
  304. //debug!("error_code::ACC_INSTR");
  305. PageFaultErr::PFERR_FETCH.bits()
  306. } else {
  307. 0
  308. };
  309. error_code |= if exit_qualification & (EptViolationExitQual::RWX_MASK.bits()) != 0 {
  310. //debug!("error_code::RWX_MASK");
  311. PageFaultErr::PFERR_PRESENT.bits()
  312. } else {
  313. 0
  314. };
  315. if exit_qualification & (EptViolationExitQual::GVA_IS_VALID.bits()) != 0 {
  316. //调试用
  317. //debug!("GVA is valid");
  318. } else {
  319. //debug!("GVA is invalid");
  320. }
  321. error_code |= if exit_qualification & (EptViolationExitQual::GVA_TRANSLATED.bits()) != 0 {
  322. //debug!("error_code:GVA GVA_TRANSLATED");
  323. PageFaultErr::PFERR_GUEST_FINAL.bits() //active
  324. } else {
  325. PageFaultErr::PFERR_GUEST_PAGE.bits()
  326. };
  327. //fixme:: 此时error_code为0x100000011
  328. vcpu.arch.exit_qual = exit_qualification;
  329. // 检查 GPA 是否超出物理内存限制,因为这是一个客户机页面错误。
  330. // 我们必须在这里模拟指令,因为如果非法地址是分页结构的地址,
  331. // 则会设置 EPT_VIOLATION_ACC_WRITE 位。
  332. // 或者,如果支持,我们还可以使用 EPT 违规的高级 VM 退出信息来重建页面错误代码。
  333. // if allow_smaller_maxphyaddr && kvm_vcpu_is_illegal_gpa(vcpu, gpa) {
  334. // return kvm_emulate_instruction(vcpu, 0);
  335. // }
  336. //debug!("EPT violation: error_code={:#x}", error_code);
  337. vcpu.page_fault(vm, gpa, error_code, None, 0)
  338. }
  339. }
  340. fn debug() {
  341. // // 3
  342. // let info = VmxAsm::vmx_vmread(VmcsFields::VMEXIT_INSTR_LEN as u32);
  343. // debug!("vmexit handler: VMEXIT_INSTR_LEN: 0x{:x}!", info);
  344. // //0
  345. // let info = VmxAsm::vmx_vmread(VmcsFields::VMEXIT_INSTR_INFO as u32);
  346. // debug!("vmexit handler: VMEXIT_INSTR_INFO: 0x{:x}!", info);
  347. // //0x64042
  348. // /*0x64042:
  349. // 将其转换为二进制:0x64042 的二进制表示是 110010000001000010。
  350. // 每个位代表一个异常向量(例如,除以零,调试,不可屏蔽中断,断点等)。
  351. // 从 vmx_update_exception_bitmap 函数中,我们看到设置的特定异常:
  352. // PF_VECTOR:页面错误
  353. // UD_VECTOR:未定义操作码
  354. // MC_VECTOR:机器检查
  355. // DB_VECTOR:调试
  356. // AC_VECTOR:对齐检查
  357. // 值 0x64042 设置了与这些异常相对应的位,这意味着当这些异常在来宾中发生时将导致 VM 退出。 */
  358. // let info = VmxAsm::vmx_vmread(control::EXCEPTION_BITMAP);
  359. // debug!("vmexit handler: EXCEPTION_BITMAP: 0x{:x}!", info);
  360. // //9
  361. // let info = VmxAsm::vmx_vmread(control::PAGE_FAULT_ERR_CODE_MASK);
  362. // debug!("vmexit handler: PAGE_FAULT_ERR_CODE_MASK: 0x{:x}!", info);
  363. // //1
  364. // let info = VmxAsm::vmx_vmread(control::PAGE_FAULT_ERR_CODE_MATCH);
  365. // debug!("vmexit handler: PAGE_FAULT_ERR_CODE_MATCH: 0x{:x}!", info);
  366. // //0
  367. // let info = VmxAsm::vmx_vmread(control::EPTP_LIST_ADDR_FULL);
  368. // debug!("vmexit handler: EPTP_LIST_ADDR_FULL: 0x{:x}!", info);
  369. // let info = VmxAsm::vmx_vmread(ro::VM_INSTRUCTION_ERROR);
  370. // debug!("vmexit handler: VM_INSTRUCTION_ERROR: 0x{:x}!", info);
  371. // let info = VmxAsm::vmx_vmread(ro::EXIT_REASON);
  372. // debug!("vmexit handler: EXIT_REASON:0x{:x}!", info);//EPT VIOLATION
  373. // let info = VmxAsm::vmx_vmread(ro::VMEXIT_INTERRUPTION_INFO);
  374. // debug!("vmexit handler: VMEXIT_INTERRUPTION_INFO: 0x{:x}!", info);
  375. // let info = VmxAsm::vmx_vmread(ro::VMEXIT_INTERRUPTION_ERR_CODE);
  376. // debug!("vmexit handler: VMEXIT_INTERRUPTION_ERR_CODE: 0x{:x}!", info);
  377. // let info = VmxAsm::vmx_vmread(ro::IDT_VECTORING_INFO);
  378. // debug!("vmexit handler: IDT_VECTORING_INFO: 0x{:x}!", info);
  379. // let info = VmxAsm::vmx_vmread(ro::IDT_VECTORING_ERR_CODE);
  380. // debug!("vmexit handler: IDT_VECTORING_ERR_CODE: 0x{:x}!", info);
  381. // let info = VmxAsm::vmx_vmread(ro::VMEXIT_INSTRUCTION_LEN);
  382. // debug!("vmexit handler: VMEXIT_INSTRUCTION_LEN: 0x{:x}!", info);
  383. // let info = VmxAsm::vmx_vmread(ro::VMEXIT_INSTRUCTION_INFO);
  384. // debug!("vmexit handler: VMEXIT_INSTRUCTION_INFO: 0x{:x}!", info);
  385. //panic
  386. // let info = VmxAsm::vmx_vmread(control::EPTP_INDEX);
  387. // debug!("vmexit handler: EPTP_INDEX: 0x{:x}!", info);
  388. //panic
  389. // let info = VmxAsm::vmx_vmread(control::VIRT_EXCEPTION_INFO_ADDR_FULL);
  390. // debug!("vmexit handler: VIRT_EXCEPTION_INFO_ADDR_FULL: 0x{:x}!", info);
  391. }