全部博文(118)
分类: LINUX
2010-04-16 13:39:27
从monitor看, 这个环境一直在little endian运行... (big endian 实验也另作研究吧)
能否加载到任意合理地址(至少有ram吧,呵呵), 试验一下即可. 结果证明是可以的, 当然应该行,因为zImage已经支持PIC代码,并且可以配置成在纯ROM环境下运行(那就得烧到到固定地址了).
研究下zImage都包含什么东西,这个先从arch/arm/boot/Makefile看看吧:
$(obj)/compressed/vmlinux: $(obj)/Image FORCE
$(Q)$(MAKE) $(build)=$(obj)/compressed $@
$(obj)/zImage: $(obj)/compressed/vmlinux FORCE #zImage 包含解压缩头的Image
$(call if_changed,objcopy)
@echo ' Kernel: $@ is ready'
.......
$(obj)/uImage: $(obj)/zImage FORCE # U-boot image
$(call if_changed,uimage)
@echo ' Image $@ is ready'
$(obj)/bootp/bootp: $(obj)/zImage initrd FORCE 包含bootp目录的image bootpImage, 如要initrd,这个平台不支持,所以加载
$(Q)$(MAKE) $(build)=$(obj)/bootp $@ RamDisk 是monitor的事情了
@:
$(obj)/bootpImage: $(obj)/bootp/bootp FORCE #包含bootp目录的image bootpImage
$(call if_changed,objcopy)
@echo ' Kernel: $@ is ready'
内核解压缩和PIC (position independent code)
arch/arm/boot/compressed/Makefile
#
# We now have a PIC decompressor implementation. Decompressors running
# from RAM should not define ZTEXTADDR. Decompressors running directly
# from ROM or Flash must define ZTEXTADDR (preferably via the config)
# FIXME: Previous assignment to ztextaddr-y is lost here. See SHARK
ifeq ($(CONFIG_ZBOOT_ROM),y) #从boot ROM运行时需要配置一个固定地址,还有BSS的地址
ZTEXTADDR := $(CONFIG_ZBOOT_ROM_TEXT)
ZBSSADDR := $(CONFIG_ZBOOT_ROM_BSS)
else
ZTEXTADDR := 0 #一般情况下,就是0, 是pic代码加上'手工'重定位,加载到任意地址
ZBSSADDR := ALIGN(4)
endif
SEDFLAGS = s/TEXT_START/$(ZTEXTADDR)/;s/BSS_START/$(ZBSSADDR)/
#把vmlinux.lds.in 中TEXT_START换成配置的地址(主要针对ZBOOT_ROM)
targets := vmlinux vmlinux.lds piggy.gz piggy.o font.o font.c
head.o misc.o $(OBJS)
EXTRA_CFLAGS := -fpic -fno-builtin
EXTRA_AFLAGS :=
.............
# Don't allow any static data in misc.o, which
# would otherwise mess up our GOT table
CFLAGS_misc.o := -Dstatic=
$(obj)/vmlinux: $(obj)/vmlinux.lds $(obj)/$(HEAD) $(obj)/piggy.o
$(addprefix $(obj)/, $(OBJS)) FORCE
$(call if_changed,ld)
@:
$(obj)/piggy.gz: $(obj)/../Image FORCE #piggy.gz 是压缩后的内核,见piggy.S
$(call if_changed,gzip)
$(obj)/piggy.o: $(obj)/piggy.gz FORCE
CFLAGS_font.o := -Dstatic=
$(obj)/font.c: $(FONTC)
$(call cmd,shipped)
$(obj)/vmlinux.lds: $(obj)/vmlinux.lds.in arch/arm/boot/Makefile .config
@sed "$(SEDFLAGS)" < $< > $@
$(obj)/misc.o: $(obj)/misc.c include/asm/arch/uncompress.h lib/inflate.c
解压缩的pic技术以后再研究吧,挺多的.这里就是增强下信心吧. 知道可以加载到任意地址,呵呵. 下面看看decompress的入口函数start的参数问题:
arch/arm/boot/compressed/head.S
/*
* sort out different calling conventions
*/
.align
start:
.type start,#function
.rept 8
mov r0, r0
.endr
b 1f
.word 0x016f2818 &nb sp; @ Magic numbers to help the loader
.word start @ absolute load/run zImage address
.word _edata @ zImage end address
1: mov r7, r1 @ save architecture ID
mov r8, r2 @ save atags pointer
从这里看出, r1 存放的是architectureID, r2存放 atags 指针. 寄存器传递参数, 加上没有用的r0, 应该是这样一个函数:
void start(0, archID, *atags)
其实,在arch/arm/kernel/head.S中有关于参数的一段详细的注释,看看就明白了:
/*
* Kernel startup entry point.
* ---------------------------
*
* This is normally called from the decompressor code. The requirements
* are: MMU = off, D-cache = off, I-cache = dont care, r0 = 0,
* r1 = machine nr, r2 = atags pointer.
*
* This code is mostly position independent, so if you link the kernel at
* 0xc0008000, you call this at __pa(0xc0008000).
*
* See linux/arch/arm/tools/mach-types for the complete list of machine
* numbers for r1.
*
* We're trying to keep crap to a minimum; DO NOT add any machine specific
* crap here - that's what the boot loader (or in extreme, well justified
* circumstances, zImage) is for.
*/
找到mach-type是:
s3c2440 ARCH_S3C2440 S3C2440 362
不过通过实验, 我们的机器看来是SMDK兼容了,machine 参数必须传递193: SMDK2410 这个才行,而cpu类型则是自动侦测的,呵呵.
对应kernel的参数和decompressed一样:
void (*theKernel)(int zero, int arch, uint params);
另:试了试big endian,发现现在linux kernel对s3c的系统还不支持big模式.make config也无此选项.
--------------------------------------------------------------------------------
15. initrd : initial ram disk load process
让我们从新审视所得到的内核console的输出(见上文),看看需要做的东西, 先来关注最后几行的输出信息,内核相关代码是:
start_kernel->rest_init->kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);->
static int __init kernel_init(void * unused)
{
..........
/*
* check if there is an early userspace init. If yes, let it do all
* the work
*/
if (!ramdisk_execute_command) /*由内核命令行参数 rdinit= 来控制,我们没有指定 (搜索就知道是rdinit=来控制了...)*/
ramdisk_execute_command = "/init";
if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) {
ramdisk_execute_command = NULL;
prepare_namespace();
}
.........
}
内核中有各种__setup宏定义的内核参数, 其前面的字符串是内核命令行, 其后的函数是这个命令行的处理函数,相关的宏定义在init.h中.grep下,很快有结果. 详细讨论先放一放.
void __init prepare_namespace(void)
{
.............
if (saved_root_name[0]) {
root_device_name = saved_root_name; /*这个就是由 root=/dev/ram传递的内核参数,稍作搜索即知*/
if (!strncmp(root_device_name, "mtd", 3)) { /*我们当然不是这个*/
.............
}
ROOT_DEV = name_to_dev_t(root_device_name); /*/dev/ram 解析出来的设备是 ROOT_DEV= root_RAM0(1,0)*/
if (strncmp(root_device_name, "/dev/", 5) == 0)
root_device_name += 5; /* root_dev_name = "ram" */
}
if (initrd_load()) /* CONFIG_BLK_DEV_INITRD 之后才能使用initrd,我们的.config是有的*/
goto out;
......
mount_root();
out:
sys_mount(".", "/", NULL, MS_MOVE, NULL); /* /root 如何成为根, 何以叫mount root,原来并不是加载 "/"*/
sys_chroot(".");
security_sb_post_mountroot();
}
int __init initrd_load(void)
{
if (mount_initrd) { /*只有配置了内核命令行: noinitrd才为0, 我们当然没有'自杀'了*/
create_dev("/dev/ram", Root_RAM0); /*创建设备先...*/
/*
* Load the initrd data into /dev/ram0. Execute it as initrd
* unless /dev/ram0 is supposed to be our actual root device,
* in that case the ram disk is just set up here, and gets
* mounted in the normal path.
*/
if (rd_load_image("/initrd.image") && ROOT_DEV != Root_RAM0) { /*下面看看/initrd.image啥时候创建的*/
sys_unlink("/initrd.image");
handle_initrd();
return 1;
}
}
sys_unlink("/initrd.image");
return 0;
}
从我们配置的参数是Root_RAM0, 最后 void __init prepare_namespace(void)会调用mount_root:
void __init mount_root(void)
{
#ifdef CONFIG_ROOT_NFS
...
#endif
#ifdef CONFIG_BLK_DEV_FD
....
#endif
#ifdef CONFIG_BLOCK
create_dev("/dev/root", ROOT_DEV);
mount_block_root("/dev/root", root_mountflags); /*有默认值 MS_RDONLY | MS_SILENT*/
#endif
}
void __init mount_block_root(char *name, int flags)
{
get_fs_names(fs_names); /*所有已经安装的文件系统的名字列表*/
retry:
for (p = fs_names; *p; p += strlen(p)+1) { /*p代表fs type, 这里name 是/dev/root,就是Root_RAM0*/
int err = do_mount_root(name, p, flags, root_mount_data); /*(dev,type,mntflags,(rootflags=,给具体文件系统的参数) )*/
.......
/*如果失败了,下面的信息倒是没有显示出来....*/
printk("VFS: Cannot open root device "%s" or %s ",
root_device_name, b);
printk("Please append a correct "root=" boot option; here are the available partitions: ");
printk_all_partitions();
panic("VFS: Unable to mount root fs on %s", b);
}
printk("List of all partitions: ");
printk_all_partitions();
printk("No filesystem could mount root, tried: "); /*列出曾经尝试的文件系统类型*/
for (p = fs_names; *p; p += strlen(p)+1)
printk(" %s", p);
printk(" ");
.......
panic("VFS: Unable to mount root fs on %s", b); /*知名panic*/
...
}
好了上面的函数就是知名的panic. 这个过程就是将文件/initrd.image 拷贝到Root_RAM0设备内, 然后创建设备文件/dev/root(这个个文件就是为了fs的接口函数准备的),然后将 /dev/root 安装到/root, 最后升级/root到文件系统根目录 '/'.
遇到这个panic从代码上看,是do_mount_root在尝试用各种文件系统来解析/dev/root后竭尽失败.无奈之下,panic的.原因可就多了,比如config的时候没有选上ramdisk支持(设备层), 或者ramdisk中的文件系统内核不支持(文件系统层), 再或者initrd的加载出了问题(无论是boot loader 还是内核创建"/initrd.image", 最后参数传递错误也不成. 经过仔细检查,内核配置和参数传递应该没有啥问题. 这里好多内核的信息没有打印出来,详细的错误也就被隐蔽了.
为了验证ramdisk是否正确加载, 在rd_load_image("/initrd.image") 里面加了不少调试信息,结果发现, 根本没有/initrd.image这个文件,在这个函数打开这个文件的时候出错了, 这证明这个文件创建失败了. 原因也很多,不过还是先扫一眼这个文件在什么地方创建.一搜,在这个函数里呢:
static int __init populate_rootfs(void) 这是一个init函数,内核悄悄的运行了他.... (运行的地方好找,不提).
static int __init populate_rootfs(void)
{
... /*解压缩先略过不看...*/
if (initrd_start) { /*这个就是内核命令行传递进来的值...., 汗...monitor里这个值是0, 显然是不对的啊*/
/* ...创建 initrd.image 这个文件*/
fd = sys_open("/initrd.image", O_WRONLY|O_CREAT, 0700);
.......
}
好了一个问题出来了, 改吧, 去monitor的参数里修改一番,也犯了不少错误:
1) 第一个就是initrd的初始地址不能为0...,
2) 再有就是有个地方出错比如我设置initrd初始地址0x1000(随意值), 大小是0x1000(随意值,不设置长度monitor不加载ramdisk,只对此monitor有效). 这个地址有傻问题? 看看输出:
Memory policy: ECC disabled, Data cache writeback
initrd (0x00000000 - 0x000003e8) extends beyond physical memory - disabling initrd
CPU S3C2440A (id 0x32440001)
这里又跳出个地方和initrd有关, 位置在bootmem初始化中:(ft...)
arch/arm/mm/init.c
static int __init check_initrd(struct meminfo *mi)
{
.......
if (phys_initrd_size) {/*最初传递的initrd start 和size都是0, 我ft.... */
for (i = 0; i < mi->nr_banks; i++) {
...........
}
if (initrd_node == -1) {
printk(KERN_ERR "initrd (0x%08lx - 0x%08lx) extends beyond "
"physical memory - disabling initrd ",
phys_initrd_start, end);
phys_initrd_start = phys_initrd_size = 0;
}
..........
}
没有仔细探究这个bank是个什么意思(估计就是S3C2440 cpu里对内存bank的划分吧),但是这个警告信息提醒了我.超出物理内存? 不一定是地址太大,呵呵,因为啊, ram物理地址并不是从0开始的,启动的时候是S3C2440 自己吧nand读到了地址0开始的一段内部ram中.....所以,吧initrd start地址和size设置成一个在RAM地址范围内的一段地址里. 重启,这下,果然不同了........
出现的信息还是不能加载root文件系统,但是initrd加载成功了, 即ramdisk已经包含了initrd.image, "/initrd.image"文件也已经创建成功了. 但是出现RAMDISK: image too big! (2078xx/4096) ,相关函数是
int __init rd_load_image(char *from) /*从/initrd.image 写入/dev/ram设备*/
{
......
/*nblocks是从ramdisk image中根据文件系统magic猜测的block数(identify_ramdisk_image),
*rd_blocks是从/dev/ram设备中读出的数值,ramdisk大小是4096K size ,1024 blocksize,可以配置
*/
if (nblocks > rd_blocks) { /*这个信息是发现加载的initrd image大小大于ram disk的大小,加载失败*/
printk("RAMDISK: image too big! (%dKiB/%ldKiB) ",
nblocks, rd_blocks);
goto done;
}
....
}
另外,建议调试阶段把kernel consolelog 设置成verbose,就是在setup_arch的最前端,调用下面函数:
console_verbose();
这样一些提示性和continue性质的信息能够显示出来方便调试.
这个原因是什么? 根据调试信息打印出来信息, identify_ramdisk_image:RAMDISK: cramfs filesystem found at block 0
显然是发现了一个cramfs,但是不知到是什么原因. 呵呵,秘密是我还没有烧一个initrd进去呢,用的是现有的, 其大小和格式都是未知的.这样当然累了,要知道原因就太难(?), 所以要自己制作一个....
--------------------------------------------------------------------------------
16. 制作ramfs
为了测试,仅制作文件系统..步骤如下:
RDSIZE=1024
BLKSIZE=1024
# 为了测试,越小越好,创建一个1M 的ram disk,这样下载快(现在我只有串口)
dd if=/dev/zero of=ramdisk.img bs=$BLKSIZE count=$RDSIZE
#在其上制作ext2文件系统
/sbin/mke2fs -F -m 0 -b $BLKSIZE ramdisk.img $RDSIZE
-F 强制运行,不管是否是块设备/或者已经安装
-m 0 设置预留块数量的百分比, 我们可不能浪费,所以一个都不保留....呵呵.
-b 1024 一个block 1k
下载,运行,呵呵又出错了:
try load ram disk /*这是自己加的调试信息*/
RAMDISK: ext2 filesystem found at block 0 /*我们制作的是ext2文件系统,这里也认出了*/
RAMDISK: Loading 4000KiB [1 disk] into ram disk... done. /*这里看出initrd真是加载成功了,到了ramdisk0中*/
load image ret 1 /*这是自己加的调试信息, mount失败*/
原因很明显,我们根本没有编译ext2文件系统支持, 重新配置编译,运行.呵呵根文件系统安装真的成功了.不过,问题还没有完:
VFS: Mounted root (ext2 filesystem).
Freeing init memory: 92K
Warning: unable to open an initial console.
Failed to execute /linuxrc. Attempting defaults...
Kernel panic - not syncing: No init found. Try passing init= option to kernel.
这个嘛,嘿嘿,我们的文件系统上啥都没有, 得搞个什么东西上去才行啊.
--------------------------------------------------------------------------------
16.1 initrd "hello world"
也试了直接编译busybox,可是看起来busybox启动了,但是没有任何输出.查busybox的说明,busybox建议最好自己用cross toolchain搞个hello world试试,看看你能不能运行,不行的话就别搞busybox了,肯定不行啦.
故而,写hello world一个(好久不hello world了):
arm-linux-gcc -static -o test test.c
arm-linux-objcopy -S test test_strip [这个去掉大部分没有用的段,大大减小bin的大小]
然后用arm-linux-readelf -A test 看看. (aeabi) 在rootfs上(ramdisk.img)建立/dev和必要的设备文件:
mount -t ext2 ramdisk.img /mnt/initrd -o loop
mkdir dev
cp -a /dev/null dev
cp -a /dev/console dev
cp /cross/src/linux2.6.24.4/test_strip linuxrc [我们的hello world就是linuxrc,呵呵]
sync
umount /mnt/initrd
gzip -9 ramdisk.img
下载, 启动.... 没有看到我熟悉的hello world
VFS: Mounted root (ext2 filesystem).
Freeing init memory: 92K
selected clock c01adf24 (pclk) quot 26, calc 115740
google下,发现自己几乎犯过了所有的毛病,先是内核并不支持eabi串口无输出,就编译成支持eabi的内核,结果一样不幸.再google,发现float point emulation必须选一,我没选,选上重来. 最终float point终于解决了这个问题.内核中只能使用浮点模拟,运行大部分的 binaries 都需要浮点支持.
最后hello world终于搞定了. (经测试,没有eabi支持的内核照样可以hello world,以后再研究吧]
VFS: Mounted root (ext2 filesystem).
Freeing init memory: 92K
selected clock c01adf24 (pclk) quot 26, calc 115740
hello world [这就是我们期待的输出...]
--------------------------------------------------------------------------------
16.2 busy box
先编译一个busybox用用,到 下载最新的1.9.2玩玩.
配置的时候选上 don't use /usr,不然make install 就完蛋了
make ARCH=arm CROSS_COMPILE=arm-linux- allnoconfig
make ARCH=arm CROSS_COMPILE=arm-linux- menuconfig /*只要ash mount echo ls cat ps dmsg sysctl, static 连接*/
make ARCH=arm CFLAGS="-I/cross/cross-arm/arm-linux/sys-include/" CROSS_COMPILE=arm-linux-
make ARCH=arm CFLAGS="-I/cross/cross-arm/arm-linux/sys-include/" CROSS_COMPILE=arm-linux- install
(需要把applets.c的警告去掉, glibc静态连接不好用,有bug,我们就试试,玩玩, 建议用ulibc,呵呵)然后就开始制作了:
# 把我们先前制作的ramdisk.img加载到临时目录
mount ramdisk.img /mnt/initrd -t ext2 -o loop=/dev/loop0
# busy box 为我们建立好了各种符号连接,copy即可
cp -rf /cross/src/busybox-1.9.2/_install/* .
我们的rootfs就有了 bin sbin 和linuxrc
# 然后在ramdisk上建立如下目录
mkdir dev
# 创建相应的设备文件
cp -a /dev/console dev
cp -a /dev/null dev
#cp -a /dev/ttyS0 dev #没用s3c2410上串口设备是s3c2410_serial1
#cp -a /dev/ttyS1 dev #
# 解除安装, 可以压缩的(俺的很小,不压缩也罢!)
umount /mnt/initrdgzip -9 ramdisk.img
#先搞这么多,先玩一把,下面是输出,爽啊
FS: Mounted root (ext2 filesystem).
Freeing init memory: 92K
selected clock c01a9df0 (pclk) quot 26, calc 115740
selected clock c01a9df0 (pclk) quot 26, calc 115740
init started: BusyBox v1.9.2 (2008-04-18 22:44:50 CST)
selected clock c01a9df0 (pclk) quot 26, calc 115740
command='reboot' action=32 tty=''
command='umount -a -r' actselected clock c01a9df0 (pclk) quot 26, calc 115740ion=64 tty=''
command='init' action=128 tty=''
command='-/bin/sh' action=4 tty=''
command='-/bin/sh' action=4 tty='/dev/tty2'
command='-/bin/sh' action=4 tty='/dev/tty3'
command='-/bin/sh' action=4 tty='/dev/tty4'
command='/etc/init.d/rcS' action=1 tty=''
starting pid 720, tty '': '/etc/init.d/rcS'
Cannot run '/etc/init.d/rcS': No such file or directory
waiting for enter to start '/bin/sh'(pid 721, tty '')
Please press Enter to activate this console.
Can't open /dev/tty3: No such file or directory
Can't open /dev/tty4: No such file or directory
process '-/bin/sh' (pid 724) exited. Scheduling it for restart.
process '-/bin/sh' (pid 726) exited. Scheduling it for restart.
Can't open /dev/tty3: No such file or directory
Can't open /dev/tty4: No such file or directory(不断重复中,看看需要做的东西还多着呢,不过ash已经启动了...ls可以工作的..呵呵)
这个事情有人报告是个bug:
关于这个问题的报告 既然如此,先升级的1.10.1看看, 测试结果:
1. applets.c的警告没有了
2. make ARCH=arm CROSS_COMPILE=arm-linux- CFLAGS=-I/cross/cross-arm/arm-linux/sys-linux这次busybox选的东西比较多,弄了个2M的ramdisk, 压缩后399k. ...
问题一样地...,好了自己写linuxrc吧.
--------------------------------------------------------------------------------
16.3 为busybox 配置脚本
1) 先简单看看各种文件系统的作用吧:
tmpfs :/dev/shm #share mem对应的文件系统
devpts :/dev/pts #目前最常见的 pseudo 终端(PTYs)实现方式
sysfs :/sys #sysfs 包含进程相关的proc fs,设备相关的devfs以及为终端相关的devpty fs的信息,是当前趋势,
由内核内的kobj形成,udev严重依赖于sysf,能够在/dev目录下产生系统真正存在的设备,而不是一锅粥的方式
sysfs和udev配合,势必要击败devfs+devfsd(?)
udev : # 需要/etc/udev/rules.d 目录和一堆的配置规则
devfs :/dev #和devfsd配合使用, devfsd将会负责创建“旧类型”兼容性设备节点;在注册/注销设备时执行自动化操作; 负责备份对根文件系统上某个目录的设备许可权和所有权的更改,以及其它更多功能
proc :proc # proc文件系统,不必多说
2) 简单的inuxrc:
#!/bin/she
cho echo "Open ARM AKAE "
echo
mount -t proc /proc /proc
mount -t sysfs sysfs /sys
mount -t devpts devpts /dev/pts
echo "Start mdev..."
mdev -s /*mdev -s 把设备从sysfs 同步到/dev目录,需要要/etc/mdev.conf*/
init /*传递给脚本inittab...*/
3) /etc/inittab
s3c2410_serial1::respawn:-/bin/sh #/*注意s3c2410上串口的设备名称是/dev/s3c2410_serial[0..3]*/
4) /etc/mdev.conf
#空文件,有就行,用了在说吧
5) 启动信息
Open ARM akae
Start mdev...
selected clock c0239b70 (pclk) quot 26, calc 115740
init started: Busyselected clock c0239b70 (pclk) quot 26, calc 115740
Box v1.10.1 (2008-04-19 15:27:01 CST)
command='-/bin/sh ' action=2 tty='/dev/s3c2410_serial1'
starting pid 229, tty '/dev/s3c2410_serial1': '-/bin/sh '
BusyBox v1.10.1 (2008-04-19 15:27:01 CST) built-in shell (ash)
Enter 'help' for a list of built-in commands.
#
6) Trouble shoot
(1) /bin/sh: can't access tty; job control turned off
busybox 通过系统控制台来做些工作, 如果我们的shell启动在 /dev/console (5,1)上, tty_io.c 函数open中noctty就被设置成1,导致这个问题,具体原因还不知道.总之要启动在一个真实的串口就没有关系.
解决方法可以通过busybox的init指定shell的设备, 注意, S3C2410上, 串口1是s3c2410_serial1.最终就是在etc/inittab中加入下面一句话就可以了:
s3c2410_serial1::respawn:-/bin/sh #/*注意s3c2410上串口的设备名称是/dev/s3c2410_serial[0..3]*/
(2)busybox的init脚本问题 no tty3 tty4 之类
可以先启动mdev, (mdev -s), 然后运行init就可以了.
就是让/dev/tty0-4 都存在即可.... (具体原因未知)
(3) 加载mtdblock3 的jffs2文件系统出现下述信息:
jffs2_scan_eraseblock(): Magic bitmask 0x1985 not found at 0x0001001c: 0x3c04
建议:将此分区数据清除,更近一步,加载一个image到这个分区.
(4)下载了新内核,ramdisk找不到了
runaddr 和 ramdisk距离太近, 内核解压缩后覆盖了ramdisk... ft
--------------------------------------------------------------------------------