本节的开始之前,先看一下uboot的链接脚本。
一、链接脚本
/*
* Copyright (c) 2004-2008 Texas Instruments
*
* (C) Copyright 2002
* Gary Jennejohn, DENX Software Engineering, <garyj@denx.de>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <config.h>
#include <asm/psci.h>
OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
OUTPUT_ARCH(arm)
ENTRY(_start) /* 程序入口代码标号 */
SECTIONS
{
#ifndef CONFIG_CMDLINE
/DISCARD/ : { *(.u_boot_list_2_cmd_*) }
#endif
#if defined(CONFIG_ARMV7_SECURE_BASE) && defined(CONFIG_ARMV7_NONSEC)
/*
* If CONFIG_ARMV7_SECURE_BASE is true, secure code will not
* bundle with u-boot, and code offsets are fixed. Secure zone
* only needs to be copied from the loading address to
* CONFIG_ARMV7_SECURE_BASE, which is the linking and running
* address for secure code.
*
* If CONFIG_ARMV7_SECURE_BASE is undefined, the secure zone will
* be included in u-boot address space, and some absolute address
* were used in secure code. The absolute addresses of the secure
* code also needs to be relocated along with the accompanying u-boot
* code.
*
* So DISCARD is only for CONFIG_ARMV7_SECURE_BASE.
*/
/DISCARD/ : { *(.rel._secure*) }
#endif
. = 0x00000000;
. = ALIGN(4);
.text :
{
*(.__image_copy_start)
*(.vectors) /* 程序入口文件 */
CPUDIR/start.o (.text*)
*(.text*)
}
#ifdef CONFIG_ARMV7_NONSEC
/* Align the secure section only if we're going to use it in situ */
.__secure_start :
#ifndef CONFIG_ARMV7_SECURE_BASE
ALIGN(CONSTANT(COMMONPAGESIZE))
#endif
{
KEEP(*(.__secure_start))
}
#ifndef CONFIG_ARMV7_SECURE_BASE
#define CONFIG_ARMV7_SECURE_BASE
#define __ARMV7_PSCI_STACK_IN_RAM
#endif
.secure_text CONFIG_ARMV7_SECURE_BASE :
AT(ADDR(.__secure_start) + SIZEOF(.__secure_start))
{
*(._secure.text)
}
.secure_data : AT(LOADADDR(.secure_text) + SIZEOF(.secure_text))
{
*(._secure.data)
}
#ifdef CONFIG_ARMV7_PSCI
.secure_stack ALIGN(ADDR(.secure_data) + SIZEOF(.secure_data),
CONSTANT(COMMONPAGESIZE)) (NOLOAD) :
#ifdef __ARMV7_PSCI_STACK_IN_RAM
AT(ADDR(.secure_stack))
#else
AT(LOADADDR(.secure_data) + SIZEOF(.secure_data))
#endif
{
KEEP(*(.__secure_stack_start))
/* Skip addreses for stack */
. = . + CONFIG_ARMV7_PSCI_NR_CPUS * ARM_PSCI_STACK_SIZE;
/* Align end of stack section to page boundary */
. = ALIGN(CONSTANT(COMMONPAGESIZE));
KEEP(*(.__secure_stack_end))
#ifdef CONFIG_ARMV7_SECURE_MAX_SIZE
/*
* We are not checking (__secure_end - __secure_start) here,
* as these are the load addresses, and do not include the
* stack section. Instead, use the end of the stack section
* and the start of the text section.
*/
ASSERT((. - ADDR(.secure_text)) <= CONFIG_ARMV7_SECURE_MAX_SIZE,
"Error: secure section exceeds secure memory size");
#endif
}
#ifndef __ARMV7_PSCI_STACK_IN_RAM
/* Reset VMA but don't allocate space if we have secure SRAM */
. = LOADADDR(.secure_stack);
#endif
#endif
.__secure_end : AT(ADDR(.__secure_end)) {
*(.__secure_end)
LONG(0x1d1071c); /* Must output something to reset LMA */
}
#endif
. = ALIGN(4);
.rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) }
. = ALIGN(4);
.data : {
*(.data*)
}
. = ALIGN(4);
. = .;
. = ALIGN(4);
.u_boot_list : {
KEEP(*(SORT(.u_boot_list*)));
}
. = ALIGN(4);
.__efi_runtime_start : {
*(.__efi_runtime_start)
}
.efi_runtime : {
*(efi_runtime_text)
*(efi_runtime_data)
}
.__efi_runtime_stop : {
*(.__efi_runtime_stop)
}
.efi_runtime_rel_start :
{
*(.__efi_runtime_rel_start)
}
.efi_runtime_rel : {
*(.relefi_runtime_text)
*(.relefi_runtime_data)
}
.efi_runtime_rel_stop :
{
*(.__efi_runtime_rel_stop)
}
. = ALIGN(4);
.image_copy_end :
{
*(.__image_copy_end)
}
.rel_dyn_start :
{
*(.__rel_dyn_start)
}
.rel.dyn : {
*(.rel*)
}
.rel_dyn_end :
{
*(.__rel_dyn_end)
}
.end :
{
*(.__end)
}
_image_binary_end = .;
/*
* Deprecated: this MMU section is used by pxa at present but
* should not be used by new boards/CPUs.
*/
. = ALIGN(4096);
.mmutable : {
*(.mmutable)
}
/*
* Compiler-generated __bss_start and __bss_end, see arch/arm/lib/bss.c
* __bss_base and __bss_limit are for linker only (overlay ordering)
*/
.bss_start __rel_dyn_start (OVERLAY) : {
KEEP(*(.__bss_start));
__bss_base = .;
}
.bss __bss_base (OVERLAY) : {
*(.bss*)
. = ALIGN(4);
__bss_limit = .;
}
.bss_end __bss_limit (OVERLAY) : {
KEEP(*(.__bss_end));
}
.dynsym _image_binary_end : { *(.dynsym) }
.dynbss : { *(.dynbss) }
.dynstr : { *(.dynstr*) }
.dynamic : { *(.dynamic*) }
.plt : { *(.plt*) }
.interp : { *(.interp*) }
.gnu.hash : { *(.gnu.hash) }
.gnu : { *(.gnu*) }
.ARM.exidx : { *(.ARM.exidx*) }
.gnu.linkonce.armexidx : { *(.gnu.linkonce.armexidx.*) }
}
二.中断向量表的存放
可以看到真正的入口是_start,真正的.text段是从.vectors开始的。从字面上就能看出.vector段是中断向量表的存放处。下面u-boot源码所示是根据CPU的硬件特性所定义的中断向量表:
arch/arm/lib/vectors.S
.section ".vectors", "ax"
/*
*************************************************************************
*
* Exception vectors as described in ARM reference manuals
*
* Uses indirect branch to allow reaching handlers anywhere in memory.
*
*************************************************************************
*/
_start:
#ifdef CONFIG_SYS_DV_NOR_BOOT_CFG
.word CONFIG_SYS_DV_NOR_BOOT_CFG
#endif
b reset
ldr pc, _undefined_instruction
ldr pc, _software_interrupt
ldr pc, _prefetch_abort
ldr pc, _data_abort
ldr pc, _not_used
ldr pc, _irq
ldr pc, _fiq
#ifdef CONFIG_ENABLE_ARM_SOC_BOOT0_HOOK
/*
* Various SoCs need something special and SoC-specific up front in
* order to boot, allow them to set that in their boot0.h file and then
* use it here.
*/
#include <asm/arch/boot0.h>
ARM_SOC_BOOT0_HOOK
#endif
/*
*************************************************************************
*
* Indirect vectors table
*
* Symbols referenced here must be defined somewhere else
*
*************************************************************************
*/
.globl _undefined_instruction
.globl _software_interrupt
.globl _prefetch_abort
.globl _data_abort
.globl _not_used
.globl _irq
.globl _fiq
_undefined_instruction: .word undefined_instruction
_software_interrupt: .word software_interrupt
_prefetch_abort: .word prefetch_abort
_data_abort: .word data_abort
_not_used: .word not_used
_irq: .word irq
_fiq: .word fiq
.balignl 16,0xdeadbeef
/*
*************************************************************************
*
* Interrupt handling
*
*************************************************************************
*/
/* SPL interrupt handling: just hang */
#ifdef CONFIG_SPL_BUILD
.align 5
undefined_instruction:
software_interrupt:
prefetch_abort:
data_abort:
not_used:
irq:
fiq:
1:
bl 1b /* hang and never return */
/* 可以看到,在SPL阶段,发生异常,则直接进入上面死循环,死掉 */
#else /* !CONFIG_SPL_BUILD */
/* IRQ stack memory (calculated at run-time) + 8 bytes */
.globl IRQ_STACK_START_IN
IRQ_STACK_START_IN:
.word 0x0badc0de
#ifdef CONFIG_USE_IRQ
/* IRQ stack memory (calculated at run-time) */
.globl IRQ_STACK_START
IRQ_STACK_START:
.word 0x0badc0de
/* IRQ stack memory (calculated at run-time) */
.globl FIQ_STACK_START
FIQ_STACK_START:
.word 0x0badc0de
#endif /* CONFIG_USE_IRQ */
......
#ifdef CONFIG_USE_IRQ
.align 5
irq:
get_irq_stack
irq_save_user_regs
bl do_irq
irq_restore_user_regs
.align 5
fiq:
get_fiq_stack
/* someone ought to write a more effiction fiq_save_user_regs */
irq_save_user_regs
bl do_fiq
irq_restore_user_regs
#else
.align 5
irq:
get_bad_stack
bad_save_user_regs
bl do_irq
.align 5
fiq:
get_bad_stack
bad_save_user_regs
bl do_fiq
#endif /* CONFIG_USE_IRQ */
#endif /* CONFIG_SPL_BUILD */
三、初始代码开始
从异常向量表的reset跳转过来,
.globl reset
.globl save_boot_params_ret
#ifdef CONFIG_ARMV7_LPAE
.global switch_to_hypervisor_ret
#endif
reset:
/* Allow the board to save important registers */
b save_boot_params
save_boot_params_ret:
#ifdef CONFIG_ARMV7_LPAE /* 未定义,什么都不做 */
/*
* check for Hypervisor support
*/
mrc p15, 0, r0, c0, c1, 1 @ read ID_PFR1
and r0, r0, #CPUID_ARM_VIRT_MASK @ mask virtualization bits
cmp r0, #(1 << CPUID_ARM_VIRT_SHIFT)
beq switch_to_hypervisor
switch_to_hypervisor_ret:
#endif
/* 关中断,设置cpu处于svc32模式
* disable interrupts (FIQ and IRQ), also set the cpu to SVC32 mode,
* except if in HYP mode already
*/
mrs r0, cpsr
and r1, r0, #0x1f @ mask mode bits
teq r1, #0x1a @ test for HYP mode
bicne r0, r0, #0x1f @ clear all mode bits
orrne r0, r0, #0x13 @ set SVC mode
orr r0, r0, #0xc0 @ disable FIQ and IRQ
msr cpsr,r0
/* 我们是不是OMAP44XX系列的,所以会指行这里,在cp15的c12寄存器设置异常向量表的基地址(这里我们设置在了_start)
* Setup vector:
* (OMAP4 spl TEXT_BASE is not 32 byte aligned.
* Continue to use ROM code vector only in OMAP4 spl)
*/
#if !(defined(CONFIG_OMAP44XX) && defined(CONFIG_SPL_BUILD))
/* Set V=0 in CP15 SCTLR register - for VBAR to point to vector */
mrc p15, 0, r0, c1, c0, 0 @ Read CP15 SCTLR Register
bic r0, #CR_V @ V = 0
mcr p15, 0, r0, c1, c0, 0 @ Write CP15 SCTLR Register
/* Set vector address in CP15 VBAR register */
ldr r0, =_start
mcr p15, 0, r0, c12, c0, 0 @Set VBAR
#endif
/* the mask ROM code should have PLL and others stable */
/* 下面的两个skip的通常是不会被定义的,如果被定义,就不会初始化底层(lowlevel),定义skip是uboot被别的bootloader加载,uboot才会不再初始化底层 */
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
bl cpu_init_cp15 /* cp15相关的(页表,icache,dcache无效) */
#ifndef CONFIG_SKIP_LOWLEVEL_INIT_ONLY
bl cpu_init_crit /* lowlovel init */
#endif
#endif
bl _main
/*------------------------------------------------------------------------------*/
ENTRY(c_runtime_cpu_setup)
/*
* If I-cache is enabled invalidate it
*/
#ifndef CONFIG_SYS_ICACHE_OFF
mcr p15, 0, r0, c7, c5, 0 @ invalidate icache
mcr p15, 0, r0, c7, c10, 4 @ DSB
mcr p15, 0, r0, c7, c5, 4 @ ISB
#endif
bx lr
ENDPROC(c_runtime_cpu_setup)
/*************************************************************************
*
* void save_boot_params(u32 r0, u32 r1, u32 r2, u32 r3)
* __attribute__((weak));
*
* Stack pointer is not yet initialized at this moment
* Don't save anything to stack even if compiled with -O0
*
*************************************************************************/
ENTRY(save_boot_params)
b save_boot_params_ret @ back to my caller
ENDPROC(save_boot_params)
.weak save_boot_params
#ifdef CONFIG_ARMV7_LPAE
ENTRY(switch_to_hypervisor)
b switch_to_hypervisor_ret
ENDPROC(switch_to_hypervisor)
.weak switch_to_hypervisor
#endif
/*************************************************************************
*
* cpu_init_cp15
*
* Setup CP15 registers (cache, MMU, TLBs). The I-cache is turned on unless
* CONFIG_SYS_ICACHE_OFF is defined.
*
*************************************************************************/
ENTRY(cpu_init_cp15)
/*
* Invalidate L1 I/D,无效页表、指令缓存、数据缓存
*/
mov r0, #0 @ set up for MCR
mcr p15, 0, r0, c8, c7, 0 @ invalidate TLBs
mcr p15, 0, r0, c7, c5, 0 @ invalidate icache
mcr p15, 0, r0, c7, c5, 6 @ invalidate BP array
mcr p15, 0, r0, c7, c10, 4 @ DSB
mcr p15, 0, r0, c7, c5, 4 @ ISB
/*
* disable MMU stuff and caches关闭内存管理单元
*/
mrc p15, 0, r0, c1, c0, 0
bic r0, r0, #0x00002000 @ clear bits 13 (--V-)
bic r0, r0, #0x00000007 @ clear bits 2:0 (-CAM)
orr r0, r0, #0x00000002 @ set bit 1 (--A-) Align
orr r0, r0, #0x00000800 @ set bit 11 (Z---) BTB
#ifdef CONFIG_SYS_ICACHE_OFF
bic r0, r0, #0x00001000 @ clear bit 12 (I) I-cache
#else
orr r0, r0, #0x00001000 @ set bit 12 (I) I-cache
#endif
mcr p15, 0, r0, c1, c0, 0
#ifdef CONFIG_ARM_ERRATA_716044 /* 没定义,跳过 */
mrc p15, 0, r0, c1, c0, 0 @ read system control register
orr r0, r0, #1 << 11 @ set bit #11
mcr p15, 0, r0, c1, c0, 0 @ write system control register
#endif
#if (defined(CONFIG_ARM_ERRATA_742230) || defined(CONFIG_ARM_ERRATA_794072)) /* 没定义,跳过 */
mrc p15, 0, r0, c15, c0, 1 @ read diagnostic register
orr r0, r0, #1 << 4 @ set bit #4
mcr p15, 0, r0, c15, c0, 1 @ write diagnostic register
#endif
#ifdef CONFIG_ARM_ERRATA_743622 /* 没定义,跳过 */
mrc p15, 0, r0, c15, c0, 1 @ read diagnostic register
orr r0, r0, #1 << 6 @ set bit #6
mcr p15, 0, r0, c15, c0, 1 @ write diagnostic register
#endif
#ifdef CONFIG_ARM_ERRATA_751472 /* 没定义,跳过 */
mrc p15, 0, r0, c15, c0, 1 @ read diagnostic register
orr r0, r0, #1 << 11 @ set bit #11
mcr p15, 0, r0, c15, c0, 1 @ write diagnostic register
#endif
#ifdef CONFIG_ARM_ERRATA_761320 /* 没定义,跳过 */
mrc p15, 0, r0, c15, c0, 1 @ read diagnostic register
orr r0, r0, #1 << 21 @ set bit #21
mcr p15, 0, r0, c15, c0, 1 @ write diagnostic register
#endif
mov r5, lr @ Store my Caller /* 返回地址保存在r5 */
mrc p15, 0, r1, c0, c0, 0 @ r1 has Read Main ID Register (MIDR) /* 读芯片id,放在r1中 */
mov r3, r1, lsr #20 @ get variant field
and r3, r3, #0xf @ r3 has CPU variant
and r4, r1, #0xf @ r4 has CPU revision
mov r2, r3, lsl #4 @ shift variant field for combined value
orr r2, r4, r2 @ r2 has combined CPU variant + revision
#ifdef CONFIG_ARM_ERRATA_798870 /* 没定义,跳过 */
cmp r2, #0x30 @ Applies to lower than R3p0
bge skip_errata_798870 @ skip if not affected rev
cmp r2, #0x20 @ Applies to including and above R2p0
blt skip_errata_798870 @ skip if not affected rev
mrc p15, 1, r0, c15, c0, 0 @ read l2 aux ctrl reg
orr r0, r0, #1 << 7 @ Enable hazard-detect timeout
push {r1-r5} @ Save the cpu info registers
bl v7_arch_cp15_set_l2aux_ctrl
isb @ Recommended ISB after l2actlr update
pop {r1-r5} @ Restore the cpu info - fall through
skip_errata_798870:
#endif
#ifdef CONFIG_ARM_ERRATA_801819 /* 没定义,跳过 */
cmp r2, #0x24 @ Applies to lt including R2p4
bgt skip_errata_801819 @ skip if not affected rev
cmp r2, #0x20 @ Applies to including and above R2p0
blt skip_errata_801819 @ skip if not affected rev
mrc p15, 0, r0, c0, c0, 6 @ pick up REVIDR reg
and r0, r0, #1 << 3 @ check REVIDR[3]
cmp r0, #1 << 3
beq skip_errata_801819 @ skip erratum if REVIDR[3] is set
mrc p15, 0, r0, c1, c0, 1 @ read auxilary control register
orr r0, r0, #3 << 27 @ Disables streaming. All write-allocate
@ lines allocate in the L1 or L2 cache.
orr r0, r0, #3 << 25 @ Disables streaming. All write-allocate
@ lines allocate in the L1 cache.
push {r1-r5} @ Save the cpu info registers
bl v7_arch_cp15_set_acr
pop {r1-r5} @ Restore the cpu info - fall through
skip_errata_801819:
#endif
#ifdef CONFIG_ARM_ERRATA_454179 /* 没定义,跳过 */
cmp r2, #0x21 @ Only on < r2p1
bge skip_errata_454179
mrc p15, 0, r0, c1, c0, 1 @ Read ACR
orr r0, r0, #(0x3 << 6) @ Set DBSM(BIT7) and IBE(BIT6) bits
push {r1-r5} @ Save the cpu info registers
bl v7_arch_cp15_set_acr
pop {r1-r5} @ Restore the cpu info - fall through
skip_errata_454179:
#endif
#ifdef CONFIG_ARM_ERRATA_430973 /* 没定义,跳过 */
cmp r2, #0x21 @ Only on < r2p1
bge skip_errata_430973
mrc p15, 0, r0, c1, c0, 1 @ Read ACR
orr r0, r0, #(0x1 << 6) @ Set IBE bit
push {r1-r5} @ Save the cpu info registers
bl v7_arch_cp15_set_acr
pop {r1-r5} @ Restore the cpu info - fall through
skip_errata_430973:
#endif
#ifdef CONFIG_ARM_ERRATA_621766 /* 没定义,跳过 */
cmp r2, #0x21 @ Only on < r2p1
bge skip_errata_621766
mrc p15, 0, r0, c1, c0, 1 @ Read ACR
orr r0, r0, #(0x1 << 5) @ Set L1NEON bit
push {r1-r5} @ Save the cpu info registers
bl v7_arch_cp15_set_acr
pop {r1-r5} @ Restore the cpu info - fall through
skip_errata_621766:
#endif
mov pc, r5 @ back to my caller 前面存储在r5的返回地址,返回
ENDPROC(cpu_init_cp15)
#if !defined(CONFIG_SKIP_LOWLEVEL_INIT) &&
!defined(CONFIG_SKIP_LOWLEVEL_INIT_ONLY)
/*************************************************************************
*
* CPU_init_critical registers
*
* setup important registers
* setup memory timing
*
*************************************************************************/
ENTRY(cpu_init_crit)
/*
* Jump to board specific initialization...
* The Mask ROM will have already initialized
* basic memory. Go here to bump up clock rate and handle
* wake up conditions.
*/
b lowlevel_init @ go setup pll,mux,memory
ENDPROC(cpu_init_crit)
#endif
可以发现执行流程是这样的:
reset ---->>> save_boot_params(什么都没干,跳回reset后面) ----->>> save_boot_params_ret----->>关中断
----->设置异常向量表的基地址 ------->cpu_init_cp15(无效icache,dcache等)------->cpu_init_crit(继续调用lowlevel)
因为后面的lowlevel是要跳转到别的文件进行初始化了,所以我们在它前面增加代码先进行调试。
在start.S的最后面增加led1的调试代码
gpio_out:
ldr r11, =0xe0200240
ldr r12, =0x01111000
str r12, [r11]
ldr r11, =0xe0200244
ldr r12, =0xff
str r12, [r11]
mov pc, lr
.globl led1_on
led1_on:
ldr r11, =0xe0200244
ldr r12, [r11]
bic r12, r12,#(1<<3)
str r12, [r11]
mov pc, lr
.globl led1_off
led1_off:
ldr r11, =0xe0200244
ldr r12, [r11]
orr r12, r12,#(1<<3)
str r12, [r11]
mov pc, lr
在执行跳转代码前面让led灯如果点亮,则可以确定执行到这里的代码都正确。
bl gpio_out
bl led1_on
/* the mask ROM code should have PLL and others stable */
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
bl cpu_init_cp15
#ifndef CONFIG_SKIP_LOWLEVEL_INIT_ONLY
bl cpu_init_crit
#endif
#endif
之后make编译生成u-boot.bin文件
烧写,测试
因为s5pv210通过sd卡启动,需要内部的irom代码校验的。所以我们先编写校验代码。
校验算法applacation note也给出了,BL1的大小常见的为8k或16k。
我这边为了方便,做成16k的BL1,代码如下
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BL1_SIZE (16*1024)
#define BL_HEADER_INFO " "
#define BL_HEADER_SIZE 16
int main(int argc,char *argv[])
{
FILE *fp = NULL;
int file_len = 0;
unsigned char *buff = NULL,data = 0; /* 这里要定义成unsigned 否则,否则data加出来的会有负数,导致校验和错误,这里用三星官方给的有点小坑 */
int buf_len = 0;
unsigned int checksum = 0,count;
int i = 0;
unsigned int nbytes = 0;
/* 输入三个参数,分别是自己可执行文件,uboot.bin,输出的16k文件 */
if(3 != argc)
{
printf("argc paramenter number is %d,not 3
",argc);
printf("%s <source file> <destination file>
",argv[0]);
return -1;
}
buff = (unsigned char *)malloc(BL1_SIZE);
if(NULL == buff)
{
printf("malloc error
");
return -1;
}
/* clear the buff memary */
memset(buff, 0, BL1_SIZE);
fp = fopen(argv[1],"rb");
if(NULL == fp)
{
printf("open source file error
");
return -1;
}
/* 定位到文件尾 */
fseek(fp,0,SEEK_END);
/* 得到文件长度 */
file_len = ftell(fp);
/* 定位到文件头 */
fseek(fp,0,SEEK_SET);
/* get write BL1 size */
count = (file_len < (BL1_SIZE - BL_HEADER_SIZE)) ? file_len : (BL1_SIZE - BL_HEADER_SIZE);
/* write BL1 header info */
memcpy(buff, BL_HEADER_INFO, BL_HEADER_SIZE);
/* read fp file count size of buff +... */
nbytes = fread(buff + BL_HEADER_SIZE, 1, count , fp);
if(count != nbytes)
{
printf("fread %s faile
",argv[1]);
free(buff);
fclose(fp);
return -1;
}
fclose(fp);
fp = NULL;
/* calculate checksum */
for(i = 0; i < count; i++ )
{
data = *(volatile unsigned char *)(buff + BL_HEADER_SIZE + i);
checksum += data;
}
*(volatile unsigned int *)buff = BL1_SIZE;
*(volatile unsigned int *)(buff + 8) = checksum;
fp = fopen(argv[2],"wb");
if(NULL == fp)
{
printf("open aim file error
");
return -1;
}
nbytes = fwrite(buff, 1, BL1_SIZE, fp);
if(BL1_SIZE != nbytes)
{
printf("write aim file faile
");
free(buff);
fclose(fp);
return -1;
}
fclose(fp);
free(buff);
return 0;
}
生成16k制作工具
gcc s5pv210_image.c -o make-16k
生成16k烧写文件
./make-16k u-boot.bin u-boot-16k.bin
烧写sd卡
sudo dd iflag=dsync oflag=dsync if=./u-boot-16k.bin of=/dev/sdb seek=1
发现led1确实被点亮了,说明到目前为止的程序运行正确。
四、lowlevel_init
lowlevel_init一般是由板级代码自己实现的.但是对于某些平台来说,也可以使用通用的lowlevel_init,其定义在arch/arm/cpu/lowlevel_init.S中。以goni为例,在移植goni的过程中,就需要在board/samsung/goni下,也就是板级目录下面创建lowlevel_init.S,在内部实现lowlevel_init。(其实只要实现了lowlevel_init了就好,没必要说在哪里是实现,但是通常规范都是创建了lowlevel_init.S来专门实现lowlevel_init函数)
在查看lowlevel_init函数之前,先找到代码在哪里,搜了一下发现除去和我们不相关的两个SOC外还是有两个定义的地方。没办法,只有查看Makefile
先看armv7下面的,可以知道这些系列的CPU我们都没定义,所以 它们是等于 NULL的,即不会包含lowlevel_init.o
ifneq ($(CONFIG_AM43XX)$(CONFIG_AM33XX)$(CONFIG_OMAP44XX)$(CONFIG_OMAP54XX)$(CONFIG_TEGRA)$(CONFIG_MX6)$(CONFIG_MX7)$(CONFIG_TI81XX)$(CONFIG_AT91FAMILY)$(CONFIG_ARCH_SUNXI)$(CONFIG_ARCH_SOCFPGA)$(CONFIG_LS102XA),)
ifneq ($(CONFIG_SKIP_LOWLEVEL_INIT),y)
obj-y += lowlevel_init.o
endif
endif
接下来看goni下面的,直接包含了,没有任何条件(即:只要是goni的板子,必然包含它下面的lowlevel.o)
obj-y := goni.o onenand.o
obj-y += lowlevel_init.o
在lowlevel_init中,要实现如下:
* 检查一些复位状态
* 关闭看门狗
* 系统时钟的初始化
* 内存、DDR的初始化
* 串口初始化(可选)
下面要用到读pro_id来区分产品类型,我们s5pv210的如下图
可以看一下lowlevel_init的代码
#include <config.h>
#include <asm/arch/cpu.h>
#include <asm/arch/clock.h>
#include <asm/arch/power.h>
/*
* Register usages:
*
* r5 has zero always (总是0,下面会用到)
* r7 has S5PC100 GPIO base, 0xE0300000(s5pc100的gpio基址是0xe0300000,s5pc110和s5pv210的是0xe0200000,下一句注释也说明了用r8来放gpio的base)
* r8 has real GPIO base, 0xE0300000, 0xE0200000 at S5PC100, S5PC110 repectively
* r9 has Mobile DDR size, 1 means 1GiB, 2 means 2GiB and so on
*/
.globl lowlevel_init
lowlevel_init:
mov r11, lr /* 里面要调用函数,会覆盖lr,这里先保存 */
/* r5 has always zero */
mov r5, #0
ldr r7, =S5PC100_GPIO_BASE /* 在mach下s5pc1xx的cpu.h中定义 */
ldr r8, =S5PC100_GPIO_BASE
/* Read CPU ID */
ldr r2, =S5PC110_PRO_ID
ldr r0, [r2]
mov r1, #0x00010000
and r0, r0, r1
cmp r0, r5
beq 100f
ldr r8, =S5PC110_GPIO_BASE
/* 可以看到上面几句的意思是如果pro_id的第16bit如果是1,则r8等于s5pc110的基址,即s5pv210的基址 */
100:
/* Turn on KEY_LED_ON [GPJ4(1)] XMSMWEN */
cmp r7, r8
beq skip_check_didle @ Support C110 only 很明显我们不相等
/* 下面几行就是检查复位状态,钥匙从睡眠状态唤醒的,会跳过一些初始化 */
ldr r0, =S5PC110_RST_STAT
ldr r1, [r0]
and r1, r1, #0x000D0000
cmp r1, #(0x1 << 19) @ DEEPIDLE_WAKEUP
beq didle_wakeup /* 我们这里不是睡眠唤醒 */
cmp r7, r8
skip_check_didle:
addeq r0, r8, #0x280 @ S5PC100_GPIO_J4
addne r0, r8, #0x2C0 @ S5PC110_GPIO_J4 r7 != r8 我们执行这里
/* 配置gpj4(1) */
ldr r1, [r0, #0x0] @ GPIO_CON_OFFSET
bic r1, r1, #(0xf << 4) @ 1 * 4-bit
orr r1, r1, #(0x1 << 4)
str r1, [r0, #0x0] @ GPIO_CON_OFFSET
/* 点亮
ldr r1, [r0, #0x4] @ GPIO_DAT_OFFSET
bic r1, r1, #(1 << 1)
str r1, [r0, #0x4] @ GPIO_DAT_OFFSET
/* Don't setup at s5pc100 */
beq 100f /* 上一次cmp的结果,我们不相等 */
/*
* Initialize Async Register Setting for EVT1
* Because we are setting EVT1 as the default value of EVT0,
* setting EVT0 as well does not make things worse.
* Thus, for the simplicity, we set for EVT0, too
*
* The "Async Registers" are:
* 0xE0F0_0000
* 0xE1F0_0000
* 0xF180_0000
* 0xF190_0000
* 0xF1A0_0000
* 0xF1B0_0000
* 0xF1C0_0000
* 0xF1D0_0000
* 0xF1E0_0000
* 0xF1F0_0000
* 0xFAF0_0000
*/
ldr r0, =0xe0f00000
ldr r1, [r0]
bic r1, r1, #0x1
str r1, [r0]
ldr r0, =0xe1f00000
ldr r1, [r0]
bic r1, r1, #0x1
str r1, [r0]
ldr r0, =0xf1800000
ldr r1, [r0]
bic r1, r1, #0x1
str r1, [r0]
ldr r0, =0xf1900000
ldr r1, [r0]
bic r1, r1, #0x1
str r1, [r0]
ldr r0, =0xf1a00000
ldr r1, [r0]
bic r1, r1, #0x1
str r1, [r0]
ldr r0, =0xf1b00000
ldr r1, [r0]
bic r1, r1, #0x1
str r1, [r0]
ldr r0, =0xf1c00000
ldr r1, [r0]
bic r1, r1, #0x1
str r1, [r0]
ldr r0, =0xf1d00000
ldr r1, [r0]
bic r1, r1, #0x1
str r1, [r0]
ldr r0, =0xf1e00000
ldr r1, [r0]
bic r1, r1, #0x1
str r1, [r0]
ldr r0, =0xf1f00000
ldr r1, [r0]
bic r1, r1, #0x1
str r1, [r0]
ldr r0, =0xfaf00000
ldr r1, [r0]
bic r1, r1, #0x1
str r1, [r0]
/*
* Diable ABB block to reduce sleep current at low temperature
* Note that it's hidden register setup don't modify it 隐藏寄存器,不管
*/
ldr r0, =0xE010C300
ldr r1, =0x00800000
str r1, [r0]
100:
/* IO retension release 没什么作用 */
ldreq r0, =S5PC100_OTHERS @ 0xE0108200
ldrne r0, =S5PC110_OTHERS @ 0xE010E000
ldr r1, [r0]
ldreq r2, =(1 << 31) @ IO_RET_REL
ldrne r2, =((1 << 31) | (1 << 30) | (1 << 29) | (1 << 28))
orr r1, r1, r2
/* Do not release retention here for S5PC110 */
streq r1, [r0]
/* Disable Watchdog ,关看门狗 */
ldreq r0, =S5PC100_WATCHDOG_BASE @ 0xEA200000
ldrne r0, =S5PC110_WATCHDOG_BASE @ 0xE2700000
str r5, [r0]
/* setting SRAM,设置bank1的总线宽度 */
ldreq r0, =S5PC100_SROMC_BASE
ldrne r0, =S5PC110_SROMC_BASE
ldr r1, =0x9
str r1, [r0]
/* S5PC100 has 3 groups of interrupt sources,我们s5pv210有四组interrupt的 */
ldreq r0, =S5PC100_VIC0_BASE @ 0xE4000000
ldrne r0, =S5PC110_VIC0_BASE @ 0xF2000000
add r1, r0, #0x00100000
add r2, r0, #0x00200000
/* Disable all interrupts (VIC0, VIC1 and VIC2) */
mvn r3, #0x0
str r3, [r0, #0x14] @ INTENCLEAR
str r3, [r1, #0x14] @ INTENCLEAR
str r3, [r2, #0x14] @ INTENCLEAR
/* Set all interrupts as IRQ */
str r5, [r0, #0xc] @ INTSELECT
str r5, [r1, #0xc] @ INTSELECT
str r5, [r2, #0xc] @ INTSELECT
/* Pending Interrupt Clear */
str r5, [r0, #0xf00] @ INTADDRESS
str r5, [r1, #0xf00] @ INTADDRESS
str r5, [r2, #0xf00] @ INTADDRESS
/* for UART ,初始化串口*/
bl uart_asm_init
/* */
bl internal_ram_init
cmp r7, r8
/* Clear wakeup status register ,清除睡眠*/
ldreq r0, =S5PC100_WAKEUP_STAT
ldrne r0, =S5PC110_WAKEUP_STAT
ldr r1, [r0]
str r1, [r0]
/* IO retension release */
ldreq r0, =S5PC100_OTHERS @ 0xE0108200
ldrne r0, =S5PC110_OTHERS @ 0xE010E000
ldr r1, [r0]
ldreq r2, =(1 << 31) @ IO_RET_REL
ldrne r2, =((1 << 31) | (1 << 30) | (1 << 29) | (1 << 28))
orr r1, r1, r2
str r1, [r0]
b 1f
didle_wakeup:
/* Wait when APLL is locked */
ldr r0, =0xE0100100 @ S5PC110_APLL_CON
lockloop:
ldr r1, [r0]
and r1, r1, #(1 << 29)
cmp r1, #(1 << 29)
bne lockloop
ldr r0, =S5PC110_INFORM0
ldr r1, [r0]
mov pc, r1
nop
nop
nop
nop
nop
1:
mov lr, r11 /* 恢复最前面保存的lr寄存器 */
mov pc, lr
/*
* system_clock_init: Initialize core clock and bus clock.
* void system_clock_init(void) 没有用到
*/
system_clock_init:
ldr r0, =S5PC110_CLOCK_BASE @ 0xE0100000
/* Check S5PC100 */
cmp r7, r8
bne 110f
100:
/* Set Lock Time */
ldr r1, =0xe10 @ Locktime : 0xe10 = 3600
str r1, [r0, #0x000] @ S5PC100_APLL_LOCK
str r1, [r0, #0x004] @ S5PC100_MPLL_LOCK
str r1, [r0, #0x008] @ S5PC100_EPLL_LOCK
str r1, [r0, #0x00C] @ S5PC100_HPLL_LOCK
/* S5P_APLL_CON */
ldr r1, =0x81bc0400 @ SDIV 0, PDIV 4, MDIV 444 (1333MHz)
str r1, [r0, #0x100]
/* S5P_MPLL_CON */
ldr r1, =0x80590201 @ SDIV 1, PDIV 2, MDIV 89 (267MHz)
str r1, [r0, #0x104]
/* S5P_EPLL_CON */
ldr r1, =0x80870303 @ SDIV 3, PDIV 3, MDIV 135 (67.5MHz)
str r1, [r0, #0x108]
/* S5P_HPLL_CON */
ldr r1, =0x80600603 @ SDIV 3, PDIV 6, MDIV 96
str r1, [r0, #0x10C]
ldr r1, [r0, #0x300]
ldr r2, =0x00003fff
bic r1, r1, r2
ldr r2, =0x00011301
orr r1, r1, r2
str r1, [r0, #0x300]
ldr r1, [r0, #0x304]
ldr r2, =0x00011110
orr r1, r1, r2
str r1, [r0, #0x304]
ldr r1, =0x00000001
str r1, [r0, #0x308]
/* Set Source Clock */
ldr r1, =0x00001111 @ A, M, E, HPLL Muxing
str r1, [r0, #0x200] @ S5PC1XX_CLK_SRC0
b 200f
110:
ldr r0, =0xE010C000 @ S5PC110_PWR_CFG
/* Set OSC_FREQ value */
ldr r1, =0xf
str r1, [r0, #0x100] @ S5PC110_OSC_FREQ
/* Set MTC_STABLE value */
ldr r1, =0xffffffff
str r1, [r0, #0x110] @ S5PC110_MTC_STABLE
/* Set CLAMP_STABLE value */
ldr r1, =0x3ff03ff
str r1, [r0, #0x114] @ S5PC110_CLAMP_STABLE
ldr r0, =S5PC110_CLOCK_BASE @ 0xE0100000
/* Set Clock divider */
ldr r1, =0x14131330 @ 1:1:4:4, 1:4:5
str r1, [r0, #0x300]
ldr r1, =0x11110111 @ UART[3210]: MMC[3210]
str r1, [r0, #0x310]
/* Set Lock Time */
ldr r1, =0x2cf @ Locktime : 30us
str r1, [r0, #0x000] @ S5PC110_APLL_LOCK
ldr r1, =0xe10 @ Locktime : 0xe10 = 3600
str r1, [r0, #0x008] @ S5PC110_MPLL_LOCK
str r1, [r0, #0x010] @ S5PC110_EPLL_LOCK
str r1, [r0, #0x020] @ S5PC110_VPLL_LOCK
/* S5PC110_APLL_CON */
ldr r1, =0x80C80601 @ 800MHz
str r1, [r0, #0x100]
/* S5PC110_MPLL_CON */
ldr r1, =0x829B0C01 @ 667MHz
str r1, [r0, #0x108]
/* S5PC110_EPLL_CON */
ldr r1, =0x80600602 @ 96MHz VSEL 0 P 6 M 96 S 2
str r1, [r0, #0x110]
/* S5PC110_VPLL_CON */
ldr r1, =0x806C0603 @ 54MHz
str r1, [r0, #0x120]
/* Set Source Clock */
ldr r1, =0x10001111 @ A, M, E, VPLL Muxing
str r1, [r0, #0x200] @ S5PC1XX_CLK_SRC0
/* OneDRAM(DMC0) clock setting */
ldr r1, =0x01000000 @ ONEDRAM_SEL[25:24] 1 SCLKMPLL
str r1, [r0, #0x218] @ S5PC110_CLK_SRC6
ldr r1, =0x30000000 @ ONEDRAM_RATIO[31:28] 3 + 1
str r1, [r0, #0x318] @ S5PC110_CLK_DIV6
/* XCLKOUT = XUSBXTI 24MHz */
add r2, r0, #0xE000 @ S5PC110_OTHERS
ldr r1, [r2]
orr r1, r1, #(0x3 << 8) @ CLKOUT[9:8] 3 XUSBXTI
str r1, [r2]
/* CLK_IP0 */
ldr r1, =0x8fefeeb @ DMC[1:0] PDMA0[3] IMEM[5]
str r1, [r0, #0x460] @ S5PC110_CLK_IP0
/* CLK_IP1 */
ldr r1, =0xe9fdf0f9 @ FIMD[0] USBOTG[16]
@ NANDXL[24]
str r1, [r0, #0x464] @ S5PC110_CLK_IP1
/* CLK_IP2 */
ldr r1, =0xf75f7fc @ CORESIGHT[8] MODEM[9]
@ HOSTIF[10] HSMMC0[16]
@ HSMMC2[18] VIC[27:24]
str r1, [r0, #0x468] @ S5PC110_CLK_IP2
/* CLK_IP3 */
ldr r1, =0x8eff038c @ I2C[8:6]
@ SYSTIMER[16] UART0[17]
@ UART1[18] UART2[19]
@ UART3[20] WDT[22]
@ PWM[23] GPIO[26] SYSCON[27]
str r1, [r0, #0x46c] @ S5PC110_CLK_IP3
/* CLK_IP4 */
ldr r1, =0xfffffff1 @ CHIP_ID[0] TZPC[8:5]
str r1, [r0, #0x470] @ S5PC110_CLK_IP3
200:
/* wait at least 200us to stablize all clock */
mov r2, #0x10000
1: subs r2, r2, #1
bne 1b
mov pc, lr
internal_ram_init:
ldreq r0, =0xE3800000
ldrne r0, =0xF1500000
ldr r1, =0x0
str r1, [r0]
mov pc, lr
/*
* uart_asm_init: Initialize UART's pins
*/
uart_asm_init:
/* set GPIO to enable UART0-UART4,只初始化了uart的引脚,没初始化uart寄存器 */
mov r0, r8
ldr r1, =0x22222222
str r1, [r0, #0x0] @ S5PC100_GPIO_A0_OFFSET
ldr r1, =0x00002222
str r1, [r0, #0x20] @ S5PC100_GPIO_A1_OFFSET
/* Check S5PC100 */
cmp r7, r8
bne 110f
/* UART_SEL GPK0[5] at S5PC100 */
add r0, r8, #0x2A0 @ S5PC100_GPIO_K0_OFFSET
ldr r1, [r0, #0x0] @ S5PC1XX_GPIO_CON_OFFSET
bic r1, r1, #(0xf << 20) @ 20 = 5 * 4-bit
orr r1, r1, #(0x1 << 20) @ Output
str r1, [r0, #0x0] @ S5PC1XX_GPIO_CON_OFFSET
ldr r1, [r0, #0x8] @ S5PC1XX_GPIO_PULL_OFFSET
bic r1, r1, #(0x3 << 10) @ 10 = 5 * 2-bit
orr r1, r1, #(0x2 << 10) @ Pull-up enabled
str r1, [r0, #0x8] @ S5PC1XX_GPIO_PULL_OFFSET
ldr r1, [r0, #0x4] @ S5PC1XX_GPIO_DAT_OFFSET
orr r1, r1, #(1 << 5) @ 5 = 5 * 1-bit
str r1, [r0, #0x4] @ S5PC1XX_GPIO_DAT_OFFSET
b 200f
110:
/*
* Note that the following address
* 0xE020'0360 is reserved address at S5PC100
*/
/* UART_SEL MP0_5[7] at S5PC110 */
add r0, r8, #0x360 @ S5PC110_GPIO_MP0_5_OFFSET
ldr r1, [r0, #0x0] @ S5PC1XX_GPIO_CON_OFFSET
bic r1, r1, #(0xf << 28) @ 28 = 7 * 4-bit
orr r1, r1, #(0x1 << 28) @ Output
str r1, [r0, #0x0] @ S5PC1XX_GPIO_CON_OFFSET
ldr r1, [r0, #0x8] @ S5PC1XX_GPIO_PULL_OFFSET
bic r1, r1, #(0x3 << 14) @ 14 = 7 * 2-bit
orr r1, r1, #(0x2 << 14) @ Pull-up enabled
str r1, [r0, #0x8] @ S5PC1XX_GPIO_PULL_OFFSET
ldr r1, [r0, #0x4] @ S5PC1XX_GPIO_DAT_OFFSET
orr r1, r1, #(1 << 7) @ 7 = 7 * 1-bit
str r1, [r0, #0x4] @ S5PC1XX_GPIO_DAT_OFFSET
200:
mov pc, lr
在goni.h头文件定义了,text段的基地址0x34800000
在最终生成的u-boot.map表中查看lowlevel的地址,发现在16k以内,所以不用改这里。
在uart2的初始化的末尾添加代码,在串口发送一个字符'O'
ldr r1, =0xe2900820
ldr r2, =0x4f
str r2, [r1]
发现串口2可以打印出'O'字符
在start.S的 调用_main之前调用,下面代码,发现不能正确打印'K'
ldr r1, =0xe2900820
ldr r2, =0x4b
str r2, [r1]
下一节开始查找问题原因