导航

    全志在线开发者论坛

    • 注册
    • 登录
    • 搜索
    • 版块
    • 话题
    • 在线文档
    • 社区主页

    【分析笔记】全志平台 gpio_wdt 驱动应用和 stack crash 解决

    其它全志芯片讨论区
    1
    1
    1336
    正在加载更多帖子
    • 从旧到新
    • 从新到旧
    • 最多赞同
    回复
    • 在新帖中回复
    登录后回复
    此主题已被删除。只有拥有主题管理权限的用户可以查看。
    • D
      dream LV 6 最后由 dream 编辑

      使用说明


      第一次遇到看门狗芯片是通过切换电平信号来喂狗,如 SGM706 芯片,之前也比较少会用到看门狗芯片。原本打算参考 sunxi-wdt.c 的框架,利用定时器自己写一个,无意中发现内核已经有 gpio_wdt.c 驱动程序,其原理也是通过内核定时器实现喂狗。因其使用了 of_get_gpio_flags() 接口获取 GPIO 信息,和 gpio-keys.c 驱动一样,该接口存在内存越界的问题,需要略作修改才能使用。

      内核配置


      内核版本:Linux 4.9

      make ARCH=arm64 menuconfig

      Device Drivers  --->
      	[*] Watchdog Timer Support  --->
      		<*>   LED Support for GPIO connected LEDs 
      			<*>   Watchdog device controlled through GPIO-line
      

      配置文件


      sys_config.fex

      全志平台便捷方式配置,不过新平台不再支持 sys_config.fex了,也可以使用通用的 dts 配置方式

      ;----------------------------------------------------------------------------------
      ;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<1><default><default><default>
      

      配置选项说明:linux-4.9\Documentation\devicetree\bindings\watchdog\gpio_wdt.txt

      * 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 = <&gpio3 9 GPIO_ACTIVE_LOW>;
      		hw_algo = "toggle";
      		hw_margin_ms = <1600>;
      	};
      

      内存越界


      日志信息

      [    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] [<ffffff800808a7d4>] dump_backtrace+0x0/0x22c
      [    3.780566] [<ffffff800808aa24>] show_stack+0x24/0x30
      [    3.786226] [<ffffff80083c9a64>] dump_stack+0x8c/0xb0
      [    3.791880] [<ffffff80081a5960>] panic+0x14c/0x298
      [    3.797247] [<ffffff80080a19d0>] print_tainted+0x0/0xa8
      [    3.803099] [<ffffff8008655044>] gpio_wdt_probe+0x230/0x258
      [    3.809338] [<ffffff80084b3e9c>] platform_drv_probe+0x60/0xac
      [    3.815770] [<ffffff80084b1a6c>] driver_probe_device+0x1b8/0x3d4
      [    3.822493] [<ffffff80084b1d1c>] __driver_attach+0x94/0x108
      [    3.828729] [<ffffff80084af68c>] bus_for_each_dev+0x88/0xc8
      [    3.834964] [<ffffff80084b1374>] driver_attach+0x30/0x3c
      [    3.840908] [<ffffff80084b0d24>] bus_add_driver+0xf8/0x24c
      [    3.847046] [<ffffff80084b2a74>] driver_register+0x9c/0xe8
      [    3.853186] [<ffffff80084b3de0>] __platform_driver_register+0x5c/0x68
      [    3.860400] [<ffffff8008d48a20>] gpio_wdt_driver_init+0x18/0x20
      [    3.867026] [<ffffff8008083a2c>] do_one_initcall+0xb0/0x14c
      [    3.873267] [<ffffff8008d10d64>] kernel_init_freeable+0x14c/0x1e8
      [    3.880093] [<ffffff8008970724>] kernel_init+0x18/0x104
      [    3.885941] [<ffffff8008083050>] 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..
      

      解决补丁

      --- 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 <linux/err.h>
       #include <linux/delay.h>
       #include <linux/module.h>
       #include <linux/of_gpio.h>
       #include <linux/platform_device.h>
       #include <linux/watchdog.h>
      +#include <linux/sunxi-gpio.h>
       
       #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(&pdev->dev, sizeof(*priv), GFP_KERNEL);
       	if (!priv)
       		return -ENOMEM;
       
       	platform_set_drvdata(pdev, priv);
       
      -	priv->gpio = of_get_gpio_flags(pdev->dev.of_node, 0, &flags);
      +	priv->gpio = of_get_gpio_flags(pdev->dev.of_node, 0, (enum of_gpio_flags *)&gpio_flags);
       	if (!gpio_is_valid(priv->gpio))
       		return priv->gpio;
       
      -	priv->active_low = flags & OF_GPIO_ACTIVE_LOW;
      +	priv->active_low = gpio_flags.data & OF_GPIO_ACTIVE_LOW;
       
       	ret = of_property_read_string(pdev->dev.of_node, "hw_algo", &algo);
       	if (ret)
       		return ret;
       	if (!strcmp(algo, "toggle")) {
       		priv->hw_algo = HW_ALGO_TOGGLE;
      

      原因分析

      1. gpio_wdt 驱动使用 of_get_gpio_flags() 获取 dts 里面 gpio 配置信息。
      2. 但是 of_get_gpio_flags() 传入 enum of_gpio_flags 类型来获取配置信息。
      3. of_get_gpio_flags() 的最终实现由具体的 SOC 厂商实现,这里是全志厂商实现。
      4. 实现的函数为:drivers/pinctrl/sunxi/pinctrl-sunxi.c --> sunxi_pinctrl_gpio_of_xlate()。
      5. 在 sunxi_pinctrl_gpio_of_xlate() 却是通过强制转换 struct gpio_config 类型存储 gpio 配置信息。
      6. enum of_gpio_flags 占用 4 字节,而 struct gpio_config 占用 20 字节,出现内存越界操作的问题。
      // 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->args[0];
      	pin = base + gpiospec->args[1];
      	pin = pin - gc->base;
      	if (pin > gc->ngpio)
      		return -EINVAL;
      
      	if (flags) {
      		// 问题出在这个条件下面的赋值语句
      		// 传进来的是 enum of_gpio_flags,只有 4Byte 
      		// 结果使用的 struct gpio_config,却有 20Byte 
      		config = (struct gpio_config *)flags;
      		config->gpio = base + gpiospec->args[1];
      		config->mul_sel = gpiospec->args[2];
      		config->drv_level = gpiospec->args[3];
      		config->pull = gpiospec->args[4];
      		config->data = gpiospec->args[5];
      	}
      
      	return pin;
      }
      
      1 条回复 最后回复 回复 引用 分享 0
      • 1 / 1
      • First post
        Last post

      Copyright © 2024 深圳全志在线有限公司 粤ICP备2021084185号 粤公网安备44030502007680号

      行为准则 | 用户协议 | 隐私权政策