有时间分析 物理地址到虚拟地址的映射

时间:2022-09-01 08:24:24

物理地址转换为虚拟地址(动态)

作者: timkyle_zhang  来源: 博客园  发布时间:2012-04-24 20:51  阅读:104 次   原文链接    [收藏]  

说明:

  参考文章http://blog.csdn.net/do2jiang/article/details/5450839

  几乎每一种外设都是通过读写设备上的寄存器来进行的,通常包括控制寄存器、状态寄存器和数据寄存器三大类,外设的寄存器通常被连续地编址。
  根据CPU体系结构的不同,CPU对IO端口的编址方式有两种:
  (1)I/O映射方式(I/O-mapped)
  典型地,如X86处理器为外设专门实现了一个单独的地址空间,称为"I/O地址空间"或者"I/O端口空间",CPU通过专门的I/O指令(如X86的IN和OUT指令)来访问这一空间中的地址单元。
  (2)内存映射方式(Memory-mapped)
  RISC指令系统的CPU(如ARM、PowerPC等)通常只实现一个物理地址空间,外设I/O端口成为内存的一部分。
  此时,CPU可以象访问一个内存单元那样访问外设I/O端口,而不需要设立专门的外设I/O指令。

  但是,这两者在硬件实现上的差异对于软件来说是完全透明的,驱动程序开发人员可以将内存映射方式的I/O端口和外设内存统一看作是"I/O内存"资源。
  一般来说,在系统运行时,外设的I/O内存资源的物理地址是已知的,由硬件的设计决定。
  但是CPU通常并没有为这些已知的外设I/O内存资源的物理地址预定义虚拟地址范围,驱动程序并不能直接通过物理地址访问I/O内存资源,
  而必须将它们映射到核心虚地址空间内(通过页表),然后才能根据映射所得到的核心虚地址范围,通过访内指令访问这些I/O内存资源。

  Linux在arch/arm/include/asm/io.h头文件中声明了函数ioremap(),用来将I/O内存资源的物理地址映射到核心虚地址空间(3GB-4GB)中,原型如下:  

1 #define ioremap(cookie,size)            __arm_ioremap(cookie, size, MT_DEVICE)

  iounmap函数用于取消ioremap()所做的映射,原型如下:

1 #define iounmap(cookie)                 __arch_iounmap(cookie)

  这两个函数都是实现在arch/arm/mm/ioremap.c文件中。

 1 void __iomem * __arm_ioremap(unsigned long phys_addr, size_t size, unsigned int mtype)
 2 {
 3         unsigned long last_addr;
 4         unsigned long offset = phys_addr & ~PAGE_MASK;
 5         unsigned long pfn = __phys_to_pfn(phys_addr);
 6 
 7         /*         * Don't allow wraparound or zero size         */
 8         last_addr = phys_addr + size - 1;
 9         if (!size || last_addr < phys_addr)
10                 return NULL;
11         return __arm_ioremap_pfn(pfn, offset, size, mtype);
12 }

  在将I/O内存资源的物理地址映射成核心虚地址后,理论上讲我们就可以象读写RAM那样直接读写I/O内存资源了。
  为了保证驱动程序的跨平台的可移植性,我们应该使用Linux中特定的函数来访问I/O内存资源,而不应该通过指向核心虚地址的指针来访问。

  独立编址   x86   端口和内存各自有独立的地址空间
  统一编址   arm   设备端口和设备内存 共用同一个地址空间

 

  设备端口(ioport)访问:
         4      2        1
  读   inl      inw     inb
  写   outl   outw   outb

 

  设备内存(iomem)访问:
  读   ioread32    ioread16    ioread8
        readl         readw        readb      readsb
  写   iowrite32   iowrite16   iowrite8
        writel        writew       writeb      writesb

 1 #define readsb(p,d,l)           __raw_readsb(__mem_pci(p),d,l)
 2 #define readsw(p,d,l)           __raw_readsw(__mem_pci(p),d,l)
 3 #define readsl(p,d,l)           __raw_readsl(__mem_pci(p),d,l)
 4 
 5 #define writesb(p,d,l)          __raw_writesb(__mem_pci(p),d,l)
 6 #define writesw(p,d,l)          __raw_writesw(__mem_pci(p),d,l)
 7 #define writesl(p,d,l)          __raw_writesl(__mem_pci(p),d,l)
 8 
 9 #define readb(c) ({ __u8  __v = __raw_readb(__mem_pci(c)); __v; })
