123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274 |
- /** @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;
- }
|