blob: 7786d3b1edbfc5e60b9cb67c823f56be7dc09157 [file] [log] [blame]
Duncan Laurie7d2b81c2012-06-23 16:08:47 -07001/*
2 * This file is part of the coreboot project.
3 *
4 * Copyright (C) 2012 The ChromiumOS Authors. All rights reserved.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; version 2 of the License.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA, 02110-1301 USA
18 */
19
20#include <arch/acpi.h>
Duncan Laurie215f27852012-10-10 14:34:49 -070021#include <cbmem.h>
Duncan Laurie7d2b81c2012-06-23 16:08:47 -070022#include <console/console.h>
23#include <pc80/mc146818rtc.h>
Duncan Laurie472ec9c2012-06-23 16:13:42 -070024#include <smbios.h>
Zheng Bao600784e2013-02-07 17:30:23 +080025#include <spi-generic.h>
Duncan Laurie7d2b81c2012-06-23 16:08:47 -070026#include <spi_flash.h>
27#include <stdint.h>
28#include <string.h>
29#include <elog.h>
30#include "elog_internal.h"
31
Duncan Laurie86bf3f52012-08-15 13:14:58 -070032#if CONFIG_CHROMEOS
33#include <vendorcode/google/chromeos/fmap.h>
34#elif CONFIG_ELOG_FLASH_BASE == 0
Duncan Laurie7d2b81c2012-06-23 16:08:47 -070035#error "CONFIG_ELOG_FLASH_BASE is invalid"
36#endif
37#if CONFIG_ELOG_FULL_THRESHOLD >= CONFIG_ELOG_AREA_SIZE
38#error "CONFIG_ELOG_FULL_THRESHOLD is larger than CONFIG_ELOG_AREA_SIZE"
39#endif
40#if (CONFIG_ELOG_AREA_SIZE - CONFIG_ELOG_FULL_THRESHOLD) < (MAX_EVENT_SIZE + 1)
41#error "CONFIG_ELOG_FULL_THRESHOLD is too small"
42#endif
43#if CONFIG_ELOG_SHRINK_SIZE >= CONFIG_ELOG_AREA_SIZE
44#error "CONFIG_ELOG_SHRINK_SIZE is larger than CONFIG_ELOG_AREA_SIZE"
45#endif
46#if (CONFIG_ELOG_AREA_SIZE - CONFIG_ELOG_SHRINK_SIZE) > \
47 CONFIG_ELOG_FULL_THRESHOLD
48#error "CONFIG_ELOG_SHRINK_SIZE is too large"
49#endif
50
51#if CONFIG_ELOG_DEBUG
52#define elog_debug(STR...) printk(BIOS_DEBUG, STR)
53#else
54#define elog_debug(STR...)
55#endif
56
57/*
58 * Static variables for ELOG state
59 */
60static int elog_initialized;
61static struct spi_flash *elog_spi;
62static struct elog_descriptor elog_flash_area;
Duncan Laurie7d2b81c2012-06-23 16:08:47 -070063
64static inline struct elog_descriptor* elog_get_flash(void)
65{
66 return &elog_flash_area;
67}
68
69/*
70 * Convert a memory mapped flash address into a flash offset
71 */
72static inline u32 elog_flash_address_to_offset(u8 *address)
73{
74 if (!elog_spi)
75 return 0;
76 return (u32)address - ((u32)~0UL - elog_spi->size + 1);
77}
78
79/*
80 * Convert a flash offset into a memory mapped flash address
81 */
82static inline u8* elog_flash_offset_to_address(u32 offset)
83{
84 if (!elog_spi)
85 return NULL;
86 return (u8*)((u32)~0UL - elog_spi->size + 1 + offset);
87}
88
89/*
90 * The ELOG header is at the very beginning of the area
91 */
92static inline struct elog_header*
93elog_get_header(struct elog_descriptor *elog)
94{
95 return elog->backing_store;
96}
97
98/*
99 * Pointer to an event log header in the event data area
100 */
101static inline struct event_header*
102elog_get_event_base(struct elog_descriptor *elog, u32 offset)
103{
104 return (struct event_header *)&elog->data[offset];
105}
106
107/*
108 * Pointer to where the next event should be stored
109 */
110static inline struct event_header*
111elog_get_next_event_base(struct elog_descriptor *elog)
112{
113 return elog_get_event_base(elog, elog->next_event_offset);
114}
115
116/*
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700117 * Update the checksum at the last byte
118 */
119static void elog_update_checksum(struct event_header *event, u8 checksum)
120{
121 u8 *event_data = (u8*)event;
122 event_data[event->length - 1] = checksum;
123}
124
125/*
126 * Simple byte checksum for events
127 */
128static u8 elog_checksum_event(struct event_header *event)
129{
130 u8 index, checksum = 0;
131 u8 *data = (u8*)event;
132
133 for (index = 0; index < event->length; index++)
134 checksum += data[index];
135 return checksum;
136}
137
138/*
139 * Check if a raw buffer is filled with ELOG_TYPE_EOL byte
140 */
141static int elog_is_buffer_clear(u8 *base, u32 size)
142{
143 u8 *current = base;
144 u8 *end = current + size;
145
146 elog_debug("elog_is_buffer_clear(base=0x%p size=%u)\n", base, size);
147
148 for (; current != end; current++) {
149 if (*current != ELOG_TYPE_EOL)
150 return 0;
151 }
152 return 1;
153}
154
155/*
156 * Verify whether ELOG area is filled with ELOG_TYPE_EOL byte
157 */
158static int elog_is_area_clear(struct elog_descriptor *elog)
159{
160 return elog_is_buffer_clear(elog->backing_store, elog->total_size);
161}
162
163/*
164 * Check that the ELOG area has been initialized and is valid.
165 */
166static int elog_is_area_valid(struct elog_descriptor *elog)
167{
168 elog_debug("elog_is_area_valid()\n");
169
170 if (elog->area_state != ELOG_AREA_HAS_CONTENT)
171 return 0;
172 if (elog->header_state != ELOG_HEADER_VALID)
173 return 0;
174 if (elog->event_buffer_state != ELOG_EVENT_BUFFER_OK)
175 return 0;
176 return 1;
177}
178
179/*
180 * Verify the contents of an ELOG Header structure
181 * Returns 1 if the header is valid, 0 otherwise
182 */
183static int elog_is_header_valid(struct elog_header *header)
184{
185 elog_debug("elog_is_header_valid()\n");
186
187 if (header->magic != ELOG_SIGNATURE) {
188 printk(BIOS_ERR, "ELOG: header magic 0x%X != 0x%X\n",
189 header->magic, ELOG_SIGNATURE);
190 return 0;
191 }
192 if (header->version != ELOG_VERSION) {
193 printk(BIOS_ERR, "ELOG: header version %u != %u\n",
194 header->version, ELOG_VERSION);
195 return 0;
196 }
197 if (header->header_size != sizeof(*header)) {
Denis 'GNUtoo' Cariklibc2c9ef2013-06-26 20:04:49 +0200198 printk(BIOS_ERR, "ELOG: header size mismatch %u != %zu\n",
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700199 header->header_size, sizeof(*header));
200 return 0;
201 }
202 return 1;
203}
204
205/*
206 * Validate the event header and data.
207 */
208static int elog_is_event_valid(struct elog_descriptor *elog, u32 offset)
209{
210 struct event_header *event;
211
212 event = elog_get_event_base(elog, offset);
213 if (!event)
214 return 0;
215
216 /* Validate event length */
217 if ((offsetof(struct event_header, type) +
218 sizeof(event->type) - 1 + offset) >= elog->data_size)
219 return 0;
220
221 /* End of event marker has been found */
222 if (event->type == ELOG_TYPE_EOL)
223 return 0;
224
225 /* Check if event fits in area */
226 if ((offsetof(struct event_header, length) +
227 sizeof(event->length) - 1 + offset) >= elog->data_size)
228 return 0;
229
230 /*
231 * If the current event length + the current offset exceeds
232 * the area size then the event area is corrupt.
233 */
234 if ((event->length + offset) >= elog->data_size)
235 return 0;
236
237 /* Event length must be at least header size + checksum */
238 if (event->length < (sizeof(*event) + 1))
239 return 0;
240
241 /* If event checksum is invalid the area is corrupt */
242 if (elog_checksum_event(event) != 0)
243 return 0;
244
245 /* Event is valid */
246 return 1;
247}
248
249/*
Gabe Black331eb082013-04-24 04:11:40 -0700250 * Write 'size' bytes of data pointed to by 'address' in the flash backing
251 * store into flash. This will not erase the flash and it assumes the flash
252 * area has been erased appropriately.
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700253 */
Gabe Black331eb082013-04-24 04:11:40 -0700254static void elog_flash_write(u8 *address, u32 size)
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700255{
Duncan Laurie215f27852012-10-10 14:34:49 -0700256 struct elog_descriptor *flash = elog_get_flash();
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700257 u32 offset;
258
Gabe Black331eb082013-04-24 04:11:40 -0700259 if (!address || !size || !elog_spi)
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700260 return;
261
Duncan Laurie215f27852012-10-10 14:34:49 -0700262 offset = flash->flash_base;
263 offset += address - (u8*)flash->backing_store;
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700264
Gabe Black331eb082013-04-24 04:11:40 -0700265 elog_debug("elog_flash_write(address=0x%p offset=0x%08x size=%u)\n",
266 address, offset, size);
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700267
268 /* Write the data to flash */
Gabe Black331eb082013-04-24 04:11:40 -0700269 elog_spi->write(elog_spi, offset, size, address);
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700270}
271
272/*
273 * Erase the first block specified in the address.
274 * Only handles flash area within a single flash block.
275 */
276static void elog_flash_erase(u8 *address, u32 size)
277{
Duncan Laurie215f27852012-10-10 14:34:49 -0700278 struct elog_descriptor *flash = elog_get_flash();
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700279 u32 offset;
280
281 if (!address || !size || !elog_spi)
282 return;
283
Duncan Laurie215f27852012-10-10 14:34:49 -0700284 offset = flash->flash_base;
285 offset += address - (u8*)flash->backing_store;
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700286
287 elog_debug("elog_flash_erase(address=0x%p offset=0x%08x size=%u)\n",
288 address, offset, size);
289
290 /* Erase the sectors in this region */
291 elog_spi->erase(elog_spi, offset, size);
292}
293
294/*
295 * Scan the event area and validate each entry and
296 * update the ELOG descriptor state.
297 */
298static void elog_update_event_buffer_state(struct elog_descriptor *elog)
299{
300 u32 count = 0;
301 u32 offset = 0;
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700302 struct event_header *event;
303
304 elog_debug("elog_update_event_buffer_state()\n");
305
306 /* Go through each event and validate it */
307 while (1) {
308 event = elog_get_event_base(elog, offset);
309
310 /* Do not de-reference anything past the area length */
311 if ((offsetof(struct event_header, type) +
312 sizeof(event->type) - 1 + offset) >= elog->data_size) {
313 elog->event_buffer_state = ELOG_EVENT_BUFFER_CORRUPTED;
314 break;
315 }
316
317 /* The end of the event marker has been found */
318 if (event->type == ELOG_TYPE_EOL)
319 break;
320
321 /* Validate the event */
322 if (!elog_is_event_valid(elog, offset)) {
323 elog->event_buffer_state = ELOG_EVENT_BUFFER_CORRUPTED;
324 break;
325 }
326
327 /* Move to the next event */
328 count++;
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700329 offset += event->length;
330 }
331
332 /* Ensure the remaining buffer is empty */
333 if (!elog_is_buffer_clear(&elog->data[offset],
334 elog->data_size - offset))
335 elog->event_buffer_state = ELOG_EVENT_BUFFER_CORRUPTED;
336
337 /* Update data into elog descriptor */
338 elog->event_count = count;
339 elog->next_event_offset = offset;
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700340}
341
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700342/*
Gabe Black42cb7092013-04-24 17:36:53 -0700343 * (Re)initialize a new ELOG descriptor
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700344 */
Gabe Black84a93d12013-04-24 23:31:41 -0700345static void elog_scan_flash(struct elog_descriptor *elog)
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700346{
Gabe Black84a93d12013-04-24 23:31:41 -0700347 elog_debug("elog_scan_flash()\n");
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700348
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700349 elog->area_state = ELOG_AREA_UNDEFINED;
350 elog->header_state = ELOG_HEADER_INVALID;
351 elog->event_buffer_state = ELOG_EVENT_BUFFER_OK;
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700352
Duncan Laurie215f27852012-10-10 14:34:49 -0700353 /* Fill memory buffer by reading from SPI */
Gabe Black42cb7092013-04-24 17:36:53 -0700354 elog_spi->read(elog_spi, elog->flash_base, elog->total_size,
355 elog->backing_store);
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700356
357 elog->next_event_offset = 0;
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700358 elog->event_count = 0;
359
Gabe Black455c68e2013-04-24 17:54:37 -0700360 /* Check if the area is empty or not */
361 if (elog_is_area_clear(elog)) {
362 elog->area_state = ELOG_AREA_EMPTY;
363 return;
364 }
365
366 elog->area_state = ELOG_AREA_HAS_CONTENT;
367
368 /* Validate the header */
369 if (!elog_is_header_valid(elog_get_header(elog))) {
370 elog->header_state = ELOG_HEADER_INVALID;
371 return;
372 }
373
374 elog->header_state = ELOG_HEADER_VALID;
375 elog_update_event_buffer_state(elog);
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700376}
377
Gabe Black331eb082013-04-24 04:11:40 -0700378static void elog_prepare_empty(struct elog_descriptor *elog)
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700379{
380 struct elog_header *header;
381
382 elog_debug("elog_prepare_empty(%u bytes)\n", data_size);
383
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700384 /* Write out the header */
Gabe Blacke62e0362013-04-23 19:36:01 -0700385 header = elog_get_header(elog);
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700386 header->magic = ELOG_SIGNATURE;
387 header->version = ELOG_VERSION;
388 header->header_size = sizeof(struct elog_header);
389 header->reserved[0] = ELOG_TYPE_EOL;
390 header->reserved[1] = ELOG_TYPE_EOL;
Gabe Black331eb082013-04-24 04:11:40 -0700391 elog_flash_write(elog->backing_store, header->header_size);
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700392
Gabe Black84a93d12013-04-24 23:31:41 -0700393 elog_scan_flash(elog);
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700394}
395
396/*
397 * Shrink the log, deleting old entries and moving the
Martin Roth56889792013-07-09 21:39:46 -0600398 * remaining ones to the front of the log.
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700399 */
400static int elog_shrink(void)
401{
Gabe Blackbfae4aa2013-04-24 03:16:21 -0700402 struct elog_descriptor *flash = elog_get_flash();
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700403 struct event_header *event;
404 u16 discard_count = 0;
405 u16 offset = 0;
Gabe Blackbfae4aa2013-04-24 03:16:21 -0700406 u16 new_size = 0;
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700407
408 elog_debug("elog_shrink()\n");
409
Gabe Black331eb082013-04-24 04:11:40 -0700410 if (flash->next_event_offset < CONFIG_ELOG_SHRINK_SIZE)
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700411 return 0;
412
413 while (1) {
414 /* Next event has exceeded constraints */
415 if (offset > CONFIG_ELOG_SHRINK_SIZE)
416 break;
417
Gabe Black331eb082013-04-24 04:11:40 -0700418 event = elog_get_event_base(flash, offset);
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700419
420 /* Reached the end of the area */
421 if (!event || event->type == ELOG_TYPE_EOL)
422 break;
423
424 offset += event->length;
425 discard_count++;
426 }
427
Gabe Black331eb082013-04-24 04:11:40 -0700428 new_size = flash->next_event_offset - offset;
429 memmove(&flash->data[0], &flash->data[offset], new_size);
430 memset(&flash->data[new_size], ELOG_TYPE_EOL,
431 flash->data_size - new_size);
Gabe Blackbfae4aa2013-04-24 03:16:21 -0700432
433 elog_flash_erase(flash->backing_store, flash->total_size);
Gabe Black331eb082013-04-24 04:11:40 -0700434 elog_flash_write(flash->backing_store, flash->total_size);
Gabe Black84a93d12013-04-24 23:31:41 -0700435 elog_scan_flash(flash);
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700436
Duncan Laurie032be822013-05-23 07:23:09 -0700437 /* Ensure the area was successfully erased */
Gabe Black331eb082013-04-24 04:11:40 -0700438 if (flash->next_event_offset >= CONFIG_ELOG_FULL_THRESHOLD) {
Duncan Laurie032be822013-05-23 07:23:09 -0700439 printk(BIOS_ERR, "ELOG: Flash area was not erased!\n");
440 return -1;
441 }
442
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700443 /* Add clear event */
444 elog_add_event_word(ELOG_TYPE_LOG_CLEAR, offset);
445
446 return 0;
447}
448
Duncan Laurie215f27852012-10-10 14:34:49 -0700449#ifndef __SMM__
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700450/*
Duncan Laurie472ec9c2012-06-23 16:13:42 -0700451 * Fill out SMBIOS Type 15 table entry so the
452 * event log can be discovered at runtime.
453 */
454int elog_smbios_write_type15(unsigned long *current, int handle)
455{
Duncan Laurie215f27852012-10-10 14:34:49 -0700456 struct elog_descriptor *flash = elog_get_flash();
Duncan Laurie472ec9c2012-06-23 16:13:42 -0700457 struct smbios_type15 *t = (struct smbios_type15 *)*current;
458 int len = sizeof(struct smbios_type15);
459
Duncan Laurie215f27852012-10-10 14:34:49 -0700460#if CONFIG_ELOG_CBMEM
461 /* Save event log buffer into CBMEM for the OS to read */
462 void *cbmem = cbmem_add(CBMEM_ID_ELOG, flash->total_size);
463 if (!cbmem)
464 return 0;
465 memcpy(cbmem, flash->backing_store, flash->total_size);
466#endif
467
Duncan Laurie472ec9c2012-06-23 16:13:42 -0700468 memset(t, 0, len);
469 t->type = SMBIOS_EVENT_LOG;
470 t->length = len - 2;
471 t->handle = handle;
Duncan Laurie215f27852012-10-10 14:34:49 -0700472 t->area_length = flash->total_size - 1;
Duncan Laurie472ec9c2012-06-23 16:13:42 -0700473 t->header_offset = 0;
474 t->data_offset = sizeof(struct elog_header);
475 t->access_method = SMBIOS_EVENTLOG_ACCESS_METHOD_MMIO32;
476 t->log_status = SMBIOS_EVENTLOG_STATUS_VALID;
477 t->change_token = 0;
Duncan Laurie215f27852012-10-10 14:34:49 -0700478#if CONFIG_ELOG_CBMEM
479 t->address = (u32)cbmem;
480#else
481 t->address = (u32)elog_flash_offset_to_address(flash->flash_base);
482#endif
Duncan Laurie472ec9c2012-06-23 16:13:42 -0700483 t->header_format = ELOG_HEADER_TYPE_OEM;
484 t->log_type_descriptors = 0;
485 t->log_type_descriptor_length = 2;
486
487 *current += len;
488 return len;
489}
Duncan Laurie215f27852012-10-10 14:34:49 -0700490#endif
Duncan Laurie472ec9c2012-06-23 16:13:42 -0700491
492/*
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700493 * Clear the entire event log
494 */
495int elog_clear(void)
496{
497 struct elog_descriptor *flash = elog_get_flash();
498
499 elog_debug("elog_clear()\n");
500
Gabe Black8f4baec2013-04-25 17:21:58 -0700501 /* Make sure ELOG structures are initialized */
502 if (elog_init() < 0)
503 return -1;
504
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700505 /* Erase flash area */
Gabe Black84a93d12013-04-24 23:31:41 -0700506 elog_flash_erase(flash->backing_store, flash->total_size);
Gabe Black331eb082013-04-24 04:11:40 -0700507 elog_prepare_empty(flash);
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700508
Gabe Black84a93d12013-04-24 23:31:41 -0700509 if (!elog_is_area_valid(flash))
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700510 return -1;
511
512 /* Log the clear event */
513 elog_add_event_word(ELOG_TYPE_LOG_CLEAR, flash->total_size);
514
515 return 0;
516}
517
Gabe Black84a93d12013-04-24 23:31:41 -0700518static void elog_find_flash(u32 *base, int *size)
519{
520#if CONFIG_CHROMEOS
521 u8 *flash_base_ptr;
522#endif
523
524 elog_debug("elog_find_flash(base = %p, size = %p)\n", base, size);
525
526#if CONFIG_CHROMEOS
527 /* Find the ELOG base and size in FMAP */
528 *size = find_fmap_entry("RW_ELOG", (void **)&flash_base_ptr);
529 if (*size < 0) {
530 printk(BIOS_WARNING, "ELOG: Unable to find RW_ELOG in FMAP, "
531 "using CONFIG_ELOG_FLASH_BASE instead\n");
532 *size = CONFIG_ELOG_AREA_SIZE;
533 } else {
534 *base = elog_flash_address_to_offset(flash_base_ptr);
535
536 /* Use configured size if smaller than FMAP size */
537 if (*size > CONFIG_ELOG_AREA_SIZE)
538 *size = CONFIG_ELOG_AREA_SIZE;
539 }
540#else
541 *base = CONFIG_ELOG_FLASH_BASE;
542 *size = CONFIG_ELOG_AREA_SIZE;
543#endif
544}
545
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700546/*
547 * Event log main entry point
548 */
549int elog_init(void)
550{
Gabe Black84a93d12013-04-24 23:31:41 -0700551 struct elog_descriptor *flash = elog_get_flash();
Duncan Laurie86bf3f52012-08-15 13:14:58 -0700552 u32 flash_base = CONFIG_ELOG_FLASH_BASE;
553 int flash_size = CONFIG_ELOG_AREA_SIZE;
Duncan Laurie86bf3f52012-08-15 13:14:58 -0700554
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700555 if (elog_initialized)
556 return 0;
557
558 elog_debug("elog_init()\n");
559
Gabe Black84a93d12013-04-24 23:31:41 -0700560 /* Prepare SPI */
561 spi_init();
562 elog_spi = spi_flash_probe(0, 0, 0, 0);
563 if (!elog_spi) {
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700564 printk(BIOS_ERR, "ELOG: Unable to find SPI flash\n");
565 return -1;
566 }
567
Gabe Black84a93d12013-04-24 23:31:41 -0700568 /* Set up the backing store */
569 elog_find_flash(&flash_base, &flash_size);
570 if (flash_base == 0) {
571 printk(BIOS_ERR, "ELOG: Invalid flash base\n");
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700572 return -1;
573 }
574
Gabe Black84a93d12013-04-24 23:31:41 -0700575 flash->backing_store = malloc(flash_size);
576 if (!flash->backing_store) {
577 printk(BIOS_ERR, "ELOG: Unable to allocate backing store\n");
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700578 return -1;
579 }
Gabe Black84a93d12013-04-24 23:31:41 -0700580 flash->flash_base = flash_base;
581 flash->total_size = flash_size;
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700582
Gabe Black84a93d12013-04-24 23:31:41 -0700583 /* Data starts immediately after header */
584 flash->data = flash->backing_store + sizeof(struct elog_header);
585 flash->data_size = flash_size - sizeof(struct elog_header);
586
587 /* Load the log from flash */
588 elog_scan_flash(flash);
589
590 /* Prepare the flash if necessary */
591 if (flash->header_state == ELOG_HEADER_INVALID ||
592 flash->event_buffer_state == ELOG_EVENT_BUFFER_CORRUPTED) {
593 /* If the header is invalid or the events are corrupted,
594 * no events can be salvaged so erase the entire area. */
595 printk(BIOS_ERR, "ELOG: flash area invalid\n");
596 elog_flash_erase(flash->backing_store, flash->total_size);
597 elog_prepare_empty(flash);
598 }
599
600 if (flash->area_state == ELOG_AREA_EMPTY)
601 elog_prepare_empty(flash);
602
603 if (!elog_is_area_valid(flash)) {
604 printk(BIOS_ERR, "ELOG: Unable to prepare flash\n");
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700605 return -1;
606 }
607
608 elog_initialized = 1;
609
Gabe Black331eb082013-04-24 04:11:40 -0700610 printk(BIOS_INFO, "ELOG: FLASH @0x%p [SPI 0x%08x]\n",
Gabe Black84a93d12013-04-24 23:31:41 -0700611 flash->backing_store, flash->flash_base);
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700612
Gabe Black84a93d12013-04-24 23:31:41 -0700613 printk(BIOS_INFO, "ELOG: area is %d bytes, full threshold %d,"
614 " shrink size %d\n", flash_size,
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700615 CONFIG_ELOG_FULL_THRESHOLD, CONFIG_ELOG_SHRINK_SIZE);
616
617 /* Log a clear event if necessary */
Gabe Black84a93d12013-04-24 23:31:41 -0700618 if (flash->event_count == 0)
619 elog_add_event_word(ELOG_TYPE_LOG_CLEAR, flash->total_size);
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700620
Duncan Laurie5c88c6f2012-09-01 14:00:23 -0700621 /* Shrink the log if we are getting too full */
Gabe Black84a93d12013-04-24 23:31:41 -0700622 if (flash->next_event_offset >= CONFIG_ELOG_FULL_THRESHOLD)
Duncan Laurie032be822013-05-23 07:23:09 -0700623 if (elog_shrink() < 0)
624 return -1;
Duncan Laurie5c88c6f2012-09-01 14:00:23 -0700625
Gabe Black84a93d12013-04-24 23:31:41 -0700626#if !defined(__SMM__)
Duncan Laurief4d36232012-06-23 16:37:45 -0700627 /* Log boot count event except in S3 resume */
Gabe Black84a93d12013-04-24 23:31:41 -0700628 if (CONFIG_ELOG_BOOT_COUNT && acpi_slp_type != 3)
Duncan Laurief4d36232012-06-23 16:37:45 -0700629 elog_add_event_dword(ELOG_TYPE_BOOT, boot_count_read());
Duncan Laurief4d36232012-06-23 16:37:45 -0700630
Duncan Laurie1fc34612012-09-09 19:14:45 -0700631 /* Check and log POST codes from previous boot */
Gabe Black84a93d12013-04-24 23:31:41 -0700632 if (CONFIG_CMOS_POST)
633 cmos_post_log();
Duncan Laurie1fc34612012-09-09 19:14:45 -0700634#endif
635
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700636 return 0;
637}
638
639/*
640 * Populate timestamp in event header with current time
641 */
642static void elog_fill_timestamp(struct event_header *event)
643{
644 event->second = cmos_read(RTC_CLK_SECOND);
645 event->minute = cmos_read(RTC_CLK_MINUTE);
646 event->hour = cmos_read(RTC_CLK_HOUR);
647 event->day = cmos_read(RTC_CLK_DAYOFMONTH);
648 event->month = cmos_read(RTC_CLK_MONTH);
649 event->year = cmos_read(RTC_CLK_YEAR);
650
651 /* Basic sanity check of expected ranges */
652 if (event->month > 0x12 || event->day > 0x31 || event->hour > 0x23 ||
653 event->minute > 0x59 || event->second > 0x59) {
654 event->year = 0;
655 event->month = 0;
656 event->day = 0;
657 event->hour = 0;
658 event->minute = 0;
659 event->second = 0;
660 }
661}
662
663/*
Gabe Black331eb082013-04-24 04:11:40 -0700664 * Add an event to the log
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700665 */
Gabe Black331eb082013-04-24 04:11:40 -0700666void elog_add_event_raw(u8 event_type, void *data, u8 data_size)
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700667{
668 struct event_header *event;
Gabe Black331eb082013-04-24 04:11:40 -0700669 struct elog_descriptor *flash = elog_get_flash();
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700670 u8 event_size;
671
Gabe Black331eb082013-04-24 04:11:40 -0700672 elog_debug("elog_add_event_raw(type=%X)\n", event_type);
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700673
674 /* Make sure ELOG structures are initialized */
675 if (elog_init() < 0)
Gabe Black331eb082013-04-24 04:11:40 -0700676 return;
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700677
678 /* Header + Data + Checksum */
679 event_size = sizeof(*event) + data_size + 1;
680 if (event_size > MAX_EVENT_SIZE) {
681 printk(BIOS_ERR, "ELOG: Event(%X) data size too "
682 "big (%d)\n", event_type, event_size);
Gabe Black331eb082013-04-24 04:11:40 -0700683 return;
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700684 }
685
686 /* Make sure event data can fit */
Gabe Black331eb082013-04-24 04:11:40 -0700687 if ((flash->next_event_offset + event_size) >= flash->data_size) {
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700688 printk(BIOS_ERR, "ELOG: Event(%X) does not fit\n",
689 event_type);
Gabe Black331eb082013-04-24 04:11:40 -0700690 return;
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700691 }
692
693 /* Fill out event data */
Gabe Black331eb082013-04-24 04:11:40 -0700694 event = elog_get_next_event_base(flash);
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700695 event->type = event_type;
696 event->length = event_size;
697 elog_fill_timestamp(event);
698
699 if (data_size)
700 memcpy(&event[1], data, data_size);
701
702 /* Zero the checksum byte and then compute checksum */
703 elog_update_checksum(event, 0);
704 elog_update_checksum(event, -(elog_checksum_event(event)));
705
706 /* Update memory descriptor parameters */
Gabe Black331eb082013-04-24 04:11:40 -0700707 flash->event_count++;
708
709 elog_flash_write((void *)event, event_size);
710
Gabe Black331eb082013-04-24 04:11:40 -0700711 flash->next_event_offset += event_size;
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700712
713 printk(BIOS_INFO, "ELOG: Event(%X) added with size %d\n",
714 event_type, event_size);
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700715
716 /* Shrink the log if we are getting too full */
Gabe Black331eb082013-04-24 04:11:40 -0700717 if (flash->next_event_offset >= CONFIG_ELOG_FULL_THRESHOLD)
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700718 elog_shrink();
719}
720
721void elog_add_event(u8 event_type)
722{
723 elog_add_event_raw(event_type, NULL, 0);
724}
725
726void elog_add_event_byte(u8 event_type, u8 data)
727{
728 elog_add_event_raw(event_type, &data, sizeof(data));
729}
730
731void elog_add_event_word(u8 event_type, u16 data)
732{
733 elog_add_event_raw(event_type, &data, sizeof(data));
734}
735
736void elog_add_event_dword(u8 event_type, u32 data)
737{
738 elog_add_event_raw(event_type, &data, sizeof(data));
739}
740
741void elog_add_event_wake(u8 source, u32 instance)
742{
743 struct elog_event_data_wake wake = {
744 .source = source,
745 .instance = instance
746 };
747 elog_add_event_raw(ELOG_TYPE_WAKE_SOURCE, &wake, sizeof(wake));
748}