自己動手寫bootloader
爲了寫一個bootloader讓板子跑起來,首先我們要知道bootloader是個什麼東東才行。簡單的說,bootloader就是一個引導內核啓動的一段小代碼,也就是說當啓動完內核之後,它的使命就已經結束。
bootloader生命週期:初始化硬件==》設置啓動參數==》跳到Linux內核的首地址==》消亡。那麼它是怎麼啓動內核的呢或者說在啓動內核之前它究竟完成了哪些工作呢?
寫代碼之前還是先了解下內核啓動的硬性條件:
1.CPU寄存器的設置: r0=0,r1=機器類型ID(linux/arch/arm/tools/mach-types),r2=啓動參數標記列表在RAM中的基地址。
2.CPU工作模式:設置爲管理模式(因爲SUV模式的權限最大),且必須禁止中斷IRQ、FIQ。
3.cache和MMU的設置:MMU必須關閉,數據cache必須關閉,指令cache可以開啓可以關閉,不過建議開啓,因爲可以加快bootloader的運行速度
bootloader的工作分爲兩個階段,由彙編語言和C語言各自掌管:
第一階段完成功能(彙編語言):包括模式設置,關閉看門狗(防止系統定時重啓),關閉中斷,關cache,設置時鐘頻率,初始化硬件(nand flash和sdram),初始化堆棧(因爲要進入到C語言中執行),跳轉到第二階段代碼的C入口點。
第二階段完成功能(C語言):重定位從flash裏把程序本身複製到sdram裏去,清bss段,爲內核設置啓動參數,調用內核。
註釋:bootloader的代碼分爲前4K和4K後,bootloader運行時前4K代碼會由硬件自動的拷貝到sram中,然後前4K代碼需要將bootloader拷貝到sdram中運行(拷貝4K後的代碼還是全部分具體情況而定),因爲nand flash是不能直接運行代碼的。
完成一個簡單的具有基本功能的bootloader:
bic r0 , r0 , #0x1f
orr r0 , r0 , #0xd3
msr cpsr , r0
/*1.2 關閉看門狗*/
ldr r0, =0x53000000
ldr r1, =0
str r1, [r0]
/*1.3 關閉中斷*/
ldr r0 , =0x4a000008
ldr r1 , =0xffffffff
str r1 , [r0]
ldr r0 , =0x4a00001c
ldr r1 , =0x7fff
str r1 , [r0]
/*1.4使能icache 關閉data cache*/
mrc p15, 0, r2, c1, c0, 0
orr r2, r2, #(1<<12)
mcr p15, 0, r2, c1, c0, 0
/*1.6 設置堆棧.判斷是nor啓動還是nand啓動初始化sp指針*/
* 無論是從NOR Flash還是從NAND Flash啓動,
* 地址0處爲指令"b Reset", 機器碼爲0xEA00000B,
* 對於從NAND Flash啓動的情況,其開始4KB的代碼會複製到CPU內部4K內存中,即SRAM
* 對於從NOR Flash啓動的情況,NOR Flash的開始地址即爲0。
* 對於NOR Flash,必須通過一定的命令序列才能寫數據,
* 所以可以根據這點差別來分辨是從NAND Flash還是NOR Flash啓動:
* 向地址0寫入一個數據,然後讀出來,如果沒有改變的話就是Nand Flash
*/
/* NOR啓動時,片內內存從0x40000000開始, sp=0x40000000+4096 */
str r1, [r1] /* 在0地址寫入0 */
ldr r1, [r1] /* 從0地址讀出數據 */
cmp r1, #0
ldreq sp, =4096
ldrne sp, =(0x40000000+4096)
*1.7 時鐘初始化,主要是MPLLCON、CLKDIVN寄存器的設置,特別注意將總線模式改爲異步總線模式*/
bl clock_init
/*1.8. 初始化SDRAM,對於13個寄存器的設置 */
bl sdram_init
/*1.9. 初始化nand*/
bl nand_init
/***************************詳見附件************************************************/
/* 2.1 重定位: 從flash裏把程序本身複製到sdram裏去 */
bl CopyCode2Ram
/* 2.2. 清bss段(初始值爲0、無初始值的全局變量、靜態變量放在BSS段 ),意味着給這些變量開闢空間,因爲bootloader是不給bss段分配空間的*/
ldr r0,= __bss_start
ldr r1, =__bss_end
cmp r0, r1
beq setup_stack
mov r2, #0
clear_loop:
str r2, [r0], #4
cmp r0, r1
bne clear_loop
/* 2.3調用c函數 */
relocate: /* relocate U-Boot to RAM */
mov r0, #0
ldr r1, =__start
ldr r2, =__bss_start
sub r2, r2, r1
/* 前面已經設置sp, 這裏不用再設置 */
{
void (*theKernel)(int zero, int machine_id, int params_addr);
/*初始化串口: 內核啓動時會用到串口輸出一些信息 */
uart_init();
puts("============BOOT for S3C2440=============\n\rCopy kernel for flash to sdram ...\n\r");
/*從NAND FLASH裏把內核讀到SDRAM裏去 */
nand_read(0x30108000, 0x60000, 0x300000);
puts("OK\n\r");
/*設置參數 */
puts("Set parametes ...");
set_params();
puts("OK\n\r");
/*啓動內核 */
puts("Boot kernel ...\n\r");
theKernel = 0x30108040;
theKernel(0, 362, 0x30000100);/*帶三個參數的指向內核首地址的指針*/
puts("\nerror!\n\r");
return 0;
}