bootargs参数意义

U-boot的环境变量值得注意的有两个: bootcmd 和bootargs。
u-bootcmd
 
  前面有说过bootcmd是自动启动时默认执行的一些命令,因此你可以在当前环境中定义各种不同配置,不同环境的参数设置,然后设置bootcmd为你经常使用的那种参数。

u-bootargs

    bootargs是环境变量中的重中之重,甚至可以说整个环境变量都是围绕着bootargs来设置的。bootargs的种类非常非常的多,我们平常只是使用了几种而已,感兴趣的可以看看这篇文章说的很全:http://blog.chinaunix.net/u2/79570/showart_1675071.html。bootargs非常的灵活,内核和文件系统的不同搭配就会有不同的设置方法,甚至你也可以不设置bootargs,而直接将其写到内核中去(在配置内核的选项中可以进行这样的设置),正是这些原因导致了bootargs使用上的困难。
 
  下面介绍一下bootargs常用参数,bootargs的种类非常的多,而且随着kernel的发展会出现一些新的参数,使得设置会更加灵活多样。

A. root

用来指定rootfs的位置, 常见的情况有:
 
  root=/dev/ramrw   
 
  root=/dev/ram0 rw
 
  请注意上面的这两种设置情况是通用的,我做过测试甚至root=/dev/ram1 rw和root=/dev/ram2rw也是可以的,网上有人说在某些情况下是不通用的,即必须设置成ram或者ram0,但是目前还没有遇到,还需要进一步确认,遇到不行的时候可以逐一尝试。

 
  root=/dev/mtdx rw
 
  root=/dev/mtdblockx rw
 
  root=/dev/mtdblock/x rw
 
  root=31:0x

上面的这几个在一定情况下是通用的,当然这要看你当前的系统是否支持,不过mtd是字符设备,而mtdblock是块设备,有时候你的挨个的试到底当前的系统支持上面那种情况下,不过root=/dev/mtdblockxrw比较通用。此外,如果直接指定设备名可以的话,那么使用此设备的设备号也是可以的。

 
  root=/dev/nfs
在文件系统为基于nfs的文件系统的时候使用。当然指定root=/dev/nfs之后,还需要指定nfsroot=serverip:nfs_dir,即指明文件系统存在那个主机的那个目录下面。

B. rootfstype

    这个选项需要跟root一起配合使用,一般如果根文件系统是ext2的话,有没有这个选项是无所谓的,但是如果是jffs2,squashfs等文件系统的话,就需要rootfstype指明文件系统的类型,不然会无法挂载根分区.

C. console

console=tty    使用虚拟串口终端设备 .
console=ttyS[,options]使用特定的串口,options可以是这样的形式bbbbpnx,这里bbbb是指串口的波特率,p是奇偶位(从来没有看过使用过),n是指的bits。
console=ttySAC[,options] 同上面。

看你当前的环境,有时用ttyS,有时用ttySAC,网上有人说,这是跟内核的版本有关,2.4用ttyS,2.6用ttySAC,但实际情况是官方文档中也是使用ttyS,所以应该是跟内核版本没有关联的。可以查看Documentation/serial-console.txt找到相关描述。

D. mem

mem=xxM 指定内存的大小,不是必须的

E. ramdisk_size

ramdisk=xxxxx                不推荐   
ramdisk_size=xxxxx 
  推荐
上 面这两个都可以告诉ramdisk驱动,创建的ramdisk的size,默认情况下是4m(s390默认8M),你可以查看Documentation/ramdisk.txt找到相关的描述,不过ramdisk=xxxxx在新版的内核都已经没有提了,不推荐使用。

F. initrd, noinitrd

当你没有使用ramdisk启动系统的时候,你需要使用noinitrd这个参数,但是如果使用了的话,就需要指定initrd=r_addr,size,r_addr表示initrd在内存中的位置,size表示initrd的大小。

G. init

init 指定的是内核启起来后,进入系统中运行的第一个脚本,一般init=/linuxrc,或者init=/etc/preinit,preinit的内容一般是创建console,null设备节点,运行init程序,挂载一些文件系统等等操作。请注意,很多初学者以为init=/linuxrc是固定写法,其实不然,/linuxrc指的是/目录下面的linuxrc脚本,一般是一个连接罢了。

H. mtdparts

mtdparts=fc000000.nor_flash:1920k(linux),128k(fdt),20M(ramdisk),4M(jffs2),38272k(user),256k(env),384k(uboot)
要 想这个参数起作用,内核中的mtd驱动必须要支持,即内核配置时需要选上DeviceDrivers 
  --->Memory Technology Device (MTD)support    --->Command line partition table parsing

mtdparts的格式如下:
mtdparts=[;
 
  := :[,]
 
  := [@offset][][ro]
 
:= unique id used in mappingdriver/device
 
:= standard linux memsize OR"-" to denote all remaining space
 
:= (NAME)
因此你在使用的时候需要按照下面的格式来设置:
mtdparts=mtd-id:@(),@()
这里面有几个必须要注意的:
a. 
  mtd-id必须要跟你当前平台的flash的mtd-id一致,不然整个mtdparts会失效
b. 
  size在设置的时候可以为实际的size(xxM,xxk,xx),也可以为'-'这表示剩余的所有空间。
举例:
假设flash 的mtd-id是sa1100,那么你可以使用下面的方式来设置:
mtdparts=sa1100:- 
      →    只有一个分区
mtdparts=sa1100:256k(ARMboot)ro,-(root) 
  →    有两个分区
可以查看drivers/mtd/cmdlinepart.c中的注释找到相关描述。

I. ip
指定系统启动之后网卡的ip地址,如果你使用基于nfs的文件系统,那么必须要有这个参数,其他的情况下就看你自己的喜好了。设置ip有两种方法:
ip = ip addr
ip=ip addr:server ip addr:gateway:netmask::which netcard:off
这两种方法可以用,不过很明显第二种要详细很多,请注意第二种中which netcard是指开发板上的网卡,而不是主机上的网卡。

说完常见的几种bootargs,那么我们来讨论平常我经常使用的几种组合:
1). 假设文件系统是ramdisk,且直接就在内存中,bootargs的设置应该如下:
setenv bootargs ‘initrd=0x32000000,0xa00000 root=/dev/ram0console=ttySAC0 mem=64M init=/linuxrc’

2). 假设文件系统是ramdisk,且在flash中,bootargs的设置应该如下:
setenv bootargs ‘mem=32M console=ttyS0,115200 root=/dev/ram rwinit=/linuxrc’
注意这种情况下你应该要在bootm命令中指定ramdisk在flash中的地址,如bootm kernel_addrramdisk_addr (fdt_addr)

3). 假设文件系统是jffs2类型的,且在flash中,bootargs的设置应该如下
setenv bootargs ‘mem=32M console=ttyS0,115200 noinitrdroot=/dev/mtdblock2 rw rootfstype=jffs2 init=/linuxrc’

4). 假设文件系统是基于nfs的,bootargs的设置应该如下
setenv bootargs ‘noinitrd mem=64M console=ttySAC0 root=/dev/nfsnfsroot=192.168.0.3:/nfsip=192.168.0.5:192.168.0.3:192.168.0.3:255.255.255.0::eth0:off’
或者
setenv bootargs ‘noinitrd mem=64M console=ttySAC0 root=/dev/nfsnfsroot=192.168.0.3:/nfs ip=192.168.0.5’


上面就是我们经常使用的几种bootargs的组合,老实说,bootargs非常非常的灵活,所以设置的方法有很多中形式,具体的还应该根据你的平台具体的情况来设置。

应用程序必须经过编译、汇编和链接后才变成可执行文件,在链接时,要对所有目标文件进行重定位(relocation),建立符号引用规则,同时为变量、函数等分配运行地址。当程序执行时,系统必须把代码加载到链接时所指定的地址空间,以保证程序在执行过程中对变量、函数等符号的正确引用,使程序正常运行。在具有操作系统的系统中,重定位过程由操作系统自动完成。  在设计Bootloader程序时,必须在裸机环境中进行,这时Bootloader映像文件的运行地址必须由程序员设定。通常情况下,将Bootloader程序下载到ROM0x0地址进行启动,而在大多数应用系统中,为了快速启动,首先将Bootloader程序拷贝到SDRAM中再运行。一般情况下,这两者的地址并不相同。

    首先来看下面的链接脚本文件:

  1. ENTRY(_start)  
  2.   ;指定输出可执行文件的起始代码段为_start.  
  3. SECTIONS  
  4. {  
  5.         .= BOOTADDR ; bootloader的开始地址/  
  6.         .= ALIGN(4); 代码以4字节对齐  
  7.         .text :;指定代码段  
  8.         {  
  9.           cpu/arch/start.o (.text) ; bootloader中的text段  
  10.           *(.text)                 ;其它text段     
  11.         }  
  12.         .= ALIGN(4)  
  13.         .rodata :{*(.rodata)};指定只读数据段  
  14.         .= ALIGN(4);  
  15.         .data :{*(.data)};指定读/写数据段  
  16.         .= ALIGN(4);  
  17.         __bss_start =.; 把__bss_start赋值为当前位置,即bss段的开始位置  
  18.         .bss :{*(.bss)}; 指定bss段  
  19.         _end =.; 把_end赋值为当前位置,即bss段的结束位置  
  20. }  

