您的位置:首页 > 运维架构 > 网站架构

ARM Linux (S3C6410架构/2.6.35内核)的内存映射(五)

2013-08-05 13:50 260 查看

ARM Linux的访问权限控制

ARM1176JZF-S处理器为访问权限控制定义了两个层次:第一层是"域"(Domain)的访问类型,第二层是页或者段的"读写权限"(Access Permission)。具体来说,过程是这样的: 1. 在ARM处理器中,MMU将整个存储空间分成最多16个域,记作D0~D15,每个域对应一定的存储区域,该区域具有相同的访问控制属性。每个域的访问权限分别由CP15的C3寄存器中的两位来设定,c3寄存器的大小为32bits,刚好可以设置16个域的访问权限。
Bits31, 3029, 2827, 2625, 2423, 2221, 2019, 1817, 1615, 1413, 1211, 109, 87, 65, 43, 21, 0
DomainD15D14D13D12D11D10D9D8D7D6D5D4D3D2D1D0
对于每一个域所对应的两个bit,访问类型设置方法如下:
访问类型含义
0b00无访问权限此时访问该域将产生访问失效
0b01用户(client)根据CP15的C1控制寄存器中的R和S位以及页表中地址变换条目中的访问权限控制位AP来确定是否允许各种系统工作模式的存储访问
0b10保留使用该值会产生不可预知的结果
0b11管理者(Manager)不考虑CP15的C1控制寄存器中的R和S位以及页表中地址变换条目中的访问权限控制位AP,在这种情况下不管系统工作在特权模式还是用户模式都不会产生访问失效
2. 如果域的访问类型是0b01的话,下面就进入第二层。所访问内存页或段的权限还要根据CP15的C1寄存器中的R和S位以及页/段表项中的访问权限控制位AP(X)来决定。
APXAP[1:0]特权模式访问权限用户模式访问权限
0b00禁止访问;S=1,R=0或S=0,R=1时只读禁止访问;S=1,R=0时只读
0b01读写禁止访问
0b10读写只读
0b11读写读写
1b00保留保留
1b01只读禁止访问
1b10只读只读
1b11只读只读
[8] 参考ARM1176JZF-S Revision: r0p7->6.5.2 Access permissions
下面看一下Linux是如何实现的。
在谈到create_mapping之前,必须说明一下Linux是如何实现对页面的访问控制的。 Linux使用结构体mem_type来定义不同的内存映射类型(arch/arm/mm/mm.h),不同的映射类型定义了不同的访问权限:
[c]
struct mem_type {
unsigned int prot_pte;
unsigned int prot_l1;
unsigned int prot_sect;
unsigned int domain;
};
[/c]
其中处成员含义如下:
prot_pte代表页表项的访问控制权,pte即第二级映射表项(页表项)。
prot_l1代表段表项的访问控制位,l1即第一级映射表项(段表项/主页表项)。
prot_sect代表主页表(注意,不是主页表项)的访问控制位和内存域。
domain代表所属的内存域。
对于ARM处理器,Linux定义了一个类型为struct mem_type的局部静态数组(arch/arm/mm/mmu.c)。根据不同的映射类型,定义了不同的访问权限。
[c]
static struct mem_type mem_types[] = {
[MT_DEVICE] = {
.prot_pte = PROT_PTE_DEVICE | L_PTE_MT_DEV_SHARED |
L_PTE_SHARED,
.prot_l1 = PMD_TYPE_TABLE,
.prot_sect = PROT_SECT_DEVICE | PMD_SECT_S,
.domain = DOMAIN_IO,
},
[MT_DEVICE_NONSHARED] = {
.prot_pte = PROT_PTE_DEVICE | L_PTE_MT_DEV_NONSHARED,
.prot_l1 = PMD_TYPE_TABLE,
.prot_sect = PROT_SECT_DEVICE,
.domain = DOMAIN_IO,
},
[MT_DEVICE_CACHED] = {
.prot_pte = PROT_PTE_DEVICE | L_PTE_MT_DEV_CACHED,
.prot_l1 = PMD_TYPE_TABLE,
.prot_sect = PROT_SECT_DEVICE | PMD_SECT_WB,
.domain = DOMAIN_IO,
},
[MT_DEVICE_WC] = {
.prot_pte = PROT_PTE_DEVICE | L_PTE_MT_DEV_WC,
.prot_l1 = PMD_TYPE_TABLE,
.prot_sect = PROT_SECT_DEVICE,
.domain = DOMAIN_IO,
},
[MT_UNCACHED] = {
.prot_pte = PROT_PTE_DEVICE,
.prot_l1 = PMD_TYPE_TABLE,
.prot_sect = PMD_TYPE_SECT | PMD_SECT_XN,
.domain = DOMAIN_IO,
},
[MT_CACHECLEAN] = {
.prot_sect = PMD_TYPE_SECT | PMD_SECT_XN,
.domain = DOMAIN_KERNEL,
},
[MT_MINICLEAN] = {
.prot_sect = PMD_TYPE_SECT | PMD_SECT_XN | PMD_SECT_MINICACHE,
.domain = DOMAIN_KERNEL,
},
[MT_LOW_VECTORS] = {
.prot_pte = L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_DIRTY |
L_PTE_EXEC,
.prot_l1 = PMD_TYPE_TABLE,
.domain = DOMAIN_USER,
},
[MT_HIGH_VECTORS] = {
.prot_pte = L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_DIRTY |
L_PTE_USER | L_PTE_EXEC,
.prot_l1 = PMD_TYPE_TABLE,
.domain = DOMAIN_USER,
},
[MT_MEMORY] = {
.prot_sect = PMD_TYPE_SECT | PMD_SECT_AP_WRITE,
.domain = DOMAIN_KERNEL,
},
[MT_ROM] = {
.prot_sect = PMD_TYPE_SECT,
.domain = DOMAIN_KERNEL,
},
[MT_MEMORY_NONCACHED] = {
.prot_sect = PMD_TYPE_SECT | PMD_SECT_AP_WRITE,
.domain = DOMAIN_KERNEL,
},
};
[/c]
系统中定义了多个映射类型,最常用的是MT_MEMORY,它对应RAM;MT_DEVICE则对应了其他I/O设备,应用于ioremap;MT_ROM对应于ROM;MT_LOW_VECTORS对应0地址开始的向量;MT_HIGH_VECTORS对应高地址开始的向量,它有vector_base宏决定。
[c]
arch/arm/include/asm/io.h

