blob: adacf6b1b32c1941bbb081d6f20bee95502020c2 [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.
Mario Scheithauera39aede2017-11-06 16:47:27 +01005 * Copyright (C) 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>
20#include <console/console.h>
21#include <cbmem.h>
Aaron Durbin1936f6c2015-07-03 17:04:21 -050022#include <symbols.h>
Aaron Durbin2daadf82015-05-01 16:48:54 -050023#include <timer.h>
Vadim Bendebury6f72d692011-09-21 16:12:39 -070024#include <timestamp.h>
Stefan Reinauerfd4f4132013-06-19 12:25:44 -070025#include <arch/early_variables.h>
Kyösti Mälkki5a5c8862014-01-26 14:41:54 +020026#include <smp/node.h>
Vadim Bendebury6f72d692011-09-21 16:12:39 -070027
Raul E Rangelb3e02202018-05-11 11:08:08 -060028#define MAX_TIMESTAMPS 192
Vadim Bendebury6f72d692011-09-21 16:12:39 -070029
Julius Werner85b1aad2016-08-19 15:17:42 -070030/* When changing this number, adjust TIMESTAMP() size ASSERT() in memlayout.h */
Julius Werner69d20c42016-02-08 19:48:11 -080031#define MAX_BSS_TIMESTAMP_CACHE 16
Kyösti Mälkkib766b1c2013-09-07 17:26:08 +030032
Stefan Reinauer6a001132017-07-13 02:20:27 +020033struct __packed timestamp_cache {
Aaron Durbinbd1499d2015-07-10 01:51:14 -050034 uint32_t cache_state;
Aaron Durbin1936f6c2015-07-03 17:04:21 -050035 struct timestamp_table table;
36 /* The struct timestamp_table has a 0 length array as its last field.
37 * The following 'entries' array serves as the storage space for the
Julius Werner69d20c42016-02-08 19:48:11 -080038 * cache when allocated in the BSS. */
39 struct timestamp_entry entries[MAX_BSS_TIMESTAMP_CACHE];
Aaron Durbin1936f6c2015-07-03 17:04:21 -050040};
Vadim Bendebury6f72d692011-09-21 16:12:39 -070041
Julius Werner8c093772016-02-09 16:09:15 -080042DECLARE_OPTIONAL_REGION(timestamp);
43
44#if defined(__PRE_RAM__)
Julius Werner7e0dea62019-02-20 18:39:22 -080045#define USE_TIMESTAMP_REGION (REGION_SIZE(timestamp) > 0)
Aaron Durbin1936f6c2015-07-03 17:04:21 -050046#else
47#define USE_TIMESTAMP_REGION 0
48#endif
49
Furquan Shaikhef179ab2019-05-18 13:47:39 -070050/* The cache location will sit in BSS when in ramstage/postcar. */
51#define TIMESTAMP_CACHE_IN_BSS (ENV_RAMSTAGE || ENV_POSTCAR)
Aaron Durbin1936f6c2015-07-03 17:04:21 -050052
Furquan Shaikhe85de022016-08-02 11:19:53 -070053#define HAS_CBMEM (ENV_ROMSTAGE || ENV_RAMSTAGE || ENV_POSTCAR)
Aaron Durbin1936f6c2015-07-03 17:04:21 -050054
Furquan Shaikhef179ab2019-05-18 13:47:39 -070055/*
56 * Storage of cache entries during ramstage/postcar prior to cbmem coming
57 * online.
58 */
Aaron Durbin1936f6c2015-07-03 17:04:21 -050059static struct timestamp_cache timestamp_cache;
60
61enum {
62 TIMESTAMP_CACHE_UNINITIALIZED = 0,
63 TIMESTAMP_CACHE_INITIALIZED,
64 TIMESTAMP_CACHE_NOT_NEEDED,
65};
66
67static void timestamp_cache_init(struct timestamp_cache *ts_cache,
68 uint64_t base)
69{
70 ts_cache->table.num_entries = 0;
Julius Werner69d20c42016-02-08 19:48:11 -080071 ts_cache->table.max_entries = MAX_BSS_TIMESTAMP_CACHE;
Aaron Durbin1936f6c2015-07-03 17:04:21 -050072 ts_cache->table.base_time = base;
73 ts_cache->cache_state = TIMESTAMP_CACHE_INITIALIZED;
Julius Werner69d20c42016-02-08 19:48:11 -080074
75 if (USE_TIMESTAMP_REGION)
Julius Werner7e0dea62019-02-20 18:39:22 -080076 ts_cache->table.max_entries = (REGION_SIZE(timestamp) -
Julius Werner69d20c42016-02-08 19:48:11 -080077 offsetof(struct timestamp_cache, entries))
78 / sizeof(struct timestamp_entry);
Aaron Durbin1936f6c2015-07-03 17:04:21 -050079}
80
81static struct timestamp_cache *timestamp_cache_get(void)
82{
83 struct timestamp_cache *ts_cache = NULL;
84
85 if (TIMESTAMP_CACHE_IN_BSS) {
86 ts_cache = &timestamp_cache;
87 } else if (USE_TIMESTAMP_REGION) {
Julius Werner7e0dea62019-02-20 18:39:22 -080088 if (REGION_SIZE(timestamp) < sizeof(*ts_cache))
Aaron Durbin1936f6c2015-07-03 17:04:21 -050089 BUG();
90 ts_cache = car_get_var_ptr((void *)_timestamp);
91 }
92
Aaron Durbin1936f6c2015-07-03 17:04:21 -050093 return ts_cache;
94}
95
96static struct timestamp_table *timestamp_alloc_cbmem_table(void)
Vadim Bendebury6f72d692011-09-21 16:12:39 -070097{
Lee Leahyb2d834a2017-03-08 16:52:22 -080098 struct timestamp_table *tst;
Vadim Bendebury6f72d692011-09-21 16:12:39 -070099
100 tst = cbmem_add(CBMEM_ID_TIMESTAMP,
101 sizeof(struct timestamp_table) +
102 MAX_TIMESTAMPS * sizeof(struct timestamp_entry));
103
Kyösti Mälkkie0344172015-05-26 06:23:02 +0300104 if (!tst)
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500105 return NULL;
Vadim Bendebury6f72d692011-09-21 16:12:39 -0700106
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500107 tst->base_time = 0;
Vadim Bendebury6f72d692011-09-21 16:12:39 -0700108 tst->max_entries = MAX_TIMESTAMPS;
109 tst->num_entries = 0;
Kyösti Mälkkib766b1c2013-09-07 17:26:08 +0300110
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500111 return tst;
Vadim Bendebury6f72d692011-09-21 16:12:39 -0700112}
113
Aaron Durbin2daadf82015-05-01 16:48:54 -0500114/* Determine if one should proceed into timestamp code. This is for protecting
115 * systems that have multiple processors running in romstage -- namely AMD
116 * based x86 platforms. */
117static int timestamp_should_run(void)
118{
Subrata Banik42c44c22019-05-15 20:27:04 +0530119 /*
120 * Only check boot_cpu() in other stages than
121 * ENV_PAYLOAD_LOADER on x86.
122 */
123 if ((!ENV_PAYLOAD_LOADER && CONFIG(ARCH_X86)) && !boot_cpu())
Aaron Durbin2daadf82015-05-01 16:48:54 -0500124 return 0;
125
126 return 1;
127}
128
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500129static struct timestamp_table *timestamp_table_get(void)
Vadim Bendebury6f72d692011-09-21 16:12:39 -0700130{
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500131 MAYBE_STATIC struct timestamp_table *ts_table = NULL;
132 struct timestamp_cache *ts_cache;
Kyösti Mälkkib766b1c2013-09-07 17:26:08 +0300133
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500134 if (ts_table != NULL)
135 return ts_table;
136
137 ts_cache = timestamp_cache_get();
138
139 if (ts_cache == NULL) {
140 if (HAS_CBMEM)
141 ts_table = cbmem_find(CBMEM_ID_TIMESTAMP);
142 return ts_table;
143 }
144
145 /* Cache is required. */
146 if (ts_cache->cache_state != TIMESTAMP_CACHE_NOT_NEEDED)
147 return &ts_cache->table;
148
149 /* Cache shouldn't be used but there's no backing store. */
150 if (!HAS_CBMEM)
151 return NULL;
152
153 ts_table = cbmem_find(CBMEM_ID_TIMESTAMP);
154
155 return ts_table;
156}
157
Martin Rothb22bbe22018-03-07 15:32:16 -0700158static const char *timestamp_name(enum timestamp_id id)
159{
160 int i;
161
162 for (i = 0; i < ARRAY_SIZE(timestamp_ids); i++) {
163 if (timestamp_ids[i].id == id)
164 return timestamp_ids[i].name;
165 }
166
167 return "Unknown timestamp ID";
168}
169
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500170static void timestamp_add_table_entry(struct timestamp_table *ts_table,
171 enum timestamp_id id, uint64_t ts_time)
172{
173 struct timestamp_entry *tse;
174
Ben Gardnerf5fd4c92015-11-20 13:25:25 -0600175 if (ts_table->num_entries >= ts_table->max_entries)
Kyösti Mälkkib766b1c2013-09-07 17:26:08 +0300176 return;
Vadim Bendebury6f72d692011-09-21 16:12:39 -0700177
178 tse = &ts_table->entries[ts_table->num_entries++];
179 tse->entry_id = id;
Stefan Reinauer3a6550d2013-08-01 13:31:44 -0700180 tse->entry_stamp = ts_time - ts_table->base_time;
Ben Gardnerf5fd4c92015-11-20 13:25:25 -0600181
Julius Wernercd49cce2019-03-05 16:53:33 -0800182 if (CONFIG(TIMESTAMPS_ON_CONSOLE))
Martin Rothb22bbe22018-03-07 15:32:16 -0700183 printk(BIOS_SPEW, "Timestamp - %s: %" PRIu64 "\n",
184 timestamp_name(id), ts_time);
185
Ben Gardnerf5fd4c92015-11-20 13:25:25 -0600186 if (ts_table->num_entries == ts_table->max_entries)
187 printk(BIOS_ERR, "ERROR: Timestamp table full\n");
Vadim Bendebury6f72d692011-09-21 16:12:39 -0700188}
189
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500190void timestamp_add(enum timestamp_id id, uint64_t ts_time)
191{
192 struct timestamp_table *ts_table;
193
Kyösti Mälkki187e4c42019-02-20 17:38:45 +0200194 if (!timestamp_should_run())
195 return;
196
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500197 ts_table = timestamp_table_get();
198
199 if (!ts_table) {
200 printk(BIOS_ERR, "ERROR: No timestamp table found\n");
201 return;
202 }
203
204 timestamp_add_table_entry(ts_table, id, ts_time);
205}
206
Vadim Bendebury6f72d692011-09-21 16:12:39 -0700207void timestamp_add_now(enum timestamp_id id)
208{
Stefan Reinauer3a6550d2013-08-01 13:31:44 -0700209 timestamp_add(id, timestamp_get());
Vadim Bendebury6f72d692011-09-21 16:12:39 -0700210}
Stefan Reinauer4221a192012-10-15 15:23:20 -0700211
Stefan Reinauer3a6550d2013-08-01 13:31:44 -0700212void timestamp_init(uint64_t base)
Kyösti Mälkkib766b1c2013-09-07 17:26:08 +0300213{
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500214 struct timestamp_cache *ts_cache;
215
Aaron Durbin2daadf82015-05-01 16:48:54 -0500216 if (!timestamp_should_run())
Kyösti Mälkkif56ff902013-09-08 13:10:28 +0300217 return;
218
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500219 ts_cache = timestamp_cache_get();
Kyösti Mälkkib766b1c2013-09-07 17:26:08 +0300220
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500221 if (!ts_cache) {
222 printk(BIOS_ERR, "ERROR: No timestamp cache to init\n");
Kyösti Mälkkib766b1c2013-09-07 17:26:08 +0300223 return;
224 }
Kyösti Mälkkib766b1c2013-09-07 17:26:08 +0300225
Kyösti Mälkki513a1a82018-06-03 12:29:50 +0300226 /* Timestamps could have already been recovered.
227 * In those circumstances honor the cache which sits in BSS
Aaron Durbinbd1499d2015-07-10 01:51:14 -0500228 * as it has already been initialized. */
Furquan Shaikhef179ab2019-05-18 13:47:39 -0700229 if (TIMESTAMP_CACHE_IN_BSS &&
Aaron Durbinbd1499d2015-07-10 01:51:14 -0500230 ts_cache->cache_state != TIMESTAMP_CACHE_UNINITIALIZED)
231 return;
232
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500233 timestamp_cache_init(ts_cache, base);
Kyösti Mälkkib766b1c2013-09-07 17:26:08 +0300234}
235
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500236static void timestamp_sync_cache_to_cbmem(int is_recovery)
Kyösti Mälkkib766b1c2013-09-07 17:26:08 +0300237{
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500238 uint32_t i;
239 struct timestamp_cache *ts_cache;
240 struct timestamp_table *ts_cache_table;
241 struct timestamp_table *ts_cbmem_table = NULL;
242
Aaron Durbin2daadf82015-05-01 16:48:54 -0500243 if (!timestamp_should_run())
Kyösti Mälkkif56ff902013-09-08 13:10:28 +0300244 return;
245
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500246 ts_cache = timestamp_cache_get();
247
248 /* No timestamp cache found */
249 if (ts_cache == NULL) {
250 printk(BIOS_ERR, "ERROR: No timestamp cache found\n");
251 return;
252 }
253
254 ts_cache_table = &ts_cache->table;
255
256 /* cbmem is being recovered. */
257 if (is_recovery) {
258 /* x86 resume path expects timestamps to be reset. */
Julius Wernercd49cce2019-03-05 16:53:33 -0800259 if (CONFIG(ARCH_ROMSTAGE_X86_32) && ENV_ROMSTAGE)
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500260 ts_cbmem_table = timestamp_alloc_cbmem_table();
261 else {
262 /* Find existing table in cbmem. */
263 ts_cbmem_table = cbmem_find(CBMEM_ID_TIMESTAMP);
264 /* No existing timestamp table. */
265 if (ts_cbmem_table == NULL)
266 ts_cbmem_table = timestamp_alloc_cbmem_table();
267 }
268 } else
269 /* First time sync. Add new table. */
270 ts_cbmem_table = timestamp_alloc_cbmem_table();
271
272 if (ts_cbmem_table == NULL) {
273 printk(BIOS_ERR, "ERROR: No timestamp table allocated\n");
274 return;
275 }
276
277 /*
278 * There's no need to worry about the base_time fields being out of
Kyösti Mälkki513a1a82018-06-03 12:29:50 +0300279 * sync because only the following configuration is used/supported:
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500280 *
Kyösti Mälkki513a1a82018-06-03 12:29:50 +0300281 * Timestamps get initialized before ramstage, which implies
282 * CBMEM initialization in romstage.
Julius Werner8c093772016-02-09 16:09:15 -0800283 * This requires the board to define a TIMESTAMP() region in its
284 * memlayout.ld (default on x86). The base_time from timestamp_init()
285 * (usually called from bootblock.c on most non-x86 boards) persists
286 * in that region until it gets synced to CBMEM in romstage.
287 * In ramstage, the BSS cache's base_time will be 0 until the second
288 * sync, which will adjust the timestamps in there to the correct
289 * base_time (from CBMEM) with the timestamp_add_table_entry() below.
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500290 *
Julius Werner8c093772016-02-09 16:09:15 -0800291 * If you try to initialize timestamps before ramstage but don't define
292 * a TIMESTAMP region, all operations will fail (safely), and coreboot
293 * will behave as if timestamps only get initialized in ramstage.
294 *
Kyösti Mälkki513a1a82018-06-03 12:29:50 +0300295 * If timestamps only get initialized in ramstage, the base_time from
296 * timestamp_init() will get ignored and all timestamps will be 0-based.
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500297 */
298 for (i = 0; i < ts_cache_table->num_entries; i++) {
299 struct timestamp_entry *tse = &ts_cache_table->entries[i];
300 timestamp_add_table_entry(ts_cbmem_table, tse->entry_id,
301 tse->entry_stamp);
302 }
303
304 /* Freshly added cbmem table has base_time 0. Inherit cache base_time */
305 if (ts_cbmem_table->base_time == 0)
306 ts_cbmem_table->base_time = ts_cache_table->base_time;
307
Subrata Banik42c44c22019-05-15 20:27:04 +0530308 /* Seed the timestamp tick frequency in ENV_PAYLOAD_LOADER. */
309 if (ENV_PAYLOAD_LOADER)
Aaron Durbinc49014e2015-08-30 21:19:55 -0500310 ts_cbmem_table->tick_freq_mhz = timestamp_tick_freq_mhz();
311
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500312 /* Cache no longer required. */
313 ts_cache_table->num_entries = 0;
314 ts_cache->cache_state = TIMESTAMP_CACHE_NOT_NEEDED;
Kyösti Mälkki3d45c402013-09-07 20:26:36 +0300315}
Kyösti Mälkkicbf5bdf2013-09-10 00:07:21 +0300316
Kyösti Mälkki806ea08b2017-07-15 20:42:20 +0300317void timestamp_rescale_table(uint16_t N, uint16_t M)
318{
319 uint32_t i;
320 struct timestamp_table *ts_table;
321
322 if (!timestamp_should_run())
323 return;
324
325 if (N == 0 || M == 0)
326 return;
327
328 ts_table = timestamp_table_get();
329
330 /* No timestamp table found */
331 if (ts_table == NULL) {
332 printk(BIOS_ERR, "ERROR: No timestamp table found\n");
333 return;
334 }
335
336 ts_table->base_time /= M;
337 ts_table->base_time *= N;
338 for (i = 0; i < ts_table->num_entries; i++) {
339 struct timestamp_entry *tse = &ts_table->entries[i];
340 tse->entry_stamp /= M;
341 tse->entry_stamp *= N;
342 }
343}
344
Werner Zeh35cceb82017-09-12 07:42:54 +0200345/*
346 * Get the time in microseconds since boot (or more precise: since timestamp
347 * table was initialized).
348 */
349uint32_t get_us_since_boot(void)
350{
351 struct timestamp_table *ts = timestamp_table_get();
352
353 if (ts == NULL || ts->tick_freq_mhz == 0)
354 return 0;
355 return (timestamp_get() - ts->base_time) / ts->tick_freq_mhz;
356}
357
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500358ROMSTAGE_CBMEM_INIT_HOOK(timestamp_sync_cache_to_cbmem)
Subrata Banik2d7a52c2019-05-10 17:51:31 +0530359POSTCAR_CBMEM_INIT_HOOK(timestamp_sync_cache_to_cbmem)
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500360RAMSTAGE_CBMEM_INIT_HOOK(timestamp_sync_cache_to_cbmem)
Aaron Durbin9e80e272015-05-01 16:48:54 -0500361
362/* Provide default timestamp implementation using monotonic timer. */
Aaron Durbin64031672018-04-21 14:45:32 -0600363uint64_t __weak timestamp_get(void)
Aaron Durbin9e80e272015-05-01 16:48:54 -0500364{
365 struct mono_time t1, t2;
366
Julius Wernercd49cce2019-03-05 16:53:33 -0800367 if (!CONFIG(HAVE_MONOTONIC_TIMER))
Aaron Durbin7aafe532015-05-07 11:32:30 -0500368 return 0;
369
Aaron Durbin9e80e272015-05-01 16:48:54 -0500370 mono_time_set_usecs(&t1, 0);
371 timer_monotonic_get(&t2);
372
373 return mono_time_diff_microseconds(&t1, &t2);
374}
Aaron Durbinc49014e2015-08-30 21:19:55 -0500375
376/* Like timestamp_get() above this matches up with microsecond granularity. */
Aaron Durbin64031672018-04-21 14:45:32 -0600377int __weak timestamp_tick_freq_mhz(void)
Aaron Durbinc49014e2015-08-30 21:19:55 -0500378{
379 return 1;
380}