在X86和ARM架構的CPU中,wince訪問系統內存的方法隨程序所屬模式層次的不同而有所區別.
? 1.在系統內核模式下(kernel mode),在OAL層訪問,只需要在OEMAddressTable 中做靜態的虛實地址映射就可以了.例如X86架構的映射表格式如下:
???; OEMAddressTable defines the mapping between Physical and Virtual Address? // 定義4GB的虛擬地址和512MB存儲的映射關系
?? ;?? o MUST be in a READONLY Section
?? ;?? o First Entry MUST be RAM, mapping from 0x80000000 -> 0x00000000
?? ;?? o each entry is of the format ( VA, PA, cbSize )
?? ;?? o cbSize must be multiple of 4M
?? ;?? o last entry must be (0, 0, 0)
?? ;?? o must have at least one non-zero entry
?? ; RAM 0x80000000 -> 0x00000000, size 64M?????? //把物理地址為0x00000000映射到虛擬地址為 0x80000000?處
?? dd? 80000000h,??? 0,?? 04000000h
?? ; FLASH and other memory, if any
?? ; dd? FlashVA,????? FlashPA,??? FlashSize
?? ; Last entry, all zeros
?? dd? 0?? 0?? 0
2.在驅動或應用程序(user mode)中訪問RAM,既可以通過OEMAddressTable+VirtualCopy方式,也可以直接用MmMapIoSpace函數建立物理地址到當前進程虛擬地址的映射關系.
經過OEMAddressTable,實現的只是CPU物理地址到OS內核層虛擬地址的一次映射,如果需要在普通的應用程序中訪問內存,還要再用VirtuaAlloc+VirtualCopy做一個內核到當前進程的二次映射(有一種情況例外,就是你的OS被配置成Full Kernel Mode,這時任何應用程序都可以訪問OS內核地址).
?????簡單說明幾個關鍵函數:
?????VirtualAlloc用于在當前進程的虛擬地址空間中保留或者提交空間,在保留時以64KB為單位,提交時以4KB為單位。其函數原型為
?LPVOID VirtualAlloc(
? LPVOID lpAddress, ?//
分配虛擬地址的起始指針
? DWORD dwSize, ????//
大小,以字節為單位
? DWORD flAllocationType, //
類型,設為MEM_RESERVE
? DWORD flProtect????//?
存取保護,設為PAGE_NOACCESS
);
? VirtualCopy
用來綁定物理地址到靜態映射虛擬地址:
? BOOL VirtualCopy(
? LPVOID lpvDest, ????????//
虛擬目的地址指針,接受VirtualAlloc的返回值
? LPVOID lpvSrc, ????????//
源物理地址指針
? DWORD cbSize, ?????????//
大小必須與虛擬地址相同
? DWORD fdwProtect??//
存取保護類型
);
這里需要注意的是
fdwProtect
參數。如果是驅動程序訪問,需要設置為
PAGE_NOCACHE
,以訪問無緩存段虛擬地址。如果映射的物理地址范圍在
0x1FFFFFFF
之上,必須使用
PAGE_PHYSICAL
,此時必須把
lpvSrc
右移八位,實現地址對齊。(這是由內核中
VirtualCopy
的實現決定的,在那個函數中會判斷如果是
PAGE_PHYSICAL
就將
PHYSADDR
左移
8
位移回來,源代碼位于
private/winceos/coreos/nk/kernel
目錄下的
virtmem.c中的DoVirtualCopy
)
?
??? MmMapIoSpace
用來把物理地址直接映射到與進程無關的虛擬地址上。函數原型為
?PVOID MmMapIoSpace(
? PHYSICAL_ADDRESS PhysicalAddress,
? ULONG NumberOfBytes,
? BOOLEAN CacheEnable
);
?
一個使用
VirtualAlloc+Copy
的例子:把物理地址為
0x10000000
的單元映射到虛擬地址空間中。
#include <windows.h>
?
#define PHYSADDR? ((PVOID)0x10000000)
// PHYSADDR is the physical address of the peripheral
// registers
?
#define SIZE? (4800*4)
?
LPVOID lpv;
BOOL bRet;
?
lpv = VirtualAlloc(0, SIZE, MEM_RESERVE, PAGE_NOACCESS);
// For a user mode driver, always leave the first
// parameter 0 and use only the flags MEM_RESERVE
// and PAGE_NOACCESS Check the return value: lpv == 0
// is an error
?
printf(TEXT("VirtualAlloc reservation @%8.8lx\r\n"), lpv);
bRet = VirtualCopy(lpv, PHYSADDR>>8, SIZE, PAGE_READWRITE | PAGE_NOCACHE | PAGE_PHYSICAL);
// The lpv parameter is the virtual address returned
// by VirtualAlloc().
// Always use PAGE_NOCACHE */
?
// Check the return value: bRet ==0 is an error */
printf(TEXT("VirtualCopy returned: %d\r\n"), bRet);
?
// At this point lpv is a virtual address which maps
// the I/O registers
// at PHYSADDR for SIZE bytes */
?