热门关键字:  ubuntu  分区  linux系统进程  函数  Fedora

bootloader分析

来源: 作者: 时间:2008-06-22 Tag: 点击:
我要分析的是arm启动时的bootloader,关于bootloader的源代码版本有很多,比较经典的有blob,u- boot,vivi等,而且网上关于这些源码的分析,移植的文章也是很多。但详细可供参考的却寥寥无几。于是只有自己重新进行分析细化,在提升自己读写程序经验的同时,也培养自己写文档的习惯,当然也趁这个机会,为网络做些贡献。希望种下的大树,也能够乘凉。
     @@@@@                                                 @@@@@
     @@@由于我的知识有限,希望大家给我指出我的错误和不科学的地方@@@
     @@@@@                                                 @@@@@
本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u1/58203/showart_489453.html




软件环境:1: vivi-embeder-1.0.0.tar.bz2(验证可用)
下载地址
http://www.eefocus.com/bbs/attachment.php?aid=2887

下载所在论坛用户名和密码都用cellboy
        2:Soursce Insight 3.5(可到网上下载一个绿色版本使用)这个软件的好处是有跟踪功能,这对于定义了大量头文件的vivi源代码来说绝对是个好的选择。强烈建议使用,虽然一开始上手有点生涩,花一个晚上摸索下吧~爱上这个软件可不要说是我介绍的。

硬件环境: s3c3410开发平台(arm920t)
         NAND flash (SAMSUNG K9F1208)64MB
         Sdram (HY57V561620BT-H) 32MX2=64M
         串口 (RS232) 2 个 .........


这篇文章的任务:完成vivi/arch/s3c2410/head.S代码分析,对头文件进行重组,删减不必要程序,完成分析文档,尽量做详细说明。


先解释下head.S后缀S为什么必须是大写,很多人在make的时候发现在这个head.S这个文件这出错,那是因为他们的文件head.s的s是小写。那这是为什么了?
由于这段代码主要是汇编编写,而GNU as是不能做宏处理和文件包含处理的。在head.S文件里有很多预处理,现在要处理这些宏和文件包含,只有交给CPP预处理器来做。CPP如何识别这些文件呢?答案是通过后缀。

man gcc可以获得

           

这就是为什么用head.S的原因了!


[vivi/arch/s3c2410/head.S]的结构,在head.S主要做的是bootloader启动的第一阶段,
大致流程如下图:





/*
* vivi/arch/s3c2410/head.S:
* Author: Janghoon Lyu nandy@mizi.com>
* Date : $Date: 2003/02/26 10:38:11 $
*
* $Revision: 1.18 $
* modify by Yong Fu :Readonly
* Date  :2008/03/05
*/
#include "config.h"
#include "linkage.h"
#include "machine.h"
@
@code start
@
.text  @下面是代码段
ENTRY(_start)  @"vivi/include/linkage.h" 的宏封装,其等同与
.globle _start ; .align 0; _start: @.align 0该指令表示把当
@前位置对齐到下一个字(4字节为一字)的起始位置.
ENTRY(ResetEntryPoint) @其等同与
.globle ResetEntryPoint; .align 0; ResetEntryPoint:
@
@ Exception vector table (physical address = 0x00000000)
@
      ARM体系结构规定在上电复位后的起始位置,必须有8条连续的跳转指令,通过硬件实现。他们就是异常向量表。ARM在上电复位后,是从0x00000000开始启动的,其实如果bootloader存在,在执行下面第一条指令后,就无条件跳转到Reset,下面一部分并没执行。设置异常向量表的作用是识别bootloader。以后系统每当有异常出现,则CPU会根据异常号,从内存的0x00000000处开始查表做相应的处理
@ 0x00: Reset
    b    Reset         
@ 0x04: Undefined instruction exception
UndefEntryPoint:   
这个变量并没有进行宏定义,我猜原因是异常向量是由硬件实现若发生对应异常,自己直接跳转到这个位置,UndefEntryPoint:是可以去掉的。
    b    HandleUndef
