xref: /freebsd/sys/x86/x86/vmware_guestrpc.c (revision ecaab0fb)
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