要解决的问题:
1. 虚拟地址怎么改?
2. bootloader默认把他下载到了那里?
3. 参数tag的地址怎么改 ,什么时候使用这个地址的?
4. 自解压的地址怎么改?
5. 从启动到linux开始运行,整个ram的布局的变换?
6. 怎么把kernel-2.6.13用uboot引导?
1. 虚拟地址怎么改?
生成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
导出的符号表是个证明。
3. 参数tag的地址怎么改?
在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留下足够的空间,避免冲突。
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已经可以从内存中消失了,
而内存中只有参数列表和正在解压缩的内核。
4. 自解压的地址怎么改?
首先看看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
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已经运行了(图片都出来了),只是串口没有弄好而已。也算有进展。
8
阅读(3002) | 评论(0) | 转发(3) |