[Linux驱动开发] miniboot代码分析

U-boot启动的代码分析:

boot.lds 规定了所有编译后的.o 文件的链接方式,所有的.c 文件编译后都会生成一个.o 文件,.o 文件然后再按lds 规定的顺序将各自相同的数据段整合到一起生成一个ELF 文件。将ELF的头拿掉就生成一个.系统可执行的.bin 文件

当系统上电的时候,CPU中的固件(在CPU出厂前就已经烧录到CPU中,用于支持特定型号的Nand Flash)会将我们已经烧录到Nand Flash中的uboot.bin的前4K代码拷贝到CPU片内的SRAM中执行。

这前4K代码会执行如下任务

1>进入SVC模式

2>关闭看门狗

3>屏蔽所以的中断

4>设置CPU的速度和频率

5>关闭CPU内部的MMU和CACHE

6>初始化SDRAM

7>把Nand Flash中的uboot后4K的代码拷贝到SDRAM中

8>清.bss段

9>跳入SDRAM中的真正的第一个C函数start_armboot()

首先我们要了解的框架来自于uboot.lds,boot.lds是告诉编译器这段该以怎样的顺序链接

SECTIONS

{

        . = 0x33F80000;    //左边的.表示设置当前属性

        __start = . ;        //右边的.表示当前链接的地址

        .text :   //text指代码段

        {

                start.o (.text)     //这里把start.o放在第一位就表示把start.s编译时放在最开始,这就是为什么把uboot烧到起始位置上它肯定运行的是start.s

                clock.o (.text)

                sdram.o (.text)

                nand.o (.text)

                * (.text)

        }

        . = ALIGN(4);       //前面的“.”代表当前值,是计算一个当前的值,是计算上面占用的整个空间,再加一个单元就表示它现在的位置,按四个字节对齐

        .rodata :      //只读数据段

        {

                * (.rodata)

        }

        . = ALIGN(4);

        .data :      //数据段,全局变量

        {

                * (.data)

        }

        . = ALIGN(4);

        __bss_start = .;   //bss表示归零段

        .bss :

        {

                * (.bss)

        }

        . = ALIGN(4);

        __bss_end = .;

        __end = .;

}

 

2、初始化的各文件

2.1> start.S

globl _start

_start:

/* 1. 硬件相关的设置 */

/*1.1 设置CPU的工作模式为SVC,cpsr[4:0]=10011;cpsr[7:5]=110,CPSR[7:0]=0x11010011*/

        mrs r0 , cpsr                  //msr:将状态寄存器CPSR&SPSR读出到通用寄存器

        bic r0 , r0 , #0x1f             //bic:位清零,r0&=~(0x1f)r0[4:0]=0x0

        orr r0 , r0 , #0xd3            //orr:逻辑或,r0|=(0xd3)r0[7:0]=0x11010011

        msr cpsr , r0             //msr:将通用寄存器读出到状态寄存器CPSR&SPSR

/*1.2 关闭看门狗,否则在U-boot启动过程中,CPU将不断重启*/       

        ldr r0, =0x53000000           //ldr:从内存中将一个32位的字读取到目标寄存器,WTCON=0X53000000

        ldr r1, =0

        str r1, [r0]               //str:将一个32位的字数据写入到指令中指定的内存单元,将r1保存到地址[r0]中,[r0]=r1

/*1.3 关闭中断*/

        ldr r0 , =0x4a000008          //主中断屏蔽寄存器(32,每位对应一个中断)INTMASK:0x4A000008,复位值0xFFFFFFFF屏蔽所有中断源

        ldr r1 , =0xffffffff

        str r1 , [r0]

        ldr r0 , =0x4a00001c          //次中断屏蔽寄存器(32,[14:0]有效)INTSUBMSK:0x4A00001c,复位值0x7FFF

        ldr r1 , =0x7fff

        str r1 , [r0]

