日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Exynos4412 Uboot 移植(四)—— Uboot引导内核过程分析

發布時間:2023/12/9 编程问答 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Exynos4412 Uboot 移植(四)—— Uboot引导内核过程分析 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

bootloader 要想啟動內核,可以直接跳到內核的第一個指令處,即內核的起始地址,這樣便可以完成內核的啟動工作了。但是要想啟動內核還需要滿足一些條件,如下所示:

1、cpu 寄存器設置

??? * R0 = 0
? ? * R1 = 機器類型 id
? ? * R2 = 啟動參數在內存中的起始地址

2、cpu 模式

? ? * 禁止所有中斷
? ??* 必須為SVC(超級用戶)模式

3、Cache、MMU

? ? * 關閉 MMU
? ? * 指令Cache可以開啟或者關閉
? ? * 數據Cache必須關閉

4、設備

? ? * DMA 設備應當停止工作

5、PC為內核的起始地址

? ? ?

? ? ? 這些需求都由 boot loader 實現,在常用的 uboot 中完成一系列的初始化后最后通過 bootm 命令加載 linux 內核。bootm 向將內核映像從各種媒介中讀出,存放在指定的位置;然后設置標記列表給內核傳遞參數;最后跳到內核的入口點去執行。


Uboot版本:u-boot-2013.01

一、bootm命令用法介紹如下:

? ? ? ?在 common/cmd_bootm.c 中可以看到bootm 的定義:


可以看到 bootm 命令使調用了do_bootm 函數。

do_bootm 函數

在cmd_bootm.c 第586行可以看到do_bootm函數的定義(為方便閱讀,對其中一些代碼進行了刪減,完整代碼請閱讀uboot源碼):

