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