首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

vivi boot loader的实现

vivi boot loader的实现

vivi boot loader的实现

参考资料:
1. 嵌入式系统 Boot Loader 技术内幕, 詹荣开 (zhanrk@sohu.com)
2. Getting started with VIVI,  Janhoon Lyu, nandy@mizi.com
3. 嵌入式设备上的Linux 系统开发,A. Santhanam etc.
4. Linux system development on an embedded device, A. Santhanam
5. vivi 有关资料http://www.mizi.com/developer/s3c2410x/index.html
6. smdk2410的硬件和软件/linux相关资料 http://www.samsung.com search  2410

说明:本文文字结构照抄” 嵌入式系统 Boot Loader 技术内幕, 詹荣开 (zhanrk@sohu.com)” 一文,以vivi中head.S作为stage1, main()作为stage2,解释了VIVI for SMDK2410 (based on S3C2410) 开发系统的bootloader的实现。将原文放在这里是为了方便读者。注意,VIVI的实现并非完全跟原文一致。多谢原文作者詹大侠的详细解释。
附录有一节__SETUP在kernel的作用来自jeppeter (member) from http:// linuxforum.net

文中对MTD subsystem linux没作解释。Google “MTD linux subsystem 文件系统 JFSS2 ”可以获得足够的解释。

Chapter 1 Boot loader基本结构
由于 Boot Loader 的实现依赖于 CPU 的体系结构,因此大多数 Boot Loader 都分为 stage1 和 stage2 两大部分。依赖于 CPU 体系结构的代码,比如设备初始化代码等,通常都放在 stage1 中,而且通常都用汇编语言来实现,以达到短小精悍的目的。而 stage2 则通常用C语言来实现,这样可以实现给复杂的功能,而且代码会具有更好的可读性和可移植性。

Boot Loader 的 stage1 通常包括以下步骤(以执行的先后顺序):

硬件设备初始化。
为加载 Boot Loader 的 stage2 准备 RAM 空间。
拷贝 Boot Loader 的 stage2 到 RAM 空间中。
设置好堆栈。
跳转到 stage2 的 C 入口点。

Boot Loader 的 stage2 通常包括以下步骤(以执行的先后顺序):

初始化本阶段要使用到的硬件设备。
检测系统内存映射(memory map)。
将 kernel 映像和根文件系统映像从 flash 上读到 RAM 空间中。
为内核设置启动参数。
调用内核。

51 c8051f(f020,f040) msp430 arm(2410,2510) fpga(xc3s4000) dsp(5116 dm642) keilc vc++ matlab linux protel Ten_layerPCB mpegx h.26x Rscode Turbocode ofdm VideoBroadcasting ldpc_code(now!)

1.1 Boot Loader 的 stage1

1.1.1 基本的硬件初始化

这是 Boot Loader 一开始就执行的操作,其目的是为 stage2 的执行以及随后的 kernel 的执行准备好一些基本的硬件环境。它通常包括以下步骤(以执行的先后顺序):

1. 屏蔽所有的中断。为中断提供服务通常是 OS 设备驱动程序的责任,因此在 Boot Loader 的执行全过程中可以不必响应任何中断。中断屏蔽可以通过写 CPU 的中断屏蔽寄存器或状态寄存器(比如 ARM 的 CPSR 寄存器)来完成。

2. 设置 CPU 的速度和时钟频率。

3. RAM 初始化。包括正确地设置系统的内存控制器的功能寄存器以及各内存库控制寄存器等。

4. 初始化 LED。典型地,通过 GPIO 来驱动 LED,其目的是表明系统的状态是 OK 还是 Error。如果板子上没有 LED,那么也可以通过初始化 UART 向串口打印 Boot Loader 的 Logo 字符信息来完成这一点。

5. 关闭 CPU 内部指令/数据 cache。


VIVI在第一阶段完成以下任务


Disable watch dog timer ;         disable all interrupts  ; 

 initialise system clocks; initialise the static memory     All LED on  

 set GPIO for UART         Initialize UART 0  ; 

copy_myself to ram;        jump to ram
 get read to call C functions          setup stack pointer
call main
1.1.2 为加载 stage2 准备 RAM 空间

为了获得更快的执行速度,通常把 stage2 加载到 RAM 空间中来执行,因此必须为加载 Boot Loader 的 stage2 准备好一段可用的 RAM 空间范围。

由于 stage2 通常是 C 语言执行代码,因此在考虑空间大小时,除了 stage2 可执行映象的大小外,还必须把堆栈空间也考虑进来。此外,空间大小最好是 memory page 大小(通常是 4KB)的倍数。一般而言,1M 的 RAM 空间已经足够了。具体的地址范围可以任意安排,比如 blob 就将它的 stage2 可执行映像安排到从系统 RAM 起始地址 0xc0200000 开始的 1M 空间内执行。但是,将 stage2 安排到整个 RAM 空间的最顶 1MB(也即(RamEnd-1MB) - RamEnd)是一种值得推荐的方法。

为了后面的叙述方便,这里把所安排的 RAM 空间范围的大小记为:stage2_size(字节),把起始地址和终止地址分别记为:stage2_start 和 stage2_end(这两个地址均以 4 字节边界对齐)。因此:

stage2_end=stage2_start+stage2_size

51 c8051f(f020,f040) msp430 arm(2410,2510) fpga(xc3s4000) dsp(5116 dm642) keilc vc++ matlab linux protel Ten_layerPCB mpegx h.26x Rscode Turbocode ofdm VideoBroadcasting ldpc_code(now!)

另外,还必须确保所安排的地址范围的的确确是可读写的 RAM 空间,因此,必须对你所安排的地址范围进行测试。具体的测试方法可以采用类似于 blob 的方法,也即:以 memory page 为被测试单位,测试每个 memory page 开始的两个字是否是可读写的。为了后面叙述的方便,我们记这个检测算法为:test_mempage,其具体步骤如下:

