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