aboutsummaryrefslogtreecommitdiff
path: root/sys/dev/coreboot/coreboot_timestamps.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/coreboot/coreboot_timestamps.c')
-rw-r--r--sys/dev/coreboot/coreboot_timestamps.c214
1 files changed, 214 insertions, 0 deletions
diff --git a/sys/dev/coreboot/coreboot_timestamps.c b/sys/dev/coreboot/coreboot_timestamps.c
new file mode 100644
index 000000000000..ab0e5b2cd114
--- /dev/null
+++ b/sys/dev/coreboot/coreboot_timestamps.c
@@ -0,0 +1,214 @@
+/*
+ * Copyright (c) 2026 Abdelkader Boudih <freebsd@seuros.com>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+/*
+ * coreboot(4) timestamp CBMEM reader
+ *
+ * Maps the timestamp CBMEM region on demand and formats boot stage
+ * timing as a human-readable sysctl. The mapping is transient —
+ * created when the sysctl is read and released immediately after.
+ */
+
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/sbuf.h>
+#include <sys/sysctl.h>
+
+#include <vm/vm.h>
+#include <vm/vm_param.h>
+#include <vm/pmap.h>
+
+#include <dev/coreboot/coreboot.h>
+
+/*
+ * Timestamp ID to name mapping.
+ * IDs from coreboot's src/commonlib/include/commonlib/timestamp_serialized.h.
+ */
+static const struct cb_id_name ts_names[] = {
+ { 1, "start of romstage" },
+ { 2, "before RAM initialization" },
+ { 3, "after RAM initialization" },
+ { 4, "end of romstage" },
+ { 5, "start of verified boot" },
+ { 6, "end of verified boot" },
+ { 8, "starting to load ramstage" },
+ { 9, "finished loading ramstage" },
+ { 10, "start of ramstage" },
+ { 11, "start of bootblock" },
+ { 12, "end of bootblock" },
+ { 13, "starting to load romstage" },
+ { 14, "finished loading romstage" },
+ { 15, "starting LZMA decompress" },
+ { 16, "finished LZMA decompress" },
+ { 17, "starting LZ4 decompress" },
+ { 18, "finished LZ4 decompress" },
+ { 19, "starting ZSTD decompress" },
+ { 20, "finished ZSTD decompress" },
+ { 30, "early chipset initialization" },
+ { 31, "device enumeration" },
+ { 40, "device configure" },
+ { 50, "device enable" },
+ { 60, "device initialization" },
+ { 65, "option ROM initialization" },
+ { 66, "option ROM copy done" },
+ { 67, "option ROM run done" },
+ { 70, "device setup done" },
+ { 75, "cbmem post" },
+ { 80, "write tables" },
+ { 85, "finalize chips" },
+ { 90, "starting to load payload" },
+ { 98, "acpi wake jump" },
+ { 99, "selfboot jump" },
+ { 100, "start of postcar" },
+ { 101, "end of postcar" },
+ { 110, "forced delay start" },
+ { 111, "forced delay end" },
+ { 112, "read microcode start" },
+ { 113, "read microcode end" },
+ { 114, "elog init start" },
+ { 115, "elog init end" },
+ { 950, "fsp memory init start" },
+ { 951, "fsp memory init end" },
+ { 952, "fsp temp RAM exit start" },
+ { 953, "fsp temp RAM exit end" },
+ { 954, "fsp silicon init start" },
+ { 955, "fsp silicon init end" },
+ { 956, "fsp enumerate start" },
+ { 957, "fsp enumerate end" },
+ { 958, "fsp finalize start" },
+ { 959, "fsp finalize end" },
+ { 960, "fsp end-of-firmware start" },
+ { 961, "fsp end-of-firmware end" },
+ { 962, "fsp multi-phase silicon init start" },
+ { 963, "fsp multi-phase silicon init end" },
+ { 964, "fsp multi-phase memory init start" },
+ { 965, "fsp multi-phase memory init end" },
+ { 970, "fsp memory init load" },
+ { 971, "fsp silicon init load" },
+ { 1000, "depthcharge start" },
+ { 1100, "vboot done" },
+ { 1101, "kernel start" },
+ { 1102, "kernel decompression" },
+ { 0, NULL }
+};
+
+static const char *
+ts_id_to_name(uint32_t id)
+{
+
+ return (cb_id_lookup(ts_names, id, NULL));
+}
+
+/*
+ * Sysctl handler for hw.coreboot.timestamps.
+ * Maps the CBMEM timestamp region, formats entries, and unmaps.
+ */
+static int
+coreboot_timestamps_sysctl(SYSCTL_HANDLER_ARGS)
+{
+ struct coreboot_softc *sc = arg1;
+ struct cb_timestamp_table *tst;
+ struct sbuf *sb;
+ void *va;
+ vm_size_t map_size;
+ uint32_t i, max, num;
+ uint64_t base;
+ uint64_t total;
+ uint32_t freq;
+ int error;
+
+ if (sc->timestamps_paddr == 0)
+ return (ENXIO);
+
+ va = pmap_mapbios(sc->timestamps_paddr,
+ sizeof(struct cb_timestamp_table));
+ if (va == NULL)
+ return (ENOMEM);
+
+ tst = (struct cb_timestamp_table *)va;
+ max = tst->max_entries;
+ num = tst->num_entries;
+ freq = tst->tick_freq_mhz;
+ base = tst->base_time;
+
+ if (max > 1024)
+ max = 1024;
+ if (num > max)
+ num = max;
+
+ pmap_unmapbios(va, sizeof(struct cb_timestamp_table));
+
+ total = (uint64_t)sizeof(struct cb_timestamp_table) +
+ (uint64_t)num * sizeof(struct cb_timestamp_entry);
+ if (total > SIZE_MAX)
+ return (EINVAL);
+ map_size = (vm_size_t)total;
+ va = pmap_mapbios(sc->timestamps_paddr, map_size);
+ if (va == NULL)
+ return (ENOMEM);
+
+ tst = (struct cb_timestamp_table *)va;
+
+ sb = sbuf_new_for_sysctl(NULL, NULL, 0, req);
+ if (sb == NULL) {
+ pmap_unmapbios(va, map_size);
+ return (ENOMEM);
+ }
+
+ sbuf_printf(sb, "\n%-4s %-40s %12s %12s\n",
+ "ID", "Stage", "Stamp (us)", "Delta (us)");
+ sbuf_printf(sb, "---- ----------------------------------------"
+ " ------------ ------------\n");
+
+ for (i = 0; i < num; i++) {
+ uint32_t eid = tst->entries[i].entry_id;
+ int64_t stamp = tst->entries[i].entry_stamp;
+ int64_t us, delta_us;
+ const char *name;
+
+ if (freq > 0)
+ us = (stamp - (int64_t)base) / (int64_t)freq;
+ else
+ us = stamp;
+
+ if (i > 0 && freq > 0) {
+ int64_t prev = tst->entries[i - 1].entry_stamp;
+ delta_us = (stamp - prev) / (int64_t)freq;
+ } else {
+ delta_us = 0;
+ }
+
+ name = ts_id_to_name(eid);
+ if (name != NULL)
+ sbuf_printf(sb, "%4u %-40s %12jd %12jd\n",
+ eid, name,
+ (intmax_t)us, (intmax_t)delta_us);
+ else
+ sbuf_printf(sb, "%4u %-40s %12jd %12jd\n",
+ eid, "(unknown)",
+ (intmax_t)us, (intmax_t)delta_us);
+ }
+
+ error = sbuf_finish(sb);
+ sbuf_delete(sb);
+ pmap_unmapbios(va, map_size);
+
+ return (error);
+}
+
+int
+coreboot_timestamps_register(struct coreboot_softc *sc,
+ struct sysctl_oid *parent)
+{
+
+ SYSCTL_ADD_PROC(&sc->sysctl_ctx,
+ SYSCTL_CHILDREN(parent), OID_AUTO, "timestamps",
+ CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE,
+ sc, 0, coreboot_timestamps_sysctl, "A",
+ "Boot stage timing from coreboot timestamps");
+
+ return (0);
+}