国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學(xué)院 > 開發(fā)設(shè)計 > 正文

Linux BOOTLOADER全程詳解(Arm S3C2410)

2019-11-17 04:36:32
字體:
供稿:網(wǎng)友
網(wǎng)上關(guān)于linux的BOOTLOADER文章不少了,但是大都是vivi,blob等比較龐大的程序,讀起來不太方便,編譯出的文件也比較大,而且更多的是面向開發(fā)用的引導(dǎo)代碼,做成產(chǎn)品時還要裁減,這一定程度影響了開發(fā)速度,對初學(xué)者學(xué)習(xí)開銷也比較大,在此分析一種簡單的BOOTLOADER,是在三星公司提供的2410 BOOTLOADER上稍微修改后的結(jié)果,編譯出來的文件大小不超過4k,希望對大家有所幫助. 1.幾個重要的概念 COMPRESSED KERNEL and DECOMPRESSED KERNEL 壓縮后的KERNEL,按照文檔資料,現(xiàn)在不提倡使用DECOMPRESSED KERNEL,而要使用COMPRESSED KERNEL,它包括了解壓器.因此要在ram分配時給壓縮和解壓的KERNEL提供足夠空間,這樣它們不會相互覆蓋. 當(dāng)執(zhí)行指令跳轉(zhuǎn)到COMPRESSED KERNEL后,解壓器就開始工作,假如解壓器探測到解壓的代碼會覆蓋掉COMPRESSED KERNEL,那它會直接跳到COMPRESSED KERNEL后存放數(shù)據(jù),并且重新定位KERNEL,所以假如沒有足夠空間,就會出錯. Jffs2 File System 可以使armlinux應(yīng)用中產(chǎn)生的數(shù)據(jù)保存在Flash上,我的板子還沒用到這個. RAMDISK 使用RAMDISK可以使ROOT FILE SYSTEM在沒有其他設(shè)備的情況下啟動.一般有兩種加載方式,我就介紹最常用的吧,把COMPRESSED RAMDISK IMAGE放到指定地址,然后由BOOTLOADER把這個地址通過啟動參數(shù)的方式ATAG_INITRD2傳遞給KERNEL.具體看代碼分析. 啟動參數(shù)(摘自IBM developer) 在調(diào)用內(nèi)核之前,應(yīng)該作一步預(yù)備工作,即:設(shè)置 Linux 內(nèi)核的啟動參數(shù)。Linux 2.4.x 以后的內(nèi)核都期望以標(biāo)記列表(tagged list)的形式來傳遞啟動參數(shù)。啟動參數(shù)標(biāo)記列表以標(biāo)記 ATAG_CORE 開始,以標(biāo)記 ATAG_NONE 結(jié)束。每個標(biāo)記由標(biāo)識被傳遞參數(shù)的 tag_header 結(jié)構(gòu)以及隨后的參數(shù)值數(shù)據(jù)結(jié)構(gòu)來組成。數(shù)據(jù)結(jié)構(gòu) tag 和 tag_header 定義在 Linux 內(nèi)核源碼的include/asm/setup.h 頭文件中. 在嵌入式 Linux 系統(tǒng)中,通常需要由 BOOTLOADER 設(shè)置的常見啟動參數(shù)有:ATAG_CORE、ATAG_MEM、ATAG_CMDLINE、ATAG_RAMDISK、ATAG_INITRD等。 (注)參數(shù)也可以用COMMANDLINE來設(shè)定,在我的BOOTLOADER里,我兩種都用了. 2.開發(fā)環(huán)境和開發(fā)板配置: CPU:S3C2410,BANK6上有64M的SDRAM(兩塊),BANK0上有32M NOR FLASH,串口當(dāng)然是逃不掉的.這樣,按照數(shù)據(jù)手冊,地址分配如下: 0x4000_0000開始是4k的片內(nèi)DRAM. 0x0000_0000開始是32M FLASH 16bit寬度 0x3000_0000開始是64M SDRAM 32bit寬度 注重:控制寄存器中的BANK6和BANK7部分必須相同. 0x4000_0000(片內(nèi)DRAM)存放4k以內(nèi)的BOOTLOADER IMAGE 0x3000_0100開始存放啟動參數(shù) 0x3120_0000 存放COMPRESSED KERNEL IMAGE 0x3200_0000 存放COMPRESSED RAMDISK 0x3000_8000 指定為DECOMPRESSED KERNEL IMAGE ADDRESS 0x3040_0000 指定為DECOMPRESSED RAMDISK IMAGE ADDRESS 開發(fā)環(huán)境:Redhat Linux,armgcc toolchain, armlinux KERNEL 如何建立armgcc的編譯環(huán)境:建議使用toolchain,而不要自己去編譯armgcc,偶試過好多次,都以失敗告終. 先下載arm-gcc 3.3.2 toolchain 將arm-linux-gcc-3.3.2.tar.bz2 解壓到 /toolchain # tar jxvf arm-linux-gcc-3.3.2.tar.bz2 # mv /usr/local/arm/3.3.2 /toolchain 在makefile 中在把arch=arm CROSS_COMPILE設(shè)置成toolchain的路徑 還有就是INCLUDE = -I ../include -I /root/my/usr/local/arm/3.3.2/include.,否則庫函數(shù)就不能用了 3.啟動方式: 可以放在FLASH里啟動,或者用Jtag仿真器.由于使用NOR FLASH,根據(jù)2410的手冊,片內(nèi)的4K DRAM在不需要設(shè)置便可以直接使用,而其他存儲器必須先初始化,比如告訴memory controller,BANK6里有兩塊SDRAM,數(shù)據(jù)寬度是32bit,= =.否則memory control會按照復(fù)位后的默認(rèn)值來處理存儲器.這樣讀寫就會產(chǎn)生錯誤. 所以第一步,通過仿真器把執(zhí)行代碼放到0x4000_0000,(在編譯的時候,設(shè)定TEXT_BAS E=0x40000000) 第二步,通過 AxD把linux KERNEL IMAGE放到目標(biāo)地址(SDRAM)中,等待調(diào)用 第三步,執(zhí)行BOOTLOADER代碼,從串口得到調(diào)試數(shù)據(jù),引導(dǎo)armlinux
4.代碼分析 講了那么多執(zhí)行的步驟,是想讓大家對啟動有個大概印象,接著就是BOOTLOADER內(nèi)部的代碼分析了,BOOTLOADER文章內(nèi)容網(wǎng)上很多,我這里精簡了下,刪除了不必要的功能. BOOTLOADER一般分為2部分,匯編部分和c語言部分,匯編部分執(zhí)行簡單的硬件初始化,C部分負(fù)責(zé)復(fù)制數(shù)據(jù),設(shè)置啟動參數(shù),串口通信等功能. BOOTLOADER的生命周期: 1. 初始化硬件,比如設(shè)置UART(至少設(shè)置一個),檢測存儲器= =. 2. 設(shè)置啟動參數(shù),這是為了告訴內(nèi)核硬件的信息,比如用哪個啟動界面,波特率 = =. 3. 跳轉(zhuǎn)到Linux KERNEL的首地址. 4. 消亡  當(dāng)然,在引導(dǎo)階段,象vivi等,都用虛地址,假如你嫌煩的話,就用實地址,都一樣. 我們來看代碼: 2410init.s .global _start//開始執(zhí)行處 _start: //下面是中斷向量 b reset @ Supervisor Mode//重新啟動后的跳轉(zhuǎn) …… …… reset: ldr r0,=WTCON /WTCON地址為53000000,watchdog的控制寄存器 */ ldr r1,=0x0 /*關(guān)watchdog*/ str r1,[r0]  ldr r0,=INTMSK ldr r1,=0xffffffff /*屏蔽所有中斷*/ str r1,[r0]  ldr r0,=INTSUBMSK ldr r1,=0x3ff /*子中斷也一樣*/ str r1,[r0] /*Initialize Ports...for display LED.*/ ldr r0, =GPFCON ldr r1, =0x55aa str r1, [r0] ldr r0, =GPFUP ldr r1, =0xff str r1, [r0] ldr r0,=GPFDAT ldr r1,=POWEROFFLED1 str r1,[r0] /* Setup clock Divider control register * you must configure CLKDIVN before LOCKTIME or MPLL UPLL * because default CLKDIVN 1,1,1 set the SDMRAM Timing Conflict nop * FCLK:HCLK:PCLK = 1:2:4 in this case */ ldr r0,=CLKDIVN ldr r1,=0x3 str r1,[r0]  /*To redUCe PLL lock time, adjust the LOCKTIME register. */ ldr r0,=LOCKTIME ldr r1,=0xffffff str r1,[r0] /*Configure MPLL */ ldr r0,=MPLLCON ldr r1,=((M_MDIV<<12)+(M_PDIV<<4)+M_SDIV) //Fin=12MHz,Fout=203MHz str r1,[r0] ldr r1,=GSTATUS2 ldr r10,[r1] tst r10,#OFFRST bne 1000f //以上這段,我沒動,就用三星寫的了,下面是主要要改的地方 /* MEMORY C0NTROLLER(MC)設(shè)置*/ add r0,pc,#MCDATA - (.+8)// r0指向MCDATA地址,那里存放著MC初始化要用到的數(shù)據(jù) ldr r1,=BWSCON // r1指向MC控制器寄存器的首地址 add r2,r0,#52 // 復(fù)制次數(shù),偏移52字  1: //按照偏移量進(jìn)行循環(huán)復(fù)制 ldr r3,[r0],#4 str r3,[r1],#4 cmp r2,r0 bne 1b .align 2  MCDATA: .Word (0+(B1_BWSCON<<4)+(B2_BWSCON<<8)+(B3_BWSCON<<12)+(B4_BWSCON<<16)+(B5_BWSCON<<20)+(B6_BWSCON<<24)+(B7_BWSCON<<28)) 上面這行就是BWSCON的數(shù)據(jù),具體參數(shù)意義如下:  需要更改設(shè)置DW6 和DW7都設(shè)置成10,即32bit,DW0 設(shè)置成01,即16bit 下面都是每個BANK的控制器數(shù)據(jù),大都是時鐘相關(guān),可以用默認(rèn)值,設(shè)置完MC后,就跳到調(diào)用main函數(shù)的部分 .word ((B0_Tacs<<13)+(B0_Tcos<<11)+(B0_Tacc<<8)+(B0_Tcoh<<6)+(B0_Tah<<4)+(B0_Tacp<<2)+(B0_PMC)) .word ((B1_Tacs<<13)+(B1_Tcos<<11)+(B1_Tacc<<8)+(B1_Tcoh<<6)+(B1_Tah<<4)+(B1_Tacp<<2)+(B1_PMC))
.word ((B2_Tacs<<13)+(B2_Tcos<<11)+(B2_Tacc<<8)+(B2_Tcoh<<6)+(B2_Tah<<4)+(B2_Tacp<<2)+(B2_PMC)) .word ((B3_Tacs<<13)+(B3_Tcos<<11)+(B3_Tacc<<8)+(B3_Tcoh<<6)+(B3_Tah<<4)+(B3_Tacp<<2)+(B3_PMC)) .word ((B4_Tacs<<13)+(B4_Tcos<<11)+(B4_Tacc<<8)+(B4_Tcoh<<6)+(B4_Tah<<4)+(B4_Tacp<<2)+(B4_PMC)) .word ((B5_Tacs<<13)+(B5_Tcos<<11)+(B5_Tacc<<8)+(B5_Tcoh<<6)+(B5_Tah<<4)+(B5_Tacp<<2)+(B5_PMC)) .word ((B6_MT<<15)+(B6_Trcd<<2)+(B6_SCAN)) .word ((B7_MT<<15)+(B7_Trcd<<2)+(B7_SCAN)) .word ((REFEN<<23)+(TREFMD<<22)+(Trp<<20)+(Trc<<18)+(Tchr<<16)+REFCNT) .word 0xB2 /* REFRESH Control Register */ .word 0x30 /* BANKSIZE Register : Burst Mode */ .word 0x30 /* SDRAM Mode Register */  .align 2 .global call_main //調(diào)用main函數(shù),函數(shù)參數(shù)都為0 call_main: ldr sp,STACK_START mov fp,#0 /* no previous frame, so fp=0*/ mov a1, #0 /* set argc to 0*/ mov a2, #0 /* set argv to NUL*/ bl main /* call main*/ STACK_START: .word STACK_BASE undefined_instruction: software_interrupt: prefetch_abort: data_abort: not_used: irq: fiq: /*以上是主要的匯編部分,實現(xiàn)了時鐘設(shè)置,串口設(shè)置watchdog關(guān)閉,中斷關(guān)閉功能(假如有需要還可以降頻使用),然后轉(zhuǎn)入main*/ 2410init.c file int main(int argc,char **argv) { u32 test = 0; void (*theKERNEL)(int zero, int arch, unsigned long params_addr) = (void (*)(int, int, unsigned long))RAM_COMPRESSED_KERNEL_BASE; //壓縮后的IMAGE地址 int i,k=0; // downPt=(RAM_COMPRESSED_KERNEL_BASE); chkBs=(_RAM_STARTADDRESS);//SDRAM開始的地方 // fromPt=(FLASH_LINUXKERNEL); MMU_EnableICache(); ChangeClockDivider(1,1); // 1:2:4 ChangeMPllvalue(M_MDIV,M_PDIV,M_SDIV); //Fin=12MHz FCLK=200MHz Port_Init();//設(shè)置I/O端口,在使用com口前,必須調(diào)用這個函數(shù),否則通信芯片根本得不到數(shù)據(jù) Uart_Init(PCLK, 115200);//PCLK使用默認(rèn)的200000,撥特率115200 /*******************(檢查ram空間)*******************/ Uart_SendString("/n/tLinux S3C2410 Nor BOOTLOADER/n"); Uart_SendString("/n/tChecking SDRAM 2410loader.c.../n"); for(;chkBs<0x33FA0140;chkBs=chkBs+0x4,test++)//  //根據(jù)我的經(jīng)驗,最好以一個字節(jié)為遞增,我們的板子,在256byte遞增檢測的時候是沒問題的,但是 //以1byte遞增就出錯了,第13跟數(shù)據(jù)線隨幾的會冒”1”,檢測出來是硬件問題,現(xiàn)象如下 //用仿真器下代碼測試SDRAM,開始沒貼28F128A3J FLASH片子,測試結(jié)果很好,但在上了FLASH片子//之后,測試數(shù)據(jù)(data)為0x00000400連續(xù)成批寫入讀出時,操作大約1k左右內(nèi)存空間就會出錯,//而且隨機(jī)。那個出錯數(shù)據(jù)總是變?yōu)?x00002400,數(shù)據(jù)總線10位和13位又沒短路發(fā)生。用其他數(shù)據(jù)//測試比如0x00000200;0x00000800沒這問題。dx幫忙。//至今沒有解決,所以我用不了Flash.  { chkPt1 = chkBs; *(u32 *)chkPt1 = test;//寫數(shù)據(jù) if(*(u32 *)chkPt1==1024))//讀數(shù)據(jù)和寫入的是否一樣? { chkPt1 += 4; Led_Display(1);
Led_Display(2); Led_Display(3); Led_Display(4); } else goto error; } Uart_SendString("/n/tSDRAM Check Successful!/n/tMemory Maping..."); get_memory_map(); //獲得可用memory 信息,做成列表,后面會作為啟動參數(shù)傳給KERNEL //所謂內(nèi)存映射就是指在4GB 物理地址空間中有哪些地址范圍被分配用來尋址系統(tǒng)的 RAM 單元。 Uart_SendString("/n/tMemory Map Successful!/n"); //我用仿真器把KERNEL,RAMDISK直接放在SDRAM上,所以下面這段是不需要的,但是假如KERNEL,RAMDISK在FLASH里,那就需要. /*******************(copy linux KERNEL)*******************/ Uart_SendString("/tLoading KERNEL IMAGE from FLASH... /n "); Uart_SendString("/tand copy KERNEL IMAGE to SDRAM at 0x31000000/n"); Uart_SendString("/t/tby LEIJUN DONG dongleijun4000@hotmail.com /n"); for(k = 0;k < 196608;k++,downPt += 1,fromPt += 1)//3*1024*1024/32linux KERNEL des,src,length=3M * (u32 *)downPt = * (u32 *)fromPt; /*******************(load RAMDISK)*******************/ Uart_SendString("/t/tloading COMPRESSED RAMDISK.../n"); downPt=(RAM_COMPRESSED_RAMDISK_BASE); fromPt=(FLASH_RAMDISK_BASE); for(k = 0;k < 196608;k++,downPt += 1,fromPt += 1)//3*1024*1024/32linux KERNEL des,src,length=3M * (u32 *)downPt = * (u32 *)fromPt; /******jffs2文件系統(tǒng),在開發(fā)中假如用不到FLASH,這段也可以不要********/ Uart_SendString("/t/tloading jffs2.../n"); downPt=(RAM_JFFS2); fromPt=(FLASH_JFFS2); for(k = 0;k < (1024*1024/32);k++,downPt += 1,fromPt += 1) * (u32 *)downPt = * (u32 *)fromPt; Uart_SendString( "Load Success...Run.../n "); /*******************(setup param)*******************/ setup_start_tag();//開始設(shè)置啟動參數(shù) setup_memory_tags();//內(nèi)存印象 setup_commandline_tag("console=ttyS0,115200n8");//啟動命令行 setup_initrd2_tag();//root device setup_RAMDISK_tag();//ramdisk image setup_end_tag(); /*關(guān)I-cache */ asm ("mrc p15, 0, %0, c1, c0, 0": "=r" (i)); i &= ~0x1000; asm ("mcr p15, 0, %0, c1, c0, 0": : "r" (i)); /* flush I-cache */ asm ("mcr p15, 0, %0, c7, c5, 0": : "r" (i)); //下面這行就跳到了COMPRESSED KERNEL的首地址 theKERNEL(0, ARCH_NUMBER, (unsigned long *)(RAM_BOOT_PARAMS)); //啟動kernel時候,I-cache可以開也可以關(guān),r0必須是0,r1必須是CPU型號 (可以從linux/arch/arm/tools/mach-types中找到),r2必須是參數(shù)的物理開始地址 /*******************END*******************/ error: Uart_SendString("/n/nPanic SDRAM check error!/n"); return 0; } static void setup_start_tag(void) { params = (struct tag *)RAM_BOOT_PARAMS;//啟動參數(shù)開始的地址 params->hdr.tag = ATAG_CORE; params->hdr.size = tag_size(tag_core); params->u.core.flags = 0; params->u.core.pagesize = 0; params->u.core.rootdev = 0; params = tag_next(params); }   static void setup_memory_tags(void)
{ int i;  for(i = 0; i < NUM_MEM_AREAS; i++) { if(memory_map[i].used) { params->hdr.tag = ATAG_MEM; params->hdr.size = tag_size(tag_mem32); params->u.mem.start = memory_map[i].start; params->u.mem.size = memory_map[i].len; params = tag_next(params); } } }   static void setup_commandline_tag(char *commandline) { int i = 0; /* skip non-existent command lines so the kernel will still * use its default command line. */ params->hdr.tag = ATAG_CMDLINE; params->hdr.size = 8; //console=ttyS0,115200n8 strcpy(params->u.cmdline.cmdline, p); params = tag_next(params); }   static void setup_initrd2_tag(void) { /* an ATAG_INITRD node tells the kernel where the compressed * ramdisk can be found. ATAG_RDIMG is a better name, actually. */ params->hdr.tag = ATAG_INITRD2; params->hdr.size = tag_size(tag_initrd); params->u.initrd.start = RAM_COMPRESSED_RAMDISK_BASE; params->u.initrd.size = 2047;//k byte params = tag_next(params); }   static void setup_ramdisk_tag(void) { /* an ATAG_RAMDISK node tells the kernel how large the * decompressed ramdisk will become. */ params->hdr.tag = ATAG_RAMDISK; params->hdr.size = tag_size(tag_ramdisk); params->u.ramdisk.start = RAM_DECOMPRESSED_RAMDISK_BASE; params->u.ramdisk.size = 7.8*1024; //k byte params->u.ramdisk.flags = 1; // automatically load ramdisk params = tag_next(params); }   static void setup_end_tag(void) { params->hdr.tag = ATAG_NONE; params->hdr.size = 0; } void Uart_Init(int pclk,int baud)//串口是很重要的 { int i; if(pclk == 0) pclk = PCLK; rUFCON0 = 0x0; //UART channel 0 FIFO control register, FIFO disable rUMCON0 = 0x0; //UART chaneel 0 MODEM control register, AFC disable  //UART0 rULCON0 = 0x3; //Line control register : Normal,No parity,1 stop,8 bits 下面這段samsung好象寫的不太對,但是我按照Normal,No parity,1 stop,8 bits算出來的確是0x245  // [10] [9] [8] [7] [6] [5] [4] [3:2] [1:0] // Clock Sel, Tx Int, Rx Int, Rx Time Out, Rx err, Loop-back, Send break, Transmit Mode, Receive Mode // 0 1 0 , 0 1 0 0 , 01 01 // PCLK Level Pulse Disable Generate Normal Normal Interrupt or Polling rUCON0 = 0x245; // Control register rUBRDIV0=( (int)(PCLK/16./ baud) -1 ); //Baud rate divisior register 0 delay(10); } 經(jīng)過以上的折騰,接下來就是kernel的活了.能不能啟動kernel,得看你編譯kernel的水平了.
這個BOOTLOADER不象blob那樣需要交互信息,使用虛擬地址,總的來說非常簡潔明了.

發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 民县| 威信县| 时尚| 海原县| 肥东县| 太仆寺旗| 兴义市| 和田县| 罗源县| 邢台县| 常宁市| 清原| 阳东县| 房产| 宜阳县| 雷山县| 平和县| 天峨县| 鹤庆县| 黔江区| 和林格尔县| 繁峙县| 榆中县| 紫金县| 浑源县| 邛崃市| 谢通门县| 巴塘县| 平湖市| 尼木县| 舒兰市| 桂林市| 敦煌市| 兴安盟| 大冶市| 孝感市| 临漳县| 南丹县| 盐池县| 县级市| 穆棱市|