u-boot-2014.10代码分析及移植说明
鉴于没有妹纸可以陪,那就找件事打发时间吧,写到哪算哪。
2014.10与2013.07的版本相比,代码上并没有跨越式的改变,但是编译方式上,却有了极大的改变,一种更为优越的编译体系Kbuild&Kconfig System终于被引入了u-boot中。
Kbuild全称是the Linux Kernel Build System,它是从linux 2.6开始引入内核的,而Kconfig即Kernel config其实算是该体系的一部分。无论在该体系引入前还是引入后,内核编译根本上所使用的一直都是GNU make那一套,即使用Makefile及其对应规则来进行编译链接。然而,linux是一个多平台的系统,其支持多种架构以及各种设备驱动,这也就意味着,它的源码是它所有支持的架构以及驱动的一个集合,我们必须针对我们自己的系统进行裁剪,即有选择性的编译。如果没有kbuild体系,我们就需要面对浩如烟海的Makefile文件,针对每一个需要或者不需要编译的文件进行对应的修改,尤其对于一些牵一发而动全身的配置,更显得麻烦。而Kbuild体系则很好的解决了这一问题,它不仅事实上简化了makefile文件,同时也简化了维护的工作。Kbuild相当于是构建在Makefile上的一个抽象层。我们只需要在代码新添加时对Kconfig及Makefile进行一次修改,之后我们的维护工作就只是通过make *config来进选择而已。另外,还需要提及的一点是,对于编译过程,Kbuild能够展示出一个很清晰的依赖关系,更方便我们的维护。
基于以上这些优点,u-boot在2002年即有人建议将其引入主线,而直到2013.10,才开始引入Kbuild,而一直到2014.10,该体系才算完整的引入主线,如今,u-boot也可以如内核一般通过make *config来方便的进行配置。
以上是编译方面的新变化,接下来以armv7架构为例逐步分析代码,该代码是直接从官网新鲜下载的,所以,也许又tm有变化。
依照往常的规矩,我们可以打开arch/arm/cpu/armv7/start.S,该文件中包含着整个程序的入口
1 /* 2 * armboot - Startup Code for OMAP3530/ARM Cortex CPU-core 3 * 4 * Copyright (c) 2004 Texas Instruments <[email protected]> 5 * 6 * Copyright (c) 2001 Marius Gr枚ger <[email protected]> 7 * Copyright (c) 2002 Alex Z眉pke <[email protected]> 8 * Copyright (c) 2002 Gary Jennejohn <[email protected]> 9 * Copyright (c) 2003 Richard Woodruff <[email protected]> 10 * Copyright (c) 2003 Kshitij <[email protected]> 11 * Copyright (c) 2006-2008 Syed Mohammed Khasim <[email protected]> 12 * 13 * SPDX-License-Identifier: GPL-2.0+ 14 */ 15 16 #include <asm-offsets.h> 17 #include <config.h> 18 #include <version.h> 19 #include <asm/system.h> 20 #include <linux/linkage.h> 21 22 /************************************************************************* 23 * 24 * Startup Code (reset vector) 25 * 26 * do important init only if we don‘t start from memory! 27 * setup Memory and board specific bits prior to relocation. 28 * relocate armboot to ram 29 * setup stack 30 * 31 *************************************************************************/ 32 33 .globl reset 34 35 reset: 36 bl save_boot_params
我们顺利找到了复位入口--reset,但是前面直接就是文件头和注释是怎么回事?-----特么的异常向量表呢?!
此时,只能从链接脚本中寻找答案了,打开arch/arm/cpu/u-boot.lds ,
1 #include <config.h> 2 OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm") 3 OUTPUT_ARCH(arm) 4 ENTRY(_start) 5 SECTIONS 6 { 7 . = 0x00000000; 8 . = ALIGN(4); 9 .text : 10 { 11 *(.__image_copy_start) 12 *(.vectors) 13 CPUDIR/start.o (.text*) 14 *(.text*) 15 }
在start.o之前有个.vectors段,于是顺藤摸瓜,找到arch/arm/vector.S
1 /* 2 * vectors - Generic ARM exception table code 3 * 4 * Copyright (c) 1998 Dan Malek <[email protected]> 5 * Copyright (c) 1999 Magnus Damm <kieraypc01.p.y.kie.era.ericsson.se> 6 * Copyright (c) 2000 Wolfgang Denk <[email protected]> 7 * Copyright (c) 2001 Alex Z眉pke <[email protected]> 8 * Copyright (c) 2001 Marius Gr枚ger <[email protected]> 9 * Copyright (c) 2002 Alex Z眉pke <[email protected]> 10 * Copyright (c) 2002 Gary Jennejohn <[email protected]> 11 * Copyright (c) 2002 Kyle Harris <[email protected]> 12 * 13 * SPDX-License-Identifier: GPL-2.0+ 14 */ 15 16 #include <config.h> 17 18 /* 19 ************************************************************************* 20 * 21 * Symbol _start is referenced elsewhere, so make it global 22 * 23 ************************************************************************* 24 */ 25 26 .globl _start 27 28 /* 29 ************************************************************************* 30 * 31 * Vectors have their own section so linker script can map them easily 32 * 33 ************************************************************************* 34 */ 35 36 .section ".vectors", "x" 37 38 /* 39 ************************************************************************* 40 * 41 * Exception vectors as described in ARM reference manuals 42 * 43 * Uses indirect branch to allow reaching handlers anywhere in memory. 44 * 45 ************************************************************************* 46 */ 47 48 _start: 49 50 #ifdef CONFIG_SYS_DV_NOR_BOOT_CFG 51 .word CONFIG_SYS_DV_NOR_BOOT_CFG 52 #endif 53 54 b reset 55 ldr pc, _undefined_instruction 56 ldr pc, _software_interrupt 57 ldr pc, _prefetch_abort 58 ldr pc, _data_abort 59 ldr pc, _not_used 60 ldr pc, _irq 61 ldr pc, _fiq
通过文件头的 .section ".vectors" 可以确定,这就是我们所需要的异常向量表,而其中全局变量_start便是整个程序的真正起点。至于这样做的原因,大概是在于,所有ARM架构的芯片,异常向量表的位置及结构是万年不易的,是可以复用的代码,因此从start.S中提出来了。
回到start.s,
1 /* 2 * disable interrupts (FIQ and IRQ), also set the cpu to SVC32 mode, 3 * except if in HYP mode already 4 */ 5 mrs r0, cpsr 6 and r1, r0, #0x1f @ mask mode bits 7 teq r1, #0x1a @ test for HYP mode 8 bicne r0, r0, #0x1f @ clear all mode bits 9 orrne r0, r0, #0x13 @ set SVC mode 10 orr r0, r0, #0xc0 @ disable FIQ and IRQ 11 msr cpsr,r0
注释已经很完善,关闭中断(FIQ和IRQ,说起来FIQ现在几乎无用了啊!),进入强大(伪)的特权模式--SVC模式
1 /* 2 * Setup vector: 3 * (OMAP4 spl TEXT_BASE is not 32 byte aligned. 4 * Continue to use ROM code vector only in OMAP4 spl) 5 */ 6 #if !(defined(CONFIG_OMAP44XX) && defined(CONFIG_SPL_BUILD)) 7 /* Set V=0 in CP15 SCTRL register - for VBAR to point to vector */ 8 mrc p15, 0, r0, c1, c0, 0 @ Read CP15 SCTRL Register 9 bic r0, #CR_V @ V = 0 10 mcr p15, 0, r0, c1, c0, 0 @ Write CP15 SCTRL Register 11 12 /* Set vector address in CP15 VBAR register */ 13 ldr r0, =_start 14 mcr p15, 0, r0, c12, c0, 0 @Set VBAR 15 #endif
配置异常向量表基址,及将_start的地址值写入VBAR寄存器
1 /* the mask ROM code should have PLL and others stable */ 2 #ifndef CONFIG_SKIP_LOWLEVEL_INIT 3 bl cpu_init_cp15 4 bl cpu_init_crit 5 #endif
所谓lowlevel init,涉及的是CPU的初始化以及芯片内PLL/CRPM/DDR等的初始化
至于cpu_init_cp15,这是针对cache以及mmu的一系列无效及关闭操作,属于无需更改的代码 。
而cpu_init_crit才是主要的lowlevel init。
1 /************************************************************************* 2 * 3 * CPU_init_critical registers 4 * 5 * setup important registers 6 * setup memory timing 7 * 8 *************************************************************************/ 9 ENTRY(cpu_init_crit) 10 /* 11 * Jump to board specific initialization... 12 * The Mask ROM will have already initialized 13 * basic memory. Go here to bump up clock rate and handle 14 * wake up conditions. 15 */ 16 b lowlevel_init @ go setup pll,mux,memory 17 ENDPROC(cpu_init_crit)
该函数,调用了lowlevel_init,
依照惯例,我们打开arch/arm/cpu/armv7/lowlevel_init.S
1 #include <asm-offsets.h> 2 #include <config.h> 3 #include <linux/linkage.h> 4 5 ENTRY(lowlevel_init) 6 /* 7 * Setup a temporary stack 8 */ 9 ldr sp, =CONFIG_SYS_INIT_SP_ADDR 10 bic sp, sp, #7 /* 8-byte alignment for ABI compliance */ 11 #ifdef CONFIG_SPL_BUILD 12 ldr r9, =gdata 13 #else 14 sub sp, sp, #GD_SIZE 15 bic sp, sp, #7 16 mov r9, sp 17 #endif 18 /* 19 * Save the old lr(passed in ip) and the current lr to stack 20 */ 21 push {ip, lr} 22 23 /* 24 * go setup pll, mux, memory 25 */ 26 bl s_init 27 pop {ip, pc} 28 ENDPROC(lowlevel_init)
这其中无非就是指定了一个临时的栈地址,用于接下来真正的初始化中使用,即s_init,s_init是需要是针对具体芯片或者单板的初始化,因此是需要自己实现的,通常以C代码来实现,其中最重要的是初始化pll、crpm以及ddr。
该函数当然也可以自己实现,但是实际上个人觉得殊无必要,因为本来也就没几行代码。
接下来就是start.S的最后一句了
1 bl _main
看看十一点多了,总算把平安夜打发了,就此打住了