64M SDRAM 地址是 0x30000000---0x33FFFFFF
------------------------------------------------------------------------------------------------------------------
我們在flash的開始處燒寫了u-boot.bin,這是可執行的二進制文件
注意和ELF可執行性文件是有區別的, 可察看 u-boot.map 參照記憶體位址是否正確.
cpu上電後可以從直接從flash地址0處取指令來執行
開始的代碼在 u-boot\cpu\arm920t\start.S中
這裡需要提一下編譯鏈接時用到的一個很重要的鏈接文件
u-boot/board/smdk2440/u-boot.lds
這個文件給出了代碼中各標號的基地址,和各個段的鏈接順序
ENTRY(_start)
SECTIONS
{
. = 0x00000000;
. = ALIGN(4);
.text :
{
cpu/arm920t/start.o (.text)
*(.text)
}
. = ALIGN(4);
.rodata : { *(.rodata) }
. = ALIGN(4);
.data : { *(.data) }
. = ALIGN(4);
.got : { *(.got) }
armboot_end_data = .;
. = ALIGN(4);
.bss : { *(.bss) }
armboot_end = .;
}
可以看到程序的入口是 _start 標號指示的,而 cpu/arm920t/start.o
則被安排在程序最開始的地方,這個標號就是在start.s中
但是還有一點是需要特別注意的,開始我也是因為這個地方而沒有很好地理解程序
雖然lds中有 . = 0x00000000 這一句,指示鏈接基地址,不過其實這句是不起作用的,
真正的鏈接基地址在 board/samsung/smdk2440/config.mk 中指定的
LDFLAGS += -Bstatic -T $(LDSCRIPT) -Ttext $(TEXT_BASE)
其中-Ttext $(TEXT_BASE)就是指定鏈接地址為TEXT_BASE的值
因而是可變的,TEXT_BASE在 board/samsung/smdk2440/config.mk中定義
TEXT_BASE = 0x33F00000
board/samsung/smdk2440/config.mk 是包括到Makefile中的,
在Makefile中有$(LD) $(LDFLAGS) $(OBJS) $(LIBS) $(LIBS) -Map u-boot.map -o u-boot
所以說起來真正的鏈接地址是0x33F00000,其實這樣在把u-boot拷到Ram中就可以實現無縫跳轉了
.globl _start
_start: b reset
跳到renset
reset: ldr r0, =pWTCON
mov r1, #0x0
str r1, [r0]
..........................
bl cpu_init_crit //bl跳轉會回來
relocate: //下面開始要把u-boot拷到Ram中
adr r0, _start /* r0 <- current position of code */
ldr r2, _armboot_start
ldr r3, _armboot_end
sub r2, r3, r2 /* r2 <- size of armboot */
ldr r1, _TEXT_BASE /* r1 <- destination address */
add r2, r0, r2 /* r2 <- source end address */
以上代碼需要注意的一點是 adr 和 ldr 的區別
adr取得是當前pc相關的偏移地址,在這里程序還是在flash中運行
所以取得地址是以0x0為基址的
而ldr取的是_armboot_start所指的值
.globl _armboot_start
_armboot_start:
.word _start
看到它的值也是_start的地址,不過我們這裡取的是絕對地址,是在鏈接是確定的以
TEXT_BASE為基址的.由於_start的偏移是0,所以r0是0,r2就是TEXT_BASE
copy_loop:
ldmia r0!, {r3-r10}
stmia r1!, {r3-r10}
cmp r0, r2
ble copy_loop
循環copy
ldr r0, _armboot_end /* set up the stack */
add r0, r0, #CONFIG_STACKSIZE
sub sp, r0, #12 /* leave 3 words for abort-stack */
ldr pc, _start_armboot
_start_armboot: .word start_armboot
//通過這一句跳轉到u-boot\lib_arm\board.c中的start_armboot函數去執行了
start_armboot的絕對地址也是以TEXT_BASE為基址的,所以可以順利的實現無縫跳轉了.
接著下來就是一系列初始化的工作了
首先定義了一個全局的數據結構 gd_t gd_data;
DECLARE_GLOBAL_DATA_PTR 這個宏定義的是一個全局的gd_t類型的指針gd
gd = &gd_data;
這樣以後就可以用gd來訪問gd_data這個數據結構了
for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
if ((*init_fnc_ptr)() != 0) {
hang ();
}
}
init_fnc_ptr中是一系列初始化函數的指針
init_fnc_t *init_sequence[] = {
cpu_init, /* basic cpu dependent setup */
board_init, /* basic board dependent setup */
interrupt_init, /* set up exceptions */
env_init, /* initialize environment */
init_baudrate, /* initialze baudrate settings */
serial_init, /* serial communications setup */
display_banner,
dram_init, /* configure available RAM banks */
display_dram_config,
NULL,
};
基本上serial_init後我們就可以用printf函數來打印信息了.
for (;;) {
main_loop ();
}
進入了主循環,在M:\u-boot\common\main.c中
{
char c = 'y';
unsigned long timedata;
printf("start linux now(y/n):");
timedata = 0;
for (;;) {
while (!tstc()) { /* while no incoming data */
if (timedata++ > 3000 * 100 *3)
goto bootm; /* timed out */
}
c = getc();
}
tstc()是測試串口是否有數據輸入,顯然沒有的話就會等待time out跳出
bootm:
if(c == 'y'||c == 'Y'){
strcpy(lastcommand , "bootm 30008000 30800000\r");
flag = 0;
rc = run_command (lastcommand, flag);
if (rc <= 0) {
/* invalid command or not repeatable, forget it */
lastcommand[0] = 0;
}
}
else{
printf("\n\n");
}
}
這樣如果串口沒有輸入或者輸入時y Y 的話,就會去執行bootm 30008000 30800000\r這條命令
否則就會到u-boot的命令行等待輸入.
執行bootm 30008000 30800000\r這條命令會調用u-boot\common\cmd_bootm.c中的
do_bootm函數,具體的命令怎樣被分解,選擇調用函數的機制我就不多說了,追著run_command去就是了
在do_bootm中調用了do_bootm_linux函數,這個函數在u-boot\lib_arm\armlinux.c中
ret = memcpy((void *)0x30008000, (void *)0x40000, 0x100000);
if (ret != (void *)0x30008000)
printf("copy kernel failed\n");
else
printf("copy kernel done\n");
ret = memcpy((void *)0x30800000, (void *)0x140000, 0x440000);
if (ret != (void *)0x30800000)
printf("haha failed\n");
else
printf("copy ramdisk done\n");
首先把kernel和ramdisk都拷到Ram相應的地方去.
setup_linux_param(0x30000000 + LINUX_PARAM_OFFSET); //也在armlinux.c中
#define LINUX_PARAM_OFFSET 0x100
建立要傳給內核的參數,參數的地址都是固定的,所以內核也知道去這裡取參數
參數格式比較複雜,我這裡好像傳得參數不多
void setup_linux_param(ulong param_base)
{
struct param_struct *params = (struct param_struct *)param_base;
...............
}
只是通過一個param_struct的結構體來傳參數的,不過現在一般都用另一種tag標記的傳參方法
一個主要的參數時char linux_cmd[] = "initrd=0x30800000,0x440000 root=/dev/ram init=/linuxrc console=ttyS0";
if (linux_cmd == NULL) {
printf("Wrong magic: could not found linux command line\n");
} else {
memcpy(params->commandline, linux_cmd, strlen(linux_cmd) + 1);
printf("linux command line is: \"%s\"\n", linux_cmd);
}
是比較重要的.移植的時候常常需要修改
接著call_linux(0, 0xc1, 0x30008000); 看出來是準備調到linux去了
0xc1是machine type,這三個參數分別給了r0,r1,r2,這些都是調用內核的約定
void call_linux(long a0, long a1, long a2)
{
__asm__(
"mov r0, %0\n"
"mov r1, %1\n"
"mov r2, %2\n"
"mov ip, #0\n"
"mcr p15, 0, ip, c13, c0, 0\n" /* zero PID */
"mcr p15, 0, ip, c7, c7, 0\n" /* invalidate I,D caches */
"mcr p15, 0, ip, c7, c10, 4\n" /* drain write buffer */
"mcr p15, 0, ip, c8, c7, 0\n" /* invalidate I,D TLBs */
"mrc p15, 0, ip, c1, c0, 0\n" /* get control register */
"bic ip, ip, #0x0001\n" /* disable MMU */
"mcr p15, 0, ip, c1, c0, 0\n" /* write control register */
"mov pc, r2\n"
"nop\n"
"nop\n"
: /* no outpus */
: "r" (a0), "r" (a1), "r" (a2)
);
}
mov pc, r2 就是這句吧,調到了30008000去執行內核了
接下來就到內核了吧
沒有留言:
張貼留言