嵌入式linux开发uboot移植(四)——uboot启动内核的机制

嵌入式linux开发uboot移植(四)——uboot启动内核的机制

一、嵌入式系统的分区

嵌入式系统部署在Flash设备上时,对于不同SoC和Flash设备,bootloader、kernel、rootfs的分区是不同的。三星S5PV210规定启动设备的分区方案如下:

SD/MMC设备的分区方案:

NandFlash设备的分区方案:

嵌入式系统在启动时,uboot、kernel、rootfs不能随意存放,必须存放在规划好的相应分区,在启动过程中uboot、kernel会到相应分区加载相应内容,确保正常启动,因此嵌入式系统中,uboot和kernel规划的分区和启动设备中uoot、kernel、rootfs的实际存储分区是一致的。在嵌入式系统的启动过程中,开机时uboot运行在SoC内部的SRAM中,uboot会在BL1阶段将整个uboot拷贝到SDRAM中0xC3E00000并远跳转到SDRAM中的BL2运行。Uboot启动kernel时,同样会将kernel从启动设备拷贝到SDRAM中的指定kernel链接位置,最终跳转到kernel运行。

二、uboot支持的内核kernel格式

Linux krenel经过编译后会生成名称为vmlinux或vmlinuz的ELF格式文件,嵌入式系统在部署时烧录的文件格式需要用objcopy工具去制作成烧录镜像格式文件Image。但由于Image太大,因此linux kernel项目对Image进行了压缩,并且在image压缩后的文件的前端附加了一部分解压缩代码,构成压缩镜像格式zImage。同时,uboot自身为了支持linux kernel的启动,在zImage压缩镜像的基础上增加了64字节的头信息,使用uboot自带的mkimage工具生成了uboot自身的压缩镜像格式uImage。在SMDK210的uboot版本中是同时支持zImage和uImage两种压缩镜像格式的。

三、uboot启动内核流程源码分析

uboot启动内核时实际是用\common\cmd_bootm.c中的do_bootm函数来实现的。

do_bootm函数中通过使用条件编译控制CONFIG_ZIMAGE_BOOT宏开关来实现uboot对于zImage压缩镜像的支持与取消支持。

1、do_bootm对zImage的解析

通过读取zImage的头部的第36字节开始的四个字节与LINUX_ZIMAGE_MAGIC(0x016f2818)标志位对比,如果相等则当前的kernel是zImage。如果当前kernel是zImage,则打印出”Boot with zImage”,并对当前uboot启动内核镜像的变量images进行赋值,指定内核版本和内核入口地址。

#define LINUX_ZIMAGE_MAGIC0x016f2818

if (argc < 2) {

addr = load_addr;//如果不指定内核地址,则使用CFG_LOAD_ADDR

} else {

addr = simple_strtoul(argv[1], NULL, 16);//使用指定内核地址

}

if (*(ulong *)(addr + 9*4) == LINUX_ZIMAGE_MAGIC) {//如果当前kernel是zImage

printf("Boot with zImage\n");//打印信息

addr = virt_to_phys(addr);//将虚拟地址转换为物理地址

hdr = (image_header_t *)addr;//将内核地址指定到结构体种?

hdr->ih_os = IH_OS_LINUX;//指定内核版本

hdr->ih_ep = ntohl(addr);//指定内核入口

memmove (&images.legacy_hdr_os_copy, hdr, sizeof(image_header_t));//将内核的信息赋值给当前uboot启动内核的images变量

images.legacy_hdr_os = hdr;

images.legacy_hdr_valid = 1;

goto after_header_check;

}

2、do_bootm对uImage的支持

使用boot_get_kernel函数校验uImage的头信息,将其中的标志位与

IH_MAGIC(0x27051956)对比,如果相等则当前kernel是uImage,打印信息

## Booting kernel from Legacy Image at XXXXXX。将得到的uImage头信息对当前uboot启动内核镜像的变量images进行赋值,指定内核版本和内核入口地址。

