您的位置:首页 > 其它

怎么把kernel-2.6.13用uboot引导?

2013-04-05 22:43 344 查看
要解决的问题:

1. kernel虚拟地址怎么改?

2. bootloader默认把他下载到了那里?

3. 参数tag的地址怎么改 ,什么时候使用这个地址的?

4. 自解压的地址怎么改?

5. 从启动到linux开始运行,整个ram的布局的变换?

6. 怎么把kernel-2.6.13用uboot引导?

1. 虚拟地址怎么改?(运行时地址,自解压后kernel地址)

生成vmlinux的命令如下:

/usr/local/arm/3.4.1/bin/arm-linux-ld -EL -p --no-undefined -X -o vmlinux -T arch/arm/kernel/vmlinux.lds arch/arm/kernel/head.o arch/arm/kernel/init_task.o init/built-in.o
--start-group usr/built-in.o arch/arm/kernel/built-in.o arch/arm/mm/built-in.o arch/arm/common/built-in.o arch/arm/mach-s3c2410/built-in.o arch/arm/nwfpe/built-in.o kernel/built-in.o mm/built-in.o fs/built-in.o ipc/built-in.o security/built-in.o
crypto/built-in.o lib/lib.a arch/arm/lib/lib.a lib/built-in.o arch/arm/lib/built-in.o drivers/built-in.o sound/built-in.o net/built-in.o --end-group .tmp_kallsyms2.o

可见连接脚本在这里。

-T arch/arm/kernel/vmlinux.lds

他的内容如下:

SECTIONS

