u-boot 启动过程 —— 基于S3C2410 --转载自周明

2024-04-17 18:32

本文主要是介绍u-boot 启动过程 —— 基于S3C2410 --转载自周明,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

 

文章来源:http://forum.linuxbj.com/node/2   

 

u-boot是广泛应用于嵌入式系统的bootloader,该软件主页是http://www.denx.de/wiki/U-Boot

 

解压缩开代码包后,以下几个目录中分类存放了主要的源码

board目录——用于放置板支持代码,相当于bootloader级的BSP。与特定板相关的代码包括频率合成、GPIO、板参数、调试串口、能源管理、按键处理等。如本例子的板级代码

common目录——用于存放体系结构无关的公共代码,主要是各种命令实现代码、环境变量实现代码等,如bootm命令

cpu目录——用于存放处理器和SOC特定代码,分别存在于以cpu名称命名的子目录中。如arm1136处理器或者是本例中的S3C2410片上系统的特有代码

driver目录——存放硬件驱动的代码,目前还没有做到完全清晰的分离架构相关/无关的代码。如S3C2410 RTC驱动和流行的dm9000百兆网卡驱动

fs目录——存放文件系统代码,包括fat、ext2以及cramfs等

include目录——存放u-boot头文件,相当一部分和linux内核中的一致

lib_xxx目录——存放于xxx架构相关的代码,如浮点routine和实际的引导代码。如arm架构内核引导代码

net目录——存放体系结构无关的网络协议,tftp、bootp等

tools目录——在本机运行的u-boot工具,如用来创建uImage的mkimage工具

 

 

 

 

本文以流行的Samsung公司的S3C2410,openmoko平台和u-boot-1.3.2(2008.5 发布)为例,介绍如何在ZIX嵌入式开发环境下探索u-boot启动过程。

虽然u-boot已经广泛应用,由于其相对的复杂性使用户在了解其内部机理和进行u-boot的移植工作时还是会碰到困难。u-boot已有一些分析文档,但多数和真正的代码不能同步或者版本老旧,难以将概念和现实的代码匹配——即硬件板上跑的代码在文档资料中却看不到,更无法紧密的跟踪。本文涉及的代码基于在s3c2410硬件运行的成熟u-boot-1.3.2代码,版本较新,提供的特性非常丰富,而且在forum.linuxbj.com可以自由浏览和下载。此u-boot代表了业界的较高水平,可以直接构建新版的嵌入式产品设计,有较高的应用价值。

u-boot总的启动流程如下
->reset
-> 设置CPU模式
-> 关闭看门狗/中断
-> 设置处理器时钟/片上总线
-> 初始化调试串口
-> MMU/外部总线/SDRAM等初始化
-> rom代码/数据搬移到ram
-> 初始化函数调用栈
-> 初始化外围设备/参数
-> 启动完毕,进入main_loop循环

嵌入式系统离不开bootloader初始化硬件以及引导操作系统。
现在,专用的嵌入式板子运行嵌入式Linux系统已经变得非常流行,u-boot是一种非常适合此类系统的bootloader。

u-boot主要提供以下功能:

  • 设置目标板硬件参数并初始化;
  • 为操作系统传递必要信息;
  • 执行交互式的底层操作;
  • 智能化装载操作系统;
  • 引导和运行的固件程序;
  • 支持大容量存储和USB接口

 

利用ZIX开发环境,能够通过比较直观的方式观察u-boot内部,而且可以将代码调试和分析同时进行,是一种了解、移植u-boot的强大工具。

使用arm工具链编译u-boot源代码,得到可以烧录的u-boot.bin文件。 
在ZIX开发环境里,可以将u-boot.bin载入s3c2410板运行,并利用gdb调试。

gdb能通过JTAG接口访问硬件,也可以通过TCP/IP访问虚拟硬件。 建立好调试连接,即可通过gdb操纵u-boot启动过程,下面可以跟随代码的执行顺序,了解从上点开始,究竟哪些操作被执行。

s3c2410复位之后,pc指针会指向0x0地址。在u-boot代码中,该0x0地址是一个向量表,
第一条指令跳转branch到复位代码start_code。 位于cpu/arm920t/start.S汇编语言文件第53行:

.globl _start  _start:  b start_code  
    ldr pc, _undefined_instruction 
    ldr pc, _software_interrupt 
    ldr pc, _prefetch_abort 
    ldr pc, _data_abort 
    ldr pc, _not_used 
    ldr pc, _irq 
    ldr pc, _fiq

复位指令跳转之后来到第154行,开始执行arm920t处理器的基本初始化。
首先修改当前程序状态寄存器CPSR,使处理器进入Supervisor|32 bit ARM模式,
并关闭ARM9TDMI中断和快速中断,这是通过设置CPSR相应掩码实现的:

start_code: 
/*
* set the cpu to SVC32 mode
*/
mrs r0,cpsr
bic r0,r0,#0x1f
orr r0,r0,#0xd3
msr cpsr,r0

紧接着,将S3C2410特有的WTCON寄存器清零,此举仅为关闭看门狗,代码位置是234行:

    ldr r0, =pWTCON 
mov r1, #0x0
str r1, [r0]

然后在241行,将S3C2410中断控制器INTMSK寄存器置为全1,
INTSUBMSK置为0x7ff,禁止全部中断源。S3C2410手册358页起对此有详细描述:

    mov r1, #0xffffffff  
ldr r0, =INTMSK
str r1, [r0]
# if defined(CONFIG_S3C2410) || defined(CONFIG_S3C2440) || defined(CONFIG_S3C2442) || /
defined(CONFIG_S3C2443)
ldr r1, =INTSUBMSK_val
ldr r0, =INTSUBMSK
str r1, [r0]
# endif

接下来在259行,访问arm920t控制寄存器CP15,并置位最高两位[31,30]。
此两位置为0b11后,处理器时钟被设置为异步模式,允许处理器异步访问总线:

    mrc p15, 0, r1, c1, c0, 0 
orr r1, r1, #0xc0000000
mcr p15, 0, r1, c1, c0, 0

至此arm920t相关的配置完成,后面开始设定S3C2410时钟合成参数。
通过设置UPLL,MPLL和CLKDIVN三个寄存器(在S3C2410手册237页起讲述),
得到需要的处理器工作频率,分别在308行:

    ldr r0, =UPLLCON  
ldr r1, =UPLLCON_val
str r1, [r0]

321行:

    ldr r1, =MPLLCON_val  
