【嵌入式Linux学习七步曲之第四篇 Linux内核移植】用BDI2000快速定位PPC Linux内核Uncompressing Kernel Image ... OK后无输出问题

本文主要是介绍【嵌入式Linux学习七步曲之第四篇 Linux内核移植】用BDI2000快速定位PPC Linux内核Uncompressing Kernel Image ... OK后无输出问题,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

 用BDI2000快速定位PPC Linux内核Uncompressing Kernel Image ... OK后无输出问题

Sailor_forever  sailing_9806#163.com 转载请注明
http://blog.csdn.net/sailor_8318/archive/2009/11/15/4812075.aspx

 


【摘要】本文以MPC8270处理器和Linux2.5.15.5内核为例,讲述了用BDI2000快速定位PPC Linux内核串口无输出的问题。分析了典型的原因并详细介绍了用BDI2000快递定位问题的基本方法,及利用GDB查看printk输出buffer的方式,模拟串口打印输出来迅速定位。

【关键字】BDI2000  GDB  MPC8270  Linux  Uncompressing  printk  printk_buf
 

病症
内核移植最痛苦的莫过于跳到内核入口后,无任何输出,这时候真是丈二和尚摸不着头脑啊

对于PPC Linux内核,典型的现象如下,
=> bootm 0x400000
*  kernel: cmdline image address = 0x00400000
## Booting kernel from Legacy Image at 00400000 ...
   Image Name:   Linux-2.6.19
   Created:      2009-11-03   8:09:41 UTC
   Image Type:   PowerPC Linux Kernel Image (gzip compressed)
   Data Size:    1285009 Bytes =  1.2 MB
   Load Address: 00000000
   Entry Point:  00000000
   Verifying Checksum ... OK
   kernel data at 0x00400040, len = 0x00139b91 (1285009)
   Uncompressing Kernel Image ... OK
   kernel loaded at 0x00000000, end = 0x002cf086
## Current stack ends at 0x03f96530
## cmdline at 0x007fff00 ... 0x007fff00
## kernel board info at 0x007ffeb0
bd address  = 0x03F96AB6
memstart    = 0x00000000
memsize     = 0x00000000
flashstart  = 0x40000000
flashsize   = 0x01000000
flashoffset = 0x00042000
sramstart   = 0x00000000
sramsize    = 0x00000000
immr_base   = 0xFFF00000
bootflags   = 0x00000001
vco         =    330 MHz
sccfreq     = 82.500 MHz
brgfreq     = 20.625 MHz
intfreq     =    198 MHz
cpmfreq     =    165 MHz
busfreq     =     66 MHz
ethaddr     = 00:A0:1E:A8:7B:CB
IP addr     = 150.236.68.211
baudrate    =  19200 bps
## No init Ramdisk
   ramdisk start = 0x00000000, ramdisk end = 0x00000000
## initrd_high = 0xffffffff, copy_to_ram = 1
   ramdisk load start = 0x00000000, ramdisk load end = 0x00000000
## Transferring control to Linux (at address 00000000) ...
   Booting using board info...

待审嫌疑犯
串口无输出,一般有以下几个典型的可疑:
1、内核中串口是否正确配置了,和开发板上目前的调试串口是否一致
2、内核使用的波特率是否和当前的调试串口一致
3、内核的串口驱动本身有问题
4、在串口初始化之前,由于内核本身移植的有问题,系统可能已经crash了

对于1和2,这都需要bootargs参数和内核一致,如console=ttyCPMx,115200
当系统有多个串口时,console采用第几个串口这个配置则尤其重要

另外关于波特率的解析,要确保内核和Uboot对于bd_t结构体的定义一致,因为内核启动时很多启动信息都是从这个板子配置信息得到的,如果解析有误的话可能会存在问题

对于3,这种情况很少见,因为对于已经支持的CPU系列,串口驱动都是比较完善的,只是一个配置的问题

对于4,内核移植需要修改的文件,不在本文讨论之列

若上述几个疑问都确认过了,串口仍然没有输出,则只能借助点灯或者仿真器来定位内核究竟在哪一步出问题了

BDI2000单步调试
用BDI2000调试PPC Linux内核的基本步骤,可参加
http://blog.csdn.net/sailor_8318/archive/2009/11/10/4795287.aspx
GDB和BDI2000调试PPC Linux内核

在MMU开启之前,系统运行的都是汇编代码,这个时候并没有任何printk打印输出,如果此阶段内核crash,则现象就是Uncompressing Kernel Image ... OK之后没有任何反应

