blob: 8816974cab73a1322fcd1dd04369e81650e01145 [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#include <vendorcode/google/chromeos/fmap.h>
Edward O'Callaghana3119e52014-06-18 14:28:03 +100033
34#if CONFIG_ELOG_FLASH_BASE == 0
Duncan Laurie7d2b81c2012-06-23 16:08:47 -070035#error "CONFIG_ELOG_FLASH_BASE is invalid"
36#endif
Edward O'Callaghana3119e52014-06-18 14:28:03 +100037
Duncan Laurie7d2b81c2012-06-23 16:08:47 -070038#if CONFIG_ELOG_FULL_THRESHOLD >= CONFIG_ELOG_AREA_SIZE
39#error "CONFIG_ELOG_FULL_THRESHOLD is larger than CONFIG_ELOG_AREA_SIZE"
40#endif
41#if (CONFIG_ELOG_AREA_SIZE - CONFIG_ELOG_FULL_THRESHOLD) < (MAX_EVENT_SIZE + 1)
42#error "CONFIG_ELOG_FULL_THRESHOLD is too small"
43#endif
44#if CONFIG_ELOG_SHRINK_SIZE >= CONFIG_ELOG_AREA_SIZE
45#error "CONFIG_ELOG_SHRINK_SIZE is larger than CONFIG_ELOG_AREA_SIZE"
46#endif
47#if (CONFIG_ELOG_AREA_SIZE - CONFIG_ELOG_SHRINK_SIZE) > \
48 CONFIG_ELOG_FULL_THRESHOLD
49#error "CONFIG_ELOG_SHRINK_SIZE is too large"
50#endif
51
52#if CONFIG_ELOG_DEBUG
53#define elog_debug(STR...) printk(BIOS_DEBUG, STR)
54#else
55#define elog_debug(STR...)
56#endif
57
58/*
59 * Static variables for ELOG state
60 */
Gabe Black0bf1feb2013-04-26 03:34:00 -070061static struct elog_area *elog_area;
62static u16 total_size;
63static u16 log_size;
64static u32 flash_base;
65
66static elog_area_state area_state;
67static elog_header_state header_state;
68static elog_event_buffer_state event_buffer_state;
69
70static u16 next_event_offset;
71static u16 event_count;
72
Duncan Laurie7d2b81c2012-06-23 16:08:47 -070073static int elog_initialized;
74static struct spi_flash *elog_spi;
Duncan Laurie7d2b81c2012-06-23 16:08:47 -070075
Aaron Durbin10a070b2013-06-28 12:30:53 -070076
77static inline u32 get_rom_size(void)
78{
79 u32 rom_size;
80
81 /* Assume the used space of the ROM image starts from 0. The
82 * physical size of the device may not be completely used. */
83 rom_size = elog_spi->size;
84 if (rom_size > CONFIG_ROM_SIZE)
85 rom_size = CONFIG_ROM_SIZE;
86
87 return rom_size;
88}
89
Duncan Laurie7d2b81c2012-06-23 16:08:47 -070090/*
91 * Convert a memory mapped flash address into a flash offset
92 */
93static inline u32 elog_flash_address_to_offset(u8 *address)
94{
Aaron Durbin10a070b2013-06-28 12:30:53 -070095 u32 rom_size;
96
Duncan Laurie7d2b81c2012-06-23 16:08:47 -070097 if (!elog_spi)
98 return 0;
Aaron Durbin10a070b2013-06-28 12:30:53 -070099
100 rom_size = get_rom_size();
101
102 return (u32)address - ((u32)~0UL - rom_size + 1);
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700103}
104
105/*
106 * Convert a flash offset into a memory mapped flash address
107 */
108static inline u8* elog_flash_offset_to_address(u32 offset)
109{
Aaron Durbin10a070b2013-06-28 12:30:53 -0700110 u32 rom_size;
111
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700112 if (!elog_spi)
113 return NULL;
Aaron Durbin10a070b2013-06-28 12:30:53 -0700114
115 rom_size = get_rom_size();
116
117 return (u8*)((u32)~0UL - rom_size + 1 + offset);
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700118}
119
120/*
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700121 * Pointer to an event log header in the event data area
122 */
123static inline struct event_header*
Gabe Black0bf1feb2013-04-26 03:34:00 -0700124elog_get_event_base(u32 offset)
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700125{
Gabe Black0bf1feb2013-04-26 03:34:00 -0700126 return (struct event_header *)&elog_area->data[offset];
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700127}
128
129/*
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700130 * Update the checksum at the last byte
131 */
132static void elog_update_checksum(struct event_header *event, u8 checksum)
133{
134 u8 *event_data = (u8*)event;
135 event_data[event->length - 1] = checksum;
136}
137
138/*
139 * Simple byte checksum for events
140 */
141static u8 elog_checksum_event(struct event_header *event)
142{
143 u8 index, checksum = 0;
144 u8 *data = (u8*)event;
145
146 for (index = 0; index < event->length; index++)
147 checksum += data[index];
148 return checksum;
149}
150
151/*
152 * Check if a raw buffer is filled with ELOG_TYPE_EOL byte
153 */
Gabe Black0bf1feb2013-04-26 03:34:00 -0700154static int elog_is_buffer_clear(void *base, u32 size)
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700155{
156 u8 *current = base;
157 u8 *end = current + size;
158
159 elog_debug("elog_is_buffer_clear(base=0x%p size=%u)\n", base, size);
160
161 for (; current != end; current++) {
162 if (*current != ELOG_TYPE_EOL)
163 return 0;
164 }
165 return 1;
166}
167
168/*
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700169 * Check that the ELOG area has been initialized and is valid.
170 */
Gabe Black0bf1feb2013-04-26 03:34:00 -0700171static int elog_is_area_valid(void)
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700172{
173 elog_debug("elog_is_area_valid()\n");
174
Gabe Black0bf1feb2013-04-26 03:34:00 -0700175 if (area_state != ELOG_AREA_HAS_CONTENT)
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700176 return 0;
Gabe Black0bf1feb2013-04-26 03:34:00 -0700177 if (header_state != ELOG_HEADER_VALID)
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700178 return 0;
Gabe Black0bf1feb2013-04-26 03:34:00 -0700179 if (event_buffer_state != ELOG_EVENT_BUFFER_OK)
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700180 return 0;
181 return 1;
182}
183
184/*
185 * Verify the contents of an ELOG Header structure
186 * Returns 1 if the header is valid, 0 otherwise
187 */
188static int elog_is_header_valid(struct elog_header *header)
189{
190 elog_debug("elog_is_header_valid()\n");
191
192 if (header->magic != ELOG_SIGNATURE) {
193 printk(BIOS_ERR, "ELOG: header magic 0x%X != 0x%X\n",
194 header->magic, ELOG_SIGNATURE);
195 return 0;
196 }
197 if (header->version != ELOG_VERSION) {
198 printk(BIOS_ERR, "ELOG: header version %u != %u\n",
199 header->version, ELOG_VERSION);
200 return 0;
201 }
202 if (header->header_size != sizeof(*header)) {
Denis 'GNUtoo' Cariklibc2c9ef2013-06-26 20:04:49 +0200203 printk(BIOS_ERR, "ELOG: header size mismatch %u != %zu\n",
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700204 header->header_size, sizeof(*header));
205 return 0;
206 }
207 return 1;
208}
209
210/*
211 * Validate the event header and data.
212 */
Gabe Black0bf1feb2013-04-26 03:34:00 -0700213static int elog_is_event_valid(u32 offset)
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700214{
215 struct event_header *event;
216
Gabe Black0bf1feb2013-04-26 03:34:00 -0700217 event = elog_get_event_base(offset);
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700218 if (!event)
219 return 0;
220
221 /* Validate event length */
222 if ((offsetof(struct event_header, type) +
Gabe Black0bf1feb2013-04-26 03:34:00 -0700223 sizeof(event->type) - 1 + offset) >= log_size)
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700224 return 0;
225
226 /* End of event marker has been found */
227 if (event->type == ELOG_TYPE_EOL)
228 return 0;
229
230 /* Check if event fits in area */
231 if ((offsetof(struct event_header, length) +
Gabe Black0bf1feb2013-04-26 03:34:00 -0700232 sizeof(event->length) - 1 + offset) >= log_size)
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700233 return 0;
234
235 /*
236 * If the current event length + the current offset exceeds
237 * the area size then the event area is corrupt.
238 */
Gabe Black0bf1feb2013-04-26 03:34:00 -0700239 if ((event->length + offset) >= log_size)
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700240 return 0;
241
242 /* Event length must be at least header size + checksum */
243 if (event->length < (sizeof(*event) + 1))
244 return 0;
245
246 /* If event checksum is invalid the area is corrupt */
247 if (elog_checksum_event(event) != 0)
248 return 0;
249
250 /* Event is valid */
251 return 1;
252}
253
254/*
Gabe Black331eb082013-04-24 04:11:40 -0700255 * Write 'size' bytes of data pointed to by 'address' in the flash backing
256 * store into flash. This will not erase the flash and it assumes the flash
257 * area has been erased appropriately.
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700258 */
Gabe Black0bf1feb2013-04-26 03:34:00 -0700259static void elog_flash_write(void *address, u32 size)
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700260{
261 u32 offset;
262
Gabe Black331eb082013-04-24 04:11:40 -0700263 if (!address || !size || !elog_spi)
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700264 return;
265
Gabe Black0bf1feb2013-04-26 03:34:00 -0700266 offset = flash_base;
267 offset += (u8 *)address - (u8 *)elog_area;
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700268
Gabe Black331eb082013-04-24 04:11:40 -0700269 elog_debug("elog_flash_write(address=0x%p offset=0x%08x size=%u)\n",
270 address, offset, size);
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700271
272 /* Write the data to flash */
Gabe Black331eb082013-04-24 04:11:40 -0700273 elog_spi->write(elog_spi, offset, size, address);
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700274}
275
276/*
277 * Erase the first block specified in the address.
278 * Only handles flash area within a single flash block.
279 */
Gabe Black0bf1feb2013-04-26 03:34:00 -0700280static void elog_flash_erase(void *address, u32 size)
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700281{
282 u32 offset;
283
284 if (!address || !size || !elog_spi)
285 return;
286
Gabe Black0bf1feb2013-04-26 03:34:00 -0700287 offset = flash_base;
288 offset += (u8 *)address - (u8*)elog_area;
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700289
290 elog_debug("elog_flash_erase(address=0x%p offset=0x%08x size=%u)\n",
291 address, offset, size);
292
293 /* Erase the sectors in this region */
294 elog_spi->erase(elog_spi, offset, size);
295}
296
297/*
Gabe Black0bf1feb2013-04-26 03:34:00 -0700298 * Scan the event area and validate each entry and update the ELOG state.
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700299 */
Gabe Black0bf1feb2013-04-26 03:34:00 -0700300static void elog_update_event_buffer_state(void)
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700301{
302 u32 count = 0;
303 u32 offset = 0;
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700304 struct event_header *event;
305
306 elog_debug("elog_update_event_buffer_state()\n");
307
308 /* Go through each event and validate it */
309 while (1) {
Gabe Black0bf1feb2013-04-26 03:34:00 -0700310 event = elog_get_event_base(offset);
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700311
312 /* Do not de-reference anything past the area length */
313 if ((offsetof(struct event_header, type) +
Gabe Black0bf1feb2013-04-26 03:34:00 -0700314 sizeof(event->type) - 1 + offset) >= log_size) {
315 event_buffer_state = ELOG_EVENT_BUFFER_CORRUPTED;
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700316 break;
317 }
318
319 /* The end of the event marker has been found */
320 if (event->type == ELOG_TYPE_EOL)
321 break;
322
323 /* Validate the event */
Gabe Black0bf1feb2013-04-26 03:34:00 -0700324 if (!elog_is_event_valid(offset)) {
325 event_buffer_state = ELOG_EVENT_BUFFER_CORRUPTED;
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700326 break;
327 }
328
329 /* Move to the next event */
330 count++;
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700331 offset += event->length;
332 }
333
334 /* Ensure the remaining buffer is empty */
Gabe Black0bf1feb2013-04-26 03:34:00 -0700335 if (!elog_is_buffer_clear(&elog_area->data[offset], log_size - offset))
336 event_buffer_state = ELOG_EVENT_BUFFER_CORRUPTED;
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700337
Gabe Black0bf1feb2013-04-26 03:34:00 -0700338 /* Update ELOG state */
339 event_count = count;
340 next_event_offset = offset;
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700341}
342
Gabe Black0bf1feb2013-04-26 03:34:00 -0700343static void elog_scan_flash(void)
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700344{
Gabe Black84a93d12013-04-24 23:31:41 -0700345 elog_debug("elog_scan_flash()\n");
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700346
Gabe Black0bf1feb2013-04-26 03:34:00 -0700347 area_state = ELOG_AREA_UNDEFINED;
348 header_state = ELOG_HEADER_INVALID;
349 event_buffer_state = ELOG_EVENT_BUFFER_OK;
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700350
Duncan Laurie215f27852012-10-10 14:34:49 -0700351 /* Fill memory buffer by reading from SPI */
Gabe Black0bf1feb2013-04-26 03:34:00 -0700352 elog_spi->read(elog_spi, flash_base, total_size, elog_area);
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700353
Gabe Black0bf1feb2013-04-26 03:34:00 -0700354 next_event_offset = 0;
355 event_count = 0;
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700356
Gabe Black455c68e2013-04-24 17:54:37 -0700357 /* Check if the area is empty or not */
Gabe Black0bf1feb2013-04-26 03:34:00 -0700358 if (elog_is_buffer_clear(elog_area, total_size)) {
359 area_state = ELOG_AREA_EMPTY;
Gabe Black455c68e2013-04-24 17:54:37 -0700360 return;
361 }
362
Gabe Black0bf1feb2013-04-26 03:34:00 -0700363 area_state = ELOG_AREA_HAS_CONTENT;
Gabe Black455c68e2013-04-24 17:54:37 -0700364
365 /* Validate the header */
Gabe Black0bf1feb2013-04-26 03:34:00 -0700366 if (!elog_is_header_valid(&elog_area->header)) {
367 header_state = ELOG_HEADER_INVALID;
Gabe Black455c68e2013-04-24 17:54:37 -0700368 return;
369 }
370
Gabe Black0bf1feb2013-04-26 03:34:00 -0700371 header_state = ELOG_HEADER_VALID;
372 elog_update_event_buffer_state();
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700373}
374
Gabe Black0bf1feb2013-04-26 03:34:00 -0700375static void elog_prepare_empty(void)
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700376{
377 struct elog_header *header;
378
Gabe Black0bf1feb2013-04-26 03:34:00 -0700379 elog_debug("elog_prepare_empty()\n");
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700380
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700381 /* Write out the header */
Gabe Black0bf1feb2013-04-26 03:34:00 -0700382 header = &elog_area->header;
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700383 header->magic = ELOG_SIGNATURE;
384 header->version = ELOG_VERSION;
385 header->header_size = sizeof(struct elog_header);
386 header->reserved[0] = ELOG_TYPE_EOL;
387 header->reserved[1] = ELOG_TYPE_EOL;
Gabe Black0bf1feb2013-04-26 03:34:00 -0700388 elog_flash_write(elog_area, header->header_size);
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700389
Gabe Black0bf1feb2013-04-26 03:34:00 -0700390 elog_scan_flash();
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700391}
392
393/*
394 * Shrink the log, deleting old entries and moving the
Martin Roth56889792013-07-09 21:39:46 -0600395 * remaining ones to the front of the log.
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700396 */
397static int elog_shrink(void)
398{
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700399 struct event_header *event;
400 u16 discard_count = 0;
401 u16 offset = 0;
Gabe Blackbfae4aa2013-04-24 03:16:21 -0700402 u16 new_size = 0;
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700403
404 elog_debug("elog_shrink()\n");
405
Gabe Black0bf1feb2013-04-26 03:34:00 -0700406 if (next_event_offset < CONFIG_ELOG_SHRINK_SIZE)
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700407 return 0;
408
409 while (1) {
410 /* Next event has exceeded constraints */
411 if (offset > CONFIG_ELOG_SHRINK_SIZE)
412 break;
413
Gabe Black0bf1feb2013-04-26 03:34:00 -0700414 event = elog_get_event_base(offset);
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700415
416 /* Reached the end of the area */
417 if (!event || event->type == ELOG_TYPE_EOL)
418 break;
419
420 offset += event->length;
421 discard_count++;
422 }
423
Gabe Black0bf1feb2013-04-26 03:34:00 -0700424 new_size = next_event_offset - offset;
425 memmove(&elog_area->data[0], &elog_area->data[offset], new_size);
426 memset(&elog_area->data[new_size], ELOG_TYPE_EOL, log_size - new_size);
Gabe Blackbfae4aa2013-04-24 03:16:21 -0700427
Gabe Black0bf1feb2013-04-26 03:34:00 -0700428 elog_flash_erase(elog_area, total_size);
429 elog_flash_write(elog_area, total_size);
430 elog_scan_flash();
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700431
Duncan Laurie032be822013-05-23 07:23:09 -0700432 /* Ensure the area was successfully erased */
Gabe Black0bf1feb2013-04-26 03:34:00 -0700433 if (next_event_offset >= CONFIG_ELOG_FULL_THRESHOLD) {
Duncan Laurie032be822013-05-23 07:23:09 -0700434 printk(BIOS_ERR, "ELOG: Flash area was not erased!\n");
435 return -1;
436 }
437
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700438 /* Add clear event */
439 elog_add_event_word(ELOG_TYPE_LOG_CLEAR, offset);
440
441 return 0;
442}
443
Duncan Laurie215f27852012-10-10 14:34:49 -0700444#ifndef __SMM__
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700445/*
Duncan Laurie472ec9c2012-06-23 16:13:42 -0700446 * Fill out SMBIOS Type 15 table entry so the
447 * event log can be discovered at runtime.
448 */
449int elog_smbios_write_type15(unsigned long *current, int handle)
450{
451 struct smbios_type15 *t = (struct smbios_type15 *)*current;
452 int len = sizeof(struct smbios_type15);
453
Duncan Laurie215f27852012-10-10 14:34:49 -0700454#if CONFIG_ELOG_CBMEM
455 /* Save event log buffer into CBMEM for the OS to read */
Gabe Black0bf1feb2013-04-26 03:34:00 -0700456 void *cbmem = cbmem_add(CBMEM_ID_ELOG, total_size);
Duncan Laurie215f27852012-10-10 14:34:49 -0700457 if (!cbmem)
458 return 0;
Gabe Black0bf1feb2013-04-26 03:34:00 -0700459 memcpy(cbmem, elog_area, total_size);
Duncan Laurie215f27852012-10-10 14:34:49 -0700460#endif
461
Duncan Laurie472ec9c2012-06-23 16:13:42 -0700462 memset(t, 0, len);
463 t->type = SMBIOS_EVENT_LOG;
464 t->length = len - 2;
465 t->handle = handle;
Gabe Black0bf1feb2013-04-26 03:34:00 -0700466 t->area_length = total_size - 1;
Duncan Laurie472ec9c2012-06-23 16:13:42 -0700467 t->header_offset = 0;
468 t->data_offset = sizeof(struct elog_header);
469 t->access_method = SMBIOS_EVENTLOG_ACCESS_METHOD_MMIO32;
470 t->log_status = SMBIOS_EVENTLOG_STATUS_VALID;
471 t->change_token = 0;
Duncan Laurie215f27852012-10-10 14:34:49 -0700472#if CONFIG_ELOG_CBMEM
473 t->address = (u32)cbmem;
474#else
Gabe Black0bf1feb2013-04-26 03:34:00 -0700475 t->address = (u32)elog_flash_offset_to_address(flash_base);
Duncan Laurie215f27852012-10-10 14:34:49 -0700476#endif
Duncan Laurie472ec9c2012-06-23 16:13:42 -0700477 t->header_format = ELOG_HEADER_TYPE_OEM;
478 t->log_type_descriptors = 0;
479 t->log_type_descriptor_length = 2;
480
481 *current += len;
482 return len;
483}
Duncan Laurie215f27852012-10-10 14:34:49 -0700484#endif
Duncan Laurie472ec9c2012-06-23 16:13:42 -0700485
486/*
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700487 * Clear the entire event log
488 */
489int elog_clear(void)
490{
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700491 elog_debug("elog_clear()\n");
492
Gabe Black8f4baec2013-04-25 17:21:58 -0700493 /* Make sure ELOG structures are initialized */
494 if (elog_init() < 0)
495 return -1;
496
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700497 /* Erase flash area */
Gabe Black0bf1feb2013-04-26 03:34:00 -0700498 elog_flash_erase(elog_area, total_size);
499 elog_prepare_empty();
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700500
Gabe Black0bf1feb2013-04-26 03:34:00 -0700501 if (!elog_is_area_valid())
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700502 return -1;
503
504 /* Log the clear event */
Gabe Black0bf1feb2013-04-26 03:34:00 -0700505 elog_add_event_word(ELOG_TYPE_LOG_CLEAR, total_size);
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700506
507 return 0;
508}
509
Gabe Black0bf1feb2013-04-26 03:34:00 -0700510static void elog_find_flash(void)
Gabe Black84a93d12013-04-24 23:31:41 -0700511{
512#if CONFIG_CHROMEOS
513 u8 *flash_base_ptr;
514#endif
515
Gabe Black0bf1feb2013-04-26 03:34:00 -0700516 elog_debug("elog_find_flash()\n");
Gabe Black84a93d12013-04-24 23:31:41 -0700517
518#if CONFIG_CHROMEOS
519 /* Find the ELOG base and size in FMAP */
Gabe Black0bf1feb2013-04-26 03:34:00 -0700520 total_size = find_fmap_entry("RW_ELOG", (void **)&flash_base_ptr);
521 if (total_size < 0) {
Gabe Black84a93d12013-04-24 23:31:41 -0700522 printk(BIOS_WARNING, "ELOG: Unable to find RW_ELOG in FMAP, "
523 "using CONFIG_ELOG_FLASH_BASE instead\n");
Gabe Black0bf1feb2013-04-26 03:34:00 -0700524 total_size = CONFIG_ELOG_AREA_SIZE;
Gabe Black84a93d12013-04-24 23:31:41 -0700525 } else {
Gabe Black0bf1feb2013-04-26 03:34:00 -0700526 flash_base = elog_flash_address_to_offset(flash_base_ptr);
Gabe Black84a93d12013-04-24 23:31:41 -0700527
528 /* Use configured size if smaller than FMAP size */
Gabe Black0bf1feb2013-04-26 03:34:00 -0700529 if (total_size > CONFIG_ELOG_AREA_SIZE)
530 total_size = CONFIG_ELOG_AREA_SIZE;
Gabe Black84a93d12013-04-24 23:31:41 -0700531 }
532#else
Gabe Black0bf1feb2013-04-26 03:34:00 -0700533 flash_base = CONFIG_ELOG_FLASH_BASE;
534 total_size = CONFIG_ELOG_AREA_SIZE;
Gabe Black84a93d12013-04-24 23:31:41 -0700535#endif
Gabe Black0bf1feb2013-04-26 03:34:00 -0700536 log_size = total_size - sizeof(struct elog_header);
Gabe Black84a93d12013-04-24 23:31:41 -0700537}
538
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700539/*
540 * Event log main entry point
541 */
542int elog_init(void)
543{
544 if (elog_initialized)
545 return 0;
546
547 elog_debug("elog_init()\n");
548
Gabe Black84a93d12013-04-24 23:31:41 -0700549 /* Prepare SPI */
550 spi_init();
551 elog_spi = spi_flash_probe(0, 0, 0, 0);
552 if (!elog_spi) {
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700553 printk(BIOS_ERR, "ELOG: Unable to find SPI flash\n");
554 return -1;
555 }
556
Gabe Black84a93d12013-04-24 23:31:41 -0700557 /* Set up the backing store */
Gabe Black0bf1feb2013-04-26 03:34:00 -0700558 elog_find_flash();
Gabe Black84a93d12013-04-24 23:31:41 -0700559 if (flash_base == 0) {
560 printk(BIOS_ERR, "ELOG: Invalid flash base\n");
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700561 return -1;
562 }
563
Gabe Black0bf1feb2013-04-26 03:34:00 -0700564 elog_area = malloc(total_size);
565 if (!elog_area) {
Gabe Black84a93d12013-04-24 23:31:41 -0700566 printk(BIOS_ERR, "ELOG: Unable to allocate backing store\n");
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700567 return -1;
568 }
Gabe Black84a93d12013-04-24 23:31:41 -0700569
570 /* Load the log from flash */
Gabe Black0bf1feb2013-04-26 03:34:00 -0700571 elog_scan_flash();
Gabe Black84a93d12013-04-24 23:31:41 -0700572
573 /* Prepare the flash if necessary */
Gabe Black0bf1feb2013-04-26 03:34:00 -0700574 if (header_state == ELOG_HEADER_INVALID ||
575 event_buffer_state == ELOG_EVENT_BUFFER_CORRUPTED) {
Gabe Black84a93d12013-04-24 23:31:41 -0700576 /* If the header is invalid or the events are corrupted,
577 * no events can be salvaged so erase the entire area. */
578 printk(BIOS_ERR, "ELOG: flash area invalid\n");
Gabe Black0bf1feb2013-04-26 03:34:00 -0700579 elog_flash_erase(elog_area, total_size);
580 elog_prepare_empty();
Gabe Black84a93d12013-04-24 23:31:41 -0700581 }
582
Gabe Black0bf1feb2013-04-26 03:34:00 -0700583 if (area_state == ELOG_AREA_EMPTY)
584 elog_prepare_empty();
Gabe Black84a93d12013-04-24 23:31:41 -0700585
Gabe Black0bf1feb2013-04-26 03:34:00 -0700586 if (!elog_is_area_valid()) {
Gabe Black84a93d12013-04-24 23:31:41 -0700587 printk(BIOS_ERR, "ELOG: Unable to prepare flash\n");
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700588 return -1;
589 }
590
591 elog_initialized = 1;
592
Gabe Black331eb082013-04-24 04:11:40 -0700593 printk(BIOS_INFO, "ELOG: FLASH @0x%p [SPI 0x%08x]\n",
Gabe Black0bf1feb2013-04-26 03:34:00 -0700594 elog_area, flash_base);
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700595
Gabe Black84a93d12013-04-24 23:31:41 -0700596 printk(BIOS_INFO, "ELOG: area is %d bytes, full threshold %d,"
Gabe Black0bf1feb2013-04-26 03:34:00 -0700597 " shrink size %d\n", total_size,
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700598 CONFIG_ELOG_FULL_THRESHOLD, CONFIG_ELOG_SHRINK_SIZE);
599
600 /* Log a clear event if necessary */
Gabe Black0bf1feb2013-04-26 03:34:00 -0700601 if (event_count == 0)
602 elog_add_event_word(ELOG_TYPE_LOG_CLEAR, total_size);
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700603
Duncan Laurie5c88c6f2012-09-01 14:00:23 -0700604 /* Shrink the log if we are getting too full */
Gabe Black0bf1feb2013-04-26 03:34:00 -0700605 if (next_event_offset >= CONFIG_ELOG_FULL_THRESHOLD)
Duncan Laurie032be822013-05-23 07:23:09 -0700606 if (elog_shrink() < 0)
607 return -1;
Duncan Laurie5c88c6f2012-09-01 14:00:23 -0700608
Gabe Black84a93d12013-04-24 23:31:41 -0700609#if !defined(__SMM__)
Duncan Laurief4d36232012-06-23 16:37:45 -0700610 /* Log boot count event except in S3 resume */
Kyösti Mälkkic3c4a382014-06-20 05:21:30 +0300611 if (CONFIG_ELOG_BOOT_COUNT && !acpi_is_wakeup_s3())
Duncan Laurief4d36232012-06-23 16:37:45 -0700612 elog_add_event_dword(ELOG_TYPE_BOOT, boot_count_read());
Duncan Laurief4d36232012-06-23 16:37:45 -0700613
Duncan Laurie1fc34612012-09-09 19:14:45 -0700614 /* Check and log POST codes from previous boot */
Gabe Black84a93d12013-04-24 23:31:41 -0700615 if (CONFIG_CMOS_POST)
616 cmos_post_log();
Duncan Laurie1fc34612012-09-09 19:14:45 -0700617#endif
618
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700619 return 0;
620}
621
622/*
623 * Populate timestamp in event header with current time
624 */
625static void elog_fill_timestamp(struct event_header *event)
626{
627 event->second = cmos_read(RTC_CLK_SECOND);
628 event->minute = cmos_read(RTC_CLK_MINUTE);
629 event->hour = cmos_read(RTC_CLK_HOUR);
630 event->day = cmos_read(RTC_CLK_DAYOFMONTH);
631 event->month = cmos_read(RTC_CLK_MONTH);
632 event->year = cmos_read(RTC_CLK_YEAR);
633
634 /* Basic sanity check of expected ranges */
635 if (event->month > 0x12 || event->day > 0x31 || event->hour > 0x23 ||
636 event->minute > 0x59 || event->second > 0x59) {
637 event->year = 0;
638 event->month = 0;
639 event->day = 0;
640 event->hour = 0;
641 event->minute = 0;
642 event->second = 0;
643 }
644}
645
646/*
Gabe Black331eb082013-04-24 04:11:40 -0700647 * Add an event to the log
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700648 */
Gabe Black331eb082013-04-24 04:11:40 -0700649void elog_add_event_raw(u8 event_type, void *data, u8 data_size)
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700650{
651 struct event_header *event;
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700652 u8 event_size;
653
Gabe Black331eb082013-04-24 04:11:40 -0700654 elog_debug("elog_add_event_raw(type=%X)\n", event_type);
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700655
656 /* Make sure ELOG structures are initialized */
657 if (elog_init() < 0)
Gabe Black331eb082013-04-24 04:11:40 -0700658 return;
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700659
660 /* Header + Data + Checksum */
661 event_size = sizeof(*event) + data_size + 1;
662 if (event_size > MAX_EVENT_SIZE) {
663 printk(BIOS_ERR, "ELOG: Event(%X) data size too "
664 "big (%d)\n", event_type, event_size);
Gabe Black331eb082013-04-24 04:11:40 -0700665 return;
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700666 }
667
668 /* Make sure event data can fit */
Gabe Black0bf1feb2013-04-26 03:34:00 -0700669 if ((next_event_offset + event_size) >= log_size) {
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700670 printk(BIOS_ERR, "ELOG: Event(%X) does not fit\n",
671 event_type);
Gabe Black331eb082013-04-24 04:11:40 -0700672 return;
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700673 }
674
675 /* Fill out event data */
Gabe Black0bf1feb2013-04-26 03:34:00 -0700676 event = elog_get_event_base(next_event_offset);
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700677 event->type = event_type;
678 event->length = event_size;
679 elog_fill_timestamp(event);
680
681 if (data_size)
682 memcpy(&event[1], data, data_size);
683
684 /* Zero the checksum byte and then compute checksum */
685 elog_update_checksum(event, 0);
686 elog_update_checksum(event, -(elog_checksum_event(event)));
687
Gabe Black0bf1feb2013-04-26 03:34:00 -0700688 /* Update the ELOG state */
689 event_count++;
Gabe Black331eb082013-04-24 04:11:40 -0700690
691 elog_flash_write((void *)event, event_size);
692
Gabe Black0bf1feb2013-04-26 03:34:00 -0700693 next_event_offset += event_size;
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700694
695 printk(BIOS_INFO, "ELOG: Event(%X) added with size %d\n",
696 event_type, event_size);
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700697
698 /* Shrink the log if we are getting too full */
Gabe Black0bf1feb2013-04-26 03:34:00 -0700699 if (next_event_offset >= CONFIG_ELOG_FULL_THRESHOLD)
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700700 elog_shrink();
701}
702
703void elog_add_event(u8 event_type)
704{
705 elog_add_event_raw(event_type, NULL, 0);
706}
707
708void elog_add_event_byte(u8 event_type, u8 data)
709{
710 elog_add_event_raw(event_type, &data, sizeof(data));
711}
712
713void elog_add_event_word(u8 event_type, u16 data)
714{
715 elog_add_event_raw(event_type, &data, sizeof(data));
716}
717
718void elog_add_event_dword(u8 event_type, u32 data)
719{
720 elog_add_event_raw(event_type, &data, sizeof(data));
721}
722
723void elog_add_event_wake(u8 source, u32 instance)
724{
725 struct elog_event_data_wake wake = {
726 .source = source,
727 .instance = instance
728 };
729 elog_add_event_raw(ELOG_TYPE_WAKE_SOURCE, &wake, sizeof(wake));
730}