当前位置:网站首页>UEFI source code learning 3.7 - norflashdxe

UEFI source code learning 3.7 - norflashdxe

2022-06-23 09:21:00 MyeDy

AARCH64 QEMU Upper NOR FLASH Drive in ArmPlatFromPkg/Drivers/NorFlashDxe Under the table of contents .

1. QEMU CFI NOR FLASH

Based on the analysis of QEMU On CFI NOR FLASH Before , Need to know something QEMU CFI NOR FLASH Parameters of .
FLASH ARRAY Address : 0x04000000 - 0x08000000
Register address :0x04000000. Registers and FLASH ARRAY With the same address , When writing 0xFF Command to register , The operation is FLASH ARRAY. Otherwise, the register of the operation .
Command table

command function
0x00Reset
0x10 Single byte write
0x20Block erase
0x28Block erase
0x40 Single byte write
0x50 eliminate Status register
0x60Block Lock / Unlock
0x70 Read Status register
0xe8 Write a Block
0x90 Read Device ID register
0x90Query CFI
0xff Enter read FLASH ARRAY Pattern

Command definition
ArmPlatFromPkg/Drivers/NorFlashDxe/NorFlash.h

#define P30_MAX_BUFFER_SIZE_IN_BYTES ((UINTN)128)
#define P30_MAX_BUFFER_SIZE_IN_WORDS (P30_MAX_BUFFER_SIZE_IN_BYTES/((UINTN)4))
#define MAX_BUFFERED_PROG_ITERATIONS 10000000
#define BOUNDARY_OF_32_WORDS 0x7F

// CFI Addresses
#define P30_CFI_ADDR_QUERY_UNIQUE_QRY 0x10
#define P30_CFI_ADDR_VENDOR_ID 0x13

// CFI Data
#define CFI_QRY 0x00595251

// READ Commands
#define P30_CMD_READ_DEVICE_ID 0x0090
#define P30_CMD_READ_STATUS_REGISTER 0x0070
#define P30_CMD_CLEAR_STATUS_REGISTER 0x0050
#define P30_CMD_READ_ARRAY 0x00FF
#define P30_CMD_READ_CFI_QUERY 0x0098

// WRITE Commands
#define P30_CMD_WORD_PROGRAM_SETUP 0x0040
#define P30_CMD_ALTERNATE_WORD_PROGRAM_SETUP 0x0010
#define P30_CMD_BUFFERED_PROGRAM_SETUP 0x00E8
#define P30_CMD_BUFFERED_PROGRAM_CONFIRM 0x00D0
#define P30_CMD_BEFP_SETUP 0x0080
#define P30_CMD_BEFP_CONFIRM 0x00D0

// ERASE Commands
#define P30_CMD_BLOCK_ERASE_SETUP 0x0020
#define P30_CMD_BLOCK_ERASE_CONFIRM 0x00D0

// SUSPEND Commands
#define P30_CMD_PROGRAM_OR_ERASE_SUSPEND 0x00B0
#define P30_CMD_SUSPEND_RESUME 0x00D0

// BLOCK LOCKING / UNLOCKING Commands
#define P30_CMD_LOCK_BLOCK_SETUP 0x0060
#define P30_CMD_LOCK_BLOCK 0x0001
#define P30_CMD_UNLOCK_BLOCK 0x00D0
#define P30_CMD_LOCK_DOWN_BLOCK 0x002F

// PROTECTION Commands
#define P30_CMD_PROGRAM_PROTECTION_REGISTER_SETUP 0x00C0

// CONFIGURATION Commands
#define P30_CMD_READ_CONFIGURATION_REGISTER_SETUP 0x0060
#define P30_CMD_READ_CONFIGURATION_REGISTER 0x0003

2. data structure

Used to describe NOR FLASH The structure of is called NOR_FLASH_INSTACE, The structure layout is as follows
 Insert picture description here

struct _NOR_FLASH_INSTANCE {
    
  UINT32                                 Signature;				-->  One 4 The signature of the byte is "norx", x On behalf of the digital 0,1,2
  EFI_HANDLE                             Handle;				-->  Corresponding image handle

  UINTN                                  DeviceBaseAddress;		--> Nor Flash The address of the register 
  UINTN                                  RegionBaseAddress;		--> Nor Flash Address of the storage area 
  UINTN                                  Size;					-->	Nor Flash Size 
  EFI_LBA                                StartLba;				-->  start Logic Block Address
	
