1. 程式人生 > >Exynos4412 核心移植—— 核心啟動過程分析

Exynos4412 核心移植—— 核心啟動過程分析

轉載地址:https://blog.csdn.net/zqixiao_09/article/details/50821995

核心啟動所用函式如下:


        與移植U-Boot 的過程相似,在移植Linux 之前,先了解它的啟動過程。Linux 的過程可以分為兩部分:架構/開發板相關的引導過程、後續的通用啟動過程。對於uImage、zImage ,它們首先進行自解壓得到vmlinux ,然後執行 vmlinux 開始“正常的”啟動流程。

       引導階段通常使用匯編語言編寫,它首先檢查核心是否支援當前架構的處理器,然後檢查是否支援當前開發板。通過檢查後,就為呼叫下一階段的start_kernel函式作準備了。這主要分如下兩個步驟:

1)-- 連線核心時使用的虛擬地址,所以要設定頁表、使能MMU;

2)呼叫C 函式 start_kernel 之前的常規工作,包括複製資料段、清除BSS段、呼叫start_kernel 函式。

        第二階段的關鍵程式碼主要使用C語言編寫。它進行核心初始化的全部工作,最後呼叫 rest_init 函式啟動init 過程,建立系統第一個程序:init 程序。在第二階段,仍有部分架構/開發板相關的程式碼,比如重新設定頁表、設定系統時鐘、初始化串列埠等。

下面是詳細解析:

一、第一階段

        與Uboot 一樣,我們在連線檔案中檢視函式入口點,核心編譯完成後會在arch/arm/kernel/下生成 vmlinux.lds 檔案,開啟:

        stext 在 linux/arch/arm/kernel/head.S 中被定義,做為函式入口點,linux/arch/arm/kernel/head.S是linux核心映像解壓後執行的第一個檔案


程式碼只是部分,但可以看到這一階段究竟做了些什麼:

a -- 設定為SVC模式,關閉IRQ、FIQ;

b -- 確定CPU的ID號,判定其是否有效;

c -- 確定machine的ID號,檢查合法性;

d -- 檢查bootloader傳入的引數列表atags的合法性

e -- 建立初始頁表

下面對上面遇到的程式段展開分析:

a -- 確保處於SVC模式


這沒什麼好講的,就是設定CPSR 模式位,並遮蔽中斷;

b -- 檢查CPU ID 是否匹配


獲取ID並放到 r9 暫存器中,呼叫_lookup_processor_type 函式, 函式主要用來判定核心是否和當前的CPU匹配,如果不匹配,r5暫存器的值應為0,此時會呼叫 _error_p函式,它用來列印錯誤資訊,即核心和當前的CPU不匹配,此時核心時不能啟動的;如果兩者匹配,會返回一個描述處理器結構的地址(在r5暫存器中),然後呼叫下面的函式。

      下面看_lookup_processor_type 函式,在arch/arm/kernel/head-common.S 中定義:

        上面的程式碼其實就是一個地址轉換過程,因為在判定CPU架構時未開啟系統的MMU功能,所以均使用實體地址,而核心程式碼在連線時是以虛擬地址來實現的,因此要想用proc_info_list 結構體,就要先找到proc_info_list 結構的實體地址,這樣必須使用上面的轉換程式碼。

        proc_info_list 結構體很重要。在Linux 核心映像中定義了很多個proc_info_list 結構,該結構表示的是核心所支援的CPU架構,這部分下面會講到,先分析上面的程式碼:

153 行:r3 儲存的是_lookup_processor_type_data 的實體地址 ;

155 行:得到虛擬地址和實體地址之間的offset;

156 - 157 行:利用offset,將 r5 和 r6 中儲存的虛擬地址轉變為實體地址,主要是獲得_proc_info_begin 及_proc_info_end 的實體地址,分別放到r5 和 r6 中

159 行:r9 中存放的是先前讀出的 processor ID,此處遮蔽不需要的位;

