|
|
|
@ -23,7 +23,7 @@ AArch64 拥有 64 位地址,支持两段虚拟内存地址空间,分别为
|
|
|
|
|
|
|
|
|
|
第 0 级翻译表的基址保存在翻译表基址寄存器(Translation Table Base Register, TTBR)中,有两个,分别为 `TTBR0_EL1` 和 `TTBR1_EL1`,MMU 会根据虚拟地址是位于低地址空间还是高地址空间,自动选择对应的 TTBR。相当于低地址空间与高地址空间各用一套不同的地址翻译系统。
|
|
|
|
|
|
|
|
|
|
翻译表共有 4 级(0~3),一般每级有 512 个表项,因此每级翻译表的索引为 9 位,一个翻译表的大小为 512 x 8 = 4KB。而最后得到的物理页帧的大小一般为 4KB,页内偏移有 12 字节。
|
|
|
|
|
翻译表共有 4 级(0~3),每级有 512 个表项,因此每级翻译表的索引为 9 位,一个翻译表的大小为 512 x 8 = 4KB。而最后得到的物理页帧的大小一般为 4KB,页内偏移有 12 字节。
|
|
|
|
|
|
|
|
|
|
一般来说,最后一级得到的物理页帧大小为 4KB,不过也可不用翻译到最后一级,而在中间的某一级就停止翻译,直接返回一个物理页帧的地址,此时该页帧的大小会大于 4KB,一般称其为块(block)。如在第 1 级就返回,会得到一个 1GB 的物理内存块;如在第 2 级返回,会得到一个 2MB 的物理内存块。翻译表项中有专门的位来区别该表项中的地址是下一级翻译表的基址还是一个块的基址。
|
|
|
|
|
|
|
|
|
@ -77,8 +77,8 @@ AArch64 拥有 64 位地址,支持两段虚拟内存地址空间,分别为
|
|
|
|
|
|
|
|
|
|
#### 内存类型与可缓存性
|
|
|
|
|
|
|
|
|
|
可为内存设置多达 8 个不同的类型,每个类型的内存的可缓存性不同。如普通可缓存内存、普通不可缓存内存、设备内存等。
|
|
|
|
|
在块/页描述符的 AttrIndex 字段可设置内存的类型。
|
|
|
|
|
可为内存设置多达 8 个不同的类型,每个类型的内存的可缓存性不同,如普通可缓存内存、普通不可缓存内存、设备内存等。
|
|
|
|
|
在块/页描述符的 AttrIndex 字段可设置内存的类型。关于内存类型、Device 与 Normal 内存的区别的详细资料参见 Programmer’s Guide for ARMv8-A 13.1 节。
|
|
|
|
|
|
|
|
|
|
8 种内存类型的配置通过 `MAIR_EL1` 实现,每 8 位代表一种类型的内存配置。具体参见 ARMv8 Reference Manual D12.2.82 节。
|
|
|
|
|
|
|
|
|
@ -148,3 +148,60 @@ Cache line 的大小可通过 `CTR_EL0` 寄存器读取,一般为 16 个 WORD
|
|
|
|
|
ASID 的引入就是为了避免在上下文切换过程中刷新 TLB。上下文切换时,每个进程会被分配一个唯一的 ASID,并将其保存到 `TTBR1_EL1` 的高 16 位中。此时 TLB 中会同时记录一个虚拟地址及其进程的 ASID 对应的物理地址,使得不同进程的相同虚拟地址在 TLB 中也会被映射为不同的物理地址。
|
|
|
|
|
|
|
|
|
|
## RustOS 中的实现
|
|
|
|
|
|
|
|
|
|
在 RustOS,aarch64 平台相关的内存管理主要实现在 `kernel/src/arch/aarch64/memory.rs` 与 `kernel/src/arch/aarch64/paging.rs` 中。此外,crate [aarch64](https://github.com/equation314/aarch64) 类似其他平台下的 x86_64、riscv 库,封装了对 aarch64 底层系统寄存器、翻译表的访问。
|
|
|
|
|
|
|
|
|
|
### 物理内存探测
|
|
|
|
|
|
|
|
|
|
### 翻译表
|
|
|
|
|
|
|
|
|
|
与其他平台一样,翻译表(页表) 实现在 `paging.rs` 中,其实只是套了层壳,诸如 map 等复杂操作的实现位于 aarch64 库中。
|
|
|
|
|
|
|
|
|
|
#### 翻译表描述符
|
|
|
|
|
|
|
|
|
|
在 `aarch64/src/paging/page_table.rs` 中实现了翻译表(`PageTable`)与翻译表项描述符(`PageTableEntry`)。翻译表有 512 个项,每个项是一个 64 位描述符。一个翻译表项描述符由下面 3 部分字段构成:
|
|
|
|
|
|
|
|
|
|
1. 地址(address):位于描述符的第 12 到 47 位。根据描述符的 `TABLE_OR_PAGE` 位,分别指向下列 3 种地址:
|
|
|
|
|
|
|
|
|
|
1. 页描述符(page descriptor):该地址指向一个 4KB 大小的页,该地址 4KB 对齐;
|
|
|
|
|
2. 块描述符(block descriptor):该地址指向一个 1GB 或 2MB 大小的块,该地址 1GB 或 2MB 对齐;
|
|
|
|
|
3. 表描述符(table descriptor):该地址指向另一个翻译表,该地址 4KB 对齐。
|
|
|
|
|
|
|
|
|
|
2. 标志位(flags):仅由一个位来表示的内存属性。对于表/块描述符包含下列位:
|
|
|
|
|
|
|
|
|
|
* VALID(0):该描述符是否有效;
|
|
|
|
|
* TABLE_OR_PAGE(1):表示该描述符是块描述符还是表/页描述符;
|
|
|
|
|
* AP_EL0(6):这段内存是否可在 EL0 下访问,即用户态;
|
|
|
|
|
* AP_RO(7):这段内存是否是只读的;
|
|
|
|
|
* AF(10):Access flag,必须为 1;
|
|
|
|
|
* nG(11):是否不是全局内存。用户态内存必须设置这一位才能使用 ASID;
|
|
|
|
|
* DBM(51):Dirty Bit Modifier。不过 dirty 位只在 ARMv8.2 中由硬件自动设置,该位用于软件模拟 dirty 位;
|
|
|
|
|
* PXN(52):这段内存是否在特权级下不可执行;
|
|
|
|
|
* UXN(53):这段内存是否在非特权级下不可执行。
|
|
|
|
|
|
|
|
|
|
以及下列保留给软件实现特定功能的位:
|
|
|
|
|
|
|
|
|
|
* WRITE(51):软件实现的 DMB 位;
|
|
|
|
|
* DIRTY(55):软件实现的 dirty 位;
|
|
|
|
|
* SWAPPED(56):软件实现的页换出标志位;
|
|
|
|
|
* WRITABLE_SHARED(57):软件实现的可写共享位,用于 COW 机制;
|
|
|
|
|
* READONLY_SHARED(58):软件实现的只读共享位,用于 COW 机制。
|
|
|
|
|
|
|
|
|
|
对于表描述符包含下列位:
|
|
|
|
|
|
|
|
|
|
* PXNTable(59):该翻译表对于的所有内存都是特权级下不可执行的;
|
|
|
|
|
* XNTable(60):该翻译表对于的所有内存都是非特权级下不可执行的。
|
|
|
|
|
|
|
|
|
|
3. 属性(attribute):属性字段指明了这段内存的内存属性,包括内存类型(位 2、3、4)与可共享性(位 8、9)。在 `aarch64/src/paging/memory_attribute.rs` 中预定义了 3 中内存属性,分别为:
|
|
|
|
|
|
|
|
|
|
1. Normal:普通可缓存内存,cache 属性为 Write-Back Non-transient Read-Allocate Write-Allocate,内部共享;
|
|
|
|
|
2. Device:Device-nGnRE 类型的内存,不可缓存,外部共享;
|
|
|
|
|
3. NormalNonCacheable:普通不可缓存内存,外部共享。
|
|
|
|
|
|
|
|
|
|
#### 自映射机制
|
|
|
|
|
|
|
|
|
|
### 启用 MMU
|
|
|
|
|
|
|
|
|
|
### 重新映射内核
|
|
|
|
|
|
|
|
|
|
### 同时使用 TTBR0_EL1 与 TTBR1_EL1
|
|
|
|
|