在CPU体系结构中,MMU和cache一样,属于core的范畴 。是CPU的一部分,不属于外部设备。这点和内存控制器不同,所以s3c2440没有MMU的参考资料,而需要查看ARM920t的参考资料。
先来分析MMU是如何转换虚拟地址到物理地址的。
TLB miss发生时,硬件会去查找内存中的页表。Translation Table Base (TTB) 寄存器存储了1级页表的地址。TTB的低14位必须为0,也就是说1级页表必须在内存中对齐到16KB边界。
1级页表每个条目描述1M的虚拟地址,因此4G内存空间,共有4096个1级页表条目,每个条目占32位。
1及页表描述符的格式如上,最低两位解释了描述符的内容。
00 表示该页不存在,虚拟地址没有对应的物理地址,MMU将产生异常。
10 Section descriptor
Section映射。没有二级页。每个section为1M,低20位全为0,用31:20,即可得到物理地址section。各个位段的内容如下表:
Bits | Description |
31:20 | Form the corresponding bits of the physical address for a section |
19:12 | Always written as 0 |
11:10 | (AP) Specify the access permissions for this section |
9 | Always written as 0 |
8:5 | Specify one of the 16 possible domains (held in the domain access control register) that contain the primary access controls |
4 | Should be written as 1, for backward compatibility |
3:2 | These bits (C and B) indicate whether the area of memory mapped by this section is treated as write-back cachable, write-through cachable, noncached buffered, or noncached nonbuffered |
1:0 | These bits must be 10 to indicate a section descriptor |
CB位指定了内存的缓存策略。
01 Coarse page table descriptor
二级映射,支持large page和small page两种。Coarse page table有256个条目,将Section的1M空间分为4K大小的页。
Bits | Description |
31:10 | These bits form the base for referencing the level two descriptor (the coarse page table index for the entry is derived from the MVA) |
9 | Always written as 0 |
8:5 | These bits specify one of the 16 possible domains (held in the domain access control registers) that contain the primary access controls |
4 | Always written as 1 |
3:2 | Always written as 0 |
1:0 | These bits must be 01 to indicate a coarse page table descriptor |
由于coarse page table基地址的低10位为0,所以其在内存中需要对齐到1KB边界。
11 Fine page table descriptor
二级映射,支持large/small/tiny page,Fine page table有1024个条目,将Section的1M空间分为1K大小的页。
Bits | Description |
31:12 | These bits form the base for referencing the level two descriptor (the fine page table index for the entry is derived from the MVA) |
11:9 | Always written as 0 |
8:5 | These bits specify one of the 16 possible domains (held in the domain access control registers) that contain the primary access controls |
4 | Always written as 1 |
3:2 | Always written as 0 |
1:0 | These bits must be 11 to indicate a fine page table descriptor |
由于Fine page table的基地址低12位为0,所以其在内存中需要对齐到4KB边界。
二级页表有两种,分为粗页表(Coarse page table)和细页表(Fine page table),其描述符内容如下:
粗页表有256个条目,每个条目表示4KB的页,支持Large page(64K)和Small page(4K)两种。
细页表有1024个条目,每个条目表示1KB的页,除支持大页和小页外,还支持Tiny page(1K)。
粗页表中的大页可以分为16个子页,因此在粗页表中,大页条目需要重复16次。大页和小页在细页表中页也是类似的。
将GPIO寄存器的地址段0x56000000,通过二级页表,映射到4K-8K。然后通过新的虚拟地址实现跑马灯。
创建页表和初始化MMU的代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 | /* * 使用二级页表将GPIO地址空间映射到4K-8K * 1级页表放在sdram的起始地址0x30000000 * GPIO空间的2级页表放在1级页表之后 */ void create_page_table() { /* * s3c2440的GPIO寄存器从0x56000000开始 * 映射 0x5600,0000 - 0x5600,0fff * 到 0x1000 - 0x1fff */ unsigned int *l1page = (unsigned int *)0x30000000; unsigned int *l2page = l1page + 4096; int i; // init section table for (i = 0; i < 4096; i++) { unsigned int l1desc; unsigned int vm = i << 20; // GPIO地址使用二级页表 if ( vm == 0) { l1desc = (unsigned int )l2page; // bits 31:10 is coarse page table base l1desc |= 0x1; // bits 1:0 should be '01b' for coarse page table descriptor } else { l1desc = i << 20; // bits 31:20 section base address l1desc |= (3 << 10); // bits 11:10 Access Permission if ( vm >= 0x30000000 && vm < 0x34000000) { l1desc |= (3 << 2); // bits 3:2 is CB, enable cache and buffer for SDRAM } l1desc |= 0x2; // bits 1:0 should be '10b' for section descriptor } l1desc |= (1 << 4); // bit 4 must be 1 *(l1page + i) = l1desc; } // init coarse page table for (i = 0; i < 256; i++) { unsigned int l2desc; // 4K is the second page if (i == 1) l2desc = 0x56000000; // map 0x56000000 to 4KB else l2desc = i << 12; l2desc |= (0xff << 4); // bits 11:4 are cp3-cp0, all set to 1 to allow r/w l2desc |= 0x2; // bits 1:0 should be '10b' for small page *(l2page + i) = l2desc; } } void init_mmu() { unsigned int ttb = 0x30000000; unsigned int mmu_ctrl; asm volatile ( // step 1: disable ICache and DCache "mov r0, #0\n\t" "mcr p15, 0, r0, c7, c7, 0\n\t" // step 2: drain write buffer "mcr p15, 0, r0, c7, c10, 4\n\t" // step 3: flush TLB "mcr p15, 0, r0, c8, c7, 0\n\t" // step 4: set domain access control register to all 1 "mvn r0, #0\n\t" "mcr p15, 0, r0, c3, c0, 0\n\t" // step 5: set translation table base register "mcr p15, 0, %0, c2, c0, 0\n" : : "r" (ttb) : "r0" ); // now we read mmu control register asm volatile ( "mrc p15, 0, %0, c1, c0, 0\n" : "=r" (mmu_ctrl)); /* clear/set related bits * * bits 29:15 read: UNP, WRITE: SBZ * bits 11:10 read: 00, write: 00 * * bit 14: TLB replacement algorithm, 0: random, 1: round-robin * bit 13: Base location of exception registers, 0: 0x0, 1: 0xFFFF0000 * bit 12: ICache enabled, 0: disabled, 1: enabled * bit 9 : R bit * bit 8 : S bit * bit 7 : B bit, endianness, 0: little, 1 big * bit 2 : C bit, DCache enable, 0: disabled, 1 enabled * bit 1 : A bit, alignment check, 0: disable check, 1: enable check * bit 0 : M bit, MMU enable, 0: disable, 1: enable */ mmu_ctrl &= 0xc000000; // clear 29:0 mmu_ctrl |= (1 << 12); // enable ICache mmu_ctrl |= (1 << 2); // enable DCache mmu_ctrl |= (1 << 1); // enable Alignment check mmu_ctrl |= (1 << 0); // enable MMU // now write it back asm volatile ( "mcr p15, 0, %0, c1, c0, 0\n" : : "r" (mmu_ctrl)); } |
这里灵活应用了内联汇编。
ARM920T Technical Reference Manual
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0151c/index.html