This is such a busy week. At the end of last week, I have just ordered my OpenRD-Ultimate box but sadly it will be delivered at the end of June. So if I just wait for that box, I will not be able to test my code. That’s terrible! After talking with my mentor, I decided first porting coreboot to RealView Versatile/PB926EJ-S board then to OpenRD-Ultimate. Since qemu can emulate PB926EJ-S, I can test my code on it quickly and freely. After this work, the basic layout, libs and headers for ARM are ready to use. So I can start to port coreboot to OpenRD-Ultimate then.
In this week, I am studying the code of U-boot for board VersatilePB. You can get the source code I am using at here. It is not the newest release but the newer ones have bugs for this board which make the building fail.
Versatile/PB926EJ-S uses the ARM926EJ-S™ cpu core. So the boot code for it is at U-boot/arch/arm/cpu/arm926ejs/start.S. It will set the CPU mode, initialize the interrupts and SDRAM, then relocate the loader code, at last, I will jump to the code in ram to continue the boot.
First, let’s look at the interrupt vector table. It is at the beginning of start.S.
_start:
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
_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.
Then following is some important addresses including TEXT_BASE, _start (C code address where this Assembler code will jump to at end), bss_start and bss_end.
.word TEXT_BASE
.globl _armboot_start
_armboot_start:
.word _start
.globl _bss_start
_bss_start:
.word __bss_start
.globl _bss_end
_bss_end:
.word _end
_bss_start and _bss_end are defined in the board-specific linker script and TEXT_BASE is defined in the board-specific config file.
Then is the actual reset code. It sets CPU to SVC32 mode, flushes v4 I/D caches, disables MMU and caches.
mrs r0,cpsr
bic r0,r0,#0x1f
orr r0,r0,#0xd3
msr cpsr,r0
bl cpu_init_crit
cpu_init_crit:
mov r0, #0
mcr p15, 0, r0, c7, c7, 0 /* flush v3/v4 cache */
mcr p15, 0, r0, c8, c7, 0 /* flush v4 TLB */
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
following, control passes to board-specific lowlevel_init function using following code.
bl lowlevel_init /* go setup pll,mux,memory */
mov lr, ip /* restore link */
mov pc, lr /* back to my caller */
This is 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:
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 follows:
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 r2, _armboot_start
ldr r3, _bss_start
sub r2, r3, r2 /* r2 <– size of armboot */
add r2, r0, r2 /* 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 addreee [r2] */
ble copy_loop
First, 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:
ldr r0, _TEXT_BASE /* upper 128 KiB: relocated uboot */
sub sp, r0, #128 /* leave 32 words for abort-stack */
sub r0, r0, #CONFIG_SYS_MALLOC_LEN /* malloc area */
sub r0, r0, #CONFIG_SYS_GBL_DATA_SIZE /* bdinfo */
sub sp, r0, #12 /* leave 3 words for abort-stack */
bic sp, sp, #7 /* 8-byte alignment for ABI compliance */
clear_bss:
ldr r0, _bss_start /* find start of bss segment */
ldr r1, _bss_end /* stop here */
mov r2, #0x00000000 /* clear */
clbss_l:str r2, [r0] /* clear loop… */
add r0, r0, #4
cmp r0, r1
ble clbss_l
OK. Now, we are ready to jump the C code.
_start_armboot:
.word start_armboot
start_armboot() is defined in file U-boot/arm/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.
Now, let’s move to the init_sequence function list. All the functions in this list will be executed one after another in function start_armboot().
board_init, /* basic board dependent setup */
timer_init, /* initialize timer */
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,
NULL,
};
First, board_init() is in file U-boot/board/armltd/versatile/versatile.c. It will set CPU clock frequency and then enable i-cache.
Then, 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.
* Timer Mode : Free Running
* Interrupt : Disabled
* Prescale : 8 Stage, Clk/256
* Tmr Siz : 16 Bit Counter
* Tmr in Wrapping Mode
*/
Since we have set 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.
Following is init_baudrate(). It is in file U-boot/arch/arm/lib/board.c. And it is just read the baudrate config from environment then save it in gd->baudrate and gd->bd->bi_baudrate.
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.
console_init_f() is in file U-boot/common/console.c and its function is trival. Just set gd->have_console to 1.
Then call display_banner() to show that we have already done something.
As saying before, this board using boot monitor to init ram, so dram_init() (in file U-boot/board/armltd/versatile/versatile.c) does noting but return.
Wo…..After display_dram_config(), we finish the init sequences.
Wait! We don’t finish the whole init process.
After that, mem_malloc_init() is called and now we can use malloc to allocate memory.
Then 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.
Then console_init_r(), it will add console devices into global device list and init output and input consoles.
Great! We have done so mush now. 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, why not call the main_loop()?
Hi Hamo,
Working with the emulation is a good idea until you can get the hardware. Looks like you made good progress the first week.
Marc
Hi posting is having much information. Thanks for the post. I would like to know some more information.
In this posting, at what line the code copied from the FLASH to SDRAM?
Also “But since this board(Versatile/PB) has its own boot monitor running before U-boot and init the RAM for us”,
if a bootmonitor is running before uboot, how/where it is started in the uboot code?