<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[【分析笔记】全志平台 gpio_wdt 驱动应用和 stack crash 解决]]></title><description><![CDATA[<h2>使用说明</h2>
<hr />
<p dir="auto">第一次遇到看门狗芯片是通过切换电平信号来喂狗，如 SGM706 芯片，之前也比较少会用到看门狗芯片。原本打算参考 sunxi-wdt.c 的框架，利用定时器自己写一个，无意中发现内核已经有 gpio_wdt.c 驱动程序，其原理也是通过内核定时器实现喂狗。因其使用了 of_get_gpio_flags() 接口获取 GPIO 信息，和 gpio-keys.c 驱动一样，该接口存在内存越界的问题，需要略作修改才能使用。</p>
<h2>内核配置</h2>
<hr />
<blockquote>
<p dir="auto">内核版本：Linux 4.9</p>
</blockquote>
<h3>make ARCH=arm64 menuconfig</h3>
<pre><code>Device Drivers  ---&gt;
	[*] Watchdog Timer Support  ---&gt;
		&lt;*&gt;   LED Support for GPIO connected LEDs 
			&lt;*&gt;   Watchdog device controlled through GPIO-line
</code></pre>
<h2>配置文件</h2>
<hr />
<h3>sys_config.fex</h3>
<blockquote>
<p dir="auto">全志平台便捷方式配置，不过新平台不再支持 sys_config.fex了，也可以使用通用的 dts 配置方式</p>
</blockquote>
<pre><code class="language-c">;----------------------------------------------------------------------------------
;gpio-wdt parameters
;compatible: 匹配平台驱动
;hw_algo: 清除看门狗计数方式：切换方式(toggle)或脉冲方式(level)
;hw_margin_ms: 看门狗电路会触发复位的最长时间(毫秒)，不能小于 2 或者大于 65535
;always-running: 如果看门狗不能关闭，使能后驱动默认会自动喂狗，在应用层调用 STOP 接口不会执行关闭
;gpios: 连接看门狗芯片 WDI 的引脚
;----------------------------------------------------------------------------------
[wdt-gpio]
compatible = "linux,wdt-gpio"
hw_algo = "level"
hw_margin_ms = 1600
always-running = "true"
gpios = port:PL12&lt;1&gt;&lt;default&gt;&lt;default&gt;&lt;default&gt;
</code></pre>
<p dir="auto">配置选项说明：linux-4.9\Documentation\devicetree\bindings\watchdog\gpio_wdt.txt</p>
<pre><code>* GPIO-controlled Watchdog

Required Properties:
- compatible: Should contain "linux,wdt-gpio".
- gpios: From common gpio binding; gpio connection to WDT reset pin.
- hw_algo: The algorithm used by the driver. Should be one of the
  following values:
  - toggle: Either a high-to-low or a low-to-high transition clears
    the WDT counter. The watchdog timer is disabled when GPIO is
    left floating or connected to a three-state buffer.
  - level: Low or high level starts counting WDT timeout,
    the opposite level disables the WDT. Active level is determined
    by the GPIO flags.
- hw_margin_ms: Maximum time to reset watchdog circuit (milliseconds).

Optional Properties:
- always-running: If the watchdog timer cannot be disabled, add this flag to
  have the driver keep toggling the signal without a client. It will only cease
  to toggle the signal when the device is open and the timeout elapsed.

Example:
	watchdog: watchdog {
		/* ADM706 */
		compatible = "linux,wdt-gpio";
		gpios = &lt;&amp;gpio3 9 GPIO_ACTIVE_LOW&gt;;
		hw_algo = "toggle";
		hw_margin_ms = &lt;1600&gt;;
	};
</code></pre>
<h2>内存越界</h2>
<hr />
<h3>日志信息</h3>
<pre><code>[    3.727333] sunxi-wdt 1c20ca0.watchdog: Watchdog enabled (timeout=16 sec, nowayout=0)
[    3.736874] of_get_named_gpiod_flags: parsed 'gpios' property of node '/soc@01c00000/wdt-gpio[0]' - status (0)
[    3.748392] Kernel panic - not syncing: stack-protector: Kernel stack is corrupted in: ffffff8008655044
[    3.748392] 
[    3.760564] CPU: 2 PID: 1 Comm: swapper/0 Not tainted 4.9.56 #179
[    3.767382] Hardware name: sun50iw1 (DT)
[    3.771771] Call trace:
[    3.774522] [&lt;ffffff800808a7d4&gt;] dump_backtrace+0x0/0x22c
[    3.780566] [&lt;ffffff800808aa24&gt;] show_stack+0x24/0x30
[    3.786226] [&lt;ffffff80083c9a64&gt;] dump_stack+0x8c/0xb0
[    3.791880] [&lt;ffffff80081a5960&gt;] panic+0x14c/0x298
[    3.797247] [&lt;ffffff80080a19d0&gt;] print_tainted+0x0/0xa8
[    3.803099] [&lt;ffffff8008655044&gt;] gpio_wdt_probe+0x230/0x258
[    3.809338] [&lt;ffffff80084b3e9c&gt;] platform_drv_probe+0x60/0xac
[    3.815770] [&lt;ffffff80084b1a6c&gt;] driver_probe_device+0x1b8/0x3d4
[    3.822493] [&lt;ffffff80084b1d1c&gt;] __driver_attach+0x94/0x108
[    3.828729] [&lt;ffffff80084af68c&gt;] bus_for_each_dev+0x88/0xc8
[    3.834964] [&lt;ffffff80084b1374&gt;] driver_attach+0x30/0x3c
[    3.840908] [&lt;ffffff80084b0d24&gt;] bus_add_driver+0xf8/0x24c
[    3.847046] [&lt;ffffff80084b2a74&gt;] driver_register+0x9c/0xe8
[    3.853186] [&lt;ffffff80084b3de0&gt;] __platform_driver_register+0x5c/0x68
[    3.860400] [&lt;ffffff8008d48a20&gt;] gpio_wdt_driver_init+0x18/0x20
[    3.867026] [&lt;ffffff8008083a2c&gt;] do_one_initcall+0xb0/0x14c
[    3.873267] [&lt;ffffff8008d10d64&gt;] kernel_init_freeable+0x14c/0x1e8
[    3.880093] [&lt;ffffff8008970724&gt;] kernel_init+0x18/0x104
[    3.885941] [&lt;ffffff8008083050&gt;] ret_from_fork+0x10/0x40
[    3.891884] SMP: stopping secondary CPUs
[    3.896276] Kernel Offset: disabled
[    3.900178] Memory Limit: none
[    3.909173] Rebooting in 5 seconds..
</code></pre>
<h3>解决补丁</h3>
<pre><code class="language-c">--- linux-4.9\drivers\watchdog\gpio_wdt.c	2022-09-13 17:51:57.000000000 +0800
+++ linux-4.9\drivers\watchdog\gpio_wdt.c	2022-09-13 17:51:37.000000000 +0800
@@ -12,12 +12,13 @@
 #include &lt;linux/err.h&gt;
 #include &lt;linux/delay.h&gt;
 #include &lt;linux/module.h&gt;
 #include &lt;linux/of_gpio.h&gt;
 #include &lt;linux/platform_device.h&gt;
 #include &lt;linux/watchdog.h&gt;
+#include &lt;linux/sunxi-gpio.h&gt;
 
 #define SOFT_TIMEOUT_MIN	1
 #define SOFT_TIMEOUT_DEF	60
 #define SOFT_TIMEOUT_MAX	0xffff
 
 enum {
@@ -138,29 +139,29 @@
 	.set_timeout	= gpio_wdt_set_timeout,
 };
 
 static int gpio_wdt_probe(struct platform_device *pdev)
 {
 	struct gpio_wdt_priv *priv;
-	enum of_gpio_flags flags;
+	struct gpio_config gpio_flags;
 	unsigned int hw_margin;
 	unsigned long f = 0;
 	const char *algo;
 	int ret;
 
 	priv = devm_kzalloc(&amp;pdev-&gt;dev, sizeof(*priv), GFP_KERNEL);
 	if (!priv)
 		return -ENOMEM;
 
 	platform_set_drvdata(pdev, priv);
 
-	priv-&gt;gpio = of_get_gpio_flags(pdev-&gt;dev.of_node, 0, &amp;flags);
+	priv-&gt;gpio = of_get_gpio_flags(pdev-&gt;dev.of_node, 0, (enum of_gpio_flags *)&amp;gpio_flags);
 	if (!gpio_is_valid(priv-&gt;gpio))
 		return priv-&gt;gpio;
 
-	priv-&gt;active_low = flags &amp; OF_GPIO_ACTIVE_LOW;
+	priv-&gt;active_low = gpio_flags.data &amp; OF_GPIO_ACTIVE_LOW;
 
 	ret = of_property_read_string(pdev-&gt;dev.of_node, "hw_algo", &amp;algo);
 	if (ret)
 		return ret;
 	if (!strcmp(algo, "toggle")) {
 		priv-&gt;hw_algo = HW_ALGO_TOGGLE;
</code></pre>
<h3>原因分析</h3>
<ol>
<li>gpio_wdt 驱动使用 of_get_gpio_flags() 获取 dts 里面 gpio 配置信息。</li>
<li>但是 of_get_gpio_flags() 传入 enum of_gpio_flags 类型来获取配置信息。</li>
<li>of_get_gpio_flags() 的最终实现由具体的 SOC 厂商实现，这里是全志厂商实现。</li>
<li>实现的函数为：drivers/pinctrl/sunxi/pinctrl-sunxi.c --&gt; sunxi_pinctrl_gpio_of_xlate()。</li>
<li>在 sunxi_pinctrl_gpio_of_xlate() 却是通过强制转换 struct gpio_config 类型存储 gpio 配置信息。</li>
<li>enum of_gpio_flags 占用 4 字节，而 struct gpio_config 占用 20 字节，出现内存越界操作的问题。</li>
</ol>
<pre><code class="language-c">// include/linux/of_gpio.h
enum of_gpio_flags {		
	OF_GPIO_ACTIVE_LOW = 0x1,
	OF_GPIO_SINGLE_ENDED = 0x2,
}; // 4Byte

// include/linux/sunxi-gpio.h
struct gpio_config {
	u32	gpio;
	u32	mul_sel;
	u32	pull;
	u32	drv_level;
	u32	data;
}; // 20Byte

// drivers/pinctrl/sunxi/pinctrl-sunxi.c
static int sunxi_pinctrl_gpio_of_xlate(struct gpio_chip *gc,
				const struct of_phandle_args *gpiospec,
				u32 *flags)
{
	struct gpio_config *config;
	int pin, base;

	base = PINS_PER_BANK * gpiospec-&gt;args[0];
	pin = base + gpiospec-&gt;args[1];
	pin = pin - gc-&gt;base;
	if (pin &gt; gc-&gt;ngpio)
		return -EINVAL;

	if (flags) {
		// 问题出在这个条件下面的赋值语句
		// 传进来的是 enum of_gpio_flags，只有 4Byte 
		// 结果使用的 struct gpio_config，却有 20Byte 
		config = (struct gpio_config *)flags;
		config-&gt;gpio = base + gpiospec-&gt;args[1];
		config-&gt;mul_sel = gpiospec-&gt;args[2];
		config-&gt;drv_level = gpiospec-&gt;args[3];
		config-&gt;pull = gpiospec-&gt;args[4];
		config-&gt;data = gpiospec-&gt;args[5];
	}

	return pin;
}
</code></pre>
]]></description><link>https://bbs.aw-ol.com/topic/2149/分析笔记-全志平台-gpio_wdt-驱动应用和-stack-crash-解决</link><generator>RSS for Node</generator><lastBuildDate>Sun, 10 May 2026 08:35:26 GMT</lastBuildDate><atom:link href="https://bbs.aw-ol.com/topic/2149.rss" rel="self" type="application/rss+xml"/><pubDate>Sat, 17 Sep 2022 03:31:46 GMT</pubDate><ttl>60</ttl></channel></rss>