Linux Audio

Check our new training course

Loading...
Note: File does not exist in v6.2.
  1// SPDX-License-Identifier: GPL-2.0
  2
  3#include <linux/pci.h>
  4#include <linux/printk.h>
  5#include <linux/screen_info.h>
  6#include <linux/string.h>
  7
  8static struct pci_dev *screen_info_lfb_pdev;
  9static size_t screen_info_lfb_bar;
 10static resource_size_t screen_info_lfb_offset;
 11static struct resource screen_info_lfb_res = DEFINE_RES_MEM(0, 0);
 12
 13static 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
 27void 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
 50static 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}
 91DECLARE_PCI_FIXUP_CLASS_HEADER(PCI_ANY_ID, PCI_ANY_ID, PCI_BASE_CLASS_DISPLAY, 16,
 92			       screen_info_fixup_lfb);
 93
 94static struct pci_dev *__screen_info_pci_dev(struct resource *res)
 95{
 96	struct pci_dev *pdev = NULL;
 97	const struct resource *r = NULL;
 98
 99	if (!(res->flags & IORESOURCE_MEM))
100		return NULL;
101
102	while (!r && (pdev = pci_get_base_class(PCI_BASE_CLASS_DISPLAY, pdev))) {
103		r = pci_find_resource(pdev, res);
104	}
105
106	return pdev;
107}
108
109/**
110 * screen_info_pci_dev() - Return PCI parent device that contains screen_info's framebuffer
111 * @si: the screen_info
112 *
113 * Returns:
114 * The screen_info's parent device or NULL on success, or a pointer-encoded
115 * errno value otherwise. The value NULL is not an error. It signals that no
116 * PCI device has been found.
117 */
118struct pci_dev *screen_info_pci_dev(const struct screen_info *si)
119{
120	struct resource res[SCREEN_INFO_MAX_RESOURCES];
121	ssize_t i, numres;
122
123	numres = screen_info_resources(si, res, ARRAY_SIZE(res));
124	if (numres < 0)
125		return ERR_PTR(numres);
126
127	for (i = 0; i < numres; ++i) {
128		struct pci_dev *pdev = __screen_info_pci_dev(&res[i]);
129
130		if (pdev)
131			return pdev;
132	}
133
134	return NULL;
135}
136EXPORT_SYMBOL(screen_info_pci_dev);