VmTdExitVeHandler.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577
  1. /** @file
  2. Copyright (c) 2021, Intel Corporation. All rights reserved.<BR>
  3. SPDX-License-Identifier: BSD-2-Clause-Patent
  4. **/
  5. #include <Library/BaseLib.h>
  6. #include <Library/DebugLib.h>
  7. #include "VmTdExitHandler.h"
  8. #include <Library/VmgExitLib.h>
  9. #include <Library/BaseMemoryLib.h>
  10. #include <IndustryStandard/Tdx.h>
  11. #include <IndustryStandard/InstructionParsing.h>
  12. typedef union {
  13. struct {
  14. UINT32 Eax;
  15. UINT32 Edx;
  16. } Regs;
  17. UINT64 Val;
  18. } MSR_DATA;
  19. typedef union {
  20. UINT8 Val;
  21. struct {
  22. UINT8 B : 1;
  23. UINT8 X : 1;
  24. UINT8 R : 1;
  25. UINT8 W : 1;
  26. } Bits;
  27. } REX;
  28. typedef union {
  29. UINT8 Val;
  30. struct {
  31. UINT8 Rm : 3;
  32. UINT8 Reg : 3;
  33. UINT8 Mod : 2;
  34. } Bits;
  35. } MODRM;
  36. typedef struct {
  37. UINT64 Regs[4];
  38. } CPUID_DATA;
  39. /**
  40. Handle an CPUID event.
  41. Use the TDVMCALL instruction to handle cpuid #ve
  42. @param[in, out] Regs x64 processor context
  43. @param[in] Veinfo VE Info
  44. @retval 0 Event handled successfully
  45. @return New exception value to propagate
  46. **/
  47. STATIC
  48. UINT64
  49. EFIAPI
  50. CpuIdExit (
  51. IN EFI_SYSTEM_CONTEXT_X64 *Regs,
  52. IN TDCALL_VEINFO_RETURN_DATA *Veinfo
  53. )
  54. {
  55. CPUID_DATA CpuIdData;
  56. UINT64 Status;
  57. Status = TdVmCallCpuid (Regs->Rax, Regs->Rcx, &CpuIdData);
  58. if (Status == 0) {
  59. Regs->Rax = CpuIdData.Regs[0];
  60. Regs->Rbx = CpuIdData.Regs[1];
  61. Regs->Rcx = CpuIdData.Regs[2];
  62. Regs->Rdx = CpuIdData.Regs[3];
  63. }
  64. return Status;
  65. }
  66. /**
  67. Handle an IO event.
  68. Use the TDVMCALL instruction to handle either an IO read or an IO write.
  69. @param[in, out] Regs x64 processor context
  70. @param[in] Veinfo VE Info
  71. @retval 0 Event handled successfully
  72. @return New exception value to propagate
  73. **/
  74. STATIC
  75. UINT64
  76. EFIAPI
  77. IoExit (
  78. IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs,
  79. IN TDCALL_VEINFO_RETURN_DATA *Veinfo
  80. )
  81. {
  82. BOOLEAN Write;
  83. UINTN Size;
  84. UINTN Port;
  85. UINT64 Val;
  86. UINT64 RepCnt;
  87. UINT64 Status;
  88. Val = 0;
  89. Write = Veinfo->ExitQualification.Io.Direction ? FALSE : TRUE;
  90. Size = Veinfo->ExitQualification.Io.Size + 1;
  91. Port = Veinfo->ExitQualification.Io.Port;
  92. if (Veinfo->ExitQualification.Io.String) {
  93. //
  94. // If REP is set, get rep-cnt from Rcx
  95. //
  96. RepCnt = Veinfo->ExitQualification.Io.Rep ? Regs->Rcx : 1;
  97. while (RepCnt) {
  98. Val = 0;
  99. if (Write == TRUE) {
  100. CopyMem (&Val, (VOID *)Regs->Rsi, Size);
  101. Regs->Rsi += Size;
  102. }
  103. Status = TdVmCall (EXIT_REASON_IO_INSTRUCTION, Size, Write, Port, Val, (Write ? NULL : &Val));
  104. if (Status != 0) {
  105. break;
  106. }
  107. if (Write == FALSE) {
  108. CopyMem ((VOID *)Regs->Rdi, &Val, Size);
  109. Regs->Rdi += Size;
  110. }
  111. if (Veinfo->ExitQualification.Io.Rep) {
  112. Regs->Rcx -= 1;
  113. }
  114. RepCnt -= 1;
  115. }
  116. } else {
  117. if (Write == TRUE) {
  118. CopyMem (&Val, (VOID *)&Regs->Rax, Size);
  119. }
  120. Status = TdVmCall (EXIT_REASON_IO_INSTRUCTION, Size, Write, Port, Val, (Write ? NULL : &Val));
  121. if ((Status == 0) && (Write == FALSE)) {
  122. CopyMem ((VOID *)&Regs->Rax, &Val, Size);
  123. }
  124. }
  125. return Status;
  126. }
  127. /**
  128. Handle an READ MSR event.
  129. Use the TDVMCALL instruction to handle msr read
  130. @param[in, out] Regs x64 processor context
  131. @param[in] Veinfo VE Info
  132. @retval 0 Event handled successfully
  133. @return New exception value to propagate
  134. **/
  135. STATIC
  136. UINT64
  137. ReadMsrExit (
  138. IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs,
  139. IN TDCALL_VEINFO_RETURN_DATA *Veinfo
  140. )
  141. {
  142. MSR_DATA Data;
  143. UINT64 Status;
  144. Status = TdVmCall (EXIT_REASON_MSR_READ, Regs->Rcx, 0, 0, 0, &Data);
  145. if (Status == 0) {
  146. Regs->Rax = Data.Regs.Eax;
  147. Regs->Rdx = Data.Regs.Edx;
  148. }
  149. return Status;
  150. }
  151. /**
  152. Handle an WRITE MSR event.
  153. Use the TDVMCALL instruction to handle msr write
  154. @param[in, out] Regs x64 processor context
  155. @param[in] Veinfo VE Info
  156. @retval 0 Event handled successfully
  157. @return New exception value to propagate
  158. **/
  159. STATIC
  160. UINT64
  161. WriteMsrExit (
  162. IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs,
  163. IN TDCALL_VEINFO_RETURN_DATA *Veinfo
  164. )
  165. {
  166. UINT64 Status;
  167. MSR_DATA Data;
  168. Data.Regs.Eax = (UINT32)Regs->Rax;
  169. Data.Regs.Edx = (UINT32)Regs->Rdx;
  170. Status = TdVmCall (EXIT_REASON_MSR_WRITE, Regs->Rcx, Data.Val, 0, 0, NULL);
  171. return Status;
  172. }
  173. STATIC
  174. VOID
  175. EFIAPI
  176. TdxDecodeInstruction (
  177. IN UINT8 *Rip
  178. )
  179. {
  180. UINTN i;
  181. DEBUG ((DEBUG_INFO, "TDX: #TD[EPT] instruction (%p):", Rip));
  182. for (i = 0; i < 15; i++) {
  183. DEBUG ((DEBUG_INFO, "%02x:", Rip[i]));
  184. }
  185. DEBUG ((DEBUG_INFO, "\n"));
  186. }
  187. #define TDX_DECODER_BUG_ON(x) \
  188. if ((x)) { \
  189. TdxDecodeInstruction(Rip); \
  190. TdVmCall(TDVMCALL_HALT, 0, 0, 0, 0, 0); \
  191. }
  192. STATIC
  193. UINT64 *
  194. EFIAPI
  195. GetRegFromContext (
  196. IN EFI_SYSTEM_CONTEXT_X64 *Regs,
  197. IN UINTN RegIndex
  198. )
  199. {
  200. switch (RegIndex) {
  201. case 0: return &Regs->Rax;
  202. break;
  203. case 1: return &Regs->Rcx;
  204. break;
  205. case 2: return &Regs->Rdx;
  206. break;
  207. case 3: return &Regs->Rbx;
  208. break;
  209. case 4: return &Regs->Rsp;
  210. break;
  211. case 5: return &Regs->Rbp;
  212. break;
  213. case 6: return &Regs->Rsi;
  214. break;
  215. case 7: return &Regs->Rdi;
  216. break;
  217. case 8: return &Regs->R8;
  218. break;
  219. case 9: return &Regs->R9;
  220. break;
  221. case 10: return &Regs->R10;
  222. break;
  223. case 11: return &Regs->R11;
  224. break;
  225. case 12: return &Regs->R12;
  226. break;
  227. case 13: return &Regs->R13;
  228. break;
  229. case 14: return &Regs->R14;
  230. break;
  231. case 15: return &Regs->R15;
  232. break;
  233. }
  234. return NULL;
  235. }
  236. /**
  237. Handle an MMIO event.
  238. Use the TDVMCALL instruction to handle either an mmio read or an mmio write.
  239. @param[in, out] Regs x64 processor context
  240. @param[in] Veinfo VE Info
  241. @retval 0 Event handled successfully
  242. @return New exception value to propagate
  243. **/
  244. STATIC
  245. INTN
  246. EFIAPI
  247. MmioExit (
  248. IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs,
  249. IN TDCALL_VEINFO_RETURN_DATA *Veinfo
  250. )
  251. {
  252. UINT64 Status;
  253. UINT32 MmioSize;
  254. UINT32 RegSize;
  255. UINT8 OpCode;
  256. BOOLEAN SeenRex;
  257. UINT64 *Reg;
  258. UINT8 *Rip;
  259. UINT64 Val;
  260. UINT32 OpSize;
  261. MODRM ModRm;
  262. REX Rex;
  263. TD_RETURN_DATA TdReturnData;
  264. UINT8 Gpaw;
  265. UINT64 TdSharedPageMask;
  266. Rip = (UINT8 *)Regs->Rip;
  267. Val = 0;
  268. Rex.Val = 0;
  269. SeenRex = FALSE;
  270. Status = TdCall (TDCALL_TDINFO, 0, 0, 0, &TdReturnData);
  271. if (Status == TDX_EXIT_REASON_SUCCESS) {
  272. Gpaw = (UINT8)(TdReturnData.TdInfo.Gpaw & 0x3f);
  273. TdSharedPageMask = 1ULL << (Gpaw - 1);
  274. } else {
  275. DEBUG ((DEBUG_ERROR, "TDCALL failed with status=%llx\n", Status));
  276. return Status;
  277. }
  278. if ((Veinfo->GuestPA & TdSharedPageMask) == 0) {
  279. DEBUG ((DEBUG_ERROR, "EPT-violation #VE on private memory is not allowed!"));
  280. TdVmCall (TDVMCALL_HALT, 0, 0, 0, 0, 0);
  281. CpuDeadLoop ();
  282. }
  283. //
  284. // Default to 32bit transfer
  285. //
  286. OpSize = 4;
  287. do {
  288. OpCode = *Rip++;
  289. if (OpCode == 0x66) {
  290. OpSize = 2;
  291. } else if ((OpCode == 0x64) || (OpCode == 0x65) || (OpCode == 0x67)) {
  292. continue;
  293. } else if ((OpCode >= 0x40) && (OpCode <= 0x4f)) {
  294. SeenRex = TRUE;
  295. Rex.Val = OpCode;
  296. } else {
  297. break;
  298. }
  299. } while (TRUE);
  300. //
  301. // We need to have at least 2 more bytes for this instruction
  302. //
  303. TDX_DECODER_BUG_ON (((UINT64)Rip - Regs->Rip) > 13);
  304. OpCode = *Rip++;
  305. //
  306. // Two-byte opecode, get next byte
  307. //
  308. if (OpCode == 0x0F) {
  309. OpCode = *Rip++;
  310. }
  311. switch (OpCode) {
  312. case 0x88:
  313. case 0x8A:
  314. case 0xB6:
  315. MmioSize = 1;
  316. break;
  317. case 0xB7:
  318. MmioSize = 2;
  319. break;
  320. default:
  321. MmioSize = Rex.Bits.W ? 8 : OpSize;
  322. break;
  323. }
  324. /* Punt on AH/BH/CH/DH unless it shows up. */
  325. ModRm.Val = *Rip++;
  326. TDX_DECODER_BUG_ON (MmioSize == 1 && ModRm.Bits.Reg > 4 && !SeenRex && OpCode != 0xB6);
  327. Reg = GetRegFromContext (Regs, ModRm.Bits.Reg | ((int)Rex.Bits.R << 3));
  328. TDX_DECODER_BUG_ON (!Reg);
  329. if (ModRm.Bits.Rm == 4) {
  330. ++Rip; /* SIB byte */
  331. }
  332. if ((ModRm.Bits.Mod == 2) || ((ModRm.Bits.Mod == 0) && (ModRm.Bits.Rm == 5))) {
  333. Rip += 4; /* DISP32 */
  334. } else if (ModRm.Bits.Mod == 1) {
  335. ++Rip; /* DISP8 */
  336. }
  337. switch (OpCode) {
  338. case 0x88:
  339. case 0x89:
  340. CopyMem ((void *)&Val, Reg, MmioSize);
  341. Status = TdVmCall (TDVMCALL_MMIO, MmioSize, 1, Veinfo->GuestPA, Val, 0);
  342. break;
  343. case 0xC7:
  344. CopyMem ((void *)&Val, Rip, OpSize);
  345. Status = TdVmCall (TDVMCALL_MMIO, MmioSize, 1, Veinfo->GuestPA, Val, 0);
  346. Rip += OpSize;
  347. default:
  348. //
  349. // 32-bit write registers are zero extended to the full register
  350. // Hence 'MOVZX r[32/64], r/m16' is
  351. // hardcoded to reg size 8, and the straight MOV case has a reg
  352. // size of 8 in the 32-bit read case.
  353. //
  354. switch (OpCode) {
  355. case 0xB6:
  356. RegSize = Rex.Bits.W ? 8 : OpSize;
  357. break;
  358. case 0xB7:
  359. RegSize = 8;
  360. break;
  361. default:
  362. RegSize = MmioSize == 4 ? 8 : MmioSize;
  363. break;
  364. }
  365. Status = TdVmCall (TDVMCALL_MMIO, MmioSize, 0, Veinfo->GuestPA, 0, &Val);
  366. if (Status == 0) {
  367. ZeroMem (Reg, RegSize);
  368. CopyMem (Reg, (void *)&Val, MmioSize);
  369. }
  370. }
  371. if (Status == 0) {
  372. TDX_DECODER_BUG_ON (((UINT64)Rip - Regs->Rip) > 15);
  373. //
  374. // We change instruction length to reflect true size so handler can
  375. // bump rip
  376. //
  377. Veinfo->ExitInstructionLength = (UINT32)((UINT64)Rip - Regs->Rip);
  378. }
  379. return Status;
  380. }
  381. /**
  382. Handle a #VE exception.
  383. Performs the necessary processing to handle a #VE exception.
  384. @param[in, out] ExceptionType Pointer to an EFI_EXCEPTION_TYPE to be set
  385. as value to use on error.
  386. @param[in, out] SystemContext Pointer to EFI_SYSTEM_CONTEXT
  387. @retval EFI_SUCCESS Exception handled
  388. @retval EFI_UNSUPPORTED #VE not supported, (new) exception value to
  389. propagate provided
  390. @retval EFI_PROTOCOL_ERROR #VE handling failed, (new) exception value to
  391. propagate provided
  392. **/
  393. EFI_STATUS
  394. EFIAPI
  395. VmTdExitHandleVe (
  396. IN OUT EFI_EXCEPTION_TYPE *ExceptionType,
  397. IN OUT EFI_SYSTEM_CONTEXT SystemContext
  398. )
  399. {
  400. UINT64 Status;
  401. TD_RETURN_DATA ReturnData;
  402. EFI_SYSTEM_CONTEXT_X64 *Regs;
  403. Regs = SystemContext.SystemContextX64;
  404. Status = TdCall (TDCALL_TDGETVEINFO, 0, 0, 0, &ReturnData);
  405. ASSERT (Status == 0);
  406. if (Status != 0) {
  407. DEBUG ((DEBUG_ERROR, "#VE happened. TDGETVEINFO failed with Status = 0x%llx\n", Status));
  408. TdVmCall (TDVMCALL_HALT, 0, 0, 0, 0, 0);
  409. }
  410. switch (ReturnData.VeInfo.ExitReason) {
  411. case EXIT_REASON_CPUID:
  412. Status = CpuIdExit (Regs, &ReturnData.VeInfo);
  413. DEBUG ((
  414. DEBUG_VERBOSE,
  415. "CPUID #VE happened, ExitReasion is %d, ExitQualification = 0x%x.\n",
  416. ReturnData.VeInfo.ExitReason,
  417. ReturnData.VeInfo.ExitQualification.Val
  418. ));
  419. break;
  420. case EXIT_REASON_HLT:
  421. Status = TdVmCall (EXIT_REASON_HLT, 0, 0, 0, 0, 0);
  422. break;
  423. case EXIT_REASON_IO_INSTRUCTION:
  424. Status = IoExit (Regs, &ReturnData.VeInfo);
  425. DEBUG ((
  426. DEBUG_VERBOSE,
  427. "IO_Instruction #VE happened, ExitReasion is %d, ExitQualification = 0x%x.\n",
  428. ReturnData.VeInfo.ExitReason,
  429. ReturnData.VeInfo.ExitQualification.Val
  430. ));
  431. break;
  432. case EXIT_REASON_MSR_READ:
  433. Status = ReadMsrExit (Regs, &ReturnData.VeInfo);
  434. DEBUG ((
  435. DEBUG_VERBOSE,
  436. "RDMSR #VE happened, ExitReasion is %d, ExitQualification = 0x%x. Regs->Rcx=0x%llx, Status = 0x%llx\n",
  437. ReturnData.VeInfo.ExitReason,
  438. ReturnData.VeInfo.ExitQualification.Val,
  439. Regs->Rcx,
  440. Status
  441. ));
  442. break;
  443. case EXIT_REASON_MSR_WRITE:
  444. Status = WriteMsrExit (Regs, &ReturnData.VeInfo);
  445. DEBUG ((
  446. DEBUG_VERBOSE,
  447. "WRMSR #VE happened, ExitReasion is %d, ExitQualification = 0x%x. Regs->Rcx=0x%llx, Status = 0x%llx\n",
  448. ReturnData.VeInfo.ExitReason,
  449. ReturnData.VeInfo.ExitQualification.Val,
  450. Regs->Rcx,
  451. Status
  452. ));
  453. break;
  454. case EXIT_REASON_EPT_VIOLATION:
  455. Status = MmioExit (Regs, &ReturnData.VeInfo);
  456. DEBUG ((
  457. DEBUG_VERBOSE,
  458. "MMIO #VE happened, ExitReasion is %d, ExitQualification = 0x%x.\n",
  459. ReturnData.VeInfo.ExitReason,
  460. ReturnData.VeInfo.ExitQualification.Val
  461. ));
  462. break;
  463. case EXIT_REASON_VMCALL:
  464. case EXIT_REASON_MWAIT_INSTRUCTION:
  465. case EXIT_REASON_MONITOR_INSTRUCTION:
  466. case EXIT_REASON_WBINVD:
  467. case EXIT_REASON_RDPMC:
  468. /* Handle as nops. */
  469. break;
  470. default:
  471. DEBUG ((
  472. DEBUG_ERROR,
  473. "Unsupported #VE happened, ExitReason is %d, ExitQualification = 0x%x.\n",
  474. ReturnData.VeInfo.ExitReason,
  475. ReturnData.VeInfo.ExitQualification.Val
  476. ));
  477. ASSERT (FALSE);
  478. CpuDeadLoop ();
  479. }
  480. if (Status) {
  481. DEBUG ((
  482. DEBUG_ERROR,
  483. "#VE Error (0x%llx) returned from host, ExitReason is %d, ExitQualification = 0x%x.\n",
  484. Status,
  485. ReturnData.VeInfo.ExitReason,
  486. ReturnData.VeInfo.ExitQualification.Val
  487. ));
  488. TdVmCall (TDVMCALL_HALT, 0, 0, 0, 0, 0);
  489. }
  490. SystemContext.SystemContextX64->Rip += ReturnData.VeInfo.ExitInstructionLength;
  491. return EFI_SUCCESS;
  492. }