case IMAGE_FORMAT_LEGACY:

type = image_get_type (os_hdr);

comp = image_get_comp (os_hdr);

os = image_get_os (os_hdr);

image_end = image_get_image_end (os_hdr);

load_start = image_get_load (os_hdr);

break;

3、do_bootm_linux启动内核

do_bootm_linux函数位于uboot/lib_arm/bootm.c中,函数的主要功能是获取环境变量中的内核传递参数,获取当前uboot启动kernel的images变量中的kernel入口地址,获取uboot中的机器码,准备向kernel传递的参数,最后跳转到kernel执行,uboot执行完毕。uboot在执行完成前打印了”Starting kernel ...”信息。如果uboot实际启动kernel过程中打印出了”Starting kernel ...”信息,则表明uboot在加载、校验kernel是正确的。如果uboot最终启动kenel失败,则大部分原因是uboot向内核传递参数错误导致的。

void do_bootm_linux (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[],

bootm_headers_t *images)

{

ulonginitrd_start, initrd_end;

ulongep = 0;

bd_t*bd = gd->bd;

char*s;

intmachid = bd->bi_arch_number;//uboot的机器码

void(*theKernel)(int zero, int arch, uint params);

intret;

#ifdef CONFIG_CMDLINE_TAG

char *commandline = getenv ("bootargs");//获取环境变量中的内核传递参数变量

#endif

if (images->legacy_hdr_valid) {

ep = image_get_ep (&images->legacy_hdr_os_copy);//获取images变量中的内核入口地址

} else {

puts ("Could not find kernel entry point!\n");

goto error;

}

theKernel = (void (*)(int, int, uint))ep;

s = getenv ("machid");//环境变量中的机器码

if (s) {//如果环境变量中定义了机器码变量,使用环境变量中的机器码

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);

//uboot准备给kernel传递参数

#if defined (CONFIG_SETUP_MEMORY_TAGS) || \

defined (CONFIG_CMDLINE_TAG) || \

defined (CONFIG_INITRD_TAG) || \

defined (CONFIG_SERIAL_TAG) || \

defined (CONFIG_REVISION_TAG) || \

defined (CONFIG_LCD) || \

defined (CONFIG_VFD) || \

defined (CONFIG_MTDPARTITION)

setup_start_tag (bd);

#ifdef CONFIG_SERIAL_TAG

setup_serial_tag (ms);

#endif

#ifdef CONFIG_REVISION_TAG

setup_revision_tag (ms);

#endif

#ifdef CONFIG_SETUP_MEMORY_TAGS

setup_memory_tags (bd);

#endif

#ifdef CONFIG_CMDLINE_TAG

setup_commandline_tag (bd, commandline);

#endif

#ifdef CONFIG_INITRD_TAG

if (initrd_start && initrd_end)

setup_initrd_tag (bd, initrd_start, initrd_end);

#endif

#if defined (CONFIG_VFD) || defined (CONFIG_LCD)

setup_videolfb_tag ((gd_t *) gd);

#endif

#ifdef CONFIG_MTDPARTITION

setup_mtdpartition_tag();

#endif

setup_end_tag (bd);

#endif

printf ("\nStarting kernel ...\n\n");//打印信息Starting kernel ...

#ifdef CONFIG_USB_DEVICE

{

extern void udc_disconnect (void);

udc_disconnect ();

}

#endif

cleanup_before_linux ();

theKernel (0, machid, bd->bi_boot_params);//跳转到kernel执行

return;

error:

do_reset (cmdtp, flag, argc, argv);

return;

}

四、uboot的传参机制

1、uboot传参机制

uboot使用tag方式传参,tag是一种数据结构,与linux kernel中的tag是相同的数据结构。tag结构体包含tag_header和tag_xxxx成员,tag_header结构体包含tag的大小和类型编码,kernel接收到tag参数后根据头信息中类型编码确定tag的类型。