  EFI_BLOCK_IO_PROTOCOL                  BlockIoProtocol;		-->  One EFI_BLOCK_IO_PROTOCOL For providing Block IO  service 
  EFI_BLOCK_IO_MEDIA                     Media;					-->  Describe the structure of the block device 
  EFI_DISK_IO_PROTOCOL                   DiskIoProtocol;		-->  One EFI_DISK_IO_PROTOCOL For providing Disk IO service 
		
  EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL    FvbProtocol;			-->  One EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL For providing Fv2 service 
  VOID                                   *ShadowBuffer;			-->  cache buffer
	
  NOR_FLASH_DEVICE_PATH                  DevicePath;			-->  Device path object 
};

3. initialization

NorFlashInitialise yes NorFlashDxe The entry function of , It's initialized NorFlashDxe.

  1. the reason being that QEMU,NorFlashPlatformInitialization Nothing is done here . The actual hardware should be initialized clock,power, iomux wait .
  2. NorFlashPlatformGetDevices from FDT( Get from the device tree file NOR FLASH Instances and number of ). There is only one example here NOR FLASH, therefore mNorFlashDeviceCount = 1.
  3. call AllocateRuntimePool Assign to each NOR_FLASH_INSTANCE Memory required by the instance .
  4. according to PCD obtain ContainVariableStorage Information .
  5. call NorFlashCreateInstance For every one NOR FLASH Object member initialization of .
  6. establish Event, Used to call back when the virtual address changes NorFlashVirtualNotifyEvent function .
EFI_STATUS
EFIAPI
NorFlashInitialise (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
    
  EFI_STATUS             Status;
  UINT32                 Index;
  NOR_FLASH_DESCRIPTION  *NorFlashDevices;
  BOOLEAN                ContainVariableStorage;
  //1.  the reason being that QEMU,NorFlashPlatformInitialization  Nothing is done here . The actual hardware should be initialized clock,power, iomux wait .
  Status = NorFlashPlatformInitialization ();
  //2. NorFlashPlatformGetDevices  from FDT( Get from the device tree file NOR FLASH Instances and number of ). There is only one example here NOR FLASH, therefore mNorFlashDeviceCount = 1.
  Status = NorFlashPlatformGetDevices (&NorFlashDevices, &mNorFlashDeviceCount);
  //3.  call AllocateRuntimePool  Assign to each NOR_FLASH_INSTANCE example .
  mNorFlashInstances = AllocateRuntimePool (sizeof (NOR_FLASH_INSTANCE *) * mNorFlashDeviceCount);

  for (Index = 0; Index < mNorFlashDeviceCount; Index++) {
    
    // Check if this NOR Flash device contain the variable storage region
	//4.  according to PCD obtain ContainVariableStorage Information .
    if (PcdGet64 (PcdFlashNvStorageVariableBase64) != 0) {
    
      ContainVariableStorage =
        (NorFlashDevices[Index].RegionBaseAddress <= PcdGet64 (PcdFlashNvStorageVariableBase64)) &&
        (PcdGet64 (PcdFlashNvStorageVariableBase64) + PcdGet32 (PcdFlashNvStorageVariableSize) <=
         NorFlashDevices[Index].RegionBaseAddress + NorFlashDevices[Index].Size);
    } else {
    
      ContainVariableStorage =
        (NorFlashDevices[Index].RegionBaseAddress <= PcdGet32 (PcdFlashNvStorageVariableBase)) &&
        (PcdGet32 (PcdFlashNvStorageVariableBase) + PcdGet32 (PcdFlashNvStorageVariableSize) <=
         NorFlashDevices[Index].RegionBaseAddress + NorFlashDevices[Index].Size);
    }
	//5.  call NorFlashCreateInstance  For every one NOR FLASH Object member initialization of .
    Status = NorFlashCreateInstance (
               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));
    }
  }

  //6.  establish Event, Used to call back when the virtual address changes NorFlashVirtualNotifyEvent function .
  Status = gBS->CreateEventEx (
                  EVT_NOTIFY_SIGNAL,
                  TPL_NOTIFY,
                  NorFlashVirtualNotifyEvent,
                  NULL,
                  &gEfiEventVirtualAddressChangeGuid,
                  &mNorFlashVirtualAddrChangeEvent
                  );
  ASSERT_EFI_ERROR (Status);

  return Status;
}

NOR FLASH The members of the object are mainly in NorFlashCreateInstance In the initialization .

  1. First call AllocateRuntimeCopyPool hold mNorFlashInstanceTemplate Copy the contents of to NOR_FLASH_INSTANCE On . You can see mNorFlashInstanceTemplate Mainly some function pointers .
  2. Initialization according to parameters NOR_FLASH_INSTANCE Members .
Member name value
DeviceBaseAddress0x04000000
RegionBaseAddress0x04000000
Size0x04000000
Media.MediaId0
Media.BlockSize0x40000
DevicePath.Vendor.Guid93E34C7E-B50E-11DF-9223-2443DFD72085
  1. call AllocateRuntimePool Allocate cache buffer.
  2. NorFlashFvbInitialize initialization NOR FLASH Upper FV.
  3. call InstallMultipleProtocolInterfaces Install the following Protocol:gEfiDevicePathProtocolGuid,gEfiBlockIoProtocolGuid, gEfiFirmwareVolumeBlockProtocolGuid,gEfiDiskIoProtocolGuid
EFI_STATUS
NorFlashCreateInstance (
  IN UINTN                NorFlashDeviceBase,
  IN UINTN                NorFlashRegionBase,
  IN UINTN                NorFlashSize,
  IN UINT32               Index,
  IN UINT32               BlockSize,
  IN BOOLEAN              SupportFvb,
  OUT NOR_FLASH_INSTANCE  **NorFlashInstance
  )
{
    
  EFI_STATUS          Status;
  NOR_FLASH_INSTANCE  *Instance;

  ASSERT (NorFlashInstance != NULL);
  //1.  First call AllocateRuntimeCopyPool hold mNorFlashInstanceTemplate Copy the contents of to NOR_FLASH_INSTANCE On . You can see mNorFlashInstanceTemplate Mainly some function pointers .
  Instance = AllocateRuntimeCopyPool (sizeof (NOR_FLASH_INSTANCE), &mNorFlashInstanceTemplate);
  if (Instance == NULL) {
    
    return EFI_OUT_OF_RESOURCES;
  }
  //2.  Initialization according to parameters NOR_FLASH_INSTANCE Members .
  Instance->DeviceBaseAddress = NorFlashDeviceBase;
  Instance->RegionBaseAddress = NorFlashRegionBase;
  Instance->Size              = NorFlashSize;

  Instance->BlockIoProtocol.Media = &Instance->Media;
  Instance->Media.MediaId         = Index;
  Instance->Media.BlockSize       = BlockSize;
  Instance->Media.LastBlock       = (NorFlashSize / BlockSize)-1;

  CopyGuid (&Instance->DevicePath.Vendor.Guid, &gEfiCallerIdGuid);
  Instance->DevicePath.Index = (UINT8)Index;
  //3.  call AllocateRuntimePool Allocate cache buffer.
  Instance->ShadowBuffer = AllocateRuntimePool (BlockSize);
  if (Instance->ShadowBuffer == NULL) {
    
    return EFI_OUT_OF_RESOURCES;
  }


   //4. NorFlashFvbInitialize  initialization NOR FLASH Upper FV.
   NorFlashFvbInitialize (Instance);
   //5.  Install the following Protoco
   Status = gBS->InstallMultipleProtocolInterfaces (
                   &Instance->Handle,
                   &gEfiDevicePathProtocolGuid,
                   &Instance->DevicePath,
                   &gEfiBlockIoProtocolGuid,
                   &Instance->BlockIoProtocol,
                   &gEfiFirmwareVolumeBlockProtocolGuid,
                   &Instance->FvbProtocol,
                   &gEfiDiskIoProtocolGuid,
                   &Instance->DiskIoProtocol,
                   NULL
                   );

  ...

  *NorFlashInstance = Instance;
  return Status;
}

mNorFlashInstanceTemplate Is defined as follows , Achieved some Protocol Function of .

