diff options
| author | Kumar Kartikeya Dwivedi <memxor@gmail.com> | 2026-09-04 10:43:19 +0200 |
|---|---|---|
| committer | Alexei Starovoitov <ast@kernel.org> | 2026-09-04 07:58:36 -0700 |
| commit | 6668ed271eaefaa63e686bdfbedaeb7b8e492722 (patch) | |
| tree | 58e8288fead484f6755ad0815fc74c3742663761 /arch/m68k/coldfire/gpio.c | |
| download | linux-stable-6668ed271eaefaa63e686bdfbedaeb7b8e492722.tar.gz linux-stable-6668ed271eaefaa63e686bdfbedaeb7b8e492722.zip | |
selftests/bpf: Reject graph kptr use after RCU unlockgrafted
Add a sleepable verifier test that loads a graph-node local kptr in an
explicit RCU read-side critical section, then passes its node to
bpf_rbtree_remove() after the section ends.
Before the verifier fix, the stale NON_OWN_REF flag makes the node look like
a live borrowed reference and the program is accepted. After the fix, the
pointer is demoted without NON_OWN_REF and the graph kfunc argument is
rejected.
Also exercise a graph kptr loaded while a spin lock provides implicit RCU
protection. The pointer must be invalidated when the lock is released, which
guards the required ordering between non-owning-reference invalidation and
RCU demotion.
Update the existing fault-protected load test state description. The
post-unlock pointer no longer carries NON_OWN_REF, but remains readable
because the load is rewritten to use BPF_PROBE_MEM.
Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
Link: https://lore.kernel.org/r/20260904084325.52250-7-memxor@gmail.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Diffstat (limited to 'arch/m68k/coldfire/gpio.c')
| -rw-r--r-- | arch/m68k/coldfire/gpio.c | 175 |
1 files changed, 175 insertions, 0 deletions
diff --git a/arch/m68k/coldfire/gpio.c b/arch/m68k/coldfire/gpio.c new file mode 100644 index 000000000..e2f7af1fa --- /dev/null +++ b/arch/m68k/coldfire/gpio.c @@ -0,0 +1,175 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Coldfire generic GPIO support. + * + * (C) Copyright 2009, Steven King <sfking@fdwdc.com> + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/device.h> +#include <linux/gpio/driver.h> + +#include <linux/io.h> +#include <asm/coldfire.h> +#include <asm/mcfsim.h> +#include <asm/mcfgpio.h> + +int __mcfgpio_get_value(unsigned gpio) +{ + return mcfgpio_read(__mcfgpio_ppdr(gpio)) & mcfgpio_bit(gpio); +} +EXPORT_SYMBOL(__mcfgpio_get_value); + +void __mcfgpio_set_value(unsigned gpio, int value) +{ + if (gpio < MCFGPIO_SCR_START) { + unsigned long flags; + MCFGPIO_PORTTYPE data; + + local_irq_save(flags); + data = mcfgpio_read(__mcfgpio_podr(gpio)); + if (value) + data |= mcfgpio_bit(gpio); + else + data &= ~mcfgpio_bit(gpio); + mcfgpio_write(data, __mcfgpio_podr(gpio)); + local_irq_restore(flags); + } else { + if (value) + mcfgpio_write(mcfgpio_bit(gpio), + MCFGPIO_SETR_PORT(gpio)); + else + mcfgpio_write(~mcfgpio_bit(gpio), + MCFGPIO_CLRR_PORT(gpio)); + } +} +EXPORT_SYMBOL(__mcfgpio_set_value); + +int __mcfgpio_direction_input(unsigned gpio) +{ + unsigned long flags; + MCFGPIO_PORTTYPE dir; + + local_irq_save(flags); + dir = mcfgpio_read(__mcfgpio_pddr(gpio)); + dir &= ~mcfgpio_bit(gpio); + mcfgpio_write(dir, __mcfgpio_pddr(gpio)); + local_irq_restore(flags); + + return 0; +} +EXPORT_SYMBOL(__mcfgpio_direction_input); + +int __mcfgpio_direction_output(unsigned gpio, int value) +{ + unsigned long flags; + MCFGPIO_PORTTYPE data; + + local_irq_save(flags); + data = mcfgpio_read(__mcfgpio_pddr(gpio)); + data |= mcfgpio_bit(gpio); + mcfgpio_write(data, __mcfgpio_pddr(gpio)); + + /* now set the data to output */ + if (gpio < MCFGPIO_SCR_START) { + data = mcfgpio_read(__mcfgpio_podr(gpio)); + if (value) + data |= mcfgpio_bit(gpio); + else + data &= ~mcfgpio_bit(gpio); + mcfgpio_write(data, __mcfgpio_podr(gpio)); + } else { + if (value) + mcfgpio_write(mcfgpio_bit(gpio), + MCFGPIO_SETR_PORT(gpio)); + else + mcfgpio_write(~mcfgpio_bit(gpio), + MCFGPIO_CLRR_PORT(gpio)); + } + local_irq_restore(flags); + return 0; +} +EXPORT_SYMBOL(__mcfgpio_direction_output); + +int __mcfgpio_request(unsigned gpio) +{ + return 0; +} +EXPORT_SYMBOL(__mcfgpio_request); + +void __mcfgpio_free(unsigned gpio) +{ + __mcfgpio_direction_input(gpio); +} +EXPORT_SYMBOL(__mcfgpio_free); + +#ifdef CONFIG_GPIOLIB + +static int mcfgpio_direction_input(struct gpio_chip *chip, unsigned offset) +{ + return __mcfgpio_direction_input(offset); +} + +static int mcfgpio_get_value(struct gpio_chip *chip, unsigned offset) +{ + return !!__mcfgpio_get_value(offset); +} + +static int mcfgpio_direction_output(struct gpio_chip *chip, unsigned offset, + int value) +{ + return __mcfgpio_direction_output(offset, value); +} + +static int mcfgpio_set_value(struct gpio_chip *chip, unsigned int offset, + int value) +{ + __mcfgpio_set_value(offset, value); + + return 0; +} + +static int mcfgpio_request(struct gpio_chip *chip, unsigned offset) +{ + return __mcfgpio_request(offset); +} + +static void mcfgpio_free(struct gpio_chip *chip, unsigned offset) +{ + __mcfgpio_free(offset); +} + +static int mcfgpio_to_irq(struct gpio_chip *chip, unsigned offset) +{ +#if defined(MCFGPIO_IRQ_MIN) + if ((offset >= MCFGPIO_IRQ_MIN) && (offset < MCFGPIO_IRQ_MAX)) +#else + if (offset < MCFGPIO_IRQ_MAX) +#endif + return MCFGPIO_IRQ_VECBASE + offset; + else + return -EINVAL; +} + +static struct gpio_chip mcfgpio_chip = { + .label = "mcfgpio", + .request = mcfgpio_request, + .free = mcfgpio_free, + .direction_input = mcfgpio_direction_input, + .direction_output = mcfgpio_direction_output, + .get = mcfgpio_get_value, + .set = mcfgpio_set_value, + .to_irq = mcfgpio_to_irq, + .base = 0, + .ngpio = MCFGPIO_PIN_MAX, +}; + +static int __init mcfgpio_sysinit(void) +{ + return gpiochip_add_data(&mcfgpio_chip, NULL); +} + +core_initcall(mcfgpio_sysinit); +#endif |
