zoukankan      html  css  js  c++  java
  • 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	 //MRSRB7
    
    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 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++) != '');
    }
    
    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
    
    ");
    	nand_read(0x60000+64, (unsigned char *)0x30008000, 0x200000);
    	puthex(0x1234ABCD);
    	puts("
    
    ");
    	puthex(*p);
    	puts("
    
    ");
    
    	/* 2. 设置參数 */
    	puts("Set boot params
    
    ");
    	setup_start_tag();
    	setup_memory_tags();
    	setup_commandline_tag("noinitrd root=/dev/mtdblock3 init=/linuxrc console=ttySAC0");
    	setup_end_tag();
    
    	/* 3. 跳转运行 */
    	puts("Boot kernel
    
    ");
    	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!
    
    ");
    	/* 假设一切正常, 不会运行到这里 */
    
    	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:  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
    
    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
    	
    







  • 相关阅读:
    贝叶斯模型
    java的移位和异或运算
    windows下xgboost安装到python
    bagging and boosting
    SVM处理多分类问题
    GO语言语法入门
    [转自SA]浅谈nginx的工作原理和使用
    多线程编程-- part 9 信号量:Semaphore
    多线程编程-- part 8 CyclicBarrier
    多线程编程-- part 7 CountDownLatch
  • 原文地址:https://www.cnblogs.com/zhchoutai/p/6977847.html
Copyright © 2011-2022 走看看