NOR_FLASH_INSTANCE  mNorFlashInstanceTemplate = {
    
  NOR_FLASH_SIGNATURE, // Signature
  NULL,                // Handle ... NEED TO BE FILLED

  0, // DeviceBaseAddress ... NEED TO BE FILLED
  0, // RegionBaseAddress ... NEED TO BE FILLED
  0, // Size ... NEED TO BE FILLED
  0, // StartLba

  {
    
    EFI_BLOCK_IO_PROTOCOL_REVISION2, // Revision
    NULL,                            // Media ... NEED TO BE FILLED
    NorFlashBlockIoReset,            // Reset;
    NorFlashBlockIoReadBlocks,       // ReadBlocks
    NorFlashBlockIoWriteBlocks,      // WriteBlocks
    NorFlashBlockIoFlushBlocks       // FlushBlocks
  }, // BlockIoProtocol

  {
    
    0,     // MediaId ... NEED TO BE FILLED
    FALSE, // RemovableMedia
    TRUE,  // MediaPresent
    FALSE, // LogicalPartition
    FALSE, // ReadOnly
    FALSE, // WriteCaching;
    0,     // BlockSize ... NEED TO BE FILLED
    4,     // IoAlign
    0,     // LastBlock ... NEED TO BE FILLED
    0,     // LowestAlignedLba
    1,     // LogicalBlocksPerPhysicalBlock
  }, // Media;

  {
    
    EFI_DISK_IO_PROTOCOL_REVISION, // Revision
    NorFlashDiskIoReadDisk,        // ReadDisk
    NorFlashDiskIoWriteDisk        // WriteDisk
  },

  {
    
    FvbGetAttributes,      // GetAttributes
    FvbSetAttributes,      // SetAttributes
    FvbGetPhysicalAddress, // GetPhysicalAddress
    FvbGetBlockSize,       // GetBlockSize
    FvbRead,               // Read
    FvbWrite,              // Write
    FvbEraseBlocks,        // EraseBlocks
    NULL,                  // ParentHandle
  },    // FvbProtoccol;
  NULL, // ShadowBuffer
  {
    
    {
    
      {
    
        HARDWARE_DEVICE_PATH,
        HW_VENDOR_DP,
        {
    
          (UINT8)(OFFSET_OF (NOR_FLASH_DEVICE_PATH, End)),
          (UINT8)(OFFSET_OF (NOR_FLASH_DEVICE_PATH, End) >> 8)
        }
      },
      {
     0x0,                               0x0, 0x0, {
     0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }
      },                                                             // GUID ... NEED TO BE FILLED
    },
    0, // Index
    {
    
      END_DEVICE_PATH_TYPE,
      END_ENTIRE_DEVICE_PATH_SUBTYPE,
      {
     sizeof (EFI_DEVICE_PATH_PROTOCOL), 0 }
    }
  }   // DevicePath
};

NorFlashFvbInitialize initialization NOR FLASH Upper FV head .

  1. call AddMemorySpace and SetMemorySpaceAttributes towards DxeCore Report NOR FLASH Of memory space .
  2. call ValidateFvHeader see NOR FLASH Is there a FV.
  3. If it doesn't exist FV Words , It's just NOR FLASH Initialization on FV, This FV It's for saving Non Volatile Variable Of .
  4. install Protocol:gEdkiiNvVarStoreFormattedGuid.
  5. establish Event, Callback when the virtual address changes FvbVirtualNotifyEvent.
EFI_STATUS
EFIAPI
NorFlashFvbInitialize (
  IN NOR_FLASH_INSTANCE  *Instance
  )
{
    
  EFI_STATUS     Status;
  UINT32         FvbNumLba;
  EFI_BOOT_MODE  BootMode;
  UINTN          RuntimeMmioRegionSize;
  ...
  //1.  call AddMemorySpace and SetMemorySpaceAttributes  towards DxeCore Report NOR FLASH Of memory space .
  RuntimeMmioRegionSize = (Instance->RegionBaseAddress - Instance->DeviceBaseAddress) + Instance->Size;
  Status = gDS->AddMemorySpace (
                  EfiGcdMemoryTypeMemoryMappedIo,
                  Instance->DeviceBaseAddress,
                  RuntimeMmioRegionSize,
                  EFI_MEMORY_UC | EFI_MEMORY_RUNTIME
                  );

  Status = gDS->SetMemorySpaceAttributes (
                  Instance->DeviceBaseAddress,
                  RuntimeMmioRegionSize,
                  EFI_MEMORY_UC | EFI_MEMORY_RUNTIME
                  );
  // according to PCD obtain NvStorageVariable Base address for storage 
  mFlashNvStorageVariableBase = (PcdGet64 (PcdFlashNvStorageVariableBase64) != 0) ?
                                PcdGet64 (PcdFlashNvStorageVariableBase64) : PcdGet32 (PcdFlashNvStorageVariableBase);
 
  //2.  call ValidateFvHeader see NOR FLASH Is there a FV.
  Status = ValidateFvHeader (Instance);


  // Install the Default FVB header if required
  if (EFI_ERROR (Status)) {
    
    // There is no valid header, so time to install one.
    //3.  If it doesn't exist FV Words , It's just NOR FLASH Initialization on FV, This FV It's for saving Non Volatile Variable Of .
    //  erase Flash
    FvbNumLba = (PcdGet32 (PcdFlashNvStorageVariableSize) + PcdGet32 (PcdFlashNvStorageFtwWorkingSize) + PcdGet32 (PcdFlashNvStorageFtwSpareSize)) / Instance->Media.BlockSize;
    Status = FvbEraseBlocks (&Instance->FvbProtocol, (EFI_LBA)0, FvbNumLba, EFI_LBA_LIST_TERMINATOR);

    //  install NvStorageVariable FV head 
    Status = InitializeFvAndVariableStoreHeaders (Instance);

  }
  //4.  install Protocol:gEdkiiNvVarStoreFormattedGuid.
  Status = gBS->InstallProtocolInterface (
                  &gImageHandle,
                  &gEdkiiNvVarStoreFormattedGuid,
                  EFI_NATIVE_INTERFACE,
                  NULL
                  );

  //5.  establish Event, Callback when the virtual address changes FvbVirtualNotifyEvent.
  Status = gBS->CreateEventEx (
                  EVT_NOTIFY_SIGNAL,
                  TPL_NOTIFY,
                  FvbVirtualNotifyEvent,
                  NULL,
                  &gEfiEventVirtualAddressChangeGuid,
                  &mFvbVirtualAddrChangeEvent
                  );
  ASSERT_EFI_ERROR (Status);

  return Status;
}

