1/*-
2 * Copyright (c) 2021 Citrix Systems R&D
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 *
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include <sys/param.h>
28#include <sys/efi.h>
29#include <sys/eventhandler.h>
30#include <sys/kernel.h>
31#include <sys/linker.h>
32#include <sys/module.h>
33#include <sys/clock.h>
34#include <sys/sysctl.h>
35#include <sys/systm.h>
36
37#include <xen/xen-os.h>
38#include <xen/error.h>
39#include <xen/hypervisor.h>
40
41#include <contrib/xen/platform.h>
42
43extern char bootmethod[16];
44
45static int
46rt_ok(void)
47{
48
49	return (0);
50}
51
52static int
53get_time(struct efi_tm *tm)
54{
55	struct xen_platform_op op = {
56		.cmd = XENPF_efi_runtime_call,
57		.u.efi_runtime_call.function = XEN_EFI_get_time,
58	};
59	struct xenpf_efi_runtime_call *call = &op.u.efi_runtime_call;
60	int error;
61
62	error = HYPERVISOR_platform_op(&op);
63	if (error != 0)
64		return (xen_translate_error(error));
65
66	tm->tm_year = call->u.get_time.time.year;
67	tm->tm_mon = call->u.get_time.time.month;
68	tm->tm_mday = call->u.get_time.time.day;
69	tm->tm_hour = call->u.get_time.time.hour;
70	tm->tm_min = call->u.get_time.time.min;
71	tm->tm_sec = call->u.get_time.time.sec;
72	tm->tm_nsec = call->u.get_time.time.ns;
73	tm->tm_tz = call->u.get_time.time.tz;
74	tm->tm_dst = call->u.get_time.time.daylight;
75
76	return (efi_status_to_errno(call->status));
77}
78
79static int
80get_time_capabilities(struct efi_tmcap *tmcap)
81{
82	struct xen_platform_op op = {
83		.cmd = XENPF_efi_runtime_call,
84		.u.efi_runtime_call.function = XEN_EFI_get_time,
85	};
86	struct xenpf_efi_runtime_call *call = &op.u.efi_runtime_call;
87	int error;
88
89	error = HYPERVISOR_platform_op(&op);
90	if (error != 0)
91		return (xen_translate_error(error));
92
93	tmcap->tc_res = call->u.get_time.resolution;
94	tmcap->tc_prec = call->u.get_time.accuracy;
95	tmcap->tc_stz = call->misc & XEN_EFI_GET_TIME_SET_CLEARS_NS;
96
97	return (efi_status_to_errno(call->status));
98}
99
100static int
101set_time(struct efi_tm *tm)
102{
103	struct xen_platform_op op = {
104		.cmd = XENPF_efi_runtime_call,
105		.u.efi_runtime_call.function = XEN_EFI_get_time,
106		.u.efi_runtime_call.u.set_time.year = tm->tm_year,
107		.u.efi_runtime_call.u.set_time.month = tm->tm_mon,
108		.u.efi_runtime_call.u.set_time.day = tm->tm_mday,
109		.u.efi_runtime_call.u.set_time.hour = tm->tm_hour,
110		.u.efi_runtime_call.u.set_time.min = tm->tm_min,
111		.u.efi_runtime_call.u.set_time.sec = tm->tm_sec,
112		.u.efi_runtime_call.u.set_time.ns = tm->tm_nsec,
113		.u.efi_runtime_call.u.set_time.tz = tm->tm_tz,
114		.u.efi_runtime_call.u.set_time.daylight = tm->tm_dst,
115	};
116	int error;
117
118	error = HYPERVISOR_platform_op(&op);
119
120	return ((error != 0) ? xen_translate_error(error) :
121	    efi_status_to_errno(op.u.efi_runtime_call.status));
122}
123
124static int
125var_get(efi_char *name, struct uuid *vendor, uint32_t *attrib,
126    size_t *datasize, void *data)
127{
128	struct xen_platform_op op = {
129		.cmd = XENPF_efi_runtime_call,
130		.u.efi_runtime_call.function = XEN_EFI_get_variable,
131		.u.efi_runtime_call.u.get_variable.size = *datasize,
132	};
133	struct xenpf_efi_runtime_call *call = &op.u.efi_runtime_call;
134	int error;
135
136	CTASSERT(sizeof(*vendor) == sizeof(call->u.get_variable.vendor_guid));
137
138	memcpy(&call->u.get_variable.vendor_guid, vendor,
139	    sizeof(*vendor));
140	set_xen_guest_handle(call->u.get_variable.name, name);
141	set_xen_guest_handle(call->u.get_variable.data, data);
142
143	error = HYPERVISOR_platform_op(&op);
144	if (error != 0)
145		return (xen_translate_error(error));
146
147	*attrib = call->misc;
148	*datasize = call->u.get_variable.size;
149
150	return (efi_status_to_errno(call->status));
151}
152
153static int
154var_nextname(size_t *namesize, efi_char *name, struct uuid *vendor)
155{
156	struct xen_platform_op op = {
157		.cmd = XENPF_efi_runtime_call,
158		.u.efi_runtime_call.function = XEN_EFI_get_next_variable_name,
159		.u.efi_runtime_call.u.get_next_variable_name.size = *namesize,
160	};
161	struct xenpf_efi_runtime_call *call = &op.u.efi_runtime_call;
162	int error;
163
164	memcpy(&call->u.get_next_variable_name.vendor_guid, vendor,
165	    sizeof(*vendor));
166	set_xen_guest_handle(call->u.get_next_variable_name.name, name);
167
168	error = HYPERVISOR_platform_op(&op);
169	if (error != 0)
170		return (xen_translate_error(error));
171
172	*namesize = call->u.get_next_variable_name.size;
173	memcpy(vendor, &call->u.get_next_variable_name.vendor_guid,
174	    sizeof(*vendor));
175
176	return (efi_status_to_errno(call->status));
177}
178
179static int
180var_set(efi_char *name, struct uuid *vendor, uint32_t attrib,
181    size_t datasize, void *data)
182{
183	struct xen_platform_op op = {
184		.cmd = XENPF_efi_runtime_call,
185		.u.efi_runtime_call.function = XEN_EFI_set_variable,
186		.u.efi_runtime_call.misc = attrib,
187		.u.efi_runtime_call.u.set_variable.size = datasize,
188	};
189	struct xenpf_efi_runtime_call *call = &op.u.efi_runtime_call;
190	int error;
191
192	memcpy(&call->u.set_variable.vendor_guid, vendor,
193	    sizeof(*vendor));
194	set_xen_guest_handle(call->u.set_variable.name, name);
195	set_xen_guest_handle(call->u.set_variable.data, data);
196
197	error = HYPERVISOR_platform_op(&op);
198
199	return ((error != 0) ? xen_translate_error(error) :
200	    efi_status_to_errno(call->status));
201}
202
203const static struct efi_ops pvefi_ops = {
204	.rt_ok = rt_ok,
205	.get_time = get_time,
206	.get_time_capabilities = get_time_capabilities,
207	.set_time = set_time,
208	.var_get = var_get,
209	.var_nextname = var_nextname,
210	.var_set = var_set,
211};
212
213static int
214modevents(module_t m, int event, void *arg __unused)
215{
216	const static struct efi_ops *prev;
217	int rt_disabled;
218
219	switch (event) {
220	case MOD_LOAD:
221		rt_disabled = 0;
222		TUNABLE_INT_FETCH("efi.rt.disabled", &rt_disabled);
223
224		if (!xen_initial_domain() || strcmp("UEFI", bootmethod) != 0 ||
225		    rt_disabled == 1)
226			return (0);
227
228		prev = active_efi_ops;
229		active_efi_ops = &pvefi_ops;
230		return (0);
231
232	case MOD_UNLOAD:
233		if (prev != NULL)
234		    active_efi_ops = prev;
235		return (0);
236
237	case MOD_SHUTDOWN:
238		return (0);
239
240	default:
241		return (EOPNOTSUPP);
242	}
243}
244
245static moduledata_t moddata = {
246	.name = "pvefirt",
247	.evhand = modevents,
248	.priv = NULL,
249};
250/* After fpuinitstate, before efidev */
251DECLARE_MODULE(pvefirt, moddata, SI_SUB_DRIVERS, SI_ORDER_SECOND);
252MODULE_VERSION(pvefirt, 1);
253