slj_win

  博客园 :: 首页 :: 新随笔 :: 联系 :: 订阅 :: 管理 ::

UBOOT引导Linux内核及向内核传递参数的方式

  一直以来没有想过有什么好的办法通过寄存器向内核传递参数,直到今天读UBOOT的实现方式。   在UBOOT中,引导内核最常用的方法是bootm命令,bootm命令可以引导“UBOOT格式”的内核。先花点时间了解一下什么是“UBOOT格式”的内核吧:用UBOOT自带的mkimage命令生成的内核称为"UBOOT"格式的内核。以下面这条命令为例:   mkimage -n "Kernel 2.4.18" -A arm -O linux -T kernel -C none -a 30007fc0 -e 30008000 -d 4020.bin vmlinux-2.4.18.img     其中与内核引导最密切的是-e 30008000,也就是内核的入口地址。其它参数可以参考帮助信息。其它UBOOT格式的内核与原来相比,只是进行(可选)了压缩,并在前面加了一个0x40大小的头。这个头里放了内核的位置(0x30007fc0)和入口地址(0x30008000)和其它信息。   bootm命令执行时,先对头部信息等进行校验,然后把头信息放到一个结构里面。最后根据内核类型调用相应的启动函数。对于Linux而言就是do_bootm_linux,在启动函数里面,有这么一个操作:theKernel = (void (*)(int, int, uint))ntohl(hdr->ih_ep);,这是最关键的一个操作,将内核的入口地址0x30008000赋给了theKernel,在启动函数的最后,使用theKernel (0, bd->bi_arch_number, bd->bi_boot_params);启动内核。   根据传参规范,三个变量分别用r0,r1,r2传给内核,这样就巧妙地利用了函数指针进行了参数传递,实在是精妙!上面讲完了内核的引导及传参,需要引起注意的就是在使用mkimage命令生成内核时,-e后面的地址要比-a后面的地址偏移0x40,原因很简单,就不在细说了。

  先后排除了几个问题,总算将ZY版的Linux2.4.18引导起来了。 引导使用的是bootm命令,原来不成功是因为bootargs没有设好,Linux已经正常启动,串口却没有输出。反复试验,把能排除的原因都排除之后,才发现是bootargs不对。由于对Linux启动不了解,浪费了不少时间...

  如果ZY看到这篇帖子,看一下这个问题:为什么串口输出那么多waiting? 我印象中是在等时钟滴答的时候才会有,是不是UBOOT中使用了Timer0,你在使用Timer0的时候没有对它进行复位? Linux version 2.4.18-rmk7 (root@localhost.localdomain) (gcc version 2.95.3 20010315 (release)) #28 Thu Apr 24 11:15:47 CST 2008 CPU: ARM ARM720T revision 2 Machine: SEP3221 BASED Kernel command line: root=/dev/ram0 console=tty0 console=ttyS0,9600 init=/linuxrc Console: colour dummy device 80x30 Calibrating delay loop... waiting...waiting...waiting...waiting...waiting...waiting...waiting...waiting...waiting...waiting...waiting...waiting...waiting...waiting...waiting...waiting...
(以下省略N千个waiting...)
26.16 BogoMIPS Memory: 8MB = 8MB total Memory: 4672KB available (1008K code, 224K data, 68K init) POSIX conformance testing by UNIFIX ttyS00 at 0xe0005000 (irq = 24) is a 16450 NET4 RAMDISK: Couldn't find valid RAM disk image starting at 0. Freeing initrd memory: 2048K Kernel panic: VFS: Unable to mount root fs on 01:00

  为了避免上面出现的情况,在int cleanup_before_linux (void)函数中,添加以下代码:   T1CR = 0x0;   T1LCR = 0x0; 达到复位Timer0的目的,果然再次用bootm命令引导Linux的时候没有再出现这么多的waiting...。
PS:Linux镜像是在使用bootm命令之前通过ICE下载到0x30007fc0地址的,将来可以烧写到Flash中,由程序搬运到将位置。由于Norflash只有2M,不能完整地放下UBOOT、Linux内核及文件系统,下面的重点可能是NandFlash的驱动...
U-Boot 1.1.4 (Aug  2 2008 - 11:03:41)

U-Boot code: 30700000 -> 30717F2C  BSS: -> 3071C320
IRQ Stack: 306deb74
FIQ Stack: 306dfb74
RAM Configuration:
Bank #0: 30000000  8 MB
Flash:  2 MB
*** Warning - bad CRC, using default environment

In:    serial
Out:   serial
Err:   serial
Hit any key to stop autoboot:  5  4  3  2  1  0
SEP4020=>bootm
## Booting image at 30007fc0 ...
   Image Name:   Kernel 2.4.18
   Created:      2008-08-01  13:40:42 UTC
   Image Type:   ARM Linux Kernel Image (uncompressed)
   Data Size:    1164515 Bytes =  1.1 MB
   Load Address: 30007fc0
   Entry Point:  30008000
   Verifying Checksum ... OK
   XIP Kernel Image ... OK

