Page MenuHomeFreeBSD

D55649.diff
No OneTemporary

D55649.diff

diff --git a/include/Makefile b/include/Makefile
--- a/include/Makefile
+++ b/include/Makefile
@@ -151,6 +151,10 @@
smp_all.h
CAMSCSIDIR= ${INCLUDEDIR}/cam/scsi
+.PATH: ${SRCTOP}/sys/dev/coreboot
+COREBOOT= coreboot_ioctl.h
+COREBOOTDIR= ${INCLUDEDIR}/dev/coreboot
+
.PATH: ${SRCTOP}/sys/fs/cd9660
FS9660= cd9660_mount.h \
cd9660_node.h \
@@ -254,6 +258,7 @@
CAMMMC \
CAMNVME \
CAMSCSI \
+ COREBOOT \
CRYPTO \
EVDEV \
FS9660 \
@@ -438,6 +443,8 @@
${SDESTDIR}${INCLUDEDIR}/dev/hyperv; \
${INSTALL_SYMLINK} ${TAG_ARGS:D${TAG_ARGS},dev} ../../../../sys/dev/pci/pcireg.h \
${SDESTDIR}${INCLUDEDIR}/dev/pci; \
+ ${INSTALL_SYMLINK} ${TAG_ARGS:D${TAG_ARGS},dev} ../../../../sys/dev/coreboot/coreboot_ioctl.h \
+ ${SDESTDIR}${INCLUDEDIR}/dev/coreboot; \
${INSTALL_SYMLINK} ${TAG_ARGS:D${TAG_ARGS},dev} ../../../../sys/dev/veriexec/veriexec_ioctl.h \
${SDESTDIR}${INCLUDEDIR}/dev/veriexec;
.for i in ${LSUBSUBDIRS}
diff --git a/share/man/man4/Makefile b/share/man/man4/Makefile
--- a/share/man/man4/Makefile
+++ b/share/man/man4/Makefile
@@ -123,6 +123,7 @@
chromebook_platform.4 \
${_chvgpio.4} \
ciss.4 \
+ coreboot.4 \
${_coretemp.4} \
cp2112.4 \
${_cpuctl.4} \
diff --git a/share/man/man4/coreboot.4 b/share/man/man4/coreboot.4
new file mode 100644
--- /dev/null
+++ b/share/man/man4/coreboot.4
@@ -0,0 +1,355 @@
+.\" SPDX-License-Identifier: BSD-2-Clause
+.\"
+.\" Copyright (c) 2026 Abdelkader Boudih <freebsd@seuros.com>
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.Dd March 2, 2026
+.Dt COREBOOT 4
+.Os
+.Sh NAME
+.Nm coreboot
+.Nd coreboot firmware table driver
+.Sh SYNOPSIS
+To compile this driver into the kernel,
+place the following line in your
+kernel configuration file:
+.Bd -ragged -offset indent
+.Cd "device coreboot"
+.Ed
+.Pp
+Alternatively, to load the driver as a
+module at boot time, place the following line in
+.Xr loader.conf 5 :
+.Bd -literal -offset indent
+coreboot_load="YES"
+.Ed
+.Sh DESCRIPTION
+The
+.Nm
+driver provides access to firmware tables created by the coreboot
+open-source firmware project.
+It discovers the coreboot table by scanning low memory for the
+.Dq LBIO
+signature, follows any forward pointer to the real table in high memory,
+and validates the IP-style checksums.
+.Pp
+The driver exposes firmware information through three interfaces:
+.Bl -tag -width "/dev/coreboot_console"
+.It Sy sysctl
+A
+.Xr sysctl 8
+tree under
+.Va hw.coreboot
+provides access to:
+.Bl -bullet -compact
+.It
+firmware version, build date, and compiler information
+.It
+mainboard identification and board configuration
+.It
+MAC addresses, framebuffer, GPIO, and SPI flash details
+.It
+TPM information and SMMSTORE configuration
+.It
+ACPI RSDP address, PCIe controller base, and TSC frequency
+.It
+boot timestamps and CBMEM entry enumeration
+.El
+.It Pa /dev/coreboot_console
+A character device providing read-only access to the CBMEM firmware console
+ring buffer.
+This contains coreboot's own boot log, analogous to
+.Xr dmesg 8
+but for the firmware stage before the OS kernel starts.
+The ring buffer uses bit 31 of its cursor as an overflow indicator;
+when set, the buffer has wrapped and data is read starting from the
+cursor position.
+.It Pa /dev/cbmem
+A character device providing
+.Xr ioctl 2
+access to individual CBMEM entries.
+CBMEM is coreboot's mechanism for passing data between firmware stages
+and to the operating system.
+.El
+.Ss Verbose Output
+By default, the driver prints a single attach summary line showing table
+location and size.
+.Pp
+When
+.Va bootverbose
+is set
+.Pq e.g., Dq Li boot -v ,
+the driver additionally prints firmware identity and parsed hardware details
+such as mainboard, board config, MAC count, ACPI RSDP, SPI flash, framebuffer,
+GPIO, and TPM summary.
+.Pp
+When
+.Va hw.coreboot.debug
+is non-zero, the driver prints extra internal diagnostics such as SMMSTORE,
+timestamp, TPM CB log, and FMAP addresses.
+.Pp
+All sysctl nodes are registered regardless of verbosity settings.
+.Ss sysctl Variables
+The following
+.Xr sysctl 8
+variables are available when the driver is loaded
+(variables only appear if the corresponding table record exists):
+.Bl -tag -width "hw.coreboot.smmstore.com_buffer"
+.It Va hw.coreboot.debug
+Enable verbose coreboot diagnostics (read-write, default 0).
+Tunable via
+.Xr loader.conf 5
+as
+.Va hw.coreboot.debug .
+.It Va hw.coreboot.version
+Firmware version string.
+.It Va hw.coreboot.build
+Build date and time.
+.It Va hw.coreboot.compile_time
+Firmware compile time string.
+.It Va hw.coreboot.compiler
+Compiler identification string.
+.It Va hw.coreboot.extra_version
+Extra version information.
+.It Va hw.coreboot.platform_blob_version
+Platform blob version string.
+.It Va hw.coreboot.serialno
+Board serial number.
+.It Va hw.coreboot.version_timestamp
+Firmware version timestamp.
+.It Va hw.coreboot.table_addr
+Physical address of the coreboot table.
+.It Va hw.coreboot.table_size
+Total size of the coreboot table in bytes.
+.It Va hw.coreboot.mainboard.vendor
+Mainboard vendor name.
+.It Va hw.coreboot.mainboard.part
+Mainboard part number.
+.It Va hw.coreboot.serial.baseaddr
+Serial port base address.
+.It Va hw.coreboot.serial.baud
+Serial port baud rate.
+.It Va hw.coreboot.serial.regwidth
+Serial port register width.
+.It Va hw.coreboot.tsc_freq_khz
+TSC frequency in kilohertz as reported by firmware.
+.It Va hw.coreboot.pcie_ctrl_base
+PCIe controller base address.
+.It Va hw.coreboot.acpi_rsdp
+ACPI RSDP physical address.
+.It Va hw.coreboot.board.fw_config
+Firmware configuration bitmask.
+.It Va hw.coreboot.board.board_id
+Board ID.
+.It Va hw.coreboot.board.ram_code
+RAM code.
+.It Va hw.coreboot.board.sku_id
+SKU ID.
+.It Va hw.coreboot.mac.N
+Factory MAC address N (formatted as xx:xx:xx:xx:xx:xx).
+.It Va hw.coreboot.boot_media.fmap_offset
+FMAP offset from boot media start.
+.It Va hw.coreboot.boot_media.cbfs_offset
+CBFS offset from boot media start.
+.It Va hw.coreboot.boot_media.cbfs_size
+CBFS size in bytes.
+.It Va hw.coreboot.boot_media.size
+Boot media size in bytes.
+.It Va hw.coreboot.mmc_early_cmd1_status
+Early eMMC/MMC CMD1 status value.
+.It Va hw.coreboot.spi_flash.size
+SPI flash size in bytes.
+.It Va hw.coreboot.spi_flash.sector_size
+SPI flash sector size in bytes.
+.It Va hw.coreboot.spi_flash.erase_cmd
+SPI flash erase command byte.
+.It Va hw.coreboot.console_type
+Firmware console type (0=serial8250, 1=VGA, 2=BTEXT, 3=LOGBUF, 4=SROM,
+5=EHCI, 6=serial8250mem).
+.It Va hw.coreboot.framebuffer.addr
+Framebuffer physical address.
+.It Va hw.coreboot.framebuffer.x_res
+Framebuffer horizontal resolution.
+.It Va hw.coreboot.framebuffer.y_res
+Framebuffer vertical resolution.
+.It Va hw.coreboot.framebuffer.bpp
+Framebuffer bits per pixel.
+.It Va hw.coreboot.gpio.N.name
+GPIO pin N name.
+.It Va hw.coreboot.gpio.N.port
+GPIO pin N port number.
+.It Va hw.coreboot.gpio.N.value
+GPIO pin N value.
+.It Va hw.coreboot.gpio.N.polarity
+GPIO pin N polarity.
+.It Va hw.coreboot.tpm.version
+TPM version (1=TPM 1.2, 2=TPM 2.0).
+.It Va hw.coreboot.tpm.ppi_addr
+TPM Physical Presence Interface address.
+.It Va hw.coreboot.tpm.cblog_addr
+TPM event log physical address.
+.It Va hw.coreboot.smmstore.num_blocks
+SMMSTORE v2 number of blocks.
+.It Va hw.coreboot.smmstore.block_size
+SMMSTORE v2 block size in bytes.
+.It Va hw.coreboot.smmstore.mmap_addr
+SMMSTORE v2 memory-mapped address.
+.It Va hw.coreboot.smmstore.com_buffer
+SMMSTORE v2 communication buffer address.
+.It Va hw.coreboot.smmstore.apm_cmd
+SMMSTORE v2 APM command byte.
+.It Va hw.coreboot.cbmem_refs.acpi_gnvs
+ACPI GNVS CBMEM physical address.
+.It Va hw.coreboot.cbmem_refs.acpi_cnvs
+ACPI CNVS CBMEM physical address.
+.It Va hw.coreboot.cbmem_refs.vpd
+VPD CBMEM physical address.
+.It Va hw.coreboot.cbmem_refs.wifi_calibration
+WiFi calibration CBMEM physical address.
+.It Va hw.coreboot.cbmem_refs.fmap
+FMAP CBMEM physical address.
+.It Va hw.coreboot.cbmem_refs.vboot_workbuf
+Vboot work buffer CBMEM physical address.
+.It Va hw.coreboot.cbmem_refs.type_c_info
+Type-C info CBMEM physical address.
+.It Va hw.coreboot.cbmem_refs.root_bridge_info
+Root bridge info CBMEM physical address.
+.It Va hw.coreboot.timestamps_addr
+Timestamps CBMEM physical address.
+.It Va hw.coreboot.timestamps
+Formatted boot stage timing table (read-only).
+Each line shows the timestamp ID, stage name, absolute time in
+microseconds, and delta from the previous stage.
+.It Va hw.coreboot.cbmem.N.id
+CBMEM entry ID.
+.It Va hw.coreboot.cbmem.N.name
+Human-readable CBMEM entry name.
+.It Va hw.coreboot.cbmem.N.address
+Physical address of the CBMEM entry.
+.It Va hw.coreboot.cbmem.N.size
+Size of the CBMEM entry in bytes.
+.El
+.Ss ioctl Interface
+The
+.Pa /dev/cbmem
+device supports the following
+.Xr ioctl 2
+commands, defined in
+.In dev/coreboot/coreboot_ioctl.h :
+.Bl -tag -width CBMEM_IOC_LIST
+.It Dv CBMEM_IOC_LIST
+Returns a
+.Vt struct cbmem_list
+containing the count and metadata of all discovered CBMEM entries.
+.It Dv CBMEM_IOC_READ
+Reads data from a CBMEM entry identified by its ID.
+Takes a
+.Vt struct cbmem_read_req
+specifying the entry ID, offset, size, and a userspace buffer.
+.El
+.Sh FILES
+.Bl -tag -width "/dev/coreboot_console"
+.It Pa /dev/coreboot_console
+Firmware console ring buffer (read-only).
+.It Pa /dev/cbmem
+CBMEM entry access device.
+.El
+.Sh EXAMPLES
+Display the coreboot firmware version:
+.Pp
+.Dl "sysctl hw.coreboot.version"
+.Pp
+Read the firmware boot log:
+.Pp
+.Dl "cat /dev/coreboot_console"
+.Pp
+List all CBMEM entries:
+.Pp
+.Dl "sysctl hw.coreboot.cbmem"
+.Pp
+Show board identification:
+.Pp
+.Dl "sysctl hw.coreboot.board"
+.Pp
+Show factory MAC addresses:
+.Pp
+.Dl "sysctl hw.coreboot.mac"
+.Pp
+Display boot stage timestamps:
+.Pp
+.Dl "sysctl hw.coreboot.timestamps"
+.Pp
+Enable debug output and reload the module (the sysctl value resets on
+unload, so it must be set again or persisted via
+.Xr loader.conf 5 ) :
+.Bd -literal -offset indent
+kldunload coreboot
+kldload coreboot
+sysctl hw.coreboot.debug=1
+dmesg | grep coreboot
+.Ed
+.Pp
+To persist debug across reboots, add to
+.Xr loader.conf 5 :
+.Bd -literal -offset indent
+hw.coreboot.debug=1
+.Ed
+.Sh COMPATIBILITY
+The
+.Nm
+driver works with any x86 system running coreboot firmware.
+The coreboot table format uses a stable ABI with forward-compatible
+tagged records; unknown tags are safely skipped.
+.Pp
+Linux provides similar functionality through a custom bus type with
+separate modules for each table record type, exposed via sysfs.
+The
+.Fx
+driver uses a single module with native
+.Xr sysctl 8
+and
+.Xr ioctl 2
+interfaces instead.
+.Sh SEE ALSO
+.Xr ioctl 2 ,
+.Xr smbios 4 ,
+.Xr sysctl 8
+.Sh HISTORY
+The
+.Nm
+driver first appeared in
+.Fx 16.0 .
+.Sh AUTHORS
+.An Abdelkader Boudih Aq Mt freebsd@seuros.com .
+.Sh CAVEATS
+The driver discovers the coreboot table by scanning physical memory
+addresses 0x0 through 0x1000 for the
+.Dq LBIO
+signature.
+On systems without coreboot firmware, the driver will silently
+not attach.
+.Pp
+The
+.Pa /dev/coreboot_console
+content is static after boot; coreboot does not write to the
+console buffer after handing off control to the payload.
diff --git a/sys/conf/files.x86 b/sys/conf/files.x86
--- a/sys/conf/files.x86
+++ b/sys/conf/files.x86
@@ -83,6 +83,10 @@
dev/bxe/57710_init_values.c optional bxe pci
dev/bxe/57711_init_values.c optional bxe pci
dev/bxe/57712_init_values.c optional bxe pci
+dev/coreboot/coreboot.c optional coreboot
+dev/coreboot/coreboot_console.c optional coreboot
+dev/coreboot/coreboot_cbmem.c optional coreboot
+dev/coreboot/coreboot_timestamps.c optional coreboot
dev/coretemp/coretemp.c optional coretemp
dev/cpuctl/cpuctl.c optional cpuctl
dev/dpms/dpms.c optional dpms
diff --git a/sys/dev/coreboot/coreboot.h b/sys/dev/coreboot/coreboot.h
new file mode 100644
--- /dev/null
+++ b/sys/dev/coreboot/coreboot.h
@@ -0,0 +1,624 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2026 Abdelkader Boudih <freebsd@seuros.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * coreboot(4) driver for FreeBSD
+ *
+ * Structures and constants derived from the coreboot table specification.
+ * Written from scratch; not a copy of GPL-licensed coreboot headers.
+ */
+
+#ifndef _DEV_COREBOOT_COREBOOT_H_
+#define _DEV_COREBOOT_COREBOOT_H_
+
+#include <sys/types.h>
+#include <sys/sysctl.h>
+#include <sys/bus.h>
+
+#include <dev/coreboot/coreboot_ioctl.h>
+
+#define CB_HEADER_SIGNATURE "LBIO"
+#define CB_HEADER_SIG_LEN 4
+
+/*
+ * Memory scan range for coreboot table discovery.
+ * Low memory (0x0–0x1000) contains a forward pointer to the real table.
+ */
+#define CB_SCAN_LOW_START 0x00000000
+#define CB_SCAN_LOW_END 0x00001000
+#define CB_SCAN_LOW_STEP 16
+
+/*
+ * Defensive limits for parsing untrusted firmware-provided lengths.
+ */
+#define CB_TABLE_ALIGN 4
+#define CB_MAX_HEADER_BYTES 4096
+#define CB_MAX_TABLE_BYTES (1024 * 1024)
+#define CB_MAX_TABLE_MAP_BYTES (CB_MAX_HEADER_BYTES + CB_MAX_TABLE_BYTES)
+#define CB_MAX_CONSOLE_BYTES (1024 * 1024)
+
+/*
+ * Coreboot table record tags - only tags we actually parse.
+ * Full enum preserved for forward compatibility (unknown tags are skipped).
+ */
+enum cb_tag {
+ CB_TAG_UNUSED = 0x0000,
+ CB_TAG_MAINBOARD = 0x0003,
+ CB_TAG_VERSION = 0x0004,
+ CB_TAG_EXTRA_VERSION = 0x0005,
+ CB_TAG_BUILD = 0x0006,
+ CB_TAG_COMPILE_TIME = 0x0007,
+ CB_TAG_COMPILER = 0x000b,
+ CB_TAG_SERIAL = 0x000f,
+ CB_TAG_CONSOLE = 0x0010,
+ CB_TAG_FORWARD = 0x0011,
+ CB_TAG_FRAMEBUFFER = 0x0012,
+ CB_TAG_GPIO = 0x0013,
+ CB_TAG_TIMESTAMPS = 0x0016,
+ CB_TAG_CBMEM_CONSOLE = 0x0017,
+ CB_TAG_ACPI_GNVS = 0x0024,
+ CB_TAG_VERSION_TIMESTAMP = 0x0026,
+ CB_TAG_WIFI_CALIBRATION = 0x0027,
+ CB_TAG_SPI_FLASH = 0x0029,
+ CB_TAG_SERIALNO = 0x002a,
+ CB_TAG_VPD = 0x002c,
+ CB_TAG_BOOT_MEDIA_PARAMS = 0x0030,
+ CB_TAG_CBMEM_ENTRY = 0x0031,
+ CB_TAG_TSC_INFO = 0x0032,
+ CB_TAG_MAC_ADDRS = 0x0033,
+ CB_TAG_VBOOT_WORKBUF = 0x0034,
+ CB_TAG_MMC_INFO = 0x0035,
+ CB_TAG_TPM_CB_LOG = 0x0036,
+ CB_TAG_FMAP = 0x0037,
+ CB_TAG_PLATFORM_BLOB_VERSION = 0x0038,
+ CB_TAG_SMMSTOREV2 = 0x0039,
+ CB_TAG_TPM_PPI_HANDOFF = 0x003a,
+ CB_TAG_BOARD_CONFIG = 0x0040,
+ CB_TAG_ACPI_CNVS = 0x0041,
+ CB_TAG_TYPE_C_INFO = 0x0042,
+ CB_TAG_ACPI_RSDP = 0x0043,
+ CB_TAG_PCIE = 0x0044,
+ CB_TAG_ROOT_BRIDGE_INFO = 0x0048,
+};
+
+/*
+ * Coreboot table header - located at a physical address found by scanning
+ * low memory for the "LBIO" signature.
+ */
+struct cb_header {
+ uint8_t signature[CB_HEADER_SIG_LEN];
+ uint32_t header_bytes;
+ uint32_t header_checksum;
+ uint32_t table_bytes;
+ uint32_t table_checksum;
+ uint32_t table_entries;
+} __packed;
+
+/*
+ * Generic record header - every table entry starts with this.
+ */
+struct cb_record {
+ uint32_t tag;
+ uint32_t size;
+} __packed;
+
+/*
+ * CB_TAG_FORWARD - pointer to the real table in high memory.
+ */
+struct cb_forward {
+ uint32_t tag;
+ uint32_t size;
+ uint64_t forward;
+} __packed;
+
+/*
+ * CB_TAG_MAINBOARD - board vendor and part number.
+ * Strings are packed after the struct, indexed by vendor_idx and part_idx.
+ */
+struct cb_mainboard {
+ uint32_t tag;
+ uint32_t size;
+ uint8_t vendor_idx;
+ uint8_t part_idx;
+ uint8_t strings[];
+} __packed;
+
+/*
+ * Variable-length string record - used by VERSION, BUILD, COMPILE_*, etc.
+ */
+struct cb_string {
+ uint32_t tag;
+ uint32_t size;
+ uint8_t string[];
+} __packed;
+
+/*
+ * CB_TAG_SERIAL - serial port configuration.
+ */
+struct cb_serial {
+ uint32_t tag;
+ uint32_t size;
+ uint32_t type;
+ uint32_t baseaddr;
+ uint32_t baud;
+ uint32_t regwidth;
+ uint32_t input_hertz;
+} __packed;
+
+/*
+ * CB_TAG_CBMEM_CONSOLE - pointer to firmware console ring buffer.
+ */
+struct cb_cbmem_ref {
+ uint32_t tag;
+ uint32_t size;
+ uint64_t cbmem_addr;
+} __packed;
+
+/*
+ * CB_TAG_CBMEM_ENTRY - one per CBMEM region.
+ */
+struct cb_cbmem_entry {
+ uint32_t tag;
+ uint32_t size;
+ uint64_t address;
+ uint32_t entry_size;
+ uint32_t id;
+} __packed;
+
+/*
+ * CB_TAG_TSC_INFO - TSC frequency.
+ */
+struct cb_tsc_info {
+ uint32_t tag;
+ uint32_t size;
+ uint32_t freq_khz;
+} __packed;
+
+/*
+ * CB_TAG_VERSION_TIMESTAMP - build version timestamp.
+ * timestamp is Unix time in seconds (seconds since 1970-01-01 UTC).
+ */
+struct cb_version_timestamp {
+ uint32_t tag;
+ uint32_t size;
+ uint32_t timestamp;
+} __packed;
+
+/*
+ * CB_TAG_BOARD_CONFIG - board identification and firmware config.
+ */
+struct cb_board_config {
+ uint32_t tag;
+ uint32_t size;
+ uint64_t fw_config;
+ uint32_t board_id;
+ uint32_t ram_code;
+ uint32_t sku_id;
+} __packed;
+
+/*
+ * CB_TAG_BOOT_MEDIA_PARAMS - offsets and sizes on boot media.
+ */
+struct cb_boot_media_params {
+ uint32_t tag;
+ uint32_t size;
+ uint64_t fmap_offset;
+ uint64_t cbfs_offset;
+ uint64_t cbfs_size;
+ uint64_t boot_media_size;
+} __packed;
+
+/*
+ * CB_TAG_MMC_INFO - early eMMC/MMC status.
+ */
+struct cb_mmc_info {
+ uint32_t tag;
+ uint32_t size;
+ int32_t early_cmd1_status;
+} __packed;
+
+/*
+ * CB_TAG_PCIE - PCIe controller base address.
+ */
+struct cb_pcie {
+ uint32_t tag;
+ uint32_t size;
+ uint64_t ctrl_base;
+} __packed;
+
+/*
+ * CB_TAG_MAC_ADDRS - factory-provisioned MAC addresses.
+ */
+struct cb_mac_address {
+ uint8_t mac_addr[6];
+ uint8_t pad[2];
+} __packed;
+
+struct cb_macs {
+ uint32_t tag;
+ uint32_t size;
+ uint32_t count;
+ struct cb_mac_address entries[];
+} __packed;
+
+/*
+ * CB_TAG_SPI_FLASH - SPI flash chip parameters.
+ */
+struct cb_flash_mmap_window {
+ uint32_t flash_base;
+ uint32_t host_base;
+ uint32_t size;
+} __packed;
+
+struct cb_spi_flash {
+ uint32_t tag;
+ uint32_t size;
+ uint32_t flash_size;
+ uint32_t sector_size;
+ uint8_t erase_cmd;
+ uint8_t flags;
+ uint16_t reserved;
+ uint32_t mmap_count;
+ struct cb_flash_mmap_window mmap_table[];
+} __packed;
+
+/*
+ * CB_TAG_CONSOLE - firmware console type.
+ */
+struct cb_console {
+ uint32_t tag;
+ uint32_t size;
+ uint16_t type;
+ uint8_t pad[2];
+} __packed;
+
+#define CB_CONSOLE_SERIAL8250 0
+#define CB_CONSOLE_VGA 1 /* obsolete */
+#define CB_CONSOLE_EHCI 5
+#define CB_CONSOLE_SERIAL8250MEM 6
+
+/*
+ * CB_TAG_FRAMEBUFFER - linear framebuffer info.
+ * The orientation, flags, and pad fields were added later.
+ * Minimum record size is up to reserved_mask_size (29 bytes).
+ */
+#define CB_FRAMEBUFFER_MIN_SIZE 29
+struct cb_framebuffer {
+ uint32_t tag;
+ uint32_t size;
+ uint64_t physical_address;
+ uint32_t x_resolution;
+ uint32_t y_resolution;
+ uint32_t bytes_per_line;
+ uint8_t bits_per_pixel;
+ uint8_t red_mask_pos;
+ uint8_t red_mask_size;
+ uint8_t green_mask_pos;
+ uint8_t green_mask_size;
+ uint8_t blue_mask_pos;
+ uint8_t blue_mask_size;
+ uint8_t reserved_mask_pos;
+ uint8_t reserved_mask_size;
+ uint8_t orientation;
+ uint8_t flags;
+ uint8_t pad;
+} __packed;
+
+/*
+ * CB_TAG_GPIO - GPIO pin states.
+ */
+struct cb_gpio {
+ uint32_t port;
+ uint32_t polarity;
+ uint32_t value;
+ uint8_t name[16];
+} __packed;
+
+struct cb_gpios {
+ uint32_t tag;
+ uint32_t size;
+ uint32_t count;
+ struct cb_gpio entries[];
+} __packed;
+
+/*
+ * CB_TAG_TPM_PPI_HANDOFF - TPM Physical Presence Interface handoff.
+ */
+struct cb_tpm_ppi {
+ uint32_t tag;
+ uint32_t size;
+ uint32_t ppi_address;
+ uint8_t tpm_version; /* 1=TPM1.2, 2=TPM2.0 */
+ uint8_t ppi_version; /* BCD encoded */
+ uint8_t pad[2];
+} __packed;
+
+/*
+ * CB_TAG_SMMSTOREV2 - SMM-based variable store configuration.
+ *
+ * The mmap_addr field was added after the initial implementation.
+ * Older coreboot emits a shorter record (32 bytes) without it.
+ * Consumers must check rec->size to detect whether mmap_addr is present.
+ */
+#define CB_SMMSTOREV2_BASE_SIZE 32 /* size without mmap_addr */
+struct cb_smmstorev2 {
+ uint32_t tag;
+ uint32_t size;
+ uint32_t num_blocks;
+ uint32_t block_size;
+ uint32_t mmap_addr_lo; /* deprecated 32-bit address */
+ uint32_t com_buffer;
+ uint32_t com_buffer_size;
+ uint8_t apm_cmd;
+ uint8_t unused[3];
+ /* Fields below only present if size > CB_SMMSTOREV2_BASE_SIZE */
+ uint64_t mmap_addr; /* 64-bit address (preferred) */
+} __packed;
+
+/*
+ * CB_TAG_ACPI_RSDP - ACPI Root System Description Pointer address.
+ */
+struct cb_acpi_rsdp {
+ uint32_t tag;
+ uint32_t size;
+ uint64_t rsdp_pointer;
+} __packed;
+
+/*
+ * CBMEM console ring buffer (in-memory structure at cbmem_addr).
+ * Bit 31 of cursor indicates overflow (ring has wrapped).
+ */
+struct cbmem_console {
+ uint32_t size;
+ uint32_t cursor;
+ uint8_t body[];
+} __packed;
+
+#define CBMEM_CONSOLE_CURSOR_MASK ((1 << 28) - 1)
+#define CBMEM_CONSOLE_OVERFLOW (1 << 31)
+
+#define CB_MAX_MAC_ADDRS 8
+#define CB_MAX_GPIOS 32
+
+/*
+ * Driver softc
+ */
+struct coreboot_softc {
+ device_t dev;
+
+ vm_paddr_t table_paddr;
+ vm_size_t table_size;
+ void *table_vaddr;
+
+ char version[64];
+ char build[64];
+ char compile_time[64];
+ char compiler[128];
+ char extra_version[64];
+ char platform_blob_version[64];
+ char serialno[64];
+ uint32_t version_timestamp;
+ int has_version_timestamp;
+
+ char mb_vendor[64];
+ char mb_part[64];
+
+ uint32_t serial_baseaddr;
+ uint32_t serial_baud;
+ uint32_t serial_regwidth;
+ int has_serial;
+
+ uint32_t tsc_freq_khz;
+ int has_tsc_info;
+
+ uint64_t pcie_ctrl_base;
+ int has_pcie;
+
+ uint64_t fmap_offset;
+ uint64_t cbfs_offset;
+ uint64_t cbfs_size;
+ uint64_t boot_media_size;
+ int has_boot_media;
+
+ int32_t mmc_early_cmd1_status;
+ int has_mmc_info;
+
+ vm_paddr_t console_paddr;
+ vm_size_t console_size;
+ uint32_t console_data_size;
+ struct cbmem_console *console_vaddr;
+ int has_console;
+ struct cdev *console_cdev;
+
+ uint32_t cbmem_count;
+ struct cbmem_entry_info cbmem_entries[CB_MAX_CBMEM_ENTRIES];
+ struct cdev *cbmem_cdev;
+
+ uint64_t fw_config;
+ uint32_t board_id;
+ uint32_t ram_code;
+ uint32_t sku_id;
+ int has_board_config;
+
+ uint32_t mac_count;
+ struct cb_mac_address macs[CB_MAX_MAC_ADDRS];
+ char mac_strs[CB_MAX_MAC_ADDRS][18];
+
+ uint64_t acpi_rsdp;
+ int has_acpi_rsdp;
+
+ uint32_t spi_flash_size;
+ uint32_t spi_sector_size;
+ uint8_t spi_erase_cmd;
+ uint8_t spi_flags;
+ int has_spi_flash;
+
+ uint16_t console_type;
+ int has_console_type;
+
+ uint64_t fb_addr;
+ uint32_t fb_x_res;
+ uint32_t fb_y_res;
+ uint32_t fb_stride;
+ uint8_t fb_bpp;
+ int has_framebuffer;
+
+ uint32_t gpio_count;
+ struct cb_gpio gpios[CB_MAX_GPIOS];
+
+ uint32_t tpm_ppi_addr;
+ uint8_t tpm_version;
+ int has_tpm;
+
+ vm_paddr_t tpm_log_paddr;
+ int has_tpm_log;
+
+ vm_paddr_t acpi_gnvs_paddr;
+ int has_acpi_gnvs;
+ vm_paddr_t acpi_cnvs_paddr;
+ int has_acpi_cnvs;
+ vm_paddr_t vpd_paddr;
+ int has_vpd;
+ vm_paddr_t wifi_cal_paddr;
+ int has_wifi_cal;
+ vm_paddr_t fmap_paddr;
+ int has_fmap;
+ vm_paddr_t vboot_workbuf_paddr;
+ int has_vboot_workbuf;
+ vm_paddr_t type_c_info_paddr;
+ int has_type_c_info;
+ vm_paddr_t root_bridge_info_paddr;
+ int has_root_bridge_info;
+
+ uint32_t smmstore_num_blocks;
+ uint32_t smmstore_block_size;
+ uint64_t smmstore_mmap_addr;
+ uint32_t smmstore_com_buffer;
+ uint8_t smmstore_apm_cmd;
+ int has_smmstore;
+
+ vm_paddr_t timestamps_paddr;
+ int has_timestamps;
+
+ struct sysctl_ctx_list sysctl_ctx;
+ struct sysctl_oid *sysctl_tree;
+};
+
+/*
+ * CBMEM ID to human-readable name lookup.
+ */
+static __inline const char *
+cbmem_id_to_name(uint32_t id)
+{
+ static const struct {
+ uint32_t id;
+ const char *name;
+ } names[] = {
+ { CBMEM_ID_ACPI, "ACPI" },
+ { CBMEM_ID_ACPI_GNVS, "ACPI GNVS" },
+ { CBMEM_ID_AFTER_CAR, "AFTER CAR" },
+ { CBMEM_ID_CBTABLE, "COREBOOT" },
+ { CBMEM_ID_CBTABLE_FWD, "COREBOOT FWD" },
+ { CBMEM_ID_CBFS_RO_MCACHE, "RO MCACHE" },
+ { CBMEM_ID_CBFS_RW_MCACHE, "RW MCACHE" },
+ { CBMEM_ID_CONSOLE, "CONSOLE" },
+ { CBMEM_ID_ELOG, "ELOG" },
+ { CBMEM_ID_FMAP, "FMAP" },
+ { CBMEM_ID_FREESPACE, "FREE SPACE" },
+ { CBMEM_ID_FSP_RESERVED_MEMORY, "FSP MEMORY" },
+ { CBMEM_ID_FSP_RUNTIME, "FSP RUNTIME" },
+ { CBMEM_ID_FSPM_VERSION, "FSPM VERSION" },
+ { CBMEM_ID_IGD_OPREGION, "IGD OPREGION" },
+ { CBMEM_ID_IMD_ROOT, "IMD ROOT" },
+ { CBMEM_ID_IMD_SMALL, "IMD SMALL" },
+ { CBMEM_ID_MEMINFO, "MEM INFO" },
+ { CBMEM_ID_MPTABLE, "SMP TABLE" },
+ { CBMEM_ID_MRCDATA, "MRC DATA" },
+ { CBMEM_ID_PIRQ, "IRQ TABLE" },
+ { CBMEM_ID_POWER_STATE, "POWER STATE" },
+ { CBMEM_ID_RAM_OOPS, "RAMOOPS" },
+ { CBMEM_ID_RAMSTAGE, "RAMSTAGE" },
+ { CBMEM_ID_REFCODE, "REFCODE" },
+ { CBMEM_ID_RESUME, "ACPI RESUME" },
+ { CBMEM_ID_ROMSTAGE_INFO, "ROMSTAGE" },
+ { CBMEM_ID_ROMSTAGE_RAM_STACK, "ROMSTG STACK" },
+ { CBMEM_ID_ROOT, "CBMEM ROOT" },
+ { CBMEM_ID_SMBIOS, "SMBIOS" },
+ { CBMEM_ID_SMM_COMBUFFER, "SMM COMBUF" },
+ { CBMEM_ID_SMM_SAVE_SPACE, "SMM BACKUP" },
+ { CBMEM_ID_TIMESTAMP, "TIMESTAMP" },
+ { CBMEM_ID_VBOOT_WORKBUF, "VBOOT WORK" },
+ { CBMEM_ID_VPD, "VPD" },
+ { 0, NULL }
+ };
+
+ for (int i = 0; names[i].name != NULL; i++) {
+ if (names[i].id == id)
+ return (names[i].name);
+ }
+ return ("UNKNOWN");
+}
+
+/*
+ * IP-style 16-bit checksum (RFC 1071) over 16-bit words.
+ * When computed over data including its checksum field, result is 0.
+ */
+static __inline uint16_t
+cb_checksum(const void *ptr, size_t len)
+{
+ const uint8_t *p = (const uint8_t *)ptr;
+ uint32_t sum = 0;
+ size_t i;
+
+ for (i = 0; i + 1 < len; i += 2)
+ sum += (uint32_t)p[i] | ((uint32_t)p[i + 1] << 8);
+
+ if (i < len)
+ sum += p[i];
+
+ while (sum >> 16)
+ sum = (sum & 0xffff) + (sum >> 16);
+
+ return ((uint16_t)~sum);
+}
+
+/* Functions exported from coreboot.c */
+struct coreboot_softc *coreboot_get_softc(void);
+
+/* Functions exported from coreboot_console.c */
+int coreboot_console_create(struct coreboot_softc *sc);
+void coreboot_console_destroy(struct coreboot_softc *sc);
+
+/* Functions exported from coreboot_cbmem.c */
+int coreboot_cbmem_create(struct coreboot_softc *sc);
+void coreboot_cbmem_destroy(struct coreboot_softc *sc);
+
+/* Functions exported from coreboot_timestamps.c */
+int coreboot_timestamps_register(struct coreboot_softc *sc,
+ struct sysctl_oid *parent);
+
+#endif /* _DEV_COREBOOT_COREBOOT_H_ */
diff --git a/sys/dev/coreboot/coreboot.c b/sys/dev/coreboot/coreboot.c
new file mode 100644
--- /dev/null
+++ b/sys/dev/coreboot/coreboot.c
@@ -0,0 +1,1463 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2026 Abdelkader Boudih <freebsd@seuros.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * coreboot(4) - FreeBSD driver for coreboot firmware tables
+ *
+ * Discovers the coreboot table by scanning low memory for the "LBIO"
+ * signature, follows CB_TAG_FORWARD to the high-memory table, and
+ * exposes firmware information through sysctl(9) and character devices.
+ */
+
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/rman.h>
+#include <sys/sysctl.h>
+
+#include <vm/vm.h>
+#include <vm/vm_param.h>
+#include <vm/pmap.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+
+#include <dev/coreboot/coreboot.h>
+
+static struct coreboot_softc *coreboot_sc;
+
+/*
+ * Debug verbosity control, non-zero enables extra output.
+ * Tunable via loader.conf: hw.coreboot.debug=1
+ * Runtime: sysctl hw.coreboot.debug=1
+ * Registered dynamically under hw.coreboot in
+ * coreboot_register_sysctls().
+ */
+static int coreboot_debug = 0;
+TUNABLE_INT("hw.coreboot.debug", &coreboot_debug);
+
+struct coreboot_softc *
+coreboot_get_softc(void)
+{
+ return (coreboot_sc);
+}
+
+static void coreboot_identify(driver_t *, device_t);
+static int coreboot_probe(device_t);
+static int coreboot_attach(device_t);
+static int coreboot_detach(device_t);
+static int coreboot_modevent(module_t, int, void *);
+
+/*
+ * Scan a physical memory region for the "LBIO" signature.
+ * Returns the physical address of the header, or 0 if not found.
+ */
+static vm_paddr_t
+coreboot_scan_region(vm_paddr_t start, vm_paddr_t end)
+{
+ vm_paddr_t addr;
+ void *va;
+ struct cb_header *hdr;
+
+ for (addr = (start == 0 ? CB_SCAN_LOW_STEP : start); addr < end;
+ addr += CB_SCAN_LOW_STEP) {
+ va = pmap_mapbios(addr, sizeof(struct cb_header));
+ if (va == NULL)
+ continue;
+
+ hdr = (struct cb_header *)va;
+ if (memcmp(hdr->signature, CB_HEADER_SIGNATURE,
+ CB_HEADER_SIG_LEN) == 0) {
+ pmap_unmapbios(va, sizeof(struct cb_header));
+ return (addr);
+ }
+ pmap_unmapbios(va, sizeof(struct cb_header));
+ }
+ return (0);
+}
+
+/*
+ * Validate length fields in the header before using them for mappings
+ * and pointer arithmetic.
+ */
+static int
+coreboot_sanitize_header(const struct cb_header *hdr, vm_size_t *map_size)
+{
+ uint64_t total;
+
+ if (hdr->header_bytes < sizeof(*hdr) ||
+ hdr->header_bytes > CB_MAX_HEADER_BYTES)
+ return (EINVAL);
+ if ((hdr->header_bytes % CB_TABLE_ALIGN) != 0)
+ return (EINVAL);
+ if (hdr->table_bytes > CB_MAX_TABLE_BYTES)
+ return (EINVAL);
+ if ((hdr->table_bytes % CB_TABLE_ALIGN) != 0)
+ return (EINVAL);
+
+ total = (uint64_t)hdr->header_bytes + (uint64_t)hdr->table_bytes;
+ if (total > CB_MAX_TABLE_MAP_BYTES)
+ return (EINVAL);
+
+ *map_size = (vm_size_t)total;
+ return (0);
+}
+
+/*
+ * Validate the coreboot header checksum.
+ * Returns 0 on success, non-zero on failure.
+ */
+static int
+coreboot_validate_header(struct cb_header *hdr, vm_size_t mapped_len)
+{
+ uint16_t cksum;
+
+ if (hdr->header_bytes > mapped_len)
+ return (EINVAL);
+
+ cksum = cb_checksum(hdr, hdr->header_bytes);
+ if (cksum != 0)
+ return (EINVAL);
+
+ return (0);
+}
+
+/*
+ * Validate checksum for the table payload.
+ */
+static int
+coreboot_validate_table(struct cb_header *hdr, vm_size_t mapped_len)
+{
+ const uint8_t *table;
+ uint16_t cksum;
+
+ if (hdr->table_bytes == 0)
+ return (0);
+
+ if ((uint64_t)hdr->header_bytes + (uint64_t)hdr->table_bytes >
+ mapped_len)
+ return (EINVAL);
+ if (hdr->table_checksum > UINT16_MAX)
+ return (EINVAL);
+
+ table = (const uint8_t *)hdr + hdr->header_bytes;
+ cksum = cb_checksum(table, hdr->table_bytes);
+ if (cksum != (uint16_t)hdr->table_checksum)
+ return (EINVAL);
+
+ return (0);
+}
+
+static void
+coreboot_copy_bounded_string(const char *src, size_t maxlen, char *dst,
+ size_t dstlen)
+{
+ size_t slen;
+
+ if (dstlen == 0)
+ return;
+
+ slen = strnlen(src, maxlen);
+ if (slen >= dstlen)
+ slen = dstlen - 1;
+ memcpy(dst, src, slen);
+ dst[slen] = '\0';
+}
+
+/*
+ * Copy a coreboot string record into a destination buffer.
+ */
+static void
+coreboot_copy_string(const struct cb_string *rec, char *dst, size_t dstlen)
+{
+ size_t slen;
+
+ slen = rec->size - sizeof(struct cb_record);
+ if (slen >= dstlen)
+ slen = dstlen - 1;
+ memcpy(dst, rec->string, slen);
+ dst[slen] = '\0';
+
+ /* Strip trailing whitespace/nulls */
+ while (slen > 0 && (dst[slen - 1] == '\0' || dst[slen - 1] == ' ' ||
+ dst[slen - 1] == '\n'))
+ dst[--slen] = '\0';
+}
+
+/*
+ * Extract mainboard vendor and part number from the strings field.
+ */
+static void
+coreboot_parse_mainboard(struct coreboot_softc *sc,
+ const struct cb_mainboard *mb)
+{
+ const char *strings = (const char *)mb->strings;
+ size_t total = mb->size - offsetof(struct cb_mainboard, strings);
+ uint8_t vendor_off, part_off;
+
+ vendor_off = mb->vendor_idx;
+ part_off = mb->part_idx;
+
+ if (vendor_off < total)
+ coreboot_copy_bounded_string(strings + vendor_off,
+ total - vendor_off, sc->mb_vendor, sizeof(sc->mb_vendor));
+ if (part_off < total)
+ coreboot_copy_bounded_string(strings + part_off,
+ total - part_off, sc->mb_part, sizeof(sc->mb_part));
+}
+
+/*
+ * Parse all records in the coreboot table and populate softc.
+ */
+static void
+coreboot_parse_table(struct coreboot_softc *sc, struct cb_header *hdr)
+{
+ uint8_t *entry;
+ uint8_t *table_end;
+ struct cb_record *rec;
+
+ entry = (uint8_t *)hdr + hdr->header_bytes;
+ table_end = entry + hdr->table_bytes;
+
+ while ((size_t)(table_end - entry) >= sizeof(struct cb_record)) {
+ size_t rec_size;
+
+ rec = (struct cb_record *)entry;
+ rec_size = rec->size;
+
+ if (rec_size < sizeof(struct cb_record))
+ break;
+ if (rec_size > (size_t)(table_end - entry))
+ break;
+
+ switch (rec->tag) {
+ case CB_TAG_VERSION:
+ coreboot_copy_string((struct cb_string *)rec,
+ sc->version, sizeof(sc->version));
+ break;
+
+ case CB_TAG_EXTRA_VERSION:
+ coreboot_copy_string((struct cb_string *)rec,
+ sc->extra_version, sizeof(sc->extra_version));
+ break;
+
+ case CB_TAG_BUILD:
+ coreboot_copy_string((struct cb_string *)rec,
+ sc->build, sizeof(sc->build));
+ break;
+
+ case CB_TAG_COMPILE_TIME:
+ coreboot_copy_string((struct cb_string *)rec,
+ sc->compile_time, sizeof(sc->compile_time));
+ break;
+
+ case CB_TAG_COMPILER:
+ coreboot_copy_string((struct cb_string *)rec,
+ sc->compiler, sizeof(sc->compiler));
+ break;
+
+ case CB_TAG_PLATFORM_BLOB_VERSION:
+ coreboot_copy_string((struct cb_string *)rec,
+ sc->platform_blob_version,
+ sizeof(sc->platform_blob_version));
+ break;
+
+ case CB_TAG_SERIALNO:
+ coreboot_copy_string((struct cb_string *)rec,
+ sc->serialno, sizeof(sc->serialno));
+ break;
+
+ case CB_TAG_VERSION_TIMESTAMP: {
+ struct cb_version_timestamp *ts =
+ (struct cb_version_timestamp *)rec;
+
+ if (rec_size < sizeof(*ts))
+ break;
+ sc->version_timestamp = ts->timestamp;
+ sc->has_version_timestamp = 1;
+ break;
+ }
+
+ case CB_TAG_MAINBOARD:
+ if (rec_size < offsetof(struct cb_mainboard, strings))
+ break;
+ coreboot_parse_mainboard(sc,
+ (struct cb_mainboard *)rec);
+ break;
+
+ case CB_TAG_SERIAL: {
+ struct cb_serial *ser = (struct cb_serial *)rec;
+
+ if (rec_size < sizeof(*ser))
+ break;
+ sc->serial_baseaddr = ser->baseaddr;
+ sc->serial_baud = ser->baud;
+ sc->serial_regwidth = ser->regwidth;
+ sc->has_serial = 1;
+ break;
+ }
+
+ case CB_TAG_TSC_INFO: {
+ struct cb_tsc_info *tsc = (struct cb_tsc_info *)rec;
+
+ if (rec_size < sizeof(*tsc))
+ break;
+ sc->tsc_freq_khz = tsc->freq_khz;
+ sc->has_tsc_info = 1;
+ break;
+ }
+
+ case CB_TAG_PCIE: {
+ struct cb_pcie *pcie = (struct cb_pcie *)rec;
+
+ if (rec_size < sizeof(*pcie))
+ break;
+ sc->pcie_ctrl_base = pcie->ctrl_base;
+ sc->has_pcie = 1;
+ break;
+ }
+
+ case CB_TAG_BOOT_MEDIA_PARAMS: {
+ struct cb_boot_media_params *bmp =
+ (struct cb_boot_media_params *)rec;
+
+ if (rec_size < sizeof(*bmp))
+ break;
+ sc->fmap_offset = bmp->fmap_offset;
+ sc->cbfs_offset = bmp->cbfs_offset;
+ sc->cbfs_size = bmp->cbfs_size;
+ sc->boot_media_size = bmp->boot_media_size;
+ sc->has_boot_media = 1;
+ break;
+ }
+
+ case CB_TAG_MMC_INFO: {
+ struct cb_mmc_info *mmc = (struct cb_mmc_info *)rec;
+
+ if (rec_size < sizeof(*mmc))
+ break;
+ sc->mmc_early_cmd1_status = mmc->early_cmd1_status;
+ sc->has_mmc_info = 1;
+ break;
+ }
+
+ case CB_TAG_CBMEM_CONSOLE: {
+ struct cb_cbmem_ref *ref = (struct cb_cbmem_ref *)rec;
+
+ if (rec_size < sizeof(*ref))
+ break;
+ sc->console_paddr = (vm_paddr_t)ref->cbmem_addr;
+ sc->has_console = 1;
+ break;
+ }
+
+ case CB_TAG_CBMEM_ENTRY: {
+ struct cb_cbmem_entry *ent =
+ (struct cb_cbmem_entry *)rec;
+
+ if (rec_size < sizeof(*ent))
+ break;
+ if (sc->cbmem_count < CB_MAX_CBMEM_ENTRIES) {
+ struct cbmem_entry_info *info =
+ &sc->cbmem_entries[sc->cbmem_count];
+ info->id = ent->id;
+ info->address = ent->address;
+ info->size = ent->entry_size;
+ strlcpy(info->name, cbmem_id_to_name(ent->id),
+ sizeof(info->name));
+ sc->cbmem_count++;
+ }
+ break;
+ }
+
+ case CB_TAG_BOARD_CONFIG: {
+ struct cb_board_config *bc =
+ (struct cb_board_config *)rec;
+
+ if (rec_size < sizeof(*bc))
+ break;
+ sc->fw_config = bc->fw_config;
+ sc->board_id = bc->board_id;
+ sc->ram_code = bc->ram_code;
+ sc->sku_id = bc->sku_id;
+ sc->has_board_config = 1;
+ break;
+ }
+
+ case CB_TAG_MAC_ADDRS: {
+ struct cb_macs *macs = (struct cb_macs *)rec;
+ uint32_t i, count;
+
+ if (rec_size < sizeof(*macs))
+ break;
+ count = macs->count;
+ if (count > CB_MAX_MAC_ADDRS)
+ count = CB_MAX_MAC_ADDRS;
+ if (rec_size < sizeof(*macs) +
+ count * sizeof(struct cb_mac_address))
+ break;
+ for (i = 0; i < count; i++)
+ sc->macs[i] = macs->entries[i];
+ sc->mac_count = count;
+ break;
+ }
+
+ case CB_TAG_ACPI_RSDP: {
+ struct cb_acpi_rsdp *rsdp =
+ (struct cb_acpi_rsdp *)rec;
+
+ if (rec_size < sizeof(*rsdp))
+ break;
+ sc->acpi_rsdp = rsdp->rsdp_pointer;
+ sc->has_acpi_rsdp = 1;
+ break;
+ }
+
+ case CB_TAG_SPI_FLASH: {
+ struct cb_spi_flash *spi =
+ (struct cb_spi_flash *)rec;
+
+ if (rec_size < sizeof(*spi))
+ break;
+ sc->spi_flash_size = spi->flash_size;
+ sc->spi_sector_size = spi->sector_size;
+ sc->spi_erase_cmd = spi->erase_cmd;
+ sc->spi_flags = spi->flags;
+ sc->has_spi_flash = 1;
+ break;
+ }
+
+ case CB_TAG_CONSOLE: {
+ struct cb_console *con = (struct cb_console *)rec;
+
+ if (rec_size < sizeof(*con))
+ break;
+ sc->console_type = con->type;
+ sc->has_console_type = 1;
+ break;
+ }
+
+ case CB_TAG_FRAMEBUFFER: {
+ struct cb_framebuffer *fb =
+ (struct cb_framebuffer *)rec;
+
+ if (rec_size < CB_FRAMEBUFFER_MIN_SIZE)
+ break;
+ sc->fb_addr = fb->physical_address;
+ sc->fb_x_res = fb->x_resolution;
+ sc->fb_y_res = fb->y_resolution;
+ sc->fb_stride = fb->bytes_per_line;
+ sc->fb_bpp = fb->bits_per_pixel;
+ sc->has_framebuffer = 1;
+ break;
+ }
+
+ case CB_TAG_GPIO: {
+ struct cb_gpios *gpios = (struct cb_gpios *)rec;
+ uint32_t i, count;
+
+ if (rec_size < sizeof(*gpios))
+ break;
+ count = gpios->count;
+ if (count > CB_MAX_GPIOS)
+ count = CB_MAX_GPIOS;
+ if (rec_size < sizeof(*gpios) +
+ count * sizeof(struct cb_gpio))
+ break;
+ for (i = 0; i < count; i++)
+ sc->gpios[i] = gpios->entries[i];
+ sc->gpio_count = count;
+ break;
+ }
+
+ case CB_TAG_TPM_PPI_HANDOFF: {
+ struct cb_tpm_ppi *tpm = (struct cb_tpm_ppi *)rec;
+
+ if (rec_size < sizeof(*tpm))
+ break;
+ sc->tpm_ppi_addr = tpm->ppi_address;
+ sc->tpm_version = tpm->tpm_version;
+ sc->has_tpm = 1;
+ break;
+ }
+
+ case CB_TAG_TIMESTAMPS: {
+ struct cb_cbmem_ref *ref = (struct cb_cbmem_ref *)rec;
+
+ if (rec_size < sizeof(*ref))
+ break;
+ sc->timestamps_paddr = (vm_paddr_t)ref->cbmem_addr;
+ sc->has_timestamps = 1;
+ break;
+ }
+
+ case CB_TAG_ACPI_GNVS: {
+ struct cb_cbmem_ref *ref = (struct cb_cbmem_ref *)rec;
+
+ if (rec_size < sizeof(*ref))
+ break;
+ sc->acpi_gnvs_paddr = (vm_paddr_t)ref->cbmem_addr;
+ sc->has_acpi_gnvs = 1;
+ break;
+ }
+
+ case CB_TAG_ACPI_CNVS: {
+ struct cb_cbmem_ref *ref = (struct cb_cbmem_ref *)rec;
+
+ if (rec_size < sizeof(*ref))
+ break;
+ sc->acpi_cnvs_paddr = (vm_paddr_t)ref->cbmem_addr;
+ sc->has_acpi_cnvs = 1;
+ break;
+ }
+
+ case CB_TAG_VPD: {
+ struct cb_cbmem_ref *ref = (struct cb_cbmem_ref *)rec;
+
+ if (rec_size < sizeof(*ref))
+ break;
+ sc->vpd_paddr = (vm_paddr_t)ref->cbmem_addr;
+ sc->has_vpd = 1;
+ break;
+ }
+
+ case CB_TAG_WIFI_CALIBRATION: {
+ struct cb_cbmem_ref *ref = (struct cb_cbmem_ref *)rec;
+
+ if (rec_size < sizeof(*ref))
+ break;
+ sc->wifi_cal_paddr = (vm_paddr_t)ref->cbmem_addr;
+ sc->has_wifi_cal = 1;
+ break;
+ }
+
+ case CB_TAG_FMAP: {
+ struct cb_cbmem_ref *ref = (struct cb_cbmem_ref *)rec;
+
+ if (rec_size < sizeof(*ref))
+ break;
+ sc->fmap_paddr = (vm_paddr_t)ref->cbmem_addr;
+ sc->has_fmap = 1;
+ break;
+ }
+
+ case CB_TAG_VBOOT_WORKBUF: {
+ struct cb_cbmem_ref *ref = (struct cb_cbmem_ref *)rec;
+
+ if (rec_size < sizeof(*ref))
+ break;
+ sc->vboot_workbuf_paddr = (vm_paddr_t)ref->cbmem_addr;
+ sc->has_vboot_workbuf = 1;
+ break;
+ }
+
+ case CB_TAG_TYPE_C_INFO: {
+ struct cb_cbmem_ref *ref = (struct cb_cbmem_ref *)rec;
+
+ if (rec_size < sizeof(*ref))
+ break;
+ sc->type_c_info_paddr = (vm_paddr_t)ref->cbmem_addr;
+ sc->has_type_c_info = 1;
+ break;
+ }
+
+ case CB_TAG_ROOT_BRIDGE_INFO: {
+ struct cb_cbmem_ref *ref = (struct cb_cbmem_ref *)rec;
+
+ if (rec_size < sizeof(*ref))
+ break;
+ sc->root_bridge_info_paddr =
+ (vm_paddr_t)ref->cbmem_addr;
+ sc->has_root_bridge_info = 1;
+ break;
+ }
+
+ case CB_TAG_TPM_CB_LOG: {
+ struct cb_cbmem_ref *ref = (struct cb_cbmem_ref *)rec;
+
+ if (rec_size < sizeof(*ref))
+ break;
+ sc->tpm_log_paddr = (vm_paddr_t)ref->cbmem_addr;
+ sc->has_tpm_log = 1;
+ break;
+ }
+
+ case CB_TAG_SMMSTOREV2: {
+ struct cb_smmstorev2 *smm =
+ (struct cb_smmstorev2 *)rec;
+
+ if (rec_size < CB_SMMSTOREV2_BASE_SIZE)
+ break;
+ sc->smmstore_num_blocks = smm->num_blocks;
+ sc->smmstore_block_size = smm->block_size;
+ sc->smmstore_com_buffer = smm->com_buffer;
+ sc->smmstore_apm_cmd = smm->apm_cmd;
+ /* 64-bit mmap_addr only present in newer coreboot */
+ if (rec_size >= sizeof(*smm))
+ sc->smmstore_mmap_addr = smm->mmap_addr;
+ else
+ sc->smmstore_mmap_addr =
+ (uint64_t)smm->mmap_addr_lo;
+ sc->has_smmstore = 1;
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ entry += rec_size;
+ }
+}
+
+/*
+ * Register the sysctl tree under hw.coreboot.*
+ */
+static void
+coreboot_register_sysctls(struct coreboot_softc *sc)
+{
+ struct sysctl_oid *oid_mb, *oid_serial, *oid_cbmem;
+ struct sysctl_oid *oid_entry;
+ struct sysctl_oid *oid_board, *oid_spi, *oid_fb, *oid_gpio;
+ struct sysctl_oid *oid_boot, *oid_cbrefs;
+ struct sysctl_oid *oid_tpm, *oid_smmstore;
+ char numstr[8];
+ uint32_t i;
+
+ sysctl_ctx_init(&sc->sysctl_ctx);
+ sc->sysctl_tree = SYSCTL_ADD_NODE(&sc->sysctl_ctx,
+ SYSCTL_STATIC_CHILDREN(_hw), OID_AUTO, "coreboot",
+ CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "coreboot firmware information");
+
+ if (sc->sysctl_tree == NULL)
+ return;
+
+ SYSCTL_ADD_INT(&sc->sysctl_ctx,
+ SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, "debug",
+ CTLFLAG_RW, &coreboot_debug, 0,
+ "Enable verbose coreboot diagnostics");
+
+ if (sc->version[0] != '\0')
+ SYSCTL_ADD_STRING(&sc->sysctl_ctx,
+ SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, "version",
+ CTLFLAG_RD, sc->version, 0, "Firmware version");
+
+ if (sc->build[0] != '\0')
+ SYSCTL_ADD_STRING(&sc->sysctl_ctx,
+ SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, "build",
+ CTLFLAG_RD, sc->build, 0, "Build date");
+
+ if (sc->compile_time[0] != '\0')
+ SYSCTL_ADD_STRING(&sc->sysctl_ctx,
+ SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, "compile_time",
+ CTLFLAG_RD, sc->compile_time, 0, "Firmware compile time");
+
+ if (sc->compiler[0] != '\0')
+ SYSCTL_ADD_STRING(&sc->sysctl_ctx,
+ SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, "compiler",
+ CTLFLAG_RD, sc->compiler, 0, "Compiler info");
+
+ if (sc->extra_version[0] != '\0')
+ SYSCTL_ADD_STRING(&sc->sysctl_ctx,
+ SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO,
+ "extra_version", CTLFLAG_RD, sc->extra_version, 0,
+ "Extra version info");
+
+ if (sc->serialno[0] != '\0')
+ SYSCTL_ADD_STRING(&sc->sysctl_ctx,
+ SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, "serialno",
+ CTLFLAG_RD, sc->serialno, 0, "Serial number");
+
+ if (sc->platform_blob_version[0] != '\0')
+ SYSCTL_ADD_STRING(&sc->sysctl_ctx,
+ SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO,
+ "platform_blob_version", CTLFLAG_RD,
+ sc->platform_blob_version, 0,
+ "Platform blob version");
+
+ if (sc->has_version_timestamp)
+ SYSCTL_ADD_U32(&sc->sysctl_ctx,
+ SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO,
+ "version_timestamp", CTLFLAG_RD, &sc->version_timestamp, 0,
+ "Firmware version timestamp");
+
+ SYSCTL_ADD_U64(&sc->sysctl_ctx, SYSCTL_CHILDREN(sc->sysctl_tree),
+ OID_AUTO, "table_addr", CTLFLAG_RD,
+ (uint64_t *)&sc->table_paddr, 0,
+ "Physical address of coreboot table");
+
+ SYSCTL_ADD_ULONG(&sc->sysctl_ctx, SYSCTL_CHILDREN(sc->sysctl_tree),
+ OID_AUTO, "table_size", CTLFLAG_RD,
+ (unsigned long *)&sc->table_size,
+ "Total coreboot table size");
+
+ if (sc->mb_vendor[0] != '\0' || sc->mb_part[0] != '\0') {
+ oid_mb = SYSCTL_ADD_NODE(&sc->sysctl_ctx,
+ SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, "mainboard",
+ CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "Mainboard information");
+
+ if (sc->mb_vendor[0] != '\0')
+ SYSCTL_ADD_STRING(&sc->sysctl_ctx,
+ SYSCTL_CHILDREN(oid_mb), OID_AUTO, "vendor",
+ CTLFLAG_RD, sc->mb_vendor, 0, "Board vendor");
+
+ if (sc->mb_part[0] != '\0')
+ SYSCTL_ADD_STRING(&sc->sysctl_ctx,
+ SYSCTL_CHILDREN(oid_mb), OID_AUTO, "part",
+ CTLFLAG_RD, sc->mb_part, 0, "Board part number");
+ }
+
+ if (sc->has_serial) {
+ oid_serial = SYSCTL_ADD_NODE(&sc->sysctl_ctx,
+ SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, "serial",
+ CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "Serial port");
+
+ SYSCTL_ADD_U32(&sc->sysctl_ctx, SYSCTL_CHILDREN(oid_serial),
+ OID_AUTO, "baseaddr", CTLFLAG_RD, &sc->serial_baseaddr, 0,
+ "Base address");
+
+ SYSCTL_ADD_U32(&sc->sysctl_ctx, SYSCTL_CHILDREN(oid_serial),
+ OID_AUTO, "baud", CTLFLAG_RD, &sc->serial_baud, 0,
+ "Baud rate");
+
+ SYSCTL_ADD_U32(&sc->sysctl_ctx, SYSCTL_CHILDREN(oid_serial),
+ OID_AUTO, "regwidth", CTLFLAG_RD, &sc->serial_regwidth, 0,
+ "Register width");
+ }
+
+ if (sc->has_tsc_info)
+ SYSCTL_ADD_U32(&sc->sysctl_ctx,
+ SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, "tsc_freq_khz",
+ CTLFLAG_RD, &sc->tsc_freq_khz, 0,
+ "TSC frequency in kHz");
+
+ if (sc->has_pcie)
+ SYSCTL_ADD_U64(&sc->sysctl_ctx,
+ SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO,
+ "pcie_ctrl_base", CTLFLAG_RD, &sc->pcie_ctrl_base, 0,
+ "PCIe controller base address");
+
+ if (sc->cbmem_count > 0) {
+ oid_cbmem = SYSCTL_ADD_NODE(&sc->sysctl_ctx,
+ SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, "cbmem",
+ CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "CBMEM entries");
+
+ for (i = 0; i < sc->cbmem_count; i++) {
+ struct cbmem_entry_info *info = &sc->cbmem_entries[i];
+
+ snprintf(numstr, sizeof(numstr), "%u", i);
+ oid_entry = SYSCTL_ADD_NODE(&sc->sysctl_ctx,
+ SYSCTL_CHILDREN(oid_cbmem), OID_AUTO, numstr,
+ CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
+ "CBMEM entry");
+
+ SYSCTL_ADD_STRING(&sc->sysctl_ctx,
+ SYSCTL_CHILDREN(oid_entry), OID_AUTO, "name",
+ CTLFLAG_RD, info->name, 0, "Entry name");
+
+ SYSCTL_ADD_U32(&sc->sysctl_ctx,
+ SYSCTL_CHILDREN(oid_entry), OID_AUTO, "id",
+ CTLFLAG_RD, &info->id, 0, "Entry ID (hex)");
+
+ SYSCTL_ADD_U64(&sc->sysctl_ctx,
+ SYSCTL_CHILDREN(oid_entry), OID_AUTO, "address",
+ CTLFLAG_RD, (uint64_t *)&info->address, 0,
+ "Physical address");
+
+ SYSCTL_ADD_U32(&sc->sysctl_ctx,
+ SYSCTL_CHILDREN(oid_entry), OID_AUTO, "size",
+ CTLFLAG_RD, &info->size, 0, "Entry size");
+ }
+ }
+
+ if (sc->has_board_config) {
+ oid_board = SYSCTL_ADD_NODE(&sc->sysctl_ctx,
+ SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, "board",
+ CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
+ "Board identification");
+
+ SYSCTL_ADD_U64(&sc->sysctl_ctx,
+ SYSCTL_CHILDREN(oid_board), OID_AUTO, "fw_config",
+ CTLFLAG_RD, &sc->fw_config, 0,
+ "Firmware configuration bitmask");
+
+ SYSCTL_ADD_U32(&sc->sysctl_ctx,
+ SYSCTL_CHILDREN(oid_board), OID_AUTO, "board_id",
+ CTLFLAG_RD, &sc->board_id, 0,
+ "Board ID");
+
+ SYSCTL_ADD_U32(&sc->sysctl_ctx,
+ SYSCTL_CHILDREN(oid_board), OID_AUTO, "ram_code",
+ CTLFLAG_RD, &sc->ram_code, 0,
+ "RAM code");
+
+ SYSCTL_ADD_U32(&sc->sysctl_ctx,
+ SYSCTL_CHILDREN(oid_board), OID_AUTO, "sku_id",
+ CTLFLAG_RD, &sc->sku_id, 0,
+ "SKU ID");
+ }
+
+ if (sc->mac_count > 0) {
+ struct sysctl_oid *oid_mac;
+
+ oid_mac = SYSCTL_ADD_NODE(&sc->sysctl_ctx,
+ SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, "mac",
+ CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
+ "Factory MAC addresses");
+
+ for (i = 0; i < sc->mac_count; i++) {
+ uint8_t *m = sc->macs[i].mac_addr;
+
+ snprintf(numstr, sizeof(numstr), "%u", i);
+ snprintf(sc->mac_strs[i], sizeof(sc->mac_strs[i]),
+ "%02x:%02x:%02x:%02x:%02x:%02x",
+ m[0], m[1], m[2], m[3], m[4], m[5]);
+
+ SYSCTL_ADD_STRING(&sc->sysctl_ctx,
+ SYSCTL_CHILDREN(oid_mac), OID_AUTO, numstr,
+ CTLFLAG_RD, sc->mac_strs[i], 0,
+ "MAC address");
+ }
+ }
+
+ if (sc->has_acpi_rsdp)
+ SYSCTL_ADD_U64(&sc->sysctl_ctx,
+ SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, "acpi_rsdp",
+ CTLFLAG_RD, &sc->acpi_rsdp, 0,
+ "ACPI RSDP physical address");
+
+ if (sc->has_boot_media) {
+ oid_boot = SYSCTL_ADD_NODE(&sc->sysctl_ctx,
+ SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, "boot_media",
+ CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
+ "Boot media parameters");
+
+ SYSCTL_ADD_U64(&sc->sysctl_ctx,
+ SYSCTL_CHILDREN(oid_boot), OID_AUTO, "fmap_offset",
+ CTLFLAG_RD, &sc->fmap_offset, 0,
+ "FMAP offset from boot media start");
+
+ SYSCTL_ADD_U64(&sc->sysctl_ctx,
+ SYSCTL_CHILDREN(oid_boot), OID_AUTO, "cbfs_offset",
+ CTLFLAG_RD, &sc->cbfs_offset, 0,
+ "CBFS offset from boot media start");
+
+ SYSCTL_ADD_U64(&sc->sysctl_ctx,
+ SYSCTL_CHILDREN(oid_boot), OID_AUTO, "cbfs_size",
+ CTLFLAG_RD, &sc->cbfs_size, 0,
+ "CBFS size in bytes");
+
+ SYSCTL_ADD_U64(&sc->sysctl_ctx,
+ SYSCTL_CHILDREN(oid_boot), OID_AUTO, "size",
+ CTLFLAG_RD, &sc->boot_media_size, 0,
+ "Boot media size in bytes");
+ }
+
+ if (sc->has_mmc_info)
+ SYSCTL_ADD_S32(&sc->sysctl_ctx,
+ SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO,
+ "mmc_early_cmd1_status", CTLFLAG_RD,
+ &sc->mmc_early_cmd1_status, 0,
+ "Early eMMC CMD1 status");
+
+ if (sc->has_spi_flash) {
+ oid_spi = SYSCTL_ADD_NODE(&sc->sysctl_ctx,
+ SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, "spi_flash",
+ CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
+ "SPI flash parameters");
+
+ SYSCTL_ADD_U32(&sc->sysctl_ctx,
+ SYSCTL_CHILDREN(oid_spi), OID_AUTO, "size",
+ CTLFLAG_RD, &sc->spi_flash_size, 0,
+ "Flash size in bytes");
+
+ SYSCTL_ADD_U32(&sc->sysctl_ctx,
+ SYSCTL_CHILDREN(oid_spi), OID_AUTO, "sector_size",
+ CTLFLAG_RD, &sc->spi_sector_size, 0,
+ "Sector size in bytes");
+
+ SYSCTL_ADD_U8(&sc->sysctl_ctx,
+ SYSCTL_CHILDREN(oid_spi), OID_AUTO, "erase_cmd",
+ CTLFLAG_RD, &sc->spi_erase_cmd, 0,
+ "Erase command byte");
+ }
+
+ if (sc->has_console_type)
+ SYSCTL_ADD_U16(&sc->sysctl_ctx,
+ SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO,
+ "console_type", CTLFLAG_RD, &sc->console_type, 0,
+ "Firmware console type");
+
+ if (sc->has_framebuffer) {
+ oid_fb = SYSCTL_ADD_NODE(&sc->sysctl_ctx,
+ SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO,
+ "framebuffer",
+ CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
+ "Framebuffer information");
+
+ SYSCTL_ADD_U64(&sc->sysctl_ctx,
+ SYSCTL_CHILDREN(oid_fb), OID_AUTO, "addr",
+ CTLFLAG_RD, &sc->fb_addr, 0,
+ "Physical address");
+
+ SYSCTL_ADD_U32(&sc->sysctl_ctx,
+ SYSCTL_CHILDREN(oid_fb), OID_AUTO, "x_res",
+ CTLFLAG_RD, &sc->fb_x_res, 0,
+ "Horizontal resolution");
+
+ SYSCTL_ADD_U32(&sc->sysctl_ctx,
+ SYSCTL_CHILDREN(oid_fb), OID_AUTO, "y_res",
+ CTLFLAG_RD, &sc->fb_y_res, 0,
+ "Vertical resolution");
+
+ SYSCTL_ADD_U8(&sc->sysctl_ctx,
+ SYSCTL_CHILDREN(oid_fb), OID_AUTO, "bpp",
+ CTLFLAG_RD, &sc->fb_bpp, 0,
+ "Bits per pixel");
+ }
+
+ if (sc->gpio_count > 0) {
+ struct sysctl_oid *oid_pin;
+
+ oid_gpio = SYSCTL_ADD_NODE(&sc->sysctl_ctx,
+ SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, "gpio",
+ CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
+ "GPIO pin states");
+
+ for (i = 0; i < sc->gpio_count; i++) {
+ struct cb_gpio *g = &sc->gpios[i];
+
+ snprintf(numstr, sizeof(numstr), "%u", i);
+ oid_pin = SYSCTL_ADD_NODE(&sc->sysctl_ctx,
+ SYSCTL_CHILDREN(oid_gpio), OID_AUTO, numstr,
+ CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
+ "GPIO pin");
+
+ /* Ensure name is NUL-terminated */
+ g->name[sizeof(g->name) - 1] = '\0';
+ SYSCTL_ADD_STRING(&sc->sysctl_ctx,
+ SYSCTL_CHILDREN(oid_pin), OID_AUTO, "name",
+ CTLFLAG_RD, g->name, 0, "Pin name");
+
+ SYSCTL_ADD_U32(&sc->sysctl_ctx,
+ SYSCTL_CHILDREN(oid_pin), OID_AUTO, "port",
+ CTLFLAG_RD, &g->port, 0, "Port number");
+
+ SYSCTL_ADD_U32(&sc->sysctl_ctx,
+ SYSCTL_CHILDREN(oid_pin), OID_AUTO, "value",
+ CTLFLAG_RD, &g->value, 0, "Pin value");
+
+ SYSCTL_ADD_U32(&sc->sysctl_ctx,
+ SYSCTL_CHILDREN(oid_pin), OID_AUTO, "polarity",
+ CTLFLAG_RD, &g->polarity, 0, "Pin polarity");
+ }
+ }
+
+ if (sc->has_tpm) {
+ oid_tpm = SYSCTL_ADD_NODE(&sc->sysctl_ctx,
+ SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, "tpm",
+ CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
+ "TPM information");
+
+ SYSCTL_ADD_U8(&sc->sysctl_ctx,
+ SYSCTL_CHILDREN(oid_tpm), OID_AUTO, "version",
+ CTLFLAG_RD, &sc->tpm_version, 0,
+ "TPM version (1=1.2, 2=2.0)");
+
+ SYSCTL_ADD_U32(&sc->sysctl_ctx,
+ SYSCTL_CHILDREN(oid_tpm), OID_AUTO, "ppi_addr",
+ CTLFLAG_RD, &sc->tpm_ppi_addr, 0,
+ "PPI address");
+
+ if (sc->has_tpm_log)
+ SYSCTL_ADD_U64(&sc->sysctl_ctx,
+ SYSCTL_CHILDREN(oid_tpm), OID_AUTO, "cblog_addr",
+ CTLFLAG_RD, (uint64_t *)&sc->tpm_log_paddr, 0,
+ "TPM event log physical address");
+ } else if (sc->has_tpm_log) {
+ /* TPM log without PPI - create tpm node just for the log */
+ oid_tpm = SYSCTL_ADD_NODE(&sc->sysctl_ctx,
+ SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, "tpm",
+ CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
+ "TPM information");
+
+ SYSCTL_ADD_U64(&sc->sysctl_ctx,
+ SYSCTL_CHILDREN(oid_tpm), OID_AUTO, "cblog_addr",
+ CTLFLAG_RD, (uint64_t *)&sc->tpm_log_paddr, 0,
+ "TPM event log physical address");
+ }
+
+ if (sc->has_smmstore) {
+ oid_smmstore = SYSCTL_ADD_NODE(&sc->sysctl_ctx,
+ SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, "smmstore",
+ CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
+ "SMMSTORE v2 configuration");
+
+ SYSCTL_ADD_U32(&sc->sysctl_ctx,
+ SYSCTL_CHILDREN(oid_smmstore), OID_AUTO, "num_blocks",
+ CTLFLAG_RD, &sc->smmstore_num_blocks, 0,
+ "Number of blocks");
+
+ SYSCTL_ADD_U32(&sc->sysctl_ctx,
+ SYSCTL_CHILDREN(oid_smmstore), OID_AUTO, "block_size",
+ CTLFLAG_RD, &sc->smmstore_block_size, 0,
+ "Block size in bytes");
+
+ SYSCTL_ADD_U64(&sc->sysctl_ctx,
+ SYSCTL_CHILDREN(oid_smmstore), OID_AUTO, "mmap_addr",
+ CTLFLAG_RD, &sc->smmstore_mmap_addr, 0,
+ "Memory-mapped address");
+
+ SYSCTL_ADD_U32(&sc->sysctl_ctx,
+ SYSCTL_CHILDREN(oid_smmstore), OID_AUTO, "com_buffer",
+ CTLFLAG_RD, &sc->smmstore_com_buffer, 0,
+ "Communication buffer address");
+
+ SYSCTL_ADD_U8(&sc->sysctl_ctx,
+ SYSCTL_CHILDREN(oid_smmstore), OID_AUTO, "apm_cmd",
+ CTLFLAG_RD, &sc->smmstore_apm_cmd, 0,
+ "APM command byte");
+ }
+
+ if (sc->has_acpi_gnvs || sc->has_acpi_cnvs || sc->has_vpd ||
+ sc->has_wifi_cal || sc->has_fmap || sc->has_vboot_workbuf ||
+ sc->has_type_c_info || sc->has_root_bridge_info) {
+ oid_cbrefs = SYSCTL_ADD_NODE(&sc->sysctl_ctx,
+ SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, "cbmem_refs",
+ CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
+ "Additional CBMEM reference addresses");
+
+ if (sc->has_acpi_gnvs)
+ SYSCTL_ADD_U64(&sc->sysctl_ctx,
+ SYSCTL_CHILDREN(oid_cbrefs), OID_AUTO, "acpi_gnvs",
+ CTLFLAG_RD, (uint64_t *)&sc->acpi_gnvs_paddr, 0,
+ "ACPI GNVS CBMEM physical address");
+ if (sc->has_acpi_cnvs)
+ SYSCTL_ADD_U64(&sc->sysctl_ctx,
+ SYSCTL_CHILDREN(oid_cbrefs), OID_AUTO, "acpi_cnvs",
+ CTLFLAG_RD, (uint64_t *)&sc->acpi_cnvs_paddr, 0,
+ "ACPI CNVS CBMEM physical address");
+ if (sc->has_vpd)
+ SYSCTL_ADD_U64(&sc->sysctl_ctx,
+ SYSCTL_CHILDREN(oid_cbrefs), OID_AUTO, "vpd",
+ CTLFLAG_RD, (uint64_t *)&sc->vpd_paddr, 0,
+ "VPD CBMEM physical address");
+ if (sc->has_wifi_cal)
+ SYSCTL_ADD_U64(&sc->sysctl_ctx,
+ SYSCTL_CHILDREN(oid_cbrefs), OID_AUTO,
+ "wifi_calibration", CTLFLAG_RD,
+ (uint64_t *)&sc->wifi_cal_paddr, 0,
+ "WiFi calibration CBMEM physical address");
+ if (sc->has_fmap)
+ SYSCTL_ADD_U64(&sc->sysctl_ctx,
+ SYSCTL_CHILDREN(oid_cbrefs), OID_AUTO, "fmap",
+ CTLFLAG_RD, (uint64_t *)&sc->fmap_paddr, 0,
+ "FMAP CBMEM physical address");
+ if (sc->has_vboot_workbuf)
+ SYSCTL_ADD_U64(&sc->sysctl_ctx,
+ SYSCTL_CHILDREN(oid_cbrefs), OID_AUTO,
+ "vboot_workbuf", CTLFLAG_RD,
+ (uint64_t *)&sc->vboot_workbuf_paddr, 0,
+ "Vboot work buffer CBMEM physical address");
+ if (sc->has_type_c_info)
+ SYSCTL_ADD_U64(&sc->sysctl_ctx,
+ SYSCTL_CHILDREN(oid_cbrefs), OID_AUTO,
+ "type_c_info", CTLFLAG_RD,
+ (uint64_t *)&sc->type_c_info_paddr, 0,
+ "Type-C info CBMEM physical address");
+ if (sc->has_root_bridge_info)
+ SYSCTL_ADD_U64(&sc->sysctl_ctx,
+ SYSCTL_CHILDREN(oid_cbrefs), OID_AUTO,
+ "root_bridge_info", CTLFLAG_RD,
+ (uint64_t *)&sc->root_bridge_info_paddr, 0,
+ "Root bridge info CBMEM physical address");
+ }
+
+ if (sc->has_timestamps) {
+ SYSCTL_ADD_U64(&sc->sysctl_ctx,
+ SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO,
+ "timestamps_addr", CTLFLAG_RD,
+ (uint64_t *)&sc->timestamps_paddr, 0,
+ "Timestamps CBMEM physical address");
+
+ coreboot_timestamps_register(sc, sc->sysctl_tree);
+ }
+}
+
+/*
+ * Map and validate a coreboot table at physical address pa.
+ * On success, *vap points to the mapped table and *sizep is the total size.
+ * The caller must pmap_unmapbios(*vap, *sizep) when done.
+ */
+static int
+coreboot_map_table(vm_paddr_t pa, void **vap, vm_size_t *sizep)
+{
+ struct cb_header *hdr;
+ void *va;
+ vm_size_t map_size;
+ int error;
+
+ va = pmap_mapbios(pa, sizeof(struct cb_header));
+ if (va == NULL)
+ return (ENOMEM);
+
+ hdr = (struct cb_header *)va;
+ if (memcmp(hdr->signature, CB_HEADER_SIGNATURE,
+ CB_HEADER_SIG_LEN) != 0) {
+ pmap_unmapbios(va, sizeof(struct cb_header));
+ return (ENXIO);
+ }
+
+ error = coreboot_sanitize_header(hdr, &map_size);
+ pmap_unmapbios(va, sizeof(struct cb_header));
+ if (error != 0)
+ return (ENXIO);
+
+ va = pmap_mapbios(pa, map_size);
+ if (va == NULL)
+ return (ENOMEM);
+
+ hdr = (struct cb_header *)va;
+ if (coreboot_validate_header(hdr, map_size) != 0 ||
+ coreboot_validate_table(hdr, map_size) != 0) {
+ pmap_unmapbios(va, map_size);
+ return (ENXIO);
+ }
+
+ *vap = va;
+ *sizep = map_size;
+ return (0);
+}
+
+/*
+ * Identify: scan low memory for "LBIO" signature and register a child
+ */
+static void
+coreboot_identify(driver_t *driver, device_t parent)
+{
+ vm_paddr_t low_addr, real_addr;
+ struct cb_header *hdr;
+ uint8_t *entry, *table_end;
+ struct cb_record *rec;
+ device_t child;
+ void *va;
+ vm_size_t map_size;
+ int error;
+
+ if (!device_is_alive(parent))
+ return;
+
+ if (device_find_child(parent, "coreboot", -1) != NULL)
+ return;
+
+ low_addr = coreboot_scan_region(CB_SCAN_LOW_START, CB_SCAN_LOW_END);
+ if (low_addr == 0)
+ return;
+
+ va = pmap_mapbios(low_addr, sizeof(struct cb_header));
+ if (va == NULL)
+ return;
+
+ /* Scan already verified the signature; re-read to get sizes. */
+ hdr = (struct cb_header *)va;
+ error = coreboot_sanitize_header(hdr, &map_size);
+ pmap_unmapbios(va, sizeof(struct cb_header));
+ if (error != 0)
+ return;
+
+ va = pmap_mapbios(low_addr, map_size);
+ if (va == NULL)
+ return;
+
+ hdr = (struct cb_header *)va;
+ if (coreboot_validate_header(hdr, map_size) != 0 ||
+ coreboot_validate_table(hdr, map_size) != 0) {
+ pmap_unmapbios(va, map_size);
+ return;
+ }
+
+ /* Look for CB_TAG_FORWARD to find the real table in high memory */
+ real_addr = low_addr;
+ entry = (uint8_t *)hdr + hdr->header_bytes;
+ table_end = entry + hdr->table_bytes;
+ while ((size_t)(table_end - entry) >= sizeof(struct cb_record)) {
+ size_t rec_size;
+
+ rec = (struct cb_record *)entry;
+ rec_size = rec->size;
+ if (rec_size < sizeof(struct cb_record))
+ break;
+ if (rec_size > (size_t)(table_end - entry))
+ break;
+ if (rec->tag == CB_TAG_FORWARD) {
+ if (rec_size >= sizeof(struct cb_forward)) {
+ struct cb_forward *fwd;
+
+ fwd = (struct cb_forward *)entry;
+ real_addr = (vm_paddr_t)fwd->forward;
+ }
+ break;
+ }
+ entry += rec_size;
+ }
+ pmap_unmapbios(va, map_size);
+
+ child = BUS_ADD_CHILD(parent, 5, "coreboot", DEVICE_UNIT_ANY);
+ if (child == NULL)
+ return;
+ device_set_driver(child, driver);
+
+ bus_set_resource(child, SYS_RES_MEMORY, 0, real_addr, PAGE_SIZE);
+ device_set_desc(child, "coreboot firmware table");
+}
+
+/*
+ * Probe: validate the coreboot header at the discovered address
+ */
+static int
+coreboot_probe(device_t dev)
+{
+ vm_paddr_t pa;
+ void *va;
+ vm_size_t map_size;
+ int error;
+
+ pa = bus_get_resource_start(dev, SYS_RES_MEMORY, 0);
+ if (pa == 0)
+ return (ENXIO);
+
+ error = coreboot_map_table(pa, &va, &map_size);
+ if (error != 0)
+ return (error);
+
+ pmap_unmapbios(va, map_size);
+ return (BUS_PROBE_SPECIFIC);
+}
+
+/*
+ * Attach: map the full table, parse records, register sysctls and cdevs
+ */
+static int
+coreboot_attach(device_t dev)
+{
+ struct coreboot_softc *sc;
+ struct cb_header *hdr;
+ vm_paddr_t pa;
+ void *va;
+ vm_size_t map_size;
+ int error;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+
+ pa = bus_get_resource_start(dev, SYS_RES_MEMORY, 0);
+
+ error = coreboot_map_table(pa, &va, &map_size);
+ if (error != 0) {
+ device_printf(dev, "coreboot table validation failed at %#jx\n",
+ (uintmax_t)pa);
+ return (error);
+ }
+
+ sc->table_paddr = pa;
+ sc->table_size = map_size;
+ sc->table_vaddr = va;
+
+ hdr = (struct cb_header *)va;
+ device_printf(dev, "coreboot table at %#jx (%u entries, %u bytes)\n",
+ (uintmax_t)pa, hdr->table_entries, hdr->table_bytes);
+
+ coreboot_parse_table(sc, hdr);
+
+ if (sc->version[0] != '\0')
+ device_printf(dev, "firmware: %s\n", sc->version);
+ if (sc->mb_vendor[0] != '\0')
+ device_printf(dev, "mainboard: %s %s\n", sc->mb_vendor,
+ sc->mb_part);
+ if (sc->has_console)
+ device_printf(dev, "CBMEM console at %#jx\n",
+ (uintmax_t)sc->console_paddr);
+ device_printf(dev, "CBMEM entries: %u\n", sc->cbmem_count);
+ if (sc->has_board_config)
+ device_printf(dev,
+ "board: id=%u sku=%u fw_config=%#jx\n",
+ sc->board_id, sc->sku_id,
+ (uintmax_t)sc->fw_config);
+ if (sc->mac_count > 0)
+ device_printf(dev, "factory MAC addresses: %u\n",
+ sc->mac_count);
+ if (sc->has_acpi_rsdp)
+ device_printf(dev, "ACPI RSDP at %#jx\n",
+ (uintmax_t)sc->acpi_rsdp);
+ if (sc->has_pcie)
+ device_printf(dev, "PCIe controller at %#jx\n",
+ (uintmax_t)sc->pcie_ctrl_base);
+ if (sc->has_boot_media)
+ device_printf(dev, "boot media: %#jx bytes, CBFS %#jx+%#jx\n",
+ (uintmax_t)sc->boot_media_size,
+ (uintmax_t)sc->cbfs_offset, (uintmax_t)sc->cbfs_size);
+ if (sc->has_mmc_info)
+ device_printf(dev, "MMC early CMD1 status: %d\n",
+ sc->mmc_early_cmd1_status);
+
+ if (bootverbose) {
+ if (sc->has_spi_flash)
+ device_printf(dev,
+ "SPI flash: %u bytes, sector %u, erase %#x\n",
+ sc->spi_flash_size, sc->spi_sector_size,
+ sc->spi_erase_cmd);
+ if (sc->has_console_type)
+ device_printf(dev, "console type: %u\n",
+ sc->console_type);
+ if (sc->has_framebuffer)
+ device_printf(dev,
+ "framebuffer: %ux%u@%ubpp at %#jx\n",
+ sc->fb_x_res, sc->fb_y_res, sc->fb_bpp,
+ (uintmax_t)sc->fb_addr);
+ if (sc->gpio_count > 0)
+ device_printf(dev, "GPIO pins: %u\n",
+ sc->gpio_count);
+ if (sc->has_tpm)
+ device_printf(dev, "TPM %u.%u PPI at %#x\n",
+ sc->tpm_version == 2 ? 2 : 1,
+ sc->tpm_version == 2 ? 0 : 2,
+ sc->tpm_ppi_addr);
+ }
+
+ if (coreboot_debug) {
+ if (sc->has_smmstore)
+ device_printf(dev,
+ "SMMSTORE v2: %u blocks x %u bytes, "
+ "apm_cmd=%#x\n",
+ sc->smmstore_num_blocks,
+ sc->smmstore_block_size,
+ sc->smmstore_apm_cmd);
+ if (sc->has_timestamps)
+ device_printf(dev, "timestamps at %#jx\n",
+ (uintmax_t)sc->timestamps_paddr);
+ if (sc->has_tpm_log)
+ device_printf(dev, "TPM CB log at %#jx\n",
+ (uintmax_t)sc->tpm_log_paddr);
+ if (sc->has_fmap)
+ device_printf(dev, "FMAP at %#jx\n",
+ (uintmax_t)sc->fmap_paddr);
+ }
+
+ coreboot_register_sysctls(sc);
+
+ coreboot_sc = sc;
+
+ if (sc->has_console) {
+ error = coreboot_console_create(sc);
+ if (error != 0)
+ device_printf(dev,
+ "failed to create /dev/coreboot_console (%d)\n",
+ error);
+ }
+ if (sc->cbmem_count > 0) {
+ error = coreboot_cbmem_create(sc);
+ if (error != 0)
+ device_printf(dev, "failed to create /dev/cbmem (%d)\n",
+ error);
+ }
+
+ return (0);
+}
+
+/*
+ * Detach: unmap table, destroy cdevs and sysctls
+ */
+static int
+coreboot_detach(device_t dev)
+{
+ struct coreboot_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ coreboot_cbmem_destroy(sc);
+ coreboot_console_destroy(sc);
+
+ coreboot_sc = NULL;
+
+ sysctl_ctx_free(&sc->sysctl_ctx);
+
+ if (sc->table_vaddr != NULL) {
+ pmap_unmapbios(sc->table_vaddr, sc->table_size);
+ sc->table_vaddr = NULL;
+ }
+
+ if (sc->console_vaddr != NULL) {
+ pmap_unmapbios(sc->console_vaddr, sc->console_size);
+ sc->console_vaddr = NULL;
+ sc->console_size = 0;
+ sc->console_data_size = 0;
+ }
+
+ return (0);
+}
+
+static int
+coreboot_modevent(module_t mod, int what, void *arg)
+{
+ device_t *devs;
+ int count, i;
+
+ switch (what) {
+ case MOD_LOAD:
+ break;
+ case MOD_UNLOAD:
+ devclass_get_devices(devclass_find("coreboot"), &devs, &count);
+ for (i = 0; i < count; i++)
+ device_delete_child(device_get_parent(devs[i]),
+ devs[i]);
+ free(devs, M_TEMP);
+ break;
+ default:
+ break;
+ }
+
+ return (0);
+}
+
+static device_method_t coreboot_methods[] = {
+ DEVMETHOD(device_identify, coreboot_identify),
+ DEVMETHOD(device_probe, coreboot_probe),
+ DEVMETHOD(device_attach, coreboot_attach),
+ DEVMETHOD(device_detach, coreboot_detach),
+ DEVMETHOD_END
+};
+
+static driver_t coreboot_driver = {
+ "coreboot",
+ coreboot_methods,
+ sizeof(struct coreboot_softc),
+};
+
+DRIVER_MODULE(coreboot, nexus, coreboot_driver, coreboot_modevent, NULL);
+MODULE_VERSION(coreboot, 1);
diff --git a/sys/dev/coreboot/coreboot_cbmem.c b/sys/dev/coreboot/coreboot_cbmem.c
new file mode 100644
--- /dev/null
+++ b/sys/dev/coreboot/coreboot_cbmem.c
@@ -0,0 +1,200 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2026 Abdelkader Boudih <freebsd@seuros.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * /dev/cbmem — ioctl interface for CBMEM entry enumeration and read
+ *
+ * Provides structured access to coreboot's CBMEM entries without
+ * requiring /dev/mem. Entries can be listed (CBMEM_IOC_LIST) or
+ * read by ID (CBMEM_IOC_READ).
+ */
+
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/ioccom.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/uio.h>
+
+#include <vm/vm.h>
+#include <vm/vm_param.h>
+#include <vm/pmap.h>
+
+#include <dev/coreboot/coreboot.h>
+
+static d_open_t coreboot_cbmem_open;
+static d_close_t coreboot_cbmem_close;
+static d_ioctl_t coreboot_cbmem_ioctl;
+
+static struct cdevsw coreboot_cbmem_cdevsw = {
+ .d_version = D_VERSION,
+ .d_open = coreboot_cbmem_open,
+ .d_close = coreboot_cbmem_close,
+ .d_ioctl = coreboot_cbmem_ioctl,
+ .d_name = "cbmem",
+};
+
+static int
+coreboot_cbmem_open(struct cdev *dev, int oflags, int devtype,
+ struct thread *td)
+{
+ struct coreboot_softc *sc;
+
+ sc = dev->si_drv1;
+ if (sc == NULL)
+ return (ENXIO);
+
+ return (0);
+}
+
+static int
+coreboot_cbmem_close(struct cdev *dev, int fflag, int devtype,
+ struct thread *td)
+{
+ return (0);
+}
+
+/*
+ * Find a CBMEM entry by its ID.
+ */
+static struct cbmem_entry_info *
+cbmem_find_entry(struct coreboot_softc *sc, uint32_t id)
+{
+ uint32_t i;
+
+ for (i = 0; i < sc->cbmem_count; i++) {
+ if (sc->cbmem_entries[i].id == id)
+ return (&sc->cbmem_entries[i]);
+ }
+ return (NULL);
+}
+
+static int
+coreboot_cbmem_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag,
+ struct thread *td)
+{
+ struct coreboot_softc *sc;
+ int error;
+
+ sc = dev->si_drv1;
+ if (sc == NULL)
+ return (ENXIO);
+
+ switch (cmd) {
+ case CBMEM_IOC_LIST: {
+ struct cbmem_list *list = (struct cbmem_list *)data;
+
+ list->count = sc->cbmem_count;
+ memcpy(list->entries, sc->cbmem_entries,
+ sc->cbmem_count * sizeof(struct cbmem_entry_info));
+ error = 0;
+ break;
+ }
+
+ case CBMEM_IOC_READ: {
+ struct cbmem_read_req *req = (struct cbmem_read_req *)data;
+ struct cbmem_entry_info *info;
+ void *mapped;
+ vm_size_t map_size;
+ uint64_t paddr;
+
+ info = cbmem_find_entry(sc, req->id);
+ if (info == NULL) {
+ error = ENOENT;
+ break;
+ }
+
+ if (req->offset >= info->size) {
+ error = EINVAL;
+ break;
+ }
+
+ if (req->size > info->size - req->offset)
+ req->size = info->size - req->offset;
+
+ if (req->size == 0) {
+ error = 0;
+ break;
+ }
+
+ if (info->address > UINT64_MAX - req->offset) {
+ error = EINVAL;
+ break;
+ }
+ paddr = info->address + req->offset;
+ map_size = req->size;
+ mapped = pmap_mapbios((vm_paddr_t)paddr, map_size);
+ if (mapped == NULL) {
+ error = ENOMEM;
+ break;
+ }
+
+ error = copyout(mapped, req->buffer, req->size);
+
+ pmap_unmapbios(mapped, map_size);
+ break;
+ }
+
+ default:
+ error = ENOTTY;
+ break;
+ }
+
+ return (error);
+}
+
+int
+coreboot_cbmem_create(struct coreboot_softc *sc)
+{
+ struct make_dev_args args;
+ int error;
+
+ if (sc->cbmem_count == 0)
+ return (ENXIO);
+
+ make_dev_args_init(&args);
+ args.mda_devsw = &coreboot_cbmem_cdevsw;
+ args.mda_uid = UID_ROOT;
+ args.mda_gid = GID_WHEEL;
+ args.mda_mode = 0440;
+ args.mda_si_drv1 = sc;
+ error = make_dev_s(&args, &sc->cbmem_cdev, "cbmem");
+ if (error != 0)
+ return (error);
+
+ return (0);
+}
+
+void
+coreboot_cbmem_destroy(struct coreboot_softc *sc)
+{
+ if (sc->cbmem_cdev != NULL) {
+ destroy_dev(sc->cbmem_cdev);
+ sc->cbmem_cdev = NULL;
+ }
+}
diff --git a/sys/dev/coreboot/coreboot_console.c b/sys/dev/coreboot/coreboot_console.c
new file mode 100644
--- /dev/null
+++ b/sys/dev/coreboot/coreboot_console.c
@@ -0,0 +1,232 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2026 Abdelkader Boudih <freebsd@seuros.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * /dev/coreboot_console — read-only access to the CBMEM firmware console
+ *
+ * The CBMEM console is a ring buffer written by coreboot during boot.
+ * Bit 31 of the cursor field indicates overflow (the buffer has wrapped).
+ * When overflow is set, data starts at cursor and wraps around.
+ * When not set, data starts at offset 0 and ends at cursor.
+ */
+
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/uio.h>
+
+#include <vm/vm.h>
+#include <vm/vm_param.h>
+#include <vm/pmap.h>
+
+#include <dev/coreboot/coreboot.h>
+
+static d_open_t coreboot_console_open;
+static d_close_t coreboot_console_close;
+static d_read_t coreboot_console_read;
+
+static struct cdevsw coreboot_console_cdevsw = {
+ .d_version = D_VERSION,
+ .d_open = coreboot_console_open,
+ .d_close = coreboot_console_close,
+ .d_read = coreboot_console_read,
+ .d_name = "coreboot_console",
+};
+
+static int
+coreboot_console_open(struct cdev *dev, int oflags, int devtype,
+ struct thread *td)
+{
+ struct coreboot_softc *sc;
+
+ sc = dev->si_drv1;
+ if (sc == NULL || sc->console_vaddr == NULL)
+ return (ENXIO);
+
+ return (0);
+}
+
+static int
+coreboot_console_close(struct cdev *dev, int fflag, int devtype,
+ struct thread *td)
+{
+ return (0);
+}
+
+/*
+ * Read the console ring buffer.
+ *
+ * The ring buffer has a cursor that may have wrapped. If the OVERFLOW
+ * bit (bit 31) is set, the ring has wrapped and data goes from
+ * body[cursor..size-1] then body[0..cursor-1]. Otherwise it's just
+ * body[0..cursor-1].
+ *
+ * We linearize the buffer into a contiguous view for the uiomove.
+ */
+static int
+coreboot_console_read(struct cdev *dev, struct uio *uio, int ioflag)
+{
+ struct coreboot_softc *sc;
+ struct cbmem_console *cons;
+ uint32_t cursor, flags, buf_size;
+ uint32_t data_start, data_len;
+ off_t offset;
+ size_t todo;
+ int error;
+
+ sc = dev->si_drv1;
+ if (sc == NULL || sc->console_vaddr == NULL)
+ return (ENXIO);
+
+ cons = sc->console_vaddr;
+ cursor = cons->cursor & CBMEM_CONSOLE_CURSOR_MASK;
+ flags = cons->cursor & ~CBMEM_CONSOLE_CURSOR_MASK;
+ buf_size = sc->console_data_size;
+
+ if (cursor > buf_size)
+ cursor = 0;
+
+ if (flags & CBMEM_CONSOLE_OVERFLOW) {
+ /*
+ * Ring has wrapped. Logical order:
+ * segment 1: body[cursor .. buf_size-1] (oldest data)
+ * segment 2: body[0 .. cursor-1] (newest data)
+ * Total length = buf_size
+ */
+ data_len = buf_size;
+ offset = uio->uio_offset;
+
+ if (offset >= data_len)
+ return (0);
+
+ while (uio->uio_resid > 0 && offset < data_len) {
+ uint32_t seg_start, seg_len;
+ uint32_t seg1_len = buf_size - cursor;
+
+ if (offset < seg1_len) {
+ seg_start = cursor + offset;
+ seg_len = seg1_len - offset;
+ } else {
+ seg_start = offset - seg1_len;
+ seg_len = cursor - seg_start;
+ }
+
+ todo = MIN(uio->uio_resid, seg_len);
+ error = uiomove(cons->body + seg_start, todo, uio);
+ if (error != 0)
+ return (error);
+ offset += todo;
+ }
+ } else {
+ /*
+ * No overflow — data is linear: body[0 .. cursor-1]
+ */
+ data_start = 0;
+ data_len = cursor;
+ offset = uio->uio_offset;
+
+ if (offset >= data_len)
+ return (0);
+
+ todo = MIN(uio->uio_resid, data_len - offset);
+ error = uiomove(cons->body + data_start + offset, todo, uio);
+ if (error != 0)
+ return (error);
+ }
+
+ return (0);
+}
+
+int
+coreboot_console_create(struct coreboot_softc *sc)
+{
+ struct cbmem_console *tmp;
+ vm_size_t map_size;
+ uint32_t cons_size;
+
+ if (!sc->has_console || sc->console_paddr == 0)
+ return (ENXIO);
+
+ tmp = (struct cbmem_console *)pmap_mapbios(sc->console_paddr,
+ sizeof(struct cbmem_console));
+ if (tmp == NULL) {
+ device_printf(sc->dev,
+ "unable to map CBMEM console header\n");
+ return (ENOMEM);
+ }
+
+ cons_size = tmp->size;
+ pmap_unmapbios(tmp, sizeof(struct cbmem_console));
+ if (cons_size == 0 || cons_size > CB_MAX_CONSOLE_BYTES) {
+ device_printf(sc->dev, "invalid CBMEM console size: %u\n",
+ cons_size);
+ return (EINVAL);
+ }
+ map_size = sizeof(struct cbmem_console) + cons_size;
+
+ sc->console_vaddr = (struct cbmem_console *)pmap_mapbios(
+ sc->console_paddr, map_size);
+ if (sc->console_vaddr == NULL) {
+ device_printf(sc->dev,
+ "unable to map CBMEM console (%zu bytes)\n",
+ (size_t)map_size);
+ return (ENOMEM);
+ }
+ sc->console_size = map_size;
+ sc->console_data_size = cons_size;
+
+ sc->console_cdev = make_dev(&coreboot_console_cdevsw, 0,
+ UID_ROOT, GID_WHEEL, 0440, "coreboot_console");
+ if (sc->console_cdev == NULL) {
+ pmap_unmapbios(sc->console_vaddr, sc->console_size);
+ sc->console_vaddr = NULL;
+ sc->console_size = 0;
+ sc->console_data_size = 0;
+ return (ENOMEM);
+ }
+ sc->console_cdev->si_drv1 = sc;
+
+ device_printf(sc->dev,
+ "CBMEM console: %u bytes, cursor at %u%s\n",
+ sc->console_vaddr->size,
+ sc->console_vaddr->cursor & CBMEM_CONSOLE_CURSOR_MASK,
+ (sc->console_vaddr->cursor & CBMEM_CONSOLE_OVERFLOW) ?
+ " (wrapped)" : "");
+
+ return (0);
+}
+
+void
+coreboot_console_destroy(struct coreboot_softc *sc)
+{
+ if (sc->console_cdev != NULL) {
+ destroy_dev(sc->console_cdev);
+ sc->console_cdev = NULL;
+ }
+}
diff --git a/sys/dev/coreboot/coreboot_ioctl.h b/sys/dev/coreboot/coreboot_ioctl.h
new file mode 100644
--- /dev/null
+++ b/sys/dev/coreboot/coreboot_ioctl.h
@@ -0,0 +1,144 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2026 Abdelkader Boudih <freebsd@seuros.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * coreboot(4) userland-visible definitions.
+ *
+ * This header is safe to include from both kernel and userland code.
+ * It contains the /dev/cbmem ioctl interface, CBMEM ID constants,
+ * and timestamp structures needed by the cbmem(8) utility.
+ */
+
+#ifndef _DEV_COREBOOT_COREBOOT_IOCTL_H_
+#define _DEV_COREBOOT_COREBOOT_IOCTL_H_
+
+#include <sys/types.h>
+#include <sys/ioccom.h>
+
+/*
+ * CBMEM ID constants (from coreboot's BSD-3-Clause cbmem_id.h).
+ * Used for human-readable CBMEM entry names in sysctl and ioctl output.
+ */
+#define CBMEM_ID_ACPI 0x41435049
+#define CBMEM_ID_ACPI_GNVS 0x474e5653
+#define CBMEM_ID_AFTER_CAR 0xc4787a93
+#define CBMEM_ID_CBTABLE 0x43425442
+#define CBMEM_ID_CBTABLE_FWD 0x43425443
+#define CBMEM_ID_CBFS_RO_MCACHE 0x524d5346
+#define CBMEM_ID_CBFS_RW_MCACHE 0x574d5346
+#define CBMEM_ID_CONSOLE 0x434f4e53
+#define CBMEM_ID_ELOG 0x454c4f47
+#define CBMEM_ID_FMAP 0x464d4150
+#define CBMEM_ID_FREESPACE 0x46524545
+#define CBMEM_ID_FSP_RESERVED_MEMORY 0x46535052
+#define CBMEM_ID_FSP_RUNTIME 0x52505346
+#define CBMEM_ID_FSPM_VERSION 0x56505346
+#define CBMEM_ID_IGD_OPREGION 0x4f444749
+#define CBMEM_ID_IMD_ROOT 0xff4017ff
+#define CBMEM_ID_IMD_SMALL 0x53a11439
+#define CBMEM_ID_MEMINFO 0x494d454d
+#define CBMEM_ID_MPTABLE 0x534d5054
+#define CBMEM_ID_MRCDATA 0x4d524344
+#define CBMEM_ID_PIRQ 0x49525154
+#define CBMEM_ID_POWER_STATE 0x50535454
+#define CBMEM_ID_RAM_OOPS 0x05430095
+#define CBMEM_ID_RAMSTAGE 0x9a357a9e
+#define CBMEM_ID_REFCODE 0x04efc0de
+#define CBMEM_ID_RESUME 0x5245534d
+#define CBMEM_ID_ROMSTAGE_INFO 0x47545352
+#define CBMEM_ID_ROMSTAGE_RAM_STACK 0x90357ac4
+#define CBMEM_ID_ROOT 0xff4007ff
+#define CBMEM_ID_SMBIOS 0x534d4254
+#define CBMEM_ID_SMM_COMBUFFER 0x53534d32
+#define CBMEM_ID_SMM_SAVE_SPACE 0x07e9acee
+#define CBMEM_ID_TIMESTAMP 0x54494d45
+#define CBMEM_ID_TPM_CB_LOG 0x54435041
+#define CBMEM_ID_VBOOT_WORKBUF 0x78007343
+#define CBMEM_ID_VPD 0x56504420
+
+#define CB_MAX_CBMEM_ENTRIES 64
+
+/*
+ * /dev/cbmem ioctl interface
+ */
+struct cbmem_entry_info {
+ uint32_t id;
+ uint64_t address;
+ uint32_t size;
+ char name[16];
+};
+
+struct cbmem_list {
+ uint32_t count;
+ struct cbmem_entry_info entries[CB_MAX_CBMEM_ENTRIES];
+};
+
+struct cbmem_read_req {
+ uint32_t id;
+ uint64_t offset;
+ uint32_t size;
+ void *buffer;
+};
+
+#define CBMEM_IOC_LIST _IOR('C', 1, struct cbmem_list)
+#define CBMEM_IOC_READ _IOWR('C', 2, struct cbmem_read_req)
+
+/*
+ * Timestamp structures - stored in a CBMEM region.
+ * Used by both the kernel driver and cbmem(8) utility.
+ */
+struct cb_timestamp_entry {
+ uint32_t entry_id;
+ int64_t entry_stamp;
+} __packed;
+
+struct cb_timestamp_table {
+ uint64_t base_time;
+ uint16_t max_entries;
+ uint16_t tick_freq_mhz;
+ uint32_t num_entries;
+ struct cb_timestamp_entry entries[];
+} __packed;
+
+/*
+ * TPM CB log structures - stored in a CBMEM region.
+ * Used by both the kernel driver and cbmem(8) utility.
+ */
+struct tpm_cb_log_entry {
+ uint32_t pcr;
+ uint32_t event_type;
+ uint8_t digest[20]; /* SHA-1 */
+ uint32_t data_len;
+} __packed;
+
+struct tpm_cb_log_table {
+ uint16_t max_entries;
+ uint16_t num_entries;
+ uint32_t entry_size;
+} __packed;
+
+#endif /* _DEV_COREBOOT_COREBOOT_IOCTL_H_ */
diff --git a/sys/dev/coreboot/coreboot_timestamps.c b/sys/dev/coreboot/coreboot_timestamps.c
new file mode 100644
--- /dev/null
+++ b/sys/dev/coreboot/coreboot_timestamps.c
@@ -0,0 +1,242 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2026 Abdelkader Boudih <freebsd@seuros.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * 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 {
+ uint32_t id;
+ const char *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)
+{
+
+ for (int i = 0; ts_names[i].name != NULL; i++) {
+ if (ts_names[i].id == id)
+ return (ts_names[i].name);
+ }
+ return (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);
+}
diff --git a/sys/modules/Makefile b/sys/modules/Makefile
--- a/sys/modules/Makefile
+++ b/sys/modules/Makefile
@@ -86,6 +86,7 @@
${_cfi} \
${_chromebook_platform} \
${_ciss} \
+ ${_coreboot} \
${_coretemp} \
${_cpsw} \
${_cpuctl} \
@@ -808,6 +809,7 @@
_chvgpio= chvgpio
_ciss= ciss
_chromebook_platform= chromebook_platform
+_coreboot= coreboot
_coretemp= coretemp
.if ${MK_SOURCELESS_HOST} != "no" && empty(KCSAN_ENABLED)
_hpt27xx= hpt27xx
diff --git a/sys/modules/coreboot/Makefile b/sys/modules/coreboot/Makefile
new file mode 100644
--- /dev/null
+++ b/sys/modules/coreboot/Makefile
@@ -0,0 +1,7 @@
+.PATH: ${SRCTOP}/sys/dev/coreboot
+
+KMOD= coreboot
+SRCS= coreboot.c coreboot_console.c coreboot_cbmem.c coreboot_timestamps.c
+SRCS+= bus_if.h device_if.h
+
+.include <bsd.kmod.mk>

File Metadata

Mime Type
text/plain
Expires
Thu, Mar 26, 11:18 AM (2 h, 12 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
30375294
Default Alt Text
D55649.diff (93 KB)

Event Timeline