1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright IBM Corp. 2024 4 * 5 * Author(s): 6 * Niklas Schnelle <schnelle@linux.ibm.com> 7 * 8 */ 9 10 #define KMSG_COMPONENT "zpci" 11 #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt 12 13 #include <linux/kernel.h> 14 #include <linux/sprintf.h> 15 #include <linux/pci.h> 16 17 #include <asm/sclp.h> 18 #include <asm/debug.h> 19 #include <asm/pci_debug.h> 20 21 #include "pci_report.h" 22 23 #define ZPCI_ERR_LOG_ID_KERNEL_REPORT 0x4714 24 25 struct zpci_report_error_data { 26 u64 timestamp; 27 u64 err_log_id; 28 char log_data[]; 29 } __packed; 30 31 #define ZPCI_REPORT_SIZE (PAGE_SIZE - sizeof(struct err_notify_sccb)) 32 #define ZPCI_REPORT_DATA_SIZE (ZPCI_REPORT_SIZE - sizeof(struct zpci_report_error_data)) 33 34 struct zpci_report_error { 35 struct zpci_report_error_header header; 36 struct zpci_report_error_data data; 37 } __packed; 38 39 static const char *zpci_state_str(pci_channel_state_t state) 40 { 41 switch (state) { 42 case pci_channel_io_normal: 43 return "normal"; 44 case pci_channel_io_frozen: 45 return "frozen"; 46 case pci_channel_io_perm_failure: 47 return "permanent-failure"; 48 default: 49 return "invalid"; 50 }; 51 } 52 53 static int debug_log_header_fn(debug_info_t *id, struct debug_view *view, 54 int area, debug_entry_t *entry, char *out_buf, 55 size_t out_buf_size) 56 { 57 unsigned long sec, usec; 58 unsigned int level; 59 char *except_str; 60 int rc = 0; 61 62 level = entry->level; 63 sec = entry->clock; 64 usec = do_div(sec, USEC_PER_SEC); 65 66 if (entry->exception) 67 except_str = "*"; 68 else 69 except_str = "-"; 70 rc += scnprintf(out_buf, out_buf_size, "%011ld:%06lu %1u %1s %04u ", 71 sec, usec, level, except_str, 72 entry->cpu); 73 return rc; 74 } 75 76 static int debug_prolog_header(debug_info_t *id, struct debug_view *view, 77 char *out_buf, size_t out_buf_size) 78 { 79 return scnprintf(out_buf, out_buf_size, "sec:usec level except cpu msg\n"); 80 } 81 82 static struct debug_view debug_log_view = { 83 "pci_msg_log", 84 &debug_prolog_header, 85 &debug_log_header_fn, 86 &debug_sprintf_format_fn, 87 NULL, 88 NULL 89 }; 90 91 /** 92 * zpci_report_status - Report the status of operations on a PCI device 93 * @zdev: The PCI device for which to report status 94 * @operation: A string representing the operation reported 95 * @status: A string representing the status of the operation 96 * 97 * This function creates a human readable report about an operation such as 98 * PCI device recovery and forwards this to the platform using the SCLP Write 99 * Event Data mechanism. Besides the operation and status strings the report 100 * also contains additional information about the device deemed useful for 101 * debug such as the currently bound device driver, if any, and error state. 102 * Additionally a string representation of pci_debug_msg_id, or as much as fits, 103 * is also included. 104 * 105 * Return: 0 on success an error code < 0 otherwise. 106 */ 107 int zpci_report_status(struct zpci_dev *zdev, const char *operation, const char *status) 108 { 109 struct zpci_report_error *report; 110 struct pci_driver *driver = NULL; 111 struct pci_dev *pdev = NULL; 112 char *buf, *end; 113 int ret; 114 115 if (!zdev || !zdev->zbus) 116 return -ENODEV; 117 118 /* Protected virtualization hosts get nothing from us */ 119 if (prot_virt_guest) 120 return -ENODATA; 121 122 report = (void *)get_zeroed_page(GFP_KERNEL); 123 if (!report) 124 return -ENOMEM; 125 if (zdev->zbus->bus) 126 pdev = pci_get_slot(zdev->zbus->bus, zdev->devfn); 127 if (pdev) 128 driver = to_pci_driver(pdev->dev.driver); 129 130 buf = report->data.log_data; 131 end = report->data.log_data + ZPCI_REPORT_DATA_SIZE; 132 buf += scnprintf(buf, end - buf, "report: %s\n", operation); 133 buf += scnprintf(buf, end - buf, "status: %s\n", status); 134 buf += scnprintf(buf, end - buf, "state: %s\n", 135 (pdev) ? zpci_state_str(pdev->error_state) : "n/a"); 136 buf += scnprintf(buf, end - buf, "driver: %s\n", (driver) ? driver->name : "n/a"); 137 ret = debug_dump(pci_debug_msg_id, &debug_log_view, buf, end - buf, true); 138 if (ret < 0) 139 pr_err("Reading PCI debug messages failed with code %d\n", ret); 140 else 141 buf += ret; 142 143 report->header.version = 1; 144 report->header.action = SCLP_ERRNOTIFY_AQ_INFO_LOG; 145 report->header.length = buf - (char *)&report->data; 146 report->data.timestamp = ktime_get_clocktai_seconds(); 147 report->data.err_log_id = ZPCI_ERR_LOG_ID_KERNEL_REPORT; 148 149 ret = sclp_pci_report(&report->header, zdev->fh, zdev->fid); 150 if (ret) 151 pr_err("Reporting PCI status failed with code %d\n", ret); 152 else 153 pr_info("Reported PCI device status\n"); 154 155 free_page((unsigned long)report); 156 157 return ret; 158 } 159