iPXE
guestrpc.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2012 Michael Brown <mbrown@fensystems.co.uk>.
00003  *
00004  * This program is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU General Public License as
00006  * published by the Free Software Foundation; either version 2 of the
00007  * License, or any later version.
00008  *
00009  * This program is distributed in the hope that it will be useful, but
00010  * WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012  * General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software
00016  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
00017  * 02110-1301, USA.
00018  *
00019  * You can also choose to distribute this program under the terms of
00020  * the Unmodified Binary Distribution Licence (as given in the file
00021  * COPYING.UBDL), provided that you have satisfied its requirements.
00022  */
00023 
00024 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
00025 
00026 /** @file
00027  *
00028  * VMware GuestRPC mechanism
00029  *
00030  */
00031 
00032 #include <stdint.h>
00033 #include <string.h>
00034 #include <errno.h>
00035 #include <assert.h>
00036 #include <ipxe/vmware.h>
00037 #include <ipxe/guestrpc.h>
00038 
00039 /* Disambiguate the various error causes */
00040 #define EPROTO_OPEN __einfo_error ( EINFO_EPROTO_OPEN )
00041 #define EINFO_EPROTO_OPEN \
00042         __einfo_uniqify ( EINFO_EPROTO, 0x00, "GuestRPC open failed" )
00043 #define EPROTO_COMMAND_LEN __einfo_error ( EINFO_EPROTO_COMMAND_LEN )
00044 #define EINFO_EPROTO_COMMAND_LEN \
00045         __einfo_uniqify ( EINFO_EPROTO, 0x01, "GuestRPC command length failed" )
00046 #define EPROTO_COMMAND_DATA __einfo_error ( EINFO_EPROTO_COMMAND_DATA )
00047 #define EINFO_EPROTO_COMMAND_DATA \
00048         __einfo_uniqify ( EINFO_EPROTO, 0x02, "GuestRPC command data failed" )
00049 #define EPROTO_REPLY_LEN __einfo_error ( EINFO_EPROTO_REPLY_LEN )
00050 #define EINFO_EPROTO_REPLY_LEN \
00051         __einfo_uniqify ( EINFO_EPROTO, 0x03, "GuestRPC reply length failed" )
00052 #define EPROTO_REPLY_DATA __einfo_error ( EINFO_EPROTO_REPLY_DATA )
00053 #define EINFO_EPROTO_REPLY_DATA \
00054         __einfo_uniqify ( EINFO_EPROTO, 0x04, "GuestRPC reply data failed" )
00055 #define EPROTO_REPLY_FINISH __einfo_error ( EINFO_EPROTO_REPLY_FINISH )
00056 #define EINFO_EPROTO_REPLY_FINISH \
00057         __einfo_uniqify ( EINFO_EPROTO, 0x05, "GuestRPC reply finish failed" )
00058 #define EPROTO_CLOSE __einfo_error ( EINFO_EPROTO_CLOSE )
00059 #define EINFO_EPROTO_CLOSE \
00060         __einfo_uniqify ( EINFO_EPROTO, 0x06, "GuestRPC close failed" )
00061 
00062 /**
00063  * Open GuestRPC channel
00064  *
00065  * @ret channel         Channel number, or negative error
00066  */
00067 int guestrpc_open ( void ) {
00068         uint16_t channel;
00069         uint32_t discard_b;
00070         uint32_t status;
00071 
00072         /* Issue GuestRPC command */
00073         status = vmware_cmd_guestrpc ( 0, GUESTRPC_OPEN, GUESTRPC_MAGIC,
00074                                        &channel, &discard_b );
00075         if ( status != GUESTRPC_OPEN_SUCCESS ) {
00076                 DBGC ( GUESTRPC_MAGIC, "GuestRPC open failed: status %08x\n",
00077                        status );
00078                 return -EPROTO_OPEN;
00079         }
00080 
00081         DBGC ( GUESTRPC_MAGIC, "GuestRPC channel %d opened\n", channel );
00082         return channel;
00083 }
00084 
00085 /**
00086  * Send GuestRPC command length
00087  *
00088  * @v channel           Channel number
00089  * @v len               Command length
00090  * @ret rc              Return status code
00091  */
00092 static int guestrpc_command_len ( int channel, size_t len ) {
00093         uint16_t discard_d;
00094         uint32_t discard_b;
00095         uint32_t status;
00096 
00097         /* Issue GuestRPC command */
00098         status = vmware_cmd_guestrpc ( channel, GUESTRPC_COMMAND_LEN, len,
00099                                        &discard_d, &discard_b );
00100         if ( status != GUESTRPC_COMMAND_LEN_SUCCESS ) {
00101                 DBGC ( GUESTRPC_MAGIC, "GuestRPC channel %d send command "
00102                        "length %zd failed: status %08x\n",
00103                        channel, len, status );
00104                 return -EPROTO_COMMAND_LEN;
00105         }
00106 
00107         return 0;
00108 }
00109 
00110 /**
00111  * Send GuestRPC command data
00112  *
00113  * @v channel           Channel number
00114  * @v data              Command data
00115  * @ret rc              Return status code
00116  */
00117 static int guestrpc_command_data ( int channel, uint32_t data ) {
00118         uint16_t discard_d;
00119         uint32_t discard_b;
00120         uint32_t status;
00121 
00122         /* Issue GuestRPC command */
00123         status = vmware_cmd_guestrpc ( channel, GUESTRPC_COMMAND_DATA, data,
00124                                        &discard_d, &discard_b );
00125         if ( status != GUESTRPC_COMMAND_DATA_SUCCESS ) {
00126                 DBGC ( GUESTRPC_MAGIC, "GuestRPC channel %d send command "
00127                        "data %08x failed: status %08x\n",
00128                        channel, data, status );
00129                 return -EPROTO_COMMAND_DATA;
00130         }
00131 
00132         return 0;
00133 }
00134 
00135 /**
00136  * Receive GuestRPC reply length
00137  *
00138  * @v channel           Channel number
00139  * @ret reply_id        Reply ID
00140  * @ret len             Reply length, or negative error
00141  */
00142 static int guestrpc_reply_len ( int channel, uint16_t *reply_id ) {
00143         uint32_t len;
00144         uint32_t status;
00145 
00146         /* Issue GuestRPC command */
00147         status = vmware_cmd_guestrpc ( channel, GUESTRPC_REPLY_LEN, 0,
00148                                        reply_id, &len );
00149         if ( status != GUESTRPC_REPLY_LEN_SUCCESS ) {
00150                 DBGC ( GUESTRPC_MAGIC, "GuestRPC channel %d receive reply "
00151                        "length failed: status %08x\n", channel, status );
00152                 return -EPROTO_REPLY_LEN;
00153         }
00154 
00155         return len;
00156 }
00157 
00158 /**
00159  * Receive GuestRPC reply data
00160  *
00161  * @v channel           Channel number
00162  * @v reply_id          Reply ID
00163  * @ret data            Reply data
00164  * @ret rc              Return status code
00165  */
00166 static int guestrpc_reply_data ( int channel, uint16_t reply_id,
00167                                  uint32_t *data ) {
00168         uint16_t discard_d;
00169         uint32_t status;
00170 
00171         /* Issue GuestRPC command */
00172         status = vmware_cmd_guestrpc ( channel, GUESTRPC_REPLY_DATA, reply_id,
00173                                        &discard_d, data );
00174         if ( status != GUESTRPC_REPLY_DATA_SUCCESS ) {
00175                 DBGC ( GUESTRPC_MAGIC, "GuestRPC channel %d receive reply "
00176                        "%d data failed: status %08x\n",
00177                        channel, reply_id, status );
00178                 return -EPROTO_REPLY_DATA;
00179         }
00180 
00181         return 0;
00182 }
00183 
00184 /**
00185  * Finish receiving GuestRPC reply
00186  *
00187  * @v channel           Channel number
00188  * @v reply_id          Reply ID
00189  * @ret rc              Return status code
00190  */
00191 static int guestrpc_reply_finish ( int channel, uint16_t reply_id ) {
00192         uint16_t discard_d;
00193         uint32_t discard_b;
00194         uint32_t status;
00195 
00196         /* Issue GuestRPC command */
00197         status = vmware_cmd_guestrpc ( channel, GUESTRPC_REPLY_FINISH, reply_id,
00198                                        &discard_d, &discard_b );
00199         if ( status != GUESTRPC_REPLY_FINISH_SUCCESS ) {
00200                 DBGC ( GUESTRPC_MAGIC, "GuestRPC channel %d finish reply %d "
00201                        "failed: status %08x\n", channel, reply_id, status );
00202                 return -EPROTO_REPLY_FINISH;
00203         }
00204 
00205         return 0;
00206 }
00207 
00208 /**
00209  * Close GuestRPC channel
00210  *
00211  * @v channel           Channel number
00212  */
00213 void guestrpc_close ( int channel ) {
00214         uint16_t discard_d;
00215         uint32_t discard_b;
00216         uint32_t status;
00217 
00218         /* Issue GuestRPC command */
00219         status = vmware_cmd_guestrpc ( channel, GUESTRPC_CLOSE, 0,
00220                                        &discard_d, &discard_b );
00221         if ( status != GUESTRPC_CLOSE_SUCCESS ) {
00222                 DBGC ( GUESTRPC_MAGIC, "GuestRPC channel %d close failed: "
00223                        "status %08x\n", channel, status );
00224                 return;
00225         }
00226 
00227         DBGC ( GUESTRPC_MAGIC, "GuestRPC channel %d closed\n", channel );
00228 }
00229 
00230 /**
00231  * Issue GuestRPC command
00232  *
00233  * @v channel           Channel number
00234  * @v command           Command
00235  * @v reply             Reply buffer
00236  * @v reply_len         Length of reply buffer
00237  * @ret len             Length of reply, or negative error
00238  *
00239  * The actual length of the reply will be returned even if the buffer
00240  * was too small.
00241  */
00242 int guestrpc_command ( int channel, const char *command, char *reply,
00243                        size_t reply_len ) {
00244         const uint8_t *command_bytes = ( ( const void * ) command );
00245         uint8_t *reply_bytes = ( ( void * ) reply );
00246         size_t command_len = strlen ( command );
00247         int orig_reply_len = reply_len;
00248         uint16_t status;
00249         uint8_t *status_bytes = ( ( void * ) &status );
00250         size_t status_len = sizeof ( status );
00251         uint32_t data;
00252         uint16_t reply_id;
00253         int len;
00254         int remaining;
00255         unsigned int i;
00256         int rc;
00257 
00258         DBGC2 ( GUESTRPC_MAGIC, "GuestRPC channel %d issuing command:\n",
00259                 channel );
00260         DBGC2_HDA ( GUESTRPC_MAGIC, 0, command, command_len );
00261 
00262         /* Sanity check */
00263         assert ( ( reply != NULL ) || ( reply_len == 0 ) );
00264 
00265         /* Send command length */
00266         if ( ( rc = guestrpc_command_len ( channel, command_len ) ) < 0 )
00267                 return rc;
00268 
00269         /* Send command data */
00270         while ( command_len ) {
00271                 data = 0;
00272                 for ( i = sizeof ( data ) ; i ; i-- ) {
00273                         if ( command_len ) {
00274                                 data = ( ( data & ~0xff ) |
00275                                          *(command_bytes++) );
00276                                 command_len--;
00277                         }
00278                         data = ( ( data << 24 ) | ( data >> 8 ) );
00279                 }
00280                 if ( ( rc = guestrpc_command_data ( channel, data ) ) < 0 )
00281                         return rc;
00282         }
00283 
00284         /* Receive reply length */
00285         if ( ( len = guestrpc_reply_len ( channel, &reply_id ) ) < 0 ) {
00286                 rc = len;
00287                 return rc;
00288         }
00289 
00290         /* Receive reply */
00291         for ( remaining = len ; remaining > 0 ; remaining -= sizeof ( data ) ) {
00292                 if ( ( rc = guestrpc_reply_data ( channel, reply_id,
00293                                                   &data ) ) < 0 ) {
00294                         return rc;
00295                 }
00296                 for ( i = sizeof ( data ) ; i ; i-- ) {
00297                         if ( status_len ) {
00298                                 *(status_bytes++) = ( data & 0xff );
00299                                 status_len--;
00300                                 len--;
00301                         } else if ( reply_len ) {
00302                                 *(reply_bytes++) = ( data & 0xff );
00303                                 reply_len--;
00304                         }
00305                         data = ( ( data << 24 ) | ( data >> 8 ) );
00306                 }
00307         }
00308 
00309         /* Finish receiving RPC reply */
00310         if ( ( rc = guestrpc_reply_finish ( channel, reply_id ) ) < 0 )
00311                 return rc;
00312 
00313         DBGC2 ( GUESTRPC_MAGIC, "GuestRPC channel %d received reply (id %d, "
00314                 "length %d):\n", channel, reply_id, len );
00315         DBGC2_HDA ( GUESTRPC_MAGIC, 0, &status, sizeof ( status ) );
00316         DBGC2_HDA ( GUESTRPC_MAGIC, sizeof ( status ), reply,
00317                     ( ( len < orig_reply_len ) ? len : orig_reply_len ) );
00318 
00319         /* Check reply status */
00320         if ( status != GUESTRPC_SUCCESS ) {
00321                 DBGC ( GUESTRPC_MAGIC, "GuestRPC channel %d command failed "
00322                        "(status %04x, reply id %d, reply length %d):\n",
00323                        channel, status, reply_id, len );
00324                 DBGC_HDA ( GUESTRPC_MAGIC, 0, command, command_len );
00325                 DBGC_HDA ( GUESTRPC_MAGIC, 0, &status, sizeof ( status ) );
00326                 DBGC_HDA ( GUESTRPC_MAGIC, sizeof ( status ), reply,
00327                            ( ( len < orig_reply_len ) ? len : orig_reply_len ));
00328                 return -EIO;
00329         }
00330 
00331         return len;
00332 }