1. 先保存 memory page 一开始两个字的内容。

2. 向这两个字中写入任意的数字。比如:向第一个字写入 0x55,第 2 个字写入 0xaa。

3. 然后,立即将这两个字的内容读回。显然,我们读到的内容应该分别是 0x55 和 0xaa。如果不是,则说明这个 memory page 所占据的地址范围不是一段有效的 RAM 空间。

4. 再向这两个字中写入任意的数字。比如:向第一个字写入 0xaa,第 2 个字中写入 0x55。

5. 然后,立即将这两个字的内容立即读回。显然,我们读到的内容应该分别是 0xaa 和 0x55。如果不是,则说明这个 memory page 所占据的地址范围不是一段有效的 RAM 空间。

6. 恢复这两个字的原始内容。测试完毕。

为了得到一段干净的 RAM 空间范围,我们也可以将所安排的 RAM 空间范围进行清零操作。

1.1.3 拷贝 stage2 到 RAM 中

拷贝时要确定两点:(1) stage2 的可执行映象在固态存储设备的存放起始地址和终止地址;(2) RAM 空间的起始地址。

1.1.4 设置堆栈指针 sp

堆栈指针的设置是为了执行 C 语言代码作好准备。通常我们可以把 sp 的值设置为(stage2_end-4),也即在 1.1.2 节所安排的那个 1MB 的 RAM 空间的最顶端(堆栈向下生长)。

此外,在设置堆栈指针 sp 之前,也可以关闭 led 灯,以提示用户我们准备跳转到 stage2。

经过上述这些执行步骤后,系统的物理内存布局应该如下图2所示。

1.1.5 跳转到 stage2 的 C 入口点

在上述一切都就绪后,就可以跳转到 Boot Loader 的 stage2 去执行了。比如,在 ARM 系统中,这可以通过修改 PC 寄存器为合适的地址来实现。

head.S 负责完成硬件初始化操作,具体分析见源码注释 ,汇编差不多忘光了,下面注释中有关汇编的东西多些。
其中"linkage.h"   
#define SYMBOL_NAME_STR(X) #X
#define SYMBOL_NAME(X) X
#ifdef __STDC__
#define SYMBOL_NAME_LABEL(X) X##:
#else
#define SYMBOL_NAME_LABEL(X) X/**/:
#endif

#define __ALIGN .align 0
#define __ALIGN_STR ".align 0"

#ifdef __ASSEMBLY__

#define ALIGN __ALIGN
#define ALIGN_STR __ALIGN_STR

#define ENTRY(name) \
  .globl SYMBOL_NAME(name); \
  ALIGN; \
  SYMBOL_NAME_LABEL(name)

#endif

其中"machine.h" 包括了
smdk2410.h (有关开发板的配置) ,
包括memory map, Porocessor memory map ,FLASH, ROM, DRAM的物理地址和在VIVI中用的虚拟地址(?),Architecture magic and machine type, UART,CPU,DRAM的初始化参数等

smdk2410.h进一步包括s3c2410.h, 有关CPU的设置,Definition of constants related to the S3C2410 microprocessor(based on ARM 920T).

51 c8051f(f020,f040) msp430 arm(2410,2510) fpga(xc3s4000) dsp(5116 dm642) keilc vc++ matlab linux protel Ten_layerPCB mpegx h.26x Rscode Turbocode ofdm VideoBroadcasting ldpc_code(now!)

/*
 * vivi/arch/s3c2410/head.S:
 *   Initialise hardware
 *
 * Copyright (C) 2001 MIZI Research, Inc.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 *
 * Author: Janghoon Lyu <nandy@mizi.com>
 * Date  : $Date: 2003/02/26 10:38:11 $
 *
 * $Revision: 1.18 $
 *
 *
 * History:
 *
 * 2002-05-14: Janghoon Lyu <nandy@mizi.com>
 *   - Initial code
 *
 */

#include "config.h"   //autoconf.h 空的
#include "linkage.h"   //定义
#include "machine.h"

@ Start of executable code

ENTRY(_start)   //入口点
ENTRY(ResetEntryPoint)

@
@ Exception vector table (physical address = 0x00000000)
@ //异常向量 表 物理地址0x0000000

@ 0x00: Reset  //最 基本操作 :复位  B 是最简单的分支。一旦遇到一个 B 指令,ARM 处理器将立即跳转到给定的地址,从那里继续执行。注意存储在分支指令中的实际的值是相对当前的 R15 的
 b Reset

@ 0x04: Undefined instruction exception //处理未定义的指令
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 // fast 中断处理
FIQEntryPoint:
 b HandleFIQ

@
@ VIVI magics
@

@ 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
@ 0x2C: this contains the platform, cpu and machine id
 .long   ARCHITECTURE_MAGIC
@ 0x30: vivi capabilities
 .long   0
#ifdef CONFIG_PM  // power management //vivi未用
@ 0x34:
 b SleepRamProc
#endif
#ifdef CONFIG_TEST  //test mode vivi未用
@ 0x38:
 b hmi
#endif

51 c8051f(f020,f040) msp430 arm(2410,2510) fpga(xc3s4000) dsp(5116 dm642) keilc vc++ matlab linux protel Ten_layerPCB mpegx h.26x Rscode Turbocode ofdm VideoBroadcasting ldpc_code(now!)

@
@ Start VIVI head
@
Reset:      //第一步 RESET
 @ disable watch dog timer  //disable watch dog定时器
 mov r1, #0x53000000
 mov r2, #0x0
 str r2, [r1]   //add 0x5300_0000 清 0,bit 5=0 disable this timer

