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 = .;
- }
点击( 此处 )折叠或打开
- /* 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基本完成。