您的位置:首页 > 运维架构 > Linux

u-boot模仿

2014-07-13 10:36 716 查看
http://blog.chinaunix.net/uid-28458801-id-4113948.html

操作系统:ubuntu10.04

前言:

在上一章节中,分析了实现的思路。下面就是实现的源码,在源码中有详细的注释。

1,start.S

点击(此处)折叠或打开

/* watch dog registers */

#define WTCON 0x53000000

/* clock register */

#define CLKDIVN 0x4C000014

#define MPLLCON 0x4C000004

#define S3C2440_MPLL_200MHZ ((0x5c<<12)|(0x01<<4)|(0x02))

#define S3C2440_MPLL_400MHZ ((0x5c<<12)|(0x01<<4)|(0x01))

/* sdram */

#define MEM_CTL_BASE 0x48000000

.text

.global _start

_start:

/* 1. 关闭看门狗 */

ldr r0, =WTCON

mov r1, #0

str r1, [r0]

/* 2. 设置时钟 */

ldr r0, =CLKDIVN

//mov r1, #0x03; // FCLK:HCLK:PCLK=1:2:4, HDIVN=1,PDIVN=1

mov r1, #0x05; // FCLK:HCLK:PCLK=1:4:8

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_200MHZ */

ldr r0, =MPLLCON

//ldr r1, =S3C2440_MPLL_200MHZ

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

add r3, r0, #(13*4)

1:

ldr r2, [r1], #4

str r2, [r0], #4

cmp r0, r3

bne 1b

/* 4. 重定位:把 bootloader 本身的代码从flash中复制到对应的内存的链接地址中*/

ldr sp, =0x34000000 /* 64M SDRAM ,设置栈指针,栈是向下生长,故指向邋邋錝DRAM的最高处*/

bl nand_init /* 初始化 NAND FLASH */

mov r0, #0 /* src执行0地址。当为NAND FLASH 启动时,src指向的是邋錍PU片内内存的地址0.

当 NOR FLASH 启动时,src指向的是NOR FLASH 的地址0*/

ldr r1, =_start /* dest地址,为 *.lds 中定义的起始地址,此处为
0x33f80000*/

ldr r2, =__bss_start /* bss 段的起始地址*/

sub r2, r2, r1 /* r2 - r1,表示这个*.bin
文件的大小*/

bl copy_code_to_sdram

bl clear_bss /* 清空 sdram 中 *.bin文件的bss段内存地址*/

/* 5. 跳转到第二部分代码中执行*/

ldr lr, =halt /* 当 main 函数返回时,执行 halt 代码*/

ldr pc, =main

halt: /* 死循环 */

b halt

sdram_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



2,boot.lds


点击(此处)折叠或打开

SECTIONS {

. = 0x33f80000;

.text : { *(.text) }

. = ALIGN(4);

.rodata : {*(.rodata)}

. = ALIGN(4);

.data : { *(.data) }

. = ALIGN(4);

__bss_start = .;

.bss : { *(.bss) *(COMMON) }

__bss_end = .;

}

3,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 long *)0x50000020))

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

#define UBRDIV0 (*((volatile
unsigned long *)0x50000028))

void nand_read(unsigned int addr, unsigned char *buf, unsigned int len);

void nand_addr(unsigned int addr);

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);

}

static void nand_select(void)

{

NFCONT &= ~(1<<1);

}

static void nand_deselect(void)

{

NFCONT |= (1<<1);

}

static void nand_cmd(unsigned char cmd)

{

volatile char i = 0;

NFCMMD = cmd;

for( i = 0; i < 10; i++) ; /* 稍微延时,确保命令正确写入*/

}

static void nand_wait_ready(void)

{

while(!(NFSTAT & 0x01)) ;

}

static unsigned char nand_data(void)

{

return NFDATA;

}

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_read(unsigned int addr, unsigned char * buffer,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 读数据*/

/* 当前NAND FLASH 是以2k为一页的*/

/* 每次读取一页数据到对应寄存器中*/

for( ; (col < 2048) && ( i < len) ; col++ )

{

buffer[i] = nand_data();

i++;

addr++;

}

col = 0; /* 如果数据分布在不同的页中,则读取下一页时,从第0列开始读取*/

}

