Chinaunix首页 | 论坛 | 博客
  • 博客访问: 642848
  • 博文数量: 75
  • 博客积分: 7001
  • 博客等级: 少将
  • 技术积分: 1465
  • 用 户 组: 普通用户
  • 注册时间: 2007-07-11 17:39
文章分类

全部博文(75)

文章存档

2010年(1)

2009年(25)

2008年(49)

我的朋友

分类: LINUX

2008-09-03 15:42:43

linux内核的移植与遭遇问题的解决
前些日子,完成u-boot的移植,随后所作的事情就是进行linux内核的移植,在移植的过程中遭遇了一些莫名其妙的错误,但无论如何,已经实现了最终的移植工作,在移植的过程中学会了一些内核的debug方法,依据此方法对内核启动过程进行了跟踪,相关的内容也将在本章节内进行阐述。
    移植硬件平台 友善之臂 s3c2410,内核版本 linux 2.6.20.3,交叉编译器gcc4.0.2
    交叉编译器的建立文章参见ARM-Linux交叉编译工具链制作攻略

    linux内核移植过程:
    (1)解压内核并进入内核代码树工作目录。

tar xjvf linux-2.6.20.3.tar.bz2
cd linux-2.6.20.3
    (2)修改Makefile文件。

vi Makefile
找到如下变量,并按照下述修改
ARCH =arm
CROSS_COMPILE =arm-linux-
    (3)在arch/arm/mach-s3c2410/common-smdk.c文件内修改flash相关信息。
  
a.分区信息设置
static struct mtd_partition smdk_default_nand_part[] = {

     [0] = {//建立bootloader 分区
          .name = "U-Boot-1.2.0",
          .size  = SZ_128K,
          .offset = 0,
     },
     [1] = {//建立u-boot参数分区
          .name = "U-Boot Parameter",
          .offset = SZ_128K,
          .size = SZ_64K,
     },
     [2] = {//建立linux内核分区
          .name = "Linux2.6.20.3",
          .offset = SZ_128K+SZ_64K,
          .size = SZ_4M+(SZ_1M-SZ_128K-SZ_64K),
     },
     [3] = {//建立根文件分区(实际用nfs,该部分用作debug内核)
          .name = "Rootfs",
          .offset = SZ_1M * 5,
          .size = SZ_1M * 5,
     },
     [4] = {//用户自定义分区
          .name = "Userfs",
          .offset = SZ_1M * 10,
          .size = SZ_1M * 55,
     }
};
b.设置flash硬件特性
static struct s3c2410_platform_nand smdk_nand_info = {
    .tacls        = 0,
    .twrph0        = 30,
    .twrph1        = 0,
    .nr_sets    = ARRAY_SIZE(smdk_nand_sets),
    .sets        = smdk_nand_sets,
};
    (4)修改drivers/mtd/nand/s3c2410.c文件中的ECC校验信息

chip->ecc.mode        = NAND_ECC_NONE;
    (5)配置内核(略:网上有很多类似文章例如:移植Linux2.6.22.2到博创2410-S(s3c2410A)系列)
    (6)编译内核
make zImage

    以上的工作给出一个patch,将此patch打上后,编译出的内核就是可以直接运行于友善之臂上。
 

文件:SBC2410-kernel.2.6.20.3.patch.tar.gz
大小:0KB
下载:下载
    
    随后的工作就是要将内核运行到卡发板上,运行的方式是tftp的方式。相关参数在u-boot中进行设置。
setenv bootcmd tftp 0x30008000 zImage.img;bootm
setenv bootargs console=ttySAC0,115200 mem=64M
saveenv
    由于一个弱智的错误,导致我的移植工作移植停留在linux解压后暂停的过程中,如下:
Uncompressing Linux.............................................................
    错误的根源如下:
    我使用的是rhel5的linux的操作系统,该系统的firefox浏览器在浏览网页时有时出现字体的覆盖,上个窗体中的mem=64M在我的浏览器中是如下的样子:mem64M。由于我整个移植过程的资料全都是依赖于网络上过来人的移植信息,因此我非常绝对的相信了这个mem64M
    错误虽然已经犯下,但也算是因或得福,在调整这个错误的过程中,我跟踪了这个linux2.6.20.3版本的内核启动方式,并采用了汇编以及led并用的调试方式,最终判定错误的出现原因,因此也在此进行整理。

    调试技巧:

    (1) 字符终端输出方式。
在内核中arch/arm/kernel/head.S中存在一个debug函数叫做printascii,该函数的汇编使用方法如下:
    
adr    r0, str_p1
bl    printascii
str_p1:.asciz"\nError: unrecognized/unsupported processor variant.\n"

    使用该调试函数可以实现低级调试信息在终端上输出。

    (2) led点灯方式。
ldr r1, =0x56000010
ldr r2, =0x154000 
str r2, [r1]

ldr r1, =0x56000018
mov r2, #0x00000000
str r2, [r1]

