KERNEL BUG: unable to handle kernel NULL pointer dereference at 00000004

来源:互联网 发布:淘宝怎么改身份证号码 编辑:程序博客网 时间:2024/05/16 19:55

出现kenel BUG信息:

BUG: unable to handle kernel NULL pointer dereference at 00000004
IP: [<c12c2c45>] firmware_loading_store+0x55/0x170
*pdpt = 0000000018ea2001 *pde = 0000000000000000
Oops: 0000 [#1] PREEMPT SMP
last sysfs file: /sys/devices/fw_device/firmware/fw_device/loading

Pid: 3509, comm: osal_fw_hotplug Not tainted 2.6.39 #3
EIP: 0060:[<c12c2c45>] EFLAGS: 00010282 CPU: 0
EIP is at firmware_loading_store+0x55/0x170
EAX: 00000000 EBX: d8968c00 ECX: 00000000 EDX: 1c0c6000
ESI: d8968c40 EDI: d93d0960 EBP: ffffffed ESP: d8c4bf20
 DS: 007b ES: 007b FS: 00d8 GS: 0033 SS: 0068
Process osal_fw_hotplug (pid: 3509, ti=d8c4a000 task=d9c86130 task.ti=d8c4a000)
Stack:
 00000161 80000000 000000d0 d89a0b00 c12c2bf0 00000002 d8ed3c30 c12b7765
 00000002 c15a4e0c 00000002 c1101826 00000002 08063188 d9736b14 c15a4e0c
 d8968c48 d9296d00 00000002 08063188 c1101780 c10b4d40 d8c4bf9c d89a0b00
Call Trace:
 [<c12c2bf0>] ? firmware_class_timeout+0x10/0x10
 [<c12b7765>] ? dev_attr_store+0x25/0x40
 [<c1101826>] ? sysfs_write_file+0xa6/0x100
 [<c1101780>] ? sysfs_poll+0x80/0x80
 [<c10b4d40>] ? vfs_write+0xa0/0x140
 [<c10b4fe1>] ? sys_write+0x41/0x80
 [<c154e9d1>] ? syscall_call+0x7/0xb
Code: 04 e8 80 08 de ff 8b 53 1c 31 c9 8b 43 18 8b 7b 10 c7 04 24 61 01 00 00 c7 44 24 04 00 00 00 80 e8 81 08 de ff 89 47 04 8b 43 10 <8b> 50 04 85 d2 0f 84 ec 00 00 00 8b 53 18 89 50 08 89 d8 c7 43
EIP: [<c12c2c45>] firmware_loading_store+0x55/0x170 SS:ESP 0068:d8c4bf20
CR2: 0000000000000004

如何调试?

这种情况下,应该是出现了kernel NULL指针,出现空指针有可能导致内核出现上述的错误信息,或者更严重的会出现Ooops,kernel panic.只关上讲,从上面的出错信息中可以了解到,出错函数位于:IP: [<c12c2c45>] firmware_loading_store+0x55/0x170,函数体大小大约为0x170,大约位于0x55的offset.

在kernel底下找到此函数,位于driver/base/firmware_class.c,这个是用于sysfs,用来进行fimrware 读写的接口。

这里涉及到注册到sysfs的接口:

static DEVICE_ATTR(loading, 0644, firmware_loading_show, firmware_loading_store);

#define DEVICE_ATTR(_name, _mode, _show, _store) \
struct device_attribute dev_attr_##_name = __ATTR(_name, _mode, _show, _store)

其节点使用函数:device_create_file(f_dev, &dev_attr_loading)来创建,然后使用uevent 来产生一个udev add的动作,kobject_uevent(&fw_priv->dev.kobj, KOBJ_ADD);

kenel path A接着就进入了休眠状态,并等待一个60s的定时器超时:

mod_timer(&fw_priv->timeout,
                                  round_jiffies_up(jiffies +
                                                   loading_timeout * HZ));===》其作用是添加定时器

 wait_for_completion(&fw_priv->completion);===》等待firmware完成,需要其它进程给出一个完成的信号才可以继续往下


kernel path B(udev)被触发,进行fimrware的loading动作

SUBSYSTEM=="firmware", ACTION=="add", RUN+="/lib/udev/osal_fw_hotplug.sh"

其中/lib/udev/osal_fw_hotplug.sh脚本如下

# Trigger the start of the loading process
echo 1 >/sys/$DEVPATH/loading
sleep 2
# Load the FW file
cat "$FIRMWARE" > /sys/$DEVPATH/data
RET_VAL=$?
sleep 2
# Check the return code; if it failed, report the failure.
if [ $RET_VAL -ne 0 ]; then
    echo -1 >/sys/$DEVPATH/loading
else
    echo 0 >/sys/$DEVPATH/loading
fi
exit 0


问题就处在firmware_loading_store这个函数!!!

static ssize_t firmware_loading_store(struct device *dev,
                                      struct device_attribute *attr,
                                      const char *buf, size_t count)
{
        struct firmware_priv *fw_priv = to_firmware_priv(dev);
        int loading = simple_strtol(buf, NULL, 10);
        int i;


        switch (loading) {
        case 1:
                printk("Trigle to loading value:%d \n",loading);
                mutex_lock(&fw_lock);
                if (!fw_priv->fw) {
                        mutex_unlock(&fw_lock);
                        break;
                }
                firmware_free_data(fw_priv->fw);
                memset(fw_priv->fw, 0, sizeof(struct firmware));
                /* If the pages are not owned by 'struct firmware' */
                for (i = 0; i < fw_priv->nr_pages; i++)
                        __free_page(fw_priv->pages[i]);
                kfree(fw_priv->pages);
                fw_priv->pages = NULL;
                fw_priv->page_array_size = 0;
                fw_priv->nr_pages = 0;
                set_bit(FW_STATUS_LOADING, &fw_priv->status);
                mutex_unlock(&fw_lock);
                break;
        case 0:
                mutex_lock(&fw_lock);
                printk("Begin to loading value:%d \n",loading);
                if (test_bit(FW_STATUS_LOADING, &fw_priv->status)) {
                        vunmap(fw_priv->fw->data);
                        fw_priv->fw->data = vmap(fw_priv->pages,
                                                 fw_priv->nr_pages,
                                                 0, PAGE_KERNEL_RO);
                        if (!fw_priv->fw->data) {
                                dev_err(dev, "%s: vmap() failed\n", __func__);
                                mutex_unlock(&fw_lock);
                                goto err;
                        }

                      /* Pages are now owned by 'struct firmware' */
                        fw_priv->fw->pages = fw_priv->pages;
                        fw_priv->pages = NULL;


                        fw_priv->page_array_size = 0;
                        fw_priv->nr_pages = 0;
                        clear_bit(FW_STATUS_LOADING, &fw_priv->status);
                        complete(&fw_priv->completion);  /*这一步,如果成功完成了,在发送complete命令给kenel path A*/
                        /*clear_bit(FW_STATUS_LOADING, &fw_priv->status);*/
                }
                else if(test_bit(FW_STATUS_DONE,&fw_priv->status))
                {
                        printk("Loading status is %d\n",fw_priv->status);
                }

                mutex_unlock(&fw_lock);   /*在此地必须加锁,并且判定此时firmware的状态,因为,firmware的状态有可能不是LOADING,而是DONE的状态,造成NULL 指针*/
                break ;
                /* fallthrough */
        default:
                dev_err(dev, "%s: unexpected value (%d)\n", __func__, loading);
                /* fallthrough */
        case -1:
        err:
                printk("loading firmware abort,file:%s,line:%d!\n",__FILE__,__LINE__);
                fw_load_abort(fw_priv);
                break;
        }


        /*return count;*/
        return count;
}


kernel path A 在接到completion信号后,被唤醒,设置状态,并删除定时器:

        set_bit(FW_STATUS_DONE, &fw_priv->status);
        del_timer_sync(&fw_priv->timeout);


这里需要略微提一下设置bit的这个函数。。。




原创粉丝点击