public inbox for devel@edk2.groups.io
 help / color / mirror / Atom feed
* [RFC PATCH 1/1] OvmfPkg/QemuFlash: Fix Runtime variable access when SEV is enabled
@ 2018-06-26 19:39 Brijesh Singh
  0 siblings, 0 replies; 15+ messages in thread
From: Brijesh Singh @ 2018-06-26 19:39 UTC (permalink / raw)
  To: edk2-devel; +Cc: Tom Lendacky, Brijesh Singh, Laszlo Ersek

Problem statement:
------------------
Fedora-28 contains 4.16 kernel -- which has all the required support to
run as an SEV guest.  When the installer is launched from SEV guest then
it fails to install the bootloader. The installer was failing to update
the 'BootOrder' UEFI runtime variable.

Root Cause Analysis
--------------------
Since QemuFlash storage memory is accessed by both guest and hypervisor
hence we need to map this memory range as unencrypted. AmdSevDxe maps the
range as "unencrypted" but later FtwNotificationEvent() in
MdeModule/Universal/Variable/RuntimeDxe/VariableDxe.c resets the mapping
and the memory region gets remapped as "encrypted". After that, any access
to the flash will end up going through the encryption engine. I did try
hacking EDK2 to restore the C-bit but that was not sufficient because UEFI
runtime services are mapped as "encrypted" in OS page table hence we end up
accessing the flash as encrypted when OS requests to update the variables.

A possible solution
---------------------
To solve the issue, after QemuFlash is probed, I allocate an encrypted
buffer and initialize this buffer with the contents from the flash memory.
When SEV is enabled, we use newly allocated encrypted buffer in
FwInstance->FvBase instead of the original flash region. The idea is if
caller grabs the FwInstance->FvBase pointer and tries to access the
FvVolumeHeader then it should get the data from the encrypted buffer.
But if caller wants read/writes to/from the flash device then we internally
use the original "unencrypted" flash region to access the data. With this
patch, I have verified that OS is able to update the runtime variable and
FC-28 installer is successfully able to complete the installation process.

If you all agree with approach then I can rework any feedbacks and remove
the rfc tag from the patch. If you have better suggestions then I am open
to explore those as well.

Cc: Laszlo Ersek <lersek@redhat.com>
Contributed-under: TianoCore Contribution Agreement 1.1
Signed-off-by: Brijesh Singh <brijesh.singh@amd.com>
---
 .../FvbServicesRuntimeDxe.inf                      |  1 +
 .../FwBlockService.c                               | 37 +++++++++++++++++++---
 2 files changed, 34 insertions(+), 4 deletions(-)

diff --git a/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FvbServicesRuntimeDxe.inf b/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FvbServicesRuntimeDxe.inf
index d7b4ec06c4e6..6bb5c2093790 100644
--- a/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FvbServicesRuntimeDxe.inf
+++ b/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FvbServicesRuntimeDxe.inf
@@ -54,6 +54,7 @@ [LibraryClasses]
   DevicePathLib
   DxeServicesTableLib
   MemoryAllocationLib
+  MemEncryptSevLib
   PcdLib
   UefiBootServicesTableLib
   UefiDriverEntryPoint
diff --git a/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FwBlockService.c b/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FwBlockService.c
index 558b395dff4a..e82b4ff70961 100644
--- a/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FwBlockService.c
+++ b/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FwBlockService.c
@@ -36,6 +36,7 @@
 #include <Library/DxeServicesTableLib.h>
 #include <Library/MemoryAllocationLib.h>
 #include <Library/UefiBootServicesTableLib.h>
+#include <Library/MemEncryptSevLib.h>
 
 #include "FwBlockService.h"
 #include "QemuFlash.h"