10 #define readw(c) ({ __u16 __v = le16_to_cpu((__force __le16) \
11                                         __raw_readw(__mem_pci(c))); __v; })
12 #define readl(c) ({ __u32 __v = le32_to_cpu((__force __le32) \
13                                         __raw_readl(__mem_pci(c))); __v; })
14 
15 #define writeb(v,c)             __raw_writeb(v,__mem_pci(c))
16 #define writew(v,c)             __raw_writew((__force __u16) \
17                                         cpu_to_le16(v),__mem_pci(c))
18 #define writel(v,c)             __raw_writel((__force __u32) \
19                                         cpu_to_le32(v),__mem_pci(c))
20 
21 #define memset_io(c,v,l)        _memset_io(__mem_pci(c),(v),(l))
22 #define memcpy_fromio(a,c,l)    _memcpy_fromio((a),__mem_pci(c),(l))
23 #define memcpy_toio(c,a,l)      _memcpy_toio(__mem_pci(c),(a),(l))
24 
25 #ifndef ioread8
26 #define ioread8(p)      ({ unsigned int __v = __raw_readb(p); __v; })
27 #define ioread16(p)     ({ unsigned int __v = le16_to_cpu((__force __le16)__raw_readw(p)); __v; })
28 #define ioread32(p)     ({ unsigned int __v = le32_to_cpu((__force __le32)__raw_readl(p)); __v; })
29 
30 #define iowrite8(v,p)   __raw_writeb(v, p)
31 #define iowrite16(v,p)  __raw_writew((__force __u16)cpu_to_le16(v), p)
32 #define iowrite32(v,p)  __raw_writel((__force __u32)cpu_to_le32(v), p)
33 
34 #define ioread8_rep(p,d,c)      __raw_readsb(p,d,c)
35 #define ioread16_rep(p,d,c)     __raw_readsw(p,d,c)
36 #define ioread32_rep(p,d,c)     __raw_readsl(p,d,c)
37 
38 #define iowrite8_rep(p,s,c)     __raw_writesb(p,s,c)
39 #define iowrite16_rep(p,s,c)    __raw_writesw(p,s,c)
40 #define iowrite32_rep(p,s,c)    __raw_writesl(p,s,c)

 

程序一:把LED灯对应的物理地址转换为虚拟地址

创建文件夹/nfsroot/kern/2012-04-24/01/。

创建文件/nfsroot/kern/2012-04-24/01/test.c,内容如下: 

 1 #include <linux/init.h>
 2 #include <linux/module.h>
 3 #include <linux/io.h>
 4 
 5 MODULE_LICENSE("GPL");
 6 
 7 // GPM0/1/2/3 --> LED0/1/2/3
 8 #define GPMCON  0x7F008820
 9 #define GPMDAT  0x7F008824
10 
11 static unsigned long p_conf = GPMCON;
12 static unsigned long p_data = GPMDAT;
13 
14 static unsigned int size = 4;
15 
16 static void * v_conf;
17 static void * v_data;
18 
19 static int __init test_init(void)
20 {
21     v_conf = ioremap(p_conf, size);
22     v_data = ioremap(p_data, size);
23 
24     *(volatile unsigned long *)v_conf = 0x1111;
25     *(volatile unsigned long *)v_data = 0x0;
26 
27     return 0;
28 }
29 
30 static void __exit test_exit(void)
31 {
32     *(volatile unsigned long *)v_data = 0xf;
33 
34     iounmap(v_data);
35     iounmap(v_conf);
36 }
37 
38 module_init(test_init);
39 module_exit(test_exit);

 创建文件/nfsroot/kern/2012-04-24/01/Makefile,内容如下:

 1 obj-m := test.o
 2 
 3 KERN := /timkyle-dev/my/arm/newnfs/home/linux-2.6.28_smdk6410
 4 
 5 all:
 6     make -C $(KERN) M=`pwd` modules
 7 
 8 clean:
 9     make -C $(KERN) M=`pwd` modules clean
10     rm -f modules.order

在主机端编译模块,过程如下:

 1 [root@localhost 01]# pwd
 2 /nfsroot/kern/2012-04-24/01
 3 [root@localhost 01]# ls
 4 Makefile  test.c
 5 [root@localhost 01]# make
 6 make -C /timkyle-dev/my/arm/newnfs/home/linux-2.6.28_smdk6410 M=`pwd` modules
 7 make[1]: Entering directory `/timkyle-dev/my/arm/newnfs/home/linux-2.6.28_smdk6410'
 8   CC [M]  /nfsroot/kern/2012-04-24/01/test.o
 9   Building modules, stage 2.
10   MODPOST 1 modules
11   CC      /nfsroot/kern/2012-04-24/01/test.mod.o
12   LD [M]  /nfsroot/kern/2012-04-24/01/test.ko
13 make[1]: Leaving directory `/timkyle-dev/my/arm/newnfs/home/linux-2.6.28_smdk6410'
14 [root@localhost 01]# ls
15 Makefile       Module.symvers  test.ko     test.mod.o
16 modules.order  test.c          test.mod.c  test.o
17 [root@localhost 01]# modinfo test.ko 
18 filename:       test.ko
19 license:        GPL
20 depends:        
21 vermagic:       2.6.28.6 mod_unload ARMv6 
22 [root@localhost 01]# 