struct tag {

struct tag_header hdr;

union {

struct tag_core         core;

struct tag_mem32        mem;

struct tag_videotext    videotext;

struct tag_ramdisk      ramdisk;

struct tag_initrd       initrd;

struct tag_serialnr     serialnr;

struct tag_revision     revision;

struct tag_videolfb     videolfb;

struct tag_cmdline      cmdline;

struct tag_acorn        acorn;

struct tag_memclk       memclk;

struct tag_mtdpart      mtdpart_info;

} u;

};

struct tag_header {

u32 size;

u32 tag;

};

uboot传参源码:

#if defined (CONFIG_SETUP_MEMORY_TAGS) || \

defined (CONFIG_CMDLINE_TAG) || \

defined (CONFIG_INITRD_TAG) || CONFIG_MTDPARTITION

setup_start_tag (bd);

#ifdef CONFIG_SETUP_MEMORY_TAGS

setup_memory_tags (bd);

#endif

#ifdef CONFIG_CMDLINE_TAG

setup_commandline_tag (bd, commandline);

#endif

#ifdef CONFIG_INITRD_TAG

if (initrd_start && initrd_end)

setup_initrd_tag (bd, initrd_start, initrd_end);

#endif

#ifdef CONFIG_MTDPARTITION

setup_mtdpartition_tag();

#endif

setup_end_tag (bd);

#endif

起始tag是ATAG_CORE类型,结束tag是ATAG_NONE类型,其他类型的tag是有效信息,环境变量bootargs传递给CONFIG_CMDLINE_TAG类型tag。环境变量mtdpart存放的启动设备分区表信息传递给ATAG_MTDPART类型tag。

uboot通过调用theKernel (0, machid, bd->bi_boot_params)函数启动内核,theKernel函数的三个参数通过寄存器来传参,第1个参数0存放在R0中,第2个参数机器码存放在R1中,第3个参数bd->bi_boot_params(传参tag的首地址)存放在R2中。

2、bootargs参数解读

bootargs是uboot中最重要的变量,uboot的环境变量的设置最终都是通过bootargs传递给kernel的。

bootargs参数各部分解读如下:

A、root

用来指定rootfs的位置, 常见的情况有:

root=/dev/ram rw

root=/dev/ram0 rw

root=/dev/mtdx rw

root=/dev/mtdblockx rw

root=/dev/nfs,在文件系统为基于nfs的文件系统的时候使用。指定root=/dev/nfs后,需要指定nfsroot=serverip:nfs_dir。

B、rootfstype

和root配合使用,一般如果根文件系统是ext2,选项可有可无,如果是jffs2,squashfs等文件系统的话,就需要rootfstype指明文件系统的类型,不然无法挂载根分区。

C、console

console=tty<n>  使用虚拟串口终端设备 <n>

console=ttyS<n>[,options] 使用特定的串口<n>,options可以是这样的形式bbbbpnx,这里bbbb是指串口的波特率,p是奇偶校验位,n是指的bits。

console=ttySAC<n>[,options] 同上面。

D、mem

mem=xxM 指定内存的大小,非必须选项

E、ramdisk_size

ramdisk=xxxxx           不推荐

ramdisk_size=xxxxx   推荐

ramdisk 驱动,创建的ramdisk的size,默认情况下是4M

F、 initrd, noinitrd

如果没有使用ramdisk启动系统,则需要使用noinitrd参数。如果使用了ramdisk启动系统,就需要指定initrd=r_addr,size, r_addr表示initrd在内存中的位置,size表示initrd的大小。

G、init

init指定内核启动后,进入系统中运行的第一个脚本,一般init=/linuxrc, 或者init=/etc/preinit,preinit的内容一般是创建console,null设备节点,运行init程序,挂载一些文件系统等等操作。

H、mtdparts

mtdparts=fc000000.nor_flash:1920k(linux),128k(fdt),20M(ramdisk),4M(jffs2),38272k(user),256k(env),384k(uboot)

mtdparts参数需要内核中的mtd驱动支持,即内核配置时需要选上Device Drivers  ---> Memory Technology Device (MTD) support  ---> Command line partition table parsing

mtdparts的格式如下:

mtdparts=<mtddef>[;<mtddef]

<mtddef>  := <mtd-id>:<partdef>[,<partdef>]

<partdef> := <size>[@offset][<name>][ro]

<mtd-id>  := unique id used in mapping driver/device

<size>    := standard linux memsize OR "-" to denote all remaining space

<name>    := (NAME)

mtdparts使用的设置格式:

mtdparts=mtd-id:<size1>@<offset1>(<name1>),<size2>@<offset2>(<name2>)

mtd-id 必须跟当前平台的flash的mtd-id一致,否则整个mtdparts会失效

size设置的时候可以为实际的size(xxM,xxk,xx),也可以用‘-‘表示剩余的所有空间。

假设flash 的mtd-id是sc1200,那么你可以使用下面的方式来设置:

mtdparts=sc1200:-     →  只有一个分区

mtdparts=sc1200:256k(ARMboot)ro,-(root)  →  有两个分区

I、ip

指定系统启动后网卡的ip地址,如果使用了基于nfs的文件系统,必须要指定ip参数。设置ip有两种方法:

ip = ip addr

ip=ip addr:server ip addr:gateway:netmask::which netcard:off

which netcard 是指开发板上的网卡,而不是主机上的网卡。

几种常见的bootargs的使用设置如下:

假设文件系统是ramdisk,且直接就在内存中,bootargs的设置如下:

setenv bootargs ‘initrd=0x32000000,0xa00000 root=/dev/ram0 console=ttySAC0 mem=64M init=/linuxrc’

假设文件系统是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)

假设文件系统是jffs2类型的,且在flash中,bootargs的设置应该如下

setenv bootargs ‘mem=32M console=ttyS0,115200 noinitrd root=/dev/mtdblock2 rw rootfstype=jffs2 init=/linuxrc’

假设文件系统是基于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’

bootargs参数的设置极其灵活,需要根据平台灵活设置。

时间: 2024-08-03 22:06:51

嵌入式linux开发uboot移植(四)——uboot启动内核的机制的相关文章

嵌入式 Linux开发Kernel移植(三)——Kernel工程Makefile分析

嵌入式 Linux开发Kernel移植(三)--Kernel工程Makefile分析 本文选择三星发布的基于SMDKV210开发板的linux 2.6.35.7版本kernel. 一.Kernel Makefle体系简介 1.Kernel Makefile体系组成 Kernel Makefile体系包含Kconfig和Kbuild两个系统. Kconfig系统 Kconfig 对应的是内核配置阶段,make xxconfig就是在使用Kconfig系统.Kconfig由三部分组成: script

嵌入式 Linux开发Kernel移植(二)——kernel内核配置和编译

嵌入式 Linux开发Kernel移植(二)--kernel内核配置和编译 本文选择linux 2.6.35.7版本kernel进行实践. 一.linux kernel源码目录分析 Kbuild,Kernel Build,管理内核编译的文件 Makefile,kernel工程的Makefile. arch,体系架构,arch目录下的子目录存放的是不同种类的架构 block,块设备,一般是存储设备,存放的块设备管理的相关代码 crypto,加密相关,存放加密算法实现代码 Documentation

向嵌入式linux开发板移植openSSH

向嵌入式linux开发板移植openSSH 晚上没事干,想着能不能把高大上的openSSH移植到我现在开发的嵌入式linux开发板上,前不久刚弄出来dropbear,但遇到一些小问题,一会说,所以把openSSH搞上去还是可以的.开发板端为服务器端,由宿主机客户端访问. 2016-8-16 背景 开发板:freescale i.mx6q 宿主机操作系统:ubuntu 14.04 开发板操作系统:linux 3.0.35 基本情况 使用dropbear后,ssh算是在板子上成功跑起来了,参见我另一

