2.7.1、uboot和内核到底是什么
2.7.1.1、uboot是一个裸机程序

2.7.1.2、内核本身也是一个"裸机程序"

2.7.1.3、部署在SD卡中特定分区内

2.7.1.4、运行时必须先加载到DDR中链接地址处

2.7.1.5、内核启动需要必要的启动参数

2.7.2、启动内核第一步:加载内核到DDR中

2.7.2.1、静态内核镜像在哪里?

//开发板运行uboot环境下
x210 # fastboot //命令
[Partition table on MoviNAND] //分区表
ptn 0 name='bootloader' start=0x0 len=N/A (use hard-coded info. (cmd: movi))
ptn 1 name='kernel' start=N/A len=N/A (use hard-coded info. (cmd: movi))
ptn 2 name='ramdisk' start=N/A len=0x300000(~3072KB) (use hard-coded info. (cmd: movi))
ptn 3 name='config' start=0xB11E00 len=0x1024BC00(~264495KB)
ptn 4 name='system' start=0x10D5DA00 len=0x1024BC00(~264495KB)
ptn 5 name='cache' start=0x20FA9600 len=0x6751800(~105798KB)
ptn 6 name='userdata' start=0x276FAE00 len=0xC3CC2A00(~3207946KB)
//开发板运行uboot环境下
x210 # help movi //命令
movi init - Initialize moviNAND and show card info
movi read {u-boot | kernel} {addr} - Read data from sd/mmc //从sd/mmc度数据到u-boot | kernel的addr的地址处
movi write {fwbl1 | u-boot | kernel} {addr} - Write data to sd/mmc
movi read rootfs {addr} [bytes(hex)] - Read rootfs data from sd/mmc by size
movi write rootfs {addr} [bytes(hex)] - Write rootfs data to sd/mmc by size
movi read {sector#} {bytes(hex)} {addr} - instead of this, you can use "mmc read"
movi write {sector#} {bytes(hex)} {addr} - instead of this, you can use "mmc write"
x210 # movi read kernel 0x30008000 //从sd/mmc读数据到kernel的addr的地址处
reading kernel.. 1073, 8192
MMC read: dev # 0, block # 1073, count 8192 ...8192 blocks read: OK
completed
x210 # bootm 0x30008000 //启动内核

//首先,开发板和网络要ping通,tftp服务器要搭建
x210 # tftp 0x30008000 zImage-qt
x210 # bootm 0x30008000 //启动内核
2.7.2.2、镜像要放在DDR的什么地址?

2.7.3、zImage和uImage的区别联系
2.7.3.1、bootm命令对应do_bootm函数

/*******************************************************************/
/* bootm - boot application image from image in memory */
/* bootm -从内存中的映像启动应用程序映像*/
/*******************************************************************/
int do_bootm (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
image_header_t *hdr; //定义一些变量
ulong addr;
ulong iflag;
const char *type_name;
uint unc_len = CFG_BOOTM_LEN;
uint8_t comp, type, os;
void *os_hdr;
ulong os_data, os_len;
ulong image_start, image_end;
ulong load_start, load_end;
ulong mem_start;
phys_size_t mem_size;
struct lmb lmb;
#if defined(CONFIG_SECURE_BOOT) //CONFIG_SECURE_BOOT没定义,不执行
int rv;
#endif
#if defined(CONFIG_SECURE_BOOT) //CONFIG_SECURE_BOOT没定义,不执行
rv = Check_Signature( (SecureBoot_CTX *)SECURE_BOOT_CONTEXT_ADDR,
(unsigned char*)CONFIG_SECURE_KERNEL_BASE,
CONFIG_SECURE_KERNEL_SIZE-128,
(unsigned char*)(CONFIG_SECURE_KERNEL_BASE+CONFIG_SECURE_KERNEL_SIZE-128),
128 );
if(rv != SB_OK) {
printf("Kernel Integrity check fail\nSystem Halt....");
while(1);
}
printf("Kernel Integirty check success.\n");
rv = Check_Signature( (SecureBoot_CTX *)SECURE_BOOT_CONTEXT_ADDR,
(unsigned char*)CONFIG_SECURE_ROOTFS_BASE,
CONFIG_SECURE_ROOTFS_SIZE-128,
(unsigned char*)(CONFIG_SECURE_ROOTFS_BASE+CONFIG_SECURE_ROOTFS_SIZE-128),
128 );
if(rv != SB_OK) {
printf("rootfs Integrity check fail\nSystem Halt....");
while(1);
}
printf("rootfs Integirty check success.\n");
#endif
memset ((void *)&images, 0, sizeof (images)); //细节操作
images.verify = getenv_yesno ("verify");
images.lmb = &lmb;
lmb_init(&lmb);
mem_start = getenv_bootm_low();
mem_size = getenv_bootm_size();
lmb_add(&lmb, (phys_addr_t)mem_start, mem_size);
board_lmb_reserve(&lmb);
//CONFIG_ZIMAGE_BOOT,用这个宏来控制进行条件编译一段代码,这段代码是用来支持zImage格式的内核启动的。
#ifdef CONFIG_ZIMAGE_BOOT //CONFIG_ZIMAGE_BOOT定义
//这个是一个定义的魔数,这个数等于0x016f2818,表示这个镜像是一个zImage。
//zImage格式的镜像中在头部的一个固定位置存放了这个数作为格式标记。
#define LINUX_ZIMAGE_MAGIC 0x016f2818
/* find out kernel image address */
//找出内核镜像地址,就是看zImage镜像被加载到DDR哪里去了
// bootm 0x30008000,所以do_boom的argc=2,argv[0]=bootm argv[1]=0x30008000。
if (argc < 2) {
//ulong load_addr = CFG_LOAD_ADDR; /* Default Load Address 默认加载地址*/
//#define CFG_LOAD_ADDR MEMORY_BASE_ADDRESS /* default load address 默认加载地址*/
//#define MEMORY_BASE_ADDRESS 0x30000000
addr = load_addr; //load_addr:默认加载地址
debug ("* kernel: default image load address = 0x%08lx\n",
load_addr);
} else {
addr = simple_strtoul(argv[1], NULL, 16);
debug ("* kernel: cmdline image address = 0x%08lx\n", img_addr);
}
//zImage头部开始的第37-40字节处存放着zImage标志魔数,从这个位置取出然后对比LINUX_ZIMAGE_MAGIC。
//相等,则是从zImage启动
if (*(ulong *)(addr + 9*4) == LINUX_ZIMAGE_MAGIC) {
printf("Boot with zImage\n"); //打印Boot with zImage
addr = virt_to_phys(addr); //把虚拟地址转成物理地址
hdr = (image_header_t *)addr; //
//这个数据结构是我们uboot启动内核使用的一个标准启动数据结构
//zImage头信息也是一个image_header_t,但是在实际启动之前需要进行一些改造。
hdr->ih_os = IH_OS_LINUX; //操作系统
hdr->ih_ep = ntohl(addr); //入口点地址
memmove (&images.legacy_hdr_os_copy, hdr, sizeof(image_header_t));
/* save pointer to image header */
images.legacy_hdr_os = hdr;
images.legacy_hdr_valid = 1;
//如果认为是zImage,就进行校验,检验完之后,直接跳转到after_header_check,就不在进行uImage校验
goto after_header_check;
}
#endif
2.7.3.2、vmlinuz和zImage和uImage



//vmlinuz-4.15.0-29-generic可执行程序在/boot目录下
root@xfj-virtual-machine:/# cd boot
root@xfj-virtual-machine:/boot# ls
abi-4.15.0-29-generic grub memtest86+.bin memtest86+_multiboot.bin System.map-4.15.0-29-generic
config-4.15.0-29-generic initrd.img-4.15.0-29-generic memtest86+.elf retpoline-4.15.0-29-generic vmlinuz-4.15.0-29-generic
root@xfj-virtual-machine:/boot#
//Image在此目录下
root@xfj-virtual-machine:~/x210v3_bsp/kernel/arch/arm/boot# pwd
/root/x210v3_bsp/kernel/arch/arm/boot
root@xfj-virtual-machine:~/x210v3_bsp/kernel/arch/arm/boot# ls
bootp compressed install.sh Makefile test test2
//vmlinux在此目录下
root@xfj-virtual-machine:~/x210v3_bsp/kernel# pwd
/root/x210v3_bsp/kernel
root@xfj-virtual-machine:~/x210v3_bsp/kernel# ls
arch COPYING.txt Documentation fs initrd.img.cpio kernel Makefile net samples sound virt
block CREDITS drivers include ipc lib mk README scripts tools
COPYING crypto firmware init Kbuild MAINTAINERS mm REPORTING-BUGS security usr
root@xfj-virtual-machine:~/x210v3_bsp/kernel#


2.7.3.3、编译内核得到uImage去启动

//第一步
root@xfj-virtual-machine:~/x210v3_bsp/uboot/tools# pwd
/root/x210v3_bsp/uboot/tools
//第二步
root@xfj-virtual-machine:~/x210v3_bsp/uboot/tools# cp mkimage /usr/local/bin
2.7.4、zImage启动细节

2.7.4.1、LINUX_ZIMAGE_MAGIC

//CONFIG_ZIMAGE_BOOT,用这个宏来控制进行条件编译一段代码,这段代码是用来支持zImage格式的内核启动的。
#ifdef CONFIG_ZIMAGE_BOOT //CONFIG_ZIMAGE_BOOT定义
//这个是一个定义的魔数,这个数等于0x016f2818,表示这个镜像是一个zImage。
//zImage格式的镜像中在头部的一个固定位置存放了这个数作为格式标记。
#define LINUX_ZIMAGE_MAGIC 0x016f2818
/* find out kernel image address */
//找出内核镜像地址,就是看zImage镜像被加载到DDR哪里去了
// bootm 0x30008000,所以do_boom的argc=2,argv[0]=bootm argv[1]=0x30008000。
if (argc < 2) {
//ulong load_addr = CFG_LOAD_ADDR; /* Default Load Address 默认加载地址*/
//#define CFG_LOAD_ADDR MEMORY_BASE_ADDRESS /* default load address 默认加载地址*/
//#define MEMORY_BASE_ADDRESS 0x30000000
addr = load_addr; //load_addr:默认加载地址
debug ("* kernel: default image load address = 0x%08lx\n",
load_addr);
} else {
addr = simple_strtoul(argv[1], NULL, 16);
debug ("* kernel: cmdline image address = 0x%08lx\n", img_addr);
}
//zImage头部开始的第37-40字节处存放着zImage标志魔数,从这个位置取出然后对比LINUX_ZIMAGE_MAGIC。
//相等,则是从zImage启动
if (*(ulong *)(addr + 9*4) == LINUX_ZIMAGE_MAGIC) {
printf("Boot with zImage\n"); //打印Boot with zImage
addr = virt_to_phys(addr); //把虚拟地址转成物理地址
hdr = (image_header_t *)addr; //
//这个数据结构是我们uboot启动内核使用的一个标准启动数据结构
//zImage头信息也是一个image_header_t,但是在实际启动之前需要进行一些改造。
hdr->ih_os = IH_OS_LINUX; //操作系统
hdr->ih_ep = ntohl(addr); //入口点地址
memmove (&images.legacy_hdr_os_copy, hdr, sizeof(image_header_t));
/* save pointer to image header */
images.legacy_hdr_os = hdr;
images.legacy_hdr_valid = 1;
//如果认为是zImage,就进行校验,检验完之后,直接跳转到after_header_check,就不在进行uImage校验
goto after_header_check;
}
#endif
2.7.4.2、image_header_t

//Image.h
/*
* Legacy format image header, 传统格式镜像头
* all data in network byte order (aka natural aka bigendian).
* 传统格式的镜像头,所有的数据都以网络字节顺序排列(也就是自然的,也就是双端)
*/
typedef struct image_header {
uint32_t ih_magic; /* Image Header Magic Number 镜像头魔数号码*/
uint32_t ih_hcrc; /* Image Header CRC Checksum 镜像头CRC校验和*/
uint32_t ih_time; /* Image Creation Timestamp 镜像创建时间*/
uint32_t ih_size; /* Image Data Size 镜像数据大小*/
uint32_t ih_load; /* Data Load Address 数据下载地址*/
uint32_t ih_ep; /* Entry Point Address 入口点地址 */
uint32_t ih_dcrc; /* Image Data CRC Checksum 镜像数据CRC校验和*/
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;
2.7.5、uImage启动
2.7.5.1、uImage启动

//Image.h
#ifndef USE_HOSTCC
/* Image format types, returned by _get_format() routine */
//镜像格式类型,由_get_format()例程返回
#define IMAGE_FORMAT_INVALID 0x00
#define IMAGE_FORMAT_LEGACY 0x01 /* legacy image_header based format 基于旧镜像头的格式*/
#define IMAGE_FORMAT_FIT 0x02 /* new, libfdt based format 基于libfdt的新格式*/

//Cmd_bootm.c
/* get kernel image header, start address and length */
//获取内核映像头、起始地址和长度
//uImage的启动校验主要在boot_get_kernel函数中,主要任务就是校验uImage的头信息,
//并且得到真正的kernel的起始位置去启动。
os_hdr = boot_get_kernel (cmdtp, flag, argc, argv,
&images, &os_data, &os_len);
if (os_len == 0) {
puts ("ERROR: can't get kernel image!\n");
return 1;
}
/**
* boot_get_kernel -查找内核映像
* @os_data:指向ulong变量的指针,将保存OS数据起始地址
* @os_len:指向ulong变量的指针,将保存OS数据长度
*
* boot_get_kernel()尝试查找内核映像,验证其完整性并定位内核数据。
*
* returns:
* 如果找到有效的映像,则指向映像头的指针,加上内核起始地址和长度,否则为空
*/
//uImage的启动校验主要在boot_get_kernel函数中,主要任务就是校验uImage的头信息,
//并且得到真正的kernel的起始位置去启动。
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)
{
image_header_t *hdr; //镜像头结构体指针
ulong img_addr; //镜像地址
#if defined(CONFIG_FIT)
void *fit_hdr;
const char *fit_uname_config = NULL;
const char *fit_uname_kernel = NULL;
const void *data;
size_t len;
int cfg_noffset;
int os_noffset;
#endif
/* find out kernel image address */
//找出内核映像地址
if (argc < 2) { //如果输入参数小于2,镜像地址为默认地址
//ulong load_addr = CFG_LOAD_ADDR; /* Default Load Address 默认加载地址*/
//#define CFG_LOAD_ADDR MEMORY_BASE_ADDRESS /* default load address 默认加载地址*/
//#define MEMORY_BASE_ADDRESS 0x30000000
img_addr = load_addr;
debug ("* kernel: default image load address = 0x%08lx\n",
load_addr);
#if defined(CONFIG_FIT) //CONFIG_FIT:设备树方式
} else if (fit_parse_conf (argv[1], load_addr, &img_addr,
&fit_uname_config)) {
debug ("* kernel: config '%s' from image at 0x%08lx\n",
fit_uname_config, img_addr);
} else if (fit_parse_subimage (argv[1], load_addr, &img_addr,
&fit_uname_kernel)) {
debug ("* kernel: subimage '%s' from image at 0x%08lx\n",
fit_uname_kernel, img_addr);
#endif
} else { //uImage方式启动走这里
img_addr = simple_strtoul(argv[1], NULL, 16);
debug ("* kernel: cmdline image address = 0x%08lx\n", img_addr);
}
show_boot_progress (1); //此是用来做调试
/* copy from dataflash if needed 如果需要,从dataflash复制*/
//检查所提供的映像开始地址是否位于数据闪存中。如果是这样,图像将被移动到系统内存中。
img_addr = genimg_get_image (img_addr);
/**
* genimg_get_image - get image from special storage (if necessary)
* genig _ get _ image-从特殊存储器获取镜像(如有必要)
* @img_addr: image start address
* @img_addr:镜像起始地址
*
* genimg_get_image() checks if provided image start adddress is located
* in a dataflash storage. If so, image is moved to a system RAM memory.
* genimg _ get _ image()检查所提供的映像开始地址是否位于数据闪存中。如果是这样,图像将被移动到系统内存中。
*
* returns:
* image start address after possible relocation from special storage
* 可能从特殊存储器重新定位后的图像起始地址
*/
ulong genimg_get_image (ulong img_addr)
{
ulong ram_addr = img_addr; //镜像起始地址
#ifdef CONFIG_HAS_DATAFLASH
ulong h_size, d_size;
if (addr_dataflash (img_addr)){
/* ger RAM address */
ram_addr = CFG_LOAD_ADDR;
/* get header size */
h_size = image_get_header_size ();
#if defined(CONFIG_FIT)
if (sizeof(struct fdt_header) > h_size)
h_size = sizeof(struct fdt_header);
#endif
/* read in header */
debug (" Reading image header from dataflash address "
"%08lx to RAM address %08lx\n", img_addr, ram_addr);
read_dataflash (img_addr, h_size, (char *)ram_addr);
/* get data size */
switch (genimg_get_format ((void *)ram_addr)) { //获取格式
case IMAGE_FORMAT_LEGACY:
d_size = image_get_data_size ((image_header_t *)ram_addr);
debug (" Legacy format image found at 0x%08lx, size 0x%08lx\n",
ram_addr, d_size);
break;
#if defined(CONFIG_FIT)
case IMAGE_FORMAT_FIT:
d_size = fit_get_size ((const void *)ram_addr) - h_size;
debug (" FIT/FDT format image found at 0x%08lx, size 0x%08lx\n",
ram_addr, d_size);
break;
#endif
#ifndef USE_HOSTCC
/**
* genimg_get_format - get image format type
* genig _ get _ format-获取镜像格式类型
* @img_addr: image start address
* @img_addr:图像起始地址
*
* genimg_get_format() checks whether provided address points to a valid
* legacy or FIT image.
* genimg_get_format()检查提供的地址是否指向有效的传统或FIT映像。
*
* New uImage format and FDT blob are based on a libfdt. FDT blob
* may be passed directly or embedded in a FIT image. In both situations
* genimg_get_format() must be able to dectect libfdt header.
*
* returns:
* image format type or IMAGE_FORMAT_INVALID if no image is present
* 如果没有图像,则为图像格式类型或图像格式无效
*/
int genimg_get_format (void *img_addr)
{
ulong format = IMAGE_FORMAT_INVALID; //IMAGE_FORMAT_INVALID默认方式
image_header_t *hdr;
printf("get_format\n"); //打印出来
#if defined(CONFIG_FIT) || defined(CONFIG_OF_LIBFDT) //设备树方式
char *fit_hdr;
#endif
hdr = (image_header_t *)img_addr;
if (image_check_magic(hdr)) //image_check_magic检查魔数
format = IMAGE_FORMAT_LEGACY;
#if defined(CONFIG_FIT) || defined(CONFIG_OF_LIBFDT)
else {
fit_hdr = (char *)img_addr;
if (fdt_check_header (fit_hdr) == 0)
format = IMAGE_FORMAT_FIT;
}
#endif
printf("-------- %x --------\n", format); //打印出来
return format;
}
2.7.5.2、设备树方式内核启动

2.7.6、do_bootm_linux函数
2.7.6.1、找到do_bootm_linux函数

//Cmd_bootm.c
do_bootm_linux (cmdtp, flag, argc, argv, &images);
//Bootm.c
void do_bootm_linux (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[],
bootm_headers_t *images)
2.7.6.2、镜像的entrypoint

//Bootm.c
void do_bootm_linux (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[],
bootm_headers_t *images)
{
ulong initrd_start, initrd_end;
ulong ep = 0;
bd_t *bd = gd->bd; //板信息结构体指针
char *s; //字符指针
int machid = bd->bi_arch_number; //机器码赋值给machid,machid用来记录机器码
void (*theKernel)(int zero, int arch, uint params); //函数指针
int ret; //返回值
#ifdef CONFIG_CMDLINE_TAG //配置命令行宏
char *commandline = getenv ("bootargs"); //获取环境变量参数 //getenv函数用来获取字符串格式的环境变量的地址
#endif
/* find kernel entry point 发现内核程序入口*/
if (images->legacy_hdr_valid) { //当前镜像有效,执行这里
//ep就是entrypoint的缩写,就是程序入口。
ep = image_get_ep (&images->legacy_hdr_os_copy);
#if defined(CONFIG_FIT)
} else if (images->fit_uname_os) { //不执行,设备树方式传参
ret = fit_image_get_entry (images->fit_hdr_os,
images->fit_noffset_os, &ep);
if (ret) {
puts ("Can't get entry point property!\n");
goto error;
}
#endif
} else {
puts ("Could not find kernel entry point!\n");
goto error;
}
//将ep赋值给theKernel,则这个函数指向就指向了内存中加载的OS镜像的真正入口地址
//(就是操作系统的第一句执行的代码)。
theKernel = (void (*)(int, int, uint))ep; //将ep强制类型转换为函数指针,然后赋值给theKernel
2.7.6.3、机器码的再次确定

//获取环境变量中的机器码
//getenv函数用来获取字符串格式的环境变量的地址
s = getenv ("machid"); //第一顺序备选是环境变量machid,
if (s) {
//simple_strtoul()函数将字符串转换为整形
machid = simple_strtoul (s, NULL, 16);
printf ("Using machid 0x%x from environment\n", machid); //打印机器码
}
ret = boot_get_ramdisk (argc, argv, images, IH_ARCH_ARM,
&initrd_start, &initrd_end);
if (ret)
goto error;
show_boot_progress (15);
debug ("## Transferring control to Linux (at address %08lx) ...\n",
(ulong) theKernel);
2.7.6.4、传参并启动概述

/* we assume that the kernel is in place */
//我们假设内核已经就位
//Starting kernel ... 这个是uboot中最后一句打印出来的东西。
printf ("\nStarting kernel ...\n\n");
2.7.7、传参详解
2.7.7.1、tag方式传参

//Setup.h
struct tag {
struct tag_header hdr; //
union {
struct tag_core core; //
struct tag_mem32 mem; //传参内容是内存配置信息。
struct tag_videotext videotext; //video相关
struct tag_ramdisk ramdisk; //
struct tag_initrd initrd; //
struct tag_serialnr serialnr; //
struct tag_revision revision; //
struct tag_videolfb videolfb; //
struct tag_cmdline cmdline; //传参内容是启动命令行参数,也就是uboot环境变量的bootargs.
/*
* Acorn specific
*/
struct tag_acorn acorn;
/*
* DC21285 specific
*/
struct tag_memclk memclk;
struct tag_mtdpart mtdpart_info; //传参内容是iNand/SD卡的分区表。
} u;
};
//Setup.h
struct tag_header {
u32 size; //tag的大小
u32 tag;
};
2.7.7.2、x210_sd.h中配置传参宏

// An highlighted block
var foo = 'bar';
static void setup_start_tag (bd_t *bd) //控制uboot给内核传参开始
{
//static struct tag *params;
params = (struct tag *) bd->bi_boot_params; //内核传参的起始地址赋值给params
//起始tag是ATAG_CORE
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_end_tag (bd_t *bd) //控制uboot给内核传参结束
{
//结束tag是ATAG_NONE
params->hdr.tag = ATAG_NONE;
params->hdr.size = 0;
}
#ifdef CONFIG_SETUP_MEMORY_TAGS
static void setup_memory_tags (bd_t *bd) //(1)CONFIG_SETUP_MEMORY_TAGS,tag_mem,传参内容是内存配置信息。
{
int i;
for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) {
params->hdr.tag = ATAG_MEM; //内存tag
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); //指向下一个tag
}
}
#endif /* CONFIG_SETUP_MEMORY_TAGS */
//(2)CONFIG_CMDLINE_TAG,tag_cmdline,传参内容是启动命令行参数,也就是uboot环境变量的bootargs.
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; //告诉此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);
}
2.7.7.3、移植时注意事项

8888

被折叠的 条评论
为什么被折叠?