#define MT_DEVICE               0
#define MT_DEVICE_NONSHARED     1
#define MT_DEVICE_CACHED        2
#define MT_DEVICE_WC            3

arch/arm/include/asm/mach/map.h

#define MT_UNCACHED             4
#define MT_CACHECLEAN           5
#define MT_MINICLEAN            6
#define MT_LOW_VECTORS          7
#define MT_HIGH_VECTORS         8
#define MT_MEMORY               9
#define MT_ROM                  10
[/c]
尽管ARM定义了16种不同的域,但是Linux只使用其中的三种:D0 ~ D2 (arch/arm/include/asm/domain.h)
[c]
#define DOMAIN_KERNEL   0
#define DOMAIN_TABLE    0
#define DOMAIN_USER     1
#define DOMAIN_IO       2
[/c]
内存空间和三种域的对应关系如下:
内存空间
设备空间DOMAIN_IO
内部高速SRAM空间/内部MINI Cache空间DOMAIN_KERNEL
RAM内存空间/ROM内存空间DOMAIN_KERNEL
高低端中断向量空间DOMAIN_USER
ARM处理器为每一个域定义了四种不两只的访问类型(0b00 ~ 0x11),Linux使用其中的三种(0b10不用),宏定义如下:
arch/arm/include/asm/domain.h
[c]
#define DOMAIN_NOACCESS 0
#define DOMAIN_CLIENT   1
#define DOMAIN_MANAGER  3
[/c]
Linux在系统引导设置MMU时初始化c3寄存器来实现对内存域的访问控制。其中对DOMAIN_USER,DOMAIN_KERNEL和DOMAIN_TABLE均设置DOMAIN_MANAGER权限;对DOMAIN_IO设置DOMAIN_CLIENT权限。
arch/arm/include/asm/domain.h
[c]
#define domain_val(dom,type)    ((type) << (2*(dom)))

arch/arm/kernel/head.S
......
mov     r5, #(domain_val(DOMAIN_USER, DOMAIN_MANAGER) | \
domain_val(DOMAIN_KERNEL, DOMAIN_MANAGER) | \
domain_val(DOMAIN_TABLE, DOMAIN_MANAGER) | \
domain_val(DOMAIN_IO, DOMAIN_CLIENT))
mcr     p15, 0, r5, c3, c0, 0           @ load domain access register
mcr     p15, 0, r4, c2, c0, 0           @ load page table pointer
b       __turn_mmu_on
ENDPROC(__enable_mmu)
[/c]
在系统的引导过程中对这3个域的访问控制位并不是一成不变的,它提供了一个名为modify_domain的宏来修改域访问控制位。系统在setup_arch中调用early_trap_init后,DOMAIN_USER的权限位将被设置成DOMAIN_CLIENT。
arch/arm/include/asm/domain.h
[c]
#define set_domain(x)                                   \
do {                                            \
__asm__ __volatile__(                           \
"mcr    p15, 0, %0, c3, c0      @ set domain"   \
: : "r" (x));                                 \
isb();                                          \
} while (0)

#define modify_domain(dom,type)                                 \
do {                                                    \
struct thread_info *thread = current_thread_info();     \
unsigned int domain = thread->cpu_domain;               \
domain &= ~domain_val(dom, DOMAIN_MANAGER);             \
thread->cpu_domain = domain | domain_val(dom, type);    \
set_domain(thread->cpu_domain);                         \
} while (0)
[/c]
本文出自 “第二月的博客” 博客,请务必保留此出处http://2ndmoon.blog.51cto.com/6542214/1283719
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: