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)