1202097Smarcel/*-
2202097Smarcel * Copyright (c) 2010 Marcel Moolenaar
3202097Smarcel * All rights reserved.
4202097Smarcel *
5202097Smarcel * Redistribution and use in source and binary forms, with or without
6202097Smarcel * modification, are permitted provided that the following conditions
7202097Smarcel * are met:
8202097Smarcel * 1. Redistributions of source code must retain the above copyright
9202097Smarcel *    notice, this list of conditions and the following disclaimer.
10202097Smarcel * 2. Redistributions in binary form must reproduce the above copyright
11202097Smarcel *    notice, this list of conditions and the following disclaimer in the
12202097Smarcel *    documentation and/or other materials provided with the distribution.
13202097Smarcel *
14202097Smarcel * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15202097Smarcel * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16202097Smarcel * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17202097Smarcel * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18202097Smarcel * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19202097Smarcel * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20202097Smarcel * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21202097Smarcel * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22202097Smarcel * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23202097Smarcel * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24202097Smarcel * SUCH DAMAGE.
25202097Smarcel */
26202097Smarcel
27202097Smarcel#include <sys/cdefs.h>
28202097Smarcel__FBSDID("$FreeBSD$");
29202097Smarcel
30202097Smarcel#include <sys/param.h>
31202097Smarcel#include <sys/conf.h>
32202097Smarcel#include <sys/fcntl.h>
33202097Smarcel#include <sys/ioccom.h>
34202273Smarcel#include <sys/malloc.h>
35202097Smarcel#include <sys/priv.h>
36202097Smarcel#include <sys/proc.h>
37202097Smarcel#include <sys/systm.h>
38202097Smarcel
39202097Smarcel#include <machine/bus.h>
40202273Smarcel#include <machine/efi.h>
41202097Smarcel#include <machine/iodev.h>
42202097Smarcel
43202273Smarcelstatic int iodev_efivar_getvar(struct iodev_efivar_req *req);
44202273Smarcelstatic int iodev_efivar_nextname(struct iodev_efivar_req *req);
45202273Smarcelstatic int iodev_efivar_setvar(struct iodev_efivar_req *req);
46202273Smarcel
47202097Smarcel/* ARGSUSED */
48202097Smarcelint
49207329Sattilioiodev_open(struct thread *td __unused)
50202097Smarcel{
51202097Smarcel
52207329Sattilio	return (0);
53202097Smarcel}
54202097Smarcel
55202097Smarcel/* ARGSUSED */
56202097Smarcelint
57207329Sattilioiodev_close(struct thread *td __unused)
58202097Smarcel{
59202097Smarcel
60202097Smarcel	return (0);
61202097Smarcel}
62202097Smarcel
63202097Smarcelint
64207329Sattilioiodev_ioctl(u_long cmd, caddr_t data)
65202097Smarcel{
66202273Smarcel	struct iodev_efivar_req *efivar_req;
67202097Smarcel	int error;
68202097Smarcel
69202097Smarcel	switch (cmd) {
70202273Smarcel	case IODEV_EFIVAR:
71202273Smarcel		efivar_req = (struct iodev_efivar_req *)data;
72202273Smarcel		efivar_req->result = 0;		/* So it's well-defined */
73202273Smarcel		switch (efivar_req->access) {
74202273Smarcel		case IODEV_EFIVAR_GETVAR:
75202273Smarcel			error = iodev_efivar_getvar(efivar_req);
76202273Smarcel			break;
77202273Smarcel		case IODEV_EFIVAR_NEXTNAME:
78202273Smarcel			error = iodev_efivar_nextname(efivar_req);
79202273Smarcel			break;
80202273Smarcel		case IODEV_EFIVAR_SETVAR:
81202273Smarcel			error = iodev_efivar_setvar(efivar_req);
82202273Smarcel			break;
83202273Smarcel		default:
84202273Smarcel			error = EINVAL;
85202273Smarcel			break;
86202273Smarcel		}
87202273Smarcel		break;
88207329Sattilio	default:
89207329Sattilio		error = ENOIOCTL;
90202097Smarcel	}
91202097Smarcel
92202097Smarcel	return (error);
93202097Smarcel}
94202097Smarcel
95202097Smarcelstatic int
96202273Smarceliodev_efivar_getvar(struct iodev_efivar_req *req)
97202273Smarcel{
98202273Smarcel	void *data;
99202273Smarcel	efi_char *name;
100202273Smarcel	int error;
101202273Smarcel
102202273Smarcel	if ((req->namesize & 1) != 0 || req->namesize < 4)
103202273Smarcel		return (EINVAL);
104202273Smarcel	if (req->datasize == 0)
105202273Smarcel		return (EINVAL);
106202273Smarcel
107202273Smarcel	/*
108202273Smarcel	 * Pre-zero the allocated memory and don't copy the last 2 bytes
109202273Smarcel	 * of the name. That should be the closing nul character (ucs-2)
110202273Smarcel	 * and if not, then we ensured a nul-terminating string. This is
111202273Smarcel	 * to protect the firmware and thus ourselves.
112202273Smarcel	 */
113202273Smarcel	name = malloc(req->namesize, M_TEMP, M_WAITOK | M_ZERO);
114202273Smarcel	error = copyin(req->name, name, req->namesize - 2);
115202273Smarcel	if (error) {
116202273Smarcel		free(name, M_TEMP);
117202273Smarcel		return (error);
118202273Smarcel	}
119202273Smarcel
120202273Smarcel	data = malloc(req->datasize, M_TEMP, M_WAITOK);
121202273Smarcel	error = efi_var_get(name, &req->vendor, &req->attrib, &req->datasize,
122202273Smarcel	    data);
123202273Smarcel	if (error == EOVERFLOW || error == ENOENT) {
124202273Smarcel		req->result = error;
125202273Smarcel		error = 0;
126202273Smarcel	}
127202273Smarcel	if (!error && !req->result)
128202273Smarcel		error = copyout(data, req->data, req->datasize);
129202273Smarcel
130202273Smarcel	free(data, M_TEMP);
131202273Smarcel	free(name, M_TEMP);
132202273Smarcel	return (error);
133202273Smarcel}
134202273Smarcel
135202273Smarcelstatic int
136202273Smarceliodev_efivar_nextname(struct iodev_efivar_req *req)
137202273Smarcel{
138202273Smarcel	efi_char *name;
139202273Smarcel	int error;
140202273Smarcel
141202273Smarcel	/* Enforce a reasonable minimum size of the name buffer. */
142202273Smarcel	if (req->namesize < 4)
143202273Smarcel		return (EINVAL);
144202273Smarcel
145202273Smarcel	name = malloc(req->namesize, M_TEMP, M_WAITOK);
146202273Smarcel	error = copyin(req->name, name, req->namesize);
147202273Smarcel	if (error) {
148202273Smarcel		free(name, M_TEMP);
149202273Smarcel		return (error);
150202273Smarcel	}
151202273Smarcel
152202273Smarcel	error = efi_var_nextname(&req->namesize, name, &req->vendor);
153202273Smarcel	if (error == EOVERFLOW || error == ENOENT) {
154202273Smarcel		req->result = error;
155202273Smarcel		error = 0;
156202273Smarcel	}
157202273Smarcel	if (!error && !req->result)
158202273Smarcel		error = copyout(name, req->name, req->namesize);
159202273Smarcel
160202273Smarcel	free(name, M_TEMP);
161202273Smarcel	return (error);
162202273Smarcel}
163202273Smarcel
164202273Smarcelstatic int
165202273Smarceliodev_efivar_setvar(struct iodev_efivar_req *req)
166202273Smarcel{
167202273Smarcel	void *data;
168202273Smarcel	efi_char *name;
169202273Smarcel	int error;
170202273Smarcel
171202273Smarcel	if ((req->namesize & 1) != 0 || req->namesize < 4)
172202273Smarcel		return (EINVAL);
173202273Smarcel
174202273Smarcel	/*
175202273Smarcel	 * Pre-zero the allocated memory and don't copy the last 2 bytes
176202273Smarcel	 * of the name. That should be the closing nul character (ucs-2)
177202273Smarcel	 * and if not, then we ensured a nul-terminating string. This is
178202273Smarcel	 * to protect the firmware and thus ourselves.
179202273Smarcel	 */
180202273Smarcel	name = malloc(req->namesize, M_TEMP, M_WAITOK | M_ZERO);
181202273Smarcel	error = copyin(req->name, name, req->namesize - 2);
182202273Smarcel	if (error) {
183202273Smarcel		free(name, M_TEMP);
184202273Smarcel		return (error);
185202273Smarcel	}
186202273Smarcel
187202273Smarcel	if (req->datasize) {
188202273Smarcel		data = malloc(req->datasize, M_TEMP, M_WAITOK);
189202273Smarcel		error = copyin(req->data, data, req->datasize);
190202273Smarcel		if (error) {
191202273Smarcel			free(data, M_TEMP);
192202273Smarcel			free(name, M_TEMP);
193202273Smarcel			return (error);
194202273Smarcel		}
195202273Smarcel	} else
196202273Smarcel		data = NULL;
197202273Smarcel
198202273Smarcel	error = efi_var_set(name, &req->vendor, req->attrib, req->datasize,
199202273Smarcel	    data);
200202273Smarcel	if (error == EAGAIN || error == ENOENT) {
201202273Smarcel		req->result = error;
202202273Smarcel		error = 0;
203202273Smarcel	}
204202273Smarcel
205202273Smarcel	free(data, M_TEMP);
206202273Smarcel	free(name, M_TEMP);
207202273Smarcel	return (error);
208202273Smarcel}
209