u-boot学习(六):自己写bootloader
依照前面分析的u-boot的启动流程,自己写一个简单的Bootloader。这是參考韦东山老师的视频写的。
1、初始化硬件:关看门狗、设置时钟、设置SDRAM、初始化NAND Flash
2、假设Bootloader比較大,要重定位到SDRAM
3、把内核从NAND FLASH读入SDRAM
4、设置“要传给内核的參数”
5、跳转运行内核
start.S代码例如以下:
#define S3C2440_MPLL_200MHZ ((0x5c<<12)|(0x01<<4)|(0x02)) #define MEM_CTL_BASE 0x48000000 .text .global _start _start: /* 1. 关看门狗 */ ldr r0, =0x53000000 mov r1, #0 str r1, [r0] /* 2. 设置时钟 */ ldr r0, =0x4c000014 mov r1, #0x03; // FCLK:HCLK:PCLK=1:2:4, HDIVN=1,PDIVN=1 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, =0x4c000004 ldr r1, =S3C2440_MPLL_200MHZ str r1, [r0] /* 3. 初始化SDRAM */ ldr r0, =MEM_CTL_BASE adr r1, sdram_config /* 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 bl nand_init mov r0, #0 ldr r1, =_start ldr r2, =__bss_start sub r2, r2, r1 bl copy_code_to_sdram bl clear_bss /* 5. 运行main */ ldr lr, =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 //MRSRB7init.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 char *)0x50000020) #define URXH0 (*(volatile unsigned char *)0x50000024) #define UBRDIV0 (*(volatile unsigned long *)0x50000028) #define TXD0READY (1<<2) void nand_read(unsigned int addr, unsigned char *buf, unsigned int len); int isBootFromNorFlash(void) { volatile int *p = (volatile int *)0; int val; val = *p; *p = 0x12345678; if (*p == 0x12345678) { /* 写成功, 是nand启动 */ *p = val; return 0; } else { /* NOR不能像内存一样写 */ return 1; } } void copy_code_to_sdram(unsigned char *src, unsigned char *dest, unsigned int len) { int i = 0; /* 假设是NOR启动 */ if (isBootFromNorFlash()) { while (i < len) { dest[i] = src[i]; i++; } } else { //nand_init(); nand_read((unsigned int)src, dest, len); } } void clear_bss(void) { extern int __bss_start, __bss_end; int *p = &__bss_start; for (; p < &__bss_end; p++) *p = 0; } 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); } void nand_select(void) { NFCONT &= ~(1<<1); } void nand_deselect(void) { NFCONT |= (1<<1); } void nand_cmd(unsigned char cmd) { volatile int i; NFCMMD = cmd; for (i = 0; i < 10; i++); } 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_wait_ready(void) { while (!(NFSTAT & 1)); } unsigned char nand_data(void) { return NFDATA; } void nand_read(unsigned int addr, unsigned char *buf, 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. 读数据 */ for (; (col < 2048) && (i < len); col++) { buf[i] = nand_data(); i++; addr++; } col = 0; } /* 7. 取消选中 */ nand_deselect(); } #define PCLK 50000000 // init.c中的clock_init函数设置PCLK为50MHz #define UART_CLK PCLK // UART0的时钟源设为PCLK #define UART_BAUD_RATE 115200 // 波特率 #define UART_BRD ((UART_CLK / (UART_BAUD_RATE * 16)) - 1) /* * 初始化UART0 * 115200,8N1,无流控 */ 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 } /* * 发送一个字符 */ 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++; } } void puthex(unsigned int val) { /* 0x1234abcd */ int i; int j; puts("0x"); for (i = 0; i < 8; i++) { j = (val >> ((7-i)*4)) & 0xf; if ((j >= 0) && (j <= 9)) putc('0' + j); else putc('A' + j - 0xa); } }boot.c
#include "setup.h" extern void uart0_init(void); extern void nand_read(unsigned int addr, unsigned char *buf, unsigned int len); extern void puts(char *str); extern void puthex(unsigned int val); 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; params->u.mem.size = 64*1024*1024; params = tag_next (params); } int strlen(char *str) { int i = 0; while (str[i]) { i++; } return i; } void strcpy(char *dest, char *src) { while ((*dest++ = *src++) != '\0'); } void setup_commandline_tag(char *cmdline) { int len = strlen(cmdline) + 1; params->hdr.tag = ATAG_CMDLINE; 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); volatile unsigned int *p = (volatile unsigned int *)0x30008000; /* 0. 帮内核设置串口: 内核启动的開始部分会从串口打印一些信息,可是内核一開始没有初始化串口 */ uart0_init(); /* 1. 从NAND FLASH里把内核读入内存 */ puts("Copy kernel from nand\n\r"); nand_read(0x60000+64, (unsigned char *)0x30008000, 0x200000); puthex(0x1234ABCD); puts("\n\r"); puthex(*p); puts("\n\r"); /* 2. 设置參数 */ 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(); /* 3. 跳转运行 */ puts("Boot kernel\n\r"); theKernel = (void (*)(int, int, unsigned int))0x30008000; theKernel(0, 362, 0x30000100); /* * mov r0, #0 * ldr r1, =362 * ldr r2, =0x30000100 * mov pc, #0x30008000 */ puts("Error!\n\r"); /* 假设一切正常, 不会运行到这里 */ return -1; }boot.lds
SECTIONS { . = 0x33f80000; .text : { *(.text) } . = ALIGN(4); .rodata : {*(.rodata*)} . = ALIGN(4); .data : { *(.data) } . = ALIGN(4); __bss_start = .; .bss : { *(.bss) *(COMMON) } __bss_end = .; }setup.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 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; #endifMakefile
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