@ 0x08: Software interrupt exception
SWIEntryPoint:
    b    HandleSWI
@ 0x0c: Prefetch Abort (Instruction Fetch Memory Abort)
PrefetchAbortEnteryPoint:
    b    HandlePrefetchAbort
@ 0x10: Data Access Memory Abort
DataAbortEntryPoint:
    b    HandleDataAbort
@ 0x14: Not used
NotUsedEntryPoint:
    b    HandleNotUsed
@ 0x18: IRQ(Interrupt Request) exception
IRQEntryPoint:
    b    HandleIRQ
@ 0x1c: FIQ(Fast Interrupt Request) exception
FIQEntryPoint:
    b    HandleFIQ
8条连续跳转的指令,7种处理器模式,一种未使用。
@
@ VIVI magics
@
  关于magics number(魔幻数字),大致了解了下其作用,主要是为了提高程序的健壮性,反馈给用户程序信息。但是具体使用方法还有待研究,关于magic number,上传一份资料。具体见1-5节

文件:
001.PDF
大小:
1223KB
下载:
下载
@ 0x20: magic number so we can verify that we only put
    .long 0
@ 0x24:
    .long 0
@ 0x28: where this vivi was linked, so we can put it in memory in the right place
    .long _start
这处存放的_start是在代码的第一句中定义的全局变量,
@ 0x2C: this contains the platform, cpu and machine id
    .long ARCHITECTURE_MAGIC
ARCHITECTURE_MAGIC这个变量出现在
"vivi/include/platform/Smdk2410.h"
#include "architecture.h"
#define MACH_TYPE
  193
#define ARCHITECTURE_MAGIC ((ARM_PLATFORM
在"architecture.h"中ARM_PLATFORM=1(arm开发板的信息) ARM_S3C2410_CPU=6(armcpu的信息)
很容易推出ARCHITECTURE_MAGIC=0x10600c1
定义platform,cpu,machine的id信息;

@ 0x30: vivi capabilities
    .long 0
#ifdef CONFIG_PM  @CONFIG_PM为配置电源管理模式
在这里先对电源管理模式进行一下描述:
在S3C2410的数据手册第七章有专门章节说明POWER MANAGEMENT:
the S3C2410X 有不同电源管理方案 来维持最佳的电源使用方式, the S3C2410X的电源管理模块能激活四种工作模式,分别为
NORMAL mode: 这个模块为CPU和外围设备提供一样的电源供应,当所有设备开启时他消费的功率最大。 这些设备能通过设置软件来开关其运行。
SLOW mode: 在这种模式下,PLL不工作,时钟信号直接由外部晶振供应。
IDLE mode: (空闲模式)这种模式下电源不给CPU供电,但任何对CPU的中断请求都从IDLE mode惊醒。
Power-OFF mode: 在这个模式下除了给wake-up逻辑单元供电,电源断开了多有的电源供应 激活Power-OFF mode 需要两个独立的电源源, 一个用于 wake-up logic. 另一个用于CPU和内部电路
在Power-OFF模式下CPU和内电路电源供应被断开
从Power-OFF模式返回可以通过设置EINT[15:0] or by RTC alarm interrupt.
具体参考:
Figure 7-8. Power Management State Diagram
Table 7-2. Clock and Power State in Each Power Mode
在这里将详细讨论Power-OFF MODE
Procedure to Enter Power_OFF mode(在Power_OFF MODE的程序设置)在这里我们主要关注后面四项,因为我们要设置的就是SDRAM在POWER OFF MODE 下的寄存器设置。
1. Set the GPIO configuration adequate for Power_OFF mode.
2. Mask all interrupts in the INTMSK register.
3. Configure the wake-up sources properly including RTC alarm.
The bit of EINTMASK corresponding to the wake-up source has not to be masked in order to let the
corresponding bit of SRCPND or EINTPEND set. Although a wake-up source is issued and the corresponding
bit of EINTMASK is masked, the wake-up will occur and the corresponding bit of SRCPND or EINTPEND will
not be set.
4. Set USB pads as suspend mode. (MISCCR[13:12]=11b)
5. Save some meaning values into GSTATUS3,4 register. These register are preserved during Power_OFF
mode.
6. Configure MISCCR[1:0] for the pull-up resisters on the data bus,D[31:0]. If there is an external BUS holder,
such as 74LVCH162245, turn off the pull-up resistors. If not, turn on the pull-up resistors
7. Stop LCD by clearing LCDCON1.ENVID bit.
8. Read rREFRESH and rCLKCON registers in order to fill the TLB.
9. Let SDRAM enter the self-refresh mode by setting the REFRESH[22]=1b.
10. Wait until SDRAM self-refresh is effective.
11. Set MISCCR[19:17]=111b to make SDRAM signals(SCLK0,SCLK1 and SCKE) protected during Power_OFF
mode
12. Set the Power_OFF mode bit in the CLKCON register.


