15b210ee3SSascha Wildner /*-
25b210ee3SSascha Wildner * Copyright (c) 2016 Netflix, Inc.
35b210ee3SSascha Wildner * All rights reserved.
45b210ee3SSascha Wildner *
55b210ee3SSascha Wildner * Redistribution and use in source and binary forms, with or without
65b210ee3SSascha Wildner * modification, are permitted provided that the following conditions
75b210ee3SSascha Wildner * are met:
85b210ee3SSascha Wildner * 1. Redistributions of source code must retain the above copyright
95b210ee3SSascha Wildner * notice, this list of conditions and the following disclaimer
105b210ee3SSascha Wildner * in this position and unchanged.
115b210ee3SSascha Wildner * 2. Redistributions in binary form must reproduce the above copyright
125b210ee3SSascha Wildner * notice, this list of conditions and the following disclaimer in the
135b210ee3SSascha Wildner * documentation and/or other materials provided with the distribution.
145b210ee3SSascha Wildner *
155b210ee3SSascha Wildner * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
165b210ee3SSascha Wildner * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
175b210ee3SSascha Wildner * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
185b210ee3SSascha Wildner * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
195b210ee3SSascha Wildner * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
205b210ee3SSascha Wildner * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
215b210ee3SSascha Wildner * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
225b210ee3SSascha Wildner * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
235b210ee3SSascha Wildner * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
245b210ee3SSascha Wildner * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
255b210ee3SSascha Wildner *
265b210ee3SSascha Wildner * $FreeBSD: head/sys/dev/efidev/efidev.c 307391 2016-10-16 06:07:43Z kib $
275b210ee3SSascha Wildner */
285b210ee3SSascha Wildner
295b210ee3SSascha Wildner #include <sys/param.h>
305b210ee3SSascha Wildner #include <sys/systm.h>
315b210ee3SSascha Wildner #include <sys/kernel.h>
325b210ee3SSascha Wildner #include <sys/bus.h>
335b210ee3SSascha Wildner #include <sys/conf.h>
345b210ee3SSascha Wildner #include <sys/lock.h>
355b210ee3SSascha Wildner #include <sys/malloc.h>
365b210ee3SSascha Wildner #include <sys/module.h>
375b210ee3SSascha Wildner #include <sys/device.h>
385b210ee3SSascha Wildner
395b210ee3SSascha Wildner #include <machine/efi.h>
405b210ee3SSascha Wildner #include <sys/efiio.h>
415b210ee3SSascha Wildner
42481d12aaSMatthew Dillon static struct lock efidev_lock = LOCK_INITIALIZER("efidev", 0, 0);
43481d12aaSMatthew Dillon
445b210ee3SSascha Wildner static d_ioctl_t efidev_ioctl;
455b210ee3SSascha Wildner static d_open_t efidev_open;
465b210ee3SSascha Wildner static d_close_t efidev_close;
475b210ee3SSascha Wildner
485b210ee3SSascha Wildner static struct dev_ops efi_ops = {
49481d12aaSMatthew Dillon { "efi", 0, D_MPSAFE },
505b210ee3SSascha Wildner .d_open = efidev_open,
515b210ee3SSascha Wildner .d_close = efidev_close,
525b210ee3SSascha Wildner .d_ioctl = efidev_ioctl,
535b210ee3SSascha Wildner };
545b210ee3SSascha Wildner
555b210ee3SSascha Wildner static int
efidev_open(struct dev_open_args * ap)565b210ee3SSascha Wildner efidev_open(struct dev_open_args *ap)
575b210ee3SSascha Wildner {
585b210ee3SSascha Wildner return 0;
595b210ee3SSascha Wildner }
605b210ee3SSascha Wildner
615b210ee3SSascha Wildner static int
efidev_close(struct dev_close_args * ap)625b210ee3SSascha Wildner efidev_close(struct dev_close_args *ap)
635b210ee3SSascha Wildner {
645b210ee3SSascha Wildner return 0;
655b210ee3SSascha Wildner }
665b210ee3SSascha Wildner
675b210ee3SSascha Wildner static int
efidev_ioctl(struct dev_ioctl_args * ap)685b210ee3SSascha Wildner efidev_ioctl(struct dev_ioctl_args *ap)
695b210ee3SSascha Wildner {
705b210ee3SSascha Wildner u_long cmd = ap->a_cmd;
715b210ee3SSascha Wildner caddr_t addr = ap->a_data;
725b210ee3SSascha Wildner int error;
735b210ee3SSascha Wildner
74481d12aaSMatthew Dillon lockmgr(&efidev_lock, LK_EXCLUSIVE);
75481d12aaSMatthew Dillon
765b210ee3SSascha Wildner switch (cmd) {
775b210ee3SSascha Wildner case EFIIOC_GET_TABLE:
785b210ee3SSascha Wildner {
795b210ee3SSascha Wildner struct efi_get_table_ioc *egtioc =
805b210ee3SSascha Wildner (struct efi_get_table_ioc *)addr;
815b210ee3SSascha Wildner
825b210ee3SSascha Wildner error = efi_get_table(&egtioc->uuid, &egtioc->ptr);
835b210ee3SSascha Wildner break;
845b210ee3SSascha Wildner }
855b210ee3SSascha Wildner case EFIIOC_GET_TIME:
865b210ee3SSascha Wildner {
875b210ee3SSascha Wildner struct efi_tm *tm = (struct efi_tm *)addr;
885b210ee3SSascha Wildner
895b210ee3SSascha Wildner error = efi_get_time(tm);
905b210ee3SSascha Wildner break;
915b210ee3SSascha Wildner }
925b210ee3SSascha Wildner case EFIIOC_SET_TIME:
935b210ee3SSascha Wildner {
945b210ee3SSascha Wildner struct efi_tm *tm = (struct efi_tm *)addr;
955b210ee3SSascha Wildner
965b210ee3SSascha Wildner error = efi_set_time(tm);
975b210ee3SSascha Wildner break;
985b210ee3SSascha Wildner }
995b210ee3SSascha Wildner case EFIIOC_VAR_GET:
1005b210ee3SSascha Wildner {
1015b210ee3SSascha Wildner struct efi_var_ioc *ev = (struct efi_var_ioc *)addr;
1025b210ee3SSascha Wildner void *data;
1035b210ee3SSascha Wildner efi_char *name;
1045b210ee3SSascha Wildner
1055b210ee3SSascha Wildner data = kmalloc(ev->datasize, M_TEMP, M_WAITOK);
1065b210ee3SSascha Wildner name = kmalloc(ev->namesize, M_TEMP, M_WAITOK);
1075b210ee3SSascha Wildner error = copyin(ev->name, name, ev->namesize);
1085b210ee3SSascha Wildner if (error)
1095b210ee3SSascha Wildner goto vg_out;
1105b210ee3SSascha Wildner if (name[ev->namesize / sizeof(efi_char) - 1] != 0) {
1115b210ee3SSascha Wildner error = EINVAL;
1125b210ee3SSascha Wildner goto vg_out;
1135b210ee3SSascha Wildner }
1145b210ee3SSascha Wildner
1155b210ee3SSascha Wildner error = efi_var_get(name, &ev->vendor, &ev->attrib,
1165b210ee3SSascha Wildner &ev->datasize, data);
1175b210ee3SSascha Wildner
1185b210ee3SSascha Wildner if (error == 0) {
1195b210ee3SSascha Wildner error = copyout(data, ev->data, ev->datasize);
1205b210ee3SSascha Wildner } else if (error == EOVERFLOW) {
1215b210ee3SSascha Wildner /*
1225b210ee3SSascha Wildner * Pass back the size we really need, but
1235b210ee3SSascha Wildner * convert the error to 0 so the copyout
1245b210ee3SSascha Wildner * happens. datasize was updated in the
1255b210ee3SSascha Wildner * efi_var_get call.
1265b210ee3SSascha Wildner */
1275b210ee3SSascha Wildner ev->data = NULL;
1285b210ee3SSascha Wildner error = 0;
1295b210ee3SSascha Wildner }
1305b210ee3SSascha Wildner vg_out:
1315b210ee3SSascha Wildner kfree(data, M_TEMP);
1325b210ee3SSascha Wildner kfree(name, M_TEMP);
1335b210ee3SSascha Wildner break;
1345b210ee3SSascha Wildner }
1355b210ee3SSascha Wildner case EFIIOC_VAR_NEXT:
1365b210ee3SSascha Wildner {
1375b210ee3SSascha Wildner struct efi_var_ioc *ev = (struct efi_var_ioc *)addr;
1385b210ee3SSascha Wildner efi_char *name;
1395b210ee3SSascha Wildner
1405b210ee3SSascha Wildner name = kmalloc(ev->namesize, M_TEMP, M_WAITOK);
1415b210ee3SSascha Wildner error = copyin(ev->name, name, ev->namesize);
1425b210ee3SSascha Wildner if (error)
1435b210ee3SSascha Wildner goto vn_out;
1445b210ee3SSascha Wildner /* Note: namesize is the buffer size, not the string lenght */
1455b210ee3SSascha Wildner
1465b210ee3SSascha Wildner error = efi_var_nextname(&ev->namesize, name, &ev->vendor);
1475b210ee3SSascha Wildner if (error == 0) {
1485b210ee3SSascha Wildner error = copyout(name, ev->name, ev->namesize);
1495b210ee3SSascha Wildner } else if (error == EOVERFLOW) {
1505b210ee3SSascha Wildner ev->name = NULL;
1515b210ee3SSascha Wildner error = 0;
1525b210ee3SSascha Wildner }
1535b210ee3SSascha Wildner vn_out:
1545b210ee3SSascha Wildner kfree(name, M_TEMP);
1555b210ee3SSascha Wildner break;
1565b210ee3SSascha Wildner }
1575b210ee3SSascha Wildner case EFIIOC_VAR_SET:
1585b210ee3SSascha Wildner {
1595b210ee3SSascha Wildner struct efi_var_ioc *ev = (struct efi_var_ioc *)addr;
1605b210ee3SSascha Wildner void *data = NULL;
1615b210ee3SSascha Wildner efi_char *name;
1625b210ee3SSascha Wildner
1635b210ee3SSascha Wildner /* datasize == 0 -> delete (more or less) */
1645b210ee3SSascha Wildner if (ev->datasize > 0)
1655b210ee3SSascha Wildner data = kmalloc(ev->datasize, M_TEMP, M_WAITOK);
1665b210ee3SSascha Wildner name = kmalloc(ev->namesize, M_TEMP, M_WAITOK);
1675b210ee3SSascha Wildner if (ev->datasize) {
1685b210ee3SSascha Wildner error = copyin(ev->data, data, ev->datasize);
1695b210ee3SSascha Wildner if (error)
1705b210ee3SSascha Wildner goto vs_out;
1715b210ee3SSascha Wildner }
1725b210ee3SSascha Wildner error = copyin(ev->name, name, ev->namesize);
1735b210ee3SSascha Wildner if (error)
1745b210ee3SSascha Wildner goto vs_out;
1755b210ee3SSascha Wildner if (name[ev->namesize / sizeof(efi_char) - 1] != 0) {
1765b210ee3SSascha Wildner error = EINVAL;
1775b210ee3SSascha Wildner goto vs_out;
1785b210ee3SSascha Wildner }
1795b210ee3SSascha Wildner
1805b210ee3SSascha Wildner error = efi_var_set(name, &ev->vendor, ev->attrib, ev->datasize,
1815b210ee3SSascha Wildner data);
1825b210ee3SSascha Wildner vs_out:
183*be1ad8f1SSascha Wildner if (ev->datasize > 0)
1845b210ee3SSascha Wildner kfree(data, M_TEMP);
1855b210ee3SSascha Wildner kfree(name, M_TEMP);
1865b210ee3SSascha Wildner break;
1875b210ee3SSascha Wildner }
1885b210ee3SSascha Wildner default:
1895b210ee3SSascha Wildner error = ENOTTY;
1905b210ee3SSascha Wildner break;
1915b210ee3SSascha Wildner }
1925b210ee3SSascha Wildner
193481d12aaSMatthew Dillon lockmgr(&efidev_lock, LK_RELEASE);
194481d12aaSMatthew Dillon
1955b210ee3SSascha Wildner return (error);
1965b210ee3SSascha Wildner }
1975b210ee3SSascha Wildner
1985b210ee3SSascha Wildner static struct cdev *efidev;
1995b210ee3SSascha Wildner
2005b210ee3SSascha Wildner static int
efidev_modevents(module_t m,int event,void * arg __unused)2015b210ee3SSascha Wildner efidev_modevents(module_t m, int event, void *arg __unused)
2025b210ee3SSascha Wildner {
2035b210ee3SSascha Wildner switch (event) {
2045b210ee3SSascha Wildner case MOD_LOAD:
2055b210ee3SSascha Wildner efidev = make_dev(&efi_ops, 0, UID_ROOT, GID_WHEEL,
2065b210ee3SSascha Wildner 0700, "efi");
2075b210ee3SSascha Wildner return (0);
2085b210ee3SSascha Wildner
2095b210ee3SSascha Wildner case MOD_UNLOAD:
2105b210ee3SSascha Wildner if (efidev != NULL)
2115b210ee3SSascha Wildner destroy_dev(efidev);
2125b210ee3SSascha Wildner efidev = NULL;
2135b210ee3SSascha Wildner return (0);
2145b210ee3SSascha Wildner
2155b210ee3SSascha Wildner case MOD_SHUTDOWN:
2165b210ee3SSascha Wildner return (0);
2175b210ee3SSascha Wildner
2185b210ee3SSascha Wildner default:
2195b210ee3SSascha Wildner return (EOPNOTSUPP);
2205b210ee3SSascha Wildner }
2215b210ee3SSascha Wildner }
2225b210ee3SSascha Wildner
2235b210ee3SSascha Wildner static moduledata_t efidev_moddata = {
2245b210ee3SSascha Wildner .name = "efidev",
2255b210ee3SSascha Wildner .evhand = efidev_modevents,
2265b210ee3SSascha Wildner .priv = NULL,
2275b210ee3SSascha Wildner };
2285b210ee3SSascha Wildner
2295b210ee3SSascha Wildner DECLARE_MODULE(efidev, efidev_moddata, SI_SUB_PRE_DRIVERS, SI_ORDER_ANY);
2305b210ee3SSascha Wildner MODULE_VERSION(efidev, 1);
2315b210ee3SSascha Wildner MODULE_DEPEND(efidev, efirt, 1, 1, 1);
232