From 646420c8dcca3bac0716c94cd849473dc4bdff0f Mon Sep 17 00:00:00 2001 From: Marcel Moolenaar Date: Thu, 14 Jan 2010 02:48:39 +0000 Subject: 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. --- sys/ia64/ia64/iodev_machdep.c | 140 ++++++++++++++++++++++++++++++++++++++++++ sys/ia64/include/iodev.h | 18 ++++++ 2 files changed, 158 insertions(+) (limited to 'sys') 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 #include #include +#include #include #include #include #include +#include #include 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 + 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; -- cgit v1.2.3