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 *
ts_id_to_name(uint32_t id)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
coreboot_timestamps_sysctl(SYSCTL_HANDLER_ARGS)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
coreboot_timestamps_register(struct coreboot_softc * sc,struct sysctl_oid * parent)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