/*1.4. 代码中的c0c1c7c8都是ARM920T的协处理器CP15的寄存器。其中c7cache控制寄存器,c8TLB控制寄存器*/

      /*使数据cache与指令cache无效*/

        mov       r0, #0

        mcr p15, 0, r0, c7, c7, 0            /* c7写入0将使ICacheDCache无效*/

        mcr p15, 0, r0, c8, c7, 0            /* c8写入0将使TLB失效 ,协处理器*/ 

        /*1.9. disable MMU stuff and caches*/

        mrc p15, 0, r0, c1, c0, 0            /*  读出控制寄存器到r0中  */

        bic  r0, r0, #0x00002300   @ clear bits 13, 9:8 (--V- --RS)

        bic  r0, r0, #0x00000087   @ clear bits 7, 2:0 (B--- -CAM)

        orr   r0, r0, #0x00000002   @ set bit 2 (A) Align

        orr   r0, r0, #0x00001000   @ set bit 12 (I) I-Cache

        mcr p15, 0, r0, c1, c0, 0            /*  保存r0到控制寄存器  */

 /*1.5 设置堆栈.判断是nor启动还是nand启动初始化sp指针*/

/* NAND启动时,片内内存从0开始, sp=4096 */

/* NOR启动时,片内内存从0x40000000开始, sp=0x40000000+4096 */

        ldr r1 ,=0 

        str r1, [r1]    /* 0地址写入0    */

        ldr r1, [r1]   /* 0地址读出数据 */

        cmp r1, #0

        ldreq sp, =4096

        ldrne sp, =(0x40000000+4096)

/*1.6 时钟初始化*/

        bl clock_init

/*1.7. 初始化SDRAM */

        bl sdram_init

/*1.8. 初始化nand */

        bl nand_init

 

/* 2.1 重定位flash里把程序本身复制到sdram里去 */

        relocate:               /* relocate U-Boot to RAM       */

        mov r0, #0

        ldr r1, =__start

        ldr r2, =__bss_start

        sub r2, r2, r1

        bl CopyCode2Ram

        led1:

        ldr r0,=0x56000050

        ldr r1,=0x5500

        str r1,[r0]

        ldr r0,=0x56000054

        ldr r1,=0xef

        str r1,[r0]

   

/* 2.2. bss段 */

        ldr r0,= __bss_start

        ldr r1, =__bss_end

        cmp r0, r1

        beq setup_stack

        mov r2, #0

        clear_loop:

        str r2, [r0], #4

        cmp r0, r1

        bne clear_loop

        led2:

        ldr r0,=0x56000050

        ldr r1,=0x5500

        str r1,[r0]

        ldr r0,=0x56000054

        ldr r1,=0xdf

        str r1,[r0]

/* 2.3. 设置栈调用c函数 */

        setup_stack:

/* 前面已经设置sp, 这里不用再设置 */

        ldr pc, =main    /* 跳到sdram里去 */

 

2.2clock.c

#define CLKDIVN (*((volatile unsigned long *)0x4C000014))  

#define MPLLCON (*((volatile unsigned long *)0x4C000004))  

void clock_init(void)

{

/*1.设置CLKDIVN*/

        CLKDIVN = 5;          /* 设置CLKDIVN5,就是讲HDIVN设置为二进制的10,由于CAMDIVN[9]没有被改变过,取默认值0,因此HCLK=FCLK/4.PDIVN被设置为1,因此PLCK=HCLK/2.因此分频比FCLK:HCLK:PCLK=1:4:8 */

/*2.将总线模式改为异步总线模式*/

__asm__(

        "mrc p15, 0, r1, c1, c0, 0\n"

        "orr r1, r1, #0xc0000000\n"

        "mcr p15, 0, r1, c1, c0, 0\n"

);

/*3.设置MPLLCON*/

        MPLLCON=0x5c011;

}

2.3sdram.c

void sdram_init(void)

