xref: /aosp_15_r20/external/coreboot/src/lib/timestamp.c (revision b9411a12aaaa7e1e6a6fb7c5e057f44ee179a49c)
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