@ 0x34:
    b    SleepRamProc   
只要翻译出上面四句话,跳转到下面的SleepRamProc 子例程就相当好理解了
#endif
#ifdef CONFIG_TEST
@ 0x38:
    b    hmi
#endif
@
@ Start VIVI head
@
Reset:
    @ disable watch dog timer
    mov    r1, #0x53000000
    mov    r2, #0x0
    str    r2, [r1]    @将r2里面的值存储到r1所包含的地址中
初始化硬件过程中第一件事就是关闭看门狗,以免watchdog记时超时引发系统重起,系统上电复位后watchdog默认是开着的,而vivi不需要他。
s3c2410用了3个寄存器对watchdog进行操作,3个寄存器分别为:WTCON,WTDAT,WTCNT。
WTCON:watchdog控制寄存器
WTDAT:watchdog数据寄存器
WTCNT:watchdog记数寄存器

以上各个寄存器的详细信息请参考s3c2410数据手册上关于第18章watchdog部分;
在这里我们只需要设置WTCON
WTCON:watchdog控制寄存器。
地址:0x53000000

16位起作用【15:0】

[15:1]可以使用默认值

[0]-------1  watchdog正常工作,0 watchdog 计时到了不发生重启
#ifdef CONFIG_S3C2410_MPORT3
    mov    r1, #0x56000000   
    mov    r2, #0x00000005
    str    r2, [r1, #0x70]  
将值0x5(0101)写入
GPHCON 0x56000070  Configure the pins of port H
GPH1 [3:2] 00 = Input  01 = Output
10 = nRTS0 11 = Reserved
GPH0 [1:0] 00 = Input  01 = Output
10 = nCTS0 11 = Reserved
    mov r2, #0x00000001
    str    r2, [r1, #0x78]
将值0x1写入
GPHUP 0x56000078 Pull-up disable register for port H
GPH[10:0]
0: The pull-up function attached to to the corresponding port pin is enabled.
1: The pull-up function is disabled.

在GPHO设置上拉电阻
    mov    r2, #0x00000001
    str r2, [r1, #0x74]
将值0x01写入
GPHDAT 0x56000074  The data register for port H
When the port is configured as input port, data from external sources can be
read to the corresponding pin. When the port is configured as output port,
data written in this register can be sent to the corresponding pin. When the
port is configured as functional pin, undefined value will be read.

在这里设置GPH0为高电平【1】,GPHO1为低电平【0】这两位的值发送到对应的端口
#endif
对于上面这段代码,整体在做什么,现在还不太清楚,看到后面的程序应该会清楚。这段代码是可选执行的,完全可以去除。
    @ disable all interrupts
    mov    r1, #INT_CTL_BASE   
    mov    r2, #0xffffffff
    str    r2, [r1, #oINTMSK]
    ldr    r2, =0x7ff
    str    r2, [r1, #oINTSUBMSK]   
上电复位后,所有中断默认是关闭的,所以可以不需要代码实现。但是为了保险起见可以写入。
[ vivi/include/S3c2410.h]
/* Interrupts */
#define INT_CTL_BASE  0x4A000000

/* Offset */
#define oSRCPND   0x00
#define oINTMOD   0x04
#define oINTMSK   0x08
#define oPRIORITY  0x0a
#define oINTPND   0x10
#define oINTOFFSET  0x14
#define oSUBSRCPND  0x18
#define oINTSUBMSK  0x1C
在这里先只讨论(详见S3C2410手册)
INTMSK(INTERRUPT MASK REGISTER)  主中断屏蔽寄存器  (32位有效)
地址:  0x4A000008

INTSUBMSK  (INTERRUPT SUB MASK REGISTER) 副中断屏蔽寄存器  (前11位有效)
地址:0x4A00001C

写 1 不响应请求, 写 0 响应请求
INTMSK写0xffffffff
INTSUBMSK写0x7ff
即可以屏蔽所有中断:
    @ initialise system clocks
    mov    r1, #CLK_CTL_BASE
    mvn    r2, #vLOCKTIME
    str    r2, [r1, #oLOCKTIME]
[ vivi/include/S3c2410.h]
/* Clock and Power Management */
#define CLK_CTL_BASE  0x4C000000
/* Offset */
#define oLOCKTIME  0x00 /* PLL lock time count register */
#define oMPLLCON  0x04 /*  MPLL configuration register */

#define oUPLLCON  0x08 /* R/W, UPLL configuration register */
#define oCLKCON   0x0C /* R/W, Clock generator control reg. */
#define oCLKSLOW  0x10 /* R/W, Slow clock control register */
#define oCLKDIVN  0x14 /* R/W, Clock divider control */
#define vLOCKTIME  0x00ffffff
#define vCLKDIVN  0x3  /* FCLK:HCLK:PCLK = 1:2:4 */
#define vMPLLCON_50  ((MDIV_50
解释如下:
用三个寄存来来控制时钟频率,
由于晶振初始频率是12MHZ=FIN

1:LOCKTIME寄存器:0x4c00000000 锁相环时间控制器设置pll时间,等待locktime时间到来,产生新的Fclk
U_TIME  [23:12] UPLL lock time count value for uclock    初始值为0xfff
M_TIME  [11:0 ]   Mpll   lock time count value for fclk,hclk,pclk   初始值为0xfff
写入的值为预定值
2:MPLLCON寄存器:0x4c0000 0004 (主锁相环时钟控制寄存器)
MDIV [19:12]  mian divider control        初始值 0x28
PDIV [ 9  : 4]   Pre-divider control      初始值 0x08
SDIV [ 1  : 0]   post divider control     初始值 0x00
时钟输出FOUT=FCLK=m*FIN/(p*2^s)
m=MDIV+8;p=PDIV+2;s=SDIV;
3:CLKDIVN寄存器:0x4C0000 0014  时钟分频控制寄存器
HDIVN [1]   0:HCLK=FCLK  1:HCLK=0.5FCLK
PDIVN [0]   0:PCLK=HCLK  1:PCLK=0.5HCLK
     @ now, CPU clock is 200 Mhz
    mov    r1, #CLK_CTL_BASE
    ldr    r2, mpll_200mhz
    str    r2, [r1, #oMPLLCON]
设置系统时钟s3c2410中第七章有详细说明
看了下基本认识如下(挑有用的看

)主要是查看datasheet
   
CLOCK & POWER MANAGEMENT  
1:时钟电源管理包含三部分: clock control, USB control, and power control.主要讨论clock control部分,关于power control等会还会涉及到。
S3C2410X 的时钟管理功能能产生三部分的时钟需求:
FCLK for CPU(ARM920T)
HCLK for the AHB bus peripherals(ARM920T, the memory controller, the interrupt controller, the
LCD controller, the DMA and the USB host block)

PCLK for the APB bus peripherals.(看门狗, IIS, I2C, PWM timer, MMC interface,
ADC, UART, GPIO, RTC and SPI)

【s3c2410只能支持AMBA 2 specification(AMBA是一种开放的ARM总线解决方案标准,为ARM广泛使用),这个版本的AMBA包含AMBA 2 AHB Interface、AMBA 2 APB Interface。也就是在S3C2410时钟电源模块中的两种总线,这两种总线所连的外设是有区别的。AHB总线连接高速外设,低速外设则通过APB总线互连。AHB总线对应Hclk,APB总线对应Pclk。如上分别连接的设备】
2:S3c2410通过PLL (Phase-Locked Loop) Block实现高频输出,它有两个PLL,MPLL用于(FCLK,HCLK,PCLK)
,UPLL用于usb block(减小电源的功率)
虽然MPLL在系统复位是默认开启的,但是在没写入值到MPLL寄存器中,. 那么外部晶振直接作为系统时钟,即使用户想直接用当前的时钟频率(12MHZ)也必须重新对MPLLCON写入控制值。
3:先看下面一段
CLOCK CONTROL LOGIC
The clock control logic determines the clock source to be used, i.e., the PLL clock (Mpll) or the direct external
clock (XTIpll or EXTCLK). When PLL is configured to a new frequency value, the clock control logic disables the
FCLK until the PLL output is stabilized using the PLL locking time. The clock control logic is also activated at
power-on reset and wakeup from power-down mode.
Power-On Reset (XTIpll)
Figure 7-4 shows the clock behavior during the power-on reset sequence. The crystal oscillator begins oscillation
within several milliseconds. When nRESET is released after the stabilization of OSC (XTIpll) clock, the PLL
starts to operate according to the default PLL configuration. However, PLL is commonly known to be unstable
after power-on reset, so Fin is fed directly to FCLK instead of the Mpll (PLL output) before the software newly
configures the PLLCON. Even if the user does not want to change the default value of PLLCON register after
reset, the user should write the same value into PLLCON register by software.
The PLL restarts the lockup sequence toward the new frequency only after the software configures the PLL with a
new frequency. FCLK can be configured as PLL output (Mpll) immediately after lock time.
1)简单的说是这样,在系统上电复位后,几个ms后晶振开始振荡起来,pll开始工作,但是由于PLL工作的不稳定性,在给MPLLCON赋值之前,cpu时钟等于FIN(12MHZ),这一部分都是硬件自己完成。开始执行代码。
2)只有在给PLLCON写入值,PLL才开始工作,在经过PLL Lock-time后PLL才能输出一个新的频率值。这一部分必须软件完成。这也是为什么即使就是MPLLCON有默认的值,也要重新输入值的原因。
软件设置过程
具体参考上一个文字框
    mov    r1, #CLK_CTL_BASE
    mov    r2, #0x3
    str    r2, [r1, #oCLKDIVN]
    mrc    p15, 0, r1, c1, c0, 0        @ read ctrl register
    orr    r1, r1, #0xc0000000        @ Asynchronous
    mcr    p15, 0, r1, c1, c0, 0        @ write ctrl register
NOTES
1. CLKDIVN should be set carefully not to exceed the limit of HCLK and PCLK.
2. If HDIVN=1, the CPU bus mode has to be changed from the fast bus mode to the asynchronous bus
mode using following instructions.
MMU_SetAsyncBusMode
mrc p15,0,r0,c1,c0,0
orr r0,r0,#R1_nF:OR:R1_iA
mcr p15,0,r0,c1,c0,0

If HDIVN=1 and the CPU bus mode is the fast bus mode, the CPU will operate by the HCLK. This feature
can be used to change the CPU frequency as a half without affecting the HCLK and PCLK.
这段的基本意思是说假如HDIVN=1,那么CPU模式在从fast bus mode变化到异步模式需要执行一下以上三行代码~
这也相当与飞机的软着陆方式
    bl    memsetup
#ifdef CONFIG_PM
最新评论共有 0 位网友发表了评论
发表评论
评论内容:不能超过250字,需审核,请自觉遵守互联网相关政策法规。
用户名: 密码:
匿名?
注册