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
43 extern char bootmethod[16];
44
45 static int
rt_ok(void)46 rt_ok(void)
47 {
48
49 return (0);
50 }
51
52 static int
get_time(struct efi_tm * tm)53 get_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
79 static int
get_time_capabilities(struct efi_tmcap * tmcap)80 get_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
100 static int
set_time(struct efi_tm * tm)101 set_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
124 static int
var_get(efi_char * name,struct uuid * vendor,uint32_t * attrib,size_t * datasize,void * data)125 var_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
153 static int
var_nextname(size_t * namesize,efi_char * name,struct uuid * vendor)154 var_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
179 static int
var_set(efi_char * name,struct uuid * vendor,uint32_t attrib,size_t datasize,void * data)180 var_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
203 const 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
213 static int
modevents(module_t m,int event,void * arg __unused)214 modevents(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
245 static moduledata_t moddata = {
246 .name = "pvefirt",
247 .evhand = modevents,
248 .priv = NULL,
249 };
250 /* After fpuinitstate, before efidev */
251 DECLARE_MODULE(pvefirt, moddata, SI_SUB_DRIVERS, SI_ORDER_SECOND);
252 MODULE_VERSION(pvefirt, 1);
253