Graperain Blog ARM Embedded News, Announcements, and Ideas

Embedded Linux Boot Process

First, plan image when porting linux , such as 64m SDRAM,

Address: 0x 0800 0000 -0x0bff ffff,32m flash, address: 0x0c00 0000-0x0dff ffff. 

Plan bootloader, linux kernel, rootdisk into flash.

Details goes that, starts from 0x0c00 0000 first 1M, put bootloader into itand starts 0x0c10 0000 2M with linux kernel, and starts the rest 0x0c30 0000 for rootdisk.

done boot,  arm920T will map 0x0c00 0000 to 0 (which could be setted by  jumper),

Factly boots from 0x0c00 0000, enter into bootloader, but as flash speed slow, so there is a small program before bootloader, copys bootloader into 0x0AFE0100 of SDRAM.

And then run bootloader from 0x 0800 0000, which called flashloader. And this flashloader have to be initialized SDRAM, or no space.

.equ SOURCE, 0x0C000
100 memory address of bootloader 
.equ TARGET, 0x0AFE0
100 as target address 
.equ SDCTL0, 0x22
1000 SDRAM controller register 
// size is stored in location 0x0C0000FC 

.global _start 
_start: //access point

//;* Init SDRAM 

// * 
// * SDRAM 
// * 

1, =SDCTL0 // 

//  Set Precharge Command 
LDR r3, =0x92
//ldr r3,=0x92
STR r3, [r

//  Issue Precharge All Commad 
LDR r3, =0x8200000 
LDR r2, [r3] 

//  Set AutoRefresh Command 
LDR r3, =0xA2
STR r3, [r

//  Issue AutoRefresh Command 
LDR r3, =0x8000000 
LDR r2, [r3] 
LDR r2, [r3] 
LDR r2, [r3] 
LDR r2, [r3] 
LDR r2, [r3] 
LDR r2, [r3] 
LDR r2, [r3] 
LDR r2, [r3] 

//  Set Mode Register 
LDR r3, =0xB2
STR r3, [r

//  Issue Mode Register Command 
LDR r3, =0x08
111800 //; Mode Register value 
LDR r2, [r3] 

//  Set Normal Mode 
LDR r3, =0x82
STR r3, [r

//;* End of SDRAM and SyncFlash Init * 

// copy code from FLASH to SRAM 

ldr r0,=SOURCE 
ldr r
sub r3,r0,#4 
ldr r2,[r3] 

ldr r3,[r0] 
str r3,[r
add r0,r0,#4 
add r
sub r2,r2,#4 
teq r2,#0 
beq _EndCopy 
b _CopyLoop 

ldr r0,=TARGET 
mov pc,r0 


Last time, no explanation of flashloader  how to put bootloader load to 0x0AFE0100. Factly, 0x0AFE0100 is the real bootloader which programmed into flash 0x0C000100:


Bootloader includes few files, such as START.s which is the only assmble program, and the others are C programming.

ldr r
ldr sp,[r
b main 
//Here jump into main function of  C code, and after done C, it will be called.
// Jump following JumpToKernel0x to LINXU kernel, and run.
.equ StackInitvalue, __end_data+0x
1000 // 4K __end_data, which assigned in linker script

.long StackInitvalue 

.global JumpToKernel 

// jump to the copy code (get the arguments right) 
mov pc, r0 

.global JumpToKernel0x 
// r0 = jump address 
// r
1-r4 = arguments to use (these get shifted) 
// jump to the copy code (get the arguments right) 
mov r8, r0 
mov r0, r
mov r
1, r2 
mov r2, r3 
mov r3, r4 
mov pc, r8 
.section ".data.boot" 
.section ".bss.boot" 

Following shows c code functions of bootloader, main functions is a little longer, please check it in sections.

int main() 

U32 *pSource, *pDestin, count; 
U8 countDown, bootOption; 
U32 delayCount; 
U32 fileSize, i; 
char c; 
char *pCmdLine; 
char *pMem; 

init(); // Initialize FLASH controller and CPU clock

EUARTinit(); // initialize serial port
1 Linux Bootloader ver 0.2.0/n"); 
EUARTputString("Copyright (C) 2002 Motorola Ltd./n/n"); 
EUARTputString((U8 *)cmdLine); 

EUARTputString("Press any key for alternate boot-up options ... "); 


Bootloarder works on init();  and initialize hardware, and printing information and system optionals as following:

0. Program bootloader image 
1. Program kernel image 
2. Program root-disk image 
3. Download kernel and boot from RAM 
4. Download kernel and boot with ver 0.
1.x bootloader format 
5. Boot a ver0.
1.x kernel 
6. Boot with a different command line 

In other words, you can choose to re-download the bootloader kernel, rootdisk and write flash,

Download method is usb connection, 10m of the rootdisk also brush about. On the usb download the discussion please see the previous post "for the arm development platform to increase usb download interface." If not selected, a direct return to the Linux kernel began to copy the SDRAM operation.


Tell me what may be asked to flashloader is not already initialized sdram controller? How Init (); Also initialization, you do not know, the younger brother is the syncflash,

You can directly use the sdram controller interface, remember: the code in the flash is not initialized to connect the flash sdram controller, or absolutely dead. So, when the program is running in flash, to initialize sdram, and now run in sdram can be assured to boldly initialize the flash, the main word width is set, the ranks of delay, because the default is the largest.


In addition, if the official position of the cpu there are enough on-chip ram, you can put the bootloader on-chip ram, finished all jump to LINUX, the younger brother is also a last resort.

If input Enter, it will enter kernel  and copy following information:

EUARTputString("Copying kernel from Flash to RAM .../n"); 
count = 0x200000; // 2 Mbytes 
pSource = (U32 *)0x0C
pDestin = (U32 *)0x08008000; 

*(pDestin++) = *(pSource++); 
count -= 4; 
} while (count > 0); 

EUARTputString("Booting kernel .../n/n"); 


Done running kernel in 0x08008000, blank 0x8000 for saving kelnel global data structure, such as are arm which more than 16K.

As known, parameters could be introduced when linux kernel boots. If PC, and takes LILO, it will be LILO:, input root=/dev/hda1. Or mem=128M or other specify files device  to done bootloader parameters introduced in embedded system.

pMem = (char *)0x083FF000; // target store address for parameters character string
pCmdLine = (char *)&cmdLine; //define the static string
while ((*(pMem++)=*(pCmdLine++)) != 0);//copy

JumpToKernel((void *)0x8008000, 0x083FF000) //jump to kernel
return (0); 
JumpToKernel defined start.S in preamble

// jump to the copy code (get the arguments right) 
mov pc, r0 

.global JumpToKernel0x 
// r0 = jump address 
// r
1 = arguments to use (these get shifted) 

As C parameters of arm-GCC called from left to right, starts R0, so R0 is the address of KERNKEL,

R1 is address of parameters character string

Till now, all preparatory work done for Linux boots, and next will be enter into linux code

Linux entrance is a small assembly code, which used to basic hardware setting and set temporary page table.

ARM LINUX is linux/arch/arm/kernle/head-armv.S, 走! 

#if defined(CONFIG_MX
mov r

1Yes, it is that, in head-armv.S , setting temporary page table is very important.

As different systems is of different image, so there is agreement of LINUX, when boot kernel, R1 is code name of system target platform, such as x86 which map image from 1M  

For example, MX1, which defined as #MACH_TYPE_MX1
Surely description of platform data structure:

1ADS, "Motorola MX1ADS") 

BOOT_MEM(0x08000000, 0x00200000, 0xf0200000) 


As its basic memory image
RAM starts from 0x08000000i/o space starts from 0x00200000i/o map space to virtual address which starts from 
Chip i/o and memory addressing uniformly. Other options will be introduced in next context.

mov r0, #F_BIT | I_BIT | MODE_SVC @ make sure svc mode //set SVC schema, and interrupt and fast interrupt is enabled

 //here set system running, arm is of seven status

 //every status have different stacks

msr cpsr_c, r0 @ and all irqs diab
bl __lookup_processor_type 

//definition processor relevant information, such as value, mask, mmuflags
//put it into
//__lookup_processor_type get following information //__lookup_architecture_type  

Here would introduce you how to tell CPU kind, such as arm7 and arm 9. How to tell them? There is only one read-only register in ARM coprocessor to store relevant information. And  __lookup_processor_type will back following structure:

.long 0x4
1009200 //CPU id 
.long 0xff00fff0 //cpu mask 
.long 0x00000c
1e @ mmuflags 
b __
.long cpu_arch_name 
.long cpu_elf_name 
.long cpu_

The first item is CPU id which will be compared with id read by coprocessor, 
others are relevant information of processor, which will be used in following initialization:

Query to the processor type and system memory image will enter the initialization process after the more critical step,
and began to set mmu, but first set up a temporary kernel page table, mapping 4m of memory, which is sufficient
during the initialization process :
 //r5=0800 0000 ram boots address  r6=0020 0000 ioaddressr7=f020 0000 vain                                                                                                                           io 
teq r7, #0 @ invalid architecture? 
moveq r0, #'a' @ yes, error 'a' 
beq __error 
bl __create_page_tables 

pgtbl r4 
//r4=0800 4000 temporary page table address
//r5=0800 0000, ram starts address
//r6=0020 0000, i/o register space starts address 
//r7=0000 3c08 
//r8=0000 0c

//the page table in 0800 4000 is just temp base page, when init_task's sweaper_page_dir ready, 
// the temp page will be useless 
// the high 
12 bit of virtual address is base table index, so we need 4kx4 = 16k temp base page, 

mov r0, r4 
mov r3, #0 
add r2, r0, #0x4000 @ 
16k of page table 
1: str r3, [r0], #4 @ Clear page table 
str r3, [r0], #4 
str r3, [r0], #4 
str r3, [r0], #4 
teq r0, r2 
* Create identity mapping for first MB of kernel. 
* This is marked cacheable and bufferable. 

* The identity mapping will be removed by 

//  linux compiling address is 0xC0008000,load address is 0x08008000, and ma virtual address 0xC0008000 into
//meanwhile, As part of the code also have direct access to 0x08008000, so 0x08008000 corresponding to the table
should be filled

// The representation in the page table is section,  AP = 11 means vistable in any mode ,

 domain is 0 
add r3, r8, r5 @ mmuflags + start of RAM 
//r3=0800 0c
add r0, r4, r5, lsr #
//r0=0800 4200 
str r3, [r0] @ identity mapping 
//*0800 4200 = 0800 0c
1e 0x200 appearance, match with 0800 0000   1
* Now setup the pagetables for our kernel direct 
* mapped region. We round TEXTADDR down to the 
* nearest megabyte boundary. 
//following image 4M 

add r0, r4, #(TEXTADDR & 0xfff00000) >> 
18 @ start of kernel 
//r0 = r4+ 0x3000 = 0800 4000 + 3000 = 0800 7000 
str r3, [r0], #4 @ PAGE_OFFSET + 0MB 
//*0800 7004 = 0800 0c
add r3, r3, #
1 << 20 
10 0c1
str r3, [r0], #4 @ PAGE_OFFSET + 
//*0800 7008 = 08
10 0c1
add r3, r3, #
1 << 20 
str r3, [r0], #4 
//*0800 700c = 0820 0c
add r3, r3, #
1 << 20 
str r3, [r0], #4 @ PAGE_OFFSET + 3MB 
//*0800 70
10 = 0830 0c1

bic r8, r8, #0x0c @ turn off cacheable 
//r8=0000 0c
12 @ and bufferable bits 
mov pc, lr //subroutine back
Next run mmu operating

On the last book has been set up the kernel page table, and then to jump to:

Function   arch/

mov r0, #0 
mcr p
15, 0, r0, c7, c7 @ invalidate I,D caches on v4 
mcr p
15, 0, r0, c7, c10, [email protected] drain write buffer on v4 
mcr p
15, 0, r0, c8, c7 @ invalidate I,D TLBs on v4 
mcr p
15, 0, r4, c2, c0 @ load page table pointer 
mov r0, #0x
1f @ Domains 0, 1 = client 
mcr p
15, 0, r0, c3, c0 @ load domain access register 
mrc p
15, 0, r0, c1, c0 @ get control register v4 
* Clear out 'unwanted' bits (then put them in if we need them) 
bic r0, r0, #0x0e00 
bic r0, r0, #0x0002 
bic r0, r0, #0x000c 
bic r0, r0, #0x
1000 @ ...0 000. .... 000. 
* Turn on what we want 
orr r0, r0, #0x003
orr r0, r0, #0x2
100 @ ..1. ...1 ..11 ...1 

#ifdef CONFIG_CPU_
orr r0, r0, #0x0004 @ .... .... .... .
#ifdef CONFIG_CPU_
orr r0, r0, #0x
1000 @ ...1 .... .... .... 
mov pc, lr 

This section of the first closed i, d cache, remove write buffer, and then set the page directory address, set
Domain protection, in the previous section, note that the domain of the page directory entry is 0, in the domain register
Of the domain 0 corresponds to 0b11, said the access mode for the manager, unrestricted.

Next, set the control register, open the d, i cache and mmu
Note that the arm of the d cache must be opened together with mmu, and i cache can be opened separately

In fact, the relationship between cache and mmu is really tight, each page table entry has a flag indicating whether it is
Cacheable, can be said to have been designed to use together

Finally, since the function returns, there is one
Mcr p15, 0, r0, c1, c0
Make the settings take effect.

The last time we talked about the completion of arm initialization, open the cache,
At this point, the assembly part of the initialization code is almost, and finally there are a few things to do:

1. Initialize the BSS segment, clear all, BSS is the global variable area.
2. Save system-related information: eg
 .long SYMBOL_NAME(compat) 
.long SYMBOL_NAME(__bss_start) 
.long SYMBOL_NAME(_end) 
.long SYMBOL_NAME(processor_id) 
.long SYMBOL_NAME(__machine_arch_type) 
.long SYMBOL_NAME(cr_alignment) 
.long SYMBOL_NAME(init_task_union)+8
3. Resets the stack pointer to point to the stack of init_task. Init_task is the first task of the system, init_task the stack in the task structure after the 8K, we will see later.

4. Finally, we will jump to the C code start_kernel.
B SYMBOL_NAME (start_kernel)

Now let us recall the current system state:
Temporary page table has been established, at 0X08004000, the mapping of the 4M, the virtual address 0XC000000 is mapped to 0X08000000.
CACHE, MMU have been opened.
The stack uses the stack of the task init_task.



Release: pty001 Category: Embedded hardware design Comment: 0 Viewed: 0
Leave a message
◎Welcome to participate in the discussion, please here to express your views and exchange your views。