blob: 372076c42dbb8000542083dcb0574876b37977ec [file] [log] [blame]
Vadim Bendebury6f72d692011-09-21 16:12:39 -07001/*
2 * This file is part of the coreboot project.
3 *
4 * Copyright (C) 2011 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.
Vadim Bendebury6f72d692011-09-21 16:12:39 -070014 */
15
Aaron Durbin1936f6c2015-07-03 17:04:21 -050016#include <assert.h>
Kyösti Mälkkib766b1c2013-09-07 17:26:08 +030017#include <stddef.h>
Vadim Bendebury6f72d692011-09-21 16:12:39 -070018#include <stdint.h>
19#include <console/console.h>
20#include <cbmem.h>
Aaron Durbin1936f6c2015-07-03 17:04:21 -050021#include <symbols.h>
Aaron Durbin2daadf82015-05-01 16:48:54 -050022#include <timer.h>
Vadim Bendebury6f72d692011-09-21 16:12:39 -070023#include <timestamp.h>
Stefan Reinauerfd4f4132013-06-19 12:25:44 -070024#include <arch/early_variables.h>
Aaron Durbin2daadf82015-05-01 16:48:54 -050025#include <rules.h>
Kyösti Mälkki5a5c8862014-01-26 14:41:54 +020026#include <smp/node.h>
Vadim Bendebury6f72d692011-09-21 16:12:39 -070027
Julius Werner69d20c42016-02-08 19:48:11 -080028#define MAX_TIMESTAMPS 84
Vadim Bendebury6f72d692011-09-21 16:12:39 -070029
Julius Werner69d20c42016-02-08 19:48:11 -080030#define MAX_BSS_TIMESTAMP_CACHE 16
Kyösti Mälkkib766b1c2013-09-07 17:26:08 +030031
Aaron Durbin1936f6c2015-07-03 17:04:21 -050032struct __attribute__((__packed__)) timestamp_cache {
Aaron Durbinbd1499d2015-07-10 01:51:14 -050033 uint32_t cache_state;
Aaron Durbin1936f6c2015-07-03 17:04:21 -050034 struct timestamp_table table;
35 /* The struct timestamp_table has a 0 length array as its last field.
36 * The following 'entries' array serves as the storage space for the
Julius Werner69d20c42016-02-08 19:48:11 -080037 * cache when allocated in the BSS. */
38 struct timestamp_entry entries[MAX_BSS_TIMESTAMP_CACHE];
Aaron Durbin1936f6c2015-07-03 17:04:21 -050039};
Vadim Bendebury6f72d692011-09-21 16:12:39 -070040
Julius Werner8c093772016-02-09 16:09:15 -080041DECLARE_OPTIONAL_REGION(timestamp);
42
43#if defined(__PRE_RAM__)
44#define USE_TIMESTAMP_REGION (_timestamp_size > 0)
Aaron Durbin1936f6c2015-07-03 17:04:21 -050045#else
46#define USE_TIMESTAMP_REGION 0
47#endif
48
49/* The cache location will sit in BSS when in ramstage. */
50#define TIMESTAMP_CACHE_IN_BSS ENV_RAMSTAGE
51
52#define HAS_CBMEM (ENV_ROMSTAGE || ENV_RAMSTAGE)
53
54/* Storage of cache entries during ramstage prior to cbmem coming online. */
55static struct timestamp_cache timestamp_cache;
56
57enum {
58 TIMESTAMP_CACHE_UNINITIALIZED = 0,
59 TIMESTAMP_CACHE_INITIALIZED,
60 TIMESTAMP_CACHE_NOT_NEEDED,
61};
62
63static void timestamp_cache_init(struct timestamp_cache *ts_cache,
64 uint64_t base)
65{
66 ts_cache->table.num_entries = 0;
Julius Werner69d20c42016-02-08 19:48:11 -080067 ts_cache->table.max_entries = MAX_BSS_TIMESTAMP_CACHE;
Aaron Durbin1936f6c2015-07-03 17:04:21 -050068 ts_cache->table.base_time = base;
69 ts_cache->cache_state = TIMESTAMP_CACHE_INITIALIZED;
Julius Werner69d20c42016-02-08 19:48:11 -080070
71 if (USE_TIMESTAMP_REGION)
72 ts_cache->table.max_entries = (_timestamp_size -
73 offsetof(struct timestamp_cache, entries))
74 / sizeof(struct timestamp_entry);
Aaron Durbin1936f6c2015-07-03 17:04:21 -050075}
76
77static struct timestamp_cache *timestamp_cache_get(void)
78{
79 struct timestamp_cache *ts_cache = NULL;
80
81 if (TIMESTAMP_CACHE_IN_BSS) {
82 ts_cache = &timestamp_cache;
83 } else if (USE_TIMESTAMP_REGION) {
84 if (_timestamp_size < sizeof(*ts_cache))
85 BUG();
86 ts_cache = car_get_var_ptr((void *)_timestamp);
87 }
88
Aaron Durbin1936f6c2015-07-03 17:04:21 -050089 return ts_cache;
90}
91
92static struct timestamp_table *timestamp_alloc_cbmem_table(void)
Vadim Bendebury6f72d692011-09-21 16:12:39 -070093{
94 struct timestamp_table* tst;
95
96 tst = cbmem_add(CBMEM_ID_TIMESTAMP,
97 sizeof(struct timestamp_table) +
98 MAX_TIMESTAMPS * sizeof(struct timestamp_entry));
99
Kyösti Mälkkie0344172015-05-26 06:23:02 +0300100 if (!tst)
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500101 return NULL;
Vadim Bendebury6f72d692011-09-21 16:12:39 -0700102
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500103 tst->base_time = 0;
Vadim Bendebury6f72d692011-09-21 16:12:39 -0700104 tst->max_entries = MAX_TIMESTAMPS;
105 tst->num_entries = 0;
Kyösti Mälkkib766b1c2013-09-07 17:26:08 +0300106
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500107 return tst;
Vadim Bendebury6f72d692011-09-21 16:12:39 -0700108}
109
Aaron Durbin2daadf82015-05-01 16:48:54 -0500110/* Determine if one should proceed into timestamp code. This is for protecting
111 * systems that have multiple processors running in romstage -- namely AMD
112 * based x86 platforms. */
113static int timestamp_should_run(void)
114{
115 /* Only check boot_cpu() in other stages than ramstage on x86. */
116 if ((!ENV_RAMSTAGE && IS_ENABLED(CONFIG_ARCH_X86)) && !boot_cpu())
117 return 0;
118
119 return 1;
120}
121
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500122static struct timestamp_table *timestamp_table_get(void)
Vadim Bendebury6f72d692011-09-21 16:12:39 -0700123{
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500124 MAYBE_STATIC struct timestamp_table *ts_table = NULL;
125 struct timestamp_cache *ts_cache;
Kyösti Mälkkib766b1c2013-09-07 17:26:08 +0300126
Aaron Durbin2daadf82015-05-01 16:48:54 -0500127 if (!timestamp_should_run())
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500128 return NULL;
Kyösti Mälkkif56ff902013-09-08 13:10:28 +0300129
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500130 if (ts_table != NULL)
131 return ts_table;
132
133 ts_cache = timestamp_cache_get();
134
135 if (ts_cache == NULL) {
136 if (HAS_CBMEM)
137 ts_table = cbmem_find(CBMEM_ID_TIMESTAMP);
138 return ts_table;
139 }
140
141 /* Cache is required. */
142 if (ts_cache->cache_state != TIMESTAMP_CACHE_NOT_NEEDED)
143 return &ts_cache->table;
144
145 /* Cache shouldn't be used but there's no backing store. */
146 if (!HAS_CBMEM)
147 return NULL;
148
149 ts_table = cbmem_find(CBMEM_ID_TIMESTAMP);
150
151 return ts_table;
152}
153
154static void timestamp_add_table_entry(struct timestamp_table *ts_table,
155 enum timestamp_id id, uint64_t ts_time)
156{
157 struct timestamp_entry *tse;
158
Ben Gardnerf5fd4c92015-11-20 13:25:25 -0600159 if (ts_table->num_entries >= ts_table->max_entries)
Kyösti Mälkkib766b1c2013-09-07 17:26:08 +0300160 return;
Vadim Bendebury6f72d692011-09-21 16:12:39 -0700161
162 tse = &ts_table->entries[ts_table->num_entries++];
163 tse->entry_id = id;
Stefan Reinauer3a6550d2013-08-01 13:31:44 -0700164 tse->entry_stamp = ts_time - ts_table->base_time;
Ben Gardnerf5fd4c92015-11-20 13:25:25 -0600165
166 if (ts_table->num_entries == ts_table->max_entries)
167 printk(BIOS_ERR, "ERROR: Timestamp table full\n");
Vadim Bendebury6f72d692011-09-21 16:12:39 -0700168}
169
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500170void timestamp_add(enum timestamp_id id, uint64_t ts_time)
171{
172 struct timestamp_table *ts_table;
173
174 ts_table = timestamp_table_get();
175
176 if (!ts_table) {
177 printk(BIOS_ERR, "ERROR: No timestamp table found\n");
178 return;
179 }
180
181 timestamp_add_table_entry(ts_table, id, ts_time);
182}
183
Vadim Bendebury6f72d692011-09-21 16:12:39 -0700184void timestamp_add_now(enum timestamp_id id)
185{
Stefan Reinauer3a6550d2013-08-01 13:31:44 -0700186 timestamp_add(id, timestamp_get());
Vadim Bendebury6f72d692011-09-21 16:12:39 -0700187}
Stefan Reinauer4221a192012-10-15 15:23:20 -0700188
Stefan Reinauer3a6550d2013-08-01 13:31:44 -0700189void timestamp_init(uint64_t base)
Kyösti Mälkkib766b1c2013-09-07 17:26:08 +0300190{
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500191 struct timestamp_cache *ts_cache;
192
Aaron Durbin2daadf82015-05-01 16:48:54 -0500193 if (!timestamp_should_run())
Kyösti Mälkkif56ff902013-09-08 13:10:28 +0300194 return;
195
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500196 ts_cache = timestamp_cache_get();
Kyösti Mälkkib766b1c2013-09-07 17:26:08 +0300197
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500198 if (!ts_cache) {
199 printk(BIOS_ERR, "ERROR: No timestamp cache to init\n");
Kyösti Mälkkib766b1c2013-09-07 17:26:08 +0300200 return;
201 }
Kyösti Mälkkib766b1c2013-09-07 17:26:08 +0300202
Aaron Durbinbd1499d2015-07-10 01:51:14 -0500203 /* In the EARLY_CBMEM_INIT case timestamps could have already been
204 * recovered. In those circumstances honor the cache which sits in BSS
205 * as it has already been initialized. */
Aaron Durbin2a983bd2015-07-11 13:36:01 -0500206 if (ENV_RAMSTAGE && IS_ENABLED(CONFIG_EARLY_CBMEM_INIT) &&
Aaron Durbinbd1499d2015-07-10 01:51:14 -0500207 ts_cache->cache_state != TIMESTAMP_CACHE_UNINITIALIZED)
208 return;
209
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500210 timestamp_cache_init(ts_cache, base);
Kyösti Mälkkib766b1c2013-09-07 17:26:08 +0300211}
212
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500213static void timestamp_sync_cache_to_cbmem(int is_recovery)
Kyösti Mälkkib766b1c2013-09-07 17:26:08 +0300214{
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500215 uint32_t i;
216 struct timestamp_cache *ts_cache;
217 struct timestamp_table *ts_cache_table;
218 struct timestamp_table *ts_cbmem_table = NULL;
219
Aaron Durbin2daadf82015-05-01 16:48:54 -0500220 if (!timestamp_should_run())
Kyösti Mälkkif56ff902013-09-08 13:10:28 +0300221 return;
222
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500223 ts_cache = timestamp_cache_get();
224
225 /* No timestamp cache found */
226 if (ts_cache == NULL) {
227 printk(BIOS_ERR, "ERROR: No timestamp cache found\n");
228 return;
229 }
230
231 ts_cache_table = &ts_cache->table;
232
233 /* cbmem is being recovered. */
234 if (is_recovery) {
235 /* x86 resume path expects timestamps to be reset. */
236 if (IS_ENABLED(CONFIG_ARCH_ROMSTAGE_X86_32) && ENV_ROMSTAGE)
237 ts_cbmem_table = timestamp_alloc_cbmem_table();
238 else {
239 /* Find existing table in cbmem. */
240 ts_cbmem_table = cbmem_find(CBMEM_ID_TIMESTAMP);
241 /* No existing timestamp table. */
242 if (ts_cbmem_table == NULL)
243 ts_cbmem_table = timestamp_alloc_cbmem_table();
244 }
245 } else
246 /* First time sync. Add new table. */
247 ts_cbmem_table = timestamp_alloc_cbmem_table();
248
249 if (ts_cbmem_table == NULL) {
250 printk(BIOS_ERR, "ERROR: No timestamp table allocated\n");
251 return;
252 }
253
254 /*
255 * There's no need to worry about the base_time fields being out of
Julius Werner8c093772016-02-09 16:09:15 -0800256 * sync because only the following configurations are used/supported:
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500257 *
Julius Werner8c093772016-02-09 16:09:15 -0800258 * 1. Timestamps get initialized before ramstage, which implies
259 * CONFIG_EARLY_CBMEM_INIT and CBMEM initialization in romstage.
260 * This requires the board to define a TIMESTAMP() region in its
261 * memlayout.ld (default on x86). The base_time from timestamp_init()
262 * (usually called from bootblock.c on most non-x86 boards) persists
263 * in that region until it gets synced to CBMEM in romstage.
264 * In ramstage, the BSS cache's base_time will be 0 until the second
265 * sync, which will adjust the timestamps in there to the correct
266 * base_time (from CBMEM) with the timestamp_add_table_entry() below.
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500267 *
Julius Werner8c093772016-02-09 16:09:15 -0800268 * 2. Timestamps only get initialized in ramstage *and*
269 * CONFIG_LATE_CBMEM_INIT is set. main() will call timestamp_init()
270 * very early (before any timestamps get logged) to set a base_time
271 * in the BSS cache, which will later get synced over to CBMEM.
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500272 *
Julius Werner8c093772016-02-09 16:09:15 -0800273 * If you try to initialize timestamps before ramstage but don't define
274 * a TIMESTAMP region, all operations will fail (safely), and coreboot
275 * will behave as if timestamps only get initialized in ramstage.
276 *
277 * If CONFIG_EARLY_CBMEM_INIT is set but timestamps only get
278 * initialized in ramstage, the base_time from timestamp_init() will
279 * get ignored and all timestamps will be 0-based.
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500280 */
281 for (i = 0; i < ts_cache_table->num_entries; i++) {
282 struct timestamp_entry *tse = &ts_cache_table->entries[i];
283 timestamp_add_table_entry(ts_cbmem_table, tse->entry_id,
284 tse->entry_stamp);
285 }
286
287 /* Freshly added cbmem table has base_time 0. Inherit cache base_time */
288 if (ts_cbmem_table->base_time == 0)
289 ts_cbmem_table->base_time = ts_cache_table->base_time;
290
Aaron Durbinc49014e2015-08-30 21:19:55 -0500291 /* Seed the timestamp tick frequency in ramstage. */
292 if (ENV_RAMSTAGE)
293 ts_cbmem_table->tick_freq_mhz = timestamp_tick_freq_mhz();
294
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500295 /* Cache no longer required. */
296 ts_cache_table->num_entries = 0;
297 ts_cache->cache_state = TIMESTAMP_CACHE_NOT_NEEDED;
Kyösti Mälkki3d45c402013-09-07 20:26:36 +0300298}
Kyösti Mälkkicbf5bdf2013-09-10 00:07:21 +0300299
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500300ROMSTAGE_CBMEM_INIT_HOOK(timestamp_sync_cache_to_cbmem)
301RAMSTAGE_CBMEM_INIT_HOOK(timestamp_sync_cache_to_cbmem)
Aaron Durbin9e80e272015-05-01 16:48:54 -0500302
303/* Provide default timestamp implementation using monotonic timer. */
304uint64_t __attribute__((weak)) timestamp_get(void)
305{
306 struct mono_time t1, t2;
307
Aaron Durbin7aafe532015-05-07 11:32:30 -0500308 if (!IS_ENABLED(CONFIG_HAVE_MONOTONIC_TIMER))
309 return 0;
310
Aaron Durbin9e80e272015-05-01 16:48:54 -0500311 mono_time_set_usecs(&t1, 0);
312 timer_monotonic_get(&t2);
313
314 return mono_time_diff_microseconds(&t1, &t2);
315}
Aaron Durbinc49014e2015-08-30 21:19:55 -0500316
317/* Like timestamp_get() above this matches up with microsecond granularity. */
318int __attribute__((weak)) timestamp_tick_freq_mhz(void)
319{
320 return 1;
321}