summaryrefslogtreecommitdiffstats
path: root/qemu/roms/ipxe/src/arch/i386/interface/vmware/guestinfo.c
diff options
context:
space:
mode:
Diffstat (limited to 'qemu/roms/ipxe/src/arch/i386/interface/vmware/guestinfo.c')
-rw-r--r--qemu/roms/ipxe/src/arch/i386/interface/vmware/guestinfo.c271
1 files changed, 271 insertions, 0 deletions
diff --git a/qemu/roms/ipxe/src/arch/i386/interface/vmware/guestinfo.c b/qemu/roms/ipxe/src/arch/i386/interface/vmware/guestinfo.c
new file mode 100644
index 000000000..a0530c8d1
--- /dev/null
+++ b/qemu/roms/ipxe/src/arch/i386/interface/vmware/guestinfo.c
@@ -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,
+};