Last active
August 29, 2015 14:02
-
-
Save Embedded-linux/573f00c69f4e1d51d85e to your computer and use it in GitHub Desktop.
arm uboot code analysis[versatilePB]
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
u-boot/arch/arm/cpu/arm926ejs/start.S | |
This file initilizies | |
cpu mode, | |
interrupts, | |
SDRAM, and | |
relocate loader code to RAM and | |
continue the boot from RAM code. | |
1.Interrupt vector Table: | |
.globl _start | |
_start: | |
b reset | |
#ifdef CONFIG_PRELOADER | |
/* No exception handlers in preloader */ | |
ldr pc, _hang | |
ldr pc, _hang | |
ldr pc, _hang | |
ldr pc, _hang | |
ldr pc, _hang | |
ldr pc, _hang | |
ldr pc, _hang | |
_hang: | |
.word do_hang | |
/* pad to 64 byte boundary */ | |
.word 0x12345678 | |
.word 0x12345678 | |
.word 0x12345678 | |
.word 0x12345678 | |
.word 0x12345678 | |
.word 0x12345678 | |
.word 0x12345678 | |
-> if config_preloader is defined it will goes to aboce code excuation | |
else | |
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 | |
_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 | |
-> _start is the position where cpu fetches the first instruction, | |
it jumps to actual reset code. | |
-> Others are jump instructions for other interrupt functions. | |
->following is important addresses including TEXT_BASE, | |
_start (C code address where this Assembler code will jump to at end), | |
bss_start and bss_end. | |
_TEXT_BASE: | |
.word CONFIG_SYS_TEXT_BASE | |
.globl _bss_start_ofs | |
_bss_start_ofs: | |
.word __bss_start - _start | |
.globl _bss_end_ofs | |
_bss_end_ofs: | |
.word _end - _start | |
->bss_srart and bss_end address | |
-> _bss_start and _bss_end are defined in the board-specific linker script | |
and TEXT_BASE is defined in the board-specific config file. | |
-> Reset code sets CPU to SVC32 mode, flushes v4 I/D caches, | |
disables MMU and caches. | |
/* | |
* the actual reset code | |
*/ | |
reset: | |
/* | |
* set the cpu to SVC32 mode | |
*/ | |
mrs r0,cpsr | |
bic r0,r0,#0x1f | |
orr r0,r0,#0xd3 | |
msr cpsr,r0 | |
#ifndef CONFIG_SKIP_LOWLEVEL_INIT | |
bl cpu_init_crit | |
#endif | |
->cpu_init_crit is intilized | |
/* | |
************************************************************************* | |
* | |
* CPU_init_critical registers | |
* | |
* setup important registers | |
* setup memory timing | |
* | |
************************************************************************* | |
*/ | |
#ifndef CONFIG_SKIP_LOWLEVEL_INIT | |
cpu_init_crit: | |
/* | |
* flush v4 I/D caches | |
*/ | |
mov r0, #0 | |
mcr p15, 0, r0, c7, c7, 0 /* flush v3/v4 cache */ | |
mcr p15, 0, r0, c8, c7, 0 /* flush v4 TLB */ | |
/* | |
* disable MMU stuff and caches | |
*/ | |
mrc p15, 0, r0, c1, c0, 0 | |
bic r0, r0, #0x00002300 /* clear bits 13, 9:8 (--V- --RS) */ | |
bic r0, r0, #0x00000087 /* clear bits 7, 2:0 (B--- -CAM) */ | |
orr r0, r0, #0x00000002 /* set bit 2 (A) Align */ | |
orr r0, r0, #0x00001000 /* set bit 12 (I) I-Cache */ | |
mcr p15, 0, r0, c1, c0, 0 | |
/* | |
* Go setup Memory and board specific bits prior to relocation. | |
*/ | |
mov ip, lr /* perserve link reg across call */ | |
bl lowlevel_init /* go setup pll,mux,memory */ | |
mov lr, ip /* restore link */ | |
mov pc, lr /* back to my caller */ | |
#endif /* CONFIG_SKIP_LOWLEVEL_INIT */ | |
#ifndef CONFIG_PRELOADER | |
-> These are the last change we do some init before relocation. | |
Normally, we set the CPU Clock Speed and init the RAM here. | |
But since this board(Versatile/PB) has its own boot monitor | |
running before U-boot and init the RAM for us. | |
So we have nothing to do in the function lowlevel_init. | |
Actually, the lowlevel_init function (U-boot/board/armltd/versatile/lowlevel_init.S) | |
looks like that: | |
.globl lowlevel_init | |
lowlevel_init: | |
/* All done by Versatile's boot monitor! */ | |
mov pc, lr | |
->It does nothing but just return to the caller. | |
After this function, the cpu_init_crit function just comes to an end. | |
-> At here, all the necessary init before relocation have finished. | |
-> Relocation code from FLASH to RAM as follows | |
#ifndef CONFIG_SKIP_RELOCATE_UBOOT | |
relocate: /* relocate U-Boot to RAM */ | |
adr r0, _start /* r0 <- current position of code */ | |
ldr r1, _TEXT_BASE /* test if we run from flash or RAM */ | |
cmp r0, r1 /* don't reloc during debug */ | |
beq stack_setup | |
ldr r3, _bss_start_ofs /* r3 <- _bss_start - _start */ | |
add r2, r0, r3 /* r2 <- source end address */ | |
copy_loop: | |
ldmia r0!, {r3-r10} /* copy from source address [r0] */ | |
stmia r1!, {r3-r10} /* copy to target address [r1] */ | |
cmp r0, r2 /* until source end address [r2] */ | |
blo copy_loop | |
#endif /* CONFIG_SKIP_RELOCATE_UBOOT */ | |
->Relocating code from Flash to RAM by setting ro register to _start address | |
and setting stack in between and copy the code form BSS_START to Source end address. | |
-> Once this is done, it is copying from Source address to Destination address. | |
-> It compares the reset address and TEXT_BASE, if they are the same, | |
we are running U-boot directly in RAM so we don’t need to relocate, | |
if not, it will copy the code between _armboot_start and _bss_start to | |
TEXT_BASE which is in RAM. | |
-> Then we will set up the stack: | |
/* Set up the stack */ | |
stack_setup: | |
ldr r0, _TEXT_BASE /* upper 128 KiB: relocated uboot */ | |
sub sp, r0, #128 /* leave 32 words for abort-stack */ | |
#ifndef CONFIG_PRELOADER | |
sub r0, r0, #CONFIG_SYS_MALLOC_LEN /* malloc area */ | |
sub r0, r0, #GENERATED_GBL_DATA_SIZE /* bdinfo */ | |
#ifdef CONFIG_USE_IRQ | |
sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ) | |
#endif | |
#endif /* CONFIG_PRELOADER */ | |
sub sp, r0, #12 /* leave 3 words for abort-stack */ | |
bic sp, sp, #7 /* 8-byte alignment for ABI compliance */ | |
clear_bss: | |
adr r2, _start | |
ldr r0, _bss_start_ofs /* find start of bss segment */ | |
add r0, r0, r2 | |
ldr r1, _bss_end_ofs /* stop here */ | |
add r1, r1, r2 | |
mov r2, #0x00000000 /* clear | |
#ifndef CONFIG_PRELOADER | |
clbss_l:str r2, [r0] /* clear loop... */ | |
add r0, r0, #4 | |
cmp r0, r1 | |
blo clbss_l | |
bl coloured_LED_init | |
bl red_LED_on | |
#endif /* CONFIG_PRELOADER */ | |
-> Now, we are ready to jump the C code. | |
ldr r0, _start_armboot_ofs | |
adr r1, _start | |
add r0, r0, r1 | |
ldr pc, r0 | |
_start_armboot_ofs: | |
->start_armboot() is defined in file U-boot/arch/arm/lib/board.c. | |
It is the 2ed stage of boot. | |
->In this function, U-boot will fully init the board, | |
then start the main_loop waiting for the input from user | |
or just booting the kernel. | |
u-boot/arch/arm/lib/board.c | |
void start_armboot (void) | |
{ | |
init_fnc_t **init_fnc_ptr; | |
char *s; | |
#if defined(CONFIG_VFD) || defined(CONFIG_LCD) | |
unsigned long addr; | |
#endif | |
char i; | |
............................................. | |
............................................. | |
............................................. | |
............................................. | |
............................................. | |
............................................. | |
............................................. | |
............................................. | |
} | |
init_fnc_t *init_sequence[] = { | |
#if defined(CONFIG_ARCH_CPU_INIT) | |
arch_cpu_init, /* basic arch cpu dependent setup */ | |
#endif | |
board_init, /* basic board dependent setup */ | |
#if defined(CONFIG_USE_IRQ) | |
interrupt_init, /* set up exceptions */ | |
#endif | |
timer_init, /* initialize timer */ | |
#ifdef CONFIG_FSL_ESDHC | |
get_clocks, | |
#endif | |
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 */ | |
#if defined(CONFIG_DISPLAY_CPUINFO) | |
print_cpuinfo, /* display cpu info (and speed) */ | |
#endif | |
#if defined(CONFIG_DISPLAY_BOARDINFO) | |
checkboard, /* display board info */ | |
#endif | |
#if defined(CONFIG_HARD_I2C) || defined(CONFIG_SOFT_I2C) | |
init_func_i2c, | |
#endif | |
dram_init, /* configure available RAM banks */ | |
#if defined(CONFIG_CMD_PCI) || defined (CONFIG_PCI) | |
arm_pci_init, | |
#endif | |
display_dram_config, | |
NULL, | |
}; | |
->board_init() is in file U-boot/board/armltd/versatile/versatile.c. | |
It will set CPU clock frequency and then enable i-cache. | |
u-boot/board/armltd/versatile/versatile.c | |
int board_init (void) | |
{ | |
/* arch number of Versatile Board */ | |
gd->bd->bi_arch_number = MACH_TYPE_VERSATILE_PB; | |
/* adress of boot parameters */ | |
gd->bd->bi_boot_params = 0x00000100; | |
gd->flags = 0; | |
icache_enable (); | |
return 0; | |
} | |
-> timer_init() is in file U-boot/arch/arm/cpu/arm926ejs/versatile/timer.c. | |
It will disable the timer first then set timer to the following mode: | |
boot/arch/arm/cpu/arm926ejs/versatile/timer.c | |
int timer_init (void) | |
{ | |
ulong tmr_ctrl_val; | |
/* 1st disable the Timer */ | |
tmr_ctrl_val = *(volatile ulong *)(CONFIG_SYS_TIMERBASE + 8); | |
tmr_ctrl_val &= ~TIMER_ENABLE; | |
*(volatile ulong *)(CONFIG_SYS_TIMERBASE + 8) = tmr_ctrl_val; | |
/* | |
* The Timer Control Register has one Undefined/Shouldn't Use Bit | |
* So we should do read/modify/write Operation | |
*/ | |
/* | |
* Timer Mode : Free Running | |
* Interrupt : Disabled | |
* Prescale : 8 Stage, Clk/256 | |
* Tmr Siz : 16 Bit Counter | |
* Tmr in Wrapping Mode | |
*/ | |
tmr_ctrl_val = *(volatile ulong *)(CONFIG_SYS_TIMERBASE + 8); | |
tmr_ctrl_val &= ~(TIMER_MODE_MSK | TIMER_INT_EN | TIMER_PRS_MSK | TIMER_SIZE_MSK | TIMER_ONE_SHT ); | |
tmr_ctrl_val |= (TIMER_ENABLE | TIMER_PRS_8S); | |
*(volatile ulong *)(CONFIG_SYS_TIMERBASE + 8) = tmr_ctrl_val; | |
/* init the timestamp and lastdec value */ | |
reset_timer_masked(); | |
return 0; | |
} | |
-> CONFIG_ENV_IS_IN_FLASH to y, env_init() is in file U-boot/common/env_flash.c. | |
It saves environment variables address to gd->env_addr. | |
u-boot/common/env_flash.c | |
#ifdef CONFIG_ENV_ADDR_REDUND | |
int env_init(void) | |
{ | |
int crc1_ok = 0, crc2_ok = 0; | |
uchar flag1 = flash_addr->flags; | |
uchar flag2 = flash_addr_new->flags; | |
ulong addr_default = (ulong)&default_environment[0]; | |
ulong addr1 = (ulong)&(flash_addr->data); | |
ulong addr2 = (ulong)&(flash_addr_new->data); | |
crc1_ok = crc32(0, flash_addr->data, ENV_SIZE) == flash_addr->crc; | |
crc2_ok = | |
crc32(0, flash_addr_new->data, ENV_SIZE) == flash_addr_new->crc; | |
if (crc1_ok && !crc2_ok) { | |
gd->env_addr = addr1; | |
gd->env_valid = 1; | |
............................................ | |
............................................ | |
............................................ | |
............................................ | |
............................................ | |
} | |
->init_baudrate() is in file U-boot/arch/arm/lib/board.c. | |
it is just read the baudrate config from environment then | |
save it in gd->baudrate and gd->bd->bi_baudrate | |
u-boot/arch/arm/lib/board.c | |
static int init_baudrate (void) | |
{ | |
char tmp[64]; /* long enough for environment variables */ | |
int i = getenv_f("baudrate", tmp, sizeof (tmp)); | |
gd->bd->bi_baudrate = gd->baudrate = (i > 0) | |
? (int) simple_strtoul (tmp, NULL, 10) | |
: CONFIG_BAUDRATE; | |
return (0); | |
} | |
-> This board uses AMBA PL011 UART device, so serial_init() is in | |
file U-boot/drivers/serial/serial_pl01x.c. | |
It will init the UART device by writing proper values into | |
UART control registers. | |
u-boot/drivers/serial/serial_pl01x.c | |
#ifdef CONFIG_PL010_SERIAL | |
int serial_init (void) | |
{ | |
struct pl01x_regs *regs = pl01x_get_regs(CONSOLE_PORT); | |
unsigned int divisor; | |
/* First, disable everything */ | |
writel(0, ®s->pl010_cr); | |
/* Set baud rate */ | |
switch (baudrate) { | |
case 9600: | |
divisor = UART_PL010_BAUD_9600; | |
break; | |
case 19200: | |
divisor = UART_PL010_BAUD_9600; | |
break; | |
case 38400: | |
divisor = UART_PL010_BAUD_38400; | |
break; | |
case 57600: | |
divisor = UART_PL010_BAUD_57600; | |
break; | |
case 115200: | |
divisor = UART_PL010_BAUD_115200; | |
break; | |
default: | |
divisor = UART_PL010_BAUD_38400; | |
} | |
writel((divisor & 0xf00) >> 8, ®s->pl010_lcrm); | |
writel(divisor & 0xff, ®s->pl010_lcrl); | |
/* Set the UART to be 8 bits, 1 stop bit, no parity, fifo enabled */ | |
writel(UART_PL010_LCRH_WLEN_8 | UART_PL010_LCRH_FEN, ®s->pl010_lcrh); | |
/* Finally, enable the UART */ | |
writel(UART_PL010_CR_UARTEN, ®s->pl010_cr); | |
return 0; | |
} | |
#endif /* CONFIG_PL010_SERIAL */ | |
-> console_init_f() is in file U-boot/common/console.c | |
and its function is trival. Just set gd->have_console to 1. | |
u-boot/common/console.c | |
/* Called before relocation - use serial functions */ | |
int console_init_f(void) | |
{ | |
gd->have_console = 1; | |
#ifdef CONFIG_SILENT_CONSOLE | |
if (getenv("silent") != NULL) | |
gd->flags |= GD_FLG_SILENT; | |
#endif | |
return 0; | |
} | |
->display_banner, / * Print u-boot ---lib_arm/board.c * / | |
static int display_banner (void) | |
{ | |
printf ("\n\n%s\n\n", version_string); | |
printf ("U-Boot code: %08lX -> %08lX BSS: -> %08lX\n", | |
_armboot_start, _armboot_end_data, _armboot_end); | |
#ifdef CONFIG_MODEM_SUPPORT | |
puts ("Modem Support enabled\n"); | |
#endif | |
#ifdef CONFIG_USE_IRQ | |
printf ("IRQ Stack: %08lx\n", IRQ_STACK_START); | |
printf ("FIQ Stack: %08lx\n", FIQ_STACK_START); | |
#endif | |
return (0); | |
} | |
-> dram_init, / * configure the available RAM- borad/armltd/versatile/versatile.c * / | |
int dram_init (void) | |
{ | |
/* dram_init must store complete ramsize in gd->ram_size */ | |
gd->ram_size = get_ram_size((volatile void *)CONFIG_SYS_SDRAM_BASE, | |
PHYS_SDRAM_1_SIZE); | |
return 0; | |
} | |
-> display_dram_config / * Display the RAM configuration size ---lib_arm/board.c * / | |
static int display_dram_config (void) | |
{ | |
DECLARE_GLOBAL_DATA_PTR; | |
int i; | |
#ifdef DEBUG | |
puts ("RAM Configuration:\n"); | |
for(i=0; i<CONFIG_NR_DRAM_BANKS; i++) { | |
printf ("Bank #%d: %08lx ", i, gd->bd->bi_dram[i].start); | |
print_size (gd->bd->bi_dram[i].size, "\n"); | |
} | |
#else | |
ulong size = 0; | |
for (i=0; i<CONFIG_NR_DRAM_BANKS; i++) { | |
size += gd->bd->bi_dram[i].size; | |
} | |
puts("DRAM: "); | |
print_size(size, "\n"); | |
#endif | |
return (0); | |
} | |
-> After display_dram_config(), we finish the init sequences. | |
After that, mem_malloc_init() is called and now we can use | |
malloc to allocate memory. | |
mem_malloc_init (_armboot_start – CONFIG_SYS_MALLOC_LEN, CONFIG_SYS_MALLOC_LEN); | |
->flash_init() is called to init flash controller | |
-> stdio_init() will init all standard I/O devices the board has. | |
-> jumptable_init() will set gd->jt to a list of common function pointers. | |
-> console_init_r(), it will add console devices into global device list | |
and init output and input consoles. | |
-> Since we don’t make use of interrupts during booting, | |
so we don’t need to enable interrupts. | |
At here, we have finished the all init sequences and | |
all the things on board are ready to use. | |
So, now call the main_loop(). | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment