blob: 7ead3831bb3c40fe35e0db67b8d24f6715ef0b32 [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.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
Patrick Georgib890a122015-03-26 15:17:45 +010017 * Foundation, Inc.
Vadim Bendebury6f72d692011-09-21 16:12:39 -070018 */
19
Aaron Durbin1936f6c2015-07-03 17:04:21 -050020#include <assert.h>
Kyösti Mälkkib766b1c2013-09-07 17:26:08 +030021#include <stddef.h>
Vadim Bendebury6f72d692011-09-21 16:12:39 -070022#include <stdint.h>
23#include <console/console.h>
24#include <cbmem.h>
Aaron Durbin1936f6c2015-07-03 17:04:21 -050025#include <symbols.h>
Aaron Durbin2daadf82015-05-01 16:48:54 -050026#include <timer.h>
Vadim Bendebury6f72d692011-09-21 16:12:39 -070027#include <timestamp.h>
Stefan Reinauerfd4f4132013-06-19 12:25:44 -070028#include <arch/early_variables.h>
Aaron Durbin2daadf82015-05-01 16:48:54 -050029#include <rules.h>
Kyösti Mälkki5a5c8862014-01-26 14:41:54 +020030#include <smp/node.h>
Vadim Bendebury6f72d692011-09-21 16:12:39 -070031
Julius Wernera7d92442014-12-02 20:51:19 -080032#define MAX_TIMESTAMPS 60
Vadim Bendebury6f72d692011-09-21 16:12:39 -070033
Aaron Durbin1936f6c2015-07-03 17:04:21 -050034#define MAX_TIMESTAMP_CACHE 16
Kyösti Mälkkib766b1c2013-09-07 17:26:08 +030035
Aaron Durbin1936f6c2015-07-03 17:04:21 -050036struct __attribute__((__packed__)) timestamp_cache {
Aaron Durbinbd1499d2015-07-10 01:51:14 -050037 uint32_t cache_state;
Aaron Durbin1936f6c2015-07-03 17:04:21 -050038 struct timestamp_table table;
39 /* The struct timestamp_table has a 0 length array as its last field.
40 * The following 'entries' array serves as the storage space for the
41 * cache. */
42 struct timestamp_entry entries[MAX_TIMESTAMP_CACHE];
43};
Vadim Bendebury6f72d692011-09-21 16:12:39 -070044
Aaron Durbin1936f6c2015-07-03 17:04:21 -050045#if (IS_ENABLED(CONFIG_HAS_PRECBMEM_TIMESTAMP_REGION) && defined(__PRE_RAM__))
46#define USE_TIMESTAMP_REGION 1
47#else
48#define USE_TIMESTAMP_REGION 0
49#endif
50
51/* The cache location will sit in BSS when in ramstage. */
52#define TIMESTAMP_CACHE_IN_BSS ENV_RAMSTAGE
53
54#define HAS_CBMEM (ENV_ROMSTAGE || ENV_RAMSTAGE)
55
56/* Storage of cache entries during ramstage prior to cbmem coming online. */
57static struct timestamp_cache timestamp_cache;
58
59enum {
60 TIMESTAMP_CACHE_UNINITIALIZED = 0,
61 TIMESTAMP_CACHE_INITIALIZED,
62 TIMESTAMP_CACHE_NOT_NEEDED,
63};
64
65static void timestamp_cache_init(struct timestamp_cache *ts_cache,
66 uint64_t base)
67{
68 ts_cache->table.num_entries = 0;
69 ts_cache->table.max_entries = MAX_TIMESTAMP_CACHE;
70 ts_cache->table.base_time = base;
71 ts_cache->cache_state = TIMESTAMP_CACHE_INITIALIZED;
72}
73
74static struct timestamp_cache *timestamp_cache_get(void)
75{
76 struct timestamp_cache *ts_cache = NULL;
77
78 if (TIMESTAMP_CACHE_IN_BSS) {
79 ts_cache = &timestamp_cache;
80 } else if (USE_TIMESTAMP_REGION) {
81 if (_timestamp_size < sizeof(*ts_cache))
82 BUG();
83 ts_cache = car_get_var_ptr((void *)_timestamp);
84 }
85
86 if (ts_cache && ts_cache->cache_state == TIMESTAMP_CACHE_UNINITIALIZED)
87 timestamp_cache_init(ts_cache, 0);
88
89 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
159 if (ts_table->num_entries == ts_table->max_entries) {
160 printk(BIOS_ERR, "ERROR: Timestamp table full\n");
Kyösti Mälkkib766b1c2013-09-07 17:26:08 +0300161 return;
162 }
Vadim Bendebury6f72d692011-09-21 16:12:39 -0700163
164 tse = &ts_table->entries[ts_table->num_entries++];
165 tse->entry_id = id;
Stefan Reinauer3a6550d2013-08-01 13:31:44 -0700166 tse->entry_stamp = ts_time - ts_table->base_time;
Vadim Bendebury6f72d692011-09-21 16:12:39 -0700167}
168
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500169void timestamp_add(enum timestamp_id id, uint64_t ts_time)
170{
171 struct timestamp_table *ts_table;
172
173 ts_table = timestamp_table_get();
174
175 if (!ts_table) {
176 printk(BIOS_ERR, "ERROR: No timestamp table found\n");
177 return;
178 }
179
180 timestamp_add_table_entry(ts_table, id, ts_time);
181}
182
Vadim Bendebury6f72d692011-09-21 16:12:39 -0700183void timestamp_add_now(enum timestamp_id id)
184{
Stefan Reinauer3a6550d2013-08-01 13:31:44 -0700185 timestamp_add(id, timestamp_get());
Vadim Bendebury6f72d692011-09-21 16:12:39 -0700186}
Stefan Reinauer4221a192012-10-15 15:23:20 -0700187
Stefan Reinauer3a6550d2013-08-01 13:31:44 -0700188void timestamp_init(uint64_t base)
Kyösti Mälkkib766b1c2013-09-07 17:26:08 +0300189{
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500190 struct timestamp_cache *ts_cache;
191
Aaron Durbin2daadf82015-05-01 16:48:54 -0500192 if (!timestamp_should_run())
Kyösti Mälkkif56ff902013-09-08 13:10:28 +0300193 return;
194
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500195 ts_cache = timestamp_cache_get();
Kyösti Mälkkib766b1c2013-09-07 17:26:08 +0300196
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500197 if (!ts_cache) {
198 printk(BIOS_ERR, "ERROR: No timestamp cache to init\n");
Kyösti Mälkkib766b1c2013-09-07 17:26:08 +0300199 return;
200 }
Kyösti Mälkkib766b1c2013-09-07 17:26:08 +0300201
Aaron Durbinbd1499d2015-07-10 01:51:14 -0500202 /* In the EARLY_CBMEM_INIT case timestamps could have already been
203 * recovered. In those circumstances honor the cache which sits in BSS
204 * as it has already been initialized. */
205 if (ENV_RAMSTAGE &&
206 ts_cache->cache_state != TIMESTAMP_CACHE_UNINITIALIZED)
207 return;
208
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500209 timestamp_cache_init(ts_cache, base);
Kyösti Mälkkib766b1c2013-09-07 17:26:08 +0300210}
211
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500212static void timestamp_sync_cache_to_cbmem(int is_recovery)
Kyösti Mälkkib766b1c2013-09-07 17:26:08 +0300213{
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500214 uint32_t i;
215 struct timestamp_cache *ts_cache;
216 struct timestamp_table *ts_cache_table;
217 struct timestamp_table *ts_cbmem_table = NULL;
218
Aaron Durbin2daadf82015-05-01 16:48:54 -0500219 if (!timestamp_should_run())
Kyösti Mälkkif56ff902013-09-08 13:10:28 +0300220 return;
221
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500222 ts_cache = timestamp_cache_get();
223
224 /* No timestamp cache found */
225 if (ts_cache == NULL) {
226 printk(BIOS_ERR, "ERROR: No timestamp cache found\n");
227 return;
228 }
229
230 ts_cache_table = &ts_cache->table;
231
232 /* cbmem is being recovered. */
233 if (is_recovery) {
234 /* x86 resume path expects timestamps to be reset. */
235 if (IS_ENABLED(CONFIG_ARCH_ROMSTAGE_X86_32) && ENV_ROMSTAGE)
236 ts_cbmem_table = timestamp_alloc_cbmem_table();
237 else {
238 /* Find existing table in cbmem. */
239 ts_cbmem_table = cbmem_find(CBMEM_ID_TIMESTAMP);
240 /* No existing timestamp table. */
241 if (ts_cbmem_table == NULL)
242 ts_cbmem_table = timestamp_alloc_cbmem_table();
243 }
244 } else
245 /* First time sync. Add new table. */
246 ts_cbmem_table = timestamp_alloc_cbmem_table();
247
248 if (ts_cbmem_table == NULL) {
249 printk(BIOS_ERR, "ERROR: No timestamp table allocated\n");
250 return;
251 }
252
253 /*
254 * There's no need to worry about the base_time fields being out of
255 * sync because the following configurations are used/supported:
256 *
257 * 1. CONFIG_HAS_PRECBMEM_TIMESTAMP_REGION is enabled. This
258 * implies CONFIG_EARLY_CBMEM_INIT so once cbmem comes
259 * online we sync the timestamps to the cbmem storage while
260 * running in romstage. In ramstage the cbmem area is
261 * recovered and utilized.
262 *
263 * 2. CONFIG_LATE_CBMEM_INIT (!CONFIG_EARLY_CBMEM_INIT) is
264 * being used. That means the only cache that exists is
265 * in ramstage. Once cbmem comes online in ramstage those
266 * values are sync'd over.
267 *
268 * Any other combinations will result in inconsistent base_time
269 * values including bizarre timestamp values.
270 */
271 for (i = 0; i < ts_cache_table->num_entries; i++) {
272 struct timestamp_entry *tse = &ts_cache_table->entries[i];
273 timestamp_add_table_entry(ts_cbmem_table, tse->entry_id,
274 tse->entry_stamp);
275 }
276
277 /* Freshly added cbmem table has base_time 0. Inherit cache base_time */
278 if (ts_cbmem_table->base_time == 0)
279 ts_cbmem_table->base_time = ts_cache_table->base_time;
280
281 /* Cache no longer required. */
282 ts_cache_table->num_entries = 0;
283 ts_cache->cache_state = TIMESTAMP_CACHE_NOT_NEEDED;
Kyösti Mälkki3d45c402013-09-07 20:26:36 +0300284}
Kyösti Mälkkicbf5bdf2013-09-10 00:07:21 +0300285
Aaron Durbin1936f6c2015-07-03 17:04:21 -0500286ROMSTAGE_CBMEM_INIT_HOOK(timestamp_sync_cache_to_cbmem)
287RAMSTAGE_CBMEM_INIT_HOOK(timestamp_sync_cache_to_cbmem)
Aaron Durbin9e80e272015-05-01 16:48:54 -0500288
289/* Provide default timestamp implementation using monotonic timer. */
290uint64_t __attribute__((weak)) timestamp_get(void)
291{
292 struct mono_time t1, t2;
293
Aaron Durbin7aafe532015-05-07 11:32:30 -0500294 if (!IS_ENABLED(CONFIG_HAVE_MONOTONIC_TIMER))
295 return 0;
296
Aaron Durbin9e80e272015-05-01 16:48:54 -0500297 mono_time_set_usecs(&t1, 0);
298 timer_monotonic_get(&t2);
299
300 return mono_time_diff_microseconds(&t1, &t2);
301}