这两天客户提出来,我们的平板cat /proc/cpuinfo出来的信息中的serial怎么是0.

客户就是上帝啊,没办法,分析找问题贝。

我们先看一下目前的cat /proc/cpuinfo的信息:

Processor       : ARMv7 Processor rev 5 (v7l)                                   

BogoMIPS        : 799.53                                                        

Features        : swp half thumb fastmult vfp edsp neon vfpv3                   

CPU implementer : 0x41                                                          

CPU architecture: 7                                                             

CPU variant     : 0x2                                                           

CPU part        : 0xc08                                                         

CPU revision    : 5                                                             

                                                                                

Hardware        : Freescale MX51 F101 Board                                     

Revision        : 51030                                                         

Serial          : 0000000000000000 



我们找到kernel中的cpuinfo的文件,路径在fs/proc/cpuinfo.c 。

我们首先看一下它的init函数:

static int __init proc_cpuinfo_init(void)

{

    proc_create("cpuinfo", 0, NULL, &proc_cpuinfo_operations);

    return 0;

}

嗯,很明星,我们cat /proc/cpuinfo的文件就是在该init中创建的。我们注意到创建该文件时传入了fops。我们再看一下proc_cpuinfo_operations这个fops定义:

static const struct file_operations proc_cpuinfo_operations = {

    .open        = cpuinfo_open,

    .read        = seq_read,

    .llseek        = seq_lseek,

    .release    = seq_release,

};



我们执行cat /proc/cpuinfo时实际就是执行了open和read这两个函数。

我们下面分别分析一下open和read分别做了什么事情。

1,open

open定义如下:

static int cpuinfo_open(struct inode *inode, struct file *file)

{

    return seq_open(file, &cpuinfo_op);

}

我们发现调用open的时候传入了cpuinfo_op这个结构。这个结构就是cpuinfo的实际操作方法。这个cpuinfo_op是每种cpu架构都必须特别定义的。我们用的是arm架构,我们找到它的定义:

arch/arm/kernel/setup.c :

const struct seq_operations cpuinfo_op = {

    .start    = c_start,

    .next    = c_next,

    .stop    = c_stop,

    .show    = c_show

};

我们知道这个结构体后,我们继续看open,

int seq_open(struct file *file, const struct seq_operations *op)

{

    struct seq_file *p = file->private_data;



    if (!p) { //如果file->private_data为空,则为它申请空间

        p = kmalloc(sizeof(*p), GFP_KERNEL);

        if (!p)

            return -ENOMEM;

        file->private_data = p;

    }

    memset(p, 0, sizeof(*p));//清0

    mutex_init(&p->lock); //初始化mutex

    p->op = op;  //将上面传进来的cpuinfo_op赋值给file



    file->f_version = 0;

    file->f_mode &= ~FMODE_PWRITE;

    return 0;

}

我们看到seq_open的主要作用是将ops保持到file->private_data中。



2,read

我们上面说cat /proc/cpuinfo就相对于执行open和read,我们下面来看看热啊的。

ssize_t seq_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)

{

    struct seq_file *m = (struct seq_file *)file->private_data;//看清楚了,把刚才上面open中的cpuinfo_op取出来了!下面就可以使用这个结构里面的方法了!

    size_t copied = 0;

    loff_t pos;

    size_t n;

    void *p;

    int err = 0;



    mutex_lock(&m->lock);

    ......

     pos = m->index;

    p = m->op->start(m, &pos);//执行cpuinfo_op中的start方法

    while (1) {

        err = PTR_ERR(p);

        if (!p || IS_ERR(p))

            break;

        err = m->op->show(m, p);//执行cpuinfo_op中show方法

        if (err < 0)

            break;

        if (unlikely(err))

            m->count = 0;

        if (unlikely(!m->count)) {

            p = m->op->next(m, p, &pos);

            m->index = pos;

            continue;

        }

        if (m->count < m->size)

            goto Fill;

        m->op->stop(m, p);

        kfree(m->buf);

        m->buf = kmalloc(m->size <<= 1, GFP_KERNEL);

        if (!m->buf)

            goto Enomem;

        m->count = 0;

        m->version = 0;

        pos = m->index;

        p = m->op->start(m, &pos);

    }

    m->op->stop(m, p);

    ......

    

}

我们看到read方法中主要执行了cpuinfo_op中方法:

const struct seq_operations cpuinfo_op = {

    .start    = c_start,

    .next    = c_next,

    .stop    = c_stop,

    .show    = c_show

};

我们下面一个一个来分析,

static void *c_start(struct seq_file *m, loff_t *pos)

{

    return *pos < 1 ? (void *)1 : NULL;

}

c_start主要验证文件的位置。

static void *c_next(struct seq_file *m, void *v, loff_t *pos)

{

    ++*pos;

    return NULL;

}

c_next移动文件位置的指针,指向下一个。

static void c_stop(struct seq_file *m, void *v)

{

}

c_stop没有做事情。



static int c_show(struct seq_file *m, void *v)

{

    int i;

    /*打印cpu的processor,例如例子中的Processor       : ARMv7 Processor rev 5 (v7l)*/

    seq_printf(m, "Processor\t: %s rev %d (%s)\n",

           cpu_name, read_cpuid_id() & 15, elf_platform);



#if defined(CONFIG_SMP)//如果是多核处理器,则分别打印cpu的processor信息和主频信息

    for_each_online_cpu(i) {

        /*

         * glibc reads /proc/cpuinfo to determine the number of

         * online processors, looking for lines beginning with

         * "processor".  Give glibc what it expects.

         */

        seq_printf(m, "processor\t: %d\n", i);

        seq_printf(m, "BogoMIPS\t: %lu.%02lu\n\n",

               per_cpu(cpu_data, i).loops_per_jiffy / (500000UL/HZ),

               (per_cpu(cpu_data, i).loops_per_jiffy / (5000UL/HZ)) % 100);

    }

#else /* CONFIG_SMP */

    seq_printf(m, "BogoMIPS\t: %lu.%02lu\n",

           loops_per_jiffy / (500000/HZ),

           (loops_per_jiffy / (5000/HZ)) % 100);

#endif



    /* dump out the processor features */

    seq_puts(m, "Features\t: ");//下面打印feature信息



    for (i = 0; hwcap_str;
i++)

        if (elf_hwcap & (1 << i))

            seq_printf(m, "%s ", hwcap_str);



    seq_printf(m, "\nCPU implementer\t: 0x%02x\n", read_cpuid_id() >> 24);

    seq_printf(m, "CPU architecture: %s\n", proc_arch[cpu_architecture()]);



    if ((read_cpuid_id() & 0x0008f000) == 0x00000000) {

        /* pre-ARM7 */

        seq_printf(m, "CPU part\t: %07x\n", read_cpuid_id() >> 4);

    } else {

        if ((read_cpuid_id() & 0x0008f000) == 0x00007000) {

            /* ARM7 */

            seq_printf(m, "CPU variant\t: 0x%02x\n",

                   (read_cpuid_id() >> 16) & 127);

        } else {

            /* post-ARM7 */

            seq_printf(m, "CPU variant\t: 0x%x\n",

                   (read_cpuid_id() >> 20) & 15);

        }

        seq_printf(m, "CPU part\t: 0x%03x\n",

               (read_cpuid_id() >> 4) & 0xfff);

    }

    seq_printf(m, "CPU revision\t: %d\n", read_cpuid_id() & 15);



    seq_puts(m, "\n");



    seq_printf(m, "Hardware\t: %s\n", machine_name);

    seq_printf(m, "Revision\t: %04x\n", system_rev);

    seq_printf(m, "Serial\t\t: %08x%08x\n",

           system_serial_high, system_serial_low);//这里我们终于看到serial打印的地方了。我们发现主要打印 system_serial_high,和system_serial_low两个变量的值。如果没有赋值,则打印0。我们要做的工作就是为这两个变量赋值。



    return 0;

}

