1 /*
2  * Copyright (C) 2012 Michael Brown <mbrown@fensystems.co.uk>.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License as
6  * published by the Free Software Foundation; either version 2 of the
7  * License, or any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17  * 02110-1301, USA.
18  *
19  * You can also choose to distribute this program under the terms of
20  * the Unmodified Binary Distribution Licence (as given in the file
21  * COPYING.UBDL), provided that you have satisfied its requirements.
22  */
23 
24 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
25 
26 /** @file
27  *
28  * VMware GuestRPC mechanism
29  *
30  */
31 
32 #include <stdint.h>
33 #include <string.h>
34 #include <errno.h>
35 #include <assert.h>
36 #include <ipxe/vmware.h>
37 #include <ipxe/guestrpc.h>
38 
39 /* Disambiguate the various error causes */
40 #define EPROTO_OPEN __einfo_error ( EINFO_EPROTO_OPEN )
41 #define EINFO_EPROTO_OPEN \
42 	__einfo_uniqify ( EINFO_EPROTO, 0x00, "GuestRPC open failed" )
43 #define EPROTO_COMMAND_LEN __einfo_error ( EINFO_EPROTO_COMMAND_LEN )
44 #define EINFO_EPROTO_COMMAND_LEN \
45 	__einfo_uniqify ( EINFO_EPROTO, 0x01, "GuestRPC command length failed" )
46 #define EPROTO_COMMAND_DATA __einfo_error ( EINFO_EPROTO_COMMAND_DATA )
47 #define EINFO_EPROTO_COMMAND_DATA \
48 	__einfo_uniqify ( EINFO_EPROTO, 0x02, "GuestRPC command data failed" )
49 #define EPROTO_REPLY_LEN __einfo_error ( EINFO_EPROTO_REPLY_LEN )
50 #define EINFO_EPROTO_REPLY_LEN \
51 	__einfo_uniqify ( EINFO_EPROTO, 0x03, "GuestRPC reply length failed" )
52 #define EPROTO_REPLY_DATA __einfo_error ( EINFO_EPROTO_REPLY_DATA )
53 #define EINFO_EPROTO_REPLY_DATA \
54 	__einfo_uniqify ( EINFO_EPROTO, 0x04, "GuestRPC reply data failed" )
55 #define EPROTO_REPLY_FINISH __einfo_error ( EINFO_EPROTO_REPLY_FINISH )
56 #define EINFO_EPROTO_REPLY_FINISH \
57 	__einfo_uniqify ( EINFO_EPROTO, 0x05, "GuestRPC reply finish failed" )
58 #define EPROTO_CLOSE __einfo_error ( EINFO_EPROTO_CLOSE )
59 #define EINFO_EPROTO_CLOSE \
60 	__einfo_uniqify ( EINFO_EPROTO, 0x06, "GuestRPC close failed" )
61 
62 /**
63  * Open GuestRPC channel
64  *
65  * @ret channel		Channel number, or negative error
66  */
guestrpc_open(void)67 int guestrpc_open ( void ) {
68 	uint16_t channel;
69 	uint32_t discard_b;
70 	uint32_t status;
71 
72 	/* Issue GuestRPC command */
73 	status = vmware_cmd_guestrpc ( 0, GUESTRPC_OPEN, GUESTRPC_MAGIC,
74 				       &channel, &discard_b );
75 	if ( status != GUESTRPC_OPEN_SUCCESS ) {
76 		DBGC ( GUESTRPC_MAGIC, "GuestRPC open failed: status %08x\n",
77 		       status );
78 		return -EPROTO_OPEN;
79 	}
80 
81 	DBGC ( GUESTRPC_MAGIC, "GuestRPC channel %d opened\n", channel );
82 	return channel;
83 }
84 
85 /**
86  * Send GuestRPC command length
87  *
88  * @v channel		Channel number
89  * @v len		Command length
90  * @ret rc		Return status code
91  */
guestrpc_command_len(int channel,size_t len)92 static int guestrpc_command_len ( int channel, size_t len ) {
93 	uint16_t discard_d;
94 	uint32_t discard_b;
95 	uint32_t status;
96 
97 	/* Issue GuestRPC command */
98 	status = vmware_cmd_guestrpc ( channel, GUESTRPC_COMMAND_LEN, len,
99 				       &discard_d, &discard_b );
100 	if ( status != GUESTRPC_COMMAND_LEN_SUCCESS ) {
101 		DBGC ( GUESTRPC_MAGIC, "GuestRPC channel %d send command "
102 		       "length %zd failed: status %08x\n",
103 		       channel, len, status );
104 		return -EPROTO_COMMAND_LEN;
105 	}
106 
107 	return 0;
108 }
109 
110 /**
111  * Send GuestRPC command data
112  *
113  * @v channel		Channel number
114  * @v data		Command data
115  * @ret rc		Return status code
116  */
guestrpc_command_data(int channel,uint32_t data)117 static int guestrpc_command_data ( int channel, uint32_t data ) {
118 	uint16_t discard_d;
119 	uint32_t discard_b;
120 	uint32_t status;
121 
122 	/* Issue GuestRPC command */
123 	status = vmware_cmd_guestrpc ( channel, GUESTRPC_COMMAND_DATA, data,
124 				       &discard_d, &discard_b );
125 	if ( status != GUESTRPC_COMMAND_DATA_SUCCESS ) {
126 		DBGC ( GUESTRPC_MAGIC, "GuestRPC channel %d send command "
127 		       "data %08x failed: status %08x\n",
128 		       channel, data, status );
129 		return -EPROTO_COMMAND_DATA;
130 	}
131 
132 	return 0;
133 }
134 
135 /**
136  * Receive GuestRPC reply length
137  *
138  * @v channel		Channel number
139  * @ret reply_id	Reply ID
140  * @ret len		Reply length, or negative error
141  */
guestrpc_reply_len(int channel,uint16_t * reply_id)142 static int guestrpc_reply_len ( int channel, uint16_t *reply_id ) {
143 	uint32_t len;
144 	uint32_t status;
145 
146 	/* Issue GuestRPC command */
147 	status = vmware_cmd_guestrpc ( channel, GUESTRPC_REPLY_LEN, 0,
148 				       reply_id, &len );
149 	if ( status != GUESTRPC_REPLY_LEN_SUCCESS ) {
150 		DBGC ( GUESTRPC_MAGIC, "GuestRPC channel %d receive reply "
151 		       "length failed: status %08x\n", channel, status );
152 		return -EPROTO_REPLY_LEN;
153 	}
154 
155 	return len;
156 }
157 
158 /**
159  * Receive GuestRPC reply data
160  *
161  * @v channel		Channel number
162  * @v reply_id		Reply ID
163  * @ret data		Reply data
164  * @ret rc		Return status code
165  */
guestrpc_reply_data(int channel,uint16_t reply_id,uint32_t * data)166 static int guestrpc_reply_data ( int channel, uint16_t reply_id,
167 				 uint32_t *data ) {
168 	uint16_t discard_d;
169 	uint32_t status;
170 
171 	/* Issue GuestRPC command */
172 	status = vmware_cmd_guestrpc ( channel, GUESTRPC_REPLY_DATA, reply_id,
173 				       &discard_d, data );
174 	if ( status != GUESTRPC_REPLY_DATA_SUCCESS ) {
175 		DBGC ( GUESTRPC_MAGIC, "GuestRPC channel %d receive reply "
176 		       "%d data failed: status %08x\n",
177 		       channel, reply_id, status );
178 		return -EPROTO_REPLY_DATA;
179 	}
180 
181 	return 0;
182 }
183 
184 /**
185  * Finish receiving GuestRPC reply
186  *
187  * @v channel		Channel number
188  * @v reply_id		Reply ID
189  * @ret rc		Return status code
190  */
guestrpc_reply_finish(int channel,uint16_t reply_id)191 static int guestrpc_reply_finish ( int channel, uint16_t reply_id ) {
192 	uint16_t discard_d;
193 	uint32_t discard_b;
194 	uint32_t status;
195 
196 	/* Issue GuestRPC command */
197 	status = vmware_cmd_guestrpc ( channel, GUESTRPC_REPLY_FINISH, reply_id,
198 				       &discard_d, &discard_b );
199 	if ( status != GUESTRPC_REPLY_FINISH_SUCCESS ) {
200 		DBGC ( GUESTRPC_MAGIC, "GuestRPC channel %d finish reply %d "
201 		       "failed: status %08x\n", channel, reply_id, status );
202 		return -EPROTO_REPLY_FINISH;
203 	}
204 
205 	return 0;
206 }
207 
208 /**
209  * Close GuestRPC channel
210  *
211  * @v channel		Channel number
212  */
guestrpc_close(int channel)213 void guestrpc_close ( int channel ) {
214 	uint16_t discard_d;
215 	uint32_t discard_b;
216 	uint32_t status;
217 
218 	/* Issue GuestRPC command */
219 	status = vmware_cmd_guestrpc ( channel, GUESTRPC_CLOSE, 0,
220 				       &discard_d, &discard_b );
221 	if ( status != GUESTRPC_CLOSE_SUCCESS ) {
222 		DBGC ( GUESTRPC_MAGIC, "GuestRPC channel %d close failed: "
223 		       "status %08x\n", channel, status );
224 		return;
225 	}
226 
227 	DBGC ( GUESTRPC_MAGIC, "GuestRPC channel %d closed\n", channel );
228 }
229 
230 /**
231  * Issue GuestRPC command
232  *
233  * @v channel		Channel number
234  * @v command		Command
235  * @v reply		Reply buffer
236  * @v reply_len		Length of reply buffer
237  * @ret len		Length of reply, or negative error
238  *
239  * The actual length of the reply will be returned even if the buffer
240  * was too small.
241  */
guestrpc_command(int channel,const char * command,char * reply,size_t reply_len)242 int guestrpc_command ( int channel, const char *command, char *reply,
243 		       size_t reply_len ) {
244 	const uint8_t *command_bytes = ( ( const void * ) command );
245 	uint8_t *reply_bytes = ( ( void * ) reply );
246 	size_t command_len = strlen ( command );
247 	int orig_reply_len = reply_len;
248 	uint16_t status;
249 	uint8_t *status_bytes = ( ( void * ) &status );
250 	size_t status_len = sizeof ( status );
251 	uint32_t data;
252 	uint16_t reply_id;
253 	int len;
254 	int remaining;
255 	unsigned int i;
256 	int rc;
257 
258 	DBGC2 ( GUESTRPC_MAGIC, "GuestRPC channel %d issuing command:\n",
259 		channel );
260 	DBGC2_HDA ( GUESTRPC_MAGIC, 0, command, command_len );
261 
262 	/* Sanity check */
263 	assert ( ( reply != NULL ) || ( reply_len == 0 ) );
264 
265 	/* Send command length */
266 	if ( ( rc = guestrpc_command_len ( channel, command_len ) ) < 0 )
267 		return rc;
268 
269 	/* Send command data */
270 	while ( command_len ) {
271 		data = 0;
272 		for ( i = sizeof ( data ) ; i ; i-- ) {
273 			if ( command_len ) {
274 				data = ( ( data & ~0xff ) |
275 					 *(command_bytes++) );
276 				command_len--;
277 			}
278 			data = ( ( data << 24 ) | ( data >> 8 ) );
279 		}
280 		if ( ( rc = guestrpc_command_data ( channel, data ) ) < 0 )
281 			return rc;
282 	}
283 
284 	/* Receive reply length */
285 	if ( ( len = guestrpc_reply_len ( channel, &reply_id ) ) < 0 ) {
286 		rc = len;
287 		return rc;
288 	}
289 
290 	/* Receive reply */
291 	for ( remaining = len ; remaining > 0 ; remaining -= sizeof ( data ) ) {
292 		if ( ( rc = guestrpc_reply_data ( channel, reply_id,
293 						  &data ) ) < 0 ) {
294 			return rc;
295 		}
296 		for ( i = sizeof ( data ) ; i ; i-- ) {
297 			if ( status_len ) {
298 				*(status_bytes++) = ( data & 0xff );
299 				status_len--;
300 				len--;
301 			} else if ( reply_len ) {
302 				*(reply_bytes++) = ( data & 0xff );
303 				reply_len--;
304 			}
305 			data = ( ( data << 24 ) | ( data >> 8 ) );
306 		}
307 	}
308 
309 	/* Finish receiving RPC reply */
310 	if ( ( rc = guestrpc_reply_finish ( channel, reply_id ) ) < 0 )
311 		return rc;
312 
313 	DBGC2 ( GUESTRPC_MAGIC, "GuestRPC channel %d received reply (id %d, "
314 		"length %d):\n", channel, reply_id, len );
315 	DBGC2_HDA ( GUESTRPC_MAGIC, 0, &status, sizeof ( status ) );
316 	DBGC2_HDA ( GUESTRPC_MAGIC, sizeof ( status ), reply,
317 		    ( ( len < orig_reply_len ) ? len : orig_reply_len ) );
318 
319 	/* Check reply status */
320 	if ( status != GUESTRPC_SUCCESS ) {
321 		DBGC ( GUESTRPC_MAGIC, "GuestRPC channel %d command failed "
322 		       "(status %04x, reply id %d, reply length %d):\n",
323 		       channel, status, reply_id, len );
324 		DBGC_HDA ( GUESTRPC_MAGIC, 0, command, command_len );
325 		DBGC_HDA ( GUESTRPC_MAGIC, 0, &status, sizeof ( status ) );
326 		DBGC_HDA ( GUESTRPC_MAGIC, sizeof ( status ), reply,
327 			   ( ( len < orig_reply_len ) ? len : orig_reply_len ));
328 		return -EIO;
329 	}
330 
331 	return len;
332 }
333