1 /* Copyright 2013-2017 IBM Corp.
2  *
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *	http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
12  * implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 /* This file contains the front end for OPAL error logging. It is used
18  * to construct a struct errorlog representing the event/error to be
19  * logged which is then passed to the platform specific backend to log
20  * the actual errors.
21  */
22 #include <skiboot.h>
23 #include <lock.h>
24 #include <errorlog.h>
25 #include <pool.h>
26 
27 /*
28  * Maximum number buffers that are pre-allocated
29  * to hold elogs that are reported on Sapphire and
30  * PowerNV.
31  */
32 #define ELOG_WRITE_MAX_RECORD		64
33 /* Platform log id as per the spec */
34 static uint32_t sapphire_elog_id = 0xB0000000;
35 
36 /* Reserved for future use */
37 /* static uint32_t powernv_elog_id = 0xB1000000; */
38 
39 /* Pool to allocate elog messages from */
40 static struct pool elog_pool;
41 static struct lock elog_lock = LOCK_UNLOCKED;
42 
43 static bool elog_available = false;
44 
get_write_buffer(int opal_event_severity)45 static struct errorlog *get_write_buffer(int opal_event_severity)
46 {
47 	struct errorlog *buf;
48 
49 	if (!elog_available)
50 		return NULL;
51 
52 	lock(&elog_lock);
53 	if (opal_event_severity == OPAL_ERROR_PANIC)
54 		buf = pool_get(&elog_pool, POOL_HIGH);
55 	else
56 		buf = pool_get(&elog_pool, POOL_NORMAL);
57 
58 	unlock(&elog_lock);
59 	return buf;
60 }
61 
62 /* Reporting of error via struct errorlog */
opal_elog_create(struct opal_err_info * e_info,uint32_t tag)63 struct errorlog *opal_elog_create(struct opal_err_info *e_info, uint32_t tag)
64 {
65 	struct errorlog *buf;
66 
67 	buf = get_write_buffer(e_info->sev);
68 	if (buf) {
69 		buf->error_event_type = e_info->err_type;
70 		buf->component_id = e_info->cmp_id;
71 		buf->subsystem_id = e_info->subsystem;
72 		buf->event_severity = e_info->sev;
73 		buf->event_subtype = e_info->event_subtype;
74 		buf->reason_code = e_info->reason_code;
75 		buf->elog_origin = ORG_SAPPHIRE;
76 
77 		lock(&elog_lock);
78 		buf->plid = ++sapphire_elog_id;
79 		unlock(&elog_lock);
80 
81 		/* Initialise the first user dump section */
82 		log_add_section(buf, tag);
83 	}
84 
85 	return buf;
86 }
87 
88 /* Add a new user data section to an existing error log */
log_add_section(struct errorlog * buf,uint32_t tag)89 void log_add_section(struct errorlog *buf, uint32_t tag)
90 {
91 	size_t size = sizeof(struct elog_user_data_section) - 1;
92 	struct elog_user_data_section *tmp;
93 
94 	if (!buf) {
95 		prerror("ELOG: Cannot add user data section. "
96 			"Buffer is invalid\n");
97 		return;
98 	}
99 
100 	if ((buf->user_section_size + size) > OPAL_LOG_MAX_DUMP) {
101 		prerror("ELOG: Size of dump data overruns buffer\n");
102 		return;
103 	}
104 
105 	tmp = (struct elog_user_data_section *)(buf->user_data_dump +
106 						buf->user_section_size);
107 	/* Use DESC if no other tag provided */
108 	tmp->tag = tag ? tag : 0x44455343;
109 	tmp->size = size;
110 
111 	buf->user_section_size += tmp->size;
112 	buf->user_section_count++;
113 }
114 
opal_elog_complete(struct errorlog * buf,bool success)115 void opal_elog_complete(struct errorlog *buf, bool success)
116 {
117 	if (!success)
118 		printf("Unable to log error\n");
119 
120 	lock(&elog_lock);
121 	pool_free_object(&elog_pool, buf);
122 	unlock(&elog_lock);
123 }
124 
log_commit(struct errorlog * elog)125 void log_commit(struct errorlog *elog)
126 {
127 	int rc;
128 
129 	if (!elog)
130 		return;
131 
132 	if (platform.elog_commit) {
133 		rc = platform.elog_commit(elog);
134 		if (rc)
135 			prerror("ELOG: Platform commit error %d\n", rc);
136 
137 		return;
138 	}
139 
140 	opal_elog_complete(elog, false);
141 }
142 
log_append_data(struct errorlog * buf,unsigned char * data,uint16_t size)143 void log_append_data(struct errorlog *buf, unsigned char *data, uint16_t size)
144 {
145 	struct elog_user_data_section *section;
146 	uint8_t n_sections;
147 	char *buffer;
148 
149 	if (!buf) {
150 		prerror("ELOG: Cannot update user data. Buffer is invalid\n");
151 		return;
152 	}
153 
154 	if ((buf->user_section_size + size) > OPAL_LOG_MAX_DUMP) {
155 		prerror("ELOG: Size of dump data overruns buffer\n");
156 		return;
157 	}
158 
159 	/* Step through user sections to find latest dump section */
160 	buffer = buf->user_data_dump;
161 	n_sections = buf->user_section_count;
162 	if (!n_sections) {
163 		prerror("ELOG: User section invalid\n");
164 		return;
165 	}
166 
167 	while (--n_sections) {
168 		section = (struct elog_user_data_section *)buffer;
169 		buffer += section->size;
170 	}
171 
172 	section = (struct elog_user_data_section *)buffer;
173 	buffer += section->size;
174 	memcpy(buffer, data, size);
175 	section->size += size;
176 	buf->user_section_size += size;
177 }
178 
log_append_msg(struct errorlog * buf,const char * fmt,...)179 void log_append_msg(struct errorlog *buf, const char *fmt, ...)
180 {
181 	char err_msg[250];
182 	va_list list;
183 
184 	if (!buf) {
185 		prerror("Tried to append log to NULL buffer\n");
186 		return;
187 	}
188 
189 	va_start(list, fmt);
190 	vsnprintf(err_msg, sizeof(err_msg), fmt, list);
191 	va_end(list);
192 
193 	/* Log the error on to Sapphire console */
194 	prerror("%s", err_msg);
195 
196 	log_append_data(buf, err_msg, strlen(err_msg));
197 }
198 
log_simple_error(struct opal_err_info * e_info,const char * fmt,...)199 uint32_t log_simple_error(struct opal_err_info *e_info, const char *fmt, ...)
200 {
201 	struct errorlog *buf;
202 	va_list list;
203 	char err_msg[250];
204 
205 	va_start(list, fmt);
206 	vsnprintf(err_msg, sizeof(err_msg), fmt, list);
207 	va_end(list);
208 
209 	/* Log the error on to Sapphire console */
210 	prerror("%s", err_msg);
211 
212 	buf = opal_elog_create(e_info, 0);
213 	if (buf == NULL) {
214 		prerror("ELOG: Error getting buffer to log error\n");
215 		return -1;
216 	}
217 
218 	log_append_data(buf, err_msg, strlen(err_msg));
219 	log_commit(buf);
220 
221 	return buf->plid;
222 }
223 
elog_init(void)224 int elog_init(void)
225 {
226 	/* Pre-allocate memory for records */
227 	if (pool_init(&elog_pool, sizeof(struct errorlog),
228 					ELOG_WRITE_MAX_RECORD, 1))
229 		return OPAL_RESOURCE;
230 
231 	elog_available = true;
232 	return 0;
233 }
234