基于AR9331(MIPS架构)分析系统启动过程(uboot)
前提:
1.AR9331是基于MIPS 24K CPU的一款WIFI1X1芯片,其SDK采用uboot作為引導。AR9331中定義的基地址是:0x9f00,0000
2.MIPS24K芯片,將固定的起始地址,規定為0xBF00,0000(見http://blog.csdn.net/phunxm/article/details/9393021 和http://www.cnblogs.com/xmphoenix/archive/2011/11/02/2233397.html有提到)
此地址屬于MIPS的KSEG1的地址范圍內(見http://blog.csdn.net/phunxm/article/details/9393021),其實際的物理地址是:0x1F00,0000(=0xBF00,0000 & 0x1FFF,FFFF)
?
A.? uboot在編譯時,會經歷如下動作:
bootstrap: depend version $(SUBDIRS) $(OBJS_BOOTSTRAP) $(LIBS_BOOTSTRAP) $(LDSCRIPT_BOOTSTRAP)
?????? UNDEF_SYM=`$(OBJDUMP) -x $(LIBS_BOOTSTRAP) |sed? -n -e 's/.*\(__u_boot_cmd_.*\)/-u\1/p'|sort|uniq`;\
?????? $(LD) $(LDFLAGS_BOOTSTRAP[xxx1]?) $$UNDEF_SYM $(OBJS_BOOTSTRAP) \
?????????? --start-group $(LIBS_BOOTSTRAP) --end-group $(PLATFORM_LIBS) \
?????????? -Map bootstrap.map -o bootstrap
和
u-boot:???????? depend version $(SUBDIRS) $(OBJS) $(LIBS) $(LDSCRIPT)
?????? UNDEF_SYM=`$(OBJDUMP) -x $(LIBS) |sed? -n -e 's/.*\(__u_boot_cmd_.*\)/-u\1/p'|sort|uniq`;\
?????? $(LD) $(LDFLAGS)[xxx2]? $$UNDEF_SYM $(OBJS) $(BOARD_EXTRA_OBJS) \
?????????? --start-group $(LIBS) --end-group $(PLATFORM_LIBS) \
?????????? -Map u-boot.map -o u-boot
以及
u-boot.lzimg: $(obj)u-boot.bin System.map
?????? @$(LZMA) e $(obj)u-boot.bin u-boot.bin.lzma
?????? @./tools/mkimage -A mips -T firmware -C lzma \
?????? -a 0x$(shell grep "T _start" $(TOPDIR)/System.map | awk '{ printf "%s", $$1 }') \
?????? -e 0x$(shell grep "T _start" $(TOPDIR)/System.map | awk '{ printf "%s", $$1 }') \
[xxx3]?????? -n 'u-boot image' -d $(obj)u-boot.bin.lzma $@
也就是說,在編譯的時候,就決定了tuboot需要在0x9f000000處被引導啟動,也就是說,燒寫tuboot時,需要燒到0x9f000000[xxx4]?處。
而0x9F00,0000屬于KSEG0范圍,其實際對應的物理地址也是0x1F00,0000[xxx5]?(=0x9F00,0000&0x7FFF,FFFF)
B.?Uboot編譯連接腳本文件,在ap121上,就是/boot/u-boot/board/ar7240/ap121/u-boot.lds
此文件的作用:
2?連接腳本是用來描述輸出文件的內存布局;源代碼經過編譯器編譯后包含如下段:
n?正文段text:包含程序的指令代碼;
n?數據段data:包含固定的數據,如常量和字符串;
n?未初始化數據段:包含未初始化的變量、數組等。
連接器的任務是將多個編譯后的文件的text、data和bass等段連接在一起;而連接腳本文件就是告訴連接器從什么地址(運行時地址)開始放置這些段
2?此文件中,最要關注的是.text字段。一切從這里開始
C.?先運行bootstrap,然后再運行uboot
在\boot\u-boot\board\ar7240\ap121\u-boot-bootstrap.lds,有定義:ENTRY(_start_bootstrap)
在\boot\u-boot\board\ar7240\ap121\u-boot.lds,有定義:ENTRY(_start)
而在boot\u-boot\board\ar7240\ap121\config.mk,有定義:
# ROM version
ifeq ($(COMPRESSED_UBOOT),1)
TEXT_BASE = 0x80010000 ???#對應uboot的TEXT正文地址,見u-boot.map的_start
BOOTSTRAP_TEXT_BASE = 0x9f000000? #對應bootstrap的TEXT正文地址,見bootstrap.map的_start_bootstrap
[xxx6]?else
TEXT_BASE = 0x9f000000
Endif
所以,先執行_start_bootstrap,再執行_start。那么,這兩個在哪兒?
D.?在bootstrap.map中,可以看到:
.text?????????? 0x000000009f000000???? 0x3aa0
?*(.text)
?.text??? ??0x000000009f000000????? 0x8b0 cpu/mips/start_bootstrap.o
??????????????? 0x000000009f000000??????????????? _start_bootstrap
???? Address of section .text set to 0x9f000000
在u-boot.map中,可以看到:
.text?????????? 0x0000000080010000??? 0x17da0
*(.text)
.text????????? 0x0000000080010000???? 0x3350 cpu/mips/start.o
0x0000000080010030??????????????? relocate_code
0x0000000080010000??????????????? _start
??????? ?? Address of section .text set to 0x80010000
??? ?? 然后,在boot/u-boot/cpu/mips中,可以找到:start_bootstrap.S和start.S
這兩個,就是真正執行_start_boostrap和_start的地方
??? ?? 在__start_bootstrap中,可以看到:bootstrap_board_init_f和bootstrap_board_init_r。最后,在bootstrap_board_init_r中,有:
addr = (char *)(BOOTSTRAP_CFG_MONITOR_BASE + ((ulong)&uboot_end_data_bootstrap - dest_addr));
??? ??? memmove (&header, (char *)addr, sizeof(image_header_t));
??????? 以及:
??????? data = addr + sizeof(image_header_t);/*越過uboot的頭,定位到uboot的凈荷開始*/
??????? fn = ntohl(hdr->ih_load);/*定位位于hdr->ih_load位置的起止程序,并執行之。這個程序就是__start*/
(*fn)(gd->ram_size);
??? 可見,bootstrap中會定位并剝掉uboot的image_header_t的頭,這樣就會調位到__start。
?????? 下面,分析_start
E.?在start.S中,可以看到:board_init_f和board_init_r(boot/u-boot/lib_mips/board.c)。這就是從匯編進入C的兩個入口。先board_init_f,再board_init_r;
最終,在board_init_r中,調用無限循環:
for (;;) {
???? main_loop ();
}
?
F.?main_loop(boot/u-boot/common/main.c)中,最終會調用:run_command (lastcommand, flag);
G.?在run_command(boot/u-boot/common/main.c)中,利用find_cmd找到合適的cmd_tbl_t *cmdtp對象,最后執行((cmdtp->cmd) (cmdtp, flag, argc, argv)
并且,在cmd_bootm.c中,有定義:
U_BOOT_CMD(
???? bootm, CFG_MAXARGS,? 1,? do_bootm,
???? "bootm?? - boot application image from memory\n",
???? "[addr [arg ...]]\n??? - boot application image stored in memory\n"
???? "\tpassing arguments 'arg ...'; when booting a Linux kernel,\n"
???? "\t'arg' can be the address of an initrd image\n"
);
在boot/u-boot/include/command.h中,有定義:
#define U_BOOT_CMD(name,maxargs,rep,cmd,usage,help) \
cmd_tbl_t __u_boot_cmd_##name Struct_Section = {#name, maxargs, rep, cmd, usage}
那么,最初的((cmdtp->cmd) (cmdtp, flag, argc, argv),就會執行到do_bootm
H.?在boot模式下,敲入printenv,可以看到uboot所用到的環境變量的值:
ar7240> printenv
bootargs=console=ttyS0,115200 root=31:02 rootfstype=squashfs init=/sbin/init mtdparts=ar7240-nor0:256k(u-boot),64k(u-boot-env),2752k(rootfs),896k(uImage),64k(NVRAM),64k(ART)[xxx7]?
bootcmd=bootm 0x9f300000[xxx8]?
bootdelay=4[xxx9]?
baudrate=115200
ethaddr=0x00:0xaa:0xbb:0xcc:0xdd:0xee
ipaddr=192.168.1.2[xxx10]?
serverip=192.168.1.10[xxx11]?
stdin=serial
stdout=serial
stderr=serial
ethact=eth0
這些環境變量的定義,是在boot/u-boot/common/environment.c中賦值的;而具體的來源,則大部分在文件boot/u-boot/include/configs/ap121.h中定義;并且這些宏定義,是通過boot/u-boot/common/env_nowhere.c中的env_init引導的。
I.?在編譯內核鏡像時,有如下命令:
/home/xxx/140703_AR9331_Dev/u11_OnlyBasicAndWLAN_AP121-4MB/build/../boot/u-boot/tools/mkimage -A mips -O linux -T kernel -C gzip -a 0x80002000 -e 0x8019bd60 -n Linux Kernel Image -d /home/xxx/140703_AR9331_Dev/u11_OnlyBasicAndWLAN_AP121-4MB/build/../linux/kernels/mips-linux-2.6.31/arch/mips/boot/vmlinux.bin.gz /home/xxx/140703_AR9331_Dev/u11_OnlyBasicAndWLAN_AP121-4MB/build/../images/ap121-2.6.31/vmlinux.gz.uImage[xxx12]?
J.?Uboot啟動內核,是調用cmd_bootm.c中的do_bootm函數,其傳入的命令參數就是:
bootm 0x9f300000[xxx13]?
然后,可以看到如下的啟動信息:
## Booting image at 9f300000 ... [xxx14]?
Image Name:?? Linux Kernel Image
Created:????? 2013-02-06? 22:27:48 UTC
Image Type:?? MIPS Linux Kernel Image (lzma compressed)
Data Size:??? 771996 Bytes = 753.9 kB
Load Address: 80002000 [xxx15]?
Entry Point:? 8019bd60[xxx16]?
Verifying Checksum at 0x9f300040 ...OK
?? Uncompressing Kernel Image ... OK
上述這些信息,都是do_bootm函數中,讀取鏡像文件頭image_header_t信息后得出的
然后,利用gunzip ((void *)ntohl(hdr->ih_load), unc_len, (uchar *)data, &len) != 0),將壓縮的鏡像文件解壓縮到hdr->ih_load[xxx17]?指向的地址。
最后,調用do_bootm_linux? (cmdtp, flag, argc, argv,addr, len_ptr, verify); 開始內核啟動過程
K.?do_bootm_linux(boot/u-boot/lib_mips/mips_linux.c)? :
2?獲得內核鏡像的啟動地址:
theKernel =
???? (void (*)(int, char **, char **, int)) ntohl (hdr->ih_ep);
2?[xxx18]?解析boot_args字段,得到:
linux_params_init (UNCACHED_SDRAM (gd->bd->bi_boot_params), commandline);
2?最后,直接運行內核鏡像的啟動地址:
flash_size_mbytes = gd->bd->bi_flashsize/(1024 * 1024);
theKernel (linux_argc, linux_argv, linux_env, flash_size_mbytes);[xxx19]?
L.?entry: 0x8019bf90地址上的程序,是什么呢?
看一下:linux/kernels/mips-linux-2.6.31/System.map,搜索8019bf9,會發現:
ffffffff8019bf90 T kernel_entry
哈哈,原來該地址上的程序是:kernel_entry
M.?kernel_entry在arch/mips/kernel/head.S中定義;并且最終會跳轉到start_kernel函數,從而進入C代碼
N.?這里就調用了start_kernel(linux/kernels/mips-linux-2.6.31/init/main.c)?
O.?Start_kernel->rest_init->kernel_thread([xxx20]kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);->kernel_init->init_post->run_init_process("/sbin/init"); --> 進入busybox的init流程
?[xxx1]## LDFLAGS_BOOTSTRAP 中,含有-Bstatic -T $(LDSCRIPT_BOOTSTRAP) -Ttext $(BOOTSTRAP_TEXT_BASE) $(PLATFORM_LDFLAGS)
## BOOTSTRAP_TEXT_BASE,在boot\u-boot\board\ar7240\ap121\config.mk中有定義,指明了BOOTSTRAP_TEXT_BASE = 0x9f000000,即bootstrap的報文段會從此地址開始。
## 那么,也就是要求:需要將tuboot放到0x9f000000處。這樣tuboot啟動時,才能找到這里的正確位置
?[xxx2]## LDFLAGS += -Bstatic -T $(LDSCRIPT) -Ttext $(TEXT_BASE) $(PLATFORM_LDFLAGS)
## 其中的$(TEXT_BASE)在boot\u-boot\board\ar7240\ap121\config.mk中有定義,指明了TEXT_BASE = 0x80010000,即uboot的報文段會從此地址開始????????
?[xxx3]?? ????????
-a 0xffffffff80010000 \
-e 0xffffffff80010000 \
?[xxx4]0x9f000000位于KSEG0地址段,其距離KSEG0地址段上限(0x9fffffff)還有0x1000000,即16M的空間。也就是說,設置0x9f000000作為基地址,也就意味著AR9331可以支持最多16MB Flash ---我的猜測,不知道是否正確
?[xxx5]和MIPS24K的固定起始地址是一樣的。這就是為何要規定基地址是0x9F00,0000的緣故
?[xxx6]這是u-boot和bootstrap在內存中的運行域
?[xxx7]由ap121.h中的CONFIG_BOOTARGS定義
表示傳遞給內核的啟動參數
?[xxx8]由ap121.h中的CONFIG_BOOTCOMMAND定義
表示自動啟動時執行的命令
這里的0x9f300000就是linux內核的TEXT_BASE地址;這也正是uboot下的cp.b命令燒寫Linux內核的目的地址
?[xxx9]執行自動啟動的等候秒數
?[xxx10]由ap121.h中的CONFIG_IPADDR定義,表示uboot模式下,單板的IP地址
?[xxx11]由ap121.h中的CONFIG_SERVERIP定義,表示uboot升級時,所認可的TFTP Server的IP地址
?[xxx12]參數說明:
??-a ==> set load address to 'addr' (hex)? -- 表示內核的運行地址
?-e ==> set entry point to 'ep' (hex)。
是入口地址
?[xxx13]這就是在使用uboot燒寫內核文件時,給出的基地址
?[xxx14]對應bootm的參數
?[xxx15]對應編譯時的-a參數
?[xxx16]對應編譯時的-e參數
?[xxx17]就是mkimage –a選項指定的地址
?[xxx18]在ap121上,這個值就是entry: 0x8019bf90
?[xxx19]也就是直接運行位于
entry: 0x8019bf90地址上的程序
?[xxx20]創建內核線程
?
轉載于:https://www.cnblogs.com/lagujw/p/3996576.html
總結
以上是生活随笔為你收集整理的基于AR9331(MIPS架构)分析系统启动过程(uboot)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: vue的form表单在提交成功后置空
- 下一篇: VMware16安装Win10系统