Skip to content

Commit 78aa89d

Browse files
committed
firmware/sysfb: Update screen_info for relocated EFI framebuffers
On ARM PCI systems, the PCI hierarchy might be reconfigured during boot and the firmware framebuffer might move as a result of that. The values in screen_info will then be invalid. Work around this problem by tracking the framebuffer's initial location before it get relocated; then fix the screen_info state between reloaction and creating the firmware framebuffer's device. This functionality has been lifted from efifb. See the commit message of commit 55d728a ("efi/fb: Avoid reconfiguration of BAR that covers the framebuffer") for more information. Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de> Reviewed-by: Javier Martinez Canillas <javierm@redhat.com> Link: https://patchwork.freedesktop.org/patch/msgid/20240212090736.11464-8-tzimmermann@suse.de
1 parent 784e27f commit 78aa89d

3 files changed

Lines changed: 106 additions & 0 deletions

File tree

drivers/firmware/sysfb.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,8 @@ static __init int sysfb_init(void)
118118
bool compatible;
119119
int ret = 0;
120120

121+
screen_info_apply_fixups();
122+
121123
mutex_lock(&disable_lock);
122124
if (disabled)
123125
goto unlock_mutex;

drivers/video/screen_info_pci.c

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,95 @@
11
// SPDX-License-Identifier: GPL-2.0
22

33
#include <linux/pci.h>
4+
#include <linux/printk.h>
45
#include <linux/screen_info.h>
6+
#include <linux/string.h>
7+
8+
static struct pci_dev *screen_info_lfb_pdev;
9+
static size_t screen_info_lfb_bar;
10+
static resource_size_t screen_info_lfb_offset;
11+
static struct resource screen_info_lfb_res = DEFINE_RES_MEM(0, 0);
12+
13+
static bool __screen_info_relocation_is_valid(const struct screen_info *si, struct resource *pr)
14+
{
15+
u64 size = __screen_info_lfb_size(si, screen_info_video_type(si));
16+
17+
if (screen_info_lfb_offset > resource_size(pr))
18+
return false;
19+
if (size > resource_size(pr))
20+
return false;
21+
if (resource_size(pr) - size < screen_info_lfb_offset)
22+
return false;
23+
24+
return true;
25+
}
26+
27+
void screen_info_apply_fixups(void)
28+
{
29+
struct screen_info *si = &screen_info;
30+
31+
if (screen_info_lfb_pdev) {
32+
struct resource *pr = &screen_info_lfb_pdev->resource[screen_info_lfb_bar];
33+
34+
if (pr->start != screen_info_lfb_res.start) {
35+
if (__screen_info_relocation_is_valid(si, pr)) {
36+
/*
37+
* Only update base if we have an actual
38+
* relocation to a valid I/O range.
39+
*/
40+
__screen_info_set_lfb_base(si, pr->start + screen_info_lfb_offset);
41+
pr_info("Relocating firmware framebuffer to offset %pa[d] within %pr\n",
42+
&screen_info_lfb_offset, pr);
43+
} else {
44+
pr_warn("Invalid relocating, disabling firmware framebuffer\n");
45+
}
46+
}
47+
}
48+
}
49+
50+
static void screen_info_fixup_lfb(struct pci_dev *pdev)
51+
{
52+
unsigned int type;
53+
struct resource res[SCREEN_INFO_MAX_RESOURCES];
54+
size_t i, numres;
55+
int ret;
56+
const struct screen_info *si = &screen_info;
57+
58+
if (screen_info_lfb_pdev)
59+
return; // already found
60+
61+
type = screen_info_video_type(si);
62+
if (type != VIDEO_TYPE_EFI)
63+
return; // only applies to EFI
64+
65+
ret = screen_info_resources(si, res, ARRAY_SIZE(res));
66+
if (ret < 0)
67+
return;
68+
numres = ret;
69+
70+
for (i = 0; i < numres; ++i) {
71+
struct resource *r = &res[i];
72+
const struct resource *pr;
73+
74+
if (!(r->flags & IORESOURCE_MEM))
75+
continue;
76+
pr = pci_find_resource(pdev, r);
77+
if (!pr)
78+
continue;
79+
80+
/*
81+
* We've found a PCI device with the framebuffer
82+
* resource. Store away the parameters to track
83+
* relocation of the framebuffer aperture.
84+
*/
85+
screen_info_lfb_pdev = pdev;
86+
screen_info_lfb_bar = pr - pdev->resource;
87+
screen_info_lfb_offset = r->start - pr->start;
88+
memcpy(&screen_info_lfb_res, r, sizeof(screen_info_lfb_res));
89+
}
90+
}
91+
DECLARE_PCI_FIXUP_CLASS_HEADER(PCI_ANY_ID, PCI_ANY_ID, PCI_BASE_CLASS_DISPLAY, 16,
92+
screen_info_fixup_lfb);
593

694
static struct pci_dev *__screen_info_pci_dev(struct resource *res)
795
{

include/linux/screen_info.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
#include <uapi/linux/screen_info.h>
66

7+
#include <linux/bits.h>
8+
79
/**
810
* SCREEN_INFO_MAX_RESOURCES - maximum number of resources per screen_info
911
*/
@@ -27,6 +29,17 @@ static inline u64 __screen_info_lfb_base(const struct screen_info *si)
2729
return lfb_base;
2830
}
2931

32+
static inline void __screen_info_set_lfb_base(struct screen_info *si, u64 lfb_base)
33+
{
34+
si->lfb_base = lfb_base & GENMASK_ULL(31, 0);
35+
si->ext_lfb_base = (lfb_base & GENMASK_ULL(63, 32)) >> 32;
36+
37+
if (si->ext_lfb_base)
38+
si->capabilities |= VIDEO_CAPABILITY_64BIT_BASE;
39+
else
40+
si->capabilities &= ~VIDEO_CAPABILITY_64BIT_BASE;
41+
}
42+
3043
static inline u64 __screen_info_lfb_size(const struct screen_info *si, unsigned int type)
3144
{
3245
u64 lfb_size = si->lfb_size;
@@ -106,8 +119,11 @@ static inline unsigned int screen_info_video_type(const struct screen_info *si)
106119
ssize_t screen_info_resources(const struct screen_info *si, struct resource *r, size_t num);
107120

108121
#if defined(CONFIG_PCI)
122+
void screen_info_apply_fixups(void);
109123
struct pci_dev *screen_info_pci_dev(const struct screen_info *si);
110124
#else
125+
static inline void screen_info_apply_fixups(void)
126+
{ }
111127
static inline struct pci_dev *screen_info_pci_dev(const struct screen_info *si)
112128
{
113129
return NULL;

0 commit comments

Comments
 (0)