学习笔记:u-boot引导内核启动

1、u-boot的功能:
- 关看门狗
- 设置栈
- 初始化时钟
- 初始化SDRAM
- 从FLASH读出内核写入SDRAM
- 启动内核
此外,在开发阶段为了方便还额外添加一些其他功能
- 烧写FLASH
- 网卡
- USB
- 串口
2、u-boot主要目的:
1. 从flash上读出内核写到SDRAM中
2. 启动内核(用do_bootm函数)

nand read.jffs2 0x30007FC0 kernel
bootm 0x30007FC0

从nand上kernel这个地址区读写到0x30007FC0地址上去,然后从0x30007FC0上启动内核
3、u-boot的配置
执行

make 100ask24x0_config

相当于执行Makefile中的

MKCONFIG    := $(SRCTREE)/mkconfig
100ask24x0_config   :   unconfig
    @$(MKCONFIG) $(@:_config=) arm arm920t 100ask24x0 NULL s3c24x0

即相当于执行下面的脚本文件

mkconfig 100ask24x0 arm arm920t 100ask24x0 NULL s3c24x0

因此,创建了顶层Makefile包含的文件

include/config.mk

里面的内容是:

ARCH   = arm
CPU    = arm920t
BOARD  = 100ask24x0
SOC    = s3c24x0

创建开发板相关的头文件

include/config.h

里面的内容是:

/* Automatically generated - do not edit */
#include 

4、分析Makefile
u-boot.bin:

$(obj)u-boot.bin:	$(obj)u-boot
        $(OBJCOPY) ${OBJCFLAGS} -O binary $< $@

u-boot:

$(obj)u-boot:		depend version $(SUBDIRS) $(OBJS) $(LIBS) $(LDSCRIPT)
        UNDEF_SYM=`$(OBJDUMP) -x $(LIBS) |sed  -n -e 's/.*\(__u_boot_cmd_.*\)/-u\1/p'|sort|uniq`;\
        cd $(LNDIR) && $(LD) $(LDFLAGS) $$UNDEF_SYM $(__OBJS) \
            --start-group $(__LIBS) --end-group $(PLATFORM_LIBS) \
            -Map u-boot.map -o u-boot

其中,LDFLAGS:

u-boot-1.1.6/config.mk:189:LDFLAGS += -Bstatic -T $(LDSCRIPT) -Ttext $(TEXT_BASE) $(PLATFORM_LDFLAGS)
LDSCRIPT:boot-1.1.6/board/100ask24x0/u-boot.lds
u-boot-1.1.6/board/100ask24x0/config.mk:25:TEXT_BASE = 0x33F80000

执行make命令,通过结果分析目标u-boot的规则

UNDEF_SYM=`arm-linux-objdump -x lib_generic/libgeneric.a board/100ask24x0/lib100ask24x0.a cpu/arm920t/libarm920t.a cpu/arm920t/s3c24x0/libs3c24x0.a lib_arm/libarm.a fs/cramfs/libcramfs.a fs/fat/libfat.a fs/fdos/libfdos.a fs/jffs2/libjffs2.a fs/reiserfs/libreiserfs.a fs/ext2/libext2fs.a net/libnet.a disk/libdisk.a rtc/librtc.a dtt/libdtt.a drivers/libdrivers.a drivers/nand/libnand.a drivers/nand_legacy/libnand_legacy.a drivers/usb/libusb.a drivers/sk98lin/libsk98lin.a common/libcommon.a |sed  -n -e 's/.*\(__u_boot_cmd_.*\)/-u\1/p'|sort|uniq`;\
cd /home/lvxiao/Ftp/system/u-boot/tmp/u-boot-1.1.6 && arm-linux-ld -Bstatic -T /home/lvxiao/Ftp/system/u-boot/tmp/u-boot-1.1.6/board/100ask24x0/u-boot.lds -Ttext 0x33F80000  $UNDEF_SYM cpu/arm920t/start.o \
--start-group lib_generic/libgeneric.a board/100ask24x0/lib100ask24x0.a cpu/arm920t/libarm920t.a cpu/arm920t/s3c24x0/libs3c24x0.a lib_arm/libarm.a fs/cramfs/libcramfs.a fs/fat/libfat.a fs/fdos/libfdos.a fs/jffs2/libjffs2.a fs/reiserfs/libreiserfs.a fs/ext2/libext2fs.a net/libnet.a disk/libdisk.a rtc/librtc.a dtt/libdtt.a drivers/libdrivers.a drivers/nand/libnand.a drivers/nand_legacy/libnand_legacy.a drivers/usb/libusb.a drivers/sk98lin/libsk98lin.a common/libcommon.a --end-group -L /usr/local/arm/gcc-3.4.5-glibc-2.3.6/bin/../lib/gcc/arm-linux/3.4.5 -lgcc \
-Map u-boot.map -o u-boot

