U-Boot之LINUX内核引导

基于samsung的Exynos 4412,U-Boot版本为2010.03

前面我们介绍了u-boot的第一阶段和第二阶段,今天我们来介绍u-boot引导内核,这也是u-boot的最后一个阶段,也就是说,这个阶段过后,u-boot的任务就彻底结束了,Linux内核将接管一切软硬件资源。下面开始我们的分析。

u-boot引导内核分为两个阶段

  • 将内核镜像从FLASH加载到内存
  • 启动内核

将内核镜像从FLASH加载到内存,这个目标一般是通过一个叫做do_xxx的函数实现的,xxx是存储介质的名称,比如do_emmc,do_nand等,我们这里使用的是emmc,所以按道理说应该是do_emmc,但是我看了源码,里面并没有按照这种方式来定义,而是用了do_emmcops的名称来替代,do_emmc有别的功能,至于为什么这样,管他呢。do_emmcops这个函数在cmd_mmc.c这个文件中,下面是与有关的代码

        else if (strcmp(argv[1], "read") == 0) {
            int dev = simple_strtoul(argv[2], NULL, 10);
            void *addr = (void *)simple_strtoul(argv[3], NULL, 16);
            u32 cnt = simple_strtoul(argv[5], NULL, 16);
            u32 blk = simple_strtoul(argv[4], NULL, 16);
            u32 n;

            struct mmc *mmc = find_mmc_device(dev);

            if (!mmc)
                return 1;

            printf("\nMMC read: dev # %d, block # %d, count %d ...",
                dev, blk, cnt);

            n = mmc->block_dev.block_read(dev, blk, cnt, addr);

            /* flush cache after read */
            flush_cache((ulong)addr, cnt * 512); /* FIXME */

            printf("%d blocks read: %s\n",
                n, (n==cnt) ? "OK" : "ERROR");
            return (n == cnt) ? 0 : 1;

addr是由我们传递的argv[3]解析得到的,mmc->block_dev.block_read把对应的内核镜像从emmc读到addr处,

U_BOOT_CMD(
    mmc, 6, 1, do_mmcops,
    "MMC sub system",
    "read <device num> addr blk# cnt\n"
    "mmc write <device num> addr blk# cnt\n"
    "mmc rescan <device num>\n"
    "mmc erase <boot | user> <device num> <start block> <block count>\n"
    "mmc list - lists available devices");

除此之外,这个函数还支持其它功能,如写入、擦除、扫描等,通过传入的参数来进行功能选择

启动内核,在上一步中, 内核镜像已经被加载到了内存中,但这个内核镜像还不一定能用,因为它还没被解压缩,也有可能不在正确的内存地址上,所以,do_bootm的首要工作是将内核解压缩并加载到正确的内存地址上,这些功能主要在bootm_load_os这个函数中完成

ret = bootm_load_os(images.os, &load_end, 1);

当内核准备好之后,我们就可以启动内核了,这个目标主要是通过do_bootm_linux这个函数来完成

/* boot the linux kernel */
int do_bootm_linux(int flag, int argc, char *argv[], bootm_headers_t * images)
{
    char *bootargs;
    ulong initrd_start, initrd_end;
    ulong rd_len;
    unsigned int data, len, checksum;
    unsigned int initrd_addr, kernend;
    void (*kernel) (struct linux_romvec *, void *);
    struct lmb *lmb = &images->lmb;
    int ret;

    if ((flag != 0) && (flag != BOOTM_STATE_OS_GO))
        return 1;

    /* Get virtual address of kernel start */
    linux_hdr = (void *)images->os.load;

    /* */
    kernel = (void (*)(struct linux_romvec *, void *))images->ep;

    /* check for a SPARC kernel */
    if ((linux_hdr->hdr[0] != 'H') ||
        (linux_hdr->hdr[1] != 'd') ||
        (linux_hdr->hdr[2] != 'r') || (linux_hdr->hdr[3] != 'S')) {
        puts("Error reading header of SPARC Linux kernel, aborting\n");
        goto error;
    }
#ifdef PRINT_KERNEL_HEADER
    printf("## Found SPARC Linux kernel %d.%d.%d ...\n",
           linux_hdr->linuxver_major,
           linux_hdr->linuxver_minor, linux_hdr->linuxver_revision);
#endif

#ifdef CONFIG_USB_UHCI
    usb_lowlevel_stop();
#endif

    /* set basic boot params in kernel header now that it has been
     * extracted and is writeable.
     */

    /* Calc length of RAM disk, if zero no ramdisk available */
    rd_len = images->rd_end - images->rd_start;

    if (rd_len) {
        ret = boot_ramdisk_high(lmb, images->rd_start, rd_len,
                    &initrd_start, &initrd_end);
        if (ret) {
            puts("### Failed to relocate RAM disk\n");
            goto error;
        }

        /* Update SPARC kernel header so that Linux knows
         * what is going on and where to find RAM disk.
         *
         * Set INITRD Image address relative to RAM Start
         */
        linux_hdr->hdr_input.ver_0203.sparc_ramdisk_image =
            initrd_start - CONFIG_SYS_RAM_BASE;
        linux_hdr->hdr_input.ver_0203.sparc_ramdisk_size = rd_len;
        /* Clear READ ONLY flag if set to non-zero */
        linux_hdr->hdr_input.ver_0203.root_flags = 1;
        /* Set root device to: Root_RAM0 */
        linux_hdr->hdr_input.ver_0203.root_dev = 0x100;
        linux_hdr->hdr_input.ver_0203.ram_flags = 0;
    } else {
        /* NOT using RAMDISK image, overwriting kernel defaults */
        linux_hdr->hdr_input.ver_0203.sparc_ramdisk_image = 0;
        linux_hdr->hdr_input.ver_0203.sparc_ramdisk_size = 0;
        /* Leave to kernel defaults
           linux_hdr->hdr_input.ver_0203.root_flags = 1;
           linux_hdr->hdr_input.ver_0203.root_dev = 0;
           linux_hdr->hdr_input.ver_0203.ram_flags = 0;
         */
    }

    /* Copy bootargs from bootargs variable to kernel readable area */
    bootargs = getenv("bootargs");
    prepare_bootargs(bootargs);

    /* turn on mmu & setup context table & page table for process 0 (kernel) */
    srmmu_init_cpu((unsigned int)kernel);

    /* Enter SPARC Linux kernel
     * From now on the only code in u-boot that will be
     * executed is the PROM code.
     */
    kernel(kernel_arg_promvec, (void *)images->ep);

    /* It will never come to this... */
    while (1) ;

      error:
    return 1;
}

这个函数主要完成了两个功能:传递参数给Linux内核跳转到linux入口地址

到这里,u-boot的任务就彻底完成了,下面就该Linux上场了

注:这里有很多细节没有介绍,但我对于这种工具性知识的学习一向讲求先整体、后细节。

如果有什么不对的地方,请指正,万分感谢。

原文地址:https://www.cnblogs.com/YinShijia/p/12036877.html

时间: 2024-10-08 10:41:38

U-Boot之LINUX内核引导的相关文章

Linux 内核引导参数简介

概述 内核引导参数大体上可以分为两类:一类与设备无关.另一类与设备有关.与设备有关的引导参数多如牛毛,需要你自己阅读内核中的相应驱动程序源码以获取其能够接受的引导参数.比如,如果你想知道可以向 AHA1542 SCSI 驱动程序传递哪些引导参数,那么就查看 drivers/scsi/aha1542.c 文件,一般在前面 100 行注释里就可以找到所接受的引导参数说明.大多数参数是通过"__setup(... , ...)"函数设置的,少部分是通过"early_param(..

Linux内核的引导

1,当系统上电或复位时,CPU会将PC指针赋值为一个特定的地址0xFFFF0并执行该地址处的指令.在PC机中,该地址位于BIOS中,它保存在主板上的ROM或Flash中 2,BIOS运行时按照CMOS的设置定义的启动设备顺序来搜索处于活动状态并且可以引导的设备.若从硬盘启动,BIOS会将硬盘MBR(主引导记录)中的内容加载到RAM.MBR是一个512字节大小的扇区,位于磁盘上的第一个扇区中(0道0柱面1扇区).当MBR被加载到RAM中之后,BIOS就会将控制权交给MBR 3,主引导加载程序查找并

Linux内核中常见内存分配函数

1.原理说明 Linux内核中采用了一种同时适用于32位和64位系统的内存分页模型,对于32位系统来说,两级页表足够用了,而在x86_64系统中,用到了四级页表,如图2-1所示.四级页表分别为: l   页全局目录(Page Global Directory) l   页上级目录(Page Upper Directory) l   页中间目录(Page Middle Directory) l   页表(Page Table) 页全局目录包含若干页上级目录的地址,页上级目录又依次包含若干页中间目录

Linux内核中常见内存分配函数zz

https://blog.csdn.net/wzhwho/article/details/4996510 1.      原理说明 Linux内核中采用了一种同时适用于32位和64位系统的内存分页模型,对于32位系统来说,两级页表足够用了,而在x86_64系统中,用到了四级页表,如图2-1所示.四级页表分别为: l         页全局目录(Page Global Directory) l         页上级目录(Page Upper Directory) l         页中间目录(

linux内核启动引导过程

linux内核(uImage格式镜像,uImage = zImage_压缩的内核镜像 + 0x40字节大小的uboot格式信息头)的启动过程大体可以分为三个阶段: 第一:内核的自解压过程(汇编+C语言实现) 主要由.arch/arm/boot/compressed对zImage完成解压,并调用call_kernel跳转到下阶段代码. 第二:板级引导阶段(汇编实现) 主要进行cpu和体系结构的检查.cpu本身的初始化以及页表的建立等 第三:通用内核启动阶段(C语言实现:重点分析) 1. 进入ini

Linux内核和根文件系统引导加载程序

续博文<u-boot之u-boot-2009.11启动过程分析> Linux内核启动及文件系统载入过程 当u-boot開始运行bootcmd命令.就进入Linux内核启动阶段,与u-boot类似,普通Linux内核的启动过程也能够分为两个阶段,但针对压缩了的内核如uImage就要包含内核自解压过程了.本文以linux-2.6.37版源代码为例分三个阶段来描写叙述内核启动全过程. 第一阶段为内核自解压过程.第二阶段主要工作是设置ARM处理器工作模式.使能MMU.设置一级页表等.而第三阶段则主要为

Linux内核编译以及内核引导启动

Linux源码编译的步骤,其实很简单,主要是在配置哪些模块的功能开启方面需要熟悉下,下面来看看,编译Linux内核需要有哪些步骤: 1.需要去Linux官网上去下载一个内核版本的源码, https://www.kernel.org/ 实例使用的是如下版本: linux-2.6.32.65.tar.xz 2.需要解压下载下来的压缩包,解压出来后会出现如下文件夹 xz  linux-2.6.32.65.tar.xz tar xvf linux-2.6.32.65.tar linux-2.6.32.6

The Kernel Boot Process.内核引导过程

原文标题:The Kernel Boot Process 原文地址:http://duartes.org/gustavo/blog/ [注:本人水平有限,只好挑一些国外高手的精彩文章翻译一下.一来自己复习,二来与大家分享.] 上一篇文章解释了计算机的引导过程,正好讲到引导装载程序把系统内核镜像塞进内存,准备跳转到内核入口点去执行的时刻.作为引导启动系列文章的最后一篇,就让我们深入内核,去看看操作系统是怎么启动的吧.由于我习惯以事实为依据讨论问题,所以文中会出现大量的链接引用Linux 内核2.6

linux kexec内核引导

linux kexec 介绍 kexec的功能是用一个运行的内核去运行一个新内核,就像运行一个应用程序一样.这种机制因为跳过了bootloader,可以实现系统的快速重启.另外kdump也是基于kexec实现(示意如下). kexec的实现有几点难点: 在当前内核的上下文中,如何用新内核去替换现有内核? 正常复位启动过程中,设备会被复位(或初始化)到已知状态.跳过了复位阶段,那如何在新内核kexec启动时,保证设备状态的可靠? kexec的使用示例如下,其分为2部分:kexec内核加载和kexe