vboot源码详细分析-1

时间:2021-07-06 15:53:40
 

     最近一直在研究bootloader之vboot,vboot短小精悍,如果只是用来进行系统的引导,而不要提供其他复杂的功能时候,我认为这是绝佳的上选。这里以MINI2440开发板配套的源码进行分析。这个源码只支持曾NANDFLASH进行启动。由于代码精短,总的代码量不超过4K,所以,就不需要像uboot那样需要将启动代码分两段进行运行,而是可以直接在芯片自带的ram里面进行运行。当然,如果要想使用复杂的功能,那只能选择其他的bootloader了,但是对vboot深入的了解,很有利于学习其它复杂的启动代码。需要的读者,可以留言留下邮箱。  

      Recently I has been studying vboot bootloader, it is very short and is used for system start, does not provide other complex functions, I think it is a wonderful choice. Here MINI2440 development board matching source code analysis. This source only supports the NANDFLASH start it at a time. JingDuan because the code, the code to the amount of not more than 4 k, so you don't need like uboot will need to run the startup code is divided into two parts, but it can be run directly in the chip and memory. And, of course, if you want to use complex function, it can only choose other bootloader, but vboot advanced knowledge, very conducive to learning other complex startup code.

       整个文件主要有以下几个文件组成。使用gnu编译器编译。用在arm上是用arm-linux-gcc编译

244x_lib.c
244x_lib.h
bitfield.h
def.h
hardware.h
head.S
main.c
Makefile
mem.lds
nand.c
nand.h
parameters.h
s3c2440.h
smdk2440.h

以上就是所包括的几个文件。具体的一个个来看下。

1、mem.lds

     此文件是一个链接脚本文件。这个文件,应该最先看。关于链接脚本文件的详细讲解,我专门转载了一篇文章,有需要的可以查看下。

SECTIONS { 
 1 . = 000000;
 2 .myhead ALIGN(0): {*(.text.FirstSector)}
 3 .text ALIGN(512): { *(.text) }
 4 .bss ALIGN(4)  : { *(.bss*)  *(COMMON) }
 5 .data ALIGN(4) : { *(.data*) *(.rodata*) }
} 

 

       上面指出了在连接的时候各个文件应该存放的何处。第2行指出将编号为.text.FirstSector段标号的代码段放在起始地址为0的地方。第一行中的.只是当前的位置。第3行其他的代码段放在紧随.text.FirstSector的地址处,并且对齐方式是512字节。第4-5行指出bss 、data的位置。根据上面我们就能知道,程序开始要从.text.FirstSector开始运行,这个.text.FirstSector在head.s里面,所以程序应该先从head.s里面进行执行。
 2、makefile

       makefile是make工作的依据,看makefile能够了解程序文件的组织方式。尤其是在研究内核的时候,我们要学会看makefile。

all:
	arm-linux-gcc -mabi=aapcs-linux -mno-thumb-interwork -Os -Wall -c head.S 244x_lib.c nand.c main.c
	arm-linux-ld -T mem.lds -Bstatic head.o 244x_lib.o nand.o main.o 
	arm-linux-objcopy -O binary -S a.out vboot.bin -R .comment -R .stab -R .stabstr
	rm *.o a.out

clean:
	rm vboot.bin

很简单的几句话,第一行使用arm-linux-gcc编译器,编译生成目标文件,第二行要注意,arm-linux-ld 使用了-T选项,用来指定所要使用的链接脚本。其余的不多说。
3、244x_lib.c 244x_lib.h

     定义并实现了一下简单的通用的函数吧,如memset();看下面便知道

 244x_lib.c

#include "def.h"
#include "s3c2440.h"
#include "244x_lib.h"

#if 1
void *memset(void *dst, int src, unsigned int len)
{
	char *p = dst;
	while (len--)
		*p++ = src;
	return dst;
}
void *memcpy(void *dst, const void *src, unsigned int len)
{
	const char *s = src;
	      char *d = dst;
	while (len --) {
		*d++ = *s++;
	}
	return dst;
}
#endif

void Port_Init(void)
{
	GPACON = 0x7fffff;
	GPBCON = 0x044555;
	GPBUP = 0x7ff;		// The pull up function is disabled GPB[10:0]
	GPCCON = 0xaaaaaaaa;
	GPCUP = 0xffff;	// The pull up function is disabled GPC[15:0] 
	GPDCON = 0x00151544;
	GPDDAT = 0x0430;
	GPDUP = 0x877A;
	GPECON = 0xaa2aaaaa;
	GPEUP = 0xf7ff;	// GPE11 is NC
	GPFCON = 0x55aa;
	GPFUP = 0xff;		// The pull up function is disabled GPF[7:0]
	GPGCON = 1<<8;
	GPGDAT = 0;
	GPHCON = 0x16faaa;
	GPHUP = 0x7ff;		// The pull up function is disabled GPH[10:0]
	EXTINT0 = 0x22222222;	// EINT[7:0]
	EXTINT1 = 0x22222222;	// EINT[15:8]
	EXTINT2 = 0x22222222;	// EINT[23:16]
}

