/** @file
OVMF support for QEMU system firmware flash device
Copyright (c) 2009 - 2013, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include
#include
#include
#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;
}