InitializeFvAndVariableStoreHeaders It's simple , It is based on PCD Read in NvStrorageVariable FV The parameters of , Then generate FV head , The final call FvbWrite (&Instance->FvbProtocol, 0, 0, &HeadersLength, Headers); hold FV Write header to NOR FLASH On .

EFI_STATUS
InitializeFvAndVariableStoreHeaders (
  IN NOR_FLASH_INSTANCE  *Instance
  )
{
    
  ...
  FirmwareVolumeHeader = (EFI_FIRMWARE_VOLUME_HEADER *)Headers;
  CopyGuid (&FirmwareVolumeHeader->FileSystemGuid, &gEfiSystemNvDataFvGuid);
  FirmwareVolumeHeader->FvLength =
    PcdGet32 (PcdFlashNvStorageVariableSize) +
    PcdGet32 (PcdFlashNvStorageFtwWorkingSize) +
    PcdGet32 (PcdFlashNvStorageFtwSpareSize);
  FirmwareVolumeHeader->Signature  = EFI_FVH_SIGNATURE;
  FirmwareVolumeHeader->Attributes = (EFI_FVB_ATTRIBUTES_2)(
                                                            EFI_FVB2_READ_ENABLED_CAP   | // Reads may be enabled
                                                            EFI_FVB2_READ_STATUS        | // Reads are currently enabled
                                                            EFI_FVB2_STICKY_WRITE       | // A block erase is required to flip bits into EFI_FVB2_ERASE_POLARITY
                                                            EFI_FVB2_MEMORY_MAPPED      | // It is memory mapped
                                                            EFI_FVB2_ERASE_POLARITY     | // After erasure all bits take this value (i.e. '1')
                                                            EFI_FVB2_WRITE_STATUS       | // Writes are currently enabled
                                                            EFI_FVB2_WRITE_ENABLED_CAP    // Writes may be enabled
                                                            );
  FirmwareVolumeHeader->HeaderLength          = sizeof (EFI_FIRMWARE_VOLUME_HEADER) + sizeof (EFI_FV_BLOCK_MAP_ENTRY);
  FirmwareVolumeHeader->Revision              = EFI_FVH_REVISION;
  FirmwareVolumeHeader->BlockMap[0].NumBlocks = Instance->Media.LastBlock + 1;
  FirmwareVolumeHeader->BlockMap[0].Length    = Instance->Media.BlockSize;
  FirmwareVolumeHeader->BlockMap[1].NumBlocks = 0;
  FirmwareVolumeHeader->BlockMap[1].Length    = 0;
  FirmwareVolumeHeader->Checksum              = CalculateCheckSum16 ((UINT16 *)FirmwareVolumeHeader, FirmwareVolumeHeader->HeaderLength);

  //
  // VARIABLE_STORE_HEADER
  //
  VariableStoreHeader = (VARIABLE_STORE_HEADER *)((UINTN)Headers + FirmwareVolumeHeader->HeaderLength);
  CopyGuid (&VariableStoreHeader->Signature, &gEfiAuthenticatedVariableGuid);
  VariableStoreHeader->Size   = PcdGet32 (PcdFlashNvStorageVariableSize) - FirmwareVolumeHeader->HeaderLength;
  VariableStoreHeader->Format = VARIABLE_STORE_FORMATTED;
  VariableStoreHeader->State  = VARIABLE_STORE_HEALTHY;

  // Install the combined super-header in the NorFlash
  Status = FvbWrite (&Instance->FvbProtocol, 0, 0, &HeadersLength, Headers);

  FreePool (Headers);
  return Status;
}

4. Read and write

4.1 NOR FLASH Reading and writing

ArmPlatFromPkg/Drivers/NorFlashDxe/NorFlash.c
Single byte write NorFlashWriteSingleWord

  1. towards NOR FLASH send out P30_CMD_WORD_PROGRAM_SETUP command .
  2. The data WriteData write in NOR FLASH Address WordAddress.
  3. Read Status register , Wait for the write to complete .
  4. send out P30_CMD_READ_ARRAY The order will NOR FLASH Set to READ ARRAY Pattern
EFI_STATUS
NorFlashWriteSingleWord (
  IN NOR_FLASH_INSTANCE  *Instance,
  IN UINTN               WordAddress,
  IN UINT32              WriteData
  )
{
    
  EFI_STATUS  Status;
  UINT32      StatusRegister;

  Status = EFI_SUCCESS;

  //1.  towards NOR FLASH send out P30_CMD_WORD_PROGRAM_SETUP command 
  SEND_NOR_COMMAND (WordAddress, 0, P30_CMD_WORD_PROGRAM_SETUP);

  //2.  The data WriteData write in NOR FLASH Address WordAddress.
  MmioWrite32 (WordAddress, WriteData);

  //3.  Read Status  register , Wait for the write to complete .
  do {
    
    // Prepare to read the status register
    StatusRegister = NorFlashReadStatusRegister (Instance, WordAddress);
    // The chip is busy while the WRITE bit is not asserted
  } while ((StatusRegister & P30_SR_BIT_WRITE) != P30_SR_BIT_WRITE);

  // Perform a full status check:
  // Mask the relevant bits of Status Register.
  // Everything should be zero, if not, we have a problem

  if (StatusRegister & P30_SR_BIT_VPP) {
    
    DEBUG ((DEBUG_ERROR, "NorFlashWriteSingleWord(WordAddress:0x%X): VPP Range Error\n", WordAddress));
    Status = EFI_DEVICE_ERROR;
  }

  if (StatusRegister & P30_SR_BIT_PROGRAM) {
    
    DEBUG ((DEBUG_ERROR, "NorFlashWriteSingleWord(WordAddress:0x%X): Program Error\n", WordAddress));
    Status = EFI_DEVICE_ERROR;
  }

  if (StatusRegister & P30_SR_BIT_BLOCK_LOCKED) {
    
    DEBUG ((DEBUG_ERROR, "NorFlashWriteSingleWord(WordAddress:0x%X): Device Protect Error\n", WordAddress));
    Status = EFI_DEVICE_ERROR;
  }

  if (!EFI_ERROR (Status)) {
    
    // Clear the Status Register
    SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_CLEAR_STATUS_REGISTER);
  }

  //4.  take NOR FLASH Set to READ ARRAY Pattern 
  SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_READ_ARRAY);

  return Status;
}

Other NorFlashWriteBlocks、NorFlashWriteSingleBlock and NorFlashWriteBuffer The process is similar : Give orders -> Writing data -> read Status register -> Set to READ ARRAY Pattern .
Just send different commands .

The implementation of reading is simple .NOR FLASH yes memory map The equipment , Directly from 0x04000000 You can read it on the address NOR FLASH The data of .

  1. Do a bunch of parameter checks , obtain NOR FLASH ARRAY From .
  2. send out P30_CMD_READ_ARRAY The command NOR FLASH Set to READY ARRAY Pattern .
  3. Transfer the data from the corresponding FLASH The address space is copied to the destination address .
EFI_STATUS
NorFlashRead (
  IN NOR_FLASH_INSTANCE  *Instance,
  IN EFI_LBA             Lba,
  IN UINTN               Offset,
  IN UINTN               BufferSizeInBytes,
  OUT VOID               *Buffer
  )
{
    
  UINTN  StartAddress;
  ...
  // 1.  Do a bunch of parameter checks , obtain NOR FLASH ARRAY From .
  StartAddress = GET_NOR_BLOCK_ADDRESS (
                   Instance->RegionBaseAddress,
                   Lba,
                   Instance->Media.BlockSize
                   );

  // 2.  send out P30_CMD_READ_ARRAY The command NOR FLASH Set to READY ARRAY Pattern .
  SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_READ_ARRAY);

  // 3.  Transfer the data from the corresponding FLASH The address space is copied to the destination address .
  AlignedCopyMem (Buffer, (VOID *)(StartAddress + Offset), BufferSizeInBytes);

  return EFI_SUCCESS;
}

4.2 Block IO Protocol Realization

ArmPlatFromPkg/Drivers/NorFlashDxe/NorFlashBlockIo.c, This is the realization BlockIO Protocol The correlation function of , The underlying call is still NorFlash.c The function on .

  • NorFlashBlockIoReadBlocks After checking a bunch of parameters, I called NorFlashReadBlocks Read the data .
  • NorFlashBlockIoWriteBlocks After doing a bunch of checks, call NorFlashWriteBlocks Write data .

