Files
system76-edk2/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/QemuFlash.c
Brijesh Singh e4a1d5a7c4 OvmfPkg/QemuFlashFvbServicesRuntimeDxe: Clear C-bit when SEV is active
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>
2018-03-09 21:44:53 +01:00

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;
}