1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Copyright IBM Corp. 2024
4 *
5 * Author(s):
6 * Niklas Schnelle <[email protected]>
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
zpci_state_str(pci_channel_state_t state)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
debug_log_header_fn(debug_info_t * id,struct debug_view * view,int area,debug_entry_t * entry,char * out_buf,size_t out_buf_size)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
debug_prolog_header(debug_info_t * id,struct debug_view * view,char * out_buf,size_t out_buf_size)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 */
zpci_report_status(struct zpci_dev * zdev,const char * operation,const char * status)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