这些文件如何组成u-boot.bin,如何排序,哪个是第一个运行的文件,从链接文件u-boot-1.1.6/board/100ask24x0/u-boot.lds可以看到,start.o放在起始运行地址,因此,cpu/arm920t/start.S是第一个运行的源程序
其中,u-boot.lds:

SECTIONS
{
    . = 0x00000000;

    . = ALIGN(4);
    .text      :
    {
      cpu/arm920t/start.o   (.text)
          board/100ask24x0/boot_init.o (.text)
      *(.text)
    }

    . = ALIGN(4);
    .rodata : { *(.rodata) }

    . = ALIGN(4);
    .data : { *(.data) }

    . = ALIGN(4);
    .got : { *(.got) }

    . = .;
    __u_boot_cmd_start = .;
    .u_boot_cmd : { *(.u_boot_cmd) }
    __u_boot_cmd_end = .;

    . = ALIGN(4);
    __bss_start = .;
    .bss : { *(.bss) }
    _end = .;
}

5、分析start.S,运行流程:
①、设置svc模式
②、关看门狗
③、屏蔽中断
④、初始化SDRAM
⑤、设置栈
⑥、时钟
⑦、重定位:将代码从Flash拷贝到SDRAM中
⑧、清BSS段
⑨、调用start_armboot

_start_armboot: .word start_armboot

6、start.S最后一步调用start_armboot函数,进入board.c中的start_armboot函数

u-boot-1.1.6\lib_arm\board.c:void start_armboot (void)

7、start_armboot函数最后调用main_loop函数,进入main.c中的main_loop函数

u-boot-1.1.6\common\main.c:void main_loop (void)
s = getenv ("bootcmd");
    if (bootdelay >= 0 && s && !abortboot (bootdelay)) {
# ifdef CONFIG_AUTOBOOT_KEYED
        int prev = disable_ctrlc(1);    /* disable Control C checking */
# endif

# ifndef CFG_HUSH_PARSER
        {
            printf("Booting Linux ...\n");         
            run_command (s, 0);
        }
# else
        parse_string_outer(s, FLAG_PARSE_SEMICOLON |
                    FLAG_EXIT_FROM_LOOP);
# endif

# ifdef CONFIG_AUTOBOOT_KEYED
        disable_ctrlc(prev);    /* restore Control C checking */
# endif
    }
run_command("menu", 0);

如果在bootdelay时间段内没有按空格则,执行run_command (s, 0);命令启动内核,如果按了空格则执行run_command("menu", 0);跳转到菜单。
8、启动内核或按空格进入u-boot菜单都会从main_loop函数进入run_command函数

u-boot-1.1.6\common\main.c:int run_command (const char *cmd, int flag)

run_command函数会根据cmd参数按照include\command.h中的如下代码找到对应的函数执行

#define _CMD_REMOVE(_name, _cmd)                    \
    int __remove_ ## _name(void)                    \
    {                               \
        if (0)                          \
            _cmd(NULL, 0, 0, NULL);             \
        return 0;                       \
    }
...
...
...
#define U_BOOT_CMD_COMPLETE(_name, _maxargs, _rep, _cmd, _usage, _help, \
                _comp)              \
    _CMD_REMOVE(sub_ ## _name, _cmd)

#endif /* CONFIG_CMDLINE */

#define U_BOOT_CMD(_name, _maxargs, _rep, _cmd, _usage, _help)      \
    U_BOOT_CMD_COMPLETE(_name, _maxargs, _rep, _cmd, _usage, _help, NULL)

9、NOR FLASH、NAND FLASH启动选择:

u-boot-1.1.6\board\100ask24x0\boot_init.c:int CopyCode2Ram(unsigned long start_addr, unsigned char *buf, int size)

10、从NAND FLASH里读出内核

u-boot-1.1.6\common\cmd_nand.c:int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])

Flash上的 uImage=头部+真正的内核,头部:

typedef struct image_header {
    uint32_t    ih_magic;   /* Image Header Magic Number    */
    uint32_t    ih_hcrc;    /* Image Header CRC Checksum    */
    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  */
    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;

头部里关键参数是:
in_load:加载地址
in_eq:入口地址
如果发现内核不在加载地址就把内核移到加载地址,再跳到入口地址。
11、启动内核前的准备
do_bootm函数功能:
-根据头部移动内核到加载地址
-调用do_bootm_linux函数启动linux内核

u-boot-1.1.6\commoncmd_bootm.c:int do_bootm (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])

12、启动内核

u-boot-1.1.6\lib_arm\Armlinux.c:void do_bootm_linux (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[],ulong addr, ulong *len_ptr, int verify)
  • 设置启动参数:在某个地址按某种格式保存参数
setup_start_tag (bd);
setup_memory_tags (bd)
setup_commandline_tag (bd, commandline);
setup_end_tag (bd);
  • 跳到入口地址:
theKernel (0, bd->bi_arch_number, bd->bi_boot_params);

bi_boot_params:启动参数所存的地址
bi_arch_number:机器ID

你可能感兴趣的:(3,u-boot,5,linux内核,u-boot,内核)