当前位置:网站首页>UEFI源码学习3.7 - NorFlashDxe
UEFI源码学习3.7 - NorFlashDxe
2022-06-23 09:12:00 【MyeDy】
文章目录
AARCH64 QEMU上的NOR FLASH驱动位于ArmPlatFromPkg/Drivers/NorFlashDxe目录下。
1. QEMU CFI NOR FLASH
在分析QEMU 上CFI NOR FLASH之前,需要知道一些QEMU CFI NOR FLASH的参数。
FLASH ARRAY地址: 0x04000000 - 0x08000000
寄存器地址:0x04000000。寄存器和FLASH ARRAY用的同一个地址,当写入0xFF命令到寄存器时,操作的是FLASH ARRAY。否则操作的寄存器。
命令表
| 命令 | 功能 |
|---|---|
| 0x00 | Reset |
| 0x10 | 单字节写入 |
| 0x20 | Block擦除 |
| 0x28 | Block擦除 |
| 0x40 | 单字节写入 |
| 0x50 | 清除Status寄存器 |
| 0x60 | Block 加锁/解锁 |
| 0x70 | 读取Status寄存器 |
| 0xe8 | 写一个Block |
| 0x90 | 读取Device ID 寄存器 |
| 0x90 | Query CFI |
| 0xff | 进入读FLASH ARRAY 模式 |
命令定义
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. 数据结构
用于描述NOR FLASH的结构叫做NOR_FLASH_INSTACE,其结构体布局如下
struct _NOR_FLASH_INSTANCE {
UINT32 Signature; --> 一个4字节的签名为"norx", x代表数字0,1,2
EFI_HANDLE Handle; --> 对应的image handle
UINTN DeviceBaseAddress; --> Nor Flash寄存器的地址
UINTN RegionBaseAddress; --> Nor Flash存储区的地址
UINTN Size; --> Nor Flash的大小
EFI_LBA StartLba; --> 起始Logic Block Address
EFI_BLOCK_IO_PROTOCOL BlockIoProtocol; --> 一个EFI_BLOCK_IO_PROTOCOL用于提供Block IO 服务
EFI_BLOCK_IO_MEDIA Media; --> 描述块设备的结构体
EFI_DISK_IO_PROTOCOL DiskIoProtocol; --> 一个EFI_DISK_IO_PROTOCOL用于提供Disk IO服务
EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL FvbProtocol; --> 一个EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL用于提供Fv2服务
VOID *ShadowBuffer; --> 缓存buffer
NOR_FLASH_DEVICE_PATH DevicePath; --> 设备路径对象
};
3. 初始化
NorFlashInitialise 是NorFlashDxe的入口函数,这里面初始化了NorFlashDxe。
- 因为是QEMU,NorFlashPlatformInitialization 这里啥也没干。实际的硬件上应该需要初始化clock,power, iomux等等。
- NorFlashPlatformGetDevices 从FDT(设备树文件上获取NOR FLASH的实例以及个数)。这里例子中只有一个NOR FLASH,所以mNorFlashDeviceCount = 1。
- 调用AllocateRuntimePool 为每一个分配NOR_FLASH_INSTANCE实例所需要的内存。
- 根据PCD获取ContainVariableStorage信息。
- 调用NorFlashCreateInstance 为每一个NOR FLASH的对象成员初始化。
- 创建Event,用于在虚拟地址改变的时候回调NorFlashVirtualNotifyEvent函数。
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. 因为是QEMU,NorFlashPlatformInitialization 这里啥也没干。实际的硬件上应该需要初始化clock,power, iomux等等。
Status = NorFlashPlatformInitialization ();
//2. NorFlashPlatformGetDevices 从FDT(设备树文件上获取NOR FLASH的实例以及个数)。这里例子中只有一个NOR FLASH,所以mNorFlashDeviceCount = 1。
Status = NorFlashPlatformGetDevices (&NorFlashDevices, &mNorFlashDeviceCount);
//3. 调用AllocateRuntimePool 为每一个分配NOR_FLASH_INSTANCE实例。
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. 根据PCD获取ContainVariableStorage信息。
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. 调用NorFlashCreateInstance 为每一个NOR FLASH的对象成员初始化。
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. 创建Event,用于在虚拟地址改变的时候回调NorFlashVirtualNotifyEvent函数。
Status = gBS->CreateEventEx (
EVT_NOTIFY_SIGNAL,
TPL_NOTIFY,
NorFlashVirtualNotifyEvent,
NULL,
&gEfiEventVirtualAddressChangeGuid,
&mNorFlashVirtualAddrChangeEvent
);
ASSERT_EFI_ERROR (Status);
return Status;
}
NOR FLASH对象的成员主要在NorFlashCreateInstance中初始化。
- 首先调用AllocateRuntimeCopyPool把mNorFlashInstanceTemplate中的内容复制到NOR_FLASH_INSTANCE上。可以看到mNorFlashInstanceTemplate主要是一些函数指针。
- 根据参数初始化NOR_FLASH_INSTANCE各个成员。
| 成员名 | 值 |
|---|---|
| DeviceBaseAddress | 0x04000000 |
| RegionBaseAddress | 0x04000000 |
| Size | 0x04000000 |
| Media.MediaId | 0 |
| Media.BlockSize | 0x40000 |
| DevicePath.Vendor.Guid | 93E34C7E-B50E-11DF-9223-2443DFD72085 |
- 调用AllocateRuntimePool分配缓存buffer。
- NorFlashFvbInitialize 初始化NOR FLASH上的FV。
- 调用InstallMultipleProtocolInterfaces 安装以下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. 首先调用AllocateRuntimeCopyPool把mNorFlashInstanceTemplate中的内容复制到NOR_FLASH_INSTANCE上。可以看到mNorFlashInstanceTemplate主要是一些函数指针。
Instance = AllocateRuntimeCopyPool (sizeof (NOR_FLASH_INSTANCE), &mNorFlashInstanceTemplate);
if (Instance == NULL) {
return EFI_OUT_OF_RESOURCES;
}
//2. 根据参数初始化NOR_FLASH_INSTANCE各个成员。
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. 调用AllocateRuntimePool分配缓存buffer。
Instance->ShadowBuffer = AllocateRuntimePool (BlockSize);
if (Instance->ShadowBuffer == NULL) {
return EFI_OUT_OF_RESOURCES;
}
//4. NorFlashFvbInitialize 初始化NOR FLASH上的FV。
NorFlashFvbInitialize (Instance);
//5. 安装以下Protoco
Status = gBS->InstallMultipleProtocolInterfaces (
&Instance->Handle,
&gEfiDevicePathProtocolGuid,
&Instance->DevicePath,
&gEfiBlockIoProtocolGuid,
&Instance->BlockIoProtocol,
&gEfiFirmwareVolumeBlockProtocolGuid,
&Instance->FvbProtocol,
&gEfiDiskIoProtocolGuid,
&Instance->DiskIoProtocol,
NULL
);
...
*NorFlashInstance = Instance;
return Status;
}
mNorFlashInstanceTemplate的定义如下,实现了一些Protocol的函数。
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 初始化NOR FLASH上的FV头。
- 调用AddMemorySpace和SetMemorySpaceAttributes 向DxeCore上报NOR FLASH的内存空间。
- 调用ValidateFvHeader查看NOR FLASH上是否存在FV。
- 如果不存在FV的话,就在NOR FLASH上初始化FV,这个FV是用来存Non Volatile Variable的。
- 安装Protocol:gEdkiiNvVarStoreFormattedGuid。
- 创建Event,在虚拟地址改变时回调FvbVirtualNotifyEvent。
EFI_STATUS
EFIAPI
NorFlashFvbInitialize (
IN NOR_FLASH_INSTANCE *Instance
)
{
EFI_STATUS Status;
UINT32 FvbNumLba;
EFI_BOOT_MODE BootMode;
UINTN RuntimeMmioRegionSize;
...
//1. 调用AddMemorySpace和SetMemorySpaceAttributes 向DxeCore上报NOR FLASH的内存空间。
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
);
//根据PCD获取NvStorageVariable存放的基地址
mFlashNvStorageVariableBase = (PcdGet64 (PcdFlashNvStorageVariableBase64) != 0) ?
PcdGet64 (PcdFlashNvStorageVariableBase64) : PcdGet32 (PcdFlashNvStorageVariableBase);
//2. 调用ValidateFvHeader查看NOR FLASH上是否存在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. 如果不存在FV的话,就在NOR FLASH上初始化FV,这个FV是用来存Non Volatile Variable的。
// 擦除Flash
FvbNumLba = (PcdGet32 (PcdFlashNvStorageVariableSize) + PcdGet32 (PcdFlashNvStorageFtwWorkingSize) + PcdGet32 (PcdFlashNvStorageFtwSpareSize)) / Instance->Media.BlockSize;
Status = FvbEraseBlocks (&Instance->FvbProtocol, (EFI_LBA)0, FvbNumLba, EFI_LBA_LIST_TERMINATOR);
// 安装NvStorageVariable FV头
Status = InitializeFvAndVariableStoreHeaders (Instance);
}
//4. 安装Protocol:gEdkiiNvVarStoreFormattedGuid。
Status = gBS->InstallProtocolInterface (
&gImageHandle,
&gEdkiiNvVarStoreFormattedGuid,
EFI_NATIVE_INTERFACE,
NULL
);
//5. 创建Event,在虚拟地址改变时回调FvbVirtualNotifyEvent。
Status = gBS->CreateEventEx (
EVT_NOTIFY_SIGNAL,
TPL_NOTIFY,
FvbVirtualNotifyEvent,
NULL,
&gEfiEventVirtualAddressChangeGuid,
&mFvbVirtualAddrChangeEvent
);
ASSERT_EFI_ERROR (Status);
return Status;
}
InitializeFvAndVariableStoreHeaders 很简单,就是根据PCD里读出NvStrorageVariable FV的各个参数,然后生成FV头,最终调用FvbWrite (&Instance->FvbProtocol, 0, 0, &HeadersLength, Headers);把FV头写入到NOR FLASH上。
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. 读写实现
4.1 NOR FLASH读写
ArmPlatFromPkg/Drivers/NorFlashDxe/NorFlash.c
单字节写入NorFlashWriteSingleWord
- 向NOR FLASH发送P30_CMD_WORD_PROGRAM_SETUP命令。
- 把数据WriteData写入NOR FLASH地址WordAddress。
- 读取Status 寄存器,等待写入完成。
- 发送P30_CMD_READ_ARRAY命令将NOR FLASH设置为READ ARRAY模式
EFI_STATUS
NorFlashWriteSingleWord (
IN NOR_FLASH_INSTANCE *Instance,
IN UINTN WordAddress,
IN UINT32 WriteData
)
{
EFI_STATUS Status;
UINT32 StatusRegister;
Status = EFI_SUCCESS;
//1. 向NOR FLASH发送P30_CMD_WORD_PROGRAM_SETUP命令
SEND_NOR_COMMAND (WordAddress, 0, P30_CMD_WORD_PROGRAM_SETUP);
//2. 把数据WriteData写入NOR FLASH地址WordAddress。
MmioWrite32 (WordAddress, WriteData);
//3. 读取Status 寄存器,等待写入完成。
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. 将NOR FLASH设置为READ ARRAY模式
SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_READ_ARRAY);
return Status;
}
其他的NorFlashWriteBlocks、NorFlashWriteSingleBlock和NorFlashWriteBuffer的流程也是类似的:发命令 ->写数据 -> 读Status寄存器 -> 设置为READ ARRAY模式。
只是发送的命令不同。
读的实现就简单了。NOR FLASH是memory map的设备,直接从0x04000000地址上读就能读到NOR FLASH的数据。
- 做一堆参数检查,获取NOR FLASH ARRAY的起始地址。
- 发送P30_CMD_READ_ARRAY命令把NOR FLASH设置为READY ARRAY模式。
- 把数据从对应的FLASH地址空间复制到目的地址上去。
EFI_STATUS
NorFlashRead (
IN NOR_FLASH_INSTANCE *Instance,
IN EFI_LBA Lba,
IN UINTN Offset,
IN UINTN BufferSizeInBytes,
OUT VOID *Buffer
)
{
UINTN StartAddress;
...
// 1. 做一堆参数检查,获取NOR FLASH ARRAY的起始地址。
StartAddress = GET_NOR_BLOCK_ADDRESS (
Instance->RegionBaseAddress,
Lba,
Instance->Media.BlockSize
);
// 2. 发送P30_CMD_READ_ARRAY命令把NOR FLASH设置为READY ARRAY模式。
SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_READ_ARRAY);
// 3. 把数据从对应的FLASH地址空间复制到目的地址上去。
AlignedCopyMem (Buffer, (VOID *)(StartAddress + Offset), BufferSizeInBytes);
return EFI_SUCCESS;
}
4.2 Block IO Protocol实现
ArmPlatFromPkg/Drivers/NorFlashDxe/NorFlashBlockIo.c,这里面就是实现了BlockIO Protocol的相关函数,底层调用还是NorFlash.c上的函数。
- NorFlashBlockIoReadBlocks 做了一堆参数检查后调用了NorFlashReadBlocks 去读数据。
- NorFlashBlockIoWriteBlocks 做了一堆检查后调用NorFlashWriteBlocks 写入数据。
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实现
Disk IO 与Block IO 也是类似的,最终调用NorFlash.c中的底层函数去实现。但是它在外层包装了一点外层逻辑以达到任意字节大小的写入功能。
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. 测试代码
测试函数如下,调用NOR FLASH的DiskIO Protocol。
- 由于当前FD上只有一个NorFlash这个DiskIO设备,所以直接用LocateProtocol去找到唯一的DiskIO以获取NOR FLASH的读写。
- 分别用DiskIO的方式和直接读FLASH地址0x4000010的方式读取FLASH上的数据。
- 往FLASH头上写入16个字节的数据。
- 分别用DiskIO的方式和直接读FLASH地址0x4000000的方式读取FLASH上的数据。
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)));
}
打印的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
边栏推荐
- Structure binary tree from preorder and inorder traversal for leetcode topic analysis
- Learn SCI thesis drawing skills (E)
- Redis学习笔记—慢查询分析
- 173. Binary Search Tree Iterator
- Leetcode topic analysis contains duplicate III
- Structure binary tree from inorder and postorder traversal for leetcode topic analysis
- Use of type dependent names must be prefixed with 'typename'
- Zone d'entrée du formulaire ionic5 et boutons radio
- 自定义标签——jsp标签增强
- Combination sum III of leetcode topic analysis
猜你喜欢

