[mmu/cache]-ARM MMU的学习笔记-一篇就够了
★★★ 個人博客導讀首頁—點擊此處 ★★★
.
說明:
在默認情況下,本文講述的都是ARMV8-aarch64架構,linux kernel 64位
.
相關文章
1、ARM cache的學習筆記-一篇就夠了
自制《armv8的VMSA/MMU/Cache介紹》學習視頻:
文章目錄
- ARMV8-aarch64的MMU
- 1、MMU概念介紹
- 2、MMU地址翻譯的過程
- 3、在secure和non-secure中使用MMU
- 4、在不同異常等級中使用MMU
- 5、memory attributes介紹
- 6、memory tagging介紹
- 7、啟用hypervisor
- 8、Access permissions
- 9、MMU/cache相關的寄存器總結
- (1)、address translation
- (2)、TLB maintenance
- (3)、cache maintenance
- (4)、Base system registers
- TTBR0_ELx TTBR1_ELx
- TCR_ELx
- MAIR_ELx
- 10、系統寄存器 --- TCR寄存器介紹
- (1)、T1SZ、T0SZ
- (2)、ORGN1、IRGN1、ORGN0、IRGN0
- (3)、SH1、SH0
- (4)、TG0/TG1 - Granule size
- (5)、IPS
- (6)、EPD1、EPD0
- (7)、TBI1、TBI0
- (8)、A1
- (10)、AS
- 11、代碼使用示例展
- (1)、設置inner/outer cache的屬性(只寫模式/回寫模式/write allocate/No-write allocate)
- optee系統中使用MMU
- 1、Optee中的TTBR0/TTBR1
- 2、optee中的頁表
- 3、tee內核頁表基地址的配置
- 4、tee內核頁表填充
- 5、virt_to_phys轉換的過程的舉例
ARMV8-aarch64的MMU
1、MMU概念介紹
MMU分為兩個部分: TLB maintenance 和 address translation
MMU的作用,主要是完成地址的翻譯,無論是main-memory地址(DDR地址),還是IO地址(設備device地址),在開啟了MMU的系統中,CPU發起的指令讀取、數據讀寫都是虛擬地址,在ARM Core內部,會先經過MMU將該虛擬地址自動轉換成物理地址,然后在將物理地址發送到AXI總線上,完成真正的物理內存、物理設備的讀寫訪問
下圖是一個linux kernel系統中宏觀的虛擬地址到物理地址轉換的視圖,可以看出在MMU進行地址轉換時,會依賴TTBRx_EL1寄存器指向的一個頁表基地址.
其中,TTBR1_EL1指向特權模式的頁表基地址,用于特權模式下的地址空間轉換;TTBR0_EL0指向非特權模式的頁表基地址,用于非特權模式下的地址空間轉換.
剛剛我們也提到,CPU發出讀寫后, MMU會自動的將虛擬地址轉換為為例地址,那么我們軟件需要做什么呢? 我們軟件需要做的其實就是管理這個頁表,按照ARM的技術要求去創建一個這樣的頁表,然后再將其基地址寫入到TTBR1_EL1或TTBR0_EL1。當然,根據實際的場景和需要,這個基地址和頁表中的內容都會發生動態變化. 例如,兩個user進程進行切換時,TTBR0_EL1是要從一個user的頁表地址,切換到另外一個user的頁表地址。
2、MMU地址翻譯的過程
using a 64KB granule with a 42-bit virtual address space,地址翻譯的過程(只用到一級頁表的情況):
使用二級頁表的情況舉例:
3、在secure和non-secure中使用MMU
TTBRx_EL1是banked的,在linux和optee雙系統的環境下,可同時開啟兩個系統的MMU.
在secure和non-secure中使用不同的頁表.secure的頁表可以映射non-secure的內存,而non-secure的頁表不能去映射secure的內存,否則在轉換時會發生錯誤
4、在不同異常等級中使用MMU
在ARMV8-aarch64架構下,頁表基地址寄存器有:
- TTBR0_EL1 – banked
- TTBR1_EL1 – banked
- TTBR1_EL2
- TTBR1_EL3
在EL0/EL1的系統中,MMU地址轉換時,如果虛擬地址在0x00000000_ffffffff - 0x0000ffff_ffffffff范圍,MMU會自動使用TTBR0_EL1指向的頁表,進行地址轉換;如果虛擬地址在0xffff0000_ffffffff - 0xffffffff_ffffffff范圍,MMU會自動使用TTBR1_EL1指向的頁表,進行地址轉換
在EL2系統中,MMU地址轉換時,會自動使用TTBR2_EL1指向的頁表
在EL3系統中,MMU地址轉換時,會自動使用TTBR3_EL1指向的頁表
5、memory attributes介紹
translation tables為每一塊region(entry)都定義了一個memory attributes條目,如同下面這個樣子:
TODO:以linux kernel為例,在創建頁表的時候,應該會設置這個memory attributes,有待看代碼去驗證
? Unprivileged eXecute Never (UXN) and Privileged eXecute Never (PXN) are execution
permissions.
? AF is the access flag.
? SH is the shareable attribute.
? AP is the access permission.
? NS is the security bit, but only at EL3 and Secure EL1. ---- secure權限配置
? Indx is the index into the MAIR_ELn
在這塊region(entry)的memory attributes條目中的BIT4:2(index)指向了系統寄存器MAIR_ELn中的attr,MAIR_ELn共有8中attr選擇
而每一個attr都有一種配置:
有人可能會問,這里的inner和outter是什么意思呢,請參見前面的cache介紹的文章
6、memory tagging介紹
When tagged addressing support is enabled, the top eight bits [63:56] of the virtual address are ignored by the processor. It internally sets bit [55] to sign-extend the address to 64-bit format. The top 8 bits can then be used to pass data. These bits are ignored for addressing and translation faults. The TCR_EL1 has separate enable bits for EL0 and EL1如果使用memory tagging, 虛擬地址的[63:56]用于傳輸簽名數據,bit[55]表示是否需要簽名.TCR_EL1也會有一個bit區分是給EL0用的還是給EL1用的
7、啟用hypervisor
(Two Stage Translations)
如果啟用了hypervisor那么虛擬地址轉換的過程將有VA—>PA變成了VA—>IPA—>PA, 也就是要經過兩次轉換.在guestos(如linux kernel)中轉換的物理地址,其實不是真實的物理地址(假物理地址),然后在EL2通過VTTBR0_EL2基地址的頁表轉換后的物理地址,才是真實的硬件地址
8、Access permissions
Access permissions are controlled through translation table entries. Access permissions control whether a region is readable or writeable, or both, and can be set separately to EL0 for unprivileged and access to EL1, EL2, and EL3 for privileged accesses, as shown in the following table參考 : SCTLR_EL1.WXN、SCTLR.UWXN
執行權限:
- non-executable (Execute Never (XN))
- The Unprivileged Execute Never (UXN)
- Privileged Execute Never (PXN)
9、MMU/cache相關的寄存器總結
MMU(address translation /TLB maintenance)、cache maintenance相關的寄存器
(1)、address translation
address translation 共計14個寄存器
(2)、TLB maintenance
TLB maintenance數十個寄存器
(以下截取部分)
(3)、cache maintenance
(4)、Base system registers
系統寄存器中, 和MMU/Cache相關的寄存器有:
TTBR0_ELx TTBR1_ELx
(aarch64)
- TTBR0_EL1
- TTBR0_EL2
- TTBR0_EL3
- TTBR1_EL1
- VTTBR_EL2
(aarch32)
- TTBR0
- TTBR1
- HTTBR
- VTTBR
TCR_ELx
(aarch64)
- TCR_EL1
- TCR_EL2
- TCR_EL3
- VTCR_EL2
(aarch32)
- TTBCR(NS)
- HTCR
- TTBCR(S)
- VTCR
MAIR_ELx
- MAIR_EL1
- MAIR_EL2
- MAIR_EL3
10、系統寄存器 — TCR寄存器介紹
在ARM Core中(aarch64),還有幾個相關的系統寄存器:
- TCR_EL1 banked
- TCR_EL2
- TCR_EL3
| ORGN1、IRGN1、ORGN0、IRGN0 | cache屬性** | outer/inner cableability的屬性(如直寫模式、回寫模式) |
| SH1、SH0 | cache的共享方式 | cache的共享屬性配置(如non-shareable, outer/inner shareable) |
| TG0/TG1 | Granule size | Granule size(其實就是頁面的大小,4k/16k/64k) |
| IPS | 物理地址size | 物理地址size,如32bit/36bit/40bit |
| EPD1、EPD0 | - | TTBR_EL1/TTBR_EL0的enable和disable |
| TBI1、TBI0 | - | top addr是ignore,還是用于MTE的計算 |
| A1 | - | ASID的選擇,是使用TTBR_EL1中的,還是使用TTBR_EL0中的 |
| AS | - | ASID是使用8bit,還是使用16bit |
(1)、T1SZ、T0SZ
- T1SZ, bits [21:16] 通過TTBR1尋址的內存區域的大小偏移量,也就是TTBR1基地址下的一級頁表的大小
- T0SZ, bits [5:0]
(2)、ORGN1、IRGN1、ORGN0、IRGN0
其實可以總結為這樣:
(3)、SH1、SH0
SH1, bits [29:28]
SH0, bits [13:12]
其實可以總結為這樣:
Shareable的很容易理解,就是某個地址的可能被別人使用。我們在定義某個頁屬性的時候會給出。Non-Shareable就是只有自己使用。當然,定義成Non-Shareable不表示別人不可以用。某個地址A如果在核1上映射成Shareable,核2映射成Non-Shareable,并且兩個核通過CCI400相連。那么核1在訪問A的時候,總線會去監聽核2,而核2訪問A的時候,總線直接訪問內存,不監聽核1。顯然這種做法是錯誤的。
對于Inner和Outer Shareable,有個簡單的的理解,就是認為他們都是一個東西。在最近的ARM A系列處理器上上,配置處理器RTL的時候,會選擇是不是把inner的傳輸送到ACE口上。當存在多個處理器簇或者需要雙向一致性的GPU時,就需要設成送到ACE端口。這樣,內部的操作,無論inner shareable還是outershareable,都會經由CCI廣播到別的ACE口上。
(4)、TG0/TG1 - Granule size
(5)、IPS
(6)、EPD1、EPD0
(7)、TBI1、TBI0
(8)、A1
(10)、AS
除了以上介紹的bit之外,剩余的bit都是特有功能使用或reserved的
11、代碼使用示例展
(1)、設置inner/outer cache的屬性(只寫模式/回寫模式/write allocate/No-write allocate)
如下代碼所示:
#define TCR_IRGN_WBWA ((UL(1) << 8) | (UL(1) << 24)) //使用TTBR0和使用TTBR1時后的inner cache的屬性設置#define TCR_ORGN_WBWA ((UL(1) << 10) | (UL(1) << 26)) //使用TTBR0和使用TTBR1時后的outer cache的屬性設置#define TCR_CACHE_FLAGS TCR_IRGN_WBWA | TCR_ORGN_WBWA // inner + outer cache的屬性值ENTRY(__cpu_setup) ....../** Set/prepare TCR and TTBR. We use 512GB (39-bit) address range for* both user and kernel.*/ldr x10, =TCR_TxSZ(VA_BITS) | TCR_CACHE_FLAGS | TCR_SMP_FLAGS | \TCR_TG_FLAGS | TCR_ASID16 | TCR_TBI0 | TCR_A1tcr_set_idmap_t0sz x10, x9......msr tcr_el1, x10ret // return to head.S ENDPROC(__cpu_setup)屬性設置了1,也就是回寫模式、write allocate模式
optee系統中使用MMU
1、Optee中的TTBR0/TTBR1
我們知道,在linux中將虛擬空間劃分為了userspace和kernel space:
例如:
aarch64 :
0x0000_0000_0000_0000 - 0x0000_ffff_ffff_ffff是userspace
0xffff_0000_0000_0000 - 0xffff_ffff_ffff_ffff是kernel space
aarch32 : 0-3G是userspace,3-4G是kernel space
當cpu發起讀寫內存時,cpu發起的是虛擬地址,如果是kernel地址,那么MMU將自動使用TTBR1做為頁表基地址進行轉換程物理地址,然后發送到AXI總線,完成真正物理地址的讀寫;
如果cpu發起的虛擬地址是userspace地址,那么MMU將使用TTBR0做為頁表基地址進行轉換程物理地址,然后發送到AXI總線,完成真正物理地址的讀寫;
那么在optee中是怎么樣的呢?
在optee中,沒用特別的將虛擬地址劃分成kernel space和userspace,統一使用0-4G地址空間(無論是aarch32還是aarch64).
在optee初始化時,禁用了TTBR1,所有整個optee的虛擬地址的專業,都是使用TTBR0做為基地址轉換成物理地址,發送到AXI總線,完成真正的物理地址讀寫
2、optee中的頁表
optee在在內核中使用一個4GB的大頁表,在user mode使用多個32M的小頁表
注意下面的圖來自官網,有點小問題,代碼中沒用使用到TTBR1,kernel和user都使用TTBR0
3、tee內核頁表基地址的配置
將基地址寫入到TTBR0
在optee的start函數和cpu_on_handler函數中,都會調用core_init_mmu_regs()
FUNC _start , : ......bl core_init_mmu_regsFUNC cpu_on_handler , : ......bl core_init_mmu_regs在core_init_mmu_regs中,獲取當前cpu的頁表基地址,寫入到ttbr0寄存器中
void core_init_mmu_regs(void) {uint64_t mair;uint64_t tcr;paddr_t ttbr0;uint64_t ips = calc_physical_addr_size_bits(max_pa);ttbr0 = virt_to_phys(l1_xlation_table[0][get_core_pos()]); //獲取當前cpu的頁表基地址mair = MAIR_ATTR_SET(ATTR_DEVICE, ATTR_DEVICE_INDEX);mair |= MAIR_ATTR_SET(ATTR_IWBWA_OWBWA_NTR, ATTR_IWBWA_OWBWA_NTR_INDEX);write_mair_el1(mair);tcr = TCR_RES1;tcr |= TCR_XRGNX_WBWA << TCR_IRGN0_SHIFT;tcr |= TCR_XRGNX_WBWA << TCR_ORGN0_SHIFT;tcr |= TCR_SHX_ISH << TCR_SH0_SHIFT;tcr |= ips << TCR_EL1_IPS_SHIFT;tcr |= 64 - __builtin_ctzl(CFG_LPAE_ADDR_SPACE_SIZE);/* Disable the use of TTBR1 */tcr |= TCR_EPD1;/** TCR.A1 = 0 => ASID is stored in TTBR0* TCR.AS = 0 => Same ASID size as in Aarch32/ARMv7*/write_tcr_el1(tcr);write_ttbr0_el1(ttbr0); //將頁表基地址寫入到ttbr0寄存器write_ttbr1_el1(0); }4、tee內核頁表填充
在optee內核中,實現的是一個4G的大頁表(一級頁表, 32bit可表示4G空間)
在section段定義了一個三元數組,用于存放頁表
uint64_t l1_xlation_table[NUM_L1_TABLES][CFG_TEE_CORE_NB_CORE][NUM_L1_ENTRIES]__aligned(NUM_L1_ENTRIES * XLAT_ENTRY_SIZE) __section(".nozi.mmu.l1");形態入下圖所示
填充頁表,那么要將哪些地址填進去呢?
在core_init_mmu_map()函數中,調用core_init_mmu_tables創建頁表,參數static_memory_map是一個數組,它指向系統中向optee已注冊了的所有內存區域的地址
調用core_init_mmu_tables()填充的頁表
參數mm就是剛才的static_memory_map,小于等于13塊的內存區域, 填充方法如下
在下列段代碼中,循環遍歷mm遍歷,對于每個內存區域,做core_mmu_map_region運算
for (n = 0; !core_mmap_is_end_of_table(mm + n); n++)if (!core_mmu_is_dynamic_vaspace(mm + n))core_mmu_map_region(mm + n);core_mmu_map_region就是將每塊內存區域,進行拆分,拆分程若干entry
void core_mmu_map_region(struct tee_mmap_region *mm) {struct core_mmu_table_info tbl_info;unsigned int idx;vaddr_t vaddr = mm->va;paddr_t paddr = mm->pa;ssize_t size_left = mm->size;int level;bool table_found;uint32_t old_attr;assert(!((vaddr | paddr) & SMALL_PAGE_MASK));while (size_left > 0) {level = 1;while (true) {assert(level <= CORE_MMU_PGDIR_LEVEL);table_found = core_mmu_find_table(vaddr, level,&tbl_info);if (!table_found)panic("can't find table for mapping");idx = core_mmu_va2idx(&tbl_info, vaddr);if (!can_map_at_level(paddr, vaddr, size_left,1 << tbl_info.shift, mm)) {/** This part of the region can't be mapped at* this level. Need to go deeper.*/if (!core_mmu_entry_to_finer_grained(&tbl_info,idx, mm->attr & TEE_MATTR_SECURE))panic("Can't divide MMU entry");level++;continue;}/* We can map part of the region at current level */core_mmu_get_entry(&tbl_info, idx, NULL, &old_attr);if (old_attr)panic("Page is already mapped");core_mmu_set_entry(&tbl_info, idx, paddr, mm->attr);paddr += 1 << tbl_info.shift;vaddr += 1 << tbl_info.shift;size_left -= 1 << tbl_info.shift;break;}} }5、virt_to_phys轉換的過程的舉例
通過畫圖講述了,在optee中,構建頁表,然后完成一個virt_to_phys轉換的過程.
- 1、系統注冊了的若干塊內存,劃分為若干個大小為4K/8K的region,每個region的地址,寫入到頁表的entry中,這樣就構建出了頁表.
- 2、將頁表物理地址寫入到TTBR0中
- 3、開啟MMU
- 4、調用virt_to_phys時,使用MMU的AT S1E1R指令,將虛擬地址寫入到MMU的Address translation中
- 5、從par_el1中讀出的物理地址,就是經過MMU轉換后的
注意:
1、在optee中禁止了TTBR1,所以在optee的kernel mode中,也是使用TTBR0
2、optee中,只使用到了一級頁表
歡迎添加微信、微信群,多多交流
總結
以上是生活随笔為你收集整理的[mmu/cache]-ARM MMU的学习笔记-一篇就够了的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: optee内存管理和页表建立
- 下一篇: [ARM-assembly]-ARM交叉