Mapping
这一节是关于seL4虚拟内存管理的讲解。
预备知识
在此之前需要学过capability课程。
成果
学完本课程,会有以下收获:
- 如何在seL4中map和unmap虚拟内存页
背景
虚拟内存
seL4在内核提供的原始的操作硬件页表的结构之上,不提供虚拟内存的管理。用户层必须提供一个服务,用来创建中间页表结构,映射和取消映射页表。
用户可以自由定义自己的地址空间布局,但是有一个限制:seL4占用内存范围较高的部分。在大多数32位的平台上,这个值是0xe0000000及以上。这个值在不同的平台上是不同的,可以在seL4的源码中找到kernelBase。
页表结构
作为启动进程的一部分,seL4使用一个顶层的硬件虚拟内存对象初始化root task,被称为VSpace。这个结构体的capability在root task的CSpace的seL4_CapInitThreadVSpaceslot中。对于每种架构,这个capability对应的是一个与硬件相关的不同的对象。下面的表中列举了对于支持的架构中的VSpace对象。
| Architecture | VSpace Object |
|---|---|
| aarch32 | seL4_PageDirectory |
| aarch64 | seL4_PageGlobalDirectory |
| ia32 | seL4_PageDirectory |
| x86_64 | seL4_PML4 |
| RISC-V | seL4_PageTable |
除了顶层的页面结构之外,还需要一个中间硬件虚拟内存对象来映射页面。下面的表中列举了这些对象(仍然是不同的架构不同的对象)
| Architecture | Objects |
|---|---|
| aarch32 | seL4_PageTable |
| aarch64 | seL4_PageUpperDirectory, seL4_PageDirectory, seL4_PageTable |
| ia32 | seL4_PageTable |
| x86_64 | seL4_PDPT, seL4_PageDirectory, seL4_PageTable |
| RISC-V | seL4_PageTable |
这个教程以x86_64架构为例,但是应该可以覆盖seL4中虚拟内存API的所有信息,可以推广到其他的架构上。
每个页面结构都可以被调用来映射和取消映射。下面就是一个映射x86_64 seL4_PDPT对象的例子:
/* map a PDPT at TEST_VADDR */
error = seL4_X86_PDPT_Map(pdpt, seL4_CapInitThreadVSpace, TEST_VADDR, seL4_X86_Default_VMAttributes);
所有的映射函数都需要3个参数:
- 要把对象映射到的CSpace
- 要把对象映射到的虚拟地址
- 虚拟内存属性
如果提供的虚拟内存地址没有与分页对象的大小对齐,seL4会遮盖所有没用的部分。举个例子,一个4k的分页要映射到0xDEADBEEF,seL4最终会让他映射到0xDEADB000。后面那部分就不要了。
虚拟内存的属性决定了映射缓存的属性,这也是与架构相关的。除了本教程使用的属性seL4_X86_Default_VMAttributes,其他属性可以在libsel4中找到。
页表
一旦特定范围的虚拟内存都映射到了中级页表结构体上,物理帧可以通过调用frame capability映射到这个范围。下面的代码片段展示了如何把一个frame映射到地址TEST——VADDR。
/* map a read-only page at TEST_VADDR */
error = seL4_X86_Page_Map(frame, seL4_CapInitThreadVSpace, TEST_VADDR, seL4_CanRead, seL4_X86_Default_VMAttributes);
要使得页面映射成功,所有的中级分页都必须被映射。libsel4的seL4_MappingFailedLookupLevel()可以用于检测哪个等级的页表结构有缺失。需要注意的是,如果对一个frame多次映射,必须同时复制他的frame capability:每个capability只能跟踪一个映射。
除了映射相关的参数外,映射函数还接受一个额外的权限参数。在上面的例子中,把映射的也权限设定为只读。
下一篇学习map的练习部分。