[edk2-devel] [PATCH 07/23] MdePkg: Update BaseIoLibIntrinsicSev to support Tdx

Min Xu posted 23 patches 4 years, 6 months ago
[edk2-devel] [PATCH 07/23] MdePkg: Update BaseIoLibIntrinsicSev to support Tdx
Posted by Min Xu 4 years, 6 months ago
RFC: https://bugzilla.tianocore.org/show_bug.cgi?id=3429

Intel TDX architecture does not prescribe a specific software convention
to perform I/O from the guest TD. Guest TD providers have many choices to
provide I/O to the guest. The common I/O models are emulated devices,
para-virtualized devices, SRIOV devices and Direct Device assignments.

TDVF chooses para-virtualized I/O (Choice-A) which use the TDG.VP.VMCALL
function to invoke the funtions provided by the host VMM to perform I/O.
Another choice (Choice-B) is the emulation performed by the #VE handler.

There are 2 benefits of para-virtualized I/O:
1. Performance.
   VMEXIT/VMENTRY is skipped so that the performance is better than #VE
   handler.
2. De-couple with #VE handler.
   Choice-B depends on the #VE handler which means I/O is not available
   until #VE handler is installed. For example, in PEI phase #VE handler
   is installed in CpuMpPei, while communication with Qemu (via I/O port)
   happen earlier than it.

BaseIoLibIntrinsicSev.inf is the IoLib used by OvmfPkg. TDVF updates
BaseIoLibIntrinsicSev to support I/O in Td guest. Below files are updated
to support I/O in Td guest.
 - IoLib.c
 - IoLibGcc.c
 - IoLibMsc.c
 - X64/IoFifoSev.nasm

In the I/O functions of above files, if IsTdxGuest() returns TRUE, then
Td I/O routine is called, otherwise the legacy I/O routine is called.
Td I/O routines are declared in IoLibTdx.h and implemented in
IoLibInternalTdx.c.

BaseIoLibIntrinsic.inf is the IoLib used by other packages. It will not
support I/O in Td guest. But some files are shared between
BaseIoLibIntrinsic and BaseIoLibIntrinsicSev (IoLib.c is the example). So
IoLibInternalTdxNull.c is created which holds the null stub of the Td I/O
routines. IoLibInternalTdxNull.c is included in BaseIoLibIntrinsic.inf.
BaseIoLibIntrinsic.inf doesn't import TdxProbeLib/TdxLib so that the Pkgs
which include BaseIoLibIntrinsic.inf need not include TdxProbeLib/TdxLib.

BaseIoLibIntrinsicArmVirt.inf is not touched because it shares no files
with BaseIoLibIntrinsicSev.

Cc: Michael D Kinney <michael.d.kinney@intel.com>
Cc: Liming Gao <gaoliming@byosoft.com.cn>
Cc: Zhiguang Liu <zhiguang.liu@intel.com>
Cc: Brijesh Singh <brijesh.singh@amd.com>
Cc: Erdem Aktas <erdemaktas@google.com>
Cc: James Bottomley <jejb@linux.ibm.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Tom Lendacky <thomas.lendacky@amd.com>
Signed-off-by: Min Xu <min.m.xu@intel.com>
---
 .../BaseIoLibIntrinsic/BaseIoLibIntrinsic.inf |   2 +
 .../BaseIoLibIntrinsicSev.inf                 |   6 +-
 MdePkg/Library/BaseIoLibIntrinsic/IoLib.c     |  97 ++-
 MdePkg/Library/BaseIoLibIntrinsic/IoLibGcc.c  |  49 +-
 .../BaseIoLibIntrinsic/IoLibInternalTdx.c     | 690 ++++++++++++++++++
 .../BaseIoLibIntrinsic/IoLibInternalTdxNull.c | 499 +++++++++++++
 MdePkg/Library/BaseIoLibIntrinsic/IoLibMsc.c  |  73 +-
 MdePkg/Library/BaseIoLibIntrinsic/IoLibTdx.h  | 411 +++++++++++
 .../BaseIoLibIntrinsic/X64/IoFifoSev.nasm     | 133 ++++
 9 files changed, 1911 insertions(+), 49 deletions(-)
 create mode 100644 MdePkg/Library/BaseIoLibIntrinsic/IoLibInternalTdx.c
 create mode 100644 MdePkg/Library/BaseIoLibIntrinsic/IoLibInternalTdxNull.c
 create mode 100644 MdePkg/Library/BaseIoLibIntrinsic/IoLibTdx.h

diff --git a/MdePkg/Library/BaseIoLibIntrinsic/BaseIoLibIntrinsic.inf b/MdePkg/Library/BaseIoLibIntrinsic/BaseIoLibIntrinsic.inf
index 97eeada0656e..27b15d9ae256 100644
--- a/MdePkg/Library/BaseIoLibIntrinsic/BaseIoLibIntrinsic.inf
+++ b/MdePkg/Library/BaseIoLibIntrinsic/BaseIoLibIntrinsic.inf
@@ -34,6 +34,8 @@
   IoLibMmioBuffer.c
   BaseIoLibIntrinsicInternal.h
   IoHighLevel.c
+  IoLibInternalTdxNull.c
+  IoLibTdx.h
 
 [Sources.IA32]
   IoLibGcc.c    | GCC
diff --git a/MdePkg/Library/BaseIoLibIntrinsic/BaseIoLibIntrinsicSev.inf b/MdePkg/Library/BaseIoLibIntrinsic/BaseIoLibIntrinsicSev.inf
index 34f9d1d1062f..53ff8d6fd37d 100644
--- a/MdePkg/Library/BaseIoLibIntrinsic/BaseIoLibIntrinsicSev.inf
+++ b/MdePkg/Library/BaseIoLibIntrinsic/BaseIoLibIntrinsicSev.inf
@@ -30,17 +30,20 @@
   IoLibMmioBuffer.c
   BaseIoLibIntrinsicInternal.h
   IoHighLevel.c
+  IoLibTdx.h
 
 [Sources.IA32]
   IoLibGcc.c    | GCC
   IoLibMsc.c    | MSFT
   IoLib.c
+  IoLibInternalTdxNull.c
   Ia32/IoFifoSev.nasm
 
 [Sources.X64]
   IoLibGcc.c    | GCC
   IoLibMsc.c    | MSFT
   IoLib.c
+  IoLibInternalTdx.c
   X64/IoFifoSev.nasm
 
 [Packages]
@@ -50,4 +53,5 @@
   DebugLib
   BaseLib
   RegisterFilterLib
-
+  TdxLib
+  TdxProbeLib
diff --git a/MdePkg/Library/BaseIoLibIntrinsic/IoLib.c b/MdePkg/Library/BaseIoLibIntrinsic/IoLib.c
index d0d7044f0901..68298749ee56 100644
--- a/MdePkg/Library/BaseIoLibIntrinsic/IoLib.c
+++ b/MdePkg/Library/BaseIoLibIntrinsic/IoLib.c
@@ -7,6 +7,7 @@
 **/
 
 #include "BaseIoLibIntrinsicInternal.h"