@@ -966,6 +967,7 @@ FvbInitialize (
   UINTN                               Length;
   UINTN                               NumOfBlocks;
   RETURN_STATUS                       PcdStatus;
+  EFI_PHYSICAL_ADDRESS                CryptedAddress;
 
   if (EFI_ERROR (QemuFlashInitialize ())) {
     //
@@ -986,6 +988,24 @@ FvbInitialize (
   BaseAddress = (UINTN) PcdGet32 (PcdOvmfFdBaseAddress);
   Length = PcdGet32 (PcdOvmfFirmwareFdSize);
 
+  //
+  // When SEV is enabled, allocate a encrypted buffer which will contain a
+  // encrypted copy of the Flash image.
+  //
+  if (MemEncryptSevIsEnabled ()) {
+    Status = gBS->AllocatePages (
+                    AllocateAnyPages,
+                    EfiRuntimeServicesData,
+                    EFI_SIZE_TO_PAGES(PcdGet32 (PcdOvmfFirmwareFdSize)),
+                    &CryptedAddress
+                    );
+    ASSERT_EFI_ERROR (Status);
+
+    CopyMem((VOID *)CryptedAddress, (VOID *)BaseAddress, Length);
+
+    BaseAddress = CryptedAddress;
+  }
+
   Status = InitializeVariableFvHeader ();
   if (EFI_ERROR (Status)) {
     DEBUG ((EFI_D_INFO,
@@ -1091,24 +1111,33 @@ FvbInitialize (
   //
   InstallProtocolInterfaces (FvbDevice);
 
-  MarkMemoryRangeForRuntimeAccess (BaseAddress, Length);
+  MarkMemoryRangeForRuntimeAccess (
+    (UINTN) PcdGet32 (PcdOvmfFdBaseAddress),
+    Length
+    );
 
   //
   // Set several PCD values to point to flash
   //
   PcdStatus = PcdSet64S (
     PcdFlashNvStorageVariableBase64,
-    (UINTN) PcdGet32 (PcdOvmfFlashNvStorageVariableBase)
+    BaseAddress
     );
   ASSERT_RETURN_ERROR (PcdStatus);
   PcdStatus = PcdSet32S (
     PcdFlashNvStorageFtwWorkingBase,
-    PcdGet32 (PcdOvmfFlashNvStorageFtwWorkingBase)
+    BaseAddress +
+    PcdGet32(PcdFlashNvStorageVariableSize) +
+    PcdGet32(PcdOvmfFlashNvStorageEventLogSize)
     );
+
   ASSERT_RETURN_ERROR (PcdStatus);
   PcdStatus = PcdSet32S (
     PcdFlashNvStorageFtwSpareBase,
-    PcdGet32 (PcdOvmfFlashNvStorageFtwSpareBase)
+    BaseAddress +
+    PcdGet32(PcdFlashNvStorageVariableSize) +
+    PcdGet32(PcdOvmfFlashNvStorageEventLogSize) +
+    PcdGet32(PcdFlashNvStorageFtwWorkingSize)
     );
   ASSERT_RETURN_ERROR (PcdStatus);
 
-- 
2.7.4



^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [RFC PATCH 1/1] OvmfPkg/QemuFlash: Fix Runtime variable access when SEV is enabled
@ 2018-06-26 19:46 Brijesh Singh
  2018-06-27 12:54 ` Laszlo Ersek
  0 siblings, 1 reply; 15+ messages in thread
From: Brijesh Singh @ 2018-06-26 19:46 UTC (permalink / raw)
  To: edk2-devel; +Cc: Tom Lendacky, Brijesh Singh, Laszlo Ersek

Problem statement:
------------------
Fedora-28 contains 4.16 kernel -- which has all the required support to
run as an SEV guest.  When the installer is launched from SEV guest then
it fails to install the bootloader. The installer was failing to update
the 'BootOrder' UEFI runtime variable.

Root Cause Analysis
--------------------
Since QemuFlash storage memory is accessed by both guest and hypervisor
hence we need to map this memory range as unencrypted. AmdSevDxe maps the
range as "unencrypted" but later FtwNotificationEvent() in
MdeModule/Universal/Variable/RuntimeDxe/VariableDxe.c resets the mapping
and the memory region gets remapped as "encrypted". After that, any access
to the flash will end up going through the encryption engine. I did try
hacking EDK2 to restore the C-bit but that was not sufficient because UEFI
runtime services are mapped as "encrypted" in OS page table hence we end up
accessing the flash as encrypted when OS requests to update the variables.

A possible solution
---------------------
To solve the issue, after QemuFlash is probed, I allocate an encrypted
buffer and initialize this buffer with the contents from the flash memory.
When SEV is enabled, we use newly allocated encrypted buffer in
FwInstance->FvBase instead of the original flash region. The idea is if
caller grabs the FwInstance->FvBase pointer and tries to access the
FvVolumeHeader then it should get the data from the encrypted buffer.
But if caller wants read/writes to/from the flash device then we internally
use the original "unencrypted" flash region to access the data. With this
patch, I have verified that OS is able to update the runtime variable and
FC-28 installer is successfully able to complete the installation process.

If you all agree with approach then I can rework any feedbacks and remove
the rfc tag from the patch. If you have better suggestions then I am open
to explore those as well.

Cc: Laszlo Ersek <lersek@redhat.com>
Contributed-under: TianoCore Contribution Agreement 1.1
Signed-off-by: Brijesh Singh <brijesh.singh@amd.com>
---
 .../FvbServicesRuntimeDxe.inf                      |  1 +
 .../FwBlockService.c                               | 37 +++++++++++++++++++---
 2 files changed, 34 insertions(+), 4 deletions(-)

diff --git a/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FvbServicesRuntimeDxe.inf b/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FvbServicesRuntimeDxe.inf
index d7b4ec06c4e6..6bb5c2093790 100644
--- a/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FvbServicesRuntimeDxe.inf
+++ b/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FvbServicesRuntimeDxe.inf
@@ -54,6 +54,7 @@ [LibraryClasses]
   DevicePathLib
   DxeServicesTableLib
   MemoryAllocationLib
+  MemEncryptSevLib
   PcdLib
   UefiBootServicesTableLib
   UefiDriverEntryPoint
diff --git a/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FwBlockService.c b/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FwBlockService.c
index 558b395dff4a..e82b4ff70961 100644
--- a/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FwBlockService.c
+++ b/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FwBlockService.c
@@ -36,6 +36,7 @@
 #include <Library/DxeServicesTableLib.h>
 #include <Library/MemoryAllocationLib.h>
 #include <Library/UefiBootServicesTableLib.h>
+#include <Library/MemEncryptSevLib.h>
 
 #include "FwBlockService.h"
 #include "QemuFlash.h"
@@ -966,6 +967,7 @@ FvbInitialize (
   UINTN                               Length;
   UINTN                               NumOfBlocks;
   RETURN_STATUS                       PcdStatus;
+  EFI_PHYSICAL_ADDRESS                CryptedAddress;
 
   if (EFI_ERROR (QemuFlashInitialize ())) {
     //
@@ -986,6 +988,24 @@ FvbInitialize (
   BaseAddress = (UINTN) PcdGet32 (PcdOvmfFdBaseAddress);
   Length = PcdGet32 (PcdOvmfFirmwareFdSize);
 
+  //
+  // When SEV is enabled, allocate a encrypted buffer which will contain a
+  // encrypted copy of the Flash image.
+  //
+  if (MemEncryptSevIsEnabled ()) {
+    Status = gBS->AllocatePages (
+                    AllocateAnyPages,
+                    EfiRuntimeServicesData,
+                    EFI_SIZE_TO_PAGES(PcdGet32 (PcdOvmfFirmwareFdSize)),
+                    &CryptedAddress
+                    );
+    ASSERT_EFI_ERROR (Status);
+
+    CopyMem((VOID *)CryptedAddress, (VOID *)BaseAddress, Length);
+
+    BaseAddress = CryptedAddress;
+  }
+
   Status = InitializeVariableFvHeader ();
   if (EFI_ERROR (Status)) {
     DEBUG ((EFI_D_INFO,
@@ -1091,24 +1111,33 @@ FvbInitialize (
   //
   InstallProtocolInterfaces (FvbDevice);
 
-  MarkMemoryRangeForRuntimeAccess (BaseAddress, Length);
+  MarkMemoryRangeForRuntimeAccess (
+    (UINTN) PcdGet32 (PcdOvmfFdBaseAddress),
+    Length
+    );
 
   //
   // Set several PCD values to point to flash
   //
   PcdStatus = PcdSet64S (
     PcdFlashNvStorageVariableBase64,
-    (UINTN) PcdGet32 (PcdOvmfFlashNvStorageVariableBase)
+    BaseAddress
     );
   ASSERT_RETURN_ERROR (PcdStatus);
   PcdStatus = PcdSet32S (
     PcdFlashNvStorageFtwWorkingBase,
-    PcdGet32 (PcdOvmfFlashNvStorageFtwWorkingBase)
+    BaseAddress +
+    PcdGet32(PcdFlashNvStorageVariableSize) +
+    PcdGet32(PcdOvmfFlashNvStorageEventLogSize)
     );
+
   ASSERT_RETURN_ERROR (PcdStatus);
   PcdStatus = PcdSet32S (
     PcdFlashNvStorageFtwSpareBase,
-    PcdGet32 (PcdOvmfFlashNvStorageFtwSpareBase)
+    BaseAddress +
+    PcdGet32(PcdFlashNvStorageVariableSize) +
+    PcdGet32(PcdOvmfFlashNvStorageEventLogSize) +
+    PcdGet32(PcdFlashNvStorageFtwWorkingSize)
     );
   ASSERT_RETURN_ERROR (PcdStatus);
 
-- 
2.7.4



^ permalink raw reply related	[flat|nested] 15+ messages in thread

* Re: [RFC PATCH 1/1] OvmfPkg/QemuFlash: Fix Runtime variable access when SEV is enabled
  2018-06-26 19:46 [RFC PATCH 1/1] OvmfPkg/QemuFlash: Fix Runtime variable access when SEV is enabled Brijesh Singh
@ 2018-06-27 12:54 ` Laszlo Ersek
  2018-06-27 16:34   ` Brijesh Singh
  2018-06-28  6:16   ` Zeng, Star
  0 siblings, 2 replies; 15+ messages in thread
From: Laszlo Ersek @ 2018-06-27 12:54 UTC (permalink / raw)
  To: Brijesh Singh, edk2-devel
  Cc: Tom Lendacky, Star Zeng, Eric Dong, Jordan Justen (Intel address)

On 06/26/18 21:46, Brijesh Singh wrote:
> Problem statement:
> ------------------
> Fedora-28 contains 4.16 kernel -- which has all the required support to
> run as an SEV guest.  When the installer is launched from SEV guest then
> it fails to install the bootloader. The installer was failing to update
> the 'BootOrder' UEFI runtime variable.
> 
> Root Cause Analysis
> --------------------
> Since QemuFlash storage memory is accessed by both guest and hypervisor
> hence we need to map this memory range as unencrypted. AmdSevDxe maps the
> range as "unencrypted" but later FtwNotificationEvent() in
> MdeModule/Universal/Variable/RuntimeDxe/VariableDxe.c resets the mapping
> and the memory region gets remapped as "encrypted".

Is this a new issue, or has it always been there, and we just failed to
notice it?

BTW, I don't understand why FtwNotificationEvent() in
"MdeModulePkg/Universal/Variable/RuntimeDxe/VariableDxe.c" has to mark
the flash range as EFI_MEMORY_RUNTIME. I thought that this action
belonged in the flash driver itself, and we do that already in
MarkMemoryRangeForRuntimeAccess(), in file
"OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FwBlockService.c".

The variable driver does not own the pflash chip (the pflash driver owns
it), so I believe the variable driver shouldn't mess with the mapping
attributes.

Here's a suggestion -- Star, Eric, can you please comment? In the
FtwNotificationEvent() function, after we get the memory descriptor for
the pflash range, first check whether EFI_MEMORY_RUNTIME is already set.
If it is, don't do anything; if it isn't, add the attribute.

This should cause no observable change on any non-SEV platform, and it
should remove the gDS->SetMemorySpaceAttributes() call for OVMF (where
it breaks things for SEV).

> After that, any access
> to the flash will end up going through the encryption engine. I did try
> hacking EDK2 to restore the C-bit

(I continue to be annoyed that the memory encryption bit is not exposed
in the GCD memory space attributes explicitly.)

> but that was not sufficient because UEFI
> runtime services are mapped as "encrypted" in OS page table

What do you mean here? Runtime services *code* or runtime services
*data*? Code must obviously be remain encrypted (otherwise we cannot
execute it in SEV). Runtime Services Data should also be mapped as
encrypted (it is normal RAM that is not used for guest<->hypervisor
exchange).

> hence we end up accessing the flash as encrypted when OS requests to update the variables.

I don't understand the "hence" here; I don't see how the implication
follows. runtime services code and data should be encrypted. Runtime
MMIO should be un-encrypted.

Ohh, wait, in MarkMemoryRangeForRuntimeAccess(), we use
"EfiGcdMemoryTypeSystemMemory". I don't have a clue why that is a good
idea. That should have been EfiGcdMemoryTypeMemoryMappedIo.

... Anyway, I think first we should go with the "check
EFI_MEMORY_RUNTIME attribute before setting it", in FtwNotificationEvent().

> 
> A possible solution
> ---------------------
> To solve the issue, after QemuFlash is probed, I allocate an encrypted
> buffer and initialize this buffer with the contents from the flash memory.
> When SEV is enabled, we use newly allocated encrypted buffer in
> FwInstance->FvBase instead of the original flash region. The idea is if
> caller grabs the FwInstance->FvBase pointer and tries to access the
> FvVolumeHeader then it should get the data from the encrypted buffer.
> But if caller wants read/writes to/from the flash device then we internally
> use the original "unencrypted" flash region to access the data.

No, this is neither safe, nor a desirable design.

Safety: all accesses (via both pointers and FVB protocol members) that
higher-level drives *think* go to the pflash chip must *actually* go to
the pflash chip.

Design: it had taken us years to get rid of various memory-emulated fake
variable stores. They *all* suck in one way or another, with various
obscure UEFI spec incompatibilities and corner cases. A strictly
pflash-based varstore is not what we should compromise on.

> With this
> patch, I have verified that OS is able to update the runtime variable and
> FC-28 installer is successfully able to complete the installation process.
> 
> If you all agree with approach then I can rework any feedbacks and remove
> the rfc tag from the patch. If you have better suggestions then I am open
> to explore those as well.

I'd like to understand the following:

(1) why does FtwNotificationEvent() set the EFI_MEMORY_RUNTIME attribute
itself, for the pflash range? -- in my opinion, that belongs in the
flash driver.

(2) Whether Star and Eric agree with setting the EFI_MEMORY_RUNTIME
attribute in FtwNotificationEvent() only if the attribute is not already
present.

(3) The implication that you describe, between runtime services/code
being mapped encrypted, and restoring the C-bit failing.

(4) Whether we should modify MarkMemoryRangeForRuntimeAccess() to
install the range into GCD as MMIO. (I feel *very* uncomfortable about
this, however; the current code has existed as-is for years, and
regressions look very risky.)

My strong preference would be a patch for (2).

Thanks,
Laszlo

> Cc: Laszlo Ersek <lersek@redhat.com>
> Contributed-under: TianoCore Contribution Agreement 1.1
> Signed-off-by: Brijesh Singh <brijesh.singh@amd.com>
> ---
>  .../FvbServicesRuntimeDxe.inf                      |  1 +
>  .../FwBlockService.c                               | 37 +++++++++++++++++++---
>  2 files changed, 34 insertions(+), 4 deletions(-)
> 
> diff --git a/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FvbServicesRuntimeDxe.inf b/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FvbServicesRuntimeDxe.inf
> index d7b4ec06c4e6..6bb5c2093790 100644
> --- a/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FvbServicesRuntimeDxe.inf
> +++ b/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FvbServicesRuntimeDxe.inf
> @@ -54,6 +54,7 @@ [LibraryClasses]
>    DevicePathLib
>    DxeServicesTableLib
>    MemoryAllocationLib
> +  MemEncryptSevLib
>    PcdLib
>    UefiBootServicesTableLib
>    UefiDriverEntryPoint
> diff --git a/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FwBlockService.c b/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FwBlockService.c
> index 558b395dff4a..e82b4ff70961 100644
> --- a/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FwBlockService.c
> +++ b/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FwBlockService.c
> @@ -36,6 +36,7 @@
>  #include <Library/DxeServicesTableLib.h>
>  #include <Library/MemoryAllocationLib.h>
>  #include <Library/UefiBootServicesTableLib.h>
> +#include <Library/MemEncryptSevLib.h>
>  
>  #include "FwBlockService.h"
>  #include "QemuFlash.h"
> @@ -966,6 +967,7 @@ FvbInitialize (
>    UINTN                               Length;
>    UINTN                               NumOfBlocks;
>    RETURN_STATUS                       PcdStatus;
> +  EFI_PHYSICAL_ADDRESS                CryptedAddress;
>  
>    if (EFI_ERROR (QemuFlashInitialize ())) {
>      //
> @@ -986,6 +988,24 @@ FvbInitialize (
>    BaseAddress = (UINTN) PcdGet32 (PcdOvmfFdBaseAddress);
>    Length = PcdGet32 (PcdOvmfFirmwareFdSize);
>  
> +  //
> +  // When SEV is enabled, allocate a encrypted buffer which will contain a
> +  // encrypted copy of the Flash image.
> +  //
> +  if (MemEncryptSevIsEnabled ()) {
> +    Status = gBS->AllocatePages (
> +                    AllocateAnyPages,
> +                    EfiRuntimeServicesData,
> +                    EFI_SIZE_TO_PAGES(PcdGet32 (PcdOvmfFirmwareFdSize)),
> +                    &CryptedAddress
> +                    );
> +    ASSERT_EFI_ERROR (Status);
> +
> +    CopyMem((VOID *)CryptedAddress, (VOID *)BaseAddress, Length);
> +
> +    BaseAddress = CryptedAddress;
> +  }
> +
>    Status = InitializeVariableFvHeader ();
>    if (EFI_ERROR (Status)) {
>      DEBUG ((EFI_D_INFO,
> @@ -1091,24 +1111,33 @@ FvbInitialize (
>    //
>    InstallProtocolInterfaces (FvbDevice);
>  
> -  MarkMemoryRangeForRuntimeAccess (BaseAddress, Length);
> +  MarkMemoryRangeForRuntimeAccess (
> +    (UINTN) PcdGet32 (PcdOvmfFdBaseAddress),
> +    Length
> +    );
>  
>    //
>    // Set several PCD values to point to flash
>    //
>    PcdStatus = PcdSet64S (
>      PcdFlashNvStorageVariableBase64,
> -    (UINTN) PcdGet32 (PcdOvmfFlashNvStorageVariableBase)
> +    BaseAddress
>      );
>    ASSERT_RETURN_ERROR (PcdStatus);
>    PcdStatus = PcdSet32S (
>      PcdFlashNvStorageFtwWorkingBase,
> -    PcdGet32 (PcdOvmfFlashNvStorageFtwWorkingBase)
> +    BaseAddress +
> +    PcdGet32(PcdFlashNvStorageVariableSize) +
> +    PcdGet32(PcdOvmfFlashNvStorageEventLogSize)
>      );
> +
>    ASSERT_RETURN_ERROR (PcdStatus);
>    PcdStatus = PcdSet32S (
>      PcdFlashNvStorageFtwSpareBase,
> -    PcdGet32 (PcdOvmfFlashNvStorageFtwSpareBase)
> +    BaseAddress +
> +    PcdGet32(PcdFlashNvStorageVariableSize) +
> +    PcdGet32(PcdOvmfFlashNvStorageEventLogSize) +
> +    PcdGet32(PcdFlashNvStorageFtwWorkingSize)
>      );
>    ASSERT_RETURN_ERROR (PcdStatus);
>  
> 



^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [RFC PATCH 1/1] OvmfPkg/QemuFlash: Fix Runtime variable access when SEV is enabled
  2018-06-27 12:54 ` Laszlo Ersek
@ 2018-06-27 16:34   ` Brijesh Singh
  2018-06-27 16:37     ` Brijesh Singh
  2018-06-27 16:59     ` Laszlo Ersek
  2018-06-28  6:16   ` Zeng, Star
  1 sibling, 2 replies; 15+ messages in thread
From: Brijesh Singh @ 2018-06-27 16:34 UTC (permalink / raw)
  To: Laszlo Ersek, edk2-devel
  Cc: brijesh.singh, Tom Lendacky, Star Zeng, Eric Dong,
	Jordan Justen (Intel address)

Thanks for the quick feedback Laszlo !


On 06/27/2018 07:54 AM, Laszlo Ersek wrote:
> On 06/26/18 21:46, Brijesh Singh wrote:
>> Problem statement:
>> ------------------
>> Fedora-28 contains 4.16 kernel -- which has all the required support to
>> run as an SEV guest.  When the installer is launched from SEV guest then
>> it fails to install the bootloader. The installer was failing to update
>> the 'BootOrder' UEFI runtime variable.
>>
>> Root Cause Analysis
>> --------------------
>> Since QemuFlash storage memory is accessed by both guest and hypervisor
>> hence we need to map this memory range as unencrypted. AmdSevDxe maps the
>> range as "unencrypted" but later FtwNotificationEvent() in
>> MdeModule/Universal/Variable/RuntimeDxe/VariableDxe.c resets the mapping
>> and the memory region gets remapped as "encrypted".
> 
> Is this a new issue, or has it always been there, and we just failed to
> notice it?
> 


The issue has been always there and we never noticed. I noticed it after
I saw installation failure. I can easily reproduce it with simple
efibootmgr command:

'efibootmgr -o 0003,0004' -- e.g change boot order


> BTW, I don't understand why FtwNotificationEvent() in
> "MdeModulePkg/Universal/Variable/RuntimeDxe/VariableDxe.c" has to mark
> the flash range as EFI_MEMORY_RUNTIME. I thought that this action
> belonged in the flash driver itself, and we do that already in
> MarkMemoryRangeForRuntimeAccess(), in file
> "OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FwBlockService.c".
> 
> The variable driver does not own the pflash chip (the pflash driver owns
> it), so I believe the variable driver shouldn't mess with the mapping
> attributes.
> 
> Here's a suggestion -- Star, Eric, can you please comment? In the
> FtwNotificationEvent() function, after we get the memory descriptor for
> the pflash range, first check whether EFI_MEMORY_RUNTIME is already set.
> If it is, don't do anything; if it isn't, add the attribute.
> 
> This should cause no observable change on any non-SEV platform, and it
> should remove the gDS->SetMemorySpaceAttributes() call for OVMF (where
> it breaks things for SEV).
> 

If Star and Eric agrees with your proposal then I can create a patch to
fix this issue.


>> After that, any access
>> to the flash will end up going through the encryption engine. I did try
>> hacking EDK2 to restore the C-bit
> 
> (I continue to be annoyed that the memory encryption bit is not exposed
> in the GCD memory space attributes explicitly.)
> 
>> but that was not sufficient because UEFI
>> runtime services are mapped as "encrypted" in OS page table
> 
> What do you mean here? Runtime services *code* or runtime services
> *data*? Code must obviously be remain encrypted (otherwise we cannot
> execute it in SEV). Runtime Services Data should also be mapped as
> encrypted (it is normal RAM that is not used for guest<->hypervisor
> exchange).
> 


Sorry, I was meaning to say both the "code" and "data" are mapped as
encrypted by the OS.


>> hence we end up accessing the flash as encrypted when OS requests to update the variables.
> 
> I don't understand the "hence" here; I don't see how the implication
> follows. runtime services code and data should be encrypted. Runtime
> MMIO should be un-encrypted.
>
> Ohh, wait, in MarkMemoryRangeForRuntimeAccess(), we use
> "EfiGcdMemoryTypeSystemMemory". I don't have a clue why that is a good
> idea. That should have been EfiGcdMemoryTypeMemoryMappedIo.
> 


Right, the memory is marked as 'system ram' and not 'mmio'.
Just to experiment, I did try changing it to 'mmio' to see if OS will
map this  region as "unencrypted" but ovmf fails with below error
message after changing it from systemRAM->mmio

ConvertPages: failed to find range FFC00000 - FFFFFFFF
ASSERT_EFI_ERROR (Status = Not Found)
ASSERT [FvbServicesRuntimeDxe] 
/home/amd/workdir/upstream/edk2/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FwBlockServie.c(864): 
!EFI_ERROR (Status)

Since this efi runtime data is mapped as C=1 by the OS, hence when OS
asks efi to update the runtime variable we end up accessing the memory
region with C=1 (runtime services are executed using OS pagetable).


> ... Anyway, I think first we should go with the "check
> EFI_MEMORY_RUNTIME attribute before setting it", in FtwNotificationEvent().
> 
>>
>> A possible solution
>> ---------------------
>> To solve the issue, after QemuFlash is probed, I allocate an encrypted
>> buffer and initialize this buffer with the contents from the flash memory.
>> When SEV is enabled, we use newly allocated encrypted buffer in
>> FwInstance->FvBase instead of the original flash region. The idea is if
>> caller grabs the FwInstance->FvBase pointer and tries to access the
>> FvVolumeHeader then it should get the data from the encrypted buffer.
>> But if caller wants read/writes to/from the flash device then we internally
>> use the original "unencrypted" flash region to access the data.
> 
> No, this is neither safe, nor a desirable design.


While hacking this I knew it was not an idle approach but wanted to
start the discussion to find an acceptable solution.


> 
> Safety: all accesses (via both pointers and FVB protocol members) that
> higher-level drives *think* go to the pflash chip must *actually* go to
> the pflash chip.
> 

Agreed, if all the access to the flash chip was going to go through
FVB protocol members then we could use bounce buffer at the lowest
level.


> Design: it had taken us years to get rid of various memory-emulated fake
> variable stores. They *all* suck in one way or another, with various
> obscure UEFI spec incompatibilities and corner cases. A strictly
> pflash-based varstore is not what we should compromise on.
> 
>> With this
>> patch, I have verified that OS is able to update the runtime variable and
>> FC-28 installer is successfully able to complete the installation process.
>>
>> If you all agree with approach then I can rework any feedbacks and remove
>> the rfc tag from the patch. If you have better suggestions then I am open
>> to explore those as well.
> 
> I'd like to understand the following:
> 
> (1) why does FtwNotificationEvent() set the EFI_MEMORY_RUNTIME attribute
> itself, for the pflash range? -- in my opinion, that belongs in the
> flash driver.
> 
> (2) Whether Star and Eric agree with setting the EFI_MEMORY_RUNTIME
> attribute in FtwNotificationEvent() only if the attribute is not already
> present.
> 
> (3) The implication that you describe, between runtime services/code
> being mapped encrypted, and restoring the C-bit failing.
> 
> (4) Whether we should modify MarkMemoryRangeForRuntimeAccess() to
> install the range into GCD as MMIO. (I feel *very* uncomfortable about
> this, however; the current code has existed as-is for years, and
> regressions look very risky.)
> 
> My strong preference would be a patch for (2).

I think (2) will solve the complete issue, we still need to figure how
to communicate the OS to map this flash memory range as 'unencrypted'
so that efi runtime services can update the variables correctly.



> 
> Thanks,
> Laszlo
> 
>> Cc: Laszlo Ersek <lersek@redhat.com>
>> Contributed-under: TianoCore Contribution Agreement 1.1
>> Signed-off-by: Brijesh Singh <brijesh.singh@amd.com>
>> ---
>>   .../FvbServicesRuntimeDxe.inf                      |  1 +
>>   .../FwBlockService.c                               | 37 +++++++++++++++++++---
>>   2 files changed, 34 insertions(+), 4 deletions(-)
>>
>> diff --git a/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FvbServicesRuntimeDxe.inf b/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FvbServicesRuntimeDxe.inf
>> index d7b4ec06c4e6..6bb5c2093790 100644
>> --- a/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FvbServicesRuntimeDxe.inf
>> +++ b/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FvbServicesRuntimeDxe.inf
>> @@ -54,6 +54,7 @@ [LibraryClasses]
>>     DevicePathLib
>>     DxeServicesTableLib
>>     MemoryAllocationLib
>> +  MemEncryptSevLib
>>     PcdLib
>>     UefiBootServicesTableLib
>>     UefiDriverEntryPoint
>> diff --git a/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FwBlockService.c b/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FwBlockService.c
>> index 558b395dff4a..e82b4ff70961 100644
>> --- a/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FwBlockService.c
>> +++ b/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FwBlockService.c
>> @@ -36,6 +36,7 @@
>>   #include <Library/DxeServicesTableLib.h>
>>   #include <Library/MemoryAllocationLib.h>
>>   #include <Library/UefiBootServicesTableLib.h>
>> +#include <Library/MemEncryptSevLib.h>
>>   
>>   #include "FwBlockService.h"
>>   #include "QemuFlash.h"
>> @@ -966,6 +967,7 @@ FvbInitialize (
>>     UINTN                               Length;
>>     UINTN                               NumOfBlocks;
>>     RETURN_STATUS                       PcdStatus;
>> +  EFI_PHYSICAL_ADDRESS                CryptedAddress;
>>   
>>     if (EFI_ERROR (QemuFlashInitialize ())) {
>>       //
>> @@ -986,6 +988,24 @@ FvbInitialize (
>>     BaseAddress = (UINTN) PcdGet32 (PcdOvmfFdBaseAddress);
>>     Length = PcdGet32 (PcdOvmfFirmwareFdSize);
>>   
>> +  //
>> +  // When SEV is enabled, allocate a encrypted buffer which will contain a
>> +  // encrypted copy of the Flash image.
>> +  //
>> +  if (MemEncryptSevIsEnabled ()) {
>> +    Status = gBS->AllocatePages (
>> +                    AllocateAnyPages,
>> +                    EfiRuntimeServicesData,
>> +                    EFI_SIZE_TO_PAGES(PcdGet32 (PcdOvmfFirmwareFdSize)),
>> +                    &CryptedAddress
>> +                    );
>> +    ASSERT_EFI_ERROR (Status);
>> +
>> +    CopyMem((VOID *)CryptedAddress, (VOID *)BaseAddress, Length);
>> +
>> +    BaseAddress = CryptedAddress;
>> +  }
>> +
>>     Status = InitializeVariableFvHeader ();
>>     if (EFI_ERROR (Status)) {
>>       DEBUG ((EFI_D_INFO,
>> @@ -1091,24 +1111,33 @@ FvbInitialize (
>>     //
>>     InstallProtocolInterfaces (FvbDevice);
>>   
>> -  MarkMemoryRangeForRuntimeAccess (BaseAddress, Length);
>> +  MarkMemoryRangeForRuntimeAccess (
>> +    (UINTN) PcdGet32 (PcdOvmfFdBaseAddress),
>> +    Length
>> +    );
>>   
>>     //
>>     // Set several PCD values to point to flash
>>     //
>>     PcdStatus = PcdSet64S (
>>       PcdFlashNvStorageVariableBase64,
>> -    (UINTN) PcdGet32 (PcdOvmfFlashNvStorageVariableBase)
>> +    BaseAddress
>>       );
>>     ASSERT_RETURN_ERROR (PcdStatus);
>>     PcdStatus = PcdSet32S (
>>       PcdFlashNvStorageFtwWorkingBase,
>> -    PcdGet32 (PcdOvmfFlashNvStorageFtwWorkingBase)
>> +    BaseAddress +
>> +    PcdGet32(PcdFlashNvStorageVariableSize) +
>> +    PcdGet32(PcdOvmfFlashNvStorageEventLogSize)
>>       );
>> +
>>     ASSERT_RETURN_ERROR (PcdStatus);
>>     PcdStatus = PcdSet32S (
>>       PcdFlashNvStorageFtwSpareBase,
>> -    PcdGet32 (PcdOvmfFlashNvStorageFtwSpareBase)
>> +    BaseAddress +
>> +    PcdGet32(PcdFlashNvStorageVariableSize) +
>> +    PcdGet32(PcdOvmfFlashNvStorageEventLogSize) +
>> +    PcdGet32(PcdFlashNvStorageFtwWorkingSize)
>>       );
>>     ASSERT_RETURN_ERROR (PcdStatus);
>>   
>>
> 


^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [RFC PATCH 1/1] OvmfPkg/QemuFlash: Fix Runtime variable access when SEV is enabled
  2018-06-27 16:34   ` Brijesh Singh
@ 2018-06-27 16:37     ` Brijesh Singh
  2018-06-27 16:59     ` Laszlo Ersek
  1 sibling, 0 replies; 15+ messages in thread
From: Brijesh Singh @ 2018-06-27 16:37 UTC (permalink / raw)
  To: Laszlo Ersek, edk2-devel
  Cc: brijesh.singh, Tom Lendacky, Star Zeng, Eric Dong,
	Jordan Justen (Intel address)



On 06/27/2018 11:34 AM, Brijesh Singh wrote:
> I think (2) will solve the complete issue, we still need to figure how

I meant to say (2) will *not* solve the complete issue !


^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [RFC PATCH 1/1] OvmfPkg/QemuFlash: Fix Runtime variable access when SEV is enabled
  2018-06-27 16:34   ` Brijesh Singh
  2018-06-27 16:37     ` Brijesh Singh
@ 2018-06-27 16:59     ` Laszlo Ersek
  2018-06-27 17:49       ` Brijesh Singh
  1 sibling, 1 reply; 15+ messages in thread
From: Laszlo Ersek @ 2018-06-27 16:59 UTC (permalink / raw)
  To: Brijesh Singh, edk2-devel
  Cc: Tom Lendacky, Star Zeng, Eric Dong, Jordan Justen (Intel address)

On 06/27/18 18:34, Brijesh Singh wrote:
> On 06/27/2018 07:54 AM, Laszlo Ersek wrote:
>> On 06/26/18 21:46, Brijesh Singh wrote:

>>> After that, any access
>>> to the flash will end up going through the encryption engine. I did try
>>> hacking EDK2 to restore the C-bit
>>
>> (I continue to be annoyed that the memory encryption bit is not exposed
>> in the GCD memory space attributes explicitly.)
>>
>>> but that was not sufficient because UEFI
>>> runtime services are mapped as "encrypted" in OS page table
>>
>> What do you mean here? Runtime services *code* or runtime services
>> *data*? Code must obviously be remain encrypted (otherwise we cannot
>> execute it in SEV). Runtime Services Data should also be mapped as
>> encrypted (it is normal RAM that is not used for guest<->hypervisor
>> exchange).
> 
> Sorry, I was meaning to say both the "code" and "data" are mapped as
> encrypted by the OS.
> 
>>> hence we end up accessing the flash as encrypted when OS requests to
>>> update the variables.
>>
>> I don't understand the "hence" here; I don't see how the implication
>> follows. runtime services code and data should be encrypted. Runtime
>> MMIO should be un-encrypted.
>>
>> Ohh, wait, in MarkMemoryRangeForRuntimeAccess(), we use
>> "EfiGcdMemoryTypeSystemMemory". I don't have a clue why that is a good
>> idea. That should have been EfiGcdMemoryTypeMemoryMappedIo.
> 
> Right, the memory is marked as 'system ram' and not 'mmio'.
> Just to experiment, I did try changing it to 'mmio' to see if OS will
> map this  region as "unencrypted" but ovmf fails with below error
> message after changing it from systemRAM->mmio
> 
> ConvertPages: failed to find range FFC00000 - FFFFFFFF
> ASSERT_EFI_ERROR (Status = Not Found)
> ASSERT [FvbServicesRuntimeDxe]
> /home/amd/workdir/upstream/edk2/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FwBlockServie.c(864):
> !EFI_ERROR (Status)

This error occurs because (I think) you modified only the AddMemorySpace
call. If you change the GCD type on that, then please update the
subsequent AllocatePages as well, from EfiRuntimeServicesData to
EfiMemoryMappedIO.

The spec says about the latter enum constant, "Used by system firmware
to request that a memory-mapped IO region be mapped by the OS to a
virtual address so it can be accessed by EFI runtime services." It seems
appropriate (and I'm a bit confused why we haven't used the MMIO GCD and
UEFI enum values for the memory type, all this time.)

> Since this efi runtime data is mapped as C=1 by the OS, hence when OS
> asks efi to update the runtime variable we end up accessing the memory
> region with C=1 (runtime services are executed using OS pagetable).

Indeed.

(And, this is only a problem when SMM is not used, i.e. when the full
variable driver stack is non-SMM, just DXE. In the SMM case, the SMM
page tables are used, and the OS cannot interfere with that.)

Anyway, in the pure DXE / runtime driver case, do you think a guest
kernel patch will be necessary too? Perhaps if you change the UEFI
memmap entry type (see AllocatePages above) to MMIO, then the guest
kernel could technically honor that.

Thanks
Laszlo


^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [RFC PATCH 1/1] OvmfPkg/QemuFlash: Fix Runtime variable access when SEV is enabled
  2018-06-27 16:59     ` Laszlo Ersek
@ 2018-06-27 17:49       ` Brijesh Singh
  2018-06-28  6:25         ` Zeng, Star
  2018-06-28 12:57         ` Laszlo Ersek
  0 siblings, 2 replies; 15+ messages in thread
From: Brijesh Singh @ 2018-06-27 17:49 UTC (permalink / raw)
  To: Laszlo Ersek, edk2-devel
  Cc: brijesh.singh, Tom Lendacky, Star Zeng, Eric Dong,
	Jordan Justen (Intel address)



On 06/27/2018 11:59 AM, Laszlo Ersek wrote:
> On 06/27/18 18:34, Brijesh Singh wrote:
>> On 06/27/2018 07:54 AM, Laszlo Ersek wrote:
>>> On 06/26/18 21:46, Brijesh Singh wrote:
> 
>>>> After that, any access
>>>> to the flash will end up going through the encryption engine. I did try
>>>> hacking EDK2 to restore the C-bit
>>>
>>> (I continue to be annoyed that the memory encryption bit is not exposed
>>> in the GCD memory space attributes explicitly.)
>>>
>>>> but that was not sufficient because UEFI
>>>> runtime services are mapped as "encrypted" in OS page table
>>>
>>> What do you mean here? Runtime services *code* or runtime services
>>> *data*? Code must obviously be remain encrypted (otherwise we cannot
>>> execute it in SEV). Runtime Services Data should also be mapped as
>>> encrypted (it is normal RAM that is not used for guest<->hypervisor
>>> exchange).
>>
>> Sorry, I was meaning to say both the "code" and "data" are mapped as
>> encrypted by the OS.
>>
>>>> hence we end up accessing the flash as encrypted when OS requests to
>>>> update the variables.
>>>
>>> I don't understand the "hence" here; I don't see how the implication
>>> follows. runtime services code and data should be encrypted. Runtime
>>> MMIO should be un-encrypted.
>>>
>>> Ohh, wait, in MarkMemoryRangeForRuntimeAccess(), we use
>>> "EfiGcdMemoryTypeSystemMemory". I don't have a clue why that is a good
>>> idea. That should have been EfiGcdMemoryTypeMemoryMappedIo.
>>
>> Right, the memory is marked as 'system ram' and not 'mmio'.
>> Just to experiment, I did try changing it to 'mmio' to see if OS will
>> map this  region as "unencrypted" but ovmf fails with below error
>> message after changing it from systemRAM->mmio
>>
>> ConvertPages: failed to find range FFC00000 - FFFFFFFF
>> ASSERT_EFI_ERROR (Status = Not Found)
>> ASSERT [FvbServicesRuntimeDxe]
>> /home/amd/workdir/upstream/edk2/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FwBlockServie.c(864):
>> !EFI_ERROR (Status)
> 
> This error occurs because (I think) you modified only the AddMemorySpace
> call. If you change the GCD type on that, then please update the
> subsequent AllocatePages as well, from EfiRuntimeServicesData to
> EfiMemoryMappedIO.
> 

Here is what I have.

--- a/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FwBlockService.c
+++ b/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FwBlockService.c
@@ -847,7 +847,7 @@ MarkMemoryRangeForRuntimeAccess (
                    );

    Status = gDS->AddMemorySpace (
-                  EfiGcdMemoryTypeSystemMemory,
+                  EfiGcdMemoryTypeMemoryMappedIo,
                    BaseAddress,
                    Length,
                    EFI_MEMORY_UC | EFI_MEMORY_RUNTIME
@@ -856,7 +856,7 @@ MarkMemoryRangeForRuntimeAccess (

    Status = gBS->AllocatePages (
                    AllocateAddress,
-                  EfiRuntimeServicesData,
+                  EfiMemoryMappedIO,
                    EFI_SIZE_TO_PAGES (Length),
                    &BaseAddress
                    );

I am still getting the error assertion failure. I can debug to see what 
is going on.


> The spec says about the latter enum constant, "Used by system firmware
> to request that a memory-mapped IO region be mapped by the OS to a
> virtual address so it can be accessed by EFI runtime services." It seems
> appropriate (and I'm a bit confused why we haven't used the MMIO GCD and
> UEFI enum values for the memory type, all this time.)
> 
>> Since this efi runtime data is mapped as C=1 by the OS, hence when OS
>> asks efi to update the runtime variable we end up accessing the memory
>> region with C=1 (runtime services are executed using OS pagetable).
> 
> Indeed.
> 
> (And, this is only a problem when SMM is not used, i.e. when the full
> variable driver stack is non-SMM, just DXE. In the SMM case, the SMM
> page tables are used, and the OS cannot interfere with that.)
> 

Good point, I will try it and let you know. As you say since SMM uses
UEFI page table hence after fixing FtwNotificationEvent(..) we should be 
good.


> Anyway, in the pure DXE / runtime driver case, do you think a guest
> kernel patch will be necessary too? Perhaps if you change the UEFI
> memmap entry type (see AllocatePages above) to MMIO, then the guest
> kernel could technically honor that.
> 


Theoretically speaking, if we are able to make this memory region as
mmio then OS should be able to map it with C=0.


-Brijesh


^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [RFC PATCH 1/1] OvmfPkg/QemuFlash: Fix Runtime variable access when SEV is enabled
  2018-06-27 12:54 ` Laszlo Ersek
  2018-06-27 16:34   ` Brijesh Singh
@ 2018-06-28  6:16   ` Zeng, Star
  2018-06-28 13:13     ` Laszlo Ersek
  1 sibling, 1 reply; 15+ messages in thread
From: Zeng, Star @ 2018-06-28  6:16 UTC (permalink / raw)
  To: Laszlo Ersek, Brijesh Singh, edk2-devel@lists.01.org
  Cc: Tom Lendacky, Dong, Eric, Justen, Jordan L, Zeng, Star

1) My understanding is Variable Driver is managing the variable region in flash although the flash read/write/erase operations are done in flash driver. Current Variable Driver needs the address (to variable region) be converted to virtual address for runtime, and it does not assume flash driver to set the runtime attribute for variable region, but do it by itself.

2) I agree this approach. If the runtime attribute has been set by flash driver, Variable Driver has no need to do that.


Thanks,
Star
-----Original Message-----
From: edk2-devel [mailto:edk2-devel-bounces@lists.01.org] On Behalf Of Laszlo Ersek
Sent: Wednesday, June 27, 2018 8:54 PM
To: Brijesh Singh <brijesh.singh@amd.com>; edk2-devel@lists.01.org
Cc: Tom Lendacky <Thomas.Lendacky@amd.com>; Dong, Eric <eric.dong@intel.com>; Zeng, Star <star.zeng@intel.com>; Justen, Jordan L <jordan.l.justen@intel.com>
Subject: Re: [edk2] [RFC PATCH 1/1] OvmfPkg/QemuFlash: Fix Runtime variable access when SEV is enabled

On 06/26/18 21:46, Brijesh Singh wrote:
> Problem statement:
> ------------------
> Fedora-28 contains 4.16 kernel -- which has all the required support 
> to run as an SEV guest.  When the installer is launched from SEV guest 
> then it fails to install the bootloader. The installer was failing to 
> update the 'BootOrder' UEFI runtime variable.
> 
> Root Cause Analysis
> --------------------
> Since QemuFlash storage memory is accessed by both guest and 
> hypervisor hence we need to map this memory range as unencrypted. 
> AmdSevDxe maps the range as "unencrypted" but later 
> FtwNotificationEvent() in 
> MdeModule/Universal/Variable/RuntimeDxe/VariableDxe.c resets the mapping and the memory region gets remapped as "encrypted".

Is this a new issue, or has it always been there, and we just failed to notice it?

BTW, I don't understand why FtwNotificationEvent() in "MdeModulePkg/Universal/Variable/RuntimeDxe/VariableDxe.c" has to mark the flash range as EFI_MEMORY_RUNTIME. I thought that this action belonged in the flash driver itself, and we do that already in MarkMemoryRangeForRuntimeAccess(), in file "OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FwBlockService.c".

The variable driver does not own the pflash chip (the pflash driver owns it), so I believe the variable driver shouldn't mess with the mapping attributes.

Here's a suggestion -- Star, Eric, can you please comment? In the
FtwNotificationEvent() function, after we get the memory descriptor for the pflash range, first check whether EFI_MEMORY_RUNTIME is already set.
If it is, don't do anything; if it isn't, add the attribute.

This should cause no observable change on any non-SEV platform, and it should remove the gDS->SetMemorySpaceAttributes() call for OVMF (where it breaks things for SEV).

> After that, any access
> to the flash will end up going through the encryption engine. I did 
> try hacking EDK2 to restore the C-bit

(I continue to be annoyed that the memory encryption bit is not exposed in the GCD memory space attributes explicitly.)

> but that was not sufficient because UEFI runtime services are mapped 
> as "encrypted" in OS page table

What do you mean here? Runtime services *code* or runtime services *data*? Code must obviously be remain encrypted (otherwise we cannot execute it in SEV). Runtime Services Data should also be mapped as encrypted (it is normal RAM that is not used for guest<->hypervisor exchange).

> hence we end up accessing the flash as encrypted when OS requests to update the variables.

I don't understand the "hence" here; I don't see how the implication follows. runtime services code and data should be encrypted. Runtime MMIO should be un-encrypted.

Ohh, wait, in MarkMemoryRangeForRuntimeAccess(), we use "EfiGcdMemoryTypeSystemMemory". I don't have a clue why that is a good idea. That should have been EfiGcdMemoryTypeMemoryMappedIo.

... Anyway, I think first we should go with the "check EFI_MEMORY_RUNTIME attribute before setting it", in FtwNotificationEvent().

> 
> A possible solution
> ---------------------
> To solve the issue, after QemuFlash is probed, I allocate an encrypted 
> buffer and initialize this buffer with the contents from the flash memory.
> When SEV is enabled, we use newly allocated encrypted buffer in
> FwInstance->FvBase instead of the original flash region. The idea is 
> FwInstance->if
> caller grabs the FwInstance->FvBase pointer and tries to access the 
> FvVolumeHeader then it should get the data from the encrypted buffer.
> But if caller wants read/writes to/from the flash device then we 
> internally use the original "unencrypted" flash region to access the data.

No, this is neither safe, nor a desirable design.

Safety: all accesses (via both pointers and FVB protocol members) that higher-level drives *think* go to the pflash chip must *actually* go to the pflash chip.

Design: it had taken us years to get rid of various memory-emulated fake variable stores. They *all* suck in one way or another, with various obscure UEFI spec incompatibilities and corner cases. A strictly pflash-based varstore is not what we should compromise on.

> With this
> patch, I have verified that OS is able to update the runtime variable 
> and
> FC-28 installer is successfully able to complete the installation process.
> 
> If you all agree with approach then I can rework any feedbacks and 
> remove the rfc tag from the patch. If you have better suggestions then 
> I am open to explore those as well.

I'd like to understand the following:

(1) why does FtwNotificationEvent() set the EFI_MEMORY_RUNTIME attribute itself, for the pflash range? -- in my opinion, that belongs in the flash driver.

(2) Whether Star and Eric agree with setting the EFI_MEMORY_RUNTIME attribute in FtwNotificationEvent() only if the attribute is not already present.

(3) The implication that you describe, between runtime services/code being mapped encrypted, and restoring the C-bit failing.

(4) Whether we should modify MarkMemoryRangeForRuntimeAccess() to install the range into GCD as MMIO. (I feel *very* uncomfortable about this, however; the current code has existed as-is for years, and regressions look very risky.)

My strong preference would be a patch for (2).

Thanks,
Laszlo

> Cc: Laszlo Ersek <lersek@redhat.com>
> Contributed-under: TianoCore Contribution Agreement 1.1
> Signed-off-by: Brijesh Singh <brijesh.singh@amd.com>
> ---
>  .../FvbServicesRuntimeDxe.inf                      |  1 +
>  .../FwBlockService.c                               | 37 +++++++++++++++++++---
>  2 files changed, 34 insertions(+), 4 deletions(-)
> 
> diff --git 
> a/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FvbServicesRuntimeDxe.inf 
> b/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FvbServicesRuntimeDxe.inf
> index d7b4ec06c4e6..6bb5c2093790 100644
> --- a/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FvbServicesRuntimeDxe.inf
> +++ b/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FvbServicesRuntimeDxe.inf
> @@ -54,6 +54,7 @@ [LibraryClasses]
>    DevicePathLib
>    DxeServicesTableLib
>    MemoryAllocationLib
> +  MemEncryptSevLib
>    PcdLib
>    UefiBootServicesTableLib
>    UefiDriverEntryPoint
> diff --git a/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FwBlockService.c 
> b/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FwBlockService.c
> index 558b395dff4a..e82b4ff70961 100644
> --- a/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FwBlockService.c
> +++ b/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FwBlockService.c
> @@ -36,6 +36,7 @@
>  #include <Library/DxeServicesTableLib.h>  #include 
> <Library/MemoryAllocationLib.h>  #include 
> <Library/UefiBootServicesTableLib.h>
> +#include <Library/MemEncryptSevLib.h>
>  
>  #include "FwBlockService.h"
>  #include "QemuFlash.h"
> @@ -966,6 +967,7 @@ FvbInitialize (
>    UINTN                               Length;
>    UINTN                               NumOfBlocks;
>    RETURN_STATUS                       PcdStatus;
> +  EFI_PHYSICAL_ADDRESS                CryptedAddress;
>  
>    if (EFI_ERROR (QemuFlashInitialize ())) {
>      //
> @@ -986,6 +988,24 @@ FvbInitialize (
>    BaseAddress = (UINTN) PcdGet32 (PcdOvmfFdBaseAddress);
>    Length = PcdGet32 (PcdOvmfFirmwareFdSize);
>  
> +  //
> +  // When SEV is enabled, allocate a encrypted buffer which will 
> + contain a  // encrypted copy of the Flash image.
> +  //
> +  if (MemEncryptSevIsEnabled ()) {
> +    Status = gBS->AllocatePages (
> +                    AllocateAnyPages,
> +                    EfiRuntimeServicesData,
> +                    EFI_SIZE_TO_PAGES(PcdGet32 (PcdOvmfFirmwareFdSize)),
> +                    &CryptedAddress
> +                    );
> +    ASSERT_EFI_ERROR (Status);
> +
> +    CopyMem((VOID *)CryptedAddress, (VOID *)BaseAddress, Length);
> +
> +    BaseAddress = CryptedAddress;
> +  }
> +
>    Status = InitializeVariableFvHeader ();
>    if (EFI_ERROR (Status)) {
>      DEBUG ((EFI_D_INFO,
> @@ -1091,24 +1111,33 @@ FvbInitialize (
>    //
>    InstallProtocolInterfaces (FvbDevice);
>  
> -  MarkMemoryRangeForRuntimeAccess (BaseAddress, Length);
> +  MarkMemoryRangeForRuntimeAccess (
> +    (UINTN) PcdGet32 (PcdOvmfFdBaseAddress),
> +    Length
> +    );
>  
>    //
>    // Set several PCD values to point to flash
>    //
>    PcdStatus = PcdSet64S (
>      PcdFlashNvStorageVariableBase64,
> -    (UINTN) PcdGet32 (PcdOvmfFlashNvStorageVariableBase)
> +    BaseAddress
>      );
>    ASSERT_RETURN_ERROR (PcdStatus);
>    PcdStatus = PcdSet32S (
>      PcdFlashNvStorageFtwWorkingBase,
> -    PcdGet32 (PcdOvmfFlashNvStorageFtwWorkingBase)
> +    BaseAddress +
> +    PcdGet32(PcdFlashNvStorageVariableSize) +
> +    PcdGet32(PcdOvmfFlashNvStorageEventLogSize)
>      );
> +
>    ASSERT_RETURN_ERROR (PcdStatus);
>    PcdStatus = PcdSet32S (
>      PcdFlashNvStorageFtwSpareBase,
> -    PcdGet32 (PcdOvmfFlashNvStorageFtwSpareBase)
> +    BaseAddress +
> +    PcdGet32(PcdFlashNvStorageVariableSize) +
> +    PcdGet32(PcdOvmfFlashNvStorageEventLogSize) +
> +    PcdGet32(PcdFlashNvStorageFtwWorkingSize)
>      );
>    ASSERT_RETURN_ERROR (PcdStatus);
>  
> 

_______________________________________________
edk2-devel mailing list
edk2-devel@lists.01.org
https://lists.01.org/mailman/listinfo/edk2-devel


^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [RFC PATCH 1/1] OvmfPkg/QemuFlash: Fix Runtime variable access when SEV is enabled
  2018-06-27 17:49       ` Brijesh Singh
@ 2018-06-28  6:25         ` Zeng, Star
  2018-06-28 13:15           ` Laszlo Ersek
  2018-06-28 12:57         ` Laszlo Ersek
  1 sibling, 1 reply; 15+ messages in thread
From: Zeng, Star @ 2018-06-28  6:25 UTC (permalink / raw)
  To: Brijesh Singh, Laszlo Ersek, edk2-devel@lists.01.org
  Cc: Tom Lendacky, Dong, Eric, Justen, Jordan L, Zeng, Star

My understanding is MMIO is not managed by UEFI memory services, but GCD services.
PI spec says " If the memory range specified by BaseAddress and Length is of type EfiGcdMemoryTypeSystemMemory or EfiGcdMemoryTypeMoreReliable, then the memory range may be automatically *allocated* for use by the *UEFI memory services*." in AddMemorySpace() description.

For MMIO, the code needs to use AddMemorySpace() + AllocateMemorySpace().


Thanks,
Star
-----Original Message-----
From: edk2-devel [mailto:edk2-devel-bounces@lists.01.org] On Behalf Of Brijesh Singh
Sent: Thursday, June 28, 2018 1:50 AM
To: Laszlo Ersek <lersek@redhat.com>; edk2-devel@lists.01.org
Cc: Tom Lendacky <Thomas.Lendacky@amd.com>; brijesh.singh@amd.com; Dong, Eric <eric.dong@intel.com>; Zeng, Star <star.zeng@intel.com>; Justen, Jordan L <jordan.l.justen@intel.com>
Subject: Re: [edk2] [RFC PATCH 1/1] OvmfPkg/QemuFlash: Fix Runtime variable access when SEV is enabled



On 06/27/2018 11:59 AM, Laszlo Ersek wrote:
> On 06/27/18 18:34, Brijesh Singh wrote:
>> On 06/27/2018 07:54 AM, Laszlo Ersek wrote:
>>> On 06/26/18 21:46, Brijesh Singh wrote:
> 
>>>> After that, any access
>>>> to the flash will end up going through the encryption engine. I did 
>>>> try hacking EDK2 to restore the C-bit
>>>
>>> (I continue to be annoyed that the memory encryption bit is not 
>>> exposed in the GCD memory space attributes explicitly.)
>>>
>>>> but that was not sufficient because UEFI runtime services are 
>>>> mapped as "encrypted" in OS page table
>>>
>>> What do you mean here? Runtime services *code* or runtime services 
>>> *data*? Code must obviously be remain encrypted (otherwise we cannot 
>>> execute it in SEV). Runtime Services Data should also be mapped as 
>>> encrypted (it is normal RAM that is not used for guest<->hypervisor 
>>> exchange).
>>
>> Sorry, I was meaning to say both the "code" and "data" are mapped as 
>> encrypted by the OS.
>>
>>>> hence we end up accessing the flash as encrypted when OS requests 
>>>> to update the variables.
>>>
>>> I don't understand the "hence" here; I don't see how the implication 
>>> follows. runtime services code and data should be encrypted. Runtime 
>>> MMIO should be un-encrypted.
>>>
>>> Ohh, wait, in MarkMemoryRangeForRuntimeAccess(), we use 
>>> "EfiGcdMemoryTypeSystemMemory". I don't have a clue why that is a 
>>> good idea. That should have been EfiGcdMemoryTypeMemoryMappedIo.
>>
>> Right, the memory is marked as 'system ram' and not 'mmio'.
>> Just to experiment, I did try changing it to 'mmio' to see if OS will 
>> map this  region as "unencrypted" but ovmf fails with below error 
>> message after changing it from systemRAM->mmio
>>
>> ConvertPages: failed to find range FFC00000 - FFFFFFFF 
>> ASSERT_EFI_ERROR (Status = Not Found) ASSERT [FvbServicesRuntimeDxe]
>> /home/amd/workdir/upstream/edk2/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FwBlockServie.c(864):
>> !EFI_ERROR (Status)
> 
> This error occurs because (I think) you modified only the 
> AddMemorySpace call. If you change the GCD type on that, then please 
> update the subsequent AllocatePages as well, from 
> EfiRuntimeServicesData to EfiMemoryMappedIO.
> 

Here is what I have.

--- a/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FwBlockService.c
+++ b/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FwBlockService.c
@@ -847,7 +847,7 @@ MarkMemoryRangeForRuntimeAccess (
                    );

    Status = gDS->AddMemorySpace (
-                  EfiGcdMemoryTypeSystemMemory,
+                  EfiGcdMemoryTypeMemoryMappedIo,
                    BaseAddress,
                    Length,
                    EFI_MEMORY_UC | EFI_MEMORY_RUNTIME @@ -856,7 +856,7 @@ MarkMemoryRangeForRuntimeAccess (

    Status = gBS->AllocatePages (
                    AllocateAddress,
-                  EfiRuntimeServicesData,
+                  EfiMemoryMappedIO,
                    EFI_SIZE_TO_PAGES (Length),
                    &BaseAddress
                    );

I am still getting the error assertion failure. I can debug to see what is going on.


> The spec says about the latter enum constant, "Used by system firmware
> to request that a memory-mapped IO region be mapped by the OS to a
> virtual address so it can be accessed by EFI runtime services." It seems
> appropriate (and I'm a bit confused why we haven't used the MMIO GCD and
> UEFI enum values for the memory type, all this time.)
> 
>> Since this efi runtime data is mapped as C=1 by the OS, hence when OS
>> asks efi to update the runtime variable we end up accessing the memory
>> region with C=1 (runtime services are executed using OS pagetable).
> 
> Indeed.
> 
> (And, this is only a problem when SMM is not used, i.e. when the full
> variable driver stack is non-SMM, just DXE. In the SMM case, the SMM
> page tables are used, and the OS cannot interfere with that.)
> 

Good point, I will try it and let you know. As you say since SMM uses
UEFI page table hence after fixing FtwNotificationEvent(..) we should be 
good.


> Anyway, in the pure DXE / runtime driver case, do you think a guest
> kernel patch will be necessary too? Perhaps if you change the UEFI
> memmap entry type (see AllocatePages above) to MMIO, then the guest
> kernel could technically honor that.
> 


Theoretically speaking, if we are able to make this memory region as
mmio then OS should be able to map it with C=0.


-Brijesh
_______________________________________________
edk2-devel mailing list
edk2-devel@lists.01.org
https://lists.01.org/mailman/listinfo/edk2-devel

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [RFC PATCH 1/1] OvmfPkg/QemuFlash: Fix Runtime variable access when SEV is enabled
  2018-06-27 17:49       ` Brijesh Singh
  2018-06-28  6:25         ` Zeng, Star
@ 2018-06-28 12:57         ` Laszlo Ersek
  2018-06-28 13:21           ` Laszlo Ersek
  2018-06-28 13:27           ` Brijesh Singh
  1 sibling, 2 replies; 15+ messages in thread
From: Laszlo Ersek @ 2018-06-28 12:57 UTC (permalink / raw)
  To: Brijesh Singh, edk2-devel
  Cc: Tom Lendacky, Star Zeng, Eric Dong, Jordan Justen (Intel address)

On 06/27/18 19:49, Brijesh Singh wrote:
> 
> 
> On 06/27/2018 11:59 AM, Laszlo Ersek wrote:
>> On 06/27/18 18:34, Brijesh Singh wrote:
>>> On 06/27/2018 07:54 AM, Laszlo Ersek wrote:
>>>> On 06/26/18 21:46, Brijesh Singh wrote:
>>
>>>>> After that, any access
>>>>> to the flash will end up going through the encryption engine. I did
>>>>> try
>>>>> hacking EDK2 to restore the C-bit
>>>>
>>>> (I continue to be annoyed that the memory encryption bit is not exposed
>>>> in the GCD memory space attributes explicitly.)
>>>>
>>>>> but that was not sufficient because UEFI
>>>>> runtime services are mapped as "encrypted" in OS page table
>>>>
>>>> What do you mean here? Runtime services *code* or runtime services
>>>> *data*? Code must obviously be remain encrypted (otherwise we cannot
>>>> execute it in SEV). Runtime Services Data should also be mapped as
>>>> encrypted (it is normal RAM that is not used for guest<->hypervisor
>>>> exchange).
>>>
>>> Sorry, I was meaning to say both the "code" and "data" are mapped as
>>> encrypted by the OS.
>>>
>>>>> hence we end up accessing the flash as encrypted when OS requests to
>>>>> update the variables.
>>>>
>>>> I don't understand the "hence" here; I don't see how the implication
>>>> follows. runtime services code and data should be encrypted. Runtime
>>>> MMIO should be un-encrypted.
>>>>
>>>> Ohh, wait, in MarkMemoryRangeForRuntimeAccess(), we use
>>>> "EfiGcdMemoryTypeSystemMemory". I don't have a clue why that is a good
>>>> idea. That should have been EfiGcdMemoryTypeMemoryMappedIo.
>>>
>>> Right, the memory is marked as 'system ram' and not 'mmio'.
>>> Just to experiment, I did try changing it to 'mmio' to see if OS will
>>> map this  region as "unencrypted" but ovmf fails with below error
>>> message after changing it from systemRAM->mmio
>>>
>>> ConvertPages: failed to find range FFC00000 - FFFFFFFF
>>> ASSERT_EFI_ERROR (Status = Not Found)
>>> ASSERT [FvbServicesRuntimeDxe]
>>> /home/amd/workdir/upstream/edk2/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FwBlockServie.c(864):
>>>
>>> !EFI_ERROR (Status)
>>
>> This error occurs because (I think) you modified only the AddMemorySpace
>> call. If you change the GCD type on that, then please update the
>> subsequent AllocatePages as well, from EfiRuntimeServicesData to
>> EfiMemoryMappedIO.
>>
> 
> Here is what I have.
> 
> --- a/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FwBlockService.c
> +++ b/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FwBlockService.c
> @@ -847,7 +847,7 @@ MarkMemoryRangeForRuntimeAccess (
>                    );
> 
>    Status = gDS->AddMemorySpace (
> -                  EfiGcdMemoryTypeSystemMemory,
> +                  EfiGcdMemoryTypeMemoryMappedIo,
>                    BaseAddress,
>                    Length,
>                    EFI_MEMORY_UC | EFI_MEMORY_RUNTIME
> @@ -856,7 +856,7 @@ MarkMemoryRangeForRuntimeAccess (
> 
>    Status = gBS->AllocatePages (
>                    AllocateAddress,
> -                  EfiRuntimeServicesData,
> +                  EfiMemoryMappedIO,
>                    EFI_SIZE_TO_PAGES (Length),
>                    &BaseAddress
>                    );
> 
> I am still getting the error assertion failure. I can debug to see what
> is going on.

Hmmm. Indeed, memory space added to GCD need not immediately show up in
the UEFI memory map, for the UEFI memory allocation services to allocate
from. IIRC, the PI spec says that *system memory* added like this *may*
show up immediately:

"""
If the memory range specified by BaseAddress and Length is of type
EfiGcdMemoryTypeSystemMemory or EfiGcdMemoryTypeMoreReliable, then the
memory range may be automatically allocated for use by the UEFI memory
services.
"""

and, indeed in edk2, it happens at once; but it doesn't apply to MMIO at
all.

So, can you replace the AllocatePages call with the following:

  Status = gDS->AllocateMemorySpace (
                  EfiGcdAllocateAddress,
                  EfiGcdMemoryTypeMemoryMappedIo,
                  0,                              // Alignment
                  EFI_SIZE_TO_PAGES (Length),
                  &BaseAddress,
                  gImageHandle,
                  NULL                            // DeviceHandle
                  );

>> The spec says about the latter enum constant, "Used by system firmware
>> to request that a memory-mapped IO region be mapped by the OS to a
>> virtual address so it can be accessed by EFI runtime services." It seems
>> appropriate (and I'm a bit confused why we haven't used the MMIO GCD and
>> UEFI enum values for the memory type, all this time.)
>>
>>> Since this efi runtime data is mapped as C=1 by the OS, hence when OS
>>> asks efi to update the runtime variable we end up accessing the memory
>>> region with C=1 (runtime services are executed using OS pagetable).
>>
>> Indeed.
>>
>> (And, this is only a problem when SMM is not used, i.e. when the full
>> variable driver stack is non-SMM, just DXE. In the SMM case, the SMM
>> page tables are used, and the OS cannot interfere with that.)
>>
> 
> Good point, I will try it and let you know. As you say since SMM uses
> UEFI page table

More correctly: SMM drivers use, at runtime as well, the page table in
SMRAM that was created by the firmware.

> hence after fixing FtwNotificationEvent(..) we should be
> good.

No, that's not precise -- FtwNotificationEvent() is not used in SMM *at
all*.

I checked that before I sent my previous email; namely,
FtwNotificationEvent() is in
"MdeModulePkg/Universal/Variable/RuntimeDxe/VariableDxe.c", which is not
built into the SMM variant of the variable driver.

Please see the variable driver stacks in OVMF:

- non-SMM:

  OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FvbServicesRuntimeDxe.inf
  OvmfPkg/EmuVariableFvbRuntimeDxe/Fvb.inf
  MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteDxe.inf
  MdeModulePkg/Universal/Variable/RuntimeDxe/VariableRuntimeDxe.inf

In the non-SMM case, we have a normal runtime driver,
VariableRuntimeDxe.inf, that implements the variable service. It relies
on the FTW driver (another runtime driver) for the FTW protocol.
Finally, both the variable driver and the FTW driver rely on the FVB
(firmware volume block) protocol, which may come from the QEMU flash
driver, or from the emulation driver. Again, all of these are runtime
drivers. "VariableRuntimeDxe.inf" includes the "VariableDxe.c" file,
hence FtwNotificationEvent() is relevant.

- SMM:

  OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FvbServicesSmm.inf
  MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteSmm.inf
  MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmm.inf
  MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmRuntimeDxe.inf

In the SMM case, we have a *split* variable driver,
"VariableSmmRuntimeDxe.inf" (which is a normal runtime driver) and
"VariableSmm.inf" (a "traditional SMM" driver). The runtime half is
unprivileged and only formats a message for the privileged SMM half,
submits the message, and then parses the response. The rest of the
variable stack, namely the privileged half of the variable driver, and
the FTW driver, and the QEMU flash (FVB) driver, all stay within SMM --
they are traditional SMM drivers too.

"VariableSmmRuntimeDxe.inf" does not include "VariableDxe.c". Instead,
"VariableSmm.inf" (the privileged half) includes "VariableSmm.c", and
that source file waits for the *SMM* FVB protocol, in the
SmmFtwNotificationEvent() function. But, this notification function,
unlike the DXE counterpart FtwNotificationEvent(), does not care about
the GCD entry attributes -- because in SMM, separate (protected) page
tables are used anyway.

If you put FtwNotificationEvent() and SmmFtwNotificationEvent() side by
side, you'll see that they perform almost identical steps:
- "Ensure [SMM] FTW protocol is installed"
- "Find the proper [SMM] FVB protocol for variable"
- "Mark the variable storage region of the FLASH as RUNTIME" -- this is
  DXE only!
- VariableWriteServiceInitialize
- "Install the Variable Write Architectural protocol"

(I skipped RecordSecureBootPolicyVarData(), because that always happens
in the DXE half. Anyway it's irrelevant here.)

Thus, my expectation is that the current issue manifests itself only if
you build OVMF without -D SMM_REQUIRE.

>> Anyway, in the pure DXE / runtime driver case, do you think a guest
>> kernel patch will be necessary too? Perhaps if you change the UEFI
>> memmap entry type (see AllocatePages above) to MMIO, then the guest
>> kernel could technically honor that.
>>
> 
> 
> Theoretically speaking, if we are able to make this memory region as
> mmio then OS should be able to map it with C=0.

Thanks
Laszlo


^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [RFC PATCH 1/1] OvmfPkg/QemuFlash: Fix Runtime variable access when SEV is enabled
  2018-06-28  6:16   ` Zeng, Star
@ 2018-06-28 13:13     ` Laszlo Ersek
  2018-06-29  2:37       ` Zeng, Star
  0 siblings, 1 reply; 15+ messages in thread
From: Laszlo Ersek @ 2018-06-28 13:13 UTC (permalink / raw)
  To: Zeng, Star, Brijesh Singh, edk2-devel@lists.01.org
  Cc: Tom Lendacky, Dong, Eric, Justen, Jordan L

On 06/28/18 08:16, Zeng, Star wrote:
> 1) My understanding is Variable Driver is managing the variable region in flash although the flash read/write/erase operations are done in flash driver. Current Variable Driver needs the address (to variable region) be converted to virtual address for runtime, and it does not assume flash driver to set the runtime attribute for variable region, but do it by itself.
> 
> 2) I agree this approach. If the runtime attribute has been set by flash driver, Variable Driver has no need to do that.

Great, thank you!

Yesterday, after I sent my email(s), I got concerned for a moment,
because it wasn't clear to me what *runtime driver* actually owned the
flash range.

However, the FVB protocol itself must be provided by a runtime driver on
UEFI/PI platforms where the variable write service implementation --
indirectly or directly -- depends on FVB, for flash writes. Indeed in
edk2, we have at least the following FVB drivers:

$ git grep -l -E \
    -e 'EfiFirmwareVolumeBlock2?ProtocolGuid' --and -e 'PROD'

EmulatorPkg/FvbServicesRuntimeDxe/FvbServicesRuntimeDxe.inf
Nt32Pkg/FvbServicesRuntimeDxe/FvbServicesRuntimeDxe.inf
OvmfPkg/EmuVariableFvbRuntimeDxe/Fvb.inf
OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FvbServicesRuntimeDxe.inf
Vlv2TbltDevicePkg/FvbRuntimeDxe/FvbRuntimeDxe.inf
Vlv2TbltDevicePkg/FvbRuntimeDxe/FvbSmmDxe.inf

All of them are DXE_RUNTIME_DRIVER modules [*] and they all call
EfiConvertPointer().

[*] "Vlv2TbltDevicePkg/FvbRuntimeDxe/FvbSmmDxe.inf" is marked as a
DXE_DRIVER (not runtime), and indeed it doesn't call
EfiConvertPointer(). I don't know what this driver is good for; the INF
file is not included in any DSC file in edk2. I guess it can be used for
flash access on an SMM platform as long as you never boot an OS / never
call ExitBootServices(). In that case however, runtime aspects have no
meaning at all.

Thanks!
Laszlo

> 
> 
> Thanks,
> Star
> -----Original Message-----
> From: edk2-devel [mailto:edk2-devel-bounces@lists.01.org] On Behalf Of Laszlo Ersek
> Sent: Wednesday, June 27, 2018 8:54 PM
> To: Brijesh Singh <brijesh.singh@amd.com>; edk2-devel@lists.01.org
> Cc: Tom Lendacky <Thomas.Lendacky@amd.com>; Dong, Eric <eric.dong@intel.com>; Zeng, Star <star.zeng@intel.com>; Justen, Jordan L <jordan.l.justen@intel.com>
> Subject: Re: [edk2] [RFC PATCH 1/1] OvmfPkg/QemuFlash: Fix Runtime variable access when SEV is enabled
> 
> On 06/26/18 21:46, Brijesh Singh wrote:
>> Problem statement:
>> ------------------
>> Fedora-28 contains 4.16 kernel -- which has all the required support 
>> to run as an SEV guest.  When the installer is launched from SEV guest 
>> then it fails to install the bootloader. The installer was failing to 
>> update the 'BootOrder' UEFI runtime variable.
>>
>> Root Cause Analysis
>> --------------------
>> Since QemuFlash storage memory is accessed by both guest and 
>> hypervisor hence we need to map this memory range as unencrypted. 
>> AmdSevDxe maps the range as "unencrypted" but later 
>> FtwNotificationEvent() in 
>> MdeModule/Universal/Variable/RuntimeDxe/VariableDxe.c resets the mapping and the memory region gets remapped as "encrypted".
> 
> Is this a new issue, or has it always been there, and we just failed to notice it?
> 
> BTW, I don't understand why FtwNotificationEvent() in "MdeModulePkg/Universal/Variable/RuntimeDxe/VariableDxe.c" has to mark the flash range as EFI_MEMORY_RUNTIME. I thought that this action belonged in the flash driver itself, and we do that already in MarkMemoryRangeForRuntimeAccess(), in file "OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FwBlockService.c".
> 
> The variable driver does not own the pflash chip (the pflash driver owns it), so I believe the variable driver shouldn't mess with the mapping attributes.
> 
> Here's a suggestion -- Star, Eric, can you please comment? In the
> FtwNotificationEvent() function, after we get the memory descriptor for the pflash range, first check whether EFI_MEMORY_RUNTIME is already set.
> If it is, don't do anything; if it isn't, add the attribute.
> 
> This should cause no observable change on any non-SEV platform, and it should remove the gDS->SetMemorySpaceAttributes() call for OVMF (where it breaks things for SEV).
> 
>> After that, any access
>> to the flash will end up going through the encryption engine. I did 
>> try hacking EDK2 to restore the C-bit
> 
> (I continue to be annoyed that the memory encryption bit is not exposed in the GCD memory space attributes explicitly.)
> 
>> but that was not sufficient because UEFI runtime services are mapped 
>> as "encrypted" in OS page table
> 
> What do you mean here? Runtime services *code* or runtime services *data*? Code must obviously be remain encrypted (otherwise we cannot execute it in SEV). Runtime Services Data should also be mapped as encrypted (it is normal RAM that is not used for guest<->hypervisor exchange).
> 
>> hence we end up accessing the flash as encrypted when OS requests to update the variables.
> 
> I don't understand the "hence" here; I don't see how the implication follows. runtime services code and data should be encrypted. Runtime MMIO should be un-encrypted.
> 
> Ohh, wait, in MarkMemoryRangeForRuntimeAccess(), we use "EfiGcdMemoryTypeSystemMemory". I don't have a clue why that is a good idea. That should have been EfiGcdMemoryTypeMemoryMappedIo.
> 
> ... Anyway, I think first we should go with the "check EFI_MEMORY_RUNTIME attribute before setting it", in FtwNotificationEvent().
> 
>>
>> A possible solution
>> ---------------------
>> To solve the issue, after QemuFlash is probed, I allocate an encrypted 
>> buffer and initialize this buffer with the contents from the flash memory.
>> When SEV is enabled, we use newly allocated encrypted buffer in
>> FwInstance->FvBase instead of the original flash region. The idea is 
>> FwInstance->if
>> caller grabs the FwInstance->FvBase pointer and tries to access the 
>> FvVolumeHeader then it should get the data from the encrypted buffer.
>> But if caller wants read/writes to/from the flash device then we 
>> internally use the original "unencrypted" flash region to access the data.
> 
> No, this is neither safe, nor a desirable design.
> 
> Safety: all accesses (via both pointers and FVB protocol members) that higher-level drives *think* go to the pflash chip must *actually* go to the pflash chip.
> 
> Design: it had taken us years to get rid of various memory-emulated fake variable stores. They *all* suck in one way or another, with various obscure UEFI spec incompatibilities and corner cases. A strictly pflash-based varstore is not what we should compromise on.
> 
>> With this
>> patch, I have verified that OS is able to update the runtime variable 
>> and
>> FC-28 installer is successfully able to complete the installation process.
>>
>> If you all agree with approach then I can rework any feedbacks and 
>> remove the rfc tag from the patch. If you have better suggestions then 
>> I am open to explore those as well.
> 
> I'd like to understand the following:
> 
> (1) why does FtwNotificationEvent() set the EFI_MEMORY_RUNTIME attribute itself, for the pflash range? -- in my opinion, that belongs in the flash driver.
> 
> (2) Whether Star and Eric agree with setting the EFI_MEMORY_RUNTIME attribute in FtwNotificationEvent() only if the attribute is not already present.
> 
> (3) The implication that you describe, between runtime services/code being mapped encrypted, and restoring the C-bit failing.
> 
> (4) Whether we should modify MarkMemoryRangeForRuntimeAccess() to install the range into GCD as MMIO. (I feel *very* uncomfortable about this, however; the current code has existed as-is for years, and regressions look very risky.)
> 
> My strong preference would be a patch for (2).
> 
> Thanks,
> Laszlo
> 
>> Cc: Laszlo Ersek <lersek@redhat.com>
>> Contributed-under: TianoCore Contribution Agreement 1.1
>> Signed-off-by: Brijesh Singh <brijesh.singh@amd.com>
>> ---
>>  .../FvbServicesRuntimeDxe.inf                      |  1 +
>>  .../FwBlockService.c                               | 37 +++++++++++++++++++---
>>  2 files changed, 34 insertions(+), 4 deletions(-)
>>
>> diff --git 
>> a/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FvbServicesRuntimeDxe.inf 
>> b/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FvbServicesRuntimeDxe.inf
>> index d7b4ec06c4e6..6bb5c2093790 100644
>> --- a/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FvbServicesRuntimeDxe.inf
>> +++ b/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FvbServicesRuntimeDxe.inf
>> @@ -54,6 +54,7 @@ [LibraryClasses]
>>    DevicePathLib
>>    DxeServicesTableLib
>>    MemoryAllocationLib
>> +  MemEncryptSevLib
>>    PcdLib
>>    UefiBootServicesTableLib
>>    UefiDriverEntryPoint
>> diff --git a/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FwBlockService.c 
>> b/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FwBlockService.c
>> index 558b395dff4a..e82b4ff70961 100644
>> --- a/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FwBlockService.c
>> +++ b/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FwBlockService.c
>> @@ -36,6 +36,7 @@
>>  #include <Library/DxeServicesTableLib.h>  #include 
>> <Library/MemoryAllocationLib.h>  #include 
>> <Library/UefiBootServicesTableLib.h>
>> +#include <Library/MemEncryptSevLib.h>
>>  
>>  #include "FwBlockService.h"
>>  #include "QemuFlash.h"
>> @@ -966,6 +967,7 @@ FvbInitialize (
>>    UINTN                               Length;
>>    UINTN                               NumOfBlocks;
>>    RETURN_STATUS                       PcdStatus;
>> +  EFI_PHYSICAL_ADDRESS                CryptedAddress;
>>  
>>    if (EFI_ERROR (QemuFlashInitialize ())) {
>>      //
>> @@ -986,6 +988,24 @@ FvbInitialize (
>>    BaseAddress = (UINTN) PcdGet32 (PcdOvmfFdBaseAddress);
>>    Length = PcdGet32 (PcdOvmfFirmwareFdSize);
>>  
>> +  //
>> +  // When SEV is enabled, allocate a encrypted buffer which will 
>> + contain a  // encrypted copy of the Flash image.
>> +  //
>> +  if (MemEncryptSevIsEnabled ()) {
>> +    Status = gBS->AllocatePages (
>> +                    AllocateAnyPages,
>> +                    EfiRuntimeServicesData,
>> +                    EFI_SIZE_TO_PAGES(PcdGet32 (PcdOvmfFirmwareFdSize)),
>> +                    &CryptedAddress
>> +                    );
>> +    ASSERT_EFI_ERROR (Status);
>> +
>> +    CopyMem((VOID *)CryptedAddress, (VOID *)BaseAddress, Length);
>> +
>> +    BaseAddress = CryptedAddress;
>> +  }
>> +
>>    Status = InitializeVariableFvHeader ();
>>    if (EFI_ERROR (Status)) {
>>      DEBUG ((EFI_D_INFO,
>> @@ -1091,24 +1111,33 @@ FvbInitialize (
>>    //
>>    InstallProtocolInterfaces (FvbDevice);
>>  
>> -  MarkMemoryRangeForRuntimeAccess (BaseAddress, Length);
>> +  MarkMemoryRangeForRuntimeAccess (
>> +    (UINTN) PcdGet32 (PcdOvmfFdBaseAddress),
>> +    Length
>> +    );
>>  
>>    //
>>    // Set several PCD values to point to flash
>>    //
>>    PcdStatus = PcdSet64S (
>>      PcdFlashNvStorageVariableBase64,
>> -    (UINTN) PcdGet32 (PcdOvmfFlashNvStorageVariableBase)
>> +    BaseAddress
>>      );
>>    ASSERT_RETURN_ERROR (PcdStatus);
>>    PcdStatus = PcdSet32S (
>>      PcdFlashNvStorageFtwWorkingBase,
>> -    PcdGet32 (PcdOvmfFlashNvStorageFtwWorkingBase)
>> +    BaseAddress +
>> +    PcdGet32(PcdFlashNvStorageVariableSize) +
>> +    PcdGet32(PcdOvmfFlashNvStorageEventLogSize)
>>      );
>> +
>>    ASSERT_RETURN_ERROR (PcdStatus);
>>    PcdStatus = PcdSet32S (
>>      PcdFlashNvStorageFtwSpareBase,
>> -    PcdGet32 (PcdOvmfFlashNvStorageFtwSpareBase)
>> +    BaseAddress +
>> +    PcdGet32(PcdFlashNvStorageVariableSize) +
>> +    PcdGet32(PcdOvmfFlashNvStorageEventLogSize) +
>> +    PcdGet32(PcdFlashNvStorageFtwWorkingSize)
>>      );
>>    ASSERT_RETURN_ERROR (PcdStatus);
>>  
>>
> 
> _______________________________________________
> edk2-devel mailing list
> edk2-devel@lists.01.org
> https://lists.01.org/mailman/listinfo/edk2-devel
> 



^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [RFC PATCH 1/1] OvmfPkg/QemuFlash: Fix Runtime variable access when SEV is enabled
  2018-06-28  6:25         ` Zeng, Star
@ 2018-06-28 13:15           ` Laszlo Ersek
  0 siblings, 0 replies; 15+ messages in thread
From: Laszlo Ersek @ 2018-06-28 13:15 UTC (permalink / raw)
  To: Zeng, Star, Brijesh Singh, edk2-devel@lists.01.org
  Cc: Tom Lendacky, Dong, Eric, Justen, Jordan L

On 06/28/18 08:25, Zeng, Star wrote:
> My understanding is MMIO is not managed by UEFI memory services, but GCD services.
> PI spec says " If the memory range specified by BaseAddress and Length is of type EfiGcdMemoryTypeSystemMemory or EfiGcdMemoryTypeMoreReliable, then the memory range may be automatically *allocated* for use by the *UEFI memory services*." in AddMemorySpace() description.
> 
> For MMIO, the code needs to use AddMemorySpace() + AllocateMemorySpace().

Right, after seeing today the (updated) AllocatePages() failure,
reported by Brijesh, I came to the same conclusion. Thank you for
correcting my initial suggestion! It's not easy to keep this details in
mind :)

Laszlo


^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [RFC PATCH 1/1] OvmfPkg/QemuFlash: Fix Runtime variable access when SEV is enabled
  2018-06-28 12:57         ` Laszlo Ersek
@ 2018-06-28 13:21           ` Laszlo Ersek
  2018-06-28 13:27           ` Brijesh Singh
  1 sibling, 0 replies; 15+ messages in thread
From: Laszlo Ersek @ 2018-06-28 13:21 UTC (permalink / raw)
  To: Brijesh Singh, edk2-devel
  Cc: Tom Lendacky, Star Zeng, Eric Dong, Jordan Justen (Intel address)

On 06/28/18 14:57, Laszlo Ersek wrote:
> On 06/27/18 19:49, Brijesh Singh wrote:
>>
>>
>> On 06/27/2018 11:59 AM, Laszlo Ersek wrote:
>>> On 06/27/18 18:34, Brijesh Singh wrote:
>>>> On 06/27/2018 07:54 AM, Laszlo Ersek wrote:
>>>>> On 06/26/18 21:46, Brijesh Singh wrote:
>>>
>>>>>> After that, any access
>>>>>> to the flash will end up going through the encryption engine. I did
>>>>>> try
>>>>>> hacking EDK2 to restore the C-bit
>>>>>
>>>>> (I continue to be annoyed that the memory encryption bit is not exposed
>>>>> in the GCD memory space attributes explicitly.)
>>>>>
>>>>>> but that was not sufficient because UEFI
>>>>>> runtime services are mapped as "encrypted" in OS page table
>>>>>
>>>>> What do you mean here? Runtime services *code* or runtime services
>>>>> *data*? Code must obviously be remain encrypted (otherwise we cannot
>>>>> execute it in SEV). Runtime Services Data should also be mapped as
>>>>> encrypted (it is normal RAM that is not used for guest<->hypervisor
>>>>> exchange).
>>>>
>>>> Sorry, I was meaning to say both the "code" and "data" are mapped as
>>>> encrypted by the OS.
>>>>
>>>>>> hence we end up accessing the flash as encrypted when OS requests to
>>>>>> update the variables.
>>>>>
>>>>> I don't understand the "hence" here; I don't see how the implication
>>>>> follows. runtime services code and data should be encrypted. Runtime
>>>>> MMIO should be un-encrypted.
>>>>>
>>>>> Ohh, wait, in MarkMemoryRangeForRuntimeAccess(), we use
>>>>> "EfiGcdMemoryTypeSystemMemory". I don't have a clue why that is a good
>>>>> idea. That should have been EfiGcdMemoryTypeMemoryMappedIo.
>>>>
>>>> Right, the memory is marked as 'system ram' and not 'mmio'.
>>>> Just to experiment, I did try changing it to 'mmio' to see if OS will
>>>> map this  region as "unencrypted" but ovmf fails with below error
>>>> message after changing it from systemRAM->mmio
>>>>
>>>> ConvertPages: failed to find range FFC00000 - FFFFFFFF
>>>> ASSERT_EFI_ERROR (Status = Not Found)
>>>> ASSERT [FvbServicesRuntimeDxe]
>>>> /home/amd/workdir/upstream/edk2/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FwBlockServie.c(864):
>>>>
>>>> !EFI_ERROR (Status)
>>>
>>> This error occurs because (I think) you modified only the AddMemorySpace
>>> call. If you change the GCD type on that, then please update the
>>> subsequent AllocatePages as well, from EfiRuntimeServicesData to
>>> EfiMemoryMappedIO.
>>>
>>
>> Here is what I have.
>>
>> --- a/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FwBlockService.c
>> +++ b/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FwBlockService.c
>> @@ -847,7 +847,7 @@ MarkMemoryRangeForRuntimeAccess (
>>                    );
>>
>>    Status = gDS->AddMemorySpace (
>> -                  EfiGcdMemoryTypeSystemMemory,
>> +                  EfiGcdMemoryTypeMemoryMappedIo,
>>                    BaseAddress,
>>                    Length,
>>                    EFI_MEMORY_UC | EFI_MEMORY_RUNTIME
>> @@ -856,7 +856,7 @@ MarkMemoryRangeForRuntimeAccess (
>>
>>    Status = gBS->AllocatePages (
>>                    AllocateAddress,
>> -                  EfiRuntimeServicesData,
>> +                  EfiMemoryMappedIO,
>>                    EFI_SIZE_TO_PAGES (Length),
>>                    &BaseAddress
>>                    );
>>
>> I am still getting the error assertion failure. I can debug to see what
>> is going on.
> 
> Hmmm. Indeed, memory space added to GCD need not immediately show up in
> the UEFI memory map, for the UEFI memory allocation services to allocate
> from. IIRC, the PI spec says that *system memory* added like this *may*
> show up immediately:
> 
> """
> If the memory range specified by BaseAddress and Length is of type
> EfiGcdMemoryTypeSystemMemory or EfiGcdMemoryTypeMoreReliable, then the
> memory range may be automatically allocated for use by the UEFI memory
> services.
> """
> 
> and, indeed in edk2, it happens at once; but it doesn't apply to MMIO at
> all.
> 
> So, can you replace the AllocatePages call with the following:
> 
>   Status = gDS->AllocateMemorySpace (
>                   EfiGcdAllocateAddress,
>                   EfiGcdMemoryTypeMemoryMappedIo,
>                   0,                              // Alignment
>                   EFI_SIZE_TO_PAGES (Length),
>                   &BaseAddress,
>                   gImageHandle,
>                   NULL                            // DeviceHandle
>                   );
> 
>>> The spec says about the latter enum constant, "Used by system firmware
>>> to request that a memory-mapped IO region be mapped by the OS to a
>>> virtual address so it can be accessed by EFI runtime services." It seems
>>> appropriate (and I'm a bit confused why we haven't used the MMIO GCD and
>>> UEFI enum values for the memory type, all this time.)
>>>
>>>> Since this efi runtime data is mapped as C=1 by the OS, hence when OS
>>>> asks efi to update the runtime variable we end up accessing the memory
>>>> region with C=1 (runtime services are executed using OS pagetable).
>>>
>>> Indeed.
>>>
>>> (And, this is only a problem when SMM is not used, i.e. when the full
>>> variable driver stack is non-SMM, just DXE. In the SMM case, the SMM
>>> page tables are used, and the OS cannot interfere with that.)
>>>
>>
>> Good point, I will try it and let you know. As you say since SMM uses
>> UEFI page table
> 
> More correctly: SMM drivers use, at runtime as well, the page table in
> SMRAM that was created by the firmware.
> 
>> hence after fixing FtwNotificationEvent(..) we should be
>> good.
> 
> No, that's not precise -- FtwNotificationEvent() is not used in SMM *at
> all*.

Sigh, I misunderstood you. You meant, "after fixing DXE, we should be
good, because the the issue only affects DXE".

I mistook your statement as "after we fix DXE, both DXE and SMM will be
OK, because the fix affects both DXE and SMM". That was not a correct
statement (because the fix only affects DXE; SMM is unaffected and needs
no fix), which is why I attempted to correct it. Of course, you never
*said* that statement. :)

Sorry about the noise!
Laszlo


^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [RFC PATCH 1/1] OvmfPkg/QemuFlash: Fix Runtime variable access when SEV is enabled
  2018-06-28 12:57         ` Laszlo Ersek
  2018-06-28 13:21           ` Laszlo Ersek
@ 2018-06-28 13:27           ` Brijesh Singh
  1 sibling, 0 replies; 15+ messages in thread
From: Brijesh Singh @ 2018-06-28 13:27 UTC (permalink / raw)
  To: Laszlo Ersek, edk2-devel
  Cc: brijesh.singh, Tom Lendacky, Star Zeng, Eric Dong,
	Jordan Justen (Intel address)



On 06/28/2018 07:57 AM, Laszlo Ersek wrote:

[...]

>>
>> --- a/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FwBlockService.c
>> +++ b/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FwBlockService.c
>> @@ -847,7 +847,7 @@ MarkMemoryRangeForRuntimeAccess (
>>                     );
>>
>>     Status = gDS->AddMemorySpace (
>> -                  EfiGcdMemoryTypeSystemMemory,
>> +                  EfiGcdMemoryTypeMemoryMappedIo,
>>                     BaseAddress,
>>                     Length,
>>                     EFI_MEMORY_UC | EFI_MEMORY_RUNTIME
>> @@ -856,7 +856,7 @@ MarkMemoryRangeForRuntimeAccess (
>>
>>     Status = gBS->AllocatePages (
>>                     AllocateAddress,
>> -                  EfiRuntimeServicesData,
>> +                  EfiMemoryMappedIO,
>>                     EFI_SIZE_TO_PAGES (Length),
>>                     &BaseAddress
>>                     );
>>
>> I am still getting the error assertion failure. I can debug to see what
>> is going on.
> 
> Hmmm. Indeed, memory space added to GCD need not immediately show up in
> the UEFI memory map, for the UEFI memory allocation services to allocate
> from. IIRC, the PI spec says that *system memory* added like this *may*
> show up immediately:
> 
> """
> If the memory range specified by BaseAddress and Length is of type
> EfiGcdMemoryTypeSystemMemory or EfiGcdMemoryTypeMoreReliable, then the
> memory range may be automatically allocated for use by the UEFI memory
> services.
> """
> 
> and, indeed in edk2, it happens at once; but it doesn't apply to MMIO at
> all.
> 
> So, can you replace the AllocatePages call with the following:
> 
>    Status = gDS->AllocateMemorySpace (
>                    EfiGcdAllocateAddress,
>                    EfiGcdMemoryTypeMemoryMappedIo,
>                    0,                              // Alignment
>                    EFI_SIZE_TO_PAGES (Length),
>                    &BaseAddress,
>                    gImageHandle,
>                    NULL                            // DeviceHandle
>                    );
> 


I did realized this in my yesterday's debug. I was looking at other
drivers (mainly PciBridge...) on how it adds the MMIO. I no longer
get the ASSERT. thank you !

After changing this system boots fine but I am getting Linux kernel
crashes when we attempt to update the UEFI runtime variables. I am
still debugging and trying to to root cause it.



>>
>> Good point, I will try it and let you know. As you say since SMM uses
>> UEFI page table
> 
> More correctly: SMM drivers use, at runtime as well, the page table in
> SMRAM that was created by the firmware.
> 
>> hence after fixing FtwNotificationEvent(..) we should be
>> good.
> 
> No, that's not precise -- FtwNotificationEvent() is not used in SMM *at
> all*.
> 
> I checked that before I sent my previous email; namely,
> FtwNotificationEvent() is in
> "MdeModulePkg/Universal/Variable/RuntimeDxe/VariableDxe.c", which is not
> built into the SMM variant of the variable driver.
> 
> Please see the variable driver stacks in OVMF:
> 
> - non-SMM:
> 
>    OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FvbServicesRuntimeDxe.inf
>    OvmfPkg/EmuVariableFvbRuntimeDxe/Fvb.inf
>    MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteDxe.inf
>    MdeModulePkg/Universal/Variable/RuntimeDxe/VariableRuntimeDxe.inf
> 
> In the non-SMM case, we have a normal runtime driver,
> VariableRuntimeDxe.inf, that implements the variable service. It relies
> on the FTW driver (another runtime driver) for the FTW protocol.
> Finally, both the variable driver and the FTW driver rely on the FVB
> (firmware volume block) protocol, which may come from the QEMU flash
> driver, or from the emulation driver. Again, all of these are runtime
> drivers. "VariableRuntimeDxe.inf" includes the "VariableDxe.c" file,
> hence FtwNotificationEvent() is relevant.
> 
> - SMM:
> 
>    OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FvbServicesSmm.inf
>    MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteSmm.inf
>    MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmm.inf
>    MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmRuntimeDxe.inf
> 
> In the SMM case, we have a *split* variable driver,
> "VariableSmmRuntimeDxe.inf" (which is a normal runtime driver) and
> "VariableSmm.inf" (a "traditional SMM" driver). The runtime half is
> unprivileged and only formats a message for the privileged SMM half,
> submits the message, and then parses the response. The rest of the
> variable stack, namely the privileged half of the variable driver, and
> the FTW driver, and the QEMU flash (FVB) driver, all stay within SMM --
> they are traditional SMM drivers too.
> 
> "VariableSmmRuntimeDxe.inf" does not include "VariableDxe.c". Instead,
> "VariableSmm.inf" (the privileged half) includes "VariableSmm.c", and
> that source file waits for the *SMM* FVB protocol, in the
> SmmFtwNotificationEvent() function. But, this notification function,
> unlike the DXE counterpart FtwNotificationEvent(), does not care about
> the GCD entry attributes -- because in SMM, separate (protected) page
> tables are used anyway.
> 
> If you put FtwNotificationEvent() and SmmFtwNotificationEvent() side by
> side, you'll see that they perform almost identical steps:
> - "Ensure [SMM] FTW protocol is installed"
> - "Find the proper [SMM] FVB protocol for variable"
> - "Mark the variable storage region of the FLASH as RUNTIME" -- this is
>    DXE only!
> - VariableWriteServiceInitialize
> - "Install the Variable Write Architectural protocol"
> 
> (I skipped RecordSecureBootPolicyVarData(), because that always happens
> in the DXE half. Anyway it's irrelevant here.)
> 
> Thus, my expectation is that the current issue manifests itself only if
> you build OVMF without -D SMM_REQUIRE.
> 


Actually I was not aware that there are two different stack (SMM vs
non-SMM). And you are absolutely right. The issue does not happen
when using -D SMM_REQUIRE flag. So, I think we just need to focus
on making non SMM work.

-Brijesh



^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [RFC PATCH 1/1] OvmfPkg/QemuFlash: Fix Runtime variable access when SEV is enabled
  2018-06-28 13:13     ` Laszlo Ersek
@ 2018-06-29  2:37       ` Zeng, Star
  0 siblings, 0 replies; 15+ messages in thread
From: Zeng, Star @ 2018-06-29  2:37 UTC (permalink / raw)
  To: Laszlo Ersek, Brijesh Singh, edk2-devel@lists.01.org
  Cc: Tom Lendacky, Dong, Eric, Justen, Jordan L, Zeng, Star

My understading:

Vlv2TbltDevicePkg/FvbRuntimeDxe/FvbSmmDxe.inf is DXE counterpart of Vlv2TbltDevicePkg/FvbRuntimeDxe/FvbSmm.inf, to produce EfiFirmwareVolumeBlock2?ProtocolGuid based on gEfiSmmFirmwareVolumeBlockProtocolGuid by SMM comm.

Platform can choose FvbSmmDxe + FvbSmm, or FvbRuntimeDxe + FvbSmm.


Thanks,
Star
-----Original Message-----
From: edk2-devel [mailto:edk2-devel-bounces@lists.01.org] On Behalf Of Laszlo Ersek
Sent: Thursday, June 28, 2018 9:14 PM
To: Zeng, Star <star.zeng@intel.com>; Brijesh Singh <brijesh.singh@amd.com>; edk2-devel@lists.01.org
Cc: Tom Lendacky <Thomas.Lendacky@amd.com>; Dong, Eric <eric.dong@intel.com>; Justen, Jordan L <jordan.l.justen@intel.com>
Subject: Re: [edk2] [RFC PATCH 1/1] OvmfPkg/QemuFlash: Fix Runtime variable access when SEV is enabled

On 06/28/18 08:16, Zeng, Star wrote:
> 1) My understanding is Variable Driver is managing the variable region in flash although the flash read/write/erase operations are done in flash driver. Current Variable Driver needs the address (to variable region) be converted to virtual address for runtime, and it does not assume flash driver to set the runtime attribute for variable region, but do it by itself.
> 
> 2) I agree this approach. If the runtime attribute has been set by flash driver, Variable Driver has no need to do that.

Great, thank you!

Yesterday, after I sent my email(s), I got concerned for a moment, because it wasn't clear to me what *runtime driver* actually owned the flash range.

However, the FVB protocol itself must be provided by a runtime driver on UEFI/PI platforms where the variable write service implementation -- indirectly or directly -- depends on FVB, for flash writes. Indeed in edk2, we have at least the following FVB drivers:

$ git grep -l -E \
    -e 'EfiFirmwareVolumeBlock2?ProtocolGuid' --and -e 'PROD'

EmulatorPkg/FvbServicesRuntimeDxe/FvbServicesRuntimeDxe.inf
Nt32Pkg/FvbServicesRuntimeDxe/FvbServicesRuntimeDxe.inf
OvmfPkg/EmuVariableFvbRuntimeDxe/Fvb.inf
OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FvbServicesRuntimeDxe.inf
Vlv2TbltDevicePkg/FvbRuntimeDxe/FvbRuntimeDxe.inf
Vlv2TbltDevicePkg/FvbRuntimeDxe/FvbSmmDxe.inf

All of them are DXE_RUNTIME_DRIVER modules [*] and they all call EfiConvertPointer().

[*] "Vlv2TbltDevicePkg/FvbRuntimeDxe/FvbSmmDxe.inf" is marked as a DXE_DRIVER (not runtime), and indeed it doesn't call EfiConvertPointer(). I don't know what this driver is good for; the INF file is not included in any DSC file in edk2. I guess it can be used for flash access on an SMM platform as long as you never boot an OS / never call ExitBootServices(). In that case however, runtime aspects have no meaning at all.

Thanks!
Laszlo

> 
> 
> Thanks,
> Star
> -----Original Message-----
> From: edk2-devel [mailto:edk2-devel-bounces@lists.01.org] On Behalf Of 
> Laszlo Ersek
> Sent: Wednesday, June 27, 2018 8:54 PM
> To: Brijesh Singh <brijesh.singh@amd.com>; edk2-devel@lists.01.org
> Cc: Tom Lendacky <Thomas.Lendacky@amd.com>; Dong, Eric 
> <eric.dong@intel.com>; Zeng, Star <star.zeng@intel.com>; Justen, 
> Jordan L <jordan.l.justen@intel.com>
> Subject: Re: [edk2] [RFC PATCH 1/1] OvmfPkg/QemuFlash: Fix Runtime 
> variable access when SEV is enabled
> 
> On 06/26/18 21:46, Brijesh Singh wrote:
>> Problem statement:
>> ------------------
>> Fedora-28 contains 4.16 kernel -- which has all the required support 
>> to run as an SEV guest.  When the installer is launched from SEV 
>> guest then it fails to install the bootloader. The installer was 
>> failing to update the 'BootOrder' UEFI runtime variable.
>>
>> Root Cause Analysis
>> --------------------
>> Since QemuFlash storage memory is accessed by both guest and 
>> hypervisor hence we need to map this memory range as unencrypted.
>> AmdSevDxe maps the range as "unencrypted" but later
>> FtwNotificationEvent() in
>> MdeModule/Universal/Variable/RuntimeDxe/VariableDxe.c resets the mapping and the memory region gets remapped as "encrypted".
> 
> Is this a new issue, or has it always been there, and we just failed to notice it?
> 
> BTW, I don't understand why FtwNotificationEvent() in "MdeModulePkg/Universal/Variable/RuntimeDxe/VariableDxe.c" has to mark the flash range as EFI_MEMORY_RUNTIME. I thought that this action belonged in the flash driver itself, and we do that already in MarkMemoryRangeForRuntimeAccess(), in file "OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FwBlockService.c".
> 
> The variable driver does not own the pflash chip (the pflash driver owns it), so I believe the variable driver shouldn't mess with the mapping attributes.
> 
> Here's a suggestion -- Star, Eric, can you please comment? In the
> FtwNotificationEvent() function, after we get the memory descriptor for the pflash range, first check whether EFI_MEMORY_RUNTIME is already set.
> If it is, don't do anything; if it isn't, add the attribute.
> 
> This should cause no observable change on any non-SEV platform, and it should remove the gDS->SetMemorySpaceAttributes() call for OVMF (where it breaks things for SEV).
> 
>> After that, any access
>> to the flash will end up going through the encryption engine. I did 
>> try hacking EDK2 to restore the C-bit
> 
> (I continue to be annoyed that the memory encryption bit is not 
> exposed in the GCD memory space attributes explicitly.)
> 
>> but that was not sufficient because UEFI runtime services are mapped 
>> as "encrypted" in OS page table
> 
> What do you mean here? Runtime services *code* or runtime services *data*? Code must obviously be remain encrypted (otherwise we cannot execute it in SEV). Runtime Services Data should also be mapped as encrypted (it is normal RAM that is not used for guest<->hypervisor exchange).
> 
>> hence we end up accessing the flash as encrypted when OS requests to update the variables.
> 
> I don't understand the "hence" here; I don't see how the implication follows. runtime services code and data should be encrypted. Runtime MMIO should be un-encrypted.
> 
> Ohh, wait, in MarkMemoryRangeForRuntimeAccess(), we use "EfiGcdMemoryTypeSystemMemory". I don't have a clue why that is a good idea. That should have been EfiGcdMemoryTypeMemoryMappedIo.
> 
> ... Anyway, I think first we should go with the "check EFI_MEMORY_RUNTIME attribute before setting it", in FtwNotificationEvent().
> 
>>
>> A possible solution
>> ---------------------
>> To solve the issue, after QemuFlash is probed, I allocate an 
>> encrypted buffer and initialize this buffer with the contents from the flash memory.
>> When SEV is enabled, we use newly allocated encrypted buffer in
>> FwInstance->FvBase instead of the original flash region. The idea is 
>> FwInstance->if
>> caller grabs the FwInstance->FvBase pointer and tries to access the 
>> FvVolumeHeader then it should get the data from the encrypted buffer.
>> But if caller wants read/writes to/from the flash device then we 
>> internally use the original "unencrypted" flash region to access the data.
> 
> No, this is neither safe, nor a desirable design.
> 
> Safety: all accesses (via both pointers and FVB protocol members) that higher-level drives *think* go to the pflash chip must *actually* go to the pflash chip.
> 
> Design: it had taken us years to get rid of various memory-emulated fake variable stores. They *all* suck in one way or another, with various obscure UEFI spec incompatibilities and corner cases. A strictly pflash-based varstore is not what we should compromise on.
> 
>> With this
>> patch, I have verified that OS is able to update the runtime variable 
>> and
>> FC-28 installer is successfully able to complete the installation process.
>>
>> If you all agree with approach then I can rework any feedbacks and 
>> remove the rfc tag from the patch. If you have better suggestions 
>> then I am open to explore those as well.
> 
> I'd like to understand the following:
> 
> (1) why does FtwNotificationEvent() set the EFI_MEMORY_RUNTIME attribute itself, for the pflash range? -- in my opinion, that belongs in the flash driver.
> 
> (2) Whether Star and Eric agree with setting the EFI_MEMORY_RUNTIME attribute in FtwNotificationEvent() only if the attribute is not already present.
> 
> (3) The implication that you describe, between runtime services/code being mapped encrypted, and restoring the C-bit failing.
> 
> (4) Whether we should modify MarkMemoryRangeForRuntimeAccess() to 
> install the range into GCD as MMIO. (I feel *very* uncomfortable about 
> this, however; the current code has existed as-is for years, and 
> regressions look very risky.)
> 
> My strong preference would be a patch for (2).
> 
> Thanks,
> Laszlo
> 
>> Cc: Laszlo Ersek <lersek@redhat.com>
>> Contributed-under: TianoCore Contribution Agreement 1.1
>> Signed-off-by: Brijesh Singh <brijesh.singh@amd.com>
>> ---
>>  .../FvbServicesRuntimeDxe.inf                      |  1 +
>>  .../FwBlockService.c                               | 37 +++++++++++++++++++---
>>  2 files changed, 34 insertions(+), 4 deletions(-)
>>
>> diff --git
>> a/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FvbServicesRuntimeDxe.inf
>> b/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FvbServicesRuntimeDxe.inf
>> index d7b4ec06c4e6..6bb5c2093790 100644
>> --- 
>> a/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FvbServicesRuntimeDxe.inf
>> +++ b/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FvbServicesRuntimeDxe.in
>> +++ f
>> @@ -54,6 +54,7 @@ [LibraryClasses]
>>    DevicePathLib
>>    DxeServicesTableLib
>>    MemoryAllocationLib
>> +  MemEncryptSevLib
>>    PcdLib
>>    UefiBootServicesTableLib
>>    UefiDriverEntryPoint
>> diff --git a/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FwBlockService.c
>> b/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FwBlockService.c
>> index 558b395dff4a..e82b4ff70961 100644
>> --- a/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FwBlockService.c
>> +++ b/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FwBlockService.c
>> @@ -36,6 +36,7 @@
>>  #include <Library/DxeServicesTableLib.h>  #include 
>> <Library/MemoryAllocationLib.h>  #include 
>> <Library/UefiBootServicesTableLib.h>
>> +#include <Library/MemEncryptSevLib.h>
>>  
>>  #include "FwBlockService.h"
>>  #include "QemuFlash.h"
>> @@ -966,6 +967,7 @@ FvbInitialize (
>>    UINTN                               Length;
>>    UINTN                               NumOfBlocks;
>>    RETURN_STATUS                       PcdStatus;
>> +  EFI_PHYSICAL_ADDRESS                CryptedAddress;
>>  
>>    if (EFI_ERROR (QemuFlashInitialize ())) {
>>      //
>> @@ -986,6 +988,24 @@ FvbInitialize (
>>    BaseAddress = (UINTN) PcdGet32 (PcdOvmfFdBaseAddress);
>>    Length = PcdGet32 (PcdOvmfFirmwareFdSize);
>>  
>> +  //
>> +  // When SEV is enabled, allocate a encrypted buffer which will 
>> + contain a  // encrypted copy of the Flash image.
>> +  //
>> +  if (MemEncryptSevIsEnabled ()) {
>> +    Status = gBS->AllocatePages (
>> +                    AllocateAnyPages,
>> +                    EfiRuntimeServicesData,
>> +                    EFI_SIZE_TO_PAGES(PcdGet32 (PcdOvmfFirmwareFdSize)),
>> +                    &CryptedAddress
>> +                    );
>> +    ASSERT_EFI_ERROR (Status);
>> +
>> +    CopyMem((VOID *)CryptedAddress, (VOID *)BaseAddress, Length);
>> +
>> +    BaseAddress = CryptedAddress;
>> +  }
>> +
>>    Status = InitializeVariableFvHeader ();
>>    if (EFI_ERROR (Status)) {
>>      DEBUG ((EFI_D_INFO,
>> @@ -1091,24 +1111,33 @@ FvbInitialize (
>>    //
>>    InstallProtocolInterfaces (FvbDevice);
>>  
>> -  MarkMemoryRangeForRuntimeAccess (BaseAddress, Length);
>> +  MarkMemoryRangeForRuntimeAccess (
>> +    (UINTN) PcdGet32 (PcdOvmfFdBaseAddress),
>> +    Length
>> +    );
>>  
>>    //
>>    // Set several PCD values to point to flash
>>    //
>>    PcdStatus = PcdSet64S (
>>      PcdFlashNvStorageVariableBase64,
>> -    (UINTN) PcdGet32 (PcdOvmfFlashNvStorageVariableBase)
>> +    BaseAddress
>>      );
>>    ASSERT_RETURN_ERROR (PcdStatus);
>>    PcdStatus = PcdSet32S (
>>      PcdFlashNvStorageFtwWorkingBase,
>> -    PcdGet32 (PcdOvmfFlashNvStorageFtwWorkingBase)
>> +    BaseAddress +
>> +    PcdGet32(PcdFlashNvStorageVariableSize) +
>> +    PcdGet32(PcdOvmfFlashNvStorageEventLogSize)
>>      );
>> +
>>    ASSERT_RETURN_ERROR (PcdStatus);
>>    PcdStatus = PcdSet32S (
>>      PcdFlashNvStorageFtwSpareBase,
>> -    PcdGet32 (PcdOvmfFlashNvStorageFtwSpareBase)
>> +    BaseAddress +
>> +    PcdGet32(PcdFlashNvStorageVariableSize) +
>> +    PcdGet32(PcdOvmfFlashNvStorageEventLogSize) +
>> +    PcdGet32(PcdFlashNvStorageFtwWorkingSize)
>>      );
>>    ASSERT_RETURN_ERROR (PcdStatus);
>>  
>>
> 
> _______________________________________________
> edk2-devel mailing list
> edk2-devel@lists.01.org
> https://lists.01.org/mailman/listinfo/edk2-devel
> 

_______________________________________________
edk2-devel mailing list
edk2-devel@lists.01.org
https://lists.01.org/mailman/listinfo/edk2-devel


^ permalink raw reply	[flat|nested] 15+ messages in thread

end of thread, other threads:[~2018-06-29  2:37 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2018-06-26 19:46 [RFC PATCH 1/1] OvmfPkg/QemuFlash: Fix Runtime variable access when SEV is enabled Brijesh Singh
2018-06-27 12:54 ` Laszlo Ersek
2018-06-27 16:34   ` Brijesh Singh
2018-06-27 16:37     ` Brijesh Singh
2018-06-27 16:59     ` Laszlo Ersek
2018-06-27 17:49       ` Brijesh Singh
2018-06-28  6:25         ` Zeng, Star
2018-06-28 13:15           ` Laszlo Ersek
2018-06-28 12:57         ` Laszlo Ersek
2018-06-28 13:21           ` Laszlo Ersek
2018-06-28 13:27           ` Brijesh Singh
2018-06-28  6:16   ` Zeng, Star
2018-06-28 13:13     ` Laszlo Ersek
2018-06-29  2:37       ` Zeng, Star
  -- strict thread matches above, loose matches on Subject: below --
2018-06-26 19:39 Brijesh Singh

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox