1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2013-2024, Juniper Networks, Inc.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
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 AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28 #include <sys/cdefs.h>
29
30 #include <sys/param.h>
31 #include <sys/systm.h>
32 #include <sys/conf.h>
33 #include <sys/kernel.h>
34 #include <sys/limits.h>
35 #include <sys/bus.h>
36 #include <sys/sbuf.h>
37 #include <sys/errno.h>
38 #include <sys/module.h>
39
40 #include <x86/vmware.h>
41 #include <x86/vmware_guestrpc.h>
42
43 /* GuestRPC Subcommands */
44 #define VMW_HVGUESTRPC_OPEN 0x00
45 #define VMW_HVGUESTRPC_SEND_LEN 0x01
46 #define VMW_HVGUESTRPC_SEND_DATA 0x02
47 #define VMW_HVGUESTRPC_RECV_LEN 0x03
48 #define VMW_HVGUESTRPC_RECV_DATA 0x04
49 #define VMW_HVGUESTRPC_FINISH_RECV 0x05
50 #define VMW_HVGUESTRPC_CLOSE 0x06
51 /* GuestRPC Parameters */
52 #define VMW_HVGUESTRPC_OPEN_MAGIC 0x49435052
53 /* GuestRPC Status */
54 #define VMW_HVGUESTRPC_FAILURE 0x00000000
55 #define VMW_HVGUESTRPC_OPEN_SUCCESS 0x00010000
56 #define VMW_HVGUESTRPC_SEND_LEN_SUCCESS 0x00810000
57 #define VMW_HVGUESTRPC_SEND_DATA_SUCCESS 0x00010000
58 #define VMW_HVGUESTRPC_RECV_LEN_SUCCESS 0x00830000
59 #define VMW_HVGUESTRPC_RECV_DATA_SUCCESS 0x00010000
60 #define VMW_HVGUESTRPC_FINISH_RECV_SUCCESS 0x00010000
61 #define VMW_HVGUESTRPC_CLOSE_SUCCESS 0x00010000
62
63 #define VMW_GUESTRPC_EBX(_p) ((_p)[1])
64 #define VMW_GUESTRPC_EDXHI(_p) ((_p)[3] >> 16)
65 #define VMW_GUESTRPC_STATUS(_p) ((_p)[2])
66
67 static __inline void
vmware_guestrpc(int chan,uint16_t subcmd,uint32_t param,u_int * p)68 vmware_guestrpc(int chan, uint16_t subcmd, uint32_t param, u_int *p)
69 {
70
71 #ifdef DEBUG_VMGUESTRPC
72 printf("%s(%d, %#x, %#x, %p)\n", __func__, chan, subcmd, param, p);
73 #endif
74 vmware_hvcall(chan, VMW_HVCMD_GUESTRPC | (subcmd << 16), param, p);
75 #ifdef DEBUG_VMGUESTRPC
76 printf("p[0] = %#x\n", p[0]);
77 printf("p[1] = %#x\n", p[1]);
78 printf("p[2] = %#x\n", p[2]);
79 printf("p[3] = %#x\n", p[3]);
80 #endif
81 }
82
83 /*
84 * Start a GuestRPC request
85 *
86 * Channel number is returned in the EDXHI parameter.
87 *
88 * This channel number must be used in successive GuestRPC requests for
89 * sending and receiving RPC data.
90 */
91 static int
vmware_guestrpc_open(void)92 vmware_guestrpc_open(void)
93 {
94 u_int p[4];
95
96 vmware_guestrpc(0, VMW_HVGUESTRPC_OPEN, VMW_HVGUESTRPC_OPEN_MAGIC,
97 p);
98 if (VMW_GUESTRPC_STATUS(p) != VMW_HVGUESTRPC_OPEN_SUCCESS)
99 return (-1);
100
101 return (VMW_GUESTRPC_EDXHI(p));
102 }
103
104 /*
105 * Send the length of the GuestRPC request
106 *
107 * In a GuestRPC request, the total length of the request must be sent
108 * before any data can be sent.
109 */
110 static int
vmware_guestrpc_send_len(int channel,size_t len)111 vmware_guestrpc_send_len(int channel, size_t len)
112 {
113 u_int p[4];
114
115 vmware_guestrpc(channel, VMW_HVGUESTRPC_SEND_LEN, len, p);
116 if (VMW_GUESTRPC_STATUS(p) != VMW_HVGUESTRPC_SEND_LEN_SUCCESS)
117 return (-1);
118
119 return (0);
120 }
121
122 /*
123 * Send the data for the GuestRPC request
124 *
125 * The total length of the GuestRPC request must be sent before any data.
126 * Data is sent 32-bit values at a time and therefore may require multiple
127 * calls to send all the data.
128 */
129 static int
vmware_guestrpc_send_data(int channel,uint32_t data)130 vmware_guestrpc_send_data(int channel, uint32_t data)
131 {
132 u_int p[4];
133
134 vmware_guestrpc(channel, VMW_HVGUESTRPC_SEND_DATA, data, p);
135 if (VMW_GUESTRPC_STATUS(p) != VMW_HVGUESTRPC_SEND_DATA_SUCCESS)
136 return (-1);
137
138 return (0);
139 }
140
141 /*
142 * Receive the length of the GuestRPC reply.
143 *
144 * Length of the reply data is returned in the EBX parameter.
145 * The reply identifier is returned in the EDXHI parameter.
146 *
147 * The reply identifier must be used as the GuestRPC parameter in calls
148 * to vmware_guestrpc_recv_data()
149 */
150 static int
vmware_guestrpc_recv_len(int channel,size_t * lenp)151 vmware_guestrpc_recv_len(int channel, size_t *lenp)
152 {
153 u_int p[4];
154
155 vmware_guestrpc(channel, VMW_HVGUESTRPC_RECV_LEN, 0, p);
156 if (VMW_GUESTRPC_STATUS(p) != VMW_HVGUESTRPC_RECV_LEN_SUCCESS)
157 return (-1);
158
159 *lenp = VMW_GUESTRPC_EBX(p);
160 return (VMW_GUESTRPC_EDXHI(p));
161 }
162
163 /*
164 * Receive the GuestRPC reply data.
165 *
166 * Data is received in 32-bit values at a time and therefore may
167 * require multiple requests to get all the data.
168 */
169 static int
vmware_guestrpc_recv_data(int channel,int id,uint32_t * datap)170 vmware_guestrpc_recv_data(int channel, int id, uint32_t *datap)
171 {
172 u_int p[4];
173
174 vmware_guestrpc(channel, VMW_HVGUESTRPC_RECV_DATA, id, p);
175 if (VMW_GUESTRPC_STATUS(p) != VMW_HVGUESTRPC_RECV_DATA_SUCCESS)
176 return (-1);
177
178 *datap = VMW_GUESTRPC_EBX(p);
179 return (0);
180 }
181
182 /*
183 * Close the GuestRPC channel.
184 */
185 static int
vmware_guestrpc_close(int channel)186 vmware_guestrpc_close(int channel)
187 {
188 u_int p[4];
189
190 vmware_guestrpc(channel, VMW_HVGUESTRPC_CLOSE, 0, p);
191 if (VMW_GUESTRPC_STATUS(p) != VMW_HVGUESTRPC_CLOSE_SUCCESS)
192 return (-1);
193
194 return (0);
195 }
196
197 /*
198 * Send a GuestRPC command.
199 */
200 int
vmware_guestrpc_cmd(struct sbuf * sbufp)201 vmware_guestrpc_cmd(struct sbuf *sbufp)
202 {
203 char *buf;
204 size_t cnt, len;
205 int chan, id, status;
206 uint32_t data;
207
208 /* Make sure we are running under VMware hypervisor */
209 if (vm_guest != VM_GUEST_VMWARE)
210 return (ENXIO);
211
212 /* Open the GuestRPC channel */
213 chan = vmware_guestrpc_open();
214 if (chan == -1)
215 return (EIO);
216
217 /* Send the length */
218 buf = sbuf_data(sbufp);
219 len = sbuf_len(sbufp);
220 status = vmware_guestrpc_send_len(chan, len);
221 if (status == -1)
222 goto done;
223
224 /* Send the data */
225 while (len > 0) {
226 data = 0;
227 cnt = min(4, len);
228 memcpy(&data, buf, cnt);
229 status = vmware_guestrpc_send_data(chan, data);
230 if (status == -1)
231 goto done;
232 buf += cnt;
233 len -= cnt;
234 }
235
236 /* Receive the length of the reply data */
237 id = vmware_guestrpc_recv_len(chan, &len);
238 if (id == -1)
239 goto done;
240
241 /* Receive the reply data */
242 sbuf_clear(sbufp);
243 while (len > 0) {
244 status = vmware_guestrpc_recv_data(chan, id, &data);
245 if (status == -1)
246 goto done;
247 sbuf_bcat(sbufp, &data, 4);
248 len -= min(4, len);
249 }
250
251 done:
252 /* Close the GuestRPC channel */
253 vmware_guestrpc_close(chan);
254 return (status == -1 ? EIO : 0);
255 }
256
257 /*
258 * Set guest information key/value pair
259 */
260 int
vmware_guestrpc_set_guestinfo(const char * keyword,const char * val)261 vmware_guestrpc_set_guestinfo(const char *keyword, const char *val)
262 {
263 struct sbuf sb;
264 char *buf;
265 int error;
266
267 #ifdef DEBUG_VMGUESTRPC
268 printf("%s: %s=%s\n", __func__, keyword, val);
269 #endif
270
271 /* Send "info-set" GuestRPC command */
272 sbuf_new(&sb, NULL, 256, SBUF_AUTOEXTEND);
273 sbuf_printf(&sb, "info-set guestinfo.fbsd.%s %s", keyword, val);
274 sbuf_trim(&sb);
275 sbuf_finish(&sb);
276
277 error = vmware_guestrpc_cmd(&sb);
278 if (error)
279 return (error);
280
281 sbuf_finish(&sb);
282 buf = sbuf_data(&sb);
283
284 #ifdef DEBUG_VMGUESTRPC
285 printf("%s: result: %s\n", __func__, buf);
286 #endif
287
288 /* Buffer will contain 1 on sucess or 0 on failure */
289 return ((buf[0] == '0') ? EINVAL : 0);
290 }
291
292 /*
293 * Get guest information key/value pair.
294 */
295 int
vmware_guestrpc_get_guestinfo(const char * keyword,struct sbuf * sbufp)296 vmware_guestrpc_get_guestinfo(const char *keyword, struct sbuf *sbufp)
297 {
298 struct sbuf sb;
299 char *buf;
300 int error;
301
302 #ifdef DEBUG_VMGUESTRPC
303 printf("%s: %s\n", __func__, keyword);
304 #endif
305
306 /* Send "info-get" GuestRPC command */
307 sbuf_new(&sb, NULL, 256, SBUF_AUTOEXTEND);
308 sbuf_printf(&sb, "info-get guestinfo.fbsd.%s", keyword);
309 sbuf_trim(&sb);
310 sbuf_finish(&sb);
311
312 error = vmware_guestrpc_cmd(&sb);
313 if (error)
314 return (error);
315
316 sbuf_finish(&sb);
317 buf = sbuf_data(&sb);
318
319 #ifdef DEBUG_VMGUESTRPC
320 printf("%s: result: %s\n", __func__, buf);
321 #endif
322
323 /*
324 * Buffer will contain "1 <value>" on success or
325 * "0 No value found" on failure
326 */
327 if (buf[0] == '0')
328 return (ENOENT);
329
330 /*
331 * Add value from buffer to the sbuf
332 */
333 sbuf_cat(sbufp, buf + 2);
334 return (0);
335 }
336
337 MODULE_VERSION(vmware_guestrpc, 1);
338