/* 7 取消选中*/

nand_deselect();

}

#if 1

int isBootFromNorFlash(void)

{

volatile int *p = (volatile int*)0;

int val = 0;

val = *p;

*p = 0x12345678; /* Nor
Flash 中是不允许写入数据的*/

if(0x12345678 == *p)

{

/* 写成功,是NAND FLASH */

*p = val;

return 0;

}

else

{

/* NOR FLASH 不能像内存一样写*/

return 1;

}

}

void copy_code_to_sdram(unsigned char *src, unsigned char *dest, unsigned int len)

{

int i = 0;

if(isBootFromNorFlash())

{

/* NOR FLASH 中的数据可以像内存中的数据那样,直接读取*/

while( i < len )

{

dest[i] = src[i];

i++;

}

}

else

{

/* NAND FLASH*/

nand_read((unsigned int )src, dest, len);

}

}

#endif

void clear_bss(void)

{

extern int __bss_start,__bss_end;

int *p = &__bss_start;

for( ; p < &__bss_end; p++)

*p = 0;

}

#define PCLK 50000000 /* start.S 中把时钟设置PCLK为50MHz*/

#define UART_CLK PCLK /* UART0的时钟源设为PCLK */

#define UART_BAUD_RATE 115200 /* 波特率为115200*/

#define UART_BRD ((UART_CLK / (UART_BAUD_RATE * 16)) - 1)

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

}

#define TXD0READY (1<<2)

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;

}

}

4,boot.c

点击(此处)折叠或打开

#include "setup.h"

extern void uart0_init(void);

extern void nand_read(unsigned int addr, unsigned char * buffer,unsigned int len);

extern void puts(char * str);

int strlen(char *str)

{

int i = 0;

while (str[i])

{

i++;

}

return i;

}

void strcpy(char *dest, char *src)

{

while ((*dest++ = *src++) != '\0');

}

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; /* SDRAM起始地址*/

params->u.mem.size = 64*1024*1024; /* 其大小为64
M */

params = tag_next (params);

}

void setup_commandline_tag(char *cmdline)

{

int len = strlen(cmdline) + 1;

params->hdr.tag = ATAG_CMDLINE;

/* 以4 个字节对齐,并且以4个字节为单位*/

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);

/* 1 帮内核设置串口: 内核启动的开始部分会从串口打印一些信息,但是内核一开始没有初始化串口*/

uart0_init();

/* 2 从NAND FLASH 中把内核读入到内存中*/

puts("Copy kernel from nand\n\r");

/* 内核存放在NAND FLASH 中的0x60000 地址。

存放的是uImage,该文件包含64 字节的头部信息。

内核一般都存放在0x30008000这个地址,除非手动修改内核。*/

nand_read(0x60000+64,( unsigned
char * )0x30008000, 0x200000);

/* 3 设置引导内核的参数*/

puts("Set boot params\n\r");

setup_start_tag();

setup_memory_tags(); /*内核大小*/

setup_commandline_tag("noinitrd root=/dev/mtdblock3 init=/linuxrc console=ttySAC0"); /* 命令参数*/

setup_end_tag();

/* 4 跳转执行*/

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

theKernel = (void (*)(int ,int ,unsigned int))0x30008000; /* 真正的内核起始地址*/

/* 参数1 : 默认为0.

参数2 : 为machine ID,当前设置为362。可在内核源码中: arch/arm/tools/mach_types
修改。

参数3 : 为参数的起始地址(params)*/

theKernel(0,362,0x30000100);

/* 5 异常:如果正确引导内核,不应该执行到这里*/

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

return -1;

}

5,setup.h

该文件是直接从uboot中复制过来的。

点击(此处)折叠或打开

/*

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

6,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

7,总结

至此,从零开始实现的uboot基本完成。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息