Branch data Line data Source code
1 : : // SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
2 : : /*
3 : : * Platform Error Log (PEL) generation
4 : : *
5 : : * Copyright 2014-2016 IBM Corp
6 : : */
7 : :
8 : : #include <string.h>
9 : : #include <errorlog.h>
10 : : #include <device.h>
11 : : #include <fsp.h>
12 : : #include <pel.h>
13 : : #include <rtc.h>
14 : :
15 : : /* Create MTMS section for sapphire log */
16 : 2 : static void create_mtms_section(struct errorlog *elog_data,
17 : : char *pel_buffer, int *pel_offset)
18 : : {
19 : : const struct dt_property *p;
20 : :
21 : 2 : struct opal_mtms_section *mtms = (struct opal_mtms_section *)
22 : 2 : (pel_buffer + *pel_offset);
23 : :
24 : 2 : mtms->v6header.id = cpu_to_be16(ELOG_SID_MACHINE_TYPE);
25 : 2 : mtms->v6header.length = cpu_to_be16(MTMS_SECTION_SIZE);
26 : 2 : mtms->v6header.version = OPAL_EXT_HRD_VER;
27 : 2 : mtms->v6header.subtype = 0;
28 : 2 : mtms->v6header.component_id = cpu_to_be16(elog_data->component_id);
29 : :
30 : 2 : memset(mtms->model, 0x00, sizeof(mtms->model));
31 : 2 : memcpy(mtms->model, dt_prop_get(dt_root, "model"), OPAL_SYS_MODEL_LEN);
32 : :
33 : 2 : memset(mtms->serial_no, 0x00, sizeof(mtms->serial_no));
34 : 2 : p = dt_find_property(dt_root, "system-id");
35 : 2 : if (p)
36 : 0 : memcpy(mtms->serial_no, p->prop, OPAL_SYS_SERIAL_LEN);
37 : : else
38 : 2 : memset(mtms->serial_no, 0, OPAL_SYS_SERIAL_LEN);
39 : :
40 : 2 : *pel_offset += MTMS_SECTION_SIZE;
41 : 2 : }
42 : :
43 : : /* Create extended header section */
44 : 2 : static void create_extended_header_section(struct errorlog *elog_data,
45 : : char *pel_buffer, int *pel_offset)
46 : : {
47 : 2 : const char *opalmodel = NULL;
48 : : const struct dt_property *p;
49 : : uint64_t extd_time;
50 : : uint32_t extd_date;
51 : :
52 : 2 : struct opal_extended_header_section *extdhdr =
53 : : (struct opal_extended_header_section *)
54 : 2 : (pel_buffer + *pel_offset);
55 : :
56 : 2 : extdhdr->v6header.id = cpu_to_be16(ELOG_SID_EXTENDED_HEADER);
57 : 2 : extdhdr->v6header.length = cpu_to_be16(EXTENDED_HEADER_SECTION_SIZE);
58 : 2 : extdhdr->v6header.version = OPAL_EXT_HRD_VER;
59 : 2 : extdhdr->v6header.subtype = 0;
60 : 2 : extdhdr->v6header.component_id = cpu_to_be16(elog_data->component_id);
61 : :
62 : 2 : memset(extdhdr->model, 0x00, sizeof(extdhdr->model));
63 : 2 : opalmodel = dt_prop_get(dt_root, "model");
64 : 2 : memcpy(extdhdr->model, opalmodel, OPAL_SYS_MODEL_LEN);
65 : :
66 : 2 : memset(extdhdr->serial_no, 0x00, sizeof(extdhdr->serial_no));
67 : 2 : p = dt_find_property(dt_root, "system-id");
68 : 2 : if (p)
69 : 0 : memcpy(extdhdr->serial_no, p->prop, OPAL_SYS_SERIAL_LEN);
70 : : else
71 : 2 : memset(extdhdr->serial_no, 0, OPAL_SYS_SERIAL_LEN);
72 : :
73 : 2 : memset(extdhdr->opal_release_version, 0x00,
74 : : sizeof(extdhdr->opal_release_version));
75 : 2 : memset(extdhdr->opal_subsys_version, 0x00,
76 : : sizeof(extdhdr->opal_subsys_version));
77 : :
78 : 2 : rtc_cache_get_datetime(&extd_date, &extd_time);
79 : 2 : extdhdr->extended_header_date = cpu_to_be32(extd_date);
80 : 2 : extdhdr->extended_header_time = cpu_to_be32(extd_time >> 32);
81 : 2 : extdhdr->opal_symid_len = 0;
82 : :
83 : 2 : *pel_offset += EXTENDED_HEADER_SECTION_SIZE;
84 : 2 : }
85 : :
86 : : /* set src type */
87 : 2 : static void settype(struct opal_src_section *src, uint8_t src_type)
88 : : {
89 : : char type[4];
90 : 2 : snprintf(type, sizeof(type), "%02X", src_type);
91 : 2 : memcpy(src->srcstring, type, 2);
92 : 2 : }
93 : :
94 : : /* set SRC subsystem type */
95 : 2 : static void setsubsys(struct opal_src_section *src, uint8_t src_subsys)
96 : : {
97 : : char subsys[4];
98 : 2 : snprintf(subsys, sizeof(subsys), "%02X", src_subsys);
99 : 2 : memcpy(src->srcstring+2, subsys, 2);
100 : 2 : }
101 : :
102 : : /* Ser reason code of SRC */
103 : 2 : static void setrefcode(struct opal_src_section *src, uint16_t src_refcode)
104 : : {
105 : : char refcode[8];
106 : 2 : snprintf(refcode, sizeof(refcode), "%04X", src_refcode);
107 : 2 : memcpy(src->srcstring+4, refcode, 4);
108 : 2 : }
109 : :
110 : : /* Create SRC section of OPAL log */
111 : 2 : static void create_src_section(struct errorlog *elog_data,
112 : : char *pel_buffer, int *pel_offset)
113 : : {
114 : 2 : struct opal_src_section *src = (struct opal_src_section *)
115 : 2 : (pel_buffer + *pel_offset);
116 : :
117 : 2 : src->v6header.id = cpu_to_be16(ELOG_SID_PRIMARY_SRC);
118 : 2 : src->v6header.length = cpu_to_be16(SRC_SECTION_SIZE);
119 : 2 : src->v6header.version = OPAL_ELOG_VERSION;
120 : 2 : src->v6header.subtype = OPAL_ELOG_SST;
121 : 2 : src->v6header.component_id = cpu_to_be16(elog_data->component_id);
122 : :
123 : 2 : src->version = OPAL_SRC_SEC_VER;
124 : 2 : src->flags = 0;
125 : 2 : src->wordcount = OPAL_SRC_MAX_WORD_COUNT;
126 : 2 : src->srclength = cpu_to_be16(SRC_LENGTH);
127 : 2 : settype(src, OPAL_SRC_TYPE_ERROR);
128 : 2 : setsubsys(src, OPAL_FAILING_SUBSYSTEM);
129 : 2 : setrefcode(src, elog_data->reason_code);
130 : 2 : memset(src->hexwords, 0 , (8 * 4));
131 : 2 : src->hexwords[0] = cpu_to_be32(OPAL_SRC_FORMAT);
132 : 2 : src->hexwords[4] = cpu_to_be32(elog_data->additional_info[0]);
133 : 2 : src->hexwords[5] = cpu_to_be32(elog_data->additional_info[1]);
134 : 2 : src->hexwords[6] = cpu_to_be32(elog_data->additional_info[2]);
135 : 2 : src->hexwords[7] = cpu_to_be32(elog_data->additional_info[3]);
136 : 2 : *pel_offset += SRC_SECTION_SIZE;
137 : 2 : }
138 : :
139 : : /* Create user header section */
140 : 2 : static void create_user_header_section(struct errorlog *elog_data,
141 : : char *pel_buffer, int *pel_offset)
142 : : {
143 : 2 : struct opal_user_header_section *usrhdr =
144 : : (struct opal_user_header_section *)
145 : 2 : (pel_buffer + *pel_offset);
146 : :
147 : 2 : usrhdr->v6header.id = cpu_to_be16(ELOG_SID_USER_HEADER);
148 : 2 : usrhdr->v6header.length = cpu_to_be16(USER_HEADER_SECTION_SIZE);
149 : 2 : usrhdr->v6header.version = OPAL_ELOG_VERSION;
150 : 2 : usrhdr->v6header.subtype = OPAL_ELOG_SST;
151 : 2 : usrhdr->v6header.component_id = cpu_to_be16(elog_data->component_id);
152 : :
153 : 2 : usrhdr->subsystem_id = elog_data->subsystem_id;
154 : 2 : usrhdr->event_scope = 0;
155 : 2 : usrhdr->event_severity = elog_data->event_severity;
156 : 2 : usrhdr->event_type = elog_data->event_subtype;
157 : :
158 : 2 : if (elog_data->elog_origin == ORG_SAPPHIRE)
159 : 2 : usrhdr->action_flags = cpu_to_be16(ERRL_ACTION_REPORT);
160 : : else
161 : 0 : usrhdr->action_flags = cpu_to_be16(ERRL_ACTION_NONE);
162 : :
163 : 2 : *pel_offset += USER_HEADER_SECTION_SIZE;
164 : 2 : }
165 : :
166 : : /* Create private header section */
167 : 2 : static void create_private_header_section(struct errorlog *elog_data,
168 : : char *pel_buffer, int *pel_offset)
169 : : {
170 : : uint64_t ctime;
171 : : uint32_t cdate;
172 : 2 : struct opal_private_header_section *privhdr =
173 : : (struct opal_private_header_section *)
174 : : pel_buffer;
175 : :
176 : 2 : privhdr->v6header.id = cpu_to_be16(ELOG_SID_PRIVATE_HEADER);
177 : 2 : privhdr->v6header.length = cpu_to_be16(PRIVATE_HEADER_SECTION_SIZE);
178 : 2 : privhdr->v6header.version = OPAL_ELOG_VERSION;
179 : 2 : privhdr->v6header.subtype = OPAL_ELOG_SST;
180 : 2 : privhdr->v6header.component_id = cpu_to_be16(elog_data->component_id);
181 : 2 : privhdr->plid = cpu_to_be32(elog_data->plid);
182 : :
183 : 2 : rtc_cache_get_datetime(&cdate, &ctime);
184 : 2 : privhdr->create_date = cpu_to_be32(cdate);
185 : 2 : privhdr->create_time = cpu_to_be32(ctime >> 32);
186 : 2 : privhdr->section_count = 5;
187 : :
188 : 2 : privhdr->creator_subid_hi = 0x00;
189 : 2 : privhdr->creator_subid_lo = 0x00;
190 : :
191 : 2 : if (elog_data->elog_origin == ORG_SAPPHIRE)
192 : 2 : privhdr->creator_id = OPAL_CID_SAPPHIRE;
193 : : else
194 : 0 : privhdr->creator_id = OPAL_CID_POWERNV;
195 : :
196 : 2 : privhdr->log_entry_id = cpu_to_be32(elog_data->plid); /*entry id is updated by FSP*/
197 : :
198 : 2 : *pel_offset += PRIVATE_HEADER_SECTION_SIZE;
199 : 2 : }
200 : :
201 : 1 : static void create_user_defined_section(struct errorlog *elog_data,
202 : : char *pel_buffer, int *pel_offset)
203 : : {
204 : 1 : char *dump = (char *)pel_buffer + *pel_offset;
205 : 1 : char *opal_buf = (char *)elog_data->user_data_dump;
206 : : struct opal_user_section *usrhdr;
207 : : struct elog_user_data_section *opal_usr_data;
208 : 1 : struct opal_private_header_section *privhdr =
209 : : (struct opal_private_header_section *)pel_buffer;
210 : : int i;
211 : :
212 : 2 : for (i = 0; i < elog_data->user_section_count; i++) {
213 : :
214 : 1 : usrhdr = (struct opal_user_section *)dump;
215 : 1 : opal_usr_data = (struct elog_user_data_section *)opal_buf;
216 : :
217 : 1 : usrhdr->v6header.id = cpu_to_be16(ELOG_SID_USER_DEFINED);
218 : 2 : usrhdr->v6header.length = cpu_to_be16(
219 : 1 : sizeof(struct opal_v6_header) +
220 : 1 : be16_to_cpu(opal_usr_data->size));
221 : 1 : usrhdr->v6header.version = OPAL_ELOG_VERSION;
222 : 1 : usrhdr->v6header.subtype = OPAL_ELOG_SST;
223 : 1 : usrhdr->v6header.component_id = cpu_to_be16(elog_data->component_id);
224 : :
225 : 1 : memcpy(usrhdr->dump, opal_buf, be16_to_cpu(opal_usr_data->size));
226 : 1 : *pel_offset += be16_to_cpu(usrhdr->v6header.length);
227 : 1 : dump += be16_to_cpu(usrhdr->v6header.length);
228 : 1 : opal_buf += be16_to_cpu(opal_usr_data->size);
229 : 1 : privhdr->section_count++;
230 : : }
231 : 1 : }
232 : :
233 : 6 : static size_t pel_user_section_size(struct errorlog *elog_data)
234 : : {
235 : : int i;
236 : 6 : size_t total = 0;
237 : 6 : char *opal_buf = (char *)elog_data->user_data_dump;
238 : : struct elog_user_data_section *opal_usr_data;
239 : :
240 : 8 : for (i = 0; i < elog_data->user_section_count; i++) {
241 : : u16 s;
242 : :
243 : 2 : opal_usr_data = (struct elog_user_data_section *)opal_buf;
244 : 2 : s = be16_to_cpu(opal_usr_data->size);
245 : 2 : total += sizeof(struct opal_v6_header) + s;
246 : 2 : opal_buf += s;
247 : : }
248 : :
249 : 6 : return total;
250 : : }
251 : :
252 : 6 : size_t pel_size(struct errorlog *elog_data)
253 : : {
254 : 6 : return PEL_MIN_SIZE + pel_user_section_size(elog_data);
255 : : }
256 : :
257 : : /* Converts an OPAL errorlog into a PEL formatted log */
258 : 3 : int create_pel_log(struct errorlog *elog_data, char *pel_buffer,
259 : : size_t pel_buffer_size)
260 : : {
261 : 3 : int pel_offset = 0;
262 : :
263 : 3 : if (pel_buffer_size < pel_size(elog_data)) {
264 : 1 : prerror("PEL buffer too small to create record\n");
265 : 1 : return 0;
266 : : }
267 : :
268 : 2 : memset(pel_buffer, 0, pel_buffer_size);
269 : :
270 : 2 : create_private_header_section(elog_data, pel_buffer, &pel_offset);
271 : 2 : create_user_header_section(elog_data, pel_buffer, &pel_offset);
272 : 2 : create_src_section(elog_data, pel_buffer, &pel_offset);
273 : 2 : create_extended_header_section(elog_data, pel_buffer, &pel_offset);
274 : 2 : create_mtms_section(elog_data, pel_buffer, &pel_offset);
275 : 2 : if (elog_data->user_section_count)
276 : 1 : create_user_defined_section(elog_data, pel_buffer, &pel_offset);
277 : :
278 : 2 : return pel_offset;
279 : : }
|