1. 程式人生 > >Uboot 怎樣傳遞引數啟動核心

Uboot 怎樣傳遞引數啟動核心

這裡,主要實現了以下三點:

1、 從nandflash啟動核心;

2、 從sdram啟動核心;

3、 利用kermit協議下載核心到sdram中去。

首先,從nandflash啟動核心,這裡有一個前提就是nandflash要分好區,並且nandflash要有核心映像zImage,但是到目前為止,lboot還沒有實現nandflash的分割槽和下載核心到nandflash的功能,所以暫且借用supervivi首相將nandflash分好區,並下載核心映像zImage到核心分割槽。如果以後時間充足的話,還要實現類似supervivi的格式化nandflash,為nandflash分割槽,下載東西到nandflash中,我想格式化nandflash的話就是再實現一個擦除nandflash的函式,前面的nand.c只實現了nand的讀函式,而下載東西到nandflash中就是在實現一個nand寫函式,而分割槽呢,個人覺得可以定義幾個巨集,分別代表每個區的啟始地址,然後在實現比如往nandflash中燒寫bootloader的時候就往nand寫函式傳入bootloader的起始地址,燒寫核心映像的時候,就傳入核心分割槽的起始地址就可以了,具體的等看看vivi的原始碼是怎麼實現的,然後在米葫蘆畫瓢的實現。好了,現在假設在nand的0x00060000處已經存放的linux核心映像zImage,那麼怎麼實現從nand啟動核心呢?其實很簡單,簡單的呼叫nandread函式將核心映像拷貝到sdram中的0x3380,0000地址處,拷貝完之後再跳到這個地址就可以啟動核心了,程式碼實現如下:

/********** SDRAMand Nand Space Allocation ****************/

#defineSDRAM_TOTAL_SIZE         0x04000000                                              //64M SDRAM

#defineSDRAM_ADDR_START         0x30000000                                            //起始地址是0x3000,0000

#defineSDRAM_LBOOT_START        0x33000000                                            //LBOOT的存放地址

#defineSDRAM_TAGS_START         0x30000100                                             //tag列表的存放地址

#define SDRAM_KERNEL_START       0x33800000                                            //kernel的存放地址

#defineNAND_KERNEL_START       0x00060000

#define NAND_KERNEL_SZIE        0x00500000

int boot_linux()

{

    setup_core_tag( (void *)SDRAM_TAGS_START);   /* standard stCore ulTag 4kpagenSize */

    setup_mem_tag(SDRAM_ADDR_START,SDRAM_TOTAL_SIZE);    /* 64Mb at0x30000000 */

    setup_cmdline_tag( CmdLine );  

    setup_end_tag();                    /* end of ulTags */

    s3c2440_nand_init();

    printf("copy Kernel from nandflash toSDRAM... ...\n\r");

    s3c2440_nand_read((unsignedchar *)SDRAM_KERNEL_START, NAND_KERNEL_START, NAND_KERNEL_SZIE);

    printf("copy Kernel from nandflash toSDRAM has finished!\n\r");

    printf("Now jump to the kernelentry!\n\r");

    theKernel = (void (*)(int, int, unsignedint))SDRAM_KERNEL_START;

    theKernel(0, S3C2440_MATHINE_TYPE,SDRAM_TAGS_START);

    return 0;

}

說道這裡不得不談下核心的啟動過程,我們知道bootloader的最終目的是做什麼呢?就是要實現啟動核心的功能,那麼我們現在bootloader已經完成了硬體的初始化,同時也為核心的啟動準備好了條件,那麼現在要做的就是傳遞核心啟動引數給核心,然後核心接收這些引數並啟動,為什麼要傳遞引數呢,linux核心是強大的,支援N種不同的開發板,所以你要告訴核心我是什麼板子?比如我是mini2440開發板,那麼linux核心就會呼叫mini2440相關的函式來實現在mini2440這塊板子上執行,當然前提是你要為你的板子移植好linux核心。

       那麼bootloader和核心引數之間是怎麼傳遞的呢?是通過tag list的形式傳遞的,網上很多講這方面的內容,我只闡述一個大概的思想:bootloader和核心使用約定俗成的形式定義一系列的引數,然後bootloader把核心需要的資訊放在這些引數裡面,之後將這個引數放在一個地址處,然後將這個地址傳給核心,核心就會到這個地址處來讀這些引數,這樣就完成核心引數的傳遞了。

       引數的統一形式如下:

struct Atag {

         struct TagHeader stHdr;

         union {

                   struct TagCore                  stCore;

                   struct TagMem32              stMem;

                   struct TagCmdline   stCmdLine;

         }u;

};

struct TagHeader {

         unsigned int nSize;                                                 //以字為單位即4個位元組

         unsigned int ulTag;

};

Atag由tag頭部TagHeader和內容u來組成,TagHeader有兩個引數:nSize代表整個Atag的大小,ulTag代表這個Atag是什麼型別的,型別定義如下:

#define ATAG_NONE        0x00000000                              //必須有,且放在最後一個位置,只有頭部,沒有內容

#define ATAG_CORE        0x54410001                              //必須有,且放在第一個位置

#define ATAG_MEM         0x54410002                              //代表記憶體

#defineATAG_CMDLINE  0x54410009                              //代表命令列引數

數字沒有什麼具體的含義,就是一種標識,按照linux核心裡面的定義值即可。

所以在跳轉到核心之前,一定要先把這些引數的值設好,設定的函式如下:

static struct Atag*pCurTag;                                                                 /*used to point at the current ulTag */

void(*theKernel)(int, int, unsigned int);

const char *CmdLine= "root=/dev/mtdblock3 console=ttySAC0,115200 mem=64M init=/linuxrc";

static void setup_core_tag(void*pStartAddr)

{

    pCurTag = (struct Atag *)pStartAddr;                                 /*start at given address */到時侯傳遞0x30000100,即引數列表在sdram中存放地址

    pCurTag->stHdr.ulTag = ATAG_CORE;                           /*start with the stCore ulTag */型別

    pCurTag->stHdr.nSize =TAG_SIZE(TagCore);                       /*nSize the ulTag */大小

    pCurTag->u.stCore.ulFlags = 1;                                          /* ensure read-only */內容

    pCurTag->u.stCore.nPageSize = 4096;                               /*systems pagenSize (4k) */

    pCurTag->u.stCore.ulRootDev = 0;                                   /*zero (typicaly overidden from commandline )*/

    pCurTag = TAG_NEXT(pCurTag);                               /*move pointer to next ulTag */

}

static void setup_mem_tag(unsignedint start, unsigned int len)                  //設定記憶體引數

{

    pCurTag->stHdr.ulTag = ATAG_MEM;                          /*Memory ulTag */

    pCurTag->stHdr.nSize =TAG_SIZE(TagMem32);                   /* nSize ulTag */

    pCurTag->u.stMem.ulStart = start;                                    /*Start of memory area (physical address) */

    pCurTag->u.stMem.nSize = len;                                 /*Length of area */

    pCurTag = TAG_NEXT(pCurTag);                               /*move pointer to next ulTag */

}

static voidsetup_cmdline_tag(const char * line)

{

    int linelen = strlen(line);

    if(!linelen)

        return;                                                                     /* do not insert aulTag for an empty commandline */

    pCurTag->stHdr.ulTag =ATAG_CMDLINE;                              /*Commandline ulTag */

    pCurTag->stHdr.nSize = (sizeof(structTagHeader) + linelen + 1+4) >> 2;

    strcpy(pCurTag->u.stCmdLine.cCmdLine,line);                               /* place commandline into ulTag */

    pCurTag = TAG_NEXT(pCurTag);                                        /*move pointer to next ulTag */

}

static voidsetup_end_tag(void)

{

    pCurTag->stHdr.ulTag = ATAG_NONE;                                  /* Empty ulTag ends list */

    pCurTag->stHdr.nSize = 0;                                                         /*zero length */

}

這裡引數是以連結串列的形式存放的,賦值主要設計兩個巨集:

#define TAG_NEXT(t)       ((struct Atag *)((unsigned int *)(t) +(t)->stHdr.nSize))

#defineTAG_SIZE(type)    ((sizeof(structTagHeader) + sizeof(struct type)) >> 2)//左移2的目的前面說過size是以字為單位即4個位元組,左移2相當於除以4,這樣就可以算出以4個位元組為單位,引數所佔的大小了。

