Commit:24e4ad7 (OvmfPkg: Add AmdSevDxe driver) added a driver which runs early in DXE phase and clears the C-bit from NonExistent entry -- which is later split and accommodate the flash MMIO. When SMM is enabled, we build two sets of page tables; first page table is used when executing code in non SMM mode (SMM-less-pgtable) and second page table is used when we are executing code in SMM mode (SMM-pgtable). During boot time, AmdSevDxe driver clears the C-bit from the SMM-less-pgtable. But when SMM is enabled, Qemu Flash services are used from SMM mode. In this patch we explicitly clear the C-bit from Qemu flash MMIO range before we probe the flash. When OVMF is built with SMM_REQUIRE then call to initialize the flash services happen after the SMM-pgtable is created and processor has served the first SMI. At this time we will have access to the SMM-pgtable. Cc: Jordan Justen <jordan.l.justen@intel.com> Cc: Laszlo Ersek <lersek@redhat.com> Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org> Contributed-under: TianoCore Contribution Agreement 1.1 Signed-off-by: Brijesh Singh <brijesh.singh@amd.com> [lersek@redhat.com: trivial coding style improvements] Reviewed-by: Laszlo Ersek <lersek@redhat.com>
		
			
				
	
	
		
			264 lines
		
	
	
		
			6.5 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			264 lines
		
	
	
		
			6.5 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/** @file
 | 
						|
  OVMF support for QEMU system firmware flash device
 | 
						|
 | 
						|
  Copyright (c) 2009 - 2013, Intel Corporation. All rights reserved.<BR>
 | 
						|
 | 
						|
  This program and the accompanying materials are licensed and made available
 | 
						|
  under the terms and conditions of the BSD License which accompanies this
 | 
						|
  distribution.  The full text of the license may be found at
 | 
						|
  http://opensource.org/licenses/bsd-license.php
 | 
						|
 | 
						|
  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
 | 
						|
  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
 | 
						|
 | 
						|
**/
 | 
						|
 | 
						|
#include <Library/BaseMemoryLib.h>
 | 
						|
#include <Library/DebugLib.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 ((EFI_D_INFO, "QEMU Flash: Failed to find probe location\n"));
 | 
						|
    return FALSE;
 | 
						|
  }
 | 
						|
 | 
						|
  DEBUG ((EFI_D_INFO, "QEMU Flash: Attempting flash detection at %p\n", Ptr));
 | 
						|
 | 
						|
  OriginalUint8 = *Ptr;
 | 
						|
  *Ptr = CLEAR_STATUS_CMD;
 | 
						|
  ProbeUint8 = *Ptr;
 | 
						|
  if (OriginalUint8 != CLEAR_STATUS_CMD &&
 | 
						|
      ProbeUint8 == CLEAR_STATUS_CMD) {
 | 
						|
    DEBUG ((EFI_D_INFO, "QemuFlashDetected => FD behaves as RAM\n"));
 | 
						|
    *Ptr = OriginalUint8;
 | 
						|
  } else {
 | 
						|
    *Ptr = READ_STATUS_CMD;
 | 
						|
    ProbeUint8 = *Ptr;
 | 
						|
    if (ProbeUint8 == OriginalUint8) {
 | 
						|
      DEBUG ((EFI_D_INFO, "QemuFlashDetected => FD behaves as ROM\n"));
 | 
						|
    } else if (ProbeUint8 == READ_STATUS_CMD) {
 | 
						|
      DEBUG ((EFI_D_INFO, "QemuFlashDetected => FD behaves as RAM\n"));
 | 
						|
      *Ptr = OriginalUint8;
 | 
						|
    } else if (ProbeUint8 == CLEARED_ARRAY_STATUS) {
 | 
						|
      DEBUG ((EFI_D_INFO, "QemuFlashDetected => FD behaves as FLASH\n"));
 | 
						|
      FlashDetected = TRUE;
 | 
						|
      *Ptr = READ_ARRAY_CMD;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  DEBUG ((EFI_D_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++) {
 | 
						|
    *Ptr = WRITE_BYTE_CMD;
 | 
						|
    *Ptr = Buffer[Loop];
 | 
						|
    Ptr++;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Restore flash to read mode
 | 
						|
  //
 | 
						|
  if (*NumBytes > 0) {
 | 
						|
    *(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);
 | 
						|
  *Ptr = BLOCK_ERASE_CMD;
 | 
						|
  *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;
 | 
						|
}
 | 
						|
 |