[IMX][UBoot] 10.啟動流程 (6) - bootz 命令啟動 Linux

文章鏈接

UBoot 啟動流程 (1) - 基本流程

UBoot 啟動流程 (2) - 平臺前期初始化階段 - board_init_f

UBoot 啟動流程 (3) - UBoot 程序重定位 - relocate_code

UBoot 啟動流程 (4) - 平臺后期初始化階段 - board_init_r

UBoot 啟動流程 (5) - UBoot 運行階段 - main_loop

UBoot 啟動流程 (6) - bootz 命令啟動 Linux


目錄

1.系統鏡像相關的數據結構

1.1.鏡像的頭部信息 - image_header_t

1.2.鏡像的基本信息 - image_info_t

1.3.鏡像文件在 uboot 中的描述 - bootm_headers_t

1.3.1.鏡像頭部信息

1.3.2.系統信息

1.3.3.引導狀態

1.3.4.線性內存管理器 - LMB

2.booz 命令啟動內核的流程 - do_bootz()

2.1.根據階段執行對應的函數 - do_bootm_states()

2.2.images 初始化 - bootz_start()

2.2.1.初始化 images 和 LMB - bootm_start()

2.2.2.設置加載地址

2.2.3.檢查 zImage 是否有效 - bootz_setup()

2.2.4.在 LMB 中為鏡像文件預留空間 - lmb_reserve()

2.2.5.查找 RAMDisk 和設備樹 - bootm_find_images()

2.3.禁用中斷 - bootm_disable_interrupts()

2.4.準備運行環境并啟動內核

2.4.1.查找 OS 對應的啟動函數 - bootm_os_get_boot_func()

2.4.2.Linux 啟動函數 - do_bootm_linux()

2.4.3.Linux 運行環境準備 - boot_prep_linux()

2.4.4.跳轉至 Linux 內核運行 - boot_jump_linux()

3.啟動過程概覽


1.系統鏡像相關的數據結構

  • image_header_t 類型的結構體,保存系統鏡像的頭部信息,如 CRC 校驗碼、操作系統類型等;

  • image_info_t 類型的結構體,保存系統鏡像的基本信息,如系統鏡像的起始地址,加載地址等;

  • bootm_headers_t 類型的結構體,保存系統鏡像的所有信息 (包括鏡像頭部信息、鏡像基本信息等),以及平臺中涉及系統加載的相關信息 (如系統鏡像在內存中的起始地址,設備樹的加載地址等);

1.1.鏡像的頭部信息 - image_header_t

image_header_t 保存系統鏡像的頭部信息:

// include/image.h
typedef struct image_header {__be32      ih_magic;   /* Image Header Magic Number    */__be32      ih_hcrc;    /* Image Header CRC Checksum    */__be32      ih_time;    /* Image Creation Timestamp */__be32      ih_size;    /* Image Data Size      */__be32      ih_load;    /* Data  Load  Address      */__be32      ih_ep;      /* Entry Point Address      */__be32      ih_dcrc;    /* Image Data CRC Checksum  */uint8_t     ih_os;      /* Operating System     */uint8_t     ih_arch;    /* CPU architecture     */uint8_t     ih_type;    /* Image Type           */uint8_t     ih_comp;    /* Compression Type     */uint8_t     ih_name[IH_NMLEN];  /* Image Name       */
} image_header_t;

其成員的含義如下 (其中 __be32 表示大端序 uint32 數據類型):

  • ih_magic:鏡像頭部的魔數,根據該值判斷文件是否為鏡像文件;

  • ih_hcrc:鏡像頭部的 CRC 校驗碼,判斷文件是否有效 (是否出錯);

  • ih_time:該鏡像文件的創建時間;

  • ih_size:整個系統鏡像文件的大小;

  • ih_load:系統在內存中的加載地址 (程序存儲起始地址);

  • ih_ep:系統在內存中的運行地址 (程序運行起始地址);

  • ih_dcrc:整個系統鏡像文件的 CRC 校驗碼;

  • ih_os:系統代碼,例如,Linux 系統的 ih_os 值為 0x05:

// include/image.h
/** Operating System Codes*/
#define IH_OS_INVALID       0   /* Invalid OS */
#define IH_OS_OPENBSD       1   /* OpenBSD    */
...
#define IH_OS_LINUX         5   /* Linux      */
...
#define IH_OS_QNX           16  /* QNX        */
...
#define IH_OS_OPENRTOS      24  /* OpenRTOS   */
  • ih_arch:CPU 的架構代碼,例如,ARM 架構 SoC 的 ih_arch 為 0x02:

// include/image.h
/** CPU Architecture Codes (supported by Linux)*/
#define IH_ARCH_INVALID     0   /* Invalid CPU  */
...
#define IH_ARCH_ARM         2   /* ARM      */
#define IH_ARCH_I386        3   /* Intel x86    */
...
#define IH_ARCH_MIPS        5   /* MIPS     */
...
#define IH_ARCH_ARM64       22  /* ARM64    */
...
#define IH_ARCH_X86_64      24  /* AMD x86_64, Intel and Via */
  • ih_type:鏡像類型,例如系統鏡像、Boot 鏡像等:

// include/image.h
/** Image Types** "Standalone Programs" are directly runnable in the environment*  provided by U-Boot; it is expected that (if they behave*  well) you can continue to work in U-Boot after return from*  the Standalone Program.* "OS Kernel Images" are usually images of some Embedded OS which*  will take over control completely. Usually these programs*  will install their own set of exception handlers, device*  drivers, set up the MMU, etc. - this means, that you cannot*  expect to re-enter U-Boot except by resetting the CPU.* "RAMDisk Images" are more or less just data blocks, and their*  parameters (address, size) are passed to an OS kernel that is*  being started.* "Multi-File Images" contain several images, typically an OS*  (Linux) kernel image and one or more data images like*  RAMDisks. This construct is useful for instance when you want*  to boot over the network using BOOTP etc., where the boot*  server provides just a single image file, but you want to get*  for instance an OS kernel and a RAMDisk image.**  "Multi-File Images" start with a list of image sizes, each*  image size (in bytes) specified by an "uint32_t" in network*  byte order. This list is terminated by an "(uint32_t)0".*  Immediately after the terminating 0 follow the images, one by*  one, all aligned on "uint32_t" boundaries (size rounded up to*  a multiple of 4 bytes - except for the last file).** "Firmware Images" are binary images containing firmware (like*  U-Boot or FPGA images) which usually will be programmed to*  flash memory.** "Script files" are command sequences that will be executed by*  U-Boot's command interpreter; this feature is especially*  useful when you configure U-Boot to use a real shell (hush)*  as command interpreter (=> Shell Scripts).*/
#define IH_TYPE_INVALID     0   /* Invalid Image        */
#define IH_TYPE_STANDALONE  1   /* Standalone Program       */
#define IH_TYPE_KERNEL      2   /* OS Kernel Image      */
...
#define IH_TYPE_RKIMAGE     23  /* Rockchip Boot Image      */
#define IH_TYPE_RKSD        24  /* Rockchip SD card     */
#define IH_TYPE_RKSPI       25  /* Rockchip SPI image       */
#define IH_TYPE_ZYNQIMAGE   26  /* Xilinx Zynq Boot Image */
#define IH_TYPE_COUNT       27  /* Number of image types */
  • ih_comp:文件壓縮類型:

// include/image.h
/** Compression Types*/
#define IH_COMP_NONE        0   /* No    Compression Used   */
#define IH_COMP_GZIP        1   /* gzip  Compression Used   */
#define IH_COMP_BZIP2       2   /* bzip2 Compression Used   */
#define IH_COMP_LZMA        3   /* lzma  Compression Used   */
#define IH_COMP_LZO         4   /* lzo   Compression Used   */
#define IH_COMP_LZ4         5   /* lz4   Compression Used   */
  • ih_name[IH_NMLEN]:鏡像名稱,名稱最大長度為 32 個字符 (包含 31 個有效字符和 1 個結束符 \0):

// include/image.h
#define IH_NMLEN        32  /* Image Name Length        */

1.2.鏡像的基本信息 - image_info_t

image_info_t 類型的結構體中,包含了系統鏡像的基本信息,如起始地址、所支持的 CPU 架構等:

// include/image.h
typedef struct image_info {ulong       start, end;     /* start/end of blob */ulong       image_start, image_len; /* start of image within blob, len of image */ulong       load;           /* load addr for the image */uint8_t     comp, type, os; /* compression, type of image, os type */uint8_t     arch;           /* CPU architecture */
} image_info_t;

其各個成員的含義如下:

  • start, end:鏡像文件在存儲介質中的起始地址和結束地址;

  • image_start, image_len:系統程序在文件中的起始地址和長度;

  • load:系統程序在內存中的加載地址 (程序的起始地址,如 0x80008000 等);

  • comp:系統鏡像的壓縮格式;

  • type:鏡像類型,例如系統鏡像、Boot 鏡像等:

  • os:系統類型,如 Linux、Windows 等;

  • arch:系統對應的架構,如 ARM、MIPS 等;

1.3.鏡像文件在 uboot 中的描述 - bootm_headers_t

uboot 中使用 bootm_headers_t 類型的變量 images 描述鏡像文件 (如 zImage、uImage 等):

// include/image.h
typedef struct bootm_headers {/** Legacy os image header, if it is a multi component image* then boot_get_ramdisk() and get_fdt() will attempt to get* data from second and third component accordingly.*/image_header_t  *legacy_hdr_os;      /* image header pointer */image_header_t   legacy_hdr_os_copy; /* header copy */ulong            legacy_hdr_valid;
...image_info_t    os; /* os image info */ulong           ep; /* entry point of OS */ulong       rd_start, rd_end; /* ramdisk start/end */char        *ft_addr;   /* flat dev tree address */ulong       ft_len;     /* length of flat device tree */ulong       initrd_start;ulong       initrd_end;ulong       cmdline_start;ulong       cmdline_end;bd_t        *kbd;
...int     verify;     /* getenv("verify")[0] != 'n' */#define BOOTM_STATE_START   (0x00000001)
#define BOOTM_STATE_FINDOS  (0x00000002)
#define BOOTM_STATE_FINDOTHER   (0x00000004)
#define BOOTM_STATE_LOADOS  (0x00000008)
#define BOOTM_STATE_RAMDISK (0x00000010)
#define BOOTM_STATE_FDT     (0x00000020)
#define BOOTM_STATE_OS_CMDLINE  (0x00000040)
#define BOOTM_STATE_OS_BD_T (0x00000080)
#define BOOTM_STATE_OS_PREP (0x00000100)
#define BOOTM_STATE_OS_FAKE_GO  (0x00000200)    /* 'Almost' run the OS */
#define BOOTM_STATE_OS_GO   (0x00000400)int     state;
...struct lmb  lmb;        /* for memory mgmt */
...
} bootm_headers_t;
-----------------------------------------------------------------------------// cmd/bootm.c
bootm_headers_t images;     /* pointers to os/initrd/fdt images */

