初学 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的