+#include "IoLibTdx.h"
 
 /**
   Reads a 64-bit I/O port.
@@ -70,6 +71,8 @@ IoWrite64 (
 
   If 8-bit MMIO register operations are not supported, then ASSERT().
 
+  For Td guest TDVMCALL_MMIO is invoked to read MMIO registers.
+
   @param  Address The MMIO register to read.
 
   @return The value read.
@@ -86,9 +89,13 @@ MmioRead8 (
 
   Flag = FilterBeforeMmIoRead (FilterWidth8, Address, &Value);
   if (Flag) {
-    MemoryFence ();
-    Value = *(volatile UINT8*)Address;
-    MemoryFence ();
+    if (IsTdxGuest ()) {
+      Value = TdMmioRead8 (Address);
+    } else {
+      MemoryFence ();
+      Value = *(volatile UINT8*)Address;
+      MemoryFence ();
+    }
   }
   FilterAfterMmIoRead (FilterWidth8, Address, &Value);
 
@@ -104,6 +111,8 @@ MmioRead8 (
 
   If 8-bit MMIO register operations are not supported, then ASSERT().
 
+  For Td guest TDVMCALL_MMIO is invoked to write MMIO registers.
+
   @param  Address The MMIO register to write.
   @param  Value   The value to write to the MMIO register.
 
@@ -121,9 +130,13 @@ MmioWrite8 (
 
   Flag = FilterBeforeMmIoWrite (FilterWidth8, Address, &Value);
   if (Flag) {
-    MemoryFence ();
-    *(volatile UINT8*)Address = Value;
-    MemoryFence ();
+    if (IsTdxGuest ()) {
+      TdMmioWrite8 (Address, Value);
+    } else {
+      MemoryFence ();
+      *(volatile UINT8*)Address = Value;
+      MemoryFence ();
+    }
   }
   FilterAfterMmIoWrite (FilterWidth8, Address, &Value);
 
@@ -140,6 +153,8 @@ MmioWrite8 (
   If 16-bit MMIO register operations are not supported, then ASSERT().
   If Address is not aligned on a 16-bit boundary, then ASSERT().
 
+  For Td guest TDVMCALL_MMIO is invoked to read MMIO registers.
+
   @param  Address The MMIO register to read.
 
   @return The value read.
@@ -157,9 +172,13 @@ MmioRead16 (
   ASSERT ((Address & 1) == 0);
   Flag = FilterBeforeMmIoRead (FilterWidth16, Address, &Value);
   if (Flag) {
-    MemoryFence ();
-    Value = *(volatile UINT16*)Address;
-    MemoryFence ();
+    if (IsTdxGuest ()) {
+      Value = TdMmioRead16 (Address);
+    } else {
+      MemoryFence ();
+      Value = *(volatile UINT16*)Address;
+      MemoryFence ();
+    }
   }
   FilterAfterMmIoRead (FilterWidth16, Address, &Value);
 
@@ -176,6 +195,8 @@ MmioRead16 (
   If 16-bit MMIO register operations are not supported, then ASSERT().
   If Address is not aligned on a 16-bit boundary, then ASSERT().
 
+  For Td guest TDVMCALL_MMIO is invoked to write MMIO registers.
+
   @param  Address The MMIO register to write.
   @param  Value   The value to write to the MMIO register.
 
@@ -195,9 +216,13 @@ MmioWrite16 (
 
   Flag = FilterBeforeMmIoWrite (FilterWidth16, Address, &Value);
   if (Flag) {
-    MemoryFence ();
-    *(volatile UINT16*)Address = Value;
-    MemoryFence ();
+    if (IsTdxGuest ()) {
+      TdMmioWrite16 (Address, Value);
+    } else {
+      MemoryFence ();
+      *(volatile UINT16*)Address = Value;
+      MemoryFence ();
+    }
   }
   FilterAfterMmIoWrite (FilterWidth16, Address, &Value);
 
@@ -214,6 +239,8 @@ MmioWrite16 (
   If 32-bit MMIO register operations are not supported, then ASSERT().
   If Address is not aligned on a 32-bit boundary, then ASSERT().
 
+  For Td guest TDVMCALL_MMIO is invoked to read MMIO registers.
+
   @param  Address The MMIO register to read.
 
   @return The value read.
@@ -232,9 +259,13 @@ MmioRead32 (
 
   Flag = FilterBeforeMmIoRead (FilterWidth32, Address, &Value);
   if (Flag) {
-    MemoryFence ();
-    Value = *(volatile UINT32*)Address;
-    MemoryFence ();
+    if (IsTdxGuest ()) {
+      Value = TdMmioRead32 (Address);
+    } else {
+      MemoryFence ();
+      Value = *(volatile UINT32*)Address;
+      MemoryFence ();
+    }
   }
   FilterAfterMmIoRead (FilterWidth32, Address, &Value);
 
@@ -251,6 +282,8 @@ MmioRead32 (
   If 32-bit MMIO register operations are not supported, then ASSERT().
   If Address is not aligned on a 32-bit boundary, then ASSERT().
 
+  For Td guest TDVMCALL_MMIO is invoked to write MMIO registers.
+
   @param  Address The MMIO register to write.
   @param  Value   The value to write to the MMIO register.
 
@@ -270,9 +303,13 @@ MmioWrite32 (
 
   Flag = FilterBeforeMmIoWrite (FilterWidth32, Address, &Value);
   if (Flag) {
-    MemoryFence ();
-    *(volatile UINT32*)Address = Value;
-    MemoryFence ();
+    if (IsTdxGuest ()) {
+      TdMmioWrite32 (Address, Value);
+    } else {
+      MemoryFence ();
+      *(volatile UINT32*)Address = Value;
+      MemoryFence ();
+    }
   }
   FilterAfterMmIoWrite (FilterWidth32, Address, &Value);
 
@@ -289,6 +326,8 @@ MmioWrite32 (
   If 64-bit MMIO register operations are not supported, then ASSERT().
   If Address is not aligned on a 64-bit boundary, then ASSERT().
 
+  For Td guest TDVMCALL_MMIO is invoked to read MMIO registers.
+
   @param  Address The MMIO register to read.
 
   @return The value read.
@@ -307,9 +346,13 @@ MmioRead64 (
 
   Flag = FilterBeforeMmIoRead (FilterWidth64, Address, &Value);
   if (Flag) {
-    MemoryFence ();
-    Value = *(volatile UINT64*)Address;
-    MemoryFence ();
+    if (IsTdxGuest ()) {
+      Value = TdMmioRead64 (Address);
+    } else {
+      MemoryFence ();
+      Value = *(volatile UINT64*)Address;
+      MemoryFence ();
+    }
   }
   FilterAfterMmIoRead (FilterWidth64, Address, &Value);
 
@@ -326,6 +369,8 @@ MmioRead64 (
   If 64-bit MMIO register operations are not supported, then ASSERT().
   If Address is not aligned on a 64-bit boundary, then ASSERT().
 
+  For Td guest TDVMCALL_MMIO is invoked to write MMIO registers.
+
   @param  Address The MMIO register to write.
   @param  Value   The value to write to the MMIO register.
 
@@ -343,9 +388,13 @@ MmioWrite64 (
 
   Flag = FilterBeforeMmIoWrite (FilterWidth64, Address, &Value);
   if (Flag) {
-    MemoryFence ();
-    *(volatile UINT64*)Address = Value;
-    MemoryFence ();
+    if (IsTdxGuest ()) {
+      TdMmioWrite64 (Address, Value);
+    } else {
+      MemoryFence ();
+      *(volatile UINT64*)Address = Value;
+      MemoryFence ();
+    }
   }
   FilterAfterMmIoWrite (FilterWidth64, Address, &Value);
 
diff --git a/MdePkg/Library/BaseIoLibIntrinsic/IoLibGcc.c b/MdePkg/Library/BaseIoLibIntrinsic/IoLibGcc.c
index ecf9ed61911f..42b5d5743a4f 100644
--- a/MdePkg/Library/BaseIoLibIntrinsic/IoLibGcc.c
+++ b/MdePkg/Library/BaseIoLibIntrinsic/IoLibGcc.c
@@ -17,6 +17,7 @@
 
 
 #include "BaseIoLibIntrinsicInternal.h"
+#include "IoLibTdx.h"
 
 /**
   Reads an 8-bit I/O port.
@@ -25,7 +26,9 @@
   This function must guarantee that all I/O read and write operations are
   serialized.
 
-  If 8-bit I/O port operations are not supported, then ASSERT().
+  If 8-bit I/O port operations are not supported, then ASSERT()
+
+  For Td guest TDVMCALL_IO is invoked to read I/O port.
 
   @param  Port  The I/O port to read.
 
@@ -43,7 +46,11 @@ IoRead8 (
 
   Flag = FilterBeforeIoRead (FilterWidth8, Port, &Data);
   if (Flag) {
-    __asm__ __volatile__ ("inb %w1,%b0" : "=a" (Data) : "d" ((UINT16)Port));
+    if (IsTdxGuest ()) {
+      Data = TdIoRead8 (Port);
+    } else {
+      __asm__ __volatile__ ("inb %w1,%b0" : "=a" (Data) : "d" ((UINT16)Port));
+    }
   }
   FilterAfterIoRead (FilterWidth8, Port, &Data);
 
@@ -59,6 +66,8 @@ IoRead8 (
 
   If 8-bit I/O port operations are not supported, then ASSERT().
 
+  For Td guest TDVMCALL_IO is invoked to write I/O port.
+
   @param  Port  The I/O port to write.
   @param  Value The value to write to the I/O port.
 
@@ -76,7 +85,11 @@ IoWrite8 (
 
   Flag = FilterBeforeIoWrite (FilterWidth8, Port, &Value);
   if (Flag) {
-    __asm__ __volatile__ ("outb %b0,%w1" : : "a" (Value), "d" ((UINT16)Port));
+    if (IsTdxGuest ()) {
+      TdIoWrite8 (Port, Value);
+    } else {
+      __asm__ __volatile__ ("outb %b0,%w1" : : "a" (Value), "d" ((UINT16)Port));
+    }
   }
   FilterAfterIoWrite (FilterWidth8, Port, &Value);
 
@@ -93,6 +106,8 @@ IoWrite8 (
   If 16-bit I/O port operations are not supported, then ASSERT().
   If Port is not aligned on a 16-bit boundary, then ASSERT().
 
+  For Td guest TDVMCALL_IO is invoked to read I/O port.
+
   @param  Port  The I/O port to read.
 
   @return The value read.
@@ -111,7 +126,11 @@ IoRead16 (
 
   Flag = FilterBeforeIoRead (FilterWidth16, Port, &Data);
   if (Flag) {
+    if (IsTdxGuest ()) {
+      Data = TdIoRead16 (Port);
+    } else {
      __asm__ __volatile__ ("inw %w1,%w0" : "=a" (Data) : "d" ((UINT16)Port));
+    }
   }
   FilterAfterIoRead (FilterWidth16, Port, &Data);
 
@@ -128,6 +147,8 @@ IoRead16 (
   If 16-bit I/O port operations are not supported, then ASSERT().
   If Port is not aligned on a 16-bit boundary, then ASSERT().
 
+  For Td guest TDVMCALL_IO is invoked to write I/O port.
+
   @param  Port  The I/O port to write.
   @param  Value The value to write to the I/O port.
 
@@ -148,7 +169,11 @@ IoWrite16 (
 
   Flag = FilterBeforeIoWrite (FilterWidth16, Port, &Value);
   if (Flag) {
-    __asm__ __volatile__ ("outw %w0,%w1" : : "a" (Value), "d" ((UINT16)Port));
+    if (IsTdxGuest ()) {
+      TdIoWrite16 (Port, Value);
+    } else {
+      __asm__ __volatile__ ("outw %w0,%w1" : : "a" (Value), "d" ((UINT16)Port));
+    }
   }
   FilterAfterIoWrite (FilterWidth16, Port, &Value);
 
@@ -165,6 +190,8 @@ IoWrite16 (
   If 32-bit I/O port operations are not supported, then ASSERT().
   If Port is not aligned on a 32-bit boundary, then ASSERT().
 
+  For Td guest TDVMCALL_IO is invoked to read I/O port.
+
   @param  Port  The I/O port to read.
 
   @return The value read.
@@ -183,7 +210,11 @@ IoRead32 (
 
   Flag = FilterBeforeIoRead (FilterWidth32, Port, &Data);
   if (Flag) {
-    __asm__ __volatile__ ("inl %w1,%0" : "=a" (Data) : "d" ((UINT16)Port));
+    if (IsTdxGuest ()) {
+      Data = TdIoRead32 (Port);
+    } else {
+      __asm__ __volatile__ ("inl %w1,%0" : "=a" (Data) : "d" ((UINT16)Port));
+    }
   }
   FilterAfterIoRead (FilterWidth32, Port, &Data);
 
@@ -200,6 +231,8 @@ IoRead32 (
   If 32-bit I/O port operations are not supported, then ASSERT().
   If Port is not aligned on a 32-bit boundary, then ASSERT().
 
+  For Td guest TDVMCALL_IO is invoked to write I/O port.
+
   @param  Port  The I/O port to write.
   @param  Value The value to write to the I/O port.
 
@@ -219,7 +252,11 @@ IoWrite32 (
 
   Flag = FilterBeforeIoWrite (FilterWidth32, Port, &Value);
   if (Flag) {
-    __asm__ __volatile__ ("outl %0,%w1" : : "a" (Value), "d" ((UINT16)Port));
+    if (IsTdxGuest ()) {
+      TdIoWrite32 (Port, Value);
+    } else {
+      __asm__ __volatile__ ("outl %0,%w1" : : "a" (Value), "d" ((UINT16)Port));
+    }
   }
   FilterAfterIoWrite (FilterWidth32, Port, &Value);
 
diff --git a/MdePkg/Library/BaseIoLibIntrinsic/IoLibInternalTdx.c b/MdePkg/Library/BaseIoLibIntrinsic/IoLibInternalTdx.c
new file mode 100644
index 000000000000..4c342f6d3873
--- /dev/null
+++ b/MdePkg/Library/BaseIoLibIntrinsic/IoLibInternalTdx.c
@@ -0,0 +1,690 @@
+/** @file
+  TDX I/O Library routines.
+
+  Copyright (c) 2020-2021, Intel Corporation. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+#include "BaseIoLibIntrinsicInternal.h"
+#include <Include/IndustryStandard/Tdx.h>
+#include <Library/TdxLib.h>
+#include <Library/TdxProbeLib.h>
+#include "IoLibTdx.h"
+
+// Size of TDVMCALL Access, including IO and MMIO
+#define TDVMCALL_ACCESS_SIZE_1      1
+#define TDVMCALL_ACCESS_SIZE_2      2
+#define TDVMCALL_ACCESS_SIZE_4      4
+#define TDVMCALL_ACCESS_SIZE_8      8
+
+// Direction of TDVMCALL Access, including IO and MMIO
+#define TDVMCALL_ACCESS_READ        0
+#define TDVMCALL_ACCESS_WRITE       1
+
+/**
+  Check if it is Tdx guest.
+
+  @return TRUE    It is Tdx guest
+  @return FALSE   It is not Tdx guest
+
+**/
+BOOLEAN
+EFIAPI
+IsTdxGuest (
+  VOID
+  )
+{
+  return TdxIsEnabled ();
+}
+
+
+/**
+  Reads an 8-bit I/O port.
+
+  TDVMCALL_IO is invoked to read I/O port.
+
+  @param  Port  The I/O port to read.
+
+  @return The value read.
+
+**/
+UINT8
+EFIAPI
+TdIoRead8 (
+  IN      UINTN                     Port
+  )
+{
+  UINT64 Status;
+  UINT64 Val;
+
+  Status = TdVmCall (TDVMCALL_IO, TDVMCALL_ACCESS_SIZE_1, TDVMCALL_ACCESS_READ, Port, 0, &Val);
+  if (Status != 0) {
+    TdVmCall (TDVMCALL_HALT, 0, 0, 0, 0, 0);
+  }
+  return (UINT8) Val;
+}
+
+/**
+  Reads a 16-bit I/O port.
+
+  TDVMCALL_IO is invoked to write I/O port.
+
+  @param  Port  The I/O port to read.
+
+  @return The value read.
+
+**/
+UINT16
+EFIAPI
+TdIoRead16 (
+  IN      UINTN                     Port
+  )
+{
+  UINT64 Status;
+  UINT64 Val;
+
+  ASSERT ((Port & 1) == 0);
+
+  Status = TdVmCall (TDVMCALL_IO, TDVMCALL_ACCESS_SIZE_2, TDVMCALL_ACCESS_READ, Port, 0, &Val);
+  if (Status != 0) {
+    TdVmCall (TDVMCALL_HALT, 0, 0, 0, 0, 0);
+  }
+  return (UINT16) Val;
+}
+
+/**
+  Reads a 32-bit I/O port.
+
+  TDVMCALL_IO is invoked to read I/O port.
+
+  @param  Port  The I/O port to read.
+
+  @return The value read.
+
+**/
+UINT32
+EFIAPI
+TdIoRead32 (
+  IN      UINTN                     Port
+  )
+{
+  UINT64 Status;
+  UINT64 Val;
+
+  ASSERT ((Port & 3) == 0);
+
+  Status = TdVmCall (TDVMCALL_IO, TDVMCALL_ACCESS_SIZE_4, TDVMCALL_ACCESS_READ, Port, 0, &Val);
+  if (Status != 0) {
+    TdVmCall (TDVMCALL_HALT, 0, 0, 0, 0, 0);
+  }
+  return (UINT32) Val;
+}
+
+/**
+  Writes an 8-bit I/O port.
+
+  TDVMCALL_IO is invoked to write I/O port.
+
+  @param  Port  The I/O port to write.
+  @param  Value The value to write to the I/O port.
+
+  @return The value written the I/O port.
+
+**/
+UINT8
+EFIAPI
+TdIoWrite8 (
+  IN      UINTN                     Port,
+  IN      UINT8                     Value
+  )
+{
+  UINT64 Status;
+  UINT64 Val;
+
+  Val = Value;
+  Status = TdVmCall (TDVMCALL_IO, TDVMCALL_ACCESS_SIZE_1, TDVMCALL_ACCESS_WRITE, Port, Val, 0);
+  if (Status != 0) {
+    TdVmCall (TDVMCALL_HALT, 0, 0, 0, 0, 0);
+  }
+  return Value;
+}
+
+/**
+  Writes a 16-bit I/O port.
+
+  TDVMCALL_IO is invoked to write I/O port.
+
+  @param  Port  The I/O port to write.
+  @param  Value The value to write to the I/O port.
+
+  @return The value written the I/O port.
+
+**/
+UINT16
+EFIAPI
+TdIoWrite16 (
+  IN      UINTN                     Port,
+  IN      UINT16                    Value
+  )
+{
+  UINT64 Status;
+  UINT64 Val;
+
+  ASSERT ((Port & 1) == 0);
+  Val = Value;
+  Status = TdVmCall (TDVMCALL_IO, TDVMCALL_ACCESS_SIZE_2, TDVMCALL_ACCESS_WRITE, Port, Val, 0);
+  if (Status != 0) {
+    TdVmCall (TDVMCALL_HALT, 0, 0, 0, 0, 0);
+  }
+  return Value;
+}
+
+/**
+  Writes a 32-bit I/O port.
+
+  TDVMCALL_IO is invoked to write I/O port.
+
+  @param  Port  The I/O port to write.
+  @param  Value The value to write to the I/O port.
+
+  @return The value written the I/O port.
+
+**/
+UINT32
+EFIAPI
+TdIoWrite32 (
+  IN      UINTN                     Port,
+  IN      UINT32                    Value
+  )
+{
+  UINT64 Status;
+  UINT64 Val;
+
+  ASSERT ((Port & 3) == 0);
+  Val = Value;
+  Status = TdVmCall (TDVMCALL_IO, TDVMCALL_ACCESS_SIZE_4, TDVMCALL_ACCESS_WRITE, Port, Val, 0);
+  if (Status != 0) {
+    TdVmCall (TDVMCALL_HALT, 0, 0, 0, 0, 0);
+  }
+  return Value;
+}
+
+/**
+  Reads an 8-bit MMIO register.
+
+  TDVMCALL_MMIO is invoked to read MMIO registers.
+
+  @param  Address The MMIO register to read.
+
+  @return The value read.
+
+**/
+UINT8
+EFIAPI
+TdMmioRead8 (
+  IN      UINTN                     Address
+  )
+{
+  UINT64                             Value;
+  UINT64                             Status;
+
+  Address |= TdSharedPageMask ();
+
+  MemoryFence ();
+  Status = TdVmCall (TDVMCALL_MMIO, TDVMCALL_ACCESS_SIZE_1, TDVMCALL_ACCESS_READ, Address, 0, &Value);
+  if (Status != 0) {
+    Value = *(volatile UINT64*) Address;
+  }
+  MemoryFence ();
+
+  return (UINT8) Value;
+}
+
+/**
+  Writes an 8-bit MMIO register.
+
+  TDVMCALL_MMIO is invoked to read write registers.
+
+  @param  Address The MMIO register to write.
+  @param  Value   The value to write to the MMIO register.
+
+  @return Value.
+
+**/
+UINT8
+EFIAPI
+TdMmioWrite8 (
+  IN      UINTN                     Address,
+  IN      UINT8                     Value
+  )
+{
+  UINT64                             Val;
+  UINT64                             Status;
+
+  Address |= TdSharedPageMask ();
+
+  MemoryFence ();
+  Val = Value;
+  Status = TdVmCall (TDVMCALL_MMIO, TDVMCALL_ACCESS_SIZE_1, TDVMCALL_ACCESS_WRITE, Address, Val, 0);
+  if (Status != 0) {
+    *(volatile UINT8*) Address = Value;
+  }
+  MemoryFence ();
+
+  return Value;
+}
+
+/**
+  Reads a 16-bit MMIO register.
+
+  TDVMCALL_MMIO is invoked to read MMIO registers.
+
+  @param  Address The MMIO register to read.
+
+  @return The value read.
+
+**/
+UINT16
+EFIAPI
+TdMmioRead16 (
+  IN      UINTN                     Address
+  )
+{
+  UINT64                             Value;
+  UINT64                             Status;
+
+  Address |= TdSharedPageMask ();
+
+  MemoryFence ();
+  Status = TdVmCall (TDVMCALL_MMIO, TDVMCALL_ACCESS_SIZE_2, TDVMCALL_ACCESS_READ, Address, 0, &Value);
+  if (Status != 0) {
+    Value = *(volatile UINT64*) Address;
+  }
+  MemoryFence ();
+
+  return (UINT16) Value;
+}
+
+/**
+  Writes a 16-bit MMIO register.
+
+  TDVMCALL_MMIO is invoked to write MMIO registers.
+
+  @param  Address The MMIO register to write.
+  @param  Value   The value to write to the MMIO register.
+
+  @return Value.
+
+**/
+UINT16
+EFIAPI
+TdMmioWrite16 (
+  IN      UINTN                     Address,
+  IN      UINT16                    Value
+  )
+{
+  UINT64                             Val;
+  UINT64                             Status;
+
+  ASSERT ((Address & 1) == 0);
+
+  Address |= TdSharedPageMask ();
+
+  MemoryFence ();
+  Val = Value;
+  Status = TdVmCall (TDVMCALL_MMIO, TDVMCALL_ACCESS_SIZE_2, TDVMCALL_ACCESS_WRITE, Address, Val, 0);
+  if (Status != 0) {
+    *(volatile UINT16*) Address = Value;
+  }
+  MemoryFence ();
+
+  return Value;
+}
+
+/**
+  Reads a 32-bit MMIO register.
+
+  TDVMCALL_MMIO is invoked to read MMIO registers.
+
+  @param  Address The MMIO register to read.
+
+  @return The value read.
+
+**/
+UINT32
+EFIAPI
+TdMmioRead32 (
+  IN      UINTN                     Address
+  )
+{
+  UINT64                             Value;
+  UINT64                             Status;
+
+  Address |= TdSharedPageMask ();
+
+  MemoryFence ();
+  Status = TdVmCall (TDVMCALL_MMIO, TDVMCALL_ACCESS_SIZE_4, TDVMCALL_ACCESS_READ, Address, 0, &Value);
+  if (Status != 0) {
+    Value = *(volatile UINT64*)Address;
+  }
+  MemoryFence ();
+
+  return (UINT32)Value;
+}
+
+/**
+  Writes a 32-bit MMIO register.
+
+  TDVMCALL_MMIO is invoked to write MMIO registers.
+
+  @param  Address The MMIO register to write.
+  @param  Value   The value to write to the MMIO register.
+
+  @return Value.
+
+**/
+UINT32
+EFIAPI
+TdMmioWrite32 (
+  IN      UINTN                     Address,
+  IN      UINT32                    Value
+  )
+{
+  UINT64                             Val;
+  UINT64                             Status;
+
+  ASSERT ((Address & 3) == 0);
+
+  Address |= TdSharedPageMask ();
+
+  MemoryFence ();
+  Val = Value;
+  Status = TdVmCall (TDVMCALL_MMIO, TDVMCALL_ACCESS_SIZE_4, TDVMCALL_ACCESS_WRITE, Address, Val, 0);
+  if (Status != 0) {
+    *(volatile UINT32*)Address = Value;
+  }
+  MemoryFence ();
+
+  return Value;
+}
+
+/**
+  Reads a 64-bit MMIO register.
+
+  TDVMCALL_MMIO is invoked to read MMIO registers.
+
+  @param  Address The MMIO register to read.
+
+  @return The value read.
+
+**/
+UINT64
+EFIAPI
+TdMmioRead64 (
+  IN      UINTN                     Address
+  )
+{
+  UINT64                             Value;
+  UINT64                             Status;
+
+  Address |= TdSharedPageMask ();
+
+  MemoryFence ();
+  Status = TdVmCall (TDVMCALL_MMIO, TDVMCALL_ACCESS_SIZE_8, TDVMCALL_ACCESS_READ, Address, 0, &Value);
+  if (Status != 0) {
+    Value = *(volatile UINT64*)Address;
+  }
+  MemoryFence ();
+
+  return Value;
+}
+
+/**
+  Writes a 64-bit MMIO register.
+
+  TDVMCALL_MMIO is invoked to write MMIO registers.
+
+  @param  Address The MMIO register to write.
+  @param  Value   The value to write to the MMIO register.
+
+**/
+UINT64
+EFIAPI
+TdMmioWrite64 (
+  IN      UINTN                     Address,
+  IN      UINT64                    Value
+  )
+{
+  UINT64          Status;
+  UINT64          Val;
+
+  ASSERT ((Address & 7) == 0);
+
+  Address |= TdSharedPageMask ();
+
+  MemoryFence ();
+  Val = Value;
+  Status = TdVmCall (TDVMCALL_MMIO, TDVMCALL_ACCESS_SIZE_8, TDVMCALL_ACCESS_WRITE, Address, Val, 0);
+  if (Status != 0) {
+    *(volatile UINT64*)Address = Value;
+  }
+  MemoryFence ();
+  return Value;
+}
+
+/**
+  Reads an 8-bit I/O port fifo into a block of memory.
+
+  Reads the 8-bit I/O fifo port specified by Port.
+  The port is read Count times, and the read data is
+  stored in the provided Buffer.
+
+  This function must guarantee that all I/O read and write operations are
+  serialized.
+
+  If 8-bit I/O port operations are not supported, then ASSERT().
+
+  In TDX a serial of TdIoRead8 is invoked to read the I/O port fifo.
+
+  @param  Port    The I/O port to read.
+  @param  Count   The number of times to read I/O port.
+  @param  Buffer  The buffer to store the read data into.
+
+**/
+VOID
+EFIAPI
+TdIoReadFifo8 (
+  IN      UINTN                     Port,
+  IN      UINTN                     Count,
+  OUT     VOID                      *Buffer
+  )
+{
+  UINT8   *Buf8;
+  UINTN   Index;
+
+  Buf8 = (UINT8 *) Buffer;
+  for (Index = 0; Index < Count; Index++) {
+    Buf8[Index] = TdIoRead8 (Port);
+  }
+}
+
+/**
+  Writes a block of memory into an 8-bit I/O port fifo.
+
+  Writes the 8-bit I/O fifo port specified by Port.
+  The port is written Count times, and the write data is
+  retrieved from the provided Buffer.
+
+  This function must guarantee that all I/O write and write operations are
+  serialized.
+
+  If 8-bit I/O port operations are not supported, then ASSERT().
+
+  In TDX a serial of TdIoWrite8 is invoked to write data to the I/O port.
+
+  @param  Port    The I/O port to write.
+  @param  Count   The number of times to write I/O port.
+  @param  Buffer  The buffer to retrieve the write data from.
+
+**/
+VOID
+EFIAPI
+TdIoWriteFifo8 (
+  IN      UINTN                     Port,
+  IN      UINTN                     Count,
+  IN      VOID                      *Buffer
+  )
+{
+  UINT8   *Buf8;
+  UINTN   Index;
+
+  Buf8 = (UINT8 *) Buffer;
+  for (Index = 0; Index < Count; Index++) {
+    TdIoWrite8 (Port, Buf8[Index]);
+  }
+}
+
+/**
+  Reads a 16-bit I/O port fifo into a block of memory.
+
+  Reads the 16-bit I/O fifo port specified by Port.
+  The port is read Count times, and the read data is
+  stored in the provided Buffer.
+
+  This function must guarantee that all I/O read and write operations are
+  serialized.
+
+  If 16-bit I/O port operations are not supported, then ASSERT().
+
+  In TDX a serial of TdIoRead16 is invoked to read data from the I/O port.
+
+  @param  Port    The I/O port to read.
+  @param  Count   The number of times to read I/O port.
+  @param  Buffer  The buffer to store the read data into.
+
+**/
+VOID
+EFIAPI
+TdIoReadFifo16 (
+  IN      UINTN                     Port,
+  IN      UINTN                     Count,
+  OUT     VOID                      *Buffer
+  )
+{
+  UINT16      *Buf16;
+  UINTN       Index;
+
+  Buf16 = (UINT16 *) Buffer;
+  for (Index = 0; Index < Count; Index++) {
+    Buf16[Index] = TdIoRead16 (Port);
+  }
+}
+
+/**
+  Writes a block of memory into a 16-bit I/O port fifo.
+
+  Writes the 16-bit I/O fifo port specified by Port.
+  The port is written Count times, and the write data is
+  retrieved from the provided Buffer.
+
+  This function must guarantee that all I/O write and write operations are
+  serialized.
+
+  If 16-bit I/O port operations are not supported, then ASSERT().
+
+  In TDX a serial of TdIoWrite16 is invoked to write data to the I/O port.
+
+  @param  Port    The I/O port to write.
+  @param  Count   The number of times to write I/O port.
+  @param  Buffer  The buffer to retrieve the write data from.
+
+**/
+VOID
+EFIAPI
+TdIoWriteFifo16 (
+  IN      UINTN                     Port,
+  IN      UINTN                     Count,
+  IN      VOID                      *Buffer
+  )
+{
+  UINT16   *Buf16;
+  UINTN    Index;
+
+  Buf16 = (UINT16 *) Buffer;
+  for (Index = 0; Index < Count; Index++) {
+    TdIoWrite16 (Port, Buf16[Index]);
+  }
+}
+
+/**
+  Reads a 32-bit I/O port fifo into a block of memory.
+
+  Reads the 32-bit I/O fifo port specified by Port.
+  The port is read Count times, and the read data is
+  stored in the provided Buffer.
+
+  This function must guarantee that all I/O read and write operations are
+  serialized.
+
+  If 32-bit I/O port operations are not supported, then ASSERT().
+
+  In TDX a serial of TdIoRead32 is invoked to read data from the I/O port.
+
+  @param  Port    The I/O port to read.
+  @param  Count   The number of times to read I/O port.
+  @param  Buffer  The buffer to store the read data into.
+
+**/
+VOID
+EFIAPI
+TdIoReadFifo32 (
+  IN      UINTN                     Port,
+  IN      UINTN                     Count,
+  OUT     VOID                      *Buffer
+  )
+{
+  UINT32      *Buf32;
+  UINTN       Index;
+
+  Buf32 = (UINT32 *) Buffer;
+  for (Index = 0; Index < Count; Index++) {
+    Buf32[Index] = TdIoRead32 (Port);
+  }
+}
+
+/**
+  Writes a block of memory into a 32-bit I/O port fifo.
+
+  Writes the 32-bit I/O fifo port specified by Port.
+  The port is written Count times, and the write data is
+  retrieved from the provided Buffer.
+
+  This function must guarantee that all I/O write and write operations are
+  serialized.
+
+  If 32-bit I/O port operations are not supported, then ASSERT().
+
+  In TDX a serial of TdIoWrite32 is invoked to write data to the I/O port.
+
+  @param  Port    The I/O port to write.
+  @param  Count   The number of times to write I/O port.
+  @param  Buffer  The buffer to retrieve the write data from.
+
+**/
+VOID
+EFIAPI
+TdIoWriteFifo32 (
+  IN      UINTN                     Port,
+  IN      UINTN                     Count,
+  IN      VOID                      *Buffer
+  )
+{
+  UINT32   *Buf32;
+  UINTN    Index;
+
+  Buf32 = (UINT32 *) Buffer;
+  for (Index = 0; Index < Count; Index++) {
+    TdIoWrite32 (Port, Buf32[Index]);
+  }
+}
+
diff --git a/MdePkg/Library/BaseIoLibIntrinsic/IoLibInternalTdxNull.c b/MdePkg/Library/BaseIoLibIntrinsic/IoLibInternalTdxNull.c
new file mode 100644
index 000000000000..f518d8ffd825
--- /dev/null
+++ b/MdePkg/Library/BaseIoLibIntrinsic/IoLibInternalTdxNull.c
@@ -0,0 +1,499 @@
+/** @file
+  Copyright (c) 2021, Intel Corporation. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+
+#include <Library/BaseLib.h>
+#include "BaseIoLibIntrinsicInternal.h"
+#include "IoLibTdx.h"
+
+/**
+  Check if it is Tdx guest.
+
+  @return TRUE    It is Tdx guest
+  @return FALSE   It is not Tdx guest
+
+**/
+BOOLEAN
+EFIAPI
+IsTdxGuest (
+  VOID
+  )
+{
+  return FALSE;
+}
+
+
+/**
+  Reads an 8-bit I/O port.
+
+  TDVMCALL_IO is invoked to read I/O port.
+
+  @param  Port  The I/O port to read.
+
+  @return The value read.
+
+**/
+UINT8
+EFIAPI
+TdIoRead8 (
+  IN      UINTN                     Port
+  )
+{
+  ASSERT (FALSE);
+  return 0;
+}
+
+/**
+  Reads a 16-bit I/O port.
+
+  TDVMCALL_IO is invoked to write I/O port.
+
+  @param  Port  The I/O port to read.
+
+  @return The value read.
+
+**/
+UINT16
+EFIAPI
+TdIoRead16 (
+  IN      UINTN                     Port
+  )
+{
+  ASSERT (FALSE);
+  return 0;
+}
+
+/**
+  Reads a 32-bit I/O port.
+
+  TDVMCALL_IO is invoked to read I/O port.
+
+  @param  Port  The I/O port to read.
+
+  @return The value read.
+
+**/
+UINT32
+EFIAPI
+TdIoRead32 (
+  IN      UINTN                     Port
+  )
+{
+  ASSERT (FALSE);
+  return 0;
+}
+
+/**
+  Writes an 8-bit I/O port.
+
+  TDVMCALL_IO is invoked to write I/O port.
+
+  @param  Port  The I/O port to write.
+  @param  Value The value to write to the I/O port.
+
+  @return The value written the I/O port.
+
+**/
+UINT8
+EFIAPI
+TdIoWrite8 (
+  IN      UINTN                     Port,
+  IN      UINT8                     Value
+  )
+{
+  ASSERT (FALSE);
+  return 0;
+}
+
+/**
+  Writes a 16-bit I/O port.
+
+  TDVMCALL_IO is invoked to write I/O port.
+
+  @param  Port  The I/O port to write.
+  @param  Value The value to write to the I/O port.
+
+  @return The value written the I/O port.
+
+**/
+UINT16
+EFIAPI
+TdIoWrite16 (
+  IN      UINTN                     Port,
+  IN      UINT16                    Value
+  )
+{
+  ASSERT (FALSE);
+  return 0;
+}
+
+/**
+  Writes a 32-bit I/O port.
+
+  TDVMCALL_IO is invoked to write I/O port.
+
+  @param  Port  The I/O port to write.
+  @param  Value The value to write to the I/O port.
+
+  @return The value written the I/O port.
+
+**/
+UINT32
+EFIAPI
+TdIoWrite32 (
+  IN      UINTN                     Port,
+  IN      UINT32                    Value
+  )
+{
+  ASSERT (FALSE);
+  return 0;
+}
+
+/**
+  Reads an 8-bit MMIO register.
+
+  TDVMCALL_MMIO is invoked to read MMIO registers.
+
+  @param  Address The MMIO register to read.
+
+  @return The value read.
+
+**/
+UINT8
+EFIAPI
+TdMmioRead8 (
+  IN      UINTN                     Address
+  )
+{
+  ASSERT (FALSE);
+  return 0;
+}
+
+/**
+  Writes an 8-bit MMIO register.
+
+  TDVMCALL_MMIO is invoked to read write registers.
+
+  @param  Address The MMIO register to write.
+  @param  Value   The value to write to the MMIO register.
+
+  @return Value.
+
+**/
+UINT8
+EFIAPI
+TdMmioWrite8 (
+  IN      UINTN                     Address,
+  IN      UINT8                     Val
+  )
+{
+  ASSERT (FALSE);
+  return 0;
+}
+
+/**
+  Reads a 16-bit MMIO register.
+
+  TDVMCALL_MMIO is invoked to read MMIO registers.
+
+  @param  Address The MMIO register to read.
+
+  @return The value read.
+
+**/
+UINT16
+EFIAPI
+TdMmioRead16 (
+  IN      UINTN                     Address
+  )
+{
+  ASSERT (FALSE);
+  return 0;
+}
+
+/**
+  Writes a 16-bit MMIO register.
+
+  TDVMCALL_MMIO is invoked to write MMIO registers.
+
+  @param  Address The MMIO register to write.
+  @param  Value   The value to write to the MMIO register.
+
+  @return Value.
+
+**/
+UINT16
+EFIAPI
+TdMmioWrite16 (
+  IN      UINTN                     Address,
+  IN      UINT16                    Val
+  )
+{
+  ASSERT (FALSE);
+  return 0;
+}
+
+/**
+  Reads a 32-bit MMIO register.
+
+  TDVMCALL_MMIO is invoked to read MMIO registers.
+
+  @param  Address The MMIO register to read.
+
+  @return The value read.
+
+**/
+UINT32
+EFIAPI
+TdMmioRead32 (
+  IN      UINTN                     Address
+  )
+{
+  ASSERT (FALSE);
+  return 0;
+}
+
+/**
+  Writes a 32-bit MMIO register.
+
+  TDVMCALL_MMIO is invoked to write MMIO registers.
+
+  @param  Address The MMIO register to write.
+  @param  Value   The value to write to the MMIO register.
+
+  @return Value.
+
+**/
+UINT32
+EFIAPI
+TdMmioWrite32 (
+  IN      UINTN                     Address,
+  IN      UINT32                    Val
+  )
+{
+  ASSERT (FALSE);
+  return 0;
+}
+
+/**
+  Reads a 64-bit MMIO register.
+
+  TDVMCALL_MMIO is invoked to read MMIO registers.
+
+  @param  Address The MMIO register to read.
+
+  @return The value read.
+
+**/
+UINT64
+EFIAPI
+TdMmioRead64 (
+  IN      UINTN                     Address
+  )
+{
+  ASSERT (FALSE);
+  return 0;
+}
+
+/**
+  Writes a 64-bit MMIO register.
+
+  TDVMCALL_MMIO is invoked to write MMIO registers.
+
+  @param  Address The MMIO register to write.
+  @param  Value   The value to write to the MMIO register.
+
+**/
+UINT64
+EFIAPI
+TdMmioWrite64 (
+  IN      UINTN                     Address,
+  IN      UINT64                    Value
+  )
+{
+  ASSERT (FALSE);
+  return 0;
+}
+
+/**
+  Reads an 8-bit I/O port fifo into a block of memory.
+
+  Reads the 8-bit I/O fifo port specified by Port.
+  The port is read Count times, and the read data is
+  stored in the provided Buffer.
+
+  This function must guarantee that all I/O read and write operations are
+  serialized.
+
+  If 8-bit I/O port operations are not supported, then ASSERT().
+
+  In TDX a serial of TdIoRead8 is invoked to read the I/O port fifo.
+
+  @param  Port    The I/O port to read.
+  @param  Count   The number of times to read I/O port.
+  @param  Buffer  The buffer to store the read data into.
+
+**/
+VOID
+EFIAPI
+TdIoReadFifo8 (
+  IN      UINTN                     Port,
+  IN      UINTN                     Count,
+  OUT     VOID                      *Buffer
+  )
+{
+  ASSERT (FALSE);
+}
+
+/**
+  Writes a block of memory into an 8-bit I/O port fifo.
+
+  Writes the 8-bit I/O fifo port specified by Port.
+  The port is written Count times, and the write data is
+  retrieved from the provided Buffer.
+
+  This function must guarantee that all I/O write and write operations are
+  serialized.
+
+  If 8-bit I/O port operations are not supported, then ASSERT().
+
+  In TDX a serial of TdIoWrite8 is invoked to write data to the I/O port.
+
+  @param  Port    The I/O port to write.
+  @param  Count   The number of times to write I/O port.
+  @param  Buffer  The buffer to retrieve the write data from.
+
+**/
+VOID
+EFIAPI
+TdIoWriteFifo8 (
+  IN      UINTN                     Port,
+  IN      UINTN                     Count,
+  IN      VOID                      *Buffer
+  )
+{
+  ASSERT (FALSE);
+}
+
+/**
+  Reads a 16-bit I/O port fifo into a block of memory.
+
+  Reads the 16-bit I/O fifo port specified by Port.
+  The port is read Count times, and the read data is
+  stored in the provided Buffer.
+
+  This function must guarantee that all I/O read and write operations are
+  serialized.
+
+  If 16-bit I/O port operations are not supported, then ASSERT().
+
+  In TDX a serial of TdIoRead16 is invoked to read data from the I/O port.
+
+  @param  Port    The I/O port to read.
+  @param  Count   The number of times to read I/O port.
+  @param  Buffer  The buffer to store the read data into.
+
+**/
+VOID
+EFIAPI
+TdIoReadFifo16 (
+  IN      UINTN                     Port,
+  IN      UINTN                     Count,
+  OUT     VOID                      *Buffer
+  )
+{
+  ASSERT (FALSE);
+}
+
+/**
+  Writes a block of memory into a 16-bit I/O port fifo.
+
+  Writes the 16-bit I/O fifo port specified by Port.
+  The port is written Count times, and the write data is
+  retrieved from the provided Buffer.
+
+  This function must guarantee that all I/O write and write operations are
+  serialized.
+
+  If 16-bit I/O port operations are not supported, then ASSERT().
+
+  In TDX a serial of TdIoWrite16 is invoked to write data to the I/O port.
+
+  @param  Port    The I/O port to write.
+  @param  Count   The number of times to write I/O port.
+  @param  Buffer  The buffer to retrieve the write data from.
+
+**/
+VOID
+EFIAPI
+TdIoWriteFifo16 (
+  IN      UINTN                     Port,
+  IN      UINTN                     Count,
+  IN      VOID                      *Buffer
+  )
+{
+  ASSERT (FALSE);
+}
+
+/**
+  Reads a 32-bit I/O port fifo into a block of memory.
+
+  Reads the 32-bit I/O fifo port specified by Port.
+  The port is read Count times, and the read data is
+  stored in the provided Buffer.
+
+  This function must guarantee that all I/O read and write operations are
+  serialized.
+
+  If 32-bit I/O port operations are not supported, then ASSERT().
+
+  In TDX a serial of TdIoRead32 is invoked to read data from the I/O port.
+
+  @param  Port    The I/O port to read.
+  @param  Count   The number of times to read I/O port.
+  @param  Buffer  The buffer to store the read data into.
+
+**/
+VOID
+EFIAPI
+TdIoReadFifo32 (
+  IN      UINTN                     Port,
+  IN      UINTN                     Count,
+  OUT     VOID                      *Buffer
+  )
+{
+  ASSERT (FALSE);
+}
+
+/**
+  Writes a block of memory into a 32-bit I/O port fifo.
+
+  Writes the 32-bit I/O fifo port specified by Port.
+  The port is written Count times, and the write data is
+  retrieved from the provided Buffer.
+
+  This function must guarantee that all I/O write and write operations are
+  serialized.
+
+  If 32-bit I/O port operations are not supported, then ASSERT().
+
+  In TDX a serial of TdIoWrite32 is invoked to write data to the I/O port.
+
+  @param  Port    The I/O port to write.
+  @param  Count   The number of times to write I/O port.
+  @param  Buffer  The buffer to retrieve the write data from.
+
+**/
+VOID
+EFIAPI
+TdIoWriteFifo32 (
+  IN      UINTN                     Port,
+  IN      UINTN                     Count,
+  IN      VOID                      *Buffer
+  )
+{
+  ASSERT (FALSE);
+}
diff --git a/MdePkg/Library/BaseIoLibIntrinsic/IoLibMsc.c b/MdePkg/Library/BaseIoLibIntrinsic/IoLibMsc.c
index d2bc5f527cf6..4d7945ae496f 100644
--- a/MdePkg/Library/BaseIoLibIntrinsic/IoLibMsc.c
+++ b/MdePkg/Library/BaseIoLibIntrinsic/IoLibMsc.c
@@ -16,6 +16,7 @@
 
 
 #include "BaseIoLibIntrinsicInternal.h"
