1/* $OpenBSD: efi.c,v 1.1 2023/01/14 12:11:11 kettenis 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 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 36int 37efiopen(dev_t dev, int flag, int mode, struct proc *p) 38{ 39 return (efi_cd.cd_ndevs > 0 ? 0 : ENXIO); 40} 41 42int 43eficlose(dev_t dev, int flag, int mode, struct proc *p) 44{ 45 return 0; 46} 47 48int 49efiioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) 50{ 51 struct efi_softc *sc = efi_cd.cd_devs[0]; 52 int error; 53 54 switch (cmd) { 55 case EFIIOC_GET_TABLE: 56 error = efiioc_get_table(sc, data); 57 break; 58 case EFIIOC_VAR_GET: 59 error = efiioc_var_get(sc, data); 60 break; 61 case EFIIOC_VAR_NEXT: 62 error = efiioc_var_next(sc, data); 63 break; 64 case EFIIOC_VAR_SET: 65 error = efiioc_var_set(sc, data); 66 break; 67 default: 68 error = ENOTTY; 69 break; 70 } 71 72 return error; 73} 74 75int 76efiioc_get_table(struct efi_softc *sc, void *data) 77{ 78 EFI_GUID esrt_guid = EFI_SYSTEM_RESOURCE_TABLE_GUID; 79 struct efi_get_table_ioc *ioc = data; 80 char *buf = NULL; 81 int error; 82 83 /* Only ESRT is supported at the moment. */ 84 if (memcmp(&ioc->uuid, &esrt_guid, sizeof(ioc->uuid)) != 0) 85 return EINVAL; 86 87 /* ESRT might not be present. */ 88 if (sc->sc_esrt == NULL) 89 return ENXIO; 90 91 if (efi_enter_check(sc)) { 92 free(buf, M_TEMP, ioc->table_len); 93 return ENOSYS; 94 } 95 96 ioc->table_len = sizeof(*sc->sc_esrt) + 97 sizeof(EFI_SYSTEM_RESOURCE_ENTRY) * sc->sc_esrt->FwResourceCount; 98 99 /* Return table length to userspace. */ 100 if (ioc->buf == NULL) { 101 efi_leave(sc); 102 return 0; 103 } 104 105 /* Refuse to copy only part of the table. */ 106 if (ioc->buf_len < ioc->table_len) { 107 efi_leave(sc); 108 return EINVAL; 109 } 110 111 buf = malloc(ioc->table_len, M_TEMP, M_WAITOK); 112 memcpy(buf, sc->sc_esrt, ioc->table_len); 113 114 efi_leave(sc); 115 116 error = copyout(buf, ioc->buf, ioc->table_len); 117 free(buf, M_TEMP, ioc->table_len); 118 119 return error; 120} 121 122int 123efiioc_var_get(struct efi_softc *sc, void *data) 124{ 125 struct efi_var_ioc *ioc = data; 126 void *value = NULL; 127 efi_char *name = NULL; 128 size_t valuesize = ioc->datasize; 129 EFI_STATUS status; 130 int error; 131 132 if (valuesize > 0) 133 value = malloc(valuesize, M_TEMP, M_WAITOK); 134 name = malloc(ioc->namesize, M_TEMP, M_WAITOK); 135 error = copyin(ioc->name, name, ioc->namesize); 136 if (error != 0) 137 goto leave; 138 139 /* NULL-terminated name must fit into namesize bytes. */ 140 if (name[ioc->namesize / sizeof(*name) - 1] != 0) { 141 error = EINVAL; 142 goto leave; 143 } 144 145 if (efi_enter_check(sc)) { 146 error = ENOSYS; 147 goto leave; 148 } 149 status = sc->sc_rs->GetVariable(name, (EFI_GUID *)&ioc->vendor, 150 &ioc->attrib, &ioc->datasize, value); 151 efi_leave(sc); 152 153 if (status == EFI_BUFFER_TOO_SMALL) { 154 /* 155 * Return size of the value, which was set by EFI RT, 156 * reporting no error to match FreeBSD's behaviour. 157 */ 158 ioc->data = NULL; 159 goto leave; 160 } 161 162 error = efi_adapt_error(status); 163 if (error == 0) 164 error = copyout(value, ioc->data, ioc->datasize); 165 166leave: 167 free(value, M_TEMP, valuesize); 168 free(name, M_TEMP, ioc->namesize); 169 return error; 170} 171 172int 173efiioc_var_next(struct efi_softc *sc, void *data) 174{ 175 struct efi_var_ioc *ioc = data; 176 efi_char *name; 177 size_t namesize = ioc->namesize; 178 EFI_STATUS status; 179 int error; 180 181 name = malloc(namesize, M_TEMP, M_WAITOK); 182 error = copyin(ioc->name, name, namesize); 183 if (error) 184 goto leave; 185 186 if (efi_enter_check(sc)) { 187 error = ENOSYS; 188 goto leave; 189 } 190 status = sc->sc_rs->GetNextVariableName(&ioc->namesize, 191 name, (EFI_GUID *)&ioc->vendor); 192 efi_leave(sc); 193 194 if (status == EFI_BUFFER_TOO_SMALL) { 195 /* 196 * Return size of the name, which was set by EFI RT, 197 * reporting no error to match FreeBSD's behaviour. 198 */ 199 ioc->name = NULL; 200 goto leave; 201 } 202 203 error = efi_adapt_error(status); 204 if (error == 0) 205 error = copyout(name, ioc->name, ioc->namesize); 206 207leave: 208 free(name, M_TEMP, namesize); 209 return error; 210} 211 212int 213efiioc_var_set(struct efi_softc *sc, void *data) 214{ 215 struct efi_var_ioc *ioc = data; 216 void *value = NULL; 217 efi_char *name = NULL; 218 EFI_STATUS status; 219 int error; 220 221 /* Zero datasize means variable deletion. */ 222 if (ioc->datasize > 0) { 223 value = malloc(ioc->datasize, M_TEMP, M_WAITOK); 224 error = copyin(ioc->data, value, ioc->datasize); 225 if (error) 226 goto leave; 227 } 228 229 name = malloc(ioc->namesize, M_TEMP, M_WAITOK); 230 error = copyin(ioc->name, name, ioc->namesize); 231 if (error) 232 goto leave; 233 234 /* NULL-terminated name must fit into namesize bytes. */ 235 if (name[ioc->namesize / sizeof(*name) - 1] != 0) { 236 error = EINVAL; 237 goto leave; 238 } 239 240 if (securelevel > 0) { 241 error = EPERM; 242 goto leave; 243 } 244 245 if (efi_enter_check(sc)) { 246 error = ENOSYS; 247 goto leave; 248 } 249 status = sc->sc_rs->SetVariable(name, (EFI_GUID *)&ioc->vendor, 250 ioc->attrib, ioc->datasize, value); 251 efi_leave(sc); 252 253 error = efi_adapt_error(status); 254 255leave: 256 free(value, M_TEMP, ioc->datasize); 257 free(name, M_TEMP, ioc->namesize); 258 return error; 259} 260 261int 262efi_adapt_error(EFI_STATUS status) 263{ 264 switch (status) { 265 case EFI_SUCCESS: 266 return 0; 267 case EFI_DEVICE_ERROR: 268 return EIO; 269 case EFI_INVALID_PARAMETER: 270 return EINVAL; 271 case EFI_NOT_FOUND: 272 return ENOENT; 273 case EFI_OUT_OF_RESOURCES: 274 return EAGAIN; 275 case EFI_SECURITY_VIOLATION: 276 return EPERM; 277 case EFI_UNSUPPORTED: 278 return ENOSYS; 279 case EFI_WRITE_PROTECTED: 280 return EROFS; 281 default: 282 return EIO; 283 } 284} 285