uboot移植
一、嵌入式linux系统的启动过程
加载 挂载 调用
uboot(bootloader) -------------> kernel(uImage/zImage)------------>rootfs文件系统 ------------------>自动调用应用程序
boot--->硬件启动及初始化 初始化linux内核的资源 设置linux的环境变量
loader--->加载linux的内核 初始化驱动程序 linux shell命令
init进程
应用程序及数据
============================================================================================================================
二、什么是uboot?
uboot---->通用的bootloader
通用:1、uboot支持多种架构的CPU
2、可以启动多种操作系统:linux、wince、vxworks
3、支持多种硬件的板子
bootloader:
1、boot:
1)CPU级初始化(BL1):ARM汇编编程, cache、MMU、clock、uart、nand、DDR2 SDRAM
2)板级初始化: C语言编程:USB、LCD、网卡、I2C
2、loader
将操作系统内核从nand flash拷贝到内存,然后给内核传递参数,并启动linux内核
============================================================================================================================
三、uboot的使用
1、uboot的帮助
GEC210 # ?
2、go
执行内存中二进制可执行文件:go 0x30000000
3、tftp
通过tftp下载文件:tftp 0x30000000 lcd.bin
4、loady
使用yumodem协议,向内存中下载一个二进制文件
5、bdinfo
查看硬件平台的信息
GEC210 # bdinfo
arch_number = 0x00000998 ----》uboot针对硬件平台的ID,该ID会传给内核,内核在启动过程中会将该ID与自身的ID做比较
env_t = 0x00000000
boot_params = 0x30000100 ----》uboot会传递启动参数给kernel,启动参数存放的地址
DRAM bank = 0x00000000 ---->内存的通道0
-> start = 0x30000000 ---->通道0内存的起始地址
-> size = 0x10000000 ---->通道0的内存大小(256MB)
DRAM bank = 0x00000001
-> start = 0x40000000
-> size = 0x10000000
ethaddr = 00:40:5C:26:0A:5B ---->MAC地址,MAC并不是写在网卡芯片中,而是在程序代码中的。
ip_addr = 192.168.1.224
baudrate = 115200 bps
思考:
GEC210平台,内存的地址范围:0x30000000~0x4FFFFFFF(512MB)
6、printenv
输出uboot的环境变量
GEC210 # printenv
bootargs=root=/dev/mtdblock4 rootfstype=yaffs2 init=/init console=ttySAC0,115200
baudrate=115200
ethaddr=00:40:5c:26:0a:5b
netmask=255.255.255.0
gatewayip=192.168.1.1
bootdelay=1
serverip=192.168.1.222
tftp=0x40000000 start.bin;go 0x40000000
bootcmd=nand read 0x30008000 0x600000 0x500000;bootm 0x30008000
ipaddr=192.168.1.224
stdin=seria
l
stdout=serial
stderr=serial
filesize=0
Environment size: 408/16380 bytes
1)bootcmd
启动命令,告诉uboot初始化结束后,自动做什么工作?(告诉boot去哪里加载内核并启动内核)
bootcmd=nand read 0x30008000 0x600000 0x500000;bootm 0x30008000
从nand flash的0x600000地址上读取0x500000(5MB)大小的内容(zImage)到0x30008000地址上。
从0x30008000地址上开始启动内核。
setenv bootcmd 'nand read 0x30008000 0x600000 0x500000; bootm 0x300088000'
思考:
setenv bootcmd 'tftp 0x40000000 start.bin; go 0x40000000'
setenv bootcmd 'tftp 0x30008000 zImage; bootm 0x30008000'
2)bootargs
启动参数--->告诉linux内核如何启动,去那里挂载rootfs。
bootargs=root=/dev/mtdblock4 rootfstype=yaffs2 init=/init console=ttySAC0,115200
root=/dev/mtdblock4 --->rootfs在哪里,nand flash的第4个分区
rootfstype=yaffs2 ---->rootfs的格式。yaffs-->小页的nand(page=512B),yaffs2--->大页的nand(2048、4096)
init=/sbin/init ---->init是linux系统启动的第一个进程,该进程需要用户指定,一般是启动shell命令的环境。
console=ttySAC0,115200 --->指定linux内核的控制器台。
setenv bootargs 'console=ttySAC0,115200 root=/dev/mtdblock4 rootfstype=yaffs2 init=/sbin/init'
7、reset
============================================================================================================================
四、通过uboot更新系统
1、设置IP
#setenv ipaddr 192.168.1.5
#setenv serverip 192.168.1.6
#setenv gatewayip 192.168.1.1
#saveenv
提示:
如果删除错误的配置
#setenv ipserver 192.168.1.5
#setenv ipserver
2、使用uboot去烧写新的uboot
#tftp 0x40000000 u-boot.bin
#nand erase 0x0 0x100000
#nand write 0x40000000 0x0 0x100000
3、使用uboot去烧写内核(zImage/uImage)
#tftp 0x40000000 zImage
#nand erase 0x600000 0x500000
#nand write 0x40000000 0x600000 0x500000
4、使用uboot去烧写rootfs
#tftp 0x40000000 rootfs.img
#nand erase 0xe00000 0xF200000
#nand write.yaffs 0x40000000 0xe00000 xxxx ---》tftp下载文件的大小。
提示:
如果想要擦除整个nand flash
#nand erase 0
============================================================================================================================
五、编译粤嵌发布的uboot源码
1、设置uboot运行平台ARCH和交叉工具链CORSS_COMPILE
# vi Makefile
ifeq ($(ARCH),arm)
CROSS_COMPILE = /usr/local/arm/arm-2009q3/bin/arm-none-linux-gnueabi-
2、对uboot进行配置
让uboot源码编译出来的u-boot.bin可以运行在特定的平台上。在Makefile文件中有配置入口,没有每个配置入口对用一个硬件平台。
gec210_nand_config : unconfig
@$(MKCONFIG) $(@:_config=) arm s5pc11x gec210 samsung s5pc110
@echo "TEXT_BASE = 0xc3e00000" > $(obj)board/samsung/gec210/config.mk
$ make gec210_nand_config
Configuring for gec210_nand board...
3、编译
#make
/usr/local/arm/arm-2009q3/bin/arm-none-linux-gnueabi-objcopy --gap-fill=0xff -O srec u-boot u-boot.srec
/usr/local/arm/arm-2009q3/bin/arm-none-linux-gnueabi-objcopy --gap-fill=0xff -O binary u-boot u-boot.bin
/usr/local/arm/arm-2009q3/bin/arm-none-linux-gnueabi-objdump -d u-boot > u-boot.dis
查看反汇编文件:
c3e00000 <_start-0x10>:
c3e00000: 00002000 .word 0x00002000
...
c3e00010 <_start>:
c3e00010: ea000013 b c3e00064
c3e00014: e59ff014 ldr pc, [pc, #20] ; c3e00030 <_undefined_instruction>
c3e00018: e59ff014 ldr pc, [pc, #20] ; c3e00034 <_software_interrupt>
c3e0001c: e59ff014 ldr pc, [pc, #20] ; c3e00038 <_prefetch_abort>
c3e00020: e59ff014 ldr pc, [pc, #20] ; c3e0003c <_data_abort>
c3e00024: e59ff014 ldr pc, [pc, #20] ; c3e00040 <_not_used>
c3e00028: e59ff014 ldr pc, [pc, #20] ; c3e00044 <_irq>
c3e0002c: e59ff014 ldr pc, [pc, #20] ; c3e00048 <_fiq>
链接地址 机器码 汇编指令 注释
4、生成u-boot.bin
将uboot.bin烧写到nand flash的0地址。
============================================================================================================================
六、uboot移植的源码来源
我们找到一个uboot源码,将该源码移植到我们特定的平台,让源码可以在我们的平台上正常工作。
来源:
1、uboot的官网
http://www.denx.de/wiki/U-Boot/WebHome
ftp://ftp.denx.de/pub/u-boot/
2、芯片的原厂或代理商
3、第三方(开发板、方案商)
目前:
SMDKV210(三星) ------> GEC210(GEC)
原则:两块板子最好是一个CPU。
思路:
分析两块板子硬件的差异,根据差异修改源代码。
============================================================================================================================
七、uboot源码的结构
1、基于CPU架构的源码lib_XXX
lib_arm/
2、uboot支持的CPU
cpu/s5pc11x/
3、uboot支持的板子
board/samsung/smdkc110/
4、公共的代码common
与硬件无关,一般是存放uboot的命令。
5、驱动的源代码
drivers/
6、文件系统的源码
fs/
7、include头文件
include/s5pc110.h
include/s5pc11x.h
include/configs/smdkv210single.h
提示:
在嵌入式平台中的数据类型
char ---- 8bits, %c, %d
short ---- 16bits
int ---- 32bits
long ---- 与CPU的字长一致
8、mkconfig
配置脚本文件
9、net
网络相关的代码。
10、sd_fusing
制作uboot启动卡的文档。
============================================================================================================================
八、编译基于SMDKV210平台的uboot源码
在uboot源码支持的现有平台中,smdkv210与GEC210最接近,所以我们要先编译以下基于SMDKV210平台的源码。
1、设置uboot运行平台ARCH和交叉工具链CORSS_COMPILE
# vi Makefile
ifeq ($(ARCH),arm
)
CROSS_COMPILE = /usr/local/arm/arm-2009q3/bin/arm-none-linux-gnueabi-
2、对uboot进行配置
让uboot源码编译出来的u-boot.bin可以运行在特定的平台上。在Makefile文件中有配置入口,没有每个配置入口对用一个硬件平台。
$ make smdkv210single_config
Configuring for smdkv210single board...
3、编译
#make
生成u-boot.bin,不能在GEC210平台上运行的。
============================================================================================================================
九、uboot源码的配置过程(以smdkv210平台为例)
smdkv210single_config : unconfig
@$(MKCONFIG) $(@:_config=) arm s5pc11x smdkc110 samsung s5pc110
@echo "TEXT_BASE = 0xc3e00000" > $(obj)board/samsung/smdkc110/config.mk
配置过程所做的三个工作。
1、unconfig
删除原来的配置。
unconfig:
@rm -f $(obj)include/config.h $(obj)include/config.mk \
$(obj)board/*/config.tmp $(obj)board/*/*/config.tmp \
$(obj)include/autoconf.mk $(obj)include/autoconf.mk.dep \
$(obj)board/$(VENDOR)/$(BOARD)/config.mk
'@'---》消除shell命令的“回显”
------------------------------------------------------------------------------------
2、@$(MKCONFIG) $(@:_config=) arm s5pc11x smdkc110 samsung s5pc110
@mkconfig smdkv210single arm s5pc11x smdkc110 samsung s5pc110
shell的参数: -----》与uboot的源码的结构必须一致
$1 ---》 smdkv210single
配置入口:include/configs/smdkv210single.h
$2 ---》 arm
CPU的架构:
ifeq ($(ARCH),arm)
CROSS_COMPILE = /usr/local/arm/arm-2009q3/bin/arm-none-linux-gnueabi-
lib_arm/
$3 ---》 s5pc11x
CPU的型号
cpu/s5pc11x/
include/s5pc11x.h
$4 ---》 smdkc110
板子的名字:
board/samsung/smdkc110/
$5 ---》 samsung
厂家的名字:
board/samsung
$6 ---》 s5pc110
SOC--->system on chip,片上系统:
include/s5pc110.h
cpu/s5pc11x/s5pc110
分析mkconfig文件:
$1 ---》 smdkv210single
$2 ---》 arm
$3 ---》 s5pc11x
$4 ---》 smdkc110
$5 ---》 samsung (可选参数)
$6 ---》 s5pc110 (可选参数)
小结:mkconfig的作用?
cd ./include
1)ln -s asm-arm asm
2)ln -s asm-arm/arch-s5pc110 asm-arm/arch
3) ln -s s5pc110.h regs.h
4) ln -s arch-s5pc11x asm-arm/arch
5) ln -s asm-arch/proc-armv asm-arch/proc
6)创建一个config.mk,并写入内容:
ARCH=arm
CPU=s5pc11x
BOARD=smdkc110
VENDOR=samsung
SOC=s5pc110
7)创建config.h头文件,并写入内容
#include
-----------------------------------------------------------------------------------------
3、@echo "TEXT_BASE = 0xc3e00000" > $(obj)board/samsung/smdkc110/config.mk
将
TEXT_BASE = 0xc3e00000
写入
board/samsung/smdkc110/config.mk
提示:什么是TEXT_BASE?
代码段的基地址,
0xc3e00000 ---->是一个虚拟地址,在uboot是使用了MMU(虚拟地址--->物理地址)
0xc3e00000 -----------> 0x33e000
00
MMU(查表)
是一个链接地址。
============================================================================================================================
十、uboot移植过程
linux底层代码的移植需要具备的素质:
1、了解底层的软件代码(uboot:汇编):语法、流程、架构、框架模型
2、会分析原理图
1、修改串口控制台
SMDKV210 --->serial3(UART2)
GEC210 --->serial1(UART0)
$vim include/configs/smdkv210single.h
将:
#define CONFIG_SERIAL3 1 /* we use UART2 on SMDKC110 */
修改为:
#define CONFIG_SERIAL1 1 /* we use UART0 on SMDKC110 */
---------------------------------------------------------------------------
2、移植DDR2内存
GEC210 ---》 ch0 : 16bis * 64M * 2 = 256MB
ch1 : 16bis * 64M * 2 = 256MB
SMDKV210 ---》ch0 : 8bits 128M * 4 = 512MB
---> ch0 : 8bits 128M * 4 = 512MB
修改:
#define MEMORY_BASE_ADDRESS 0x30000000
#define SDRAM_BANK_SIZE 0x10000000
define MEMORY_BASE_ADDRESS1 0x40000000
define PHYS_SDRAM_2 (MEMORY_BASE_ADDRESS1)
https://www.360docs.net/doc/d03263265.html,
#define DMC0_MEMCONFIG_0 0x30F00313 // MemConfig0
DMC0_MEMCONFIG_0
[31:24] --->AXI Base Address
0x30
[23:16] --->AXI Base Address Mask
0xF0
[15:12] --->Address Mapping Method (AXI to memory)
0x0 = Linear ({bank, row, column, width}),
[11:8] --->Number of Column Address Bits
0x3 = 10 bits
[7:4] --->Number of Row Address Bits
0x1 = 13 bits
[3:0] --->Number of Banks
0x3 = 8 banks
提示:
求DDR内存的大小:(地址总线、数据总线)
2^(行地址+列地址+bank地址)*数据总线 = 2^(13+10+3)*16bits = 2^26 * 16bits = 64M *16bits
注意:
初始化DDR2内存,是在C语言运行之前。内存的初始化是汇编实现。
---------------------------------------------------------------------------
3、移植网卡
1)DM9000的基地址:0x88000000
偏移量是:0x8
2)初始化SROM1的寄存器
static void dm9000_pre_init(void)
{
unsigned int tmp;
/* DM9000 on SROM BANK1, 16 bit */
SROM_BW_REG &= ~(0xf << 4);
SROM_BW_REG |= (0x1 << 4); //--->初始化寄存器1:SROM_BW
SROM_BC1_REG = ((0<<28)|(0<<24)|(5<<16)|(0<<12)|(0<<8)|(0<<4)|(0<<0)); //初始化寄存器1:SROM_BC1
/* Set MP01_1 as SROM_CSn[1] */
tmp = MP01CON_REG;
tmp &=~(0xf<<4);
tmp |=(2<<4);
MP01CON_REG = tmp;
}
4、屏蔽SRAM 初始化代码
SMDKV210 --->SRAM
GEC210 --->NC
5、屏蔽初始化PMIC
SMDKV210 --->PMIC(MAX8698)
GEC210 --->NC
6、屏蔽 nor flash 初始化代码
SMDKV210 --->PMIC(MAX8698)
GEC210 --->NC
7、修改uboot环境参数(可选)
1)主机名
2)网络配置
3)启动命令
4)启动参数
==========================================================================================================
十一、问题分
析
1、如何制作一个启动卡
S5PV210支持从sd启动,需要将uboot写入SD,使用SD卡作为启动卡。
1)windows
SD-Flasher.exe
2)SD-fusing
------------------------------------------------------------
2、移植过程中,只输出“OK”
“OK”是在汇编阶段输出的,还没有运行C语言。
3、成功的标志
1)串口输出正常
2)能加载linux内核 bootcmd
3)能设置环境变量可以设置,可以保存
4)通过bdinfo命令查看板子信息
5)可以使用tftp下载文件
注意:uboot默认没有启动logo。
4、
5、
注意:
uboot的编译不能在windows和linux的共享编译。
==========================================================================================================
十二、如何找到uboot的入口
board/samsung/smdkc110/u-boot.lds
OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
OUTPUT_ARCH(arm)
ENTRY(_start) //程序的入口,相当于main函数
SECTIONS
{
. = 0x00000000;
. = ALIGN(4); //4字节对齐
.text : //代码段
{
cpu/s5pc11x/start.o (.text) //第一个要链接的目标文件
//得到uboot的入口:cpu/s5pc11x/start.S中的_start入口
cpu/s5pc11x/s5pc110/cpu_init.o (.text)
board/samsung/smdkc110/lowlevel_init.o (.text)
cpu/s5pc11x/onenand_cp.o (.text)
cpu/s5pc11x/nand_cp.o (.text)
cpu/s5pc11x/movi.o (.text)
common/secure_boot.o (.text)
common/ace_sha1.o (.text)
cpu/s5pc11x/pmic.o (.text)
*(.text)
}
. = ALIGN(4);
.rodata : { *(.rodata) } //只读数据区
. = ALIGN(4); //可读写的数据区
.data : { *(.data) }
. = ALIGN(4);
.got : { *(.got) }
__u_boot_cmd_start = .;
.u_boot_cmd : { *(.u_boot_cmd) }
__u_boot_cmd_end = .;
. = ALIGN(4);
.mmudata : { *(.mmudata) } //存放MMU的pagetable(虚拟地址和物理地址的关系及该地址访问属性)
. = ALIGN(4);
__bss_start = .; //未初始化的数据段
.bss : { *(.bss) }
_end = .;
}
==========================================================================================================
十三、uboot的执行流程
cpu/s5pc11x/start.S
1、异常中断向量表
.globl _start
_start: b reset
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
2、reset:
msr cpsr_c, #0xd3 @ I & F disable, Mode: 0x13 - SVC
bl disable_l2cache
bl set_l2cache_auxctrl_cycle
bl enable_l2cache
/*
* Invalidate L1 I/D
*/
/*
* disable MMU stuff and caches
*/
3、得到CPU启动方式
/* Read booting information */
ldr r0, =PRO_ID_BASE
ldr r1, [r0,#OMR_OFFSET]
bic r2, r1, #0xffffffc1
r2寄存器保存启动方式:nand、 nor、SD、串口、
/* NAND BOOT *
/
cmp r2, #0x0 @ 512B 4-cycle
moveq r3, #BOOT_NAND
cmp r2, #0x2 @ 2KB 5-cycle GEC210启动方式
moveq r3, #BOOT_NAND
cmp r2, #0x4 @ 4KB 5-cycle 8-bit ECC
moveq r3, #BOOT_NAND
cmp r2, #0x6 @ 4KB 5-cycle 16-bit ECC
moveq r3, #BOOT_NAND
cmp r2, #0x8 @ OneNAND Mux
moveq r3, #BOOT_ONENAND
/* SD/MMC BOOT */
cmp r2, #0xc
moveq r3, #BOOT_MMCSD
/* NOR BOOT */
cmp r2, #0x14
moveq r3, #BOOT_NOR
/* Uart BOOTONG failed */
cmp r2, #(0x1<<4)
moveq r3, #BOOT_SEC_DEV
ldr r0, =INF_REG_BASE
str r3, [r0, #INF_REG3_OFFSET] //存放启动方式
4、bl lowlevel_init--->初级初始化
/* check reset status */
如果是冷启动,则初始化内存
1) /* Disable Watchdog */
ldr r0, =ELFIN_WATCHDOG_BASE /* 0xE2700000 */
mov r1, #0
str r1, [r0]
2) /* init system clock */
bl system_clock_init
3) /* Memory initialize */
bl mem_ctrl_asm_init
4) /* for UART */
bl uart_asm_init //串口输出'O'
5) bl tzpc_init
6) /* Print 'K' */
ldr r0, =ELFIN_UART_CONSOLE_BASE
ldr r1, =0x4b4b4b4b
str r1, [r0, #UTXH_OFFSET]
5、nand_boot ---》从nand flash中,将uboot代码拷贝到内存中。
mov r0, #0x1000
bl copy_from_nand
b after_copy
1)copy_from_nand @
nandll_read_blocks(CFG_PHY_UBOOT_BASE, COPY_BL2_SIZE);
拷贝的目标地址:0x33e00000
拷贝的大小是512KB(uboot.bin <= 512KB)
2)after_copy
6、MMU的初始化
1)初始化MMU的页表基地址
/* Set the TTB register */
ldr r0, _mmu_table_base
ldr r1, =CFG_PHY_UBOOT_BASE
ldr r2, =0xfff00000
bic r0, r0, r2
orr r1, r0, r1
mcr p15, 0, r1, c2, c0, 0
2)打开MMU
/* Enable the MMU */
mmu_on:
mrc p15, 0, r0, c1, c0, 0
orr r0, r0, #1
mcr p15, 0, r0, c1, c0, 0
7、将BBS段初始化为0
clear_bss:
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
8、跳转到start_arm_boot (进入C环境)
ldr pc, _start_armboot
_start_armboot:
.word start_armboot
lib_arm/board.c
1、初始化两个结构体bd_t和gd_t
2、进入死循环
for (;;) {
main_loop ();
}
1)等待bootdelay时间减到0,就自动执行bootcmd = nand read 0x30008000 0x600000 0x500000; bootm 0x300088000
2)在bootdelay减到0之前控制台接收任意一个按键,就会进入uboot命令行,循环处理uboot命令。