嵌入式 Linux开发Kernel移植(一)——kernel内核简介

嵌入式 Linux开发Kernel移植(一)--kernel内核简介 一.Linux Kernel 1.linux kernel简介 Linux kernel是芬兰黑客 Linus Torvalds 1991年在英特尔x86架构上用C语言开发的自由免费的符合POSIX标准的类Unix操作系统. Linux kernel是一个一体化内核(monolithic kernel)系统,提供硬件抽象层.磁盘及文件系统控制.多任务等功能的系统软件,不是一套完整的操作系统. 内核空间与用户空间是程序执行的两种

嵌入式linux开发uboot移植(三)——uboot启动过程源码分析

嵌入式linux开发uboot移植(三)--uboot启动过程源码分析 一.uboot启动流程简介 与大多数BootLoader一样,uboot的启动过程分为BL1和BL2两个阶段.BL1阶段通常是开发板的配置等设备初始化代码,需要依赖依赖于SoC体系结构,通常用汇编语言来实现:BL2阶段主要是对外部设备如网卡.Flash等的初始化以及uboot命令集等的自身实现,通常用C语言来实现. 1.BL1阶段 uboot的BL1阶段代码通常放在start.s文件中,用汇编语言实现,其主要代码功能如下:

嵌入式linux开发uboot移植(七)——三星官方uboot的移植

嵌入式linux开发uboot移植(七)--三星官方uboot的移植 友善之臂Smart210开发板是基于三星SMDKV210评估板裁剪.调整而来的.因此三星官方发布的基于SMDKV210评估板的uboot是移植uboot到Smart210开发板的最合适uboot版本.本文将SMDKV210的uboot移植到Smart210开发板.Smart210开发板的配置如下: SoC:Samsung S5PV210 SDRAM:512MB DDR2 RAM FLASH存储:2G MLC NAND Flas

嵌入式linux开发uboot移植(五)——uboot命令体系

嵌入式linux开发uboot移植(五)--uboot命令体系 本文将根据SMDKV210开发板的三星官方uboot源码分析uboot的命令体系.内容 包括uboot的命令体系的实现机制,uboot命令是如何执行的,以及如何在uboot中添加一个自定义的命令. 一.uboot命令体系简介 uboot命令体系代码放在uboot/common中,包括cmd_xxx.c.command.c .main.c源码文件.uboot实现命令体系的方法是每一个uboot命令对应一个函数,与shell的实现是一致

嵌入式linux开发uboot移植(一)——uboot项目简介

嵌入式linux开发uboot移植(一)--uboot项目简介 一.uboot简介 U-Boot,全称 Universal Boot Loader,是遵循GPL条款的从FADSROM.8xxROM.PPCBOOT逐步发展演化而来的开放源码项目. 在操作系统方面,U-Boot不仅支持嵌入式Linux系统的引导,它还支持NetBSD, VxWorks, QNX, RTEMS, ARTOS, LynxOS, android嵌入式操作系统.目前支持的目标操作系统是OpenBSD, NetBSD, Fre

嵌入式linux开发uboot移植(二)——uboot工程源码目录分析

嵌入式linux开发uboot移植(二)--uboot工程源码目录分析 本文分析的uboot为uboot_smdkv210,是三星官方发布的基于S5PV210评估开发板对应的uboot. 一.uboot源码目录结构解析 1.cpu 本文件夹下的子文件与处理器相关,每个文件夹代表一种CPU系列.每个子目录中都包括cpu.c.interrupts.c.start.S文件. cpu.c主要用于初始化CPU.设置指令Cache和数据Cache等 interrupt.c主要用于设置系统的各种中断和异常 s