天天看點

[mmu/cache]-ARM MMU的學習筆記-一篇就夠了

★★★ 個人部落格導讀首頁—點選此處 ★★★

.

說明:

在預設情況下,本文講述的都是ARMV8-aarch64架構,linux kernel 64位

.

相關文章

1、ARM cache的學習筆記-一篇就夠了

自制《armv8的VMSA/MMU/Cache介紹》學習視訊:

[mmu/cache]-ARM MMU的學習筆記-一篇就夠了

文章目錄

      • 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/cache]-ARM MMU的學習筆記-一篇就夠了

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的頁表位址。

[mmu/cache]-ARM MMU的學習筆記-一篇就夠了

2、MMU位址翻譯的過程

using a 64KB granule with a 42-bit virtual address space,位址翻譯的過程(隻用到一級頁表的情況):

[mmu/cache]-ARM MMU的學習筆記-一篇就夠了

使用二級頁表的情況舉例:

[mmu/cache]-ARM MMU的學習筆記-一篇就夠了

3、在secure和non-secure中使用MMU

TTBRx_EL1是banked的,在linux和optee雙系統的環境下,可同時開啟兩個系統的MMU.

在secure和non-secure中使用不同的頁表.secure的頁表可以映射non-secure的記憶體,而non-secure的頁表不能去映射secure的記憶體,否則在轉換時會發生錯誤

[mmu/cache]-ARM MMU的學習筆記-一篇就夠了

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指向的頁表

[mmu/cache]-ARM MMU的學習筆記-一篇就夠了

5、memory attributes介紹

translation tables為每一塊region(entry)都定義了一個memory attributes條目,如同下面這個樣子:

TODO:以linux kernel為例,在建立頁表的時候,應該會設定這個memory attributes,有待看代碼去驗證

[mmu/cache]-ARM MMU的學習筆記-一篇就夠了

• 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選擇

[mmu/cache]-ARM MMU的學習筆記-一篇就夠了

而每一個attr都有一種配置:

[mmu/cache]-ARM MMU的學習筆記-一篇就夠了

有人可能會問,這裡的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基位址的頁表轉換後的實體位址,才是真實的硬體位址

IPA : intermediate physical address
           
[mmu/cache]-ARM MMU的學習筆記-一篇就夠了

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
[mmu/cache]-ARM MMU的學習筆記-一篇就夠了

執行權限:

  • 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個寄存器

[mmu/cache]-ARM MMU的學習筆記-一篇就夠了
(2)、TLB maintenance

TLB maintenance數十個寄存器

(以下截取部分)

[mmu/cache]-ARM MMU的學習筆記-一篇就夠了
(3)、cache maintenance
[mmu/cache]-ARM MMU的學習筆記-一篇就夠了
[mmu/cache]-ARM MMU的學習筆記-一篇就夠了
(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
    [mmu/cache]-ARM MMU的學習筆記-一篇就夠了
比特位 功能 說明
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
[mmu/cache]-ARM MMU的學習筆記-一篇就夠了

其實可以總結為這樣:

[mmu/cache]-ARM MMU的學習筆記-一篇就夠了
(3)、SH1、SH0

SH1, bits [29:28]

SH0, bits [13:12]

[mmu/cache]-ARM MMU的學習筆記-一篇就夠了

其實可以總結為這樣:

[mmu/cache]-ARM MMU的學習筆記-一篇就夠了

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
[mmu/cache]-ARM MMU的學習筆記-一篇就夠了
(5)、IPS
[mmu/cache]-ARM MMU的學習筆記-一篇就夠了
(6)、EPD1、EPD0
[mmu/cache]-ARM MMU的學習筆記-一篇就夠了
(7)、TBI1、TBI0
[mmu/cache]-ARM MMU的學習筆記-一篇就夠了
(8)、A1
[mmu/cache]-ARM MMU的學習筆記-一篇就夠了
(10)、AS
[mmu/cache]-ARM MMU的學習筆記-一篇就夠了

除了以上介紹的bit之外,剩餘的bit都是特有功能使用或reserved的

[mmu/cache]-ARM MMU的學習筆記-一篇就夠了

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_A1
	tcr_set_idmap_t0sz	x10, x9

......
	msr	tcr_el1, x10
	ret					// return to head.S
ENDPROC(__cpu_setup)
           

屬性設定了1,也就是回寫模式、write allocate模式

[mmu/cache]-ARM MMU的學習筆記-一篇就夠了

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總線,完成真正的實體位址讀寫

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()]);

	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);
	write_ttbr1_el1(0);
	}
           
