操作系统: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中复制过来的。
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基本完成。
阅读(1503) | 评论(0) | 转发(0) |