Starting kernel ...

booting linux from 0x30008000Linux version 2.4.18-rmk7 (root@localhost.localdomain) (gcc version 2.95.3 20010315 (release)) #28 Thu Apr 24 11:15:47 CST 2008 CPU: ARM ARM720T revision 2 Machine: SEP3221 BASED Kernel command line: root=/dev/ram0 console=tty0 console=ttyS0,9600 init=/linuxrc Console: colour dummy device 80x30 Calibrating delay loop... waiting...waiting...waiting...waiting...waiting...26.16 BogoMIPS Memory: 8MB = 8MB total Memory: 4672KB available (1008K code, 224K data, 68K init) POSIX conformance testing by UNIFIX ttyS00 at 0xe0005000 (irq = 24) is a 16450 NET4 RAMDISK: Couldn't find valid RAM disk image starting at 0. Freeing initrd memory: 2048K Kernel panic: VFS: Unable to mount root fs on 01:00

  

引导RAMFS方法

  假设将文件系统下载到0x30300000.   用BOOTM引导内核和文件系统时,不仅需要将内核进行mkimage处理,文件系统也需要进行相应处理.   文件系统处理命令:   mkimage -n "RAMFS" -A arm -O linux -T ramdisk -C none -a 30300000 -e 30300040 -d initrd.bin initrd.img   进行处理后,与内核处理结果相似,会在原来的文件系统映像前面加上一个64字节的头,这个头里包含了幻数,CRC校验信息和最重要的:文件系统起始地址长度
  如果使用UBOOT引导内核,同时希望将文件系统一并引导,bootm命令后面需要跟两个参数,第一个参数是内核所在的地址,第二个参数是文件系统所在的位置,如bootm 30007fc0 30300000   上面说的引导文件系统是利用了bootloader向内核标准传参方法,如果已经在内核将诸如机器号,文件系统地址等全部写死的话,则不需要进行上述操作,只需将PC切换到内核所在地址就可以了.
  

关于bootload向内核传递参数

  第一帖中提到,bootloader巧妙地利用函数指针及传参规范将R0:0x0,R1:机器号,R2:参数地址传递给内核.由于R0,R1比较简单,不需要再作说明.需要花点时间了解的是R2寄存器.   R2寄存器传递的是一个指针,这个指针指向一个TAG区域.UBOOT和Linux内核之间正是通过这个扩展了的TAG区域来进行复杂参数的传递,如command line,文件系统信息等等,用户也可以扩展这个TAG来进行更多参数的传递.TAG区域存放的地址,也就是R2的值,是在/board/yourboard/youboard.c里的board_init函数中初始化的,如在UB4020中初始化为:gd->bd->bi_boot_params = 0x30000100;,这是一个绝对地址.   TAG区的结构比较简单,可以视为一个一个TAG的排列(数组?),每一个TAG传递一种特定类型的参数.各种系统TAG的定义可以参考./include/asm-arm/setup.h.   下面是一个TAG区的例子:   0x30000100  00000005 54410001 00000000 00000000   0x30000110  00000000 0000000F 54410009 746F6F72   0x30000120  65642F3D 61722F76 7220306D 6F632077   0x30000130  6C6F736E 74743D65 2C305379 30303639   0x30000140  696E6920 6C2F3D74 78756E69 EA006372   0x30000150  00000004 54420005 30300040 00200000   0x30000160  00000000 00000000   我们可以看到一共有三个TAG:   第一个TAG的长度是5个字,类型是ATAG_CORE(54410001),有三个元素,均为全零.TAG区必须以这个TAG开头.   第二个TAG的长度是F个字,类型是ATAG_CMDLINE(54410009),这是一个字符串,是向内核传递的kernel command line   第三个TAG的长度是4个字,类型是ATAG_INITRD2(54410005),有两个元素,第一个是start:30300040(30300000+40),第二个是size:200000(2M)   如果说还有第四个TAG,那就是末尾的两个全零,这是TAG结束的标志.   这些TAG是在./lib_arm/arm_linux.c中的do_bootm_linux函数中建立起来的.具体建立哪些TAG,由相应的控制宏决定.具体可以参考相应代码.例子中第一个TAG是起始TAG,如果环境变量中有bootargs,则建立第二个TAG,如果bootm有两个参数(引导文件系统),则会读取文件系统头部的必要信息,建立第三个TAG.   内核启动后,将根据R2寄存器的值找到这些TAG,并根据TAG类型,调用相应的处理函数进行处理,从而获取内核运行的必要信息.

 

posted on 2011-11-09 13:48  slj_win  阅读(2846)  评论(0)    收藏  举报