screen_info_pci.c 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. // SPDX-License-Identifier: GPL-2.0
  2. #include <linux/pci.h>
  3. #include <linux/printk.h>
  4. #include <linux/screen_info.h>
  5. #include <linux/string.h>
  6. static struct pci_dev *screen_info_lfb_pdev;
  7. static size_t screen_info_lfb_bar;
  8. static resource_size_t screen_info_lfb_res_start; // original start of resource
  9. static resource_size_t screen_info_lfb_offset; // framebuffer offset within resource
  10. static bool __screen_info_relocation_is_valid(const struct screen_info *si, struct resource *pr)
  11. {
  12. u64 size = __screen_info_lfb_size(si, screen_info_video_type(si));
  13. if (screen_info_lfb_offset > resource_size(pr))
  14. return false;
  15. if (size > resource_size(pr))
  16. return false;
  17. if (resource_size(pr) - size < screen_info_lfb_offset)
  18. return false;
  19. return true;
  20. }
  21. void screen_info_apply_fixups(void)
  22. {
  23. struct screen_info *si = &screen_info;
  24. if (screen_info_lfb_pdev) {
  25. struct resource *pr = &screen_info_lfb_pdev->resource[screen_info_lfb_bar];
  26. if (pr->start != screen_info_lfb_res_start) {
  27. if (__screen_info_relocation_is_valid(si, pr)) {
  28. /*
  29. * Only update base if we have an actual
  30. * relocation to a valid I/O range.
  31. */
  32. __screen_info_set_lfb_base(si, pr->start + screen_info_lfb_offset);
  33. pr_info("Relocating firmware framebuffer to offset %pa[d] within %pr\n",
  34. &screen_info_lfb_offset, pr);
  35. } else {
  36. pr_warn("Invalid relocating, disabling firmware framebuffer\n");
  37. }
  38. }
  39. }
  40. }
  41. static int __screen_info_lfb_pci_bus_region(const struct screen_info *si, unsigned int type,
  42. struct pci_bus_region *r)
  43. {
  44. u64 base, size;
  45. base = __screen_info_lfb_base(si);
  46. if (!base)
  47. return -EINVAL;
  48. size = __screen_info_lfb_size(si, type);
  49. if (!size)
  50. return -EINVAL;
  51. r->start = base;
  52. r->end = base + size - 1;
  53. return 0;
  54. }
  55. static void screen_info_fixup_lfb(struct pci_dev *pdev)
  56. {
  57. unsigned int type;
  58. struct pci_bus_region bus_region;
  59. int ret;
  60. struct resource r = {
  61. .flags = IORESOURCE_MEM,
  62. };
  63. const struct resource *pr;
  64. const struct screen_info *si = &screen_info;
  65. if (screen_info_lfb_pdev)
  66. return; // already found
  67. type = screen_info_video_type(si);
  68. if (!__screen_info_has_lfb(type))
  69. return; // only applies to EFI; maybe VESA
  70. ret = __screen_info_lfb_pci_bus_region(si, type, &bus_region);
  71. if (ret < 0)
  72. return;
  73. /*
  74. * Translate the PCI bus address to resource. Account
  75. * for an offset if the framebuffer is behind a PCI host
  76. * bridge.
  77. */
  78. pcibios_bus_to_resource(pdev->bus, &r, &bus_region);
  79. pr = pci_find_resource(pdev, &r);
  80. if (!pr)
  81. return;
  82. /*
  83. * We've found a PCI device with the framebuffer
  84. * resource. Store away the parameters to track
  85. * relocation of the framebuffer aperture.
  86. */
  87. screen_info_lfb_pdev = pdev;
  88. screen_info_lfb_bar = pr - pdev->resource;
  89. screen_info_lfb_offset = r.start - pr->start;
  90. screen_info_lfb_res_start = bus_region.start;
  91. }
  92. DECLARE_PCI_FIXUP_CLASS_HEADER(PCI_ANY_ID, PCI_ANY_ID, PCI_BASE_CLASS_DISPLAY, 16,
  93. screen_info_fixup_lfb);
  94. static struct pci_dev *__screen_info_pci_dev(struct resource *res)
  95. {
  96. struct pci_dev *pdev = NULL;
  97. const struct resource *r = NULL;
  98. if (!(res->flags & IORESOURCE_MEM))
  99. return NULL;
  100. while (!r && (pdev = pci_get_base_class(PCI_BASE_CLASS_DISPLAY, pdev))) {
  101. r = pci_find_resource(pdev, res);
  102. }
  103. return pdev;
  104. }
  105. /**
  106. * screen_info_pci_dev() - Return PCI parent device that contains screen_info's framebuffer
  107. * @si: the screen_info
  108. *
  109. * Returns:
  110. * The screen_info's parent device or NULL on success, or a pointer-encoded
  111. * errno value otherwise. The value NULL is not an error. It signals that no
  112. * PCI device has been found.
  113. */
  114. struct pci_dev *screen_info_pci_dev(const struct screen_info *si)
  115. {
  116. struct resource res[SCREEN_INFO_MAX_RESOURCES];
  117. ssize_t i, numres;
  118. numres = screen_info_resources(si, res, ARRAY_SIZE(res));
  119. if (numres < 0)
  120. return ERR_PTR(numres);
  121. for (i = 0; i < numres; ++i) {
  122. struct pci_dev *pdev = __screen_info_pci_dev(&res[i]);
  123. if (pdev)
  124. return pdev;
  125. }
  126. return NULL;
  127. }
  128. EXPORT_SYMBOL(screen_info_pci_dev);