ldr r1, =0x56000014
mov r2, #0x00000000
str r2, [r1]
b .   /*因为改变了寄存器值会影响系统随后的启动,因此在此进入死循环*/
        运行该代码后,板子上的led全部都会被打开。   

调试跟踪过程
    因为我的错误起源于解压之后,因此,我的调试工作也从解压后的第一个执行的程序开始,该程序存在于arch/arm/kernel/head.S文件。
    引导过程中的主工作流程代码如下:
    __INIT
    .type    stext, %function
ENTRY(stext)
    msr    cpsr_c, #PSR_F_BIT | PSR_I_BIT | SVC_MODE 
                                     @ ensure svc mode
                                     @ and irqs disabled
    mrc    p15, 0, r9, c0, c0        @ get processor id
    bl    __lookup_processor_type    @ r5=procinfo r9=cpuid
    movs    r10, r5                  @ invalid processor (r5=0)?
    beq    __error_p                 @ yes, error 'p'
    bl    __lookup_machine_type      @ r5=machinfo
    movs    r8, r5                   @ invalid machine (r5=0)?
    beq    __error_a                 @ yes, error 'a'
    bl    __create_page_tables
    ldr    r13, __switch_data        @ address to jump to after
                                     @ mmu has been enabled
    adr    lr, __enable_mmu          @ return (PIC) address
    add    pc, r10, #PROCINFO_INITFUNC

    .type    __enable_mmu, %function
__enable_mmu:
#ifdef CONFIG_ALIGNMENT_TRAP
    orr    r0, r0, #CR_A
#else
    bic    r0, r0, #CR_A
#endif
#ifdef CONFIG_CPU_DCACHE_DISABLE
    bic    r0, r0, #CR_C
#endif
#ifdef CONFIG_CPU_BPREDICT_DISABLE
    bic    r0, r0, #CR_Z
#endif
#ifdef CONFIG_CPU_ICACHE_DISABLE
    bic    r0, r0, #CR_I
#endif
    mov    r5, #(domain_val(DOMAIN_USER, DOMAIN_MANAGER) | \
              domain_val(DOMAIN_KERNEL, DOMAIN_MANAGER) | \
              domain_val(DOMAIN_TABLE, DOMAIN_MANAGER) | \
              domain_val(DOMAIN_IO, DOMAIN_CLIENT))
    mcr    p15, 0, r5, c3, c0, 0        @ load domain access register
    mcr    p15, 0, r4, c2, c0, 0        @ load page table pointer
    b    __turn_mmu_on


    .align    5
    .type    __turn_mmu_on, %function
__turn_mmu_on:
    mov    r0, r0
    mcr    p15, 0, r0, c1, c0, 0        @ write control reg
    mrc    p15, 0, r3, c0, c0, 0        @ read id reg
    mov    r3, r3
    mov    r3, r3
    mov    pc, r13

    .type    __create_page_tables, %function
__create_page_tables:
    代码略.

    
    代码执行的流程描述如下:
    (1) 首先进入SVC模式,关闭中断,并检测cpu类型。
    (2) 创建页表格 bl    __create_page_tables
    (3) 
ldr r13, __switch_data 这是一个非常重要的步骤,他的作用将在随后的步骤中体现
    (4) 
adr lr, __enable_mmu 也是一个非常重要的步骤,作用在随后步骤中解释。
    (5) add pc, r10, #PROCINFO_INITFUNC
        
#PROCINFO_INITFUNC宏定义在include/asm/asm-offset.h中,这个宏的定义如下
#define PROCINFO_INITFUNC 16 
/* offsetof(struct proc_info_list, __cpu_flush)    @ */
        那么现在我们就不得不提出问题, r10内的内容是什么? 这个16又是如何使用的呢?
        事实上,r10内存放的是一个结构体的首地址,该结构体在__lookup_processor_type后由r5传来,而r5则指向一个定义的结构体,该结构体声明于include/arm-asm/procinfo.h文件中,结构体如下:
 

struct proc_info_list {
    unsigned int        cpu_val;
    unsigned int        cpu_mask;
    unsigned long  __cpu_mm_mmu_flags;/*used by head.S */
    unsigned long  __cpu_io_mmu_flags;/*used by head.S */
    unsigned long  __cpu_flush;    /* used by head.S */
    const char        *arch_name;
    const char        *elf_name;
    unsigned int        elf_hwcap;
    const char        *cpu_name;
    struct processor    *proc;
    struct cpu_tlb_fns    *tlb;
    struct cpu_user_fns    *user;
    struct cpu_cache_fns    *cache;
};

       其具体的值的实现在arch/arm/mm/proc-arm9tdmi.S中,具体如下:
       
__arm9tdmi_proc_info:
        .long    0x41009900
        .long    0xfff8ff00
        .long    0
        .long    0
        b    __arm9tdmi_setup
        .long    cpu_arch_name
        .long    cpu_elf_name
        .long    HWCAP_SWP | HWCAP_THUMB | HWCAP_26BIT
        .long    cpu_arm9tdmi_name
        .long    arm9tdmi_processor_functions
        .long    0
        .long    0
        .long    v4_cache_fns
        .size    __arm9tdmi_proc_info, . - __arm9dmi_proc_info

       应此add pc, r10, #PROCINFO_INITFUNC实际上就是调用语句b __arm9tdmi_setup
       