线性表(SequenceList)的顺序表示与实现----线性结构

Zone d'entrée du formulaire ionic5 et boutons radio

一个采用直接映射方式的32KB缓存......存储器课后习题

Flink error --caused by: org apache. calcite. sql. parser. SqlParseException: Encountered “time“

【学习资源】理解数学和热爱数学

The sliding window of the force button "step by step" (209. sub array with the smallest length, 904. fruit basket)
Redis学习笔记—数据类型:哈希(hash)

Redis学习笔记—持久化机制之RDB

Click Add drop-down box

ionic5錶單輸入框和單選按鈕
随机推荐
【学习资源】理解数学和热爱数学
【NanoPi2试用体验】裸机第一步
Kotlin Series 1: getting started with basics
Redis learning notes - redis and Lua
Redis学习笔记—Redis与Lua
Redis学习笔记—数据库管理
Redis learning notes - detailed explanation of redis benchmark
@Response
Leetcode topic analysis 3sum closest
JSP入门总结
【活动报名】SOFAStack × CSDN 联合举办开源系列 Meetup ,6 月 24 日火热开启
node request模块cookie使用
Learn SCI thesis drawing skills (E)
Redis learning notes - geographic information location (GEO)
Use of type dependent names must be prefixed with 'typename'
GPIO novice
Leetcode topic analysis contains duplicate III
Redis learning notes RDB of persistence mechanism
Typora设置图片上传服务
社区文章|MOSN 构建 Subset 优化思路分享