1.3.1.鏡像頭部信息

  • *legacy_hdr_os 為鏡像頭部信息的指針;

  • legacy_hdr_os_copy 為鏡像頭部信息的副本;

  • legacy_hdr_valid 標記鏡像頭部信息是否有效;

// include/image.h
typedef struct bootm_headers {image_header_t  *legacy_hdr_os;      /* image header pointer */image_header_t   legacy_hdr_os_copy; /* header copy */ulong            legacy_hdr_valid;...

1.3.2.系統信息

  • os 為系統鏡像的基本信息;

  • ep 為系統程序在鏡像文件中的起始地址;

  • rd_start, rd_end 分別為虛擬磁盤 RAMDisk 在內存中的起始地址和結束地址;

  • *ft_addr 為設備樹的起始地址;

  • ft_len 為設備樹的長度;

  • initrd_start 為初始化 RAMDisk 的起始地址;

  • initrd_end 為初始化 RAMDisk 的結束地址;

  • cmdline_start 為 Linux 啟動命令行的起始地址;

  • cmdline_end 為 Linux 啟動命令行的結束地址;

  • *kbd 為開發板的硬件信息 bd 的指針;

  • verify 為校驗使能標志,若環境變量 verify 不為 n 則啟用校驗;

// include/image.h
typedef struct bootm_headers {...image_info_t    os; /* os image info */ulong           ep; /* entry point of OS */ulong       rd_start, rd_end; /* ramdisk start/end */char        *ft_addr;   /* flat dev tree address */ulong       ft_len;     /* length of flat device tree */ulong       initrd_start;ulong       initrd_end;ulong       cmdline_start;ulong       cmdline_end;bd_t        *kbd;
...int     verify;     /* getenv("verify")[0] != 'n' */

1.3.3.引導狀態

  • BOOTM_STATE_xxx 宏定義內核的引導狀態,即內核加載過程中的各個階段;

  • state 為內核當前的引導狀態;

// include/image.h
typedef struct bootm_headers {...
#define BOOTM_STATE_START   (0x00000001)
#define BOOTM_STATE_FINDOS  (0x00000002)
#define BOOTM_STATE_FINDOTHER   (0x00000004)
#define BOOTM_STATE_LOADOS  (0x00000008)
#define BOOTM_STATE_RAMDISK (0x00000010)
#define BOOTM_STATE_FDT     (0x00000020)
#define BOOTM_STATE_OS_CMDLINE  (0x00000040)
#define BOOTM_STATE_OS_BD_T (0x00000080)
#define BOOTM_STATE_OS_PREP (0x00000100)
#define BOOTM_STATE_OS_FAKE_GO  (0x00000200)    /* 'Almost' run the OS */
#define BOOTM_STATE_OS_GO   (0x00000400)int     state;

1.3.4.線性內存管理器 - LMB

  • lmb (Logical Memory Blocks) 為線性內存塊管理器,用于動態內存分配:

    • memory 為可用的內存區域;

    • reserved 為預留內存區域,如設備映射內存、啟動代碼所占用的內存;

// include/lmb.h
struct lmb {struct lmb_region memory;struct lmb_region reserved;
};
-------------------------------------------// include/image.h
typedef struct bootm_headers {...struct lmb  lmb;        /* for memory mgmt */

2.booz 命令啟動內核的流程 - do_bootz()

bootz 命令用于啟動 Linux 內核,其對應的執行函數為 do_bootz(),包含如下幾個階段:

  • bootz_start() 初始化 images 結構體,設置系統的加載地址,并為系統鏡像預留內存;

  • bootm_disable_interrupts() 在啟動過程中禁用所有中斷;

  • 準備內核的執行環境并啟動內核;

// cmd/bootm.c
int do_bootz(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{...// 初始化 images 結構體if (bootz_start(cmdtp, flag, argc, argv, &images))return 1;/** We are doing the BOOTM_STATE_LOADOS state ourselves, so must* disable interrupts ourselves*/// 內核啟動過程中禁用中斷bootm_disable_interrupts(); // 標記需要啟動的系統類型為 Linuximages.os.os = IH_OS_LINUX;// 準備運行環境并啟動內核ret = do_bootm_states(cmdtp, flag, argc, argv,BOOTM_STATE_OS_PREP | BOOTM_STATE_OS_FAKE_GO |BOOTM_STATE_OS_GO,&images, 1);return ret;
}

2.1.根據階段執行對應的函數 - do_bootm_states()

內核的加載過程分為多個階段,do_bootm_states() 根據階段標志 states 調用對應的函數:

// common/bootm.c
int do_bootm_states(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[],int states, bootm_headers_t *images, int boot_progress)
{.../* Work through the states and see how far we get. We stop on any error. */// 初始化階段if (states & BOOTM_STATE_START)ret = bootm_start(cmdtp, flag, argc, argv);if (!ret && (states & BOOTM_STATE_FINDOS))ret = bootm_find_os(cmdtp, flag, argc, argv);if (!ret && (states & BOOTM_STATE_FINDOTHER)) {ret = bootm_find_other(cmdtp, flag, argc, argv);argc = 0;   /* consume the args */}/* Load the OS */// 加載內核if (!ret && (states & BOOTM_STATE_LOADOS)) {ulong load_end;iflag = bootm_disable_interrupts();ret = bootm_load_os(images, &load_end, 0);if (ret == 0)lmb_reserve(&images->lmb, images->os.load,(load_end - images->os.load));...}/* Relocate the ramdisk */// RAMDisk 重定位if (!ret && (states & BOOTM_STATE_RAMDISK)) {ulong rd_len = images->rd_end - images->rd_start;ret = boot_ramdisk_high(&images->lmb, images->rd_start,rd_len, &images->initrd_start, &images->initrd_end);...}// 設備樹重定位if (!ret && (states & BOOTM_STATE_FDT)) {boot_fdt_add_mem_rsv_regions(&images->lmb, images->ft_addr);ret = boot_relocate_fdt(&images->lmb, &images->ft_addr,&images->ft_len);}// 查找內核對應的啟動函數/* From now on, we need the OS boot function */...boot_fn = bootm_os_get_boot_func(images->os.os);need_boot_fn = states & (BOOTM_STATE_OS_CMDLINE |BOOTM_STATE_OS_BD_T | BOOTM_STATE_OS_PREP |BOOTM_STATE_OS_FAKE_GO | BOOTM_STATE_OS_GO);if (boot_fn == NULL && need_boot_fn) {...}// 執行內核對應的啟動函數/* Call various other states that are not generally used */if (!ret && (states & BOOTM_STATE_OS_CMDLINE))ret = boot_fn(BOOTM_STATE_OS_CMDLINE, argc, argv, images);if (!ret && (states & BOOTM_STATE_OS_BD_T))ret = boot_fn(BOOTM_STATE_OS_BD_T, argc, argv, images);if (!ret && (states & BOOTM_STATE_OS_PREP))ret = boot_fn(BOOTM_STATE_OS_PREP, argc, argv, images);...// 運行內核/* Now run the OS! We hope this doesn't return */if (!ret && (states & BOOTM_STATE_OS_GO))ret = boot_selected_os(argc, argv, BOOTM_STATE_OS_GO, images, boot_fn);|--> boot_fn(state, argc, argv, images);// 啟用中斷,執行復位/* Deal with any fallout */
err:if (iflag)enable_interrupts();if (ret == BOOTM_ERR_UNIMPLEMENTED)bootstage_error(BOOTSTAGE_ID_DECOMP_UNIMPL);else if (ret == BOOTM_ERR_RESET)do_reset(cmdtp, flag, argc, argv);return ret;
}

2.2.images 初始化 - bootz_start()

uboot 使用 bootm_headers_t 類型的結構體 images 描述鏡像文件,內核啟動前,先調用 bootz_start() 初始化 images 結構體以及運行環境 (分配內存、查找設備樹等):

// cmd/bootm.c
static int bootz_start(cmd_tbl_t *cmdtp, int flag, int argc,char * const argv[], bootm_headers_t *images)
{...// 執行初始化階段 BOOTM_STATE_STARTret = do_bootm_states(cmdtp, flag, argc, argv, BOOTM_STATE_START, images, 1);/* Setup Linux kernel zImage entry point */// 設置 OS 在內存中的起始地址 0x80800000if (!argc) {images->ep = load_addr;debug("*  kernel: default image load address = 0x%08lx\n", load_addr);} else {images->ep = simple_strtoul(argv[0], NULL, 16);debug("*  kernel: cmdline image address = 0x%08lx\n", images->ep);}// 檢查鏡像文件是否有效ret = bootz_setup(images->ep, &zi_start, &zi_end);...// 在內存中為鏡像文件預留空間lmb_reserve(&images->lmb, images->ep, zi_end - zi_start);/** Handle the BOOTM_STATE_FINDOTHER state ourselves as we do not* have a header that provide this informaiton.*/// 查找 RAMDisk、設備樹等其他模塊if (bootm_find_images(flag, argc, argv))return 1;...return 0;
}

2.2.1.初始化 images 和 LMB - bootm_start()

do_bootm_states() 調用 BOOTM_STATE_START 階段對應的函數 bootm_start():

  • 調用 memset() 清空 images 結構體;

  • boot_start_lmb() 初始化 LMB,并創建一個 dummy lmb,同時在內存中分配內存;

  • bootstage_mark_name() 記錄當前階段的相關信息;

// cmd/bootm.c
static int bootz_start(cmd_tbl_t *cmdtp, int flag, int argc,char * const argv[], bootm_headers_t *images)
{...// 執行初始化階段 BOOTM_STATE_STARTret = do_bootm_states(cmdtp, flag, argc, argv, BOOTM_STATE_START, images, 1);
-------------------------------------------------------------------------------   // common/bootm.c
int do_bootm_states(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[],int states, bootm_headers_t *images, int boot_progress)
{...if (states & BOOTM_STATE_START)ret = bootm_start(cmdtp, flag, argc, argv);
-------------------------------------------------------------------------------// common/bootm.c
static int bootm_start(cmd_tbl_t *cmdtp, int flag, int argc,char * const argv[])
{// 清空 images 結構體memset((void *)&images, 0, sizeof(images));// 檢查環境變量 verify,確定是否需要對鏡像文件進行校驗images.verify = getenv_yesno("verify"); // 初始化 LMBboot_start_lmb(&images);|--> lmb_init(&images->lmb); // 將 LMB 的地址、大小等全部置 0,計數值初始化為 1|--> lmb_add(&images->lmb, (phys_addr_t)mem_start, mem_size); // 分配一塊 lmb|--> arch_lmb_reserve(&images->lmb);|--> sp = get_sp(); // 獲取當前的 SP 指針|--> sp -= 4096; // 4K 字節對齊|--> lmb_reserve(lmb, sp, ...); // 分配棧空間 |--> board_lmb_reserve(&images->lmb); // 未實現該方法// 記錄當前階段的相關信息bootstage_mark_name(BOOTSTAGE_ID_BOOTM_START, "bootm_start");images.state = BOOTM_STATE_START;return 0;
}

2.2.2.設置加載地址

用戶可以在命令行中指定 OS 的加載地址,若未指定則使用 load_addr 中保存的默認地址:

// cmd/bootm.c
static int bootz_start(cmd_tbl_t *cmdtp, int flag, int argc,char * const argv[], bootm_headers_t *images)
{...// 設置 OS 在內存中的起始地址if (!argc) { // 使用默認加載地址images->ep = load_addr;debug("*  kernel: default image load address = 0x%08lx\n", load_addr);} else { // 使用命令行中指定的加載地址images->ep = simple_strtoul(argv[0], NULL, 16);debug("*  kernel: cmdline image address = 0x%08lx\n", images->ep);}

2.2.3.檢查 zImage 是否有效 - bootz_setup()

  • 使用 map_sysmem() 將鏡像地址映射為可訪問的指針 (獲取鏡像文件的頭部信息);

  • 檢查魔數是否為 Linux 鏡像文件;

  • 打印鏡像文件的起始地址和結束地址;

// cmd/bootm.c
static int bootz_start(cmd_tbl_t *cmdtp, int flag, int argc,char * const argv[], bootm_headers_t *images)
{...// 檢查鏡像文件是否有效ret = bootz_setup(images->ep, &zi_start, &zi_end);
--------------------------------------------------------------// arch/arm/lib/bootm.c
int bootz_setup(ulong image, ulong *start, ulong *end)
{struct zimage_header *zi;// 檢查鏡像文件是否有效zi = (struct zimage_header *)map_sysmem(image, 0); // 獲取鏡像文件的頭部信息if (zi->zi_magic != LINUX_ARM_ZIMAGE_MAGIC) { // 檢查是否為 Linux 鏡像文件puts("Bad Linux ARM zImage magic!\n");return 1;}// 打印鏡像文件在內存中的起始地址和結束地址*start = zi->zi_start;*end = zi->zi_end;printf("Kernel image @ %#08lx [ %#08lx - %#08lx ]\n", image, *start, *end);return 0;
}

2.2.4.在 LMB 中為鏡像文件預留空間 - lmb_reserve()

調用 lmb_reserve() 為鏡像文件預留內存空間 (分配 LMB):

// cmd/bootm.c
static int bootz_start(cmd_tbl_t *cmdtp, int flag, int argc,char * const argv[], bootm_headers_t *images)
{...// 在內存中為鏡像文件預留空間lmb_reserve(&images->lmb, images->ep, zi_end - zi_start);
----------------------------------------------------------------// lib/lmb.c
long lmb_reserve(struct lmb *lmb, phys_addr_t base, phys_size_t size)
{struct lmb_region *_rgn = &(lmb->reserved);return lmb_add_region(_rgn, base, size); // 分配一個 LMB
}

2.2.5.查找 RAMDisk 和設備樹 - bootm_find_images()

  • boot_get_ramdisk() 查找 RAMDisk (IMX 未使用);

  • boot_get_fdt() 查找設備樹,并設置設備樹文件的起始地址和長度;

// cmd/bootm.c
static int bootz_start(cmd_tbl_t *cmdtp, int flag, int argc,char * const argv[], bootm_headers_t *images)
{...// 查找 RAMDisk、設備樹等其他模塊if (bootm_find_images(flag, argc, argv))return 1;
-----------------------------------------------------------// common/bootm.c
int bootm_find_images(int flag, int argc, char * const argv[])
{int ret;/* find ramdisk */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)/* find flattened device tree */// 在鏡像文件中查找設備樹ret = boot_get_fdt(flag, argc, argv, IH_ARCH_DEFAULT, &images,&images.ft_addr, &images.ft_len);|--> ... // 在鏡像文件中查找設備樹|--> *of_flat_tree = fdt_blob; // 將設備樹的地址保存至 images.ft_addr|--> *of_size = fdt_totalsize(fdt_blob); // 將設備樹的長度保存至 images.ft_len...// 為設備樹分配內存,并將設備樹在內存中的起始地址,保存至環境變量 fdtaddr 中set_working_fdt_addr((ulong)images.ft_addr);|--> buf = map_sysmem(addr, 0);|--> setenv_hex("fdtaddr", addr);
#endif...return 0;
}

2.3.禁用中斷 - bootm_disable_interrupts()

內核加載過程中需要禁用中斷,同時禁用以太網和 USB:

// cmd/bootm.c
int do_bootz(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{...// 內核啟動過程中禁用中斷bootm_disable_interrupts(); 
------------------------------------------------------------------------// common/bootm.c
ulong bootm_disable_interrupts(void)
{ulong iflag;/** We have reached the point of no return: we are going to* overwrite all exception vector code, so we cannot easily* recover from any failures any more...*/iflag = disable_interrupts();... // 禁用以太網和 USBreturn iflag;
}
------------------------------------------------------------------------// arch/arm/lib/interrupts.c
int disable_interrupts (void)
{unsigned long old,temp;__asm__ __volatile__("mrs %0, cpsr\n""orr %1, %0, #0xc0\n""msr cpsr_c, %1": "=r" (old), "=r" (temp):: "memory");return (old & 0x80) == 0;
}

2.4.準備運行環境并啟動內核

分別調用 BOOTM_STATE_OS_PREPBOOTM_STATE_OS_FAKE_GOBOOTM_STATE_OS_GO 階段對應的函數:

// cmd/bootm.c
int do_bootz(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{...// 標記需要啟動的系統類型為 Linuximages.os.os = IH_OS_LINUX; // 準備運行環境并啟動內核ret = do_bootm_states(cmdtp, flag, argc, argv,BOOTM_STATE_OS_PREP | BOOTM_STATE_OS_FAKE_GO |BOOTM_STATE_OS_GO,&images, 1);

2.4.1.查找 OS 對應的啟動函數 - bootm_os_get_boot_func()

不同操作系統所使用的啟動函數不同,啟動函數定義在 boot_os_fn 類型的結構體數組 boot_os 中,Linux 系統對應的啟動函數為 do_bootm_linux():

// common/bootm_os.c
static boot_os_fn *boot_os[] = {[IH_OS_U_BOOT] = do_bootm_standalone,
#ifdef CONFIG_BOOTM_LINUX[IH_OS_LINUX] = do_bootm_linux, // Linux 系統對應的啟動函數
#endif
#ifdef CONFIG_BOOTM_NETBSD[IH_OS_NETBSD] = do_bootm_netbsd,
#endif
...
#ifdef CONFIG_BOOTM_OPENRTOS[IH_OS_OPENRTOS] = do_bootm_openrtos,
#endif
};

do_bootm_states() 啟動內核時,會調用 bootm_os_get_boot_func() 查找系統對應的啟動函數:

// common/bootm.c
int do_bootm_states(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[],int states, bootm_headers_t *images, int boot_progress)
{.../* From now on, we need the OS boot function */...boot_fn = bootm_os_get_boot_func(images->os.os); // 查找系統對應的啟動函數|--> return boot_os[os]; // boot_fn = do_bootm_linux()need_boot_fn = states & (BOOTM_STATE_OS_CMDLINE |BOOTM_STATE_OS_BD_T | BOOTM_STATE_OS_PREP |BOOTM_STATE_OS_FAKE_GO | BOOTM_STATE_OS_GO);.../* Call various other states that are not generally used */...// 執行 do_bootm_linux()if (!ret && (states & BOOTM_STATE_OS_PREP))ret = boot_fn(BOOTM_STATE_OS_PREP, argc, argv, images);

do_bootz() 啟動 Linux 內核時,設置了 BOOTM_STATE_OS_PREP 標志,表示需要啟動的系統類型為 Linux,因此會執行 do_bootm_linux()

2.4.2.Linux 啟動函數 - do_bootm_linux()

檢查啟動階段標志,并執行對應的系統環境準備函數 boot_prep_linux() 或運行函數 boot_jump_linux():

// arch/arm/lib/bootm.c
int do_bootm_linux(int flag, int argc, char * const argv[],bootm_headers_t *images)
{/* No need for those on ARM */if (flag & BOOTM_STATE_OS_BD_T || flag & BOOTM_STATE_OS_CMDLINE)return -1;// 準備啟動環境if (flag & BOOTM_STATE_OS_PREP) {boot_prep_linux(images); return 0;}// 運行 Linux 內核if (flag & (BOOTM_STATE_OS_GO | BOOTM_STATE_OS_FAKE_GO)) {boot_jump_linux(images, flag); return 0;}boot_prep_linux(images);boot_jump_linux(images, flag);return 0;
}

2.4.3.Linux 運行環境準備 - boot_prep_linux()

處理環境變量 bootargs,其中保存了傳遞給 Linux 內核的啟動參數:

// arch/arm/lib/bootm.c
static void boot_prep_linux(bootm_headers_t *images)
{// 從環境變量 bootargs 中讀取 Linux 的啟動命令char *commandline = getenv("bootargs");if (IMAGE_ENABLE_OF_LIBFDT && images->ft_len) { // 檢查是否使用設備樹debug("using: FDT\n");if (image_setup_linux(images)) { // 準備啟動環境printf("FDT creation failed! hanging...");hang();}} else if (BOOTM_ENABLE_TAGS) {...} else {printf("FDT and ATAGS support not compiled in - hanging\n");hang();}
}

若配置使用設備樹,則調用 image_setup_linux() 準備啟動環境,包括調整設備樹、分配存儲 cmdline 的內存、處理 Ramdisk 以及設置啟動參數:

  • 將 LMB 中的內存區域添加至設備樹;

  • 在內核中為 cmdline 分配內存;

  • 重定位 RAMDisk;

  • 重定位設備樹;

  • 設置設備樹中內存相關的節點;

// common/image.c
int image_setup_linux(bootm_headers_t *images)
{ulong of_size = images->ft_len; // 獲取設備樹的長度char **of_flat_tree = &images->ft_addr; // 獲取設備樹在鏡像文件中的起始地址ulong *initrd_start = &images->initrd_start; // RAMDisk 的起始地址ulong *initrd_end = &images->initrd_end; // RAMDisk 的結束地址struct lmb *lmb = &images->lmb; // LMBulong rd_len;int ret;// 將 LMB 中預留的內存區域,添加到設備樹的 /memory 節點和 /reserved-memory 節點,// 使內核可以知道需要預留這些區域if (IMAGE_ENABLE_OF_LIBFDT)boot_fdt_add_mem_rsv_regions(lmb, *of_flat_tree);// 在內核中分配內存,用于存儲 cmdlineif (IMAGE_BOOT_GET_CMDLINE) {ret = boot_get_cmdline(lmb, &images->cmdline_start, &images->cmdline_end);if (ret) {puts("ERROR with allocation of cmdline\n");return ret;}}// 將 RAMDisk 重定位至高地址的內存中,避免和內核文件沖突if (IMAGE_ENABLE_RAMDISK_HIGH) {rd_len = images->rd_end - images->rd_start;ret = boot_ramdisk_high(lmb, images->rd_start, rd_len,initrd_start, initrd_end);if (ret)return ret;}// 如果當前設備樹的位置不合適,例如,與內核加載區域沖突,// 則使用 LMB 分配新的內存區域并復制設備樹if (IMAGE_ENABLE_OF_LIBFDT) {ret = boot_relocate_fdt(lmb, of_flat_tree, &of_size);if (ret)return ret;}// 設置設備樹中的 bootargs 節點,更新 /memory 節點的內存范圍,添加預留內存的相關描述if (IMAGE_ENABLE_OF_LIBFDT && of_size) {ret = image_setup_libfdt(images, *of_flat_tree, of_size, lmb);if (ret)return ret;}return 0;
}

2.4.4.跳轉至 Linux 內核運行 - boot_jump_linux()

查找 Linux 內核的運行函數 kernel_entry (Linux 系統的第一個函數,類似于 _start) 并執行

內核運行前,會調用 announce_and_cleanup() 打印啟動信息并清除之前的緩存 (I-Cache 和 D-Cache):

// arch/arm/lib/bootm.c
static void boot_jump_linux(bootm_headers_t *images, int flag)
{
#ifdef CONFIG_ARM64...
#elseunsigned long machid = gd->bd->bi_arch_number; // 開發板的硬件 ID (使用設備樹時無效)char *s;void (*kernel_entry)(int zero, int arch, uint params);unsigned long r2;int fake = (flag & BOOTM_STATE_OS_FAKE_GO);// 運行 Linux 內核的入口kernel_entry = (void (*)(int, int, uint))images->ep;...// 輸出調試信息,表示即將開始 Linux 內核的運行階段debug("## Transferring control to Linux (at address %08lx)" \"...\n", (ulong) kernel_entry);// 記錄當前的運行階段bootstage_mark(BOOTSTAGE_ID_RUN_OS);// 打印啟動信息:starting kernel ... 并初始化緩存 (清理掉原來的緩存)announce_and_cleanup(fake);|--> cleanup_before_linux(); // 清除 I-Cache 和 D-Cache// 獲取設備樹的地址if (IMAGE_ENABLE_OF_LIBFDT && images->ft_len)r2 = (unsigned long)images->ft_addr;elser2 = gd->bd->bi_boot_params;...// 運行內核kernel_entry(0, machid, r2);...
}

因為后續將要運行 Linux 內核,之前 uboot 所使用的數據緩存與指令緩存不再需要,調用 announce_and_cleanup() 將其清除,最后執行 kernel_entry() 運行 Linux 內核,其第二個參數 r2 為設備樹的地址或者啟動命令 bootargs

kernel_entry() 由操作系統定義,是其運行的第一個函數,類似 uboot 的 _start() 函數

3.啟動過程概覽

綜上,uboot 啟動 Linux 內核的過程大致如下圖所示:

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/912968.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/912968.shtml
英文地址,請注明出處:http://en.pswp.cn/news/912968.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

TCP 三次握手協商 MSS 前,如何確定 MSS 值(結合 Linux 內核源碼分析)

文章目錄 一、SYN總結影響 SYN MSS 的因素 二、SYNACK總結影響 SYNACK MSS 的因素 結合 Linux 內核源碼 一、SYN 總結影響 SYN MSS 的因素 套接字選項 TCP_MAXSEG路由選項 advmss出口 MTU 減去 40(TCP 和 IP 的固定首部大小)IPV4_MAX_PMTU - 40(同上) 二、SYNACK 總結影響 SY…

面試150 矩陣置0

思路 我們使用兩個標記集合,分別記錄當矩陣的元素為0的時候的橫、縱坐標。然后在對矩陣元素進行遍歷,如果所在行或者所在列的索引在集合中,對應的矩陣元素修改為0即可 class Solution:def setZeroes(self, matrix: List[List[int]]) -> N…

Element UI 完整使用實戰示例

以下是 Element UI 的完整使用實戰示例,涵蓋從環境搭建、基礎組件使用到項目實戰的全流程,結合多個實際場景和代碼示例: 一、環境搭建與基礎配置 1. 安裝 Element UI 通過 npm 或 yarn 安裝: npm install element-ui --save # …

C# 線程同步(一)同步概念介紹

目錄 1.阻塞(Blocking) 2.阻塞 VS 輪詢 3.線程狀態 到目前為止,我們已經闡述了如何在線程上啟動任務、配置線程以及實現雙向數據傳遞。同時,我們也說明了局部變量是線程私有的,而引用可以通過共享字段在線程間傳遞以…

解決leetcode第3588題.找到最大三角形面積

3588.找到最大三角形面積難度:中等問題描述:給你一個二維數組coords,大小為nx2,表示一個無限笛卡爾平面上n個點的坐標。找出一個最大三角形的兩倍面積,其中三角形的三個頂點來自coords中的任意三個點,并且該…

WIFI 安全測試記錄

之前為實訓課特意買的無線網卡沒用上,但是我怎么可能讓他荒廢。所以用了幾個下午,淺學了WiFi,當然沒找到什么好教材,自己摸索著學的很基礎,主要是當練習了,特此把我此前學習…WiFi密碼實踐過程寫上來。 省流…

android14設置--網絡--Internet副標題修改

收銀機訂制項目 插SIM卡,設備使用數據流量時,設置–網絡–Internet副標題顯示對應SIM卡運營商名稱,客戶要求修改這時的名稱(注意圖標也要同步修改) packages\apps\Settings\src\com\android\settings\network\InternetPreferenceController.j…

Web3區塊鏈有哪些崗位?

Web3區塊鏈領域的崗位豐富多樣,涵蓋技術開發、產品管理、運營、商務等多個方面,以下是具體介紹: - 技術開發類: - 智能合約開發工程師:負責編寫、審計和優化智能合約,常見于DeFi開發,包括抵押…

解決 Spring Boot 對 Elasticsearch 字段沒有小駝峰映射的問題

場景重現在使用 MyBatis/Mybatis-Plus 框架對 MySQL 操作時習慣了字段名小駝峰映射,然而在操作 Elasticsearch 時發現字段名沒有小駝峰映射。解決方法1. 使用 ObjectMapper 手動轉換: 這是最直接也最常用的方法。 在 Spring Boot 應用中使用 ObjectMappe…

Error:Cannot find module ‘chokidar‘

錯誤復現 在vue開發中,出現報錯:Error:Cannot find module ‘chokidar’ 原因 缺包導致 解決方案 直接安裝依賴包 npm install chokidar依舊無效,刪除node_modules重新安裝 rm -rf node_modules npm i

Spring AI 向量數據庫詳解與 RAG 簡單實戰項目

一、什么是向量數據庫? 向量數據庫用于存儲、檢索稠密語義向量(Embedding),是構建 RAG(檢索增強生成)系統的核心組件。它支持近似最近鄰搜索(ANN),可根據語義相似度找出…

【RK3568+PG2L50H開發板實驗例程】Linux部分/FPGA FSPI 通信案例

本原創文章由深圳市小眼睛科技有限公司創作,版權歸本公司所有,如需轉載,需授權并注明出處(www.meyesemi.com) 1. 簡介 本案例旨在 ARM端運行 Linux系統,基通過 FSPI測試。 2. ARM端和 FPGA端通信流程 (1)ARM端實現SP…

github如何創建一個自己的倉庫保姆級教程

文章目錄 準備階段(github官網)添加ssh公鑰添加token創建倉庫 本地設置本地代理創建倉庫添加文件到倉庫進行提交 準備階段(github官網) 添加ssh公鑰 創建SSH KEY。先看一下你C盤用戶目錄下有沒有.ssh目錄,有的話看下里面有沒有id_rsa和id_rsa.pub這兩個文件&#…

LabVIEW 網絡流通信功能

LabVIEW 的網絡流技術實現主機 VI(Host VI)與客戶端 VI(ClientVI)間的雙向數據交互,包含命令發送與波形數據傳輸,支持跨設備、跨進程的實時通信,滿足分布式系統中數據交互與控制需求。 主機 VI邏…

Prompt 精通之路(一)- AI 時代的新語言:到底什么是 Prompt?為什么它如此重要?

AI 時代的新語言:到底什么是 Prompt?為什么它如此重要? 標簽: #Prompt新手指南 #提示詞入門 #AI指令 #人工智能 #ChatGPT 🚀 Prompt 精通之路:系列文章導航 第一篇:AI 時代的新語言&#xff1a…

uniapp 滾動tab

uniapp woui unibest <route lang"json5">{style: {navigationBarTitleText: 知識產權,navigationBarBackgroundColor: #C80F06,navigationBarTextStyle: white,backgroundColorTop: #C80F06,},} </route> <template><view class"bgc-b …

日事清駕駛艙模式上線:實時數據更新+項目管理+數據可視化,提升決策效率?

大家好&#xff01;我們在日事清最新更新中推出了一個令人激動的新功能——駕駛艙模式。這一全新界面將為企業管理者和團隊提供一個全面、實時的數據展示平臺。下面&#xff0c;讓我們詳細了解這個功能如何幫助您更好地把握企業動態和提升決策效率。 快速入口&#xff1a;一鍵激…

【Maven】Maven深度避坑指南:依賴沖突全維度解決方案與工業級實戰(超萬字解析)

注&#xff1a;本文基于50大型企業級項目經驗&#xff0c;結合Maven底層源碼機制&#xff0c;系統化解決依賴沖突問題。包含20個實戰場景、10類特殊案例及5大防御體系構建方案。 Maven深度避坑指南&#xff1a;依賴沖突全維度解決方案與工業級實戰&#xff08;超萬字解析&#…

Rust Web 全棧開發(二):構建 HTTP Server

Rust Web 全棧開發&#xff08;二&#xff09;&#xff1a;構建 HTTP Server Rust Web 全棧開發&#xff08;二&#xff09;&#xff1a;構建 HTTP Server創建成員包/庫&#xff1a;httpserver、http解析 HTTP 請求HTTP 請求的構成構建 HttpRequest 構建 HTTP 響應HTTP 響應的構…

小架構step系列01:小架構初衷

1 概述 小公司做業務服務&#xff0c;需要聚焦到實際的業務上&#xff0c;盡快通過業務服務客戶&#xff0c;給客戶創建價值&#xff0c;公司才能生存下去。在技術上采用的Web應用架構具備以下特點&#xff1a; 主要由開源組件組裝而成。這樣既可以節省成本&#xff0c;也可以把…