From dd485992dc213dc19876a331f3a044dcb2e7560b Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 5 Jan 2016 17:20:36 +0000 Subject: [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 Signed-off-by: Michael Brown --- src/arch/i386/Makefile | 1 - src/arch/i386/include/ipxe/guestrpc.h | 68 ------ src/arch/i386/include/ipxe/vmware.h | 81 ------- src/arch/i386/interface/vmware/guestinfo.c | 271 ----------------------- src/arch/i386/interface/vmware/guestrpc.c | 332 ----------------------------- src/arch/i386/interface/vmware/vmconsole.c | 138 ------------ src/arch/i386/interface/vmware/vmware.c | 62 ------ src/arch/x86/Makefile | 1 + src/arch/x86/include/ipxe/guestrpc.h | 68 ++++++ src/arch/x86/include/ipxe/vmware.h | 81 +++++++ src/arch/x86/interface/vmware/guestinfo.c | 271 +++++++++++++++++++++++ src/arch/x86/interface/vmware/guestrpc.c | 332 +++++++++++++++++++++++++++++ src/arch/x86/interface/vmware/vmconsole.c | 138 ++++++++++++ src/arch/x86/interface/vmware/vmware.c | 62 ++++++ 14 files changed, 953 insertions(+), 953 deletions(-) delete mode 100644 src/arch/i386/include/ipxe/guestrpc.h delete mode 100644 src/arch/i386/include/ipxe/vmware.h delete mode 100644 src/arch/i386/interface/vmware/guestinfo.c delete mode 100644 src/arch/i386/interface/vmware/guestrpc.c delete mode 100644 src/arch/i386/interface/vmware/vmconsole.c delete mode 100644 src/arch/i386/interface/vmware/vmware.c create mode 100644 src/arch/x86/include/ipxe/guestrpc.h create mode 100644 src/arch/x86/include/ipxe/vmware.h create mode 100644 src/arch/x86/interface/vmware/guestinfo.c create mode 100644 src/arch/x86/interface/vmware/guestrpc.c create mode 100644 src/arch/x86/interface/vmware/vmconsole.c create mode 100644 src/arch/x86/interface/vmware/vmware.c (limited to 'src/arch') diff --git a/src/arch/i386/Makefile b/src/arch/i386/Makefile index 7f706a5a4..478e0634c 100644 --- a/src/arch/i386/Makefile +++ b/src/arch/i386/Makefile @@ -115,7 +115,6 @@ SRCDIRS += arch/i386/interface/pcbios SRCDIRS += arch/i386/interface/pxe SRCDIRS += arch/i386/interface/pxeparent SRCDIRS += arch/i386/interface/syslinux -SRCDIRS += arch/i386/interface/vmware SRCDIRS += arch/i386/hci/commands # Include common x86 Makefile diff --git a/src/arch/i386/include/ipxe/guestrpc.h b/src/arch/i386/include/ipxe/guestrpc.h deleted file mode 100644 index bc3d85506..000000000 --- a/src/arch/i386/include/ipxe/guestrpc.h +++ /dev/null @@ -1,68 +0,0 @@ -#ifndef _IPXE_GUESTRPC_H -#define _IPXE_GUESTRPC_H - -/** @file - * - * VMware GuestRPC mechanism - * - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -#include -#include - -/** GuestRPC magic number */ -#define GUESTRPC_MAGIC 0x49435052 /* "RPCI" */ - -/** Open RPC channel */ -#define GUESTRPC_OPEN 0x00 - -/** Open RPC channel success status */ -#define GUESTRPC_OPEN_SUCCESS 0x00010000 - -/** Send RPC command length */ -#define GUESTRPC_COMMAND_LEN 0x01 - -/** Send RPC command length success status */ -#define GUESTRPC_COMMAND_LEN_SUCCESS 0x00810000 - -/** Send RPC command data */ -#define GUESTRPC_COMMAND_DATA 0x02 - -/** Send RPC command data success status */ -#define GUESTRPC_COMMAND_DATA_SUCCESS 0x00010000 - -/** Receive RPC reply length */ -#define GUESTRPC_REPLY_LEN 0x03 - -/** Receive RPC reply length success status */ -#define GUESTRPC_REPLY_LEN_SUCCESS 0x00830000 - -/** Receive RPC reply data */ -#define GUESTRPC_REPLY_DATA 0x04 - -/** Receive RPC reply data success status */ -#define GUESTRPC_REPLY_DATA_SUCCESS 0x00010000 - -/** Finish receiving RPC reply */ -#define GUESTRPC_REPLY_FINISH 0x05 - -/** Finish receiving RPC reply success status */ -#define GUESTRPC_REPLY_FINISH_SUCCESS 0x00010000 - -/** Close RPC channel */ -#define GUESTRPC_CLOSE 0x06 - -/** Close RPC channel success status */ -#define GUESTRPC_CLOSE_SUCCESS 0x00010000 - -/** RPC command success status */ -#define GUESTRPC_SUCCESS 0x2031 /* "1 " */ - -extern int guestrpc_open ( void ); -extern void guestrpc_close ( int channel ); -extern int guestrpc_command ( int channel, const char *command, char *reply, - size_t reply_len ); - -#endif /* _IPXE_GUESTRPC_H */ diff --git a/src/arch/i386/include/ipxe/vmware.h b/src/arch/i386/include/ipxe/vmware.h deleted file mode 100644 index 24f60a03a..000000000 --- a/src/arch/i386/include/ipxe/vmware.h +++ /dev/null @@ -1,81 +0,0 @@ -#ifndef _IPXE_VMWARE_H -#define _IPXE_VMWARE_H - -/** @file - * - * VMware backdoor mechanism - * - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -#include - -/** VMware backdoor I/O port */ -#define VMW_PORT 0x5658 - -/** VMware backdoor magic value */ -#define VMW_MAGIC 0x564d5868 /* "VMXh" */ - -/** VMware backdoor magic instruction */ -#define VMW_BACKDOOR "inl %%dx, %%eax" - -/** Get VMware version */ -#define VMW_CMD_GET_VERSION 0x0a - -/** Issue GuestRPC command */ -#define VMW_CMD_GUESTRPC 0x1e - -/** - * Get VMware version - * - * @ret version VMware version(?) - * @ret magic VMware magic number, if present - * @ret product_type VMware product type - */ -static inline __attribute__ (( always_inline )) void -vmware_cmd_get_version ( uint32_t *version, uint32_t *magic, - uint32_t *product_type ) { - uint32_t discard_d; - - /* Perform backdoor call */ - __asm__ __volatile__ ( VMW_BACKDOOR - : "=a" ( *version ), "=b" ( *magic ), - "=c" ( *product_type ), "=d" ( discard_d ) - : "0" ( VMW_MAGIC ), "1" ( 0 ), - "2" ( VMW_CMD_GET_VERSION ), - "3" ( VMW_PORT ) ); -} - -/** - * Issue GuestRPC command - * - * @v channel Channel number - * @v subcommand GuestRPC subcommand - * @v parameter Subcommand-specific parameter - * @ret edxhi Subcommand-specific result - * @ret ebx Subcommand-specific result - * @ret status Command status - */ -static inline __attribute__ (( always_inline )) uint32_t -vmware_cmd_guestrpc ( int channel, uint16_t subcommand, uint32_t parameter, - uint16_t *edxhi, uint32_t *ebx ) { - uint32_t discard_a; - uint32_t status; - uint32_t edx; - - /* Perform backdoor call */ - __asm__ __volatile__ ( VMW_BACKDOOR - : "=a" ( discard_a ), "=b" ( *ebx ), - "=c" ( status ), "=d" ( edx ) - : "0" ( VMW_MAGIC ), "1" ( parameter ), - "2" ( VMW_CMD_GUESTRPC | ( subcommand << 16 )), - "3" ( VMW_PORT | ( channel << 16 ) ) ); - *edxhi = ( edx >> 16 ); - - return status; -} - -extern int vmware_present ( void ); - -#endif /* _IPXE_VMWARE_H */ diff --git a/src/arch/i386/interface/vmware/guestinfo.c b/src/arch/i386/interface/vmware/guestinfo.c deleted file mode 100644 index a0530c8d1..000000000 --- a/src/arch/i386/interface/vmware/guestinfo.c +++ /dev/null @@ -1,271 +0,0 @@ -/* - * Copyright (C) 2012 Michael Brown . - * - * 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 -#include -#include -#include -#include -#include -#include -#include -#include - -/** 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, -}; diff --git a/src/arch/i386/interface/vmware/guestrpc.c b/src/arch/i386/interface/vmware/guestrpc.c deleted file mode 100644 index ef7ee8151..000000000 --- a/src/arch/i386/interface/vmware/guestrpc.c +++ /dev/null @@ -1,332 +0,0 @@ -/* - * Copyright (C) 2012 Michael Brown . - * - * 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 -#include -#include -#include -#include -#include - -/* 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; -} diff --git a/src/arch/i386/interface/vmware/vmconsole.c b/src/arch/i386/interface/vmware/vmconsole.c deleted file mode 100644 index f7df4f75b..000000000 --- a/src/arch/i386/interface/vmware/vmconsole.c +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright (C) 2012 Michael Brown . - * - * 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 -#include -#include -#include -#include -#include - -/** 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, -}; diff --git a/src/arch/i386/interface/vmware/vmware.c b/src/arch/i386/interface/vmware/vmware.c deleted file mode 100644 index a415465fb..000000000 --- a/src/arch/i386/interface/vmware/vmware.c +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (C) 2012 Michael Brown . - * - * 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 -#include -#include - -/** - * 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; -} diff --git a/src/arch/x86/Makefile b/src/arch/x86/Makefile index 98c49b98d..4ab741db7 100644 --- a/src/arch/x86/Makefile +++ b/src/arch/x86/Makefile @@ -6,6 +6,7 @@ INCDIRS += arch/x86/include # SRCDIRS += arch/x86/core SRCDIRS += arch/x86/interface/efi +SRCDIRS += arch/x86/interface/vmware SRCDIRS += arch/x86/prefix SRCDIRS += arch/x86/hci/commands SRCDIRS += arch/x86/drivers/xen diff --git a/src/arch/x86/include/ipxe/guestrpc.h b/src/arch/x86/include/ipxe/guestrpc.h new file mode 100644 index 000000000..bc3d85506 --- /dev/null +++ b/src/arch/x86/include/ipxe/guestrpc.h @@ -0,0 +1,68 @@ +#ifndef _IPXE_GUESTRPC_H +#define _IPXE_GUESTRPC_H + +/** @file + * + * VMware GuestRPC mechanism + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include + +/** GuestRPC magic number */ +#define GUESTRPC_MAGIC 0x49435052 /* "RPCI" */ + +/** Open RPC channel */ +#define GUESTRPC_OPEN 0x00 + +/** Open RPC channel success status */ +#define GUESTRPC_OPEN_SUCCESS 0x00010000 + +/** Send RPC command length */ +#define GUESTRPC_COMMAND_LEN 0x01 + +/** Send RPC command length success status */ +#define GUESTRPC_COMMAND_LEN_SUCCESS 0x00810000 + +/** Send RPC command data */ +#define GUESTRPC_COMMAND_DATA 0x02 + +/** Send RPC command data success status */ +#define GUESTRPC_COMMAND_DATA_SUCCESS 0x00010000 + +/** Receive RPC reply length */ +#define GUESTRPC_REPLY_LEN 0x03 + +/** Receive RPC reply length success status */ +#define GUESTRPC_REPLY_LEN_SUCCESS 0x00830000 + +/** Receive RPC reply data */ +#define GUESTRPC_REPLY_DATA 0x04 + +/** Receive RPC reply data success status */ +#define GUESTRPC_REPLY_DATA_SUCCESS 0x00010000 + +/** Finish receiving RPC reply */ +#define GUESTRPC_REPLY_FINISH 0x05 + +/** Finish receiving RPC reply success status */ +#define GUESTRPC_REPLY_FINISH_SUCCESS 0x00010000 + +/** Close RPC channel */ +#define GUESTRPC_CLOSE 0x06 + +/** Close RPC channel success status */ +#define GUESTRPC_CLOSE_SUCCESS 0x00010000 + +/** RPC command success status */ +#define GUESTRPC_SUCCESS 0x2031 /* "1 " */ + +extern int guestrpc_open ( void ); +extern void guestrpc_close ( int channel ); +extern int guestrpc_command ( int channel, const char *command, char *reply, + size_t reply_len ); + +#endif /* _IPXE_GUESTRPC_H */ diff --git a/src/arch/x86/include/ipxe/vmware.h b/src/arch/x86/include/ipxe/vmware.h new file mode 100644 index 000000000..24f60a03a --- /dev/null +++ b/src/arch/x86/include/ipxe/vmware.h @@ -0,0 +1,81 @@ +#ifndef _IPXE_VMWARE_H +#define _IPXE_VMWARE_H + +/** @file + * + * VMware backdoor mechanism + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include + +/** VMware backdoor I/O port */ +#define VMW_PORT 0x5658 + +/** VMware backdoor magic value */ +#define VMW_MAGIC 0x564d5868 /* "VMXh" */ + +/** VMware backdoor magic instruction */ +#define VMW_BACKDOOR "inl %%dx, %%eax" + +/** Get VMware version */ +#define VMW_CMD_GET_VERSION 0x0a + +/** Issue GuestRPC command */ +#define VMW_CMD_GUESTRPC 0x1e + +/** + * Get VMware version + * + * @ret version VMware version(?) + * @ret magic VMware magic number, if present + * @ret product_type VMware product type + */ +static inline __attribute__ (( always_inline )) void +vmware_cmd_get_version ( uint32_t *version, uint32_t *magic, + uint32_t *product_type ) { + uint32_t discard_d; + + /* Perform backdoor call */ + __asm__ __volatile__ ( VMW_BACKDOOR + : "=a" ( *version ), "=b" ( *magic ), + "=c" ( *product_type ), "=d" ( discard_d ) + : "0" ( VMW_MAGIC ), "1" ( 0 ), + "2" ( VMW_CMD_GET_VERSION ), + "3" ( VMW_PORT ) ); +} + +/** + * Issue GuestRPC command + * + * @v channel Channel number + * @v subcommand GuestRPC subcommand + * @v parameter Subcommand-specific parameter + * @ret edxhi Subcommand-specific result + * @ret ebx Subcommand-specific result + * @ret status Command status + */ +static inline __attribute__ (( always_inline )) uint32_t +vmware_cmd_guestrpc ( int channel, uint16_t subcommand, uint32_t parameter, + uint16_t *edxhi, uint32_t *ebx ) { + uint32_t discard_a; + uint32_t status; + uint32_t edx; + + /* Perform backdoor call */ + __asm__ __volatile__ ( VMW_BACKDOOR + : "=a" ( discard_a ), "=b" ( *ebx ), + "=c" ( status ), "=d" ( edx ) + : "0" ( VMW_MAGIC ), "1" ( parameter ), + "2" ( VMW_CMD_GUESTRPC | ( subcommand << 16 )), + "3" ( VMW_PORT | ( channel << 16 ) ) ); + *edxhi = ( edx >> 16 ); + + return status; +} + +extern int vmware_present ( void ); + +#endif /* _IPXE_VMWARE_H */ diff --git a/src/arch/x86/interface/vmware/guestinfo.c b/src/arch/x86/interface/vmware/guestinfo.c new file mode 100644 index 000000000..a0530c8d1 --- /dev/null +++ b/src/arch/x86/interface/vmware/guestinfo.c @@ -0,0 +1,271 @@ +/* + * Copyright (C) 2012 Michael Brown . + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +/** 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, +}; diff --git a/src/arch/x86/interface/vmware/guestrpc.c b/src/arch/x86/interface/vmware/guestrpc.c new file mode 100644 index 000000000..ef7ee8151 --- /dev/null +++ b/src/arch/x86/interface/vmware/guestrpc.c @@ -0,0 +1,332 @@ +/* + * Copyright (C) 2012 Michael Brown . + * + * 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 +#include +#include +#include +#include +#include + +/* 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; +} diff --git a/src/arch/x86/interface/vmware/vmconsole.c b/src/arch/x86/interface/vmware/vmconsole.c new file mode 100644 index 000000000..f7df4f75b --- /dev/null +++ b/src/arch/x86/interface/vmware/vmconsole.c @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2012 Michael Brown . + * + * 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 +#include +#include +#include +#include +#include + +/** 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, +}; diff --git a/src/arch/x86/interface/vmware/vmware.c b/src/arch/x86/interface/vmware/vmware.c new file mode 100644 index 000000000..a415465fb --- /dev/null +++ b/src/arch/x86/interface/vmware/vmware.c @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2012 Michael Brown . + * + * 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 +#include +#include + +/** + * 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; +} -- cgit v1.2.3-55-g7522