160 行:檢視程式碼和CPU硬體是否匹配,如果匹配就返回,此時 r5 存放的是該CPU型別對應的結構體_proc_info_list 的基地址 ;不成功,則檢視下一個 proc_info_list 結構體;

163行:如果直到 _proc_info_end ,都沒有匹配,則定為未知CPU,向 r5 賦 0,然後返回 ;

      下面來看看 proc_info_list 結構體 ,這個結構體在 arch/arm/include/asm/procinfo.h 中定義:


對於 Cortex-A9 來說,其結構體在檔案 arch/arm/mm/proc-v7.S 中初始化


       .section ".proc.info.init"表明了該結構在編譯後存放的位置。在連結檔案arch/arm/kernel/vmlinux.lds中:

__proc_info_begin = .;

*(.proc.info.init)

__proc_info_end = .;


      上面兩個變數 _proc_info_begin  與 _proc_info_end 用於計算 proc_info_list 結構的實體地址。

      如果CPU ID匹配,在編譯核心檔案時,會編譯 proc-v7.S 這個檔案,可以在arch/arm/mm/Makefile 中看到這個檔案


c -- 檢測 機器ID是否匹配

      主要用到_lookup_machine_type 函式,其與_lookup_processor_type 函式實現程式碼很相似,這裡不予闡述;

d -- 檢查bootloader傳入的引數列表atags的合法性


_vet_atags 函式用於檢測引數列表atags的合法性

        核心引數連結串列的格式和說明可以從核心原始碼目錄樹中的 中找到,引數連結串列必須以ATAG_CORE 開始,以ATAG_NONE結束。這裡的 ATAG_CORE,ATAG_NONE是各個引數的標記,本身是一個32位值,例如:ATAG_CORE=0x54410001。其它的引數標記還包括: ATAG_MEM32 , ATAG_INITRD , ATAG_RAMDISK ,ATAG_COMDLINE 等。每個引數標記就代表一個引數結構體,由各個引數結構體構成了引數連結串列。引數結構體的定義如下:

[cpp] view plain copy print?
  1. struct tag {  
  2.       struct  tag_header  hdr;  
  3.       union {  
  4.              struct tag_core  core;  
  5.        struct tag_mem32   mem;  
  6.           struct tag_videotext videotext;  
  7. struct tag_ramdisk  ramdisk;  
  8. struct tag_initrd     initrd;  
  9.           struct tag_serialnr     serialnr;  
  10. struct tag_revision  revision;  
  11.           struct tag_videolfb  videolfb;  
  12.           struct tag_cmdline  cmdline;  
  13.  struct tag_acorn       acorn;  
  14. struct tag_memclk    memclk;  
  15.         } u;  
  16. };  
struct tag {
      struct  tag_header  hdr;
      union {
             struct tag_core  core;
       struct tag_mem32   mem;
          struct tag_videotext videotext;
struct tag_ramdisk  ramdisk;
struct tag_initrd     initrd;
          struct tag_serialnr     serialnr;
struct tag_revision  revision;
          struct tag_videolfb  videolfb;
          struct tag_cmdline  cmdline;
 struct tag_acorn       acorn;
struct tag_memclk    memclk;
        } u;
};

引數結構體包括兩個部分,一個是 tag_header結構體,一個是u聯合體。

tag_header結構體的定義如下: 
   struct tag_header { 
                 u32 size;   
                 u32 tag; 
}; 

其中 size:表示整個 tag 結構體的大小(用字的個數來表示,而不是位元組的個數),等於tag_header的大小加上 u聯合體的大小,例如,引數結構體 ATAG_CORE 的 size=(sizeof(tag->tag_header)+sizeof(tag->u.core))>>2,一般通過函式 tag_size(struct * tag_xxx)來獲得每個引數結構體的 size。其中 tag:表示整個 tag 結構體的標記,如:ATAG_CORE等。 

