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/sysfb/Kconfig | 89 ++ drivers/gpu/drm/sysfb/Makefile | 13 + drivers/gpu/drm/sysfb/corebootdrm.c | 455 ++++++++++ drivers/gpu/drm/sysfb/drm_sysfb.c | 59 ++ drivers/gpu/drm/sysfb/drm_sysfb_helper.h | 209 +++++ drivers/gpu/drm/sysfb/drm_sysfb_modeset.c | 608 +++++++++++++ drivers/gpu/drm/sysfb/drm_sysfb_screen_info.c | 81 ++ drivers/gpu/drm/sysfb/efidrm.c | 434 +++++++++ drivers/gpu/drm/sysfb/ofdrm.c | 1170 +++++++++++++++++++++++++ drivers/gpu/drm/sysfb/simpledrm.c | 972 ++++++++++++++++++++ drivers/gpu/drm/sysfb/vesadrm.c | 679 ++++++++++++++ 11 files changed, 4769 insertions(+) create mode 100644 drivers/gpu/drm/sysfb/Kconfig create mode 100644 drivers/gpu/drm/sysfb/Makefile create mode 100644 drivers/gpu/drm/sysfb/corebootdrm.c create mode 100644 drivers/gpu/drm/sysfb/drm_sysfb.c create mode 100644 drivers/gpu/drm/sysfb/drm_sysfb_helper.h create mode 100644 drivers/gpu/drm/sysfb/drm_sysfb_modeset.c create mode 100644 drivers/gpu/drm/sysfb/drm_sysfb_screen_info.c create mode 100644 drivers/gpu/drm/sysfb/efidrm.c create mode 100644 drivers/gpu/drm/sysfb/ofdrm.c create mode 100644 drivers/gpu/drm/sysfb/simpledrm.c create mode 100644 drivers/gpu/drm/sysfb/vesadrm.c (limited to 'drivers/gpu/drm/sysfb') diff --git a/drivers/gpu/drm/sysfb/Kconfig b/drivers/gpu/drm/sysfb/Kconfig new file mode 100644 index 000000000..f7e481788 --- /dev/null +++ b/drivers/gpu/drm/sysfb/Kconfig @@ -0,0 +1,89 @@ +# SPDX-License-Identifier: GPL-2.0-only + +menu "Drivers for system framebuffers" + depends on DRM + +config DRM_SYSFB_HELPER + tristate + depends on DRM + +config DRM_COREBOOTDRM + tristate "Coreboot framebuffer driver" + depends on DRM && MMU + depends on GOOGLE_FRAMEBUFFER_COREBOOT + select APERTURE_HELPERS + select DRM_CLIENT_SELECTION + select DRM_GEM_SHMEM_HELPER + select DRM_KMS_HELPER + select DRM_SYSFB_HELPER + help + DRM driver for coreboot-provided framebuffers. + + This driver assumes that the display hardware has been initialized + by coreboot firmware before the kernel boots. Scanout buffer, size, + and display format must be provided via coreboot framebuffer device. + +config DRM_EFIDRM + tristate "EFI framebuffer driver" + depends on DRM && MMU && EFI && (!SYSFB_SIMPLEFB || COMPILE_TEST) + select APERTURE_HELPERS + select DRM_CLIENT_SELECTION + select DRM_GEM_SHMEM_HELPER + select DRM_KMS_HELPER + select DRM_SYSFB_HELPER + select SYSFB + help + DRM driver for EFI framebuffers. + + This driver assumes that the display hardware has been initialized + by the firmware or bootloader before the kernel boots. Scanout + buffer, size, and display format must be provided via EFI interfaces. + +config DRM_OFDRM + tristate "Open Firmware display driver" + depends on DRM && MMU && OF && (PPC || COMPILE_TEST) + select APERTURE_HELPERS + select DRM_CLIENT_SELECTION + select DRM_GEM_SHMEM_HELPER + select DRM_KMS_HELPER + select DRM_SYSFB_HELPER + help + DRM driver for Open Firmware framebuffers. + + This driver assumes that the display hardware has been initialized + by the Open Firmware before the kernel boots. Scanout buffer, size, + and display format must be provided via device tree. + +config DRM_SIMPLEDRM + tristate "Simple framebuffer driver" + depends on DRM && MMU + select APERTURE_HELPERS + select DRM_CLIENT_SELECTION + select DRM_GEM_SHMEM_HELPER + select DRM_KMS_HELPER + select DRM_SYSFB_HELPER + help + DRM driver for simple platform-provided framebuffers. + + This driver assumes that the display hardware has been initialized + by the firmware or bootloader before the kernel boots. Scanout + buffer, size, and display format must be provided via device tree's + simple-framebuffer node. + +config DRM_VESADRM + tristate "VESA framebuffer driver" + depends on DRM && MMU && X86 && (!SYSFB_SIMPLEFB || COMPILE_TEST) + select APERTURE_HELPERS + select DRM_CLIENT_SELECTION + select DRM_GEM_SHMEM_HELPER + select DRM_KMS_HELPER + select DRM_SYSFB_HELPER + select SYSFB + help + DRM driver for VESA framebuffers. + + This driver assumes that the display hardware has been initialized + by the firmware or bootloader before the kernel boots. Scanout + buffer, size, and display format must be provided via VBE interfaces. + +endmenu diff --git a/drivers/gpu/drm/sysfb/Makefile b/drivers/gpu/drm/sysfb/Makefile new file mode 100644 index 000000000..85c9087ab --- /dev/null +++ b/drivers/gpu/drm/sysfb/Makefile @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: GPL-2.0-only + +drm_sysfb_helper-y := \ + drm_sysfb.o \ + drm_sysfb_modeset.o +drm_sysfb_helper-$(CONFIG_SCREEN_INFO) += drm_sysfb_screen_info.o +obj-$(CONFIG_DRM_SYSFB_HELPER) += drm_sysfb_helper.o + +obj-$(CONFIG_DRM_COREBOOTDRM) += corebootdrm.o +obj-$(CONFIG_DRM_EFIDRM) += efidrm.o +obj-$(CONFIG_DRM_OFDRM) += ofdrm.o +obj-$(CONFIG_DRM_SIMPLEDRM) += simpledrm.o +obj-$(CONFIG_DRM_VESADRM) += vesadrm.o diff --git a/drivers/gpu/drm/sysfb/corebootdrm.c b/drivers/gpu/drm/sysfb/corebootdrm.c new file mode 100644 index 000000000..853da9a60 --- /dev/null +++ b/drivers/gpu/drm/sysfb/corebootdrm.c @@ -0,0 +1,455 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "drm_sysfb_helper.h" + +#define DRIVER_NAME "corebootdrm" +#define DRIVER_DESC "DRM driver for Coreboot framebuffers" +#define DRIVER_MAJOR 1 +#define DRIVER_MINOR 0 + +static const struct drm_format_info * +corebootdrm_get_format_fb(struct drm_device *dev, const struct lb_framebuffer *fb) +{ + static const struct drm_sysfb_format formats[] = { + { PIXEL_FORMAT_XRGB1555, DRM_FORMAT_XRGB1555, }, + { PIXEL_FORMAT_RGB565, DRM_FORMAT_RGB565, }, + { PIXEL_FORMAT_RGB888, DRM_FORMAT_RGB888, }, + { PIXEL_FORMAT_XRGB8888, DRM_FORMAT_XRGB8888, }, + { PIXEL_FORMAT_XBGR8888, DRM_FORMAT_XBGR8888, }, + { PIXEL_FORMAT_XRGB2101010, DRM_FORMAT_XRGB2101010, }, + }; + const struct pixel_format pixel = { + .bits_per_pixel = fb->bits_per_pixel, + .indexed = false, + .alpha = { + .offset = 0, + .length = 0, + }, + .red = { + .offset = fb->red_mask_pos, + .length = fb->red_mask_size, + }, + .green = { + .offset = fb->green_mask_pos, + .length = fb->green_mask_size, + }, + .blue = { + .offset = fb->blue_mask_pos, + .length = fb->blue_mask_size, + }, + }; + + return drm_sysfb_get_format(dev, formats, ARRAY_SIZE(formats), &pixel); +} + +static int corebootdrm_get_width_fb(struct drm_device *dev, const struct lb_framebuffer *fb) +{ + return drm_sysfb_get_validated_int0(dev, "width", fb->x_resolution, INT_MAX); +} + +static int corebootdrm_get_height_fb(struct drm_device *dev, const struct lb_framebuffer *fb) +{ + return drm_sysfb_get_validated_int0(dev, "height", fb->y_resolution, INT_MAX); +} + +static int corebootdrm_get_pitch_fb(struct drm_device *dev, const struct drm_format_info *format, + unsigned int width, const struct lb_framebuffer *fb) +{ + u64 bytes_per_line = fb->bytes_per_line; + + if (!bytes_per_line) + bytes_per_line = drm_format_info_min_pitch(format, 0, width); + + return drm_sysfb_get_validated_int0(dev, "pitch", bytes_per_line, INT_MAX); +} + +static resource_size_t corebootdrm_get_size_fb(struct drm_device *dev, unsigned int height, + unsigned int pitch, + const struct lb_framebuffer *fb) +{ + resource_size_t size; + + if (check_mul_overflow(height, pitch, &size)) + return 0; + + return size; +} + +static phys_addr_t corebootdrm_get_address_fb(struct drm_device *dev, resource_size_t size, + const struct lb_framebuffer *fb) +{ + if (size > PHYS_ADDR_MAX) + return 0; + if (!fb->physical_address) + return 0; + if (fb->physical_address > (PHYS_ADDR_MAX - size)) + return 0; + + return fb->physical_address; +} + +static enum drm_panel_orientation corebootdrm_get_orientation_fb(struct drm_device *dev, + const struct lb_framebuffer *fb) +{ + if (!LB_FRAMEBUFFER_HAS_ORIENTATION(fb)) + return DRM_MODE_PANEL_ORIENTATION_UNKNOWN; + + switch (fb->orientation) { + case LB_FRAMEBUFFER_ORIENTATION_NORMAL: + return DRM_MODE_PANEL_ORIENTATION_NORMAL; + case LB_FRAMEBUFFER_ORIENTATION_BOTTOM_UP: + return DRM_MODE_PANEL_ORIENTATION_BOTTOM_UP; + case LB_FRAMEBUFFER_ORIENTATION_LEFT_UP: + return DRM_MODE_PANEL_ORIENTATION_LEFT_UP; + case LB_FRAMEBUFFER_ORIENTATION_RIGHT_UP: + return DRM_MODE_PANEL_ORIENTATION_RIGHT_UP; + } + + return DRM_MODE_PANEL_ORIENTATION_UNKNOWN; +} + +/* + * Simple Framebuffer device + */ + +struct corebootdrm_device { + struct drm_sysfb_device sysfb; + + /* modesetting */ + u32 formats[DRM_SYSFB_PLANE_NFORMATS(1)]; + struct drm_plane primary_plane; + struct drm_crtc crtc; + struct drm_encoder encoder; + struct drm_connector connector; +}; + +/* + * Modesetting + */ + +static const u64 corebootdrm_primary_plane_format_modifiers[] = { + DRM_SYSFB_PLANE_FORMAT_MODIFIERS, +}; + +static const struct drm_plane_helper_funcs corebootdrm_primary_plane_helper_funcs = { + DRM_SYSFB_PLANE_HELPER_FUNCS, +}; + +static const struct drm_plane_funcs corebootdrm_primary_plane_funcs = { + DRM_SYSFB_PLANE_FUNCS, + .destroy = drm_plane_cleanup, +}; + +static const struct drm_crtc_helper_funcs corebootdrm_crtc_helper_funcs = { + DRM_SYSFB_CRTC_HELPER_FUNCS, +}; + +static const struct drm_crtc_funcs corebootdrm_crtc_funcs = { + DRM_SYSFB_CRTC_FUNCS, + .destroy = drm_crtc_cleanup, +}; + +static const struct drm_encoder_funcs corebootdrm_encoder_funcs = { + .destroy = drm_encoder_cleanup, +}; + +static const struct drm_connector_helper_funcs corebootdrm_connector_helper_funcs = { + DRM_SYSFB_CONNECTOR_HELPER_FUNCS, +}; + +static const struct drm_connector_funcs corebootdrm_connector_funcs = { + DRM_SYSFB_CONNECTOR_FUNCS, + .destroy = drm_connector_cleanup, +}; + +static const struct drm_mode_config_funcs corebootdrm_mode_config_funcs = { + DRM_SYSFB_MODE_CONFIG_FUNCS, +}; + +static int corebootdrm_mode_config_init(struct corebootdrm_device *cdev, + enum drm_panel_orientation orientation) +{ + struct drm_sysfb_device *sysfb = &cdev->sysfb; + struct drm_device *dev = &sysfb->dev; + const struct drm_format_info *format = sysfb->fb_format; + unsigned int width = sysfb->fb_mode.hdisplay; + unsigned int height = sysfb->fb_mode.vdisplay; + struct drm_plane *primary_plane; + struct drm_crtc *crtc; + struct drm_encoder *encoder; + struct drm_connector *connector; + size_t nformats; + int ret; + + ret = drmm_mode_config_init(dev); + if (ret) + return ret; + + dev->mode_config.min_width = width; + dev->mode_config.max_width = max_t(unsigned int, width, DRM_SHADOW_PLANE_MAX_WIDTH); + dev->mode_config.min_height = height; + dev->mode_config.max_height = max_t(unsigned int, height, DRM_SHADOW_PLANE_MAX_HEIGHT); + dev->mode_config.funcs = &corebootdrm_mode_config_funcs; + dev->mode_config.preferred_depth = format->depth; + + /* Primary plane */ + + nformats = drm_sysfb_build_fourcc_list(dev, &format->format, 1, + cdev->formats, ARRAY_SIZE(cdev->formats)); + + primary_plane = &cdev->primary_plane; + ret = drm_universal_plane_init(dev, primary_plane, 0, &corebootdrm_primary_plane_funcs, + cdev->formats, nformats, + corebootdrm_primary_plane_format_modifiers, + DRM_PLANE_TYPE_PRIMARY, NULL); + if (ret) + return ret; + drm_plane_helper_add(primary_plane, &corebootdrm_primary_plane_helper_funcs); + drm_plane_enable_fb_damage_clips(primary_plane); + + /* CRTC */ + + crtc = &cdev->crtc; + ret = drm_crtc_init_with_planes(dev, crtc, primary_plane, NULL, + &corebootdrm_crtc_funcs, NULL); + if (ret) + return ret; + drm_crtc_helper_add(crtc, &corebootdrm_crtc_helper_funcs); + + /* Encoder */ + + encoder = &cdev->encoder; + ret = drm_encoder_init(dev, encoder, &corebootdrm_encoder_funcs, + DRM_MODE_ENCODER_NONE, NULL); + if (ret) + return ret; + encoder->possible_crtcs = drm_crtc_mask(crtc); + + /* Connector */ + + connector = &cdev->connector; + ret = drm_connector_init(dev, connector, &corebootdrm_connector_funcs, + DRM_MODE_CONNECTOR_Unknown); + if (ret) + return ret; + drm_connector_helper_add(connector, &corebootdrm_connector_helper_funcs); + drm_connector_set_panel_orientation_with_quirk(connector, orientation, + width, height); + + ret = drm_connector_attach_encoder(connector, encoder); + if (ret) + return ret; + + return 0; +} + +/* + * DRM driver + */ + +DEFINE_DRM_GEM_FOPS(corebootdrm_fops); + +static struct drm_driver corebootdrm_drm_driver = { + DRM_GEM_SHMEM_DRIVER_OPS, + DRM_FBDEV_SHMEM_DRIVER_OPS, + .name = DRIVER_NAME, + .desc = DRIVER_DESC, + .major = DRIVER_MAJOR, + .minor = DRIVER_MINOR, + .driver_features = DRIVER_ATOMIC | DRIVER_GEM | DRIVER_MODESET, + .fops = &corebootdrm_fops, +}; + +/* + * Coreboot driver + */ + +static int corebootdrm_pm_suspend(struct device *dev) +{ + struct corebootdrm_device *cdev = dev_get_drvdata(dev); + struct drm_device *drm = &cdev->sysfb.dev; + + return drm_mode_config_helper_suspend(drm); +} + +static int corebootdrm_pm_resume(struct device *dev) +{ + struct corebootdrm_device *cdev = dev_get_drvdata(dev); + struct drm_device *drm = &cdev->sysfb.dev; + + return drm_mode_config_helper_resume(drm); +} + +static DEFINE_SIMPLE_DEV_PM_OPS(corebootdrm_pm_ops, corebootdrm_pm_suspend, corebootdrm_pm_resume); + +static int corebootdrm_probe(struct platform_device *pdev) +{ + const struct lb_framebuffer *fb = dev_get_platdata(&pdev->dev); + struct corebootdrm_device *cdev; + struct drm_sysfb_device *sysfb; + struct drm_device *dev; + const struct drm_format_info *format; + int width, height, pitch; + resource_size_t size; + phys_addr_t address; + enum drm_panel_orientation orientation; + struct resource *res, *mem = NULL; + struct resource aperture; + void __iomem *screen_base; + int ret; + + cdev = devm_drm_dev_alloc(&pdev->dev, &corebootdrm_drm_driver, + struct corebootdrm_device, sysfb.dev); + if (IS_ERR(cdev)) + return PTR_ERR(cdev); + platform_set_drvdata(pdev, cdev); + + sysfb = &cdev->sysfb; + dev = &sysfb->dev; + + if (!fb) { + drm_err(dev, "coreboot framebuffer not found\n"); + return -EINVAL; + } else if (!LB_FRAMEBUFFER_HAS_LFB(fb)) { + drm_err(dev, "coreboot framebuffer entry too small\n"); + return -EINVAL; + } + + /* + * Hardware settings + */ + + format = corebootdrm_get_format_fb(dev, fb); + if (!format) + return -EINVAL; + width = corebootdrm_get_width_fb(dev, fb); + if (width < 0) + return width; + height = corebootdrm_get_height_fb(dev, fb); + if (height < 0) + return height; + pitch = corebootdrm_get_pitch_fb(dev, format, width, fb); + if (pitch < 0) + return pitch; + size = corebootdrm_get_size_fb(dev, height, pitch, fb); + if (!size) + return -EINVAL; + address = corebootdrm_get_address_fb(dev, size, fb); + if (!address) + return -EINVAL; + orientation = corebootdrm_get_orientation_fb(dev, fb); + + sysfb->fb_mode = drm_sysfb_mode(width, height, 0, 0); + sysfb->fb_format = format; + sysfb->fb_pitch = pitch; + + drm_dbg(dev, "display mode={" DRM_MODE_FMT "}\n", DRM_MODE_ARG(&sysfb->fb_mode)); + drm_dbg(dev, "framebuffer format=%p4cc, size=%dx%d, pitch=%d byte\n", + &format->format, width, height, pitch); + + /* + * Memory management + */ + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + drm_err(dev, "memory resource not found\n"); + return -EINVAL; + } + + mem = devm_request_mem_region(&pdev->dev, res->start, resource_size(res), + dev->driver->name); + if (!mem) { + drm_warn(dev, "could not acquire memory resource at %pr\n", res); + /* + * We cannot make this fatal. Sometimes this comes from magic + * spaces our resource handlers simply don't know about. Use + * the memory resource as-is and try to map that instead. + */ + mem = res; + } + + drm_dbg(dev, "using memory resource at %pr\n", mem); + + aperture = DEFINE_RES_MEM(address, size); + if (!resource_contains(mem, &aperture)) { + drm_err(dev, "framebuffer aperture at invalid memory range %pr\n", &aperture); + return -EINVAL; + } + + ret = devm_aperture_acquire_for_platform_device(pdev, address, size); + if (ret) { + drm_err(dev, "could not acquire framebuffer aperture: %d\n", ret); + return ret; + } + + screen_base = devm_ioremap_wc(&pdev->dev, address, size); + if (!screen_base) + return -ENOMEM; + + iosys_map_set_vaddr_iomem(&sysfb->fb_addr, screen_base); + + /* + * DRM mode setting and registration + */ + + ret = corebootdrm_mode_config_init(cdev, orientation); + if (ret) + return ret; + + drm_mode_config_reset(dev); + + ret = drm_dev_register(dev, 0); + if (ret) + return ret; + + drm_client_setup(dev, sysfb->fb_format); + + return 0; +} + +static void corebootdrm_remove(struct platform_device *pdev) +{ + struct corebootdrm_device *cdev = platform_get_drvdata(pdev); + struct drm_device *dev = &cdev->sysfb.dev; + + drm_dev_unplug(dev); +} + +static struct platform_driver corebootdrm_platform_driver = { + .driver = { + .name = "coreboot-framebuffer", + .pm = pm_sleep_ptr(&corebootdrm_pm_ops), + }, + .probe = corebootdrm_probe, + .remove = corebootdrm_remove, +}; + +module_platform_driver(corebootdrm_platform_driver); + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/sysfb/drm_sysfb.c b/drivers/gpu/drm/sysfb/drm_sysfb.c new file mode 100644 index 000000000..fbfb37d0f --- /dev/null +++ b/drivers/gpu/drm/sysfb/drm_sysfb.c @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include +#include +#include + +#include + +#include "drm_sysfb_helper.h" + +int drm_sysfb_get_validated_int(struct drm_device *dev, const char *name, + u64 value, u32 max) +{ + if (value > min(max, INT_MAX)) { + drm_warn(dev, "%s of %llu exceeds maximum of %u\n", name, value, max); + return -EINVAL; + } + return value; +} +EXPORT_SYMBOL(drm_sysfb_get_validated_int); + +int drm_sysfb_get_validated_int0(struct drm_device *dev, const char *name, + u64 value, u32 max) +{ + if (!value) { + drm_warn(dev, "%s of 0 not allowed\n", name); + return -EINVAL; + } + return drm_sysfb_get_validated_int(dev, name, value, max); +} +EXPORT_SYMBOL(drm_sysfb_get_validated_int0); + +const struct drm_format_info *drm_sysfb_get_format(struct drm_device *dev, + const struct drm_sysfb_format *formats, + size_t nformats, + const struct pixel_format *pixel) +{ + const struct drm_format_info *format = NULL; + size_t i; + + for (i = 0; i < nformats; ++i) { + const struct drm_sysfb_format *f = &formats[i]; + + if (pixel_format_equal(pixel, &f->pixel)) { + format = drm_format_info(f->fourcc); + break; + } + } + + if (!format) + drm_warn(dev, "No compatible color format found\n"); + + return format; +} +EXPORT_SYMBOL(drm_sysfb_get_format); + +MODULE_DESCRIPTION("Helpers for DRM sysfb drivers"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/sysfb/drm_sysfb_helper.h b/drivers/gpu/drm/sysfb/drm_sysfb_helper.h new file mode 100644 index 000000000..547f2327a --- /dev/null +++ b/drivers/gpu/drm/sysfb/drm_sysfb_helper.h @@ -0,0 +1,209 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef DRM_SYSFB_HELPER_H +#define DRM_SYSFB_HELPER_H + +#include +#include + +#include