2、optee中的頁表

optee在在核心中使用一個4GB的大頁表,在user mode使用多個32M的小頁表

注意下面的圖來自官網,有點小問題,代碼中沒用使用到TTBR1,kernel和user都使用TTBR0

[mmu/cache]-ARM MMU的學習筆記-一篇就夠了
3、tee核心頁表基位址的配置

将基位址寫入到TTBR0

在optee的start函數和cpu_on_handler函數中,都會調用core_init_mmu_regs()

FUNC _start , :
......
	bl	core_init_mmu_regs

FUNC 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");
           

形态入下圖所示

[mmu/cache]-ARM MMU的學習筆記-一篇就夠了

填充頁表,那麼要将哪些位址填進去呢?

在core_init_mmu_map()函數中,調用core_init_mmu_tables建立頁表,參數static_memory_map是一個數組,它指向系統中向optee已注冊了的所有記憶體區域的位址

void core_init_mmu_map(void)
{
......
	core_init_mmu_tables(static_memory_map);
......
}
           

調用core_init_mmu_tables()填充的頁表

參數mm就是剛才的static_memory_map,小于等于13塊的記憶體區域, 填充方法如下

void core_init_mmu_tables(struct tee_mmap_region *mm)
{
	uint64_t max_va = 0;
	size_t n;

#ifdef CFG_CORE_UNMAP_CORE_AT_EL0
	COMPILE_TIME_ASSERT(CORE_MMU_L1_TBL_OFFSET ==
			   sizeof(l1_xlation_table) / 2);
#endif
	max_pa = get_nsec_ddr_max_pa();

	for (n = 0; !core_mmap_is_end_of_table(mm + n); n++) {
		paddr_t pa_end;
		vaddr_t va_end;

		debug_print(" %010" PRIxVA " %010" PRIxPA " %10zx %x",
			    mm[n].va, mm[n].pa, mm[n].size, mm[n].attr);

		if (!IS_PAGE_ALIGNED(mm[n].pa) || !IS_PAGE_ALIGNED(mm[n].size))
			panic("unaligned region");

		pa_end = mm[n].pa + mm[n].size - 1;
		va_end = mm[n].va + mm[n].size - 1;
		if (pa_end > max_pa)
			max_pa = pa_end;
		if (va_end > max_va)
			max_va = va_end;
	}

	/* Clear table before use */
	memset(l1_xlation_table, 0, sizeof(l1_xlation_table));   ------------------------清空頁表

	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);   ------------------------填充目前cpu的頁表

	/*
	 * Primary mapping table is ready at index `get_core_pos()`
	 * whose value may not be ZERO. Take this index as copy source.
	 */
	for (n = 0; n < CFG_TEE_CORE_NB_CORE; n++) {
		if (n == get_core_pos())
			continue;

		memcpy(l1_xlation_table[0][n],
		       l1_xlation_table[0][get_core_pos()],   ------------------------将目前cpu的頁表,拷貝到其它cpu的頁表中
		       XLAT_ENTRY_SIZE * NUM_L1_ENTRIES);
	}

	for (n = 1; n < NUM_L1_ENTRIES; n++) {
		if (!l1_xlation_table[0][0][n]) {
			user_va_idx = n;
			break;
		}
	}
	assert(user_va_idx != -1);

	COMPILE_TIME_ASSERT(CFG_LPAE_ADDR_SPACE_SIZE > 0);
	assert(max_va < CFG_LPAE_ADDR_SPACE_SIZE);
}
           

在下列段代碼中,循環周遊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的學習筆記-一篇就夠了
歡迎添加微信、微信群,多多交流
[mmu/cache]-ARM MMU的學習筆記-一篇就夠了

繼續閱讀