高通的描述
看门狗定时器是一个固定长度的计数器,它使系统能够从意外的硬件或软件灾难中恢复。
除非系统定期重置看门狗定时器,否则看门狗定时器会在定时器超时后自动重置系统。
MSM ASIC 为芯片系统使用了一个看门狗定时器。
调制解调器软件负责重置看门狗(kicking or petting the dog)并通过定期检查系统中的其他处理器(通过中断线握手)来检查系统中的其他处理器是否正常工作。
除了复位触发信号(wdog expired),还可以在看门狗到期之前生成看门狗中断,以允许处理器在重置系统之前尝试恢复系统。在以应用程序为中心的引导系统中,系统中驻留的看门狗不止一个。
全局系统重置将由系统信任根(SRoT)控制,例如应用处理器。所有其他子系统,包括调制解调器,一旦它们的看门狗到期,它只能向应用程序处理器生成中断。
然后应用处理器处理其余的操作。为了提供来自应用处理器的实时反应,子系统应该有一个专用的看门狗定时器。
在提供 BITE 和 BARK 看门狗定时器支持的系统中,当定时器超时时,相应的中断将被触发。
BITE 超时将向应用处理器发送中断并立即触发子系统复位。 BARK 超时将对子系统本身产生 FIQ 中断。
子系统必须将这种情况作为灾难性重启情况进行处理。还需要一次性写入看门狗寄存器以阻止恶意攻击禁用看门狗并允许
软件流程
代码路径
kernel/drivers/soc/qcom/watchdog_v2.c.
还有一个由应用程序端驱动的硬件看门狗。 如果硬件看门狗在 10 秒内没有受到pet,则会断言中断,这将使 Linux 内核panic并打印回溯(触发panic超时并在配置时重置)。
这被称为bark。 如果看门狗持续 11 秒没有被pet,将会重启。 这是dog bite。 看门狗驱动程序当前安排自己每 10 秒pet看门狗。
设备树中的配置
在msm8909平台上,看门狗的设备树配置信息默认保存在msm8909.dtsi中。
qcom,wdt@b017000 {compatible = "qcom,msm-watchdog" ;//Compatible name for matchingreg = <0xb017000 0x1000>; //Watchdog register addressreg-names = "wdt-base"; //resouce nameinterrupts = <0 3 0>, <0 4 0>;qcom,bark-time = <11000>; //Dog bark time configurationqcom,pet-time = <10000>; //Feed dog time configurationqcom,ipi-ping; //Used to ping other cpu states
};
驱动 Watchdog_v2.c
主要结构体
struct msm_watchdog_data {unsigned int __iomem phys_base;size_t size;void __iomem *base;void __iomem *wdog_absent_base;unsigned int pet_time; unsigned int bark_time; unsigned int bark_irq; unsigned int bite_irq; bool do_ipi_ping; unsigned long long last_pet; struct work_struct init_dogwork_struct; struct delayed_work dogwork_struct; bool irq_ppi; struct msm_watchdog_data __percpu **wdog_cpu_dd; struct notifier_block panic_blk; bool enabled; };
主要流程图
probe->
msm_wdog_dt_to_pdata(pdev, wdog_dd);
//Obtain information about device tree configuration, apply for memory space, and map memory.INIT_WORK(&wdog_dd->init_dogwork_struct, init_watchdog_work); INIT_DELAYED_WORK(&wdog_dd->dogwork_struct, pet_watchdog_work); queue_work(wdog_wq, &wdog_dd->init_dogwork_struct);
//Scheduling init workdevm_request_irq(wdog_dd->dev, wdog_dd->bark_irq,wdog_bark_handler, IRQF_TRIGGER_RISING,"apps_wdog_bark", wdog_dd);
//Application interrupttimeout = (wdog_dd->bark_time * WDT_HZ)/1000; __raw_writel(timeout, wdog_dd->base + WDT0_BARK_TIME);
// set register for bark-time Default 11s__raw_writel(timeout + 3*WDT_HZ, wdog_dd->base + WDT0_BITE_TIME);
// set register for bite-time Default 14swdog_dd->panic_blk.notifier_call = panic_wdog_handler; // set handle for panicqueue_delayed_work(wdog_wq, &wdog_dd->dogwork_struct,delay_time);
// Calling a delayed work queue__raw_writel(1, wdog_dd->base + WDT0_EN); // enable WDT0__raw_writel(1, wdog_dd->base + WDT0_RST);
// RST WDT0wdog_dd->last_pet = sched_clock();
// Record the time of the last dog feeding
queue_delayed_work->
ping_other_cpus(wdog_dd);
//ping the status of other cpu
pet_watchdog(wdog_dd);
//pet_watchdog
queue_delayed_work(wdog_wq,&wdog_dd->dogwork_struct, delay_time);
// delay_time = msecs_to_jiffies(wdog_dd->pet_time);
pet_watchdog->
static void pet_watchdog(struct msm_watchdog_data *wdog_dd){int slack, i, count, prev_count = 0;unsigned long long time_ns;unsigned long long slack_ns;unsigned long long bark_time_ns = wdog_dd->bark_time * 1000000ULL;for (i = 0; i < 2; i++) {count = (__raw_readl(wdog_dd->base + WDT0_STS) >> 1) & 0xFFFFF;if (count != prev_count) {prev_count = count;i = 0;}}slack = ((wdog_dd->bark_time * WDT_HZ) / 1000) - count;if (slack < wdog_dd->min_slack_ticks)wdog_dd->min_slack_ticks = slack;__raw_writel(1, wdog_dd->base + WDT0_RST);time_ns = sched_clock();slack_ns = (wdog_dd->last_pet + bark_time_ns) - time_ns;if (slack_ns < wdog_dd->min_slack_ns)wdog_dd->min_slack_ns = slack_ns;wdog_dd->last_pet = time_ns;}
如果未喂狗,则会触发看门狗中断
static irqreturn_t wdog_bark_handler(int irq, void *dev_id){struct msm_watchdog_data *wdog_dd = (struct msm_watchdog_data *)dev_id;unsigned long nanosec_rem;unsigned long long t = sched_clock();nanosec_rem = do_div(t, 1000000000);printk(KERN_INFO "Watchdog bark! Now = %lu.%06lu\n", (unsigned long) t,nanosec_rem / 1000);nanosec_rem = do_div(wdog_dd->last_pet, 1000000000);printk(KERN_INFO "Watchdog last pet at %lu.%06lu\n", (unsigned long)wdog_dd->last_pet, nanosec_rem / 1000);if (wdog_dd->do_ipi_ping)dump_cpu_alive_mask(wdog_dd);msm_trigger_wdog_bite(); //wdog bite The machine needs to be resetpanic("Failed to cause a watchdog bite! - Falling back to kernel panic!");return IRQ_HANDLED;}
msm_trigger_wdog_bite
void msm_trigger_wdog_bite(void){if (!wdog_data)return;pr_info("Causing a watchdog bite!");__raw_writel(1, wdog_data->base + WDT0_BITE_TIME); mb();__raw_writel(1, wdog_data->base + WDT0_RST); //Write a value to the hardware watchdog register. Trigger a hardware reset.mb();/* Delay to make sure bite occurs */mdelay(1);pr_err("Wdog - STS: 0x%x, CTL: 0x%x, BARK TIME: 0x%x, BITE TIME: 0x%x",__raw_readl(wdog_data->base + WDT0_STS),__raw_readl(wdog_data->base + WDT0_EN),__raw_readl(wdog_data->base + WDT0_BARK_TIME),__raw_readl(wdog_data->base + WDT0_BITE_TIME));}