#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/bus.h>
#include <sys/conf.h>
#include <sys/lock.h>
#include <sys/malloc.h>
#include <sys/module.h>
#include <sys/device.h>
#include <machine/efi.h>
#include <sys/efiio.h>
static struct lock efidev_lock = LOCK_INITIALIZER("efidev", 0, 0);
static d_ioctl_t efidev_ioctl;
static d_open_t efidev_open;
static d_close_t efidev_close;
static struct dev_ops efi_ops = {
{ "efi", 0, D_MPSAFE },
.d_open = efidev_open,
.d_close = efidev_close,
.d_ioctl = efidev_ioctl,
};
static int
efidev_open(struct dev_open_args *ap)
{
return 0;
}
static int
efidev_close(struct dev_close_args *ap)
{
return 0;
}
static int
efidev_ioctl(struct dev_ioctl_args *ap)
{
u_long cmd = ap->a_cmd;
caddr_t addr = ap->a_data;
int error;
lockmgr(&efidev_lock, LK_EXCLUSIVE);
switch (cmd) {
case EFIIOC_GET_TABLE:
{
struct efi_get_table_ioc *egtioc =
(struct efi_get_table_ioc *)addr;
error = efi_get_table(&egtioc->uuid, &egtioc->ptr);
break;
}
case EFIIOC_GET_TIME:
{
struct efi_tm *tm = (struct efi_tm *)addr;
error = efi_get_time(tm);
break;
}
case EFIIOC_SET_TIME:
{
struct efi_tm *tm = (struct efi_tm *)addr;
error = efi_set_time(tm);
break;
}
case EFIIOC_VAR_GET:
{
struct efi_var_ioc *ev = (struct efi_var_ioc *)addr;
void *data;
efi_char *name;
data = kmalloc(ev->datasize, M_TEMP, M_WAITOK);
name = kmalloc(ev->namesize, M_TEMP, M_WAITOK);
error = copyin(ev->name, name, ev->namesize);
if (error)
goto vg_out;
if (name[ev->namesize / sizeof(efi_char) - 1] != 0) {
error = EINVAL;
goto vg_out;
}
error = efi_var_get(name, &ev->vendor, &ev->attrib,
&ev->datasize, data);
if (error == 0) {
error = copyout(data, ev->data, ev->datasize);
} else if (error == EOVERFLOW) {
ev->data = NULL;
error = 0;
}
vg_out:
kfree(data, M_TEMP);
kfree(name, M_TEMP);
break;
}
case EFIIOC_VAR_NEXT:
{
struct efi_var_ioc *ev = (struct efi_var_ioc *)addr;
efi_char *name;
name = kmalloc(ev->namesize, M_TEMP, M_WAITOK);
error = copyin(ev->name, name, ev->namesize);
if (error)
goto vn_out;
error = efi_var_nextname(&ev->namesize, name, &ev->vendor);
if (error == 0) {
error = copyout(name, ev->name, ev->namesize);
} else if (error == EOVERFLOW) {
ev->name = NULL;
error = 0;
}
vn_out:
kfree(name, M_TEMP);
break;
}
case EFIIOC_VAR_SET:
{
struct efi_var_ioc *ev = (struct efi_var_ioc *)addr;
void *data = NULL;
efi_char *name;
if (ev->datasize > 0)
data = kmalloc(ev->datasize, M_TEMP, M_WAITOK);
name = kmalloc(ev->namesize, M_TEMP, M_WAITOK);
if (ev->datasize) {
error = copyin(ev->data, data, ev->datasize);
if (error)
goto vs_out;
}
error = copyin(ev->name, name, ev->namesize);
if (error)
goto vs_out;
if (name[ev->namesize / sizeof(efi_char) - 1] != 0) {
error = EINVAL;
goto vs_out;
}
error = efi_var_set(name, &ev->vendor, ev->attrib, ev->datasize,
data);
vs_out:
if (ev->datasize > 0)
kfree(data, M_TEMP);
kfree(name, M_TEMP);
break;
}
default:
error = ENOTTY;
break;
}
lockmgr(&efidev_lock, LK_RELEASE);
return (error);
}
static struct cdev *efidev;
static int
efidev_modevents(module_t m, int event, void *arg __unused)
{
switch (event) {
case MOD_LOAD:
efidev = make_dev(&efi_ops, 0, UID_ROOT, GID_WHEEL,
0700, "efi");
return (0);
case MOD_UNLOAD:
if (efidev != NULL)
destroy_dev(efidev);
efidev = NULL;
return (0);
case MOD_SHUTDOWN:
return (0);
default:
return (EOPNOTSUPP);
}
}
static moduledata_t efidev_moddata = {
.name = "efidev",
.evhand = efidev_modevents,
.priv = NULL,
};
DECLARE_MODULE(efidev, efidev_moddata, SI_SUB_PRE_DRIVERS, SI_ORDER_ANY);
MODULE_VERSION(efidev, 1);
MODULE_DEPEND(efidev, efirt, 1, 1, 1);