初学 Watcom C的时候遇到一个问题“如何访问指定的内存”。当时为了这个问题花费了不少功夫,最后才发现直接用指针就可以进行访问,因为过于简单以至于网上都没有人问过…….
最近看 UEFI 编程,同样也遇到了这个问题,我去查找了 mm命令的source code,看到了它使用了PCI Root Bridge I/O Protocol 这个Protocol,然后就去研究之。同样越研究越迷糊,最终发现虽然这个Protocol提供了内存访问函数,但是本质上依然使用指针来直接访问。出于保护模式下的内存,任何其他方法都有“脱了裤子放屁—–多此一举”之嫌。
参考 mm 源程序,很快写出代码:
#include <Uefi.h>
#include <Library/UefiLib.h>
#include <Library/ShellCEntryLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Protocol/DeviceIo.h>
extern EFI_BOOT_SERVICES *gBS;
extern EFI_SYSTEM_TABLE *gST;
extern EFI_RUNTIME_SERVICES *gRT;
typedef enum {
EfiPciWidthUint8,
EfiPciWidthUint16,
EfiPciWidthUint32,
EfiPciWidthUint64
} DUMMY;
VOID
ReadMem (
EFI_IO_WIDTH Width,
UINT64 Address,
UINTN Size,
VOID *Buffer
)
{
do {
if (Width == EfiPciWidthUint8) {
*(UINT8 *) Buffer = *(UINT8 *) (UINTN) Address;
Address -= 1;
} else if (Width == EfiPciWidthUint16) {
*(UINT16 *) Buffer = *(UINT16 *) (UINTN) Address;
Address -= 2;
} else if (Width == EfiPciWidthUint32) {
*(UINT32 *) Buffer = *(UINT32 *) (UINTN) Address;
Address -= 4;
} else if (Width == EfiPciWidthUint64) {
*(UINT64 *) Buffer = *(UINT64 *) (UINTN) Address;
Address -= 8;
} else {
Print(L"Can't read memory at %X",Width);
break;
}
//
//
//
Size--;
} while (Size > 0);
}
int
EFIAPI
main (
IN int Argc,
IN CHAR16 **Argv
)
{
CHAR8 *Mem1;
UINT32 Buffer;
Mem1=AllocatePool(4);
*(Mem1+0)='L';
*(Mem1+1)='A';
*(Mem1+2)='B';
*(Mem1+3)='Z';
Print(L"Memory Address: %X\n",Mem1);
Print(L"%X\n",*Mem1);
Print(L"%X\n",*(Mem1+1));
Print(L"%X\n",*(Mem1+2));
Print(L"%X\n",*(Mem1+3));
ReadMem(EfiPciWidthUint32, (UINT64)Mem1, 1, &Buffer);
Print(L"Read[%X]=%X\n",Mem1,Buffer);
FreePool(Mem1);
return EFI_SUCCESS;
}
在NT32模拟器中实验时发现,模拟环境中不是所有的内存空间都是可以直接访问的。稍有不慎就会得到错误信息,模拟器也会随之崩溃。于是,代码是创建一个4 Bytes长的内存空间,写入一些字符,然后再 ReadMem 读取出来,这样做能够保证访问的内存是可以被正常操作的。
运行结果:
参考的 mm.c 来自EfiShell 1.06\Shell\mm\mm.c。
mm
本文提到的完整代码下载
ReadMEM

樓主有興趣寫一個DMA access IO的 Application嗎?
是访问映射到memory的 IO 吗? 我没有搞过 DMA的