From a5f7a5bb3b7f28ba7e4fa246775b29a0e5537255 Mon Sep 17 00:00:00 2001 From: Jinke Han Date: Tue, 8 Sep 2026 15:37:42 +0800 Subject: x86/kprobes: Fix crash when probing CS CALL instructions When using eBPF to probe CS CALL instructions within a function, a crash can be triggered. The eBPF tool probes offset 257 of the __hrtimer_run_queues() function: <__hrtimer_run_queues+249>: nopl 0x0(%rax,%rax,1) <__hrtimer_run_queues+254>: mov %r14,%rdi <__hrtimer_run_queues+257>: cs call <__x86_indirect_thunk_r12> <__hrtimer_run_queues+263>: mov %eax,%r12d <__hrtimer_run_queues+266>: xchg %ax,%ax <__hrtimer_run_queues+268>: mov %r13,%rdi Which triggers this crash: BUG: unable to handle page fault for address: 00000000000f41c9 #PF: supervisor write access in kernel mode #PF: error_code(0x0002) - not-present page PGD 0 P4D 0 Oops: 0002 [#1] SMP NOPTI CPU: 1 PID: 0 Comm: swapper/1 Kdump: loaded Tainted: P RIP: 0010:__hrtimer_run_queues+0x106/0x230 Note that __hrtimer_run_queues+0x106 is __hrtimer_run_queues+262, which is at the 6th byte of the above CS CALL instruction. Since the CS CALL instruction occupies 6 bytes, the exception occurred in the middle of that call instruction. The root cause is that when using eBPF tools to probe in the middle of a function, a kprobe with INT3 is used as the underlying implementation. During single-step emulation of the original CALL instruction, int3_emulate_call() assumes that the probed CALL instruction is 5 bytes long. However, the actual CS-prefixed CALL instruction occupies 6 bytes, so it constructs an incorrect exception return address. When the CPU returns from the kprobe handler, the next instruction to be executed is at the address of the last byte of that CS CALL instruction. Coincidentally, starting from that address, the CPU fetches and decodes a completely different instruction, which ultimately triggers a kernel crash. Fix the issue by using the actual instruction length obtained from the instruction decoder when constructing the exception return address, rather than relying on the hardcoded CALL_INSN_SIZE macro. [ mingo: Refined the changelog ] Fixes: 6256e668b7af ("x86/kprobes: Use int3 instead of debug trap for single-step") Suggested-by: Masami Hiramatsu (Google) Signed-off-by: Jinke Han Signed-off-by: Ingo Molnar Reviewed-by: Masami Hiramatsu (Google) Acked-by: Yafang Shao Acked-by: Borislav Petkov Cc: Peter Zijlstra Link: https://patch.msgid.link/20260908073742.GA10517@didi-ThinkCentre-M920t-N000 --- drivers/gpu/drm/sitronix/Kconfig | 80 +++ drivers/gpu/drm/sitronix/Makefile | 6 + drivers/gpu/drm/sitronix/st7571-i2c.c | 158 ++++++ drivers/gpu/drm/sitronix/st7571-spi.c | 76 +++ drivers/gpu/drm/sitronix/st7571.c | 918 ++++++++++++++++++++++++++++++++++ drivers/gpu/drm/sitronix/st7571.h | 91 ++++ drivers/gpu/drm/sitronix/st7586.c | 498 ++++++++++++++++++ drivers/gpu/drm/sitronix/st7735r.c | 378 ++++++++++++++ drivers/gpu/drm/sitronix/st7920.c | 867 ++++++++++++++++++++++++++++++++ 9 files changed, 3072 insertions(+) create mode 100644 drivers/gpu/drm/sitronix/Kconfig create mode 100644 drivers/gpu/drm/sitronix/Makefile create mode 100644 drivers/gpu/drm/sitronix/st7571-i2c.c create mode 100644 drivers/gpu/drm/sitronix/st7571-spi.c create mode 100644 drivers/gpu/drm/sitronix/st7571.c create mode 100644 drivers/gpu/drm/sitronix/st7571.h create mode 100644 drivers/gpu/drm/sitronix/st7586.c create mode 100644 drivers/gpu/drm/sitronix/st7735r.c create mode 100644 drivers/gpu/drm/sitronix/st7920.c (limited to 'drivers/gpu/drm/sitronix') diff --git a/drivers/gpu/drm/sitronix/Kconfig b/drivers/gpu/drm/sitronix/Kconfig new file mode 100644 index 000000000..41a428ef8 --- /dev/null +++ b/drivers/gpu/drm/sitronix/Kconfig @@ -0,0 +1,80 @@ +config DRM_ST7571 + tristate "DRM support for Sitronix ST7567/ST7571 display panels" + depends on DRM && MMU + select DRM_CLIENT_SELECTION + select DRM_GEM_SHMEM_HELPER + select DRM_KMS_HELPER + select VIDEOMODE_HELPERS + help + Sitronix ST7571 is a driver and controller for 4-level gray + scale and monochrome dot matrix LCD panels. + + DRM driver for Sitronix ST7567/ST7571 panels. + This is only the core driver, a driver for the appropriate bus + transport in your chip also must be selected. + + if M is selected the module will be called st7571. + +config DRM_ST7571_I2C + tristate "DRM support for Sitronix ST7567/ST7571 display panels (I2C)" + depends on DRM_ST7571 && I2C + select REGMAP + help + Sitronix ST7571 is a driver and controller for 4-level gray + scale and monochrome dot matrix LCD panels. + + DRM driver for Sitronix ST7565/ST7571 panels connected via I2C bus. + + if M is selected the module will be called st7571-i2c. + +config DRM_ST7571_SPI + tristate "DRM support for Sitronix ST7567/ST7571 display panels (SPI)" + depends on DRM_ST7571 && SPI + select REGMAP_SPI + help + Sitronix ST7571 is a driver and controller for 4-level gray + scale and monochrome dot matrix LCD panels. + + DRM driver for Sitronix ST7565/ST7571 panels connected via SPI bus. + + if M is selected the module will be called st7571-spi. + +config DRM_ST7586 + tristate "DRM support for Sitronix ST7586 display panels" + depends on DRM && SPI + select DRM_CLIENT_SELECTION + select DRM_KMS_HELPER + select DRM_GEM_DMA_HELPER + select DRM_MIPI_DBI + help + DRM driver for the following Sitronix ST7586 panels: + * LEGO MINDSTORMS EV3 + + If M is selected the module will be called st7586. + +config DRM_ST7735R + tristate "DRM support for Sitronix ST7715R/ST7735R display panels" + depends on DRM && SPI + select DRM_CLIENT_SELECTION + select DRM_KMS_HELPER + select DRM_GEM_DMA_HELPER + select DRM_MIPI_DBI + select BACKLIGHT_CLASS_DEVICE + help + DRM driver for Sitronix ST7715R/ST7735R with one of the following + LCDs: + * Jianda JD-T18003-T01 1.8" 128x160 TFT + * Okaya RH128128T 1.44" 128x128 TFT + + If M is selected the module will be called st7735r. + +config DRM_ST7920 + tristate "DRM support for Sitronix ST7920 LCD displays" + depends on DRM && SPI && MMU + select DRM_GEM_SHMEM_HELPER + select DRM_KMS_HELPER + select REGMAP_SPI + help + DRM driver for the ST7920 Sitronix LCD controllers. + + If M is selected the module will be called st7920. diff --git a/drivers/gpu/drm/sitronix/Makefile b/drivers/gpu/drm/sitronix/Makefile new file mode 100644 index 000000000..d03beff37 --- /dev/null +++ b/drivers/gpu/drm/sitronix/Makefile @@ -0,0 +1,6 @@ +obj-$(CONFIG_DRM_ST7571) += st7571.o +obj-$(CONFIG_DRM_ST7571_I2C) += st7571-i2c.o +obj-$(CONFIG_DRM_ST7571_SPI) += st7571-spi.o +obj-$(CONFIG_DRM_ST7586) += st7586.o +obj-$(CONFIG_DRM_ST7735R) += st7735r.o +obj-$(CONFIG_DRM_ST7920) += st7920.o diff --git a/drivers/gpu/drm/sitronix/st7571-i2c.c b/drivers/gpu/drm/sitronix/st7571-i2c.c new file mode 100644 index 000000000..44bc94be3 --- /dev/null +++ b/drivers/gpu/drm/sitronix/st7571-i2c.c @@ -0,0 +1,158 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Driver for Sitronix ST7571 connected via I2C bus. + * + * Copyright (C) 2025 Marcus Folkesson + */ + +#include +#include +#include + +#include "st7571.h" + +struct st7571_i2c_transport { + struct i2c_client *client; + + /* + * Depending on the hardware design, the acknowledge signal may be hard to + * recognize as a valid logic "0" level. + * Therefor, ignore NAK if possible to stay compatible with most hardware designs + * and off-the-shelf panels out there. + * + * From section 6.4 MICROPOCESSOR INTERFACE section in the datasheet: + * + * "By connecting SDA_OUT to SDA_IN externally, the SDA line becomes fully + * I2C interface compatible. + * Separating acknowledge-output from serial data + * input is advantageous for chip-on-glass (COG) applications. In COG + * applications, the ITO resistance and the pull-up resistor will form a + * voltage divider, which affects acknowledge-signal level. Larger ITO + * resistance will raise the acknowledged-signal level and system cannot + * recognize this level as a valid logic “0” level. By separating SDA_IN from + * SDA_OUT, the IC can be used in a mode that ignores the acknowledge-bit. + * For applications which check acknowledge-bit, it is necessary to minimize + * the ITO resistance of the SDA_OUT trace to guarantee a valid low level." + * + */ + bool ignore_nak; +}; + +static int st7571_i2c_regmap_write(void *context, const void *data, size_t count) +{ + struct st7571_i2c_transport *t = context; + int ret; + + struct i2c_msg msg = { + .addr = t->client->addr, + .flags = t->ignore_nak ? I2C_M_IGNORE_NAK : 0, + .len = count, + .buf = (u8 *)data + }; + + ret = i2c_transfer(t->client->adapter, &msg, 1); + + /* + * Unfortunately, there is no way to check if the transfer failed because of + * a NAK or something else as I2C bus drivers use different return values for NAK. + * + * However, if the transfer fails and ignore_nak is set, we know it is an error. + */ + if (ret < 0 && t->ignore_nak) + return ret; + + return 0; +} + +/* The st7571 driver does not read registers but regmap expects a .read */ +static int st7571_i2c_regmap_read(void *context, const void *reg_buf, + size_t reg_size, void *val_buf, size_t val_size) +{ + return -EOPNOTSUPP; +} + +static const struct regmap_bus st7571_i2c_regmap_bus = { + .read = st7571_i2c_regmap_read, + .write = st7571_i2c_regmap_write, +}; + +static const struct regmap_config st7571_i2c_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .use_single_write = true, +}; + +static int st7571_i2c_probe(struct i2c_client *client) +{ + struct st7571_device *st7571; + struct st7571_i2c_transport *t; + struct regmap *regmap; + + t = devm_kzalloc(&client->dev, sizeof(*t), GFP_KERNEL); + if (!t) + return -ENOMEM; + + t->client = client; + + /* + * The hardware design could make it hard to detect a NAK on the I2C bus. + * If the adapter does not support protocol mangling do + * not set the I2C_M_IGNORE_NAK flag at the expense * of possible + * cruft in the logs. + */ + if (i2c_check_functionality(client->adapter, I2C_FUNC_PROTOCOL_MANGLING)) + t->ignore_nak = true; + + regmap = devm_regmap_init(&client->dev, &st7571_i2c_regmap_bus, + t, &st7571_i2c_regmap_config); + if (IS_ERR(regmap)) { + return dev_err_probe(&client->dev, PTR_ERR(regmap), + "Failed to initialize regmap\n"); + } + + st7571 = st7571_probe(&client->dev, regmap); + if (IS_ERR(st7571)) + return dev_err_probe(&client->dev, PTR_ERR(st7571), + "Failed to initialize regmap\n"); + + i2c_set_clientdata(client, st7571); + return 0; +} + +static void st7571_i2c_remove(struct i2c_client *client) +{ + struct st7571_device *st7571 = i2c_get_clientdata(client); + + st7571_remove(st7571); +} + +static const struct of_device_id st7571_of_match[] = { + { .compatible = "sitronix,st7567", .data = &st7567_config }, + { .compatible = "sitronix,st7571", .data = &st7571_config }, + {}, +}; +MODULE_DEVICE_TABLE(of, st7571_of_match); + +static const struct i2c_device_id st7571_id[] = { + { "st7567", 0 }, + { "st7571", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, st7571_id); + +static struct i2c_driver st7571_i2c_driver = { + .driver = { + .name = "st7571-i2c", + .of_match_table = st7571_of_match, + }, + .probe = st7571_i2c_probe, + .remove = st7571_i2c_remove, + .id_table = st7571_id, +}; + +module_i2c_driver(st7571_i2c_driver); + +MODULE_AUTHOR("Marcus Folkesson "); +MODULE_DESCRIPTION("DRM Driver for Sitronix ST7571 LCD controller (I2C)"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("DRM_ST7571"); diff --git a/drivers/gpu/drm/sitronix/st7571-spi.c b/drivers/gpu/drm/sitronix/st7571-spi.c new file mode 100644 index 000000000..0206e9162 --- /dev/null +++ b/drivers/gpu/drm/sitronix/st7571-spi.c @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Driver for Sitronix ST7571 connected via SPI bus. + * + * Copyright (C) 2025 Marcus Folkesson + */ + +#include +#include +#include + +#include "st7571.h" + +static const struct regmap_config st7571_spi_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .can_multi_write = true, +}; + +static int st7571_spi_probe(struct spi_device *spi) +{ + struct st7571_device *st7571; + struct regmap *regmap; + + regmap = devm_regmap_init_spi(spi, &st7571_spi_regmap_config); + if (IS_ERR(regmap)) { + return dev_err_probe(&spi->dev, PTR_ERR(regmap), + "Failed to initialize regmap\n"); + } + + st7571 = st7571_probe(&spi->dev, regmap); + if (IS_ERR(st7571)) + return dev_err_probe(&spi->dev, PTR_ERR(st7571), + "Failed to initialize regmap\n"); + + spi_set_drvdata(spi, st7571); + return 0; +} + +static void st7571_spi_remove(struct spi_device *spi) +{ + struct st7571_device *st7571 = spi_get_drvdata(spi); + + st7571_remove(st7571); +} + +static const struct of_device_id st7571_of_match[] = { + { .compatible = "sitronix,st7567", .data = &st7567_config }, + { .compatible = "sitronix,st7571", .data = &st7571_config }, + {}, +}; +MODULE_DEVICE_TABLE(of, st7571_of_match); + +static const struct spi_device_id st7571_spi_id[] = { + { "st7567", 0 }, + { "st7571", 0 }, + { } +}; +MODULE_DEVICE_TABLE(spi, st7571_spi_id); + +static struct spi_driver st7571_spi_driver = { + .driver = { + .name = "st7571-spi", + .of_match_table = st7571_of_match, + }, + .probe = st7571_spi_probe, + .remove = st7571_spi_remove, + .id_table = st7571_spi_id, +}; + +module_spi_driver(st7571_spi_driver); + +MODULE_AUTHOR("Marcus Folkesson "); +MODULE_DESCRIPTION("DRM Driver for Sitronix ST7571 LCD controller (SPI)"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("DRM_ST7571"); diff --git a/drivers/gpu/drm/sitronix/st7571.c b/drivers/gpu/drm/sitronix/st7571.c new file mode 100644 index 000000000..20954c33e --- /dev/null +++ b/drivers/gpu/drm/sitronix/st7571.c @@ -0,0 +1,918 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Driver for Sitronix ST7571, a 4 level gray scale dot matrix LCD controller + * + * Copyright (C) 2025 Marcus Folkesson + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include