全志平台 gpio-keys 驱动应用和 stack crash 解决
-
内核配置
内核版本:Linux version 4.9.56
make ARCH=arm64 menuconfig
Device Drivers ---> Input device support ---> [*] Keyboards ---> <*> GPIO Buttons
配置文件
sys_config.fex
;---------------------------------------------------------------------------------- ;gpio-keys parameters ;---------------------------------------------------------------------------------- [gpio-keys] compatible = "gpio-keys"; [gpio-keys/up] linux,code = 103 linux,input-type = 1 gpios = port:PH11<6><default><default><default> [gpio-keys/down] linux,code = 108 linux,input-type = 1 gpios = port:PH08<0><default><default><default> [gpio-keys/enter] linux,code = 28 linux,input-type = 1 gpios = port:PH10<6><default><default><default>
内存越界
日志信息
[ 2.868360] of_get_named_gpiod_flags: parsed 'gpios' property of node '/soc@01c00000/gpio-keys/up[0]' - status (0) [ 2.868366] of_get_gpio_flags button->gpio:235... [ 2.868393] of_get_named_gpiod_flags: parsed 'gpios' property of node '/soc@01c00000/gpio-keys/down[0]' - status (0) [ 2.868396] of_get_gpio_flags button->gpio:232... [ 2.868417] of_get_named_gpiod_flags: parsed 'gpios' property of node '/soc@01c00000/gpio-keys/enter[0]' - status (0) [ 2.868420] of_get_gpio_flags button->gpio:234... [ 2.869024] input: gpio-keys as /devices/platform/soc/gpio-keys/input/input3 [ 2.869369] Kernel panic - not syncing: stack-protector: Kernel stack is corrupted in: ffffff8008600ce0 [ 2.869369] [ 2.869380] CPU: 1 PID: 1 Comm: swapper/0 Not tainted 4.9.56 #165 [ 2.869383] Hardware name: sun50iw1 (DT) [ 2.869388] Call trace: [ 2.869409] [<ffffff800808a7d4>] dump_backtrace+0x0/0x22c [ 2.869417] [<ffffff800808aa24>] show_stack+0x24/0x30 [ 2.869430] [<ffffff80083c9a64>] dump_stack+0x8c/0xb0 [ 2.869438] [<ffffff80081a5960>] panic+0x14c/0x298 [ 2.869450] [<ffffff80080a19d0>] print_tainted+0x0/0xa8 [ 2.869463] [<ffffff8008600ce0>] gpio_keys_probe+0x6d0/0x804 [ 2.869474] [<ffffff80084b3e9c>] platform_drv_probe+0x60/0xac [ 2.869481] [<ffffff80084b1a6c>] driver_probe_device+0x1b8/0x3d4 [ 2.869485] SMP: stopping secondary CPUs [ 2.877351] Kernel Offset: disabled [ 2.877354] Memory Limit: none
解决补丁
diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c old mode 100644 new mode 100755 index 9b8079c..04e1580 --- a/drivers/input/keyboard/gpio_keys.c +++ b/drivers/input/keyboard/gpio_keys.c @@ -32,6 +32,7 @@ #include <linux/of_gpio.h> #include <linux/of_irq.h> #include <linux/spinlock.h> +#include <linux/sunxi-gpio.h> struct gpio_button_data { const struct gpio_keys_button *button; @@ -664,11 +665,11 @@ static void gpio_keys_close(struct input_dev *input) i = 0; for_each_available_child_of_node(node, pp) { - enum of_gpio_flags flags; + struct gpio_config gpio_flags; button = &pdata->buttons[i++]; - button->gpio = of_get_gpio_flags(pp, 0, &flags); + button->gpio = of_get_gpio_flags(pp, 0, (enum of_gpio_flags *)&gpio_flags); if (button->gpio < 0) { error = button->gpio; if (error != -ENOENT) { @@ -679,7 +680,7 @@ static void gpio_keys_close(struct input_dev *input) return ERR_PTR(error); } } else { - button->active_low = flags & OF_GPIO_ACTIVE_LOW; + button->active_low = gpio_flags.data & OF_GPIO_ACTIVE_LOW; } button->irq = irq_of_parse_and_map(pp, 0);
原因分析
- gpio_keys 驱动使用 of_get_gpio_flags() 获取 dts 里面 gpio 配置信息。
- 但是 of_get_gpio_flags() 传入 enum of_gpio_flags 类型来获取配置信息。
- of_get_gpio_flags() 的最终实现由具体的 SOC 厂商实现,这里是全志厂商实现。
- 实现的函数为:drivers/pinctrl/sunxi/pinctrl-sunxi.c --> sunxi_pinctrl_gpio_of_xlate()。
- 在 sunxi_pinctrl_gpio_of_xlate() 却是通过强制转换 struct gpio_config 类型存储 gpio 配置信息。
- 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; }
-
Copyright © 2024 深圳全志在线有限公司 粤ICP备2021084185号 粤公网安备44030502007680号