在开发板端载入模块,观看LED情况,卸载模块,过程如下:

 1 [root@timkyle 01]# pwd
 2 /kern/2012-04-24/01
 3 [root@timkyle 01]# ls
 4 Makefile        modules.order   test.ko         test.mod.o
 5 Module.symvers  test.c          test.mod.c      test.o
 6 [root@timkyle 01]# modinfo test.ko 
 7 filename:       test.ko
 8 license:        GPL
 9 vermagic:       2.6.28.6 mod_unload ARMv6 
10 [root@timkyle 01]# lsmod 
11 [root@timkyle 01]# insmod test.ko 
12 [root@timkyle 01]# lsmod
13 test 1224 0 - Live 0xbf00c000
14 [root@timkyle 01]# rmmod test
15 [root@timkyle 01]# lsmod
16 [root@timkyle 01]# 

 

程序二:对虚拟地址由直接指针操作,改为用内核函数操作

创建文件夹/nfsroot/kern/2012-04-24/02/。

创建文件/nfsroot/kern/2012-04-24/02/test.c,内容如下:

 1 #include <linux/init.h>
 2 #include <linux/module.h>
 3 #include <linux/io.h>
 4 
 5 MODULE_LICENSE("GPL");
 6 
 7 // GPM0/1/2/3 --> LED0/1/2/3
 8 #define GPMCON  0x7F008820
 9 #define GPMDAT  0x7F008824
10 
11 static unsigned long p_conf = GPMCON;
12 static unsigned long p_data = GPMDAT;
13 
14 static unsigned int size = 4;
15 
16 static void * v_conf;
17 static void * v_data;
18 
19 static int __init test_init(void)
20 {
21     v_conf = ioremap(p_conf, size);
22     v_data = ioremap(p_data, size);
23 
24 #if 0
25     *(volatile unsigned long *)v_conf = 0x1111;
26     *(volatile unsigned long *)v_data = 0x0;
27 #else
28     iowrite32(0x1111, v_conf);
29     iowrite32(0x0, v_data);
30 #endif
31 
32     return 0;
33 }
34 
35 static void __exit test_exit(void)
36 {
37 #if 0
38     *(volatile unsigned long *)v_data = 0xf;
39 #else
40     iowrite32(0xf, v_data);
41 #endif
42 
43     iounmap(v_data);
44     iounmap(v_conf);
45 }
46 
47 module_init(test_init);
48 module_exit(test_exit);

创建文件/nfsroot/kern/2012-04-24/02/Makefile,内容和程序一相同。

在主机端编译模块,过程和程序一相同。

在开发板端载入模块,观看LED情况,卸载模块,过程和程序一相同。

 

程序三:整页映射,然后通过偏移量对应需要地址

创建文件夹/kern/2012-04-24/08/。

创建文件/kern/2012-04-24/08/test.c,内容如下:

 

 1 #include <linux/init.h>
 2 #include <linux/module.h>
 3 #include <linux/io.h>
 4 
 5 MODULE_LICENSE("GPL");
 6 
 7 // GPMCON  0x7F008820
 8 // GPMDAT  0x7F008824
 9 #define BASE 0x7F008000
10 
11 static unsigned long base = BASE;
12 static void *v_base;
13 static void *v_conf;
14 static void *v_data;
15 
16 static int __init test_init(void)
17 {
18         v_base = ioremap(base, SZ_4K);
19         v_conf = v_base + 0x820;
20         v_data = v_base + 0x824;
21 
22         iowrite32(0x1111, v_conf);
23         iowrite32(0x0, v_data);
24 
25         return 0;
26 }
27 
28 static void __exit test_exit(void)
29 {
30         iowrite32(0xf, v_data);
31         iounmap(v_base);
32 }
33 
34 module_init(test_init);
35 module_exit(test_exit);

 

创建文件/nfsroot/kern/2012-04-24/08/Makefile,内容和程序一相同。

在主机端编译模块,过程和程序一相同。

在开发板端载入模块,观看LED情况,卸载模块,过程和程序一相同。