diff --git a/xen/arch/x86/hvm/hvm.c b/xen/arch/x86/hvm/hvm.c index 4e03e067fa..af13de3745 100644 --- a/xen/arch/x86/hvm/hvm.c +++ b/xen/arch/x86/hvm/hvm.c @@ -4079,6 +4079,8 @@ static int hvm_allow_set_param(struct domain *d, { /* The following parameters should only be changed once. */ case HVM_PARAM_VIRIDIAN: + case HVM_PARAM_IOREQ_PFN: + case HVM_PARAM_BUFIOREQ_PFN: case HVM_PARAM_IOREQ_SERVER_PFN: case HVM_PARAM_NR_IOREQ_SERVER_PAGES: case HVM_PARAM_ALTP2M: @@ -4250,6 +4252,17 @@ static int hvmop_set_param( break; } + + case HVM_PARAM_IOREQ_PFN: + case HVM_PARAM_BUFIOREQ_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); + if ( a.value ) + set_bit(a.index, &d->arch.hvm.ioreq_gfn.legacy_mask); + break; + case HVM_PARAM_X87_FIP_WIDTH: if ( a.value != 0 && a.value != 4 && a.value != 8 ) { diff --git a/xen/arch/x86/hvm/ioreq.c b/xen/arch/x86/hvm/ioreq.c index 3569beaad5..e2e755a8a1 100644 --- a/xen/arch/x86/hvm/ioreq.c +++ b/xen/arch/x86/hvm/ioreq.c @@ -237,6 +237,22 @@ bool handle_hvm_io_completion(struct vcpu *v) return true; } +static gfn_t hvm_alloc_legacy_ioreq_gfn(struct hvm_ioreq_server *s) +{ + struct domain *d = s->target; + unsigned int i; + + BUILD_BUG_ON(HVM_PARAM_BUFIOREQ_PFN != HVM_PARAM_IOREQ_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) ) + 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; @@ -248,7 +264,29 @@ static gfn_t hvm_alloc_ioreq_gfn(struct hvm_ioreq_server *s) return _gfn(d->arch.hvm.ioreq_gfn.base + i); } - return INVALID_GFN; + /* + * If we are out of 'normal' GFNs then we may still have a 'legacy' + * GFN available. + */ + return hvm_alloc_legacy_ioreq_gfn(s); +} + +static bool hvm_free_legacy_ioreq_gfn(struct hvm_ioreq_server *s, + gfn_t gfn) +{ + struct domain *d = s->target; + unsigned int i; + + for ( i = HVM_PARAM_IOREQ_PFN; i <= HVM_PARAM_BUFIOREQ_PFN; i++ ) + { + if ( gfn_eq(gfn, _gfn(d->arch.hvm.params[i])) ) + break; + } + if ( i > HVM_PARAM_BUFIOREQ_PFN ) + return false; + + set_bit(i, &d->arch.hvm.ioreq_gfn.legacy_mask); + return true; } static void hvm_free_ioreq_gfn(struct hvm_ioreq_server *s, gfn_t gfn) @@ -258,7 +296,11 @@ static void hvm_free_ioreq_gfn(struct hvm_ioreq_server *s, gfn_t gfn) ASSERT(!gfn_eq(gfn, INVALID_GFN)); - set_bit(i, &d->arch.hvm.ioreq_gfn.mask); + if ( !hvm_free_legacy_ioreq_gfn(s, gfn) ) + { + ASSERT(i < sizeof(d->arch.hvm.ioreq_gfn.mask) * 8); + set_bit(i, &d->arch.hvm.ioreq_gfn.mask); + } } static void hvm_unmap_ioreq_gfn(struct hvm_ioreq_server *s, bool buf) diff --git a/xen/include/asm-x86/hvm/domain.h b/xen/include/asm-x86/hvm/domain.h index 80b2ab041e..3e7331817f 100644 --- a/xen/include/asm-x86/hvm/domain.h +++ b/xen/include/asm-x86/hvm/domain.h @@ -95,7 +95,8 @@ struct hvm_domain { /* Guest page range used for non-default ioreq servers */ struct { unsigned long base; - unsigned long mask; + unsigned long mask; /* indexed by GFN minus base */ + unsigned long legacy_mask; /* indexed by HVM param number */ } ioreq_gfn; /* Lock protects all other values in the sub-struct and the default */