这个时候可以阶段性的设置断点,快速定位在跳转到start_kernel之前,内核是否运行正常。若不正常,则回退,定位出问题的大概范围,然后单步调试

断点显示printk输出

当进入start_kernel后,此时已经建立起了完善的C代码运行环境,开始调用
Printk打印系统关键的启动信息,当然在串口未真正初始化之前,printk将打印信息都暂时存储在了一个全局的buffer之中__log_buf,其大小在内核选项中可配置

系统最早的串口输出在console_init之后,若console没有问题,则前期积累的打印信息将在此之后真正输出到物理串口上。

若程序运行到console_init之后,仍然没有crash且没有串口输出,则说明串口有问题。若程序未能运行到console_init,则说明在start_kernel入口到console_init之间的代码有问题。则只能跟踪了。

当系统出现可控的异常时,一般都会有打印信息。此时不是没有串口吗?怎么看打印信息了?

每次调用printk时,都会将本次待打印的信息保存在一个static的数组中printk_buf,在vprintk函数中设置断点,待将本次打印信息解析后存储在printk_buf中,即可通过host断的GDB环境查看printk_buf中的打印信息,相当于主机环境做了一个虚拟的串口啦

asmlinkage int printk(const char *fmt, ...)
{
    va_list args;
    int r;

    va_start(args, fmt);
    r = vprintk(fmt, args);
    va_end(args);

    return r;
}

/* cpu currently holding logbuf_lock */
static volatile unsigned int printk_cpu = UINT_MAX;

asmlinkage int vprintk(const char *fmt, va_list args)
{
    unsigned long flags;
    int printed_len;
    char *p;
    static char printk_buf[1024];
    static int log_level_unknown = 1;

    preempt_disable();
    if (unlikely(oops_in_progress) && printk_cpu == smp_processor_id())
        /* If a crash is occurring during printk() on this CPU,
         * make sure we can't deadlock */
        zap_locks();

    /* This stops the holder of console_sem just where we want him */
    local_irq_save(flags);
    lockdep_off();
    spin_lock(&logbuf_lock);
    printk_cpu = smp_processor_id();

    /* Emit the output into the temporary buffer */
    printed_len = vscnprintf(printk_buf, sizeof(printk_buf), fmt, args);

    /*
     * Copy the output into log_buf.  If the caller didn't provide
     * appropriate log level tags, we insert them here
     */
    for (p = printk_buf; *p; p++) {
        if (log_level_unknown) {

通过查看printk的参数const char *fmt,可大概知道相关的打印信息,但并没有完全解析,在printed_len = vscnprintf(printk_buf, sizeof(printk_buf), fmt, args)之后,打印信息便保存在了printk_buf中。

GDB的x/s printk_buf或者display printk_buf都可以查看printk_buf中的内容。断点设置在_len = vscnprintf(printk_buf, sizeof(printk_buf), fmt, args)之后即可,每次系统有关键的信息打印时,在此即可看到,相当于串口正常工作了。

打印调试比GDB单步调试要高效的多,内核在关键启动过程中都有打印输出,因此根据虚拟的打印信息可以快速定位问题。


Breakpoint 3, vprintk (fmt=<value optimized out>, args=<value optimized out>)
    at kernel/printk.c:554
554             for (p = printk_buf; *p; p++) {
(gdb) x/s printk_buf
0xc01d120c <printk_buf.15252>:   "Linux version 2.6.15.5 (sailing@cnbjc0052) (gcc version 4.2.2) #2 Fri Nov 6 12:09:22 CST 2009/n"
(gdb) display printk_buf
1: printk_buf ="Linux version 2.6.15.5 (sailing@cnbjc0052) (gcc version 4.2.2) #2 Fri Nov 6 12:09:22 CST 2009/n"
(gdb) c
Breakpoint 3, vprintk (fmt=<value optimized out>, args=<value optimized out>)
    at kernel/printk.c:554
554             for (p = printk_buf; *p; p++) {
1: printk_buf = "<5>Kernel command line: /n/000, LIFO batch:0/n/000 (gcc version 4.2.2) #2 Fri Nov 6 12:09:22 CST 2009/n", '/0' <repeats 929 times>

发现内核命令行参数既然是空???????如果console参数有问题的话,内核当然无法正常初始化调试串口啦

仔细检查bootargs,发现因为手动设置的bootargs,拷贝时不当心拷贝成了boottargs,因此uboot无法识别这个参数,导致启动时传递给内核的启动参数是空串

重新设置bootargs,由下列打印信息可以看出,内核正确解析了U-boot传递过来的启动参数
1: printk_buf = "<5>Kernel command line: console=ttyCPM1,19200 root=/dev/nfs rw nfsroot=150.236.70.104:/home/sailing/eldk_tool/ppc_82xx ip=147.128.19.123:150.236.70.104:147.128.19.1:255.255.255.128:eric::off/n", '/

在console_init之后即有打印信息输出啦

这篇关于【嵌入式Linux学习七步曲之第四篇 Linux内核移植】用BDI2000快速定位PPC Linux内核Uncompressing Kernel Image ... OK后无输出问题的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/284516

相关文章

HarmonyOS学习(七)——UI(五)常用布局总结

自适应布局 1.1、线性布局(LinearLayout) 通过线性容器Row和Column实现线性布局。Column容器内的子组件按照垂直方向排列,Row组件中的子组件按照水平方向排列。 属性说明space通过space参数设置主轴上子组件的间距,达到各子组件在排列上的等间距效果alignItems设置子组件在交叉轴上的对齐方式,且在各类尺寸屏幕上表现一致,其中交叉轴为垂直时,取值为Vert

Ilya-AI分享的他在OpenAI学习到的15个提示工程技巧

Ilya(不是本人,claude AI)在社交媒体上分享了他在OpenAI学习到的15个Prompt撰写技巧。 以下是详细的内容: 提示精确化:在编写提示时,力求表达清晰准确。清楚地阐述任务需求和概念定义至关重要。例:不用"分析文本",而用"判断这段话的情感倾向:积极、消极还是中性"。 快速迭代:善于快速连续调整提示。熟练的提示工程师能够灵活地进行多轮优化。例:从"总结文章"到"用

无人叉车3d激光slam多房间建图定位异常处理方案-墙体画线地图切分方案

墙体画线地图切分方案 针对问题:墙体两侧特征混淆误匹配,导致建图和定位偏差,表现为过门跳变、外月台走歪等 ·解决思路:预期的根治方案IGICP需要较长时间完成上线,先使用切分地图的工程化方案,即墙体两侧切分为不同地图,在某一侧只使用该侧地图进行定位 方案思路 切分原理:切分地图基于关键帧位置,而非点云。 理论基础:光照是直线的,一帧点云必定只能照射到墙的一侧,无法同时照到两侧实践考虑:关

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

linux-基础知识3

打包和压缩 zip 安装zip软件包 yum -y install zip unzip 压缩打包命令: zip -q -r -d -u 压缩包文件名 目录和文件名列表 -q:不显示命令执行过程-r:递归处理,打包各级子目录和文件-u:把文件增加/替换到压缩包中-d:从压缩包中删除指定的文件 解压:unzip 压缩包名 打包文件 把压缩包从服务器下载到本地 把压缩包上传到服务器(zip

学习hash总结

2014/1/29/   最近刚开始学hash,名字很陌生,但是hash的思想却很熟悉,以前早就做过此类的题,但是不知道这就是hash思想而已,说白了hash就是一个映射,往往灵活利用数组的下标来实现算法,hash的作用:1、判重;2、统计次数;

好题——hdu2522(小数问题:求1/n的第一个循环节)

好喜欢这题,第一次做小数问题,一开始真心没思路,然后参考了网上的一些资料。 知识点***********************************无限不循环小数即无理数,不能写作两整数之比*****************************(一开始没想到,小学没学好) 此题1/n肯定是一个有限循环小数,了解这些后就能做此题了。 按照除法的机制,用一个函数表示出来就可以了,代码如下

hdu1043(八数码问题,广搜 + hash(实现状态压缩) )

利用康拓展开将一个排列映射成一个自然数,然后就变成了普通的广搜题。 #include<iostream>#include<algorithm>#include<string>#include<stack>#include<queue>#include<map>#include<stdio.h>#include<stdlib.h>#include<ctype.h>#inclu

电脑桌面文件删除了怎么找回来?别急,快速恢复攻略在此

在日常使用电脑的过程中,我们经常会遇到这样的情况:一不小心,桌面上的某个重要文件被删除了。这时,大多数人可能会感到惊慌失措,不知所措。 其实,不必过于担心,因为有很多方法可以帮助我们找回被删除的桌面文件。下面,就让我们一起来了解一下这些恢复桌面文件的方法吧。 一、使用撤销操作 如果我们刚刚删除了桌面上的文件,并且还没有进行其他操作,那么可以尝试使用撤销操作来恢复文件。在键盘上同时按下“C