長篇連載--arm linux演藝---序
滾滾長江東四水,浪花淘凈英雄。
大家好,許多人和我一樣,正在苦讀linux源代碼,希望有照一日,寶典在手,天下我有。小弟不才,也讀了兩年,寫的幾首歪詩。從本級開始,把我所理解的linux如何啟動貼出來,不懂之處大家討論一番。也希望把linux從頭到尾討論一遍,計劃寫它240回,三年寫完(笑。。。),歡迎大家動員一些牛人來參與討論,提高人氣,增加流量。
小弟用的是arm920T,跑LINUX 2。4。18,下面是第一回。。。。。
長篇連載--arm linux演藝---第一回
--------------------------------------------------------------------------------
話說。。。(噓聲,“入正題把!“)
好好:
首先,porting linux的時候要規劃內存影像,如小弟的系統有64m SDRAM,
地址從0x 0800 0000 -0x0bff ffff,32m Flash,地址從0x0c00 0000-0x0dff ffff.
規劃如下:bootloader, linux kernel, rootdisk放在flash里。
具體從 0x0c00 0000開始的第一個1M放bootloader,
0x0c10 0000開始的2m放linux kernel,從 0x0c30 0000開始都給rootdisk。
啟動:
首先,啟動后arm920T將地址0x0c00 0000映射到0(可通過跳線設置),
實際上從0x0c00 0000啟動,進入我們的bootloader,但由于flash速度慢,
所以bootloader前面有一小段程序把bootloader拷貝到SDRAM 中的0x0AFE0100,
再從0x 0800 0000 運行bootloader,我們叫這段小程序為flashloader,
flashloader必須要首先初始化SDRAM,不然往那放那些東東:
.equ SOURCE, 0x0C000100 bootloader的存放地址
.equ TARGET, 0x0AFE0100 目標地址
.equ SDCTL0, 0x221000 SDRAM控制器寄存器
// size is stored in location 0x0C0000FC
.global _start
_start: //入口點
//;***************************************
//;* Init SDRAM
//;***************************************
// ;***************
// ;* SDRAM
// ;***************
LDR r1, =SDCTL0 //
// ; Set PRecharge Command
LDR r3, =0x92120200
//ldr r3,=0x92120251
STR r3, [r1]
// ; Issue Precharge All Commad
LDR r3, =0x8200000
LDR r2, [r3]
// ; Set AutoRefresh Command
LDR r3, =0xA2120200
STR r3, [r1]
// ; Issue AutoRefresh Command
LDR r3, =0x8000000
LDR r2, [r3]
LDR r2, [r3]
LDR r2, [r3]
LDR r2, [r3]
LDR r2, [r3]
LDR r2, [r3]
LDR r2, [r3]
LDR r2, [r3]
// ; Set Mode Register
LDR r3, =0xB2120200
STR r3, [r1]
// ; Issue Mode Register Command
LDR r3, =0x08111800 //; Mode Register Value
LDR r2, [r3]
// ; Set Normal Mode
LDR r3, =0x82124200
STR r3, [r1]
//;***************************************
//;* End of SDRAM and SyncFlash Init *
//;***************************************
// copy code from FLASH to SRAM
_CopyCodes:
ldr r0,=SOURCE
ldr r1,=TARGET
sub r3,r0,#4
ldr r2,[r3]
_CopyLoop:
ldr r3,[r0]
str r3,[r1]
add r0,r0,#4
add r1,r1,#4
sub r2,r2,#4
teq r2,#0
beq _EndCopy
b _CopyLoop
_EndCopy:
ldr r0,=TARGET
mov pc,r0
欲知后事如何,下回分解:
長篇連載--arm linux演藝---第二回
--------------------------------------------------------------------------------
上回書說到flashloader把bootloader load到0x0AFE0100, 然回跳了過去,
其實0x0AFE0100 就是燒在flash 0x0C000100中的真正的bootloader:
bootloader 有幾個文件組成,先是START.s,也是唯一的一個匯編程序,其余的都是C寫成的,START.s主要初始化堆棧:
_start:
ldr r1,=StackInit
ldr sp,[r1]
b main
//此處我們跳到了C代碼的main函數,當C代碼執行完后,還要調用
//下面的JumpToKernel0x跳到LINXU kernel運行
.equ StackInitValue, __end_data+0x1000 // 4K __end_data在連結腳本中指定
StackInit:
.long StackInitValue
.global JumpToKernel
JumpToKernel:
// jump to the copy code (get the arguments right)
mov pc, r0
.global JumpToKernel0x
// r0 = jump address
// r1-r4 = arguments to use (these get shifted)
JumpToKernel0x:
// jump to the copy code (get the arguments right)
mov r8, r0
mov r0, r1
mov r1, r2
mov r2, r3
mov r3, r4
mov pc, r8
.section ".data.boot"
.section ".bss.boot"
欲知bootloader中的c代碼如何運行,請看下集
長篇連載--arm linux演藝---第三回
--------------------------------------------------------------------------------
書接上回:
下面讓我們看看bootloader的c代碼干了些什么。
main函數比較長,讓我們分段慢慢看。
int main()
{
U32 *pSource, *pDestin, count;
U8 countDown, bootOption;
U32 delayCount;
U32 fileSize, i;
char c;
char *pCmdLine;
char *pMem;
init(); //初始化FLASH控制器和CPU時鐘
EUARTinit(); //串口初始化
EUARTputString("/n/nDBMX1 Linux Bootloader ver 0.2.0/n");
EUARTputString("Copyright (C) 2002 Motorola Ltd./n/n");
EUARTputString((U8 *)cmdLine);
EUARTputString("/n/n");
EUARTputString("Press any key for alternate boot-up options ... ");
小弟的bootloader主要干這么幾件事:init(); 初始化硬件,打印一些信息和提供一些操作選項:
0. Program bootloader image
1. Program kernel image
2. Program root-disk image
3. Download kernel and boot from RAM
4. Download kernel and boot with ver 0.1.x bootloader format
5. Boot a ver0.1.x kernel
6. Boot with a different command line
也就是說,可以在bootloader里選擇重新下載kernel,rootdisk并寫入flash,
下載的方法是用usb連接,10m的rootdisk也就刷的一下。關于usb下載的討論請參看先前的貼子“為arm開發平臺增加usb下載接口“。
假如不選,直接回車,就開始把整個linux的內核拷貝到SDRAM中運行。
列位看官,可能有人要問,在flashloader中不是已經初始化過sdram控制器了嗎?怎么init(); 中還要初始化呢,各位有所不知,小弟用的是syncflash,
可以直接使用sdram控制器的接口,切記:在flash中運行的代碼是不能初始化連接flash的sdram控制器的,不然絕對死掉了。所以,當程序在flash中運行的時候,去初始化sdram,而現在在sdram中運行,可放心大膽地初始化flash了,主要是設定字寬,行列延時,因為缺省都是最大的。
另外,假如列位看官的cpu有足夠的片內ram,完全可以先把bootloader放在片內ram,干完一切后再跳到LINUX,小弟著也是不得已而為之啊。
今天太晚了,回去睡覺了。。。
長篇連載--arm linux演藝---第四回
--------------------------------------------------------------------------------
假如直接輸入回車,進入kernel拷貝工作:
EUARTputString("Copying kernel from Flash to RAM .../n");
count = 0x200000; // 2 Mbytes
pSource = (U32 *)0x0C100000;
pDestin = (U32 *)0x08008000;
do
{
*(pDestin++) = *(pSource++);
count -= 4;
} while (count > 0);
}
EUARTputString("Booting kernel .../n/n");
這一段沒有什么可說的,運行完后kernel就在0x08008000了,至于為什么要
空出0x8000的一段,主要是放kelnel的一些全局數據結構,如內核頁表,arm的頁目錄要有16k大。
我們知道,linux內核啟動的時候可以傳入參數,如在PC上,假如使用LILO,
當出現LILO:,我們可以輸入root=/dev/hda1.或mem=128M等指定文件系統的設備或內存大小,在嵌入式系統上,參數的傳入是要靠bootloader完成的,
pMem = (char *)0x083FF000; //參數字符串的目標存放地址
pCmdLine = (char *)&cmdLine; //定義的靜態字符串
while ((*(pMem++)=*(pCm