1*5f74217cSAbdelkader Boudih /*
2*5f74217cSAbdelkader Boudih * Copyright (c) 2026 Abdelkader Boudih <freebsd@seuros.com>
3*5f74217cSAbdelkader Boudih *
4*5f74217cSAbdelkader Boudih * SPDX-License-Identifier: BSD-2-Clause
5*5f74217cSAbdelkader Boudih */
6*5f74217cSAbdelkader Boudih
7*5f74217cSAbdelkader Boudih /*
8*5f74217cSAbdelkader Boudih * /dev/coreboot_console — read-only access to the CBMEM firmware console
9*5f74217cSAbdelkader Boudih *
10*5f74217cSAbdelkader Boudih * The CBMEM console is a ring buffer written by coreboot during boot.
11*5f74217cSAbdelkader Boudih * Bit 31 of the cursor field indicates overflow (the buffer has wrapped).
12*5f74217cSAbdelkader Boudih * When overflow is set, data starts at cursor and wraps around.
13*5f74217cSAbdelkader Boudih * When not set, data starts at offset 0 and ends at cursor.
14*5f74217cSAbdelkader Boudih */
15*5f74217cSAbdelkader Boudih
16*5f74217cSAbdelkader Boudih #include <sys/systm.h>
17*5f74217cSAbdelkader Boudih #include <sys/bus.h>
18*5f74217cSAbdelkader Boudih #include <sys/conf.h>
19*5f74217cSAbdelkader Boudih #include <sys/kernel.h>
20*5f74217cSAbdelkader Boudih #include <sys/module.h>
21*5f74217cSAbdelkader Boudih #include <sys/uio.h>
22*5f74217cSAbdelkader Boudih
23*5f74217cSAbdelkader Boudih #include <vm/vm.h>
24*5f74217cSAbdelkader Boudih #include <vm/vm_param.h>
25*5f74217cSAbdelkader Boudih #include <vm/pmap.h>
26*5f74217cSAbdelkader Boudih
27*5f74217cSAbdelkader Boudih #include <dev/coreboot/coreboot.h>
28*5f74217cSAbdelkader Boudih
29*5f74217cSAbdelkader Boudih static d_read_t coreboot_console_read;
30*5f74217cSAbdelkader Boudih
31*5f74217cSAbdelkader Boudih static struct cdevsw coreboot_console_cdevsw = {
32*5f74217cSAbdelkader Boudih .d_version = D_VERSION,
33*5f74217cSAbdelkader Boudih .d_read = coreboot_console_read,
34*5f74217cSAbdelkader Boudih .d_name = "coreboot_console",
35*5f74217cSAbdelkader Boudih };
36*5f74217cSAbdelkader Boudih
37*5f74217cSAbdelkader Boudih /*
38*5f74217cSAbdelkader Boudih * Read the console ring buffer.
39*5f74217cSAbdelkader Boudih *
40*5f74217cSAbdelkader Boudih * The ring buffer has a cursor that may have wrapped. If the OVERFLOW
41*5f74217cSAbdelkader Boudih * bit (bit 31) is set, the ring has wrapped and data goes from
42*5f74217cSAbdelkader Boudih * body[cursor..size-1] then body[0..cursor-1]. Otherwise it's just
43*5f74217cSAbdelkader Boudih * body[0..cursor-1].
44*5f74217cSAbdelkader Boudih *
45*5f74217cSAbdelkader Boudih * We linearize the buffer into a contiguous view for the uiomove.
46*5f74217cSAbdelkader Boudih */
47*5f74217cSAbdelkader Boudih static int
coreboot_console_read(struct cdev * dev,struct uio * uio,int ioflag)48*5f74217cSAbdelkader Boudih coreboot_console_read(struct cdev *dev, struct uio *uio, int ioflag)
49*5f74217cSAbdelkader Boudih {
50*5f74217cSAbdelkader Boudih struct coreboot_softc *sc;
51*5f74217cSAbdelkader Boudih struct cbmem_console *cons;
52*5f74217cSAbdelkader Boudih uint32_t cursor, flags, buf_size;
53*5f74217cSAbdelkader Boudih uint32_t data_start, data_len;
54*5f74217cSAbdelkader Boudih off_t offset;
55*5f74217cSAbdelkader Boudih size_t todo;
56*5f74217cSAbdelkader Boudih int error;
57*5f74217cSAbdelkader Boudih
58*5f74217cSAbdelkader Boudih sc = dev->si_drv1;
59*5f74217cSAbdelkader Boudih if (sc->console_vaddr == NULL)
60*5f74217cSAbdelkader Boudih return (ENXIO);
61*5f74217cSAbdelkader Boudih
62*5f74217cSAbdelkader Boudih cons = sc->console_vaddr;
63*5f74217cSAbdelkader Boudih cursor = cons->cursor & CBMEM_CONSOLE_CURSOR_MASK;
64*5f74217cSAbdelkader Boudih flags = cons->cursor & ~CBMEM_CONSOLE_CURSOR_MASK;
65*5f74217cSAbdelkader Boudih buf_size = sc->console_data_size;
66*5f74217cSAbdelkader Boudih
67*5f74217cSAbdelkader Boudih if (cursor > buf_size)
68*5f74217cSAbdelkader Boudih cursor = 0;
69*5f74217cSAbdelkader Boudih
70*5f74217cSAbdelkader Boudih if (flags & CBMEM_CONSOLE_OVERFLOW) {
71*5f74217cSAbdelkader Boudih /*
72*5f74217cSAbdelkader Boudih * Ring has wrapped. Logical order:
73*5f74217cSAbdelkader Boudih * segment 1: body[cursor .. buf_size-1] (oldest data)
74*5f74217cSAbdelkader Boudih * segment 2: body[0 .. cursor-1] (newest data)
75*5f74217cSAbdelkader Boudih * Total length = buf_size
76*5f74217cSAbdelkader Boudih */
77*5f74217cSAbdelkader Boudih data_len = buf_size;
78*5f74217cSAbdelkader Boudih offset = uio->uio_offset;
79*5f74217cSAbdelkader Boudih
80*5f74217cSAbdelkader Boudih if (offset >= data_len)
81*5f74217cSAbdelkader Boudih return (0);
82*5f74217cSAbdelkader Boudih
83*5f74217cSAbdelkader Boudih while (uio->uio_resid > 0 && offset < data_len) {
84*5f74217cSAbdelkader Boudih uint32_t seg_start, seg_len;
85*5f74217cSAbdelkader Boudih uint32_t seg1_len = buf_size - cursor;
86*5f74217cSAbdelkader Boudih
87*5f74217cSAbdelkader Boudih if (offset < seg1_len) {
88*5f74217cSAbdelkader Boudih seg_start = cursor + offset;
89*5f74217cSAbdelkader Boudih seg_len = seg1_len - offset;
90*5f74217cSAbdelkader Boudih } else {
91*5f74217cSAbdelkader Boudih seg_start = offset - seg1_len;
92*5f74217cSAbdelkader Boudih seg_len = cursor - seg_start;
93*5f74217cSAbdelkader Boudih }
94*5f74217cSAbdelkader Boudih
95*5f74217cSAbdelkader Boudih todo = MIN(uio->uio_resid, seg_len);
96*5f74217cSAbdelkader Boudih error = uiomove(cons->body + seg_start, todo, uio);
97*5f74217cSAbdelkader Boudih if (error != 0)
98*5f74217cSAbdelkader Boudih return (error);
99*5f74217cSAbdelkader Boudih offset += todo;
100*5f74217cSAbdelkader Boudih }
101*5f74217cSAbdelkader Boudih } else {
102*5f74217cSAbdelkader Boudih /*
103*5f74217cSAbdelkader Boudih * No overflow — data is linear: body[0 .. cursor-1]
104*5f74217cSAbdelkader Boudih */
105*5f74217cSAbdelkader Boudih data_start = 0;
106*5f74217cSAbdelkader Boudih data_len = cursor;
107*5f74217cSAbdelkader Boudih offset = uio->uio_offset;
108*5f74217cSAbdelkader Boudih
109*5f74217cSAbdelkader Boudih if (offset >= data_len)
110*5f74217cSAbdelkader Boudih return (0);
111*5f74217cSAbdelkader Boudih
112*5f74217cSAbdelkader Boudih todo = MIN(uio->uio_resid, data_len - offset);
113*5f74217cSAbdelkader Boudih error = uiomove(cons->body + data_start + offset, todo, uio);
114*5f74217cSAbdelkader Boudih if (error != 0)
115*5f74217cSAbdelkader Boudih return (error);
116*5f74217cSAbdelkader Boudih }
117*5f74217cSAbdelkader Boudih
118*5f74217cSAbdelkader Boudih return (0);
119*5f74217cSAbdelkader Boudih }
120*5f74217cSAbdelkader Boudih
121*5f74217cSAbdelkader Boudih int
coreboot_console_create(struct coreboot_softc * sc)122*5f74217cSAbdelkader Boudih coreboot_console_create(struct coreboot_softc *sc)
123*5f74217cSAbdelkader Boudih {
124*5f74217cSAbdelkader Boudih struct cbmem_console *tmp;
125*5f74217cSAbdelkader Boudih vm_size_t map_size;
126*5f74217cSAbdelkader Boudih uint32_t cons_size;
127*5f74217cSAbdelkader Boudih
128*5f74217cSAbdelkader Boudih if (!sc->has_console || sc->console_paddr == 0)
129*5f74217cSAbdelkader Boudih return (ENXIO);
130*5f74217cSAbdelkader Boudih
131*5f74217cSAbdelkader Boudih tmp = (struct cbmem_console *)pmap_mapbios(sc->console_paddr,
132*5f74217cSAbdelkader Boudih sizeof(struct cbmem_console));
133*5f74217cSAbdelkader Boudih if (tmp == NULL) {
134*5f74217cSAbdelkader Boudih device_printf(sc->dev,
135*5f74217cSAbdelkader Boudih "unable to map CBMEM console header\n");
136*5f74217cSAbdelkader Boudih return (ENOMEM);
137*5f74217cSAbdelkader Boudih }
138*5f74217cSAbdelkader Boudih
139*5f74217cSAbdelkader Boudih cons_size = tmp->size;
140*5f74217cSAbdelkader Boudih pmap_unmapbios(tmp, sizeof(struct cbmem_console));
141*5f74217cSAbdelkader Boudih if (cons_size == 0 || cons_size > CB_MAX_CONSOLE_BYTES) {
142*5f74217cSAbdelkader Boudih device_printf(sc->dev, "invalid CBMEM console size: %u\n",
143*5f74217cSAbdelkader Boudih cons_size);
144*5f74217cSAbdelkader Boudih return (EINVAL);
145*5f74217cSAbdelkader Boudih }
146*5f74217cSAbdelkader Boudih map_size = sizeof(struct cbmem_console) + cons_size;
147*5f74217cSAbdelkader Boudih
148*5f74217cSAbdelkader Boudih sc->console_vaddr = (struct cbmem_console *)pmap_mapbios(
149*5f74217cSAbdelkader Boudih sc->console_paddr, map_size);
150*5f74217cSAbdelkader Boudih if (sc->console_vaddr == NULL) {
151*5f74217cSAbdelkader Boudih device_printf(sc->dev,
152*5f74217cSAbdelkader Boudih "unable to map CBMEM console (%zu bytes)\n",
153*5f74217cSAbdelkader Boudih (size_t)map_size);
154*5f74217cSAbdelkader Boudih return (ENOMEM);
155*5f74217cSAbdelkader Boudih }
156*5f74217cSAbdelkader Boudih sc->console_size = map_size;
157*5f74217cSAbdelkader Boudih sc->console_data_size = cons_size;
158*5f74217cSAbdelkader Boudih
159*5f74217cSAbdelkader Boudih struct make_dev_args args;
160*5f74217cSAbdelkader Boudih int error;
161*5f74217cSAbdelkader Boudih
162*5f74217cSAbdelkader Boudih make_dev_args_init(&args);
163*5f74217cSAbdelkader Boudih args.mda_devsw = &coreboot_console_cdevsw;
164*5f74217cSAbdelkader Boudih args.mda_uid = UID_ROOT;
165*5f74217cSAbdelkader Boudih args.mda_gid = GID_WHEEL;
166*5f74217cSAbdelkader Boudih args.mda_mode = 0440;
167*5f74217cSAbdelkader Boudih args.mda_si_drv1 = sc;
168*5f74217cSAbdelkader Boudih error = make_dev_s(&args, &sc->console_cdev, "coreboot_console");
169*5f74217cSAbdelkader Boudih if (error != 0) {
170*5f74217cSAbdelkader Boudih pmap_unmapbios(sc->console_vaddr, sc->console_size);
171*5f74217cSAbdelkader Boudih sc->console_vaddr = NULL;
172*5f74217cSAbdelkader Boudih sc->console_size = 0;
173*5f74217cSAbdelkader Boudih sc->console_data_size = 0;
174*5f74217cSAbdelkader Boudih return (error);
175*5f74217cSAbdelkader Boudih }
176*5f74217cSAbdelkader Boudih
177*5f74217cSAbdelkader Boudih device_printf(sc->dev,
178*5f74217cSAbdelkader Boudih "CBMEM console: %u bytes, cursor at %u%s\n",
179*5f74217cSAbdelkader Boudih sc->console_vaddr->size,
180*5f74217cSAbdelkader Boudih sc->console_vaddr->cursor & CBMEM_CONSOLE_CURSOR_MASK,
181*5f74217cSAbdelkader Boudih (sc->console_vaddr->cursor & CBMEM_CONSOLE_OVERFLOW) ?
182*5f74217cSAbdelkader Boudih " (wrapped)" : "");
183*5f74217cSAbdelkader Boudih
184*5f74217cSAbdelkader Boudih return (0);
185*5f74217cSAbdelkader Boudih }
186*5f74217cSAbdelkader Boudih
187*5f74217cSAbdelkader Boudih void
coreboot_console_destroy(struct coreboot_softc * sc)188*5f74217cSAbdelkader Boudih coreboot_console_destroy(struct coreboot_softc *sc)
189*5f74217cSAbdelkader Boudih {
190*5f74217cSAbdelkader Boudih
191*5f74217cSAbdelkader Boudih coreboot_cdev_destroy(&sc->console_cdev);
192*5f74217cSAbdelkader Boudih }
193