EFI_STATUS
EFIAPI
NorFlashBlockIoReadBlocks (
  IN  EFI_BLOCK_IO_PROTOCOL  *This,
  IN  UINT32                 MediaId,
  IN  EFI_LBA                Lba,
  IN  UINTN                  BufferSizeInBytes,
  OUT VOID                   *Buffer
  )
{
    
  NOR_FLASH_INSTANCE  *Instance;
  EFI_STATUS          Status;
  EFI_BLOCK_IO_MEDIA  *Media;

  if (This == NULL) {
    
    return EFI_INVALID_PARAMETER;
  }

  Instance = INSTANCE_FROM_BLKIO_THIS (This);
  Media    = This->Media;

  DEBUG ((DEBUG_BLKIO, "NorFlashBlockIoReadBlocks(MediaId=0x%x, Lba=%ld, BufferSize=0x%x bytes (%d kB), BufferPtr @ 0x%08x)\n", MediaId, Lba, BufferSizeInBytes, Buffer));

  if (!Media) {
    
    Status = EFI_INVALID_PARAMETER;
  } else if (!Media->MediaPresent) {
    
    Status = EFI_NO_MEDIA;
  } else if (Media->MediaId != MediaId) {
    
    Status = EFI_MEDIA_CHANGED;
  } else if ((Media->IoAlign > 2) && (((UINTN)Buffer & (Media->IoAlign - 1)) != 0)) {
    
    Status = EFI_INVALID_PARAMETER;
  } else {
    
    Status = NorFlashReadBlocks (Instance, Lba, BufferSizeInBytes, Buffer);
  }

  return Status;
}


EFI_STATUS
EFIAPI
NorFlashBlockIoWriteBlocks (
  IN  EFI_BLOCK_IO_PROTOCOL  *This,
  IN  UINT32                 MediaId,
  IN  EFI_LBA                Lba,
  IN  UINTN                  BufferSizeInBytes,
  IN  VOID                   *Buffer
  )
{
    
  NOR_FLASH_INSTANCE  *Instance;
  EFI_STATUS          Status;

  Instance = INSTANCE_FROM_BLKIO_THIS (This);

  DEBUG ((DEBUG_BLKIO, "NorFlashBlockIoWriteBlocks(MediaId=0x%x, Lba=%ld, BufferSize=0x%x bytes (%d kB), BufferPtr @ 0x%08x)\n", MediaId, Lba, BufferSizeInBytes, Buffer));

  if ( !This->Media->MediaPresent ) {
    
    Status = EFI_NO_MEDIA;
  } else if ( This->Media->MediaId != MediaId ) {
    
    Status = EFI_MEDIA_CHANGED;
  } else if ( This->Media->ReadOnly ) {
    
    Status = EFI_WRITE_PROTECTED;
  } else {
    
    Status = NorFlashWriteBlocks (Instance, Lba, BufferSizeInBytes, Buffer);
  }

  return Status;
}

4.3 Disk IO Protocol Realization

Disk IO And Block IO It's the same thing , The final call NorFlash.c The underlying functions in the . But it wraps a little bit of outer logic in the outer layer to achieve the writing function of arbitrary byte size .

EFI_STATUS
EFIAPI
NorFlashDiskIoReadDisk (
  IN EFI_DISK_IO_PROTOCOL  *This,
  IN UINT32                MediaId,
  IN UINT64                DiskOffset,
  IN UINTN                 BufferSize,
  OUT VOID                 *Buffer
  )
{
    
  NOR_FLASH_INSTANCE  *Instance;
  UINT32              BlockSize;
  UINT32              BlockOffset;
  EFI_LBA             Lba;

  Instance = INSTANCE_FROM_DISKIO_THIS (This);

  if (MediaId != Instance->Media.MediaId) {
    
    return EFI_MEDIA_CHANGED;
  }

  BlockSize = Instance->Media.BlockSize;
  Lba       = (EFI_LBA)DivU64x32Remainder (DiskOffset, BlockSize, &BlockOffset);

  return NorFlashRead (Instance, Lba, BlockOffset, BufferSize, Buffer);
}