[cpp] view plain copy print?
  1. __vet_atags:  
  2. tst r2, #0x3 //r2指向該引數連結串列的起始位置,此處判斷它是否字對齊  
  3. bne 1f  
  4. ldr r5, [r2, #0] //獲取第一個tag結構的size  
  5. //#define ATAG_CORE_SIZE ((2*4 + 3*4) >> 2) 判斷該tag的長度是否合法  
  6. subs r5, r5, #ATAG_CORE_SIZE  
  7. bne 1f  
  8. ldr r5, [r2, #4]  //獲取第一個tag結構體的標記,  
  9. ldr r6, =ATAG_CORE   
  10. cmp r5, r6 //判斷第一個tag結構體的標記是不是ATAG_CORE  
  11. bne 1f    
  12. mov pc, lr //正常退出  
  13. 1: mov r2, #0  
  14. mov pc, lr  //引數連表不正確  
  15. ENDPROC(__vet_atags)  
__vet_atags:
tst r2, #0x3 //r2指向該引數連結串列的起始位置,此處判斷它是否字對齊
bne 1f
 
ldr r5, [r2, #0] //獲取第一個tag結構的size
//#define ATAG_CORE_SIZE ((2*4 + 3*4) >> 2) 判斷該tag的長度是否合法
subs r5, r5, #ATAG_CORE_SIZE
bne 1f
ldr r5, [r2, #4]  //獲取第一個tag結構體的標記,
ldr r6, =ATAG_CORE 
cmp r5, r6 //判斷第一個tag結構體的標記是不是ATAG_CORE
bne 1f  
 
mov pc, lr //正常退出
 
1: mov r2, #0
mov pc, lr  //引數連表不正確
ENDPROC(__vet_atags)
e -- 建立初始頁表


其在下面被執行:


下面是詳細分析:

[cpp] view plain copy print?
  1. /* 
  2.  * Setup the initial page tables.  We only setup the barest 
  3.  * amount which are required to get the kernel running, which 
  4.  * generally means mapping in the kernel code. 
  5.  * 
  6.  * r8 = phys_offset, r9 = cpuid, r10 = procinfo 
  7.  * 
  8.  * Returns: 
  9.  *  r0, r3, r5-r7 corrupted 
  10.  *  r4 = page table (see ARCH_PGD_SHIFT in asm/memory.h) 
  11.  */  
  12. __create_page_tables:  
  13.     pgtbl   r4, r8              @ page table address  
  14.     /* 
  15.      * Clear the swapper page table 
  16.      */  
  17.     mov r0, r4  
  18.     mov r3, #0  
  19.     add r6, r0, #PG_DIR_SIZE  
  20. 1:  str r3, [r0], #4  
  21.     str r3, [r0], #4  
  22.     str r3, [r0], #4  
  23.     str r3, [r0], #4  
  24.     teq r0, r6  
  25.     bne 1b  
  26. #ifdef CONFIG_ARM_LPAE  
  27.     /* 
  28.      * Build the PGD table (first level) to point to the PMD table. A PGD 
  29.      * entry is 64-bit wide. 
  30.      */  
  31.     mov r0, r4  
  32.     add r3, r4, #0x1000         @ first PMD table address  
  33.     orr r3, r3, #3          @ PGD block type  
  34.     mov r6, #4              @ PTRS_PER_PGD  
  35.     mov r7, #1 << (55 - 32)       @ L_PGD_SWAPPER  
  36. 1:  
  37. #ifdef CONFIG_CPU_ENDIAN_BE8  
  38.     str r7, [r0], #4            @ set top PGD entry bits  
  39.     str r3, [r0], #4            @ set bottom PGD entry bits  
  40. #else  
  41.     str r3, [r0], #4            @ set bottom PGD entry bits  
  42.     str r7, [r0], #4            @ set top PGD entry bits  
  43. #endif  
  44.     add r3, r3, #0x1000         @ next PMD table  
  45.     subs    r6, r6, #1  
  46.     bne 1b  
  47.     add r4, r4, #0x1000         @ point to the PMD tables  
  48. #ifdef CONFIG_CPU_ENDIAN_BE8  
  49.     add r4, r4, #4          @ we only write the bottom word  
  50. #endif  
  51. #endif  
  52.     ldr r7, [r10, #PROCINFO_MM_MMUFLAGS] @ mm_mmuflags  
  53.     /* 
  54.      * Create identity mapping to cater for __enable_mmu. 
  55.      * This identity mapping will be removed by paging_init(). 
  56.      */  
  57.     adr r0, __turn_mmu_on_loc  
  58.     ldmia   r0, {r3, r5, r6}  
  59.     sub r0, r0, r3          @ virt->phys offset  
  60.     add r5, r5, r0          @ phys __turn_mmu_on  
  61.     add r6, r6, r0          @ phys __turn_mmu_on_end  
  62.     mov r5, r5, lsr #SECTION_SHIFT  
  63.     mov r6, r6, lsr #SECTION_SHIFT  
  64. 1:  orr r3, r7, r5, lsl #SECTION_SHIFT  @ flags + kernel base  
  65.     str r3, [r4, r5, lsl #PMD_ORDER]    @ identity mapping  
  66.     cmp r5, r6  
  67.     addlo   r5, r5, #1          @ next section  
  68.     blo 1b  
  69.     /* 
  70.      * Map our RAM from the start to the end of the kernel .bss section. 
  71.      */  
  72.     add r0, r4, #PAGE_OFFSET >> (SECTION_SHIFT - PMD_ORDER)  
  73.     ldr r6, =(_end - 1)  
  74.     orr r3, r8, r7  
  75.     add r6, r4, r6, lsr #(SECTION_SHIFT - PMD_ORDER)  
  76. 1:  str r3, [r0], #1 << PMD_ORDER  
  77.     add r3, r3, #1 << SECTION_SHIFT  
  78.     cmp r0, r6  
  79.     bls 1b  
  80. #ifdef CONFIG_XIP_KERNEL  
  81.     /* 
  82.      * Map the kernel image separately as it is not located in RAM. 
  83.      */  
  84. #define XIP_START XIP_VIRT_ADDR(CONFIG_XIP_PHYS_ADDR)  
  85.     mov r3, pc  
  86.     mov r3, r3, lsr #SECTION_SHIFT  
  87.     orr r3, r7, r3, lsl #SECTION_SHIFT  
  88.     add r0, r4,  #(XIP_START & 0xff000000) >> (SECTION_SHIFT - PMD_ORDER)  
  89.     str r3, [r0, #((XIP_START & 0x00f00000) >> SECTION_SHIFT) << PMD_ORDER]!  
  90.     ldr r6, =(_edata_loc - 1)  
  91.     add r0, r0, #1 << PMD_ORDER  
  92.     add r6, r4, r6, lsr #(SECTION_SHIFT - PMD_ORDER)  
  93. 1:  cmp r0, r6  
  94.     add r3, r3, #1 << SECTION_SHIFT  
  95.     strls   r3, [r0], #1 << PMD_ORDER  
  96.     bls 1b  
  97. #endif  
  98.     /* 
  99.      * Then map boot params address in r2 if specified. 
  100.      * We map 2 sections in case the ATAGs/DTB crosses a section boundary. 
  101.      */  
  102.     mov r0, r2, lsr #SECTION_SHIFT  
  103.     movs    r0, r0, lsl #SECTION_SHIFT  
  104.     subne   r3, r0, r8  
  105.     addne   r3, r3, #PAGE_OFFSET  
  106.     addne   r3, r4, r3, lsr #(SECTION_SHIFT - PMD_ORDER)  
  107.     orrne   r6, r7, r0  
  108.     strne   r6, [r3], #1 << PMD_ORDER  
  109.     addne   r6, r6, #1 << SECTION_SHIFT  
  110.     strne   r6, [r3]  
  111. #if defined(CONFIG_ARM_LPAE) && defined(CONFIG_CPU_ENDIAN_BE8)  
  112.     sub r4, r4, #4          @ Fixup page table pointer  
  113.                         @ for 64-bit descriptors  
  114. #endif  
  115. #ifdef CONFIG_DEBUG_LL  
  116. #if !defined(CONFIG_DEBUG_ICEDCC) && !defined(CONFIG_DEBUG_SEMIHOSTING)  
  117.     /* 
  118.      * Map in IO space for serial debugging. 
  119.      * This allows debug messages to be output 
  120.      * via a serial console before paging_init. 
  121.      */  
  122.     addruart r7, r3, r0  
  123.     mov r3, r3, lsr #SECTION_SHIFT  
  124.     mov r3, r3, lsl #PMD_ORDER  
  125.     add r0, r4, r3  
  126.     mov r3, r7, lsr #SECTION_SHIFT  
  127.     ldr r7, [r10, #PROCINFO_IO_MMUFLAGS] @ io_mmuflags  
  128.     orr r3, r7, r3, lsl #SECTION_SHIFT  
  129. #ifdef CONFIG_ARM_LPAE  
  130.     mov r7, #1 << (54 - 32)       @ XN  
  131. #ifdef CONFIG_CPU_ENDIAN_BE8  
  132.     str r7, [r0], #4  
  133.     str r3, [r0], #4  
  134. #else  
  135.     str r3, [r0], #4  
  136.     str r7, [r0], #4  
  137. #endif  
  138. #else  
  139.     orr r3, r3, #PMD_SECT_XN  
  140.     str r3, [r0], #4  
  141. #endif  
  142. #else /* CONFIG_DEBUG_ICEDCC || CONFIG_DEBUG_SEMIHOSTING */  
  143.     /* we don't need any serial debugging mappings */  
  144.     ldr r7, [r10, #PROCINFO_IO_MMUFLAGS] @ io_mmuflags  
  145. #endif  
  146. #if defined(CONFIG_ARCH_NETWINDER) || defined(CONFIG_ARCH_CATS)  
  147.     /* 
  148.      * If we're using the NetWinder or CATS, we also need to map 
  149.      * in the 16550-type serial port for the debug messages 
  150.      */  
  151.     add r0, r4, #0xff000000 >> (SECTION_SHIFT - PMD_ORDER)  
  152.     orr r3, r7, #0x7c000000  
  153.     str r3, [r0]  
  154. #endif  
  155. #ifdef CONFIG_ARCH_RPC  
  156.     /* 
  157.      * Map in screen at 0x02000000 & SCREEN2_BASE 
  158.      * Similar reasons here - for debug.  This is 
  159.      * only for Acorn RiscPC architectures. 
  160.      */  
  161.     add r0, r4, #0x02000000 >> (SECTION_SHIFT - PMD_ORDER)  
  162.     orr r3, r7, #0x02000000  
  163.     str r3, [r0]  
  164.     add r0, r4, #0xd8000000 >> (SECTION_SHIFT - PMD_ORDER)  
  165.     str r3, [r0]  
  166. #endif  
  167. #endif  
  168. #ifdef CONFIG_ARM_LPAE  
  169.     sub r4, r4, #0x1000     @ point to the PGD table  
  170.     mov r4, r4, lsr #ARCH_PGD_SHIFT  
  171. #endif  
  172.     mov pc, lr  
  173. ENDPROC(__create_page_tables)  
/*
 * 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 = page table (see ARCH_PGD_SHIFT in asm/memory.h)
 */
__create_page_tables:
	pgtbl	r4, r8				@ page table address

	/*
	 * 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

#ifdef CONFIG_ARM_LPAE
	/*
	 * Build the PGD table (first level) to point to the PMD table. A PGD
	 * entry is 64-bit wide.
	 */
	mov	r0, r4
	add	r3, r4, #0x1000			@ first PMD table address
	orr	r3, r3, #3			@ PGD block type
	mov	r6, #4				@ PTRS_PER_PGD
	mov	r7, #1 << (55 - 32)		@ L_PGD_SWAPPER
1:
#ifdef CONFIG_CPU_ENDIAN_BE8
	str	r7, [r0], #4			@ set top PGD entry bits
	str	r3, [r0], #4			@ set bottom PGD entry bits
#else
	str	r3, [r0], #4			@ set bottom PGD entry bits
	str	r7, [r0], #4			@ set top PGD entry bits
#endif
	add	r3, r3, #0x1000			@ next PMD table
	subs	r6, r6, #1
	bne	1b

	add	r4, r4, #0x1000			@ point to the PMD tables
#ifdef CONFIG_CPU_ENDIAN_BE8
	add	r4, r4, #4			@ we only write the bottom word
#endif
#endif

	ldr	r7, [r10, #PROCINFO_MM_MMUFLAGS] @ mm_mmuflags

	/*
	 * 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

	/*
	 * 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

#ifdef CONFIG_XIP_KERNEL
	/*
	 * Map the kernel image separately as it is not located in RAM.
	 */
#define XIP_START XIP_VIRT_ADDR(CONFIG_XIP_PHYS_ADDR)
	mov	r3, pc
	mov	r3, r3, lsr #SECTION_SHIFT
	orr	r3, r7, r3, lsl #SECTION_SHIFT
	add	r0, r4,  #(XIP_START & 0xff000000) >> (SECTION_SHIFT - PMD_ORDER)
	str	r3, [r0, #((XIP_START & 0x00f00000) >> SECTION_SHIFT) << PMD_ORDER]!
	ldr	r6, =(_edata_loc - 1)
	add	r0, r0, #1 << PMD_ORDER
	add	r6, r4, r6, lsr #(SECTION_SHIFT - PMD_ORDER)
1:	cmp	r0, r6
	add	r3, r3, #1 << SECTION_SHIFT
	strls	r3, [r0], #1 << PMD_ORDER
	bls	1b
#endif

	/*
	 * 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]

#if defined(CONFIG_ARM_LPAE) && defined(CONFIG_CPU_ENDIAN_BE8)
	sub	r4, r4, #4			@ Fixup page table pointer
						@ for 64-bit descriptors
#endif

#ifdef CONFIG_DEBUG_LL
#if !defined(CONFIG_DEBUG_ICEDCC) && !defined(CONFIG_DEBUG_SEMIHOSTING)
	/*
	 * Map in IO space for serial debugging.
	 * This allows debug messages to be output
	 * via a serial console before paging_init.
	 */
	addruart r7, r3, r0

	mov	r3, r3, lsr #SECTION_SHIFT
	mov	r3, r3, lsl #PMD_ORDER

	add	r0, r4, r3
	mov	r3, r7, lsr #SECTION_SHIFT
	ldr	r7, [r10, #PROCINFO_IO_MMUFLAGS] @ io_mmuflags
	orr	r3, r7, r3, lsl #SECTION_SHIFT
#ifdef CONFIG_ARM_LPAE
	mov	r7, #1 << (54 - 32)		@ XN
#ifdef CONFIG_CPU_ENDIAN_BE8
	str	r7, [r0], #4
	str	r3, [r0], #4
#else
	str	r3, [r0], #4
	str	r7, [r0], #4
#endif
#else
	orr	r3, r3, #PMD_SECT_XN
	str	r3, [r0], #4
#endif

#else /* CONFIG_DEBUG_ICEDCC || CONFIG_DEBUG_SEMIHOSTING */
	/* we don't need any serial debugging mappings */
	ldr	r7, [r10, #PROCINFO_IO_MMUFLAGS] @ io_mmuflags
#endif

#if defined(CONFIG_ARCH_NETWINDER) || defined(CONFIG_ARCH_CATS)
	/*
	 * If we're using the NetWinder or CATS, we also need to map
	 * in the 16550-type serial port for the debug messages
	 */
	add	r0, r4, #0xff000000 >> (SECTION_SHIFT - PMD_ORDER)
	orr	r3, r7, #0x7c000000
	str	r3, [r0]
#endif
#ifdef CONFIG_ARCH_RPC
	/*
	 * Map in screen at 0x02000000 & SCREEN2_BASE
	 * Similar reasons here - for debug.  This is
	 * only for Acorn RiscPC architectures.
	 */
	add	r0, r4, #0x02000000 >> (SECTION_SHIFT - PMD_ORDER)
	orr	r3, r7, #0x02000000
	str	r3, [r0]
	add	r0, r4, #0xd8000000 >> (SECTION_SHIFT - PMD_ORDER)
	str	r3, [r0]
#endif
#endif
#ifdef CONFIG_ARM_LPAE
	sub	r4, r4, #0x1000		@ point to the PGD table
	mov	r4, r4, lsr #ARCH_PGD_SHIFT
#endif
	mov	pc, lr
ENDPROC(__create_page_tables)


f -- 使能MMU,跳轉到start_kernel


檔案linux/arch/arm/kernel/head.S


檔案linux/arch/arm/kernel/head.S


在前面有過這樣的指令操作ldr r13, __switch_data 

mov pc, r13 就是將跳轉到__switch_data處。

在檔案linux/arch/arm/kernel/head-common.S中:

[cpp] view plain copy print?
  1. .type __switch_data, %object  //定義一個物件  
  2. __switch_data:  
  3. .long __mmap_switched  //由此可知上面程式將跳轉到該程式段處。  
  4. .long __data_loc @ r4  
  5. .long _data @ r5  
  6. .long __bss_start @ r6  
  7. .long _end @ r7  
  8. .long processor_id @ r4  
  9. .long __machine_arch_type @ r5  
  10. .long __atags_pointer @ r6  
  11. .long cr_alignment @ r7  
  12. .long init_thread_union + THREAD_START_SP @ sp  
  13.  . = PAGE_OFFSET + TEXT_OFFSET;  
  14.  #else  
  15.  . = ALIGN(THREAD_SIZE);  
  16.  __data_loc = .;  
  17.  #endif  
  18.  .data : AT(__data_loc) {  //此處資料儲存在上面__data_loc處。  
  19.  _data = .;  
  20.  *(.data.init_task)  
  21. …………………………  
  22. .bss : {  
  23. __bss_start = .;  
  24. *(.bss)  
  25. *(COMMON)  
  26. _end = .;  
  27. }  
  28. ………………………………  
  29. }  
  30. init_thread_union 是 init程序的基地址. 在 arch/arm/kernel/init_task.c 中:  
  31. 00033: union thread_union init_thread_union  
  32. 00034:         __attribute__((__section__(".init.task"))) =  
  33. 00035:                 { INIT_THREAD_INFO(init_task) };          
  34.     對照 vmlnux.lds.S 中,我們可以知道init task是存放在 .data 段的開始8k, 並且是THREAD_SIZE(8k)對齊的  
  35. */  
  36. __mmap_switched:  
  37. adr r3, __switch_data + 4  
  38. ldmia r3!, {r4, r5, r6, r7}    
  39. cmp r4, r5 @ Copy data segment if needed  
  40. 1: cmpne r5, r6  //將 __data_loc處資料搬移到_data處  
  41. ldrne fp, [r4], #4  
  42. strne fp, [r5], #4  
  43. bne 1b  
  44. mov fp, #0 //清除BSS段內容  
  45. 1: cmp r6, r7      
  46. strcc fp, [r6],#4  
  47. bcc 1b  
  48. ldmia r3, {r4, r5, r6, r7, sp}  
  49. str r9, [r4] @ Save processor ID  
  50. str r1, [r5] @ Save machine type  
  51. str r2, [r6] @ Save atags pointer  
  52. bic r4, r0, #CR_A @ Clear 'A' bit  
  53. stmia r7, {r0, r4} @ Save control register values  
  54. b start_kernel  //程式跳轉到函式start_kernel進入C語言部分。  
  55. ENDPROC(__mmap_switched)