BootLoader设计

本例子是基于三星的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;

}

 

 

 

 

 

 

 

 

 

 

 

 

 

你可能感兴趣的:(loader)