文章目录
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