Uboot启动过程详解
u-boot系統啟動流程?大多數bootloader都分為stage1和stage2兩部分,u-boot也不例外。
依賴于CPU體系結構的代碼(如設備初始化代碼等)通常都放在stage1且可以用匯編語言來實現,而stage2則通常用C語言來實現,這樣可以實現復雜的功能,而且有更好的可讀性和移植性。
??1、Stage1?start.S代碼結構?u-boot的stage1代碼通常放在start.S文件中,他用匯編語言寫成,其主要代碼部分如下
(1)?定義入口。:?
該工作通過修改連接器腳本來完成。
(2)設置異常向量(Exception?Vector)。?
(3)設置CPU的速度、時鐘頻率及終端控制寄存器。?
(4)初始化內存控制器。?
(5)將ROM中的程序復制到RAM中。?
(6)初始化堆棧。?
(7)轉到RAM中執行,該工作可使用指令ldr?pc來完成。
2、Stage2
?C語言代碼部分?lib_arm/board.c中的start?arm?boot是C語言開始的函數也是整個啟動代碼中C語言的主函數,同時還是整個u-boot(armboot)的主函數,該函數只要完成如下操作:?
(1)調用一系列的初始化函數。?
(2)初始化Flash設備。?
(3)初始化系統內存分配函數。?
(4)如果目標系統擁有NAND設備,則初始化NAND設備。?
(5)如果目標系統有顯示設備,則初始化該類設備。?
(6)初始化相關網絡設備,填寫IP、MAC地址等。?
(7)進去命令循環(即整個boot的工作循環),接受用戶從串口輸入的命令,然后進行相應的工作。?
3、U-Boot的啟動順序
主要順序如下圖所示
?????????????????????????函數順序???????????????初始化順序
?????????????????????????????????圖為?U-Boot順序
下面就根據代碼進行解釋:
/***********************?中斷向量?***********************/
.globl?_start?????????????????????????//u-boot啟動入口
_start:?b???????reset???????????????//復位向量并且跳轉到reset
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?????????????????????//中斷向量
b??sleep_setting?????????????//跳轉到sleep_setting
并通過下段代碼拷貝到內存里
relocate:?????????????????????????????//把uboot重新定位到RAM
adr?r0,?_start??????????????????//?r0?是代碼的當前位置?
ldr?r2,?_armboot_start???????????????//r2?是armboot的開始地址
ldr?r3,?_armboot_end????????????????//r3?是armboot的結束地址
sub?r2,?r3,?r2??????????????????????//?r2得到armboot的大小?
ldr?r1,?_TEXT_BASE????????????//?r1?得到目標地址??
add?r2,?r0,?r2???????????????????????//?r2?得到源結束地址?
copy_loop:?????????????????????????????//重新定位代碼
ldmia?r0!,?{r3-r10}??????????????????//從源地址[r0]中復制
stmia?r1!,?{r3-r10}??????????????????//復制到目標地址[r1]
cmp??r0,?r2????????????????????????//復制數據塊直到源數據末尾地址[r2]
ble?copy_loop
系統上電或reset后,cpu的PC一般都指向0x0地址,在0x0地址上的指令是
reset:?????????????????????????????????//復位啟動子程序
/********?設置CPU為SVC32模式***********/
mrs?r0,cpsr???????????????????????//將CPSR狀態寄存器讀取,保存到R0中
bic?r0,r0,#0x1f
orr?r0,r0,#0xd3
msr?cpsr,r0???
?????????????????????//將R0寫入狀態寄存器中
/**************?關閉看門狗?******************/
ldr??????r0,?=pWTCON
mov?????r1,?#0x0
str???????r1,?[r0]
/**************?關閉所有中斷?*****************/
mov?r1,?#0xffffffff
ldr?r0,?=INTMSK
str?r1,?[r0]
ldr?r2,?=0x7ff
ldr?r0,?=INTSUBMSK
str?r2,?[r0]
/**************?初始化系統時鐘?*****************/
ldr?r0,?=LOCKTIME
ldr?????r1,?=0xffffff?
str?????r1,?[r0]
clear_bss:
????????ldr???????r0,?_bss_start???????????//找到bss的起始地址?
????????add??????r0,?r0,?#4??????????????//從bss的第一個字開始?
????????ldr???????r1,?_bss_end???????????//?bss末尾地址?
????????mov??????r2,?#0x00000000???????//清零??
clbss_l:str????????r2,?[r0]????????????????//?bss段空間地址清零循環?
????????add?????r0,?r0,?#4
????????cmp?????r0,?r1
????????bne??????clbss_l
/*****************?關鍵的初始化子程序?************************/
/?*?cpu初始化關鍵寄存器
*?設置重要寄存器
*?設置內存時鐘
*?/
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
/*******?在重新定位前,我們要設置RAM的時間,因為內存時鐘依賴開發板硬件的,你將會找到board目錄底下的memsetup.S。**************/
mov?ip,?lr
#ifndef?CONFIG_S3C2440A_JTAG_BOOT
bl?memsetup????????//調用memsetup子程序(在board/smdk2442memsetup.S)
#endif
mov?lr,?ip
mov?pc,?lr????????????????????????//子程序返回
memsetup:???
/****************?初始化內存?**************/
????????mov?????r1,?#MEM_CTL_BASE
????????adrl????r2,?mem_cfg_val
????????add?????r3,?r1,?#52
1:???????ldr?????r4,?[r2],?#4
????????str?????r4,?[r1],?#4
????????cmp?????r1,?r3
????????bne?????1b
/***********?跳轉到原來進來的下一個指令(start.S文件里)?***************/??
mov?????pc,?lr?????????????????//子程序返回
/******************?建立堆棧?*******************/
ldr?r0,?_armboot_end???????????????//armboot_end重定位
add?r0,?r0,?#CONFIG_STACKSIZE????//向下配置堆棧空間
sub?sp,?r0,?#12??????????????????//為abort-stack預留個3字
/****************?跳轉到C代碼去?**************/
ldr?pc,?_start_armboot???????????//跳轉到start_armboot函數入口,start_armboot
字保存函數入口指針
_start_armboot:?.word?start_armboot????//start_armboot函數在lib_arm/board.c中實現
從此進入第二階段C語言代碼部分
/****************?異常處理程序?*******************/
.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
.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
sleep_setting:???????????????????????????//休眠設置
@?prepare?the?SDRAM?self-refresh?mode
ldr?r0,?=0x48000024?@?REFRESH?Register
ldr?r1,?[r0]
orr?r1,?r1,#(1bd?=?&bd_data;
memset?(gd->bd,?0,?sizeof?(bd_t));
monitor_flash_len?=?_armboot_end_data?-?_armboot_start;
/***?調用執行init_sequence數組按順序執行初始化?***/
for?(init_fnc_ptr?=?init_sequence;?*init_fnc_ptr;?++init_fnc_ptr)
{
if?((*init_fnc_ptr)()?!=?0)
??{
??hang?();
}
}
#if?0
/****************?配置可用的flash單元?*************/
size?=?flash_init?();?????????????//初始化flash
display_flash_config?(size);??????//顯示flash的大小
/********?_arm_boot在armboot.lds鏈接腳本中定義?********/
#endif
#ifdef?CONFIG_VFD
#??ifndef?PAGE_SIZE
#??define?PAGE_SIZE?4096
#??endif
/***********?為VFD顯示預留內存(整個頁面)??**********/
/********?armboot_real_end在board-specific鏈接腳本中定義********/
addr?=?(_armboot_real_end?+?(PAGE_SIZE?-?1))?&?~(PAGE_SIZE?-?1);
size?=?vfd_setmem?(addr);
gd->fb_base?=?addr;
/*******?進入下一個界面?********/
addr?+=?size;
addr?=?(addr?+?(PAGE_SIZE?-?1))?&?~(PAGE_SIZE?-?1);
mem_malloc_init?(addr);
#else
/********??armboot_real_end?在board-specific鏈接腳本中定義?*******/
mem_malloc_init?(_armboot_real_end);
#endif????/*?CONFIG_VFD?*/
#if?(CONFIG_COMMANDS?&?CFG_CMD_NAND)
puts?("NAND:");
nand_init();??/*?NAND初始化?*/
#endif
#ifdef?CONFIG_HAS_DATAFLASH
AT91F_DataflashInit();
dataflash_print_info();
#endif
/*********?初始化環境?**********/
env_relocate?();
/***********?配置環境變量,重新定位?**********/
#ifdef?CONFIG_VFD
/*?must?do?this?after?the?framebuffer?is?allocated?*/
drv_vfd_init();
#endif
/*?從環境中得到IP地址?*/
bd_data.bi_ip_addr?=?getenv_IPaddr?("ipaddr");
/*以太網接口MAC地址*/
{
??int?i;
??ulong?reg;
??char?*s,?*e;
??uchar?tmp[64];
??i?=?getenv_r?("ethaddr",?tmp,?sizeof?(tmp));
??s?=?(i?>?0)???tmp?:?NULL;
??for?(reg?=?0;?reg?bd->bi_enetaddr);
#endif
#ifdef?CONFIG_DRIVER_LAN91C96
if?(getenv?("ethaddr"))?{
??smc_set_mac_addr(gd->bd->bi_enetaddr);
}
/*?eth_hw_init();?*/
#endif?/*?CONFIG_DRIVER_LAN91C96?*/
/*?通過環境變量初始化*/
if?((s?=?getenv?("loadaddr"))?!=?NULL)?{
??load_addr?=?simple_strtoul?(s,?NULL,?16);
}?
#if?(CONFIG_COMMANDS?&?CFG_CMD_NET)
if?((s?=?getenv?("bootfile"))?!=?NULL)?{
??copy_filename?(BootFile,?s,?sizeof?(BootFile));
}
#endif?/*?CFG_CMD_NET?*/
#ifdef?BOARD_POST_INIT
board_post_init?();
#endif
/*?main_loop()?總是試圖自動啟動,循環不斷執行*/
for?(;;)?{
??main_loop?();?/*主循環函數處理執行用戶命令—common/main.c
}
/*?NOTREACHED?-?no?way?out?of?command?loop?except?booting?*/
}
轉自:?http://blog.chinaunix.net/uid-24951403-id-2212589.html
總結
以上是生活随笔為你收集整理的Uboot启动过程详解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 2022年企业微信生态研究报告
- 下一篇: uboot 详细注释讲解