Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F149590461
D55649.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
93 KB
Referenced Files
None
Subscribers
None
D55649.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D55649: coreboot: Add coreboot firmware table driver
Attached
Detach File
Event Timeline
Log In to Comment