mirror of
https://github.com/freebsd/freebsd-src.git
synced 2026-10-29 11:54:38 +01:00
Nexus-attached driver that discovers and parses coreboot's LBIO tables from physical memory. Exposes firmware metadata (version, build info, mainboard, serial config, TSC frequency, CBMEM entries) via sysctl hw.coreboot.*, the firmware console ring buffer via /dev/coreboot_console, and structured CBMEM entry access via /dev/cbmem ioctl interface. Tested on: - Qotom Q535G6 (Kabylake) - Intel NUC D54250WYK (Haswell) - Intel NUC D33217GKE (Ivy Bridge) - Dell 3100 2-in-1 (Gabbiter) - Dell 3100 (Fleex) - Lenovo IdeaPad 320s - Lenovo ThinkPad T480 - HP Chromebook 11 G4 - HP Chromebook 11 G5 - HP Chromebook 11 G6 EE - HP Chromebook 14 G4 - HP Chromebook 14 G5 - HP Chromebook x360 11 G1 EE - HP Chromebook x360 11 G2 EE - HP Chromebook x360 14 G1 - Acer C720 - Acer Chromebook 11 - Lenovo N22 Reviewed by: ngie, kib, adrian Differential Revision: https://reviews.freebsd.org/D55649
193 lines
4.6 KiB
C
193 lines
4.6 KiB
C
/*
|
|
* Copyright (c) 2026 Abdelkader Boudih <[email protected]>
|
|
*
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
*/
|
|
|
|
/*
|
|
* /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_read_t coreboot_console_read;
|
|
|
|
static struct cdevsw coreboot_console_cdevsw = {
|
|
.d_version = D_VERSION,
|
|
.d_read = coreboot_console_read,
|
|
.d_name = "coreboot_console",
|
|
};
|
|
|
|
/*
|
|
* 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->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;
|
|
|
|
struct make_dev_args args;
|
|
int error;
|
|
|
|
make_dev_args_init(&args);
|
|
args.mda_devsw = &coreboot_console_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->console_cdev, "coreboot_console");
|
|
if (error != 0) {
|
|
pmap_unmapbios(sc->console_vaddr, sc->console_size);
|
|
sc->console_vaddr = NULL;
|
|
sc->console_size = 0;
|
|
sc->console_data_size = 0;
|
|
return (error);
|
|
}
|
|
|
|
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)
|
|
{
|
|
|
|
coreboot_cdev_destroy(&sc->console_cdev);
|
|
}
|