/** @file NorFlashDxe.c Copyright (c) 2011 - 2014, ARM Ltd. All rights reserved.
Copyright (c) 2017, Socionext Inc. All rights reserved.
Copyright (c) 2017, Linaro, Ltd. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include #include #include #include #include #include #include #include #include #include "NorFlash.h" // // Global variable declarations // STATIC NOR_FLASH_INSTANCE **mNorFlashInstances; STATIC UINT32 mNorFlashDeviceCount; STATIC EFI_EVENT mNorFlashVirtualAddrChangeEvent; EFI_STATUS EFIAPI NorFlashFvbInitialize ( IN NOR_FLASH_INSTANCE* Instance ) { EFI_STATUS Status; UINT32 FvbNumLba; EFI_BOOT_MODE BootMode; UINTN RuntimeMmioRegionSize; UINTN BlockSize; DEBUG ((DEBUG_BLKIO,"NorFlashFvbInitialize\n")); BlockSize = Instance->BlockSize; // FirmwareVolumeHeader->FvLength is declared to have the Variable area // AND the FTW working area AND the FTW Spare contiguous. ASSERT(PcdGet32(PcdFlashNvStorageVariableBase) + PcdGet32(PcdFlashNvStorageVariableSize) == PcdGet32(PcdFlashNvStorageFtwWorkingBase)); ASSERT(PcdGet32(PcdFlashNvStorageFtwWorkingBase) + PcdGet32(PcdFlashNvStorageFtwWorkingSize) == PcdGet32(PcdFlashNvStorageFtwSpareBase)); // Check if the size of the area is at least one block size ASSERT((PcdGet32(PcdFlashNvStorageVariableSize) > 0) && (PcdGet32(PcdFlashNvStorageVariableSize) / BlockSize > 0)); ASSERT((PcdGet32(PcdFlashNvStorageFtwWorkingSize) > 0) && (PcdGet32(PcdFlashNvStorageFtwWorkingSize) / BlockSize > 0)); ASSERT((PcdGet32(PcdFlashNvStorageFtwSpareSize) > 0) && (PcdGet32(PcdFlashNvStorageFtwSpareSize) / BlockSize > 0)); // Ensure the Variable areas are aligned on block size boundaries ASSERT((PcdGet32(PcdFlashNvStorageVariableBase) % BlockSize) == 0); ASSERT((PcdGet32(PcdFlashNvStorageFtwWorkingBase) % BlockSize) == 0); ASSERT((PcdGet32(PcdFlashNvStorageFtwSpareBase) % BlockSize) == 0); Instance->Initialized = TRUE; mFlashNvStorageVariableBase = FixedPcdGet32 (PcdFlashNvStorageVariableBase); // Set the index of the first LBA for the FVB Instance->StartLba = (PcdGet32 (PcdFlashNvStorageVariableBase) - Instance->RegionBaseAddress) / BlockSize; BootMode = GetBootModeHob (); if (BootMode == BOOT_WITH_DEFAULT_SETTINGS) { Status = EFI_INVALID_PARAMETER; } else { // Determine if there is a valid header at the beginning of the NorFlash Status = ValidateFvHeader (Instance); } // Install the Default FVB header if required if (EFI_ERROR(Status)) { // There is no valid header, so time to install one. DEBUG ((DEBUG_INFO, "%a: The FVB Header is not valid.\n", __FUNCTION__)); DEBUG ((DEBUG_INFO, "%a: Installing a correct one for this volume.\n", __FUNCTION__)); // Erase all the NorFlash that is reserved for variable storage FvbNumLba = (PcdGet32(PcdFlashNvStorageVariableSize) + PcdGet32(PcdFlashNvStorageFtwWorkingSize) + PcdGet32(PcdFlashNvStorageFtwSpareSize)) / Instance->BlockSize; Status = FvbEraseBlocks (&Instance->FvbProtocol, (EFI_LBA)0, FvbNumLba, EFI_LBA_LIST_TERMINATOR); if (EFI_ERROR(Status)) { return Status; } // Install all appropriate headers Status = InitializeFvAndVariableStoreHeaders (Instance); if (EFI_ERROR(Status)) { return Status; } } // // The driver implementing the variable read service can now be dispatched; // the varstore headers are in place. // Status = gBS->InstallProtocolInterface (&gImageHandle, &gEdkiiNvVarStoreFormattedGuid, EFI_NATIVE_INTERFACE, NULL); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_ERROR, "%a: Failed to install gEdkiiNvVarStoreFormattedGuid\n", __FUNCTION__)); return Status; } // // Declare the Non-Volatile storage as EFI_MEMORY_RUNTIME // RuntimeMmioRegionSize = Instance->Size; Status = gDS->AddMemorySpace (EfiGcdMemoryTypeMemoryMappedIo, Instance->RegionBaseAddress, RuntimeMmioRegionSize, EFI_MEMORY_UC | EFI_MEMORY_RUNTIME); ASSERT_EFI_ERROR (Status); Status = gDS->AddMemorySpace (EfiGcdMemoryTypeMemoryMappedIo, Instance->DeviceBaseAddress, SIZE_4KB, EFI_MEMORY_UC | EFI_MEMORY_RUNTIME); ASSERT_EFI_ERROR (Status); Status = gDS->SetMemorySpaceAttributes (Instance->RegionBaseAddress, RuntimeMmioRegionSize, EFI_MEMORY_UC | EFI_MEMORY_RUNTIME); ASSERT_EFI_ERROR (Status); Status = gDS->SetMemorySpaceAttributes (Instance->DeviceBaseAddress, SIZE_4KB, EFI_MEMORY_UC | EFI_MEMORY_RUNTIME); ASSERT_EFI_ERROR (Status); return Status; } /** Fixup internal data so that EFI can be call in virtual mode. Call the passed in Child Notify event and convert any pointers in lib to virtual mode. @param[in] Event The Event that is being processed @param[in] Context Event Context **/ STATIC VOID EFIAPI NorFlashVirtualNotifyEvent ( IN EFI_EVENT Event, IN VOID *Context ) { UINTN Index; EfiConvertPointer (0x0, (VOID**)&mFlashNvStorageVariableBase); for (Index = 0; Index < mNorFlashDeviceCount; Index++) { EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->HostRegisterBaseAddress); EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->DeviceBaseAddress); EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->RegionBaseAddress); // Convert Fvb EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->FvbProtocol.EraseBlocks); EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->FvbProtocol.GetAttributes); EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->FvbProtocol.GetBlockSize); EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->FvbProtocol.GetPhysicalAddress); EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->FvbProtocol.Read); EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->FvbProtocol.SetAttributes); EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->FvbProtocol.Write); if (mNorFlashInstances[Index]->ShadowBuffer != NULL) { EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->ShadowBuffer); } } return; } EFI_STATUS EFIAPI NorFlashInitialise ( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable ) { EFI_STATUS Status; EFI_PHYSICAL_ADDRESS HostRegisterBaseAddress; UINT32 Index; NOR_FLASH_DESCRIPTION* NorFlashDevices; BOOLEAN ContainVariableStorage; // Register HSSPI FIP006 register region HostRegisterBaseAddress = PcdGet32 (PcdFip006DxeRegBaseAddress); Status = gDS->AddMemorySpace ( EfiGcdMemoryTypeMemoryMappedIo, HostRegisterBaseAddress, SIZE_4KB, EFI_MEMORY_UC | EFI_MEMORY_RUNTIME ); ASSERT_EFI_ERROR (Status); Status = gDS->SetMemorySpaceAttributes ( HostRegisterBaseAddress, SIZE_4KB, EFI_MEMORY_UC | EFI_MEMORY_RUNTIME); ASSERT_EFI_ERROR (Status); Status = NorFlashPlatformInitialization (); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_ERROR, "NorFlashInitialise: Fail to initialize Nor Flash devices\n")); return Status; } // Initialize NOR flash instances Status = NorFlashPlatformGetDevices (&NorFlashDevices, &mNorFlashDeviceCount); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_ERROR,"NorFlashInitialise: Fail to get Nor Flash devices\n")); return Status; } mNorFlashInstances = AllocateRuntimePool (sizeof(NOR_FLASH_INSTANCE*) * mNorFlashDeviceCount); for (Index = 0; Index < mNorFlashDeviceCount; Index++) { // Check if this NOR Flash device contain the variable storage region ContainVariableStorage = (NorFlashDevices[Index].RegionBaseAddress <= PcdGet32 (PcdFlashNvStorageVariableBase)) && (PcdGet32 (PcdFlashNvStorageVariableBase) + PcdGet32 (PcdFlashNvStorageVariableSize) <= NorFlashDevices[Index].RegionBaseAddress + NorFlashDevices[Index].Size); Status = NorFlashCreateInstance ( HostRegisterBaseAddress, NorFlashDevices[Index].DeviceBaseAddress, NorFlashDevices[Index].RegionBaseAddress, NorFlashDevices[Index].Size, Index, NorFlashDevices[Index].BlockSize, ContainVariableStorage, &mNorFlashInstances[Index] ); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_ERROR, "NorFlashInitialise: Fail to create instance for NorFlash[%d]\n", Index)); continue; } Status = gBS->InstallMultipleProtocolInterfaces ( &mNorFlashInstances[Index]->Handle, &gEfiDevicePathProtocolGuid, &mNorFlashInstances[Index]->DevicePath, &gEfiFirmwareVolumeBlockProtocolGuid, &mNorFlashInstances[Index]->FvbProtocol, NULL ); ASSERT_EFI_ERROR (Status); } // // Register for the virtual address change event // Status = gBS->CreateEventEx (EVT_NOTIFY_SIGNAL, TPL_NOTIFY, NorFlashVirtualNotifyEvent, NULL, &gEfiEventVirtualAddressChangeGuid, &mNorFlashVirtualAddrChangeEvent); ASSERT_EFI_ERROR (Status); return Status; } VOID EFIAPI NorFlashLock ( NOR_FLASH_LOCK_CONTEXT *Context ) { if (!EfiAtRuntime ()) { // Raise TPL to TPL_HIGH to stop anyone from interrupting us. Context->OriginalTPL = gBS->RaiseTPL (TPL_HIGH_LEVEL); } else { Context->InterruptsEnabled = SaveAndDisableInterrupts (); } } VOID EFIAPI NorFlashUnlock ( NOR_FLASH_LOCK_CONTEXT *Context ) { if (!EfiAtRuntime ()) { // Interruptions can resume. gBS->RestoreTPL (Context->OriginalTPL); } else if (Context->InterruptsEnabled) { SetInterruptState (TRUE); } }