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