iodev_machdep.c revision 270296
1/*-
2 * Copyright (c) 2010 Marcel Moolenaar
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD: stable/10/sys/ia64/ia64/iodev_machdep.c 270296 2014-08-21 19:51:07Z emaste $");
29
30#include <sys/param.h>
31#include <sys/conf.h>
32#include <sys/efi.h>
33#include <sys/fcntl.h>
34#include <sys/ioccom.h>
35#include <sys/malloc.h>
36#include <sys/priv.h>
37#include <sys/proc.h>
38#include <sys/systm.h>
39
40#include <machine/bus.h>
41#include <machine/iodev.h>
42
43static int iodev_efivar_getvar(struct iodev_efivar_req *req);
44static int iodev_efivar_nextname(struct iodev_efivar_req *req);
45static int iodev_efivar_setvar(struct iodev_efivar_req *req);
46
47/* ARGSUSED */
48int
49iodev_open(struct thread *td __unused)
50{
51
52	return (0);
53}
54
55/* ARGSUSED */
56int
57iodev_close(struct thread *td __unused)
58{
59
60	return (0);
61}
62
63int
64iodev_ioctl(u_long cmd, caddr_t data)
65{
66	struct iodev_efivar_req *efivar_req;
67	int error;
68
69	switch (cmd) {
70	case IODEV_EFIVAR:
71		efivar_req = (struct iodev_efivar_req *)data;
72		efivar_req->result = 0;		/* So it's well-defined */
73		switch (efivar_req->access) {
74		case IODEV_EFIVAR_GETVAR:
75			error = iodev_efivar_getvar(efivar_req);
76			break;
77		case IODEV_EFIVAR_NEXTNAME:
78			error = iodev_efivar_nextname(efivar_req);
79			break;
80		case IODEV_EFIVAR_SETVAR:
81			error = iodev_efivar_setvar(efivar_req);
82			break;
83		default:
84			error = EINVAL;
85			break;
86		}
87		break;
88	default:
89		error = ENOIOCTL;
90	}
91
92	return (error);
93}
94
95static int
96iodev_efivar_getvar(struct iodev_efivar_req *req)
97{
98	void *data;
99	efi_char *name;
100	int error;
101
102	if ((req->namesize & 1) != 0 || req->namesize < 4)
103		return (EINVAL);
104	if (req->datasize == 0)
105		return (EINVAL);
106
107	/*
108	 * Pre-zero the allocated memory and don't copy the last 2 bytes
109	 * of the name. That should be the closing nul character (ucs-2)
110	 * and if not, then we ensured a nul-terminating string. This is
111	 * to protect the firmware and thus ourselves.
112	 */
113	name = malloc(req->namesize, M_TEMP, M_WAITOK | M_ZERO);
114	error = copyin(req->name, name, req->namesize - 2);
115	if (error) {
116		free(name, M_TEMP);
117		return (error);
118	}
119
120	data = malloc(req->datasize, M_TEMP, M_WAITOK);
121	error = efi_var_get(name, &req->vendor, &req->attrib, &req->datasize,
122	    data);
123	if (error == EOVERFLOW || error == ENOENT) {
124		req->result = error;
125		error = 0;
126	}
127	if (!error && !req->result)
128		error = copyout(data, req->data, req->datasize);
129
130	free(data, M_TEMP);
131	free(name, M_TEMP);
132	return (error);
133}
134
135static int
136iodev_efivar_nextname(struct iodev_efivar_req *req)
137{
138	efi_char *name;
139	int error;
140
141	/* Enforce a reasonable minimum size of the name buffer. */
142	if (req->namesize < 4)
143		return (EINVAL);
144
145	name = malloc(req->namesize, M_TEMP, M_WAITOK);
146	error = copyin(req->name, name, req->namesize);
147	if (error) {
148		free(name, M_TEMP);
149		return (error);
150	}
151
152	error = efi_var_nextname(&req->namesize, name, &req->vendor);
153	if (error == EOVERFLOW || error == ENOENT) {
154		req->result = error;
155		error = 0;
156	}
157	if (!error && !req->result)
158		error = copyout(name, req->name, req->namesize);
159
160	free(name, M_TEMP);
161	return (error);
162}
163
164static int
165iodev_efivar_setvar(struct iodev_efivar_req *req)
166{
167	void *data;
168	efi_char *name;
169	int error;
170
171	if ((req->namesize & 1) != 0 || req->namesize < 4)
172		return (EINVAL);
173
174	/*
175	 * Pre-zero the allocated memory and don't copy the last 2 bytes
176	 * of the name. That should be the closing nul character (ucs-2)
177	 * and if not, then we ensured a nul-terminating string. This is
178	 * to protect the firmware and thus ourselves.
179	 */
180	name = malloc(req->namesize, M_TEMP, M_WAITOK | M_ZERO);
181	error = copyin(req->name, name, req->namesize - 2);
182	if (error) {
183		free(name, M_TEMP);
184		return (error);
185	}
186
187	if (req->datasize) {
188		data = malloc(req->datasize, M_TEMP, M_WAITOK);
189		error = copyin(req->data, data, req->datasize);
190		if (error) {
191			free(data, M_TEMP);
192			free(name, M_TEMP);
193			return (error);
194		}
195	} else
196		data = NULL;
197
198	error = efi_var_set(name, &req->vendor, req->attrib, req->datasize,
199	    data);
200	if (error == EAGAIN || error == ENOENT) {
201		req->result = error;
202		error = 0;
203	}
204
205	free(data, M_TEMP);
206	free(name, M_TEMP);
207	return (error);
208}
209