首先,porting linux的時(shí)候要規(guī)劃內(nèi)存影像,如小弟的系統(tǒng)有64m SDRAM, 地址從0x 0800 0000 -0x0bff ffff,32m flash,地址從0x0c00 0000-0x0dff ffff. 規(guī)劃如下:bootloader, linux kernel, rootdisk放在flash里。 具體從 0x0c00 0000開始的第一個(gè)1M放bootloader, 0x0c10 0000開始的2m放linux kernel,從 0x0c30 0000開始都給rootdisk。
啟動(dòng): 首先,啟動(dòng)后arm920T將地址0x0c00 0000映射到0(可通過(guò)跳線設(shè)置), 實(shí)際上從0x0c00 0000啟動(dòng),進(jìn)入我們的bootloader,但由于flash速度慢, 所以bootloader前面有一小段程序把bootloader拷貝到SDRAM 中的0x0AFE0100, 再?gòu)?x 0800 0000 運(yùn)行bootloader,我們叫這段小程序?yàn)閒lashloader, flashloader必須要首先初始化SDRAM,不然往那放那些東東:
.equ SOURCE, 0x0C000100 bootloader的存放地址 .equ TARGET, 0x0AFE0100 目標(biāo)地址 .equ SDCTL0, 0x221000 SDRAM控制器寄存器 // size is stored in location 0x0C0000FC
.global _start _start: //入口點(diǎn)
//;*************************************** //;* 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
上回書說(shuō)到flashloader把bootloader load到0x0AFE0100, 然回跳了過(guò)去, 其實(shí)0x0AFE0100 就是燒在flash 0x0C000100中的真正的bootloader:
bootloader 有幾個(gè)文件組成,先是START.s,也是唯一的一個(gè)匯編程序,其余的都是C寫成的,START.s主要初始化堆棧:
_start: ldr r1,=StackInit ldr sp,[r1] b main
//此處我們跳到了C代碼的main函數(shù),當(dāng)C代碼執(zhí)行完后,還要調(diào)用 //下面的JumpToKernel0x跳到LINXU kernel運(yùn)行
.equ StackInitvalue, __end_data+0x1000 // 4K __end_data在連結(jié)腳本中指定
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代碼干了些什么。main函數(shù)比較長(zhǎng),讓我們分段慢慢看。
int main() { U32 *pSource, *pDestin, count; U8 countDown, bootOption; U32 delayCount; U32 fileSize, i; char c; char *pCmdLine; char *pMem;
init(); //初始化FLASH控制器和CPU時(shí)鐘
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(); 初始化硬件,打印一些信息和提供一些操作選項(xiàng): 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
也就是說(shuō),可以在bootloader里選擇重新下載kernel,rootdisk并寫入flash, 下載的方法是用usb連接,10m的rootdisk也就刷的一下。關(guān)于usb下載的討論請(qǐng)參看先前的貼子“為arm開發(fā)平臺(tái)增加usb下載接口“。 如果不選,直接回車,就開始把整個(gè)linux的內(nèi)核拷貝到SDRAM中運(yùn)行。
列位看官,可能有人要問(wèn),在flashloader中不是已經(jīng)初始化過(guò)sdram控制器了嗎?怎么init(); 中還要初始化呢,各位有所不知,小弟用的是syncflash, 可以直接使用sdram控制器的接口,切記:在flash中運(yùn)行的代碼是不能初始化連接flash的sdram控制器的,不然絕對(duì)死掉了。所以,當(dāng)程序在flash中運(yùn)行的時(shí)候,去初始化sdram,而現(xiàn)在在sdram中運(yùn)行,可放心大膽地初始化flash了,主要是設(shè)定字寬,行列延時(shí),因?yàn)槿笔《际亲畲蟮摹?nbsp;
另外,如果列位看官的cpu有足夠的片內(nèi)ram,完全可以先把bootloader放在片內(nèi)ram,干完一切后再跳到LINUX,小弟著也是不得已而為之啊。
如果直接輸入回車,進(jìn)入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");
這一段沒有什么可說(shuō)的,運(yùn)行完后kernel就在0x08008000了,至于為什么要 空出0x8000的一段,主要是放kelnel的一些全局?jǐn)?shù)據(jù)結(jié)構(gòu),如內(nèi)核頁(yè)表,arm的頁(yè)目錄要有16k大。
我們知道,linux內(nèi)核啟動(dòng)的時(shí)候可以傳入?yún)?shù),如在PC上,如果使用LILO, 當(dāng)出現(xiàn)LILO:,我們可以輸入root=/dev/hda1.或mem=128M等指定文件系統(tǒng)的設(shè)備或內(nèi)存大小,在嵌入式系統(tǒng)上,參數(shù)的傳入是要靠bootloader完成的,
pMem = (char *)0x083FF000; //參數(shù)字符串的目標(biāo)存放地址 pCmdLine = (char *)&cmdLine; //定義的靜態(tài)字符串 while ((*(pMem++)=*(pCmdLine++)) != 0);//拷貝
JumpToKernel((void *)0x8008000, 0x083FF000) //跳轉(zhuǎn)到內(nèi)核
return (0); JumpToKernel在前文中的start.S定義過(guò):
JumpToKernel: // jump to the copy code
|