//=====================================================================
static inline void Uart_SendByte(int data)
{
	while (!(UTRSTAT0 & 0x2)) ;	//Wait until THR is empty.
	UTXH0 = data;
}

//====================================================================
void Uart_SendString(char *pt)
{
	while (*pt)
		Uart_SendByte(*pt++);
}

244x_lib.h

#ifndef __2442lib_h__
#define __2442lib_h__
#define NO_USE_STANDARD_C_LIB

void *memset(void *dst, int src, unsigned int len);
void *memcpy(void *dst, const void *src, unsigned int len);

#define min(x1,x2) (((x1)<(x2))? (x1):(x2))
#define max(x1,x2) (((x1)>(x2))? (x1):(x2))

// 2442lib.c
void Port_Init(void);
void Uart_SendString(char *pt);

#endif				//__2442lib_h__


4、def.h    对数据类型进行了重定义

     bitfield.h  定义了一些进行位操作的宏

     hardware.h  基本的宏定义

     head.S    这个文件很重要,程序首先从这里运行,是使用汇编来实现的。后面进行重点的分析。代码太长,后面总体介绍的部分不再贴出代码。

     main.c    很重要的文件,汇编运行完后,将调到这个文件的Main()函数中去执行。等Main跑完了,启动代码段也就结束了

     nand.c   nand.h  这里主要用来实现对nandflash进行的操作。比如读写,检查ID,检查坏块等

5、parameters.h

      定义了关于内核的存放位置,大小,操作系统的类型的宏定义。

#undef LINUX_CMD_LINE
#define LINUX_CMD_LINE "noinitrd root=/dev/mtdblock3 init=/linuxrc console=ttySAC0"

//#undef LINUX_CMD_LINE
//#define LINUX_CMD_LINE "console=ttySAC0 root=/dev/nfs nfsroot=192.168.1.99:/opt/FriendlyARM/mini2440/new_root_nfs ip=192.168.1.30:192.168.1.99:192.168.1.1:255.255.255.0:sbc2440.arm9.net:eth0:off"

#define OS_LINUX 0x02
#define OS_WINCE 0x04

#if 1
#define OS_TYPE		OS_LINUX
#define HAS_NAND_BIOS	0xFF
#define LOGO_POS	0xFFFF          
#define OS_START	0x60000                //nand中的起始位置
#define OS_LENGTH	0x500000              //OS长度
#define OS_RAM_START	0x30008000  //OS在ram中的起始地址

#else
#define OS_TYPE		OS_WINCE
#define HAS_NAND_BIOS	0xFF
#define LOGO_POS	0xFFFF
#define OS_START	(0xb00 * 512)             
#define OS_LENGTH	(0x1E00000)              
#define OS_RAM_START	0x30200000     
#endif
#define ZBOOT_MAGIC	0x19710829
#if !defined(__ASSEMBLY__)
extern  struct zboot_first_sector {
	unsigned char  dont_care[0x20];
	unsigned int   magic;
	unsigned char  os_type;
	unsigned char  has_nand_bios;
	unsigned short logo_pos;
	unsigned int   os_start;
	unsigned int   os_length;
	unsigned int   os_ram_start;
	unsigned char  linux_cmd[512 - 0x34];
}  __attribute__((packed)) first_sector;            //用于描述操作系统相关信息的结构体。
//__attribute__((packed))用于说明,这个结构体以最小内存的方式进行存放,要注意一点是这里已经定义
//了结构体变量first_sector,也就是说在.h文件中定义了变量。
#define g_magic			(first_sector.magic)
#define g_os_type		(first_sector.os_type)
#define g_has_nand_bios		(first_sector.has_nand_bios)
#define g_logo_pos		(first_sector.logo_pos)
#define g_os_start		(first_sector.os_start)
#define g_os_length		(first_sector.os_length)
#define g_os_ram_start		(first_sector.os_ram_start)
#define g_linux_cmd_line	(first_sector.linux_cmd)
//以上是对数据的重新定义,以为方便使用。
#endif

6、s3c2440.h   与soc相关的一些寄存器之类的定义。

       smdk2440.h 与板子密切相关的一些控制参数。

以上是所有的文件的组织形式。下面简要说明下启动代码的工作流程

      复位运行-->关闭看门狗-->屏蔽中断-->初始化系统时钟-->初始化USB时钟-->

      初始化堆栈-->初始化串口-->跳转到Main函数-->初始I/DCACHE-->初始化I/O口-->

       初始化NANDFLASH控制器-->获得参数-->拷贝linux代码到ram-->执行linux加载>>

本片进行总体的介绍,请看下篇具体分析