還有一點要說明的是:

pCurTag->stHdr.nSize= (sizeof(struct TagHeader) + linelen + 1+4) >> 2;

這裡為什麼要+1+4,+1代表著加上’\0’,因為strlen並沒有將‘\0’算上,+4為什麼呢?比如我實際長度只有2,那麼以4位元組為單位的話,長度應該是1,你鑰匙直接用2/4的話,那麼長度為0了,加上4就能保證長度為1,主要就是這麼一樣意思。

所以在剛上來從nand啟動時,我們首先要將引數設定好,然後在複製核心映像到SDRAM中,然後跳到核心入口執行核心映像,那麼怎麼跳到核心入口呢,其實就是跳到核心的存放地址即可:

void (*theKernel)(int, int, unsigned int);

    theKernel = (void (*)(int, int, unsignedint))SDRAM_KERNEL_START;

    theKernel(0, S3C2440_MATHINE_TYPE, SDRAM_TAGS_START)

這裡首先將核心存放地址強制轉換,然後賦給函式指標theKernel,然後傳入機器碼,核心引數列表地址即可。這樣,就可以從nandflash啟動核心了。

接下來:實現從SDRAM啟動核心,思路是一樣的,看實現程式碼:

intboot_linux_sdram()

{

    setup_core_tag( (void *)SDRAM_TAGS_START);                         /* standard stCore ulTag 4k pagenSize*/

    setup_mem_tag(SDRAM_ADDR_START,SDRAM_TOTAL_SIZE);  /* 64Mb at 0x30000000*/

    setup_cmdline_tag( CmdLine );  

    setup_end_tag();                                                            /*end of ulTags */

    theKernel = (void (*)(int, int, unsignedint))SDRAM_KERNEL_START;

    theKernel(0, S3C2440_MATHINE_TYPE, SDRAM_TAGS_START);

    return 0;

}

這裡就要求我們首先在SDRAM_KERNEL_START處存放有核心映像,那麼我們就得首先實現下載核心映像到SDRAM中的功能,這裡利用的kermit協議來實現了,關於kermit協議的介紹,我會在附件中貼出,裡面介紹的很詳細。

要實現利用kermit協議使用串列埠下載核心映像到SDRAM中,我們要做的工作就是實現接收部份的程式碼,因為我們是從PC->開發板,傳送部份的程式碼是由PC來實現的,如PC上的C-Kermit軟體,而接收部份的程式碼是很簡單的,主要實現傳送ACK包就可以了,看下面的程式前,首先要把附件中kermit協議看了,裡面的包格式,使用的編碼方式等。傳送ACK包是通過寫串列埠和我們開發板上的串列埠聯絡起來的,看程式:

傳送ACK包程式:

#defineKERM_ACK_LEN 0x10

static voidsend_ack_packet(unsigned int seq)                                                            //傳送ACK包

{

         unsigned char buf[KERM_ACK_LEN];

         int index = 0, check_sum = 0;

         buf[index++] = MARK_START;                                                                              //MARK段:SOH

         buf[index++] = ENC_PRINT(3);                                                                             //LEN段:0x3+0x20

         buf[index++] = seq + KERM_KEY_SPACE;                                                            //SEQ段:第幾個包

         buf[index++] = KERM_TYPE_ACK;                                                                      //TYPE段:ACK包

         buf[index] = '\0';

         index = 1;                                                                                                  //CHECK段只計算LEN段和SEQ段

         while (buf[index])

         {

                   check_sum += buf[index];                                                                   //採用單位元組算術校驗和

                   index++;

         }

         buf[index++] = (KERM_KEY_SPACE + (0x3f & (check_sum + (0x03 & (check_sum>> 6))))) & 0xff;//在編碼之前把第6、7兩位提出加到低6位上

         buf[index++] = KERM_KEY_TERM;

         buf[index] = '\0';

#ifdef CONFIG_TOPHALF_KERMIT

         UartPuts((const char *)buf);

#else

         index = 0;

         while (buf[index])

                   GtUartWriteByte(buf[index++]);                                                                              //傳送ACK包,使用串列埠傳送

#endif

}

接收檔案程式:

#defineKERM_BUF_LEN   128

