自己动手写一个简单的bootloader
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), 471685488@qq.com
* 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 = (2*m*Fin) / (p*2^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,64*1024*1024就是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), 471685488@qq.com
* 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), 471685488@qq.com
* 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 = 64*1024*1024; /* 大小为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: $(objs)
${LD} -Tboot.lds -o boot.elf $^
${OBJCOPY} -O binary -S boot.elf $@
${OBJDUMP} -D -m arm boot.elf > boot.dis
%.o:%.c
${CC} $(CPPFLAGS) $(CFLAGS) -c -o $@ $<
%.o:%.S
${CC} $(CPPFLAGS) $(CFLAGS) -c -o $@ $<
clean:
rm -f *.o *.bin *.elf *.dis
(六)setup.h
/*
* linux/include/asm/setup.h
*
* Copyright (C) 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