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