EFI_STATUS
EFIAPI
NorFlashDiskIoWriteDisk (
  IN EFI_DISK_IO_PROTOCOL  *This,
  IN UINT32                MediaId,
  IN UINT64                DiskOffset,
  IN UINTN                 BufferSize,
  IN VOID                  *Buffer
  )
{
    
  NOR_FLASH_INSTANCE  *Instance;
  UINT32              BlockSize;
  UINT32              BlockOffset;
  EFI_LBA             Lba;
  UINTN               RemainingBytes;
  UINTN               WriteSize;
  EFI_STATUS          Status;

  Instance = INSTANCE_FROM_DISKIO_THIS (This);

  if (MediaId != Instance->Media.MediaId) {
    
    return EFI_MEDIA_CHANGED;
  }

  BlockSize = Instance->Media.BlockSize;
  Lba       = (EFI_LBA)DivU64x32Remainder (DiskOffset, BlockSize, &BlockOffset);

  RemainingBytes = BufferSize;

  WriteSize = MIN (RemainingBytes, ((DiskOffset | (BlockSize - 1)) + 1) - DiskOffset);

  do {
    
    if (WriteSize == BlockSize) {
    
      // Write a full block
      Status = NorFlashWriteFullBlock (Instance, Lba, Buffer, BlockSize / sizeof (UINT32));
    } else {
    
      // Write a partial block
      Status = NorFlashWriteSingleBlock (Instance, Lba, BlockOffset, &WriteSize, Buffer);
    }

    if (EFI_ERROR (Status)) {
    
      return Status;
    }

    // Now continue writing either all the remaining bytes or single blocks.
    RemainingBytes -= WriteSize;
    Buffer          = (UINT8 *)Buffer + WriteSize;
    Lba++;
    BlockOffset = 0;
    WriteSize   = MIN (RemainingBytes, BlockSize);
  } while (RemainingBytes);

  return Status;
}

5. Test code

The test function is as follows , call NOR FLASH Of DiskIO Protocol.

  • Due to the current FD There is only one NorFlash This DiskIO equipment , So just use LocateProtocol To find the only DiskIO In order to get NOR FLASH Read and write .
  • Use them separately DiskIO And direct reading FLASH Address 0x4000010 Read by FLASH The data on the .
  • Go to FLASH Write... On the header 16 Bytes of data .
  • Use them separately DiskIO And direct reading FLASH Address 0x4000000 Read by FLASH The data on the .
VOID
TestDxeTestNorFlashProtocol()
{
    
    EFI_STATUS                    Status;
    UINT32 Buffer[128];
    UINT32 *Addr = (UINT32 *)0x4000000;
    DEBUG((DEBUG_INFO, "TestDxeTestNorFlashProtocol\n"));
    EFI_DISK_IO_PROTOCOL         *DiskIo;
    Status = gBS->LocateProtocol(&gEfiDiskIoProtocolGuid, NULL, (VOID **)&DiskIo);
    if (Status != EFI_SUCCESS) {
    
        DEBUG((DEBUG_INFO, "Locate block io failed\n"));
        return;
    }
    DiskIo->ReadDisk(DiskIo, 0, 0, 512, Buffer);
    DEBUG((DEBUG_INFO, "#######Read by Bufffer:0x%08x 0x%08x 0x%08x 0x%08x\n", Buffer[4], Buffer[5], Buffer[6], Buffer[7]));
    DEBUG((DEBUG_INFO, "#######Read by Address: 0x4000010: 0x%08x 0x%08x 0x%08x 0x%08x\n", *(Addr + 4), *(Addr+5), *(Addr+6), *(Addr + 7)));
    for (UINT32 i = 0;  i < 4; i++) {
    
        Buffer[i] = i;
    }

    DiskIo->WriteDisk(DiskIo, 0, 0, 16, Buffer);
    DiskIo->ReadDisk(DiskIo, 0, 0, 512, Buffer);
    DEBUG((DEBUG_INFO, "#######Read by Bufffer: 0x%08x 0x%08x 0x%08x 0x%08x\n", Buffer[0], Buffer[1], Buffer[2], Buffer[3]));
    DEBUG((DEBUG_INFO, "#######Read by Address: 0x4000000: 0x%08x 0x%08x 0x%08x 0x%08x\n", *Addr, *(Addr+1), *(Addr+2), *(Addr + 3)));
}

Printed log

TestDxeTestNorFlashProtocol
#######Read by Bufffer:			   0xFFF12B8D 0x4C8B7696 0x472785A9 0x504F5B07
#######Read by Address: 0x4000010: 0xFFF12B8D 0x4C8B7696 0x472785A9 0x504F5B07
#######Read by Bufffer: 		   0x00000000 0x00000001 0x00000002 0x00000003
#######Read by Address: 0x4000000: 0x00000000 0x00000001 0x00000002 0x00000003
原网站

版权声明
本文为[MyeDy]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/174/202206230912174346.html