嵌入式 hi3518c平臺uboot中start.s小結
第一階段:
/*====================================Hi3518c start.S Begin 2014-04-20=============================================*/
/*
* armboot - Startup Code for ARM926EJS CPU-core
*
* Copyright (c) 2003 Texas Instruments
*
* ----- Adapted for OMAP1610 OMAP730 from ARM925t code ------
*
* Copyright (c) 2001 Marius Gr?ger <
* Copyright (c) 2002 Alex Z?pke <[email protected]>
* Copyright (c) 2002 Gary Jennejohn <[email protected]>
* Copyright (c) 2003 Richard Woodruff <[email protected]>
* Copyright (c) 2003 Kshitij <[email protected]>
*
* See file CREDITS for list of people who contributed to this
* project.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#include <config.h>
#include <version.h>
/*
*************************************************************************
*
* Jump vector table as in table 3.1 in [1]
*
*************************************************************************
*/
.globl _start //彙編程式都要提供一個_start符號並且用.globl宣告
_start: b reset //B或BL指令引起處理器轉移到“子程式名”處開始執行 復位
ldr pc, _undefined_instruction
ldr pc, _software_interrupt
ldr pc, _prefetch_abort
ldr pc, _data_abort
ldr pc, _not_used
ldr pc, _irq
ldr pc, _fiq
_undefined_instruction: .word undefined_instruction
_software_interrupt: .word software_interrupt
_prefetch_abort: .word prefetch_abort
_data_abort: .word data_abort
_not_used: .word not_used
_irq: .word irq
_fiq: .word fiq
_pad: .word 0x12345678 /* now 16*4=64 */
/*.fill
語法:.fill repeat, size, value
含義是反覆拷貝 size個位元組,重複 repeat 次,
其中 size 和 value 是可選的,預設值分別為 1 和 0.
*/
__blank_zone_start:
.fill 1024*4,1,0 //給某個具體的暫存器裡填數
__blank_zone_end:
.globl _blank_zone_start
_blank_zone_start:
.word __blank_zone_start
.globl _blank_zone_end
_blank_zone_end:
.word __blank_zone_end
.balignl 16,0xdeadbeef
/*
*************************************************************************
*
* Startup Code (reset vector)
*
* do important init only if we don't start from memory!
* setup Memory and board specific bits prior to relocation.
* relocate armboot to ram
* setup stack
*
*************************************************************************
*/
_TEXT_BASE:
.word TEXT_BASE
.globl _armboot_start
_armboot_start:
.word _start
/*
* These are defined in the board-specific linker script.
*/
.globl _bss_start
_bss_start:
.word __bss_start
.globl _bss_end
_bss_end:
.word _end
#ifdef CONFIG_USE_IRQ
/* IRQ stack memory (calculated at run-time) */
.globl IRQ_STACK_START
IRQ_STACK_START:
.word 0x0badc0de
/* IRQ stack memory (calculated at run-time) */
.globl FIQ_STACK_START
FIQ_STACK_START:
.word 0x0badc0de
#endif
_clr_remap_spi_entry:
.word SF_TEXT_ADRS + do_clr_remap - TEXT_BASE
_clr_remap_nand_entry:
.word NAND_TEXT_ADRS + do_clr_remap - TEXT_BASE
/*
* the actual reset code
*/
reset:
/*
* set the cpu to SVC32 mode
*/
mrs r0,cpsr //將狀態暫存器的內容傳送至通用暫存器,將CPSR中的內容傳送至R0
bic r0,r0,#0x1f //位清除指令 將R0最低5位清零,其餘位不變 工作模式位清零
orr r0,r0,#0xd3 //工作模式位設定為“10011”(管理模式),並將中斷禁止位和快中斷禁止位置1 "1101 0011" 指令用於在兩個運算元上進行邏輯或運算,並把結果放置到目的暫存器中
msr cpsr,r0 //將通用暫存器的內容傳送至狀態暫存器,將中的內容R0傳送至CPSR
/*
* we do sys-critical inits only at reboot,
* not when booting from ram!
*/
/*
* flush v4 I/D caches
*/
mov r0, #0 //置零ro通用暫存器
mcr p15, 0, r0, c7, c7, 0 /* flush v3/v4 cache */ //向c7寫入0將使ICache與DCache無效 "0"表示省略opcode_2 MCR{<cond>} p15, 0, <Rd>, <CRn>, <CRm>{,<opcode_2>}
mcr p15, 0, r0, c8, c7, 0 /* flush v4 TLB */ //MCR{條件} 協處理器編碼,協處理器操作碼1,源暫存器,目的暫存器1,目的暫存器2,協處理器操作碼2
/*
* disable MMU stuff and caches
*/
mrc p15, 0, r0, c1, c0, 0
bic r0, r0, #0x00002300 /* clear bits 13, 9:8 (--V- --RS) */
bic r0, r0, #0x00000087 /* clear bits 7, 2:0 (B--- -CAM) */
orr r0, r0, #0x00000002 /* set bit 2 (A) Align */
mcr p15, 0, r0, c1, c0, 0 //儲存r0到控制暫存器
mov r0, pc, lsr#24 //LSL、LSR、ASR、ROR 暫存器移位
cmp r0, #0x0
bne do_clr_remap //檢測是否需要跳轉,PC的高八位如果不為0(已經在ram中運行了)則跳轉 不等於則調轉
check_start_mode:
ldr r0, =REG_BASE_SCTL
ldr r0, [r0, #REG_SYSSTAT]
mov r6, r0, lsr#5
and r6, #0x1
/* reg[0x2005008c:5]:
* 0: start from spi
* 1: start from nand
*/
cmp r6, #BOOT_FROM_SPI
ldreq pc, _clr_remap_spi_entry
ldr pc, _clr_remap_nand_entry
@b . /* bug here */
/*
LDR和STR用來存取記憶體,關於"索引偏移",你是不是指pre-indexed addressing和post-indexed addressingpre-indexed addressing是指地址經過運算不寫回基址暫存器post-indexed addressing則回寫到基址暫存器比如pre-indexed addressing:mov r1,#0STR r0, [r1, #0x10] ;r1+0x10這個是所用的實際地址值,但是不回寫入r1,在此句之後,r1=0post-indexed addressing:STR
r0, [r1], #0x10 ;r1+0x10這個是所用的實際地址值,這個值回寫入r1,此句之後,r1=0x10
*/
do_clr_remap:
ldr r4, =REG_BASE_SCTL //用來從儲存器(確切地說是地址空間)中裝載資料到通用暫存器 系統控制器暫存器 0x20050000 寫地址
ldr r0, [r4, #REG_SC_CTRL] //載入32位的立即數或一個地址值到指定暫存器 不回寫 其實是r4+#0x0是實際地址值
/* reg[0x20050000:8]:
* 0: keep remap
* 1: clear remap 重對映
*/
@Set clear remap bit.
orr r0, #(1<<8) //第八位置1
str r0, [r4, #REG_SC_CTRL] //不回寫 @表示註釋
@Setup TCM (ENABLED, 2KB) // TCM時鐘門控使能
ldr r0, =( 1 | (MEM_CONF_ITCM_SIZE<<2) | MEM_BASE_ITCM)
mcr p15, 0, r0, c9, c1, 1
@enable I-Cache now
mrc p15, 0, r0, c1, c0, 0
orr r0, r0, #0x00001000 /* set bit 12 (I) I-Cache */
mcr p15, 0, r0, c1, c0, 0
@Check if I'm running in ddr //程式碼記憶體執行測試
mov r0, pc, lsr#28
cmp r0, #8
bleq relocate //小於等於跳轉
ldr r0, _blank_zone_start
ldr r1, _TEXT_BASE //程式碼段
sub r0, r0, r1 //減法 sub a,b (a-b)
adrl r1, _start //將相對於程式或相對於暫存器的地址載入暫存器中 adrl寬
add r0, r0, r1 //加法
mov r1, #0 /* flags: 0->normal 1->pm */
bl init_registers //初始化暫存器
#ifndef CONFIG_SKIP_RELOCATE_UBOOT
relocate:
@copy arm exception table in 0 address
adrl r0, _start
mov r1, #0
mov r2, #0x100 /* copy arm Exception table to 0 addr */
add r2, r0, r2
copy_exception_table:
ldmia r0!, {r3 - r10}
stmia r1!, {r3 - r10}
cmp r0, r2
ble copy_exception_table
@relocate U-Boot to RAM
adrl r0, _start /* r0 <- current position of code */
ldr r1, _TEXT_BASE /* test if we run from flash or RAM */
cmp r0, r1 /* don't reloc during debug */
beq stack_setup
ldr r2, _armboot_start
ldr r3, _bss_start
sub r2, r3, r2 /* r2 <- size of armboot */
add r2, r0, r2 /* r2 <- source end address */
copy_loop:
ldmia r0!, {r3-r10} /* copy from source address [r0] */
stmia r1!, {r3-r10} /* copy to target address [r1] */
cmp r0, r2 /* until source end addreee [r2] */
ble copy_loop
#endif /* CONFIG_SKIP_RELOCATE_UBOOT */
/* Set up the stack */
stack_setup:
ldr r0, _TEXT_BASE /* upper 128 KiB: relocated uboot */
sub r0, r0, #CONFIG_SYS_MALLOC_LEN /* malloc area */
sub r0, r0, #CONFIG_SYS_GBL_DATA_SIZE /* bdinfo */
#ifdef CONFIG_USE_IRQ
sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)
#endif
sub sp, r0, #12 /* leave 3 words for abort-stack */
bic sp, sp, #7 /*8-byte alignment for ABI compliance*/
clear_bss:
ldr r0, _bss_start /* find start of bss segment */
ldr r1, _bss_end /* stop here */
mov r2, #0x00000000 /* clear */
clbss_l:str r2, [r0] /* clear loop... */
add r0, r0, #4
cmp r0, r1
ble clbss_l
ldr pc, _start_armboot
_start_armboot:
.word start_armboot
/*
*************************************************************************
*
* CPU_init_critical registers
*
* setup important registers
* setup memory timing
*
*************************************************************************
*/
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
cpu_init_crit:
/*
* flush v4 I/D caches
*/
mov r0, #0
mcr p15, 0, r0, c7, c7, 0 /* flush v3/v4 cache */
mcr p15, 0, r0, c8, c7, 0 /* flush v4 TLB */
/*
* disable MMU stuff and caches
*/
mrc p15, 0, r0, c1, c0, 0
bic r0, r0, #0x00002300 /* clear bits 13, 9:8 (--V- --RS) */
bic r0, r0, #0x00000087 /* clear bits 7, 2:0 (B--- -CAM) */
orr r0, r0, #0x00000002 /* set bit 2 (A) Align */
orr r0, r0, #0x00001000 /* set bit 12 (I) I-Cache */
mcr p15, 0, r0, c1, c0, 0
/*
* Go setup Memory and board specific bits prior to relocation.
*/
mov ip, lr /* perserve link reg across call */
@bl lowlevel_init /* go setup pll,mux,memory */
mov lr, ip /* restore link */
mov pc, lr /* back to my caller */
#endif /* CONFIG_SKIP_LOWLEVEL_INIT */
#ifndef CONFIG_PRELOADER
/*
*************************************************************************
*
* Interrupt handling
*
*************************************************************************
*/
@
@ IRQ stack frame.
@
#define S_FRAME_SIZE 72
#define S_OLD_R0 68
#define S_PSR 64
#define S_PC 60
#define S_LR 56
#define S_SP 52
#define S_IP 48
#define S_FP 44
#define S_R10 40
#define S_R9 36
#define S_R8 32
#define S_R7 28
#define S_R6 24
#define S_R5 20
#define S_R4 16
#define S_R3 12
#define S_R2 8
#define S_R1 4
#define S_R0 0
#define MODE_SVC 0x13
#define I_BIT 0x80
/*
* use bad_save_user_regs for abort/prefetch/undef/swi ...
* use irq_save_user_regs / irq_restore_user_regs for IRQ/FIQ handling
*/
.macro bad_save_user_regs
@ carve out a frame on current user stack
sub sp, sp, #S_FRAME_SIZE
stmia sp, {r0 - r12} @ Save user registers (now in svc mode) r0-r12
ldr r2, _armboot_start
sub r2, r2, #(CONFIG_STACKSIZE+CONFIG_SYS_MALLOC_LEN)
@ set base 2 words into abort stack
sub r2, r2, #(CONFIG_SYS_GBL_DATA_SIZE+8)
@ get values for "aborted" pc and cpsr (into parm regs)
ldmia r2, {r2 - r3}
add r0, sp, #S_FRAME_SIZE @ grab pointer to old stack
add r5, sp, #S_SP
mov r1, lr
stmia r5, {r0 - r3} @ save sp_SVC, lr_SVC, pc, cpsr
mov r0, sp @ save current stack into r0 (param register)
.endm
.macro irq_save_user_regs
sub sp, sp, #S_FRAME_SIZE
stmia sp, {r0 - r12} @ Calling r0-r12
@ !!!! R8 NEEDS to be saved !!!! a reserved stack spot would be good.
add r8, sp, #S_PC
stmdb r8, {sp, lr}^ @ Calling SP, LR
str lr, [r8, #0] @ Save calling PC
mrs r6, spsr
str r6, [r8, #4] @ Save CPSR
str r0, [r8, #8] @ Save OLD_R0
mov r0, sp
.endm
.macro irq_restore_user_regs
ldmia sp, {r0 - lr}^ @ Calling r0 - lr
mov r0, r0
ldr lr, [sp, #S_PC] @ Get PC
add sp, sp, #S_FRAME_SIZE
subs pc, lr, #4 @ return & move spsr_svc into cpsr
.endm
.macro get_bad_stack
ldr r13, _armboot_start @ setup our mode stack
sub r13, r13, #(CONFIG_STACKSIZE+CONFIG_SYS_MALLOC_LEN)
@ reserved a couple spots in abort stack
sub r13, r13, #(CONFIG_SYS_GBL_DATA_SIZE+8)
str lr, [r13] @ save caller lr in position 0 of saved stack
mrs lr, spsr @ get the spsr
str lr, [r13, #4] @ save spsr in position 1 of saved stack
mov r13, #MODE_SVC @ prepare SVC-Mode
@ msr spsr_c, r13
msr spsr, r13 @ switch modes, make sure moves will execute
mov lr, pc @ capture return pc
movs pc, lr @ jump to next instruction & switch modes.
.endm
.macro get_irq_stack @ setup IRQ stack
ldr sp, IRQ_STACK_START
.endm
.macro get_fiq_stack @ setup FIQ stack
ldr sp, FIQ_STACK_START
.endm
#endif /* CONFIG_PRELOADER */
/*
* exception handlers
*/
#ifdef CONFIG_PRELOADER
.align 5 //加上.align彙編語句後,指令就對齊
do_hang:
ldr sp, _TEXT_BASE /* switch to abort stack */
1:
bl 1b /* hang and never return */
#else /* !CONFIG_PRELOADER */
.align 5
undefined_instruction:
get_bad_stack
bad_save_user_regs
bl do_undefined_instruction
.align 5
software_interrupt:
get_bad_stack
bad_save_user_regs
bl do_software_interrupt
.align 5
prefetch_abort:
get_bad_stack
bad_save_user_regs
bl do_prefetch_abort
.align 5
data_abort:
get_bad_stack
bad_save_user_regs
bl do_data_abort
.align 5
not_used:
get_bad_stack
bad_save_user_regs
bl do_not_used
#ifdef CONFIG_USE_IRQ
.align 5
irq:
get_irq_stack
irq_save_user_regs
bl do_irq
irq_restore_user_regs
.align 5
fiq:
get_fiq_stack
/* someone ought to write a more effiction fiq_save_user_regs */
irq_save_user_regs
bl do_fiq
irq_restore_user_regs
#else
.align 5
irq:
get_bad_stack
bad_save_user_regs
bl do_irq
.align 5
fiq:
get_bad_stack
bad_save_user_regs
bl do_fiq
#endif
#endif /* CONFIG_PRELOADER */
#include "lowlevel_init.S"
/*====================================Hi3518c start.S End=============================================*/
最近一直在做U-boot和Linux核心的編譯與移植的工作,就來講一講對U-boot的初步理解。我的目標板核心片是i.MX255,以下都是依據這個環境所言。
1.U-boot啟動過程:
1)/uboot/cpu/arm926ejs/start.S檔案是Uboot的入口程式。
2)/uboot/lib_arm/board.c Uboot執行的第一個C函式,完成系統的初始化。
3)init_sequence[] 是基本的初始化函式指標。
4)void start_armboot(void) 數序執行init_sequence[]陣列中的初始化函式。
我把U-boot的執行過程簡化描述如下:
check board->dram_init->flash init->nand init->env relocate->ip,mac獲取->device init->網絡卡初始化->進入main_loop函式,等待串列埠輸入(無輸入則執行bootcmd命令)。
2.U-boot和核心的主要關係式核心啟動過程中引數的傳遞。
U-boot會給Linux Kernel傳遞很多引數,如:串列埠,RAM,videofb等。而核心也會讀取和處理這些引數。兩者之間通過struct tag來傳遞引數。U-boot把要傳遞給kernel的東西儲存在struct tag資料結構中,啟動kernel時,把這個結構體的實體地址傳給kernel;Linux kernel通過這個地址,用parse_tags分析出傳遞過來的引數。
1、u-boot給kernel傳引數:
在uboot/common/cmd_bootm.c檔案中,bootm命令對應的do_bootm函式,當分析uImage中資訊發現OS是Linux時,呼叫. /lib_arm/bootm.c檔案中的do_bootm_linux函式來啟動Linux kernel。
2、核心讀取U-boot傳遞的相關引數:
對於Linux Kernel配合ARM平臺啟動時,先執行arch/arm/kernel/head.S,這個檔案會呼叫arch/arm/kernel/head-common.S中的函式,在最後呼叫start_kernel。
1. kernel執行的史前時期和記憶體佈局
在arm平臺下,zImage.bin壓縮映象是由bootloader載入到實體記憶體,然後跳到zImage.bin裡一段程式,它專門於將被壓縮的kernel解壓縮到KERNEL_RAM_PADDR開始的一段記憶體中,接著跳進真正的kernel去執行。該kernel的執行起點是stext函式,定義於arch/arm/kernel/head.S。
在分析stext函式前,先介紹此時記憶體的佈局如下圖所示
在開發板tqs3c2440中,SDRAM連線到記憶體控制器的Bank6中,它的開始記憶體地址是0x30000000,大小為64M,即0x20000000。 ARM Linux kernel將SDRAM的開始地址定義為PHYS_OFFSET。經bootloader載入kernel並由自解壓部分程式碼執行後,最終kernel被放置到KERNEL_RAM_PADDR(=PHYS_OFFSET + TEXT_OFFSET,即0x30008000)地址上的一段記憶體,經此放置後,kernel程式碼以後均不會被移動。
在進入kernel程式碼前,即bootloader和自解壓縮階段,ARM未開啟MMU功能。因此kernel啟動程式碼一個重要功能是設定好相應的頁表,並開啟MMU功能。為了支援MMU功能,kernel映象中的所有符號,包括程式碼段和資料段的符號,在連結時都生成了它在開啟MMU時,所在實體記憶體地址對映到的虛擬記憶體地址。
以arm kernel第一個符號(函式)stext為例,在編譯連結,它生成的虛擬地址是0xc0008000,而放置它的實體地址為0x30008000(還記得這是PHYS_OFFSET+TEXT_OFFSET嗎?)。實際上這個變換可以利用簡單的公式進行表示:va = pa – PHYS_OFFSET + PAGE_OFFSET。Arm linux最終的kernel空間的頁表,就是按照這個關係來建立。
之所以較早提及arm linux 的記憶體對映,原因是在進入kernel程式碼,裡面所有符號地址值為清一色的0xCXXXXXXX地址,而此時ARM未開啟MMU功能,故在執行stext函式第一條執行時,它的PC值就是stext所在的記憶體地址(即實體地址,0x30008000)。因此,下面有些程式碼,需要使用地址無關技術。
2. 一覽stext函式
這裡的啟動流程指的是解壓後kernel開始執行的一部分程式碼,這部分程式碼和ARM體系結構是緊密聯絡在一起的,所以最好是將ARM ARCHITECTURE REFERENCE MANUL仔細讀讀,尤其裡面關於控制暫存器啊,MMU方面的內容~
stext函式定義在Arch/arm/kernel/head.S,它的功能是獲取處理器型別和機器型別資訊,並建立臨時的頁表,然後開啟MMU功能,並跳進第一個C語言函式start_kernel。
stext函式的在前置條件是:MMU, D-cache, 關閉; r0 = 0, r1 = machine nr, r2 = atags prointer.
前面說過解壓以後,程式碼會跳到解壓完成以後的vmlinux開始執行,具體從什麼地方開始執行我們可以看看生成的vmlinux.lds(arch/arm/kernel/)這個檔案:
[cpp]view plaincopyprint?- 1. OUTPUT_ARCH(arm)
- 2. ENTRY(stext)
- 3. jiffies = jiffies_64;
- 4. SECTIONS
- 5. {
- 6. . = 0x80000000 + 0x00008000;
- 7. .text.head : {
- 8. _stext = .;
- 9. _sinittext = .;
- 0. *(.text.h
1. OUTPUT_ARCH(arm)
2. ENTRY(stext)
3. jiffies = jiffies_64;
4. SECTIONS
5. {
6. . = 0x80000000 + 0x00008000;
7. .text.head : {
8. _stext = .;
9. _sinittext = .;
10. *(.text.h
很明顯我們的vmlinx最開頭的section是.text.head,這裡我們不能看ENTRY的內容,以為這時候我們沒有作業系統,根本不知道如何來解析這裡的入口地址,我們只能來分析他的section(不過一般來說這裡的ENTRY和我們從seciton分析的結果是一樣的),這裡的.text.head section我們很容易就能在arch/arm/kernel/head.S裡面找到,而且它裡面的第一個符號就是我們的stext:
[cpp]view plaincopyprint?- # .section ".text.head", "ax"
- #
- # ENTRY(stext)
- #
- # /* 設定CPU執行模式為SVC,並關中斷 */
- #
- # msr cpsr_c, #PSR_F_BIT | PSR_I_BIT | SVC_MODE @ ensure svc mode
- #
- # @ and irqs disabled
- #
- # mrc p15, 0, r9, c0, c0 @ get processor id
- #
- # bl __lookup_processor_type @ r5=procinfo r9=cupid
- #
- # /* r10指向cpu對應的proc_info記錄 */
- #
- # movs r10, r5 @ invalid processor (r5=0)?
- #
- # beq __error_p @ yes, error 'p'
- #
- # bl __lookup_machine_type @ r5=machinfo
- #
- # /* r8 指向開發板對應的arch_info記錄 */
- #
- # movs r8, r5 @ invalid machine (r5=0)?
- #
- # beq __error_a @ yes, error 'a'
- #
- # /* __vet_atags函式涉及bootloader造知kernel實體記憶體的情況,我們暫時不分析它。 */
- #
- # bl __vet_atags
- #
- # /* 建立臨時頁表 */
- #
- # bl __create_page_tables
- # /*
- #
- # * The following calls CPU specific code in a position independent
- #
- # * manner. See arch/arm/mm/proc-*.S for details. r10 = base of
- #
- # * xxx_proc_info structure selected by __lookup_machine_type
- #
- # * above. On return, the CPU will be ready for the MMU to be
- #
- # * turned on, and r0 will hold the CPU control register value.
- #
- # */
- #
- # /* 這裡的邏輯關係相當複雜,先是從proc_info結構中的中跳進__arm920_setup函式,
- #
- # * 然後執__enable_mmu 函式。最後在__enable_mmu函式通過mov pc, r13來執行__switch_data,
- #
- # * __switch_data函式在最後一條語句,魚躍龍門,跳進第一個C語言函式start_kernel。
- # */
- #
- # ldr r13, __switch_data @ address to jump to after
- #
- # @ mmu has been enabled
- #
- # adr lr, __enable_mmu @ return (PIC) address
- #
- # add pc, r10, #PROCINFO_INITFUNC
- #
- # ENDPROC(stext)
# .section ".text.head", "ax"
#
# ENTRY(stext)
#
# /* 設定CPU執行模式為SVC,並關中斷 */
#
# msr cpsr_c, #PSR_F_BIT | PSR_I_BIT | SVC_MODE @ ensure svc mode
#
# @ and irqs disabled
#
# mrc p15, 0, r9, c0, c0 @ get processor id
#
# bl __lookup_processor_type @ r5=procinfo r9=cupid
#
# /* r10指向cpu對應的proc_info記錄 */
#
# movs r10, r5 @ invalid processor (r5=0)?
#
# beq __error_p @ yes, error 'p'
#
# bl __lookup_machine_type @ r5=machinfo
#
# /* r8 指向開發板對應的arch_info記錄 */
#
# movs r8, r5 @ invalid machine (r5=0)?
#
# beq __error_a @ yes, error 'a'
#
# /* __vet_atags函式涉及bootloader造知kernel實體記憶體的情況,我們暫時不分析它。 */
#
# bl __vet_atags
#
# /* 建立臨時頁表 */
#
# bl __create_page_tables
# /*
#
# * The following calls CPU specific code in a position independent
#
# * manner. See arch/arm/mm/proc-*.S for details. r10 = base of
#
# * xxx_proc_info structure selected by __lookup_machine_type
#
# * above. On return, the CPU will be ready for the MMU to be
#
# * turned on, and r0 will hold the CPU control register value.
#
# */
#
# /* 這裡的邏輯關係相當複雜,先是從proc_info結構中的中跳進__arm920_setup函式,
#
# * 然後執__enable_mmu 函式。最後在__enable_mmu函式通過mov pc, r13來執行__switch_data,
#
# * __switch_data函式在最後一條語句,魚躍龍門,跳進第一個C語言函式start_kernel。
# */
#
# ldr r13, __switch_data @ address to jump to after
#
# @ mmu has been enabled
#
# adr lr, __enable_mmu @ return (PIC) address
#
# add pc, r10, #PROCINFO_INITFUNC
#
# ENDPROC(stext)
這裡的ENTRY這個巨集實際我們可以在include/linux/linkage.h裡面找到,可以看到他實際上就是宣告一個GLOBAL Symbol,後面的ENDPROC和END唯一的區別是前面的聲明瞭一個函式,可以在c裡面被呼叫。
[cpp]view plaincopyprint?- 1. #ifndef ENTRY
- 2. #define ENTRY(name) /
- 3. .globl name; /
- 4. ALIGN; /
- 5. name:
- 6. #endif
- 7. #ifndef WEAK
- 8. #define WEAK(name) /
- 9. .weak name; /
- 10. name:
- 11. #endif
- 12. #ifndef END
- 13. #define END(name) /
- 14. .size name, .-name
- 15. #endif
- 16. /* If symbol 'name' is treated as a subroutine (gets called, and returns)
- 17. * then please use ENDPROC to mark 'name' as STT_FUNC for the benefit of
- 18. * static analysis tools such as stack depth analyzer.
- 19. */
- 20. #ifndef ENDPROC
- 21. #define ENDPROC(name) /
- 22. .type name, @function; /
- 23. END(name)
- 24. #endif
1. #ifndef ENTRY
2. #define ENTRY(name) /
3. .globl name; /
4. ALIGN; /
5. name:
6. #endif
7. #ifndef WEAK
8. #define WEAK(name) /
9. .weak name; /
10. name:
11. #endif
12. #ifndef END
13. #define END(name) /
14. .size name, .-name
15. #endif
16. /* If symbol 'name' is treated as a subroutine (gets called, and returns)
17. * then please use ENDPROC to mark 'name' as STT_FUNC for the benefit of
18. * static analysis tools such as stack depth analyzer.
19. */
20. #ifndef ENDPROC
21. #define ENDPROC(name) /
22. .type name, @function; /
23. END(name)
24. #endif
找到了vmlinux的起始程式碼我們就來進行分析了,先總體概括一下這部分程式碼所完成的功能,head.S會首先檢查proc和arch以及atag的有效性,然後會建立初始化頁表,並進行CPU必要的處理以後開啟MMU,並跳轉到start_kernel這個symbol開始執行後面的C程式碼。這裡有很多變數都是我們進行kernel移植時需要特別注意的,下面會一一講到。
在這裡我們首先看看這段彙編開始跑的時候的暫存器資訊,這裡的暫存器內容實際上是同bootloader跳轉到解壓程式碼是一樣的,就是r1=arch r2=atag addr。下面我們就具體來看看這個head.S跑的過程:
[cpp]view plaincopyprint?- 1. msr cpsr_c, #PSR_F_BIT | PSR_I_BIT | SVC_MODE @ ensure svc mode
- 2. @ and irqs disabled
- 3. mrc p15, 0, r9, c0, c0 @ get processor id
1. msr cpsr_c, #PSR_F_BIT | PSR_I_BIT | SVC_MODE @ ensure svc mode
2. @ and irqs disabled
3. mrc p15, 0, r9, c0, c0 @ get processor id
首先進入SVC模式並關閉所有中斷,並從arm協處理器裡面讀到CPU ID,這裡的CPU主要是指arm架構相關的CPU型號,比如ARM9,ARM11等等。
3 __lookup_processor_type 函式
然後跳轉到__lookup_processor_type,這個函式定義在head-common.S裡面,這裡的bl指令會儲存當前的pc在lr裡面
__lookup_processor_type 函式是一個非常講究技巧的函式,如果你將它領會,也將領會kernel了一些魔法。
Kernel 程式碼將所有CPU資訊的定義都放到.proc.info.init段中,因此可以認為.proc.info.init段就是一個數組,每個元素都定義了一個或一種CPU的資訊。目前__lookup_processor_type使用該元素的前兩個欄位cpuid和mask來匹配當前CPUID,如果滿足 CPUID & mask == cpuid,則找到當前cpu的定義並返回。
下面是tqs3c2440開發板,CPU的定義資訊,cpuid = 0x41009200,mask = 0xff00fff0。如果是碼是執行在tqs3c2440開發板上,那麼函式返回下面的定義:
最後__lookup_processor_type會從這個函式返回,我們具體看看這個函式:
[cpp]view plaincopyprint?- # __lookup_processor_type:
- # /* adr 是相對定址,它的尋計算結果是將當前PC值加上3f符號與PC的偏移量,
- # * 而PC是實體地址,因此r3的結果也是3f符號的實體地址 */
- #
- # adr r3, 3f
- #
- # /* r5值為__proc_info_bein, r6值為__proc_ino_end,而r7值為.,
- # * 也即3f符號的連結地址。請注意,在連結期間,__proc_info_begin和
- # * __proc_info_end以及.均是連結地址,也即虛執地址。
- # */
- #
- # ldmda r3, {r5 - r7}
- #
- # /* r3為3f的實體地址,而r7為3f的虛擬地址。結果是r3為虛擬地址與實體地址的差值,即PHYS_OFFSET - PAGE_OFFSET。*/
- #
- # sub r3, r3, r7 @ get offset between virt&phys
- #
- # /* r5為__proc_info_begin的實體地址, 即r5指標__proc_info陣列的首地址 */
- #
- # add r5, r5, r3 @ convert v