ovmf-debug-log.c 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. // SPDX-License-Identifier: GPL-2.0
  2. #include <linux/efi.h>
  3. #include <linux/init.h>
  4. #include <linux/io.h>
  5. #include <linux/kernel.h>
  6. #include <linux/kobject.h>
  7. #include <linux/module.h>
  8. #include <linux/platform_device.h>
  9. #include <linux/sysfs.h>
  10. #define OVMF_DEBUG_LOG_MAGIC1 0x3167646d666d766f // "ovmfmdg1"
  11. #define OVMF_DEBUG_LOG_MAGIC2 0x3267646d666d766f // "ovmfmdg2"
  12. struct ovmf_debug_log_header {
  13. u64 magic1;
  14. u64 magic2;
  15. u64 hdr_size;
  16. u64 log_size;
  17. u64 lock; // edk2 spinlock
  18. u64 head_off;
  19. u64 tail_off;
  20. u64 truncated;
  21. u8 fw_version[128];
  22. };
  23. static struct ovmf_debug_log_header *hdr;
  24. static u8 *logbuf;
  25. static u64 logbufsize;
  26. static ssize_t ovmf_log_read(struct file *filp, struct kobject *kobj,
  27. const struct bin_attribute *attr, char *buf,
  28. loff_t offset, size_t count)
  29. {
  30. u64 start, end;
  31. start = hdr->head_off + offset;
  32. if (hdr->head_off > hdr->tail_off && start >= hdr->log_size)
  33. start -= hdr->log_size;
  34. end = start + count;
  35. if (start > hdr->tail_off) {
  36. if (end > hdr->log_size)
  37. end = hdr->log_size;
  38. } else {
  39. if (end > hdr->tail_off)
  40. end = hdr->tail_off;
  41. }
  42. if (start > logbufsize || end > logbufsize)
  43. return 0;
  44. if (start >= end)
  45. return 0;
  46. memcpy(buf, logbuf + start, end - start);
  47. return end - start;
  48. }
  49. static struct bin_attribute ovmf_log_bin_attr = {
  50. .attr = {
  51. .name = "ovmf_debug_log",
  52. .mode = 0444,
  53. },
  54. .read = ovmf_log_read,
  55. };
  56. int __init ovmf_log_probe(unsigned long ovmf_debug_log_table)
  57. {
  58. int ret = -EINVAL;
  59. u64 size;
  60. /* map + verify header */
  61. hdr = memremap(ovmf_debug_log_table, sizeof(*hdr), MEMREMAP_WB);
  62. if (!hdr) {
  63. pr_err("OVMF debug log: header map failed\n");
  64. return -EINVAL;
  65. }
  66. if (hdr->magic1 != OVMF_DEBUG_LOG_MAGIC1 ||
  67. hdr->magic2 != OVMF_DEBUG_LOG_MAGIC2) {
  68. printk(KERN_ERR "OVMF debug log: magic mismatch\n");
  69. goto err_unmap;
  70. }
  71. size = hdr->hdr_size + hdr->log_size;
  72. pr_info("OVMF debug log: firmware version: \"%s\"\n", hdr->fw_version);
  73. pr_info("OVMF debug log: buffer size: %lluk\n", size / 1024);
  74. /* map complete log buffer */
  75. memunmap(hdr);
  76. hdr = memremap(ovmf_debug_log_table, size, MEMREMAP_WB);
  77. if (!hdr) {
  78. pr_err("OVMF debug log: buffer map failed\n");
  79. return -EINVAL;
  80. }
  81. logbuf = (void *)hdr + hdr->hdr_size;
  82. logbufsize = hdr->log_size;
  83. ovmf_log_bin_attr.size = size;
  84. ret = sysfs_create_bin_file(efi_kobj, &ovmf_log_bin_attr);
  85. if (ret != 0) {
  86. pr_err("OVMF debug log: sysfs register failed\n");
  87. goto err_unmap;
  88. }
  89. return 0;
  90. err_unmap:
  91. memunmap(hdr);
  92. return ret;
  93. }