[kernel 啟動流程] (第五章)第一階段之——臨時核心頁表的建立
本文是基於arm平臺。例子都是以tiny210(s5pv210 armv7)為基礎的。
[kernel 啟動流程]系列:
建議參考文件:
================================================
零、說明
本文是《[kernel 啟動流程] (第一章)概述》的延伸,
閱讀本文前建議先閱讀《[kernel 啟動流程] (第一章)概述》
1、kernel啟動流程第一階段簡單說明
arch/arm/kernel/head.S
- kernel入口地址對應stext
ENTRY(stext)
第一階段要做的事情,也就是stext的實現內容
- 設定為SVC模式,關閉所有中斷
- 獲取CPU ID,提取相應的proc info
- 驗證tags或者dtb
- 建立臨時核心頁表的頁表項
- 配置r13暫存器,也就是設定開啟MMU之後要跳轉到的函式。
- 使能MMU
- 跳轉到start_kernel,也就是跳轉到第二階段
本文要介紹的是“建立臨時核心頁表的頁表項”的部分。
2、疑問
主要帶著以下幾個問題去理解
- 什麼是MMU?以及其和頁表之間的關係?
- 頁表內容?頁表格式?如何進行建立?
3、對應程式碼實現
__HEAD
ENTRY(stext)
ldr r8, =PLAT_PHYS_OFFSET @ always constant in this case
/*
* r1 = machine no, r2 = atags or dtb,
* r8 = phys_offset, r9 = cpuid, r10 = procinfo
*/
bl __create_page_tables
__create_page_tables的工作就是建立臨時核心頁表。而建立臨時核心頁表則是為了開啟MMU功能。
一、MMU和頁表簡單介紹
以下只是為了理解很簡單地進行介紹。
並且主要介紹段式管理的工作原理。
1、MMU簡單介紹
MMU是Memory Management Unit的縮寫,中文名是記憶體管理單元,它是中央處理器(CPU)中用來管理虛擬儲存器、物理儲存器的控制線路。
其主要功能如下:
將線性地址對映為實體地址
所謂線性地址就是指虛擬地址,而實體地址就是指實際在RAM上的地址提供硬體機制的記憶體訪問授權
2、頁表介紹
MMU工作的核心是頁表,也就是其根據頁表來找到對映關係以及許可權。
頁表由頁表項構成,每一個虛擬地址對映區都會有一個對應的頁表項。
arm的頁表有如下分類(在本章中使用的是段式管理,所以這裡只說明段式管理):
- 段式管理頁表
在arm開啟MMU初期,使用的是臨時核心頁表,其型別就是段式頁表。
段式頁表將4GB的地址空間(32bit系統)劃分成4096個1MB的段,因此段式頁表有4096個頁表項,每個頁表項有32bit(4 byte),故段式頁表需要16KB的空間
頁表項格式如下:
位號 | 功能 |
---|---|
31:20 bit | 段序號 |
20: 0 bit | MMU flag |
3、arm上MMU的工作原理
arm將頁表基地址存放在協處理器cp15的c2暫存器上,具體參考《ARM的CP15協處理器的暫存器》。
如下說明:
CP15 中的暫存器 C2 儲存的是頁表的基地址,即一級對映描述符表的基地址。
arm的MMU會根據虛擬地址計算出其相應頁表項的偏移,從cp15的c2暫存器中獲取頁表基址之後,加上偏移得到對應的頁表項地址。後續操作就是根據頁表結構來做的。這些動作都是MMU硬體處理!
如果是段式頁表的話,再根據段內偏移以及頁表項中的物理段基址最終得到對應的實體地址。
段式管理頁表工作舉例(先不關心MMU flag):
假設
(1) 頁表基地址為0x0(存放在CP15的c2暫存器上).
(2) 0xc0000000所在段(也就是段序號為0xc00)的頁表項地址0x3000,
(3) 頁表項地址0x3000的值為0x20000000(也就是段序號為0x300).
當虛擬地址為0xc0001000,計算方式如下
(1) 左移20位的得到虛擬地址所在段序號為0xc00,獲取低20位得到段內偏移為0x1000
(2) 計算對應頁表項地址=頁表基地址0x0+段序號0xc00*頁表項長度4=0x3000.
(3) 0x3000地址上的值為0x20000000,提取高12位得到0x200,所以對應物理段基址為0x20000000
(4) 物理段基址加上段內偏移得到實際的實體地址0x20001000.
4、臨時核心頁表及其內容
為了開啟MMU,核心需要建立一個臨時核心頁表,用於kenrel啟動過程中的開啟MMU的過渡階段。
並且,使用的是段式管理的方法。
需要建立如下區域的對映
開啟MMU的函式的程式碼區域的恆等對映。
恆等對映是指虛擬地址和實體地址一致的對映。
在開啟MMU的過程中,CPU還是按照地址順序一條接著一條去獲取指令,也就是說此時PC指標還是指向這段程式碼區域的實體地址。當MMU開啟之後,如果沒有恆等對映的話,PC指標此時還是指向這段程式碼區域的實體地址,但實際上CPU會把PC指標的地址當作虛擬地址進行操作,而造成找不到對應的實體地址。因此,如果做了恆等對映,虛擬地址和實體地址一致,及時CPU會把PC指標的地址當作虛擬地址進行操作,最終仍會對映到相同的實體地址上。kernel映象的對映
在《[kernel 啟動流程] 前篇——vmlinux.lds分析》中,我們已經知道了kernel的連結地址是0xc0008000。
而我們把kernel載入到0x20008000的位置上。kernel映象的連線區域對映到實際的實體地址的區域。dtb區域的對映
在kernel啟動初期需要使用到dtb的東西,因此,在臨時核心頁表中需要做這些區域的對映。
二、s5pv210對映說明
記憶體地址和對應段頁表項的地址如下:
(s5p210的物理RAM地址偏移是0x20000000,所以段頁表項的基地址是0x20004000)
虛擬段 | 物理段 | 對應頁表項地址 | 計算方式 | 臨時頁表對映的值 |
---|---|---|---|---|
0x00000000-0x000FFFFF | - | 0x20004000 | (0x20004000+0x000*4) | - |
0x00100000-0x002FFFFF | - | 0x20004004 | (0x20004000+0x001*4) | - |
0x00200000-0x003FFFFF | - | 0x20004008 | (0x20004000+0x002*4) | - |
… | ||||
0x20100000-0x201FFFFF | 0x20100000-0x201FFFFF | 0x20004804 | (0x20004000+0x201*4) | (0x201<<20) | mmuflags |
… | ||||
0xC0000000-0xC00FFFFF | 0x20000000-0x200FFFFF | 0x20007000 | (0x20004000+0xC00*4) | (0x200<<20) | mmuflags |
0xC0100000-0xC01FFFFF | 0x20100000-0x201FFFFF | 0X20007004 | (0x20004000+0xC01*4) | (0x201<<20) | mmuflags |
… | ||||
0xC0500000-0xC05FFFFF | 0x20500000-0x205FFFFF | 0X20007014 | (0x20004000+0xC05*4) | (0x205<<20) | mmuflags |
… | ||||
0xDFC00000-0xDFCFFFFF | 0x3FC00000-3FCFFFFF | 0x200077F0 | (0x20004000+0xDFC*4) | (0x205<<20) | mmuflags |
0xDFD00000-0xDFDFFFFF | 0x3FD00000-3FDFFFFF | 0x200077F4 | (0x20004000+0xDFC*4) | (0x205<<20) | mmuflags |
… | ||||
0xFFF00000-0xFFFFFFFF | - | 0x20007FFC | (0x20004000+0xFFF*4) | - |
其中(markdown搞個圖片不方便,只能用表格和文字描述了。。。。)
- [0x20100000-0x201FFFFF]段是開啟MMU的函式的程式碼區域的恆等對映。
- [0xC0000000-0xC00FFFFF]段到[0xC0500000-0xC05FFFFF]段是kernel映象的對映
- [0xDFC00000-0xDFCFFFFF]段到[0xDFD00000-0xDFDFFFFF]段是dtb記憶體區域的對映
後續程式碼會進行說明。
三、程式碼分析
1、程式碼入口
(1)分配實體地址給r8
ENTRY(stext)
...
#define PLAT_PHYS_OFFSET UL(CONFIG_PHYS_OFFSET)
ldr r8, =PLAT_PHYS_OFFSET @ always constant in this case
實體地址PHYS_OFFSET的定義如下:
arch/arm/Kconfig
config PHYS_OFFSET
hex "Physical address of main memory" if MMU
depends on !ARM_PATCH_PHYS_VIRT
default 0x20000000 if ARCH_S5PV210
所以config是0x20000000,和s5pv210的ddr起始ram一致。
(2)呼叫 bl __create_page_tables
ENTRY(stext)
...
bl __create_page_tables
__create_page_tables主要用於建立臨時核心頁表。
__create_page_tables程式碼總覽,後續小節會詳細分析:
arch/arm/kernel/head.S
移除掉CONFIG_ARM_LPAE和CONFIG_DEBUG_LL的無關部分
/*
* Setup the initial page tables. We only setup the barest
* amount which are required to get the kernel running, which
* generally means mapping in the kernel code.
*
* r8 = phys_offset, r9 = cpuid, r10 = procinfo
*
* Returns:
* r0, r3, r5-r7 corrupted
* r4 = physical page table address
*/
__create_page_tables:
pgtbl r4, r8 @ page table address
@============上述程式碼見下<詳解1>
/*
* Clear the swapper page table
*/
mov r0, r4
mov r3, #0
add r6, r0, #PG_DIR_SIZE
1: str r3, [r0], #4
str r3, [r0], #4
str r3, [r0], #4
str r3, [r0], #4
teq r0, r6
bne 1b
@============上述程式碼見下<詳解2>
ldr r7, [r10, #PROCINFO_MM_MMUFLAGS] @ mm_mmuflags
@============上述程式碼見下<詳解3>
/*
* Create identity mapping to cater for __enable_mmu.
* This identity mapping will be removed by paging_init().
*/
adr r0, __turn_mmu_on_loc
ldmia r0, {r3, r5, r6}
sub r0, r0, r3 @ virt->phys offset
add r5, r5, r0 @ phys __turn_mmu_on
add r6, r6, r0 @ phys __turn_mmu_on_end
mov r5, r5, lsr #SECTION_SHIFT
mov r6, r6, lsr #SECTION_SHIFT
1: orr r3, r7, r5, lsl #SECTION_SHIFT @ flags + kernel base
str r3, [r4, r5, lsl #PMD_ORDER] @ identity mapping
cmp r5, r6
addlo r5, r5, #1 @ next section
blo 1b
@============上述程式碼見下<詳解4>
/*
* Map our RAM from the start to the end of the kernel .bss section.
*/
add r0, r4, #PAGE_OFFSET >> (SECTION_SHIFT - PMD_ORDER)
ldr r6, =(_end - 1)
orr r3, r8, r7
add r6, r4, r6, lsr #(SECTION_SHIFT - PMD_ORDER)
1: str r3, [r0], #1 << PMD_ORDER
add r3, r3, #1 << SECTION_SHIFT
cmp r0, r6
bls 1b
@============上述程式碼見下<詳解5>
/*
* Then map boot params address in r2 if specified.
* We map 2 sections in case the ATAGs/DTB crosses a section boundary.
*/
mov r0, r2, lsr #SECTION_SHIFT
movs r0, r0, lsl #SECTION_SHIFT
subne r3, r0, r8
addne r3, r3, #PAGE_OFFSET
addne r3, r4, r3, lsr #(SECTION_SHIFT - PMD_ORDER)
orrne r6, r7, r0
strne r6, [r3], #1 << PMD_ORDER
addne r6, r6, #1 << SECTION_SHIFT
strne r6, [r3]
@============上述程式碼見下<詳解6>
ret lr
ENDPROC(__create_page_tables)
2、詳解1
pgtbl r4, r8 @ page table address
pgtbl 巨集用於通過DRAM實體地址來獲取頁表的實體地址。
前面我們已經知道r8用於存放DRAM的起始實體地址,r4則是要存放計算得到的頁表實體地址。
pgtbl 巨集如下:
arch/arm/kernel/head.S
.macro pgtbl, rd, phys
add \rd, \phys, #TEXT_OFFSET
sub \rd, \rd, #PG_DIR_SIZE
.endm
通過《[kernel 啟動流程] 前篇——vmlinux.lds分析》我們已經知道kernel在放在DRAM上偏移TEXT_OFFSET的位置上。
而linux規定將TEXT_OFFSET之前的PG_DIR_SIZE大小的空間用作臨時頁表。
所以計算方式如下:
kernel起始地址=DRAM起始實體地址+TEXT_OFFSET=0x20008000
核心頁表地址=kernel起始地址-PG_DIR_SIZE=0x20004000
所以程式碼換算成如下計算:
\rd(r4) = phys(r8) + TEXT_OFFSET
\rd(r4) = \rd(r4) - PG_DIR_SIZE
* TEXT_OFFSET如下:
./arch/arm/Makefile
# The byte offset of the kernel image in RAM from the start of RAM.
TEXT_OFFSET := $(textofs-y)
# Text offset. This list is sorted numerically by address in order to
# provide a means to avoid/resolve conflicts in multi-arch kernels.
textofs-y := 0x00008000
32K的偏移
* PG_DIR_SIZE如下:
arch/arm/kernel/head.S
#define PG_DIR_SIZE 0x4000
#define PMD_ORDER 2
前面也說過頁表的大小是16K,剛好和這裡是符合的。
最終獲得頁表實體地址(s5pv210是0x20004000)並且存放在r4暫存器中。
3、詳解2
為臨時核心頁表分配空間之後,接下來的任務就是清空臨時核心頁表分配空間。
/*
* Clear the swapper page table
*/
mov r0, r4
@ 將臨時核心頁表實體地址放到r0上
mov r3, #0
@ 在r3上存放0值
add r6, r0, #PG_DIR_SIZE
@ 將臨時核心頁表的末尾實體地址放到r6上
1: str r3, [r0], #4
@ 從r0(臨時核心頁表實體地址)指向的暫存器上開始寫入0值,每16個位元組一個迴圈
str r3, [r0], #4
str r3, [r0], #4
str r3, [r0], #4
teq r0, r6
@ 比較是否已經寫到了r6(臨時核心頁表的末尾實體地址)上
bne 1b
@ 如果還沒有寫完,進入下一個迴圈
4、詳解3
設定MMU的標識並存放到r7暫存器中,後續需要寫入到臨時核心頁表的頁表項中
ldr r7, [r10, #PROCINFO_MM_MMUFLAGS] @ mm_mmuflags
具體需要參考前面的文章《[kernel 啟動流程] (第三章)第一階段之——proc info的獲取》。
通過這邊文章我們已經知道已經將和對應CPU配置的proc info存放到了r10暫存器中。
PROCINFO_MM_MMUFLAGS對應如下
DEFINE(PROCINFO_MM_MMUFLAGS, offsetof(struct proc_info_list, __cpu_mm_mmu_flags));
PROCINFO_MM_MMUFLAGS對應proc_info_list中的__cpu_mm_mmu_flags,這個成員用於表示臨時頁表對映的核心空間的MMU標識。
s5pv210的這個值對應為PMD_TYPE_SECT | PMD_SECT_AP_WRITE | PMD_SECT_AP_READ | \
PMD_SECT_AF | PMD_FLAGS_UP。
最終,將這個成員對應的值寫入到r7暫存器中。
5、詳解4
開始進行對映表的建立,首先是建立恆等對映。
程式碼總覽如下
/*
* Create identity mapping to cater for __enable_mmu.
* This identity mapping will be removed by paging_init().
*/
adr r0, __turn_mmu_on_loc
ldmia r0, {r3, r5, r6}
sub r0, r0, r3 @ virt->phys offset
add r5, r5, r0 @ phys __turn_mmu_on
add r6, r6, r0 @ phys __turn_mmu_on_end
mov r5, r5, lsr #SECTION_SHIFT
mov r6, r6, lsr #SECTION_SHIFT
1: orr r3, r7, r5, lsl #SECTION_SHIFT @ flags + kernel base
str r3, [r4, r5, lsl #PMD_ORDER] @ identity mapping
cmp r5, r6
addlo r5, r5, #1 @ next section
blo 1b
也就是對開啟mmu的函式,也就是__turn_mmu_on進行恆等對映。
所謂恆等對映,就是將實體地址相應到相同的虛擬地址上。
__turn_mmu_on程式碼如下
ENTRY(__turn_mmu_on)
mov r0, r0
instr_sync
mcr p15, 0, r0, c1, c0, 0 @ write control reg
mrc p15, 0, r3, c0, c0, 0 @ read id reg
instr_sync
mov r3, r3
mov r3, r13
ret r3
__turn_mmu_on_end:
ENDPROC(__turn_mmu_on)
__turn_mmu_on和__turn_mmu_on_end標識了其起始程式碼地址和末端程式碼地址。
通過System.map檢視其連線地址如下:
c0100000 T __turn_mmu_on
c0100020 t __turn_mmu_on_end
kernel將這些連結地址存放到了__turn_mmu_on_loc中。
__turn_mmu_on_loc:
.long .
.long __turn_mmu_on
.long __turn_mmu_on_end
也就是說臨時核心對映表需要為__turn_mmu_on新增如下頁表
虛擬段 | 物理段 | 對應頁表位置 | 計算方式 | 臨時頁表對映的值 |
---|---|---|---|---|
0x20100000-0x201FFFFF | 0x20100000-0x201FFFFF | 0x20004804 | (0x20004000+0x201*4) | (0x201<<20) | mmuflags |
程式碼分析如下
adr r0, __turn_mmu_on_loc
ldmia r0, {r3, r5, r6}
sub r0, r0, r3 @ virt->phys offset
add r5, r5, r0 @ phys __turn_mmu_on
add r6, r6, r0 @ phys __turn_mmu_on_end
@ 通過以下程式碼將其轉化為實體地址並且存放到r5和r6暫存器中,
@ 具體方法在《[kernel 啟動流程] (第三章)第一階段之——proc info的獲取》中說明過了。
mov r5, r5, lsr #SECTION_SHIFT
mov r6, r6, lsr #SECTION_SHIFT
@ 以1M做為一個段,所以對應段序號是記憶體地址左移20位。
@ arch/arm/include/asm/pgtable-2level.h
@ #define SECTION_SHIFT 20
@ 以上獲取__turn_mmu_on程式碼部分所對應的段序號,
@ r5存放起始地址的段序號,r6存放末地址的段序號。
@ 後續就是填充相應的段頁表項
1: orr r3, r7, r5, lsl #SECTION_SHIFT @ flags + kernel base
@ 填寫對應段的段頁表項的內容,存放在r3中。
@ 因為是恆等對映,所以對映後的段地址就是實體地址的段序號左移SECTION_SHIFT。
@ 頁表項內容為段序號(r5)左移SECTION_SHIFT後或上MMU標識(r7),
str r3, [r4, r5, lsl #PMD_ORDER] @ identity mapping
@ 將段頁表項值(r3)寫入到對應的段頁表項中
@ 段頁表項的地址=段頁表起始地址(r4)+段序號(r5)*段頁表項的size(1<<PMD_ORDER,4K)
cmp r5, r6
addlo r5, r5, #1 @ next section
blo 1b
@ 判斷是否已經寫到__turn_mmu_on末地址的對應的段頁表項中,如果沒有的話,繼續寫入下一個段。
通過上述,就完成了__turn_mmu_on程式碼部分的恆等對映。
6、詳解5
以下對kernel的核心空間進行對映。
通過System.map中可以看出kernel的連線區域如下:
c0008000 T stext
c0547d74 B _end
其相應在實體地址上的記憶體區域是
0x20008000到0x20547d74的區域。
因此後續要建立物理區[0x20008000-0x20547d74]到核心對映區[0xc0008000到0xc0547d74]的記憶體對映。
對應如下:
虛擬段 | 物理段 | 對應頁表位置 | 計算方式 | 臨時頁表對映的值 |
---|---|---|---|---|
0xC0000000-0xC00FFFFF | 0x20000000-0x200FFFFF | 0x20007000 | (0x20004000+0xC00*4) | (0x200<<20) | mmuflags |
0xC0100000-0xC01FFFFF | 0x20100000-0x201FFFFF | 0X20007004 | (0x20004000+0xC01*4) | (0x201<<20) | mmuflags |
… | ||||
0xC0500000-0xC05FFFFF | 0x20500000-0x205FFFFF | 0X20007014 | (0x20004000+0xC05*4) | (0x205<<20) | mmuflags |
程式碼分析如下:
/*
* Map our RAM from the start to the end of the kernel .bss section.
*/
add r0, r4, #PAGE_OFFSET >> (SECTION_SHIFT - PMD_ORDER)
@ PAGE_OFFSET表示核心空間的偏移,這裡是0xc0000000,也是核心對映區的起始段的起始地址。
@ 將PAGE_OFFSET左移(SECTION_SHIFT - PMD_ORDER)後得到0xc0000000所在段的段頁表項的地址偏移。
@ 將段頁表項的地址偏移+臨時核心頁表地址(r4)得到0xc0000000所在段的段頁表項的實體地址。
@ 將這個實體地址存放到r0暫存器中
ldr r6, =(_end - 1)
@ 將核心對映區的末尾地址存入r6暫存器中。
orr r3, r8, r7
@ 將DRAM起始實體地址(r8)或上MMU標識(r7),得到0xc0000000所在段的段頁表項的內容。存放在r3暫存器中。
@ 例如0xc0000000對應段的段頁表項就應該是0x20000000 | MMU_FLAG.
add r6, r4, r6, lsr #(SECTION_SHIFT - PMD_ORDER)
@ 將核心對映區的末尾地址(r6)左移(SECTION_SHIFT - PMD_ORDER)後得到其所在段的段頁表項的實體地址
1: str r3, [r0], #1 << PMD_ORDER
@ 將r3存入當前段頁表項中([r0])
@ 然後將r0加上4得到下一個段頁表項的地址
add r3, r3, #1 << SECTION_SHIFT
@ 更新r3中的頁表項值為下一個段的頁表項值。
@ 只需要更新其被對映地址,也就是直接加上0x100000.
@ 例如當前是0xc0000000所在段,其對應的實體地址是0x20000000,其段頁表項值為0x20000000 | mmu_flag
@ 那麼下一個段就應該是0xc0100000,其對應實體地址應該是0x20100000,那麼其段頁表項值為0x20100000 | mmu_flag
cmp r0, r6
@ 判斷是否已經是核心對映區的末尾段了。
bls 1b
@ 如果不是的話,進入下一次迴圈。
7、詳解6
後續就是建立DTB的對映區。
方法和上述類似,主要是從r2中提取dtb的實體記憶體地址,計算出對應虛擬地址之後,進行對映表建立。
並且其固定映射了兩個段。所以DTB的大小不能超過1M。
例如:tiny210中ubootlog
Loading Device Tree to 3fc6d000, end 3fc75acb ... OK
所以應該建立 DTB arm區[0x3fc00000-0x3fe00000] 到 DTB對映區[0xdfc00000-0xdfe00000]的對映表。
對應如下:
虛擬段 | 物理段 | 對應頁表位置 | 計算方式 | 臨時頁表對映的值 |
---|---|---|---|---|
0xDFC00000-0xDFCFFFFF | 0x3FC00000-3FCFFFFF | 0x200077F0 | (0x20004000+0xDFC*4) | (0x205<<20) | mmuflags |
0xDFD00000-0xDFDFFFFF | 0x3FD00000-3FDFFFFF | 0x200077F4 | (0x20004000+0xDFC*4) | (0x205<<20) | mmuflags |
程式碼分析如下:
/*
* Then map boot params address in r2 if specified.
* We map 2 sections in case the ATAGs/DTB crosses a section boundary.
*/
mov r0, r2, lsr #SECTION_SHIFT
@ 將dtb起始實體地址(r2)左移SECTION_SHIFT,存放在r0中。
movs r0, r0, lsl #SECTION_SHIFT
@ 再將r0右移SECTION_SHIFT得到這個實體記憶體段的地址(和上一步簡單理解就是把低20位清零)
subne r3, r0, r8
@ 計算dtb實體記憶體段(r0)對應DRAM起始地址(r8)的偏移,存放在r3中
addne r3, r3, #PAGE_OFFSET
@ 將偏移(r3)加上,核心空間起始地址PAGE_OFFSET,得到要對映到的虛擬地址,
@ 例如0x3fc00000,對應偏移是0x1fc00000,那麼計算得要其要對映到的虛擬地址是0xdfc00000.
addne r3, r4, r3, lsr #(SECTION_SHIFT - PMD_ORDER)
@ 獲取要對映的虛擬地址的段的頁表項的地址,存放在r3中。
orrne r6, r7, r0
@ 將實體記憶體段地址(r0)或上mmu標識(r7),得到對應頁表項值,存放到r6中
strne r6, [r3], #1 << PMD_ORDER
@ 將頁表項值(r6)寫入到頁表項中([r3])
@ 然後r3+4,獲取到下一個頁表項的地址
addne r6, r6, #1 << SECTION_SHIFT
@ 頁表項值+0x100000,得到下一個頁表項應該寫入的頁表項值
strne r6, [r3]
@ 將頁表項值(r6)寫入到頁表項中([r3])
上述就完成了dtb所在的兩個段的對映。
綜上,臨時核心頁表就建立完成,並且放在了0x20004000的實體地址上。