Skip to content

Commit 38ac028

Browse files
Ard BiesheuvelIngo Molnar
authored andcommitted
fbdev/efifb: Honour UEFI memory map attributes when mapping the FB
If the framebuffer address provided by the Graphics Output Protocol (GOP) is covered by the UEFI memory map, it will tell us which memory attributes are permitted when mapping this region. In some cases, (KVM guest on ARM), violating this will result in loss of coherency, which means that updates sent to the framebuffer by the guest will not be observeable by the host, and the emulated display simply does not work. So if the memory map contains such a description, take the attributes field into account, and add support for creating WT or WB mappings of the framebuffer region. Tested-by: Laszlo Ersek <[email protected]> Signed-off-by: Ard Biesheuvel <[email protected]> Acked-by: Bartlomiej Zolnierkiewicz <[email protected]> Cc: Linus Torvalds <[email protected]> Cc: Peter Jones <[email protected]> Cc: Peter Zijlstra <[email protected]> Cc: Thomas Gleixner <[email protected]> Cc: [email protected] Link: http://lkml.kernel.org/r/[email protected] Signed-off-by: Ingo Molnar <[email protected]>
1 parent 7e1550b commit 38ac028

File tree

1 file changed

+41
-10
lines changed

1 file changed

+41
-10
lines changed

drivers/video/fbdev/efifb.c

Lines changed: 41 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
#include <drm/drm_connector.h> /* For DRM_MODE_PANEL_ORIENTATION_* */
2121

2222
static bool request_mem_succeeded = false;
23-
static bool nowc = false;
23+
static u64 mem_flags = EFI_MEMORY_WC | EFI_MEMORY_UC;
2424

2525
static struct fb_var_screeninfo efifb_defined = {
2626
.activate = FB_ACTIVATE_NOW,
@@ -68,8 +68,12 @@ static int efifb_setcolreg(unsigned regno, unsigned red, unsigned green,
6868

6969
static void efifb_destroy(struct fb_info *info)
7070
{
71-
if (info->screen_base)
72-
iounmap(info->screen_base);
71+
if (info->screen_base) {
72+
if (mem_flags & (EFI_MEMORY_UC | EFI_MEMORY_WC))
73+
iounmap(info->screen_base);
74+
else
75+
memunmap(info->screen_base);
76+
}
7377
if (request_mem_succeeded)
7478
release_mem_region(info->apertures->ranges[0].base,
7579
info->apertures->ranges[0].size);
@@ -104,7 +108,7 @@ static int efifb_setup(char *options)
104108
else if (!strncmp(this_opt, "width:", 6))
105109
screen_info.lfb_width = simple_strtoul(this_opt+6, NULL, 0);
106110
else if (!strcmp(this_opt, "nowc"))
107-
nowc = true;
111+
mem_flags &= ~EFI_MEMORY_WC;
108112
}
109113
}
110114

@@ -164,6 +168,7 @@ static int efifb_probe(struct platform_device *dev)
164168
unsigned int size_remap;
165169
unsigned int size_total;
166170
char *option = NULL;
171+
efi_memory_desc_t md;
167172

168173
if (screen_info.orig_video_isVGA != VIDEO_TYPE_EFI || pci_dev_disabled)
169174
return -ENODEV;
@@ -272,12 +277,35 @@ static int efifb_probe(struct platform_device *dev)
272277
info->apertures->ranges[0].base = efifb_fix.smem_start;
273278
info->apertures->ranges[0].size = size_remap;
274279

275-
if (nowc)
276-
info->screen_base = ioremap(efifb_fix.smem_start, efifb_fix.smem_len);
277-
else
278-
info->screen_base = ioremap_wc(efifb_fix.smem_start, efifb_fix.smem_len);
280+
if (!efi_mem_desc_lookup(efifb_fix.smem_start, &md)) {
281+
if ((efifb_fix.smem_start + efifb_fix.smem_len) >
282+
(md.phys_addr + (md.num_pages << EFI_PAGE_SHIFT))) {
283+
pr_err("efifb: video memory @ 0x%lx spans multiple EFI memory regions\n",
284+
efifb_fix.smem_start);
285+
err = -EIO;
286+
goto err_release_fb;
287+
}
288+
/*
289+
* If the UEFI memory map covers the efifb region, we may only
290+
* remap it using the attributes the memory map prescribes.
291+
*/
292+
mem_flags |= EFI_MEMORY_WT | EFI_MEMORY_WB;
293+
mem_flags &= md.attribute;
294+
}
295+
if (mem_flags & EFI_MEMORY_WC)
296+
info->screen_base = ioremap_wc(efifb_fix.smem_start,
297+
efifb_fix.smem_len);
298+
else if (mem_flags & EFI_MEMORY_UC)
299+
info->screen_base = ioremap(efifb_fix.smem_start,
300+
efifb_fix.smem_len);
301+
else if (mem_flags & EFI_MEMORY_WT)
302+
info->screen_base = memremap(efifb_fix.smem_start,
303+
efifb_fix.smem_len, MEMREMAP_WT);
304+
else if (mem_flags & EFI_MEMORY_WB)
305+
info->screen_base = memremap(efifb_fix.smem_start,
306+
efifb_fix.smem_len, MEMREMAP_WB);
279307
if (!info->screen_base) {
280-
pr_err("efifb: abort, cannot ioremap video memory 0x%x @ 0x%lx\n",
308+
pr_err("efifb: abort, cannot remap video memory 0x%x @ 0x%lx\n",
281309
efifb_fix.smem_len, efifb_fix.smem_start);
282310
err = -EIO;
283311
goto err_release_fb;
@@ -371,7 +399,10 @@ static int efifb_probe(struct platform_device *dev)
371399
err_groups:
372400
sysfs_remove_groups(&dev->dev.kobj, efifb_groups);
373401
err_unmap:
374-
iounmap(info->screen_base);
402+
if (mem_flags & (EFI_MEMORY_UC | EFI_MEMORY_WC))
403+
iounmap(info->screen_base);
404+
else
405+
memunmap(info->screen_base);
375406
err_release_fb:
376407
framebuffer_release(info);
377408
err_release_mem:

0 commit comments

Comments
 (0)