生活随笔
收集整理的這篇文章主要介紹了
uboot 命令分析(一) — bootm
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
bootm?用于將內核鏡像加載到內存的指定地址處,如果有需要還要解壓鏡像,然后根據操作系統和體系結構的不同給內核傳遞不同的啟動參數,最后啟動內核。
一、arm 架構處理器對 linux 內核啟動之前環境的五點需求
1、cpu 寄存器設置
? ? * R0 = 0
? ? * R1 = 板級 id
? ? * R2 = 啟動參數在內存中的起始地址
2、cpu 模式
? ? * 禁止所有中斷
? ? * 必須為SVC(超級用戶)模式
3、緩存、MMU
? ? * 關閉 MMU
? ? * 指令緩存可以開啟或者關閉
? ? * 數據緩存必須關閉并且不能包含任何臟數據
4、設備
? ? * DMA 設備應當停止工作
5、boot loader 需要跳轉到內核鏡像的第一條指令處
這些需求都由 boot loader 實現,在常用的 uboot 中完成一系列的初始化后最后通過 bootm 命令加載 linux 內核。該命令用法介紹如下:
[cpp]?view plaincopy
#?help?bootm?? bootm?-?boot?application?image?from?memory?? ?? Usage:?? bootm?[addr?[arg?...]]?? ????-?boot?application?image?stored?in?memory?? ????????passing?arguments?'arg?...';?when?booting?a?Linux?kernel,?? ????????'arg'?can?be?the?address?of?an?initrd?image?? ?? Sub-commands?to?do?part?of?the?bootm?sequence.??The?sub-commands?must?be?? issued?in?the?order?below?(it's?ok?to?not?issue?all?sub-commands):?? ????????start?[addr?[arg?...]]?? ????????loados??-?load?OS?image?? ????????cmdline?-?OS?specific?command?line?processing/setup?? ????????bdt?????-?OS?specific?bd_t?processing?? ????????prep????-?OS?specific?prep?before?relocation?or?go?? ????????go??????-?start?OS??
二、基礎數據結構
在 bootm 中常用的數據結構有?image_info_t?和?bootm_headers_t,定義如下:
[cpp]?view plaincopy
?? typedef?struct?image_info?{?? ????ulong???????start,?end;??????????????? ????ulong???????image_start,?image_len;??? ????ulong???????load;????????????????????? ????uint8_t?????comp,?type,?os;??????????? }?image_info_t;?? ?? ?? typedef?struct?bootm_headers?{?? ????image_header_t??*legacy_hdr_os;??????? ????image_header_t??legacy_hdr_os_copy;??? ????ulong???????????legacy_hdr_valid;????? ?? ????image_info_t????os;??????????????????? ????ulong???????????ep;??????????????????? ????ulong???????????rd_start,?rd_end;????? ????ulong???????????ft_len;??????????????? ????ulong???????????initrd_start;?? ????ulong???????????initrd_end;?? ????ulong???????????cmdline_start;?? ????ulong???????????cmdline_end;?? ????bd_t????????????*kbd;?? ????int?????????????verify;??????????????? ?? #define?BOOTM_STATE_START???????(0x00000001)?? #define?BOOTM_STATE_LOADOS??????(0x00000002)?? #define?BOOTM_STATE_RAMDISK?????(0x00000004)?? #define?BOOTM_STATE_FDT?????????(0x00000008)?? #define?BOOTM_STATE_OS_CMDLINE??(0x00000010)?? #define?BOOTM_STATE_OS_BD_T?????(0x00000020)?? #define?BOOTM_STATE_OS_PREP?????(0x00000040)?? #define?BOOTM_STATE_OS_GO???????(0x00000080)?? ????int?????????????state;???????????????? ?? ????struct?lmb??lmb;?????????????????????? }?bootm_headers_t;??
三、代碼解析
bootm 的主函數為?do_bootm,代碼如下:
[cpp]?view plaincopy
int?do_bootm?(cmd_tbl_t?*cmdtp,?int?flag,?int?argc,?char?*argv[])?? {?? ????ulong??iflag;?? ????ulong??load_end?=?0;?? ????int????ret;?? ????boot_os_fn??*boot_fn;?? ?? ????if?(bootm_start(cmdtp,?flag,?argc,?argv))??????? ????????return?1;?? ?? ????iflag?=?disable_interrupts();??????? ????usb_stop();????????????????????????? ?? ????ret?=?bootm_load_os(images.os,?&load_end,?1);???? ?? ????lmb_reserve(&images.lmb,?images.os.load,?(load_end?-?images.os.load));?? ?? ????if?(images.os.os?==?IH_OS_LINUX)???? ????????fixup_silent_linux();?? ?? ????boot_fn?=?boot_os[images.os.os];???? ?? ????arch_preboot_os();?????????????????? ?? ????boot_fn(0,?argc,?argv,?&images);???? ?? ????puts?("\n##?Control?returned?to?monitor?-?resetting...\n");?? ????do_reset?(cmdtp,?flag,?argc,?argv);?? ?? ????return?1;?? }??
該函數的實現分為 3 個部分:首先通過 bootm_start 函數分析鏡像的信息,如果滿足判定條件則進入 bootm_load_os 函數進行加載,加載完成后就可以調用 boot_fn 開始啟動。
1、bootm_start
[cpp]?view plaincopy
static?int?bootm_start(cmd_tbl_t?*cmdtp,?int?flag,?int?argc,?char?*argv[])?? {?? ????ulong???????mem_start;?? ????phys_size_t?mem_size;?? ????void????????*os_hdr;?? ????int?????ret;?? ?? ????memset?((void?*)&images,?0,?sizeof?(images));?? ????images.verify?=?getenv_yesno?("verify");???? ?? ????lmb_init(&images.lmb);?? ?? ????mem_start?=?getenv_bootm_low();?? ????mem_size?=?getenv_bootm_size();?? ?? ????lmb_add(&images.lmb,?(phys_addr_t)mem_start,?mem_size);?? ?? ????arch_lmb_reserve(&images.lmb);?? ????board_lmb_reserve(&images.lmb);?? ?? ?????? ????os_hdr?=?boot_get_kernel?(cmdtp,?flag,?argc,?argv,?? ????????????&images,?&images.os.image_start,?&images.os.image_len);?? ????if?(images.os.image_len?==?0)?{?? ????????puts?("ERROR:?can't?get?kernel?image!\n");?? ????????return?1;?? ????}?? ?? ?????? ????switch?(genimg_get_format?(os_hdr))?{?? ????case?IMAGE_FORMAT_LEGACY:?? ????????images.os.type?=?image_get_type?(os_hdr);???? ????????images.os.comp?=?image_get_comp?(os_hdr);???? ????????images.os.os?=?image_get_os?(os_hdr);???????? ?? ????????images.os.end?=?image_get_image_end?(os_hdr);???? ????????images.os.load?=?image_get_load?(os_hdr);???????? ????????break;?? ????default:?? ????????puts?("ERROR:?unknown?image?format?type!\n");?? ????????return?1;?? ????}?? ?? ?????? ????if?(images.legacy_hdr_valid)?{?? ????????images.ep?=?image_get_ep?(&images.legacy_hdr_os_copy);?? ????}?else?{?? ????????puts?("Could?not?find?kernel?entry?point!\n");?? ????????return?1;?? ????}?? ?? ????if?(images.os.os?==?IH_OS_LINUX)?{?? ?????????? ????????ret?=?boot_get_ramdisk?(argc,?argv,?&images,?IH_INITRD_ARCH,?? ????????????????&images.rd_start,?&images.rd_end);?? ????????if?(ret)?{?? ????????????puts?("Ramdisk?image?is?corrupt?or?invalid\n");?? ????????????return?1;?? ????????}?? ????}?? ????images.os.start?=?(ulong)os_hdr;???? ????images.state?=?BOOTM_STATE_START;??? ?? ????return?0;?? }??
該函數主要進行鏡像的有效性判定、校驗、計算入口地址等操作,大部分工作通過?
boot_get_kernel ->?image_get_kernel
?完成,代碼如下:
[cpp]?view plaincopy
static?image_header_t?*image_get_kernel?(ulong?img_addr,?int?verify)?? {?? ????image_header_t?*hdr?=?(image_header_t?*)img_addr;?? ?? ????if?(!image_check_magic(hdr))?{????? ????????puts?("Bad?Magic?Number\n");?? ????????show_boot_progress?(-1);?? ????????return?NULL;?? ????}?? ????show_boot_progress?(2);?? ?? ????if?(!image_check_hcrc?(hdr))?{????? ????????puts?("Bad?Header?Checksum\n");?? ????????show_boot_progress?(-2);?? ????????return?NULL;?? ????}?? ?? ????show_boot_progress?(3);?? ????image_print_contents?(hdr);???????? ?? ????if?(verify)?{?????????????????????? ????????puts?("???Verifying?Checksum?...?");?? ????????if?(!image_check_dcrc?(hdr))?{?? ????????????printf?("Bad?Data?CRC\n");?? ????????????show_boot_progress?(-3);?? ????????????return?NULL;?? ????????}?? ????????puts?("OK\n");?? ????}?? ????show_boot_progress?(4);?? ?? ????if?(!image_check_target_arch?(hdr))?{????? ????????printf?("Unsupported?Architecture?0x%x\n",?image_get_arch?(hdr));?? ????????show_boot_progress?(-4);?? ????????return?NULL;?? ????}?? ????return?hdr;?? }?? ?? static?void?*boot_get_kernel?(cmd_tbl_t?*cmdtp,?int?flag,?int?argc,?char?*argv[],?? ????????bootm_headers_t?*images,?ulong?*os_data,?ulong?*os_len)?? {?? ????ulong??img_addr;?? ????image_header_t??*hdr;?? ?? ????if?(argc?<?2)?{???? ????????img_addr?=?load_addr;?? ????????debug?("*??kernel:?default?image?load?address?=?0x%08lx\n",?load_addr);?? ????}?else?{?? ????????img_addr?=?simple_strtoul(argv[1],?NULL,?16);?? ????????debug?("*??kernel:?cmdline?image?address?=?0x%08lx\n",?img_addr);?? ????}?? ?? ????*os_data?=?*os_len?=?0;?? ????switch?(genimg_get_format?((void?*)img_addr))?{?? ????case?IMAGE_FORMAT_LEGACY:?? ????????printf?("##?Booting?kernel?from?Legacy?Image?at?%08lx?...\n",?img_addr);?? ????????hdr?=?image_get_kernel?(img_addr,?images->verify);???? ????????if?(!hdr)?? ????????????return?NULL;?? ????????show_boot_progress?(5);?? ?? ?????????? ????????switch?(image_get_type?(hdr))?{?? ????????case?IH_TYPE_KERNEL:?? ????????????*os_data?=?image_get_data?(hdr);????????? ????????????*os_len?=?image_get_data_size?(hdr);????? ????????????break;?? ????????default:?? ????????????printf?("Wrong?Image?Type?for?%s?command\n",?cmdtp->name);?? ????????????show_boot_progress?(-5);?? ????????????return?NULL;?? ????????}?? ?? ?????????? ????????memmove?(&images->legacy_hdr_os_copy,?hdr,?sizeof(image_header_t));?? ?? ????????images->legacy_hdr_os?=?hdr;?? ????????images->legacy_hdr_valid?=?1;???? ????????break;?? ????default:?? ????????printf?("Wrong?Image?Format?for?%s?command\n",?cmdtp->name);?? ????????show_boot_progress?(-108);?? ????????return?NULL;?? ????}?? ?? ????debug?("???kernel?data?at?0x%08lx,?len?=?0x%08lx?(%ld)\n",?*os_data,?*os_len,?*os_len);?? ?? ????return?(void?*)img_addr;???? }??
在有效性判定完成時會打印出鏡像的一些信息,代碼如下:
[cpp]?view plaincopy
void?image_print_contents?(const?void?*ptr)?? {?? ????const?image_header_t?*hdr?=?(const?image_header_t?*)ptr;?? ????const?char?*p;?? ?? ????p?=?"???";?? ????printf?("%sImage?Name:???%.*s\n",?p,?IH_NMLEN,?image_get_name?(hdr));???? #if?defined(CONFIG_TIMESTAMP)?||?defined(CONFIG_CMD_DATE)?||?defined(USE_HOSTCC)?? ????printf?("%sCreated:??????",?p);???? ????genimg_print_time?((time_t)image_get_time?(hdr));?? #endif?? ????printf?("%sImage?Type:???",?p);???? ????image_print_type?(hdr);?? ????printf?("%sData?Size:????",?p);???? ????genimg_print_size?(image_get_data_size?(hdr));?? ????printf?("%sLoad?Address:?%08x\n",?p,?image_get_load?(hdr));???? ????printf?("%sEntry?Point:??%08x\n",?p,?image_get_ep?(hdr));?????? }??
2、bootm_load_os
這個函數主要判段鏡像是否需要解壓,并且將鏡像移動到加載地址:
[cpp]?view plaincopy
static?int?bootm_load_os(image_info_t?os,?ulong?*load_end,?int?boot_progress)?? {?? ????uint8_t?comp?=?os.comp;??????????? ????ulong?load?=?os.load;????????????? ????ulong?blob_start?=?os.start;?????? ????ulong?blob_end?=?os.end;?????????? ????ulong?image_start?=?os.image_start;?????? ????ulong?image_len?=?os.image_len;?????????? ????uint?unc_len?=?CONFIG_SYS_BOOTM_LEN;????? ?? ????const?char?*type_name?=?genimg_get_type_name?(os.type);???? ?? ????switch?(comp)?{???? ????case?IH_COMP_NONE:???? ????????if?(load?==?blob_start)?{????? ????????????printf?("???XIP?%s?...?",?type_name);?? ????????}?else?{?? ????????????printf?("???Loading?%s?...?",?type_name);?? ?? ????????????if?(load?!=?image_start)?{?? ????????????????memmove_wd?((void?*)load,?(void?*)image_start,?image_len,?CHUNKSZ);?? ????????????}?? ????????}?? ????????*load_end?=?load?+?image_len;?? ????????puts("OK\n");?? ????????break;?? ????case?IH_COMP_GZIP:???? ????????printf?("???Uncompressing?%s?...?",?type_name);?? ????????if?(gunzip?((void?*)load,?unc_len,?(uchar?*)image_start,?&image_len)?!=?0)?{???? ????????????puts?("GUNZIP:?uncompress,?out-of-mem?or?overwrite?error?"?? ????????????????"-?must?RESET?board?to?recover\n");?? ????????????return?BOOTM_ERR_RESET;?? ????????}?? ?? ????????*load_end?=?load?+?image_len;?? ????????break;?? ????...?? ????default:?? ????????printf?("Unimplemented?compression?type?%d\n",?comp);?? ????????return?BOOTM_ERR_UNIMPLEMENTED;?? ????}?? ????puts?("OK\n");?? ????debug?("???kernel?loaded?at?0x%08lx,?end?=?0x%08lx\n",?load,?*load_end);?? ?? ????if?((load?<?blob_end)?&&?(*load_end?>?blob_start))?{?? ????????debug?("images.os.start?=?0x%lX,?images.os.end?=?0x%lx\n",?blob_start,?blob_end);?? ????????debug?("images.os.load?=?0x%lx,?load_end?=?0x%lx\n",?load,?*load_end);?? ????????return?BOOTM_ERR_OVERLAP;?? ????}?? ?? ????return?0;?? }??
3、do_bootm_linux
在 arm linux 平臺中 boot_fn 函數指針指向的函數是位于 lib_arm/bootm.c 的 do_bootm_linux,這是內核啟動前最后的一個函數,該函數主要完成啟動參數的初始化,并將板子設定為滿足內核啟動的環境,代碼如下:
[cpp]?view plaincopy
int?do_bootm_linux(int?flag,?int?argc,?char?*argv[],?bootm_headers_t?*images)?? {?? ????bd_t??*bd?=?gd->bd;?? ????char??*s;?? ????int???machid?=?bd->bi_arch_number;?? ????void??(*theKernel)(int?zero,?int?arch,?uint?params);?? ?? ????char??*commandline?=?getenv?("bootargs");???????????? ?? ????if?((flag?!=?0)?&&?(flag?!=?BOOTM_STATE_OS_GO))?????? ????????return?1;?? ?? ????theKernel?=?(void?(*)(int,?int,?uint))images->ep;???? ?? ????s?=?getenv?("machid");???? ????if?(s)?{?? ????????machid?=?simple_strtoul?(s,?NULL,?16);?? ????????printf?("Using?machid?0x%x?from?environment\n",?machid);?? ????}?? ?? ????debug?("##?Transferring?control?to?Linux?(at?address?%08lx)?...\n",?(ulong)?theKernel);?? ?? ?????? ????setup_start_tag?(bd);??????????????????????? ????setup_serial_tag?(¶ms);????????????????? ????setup_revision_tag?(¶ms);??????????????? ????setup_memory_tags?(bd);????????????????????? ????setup_commandline_tag?(bd,?commandline);???? ????if?(images->rd_start?&&?images->rd_end)?? ????????setup_initrd_tag?(bd,?images->rd_start,?images->rd_end);???? ????setup_videolfb_tag?((gd_t?*)?gd);??????????? ????setup_end_tag?(bd);????????????????????????? ?? ????printf?("\nStarting?kernel?...\n\n");?? ?? ????cleanup_before_linux?();???? ?? ????? ?? ????theKernel?(0,?machid,?bd->bi_boot_params);??????? ?? ????return?1;?? }??
這里還列舉部分啟動參數的初始化函數:
[cpp]?view plaincopy
static?void?setup_start_tag?(bd_t?*bd)?? {?? ????params?=?(struct?tag?*)?bd->bi_boot_params;??? ?? ????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?(bd_t?*bd)?? {?? ????int?i;?? ?? ????for?(i?=?0;?i?<?CONFIG_NR_DRAM_BANKS;?i++)?{?? ????????params->hdr.tag?=?ATAG_MEM;???? ????????params->hdr.size?=?tag_size?(tag_mem32);?? ?? ????????params->u.mem.start?=?bd->bi_dram[i].start;???? ????????params->u.mem.size?=?bd->bi_dram[i].size;?????? ?? ????????params?=?tag_next?(params);?? ????}?? }?? ?? static?void?setup_commandline_tag?(bd_t?*bd,?char?*commandline)?? {?? ????char?*p;?? ?? ????if?(!commandline)?? ????????return;?? ?? ????for?(p?=?commandline;?*p?==?'?';?p++);???? ?? ????if?(*p?==?'\0')??????????????????????????? ????????return;?? ?? ????params->hdr.tag?=?ATAG_CMDLINE;??????????? ????params->hdr.size?=?(sizeof?(struct?tag_header)?+?strlen?(p)?+?1?+?4)?>>?2;???? ?? ????strcpy?(params->u.cmdline.cmdline,?p);???? ?? ????params?=?tag_next?(params);?? }?? ?? static?void?setup_end_tag?(bd_t?*bd)?? {?? ????params->hdr.tag?=?ATAG_NONE;???? ????params->hdr.size?=?0;?? } ?
總結
以上是生活随笔為你收集整理的uboot 命令分析(一) — bootm的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。