Linux3.4.72内核启动分析2 -- 让串口正常工作

在上一篇文章中介绍了zImage是怎么生成的,这对理解Linux 启动的第一部分汇编很有用。由于移植的Linux3.4.72源码是纯净的,所以可以从头学习如果移植Linux到samsung 2440 芯片。

首先需要更改Linux根目录中的Makefile 文件

#ARCH		?= $(SUBARCH)
ARCH		?= arm
#CROSS_COMPILE	?= $(CONFIG_CROSS_COMPILE:"%"=%)
CROSS_COMPILE	?= arm-linux-

为什么要更改文件的这个地方? 如果不更改这里,将ARCH 设置成arm,那么在下面执行 make s3c2410_defconfig就会出错。原因就是找不到s3c2410_defconfig.

原来的Makefile文件中ARCH值为

SUBARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ \
      -e s/arm.*/arm/ -e s/sa110/arm/ \
      -e s/s390x/s390/ -e s/parisc64/parisc/ \
      -e s/ppc.*/powerpc/ -e s/mips.*/mips/ \
      -e s/sh[234].*/sh/ )
SUBARCH得到的结果就是当前主机的体系结构--x86。 所以如果不更改这个地方,就会去arch/x86/configs目录中查找。

更改Makefile文件后,执行 $# make  s3c2410_defconfig  结果就是将arch/arm/configs/s3c2410_defconfig拷贝到根目录下的.config文件。

接下来在linux的根目录下,直接执行make就可以了,最终就会生成zImage文件,位置arch/arm/boot/zImage。

将该zImage用TQ2440的u-boot烧写进nandflash中,然后启动,在串口中没有看到任何信息,说明串口还是有问题的。

为什么呢???

第一就是U-boot向内核提供的参数没有被内核找到或者解析不正确。我使用的是TQ提供好的u-boot,还好有源码。在u-boot源码中我知道,其将参数存放在了0x30000100的地址,并且是按照struct param_struct  这种结构体存储的。struct param_struct  是一种比较古老的u-boot传递给kernel的参数格式。 先说第一种情况,即位置Linux 3.4.70寻址的位置是否正确。 这里先说下结果,位置是正确的,所以是解析不正确造成的。

MACHINE_START(S3C2440, "SMDK2440")
	/* Maintainer: Ben Dooks <ben-linux@fluff.org> */
	.atag_offset	= 0x100,    //参数位移

	.init_irq	= s3c24xx_init_irq,
	.map_io		= smdk2440_map_io,
	.init_machine	= smdk2440_machine_init,
	.timer		= &s3c24xx_timer,
	.restart	= s3c244x_restart,
MACHINE_END

这部分代码时mach-s3c2440.c中的。 在这个结构体重,.atag_offset被设置成0x100,它在下面的地方被用到,调用的顺序是 start_kernel()-> setup_arch()->setup_machine_tags()

static struct machine_desc * __init setup_machine_tags(unsigned int nr)
{
    struct tag *tags = (struct tag *)&init_tags;
    struct machine_desc *mdesc = NULL, *p;
    char *from = default_command_line;

 	...................
    for_each_machine_desc(p)   // 查找 符合的machine_desc, 我这里的结果就是mach-s3c2440定义的
        if (nr == p->nr) {
            printk("Machine: %s\n", p->name);
            mdesc = p;
            break;
        }
	.................

    if (__atags_pointer)
        tags = phys_to_virt(__atags_pointer);
    else if (mdesc->atag_offset)
        tags = (void *)(PAGE_OFFSET + mdesc->atag_offset);   // 由于mmu已经打开,所以这个PAGE_OFFSET 是0xc0000000.这个地址是虚拟地址,对应的是0x30000000,
							    // mdesc->atag_offset就是0x100,所以地址就是0x30000100,与u-boot的将参数存储的地址一样。

#if defined(CONFIG_DEPRECATED_PARAM_STRUCT)   // 这里很重要,由于我先前没有打开这个宏,导致kernel无法识别 旧的struct param_struct 结构体。所以这里需要将宏					     //打开,让kernel 帮助将 param_struch转换成 tags。
    /*
     * If we have the old style parameters, convert them to
     * a tag list.
     */
    if (tags->hdr.tag != ATAG_CORE)
        convert_to_tag_list(tags);  // 这里就是转换的函数
#endif