{

. = 0xC0008000; 虚拟地址就是这里了。

.init : { /* Init code and data */

_stext = .;

_sinittext = .;

*(.init.text)

_einittext = .;

......

但是这个文件是本目录下vmlinux.lds.S文件生成的,vmlinux.lds.S内容如下

SECTIONS

{

. = TEXTADDR;

.init : { /* Init code and data */

_stext = .;

_sinittext = .;

*(.init.text)

_einittext = .;

......

看看 TEXTADDR 这个车票是怎么来的,在/arch/arm/makefile中

textaddr-y := 0xC0008000

TEXTADDR := $(textaddr-y)

可见就是从这里来的。

改成我想要的试试

textaddr-y := 0xC0009000

编译出错,出错信息如下:

arch/arm/kernel/head.S:48:2: #error TEXTADDR must start at 0xXXXX8000

去head.S看看,有如下内容

/*

* We place the page tables 16K below TEXTADDR. Therefore, we must make sure

* that TEXTADDR is correctly set. Currently, we expect the least significant

* 16 bits to be 0x8000, but we could probably relax this restriction to

* TEXTADDR >= PAGE_OFFSET + 0x4000

*

* Note that swapper_pg_dir is the virtual address of the page tables, and

* pgtbl gives us a position-independent reference to these tables. We can

* do this because stext == TEXTADDR

*/

#if (TEXTADDR & 0xffff) != 0x8000

#error TEXTADDR must start at 0xXXXX8000

#endif

就是说必须是0xXXXX8000 这样的虚拟地址。

改成下面的试试

textaddr-y := 0xa0008000

ok

SECTIONS

{

. = 0xa0008000;

.init : { /* Init code and data */

_stext = .;

_sinittext = .;

*(.init.text)

......

上面是新生成的vmlinux.lds,就是我想要的了,一直到最后都没有错误,0xc0000000-0xffffffff的1g分配给内核,这符合道理,呵呵。

lzd@lzd-laptop:~/Desktop/kernel-2.6.13$ cat System.map |head -20

a0004000 A swapper_pg_dir

a0008000 T __init_begin

a0008000 T _sinittext

a0008000 T stext

a0008000 T _stext

a000802c t __switch_data

a0008050 t __mmap_switched

a0008094 t __enable_mmu

a00080c0 t __turn_mmu_on

a00080d8 t __create_page_tables

a0008148 t __error

a0008148 t __error_a

a0008148 t __error_p

a0008150 t __lookup_processor_type

a000818c T lookup_processor_type

a00081b0 t __lookup_machine_type

a00081e4 T lookup_machine_type

a0008200 t nosmp

a0008224 t maxcpus

a0008254 t debug_kernel

导出的符号表是个证明。

2.bootloader默认把他(内核)下载到了那里?

最后生成的内核镜象有两种zImage以及uImage。其中zImage下载到目标板中后,可以直接用uboot的命令go来进行直接跳转。

这时候内核直接解压启动。但是无法挂载文件系统,因为go命令没有将内核需要的相关的启动参数传递给内核。传递启动参数我

们必须使用命令bootm来进行跳转。Bootm命令跳转只处理uImage的镜象(什么时候读读bootm,看看是怎么传递tag list的)。

#define CONFIG_BOOTDELAY 3

/*#define CONFIG_BOOTARGS "root=ramfs devfs=mount console=ttySA0,9600" */

/*#define CONFIG_BOOTCOMMAND "tftp; bootm" */

通常,上面要自己设置,因为每个人下载uimage的地点不同。比如

#define CONFIG_BOOTCOMMAND "nboot 0x32000000 0 0 ;bootm 0x32000000"

先把nandflash中的内核下载到0x32000000(解压前地址),然后传递启动参数(只是传递启动参数吗?),跳过去。

这样uboot kernel taglist在内存的不同位置,不冲突,也就是说0x32000000不是随便设置的,首先他不能覆盖taglist的位置

也不能覆盖uboot,更重要的是要给解压缩后内核的地址留出空间来。内核开始解压启动。到了这里,uboot已经可以从内存中消失了,

而内存中只有参数列表和正在解压缩的内核。

3. 参数tag的地址怎么改?(一般在内存的最开始部分0x30000100)

在uboot中的board/smdk2410/smdk2410.c中(其他的板子会有变化)

/* adress of boot parameters */

gd->bd->bi_boot_params = 0x30000100;

这个0x30000100就是内核参数的地址,当然,这么作只是uboot单方的,如果内核不知道,也没有用。

在arch/arm/s3c2410/mach-smdk2410.c中

MACHINE_START(SMDK2410, "SMDK2410") /* @TODO: request a new identifier and switch

* to SMDK2410 */

/* Maintainer: Jonas Dietsche */

.phys_ram = S3C2410_SDRAM_PA,

.phys_io = S3C2410_PA_UART,

.io_pg_offst = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,

.boot_params = S3C2410_SDRAM_PA + 0x100, 就是这里了。

.map_io = smdk2410_map_io,

.init_irq = smdk2410_init_irq,

.timer = &s3c24xx_timer,

MACHINE_END

在include/asm-arm/arch-s3c2410/map.h中

#define S3C2410_CS6 (0x30000000)

#define S3C2410_SDRAM_PA (S3C2410_CS6)

可见

.boot_params = 0x30000100 正好与uboot约定的地址吻合。

至于内核什么时候使用这个参数,以后在分析。???

在board/smdk2410/config.mk中只有下面的定义

TEXT_BASE = 0x33F80000

这个是用来确定,uboot要把自家加载到内核的什么地址的地址。为什么要这么靠后呢?为了给uimage留下足够的空间,避免冲突。

4. 自解压的地址怎么改?(把内核解压缩到0x30008000,虚拟地址0xC0008000)

首先看看uimage的解压缩地址。

在***uimage的时候已经指定了,他的地址是0x30008000,有如下的命令:

mkimage -A arm -O linux -T kernel -C gzip -a 0x30008000 -e 0x30008000 -n "linux kernel image" -d linux.bin.gz uImage

在来看看zImage的解压缩地址。

在arch/arm/boot/makefile 中

ifneq ($(MACHINE),)

include $(srctree)/$(MACHINE)/Makefile.boot

endif

# Note: the following conditions must always be true:

# ZRELADDR == virt_to_phys(TEXTADDR)

# PARAMS_PHYS must be within 4MB of ZRELADDR

# INITRD_PHYS must be in RAM

ZRELADDR := $(zreladdr-y)

PARAMS_PHYS := $(params_phys-y)

INITRD_PHYS := $(initrd_phys-y)

而在arch/arm/mach-s3c2410/makefile.boot中

zreladdr-y := 0x30008000

params_phys-y := 0x30000100

也就是说,在这里可以设置内核的解压缩地址,和taglist的地址。

4. 从启动到linux开始运行,整个ram的布局的变换?

经过上面的分析,已经很清晰了。

6. 怎么把kernel-2.6.13用uboot引导?

生成uboot格式的内核压缩镜像

arm-linux-objcopy -O binary -R .comment -S ../vmlinux linux.bin &&gzip -9 linux.bin&&./mkimage -A arm -O linux -T kernel -C gzip -a 0x30008000 -e 0x30008000 -n "linux
kernel image" -d linux.bin.gz uimage

gzip -9 linux.bin

./mkimage -A arm -O linux -T kernel -C gzip -a 0x30008000 -e 0x30008000 -n "linux kernel image" -d linux.bin.gz uimage

lzd> nfs 0x32000000 /home/lzd/nfs/uimage (nfs到内存0x32000000)


lzd> bootm 0x32000000

## Booting image at 32000000 ...

Image Name: linux kernel image

Created: 2009-09-30 10:57:54 UTC

Image Type: ARM Linux Kernel Image (gzip compressed)

Data Size: 1534476 Bytes = 1.5 MB

Load Address: 30008000

Entry Point: 30008000(解压后地址)

Verifying Checksum ... OK

Uncompressing Kernel Image ... OK



Starting kernel ...

会死在这里,为什么呢?

我用的vmlinux是源代码根目录下的vmlinux,用了arch/arm/boot/compress/vmlinux就会这样了



Starting kernel ...



Uncompressing Linux.............................................................



不论哪种情况, 在跳到 Linux 内核执行之前 CPU 的寄存器必须满足以下条件:
r0=0,

r1=处理器类型,

r2=标记列表在 RAM 中的地址。

调试如下

lzd> bootm 0x32000000

## Booting image at 32000000 ...

Image Name: Linux Kernel Image

Created: 2009-10-02 2:49:30 UTC

Image Type: ARM Linux Kernel Image (gzip compressed)

Data Size: 1534476 Bytes = 1.5 MB

Load Address: 30008000

Entry Point: 30008000

Verifying Checksum ... OK

Uncompressing Kernel Image ... OK

argc = 2

No initrd

## Transferring control to Linux (at address 30008000) ...



Starting kernel ...



theKernel (0, bd->bi_arch_number, bd->bi_boot_params);

theKernel:30008000,0,16a,30000100

theKernel的地址是0x30008000 r0=0 r1=0x16a r2=0x30000100

跟上面的描述是一致的,为什么启动不起来呢?

现在就跳到了head.S文件,在没有用r0 r1 r2 之前,不能破坏他们的数据。

在lds里有

__arch_info_begin = .;

*(.arch.info)

__arch_info_end = .;

作了一个点灯代码,在uboot里go了一下,闪烁的挺快的

把他加到linux的head.S文件后,在运行这个uimage,发现速度超级慢,不知道原因何在?

但是这说明了,在解压缩内核后,他确实被解压缩到了0x30008000的位置,并且传递的r0

r1 r2都是正确的,只是速度下来了不少。

如果把linux.bin(没有经过压缩的文件)文件直接下载到0x30008000位置,然后go的话,速度没问题。

linux.bin文件就是经过objcopy的二进制文件,他不是gzip压缩的。

go的关键代码如下:

rc = ((ulong (*)(int, char *[]))addr) (--argc, &argv[1]);

哦,找到原因了:

cleanup_before_linux ();

debug("theKernel (0, bd->bi_arch_number, bd->bi_boot_params);\n");

debug("theKernel:%x,0,%x,%x\n",theKernel,bd->bi_arch_number,bd->bi_boot_params);

theKernel (0, bd->bi_arch_number, bd->bi_boot_params);

int cleanup_before_linux (void)

{

/*

* this function is called just before we call linux

* it prepares the processor for linux

*

* we turn off caches etc ...

*/

unsigned long i;

disable_interrupts ();

/* turn off I/D-cache */

asm ("mrc p15, 0, %0, c1, c0, 0":"=r" (i));

i &= ~(C1_DC | C1_IC);

asm ("mcr p15, 0, %0, c1, c0, 0": :"r" (i));

/* flush I/D-cache */

i = 0;

asm ("mcr p15, 0, %0, c7, c7, 0": :"r" (i));

return (0);

}

呵呵,go和bootm的区别很大阿,

go只是简单的跳过去,bootm作了很多事,这里他/* turn off I/D-cache */并且

/* flush I/D-cache */,以前不太了解i/d cache对速度的提高有多大影响,现在明白了,原来影响这么大。

这说明uboot 解压缩正常,跳到linux的head.S的代码都正常,主要原因还是没出来,但是排除了 有降低cpu速度的原因。

现在可以开始调试linux这个大家伙了。

bl __lookup_processor_type @ r5=procinfo r9=cpuid

movs r10, r5 @ invalid processor (r5=0)?

beq __error_p @ yes, error 'p'

把点灯代码放在这里,ok,

bl __lookup_machine_type @ r5=machinfo

movs r8, r5 @ invalid machine (r5=0)?

beq __error_a

但是放在这里就死掉了,呵呵,找到原因了。

/*

* Look in include/asm-arm/procinfo.h and arch/arm/kernel/arch.[ch] for

* more information about the __proc_info and __arch_info structures.

*/

.long __proc_info_begin

.long __proc_info_end

3: .long .

.long __arch_info_begin

.long __arch_info_end

/*

* Lookup machine architecture in the linker-build list of architectures.

* Note that we can't use the absolute addresses for the __arch_info

* lists since we aren't running with the MMU on (and therefore, we are

* not in the correct address space). We have to calculate the offset.

*

* r1 = machine architecture number

* Returns:

* r3, r4, r6 corrupted

* r5 = mach_info pointer in physical address space

*/

.type __lookup_machine_type, %function

__lookup_machine_type:

adr r3, 3b

ldmia r3, {r4, r5, r6}

sub r3, r3, r4 @ get offset between virt&phys

add r5, r5, r3 @ convert virt addresses to

add r6, r6, r3 @ physical address space

1: ldr r3, [r5, #MACHINFO_TYPE] @ get machine type MACHINFO_TYPE是偏移量,为0

teq r3, r1 @ matches loader number?

beq 2f @ found

add r5, r5, #SIZEOF_MACHINE_DESC @ next machine_desc

cmp r5, r6

blo 1b

mov r5, #0 @ unknown machine

2: mov pc, lr

分析:

现看看__arch_info_begin在那个位置。

lzd@lzd-laptop:~/Desktop/kernel-2.6.13$ cat System.map |grep arch_info_begin

a001d794 T __arch_info_begin

在虚拟地址 0xa001d794 处,后面的注释很清晰,r5放的是 __arch_info_begin 的物理地址

r6放的是 __arch_info_end 的物理地址,就是实实在在可以访问到该数据的地址,大概在0x30008000以上的某个地儿。

在前面我把虚拟地址改了,现在改回来0xc0008000可能是这个地址引起的。后面证明不是

下面是点灯代码:

LDR R0,=0x56000010

MOV R1,#0x00000400

STR R1,[R0]

MAIN_LOOP: LDR R0,=0x56000014

MOV R1,#0x00000000

STR R1,[R0]

mov r5, #0x100000

1: subs r5, r5, #1

bne 1b

MOV R1,#0xffffffff

STR R1,[R0]

mov r5, #0x100000

1: subs r5, r5, #1

bne 1b

B MAIN_LOOP

在/include/asm-arm/match/arch.h中有 machine_desc 的定义

struct machine_desc {

/*

* Note! The first five elements are used

* by assembler code in head-armv.S

*/

unsigned int nr; /* architecture number */

unsigned int phys_ram; /* start of physical ram */

unsigned int phys_io; /* start of physical io */

unsigned int io_pg_offst; /* byte offset for io

* page tabe entry */

const char *name; /* architecture name */

unsigned long boot_params; /* tagged list */

unsigned int video_start; /* start of video RAM */

unsigned int video_end; /* end of video RAM */

unsigned int reserve_lp0 :1; /* never has lp0 */

unsigned int reserve_lp1 :1; /* never has lp1 */

unsigned int reserve_lp2 :1; /* never has lp2 */

unsigned int soft_reboot :1; /* soft reboot */

void (*fixup)(struct machine_desc *,

struct tag *, char **,

struct meminfo *);

void (*map_io)(void);/* IO mapping function */

void (*init_irq)(void);

struct sys_timer *timer; /* system tick timer */

void (*init_machine)(void);

};

arm-linux-objdump -d vmlinux > dump

从dump文件中找__arch_info_begin,得到如下的信息。

对应上面的数据结构,分析。

c001d754 <__arch_info_begin>:

c001d754: 0000030e 30e对应着nr,在include/asm/match-types.h中#define MACH_TYPE_QQ2440 782(0x30e)

c001d758: 30000000 andcc r0, r0, r0

c001d75c: 50000000 andpl r0, r0, r0

c001d760: 00003c20 andeq r3, r0, r0, lsr #24

c001d764: c026cb70 eorgt ip, r6, r0, ror fp

c001d768: 30000100 andcc r0, r0, r0, lsl #2

...

c001d77c: c001107c andgt r1, r1, ip, ror r0

c001d780: c00110d8 ldrgtd r1, [r1], -r8

c001d784: c02a98b4 strgth r9, [sl], -r4

c001d788: c00110ec andgt r1, r1, ip, ror #1

c001d78c <__arch_info_end>:

78c - 754 = 56(dec)

而 machine_desc 也正好是56个字节,14个机器字(4byte)

现在发现原因了,怪不得不匹配,要传递给它的应该是0x30e才ok,而我的uboot传递的是 0x16a(bd->bi_arch_number)

现在要么改掉uboot的 bd->bi_arcmachine_arch_type

要么改掉linux的0x30e

# undef machine_arch_type

# define machine_arch_type __machine_arch_type

# else

# define machine_arch_type MACH_TYPE_QQ2440

# endif

# define machine_is_qq2440() (machine_arch_type == MACH_TYPE_QQ2440)

#else

# define machine_is_qq2440() (0)

#endif

哦,现在明白了,在config_n35配置文件中,有这样的定义

CONFIG_SOUND_QQ2440=y

#ifdef CONFIG_ARCH_QQ2440

# ifdef machine_arch_type

# undef machine_arch_type

# define machine_arch_type __machine_arch_type

# else

# define machine_arch_type MACH_TYPE_QQ2440

# endif

# define machine_is_qq2440() (machine_arch_type == MACH_TYPE_QQ2440)

#else

# define machine_is_qq2440() (0)

#endif

就是说在config_n35里面可以定义这个板子的类型,如果和uboot传递进来的0x16a匹配,就ok了。

我不想改uboot传进来的0x16a,那就去改config_n35的配置去。

#define MACH_TYPE_S3C2440 362(0x16a)

所以要在config_n35里改成 MACH_TYPE_S3C2440对应的配置。

在include/asm/match-types.h中

#ifdef CONFIG_ARCH_S3C2440

# ifdef machine_arch_type

# undef machine_arch_type

# define machine_arch_type __machine_arch_type

# else

# define machine_arch_type MACH_TYPE_S3C2440

# endif

# define machine_is_s3c2440() (machine_arch_type == MACH_TYPE_S3C2440)

#else

# define machine_is_s3c2440() (0)

#endif

也就是说要定义CONFIG_ARCH_S3C2440 为y。

# CONFIG_ARCH_S3C2440 is not set

# CONFIG_ARCH_AESOP2440 is not set

CONFIG_ARCH_QQ2440=y

就是说要关掉CONFIG_ARCH_QQ2440,打开CONFIG_ARCH_S3C2440

ok

lzd@lzd-laptop:~/Desktop/kernel-2.6.13$ make menuconfig

在arch/arm/match-s3c2410/Kconfig中

config ARCH_S3C2440

bool "SMDK2440"

select CPU_S3C2440

help

Say Y here if you are using the SMDK2440.

config ARCH_QQ2440

bool "QQ2440/mini2440"

select CPU_S3C2440

help

Say Y here if you are using the FriendlyARM QQ2440 or mini2440.

在同样目录的 makefile中

obj-$(CONFIG_ARCH_BAST) += mach-bast.o usb-simtec.o

obj-$(CONFIG_ARCH_H1940) += mach-h1940.o

obj-$(CONFIG_MACH_N30) += mach-n30.o

obj-$(CONFIG_ARCH_SMDK2410) += mach-smdk2410.o

obj-$(CONFIG_ARCH_S3C2440) += mach-smdk2440.o

# ghcstop add

obj-$(CONFIG_ARCH_AESOP2440) += mach-aesop2440.o

obj-$(CONFIG_ARCH_QQ2440) += mach-qq2440.o

obj-$(CONFIG_MACH_VR1000) += mach-vr1000.o usb-simtec.o

obj-$(CONFIG_MACH_RX3715) += mach-rx3715.o

obj-$(CONFIG_MACH_OTOM) += mach-otom.o

obj-$(CONFIG_MACH_NEXCODER_2440) += mach-nexcoder.o

就是说可以定义多个板子的支持,在mach-xxx.c中定义了该板子相关的一些内容,比如machine_desc。

也就是说要打开

CONFIG_ARCH_S3C2440

就要选上这个条目了,同时还要关掉ARCH_QQ2440这个条目。

好了,去make喽。

ok

nfs

bootm

有了新的问题,输出来的东西是乱码,但是有输出,说明linux已经运行了(图片都出来了),只是串口没有弄好而已。也算有进展。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: