`
0769
  • 浏览: 83611 次
  • 性别: Icon_minigender_1
  • 来自: 东莞
社区版块
存档分类
最新评论

U-Boot启动分析C语言部分

阅读更多
U-Boot启动分析C语言部分
 
 
最近由于在做u-boot-1.1.4到深圳优龙的ST2410开发板的移植,想通过修改U-Boot的源代码来让它支持从NAND Flash的启动。现在移植已经做得有点眉目了,还存在一些问题。在这个过程中,最近遇到的问题是启动ARM Linux的必备条件和U-Boot的go命令和bootm命令的一些区别,有所体会,在此记录一下。

先来引用一下这篇介绍“ARM Linux内核启动要求”的文章ARM Linux Kernel Boot Requirements,是ARM Linux内核的维护者Russell King写的。
  • CPU register settings
    • r0 = 0.
    • r1 = machine type number.
    • r2 = physical address of tagged list in system RAM.
  • CPU mode
    • All forms of interrupts must be disabled (IRQs and FIQs.)
    • The CPU must be in SVC mode. (A special exception exists for Angel.)
  • Caches, MMUs
    • The MMU must be off.
    • Instruction cache may be on or off.
    • Data cache must be off and must not contain any stale data.
  • Devices
    • DMA to/from devices should be quiesced.
  • The boot loader is expected to call the kernel image by jumping directly to the first instruction of the kernel image.
大致就是以上条件了,请特别关注一下第一条,这个基本上就是U-Boot的go命令和bootm命令之间的本质区别所在了。先来看看bootm命令的实现,在common/cmd_bootm.c的第119行开始有:

#ifdef CONFIG_PPC
static boot_os_Fcn do_bootm_linux;
#else
extern boot_os_Fcn do_bootm_linux;
#endif

这里的预编译宏说明了,非PPC体系结构的CPU的do_bootm_linux()函数都不是在这个文件内实现的(extern)。可想而知,这个函数的实现应该是和体系结构相关的,具体到arm体系结构的实现就是在lib_arm/armlinux.c这个文件当中。可以看到从lib_arm/armlinux.c中的第77行开始就是do_bootm_linux()函数的实现。
 
其中第85行声明了这样一个函数指针theKernel:

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

看看它的名字和参数的命名我们也可以猜到这个其实就是内核的入口函数的指针了。几个参数的命名也说明了上文提到的ARM Linux内核启动要求的第一条,因为根据ACPS(ARM/Thumb Procedure Call Standard)的规定,这三个参数就是依次使用r0,r1和r2来传递的。
 
接下来第93行就是给这个函数指针赋值:

theKernel = (void (*)(int, int, uint))ntohl(hdr->ih_ep);

可以看到theKernel被赋值为hdr->ih_ep,这个hdr是指使用tools/mkimage工具程序制作uImage时加在linux.bin.gz前面的一个头部,而ih_ep结构体成员保存的就是使用mkimage时指定的-e参数的值,即内核的入口点(Entry Point)。 知道了hdr->ih_ep的意义之后,给theKernel赋这个值也就是理所当然的了。
 
最后是对内核入口函数的调用,发生在第270行:

theKernel (0, bd->bi_arch_number, bd->bi_boot_params);

调用的时候对参数进行赋值,r0=0,r1=bd->bi_arch_number,r2=bd->bi_boot_params,一个都不少。至此U-Boot的使命完成,开始进入ARM Linux的美丽新世界。
本文还是以u-boot-1.1.4为例,以make smdk2410_config命令配置源代码之后进行分析。
对于C语言部分代码的调用出现在cpu/arm920t/start.S的第223行:
[code]
        ldr pc, _start_armboot
 
_start_armboot: .word start_armboot
[/code]
这里的start_armboot就是lib_arm/board.c中第207行的start_armboot()函数,由此U-Boot开始执行C语言部分的代码。
start_armboot()函数一开始首先是一个宏调用:
[code]
DECLARE_GLOBAL_DATA_PTR;
[/code]
这个宏的定义在include/asm-arm/global_data.h文件的第64行:
[code]
#define DECLARE_GLOBAL_DATA_PTR     register volatile gd_t *gd asm ("r8")
[/code]
大概的意思是声明一个指向gd_t结构体变量的指针gd,并固定使用寄存器r8来存放该指针。而对gd_t结构体的定义从上面的第36行开始:
[code]
typedef struct global_data {
    bd_t  *bd;
    unsigned long flags;
    unsigned long baudrate;
    unsigned long have_console; /* serial_init() was called */
    unsigned long reloc_off; /* Relocation Offset */
    unsigned long env_addr; /* Address  of Environment struct */
    unsigned long env_valid; /* Checksum of Environment valid? */
    unsigned long fb_base; /* base address of frame buffer */
#ifdef CONFIG_VFD
    unsigned char vfd_type; /* display type */
#endif
#if 0
    unsigned long cpu_clk; /* CPU clock in Hz!  */
    unsigned long bus_clk;
    unsigned long ram_size; /* RAM size */
    unsigned long reset_status; /* reset status register at boot */
#endif
    void  **jt;  /* jump table */
} gd_t;
[/code]
在lib_arm/board.c文件中之所以能够直接使用这个宏和gd_t结构体是因为包含了include/common.h头文件,在common.h的第115行包含了include/asm-arm/global_data.h头文件。
继续来看start_armboot()函数的执行。lib_arm/board.c第229行开始的一个for循环:
[code]
for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
    if ((*init_fnc_ptr)() != 0) {
        hang ();
    }
}
[/code]
这里的init_sequence是定义在上面第190行处的一个指针数组:
[code]
init_fnc_t *init_sequence[] = {
    cpu_init,  /* basic cpu dependent setup */
    board_init,  /* basic board dependent setup */
    interrupt_init,  /* set up exceptions */
    env_init,  /* initialize environment */
    init_baudrate,  /* initialze baudrate settings */
    serial_init,  /* serial communications setup */
    console_init_f,  /* stage 1 init of console */
    display_banner,  /* say that we are here */
    dram_init,  /* configure available RAM banks */
    display_dram_config,
#if defined(CONFIG_VCMA9) || defined (CONFIG_CMC_PU2)
    checkboard,
#endif
    NULL,
};
[/code]
数组类型init_fnc_t则是一个定义在第188行的函数指针类型:
[code]
typedef int (init_fnc_t) (void);
[/code]
由此可知,init_sequence[]数组当中的所有元素都是函数指针了,而这个for循环的作用就是遍历这个数组的所有元素,然后用“(*init_fnc_ptr)()”就依次调用了这些函数来进行初始化的工作。
书接上回,我们依次来看看init_sequence[]数组当中的各个元素。
 
首先是cpu_init()函数,定义于lib_arm/arm920t/cpu.c第88行。
 
接下来的board_init()函数定义于board/smdk2410/smdk2410.c第68行:

int board_init (void)
{
    DECLARE_GLOBAL_DATA_PTR;
    S3C24X0_CLOCK_POWER * const clk_power = S3C24X0_GetBase_CLOCK_POWER();
    S3C24X0_GPIO * const gpio = S3C24X0_GetBase_GPIO();

    /* to reduce PLL lock time, adjust the LOCKTIME register */
    clk_power->LOCKTIME = 0xFFFFFF;

    /* configure MPLL */
    clk_power->MPLLCON = ((M_MDIV << 12) + (M_PDIV << 4) + M_SDIV);

    /* some delay between MPLL and UPLL */
    delay (4000);

    /* configure UPLL */
    clk_power->UPLLCON = ((U_M_MDIV << 12) + (U_M_PDIV << 4) + U_M_SDIV);

    /* some delay between MPLL and UPLL */
    delay (8000);

    /* set up the I/O ports */
    gpio->GPACON = 0x007FFFFF;
    gpio->GPBCON = 0x00044555;
    gpio->GPBUP = 0x000007FF;
    gpio->GPCCON = 0xAAAAAAAA;
    gpio->GPCUP = 0x0000FFFF;
    gpio->GPDCON = 0xAAAAAAAA;
    gpio->GPDUP = 0x0000FFFF;
    gpio->GPECON = 0xAAAAAAAA;
    gpio->GPEUP = 0x0000FFFF;
    gpio->GPFCON = 0x000055AA;
    gpio->GPFUP = 0x000000FF;
    gpio->GPGCON = 0xFF95FFBA;
    gpio->GPGUP = 0x0000FFFF;
    gpio->GPHCON = 0x002AFAAA;
    gpio->GPHUP = 0x000007FF;

    /* arch number of SMDK2410-Board */
    gd->bd->bi_arch_number = MACH_TYPE_SMDK2410;

    /* adress of boot parameters */
    gd->bd->bi_boot_params = 0x30000100;

    icache_enable();
    dcache_enable();

    return 0;
}

interrupt_init()函数定义于cpu/arm920t/s3c24x0/interrupts.c第55行:

int interrupt_init (void)
{
    S3C24X0_TIMERS * const timers = S3C24X0_GetBase_TIMERS();

    /* use PWM Timer 4 because it has no output */
    /* prescaler for Timer 4 is 16 */
    timers->TCFG0 = 0x0f00;
    if (timer_load_val == 0)
    {
        /*
         * for 10 ms clock period @ PCLK with 4 bit divider = 1/2
         * (default) and prescaler = 16. Should be 10390
         * @33.25MHz and 15625 @ 50 MHz
         */

        timer_load_val = get_PCLK()/(2 * 16 * 100);
    }
    /* load value for 10 ms timeout */
    lastdec = timers->TCNTB4 = timer_load_val;
    /* auto load, manual update of Timer 4 */
    timers->TCON = (timers->TCON & ~0x0700000) | 0x600000;
    /* auto load, start Timer 4 */
    timers->TCON = (timers->TCON & ~0x0700000) | 0x500000;
    timestamp = 0;

    return (0);
}

分享到:
评论

相关推荐

    u-boot 启动流程分析

    《U-Boot启动流程分析——深入理解ARM启动》 在嵌入式系统的世界里,U-Boot作为一款广泛应用的引导加载程序,对于系统的初始化和启动起着至关重要的作用。本文将详细探讨U-Boot在ARM架构下的启动流程,旨在帮助读者...

    U-boot启动流程分析

    U-Boot启动流程分析: U-Boot是一种非常流行的开源Bootloader,广泛应用于嵌入式系统中,负责初始化硬件设备,建立内存空间的映射图,从而为操作系统的运行创造条件。U-Boot启动流程分为两个主要阶段:第一阶段和第...

    基于PXA255的U-Boot启动分析及移植

    基于PXA255的U-Boot启动分析及移植 U-Boot是一款功能强大、灵活性高的开放源码Bootloader,能够支持多种处理器架构和开发板。为了在PXA255目标板上移植U-Boot,需要详细了解U-Boot的启动流程和移植过程。 U-Boot的...

    U-Boot启动流程分析与S3C2440上的移植.pdf

    ### U-Boot启动流程分析与S3C2440上的移植 #### 一、引言 引导加载程序(Bootloader)是系统启动时运行的第一段代码,它负责初始化硬件和引导操作系统内核。在嵌入式系统中,Bootloader的重要性不言而喻。U-Boot...

    u-boot代码详细分析

    ### U-Boot代码详细分析 #### 1. U-Boot简介 ##### 1.1 U-Boot的来源 U-Boot(Universal Boot Loader)是一种开源的Bootloader项目,旨在为不同架构的处理器提供通用的引导加载支持。U-Boot最初是由DENX软件工程...

    U-Boot启动过程

    ### U-Boot启动过程详解 #### 一、U-Boot简介 U-Boot(Universal Boot Loader)是一款开源的通用引导加载程序,适用于多种处理器架构,包括但不限于ARM、PowerPC、x86等。U-Boot的主要功能是初始化硬件资源、加载...

    U-Boot启动第二阶段代码分析.rar_U-Boot启动;第二阶段_arm_启动分析

    U-Boot第一阶段的启动流程。(nandflash启动,把nand的4k代码考到sram中,因为nand没址线,不能映射到内存,所以通过sram进行过度,sram中4k代码把整个uboot拷贝到sdram上,初始化好堆栈,为c语言提供条件,进入uboot...

    U-BOOT的启动流程及移植

    U-BOOT启动流程一般分为两个阶段:Stage 1和Stage 2。 - **Stage 1(start.s代码结构)** - 入口点定义:Bootloader通常从ROM(通常是Flash)的0x0地址开始执行。 - 异常向量设置:配置异常处理机制。 - CPU配置...

    U-Boot启动流程分析

    总结来说,U-Boot启动流程的分析涵盖了从硬件初始化到操作系统加载的全过程,理解这一流程对于嵌入式系统的开发和调试至关重要。通过对链接脚本、汇编代码和C代码的深入理解,我们可以更好地定制和优化U-Boot以适应...

    ARm 处理器 U-boot启动过程详解

    总结起来,ARm处理器上的U-boot启动过程涉及了硬件初始化、设备检测、环境变量管理、操作系统加载等多个环节,是嵌入式系统和服务器启动流程中的重要组成部分。理解这个过程对于系统开发、调试和故障排查都至关重要...

    基于S3C2440的u-boot移植详解

    #### 三、S3C2440上的u-boot启动过程分析 针对S3C2440平台,我们将详细介绍u-boot第一阶段的实现细节。 ##### 3.1 硬件初始化 硬件初始化部分主要包括以下步骤: - **设置异常向量**:通过修改程序计数器(PC)...

    庖丁解牛分析U-BOOT启动过程

    《庖丁解牛分析U-BOOT启动过程》 U-Boot作为嵌入式系统中广泛使用的引导加载程序,它的启动过程对于理解系统运作至关重要。本文将深入解析DM368平台下U-Boot的启动流程,帮助初学者掌握移植U-Boot的基础知识。 一...

    u-boot启动过程分析

    ### U-Boot启动过程分析 #### 一、概述 U-Boot(Universal Boot Loader)是一种广泛应用于嵌入式系统的引导加载程序,它支持多种处理器架构和操作系统。在启动过程中,U-Boot首先通过汇编语言完成CPU的初始化,...

    u-boot-1.3.2

    一、U-Boot启动流程详解 1. 上电自检(POST):硬件初始化,包括CPU、内存、时钟等基本组件的检测。 2. 引导加载器加载:通常U-Boot程序位于设备的固件存储区,如ROM或闪存中。 3. 系统设置:配置CPU频率、内存参数...

    xilinx u-boot

    在学习和修改xilinx u-boot源代码时,你需要有扎实的C语言基础,了解汇编语言和嵌入式系统原理。同时,对Xilinx的硬件平台和相关开发工具(如Vivado或SDK)的熟悉也是必不可少的。通过阅读源代码、理解其架构、调试...

Global site tag (gtag.js) - Google Analytics