ILD

MMU
作者:HerbertYuan 邮箱:yuanjp89@163.com
发布时间:2017-10-8 站点:Inside Linux Development

在CPU体系结构中,MMU和cache一样,属于core的范畴 。是CPU的一部分,不属于外部设备。这点和内存控制器不同,所以s3c2440没有MMU的参考资料,而需要查看ARM920t的参考资料。


1 ARMv4 MMU

先来分析MMU是如何转换虚拟地址到物理地址的。


1.1 Translation Table Base

TLB miss发生时,硬件会去查找内存中的页表。Translation Table Base (TTB) 寄存器存储了1级页表的地址。TTB的低14位必须为0,也就是说1级页表必须在内存中对齐到16KB边界。


1.2 Translation table 一级页表

1级页表每个条目描述1M的虚拟地址,因此4G内存空间,共有4096个1级页表条目,每个条目占32位。

1及页表描述符的格式如上,最低两位解释了描述符的内容。


00 表示该页不存在,虚拟地址没有对应的物理地址,MMU将产生异常。


10 Section descriptor

Section映射。没有二级页。每个section为1M,低20位全为0,用31:20,即可得到物理地址section。各个位段的内容如下表:

BitsDescription
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:5Specify one of the 16 possible domains (held in the domain access control register) that contain the primary access controls
4Should 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:0These 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大小的页。

BitsDescription
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大小的页。

BitsDescription
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边界。


1.3 二级页表

二级页表有两种,分为粗页表(Coarse page table)和细页表(Fine page table),其描述符内容如下:

粗页表有256个条目,每个条目表示4KB的页,支持Large page(64K)和Small page(4K)两种。

细页表有1024个条目,每个条目表示1KB的页,除支持大页和小页外,还支持Tiny page(1K)。

粗页表中的大页可以分为16个子页,因此在粗页表中,大页条目需要重复16次。大页和小页在细页表中页也是类似的。


2 实战

将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


Copyright © linuxdev.cc 2017-2024. Some Rights Reserved.