jcs's openbsd hax
openbsd
at jcs 306 lines 7.0 kB view raw
1/* $OpenBSD: efi.c,v 1.4 2025/09/16 12:18:10 hshoexer Exp $ */ 2/* 3 * Copyright (c) 2022 3mdeb <contact@3mdeb.com> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18#include <sys/param.h> 19#include <sys/systm.h> 20#include <sys/malloc.h> 21 22#include <dev/efi/efi.h> 23#include <dev/efi/efiio.h> 24#include <machine/efivar.h> 25 26struct cfdriver efi_cd = { 27 NULL, "efi", DV_DULL, CD_COCOVM 28}; 29 30int efiioc_get_table(struct efi_softc *sc, void *); 31int efiioc_var_get(struct efi_softc *sc, void *); 32int efiioc_var_next(struct efi_softc *sc, void *); 33int efiioc_var_set(struct efi_softc *sc, void *); 34int efi_adapt_error(EFI_STATUS); 35 36EFI_GET_VARIABLE efi_get_variable; 37EFI_SET_VARIABLE efi_set_variable; 38EFI_GET_NEXT_VARIABLE_NAME efi_get_next_variable_name; 39 40int 41efiopen(dev_t dev, int flag, int mode, struct proc *p) 42{ 43 return (efi_cd.cd_ndevs > 0 ? 0 : ENXIO); 44} 45 46int 47eficlose(dev_t dev, int flag, int mode, struct proc *p) 48{ 49 return 0; 50} 51 52int 53efiioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) 54{ 55 struct efi_softc *sc = efi_cd.cd_devs[0]; 56 int error; 57 58 if (sc->sc_rs == NULL || sc->sc_pm == NULL) 59 return ENOTTY; 60 61 switch (cmd) { 62 case EFIIOC_GET_TABLE: 63 error = efiioc_get_table(sc, data); 64 break; 65 case EFIIOC_VAR_GET: 66 error = efiioc_var_get(sc, data); 67 break; 68 case EFIIOC_VAR_NEXT: 69 error = efiioc_var_next(sc, data); 70 break; 71 case EFIIOC_VAR_SET: 72 error = efiioc_var_set(sc, data); 73 break; 74 default: 75 error = ENOTTY; 76 break; 77 } 78 79 return error; 80} 81 82int 83efiioc_get_table(struct efi_softc *sc, void *data) 84{ 85 EFI_GUID esrt_guid = EFI_SYSTEM_RESOURCE_TABLE_GUID; 86 struct efi_get_table_ioc *ioc = data; 87 char *buf = NULL; 88 int error; 89 90 /* Only ESRT is supported at the moment. */ 91 if (memcmp(&ioc->uuid, &esrt_guid, sizeof(ioc->uuid)) != 0) 92 return EINVAL; 93 94 /* ESRT might not be present. */ 95 if (sc->sc_esrt == NULL) 96 return ENXIO; 97 98 if (efi_enter_check(sc)) { 99 free(buf, M_TEMP, ioc->table_len); 100 return ENOSYS; 101 } 102 103 ioc->table_len = sizeof(*sc->sc_esrt) + 104 sizeof(EFI_SYSTEM_RESOURCE_ENTRY) * sc->sc_esrt->FwResourceCount; 105 106 /* Return table length to userspace. */ 107 if (ioc->buf == NULL) { 108 efi_leave(sc); 109 return 0; 110 } 111 112 /* Refuse to copy only part of the table. */ 113 if (ioc->buf_len < ioc->table_len) { 114 efi_leave(sc); 115 return EINVAL; 116 } 117 118 buf = malloc(ioc->table_len, M_TEMP, M_WAITOK); 119 memcpy(buf, sc->sc_esrt, ioc->table_len); 120 121 efi_leave(sc); 122 123 error = copyout(buf, ioc->buf, ioc->table_len); 124 free(buf, M_TEMP, ioc->table_len); 125 126 return error; 127} 128 129int 130efiioc_var_get(struct efi_softc *sc, void *data) 131{ 132 struct efi_var_ioc *ioc = data; 133 void *value = NULL; 134 efi_char *name = NULL; 135 size_t valuesize = ioc->datasize; 136 EFI_STATUS status; 137 int error; 138 139 if (valuesize > 0) 140 value = malloc(valuesize, M_TEMP, M_WAITOK); 141 name = malloc(ioc->namesize, M_TEMP, M_WAITOK); 142 error = copyin(ioc->name, name, ioc->namesize); 143 if (error != 0) 144 goto leave; 145 146 /* NULL-terminated name must fit into namesize bytes. */ 147 if (name[ioc->namesize / sizeof(*name) - 1] != 0) { 148 error = EINVAL; 149 goto leave; 150 } 151 152 if (efi_get_variable) { 153 status = efi_get_variable(name, (EFI_GUID *)&ioc->vendor, 154 &ioc->attrib, &ioc->datasize, value); 155 } else { 156 if (efi_enter_check(sc)) { 157 error = ENOSYS; 158 goto leave; 159 } 160 status = sc->sc_rs->GetVariable(name, (EFI_GUID *)&ioc->vendor, 161 &ioc->attrib, &ioc->datasize, value); 162 efi_leave(sc); 163 } 164 165 if (status == EFI_BUFFER_TOO_SMALL) { 166 /* 167 * Return size of the value, which was set by EFI RT, 168 * reporting no error to match FreeBSD's behaviour. 169 */ 170 ioc->data = NULL; 171 goto leave; 172 } 173 174 error = efi_adapt_error(status); 175 if (error == 0) 176 error = copyout(value, ioc->data, ioc->datasize); 177 178leave: 179 free(value, M_TEMP, valuesize); 180 free(name, M_TEMP, ioc->namesize); 181 return error; 182} 183 184int 185efiioc_var_next(struct efi_softc *sc, void *data) 186{ 187 struct efi_var_ioc *ioc = data; 188 efi_char *name; 189 size_t namesize = ioc->namesize; 190 EFI_STATUS status; 191 int error; 192 193 name = malloc(namesize, M_TEMP, M_WAITOK); 194 error = copyin(ioc->name, name, namesize); 195 if (error) 196 goto leave; 197 198 if (efi_get_next_variable_name) { 199 status = efi_get_next_variable_name(&ioc->namesize, 200 name, (EFI_GUID *)&ioc->vendor); 201 } else { 202 if (efi_enter_check(sc)) { 203 error = ENOSYS; 204 goto leave; 205 } 206 status = sc->sc_rs->GetNextVariableName(&ioc->namesize, 207 name, (EFI_GUID *)&ioc->vendor); 208 efi_leave(sc); 209 } 210 211 if (status == EFI_BUFFER_TOO_SMALL) { 212 /* 213 * Return size of the name, which was set by EFI RT, 214 * reporting no error to match FreeBSD's behaviour. 215 */ 216 ioc->name = NULL; 217 goto leave; 218 } 219 220 error = efi_adapt_error(status); 221 if (error == 0) 222 error = copyout(name, ioc->name, ioc->namesize); 223 224leave: 225 free(name, M_TEMP, namesize); 226 return error; 227} 228 229int 230efiioc_var_set(struct efi_softc *sc, void *data) 231{ 232 struct efi_var_ioc *ioc = data; 233 void *value = NULL; 234 efi_char *name = NULL; 235 EFI_STATUS status; 236 int error; 237 238 /* Zero datasize means variable deletion. */ 239 if (ioc->datasize > 0) { 240 value = malloc(ioc->datasize, M_TEMP, M_WAITOK); 241 error = copyin(ioc->data, value, ioc->datasize); 242 if (error) 243 goto leave; 244 } 245 246 name = malloc(ioc->namesize, M_TEMP, M_WAITOK); 247 error = copyin(ioc->name, name, ioc->namesize); 248 if (error) 249 goto leave; 250 251 /* NULL-terminated name must fit into namesize bytes. */ 252 if (name[ioc->namesize / sizeof(*name) - 1] != 0) { 253 error = EINVAL; 254 goto leave; 255 } 256 257 if (securelevel > 0) { 258 error = EPERM; 259 goto leave; 260 } 261 262 if (efi_set_variable) { 263 status = efi_set_variable(name, (EFI_GUID *)&ioc->vendor, 264 ioc->attrib, ioc->datasize, value); 265 } else { 266 if (efi_enter_check(sc)) { 267 error = ENOSYS; 268 goto leave; 269 } 270 status = sc->sc_rs->SetVariable(name, (EFI_GUID *)&ioc->vendor, 271 ioc->attrib, ioc->datasize, value); 272 efi_leave(sc); 273 } 274 275 error = efi_adapt_error(status); 276 277leave: 278 free(value, M_TEMP, ioc->datasize); 279 free(name, M_TEMP, ioc->namesize); 280 return error; 281} 282 283int 284efi_adapt_error(EFI_STATUS status) 285{ 286 switch (status) { 287 case EFI_SUCCESS: 288 return 0; 289 case EFI_DEVICE_ERROR: 290 return EIO; 291 case EFI_INVALID_PARAMETER: 292 return EINVAL; 293 case EFI_NOT_FOUND: 294 return ENOENT; 295 case EFI_OUT_OF_RESOURCES: 296 return EAGAIN; 297 case EFI_SECURITY_VIOLATION: 298 return EPERM; 299 case EFI_UNSUPPORTED: 300 return ENOSYS; 301 case EFI_WRITE_PROTECTED: 302 return EROFS; 303 default: 304 return EIO; 305 } 306}