(六) 自己写简单的u-boot

来源:互联网 发布:用javascript输出菱形 编辑:程序博客网 时间:2024/04/30 00:54

前言:想想 uboot 的代码量,我们说自己写一个 bootloader 是不是口出狂言了?然而并没有,bootloader 的唯一目的只有一个,那便是启动内核。内核就是一大段可执行程序,我们只要跳转到它的入口地址去执行不就OK? 所以,写一个简单的 bootloader 并不困难。

    现在来思考一下,目的是启动内核,那么内核在哪里?刚上电的时候,内核肯定是位于 nandflash 里的,我们得具备初始化 nandflash 并读取的功能。那么,我们将内核去读到 sdram 然后跳转到入口地址去执行就够了么?然而还不行,bootloader 启动内核时还需要给内核传递一些信息(tag),两个必不可少的信息是 内存标记和命令行参数。信息传递完,那么内核就一定能起得来么?未必,启动内核时,我们还会传递一个机器ID,如果Uboot传递进去的机器ID和内核支持的机器ID不匹配,那么内核也无法启动。想必,分析过 uboot 的同学对这个并不陌生。

一、自己写 bootloader ,或者移植 uboot ,需要知道的一些信息

  • 1、内核在 nandflash 中的地址,是 uImage 还是 zImage ,我这里用的是 uImage ,在 nandflash 里的 0x60000处,uImage 是用来支持 uboot 启动的,不过我们自己写的话用 uImage 也没关系,因为 uImage 就是在 zImage 的基础上加了一个 64 字节的头部,用来告诉 uboot 内核的信息,比如版本号、加载地址、链接地址、编译时间等。uboot 会获取到这个头部,干一些事情。我们自己写的话,就不需要头部了,从NAND FLASH里把内核读入内存,直接跳到 uImage 启示地址 + 64 的地方去读就OK 。

  • 2、内核的链接地址:0x30008000 ,这个连接地址实际上应该说是“真正”内核的链接地址,如果是 uImage ,不应该包含它的头部。知道什么意思了吧,使用 uImage 时,我们应当将整个 uImage 拷贝到 0x30008000 - 64 的地方去,也就是 0x30007fc0 。

  • 3、bootloader 的连接地址:uboot的链接地址一般为 0x3ff80000 处,我们自己写的也用这个地址好了。说到这里,必须得提一下,对于2440来说,不管是 nand 启动,还是 nor 启动,刚上电的时候我们的 bootloader 并不是运行在链接地址处,那么这时候跑在非链接地址处的这些指令就得有特殊要求——位置无关码<了解更多点我跳转>。我个人做法,在将自身代码拷贝到链接地址处之前,能用汇编写的尽量不用C写,因为用汇编我们自己可以分得清哪些是位置无关码,迫不得已的情况下用C写,写完看反汇编,看看那些跳转指令是否依赖于当前PC值。
  • 4、标记 tag 的地址,2440 常用的做法是放在 0x30000100 处,我们同样也放在这里。其实放那都行,地址会作为参数传递给内核的。tag的放置方法,可以参考 uboot ,必须以 ATAG_CORE 类型的开头,以 ATAG_NONE 类型的结尾。
  • 5、启动内核时传递给内核的参数,3个,第一个默认0,第三个是前边提到的 tag 的地址,第二个是机器ID。内核中所有支持的“机器”或者称作开发板、单板都用一个 MACHINE_START 宏来定义,这个宏的作用就是填充一个 machine_desc 类型的结构体,填充的过程中会对它的 .nr 成员赋值,这个 nr 就是所谓的机器ID。举个例子:
MACHINE_START(HALIBUT,"Halibut Board (QCT SURF7200A)")        .boot_params      = 0x10000100,        .map_io           = halibut_map_io,        .init_irq         = halibut_init_irq,        .init_machine     = halibut_init,        .timer            = &msm_timer,    MACHINE_END   struct machine_desc __mach_desc_HALIBUT{    __used                                                              __attribute__((__section__(".arch.info.init")))= {        .nr               = MACH_TYPE_HALIBUT,                      .name             = "HalibutBoard (QCT SURF7200A)",        .boot_params      = 0x10000100,        .map_io           = halibut_map_io,        .init_irq         = halibut_init_irq,        .init_machine     = halibut_init,        .timer            = &msm_timer,    };   

MACH_TYPE_HALIBUT 这个宏,搜遍代码也没找到,我在 linux-2.6.32.2\arch\arm\tools\mach-types 里发现了一点踪迹,应该是在这里的信息生成的宏吧。对于 JZ2440 来说,机器ID定义的是 362 。这里边也有个 boot_params 和前边实际tag存放的地址要一致。

  • 6、内核刚启动时会打印一些信息,但是那时候内核自己并不会初始化串口,也为了方便自己调试,bootloader里需要初始化串口。

二、概括一下自己写bootloader的步骤

根据前面的分析大概知道了bootloader需要干什么

这里写图片描述

start.S

#define S3C2440_MPLL_200MHZ     ((0x5c<<12)|(0x01<<4)|(0x02))#define S3C2440_MPLL_400MHZ     ((0x5c<<12)|(0x01<<4)|(0x01))#define MEM_CTL_BASE    0x48000000.text.global _start_start:/* 1. 关看门狗 */    ldr r0, =0x53000000    mov r1, #0    str r1, [r0]/* 2. 设置时钟 */    ldr r0, =0x4c000014    //mov r1, #0x03;            // FCLK:HCLK:PCLK=1:2:4, HDIVN=1,PDIVN=1    mov r1, #0x05;            // FCLK:HCLK:PCLK=1:4:8, HDIVN=4,PDIVN=1    str r1, [r0]    /* 如果HDIVN非0,CPU的总线模式应该从“fast bus mode”变为“asynchronous bus mode” */    mrc p15, 0, r1, c1, c0, 0       /* 读出控制寄存器 */     orr r1, r1, #0xc0000000         /* 设置为“asynchronous bus mode” */    mcr p15, 0, r1, c1, c0, 0       /* 写入控制寄存器 */    /* MPLLCON = S3C2440_MPLL_400MHZ */    ldr r0, =0x4c000004    ldr r1, =S3C2440_MPLL_400MHZ    str r1, [r0]    /* 启动ICACHE */    mrc p15,0,r0,c1,c0,0 @ read control reg    orr r0, r0,#(1<<12)    mcr p15,0,r0,c1,c0,0 @write it back    /* 3. 初始化SDRAM */    ldr r0, =MEM_CTL_BASE    adr r1, sdram_config     /* sdram_config的当前地址 */    add r3, r0, #(13*4)1:    ldr r2, [r1], #4    str r2, [r0], #4    cmp r0, r3    bne 1b    /* 4. 设置栈 */    ldr sp, =0x34000000/* 4. 重定位 : 把bootloader本身的代码从Nor_flash或者Nand_flash复制到SDRAM *//* copy_code_to_sdram(unsigned char *src, unsigned char *dest, unsigned int len) */    mov r0, #0 //第一个参数起始地址:r0:0    ldr r1, =_start //第二个参数链接地址:r1:链接地址 _start=0X33f80000    ldr r2, =__bss_start    sub r2, r2, r1 //r2:__bss_start - _start(参考链接脚本)0X33f80000开始存放    bl copy_code_to_sdram/* 5. 清楚bss段 */     bl clear_bss/* 6. 执行main */    ldr lr, =halt    ldr pc, =mainhalt:    b haltsdram_config:    .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

/* NAND FLASH控制器 */#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))/* GPIO */#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)#define TXD0READY   (1<<2)void nand_read(unsigned int addr, unsigned char *buf, unsigned int len);void nand_init(void);/* Norflash可以像内存那样读,但是不能写 *//* 我们先写一个值,看能不能成功。最后将该地址处的值还原 *//* 如果成功就证明是Nand_Flash启动,否则就是NorFlash启动 */int isBootFromNorFlash(void){    volatile int *p = (volatile int *)0;    int val;    val = *p;    *p = 0x12345678;    if (*p == 0x12345678)    {        /* 写成功, 是nand启动 */        *p = val;        return 0;    }    else    {        /* NOR不能像内存一样写 */        return 1;    }}void copy_code_to_sdram(unsigned char *src, unsigned char *dest, unsigned int len){       int i = 0;    /* 如果是NOR启动 */    if (isBootFromNorFlash())    {        while (i < len)        {            dest[i] = src[i];            i++;        }    }    else    {        nand_init();        nand_read((unsigned int)src, dest, len);    }}void clear_bss(void){    extern int __bss_start, __bss_end;    int *p = &__bss_start;    for (; p < &__bss_end; p++)        *p = 0;}void nand_init(void){#define TACLS   0#define TWRPH0  1#define TWRPH1  0    /* 设置时序 */    NFCONF = (TACLS<<12)|(TWRPH0<<8)|(TWRPH1<<4);    /* 使能NAND Flash控制器, 初始化ECC, 禁止片选 */    NFCONT = (1<<4)|(1<<1)|(1<<0);  }void nand_select(void){    NFCONT &= ~(1<<1); }void nand_deselect(void){    NFCONT |= (1<<1);   }void nand_cmd(unsigned char cmd){    volatile int i;    NFCMMD = cmd;    for (i = 0; i < 10; i++);}void nand_addr(unsigned int addr){    unsigned int col  = addr % 2048;    unsigned int page = addr / 2048;    volatile int i;    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++);   }void nand_wait_ready(void){    while (!(NFSTAT & 1));}unsigned char nand_data(void){    return NFDATA;}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. 发出读命令00h */        nand_cmd(0x00);        /* 3. 发出地址(分5步发出) */        nand_addr(addr);        /* 4. 发出读命令30h */        nand_cmd(0x30);        /* 5. 判断状态 */        nand_wait_ready();        /* 6. 读数据 */        for (; (col < 2048) && (i < len); col++)        {            buf[i] = nand_data();            i++;            addr++;        }        col = 0;    }    /* 7. 取消选中 */           nand_deselect();}#define PCLK            50000000    // init.c中的clock_init函数设置PCLK为50MHz#define UART_CLK        PCLK        //  UART0的时钟源设为PCLK#define UART_BAUD_RATE  115200      // 波特率#define UART_BRD        ((UART_CLK  / (UART_BAUD_RATE * 16)) - 1)/* * 初始化UART0 * 115200,8N1,无流控 */void uart0_init(void){    GPHCON  |= 0xa0;    // GPH2,GPH3用作TXD0,RXD0    GPHUP   = 0x0c;     // GPH2,GPH3内部上拉    ULCON0  = 0x03;     // 8N1(8个数据位,无较验,1个停止位)    UCON0   = 0x05;     // 查询方式,UART时钟源为PCLK    UFCON0  = 0x00;     // 不使用FIFO    UMCON0  = 0x00;     // 不使用流控    UBRDIV0 = UART_BRD; // 波特率为115200}/* * 发送一个字符 */void putc(unsigned char c){    /* 等待,直到发送缓冲区中的数据已经全部发送出去 */    while (!(UTRSTAT0 & TXD0READY));    /* 向UTXH0寄存器中写入数据,UART即自动将它发送出去 */    UTXH0 = c;}void puts(char *str){    int i = 0;    while (str[i])    {        putc(str[i]);        i++;    }}void puthex(unsigned int val){    /* 0x1234abcd */    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

#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;void setup_start_tag(void){    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);}void setup_memory_tags(void){    params->hdr.tag = ATAG_MEM;    params->hdr.size = tag_size (tag_mem32);    params->u.mem.start = 0x30000000;    params->u.mem.size  = 64*1024*1024;    params = tag_next (params);}int strlen(char *str){    int i = 0;    while (str[i])    {        i++;    }    return i;}void strcpy(char *dest, char *src){    while ((*dest++ = *src++) != '\0');}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;    strcpy (params->u.cmdline.cmdline, cmdline);    params = tag_next (params);}void setup_end_tag(void){    params->hdr.tag = ATAG_NONE;    params->hdr.size = 0;}int main(void){    void (*theKernel)(int zero, int arch, unsigned int params);    volatile unsigned int *p = (volatile unsigned int *)0x30008000;    /* 8. 帮内核设置串口: 内核启动的开始部分会从串口打印一些信息,但是内核一开始没有初始化串口 */    uart0_init();    /* 9. 从NAND FLASH里把内核读入内存 */    /* 0x60000+64(64字节的头部=>我们直接跳过头部读取zimage就行了) */    /* 内核地址0x30008000(内核中配置好的地址)nand_read的实现 */    puts("CZG!!!");    puts("\r\n");    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");    /* 10. 设置参数 */    puts("Set boot params\n\r");    /* 下面几个函数,都向地址内写入了 */    /* (1)参数代号 */    /* (2)本参数所占内存大小 */    /* (3)参数内容 */    /* (4)每个参数设置最后都指向了下一参数设置地址 */    setup_start_tag();    setup_memory_tags();    setup_commandline_tag("noinitrd root=/dev/mtdblock3 init=/linuxrc console=ttySAC0");    setup_end_tag();    /* 3. 跳转执行,启动内核 */    puts("Boot kernel\n\r");    theKernel = (void (*)(int, int, unsigned int))0x30008000;    theKernel(0, 362, 0x30000100);      /*      *  mov r0, #0     *  ldr r1, =362     *  ldr r2, =0x30000100     *  mov pc, #0x30008000      */    puts("Error!\n\r");    /* 如果一切正常, 不会执行到这里 */    return -1;}

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   0x00000000struct tag_header {    u32 size;    u32 tag;};/* The list must start with an ATAG_CORE node */#define ATAG_CORE   0x54410001struct tag_core {    u32 flags;      /* bit 0 = read-only */    u32 pagesize;    u32 rootdev;};/* it is allowed to have multiple ATAG_MEM nodes */#define ATAG_MEM    0x54410002struct tag_mem32 {    u32 size;    u32 start;  /* physical start address */};/* VGA text type displays */#define ATAG_VIDEOTEXT  0x54410003struct 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    0x54410004struct 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    0x54420005struct 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 0x54410006struct tag_serialnr {    u32 low;    u32 high;};/* board revision */#define ATAG_REVISION   0x54410007struct tag_revision {    u32 rev;};/* initial values for vesafb-type framebuffers. see struct screen_info * in include/linux/tty.h */#define ATAG_VIDEOLFB   0x54410008struct 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    0x54410009struct tag_cmdline {    char    cmdline[1]; /* this is the minimum size */};/* acorn RiscPC specific information */#define ATAG_ACORN  0x41000101struct 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 0x41000402struct 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_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 8struct meminfo {    int nr_banks;    unsigned long end;    struct {        unsigned long start;        unsigned long size;        int           node;    } bank[NR_BANKS];};extern struct meminfo meminfo;#endif

Makefile

CC      = arm-linux-gccLD      = arm-linux-ldAR      = arm-linux-arOBJCOPY = arm-linux-objcopyOBJDUMP = arm-linux-objdumpCFLAGS      := -Wall -O2  -fno-builtin #-Wall:打开警告信息 -O2:2级优化(常用) -fno-builtin:不使用内建函数(如putchar)CPPFLAGS    := -nostdinc  -nostdlib #-nostdinc:不在标准系统目录中搜索头文件,只在-I指定的目录中搜索                                                      #-nostdlib:不连接标准启动文件和标准库文件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 *.dis *.elf

boot.lds

SECTIONS {    . = 0x33f80000;/*设置当前运行地址为0x33f80000*/    .text          :   { *(.text) }/*所有输入文件的代码段*/    . = ALIGN(4);    .rodata  : {*(.rodata)} /*ALIGN(4),表示运行地址4字节对齐。*/    . = ALIGN(4);    .data  : { *(.data) }    . = ALIGN(4);    __bss_start = .;    .bss   : { *(.bss)  *(COMMON) }    __bss_end = .;}

启动LOG

Copy kernel from nand  0x1234ABCD  0xE1A00000  Set boot params  Boot kernel  Uncompressing Linux...................................................................................................................... done, booting the kernel.  Linux version 2.6.22.6 (book@book-desktop) (gcc version 3.4.5) #1 Fri Aug 23 15:33:35 CST 2013  CPU: ARM920T [41129200] revision 0 (ARMv4T), cr=c0007177  Machine: SMDK2440  Memory policy: ECC disabled, Data cache writeback  CPU S3C2440A (id 0x32440001)  S3C244X: core 200.000 MHz, memory 100.000 MHz, peripheral 50.000 MHz  S3C24XX Clocks, (c) 2004 Simtec Electronics  **************************************************
原创粉丝点击