如果直接在iTCO_wdt.c中这样开启看门狗:
static int __init iTCO_wdt_init_module(void) { int err; printk(KERN_INFO PFX "Intel TCO WatchDog Timer Driver v%s\n", DRV_VERSION); err = platform_driver_register(&iTCO_wdt_driver); if (err) return err; iTCO_wdt_platform_device = platform_device_register_simple(DRV_NAME, -1, NULL, 0); if (IS_ERR(iTCO_wdt_platform_device)) { err = PTR_ERR(iTCO_wdt_platform_device); goto unreg_platform_driver; } iTCO_wdt_start(); if (iTCO_wdt_set_heartbeat(WATCHDOG_HEARTBEAT)) { return -EINVAL; } iTCO_wdt_keepalive(); return 0; unreg_platform_driver: platform_driver_unregister(&iTCO_wdt_driver); return err; }
会在某些情况下导致内核崩溃
跟踪一下:
iTCO_wdt_probe()
--> iTCO_wdt_init():
static int __devinit iTCO_wdt_init(struct pci_dev *pdev, const struct pci_device_id *ent, struct platform_device *dev) { ... if (iTCO_wdt_private.iTCO_version == 2) { pci_read_config_dword(pdev, 0xf0, &base_address); RCBA = base_address & 0xffffc000; iTCO_wdt_private.gcs = ioremap((RCBA + 0x3410), 4); } ... /* Check chipset's NO_REBOOT bit */ if (iTCO_wdt_unset_NO_REBOOT_bit() && iTCO_vendor_check_noreboot_on()) { printk(KERN_ERR PFX "failed to reset NO_REBOOT flag, " "reboot disabled by hardware\n"); ret = -ENODEV; /* Cannot reset NO_REBOOT bit */ goto out; } ... out: if (iTCO_wdt_private.iTCO_version == 2) iounmap(iTCO_wdt_private.gcs); pci_dev_put(iTCO_wdt_private.pdev); iTCO_wdt_private.ACPIBASE = 0; return ret; }
在iTCO_wdt_init()中会调用iTCO_wdt_unset_NO_REBOOT_bit()函数,如果这个函数中NO_REBOOT是硬件禁止的(这里和南桥的SPKR信号有关,即和蜂鸣器的那个信号有关),在我们产品上应该去掉SPKR上的电阻R894,否则,内核启动时会出现:
iTCO_wdt: failed to reset NO_REBOOT flag, reboot disabled by hardware
这种打印。
继续关注:如果这里出现了这种打印,也就是说这里会执行goto out
在out:中会将iTCO_wdt_private.gcs申请的内存释放掉,那么:
在iTCO_wdt_init_module()中再去调用iTCO_wdt_start(),然后iTCO_wdt_start()会调用iTCO_wdt_unset_NO_REBOOT_bit(),
在iTCO_wdt_unset_NO_REBOOT_bit()中仍会对iTCO_wdt_private.gcs进行访问,那么内核就会访问被释放掉的内存,那么内核就可能会crack!
解决方案:
需要在iTCO_wdt_init_module()中调用iTCO_wdt_start()时,先判断iTCO_wdt_private.ACPIBASE该值是否为0,如果是0,则不去调用,否则才调用:
if(0 != iTCO_wdt_private.ACPIBASE) { iTCO_wdt_start(); if (iTCO_wdt_set_heartbeat(WATCHDOG_HEARTBEAT)) { return -EINVAL; } iTCO_wdt_keepalive(); }