__arm9tdmi_setup中只有一个语句就是mov pc, lr。因为之前的步骤(4)中使用了adr lr, __enable_mmu 这个语句将__enable_mmu放入到lr中,因此,这此跳转直接转到__enable_mmu函数内。
    (5)调用__enable_mmu函数,该函数内会调用__turn_mmu_on函数,打开对mmu的支持。
       在这部分,我遭遇到了另一个问题,这个问题就是led程序在打开mmu之后就不再好用了,为此我查到了一个相关的介绍资料在该FAQ中特别指明了这个问题的普遍性(言辞有点激烈,有点打击人),具体部分的摘录如下:

 When I trace kernel in head.S I found problems that may be caused by __turn_mmu_on. Any idea ?
[5 April 2004 - ]
Please read the comment in head.S:
/*
* Enable the MMU. This completely changes the structure of the visible
* memory space. You will not be able to trace execution through this.
* If you have an enquiry about this, *please* check the linux-arm-kernel
* mailing list archives BEFORE sending another post to the list.
*/
It's been asked soo many times that it really is a frequently asked question. The above comment is supposed to head off further questions, but alas... Hopefully this FAQ entry will do the job.
[11 January 2002 - ]
The number 1 problem is that people just do not realise what "enabling the MMU" means. It means that _all_ of the addressable memory _will_ change, and this normally means various regions you'd like to write to no longer exist - in other words, if you have debugging code present that doesn't cater for a complete change in the memory structure, then your debugging code is buggy.

(Side note: I've even had people insist that their debugging code is perfect, even after mentioning the above, and then we've gone through several mails, ended up having the debugging code posted, and it turns out to buggy, assuming that the LEDs at some random address are still accessible.)

The best advice is if you're using your own debugging code, remove it. Use the debugging printascii() stuff for your platform in arch/arm/kernel/debug-armv.S (enabled by CONFIG_DEBUG_LL) and drop a printascii() into printk() (kernel/printk.c) just after the vsprintf() call, and monitor the relevant serial port.


    其实主要的说明就是这会mmu已经开启了,系统中的物理地址都变成虚拟地址了,因此原来基于物理地址的调试方案将都会失败,但可以使用printascii继续调试,该调试功能同时支持物理地址与虚拟地址,并且提供了一个解决方案就是将printascii加入到printk的vsprintf()之后。(注:通过这种方案也可以发现是否是因为u-boot于linux的频率不匹配而导致的终端内容无显示)

它说到的这个调试方案的修改方法(我加在vprintk内):修改该文件 kernel/printk.c
extern void printascii(const char*);//kernel/printk.c 516行
printascii(printk_buf);//
//kernel/printk.c 539行

    (6)__turn_mmu_on:最后一句是mov pc, r13 上述重要步骤之第(3)步恰好就是将__switch_data传入到r13中。因此pc直接指向的是__switch_data代码部分。
     __switch_data定义在arch/arm/kernel/head-common.S中,具体实现代码如下:
    .type    __switch_data, %object
__switch_data:
    .long    __mmap_switched
    .long    __data_loc            @ r4
    .long    __data_start            @ r5
    .long    __bss_start            @ r6
    .long    _end                @ r7
    .long    processor_id            @ r4
    .long    __machine_arch_type        @ r5
    .long    cr_alignment            @ r6
    .long    init_thread_union + THREAD_START_SP @ sp
    由此可以看出,实际上是执行__mmap_switched函数。
    (7) 在__mmap_switched 的最后一句就是b start_kernel,到此位置,我们就跳入到内核初始化函数中。
    
    实际在调试的过程中,我遭遇的问题就是在b start_kernel之前printascii都是好用的,可是到start_kernel之后,这个函数就不好用了,太奇怪了。琢磨2天没有希望,决定换一个交叉编译器,选择了的交叉编译器,该编译器建立在linux2.6.22的内核的基础之上制作。我选择了一款linux2.6.22的内核按上述步骤进行编译,得到的内核burn到板子里,printascii竟然好用了!但是仍然是跑到一半,就会挂起。真是让人挠头。我觉得可能是编译器有问题。于是就着根据linux2.6.22部分的start_kernel进行调试(调试步骤比较简单,就不多说了),最终发现挂起在内存page初始化的部分!不由的让我想到我传入到内核的参数,是否是因为“mem64M”的问题呢?到网上再此搜索,终于在一个站点上看到,竟然是mem=64M,我晕的头都大了。改变bootargs重启板子,引导启动信息终于显示出来了,换回到linux2.26.20.3并换回编译器,引导信息仍然可显示出来。到此为止,我算是成功的搞定了这个破问题。

    希望我的这次经历能给看到这篇文章的同僚们一些启示,那也就不妄我这几天的郁闷了。
阅读(2049) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~