1 /* SPDX-License-Identifier: GPL-2.0-only */
2
3 #include <assert.h>
4 #include <stdint.h>
5 #include <console/console.h>
6 #include <cbmem.h>
7 #include <symbols.h>
8 #include <timer.h>
9 #include <timestamp.h>
10 #include <smp/node.h>
11
12 #define MAX_TIMESTAMPS 192
13
14 /* This points to the active timestamp_table and can change within a stage
15 as CBMEM comes available. */
16 static struct timestamp_table *glob_ts_table;
17
timestamp_cache_init(struct timestamp_table * ts_cache,uint64_t base)18 static void timestamp_cache_init(struct timestamp_table *ts_cache,
19 uint64_t base)
20 {
21 ts_cache->num_entries = 0;
22 ts_cache->base_time = base;
23 ts_cache->max_entries = (REGION_SIZE(timestamp) -
24 offsetof(struct timestamp_table, entries))
25 / sizeof(struct timestamp_entry);
26 }
27
timestamp_cache_get(void)28 static struct timestamp_table *timestamp_cache_get(void)
29 {
30 struct timestamp_table *ts_cache = NULL;
31
32 if (!ENV_ROMSTAGE_OR_BEFORE)
33 return NULL;
34
35 if (REGION_SIZE(timestamp) < sizeof(*ts_cache)) {
36 BUG();
37 } else {
38 ts_cache = (void *)_timestamp;
39 }
40
41 return ts_cache;
42 }
43
timestamp_alloc_cbmem_table(void)44 static struct timestamp_table *timestamp_alloc_cbmem_table(void)
45 {
46 struct timestamp_table *tst;
47
48 tst = cbmem_add(CBMEM_ID_TIMESTAMP,
49 sizeof(struct timestamp_table) +
50 MAX_TIMESTAMPS * sizeof(struct timestamp_entry));
51
52 if (!tst)
53 return NULL;
54
55 tst->base_time = 0;
56 tst->max_entries = MAX_TIMESTAMPS;
57 tst->num_entries = 0;
58
59 return tst;
60 }
61
62 /* Determine if one should proceed into timestamp code. This is for protecting
63 * systems that have multiple processors running in romstage -- namely AMD
64 * based x86 platforms. */
timestamp_should_run(void)65 static int timestamp_should_run(void)
66 {
67 /*
68 * Only check boot_cpu() in other stages than
69 * ENV_PAYLOAD_LOADER on x86.
70 */
71 if ((!ENV_PAYLOAD_LOADER && ENV_X86) && !boot_cpu())
72 return 0;
73
74 return 1;
75 }
76
timestamp_table_get(void)77 static struct timestamp_table *timestamp_table_get(void)
78 {
79 if (glob_ts_table)
80 return glob_ts_table;
81
82 glob_ts_table = timestamp_cache_get();
83
84 return glob_ts_table;
85 }
86
timestamp_table_set(struct timestamp_table * ts)87 static void timestamp_table_set(struct timestamp_table *ts)
88 {
89 glob_ts_table = ts;
90 }
91
timestamp_name(enum timestamp_id id)92 static const char *timestamp_name(enum timestamp_id id)
93 {
94 int i;
95
96 for (i = 0; i < ARRAY_SIZE(timestamp_ids); i++) {
97 if (timestamp_ids[i].id == id)
98 return timestamp_ids[i].name;
99 }
100
101 return "Unknown timestamp ID";
102 }
103
timestamp_add_table_entry(struct timestamp_table * ts_table,enum timestamp_id id,int64_t ts_time)104 static void timestamp_add_table_entry(struct timestamp_table *ts_table,
105 enum timestamp_id id, int64_t ts_time)
106 {
107 struct timestamp_entry *tse;
108
109 if (ts_table->num_entries >= ts_table->max_entries)
110 return;
111
112 tse = &ts_table->entries[ts_table->num_entries++];
113 tse->entry_id = id;
114 tse->entry_stamp = ts_time;
115
116 if (ts_table->num_entries == ts_table->max_entries)
117 printk(BIOS_ERR, "Timestamp table full\n");
118 }
119
timestamp_add(enum timestamp_id id,int64_t ts_time)120 void timestamp_add(enum timestamp_id id, int64_t ts_time)
121 {
122 struct timestamp_table *ts_table;
123
124 if (!timestamp_should_run())
125 return;
126
127 ts_table = timestamp_table_get();
128
129 if (!ts_table) {
130 printk(BIOS_ERR, "No timestamp table found\n");
131 return;
132 }
133
134 ts_time -= ts_table->base_time;
135 timestamp_add_table_entry(ts_table, id, ts_time);
136
137 if (CONFIG(TIMESTAMPS_ON_CONSOLE))
138 printk(BIOS_INFO, "Timestamp - %s: %lld\n", timestamp_name(id), ts_time);
139 }
140
timestamp_add_now(enum timestamp_id id)141 void timestamp_add_now(enum timestamp_id id)
142 {
143 timestamp_add(id, timestamp_get());
144 }
145
timestamp_init(uint64_t base)146 void timestamp_init(uint64_t base)
147 {
148 struct timestamp_table *ts_cache;
149
150 assert(ENV_ROMSTAGE_OR_BEFORE);
151
152 if (!timestamp_should_run())
153 return;
154
155 ts_cache = timestamp_cache_get();
156
157 if (!ts_cache) {
158 printk(BIOS_ERR, "No timestamp cache to init\n");
159 return;
160 }
161
162 timestamp_cache_init(ts_cache, base);
163 timestamp_table_set(ts_cache);
164 }
165
timestamp_sync_cache_to_cbmem(struct timestamp_table * ts_cbmem_table)166 static void timestamp_sync_cache_to_cbmem(struct timestamp_table *ts_cbmem_table)
167 {
168 uint32_t i;
169 struct timestamp_table *ts_cache_table;
170
171 ts_cache_table = timestamp_table_get();
172 if (!ts_cache_table) {
173 printk(BIOS_ERR, "No timestamp cache found\n");
174 return;
175 }
176
177 /*
178 * There's no need to worry about the base_time fields being out of
179 * sync because only the following configuration is used/supported:
180 *
181 * Timestamps get initialized before ramstage, which implies
182 * CBMEM initialization in romstage.
183 * This requires the board to define a TIMESTAMP() region in its
184 * memlayout.ld (default on x86). The base_time from timestamp_init()
185 * (usually called from bootblock.c on most non-x86 boards) persists
186 * in that region until it gets synced to CBMEM in romstage.
187 * In ramstage, the BSS cache's base_time will be 0 until the second
188 * sync, which will adjust the timestamps in there to the correct
189 * base_time (from CBMEM) with the timestamp_add_table_entry() below.
190 *
191 * If you try to initialize timestamps before ramstage but don't define
192 * a TIMESTAMP region, all operations will fail (safely), and coreboot
193 * will behave as if timestamps collection was disabled.
194 */
195
196 /* Inherit cache base_time. */
197 ts_cbmem_table->base_time = ts_cache_table->base_time;
198
199 for (i = 0; i < ts_cache_table->num_entries; i++) {
200 struct timestamp_entry *tse = &ts_cache_table->entries[i];
201 timestamp_add_table_entry(ts_cbmem_table, tse->entry_id,
202 tse->entry_stamp);
203 }
204
205 /* Cache no longer required. */
206 ts_cache_table->num_entries = 0;
207 }
208
timestamp_reinit(int is_recovery)209 static void timestamp_reinit(int is_recovery)
210 {
211 struct timestamp_table *ts_cbmem_table;
212
213 if (!timestamp_should_run())
214 return;
215
216 /* First time into romstage we make a clean new table. For platforms that travel
217 through this path on resume, ARCH_X86 S3, timestamps are also reset. */
218 if (ENV_CREATES_CBMEM) {
219 ts_cbmem_table = timestamp_alloc_cbmem_table();
220 } else {
221 /* Find existing table in cbmem. */
222 ts_cbmem_table = cbmem_find(CBMEM_ID_TIMESTAMP);
223 }
224
225 if (ts_cbmem_table == NULL) {
226 printk(BIOS_ERR, "No timestamp table allocated\n");
227 timestamp_table_set(NULL);
228 return;
229 }
230
231 if (ENV_CREATES_CBMEM)
232 timestamp_sync_cache_to_cbmem(ts_cbmem_table);
233
234 /* Seed the timestamp tick frequency in ENV_PAYLOAD_LOADER. */
235 if (ENV_PAYLOAD_LOADER)
236 ts_cbmem_table->tick_freq_mhz = timestamp_tick_freq_mhz();
237
238 timestamp_table_set(ts_cbmem_table);
239 }
240
timestamp_rescale_table(uint16_t N,uint16_t M)241 void timestamp_rescale_table(uint16_t N, uint16_t M)
242 {
243 uint32_t i;
244 struct timestamp_table *ts_table;
245
246 if (!timestamp_should_run())
247 return;
248
249 if (N == 0 || M == 0)
250 return;
251
252 ts_table = timestamp_table_get();
253
254 /* No timestamp table found */
255 if (ts_table == NULL) {
256 printk(BIOS_ERR, "No timestamp table found\n");
257 return;
258 }
259
260 ts_table->base_time /= M;
261 ts_table->base_time *= N;
262 for (i = 0; i < ts_table->num_entries; i++) {
263 struct timestamp_entry *tse = &ts_table->entries[i];
264 tse->entry_stamp /= M;
265 tse->entry_stamp *= N;
266 }
267 }
268
269 /*
270 * Get the time in microseconds since boot (or more precise: since timestamp
271 * table was initialized).
272 */
get_us_since_boot(void)273 uint32_t get_us_since_boot(void)
274 {
275 struct timestamp_table *ts = timestamp_table_get();
276
277 if (ts == NULL || ts->tick_freq_mhz == 0)
278 return 0;
279 return (timestamp_get() - ts->base_time) / ts->tick_freq_mhz;
280 }
281
282 CBMEM_READY_HOOK(timestamp_reinit);
283
284 /* Provide default timestamp implementation using monotonic timer. */
timestamp_get(void)285 uint64_t __weak timestamp_get(void)
286 {
287 struct mono_time t1, t2;
288
289 if (!CONFIG(HAVE_MONOTONIC_TIMER))
290 return 0;
291
292 mono_time_set_usecs(&t1, 0);
293 timer_monotonic_get(&t2);
294
295 return mono_time_diff_microseconds(&t1, &t2);
296 }
297
298 /* Like timestamp_get() above this matches up with microsecond granularity. */
timestamp_tick_freq_mhz(void)299 int __weak timestamp_tick_freq_mhz(void)
300 {
301 return 1;
302 }
303