(嵌入式开发)自己写bootloader之编写第二阶段
内核编译(make)之后会生成两个文件,一个Image,一个zImage,其中Image为内核映像文件,而zImage为内核的一种映像压缩文件,Image大约为4M,而zImage不到2M。 那么uImage又是什么的?它是uboot专用的映像文件,它是在zImage之前加上一个长度为64字节的“头”,说明这个内核的版本、加载位置、生成时间、大小等信息;其0x40之后与zImage没区别。
如何生成uImage文件?首先在uboot的/tools目录下寻找mkimage文件,把其copy到系统/usr/local/bin目录下,这样就完成制作工具。然后在内核目录下运行make uImage,如果成功,便可以在arch/arm/boot/目录下发现uImage文件,其大小比zImage多64个字节。 其实就是一个自动跟手动的区别,有了uImage头部的描述,u-boot就知道对应Image的信息,如果没有头部则需要自己手动去搞那些参数。 U-boot的U是“通用”的意思。 zImage是ARM
Linux常用的一种压缩映像文件,uImage是U-boot专用的映像文件,它是在zImage之前加上一个长度为0x40的“头”,说明这个映像文件的类型、加载位置、生成时间、大小等信息。换句话说,如果直接从uImage的0x40位置开始执行,zImage和uImage没有任何区别。另外,Linux2.4内核不支持uImage,Linux2.6内核加入了很多对嵌入式系统的支持,但是uImage的生成也需要设置,这个以后我会介绍。
几种linux内核文件的区别:
1、vmlinux 编译出来的最原始的内核文件,未压缩。 2、zImage 是vmlinux经过gzip压缩后的文件。 3、bzImage bz表示“big zImage”,不是用bzip2压缩的。两者的不同之处在于,zImage解压缩内核到低端内存(第一个640K),bzImage解压缩内核到高端内存(1M以上)。如果内核比较小,那么采用zImage或bzImage都行,如果比较大应该用bzImage。
4、uImage U-boot专用的映像文件,它是在zImage之前加上一个长度为0x40的tag。 5、vmlinuz 是bzImage/zImage文件的拷贝或指向bzImage/zImage的链接。
6、initrd 是“initial ramdisk”的简写。一般被用来临时的引导硬件到实际内核vmlinuz能够接管并继续引导的状态
一般情况下都在生成 vmlinux 后,再对内核进行压缩成为 zImage,压缩的目录是 kernel/arch/arm/boot。
下载到 flash 中的是压缩后的 zImage 文件, zImage 是由压缩后的 vmlinux 和解压缩程序组成,如下图所示:
查看 2410 的 datasheet ,发现内存映射的基址是 0x3000 0000 ,那么 0x30008000 又是如何来的呢?
在内核文档 kernel/Document/arm/Booting 文件中有:
Calling the kernel image
Existing boot loaders: MANDATORY New boot loaders: MANDATORY
There are two options for calling the kernel zImage. If the zImage is stored in flash, and is linked correctly to be run
from flash, then it is legal for the boot loader to call the zImage in flash directly.
The zImage may also be placed in system RAM (at any location) and called there. Note that the kernel uses 16K of
RAM below the image to store page tables. The recommended placement is 32KiB into RAM. 看来在 image 下面用了 32K(0x8000)的空间存放内核页表,
0x30008000 就是 2410 的内核在 RAM 中的启动地址,这个地址就是这么来的。
用U-Boot启动Linux内核
1、下载u-boot.bin到SDRAM的0x30008000处
tftp 0x30008000 uImage 2、启动内核
bootm 0x30008000
0X200000表示2M
6. 清bss段(bss段是没有初始化的全局变量和初始化为零的全局变量)
void clear_bss(void)
{
extern int __bss_start, __bss_end; // __bss_start, __bss_end变量的值在链接脚本中设置的
int *p = &__bss_start; for (; p < &__bss_end; p++)
*p = 0;
} 7. 传递启动参数
a. 初始化串口
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
}
b. 将内核从nand flash中读到SDRAM中
nand_read(0x60000+64, (unsigned char *)0x30008000, 0x200000);
读入的内核是uImage,它包含64字节的头部和内核文件。 c. 设置启动参数
/* 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(); 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;
//向4取整
strcpy (params->u.cmdline.cmdline, cmdline); params = tag_next (params);
} void setup_end_tag(void)
{
params->hdr.tag = ATAG_NONE;
params->hdr.size = 0; 8. 启动内核
<pre name="code" class="objc">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 内核支持的单板ID
* ldr r2, =0x30000100 参数的位置
* mov pc, #0x30008000
*/ puts("Error!\n\r");
/* 如果一切正常, 不会执行到这里 */ return -1;
}
//最后还需写makefile
<pre name="code" class="objc">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
(嵌入式开发)自己写bootloader之编写第二阶段的更多相关文章
- (嵌入式开发)自己写bootloader之编写第一阶段
最简单的bootloader的编写步骤: 1. 初始化硬件:关看门狗.设置时钟.设置SDRAM.初始化NAND FLASH 2. 如果bootloader比较大,要把它重定位到SDRAM 3. 把内核 ...
- 【嵌入式开发】 Bootloader 详解 ( 代码环境 | ARM 启动流程 | uboot 工作流程 | 架构设计)
作者 : 韩曙亮 博客地址 : http://blog.csdn.net/shulianghan/article/details/42462795 转载请著名出处 相关资源下载 : -- u-boo ...
- 【嵌入式开发】向开发板中烧写Linux系统-型号S3C6410
作者 : 万境绝尘 转载请著名出处 终于拿到板子了, 嵌入式开发正式开启. 板子型号 : 三星 S3C6410 基于ARM11, 指令集基于arm6指令集; 为毛不是 Cortext A9的板子; 烧 ...
- 嵌入式系统烧写uboot/bootloader/kernel的一般方法
嵌入式系统烧写uboot/bootloader/kernel的一般方法 本文介绍了在嵌入式系统中烧写uboot/bootloader/kernel 的一般方法,以及如果uboot或者内核出现错误, ...
- 谈谈iOS开发如何写个人中心这类页面--静态tableView页面的编写
本文来自 网易云社区 . 一.本文讲的是什么问题? 在开发 iOS 应用时,基本都会遇到个人中心.设置.详情信息等页面,这里截取了某应用的详情编辑页面和个人中心页面,如下: 我们以页面结构的角度考虑这 ...
- 用arduino的uno开发板为nano板子烧写bootloader
这篇文章,是为了记录下某宝上淘到的一个没有bootloader的nano开发板的历程(比较坑),自己搜索资料而记录的. 如果没有bootloader,板子就不能接收上传的程序,什么也干不了. 烧写bo ...
- 【嵌入式开发】写入开发板Linux系统-模型S3C6410
笔者 : 万境绝尘 转载请著名出处 最终拿到板子了, 嵌入式开发正式开启. 板子型号 : 三星 S3C6410 基于ARM11, 指令集基于arm6指令集; 为毛不是 Cortext A9的板子; 烧 ...
- 应聘linux/ARM嵌入式开发岗位
**************************************************************** 因为发在中华英才和智联招聘没有人采我所以我 在这里发布我的个人简历希望 ...
- 【嵌入式开发】裸机引导操作系统和ARM 内存操作 ( DRAM SRAM 类型 简介 | Logical Bank | 内存地址空间介绍 | 内存芯片连接方式 | 内存初始化 | 汇编代码示例 )
[嵌入式开发]ARM 内存操作 ( DRAM SRAM 类型 简介 | Logical Bank | 内存地址空间介绍 | 内存芯片连接方式 | 内存初始化 | 汇编代码示例 ) 一. 内存 ...
随机推荐
- OpenCASCADE Make Face With Holes
OpenCASCADE Make Face With Holes eryar@163.com OpenCASCADE提供了构造Face的类BRepBuilderAPI_MakeFace,使用这个类可以 ...
- Activity中recreate方法的应用
參考两篇文章:http://blog.csdn.net/watermusicyes/article/details/47392949 http://blog.csdn.net/droyon/a ...
- leetCode 85.Maximal Rectangle (最大矩阵) 解题思路和方法
Given a 2D binary matrix filled with 0's and 1's, find the largest rectangle containing all ones and ...
- javafx KeyCombination
import javafx.application.Application; import javafx.application.Platform; import javafx.event.Actio ...
- deep-in-es6(五)
解构 Destructuring: 解构赋值允许使用类似数组或对象字面量的语法将数组和对象的属性赋值给给中变量. 一般情况访问数组中的前三个元素: var first = arr[0]; var se ...
- 几个不错的开源的.net界面控件
转自原文 几个不错的开源的.net界面控件 (转) 几个不错的开源的.net界面控件 - zt 介绍几个自己觉得不错的几个开源的.net界面控件,不知道是否有人介绍过. DockPanel Suite ...
- Axios再记录
一个基于Promise 用于浏览器和 nodejs 的 HTTP 客户端(可实现ajax的请求) 有关学习网址:https://www.tuicool.com/articles/eMb2yuY ...
- POJ 3252 Round Numbers(组合数学)
Round Numbers Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 10223 Accepted: 3726 De ...
- sql%rowcount 返回影响行数
oracle中.返回影响行数是:If sql%rowcount 举例: update ut_calenderStatus t set t.calenderstatus=pi_flg, t.m=pi_M ...
- Python - 字典按值(value)排序
字典安值排序是一个伪命题. 字典本身是不能被排序的, 已经依照关键字(key)排序, 可是列表(list)和元组(tuple)能够排序, 所以字典须要转换列表后排序. 如 import operato ...