BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=3008
The original SEV-ES support missed updating the QemuFlashEraseBlock()
function to successfully erase blocks. Update QemuFlashEraseBlock() to
call the QemuFlashPtrWrite() to be able to successfully perform the
commands under SEV-ES.
Fixes: 437eb3f7a8
Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Cc: Ard Biesheuvel <ard.biesheuvel@arm.com>
Reviewed-by: Laszlo Ersek <lersek@redhat.com>
Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
Message-Id: <309c5317a3107bd0e650be20731842a2e1d4b59a.1604685192.git.thomas.lendacky@amd.com>
		
	
		
			
				
	
	
		
			275 lines
		
	
	
		
			6.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			275 lines
		
	
	
		
			6.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /** @file
 | |
|   OVMF support for QEMU system firmware flash device
 | |
| 
 | |
|   Copyright (c) 2009 - 2013, Intel Corporation. All rights reserved.<BR>
 | |
| 
 | |
|   SPDX-License-Identifier: BSD-2-Clause-Patent
 | |
| 
 | |
| **/
 | |
| 
 | |
| #include <Library/BaseMemoryLib.h>
 | |
| #include <Library/DebugLib.h>
 | |
| #include <Library/MemEncryptSevLib.h>
 | |
| #include <Library/PcdLib.h>
 | |
| 
 | |
| #include "QemuFlash.h"
 | |
| 
 | |
| #define WRITE_BYTE_CMD           0x10
 | |
| #define BLOCK_ERASE_CMD          0x20
 | |
| #define CLEAR_STATUS_CMD         0x50
 | |
| #define READ_STATUS_CMD          0x70
 | |
| #define READ_DEVID_CMD           0x90
 | |
| #define BLOCK_ERASE_CONFIRM_CMD  0xd0
 | |
| #define READ_ARRAY_CMD           0xff
 | |
| 
 | |
| #define CLEARED_ARRAY_STATUS  0x00
 | |
| 
 | |
| 
 | |
| UINT8 *mFlashBase;
 | |
| 
 | |
| STATIC UINTN       mFdBlockSize = 0;
 | |
| STATIC UINTN       mFdBlockCount = 0;
 | |
| 
 | |
| STATIC
 | |
| volatile UINT8*
 | |
| QemuFlashPtr (
 | |
|   IN        EFI_LBA                             Lba,
 | |
|   IN        UINTN                               Offset
 | |
|   )
 | |