    if (mdesc->fixup)
        mdesc->fixup(tags, &from, &meminfo);

    if (tags->hdr.tag == ATAG_CORE) {
        if (meminfo.nr_banks != 0)
            squash_mem_tags(tags);
        save_atags(tags);
        parse_tags(tags);   // 这里面有一个tag的操作会调用到parse_tag_cmdline()函数,这个函数主要是给default_command_line数组赋值,这个数组就是form。			    	  //至于如个调用到这个函数的,与调用arch_initcall宏标记过的函数是一样的,这里就不说了。
    }
       
    /* parse_early_param needs a boot_command_line */
    strlcpy(boot_command_line, from, COMMAND_LINE_SIZE);  
    early_print("boot_command_line =%s\n",boot_command_line); // 打印结果是"noinitrd root=/dev/mtdblock2 init=/linuxrc console=ttySAC0" ,u-boot传输的参数中虽然带了console的名称,但是没有带波特率,所以我的kernel的默认是38400. 如果想让u-boot带上波特率,那么这样写  console=ttySAC0,115200n8r
    //strlcpy(boot_command_line,"noinitrd root=/dev/mtdblock2 init=/linuxrc console=ttySAC0,115200n8r",COMMAND_LINE_SIZE); // 这里是我原来修改的方法,挫死了
    return mdesc;
}

至于这个console=ttySAC0,115200n8r最终是怎么被串口驱动用到的这里也不多讲了,那块用到东西比较多,虽然我是从串口的驱动程序开始的线索,读了3遍,依然不能很清楚的描述出来,比较做了很多得抽象工作在里面,不过我还是准备在读一遍。建议可以从printk的实现来看,找到strcut  console console_console_drivers 相关的东西,然后在从串口的驱动注册函数看,最终的结果是他俩肯定是有关系的。

第二就是内核中的时钟源设置的不对,这个比较简单了,在上面的问题调整出来,串口上会打印乱码,而且无论怎么设置串口工具的波特率打印的依然是乱码,所以想到的就是时钟不对,顺藤摸瓜就可以了。我所用的开发板时钟源是12Mhz,而Linux中的源码设置是16934400hz。
更改的位置:

static void __init smdk2440_map_io(void)
{
    s3c24xx_init_io(smdk2440_iodesc, ARRAY_SIZE(smdk2440_iodesc));
   // s3c24xx_init_clocks(16934400);
    s3c24xx_init_clocks(12000000);
    s3c24xx_init_uarts(smdk2440_uartcfgs, ARRAY_SIZE(smdk2440_uartcfgs));
}
这样我的开发板的串口就可以正常的工作了。 但是启动到文件系统的时候就停止了,以为还没弄到哪里。
上一遍中提到的问题,linux kernel 第一部分汇编做了 什么,其跟串口初始是否有关系。答案是没有关系的,至少在s3c2440的情况下没有。我在开始调试的时候,只能从汇编开始看起,还好汇编中提供了打印的方法,不至于我无路可走。 至于linux kernel 的汇编做了什么有机会再分析。
在printk正常工作前 依然可以依靠打印进行调试的,在汇编中调用 kputc 和 kphex, 在c文件中调用early_print函数,这些函数都是用宏控制的,默认是关闭的。注意:在汇编中的打印函数会调用r0 r1 r2 寄存器,所以一旦调用了打印函数,就会引起启动异常,所以在得到打印结果后,需要将打印函数去掉。
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值