关于访问物理地址

jxixian   2010-6-4 10:27 楼主
我看SMDK6410里面的那些驱动访问物理地址来读写寄存器,都是用一个函数DrvLib_MapIoSpace()来将手册上寄存器的物理地址转换一下。

在_WIN32_WCE >= 600的时候(我用的就是wince6),函数如下,那些驱动在用这个时第三个参数基本都是false:

  1. void *DrvLib_MapIoSpace(UINT32 PhysicalAddress, UINT32 NumberOfBytes, BOOL CacheEnable)
  2. {
  3.     UINT32 MappedAddr = 0;
  4.     int index = 0;

  5.     DRVLIB_MSG((_T("[DRVLIB] ++DrvLib_MapIoSpace(0x%08x, 0x%08x, %d)\n\r"), PhysicalAddress, NumberOfBytes, CacheEnable));

  6.     while(g_oalAddressTable[index][2])    // Find to end of table
  7.     {
  8.         if (    (PhysicalAddress >= g_oalAddressTable[index][1])
  9.             && (PhysicalAddress < ( g_oalAddressTable[index][1]+0x100000*g_oalAddressTable[index][2]) ))
  10.         {
  11.             // Matched Address Found

  12.             MappedAddr = g_oalAddressTable[index][0] +(PhysicalAddress - g_oalAddressTable[index][1]);
  13.             if (!CacheEnable)
  14.             {
  15.                 MappedAddr += 0x20000000;    // Offset to Uncached Area
  16.             }

  17.             break;
  18.         }

  19.         index++;        // Find Next
  20.     }

  21.     if (MappedAddr)
  22.     {
  23.         DRVLIB_MSG((_T("[DRVLIB] --DrvLib_MapIoSpace() = 0x%08x\n\r"), MappedAddr));

  24.         return (void *)MappedAddr;
  25.     }
  26.     else
  27.     {
  28.         DRVLIB_ERR((_T("[DRVLIB:ERR] --DrvLib_MapIoSpace() = NULL\n\r")));

  29.         return NULL;
  30.     }
  31. }


而表g_oalAddressTable[][3]是这么一个:

        DCD     0x80000000, 0x50000000,  128     ; 128 MB DRAM
        DCD     0x90000000, 0x70000000,  4      ; SROM SFR      
        DCD     0x90400000, 0x71000000,  4      ; TZIC0      
        DCD     0x90800000, 0x72000000,  1    ; FIMG-3DSE SFR                    
        DCD     0x90A00000, 0x74000000,  2      ; Indirect Host I/F      
        DCD     0x90C00000, 0x74300000,  2      ; USB Host      
        DCD     0x90E00000, 0x75000000,  2      ; DMA0      
        DCD     0x91000000, 0x76100000,  3      ; 2D Graphics      
        DCD     0x91300000, 0x77000000,  3      ; Post Processor
        DCD     0x91600000, 0x78000000,  1      ; Camera I/F
        DCD     0x91700000, 0x78800000,  1      ; JPEG
        DCD     0x91800000, 0x7C000000,  5      ; USB OTG LINK
        DCD     0x91D00000, 0x7D000000,  13      ; D&I(Security Subsystem Config) SFR
        DCD     0x92A00000, 0x7E000000,  1      ; DMC, MFC, WDT, RTC, HSI TX/RX, Keypad, ADC, SYSCON
        DCD     0x92B00000, 0x7F000000,  1      ; TZPC, AC97, I2S, I2C, UART, PWM, IrDA, GPIO, PCM, SPI
        DCD     0x93000000, 0x00000000,  16      ; 32 MB SROM(SRAM/ROM) BANK 0
        DCD     0x94000000, 0x18000000,  32      ; 32 MB SROM(SRAM/ROM) BANK 1 = DM9000A
        DCD     0x00000000, 0x00000000,  0      ; end of table

那么经过这样的转换得到的就是所谓程序可以使用的虚拟地址么?

如果是这样,那我在程序里面也按照这种方法直接给一个指针赋值,不就可以肆意访问物理地址了么?比如:
我想访问0x50001000,那么就按照这个表,在程序里给一个指针赋值成0xA0001000,然后使用它。

姑且不讨论什么安全不安全,按这样做就能访问到指定的物理地址了吗?(感觉这个转换好简单……)

这种转换的意义又何在呢?

回复评论 (6)

这个是静态映射,在wince6里面只有内核态的程序才可以访问
在wince5里应用程序也可以访问
虚拟内存映射分静态和动态
除了安全原因,还有一个重要的作用是协同多个应用程序,让他们彼此不互相影响
简单的说
你写了个应用程序,定义int i,编译以后 i 就是一个内存地址
我写了一个应用程序 也int i,也是一个内存地址
如果都用物理地址,我的编译器不知道你用了哪些物理地址,哪些没用,有可能把i定义到和你相同的地址上
程序就混乱了.
有了动态映射,即使两个应用程序的i都定义在一个地址上,操作系统会把这个相同的虚拟地址映射到不同的物理地址
点赞  2010-6-4 10:58
楼上分析的很精辟
点赞  2010-6-4 11:09
引用: 引用 1 楼 reallyu 的回复:

这个是静态映射,在wince6里面只有内核态的程序才可以访问
在wince5里应用程序也可以访问
虚拟内存映射分静态和动态
除了安全原因,还有一个重要的作用是协同多个应用程序,让他们彼此不互相影响
简单的说
你写了个应用程序,定义int i,编译以后 i 就是一个内存地址
我写了一个应用程序 也int i,也是一个内存地址
如果都用物理地址,我的编译器不知道你用了哪些物理地址,哪……


嗯……我很迟钝……要理解一会……………………是不是这个意思:

在用户态,虚拟地址到物理地址的映射使每个进程拥有独立的地址空间,是为了让他们之间不互相影响。而这种映射属于“动态”。

那么在内核态,用我看到的这种方法进行地址映射是“静态”,静态的地址映射的意义又是什么呢?

在BSP里面的那些驱动都是运行在内核态的吧,那在这些驱动里面如果用malloc分配得到一块内存,获得的虚拟地址也是按照这种方式映射的么?
点赞  2010-6-4 12:02


一般不访问硬件的程序,不用关心底层到底访问到哪里的物理地址,因为你的任何变量,函数撒的都是在访问DDR中的一块内存,你没必要去关心。就如malloc,它就分配一段内存而已,它分配第几块,不会影响你的程序。

而如果你要访问确定的物理地址,比如寄存器,你必须做虚拟地址的映射,操作系统只认虚拟地址,g_oalAddressTable是给MMU用的。
比如DCD 0x80000000, 0x50000000, 128 ; 128 MB DRAM
根据这些项,当程序遇到一个地址是0x80000000~0x90000000 时,mmu知道你访问的是DRAM,就映射到对应的物理地址,而你没必要关心这一段,因为你不需要非得用Dram中的哪一段保存你的数据。
而其他项,你就需要按照映射表操作了。偏一点,就访问的其他寄存器。

“这种转换的意义又何在呢?”
操作系统只认虚拟地址,不同的芯片它内部的地址空间不同,你需要把对应的地址空间整合进2GB的kernel地址空间中去。如果直接访问物理地址(比如0x50000000),就会出错,因为wince6中0x80000000以下的是用户程序空间(在物理上这实际上DDR中的一段)。
点赞  2010-6-5 16:50
“比如:我想访问0x50001000,那么就按照这个表,在程序里给一个指针赋值成0xA0001000,然后使用它。”

可以这样做,你实际访问的是DDR中offset为0x1000的单元(uncached)
点赞  2010-6-5 16:54
谢谢大家解答~
点赞  2010-6-7 09:44
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 京公网安备 11010802033920号
    写回复