Download presentation
Presentation is loading. Please wait.
1
课前回顾
2
学习目标
3
学习指南
4
难重点
5
知识点
6
第章 大纲要求:当前关键词:难重点:
7
嵌入式系统设计
8
Bootloader U-BOOT
9
Bank 的划分 面对不同速度、总线宽度的外设,通常,嵌入式处理器都把外部总线分成不同的Bank空间,对应不同的地址。
S3C44B0的Bank0-5支持,ROM、SRAM等那些线性寻址的存储器或者外设;Bank6-7,还可以支持SDRAM、DRAM等。而且,每一个Bank都可以配置成8-32bit的总线,大端(big endian)或者小端(little endian)等
10
嵌入式处理器的启动过程(1) 通常系统上电(或者复位)以后,程序从地址空间的0x0开始
即PC(程序计数器)指针,指向0x0。从这个地址开始读取指令并运行。通常这个地址对应的是Bank0
11
嵌入式处理器的启动过程(2) 因为系统是从bank0上引导的,所以,在Bank0上连接保存有启动代码的Flash ROM,这就是我们常说的Boot ROM
12
Bootloader要实现的主要工作1 硬件系统自检 配置其他Bank或者端口、外设等工作模式 处理中系统的中断
在不支持remap的处理器中,中断必然要经过Boot ROM的空间,这时,Boot ROM需要处理的任务就是把固定的中断向量映射到一个可编程的中断处理子程序的地址
13
Bootloader要实现的主要工作2 引导操作系统
系统配置完成以后, Bootloader需要把操作系统(或者其他程序)装载到SDRAM(就是系统的RAM区),然后,把PC指针指向程序的RAM空间,使操作系统启动,这就是引导。
14
Bootloader要实现的主要工作3 Flash(Boot ROM)编程
要想实现Boot ROM的自编程,必须让Boot 程序是在RAM中运行
15
Bootloader的制作 Boot ROM使用来配置系统启动的。没有Boot ROM,系统就不能配置,更不能启动。
使用插座连接Flash配合编程器 通过ARM的JTAG接口
16
Bootloader
17
Bootloader的概念 Boot Loader 就是在操作系统内核运行之前运行的一段小程序。通过这段小程序,我们可以初始化硬件设备、建立内存空间的映射图,从而将系统的软硬件环境带到一个合适的状态,以便为最终调用操作系统内核准备好正确的环境。 通常,Boot Loader 是严重地依赖于硬件而实现的,特别是在嵌入式世界。因此,在嵌入式世界里建立一个通用的Boot Loader 几乎是不可能的。尽管如此,我们仍然可以对Boot Loader 归纳出一些通用的概念来,以指导用户特定的Boot Loader 设计与实现。
18
嵌入式系统离不开Bootloader 每种CPU 体系结构都有不同的BootLoader。
有些Boot Loader 也支持多种体系结构的CPU,比如U-Boot 就同时支持ARM 体系结构和MIPS 体系结构。 除了依赖于CPU 的体系结构外,Boot Loader 实际上也依赖于具体的嵌入式板级设备的配置。这也就是说,对于两块不同的嵌入式板而言,即使它们是基于同一种CPU 而构建的,要想让运行在一块板子上的Boot Loader 程序也能运行在另一块板子上,通常也都需要修改Boot Loader 的源程序。
19
Boot Loader 的安装媒介 系统加电或复位后,所有的CPU 通常都从某个由CPU 制造商预先安排的地址上取指令。比如,基于ARM7TDMI core 的CPU 在复位时通常都从地址0x 取它的第一条指令。 基于CPU 构建的嵌入式系统通常都有某种类型的固态存储设备(比如:ROM、EEPROM 或FLASH等)被映射到这个预先安排的地址上。因此在系统加电后,CPU 将首先执行Boot Loader 程序。
20
典型存储空间分配结构 启动参数 内核 根文件系统 bootloader
21
Blob的存储器区域分配 Blob 0-60KB 0x0c100000 60KB 参数 60-64KB 0x0c110000 4KB 内核
FLASH SDRAM 大小 Blob 0-60KB 0x0c100000 60KB 参数 60-64KB 0x0c110000 4KB 内核 64-768KB 0x0c300000 704KB 根文件系统 KB 0x0c400000 1280KB
22
Bootloader空间结构划分
23
控制Boot Loader 的设备或机制 主机和目标机之间一般通过串口建立连接,Boot Loader 软件在执行时通常会通过串口来进行I/O,比如:输出打印信息到串口,从串口读取用户控制字符等
24
Boot Loader 的操作模式 大多数Boot Loader 都包含两种不同的操作模式:“启动加载”模式和“下载”模式,这种区别仅对于开发人员才有意义。 最终用户的角度看,Boot Loader 的作用就是用来加载操作系统,而并不存在所谓的启动加载模式与下载工作模式的区别。
25
启动加载模式 也称为“自主”(Autonomous)模式。也即Boot Loader 从目标机上的某个固态存储设备上将操作系统加载到RAM 中运行,整个过程并没有用户的介入。这种模式是Boot Loader 的正常工作模式,因此在嵌入式产品发布的时侯,Boot Loader 显然必须工作在这种模式下。
26
下载模式 在这种模式下,目标机上的Boot Loader 将通过串口连接或网络连接等通信手段从主机下载文件,比如:下载内核映像和根文件系统映像等。从主机下载的文件通常首先被Boot Loader 保存到目标机的RAM 中,然后再被Boot Loader 写到目标机上的FLASH 类固态存储设备中。 Boot Loader 的这种模式通常在第一次安装内核与根文件系统时被使用;此外,以后的系统更新也会使用Boot Loader的这种工作模式。工作于这种模式下的Boot Loader 通常都会向它的终端用户提供一个简单的命令行接口。
27
BootLoader 与主机如何通信? 最常见的情况就是,目标机上的Boot Loader 通过串口与主机之间进行文件传输,传输协议通常是xmodem/ymodem/zmodem 协议中的一种。但是,串口传输的速度比较慢,因此通过以太网连接并借助TFTP 协议来下载文件是个更好的选择。
28
Boot Loader划分 大多数Boot Loader 都分为stage1 和stage2 两大部分。依赖于CPU 体系结构的代码,比如设备初始化代码等,通常都放在stage1 中,而且通常都用汇编语言来实现,以达到短小精悍的目的。而stage2 则通常用C语言来实现,这样可以实现给复杂的功能,而且代码会具有更好的可读性和可移植性。
29
Stage1 通常包括以下步骤(以执行的先后顺序): 硬件设备初始化。 为加载Boot Loader 的stage2 准备RAM 空间。
设置好堆栈。 跳转到stage2 的C 入口点。
30
硬件设备初始化 屏蔽所有的中断 设置 CPU 的速度和时钟频率。 RAM 初始化。 初始化 LED。
为中断提供服务通常是 OS 设备驱动程序的责任,因此在 Boot Loader 的执行全过程中可以不必响应任何中断。中断屏蔽可以通过写 CPU 的中断屏蔽寄存器或状态寄存器(比如 ARM 的 CPSR 寄存器)来完成。 设置 CPU 的速度和时钟频率。 RAM 初始化。 包括正确地设置系统的内存控制器的功能寄存器以及各内存库控制寄存器等。 初始化 LED。 典型地,通过 GPIO 来驱动 LED,其目的是表明系统的状态是 OK 还是 Error。如果板子上没有 LED,那么也可以通过初始化 UART 向串口打印 Boot Loader 的 Logo 字符信息来完成这一点。 关闭 CPU 内部指令/数据 cache
31
为加载 stage2 准备 RAM 空间 由于 stage2 通常是 C 语言执行代码,因此在考虑空间大小时,除了 stage2 可执行映象的大小外,还必须把堆栈空间也考虑进来。 空间大小最好是内存页大小(通常是 4KB)的倍数。 一般而言,1M 的 RAM 空间已经足够了。 具体的地址范围可以任意安排。但是,将 stage2 安排到整个 RAM 空间的最顶 1MB(也即(RamEnd-1MB) - RamEnd)是一种值得推荐的方法
32
拷贝 stage2 到 RAM 中 需要考虑: stage2 的可执行映象在固态存储设备的存放起始地址和终止地址; RAM 空间的起始地址。
33
设置堆栈指针 堆栈指针的设置是为了执行 C 语言代码作好准备。
34
跳转到 stage2 的 C 入口点
35
Stage2 通常包括以下步骤(以执行的先后顺序): 初始化本阶段要使用到的硬件设备。 检测系统内存映射(memory map)。
将kernel 映像和根文件系统映像从flash 上读到RAM 空间中。 为内核设置启动参数。 调用内核。
36
Stage2 Stage2的代码一般都是用C语言实现 与一般C语言不同,编译Bootloader时不用使用函数库。
37
Stage2 stage2 的代码通常用 C 语言来实现,以便于实现更复杂的功能和取得更好的代码可读性和可移植性。
但是与普通 C 语言应用程序不同的是,在编译和链接 boot loader 这样的程序时,不能使用 glibc 库中任何支持函数。
38
问题-Main()的入口? 直接把 main() 函数的起始地址作为整个 stage2 执行映像的入口点或许是最直接的想法。
39
解决- Main()的入口 trampoline(弹簧床) : trampoline小程序来作为 main() 函数的外部包裹(external wrapper) 用汇编语言写一段trampoline 小程序,并将这段 trampoline 小程序来作为 stage2 可执行映象的执行入口点。 在 trampoline 汇编小程序中用 CPU 跳转指令跳入 main() 函数中去执行; 当 main() 函数返回时,CPU 执行路径显然再次回到我们的 trampoline 程序。
40
trampoline(弹簧床)示例 来自Blob bootloader .text .globl _trampoline
Bl main /*if main ever return we just call it again*/ B _trampoline
41
初始化本阶段要使用到的硬件设备 初始化至少一个串口,以便和终端用户进行 I/O 输出信息 初始化计时器等。
设备初始化完成后,可以输出一些打印信息,程序名字字符串、版本号等。
42
检测系统的内存映射 内存映射就是指在整个4GB 物理地址空间中有哪些地址范围被分配用来寻址系统的 RAM 单元。
通常CPU预留一大段足够的地址空间给系统的RAM 嵌入式系统却不一定实现CPU预留的全部RAM空间,即嵌入式系统往往只把CPU预留的全部RAM地址空间中的一部分映射到RAM空间,而让剩余部分处于未使用状态。
43
地址空间描述 Typedef struct memory_area_struct{
u32 start;/*the base address of the memory region*/ u32 size; /*the byte number of the memory region*/ int used; }memory_area_t; Used = 1 说明某段地址被使用 Used = 0 说明某段地址并未被使用
44
加载内核映像和根文件系统映像 规划内存占用的布局 内核映象所占用的内存范围 根文件系统所占用的内存范围
一般将其拷贝到(MEM_START+0x8000)地址开始的大约1MB大小的空间,内核一般小于1M 根文件系统所占用的内存范围 一般将其拷贝到(MEM_START+0x0080,0000)地址开始的地方
45
内存布局
46
加载内核映像和根文件系统映像 从flash上拷贝 示例
ARM CPU一般从统一的内存地址空间寻址flash等固态存储设备,从flash上读取和从RAM中读取方式相同。 示例 While(count){ *dest++=*src++;/*they are all aligned with the word boundary*/ count -=4;/*byte number*/ }
47
设置内核的启动参数 Linux2.4.x之后的内核都采用标记列表(tagged list)的形式来传递启动参数
启动参数标记列表以标记ATAG_CORE开始,以标记ATAG_NONE结束 数据结构tag以及tag_header在linux的源码 Include/asm/setup.h中定义
48
Setup.h /*the list end with an ATAG_NONE node*/
#define ATAG_NONE 0x Struct tag_header{ u32 size;/*size以字数为单位*/ u32 tag; }
49
Tag Struct tag{ struct tag_header hdr; union{ struct tag_core core;
struct tag_mem32 mem; struct tag_videotext videotext; …… struct tag_cmdline cmdline; }
50
需要设置的tag 嵌入式Linux系统中,通常需要bootloader设置的常见启动参数有:ATAG_CORE, ATAG_MEM, ATAG_CMDLINE, ATAG_RAMDISK, ATAG_INITRD
51
Tag举例 ATAG_RAMDISK示例 Params->hdr.tag = ATAG_RAMDISK;
Params->hdr.size = tag_size(tag_ramdisk); Params->u.ramdisk.start = 0; Params->u.ramdisk.size = RAMDISK_SIZE; Params->u.ramdisk.flags = 1; ATAG_NONE示例 Static void setup_end_tag(void){ Params->hdr.tag = ATAG_NONE; Params->hdr.size = 0; }
52
调用内核 Bootloader调用内核的方法是直接跳转到内核第一条指令处。 对于ARM Linux,在跳转之前必须满足下列条件
CPU寄存器的设置 R0=0 R1=机器类型ID R2=启动参数标记列表在RAM中的起始地址 CPU模式 必须禁止中断(IRQ与FIQ) CPU必须为SVC模式 Cache和MMU的设置 MMU必须关闭 数据Cache必须关闭
53
嵌入式系统Bootloader
54
LILO LILO-Linux Boot Loader 当安装Linux时安装 lilo命令可以配置LILO LILO在以下情况必须重启
主启动分区Master Boot Record Linux启动分区的启动分区 lilo命令可以配置LILO 配置文件为/etc/lilo.conf LILO在以下情况必须重启 启动分区被修改 重新编译内核 启动进程被修改
55
LILO LILO用来加载内核映象并启动内核 LILO支持多个内核,并提供选择 单用户模式
内核映象可以为vmlinuxz、zImage、bzImage LILO支持多个内核,并提供选择 用户可以选择启动哪一个内核 允许传递配置参数给内核 单用户模式 通过LILO可以进入单用户模式 在LILO提示下,输入:Linux single
56
U-BOOT
57
U-boot介绍 "Universal Bootloader" open source的bootloader 支持多种处理器 支持多种开发板
PPCboot armboot 支持多种处理器 PPC, ARM, MIPS, x86, m68k, NIOS, Microblaze 支持多种开发板 超过216种,几乎常见的开发板均在其中
58
U-Boot设计原则 易于移植到其他架构,支持新型处理器以及新的开发板 易于调试,通过串口输出可以容易调试 特征和命令均可以重新配置
尽可能保持小size 尽可能保持可靠
59
用户接口 CLI-simple command line interface
通过串口执行命令 Bourne compatible shell (HUSH shell from Busybox)
60
基本命令集 Information Commands bdinfo − print Board Info structure ¨
coninfo − print console devices and informations ¨ flinfo − print FLASH memory information ¨ iminfo − print header information for application image ¨ imls − list all images found in flash ¨ help − print online help
61
基本命令集 Memory Commands base − print or set address offset ¨
crc32 − checksum calculation ¨ cmp − memory compare ¨ cp − memory copy ¨ md − memory display ¨ mm − memory modify (auto−incrementing) ¨ mtest − simple RAM test ¨ mw − memory write (fill) ¨ nm − memory modify (constant address) ¨ loop − infinite loop on address range
62
基本命令集 Flash Memory Commands cp − memory copy (program flash) ¨
flinfo − print FLASH memory information ¨ erase − erase FLASH memory ¨ protect − enable or disable FLASH write protection
63
基本命令集 Execution Control Commands autoscr − run script from memory ¨
bootm − boot application image from memory ¨ bootelf − Boot from an ELF image in memory ¨ bootvx − Boot vxWorks from an ELF image ¨ go − start application at address 'addr'
64
基本命令集 Network Commands
bootp − boot image via network using BOOTP/TFTP protocol ¨ cdp − Perform Cisco Discovery Protocol network configuration ¨ dhcp − invoke DHCP client to obtain IP/boot params ¨ loadb − load binary file over serial line (kermit mode) ¨ loads − load S−Record file over serial line ¨ nfs − boot image via network using NFS protocol ¨ ping − send ICMP ECHO_REQUEST to network host ¨ rarpboot− boot image via network using RARP/TFTP protocol ¨ tftpboot− boot image via network using TFTP protocol
65
高级命令集 Logbuffer Manipulation Commands
Bedbug Embedded Debugger Commands POST − Hardware Diagnose Commands
66
u-boot的体系结构
67
u-boot的体系结构 u-boot是一个层次式结构 串口驱动(UART Driver) 以太网驱动(Ethernet Driver)
Flash驱动(Flash驱动) USB驱动(USB Driver)。
68
内存分布
69
U-BOOT内存分布
70
U-Boot 引导内核 U-Boot 引导内核启动时向内核传递了一个命令行,内核根据这个命令行的参数来对系统进行相应的设置。
在linux 核启动时,必须要挂载一个根文件系统。根文件系统可以通过网络文件系统和本地文件系统两种方式提供。 在U-Boot 里面与此有关的设置是bootargs这个环境变量。
71
通过NFS 启动linux 利用NFS 来向linux 提供根文件系统主要是方便调试,大家可以把自己的文件系统先在主机上做好,利用NFS 进行挂载,成功了之后再把它做成本地文件系统。设置如下: setenv bootargs root=/dev/nfs rw nfsroot=<server-ip>:<root-dir> ip=<client-ip>:<server-ip>:<gw-ip>:<netmask>:<hostname>:<device>:<autoc onf> 这里“root=/dev/nfs”是告诉内核使用NFS 来来挂载根文件系统,后面的“rw”表示挂载的根文件系统是可读可写的,后面的“nfsroot”指明了网络文件系统所在的主机和路径。下面的“ip”配置为内核的网络设置提供了参数,其中 <device>表示使用NFS 需要用到的设备名称,<autoconfig>表示内核自动配置的使用方法。关于这个命令行的详细用法大家可以参照linux/Documentation/nfsroot.txt 这个文件,下面以我的目标系统为例: setenv bootargs root=/dev/nfs rw nfsroot= :/opt/eldk/ppc_82xx ip= : : : :aubrey:eth1:off
72
通过本地文件系统启动linux 当前所有的FLASH 文件系统都是基于MTD (Memory Technology Device)层的,通过MTD 来向linux 提供文件系统的,设置bootargs 参数如下: setenv bootargs root=/dev/mtdblock1 rw ip= : : : :aubrey:eth1:off 这里MTD 会提供两种类型的设备,/dev/mtd0 表示字符型设备,直接的读写和擦除需要使用这种设备接口,而与文件系统相关的操作例如mount 我们就必须用/dev/mtdblock0这种块设备接口。
73
常用U-BOOT命令 help[COMMAND] 打印命令的详细说明 setenvNAME VALUE 设置环境变量
BOOT> help tftp setenvNAME VALUE 设置环境变量 BOOT> setenv ipaddr printenv[NAME] 打印环境变量的值 BOOT> printenv ipaddr saveenv把环境变量保存到flash中 BOOT> saveenv Loadb [ADDR BAUDRATE] 串口下载 BOOT> loadb 0x Tftp ADDR FILENAME 以太网下载 BOOT> tftp 0x1000 linux.bin goADDR 从某地址运行程序 BOOT> go 0x1000 flDEST SRC LENGTH 烧写FLASH BOOT> fl 0x x x20000 md[.b,.w,.l] ADDR 显示内存数据 BOOT> md 0x1000 bootelf[ADDR] 从某地址运行elf格式的程序 BOOT> bootelf 0x1000
74
编译U-Boot 编译命令 编译结果 u-boot – in ELF format
cd u-boot-1.0.2 make distclean make omap1610h2_config make 编译结果 u-boot – in ELF format u-boot.bin – a raw binary image u-boot.srec – in MotorolaTM srec format
75
U-Boot源码分析
76
Start.S 在U-boot众多文件中,最重要莫过于start.S文件
U-boot源码中不止一个start.s文件,实际上每种被u-boot支持的CPU都会在它相应的文件夹中找到它自己的start.s文件 start.S是U-boot程序的开始点,CPU启动后需要执行的第一条命令就是该文件的开始 start.S被放在flash中cpu加电或复位的起始处,作为系统的第一条执行的指令。
77
start.S 代码结构 1) 定义入口 一个可执行的Image 必须有一个入口点并且只能有一个唯一的全局入口,通常这个入口放在Rom(flash)的0x0 地址。例如start.S 中的 .globl _start _start: 这个工作主要是修改连接器脚本文件(lds)
78
链接脚本文件 程序由各个段组成,连接脚本文件是告诉连接器从什么地址开始放置这些段 。 那为什么我们在PC上编程是没有写过链接脚本语言呢 ?
gcc等编译器内置有缺省的连接脚本。如果采用缺省脚本,则生成的目标代码需要操作系统才能加载运行。 为了能在嵌入式系统上直接运行,需要编写自己的连接脚本文件。
79
链接脚本文件 GNU编译器生成的目标文件缺省为elf格式。
elf文件由若干段(section)组成,如不特殊指明,由C源程序生成的目标代码中包含如下段: .text(正文段)包含程序的指令代码; .data(数据段)包含固定的数据,如常量、字符串; .bss(未初始化数据段)包含未初始化的变量、数组等。 C++源程序生成的目标代码中还包括.fini(析构函数代码)和.init(构造函数代码)等。
80
典型的 vmlinux.lds 文件 OUTPUT_ARCH(<arch>) /* <arch> includes architecture type */ ENTRY(stext) /* stext is the kernel entry point */ SECTIONS /* SECTIONS command describes the layout of the output file */ { . = TEXTADDR; /* TEXTADDR is LMA for the kernel */ .init : { /* Init code and data*/ _stext = .; /* First section is stext followed by __init data section */ __init_begin = .; *(.text.init) __init_end = .; }
81
典型的 vmlinux.lds 文件 .text : { /* Real text segment follows __init_data section */ _text = .; *(.text) _etext = .; /* End of text section*/ } .data :{ _data=.; /* Data section comes after text section */ *(.data) _edata=.; } /* Data section ends here */ .bss : { /* BSS section follows symbol table section */ __bss_start = .; *(.bss) _end = . ; /* BSS section ends here */ } }
82
/board/Smdk2410 OUTPUT_ARCH(arm) //CPU类型
ENTRY(_start) //整个U-boot入口点是_start(在start.s中) SECTIONS { . = 0x ; //目标代码起始点为0X . = ALIGN(4); .text : //text段 cpu/arm920t/start.o (.text)//text段的第一个目标文件是start.o, *(.text) //其他所有代码段放在start.o后面 }
83
/board/Smdk2410 . = ALIGN(4); .rodata : { *(.rodata) }
.data : { *(.data) } //所有数据段放在data段中,data 段在text段后 .got : { *(.got) } __u_boot_cmd_start = .; .u_boot_cmd : { *(.u_boot_cmd) } __u_boot_cmd_end = .;
84
/board/Smdk2410 armboot_end_data = .; . = ALIGN(4);
.bss : { *(.bss) } / /bss段 armboot_end = .; }
85
start.S 代码结构 2) 设置异常向量(Exception Vector)
异常向量表,也可称为中断向量表,必须是从0 地址开始,连续的存放。 包括了复位(reset),未定义处理(undef),软件中断(SWI),预去指令错误(Pabort),数据错误(Dabort),保留 IRQ,FIQ 等。
86
start.S 代码结构 3) 初始化CPU 相关的pll,clock,中断控制寄存器
依次为关闭watch dog timer,关闭中断,设置LockTime,PLL(phase lock loop),以及时钟。 这些值(除了LOCKTIME)都可从硬件手册查到。
87
start.S 代码结构 4) 初始化内存控制器 内存控制器,主要通过设置13 个从1c80000 开始的寄存器来设置,包括总线宽度,
8 个内存bank,bank 大小,sclk,以及两个bank mode。
88
start.S 代码结构 5) 将rom 中的程序复制到RAM 中 6) 初始化堆栈 7) 转到RAM 中执行
首先取得bootloader 在flash 的起始地址 通过标号之差计算出这个程序代码的大小。这些标号,编译器会在连接(link)的时候生成正确的分布的值。 取得正确信息后,通过寄存器做为复制的中间媒介,将代码复制到RAM 中。 6) 初始化堆栈 进入各种模式设置相应模式的堆栈。 7) 转到RAM 中执行 使用指令ldr,pc,RAM 中C 函数地址就可以转到RAM 中去执行。
89
start_armboot start_armboot完成的工作是硬件初始化,完成stage2的第一步。
92
第章 大纲要求:当前关键词:难重点:
93
本课小结
94
讨论及课后习题
Similar presentations