screen_info_pci.c 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  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_offset;
  9. static struct resource screen_info_lfb_res = DEFINE_RES_MEM(0, 0);
  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 void screen_info_fixup_lfb(struct pci_dev *pdev)
  42. {
  43. unsigned int type;
  44. struct resource res[SCREEN_INFO_MAX_RESOURCES];
  45. size_t i, numres;
  46. int ret;
  47. const struct screen_info *si = &screen_info;
  48. if (screen_info_lfb_pdev)
  49. return; // already found
  50. type = screen_info_video_type(si);
  51. if (type != VIDEO_TYPE_EFI)
  52. return; // only applies to EFI
  53. ret = screen_info_resources(si, res, ARRAY_SIZE(res));
  54. if (ret < 0)
  55. return;
  56. numres = ret;
  57. for (i = 0; i < numres; ++i) {
  58. struct resource *r = &res[i];
  59. const struct resource *pr;
  60. if (!(r->flags & IORESOURCE_MEM))
  61. continue;
  62. pr = pci_find_resource(pdev, r);
  63. if (!pr)
  64. continue;
  65. /*
  66. * We've found a PCI device with the framebuffer
  67. * resource. Store away the parameters to track
  68. * relocation of the framebuffer aperture.
  69. */
  70. screen_info_lfb_pdev = pdev;
  71. screen_info_lfb_bar = pr - pdev->resource;
  72. screen_info_lfb_offset = r->start - pr->start;
  73. memcpy(&screen_info_lfb_res, r, sizeof(screen_info_lfb_res));
  74. }
  75. }
  76. DECLARE_PCI_FIXUP_CLASS_HEADER(PCI_ANY_ID, PCI_ANY_ID, PCI_BASE_CLASS_DISPLAY, 16,
  77. screen_info_fixup_lfb);
  78. static struct pci_dev *__screen_info_pci_dev(struct resource *res)
  79. {
  80. struct pci_dev *pdev = NULL;
  81. const struct resource *r = NULL;
  82. if (!(res->flags & IORESOURCE_MEM))
  83. return NULL;
  84. while (!r && (pdev = pci_get_base_class(PCI_BASE_CLASS_DISPLAY, pdev))) {
  85. r = pci_find_resource(pdev, res);
  86. }
  87. return pdev;
  88. }
  89. /**
  90. * screen_info_pci_dev() - Return PCI parent device that contains screen_info's framebuffer
  91. * @si: the screen_info
  92. *
  93. * Returns:
  94. * The screen_info's parent device or NULL on success, or a pointer-encoded
  95. * errno value otherwise. The value NULL is not an error. It signals that no
  96. * PCI device has been found.
  97. */
  98. struct pci_dev *screen_info_pci_dev(const struct screen_info *si)
  99. {
  100. struct resource res[SCREEN_INFO_MAX_RESOURCES];
  101. ssize_t i, numres;
  102. numres = screen_info_resources(si, res, ARRAY_SIZE(res));
  103. if (numres < 0)
  104. return ERR_PTR(numres);
  105. for (i = 0; i < numres; ++i) {
  106. struct pci_dev *pdev = __screen_info_pci_dev(&res[i]);
  107. if (pdev)
  108. return pdev;
  109. }
  110. return NULL;
  111. }
  112. EXPORT_SYMBOL(screen_info_pci_dev);