#ifdef CONFIG_S3C2410_MPORT3 //另一种Platform 非SMDK
 mov r1, #0x56000000
 mov r2, #0x00000005
 str r2, [r1, #0x70]
 mov r2, #0x00000001
 str r2, [r1, #0x78]
 mov r2, #0x00000001
 str r2, [r1, #0x74]
#endif

 @ disable all interrupts  //禁止 所有中断
 mov r1, #INT_CTL_BASE  //0x4A00_0000 source pending register
 mov r2, #0xffffffff
 str r2, [r1, #oINTMSK]   //0x4A00_0008
//0x4A00_0008  INTERRUPT MASK register=0xFFFFFFFF, disable all int
 ldr r2, =0x7ff
 str r2, [r1, #oINTSUBMSK]  //0x4A00_001C
  //Interrupt sub mask register ,  bit[10:0] = 1 ->0x7FF ->disable all

 @ initialise system clocks  //初始化系统时钟
 mov r1, #CLK_CTL_BASE  // LOCK TIME COUNT REGISTER(LOCKTIME)
      //0x4c000000
 mvn r2, #0xff000000   
 str r2, [r1, #oLOCKTIME]  //0x4C000000 ->0xFF00_0000;

 @ldr r2, mpll_50mhz  //CPU定成50Mhz
 @str r2, [r1, #oMPLLCON]  //
#ifndef CONFIG_S3C2410_MPORT1  //如果未定义成MPORT1 (一种plat form)
 @ 1:2:4
 mov r1, #CLK_CTL_BASE
 mov r2, #0x3   
 str r2, [r1, #oCLKDIVN]  //
// vCLKDIVN  0x3  /* FCLK:HCLKCLK = 1:2:4 */
 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

 @ now, CPU clock is 200 Mhz  //CPU定成200Mhz
 mov r1, #CLK_CTL_BASE
 ldr r2, mpll_200mhz
 str r2, [r1, #oMPLLCON]
#else      //platform= MPORT1 ,以下不理
 @ 1:2:2
    mov r1, #CLK_CTL_BASE
    ldr r2, clock_clkdivn
    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

    @ now, CPU clock is 100 Mhz
    mov r1, #CLK_CTL_BASE
    ldr r2, mpll_100mhz
    str r2, [r1, #oMPLLCON]
#endif
 bl memsetup    //第2步memsetup

#ifdef CONFIG_PM     //如果有 Power management:不用
 @ Check if this is a wake-up from sleep
 ldr r1, PMST_ADDR
 ldr r0, [r1]
 tst r0, #(PMST_SMR)
 bne WakeupStart
#endif

51 c8051f(f020,f040) msp430 arm(2410,2510) fpga(xc3s4000) dsp(5116 dm642) keilc vc++ matlab linux protel Ten_layerPCB mpegx h.26x Rscode Turbocode ofdm VideoBroadcasting ldpc_code(now!)

#ifdef CONFIG_S3C2410_SMDK   //SMDK platform
 @ All LED on     //点灯,好歹通知一下外面的同志3
 mov r1, #GPIO_CTL_BASE
 add r1, r1, #oGPIO_F
 ldr r2,=0x55aa
 str r2, [r1, #oGPIO_CON]
 mov r2, #0xff
 str r2, [r1, #oGPIO_UP]
 mov r2, #0x00
 str r2, [r1, #oGPIO_DAT]
#endif

#if 0
 @ SVC
 mrs r0, cpsr
 bic r0, r0, #0xdf
 orr r1, r0, #0xd3
 msr cpsr_all, r1
#endif
      //设置串口 ,内外联络的通道
 @ set GPIO for UART
 mov r1, #GPIO_CTL_BASE  // 0x5600_0000
 add r1, r1, #oGPIO_H  
// oGPIO_H 0x70 PORT H CONTROL REGISTERS

 ldr r2, gpio_con_uart  // vGPHCON= 0x0016faaa
 str r2, [r1, #oGPIO_CON]  // 01 01 10 11 11 10 10 10 10 10 10 B
   //oGPIO_CON = 0x0 
// GPH0 bit[1:0] = 10  nCTS0
// GPH1 bit[3:2] = 10  nRTS0
// GPH2 bit[5:4] = 10  TXD0
// GPH3 bit[7:6] = 10  RXD0
// GPH4 bit[9:8] = 10 TXD1
// GPH5 bit[11:10] = 10 RXD1
// GPH6 bit[13:12] = 11  nRTS1
// GPH7 bit[15:14] = 11 nCTS1
// GPH8 bit[17:16] = 10 UEXTCLK
// GPH9 bit[19:18] = 01 Output
// GPH10 bit[21:20] = 01 Output

 ldr r2, gpio_up_uart  // vGPHUP 0x000007ff = 0111 1111 1111  B
 str r2, [r1, #oGPIO_UP] 
// oGPIO_UP 0x8 /* R/W, Pull-up disable register */
  //  0x7FF ->  1: The pull-up function is disabled. For all GPHx
// reg GPHUP 0x56000078

 bl InitUART   //initialize UART

#ifdef CONFIG_DEBUG_LL   //low level debugging info
 @ Print current Program Counter //vivi def没用
 ldr r1, SerBase   //往串口上输出 info
 mov r0, #'\r'
 bl rintChar
 mov r0, #'\n'
 bl rintChar
 mov r0, #'@'
 bl rintChar
 mov r0, pc
 bl rintHexWord
#endif


#ifdef CONFIG_BOOTUP_MEMTEST  //  comment 'Low Level Hardware Debugging'
  bool ' Enable simple memory test' CONFIG_BOOTUP_MEMTEST  //vivi def没用
 @ simple memory test to find some DRAM flaults.
 bl memtest //check the first 1MB  in increments of 4k//改大点3
#endif

#ifdef CONFIG_S3C2410_NAND_BOOT
 bl copy_myself

 @ jump to ram
 ldr r1, =on_the_ram  //将on_the_ram的地址装入r1
 add pc, r1, #0   //pc = r1+0
 nop
 nop
1: b 1b  @ infinite loop  //硬是看不懂这个 B

on_the_ram:
#endif

51 c8051f(f020,f040) msp430 arm(2410,2510) fpga(xc3s4000) dsp(5116 dm642) keilc vc++ matlab linux protel Ten_layerPCB mpegx h.26x Rscode Turbocode ofdm VideoBroadcasting ldpc_code(now!)

#ifdef CONFIG_DEBUG_LL
 ldr r1, SerBase
 ldr r0, STR_STACK
 bl rintWord
 ldr r0, DW_STACK_START
 bl rintHexWord
#endif

 @ get read to call C functions
 ldr sp, DW_STACK_START @ setup stack pointer
   // STACK_BASE+STACK_SIZE-4
// STACK_BASE = (VIVI_PRIV_RAM_BASE - STACK_SIZE)
//STACK从上往下用。  所以 STACK_START = STACK_BASE+STACK_SIZE-4
 mov fp, #0   @ no previous frame, so fp=0
 mov a2, #0   @ set argv to NULL

 bl main   @ call main //如果正常,一去不复返的了

mov pc, #FLASH_BASE @ otherwise, reboot, //FLASH_BASE=ROM_BASE0 = 0x0

@
@ End VIVI head
@

/*
 * subroutines
 */

@
@ Wake-up codes
@
#ifdef CONFIG_PM
WakeupStart:   // power management 用
 @ Clear sleep reset bit
 ldr r0, PMST_ADDR
 mov r1, #PMST_SMR
 str r1, [r0]

 @ Release the SDRAM signal protections
 ldr r0, PMCTL1_ADDR
 ldr r1, [r0]
 bic r1, r1, #(SCLKE | SCLK1 | SCLK0)
 str r1, [r0]

 @ Go...
 ldr r0, PMSR0_ADDR @ read a return address
 ldr r1, [r0]
 mov pc, r1
 nop
 nop
1: b 1b  @ infinite loop

SleepRamProc:    //power management用
 @ SDRAM is in the self-refresh mode */
 ldr r0, REFR_ADDR
 ldr r1, [r0]
 orr r1, r1, #SELF_REFRESH
 str r1, [r0]

 @ wait until SDRAM into self-refresh
 mov r1, #16
1: subs r1, r1, #1 
 bne 1b

 @ Set the SDRAM singal protections
 ldr r0, PMCTL1_ADDR
 ldr r1, [r0]
 orr r1, r1, #(SCLKE | SCLK1 | SCLK0)
 str r1, [r0]

 /* Sleep... Now */
 ldr r0, PMCTL0_ADDR
 ldr r1, [r0]
 orr r1, r1, #SLEEP_ON
 str r1, [r0] 
1: b 1b

#ifdef CONFIG_TEST
hmi:     
 ldr r0, PMCTL0_ADDR  // PMCTL0_ADDR: .long 0x4c00000c, Clock Gen Ctrl
 ldr r1, =0x7fff0  // reset clock gen ctrl
 str r1, [r0]
 
 @ All LED on    //点灯?
 mov r1, #GPIO_CTL_BASE
 add r1, r1, #oGPIO_F
 ldr r2,=0x55aa
 str r2, [r1, #oGPIO_CON]
 mov r2, #0xff
 str r2, [r1, #oGPIO_UP]
 mov r2, #0xe0
 str r2, [r1, #oGPIO_DAT]
1: b 1b
#endif

#endif

ENTRY(memsetup)    //memsetup子程序
 @ initialise the static memory

 @ set memory control registers
 mov r1, #MEM_CTL_BASE  //memory controller
 adrl r2, mem_cfg_val
 add r3, r1, #52
1: ldr r4, [r2], #4
 str r4, [r1], #4
 cmp r1, r3
 bne 1b

 mov pc, lr    //这里返回了么?
  注意注意:这里memsetup已经返回了,下面是独立的子程序了呢

51 c8051f(f020,f040) msp430 arm(2410,2510) fpga(xc3s4000) dsp(5116 dm642) keilc vc++ matlab linux protel Ten_layerPCB mpegx h.26x Rscode Turbocode ofdm VideoBroadcasting ldpc_code(now!)

#ifdef CONFIG_S3C2410_NAND_BOOT  // NAND如此,NOR应该如何处理呢?
@      //不需要copy vivi to ram?????
@ copy_myself: copy vivi to ram
@
copy_myself:
 mov r10, lr

 @ reset NAND
 mov r1, #NAND_CTL_BASE
 ldr r2, =0xf830  @ initial value
 str r2, [r1, #oNFCONF]
 ldr r2, [r1, #oNFCONF]
 bic r2, r2, #0x800  @ enable chip
 str r2, [r1, #oNFCONF]
 mov r2, #0xff  @ RESET command
 strb r2, [r1, #oNFCMD]
 mov r3, #0   @ wait
1: add r3, r3, #0x1
 cmp r3, #0xa
 blt 1b
2: ldr r2, [r1, #oNFSTAT] @ wait ready
 tst r2, #0x1
 beq 2b
 ldr r2, [r1, #oNFCONF]
 orr r2, r2, #0x800  @ disable chip
 str r2, [r1, #oNFCONF]

 @ get read to call C functions (for nand_read())
 ldr sp, DW_STACK_START @ setup stack pointer
 mov fp, #0   @ no previous frame, so fp=0

 @ copy vivi to RAM
 ldr r0, =VIVI_RAM_BASE  //(DRAM_BASE + DRAM_SIZE - VIVI_RAM_SIZE)
   //0x33f00000
 mov     r1, #0x0   //start address, now vivi is in steppingstone
 mov r2, #0x20000    //128k ?
 bl nand_read_ll 
// nand_read_ll(unsigned char *buf, unsigned long start_addr, int size)
// ro = buf , r1 =start , size = r2=128k ??yeah ? 要这么多干吗?
 tst r0, #0x0   //返回值在r0中
 beq ok_nand_read   //nand_read_ll()顺利返回
#ifdef CONFIG_DEBUG_LL
bad_nand_read:
 ldr r0, STR_FAIL
 ldr r1, SerBase
 bl rintWord
1: b 1b  @ infinite loop
#endif
 
ok_nand_read:
#ifdef CONFIG_DEBUG_LL
 ldr r0, STR_OK
 ldr r1, SerBase
 bl rintWord
#endif

 @ verify
 mov r0, #0   //flash start add? no. 是用NAND启动时,从NAND copy到phy add=0的4Kbytes SRAM(stepping stone)中的vivi
 ldr r1, =0x33f00000 //VIVI_RAM_BASE
// VIVI_RAM_BASE = (DRAM_BASE + DRAM_SIZE(64M) - VIVI_RAM_SIZE)
//= 0x3000_0000+0x0400_0000-0x0010_0000 = 0x33f0_0000
 mov r2, #0x400 @ 4 bytes * 1024 = 4K-bytes //移4K去VIVI_RAM_BASE ?NO   比较4Kbytes? YES
go_next:
 ldr r3, [r0], #4  //将[r0]指向的数据(32bit?)放进r3 ,然后r0+4->r0
 ldr r4, [r1], #4  //将[r1]指向的数据(32bit?)放进r4 ,然后r1+4->r1
 teq r3, r4   //比较r3/r4的大小
 bne notmatch  //出现不匹配的情况
 subs r2, r2, #4  //r2-4 ->r2, if subs 结果为0,flagZ==1
 beq done_nand_read //若r2==0条件(flagZ==1)成立,跳到done_nand_read
 bne go_next  //flagZ==0; 说明r2!=0
notmatch:
#ifdef CONFIG_DEBUG_LL
 sub r0, r0, #4
 ldr r1, SerBase
 bl rintHexWord
 ldr r0, STR_FAIL
 ldr r1, SerBase
 bl rintWord
#endif
1: b 1b   //匹配与否,不匹配时去哪里了?
done_nand_read:

#ifdef CONFIG_DEBUG_LL
 ldr r0, STR_OK
 ldr r1, SerBase
 bl rintWord
#endif

 mov pc, r10   //返回罗,在函数入口处mov r10, lr

51 c8051f(f020,f040) msp430 arm(2410,2510) fpga(xc3s4000) dsp(5116 dm642) keilc vc++ matlab linux protel Ten_layerPCB mpegx h.26x Rscode Turbocode ofdm VideoBroadcasting ldpc_code(now!)

@ clear memory
@ r0: start address
@ r1: length
mem_clear:
 mov r2, #0
 mov r3, r2
 mov r4, r2
 mov r5, r2
 mov r6, r2
 mov r7, r2
 mov r8, r2
 mov r9, r2

clear_loop:
 stmia r0!, {r2-r9}
 subs r1, r1, #(8 * 4)
 bne clear_loop

 mov pc, lr

#endif @ CONFIG_S3C2410_NAND_BOOT


#ifdef CONFIG_BOOTUP_MEMTEST // Low Level Hardware Debugging Enable simple memory test  //vivi未用
@
@ Simple memory test function
@
memtest:
 mov r10, lr

#ifdef CONFIG_DEBUG_LL  //low level debugging,往serport上面捣腾信息
 mov r0, #'M'
 ldr r1, SerBase
 bl rintChar
 mov r0, #'T'
 ldr r1, SerBase
 bl rintChar
 mov r0, #'S'
 ldr r1, SerBase
 bl rintChar
 mov r0, #'T'
 ldr r1, SerBase
 bl rintChar
 mov r0, #' '
 ldr r1, SerBase
 bl rintChar
#endif

        /* check the first 1MB  in increments of 4k */ //循环测试1M得SDRAM//我们应该改大点
        mov     r7, #0x1000
        mov     r6, r7, lsl #8  /* 4k << 2^8 = 1MB */
        mov     r5, #DRAM_BASE  
//DRAM_BASE =DRAM_BASE0= 0x30000000      /* base address of dram bank 0 */

mem_test_loop:
        mov     r0, r5 
        bl      testram_nostack
        teq     r0, #1 
        beq     badram 

        add     r5, r5, r7
        subs    r6, r6, r7
        bne     mem_test_loop


 @ the first megabyte is OK. so let us clear it.
        mov     r0, #((1024 * 1024) / (8 * 4)) @ 1MB in steps of 32 bytes
        mov     r1, #DRAM_BASE
        mov     r2, #0 
        mov     r3, #0 
        mov     r4, #0 
        mov     r5, #0 
        mov     r6, #0 
        mov     r7, #0 
        mov     r8, #0 
        mov     r9, #0 

51 c8051f(f020,f040) msp430 arm(2410,2510) fpga(xc3s4000) dsp(5116 dm642) keilc vc++ matlab linux protel Ten_layerPCB mpegx h.26x Rscode Turbocode ofdm VideoBroadcasting ldpc_code(now!)


clear_loop_memtest:
        stmia   r1!, {r2-r9}
        subs    r0, r0, #(8 * 4)
        bne     clear_loop_memtest

#ifdef CONFIG_DEBUG_LL
 ldr r0, STR_OK
 ldr r1, SerBase
 bl rintWord
#endif

 mov pc, r10  @ return    memtest return

badram:
#ifdef CONFIG_DEBUG_LL
 ldr r0, STR_FAIL
 ldr r1, SerBase
 bl rintWord
#endif
1: b 1b  @ loop   有坏// 死循环?


@ testmem.S: memory tester, test if there is RAM available at given location
@ //called by memtest
@ Copyright (C) 2001 Russell King (rmk@arm.linux.org.uk)
@
@ This version clobbers registers r1-r4, so be sure to store their contents
@ in a safe position. This function is not APCS compliant, so only use it
@ from assembly code.
@
@ r0 = address to test
@ returns r0 = 0 - ram present, r0 = 1 - no ram
@ clobbers r1 - r4
ENTRY(testram_nostack)
        ldmia   r0, {r1, r2}    @ store current value in r1 and r2
        mov     r3, #0x55       @ write 0x55 to first word
        mov     r4, #0xaa       @ 0xaa to second
        stmia   r0, {r3, r4}
        ldmia   r0, {r3, r4}    @ read it back
        teq     r3, #0x55       @ do the values match
        teqeq   r4, #0xaa
        bne     bad             @ oops, no
        mov     r3, #0xaa       @ write 0xaa to first word
        mov     r4, #0x55       @ 0x55 to second
        stmia   r0, {r3, r4}
        ldmia   r0, {r3, r4}    @ read it back
        teq     r3, #0xaa       @ do the values match
        teqeq   r4, #0x55
bad:    stmia   r0, {r1, r2}    @ in any case, restore old data
        moveq   r0, #0          @ ok - all values matched
        movne   r0, #1          @ no ram at this location
        mov     pc, lr

#endif @ CONFIG_BOOTUP_MEMTEST

@ Initialize UART
@
@ r0 = number of UART port

//SerBase  = UART0_CTL_BASE = UART_CTL_BASE =0x50000000
// 0x5000_0000 UART channel 0 line control register


InitUART:
 ldr r1, SerBase   //0x5000_0000
 mov r2, #0x0
 str r2, [r1, #oUFCON]  //清零 oUFCON
//#define oUFCON   0x08 /* R/W, UART FIFO control register */
// UFCON0 0x50000008 R/W UART channel 0 FIFO control register
Tx FIFO Trigger Level [7:6] : 00 : Empty
Rx FIFO Trigger Level [5:4]: 00 : 00 = 4-byte
Tx FIFO Reset [2] : 0 = Normal
Rx FIFO Reset [1] : 0 = Normal
FIFO Enable [0] 0 = Disable
 str r2, [r1, #oUMCON] //  * (0x5000_0000 + oUMCON (0x0C) ) = 0;
// UART modem control register
//  Auto Flow Control (AFC) [4] 0 = Disable
// Request to Send [0] = 0; // If AFC bit is enabled, this value will be ignored. In this case
the S3C2410A will control nRTS automatically. If AFC bit is disabled, nRTS must be controlled by software.  0 = 'H' level (Inactivate nRTS)   1 = 'L' level (Activate nRTS)

 mov r2, #0x3  //
 str r2, [r1, #oULCON] //* (0x5000_0000 + oULCON(0x00) ) = 0x3 =0011B
// UART line control register      8N1
// Infra-Red Mode [6] : 0 : 0 = Normal mode operation
// Parity Mode [5:3] : 000 = 0xx = No parity
// Number of Stop Bit [2] : 0 : 0 = One stop bit per frame
// Word Length [1:0] : 11 = 8-bit

51 c8051f(f020,f040) msp430 arm(2410,2510) fpga(xc3s4000) dsp(5116 dm642) keilc vc++ matlab linux protel Ten_layerPCB mpegx h.26x Rscode Turbocode ofdm VideoBroadcasting ldpc_code(now!)

bootloader stag1后续部分的程序解释,有点长,打包发上来.

b0AjXh55.rar (2.26 KB)
51 c8051f(f020,f040) msp430 arm(2410,2510) fpga(xc3s4000) dsp(5116 dm642) keilc vc++ matlab linux protel Ten_layerPCB mpegx h.26x Rscode Turbocode ofdm VideoBroadcasting ldpc_code(now!)

下面是设置 ATAG_RAMDISK 的示例代码,它告诉内核解压后的 Ramdisk 有多大(单位是KB):


params->hdr.tag = ATAG_RAMDISK;
params->hdr.size = tag_size(tag_ramdisk);
 
params->u.ramdisk.start = 0;
params->u.ramdisk.size = RAMDISK_SIZE; /* 请注意,单位是KB */
params->u.ramdisk.flags = 1; /* automatically load ramdisk */
 
params = tag_next(params);

 


最后,设置 ATAG_NONE 标记,结束整个启动参数列表:


static void setup_end_tag(void)
{
 params->hdr.tag = ATAG_NONE;
 params->hdr.size = 0;
}

 


1.2.5 调用内核

Boot Loader 调用 Linux 内核的方法是直接跳转到内核的第一条指令处,也即直接跳转到 MEM_START+0x8000 地址处。在跳转时,下列条件要满足:

1. CPU 寄存器的设置:

R0=0;


R1=机器类型 ID;关于 Machine Type Number,可以参见 linux/arch/arm/tools/mach-types。


R2=启动参数标记列表在 RAM 中起始基地址;


2. CPU 模式:

必须禁止中断(IRQs和FIQs);


CPU 必须 SVC 模式;


3. Cache 和 MMU 的设置:

MMU 必须关闭;


指令 Cache 可以打开也可以关闭;


数据 Cache 必须关闭;
如果用 C 语言,可以像下列示例代码这样来调用内核:


void (*theKernel)(int zero, int arch, u32 params_addr) = (void (*)(int, int, u32))KERNEL_RAM_BASE;
……
theKernel(0, ARCH_NUMBER, (u32) kernel_params_start);

 


注意,theKernel()函数调用应该永远不返回的。如果这个调用返回,则说明出错。

51 c8051f(f020,f040) msp430 arm(2410,2510) fpga(xc3s4000) dsp(5116 dm642) keilc vc++ matlab linux protel Ten_layerPCB mpegx h.26x Rscode Turbocode ofdm VideoBroadcasting ldpc_code(now!)
kankan


Stage2在VIVI中的具体体现
Vivi\init\main.c

int main(int argc, char *argv[])
{
/* * Step 1:   NB:  MMU. */
/* 硬件上按下某个key之后,clear_mem(USER_RAM_BASE(),USER_RAM_SIZE);
// clear_mem()空的呢   */
 reset_handler();

/*  Step 2:  Board甫 檬扁拳 钦聪促. */
// init_time(), set_gpios(setting GPIO  registers)
 ret = board_init();

/* Step 3:*   4G甫 府聪绢(linear)窍霸  *   MMU甫 难技夸.  */
// 
 mem_map_init();

void mem_map_init(void)
{
#ifdef CONFIG_S3C2410_NAND_BOOT
 mem_map_nand_boot();
//mem_mapping_linear-> 将4G空间线性映射成4K个1M空间,并将其中的有效的DRAM做成cacheable
#else
 mem_map_nor();
//  copy_vivi_to_ram(); //?memcpy(VIVI_RAM_BASE, VIVI_ROM_BASE, VIVI_RAM_SIZE); NOR在这里将vivi从nor flash copy到ram中 VIVI_RAM_BASE= (DRAM_BASE + DRAM_SIZE - VIVI_RAM_SIZE), VIVI_ROM_BASE=0x0, VIVI_RAM_SIZE=SZ_1M  //oho,比NAND在stage 1中copy 128K还多呢 ,1M可是把kernel也给copy到DRAM中了,可惜位置太高,在VIVI_RAM中,不在boot_mem_base+KERNEL_OFFSET,所以后面还会再copy一次的
// mem_mapping_linear();-> 将4G空间线性映射成4K个1M空间,并将其中的DRAM做成cacheable
// nor_flash_mapping();?将FLASH_BASE和FLASH_UNCACHED_BASE 都映射到FLASH_BASE
 //0x0 ?0x0 ,cacheable ,  0x1000_0000 -> 0x0 , uncacheable   SIZE为FLASH_SIZE
// nor_flash_remapping();->"Map flash virtual section to DRAM at  VIVI_RAM_BASE; 
//  *(mmu_tlb_base + (VIVI_ROM_BASE >> 20)) =  (VIVI_RAM_BASE | MMU_SECDESC | MMU_CACHEABLE);  
 //0x0 ? vivi_ram_base , cacheable  SIZE为1M = MMU_SECTION_SIZE = VIVI RAM的大小
#endif
 cache_clean_invalidate();
//clean and invalidate all cache lines
 tlb_invalidate();
//Invalidate all TLB entries
}

 mmu_init();

//  /* Invalidate caches */ /* Load page table pointer */ /* Write domain id (cp15_r3) */
 /* Set control register v4 */ /* Clear out 'unwanted' bits (then put them in if we need them) */
 /* Turn on what we want */ /* Fault checking enabled */
#ifdef CONFIG_CPU_D_CACHE_ON enable Data cache
#ifdef CONFIG_CPU_I_CACHE_ON  enable Instruction cache
 /* MMU enabled */

 / * Now, vivi is running on the ram. MMU is enabled.
  */

 /*  Step 4: initialize the heap area*/
 ret = heap_init();

//malloc_init(): initialize heap area at (VIVI_RAM_BASE - HEAP_SIZE), size = HEAP_SIZE (SZ_1M) 这一段为virtual-phy是linear的 //除了NOR中的0x0->VIVI_RAM_BASE, FLASH_UNCACED_BASE(0x1000_0000) ? 0x0

 /* Step 5:   MTD狼 颇萍记partition 沥焊啊   *. */
 ret = mtd_dev_init();

//  mtd_init(); 初始化不同的MTD根据 CONFIG_MTD_xxx, and CONFIG_S3C2410_AMD_BOOT, INTEL_BOOT
//注意,这里的初始化 跟MTD partition上面的文件系统没有上面关系
//intel_init()其实就是jedec_init
#ifdef CONFIG_MTD_CFI
 add_command(&flash_cmd);
#endif

 /* Step 6:.  */
 init_priv_data();

#ifdef CONFIG_PARSE_PRIV_DATA:
  vivi will be able to get MTD partition information from MTD.
 #else, vivi will use default parameters in the vivi's code.
init_priv_data(void)
{
 ret_def = get_default_priv_data();
//get_default_param_tlb() 
->cp smdk.c中default_vivi_parameter to VIVI_PRIV_RAM_BASE+PARAMETER_TLB_OFFSET (virt=phy in this area)
//such as mach_type, media_type,boot_mem_base,baudrate, Xmodem, boot_delay
//get_default_linux_cmd())
->cp smdk.c中char linux_cmd[] = "noinitrd root=/dev/bon/2 init=/linuxrc console=ttyS0";
to (VIVI_PRIV_RAM_BASE + LINUX_CMD_OFFSET)
//get_default_mtd_partition()
->smdk中的default_mtd_partitions to VIVI_PRIV_RAM_BASE + MTD_PART_OFFSET
 ret_saved = load_saved_priv_data();
//将以前saved param partition 从DRAM_BASE+xxx_OFFSET复制到VIVI_PRIV_RAM_BASE+xxx_OFFSET
 //缺省用saved,  上此saved参数
}


 /* Step 7: */

 misc();
// add_command(&cpu_cmd);
/* add user command ; cpu_cmd可以  Display cpu information and Change cpu clock and bus clock\n");
 init_builtin_cmds();
/* Register basic user commands */

 /* Step 8: */
 boot_or_vivi();
//等timeout(可以在default_parameter boot_delay中设定,可以用boot_delay命令设定,可以。。无关紧要)
//有key按下就vivi,否则就run_autoboot()
//?”boot” ?command_boot()?
// media_type = get_param_value("media_type", &ret);//default = MT_S3C2410->NAND or NOR
//从*vivi_params = (VIVI_PRIV_RAM_BASE + PARAMETER_TLB_OFFSET + 16);取参数(def or saved)
 kernel_part = get_mtd_partition("kernel"); //default=
// mtd_parts = (mtd_partition_t *)(VIVI_PRIV_RAM_BASE + MTD_PART_OFFSET + 16);取参数(def or saved)
 from = kernel_part->offset; // default smdk.c中default_mtd_partitions中offset=0x30000
 size = kernel_part->size; // default smdk.c中default_mtd_partitions中size=0xC0000

 boot_kernel(from, size, media_type);//def: boot_kernel(0x30000,0xC0000,NAND/NOR);
 
 return 0;
}
main()到此结束

51 c8051f(f020,f040) msp430 arm(2410,2510) fpga(xc3s4000) dsp(5116 dm642) keilc vc++ matlab linux protel Ten_layerPCB mpegx h.26x Rscode Turbocode ofdm VideoBroadcasting ldpc_code(now!)

int boot_kernel(ulong from, size_t size, int media_type)
{
 int ret;
 ulong boot_mem_base; /* base address of bootable memory ,vitual = phy add */
 ulong to;
 ulong mach_type;

 boot_mem_base = get_param_value("boot_mem_base", &ret);
//default: 0x3000_0000; vir = linear
 if (ret) {
  printk("Can't get base address of bootable memory\n");
  printk("Get default DRAM address. (0x%08lx\n", DRAM_BASE);
  boot_mem_base = DRAM_BASE;
 }

 /* copy kerne image */
 to = boot_mem_base + LINUX_KERNEL_OFFSET; //0x3000_8000 的说
//copy_vivi_to_ram中已经把kernel copy到了VIVI_RAM,浪费的说

 ret = copy_kernel_img(to, (char *)from, size, media_type);
//   case MT_NOR_FLASH:
//   memcpy((char *)to, (from + FLASH_UNCACHED_BASE), size);
// virtual add FLASH_UNCACHED_BASE 对应phy add 0x0,又从flash里面copy一次kernel 到to
//  case MT_SMC_S3C2410: //按NAND的规矩copy
//   ret = nand_read_ll((unsigned char *)dst,  (unsigned long)src, (int)size);

 /* 检查是否是compressed linux kernel image */
 if (*(ulong *)(to + 9*4) != LINUX_ZIMAGE_MAGIC) {
  printk("Warning: this binary is not compressed linux kernel image\n");
  printk("zImage magic = 0x%08lx\n", *(ulong *)(to + 9*4));
 } else {
  printk("zImage magic = 0x%08lx\n", *(ulong *)(to + 9*4));
 }

 /* Setup linux parameters and linux command line */
 setup_linux_param(boot_mem_base + LINUX_PARAM_OFFSET); 0x3000_0100
// LINUX_PARAM_OFFSET 0x100
//  params->u1.s.page_size = LINUX_PAGE_SIZE  ;  = SZ_4K
// params->u1.s.nr_pages = (DRAM_SIZE >> LINUX_PAGE_SHIFT); //SZ_4K =12bit =LINUX_PAGE_SHIFT
//加点什么for NOR?
// /* set linux command line */
// linux_cmd = get_linux_cmd_line(); -> char *linux_cmd_line = (char *)(VIVI_PRIV_RAM_BASE + LINUX_CMD_OFFSET + 8);
// if (linux_cmd == NULL) {  //default = smdk.c中char linux_cmd[] = "noinitrd root=/dev/bon/2 init=/linuxrc console=ttyS0";
//  printk("Wrong magic: could not found linux command line\n");
// } else {
//  memcpy(params->commandline, linux_cmd, strlen(linux_cmd) + 1);
//  printk("linux command line is: \"%s\"\n", linux_cmd);
// }

 /* Get machine type */
 mach_type = get_param_value("mach_type", &ret); //default :smdk2410.h  MACH_TYPE 193 
 printk("MACH_TYPE = %d\n", mach_type);

 /* Go Go Go */
 printk("NOW, Booting Linux......\n");
 call_linux(0, mach_type, to);
// void  call_linux(long a0, long a1, long a2)
/* r0 = must contain a zero or else the kernel loops
 * r1 = architecture type
 * r2 = address to be executed */
//{
// cache_clean_invalidate();
// tlb_invalidate();

__asm__(
 "mov r0, %0\n"   // a0
 "mov r1, %1\n"   // a1
 "mov r2, %2\n"   // a2
 "mov ip, #0\n"
 "mcr p15, 0, ip, c13, c0, 0\n" /* zero PID */
 "mcr p15, 0, ip, c7, c7, 0\n" /* invalidate I,D caches */
 "mcr p15, 0, ip, c7, c10, 4\n" /* drain write buffer */
 "mcr p15, 0, ip, c8, c7, 0\n" /* invalidate I,D TLBs */
 "mrc p15, 0, ip, c1, c0, 0\n" /* get control register */
 "bic ip, ip, #0x0001\n"  /* disable MMU */
 "mcr p15, 0, ip, c1, c0, 0\n" /* write control register */
 "mov pc, r2\n"
 "nop\n"
 "nop\n"
 : /* no outpus */
 : "r" (a0), "r" (a1), "r" (a2)
 );
}
 return 0; 
}

51 c8051f(f020,f040) msp430 arm(2410,2510) fpga(xc3s4000) dsp(5116 dm642) keilc vc++ matlab linux protel Ten_layerPCB mpegx h.26x Rscode Turbocode ofdm VideoBroadcasting ldpc_code(now!)
返回列表