依照前面分析的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++) != '\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; #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

u-boot学习(六):自己写bootloader的更多相关文章

  1. Linux学习 : 自己写bootloader

    一.bootloader 目标:启动内核 基本功能: ①初始化硬件:关看门狗.设置时钟.设置SDRAM.初始化NAND FLASH ②image比较大需要重定位到SDRAM ②将内核从NAND FLA ...

  2. spring boot 学习(六)spring boot 各版本中使用 log4j2 记录日志

    spring boot 各版本中使用 log4j2 记录日志 前言 Spring Boot中默认日志工具是 logback,只不过我不太喜欢 logback.为了更好支持 spring boot 框架 ...

  3. Spring boot 学习六 spring 继承 mybatis (基于注解)

    MyBatis提供了多个注解如:@InsertProvider,@UpdateProvider,@DeleteProvider和@SelectProvider,这些都是建立动态语言和让MyBatis执 ...

  4. Spring Boot 学习笔记(六) 整合 RESTful 参数传递

    Spring Boot 学习笔记 源码地址 Spring Boot 学习笔记(一) hello world Spring Boot 学习笔记(二) 整合 log4j2 Spring Boot 学习笔记 ...

  5. Spring Boot学习路线

    Spring Boot 学习路线,本文计划根据作者近几年的工作.学习经验,来分析和制定一个学习使用 Spring Boot技术的步骤路线图. SpringBoot是伴随着Spring4.0诞生的: S ...

  6. spring boot / cloud (六) 开启CORS跨域访问

    spring boot / cloud (六) 开启CORS跨域访问 前言 什么是CORS? Cross-origin resource sharing(跨域资源共享),是一个W3C标准,它允许你向一 ...

  7. Spring Boot入门(六):使用MyBatis访问MySql数据库(注解方式)

    本系列博客记录自己学习Spring Boot的历程,如帮助到你,不胜荣幸,如有错误,欢迎指正! 本篇博客我们讲解下在Spring Boot中使用MyBatis访问MySql数据库的简单用法. 1.前期 ...

  8. Spring Boot学习大全(入门)

    Spring Boot学习(入门) 1.了解Spring boot Spring boot的官网(https://spring.io),我们需要的一些jar包,配置文件都可以在下载.添置书签后,我自己 ...

  9. Spring Boot学习记录(二)--thymeleaf模板 - CSDN博客

    ==他的博客应该不错,没有细看 Spring Boot学习记录(二)--thymeleaf模板 - CSDN博客 http://blog.csdn.net/u012706811/article/det ...

  10. Spring boot学习1 构建微服务:Spring boot 入门篇

    Spring boot学习1 构建微服务:Spring boot 入门篇 Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程.该框 ...

随机推荐

  1. SQL-Oracle-创建Dblink

    create database link DBLINK_IMARK_RAC connect to imark identified by imarkDB12345 using '(DESCRIPTIO ...

  2. etymology-R

    1)vor = to eat devour vt. 狼吞虎咽地吃光: 吞没,毁灭: 目不转睛地看[de-向下+vour-吃] voracity  n.贪食,贪婪.拉丁词根vor-,vorac-表示吞食 ...

  3. Hdu oj 1017 A Mathematical Curiosity

    题目:pid=1017">点击打开链接 #include<stdio.h> int main() { int t; scanf("%d",&t) ...

  4. Newton均差插值性质证明

    watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvemh1cWl1aHVp/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA ...

  5. java中super的作用

    super()的作用 super能够用来訪问超类的构造方法和被子类所隐藏的方法.假设子类中有方法与超类中的方法名称和參数同样,则超类中的方法就被隐藏起来,也就是说在子类中重载了父类中的方法. 引用父类 ...

  6. BroadcastReceiver类

    java.lang.Object    ↳ android.content.BroadcastReceiver 已知直接子类 AppWidgetProvider DeviceAdminReceiver ...

  7. BZOJ3170: [Tjoi2013]松鼠聚会

    [传送门:BZOJ3170] 简要题意: 给出n个点的坐标,规定两个点的距离=max(|x1-x2|,|y1-y2|) 要求选出一个点,使得这个点到所有点的距离和最小 题解: 切比雪夫转换例题 将一个 ...

  8. python绘制caffe中网络模型

    caffe-master/python/draw_net.py 实现绘制caffe中定义的网络模型功能,将.prototxt文件可视化. 需要先安装pydot和protobuf工具 通过Anacond ...

  9. SPFA(Shortest Path Faster Algorithm)

    特别说明 本文转载自三金(frinemore)的博客: 点这 前言 1.关于SPFA,它没死. 2.接下来的所有代码,都是自己手写的(未检查正确性,补充的代码有检查过,是对的),有错误请帮忙指出. S ...

  10. 关于在bootstrap的tab栏中渲染echats图表,切换tab时echats不显示问题

    在开发过程中遇到这样个问题: 利用bootstrap中的tab栏,每当点击tab栏的导航时,echats仅仅只渲染第一个tab的内容,切换tab时,echats图表不显示. 其html代码为: < ...