Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1174801
  • 博文数量: 93
  • 博客积分: 7185
  • 博客等级: 准将
  • 技术积分: 3560
  • 用 户 组: 普通用户
  • 注册时间: 2007-04-02 13:54
文章分类
文章存档

2011年(43)

2010年(11)

2009年(27)

2008年(12)

分类: 嵌入式

2009-09-07 23:28:15

文件: S3C2440 从cross-tools到 linux2.6.24.4.zip
大小: 43KB
下载: 下载
14. 内核加载地址和start参数问题
   从上面的分析, 知道内核被加载到0x30400 0000, 这个其实是zImage的加载地址, 就是说内核解压缩程序的运行地址. 还有一个问题,我们的内核一直是little endian . arm-linux-objdump下就知道.
  
一个问题是Big-endian的内核如何run, 另一个是解压缩程序的运行地址是随意的吗?

  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 kernels3c的系统还不支持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进去呢,用的是现有的, 其大小和格式都是未知的.这样当然累了,要知道原因就太难(?), 所以要自己制作一个....

完整下载

阅读(1437) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~