本例子是基于三星的S3C2410ARM开发板
内存分布如下:
| 未用 |
0x60000000 --------------------
| 特殊寄存器区 |
0x48000000 ---------------------
| 未用 |
0x40000000 --------------------- <==============
| SDRAM(nGCS7) | ||
0x38000000 --------------------- || 内存区域
| SDRAM(nGCS6) | ||
0x30000000 --------------------- <=============
| SROM(nGCS5) | ||
0x28000000 --------------------- ||
| SROM(nGCS4) | ||
0x20000000 --------------------- ||
| SROM(nGCS3) | || 只读存储器区域
0x18000000 --------------------- ||
| SROM(nGCS2) | ||
0x10000000 --------------------- ||
| SROM(nGCS1) | ||
0x08000000 --------------------- <==============
| BootSRAM(4K) |
0x00000000 ----------------------<---------Nand-Flash内部4KB空间被映射到0x00000000
stage1程序设计:
主要完成任务有:屏蔽所有中断、设置CPU的速度和时钟频率、RAM初始化、初始化串口、初始化USB、设置堆栈指针sp、调用C语言main函数等
START.S
;引入外部标号在这声明
IMPORT |Image$$RO$$Base| ;只读代码段开始地址
IMPORT |Image$$RO$$Limit| ;只读区域大小
IMPORT |Image$$RW$$Base| ;可读写存储区域起始地址
IMPORT |Image$$ZI$$Base| ;清零区域起始地址
IMPORT |Image$$ZI$$Limit| ;清零区域大小
IMPORT Main ;引入外部函数Main,进入C语言程序
;给外部使用的标号在这里
EXPORT ResetEntry
;内部常量定义
;工程实际存储空间定义
SDRAM_END EQU 0x34000000 ;64M SDRAM存储空间
;开始堆栈地址空间
_STACK_BASEADDRESS EQU (SDRAM_END - 0X8000) ;0x33ff8000堆栈基地址
_MMUTT_STARTADDRESS EQU (SDRAM_END - 0X8000) ;0x33ff800定义MMU表基地址
_ISR_STARTADDRESS EQU (SDRAM_END - 0X100) ;0x3ffff00堆栈服务程序开始地址
;ARM 异常模式定义
USERMODE EQU 0x10 ;0b10000用户模式
FIQMODE EQU 0x11 ;0b10001 FIQ模式
IRQMODE EQU 0x12 ; 0b10010 IRQ模式
SVCMODE EQU 0x13 ; 0b10011 管理模式
ABORTMODE EQU 0x17 ;0b10111 终止模式
UNDEFMODE EQU 0x1b ;0b11011 未定义
MODMASK EQU 0x1f ;0b11111 系统模式
NOINT EQU 0xC0 ;屏蔽中断位
;ARM时钟定义
FCLK EQU 50000000
M_MDIV EQU 0X5C ;Fin=12.0MHz Fout=50.0MHz
M_PDIV EQU 0X4
M_SDIV EQU 0X2
;控制寄存器的地址定义
BWSCON EQU 0X48000000 ;总线宽度和等待控制寄存器
INTMSK EQU 0X4A000008 ;中断屏蔽控制寄存器
INTOFFSET EQU 0X4A000014 ;中断请求源偏移寄存器
INTSUBMSK EQU 0X4A00001C ;次级中断屏蔽寄存器
LOCKTIME EQU 0X4C000000 ;PLL锁定时间控制寄存器
MPLLCON EQU 0X4C000004 ;MPLL控制寄存器
WTCON EQU 0X53000000 ;看门狗定时器模式寄存器
;ARM异常模式堆栈
UserStack EQU (_STACK_BASEADDRESS-0X3800)
SVCStack EQU (_STACK_BASEADDRESS - 0X2800)
UndefStack EQU (_STACK_BASEADDRESS - 0X2400)
AbortStack EQU (_STACK_BASEADDRESS - 0X2000)
IRQStack EQU (_STACK_BASEADDRESS - 0X1000)
FIQStack EQU (_STACK_BASEADDRESS - 0X0)
MACRO
$HandlerLabel HANDLER $HandleLabel
$HandlerLabel
sub sp,sp,#4
stmfd sp!,{r0}
ldr r0,=$HandlerLabel
ldr r0,[r0]
str r0,[sp,#4]
ldmfd sp!,{r0,pc}
MEND
这个宏的作用是把各个中断程序的地址装入当前的PC,2410有两种装断模式:
一种是没有中断向量表
一种是使用中断向量表的
使用中断向量表只能是IRQ方式,当使用中断向量表的时候,中断发生时由2410的中断控制器自动跳转到相应的位置
CODE32
AREA SelfBoot,CODE,READONLY
ENTRY
ResetEntry
b ResetHandler
b HandlerUndef
b HandlerSWI
b HandlerPabort
b HandlerDabort
b .
b HandlerIRQ
b HandlerFIQ
b .
LTORG
HandlerFIQ HANDLER HandlerFIQ
HandlerIRQ HANDLER HandlerIRQ
HandlerUndef HANDLER HandlerUndef
HandlerSWI HANDLER HandlerSWI
HandlerDabort HANDLER HandlerDaBort
HandlerPabort HANDLER HandlerPabort
IsrIRQ
sub sp,sp,#4
stmfd sp!,{r8-r9}
ldr r9,INTOFFSET
ldr r9,[r9]
ldr r8,=HandlerEINT0
add r8,r8,r9,lsl #2
ldr r8,[r8]
str r8,[sp,#8]
ldmfd sp!,{r8-r9,pc}
ResetHandler
ldr r0=WTCON
ldr r1,=0x0
str r1,[r0]
ldr r0,=INTMSK
ldr r1,=0xffffffff
str r1,[r0]
ldr r0,=INTSUBMSK
ldr r1,=0x3ff
str r1,[r0]
ldr r0,=LOCKTIME
ldr r1,=((M_MDIV << 12) + (M_PDIV <<4)+ M_SDIV)
str r1,[r0]
adr r0,MSRDATA
ldr r1,=BWSCON
add r2,r0,#52
ldr r3,[r0],#4
str r3,[r1],#4
cmp r2,r0
bne %B0
bl InitStacks
ldr r0,=HandlerIRQ
ldr r1,=IsrIRQ
str r1,[r0]
adr r0,ResetEntry
ldr r2,|Image$$RO$$Base|
cmp r0,r2
ldreq r0,|Image$$RO$$Limit|
beq InitRam
ldr r3,|Image$$R0$$Limit|
0
ldmia r0!,{r4-r7}
stmid r2!,{r4-r7}
cmp r2,r3
bcc %B0
sub r0,r2,r3
sub r0,r0,r2
InitRam
ldr r2,|Image$$RW$$Base|
ldr r3,|Image$$ZI$$Base|
0
cmp r2,r3
ldrccr1 [r0],#4
strccr1 [r2],#4
bcc %B0
mov r0,#0
ldr r3,|Image$$ZI$$Limit|
1
cmp r2,r3
strcc r0,[r2],#4
bl Main
b .
InitStacks
mrs r0,cpsr
bic r0,r0,#MODEMASK
orr r1,r0,#UNDEFMODE | NOINT
msr cpsr_cxsf,r1
ldr sp,=UndefStack
orr r1,r0,#ABORTMODE | NOINT
msr cpsr_cxsf,r1
ldr sp,=AbortStack
orr r1,r0,#IRQMODE | NOINT
msr cpsr_cxsf,r1
ldr sp,=IRQStack
orr r1,r0,#FIQMODE | NOINT
msr cpsr_cxsf,r1
ldr sp,=FIQStack
bic r0,r0,#MODEMASK | NOINT
orr r1,r0,#SVCMODE
msr cpsr_cxsf,r1
ldr sp,=SVCStack
mov pc,lr
LTORG
SMRDATA DATA
DCD (0+B1_BWSCON << 4) + (B2_BWSCON << 8) + (B3_BWSCON << 12) + (B4_BWSCON << 16) + (B5_BWSCON << 20) + (B6_BWSCON << 24) + (B7_BWSCON << 28))
...
...
DCD 0x32
DCD 0x30
DCD 0x30
BootLoader 的stage2
BootLoader的stage2通常用C语言来实现,主要完成初始化本阶段要使用到的硬件设备、检测系统内存映像,将Kernel镜像和根文件系统镜像从Flash上读到RAM空间中、为内核设置启动参数、调用内核等几个内容
init.c
#define USB_COMMAND (*(unsigned char data *)0x54000000)
#define USB_DATA (*(unsigned char data *)0x54000004)
int main(int argc,char **argv)
{
u32 test = 0;
void (*theKERNEL)(int zero,int arch,unsigned long params_addr) = (void (*)(int ,int,unsigned long))RAM_COMPESSED_KERNEL_BASE;
//压缩后的IMAGE地址
int i,k = 0;
chkBs = (_RAM_STARTADDRESS); //SDRAM开始的地方
MMU_EnableICache();
ChangeClockDivider(1,1); //时钟驱动1:2:4
ChangeMPllValue(M_MDIV,M_PDIV,M_SDIV);
Port_Init(); //设置I/O端口,在使用com口前,必须调用这个函数,否则通信芯片得不到数据
Uart_Init(PCLK,115200); //初始化串口,PCLK使用默认值200000,波特率115200
USB_Init();
检查RAM空间
Uart_SendString("ntLinux S3C2410 Nor BootLoadern");
Uart_SendString("ntChecking SDRAM 2410loader.c ...n");
for(;chkBs < 0x33FA0140 ; chkBs = chkBs + 0x4,test ++)以字为单位检查内容
{
chkPt1 = chkBs;
*(u32 *)chkPt1 = test; 写数据
if(*(u32 *)chkPt1 == 1024) 读数据和写入的是否一样
{
chkPt1 += 4;
Led_Display(1); LED灯亮
Led_Display(2);
Led_Display(3);
Led_Display(4);
}
else
goto error;
}
Uart_SendString("nt SDRAM Check Successful ! ntMemory Maping ....");
get_memory_map(); 获得可用memory信息,做成列表,后面会作为启动参数传给KERNEL
Uart_SendString("nt Memory Map Successful!n");
Uart_SendString("tLoading KERNEL IMAGE from FLASH ... n ");
Uart_SendString("tand copy KERNEL IMAGE to SDRAM at 0x31000000n ");
for(k = 0 ;k < 98304 ; k ++,downPt += 1,fromPt += 1) 复制内核
*(u32 *)downPt = *(u32 *)fromPt;
//Linux内核大小:3M,3*1024*1024/32 = 98304
加载RAMDISK
Uart_SendString("ttloading COMPRESSED RAMDISK ... n ");
downPt = (RAM_COMPRESSED_RAMDISK_BASE);
fromPt = (FLASH_RAMDISK_BASE);
jffs2文件系统,在开发中如果用不到FLASH,这段也可以不要
for(k = 0; k < 98304 ; k ++,downPt += 1,fromPt += 1)
*(u32 *)downPt = * (u32 *)fromPt;
Uart_SendString("ttloading jffs2 ... n");
downPt = (RAM_JFFS2);
fromPt = (FLASH_JFFS2);
for(k = 0; k < (1024 * 1024 / 32) ; k ++,downPt += 1,fromPt += 1)
*(u32 *)downPt = *(u32 *)fromPt;
Uart_Sending("Load Success ... Run ... n ");
设置Linux参数
setup_start_tag(); 开始设置启动参数
setup_memory_tags(); 设置内存参数
setup_commandline_tag("console=ttyS0,115200n8"); 启动命令行
setup_initrd2_tag(); 初始化根设备
setup_ramdisk_tag(); 建立RAMDISK
setup_end_tag();
asm("mrc p15,0,%0,c1,c0,0":"=r" (i));
i &= ~0x1000;
asm("mcr p15,0,%0,c1,c0,0)::"r"(i));
asm("mcr p15,0,%0,c7,c5,0"::"r"(i));
下面这行就跳到COMPRESSED KERNEL的首地址
theKERNEL(0,ARCH_NUMBER,(unsigned long *)(RAM_BOOT_PARAMS));
error: 程序出错结束
Uart_SendString("nnPanic SDRAM check error!n");
return 0;
}
static void setup_start_tag(void) 设置启动参数
{
params = (struct tag *)RAM_BOOT_PARAMS; 启动参数开始的地址
params -> hdr.tag = ARAG_CORE;
params -> hdr.size = tag_size(tag_core);
params -> u.core.flags = 0;
params -> u.core.pagesize = 0;
params -> u.core.rootdev = 0;
params = tag_next(params);
}
static void setup_memory_tags(void) 设置内存标志
{
int i;
for(i = 0; i < NUM_MUM_AREAS ; i++)
{
if(memory_map[i].used)
{
params -> hdr.tag = ATAG_MEM;
params -> hdr.size = tag_size(tag_mem32);
params -> u.mem.start = memory_map[i].start;
params -> u.mem.size = memory_map[i].len;
params = tag_next(params);
}
}
}
static void setup_commandline_tag(char *commandline) 设置命令行
{
int i = 0;
params -> hdr.tag = ATAG_CMDLINE;
params -> hdr.size = 8;
strcpy(params->u.cmdline.cmdline,p);
params = tag_next(params);
}
static void setup_initrd2_tag(void) 初始化根设备
{
params -> hdr.tag = ATAG_INITRD2;
params -> hdr.size = tag_size(tag_initrd);
params -> u.initrd.start = RAM_COMPRESSED_RAMDISK_BASE;
params -> u.initrd.size = 2047;
params = tag_next(params);
}
static void setup_ramdisk_tag(void) 建立RAMDISK
{
params -> hdr.tag = ATAG_RAMDISK;
params -> hdr.size = tag_size(tag_ramdisk);
params -> u.ramdisk.start = RAM_DECOMPRESSED_RAMDISK_BASE;
params -> u.ramdisk.flags = 1;
params = tag_next(params);
}
static void setup_end_tag(void) 设置结束
{
params -> hdr.tag = ATAG_NONE;
params -> hdr.size = 0;
}
void Uart_Init(int pclk,int baud) 串口初始化
{
int i;
if(pclk == 0)
pclk = PCLK;
rUFCON0 = 0x0;
rUMCON0 = 0x0
rULCON0 = 0x3;
rUCON0 = 0x245;
rUBRDIV0 = ((int)(PCLK/16./baud) -1 ); 波特率分配寄存器
delay(10);
}
void USB_Init() USB初始化
{
USB_COMMAND = 0xf3;
USB_DATA = 0x06; 设置模式0
USB_DATA = 0x03; 初始化频率12MHz
USB_COMMAND = 0xd0;
USB_DATA = 0x80; 设置地址0使能
USB_COMMAND = 0xf3; 连接主机
USB_DATA = 0x16;
}