diff options
author | Marcel Moolenaar <marcel@FreeBSD.org> | 2010-01-14 02:48:39 +0000 |
---|---|---|
committer | Marcel Moolenaar <marcel@FreeBSD.org> | 2010-01-14 02:48:39 +0000 |
commit | 646420c8dcca3bac0716c94cd849473dc4bdff0f (patch) | |
tree | 5ac0aa1deb46d7436ead499851d538d9836dfaaf /sys | |
parent | 684b17831f973f0caf055ebc0cc87fee2a305d10 (diff) | |
download | src-646420c8dcca3bac0716c94cd849473dc4bdff0f.tar.gz src-646420c8dcca3bac0716c94cd849473dc4bdff0f.zip |
Add ioctl requests to /dev/io on ia64 for reading and writing
EFI variables. The primary reason for this is that it allows
sysinstall(8) to add a boot menu item for the newly installed
FreeBSD image.
Notes
Notes:
svn path=/head/; revision=202273
Diffstat (limited to 'sys')
-rw-r--r-- | sys/ia64/ia64/iodev_machdep.c | 140 | ||||
-rw-r--r-- | sys/ia64/include/iodev.h | 18 |
2 files changed, 158 insertions, 0 deletions
diff --git a/sys/ia64/ia64/iodev_machdep.c b/sys/ia64/ia64/iodev_machdep.c index 498a042cfce0..d255aae127ba 100644 --- a/sys/ia64/ia64/iodev_machdep.c +++ b/sys/ia64/ia64/iodev_machdep.c @@ -31,16 +31,22 @@ __FBSDID("$FreeBSD$"); #include <sys/conf.h> #include <sys/fcntl.h> #include <sys/ioccom.h> +#include <sys/malloc.h> #include <sys/priv.h> #include <sys/proc.h> #include <sys/systm.h> #include <machine/bus.h> +#include <machine/efi.h> #include <machine/iodev.h> static int iodev_pio_read(struct iodev_pio_req *req); static int iodev_pio_write(struct iodev_pio_req *req); +static int iodev_efivar_getvar(struct iodev_efivar_req *req); +static int iodev_efivar_nextname(struct iodev_efivar_req *req); +static int iodev_efivar_setvar(struct iodev_efivar_req *req); + /* ARGSUSED */ int ioopen(struct cdev *dev __unused, int flags __unused, int fmt __unused, @@ -69,6 +75,7 @@ int ioioctl(struct cdev *dev __unused, u_long cmd, caddr_t data, int fflag __unused, struct thread *td __unused) { + struct iodev_efivar_req *efivar_req; struct iodev_pio_req *pio_req; int error; @@ -88,6 +95,24 @@ ioioctl(struct cdev *dev __unused, u_long cmd, caddr_t data, break; } break; + case IODEV_EFIVAR: + efivar_req = (struct iodev_efivar_req *)data; + efivar_req->result = 0; /* So it's well-defined */ + switch (efivar_req->access) { + case IODEV_EFIVAR_GETVAR: + error = iodev_efivar_getvar(efivar_req); + break; + case IODEV_EFIVAR_NEXTNAME: + error = iodev_efivar_nextname(efivar_req); + break; + case IODEV_EFIVAR_SETVAR: + error = iodev_efivar_setvar(efivar_req); + break; + default: + error = EINVAL; + break; + } + break; } return (error); @@ -158,3 +183,118 @@ iodev_pio_write(struct iodev_pio_req *req) return (0); } + +static int +iodev_efivar_getvar(struct iodev_efivar_req *req) +{ + void *data; + efi_char *name; + int error; + + if ((req->namesize & 1) != 0 || req->namesize < 4) + return (EINVAL); + if (req->datasize == 0) + return (EINVAL); + + /* + * Pre-zero the allocated memory and don't copy the last 2 bytes + * of the name. That should be the closing nul character (ucs-2) + * and if not, then we ensured a nul-terminating string. This is + * to protect the firmware and thus ourselves. + */ + name = malloc(req->namesize, M_TEMP, M_WAITOK | M_ZERO); + error = copyin(req->name, name, req->namesize - 2); + if (error) { + free(name, M_TEMP); + return (error); + } + + data = malloc(req->datasize, M_TEMP, M_WAITOK); + error = efi_var_get(name, &req->vendor, &req->attrib, &req->datasize, + data); + if (error == EOVERFLOW || error == ENOENT) { + req->result = error; + error = 0; + } + if (!error && !req->result) + error = copyout(data, req->data, req->datasize); + + free(data, M_TEMP); + free(name, M_TEMP); + return (error); +} + +static int +iodev_efivar_nextname(struct iodev_efivar_req *req) +{ + efi_char *name; + int error; + + /* Enforce a reasonable minimum size of the name buffer. */ + if (req->namesize < 4) + return (EINVAL); + + name = malloc(req->namesize, M_TEMP, M_WAITOK); + error = copyin(req->name, name, req->namesize); + if (error) { + free(name, M_TEMP); + return (error); + } + + error = efi_var_nextname(&req->namesize, name, &req->vendor); + if (error == EOVERFLOW || error == ENOENT) { + req->result = error; + error = 0; + } + if (!error && !req->result) + error = copyout(name, req->name, req->namesize); + + free(name, M_TEMP); + return (error); +} + +static int +iodev_efivar_setvar(struct iodev_efivar_req *req) +{ + void *data; + efi_char *name; + int error; + + if ((req->namesize & 1) != 0 || req->namesize < 4) + return (EINVAL); + + /* + * Pre-zero the allocated memory and don't copy the last 2 bytes + * of the name. That should be the closing nul character (ucs-2) + * and if not, then we ensured a nul-terminating string. This is + * to protect the firmware and thus ourselves. + */ + name = malloc(req->namesize, M_TEMP, M_WAITOK | M_ZERO); + error = copyin(req->name, name, req->namesize - 2); + if (error) { + free(name, M_TEMP); + return (error); + } + + if (req->datasize) { + data = malloc(req->datasize, M_TEMP, M_WAITOK); + error = copyin(req->data, data, req->datasize); + if (error) { + free(data, M_TEMP); + free(name, M_TEMP); + return (error); + } + } else + data = NULL; + + error = efi_var_set(name, &req->vendor, req->attrib, req->datasize, + data); + if (error == EAGAIN || error == ENOENT) { + req->result = error; + error = 0; + } + + free(data, M_TEMP); + free(name, M_TEMP); + return (error); +} diff --git a/sys/ia64/include/iodev.h b/sys/ia64/include/iodev.h index 11d05fc1c21d..6d2ae19d36dc 100644 --- a/sys/ia64/include/iodev.h +++ b/sys/ia64/include/iodev.h @@ -29,6 +29,8 @@ #ifndef _MACHINE_IODEV_H_ #define _MACHINE_IODEV_H_ +#include <sys/uuid.h> + struct iodev_pio_req { u_int access; #define IODEV_PIO_READ 0 @@ -40,6 +42,22 @@ struct iodev_pio_req { #define IODEV_PIO _IOWR('I', 0, struct iodev_pio_req) +struct iodev_efivar_req { + u_int access; +#define IODEV_EFIVAR_GETVAR 0 +#define IODEV_EFIVAR_NEXTNAME 1 +#define IODEV_EFIVAR_SETVAR 2 + u_int result; /* errno value */ + size_t namesize; + u_short *name; /* UCS-2 */ + struct uuid vendor; + uint32_t attrib; + size_t datasize; + void *data; +}; + +#define IODEV_EFIVAR _IOWR('I', 1, struct iodev_efivar_req) + #ifdef _KERNEL d_open_t ioopen; |