int GtSerialLoad(void*pLoadAddr)    //接收kermit send傳送過來的資料到pLoadAddr地址處,pLoadAddr就為核心在SDRAM

                                                        //中的存放地址

{

         unsigned char buf[KERM_BUF_LEN];

         unsigned char curr_char;

         int index;

         int check_sum, len, seq, type;

         unsigned char *pAddr = (unsigned char*)pLoadAddr;  //pAddr =核心在SDRAM中的存放地址

         do {

                   while(MARK_START != GtUartReadByte());                       //沒收到啟始包,迴圈等待

                   index = 0;

                   while(KERM_KEY_TERM !=(buf[index] = GtUartReadByte())) //接收一包資料

                            index++;

                   index = 0;

                   /* length decode */

                   len = buf[index++];

                   check_sum = len;

                   len -= KERM_KEY_SPACE;                                                     //解碼,編碼是+0X20,解碼時-0x20

                   /* sequence decode */

                   seq = buf[index++];

                   check_sum += seq;

                   seq -= KERM_KEY_SPACE;

                   /* get package type */

                   type = buf[index++];

                   check_sum += type;

                   if (len) //fixme: handleextended length                                         //根據協議要減去兩個位元組

                            len -= 2;

                   while (len > 1) {

                            curr_char =buf[index++];

                            check_sum +=curr_char;

                            len--;

                            if (type !=KERM_TYPE_DATA)                             //接收資料包

                                     continue;

                            if (curr_char ==KERM_KEY_SHARP) /* '#' */    //資料包中的控制字元

                            {

                                     curr_char =buf[index++];

                                     check_sum+= curr_char;

                                     len--;

                                      if (0x40 == (curr_char & 0x60))                                                           

                                               curr_char= curr_char & (~0x40);

                                     else if (0x3f == (curr_char & 0x7f))

                                               curr_char|= 0x40;

                            }

                            *pAddr++ =curr_char;

                   }

                   /* checksum */

                   curr_char = buf[index++];                                                                                                                                                                                                                                                                                                                                                                                                                                      //校驗資料是否正確

                   if (curr_char !=(KERM_KEY_SPACE + (0x3f &(check_sum + (0x03 & (check_sum >> 6))))))

#ifdefCONFIG_TOPHALF_KERMIT

                            goto Loop;

#else

                            goto Error;

#endif

                   /* terminator */

                   curr_char = buf[index++];

                   if (curr_char != KERM_KEY_TERM)                                                                                                                                                         

#ifdefCONFIG_TOPHALF_KERMIT

                            goto Loop;

#else

                            goto Error;

#endif

                   /* send ack package */

                   send_ack_packet(seq);                                             /傳送ACK包

         }while(type != KERM_TYPE_BREAK);                             //收到'B'代表檔案傳送結束                         

         return pAddr - (unsigned char *)pLoadAddr;                         //返回接收檔案的大小

#ifdefCONFIG_TOPHALF_KERMIT

Loop:

         while (1)

            GtUartWriteByte('E');

#else

Error:

#endif

         printf("Error\n\r");

        return -1;

}

下載完之後,我們在執行int boot_linux_sdram() 就可以實現從SDRAM啟動了。 

最後講下Makefile:Makefile實現的功能是:每個資料夾內都有自己的Makefile,根目錄下的主Makefile會進入各個子目錄並呼叫各自的Makefile。每個子目錄下的Makefile把自己編譯的程式碼連結成一個build-in.o檔案, 主Makefile把各個子目錄下的build-in.o連結成一個可執行檔案。

這裡學著Li Hongwang大俠的做法:

CC              = arm-lwm-linux-gnueabi-gcc

LD              = arm-lwm-linux-gnueabi-ld

AR              = arm-lwm-linux-gnueabi-ar

OBJCOPY    = arm-lwm-linux-gnueabi-objcopy

OBJDUMP   = arm-lwm-linux-gnueabi-objdump

INCLUDEDIR        := $(shell pwd)/include

CFLAGS               := -Wall -O2

CPPFLAGS  := -nostdinc -fno-builtin -I$(INCLUDEDIR)

BUILT_IN_OBJ =built-in.o

export CC LD AROBJCOPY OBJDUMP INCLUDEDIR CFLAGS CPPFLAGS