| {
 | |
|   return mFlashBase + ((UINTN)Lba * mFdBlockSize) + Offset;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Determines if the QEMU flash memory device is present.
 | |
| 
 | |
|   @retval FALSE   The QEMU flash device is not present.
 | |
|   @retval TRUE    The QEMU flash device is present.
 | |
| 
 | |
| **/
 | |
| STATIC
 | |
| BOOLEAN
 | |
| QemuFlashDetected (
 | |
|   VOID
 | |
|   )
 | |
| {
 | |
|   BOOLEAN  FlashDetected;
 | |
|   volatile UINT8  *Ptr;
 | |
| 
 | |
|   UINTN Offset;
 | |
|   UINT8 OriginalUint8;
 | |
|   UINT8 ProbeUint8;
 | |
| 
 | |
|   FlashDetected = FALSE;
 | |
|   Ptr = QemuFlashPtr (0, 0);
 | |
| 
 | |
|   for (Offset = 0; Offset < mFdBlockSize; Offset++) {
 | |
|     Ptr = QemuFlashPtr (0, Offset);
 | |
|     ProbeUint8 = *Ptr;
 | |
|     if (ProbeUint8 != CLEAR_STATUS_CMD &&
 | |
|         ProbeUint8 != READ_STATUS_CMD &&
 | |
|         ProbeUint8 != CLEARED_ARRAY_STATUS) {
 | |
|       break;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (Offset >= mFdBlockSize) {
 | |
|     DEBUG ((DEBUG_INFO, "QEMU Flash: Failed to find probe location\n"));
 | |
|     return FALSE;
 | |
|   }
 | |
| 
 | |
|   DEBUG ((DEBUG_INFO, "QEMU Flash: Attempting flash detection at %p\n", Ptr));
 | |
| 
 | |
|   if (MemEncryptSevEsIsEnabled ()) {
 | |
|     //
 | |
|     // When SEV-ES is enabled, the check below can result in an infinite
 | |
|     // loop with respect to a nested page fault. When the memslot is mapped
 | |
|     // read-only, the nested page table entry is read-only. The check below
 | |
|     // will cause a nested page fault that cannot be emulated, causing
 | |
|     // the instruction to retried over and over. For SEV-ES, acknowledge that
 | |
|     // the FD appears as ROM and not as FLASH, but report FLASH anyway because
 | |
|     // FLASH behavior can be simulated using VMGEXIT.
 | |
|     //
 | |
|     DEBUG ((DEBUG_INFO,
 | |
|       "QEMU Flash: SEV-ES enabled, assuming FD behaves as FLASH\n"));
 | |
|     return TRUE;
 | |
|   }
 | |
| 
 | |
|   OriginalUint8 = *Ptr;
 | |
|   *Ptr = CLEAR_STATUS_CMD;
 | |
|   ProbeUint8 = *Ptr;
 | |
|   if (OriginalUint8 != CLEAR_STATUS_CMD &&
 | |
|       ProbeUint8 == CLEAR_STATUS_CMD) {
 | |
|     DEBUG ((DEBUG_INFO, "QemuFlashDetected => FD behaves as RAM\n"));
 | |
|     *Ptr = OriginalUint8;
 | |
|   } else {
 | |
|     *Ptr = READ_STATUS_CMD;
 | |
|     ProbeUint8 = *Ptr;
 | |
|     if (ProbeUint8 == OriginalUint8) {
 | |
|       DEBUG ((DEBUG_INFO, "QemuFlashDetected => FD behaves as ROM\n"));
 | |
|     } else if (ProbeUint8 == READ_STATUS_CMD) {
 | |
|       DEBUG ((DEBUG_INFO, "QemuFlashDetected => FD behaves as RAM\n"));
 | |
|       *Ptr = OriginalUint8;
 | |
|     } else if (ProbeUint8 == CLEARED_ARRAY_STATUS) {
 | |
|       DEBUG ((DEBUG_INFO, "QemuFlashDetected => FD behaves as FLASH\n"));
 | |
|       FlashDetected = TRUE;
 | |
|       *Ptr = READ_ARRAY_CMD;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   DEBUG ((DEBUG_INFO, "QemuFlashDetected => %a\n",
 | |
|                       FlashDetected ? "Yes" : "No"));
 | |
|   return FlashDetected;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Read from QEMU Flash
 | |
| 
 | |
|   @param[in] Lba      The starting logical block index to read from.
 | |
|   @param[in] Offset   Offset into the block at which to begin reading.
 | |
|   @param[in] NumBytes On input, indicates the requested read size. On
 | |
|                       output, indicates the actual number of bytes read
 | |
|   @param[in] Buffer   Pointer to the buffer to read into.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| QemuFlashRead (
 | |
|   IN        EFI_LBA                              Lba,
 | |
|   IN        UINTN                                Offset,
 | |
|   IN        UINTN                                *NumBytes,
 | |
|   IN        UINT8                                *Buffer
 | |
|   )
 | |
| {
 | |
|   UINT8  *Ptr;
 | |
| 
 | |
|   //
 | |
|   // Only write to the first 64k. We don't bother saving the FTW Spare
 | |
|   // block into the flash memory.
 | |
|   //
 | |
|   if (Lba >= mFdBlockCount) {
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Get flash address
 | |
|   //
 | |
|   Ptr = (UINT8*) QemuFlashPtr (Lba, Offset);
 | |
| 
 | |
|   CopyMem (Buffer, Ptr, *NumBytes);
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Write to QEMU Flash
 | |
| 
 | |
|   @param[in] Lba      The starting logical block index to write to.
 | |
|   @param[in] Offset   Offset into the block at which to begin writing.
 | |
|   @param[in] NumBytes On input, indicates the requested write size. On
 | |
|                       output, indicates the actual number of bytes written
 | |
|   @param[in] Buffer   Pointer to the data to write.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| QemuFlashWrite (
 | |
|   IN        EFI_LBA                             Lba,
 | |
|   IN        UINTN                               Offset,
 | |
|   IN        UINTN                               *NumBytes,
 | |
|   IN        UINT8                               *Buffer
 | |
|   )
 | |
| {
 | |
|   volatile UINT8  *Ptr;
 | |
|   UINTN           Loop;
 | |
| 
 | |
|   //
 | |
|   // Only write to the first 64k. We don't bother saving the FTW Spare
 | |
|   // block into the flash memory.
 | |
|   //
 | |
|   if (Lba >= mFdBlockCount) {
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Program flash
 | |
|   //
 | |
|   Ptr = QemuFlashPtr (Lba, Offset);
 | |
|   for (Loop = 0; Loop < *NumBytes; Loop++) {
 | |
|     QemuFlashPtrWrite (Ptr, WRITE_BYTE_CMD);
 | |
|     QemuFlashPtrWrite (Ptr, Buffer[Loop]);
 | |
| 
 | |
|     Ptr++;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Restore flash to read mode
 | |
|   //
 | |
|   if (*NumBytes > 0) {
 | |
|     QemuFlashPtrWrite (Ptr - 1, READ_ARRAY_CMD);
 | |
|   }
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Erase a QEMU Flash block
 | |
| 
 | |
|   @param Lba    The logical block index to erase.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| QemuFlashEraseBlock (
 | |
|   IN   EFI_LBA      Lba
 | |
|   )
 | |
| {
 | |
|   volatile UINT8  *Ptr;
 | |
| 
 | |
|   if (Lba >= mFdBlockCount) {
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   Ptr = QemuFlashPtr (Lba, 0);
 | |
|   QemuFlashPtrWrite (Ptr, BLOCK_ERASE_CMD);
 | |
|   QemuFlashPtrWrite (Ptr, BLOCK_ERASE_CONFIRM_CMD);
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Initializes QEMU flash memory support
 | |
| 
 | |
|   @retval EFI_WRITE_PROTECTED   The QEMU flash device is not present.
 | |
|   @retval EFI_SUCCESS           The QEMU flash device is supported.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| QemuFlashInitialize (
 | |
|   VOID
 | |
|   )
 | |
| {
 | |
|   mFlashBase = (UINT8*)(UINTN) PcdGet32 (PcdOvmfFdBaseAddress);
 | |
|   mFdBlockSize = PcdGet32 (PcdOvmfFirmwareBlockSize);
 | |
|   ASSERT(PcdGet32 (PcdOvmfFirmwareFdSize) % mFdBlockSize == 0);
 | |
|   mFdBlockCount = PcdGet32 (PcdOvmfFirmwareFdSize) / mFdBlockSize;
 | |
| 
 | |
|   //
 | |
|   // execute module specific hooks before probing the flash
 | |
|   //
 | |
|   QemuFlashBeforeProbe (
 | |
|     (EFI_PHYSICAL_ADDRESS)(UINTN) mFlashBase,
 | |
|     mFdBlockSize,
 | |
|     mFdBlockCount
 | |
|     );
 | |
| 
 | |
|   if (!QemuFlashDetected ()) {
 | |
|     ASSERT (!FeaturePcdGet (PcdSmmSmramRequire));
 | |
|     return EFI_WRITE_PROTECTED;
 | |
|   }
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 |