[cpp]?view plaincopy
  • /*******************************************************************/??
  • /*?bootm?-?boot?application?image?from?image?in?memory?*/??
  • /*******************************************************************/??
  • ??
  • int?do_bootm(cmd_tbl_t?*cmdtp,?int?flag,?int?argc,?char?*?const?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();//?關閉usb設備??
  • ??
  • ????ret?=?bootm_load_os(images.os,?&load_end,?1);//加載內核??
  • ??
  • ????lmb_reserve(&images.lmb,?images.os.load,?(load_end?-?images.os.load));??
  • ??
  • ????if?(images.os.type?==?IH_TYPE_STANDALONE)?{//如有需要,關閉內核的串口??
  • ????????if?(iflag)??
  • ????????????enable_interrupts();??
  • ????????/*?This?may?return?when?'autostart'?is?'no'?*/??
  • ????????bootm_start_standalone(iflag,?argc,?argv);??
  • ????????return?0;??
  • ????}??
  • ??
  • ????boot_fn?=?boot_os[images.os.os];//獲取啟動參數??
  • ??
  • ????arch_preboot_os();//啟動前準備??
  • ??
  • ????boot_fn(0,?argc,?argv,?&images);//啟動,不再返回??
  • ??
  • #ifdef?DEBUG??
  • ????puts("\n##?Control?returned?to?monitor?-?resetting...\n");??
  • #endif??
  • ????do_reset(cmdtp,?flag,?argc,?argv);??
  • ??
  • ????return?1;??
  • }??
  • 該函數的實現分為 3 個部分:

    a -- 首先通過 bootm_start 函數分析鏡像的信息;

    b -- 如果滿足判定條件則進入 bootm_load_os 函數進行加載;

    c -- 加載完成后就可以調用 boot_fn 開始啟動。

    1、bootm_start

    在cmd_bootm.c 第193行可以看到bootm_start函數的定義,?主要作用是填充內核相關信息

    [cpp]?view plaincopy
  • static?int?bootm_start(cmd_tbl_t?*cmdtp,?int?flag,?int?argc,?char?*?const?argv[])??
  • {??
  • ????void????????*os_hdr;??
  • ????int?????ret;??
  • ??
  • ????memset((void?*)&images,?0,?sizeof(images));??
  • ????images.verify?=?getenv_yesno("verify");//獲取環境變量??
  • ??
  • ????boot_start_lmb(&images);??
  • ??
  • ????bootstage_mark_name(BOOTSTAGE_ID_BOOTM_START,?"bootm_start");??
  • ??
  • ????/*獲取鏡像頭,加載地址,長度?*/??
  • ????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;??
  • ??
  • ????/*?查詢內核入口地址*/??
  • ????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.type?==?IH_TYPE_KERNEL_NOLOAD)?{??
  • ????????images.os.load?=?images.os.image_start;??
  • ????????images.ep?+=?images.os.load;??
  • ????}??
  • ??
  • ????if?(((images.os.type?==?IH_TYPE_KERNEL)?||??
  • ?????????(images.os.type?==?IH_TYPE_KERNEL_NOLOAD)?||??
  • ?????????(images.os.type?==?IH_TYPE_MULTI))?&&??
  • ????????(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;??
  • ????????}??
  • ??
  • #if?defined(CONFIG_OF_LIBFDT)??
  • ????????/*?找到設備樹,設備樹是linux?3.XX版本特有的?*/??
  • ????????ret?=?boot_get_fdt(flag,?argc,?argv,?&images,??
  • ???????????????????&images.ft_addr,?&images.ft_len);??
  • ????????if?(ret)?{??
  • ????????????puts("Could?not?find?a?valid?device?tree\n");??
  • ????????????return?1;??
  • ????????}??
  • ??
  • ????????set_working_fdt_addr(images.ft_addr);??
  • #endif??
  • ????}??
  • ??
  • ????images.os.start?=?(ulong)os_hdr;//賦值加載地址??
  • ????images.state?=?BOOTM_STATE_START;//更新狀態??
  • ??
  • ????return?0;??
  • }??
  • 該函數主要進行 鏡像的有效性判定、校驗、計算入口地址 等操作,大部分工作通過? boot_get_kernel -> image_get_kernel ?完成。


    2、bootm_load_os

    ? ? ? 在cmd_bootm.c 第317行可以看到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:??/*?鏡像采用?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

    在bootm_load_os 執行結束后,回到do_bootm 函數,調用boot_fn 運行linux 內核;


    boot_os 為函數指針數組,在cmd_bootm.c 136行有定義


    可以看出 boot_fn 函數指針指向的函數是位于?arch/arm/lib/bootm.c的 do_bootm_linux,這是內核啟動前最后的一個函數,該函數主要完成啟動參數的初始化,并將板子設定為滿足內核啟動的環境,代碼如下:


    可以看到 do_bootm_linux 實際調用的是?boot_jump_linux 函數。


    4、boot_jump_linux?

    在arch/arm/lib/bootm.c 下第326行有定義

    [cpp]?view plaincopy
  • /*?Subcommand:?GO?*/??
  • static?void?boot_jump_linux(bootm_headers_t?*images)??
  • {??
  • ????unsigned?long?machid?=?gd->bd->bi_arch_number;//獲取機器碼??
  • ????char?*s;??
  • ????void?(*kernel_entry)(int?zero,?int?arch,?uint?params);//內核入口函數??
  • ????unsigned?long?r2;??
  • ??
  • ????kernel_entry?=?(void?(*)(int,?int,?uint))images->ep;??
  • ??
  • ????s?=?getenv("machid");//從環境變量中獲取機器碼??
  • ????if?(s)?{??
  • ????????strict_strtoul(s,?16,?&machid);??
  • ????????printf("Using?machid?0x%lx?from?environment\n",?machid);??
  • ????}??
  • ??
  • ????debug("##?Transferring?control?to?Linux?(at?address?%08lx)"?\??
  • ????????"...\n",?(ulong)?kernel_entry);??
  • ????bootstage_mark(BOOTSTAGE_ID_RUN_OS);??
  • ????announce_and_cleanup();??
  • ??
  • #ifdef?CONFIG_OF_LIBFDT??
  • ????if?(images->ft_len)??
  • ????????r2?=?(unsigned?long)images->ft_addr;??
  • ????else??
  • #endif??
  • ????????r2?=?gd->bd->bi_boot_params;//將啟動參數地址賦給?r2???
  • ??
  • ????kernel_entry(0,?machid,?r2);??
  • }??

  • kernel_entry(0, machid, r2)?

    真正將控制權交給內核,?啟動內核;

    滿足arm架構linux內核啟動時的寄存器設置條件:第一個參數為0 ;第二個參數為板子id需與內核中的id匹配,第三個參數為啟動參數地址 。


    二、為內核設置啟動參數

    ? ? ? ? Uboot 也是通過標記列表向內核傳遞參數,標記在源代碼中定義為tag,是一個結構體,在arch/arm/include/asm/setup.h?中定義。


    tag_header 結構體定義如下:



    ? ? ?在一些內存標記、命令行標記的示例代碼就是取自Uboot 中的?setup_memory_tags、setup_commandline_tag函數,他們都是在?arch/arm/lib/bootm.c中定義。

    [cpp]?view plaincopy
  • #if?defined(CONFIG_SETUP_MEMORY_TAGS)?||?\??
  • ????defined(CONFIG_CMDLINE_TAG)?||?\??
  • ????defined(CONFIG_INITRD_TAG)?||?\??
  • ????defined(CONFIG_SERIAL_TAG)?||?\??
  • ????defined(CONFIG_REVISION_TAG)??
  • 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);??
  • }??
  • #endif??
  • ??
  • #ifdef?CONFIG_SETUP_MEMORY_TAGS??
  • 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);??
  • ????}??
  • }??
  • #endif??
  • ??
  • #ifdef?CONFIG_CMDLINE_TAG??
  • static?void?setup_commandline_tag(bd_t?*bd,?char?*commandline)??
  • {??
  • ????char?*p;??
  • ??
  • ????if?(!commandline)??
  • ????????return;??
  • ??
  • ????/*?eat?leading?white?space?*/??
  • ????for?(p?=?commandline;?*p?==?'?';?p++);??
  • ??
  • ????/*?skip?non-existent?command?lines?so?the?kernel?will?still?
  • ?????*?use?its?default?command?line.?
  • ?????*/??
  • ????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);??
  • }??
  • #endif??

  • 一般有 setup_memory_tags、setup_commandline_tag 這兩個標記就可以了,在配置文件Include/configs/fs4412.h中定義:

    總結

    以上是生活随笔為你收集整理的Exynos4412 Uboot 移植(四)—— Uboot引导内核过程分析的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。