[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的物理地址上。

發佈了48 篇原創文章 · 獲贊 118 · 訪問量 23萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章