export BUILT_IN_OBJ

SUBDIRS =  start device lib app main

/*可以google下foreach的用法*/

SUBOBJS :=$(foreach n, $(SUBDIRS), $(n)/$(BUILT_IN_OBJ))

all: lboot.binlboot.dis

lboot.bin :lboot.elf

         ${OBJCOPY} -O binary -S $^ [email protected]

lboot.dis :lboot.elf

         ${OBJDUMP} -D -m arm $^ > [email protected]

lboot.elf :$(SUBOBJS)

         ${LD} -Tlboot.lds -o [email protected] $^

$(SUBOBJS) :$(SUBDIRS)

.PHONY : $(SUBDIRS)

$(SUBDIRS) :

         @make -C [email protected] all

.PHONE : clean

clean:

         rm -f lboot.dis lboot.bin lboot.elf *.o

         @for subdir in $(SUBDIRS); do \

         (make clean -C $$subdir); \

         done

總結:

可以這麼說,到目前為止,基本上都是參考Li Hongwang大俠的部落格及程式碼做的,只是添加了自動識別norflash啟動和nandflash啟動,並修改了nandflash部份的程式碼,因為我的nandflash是128M,nandflash從128M是一個分界點,頁的大小不一樣了。

下面又要到關鍵的部份了,因為我的網絡卡是DM9000,^_^,在實現網口下載功能之前或者之後,我可能還會實現nandflash的格式化,nandflash的下載,nandflash的分割槽,之後我可能會根據我的開發板完成一些測試的功能,如測試LED,ADC,按鍵等。

好了,想起一位老師的一句話,既然你已經選擇了讀研,那麼你肯定對你有著更高的期望,那麼就讓我們帶著夢想,珍惜在校的每一天,繼續努力吧。

效果圖:

此處轉載http://blog.chinaunix.net/space.php?uid=18921523&do=blog&id=199952

相關推薦

Uboot 怎樣傳遞引數啟動核心

這裡,主要實現了以下三點: 1、 從nandflash啟動核心; 2、 從sdram啟動核心; 3、 利用kermit協議下載核心到sdram中去。 首先,從nandflash啟動核心,這裡有一個前提就是nandflash要分好區,並且nandflash要有核心映像zIma

Uboot到底如何啟動核心

