[PATCH v2] efifb: avoid reconfiguration of BAR that covers the framebuffer
Ard Biesheuvel
ard.biesheuvel at linaro.org
Wed Mar 22 04:08:48 PDT 2017
> On 22 Mar 2017, at 10:29, Lorenzo Pieralisi <lorenzo.pieralisi at arm.com> wrote:
>
>> On Tue, Mar 21, 2017 at 07:16:50PM +0000, Ard Biesheuvel wrote:
>> On UEFI systems, the PCI subsystem is enumerated by the firmware,
>> and if a graphical framebuffer is exposed by a PCI device, its base
>> address and size are exposed to the OS via the Graphics Output
>> Protocol (GOP).
>>
>> On arm64 PCI systems, the entire PCI hierarchy is reconfigured from
>> scratch at boot. This may result in the GOP framebuffer address to
>> become stale, if the BAR covering the framebuffer is modified. This
>> will cause the framebuffer to become unresponsive, and may in some
>> cases result in unpredictable behavior if the range is reassigned to
>> another device.
>>
>> So add a quirk to the EFI fb driver to find the BAR associated with
>> the GOP base address, and set the IORESOURCE_PCI_FIXED attribute so
>> that the PCI core will leave it alone.
>>
>> Signed-off-by: Ard Biesheuvel <ard.biesheuvel at linaro.org>
>> ---
>> As it turns out, setting the IORESOURCE_PCI_FIXED flag is not sufficient
>> to make the PCI core leave the BARs alone, so instead, the BAR resource
>> is claimed in the quirk handler.
>>
>> As suggested by Lorenzo, a check is added that the device has memory
>> decoding enabled, and if it doesn't, no attempt is made to use the
>> EFI framebuffer.
>>
>> drivers/video/fbdev/efifb.c | 58 +++++++++++++++++++-
>> 1 file changed, 57 insertions(+), 1 deletion(-)
>>
>> diff --git a/drivers/video/fbdev/efifb.c b/drivers/video/fbdev/efifb.c
>> index 8c4dc1e1f94f..eeeaf78c4a5b 100644
>> --- a/drivers/video/fbdev/efifb.c
>> +++ b/drivers/video/fbdev/efifb.c
>> @@ -10,6 +10,7 @@
>> #include <linux/efi.h>
>> #include <linux/errno.h>
>> #include <linux/fb.h>
>> +#include <linux/pci.h>
>> #include <linux/platform_device.h>
>> #include <linux/screen_info.h>
>> #include <video/vga.h>
>> @@ -143,6 +144,9 @@ static struct attribute *efifb_attrs[] = {
>> };
>> ATTRIBUTE_GROUPS(efifb);
>>
>> +static bool pci_bar_found; /* did we find a BAR matching the efifb base? */
>> +static bool pci_bar_disabled; /* was it disabled? */
>> +
>> static int efifb_probe(struct platform_device *dev)
>> {
>> struct fb_info *info;
>> @@ -152,7 +156,7 @@ static int efifb_probe(struct platform_device *dev)
>> unsigned int size_total;
>> char *option = NULL;
>>
>> - if (screen_info.orig_video_isVGA != VIDEO_TYPE_EFI)
>> + if (screen_info.orig_video_isVGA != VIDEO_TYPE_EFI || pci_bar_disabled)
>> return -ENODEV;
>>
>> if (fb_get_options("efifb", &option))
>> @@ -360,3 +364,55 @@ static struct platform_driver efifb_driver = {
>> };
>>
>> builtin_platform_driver(efifb_driver);
>> +
>> +static void claim_efifb_bar(struct pci_dev *dev, int idx)
>> +{
>> + u16 word;
>> +
>> + pci_bar_found = true;
>> +
>> + if (pci_claim_resource(dev, idx)) {
>
> I would not do that. If claiming the resource succeeds, it will become
> immutable (ie it becomes part of the resource tree), if it is your FB
> fine if it isn't this will mess things up since a) you won't be able to
> claim the PCI resources for the real FB and b) you won't be able to
> reallocate the PCI resources for the device we *think* is the FB but it
> actually isn't (unless we force a realloc).
>
> So, I would carry out the check below first, if the device is enabled
> we should flag its resource IORESOURCE_PCI_FIXED otherwise just do
> nothing and let PCI core (through arch specific realloc policy) handle
> it.
>
Well, it turned out that this does not actually work: the BAR is not reassigned, but the same range ends up being given to another device in some cases.
> Side note: I *think* it should be fine to claim a resource twice
> (ie in x86 the FB would have been claimed already by core x86 code),
> just thought it is worth checking.
>
> Lorenzo
>
>> + pci_bar_disabled = true;
>> + dev_err(&dev->dev, "BAR %d: failed to claim efifb BAR\n", idx);
>> + return;
>> + }
>> +
>> + pci_read_config_word(dev, PCI_COMMAND, &word);
>> + if (!(word & PCI_COMMAND_MEMORY)) {
>> + pci_bar_disabled = true;
>> + dev_err(&dev->dev,
>> + "BAR %d: efifb BAR has memory decoding disabled!\n", idx);
>> + return;
>> + }
>> +
>> + dev_info(&dev->dev, "BAR %d: claimed for efifb\n", idx);
>> +}
>> +
>> +static void efifb_fixup_resources(struct pci_dev *dev)
>> +{
>> + u64 base = screen_info.lfb_base;
>> + u64 size = screen_info.lfb_size;
>> + int i;
>> +
>> + if (pci_bar_found || screen_info.orig_video_isVGA != VIDEO_TYPE_EFI)
>> + return;
>> +
>> + if (screen_info.capabilities & VIDEO_CAPABILITY_64BIT_BASE)
>> + base |= (u64)screen_info.ext_lfb_base << 32;
>> +
>> + if (!base)
>> + return;
>> +
>> + for (i = 0; i < PCI_STD_RESOURCE_END; i++) {
>> + struct resource *res = &dev->resource[i];
>> +
>> + if (!(res->flags & IORESOURCE_MEM))
>> + continue;
>> +
>> + if (res->start <= base && res->end >= base + size - 1) {
>> + claim_efifb_bar(dev, i);
>> + break;
>> + }
>> + }
>> +}
>> +DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, efifb_fixup_resources);
>> --
>> 2.7.4
>>
More information about the linux-arm-kernel
mailing list