xref: /freebsd/sys/dev/coreboot/coreboot_console.c (revision 5f74217c05a99f38a31a6e4950220964595d4ac8)
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