好了,问题分析差不多了,下面就是实现它。这个值就是cpu的uuid,Unique ID是芯片的唯一的ID,是芯片的产线上的信息。每个芯片都有不同的值,每种芯片都有不一样的方法去读。

我们平板用的是imx51,以imx51为例子,它是通过IIM读Fuse的数据。地址是:(0x83F98000 + 0x820) ~ (0x83F98000 + 0x83C),共8个字节。

具体实现,代码奉上:

在driver/char/mxc_iim.c中的probe加上:

    /*via iim, read cpu UID*/

//open iim

    iim_data->clk = clk_get(NULL, "iim_clk");

    if (IS_ERR(iim_data->clk)) {

        dev_err(iim_data->dev, "No IIM clock defined\n");

        return -ENODEV;

    }

    clk_enable(iim_data->clk);



    mxc_iim_disable_irq();



//read iim

    addr = 0x820; //uid start addr

    for(i=0;i<32;i+=4){

        bank = (addr + i - iim_data->bank_start) >> 10;

        row  = ((addr + i - iim_data->bank_start) & 0x3ff) >> 2;



        dev_dbg(iim_data->dev, "Read fuse at bank:%d row:%d\n",

                bank, row);

        mutex_lock(&iim_data->mutex);

        fuse_val = sense_fuse(bank, row, 0);

        serial[i/4] = fuse_val;

        mutex_unlock(&iim_data->mutex);

        dev_dbg(iim_data->dev, "fuses at addr0x%x(bank:%d, row:%d) = 0x%x\n",

             addr + i, bank, row, fuse_val);

    }

    system_serial_low = ((serial[3]<<24)&0xff000000) + ((serial[2]<<16)&0x00ff0000) + ((serial[1]<<8)&0x0000ff00) + (serial[0]&0x000000ff);



    system_serial_high = ((serial[7]<<24)&0xff000000) + ((serial[6]<<16)&0x00ff0000) + ((serial[5]<<8)&0x0000ff00) + (serial[4]&0x000000ff);



    dev_info(iim_data->dev, "system_serial_high:0x%x, system_serial_low:0x%x", system_serial_high, system_serial_low);





OK,至此就非常完美地实现了!

