




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、第六章 Bootloader,一个嵌入式 Linux 系统从软件的角度看通常可以分为五个层次:,引导加载程序 包括固化在固件中的 boot 代码(可选),和 Bootloader 两大部分。 2. Linux 内核 特定于嵌入式板子的定制内核(包括驱动程序)以及内核的 启动参数。 3. 文件系统 建立于 Flash 设备之上文件系统。 4.驱动程序编写(移植) 5. 用户应用程序 特定于用户的应用程序。有时在用户应用程序和内核层之间 可能还会包括一个嵌入式图形用户界面。常用的嵌入式 GUI 有:QT 和 MiniGUI 。,固态存储设备的典型空间分配结构,引导加载程序是系统加电后运行的第一段软
2、件代码。 在 PC 机中: PC 机中的引导加载程序由 BIOS(其本质就是一段固 件程序)和位于硬盘中的OS Boot Loader(比如,LILO 和 GRUB 等)一起组成。 BIOS: 在完成硬件检测和资源分配后,将硬盘中的 Boot Loader 读到系统的 RAM 中,然后将控制权交给 OS Boot Loader。 Boot Loader: 主要运行任务就是将内核映象从硬盘上读到 RAM 中,然后跳转到内核的入口点去运行,也即开始启动 操作系统。,在嵌入式系统中: 通常并没有像 BIOS 那样的固件程序 (有的嵌入式 CPU 也会内嵌一段短小的启动程序) ,因此整个系统的加载启动
3、任务就完全由 Boot Loader 来完成。 比如在一个基于 ARM7TDMI core 的嵌入式系统 中,系统在上电或复位时通常都从地址 0 x00000000 处 开始执行,而在这个地址处安排的通常就是系统的 Boot Loader 程序。,系统加电或复位后,所有的处理器通常都从某个预先 安排的地址上取指令。 比如,ARM在复位时从地址0 x0取指。 嵌入式系统中通常都有某种类型的固态存储设备(比 如:ROM、EEPROM 或FLASH 等)被映射到这个预先安 排的地址上。因此在系统加电后,处理器将首先执行 Bootloader 程序。 Bootloader是最先被系统执行的程序。,每种
4、不同的 CPU 体系结构都有不同的 Boot Loader。 有些 Boot Loader 也支持多种体系结构的 CPU,比如 U-Boot 就同时支持 ARM 体系结构和MIPS体系结构。 Boot Loader 实际上也依赖于具体的嵌入式板级 设备的配置。对于两块不同的嵌入式板而言,即使 它们是基于同一种 CPU 而构建的,要想让运行在一 块板子上的 Boot Loader 程序也能运行在另一块 板子上,通常也都需要修改 Boot Loader 的源程序。,一、 bootloader的基本概念 1、什么是bootloader(引导加载程序) 简单地说BootLoader就是在操作系统内核或
5、用户应用程序运行之前运行的一段小程序。通过这段小程序,我们可以初始化硬件设备、建立内存空间的映射图(有的CPU没有内存映射功能如S3C44B0),从而将系统的软硬件环境带到一个合适的状态,以便为最终调用操作系统内核或用户应用程序准备好正确的环境。,2、 BootLoader的操作模式 多数BootLoader都包含两种不同的操作模式。“启动加载”模式和“下载”模式,这种区别仅对于开发人员才有意义。但从最终用户的角度看,BootLoader的作用就是用来加载操作系统,而并不存在所谓的启动加载模式与下载工作模式的区别。 启动加载(Boot loading)模式:这种模式也称为“自主”(Autono
6、mous)模式,也即BootLoader从目标机上的某个固态存储设备上将操作系统加载到RAM中运行,整个过程并没有用户的介入。这种模式是BootLoader的正常工作模式。因此在嵌入式产品发布的时候, BootLoader显然必须工作在这种模式下。,下载(Down loading)模式: 在这种模式下 目标机上的BootLoader 将通过串口连接或网络连接等通信手段从主机下载文件,比如:下载应用程序、数据文件、内核映像等。从主机下载的文件通常首先被BootLoader保存到目标机的RAM中,然后再被BootLoader写到目标机上的固态存储设备中。BootLoader的这种模式通常在系统更新
7、时使用。工作于这种模式下的 BootLoader通常都会向它的终端用户提供一个简单的命令行接口。,最常见的情况是,目标机上的BootLoader通过串口与主机之间进行文件传输,传输协议通常xmodem/ymodem/zmodem协议中的一种。但是,由于串口传输的速度是有限的,因此通过以太网连接并借助TFTP协议来下载文件是个更好的选择。但是,在通过以太网连接和TFTP协议来下载文件时,因为主机方必须有一个软件用来的提供TFTP服务,所以操作相对复杂。,3、BootLoader的启动过程 由于 Boot Loader 的实现依赖于 CPU 的体系结构,因此大多数 Boot Loader 的启动都
8、分为 stage1 和 stage2两大部分。依赖于 CPU 体系结构的代码,比如设备初始化代码等,通常都放在 stage1中,而且通常都用汇编语言来实现,以达到短小精悍的目的。而 stage2则通常用C语言来实现,这样可以实现更复杂的功能,而且代码会具有更好的可读性和可移植性。,通常包括以下步骤(以执行的先后顺序): 硬件设备初始化 1 屏蔽所有的中断。为中断提供服务通常是 OS 设备驱动程序的责任,因此在 Boot Loader 的执行全过程中可以不必响应任何中断。中断屏蔽可以通过写 CPU 的中断屏蔽寄存器或状态寄存器(比如 ARM 的 CPSR 寄存器)来完成。 2 设置 CPU 的速
9、度和时钟频率。 3 RAM 初始化。包括正确地设置系统的内存控制器的功能寄存器以及各内存库控制寄存器等。 4 初始化 LED。典型地,通过 GPIO 来驱动 LED,其目的是表明系统的状态是 OK 还是 Error。如果板子上没有 LED,那么也可以通过初始化 UART 向串口打印 Boot Loader 的 Logo 字符信息来完成这一点。 5 关闭 CPU 内部指令数据 cache。,Boot Loader 的 stage1,为加载 Boot Loader 的 stage2 准备 RAM 空间。 为了获得更快的执行速度,通常把 stage2 加载到 RAM 空间中来执行,因此必须为加载 B
10、oot Loader 的 stage2 准备好一段可用的 RAM 空间范围。 空间大小最好是 memory page 大小(通常是4KB)的倍数。一般而言,1M 的 RAM 空间已经足够了。具体的地址范围可以任意安排。 比如 blob 就将它的 stage2 可执行映像安排到从系统 RAM 起始地址 0 xc0200000 开始的 1M 空间内执行。 拷贝 Boot Loader 的 stage2 到 RAM 空间中。 拷贝时要确定两点:(1) stage2 的可执行映象在固态存储设备的存放起始地址和终止地址;(2) RAM 空间的起始地址。 设置好堆栈。 堆栈指针的设置是为了执行 C 语言代
11、码作好准备。 通常我们可以把 sp 的值设置为(stage2_end-4),也即在前面所安排的那个1MB的RAM 空间的最顶端(堆栈向下生长)。 此外,在设置堆栈指针 sp 之前,也可以关闭 LED ,以提示用户准备跳转到 stage2。,跳转到 stage2 的 C 入口点。 在上述一切都就绪后,就可以跳转到 Boot Loader 的 stage2 去执行了。在ARM系统中,可以通过修改PC寄存器为合适的地址来实现。,比如,用以下代码来实现两个阶段的交接工作。 如(U-Boot中可以用以下两行代码实现) ldr pc _start_armboot _start_armboot:.word
12、start_armboot 在vivi中是通过以下实现的: bl main mov pc,#FLASH_BASE,stage2 的代码通常用 C 语言来实现,以便于实现更复杂的功能和取得更好的代码可读性和可移植性。 Boot Loader 的 stage2 通常包括以下步骤(以执行的先后顺序): 初始化本阶段要使用到的硬件设备。 通常包括:(1)初始化至少一个串口,以便和终端用户进行 I/O 输出信息;(2)初始化计时器等。设备初始化完成后,可以输出一些打印信息,程序名字字符串、版本号等。 检测系统内存映射(memory map)。 所谓内存映射就是指在整个 4GB 物理地址空间中有哪些地址范
13、围被分配用来寻址系统的 RAM 单元。比如,在 SA-1100 CPU 中,从0 xC000,0000 开始的 512M 地址空间被用作系统的 RAM 地址空间,而在 Samsung S3C44B0X CPU 中,从0 x0c00,0000 到 0 x1000,0000 之间的 64M 地址空间被用作系统的 RAM 地址空间。,Bootloader 的stage2,将 kernel 映像和根文件系统映像从 flash 上读到 RAM 空间中。 (1)规划内存占用的布局这里包括两个方面:内核映像所占用的内存范围;根文件系统所占用的内存范围。在规划内存占用的布局时,主要考虑基地址和映像的大小两个方
14、面。 (2)从 Flash 上拷贝数据 为内核设置启动参数。 在将内核映像和根文件系统映像拷贝到 RAM 空间中后,就可以准备启动 Linux 内核了。但是在调用内核之前,应该作一步准备工作,即:设置 Linux 内核的启动参数。 调用内核。,VIVI简介,VIVI是韩国Mizi公司开发的BootLoader,可用于ARM9处理器的引导。VIVI利用串行通信为用户提供接口。为连接VIVI,首先利用串口电缆连接宿主机和目标板,然后在主机上运行串口通信程序(minicom),并在目标板上正确设置VIVI以支持串口。正确连接后,就可以由串口通信程序显示提示信息,提示信息的最后一行如下所示: Pres
15、s Return to start the LINUX now, any other key for vivi. VIVI也有前面说过的两种工作模式,启动模式可以在一段时间后自行启动Linux内核,这是VIVI的默认方式。出现上述信息后,如果按除回车键外的任意键,即可进入下载模式,出现“vivi”提示符。在下载模式下,VIVI为用户提供了一个命令行接口,通过该接口可以使用VIVI提供的一些命令。,二、常用BootLoader代码分析,1load命令 将二进制文件载入到Flash或者RAM,命令格式: load | 其中命令行参数描述装载位置,有flash和ram两种选项;参数 或 描述装载的地
16、址,如果有提前定义的MTD分区信息,可以只输入分区名称,否则需要指定地址和大小;参数 确定文件的传输协议,常采用的选项“x”用来指定采用xmodem协议。 例如:vivi load flash kernel x,装载压缩映像文件zImage到flash存储器中,地址是kernel分区,并采用xmodem传输协议。 也可以指定地址和大小,例如:vivi load flash 0 x80000 0 xc0000 x。,2part命令 操作MTD分区信息,比如,显示、增加、删除、复位、保存MTD分区等。 part show:显示mtd分区信息。 part add :增加新的mtd分区,其中为新mtd
17、分区名称,是mtd器件的偏移,表示mtd分区的大小,表示分区类型,可选项有JFFS2、LOCKED和BONFS。 part del :删除一个mtd分区。 part reset:恢复mtd 分区为默认值。 part save:在flash中永久保存参数值和分区信息。 3param命令 用来设置或者察看参数。例如:改变“linux command line”,使用 vivi param set linux_cmd_line “you wish.”。 也可以改变引导程序启动的时间,使用vivi param set boot_delay 100000实现。,4boot命令 用来引导存储在flash存
18、储器或者RAM中的linux内核。命令格式: boot | 参数 设定存储linux内核映像的位置,可选项有ram、nor和smc。 参数 或 描述存储内核的地址,如果有提前定义的MTD分区信息,可以只输入分区名称,否则需要指定地址和大小。 例如:vivi boot nor 0 x80000表示从flash存储器中读出linux内核,偏移是0 x80000。 5flash命令 存储器管理命令,例如:flash erase | ,表示擦除flash存储器。,VIVI的代码分析与移植,1arch 此目录包括了所有VIVI支持的目标板的子目录,例如s3c2410目录 。 2Documentation
19、 存放了许多文档,非常详细,主要是VIVI的使用指南。 3drivers 其中包括了引导内核所需的MTD设备(mtd)和串口驱动程序(serial)。MTD目录下分maps、nand和nor三个目录,实现对Nand Flash和Nor Flash的读写控制。Serial目录下的文件实现对串口的控制,并支持xmodem和ymodem协议。 4include 头文件的公共目录,其中的S3C2410.h定义了处理器的一些寄存器,以及NAND Flash的一些寄存器等。Platform/smdk2410.h定义了与目标板相关的资源配置参数,修改波特率、引导参数和物理内存映射等参数就可适用于自己的目标板
20、。,5Init 这个目录只有main.c和version.c两个文件。与普通的C程序一样,VIVI将从main函数开始执行。 6Lib 一些平台公共的接口代码,比如,time.c里的udelay()和mdelay()。 7scripts 主要在配置时用到,存放了配置所需的脚本文件,如Menuconfig和Configure文件,以方便对VIVI的配置。,VIVI的运行也可以分为两个阶段。 在第一阶段完成含有依赖于CPU体系结构硬件初始化的代码,利用汇编语言完成。 第二阶段是用C语言完成的。在跳转进main()函数之前,利用汇编语言编写了一段trampoline程序(弹簧床)作为阶段2可执行镜像
21、的执行入口点。之后可以在trampoline中用处理器的跳转指令进入main()函数中去执行。 当main()函数返回时,CPU就进行复位。,vivi的第一阶段,完成含依赖于CPU的体系结构硬件初始化的代码,包括禁止中断、初始化串口、复制自身到RAM等。相关代码集中在head.S(viviarchs3c2410目录下):,Head.S: #include config.h #include linkage.h #include machine.h Start of executable code ENTRY(_start) ;程序入口点 ENTRY(ResetEntryPoint) , Exc
22、eption vector table (physical address = 0 x00000000) ;异常向量表物理地址 0 x00: Reset ;复位异常 b Reset 0 x04: Undefined instruction exception ;未定义的指令异常 UndefEntryPoint: b HandleUndef 0 x08: Software interrupt exception ;软件中断异常,SWIEntryPoint: b HandleSWI 0 x0c: Prefetch Abort (Instruction Fetch Memory Abort) ;内存
23、操作异常 PrefetchAbortEnteryPoint: b HandlePrefetchAbort 0 x10: Data Access Memory Abort ;数据异常 DataAbortEntryPoint: b HandleDataAbort 0 x14: Not used ;未使用 NotUsedEntryPoint: b HandleNotUsed 0 x18: IRQ(Interrupt Request) exception ;普通中断异常,IRQEntryPoint: b HandleIRQ 0 x1c: FIQ(Fast Interrupt Request) exce
24、ption ;快速中断异常 FIQEntryPoint: b HandleFIQ VIVI magics ,ARM规定,在起始必须有8条跳转指令,可以用b,也可以用ldr pc,文件名。这样的8条规则的标志被arm定义为bootloader的识别标志,检测到这样的标志后,就可以从该位置启动。这样的做法是因为开始的时候不一定有bootloader,必须有一种识别机制,如果识别到bootloader,那么就从bootloader启动。,下面是固定位置存放环境变量 对vivi的这些magic number,虽然设计在这里,不过大部分没有使用。其中0 x20和0 x24没有使用,在0 x2C处,设计了
25、一个magic number,组成的格式如下:bit31:24为platform,bit23:16为cpu type,bit15:0为machine id。关于ARCHITECTURE_MAGIC的定义,在【include/platform/smdk2410.h】, 0 x20: magic number so we can verify that we only put .long 0 0 x24: .long 0 0 x28: where this vivi was linked, so we can put it in memory in the right place .long _s
26、tart 0 x2C: this contains the platform, cpu and machine id .long ARCHITECTURE_MAGIC 0 x30: vivi capabilities .long 0 #ifdef CONFIG_PM ;vivi没有使用电源管理 0 x34: b SleepRamProc #endif #ifdef CONFIG_TEST 0 x38: b hmi #endif Start VIVI head Reset:, disable watch dog timer ;禁止看门狗计时器 mov r1, #0 x53000000 ;WTCO
27、N寄存器地址是 0 x53000000,清0 mov r2, #0 x0 str r2, r1 #ifdef CONFIG_S3C2410_MPORT3 ;不符合条件,跳到下面的关中断 /* 在/vivi/include/autoconf.h中#undef CONFIG_S3C2410_MPORT3*/ mov r1, #0 x56000000 ;GPACON寄存器地址是 0 x56000000 mov r2, #0 x00000005 str r2, r1, #0 x70 ;配置GPHCON控制寄存器 mov r2, #0 x00000001 str r2, r1, #0 x78 ;配置GP
28、HUP上拉寄存器 mov r2, #0 x00000001 str r2, r1, #0 x74 ;配置GPHDAT数据寄存器,#endif disable all interrupts ;禁止全部中断 mov r1, #INT_CTL_BASE mov r2, #0 xffffffff str r2, r1, #oINTMSK ;掩码关闭所有中断 ldr r2, =0 x7ff str r2, r1, #oINTSUBMSK, initialise system clocks ;初始化系统时钟 mov r1, #CLK_CTL_BASE ;定义CLK_CTL_BASE ox4C000000
29、mvn r2, #0 xff000000 str r2, r1, #oLOCKTIME ldr r2, mpll_50mhz ;CPU的频率是50MHz str r2, r1, #oMPLLCON #ifndef CONFIG_S3C2410_MPORT1 ;满足条件,向下执行 /* 在/vivi/include/autoconf.h中#undef CONFIG_S3C2410_MPORT1*/ 1:2:4 mov r1, #CLK_CTL_BASE mov r2, #0 x3 str r2, r1, #oCLKDIVN mrc p15, 0, r1, c1, c0, 0 read ctrl
30、register orr r1, r1, #0 xc0000000 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 1:2:2 mov r1, #CLK_CTL_BASE ldr r2, clock_clkdivn str r2, r1, #oCLKDIVN mrc p15, 0, r1, c1, c0,
31、0 read ctrl register orr r1, r1, #0 xc0000000 Asynchronous mcr p15, 0, r1, c1, c0, 0 write ctrl register now, CPU clock is 100 Mhz ;CPU的频率是100MHz mov r1, #CLK_CTL_BASE ldr r2, mpll_100mhz str r2, r1, #oMPLLCON #endif bl memsetup ;跳转到memsetup函数,/* Memsetup函数的实现: ENTRY(memsetup) initialise the static
32、memory set memory control registers ;设置内存控制寄存器的初值 mov r1, #MEM_CTL_BASE adrl r2, mem_cfg_val /* Data Area Memory configuration values .align 4 mem_cfg_val: ;定义好的13*4=52个字节初值,.long vBWSCON ;总线宽度和等待控制寄存器 在/vivi/include/platform/smdk2410.h中赋值 /* SDRAM从32位变成16位,需要修改vBWSCON的值 */ .long vBANKCON0 .long vBA
33、NKCON1 .long vBANKCON2 .long vBANKCON3,/* 网卡控制器vBANKCON3的值可能需要修改 */ .long vBANKCON4 .long vBANKCON5 .long vBANKCON6 /* SDRAM从32位变成16位,可能需要修改vBANKCON6的值 */ .long vBANKCON7 .long vREFRESH ;SDRAM刷新控制寄存器 .long vBANKSIZE ;可变的组大小寄存器 /* SDRAM从64MB变成32MB,需要修改vBANKSIZE的值 */ .long vMRSRB6 ;BANK6的模式设置寄存器 .long
34、 vMRSRB7 ;BANK7的模式设置寄存器 */ add r3, r1, #52 1: ldr r4, r2, #4 str r4, r1, #4 cmp r1, r3 bne 1b ;循环操作,直到13个寄存器赋值完成 mov pc, lr,*/ #ifdef CONFIG_PM ;vivi考虑不需要使用电源管理 Check if this is a wake-up from sleep ldr r1, PMST_ADDR ldr r0, r1 tst r0, #(PMST_SMR) bne WakeupStart ;查看状态,判断是否需要跳转到WakeupStart #endif #i
35、fdef CONFIG_S3C2410_SMDK ;SMDK开发板使用 All LED on ;点亮开发板上的LED mov r1, #GPIO_CTL_BASE add r1, r1, #oGPIO_F ;LED使用GPIOF组的管脚 ldr r2,=0 x55aa ;使能EINT0,EINT1,EINT2,EINT3, ;另四个管脚配置成输出,屏蔽EINT4,5,6,7 str r2, r1, #oGPIO_CON ;配置管脚 mov r2, #0 xff str r2, r1, #oGPIO_UP ;禁止上拉功能 mov r2, #0 x00 str r2, r1, #oGPIO_DAT
36、 ;PORT H 数据寄存器,#endif #if 0 SVC ;进入系统管理模式 mrs r0, cpsr bic r0, r0, #0 xdf orr r1, r0, #0 xd3 msr cpsr_all, r1 #endif set GPIO for UART ;设置串口 mov r1, #GPIO_CTL_BASE add r1, r1, #oGPIO_H ;设置GPIO_H组管脚为串口 ldr r2, gpio_con_uart str r2, r1, #oGPIO_CON ldr r2, gpio_up_uart str r2, r1, #oGPIO_UP /* initial
37、values for GPIO gpio_con_uart: .long vGPHCON ;vGPHCON在/vivi/include/platform/smdk2410.h中赋值 ;#define vGPHCON 0 x0016faaa ;GPIO_H配置为nCTS0,nRTS0, RXD0,TXD0, RXD1, ,TXD1,nCTS1,nRTS1,/* 三个串口都使能,可能需要修改#define vGPHCON 0 x0016aaaa */ gpio_up_uart: .long Vgphup ;同上 #define vGPHUP 0 x000007ff ;The pull-up fun
38、ction is disabled. */ bl InitUART ;跳转到InitUART串口初始化函数 Initialize UART r0 = number of UART port InitUART: ldr r1, SerBase /* .align 4 ;缺省情况下在vivi中只初始化了UART0 SerBase: #if defined(CONFIG_SERIAL_UART0) .long UART0_CTL_BASE ;基地址在/vivi/include/s3c2410.h中定义 #elif defined(CONFIG_SERIAL_UART1) .long UART1_CT
39、L_BASE #elif defined(CONFIG_SERIAL_UART2) .long UART2_CTL_BASE #else #error not defined base address of serial #endif,*/ mov r2, #0 x0 str r2, r1, #oUFCON ; UART FIFO控制寄存器 str r2, r1, #oUMCON ; UART MODEM控制寄存器 mov r2, #0 x3 str r2, r1, #oULCON ; UART 列控制寄存器 ldr r2, =0 x245 str r2, r1, #oUCON ; UART控
40、制寄存器 #define UART_BRD (50000000 / (UART_BAUD_RATE * 16) - 1) mov r2, #UART_BRD; 波特率设置 str r2, r1, #oUBRDIV ;波特率约数寄存器 mov r3, #100 mov r2, #0 x0 1: sub r3, r3, #0 x1 tst r2, r3 bne 1b #if 0 mov r2, #U str r2, r1, #oUTXHL ; UART 传输缓冲区寄存器 1: ldr r3, r1, #oUTRSTAT ;UART 错误寄存器,and r3, r3, #UTRSTAT_TX_EMP
41、TY tst r3, #UTRSTAT_TX_EMPTY bne 1b mov r2, #0 str r2, r1, #oUTXHL 1: ldr r3, r1, #oUTRSTAT ;UART TX/RX状态寄存器 and r3, r3, #UTRSTAT_TX_EMPTY tst r3, #UTRSTAT_TX_EMPTY bne 1b #endif mov pc, lr */ #ifdef CONFIG_DEBUG_LL ;打印调试信息,缺省未定义 Print current Program Counter ldr r1, SerBase mov r0, #r bl PrintChar
42、mov r0, #n bl PrintChar mov r0, #,bl PrintChar mov r0, pc bl PrintHexWord #endif #ifdef CONFIG_BOOTUP_MEMTEST simple memory test to find some DRAM flaults. bl memtest #endif #ifdef CONFIG_S3C2410_NAND_BOOT ;从NAND Flash启动 bl copy_myself ;跳转到copy_myself函数 , /* copy_myself: copy vivi to ram;复制到RAM copy
43、_myself: mov r10, lr reset NAND; 复位NAND FLASH mov r1, #NAND_CTL_BASE ldr r2, =0 xf830 initial value,str r2, r1, #oNFCONF ;配置NAND FLASH ldr r2, r1, #oNFCONF bic r2, r2, #0 x800 ;芯片使能,Bit11为0,使能,即允许访问 str r2, r1, #oNFCONF mov r2, #0 xff RESET command strb r2, r1, #oNFCMD ;设置NAND FLASH命令 mov r3, #0 wai
44、t 1: add r3, r3, #0 x1 cmp r3, #0 xa blt 1b 2: ldr r2, r1, #oNFSTAT wait ready ; NAND FLASH操作状态寄存器 tst r2, #0 x1 beq 2b ldr r2, r1, #oNFCONF orr r2, r2, #0 x800 ;禁止芯片,不允许访问 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 pre
45、vious frame, so fp=0 copy vivi to RAM ldr r0, =VIVI_RAM_BASE,*/ mov r1, #0 x0 mov r2, #0 x20000 ;0 x20000-128k字节 bl nand_read_ll ;nand_read_ll在/vivi/arch/s3c2410/nand_read.c中定义 ;r0,r1,r2分别为函数的三个参数 ;从NANDFlash的0地址拷贝128k到SDRAM指定处 tst r0, #0 x0 beq ok_nand_read #ifdef CONFIG_DEBUG_LL bad_nand_read: ldr
46、 r0, STR_FAIL ldr r1, SerBase bl PrintWord 1: b 1b infinite loop #endif ok_nand_read: #ifdef CONFIG_DEBUG_LL ldr r0, STR_OK ldr r1, SerBase bl PrintWord #endif,/*在/vivi/linux/platform/smdk2410.h中定义 #define VIVI_RAM_BASE (DRAM_BASE + DRAM_SIZE - VIVI_RAM_SIZE), verify mov r0, #0 ldr r1, =0 x33f00000
47、mov r2, #0 x400 4 bytes * 1024 = 4K-bytes go_next: ldr r3, r0, #4 ldr r4, r1, #4 teq r3, r4 bne notmatch subs r2, r2, #4 beq done_nand_read bne go_next notmatch: #ifdef CONFIG_DEBUG_LL sub r0, r0, #4 ldr r1, SerBase bl PrintHexWord ldr r0, STR_FAIL ldr r1, SerBase bl PrintWord #endif,1: b 1b done_na
48、nd_read: #ifdef CONFIG_DEBUG_LL ldr r0, STR_OK ldr r1, SerBase bl PrintWord #endif mov pc, r10 ;vivi拷贝到SDRAM完成,函数返回 */ jump to ram ldr r1, =on_the_ram add pc, r1, #0 nop nop 1: b 1b infinite loop on_the_ram: #endif #ifdef CONFIG_DEBUG_LL ldr r1, SerBase ldr r0, STR_STACK bl PrintWord ldr r0, DW_STAC
49、K_START bl PrintHexWord #endif, get read to call C functions ldr sp, DW_STACK_START setup stack pointer 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 End VIVI head ,Bootloader的第二阶段是用C语言完成的,但是与普通 C语言应用程序不同的是,在编译和链接Bootloader时
50、,不能使用glibc库中的任何支持函数,从哪里跳转进 main() 函数呢? 直接把 main() 函数的起始地址作为整个 stage2 执行映像的入口点或许是最直接的想法。 但是这样做有两个缺点: 1)无法通过main() 函数传递函数参数; 2)无法处理 main() 函数返回的情况。 一种较为巧妙的方法是利用弹簧床的概念,也就是用汇编语言写一段trampoline 小程序,并将这段程序作为stage2可执行映象的执行入口点,然后在trampoline汇编小程序中用CPU跳转指令跳入main()函数中去执行。当main()函数返回时,CPU执行路径再次回到trampoline程序。简而言之
51、,这种方法的思想就是:用这段 trampoline小程序来作为main()函数的外部包裹。,vivi的第二阶段,Trampoline程序的源代码如下: ldrsp,DW_STACK_START;设置堆栈指针 movfp,#0 ;no previous frame,so fp=0 mova2,#0 ;set argv to NULL blmain ;call main movpc,#FLASH_BASE ;否则,reboot,vivi的第二阶段是从main()函数开始,同一般的C语言程序一样,该函数在/init/main.c文件中,总共可以分为8个步骤。 (1) 函数开始,通过putstr(vi
52、vi_banner)打印出vivi的版本。 Vivi_banner在/init/version.c文件中定义 (2) 对开发板进行初始化(board_init函数),board_init是与开发板紧密相关的,这个函数在/arch/s3c2410/smdk.c文件中。开发板初始化主要完成两个功能,时钟初始化(init_time())和通用IO口设置(set_gpios())。,void set_gpios(void) GPACON = vGPACON; GPBCON = vGPBCON; GPBUP = vGPBUP; GPCCON = vGPCCON; GPCUP = vGPCUP; GPDC
53、ON = vGPDCON; GPDUP = vGPDUP; GPECON = vGPECON; GPEUP = vGPEUP; GPFCON = vGPFCON; GPFUP = vGPFUP; GPGCON = vGPGCON; GPGUP = vGPGUP; GPHCON = vGPHCON; GPHUP = vGPHUP; EXTINT0 = vEXTINT0; EXTINT1 = vEXTINT1; EXTINT2 = vEXTINT2; GPIO口在smdk2410.h(viviincludeplatform目录下)文件中定义。 这里vGPxCON中的v表示value,oGPxCON
54、中的o表示offset,(3) 内存映射初始化和内存管理单元的初始化工作: mem_map_init(); mmu_init(); 这两个函数都在/arch/s3c2410/mmu.c文件中。 void mem_map_init(void) #ifdef CONFIG_S3C2410_NAND_BOOT mem_map_nand_boot(); #else mem_map_nor(); #endif cache_clean_invalidate(); tlb_invalidate(); 如果配置vivi时使用了NAND作为启动设备,则执行mem_map_nand_boot(),否则执行mem_
55、map_nor()。这里要注意的是,如果使用NOR启动,则必须先把vivi代码复制到RAM中。这个过程是由copy_vivi_to_ram()函数来完成的。代码如下: static void copy_vivi_to_ram(void) putstr_hex(Evacuating 1MB of Flash to DRAM at 0 x, VIVI_RAM_BASE); memcpy(void *)VIVI_RAM_BASE, (void *)VIVI_ROM_BASE,VIVI_RAM_SIZE); ,VIVI_RAM_BASE、VIVI_ROM_BASE、VIVI_RAM_SIZE这些值都可以在smdk2410.h中查到,并且这些值必须根据自己开发板的RAM实际大小修改。这也是在移植vivi的过程中需要注意的一个地方。 mmu_init()函数中执
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 技术咨询技术服务合同
- 股份代持协议模板
- 全新夫妻离婚财产协议
- 火锅店奖惩制度
- 合同能源管理在热计量节能改造中的实践案例
- 雁门太守行市公开课教案
- 中北大学本科培养方案
- 水电站综自改造施工方案
- 保安开除员工合同样本
- 个人变压器合同样本
- 静疗横断面调查护理
- DB45T 1056-2014 土地整治工程 第2部分:质量检验与评定规程
- 2025年3月《提振消费专项行动方案》解读学习课件
- 4-6岁幼儿同伴交往能力量表
- 人教版 数学一年级下册 第三单元 100以内数的认识综合素养评价(含答案)
- T-CEPPC 18-2024 电力企业数字化转型成熟度评价指南
- XX化工企业停工安全风险评估报告
- 2025年济源职业技术学院单招职业技能测试题库学生专用
- 全国川教版信息技术八年级下册第二单元第3节《评价文创作品》教学设计
- 急诊科护理创新管理
- 临边防护安全培训课件
评论
0/150
提交评论