/** @file Copyright (c) 2014-2018, Linaro Ltd. All rights reserved.
Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include #include #include #include #include #include #define QEMU_NOR_BLOCK_SIZE SIZE_256KB #define MAX_FLASH_BANKS 4 STATIC VIRT_NOR_FLASH_DESCRIPTION mNorFlashDevices[MAX_FLASH_BANKS]; EFI_STATUS VirtNorFlashPlatformInitialization ( VOID ) { return EFI_SUCCESS; } EFI_STATUS VirtNorFlashPlatformGetDevices ( OUT VIRT_NOR_FLASH_DESCRIPTION **NorFlashDescriptions, OUT UINT32 *Count ) { FDT_CLIENT_PROTOCOL *FdtClient; INT32 Node; EFI_STATUS Status; EFI_STATUS FindNodeStatus; CONST UINT32 *Reg; UINT32 PropSize; UINT32 Num; UINT64 Base; UINT64 Size; BOOLEAN Found; Status = gBS->LocateProtocol ( &gFdtClientProtocolGuid, NULL, (VOID **)&FdtClient ); ASSERT_EFI_ERROR (Status); Num = 0; Found = FALSE; for (FindNodeStatus = FdtClient->FindCompatibleNode ( FdtClient, "cfi-flash", &Node ); !EFI_ERROR (FindNodeStatus) && Num < MAX_FLASH_BANKS; FindNodeStatus = FdtClient->FindNextCompatibleNode ( FdtClient, "cfi-flash", Node, &Node )) { Status = FdtClient->GetNodeProperty ( FdtClient, Node, "reg", (CONST VOID **)&Reg, &PropSize ); if (EFI_ERROR (Status)) { DEBUG (( DEBUG_ERROR, "%a: GetNodeProperty () failed (Status == %r)\n", __func__, Status )); continue; } ASSERT ((PropSize % (4 * sizeof (UINT32))) == 0); while (PropSize >= (4 * sizeof (UINT32)) && Num < MAX_FLASH_BANKS) { Base = SwapBytes64 (ReadUnaligned64 ((VOID *)&Reg[0])); Size = SwapBytes64 (ReadUnaligned64 ((VOID *)&Reg[2])); Reg += 4; PropSize -= 4 * sizeof (UINT32); // // Disregard any flash devices that overlap with the primary FV. // The firmware is not updatable from inside the guest anyway. // if ((PcdGet32 (PcdOvmfFdBaseAddress) + PcdGet32 (PcdOvmfFirmwareFdSize) > Base) && ((Base + Size) > PcdGet32 (PcdOvmfFdBaseAddress))) { continue; } mNorFlashDevices[Num].DeviceBaseAddress = (UINTN)Base; mNorFlashDevices[Num].RegionBaseAddress = (UINTN)Base; mNorFlashDevices[Num].Size = (UINTN)Size; mNorFlashDevices[Num].BlockSize = QEMU_NOR_BLOCK_SIZE; Num++; if (!Found) { // // By default, the second available flash is stored as a non-volatile variable. // Status = PcdSet32S (PcdFlashNvStorageVariableBase, Base); ASSERT_EFI_ERROR (Status); // // The Base is the value of PcdFlashNvStorageVariableBase, // PcdFlashNvStorageFtwWorkingBase can be got by // PcdFlashNvStorageVariableBase + PcdFlashNvStorageVariableSize // Base += PcdGet32 (PcdFlashNvStorageVariableSize); Status = PcdSet32S (PcdFlashNvStorageFtwWorkingBase, Base); ASSERT_EFI_ERROR (Status); // // Now, the Base is the value of PcdFlashNvStorageFtwWorkingBase, // PcdFlashNvStorageFtwSpareBase can be got by // PcdFlashNvStorageFtwWorkingBase + PcdFlashNvStorageFtwWorkingSize. // Base += PcdGet32 (PcdFlashNvStorageFtwWorkingSize); Status = PcdSet32S (PcdFlashNvStorageFtwSpareBase, Base); ASSERT_EFI_ERROR (Status); Found = TRUE; } } // // UEFI takes ownership of the NOR flash, and exposes its functionality // through the UEFI Runtime Services GetVariable, SetVariable, etc. This // means we need to disable it in the device tree to prevent the OS from // attaching its device driver as well. // Note that this also hides other flash banks, but the only other flash // bank we expect to encounter is the one that carries the UEFI executable // code, which is not intended to be guest updatable, and is usually backed // in a readonly manner by QEMU anyway. // Status = FdtClient->SetNodeProperty ( FdtClient, Node, "status", "disabled", sizeof ("disabled") ); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_WARN, "Failed to set NOR flash status to 'disabled'\n")); } } *NorFlashDescriptions = mNorFlashDevices; *Count = Num; return EFI_SUCCESS; }