需要指出的是,链接脚本中所描述的输出段地址为虚拟地址VMAVirtualMemoryAddress)。这里的虚拟地址仅指映像文件执行时,各输出段所重定位到相应的存储地址空间,与映像文件烧写到的实际的地址无关(即映像的加载地址)。因此,上面的链接脚本实际上指定了Bootloader映像在执行时,将被重定位到BOOTADDR开始的存储地址空间,以保证在相关位置对符号进行正确引用,使程序正常运行。

假设这里指定BOOTADDR= 0x0。以ARM为例,ARM处理器复位后总是从0x0地址取第1条指令,因此只需把BOOTADDR设置为0,再把编译后生成的可执行二进制文件下载到ROM0x0地址开始的存储空间,程序便可正常引导;但是,一旦在链接时指定映像文件从0x0地址开始,那么Bootloader就只能在0x0地址开始的ROM空间内运行,而无法拷贝到SDRAM空间运行实现快速引导。当然,搬运代码最后的跳转语句可以写成绝对地址,如jmp 0x10000,这样可以正确的跳到RAM中的0x10000地址处,但当执行继续执行碰到其他符号地址计算,或全局数据访问的时候,由于此时不是位置无关代码,此时地址的计算需要查询map表,但是map表中的地址仍然在ROM空间中,所以还会跳回ROM空间,另外,还会有其他问题,如动态内存申请等。

有了位置无关代码,只需修改链接脚本文件的BOOTADDR=0x10000即可,即将整个镜像文件都映射到RAM空间,但是bootloader最开始的搬运代码必须是位置无关的代码,这样虽然搬运代码被映射到RAM地址空间,但它在0x0开始的ROM中也能正确执行,搬运代码最后的跳转语句就可以跳转到某个标号了,因为此时标号的地址已经被映射到了ram空间,之后的代码执行将没有任何问题。

当然,可以将搬运代码和搬运完成跳转到的代码分段映射,即搬运代码映射到ROM地址空间,其他代码映射到RAM空间。但是这样会存在一个问题:生成的bin文件变得非常大。生成的bin文件将会按照映射到地址空间来生成,如果ROM空间与RAM空间地址不连续,假设ROM地址空间为0x0 ~ 0x1000 , RAM地址空间为0x10000~0x20000,那么, 0x1000~0x10000之间的地址空间都被填充为0,除非在生成bin文件时重新进行拼装。

如何编写位置无关代码呢?

引用同一位置无关段或相对位置固定的另一位置无关段中的符号时,必须是基于PC的符号引用,即使用相对于当前PC的偏移量来实现跳转或进行常量访问。

1.位置无关的程序跳转。使用相对跳转指令实现程序跳转。指令中所跳转的目标地址用基于当前PC的偏移量来表示,与链接时分配给地址标号的绝对地址值无关,因而代码可以在任何位置进行跳转,实现位置无关性。

2.位置无关的常量访问。在应用程序中,经常要读写相关寄存器以完成必要的硬件初始化。为增强程序的可读性,利用EQU伪指令对一些常量进行赋值,但在访问过程中,必须实现位置无关性。

3. 使用绝对地址进行跳转,一般是在不同的位置无关代码段之间跳转。

最后,总结一下位置无关代码段的优点:

1.简化设计,方便实现系统的快速引导。位置无关代码可以避免在引导时进行地址映射,并方便地跳转到RAM中实现快速引导

2.实现复位处理智能化。位置无关的代码可以被加载到任意地址空间运行