imx51-linux的cpuinfo之分析的更多相关文章

  1. [转]linux /proc/cpuinfo 文件分析

    在Linux系统中,提供了proc文件系统显示系统的软硬件信息.如果想了解系统中CPU的提供商和相关配置信息,则可以通过/proc/cpuinfo文件得到.本文章针对该文件进行简单的总结. 基于不同指 ...

  2. /proc/cpuinfo 文件分析(查看CPU信息)

    /proc/cpuinfo文件分析 根据以下内容,我们则可以很方便的知道当前系统关于CPU.CPU的核数.CPU是否启用超线程等信息. <1>查询系统具有多少个逻辑核:cat /proc/ ...

  3. linux服务器宕机分析/性能瓶颈分析

    linux服务器宕机分析/性能瓶颈分析   服务器宕机原因很多,资源不足.应用.硬件.系统内核bug等,以下一个小例子 服务器宕机了,首先得知道服务器宕机的时间点,然后分析日志查找原因 1.last ...

  4. Android/Linux下CGroup框架分析及其使用

    1 cgroup介绍 CGroup是control group的简称,它为Linux kernel提供一种任务聚集和划分的机制,可以限制.记录.隔离进程组(process groups)所使用的资源( ...

  5. Linux Kernel Oops异常分析

    1.PowerPC小系统内核异常分析 1.1  异常打印 Unable to handle kernel paging request for data at address 0x36fef31eFa ...

  6. 20169212《Linux内核原理与分析》课程总结

    20169212<Linux内核原理与分析>课程总结 每周作业链接汇总 第一周作业:完成linux基础入门实验,了解一些基础的命令操作. 第二周作业:学习MOOC课程--计算机是如何工作的 ...

  7. 1.linux服务器的性能分析与优化

    [教程主题]:1.linux服务器的性能分析与优化 [课程录制]: 创E [主要内容] [1]影响Linux服务器性能的因素 操作系统级 CPU 目前大部分CPU在同一时间只能运行一个线程,超线程的处 ...

  8. 20169212《Linux内核原理与分析》第二周作业

    <Linux内核原理与分析>第二周作业 这一周学习了MOOCLinux内核分析的第一讲,计算机是如何工作的?由于本科对相关知识的不熟悉,所以感觉有的知识理解起来了有一定的难度,不过多查查资 ...

  9. Linux系统日志及日志分析

    Linux系统日志及日志分析   Linux系统拥有非常灵活和强大的日志功能,可以保存几乎所有的操作记录,并可以从中检索出我们需要的信息. 大部分Linux发行版默认的日志守护进程为 syslog,位 ...

  10. 高性能Linux服务器 第10章 基于Linux服务器的性能分析与优化

    高性能Linux服务器 第10章    基于Linux服务器的性能分析与优化 作为一名Linux系统管理员,最主要的工作是优化系统配置,使应用在系统上以最优的状态运行.但硬件问题.软件问题.网络环境等 ...

随机推荐

  1. IT男的别样人生,爱折腾,竟然辞职跑丽江去了

    深圳待了4年,在深圳腾讯总部任职,北漂了5年多,任某知名团购公司CTO,有了孩子以后才知道自己想要什么 2015年4月,我和老婆还有6个月的儿子丽江游, 却在旅行的第四天, 买下了位于束河古镇正门的高 ...

  2. Linux SWAP 交换分区配置说明

    一.SWAP 说明1.1 SWAP 概述        当系统的物理内存不够用的时候,就需要将物理内存中的一部分空间释放出来,以供当前运行的程序使用.那些被释放的空间可能来自一些很长时间没有什么操作的 ...

  3. 深度学习与计算机视觉系列(2)_图像分类与KNN

    作者: 寒小阳 &&龙心尘 时间:2015年11月. 出处: http://blog.csdn.net/han_xiaoyang/article/details/49949535 ht ...

  4. UNIX网络编程——UNIX域套接字编程和socketpair 函数

    一.UNIX Domain Socket IPC socket API原本是为网络通讯设计的,但后来在socket的框架上发展出一种IPC机制,就是UNIX Domain Socket.虽然网络soc ...

  5. ceil和floor函数的编程实践

    ceil()向上取整 floor向下取整 题目 在最近几场魔兽争霸赛中,赫柏对自己的表现都不满意. 为了尽快提升战力,赫柏来到了雷鸣交易行并找到了幻兽师格丽,打算让格丽为自己的七阶幻兽升星. 经过漫长 ...

  6. 6. React 表单使用介绍

            表单是前端页面中非常重要也是非常常用的一个内容,react 也在表单方面进行了很多封装,让开发者可以方便快捷地在 react 组件中使用表单.下面介绍如何在组件中正确的使用表单,从而可 ...

  7. 网站开发进阶(四十三)html中,路径前加“/” 与不加“/”的区别

    网站开发进阶(四十三)html中,路径前加"/" 与不加"/"的区别 前言 <script src="js/downloadify.js&quo ...

  8. 查找maven中的groupId,artifactId,version等信息的方式

    可以查看:http://search.maven.org/   输入要想找的东西 

  9. JAVA之旅(二十六)——装饰设计模式,继承和装饰的区别,LineNumberReader,自定义LineNumberReader,字节流读取操作,I/O复制图片

    JAVA之旅(二十六)--装饰设计模式,继承和装饰的区别,LineNumberReader,自定义LineNumberReader,字节流读取操作,I/O复制图片 一.装饰设计模式 其实我们自定义re ...

  10. cocos2dx深度检测与Zorder

    cocos2dx里面有两个渲染队列,RenderQueue和TransparentRenderQueue.我们可以从Renderer::render()的代码看到: void Renderer::re ...