1 /*
2 * Copyright (c) 2026 Abdelkader Boudih <freebsd@seuros.com>
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 */
6
7 /*
8 * /dev/cbmem — ioctl interface for CBMEM entry enumeration and read
9 *
10 * Provides structured access to coreboot's CBMEM entries without
11 * requiring /dev/mem. Entries can be listed (CBMEM_IOC_LIST) or
12 * read by ID (CBMEM_IOC_READ).
13 */
14
15 #include <sys/systm.h>
16 #include <sys/bus.h>
17 #include <sys/conf.h>
18 #include <sys/ioccom.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_ioctl_t coreboot_cbmem_ioctl;
30
31 static struct cdevsw coreboot_cbmem_cdevsw = {
32 .d_version = D_VERSION,
33 .d_ioctl = coreboot_cbmem_ioctl,
34 .d_name = "cbmem",
35 };
36
37 /*
38 * Find a CBMEM entry by its ID.
39 */
40 static struct cbmem_entry_info *
cbmem_find_entry(struct coreboot_softc * sc,uint32_t id)41 cbmem_find_entry(struct coreboot_softc *sc, uint32_t id)
42 {
43 uint32_t i;
44
45 for (i = 0; i < sc->cbmem_count; i++) {
46 if (sc->cbmem_entries[i].id == id)
47 return (&sc->cbmem_entries[i]);
48 }
49 return (NULL);
50 }
51
52 static int
coreboot_cbmem_ioctl(struct cdev * dev,u_long cmd,caddr_t data,int fflag,struct thread * td)53 coreboot_cbmem_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag,
54 struct thread *td)
55 {
56 struct coreboot_softc *sc;
57 int error;
58
59 sc = dev->si_drv1;
60
61 switch (cmd) {
62 case CBMEM_IOC_LIST: {
63 struct cbmem_list *list = (struct cbmem_list *)data;
64
65 list->count = sc->cbmem_count;
66 memcpy(list->entries, sc->cbmem_entries,
67 sc->cbmem_count * sizeof(struct cbmem_entry_info));
68 error = 0;
69 break;
70 }
71
72 case CBMEM_IOC_READ: {
73 struct cbmem_read_req *req = (struct cbmem_read_req *)data;
74 struct cbmem_entry_info *info;
75 void *mapped;
76 vm_size_t map_size;
77 uint64_t paddr;
78
79 info = cbmem_find_entry(sc, req->id);
80 if (info == NULL) {
81 error = ENOENT;
82 break;
83 }
84
85 if (req->offset >= info->size) {
86 error = EINVAL;
87 break;
88 }
89
90 if (req->size > info->size - req->offset)
91 req->size = info->size - req->offset;
92
93 if (req->size == 0) {
94 error = 0;
95 break;
96 }
97
98 if (info->address > UINT64_MAX - req->offset) {
99 error = EINVAL;
100 break;
101 }
102 paddr = info->address + req->offset;
103 map_size = req->size;
104 mapped = pmap_mapbios((vm_paddr_t)paddr, map_size);
105 if (mapped == NULL) {
106 error = ENOMEM;
107 break;
108 }
109
110 error = copyout(mapped, req->buffer, req->size);
111
112 pmap_unmapbios(mapped, map_size);
113 break;
114 }
115
116 default:
117 error = ENOTTY;
118 break;
119 }
120
121 return (error);
122 }
123
124 int
coreboot_cbmem_create(struct coreboot_softc * sc)125 coreboot_cbmem_create(struct coreboot_softc *sc)
126 {
127 struct make_dev_args args;
128 int error;
129
130 if (sc->cbmem_count == 0)
131 return (ENXIO);
132
133 make_dev_args_init(&args);
134 args.mda_devsw = &coreboot_cbmem_cdevsw;
135 args.mda_uid = UID_ROOT;
136 args.mda_gid = GID_WHEEL;
137 args.mda_mode = 0440;
138 args.mda_si_drv1 = sc;
139 error = make_dev_s(&args, &sc->cbmem_cdev, "cbmem");
140 if (error != 0)
141 return (error);
142
143 return (0);
144 }
145
146 void
coreboot_cbmem_destroy(struct coreboot_softc * sc)147 coreboot_cbmem_destroy(struct coreboot_softc *sc)
148 {
149
150 coreboot_cdev_destroy(&sc->cbmem_cdev);
151 }
152