File Index
Files and directories
Sign in
FILE

efifb.c

File preview with syntax highlighting.

linux-6.19.14/drivers/video/fbdev/efifb.c C
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * Framebuffer driver for EFI/UEFI based system
  4 *
  5 * (c) 2006 Edgar Hucek <gimli@dark-green.com>
  6 * Original efi driver written by Gerd Knorr <kraxel@goldbach.in-berlin.de>
  7 *
  8 */
  9
 10#include <linux/aperture.h>
 11#include <linux/kernel.h>
 12#include <linux/efi.h>
 13#include <linux/efi-bgrt.h>
 14#include <linux/errno.h>
 15#include <linux/fb.h>
 16#include <linux/platform_device.h>
 17#include <linux/printk.h>
 18#include <linux/screen_info.h>
 19#include <video/vga.h>
 20#include <asm/efi.h>
 21#include <drm/drm_utils.h> /* For drm_get_panel_orientation_quirk */
 22#include <drm/drm_connector.h>  /* For DRM_MODE_PANEL_ORIENTATION_* */
 23
 24struct bmp_file_header {
 25	u16 id;
 26	u32 file_size;
 27	u32 reserved;
 28	u32 bitmap_offset;
 29} __packed;
 30
 31struct bmp_dib_header {
 32	u32 dib_header_size;
 33	s32 width;
 34	s32 height;
 35	u16 planes;
 36	u16 bpp;
 37	u32 compression;
 38	u32 bitmap_size;
 39	u32 horz_resolution;
 40	u32 vert_resolution;
 41	u32 colors_used;
 42	u32 colors_important;
 43} __packed;
 44
 45static bool use_bgrt = true;
 46static bool request_mem_succeeded = false;
 47static u64 mem_flags = EFI_MEMORY_WC | EFI_MEMORY_UC;
 48
 49struct efifb_par {
 50	u32 pseudo_palette[16];
 51	resource_size_t base;
 52	resource_size_t size;
 53};
 54
 55static struct fb_var_screeninfo efifb_defined = {
 56	.activate		= FB_ACTIVATE_NOW,
 57	.height			= -1,
 58	.width			= -1,
 59	.right_margin		= 32,
 60	.upper_margin		= 16,
 61	.lower_margin		= 4,
 62	.vsync_len		= 4,
 63	.vmode			= FB_VMODE_NONINTERLACED,
 64};
 65
 66static struct fb_fix_screeninfo efifb_fix = {
 67	.id			= "EFI VGA",
 68	.type			= FB_TYPE_PACKED_PIXELS,
 69	.accel			= FB_ACCEL_NONE,
 70	.visual			= FB_VISUAL_TRUECOLOR,
 71};
 72
 73static int efifb_setcolreg(unsigned regno, unsigned red, unsigned green,
 74			   unsigned blue, unsigned transp,
 75			   struct fb_info *info)
 76{
 77	/*
 78	 *  Set a single color register. The values supplied are
 79	 *  already rounded down to the hardware's capabilities
 80	 *  (according to the entries in the `var' structure). Return
 81	 *  != 0 for invalid regno.
 82	 */
 83
 84	if (regno >= info->cmap.len)
 85		return 1;
 86
 87	if (regno < 16) {
 88		red   >>= 16 - info->var.red.length;
 89		green >>= 16 - info->var.green.length;
 90		blue  >>= 16 - info->var.blue.length;
 91		((u32 *)(info->pseudo_palette))[regno] =
 92			(red   << info->var.red.offset)   |
 93			(green << info->var.green.offset) |
 94			(blue  << info->var.blue.offset);
 95	}
 96	return 0;
 97}
 98
 99/*
100 * If fbcon deffered console takeover is configured, the intent is for the
101 * framebuffer to show the boot graphics (e.g. vendor logo) until there is some
102 * (error) message to display. But the boot graphics may have been destroyed by
103 * e.g. option ROM output, detect this and restore the boot graphics.
104 */
105#if defined CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER && \
106    defined CONFIG_ACPI_BGRT
107static void efifb_copy_bmp(u8 *src, u32 *dst, int width, const struct screen_info *si)
108{
109	u8 r, g, b;
110
111	while (width--) {
112		b = *src++;
113		g = *src++;
114		r = *src++;
115		*dst++ = (r << si->red_pos)   |
116			 (g << si->green_pos) |
117			 (b << si->blue_pos);
118	}
119}
120
121#ifdef CONFIG_X86
122/*
123 * On x86 some firmwares use a low non native resolution for the display when
124 * they have shown some text messages. While keeping the bgrt filled with info
125 * for the native resolution. If the bgrt image intended for the native
126 * resolution still fits, it will be displayed very close to the right edge of
127 * the display looking quite bad. This function checks for this.
128 */
129static bool efifb_bgrt_sanity_check(const struct screen_info *si, u32 bmp_width)
130{
131	/*
132	 * All x86 firmwares horizontally center the image (the yoffset
133	 * calculations differ between boards, but xoffset is predictable).
134	 */
135	u32 expected_xoffset = (si->lfb_width - bmp_width) / 2;
136
137	return bgrt_tab.image_offset_x == expected_xoffset;
138}
139#else
140static bool efifb_bgrt_sanity_check(const struct screen_info *si, u32 bmp_width)
141{
142	return true;
143}
144#endif
145
146static void efifb_show_boot_graphics(struct fb_info *info, const struct screen_info *si)
147{
148	u32 bmp_width, bmp_height, bmp_pitch, dst_x, y, src_y;
149	struct bmp_file_header *file_header;
150	struct bmp_dib_header *dib_header;
151	void *bgrt_image = NULL;
152	u8 *dst = info->screen_base;
153
154	if (!use_bgrt)
155		return;
156
157	if (!bgrt_tab.image_address) {
158		pr_info("efifb: No BGRT, not showing boot graphics\n");
159		return;
160	}
161
162	if (bgrt_tab.status & 0x06) {
163		pr_info("efifb: BGRT rotation bits set, not showing boot graphics\n");
164		return;
165	}
166
167	/* Avoid flashing the logo if we're going to print std probe messages */
168	if (console_loglevel > CONSOLE_LOGLEVEL_QUIET)
169		return;
170
171	/* bgrt_tab.status is unreliable, so we don't check it */
172
173	if (si->lfb_depth != 32) {
174		pr_info("efifb: not 32 bits, not showing boot graphics\n");
175		return;
176	}
177
178	bgrt_image = memremap(bgrt_tab.image_address, bgrt_image_size,
179			      MEMREMAP_WB);
180	if (!bgrt_image) {
181		pr_warn("efifb: Ignoring BGRT: failed to map image memory\n");
182		return;
183	}
184
185	if (bgrt_image_size < (sizeof(*file_header) + sizeof(*dib_header)))
186		goto error;
187
188	file_header = bgrt_image;
189	if (file_header->id != 0x4d42 || file_header->reserved != 0)
190		goto error;
191
192	dib_header = bgrt_image + sizeof(*file_header);
193	if (dib_header->dib_header_size != 40 || dib_header->width < 0 ||
194	    dib_header->planes != 1 || dib_header->bpp != 24 ||
195	    dib_header->compression != 0)
196		goto error;
197
198	bmp_width = dib_header->width;
199	bmp_height = abs(dib_header->height);
200	bmp_pitch = round_up(3 * bmp_width, 4);
201
202	if ((file_header->bitmap_offset + bmp_pitch * bmp_height) >
203				bgrt_image_size)
204		goto error;
205
206	if ((bgrt_tab.image_offset_x + bmp_width) > si->lfb_width ||
207	    (bgrt_tab.image_offset_y + bmp_height) > si->lfb_height)
208		goto error;
209
210	if (!efifb_bgrt_sanity_check(si, bmp_width))
211		goto error;
212
213	pr_info("efifb: showing boot graphics\n");
214
215	for (y = 0; y < si->lfb_height; y++, dst += si->lfb_linelength) {
216		/* Only background? */
217		if (y < bgrt_tab.image_offset_y ||
218		    y >= (bgrt_tab.image_offset_y + bmp_height)) {
219			memset(dst, 0, 4 * si->lfb_width);
220			continue;
221		}
222
223		src_y = y - bgrt_tab.image_offset_y;
224		/* Positive header height means upside down row order */
225		if (dib_header->height > 0)
226			src_y = (bmp_height - 1) - src_y;
227
228		memset(dst, 0, bgrt_tab.image_offset_x * 4);
229		dst_x = bgrt_tab.image_offset_x;
230		efifb_copy_bmp(bgrt_image + file_header->bitmap_offset +
231					    src_y * bmp_pitch,
232			       (u32 *)dst + dst_x, bmp_width, si);
233		dst_x += bmp_width;
234		memset((u32 *)dst + dst_x, 0, (si->lfb_width - dst_x) * 4);
235	}
236
237	memunmap(bgrt_image);
238	return;
239
240error:
241	memunmap(bgrt_image);
242	pr_warn("efifb: Ignoring BGRT: unexpected or invalid BMP data\n");
243}
244#else
245static inline void efifb_show_boot_graphics(struct fb_info *info, const struct screen_info *si)
246{ }
247#endif
248
249/*
250 * fb_ops.fb_destroy is called by the last put_fb_info() call at the end
251 * of unregister_framebuffer() or fb_release(). Do any cleanup here.
252 */
253static void efifb_destroy(struct fb_info *info)
254{
255	struct efifb_par *par = info->par;
256
257	if (info->screen_base) {
258		if (mem_flags & (EFI_MEMORY_UC | EFI_MEMORY_WC))
259			iounmap(info->screen_base);
260		else
261			memunmap(info->screen_base);
262	}
263
264	if (request_mem_succeeded)
265		release_mem_region(par->base, par->size);
266	fb_dealloc_cmap(&info->cmap);
267
268	framebuffer_release(info);
269}
270
271static const struct fb_ops efifb_ops = {
272	.owner		= THIS_MODULE,
273	FB_DEFAULT_IOMEM_OPS,
274	.fb_destroy	= efifb_destroy,
275	.fb_setcolreg	= efifb_setcolreg,
276};
277
278static void efifb_setup(struct screen_info *si, char *options)
279{
280	char *this_opt;
281
282	if (options && *options) {
283		while ((this_opt = strsep(&options, ",")) != NULL) {
284			if (!*this_opt) continue;
285
286			efifb_setup_from_dmi(si, this_opt);
287
288			if (!strncmp(this_opt, "base:", 5))
289				si->lfb_base = simple_strtoul(this_opt+5, NULL, 0);
290			else if (!strncmp(this_opt, "stride:", 7))
291				si->lfb_linelength = simple_strtoul(this_opt+7, NULL, 0) * 4;
292			else if (!strncmp(this_opt, "height:", 7))
293				si->lfb_height = simple_strtoul(this_opt+7, NULL, 0);
294			else if (!strncmp(this_opt, "width:", 6))
295				si->lfb_width = simple_strtoul(this_opt+6, NULL, 0);
296			else if (!strcmp(this_opt, "nowc"))
297				mem_flags &= ~EFI_MEMORY_WC;
298			else if (!strcmp(this_opt, "nobgrt"))
299				use_bgrt = false;
300		}
301	}
302}
303
304static inline bool fb_base_is_valid(struct screen_info *si)
305{
306	if (si->lfb_base)
307		return true;
308
309	if (!(si->capabilities & VIDEO_CAPABILITY_64BIT_BASE))
310		return false;
311
312	if (si->ext_lfb_base)
313		return true;
314
315	return false;
316}
317
318#define efifb_attr_decl(name, fmt)					\
319static ssize_t name##_show(struct device *dev,				\
320			   struct device_attribute *attr,		\
321			   char *buf)					\
322{									\
323	struct screen_info *si = dev_get_drvdata(dev);			\
324	if (!si)							\
325		return -ENODEV;						\
326	return sprintf(buf, fmt "\n", (si->lfb_##name));		\
327}									\
328static DEVICE_ATTR_RO(name)
329
330efifb_attr_decl(base, "0x%x");
331efifb_attr_decl(linelength, "%u");
332efifb_attr_decl(height, "%u");
333efifb_attr_decl(width, "%u");
334efifb_attr_decl(depth, "%u");
335
336static struct attribute *efifb_attrs[] = {
337	&dev_attr_base.attr,
338	&dev_attr_linelength.attr,
339	&dev_attr_width.attr,
340	&dev_attr_height.attr,
341	&dev_attr_depth.attr,
342	NULL
343};
344ATTRIBUTE_GROUPS(efifb);
345
346static int efifb_probe(struct platform_device *dev)
347{
348	struct screen_info *si;
349	struct fb_info *info;
350	struct efifb_par *par;
351	int err, orientation;
352	unsigned int size_vmode;
353	unsigned int size_remap;
354	unsigned int size_total;
355	char *option = NULL;
356	efi_memory_desc_t md;
357
358	/*
359	 * If we fail probing the device, the kernel might try a different
360	 * driver. We get a copy of the attached screen_info, so that we can
361	 * modify its values without affecting later drivers.
362	 */
363	si = dev_get_platdata(&dev->dev);
364	if (!si)
365		return -ENODEV;
366	si = devm_kmemdup(&dev->dev, si, sizeof(*si), GFP_KERNEL);
367	if (!si)
368		return -ENOMEM;
369
370	dev_set_drvdata(&dev->dev, si);
371
372	if (si->orig_video_isVGA != VIDEO_TYPE_EFI)
373		return -ENODEV;
374
375	if (fb_get_options("efifb", &option))
376		return -ENODEV;
377	efifb_setup(si, option);
378
379	/* We don't get linelength from UGA Draw Protocol, only from
380	 * EFI Graphics Protocol.  So if it's not in DMI, and it's not
381	 * passed in from the user, we really can't use the framebuffer.
382	 */
383	if (!si->lfb_linelength)
384		return -ENODEV;
385
386	if (!si->lfb_depth)
387		si->lfb_depth = 32;
388	if (!si->pages)
389		si->pages = 1;
390	if (!fb_base_is_valid(si)) {
391		printk(KERN_DEBUG "efifb: invalid framebuffer address\n");
392		return -ENODEV;
393	}
394	printk(KERN_INFO "efifb: probing for efifb\n");
395
396	/* just assume they're all unset if any are */
397	if (!si->blue_size) {
398		si->blue_size = 8;
399		si->blue_pos = 0;
400		si->green_size = 8;
401		si->green_pos = 8;
402		si->red_size = 8;
403		si->red_pos = 16;
404		si->rsvd_size = 8;
405		si->rsvd_pos = 24;
406	}
407
408	efifb_fix.smem_start = __screen_info_lfb_base(si);
409
410	efifb_defined.bits_per_pixel = si->lfb_depth;
411	efifb_defined.xres = si->lfb_width;
412	efifb_defined.yres = si->lfb_height;
413	efifb_fix.line_length = si->lfb_linelength;
414
415	/*   size_vmode -- that is the amount of memory needed for the
416	 *                 used video mode, i.e. the minimum amount of
417	 *                 memory we need. */
418	size_vmode = efifb_defined.yres * efifb_fix.line_length;
419
420	/*   size_total -- all video memory we have. Used for
421	 *                 entries, ressource allocation and bounds
422	 *                 checking. */
423	size_total = si->lfb_size;
424	if (size_total < size_vmode)
425		size_total = size_vmode;
426
427	/*   size_remap -- the amount of video memory we are going to
428	 *                 use for efifb.  With modern cards it is no
429	 *                 option to simply use size_total as that
430	 *                 wastes plenty of kernel address space. */
431	size_remap  = size_vmode * 2;
432	if (size_remap > size_total)
433		size_remap = size_total;
434	if (size_remap % PAGE_SIZE)
435		size_remap += PAGE_SIZE - (size_remap % PAGE_SIZE);
436	efifb_fix.smem_len = size_remap;
437
438	if (request_mem_region(efifb_fix.smem_start, size_remap, "efifb")) {
439		request_mem_succeeded = true;
440	} else {
441		/* We cannot make this fatal. Sometimes this comes from magic
442		   spaces our resource handlers simply don't know about */
443		pr_warn("efifb: cannot reserve video memory at 0x%lx\n",
444			efifb_fix.smem_start);
445	}
446
447	info = framebuffer_alloc(sizeof(*par), &dev->dev);
448	if (!info) {
449		err = -ENOMEM;
450		goto err_release_mem;
451	}
452	par = info->par;
453	info->pseudo_palette = par->pseudo_palette;
454
455	par->base = efifb_fix.smem_start;
456	par->size = size_remap;
457
458	if (efi_enabled(EFI_MEMMAP) &&
459	    !efi_mem_desc_lookup(efifb_fix.smem_start, &md)) {
460		if ((efifb_fix.smem_start + efifb_fix.smem_len) >
461		    (md.phys_addr + (md.num_pages << EFI_PAGE_SHIFT))) {
462			pr_err("efifb: video memory @ 0x%lx spans multiple EFI memory regions\n",
463			       efifb_fix.smem_start);
464			err = -EIO;
465			goto err_release_fb;
466		}
467		/*
468		 * If the UEFI memory map covers the efifb region, we may only
469		 * remap it using the attributes the memory map prescribes.
470		 */
471		md.attribute &= EFI_MEMORY_UC | EFI_MEMORY_WC |
472				EFI_MEMORY_WT | EFI_MEMORY_WB;
473		if (md.attribute) {
474			mem_flags |= EFI_MEMORY_WT | EFI_MEMORY_WB;
475			mem_flags &= md.attribute;
476		}
477	}
478	if (mem_flags & EFI_MEMORY_WC)
479		info->screen_base = ioremap_wc(efifb_fix.smem_start,
480					       efifb_fix.smem_len);
481	else if (mem_flags & EFI_MEMORY_UC)
482		info->screen_base = ioremap(efifb_fix.smem_start,
483					    efifb_fix.smem_len);
484	else if (mem_flags & EFI_MEMORY_WT)
485		info->screen_base = memremap(efifb_fix.smem_start,
486					     efifb_fix.smem_len, MEMREMAP_WT);
487	else if (mem_flags & EFI_MEMORY_WB)
488		info->screen_base = memremap(efifb_fix.smem_start,
489					     efifb_fix.smem_len, MEMREMAP_WB);
490	if (!info->screen_base) {
491		pr_err("efifb: abort, cannot remap video memory 0x%x @ 0x%lx\n",
492			efifb_fix.smem_len, efifb_fix.smem_start);
493		err = -EIO;
494		goto err_release_fb;
495	}
496
497	efifb_show_boot_graphics(info, si);
498
499	pr_info("efifb: framebuffer at 0x%lx, using %dk, total %dk\n",
500	       efifb_fix.smem_start, size_remap/1024, size_total/1024);
501	pr_info("efifb: mode is %dx%dx%d, linelength=%d, pages=%d\n",
502	       efifb_defined.xres, efifb_defined.yres,
503	       efifb_defined.bits_per_pixel, efifb_fix.line_length,
504	       si->pages);
505
506	efifb_defined.xres_virtual = efifb_defined.xres;
507	efifb_defined.yres_virtual = efifb_fix.smem_len /
508					efifb_fix.line_length;
509	pr_info("efifb: scrolling: redraw\n");
510	efifb_defined.yres_virtual = efifb_defined.yres;
511
512	/* some dummy values for timing to make fbset happy */
513	efifb_defined.pixclock     = 10000000 / efifb_defined.xres *
514					1000 / efifb_defined.yres;
515	efifb_defined.left_margin  = (efifb_defined.xres / 8) & 0xf8;
516	efifb_defined.hsync_len    = (efifb_defined.xres / 8) & 0xf8;
517
518	efifb_defined.red.offset    = si->red_pos;
519	efifb_defined.red.length    = si->red_size;
520	efifb_defined.green.offset  = si->green_pos;
521	efifb_defined.green.length  = si->green_size;
522	efifb_defined.blue.offset   = si->blue_pos;
523	efifb_defined.blue.length   = si->blue_size;
524	efifb_defined.transp.offset = si->rsvd_pos;
525	efifb_defined.transp.length = si->rsvd_size;
526
527	pr_info("efifb: %s: "
528	       "size=%d:%d:%d:%d, shift=%d:%d:%d:%d\n",
529	       "Truecolor",
530	       si->rsvd_size,
531	       si->red_size,
532	       si->green_size,
533	       si->blue_size,
534	       si->rsvd_pos,
535	       si->red_pos,
536	       si->green_pos,
537	       si->blue_pos);
538
539	efifb_fix.ypanstep  = 0;
540	efifb_fix.ywrapstep = 0;
541
542	info->fbops = &efifb_ops;
543	info->var = efifb_defined;
544	info->fix = efifb_fix;
545
546	orientation = drm_get_panel_orientation_quirk(efifb_defined.xres,
547						      efifb_defined.yres);
548	switch (orientation) {
549	default:
550		info->fbcon_rotate_hint = FB_ROTATE_UR;
551		break;
552	case DRM_MODE_PANEL_ORIENTATION_BOTTOM_UP:
553		info->fbcon_rotate_hint = FB_ROTATE_UD;
554		break;
555	case DRM_MODE_PANEL_ORIENTATION_LEFT_UP:
556		info->fbcon_rotate_hint = FB_ROTATE_CCW;
557		break;
558	case DRM_MODE_PANEL_ORIENTATION_RIGHT_UP:
559		info->fbcon_rotate_hint = FB_ROTATE_CW;
560		break;
561	}
562
563	err = fb_alloc_cmap(&info->cmap, 256, 0);
564	if (err < 0) {
565		pr_err("efifb: cannot allocate colormap\n");
566		goto err_unmap;
567	}
568
569	err = devm_aperture_acquire_for_platform_device(dev, par->base, par->size);
570	if (err) {
571		pr_err("efifb: cannot acquire aperture\n");
572		goto err_fb_dealloc_cmap;
573	}
574	err = devm_register_framebuffer(&dev->dev, info);
575	if (err < 0) {
576		pr_err("efifb: cannot register framebuffer\n");
577		goto err_fb_dealloc_cmap;
578	}
579	fb_info(info, "%s frame buffer device\n", info->fix.id);
580	return 0;
581
582err_fb_dealloc_cmap:
583	fb_dealloc_cmap(&info->cmap);
584err_unmap:
585	if (mem_flags & (EFI_MEMORY_UC | EFI_MEMORY_WC))
586		iounmap(info->screen_base);
587	else
588		memunmap(info->screen_base);
589err_release_fb:
590	framebuffer_release(info);
591err_release_mem:
592	if (request_mem_succeeded)
593		release_mem_region(efifb_fix.smem_start, size_total);
594	return err;
595}
596
597static struct platform_driver efifb_driver = {
598	.driver = {
599		.name = "efi-framebuffer",
600		.dev_groups = efifb_groups,
601	},
602	.probe = efifb_probe,
603};
604
605builtin_platform_driver(efifb_driver);
~/ Read-only file preview.