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 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_res_start; // original start of resource 11 static resource_size_t screen_info_lfb_offset; // framebuffer offset within resource 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 int __screen_info_lfb_pci_bus_region(const struct screen_info *si, unsigned int type, 51 struct pci_bus_region *r) 52 { 53 u64 base, size; 54 55 base = __screen_info_lfb_base(si); 56 if (!base) 57 return -EINVAL; 58 59 size = __screen_info_lfb_size(si, type); 60 if (!size) 61 return -EINVAL; 62 63 r->start = base; 64 r->end = base + size - 1; 65 66 return 0; 67 } 68 69 static void screen_info_fixup_lfb(struct pci_dev *pdev) 70 { 71 unsigned int type; 72 struct pci_bus_region bus_region; 73 int ret; 74 struct resource r = { 75 .flags = IORESOURCE_MEM, 76 }; 77 const struct resource *pr; 78 const struct screen_info *si = &screen_info; 79 80 if (screen_info_lfb_pdev) 81 return; // already found 82 83 type = screen_info_video_type(si); 84 if (!__screen_info_has_lfb(type)) 85 return; // only applies to EFI; maybe VESA 86 87 ret = __screen_info_lfb_pci_bus_region(si, type, &bus_region); 88 if (ret < 0) 89 return; 90 91 /* 92 * Translate the PCI bus address to resource. Account 93 * for an offset if the framebuffer is behind a PCI host 94 * bridge. 95 */ 96 pcibios_bus_to_resource(pdev->bus, &r, &bus_region); 97 98 pr = pci_find_resource(pdev, &r); 99 if (!pr) 100 return; 101 102 /* 103 * We've found a PCI device with the framebuffer 104 * resource. Store away the parameters to track 105 * relocation of the framebuffer aperture. 106 */ 107 screen_info_lfb_pdev = pdev; 108 screen_info_lfb_bar = pr - pdev->resource; 109 screen_info_lfb_offset = r.start - pr->start; 110 screen_info_lfb_res_start = bus_region.start; 111 } 112 DECLARE_PCI_FIXUP_CLASS_HEADER(PCI_ANY_ID, PCI_ANY_ID, PCI_BASE_CLASS_DISPLAY, 16, 113 screen_info_fixup_lfb); 114 115 static struct pci_dev *__screen_info_pci_dev(struct resource *res) 116 { 117 struct pci_dev *pdev = NULL; 118 const struct resource *r = NULL; 119 120 if (!(res->flags & IORESOURCE_MEM)) 121 return NULL; 122 123 while (!r && (pdev = pci_get_base_class(PCI_BASE_CLASS_DISPLAY, pdev))) { 124 r = pci_find_resource(pdev, res); 125 } 126 127 return pdev; 128 } 129 130 /** 131 * screen_info_pci_dev() - Return PCI parent device that contains screen_info's framebuffer 132 * @si: the screen_info 133 * 134 * Returns: 135 * The screen_info's parent device or NULL on success, or a pointer-encoded 136 * errno value otherwise. The value NULL is not an error. It signals that no 137 * PCI device has been found. 138 */ 139 struct pci_dev *screen_info_pci_dev(const struct screen_info *si) 140 { 141 struct resource res[SCREEN_INFO_MAX_RESOURCES]; 142 ssize_t i, numres; 143 144 numres = screen_info_resources(si, res, ARRAY_SIZE(res)); 145 if (numres < 0) 146 return ERR_PTR(numres); 147 148 for (i = 0; i < numres; ++i) { 149 struct pci_dev *pdev = __screen_info_pci_dev(&res[i]); 150 151 if (pdev) 152 return pdev; 153 } 154 155 return NULL; 156 } 157 EXPORT_SYMBOL(screen_info_pci_dev); 158