* [Qemu-devel] [PATCH 0/6] Add limited support of VMware's hyper-call rpc
@ 2015-01-30 21:06 Don Slutz
2015-01-30 21:06 ` [Qemu-devel] [PATCH 1/6] vmport.c: Fix vmport_cmd_ram_size Don Slutz
` (5 more replies)
0 siblings, 6 replies; 9+ messages in thread
From: Don Slutz @ 2015-01-30 21:06 UTC (permalink / raw)
To: qemu-devel
Cc: Michael S. Tsirkin, Markus Armbruster, Don Slutz, Luiz Capitulino,
Anthony Liguori, Paolo Bonzini, Andreas Färber,
Richard Henderson
The support included is enough to allow VMware tools to install in a
guest and provide guestinfo support. guestinfo support is provided
by what is known as VMware RPC support.
One of the better on-line references is:
https://sites.google.com/site/chitchatvmback/backdoor
As a place to get more accurate information by studying:
http://open-vm-tools.sourceforge.net/
With vmware tools installed, you can do:
-------------------------------------------------------------------------------
Last login: Fri Jan 30 16:03:08 2015
[root@C63-min-tools ~]# vmtoolsd --cmd "info-get guestinfo.joejoel"
No value found
[root@C63-min-tools ~]# vmtoolsd --cmd "info-set guestinfo.joejoel bar"
[root@C63-min-tools ~]# vmtoolsd --cmd "info-get guestinfo.joejoel"
bar
[root@C63-min-tools ~]#
-------------------------------------------------------------------------------
to access guest info. QMP access is also provided.
The live migration code is still in progress.
Don Slutz (6):
vmport.c: Fix vmport_cmd_ram_size
vmport_rpc: Add the object vmport_rpc
vmport_rpc: Add limited support of VMware's hyper-call rpc
vmport_rpc: Add QMP access to vmport_rpc object.
vmport: Add VMware all ring hack
MAINTAINERS: add VMware port
MAINTAINERS | 7 +
hw/i386/pc.c | 34 +-
hw/i386/pc_piix.c | 2 +-
hw/i386/pc_q35.c | 2 +-
hw/misc/Makefile.objs | 1 +
hw/misc/vmport.c | 2 +-
hw/misc/vmport_rpc.c | 1188 ++++++++++++++++++++++++++++++++++++++++++++++
include/hw/i386/pc.h | 6 +-
qapi-schema.json | 95 ++++
qmp-commands.hx | 141 ++++++
target-i386/cpu.c | 4 +
target-i386/cpu.h | 2 +
target-i386/seg_helper.c | 6 +
trace-events | 20 +
14 files changed, 1505 insertions(+), 5 deletions(-)
create mode 100644 hw/misc/vmport_rpc.c
--
1.8.4
^ permalink raw reply [flat|nested] 9+ messages in thread
* [Qemu-devel] [PATCH 1/6] vmport.c: Fix vmport_cmd_ram_size
2015-01-30 21:06 [Qemu-devel] [PATCH 0/6] Add limited support of VMware's hyper-call rpc Don Slutz
@ 2015-01-30 21:06 ` Don Slutz
2015-01-30 21:06 ` [Qemu-devel] [PATCH 2/6] vmport_rpc: Add the object vmport_rpc Don Slutz
` (4 subsequent siblings)
5 siblings, 0 replies; 9+ messages in thread
From: Don Slutz @ 2015-01-30 21:06 UTC (permalink / raw)
To: qemu-devel
Cc: Michael S. Tsirkin, Markus Armbruster, Don Slutz, Luiz Capitulino,
Anthony Liguori, Paolo Bonzini, Andreas Färber,
Richard Henderson
Based on
https://sites.google.com/site/chitchatvmback/backdoor
and testing on ESXi, this should be in MB not bytes.
Signed-off-by: Don Slutz <dslutz@verizon.com>
---
hw/misc/vmport.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/hw/misc/vmport.c b/hw/misc/vmport.c
index 7fcc00d..6b350ce 100644
--- a/hw/misc/vmport.c
+++ b/hw/misc/vmport.c
@@ -110,7 +110,7 @@ static uint32_t vmport_cmd_ram_size(void *opaque, uint32_t addr)
X86CPU *cpu = X86_CPU(current_cpu);
cpu->env.regs[R_EBX] = 0x1177;
- return ram_size;
+ return ram_size >> 20; /* in MB */
}
static uint32_t vmport_cmd_xtest(void *opaque, uint32_t addr)
--
1.8.4
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [Qemu-devel] [PATCH 2/6] vmport_rpc: Add the object vmport_rpc
2015-01-30 21:06 [Qemu-devel] [PATCH 0/6] Add limited support of VMware's hyper-call rpc Don Slutz
2015-01-30 21:06 ` [Qemu-devel] [PATCH 1/6] vmport.c: Fix vmport_cmd_ram_size Don Slutz
@ 2015-01-30 21:06 ` Don Slutz
2015-01-30 21:06 ` [Qemu-devel] [PATCH 3/6] vmport_rpc: Add limited support of VMware's hyper-call rpc Don Slutz
` (3 subsequent siblings)
5 siblings, 0 replies; 9+ messages in thread
From: Don Slutz @ 2015-01-30 21:06 UTC (permalink / raw)
To: qemu-devel
Cc: Michael S. Tsirkin, Markus Armbruster, Don Slutz, Luiz Capitulino,
Anthony Liguori, Paolo Bonzini, Andreas Färber,
Richard Henderson
This is the 1st part of "Add limited support of VMware's hyper-call
rpc".
This patch uses existing infrastructure used by vmmouse.c (provided
by vmport.c) to handle the VMware backdoor command 30.
One of the better on-line references is:
https://sites.google.com/site/chitchatvmback/backdoor
More in next patch.
Signed-off-by: Don Slutz <dslutz@verizon.com>
---
hw/i386/pc.c | 6 +++
hw/misc/Makefile.objs | 1 +
hw/misc/vmport_rpc.c | 126 ++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 133 insertions(+)
create mode 100644 hw/misc/vmport_rpc.c
diff --git a/hw/i386/pc.c b/hw/i386/pc.c
index c7af6aa..efae4d5 100644
--- a/hw/i386/pc.c
+++ b/hw/i386/pc.c
@@ -1435,8 +1435,14 @@ void pc_basic_device_init(ISABus *isa_bus, qemu_irq *gsi,
i8042 = isa_create_simple(isa_bus, "i8042");
i8042_setup_a20_line(i8042, &a20_line[0]);
if (!no_vmport) {
+ ISADevice *vmport_rpc;
+
vmport_init(isa_bus);
vmmouse = isa_try_create(isa_bus, "vmmouse");
+ vmport_rpc = isa_try_create(isa_bus, "vmport_rpc");
+ if (vmport_rpc) {
+ qdev_init_nofail(DEVICE(vmport_rpc));
+ }
} else {
vmmouse = NULL;
}
diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs
index 029a56f..5496992 100644
--- a/hw/misc/Makefile.objs
+++ b/hw/misc/Makefile.objs
@@ -7,6 +7,7 @@ common-obj-$(CONFIG_ISA_TESTDEV) += pc-testdev.o
common-obj-$(CONFIG_PCI_TESTDEV) += pci-testdev.o
obj-$(CONFIG_VMPORT) += vmport.o
+obj-$(CONFIG_VMPORT) += vmport_rpc.o
# ARM devices
common-obj-$(CONFIG_PL310) += arm_l2x0.o
diff --git a/hw/misc/vmport_rpc.c b/hw/misc/vmport_rpc.c
new file mode 100644
index 0000000..da724a4
--- /dev/null
+++ b/hw/misc/vmport_rpc.c
@@ -0,0 +1,126 @@
+/*
+ * QEMU VMPORT RPC emulation
+ *
+ * Copyright (C) 2015 Verizon Corporation
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License Version 2 (GPLv2)
+ * as published by the Free Software Foundation.
+ *
+ * This file 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. <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * One of the better on-line references is:
+ *
+ * https://sites.google.com/site/chitchatvmback/backdoor
+ *
+ * Which points you to:
+ *
+ * http://open-vm-tools.sourceforge.net/
+ *
+ * as a place to get more accurate information by studying.
+ */
+
+#include "hw/hw.h"
+#include "hw/i386/pc.h"
+#include "hw/qdev.h"
+#include "trace.h"
+#include "qmp-commands.h"
+#include "qapi/qmp/qerror.h"
+
+/* #define VMPORT_RPC_DEBUG */
+
+#define TYPE_VMPORT_RPC "vmport_rpc"
+#define VMPORT_RPC(obj) OBJECT_CHECK(VMPortRpcState, (obj), TYPE_VMPORT_RPC)
+
+/* VMPORT RPC Command */
+#define VMPORT_RPC_COMMAND 30
+
+/* The vmport_rpc object. */
+typedef struct VMPortRpcState {
+ ISADevice parent_obj;
+
+ /* Properties */
+ uint64_t reset_time;
+ uint64_t build_number_value;
+ uint64_t build_number_time;
+
+ /* Private data */
+} VMPortRpcState;
+
+typedef struct {
+ uint32_t eax;
+ uint32_t ebx;
+ uint32_t ecx;
+ uint32_t edx;
+ uint32_t esi;
+ uint32_t edi;
+} vregs;
+
+static uint32_t vmport_rpc_ioport_read(void *opaque, uint32_t addr)
+{
+ VMPortRpcState *s = opaque;
+ union {
+ uint32_t data[6];
+ vregs regs;
+ } ur;
+
+ vmmouse_get_data(ur.data);
+
+ s->build_number_time++;
+
+ vmmouse_set_data(ur.data);
+ return ur.data[0];
+}
+
+static void vmport_rpc_reset(DeviceState *d)
+{
+ VMPortRpcState *s = VMPORT_RPC(d);
+
+ s->reset_time = 14;
+ s->build_number_value = 0;
+ s->build_number_time = 0;
+}
+
+static void vmport_rpc_realizefn(DeviceState *dev, Error **errp)
+{
+ VMPortRpcState *s = VMPORT_RPC(dev);
+
+ vmport_register(VMPORT_RPC_COMMAND, vmport_rpc_ioport_read, s);
+}
+
+static Property vmport_rpc_properties[] = {
+ DEFINE_PROP_UINT64("reset-time", VMPortRpcState, reset_time, 14),
+ DEFINE_PROP_UINT64("build-number-value", VMPortRpcState,
+ build_number_value, 0),
+ DEFINE_PROP_UINT64("build-number-time", VMPortRpcState,
+ build_number_time, 0),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void vmport_rpc_class_initfn(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->realize = vmport_rpc_realizefn;
+ dc->reset = vmport_rpc_reset;
+ dc->props = vmport_rpc_properties;
+}
+
+static const TypeInfo vmport_rpc_info = {
+ .name = TYPE_VMPORT_RPC,
+ .parent = TYPE_ISA_DEVICE,
+ .instance_size = sizeof(VMPortRpcState),
+ .class_init = vmport_rpc_class_initfn,
+};
+
+static void vmport_rpc_register_types(void)
+{
+ type_register_static(&vmport_rpc_info);
+}
+
+type_init(vmport_rpc_register_types)
--
1.8.4
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [Qemu-devel] [PATCH 3/6] vmport_rpc: Add limited support of VMware's hyper-call rpc
2015-01-30 21:06 [Qemu-devel] [PATCH 0/6] Add limited support of VMware's hyper-call rpc Don Slutz
2015-01-30 21:06 ` [Qemu-devel] [PATCH 1/6] vmport.c: Fix vmport_cmd_ram_size Don Slutz
2015-01-30 21:06 ` [Qemu-devel] [PATCH 2/6] vmport_rpc: Add the object vmport_rpc Don Slutz
@ 2015-01-30 21:06 ` Don Slutz
2015-01-30 21:06 ` [Qemu-devel] [PATCH 4/6] vmport_rpc: Add QMP access to vmport_rpc object Don Slutz
` (2 subsequent siblings)
5 siblings, 0 replies; 9+ messages in thread
From: Don Slutz @ 2015-01-30 21:06 UTC (permalink / raw)
To: qemu-devel
Cc: Michael S. Tsirkin, Markus Armbruster, Don Slutz, Luiz Capitulino,
Anthony Liguori, Paolo Bonzini, Andreas Färber,
Richard Henderson
The support included is enough to allow VMware tools to install in a
guest and provide guestinfo support. guestinfo support is provided
by what is known as VMware RPC support.
If the guest is running VMware tools, then the "build version" of
the tools is also available via the property build-number-value of
the vmport_rpc object. The build-number-time property is changed
every time the VMware tools running in the guest sends the
"build version" via the rpc.
The property reset-time controls how often to request them in
seconds minus 1. The minus 1 is to handle to 0 case. I.E. the
fastest that can be selected is every second. The default is 4
times a minute.
The VMware RPC support includes the notion of channels that are
opened, active and closed. All RPC messages sent via a channel
starts with normal ASCII text. The message some times does include
binary data.
Currently there are 2 protocols defined for VMware RPC. They
determine the direction for data flow, guest to tool stack or tool
stack to guest.
There is no provided interrupt for VMware RPC.
Getting VMPORT_RPC_DEBUG will provide a higher level trace calls
that are simpler to understand.
Signed-off-by: Don Slutz <dslutz@verizon.com>
---
hw/misc/vmport_rpc.c | 789 ++++++++++++++++++++++++++++++++++++++++++++++++++-
trace-events | 20 ++
2 files changed, 808 insertions(+), 1 deletion(-)
diff --git a/hw/misc/vmport_rpc.c b/hw/misc/vmport_rpc.c
index da724a4..e12f912 100644
--- a/hw/misc/vmport_rpc.c
+++ b/hw/misc/vmport_rpc.c
@@ -40,6 +40,125 @@
/* VMPORT RPC Command */
#define VMPORT_RPC_COMMAND 30
+/* Limits on amount of non guest memory to use */
+#define MAX_KEY_LEN 128
+#define MIN_VAL_LEN 64
+#define MAX_VAL_LEN 8192
+#define MAX_NUM_KEY 256
+#define MAX_BKTS 4
+/* Max number of channels. */
+#define GUESTMSG_MAX_CHANNEL 8
+
+/*
+ * All of VMware's rpc is based on 32bit registers. So this is the
+ * number of bytes in ebx.
+ */
+#define CHAR_PER_CALL sizeof(uint32_t)
+/* Room for basic commands */
+#define EXTRA_SEND 22
+/* Status code and NULL */
+#define EXTRA_RECV 2
+#define MAX_SEND_BUF DIV_ROUND_UP(EXTRA_SEND + MAX_KEY_LEN + MAX_VAL_LEN, \
+ CHAR_PER_CALL)
+#define MAX_RECV_BUF DIV_ROUND_UP(EXTRA_RECV + MAX_VAL_LEN, CHAR_PER_CALL)
+#define MIN_SEND_BUF DIV_ROUND_UP(EXTRA_SEND + MAX_KEY_LEN + MIN_VAL_LEN, \
+ CHAR_PER_CALL)
+
+/* Reply statuses */
+/* The basic request succeeded */
+#define MESSAGE_STATUS_SUCCESS 0x0001
+/* vmware has a message available for its party */
+#define MESSAGE_STATUS_DORECV 0x0002
+/* The channel has been closed */
+#define MESSAGE_STATUS_CLOSED 0x0004
+/* vmware removed the message before the party fetched it */
+#define MESSAGE_STATUS_UNSENT 0x0008
+/* A checkpoint occurred */
+#define MESSAGE_STATUS_CPT 0x0010
+/* An underlying device is powering off */
+#define MESSAGE_STATUS_POWEROFF 0x0020
+/* vmware has detected a timeout on the channel */
+#define MESSAGE_STATUS_TIMEOUT 0x0040
+/* vmware supports high-bandwidth for sending and receiving the payload */
+#define MESSAGE_STATUS_HB 0x0080
+
+/* Max number of channels. */
+#define GUESTMSG_MAX_CHANNEL 8
+
+/* Flags to open a channel. */
+#define GUESTMSG_FLAG_COOKIE 0x80000000
+
+/* Data to guest */
+#define VMWARE_PROTO_TO_GUEST 0x4f4c4354
+/* Data from guest */
+#define VMWARE_PROTO_FROM_GUEST 0x49435052
+
+/*
+ * Error return values used only in this file. The routine
+ * convert_local_rc() is used to convert these to an Error
+ * object.
+ */
+#define VMPORT_DEVICE_NOT_FOUND -1
+#define SEND_NOT_OPEN -2
+#define SEND_SKIPPED -3
+#define SEND_TRUCATED -4
+#define SEND_NO_MEMORY -5
+#define GUESTINFO_NOTFOUND -6
+#define GUESTINFO_VALTOOLONG -7
+#define GUESTINFO_KEYTOOLONG -8
+#define GUESTINFO_TOOMANYKEYS -9
+#define GUESTINFO_NO_MEMORY -10
+
+
+/* The VMware RPC guest info storage . */
+typedef struct {
+ char *key_data;
+ char *val_data;
+ uint16_t val_len;
+ uint16_t val_max;
+ uint8_t key_len;
+} guestinfo_t;
+
+/* The VMware RPC bucket control. */
+typedef struct {
+ uint16_t recv_len;
+ uint16_t recv_idx;
+} bucket_control_t;
+
+/* The VMware RPC bucket info. */
+typedef struct {
+ union {
+ uint32_t *words;
+ char *bytes;
+ } recv;
+ bucket_control_t ctl;
+ uint16_t recv_buf_max;
+} bucket_t;
+
+
+/* The VMware RPC channel control. */
+typedef struct {
+ uint64_t active_time;
+ uint32_t chan_id;
+ uint32_t cookie;
+ uint32_t proto_num;
+ uint16_t send_len;
+ uint16_t send_idx;
+ uint8_t recv_read;
+ uint8_t recv_write;
+} channel_control_t;
+
+/* The VMware RPC channel info. */
+typedef struct {
+ union {
+ uint32_t *words;
+ char *bytes;
+ } send;
+ channel_control_t ctl;
+ bucket_t recv_bkt[MAX_BKTS];
+ uint16_t send_buf_max;
+} channel_t;
+
/* The vmport_rpc object. */
typedef struct VMPortRpcState {
ISADevice parent_obj;
@@ -50,8 +169,46 @@ typedef struct VMPortRpcState {
uint64_t build_number_time;
/* Private data */
+ uint64_t ping_time;
+ uint32_t open_cookie;
+ uint32_t used_guestinfo;
+ channel_t chans[GUESTMSG_MAX_CHANNEL];
+ guestinfo_t *guestinfo[MAX_NUM_KEY];
+#ifdef VMPORT_RPC_DEBUG
+ unsigned int end;
+ unsigned int last;
+ char out[2048];
+#endif
} VMPortRpcState;
+typedef int FindVMPortRpcDeviceFunc(VMPortRpcState *s, void *arg);
+
+/*
+ * The input and output argumenet that find_VMPortRpc_device() uses
+ * to do it's work.
+ */
+typedef struct VMPortRpcFind {
+ void *arg;
+ FindVMPortRpcDeviceFunc *func;
+ int found;
+ int rc;
+} VMPortRpcFind;
+
+/* Basic request types */
+typedef enum {
+ MESSAGE_TYPE_OPEN,
+ MESSAGE_TYPE_SENDSIZE,
+ MESSAGE_TYPE_SENDPAYLOAD,
+ MESSAGE_TYPE_RECVSIZE,
+ MESSAGE_TYPE_RECVPAYLOAD,
+ MESSAGE_TYPE_RECVSTATUS,
+ MESSAGE_TYPE_CLOSE,
+} MessageType;
+
+/*
+ * Overlay on the array that vmmouse_get_data() returns. The code is
+ * easier to read using register names.
+ */
typedef struct {
uint32_t eax;
uint32_t ebx;
@@ -61,6 +218,636 @@ typedef struct {
uint32_t edi;
} vregs;
+#ifdef VMPORT_RPC_DEBUG
+/*
+ * Add helper function for traceing. This routine will convert
+ * binary data into more normal characters so that the trace data is
+ * earier to read and will not have nulls in it.
+ */
+static void safe_print(VMPortRpcState *s, int len, const char *msg)
+{
+ unsigned char c;
+ unsigned int i, k;
+
+ s->end = len;
+ /* 1 byte can cnvert to 3 bytes, and save room at the end. */
+ if (s->end > (sizeof(s->out) / 3 - 6)) {
+ s->end = sizeof(s->out) / 3 - 6;
+ }
+ s->out[0] = '<';
+ k = 1;
+ for (i = 0; i < s->end; i++) {
+ c = msg[i];
+ if ((c == '^') || (c == '\\') || (c == '>')) {
+ s->out[k++] = '\\';
+ s->out[k++] = c;
+ } else if ((c >= ' ') && (c <= '~')) {
+ s->out[k++] = c;
+ } else if (c < ' ') {
+ s->out[k++] = '^';
+ s->out[k++] = c ^ 0x40;
+ } else {
+ snprintf(&s->out[k], sizeof(s->out) - k, "\\%02x", c);
+ k += 3;
+ }
+ }
+ s->out[k++] = '>';
+ if (len > s->end) {
+ s->out[k++] = '.';
+ s->out[k++] = '.';
+ s->out[k++] = '.';
+ }
+ s->out[k++] = 0;
+ s->last = k;
+}
+#endif
+
+/*
+ * Copy message into a free receive bucket buffer which vmtools will
+ * use to read from 4 (CHAR_PER_CALL) bytes at a time until done
+ * with it.
+ */
+static int send(VMPortRpcState *s, channel_t *c,
+ const char *msg, unsigned int cur_recv_len)
+{
+ int rc;
+ unsigned int my_bkt = c->ctl.recv_write;
+ unsigned int next_bkt = my_bkt + 1;
+ bucket_t *b;
+
+ if (next_bkt >= MAX_BKTS) {
+ next_bkt = 0;
+ }
+
+ if (next_bkt == c->ctl.recv_read) {
+#ifdef VMPORT_RPC_DEBUG
+ {
+ char prefix[30];
+
+ snprintf(prefix, sizeof(prefix),
+ "VMware _send skipped %d (%d, %d) ",
+ c->ctl.chan_id, my_bkt, c->ctl.recv_read);
+ prefix[sizeof(prefix) - 1] = 0;
+ safe_print(s, cur_recv_len, msg);
+ trace_vmport_rpc_send_skip(prefix, s->end, cur_recv_len, s->last,
+ sizeof(s->out), s->out);
+ }
+#endif
+ return SEND_SKIPPED;
+ }
+
+ c->ctl.recv_write = next_bkt;
+ b = &c->recv_bkt[my_bkt];
+#ifdef VMPORT_RPC_DEBUG
+ {
+ char prefix[30];
+
+ snprintf(prefix, sizeof(prefix), "VMware _send %d (%d) ",
+ c->ctl.chan_id, my_bkt);
+ prefix[sizeof(prefix) - 1] = 0;
+ safe_print(s, cur_recv_len, msg);
+ trace_vmport_rpc_send_normal(prefix, s->end, cur_recv_len, s->last,
+ sizeof(s->out), s->out);
+ }
+#endif
+
+ if (b->recv_buf_max < MAX_RECV_BUF) {
+ size_t new_recv_buf_max = DIV_ROUND_UP(cur_recv_len, CHAR_PER_CALL);
+
+ if (new_recv_buf_max > b->recv_buf_max) {
+ uint32_t *new_recv_buf =
+ g_try_malloc((new_recv_buf_max + 1) * CHAR_PER_CALL);
+
+ if (new_recv_buf) {
+ g_free(b->recv.words);
+ b->recv.words = new_recv_buf;
+ b->recv_buf_max = new_recv_buf_max;
+ }
+ }
+ }
+ if (!b->recv.words) {
+ return SEND_NO_MEMORY;
+ }
+ b->ctl.recv_len = cur_recv_len;
+ b->ctl.recv_idx = 0;
+ rc = 0;
+ if (cur_recv_len > (b->recv_buf_max * CHAR_PER_CALL)) {
+ trace_vmport_rpc_send_big(cur_recv_len,
+ b->recv_buf_max * CHAR_PER_CALL);
+ cur_recv_len = b->recv_buf_max * CHAR_PER_CALL;
+ b->recv.words[b->recv_buf_max] = 0;
+ rc = SEND_TRUCATED;
+ } else {
+ b->recv.words[cur_recv_len / CHAR_PER_CALL] = 0;
+ b->recv.words[DIV_ROUND_UP(cur_recv_len, CHAR_PER_CALL)] = 0;
+ }
+ memcpy(b->recv.words, msg, cur_recv_len);
+ return rc;
+}
+
+static int ctrl_send(VMPortRpcState *s, char *msg)
+{
+ int rc = SEND_NOT_OPEN;
+ unsigned int i;
+
+ if (!s) {
+ return rc;
+ }
+ s->ping_time = get_clock() / 1000000000LL;
+ for (i = 0; i < GUESTMSG_MAX_CHANNEL; i++) {
+ if (s->chans[i].ctl.proto_num == VMWARE_PROTO_TO_GUEST) {
+ rc = send(s, &s->chans[i], msg, strlen(msg) + 1);
+ }
+ }
+ return rc;
+}
+
+static void sweep(VMPortRpcState *s, unsigned long now_time)
+{
+ unsigned int i;
+
+ for (i = 0; i < GUESTMSG_MAX_CHANNEL; i++) {
+ if (s->chans[i].ctl.proto_num) {
+ channel_t *c = &s->chans[i];
+ long delta = now_time - c->ctl.active_time;
+
+ if (delta >= 80) {
+ trace_vmport_rpc_sweep(c->ctl.chan_id, delta);
+ /* Return channel to free pool */
+ c->ctl.proto_num = 0;
+ }
+ }
+ }
+}
+
+static channel_t *new_chan(VMPortRpcState *s, unsigned long now_time)
+{
+ unsigned int i;
+
+ for (i = 0; i < GUESTMSG_MAX_CHANNEL; i++) {
+ if (!s->chans[i].ctl.proto_num) {
+ channel_t *c = &s->chans[i];
+
+ c->ctl.chan_id = i;
+ c->ctl.cookie = s->open_cookie++;
+ c->ctl.active_time = now_time;
+ c->ctl.send_len = 0;
+ c->ctl.send_idx = 0;
+ c->ctl.recv_read = 0;
+ c->ctl.recv_write = 0;
+ if (!c->send.words) {
+ uint32_t *new_send_buf =
+ g_try_malloc0((MIN_SEND_BUF + 1) * CHAR_PER_CALL);
+ if (new_send_buf) {
+ c->send.words = new_send_buf;
+ c->send_buf_max = MIN_SEND_BUF;
+ }
+ }
+ if (!c->send.words) {
+ return NULL;
+ }
+ return c;
+ }
+ }
+ return NULL;
+}
+
+static void process_send_size(VMPortRpcState *s, channel_t *c,
+ vregs *ur)
+{
+ /* vmware tools often send a 0 byte request size. */
+ c->ctl.send_len = ur->ebx;
+ c->ctl.send_idx = 0;
+
+ if (c->send_buf_max < MAX_SEND_BUF) {
+ size_t new_send_max = DIV_ROUND_UP(c->ctl.send_len, CHAR_PER_CALL);
+
+ if (new_send_max > c->send_buf_max) {
+ uint32_t *new_send_buf =
+ g_try_malloc0((new_send_max + 1) * CHAR_PER_CALL);
+
+ if (new_send_buf) {
+ g_free(c->send.words);
+ c->send.words = new_send_buf;
+ c->send_buf_max = new_send_max;
+ }
+ }
+ }
+ ur->ecx = (MESSAGE_STATUS_SUCCESS << 16) | (ur->ecx & 0xffff);
+ trace_vmport_detail_rpc_process_send_size(c->ctl.chan_id,
+ c->ctl.send_len);
+}
+
+/* ret_buffer is in/out param */
+static int get_guestinfo(VMPortRpcState *s,
+ char *a_info_key, unsigned int a_key_len,
+ char *ret_buffer, unsigned int ret_buffer_len)
+{
+ unsigned int i;
+
+ for (i = 0; i < s->used_guestinfo; i++) {
+ if (s->guestinfo[i] &&
+ (s->guestinfo[i]->key_len == a_key_len) &&
+ (memcmp(a_info_key, s->guestinfo[i]->key_data,
+ s->guestinfo[i]->key_len) == 0)) {
+ unsigned int ret_len = 2 + s->guestinfo[i]->val_len;
+
+ ret_buffer[0] = '1';
+ ret_buffer[1] = ' ';
+ if (ret_len > ret_buffer_len - 1) {
+ ret_len = ret_buffer_len - 1;
+ }
+ memcpy(ret_buffer + 2, s->guestinfo[i]->val_data, ret_len);
+ return ret_len;
+ }
+ }
+
+ return GUESTINFO_NOTFOUND;
+}
+
+static int set_guestinfo(VMPortRpcState *s, int a_key_len,
+ unsigned int a_val_len, char *a_info_key, char *val)
+{
+ unsigned int i;
+ int rc = 0;
+
+ trace_vmport_rpc_set_guestinfo(a_key_len, a_key_len, a_info_key,
+ a_val_len, a_val_len, val);
+
+ if (a_key_len <= MAX_KEY_LEN) {
+ if (a_val_len <= MAX_VAL_LEN) {
+ for (i = 0; i < s->used_guestinfo; i++) {
+ if (!s->guestinfo[i]) {
+ trace_vmport_rpc_set_guestinfo_bad(
+ i, s->used_guestinfo);
+ } else if ((s->guestinfo[i]->key_len == a_key_len) &&
+ (memcmp(a_info_key, s->guestinfo[i]->key_data,
+ s->guestinfo[i]->key_len) == 0)) {
+ if (a_val_len > s->guestinfo[i]->val_max) {
+ char *new_val = g_try_malloc(a_val_len);
+
+ if (!new_val) {
+ return GUESTINFO_NO_MEMORY;
+ }
+ g_free(s->guestinfo[i]->val_data);
+ s->guestinfo[i]->val_max = a_val_len;
+ s->guestinfo[i]->val_data = new_val;
+ }
+ s->guestinfo[i]->val_len = a_val_len;
+ memcpy(s->guestinfo[i]->val_data, val, a_val_len);
+ break;
+ }
+ }
+ if (i >= s->used_guestinfo) {
+ int free_i = s->used_guestinfo;
+ int new_val_len = a_val_len;
+
+ if (new_val_len < MIN_VAL_LEN) {
+ new_val_len = MIN_VAL_LEN;
+ }
+ if (free_i < MAX_NUM_KEY) {
+ s->guestinfo[free_i] =
+ g_try_malloc(sizeof(*s->guestinfo[free_i]));
+ } else {
+ return GUESTINFO_TOOMANYKEYS;
+ }
+ if (!s->guestinfo[free_i]) {
+ return GUESTINFO_NO_MEMORY;
+ }
+ s->guestinfo[free_i]->key_data = g_try_malloc(a_key_len);
+ if (!s->guestinfo[free_i]->key_data) {
+ return GUESTINFO_NO_MEMORY;
+ }
+ s->guestinfo[free_i]->val_data = g_try_malloc(new_val_len);
+ if (!s->guestinfo[free_i]->val_data) {
+ return GUESTINFO_NO_MEMORY;
+ }
+ s->used_guestinfo++;
+ s->guestinfo[free_i]->key_len = a_key_len;
+ s->guestinfo[free_i]->val_len = a_val_len;
+ s->guestinfo[free_i]->val_max = new_val_len;
+ memcpy(s->guestinfo[free_i]->key_data,
+ a_info_key, a_key_len);
+ memcpy(s->guestinfo[free_i]->val_data,
+ val, a_val_len);
+ }
+ } else {
+ rc = GUESTINFO_VALTOOLONG;
+ }
+ } else {
+ rc = GUESTINFO_KEYTOOLONG;
+ }
+ return rc;
+}
+
+static void process_send_payload(VMPortRpcState *s,
+ channel_t *c,
+ vregs *ur,
+ unsigned long now_time)
+{
+ /* Accumulate 4 (CHAR_PER_CALL) bytes of paload into send_buf
+ * using offset */
+ if (c->ctl.send_idx < c->send_buf_max) {
+ c->send.words[c->ctl.send_idx] = ur->ebx;
+ }
+
+ c->ctl.send_idx++;
+ ur->ecx = (MESSAGE_STATUS_SUCCESS << 16) | (ur->ecx & 0xffff);
+
+ if (c->ctl.send_idx * CHAR_PER_CALL >= c->ctl.send_len) {
+
+ /* We are done accumulating so handle the command */
+
+ if (c->ctl.send_idx <= c->send_buf_max) {
+ c->send.words[c->ctl.send_idx] = 0;
+ }
+#ifdef VMPORT_RPC_DEBUG
+ {
+ char prefix[30];
+
+ snprintf(prefix, sizeof(prefix),
+ "VMware RECV %d (%d) ",
+ c->ctl.chan_id, c->ctl.recv_read);
+ prefix[sizeof(prefix) - 1] = 0;
+ safe_print(s, MIN(c->ctl.send_len,
+ c->send_buf_max * CHAR_PER_CALL),
+ c->send.bytes);
+ trace_vmport_rpc_recv_normal(prefix, s->end, c->ctl.send_len,
+ s->last, sizeof(s->out), s->out);
+ }
+#endif
+ if (c->ctl.proto_num == VMWARE_PROTO_FROM_GUEST) {
+ /*
+ * Eaxmples of messages:
+ *
+ * log toolbox: Version: build-341836
+ * SetGuestInfo 4 build-341836
+ * info-get guestinfo.ip
+ * info-set guestinfo.ip joe
+ *
+ */
+
+ char *build = NULL;
+ char *info_key = NULL;
+ char *ret_msg = (char *)"1 ";
+ char ret_buffer[2 + MAX_VAL_LEN + 2];
+ unsigned int ret_len = strlen(ret_msg) + 1;
+
+ if (strncmp(c->send.bytes, "log toolbox: Version: build-",
+ strlen("log toolbox: Version: build-")) == 0) {
+ build = c->send.bytes +
+ strlen("log toolbox: Version: build-");
+ } else if (strncmp(c->send.bytes, "SetGuestInfo 4 build-",
+ strlen("SetGuestInfo 4 build-")) == 0) {
+ build = c->send.bytes + strlen("SetGuestInfo 4 build-");
+ } else if (strncmp(c->send.bytes, "info-get guestinfo.",
+ strlen("info-get guestinfo.")) == 0) {
+ unsigned int a_key_len = c->ctl.send_len -
+ strlen("info-get guestinfo.");
+ int rc;
+
+ info_key = c->send.bytes + strlen("info-get guestinfo.");
+ if (a_key_len <= MAX_KEY_LEN) {
+
+ rc = get_guestinfo(s, info_key, a_key_len,
+ ret_buffer, sizeof(ret_buffer));
+ if (rc == GUESTINFO_NOTFOUND) {
+ ret_msg = (char *)"0 No value found";
+ ret_len = strlen(ret_msg) + 1;
+ } else {
+ ret_msg = ret_buffer;
+ ret_len = rc;
+ }
+ } else {
+ ret_msg = (char *)"0 Key is too long";
+ ret_len = strlen(ret_msg) + 1;
+ }
+ } else if (strncmp(c->send.bytes, "info-set guestinfo.",
+ strlen("info-set guestinfo.")) == 0) {
+ char *val;
+ unsigned int rest_len = c->ctl.send_len
+ - strlen("info-set guestinfo.");
+
+ info_key = c->send.bytes + strlen("info-set guestinfo.");
+ val = strstr(info_key, " ");
+ if (val) {
+ unsigned int a_key_len = val - info_key;
+ unsigned int a_val_len = rest_len - a_key_len - 1;
+ int rc;
+
+ val++;
+ rc = set_guestinfo(s, a_key_len, a_val_len,
+ info_key, val);
+ switch (rc) {
+ case 0:
+ ret_msg = (char *)"1 ";
+ break;
+ case GUESTINFO_VALTOOLONG:
+ ret_msg = (char *)"0 Value too long";
+ break;
+ case GUESTINFO_KEYTOOLONG:
+ ret_msg = (char *)"0 Key is too long";
+ break;
+ case GUESTINFO_TOOMANYKEYS:
+ ret_msg = (char *)"0 Too many keys";
+ break;
+ case GUESTINFO_NO_MEMORY:
+ ret_msg = (char *)"0 Out of memory";
+ break;
+ }
+ ret_len = strlen(ret_msg) + 1;
+ } else {
+ ret_msg =
+ (char *)"0 Two and exactly two arguments expected";
+ ret_len = strlen(ret_msg) + 1;
+ }
+ }
+
+ send(s, c, ret_msg, ret_len);
+
+ if (build) {
+ s->build_number_value = strtol(build, NULL, 10);
+ s->build_number_time = now_time;
+ }
+ } else {
+ unsigned int my_bkt = c->ctl.recv_read - 1;
+ bucket_t *b;
+
+ if (my_bkt >= MAX_BKTS) {
+ my_bkt = MAX_BKTS - 1;
+ }
+ b = &c->recv_bkt[my_bkt];
+ b->ctl.recv_len = 0;
+ }
+ }
+}
+
+static void process_recv_size(VMPortRpcState *s, channel_t *c,
+ vregs *ur)
+{
+ bucket_t *b;
+ int16_t recv_len;
+
+ b = &c->recv_bkt[c->ctl.recv_read];
+ recv_len = b->ctl.recv_len;
+ if (recv_len) {
+ ur->ecx = ((MESSAGE_STATUS_DORECV | MESSAGE_STATUS_SUCCESS) << 16) |
+ (ur->ecx & 0xffff);
+ ur->edx = (ur->edx & 0xffff) | (MESSAGE_TYPE_SENDSIZE << 16);
+ ur->ebx = recv_len;
+ } else {
+ ur->ecx = (MESSAGE_STATUS_SUCCESS << 16) | (ur->ecx & 0xffff);
+ }
+ trace_vmport_detail_rpc_process_recv_size(c->ctl.chan_id, recv_len);
+}
+
+static void process_recv_payload(VMPortRpcState *s,
+ channel_t *c,
+ vregs *ur)
+{
+ bucket_t *b;
+
+ b = &c->recv_bkt[c->ctl.recv_read];
+ if (b->ctl.recv_idx < b->recv_buf_max) {
+ ur->ebx = b->recv.words[b->ctl.recv_idx++];
+ } else {
+ ur->ebx = 0;
+ }
+ ur->ecx = (MESSAGE_STATUS_SUCCESS << 16) | (ur->ecx & 0xffff);
+ ur->edx = (ur->edx & 0xffff) | (MESSAGE_TYPE_SENDPAYLOAD << 16);
+}
+
+static void process_recv_status(VMPortRpcState *s,
+ channel_t *c,
+ vregs *ur)
+{
+ ur->ecx = (MESSAGE_STATUS_SUCCESS << 16) | (ur->ecx & 0xffff);
+ c->ctl.recv_read++;
+ if (c->ctl.recv_read >= MAX_BKTS) {
+ c->ctl.recv_read = 0;
+ }
+}
+
+static void process_close(VMPortRpcState *s, channel_t *c,
+ vregs *ur)
+{
+ /* Return channel to free pool */
+ c->ctl.proto_num = 0;
+ ur->ecx = (MESSAGE_STATUS_SUCCESS << 16) | (ur->ecx & 0xffff);
+ trace_vmport_rpc_process_close(c->ctl.chan_id);
+}
+
+static void process_packet(VMPortRpcState *s, channel_t *c,
+ vregs *ur, unsigned int sub_cmd,
+ unsigned long now_time)
+{
+ c->ctl.active_time = now_time;
+
+ switch (sub_cmd) {
+ case MESSAGE_TYPE_SENDSIZE:
+ process_send_size(s, c, ur);
+ break;
+
+ case MESSAGE_TYPE_SENDPAYLOAD:
+ process_send_payload(s, c, ur, now_time);
+ break;
+
+ case MESSAGE_TYPE_RECVSIZE:
+ process_recv_size(s, c, ur);
+ break;
+
+ case MESSAGE_TYPE_RECVPAYLOAD:
+ process_recv_payload(s, c, ur);
+ break;
+
+ case MESSAGE_TYPE_RECVSTATUS:
+ process_recv_status(s, c, ur);
+ break;
+
+ case MESSAGE_TYPE_CLOSE:
+ process_close(s, c, ur);
+ break;
+
+ default:
+ ur->ecx = 0;
+ break;
+ }
+}
+
+static void vmport_rpc(VMPortRpcState *s , vregs *ur)
+{
+ unsigned int sub_cmd = (ur->ecx >> 16) & 0xffff;
+ channel_t *c = NULL;
+ uint16_t msg_id;
+ uint32_t msg_cookie;
+ unsigned long now_time = get_clock() / 1000000000LL;
+ long delta = now_time - s->ping_time;
+
+ trace_vmport_detail_rpc_start(sub_cmd, ur->eax, ur->ebx, ur->ecx, ur->edx,
+ ur->esi, ur->edi);
+
+ if (!s) {
+ return;
+ }
+ if (delta > s->reset_time) {
+ trace_vmport_rpc_ping(delta);
+ ctrl_send(s, (char *)"reset");
+ }
+ sweep(s, now_time);
+ do {
+ /* Check to see if a new open request is happening... */
+ if (MESSAGE_TYPE_OPEN == sub_cmd) {
+ c = new_chan(s, now_time);
+ if (!c) {
+ trace_vmport_rpc_nofree();
+ break;
+ }
+
+ /* Attach the apropriate protocol the the channel */
+ c->ctl.proto_num = ur->ebx & ~GUESTMSG_FLAG_COOKIE;
+ ur->ecx = (MESSAGE_STATUS_SUCCESS << 16) | (ur->ecx & 0xffff);
+ ur->edx = (ur->edx & 0xffff) | (c->ctl.chan_id << 16);
+ ur->edi = c->ctl.cookie & 0xffff;
+ ur->esi = (c->ctl.cookie >> 16) & 0xffff;
+ trace_vmport_rpc_process_open(c->ctl.chan_id, c->ctl.proto_num);
+ if (c->ctl.proto_num == VMWARE_PROTO_TO_GUEST) {
+ send(s, c, "reset", strlen("reset") + 1);
+ }
+ break;
+ }
+
+ msg_id = (ur->edx >> 16) & 0xffff;
+ msg_cookie = (ur->edi & 0xffff) | (ur->esi << 16);
+ if (msg_id >= GUESTMSG_MAX_CHANNEL) {
+ trace_vmport_rpc_bad_chan(msg_id, GUESTMSG_MAX_CHANNEL);
+ break;
+ }
+ c = &s->chans[msg_id];
+ if (!c->ctl.proto_num) {
+ trace_vmport_rpc_chan_not_open(msg_id);
+ break;
+ }
+
+ /* We check the cookie here since it's possible that the
+ * connection timed out on us and another channel was opened
+ * if this happens, return error and the vmware tool will
+ * need to reopen the connection
+ */
+ if (msg_cookie != c->ctl.cookie) {
+ trace_vmport_rpc_bad_cookie(msg_cookie, c->ctl.cookie);
+ break;
+ }
+ process_packet(s, c, ur, sub_cmd, now_time);
+ } while (0);
+
+ if (!c) {
+ ur->ecx &= 0xffff;
+ }
+
+ trace_vmport_detail_rpc_end(sub_cmd, ur->eax, ur->ebx, ur->ecx, ur->edx,
+ ur->esi, ur->edi);
+}
+
static uint32_t vmport_rpc_ioport_read(void *opaque, uint32_t addr)
{
VMPortRpcState *s = opaque;
@@ -71,7 +858,7 @@ static uint32_t vmport_rpc_ioport_read(void *opaque, uint32_t addr)
vmmouse_get_data(ur.data);
- s->build_number_time++;
+ vmport_rpc(s, &ur.regs);
vmmouse_set_data(ur.data);
return ur.data[0];
diff --git a/trace-events b/trace-events
index 226f786..88c3181 100644
--- a/trace-events
+++ b/trace-events
@@ -859,6 +859,26 @@ pc87312_info_ide(uint32_t base) "base 0x%x"
pc87312_info_parallel(uint32_t base, uint32_t irq) "base 0x%x, irq %u"
pc87312_info_serial(int n, uint32_t base, uint32_t irq) "id=%d, base 0x%x, irq %u"
+# hw/misc/vmport_rpc.c
+vmport_detail_rpc_start(int sub_cmd, unsigned int ax, unsigned int bx, unsigned int cx, unsigned int dx, unsigned int si, unsigned int di) "sub_cmd=0x%x ax=%x bx=%x cx=%x dx=%x si=%x di=%x"
+vmport_detail_rpc_end(int sub_cmd, unsigned int ax, unsigned int bx, unsigned int cx, unsigned int dx, unsigned int si, unsigned int di) "sub_cmd=0x%x ax=%x bx=%x cx=%x dx=%x si=%x di=%x"
+vmport_rpc_send_skip(char *prefix, unsigned int end, unsigned int len, unsigned int k, size_t out_size, char *out) "%s%d[%d,%d,%zu]%s"
+vmport_rpc_send_normal(char *prefix, unsigned int end, unsigned int len, unsigned int k, size_t out_size, char *out) "%s%d[%d,%d,%zu]%s"
+vmport_rpc_recv_normal(char *prefix, unsigned int end, unsigned int len, unsigned int k, size_t out_size, char *out) "%s%d[%d,%d,%zu]%s"
+vmport_rpc_set_guestinfo(int klen, int kmaxlen, char *key, int vlen, int vmaxlen, char *val) "klen=%d key=%.*s vlen=%d val=%.*s"
+vmport_rpc_set_guestinfo_bad(int i, int used_guestinfo) "i=%d not allocated, but used_guestinfo=%d"
+vmport_rpc_send_big(int len, size_t maxlen) "recv_len=%d >= %zd"
+vmport_rpc_sweep(int chan_id, long delta) "flush %d. delta=%ld"
+vmport_rpc_ping(long delta) "delta=%ld"
+vmport_rpc_nofree(void) "failed to find a free channel"
+vmport_rpc_bad_chan(int chan_id, int max_chan_id) "chan_id=%d >= %d"
+vmport_rpc_chan_not_open(int chan_id) "chan_id=%d"
+vmport_rpc_bad_cookie(int msg_cookie, int ctl_cookie) "%x vs %x"
+vmport_detail_rpc_process_send_size(int chan_id, int send_len) "chan %d is %d"
+vmport_detail_rpc_process_recv_size(int chan_id, int send_len) "chan %d is %d"
+vmport_rpc_process_close(int chan_id) "chan %d"
+vmport_rpc_process_open(int chan_id, int proto_num) "chan %d proto 0x%x"
+
# hw/scsi/vmw_pvscsi.c
pvscsi_ring_init_data(uint32_t txr_len_log2, uint32_t rxr_len_log2) "TX/RX rings logarithms set to %d/%d"
pvscsi_ring_init_msg(uint32_t len_log2) "MSG ring logarithm set to %d"
--
1.8.4
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [Qemu-devel] [PATCH 4/6] vmport_rpc: Add QMP access to vmport_rpc object.
2015-01-30 21:06 [Qemu-devel] [PATCH 0/6] Add limited support of VMware's hyper-call rpc Don Slutz
` (2 preceding siblings ...)
2015-01-30 21:06 ` [Qemu-devel] [PATCH 3/6] vmport_rpc: Add limited support of VMware's hyper-call rpc Don Slutz
@ 2015-01-30 21:06 ` Don Slutz
2015-01-30 22:32 ` Eric Blake
2015-01-30 21:06 ` [Qemu-devel] [PATCH 5/6] vmport: Add VMware all ring hack Don Slutz
2015-01-30 21:06 ` [Qemu-devel] [PATCH 6/6] MAINTAINERS: add VMware port Don Slutz
5 siblings, 1 reply; 9+ messages in thread
From: Don Slutz @ 2015-01-30 21:06 UTC (permalink / raw)
To: qemu-devel
Cc: Michael S. Tsirkin, Markus Armbruster, Don Slutz, Luiz Capitulino,
Anthony Liguori, Paolo Bonzini, Andreas Färber,
Richard Henderson
This adds two new inject commands:
inject-vmport-reboot
inject-vmport-halt
And three guest info commands:
vmport-guestinfo-set
vmport-guestinfo-get
query-vmport-guestinfo
More details in qmp-commands.hx
Signed-off-by: Don Slutz <dslutz@verizon.com>
---
hw/misc/vmport_rpc.c | 275 +++++++++++++++++++++++++++++++++++++++++++++++++++
qapi-schema.json | 95 ++++++++++++++++++
qmp-commands.hx | 141 ++++++++++++++++++++++++++
3 files changed, 511 insertions(+)
diff --git a/hw/misc/vmport_rpc.c b/hw/misc/vmport_rpc.c
index e12f912..74cc95e 100644
--- a/hw/misc/vmport_rpc.c
+++ b/hw/misc/vmport_rpc.c
@@ -218,6 +218,56 @@ typedef struct {
uint32_t edi;
} vregs;
+/*
+ * Run func() for every VMPortRpc device, traverse the tree for
+ * everything else. Note: This routine expects that opaque is a
+ * VMPortRpcFind pointer and not NULL.
+ */
+static int find_VMPortRpc_device(Object *obj, void *opaque)
+{
+ VMPortRpcFind *find = opaque;
+ Object *dev;
+ VMPortRpcState *s;
+
+ if (find->found) {
+ return 0;
+ }
+ dev = object_dynamic_cast(obj, TYPE_VMPORT_RPC);
+ s = (VMPortRpcState *)dev;
+
+ if (!s) {
+ /* Container, traverse it for children */
+ return object_child_foreach(obj, find_VMPortRpc_device, opaque);
+ }
+
+ find->found++;
+ find->rc = find->func(s, find->arg);
+
+ return 0;
+}
+
+/*
+ * Loop through all dynamically created VMPortRpc devices and call
+ * func() for each instance.
+ */
+static int foreach_dynamic_VMPortRpc_device(FindVMPortRpcDeviceFunc *func,
+ void *arg)
+{
+ VMPortRpcFind find = {
+ .func = func,
+ .arg = arg,
+ };
+
+ /* Loop through all VMPortRpc devices that were spawened outside
+ * the machine */
+ find_VMPortRpc_device(qdev_get_machine(), &find);
+ if (find.found) {
+ return find.rc;
+ } else {
+ return VMPORT_DEVICE_NOT_FOUND;
+ }
+}
+
#ifdef VMPORT_RPC_DEBUG
/*
* Add helper function for traceing. This routine will convert
@@ -465,6 +515,26 @@ static int get_guestinfo(VMPortRpcState *s,
return GUESTINFO_NOTFOUND;
}
+static int get_qmp_guestinfo(VMPortRpcState *s,
+ unsigned int a_key_len, char *a_info_key,
+ unsigned int *a_value_len, void **a_value_data)
+{
+ unsigned int i;
+
+ for (i = 0; i < s->used_guestinfo; i++) {
+ if (s->guestinfo[i] &&
+ (s->guestinfo[i]->key_len == a_key_len) &&
+ (memcmp(a_info_key, s->guestinfo[i]->key_data,
+ s->guestinfo[i]->key_len) == 0)) {
+ *a_value_len = s->guestinfo[i]->val_len;
+ *a_value_data = s->guestinfo[i]->val_data;
+ return 0;
+ }
+ }
+
+ return GUESTINFO_NOTFOUND;
+}
+
static int set_guestinfo(VMPortRpcState *s, int a_key_len,
unsigned int a_val_len, char *a_info_key, char *val)
{
@@ -864,6 +934,210 @@ static uint32_t vmport_rpc_ioport_read(void *opaque, uint32_t addr)
return ur.data[0];
}
+static int find_send(VMPortRpcState *s, void *arg)
+{
+ return ctrl_send(s, arg);
+}
+
+static void convert_local_rc(Error **errp, int rc)
+{
+ switch (rc) {
+ case 0:
+ break;
+ case VMPORT_DEVICE_NOT_FOUND:
+ error_set(errp, QERR_DEVICE_NOT_FOUND, TYPE_VMPORT_RPC);
+ break;
+ case SEND_NOT_OPEN:
+ error_set(errp, ERROR_CLASS_GENERIC_ERROR, "VMWare rpc not open");
+ break;
+ case SEND_SKIPPED:
+ error_set(errp, ERROR_CLASS_GENERIC_ERROR, "VMWare rpc send skipped");
+ break;
+ case SEND_TRUCATED:
+ error_set(errp, ERROR_CLASS_GENERIC_ERROR, "VMWare rpc send trucated");
+ break;
+ case SEND_NO_MEMORY:
+ error_set(errp, ERROR_CLASS_GENERIC_ERROR,
+ "VMWare rpc send out of memory");
+ break;
+ case GUESTINFO_NOTFOUND:
+ error_set(errp, ERROR_CLASS_GENERIC_ERROR,
+ "VMWare guestinfo not found");
+ break;
+ case GUESTINFO_VALTOOLONG:
+ error_set(errp, ERROR_CLASS_GENERIC_ERROR,
+ "VMWare guestinfo value too long");
+ break;
+ case GUESTINFO_KEYTOOLONG:
+ error_set(errp, ERROR_CLASS_GENERIC_ERROR,
+ "VMWare guestinfo key too long");
+ break;
+ case GUESTINFO_TOOMANYKEYS:
+ error_set(errp, ERROR_CLASS_GENERIC_ERROR,
+ "VMWare guestinfo too many keys");
+ break;
+ case GUESTINFO_NO_MEMORY:
+ error_set(errp, ERROR_CLASS_GENERIC_ERROR,
+ "VMWare guestinfo out of memory");
+ break;
+ default:
+ error_set(errp, ERROR_CLASS_GENERIC_ERROR,
+ "VMWare rpc send rc=%d unknown", rc);
+ break;
+ }
+}
+
+void qmp_inject_vmport_reboot(Error **errp)
+{
+ int rc = foreach_dynamic_VMPortRpc_device(find_send, (void *)"OS_Reboot");
+
+ convert_local_rc(errp, rc);
+}
+
+void qmp_inject_vmport_halt(Error **errp)
+{
+ int rc = foreach_dynamic_VMPortRpc_device(find_send, (void *)"OS_Halt");
+
+ convert_local_rc(errp, rc);
+}
+
+typedef struct keyValue {
+ void *key_data;
+ void *value_data;
+ unsigned int key_len;
+ unsigned int value_len;
+} keyValue;
+
+static int find_set(VMPortRpcState *s, void *arg)
+{
+ keyValue *key_value = arg;
+
+ return set_guestinfo(s, key_value->key_len, key_value->value_len,
+ key_value->key_data, key_value->value_data);
+}
+
+static int find_get(VMPortRpcState *s, void *arg)
+{
+ keyValue *key_value = arg;
+
+ return get_qmp_guestinfo(s, key_value->key_len, key_value->key_data,
+ &key_value->value_len, &key_value->value_data);
+}
+
+void qmp_vmport_guestinfo_set(const char *key, const char *value,
+ bool has_format, enum DataFormat format,
+ Error **errp)
+{
+ int rc;
+ keyValue key_value;
+
+ if (strncmp(key, "guestinfo.", strlen("guestinfo.")) == 0) {
+ key_value.key_data = (void *)(key + strlen("guestinfo."));
+ key_value.key_len = strlen(key) - strlen("guestinfo.");
+ } else {
+ key_value.key_data = (void *)key;
+ key_value.key_len = strlen(key);
+ }
+ if (has_format && (format == DATA_FORMAT_BASE64)) {
+ gsize write_count;
+
+ key_value.value_data = g_base64_decode(value, &write_count);
+ key_value.value_len = write_count;
+ } else {
+ key_value.value_data = (void *)value;
+ key_value.value_len = strlen(value);
+ }
+
+ rc = foreach_dynamic_VMPortRpc_device(find_set, (void *)&key_value);
+
+ if (key_value.value_data != value) {
+ g_free(key_value.value_data);
+ }
+
+ if (rc) {
+ convert_local_rc(errp, rc);
+ return;
+ }
+}
+
+char *qmp_vmport_guestinfo_get(const char *key,
+ bool has_format, enum DataFormat format,
+ Error **errp)
+{
+ int rc;
+ keyValue key_value;
+ char *value;
+
+ if (strncmp(key, "guestinfo.", strlen("guestinfo.")) == 0) {
+ key_value.key_data = (void *)(key + strlen("guestinfo."));
+ key_value.key_len = strlen(key) - strlen("guestinfo.");
+ } else {
+ key_value.key_data = (void *)key;
+ key_value.key_len = strlen(key);
+ }
+
+ rc = foreach_dynamic_VMPortRpc_device(find_get, (void *)&key_value);
+ if (rc) {
+ convert_local_rc(errp, rc);
+ return NULL;
+ }
+
+ if (has_format && (format == DATA_FORMAT_BASE64)) {
+ value = g_base64_encode(key_value.value_data, key_value.value_len);
+ } else {
+ /*
+ * FIXME should check for complete, valid UTF-8 characters.
+ * Invalid sequences should be replaced by a suitable
+ * replacement character.
+ */
+ value = g_malloc(key_value.value_len + 1);
+ memcpy(value, key_value.value_data, key_value.value_len);
+ value[key_value.value_len] = 0;
+ }
+
+ return value;
+}
+
+static int find_list(VMPortRpcState *s, void *arg)
+{
+ VmportGuestInfoList **keys = arg;
+ unsigned int i;
+
+ for (i = 0; i < s->used_guestinfo; i++) {
+ if (s->guestinfo[i] && s->guestinfo[i]->key_len) {
+ VmportGuestInfoList *info = g_malloc0(sizeof(*info));
+
+ info->value = g_malloc0(sizeof(*info->value));
+#ifdef VMPORT_SHORT
+ info->value->key = g_strndup(s->guestinfo[i]->key_data,
+ s->guestinfo[i]->key_len);
+#else
+ info->value->key = g_strdup_printf("guestinfo.%.*s",
+ (int)s->guestinfo[i]->key_len,
+ s->guestinfo[i]->key_data);
+#endif
+
+ info->next = *keys;
+ *keys = info;
+ }
+ }
+
+ return 0;
+}
+
+VmportGuestInfoList *qmp_query_vmport_guestinfo(Error **errp)
+{
+ VmportGuestInfoList *keys = NULL;
+ int rc = foreach_dynamic_VMPortRpc_device(find_list, (void *)&keys);
+
+ if (rc) {
+ convert_local_rc(errp, rc);
+ }
+
+ return keys;
+}
+
+
static void vmport_rpc_reset(DeviceState *d)
{
VMPortRpcState *s = VMPORT_RPC(d);
@@ -871,6 +1145,7 @@ static void vmport_rpc_reset(DeviceState *d)
s->reset_time = 14;
s->build_number_value = 0;
s->build_number_time = 0;
+ memset(&s->chans, 0, sizeof(s->chans));
}
static void vmport_rpc_realizefn(DeviceState *dev, Error **errp)
diff --git a/qapi-schema.json b/qapi-schema.json
index e16f8eb..6c3146f 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -1271,6 +1271,101 @@
{ 'command': 'inject-nmi' }
##
+# @inject-vmport-reboot:
+#
+# Injects a VMWare Tools reboot to the guest.
+#
+# Returns: If successful, nothing
+#
+# Since: 2.3
+#
+##
+{ 'command': 'inject-vmport-reboot' }
+
+##
+# @inject-vmport-halt:
+#
+# Injects a VMWare Tools halt to the guest.
+#
+# Returns: If successful, nothing
+#
+# Since: 2.3
+#
+##
+{ 'command': 'inject-vmport-halt' }
+
+##
+# @vmport-guestinfo-set:
+#
+# Set a VMWare Tools guestinfo key to a value
+#
+# @key: the key to set
+#
+# @value: The data to set the key to
+#
+# @format: #optional value encoding (default 'utf8').
+# - base64: value must be base64 encoded text. Its binary
+# decoding gets set.
+# Bug: invalid base64 is currently not rejected.
+# Whitespace *is* invalid.
+# - utf8: value's UTF-8 encoding is written
+# - value itself is always Unicode regardless of format, like
+# any other string.
+#
+# Returns: Nothing on success
+#
+# Since: 2.3
+##
+{ 'command': 'vmport-guestinfo-set',
+ 'data': {'key': 'str', 'value': 'str',
+ '*format': 'DataFormat'} }
+
+##
+# @vmport-guestinfo-get:
+#
+# Get a VMWare Tools guestinfo value for a key
+#
+# @key: the key to get
+#
+# @format: #optional data encoding (default 'utf8').
+# - base64: the value is returned in base64 encoding.
+# - utf8: the value is interpreted as UTF-8.
+# Bug: can screw up when the buffer contains invalid UTF-8
+# sequences, NUL characters.
+# - The return value is always Unicode regardless of format,
+# like any other string.
+#
+# Returns: value for the guest info key
+#
+# Since: 2.3
+##
+{ 'command': 'vmport-guestinfo-get',
+ 'data': {'key': 'str', '*format': 'DataFormat'},
+ 'returns': 'str' }
+
+##
+# @VmportGuestInfo:
+#
+# Information about a single VMWare Tools guestinfo
+#
+# @key: The known key
+#
+# Since: 2.3
+##
+{ 'type': 'VmportGuestInfo', 'data': {'key': 'str'} }
+
+##
+# @query-vmport-guestinfo:
+#
+# Returns information about VMWare Tools guestinfo
+#
+# Returns: a list of @VmportGuestInfo
+#
+# Since: 2.3
+##
+{ 'command': 'query-vmport-guestinfo', 'returns': ['VmportGuestInfo'] }
+
+##
# @set_link:
#
# Sets the link status of a virtual network adapter.
diff --git a/qmp-commands.hx b/qmp-commands.hx
index c5f16dd..0aa11df 100644
--- a/qmp-commands.hx
+++ b/qmp-commands.hx
@@ -491,6 +491,147 @@ Note: inject-nmi fails when the guest doesn't support injecting.
EQMP
{
+ .name = "inject-vmport-reboot",
+ .args_type = "",
+ .mhandler.cmd_new = qmp_marshal_input_inject_vmport_reboot,
+ },
+
+SQMP
+inject-vmport-reboot
+----------
+
+Inject a VMWare Tools reboot to the guest.
+
+Arguments: None.
+
+Example:
+
+-> { "execute": "inject-vmport-reboot" }
+<- { "return": {} }
+
+Note: inject-vmport-reboot fails when the guest doesn't support injecting.
+
+EQMP
+
+ {
+ .name = "inject-vmport-halt",
+ .args_type = "",
+ .mhandler.cmd_new = qmp_marshal_input_inject_vmport_halt,
+ },
+
+SQMP
+inject-vmport-halt
+----------
+
+Inject a VMWare Tools halt to the guest.
+
+Arguments: None.
+
+Example:
+
+-> { "execute": "inject-vmport-halt" }
+<- { "return": {} }
+
+Note: inject-vmport-halt fails when the guest doesn't support injecting.
+
+EQMP
+
+ {
+ .name = "vmport-guestinfo-set",
+ .args_type = "key:s,value:s,format:s?",
+ .mhandler.cmd_new = qmp_marshal_input_vmport_guestinfo_set,
+ },
+
+SQMP
+vmport-guestinfo-set
+----------
+
+Set a VMWare Tools guestinfo key to a value
+
+Arguments:
+
+- "key": the key to set (json-string)
+- "value": data to write (json-string)
+- "format": data format (json-string, optional)
+ - Possible values: "utf8" (default), "base64"
+ Bug: invalid base64 is currently not rejected.
+ Whitespace *is* invalid.
+
+Example:
+
+-> { "execute": "vmport-guestinfo-set",
+ "arguments": { "key": "foo",
+ "value": "abcdefgh",
+ "format": "utf8" } }
+<- { "return": {} }
+
+EQMP
+
+ {
+ .name = "vmport-guestinfo-get",
+ .args_type = "key:s,format:s?",
+ .mhandler.cmd_new = qmp_marshal_input_vmport_guestinfo_get,
+ },
+
+SQMP
+vmport-guestinfo-get
+----------
+
+Get a VMWare Tools guestinfo value for a key
+
+Arguments:
+
+- "key": the key to get (json-string)
+- "format": data format (json-string, optional)
+ - Possible values: "utf8" (default), "base64"
+ - Naturally, format "utf8" works only when the ring buffer
+ contains valid UTF-8 text. Invalid UTF-8 sequences get
+ replaced. Bug: replacement doesn't work. Bug: can screw
+ up on encountering NUL characters.
+
+Example:
+
+-> { "execute": "vmport-guestinfo-get",
+ "arguments": { "key": "foo",
+ "format": "utf8" } }
+<- {"return": "abcdefgh"}
+
+
+EQMP
+
+ {
+ .name = "query-vmport-guestinfo",
+ .args_type = "",
+ .mhandler.cmd_new = qmp_marshal_input_query_vmport_guestinfo,
+ },
+
+SQMP
+query-vmport-guestinfo
+----------
+
+Returns information about VMWare Tools guestinfo. The returned value is a json-array
+of all keys.
+
+Example:
+
+-> { "execute": "query-vmport-guestinfo" }
+<- {
+ "return": [
+ {
+ "key": "ip",
+ },
+ {
+ "key": "foo",
+ },
+ {
+ "key": "long",
+ }
+ ]
+ }
+
+EQMP
+
+ {
.name = "ringbuf-write",
.args_type = "device:s,data:s,format:s?",
.mhandler.cmd_new = qmp_marshal_input_ringbuf_write,
--
1.8.4
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [Qemu-devel] [PATCH 5/6] vmport: Add VMware all ring hack
2015-01-30 21:06 [Qemu-devel] [PATCH 0/6] Add limited support of VMware's hyper-call rpc Don Slutz
` (3 preceding siblings ...)
2015-01-30 21:06 ` [Qemu-devel] [PATCH 4/6] vmport_rpc: Add QMP access to vmport_rpc object Don Slutz
@ 2015-01-30 21:06 ` Don Slutz
2015-01-30 21:06 ` [Qemu-devel] [PATCH 6/6] MAINTAINERS: add VMware port Don Slutz
5 siblings, 0 replies; 9+ messages in thread
From: Don Slutz @ 2015-01-30 21:06 UTC (permalink / raw)
To: qemu-devel
Cc: Michael S. Tsirkin, Markus Armbruster, Don Slutz, Luiz Capitulino,
Anthony Liguori, Paolo Bonzini, Andreas Färber,
Richard Henderson
This is done by adding a new machine property vmware-port-ring3 that
needs to be enabled to have any effect. It only effects accel=tcg
mode. It is needed if you want to use VMware tools in accel=tcg
mode.
Signed-off-by: Don Slutz <dslutz@verizon.com>
(cherry picked from commit 6d99c91fc9ae27b476e89a8cc880b4a46e237536)
---
hw/i386/pc.c | 28 +++++++++++++++++++++++++++-
hw/i386/pc_piix.c | 2 +-
hw/i386/pc_q35.c | 2 +-
include/hw/i386/pc.h | 6 +++++-
target-i386/cpu.c | 4 ++++
target-i386/cpu.h | 2 ++
target-i386/seg_helper.c | 6 ++++++
7 files changed, 46 insertions(+), 4 deletions(-)
diff --git a/hw/i386/pc.c b/hw/i386/pc.c
index efae4d5..3999bbf 100644
--- a/hw/i386/pc.c
+++ b/hw/i386/pc.c
@@ -1012,7 +1012,9 @@ void pc_hot_add_cpu(const int64_t id, Error **errp)
pc_new_cpu(current_cpu_model, apic_id, icc_bridge, errp);
}
-void pc_cpus_init(const char *cpu_model, DeviceState *icc_bridge)
+/* vmware_port_ring3 true says enable VMware port access in ring3. */
+void pc_cpus_init(const char *cpu_model, DeviceState *icc_bridge,
+ bool vmware_port_ring3)
{
int i;
X86CPU *cpu = NULL;
@@ -1044,6 +1046,9 @@ void pc_cpus_init(const char *cpu_model, DeviceState *icc_bridge)
error_free(error);
exit(1);
}
+ if (vmware_port_ring3) {
+ cpu->env.hflags2 |= HF2_VMPORT_HACK_MASK;
+ }
}
/* map APIC MMIO area if CPU has APIC */
@@ -1774,6 +1779,21 @@ static bool pc_machine_get_aligned_dimm(Object *obj, Error **errp)
return pcms->enforce_aligned_dimm;
}
+static bool pc_machine_get_vmware_port_ring3(Object *obj, Error **errp)
+{
+ PCMachineState *pcms = PC_MACHINE(obj);
+
+ return pcms->vmware_port_ring3;
+}
+
+static void pc_machine_set_vmware_port_ring3(Object *obj, bool value,
+ Error **errp)
+{
+ PCMachineState *pcms = PC_MACHINE(obj);
+
+ pcms->vmware_port_ring3 = value;
+}
+
static void pc_machine_initfn(Object *obj)
{
PCMachineState *pcms = PC_MACHINE(obj);
@@ -1804,6 +1824,12 @@ static void pc_machine_initfn(Object *obj)
object_property_add_bool(obj, PC_MACHINE_ENFORCE_ALIGNED_DIMM,
pc_machine_get_aligned_dimm,
NULL, NULL);
+
+ pcms->vmware_port_ring3 = false;
+ object_property_add_bool(obj, PC_MACHINE_VMWARE_PORT_RING3,
+ pc_machine_get_vmware_port_ring3,
+ pc_machine_set_vmware_port_ring3,
+ NULL);
}
static void pc_machine_class_init(ObjectClass *oc, void *data)
diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c
index 38b42b0..8434c04 100644
--- a/hw/i386/pc_piix.c
+++ b/hw/i386/pc_piix.c
@@ -146,7 +146,7 @@ static void pc_init1(MachineState *machine,
object_property_add_child(qdev_get_machine(), "icc-bridge",
OBJECT(icc_bridge), NULL);
- pc_cpus_init(machine->cpu_model, icc_bridge);
+ pc_cpus_init(machine->cpu_model, icc_bridge, pc_machine->vmware_port_ring3);
if (kvm_enabled() && kvmclock_enabled) {
kvmclock_create();
diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c
index 63027ee..d952fa1 100644
--- a/hw/i386/pc_q35.c
+++ b/hw/i386/pc_q35.c
@@ -135,7 +135,7 @@ static void pc_q35_init(MachineState *machine)
object_property_add_child(qdev_get_machine(), "icc-bridge",
OBJECT(icc_bridge), NULL);
- pc_cpus_init(machine->cpu_model, icc_bridge);
+ pc_cpus_init(machine->cpu_model, icc_bridge, pc_machine->vmware_port_ring3);
pc_acpi_init("q35-acpi-dsdt.aml");
kvmclock_create();
diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h
index 69d9cf8..d31f157 100644
--- a/include/hw/i386/pc.h
+++ b/include/hw/i386/pc.h
@@ -40,6 +40,7 @@ struct PCMachineState {
uint64_t max_ram_below_4g;
OnOffAuto vmport;
+ bool vmware_port_ring3;
bool enforce_aligned_dimm;
};
@@ -48,6 +49,7 @@ struct PCMachineState {
#define PC_MACHINE_MAX_RAM_BELOW_4G "max-ram-below-4g"
#define PC_MACHINE_VMPORT "vmport"
#define PC_MACHINE_ENFORCE_ALIGNED_DIMM "enforce-aligned-dimm"
+#define PC_MACHINE_VMWARE_PORT_RING3 "vmware-port-ring3"
/**
* PCMachineClass:
@@ -177,7 +179,9 @@ extern int fd_bootchk;
void pc_register_ferr_irq(qemu_irq irq);
void pc_acpi_smi_interrupt(void *opaque, int irq, int level);
-void pc_cpus_init(const char *cpu_model, DeviceState *icc_bridge);
+/* vmware_port_ring3 true says enable VMware port access in ring3. */
+void pc_cpus_init(const char *cpu_model, DeviceState *icc_bridge,
+ bool vmware_port_ring3);
void pc_hot_add_cpu(const int64_t id, Error **errp);
void pc_acpi_init(const char *default_dsdt);
diff --git a/target-i386/cpu.c b/target-i386/cpu.c
index 3a9b32e..a787599 100644
--- a/target-i386/cpu.c
+++ b/target-i386/cpu.c
@@ -2594,6 +2594,7 @@ static void x86_cpu_reset(CPUState *s)
X86CPUClass *xcc = X86_CPU_GET_CLASS(cpu);
CPUX86State *env = &cpu->env;
int i;
+ bool save_vmware_port_ring3 = env->hflags2 & HF2_VMPORT_HACK_MASK;
xcc->parent_reset(s);
@@ -2609,6 +2610,9 @@ static void x86_cpu_reset(CPUState *s)
env->hflags |= HF_SOFTMMU_MASK;
#endif
env->hflags2 |= HF2_GIF_MASK;
+ if (save_vmware_port_ring3) {
+ env->hflags2 |= HF2_VMPORT_HACK_MASK;
+ }
cpu_x86_update_cr0(env, 0x60000010);
env->a20_mask = ~0x0;
diff --git a/target-i386/cpu.h b/target-i386/cpu.h
index 478450c..b5e2b68 100644
--- a/target-i386/cpu.h
+++ b/target-i386/cpu.h
@@ -186,11 +186,13 @@
#define HF2_HIF_SHIFT 1 /* value of IF_MASK when entering SVM */
#define HF2_NMI_SHIFT 2 /* CPU serving NMI */
#define HF2_VINTR_SHIFT 3 /* value of V_INTR_MASKING bit */
+#define HF2_VMPORT_HACK_SHIFT 4 /* skip iopl checking for VMware port */
#define HF2_GIF_MASK (1 << HF2_GIF_SHIFT)
#define HF2_HIF_MASK (1 << HF2_HIF_SHIFT)
#define HF2_NMI_MASK (1 << HF2_NMI_SHIFT)
#define HF2_VINTR_MASK (1 << HF2_VINTR_SHIFT)
+#define HF2_VMPORT_HACK_MASK (1 << HF2_VMPORT_HACK_SHIFT)
#define CR0_PE_SHIFT 0
#define CR0_MP_SHIFT 1
diff --git a/target-i386/seg_helper.c b/target-i386/seg_helper.c
index fa374d0..a1e6a2c 100644
--- a/target-i386/seg_helper.c
+++ b/target-i386/seg_helper.c
@@ -2566,6 +2566,12 @@ static inline void check_io(CPUX86State *env, int addr, int size)
{
int io_offset, val, mask;
+ /* vmport hack: skip iopl checking for VMware port 0x5658 (see
+ * vmport_realizefn()) */
+ if ((env->hflags2 & HF2_VMPORT_HACK_MASK) && (addr == 0x5658)) {
+ return;
+ }
+
/* TSS must be a valid 32 bit one */
if (!(env->tr.flags & DESC_P_MASK) ||
((env->tr.flags >> DESC_TYPE_SHIFT) & 0xf) != 9 ||
--
1.8.4
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [Qemu-devel] [PATCH 6/6] MAINTAINERS: add VMware port
2015-01-30 21:06 [Qemu-devel] [PATCH 0/6] Add limited support of VMware's hyper-call rpc Don Slutz
` (4 preceding siblings ...)
2015-01-30 21:06 ` [Qemu-devel] [PATCH 5/6] vmport: Add VMware all ring hack Don Slutz
@ 2015-01-30 21:06 ` Don Slutz
5 siblings, 0 replies; 9+ messages in thread
From: Don Slutz @ 2015-01-30 21:06 UTC (permalink / raw)
To: qemu-devel
Cc: Michael S. Tsirkin, Markus Armbruster, Don Slutz, Luiz Capitulino,
Anthony Liguori, Paolo Bonzini, Andreas Färber,
Richard Henderson
Signed-off-by: Don Slutz <dslutz@verizon.com>
---
MAINTAINERS | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index fd335a4..b60ee6a 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -741,6 +741,13 @@ S: Maintained
F: hw/net/vmxnet*
F: hw/scsi/vmw_pvscsi*
+VMware port
+M: Don Slutz <dslutz@verizon.com>
+S: Maintained
+F: hw/misc/vmport.c
+F: hw/input/vmmouse.c
+F: hw/misc/vmport_rpc.c
+
Subsystems
----------
Audio
--
1.8.4
^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [Qemu-devel] [PATCH 4/6] vmport_rpc: Add QMP access to vmport_rpc object.
2015-01-30 21:06 ` [Qemu-devel] [PATCH 4/6] vmport_rpc: Add QMP access to vmport_rpc object Don Slutz
@ 2015-01-30 22:32 ` Eric Blake
2015-02-03 1:22 ` Don Slutz
0 siblings, 1 reply; 9+ messages in thread
From: Eric Blake @ 2015-01-30 22:32 UTC (permalink / raw)
To: Don Slutz, qemu-devel
Cc: Michael S. Tsirkin, Markus Armbruster, Luiz Capitulino,
Anthony Liguori, Paolo Bonzini, Andreas Färber,
Richard Henderson
[-- Attachment #1: Type: text/plain, Size: 4881 bytes --]
On 01/30/2015 02:06 PM, Don Slutz wrote:
> This adds two new inject commands:
>
> inject-vmport-reboot
> inject-vmport-halt
>
> And three guest info commands:
>
> vmport-guestinfo-set
> vmport-guestinfo-get
> query-vmport-guestinfo
>
> More details in qmp-commands.hx
>
> Signed-off-by: Don Slutz <dslutz@verizon.com>
> ---
> +static int foreach_dynamic_VMPortRpc_device(FindVMPortRpcDeviceFunc *func,
> + void *arg)
> +{
> + VMPortRpcFind find = {
> + .func = func,
> + .arg = arg,
> + };
> +
> + /* Loop through all VMPortRpc devices that were spawened outside
s/spawened/spawned/
> #ifdef VMPORT_RPC_DEBUG
> /*
> * Add helper function for traceing. This routine will convert
Pre-existing as of this patch, but while you are here:
s/traceing/tracing/
unless it occurred earlier in the series, in which case fix it there.
> +static void convert_local_rc(Error **errp, int rc)
> +{
> + switch (rc) {
> + case 0:
> + break;
> + case VMPORT_DEVICE_NOT_FOUND:
> + error_set(errp, QERR_DEVICE_NOT_FOUND, TYPE_VMPORT_RPC);
> + break;
> + case SEND_NOT_OPEN:
> + error_set(errp, ERROR_CLASS_GENERIC_ERROR, "VMWare rpc not open");
shorter as:
error_setg(errp, "VMWare rpc not open");
and similar for all your other uses of ERROR_CLASS_GENERIC_ERROR
> +++ b/qapi-schema.json
> @@ -1271,6 +1271,101 @@
> { 'command': 'inject-nmi' }
>
> ##
> +# @inject-vmport-reboot:
> +#
> +# Injects a VMWare Tools reboot to the guest.
> +#
> +# Returns: If successful, nothing
> +#
> +# Since: 2.3
> +#
> +##
> +{ 'command': 'inject-vmport-reboot' }
> +
> +##
> +# @inject-vmport-halt:
> +#
> +# Injects a VMWare Tools halt to the guest.
> +#
> +# Returns: If successful, nothing
> +#
> +# Since: 2.3
> +#
> +##
> +{ 'command': 'inject-vmport-halt' }
Why two commands? Why not just 'inject-vmport-action' with a parameter
that says whether the action is 'reboot', 'halt', or something else?
> +
> +##
> +# @vmport-guestinfo-set:
> +#
> +# Set a VMWare Tools guestinfo key to a value
> +#
> +# @key: the key to set
> +#
> +# @value: The data to set the key to
> +#
> +# @format: #optional value encoding (default 'utf8').
> +# - base64: value must be base64 encoded text. Its binary
> +# decoding gets set.
> +# Bug: invalid base64 is currently not rejected.
We should fix the bug, rather than document it.
> +# Whitespace *is* invalid.
> +# - utf8: value's UTF-8 encoding is written
> +# - value itself is always Unicode regardless of format, like
> +# any other string.
This was confusing to read - there are three bullets, so it looks like
you meant to document three valid DataFormat enum values; but there are
only two. The comment about 'value' being supplied as valid JSON UTF8
and only later decoded according to 'format' might belong better on
'value', if at all. On the other hand, I see you are just blindly
copy-and-pasting from 'ringbuf-write'.
> +#
> +# Returns: Nothing on success
> +#
> +# Since: 2.3
> +##
> +{ 'command': 'vmport-guestinfo-set',
> + 'data': {'key': 'str', 'value': 'str',
> + '*format': 'DataFormat'} }
> +
> +##
> +# @vmport-guestinfo-get:
> +#
> +# Get a VMWare Tools guestinfo value for a key
> +#
> +# @key: the key to get
> +#
> +# @format: #optional data encoding (default 'utf8').
> +# - base64: the value is returned in base64 encoding.
> +# - utf8: the value is interpreted as UTF-8.
> +# Bug: can screw up when the buffer contains invalid UTF-8
> +# sequences, NUL characters.
> +# - The return value is always Unicode regardless of format,
> +# like any other string.
Similar comments, but again sourced by copy-and-pasting from an existing
interface.
> +#
> +# Returns: value for the guest info key
> +#
> +# Since: 2.3
> +##
> +{ 'command': 'vmport-guestinfo-get',
> + 'data': {'key': 'str', '*format': 'DataFormat'},
> + 'returns': 'str' }
> +
> +##
> +# @VmportGuestInfo:
> +#
> +# Information about a single VMWare Tools guestinfo
> +#
> +# @key: The known key
> +#
> +# Since: 2.3
> +##
> +{ 'type': 'VmportGuestInfo', 'data': {'key': 'str'} }
> +
> +##
> +# @query-vmport-guestinfo:
> +#
> +# Returns information about VMWare Tools guestinfo
> +#
> +# Returns: a list of @VmportGuestInfo
> +#
> +# Since: 2.3
> +##
> +{ 'command': 'query-vmport-guestinfo', 'returns': ['VmportGuestInfo'] }
> +
> +##
Interface seems more or less okay, since it copies from existing idioms.
--
Eric Blake eblake redhat com +1-919-301-3266
Libvirt virtualization library http://libvirt.org
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 604 bytes --]
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [Qemu-devel] [PATCH 4/6] vmport_rpc: Add QMP access to vmport_rpc object.
2015-01-30 22:32 ` Eric Blake
@ 2015-02-03 1:22 ` Don Slutz
0 siblings, 0 replies; 9+ messages in thread
From: Don Slutz @ 2015-02-03 1:22 UTC (permalink / raw)
To: Eric Blake, Don Slutz, qemu-devel
Cc: Michael S. Tsirkin, Markus Armbruster, Luiz Capitulino,
Anthony Liguori, Paolo Bonzini, Andreas Färber,
Richard Henderson
On 01/30/15 17:32, Eric Blake wrote:
> On 01/30/2015 02:06 PM, Don Slutz wrote:
>> This adds two new inject commands:
>>
>> inject-vmport-reboot
>> inject-vmport-halt
>>
>> And three guest info commands:
>>
>> vmport-guestinfo-set
>> vmport-guestinfo-get
>> query-vmport-guestinfo
>>
>> More details in qmp-commands.hx
>>
>> Signed-off-by: Don Slutz <dslutz@verizon.com>
>> ---
>
>> +static int foreach_dynamic_VMPortRpc_device(FindVMPortRpcDeviceFunc *func,
>> + void *arg)
>> +{
>> + VMPortRpcFind find = {
>> + .func = func,
>> + .arg = arg,
>> + };
>> +
>> + /* Loop through all VMPortRpc devices that were spawened outside
>
> s/spawened/spawned/
>
Fixed.
>
>> #ifdef VMPORT_RPC_DEBUG
>> /*
>> * Add helper function for traceing. This routine will convert
>
> Pre-existing as of this patch, but while you are here:
> s/traceing/tracing/
> unless it occurred earlier in the series, in which case fix it there.
>
Fixed.
>
>> +static void convert_local_rc(Error **errp, int rc)
>> +{
>> + switch (rc) {
>> + case 0:
>> + break;
>> + case VMPORT_DEVICE_NOT_FOUND:
>> + error_set(errp, QERR_DEVICE_NOT_FOUND, TYPE_VMPORT_RPC);
>> + break;
>> + case SEND_NOT_OPEN:
>> + error_set(errp, ERROR_CLASS_GENERIC_ERROR, "VMWare rpc not open");
>
> shorter as:
> error_setg(errp, "VMWare rpc not open");
>
> and similar for all your other uses of ERROR_CLASS_GENERIC_ERROR
>
Fixed.
>> +++ b/qapi-schema.json
>> @@ -1271,6 +1271,101 @@
>> { 'command': 'inject-nmi' }
>>
>> ##
>> +# @inject-vmport-reboot:
>> +#
>> +# Injects a VMWare Tools reboot to the guest.
>> +#
>> +# Returns: If successful, nothing
>> +#
>> +# Since: 2.3
>> +#
>> +##
>> +{ 'command': 'inject-vmport-reboot' }
>> +
>> +##
>> +# @inject-vmport-halt:
>> +#
>> +# Injects a VMWare Tools halt to the guest.
>> +#
>> +# Returns: If successful, nothing
>> +#
>> +# Since: 2.3
>> +#
>> +##
>> +{ 'command': 'inject-vmport-halt' }
>
> Why two commands? Why not just 'inject-vmport-action' with a parameter
> that says whether the action is 'reboot', 'halt', or something else?
>
This is a corner case. I will convert it to inject-vmport-action with
a parameter.
>> +
>> +##
>> +# @vmport-guestinfo-set:
>> +#
>> +# Set a VMWare Tools guestinfo key to a value
>> +#
>> +# @key: the key to set
>> +#
>> +# @value: The data to set the key to
>> +#
>> +# @format: #optional value encoding (default 'utf8').
>> +# - base64: value must be base64 encoded text. Its binary
>> +# decoding gets set.
>> +# Bug: invalid base64 is currently not rejected.
>
> We should fix the bug, rather than document it.
>
Well, this turns out to be complex. If there is a bug it is in GLIB.
However the Glib reference manual refers to RFC 1421 and RFC 2045 and
MIME encoding. Based on all that (which seems to match:
http://en.wikipedia.org/wiki/Base64
) MIME states that all characters outside the (base64) alphabet are
to be ignored. Testing shows that g_base64_decode() does this.
The confusion is that most non-MIME uses reject a base64 string
that contain characters outside the alphabet. I was just following
the other uses of base64 in this file.
DataFormat refers to RFC 3548, which has the info:
" Implementations MUST reject the encoding if it contains characters
outside the base alphabet when interpreting base encoded data, unless
the specification referring to this document explicitly states
otherwise. Such specifications may, as MIME does, instead state that
characters outside the base encoding alphabet should simply be
ignored when interpreting data ("be liberal in what you accept")."
So with GLIB going the MIME way, I do not think this is a QEMU bug
(you could consider this a GLIB bug, but the document I found says
that GLIB goes the MIME way and so does not reject anything).
Having now looked closer at this, I plan to drop this "bug" section
since I am happy with what GLIB is doing.
>> +# Whitespace *is* invalid.
>> +# - utf8: value's UTF-8 encoding is written
>> +# - value itself is always Unicode regardless of format, like
>> +# any other string.
>
> This was confusing to read - there are three bullets, so it looks like
> you meant to document three valid DataFormat enum values; but there are
> only two. The comment about 'value' being supplied as valid JSON UTF8
> and only later decoded according to 'format' might belong better on
> 'value', if at all. On the other hand, I see you are just blindly
> copy-and-pasting from 'ringbuf-write'.
>
Yup. Will just drop the "- value..."
>
>> +#
>> +# Returns: Nothing on success
>> +#
>> +# Since: 2.3
>> +##
>> +{ 'command': 'vmport-guestinfo-set',
>> + 'data': {'key': 'str', 'value': 'str',
>> + '*format': 'DataFormat'} }
>> +
>> +##
>> +# @vmport-guestinfo-get:
>> +#
>> +# Get a VMWare Tools guestinfo value for a key
>> +#
>> +# @key: the key to get
>> +#
>> +# @format: #optional data encoding (default 'utf8').
>> +# - base64: the value is returned in base64 encoding.
>> +# - utf8: the value is interpreted as UTF-8.
>> +# Bug: can screw up when the buffer contains invalid UTF-8
>> +# sequences, NUL characters.
>> +# - The return value is always Unicode regardless of format,
>> +# like any other string.
>
> Similar comments, but again sourced by copy-and-pasting from an existing
> interface.
>
Will adjust as above.
>> +#
>> +# Returns: value for the guest info key
>> +#
>> +# Since: 2.3
>> +##
>> +{ 'command': 'vmport-guestinfo-get',
>> + 'data': {'key': 'str', '*format': 'DataFormat'},
>> + 'returns': 'str' }
>> +
>> +##
>> +# @VmportGuestInfo:
>> +#
>> +# Information about a single VMWare Tools guestinfo
>> +#
>> +# @key: The known key
>> +#
>> +# Since: 2.3
>> +##
>> +{ 'type': 'VmportGuestInfo', 'data': {'key': 'str'} }
>> +
>> +##
>> +# @query-vmport-guestinfo:
>> +#
>> +# Returns information about VMWare Tools guestinfo
>> +#
>> +# Returns: a list of @VmportGuestInfo
>> +#
>> +# Since: 2.3
>> +##
>> +{ 'command': 'query-vmport-guestinfo', 'returns': ['VmportGuestInfo'] }
>> +
>> +##
>
> Interface seems more or less okay, since it copies from existing idioms.
>
>
Thanks,
-Don Slutz
^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2015-02-03 1:22 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2015-01-30 21:06 [Qemu-devel] [PATCH 0/6] Add limited support of VMware's hyper-call rpc Don Slutz
2015-01-30 21:06 ` [Qemu-devel] [PATCH 1/6] vmport.c: Fix vmport_cmd_ram_size Don Slutz
2015-01-30 21:06 ` [Qemu-devel] [PATCH 2/6] vmport_rpc: Add the object vmport_rpc Don Slutz
2015-01-30 21:06 ` [Qemu-devel] [PATCH 3/6] vmport_rpc: Add limited support of VMware's hyper-call rpc Don Slutz
2015-01-30 21:06 ` [Qemu-devel] [PATCH 4/6] vmport_rpc: Add QMP access to vmport_rpc object Don Slutz
2015-01-30 22:32 ` Eric Blake
2015-02-03 1:22 ` Don Slutz
2015-01-30 21:06 ` [Qemu-devel] [PATCH 5/6] vmport: Add VMware all ring hack Don Slutz
2015-01-30 21:06 ` [Qemu-devel] [PATCH 6/6] MAINTAINERS: add VMware port Don Slutz
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).