1.uboot啟動核心的程式碼縮減如下: Uboot 1.16/lib_arm/board.c中start_armboot()函式呼叫/common/main.c中main_loop()函式,在main_loop()中有uboot啟動核心的程式碼: s = getenv ("bootcmd"

omapl138移植uboot系列之啟動核心原理(啟動核心第三篇)

struct tag {          struct tag_header hdr;          union {                    struct tag_core                  core;                

移植U-BOOT之裁剪和修改預設引數(易用性)啟動核心,以及對uboot進行分割槽

今天我們來裁剪U-BOOT,使其更加易用,修改預設引數,以及製作最終修改好得補丁檔案方便以後的快速移植。 那麼如果想看之前的關於網絡卡以及flash等的移植,請點選連結檢視:點選連結檢視 在裁剪修改之前呢,我們先來了解一下U-BOOT的環境引數(環境變數):

【Linux】【cmdline】uboot傳遞核心的root裝置序號動態變化導致啟動失敗

現象 板子上插入SD卡啟動Linux系統後,emmc的裝置名稱是/dev/mmcblck1;板子上沒有插入SD卡,Linux啟動後,emmc的裝置名稱是/dev/mmcblck0. uboot傳遞給核心的cmdline引數bootargs 中root=/de

uboot核心引數傳遞核心解析uboot傳遞核心引數

一、核心引數的傳遞 uboot將核心引數存放在記憶體的某一地址上,bi_boot_params存放uboot傳給kernel 核心引數的首地址. int board_init (void) {gd->bd->bi_arch_number = MACH_TYPE_

UBOOT傳遞核心引數

1.核心引數傳遞 核心中的引數是核心提供的,在配置核心時指定,而u-boot提供的則在u-boot啟動時傳遞到核心取代核心提供的。u-boot的引數傳遞利用了三個通用暫存器R0,R1,R2。u-boot在啟動的過程中把引數放到3個暫存器中,到核心啟動時再把暫存器中的引數取出

核心檢視uboot傳遞引數簡單方法

u-boot  設定bootargs=console=ttyS0,115200n8 initrd=2g,1 root=/dev/ram rawview=myrawview static int __init rawview_setup(char *str){ print

java啟動exe程式,傳遞引數和獲取引數

1、java中啟動exe程式 ,並新增傳參 String[] cmd = {"hh.exe","12315"}; Process process = null; try { ProcessBuilder pb =

uboot啟動核心過程

我們都知道u-boot被締造出來的使命是 啟動核心。 那麼,他是如何完成他的使命的呢! (1)我們先來分析下Linux核心映象這個概念吧。 我們編譯核心完(編譯成功)會生成vmlinux,Image,zImage,再通過 uboot提供的工具mkimage,執行m

uboot啟動流程之進uboot命令列和啟動核心

1.上電進board_init_r-->init_sequence_r-->run_main_loop -->main_loop-->bootdelay_process-->autoboot_command主要就是這麼個流程 2.具體怎麼決定

Android 啟動另外的APP及傳遞引數

                有時候需要從一個APP中啟動另外一個APP,比如Twitter/微信等。如果你不知道那個APP的Activity,但是知道包名(package name),那麼可以使用如下的方法:Intent LaunchIntent = getPackageManager().getLaun

omapl138移植uboot系列之修改移植TI官方移植的Linux核心(啟動核心第二篇)

修改Linux核心原始碼     實際上,剛剛我們已經成功的啟動了TI移植過的Linux核心,但是從串列埠控制檯的現象來看,“Starting Kernel”之後什麼資訊都沒有輸出,這就需要我們在TI移植過的核心原始碼之上進行相應修改,以適合我們的639A板卡。

uboot給linux傳遞引數流程

是用mindmanager轉的,格式有點不太好。 1 ATAG 1.1 為什麼用ATAG          uboot用atag向kernel傳遞資訊:          atag的定義可以在uboot的include/asm/setup.h 中找到,對應linux中的定義

UBOOT——啟動核心

https://www.cnblogs.com/biaohc/p/6403863.html   UBOOT——啟動核心 1:什麼是UBOOT,為什麼要有UBOOT?   UBOOT的主要作用是用來啟動linux核心,因為CPU不能直接從塊裝置中執行程式碼,需要把塊裝置中的程

Uboot的作用以及啟動核心的過程

第二階段比較複雜,做的工作主要是1.從flash中讀出核心。2.啟動核心。start_arm_boot的主要流程為,設定機器id,初始化flash,然後進入main_loop,等待uboot命令,uboot要啟動核心,主要經過兩個函式,第一個是s=getenv("bootcmd"),第二個是run_comma

uboot傳遞引數到kernel的兩種方式

第一:         寫新引數到bootargs,驅動中用__setup(“新引數名=”, function)註冊自定義函式解析新引數“=”後的字串,再使用EXPORT_SYMBOL()匯出。 第二:         自定義新引數到uboot環境變數中,uboot中使用g

WPF 利用Process.Start()方法啟動指定路徑下的exe檔案並傳遞引數

簡單來說就是實現一個程式A 開啟程式B,並且在開啟的時候傳遞一些引數給B,最後在B視窗上顯示出引數,這個小功能也是折騰了我半天。現在把我的過程整理記錄下來。 1.首先我們得有一個被呼叫的程式,新建一個簡單的WPF程式,命名為:argTest。裡面加一個label,用來顯示接

Unity3D和IOS的互動教程——通過URL啟動APP並傳遞引數

最近專案(麻將手遊)中遇到這樣一個需求:在建立一個私人房後通過ShareSDK分享一個連線到微信,點選這個連線後設備將跳轉至下載頁面或是啟動APP並直接進入到指定房間。由於目前沒有原生IOS的開發經驗,在發揮面向百度程式設計師的特長之後,寫出來下面的方案並用於專案中,實測可

自己寫bootloader筆記6---boot.c分析(u-boot向核心傳遞引數及跳轉到核心

#include "setup.h"extern void uart0_init(void); extern void nand_read(unsigned int addr, unsigned char *buf, unsigned int len); extern void puts(char *str)