screen_info_pci.c 4.0 KB

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