* [PATCH v2 0/2] gpio: mmio: report the line direction on chips without direction registers
@ 2026-09-02 7:39 Mehmet Fide
2026-09-02 7:39 ` [PATCH v2 1/2] pinctrl: imx: answer OUTPUT_ENABLE/INPUT_ENABLE queries from the pad register Mehmet Fide
2026-09-02 7:39 ` [PATCH v2 2/2] gpio: mmio: track the direction of chips without direction registers Mehmet Fide
0 siblings, 2 replies; 5+ messages in thread
From: Mehmet Fide @ 2026-09-02 7:39 UTC (permalink / raw)
To: Bartosz Golaszewski, Linus Walleij
Cc: Dong Aisheng, Fabio Estevam, Frank Li, Jacky Bai, Sascha Hauer,
Pengutronix Kernel Team, imx, linux-gpio, linux-arm-kernel,
linux-kernel, Mehmet Fide
From: Mehmet Fide <mehmet.fide@screeningeagle.com>
Hi Bartosz, Linus,
this replaces the gpiolib guard patch [1], along the lines Bartosz
suggested there: instead of teaching gpiod_get_direction() to stay quiet
when a chip has no get_direction(), give gpio-mmio one and let the pin
controller tell it what the pad does.
The user is the Vybrid GPIO block (gpio-vf610, a generic mmio chip with
GPIO_GENERIC_PINCTRL_BACKEND and no direction registers, the direction
lives in the iomuxc pad as the OBE bit). Today every
gpiod_get_direction() there trips the WARN in gpiolib, 21 backtraces per
boot on a Colibri VF61/VF50.
Patch 1 is the pinctrl-imx side. Bartosz asked whether the raw register
coming back from pin_config_get() is a bug in pinctrl-imx: it is, the
callback never looked at which parameter was requested. It now answers
PIN_CONFIG_OUTPUT_ENABLE and PIN_CONFIG_INPUT_ENABLE on SoCs that say
where those bits live (Vybrid: OBE bit 1, IBE bit 0) and -ENOTSUPP for
everything else; the debugfs dump, the only raw-register user, reads the
register through its own helper. Converting the driver fully to generic
pinconf is a bigger job than this fix needs.
Patch 2 keeps the direction in gpio-mmio's existing shadow and installs
the shadow-reading get_direction() for the "pinctrl backend, no
direction registers" combination. The pad is asked once, from request(),
in process context. v1 asked pinctrl from get_direction() itself; the
Sashiko review pointed out that gpiochip_lock_as_irq() calls
get_direction() for !can_sleep chips under the irq descriptor lock, so
the pinctrl mutex is not an option there.
Tested on a Colibri VF61 (Iris carrier) on top of gpio/for-next, with
DEBUG_ATOMIC_SLEEP and PROVE_LOCKING enabled this time: no backtraces,
and /sys/kernel/debug/gpio shows the right direction for every requested
line (the hogs, the SD card detect input, the USB VBUS regulator
output). Lines the pin controller cannot answer for keep
the input default gpiolib assumed before, so nothing that worked before
is affected. The initial direction scan in gpiochip_add_data_with_key()
runs before the pin ranges exist and still guesses; only requested lines
get the real answer.
Patch 2 needs patch 1 to give correct answers; taking both through one
tree, with an ack from the other side, avoids the window.
[1] https://lore.kernel.org/linux-gpio/20260813193715.2346477-1-mehmet.fide@gmail.com/
Mehmet Fide (2):
pinctrl: imx: answer OUTPUT_ENABLE/INPUT_ENABLE queries from the pad
register
gpio: mmio: track the direction of chips without direction registers
drivers/gpio/gpio-mmio.c | 63 +++++++++++++++++++++--
drivers/pinctrl/freescale/pinctrl-imx.c | 51 ++++++++++++++++--
drivers/pinctrl/freescale/pinctrl-imx.h | 4 ++
drivers/pinctrl/freescale/pinctrl-vf610.c | 2 +
4 files changed, 113 insertions(+), 7 deletions(-)
--
2.54.0
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH v2 1/2] pinctrl: imx: answer OUTPUT_ENABLE/INPUT_ENABLE queries from the pad register
2026-09-02 7:39 [PATCH v2 0/2] gpio: mmio: report the line direction on chips without direction registers Mehmet Fide
@ 2026-09-02 7:39 ` Mehmet Fide
2026-09-02 7:56 ` sashiko-bot
2026-09-02 7:39 ` [PATCH v2 2/2] gpio: mmio: track the direction of chips without direction registers Mehmet Fide
1 sibling, 1 reply; 5+ messages in thread
From: Mehmet Fide @ 2026-09-02 7:39 UTC (permalink / raw)
To: Bartosz Golaszewski, Linus Walleij
Cc: Dong Aisheng, Fabio Estevam, Frank Li, Jacky Bai, Sascha Hauer,
Pengutronix Kernel Team, imx, linux-gpio, linux-arm-kernel,
linux-kernel, Mehmet Fide
From: Mehmet Fide <mehmet.fide@screeningeagle.com>
The mmio pinconf get callback ignores which parameter was requested and
returns the raw conf register, so a generic query through
pinctrl_gpio_get_config() gets register bits back instead of the packed
parameter it asked for.
Decode the requested parameter and answer PIN_CONFIG_OUTPUT_ENABLE and
PIN_CONFIG_INPUT_ENABLE on SoCs that declare where those bits live in
the pad register; Vybrid has OBE at bit 1 and IBE at bit 0. The answer
is 0 with the bit value as the argument, which is what the
pinctrl_gpio_get_config() users (gpio-by-pinctrl, and gpio-mmio in the
next patch) expect. Everything else, including pins the device tree
never configured, gets -ENOTSUPP.
The only in-tree user of the raw register was the debugfs group dump,
which called the callback with an uninitialized config; it now reads
the register through its own helper, like the single pin dump already
did.
This gives gpio-mmio a way to read back the line direction on chips
whose direction lives in the pin controller.
Suggested-by: Bartosz Golaszewski <brgl@kernel.org>
Signed-off-by: Mehmet Fide <mehmet.fide@screeningeagle.com>
---
v2:
- the pin_config_get callback answers only the parameters the SoC
declares and returns -ENOTSUPP otherwise; the debugfs group dump reads
the raw register through its own helper instead of calling the
callback with an uninitialized config (Sashiko)
- subject prefix pinctrl: imx:
drivers/pinctrl/freescale/pinctrl-imx.c | 51 +++++++++++++++++++++--
drivers/pinctrl/freescale/pinctrl-imx.h | 4 ++
drivers/pinctrl/freescale/pinctrl-vf610.c | 2 +
3 files changed, 54 insertions(+), 3 deletions(-)
drivers/pinctrl/freescale/pinctrl-imx.c | 51 +++++++++++++++++++++--
drivers/pinctrl/freescale/pinctrl-imx.h | 4 ++
drivers/pinctrl/freescale/pinctrl-vf610.c | 2 +
3 files changed, 54 insertions(+), 3 deletions(-)
diff --git a/drivers/pinctrl/freescale/pinctrl-imx.c b/drivers/pinctrl/freescale/pinctrl-imx.c
index 9a45b376d36f..506ee6627c82 100644
--- a/drivers/pinctrl/freescale/pinctrl-imx.c
+++ b/drivers/pinctrl/freescale/pinctrl-imx.c
@@ -21,6 +21,7 @@
#include <linux/pinctrl/machine.h>
#include <linux/pinctrl/pinconf.h>
+#include <linux/pinctrl/pinconf-generic.h>
#include <linux/pinctrl/pinctrl.h>
#include <linux/pinctrl/pinmux.h>
@@ -291,8 +292,8 @@ struct pinmux_ops imx_pmx_ops = {
.set_mux = imx_pmx_set,
};
-static int imx_pinconf_get_mmio(struct pinctrl_dev *pctldev, unsigned pin_id,
- unsigned long *config)
+static int imx_pinconf_get_raw_mmio(struct pinctrl_dev *pctldev,
+ unsigned int pin_id, unsigned long *config)
{
struct imx_pinctrl *ipctl = pinctrl_dev_get_drvdata(pctldev);
const struct imx_pinctrl_soc_info *info = ipctl->info;
@@ -312,6 +313,38 @@ static int imx_pinconf_get_mmio(struct pinctrl_dev *pctldev, unsigned pin_id,
return 0;
}
+static int imx_pinconf_get_mmio(struct pinctrl_dev *pctldev,
+ unsigned int pin_id, unsigned long *config)
+{
+ struct imx_pinctrl *ipctl = pinctrl_dev_get_drvdata(pctldev);
+ const struct imx_pinctrl_soc_info *info = ipctl->info;
+ const struct imx_pin_reg *pin_reg = &ipctl->pin_regs[pin_id];
+ enum pin_config_param param = pinconf_to_config_param(*config);
+ unsigned int mask;
+ u32 raw;
+
+ /* only the parameters the SoC declares a pad bit for */
+ switch (param) {
+ case PIN_CONFIG_OUTPUT_ENABLE:
+ mask = info->obe_mask;
+ break;
+ case PIN_CONFIG_INPUT_ENABLE:
+ mask = info->ibe_mask;
+ break;
+ default:
+ mask = 0;
+ break;
+ }
+
+ if (!mask || pin_reg->conf_reg == -1)
+ return -ENOTSUPP;
+
+ raw = readl(ipctl->base + pin_reg->conf_reg);
+ *config = pinconf_to_config_packed(param, !!(raw & mask));
+
+ return 0;
+}
+
static int imx_pinconf_get(struct pinctrl_dev *pctldev,
unsigned pin_id, unsigned long *config)
{
@@ -324,6 +357,18 @@ static int imx_pinconf_get(struct pinctrl_dev *pctldev,
return imx_pinconf_get_mmio(pctldev, pin_id, config);
}
+static int imx_pinconf_get_raw(struct pinctrl_dev *pctldev,
+ unsigned int pin_id, unsigned long *config)
+{
+ struct imx_pinctrl *ipctl = pinctrl_dev_get_drvdata(pctldev);
+ const struct imx_pinctrl_soc_info *info = ipctl->info;
+
+ if (info->flags & IMX_USE_SCU)
+ return info->imx_pinconf_get(pctldev, pin_id, config);
+ else
+ return imx_pinconf_get_raw_mmio(pctldev, pin_id, config);
+}
+
static int imx_pinconf_set_mmio(struct pinctrl_dev *pctldev,
unsigned pin_id, unsigned long *configs,
unsigned num_configs)
@@ -426,7 +471,7 @@ static void imx_pinconf_group_dbg_show(struct pinctrl_dev *pctldev,
struct imx_pin *pin = &((struct imx_pin *)(grp->data))[i];
name = pin_get_name(pctldev, pin->pin);
- ret = imx_pinconf_get(pctldev, pin->pin, &config);
+ ret = imx_pinconf_get_raw(pctldev, pin->pin, &config);
if (ret)
return;
seq_printf(s, " %s: 0x%lx\n", name, config);
diff --git a/drivers/pinctrl/freescale/pinctrl-imx.h b/drivers/pinctrl/freescale/pinctrl-imx.h
index f65ff45b4003..8fa7e1e2521d 100644
--- a/drivers/pinctrl/freescale/pinctrl-imx.h
+++ b/drivers/pinctrl/freescale/pinctrl-imx.h
@@ -91,6 +91,10 @@ struct imx_pinctrl_soc_info {
unsigned int mux_mask;
u8 mux_shift;
+ /* OBE/IBE bits in the conf register, 0 if the pad does not have them */
+ unsigned int obe_mask;
+ unsigned int ibe_mask;
+
int (*gpio_set_direction)(struct pinctrl_dev *pctldev,
struct pinctrl_gpio_range *range,
unsigned offset,
diff --git a/drivers/pinctrl/freescale/pinctrl-vf610.c b/drivers/pinctrl/freescale/pinctrl-vf610.c
index 76a4bc0181a0..77d077618782 100644
--- a/drivers/pinctrl/freescale/pinctrl-vf610.c
+++ b/drivers/pinctrl/freescale/pinctrl-vf610.c
@@ -319,6 +319,8 @@ static const struct imx_pinctrl_soc_info vf610_pinctrl_info = {
.gpio_set_direction = vf610_pmx_gpio_set_direction,
.mux_mask = 0x700000,
.mux_shift = 20,
+ .obe_mask = 0x2,
+ .ibe_mask = 0x1,
};
static const struct of_device_id vf610_pinctrl_of_match[] = {
--
2.54.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH v2 2/2] gpio: mmio: track the direction of chips without direction registers
2026-09-02 7:39 [PATCH v2 0/2] gpio: mmio: report the line direction on chips without direction registers Mehmet Fide
2026-09-02 7:39 ` [PATCH v2 1/2] pinctrl: imx: answer OUTPUT_ENABLE/INPUT_ENABLE queries from the pad register Mehmet Fide
@ 2026-09-02 7:39 ` Mehmet Fide
1 sibling, 0 replies; 5+ messages in thread
From: Mehmet Fide @ 2026-09-02 7:39 UTC (permalink / raw)
To: Bartosz Golaszewski, Linus Walleij
Cc: Dong Aisheng, Fabio Estevam, Frank Li, Jacky Bai, Sascha Hauer,
Pengutronix Kernel Team, imx, linux-gpio, linux-arm-kernel,
linux-kernel, Mehmet Fide
From: Mehmet Fide <mehmet.fide@screeningeagle.com>
A generic chip with GPIO_GENERIC_PINCTRL_BACKEND and no direction
registers sets the direction through pinctrl but has no get_direction
callback, so every gpiod_get_direction() call trips the WARN in gpiolib
and the direction gpiolib reports is whatever it assumed. On a Vybrid
Colibri module that is 21 backtraces per boot.
Keep the direction of such a chip in the existing shadow: the direction
setters update sdir under the chip lock, and get_direction() is the
shadow-reading path already used for unreadable direction registers.
That keeps the callback usable in atomic context, which it has to be:
gpiochip_lock_as_irq() calls it for !can_sleep chips from
gpiochip_irq_domain_activate(), under the irq descriptor lock.
The pad's actual state is read once, in process context, when a line is
requested: gpiolib calls request() right before get_direction() for a
new line, so the shadow is seeded from PIN_CONFIG_OUTPUT_ENABLE there and
the line reports what the pin controller says. Lines pinctrl cannot
answer for keep the input default, which is what gpiolib assumed before.
Suggested-by: Bartosz Golaszewski <brgl@kernel.org>
Signed-off-by: Mehmet Fide <mehmet.fide@screeningeagle.com>
---
v2:
- keep the direction in the chip's shadow and reuse the shadow-reading
get_direction(); seed the shadow from pinctrl in request(), because
gpiochip_lock_as_irq() calls get_direction() under the irq descriptor
lock for !can_sleep chips (Sashiko)
- lines pinctrl cannot answer for keep the input default, no error
returned to gpiochip_lock_as_irq() (Sashiko)
- drop the claim about the initial direction scan, it runs before the
pin ranges exist (Sashiko)
drivers/gpio/gpio-mmio.c | 65 +++++++++++++++++++++++++++++++++++++---
1 file changed, 61 insertions(+), 4 deletions(-)
drivers/gpio/gpio-mmio.c | 63 +++++++++++++++++++++++++++++++++++++---
1 file changed, 59 insertions(+), 4 deletions(-)
diff --git a/drivers/gpio/gpio-mmio.c b/drivers/gpio/gpio-mmio.c
index 7e4b3e8d609f..84bf8cc7a0c7 100644
--- a/drivers/gpio/gpio-mmio.c
+++ b/drivers/gpio/gpio-mmio.c
@@ -49,6 +49,7 @@ o ` ~~~~\___/~~~~ ` controller in FPGA is ,.`
#include <linux/log2.h>
#include <linux/module.h>
#include <linux/pinctrl/consumer.h>
+#include <linux/pinctrl/pinconf-generic.h>
#include <linux/platform_device.h>
#include <linux/property.h>
#include <linux/spinlock.h>
@@ -372,7 +373,17 @@ static int gpio_mmio_dir_in_err(struct gpio_chip *gc, unsigned int gpio)
static int gpio_mmio_simple_dir_in(struct gpio_chip *gc, unsigned int gpio)
{
- return gpio_mmio_dir_return(gc, gpio, false);
+ struct gpio_generic_chip *chip = to_gpio_generic_chip(gc);
+ int ret;
+
+ ret = gpio_mmio_dir_return(gc, gpio, false);
+ if (ret)
+ return ret;
+
+ guard(raw_spinlock_irqsave)(&chip->lock);
+ chip->sdir &= ~gpio_mmio_line2mask(gc, gpio);
+
+ return 0;
}
static int gpio_mmio_dir_out_err(struct gpio_chip *gc, unsigned int gpio,
@@ -384,9 +395,19 @@ static int gpio_mmio_dir_out_err(struct gpio_chip *gc, unsigned int gpio,
static int gpio_mmio_simple_dir_out(struct gpio_chip *gc, unsigned int gpio,
int val)
{
+ struct gpio_generic_chip *chip = to_gpio_generic_chip(gc);
+ int ret;
+
gc->set(gc, gpio, val);
- return gpio_mmio_dir_return(gc, gpio, true);
+ ret = gpio_mmio_dir_return(gc, gpio, true);
+ if (ret)
+ return ret;
+
+ guard(raw_spinlock_irqsave)(&chip->lock);
+ chip->sdir |= gpio_mmio_line2mask(gc, gpio);
+
+ return 0;
}
static int gpio_mmio_dir_in(struct gpio_chip *gc, unsigned int gpio)
@@ -601,20 +622,54 @@ static int gpio_mmio_setup_direction(struct gpio_generic_chip *chip,
gc->direction_input = gpio_mmio_dir_in_err;
else
gc->direction_input = gpio_mmio_simple_dir_in;
+
+ if (cfg->flags & GPIO_GENERIC_PINCTRL_BACKEND) {
+ chip->dir_unreadable = true;
+ gc->get_direction = gpio_mmio_get_dir;
+ }
}
return 0;
}
+/*
+ * Without direction registers the direction lives in the pin controller,
+ * so seed the shadow from there when a line is requested.
+ */
+static void gpio_mmio_seed_dir_from_pinctrl(struct gpio_chip *gc,
+ unsigned int gpio)
+{
+ struct gpio_generic_chip *chip = to_gpio_generic_chip(gc);
+ unsigned long config;
+
+ if (!IS_ENABLED(CONFIG_PINCTRL) || chip->reg_dir_out || chip->reg_dir_in)
+ return;
+
+ config = pinconf_to_config_packed(PIN_CONFIG_OUTPUT_ENABLE, 0);
+ if (pinctrl_gpio_get_config(gc, gpio, &config))
+ return;
+
+ guard(raw_spinlock_irqsave)(&chip->lock);
+ if (config)
+ chip->sdir |= gpio_mmio_line2mask(gc, gpio);
+ else
+ chip->sdir &= ~gpio_mmio_line2mask(gc, gpio);
+}
+
static int gpio_mmio_request(struct gpio_chip *gc, unsigned int gpio_pin)
{
struct gpio_generic_chip *chip = to_gpio_generic_chip(gc);
+ int ret;
if (gpio_pin >= gc->ngpio)
return -EINVAL;
- if (chip->pinctrl)
- return gpiochip_generic_request(gc, gpio_pin);
+ if (chip->pinctrl) {
+ ret = gpiochip_generic_request(gc, gpio_pin);
+ if (ret)
+ return ret;
+ gpio_mmio_seed_dir_from_pinctrl(gc, gpio_pin);
+ }
return 0;
}
--
2.54.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [PATCH v2 1/2] pinctrl: imx: answer OUTPUT_ENABLE/INPUT_ENABLE queries from the pad register
2026-09-02 7:39 ` [PATCH v2 1/2] pinctrl: imx: answer OUTPUT_ENABLE/INPUT_ENABLE queries from the pad register Mehmet Fide
@ 2026-09-02 7:56 ` sashiko-bot
2026-09-02 11:08 ` Mehmet Fide
0 siblings, 1 reply; 5+ messages in thread
From: sashiko-bot @ 2026-09-02 7:56 UTC (permalink / raw)
To: Mehmet Fide; +Cc: imx, Frank.Li
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
New issues:
- [High] Dangerous asymmetry between `pin_config_get` (generic) and `pin_config_set` (raw) causes hardware register corruption.
- [Medium] Incomplete transition to generic pinconf API breaks SCU-based platforms.
Pre-existing issues:
- [Critical] Out-of-bounds write during Device Tree parsing in imx_pinctrl_parse_pin_mmio
--
commit 5ebc05bfe4b54b3133759fc4096ed3eedaf8a185
Author: Mehmet Fide <mehmet.fide@screeningeagle.com>
pinctrl: imx: answer OUTPUT_ENABLE/INPUT_ENABLE queries from the pad register
The mmio pinconf get callback ignores which parameter was requested and
returns the raw conf register, so a generic query through
pinctrl_gpio_get_config() gets register bits back instead of the packed
parameter it asked for. Decode the requested parameter and answer
PIN_CONFIG_OUTPUT_ENABLE and PIN_CONFIG_INPUT_ENABLE on SoCs that declare
where those bits live in the pad register.
> diff --git a/drivers/pinctrl/freescale/pinctrl-imx.c b/drivers/pinctrl/freescale/pinctrl-imx.c
> index 9a45b376d36f9..506ee6627c827 100644
> --- a/drivers/pinctrl/freescale/pinctrl-imx.c
> +++ b/drivers/pinctrl/freescale/pinctrl-imx.c
[ ... ]
> @@ -312,6 +313,38 @@ static int imx_pinconf_get_mmio(struct pinctrl_dev *pctldev, unsigned pin_id,
> return 0;
> }
>
> +static int imx_pinconf_get_mmio(struct pinctrl_dev *pctldev,
> + unsigned int pin_id, unsigned long *config)
> +{
> + struct imx_pinctrl *ipctl = pinctrl_dev_get_drvdata(pctldev);
> + const struct imx_pinctrl_soc_info *info = ipctl->info;
> + const struct imx_pin_reg *pin_reg = &ipctl->pin_regs[pin_id];
> + enum pin_config_param param = pinconf_to_config_param(*config);
> + unsigned int mask;
> + u32 raw;
> +
> + /* only the parameters the SoC declares a pad bit for */
> + switch (param) {
> + case PIN_CONFIG_OUTPUT_ENABLE:
> + mask = info->obe_mask;
> + break;
> + case PIN_CONFIG_INPUT_ENABLE:
> + mask = info->ibe_mask;
> + break;
> + default:
> + mask = 0;
> + break;
> + }
> +
> + if (!mask || pin_reg->conf_reg == -1)
> + return -ENOTSUPP;
> +
> + raw = readl(ipctl->base + pin_reg->conf_reg);
> + *config = pinconf_to_config_packed(param, !!(raw & mask));
> +
> + return 0;
> +}
> +
> static int imx_pinconf_get(struct pinctrl_dev *pctldev,
> unsigned pin_id, unsigned long *config)
> {
> @@ -324,6 +357,18 @@ static int imx_pinconf_get(struct pinctrl_dev *pctldev,
> return imx_pinconf_get_mmio(pctldev, pin_id, config);
> }
[Severity: Medium]
Does this bypass the generic decoding logic for SCU platforms?
If a generic caller like pinctrl_gpio_get_config() queries an SCU-based
platform, it appears the SCU path (info->imx_pinconf_get) will overwrite
the generic parameter in *config with a raw hardware register value instead
of unpacking it.
> +static int imx_pinconf_get_raw(struct pinctrl_dev *pctldev,
> + unsigned int pin_id, unsigned long *config)
> +{
> + struct imx_pinctrl *ipctl = pinctrl_dev_get_drvdata(pctldev);
> + const struct imx_pinctrl_soc_info *info = ipctl->info;
> +
> + if (info->flags & IMX_USE_SCU)
> + return info->imx_pinconf_get(pctldev, pin_id, config);
> + else
> + return imx_pinconf_get_raw_mmio(pctldev, pin_id, config);
> +}
> +
> static int imx_pinconf_set_mmio(struct pinctrl_dev *pctldev,
> unsigned pin_id, unsigned long *configs,
> unsigned num_configs)
[Severity: High]
Does this introduce an asymmetry between the get and set callbacks causing
hardware corruption?
While the get callback now unpacks generic parameters, imx_pinconf_set_mmio()
remains completely raw. If a generic caller like pinctrl_gpio_set_config()
passes a packed config (e.g., PIN_CONFIG_LEVEL), will it be blindly written
into the hardware pad configuration register here?
[Severity: Critical]
This is a pre-existing issue, but can the Device Tree parsing cause an
out-of-bounds write here?
Looking at imx_pinctrl_parse_pin_mmio() in drivers/pinctrl/freescale/pinctrl-imx.c:
*pin_id = (mux_reg != -1) ? mux_reg / 4 : conf_reg / 4;
pin_reg = &ipctl->pin_regs[*pin_id];
pin->pin = *pin_id;
pin_reg->mux_reg = mux_reg;
pin_reg->conf_reg = conf_reg;
If a maliciously crafted Device Tree overlay provides an excessively large
mux_reg or conf_reg value, the unvalidated pin_id is used to index into
ipctl->pin_regs, potentially overwriting arbitrary memory.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260902073946.3709080-1-mehmet.fide@gmail.com?part=1
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH v2 1/2] pinctrl: imx: answer OUTPUT_ENABLE/INPUT_ENABLE queries from the pad register
2026-09-02 7:56 ` sashiko-bot
@ 2026-09-02 11:08 ` Mehmet Fide
0 siblings, 0 replies; 5+ messages in thread
From: Mehmet Fide @ 2026-09-02 11:08 UTC (permalink / raw)
To: Bartosz Golaszewski, Linus Walleij
Cc: Dong Aisheng, Fabio Estevam, Frank Li, Jacky Bai, Sascha Hauer,
Pengutronix Kernel Team, imx, linux-gpio, linux-arm-kernel,
linux-kernel, Mehmet Fide, sashiko-bot
From: Mehmet Fide <mehmet.fide@screeningeagle.com>
> New issues:
> - [High] Dangerous asymmetry between `pin_config_get` (generic) and `pin_config_set` (raw) causes hardware register corruption.
> - [Medium] Incomplete transition to generic pinconf API breaks SCU-based platforms.
>
> Pre-existing issues:
> - [Critical] Out-of-bounds write during Device Tree parsing in imx_pinctrl_parse_pin_mmio
[...]
> [Severity: Medium]
> Does this bypass the generic decoding logic for SCU platforms?
>
> If a generic caller like pinctrl_gpio_get_config() queries an SCU-based
> platform, it appears the SCU path (info->imx_pinconf_get) will overwrite
> the generic parameter in *config with a raw hardware register value instead
> of unpacking it.
Yes, that slipped through: the SCU callback returns the raw pad value
like the mmio one did. v3 makes imx_pinconf_get() return -ENOTSUPP for
IMX_USE_SCU SoCs, so a generic query gets "not supported" there instead
of register bits; the debugfs dump keeps reading the raw value through
the SCU path as before.
[...]
> [Severity: High]
> Does this introduce an asymmetry between the get and set callbacks causing
> hardware corruption?
>
> While the get callback now unpacks generic parameters, imx_pinconf_set_mmio()
> remains completely raw. If a generic caller like pinctrl_gpio_set_config()
> passes a packed config (e.g., PIN_CONFIG_LEVEL), will it be blindly written
> into the hardware pad configuration register here?
It would, and it would have before this patch too; the set side is not
changed here. The fsl,pins binding hands pin_config_set the raw pad
register value and that is the only thing it accepts. The one in-tree
route for a packed config into it is gpiochip_generic_config(), and the
GPIO drivers on these pin controllers do not install it as set_config
(gpio-mmio, gpio-vf610, gpio-mxc); gpio-mxc calls it directly only on
the SCU based SoCs, with the SCU wakeup code the SCU set callback
expects, not a generic parameter. Without a set_config the gpiolib bias
and persist helpers stop at -ENOTSUPP before reaching pinctrl. A raw
value and a packed one cannot be told apart in the callback either, so
decoding on the set side means a real conversion of the driver, not a
fix that belongs in this series. v3 states that boundary in the commit
message.
[...]
> [Severity: Critical]
> This is a pre-existing issue, but can the Device Tree parsing cause an
> out-of-bounds write here?
>
> Looking at imx_pinctrl_parse_pin_mmio() in drivers/pinctrl/freescale/pinctrl-imx.c:
>
> *pin_id = (mux_reg != -1) ? mux_reg / 4 : conf_reg / 4;
> pin_reg = &ipctl->pin_regs[*pin_id];
> pin->pin = *pin_id;
> pin_reg->mux_reg = mux_reg;
> pin_reg->conf_reg = conf_reg;
>
> If a maliciously crafted Device Tree overlay provides an excessively large
> mux_reg or conf_reg value, the unvalidated pin_id is used to index into
> ipctl->pin_regs, potentially overwriting arbitrary memory.
Agreed that the index is not checked against info->npins; it has been
like that since the parser was written and this patch does not touch it.
A bounds check with -EINVAL is a one-liner, I will send it as a separate
patch after this series.
Mehmet
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-09-02 11:08 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-02 7:39 [PATCH v2 0/2] gpio: mmio: report the line direction on chips without direction registers Mehmet Fide
2026-09-02 7:39 ` [PATCH v2 1/2] pinctrl: imx: answer OUTPUT_ENABLE/INPUT_ENABLE queries from the pad register Mehmet Fide
2026-09-02 7:56 ` sashiko-bot
2026-09-02 11:08 ` Mehmet Fide
2026-09-02 7:39 ` [PATCH v2 2/2] gpio: mmio: track the direction of chips without direction registers Mehmet Fide
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox