@@ -166,7 +166,7 @@ static int xendevicemodel_op(
}
int xendevicemodel_create_ioreq_server(
- xendevicemodel_handle *dmod, domid_t domid, int handle_bufioreq,
+ xendevicemodel_handle *dmod, domid_t domid, int flags,
ioservid_t *id)
{
struct xen_dm_op op;
@@ -178,7 +178,7 @@ int xendevicemodel_create_ioreq_server(
op.op = XEN_DMOP_create_ioreq_server;
data = &op.u.create_ioreq_server;
- data->handle_bufioreq = handle_bufioreq;
+ data->flags = flags;
rc = xendevicemodel_op(dmod, domid, 1, &op, sizeof(op));
if (rc)
@@ -20,7 +20,7 @@
#include <xenguest.h>
#define INVALID_PFN ((xen_pfn_t)-1)
-#define X86_HVM_NR_SPECIAL_PAGES 8
+#define X86_HVM_NR_SPECIAL_PAGES 9
#define X86_HVM_END_SPECIAL_REGION 0xff000u
#define XG_MAX_MODULES 2
@@ -67,6 +67,7 @@
#define SPECIALPAGE_IOREQ 5
#define SPECIALPAGE_IDENT_PT 6
#define SPECIALPAGE_CONSOLE 7
+#define SPECIALPAGE_VMPORT_REGS 8
#define special_pfn(x) \
(X86_HVM_END_SPECIAL_REGION - X86_HVM_NR_SPECIAL_PAGES + (x))
@@ -657,6 +658,8 @@ static int alloc_magic_pages_hvm(struct xc_dom_image *dom)
special_pfn(SPECIALPAGE_BUFIOREQ));
xc_hvm_param_set(xch, domid, HVM_PARAM_IOREQ_PFN,
special_pfn(SPECIALPAGE_IOREQ));
+ xc_hvm_param_set(xch, domid, HVM_PARAM_VMPORT_REGS_PFN,
+ special_pfn(SPECIALPAGE_VMPORT_REGS));
xc_hvm_param_set(xch, domid, HVM_PARAM_CONSOLE_PFN,
special_pfn(SPECIALPAGE_CONSOLE));
xc_hvm_param_set(xch, domid, HVM_PARAM_PAGING_RING_PFN,
@@ -1822,6 +1822,8 @@ static int libxl__build_device_model_args_new(libxl__gc *gc,
}
}
+ if (libxl_defbool_val(c_info->vmware_port))
+ machinearg = GCSPRINTF("%s,vmport=on", machinearg);
flexarray_append(dm_args, machinearg);
for (i = 0; b_info->extra_hvm && b_info->extra_hvm[i] != NULL; i++)
flexarray_append(dm_args, b_info->extra_hvm[i]);
@@ -419,8 +419,7 @@ static int dm_op(const struct dmop_args *op_args)
if ( data->pad[0] || data->pad[1] || data->pad[2] )
break;
- rc = hvm_create_ioreq_server(d, data->handle_bufioreq,
- &data->id);
+ rc = hvm_create_ioreq_server(d, data->flags, &data->id);
break;
}
@@ -28,6 +28,8 @@
#include <asm/iocap.h>
#include <asm/vm_event.h>
+vmware_regs_t *get_vmport_regs_any(struct hvm_ioreq_server *s, struct vcpu *v);
+
struct hvmemul_cache
{
/* The cache is disabled as long as num_ents > max_ents. */
@@ -173,6 +175,8 @@ static int hvmemul_do_io(
};
void *p_data = (void *)data;
int rc;
+ bool_t is_vmware = !is_mmio && !data_is_addr &&
+ vmport_check_port(p.addr, p.size);
/*
* Weird-sized accesses have undefined behaviour: we discard writes
@@ -189,11 +193,17 @@ static int hvmemul_do_io(
case STATE_IOREQ_NONE:
break;
case STATE_IORESP_READY:
+ {
+ uint8_t calc_type = is_mmio ? IOREQ_TYPE_COPY : IOREQ_TYPE_PIO;
+
+ if ( is_vmware )
+ calc_type = IOREQ_TYPE_VMWARE_PORT;
+
vio->io_req.state = STATE_IOREQ_NONE;
p = vio->io_req;
/* Verify the emulation request has been correctly re-issued */
- if ( (p.type != (is_mmio ? IOREQ_TYPE_COPY : IOREQ_TYPE_PIO)) ||
+ if ( (p.type != calc_type) ||
(p.addr != addr) ||
(p.size != size) ||
(p.count > *reps) ||
@@ -202,7 +212,7 @@ static int hvmemul_do_io(
(p.data_is_ptr != data_is_addr) ||
(data_is_addr && (p.data != data)) )
domain_crash(currd);
-
+ }
if ( data_is_addr )
return X86EMUL_UNHANDLEABLE;
@@ -322,6 +332,49 @@ static int hvmemul_do_io(
}
}
+ if ( unlikely(is_vmware) )
+ {
+ vmware_regs_t *vr;
+
+ BUILD_BUG_ON(sizeof(ioreq_t) < sizeof(vmware_regs_t));
+
+ p.type = vio->io_req.type = IOREQ_TYPE_VMWARE_PORT;
+ s = hvm_select_ioreq_server(currd, &p);
+ vr = get_vmport_regs_any(s, curr);
+
+ /*
+ * If there is no suitable backing DM, just ignore accesses. If
+ * we do not have access to registers to pass to QEMU, just
+ * ignore access.
+ */
+ if ( !s || !vr )
+ {
+ rc = hvm_process_io_intercept(&null_handler, &p);
+ vio->io_req.state = STATE_IOREQ_NONE;
+ }
+ else
+ {
+ const struct cpu_user_regs *regs = guest_cpu_user_regs();
+
+ p.data = regs->rax;
+ /* The code in QEMU that uses these registers,
+ * vmport.c and vmmouse.c, only uses the 32bit part
+ * of the register. This is how VMware defined the
+ * use of these registers.
+ */
+ vr->ebx = regs->ebx;
+ vr->ecx = regs->ecx;
+ vr->edx = regs->edx;
+ vr->esi = regs->esi;
+ vr->edi = regs->edi;
+
+ rc = hvm_send_ioreq(s, &p, 0);
+ if ( rc != X86EMUL_RETRY || currd->is_shutting_down )
+ vio->io_req.state = STATE_IOREQ_NONE;
+ }
+ break;
+ }
+
if ( !s )
s = hvm_select_ioreq_server(currd, &p);
@@ -4122,6 +4122,7 @@ static int hvm_allow_set_param(struct domain *d,
/* Fall through */
case HVM_PARAM_IOREQ_PFN:
case HVM_PARAM_BUFIOREQ_PFN:
+ case HVM_PARAM_VMPORT_REGS_PFN:
case HVM_PARAM_IOREQ_SERVER_PFN:
case HVM_PARAM_NR_IOREQ_SERVER_PAGES:
case HVM_PARAM_ALTP2M:
@@ -4279,10 +4280,13 @@ static int hvm_set_param(struct domain *d, uint32_t index, uint64_t value)
case HVM_PARAM_IOREQ_PFN:
case HVM_PARAM_BUFIOREQ_PFN:
+ case HVM_PARAM_VMPORT_REGS_PFN:
BUILD_BUG_ON(HVM_PARAM_IOREQ_PFN >
sizeof(d->arch.hvm.ioreq_gfn.legacy_mask) * 8);
BUILD_BUG_ON(HVM_PARAM_BUFIOREQ_PFN >
sizeof(d->arch.hvm.ioreq_gfn.legacy_mask) * 8);
+ BUILD_BUG_ON(HVM_PARAM_VMPORT_REGS_PFN >
+ sizeof(d->arch.hvm.ioreq_gfn.legacy_mask) * 8);
if ( value )
set_bit(index, &d->arch.hvm.ioreq_gfn.legacy_mask);
break;
@@ -108,6 +108,44 @@ static struct hvm_ioreq_vcpu *get_pending_vcpu(const struct vcpu *v,
return NULL;
}
+static vmware_regs_t *get_vmport_regs_one(struct hvm_ioreq_server *s,
+ struct vcpu *v)
+{
+ struct hvm_ioreq_vcpu *sv;
+
+ list_for_each_entry ( sv, &s->ioreq_vcpu_list, list_entry )
+ {
+ if ( sv->vcpu == v )
+ {
+ shared_vmport_iopage_t *p = s->vmport_ioreq.va;
+ if ( !p )
+ return NULL;
+ return &p->vcpu_vmport_regs[v->vcpu_id];
+ }
+ }
+ return NULL;
+}
+
+vmware_regs_t *get_vmport_regs_any(struct hvm_ioreq_server *s, struct vcpu *v)
+{
+ struct domain *d = v->domain;
+ unsigned int id;
+
+ ASSERT((v == current) || !vcpu_runnable(v));
+
+ if ( s )
+ return get_vmport_regs_one(s, v);
+
+ FOR_EACH_IOREQ_SERVER(d, id, s)
+ {
+ vmware_regs_t *ret = get_vmport_regs_one(s, v);
+
+ if ( ret )
+ return ret;
+ }
+ return NULL;
+}
+
bool hvm_io_pending(struct vcpu *v)
{
return get_pending_vcpu(v, NULL);
@@ -206,6 +244,26 @@ bool handle_hvm_io_completion(struct vcpu *v)
return handle_mmio();
case HVMIO_pio_completion:
+ if ( vio->io_req.type == IOREQ_TYPE_VMWARE_PORT )
+ {
+ vmware_regs_t *vr = get_vmport_regs_any(NULL, v);
+
+ if ( vr )
+ {
+ struct cpu_user_regs *regs = guest_cpu_user_regs();
+
+ /* The code in QEMU that uses these registers,
+ * vmport.c and vmmouse.c, only uses the 32bit part
+ * of the register. This is how VMware defined the
+ * use of these registers.
+ */
+ regs->ebx = vr->ebx;
+ regs->ecx = vr->ecx;
+ regs->edx = vr->edx;
+ regs->esi = vr->esi;
+ regs->edi = vr->edi;
+ }
+ }
return handle_pio(vio->io_req.addr, vio->io_req.size,
vio->io_req.dir);
@@ -233,16 +291,28 @@ static gfn_t hvm_alloc_legacy_ioreq_gfn(struct hvm_ioreq_server *s)
unsigned int i;
BUILD_BUG_ON(HVM_PARAM_BUFIOREQ_PFN != HVM_PARAM_IOREQ_PFN + 1);
+ BUILD_BUG_ON(HVM_PARAM_VMPORT_REGS_PFN != HVM_PARAM_BUFIOREQ_PFN + 1);
for ( i = HVM_PARAM_IOREQ_PFN; i <= HVM_PARAM_BUFIOREQ_PFN; i++ )
{
- if ( !test_and_clear_bit(i, &d->arch.hvm.ioreq_gfn.legacy_mask) )
+ if ( test_and_clear_bit(i, &d->arch.hvm.ioreq_gfn.legacy_mask) )
return _gfn(d->arch.hvm.params[i]);
}
return INVALID_GFN;
}
+static gfn_t hvm_alloc_legacy_vmport_gfn(struct hvm_ioreq_server *s)
+{
+ struct domain *d = s->target;
+ unsigned int i = HVM_PARAM_VMPORT_REGS_PFN;
+
+ if ( test_and_clear_bit(i, &d->arch.hvm.ioreq_gfn.legacy_mask) )
+ return _gfn(d->arch.hvm.params[i]);
+
+ return INVALID_GFN;
+}
+
static gfn_t hvm_alloc_ioreq_gfn(struct hvm_ioreq_server *s)
{
struct domain *d = s->target;
@@ -267,12 +337,12 @@ static bool hvm_free_legacy_ioreq_gfn(struct hvm_ioreq_server *s,
struct domain *d = s->target;
unsigned int i;
- for ( i = HVM_PARAM_IOREQ_PFN; i <= HVM_PARAM_BUFIOREQ_PFN; i++ )
+ for ( i = HVM_PARAM_IOREQ_PFN; i <= HVM_PARAM_VMPORT_REGS_PFN; i++ )
{
if ( gfn_eq(gfn, _gfn(d->arch.hvm.params[i])) )
break;
}
- if ( i > HVM_PARAM_BUFIOREQ_PFN )
+ if ( i > HVM_PARAM_VMPORT_REGS_PFN )
return false;
set_bit(i, &d->arch.hvm.ioreq_gfn.legacy_mask);
@@ -293,9 +363,29 @@ static void hvm_free_ioreq_gfn(struct hvm_ioreq_server *s, gfn_t gfn)
}
}
-static void hvm_unmap_ioreq_gfn(struct hvm_ioreq_server *s, bool buf)
+typedef enum {
+ ioreq_pt_ioreq,
+ ioreq_pt_bufioreq,
+ ioreq_pt_vmport,
+} ioreq_pt_;
+
+static void hvm_unmap_ioreq_gfn(struct hvm_ioreq_server *s, ioreq_pt_ pt)
{
- struct hvm_ioreq_page *iorp = buf ? &s->bufioreq : &s->ioreq;
+ struct hvm_ioreq_page *iorp = NULL;
+
+ switch ( pt )
+ {
+ case ioreq_pt_ioreq:
+ iorp = &s->ioreq;
+ break;
+ case ioreq_pt_bufioreq:
+ iorp = &s->bufioreq;
+ break;
+ case ioreq_pt_vmport:
+ iorp = &s->vmport_ioreq;
+ break;
+ }
+ ASSERT(iorp);
if ( gfn_eq(iorp->gfn, INVALID_GFN) )
return;
@@ -307,12 +397,26 @@ static void hvm_unmap_ioreq_gfn(struct hvm_ioreq_server *s, bool buf)
iorp->gfn = INVALID_GFN;
}
-static int hvm_map_ioreq_gfn(struct hvm_ioreq_server *s, bool buf)
+static int hvm_map_ioreq_gfn(struct hvm_ioreq_server *s, ioreq_pt_ pt)
{
struct domain *d = s->target;
- struct hvm_ioreq_page *iorp = buf ? &s->bufioreq : &s->ioreq;
+ struct hvm_ioreq_page *iorp = NULL;
int rc;
+ switch ( pt )
+ {
+ case ioreq_pt_ioreq:
+ iorp = &s->ioreq;
+ break;
+ case ioreq_pt_bufioreq:
+ iorp = &s->bufioreq;
+ break;
+ case ioreq_pt_vmport:
+ iorp = &s->vmport_ioreq;
+ break;
+ }
+ ASSERT(iorp);
+
if ( iorp->page )
{
/*
@@ -329,7 +433,10 @@ static int hvm_map_ioreq_gfn(struct hvm_ioreq_server *s, bool buf)
if ( d->is_dying )
return -EINVAL;
- iorp->gfn = hvm_alloc_ioreq_gfn(s);
+ if ( pt == ioreq_pt_vmport )
+ iorp->gfn = hvm_alloc_legacy_vmport_gfn(s);
+ else
+ iorp->gfn = hvm_alloc_ioreq_gfn(s);
if ( gfn_eq(iorp->gfn, INVALID_GFN) )
return -ENOMEM;
@@ -338,16 +445,30 @@ static int hvm_map_ioreq_gfn(struct hvm_ioreq_server *s, bool buf)
&iorp->va);
if ( rc )
- hvm_unmap_ioreq_gfn(s, buf);
+ hvm_unmap_ioreq_gfn(s, pt);
return rc;
}
-static int hvm_alloc_ioreq_mfn(struct hvm_ioreq_server *s, bool buf)
+static int hvm_alloc_ioreq_mfn(struct hvm_ioreq_server *s, ioreq_pt_ pt)
{
- struct hvm_ioreq_page *iorp = buf ? &s->bufioreq : &s->ioreq;
+ struct hvm_ioreq_page *iorp = NULL;
struct page_info *page;
+ switch ( pt )
+ {
+ case ioreq_pt_ioreq:
+ iorp = &s->ioreq;
+ break;
+ case ioreq_pt_bufioreq:
+ iorp = &s->bufioreq;
+ break;
+ case ioreq_pt_vmport:
+ iorp = &s->vmport_ioreq;
+ break;
+ }
+ ASSERT(iorp);
+
if ( iorp->page )
{
/*
@@ -391,10 +512,25 @@ static int hvm_alloc_ioreq_mfn(struct hvm_ioreq_server *s, bool buf)
return -ENOMEM;
}
-static void hvm_free_ioreq_mfn(struct hvm_ioreq_server *s, bool buf)
+static void hvm_free_ioreq_mfn(struct hvm_ioreq_server *s, ioreq_pt_ pt)
{
- struct hvm_ioreq_page *iorp = buf ? &s->bufioreq : &s->ioreq;
- struct page_info *page = iorp->page;
+ struct hvm_ioreq_page *iorp = NULL;
+ struct page_info *page = NULL;
+
+ switch ( pt )
+ {
+ case ioreq_pt_ioreq:
+ iorp = &s->ioreq;
+ break;
+ case ioreq_pt_bufioreq:
+ iorp = &s->bufioreq;
+ break;
+ case ioreq_pt_vmport:
+ iorp = &s->vmport_ioreq;
+ break;
+ }
+ ASSERT(iorp);
+ page = iorp->page;
if ( !page )
return;
@@ -418,7 +554,9 @@ bool is_ioreq_server_page(struct domain *d, const struct page_info *page)
FOR_EACH_IOREQ_SERVER(d, id, s)
{
- if ( (s->ioreq.page == page) || (s->bufioreq.page == page) )
+ if ( (s->ioreq.page == page) ||
+ (s->bufioreq.page == page) ||
+ (s->vmport_ioreq.page == page) )
{
found = true;
break;
@@ -430,11 +568,25 @@ bool is_ioreq_server_page(struct domain *d, const struct page_info *page)
return found;
}
-static void hvm_remove_ioreq_gfn(struct hvm_ioreq_server *s, bool buf)
+static void hvm_remove_ioreq_gfn(struct hvm_ioreq_server *s, ioreq_pt_ pt)
{
struct domain *d = s->target;
- struct hvm_ioreq_page *iorp = buf ? &s->bufioreq : &s->ioreq;
+ struct hvm_ioreq_page *iorp = NULL;
+
+ switch ( pt )
+ {
+ case ioreq_pt_ioreq:
+ iorp = &s->ioreq;
+ break;
+ case ioreq_pt_bufioreq:
+ iorp = &s->bufioreq;
+ break;
+ case ioreq_pt_vmport:
+ iorp = &s->vmport_ioreq;
+ break;
+ }
+ ASSERT(iorp);
if ( gfn_eq(iorp->gfn, INVALID_GFN) )
return;
@@ -445,12 +597,26 @@ static void hvm_remove_ioreq_gfn(struct hvm_ioreq_server *s, bool buf)
clear_page(iorp->va);
}
-static int hvm_add_ioreq_gfn(struct hvm_ioreq_server *s, bool buf)
+static int hvm_add_ioreq_gfn(struct hvm_ioreq_server *s, ioreq_pt_ pt)
{
struct domain *d = s->target;
- struct hvm_ioreq_page *iorp = buf ? &s->bufioreq : &s->ioreq;
+ struct hvm_ioreq_page *iorp = NULL;
int rc;
+ switch ( pt )
+ {
+ case ioreq_pt_ioreq:
+ iorp = &s->ioreq;
+ break;
+ case ioreq_pt_bufioreq:
+ iorp = &s->bufioreq;
+ break;
+ case ioreq_pt_vmport:
+ iorp = &s->vmport_ioreq;
+ break;
+ }
+ ASSERT(iorp);
+
if ( gfn_eq(iorp->gfn, INVALID_GFN) )
return 0;
@@ -480,6 +646,9 @@ static void hvm_update_ioreq_evtchn(struct hvm_ioreq_server *s,
#define HANDLE_BUFIOREQ(s) \
((s)->bufioreq_handling != HVM_IOREQSRV_BUFIOREQ_OFF)
+#define HANDLE_VMPORT_IOREQ(s) \
+ ((s)->target->arch.hvm.is_vmware_port_enabled)
+
static int hvm_ioreq_server_add_vcpu(struct hvm_ioreq_server *s,
struct vcpu *v)
{
@@ -590,42 +759,72 @@ static int hvm_ioreq_server_map_pages(struct hvm_ioreq_server *s)
{
int rc;
- rc = hvm_map_ioreq_gfn(s, false);
+ rc = hvm_map_ioreq_gfn(s, ioreq_pt_ioreq);
if ( !rc && HANDLE_BUFIOREQ(s) )
- rc = hvm_map_ioreq_gfn(s, true);
+ rc = hvm_map_ioreq_gfn(s, ioreq_pt_bufioreq);
if ( rc )
- hvm_unmap_ioreq_gfn(s, false);
+ {
+ hvm_unmap_ioreq_gfn(s, ioreq_pt_ioreq);
+ return rc;
+ }
+
+ if ( HANDLE_VMPORT_IOREQ(s) )
+ {
+ rc = hvm_map_ioreq_gfn(s, ioreq_pt_vmport);
+
+ if ( rc )
+ {
+ hvm_unmap_ioreq_gfn(s, ioreq_pt_bufioreq);
+ hvm_unmap_ioreq_gfn(s, ioreq_pt_ioreq);
+ }
+ }
return rc;
}
static void hvm_ioreq_server_unmap_pages(struct hvm_ioreq_server *s)
{
- hvm_unmap_ioreq_gfn(s, true);
- hvm_unmap_ioreq_gfn(s, false);
+ hvm_unmap_ioreq_gfn(s, ioreq_pt_vmport);
+ hvm_unmap_ioreq_gfn(s, ioreq_pt_bufioreq);
+ hvm_unmap_ioreq_gfn(s, ioreq_pt_ioreq);
}
static int hvm_ioreq_server_alloc_pages(struct hvm_ioreq_server *s)
{
int rc;
- rc = hvm_alloc_ioreq_mfn(s, false);
+ rc = hvm_alloc_ioreq_mfn(s, ioreq_pt_ioreq);
- if ( !rc && (s->bufioreq_handling != HVM_IOREQSRV_BUFIOREQ_OFF) )
- rc = hvm_alloc_ioreq_mfn(s, true);
+ if ( !rc && HANDLE_BUFIOREQ(s) )
+ rc = hvm_alloc_ioreq_mfn(s, ioreq_pt_bufioreq);
if ( rc )
- hvm_free_ioreq_mfn(s, false);
+ {
+ hvm_free_ioreq_mfn(s, ioreq_pt_ioreq);
+ return rc;
+ }
+
+ if ( HANDLE_VMPORT_IOREQ(s) )
+ {
+ rc = hvm_alloc_ioreq_mfn(s, ioreq_pt_vmport);
+
+ if ( rc )
+ {
+ hvm_free_ioreq_mfn(s, ioreq_pt_bufioreq);
+ hvm_free_ioreq_mfn(s, ioreq_pt_ioreq);
+ }
+ }
return rc;
}
static void hvm_ioreq_server_free_pages(struct hvm_ioreq_server *s)
{
- hvm_free_ioreq_mfn(s, true);
- hvm_free_ioreq_mfn(s, false);
+ hvm_free_ioreq_mfn(s, ioreq_pt_vmport);
+ hvm_free_ioreq_mfn(s, ioreq_pt_bufioreq);
+ hvm_free_ioreq_mfn(s, ioreq_pt_ioreq);
}
static void hvm_ioreq_server_free_rangesets(struct hvm_ioreq_server *s)
@@ -645,12 +844,38 @@ static int hvm_ioreq_server_alloc_rangesets(struct hvm_ioreq_server *s,
for ( i = 0; i < NR_IO_RANGE_TYPES; i++ )
{
char *name;
+ char *type_name = NULL;
+ unsigned int limit;
- rc = asprintf(&name, "ioreq_server %d %s", id,
- (i == XEN_DMOP_IO_RANGE_PORT) ? "port" :
- (i == XEN_DMOP_IO_RANGE_MEMORY) ? "memory" :
- (i == XEN_DMOP_IO_RANGE_PCI) ? "pci" :
- "");
+ switch ( i )
+ {
+ case XEN_DMOP_IO_RANGE_PORT:
+ type_name = "port";
+ limit = MAX_NR_IO_RANGES;
+ break;
+ case XEN_DMOP_IO_RANGE_MEMORY:
+ type_name = "memory";
+ limit = MAX_NR_IO_RANGES;
+ break;
+ case XEN_DMOP_IO_RANGE_PCI:
+ type_name = "pci";
+ limit = MAX_NR_IO_RANGES;
+ break;
+ case XEN_DMOP_IO_RANGE_VMWARE_PORT:
+ type_name = "VMware port";
+ limit = 1;
+ break;
+ case XEN_DMOP_IO_RANGE_TIMEOFFSET:
+ type_name = "timeoffset";
+ limit = 1;
+ break;
+ default:
+ break;
+ }
+ if ( !type_name )
+ continue;
+
+ rc = asprintf(&name, "ioreq_server %d %s", id, type_name);
if ( rc )
goto fail;
@@ -663,7 +888,11 @@ static int hvm_ioreq_server_alloc_rangesets(struct hvm_ioreq_server *s,
if ( !s->range[i] )
goto fail;
- rangeset_limit(s->range[i], MAX_NR_IO_RANGES);
+ rangeset_limit(s->range[i], limit);
+
+ /* VMware port */
+ if ( i == XEN_DMOP_IO_RANGE_VMWARE_PORT && s->vmport_enabled )
+ rc = rangeset_add_range(s->range[i], 1, 1);
}
return 0;
@@ -683,8 +912,9 @@ static void hvm_ioreq_server_enable(struct hvm_ioreq_server *s)
if ( s->enabled )
goto done;
- hvm_remove_ioreq_gfn(s, false);
- hvm_remove_ioreq_gfn(s, true);
+ hvm_remove_ioreq_gfn(s, ioreq_pt_vmport);
+ hvm_remove_ioreq_gfn(s, ioreq_pt_bufioreq);
+ hvm_remove_ioreq_gfn(s, ioreq_pt_ioreq);
s->enabled = true;
@@ -704,8 +934,9 @@ static void hvm_ioreq_server_disable(struct hvm_ioreq_server *s)
if ( !s->enabled )
goto done;
- hvm_add_ioreq_gfn(s, true);
- hvm_add_ioreq_gfn(s, false);
+ hvm_add_ioreq_gfn(s, ioreq_pt_vmport);
+ hvm_add_ioreq_gfn(s, ioreq_pt_bufioreq);
+ hvm_add_ioreq_gfn(s, ioreq_pt_ioreq);
s->enabled = false;
@@ -714,7 +945,7 @@ static void hvm_ioreq_server_disable(struct hvm_ioreq_server *s)
}
static int hvm_ioreq_server_init(struct hvm_ioreq_server *s,
- struct domain *d, int bufioreq_handling,
+ struct domain *d, int flags,
ioservid_t id)
{
struct domain *currd = current->domain;
@@ -730,14 +961,18 @@ static int hvm_ioreq_server_init(struct hvm_ioreq_server *s,
INIT_LIST_HEAD(&s->ioreq_vcpu_list);
spin_lock_init(&s->bufioreq_lock);
+ s->vmport_enabled = d->arch.hvm.is_vmware_port_enabled &&
+ !(flags & HVM_IOREQSRV_DISABLE_VMPORT);
+
s->ioreq.gfn = INVALID_GFN;
s->bufioreq.gfn = INVALID_GFN;
+ s->vmport_ioreq.gfn = INVALID_GFN;
rc = hvm_ioreq_server_alloc_rangesets(s, id);
if ( rc )
return rc;
- s->bufioreq_handling = bufioreq_handling;
+ s->bufioreq_handling = flags & HVM_IOREQSRV_BUFIOREQ_MASK;
for_each_vcpu ( d, v )
{
@@ -780,14 +1015,15 @@ static void hvm_ioreq_server_deinit(struct hvm_ioreq_server *s)
put_domain(s->emulator);
}
-int hvm_create_ioreq_server(struct domain *d, int bufioreq_handling,
+int hvm_create_ioreq_server(struct domain *d, int flags,
ioservid_t *id)
{
struct hvm_ioreq_server *s;
unsigned int i;
int rc;
- if ( bufioreq_handling > HVM_IOREQSRV_BUFIOREQ_ATOMIC )
+ if ( flags & ~HVM_IOREQSRV_FLAGS_MASK ||
+ (flags & HVM_IOREQSRV_BUFIOREQ_MASK) > HVM_IOREQSRV_BUFIOREQ_ATOMIC )
return -EINVAL;
s = xzalloc(struct hvm_ioreq_server);
@@ -813,7 +1049,7 @@ int hvm_create_ioreq_server(struct domain *d, int bufioreq_handling,
*/
set_ioreq_server(d, i, s);
- rc = hvm_ioreq_server_init(s, d, bufioreq_handling, i);
+ rc = hvm_ioreq_server_init(s, d, flags, i);
if ( rc )
{
set_ioreq_server(d, i, NULL);
@@ -1004,6 +1240,8 @@ int hvm_map_io_range_to_ioreq_server(struct domain *d, ioservid_t id,
case XEN_DMOP_IO_RANGE_PORT:
case XEN_DMOP_IO_RANGE_MEMORY:
case XEN_DMOP_IO_RANGE_PCI:
+ case XEN_DMOP_IO_RANGE_TIMEOFFSET:
+ case XEN_DMOP_IO_RANGE_VMWARE_PORT:
r = s->range[type];
break;
@@ -1056,6 +1294,8 @@ int hvm_unmap_io_range_from_ioreq_server(struct domain *d, ioservid_t id,
case XEN_DMOP_IO_RANGE_PORT:
case XEN_DMOP_IO_RANGE_MEMORY:
case XEN_DMOP_IO_RANGE_PCI:
+ case XEN_DMOP_IO_RANGE_TIMEOFFSET:
+ case XEN_DMOP_IO_RANGE_VMWARE_PORT:
r = s->range[type];
break;
@@ -1248,7 +1488,10 @@ struct hvm_ioreq_server *hvm_select_ioreq_server(struct domain *d,
uint64_t addr;
unsigned int id;
- if ( p->type != IOREQ_TYPE_COPY && p->type != IOREQ_TYPE_PIO )
+ if ( p->type != IOREQ_TYPE_COPY &&
+ p->type != IOREQ_TYPE_PIO &&
+ p->type != IOREQ_TYPE_VMWARE_PORT &&
+ p->type != IOREQ_TYPE_TIMEOFFSET )
return NULL;
cf8 = d->arch.hvm.pci_cf8;
@@ -1282,8 +1525,9 @@ struct hvm_ioreq_server *hvm_select_ioreq_server(struct domain *d,
}
else
{
- type = (p->type == IOREQ_TYPE_PIO) ?
- XEN_DMOP_IO_RANGE_PORT : XEN_DMOP_IO_RANGE_MEMORY;
+ type = (p->type == IOREQ_TYPE_PIO) ? XEN_DMOP_IO_RANGE_PORT :
+ (p->type == IOREQ_TYPE_VMWARE_PORT) ? XEN_DMOP_IO_RANGE_VMWARE_PORT :
+ XEN_DMOP_IO_RANGE_MEMORY;
addr = p->addr;
}
@@ -1326,6 +1570,14 @@ struct hvm_ioreq_server *hvm_select_ioreq_server(struct domain *d,
}
break;
+
+ case XEN_DMOP_IO_RANGE_VMWARE_PORT:
+ case XEN_DMOP_IO_RANGE_TIMEOFFSET:
+ /* The 'special' range of [1,1] is checked for being enabled. */
+ if ( rangeset_contains_singleton(r, 1) )
+ return s;
+
+ break;
}
}
@@ -14,6 +14,7 @@
*/
#include <xen/lib.h>
+#include <asm/mc146818rtc.h>
#include <asm/hvm/hvm.h>
#include <asm/hvm/support.h>
@@ -23,6 +24,32 @@ static int vmport_ioport(int dir, uint32_t port, uint32_t bytes, uint32_t *val)
{
struct cpu_user_regs *regs = guest_cpu_user_regs();
+#define port_overlap(p, n) \
+ ((p + n > BDOOR_PORT) && (p + n <= BDOOR_PORT + 4) ? 1 : \
+ (BDOOR_PORT + 4 > p) && (BDOOR_PORT + 4 <= p + n) ? 1 : 0)
+
+ BUILD_BUG_ON(port_overlap(PIT_BASE, 4));
+ BUILD_BUG_ON(port_overlap(0x61, 1));
+ BUILD_BUG_ON(port_overlap(XEN_HVM_DEBUGCONS_IOPORT, 1));
+ BUILD_BUG_ON(port_overlap(0xcf8, 4));
+/* #define TMR_VAL_ADDR_V0 (ACPI_PM_TMR_BLK_ADDRESS_V0) */
+ BUILD_BUG_ON(port_overlap(ACPI_PM_TMR_BLK_ADDRESS_V0, 4));
+/* #define PM1a_STS_ADDR_V0 (ACPI_PM1A_EVT_BLK_ADDRESS_V0) */
+ BUILD_BUG_ON(port_overlap(ACPI_PM1A_EVT_BLK_ADDRESS_V0, 4));
+ BUILD_BUG_ON(port_overlap(RTC_PORT(0), 2));
+ BUILD_BUG_ON(port_overlap(0x3c4, 2));
+ BUILD_BUG_ON(port_overlap(0x3ce, 2));
+/*
+ * acpi_smi_cmd can not be checked at build time:
+ * xen/include/asm-x86/acpi.h:extern u32 acpi_smi_cmd;
+ * xen/arch/x86/acpi/boot.c: acpi_smi_cmd = fadt->smi_command;
+ BUILD_BUG_ON(port_overlap(acpi_smi_cmd, 1));
+*/
+ BUILD_BUG_ON(port_overlap(0x20, 2));
+ BUILD_BUG_ON(port_overlap(0xa0, 2));
+ BUILD_BUG_ON(port_overlap(0x4d0, 1));
+ BUILD_BUG_ON(port_overlap(0x4d1, 1));
+
/*
* While VMware expects only 32-bit in, they do support using
* other sizes and out. However they do require only the 1 port
@@ -137,6 +164,15 @@ void vmport_register(struct domain *d)
register_portio_handler(d, BDOOR_PORT, 4, vmport_ioport);
}
+bool_t vmport_check_port(unsigned int port, unsigned int bytes)
+{
+ struct domain *currd = current->domain;
+
+ return is_hvm_domain(currd) &&
+ currd->arch.hvm.is_vmware_port_enabled &&
+ (port >= BDOOR_PORT) && ((port + bytes) <= (BDOOR_PORT + 4));
+}
+
/*
* Local variables:
* mode: C
@@ -43,7 +43,7 @@ struct hvm_ioreq_vcpu {
bool pending;
};
-#define NR_IO_RANGE_TYPES (XEN_DMOP_IO_RANGE_PCI + 1)
+#define NR_IO_RANGE_TYPES (XEN_DMOP_IO_RANGE_VMWARE_PORT + 1)
#define MAX_NR_IO_RANGES 256
struct hvm_ioreq_server {
@@ -54,6 +54,7 @@ struct hvm_ioreq_server {
struct hvm_ioreq_page ioreq;
struct list_head ioreq_vcpu_list;
+ struct hvm_ioreq_page vmport_ioreq;
struct hvm_ioreq_page bufioreq;
/* Lock to serialize access to buffered ioreq ring */
@@ -62,6 +63,7 @@ struct hvm_ioreq_server {
struct rangeset *range[NR_IO_RANGE_TYPES];
bool enabled;
uint8_t bufioreq_handling;
+ bool_t vmport_enabled;
};
#ifdef CONFIG_MEM_SHARING
@@ -790,6 +790,7 @@ static inline bool hvm_has_set_descriptor_access_exiting(void)
#endif /* CONFIG_HVM */
void vmport_register(struct domain *d);
+bool_t vmport_check_port(unsigned int port, unsigned int bytes);
#endif /* __ASM_X86_HVM_HVM_H__ */
@@ -60,16 +60,24 @@ typedef uint16_t ioservid_t;
* secondary emulator.
*
* The <id> handed back is unique for target domain. The valur of
- * <handle_bufioreq> should be one of HVM_IOREQSRV_BUFIOREQ_* defined in
- * hvm_op.h. If the value is HVM_IOREQSRV_BUFIOREQ_OFF then the buffered
+ * <flags> should be one of HVM_IOREQSRV_BUFIOREQ_* defined in
+ * hvm_op.h and ored with HVM_IOREQSRV_DISABLE_VMPORT defined below
+ * if not the 1st IOREQ Server that supports VMware port operation.
+ * If the value is HVM_IOREQSRV_BUFIOREQ_OFF then the buffered
* ioreq ring will not be allocated and hence all emulation requests to
* this server will be synchronous.
*/
#define XEN_DMOP_create_ioreq_server 1
struct xen_dm_op_create_ioreq_server {
- /* IN - should server handle buffered ioreqs */
- uint8_t handle_bufioreq;
+ /* IN - should server handle buffered ioreqs and/or vmport regs */
+#define HVM_IOREQSRV_BUFIOREQ_MASK 3
+/*
+ * Disable vmport regs mapping.
+ */
+#define HVM_IOREQSRV_DISABLE_VMPORT 4
+#define HVM_IOREQSRV_FLAGS_MASK 7
+ uint8_t flags;
uint8_t pad[3];
/* OUT - server id */
ioservid_t id;
@@ -132,6 +140,9 @@ struct xen_dm_op_get_ioreq_server_info {
*
* NOTE: unless an emulation request falls entirely within a range mapped
* by a secondary emulator, it will not be passed to that emulator.
+ *
+ * NOTE: The 'special' range of [1,1] is what is checked for on
+ * TIMEOFFSET and VMWARE_PORT.
*/
#define XEN_DMOP_map_io_range_to_ioreq_server 3
#define XEN_DMOP_unmap_io_range_from_ioreq_server 4
@@ -145,6 +156,8 @@ struct xen_dm_op_ioreq_server_range {
# define XEN_DMOP_IO_RANGE_PORT 0 /* I/O port range */
# define XEN_DMOP_IO_RANGE_MEMORY 1 /* MMIO range */
# define XEN_DMOP_IO_RANGE_PCI 2 /* PCI segment/bus/dev/func range */
+# define XEN_DMOP_IO_RANGE_TIMEOFFSET 7 /* TIMEOFFSET special range */
+# define XEN_DMOP_IO_RANGE_VMWARE_PORT 9 /* VMware port special range */
/* IN - inclusive start and end of range */
uint64_aligned_t start, end;
};
@@ -37,6 +37,7 @@
#define IOREQ_TYPE_PCI_CONFIG 2
#define IOREQ_TYPE_TIMEOFFSET 7
#define IOREQ_TYPE_INVALIDATE 8 /* mapcache */
+#define IOREQ_TYPE_VMWARE_PORT 9 /* pio + vmport registers */
/*
* VMExit dispatcher should cooperate with instruction decoder to
@@ -48,6 +49,8 @@
*
* 63....48|47..40|39..35|34..32|31........0
* SEGMENT |BUS |DEV |FN |OFFSET
+ *
+ * For I/O type IOREQ_TYPE_VMWARE_PORT also use the vmware_regs.
*/
struct ioreq {
uint64_t addr; /* physical address */
@@ -66,11 +69,25 @@ struct ioreq {
};
typedef struct ioreq ioreq_t;
+struct vmware_regs {
+ uint32_t esi;
+ uint32_t edi;
+ uint32_t ebx;
+ uint32_t ecx;
+ uint32_t edx;
+};
+typedef struct vmware_regs vmware_regs_t;
+
struct shared_iopage {
struct ioreq vcpu_ioreq[1];
};
typedef struct shared_iopage shared_iopage_t;
+struct shared_vmport_iopage {
+ struct vmware_regs vcpu_vmport_regs[1];
+};
+typedef struct shared_vmport_iopage shared_vmport_iopage_t;
+
struct buf_ioreq {
uint8_t type; /* I/O type */
uint8_t pad:1;
@@ -94,8 +94,8 @@
#define HVM_PARAM_STORE_EVTCHN 2
#define HVM_PARAM_IOREQ_PFN 5
-
#define HVM_PARAM_BUFIOREQ_PFN 6
+#define HVM_PARAM_VMPORT_REGS_PFN 7
#if defined(__i386__) || defined(__x86_64__)