{

        volatile unsigned long *p = (volatile unsigned long *)0x48000000; /* BWSCON 0x48000000 */

        p[0] = 0x22011110;  /* BWSCON */

        p[1] = 0x700;       /* BANKCON0 */

        p[2] = 0x700;       /* BANKCON1 */

        p[3] = 0x700;       /* BANKCON2 */

        p[4] = 0x700;       /* BANKCON3 */

        p[5] = 0x700;       /* BANKCON4 */

        p[6] = 0x700;       /* BANKCON5 */

        p[7] = 0x18005;     /* BANKCON6 */

        p[8] = 0x18005;     /* BANKCON7 */

        p[9] = 0x8c04f4;    /* REFRESH: Refresh count = 2^11 + 1 - 100x7.8125 = 1268 */

        p[10] = 0xB1;       /* BANKSIZE */

        p[11] = 0x30;       /* MRSRB6 */

        p[12] = 0x30;       /* MRSRB7 */

}

2.4nand.c

#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 long *)0x4E000020))

static void nand_select(void)

{

        NFCONT &= ~(1<<1);

}

static void nand_deselect(void)

{

        NFCONT |= (1<<1);

}

static void nand_cmd(int cmd)

{

        NFCMMD= cmd;

}

static void wait_ready(void)

{

while(!(NFSTAT & 1)); 

}

        static void nand_reset(void)

{

/*1,使能芯片*/

nand_select();

/*2,写复位0xff命令*/

nand_cmd(0xff);

/*3.等待忙状态*/

wait_ready();

/*4,关闭芯片*/

nand_deselect();

}

/*nand 初始化函数*/

void nand_init(void)

{

/*1.设置时序*/

        #define TACLS   2 

        #define TWRPH0  1 

        #define TWRPH1  0

        NFCONF = (TACLS<<12)|(TWRPH0<<8)|(TWRPH1<<4);

/*2.使能NAND Flash控制器初始化ECC, 禁止片选 */

       NFCONT = (1<<4)|(1<<1)|(1<<0);

/*3.reset*/

        nand_reset();

}

static void nand_addr(unsigned int addr)

{

        int i;

        int col, page;

        col = addr & 2047;

        page = addr / 2048;

        NFADDR = col & 0xff; /* Column Address A0~A7 */

        for(i=0; i<10; i++);

        NFADDR = (col >> 8) & 0x0f; /* Column Address A8~A11 */

        for(i=0; i<10; i++);

        NFADDR = page & 0xff; /* Row Address A12~A19 */

        for(i=0; i<10; i++);

        NFADDR = (page >> 8) & 0xff; /* Row Address A20~A27 */

        for(i=0; i<10; i++);

        //NFADDR = (page >> 16) & 0x03; /* Row Address A28~A29 */

        NFADDR = (page >> 16) & 0x01; /* Row Address A28 */

        for(i=0; i<10; i++);

}

static unsigned char nand_get_data(void)

{

        return NFDATA;

}

void nand_read(unsigned char *buf, unsigned long start_addr, int size)

{

        int i ,j ;

        int offset;

/*1,选中芯片*/

        nand_select();

        for(i=start_addr;i<(start_addr+size);)

        {

                offset = start_addr & 2047 ;

/*2.发出read0命令*/

                nand_cmd(0);

/*3,发地址*/

                nand_addr(i);

/*4,发出read30命令*/

                nand_cmd(0x30);

/*5,等待忙状态*/

                wait_ready();

/*6,读数据*/

                for(j=0;j<(2048-offset);j++,i++)

                {

                       *buf = nand_get_data();

                       buf++;

                }

        }

/*7.关闭芯片*/

        nand_deselect();

}

 

static int bBootFrmNORFlash(void)

{

        volatile unsigned int *pdw = (volatile unsigned int *)0;

        unsigned int dwVal;

        dwVal = *pdw;       

        *pdw = 0x12345678;

        if (*pdw != 0x12345678)

        {

                return 1;

        }

        else

        {

                *pdw = dwVal;

                return 0;

        }

}

 

int CopyCode2Ram(unsigned long start_addr, unsigned char *buf, int size)

{

  unsigned int *pdwDest;

  unsigned int *pdwSrc;

  int i;

  if (bBootFrmNORFlash())

  {  

    pdwDest = (unsigned int *)buf;

    pdwSrc  = (unsigned int *)start_addr;

    // 从 NOR Flash启动 

    for (i = 0; i < size / 4; i++)

    {

        pdwDest[i] = pdwSrc[i];

    }    

    return 0;

  }

  else

  {

nand_init();

    nand_read(buf, start_addr, size );

return 0;

  }

}

 

