blob: 2e14fdd64759b9f8100982d840deee87746efab0 [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.
Werner Zeh35cceb82017-09-12 07:42:54 +02005 * Copyright 2017 Siemens AG.
Vadim Bendebury6f72d692011-09-21 16:12:39 -07006 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; version 2 of the License.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
Vadim Bendebury6f72d692011-09-21 16:12:39 -070015 */
16
Aaron Durbin1936f6c2015-07-03 17:04:21 -050017#include <assert.h>
Kyösti Mälkkib766b1c2013-09-07 17:26:08 +030018#include <stddef.h>
Vadim Bendebury6f72d692011-09-21 16:12:39 -070019#include <stdint.h>
Stefan Reinauer6a001132017-07-13 02:20:27 +020020#include <compiler.h>
Vadim Bendebury6f72d692011-09-21 16:12:39 -070021#include <console/console.h>
22#include <cbmem.h>
Aaron Durbin1936f6c2015-07-03 17:04:21 -050023#include <symbols.h>
Aaron Durbin2daadf82015-05-01 16:48:54 -050024#include <timer.h>
Vadim Bendebury6f72d692011-09-21 16:12:39 -070025#include <timestamp.h>
Stefan Reinauerfd4f4132013-06-19 12:25:44 -070026#include <arch/early_variables.h>
Aaron Durbin2daadf82015-05-01 16:48:54 -050027#include <rules.h>
Kyösti Mälkki5a5c8862014-01-26 14:41:54 +020028#include <smp/node.h>
Vadim Bendebury6f72d692011-09-21 16:12:39 -070029
Julius Werner69d20c42016-02-08 19:48:11 -080030#define MAX_TIMESTAMPS 84
Vadim Bendebury6f72d692011-09-21 16:12:39 -070031
Julius Werner85b1aad2016-08-19 15:17:42 -070032/* When changing this number, adjust TIMESTAMP() size ASSERT() in memlayout.h */
Julius Werner69d20c42016-02-08 19:48:11 -080033#define MAX_BSS_TIMESTAMP_CACHE 16
Kyösti Mälkkib766b1c2013-09-07 17:26:08 +030034
Stefan Reinauer6a001132017-07-13 02:20:27 +020035struct __packed timestamp_cache {
Aaron Durbinbd1499d2015-07-10 01:51:14 -050036 uint32_t cache_state;
Aaron Durbin1936f6c2015-07-03 17:04:21 -050037 struct timestamp_table table;
38 /* The struct timestamp_table has a 0 length array as its last field.
39 * The following 'entries' array serves as the storage space for the
Julius Werner69d20c42016-02-08 19:48:11 -080040 * cache when allocated in the BSS. */
41 struct timestamp_entry entries[MAX_BSS_TIMESTAMP_CACHE];
Aaron Durbin1936f6c2015-07-03 17:04:21 -050042};
Vadim Bendebury6f72d692011-09-21 16:12:39 -070043
Julius Werner8c093772016-02-09 16:09:15 -080044DECLARE_OPTIONAL_REGION(timestamp);
45
46#if defined(__PRE_RAM__)
47#define USE_TIMESTAMP_REGION (_timestamp_size > 0)
Aaron Durbin1936f6c2015-07-03 17:04:21 -050048#else
49#define USE_TIMESTAMP_REGION 0
50#endif
51
52/* The cache location will sit in BSS when in ramstage. */
53#define TIMESTAMP_CACHE_IN_BSS ENV_RAMSTAGE
54
Furquan Shaikhe85de022016-08-02 11:19:53 -070055#define HAS_CBMEM (ENV_ROMSTAGE || ENV_RAMSTAGE || ENV_POSTCAR)
Aaron Durbin1936f6c2015-07-03 17:04:21 -050056
57/* Storage of cache entries during ramstage prior to cbmem coming online. */
58static struct timestamp_cache timestamp_cache;
59
60enum {
61 TIMESTAMP_CACHE_UNINITIALIZED = 0,
62 TIMESTAMP_CACHE_INITIALIZED,
63 TIMESTAMP_CACHE_NOT_NEEDED,
64};
65
66static void timestamp_cache_init(struct timestamp_cache *ts_cache,
67 uint64_t base)
68{
69 ts_cache->table.num_entries = 0;
Julius Werner69d20c42016-02-08 19:48:11 -080070 ts_cache->table.max_entries = MAX_BSS_TIMESTAMP_CACHE;
Aaron Durbin1936f6c2015-07-03 17:04:21 -050071 ts_cache->table.base_time = base;
72 ts_cache->cache_state = TIMESTAMP_CACHE_INITIALIZED;
Julius Werner69d20c42016-02-08 19:48:11 -080073
74 if (USE_TIMESTAMP_REGION)
75 ts_cache->table.max_entries = (_timestamp_size -
76 offsetof(struct timestamp_cache, entries))
77 / sizeof(struct timestamp_entry);
Aaron Durbin1936f6c2015-07-03 17:04:21 -050078}
79
80static struct timestamp_cache *timestamp_cache_get(void)
81{
82 struct timestamp_cache *ts_cache = NULL;
83
84 if (TIMESTAMP_CACHE_IN_BSS) {
85 ts_cache = &timestamp_cache;
86 } else if (USE_TIMESTAMP_REGION) {
87 if (_timestamp_size < sizeof(*ts_cache))
88 BUG();
89 ts_cache = car_get_var_ptr((void *)_timestamp);
90 }
91
Aaron Durbin1936f6c2015-07-03 17:04:21 -050092 return ts_cache;
93}
94
95static struct timestamp_table *timestamp_alloc_cbmem_table(void)
Vadim Bendebury6f72d692011-09-21 16:12:39 -070096{
Lee Leahyb2d834a2017-03-08 16:52:22 -080097 struct timestamp_table *tst;
Vadim Bendebury6f72d692011-09-21 16:12:39 -070098
99 tst = cbmem_add(CBMEM_ID_TIMESTAMP,
100 sizeof(struct timestamp_table) +
101 MAX_TIMESTAMPS * sizeof(struct timestamp_entry));
102
Kyösti Mälkkie0344172015-05-26 06:23:02 +0300103 if (!tst)
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500104 return NULL;
Vadim Bendebury6f72d692011-09-21 16:12:39 -0700105
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500106 tst->base_time = 0;
Vadim Bendebury6f72d692011-09-21 16:12:39 -0700107 tst->max_entries = MAX_TIMESTAMPS;
108 tst->num_entries = 0;
Kyösti Mälkkib766b1c2013-09-07 17:26:08 +0300109
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500110 return tst;
Vadim Bendebury6f72d692011-09-21 16:12:39 -0700111}
112
Aaron Durbin2daadf82015-05-01 16:48:54 -0500113/* Determine if one should proceed into timestamp code. This is for protecting
114 * systems that have multiple processors running in romstage -- namely AMD
115 * based x86 platforms. */
116static int timestamp_should_run(void)
117{
118 /* Only check boot_cpu() in other stages than ramstage on x86. */
119 if ((!ENV_RAMSTAGE && IS_ENABLED(CONFIG_ARCH_X86)) && !boot_cpu())
120 return 0;
121
122 return 1;
123}
124
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500125static struct timestamp_table *timestamp_table_get(void)
Vadim Bendebury6f72d692011-09-21 16:12:39 -0700126{
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500127 MAYBE_STATIC struct timestamp_table *ts_table = NULL;
128 struct timestamp_cache *ts_cache;
Kyösti Mälkkib766b1c2013-09-07 17:26:08 +0300129
Aaron Durbin2daadf82015-05-01 16:48:54 -0500130 if (!timestamp_should_run())
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500131 return NULL;
Kyösti Mälkkif56ff902013-09-08 13:10:28 +0300132
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500133 if (ts_table != NULL)
134 return ts_table;
135
136 ts_cache = timestamp_cache_get();
137
138 if (ts_cache == NULL) {
139 if (HAS_CBMEM)
140 ts_table = cbmem_find(CBMEM_ID_TIMESTAMP);
141 return ts_table;
142 }
143
144 /* Cache is required. */
145 if (ts_cache->cache_state != TIMESTAMP_CACHE_NOT_NEEDED)
146 return &ts_cache->table;
147
148 /* Cache shouldn't be used but there's no backing store. */
149 if (!HAS_CBMEM)
150 return NULL;
151
152 ts_table = cbmem_find(CBMEM_ID_TIMESTAMP);
153
154 return ts_table;
155}
156
157static void timestamp_add_table_entry(struct timestamp_table *ts_table,
158 enum timestamp_id id, uint64_t ts_time)
159{
160 struct timestamp_entry *tse;
161
Ben Gardnerf5fd4c92015-11-20 13:25:25 -0600162 if (ts_table->num_entries >= ts_table->max_entries)
Kyösti Mälkkib766b1c2013-09-07 17:26:08 +0300163 return;
Vadim Bendebury6f72d692011-09-21 16:12:39 -0700164
165 tse = &ts_table->entries[ts_table->num_entries++];
166 tse->entry_id = id;
Stefan Reinauer3a6550d2013-08-01 13:31:44 -0700167 tse->entry_stamp = ts_time - ts_table->base_time;
Ben Gardnerf5fd4c92015-11-20 13:25:25 -0600168
169 if (ts_table->num_entries == ts_table->max_entries)
170 printk(BIOS_ERR, "ERROR: Timestamp table full\n");
Vadim Bendebury6f72d692011-09-21 16:12:39 -0700171}
172
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500173void timestamp_add(enum timestamp_id id, uint64_t ts_time)
174{
175 struct timestamp_table *ts_table;
176
177 ts_table = timestamp_table_get();
178
179 if (!ts_table) {
180 printk(BIOS_ERR, "ERROR: No timestamp table found\n");
181 return;
182 }
183
184 timestamp_add_table_entry(ts_table, id, ts_time);
185}
186
Vadim Bendebury6f72d692011-09-21 16:12:39 -0700187void timestamp_add_now(enum timestamp_id id)
188{
Stefan Reinauer3a6550d2013-08-01 13:31:44 -0700189 timestamp_add(id, timestamp_get());
Vadim Bendebury6f72d692011-09-21 16:12:39 -0700190}
Stefan Reinauer4221a192012-10-15 15:23:20 -0700191
Stefan Reinauer3a6550d2013-08-01 13:31:44 -0700192void timestamp_init(uint64_t base)
Kyösti Mälkkib766b1c2013-09-07 17:26:08 +0300193{
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500194 struct timestamp_cache *ts_cache;
195
Aaron Durbin2daadf82015-05-01 16:48:54 -0500196 if (!timestamp_should_run())
Kyösti Mälkkif56ff902013-09-08 13:10:28 +0300197 return;
198
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500199 ts_cache = timestamp_cache_get();
Kyösti Mälkkib766b1c2013-09-07 17:26:08 +0300200
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500201 if (!ts_cache) {
202 printk(BIOS_ERR, "ERROR: No timestamp cache to init\n");
Kyösti Mälkkib766b1c2013-09-07 17:26:08 +0300203 return;
204 }
Kyösti Mälkkib766b1c2013-09-07 17:26:08 +0300205
Aaron Durbinbd1499d2015-07-10 01:51:14 -0500206 /* In the EARLY_CBMEM_INIT case timestamps could have already been
207 * recovered. In those circumstances honor the cache which sits in BSS
208 * as it has already been initialized. */
Aaron Durbin2a983bd2015-07-11 13:36:01 -0500209 if (ENV_RAMSTAGE && IS_ENABLED(CONFIG_EARLY_CBMEM_INIT) &&
Aaron Durbinbd1499d2015-07-10 01:51:14 -0500210 ts_cache->cache_state != TIMESTAMP_CACHE_UNINITIALIZED)
211 return;
212
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500213 timestamp_cache_init(ts_cache, base);
Kyösti Mälkkib766b1c2013-09-07 17:26:08 +0300214}
215
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500216static void timestamp_sync_cache_to_cbmem(int is_recovery)
Kyösti Mälkkib766b1c2013-09-07 17:26:08 +0300217{
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500218 uint32_t i;
219 struct timestamp_cache *ts_cache;
220 struct timestamp_table *ts_cache_table;
221 struct timestamp_table *ts_cbmem_table = NULL;
222
Aaron Durbin2daadf82015-05-01 16:48:54 -0500223 if (!timestamp_should_run())
Kyösti Mälkkif56ff902013-09-08 13:10:28 +0300224 return;
225
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500226 ts_cache = timestamp_cache_get();
227
228 /* No timestamp cache found */
229 if (ts_cache == NULL) {
230 printk(BIOS_ERR, "ERROR: No timestamp cache found\n");
231 return;
232 }
233
234 ts_cache_table = &ts_cache->table;
235
236 /* cbmem is being recovered. */
237 if (is_recovery) {
238 /* x86 resume path expects timestamps to be reset. */
239 if (IS_ENABLED(CONFIG_ARCH_ROMSTAGE_X86_32) && ENV_ROMSTAGE)
240 ts_cbmem_table = timestamp_alloc_cbmem_table();
241 else {
242 /* Find existing table in cbmem. */
243 ts_cbmem_table = cbmem_find(CBMEM_ID_TIMESTAMP);
244 /* No existing timestamp table. */
245 if (ts_cbmem_table == NULL)
246 ts_cbmem_table = timestamp_alloc_cbmem_table();
247 }
248 } else
249 /* First time sync. Add new table. */
250 ts_cbmem_table = timestamp_alloc_cbmem_table();
251
252 if (ts_cbmem_table == NULL) {
253 printk(BIOS_ERR, "ERROR: No timestamp table allocated\n");
254 return;
255 }
256
257 /*
258 * There's no need to worry about the base_time fields being out of
Julius Werner8c093772016-02-09 16:09:15 -0800259 * sync because only the following configurations are used/supported:
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500260 *
Julius Werner8c093772016-02-09 16:09:15 -0800261 * 1. Timestamps get initialized before ramstage, which implies
262 * CONFIG_EARLY_CBMEM_INIT and CBMEM initialization in romstage.
263 * This requires the board to define a TIMESTAMP() region in its
264 * memlayout.ld (default on x86). The base_time from timestamp_init()
265 * (usually called from bootblock.c on most non-x86 boards) persists
266 * in that region until it gets synced to CBMEM in romstage.
267 * In ramstage, the BSS cache's base_time will be 0 until the second
268 * sync, which will adjust the timestamps in there to the correct
269 * base_time (from CBMEM) with the timestamp_add_table_entry() below.
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500270 *
Julius Werner8c093772016-02-09 16:09:15 -0800271 * 2. Timestamps only get initialized in ramstage *and*
272 * CONFIG_LATE_CBMEM_INIT is set. main() will call timestamp_init()
273 * very early (before any timestamps get logged) to set a base_time
274 * in the BSS cache, which will later get synced over to CBMEM.
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500275 *
Julius Werner8c093772016-02-09 16:09:15 -0800276 * If you try to initialize timestamps before ramstage but don't define
277 * a TIMESTAMP region, all operations will fail (safely), and coreboot
278 * will behave as if timestamps only get initialized in ramstage.
279 *
280 * If CONFIG_EARLY_CBMEM_INIT is set but timestamps only get
281 * initialized in ramstage, the base_time from timestamp_init() will
282 * get ignored and all timestamps will be 0-based.
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500283 */
284 for (i = 0; i < ts_cache_table->num_entries; i++) {
285 struct timestamp_entry *tse = &ts_cache_table->entries[i];
286 timestamp_add_table_entry(ts_cbmem_table, tse->entry_id,
287 tse->entry_stamp);
288 }
289
290 /* Freshly added cbmem table has base_time 0. Inherit cache base_time */
291 if (ts_cbmem_table->base_time == 0)
292 ts_cbmem_table->base_time = ts_cache_table->base_time;
293
Aaron Durbinc49014e2015-08-30 21:19:55 -0500294 /* Seed the timestamp tick frequency in ramstage. */
295 if (ENV_RAMSTAGE)
296 ts_cbmem_table->tick_freq_mhz = timestamp_tick_freq_mhz();
297
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500298 /* Cache no longer required. */
299 ts_cache_table->num_entries = 0;
300 ts_cache->cache_state = TIMESTAMP_CACHE_NOT_NEEDED;
Kyösti Mälkki3d45c402013-09-07 20:26:36 +0300301}
Kyösti Mälkkicbf5bdf2013-09-10 00:07:21 +0300302
Kyösti Mälkki806ea08b2017-07-15 20:42:20 +0300303void timestamp_rescale_table(uint16_t N, uint16_t M)
304{
305 uint32_t i;
306 struct timestamp_table *ts_table;
307
308 if (!timestamp_should_run())
309 return;
310
311 if (N == 0 || M == 0)
312 return;
313
314 ts_table = timestamp_table_get();
315
316 /* No timestamp table found */
317 if (ts_table == NULL) {
318 printk(BIOS_ERR, "ERROR: No timestamp table found\n");
319 return;
320 }
321
322 ts_table->base_time /= M;
323 ts_table->base_time *= N;
324 for (i = 0; i < ts_table->num_entries; i++) {
325 struct timestamp_entry *tse = &ts_table->entries[i];
326 tse->entry_stamp /= M;
327 tse->entry_stamp *= N;
328 }
329}
330
Werner Zeh35cceb82017-09-12 07:42:54 +0200331/*
332 * Get the time in microseconds since boot (or more precise: since timestamp
333 * table was initialized).
334 */
335uint32_t get_us_since_boot(void)
336{
337 struct timestamp_table *ts = timestamp_table_get();
338
339 if (ts == NULL || ts->tick_freq_mhz == 0)
340 return 0;
341 return (timestamp_get() - ts->base_time) / ts->tick_freq_mhz;
342}
343
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500344ROMSTAGE_CBMEM_INIT_HOOK(timestamp_sync_cache_to_cbmem)
345RAMSTAGE_CBMEM_INIT_HOOK(timestamp_sync_cache_to_cbmem)
Aaron Durbin9e80e272015-05-01 16:48:54 -0500346
347/* Provide default timestamp implementation using monotonic timer. */
348uint64_t __attribute__((weak)) timestamp_get(void)
349{
350 struct mono_time t1, t2;
351
Aaron Durbin7aafe532015-05-07 11:32:30 -0500352 if (!IS_ENABLED(CONFIG_HAVE_MONOTONIC_TIMER))
353 return 0;
354
Aaron Durbin9e80e272015-05-01 16:48:54 -0500355 mono_time_set_usecs(&t1, 0);
356 timer_monotonic_get(&t2);
357
358 return mono_time_diff_microseconds(&t1, &t2);
359}
Aaron Durbinc49014e2015-08-30 21:19:55 -0500360
361/* Like timestamp_get() above this matches up with microsecond granularity. */
362int __attribute__((weak)) timestamp_tick_freq_mhz(void)
363{
364 return 1;
365}