blob: 71eaad207b6acab7509eb958e681a2e7fe8d7376 [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.
Duncan Laurie7d2b81c2012-06-23 16:08:47 -070014 */
15
David Hendricks259e49a2014-03-20 20:31:23 -070016#if CONFIG_HAVE_ACPI_RESUME == 1
Duncan Laurie7d2b81c2012-06-23 16:08:47 -070017#include <arch/acpi.h>
David Hendricks259e49a2014-03-20 20:31:23 -070018#endif
Duncan Laurie215f27852012-10-10 14:34:49 -070019#include <cbmem.h>
Duncan Laurie7d2b81c2012-06-23 16:08:47 -070020#include <console/console.h>
David Hendricks259e49a2014-03-20 20:31:23 -070021#if CONFIG_ARCH_X86
Duncan Laurie7d2b81c2012-06-23 16:08:47 -070022#include <pc80/mc146818rtc.h>
David Hendricks259e49a2014-03-20 20:31:23 -070023#endif
Gabe Blacka4c4b1c2014-04-30 21:41:23 -070024#include <bcd.h>
Aaron Durbin0424c952015-03-28 23:56:22 -050025#include <fmap.h>
Gabe Blacka4c4b1c2014-04-30 21:41:23 -070026#include <rtc.h>
Duncan Laurie472ec9c2012-06-23 16:13:42 -070027#include <smbios.h>
Zheng Bao600784e2013-02-07 17:30:23 +080028#include <spi-generic.h>
Duncan Laurie7d2b81c2012-06-23 16:08:47 -070029#include <spi_flash.h>
30#include <stdint.h>
31#include <string.h>
32#include <elog.h>
33#include "elog_internal.h"
34
Edward O'Callaghana3119e52014-06-18 14:28:03 +100035
Stefan Reinauer45a225b2015-03-16 16:47:39 -070036#if !IS_ENABLED(CONFIG_CHROMEOS) && CONFIG_ELOG_FLASH_BASE == 0
Duncan Laurie7d2b81c2012-06-23 16:08:47 -070037#error "CONFIG_ELOG_FLASH_BASE is invalid"
38#endif
Duncan Laurie7d2b81c2012-06-23 16:08:47 -070039
40#if CONFIG_ELOG_DEBUG
41#define elog_debug(STR...) printk(BIOS_DEBUG, STR)
42#else
43#define elog_debug(STR...)
44#endif
45
46/*
47 * Static variables for ELOG state
48 */
Gabe Black0bf1feb2013-04-26 03:34:00 -070049static struct elog_area *elog_area;
50static u16 total_size;
Sol Boucherb4191492015-03-12 18:22:31 -070051static u16 log_size; /* excluding header */
Gabe Black0bf1feb2013-04-26 03:34:00 -070052static u32 flash_base;
Sol Boucherb4191492015-03-12 18:22:31 -070053static u16 full_threshold; /* from end of header */
54static u16 shrink_size; /* from end of header */
Gabe Black0bf1feb2013-04-26 03:34:00 -070055
56static elog_area_state area_state;
57static elog_header_state header_state;
58static elog_event_buffer_state event_buffer_state;
59
Sol Boucherb4191492015-03-12 18:22:31 -070060static u16 next_event_offset; /* from end of header */
Gabe Black0bf1feb2013-04-26 03:34:00 -070061static u16 event_count;
62
Duncan Laurie7d2b81c2012-06-23 16:08:47 -070063static struct spi_flash *elog_spi;
Duncan Laurie7d2b81c2012-06-23 16:08:47 -070064
Julius Werner5d686222015-01-27 15:40:47 -080065static enum {
66 ELOG_UNINITIALIZED = 0,
67 ELOG_INITIALIZED,
68 ELOG_BROKEN,
69} elog_initialized = ELOG_UNINITIALIZED;
Aaron Durbin10a070b2013-06-28 12:30:53 -070070
71static inline u32 get_rom_size(void)
72{
73 u32 rom_size;
74
75 /* Assume the used space of the ROM image starts from 0. The
76 * physical size of the device may not be completely used. */
77 rom_size = elog_spi->size;
78 if (rom_size > CONFIG_ROM_SIZE)
79 rom_size = CONFIG_ROM_SIZE;
80
81 return rom_size;
82}
83
Duncan Laurie7d2b81c2012-06-23 16:08:47 -070084/*
Duncan Laurie7d2b81c2012-06-23 16:08:47 -070085 * Pointer to an event log header in the event data area
86 */
87static inline struct event_header*
Gabe Black0bf1feb2013-04-26 03:34:00 -070088elog_get_event_base(u32 offset)
Duncan Laurie7d2b81c2012-06-23 16:08:47 -070089{
Gabe Black0bf1feb2013-04-26 03:34:00 -070090 return (struct event_header *)&elog_area->data[offset];
Duncan Laurie7d2b81c2012-06-23 16:08:47 -070091}
92
93/*
Duncan Laurie7d2b81c2012-06-23 16:08:47 -070094 * Update the checksum at the last byte
95 */
96static void elog_update_checksum(struct event_header *event, u8 checksum)
97{
98 u8 *event_data = (u8*)event;
99 event_data[event->length - 1] = checksum;
100}
101
102/*
103 * Simple byte checksum for events
104 */
105static u8 elog_checksum_event(struct event_header *event)
106{
107 u8 index, checksum = 0;
108 u8 *data = (u8*)event;
109
110 for (index = 0; index < event->length; index++)
111 checksum += data[index];
112 return checksum;
113}
114
115/*
116 * Check if a raw buffer is filled with ELOG_TYPE_EOL byte
117 */
Gabe Black0bf1feb2013-04-26 03:34:00 -0700118static int elog_is_buffer_clear(void *base, u32 size)
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700119{
120 u8 *current = base;
121 u8 *end = current + size;
122
123 elog_debug("elog_is_buffer_clear(base=0x%p size=%u)\n", base, size);
124
125 for (; current != end; current++) {
126 if (*current != ELOG_TYPE_EOL)
127 return 0;
128 }
129 return 1;
130}
131
132/*
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700133 * Check that the ELOG area has been initialized and is valid.
134 */
Gabe Black0bf1feb2013-04-26 03:34:00 -0700135static int elog_is_area_valid(void)
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700136{
137 elog_debug("elog_is_area_valid()\n");
138
Gabe Black0bf1feb2013-04-26 03:34:00 -0700139 if (area_state != ELOG_AREA_HAS_CONTENT)
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700140 return 0;
Gabe Black0bf1feb2013-04-26 03:34:00 -0700141 if (header_state != ELOG_HEADER_VALID)
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700142 return 0;
Gabe Black0bf1feb2013-04-26 03:34:00 -0700143 if (event_buffer_state != ELOG_EVENT_BUFFER_OK)
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700144 return 0;
145 return 1;
146}
147
148/*
149 * Verify the contents of an ELOG Header structure
150 * Returns 1 if the header is valid, 0 otherwise
151 */
152static int elog_is_header_valid(struct elog_header *header)
153{
154 elog_debug("elog_is_header_valid()\n");
155
156 if (header->magic != ELOG_SIGNATURE) {
157 printk(BIOS_ERR, "ELOG: header magic 0x%X != 0x%X\n",
158 header->magic, ELOG_SIGNATURE);
159 return 0;
160 }
161 if (header->version != ELOG_VERSION) {
162 printk(BIOS_ERR, "ELOG: header version %u != %u\n",
163 header->version, ELOG_VERSION);
164 return 0;
165 }
166 if (header->header_size != sizeof(*header)) {
Denis 'GNUtoo' Cariklibc2c9ef2013-06-26 20:04:49 +0200167 printk(BIOS_ERR, "ELOG: header size mismatch %u != %zu\n",
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700168 header->header_size, sizeof(*header));
169 return 0;
170 }
171 return 1;
172}
173
174/*
175 * Validate the event header and data.
176 */
Gabe Black0bf1feb2013-04-26 03:34:00 -0700177static int elog_is_event_valid(u32 offset)
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700178{
179 struct event_header *event;
180
Gabe Black0bf1feb2013-04-26 03:34:00 -0700181 event = elog_get_event_base(offset);
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700182 if (!event)
183 return 0;
184
185 /* Validate event length */
186 if ((offsetof(struct event_header, type) +
Gabe Black0bf1feb2013-04-26 03:34:00 -0700187 sizeof(event->type) - 1 + offset) >= log_size)
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700188 return 0;
189
190 /* End of event marker has been found */
191 if (event->type == ELOG_TYPE_EOL)
192 return 0;
193
194 /* Check if event fits in area */
195 if ((offsetof(struct event_header, length) +
Gabe Black0bf1feb2013-04-26 03:34:00 -0700196 sizeof(event->length) - 1 + offset) >= log_size)
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700197 return 0;
198
199 /*
200 * If the current event length + the current offset exceeds
201 * the area size then the event area is corrupt.
202 */
Gabe Black0bf1feb2013-04-26 03:34:00 -0700203 if ((event->length + offset) >= log_size)
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700204 return 0;
205
206 /* Event length must be at least header size + checksum */
207 if (event->length < (sizeof(*event) + 1))
208 return 0;
209
210 /* If event checksum is invalid the area is corrupt */
211 if (elog_checksum_event(event) != 0)
212 return 0;
213
214 /* Event is valid */
215 return 1;
216}
217
218/*
Gabe Black331eb082013-04-24 04:11:40 -0700219 * Write 'size' bytes of data pointed to by 'address' in the flash backing
220 * store into flash. This will not erase the flash and it assumes the flash
221 * area has been erased appropriately.
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700222 */
Gabe Black0bf1feb2013-04-26 03:34:00 -0700223static void elog_flash_write(void *address, u32 size)
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700224{
225 u32 offset;
226
Gabe Black331eb082013-04-24 04:11:40 -0700227 if (!address || !size || !elog_spi)
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700228 return;
229
Gabe Black0bf1feb2013-04-26 03:34:00 -0700230 offset = flash_base;
231 offset += (u8 *)address - (u8 *)elog_area;
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700232
Gabe Black331eb082013-04-24 04:11:40 -0700233 elog_debug("elog_flash_write(address=0x%p offset=0x%08x size=%u)\n",
234 address, offset, size);
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700235
236 /* Write the data to flash */
Gabe Black331eb082013-04-24 04:11:40 -0700237 elog_spi->write(elog_spi, offset, size, address);
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700238}
239
240/*
241 * Erase the first block specified in the address.
242 * Only handles flash area within a single flash block.
243 */
Gabe Black0bf1feb2013-04-26 03:34:00 -0700244static void elog_flash_erase(void *address, u32 size)
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700245{
246 u32 offset;
247
248 if (!address || !size || !elog_spi)
249 return;
250
Gabe Black0bf1feb2013-04-26 03:34:00 -0700251 offset = flash_base;
252 offset += (u8 *)address - (u8*)elog_area;
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700253
254 elog_debug("elog_flash_erase(address=0x%p offset=0x%08x size=%u)\n",
255 address, offset, size);
256
257 /* Erase the sectors in this region */
258 elog_spi->erase(elog_spi, offset, size);
259}
260
261/*
Gabe Black0bf1feb2013-04-26 03:34:00 -0700262 * Scan the event area and validate each entry and update the ELOG state.
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700263 */
Gabe Black0bf1feb2013-04-26 03:34:00 -0700264static void elog_update_event_buffer_state(void)
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700265{
266 u32 count = 0;
267 u32 offset = 0;
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700268 struct event_header *event;
269
270 elog_debug("elog_update_event_buffer_state()\n");
271
272 /* Go through each event and validate it */
273 while (1) {
Gabe Black0bf1feb2013-04-26 03:34:00 -0700274 event = elog_get_event_base(offset);
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700275
276 /* Do not de-reference anything past the area length */
277 if ((offsetof(struct event_header, type) +
Gabe Black0bf1feb2013-04-26 03:34:00 -0700278 sizeof(event->type) - 1 + offset) >= log_size) {
279 event_buffer_state = ELOG_EVENT_BUFFER_CORRUPTED;
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700280 break;
281 }
282
283 /* The end of the event marker has been found */
284 if (event->type == ELOG_TYPE_EOL)
285 break;
286
287 /* Validate the event */
Gabe Black0bf1feb2013-04-26 03:34:00 -0700288 if (!elog_is_event_valid(offset)) {
289 event_buffer_state = ELOG_EVENT_BUFFER_CORRUPTED;
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700290 break;
291 }
292
293 /* Move to the next event */
294 count++;
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700295 offset += event->length;
296 }
297
298 /* Ensure the remaining buffer is empty */
Gabe Black0bf1feb2013-04-26 03:34:00 -0700299 if (!elog_is_buffer_clear(&elog_area->data[offset], log_size - offset))
300 event_buffer_state = ELOG_EVENT_BUFFER_CORRUPTED;
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700301
Gabe Black0bf1feb2013-04-26 03:34:00 -0700302 /* Update ELOG state */
303 event_count = count;
304 next_event_offset = offset;
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700305}
306
Gabe Black0bf1feb2013-04-26 03:34:00 -0700307static void elog_scan_flash(void)
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700308{
Gabe Black84a93d12013-04-24 23:31:41 -0700309 elog_debug("elog_scan_flash()\n");
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700310
Gabe Black0bf1feb2013-04-26 03:34:00 -0700311 area_state = ELOG_AREA_UNDEFINED;
312 header_state = ELOG_HEADER_INVALID;
313 event_buffer_state = ELOG_EVENT_BUFFER_OK;
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700314
Duncan Laurie215f27852012-10-10 14:34:49 -0700315 /* Fill memory buffer by reading from SPI */
Gabe Black0bf1feb2013-04-26 03:34:00 -0700316 elog_spi->read(elog_spi, flash_base, total_size, elog_area);
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700317
Gabe Black0bf1feb2013-04-26 03:34:00 -0700318 next_event_offset = 0;
319 event_count = 0;
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700320
Gabe Black455c68e2013-04-24 17:54:37 -0700321 /* Check if the area is empty or not */
Gabe Black0bf1feb2013-04-26 03:34:00 -0700322 if (elog_is_buffer_clear(elog_area, total_size)) {
323 area_state = ELOG_AREA_EMPTY;
Gabe Black455c68e2013-04-24 17:54:37 -0700324 return;
325 }
326
Gabe Black0bf1feb2013-04-26 03:34:00 -0700327 area_state = ELOG_AREA_HAS_CONTENT;
Gabe Black455c68e2013-04-24 17:54:37 -0700328
329 /* Validate the header */
Gabe Black0bf1feb2013-04-26 03:34:00 -0700330 if (!elog_is_header_valid(&elog_area->header)) {
331 header_state = ELOG_HEADER_INVALID;
Gabe Black455c68e2013-04-24 17:54:37 -0700332 return;
333 }
334
Gabe Black0bf1feb2013-04-26 03:34:00 -0700335 header_state = ELOG_HEADER_VALID;
336 elog_update_event_buffer_state();
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700337}
338
Gabe Black0bf1feb2013-04-26 03:34:00 -0700339static void elog_prepare_empty(void)
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700340{
341 struct elog_header *header;
342
Gabe Black0bf1feb2013-04-26 03:34:00 -0700343 elog_debug("elog_prepare_empty()\n");
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700344
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700345 /* Write out the header */
Gabe Black0bf1feb2013-04-26 03:34:00 -0700346 header = &elog_area->header;
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700347 header->magic = ELOG_SIGNATURE;
348 header->version = ELOG_VERSION;
349 header->header_size = sizeof(struct elog_header);
350 header->reserved[0] = ELOG_TYPE_EOL;
351 header->reserved[1] = ELOG_TYPE_EOL;
Gabe Black0bf1feb2013-04-26 03:34:00 -0700352 elog_flash_write(elog_area, header->header_size);
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700353
Gabe Black0bf1feb2013-04-26 03:34:00 -0700354 elog_scan_flash();
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700355}
356
357/*
358 * Shrink the log, deleting old entries and moving the
Martin Roth56889792013-07-09 21:39:46 -0600359 * remaining ones to the front of the log.
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700360 */
361static int elog_shrink(void)
362{
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700363 struct event_header *event;
364 u16 discard_count = 0;
365 u16 offset = 0;
Gabe Blackbfae4aa2013-04-24 03:16:21 -0700366 u16 new_size = 0;
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700367
368 elog_debug("elog_shrink()\n");
369
Sol Boucherb4191492015-03-12 18:22:31 -0700370 if (next_event_offset < shrink_size)
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700371 return 0;
372
373 while (1) {
374 /* Next event has exceeded constraints */
Sol Boucherb4191492015-03-12 18:22:31 -0700375 if (offset > shrink_size)
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700376 break;
377
Gabe Black0bf1feb2013-04-26 03:34:00 -0700378 event = elog_get_event_base(offset);
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700379
380 /* Reached the end of the area */
381 if (!event || event->type == ELOG_TYPE_EOL)
382 break;
383
384 offset += event->length;
385 discard_count++;
386 }
387
Gabe Black0bf1feb2013-04-26 03:34:00 -0700388 new_size = next_event_offset - offset;
389 memmove(&elog_area->data[0], &elog_area->data[offset], new_size);
390 memset(&elog_area->data[new_size], ELOG_TYPE_EOL, log_size - new_size);
Gabe Blackbfae4aa2013-04-24 03:16:21 -0700391
Gabe Black0bf1feb2013-04-26 03:34:00 -0700392 elog_flash_erase(elog_area, total_size);
393 elog_flash_write(elog_area, total_size);
394 elog_scan_flash();
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700395
Duncan Laurie032be822013-05-23 07:23:09 -0700396 /* Ensure the area was successfully erased */
Sol Boucherb4191492015-03-12 18:22:31 -0700397 if (next_event_offset >= full_threshold) {
Duncan Laurie032be822013-05-23 07:23:09 -0700398 printk(BIOS_ERR, "ELOG: Flash area was not erased!\n");
399 return -1;
400 }
401
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700402 /* Add clear event */
403 elog_add_event_word(ELOG_TYPE_LOG_CLEAR, offset);
404
405 return 0;
406}
407
Duncan Laurie215f27852012-10-10 14:34:49 -0700408#ifndef __SMM__
Furquan Shaikh7f4221c2014-11-12 16:19:37 -0800409#if IS_ENABLED(CONFIG_ARCH_X86)
410
411/*
412 * Convert a flash offset into a memory mapped flash address
413 */
414static inline u8 *elog_flash_offset_to_address(u32 offset)
415{
416 u32 rom_size;
417
418 if (!elog_spi)
419 return NULL;
420
421 rom_size = get_rom_size();
422
423 return (u8 *)((u32)~0UL - rom_size + 1 + offset);
424}
425
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700426/*
Duncan Laurie472ec9c2012-06-23 16:13:42 -0700427 * Fill out SMBIOS Type 15 table entry so the
428 * event log can be discovered at runtime.
429 */
430int elog_smbios_write_type15(unsigned long *current, int handle)
431{
432 struct smbios_type15 *t = (struct smbios_type15 *)*current;
433 int len = sizeof(struct smbios_type15);
434
Duncan Laurie215f27852012-10-10 14:34:49 -0700435#if CONFIG_ELOG_CBMEM
436 /* Save event log buffer into CBMEM for the OS to read */
Gabe Black0bf1feb2013-04-26 03:34:00 -0700437 void *cbmem = cbmem_add(CBMEM_ID_ELOG, total_size);
Duncan Laurie215f27852012-10-10 14:34:49 -0700438 if (!cbmem)
439 return 0;
Gabe Black0bf1feb2013-04-26 03:34:00 -0700440 memcpy(cbmem, elog_area, total_size);
Duncan Laurie215f27852012-10-10 14:34:49 -0700441#endif
442
Duncan Laurie472ec9c2012-06-23 16:13:42 -0700443 memset(t, 0, len);
444 t->type = SMBIOS_EVENT_LOG;
445 t->length = len - 2;
446 t->handle = handle;
Gabe Black0bf1feb2013-04-26 03:34:00 -0700447 t->area_length = total_size - 1;
Duncan Laurie472ec9c2012-06-23 16:13:42 -0700448 t->header_offset = 0;
449 t->data_offset = sizeof(struct elog_header);
450 t->access_method = SMBIOS_EVENTLOG_ACCESS_METHOD_MMIO32;
451 t->log_status = SMBIOS_EVENTLOG_STATUS_VALID;
452 t->change_token = 0;
Duncan Laurie215f27852012-10-10 14:34:49 -0700453#if CONFIG_ELOG_CBMEM
454 t->address = (u32)cbmem;
455#else
Gabe Black0bf1feb2013-04-26 03:34:00 -0700456 t->address = (u32)elog_flash_offset_to_address(flash_base);
Duncan Laurie215f27852012-10-10 14:34:49 -0700457#endif
Duncan Laurie472ec9c2012-06-23 16:13:42 -0700458 t->header_format = ELOG_HEADER_TYPE_OEM;
459 t->log_type_descriptors = 0;
460 t->log_type_descriptor_length = 2;
461
462 *current += len;
463 return len;
464}
Duncan Laurie215f27852012-10-10 14:34:49 -0700465#endif
Furquan Shaikh7f4221c2014-11-12 16:19:37 -0800466#endif
Duncan Laurie472ec9c2012-06-23 16:13:42 -0700467
468/*
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700469 * Clear the entire event log
470 */
471int elog_clear(void)
472{
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700473 elog_debug("elog_clear()\n");
474
Gabe Black8f4baec2013-04-25 17:21:58 -0700475 /* Make sure ELOG structures are initialized */
476 if (elog_init() < 0)
477 return -1;
478
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700479 /* Erase flash area */
Gabe Black0bf1feb2013-04-26 03:34:00 -0700480 elog_flash_erase(elog_area, total_size);
481 elog_prepare_empty();
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700482
Gabe Black0bf1feb2013-04-26 03:34:00 -0700483 if (!elog_is_area_valid())
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700484 return -1;
485
486 /* Log the clear event */
Gabe Black0bf1feb2013-04-26 03:34:00 -0700487 elog_add_event_word(ELOG_TYPE_LOG_CLEAR, total_size);
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700488
489 return 0;
490}
491
Gabe Black0bf1feb2013-04-26 03:34:00 -0700492static void elog_find_flash(void)
Gabe Black84a93d12013-04-24 23:31:41 -0700493{
Gabe Black0bf1feb2013-04-26 03:34:00 -0700494 elog_debug("elog_find_flash()\n");
Gabe Black84a93d12013-04-24 23:31:41 -0700495
Aaron Durbin0424c952015-03-28 23:56:22 -0500496 if (IS_ENABLED(CONFIG_CHROMEOS)) {
497 /* Find the ELOG base and size in FMAP */
498 struct region r;
499
500 if (fmap_locate_area("RW_ELOG", &r) < 0) {
501 printk(BIOS_WARNING,
502 "ELOG: Unable to find RW_ELOG in FMAP\n");
503 flash_base = total_size = 0;
504 } else {
505 flash_base = region_offset(&r);
506 total_size = MIN(region_sz(&r), CONFIG_ELOG_AREA_SIZE);
507 }
Gabe Black84a93d12013-04-24 23:31:41 -0700508 } else {
Aaron Durbin0424c952015-03-28 23:56:22 -0500509 flash_base = CONFIG_ELOG_FLASH_BASE;
510 total_size = CONFIG_ELOG_AREA_SIZE;
Gabe Black84a93d12013-04-24 23:31:41 -0700511 }
Gabe Black0bf1feb2013-04-26 03:34:00 -0700512 log_size = total_size - sizeof(struct elog_header);
Sol Boucherb4191492015-03-12 18:22:31 -0700513 full_threshold = log_size - ELOG_MIN_AVAILABLE_ENTRIES * MAX_EVENT_SIZE;
514 shrink_size = MIN(total_size * ELOG_SHRINK_PERCENTAGE / 100,
515 full_threshold);
Gabe Black84a93d12013-04-24 23:31:41 -0700516}
517
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700518/*
519 * Event log main entry point
520 */
521int elog_init(void)
522{
Julius Werner5d686222015-01-27 15:40:47 -0800523 switch (elog_initialized) {
524 case ELOG_UNINITIALIZED:
525 break;
526 case ELOG_INITIALIZED:
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700527 return 0;
Julius Werner5d686222015-01-27 15:40:47 -0800528 case ELOG_BROKEN:
529 return -1;
530 }
531 elog_initialized = ELOG_BROKEN;
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700532
533 elog_debug("elog_init()\n");
534
David Hendricks9acbd6f2014-04-13 16:45:31 -0700535 /* Probe SPI chip. SPI controller must already be initialized. */
David Hendricksc9470242014-04-14 14:57:36 -0700536 elog_spi = spi_flash_probe(CONFIG_BOOT_MEDIA_SPI_BUS, 0);
Gabe Black84a93d12013-04-24 23:31:41 -0700537 if (!elog_spi) {
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700538 printk(BIOS_ERR, "ELOG: Unable to find SPI flash\n");
539 return -1;
540 }
541
Gabe Black84a93d12013-04-24 23:31:41 -0700542 /* Set up the backing store */
Gabe Black0bf1feb2013-04-26 03:34:00 -0700543 elog_find_flash();
Gabe Black84a93d12013-04-24 23:31:41 -0700544 if (flash_base == 0) {
545 printk(BIOS_ERR, "ELOG: Invalid flash base\n");
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700546 return -1;
Sol Boucherb4191492015-03-12 18:22:31 -0700547 } else if (total_size < sizeof(struct elog_header) + MAX_EVENT_SIZE) {
548 printk(BIOS_ERR, "ELOG: Region too small to hold any events\n");
549 return -1;
550 } else if (log_size - shrink_size >= full_threshold) {
551 printk(BIOS_ERR,
552 "ELOG: SHRINK_PERCENTAGE set too small for MIN_AVAILABLE_ENTRIES\n");
553 return -1;
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700554 }
555
Gabe Black0bf1feb2013-04-26 03:34:00 -0700556 elog_area = malloc(total_size);
557 if (!elog_area) {
Gabe Black84a93d12013-04-24 23:31:41 -0700558 printk(BIOS_ERR, "ELOG: Unable to allocate backing store\n");
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700559 return -1;
560 }
Gabe Black84a93d12013-04-24 23:31:41 -0700561
562 /* Load the log from flash */
Gabe Black0bf1feb2013-04-26 03:34:00 -0700563 elog_scan_flash();
Gabe Black84a93d12013-04-24 23:31:41 -0700564
565 /* Prepare the flash if necessary */
Gabe Black0bf1feb2013-04-26 03:34:00 -0700566 if (header_state == ELOG_HEADER_INVALID ||
567 event_buffer_state == ELOG_EVENT_BUFFER_CORRUPTED) {
Gabe Black84a93d12013-04-24 23:31:41 -0700568 /* If the header is invalid or the events are corrupted,
569 * no events can be salvaged so erase the entire area. */
570 printk(BIOS_ERR, "ELOG: flash area invalid\n");
Gabe Black0bf1feb2013-04-26 03:34:00 -0700571 elog_flash_erase(elog_area, total_size);
572 elog_prepare_empty();
Gabe Black84a93d12013-04-24 23:31:41 -0700573 }
574
Gabe Black0bf1feb2013-04-26 03:34:00 -0700575 if (area_state == ELOG_AREA_EMPTY)
576 elog_prepare_empty();
Gabe Black84a93d12013-04-24 23:31:41 -0700577
Gabe Black0bf1feb2013-04-26 03:34:00 -0700578 if (!elog_is_area_valid()) {
Gabe Black84a93d12013-04-24 23:31:41 -0700579 printk(BIOS_ERR, "ELOG: Unable to prepare flash\n");
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700580 return -1;
581 }
582
Gabe Black331eb082013-04-24 04:11:40 -0700583 printk(BIOS_INFO, "ELOG: FLASH @0x%p [SPI 0x%08x]\n",
Gabe Black0bf1feb2013-04-26 03:34:00 -0700584 elog_area, flash_base);
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700585
Gabe Black84a93d12013-04-24 23:31:41 -0700586 printk(BIOS_INFO, "ELOG: area is %d bytes, full threshold %d,"
Sol Boucherb4191492015-03-12 18:22:31 -0700587 " shrink size %d\n", total_size, full_threshold, shrink_size);
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700588
Julius Wernerefae69a2015-02-05 12:55:45 -0800589 elog_initialized = ELOG_INITIALIZED;
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700590
Duncan Laurie5c88c6f2012-09-01 14:00:23 -0700591 /* Shrink the log if we are getting too full */
Sol Boucherb4191492015-03-12 18:22:31 -0700592 if (next_event_offset >= full_threshold)
Duncan Laurie032be822013-05-23 07:23:09 -0700593 if (elog_shrink() < 0)
594 return -1;
Duncan Laurie5c88c6f2012-09-01 14:00:23 -0700595
Julius Wernerefae69a2015-02-05 12:55:45 -0800596 /* Log a clear event if necessary */
597 if (event_count == 0)
598 elog_add_event_word(ELOG_TYPE_LOG_CLEAR, total_size);
599
Gabe Black84a93d12013-04-24 23:31:41 -0700600#if !defined(__SMM__)
Duncan Laurief4d36232012-06-23 16:37:45 -0700601 /* Log boot count event except in S3 resume */
David Hendricks259e49a2014-03-20 20:31:23 -0700602#if CONFIG_ELOG_BOOT_COUNT == 1
603#if CONFIG_HAVE_ACPI_RESUME == 1
604 if (!acpi_is_wakeup_s3())
605#endif
Duncan Laurief4d36232012-06-23 16:37:45 -0700606 elog_add_event_dword(ELOG_TYPE_BOOT, boot_count_read());
David Hendricks6a29e6c2014-06-18 13:03:03 -0700607#else
608 /* If boot count is not implemented, fake it. */
609 elog_add_event_dword(ELOG_TYPE_BOOT, 0);
David Hendricks259e49a2014-03-20 20:31:23 -0700610#endif
Duncan Laurief4d36232012-06-23 16:37:45 -0700611
David Hendricks259e49a2014-03-20 20:31:23 -0700612#if CONFIG_ARCH_X86
Duncan Laurie1fc34612012-09-09 19:14:45 -0700613 /* Check and log POST codes from previous boot */
Gabe Black84a93d12013-04-24 23:31:41 -0700614 if (CONFIG_CMOS_POST)
615 cmos_post_log();
Duncan Laurie1fc34612012-09-09 19:14:45 -0700616#endif
David Hendricks259e49a2014-03-20 20:31:23 -0700617#endif
Duncan Laurie1fc34612012-09-09 19:14:45 -0700618
Julius Werner5d686222015-01-27 15:40:47 -0800619 elog_initialized = ELOG_INITIALIZED;
620
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700621 return 0;
622}
623
624/*
625 * Populate timestamp in event header with current time
626 */
627static void elog_fill_timestamp(struct event_header *event)
628{
Patrick Georgi6e5f22e2015-04-22 13:28:21 +0200629#if IS_ENABLED(CONFIG_RTC)
Gabe Blacka4c4b1c2014-04-30 21:41:23 -0700630 struct rtc_time time;
631
632 rtc_get(&time);
633 event->second = bin2bcd(time.sec);
634 event->minute = bin2bcd(time.min);
635 event->hour = bin2bcd(time.hour);
636 event->day = bin2bcd(time.mday);
637 event->month = bin2bcd(time.mon);
Aaron Durbinc41c6a42015-11-06 15:20:23 -0600638 event->year = bin2bcd(time.year % 100);
Patrick Georgi6e5f22e2015-04-22 13:28:21 +0200639
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700640 /* Basic sanity check of expected ranges */
641 if (event->month > 0x12 || event->day > 0x31 || event->hour > 0x23 ||
Patrick Georgi6e5f22e2015-04-22 13:28:21 +0200642 event->minute > 0x59 || event->second > 0x59)
643#endif
644 {
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700645 event->year = 0;
646 event->month = 0;
647 event->day = 0;
648 event->hour = 0;
649 event->minute = 0;
650 event->second = 0;
651 }
652}
653
654/*
Gabe Black331eb082013-04-24 04:11:40 -0700655 * Add an event to the log
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700656 */
Gabe Black331eb082013-04-24 04:11:40 -0700657void elog_add_event_raw(u8 event_type, void *data, u8 data_size)
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700658{
659 struct event_header *event;
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700660 u8 event_size;
661
Gabe Black331eb082013-04-24 04:11:40 -0700662 elog_debug("elog_add_event_raw(type=%X)\n", event_type);
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700663
664 /* Make sure ELOG structures are initialized */
665 if (elog_init() < 0)
Gabe Black331eb082013-04-24 04:11:40 -0700666 return;
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700667
668 /* Header + Data + Checksum */
669 event_size = sizeof(*event) + data_size + 1;
670 if (event_size > MAX_EVENT_SIZE) {
671 printk(BIOS_ERR, "ELOG: Event(%X) data size too "
672 "big (%d)\n", event_type, event_size);
Gabe Black331eb082013-04-24 04:11:40 -0700673 return;
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700674 }
675
676 /* Make sure event data can fit */
Gabe Black0bf1feb2013-04-26 03:34:00 -0700677 if ((next_event_offset + event_size) >= log_size) {
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700678 printk(BIOS_ERR, "ELOG: Event(%X) does not fit\n",
679 event_type);
Gabe Black331eb082013-04-24 04:11:40 -0700680 return;
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700681 }
682
683 /* Fill out event data */
Gabe Black0bf1feb2013-04-26 03:34:00 -0700684 event = elog_get_event_base(next_event_offset);
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700685 event->type = event_type;
686 event->length = event_size;
687 elog_fill_timestamp(event);
688
689 if (data_size)
690 memcpy(&event[1], data, data_size);
691
692 /* Zero the checksum byte and then compute checksum */
693 elog_update_checksum(event, 0);
694 elog_update_checksum(event, -(elog_checksum_event(event)));
695
Gabe Black0bf1feb2013-04-26 03:34:00 -0700696 /* Update the ELOG state */
697 event_count++;
Gabe Black331eb082013-04-24 04:11:40 -0700698
699 elog_flash_write((void *)event, event_size);
700
Gabe Black0bf1feb2013-04-26 03:34:00 -0700701 next_event_offset += event_size;
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700702
703 printk(BIOS_INFO, "ELOG: Event(%X) added with size %d\n",
704 event_type, event_size);
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700705
706 /* Shrink the log if we are getting too full */
Sol Boucherb4191492015-03-12 18:22:31 -0700707 if (next_event_offset >= full_threshold)
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700708 elog_shrink();
709}
710
711void elog_add_event(u8 event_type)
712{
713 elog_add_event_raw(event_type, NULL, 0);
714}
715
716void elog_add_event_byte(u8 event_type, u8 data)
717{
718 elog_add_event_raw(event_type, &data, sizeof(data));
719}
720
721void elog_add_event_word(u8 event_type, u16 data)
722{
723 elog_add_event_raw(event_type, &data, sizeof(data));
724}
725
726void elog_add_event_dword(u8 event_type, u32 data)
727{
728 elog_add_event_raw(event_type, &data, sizeof(data));
729}
730
731void elog_add_event_wake(u8 source, u32 instance)
732{
733 struct elog_event_data_wake wake = {
734 .source = source,
735 .instance = instance
736 };
737 elog_add_event_raw(ELOG_TYPE_WAKE_SOURCE, &wake, sizeof(wake));
738}