blob: de349281d6f6ddf6d81eb5efea67e6ebbf0b4d8a [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 */
Gabe Black0bf1feb2013-04-26 03:34:00 -070060static struct elog_area *elog_area;
61static u16 total_size;
62static u16 log_size;
63static u32 flash_base;
64
65static elog_area_state area_state;
66static elog_header_state header_state;
67static elog_event_buffer_state event_buffer_state;
68
69static u16 next_event_offset;
70static u16 event_count;
71
Duncan Laurie7d2b81c2012-06-23 16:08:47 -070072static int elog_initialized;
73static struct spi_flash *elog_spi;
Duncan Laurie7d2b81c2012-06-23 16:08:47 -070074
Aaron Durbin10a070b2013-06-28 12:30:53 -070075
76static inline u32 get_rom_size(void)
77{
78 u32 rom_size;
79
80 /* Assume the used space of the ROM image starts from 0. The
81 * physical size of the device may not be completely used. */
82 rom_size = elog_spi->size;
83 if (rom_size > CONFIG_ROM_SIZE)
84 rom_size = CONFIG_ROM_SIZE;
85
86 return rom_size;
87}
88
Duncan Laurie7d2b81c2012-06-23 16:08:47 -070089/*
90 * Convert a memory mapped flash address into a flash offset
91 */
92static inline u32 elog_flash_address_to_offset(u8 *address)
93{
Aaron Durbin10a070b2013-06-28 12:30:53 -070094 u32 rom_size;
95
Duncan Laurie7d2b81c2012-06-23 16:08:47 -070096 if (!elog_spi)
97 return 0;
Aaron Durbin10a070b2013-06-28 12:30:53 -070098
99 rom_size = get_rom_size();
100
101 return (u32)address - ((u32)~0UL - rom_size + 1);
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700102}
103
104/*
105 * Convert a flash offset into a memory mapped flash address
106 */
107static inline u8* elog_flash_offset_to_address(u32 offset)
108{
Aaron Durbin10a070b2013-06-28 12:30:53 -0700109 u32 rom_size;
110
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700111 if (!elog_spi)
112 return NULL;
Aaron Durbin10a070b2013-06-28 12:30:53 -0700113
114 rom_size = get_rom_size();
115
116 return (u8*)((u32)~0UL - rom_size + 1 + offset);
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700117}
118
119/*
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700120 * Pointer to an event log header in the event data area
121 */
122static inline struct event_header*
Gabe Black0bf1feb2013-04-26 03:34:00 -0700123elog_get_event_base(u32 offset)
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700124{
Gabe Black0bf1feb2013-04-26 03:34:00 -0700125 return (struct event_header *)&elog_area->data[offset];
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700126}
127
128/*
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700129 * Update the checksum at the last byte
130 */
131static void elog_update_checksum(struct event_header *event, u8 checksum)
132{
133 u8 *event_data = (u8*)event;
134 event_data[event->length - 1] = checksum;
135}
136
137/*
138 * Simple byte checksum for events
139 */
140static u8 elog_checksum_event(struct event_header *event)
141{
142 u8 index, checksum = 0;
143 u8 *data = (u8*)event;
144
145 for (index = 0; index < event->length; index++)
146 checksum += data[index];
147 return checksum;
148}
149
150/*
151 * Check if a raw buffer is filled with ELOG_TYPE_EOL byte
152 */
Gabe Black0bf1feb2013-04-26 03:34:00 -0700153static int elog_is_buffer_clear(void *base, u32 size)
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700154{
155 u8 *current = base;
156 u8 *end = current + size;
157
158 elog_debug("elog_is_buffer_clear(base=0x%p size=%u)\n", base, size);
159
160 for (; current != end; current++) {
161 if (*current != ELOG_TYPE_EOL)
162 return 0;
163 }
164 return 1;
165}
166
167/*
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700168 * Check that the ELOG area has been initialized and is valid.
169 */
Gabe Black0bf1feb2013-04-26 03:34:00 -0700170static int elog_is_area_valid(void)
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700171{
172 elog_debug("elog_is_area_valid()\n");
173
Gabe Black0bf1feb2013-04-26 03:34:00 -0700174 if (area_state != ELOG_AREA_HAS_CONTENT)
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700175 return 0;
Gabe Black0bf1feb2013-04-26 03:34:00 -0700176 if (header_state != ELOG_HEADER_VALID)
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700177 return 0;
Gabe Black0bf1feb2013-04-26 03:34:00 -0700178 if (event_buffer_state != ELOG_EVENT_BUFFER_OK)
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700179 return 0;
180 return 1;
181}
182
183/*
184 * Verify the contents of an ELOG Header structure
185 * Returns 1 if the header is valid, 0 otherwise
186 */
187static int elog_is_header_valid(struct elog_header *header)
188{
189 elog_debug("elog_is_header_valid()\n");
190
191 if (header->magic != ELOG_SIGNATURE) {
192 printk(BIOS_ERR, "ELOG: header magic 0x%X != 0x%X\n",
193 header->magic, ELOG_SIGNATURE);
194 return 0;
195 }
196 if (header->version != ELOG_VERSION) {
197 printk(BIOS_ERR, "ELOG: header version %u != %u\n",
198 header->version, ELOG_VERSION);
199 return 0;
200 }
201 if (header->header_size != sizeof(*header)) {
Denis 'GNUtoo' Cariklibc2c9ef2013-06-26 20:04:49 +0200202 printk(BIOS_ERR, "ELOG: header size mismatch %u != %zu\n",
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700203 header->header_size, sizeof(*header));
204 return 0;
205 }
206 return 1;
207}
208
209/*
210 * Validate the event header and data.
211 */
Gabe Black0bf1feb2013-04-26 03:34:00 -0700212static int elog_is_event_valid(u32 offset)
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700213{
214 struct event_header *event;
215
Gabe Black0bf1feb2013-04-26 03:34:00 -0700216 event = elog_get_event_base(offset);
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700217 if (!event)
218 return 0;
219
220 /* Validate event length */
221 if ((offsetof(struct event_header, type) +
Gabe Black0bf1feb2013-04-26 03:34:00 -0700222 sizeof(event->type) - 1 + offset) >= log_size)
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700223 return 0;
224
225 /* End of event marker has been found */
226 if (event->type == ELOG_TYPE_EOL)
227 return 0;
228
229 /* Check if event fits in area */
230 if ((offsetof(struct event_header, length) +
Gabe Black0bf1feb2013-04-26 03:34:00 -0700231 sizeof(event->length) - 1 + offset) >= log_size)
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700232 return 0;
233
234 /*
235 * If the current event length + the current offset exceeds
236 * the area size then the event area is corrupt.
237 */
Gabe Black0bf1feb2013-04-26 03:34:00 -0700238 if ((event->length + offset) >= log_size)
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700239 return 0;
240
241 /* Event length must be at least header size + checksum */
242 if (event->length < (sizeof(*event) + 1))
243 return 0;
244
245 /* If event checksum is invalid the area is corrupt */
246 if (elog_checksum_event(event) != 0)
247 return 0;
248
249 /* Event is valid */
250 return 1;
251}
252
253/*
Gabe Black331eb082013-04-24 04:11:40 -0700254 * Write 'size' bytes of data pointed to by 'address' in the flash backing
255 * store into flash. This will not erase the flash and it assumes the flash
256 * area has been erased appropriately.
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700257 */
Gabe Black0bf1feb2013-04-26 03:34:00 -0700258static void elog_flash_write(void *address, u32 size)
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700259{
260 u32 offset;
261
Gabe Black331eb082013-04-24 04:11:40 -0700262 if (!address || !size || !elog_spi)
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700263 return;
264
Gabe Black0bf1feb2013-04-26 03:34:00 -0700265 offset = flash_base;
266 offset += (u8 *)address - (u8 *)elog_area;
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700267
Gabe Black331eb082013-04-24 04:11:40 -0700268 elog_debug("elog_flash_write(address=0x%p offset=0x%08x size=%u)\n",
269 address, offset, size);
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700270
271 /* Write the data to flash */
Gabe Black331eb082013-04-24 04:11:40 -0700272 elog_spi->write(elog_spi, offset, size, address);
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700273}
274
275/*
276 * Erase the first block specified in the address.
277 * Only handles flash area within a single flash block.
278 */
Gabe Black0bf1feb2013-04-26 03:34:00 -0700279static void elog_flash_erase(void *address, u32 size)
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700280{
281 u32 offset;
282
283 if (!address || !size || !elog_spi)
284 return;
285
Gabe Black0bf1feb2013-04-26 03:34:00 -0700286 offset = flash_base;
287 offset += (u8 *)address - (u8*)elog_area;
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700288
289 elog_debug("elog_flash_erase(address=0x%p offset=0x%08x size=%u)\n",
290 address, offset, size);
291
292 /* Erase the sectors in this region */
293 elog_spi->erase(elog_spi, offset, size);
294}
295
296/*
Gabe Black0bf1feb2013-04-26 03:34:00 -0700297 * Scan the event area and validate each entry and update the ELOG state.
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700298 */
Gabe Black0bf1feb2013-04-26 03:34:00 -0700299static void elog_update_event_buffer_state(void)
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700300{
301 u32 count = 0;
302 u32 offset = 0;
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700303 struct event_header *event;
304
305 elog_debug("elog_update_event_buffer_state()\n");
306
307 /* Go through each event and validate it */
308 while (1) {
Gabe Black0bf1feb2013-04-26 03:34:00 -0700309 event = elog_get_event_base(offset);
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700310
311 /* Do not de-reference anything past the area length */
312 if ((offsetof(struct event_header, type) +
Gabe Black0bf1feb2013-04-26 03:34:00 -0700313 sizeof(event->type) - 1 + offset) >= log_size) {
314 event_buffer_state = ELOG_EVENT_BUFFER_CORRUPTED;
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700315 break;
316 }
317
318 /* The end of the event marker has been found */
319 if (event->type == ELOG_TYPE_EOL)
320 break;
321
322 /* Validate the event */
Gabe Black0bf1feb2013-04-26 03:34:00 -0700323 if (!elog_is_event_valid(offset)) {
324 event_buffer_state = ELOG_EVENT_BUFFER_CORRUPTED;
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700325 break;
326 }
327
328 /* Move to the next event */
329 count++;
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700330 offset += event->length;
331 }
332
333 /* Ensure the remaining buffer is empty */
Gabe Black0bf1feb2013-04-26 03:34:00 -0700334 if (!elog_is_buffer_clear(&elog_area->data[offset], log_size - offset))
335 event_buffer_state = ELOG_EVENT_BUFFER_CORRUPTED;
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700336
Gabe Black0bf1feb2013-04-26 03:34:00 -0700337 /* Update ELOG state */
338 event_count = count;
339 next_event_offset = offset;
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700340}
341
Gabe Black0bf1feb2013-04-26 03:34:00 -0700342static void elog_scan_flash(void)
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700343{
Gabe Black84a93d12013-04-24 23:31:41 -0700344 elog_debug("elog_scan_flash()\n");
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700345
Gabe Black0bf1feb2013-04-26 03:34:00 -0700346 area_state = ELOG_AREA_UNDEFINED;
347 header_state = ELOG_HEADER_INVALID;
348 event_buffer_state = ELOG_EVENT_BUFFER_OK;
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700349
Duncan Laurie215f27852012-10-10 14:34:49 -0700350 /* Fill memory buffer by reading from SPI */
Gabe Black0bf1feb2013-04-26 03:34:00 -0700351 elog_spi->read(elog_spi, flash_base, total_size, elog_area);
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700352
Gabe Black0bf1feb2013-04-26 03:34:00 -0700353 next_event_offset = 0;
354 event_count = 0;
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700355
Gabe Black455c68e2013-04-24 17:54:37 -0700356 /* Check if the area is empty or not */
Gabe Black0bf1feb2013-04-26 03:34:00 -0700357 if (elog_is_buffer_clear(elog_area, total_size)) {
358 area_state = ELOG_AREA_EMPTY;
Gabe Black455c68e2013-04-24 17:54:37 -0700359 return;
360 }
361
Gabe Black0bf1feb2013-04-26 03:34:00 -0700362 area_state = ELOG_AREA_HAS_CONTENT;
Gabe Black455c68e2013-04-24 17:54:37 -0700363
364 /* Validate the header */
Gabe Black0bf1feb2013-04-26 03:34:00 -0700365 if (!elog_is_header_valid(&elog_area->header)) {
366 header_state = ELOG_HEADER_INVALID;
Gabe Black455c68e2013-04-24 17:54:37 -0700367 return;
368 }
369
Gabe Black0bf1feb2013-04-26 03:34:00 -0700370 header_state = ELOG_HEADER_VALID;
371 elog_update_event_buffer_state();
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700372}
373
Gabe Black0bf1feb2013-04-26 03:34:00 -0700374static void elog_prepare_empty(void)
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700375{
376 struct elog_header *header;
377
Gabe Black0bf1feb2013-04-26 03:34:00 -0700378 elog_debug("elog_prepare_empty()\n");
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700379
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700380 /* Write out the header */
Gabe Black0bf1feb2013-04-26 03:34:00 -0700381 header = &elog_area->header;
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700382 header->magic = ELOG_SIGNATURE;
383 header->version = ELOG_VERSION;
384 header->header_size = sizeof(struct elog_header);
385 header->reserved[0] = ELOG_TYPE_EOL;
386 header->reserved[1] = ELOG_TYPE_EOL;
Gabe Black0bf1feb2013-04-26 03:34:00 -0700387 elog_flash_write(elog_area, header->header_size);
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700388
Gabe Black0bf1feb2013-04-26 03:34:00 -0700389 elog_scan_flash();
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700390}
391
392/*
393 * Shrink the log, deleting old entries and moving the
Martin Roth56889792013-07-09 21:39:46 -0600394 * remaining ones to the front of the log.
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700395 */
396static int elog_shrink(void)
397{
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700398 struct event_header *event;
399 u16 discard_count = 0;
400 u16 offset = 0;
Gabe Blackbfae4aa2013-04-24 03:16:21 -0700401 u16 new_size = 0;
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700402
403 elog_debug("elog_shrink()\n");
404
Gabe Black0bf1feb2013-04-26 03:34:00 -0700405 if (next_event_offset < CONFIG_ELOG_SHRINK_SIZE)
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700406 return 0;
407
408 while (1) {
409 /* Next event has exceeded constraints */
410 if (offset > CONFIG_ELOG_SHRINK_SIZE)
411 break;
412
Gabe Black0bf1feb2013-04-26 03:34:00 -0700413 event = elog_get_event_base(offset);
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700414
415 /* Reached the end of the area */
416 if (!event || event->type == ELOG_TYPE_EOL)
417 break;
418
419 offset += event->length;
420 discard_count++;
421 }
422
Gabe Black0bf1feb2013-04-26 03:34:00 -0700423 new_size = next_event_offset - offset;
424 memmove(&elog_area->data[0], &elog_area->data[offset], new_size);
425 memset(&elog_area->data[new_size], ELOG_TYPE_EOL, log_size - new_size);
Gabe Blackbfae4aa2013-04-24 03:16:21 -0700426
Gabe Black0bf1feb2013-04-26 03:34:00 -0700427 elog_flash_erase(elog_area, total_size);
428 elog_flash_write(elog_area, total_size);
429 elog_scan_flash();
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700430
Duncan Laurie032be822013-05-23 07:23:09 -0700431 /* Ensure the area was successfully erased */
Gabe Black0bf1feb2013-04-26 03:34:00 -0700432 if (next_event_offset >= CONFIG_ELOG_FULL_THRESHOLD) {
Duncan Laurie032be822013-05-23 07:23:09 -0700433 printk(BIOS_ERR, "ELOG: Flash area was not erased!\n");
434 return -1;
435 }
436
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700437 /* Add clear event */
438 elog_add_event_word(ELOG_TYPE_LOG_CLEAR, offset);
439
440 return 0;
441}
442
Duncan Laurie215f27852012-10-10 14:34:49 -0700443#ifndef __SMM__
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700444/*
Duncan Laurie472ec9c2012-06-23 16:13:42 -0700445 * Fill out SMBIOS Type 15 table entry so the
446 * event log can be discovered at runtime.
447 */
448int elog_smbios_write_type15(unsigned long *current, int handle)
449{
450 struct smbios_type15 *t = (struct smbios_type15 *)*current;
451 int len = sizeof(struct smbios_type15);
452
Duncan Laurie215f27852012-10-10 14:34:49 -0700453#if CONFIG_ELOG_CBMEM
454 /* Save event log buffer into CBMEM for the OS to read */
Gabe Black0bf1feb2013-04-26 03:34:00 -0700455 void *cbmem = cbmem_add(CBMEM_ID_ELOG, total_size);
Duncan Laurie215f27852012-10-10 14:34:49 -0700456 if (!cbmem)
457 return 0;
Gabe Black0bf1feb2013-04-26 03:34:00 -0700458 memcpy(cbmem, elog_area, total_size);
Duncan Laurie215f27852012-10-10 14:34:49 -0700459#endif
460
Duncan Laurie472ec9c2012-06-23 16:13:42 -0700461 memset(t, 0, len);
462 t->type = SMBIOS_EVENT_LOG;
463 t->length = len - 2;
464 t->handle = handle;
Gabe Black0bf1feb2013-04-26 03:34:00 -0700465 t->area_length = total_size - 1;
Duncan Laurie472ec9c2012-06-23 16:13:42 -0700466 t->header_offset = 0;
467 t->data_offset = sizeof(struct elog_header);
468 t->access_method = SMBIOS_EVENTLOG_ACCESS_METHOD_MMIO32;
469 t->log_status = SMBIOS_EVENTLOG_STATUS_VALID;
470 t->change_token = 0;
Duncan Laurie215f27852012-10-10 14:34:49 -0700471#if CONFIG_ELOG_CBMEM
472 t->address = (u32)cbmem;
473#else
Gabe Black0bf1feb2013-04-26 03:34:00 -0700474 t->address = (u32)elog_flash_offset_to_address(flash_base);
Duncan Laurie215f27852012-10-10 14:34:49 -0700475#endif
Duncan Laurie472ec9c2012-06-23 16:13:42 -0700476 t->header_format = ELOG_HEADER_TYPE_OEM;
477 t->log_type_descriptors = 0;
478 t->log_type_descriptor_length = 2;
479
480 *current += len;
481 return len;
482}
Duncan Laurie215f27852012-10-10 14:34:49 -0700483#endif
Duncan Laurie472ec9c2012-06-23 16:13:42 -0700484
485/*
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700486 * Clear the entire event log
487 */
488int elog_clear(void)
489{
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700490 elog_debug("elog_clear()\n");
491
Gabe Black8f4baec2013-04-25 17:21:58 -0700492 /* Make sure ELOG structures are initialized */
493 if (elog_init() < 0)
494 return -1;
495
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700496 /* Erase flash area */
Gabe Black0bf1feb2013-04-26 03:34:00 -0700497 elog_flash_erase(elog_area, total_size);
498 elog_prepare_empty();
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700499
Gabe Black0bf1feb2013-04-26 03:34:00 -0700500 if (!elog_is_area_valid())
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700501 return -1;
502
503 /* Log the clear event */
Gabe Black0bf1feb2013-04-26 03:34:00 -0700504 elog_add_event_word(ELOG_TYPE_LOG_CLEAR, total_size);
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700505
506 return 0;
507}
508
Gabe Black0bf1feb2013-04-26 03:34:00 -0700509static void elog_find_flash(void)
Gabe Black84a93d12013-04-24 23:31:41 -0700510{
511#if CONFIG_CHROMEOS
512 u8 *flash_base_ptr;
513#endif
514
Gabe Black0bf1feb2013-04-26 03:34:00 -0700515 elog_debug("elog_find_flash()\n");
Gabe Black84a93d12013-04-24 23:31:41 -0700516
517#if CONFIG_CHROMEOS
518 /* Find the ELOG base and size in FMAP */
Gabe Black0bf1feb2013-04-26 03:34:00 -0700519 total_size = find_fmap_entry("RW_ELOG", (void **)&flash_base_ptr);
520 if (total_size < 0) {
Gabe Black84a93d12013-04-24 23:31:41 -0700521 printk(BIOS_WARNING, "ELOG: Unable to find RW_ELOG in FMAP, "
522 "using CONFIG_ELOG_FLASH_BASE instead\n");
Gabe Black0bf1feb2013-04-26 03:34:00 -0700523 total_size = CONFIG_ELOG_AREA_SIZE;
Gabe Black84a93d12013-04-24 23:31:41 -0700524 } else {
Gabe Black0bf1feb2013-04-26 03:34:00 -0700525 flash_base = elog_flash_address_to_offset(flash_base_ptr);
Gabe Black84a93d12013-04-24 23:31:41 -0700526
527 /* Use configured size if smaller than FMAP size */
Gabe Black0bf1feb2013-04-26 03:34:00 -0700528 if (total_size > CONFIG_ELOG_AREA_SIZE)
529 total_size = CONFIG_ELOG_AREA_SIZE;
Gabe Black84a93d12013-04-24 23:31:41 -0700530 }
531#else
Gabe Black0bf1feb2013-04-26 03:34:00 -0700532 flash_base = CONFIG_ELOG_FLASH_BASE;
533 total_size = CONFIG_ELOG_AREA_SIZE;
Gabe Black84a93d12013-04-24 23:31:41 -0700534#endif
Gabe Black0bf1feb2013-04-26 03:34:00 -0700535 log_size = total_size - sizeof(struct elog_header);
Gabe Black84a93d12013-04-24 23:31:41 -0700536}
537
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700538/*
539 * Event log main entry point
540 */
541int elog_init(void)
542{
543 if (elog_initialized)
544 return 0;
545
546 elog_debug("elog_init()\n");
547
Gabe Black84a93d12013-04-24 23:31:41 -0700548 /* Prepare SPI */
549 spi_init();
550 elog_spi = spi_flash_probe(0, 0, 0, 0);
551 if (!elog_spi) {
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700552 printk(BIOS_ERR, "ELOG: Unable to find SPI flash\n");
553 return -1;
554 }
555
Gabe Black84a93d12013-04-24 23:31:41 -0700556 /* Set up the backing store */
Gabe Black0bf1feb2013-04-26 03:34:00 -0700557 elog_find_flash();
Gabe Black84a93d12013-04-24 23:31:41 -0700558 if (flash_base == 0) {
559 printk(BIOS_ERR, "ELOG: Invalid flash base\n");
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700560 return -1;
561 }
562
Gabe Black0bf1feb2013-04-26 03:34:00 -0700563 elog_area = malloc(total_size);
564 if (!elog_area) {
Gabe Black84a93d12013-04-24 23:31:41 -0700565 printk(BIOS_ERR, "ELOG: Unable to allocate backing store\n");
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700566 return -1;
567 }
Gabe Black84a93d12013-04-24 23:31:41 -0700568
569 /* Load the log from flash */
Gabe Black0bf1feb2013-04-26 03:34:00 -0700570 elog_scan_flash();
Gabe Black84a93d12013-04-24 23:31:41 -0700571
572 /* Prepare the flash if necessary */
Gabe Black0bf1feb2013-04-26 03:34:00 -0700573 if (header_state == ELOG_HEADER_INVALID ||
574 event_buffer_state == ELOG_EVENT_BUFFER_CORRUPTED) {
Gabe Black84a93d12013-04-24 23:31:41 -0700575 /* If the header is invalid or the events are corrupted,
576 * no events can be salvaged so erase the entire area. */
577 printk(BIOS_ERR, "ELOG: flash area invalid\n");
Gabe Black0bf1feb2013-04-26 03:34:00 -0700578 elog_flash_erase(elog_area, total_size);
579 elog_prepare_empty();
Gabe Black84a93d12013-04-24 23:31:41 -0700580 }
581
Gabe Black0bf1feb2013-04-26 03:34:00 -0700582 if (area_state == ELOG_AREA_EMPTY)
583 elog_prepare_empty();
Gabe Black84a93d12013-04-24 23:31:41 -0700584
Gabe Black0bf1feb2013-04-26 03:34:00 -0700585 if (!elog_is_area_valid()) {
Gabe Black84a93d12013-04-24 23:31:41 -0700586 printk(BIOS_ERR, "ELOG: Unable to prepare flash\n");
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700587 return -1;
588 }
589
590 elog_initialized = 1;
591
Gabe Black331eb082013-04-24 04:11:40 -0700592 printk(BIOS_INFO, "ELOG: FLASH @0x%p [SPI 0x%08x]\n",
Gabe Black0bf1feb2013-04-26 03:34:00 -0700593 elog_area, flash_base);
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700594
Gabe Black84a93d12013-04-24 23:31:41 -0700595 printk(BIOS_INFO, "ELOG: area is %d bytes, full threshold %d,"
Gabe Black0bf1feb2013-04-26 03:34:00 -0700596 " shrink size %d\n", total_size,
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700597 CONFIG_ELOG_FULL_THRESHOLD, CONFIG_ELOG_SHRINK_SIZE);
598
599 /* Log a clear event if necessary */
Gabe Black0bf1feb2013-04-26 03:34:00 -0700600 if (event_count == 0)
601 elog_add_event_word(ELOG_TYPE_LOG_CLEAR, total_size);
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700602
Duncan Laurie5c88c6f2012-09-01 14:00:23 -0700603 /* Shrink the log if we are getting too full */
Gabe Black0bf1feb2013-04-26 03:34:00 -0700604 if (next_event_offset >= CONFIG_ELOG_FULL_THRESHOLD)
Duncan Laurie032be822013-05-23 07:23:09 -0700605 if (elog_shrink() < 0)
606 return -1;
Duncan Laurie5c88c6f2012-09-01 14:00:23 -0700607
Gabe Black84a93d12013-04-24 23:31:41 -0700608#if !defined(__SMM__)
Duncan Laurief4d36232012-06-23 16:37:45 -0700609 /* Log boot count event except in S3 resume */
Gabe Black84a93d12013-04-24 23:31:41 -0700610 if (CONFIG_ELOG_BOOT_COUNT && acpi_slp_type != 3)
Duncan Laurief4d36232012-06-23 16:37:45 -0700611 elog_add_event_dword(ELOG_TYPE_BOOT, boot_count_read());
Duncan Laurief4d36232012-06-23 16:37:45 -0700612
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
617
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700618 return 0;
619}
620
621/*
622 * Populate timestamp in event header with current time
623 */
624static void elog_fill_timestamp(struct event_header *event)
625{
626 event->second = cmos_read(RTC_CLK_SECOND);
627 event->minute = cmos_read(RTC_CLK_MINUTE);
628 event->hour = cmos_read(RTC_CLK_HOUR);
629 event->day = cmos_read(RTC_CLK_DAYOFMONTH);
630 event->month = cmos_read(RTC_CLK_MONTH);
631 event->year = cmos_read(RTC_CLK_YEAR);
632
633 /* Basic sanity check of expected ranges */
634 if (event->month > 0x12 || event->day > 0x31 || event->hour > 0x23 ||
635 event->minute > 0x59 || event->second > 0x59) {
636 event->year = 0;
637 event->month = 0;
638 event->day = 0;
639 event->hour = 0;
640 event->minute = 0;
641 event->second = 0;
642 }
643}
644
645/*
Gabe Black331eb082013-04-24 04:11:40 -0700646 * Add an event to the log
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700647 */
Gabe Black331eb082013-04-24 04:11:40 -0700648void elog_add_event_raw(u8 event_type, void *data, u8 data_size)
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700649{
650 struct event_header *event;
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700651 u8 event_size;
652
Gabe Black331eb082013-04-24 04:11:40 -0700653 elog_debug("elog_add_event_raw(type=%X)\n", event_type);
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700654
655 /* Make sure ELOG structures are initialized */
656 if (elog_init() < 0)
Gabe Black331eb082013-04-24 04:11:40 -0700657 return;
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700658
659 /* Header + Data + Checksum */
660 event_size = sizeof(*event) + data_size + 1;
661 if (event_size > MAX_EVENT_SIZE) {
662 printk(BIOS_ERR, "ELOG: Event(%X) data size too "
663 "big (%d)\n", event_type, event_size);
Gabe Black331eb082013-04-24 04:11:40 -0700664 return;
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700665 }
666
667 /* Make sure event data can fit */
Gabe Black0bf1feb2013-04-26 03:34:00 -0700668 if ((next_event_offset + event_size) >= log_size) {
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700669 printk(BIOS_ERR, "ELOG: Event(%X) does not fit\n",
670 event_type);
Gabe Black331eb082013-04-24 04:11:40 -0700671 return;
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700672 }
673
674 /* Fill out event data */
Gabe Black0bf1feb2013-04-26 03:34:00 -0700675 event = elog_get_event_base(next_event_offset);
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700676 event->type = event_type;
677 event->length = event_size;
678 elog_fill_timestamp(event);
679
680 if (data_size)
681 memcpy(&event[1], data, data_size);
682
683 /* Zero the checksum byte and then compute checksum */
684 elog_update_checksum(event, 0);
685 elog_update_checksum(event, -(elog_checksum_event(event)));
686
Gabe Black0bf1feb2013-04-26 03:34:00 -0700687 /* Update the ELOG state */
688 event_count++;
Gabe Black331eb082013-04-24 04:11:40 -0700689
690 elog_flash_write((void *)event, event_size);
691
Gabe Black0bf1feb2013-04-26 03:34:00 -0700692 next_event_offset += event_size;
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700693
694 printk(BIOS_INFO, "ELOG: Event(%X) added with size %d\n",
695 event_type, event_size);
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700696
697 /* Shrink the log if we are getting too full */
Gabe Black0bf1feb2013-04-26 03:34:00 -0700698 if (next_event_offset >= CONFIG_ELOG_FULL_THRESHOLD)
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700699 elog_shrink();
700}
701
702void elog_add_event(u8 event_type)
703{
704 elog_add_event_raw(event_type, NULL, 0);
705}
706
707void elog_add_event_byte(u8 event_type, u8 data)
708{
709 elog_add_event_raw(event_type, &data, sizeof(data));
710}
711
712void elog_add_event_word(u8 event_type, u16 data)
713{
714 elog_add_event_raw(event_type, &data, sizeof(data));
715}
716
717void elog_add_event_dword(u8 event_type, u32 data)
718{
719 elog_add_event_raw(event_type, &data, sizeof(data));
720}
721
722void elog_add_event_wake(u8 source, u32 instance)
723{
724 struct elog_event_data_wake wake = {
725 .source = source,
726 .instance = instance
727 };
728 elog_add_event_raw(ELOG_TYPE_WAKE_SOURCE, &wake, sizeof(wake));
729}