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基本完成。
操作系统: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基本完成。
相关文章推荐
- Installing Fedora 8 and let it Dual Boot with Windows - A Guide for Windows Users
- u-boot 相关问题与知识
- 模仿微信收藏文件的标签处理
- 使用 spring-boot 中各种异常整理
- spring-boot跳转页面
- spring boot - hello world
- Spring Boot 属性配置和使用
- spring boot 注解方式配置多数据源与使用
- SpringBoot | 第三十一章:MongoDB的集成和使用
- Start Oracle server when system boot
- u-boot-2012.07 for FL2440
- hisi平台mii网络模式和rmii网络模式的uboot制作
- Delphi Boot Camp 网络研讨会 (优酷视频)
- spring boot mybatis
- Spring Boot 配置文件和日志文件放到jar之外
- Spring Boot整合SpringJPA
- SpringBoot(七)整合themeleaf+bootstrap
- BOOT.INI文件详解
- Android 分区详解: boot, system, recovery, data, cache & misc