15年10月31日19:44:27
(一) start.S
写这一段代码前,先要清楚bootloader开始的时候都做什么了。无非就是硬件的初始化,我们想要写一个简单的bootloader,它的功能只是要能启动内核就行,因此,与uboot相比,它要做的东西很少。
总结出来就是:
(1)关看门狗;
(2)初始化时钟,设置分频系数,让板子跑的更快点;
(3)重定位代码,根据ARM的启动方式,如果是从NOR FLASH启动的话,首地址就是0,直接启动就行,把代码复制到SDRAM中即可,如果是NAND FLASH启动的话,一上电自动把前4K代码复制到steppingstone中,然后重新把代码复制到它的链接地址中。这样的话,在复制之前,就需要先初始化SDRAM。如果是NAND启动,同时需要先把NAND FLASH初始化了。
(4)清BSS段;
(5)跳到main函数,执行启动的第二阶段 。
以下是所有代码的全部注释,源代码是带灰色阴影的,注释是没有背景的。
/*
* =============================================================================
*
Filename: start.S
Description: 自己写bootloader,第一个启动文件。
Version: 1.0
Created: 2015年10月26日 20时13分24秒
Revision: none
Compiler: arm-linux-gcc
*
Author: Snoopy (ybx), [email protected]
Organization: TJPU
*
* =============================================================================
/
#define MPLL_200MHZ ((92<<12) | (1<<4) | (1<<1))
.text
.global _start
_start:
/ 1. 关看门狗 /
ldr r0, =0x53000000
mov r1, #0
str r1, [r0]
/ 对于2440来说,看门狗的地址是0x53000000,只要将它置为0即可。 /
/ 2. 初始化时钟 /
ldr r0, =0x4c000014
mov r1, #0x03;
str r1, [r0]
/ 其中 0x4c000014 为CLKDIVN寄存器的地址值,CLKDIVN寄存器用于控制FCLK,HCLK,PCLK之间的比例关系,想要设置分频比为FCLK:HCLK:PCLK=1:2:4,需要设置CLKDIVN寄存器,HDIVN=1,PDIVN=1,即CLKDIVN的第[0]位为1,第[2:1]位为01。 /
/ change the mode to the asynchronous bus mode /
mrc p15, 0, r1, c1, c0, 0 / 读出控制寄存器 /
orr r1, r1, #0xc0000000 / 设置为“asynchronous bus mode” /
mcr p15, 0, r1, c1, c0, 0 / 写入控制寄存器 /
/ 如果HDIVN非0,CPU的总线模式应该从“fast bus mode”变为“asynchronous bus mode”,这个是数据手册里面说的。在上面的设置中,可以看到HDIVN=1,所以需要完成这样的改变。 /
/ 设置分频系数 /
ldr r0, =0x4c000004
ldr r1, = MPLL_200MHZ
str r1, [r0]
/ 0x4c000004是MPLLCON寄存器的地址值,MPLLCON寄存器用于控制FCLK和Fin的比例关系,
FCLK = (2mFin) / (p2^s),在上式中,m=MDIV+8,p=PDIV+2,s=SDIV。其中MDIV是MPLLCON的[19:12]位, PDIV是MPLLCON的[9:4]位, SDIV 是MPLLCON的[1:0]位。已知系统外部晶振输入为12MHz,需要FCLK输出为200MHz,可以算出来MDIV=92,PDIV=4,SDIV=1。 /
/ 3. 初始化SDRAM /
ldr r0, =0x48000000
adr r1, sdram_init
add r3, r1, #52
1:
ldr r2, [r1], #4
str r2, [r0], #4
cmp r3, r1
bne 1b
/ 0x48000000是BWSCON 寄存器的地址值,这里其实是设置存储控制器的,2440一共有8个BANK,关于各个BANK的性质,在这里不再叙述,需要按照数据手册,将BWSCON,BANKCON0~7,REFRESH,BANKSIZE,MRSRB6,MRSRB7这几个寄存器的值一一算出来,然后依次存到寄存器中,sdram_init 是程序的一个标号,用来存放算出来的这些值,然后用上面的方法一一存进去,这种方法在uboot中很常用,必须能够熟练使用。同时使用到了adr这个中等范围取址指令。 /
/ 4. 重定位 /
ldr sp, =0x34000000
/ 在使用c语言前需要先设置好栈,SDRAM的地址为0x30000000,一共有64M,6410241024就是0x4000000,所以就把栈设置在最高处吧~ /
bl nand_init
/ 因为下面的copy_code_to_sdram代码中需要使用NAND FLASH,所以需要提前初始化它,就在这初始化了。 /
mov r0, #0
ldr r1, =_start
ldr r2, =__bss_start
sub r2, r2, r1
bl copy_code_to_sdram / 这个函数需要3个参数,所以需要在上面写出对应的r0,r1,r2;
* 源是从0开始,目的是程序的链接地址,长度是程序长度,即bss段开始 * 的地址减去链接地址,正好就是程序的长度。
/
bl clear_bss
/ 5. 执行main /
ldr lr, =halt_loop
ldr pc, =main
halt_loop:
b halt_loop
/ 设置循环的目的是防止程序跑飞了,如果非要问程序为啥会跑飞,大致答案是这样的:ARM是一条一条取址执行的,如果在执行完main函数后,或者由于某种原因从main函数中跳出来了,那程序就会继续往下执行,但是程序是写在ram中的,后面的东西是不可预测的,所以就在这设置一个死循环,就让它在这一直转圈就好了~~~ /
sdram_init:
.long 0x22011110 //BWSCON
.long 0x00000700 //BANKCON0
.long 0x00000700 //BANKCON1
.long 0x00000700 //BANKCON2
.long 0x00000700 //BANKCON3
.long 0x00000700 //BANKCON4
.long 0x00000700 //BANKCON5
.long 0x00018005 //BANKCON6
.long 0x00018005 //BANKCON7
.long 0x008C04F4 //REFRESH
.long 0x000000B1 //BANKSIZE
.long 0x00000030 //MRSRB6
.long 0x00000030 //MRSRB7
(二)init.c
/
=====================================================================================
*
Filename: init.c
Description: c语言的一些初始化函数等。
Version: 1.0
Created: 2015年10月26日 22时22分53秒
Revision: none
Compiler: arm-linux-gcc
*
Author: Snoopy (ybx), [email protected]
Organization: TJPU
*
* =====================================================================================
/
/ NAND FLASH registers /
#define NFCONF ((volatile unsigned long )0x4e000000)
#define NFCONT ((volatile unsigned long )0x4e000004)
#define NFCMMD ((volatile unsigned char )0x4e000008)
#define NFADDR ((volatile unsigned char )0x4e00000c)
#define NFDATA ((volatile unsigned char )0x4e000010)
#define NFSTAT ((volatile unsigned char )0x4e000020)
/ serial /
#define PCLK 50000000
#define UART_BAUDRATE 115200
#define UART_BRD (PCLK/(UART_BAUDRATE * 16) - 1)
/ GPIO registers /
#define GPHCON ((volatile unsigned long )0x56000070)
#define GPHUP ((volatile unsigned long )0x56000078)
/ UART registers*/
#define ULCON0 ((volatile unsigned long )0x50000000)
#define UCON0 ((volatile unsigned long )0x50000004)
#define UFCON0 ((volatile unsigned long )0x50000008)
#define UMCON0 ((volatile unsigned long )0x5000000c)
#define UTRSTAT0 ((volatile unsigned long )0x50000010)
#define UTXH0 ((volatile unsigned char )0x50000020)
#define URXH0 ((volatile unsigned char )0x50000024)
#define UBRDIV0 ((volatile unsigned long )0x50000028)
void nand_read (unsigned int addr, unsigned char buf, unsigned int len);
/
=== FUNCTION ======================================================================
Name: is_boot_from_norflash
Description: 判断程序是否从norflash启动的。
* =====================================================================================
/
int
is_boot_from_norflash (void)
{
volatile int p = (volatile int )0;
int val;
val = p;
p = 0x12345678;
if (val == p)
{
/ NOR /
return 1;
}
else
{
/ NAND /
p = val;
return 0;
}
} / ----- end of function isBootFromNorFlash ----- /
/ 对于这个函数,它在copy_code_to_sdram 函数中使用,用于判断程序是否从norflash启动,怎么判断呢,这就是根据nor flash与nand flash的特性不同来判断,nor flash只能读,不能往里面写入值,我们就从0地址取一个值(你也可以从其他地方…但是不能超出4k,最好是0地址),将0x12345678赋给它,如果它的值变成 0x12345678了,就说明写入成功,就是nand flash,写入成功的话就把代码原来的值毁了,还需要将原来的值赋回去,如果没有变成 0x12345678,那它就是nor flash。 /
/
=== FUNCTION ======================================================================
Name: copy_code_to_sdram
Description: 把代码复制到sdram中。
* =====================================================================================
/
void
copy_code_to_sdram (unsigned char src, unsigned char dst, unsigned int len)
{
int i = 0;
if (is_boot_from_norflash())
{
/ 从nor flash 启动的 /
while (i < len)
{
dst[i] = src[i];
i++;
}
}
else
{
/ 从nand flash 启动的 /
//nand_init();
nand_read((unsigned int)src, dst, len);
}
} / ----- end of function copy_code_to_sdram ----- /
/ 对于这个函数,首先应该明确三点:源,目的,长度。如果是从nor flash启动的话,直接dst[i] = src[i]就行了,简单粗暴。如果是从nand 启动的话,就麻烦一点了,需要先初始化nand flash,然后用nand_read函数来读取。如果想用nand_read函数,就需要发地址,发命令,片选等等一大堆nand操作函数,所以一会慢慢写这些函数。 /
/
=== FUNCTION ======================================================================
Name: clear_bss
Description: 清bss段。
=====================================================================================
/
void
clear_bss (void)
{
extern int __bss_start, __bss_end;
int p = &__bss_start;
for (; p < &__bss_end; p++)
{
p = 0;
}
} / ----- end of function clear_bss ----- /
/ 清BSS段,就是把BSS段里面的值都写为0,需要用到链接脚本中的BSS段的起始地址。在c语言中就是如上所示那样调用的。 /
/ 以下几个就是nand flash的操作函数,在裸板程序中都写过了,就不一一分析了。 /
/
=== FUNCTION ======================================================================
Name: nand_init
Description: nand初始化函数,设置一些时间参数等。
* =====================================================================================
/
void
nand_init (void)
{
NFCONF = (0<<12) | (3<<8) | (0<<4);
NFCONT = (1<<4) | (1<<1) | (1<<0);
} /* ----- end of function nand_init ----- /
/
* === FUNCTION ======================================================================
Name: nand_select
Description: 片选函数。
* =====================================================================================
/
void
nand_select (void)
{
NFCONT &= ~(1<<1);
} / ----- end of function nand_select ----- /
/
* === FUNCTION ======================================================================
Name: nand_deselect
Description: 取消片选函数。
* =====================================================================================
/
void
nand_deselect (void)
{
NFCONT |= (1<<1);
} / ----- end of function nand_deselect ----- /
/
* === FUNCTION ======================================================================
Name: nand_cmd
Description: nand中发送命令函数。
* =====================================================================================
/
void
nand_cmd (unsigned char cmd)
{
int i;
NFCMMD = cmd;
for (i = 0; i < 10; i++);
} / ----- end of function nand_cmd ----- /
/ 发送命令函数,就是往 NFCMMD这个寄存器里面写cmd命令就好,加循环就是为了让它多写一会~ /
/
* === FUNCTION ======================================================================
Name: nand_addr
Description: nand中发送地址函数。
* =====================================================================================
/
void
nand_addr (unsigned int addr)
{
int i;
int col, page;
col = addr % 2048;
page = addr / 2048;
NFADDR = col & 0xff;
for (i = 0; i < 10; i++);
NFADDR = (col>>8) & 0xff;
for (i = 0; i < 10; i++);
NFADDR = page & 0xff;
for (i = 0; i < 10; i++);
NFADDR = (page>>8) & 0xff;
for (i = 0; i < 10; i++);
NFADDR = (page>>16) & 0xff;
for (i = 0; i < 10; i++);
} / ----- end of function nand_addr ----- /
/ 发送地址函数,主要是需要好好理解col和page的算法,col是这一页中的第几个数据,用addr % 2048取余运算来做,余数正好就是第几个,page是需要求出是2048的几倍,用除法来做就好了~在计算机中,除法只会保存整数,因为我们定义的数据类型都是int类型的。 /
/
* === FUNCTION ======================================================================
Name: nand_wait_idle
Description: 等待就绪函数。
* =====================================================================================
/
void
nand_wait_idle (void)
{
while (!(NFSTAT & 1));
} / ----- end of function nand_wait_idle ----- /
/
* === FUNCTION ======================================================================
Name: nand_read_data
Description: 读取NFDATA寄存器中的数据函数。
* =====================================================================================
/
unsigned long
nand_read_data (void)
{
return NFDATA;
} / ----- end of function nand_read_data ----- /
/
* === FUNCTION ======================================================================
Name: nand_read
Description: nand读函数。
* =====================================================================================
/
void
nand_read (unsigned int addr, unsigned char buf, unsigned int len)
{
int col = addr % 2048;
int i = 0;
/ 1. 选中芯片 /
nand_select();
while (i < len)
{
/ 2. 发送00命令 /
nand_cmd(0x00);
/ 3. 发送地址 /
nand_addr(addr);
/ 4. 发送30命令 /
nand_cmd(0x30);
/ 5. 等待发送完毕 /
nand_wait_idle();
for (; (col < 2048) && (i < len); col++)
{
buf[i] = nand_read_data();
i++;
addr++;
/ 我用下面这几句话就不对,为什么?其他的都一样。
* buf = nand_read_data();
* buf++;
* addr++;
/
}
col = 0;
}
/ 6. 取消片选 /
nand_deselect();
} / ----- end of function nand_read ----- /
/ 这个函数中不解的就是我在函数中注释那几句,可能是自己的c语言知识不过关吧,先放在这,以后再解决。 /
/ 以下几个是串口函数的操作函数。 /
/
=== FUNCTION ======================================================================
Name: uart0_init
Description: 初始化串口函数。
=====================================================================================
/
void
uart0_init(void)
{
GPHCON = 0xa0;
GPHUP = 0x0c;
ULCON0 = 0x03;
UCON0 = 0x05;
UFCON0 = 0x00;
UMCON0 = 0x00;
UBRDIV0 = UART_BRD;
} / ----- end of function uart0_init ----- /
/-----------------------------------------------------------------------------
等待输入函数,在这个程序中其实不需要。
-----------------------------------------------------------------------------/
unsigned char getc(void)
{
while (!(UTRSTAT0 & (1<<0)));
return URXH0;
}
/-----------------------------------------------------------------------------
输出一个字符的函数。
-----------------------------------------------------------------------------/
void putc(unsigned char c)
{
UTXH0 = c;
while (!(UTRSTAT0 & (1<<2)));
}
/-----------------------------------------------------------------------------
输出字符串函数。
-----------------------------------------------------------------------------/
void puts(char str)
{
int i = 0;
while (str[i])
{
putc(str[i]);
i++;
}
}
/-----------------------------------------------------------------------------
将val以16进制的形式输出。
-----------------------------------------------------------------------------/
void puthex(unsigned int val)
{
int i;
int j;
puts(“0x”);
for (i = 0; i < 8; i++)
{
j = (val >> ((7-i)4)) & 0xf;
if ((j >= 0) && (j <= 9))
putc(‘0’ + j);
else
putc(‘A’ + j - 0xa);
}
}
(三)boot.c 启动代码的第二阶段。
这一段代码就是设置bootloader传给内核的参数,然后启动内核就行了。
(1)首先从nand flash中把内核读入内存中,用nand_read函数即可,但是源,目的,长度是多少呢?我们把内核映像uImage放在了0x00060000的地方,uImage是64字节的头部+真正的内核(zImage),所以源地址是0x00060000+64,读到哪呢?在uImage的64字节头部中,有一个image_head_t结构体,里面重要的参数有两个,一个是in_load(加载地址),另一个是in_ep(入口地址),在启动内核的过程中,如果uboot发现uImage不位于它的加载地址的话,就将把它移到加载地址处,若直接位于加载地址上的话,就不需要移动了,这样就可以减少加载时间,所以我们就直接将它放在加载地址上面,就是0x30008000,读2M肯定够用了,一般内核会剪裁到2M以下,所以直接读2M,其实也可以查看uImage的准确大小,读那么大也行。所以nand_read函数如下所示:
nand_read(0x60000+64, (unsigned char )0x30008000, 0x200000);
(2)那么bootloader和内核是如何传递参数的呢?内核启动的时候,bootloader已经死掉了…所以它肯定是把这些参数放在一个固定的位置,然后内核去这个固定的位置取就行~即所谓的在某个地址(0x30000100),按照某种格式(tag)保存数据。
参照uboot中do_bootm_linux中,设置setup_start_tag();setup_memory_tag();setup_commandline_tag(“…”);setup_end_tag();将这些tag设置好即可。
下面贴图来显示这个tag在内存中的分布:
对于size的大小和next的定位,仔细查看setup.h中的函数定义,就能够分析出来。
(3)跳转执行。
<1>跳到内核的入口地址去执行,即0x30008000。
theKernel = (void () (int, int ,uint))0x30008000;
这个函数在uboot中的原型是:theKernel = (void ()(int, int, uint))ntohl(hdr->ih_ep);
跳到内核的入口地址处。
<2>执行:
theKernel (0, 362, 0x30000100);
函数原型是:void (theKernel) (int zero, int arch,unsigned int params);
其中第一个参数是0,第二个参数是机器ID,2440为362,第三个参数为内核需要的参数的存放地址,即那些tag的存放地址(0x30000100)。
这些其实是参照uboot里面写的,下一篇文章会仔细分析分析uboot的代码。
需要注意的是,bootloader是不依赖任何库函数来执行的,所有的函数比如strlen等等都是需要自己写的,这个也就同时锻炼了自己的c语言能力。其中用到的 setup.h 这个头文件,是直接从uboot里面拷贝过来的,同时简单修改了一点,比如u32,u16的宏定义,里面定义了tag结构体以及设置tag用到的一些宏和函数。应该仔细研究研究这些代码。
/
* =====================================================================================
*
Filename: boot.c
Description: 启动代码,bootloader的第二阶段。
Version: 1.0
Created: 2015年10月27日 15时38分20秒
Revision: none
Compiler: arm-linux-gcc
*
Author: Snoopy (ybx), [email protected]
Organization: TJPU
*
* =====================================================================================
/
#include “setup.h”
extern void uart0_init(void);
extern void nand_read(unsigned int addr, unsigned char buf, unsigned int len);
extern void puts(char str);
extern void puthex(unsigned int val);
static struct tag params;
/-----------------------------------------------------------------------------
strlen函数,需要自己实现。
-----------------------------------------------------------------------------/
int strlen(char str)
{
int i = 0;
while (str[i])
{
i++;
}
return i;
}
/-----------------------------------------------------------------------------
strcpy函数,同样需要自己去实现。
-----------------------------------------------------------------------------/
void strcpy(char dst, char src)
{
while ((dst++ = src++) != ‘\0’);
}
/-----------------------------------------------------------------------------
设置传给内核的参数(tag),这是第一个tag,tag_size这个函数也是需要自己去实现的,
在setup.h中用宏定义的形式实现的。
-----------------------------------------------------------------------------/
static void setup_start_tag()
{
params = (struct tag )0x30000100;
params->hdr.tag = ATAG_CORE;
params->hdr.size = tag_size(tag_core);
params->u.core.flags = 0;
params->u.core.pagesize = 0;
params->u.core.rootdev = 0;
params = tag_next(params);
}
/-----------------------------------------------------------------------------
设置memory_tag。
-----------------------------------------------------------------------------/
static void setup_memory_tag()
{
params->hdr.tag = ATAG_MEM;
params->hdr.size = tag_size(tag_mem32);
params->u.mem.start = 0x30000000; / SDRAM的起始地址 /
params->u.mem.size = 6410241024; / 大小为64M /
params = tag_next(params);
}
/-----------------------------------------------------------------------------
设置commandline_tag。
-----------------------------------------------------------------------------/
static void setup_commandline_tag(char cmdline)
{
int len = strlen(cmdline) + 1;
params->hdr.tag = ATAG_CMDLINE;
params->hdr.size = (sizeof(struct tag_header) + len + 3) >> 2; / 这里要4字节对齐 /
strcpy(params->u.cmdline.cmdline, cmdline);
params = tag_next(params);
}
/-----------------------------------------------------------------------------
最后结束的时候,需要设置这个end_tag。给它赋0即可。
-----------------------------------------------------------------------------/
static void setup_end_tag()
{
params->hdr.tag = ATAG_NONE;
params->hdr.size = 0;
}
/*
* === FUNCTION ======================================================================
Name: main
Description: 这个是主函数,完成启动内核的任务。
* =====================================================================================
/
int
main (void)
{
void (theKernel)(int zero, int arch, unsigned int params);
//volatile unsigned int p = (volatile unsigned int )0x30008000;
/ 0. 帮内核初始化串口,这样就可以有打印信息从串口打印出来,否则内核会卡死在这里。 /
uart0_init();
/ 1. 从NAND FLASH 中把内核读入内存 /
puts(“Copy kernel from nand\n\r”);
nand_read(0x60000+64, (unsigned char )0x30008000, 0x200000);
//puthex(0x1234ABCD);
//puts("\n\r");
//puthex(p);
//puts("\n\r");
/ 2. 设置参数(TAGS)/
puts(“Set boot params\n\r”);
setup_start_tag();
setup_memory_tag();
setup_commandline_tag(“noinitrd root=/dev/mtdbolck3 init=/linuxrc console=ttySAC0”);
setup_end_tag();
/ 3. 跳转执行 /
puts(“Boot the Kernel\n\r”);
theKernel = (void ()(int, int, unsigned int))0x30008000;
theKernel(0, 362, 0x30000100);
/ 正常情况下不会执行到这里,如果执行到这里就是肯定出错了,打印出错信息。 /
puts(“Error!\n\r”);
return -1;
} / ---------- end of function main ---------- /
(四)boot.lds 链接脚本
SECTIONS {
. = 0x33f80000;
.text : {(.text)}
. = ALIGN(4);
.rodata : {(.rodata)}
. = ALIGN(4);
.data : {(.data)}
. = ALIGN(4);
__bss_start = .;
.bss : {(.bss) (COMMON)}
__bss_end = .;
}
(五)Makefile
CC = arm-linux-gcc
LD = arm-linux-ld
AR = arm-linux-ar
OBJCOPY = arm-linux-objcopy
OBJDUMP = arm-linux-objdump
CFLAGS := -Wall -O2
CPPFLAGS := -nostdinc -nostdlib -fno-builtin
objs := start.o init.o boot.o
boot.bin: KaTeX parse error: Expected 'EOF', got '&' at position 13: (objs)
&̲nbsp; &nb…{LD} -Tboot.lds -o boot.elf KaTeX parse error: Expected 'EOF', got '&' at position 8: ^
&̲nbsp; &nb…{OBJCOPY} -O binary -S boot.elf KaTeX parse error: Expected 'EOF', got '&' at position 8: @
&̲nbsp; &nb…{OBJDUMP} -D -m arm boot.elf > boot.dis
%.o:%.c
${CC} $(CPPFLAGS) $(CFLAGS) -c -o $@ KaTeX parse error: Expected 'EOF', got '&' at position 1: &̲lt;
%.…{CC} $(CPPFLAGS) $(CFLAGS) -c -o $@ $<
clean:
rm -f .o .bin .elf .dis
(六)setup.h
/
linux/include/asm/setup.h
Copyright © 1997-1999 Russell King
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
Structure passed to kernel to tell it about the
hardware it’s running on. See linux/Documentation/arm/Setup
for more info.
* NOTE:
This file contains two ways to pass information from the boot
loader to the kernel. The old struct param_struct is deprecated,
but it will be kept in the kernel for 5 years from now
(2001). This will allow boot loaders to convert to the new struct
tag way.
/
#ifndef __ASMARM_SETUP_H
#define __ASMARM_SETUP_H
#define u8 unsigned char
#define u16 unsigned short
#define u32 unsigned long
/*
* Usage:
- do not go blindly adding fields, add them at the end
- when adding fields, don’t rely on the address until
a patch from me has been released
- unused fields should be zero (for future expansion)
- this structure is relatively short-lived - only
guaranteed to contain useful data in setup_arch()
/
#define COMMAND_LINE_SIZE 1024
/ This is the old deprecated way to pass parameters to the kernel /
struct param_struct {
union {
struct {
unsigned long page_size; / 0 /
unsigned long nr_pages; / 4 /
unsigned long ramdisk_size; / 8 /
unsigned long flags; / 12 /
#define FLAG_READONLY 1
#define FLAG_RDLOAD 4
#define FLAG_RDPROMPT 8
unsigned long rootdev; / 16 /
unsigned long video_num_cols; / 20 /
unsigned long video_num_rows; / 24 /
unsigned long video_x; / 28 /
unsigned long video_y; / 32 /
unsigned long memc_control_reg; / 36 /
unsigned char sounddefault; / 40 /
unsigned char adfsdrives; / 41 /
unsigned char bytes_per_char_h; / 42 /
unsigned char bytes_per_char_v; / 43 /
unsigned long pages_in_bank[4]; / 44 /
unsigned long pages_in_vram; / 60 /
unsigned long initrd_start; / 64 /
unsigned long initrd_size; / 68 /
unsigned long rd_start; / 72 /
unsigned long system_rev; / 76 /
unsigned long system_serial_low; / 80 /
unsigned long system_serial_high; / 84 /
unsigned long mem_fclk_21285; / 88 /
} s;
char unused[256];
} u1;
union {
char paths[8][128];
struct {
unsigned long magic;
char n[1024 - sizeof(unsigned long)];
} s;
} u2;
char commandline[COMMAND_LINE_SIZE];
};
/
* The new way of passing information: a list of tagged entries
/
/ The list ends with an ATAG_NONE node. /
#define ATAG_NONE 0x00000000
struct tag_header {
u32 size;
u32 tag;
};
/ The list must start with an ATAG_CORE node /
#define ATAG_CORE 0x54410001
struct tag_core {
u32 flags; / bit 0 = read-only /
u32 pagesize;
u32 rootdev;
};
/ it is allowed to have multiple ATAG_MEM nodes /
#define ATAG_MEM 0x54410002
struct tag_mem32 {
u32 size;
u32 start; / physical start address /
};
/ VGA text type displays /
#define ATAG_VIDEOTEXT 0x54410003
struct tag_videotext {
u8 x;
u8 y;
u16 video_page;
u8 video_mode;
u8 video_cols;
u16 video_ega_bx;
u8 video_lines;
u8 video_isvga;
u16 video_points;
};
/ describes how the ramdisk will be used in kernel /
#define ATAG_RAMDISK 0x54410004
struct tag_ramdisk {
u32 flags; / bit 0 = load, bit 1 = prompt /
u32 size; / decompressed ramdisk size in kilo bytes /
u32 start; / starting block of floppy-based RAM disk image /
};
/ describes where the compressed ramdisk image lives (virtual address) /
/
* this one accidentally used virtual addresses - as such,
* its depreciated.
/
#define ATAG_INITRD 0x54410005
/ describes where the compressed ramdisk image lives (physical address) /
#define ATAG_INITRD2 0x54420005
struct tag_initrd {
u32 start; / physical start address /
u32 size; / size of compressed ramdisk image in bytes /
};
/ board serial number. “64 bits should be enough for everybody” /
#define ATAG_SERIAL 0x54410006
struct tag_serialnr {
u32 low;
u32 high;
};
/ board revision /
#define ATAG_REVISION 0x54410007
struct tag_revision {
u32 rev;
};
/ initial values for vesafb-type framebuffers. see struct screen_info
* in include/linux/tty.h
/
#define ATAG_VIDEOLFB 0x54410008
struct tag_videolfb {
u16 lfb_width;
u16 lfb_height;
u16 lfb_depth;
u16 lfb_linelength;
u32 lfb_base;
u32 lfb_size;
u8 red_size;
u8 red_pos;
u8 green_size;
u8 green_pos;
u8 blue_size;
u8 blue_pos;
u8 rsvd_size;
u8 rsvd_pos;
};
/ command line: \0 terminated string /
#define ATAG_CMDLINE 0x54410009
struct tag_cmdline {
char cmdline[1]; / this is the minimum size /
};
/ acorn RiscPC specific information /
#define ATAG_ACORN 0x41000101
struct tag_acorn {
u32 memc_control_reg;
u32 vram_pages;
u8 sounddefault;
u8 adfsdrives;
};
/ footbridge memory clock, see arch/arm/mach-footbridge/arch.c /
#define ATAG_MEMCLK 0x41000402
struct tag_memclk {
u32 fmemclk;
};
struct tag {
struct tag_header hdr;
union {
struct tag_core core;
struct tag_mem32 mem;
struct tag_videotext videotext;
struct tag_ramdisk ramdisk;
struct tag_initrd initrd;
struct tag_serialnr serialnr;
struct tag_revision revision;
struct tag_videolfb videolfb;
struct tag_cmdline cmdline;
/
* Acorn specific
/
struct tag_acorn acorn;
/
* DC21285 specific
*/
struct tag_memclk memclk;
} u;
};
struct tagtable {
u32 tag;
int (*parse)(const struct tag *);
};
#define __tag attribute((unused, section(".taglist")))
#define __tagtable(tag, fn) \
static struct tagtable _tagtable##fn __tag = { tag, fn }
#define tag_member_present(tag,member) \
((unsigned long)(&((struct tag )0L)->member + 1) \
<= (tag)->hdr.size * 4)
#define tag_next(t) ((struct tag )((u32 )(t) + (t)->hdr.size))
#define tag_size(type) ((sizeof(struct tag_header) + sizeof(struct type)) >> 2)
#define for_each_tag(t,base) \
for (t = base; t->hdr.size; t = tag_next(t))
/
Memory map description
/
#define NR_BANKS 8
struct meminfo {
int nr_banks;
unsigned long end;
struct {
unsigned long start;
unsigned long size;
int node;
} bank[NR_BANKS];
};
extern struct meminfo meminfo;
#endif
上一篇:ARM的启动和中断向量表
下一篇:u-boot-1.1.6源码分析
相关热门文章
给主人留下些什么吧!~~
评论热议