3.便于调试。Bootloader的调试通常也是一个繁琐的过程,使用位置无关代码,则可以将映像文件加载到RAM中进行调试,这既能真实地反映程序从ROM中   进行系统引导的情况,又可以避免频繁烧写程序存储器。

from:http://blog.csdn.net/ustc_dylan/article/details/6965330

位置无关代码,即该段代码无论放在内存的哪个地址,都能正确运行。究其原因,是因为代码里
没有使用绝对地址,都是相对地址。

位置无关的写法:
(1) B指令
B指令接受一个相对地址,因此在汇编里用B跳转到一个标号时,实际编译的结果是一个相对跳转。
相对地址有个范围限制,即目标不能太远,一般目标放在同一个文件里是肯定可以的。
_start:
    b  _reset
_reset:
      ...

(2) BL
BL用于调用函数,也是一个相对跳转

(3) ADR
获取标号的地址,在编译时会使用PC+偏移的方式得到该位置的地址。例如,当TEXT_BASE是0时
SMRDATA可能被放在0x100的位置,当TEXT_BASE为0x30000000时放在0x30000100的位置。使用ADR
总能获取正确的位置,与程序的加载地址无关。
    ADR R0, SMRDATA
SMRDATA:
    .word  0x22111120
    .word  0x00002F50
    .word  0x00000700
(相应的, LDR Rn, =LABEL是位置相关的)

(4) LDR
当加标号时,LDR可以用于伪指令,也可以真指令。
真指令: (标号前不加=号,表示取标号处的值)
    LDR R0,  SDRDATA
实际被编译为LDR R0, [PC, #NN],其中NN是目标的相对距离

伪指令: (标号前加=号,取标号的地址)
    LDR R0, = SDRDATA
实际编译的时候的时候,会在某位置存处SDRDATA的值,然后用一个LDR取出来。
显然,用LDR时,加不加=号有很大区别。
无=号:取该标号处的值,位置无关
有=号:取该标号的地址,位置相关

举例分析
例1:中断向量跳转
_start:    
    b       reset
    ldr    pc, _undefined_instruction
    ldr    pc, _software_interrupt
    ldr    pc, _prefetch_abort
    ldr    pc, _data_abort
    ldr    pc, _not_used
    ldr    pc, _irq
    ldr    pc, _fiq

_undefined_instruction:    .word undefined_instruction
_software_interrupt:    .word software_interrupt
_prefetch_abort:    .word prefetch_abort
_data_abort:        .word data_abort
_not_used:        .word not_used
_irq:            .word irq
_fiq:            .word fiq
其中,
ldr pc, _irq,由于没加=号,表示取值_irq处的值放在pc里 (位置无关)
_irq:  .word irq ,表示_irq存放的值是irq的绝对地址(位置有关)

例2:
bl  main ; 位置无关
ldr pc, =main; 把main的地址放在pc,位置相关

例3: 静态变量
_MAGIC_NUM:
    .word 0x12345678
取值
    LDR  R0, _MAGIC_NUM  ; 位置无关

例4: 存放标号绝对地址(绝对地址是编译的时候已经固定)
_OS_Running_p:
    .word  OS_Runing
则_OS_Running_p存放的是标号OS_Running的绝对地址

例5: 显式LDR和隐式LDR
以给某C中的变量的g_num赋值为例
(1) 使用伪指令LDR,即为隐式
    LDR  R0, =g_num    @取g_num的地址到R0
    MOV R1, #10
            STR  R1, R0
(2) 显式赋值
先定义一个变量p_g_num,用于保存g_num的地址
p_g_num:
    .word   g_num   @ g_num的绝对地址
然后赋值
    LDR R0, p_g_num
    MOV R1, #10
    STR R1,  R0
显然,两者其实一样,伪指令被展开后其实就是(2)的样子。
不同点在于:在多次引用的时候,如果使用伪指令,则会有多个临时定义。所以,
在多次引用的时候应该使用显式定义。

例6: 使用LinkScript中的变量
这种情形和例5相同
1) LinkScript中定义了两个位置
{
    __bss_start = .;
    .bss : { *(.bss) }
    _end = .;
}
2) 定义两个变量,用于存处这两个位置
.globl _bss_start
_bss_start:
    .word __bss_start

.globl _bss_end
_bss_end:
    .word _end
3) 使用这两个位置
    ldr    r0, _bss_start        /* find start of bss segment        */
    ldr    r1, _bss_end        /* stop here                        */

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值