1.stm32f 如何写bootloader
Linux bootloader 编写方法对于移植 linux 到其它开发板的人来说,编写 boot loader 是一个不可避免的过程。
对于学习linux的人来讲,编写 bootloader 也是一个很有挑战性的工作。本文通过对 linux引导协议进行分析,详细阐述了如何编写一个可以在 i386 机器上引导 2.4.20内核的基本的bootloader。
内容1.概述linux运行在保护模式下,但是当机器启动复位的时候却处于实模式下。所以写bootloader做的工作也是在实模式之下的。
linux的内核有多种格式,老式的zImage和新型的bzImage。它们之间最大的差别是对于内核体积大小的限制。
由于zImage内核需要放在实模式1MB的内存之内,所以其体积受到了限制。目前采用的内核格式大多为bzImage,这种格式没有1MB内存限制。
本文以下部分主要以bzImage为例进行分析。回页首2.bzImage格式内核的结构bzImage内核从前向后分为3个部分,前512字节被称为bootsect,这就是软盘引导linux时用到的bootloader,如果不从软盘引导,这部分就没有用,其中存储了一些编译时生成的内核启动选项的默认值。
从512个字节开始的512*n个字节称为setup部分,这是linux内核的实模式部分,这部分在实模式下运行,主要功能是为保护模式的linux内核启动准备环境。这个部分最后会切换进入保护模式,跳转到保护模式的内核执行。
最后的部分就是保护模式的内核,也就是真正意义上的linux内核。其中n的大小可以从bootsect后半部得到,详细地址可以参阅linux boot protocol。
回页首3.引导过程概述第一步,打开冰箱门;第二步把大象放到冰箱里……不要笑,过程就是这么简单。首先需要把linux内核的setup部分拷贝到9020H:0开始的地址,然后把保护模式内核拷贝到1MB开始的地址,然后根据Linux Boot Protocol 2.03的内容设定参数区的内容,基地址就是9000H:0,最后使用一条ljmp $0x9020,$0跳转到setup段,剩下的事情就是linux自己的了回页首4.THE LINUX/I386 BOOT PROTOCOL这个就是我们引导linux所使用的协议,它的位置在:Documetation/i386/boot.txt中。
里面详细的写了引导linux所需要知道的一切知识,对于其它体系结构的CPU,也一定存在着类似的东东,仿照本文的方法就可以了。回页首5.细节一:基本引导参数当然我们不指定任何参数linux内核也可以启动,但是这样有可能启动进入一个我们不支持的framebuffer模式,导致没有任何屏幕显示;也可能mount了错误的根分区失败,导致No Init Found的kernel panic。
所以我们必须要指定一些东西。如果你像我一样是一个懒人,那么可以直接把bootsect拷到9000H:0的位置,使用软盘引导时它会把自己复制到这个地方的,这里面有些默认的设置,详情请见boot.txt。
首先是root的位置,这里bootsect_pos指向的是9000H:0的地址。bootsect_pos[0x1fc] = root_minor;bootsect_pos[0x1fd] = root_major;其中root_minor和root_major分别是root的主设备号和次设备号。
当前显示模式:bootsect_pos[0x1fa] = 0xff;bootsect_pos[0x1fb] = 0xff;这两个数值相当于引导参数vga=0xHHH的值,两个0xff代表文本模式。bootsect_pos[0x210] = 0xff;这是在设定你的bootloader的类型,其实只要不是0就行,因为0代表的loader太旧无法引导新的内核,setup发现这个后就会停下来。
按照规范你应该写成0xff,这表示未知的boot loader,如果你的bootloader已经得到了一个官方分配的type id,那就写上自己的数值。回页首6.细节二:如何加载内核如果你现在的环境是一无所有,那么必须使用bios中断或者ATA指令去读硬盘了,不过如果你手中如果有基本的DOS系统,那么就可以使用DOS的程序了。
为了能够操作整个4GB的地址空间,我使用了WATCOM C写了个小程序读内核,不过你可以仿照bootsect里面的做法,在实模式中读一部分,然后进入到保护模式拷贝到1MB以上,然后再从实模式读一部分……需要注意1:9000H:0也是DOS占用的地址空间,所以读完内核后就不要返回DOS了,否则会有问题;注意2:一定保证是纯DOS,不要加载HIMEM或者EMM386这样的东西,它们会使上面的引导过程失败。loadlin倒是可以来者通吃几乎所有的DOS,不过它的作者也是这方面的大牛,对DOS下的内存管理非常的熟悉。
我们现在研究这些古老的东西很难找资料了,况且我们是在写bootloader,不是DOS killer^_^。回页首7.引导时的高级功能1)initrdinitrd是启动时的一个小虚拟盘,一般用它来实现模块化的内核。
引导initrd的方法主要有两个要点: 第一,把initrd读入内存,我们可以仿照大多数boot loader的方法把它放在内存的最高端; 第二,设定initrd的起始位置和长度bootsect_pos[0x218]开始的4个字节放的是起始物理地址,bootsect_pos[0x21c]开始的4个字节放的是initrd的长度。2)command_line支持用command_line你可以给内核传一些参数,自己定制内核的行为。
我是这样做的,首先把command_line放在9900H:0的地址里,然后把9900H:0的物理地址存放在bootsect_pos[0x228]开始的4个字节里面。注意一定是物理地址,所以你应该放99000H这个数,然后内核就会识别你的command_line了。
2.怎样从零开始写bootloader 第一部分
一般Bootloader和Linux的启动过程的讲解都是嵌入式书籍的一个章节而已,从我自身的学习经验来给出一些建议:
1. 可以看 Embedded Linux Primer (豆瓣) 第二版,网上有英文版(建议)和中文版的pdf,里面的Chapter5和7分别简述了Linux的初始化启动过程和Bootloader基础。还有后面的Busybox章节也不错。另外构建嵌入式LINUX系统 (豆瓣)也可以作为参考。
2. 2. 找一下韦东山的第一期和毕业班,里面也讲到了Bootloader和Linux如何初始化以及如何执行第一个用户程序。尽管其实只讲了一部分,但是对于Linux的启动流程的学习还是很有帮组的。跟着写一个Bootloader绝对是学习Bootloader的一种好方式。如同在stackoverflow里面对于如何学习Bootloader给出的答案一样“The best method to learn it, is to recreate one”。如果自己写一个有困难,那么就先学习移植吧。
3. 3. 其实Bootloader就是裸机程序,因此如果你对裸机程序熟悉,那么Bootloader甚至说不上需要学习,给出一本书籍用于参考:ARM处理器裸机开发实战:机制而非策略(附CD-ROM光盘1张)/王小强
其他国人写的书籍中的Bootloader大都是泛泛而谈,对于有经验的人用不着,对于初学者更多的是雾里看花,例如:
杨铸与人合著的两本书:深入浅出:嵌入式底层开发/杨铸和构建嵌入式Linux核心系统实战(附光盘1张)/杨铸
韦东山写的那本书:嵌入式Linux应用开发完全手册(附CD光盘1张)/韦东山
以及各类“精通”与“实战”之流。
但,不管如何,开卷有益,同时也因人而异。还是需要题主自己考究。
最后:
如果你学得很痛苦,那么很可能没有走在正确的路上。我觉得学习过程应该是不断+1的过程,看,看得懂的代码和书。否则继续学习基础,或者直接跳过这些东西直接到Linux上面学习驱动开发或者应用开发,其实这些知识点没有那么多牵连,很多人给出的嵌入式学习步骤绝对是带人入坑。
转载请注明出处育才学习网 » bootloader怎么写