blob: 0d16b2bed599737c39bd52e16bf7b84ccdb5a6c5 [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
Martin Roth32c27c22017-06-24 14:07:25 -060016#if IS_ENABLED(CONFIG_HAVE_ACPI_RESUME)
Duncan Laurie7d2b81c2012-06-23 16:08:47 -070017#include <arch/acpi.h>
David Hendricks259e49a2014-03-20 20:31:23 -070018#endif
Karthikeyan Ramasubramanian07bc08c2018-11-06 12:59:18 -070019#include <arch/early_variables.h>
Julius Wernera46ee4d2016-08-08 16:18:28 -070020#include <bootstate.h>
Duncan Laurie215f27852012-10-10 14:34:49 -070021#include <cbmem.h>
Duncan Laurie7d2b81c2012-06-23 16:08:47 -070022#include <console/console.h>
Martin Roth32c27c22017-06-24 14:07:25 -060023#if IS_ENABLED(CONFIG_ARCH_X86)
Duncan Laurie7d2b81c2012-06-23 16:08:47 -070024#include <pc80/mc146818rtc.h>
David Hendricks259e49a2014-03-20 20:31:23 -070025#endif
Gabe Blacka4c4b1c2014-04-30 21:41:23 -070026#include <bcd.h>
Furquan Shaikhe19d9af2016-07-15 15:54:36 -070027#include <boot_device.h>
28#include <commonlib/region.h>
Aaron Durbin0424c952015-03-28 23:56:22 -050029#include <fmap.h>
Aaron Durbin12974432016-08-09 17:01:27 -050030#include <lib.h>
Gabe Blacka4c4b1c2014-04-30 21:41:23 -070031#include <rtc.h>
Duncan Laurie472ec9c2012-06-23 16:13:42 -070032#include <smbios.h>
Duncan Laurie7d2b81c2012-06-23 16:08:47 -070033#include <stdint.h>
34#include <string.h>
35#include <elog.h>
36#include "elog_internal.h"
37
Edward O'Callaghana3119e52014-06-18 14:28:03 +100038
Aaron Durbin12974432016-08-09 17:01:27 -050039#if IS_ENABLED(CONFIG_ELOG_DEBUG)
Duncan Laurie7d2b81c2012-06-23 16:08:47 -070040#define elog_debug(STR...) printk(BIOS_DEBUG, STR)
41#else
42#define elog_debug(STR...)
43#endif
44
Aaron Durbin0284f622016-08-05 14:33:53 -050045#define NV_NEEDS_ERASE (~(size_t)0)
Karthikeyan Ramasubramanian07bc08c2018-11-06 12:59:18 -070046enum elog_init_state {
Julius Werner5d686222015-01-27 15:40:47 -080047 ELOG_UNINITIALIZED = 0,
48 ELOG_INITIALIZED,
49 ELOG_BROKEN,
Karthikeyan Ramasubramanian07bc08c2018-11-06 12:59:18 -070050};
51
52struct elog_state {
53 u16 full_threshold;
54 u16 shrink_size;
55
56 /*
57 * The non-volatile storage chases the mirrored copy. When nv_last_write
58 * is less than the mirrored last write the non-volatile storage needs
59 * to be updated.
60 */
61 size_t mirror_last_write;
62 size_t nv_last_write;
63
64 struct region_device nv_dev;
65 /* Device that mirrors the eventlog in memory. */
66 struct mem_region_device mirror_dev;
67
68 enum elog_init_state elog_initialized;
69};
70
71static struct elog_state g_elog_state CAR_GLOBAL;
Aaron Durbin10a070b2013-06-28 12:30:53 -070072
Karthikeyan Ramasubramanian5ac4b852018-11-08 11:57:51 -070073#define ELOG_SIZE (4 * KiB)
74static uint8_t elog_mirror_buf[ELOG_SIZE] CAR_GLOBAL;
75
76static void *get_elog_mirror_buffer(void)
77{
78 return car_get_var_ptr(elog_mirror_buf);
79}
80
Aaron Durbin281c9942016-08-04 14:25:59 -050081static inline struct region_device *mirror_dev_get(void)
82{
Karthikeyan Ramasubramanian07bc08c2018-11-06 12:59:18 -070083 struct elog_state *es = car_get_var_ptr(&g_elog_state);
84
85 return &es->mirror_dev.rdev;
Aaron Durbin281c9942016-08-04 14:25:59 -050086}
87
Aaron Durbinf41a9a62016-08-04 15:59:35 -050088static size_t elog_events_start(void)
89{
90 /* Events are added directly after the header. */
91 return sizeof(struct elog_header);
92}
93
94static size_t elog_events_total_space(void)
95{
Karthikeyan Ramasubramanian07bc08c2018-11-06 12:59:18 -070096 struct elog_state *es = car_get_var_ptr(&g_elog_state);
97
98 return region_device_sz(&es->nv_dev) - elog_events_start();
Aaron Durbinf41a9a62016-08-04 15:59:35 -050099}
100
Aaron Durbin281c9942016-08-04 14:25:59 -0500101static struct event_header *elog_get_event_buffer(size_t offset, size_t size)
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700102{
Aaron Durbin281c9942016-08-04 14:25:59 -0500103 return rdev_mmap(mirror_dev_get(), offset, size);
104}
105
Aaron Durbin0284f622016-08-05 14:33:53 -0500106static struct event_header *elog_get_next_event_buffer(size_t size)
107{
Karthikeyan Ramasubramanian07bc08c2018-11-06 12:59:18 -0700108 struct elog_state *es = car_get_var_ptr(&g_elog_state);
109
110 elog_debug("ELOG: new event at offset 0x%zx\n", es->mirror_last_write);
111 return elog_get_event_buffer(es->mirror_last_write, size);
Aaron Durbin0284f622016-08-05 14:33:53 -0500112}
113
Aaron Durbin281c9942016-08-04 14:25:59 -0500114static void elog_put_event_buffer(struct event_header *event)
115{
116 rdev_munmap(mirror_dev_get(), event);
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700117}
118
Aaron Durbin0284f622016-08-05 14:33:53 -0500119static size_t elog_mirror_reset_last_write(void)
120{
Karthikeyan Ramasubramanian07bc08c2018-11-06 12:59:18 -0700121 struct elog_state *es = car_get_var_ptr(&g_elog_state);
Aaron Durbin0284f622016-08-05 14:33:53 -0500122 /* Return previous write value. */
Karthikeyan Ramasubramanian07bc08c2018-11-06 12:59:18 -0700123 size_t prev = es->mirror_last_write;
124
125 es->mirror_last_write = 0;
Aaron Durbin0284f622016-08-05 14:33:53 -0500126 return prev;
127}
128
129static void elog_mirror_increment_last_write(size_t size)
130{
Karthikeyan Ramasubramanian07bc08c2018-11-06 12:59:18 -0700131 struct elog_state *es = car_get_var_ptr(&g_elog_state);
132
133 es->mirror_last_write += size;
Aaron Durbin0284f622016-08-05 14:33:53 -0500134}
135
136static void elog_nv_reset_last_write(void)
137{
Karthikeyan Ramasubramanian07bc08c2018-11-06 12:59:18 -0700138 struct elog_state *es = car_get_var_ptr(&g_elog_state);
139
140 es->nv_last_write = 0;
Aaron Durbin0284f622016-08-05 14:33:53 -0500141}
142
143static void elog_nv_increment_last_write(size_t size)
144{
Karthikeyan Ramasubramanian07bc08c2018-11-06 12:59:18 -0700145 struct elog_state *es = car_get_var_ptr(&g_elog_state);
146
147 es->nv_last_write += size;
Aaron Durbin0284f622016-08-05 14:33:53 -0500148}
149
150static void elog_nv_needs_possible_erase(void)
151{
Karthikeyan Ramasubramanian07bc08c2018-11-06 12:59:18 -0700152 struct elog_state *es = car_get_var_ptr(&g_elog_state);
153
Aaron Durbin0284f622016-08-05 14:33:53 -0500154 /* If last write is 0 it means it is already erased. */
Karthikeyan Ramasubramanian07bc08c2018-11-06 12:59:18 -0700155 if (es->nv_last_write != 0)
156 es->nv_last_write = NV_NEEDS_ERASE;
Aaron Durbin0284f622016-08-05 14:33:53 -0500157}
158
159static bool elog_should_shrink(void)
160{
Karthikeyan Ramasubramanian07bc08c2018-11-06 12:59:18 -0700161 struct elog_state *es = car_get_var_ptr(&g_elog_state);
162
163 return es->mirror_last_write >= es->full_threshold;
Aaron Durbin0284f622016-08-05 14:33:53 -0500164}
165
166static bool elog_nv_needs_erase(void)
167{
Karthikeyan Ramasubramanian07bc08c2018-11-06 12:59:18 -0700168 struct elog_state *es = car_get_var_ptr(&g_elog_state);
169
170 return es->nv_last_write == NV_NEEDS_ERASE;
Aaron Durbin0284f622016-08-05 14:33:53 -0500171}
172
173static bool elog_nv_needs_update(void)
174{
Karthikeyan Ramasubramanian07bc08c2018-11-06 12:59:18 -0700175 struct elog_state *es = car_get_var_ptr(&g_elog_state);
176
177 return es->nv_last_write != es->mirror_last_write;
Aaron Durbin0284f622016-08-05 14:33:53 -0500178}
179
180static size_t elog_nv_region_to_update(size_t *offset)
181{
Karthikeyan Ramasubramanian07bc08c2018-11-06 12:59:18 -0700182 struct elog_state *es = car_get_var_ptr(&g_elog_state);
183
184 *offset = es->nv_last_write;
185 return es->mirror_last_write - es->nv_last_write;
Aaron Durbin0284f622016-08-05 14:33:53 -0500186}
187
188/*
189 * When parsing state from the NV one needs to adjust both the NV and mirror
190 * write state. Therefore, provide helper functions which adjust both
191 * at the same time.
192 */
193static void elog_tandem_reset_last_write(void)
194{
195 elog_mirror_reset_last_write();
196 elog_nv_reset_last_write();
197}
198
199static void elog_tandem_increment_last_write(size_t size)
200{
201 elog_mirror_increment_last_write(size);
202 elog_nv_increment_last_write(size);
203}
204
Aaron Durbin12974432016-08-09 17:01:27 -0500205static void elog_debug_dump_buffer(const char *msg)
206{
207 struct region_device *rdev;
208 void *buffer;
209
210 if (!IS_ENABLED(CONFIG_ELOG_DEBUG))
211 return;
212
213 elog_debug(msg);
214
215 rdev = mirror_dev_get();
216
217 buffer = rdev_mmap_full(rdev);
218
219 if (buffer == NULL)
220 return;
221
Aaron Durbind10f9d52016-08-10 11:37:14 -0500222 hexdump(buffer, region_device_sz(rdev));
Aaron Durbin12974432016-08-09 17:01:27 -0500223
224 rdev_munmap(rdev, buffer);
225}
226
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700227/*
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700228 * Update the checksum at the last byte
229 */
230static void elog_update_checksum(struct event_header *event, u8 checksum)
231{
232 u8 *event_data = (u8*)event;
233 event_data[event->length - 1] = checksum;
234}
235
236/*
237 * Simple byte checksum for events
238 */
239static u8 elog_checksum_event(struct event_header *event)
240{
241 u8 index, checksum = 0;
242 u8 *data = (u8*)event;
243
244 for (index = 0; index < event->length; index++)
245 checksum += data[index];
246 return checksum;
247}
248
249/*
Aaron Durbin422c4402016-08-04 10:21:06 -0500250 * Check if mirrored buffer is filled with ELOG_TYPE_EOL byte from the
251 * provided offset to the end of the mirrored buffer.
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700252 */
Aaron Durbin422c4402016-08-04 10:21:06 -0500253static int elog_is_buffer_clear(size_t offset)
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700254{
Aaron Durbin281c9942016-08-04 14:25:59 -0500255 size_t i;
256 const struct region_device *rdev = mirror_dev_get();
257 size_t size = region_device_sz(rdev) - offset;
258 uint8_t *buffer = rdev_mmap(rdev, offset, size);
259 int ret = 1;
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700260
Aaron Durbin422c4402016-08-04 10:21:06 -0500261 elog_debug("elog_is_buffer_clear(offset=%zu size=%zu)\n", offset, size);
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700262
Aaron Durbin281c9942016-08-04 14:25:59 -0500263 if (buffer == NULL)
264 return 0;
265
266 for (i = 0; i < size; i++) {
267 if (buffer[i] != ELOG_TYPE_EOL) {
268 ret = 0;
269 break;
270 }
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700271 }
Aaron Durbin281c9942016-08-04 14:25:59 -0500272 rdev_munmap(rdev, buffer);
273 return ret;
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700274}
275
276/*
Aaron Durbin281c9942016-08-04 14:25:59 -0500277 * Verify if the mirrored elog structure is valid.
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700278 * Returns 1 if the header is valid, 0 otherwise
279 */
Aaron Durbin281c9942016-08-04 14:25:59 -0500280static int elog_is_header_valid(void)
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700281{
Aaron Durbin281c9942016-08-04 14:25:59 -0500282 struct elog_header *header;
283
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700284 elog_debug("elog_is_header_valid()\n");
285
Aaron Durbin281c9942016-08-04 14:25:59 -0500286 header = rdev_mmap(mirror_dev_get(), 0, sizeof(*header));
287
288 if (header == NULL) {
289 printk(BIOS_ERR, "ELOG: could not map header.\n");
290 return 0;
291 }
292
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700293 if (header->magic != ELOG_SIGNATURE) {
294 printk(BIOS_ERR, "ELOG: header magic 0x%X != 0x%X\n",
295 header->magic, ELOG_SIGNATURE);
296 return 0;
297 }
298 if (header->version != ELOG_VERSION) {
299 printk(BIOS_ERR, "ELOG: header version %u != %u\n",
300 header->version, ELOG_VERSION);
301 return 0;
302 }
303 if (header->header_size != sizeof(*header)) {
Denis 'GNUtoo' Cariklibc2c9ef2013-06-26 20:04:49 +0200304 printk(BIOS_ERR, "ELOG: header size mismatch %u != %zu\n",
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700305 header->header_size, sizeof(*header));
306 return 0;
307 }
308 return 1;
309}
310
311/*
312 * Validate the event header and data.
313 */
Aaron Durbin281c9942016-08-04 14:25:59 -0500314static size_t elog_is_event_valid(size_t offset)
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700315{
Aaron Durbin281c9942016-08-04 14:25:59 -0500316 uint8_t checksum;
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700317 struct event_header *event;
Aaron Durbin281c9942016-08-04 14:25:59 -0500318 uint8_t len;
319 const size_t len_offset = offsetof(struct event_header, length);
320 const size_t size = sizeof(len);
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700321
Aaron Durbin281c9942016-08-04 14:25:59 -0500322 /* Read and validate length. */
323 if (rdev_readat(mirror_dev_get(), &len, offset + len_offset, size) < 0)
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700324 return 0;
325
326 /* Event length must be at least header size + checksum */
Aaron Durbin281c9942016-08-04 14:25:59 -0500327 if (len < (sizeof(*event) + sizeof(checksum)))
328 return 0;
329
330 if (len > MAX_EVENT_SIZE)
331 return 0;
332
333 event = elog_get_event_buffer(offset, len);
334 if (!event)
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700335 return 0;
336
337 /* If event checksum is invalid the area is corrupt */
Aaron Durbin281c9942016-08-04 14:25:59 -0500338 checksum = elog_checksum_event(event);
339 elog_put_event_buffer(event);
340
341 if (checksum != 0)
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700342 return 0;
343
344 /* Event is valid */
Aaron Durbin281c9942016-08-04 14:25:59 -0500345 return len;
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700346}
347
348/*
Aaron Durbin052a9952016-08-03 22:21:29 -0500349 * Write 'size' bytes of data from provided 'offset' in the mirrored elog to
350 * the flash backing store. This will not erase the flash and it assumes the
351 * flash area has been erased appropriately.
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700352 */
Aaron Durbind10f9d52016-08-10 11:37:14 -0500353static void elog_nv_write(size_t offset, size_t size)
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700354{
Aaron Durbin052a9952016-08-03 22:21:29 -0500355 void *address;
Aaron Durbin281c9942016-08-04 14:25:59 -0500356 const struct region_device *rdev = mirror_dev_get();
Karthikeyan Ramasubramanian07bc08c2018-11-06 12:59:18 -0700357 struct elog_state *es = car_get_var_ptr(&g_elog_state);
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700358
Aaron Durbind10f9d52016-08-10 11:37:14 -0500359 if (!size)
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700360 return;
361
Aaron Durbin281c9942016-08-04 14:25:59 -0500362 address = rdev_mmap(rdev, offset, size);
Aaron Durbin052a9952016-08-03 22:21:29 -0500363
John E. Kabat Jrd45011c2017-09-29 11:23:35 -0600364 elog_debug("%s(address=0x%p offset=0x%08zx size=%zu)\n", __func__,
365 address, offset, size);
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700366
Aaron Durbin281c9942016-08-04 14:25:59 -0500367 if (address == NULL)
368 return;
369
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700370 /* Write the data to flash */
Karthikeyan Ramasubramanian07bc08c2018-11-06 12:59:18 -0700371 if (rdev_writeat(&es->nv_dev, address, offset, size) != size)
Aaron Durbind10f9d52016-08-10 11:37:14 -0500372 printk(BIOS_ERR, "ELOG: NV Write failed at 0x%zx, size 0x%zx\n",
373 offset, size);
Aaron Durbin281c9942016-08-04 14:25:59 -0500374
375 rdev_munmap(rdev, address);
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700376}
377
378/*
379 * Erase the first block specified in the address.
380 * Only handles flash area within a single flash block.
381 */
Aaron Durbind10f9d52016-08-10 11:37:14 -0500382static void elog_nv_erase(void)
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700383{
Karthikeyan Ramasubramanian07bc08c2018-11-06 12:59:18 -0700384 struct elog_state *es = car_get_var_ptr(&g_elog_state);
385 size_t size = region_device_sz(&es->nv_dev);
Aaron Durbind10f9d52016-08-10 11:37:14 -0500386 elog_debug("%s()\n", __func__);
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700387
388 /* Erase the sectors in this region */
Karthikeyan Ramasubramanian07bc08c2018-11-06 12:59:18 -0700389 if (rdev_eraseat(&es->nv_dev, 0, size) != size)
Aaron Durbind10f9d52016-08-10 11:37:14 -0500390 printk(BIOS_ERR, "ELOG: erase failure.\n");
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700391}
392
393/*
Gabe Black0bf1feb2013-04-26 03:34:00 -0700394 * Scan the event area and validate each entry and update the ELOG state.
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700395 */
Aaron Durbin9d130a92016-08-05 15:24:33 -0500396static int elog_update_event_buffer_state(void)
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700397{
Aaron Durbinf41a9a62016-08-04 15:59:35 -0500398 size_t offset = elog_events_start();
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700399
400 elog_debug("elog_update_event_buffer_state()\n");
401
402 /* Go through each event and validate it */
403 while (1) {
Aaron Durbin281c9942016-08-04 14:25:59 -0500404 uint8_t type;
405 const size_t type_offset = offsetof(struct event_header, type);
406 size_t len;
407 const size_t size = sizeof(type);
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700408
Aaron Durbin281c9942016-08-04 14:25:59 -0500409 if (rdev_readat(mirror_dev_get(), &type,
Aaron Durbinf41a9a62016-08-04 15:59:35 -0500410 offset + type_offset, size) < 0) {
Aaron Durbin9d130a92016-08-05 15:24:33 -0500411 return -1;
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700412 }
413
414 /* The end of the event marker has been found */
Aaron Durbin281c9942016-08-04 14:25:59 -0500415 if (type == ELOG_TYPE_EOL)
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700416 break;
417
418 /* Validate the event */
Aaron Durbinf41a9a62016-08-04 15:59:35 -0500419 len = elog_is_event_valid(offset);
Aaron Durbin281c9942016-08-04 14:25:59 -0500420
Aaron Durbin12974432016-08-09 17:01:27 -0500421 if (!len) {
422 printk(BIOS_ERR, "ELOG: Invalid event @ offset 0x%zx\n",
423 offset);
Aaron Durbin9d130a92016-08-05 15:24:33 -0500424 return -1;
Aaron Durbin12974432016-08-09 17:01:27 -0500425 }
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700426
427 /* Move to the next event */
Aaron Durbin0284f622016-08-05 14:33:53 -0500428 elog_tandem_increment_last_write(len);
Aaron Durbin281c9942016-08-04 14:25:59 -0500429 offset += len;
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700430 }
431
432 /* Ensure the remaining buffer is empty */
Aaron Durbin12974432016-08-09 17:01:27 -0500433 if (!elog_is_buffer_clear(offset)) {
434 printk(BIOS_ERR, "ELOG: buffer not cleared from 0x%zx\n",
435 offset);
Aaron Durbin9d130a92016-08-05 15:24:33 -0500436 return -1;
Aaron Durbin12974432016-08-09 17:01:27 -0500437 }
Aaron Durbin9d130a92016-08-05 15:24:33 -0500438
439 return 0;
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700440}
441
Aaron Durbin9d130a92016-08-05 15:24:33 -0500442static int elog_scan_flash(void)
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700443{
Gabe Black84a93d12013-04-24 23:31:41 -0700444 elog_debug("elog_scan_flash()\n");
Aaron Durbin281c9942016-08-04 14:25:59 -0500445 void *mirror_buffer;
446 const struct region_device *rdev = mirror_dev_get();
Karthikeyan Ramasubramanian07bc08c2018-11-06 12:59:18 -0700447 struct elog_state *es = car_get_var_ptr(&g_elog_state);
448 size_t size = region_device_sz(&es->nv_dev);
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700449
Duncan Laurie215f27852012-10-10 14:34:49 -0700450 /* Fill memory buffer by reading from SPI */
Aaron Durbin281c9942016-08-04 14:25:59 -0500451 mirror_buffer = rdev_mmap_full(rdev);
Karthikeyan Ramasubramanian07bc08c2018-11-06 12:59:18 -0700452 if (rdev_readat(&es->nv_dev, mirror_buffer, 0, size) != size) {
Aaron Durbind10f9d52016-08-10 11:37:14 -0500453 rdev_munmap(rdev, mirror_buffer);
454 printk(BIOS_ERR, "ELOG: NV read failure.\n");
455 return -1;
456 }
Aaron Durbin281c9942016-08-04 14:25:59 -0500457 rdev_munmap(rdev, mirror_buffer);
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700458
Aaron Durbin0284f622016-08-05 14:33:53 -0500459 /* No writes have been done yet. */
460 elog_tandem_reset_last_write();
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700461
Gabe Black455c68e2013-04-24 17:54:37 -0700462 /* Check if the area is empty or not */
Aaron Durbin12974432016-08-09 17:01:27 -0500463 if (elog_is_buffer_clear(0)) {
464 printk(BIOS_ERR, "ELOG: NV Buffer Cleared.\n");
Aaron Durbin9d130a92016-08-05 15:24:33 -0500465 return -1;
Aaron Durbin12974432016-08-09 17:01:27 -0500466 }
Gabe Black455c68e2013-04-24 17:54:37 -0700467
Aaron Durbin0284f622016-08-05 14:33:53 -0500468 /* Indicate that header possibly written. */
469 elog_tandem_increment_last_write(elog_events_start());
470
Gabe Black455c68e2013-04-24 17:54:37 -0700471 /* Validate the header */
Aaron Durbin12974432016-08-09 17:01:27 -0500472 if (!elog_is_header_valid()) {
473 printk(BIOS_ERR, "ELOG: NV Buffer Invalid.\n");
Aaron Durbin9d130a92016-08-05 15:24:33 -0500474 return -1;
Aaron Durbin12974432016-08-09 17:01:27 -0500475 }
Gabe Black455c68e2013-04-24 17:54:37 -0700476
Aaron Durbin9d130a92016-08-05 15:24:33 -0500477 return elog_update_event_buffer_state();
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700478}
479
Aaron Durbin0284f622016-08-05 14:33:53 -0500480static void elog_write_header_in_mirror(void)
Aaron Durbin281c9942016-08-04 14:25:59 -0500481{
482 static const struct elog_header header = {
483 .magic = ELOG_SIGNATURE,
484 .version = ELOG_VERSION,
485 .header_size = sizeof(struct elog_header),
486 .reserved = {
487 [0] = ELOG_TYPE_EOL,
488 [1] = ELOG_TYPE_EOL,
489 },
490 };
491
492 rdev_writeat(mirror_dev_get(), &header, 0, sizeof(header));
Aaron Durbin0284f622016-08-05 14:33:53 -0500493 elog_mirror_increment_last_write(elog_events_start());
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700494}
495
Aaron Durbin281c9942016-08-04 14:25:59 -0500496static void elog_move_events_to_front(size_t offset, size_t size)
497{
498 void *src;
499 void *dest;
Aaron Durbinf41a9a62016-08-04 15:59:35 -0500500 size_t start_offset = elog_events_start();
Aaron Durbin281c9942016-08-04 14:25:59 -0500501 const struct region_device *rdev = mirror_dev_get();
502
Aaron Durbin281c9942016-08-04 14:25:59 -0500503 src = rdev_mmap(rdev, offset, size);
504 dest = rdev_mmap(rdev, start_offset, size);
505
506 if (src == NULL || dest == NULL) {
507 printk(BIOS_ERR, "ELOG: failure moving events!\n");
508 rdev_munmap(rdev, dest);
509 rdev_munmap(rdev, src);
510 return;
511 }
512
513 /* Move the events to the front. */
514 memmove(dest, src, size);
515 rdev_munmap(rdev, dest);
516 rdev_munmap(rdev, src);
517
518 /* Mark EOL for previously used buffer until the end. */
519 offset = start_offset + size;
520 size = region_device_sz(rdev) - offset;
521 dest = rdev_mmap(rdev, offset, size);
522 if (dest == NULL) {
523 printk(BIOS_ERR, "ELOG: failure filling EOL!\n");
524 return;
525 }
526 memset(dest, ELOG_TYPE_EOL, size);
527 rdev_munmap(rdev, dest);
528}
529
Aaron Durbin0284f622016-08-05 14:33:53 -0500530/* Perform the shrink and move events returning the size of bytes shrunk. */
531static size_t elog_do_shrink(size_t requested_size, size_t last_write)
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700532{
Aaron Durbin281c9942016-08-04 14:25:59 -0500533 const struct region_device *rdev = mirror_dev_get();
Aaron Durbinf41a9a62016-08-04 15:59:35 -0500534 size_t offset = elog_events_start();
Aaron Durbin0284f622016-08-05 14:33:53 -0500535 size_t remaining_size;
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700536
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700537 while (1) {
Aaron Durbin281c9942016-08-04 14:25:59 -0500538 const size_t type_offset = offsetof(struct event_header, type);
539 const size_t len_offset = offsetof(struct event_header, length);
540 const size_t size = sizeof(uint8_t);
541 uint8_t type;
542 uint8_t len;
Aaron Durbin281c9942016-08-04 14:25:59 -0500543
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700544 /* Next event has exceeded constraints */
Aaron Durbin0284f622016-08-05 14:33:53 -0500545 if (offset > requested_size)
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700546 break;
547
Aaron Durbinf41a9a62016-08-04 15:59:35 -0500548 if (rdev_readat(rdev, &type, offset + type_offset, size) < 0)
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700549 break;
550
Aaron Durbin281c9942016-08-04 14:25:59 -0500551 /* Reached the end of the area */
552 if (type == ELOG_TYPE_EOL)
553 break;
554
Aaron Durbinf41a9a62016-08-04 15:59:35 -0500555 if (rdev_readat(rdev, &len, offset + len_offset, size) < 0)
Aaron Durbin281c9942016-08-04 14:25:59 -0500556 break;
557
558 offset += len;
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700559 }
560
Aaron Durbin0284f622016-08-05 14:33:53 -0500561 /*
562 * Move the events and update the last write. The last write before
563 * shrinking was captured prior to resetting the counter to determine
564 * actual size we're keeping.
565 */
566 remaining_size = last_write - offset;
Aaron Durbin12974432016-08-09 17:01:27 -0500567 elog_debug("ELOG: shrinking offset: 0x%zx remaining_size: 0x%zx\n",
568 offset, remaining_size);
Aaron Durbin0284f622016-08-05 14:33:53 -0500569 elog_move_events_to_front(offset, remaining_size);
570 elog_mirror_increment_last_write(remaining_size);
Gabe Blackbfae4aa2013-04-24 03:16:21 -0700571
Aaron Durbin0284f622016-08-05 14:33:53 -0500572 /* Return the amount of data removed. */
573 return offset - elog_events_start();
574}
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700575
Aaron Durbin0284f622016-08-05 14:33:53 -0500576/*
577 * Shrink the log, deleting old entries and moving the
578 * remaining ones to the front of the log.
579 */
Aaron Durbineec1c282016-08-06 10:02:37 -0500580static int elog_shrink_by_size(size_t requested_size)
Aaron Durbin0284f622016-08-05 14:33:53 -0500581{
582 size_t shrunk_size;
583 size_t captured_last_write;
584 size_t total_event_space = elog_events_total_space();
585
586 elog_debug("%s()\n", __func__);
587
588 /* Indicate possible erase required. */
589 elog_nv_needs_possible_erase();
590
591 /* Capture the last write to determine data size in buffer to shrink. */
592 captured_last_write = elog_mirror_reset_last_write();
593
594 /* Prepare new header. */
595 elog_write_header_in_mirror();
596
597 /* Determine if any actual shrinking is required. */
598 if (requested_size >= total_event_space)
599 shrunk_size = total_event_space;
600 else
601 shrunk_size = elog_do_shrink(requested_size,
602 captured_last_write);
Duncan Laurie032be822013-05-23 07:23:09 -0700603
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700604 /* Add clear event */
Aaron Durbineec1c282016-08-06 10:02:37 -0500605 return elog_add_event_word(ELOG_TYPE_LOG_CLEAR, shrunk_size);
Aaron Durbin0284f622016-08-05 14:33:53 -0500606}
607
608static int elog_prepare_empty(void)
609{
610 elog_debug("elog_prepare_empty()\n");
Aaron Durbineec1c282016-08-06 10:02:37 -0500611 return elog_shrink_by_size(elog_events_total_space());
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700612}
613
Aaron Durbineec1c282016-08-06 10:02:37 -0500614static int elog_shrink(void)
Aaron Durbin0284f622016-08-05 14:33:53 -0500615{
Karthikeyan Ramasubramanian07bc08c2018-11-06 12:59:18 -0700616 struct elog_state *es = car_get_var_ptr(&g_elog_state);
617
Aaron Durbin0284f622016-08-05 14:33:53 -0500618 if (elog_should_shrink())
Karthikeyan Ramasubramanian07bc08c2018-11-06 12:59:18 -0700619 return elog_shrink_by_size(es->shrink_size);
Aaron Durbineec1c282016-08-06 10:02:37 -0500620 return 0;
Aaron Durbin0284f622016-08-05 14:33:53 -0500621}
622
Furquan Shaikh7f4221c2014-11-12 16:19:37 -0800623/*
624 * Convert a flash offset into a memory mapped flash address
625 */
Furquan Shaikhe19d9af2016-07-15 15:54:36 -0700626static inline u8 *elog_flash_offset_to_address(void)
Furquan Shaikh7f4221c2014-11-12 16:19:37 -0800627{
Karthikeyan Ramasubramanian07bc08c2018-11-06 12:59:18 -0700628 struct elog_state *es = car_get_var_ptr(&g_elog_state);
629
Aaron Durbin16c173f2016-08-11 14:04:10 -0500630 /* Only support memory-mapped devices. */
631 if (!IS_ENABLED(CONFIG_BOOT_DEVICE_MEMORY_MAPPED))
Furquan Shaikhe19d9af2016-07-15 15:54:36 -0700632 return NULL;
Furquan Shaikh7f4221c2014-11-12 16:19:37 -0800633
Karthikeyan Ramasubramanian07bc08c2018-11-06 12:59:18 -0700634 if (!region_device_sz(&es->nv_dev))
Furquan Shaikh7f4221c2014-11-12 16:19:37 -0800635 return NULL;
636
Aaron Durbind10f9d52016-08-10 11:37:14 -0500637 /* Get a view into the read-only boot device. */
Karthikeyan Ramasubramanian07bc08c2018-11-06 12:59:18 -0700638 return rdev_mmap(boot_device_ro(), region_device_offset(&es->nv_dev),
639 region_device_sz(&es->nv_dev));
Furquan Shaikh7f4221c2014-11-12 16:19:37 -0800640}
641
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700642/*
Duncan Laurie472ec9c2012-06-23 16:13:42 -0700643 * Fill out SMBIOS Type 15 table entry so the
644 * event log can be discovered at runtime.
645 */
646int elog_smbios_write_type15(unsigned long *current, int handle)
647{
648 struct smbios_type15 *t = (struct smbios_type15 *)*current;
649 int len = sizeof(struct smbios_type15);
Aaron Durbin18fedb32016-08-05 21:45:54 -0500650 uintptr_t log_address;
Karthikeyan Ramasubramanian07bc08c2018-11-06 12:59:18 -0700651 struct elog_state *es = car_get_var_ptr(&g_elog_state);
652 size_t elog_size = region_device_sz(&es->nv_dev);
Duncan Laurie472ec9c2012-06-23 16:13:42 -0700653
Aaron Durbin18fedb32016-08-05 21:45:54 -0500654 if (IS_ENABLED(CONFIG_ELOG_CBMEM)) {
655 /* Save event log buffer into CBMEM for the OS to read */
Aaron Durbind10f9d52016-08-10 11:37:14 -0500656 void *cbmem = cbmem_add(CBMEM_ID_ELOG, elog_size);
Aaron Durbin18fedb32016-08-05 21:45:54 -0500657 if (cbmem)
Aaron Durbind10f9d52016-08-10 11:37:14 -0500658 rdev_readat(mirror_dev_get(), cbmem, 0, elog_size);
Aaron Durbin18fedb32016-08-05 21:45:54 -0500659 log_address = (uintptr_t)cbmem;
660 } else {
661 log_address = (uintptr_t)elog_flash_offset_to_address();
662 }
663
664 if (!log_address) {
665 printk(BIOS_WARNING, "SMBIOS type 15 log address invalid.\n");
Duncan Laurie215f27852012-10-10 14:34:49 -0700666 return 0;
Aaron Durbin18fedb32016-08-05 21:45:54 -0500667 }
Duncan Laurie215f27852012-10-10 14:34:49 -0700668
Duncan Laurie472ec9c2012-06-23 16:13:42 -0700669 memset(t, 0, len);
670 t->type = SMBIOS_EVENT_LOG;
671 t->length = len - 2;
672 t->handle = handle;
Aaron Durbind10f9d52016-08-10 11:37:14 -0500673 t->area_length = elog_size - 1;
Duncan Laurie472ec9c2012-06-23 16:13:42 -0700674 t->header_offset = 0;
675 t->data_offset = sizeof(struct elog_header);
676 t->access_method = SMBIOS_EVENTLOG_ACCESS_METHOD_MMIO32;
677 t->log_status = SMBIOS_EVENTLOG_STATUS_VALID;
678 t->change_token = 0;
Aaron Durbin18fedb32016-08-05 21:45:54 -0500679 t->address = log_address;
Duncan Laurie472ec9c2012-06-23 16:13:42 -0700680 t->header_format = ELOG_HEADER_TYPE_OEM;
681 t->log_type_descriptors = 0;
682 t->log_type_descriptor_length = 2;
683
684 *current += len;
685 return len;
686}
687
688/*
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700689 * Clear the entire event log
690 */
691int elog_clear(void)
692{
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700693 elog_debug("elog_clear()\n");
694
Gabe Black8f4baec2013-04-25 17:21:58 -0700695 /* Make sure ELOG structures are initialized */
696 if (elog_init() < 0)
697 return -1;
698
Aaron Durbin0284f622016-08-05 14:33:53 -0500699 return elog_prepare_empty();
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700700}
701
Aaron Durbin367f2b92016-08-05 15:41:37 -0500702static int elog_find_flash(void)
Gabe Black84a93d12013-04-24 23:31:41 -0700703{
Aaron Durbind10f9d52016-08-10 11:37:14 -0500704 size_t total_size;
Aaron Durbinf41a9a62016-08-04 15:59:35 -0500705 size_t reserved_space = ELOG_MIN_AVAILABLE_ENTRIES * MAX_EVENT_SIZE;
Karthikeyan Ramasubramanian07bc08c2018-11-06 12:59:18 -0700706 struct elog_state *es = car_get_var_ptr(&g_elog_state);
707 struct region_device *rdev = &es->nv_dev;
Aaron Durbin5d208ff2016-07-22 14:30:55 -0500708
Aaron Durbind10f9d52016-08-10 11:37:14 -0500709 elog_debug("%s()\n", __func__);
Gabe Black84a93d12013-04-24 23:31:41 -0700710
Aaron Durbin5d208ff2016-07-22 14:30:55 -0500711 /* Find the ELOG base and size in FMAP */
Aaron Durbind10f9d52016-08-10 11:37:14 -0500712 if (fmap_locate_area_as_rdev_rw("RW_ELOG", rdev) < 0) {
Aaron Durbin5d208ff2016-07-22 14:30:55 -0500713 printk(BIOS_WARNING, "ELOG: Unable to find RW_ELOG in FMAP\n");
Aaron Durbin367f2b92016-08-05 15:41:37 -0500714 return -1;
Gabe Black84a93d12013-04-24 23:31:41 -0700715 }
Aaron Durbin5d208ff2016-07-22 14:30:55 -0500716
Karthikeyan Ramasubramanian5ac4b852018-11-08 11:57:51 -0700717 if (region_device_sz(rdev) < ELOG_SIZE) {
718 printk(BIOS_WARNING, "ELOG: Needs a minimum size of %dKiB: %zu\n",
719 ELOG_SIZE / KiB, region_device_sz(rdev));
Aaron Durbin367f2b92016-08-05 15:41:37 -0500720 return -1;
721 }
722
Aaron Durbind10f9d52016-08-10 11:37:14 -0500723 printk(BIOS_INFO, "ELOG: NV offset 0x%zx size 0x%zx\n",
724 region_device_offset(rdev), region_device_sz(rdev));
725
Aaron Durbin367f2b92016-08-05 15:41:37 -0500726 /* Keep 4KiB max size until large malloc()s have been fixed. */
Karthikeyan Ramasubramanian5ac4b852018-11-08 11:57:51 -0700727 total_size = MIN(ELOG_SIZE, region_device_sz(rdev));
Aaron Durbind10f9d52016-08-10 11:37:14 -0500728 rdev_chain(rdev, rdev, 0, total_size);
Aaron Durbin367f2b92016-08-05 15:41:37 -0500729
Karthikeyan Ramasubramanian07bc08c2018-11-06 12:59:18 -0700730 es->full_threshold = total_size - reserved_space;
731 es->shrink_size = total_size * ELOG_SHRINK_PERCENTAGE / 100;
Aaron Durbin367f2b92016-08-05 15:41:37 -0500732
Karthikeyan Ramasubramanian07bc08c2018-11-06 12:59:18 -0700733 if (reserved_space > es->shrink_size) {
Aaron Durbin367f2b92016-08-05 15:41:37 -0500734 printk(BIOS_ERR, "ELOG: SHRINK_PERCENTAGE too small\n");
735 return -1;
736 }
737
738 return 0;
Gabe Black84a93d12013-04-24 23:31:41 -0700739}
740
Aaron Durbin0284f622016-08-05 14:33:53 -0500741static int elog_sync_to_nv(void)
742{
743 size_t offset;
744 size_t size;
745 bool erase_needed;
Karthikeyan Ramasubramanian07bc08c2018-11-06 12:59:18 -0700746 struct elog_state *es = car_get_var_ptr(&g_elog_state);
Aaron Durbin0284f622016-08-05 14:33:53 -0500747
748 /* Determine if any updates are required. */
749 if (!elog_nv_needs_update())
750 return 0;
751
752 erase_needed = elog_nv_needs_erase();
753
754 /* Erase if necessary. */
755 if (erase_needed) {
Aaron Durbind10f9d52016-08-10 11:37:14 -0500756 elog_nv_erase();
Aaron Durbin0284f622016-08-05 14:33:53 -0500757 elog_nv_reset_last_write();
758 }
759
760 size = elog_nv_region_to_update(&offset);
761
Aaron Durbind10f9d52016-08-10 11:37:14 -0500762 elog_nv_write(offset, size);
Aaron Durbin0284f622016-08-05 14:33:53 -0500763 elog_nv_increment_last_write(size);
764
765 /*
766 * If erase wasn't performed then don't rescan. Assume the appended
767 * write was successful.
768 */
769 if (!erase_needed)
770 return 0;
771
Aaron Durbin12974432016-08-09 17:01:27 -0500772 elog_debug_dump_buffer("ELOG: in-memory mirror:\n");
773
Aaron Durbin0284f622016-08-05 14:33:53 -0500774 /* Mark broken if the scan failed after a sync. */
Aaron Durbin9d130a92016-08-05 15:24:33 -0500775 if (elog_scan_flash() < 0) {
Aaron Durbin0284f622016-08-05 14:33:53 -0500776 printk(BIOS_ERR, "ELOG: Sync back from NV storage failed.\n");
Aaron Durbin12974432016-08-09 17:01:27 -0500777 elog_debug_dump_buffer("ELOG: Buffer from NV:\n");
Karthikeyan Ramasubramanian07bc08c2018-11-06 12:59:18 -0700778 es->elog_initialized = ELOG_BROKEN;
Aaron Durbin0284f622016-08-05 14:33:53 -0500779 return -1;
780 }
781
782 return 0;
783}
784
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700785/*
Daniel Kurtz7c670692018-05-24 18:24:07 -0600786 * Do not log boot count events in S3 resume or SMM.
787 */
788static bool elog_do_add_boot_count(void)
789{
790 if (ENV_SMM)
791 return false;
792
793#if IS_ENABLED(CONFIG_HAVE_ACPI_RESUME)
794 return !acpi_is_wakeup_s3();
795#else
796 return true;
797#endif
798}
799
Karthikeyan Ramasubramaniancca1f372018-11-06 13:00:27 -0700800static void ramstage_elog_add_boot_count(void)
801{
802 if (elog_do_add_boot_count()) {
803 elog_add_event_dword(ELOG_TYPE_BOOT, boot_count_read());
804
805#if IS_ENABLED(CONFIG_ARCH_X86)
806 /* Check and log POST codes from previous boot */
807 if (IS_ENABLED(CONFIG_CMOS_POST))
808 cmos_post_log();
809#endif
810 }
811}
812
Daniel Kurtz7c670692018-05-24 18:24:07 -0600813/*
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700814 * Event log main entry point
815 */
816int elog_init(void)
817{
Aaron Durbin281c9942016-08-04 14:25:59 -0500818 void *mirror_buffer;
Aaron Durbind10f9d52016-08-10 11:37:14 -0500819 size_t elog_size;
Karthikeyan Ramasubramanian07bc08c2018-11-06 12:59:18 -0700820 struct elog_state *es = car_get_var_ptr(&g_elog_state);
Aaron Durbin281c9942016-08-04 14:25:59 -0500821
Karthikeyan Ramasubramanian07bc08c2018-11-06 12:59:18 -0700822 switch (es->elog_initialized) {
Julius Werner5d686222015-01-27 15:40:47 -0800823 case ELOG_UNINITIALIZED:
824 break;
825 case ELOG_INITIALIZED:
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700826 return 0;
Julius Werner5d686222015-01-27 15:40:47 -0800827 case ELOG_BROKEN:
828 return -1;
829 }
Karthikeyan Ramasubramanian07bc08c2018-11-06 12:59:18 -0700830 es->elog_initialized = ELOG_BROKEN;
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700831
832 elog_debug("elog_init()\n");
833
Gabe Black84a93d12013-04-24 23:31:41 -0700834 /* Set up the backing store */
Aaron Durbin367f2b92016-08-05 15:41:37 -0500835 if (elog_find_flash() < 0)
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700836 return -1;
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700837
Karthikeyan Ramasubramanian07bc08c2018-11-06 12:59:18 -0700838 elog_size = region_device_sz(&es->nv_dev);
Karthikeyan Ramasubramanian5ac4b852018-11-08 11:57:51 -0700839 mirror_buffer = get_elog_mirror_buffer();
Aaron Durbin281c9942016-08-04 14:25:59 -0500840 if (!mirror_buffer) {
Gabe Black84a93d12013-04-24 23:31:41 -0700841 printk(BIOS_ERR, "ELOG: Unable to allocate backing store\n");
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700842 return -1;
843 }
Karthikeyan Ramasubramanian07bc08c2018-11-06 12:59:18 -0700844 mem_region_device_rw_init(&es->mirror_dev, mirror_buffer, elog_size);
Gabe Black84a93d12013-04-24 23:31:41 -0700845
Aaron Durbin0284f622016-08-05 14:33:53 -0500846 /*
847 * Mark as initialized to allow elog_init() to be called and deemed
848 * successful in the prepare/shrink path which adds events.
849 */
Karthikeyan Ramasubramanian07bc08c2018-11-06 12:59:18 -0700850 es->elog_initialized = ELOG_INITIALIZED;
Aaron Durbin0284f622016-08-05 14:33:53 -0500851
Aaron Durbin9d130a92016-08-05 15:24:33 -0500852 /* Load the log from flash and prepare the flash if necessary. */
Aaron Durbin12974432016-08-09 17:01:27 -0500853 if (elog_scan_flash() < 0 && elog_prepare_empty() < 0) {
854 printk(BIOS_ERR, "ELOG: Unable to prepare flash\n");
855 return -1;
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700856 }
857
Aaron Durbind10f9d52016-08-10 11:37:14 -0500858 printk(BIOS_INFO, "ELOG: area is %zu bytes, full threshold %d,"
Karthikeyan Ramasubramanian07bc08c2018-11-06 12:59:18 -0700859 " shrink size %d\n", region_device_sz(&es->nv_dev),
860 es->full_threshold, es->shrink_size);
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700861
Karthikeyan Ramasubramaniancca1f372018-11-06 13:00:27 -0700862 if (ENV_RAMSTAGE)
863 ramstage_elog_add_boot_count();
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700864 return 0;
865}
866
867/*
868 * Populate timestamp in event header with current time
869 */
870static void elog_fill_timestamp(struct event_header *event)
871{
Patrick Georgi6e5f22e2015-04-22 13:28:21 +0200872#if IS_ENABLED(CONFIG_RTC)
Gabe Blacka4c4b1c2014-04-30 21:41:23 -0700873 struct rtc_time time;
874
875 rtc_get(&time);
876 event->second = bin2bcd(time.sec);
877 event->minute = bin2bcd(time.min);
878 event->hour = bin2bcd(time.hour);
879 event->day = bin2bcd(time.mday);
880 event->month = bin2bcd(time.mon);
Aaron Durbinc41c6a42015-11-06 15:20:23 -0600881 event->year = bin2bcd(time.year % 100);
Patrick Georgi6e5f22e2015-04-22 13:28:21 +0200882
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700883 /* Basic sanity check of expected ranges */
884 if (event->month > 0x12 || event->day > 0x31 || event->hour > 0x23 ||
Patrick Georgi6e5f22e2015-04-22 13:28:21 +0200885 event->minute > 0x59 || event->second > 0x59)
886#endif
887 {
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700888 event->year = 0;
889 event->month = 0;
890 event->day = 0;
891 event->hour = 0;
892 event->minute = 0;
893 event->second = 0;
894 }
895}
896
897/*
Gabe Black331eb082013-04-24 04:11:40 -0700898 * Add an event to the log
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700899 */
Aaron Durbineec1c282016-08-06 10:02:37 -0500900int elog_add_event_raw(u8 event_type, void *data, u8 data_size)
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700901{
902 struct event_header *event;
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700903 u8 event_size;
904
Gabe Black331eb082013-04-24 04:11:40 -0700905 elog_debug("elog_add_event_raw(type=%X)\n", event_type);
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700906
907 /* Make sure ELOG structures are initialized */
908 if (elog_init() < 0)
Aaron Durbineec1c282016-08-06 10:02:37 -0500909 return -1;
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700910
911 /* Header + Data + Checksum */
912 event_size = sizeof(*event) + data_size + 1;
913 if (event_size > MAX_EVENT_SIZE) {
914 printk(BIOS_ERR, "ELOG: Event(%X) data size too "
915 "big (%d)\n", event_type, event_size);
Aaron Durbineec1c282016-08-06 10:02:37 -0500916 return -1;
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700917 }
918
919 /* Make sure event data can fit */
Aaron Durbin0284f622016-08-05 14:33:53 -0500920 event = elog_get_next_event_buffer(event_size);
Aaron Durbin281c9942016-08-04 14:25:59 -0500921 if (event == NULL) {
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700922 printk(BIOS_ERR, "ELOG: Event(%X) does not fit\n",
923 event_type);
Aaron Durbineec1c282016-08-06 10:02:37 -0500924 return -1;
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700925 }
926
927 /* Fill out event data */
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700928 event->type = event_type;
929 event->length = event_size;
930 elog_fill_timestamp(event);
931
932 if (data_size)
933 memcpy(&event[1], data, data_size);
934
935 /* Zero the checksum byte and then compute checksum */
936 elog_update_checksum(event, 0);
937 elog_update_checksum(event, -(elog_checksum_event(event)));
Aaron Durbin281c9942016-08-04 14:25:59 -0500938 elog_put_event_buffer(event);
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700939
Aaron Durbin0284f622016-08-05 14:33:53 -0500940 elog_mirror_increment_last_write(event_size);
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700941
Julius Werner4d9fafa2017-04-12 13:24:23 -0700942 printk(BIOS_INFO, "ELOG: Event(%X) added with size %d ",
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700943 event_type, event_size);
Julius Werner4d9fafa2017-04-12 13:24:23 -0700944 if (event->day != 0) {
945 printk(BIOS_INFO, "at 20%02x-%02x-%02x %02x:%02x:%02x UTC\n",
946 event->year, event->month, event->day,
947 event->hour, event->minute, event->second);
948 } else {
949 printk(BIOS_INFO, "(timestamp unavailable)\n");
950 }
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700951
952 /* Shrink the log if we are getting too full */
Aaron Durbineec1c282016-08-06 10:02:37 -0500953 if (elog_shrink() < 0)
954 return -1;
Aaron Durbin0284f622016-08-05 14:33:53 -0500955
956 /* Ensure the updates hit the non-volatile storage. */
Aaron Durbineec1c282016-08-06 10:02:37 -0500957 return elog_sync_to_nv();
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700958}
959
Aaron Durbineec1c282016-08-06 10:02:37 -0500960int elog_add_event(u8 event_type)
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700961{
Aaron Durbineec1c282016-08-06 10:02:37 -0500962 return elog_add_event_raw(event_type, NULL, 0);
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700963}
964
Aaron Durbineec1c282016-08-06 10:02:37 -0500965int elog_add_event_byte(u8 event_type, u8 data)
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700966{
Aaron Durbineec1c282016-08-06 10:02:37 -0500967 return elog_add_event_raw(event_type, &data, sizeof(data));
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700968}
969
Aaron Durbineec1c282016-08-06 10:02:37 -0500970int elog_add_event_word(u8 event_type, u16 data)
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700971{
Aaron Durbineec1c282016-08-06 10:02:37 -0500972 return elog_add_event_raw(event_type, &data, sizeof(data));
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700973}
974
Aaron Durbineec1c282016-08-06 10:02:37 -0500975int elog_add_event_dword(u8 event_type, u32 data)
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700976{
Aaron Durbineec1c282016-08-06 10:02:37 -0500977 return elog_add_event_raw(event_type, &data, sizeof(data));
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700978}
979
Aaron Durbineec1c282016-08-06 10:02:37 -0500980int elog_add_event_wake(u8 source, u32 instance)
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700981{
982 struct elog_event_data_wake wake = {
983 .source = source,
984 .instance = instance
985 };
Aaron Durbineec1c282016-08-06 10:02:37 -0500986 return elog_add_event_raw(ELOG_TYPE_WAKE_SOURCE, &wake, sizeof(wake));
Duncan Laurie7d2b81c2012-06-23 16:08:47 -0700987}
Julius Wernera46ee4d2016-08-08 16:18:28 -0700988
Richard Spiegele6809902018-08-20 13:51:30 -0700989int elog_add_extended_event(u8 type, u32 complement)
990{
991 struct elog_event_extended_event event = {
992 .event_type = type,
993 .event_complement = complement
994 };
995 return elog_add_event_raw(ELOG_TYPE_EXTENDED_EVENT,
996 &event,
997 sizeof(event));
998}
999
Julius Wernera46ee4d2016-08-08 16:18:28 -07001000/* Make sure elog_init() runs at least once to log System Boot event. */
1001static void elog_bs_init(void *unused) { elog_init(); }
1002BOOT_STATE_INIT_ENTRY(BS_POST_DEVICE, BS_ON_ENTRY, elog_bs_init, NULL);