【uboot本质】
(1)uboot的本质就是一个裸机程序,和我们裸机全集中写的那些裸机程序xx.bin并没有本质区别。
如果非说要有区别,那就是:我们写的大部分小于16KB,而uboot大于16KB(一般uboot在180k-400k之间)
(2)uboot本身是一个开源项目,由若干个.c文件和.h文件组成,
配置编译之后会生成一个uboot.bin,这就是uboot这个裸机程序的镜像文件。
然后这个镜像文件被合理的烧录到启动介质中拿给SoC去启动。也就是说uboot在没有运行时表现为uboot.bin,
一般躺在启动介质中。
U-boot的工作模式
- 启动加载模式:
启动加载模式,为Bootloader正常工作模式,一款开发板,正常上电后,Bootloader将嵌入式操作系统==从FLASH中加载到SDRAM中==运行。
- 下载模式:
下载模式,就是Bootloader通过通信,将内核镜像、根文件系统镜像从PC机直接下载到目标板的FLASH中。
启动流程
编辑 播报
大多数BootLoader都分为stage1和stage2两大部分,U-boot也不例外。依赖于cpu体系结构的代码(如设备初始化代码等)通常都放在stage1且可以用汇编语言来实现,而stage2则通常用C语言来实现,这样可以实现复杂的功能,而且有更好的可读性和移植性。
1、 stage1(start.s代码结构)
U-boot的stage1代码通常放在start.s文件中,它用汇编语言写成,其主要代码部分如下:
(1) 定义入口。由于一个可执行的image必须有一个入口点,并且只能有一个全局入口,通常这个入口放在rom(Flash)的0x0地址,因此,必须通知编译器以使其知道这个入口,该工作可通过修改连接器脚本来完成。
(2)设置异常向量(exception vector)。
(4)初始化内存控制器 。
(5)将rom中的程序复制到ram中。
(6)初始化堆栈 。
(7)转到ram中执行,该工作可使用指令ldrpc来完成。
2、 stage2(C语言代码部分)
lib_arm/board.c中的start armboot是C语言开始的函数,也是整个启动代码中C语言的主函数,同时还是整个u-boot(armboot)的主函数,该函数主要完成如下操作:
(1)调用一系列的初始化函数。
(2)初始化flash设备。
(3)初始化系统内存分配函数。
(4)如果目标系统拥有nand设备,则初始化nand设备。
(5)如果目标系统有显示设备,则初始化该类设备。
(6)初始化相关网络设备,填写ip,c地址等。
(7)进入命令循环(即整个boot的工作循环),接受用户从串口输入的命令,然后进行相应的工作。
我所接触过三个平台
MTK: boot rom -> preloader -> lk (可以理解是uboot) -> kernel
RK: bootrom -> spl(miniloader) -> uboot->trust (optee) -> kernel
NXP: bootrom -> bl2 -> ATF ->uboot->kernel
u-boot启动详细函数调用流程
u-boot:启动详细的代码调用流程
u-boot.lds:(arch/arm/cpu/u-boot.lds)
|-->_start:(arch/arm/lib/vectors.S)
|-->reset(arch/arm/cpu/armv7/start.S)
|-->save_boot_params(arch/arm/cpu/armv7/start.S)/*将引导参数保存到内存中*/
|-->save_boot_params_ret(arch/arm/cpu/armv7/start.S)
|-->cpu_init_cp15(arch/arm/cpu/armv7/start.S)/*初始化*/
|-->cpu_init_crit(arch/arm/cpu/armv7/start.S)
|-->lowlevel_init(arch/arm/cpu/armv7/lowlevel_init.S)
|-->_main(arch/arm/lib/crt0.S)
|-->board_init_f_alloc_reserve(common/init/board_init.c)/*为u-boot的gd结构体分配空间*/
|-->board_init_f_init_reserve(common/init/board_init.c) /*将gd结构体清零*/
|-->board_init_f(common/board_f.c)
|-->initcall_run_list(include/initcall.h) /*初始化序列函数*/
|-->init_sequence_f[](common/board_f.c) /* 初始化序列函数数组 */
|-->board_early_init_f(board/freescale/mx6ull_toto/mx6ull_toto.c)/*初始化串口的IO配置*/
|-->timer_init(arch/arm/imx-common/timer.c) /*初始化内核定时器,为uboot提供时钟节拍*/
|-->init_baud_rate(common/board_f.c) /*初始化波特率*/
|-->serial_init(drivers/serial/serial.c) /*初始化串口通信设置*/
|-->console_init_f(common/console.c) /*初始化控制台*/
|-->...
|-->relocate_code(arch/arm/lib/relocate.S) /*主要完成镜像拷贝和重定位*/
|-->relocate_vectors(arch/arm/lib/relocate.S)/*重定位向量表*/
|-->board_init_r(common/board_r.c)/*板级初始化*/
|-->initcall_run_list(include/initcall.h)/*初始化序列函数*/
|-->init_sequence_r[](common/board_f.c)/*序列函数*/
|-->initr_reloc(common/board_r.c) /*设置 gd->flags,标记重定位完成*/
|-->serial_initialize(drivers/serial/serial-uclass.c)/*初始化串口*/
|-->serial_init(drivers/serial/serial-uclass.c) /*初始化串口*/
|-->initr_mmc(common/board_r.c) /*初始化emmc*/
|-->mmc_initialize(drivers/mmc/mmc.c)
|-->mmc_do_preinit(drivers/mmc/mmc.c)
|-->mmc_start_init(drivers/mmc/mmc.c)
|-->console_init_r(common/console.c) /*初始化控制台*/
|-->interrupt_init(arch/arm/lib/interrupts.c) /*初始化中断*/
|-->initr_net(common/board_r.c) /*初始化网络设备*/
|-->eth_initialize(net/eth-uclass.c)
|-->eth_common_init(net/eth_common.c)
|-->phy_init(drivers/net/phy/phy.c)
|-->uclass_first_device_check(drivers/core/uclass.c)
|-->uclass_find_first_device(drivers/core/uclass.c)
|-->device_probe(drivers/core/device.c)
|-->device_of_to_plat(drivers/core/device.c)
|-->drv->of_to_plat
|-->fecmxc_of_to_plat(drivers/net/fec_mxc.c)/*解析设备树信息*/
|-->device_get_uclass_id(drivers/core/device.c)
|-->uclass_pre_probe_device(drivers/core/uclass.c)
|-->drv->probe(dev)
/*drivers/net/fec_mxc.c*/
U_BOOT_DRIVER(fecmxc_gem) = {
.name = "fecmxc",
.id = UCLASS_ETH,
.of_match = fecmxc_ids,
.of_to_plat = fecmxc_of_to_plat,
.probe = fecmxc_probe,
.remove = fecmxc_remove,
.ops = &fecmxc_ops,
.priv_auto = sizeof(struct fec_priv),
.plat_auto = sizeof(struct eth_pdata),
};
|-->fecmxc_probe(drivers/net/fec_mxc.c)/*探测和初始化*/
|-->fec_get_miibus(drivers/net/fec_mxc.c)
|-->mdio_alloc(drivers/net/fec_mxc.c)
|-->bus->read = fec_phy_read;
|-->bus->write = fec_phy_write;
|-->mdio_register(common/miiphyutil.c)
|-->fec_mii_setspeed(drivers/net/fec_mxc.c)
|-->fec_phy_init(drivers/net/fec_mxc.c)
|-->device_get_phy_addr(drivers/net/fec_mxc.c)
|-->phy_connect(drivers/net/phy/phy.c)
|-->phy_find_by_mask(drivers/net/phy/phy.c)
|-->bus->reset(bus)
|-->get_phy_device_by_mask(drivers/net/phy/phy.c)
|-->create_phy_by_mask(drivers/net/phy/phy.c)
|-->phy_device_create(drivers/net/phy/phy.c)
|-->phy_probe(drivers/net/phy/phy.c)
|-->phy_connect_dev(drivers/net/phy/phy.c)
|-->phy_reset(drivers/net/phy/phy.c)
|-->phy_config(drivers/net/phy/phy.c)
|-->board_phy_config(drivers/net/phy/phy.c)
|-->phydev->drv->config(phydev)
/*drivers/net/phy/smsc.c*/
static struct phy_driver lan8710_driver = {
.name = "SMSC LAN8710/LAN8720",
.uid = 0x0007c0f0,
.mask = 0xffff0,
.features = PHY_BASIC_FEATURES,
.config = &genphy_config_aneg,
.startup = &genphy_startup,
.shutdown = &genphy_shutdown,
};
|-->genphy_config_aneg(drivers/net/phy/phy.c)
|-->phy_reset(需要手动调用)(drivers/net/phy/phy.c)
|-->genphy_setup_forced(drivers/net/phy/phy.c)
|-->genphy_config_advert(drivers/net/phy/phy.c)
|-->genphy_restart_aneg(drivers/net/phy/phy.c)
|-->uclass_post_probe_device(drivers/core/uclass.c)
|-->uc_drv->post_probe(drivers/core/uclass.c)
/*net/eth-uclass.c*/
UCLASS_DRIVER(ethernet) = {
.name = "ethernet",
.id = UCLASS_ETH,
.post_bind = eth_post_bind,
.pre_unbind = eth_pre_unbind,
.post_probe = eth_post_probe,
.pre_remove = eth_pre_remove,
.priv_auto = sizeof(struct eth_uclass_priv),
.per_device_auto = sizeof(struct eth_device_priv),
.flags = DM_UC_FLAG_SEQ_ALIAS,
};
|-->eth_post_probe(net/eth-uclass.c)
|-->eth_write_hwaddr(drivers/core/uclass.c)
|-->...
|-->run_main_loop(common/board_r.c)/*主循环,处理命令*/
|-->main_loop(common/main.c)
|-->bootdelay_process(common/autoboot.c) /*读取环境变量bootdelay和bootcmd的内容*/
|-->autoboot_command(common/autoboot.c) /*倒计时按下执行,没有操作执行bootcmd的参数*/
|-->abortboot(common/autoboot.c)
|-->printf("Hit any key to stop autoboot: %2d ", bootdelay);
/*到这里就是我们看到uboot延时3s启动内核的地方*/
|-->cli_loop(common/cli.c) /*倒计时按下space键,执行用户输入命令*/
程序入口
U-Boot 源码文件众多,我们如何知道最开始的启动文件(程序入口)是哪个呢?程序的链接是由链接脚本来决定的,所以通过链接脚本可以找到程序的入口,链接脚本为arch/arm/cpu/u-boot.lds,它描述了如何生成最终的二进制文件,其中就包含程序入口。
参考: u-boot启动流程分析-史上最全最详细 - 知乎 (zhihu.com)
链接脚本 u-boot.lds 详解
u-boot.lds
u-boot.lds:文件所在位置arch/arm/cpu/u-boot.lds
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Copyright (c) 2004-2008 Texas Instruments
*
* (C) Copyright 2002
* Gary Jennejohn, DENX Software Engineering, <garyj@denx.de>
*/
#include <config.h>
#include <asm/psci.h>
/* 指定输出可执行文件: "elf 32位 小端格式 arm指令" */
OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
/* 指定输出可执行文件的目标架构:"arm" */
OUTPUT_ARCH(arm)
/* 指定输出可执行文件的起始地址为:"_start" */
ENTRY(_start)
SECTIONS
{
#ifndef CONFIG_CMDLINE
/DISCARD/ : { *(__u_boot_list_2_cmd_*) }
#endif
#if defined(CONFIG_ARMV7_SECURE_BASE) && defined(CONFIG_ARMV7_NONSEC)
/*
* If CONFIG_ARMV7_SECURE_BASE is true, secure code will not
* bundle with u-boot, and code offsets are fixed. Secure zone
* only needs to be copied from the loading address to
* CONFIG_ARMV7_SECURE_BASE, which is the linking and running
* address for secure code.
*
* If CONFIG_ARMV7_SECURE_BASE is undefined, the secure zone will
* be included in u-boot address space, and some absolute address
* were used in secure code. The absolute addresses of the secure
* code also needs to be relocated along with the accompanying u-boot
* code.
*
* So DISCARD is only for CONFIG_ARMV7_SECURE_BASE.
*/
/DISCARD/ : { *(.rel._secure*) }
#endif
/*
* 指定可执行文件(image)的全局入口地址,通常都放在ROM(flash)0x0位置
* 设置 0 的原因是 arm 内核的处理器,上电后默认是从 0x00000000 处启动
*/
. = 0x00000000;
. = ALIGN(4); ``````````/* 中断向量表 */
.text :
{
*(.__image_copy_start) /* u-boot 的设计中需要将 u-boot 的镜像拷贝到 ram(sdram,ddr....)中执行,这里表示复制的开始地址 */
*(.vectors) /* 中断向量表 */
CPUDIR/start.o (.text*) /* CPUDIR/start.o 中的所有.text 段 */
}
/* This needs to come before *(.text*) */
.__efi_runtime_start : {
*(.__efi_runtime_start)
}
.efi_runtime : {
*(.text.efi_runtime*)
*(.rodata.efi_runtime*)
*(.data.efi_runtime*)
}
.__efi_runtime_stop : {
*(.__efi_runtime_stop)
}
.text_rest :
{
*(.text*)
}
#ifdef CONFIG_ARMV7_NONSEC
/* Align the secure section only if we're going to use it in situ */
.__secure_start
#ifndef CONFIG_ARMV7_SECURE_BASE
ALIGN(CONSTANT(COMMONPAGESIZE))
#endif
: {
KEEP(*(.__secure_start))
}
#ifndef CONFIG_ARMV7_SECURE_BASE
#define CONFIG_ARMV7_SECURE_BASE
#define __ARMV7_PSCI_STACK_IN_RAM
#endif
.secure_text CONFIG_ARMV7_SECURE_BASE :
AT(ADDR(.__secure_start) + SIZEOF(.__secure_start))
{
*(._secure.text)
}
.secure_data : AT(LOADADDR(.secure_text) + SIZEOF(.secure_text))
{
*(._secure.data)
}
#ifdef CONFIG_ARMV7_PSCI
.secure_stack ALIGN(ADDR(.secure_data) + SIZEOF(.secure_data),
CONSTANT(COMMONPAGESIZE)) (NOLOAD) :
#ifdef __ARMV7_PSCI_STACK_IN_RAM
AT(ADDR(.secure_stack))
#else
AT(LOADADDR(.secure_data) + SIZEOF(.secure_data))
#endif
{
KEEP(*(.__secure_stack_start))
/* Skip addreses for stack */
. = . + CONFIG_ARMV7_PSCI_NR_CPUS * ARM_PSCI_STACK_SIZE;
/* Align end of stack section to page boundary */
. = ALIGN(CONSTANT(COMMONPAGESIZE));
KEEP(*(.__secure_stack_end))
#ifdef CONFIG_ARMV7_SECURE_MAX_SIZE
/*
* We are not checking (__secure_end - __secure_start) here,
* as these are the load addresses, and do not include the
* stack section. Instead, use the end of the stack section
* and the start of the text section.
*/
ASSERT((. - ADDR(.secure_text)) <= CONFIG_ARMV7_SECURE_MAX_SIZE,
"Error: secure section exceeds secure memory size");
#endif
}
#ifndef __ARMV7_PSCI_STACK_IN_RAM
/* Reset VMA but don't allocate space if we have secure SRAM */
. = LOADADDR(.secure_stack);
#endif
#endif
.__secure_end : AT(ADDR(.__secure_end)) {
*(.__secure_end)
LONG(0x1d1071c); /* Must output something to reset LMA */
}
#endif
/*
* .rodata 段,确保是以4字节对齐
*/
. = ALIGN(4);
.rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) }
/*
* data段,确保是以4字节对齐
*/
. = ALIGN(4);
.data : {
*(.data*)
}
. = ALIGN(4);
. = .;
/*
* u_boot_list 段,确保是以 4 字节对齐
* 这里存放的都是 u_boot_list 中的函数
*/
. = ALIGN(4);
__u_boot_list : {
KEEP(*(SORT(__u_boot_list*)));
}
. = ALIGN(4);
.efi_runtime_rel_start :
{
*(.__efi_runtime_rel_start)
}
.efi_runtime_rel : {
*(.rel*.efi_runtime)
*(.rel*.efi_runtime.*)
}
.efi_runtime_rel_stop :
{
*(.__efi_runtime_rel_stop)
}
/*
* __image_copy_end 也是个符号表示一个结束地址,确保是以4字节对齐
*/
. = ALIGN(4);
.image_copy_end : /* u-boot 的设计中需要将 u-boot 的镜像拷贝到ram(sdram,ddr....)中执行,这里表示复制的结束地址 */
{
*(.__image_copy_end)
}
.rel_dyn_start : /* .rel.dyn 段起始地址 */
{
*(.__rel_dyn_start)
}
.rel.dyn : {
*(.rel*)
}
.rel_dyn_end : /* .rel.dyn 段结束地址 */
{
*(.__rel_dyn_end)
}
.end :
{
*(.__end)
}
_image_binary_end = .; /* bin文件结束地址 */
/*
* Deprecated: this MMU section is used by pxa at present but
* should not be used by new boards/CPUs.
*/
. = ALIGN(4096);
.mmutable : {
*(.mmutable)
}
/*
* Compiler-generated __bss_start and __bss_end, see arch/arm/lib/bss.c
* __bss_base and __bss_limit are for linker only (overlay ordering)
*/
.bss_start __rel_dyn_start (OVERLAY) : { /* .bss段起始地址 */
KEEP(*(.__bss_start));
__bss_base = .;
}
.bss __bss_base (OVERLAY) : {
*(.bss*)
. = ALIGN(4);
__bss_limit = .;
}
.bss_end __bss_limit (OVERLAY) : { /* .bss段结束地址 */
KEEP(*(.__bss_end));
}
.dynsym _image_binary_end : { *(.dynsym) }
.dynbss : { *(.dynbss) }
.dynstr : { *(.dynstr*) }
.dynamic : { *(.dynamic*) }
.plt : { *(.plt*) }
.interp : { *(.interp*) }
.gnu.hash : { *(.gnu.hash) }
.gnu : { *(.gnu*) }
.ARM.exidx : { *(.ARM.exidx*) }
.gnu.linkonce.armexidx : { *(.gnu.linkonce.armexidx.*) }
}
通过上面的分析可以看出: 由于在链接脚本中规定了文件start.o(对应于start.S)作为整个uboot的起始点,因此启动uboot时会执行首先执行start.S。 一般来说,内存空间可分为代码段、数据段、全局变量段、未初始化变量区、栈区、堆区等.其中,栈区由指针SP决定,堆区实质上是由C代码实现的,其它段则由编译器决定.从上面的分析可以看出,从0x00000000地址开始,编译器首先将代码段放在最开始的位置,然后是数据段,然后是bss段(未初始化变量区)。
u-boot.map
u-boot.map 是uboot的映射文件,可以从此文件看到某个文件或者函数链接到了哪个地址,下面打开 u-boot.map,查看各个段的起始地址和结束分别是多少;
从u-boot.map映射文件种,可以知道__image_copy_start为0X87800000,而.text的起始地址也是0X87800000,vectors 段的起始地址也是0X87800000,可以得出各个段的地址关系表,如下;
变量名 | 地址 | 描述 |
---|---|---|
__image_copy_start | 0x87800000 | u-boot拷贝的起始地址 |
__image_copy_end | 0x87850ff0 | u-boot拷贝的结束地址 |
.vectors | 0x87800000 | 中断向量表的起始地址 |
.text | 0x878002e8 | .text段的起始地址 |
__rel_dyn_start | 0x87850ff0 | .rel_dyn段的起始地址 |
__rel_dyn_end | 0x8785cf30 | .rel_dyn段的结束地址 |
_image_binary_end | 0x8785cf30 | 镜像结束地址 |
__bss_start | 0x87850ff0 | .bss段的起始地址 |
__bss_end | 0x878585c0 | .bss段的结束地址 |
注:表中的变量除了__image_copy_start以外,其他的变量值每次编译的时候可能会变化。修改uboot 代码、配置等都会影响到这些值。所以,一切以实际值为准!
uboot的简化版启动流程:
1、设置状态寄存器 cpsr ,使CPU进入 SVC 特权模式,并且禁止 FIQ 和 IRQ;
2、关闭看门狗、中断、MMU、Cache;
3、初始化部分寄存器和外设(时钟、串口、Flash、内存);
4、自搬移uboot到内存中运行;
5、设置栈空间并初始化global_data;
6、剩余大部分硬件的初始化;
7、搬移Linux内核到内存;
如何编译Uboot
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- distclean
make ARCH=arm CORSS_COMPILE=arm-linux-gnueabihf- colibri-imx6ull_defconfig
make V=1 ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j8
ARCH=arm
:arm架构
CROSS_COMPILE
:使用的交叉编译器
如果编译出错,your compile older 6.0,可以参考【 1】
colibri-imx6ull_defconfig
:指定一个config
文件,作为相关版型的配置信息
V=1
:这个选项能显示出编译过程中的详细信息,即是verbose编译模式
-j8
:多核并行编译,可以提高编译速度,受硬件限制
U-boot的存放位置
嵌入式系统,一般使用Flash来作为启动设备,Flash上存储着U-boot、环境变量、内核映像、文件系统等。U-boot存放于Flash的起始地址,所在扇区由Soc规定。
掌握uboot使用的2个关键点:命令和环境变量
环境变量值得注意的有两个:bootcmd和bootargs。
bootcmd:自动启动时执行的命令
uboot上电启动后会自动倒数bootdelay秒,如果没有人按下回车打断启动,则uboot会自动执行bootcmd启动命令来启动内核。
例如:bootcmd=boot_logo;nand read 1000000 3c0000 300000;bootm 1000000
意思是启动u-boot后,执行boot_logo显示logo信息,然后从nand flash中读内核映像到内存,然后启动内核。
bootargs:传递给内核的启动参数
这个参数也比较重要,如果没有设置对,内核很有可能启动不起来,报Not init found之类的日志。还有之前说的Uboot支持多种启动方式也是通过这个bootargs来区分到底是什么方式启动内核的。
下面介绍一下bootargs常用参数,bootargs的种类非常的多,而且随着kernel的发展会出现一些新的参数,使得设置会更加灵活多样。
A. root
用来指定rootfs的位置, 常见的情况有:
root=/dev/ram rw
root=/dev/ram0 rw
请注意上面的这两种设置情况是通用的。
root=/dev/mtdx rw
root=/dev/mtdblockx rw
root=/dev/mtdblock/x rw
上面的这几个在一定情况下是通用的,当然这要看你当前的系统是否支持,不过mtd是字符设备,而mtdblock是块设备,有时候你的挨个的试到底当前的系统支持上面那种情况下,不过root=/dev/mtdblockx rw比较通用。
root=/dev/nfs
在文件系统为基于nfs的文件系统的时候使用。当然指定root=/dev/nfs之后,还需要指定nfsroot=serverip:nfs_dir,即指明文件系统存在那个主机的那个目录下面。
B console
console=tty使用虚拟串口终端设备.
console=ttyS[,options]使用特定的串口,options可以是这样的形式bbbbpnx,这里bbbb是指串口的波特率,p是奇偶位(从来没有看过使用过),n是指的bits。
console=ttySAC[,options]同上面。
console=ttyS0,115200
上面是使用特定的串口
C. mem
mem=xxM
指定内核使用内存的大小,不是必须的。
比如物理内存是1024M, mem=1000M,那么最后保留的24M不被内核使用。这种方法适用于没有DTS的Linux版本,kernle的参数通过cmdline传递过去。
对于有DTS的Linux版本,kernel的参数是通过设备树传入的,因此需要修改设备树的reserved-memory来保留预留内存,所以目前这个方法用的比较多
D. ramdisk_size
ramdisk=xxxxx不推荐
ramdisk_size=xxxxx推荐
上面这两个都可以告诉ramdisk驱动,创建的ramdisk的size,默认情况下是4m(s390默认8M),你可以查看Documentation/ramdisk.txt找到相关的描述,不过ramdisk=xxxxx在新版的内核都已经没有提了,不推荐使用。
E. initrd, noinitrd
当你没有使用ramdisk启动系统的时候,你需要使用noinitrd这个参数,但是如果使用了的话,就需要指定initrd=r_addr,size, r_addr表示initrd在内存中的位置,size表示initrd的大小。
用bootz和booti (kernel_addr_r)(initrd) (fdt_addr_r)命令启动内核时,就要指定initrd内存地址,不然一般用-表示不用initrd内存地址
F. init
init指定的是内核启起来后,进入系统中运行的第一个脚本,一般init=/linuxr。/linuxrc指的是/目录下面的linuxrc脚本,一般是一个连接罢了
G. mtdparts
mtdparts=fc000000.nor_flash:1920k(linux),128k(fdt),20M(ramdisk),4M(jffs2),38272k(user),256k(env),384k(uboot)
要想这个参数起作用,内核中的mtd驱动必须要支持,即内核配置时需要选上Device Drivers ---> Memory Technology Device (MTD) support ---> Command line partition table parsing
mtdparts的格式如下:
mtdparts=[;
:= :[,]
:= [@offset][][ro]
:= unique id used in mapping driver/device
:= standard linux memsize OR "-" to denote all remaining space
:= (NAME)
因此你在使用的时候需要按照下面的格式来设置:
mtdparts=mtd-id:@(),@()
这里面有几个必须要注意的:
a. mtd-id 必须要跟你当前平台的flash的mtd-id一致,不然整个mtdparts会失效
b. size在设置的时候可以为实际的size(xxM,xxk,xx),也可以为'-'这表示剩余的所有空间。
举例:
假设flash 的mtd-id是sa1100,那么你可以使用下面的方式来设置:
mtdparts=sa1100:- → 只有一个分区
mtdparts=sa1100:256k(ARMboot)ro,-(root) → 有两个分区
可以查看drivers/mtd/cmdlinepart.c中的注释找到相关描述。
说完常见的几种bootargs,那么我们来讨论平常我经常使用的几种组合:
1). 假设文件系统是ramdisk,且直接就在内存中,bootargs的设置应该如下:
setenv bootargs ‘initrd=0x32000000,0xa00000 root=/dev/ram0 console=ttySAC0 mem=64M init=/linuxrc’
2). 假设文件系统是ramdisk,且在flash中,bootargs的设置应该如下:
setenv bootargs ‘mem=32M console=ttyS0,115200 root=/dev/ram rw init=/linuxrc’
注意这种情况下你应该要在bootm命令中指定ramdisk在flash中的地址,如bootm kernel_addr ramdisk_addr (fdt_addr)
3). 假设文件系统是jffs2类型的,且在flash中,bootargs的设置应该如下
setenv bootargs ‘mem=32M console=ttyS0,115200 noinitrd root=/dev/mtdblock2 rw rootfstype=jffs2 init=/linuxrc’
4).假设文件系统是基于nfs的,bootargs的设置应该如下
setenv bootargs ‘noinitrd mem=64M console=ttySAC0 root=/dev/nfs nfsroot=192.168.0.3:/nfs ip=192.168.0.5:192.168.0.3:192.168.0.3:255.255.255.0::eth0:off’
或者
setenv bootargs ‘noinitrd mem=64M console=ttySAC0 root=/dev/nfs nfsroot=192.168.0.3:/nfs ip=192.168.0.5’
下面是指rootfs在EMMC第三分区里启动