+#include "IoLibTdx.h"
 
 //
 // Microsoft Visual Studio 7.1 Function Prototypes for I/O Intrinsics.
@@ -54,6 +55,8 @@ void          _ReadWriteBarrier (void);
 
   If 8-bit I/O port operations are not supported, then ASSERT().
 
+  For Td guest TDVMCALL_IO is invoked to read I/O port.
+
   @param  Port  The I/O port to read.
 
   @return The value read.
@@ -70,9 +73,13 @@ IoRead8 (
 
   Flag = FilterBeforeIoRead (FilterWidth8, Port, &Value);
   if (Flag) {
-    _ReadWriteBarrier ();
-    Value = (UINT8)_inp ((UINT16)Port);
-    _ReadWriteBarrier ();
+    if (IsTdxGuest ()) {
+      Value = TdIoRead8 (Port);
+    } else {
+      _ReadWriteBarrier ();
+      Value = (UINT8)_inp ((UINT16)Port);
+      _ReadWriteBarrier ();
+    }
   }
   FilterAfterIoRead (FilterWidth8, Port, &Value);
 
@@ -88,6 +95,8 @@ IoRead8 (
 
   If 8-bit I/O port operations are not supported, then ASSERT().
 
+  For Td guest TDVMCALL_IO is invoked to write I/O port.
+
   @param  Port  The I/O port to write.
   @param  Value The value to write to the I/O port.
 
@@ -105,9 +114,13 @@ IoWrite8 (
 
   Flag = FilterBeforeIoWrite(FilterWidth8, Port, &Value);
   if (Flag) {
-    _ReadWriteBarrier ();
-    (UINT8)_outp ((UINT16)Port, Value);
-    _ReadWriteBarrier ();
+    if (IsTdxGuest ()) {
+      TdIoWrite8 (Port, Value);
+    } else {
+      _ReadWriteBarrier ();
+      (UINT8)_outp ((UINT16)Port, Value);
+      _ReadWriteBarrier ();
+    }
   }
   FilterAfterIoWrite (FilterWidth8, Port, &Value);
 
@@ -124,6 +137,8 @@ IoWrite8 (
   If 16-bit I/O port operations are not supported, then ASSERT().
   If Port is not aligned on a 16-bit boundary, then ASSERT().
 
+  For Td guest TDVMCALL_IO is invoked to read I/O port.
+
   @param  Port  The I/O port to read.
 
   @return The value read.
@@ -142,9 +157,13 @@ IoRead16 (
 
   Flag = FilterBeforeIoRead (FilterWidth16, Port, &Value);
   if (Flag) {
-    _ReadWriteBarrier ();
-    Value = _inpw ((UINT16)Port);
-    _ReadWriteBarrier ();
+    if (IsTdxGuest ()) {
+      Value = TdIoRead16 (Port);
+    } else {
+      _ReadWriteBarrier ();
+      Value = _inpw ((UINT16)Port);
+      _ReadWriteBarrier ();
+    }
   }
   FilterBeforeIoRead (FilterWidth16, Port, &Value);
 
@@ -161,6 +180,8 @@ IoRead16 (
   If 16-bit I/O port operations are not supported, then ASSERT().
   If Port is not aligned on a 16-bit boundary, then ASSERT().
 
+  For Td guest TDVMCALL_IO is invoked to write I/O port.
+
   @param  Port  The I/O port to write.
   @param  Value The value to write to the I/O port.
 
@@ -180,9 +201,13 @@ IoWrite16 (
 
   Flag = FilterBeforeIoWrite(FilterWidth16, Port, &Value);
   if (Flag) {
-    _ReadWriteBarrier ();
-    _outpw ((UINT16)Port, Value);
-    _ReadWriteBarrier ();
+    if (IsTdxGuest ()) {
+      TdIoWrite16 (Port, Value);
+    } else {
+      _ReadWriteBarrier ();
+      _outpw ((UINT16)Port, Value);
+      _ReadWriteBarrier ();
+    }
   }
   FilterAfterIoWrite (FilterWidth16, Port, &Value);
 
@@ -199,6 +224,8 @@ IoWrite16 (
   If 32-bit I/O port operations are not supported, then ASSERT().
   If Port is not aligned on a 32-bit boundary, then ASSERT().
 
+  For Td guest TDVMCALL_IO is invoked to read I/O port.
+
   @param  Port  The I/O port to read.
 
   @return The value read.
@@ -217,9 +244,13 @@ IoRead32 (
 
   Flag = FilterBeforeIoRead(FilterWidth32, Port, &Value);
   if (Flag) {
-    _ReadWriteBarrier ();
-    Value = _inpd ((UINT16)Port);
-    _ReadWriteBarrier ();
+    if (IsTdxGuest ()) {
+      Value = TdIoRead32 (Port);
+    } else {
+      _ReadWriteBarrier ();
+      Value = _inpd ((UINT16)Port);
+      _ReadWriteBarrier ();
+    }
   }
   FilterAfterIoRead (FilterWidth32, Port, &Value);
 
@@ -236,6 +267,8 @@ IoRead32 (
   If 32-bit I/O port operations are not supported, then ASSERT().
   If Port is not aligned on a 32-bit boundary, then ASSERT().
 
+  For Td guest TDVMCALL_IO is invoked to write I/O port.
+
   @param  Port  The I/O port to write.
   @param  Value The value to write to the I/O port.
 
@@ -255,9 +288,13 @@ IoWrite32 (
 
   Flag = FilterBeforeIoWrite(FilterWidth32, Port, &Value);
   if (Flag) {
-    _ReadWriteBarrier ();
-    _outpd ((UINT16)Port, Value);
-    _ReadWriteBarrier ();
+    if (IsTdxGuest ()) {
+      TdIoWrite32 (Port, Value);
+    } else {
+      _ReadWriteBarrier ();
+      _outpd ((UINT16)Port, Value);
+      _ReadWriteBarrier ();
+    }
   }
   FilterAfterIoWrite (FilterWidth32, Port, &Value);
 
diff --git a/MdePkg/Library/BaseIoLibIntrinsic/IoLibTdx.h b/MdePkg/Library/BaseIoLibIntrinsic/IoLibTdx.h
new file mode 100644
index 000000000000..3aad197d3b39
--- /dev/null
+++ b/MdePkg/Library/BaseIoLibIntrinsic/IoLibTdx.h
@@ -0,0 +1,411 @@
+/** @file
+  Header file for Tdx IO library.
+
+  Copyright (c) 2020 - 2021, Intel Corporation. All rights reserved.<BR>
+   SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#ifndef IOLIB_TDX_H_
+#define IOLIB_TDX_H_
+
+/**
+  Check if it is Tdx guest.
+
+  @return TRUE    It is Tdx guest
+  @return FALSE   It is not Tdx guest
+
+**/
+BOOLEAN
+EFIAPI
+IsTdxGuest (
+  VOID
+  );
+
+
+/**
+  Reads an 8-bit I/O port.
+
+  TDVMCALL_IO is invoked to read I/O port.
+
+  @param  Port  The I/O port to read.
+
+  @return The value read.
+
+**/
+UINT8
+EFIAPI
+TdIoRead8 (
+  IN      UINTN                     Port
+  );
+
+/**
+  Reads a 16-bit I/O port.
+
+  TDVMCALL_IO is invoked to write I/O port.
+
+  @param  Port  The I/O port to read.
+
+  @return The value read.
+
+**/
+UINT16
+EFIAPI
+TdIoRead16 (
+  IN      UINTN                     Port
+  );
+
+/**
+  Reads a 32-bit I/O port.
+
+  TDVMCALL_IO is invoked to read I/O port.
+
+  @param  Port  The I/O port to read.
+
+  @return The value read.
+
+**/
+UINT32
+EFIAPI
+TdIoRead32 (
+  IN      UINTN                     Port
+  );
+
+/**
+  Writes an 8-bit I/O port.
+
+  TDVMCALL_IO is invoked to write I/O port.
+
+  @param  Port  The I/O port to write.
+  @param  Value The value to write to the I/O port.
+
+  @return The value written the I/O port.
+
+**/
+UINT8
+EFIAPI
+TdIoWrite8 (
+  IN      UINTN                     Port,
+  IN      UINT8                     Value
+  );
+
+/**
+  Writes a 16-bit I/O port.
+
+  TDVMCALL_IO is invoked to write I/O port.
+
+  @param  Port  The I/O port to write.
+  @param  Value The value to write to the I/O port.
+
+  @return The value written the I/O port.
+
+**/
+UINT16
+EFIAPI
+TdIoWrite16 (
+  IN      UINTN                     Port,
+  IN      UINT16                    Value
+  );
+
+/**
+  Writes a 32-bit I/O port.
+
+  TDVMCALL_IO is invoked to write I/O port.
+
+  @param  Port  The I/O port to write.
+  @param  Value The value to write to the I/O port.
+
+  @return The value written the I/O port.
+
+**/
+UINT32
+EFIAPI
+TdIoWrite32 (
+  IN      UINTN                     Port,
+  IN      UINT32                    Value
+  );
+
+/**
+  Reads an 8-bit MMIO register.
+
+  TDVMCALL_MMIO is invoked to read MMIO registers.
+
+  @param  Address The MMIO register to read.
+
+  @return The value read.
+
+**/
+UINT8
+EFIAPI
+TdMmioRead8 (
+  IN      UINTN                     Address
+  );
+
+/**
+  Writes an 8-bit MMIO register.
+
+  TDVMCALL_MMIO is invoked to read write registers.
+
+  @param  Address The MMIO register to write.
+  @param  Value   The value to write to the MMIO register.
+
+  @return Value.
+
+**/
+UINT8
+EFIAPI
+TdMmioWrite8 (
+  IN      UINTN                     Address,
+  IN      UINT8                     Val
+  );
+
+/**
+  Reads a 16-bit MMIO register.
+
+  TDVMCALL_MMIO is invoked to read MMIO registers.
+
+  @param  Address The MMIO register to read.
+
+  @return The value read.
+
+**/
+UINT16
+EFIAPI
+TdMmioRead16 (
+  IN      UINTN                     Address
+  );
+
+/**
+  Writes a 16-bit MMIO register.
+
+  TDVMCALL_MMIO is invoked to write MMIO registers.
+
+  @param  Address The MMIO register to write.
+  @param  Value   The value to write to the MMIO register.
+
+  @return Value.
+
+**/
+UINT16
+EFIAPI
+TdMmioWrite16 (
+  IN      UINTN                     Address,
+  IN      UINT16                    Val
+  );
+
+/**
+  Reads a 32-bit MMIO register.
+
+  TDVMCALL_MMIO is invoked to read MMIO registers.
+
+  @param  Address The MMIO register to read.
+
+  @return The value read.
+
+**/
+UINT32
+EFIAPI
+TdMmioRead32 (
+  IN      UINTN                     Address
+  );
+
+/**
+  Writes a 32-bit MMIO register.
+
+  TDVMCALL_MMIO is invoked to write MMIO registers.
+
+  @param  Address The MMIO register to write.
+  @param  Value   The value to write to the MMIO register.
+
+  @return Value.
+
+**/
+UINT32
+EFIAPI
+TdMmioWrite32 (
+  IN      UINTN                     Address,
+  IN      UINT32                    Val
+  );
+
+/**
+  Reads a 64-bit MMIO register.
+
+  TDVMCALL_MMIO is invoked to read MMIO registers.
+
+  @param  Address The MMIO register to read.
+
+  @return The value read.
+
+**/
+UINT64
+EFIAPI
+TdMmioRead64 (
+  IN      UINTN                     Address
+  );
+
+/**
+  Writes a 64-bit MMIO register.
+
+  TDVMCALL_MMIO is invoked to write MMIO registers.
+
+  @param  Address The MMIO register to write.
+  @param  Value   The value to write to the MMIO register.
+
+**/
+UINT64
+EFIAPI
+TdMmioWrite64 (
+  IN      UINTN                     Address,
+  IN      UINT64                    Value
+  );
+
+/**
+  Reads an 8-bit I/O port fifo into a block of memory in Tdx.
+
+  Reads the 8-bit I/O fifo port specified by Port.
+  The port is read Count times, and the read data is
+  stored in the provided Buffer.
+
+  This function must guarantee that all I/O read and write operations are
+  serialized.
+
+  If 8-bit I/O port operations are not supported, then ASSERT().
+
+  @param  Port    The I/O port to read.
+  @param  Count   The number of times to read I/O port.
+  @param  Buffer  The buffer to store the read data into.
+
+**/
+VOID
+EFIAPI
+TdIoReadFifo8 (
+  IN      UINTN                     Port,
+  IN      UINTN                     Count,
+  OUT     VOID                      *Buffer
+  );
+
+/**
+  Writes a block of memory into an 8-bit I/O port fifo in Tdx.
+
+  Writes the 8-bit I/O fifo port specified by Port.
+  The port is written Count times, and the write data is
+  retrieved from the provided Buffer.
+
+  This function must guarantee that all I/O write and write operations are
+  serialized.
+
+  If 8-bit I/O port operations are not supported, then ASSERT().
+
+  @param  Port    The I/O port to write.
+  @param  Count   The number of times to write I/O port.
+  @param  Buffer  The buffer to retrieve the write data from.
+
+**/
+VOID
+EFIAPI
+TdIoWriteFifo8 (
+  IN      UINTN                     Port,
+  IN      UINTN                     Count,
+  IN      VOID                      *Buffer
+  );
+
+/**
+  Reads a 16-bit I/O port fifo into a block of memory in Tdx.
+
+  Reads the 16-bit I/O fifo port specified by Port.
+  The port is read Count times, and the read data is
+  stored in the provided Buffer.
+
+  This function must guarantee that all I/O read and write operations are
+  serialized.
+
+  If 16-bit I/O port operations are not supported, then ASSERT().
+
+  @param  Port    The I/O port to read.
+  @param  Count   The number of times to read I/O port.
+  @param  Buffer  The buffer to store the read data into.
+
+**/
+VOID
+EFIAPI
+TdIoReadFifo16 (
+  IN      UINTN                     Port,
+  IN      UINTN                     Count,
+  OUT     VOID                      *Buffer
+  );
+
+/**
+  Writes a block of memory into a 16-bit I/O port fifo in Tdx.
+
+  Writes the 16-bit I/O fifo port specified by Port.
+  The port is written Count times, and the write data is
+  retrieved from the provided Buffer.
+
+  This function must guarantee that all I/O write and write operations are
+  serialized.
+
+  If 16-bit I/O port operations are not supported, then ASSERT().
+
+  @param  Port    The I/O port to write.
+  @param  Count   The number of times to write I/O port.
+  @param  Buffer  The buffer to retrieve the write data from.
+
+**/
+VOID
+EFIAPI
+TdIoWriteFifo16 (
+  IN      UINTN                     Port,
+  IN      UINTN                     Count,
+  IN      VOID                      *Buffer
+  );
+
+/**
+  Reads a 32-bit I/O port fifo into a block of memory in Tdx.
+
+  Reads the 32-bit I/O fifo port specified by Port.
+  The port is read Count times, and the read data is
+  stored in the provided Buffer.
+
+  This function must guarantee that all I/O read and write operations are
+  serialized.
+
+  If 32-bit I/O port operations are not supported, then ASSERT().
+
+  @param  Port    The I/O port to read.
+  @param  Count   The number of times to read I/O port.
+  @param  Buffer  The buffer to store the read data into.
+
+**/
+VOID
+EFIAPI
+TdIoReadFifo32 (
+  IN      UINTN                     Port,
+  IN      UINTN                     Count,
+  OUT     VOID                      *Buffer
+  );
+
+/**
+  Writes a block of memory into a 32-bit I/O port fifo in Tdx.
+
+  Writes the 32-bit I/O fifo port specified by Port.
+  The port is written Count times, and the write data is
+  retrieved from the provided Buffer.
+
+  This function must guarantee that all I/O write and write operations are
+  serialized.
+
+  If 32-bit I/O port operations are not supported, then ASSERT().
+
+  @param  Port    The I/O port to write.
+  @param  Count   The number of times to write I/O port.
+  @param  Buffer  The buffer to retrieve the write data from.
+
+**/
+VOID
+EFIAPI
+TdIoWriteFifo32 (
+  IN      UINTN                     Port,
+  IN      UINTN                     Count,
+  IN      VOID                      *Buffer
+  );
+
+#endif
diff --git a/MdePkg/Library/BaseIoLibIntrinsic/X64/IoFifoSev.nasm b/MdePkg/Library/BaseIoLibIntrinsic/X64/IoFifoSev.nasm
index 106f8881c55c..7f426495bbe3 100644
--- a/MdePkg/Library/BaseIoLibIntrinsic/X64/IoFifoSev.nasm
+++ b/MdePkg/Library/BaseIoLibIntrinsic/X64/IoFifoSev.nasm
@@ -10,6 +10,79 @@
     DEFAULT REL
     SECTION .text
 
+extern ASM_PFX(TdIoReadFifo8)
+extern ASM_PFX(TdIoReadFifo16)
+extern ASM_PFX(TdIoReadFifo32)
+extern ASM_PFX(TdIoWriteFifo8)
+extern ASM_PFX(TdIoWriteFifo16)
+extern ASM_PFX(TdIoWriteFifo32)
+
+;------------------------------------------------------------------------------
+; Check whether we need to unroll the String I/O in Tdx guest
+;
+; Return // eax   (1 - unroll, 0 - no unroll)
+;------------------------------------------------------------------------------
+global ASM_PFX(TdxNoRepIo)
+ASM_PFX(TdxNoRepIo):
+  ; CPUID clobbers ebx, ecx and edx
+  push      rbx
+  push      rcx
+  push      rdx
+
+  ;
+  ; CPUID (0)
+  ;
+  mov     eax, 0
+  cpuid
+  cmp     ebx, 0x756e6547  ; "Genu"
+  jne     @NoTdx
+  cmp     edx, 0x49656e69  ; "ineI"
+  jne     @NoTdx
+  cmp     ecx, 0x6c65746e  ; "ntel"
+  jne     @NoTdx
+
+  ;
+  ; CPUID (1)
+  ;
+  mov     eax, 1
+  cpuid
+  test    ecx, 0x80000000
+  jz      @NoTdx
+
+  ;
+  ; CPUID[0].EAX >= 0x21?
+  ;
+  mov     eax, 0
+  cpuid
+  cmp     eax, 0x21
+  jl      @NoTdx
+
+  ;
+  ; CPUID (0x21,0)
+  ;
+  mov     eax, 0x21
+  mov     ecx, 0
+  cpuid
+
+  cmp     ebx, 0x65746E49   ; "Inte"
+  jne     @NoTdx
+  cmp     edx, 0x5844546C   ; "lTDX"
+  jne     @NoTdx
+  cmp     ecx, 0x20202020   ; "    "
+  jne     @NoTdx
+
+  mov     rax, 1
+  jmp     @ExitTdxNoRepIo
+
+@NoTdx:
+  mov     rax, 0
+
+@ExitTdxNoRepIo:
+  pop     rdx
+  pop     rcx
+  pop     rbx
+  ret
+
 ;------------------------------------------------------------------------------
 ; Check whether we need to unroll the String I/O in SEV guest
 ;
@@ -75,6 +148,16 @@ ASM_PFX(SevNoRepIo):
 ;------------------------------------------------------------------------------
 global ASM_PFX(IoReadFifo8)
 ASM_PFX(IoReadFifo8):
+    call    ASM_PFX(TdxNoRepIo)
+    test    rax, rax
+    jz      @NonTd_IoReadFifo8
+
+    sub     rsp, 020h
+    call    TdIoReadFifo8
+    add     rsp, 020h
+    ret
+
+@NonTd_IoReadFifo8:
     xchg    rcx, rdx
     xchg    rdi, r8             ; rdi: buffer address; r8: save rdi
 
@@ -111,6 +194,16 @@ ASM_PFX(IoReadFifo8):
 ;------------------------------------------------------------------------------
 global ASM_PFX(IoReadFifo16)
 ASM_PFX(IoReadFifo16):
+    call    ASM_PFX(TdxNoRepIo)
+    test    rax, rax
+    jz      @NonTd_IoReadFifo16
+
+    sub     rsp, 020h
+    call    TdIoReadFifo16
+    add     rsp, 020h
+    ret
+
+@NonTd_IoReadFifo16:
     xchg    rcx, rdx
     xchg    rdi, r8             ; rdi: buffer address; r8: save rdi
 
@@ -147,6 +240,16 @@ ASM_PFX(IoReadFifo16):
 ;------------------------------------------------------------------------------
 global ASM_PFX(IoReadFifo32)
 ASM_PFX(IoReadFifo32):
+    call    ASM_PFX(TdxNoRepIo)
+    test    rax, rax
+    jz      @NonTd_IoReadFifo32
+
+    sub     rsp, 020h
+    call    TdIoReadFifo32
+    add     rsp, 020h
+    ret
+
+@NonTd_IoReadFifo32:
     xchg    rcx, rdx
     xchg    rdi, r8             ; rdi: buffer address; r8: save rdi
 
@@ -183,6 +286,16 @@ ASM_PFX(IoReadFifo32):
 ;------------------------------------------------------------------------------
 global ASM_PFX(IoWriteFifo8)
 ASM_PFX(IoWriteFifo8):
+    call    ASM_PFX(TdxNoRepIo)
+    test    rax, rax
+    jz      @NonTd_IoWriteFifo8
+
+    sub     rsp, 020h
+    call    TdIoWriteFifo8
+    add     rsp, 020h
+    ret
+
+@NonTd_IoWriteFifo8:
     xchg    rcx, rdx
     xchg    rsi, r8             ; rsi: buffer address; r8: save rsi
 
@@ -219,6 +332,16 @@ ASM_PFX(IoWriteFifo8):
 ;------------------------------------------------------------------------------
 global ASM_PFX(IoWriteFifo16)
 ASM_PFX(IoWriteFifo16):
+    call    ASM_PFX(TdxNoRepIo)
+    test    rax, rax
+    jz      @NonTd_IoWriteFifo16
+
+    sub     rsp, 020h
+    call    TdIoWriteFifo16
+    add     rsp, 020h
+    ret
+
+@NonTd_IoWriteFifo16:
     xchg    rcx, rdx
     xchg    rsi, r8             ; rsi: buffer address; r8: save rsi
 
@@ -255,6 +378,16 @@ ASM_PFX(IoWriteFifo16):
 ;------------------------------------------------------------------------------
 global ASM_PFX(IoWriteFifo32)
 ASM_PFX(IoWriteFifo32):
+    call    ASM_PFX(TdxNoRepIo)
+    test    rax, rax
+    jz      @NonTd_IoWriteFifo32
+
+    sub     rsp, 020h
+    call    TdIoWriteFifo32
+    add     rsp, 020h
+    ret
+
+@NonTd_IoWriteFifo32:
     xchg    rcx, rdx
     xchg    rsi, r8             ; rsi: buffer address; r8: save rsi
 
-- 
2.29.2.windows.2



-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#79163): https://edk2.groups.io/g/devel/message/79163
Mute This Topic: https://groups.io/mt/84837896/1787277
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [importer@patchew.org]
-=-=-=-=-=-=-=-=-=-=-=-


Re: [edk2-devel] [PATCH 07/23] MdePkg: Update BaseIoLibIntrinsicSev to support Tdx
Posted by Gerd Hoffmann 4 years, 5 months ago
  Hi,

> In the I/O functions of above files, if IsTdxGuest() returns TRUE, then
> Td I/O routine is called, otherwise the legacy I/O routine is called.
> Td I/O routines are declared in IoLibTdx.h and implemented in
> IoLibInternalTdx.c.

Sorry, I'm a bit late to the party, but what is the overall long plan
here?

IIRC some of the TDX features require a separate firmware binary.  So,
if we need a separate binary anyway at some point in the future, isn't
it simpler then to use a separate firmware binary right from the start?

You can simply add a Tdx-specific variant of the library
(BaseIoLibIntrinsicTdx.inf) and switch at compile time instead of
having runtime switches all over the place.

take care,
  Gerd



-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#79414): https://edk2.groups.io/g/devel/message/79414
Mute This Topic: https://groups.io/mt/84837896/1787277
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [importer@patchew.org]
-=-=-=-=-=-=-=-=-=-=-=-


Re: [edk2-devel] [PATCH 07/23] MdePkg: Update BaseIoLibIntrinsicSev to support Tdx
Posted by Min Xu 4 years, 5 months ago
On August 17, 2021 4:38 PM, Gerd Hoffmann wrote:
> 
>   Hi,
> 
> > In the I/O functions of above files, if IsTdxGuest() returns TRUE,
> > then Td I/O routine is called, otherwise the legacy I/O routine is called.
> > Td I/O routines are declared in IoLibTdx.h and implemented in
> > IoLibInternalTdx.c.
> 
> Sorry, I'm a bit late to the party, but what is the overall long plan here?
>
Yes there are discussions about the TDVF (Trust Domain Virtual Firmware).
https://edk2.groups.io/g/devel/topic/83283616#76022
The design slides and recorded meeting are in below link:
https://edk2.groups.io/g/devel/files/Designs/2021/0611

> 
> IIRC some of the TDX features require a separate firmware binary.  So, if we
> need a separate binary anyway at some point in the future, isn't it simpler then
> to use a separate firmware binary right from the start?
> 
> You can simply add a Tdx-specific variant of the library
> (BaseIoLibIntrinsicTdx.inf) and switch at compile time instead of having runtime
> switches all over the place.
> 
TDVF has 2 Config for upstream. See https://edk2.groups.io/g/devel/message/76367
Config-A merge the *basic* TDVF features to existing OvmfX64Pkg.dsc. (Align with existing SEV).
OvmfX64Pkg.dsc includes SEV/TDX/normal OVMF basic boot capability. The final binary can run on SEV/TDX/normal OVMF
So we have to probe the Td guest in run-time and switch to the corresponding I/O routine.
The solution of using a separate firmware binary is not feasible in this situation.

Thanks.
Min


-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#79469): https://edk2.groups.io/g/devel/message/79469
Mute This Topic: https://groups.io/mt/84837896/1787277
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [importer@patchew.org]
-=-=-=-=-=-=-=-=-=-=-=-


Re: [edk2-devel] [PATCH 07/23] MdePkg: Update BaseIoLibIntrinsicSev to support Tdx
Posted by Gerd Hoffmann 4 years, 5 months ago
  Hi,

> > IIRC some of the TDX features require a separate firmware binary.  So, if we
> > need a separate binary anyway at some point in the future, isn't it simpler then
> > to use a separate firmware binary right from the start?
> > 
> > You can simply add a Tdx-specific variant of the library
> > (BaseIoLibIntrinsicTdx.inf) and switch at compile time instead of having runtime
> > switches all over the place.
> > 
> TDVF has 2 Config for upstream. See https://edk2.groups.io/g/devel/message/76367
> Config-A merge the *basic* TDVF features to existing OvmfX64Pkg.dsc. (Align with existing SEV).

Hmm, so we'll have two variants with two feature sets.

One more question:  How does this align with the WorkArea changes posted
yesterday?  The WorkArea gets a mode field for SEV / TDX / normal, so
I think you should be able to use that instead of invoking cpuid each
time you need to know whenever tdx is active or not.

take care,
  Gerd



-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#79550): https://edk2.groups.io/g/devel/message/79550
Mute This Topic: https://groups.io/mt/84837896/1787277
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [importer@patchew.org]
-=-=-=-=-=-=-=-=-=-=-=-


Re: [edk2-devel] [PATCH 07/23] MdePkg: Update BaseIoLibIntrinsicSev to support Tdx
Posted by Min Xu 4 years, 5 months ago
On August 19, 2021 2:31 PM, Gerd Hoffmann wrote:
> > > IIRC some of the TDX features require a separate firmware binary.
> > > So, if we need a separate binary anyway at some point in the future,
> > > isn't it simpler then to use a separate firmware binary right from the start?
> > >
> > > You can simply add a Tdx-specific variant of the library
> > > (BaseIoLibIntrinsicTdx.inf) and switch at compile time instead of
> > > having runtime switches all over the place.
> > >
> > TDVF has 2 Config for upstream. See
> > https://edk2.groups.io/g/devel/message/76367
> > Config-A merge the *basic* TDVF features to existing OvmfX64Pkg.dsc.
> (Align with existing SEV).
> 
> Hmm, so we'll have two variants with two feature sets.
> 
> One more question:  How does this align with the WorkArea changes posted
> yesterday?  The WorkArea gets a mode field for SEV / TDX / normal, so I think
> you should be able to use that instead of invoking cpuid each time you need
> to know whenever tdx is active or not.
We don't want to make the TdxProbeLib depend on the WorkArea. CPUID(0x21)
makes TdxProbeLib less dependency.  For Intel TDX the WorkArea is designed to
be used in ResetVector phase.
> 
Thanks!
Min


-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#79571): https://edk2.groups.io/g/devel/message/79571
Mute This Topic: https://groups.io/mt/84837896/1787277
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [importer@patchew.org]
-=-=-=-=-=-=-=-=-=-=-=-


Re: [edk2-devel] [PATCH 07/23] MdePkg: Update BaseIoLibIntrinsicSev to support Tdx
Posted by Gerd Hoffmann 4 years, 5 months ago
  Hi,

> > One more question:  How does this align with the WorkArea changes posted
> > yesterday?  The WorkArea gets a mode field for SEV / TDX / normal, so I think
> > you should be able to use that instead of invoking cpuid each time you need
> > to know whenever tdx is active or not.
> We don't want to make the TdxProbeLib depend on the WorkArea.

Why?

> CPUID(0x21) makes TdxProbeLib less dependency.  For Intel TDX the
> WorkArea is designed to be used in ResetVector phase.

I don't see why the dependency is a problem.  TDX-enabled builds need
both TdxProbeLib / TdxLib and WorkArea anyway.

Each cpuid instruction is a vmexit, and the code invokes cpuid twice for
each io / mmio access.

take care,
  Gerd



-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#79625): https://edk2.groups.io/g/devel/message/79625
Mute This Topic: https://groups.io/mt/84837896/1787277
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [importer@patchew.org]
-=-=-=-=-=-=-=-=-=-=-=-


Re: [edk2-devel] [PATCH 07/23] MdePkg: Update BaseIoLibIntrinsicSev to support Tdx
Posted by Erdem Aktas via groups.io 4 years, 5 months ago
On Thu, Aug 12, 2021 at 2:57 PM Min Xu <min.m.xu@intel.com> wrote:
 > +UINT8
> +EFIAPI
> +TdMmioRead8 (
> +  IN      UINTN                     Address
> +  )
> +{
> +  UINT64                             Value;
> +  UINT64                             Status;
> +
> +  Address |= TdSharedPageMask ();

Why is the SharedBit set? VMM does not care if the sharedbit is set.
Actually it should not even be aware of it.
> +  MemoryFence ();
> +  Status = TdVmCall (TDVMCALL_MMIO, TDVMCALL_ACCESS_SIZE_1, TDVMCALL_ACCESS_READ, Address, 0, &Value);
> +  if (Status != 0) {
So for some reason, MMIO read fails, we are doing a memory read. Why
should an MMIO read fail? Could you elaborate which use case this
covers?


-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#80524): https://edk2.groups.io/g/devel/message/80524
Mute This Topic: https://groups.io/mt/84837896/1787277
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [importer@patchew.org]
-=-=-=-=-=-=-=-=-=-=-=-


Re: [edk2-devel] [PATCH 07/23] MdePkg: Update BaseIoLibIntrinsicSev to support Tdx
Posted by Min Xu 4 years, 4 months ago
On September 11, 2021 9:16 AM, Erdem Aktas wrote:
> 
> On Thu, Aug 12, 2021 at 2:57 PM Min Xu <min.m.xu@intel.com> wrote:
>  > +UINT8
> > +EFIAPI
> > +TdMmioRead8 (
> > +  IN      UINTN                     Address
> > +  )
> > +{
> > +  UINT64                             Value;
> > +  UINT64                             Status;
> > +
> > +  Address |= TdSharedPageMask ();
> 
> Why is the SharedBit set? VMM does not care if the sharedbit is set.
> Actually it should not even be aware of it.
>
That is because GPA for MMIO region that VMM emulates must be shared region.  i.e. shared bit must be set.
See Section 12.3 in below link.
https://software.intel.com/content/dam/develop/external/us/en/documents/tdx-module-1.0-public-spec-v0.931.pdf
>
> > +  MemoryFence ();
> > +  Status = TdVmCall (TDVMCALL_MMIO, TDVMCALL_ACCESS_SIZE_1,
> > + TDVMCALL_ACCESS_READ, Address, 0, &Value);  if (Status != 0) {
> So for some reason, MMIO read fails, we are doing a memory read. Why
> should an MMIO read fail? Could you elaborate which use case this covers?
If invalid operands provided by TD, e.g., MMIO address, TDG.VP.VMCALL_INVALID_OPERAND is returned.
See Section 3.7 Table 3-21 in below link:
https://software.intel.com/content/dam/develop/external/us/en/documents/intel-tdx-guest-hypervisor-communication-interface-1.0-344426-002.pdf
> 
Thanks!
Min


-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#81207): https://edk2.groups.io/g/devel/message/81207
Mute This Topic: https://groups.io/mt/84837896/1787277
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [importer@patchew.org]
-=-=-=-=-=-=-=-=-=-=-=-