summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormarcel <marcel@FreeBSD.org>2010-01-14 02:48:39 +0000
committermarcel <marcel@FreeBSD.org>2010-01-14 02:48:39 +0000
commit47afb8a1a7c815fd4e3f38832563ee8e7bae7e89 (patch)
tree5ac0aa1deb46d7436ead499851d538d9836dfaaf
parent109b8dd039a958ab901e77f5fbd2762657cac380 (diff)
downloadFreeBSD-src-47afb8a1a7c815fd4e3f38832563ee8e7bae7e89.zip
FreeBSD-src-47afb8a1a7c815fd4e3f38832563ee8e7bae7e89.tar.gz
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.
-rw-r--r--sys/ia64/ia64/iodev_machdep.c140
-rw-r--r--sys/ia64/include/iodev.h18
2 files changed, 158 insertions, 0 deletions
diff --git a/sys/ia64/ia64/iodev_machdep.c b/sys/ia64/ia64/iodev_machdep.c
index 498a042..d255aae 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 11d05fc..6d2ae19 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;
OpenPOWER on IntegriCloud