From: Bjorn Helgaas <helgaas@kernel.org>
To: Alexander Graf <agraf@suse.de>
Cc: linux-kernel@vger.kernel.org,
linux-arm-kernel@lists.infradead.org, linux-pci@vger.kernel.org,
Ard Biesheuvel <ard.biesheuvel@linaro.org>
Subject: Re: [PATCH] arm64: Relocate screen_info.lfb_base on PCI BAR allocation
Date: Thu, 28 Apr 2016 11:20:35 -0500 [thread overview]
Message-ID: <20160428162035.GB19785@localhost> (raw)
In-Reply-To: <1461795744-28837-1-git-send-email-agraf@suse.de>
On Thu, Apr 28, 2016 at 12:22:24AM +0200, Alexander Graf wrote:
> When booting with efifb, we get a frame buffer address passed into the system.
> This address can be backed by any device, including PCI devices.
I guess we get the frame buffer address via EFI, but it doesn't tell
us what PCI device it's connected to?
This same thing could happen on any EFI arch, I guess. Maybe even on
non-EFI arches, if there's a way to discover the frame buffer address
as a bare address rather than a "offset X into BAR Y of PCI device Z"
sort of thing.
> PCI devices can have their BARs mapped to various places inside the PCI window
> though. Linux makes use of that on early boot and usually maps PCI BARs wherever
> it thinks makes sense.
>
> If we now load the efifb driver after that BAR map has happened, the frame
> buffer address we received may be invalid, because it was in a BAR map before
> Linux modified it.
>
> To work around that issue, this patch introduces a BAR mapping callback that
> gets called every time Linux (re)allocates a BAR. That way our arm64 efi code
> can check whether the frame buffer is inside the old map and adjust it to
> the new one.
>
> With this and the efifb patches applied, I can successfully see efifb output
> even after Linux remapped BARs.
>
> Signed-off-by: Alexander Graf <agraf@suse.de>
> ---
> arch/arm64/kernel/efi.c | 40 +++++++++++++++++++++++++++++++++++++++-
> drivers/pci/setup-res.c | 29 +++++++++++++++++++++++++++++
> include/linux/pci.h | 8 ++++++++
> 3 files changed, 76 insertions(+), 1 deletion(-)
>
> diff --git a/arch/arm64/kernel/efi.c b/arch/arm64/kernel/efi.c
> index 56a76b6..3612110 100644
> --- a/arch/arm64/kernel/efi.c
> +++ b/arch/arm64/kernel/efi.c
> @@ -27,6 +27,7 @@
> #include <linux/slab.h>
> #include <linux/spinlock.h>
> #include <linux/platform_device.h>
> +#include <linux/pci.h>
>
> #include <asm/cacheflush.h>
> #include <asm/efi.h>
> @@ -213,6 +214,41 @@ static __init void reserve_regions(void)
> set_bit(EFI_MEMMAP, &efi.flags);
> }
>
> +#ifdef CONFIG_PCI
> +static bool efi_pci_overlaps_efifb(struct pci_bar_update_info *update_info)
> +{
> + /* is the screen_info frame buffer inside the pci BAR? */
> + if (screen_info.lfb_base >= update_info->old_start &&
> + (screen_info.lfb_base + screen_info.lfb_size) <=
> + (update_info->old_start + update_info->size))
> + return true;
> +
> + return false;
> +}
> +
> +static int efi_pci_notifier(struct notifier_block *self,
> + unsigned long cmd, void *v)
> +{
> + struct pci_bar_update_info *update_info = v;
> +
> + /*
> + * When we reallocate a BAR that contains our frame buffer, set the
> + * screen_info base to where it belongs
> + */
> + if (efi_pci_overlaps_efifb(update_info)) {
> + u64 diff = (update_info->new_start - update_info->old_start);
> + screen_info.lfb_base += diff;
> + }
> +
> + return NOTIFY_OK;
> +}
> +static struct notifier_block efi_pci_notifier_block = {
> + .notifier_call = efi_pci_notifier,
> +};
> +#else
> +#define pci_notify_on_update_resource(a)
> +#endif
> +
> void __init efi_init_fdt(void *fdt)
> {
> struct efi_fdt_params params;
> @@ -246,8 +282,10 @@ void __init efi_init_fdt(void *fdt)
> reserve_regions();
> early_memunmap(memmap.map, params.mmap_size);
>
> - if (screen_info.orig_video_isVGA == VIDEO_TYPE_EFI)
> + if (screen_info.orig_video_isVGA == VIDEO_TYPE_EFI) {
> + pci_notify_on_update_resource(&efi_pci_notifier_block);
> memblock_reserve(screen_info.lfb_base, screen_info.lfb_size);
> + }
> }
>
> static int __init register_gop_device(void)
> diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c
> index 604011e..d5c24fc 100644
> --- a/drivers/pci/setup-res.c
> +++ b/drivers/pci/setup-res.c
> @@ -23,8 +23,10 @@
> #include <linux/ioport.h>
> #include <linux/cache.h>
> #include <linux/slab.h>
> +#include <linux/notifier.h>
> #include "pci.h"
>
> +static RAW_NOTIFIER_HEAD(bar_update_chain);
>
> void pci_update_resource(struct pci_dev *dev, int resno)
> {
> @@ -35,6 +37,9 @@ void pci_update_resource(struct pci_dev *dev, int resno)
> int reg;
> enum pci_bar_type type;
> struct resource *res = dev->resource + resno;
> + struct pci_bar_update_info update_info;
> + struct pci_bus_region update_reg;
> + struct resource update_res;
>
> if (dev->is_virtfn) {
> dev_warn(&dev->dev, "can't update VF BAR%d\n", resno);
> @@ -77,6 +82,22 @@ void pci_update_resource(struct pci_dev *dev, int resno)
> }
>
> /*
> + * Fetch the old BAR location from the device, so we can notify
> + * users of that BAR that its location is changing.
> + */
> + pci_read_config_dword(dev, reg, &check);
> + update_reg.start = check & PCI_BASE_ADDRESS_MEM_MASK;
> + if (check & PCI_BASE_ADDRESS_MEM_TYPE_64) {
> + pci_read_config_dword(dev, reg, &check);
> + update_reg.start |= ((u64)check) << 32;
> + }
> + update_info.size = region.end - region.start;
> + update_reg.end = update_reg.start + update_info.size;
> + pcibios_bus_to_resource(dev->bus, &update_res, &update_reg);
> + update_info.old_start = update_res.start;
> + update_info.new_start = res->start;
> +
> + /*
> * We can't update a 64-bit BAR atomically, so when possible,
> * disable decoding so that a half-updated BAR won't conflict
> * with another device.
> @@ -108,6 +129,14 @@ void pci_update_resource(struct pci_dev *dev, int resno)
>
> if (disable)
> pci_write_config_word(dev, PCI_COMMAND, cmd);
> +
> + /* Tell interested parties that the BAR mapping changed */
> + raw_notifier_call_chain(&bar_update_chain, 0, &update_info);
> +}
> +
> +int pci_notify_on_update_resource(struct notifier_block *nb)
> +{
> + return raw_notifier_chain_register(&bar_update_chain, nb);
> }
>
> int pci_claim_resource(struct pci_dev *dev, int resource)
> diff --git a/include/linux/pci.h b/include/linux/pci.h
> index c061250..04a430e 100644
> --- a/include/linux/pci.h
> +++ b/include/linux/pci.h
> @@ -30,6 +30,7 @@
> #include <linux/device.h>
> #include <linux/io.h>
> #include <linux/resource_ext.h>
> +#include <linux/notifier.h>
> #include <uapi/linux/pci.h>
>
> #include <linux/pci_ids.h>
> @@ -1037,6 +1038,13 @@ int pci_select_bars(struct pci_dev *dev, unsigned long flags);
> bool pci_device_is_present(struct pci_dev *pdev);
> void pci_ignore_hotplug(struct pci_dev *dev);
>
> +struct pci_bar_update_info {
> + u64 old_start;
> + u64 new_start;
> + u64 size;
> +};
> +int pci_notify_on_update_resource(struct notifier_block *nb);
> +
> /* ROM control related routines */
> int pci_enable_rom(struct pci_dev *pdev);
> void pci_disable_rom(struct pci_dev *pdev);
> --
> 1.8.5.6
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-pci" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
WARNING: multiple messages have this Message-ID (diff)
From: helgaas@kernel.org (Bjorn Helgaas)
To: linux-arm-kernel@lists.infradead.org
Subject: [PATCH] arm64: Relocate screen_info.lfb_base on PCI BAR allocation
Date: Thu, 28 Apr 2016 11:20:35 -0500 [thread overview]
Message-ID: <20160428162035.GB19785@localhost> (raw)
In-Reply-To: <1461795744-28837-1-git-send-email-agraf@suse.de>
On Thu, Apr 28, 2016 at 12:22:24AM +0200, Alexander Graf wrote:
> When booting with efifb, we get a frame buffer address passed into the system.
> This address can be backed by any device, including PCI devices.
I guess we get the frame buffer address via EFI, but it doesn't tell
us what PCI device it's connected to?
This same thing could happen on any EFI arch, I guess. Maybe even on
non-EFI arches, if there's a way to discover the frame buffer address
as a bare address rather than a "offset X into BAR Y of PCI device Z"
sort of thing.
> PCI devices can have their BARs mapped to various places inside the PCI window
> though. Linux makes use of that on early boot and usually maps PCI BARs wherever
> it thinks makes sense.
>
> If we now load the efifb driver after that BAR map has happened, the frame
> buffer address we received may be invalid, because it was in a BAR map before
> Linux modified it.
>
> To work around that issue, this patch introduces a BAR mapping callback that
> gets called every time Linux (re)allocates a BAR. That way our arm64 efi code
> can check whether the frame buffer is inside the old map and adjust it to
> the new one.
>
> With this and the efifb patches applied, I can successfully see efifb output
> even after Linux remapped BARs.
>
> Signed-off-by: Alexander Graf <agraf@suse.de>
> ---
> arch/arm64/kernel/efi.c | 40 +++++++++++++++++++++++++++++++++++++++-
> drivers/pci/setup-res.c | 29 +++++++++++++++++++++++++++++
> include/linux/pci.h | 8 ++++++++
> 3 files changed, 76 insertions(+), 1 deletion(-)
>
> diff --git a/arch/arm64/kernel/efi.c b/arch/arm64/kernel/efi.c
> index 56a76b6..3612110 100644
> --- a/arch/arm64/kernel/efi.c
> +++ b/arch/arm64/kernel/efi.c
> @@ -27,6 +27,7 @@
> #include <linux/slab.h>
> #include <linux/spinlock.h>
> #include <linux/platform_device.h>
> +#include <linux/pci.h>
>
> #include <asm/cacheflush.h>
> #include <asm/efi.h>
> @@ -213,6 +214,41 @@ static __init void reserve_regions(void)
> set_bit(EFI_MEMMAP, &efi.flags);
> }
>
> +#ifdef CONFIG_PCI
> +static bool efi_pci_overlaps_efifb(struct pci_bar_update_info *update_info)
> +{
> + /* is the screen_info frame buffer inside the pci BAR? */
> + if (screen_info.lfb_base >= update_info->old_start &&
> + (screen_info.lfb_base + screen_info.lfb_size) <=
> + (update_info->old_start + update_info->size))
> + return true;
> +
> + return false;
> +}
> +
> +static int efi_pci_notifier(struct notifier_block *self,
> + unsigned long cmd, void *v)
> +{
> + struct pci_bar_update_info *update_info = v;
> +
> + /*
> + * When we reallocate a BAR that contains our frame buffer, set the
> + * screen_info base to where it belongs
> + */
> + if (efi_pci_overlaps_efifb(update_info)) {
> + u64 diff = (update_info->new_start - update_info->old_start);
> + screen_info.lfb_base += diff;
> + }
> +
> + return NOTIFY_OK;
> +}
> +static struct notifier_block efi_pci_notifier_block = {
> + .notifier_call = efi_pci_notifier,
> +};
> +#else
> +#define pci_notify_on_update_resource(a)
> +#endif
> +
> void __init efi_init_fdt(void *fdt)
> {
> struct efi_fdt_params params;
> @@ -246,8 +282,10 @@ void __init efi_init_fdt(void *fdt)
> reserve_regions();
> early_memunmap(memmap.map, params.mmap_size);
>
> - if (screen_info.orig_video_isVGA == VIDEO_TYPE_EFI)
> + if (screen_info.orig_video_isVGA == VIDEO_TYPE_EFI) {
> + pci_notify_on_update_resource(&efi_pci_notifier_block);
> memblock_reserve(screen_info.lfb_base, screen_info.lfb_size);
> + }
> }
>
> static int __init register_gop_device(void)
> diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c
> index 604011e..d5c24fc 100644
> --- a/drivers/pci/setup-res.c
> +++ b/drivers/pci/setup-res.c
> @@ -23,8 +23,10 @@
> #include <linux/ioport.h>
> #include <linux/cache.h>
> #include <linux/slab.h>
> +#include <linux/notifier.h>
> #include "pci.h"
>
> +static RAW_NOTIFIER_HEAD(bar_update_chain);
>
> void pci_update_resource(struct pci_dev *dev, int resno)
> {
> @@ -35,6 +37,9 @@ void pci_update_resource(struct pci_dev *dev, int resno)
> int reg;
> enum pci_bar_type type;
> struct resource *res = dev->resource + resno;
> + struct pci_bar_update_info update_info;
> + struct pci_bus_region update_reg;
> + struct resource update_res;
>
> if (dev->is_virtfn) {
> dev_warn(&dev->dev, "can't update VF BAR%d\n", resno);
> @@ -77,6 +82,22 @@ void pci_update_resource(struct pci_dev *dev, int resno)
> }
>
> /*
> + * Fetch the old BAR location from the device, so we can notify
> + * users of that BAR that its location is changing.
> + */
> + pci_read_config_dword(dev, reg, &check);
> + update_reg.start = check & PCI_BASE_ADDRESS_MEM_MASK;
> + if (check & PCI_BASE_ADDRESS_MEM_TYPE_64) {
> + pci_read_config_dword(dev, reg, &check);
> + update_reg.start |= ((u64)check) << 32;
> + }
> + update_info.size = region.end - region.start;
> + update_reg.end = update_reg.start + update_info.size;
> + pcibios_bus_to_resource(dev->bus, &update_res, &update_reg);
> + update_info.old_start = update_res.start;
> + update_info.new_start = res->start;
> +
> + /*
> * We can't update a 64-bit BAR atomically, so when possible,
> * disable decoding so that a half-updated BAR won't conflict
> * with another device.
> @@ -108,6 +129,14 @@ void pci_update_resource(struct pci_dev *dev, int resno)
>
> if (disable)
> pci_write_config_word(dev, PCI_COMMAND, cmd);
> +
> + /* Tell interested parties that the BAR mapping changed */
> + raw_notifier_call_chain(&bar_update_chain, 0, &update_info);
> +}
> +
> +int pci_notify_on_update_resource(struct notifier_block *nb)
> +{
> + return raw_notifier_chain_register(&bar_update_chain, nb);
> }
>
> int pci_claim_resource(struct pci_dev *dev, int resource)
> diff --git a/include/linux/pci.h b/include/linux/pci.h
> index c061250..04a430e 100644
> --- a/include/linux/pci.h
> +++ b/include/linux/pci.h
> @@ -30,6 +30,7 @@
> #include <linux/device.h>
> #include <linux/io.h>
> #include <linux/resource_ext.h>
> +#include <linux/notifier.h>
> #include <uapi/linux/pci.h>
>
> #include <linux/pci_ids.h>
> @@ -1037,6 +1038,13 @@ int pci_select_bars(struct pci_dev *dev, unsigned long flags);
> bool pci_device_is_present(struct pci_dev *pdev);
> void pci_ignore_hotplug(struct pci_dev *dev);
>
> +struct pci_bar_update_info {
> + u64 old_start;
> + u64 new_start;
> + u64 size;
> +};
> +int pci_notify_on_update_resource(struct notifier_block *nb);
> +
> /* ROM control related routines */
> int pci_enable_rom(struct pci_dev *pdev);
> void pci_disable_rom(struct pci_dev *pdev);
> --
> 1.8.5.6
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-pci" in
> the body of a message to majordomo at vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
next prev parent reply other threads:[~2016-04-28 16:20 UTC|newest]
Thread overview: 34+ messages / expand[flat|nested] mbox.gz Atom feed top
2016-04-27 22:22 [PATCH] arm64: Relocate screen_info.lfb_base on PCI BAR allocation Alexander Graf
2016-04-27 22:22 ` Alexander Graf
2016-04-28 16:20 ` Bjorn Helgaas [this message]
2016-04-28 16:20 ` Bjorn Helgaas
2016-04-28 16:41 ` Alexander Graf
2016-04-28 16:41 ` Alexander Graf
2016-04-28 18:06 ` Bjorn Helgaas
2016-04-28 18:06 ` Bjorn Helgaas
2016-04-28 21:39 ` Alexander Graf
2016-04-28 21:39 ` Alexander Graf
2016-04-29 10:03 ` Lorenzo Pieralisi
2016-04-29 10:03 ` Lorenzo Pieralisi
2016-04-29 13:41 ` Bjorn Helgaas
2016-04-29 13:41 ` Bjorn Helgaas
2016-04-29 13:51 ` Ard Biesheuvel
2016-04-29 13:51 ` Ard Biesheuvel
2016-04-29 20:06 ` Bjorn Helgaas
2016-04-29 20:06 ` Bjorn Helgaas
2016-04-29 20:25 ` Ard Biesheuvel
2016-04-29 20:25 ` Ard Biesheuvel
2016-04-29 20:51 ` Alexander Graf
2016-04-29 20:51 ` Alexander Graf
2016-04-29 21:37 ` Bjorn Helgaas
2016-04-29 21:37 ` Bjorn Helgaas
2016-04-29 21:52 ` Alexander Graf
2016-04-29 21:52 ` Alexander Graf
2016-04-29 22:03 ` Alexander Graf
2016-04-29 22:03 ` Alexander Graf
2016-04-29 23:31 ` Bjorn Helgaas
2016-04-29 23:31 ` Bjorn Helgaas
2016-04-30 21:17 ` Matt Fleming
2016-04-30 21:17 ` Matt Fleming
2016-04-29 21:20 ` Bjorn Helgaas
2016-04-29 21:20 ` Bjorn Helgaas
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20160428162035.GB19785@localhost \
--to=helgaas@kernel.org \
--cc=agraf@suse.de \
--cc=ard.biesheuvel@linaro.org \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-pci@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.