mirror of
https://github.com/ipxe/ipxe
synced 2025-12-25 00:17:57 +03:00
[vmware] Expose GuestRPC mechanism in 64-bit builds
The GuestRPC mechanism (used for VMWARE_SETTINGS and CONSOLE_VMWARE) does not use any real-mode code and so can be exposed in both 64-bit and 32-bit builds. Reported-by: Matthew Helton <mwhelton@gmail.com> Signed-off-by: Michael Brown <mcb30@ipxe.org>
This commit is contained in:
271
src/arch/x86/interface/vmware/guestinfo.c
Normal file
271
src/arch/x86/interface/vmware/guestinfo.c
Normal file
@@ -0,0 +1,271 @@
|
||||
/*
|
||||
* Copyright (C) 2012 Michael Brown <mbrown@fensystems.co.uk>.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*/
|
||||
|
||||
FILE_LICENCE ( GPL2_OR_LATER );
|
||||
|
||||
/** @file
|
||||
*
|
||||
* VMware GuestInfo settings
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <ipxe/init.h>
|
||||
#include <ipxe/settings.h>
|
||||
#include <ipxe/netdevice.h>
|
||||
#include <ipxe/guestrpc.h>
|
||||
|
||||
/** GuestInfo GuestRPC channel */
|
||||
static int guestinfo_channel;
|
||||
|
||||
/**
|
||||
* Fetch value of typed GuestInfo setting
|
||||
*
|
||||
* @v settings Settings block
|
||||
* @v setting Setting to fetch
|
||||
* @v type Setting type to attempt (or NULL for default)
|
||||
* @v data Buffer to fill with setting data
|
||||
* @v len Length of buffer
|
||||
* @ret found Setting found in GuestInfo
|
||||
* @ret len Length of setting data, or negative error
|
||||
*/
|
||||
static int guestinfo_fetch_type ( struct settings *settings,
|
||||
struct setting *setting,
|
||||
const struct setting_type *type,
|
||||
void *data, size_t len, int *found ) {
|
||||
const char *parent_name = settings->parent->name;
|
||||
char command[ 24 /* "info-get guestinfo.ipxe." */ +
|
||||
strlen ( parent_name ) + 1 /* "." */ +
|
||||
strlen ( setting->name ) + 1 /* "." */ +
|
||||
( type ? strlen ( type->name ) : 0 ) + 1 /* NUL */ ];
|
||||
struct setting *predefined;
|
||||
char *info;
|
||||
int info_len;
|
||||
int check_len;
|
||||
int ret;
|
||||
|
||||
/* Construct info-get command */
|
||||
snprintf ( command, sizeof ( command ),
|
||||
"info-get guestinfo.ipxe.%s%s%s%s%s",
|
||||
parent_name, ( parent_name[0] ? "." : "" ), setting->name,
|
||||
( type ? "." : "" ), ( type ? type->name : "" ) );
|
||||
|
||||
/* Check for existence and obtain length of GuestInfo value */
|
||||
info_len = guestrpc_command ( guestinfo_channel, command, NULL, 0 );
|
||||
if ( info_len < 0 ) {
|
||||
ret = info_len;
|
||||
goto err_get_info_len;
|
||||
}
|
||||
|
||||
/* Mark as found */
|
||||
*found = 1;
|
||||
|
||||
/* Determine default type if necessary */
|
||||
if ( ! type ) {
|
||||
predefined = find_setting ( setting->name );
|
||||
type = ( predefined ? predefined->type : &setting_type_string );
|
||||
}
|
||||
assert ( type != NULL );
|
||||
|
||||
/* Allocate temporary block to hold GuestInfo value */
|
||||
info = zalloc ( info_len + 1 /* NUL */ );
|
||||
if ( ! info ) {
|
||||
DBGC ( settings, "GuestInfo %p could not allocate %d bytes\n",
|
||||
settings, info_len );
|
||||
ret = -ENOMEM;
|
||||
goto err_alloc;
|
||||
}
|
||||
info[info_len] = '\0';
|
||||
|
||||
/* Fetch GuestInfo value */
|
||||
check_len = guestrpc_command ( guestinfo_channel, command,
|
||||
info, info_len );
|
||||
if ( check_len < 0 ) {
|
||||
ret = check_len;
|
||||
goto err_get_info;
|
||||
}
|
||||
if ( check_len != info_len ) {
|
||||
DBGC ( settings, "GuestInfo %p length mismatch (expected %d, "
|
||||
"got %d)\n", settings, info_len, check_len );
|
||||
ret = -EIO;
|
||||
goto err_get_info;
|
||||
}
|
||||
DBGC2 ( settings, "GuestInfo %p found %s = \"%s\"\n",
|
||||
settings, &command[9] /* Skip "info-get " */, info );
|
||||
|
||||
/* Parse GuestInfo value according to type */
|
||||
ret = setting_parse ( type, info, data, len );
|
||||
if ( ret < 0 ) {
|
||||
DBGC ( settings, "GuestInfo %p could not parse \"%s\" as %s: "
|
||||
"%s\n", settings, info, type->name, strerror ( ret ) );
|
||||
goto err_parse;
|
||||
}
|
||||
|
||||
err_parse:
|
||||
err_get_info:
|
||||
free ( info );
|
||||
err_alloc:
|
||||
err_get_info_len:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch value of GuestInfo setting
|
||||
*
|
||||
* @v settings Settings block
|
||||
* @v setting Setting to fetch
|
||||
* @v data Buffer to fill with setting data
|
||||
* @v len Length of buffer
|
||||
* @ret len Length of setting data, or negative error
|
||||
*/
|
||||
static int guestinfo_fetch ( struct settings *settings,
|
||||
struct setting *setting,
|
||||
void *data, size_t len ) {
|
||||
struct setting_type *type;
|
||||
int found = 0;
|
||||
int ret;
|
||||
|
||||
/* Try default type first */
|
||||
ret = guestinfo_fetch_type ( settings, setting, NULL,
|
||||
data, len, &found );
|
||||
if ( found )
|
||||
return ret;
|
||||
|
||||
/* Otherwise, try all possible types */
|
||||
for_each_table_entry ( type, SETTING_TYPES ) {
|
||||
ret = guestinfo_fetch_type ( settings, setting, type,
|
||||
data, len, &found );
|
||||
if ( found )
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Not found */
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
/** GuestInfo settings operations */
|
||||
static struct settings_operations guestinfo_settings_operations = {
|
||||
.fetch = guestinfo_fetch,
|
||||
};
|
||||
|
||||
/** GuestInfo settings */
|
||||
static struct settings guestinfo_settings = {
|
||||
.refcnt = NULL,
|
||||
.siblings = LIST_HEAD_INIT ( guestinfo_settings.siblings ),
|
||||
.children = LIST_HEAD_INIT ( guestinfo_settings.children ),
|
||||
.op = &guestinfo_settings_operations,
|
||||
};
|
||||
|
||||
/** Initialise GuestInfo settings */
|
||||
static void guestinfo_init ( void ) {
|
||||
int rc;
|
||||
|
||||
/* Open GuestRPC channel */
|
||||
guestinfo_channel = guestrpc_open();
|
||||
if ( guestinfo_channel < 0 ) {
|
||||
rc = guestinfo_channel;
|
||||
DBG ( "GuestInfo could not open channel: %s\n",
|
||||
strerror ( rc ) );
|
||||
return;
|
||||
}
|
||||
|
||||
/* Register root GuestInfo settings */
|
||||
if ( ( rc = register_settings ( &guestinfo_settings, NULL,
|
||||
"vmware" ) ) != 0 ) {
|
||||
DBG ( "GuestInfo could not register settings: %s\n",
|
||||
strerror ( rc ) );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/** GuestInfo settings initialiser */
|
||||
struct init_fn guestinfo_init_fn __init_fn ( INIT_NORMAL ) = {
|
||||
.initialise = guestinfo_init,
|
||||
};
|
||||
|
||||
/**
|
||||
* Create per-netdevice GuestInfo settings
|
||||
*
|
||||
* @v netdev Network device
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static int guestinfo_net_probe ( struct net_device *netdev ) {
|
||||
struct settings *settings;
|
||||
int rc;
|
||||
|
||||
/* Do nothing unless we have a GuestInfo channel available */
|
||||
if ( guestinfo_channel < 0 )
|
||||
return 0;
|
||||
|
||||
/* Allocate and initialise settings block */
|
||||
settings = zalloc ( sizeof ( *settings ) );
|
||||
if ( ! settings ) {
|
||||
rc = -ENOMEM;
|
||||
goto err_alloc;
|
||||
}
|
||||
settings_init ( settings, &guestinfo_settings_operations, NULL, NULL );
|
||||
|
||||
/* Register settings */
|
||||
if ( ( rc = register_settings ( settings, netdev_settings ( netdev ),
|
||||
"vmware" ) ) != 0 ) {
|
||||
DBGC ( settings, "GuestInfo %p could not register for %s: %s\n",
|
||||
settings, netdev->name, strerror ( rc ) );
|
||||
goto err_register;
|
||||
}
|
||||
DBGC ( settings, "GuestInfo %p registered for %s\n",
|
||||
settings, netdev->name );
|
||||
|
||||
return 0;
|
||||
|
||||
err_register:
|
||||
free ( settings );
|
||||
err_alloc:
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove per-netdevice GuestInfo settings
|
||||
*
|
||||
* @v netdev Network device
|
||||
*/
|
||||
static void guestinfo_net_remove ( struct net_device *netdev ) {
|
||||
struct settings *parent = netdev_settings ( netdev );
|
||||
struct settings *settings;
|
||||
|
||||
list_for_each_entry ( settings, &parent->children, siblings ) {
|
||||
if ( settings->op == &guestinfo_settings_operations ) {
|
||||
DBGC ( settings, "GuestInfo %p unregistered for %s\n",
|
||||
settings, netdev->name );
|
||||
unregister_settings ( settings );
|
||||
free ( settings );
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** GuestInfo per-netdevice driver */
|
||||
struct net_driver guestinfo_net_driver __net_driver = {
|
||||
.name = "GuestInfo",
|
||||
.probe = guestinfo_net_probe,
|
||||
.remove = guestinfo_net_remove,
|
||||
};
|
||||
332
src/arch/x86/interface/vmware/guestrpc.c
Normal file
332
src/arch/x86/interface/vmware/guestrpc.c
Normal file
@@ -0,0 +1,332 @@
|
||||
/*
|
||||
* Copyright (C) 2012 Michael Brown <mbrown@fensystems.co.uk>.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*
|
||||
* You can also choose to distribute this program under the terms of
|
||||
* the Unmodified Binary Distribution Licence (as given in the file
|
||||
* COPYING.UBDL), provided that you have satisfied its requirements.
|
||||
*/
|
||||
|
||||
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
||||
|
||||
/** @file
|
||||
*
|
||||
* VMware GuestRPC mechanism
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
#include <ipxe/vmware.h>
|
||||
#include <ipxe/guestrpc.h>
|
||||
|
||||
/* Disambiguate the various error causes */
|
||||
#define EPROTO_OPEN __einfo_error ( EINFO_EPROTO_OPEN )
|
||||
#define EINFO_EPROTO_OPEN \
|
||||
__einfo_uniqify ( EINFO_EPROTO, 0x00, "GuestRPC open failed" )
|
||||
#define EPROTO_COMMAND_LEN __einfo_error ( EINFO_EPROTO_COMMAND_LEN )
|
||||
#define EINFO_EPROTO_COMMAND_LEN \
|
||||
__einfo_uniqify ( EINFO_EPROTO, 0x01, "GuestRPC command length failed" )
|
||||
#define EPROTO_COMMAND_DATA __einfo_error ( EINFO_EPROTO_COMMAND_DATA )
|
||||
#define EINFO_EPROTO_COMMAND_DATA \
|
||||
__einfo_uniqify ( EINFO_EPROTO, 0x02, "GuestRPC command data failed" )
|
||||
#define EPROTO_REPLY_LEN __einfo_error ( EINFO_EPROTO_REPLY_LEN )
|
||||
#define EINFO_EPROTO_REPLY_LEN \
|
||||
__einfo_uniqify ( EINFO_EPROTO, 0x03, "GuestRPC reply length failed" )
|
||||
#define EPROTO_REPLY_DATA __einfo_error ( EINFO_EPROTO_REPLY_DATA )
|
||||
#define EINFO_EPROTO_REPLY_DATA \
|
||||
__einfo_uniqify ( EINFO_EPROTO, 0x04, "GuestRPC reply data failed" )
|
||||
#define EPROTO_REPLY_FINISH __einfo_error ( EINFO_EPROTO_REPLY_FINISH )
|
||||
#define EINFO_EPROTO_REPLY_FINISH \
|
||||
__einfo_uniqify ( EINFO_EPROTO, 0x05, "GuestRPC reply finish failed" )
|
||||
#define EPROTO_CLOSE __einfo_error ( EINFO_EPROTO_CLOSE )
|
||||
#define EINFO_EPROTO_CLOSE \
|
||||
__einfo_uniqify ( EINFO_EPROTO, 0x06, "GuestRPC close failed" )
|
||||
|
||||
/**
|
||||
* Open GuestRPC channel
|
||||
*
|
||||
* @ret channel Channel number, or negative error
|
||||
*/
|
||||
int guestrpc_open ( void ) {
|
||||
uint16_t channel;
|
||||
uint32_t discard_b;
|
||||
uint32_t status;
|
||||
|
||||
/* Issue GuestRPC command */
|
||||
status = vmware_cmd_guestrpc ( 0, GUESTRPC_OPEN, GUESTRPC_MAGIC,
|
||||
&channel, &discard_b );
|
||||
if ( status != GUESTRPC_OPEN_SUCCESS ) {
|
||||
DBGC ( GUESTRPC_MAGIC, "GuestRPC open failed: status %08x\n",
|
||||
status );
|
||||
return -EPROTO_OPEN;
|
||||
}
|
||||
|
||||
DBGC ( GUESTRPC_MAGIC, "GuestRPC channel %d opened\n", channel );
|
||||
return channel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send GuestRPC command length
|
||||
*
|
||||
* @v channel Channel number
|
||||
* @v len Command length
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static int guestrpc_command_len ( int channel, size_t len ) {
|
||||
uint16_t discard_d;
|
||||
uint32_t discard_b;
|
||||
uint32_t status;
|
||||
|
||||
/* Issue GuestRPC command */
|
||||
status = vmware_cmd_guestrpc ( channel, GUESTRPC_COMMAND_LEN, len,
|
||||
&discard_d, &discard_b );
|
||||
if ( status != GUESTRPC_COMMAND_LEN_SUCCESS ) {
|
||||
DBGC ( GUESTRPC_MAGIC, "GuestRPC channel %d send command "
|
||||
"length %zd failed: status %08x\n",
|
||||
channel, len, status );
|
||||
return -EPROTO_COMMAND_LEN;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send GuestRPC command data
|
||||
*
|
||||
* @v channel Channel number
|
||||
* @v data Command data
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static int guestrpc_command_data ( int channel, uint32_t data ) {
|
||||
uint16_t discard_d;
|
||||
uint32_t discard_b;
|
||||
uint32_t status;
|
||||
|
||||
/* Issue GuestRPC command */
|
||||
status = vmware_cmd_guestrpc ( channel, GUESTRPC_COMMAND_DATA, data,
|
||||
&discard_d, &discard_b );
|
||||
if ( status != GUESTRPC_COMMAND_DATA_SUCCESS ) {
|
||||
DBGC ( GUESTRPC_MAGIC, "GuestRPC channel %d send command "
|
||||
"data %08x failed: status %08x\n",
|
||||
channel, data, status );
|
||||
return -EPROTO_COMMAND_DATA;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Receive GuestRPC reply length
|
||||
*
|
||||
* @v channel Channel number
|
||||
* @ret reply_id Reply ID
|
||||
* @ret len Reply length, or negative error
|
||||
*/
|
||||
static int guestrpc_reply_len ( int channel, uint16_t *reply_id ) {
|
||||
uint32_t len;
|
||||
uint32_t status;
|
||||
|
||||
/* Issue GuestRPC command */
|
||||
status = vmware_cmd_guestrpc ( channel, GUESTRPC_REPLY_LEN, 0,
|
||||
reply_id, &len );
|
||||
if ( status != GUESTRPC_REPLY_LEN_SUCCESS ) {
|
||||
DBGC ( GUESTRPC_MAGIC, "GuestRPC channel %d receive reply "
|
||||
"length failed: status %08x\n", channel, status );
|
||||
return -EPROTO_REPLY_LEN;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
/**
|
||||
* Receive GuestRPC reply data
|
||||
*
|
||||
* @v channel Channel number
|
||||
* @v reply_id Reply ID
|
||||
* @ret data Reply data
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static int guestrpc_reply_data ( int channel, uint16_t reply_id,
|
||||
uint32_t *data ) {
|
||||
uint16_t discard_d;
|
||||
uint32_t status;
|
||||
|
||||
/* Issue GuestRPC command */
|
||||
status = vmware_cmd_guestrpc ( channel, GUESTRPC_REPLY_DATA, reply_id,
|
||||
&discard_d, data );
|
||||
if ( status != GUESTRPC_REPLY_DATA_SUCCESS ) {
|
||||
DBGC ( GUESTRPC_MAGIC, "GuestRPC channel %d receive reply "
|
||||
"%d data failed: status %08x\n",
|
||||
channel, reply_id, status );
|
||||
return -EPROTO_REPLY_DATA;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finish receiving GuestRPC reply
|
||||
*
|
||||
* @v channel Channel number
|
||||
* @v reply_id Reply ID
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static int guestrpc_reply_finish ( int channel, uint16_t reply_id ) {
|
||||
uint16_t discard_d;
|
||||
uint32_t discard_b;
|
||||
uint32_t status;
|
||||
|
||||
/* Issue GuestRPC command */
|
||||
status = vmware_cmd_guestrpc ( channel, GUESTRPC_REPLY_FINISH, reply_id,
|
||||
&discard_d, &discard_b );
|
||||
if ( status != GUESTRPC_REPLY_FINISH_SUCCESS ) {
|
||||
DBGC ( GUESTRPC_MAGIC, "GuestRPC channel %d finish reply %d "
|
||||
"failed: status %08x\n", channel, reply_id, status );
|
||||
return -EPROTO_REPLY_FINISH;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close GuestRPC channel
|
||||
*
|
||||
* @v channel Channel number
|
||||
*/
|
||||
void guestrpc_close ( int channel ) {
|
||||
uint16_t discard_d;
|
||||
uint32_t discard_b;
|
||||
uint32_t status;
|
||||
|
||||
/* Issue GuestRPC command */
|
||||
status = vmware_cmd_guestrpc ( channel, GUESTRPC_CLOSE, 0,
|
||||
&discard_d, &discard_b );
|
||||
if ( status != GUESTRPC_CLOSE_SUCCESS ) {
|
||||
DBGC ( GUESTRPC_MAGIC, "GuestRPC channel %d close failed: "
|
||||
"status %08x\n", channel, status );
|
||||
return;
|
||||
}
|
||||
|
||||
DBGC ( GUESTRPC_MAGIC, "GuestRPC channel %d closed\n", channel );
|
||||
}
|
||||
|
||||
/**
|
||||
* Issue GuestRPC command
|
||||
*
|
||||
* @v channel Channel number
|
||||
* @v command Command
|
||||
* @v reply Reply buffer
|
||||
* @v reply_len Length of reply buffer
|
||||
* @ret len Length of reply, or negative error
|
||||
*
|
||||
* The actual length of the reply will be returned even if the buffer
|
||||
* was too small.
|
||||
*/
|
||||
int guestrpc_command ( int channel, const char *command, char *reply,
|
||||
size_t reply_len ) {
|
||||
const uint8_t *command_bytes = ( ( const void * ) command );
|
||||
uint8_t *reply_bytes = ( ( void * ) reply );
|
||||
size_t command_len = strlen ( command );
|
||||
int orig_reply_len = reply_len;
|
||||
uint16_t status;
|
||||
uint8_t *status_bytes = ( ( void * ) &status );
|
||||
size_t status_len = sizeof ( status );
|
||||
uint32_t data;
|
||||
uint16_t reply_id;
|
||||
int len;
|
||||
int remaining;
|
||||
unsigned int i;
|
||||
int rc;
|
||||
|
||||
DBGC2 ( GUESTRPC_MAGIC, "GuestRPC channel %d issuing command:\n",
|
||||
channel );
|
||||
DBGC2_HDA ( GUESTRPC_MAGIC, 0, command, command_len );
|
||||
|
||||
/* Sanity check */
|
||||
assert ( ( reply != NULL ) || ( reply_len == 0 ) );
|
||||
|
||||
/* Send command length */
|
||||
if ( ( rc = guestrpc_command_len ( channel, command_len ) ) < 0 )
|
||||
return rc;
|
||||
|
||||
/* Send command data */
|
||||
while ( command_len ) {
|
||||
data = 0;
|
||||
for ( i = sizeof ( data ) ; i ; i-- ) {
|
||||
if ( command_len ) {
|
||||
data = ( ( data & ~0xff ) |
|
||||
*(command_bytes++) );
|
||||
command_len--;
|
||||
}
|
||||
data = ( ( data << 24 ) | ( data >> 8 ) );
|
||||
}
|
||||
if ( ( rc = guestrpc_command_data ( channel, data ) ) < 0 )
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Receive reply length */
|
||||
if ( ( len = guestrpc_reply_len ( channel, &reply_id ) ) < 0 ) {
|
||||
rc = len;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Receive reply */
|
||||
for ( remaining = len ; remaining > 0 ; remaining -= sizeof ( data ) ) {
|
||||
if ( ( rc = guestrpc_reply_data ( channel, reply_id,
|
||||
&data ) ) < 0 ) {
|
||||
return rc;
|
||||
}
|
||||
for ( i = sizeof ( data ) ; i ; i-- ) {
|
||||
if ( status_len ) {
|
||||
*(status_bytes++) = ( data & 0xff );
|
||||
status_len--;
|
||||
len--;
|
||||
} else if ( reply_len ) {
|
||||
*(reply_bytes++) = ( data & 0xff );
|
||||
reply_len--;
|
||||
}
|
||||
data = ( ( data << 24 ) | ( data >> 8 ) );
|
||||
}
|
||||
}
|
||||
|
||||
/* Finish receiving RPC reply */
|
||||
if ( ( rc = guestrpc_reply_finish ( channel, reply_id ) ) < 0 )
|
||||
return rc;
|
||||
|
||||
DBGC2 ( GUESTRPC_MAGIC, "GuestRPC channel %d received reply (id %d, "
|
||||
"length %d):\n", channel, reply_id, len );
|
||||
DBGC2_HDA ( GUESTRPC_MAGIC, 0, &status, sizeof ( status ) );
|
||||
DBGC2_HDA ( GUESTRPC_MAGIC, sizeof ( status ), reply,
|
||||
( ( len < orig_reply_len ) ? len : orig_reply_len ) );
|
||||
|
||||
/* Check reply status */
|
||||
if ( status != GUESTRPC_SUCCESS ) {
|
||||
DBGC ( GUESTRPC_MAGIC, "GuestRPC channel %d command failed "
|
||||
"(status %04x, reply id %d, reply length %d):\n",
|
||||
channel, status, reply_id, len );
|
||||
DBGC_HDA ( GUESTRPC_MAGIC, 0, command, command_len );
|
||||
DBGC_HDA ( GUESTRPC_MAGIC, 0, &status, sizeof ( status ) );
|
||||
DBGC_HDA ( GUESTRPC_MAGIC, sizeof ( status ), reply,
|
||||
( ( len < orig_reply_len ) ? len : orig_reply_len ));
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
138
src/arch/x86/interface/vmware/vmconsole.c
Normal file
138
src/arch/x86/interface/vmware/vmconsole.c
Normal file
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
* Copyright (C) 2012 Michael Brown <mbrown@fensystems.co.uk>.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*
|
||||
* You can also choose to distribute this program under the terms of
|
||||
* the Unmodified Binary Distribution Licence (as given in the file
|
||||
* COPYING.UBDL), provided that you have satisfied its requirements.
|
||||
*/
|
||||
|
||||
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
||||
|
||||
/** @file
|
||||
*
|
||||
* VMware logfile console
|
||||
*
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <ipxe/console.h>
|
||||
#include <ipxe/lineconsole.h>
|
||||
#include <ipxe/init.h>
|
||||
#include <ipxe/guestrpc.h>
|
||||
#include <config/console.h>
|
||||
|
||||
/** VMware logfile console buffer size */
|
||||
#define VMCONSOLE_BUFSIZE 128
|
||||
|
||||
/* Set default console usage if applicable */
|
||||
#if ! ( defined ( CONSOLE_VMWARE ) && CONSOLE_EXPLICIT ( CONSOLE_VMWARE ) )
|
||||
#undef CONSOLE_VMWARE
|
||||
#define CONSOLE_VMWARE ( CONSOLE_USAGE_ALL & ~CONSOLE_USAGE_TUI )
|
||||
#endif
|
||||
|
||||
/** VMware logfile console GuestRPC channel */
|
||||
static int vmconsole_channel;
|
||||
|
||||
/** VMware logfile console line buffer */
|
||||
static struct {
|
||||
char prefix[4];
|
||||
char message[VMCONSOLE_BUFSIZE];
|
||||
} vmconsole_buffer = {
|
||||
.prefix = "log ",
|
||||
};
|
||||
|
||||
/** VMware logfile console ANSI escape sequence handlers */
|
||||
static struct ansiesc_handler vmconsole_handlers[] = {
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
/** VMware logfile line console */
|
||||
static struct line_console vmconsole_line = {
|
||||
.buffer = vmconsole_buffer.message,
|
||||
.len = sizeof ( vmconsole_buffer.message ),
|
||||
.ctx = {
|
||||
.handlers = vmconsole_handlers,
|
||||
},
|
||||
};
|
||||
|
||||
/** VMware logfile console recursion marker */
|
||||
static int vmconsole_entered;
|
||||
|
||||
/**
|
||||
* Print a character to VMware logfile console
|
||||
*
|
||||
* @v character Character to be printed
|
||||
*/
|
||||
static void vmconsole_putchar ( int character ) {
|
||||
int rc;
|
||||
|
||||
/* Ignore if we are already mid-logging */
|
||||
if ( vmconsole_entered )
|
||||
return;
|
||||
|
||||
/* Fill line buffer */
|
||||
if ( line_putchar ( &vmconsole_line, character ) == 0 )
|
||||
return;
|
||||
|
||||
/* Guard against re-entry */
|
||||
vmconsole_entered = 1;
|
||||
|
||||
/* Send log message */
|
||||
if ( ( rc = guestrpc_command ( vmconsole_channel,
|
||||
vmconsole_buffer.prefix, NULL, 0 ) ) <0){
|
||||
DBG ( "VMware console could not send log message: %s\n",
|
||||
strerror ( rc ) );
|
||||
}
|
||||
|
||||
/* Clear re-entry flag */
|
||||
vmconsole_entered = 0;
|
||||
}
|
||||
|
||||
/** VMware logfile console driver */
|
||||
struct console_driver vmconsole __console_driver = {
|
||||
.putchar = vmconsole_putchar,
|
||||
.disabled = CONSOLE_DISABLED,
|
||||
.usage = CONSOLE_VMWARE,
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialise VMware logfile console
|
||||
*
|
||||
*/
|
||||
static void vmconsole_init ( void ) {
|
||||
int rc;
|
||||
|
||||
/* Attempt to open console */
|
||||
vmconsole_channel = guestrpc_open();
|
||||
if ( vmconsole_channel < 0 ) {
|
||||
rc = vmconsole_channel;
|
||||
DBG ( "VMware console could not be initialised: %s\n",
|
||||
strerror ( rc ) );
|
||||
return;
|
||||
}
|
||||
|
||||
/* Mark console as available */
|
||||
vmconsole.disabled = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* VMware logfile console initialisation function
|
||||
*/
|
||||
struct init_fn vmconsole_init_fn __init_fn ( INIT_CONSOLE ) = {
|
||||
.initialise = vmconsole_init,
|
||||
};
|
||||
62
src/arch/x86/interface/vmware/vmware.c
Normal file
62
src/arch/x86/interface/vmware/vmware.c
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright (C) 2012 Michael Brown <mbrown@fensystems.co.uk>.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*
|
||||
* You can also choose to distribute this program under the terms of
|
||||
* the Unmodified Binary Distribution Licence (as given in the file
|
||||
* COPYING.UBDL), provided that you have satisfied its requirements.
|
||||
*/
|
||||
|
||||
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
||||
|
||||
/** @file
|
||||
*
|
||||
* VMware backdoor mechanism
|
||||
*
|
||||
* Based on the unofficial documentation at
|
||||
*
|
||||
* http://sites.google.com/site/chitchatvmback/backdoor
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <errno.h>
|
||||
#include <ipxe/vmware.h>
|
||||
|
||||
/**
|
||||
* Detect VMware presence
|
||||
*
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
int vmware_present ( void ) {
|
||||
uint32_t version;
|
||||
uint32_t magic;
|
||||
uint32_t product_type;
|
||||
|
||||
/* Perform backdoor call */
|
||||
vmware_cmd_get_version ( &version, &magic, &product_type );
|
||||
|
||||
/* Check for VMware presence */
|
||||
if ( magic != VMW_MAGIC ) {
|
||||
DBGC ( VMW_MAGIC, "VMware not present\n" );
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
DBGC ( VMW_MAGIC, "VMware product type %04x version %08x detected\n",
|
||||
product_type, version );
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user