xref: /freebsd/sys/dev/coreboot/coreboot_timestamps.c (revision f5dc2263ab1be8a35a7e27e82103f9ccd41ae584)
1 /*
2  * Copyright (c) 2026 Abdelkader Boudih <freebsd@seuros.com>
3  *
4  * SPDX-License-Identifier: BSD-2-Clause
5  */
6 
7 /*
8  * coreboot(4) timestamp CBMEM reader
9  *
10  * Maps the timestamp CBMEM region on demand and formats boot stage
11  * timing as a human-readable sysctl.  The mapping is transient —
12  * created when the sysctl is read and released immediately after.
13  */
14 
15 #include <sys/systm.h>
16 #include <sys/kernel.h>
17 #include <sys/sbuf.h>
18 #include <sys/sysctl.h>
19 
20 #include <vm/vm.h>
21 #include <vm/vm_param.h>
22 #include <vm/pmap.h>
23 
24 #include <dev/coreboot/coreboot.h>
25 
26 /*
27  * Timestamp ID to name mapping.
28  * IDs from coreboot's src/commonlib/include/commonlib/timestamp_serialized.h.
29  */
30 static const struct cb_id_name ts_names[] = {
31 	{    1, "start of romstage" },
32 	{    2, "before RAM initialization" },
33 	{    3, "after RAM initialization" },
34 	{    4, "end of romstage" },
35 	{    5, "start of verified boot" },
36 	{    6, "end of verified boot" },
37 	{    8, "starting to load ramstage" },
38 	{    9, "finished loading ramstage" },
39 	{   10, "start of ramstage" },
40 	{   11, "start of bootblock" },
41 	{   12, "end of bootblock" },
42 	{   13, "starting to load romstage" },
43 	{   14, "finished loading romstage" },
44 	{   15, "starting LZMA decompress" },
45 	{   16, "finished LZMA decompress" },
46 	{   17, "starting LZ4 decompress" },
47 	{   18, "finished LZ4 decompress" },
48 	{   19, "starting ZSTD decompress" },
49 	{   20, "finished ZSTD decompress" },
50 	{   30, "early chipset initialization" },
51 	{   31, "device enumeration" },
52 	{   40, "device configure" },
53 	{   50, "device enable" },
54 	{   60, "device initialization" },
55 	{   65, "option ROM initialization" },
56 	{   66, "option ROM copy done" },
57 	{   67, "option ROM run done" },
58 	{   70, "device setup done" },
59 	{   75, "cbmem post" },
60 	{   80, "write tables" },
61 	{   85, "finalize chips" },
62 	{   90, "starting to load payload" },
63 	{   98, "acpi wake jump" },
64 	{   99, "selfboot jump" },
65 	{  100, "start of postcar" },
66 	{  101, "end of postcar" },
67 	{  110, "forced delay start" },
68 	{  111, "forced delay end" },
69 	{  112, "read microcode start" },
70 	{  113, "read microcode end" },
71 	{  114, "elog init start" },
72 	{  115, "elog init end" },
73 	{  950, "fsp memory init start" },
74 	{  951, "fsp memory init end" },
75 	{  952, "fsp temp RAM exit start" },
76 	{  953, "fsp temp RAM exit end" },
77 	{  954, "fsp silicon init start" },
78 	{  955, "fsp silicon init end" },
79 	{  956, "fsp enumerate start" },
80 	{  957, "fsp enumerate end" },
81 	{  958, "fsp finalize start" },
82 	{  959, "fsp finalize end" },
83 	{  960, "fsp end-of-firmware start" },
84 	{  961, "fsp end-of-firmware end" },
85 	{  962, "fsp multi-phase silicon init start" },
86 	{  963, "fsp multi-phase silicon init end" },
87 	{  964, "fsp multi-phase memory init start" },
88 	{  965, "fsp multi-phase memory init end" },
89 	{  970, "fsp memory init load" },
90 	{  971, "fsp silicon init load" },
91 	{ 1000, "depthcharge start" },
92 	{ 1100, "vboot done" },
93 	{ 1101, "kernel start" },
94 	{ 1102, "kernel decompression" },
95 	{ 0, NULL }
96 };
97 
98 static const char *
99 ts_id_to_name(uint32_t id)
100 {
101 
102 	return (cb_id_lookup(ts_names, id, NULL));
103 }
104 
105 /*
106  * Sysctl handler for hw.coreboot.timestamps.
107  * Maps the CBMEM timestamp region, formats entries, and unmaps.
108  */
109 static int
110 coreboot_timestamps_sysctl(SYSCTL_HANDLER_ARGS)
111 {
112 	struct coreboot_softc *sc = arg1;
113 	struct cb_timestamp_table *tst;
114 	struct sbuf *sb;
115 	void *va;
116 	vm_size_t map_size;
117 	uint32_t i, max, num;
118 	uint64_t base;
119 	uint64_t total;
120 	uint32_t freq;
121 	int error;
122 
123 	if (sc->timestamps_paddr == 0)
124 		return (ENXIO);
125 
126 	va = pmap_mapbios(sc->timestamps_paddr,
127 	    sizeof(struct cb_timestamp_table));
128 	if (va == NULL)
129 		return (ENOMEM);
130 
131 	tst = (struct cb_timestamp_table *)va;
132 	max = tst->max_entries;
133 	num = tst->num_entries;
134 	freq = tst->tick_freq_mhz;
135 	base = tst->base_time;
136 
137 	if (max > 1024)
138 		max = 1024;
139 	if (num > max)
140 		num = max;
141 
142 	pmap_unmapbios(va, sizeof(struct cb_timestamp_table));
143 
144 	total = (uint64_t)sizeof(struct cb_timestamp_table) +
145 	    (uint64_t)num * sizeof(struct cb_timestamp_entry);
146 	if (total > SIZE_MAX)
147 		return (EINVAL);
148 	map_size = (vm_size_t)total;
149 	va = pmap_mapbios(sc->timestamps_paddr, map_size);
150 	if (va == NULL)
151 		return (ENOMEM);
152 
153 	tst = (struct cb_timestamp_table *)va;
154 
155 	sb = sbuf_new_for_sysctl(NULL, NULL, 0, req);
156 	if (sb == NULL) {
157 		pmap_unmapbios(va, map_size);
158 		return (ENOMEM);
159 	}
160 
161 	sbuf_printf(sb, "\n%-4s %-40s %12s %12s\n",
162 	    "ID", "Stage", "Stamp (us)", "Delta (us)");
163 	sbuf_printf(sb, "---- ----------------------------------------"
164 	    " ------------ ------------\n");
165 
166 	for (i = 0; i < num; i++) {
167 		uint32_t eid = tst->entries[i].entry_id;
168 		int64_t stamp = tst->entries[i].entry_stamp;
169 		int64_t us, delta_us;
170 		const char *name;
171 
172 		if (freq > 0)
173 			us = (stamp - (int64_t)base) / (int64_t)freq;
174 		else
175 			us = stamp;
176 
177 		if (i > 0 && freq > 0) {
178 			int64_t prev = tst->entries[i - 1].entry_stamp;
179 			delta_us = (stamp - prev) / (int64_t)freq;
180 		} else {
181 			delta_us = 0;
182 		}
183 
184 		name = ts_id_to_name(eid);
185 		if (name != NULL)
186 			sbuf_printf(sb, "%4u %-40s %12jd %12jd\n",
187 			    eid, name,
188 			    (intmax_t)us, (intmax_t)delta_us);
189 		else
190 			sbuf_printf(sb, "%4u %-40s %12jd %12jd\n",
191 			    eid, "(unknown)",
192 			    (intmax_t)us, (intmax_t)delta_us);
193 	}
194 
195 	error = sbuf_finish(sb);
196 	sbuf_delete(sb);
197 	pmap_unmapbios(va, map_size);
198 
199 	return (error);
200 }
201 
202 int
203 coreboot_timestamps_register(struct coreboot_softc *sc,
204     struct sysctl_oid *parent)
205 {
206 
207 	SYSCTL_ADD_PROC(&sc->sysctl_ctx,
208 	    SYSCTL_CHILDREN(parent), OID_AUTO, "timestamps",
209 	    CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE,
210 	    sc, 0, coreboot_timestamps_sysctl, "A",
211 	    "Boot stage timing from coreboot timestamps");
212 
213 	return (0);
214 }
215