Skip to content

Instantly share code, notes, and snippets.

@Embedded-linux
Last active August 29, 2015 14:02
Show Gist options
  • Save Embedded-linux/573f00c69f4e1d51d85e to your computer and use it in GitHub Desktop.
Save Embedded-linux/573f00c69f4e1d51d85e to your computer and use it in GitHub Desktop.
arm uboot code analysis[versatilePB]
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, &regs->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, &regs->pl010_lcrm);
writel(divisor & 0xff, &regs->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, &regs->pl010_lcrh);
/* Finally, enable the UART */
writel(UART_PL010_CR_UARTEN, &regs->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