自己动手写一个简单的bootloader

自己动手写一个简单的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
<script>window._bd_share_config={"common":{"bdSnsKey":{},"bdText":"","bdMini":"2","bdMiniList":false,"bdPic":"","bdStyle":"0","bdSize":"16"},"share":{}};with(document)0[(getElementsByTagName('head')[0]||body).appendChild(createElement('script')).src='http://bdimg.share.baidu.com/static/api/js/share.js?v=89860593.js?cdnversion='+~(-new Date()/36e5)];</script>
阅读(169) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~
评论热议
  • 7
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值