str r1, [r0, #-4] /* MPLLCON */

/* FCLK:HCLK:PCLK = 1:2:4 */
ldr r0, =CLKDIVN
mov r1, #CLKDIVN_val
str r1, [r0]

S3C2410的UART0得到初始化,以便于尽早通过UART0打印信息。
此段代码从332行开始,其中涉及到的寄存器读者可参考S3C2410手册293页起:

    /* enable uart */
ldr r0, =0x4c00000c /* clkcon */
ldr r1, =0x7fff0 /* all clocks on */
str r1, [r0]
/* gpio UART0 init */
ldr r0, =0x56000070
mov r1, #0xaa
str r1, [r0]
/* init uart */
ldr r0, =0x50000000
mov r1, #0x03
str r1, [r0]
ldr r1, =0x245
str r1, [r0, #0x04]
mov r1, #0x01
str r1, [r0, #0x08]
mov r1, #0x00
str r1, [r0, #0x0c]
mov r1, #0x1a
str r1, [r0, #0x28]

完成UART0设置之后,根据不同的编译时选项和运行时参数,代码会在360行进入相应的分支,分别是

  1. 从nand启动,代码执行lowlevel_init,主要是清除cpu cache,以及关闭mmu和i-cache,
    并且根据板极硬件配置初始化外部存储器总线和GPIO,最后把代码从nand flash中拷贝到ram中并继续执行。
  2. 从nor启动,与第1种情况相比,仅仅把代码拷贝部分简化,将DATA段从flash中拷贝到ram中,其余相同
  3. 从ram启动,因为u-boot已经处于配置好的ram中,
    所以会跳过所有cache,mmu,sdram,nand和nor相关代码,跳转到done_relocate执行

下面以最复杂的nand启动情况为例分析。首先会跳转到572行执行cpu_init_crit,
通过操作CP15完成flush处理器arm920t的cache和tlb,并关闭mmu和i-cache:

cpu_init_crit:
/*
* flush v4 I/D caches
*/
mov r0, #0
mcr p15, 0, r0, c7, c7, 0 /* flush v3/v4 cache */
mcr p15, 0, r0, c8, c7, 0 /* flush v4 TLB */
/*
* disable MMU stuff and caches
 */
mrc p15, 0, r0, c1, c0, 0
bic r0, r0, #0x00002300 @ clear bits 13, 9:8 (--V- --RS)
bic r0, r0, #0x00000087 @ clear bits 7, 2:0 (B--- -CAM)
orr r0, r0, #0x00000002 @ set bit 2 (A) Align
orr r0, r0, #0x00001000 @ set bit 12 (I) I-Cache
mcr p15, 0, r0, c1, c0, 0

然后跳转到board/neo1973/common/lowlevel_init.S文件的139行执行,
进行总线数据宽度、时序、SDRAM控制、GPIO等配置,配置完毕后会返回start.S继续执行。
因为该代码是与板相关,故放在board目录里面。由于代码较多,只粘贴开始部分:

    /* memory control configuration */  
/* make r0 relative the current location so that it */
/* reads SMRDATA out of FLASH rather than memory ! */
adr r0, SMRDATA
ldr r1, =BWSCON /* Bus Width Status Controller */
add r2, r0, #13*4

完成板级设置后,在cpu/arm920t/start.S的373行判断代码自身的执行位置。如果从stepping stone内执行,
并且u-boot配置为nand boot模式,则跳转到nand_load拷贝代码:

    ldr r1, =BWSCON /* Z = CPU booted from NAND */ 
ldr r1, [r1]
tst r1, #6 /* BWSCON[2:1] = OM[1:0] */
teqeq r0, #0 /* Z &= running at address 0 */
beq nand_load

在417行是nand_load代码,首先会跳转到614行执行may_resume
以检测系统是从待机模式唤醒还是上电启动。如果唤醒,则会根据之前保存的现场进行相应处理,
本文不做更多叙述;如果是启动,则会返回nand_load继续执行。在nand_load里初始化s3c2410的nand controller,
涉及存储器映射和寄存器NFCONF等,参见S3C2410手册215页起。同样,仅粘贴开始部分的代码: 

    mov r1, #S3C2410_NAND_BASE 
ldr r2, =0xf842 @ initial value enable tacls=3,rph0=6,rph1=0
str r2, [r1, #oNFCONF]
ldr r2, [r1, #oNFCONF]
bic r2, r2, #0x800 @ enable chip

在451行继续根据配置设定栈指针,为后面调用C函数执行拷贝作准备:

    ldr r0, _TEXT_BASE /* upper 128 KiB: relocated uboot */ 
sub r0, r0, #CFG_MALLOC_LEN /* malloc area */
sub r0, r0, #CFG_GBL_DATA_SIZE /* bdinfo */
#ifdef CONFIG_USE_IRQ
sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)
#endif
sub sp, r0, #12 /* leave 3 words for abort-stack */

然后在460行,将SDRAM中的目标地址存入r0,0x0地址存入r1,u-boot长度存入r2,
跳入cpu/arm920t/s3c24x0/nand_read.c文件第154行执行nand_read_ll函数,该函数接受前面3个寄存器中的值作为参数,并将返回值放回r0:

    ldr r0, _TEXT_BASE 
mov r1, #0x0
mov r2, #CFG_UBOOT_SIZE
bl nand_read_ll

在nand_read_ll函数中实现了nand flash访问代码,并且支持自动跳过坏块的特性,函数循环执行nand页面读取并存入SDRAM,直到u-boot全部拷贝完,并返回0,该C代码留给读者自己阅读。nand_read_ll返回0后,会跳转到ok_nand_read,并482行对拷贝的头4K字节进行校验:

    @ verify 
mov r0, #0
@ldr r1, =0x33f00000
ldr r1, _TEXT_BASE
mov r2, #0x400
@ 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

校验通过后代码506行,在地址为_booted_from_nand的SDRAM位置保存1,以便告诉上层软件本次启动是从nand引导:

done_nand_read: 
ldr r0, _booted_from_nand
mov r1, #1
strb r1, [r0]

然后在518行,将中断向量表拷贝到0x0: 

    mov r0, #0 
ldr r1, _TEXT_BASE
mov r2, #0x40
irqvec_cpy_next:
ldr r3, [r1], #4
str r3, [r0], #4
subs r2, r2, #4
bne irqvec_cpy_next

在532行,设置栈指针: 

    ldr r0, _TEXT_BASE    /* upper 128 KiB: relocated uboot */ 
sub r0, r0, #CFG_MALLOC_LEN /* malloc area */
sub r0, r0, #CFG_GBL_DATA_SIZE /* bdinfo */
#ifdef CONFIG_USE_IRQ
sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)
#endif
sub sp, r0, #12 /* leave 3 words for abort-stack */

在541行,清除bss段并跳转到真正的C函数start_armboot,进入更高级的硬件初始化代码,汇编初始化部分也全部完成使命: 

    ldr r0, _bss_start /* find start of bss segment */ 
ldr r1, _bss_end /* stop here */
mov r2, #0x00000000 /* clear */
clbss_l:
str r2, [r0] /* clear loop... */
add r0, r0, #4
cmp r0, r1
ble clbss_l
ldr pc, _start_armboot

start_armboot函数位于lib_arm/board.c文件第277行,首先初始化globel_data类型的变量gd。该变量是一个结构,其成员大多是板子的一些基本设置,如序列号、ip地址、mac地址等(欲知结构的原型可参考include/asm-arm/globel_data.h和include/asm-arm/u-boot.h): 

         gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN - sizeof(gd_t));
/* compiler optimization barrier needed for GCC >= 3.4 */
__asm__ __volatile__("": : :"memory");

memset ((void*)gd, 0, sizeof (gd_t));
gd->bd = (bd_t*)((char*)gd - sizeof(bd_t));
memset (gd->bd, 0, sizeof (bd_t));

然后在一个for循环中从init_sequence地址开始,逐个调用初始化C函数至NULL为止。这些routine函数按调用顺序分别是

  1. cpu_init() ——在common/main.c文件,执行初始化中断栈操作
  2. board_init() ——在board/neo1973/gta01/gta01.c文件中,执行板级初始化,主要是更新GPIO和PLL设置
  3. interrupt_init() ——在/cpu/arm920t/s3c24x0/interrupts.c文件中,执行时钟中断初始化
  4. env_init() ——在common/env_nand.c文件中,设置缺省环境变量
  5. init_baudrate() ——在lib_arm/board.c文件中,将环境变量中的baudrate存入bd_info结构bd
  6. serial_init() ——在common/serial.c文件中,调用驱动中真正的init()初始化串口
  7. console_init_f() ——在common/console.c文件中,更新global_data结构gd的have_console标记为1
  8. display_banner() ——在lib_arm/board.c文件中,打印u-boot banner,输出版本、运行地址等信息。比如在控制台看到的
  9. init_func_i2c() ——在lib_arm/board.c文件中,初始化i2c总线
  10. dram_init() ——在board/neo1973/gta01/gta01.c文件中,填充bd->bi_dram[0]的start和size成员,用来描述u-boot可用的ram
  11. display_dram_config() ——在board/neo1973/gta01/gta01.c文件中,打印当前ram配置。在控制台能够看到相应的 DRAM:  128 MB

利用gdb可以清晰的看到调用过程:

         for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) { 
if ((*init_fnc_ptr)() != 0) {
hang ();
}
}

接着是一些可选外设的初始化,如显示屏、nor、nand、dataflash、网卡等,此过程执行后全部初始化工作完成。下面仅粘贴nor代码:

#ifndef CFG_NO_FLASH 
/* configure available FLASH banks */
size = flash_init ();
display_flash_config (size);
#endif /* CFG_NO_FLASH */

之后在457行进入无限循环,调用common/main.c文件的278行main_loop()函数,u-boot完成启动过程。main_loop提供一个交互式命令行,可通过串口或usb控制台操作,也可以进一步引导操作系统:

         for (;;) { 
main_loop ();
}

 

这篇关于u-boot 启动过程 —— 基于S3C2410 --转载自周明的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/912519

相关文章

浅析Spring Security认证过程

类图 为了方便理解Spring Security认证流程,特意画了如下的类图,包含相关的核心认证类 概述 核心验证器 AuthenticationManager 该对象提供了认证方法的入口,接收一个Authentiaton对象作为参数; public interface AuthenticationManager {Authentication authenticate(Authenti

作业提交过程之HDFSMapReduce

作业提交全过程详解 (1)作业提交 第1步:Client调用job.waitForCompletion方法,向整个集群提交MapReduce作业。 第2步:Client向RM申请一个作业id。 第3步:RM给Client返回该job资源的提交路径和作业id。 第4步:Client提交jar包、切片信息和配置文件到指定的资源提交路径。 第5步:Client提交完资源后,向RM申请运行MrAp

MySQL数据库宕机,启动不起来,教你一招搞定!

作者介绍:老苏,10余年DBA工作运维经验,擅长Oracle、MySQL、PG、Mongodb数据库运维(如安装迁移,性能优化、故障应急处理等)公众号:老苏畅谈运维欢迎关注本人公众号,更多精彩与您分享。 MySQL数据库宕机,数据页损坏问题,启动不起来,该如何排查和解决,本文将为你说明具体的排查过程。 查看MySQL error日志 查看 MySQL error日志,排查哪个表(表空间

springboot3打包成war包,用tomcat8启动

1、在pom中,将打包类型改为war <packaging>war</packaging> 2、pom中排除SpringBoot内置的Tomcat容器并添加Tomcat依赖,用于编译和测试,         *依赖时一定设置 scope 为 provided (相当于 tomcat 依赖只在本地运行和测试的时候有效,         打包的时候会排除这个依赖)<scope>provided

内核启动时减少log的方式

内核引导选项 内核引导选项大体上可以分为两类:一类与设备无关、另一类与设备有关。与设备有关的引导选项多如牛毛,需要你自己阅读内核中的相应驱动程序源码以获取其能够接受的引导选项。比如,如果你想知道可以向 AHA1542 SCSI 驱动程序传递哪些引导选项,那么就查看 drivers/scsi/aha1542.c 文件,一般在前面 100 行注释里就可以找到所接受的引导选项说明。大多数选项是通过"_

【机器学习】高斯过程的基本概念和应用领域以及在python中的实例

引言 高斯过程(Gaussian Process,简称GP)是一种概率模型,用于描述一组随机变量的联合概率分布,其中任何一个有限维度的子集都具有高斯分布 文章目录 引言一、高斯过程1.1 基本定义1.1.1 随机过程1.1.2 高斯分布 1.2 高斯过程的特性1.2.1 联合高斯性1.2.2 均值函数1.2.3 协方差函数(或核函数) 1.3 核函数1.4 高斯过程回归(Gauss

用命令行的方式启动.netcore webapi

用命令行的方式启动.netcore web项目 进入指定的项目文件夹,比如我发布后的代码放在下面文件夹中 在此地址栏中输入“cmd”,打开命令提示符,进入到发布代码目录 命令行启动.netcore项目的命令为:  dotnet 项目启动文件.dll --urls="http://*:对外端口" --ip="本机ip" --port=项目内部端口 例: dotnet Imagine.M

Linux服务器Java启动脚本

Linux服务器Java启动脚本 1、初版2、优化版本3、常用脚本仓库 本文章介绍了如何在Linux服务器上执行Java并启动jar包, 通常我们会使用nohup直接启动,但是还是需要手动停止然后再次启动, 那如何更优雅的在服务器上启动jar包呢,让我们一起探讨一下吧。 1、初版 第一个版本是常用的做法,直接使用nohup后台启动jar包, 并将日志输出到当前文件夹n

衡石分析平台使用手册-单机安装及启动

单机安装及启动​ 本文讲述如何在单机环境下进行 HENGSHI SENSE 安装的操作过程。 在安装前请确认网络环境,如果是隔离环境,无法连接互联网时,请先按照 离线环境安装依赖的指导进行依赖包的安装,然后按照本文的指导继续操作。如果网络环境可以连接互联网,请直接按照本文的指导进行安装。 准备工作​ 请参考安装环境文档准备安装环境。 配置用户与安装目录。 在操作前请检查您是否有 sud

SpringBoot项目是如何启动

启动步骤 概念 运行main方法,初始化SpringApplication 从spring.factories读取listener ApplicationContentInitializer运行run方法读取环境变量,配置信息创建SpringApplication上下文预初始化上下文,将启动类作为配置类进行读取调用 refresh 加载 IOC容器,加载所有的自动配置类,创建容器在这个过程