2.5main.c

int main(void)

{

        void (*theKernel)(int zero, int machine_id, int params_addr);

/* 0. 初始化串口内核启动时会用到串口输出一些信息 */

        uart_init();

        puts("============MINI_BOOT for MY2440============\n\rCopy kernel for flash to sdram ...\n\r");

/* 1. NAND FLASH里把内核读到SDRAM里去 */

        nand_read(0x30108000, 0x60000, 0x300000);

        puts("OK\n\r");

/* 2. 设置参数 */

        puts("Set parametes ...");

        set_params();

        puts("OK\n\r");

/* 3. 启动内核 */

        puts("Boot kernel ...\n\r");

        theKernel = 0x30108040;

        theKernel(0, 362, 0x30000100); 

        puts("\nerror!\n\r");

        return 0;

}

 

2.6uart.c

#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 UBRDIV0    (*((volatile unsigned long *)0x50000028))

#define UTXH0    (*((volatile unsigned char *)0x50000020))

#define URXH0    (*((volatile unsigned char *)0x50000024))

#define GPHCON    (*((volatile unsigned long *)0x56000070))

void putc(char c)

{

/* 如果上一个数据还没有发送出去等待 */

        while ((UTRSTAT0 & (1<<2)) == 0);

        UTXH0 = c;

}

char getc(void)

{

/* 如果没有接收到数据等待 */

        while ((UTRSTAT0 & (1<<0)) == 0);

        return URXH0;

}

void puts(char *str)

{

        int i = 0;

        while (str[i])

        {

                putc(str[i]);

                i++;

        }

}

void uart_init(void)

{

        GPHCON &= ~0xff;

        GPHCON |= 0xA0;

        ULCON0 = 0x03;  /* 8n1 */

        UCON0  = 0x05;  /* 查询方式 */

        UFCON0 = 0;  /* 不使用FIFO */

        UMCON0 = 0;  /* 不使用流控 */

/* 设置波特率 */

/* UBRDIV0 = (int)(PCLK / (bps x 16) ) 1

 *         = 50,000,000 / (115200 x 16) - 1

 *         = 26

 * 真正的波特率 = PCLK / (UBRDIV0 + 1) / 16 

 *              = 115740.7

 */

        UBRDIV0 = 26;

// puts("uart_init ok!!\n\r");

}

 

2.7、params.c

#include "setup.h"

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); // = params + params->hdr.size

}

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(const char * s)
{
 const char *sc;

 for (sc = s; *sc != '\0'; ++sc)
  /* nothing */;
 return sc - s;
}

char * strcpy(char * dest,const char *src)
{
 char *tmp = dest;

 while ((*dest++ = *src++) != '\0')
  /* nothing */;
 return tmp;
}

void setup_commandline_tag(void)
{
 char *p = "console=ttySAC0,115200 root=/dev/nfs nfsroot=192.168.7.106:/home/terryyuan/107fs,proto=tcp,nfsvers=3,nolock ip=192.168.7.233:192.168.7.106:192.168.7.1:255.255.255.0::eth0:off";
 
 params->hdr.tag = ATAG_CMDLINE;
 params->hdr.size =
  (sizeof (struct tag_header) + strlen (p) + 1 + 3) >> 2;

 strcpy(params->u.cmdline.cmdline, p);

 params = tag_next (params);
}

void setup_end_tag ()
{
 params->hdr.tag = ATAG_NONE;
 params->hdr.size = 0;
}

void set_params(void)
{
 setup_start_tag ();                     

 setup_memory_tags();                   
 
 setup_commandline_tag ();
                                             
 setup_end_tag ();                       
}

 

2.8setup.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 int

/*

 * 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 {

        t nr_banks;

        unsigned long end;

        struct {

                unsigned long start;

                unsigned long size;

                int           node;

                } 

                bank[NR_BANKS];

        };

extern struct meminfo meminfo;

#endif

你可能感兴趣的:(c,linux,struct,header,video,代码分析)