Jan Beulich
2013-Nov-11 08:33 UTC
[PATCH] nested SVM: adjust guest handling of structure mappings
For one, nestedsvm_vmcb_map() error checking must not consist of using
assertions: Global (permanent) mappings can fail, and hence failure
needs to be dealt with properly. And non-global (transient) mappings
can''t fail anyway.
And then the I/O port access bitmap handling was broken: It checked
only to first of the accessed ports rather than each of them.
Signed-off-by: Jan Beulich <jbeulich@suse.com>
--- a/xen/arch/x86/hvm/svm/nestedsvm.c
+++ b/xen/arch/x86/hvm/svm/nestedsvm.c
@@ -360,7 +360,6 @@ static int nsvm_vmrun_permissionmap(stru
svm->ns_iomap_pa = ns_vmcb->_iopm_base_pa;
ns_viomap = hvm_map_guest_frame_ro(svm->ns_iomap_pa >> PAGE_SHIFT,
0);
- ASSERT(ns_viomap != NULL);
ioport_80 = test_bit(0x80, ns_viomap);
ioport_ed = test_bit(0xed, ns_viomap);
hvm_unmap_guest_frame(ns_viomap, 0);
@@ -866,40 +865,45 @@ nsvm_vmcb_guest_intercepts_msr(unsigned
static int
nsvm_vmcb_guest_intercepts_ioio(paddr_t iopm_pa, uint64_t exitinfo1)
{
- unsigned long iopm_gfn = iopm_pa >> PAGE_SHIFT;
- unsigned long *io_bitmap = NULL;
+ unsigned long gfn = iopm_pa >> PAGE_SHIFT;
+ unsigned long *io_bitmap;
ioio_info_t ioinfo;
uint16_t port;
+ unsigned int size;
bool_t enabled;
- unsigned long gfn = 0; /* gcc ... */
ioinfo.bytes = exitinfo1;
port = ioinfo.fields.port;
+ size = ioinfo.fields.sz32 ? 4 : ioinfo.fields.sz16 ? 2 : 1;
- switch (port) {
- case 0 ... 32767: /* first 4KB page */
- gfn = iopm_gfn;
+ switch ( port )
+ {
+ case 0 ... 8 * PAGE_SIZE - 1: /* first 4KB page */
break;
- case 32768 ... 65535: /* second 4KB page */
- port -= 32768;
- gfn = iopm_gfn + 1;
+ case 8 * PAGE_SIZE ... 2 * 8 * PAGE_SIZE - 1: /* second 4KB page */
+ port -= 8 * PAGE_SIZE;
+ ++gfn;
break;
default:
BUG();
break;
}
- io_bitmap = hvm_map_guest_frame_ro(gfn, 0);
- if (io_bitmap == NULL) {
- gdprintk(XENLOG_ERR,
- "IOIO intercept: mapping of permission map failed\n");
- return NESTEDHVM_VMEXIT_ERROR;
+ for ( io_bitmap = hvm_map_guest_frame_ro(gfn, 0); ; )
+ {
+ enabled = test_bit(port, io_bitmap);
+ if ( !enabled || !--size )
+ break;
+ if ( unlikely(++port == 8 * PAGE_SIZE) )
+ {
+ hvm_unmap_guest_frame(io_bitmap, 0);
+ io_bitmap = hvm_map_guest_frame_ro(++gfn, 0);
+ port -= 8 * PAGE_SIZE;
+ }
}
-
- enabled = test_bit(port, io_bitmap);
hvm_unmap_guest_frame(io_bitmap, 0);
- if (!enabled)
+ if ( !enabled )
return NESTEDHVM_VMEXIT_HOST;
return NESTEDHVM_VMEXIT_INJECT;
@@ -966,8 +970,8 @@ nsvm_vmcb_guest_intercepts_exitcode(stru
switch (exitcode) {
case VMEXIT_MSR:
ASSERT(regs != NULL);
- nestedsvm_vmcb_map(v, nv->nv_vvmcxaddr);
- ASSERT(nv->nv_vvmcx != NULL);
+ if ( !nestedsvm_vmcb_map(v, nv->nv_vvmcxaddr) )
+ break;
ns_vmcb = nv->nv_vvmcx;
vmexits = nsvm_vmcb_guest_intercepts_msr(svm->ns_cached_msrpm,
regs->ecx, ns_vmcb->exitinfo1 != 0);
@@ -975,8 +979,8 @@ nsvm_vmcb_guest_intercepts_exitcode(stru
return 0;
break;
case VMEXIT_IOIO:
- nestedsvm_vmcb_map(v, nv->nv_vvmcxaddr);
- ASSERT(nv->nv_vvmcx != NULL);
+ if ( !nestedsvm_vmcb_map(v, nv->nv_vvmcxaddr) )
+ break;
ns_vmcb = nv->nv_vvmcx;
vmexits = nsvm_vmcb_guest_intercepts_ioio(ns_vmcb->_iopm_base_pa,
ns_vmcb->exitinfo1);
_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
http://lists.xen.org/xen-devel
Jan Beulich
2013-Nov-11 11:07 UTC
[PATCH v2] nested SVM: adjust guest handling of structure mappings
For one, nestedsvm_vmcb_map() error checking must not consist of using
assertions: Global (permanent) mappings can fail, and hence failure
needs to be dealt with properly. And non-global (transient) mappings
can''t fail anyway.
And then the I/O port access bitmap handling was broken: It checked
only to first of the accessed ports rather than each of them.
Signed-off-by: Jan Beulich <jbeulich@suse.com>
---
v2: Refine the change to nsvm_vmrun_permissionmap() -
hvm_map_guest_frame_*() can return NULL for reasons other than
map_domain_page_global() doing so (pointed out by Andrew Cooper).
That leaves the function broken though (it ought to handle the
shared/paged out cases in an appropriate way).
--- a/xen/arch/x86/hvm/svm/nestedsvm.c
+++ b/xen/arch/x86/hvm/svm/nestedsvm.c
@@ -342,7 +342,7 @@ static int nsvm_vmrun_permissionmap(stru
unsigned int i;
enum hvm_copy_result ret;
unsigned long *ns_viomap;
- bool_t ioport_80, ioport_ed;
+ bool_t ioport_80 = 1, ioport_ed = 1;
ns_msrpm_ptr = (unsigned long *)svm->ns_cached_msrpm;
@@ -360,10 +360,12 @@ static int nsvm_vmrun_permissionmap(stru
svm->ns_iomap_pa = ns_vmcb->_iopm_base_pa;
ns_viomap = hvm_map_guest_frame_ro(svm->ns_iomap_pa >> PAGE_SHIFT,
0);
- ASSERT(ns_viomap != NULL);
- ioport_80 = test_bit(0x80, ns_viomap);
- ioport_ed = test_bit(0xed, ns_viomap);
- hvm_unmap_guest_frame(ns_viomap, 0);
+ if ( ns_viomap )
+ {
+ ioport_80 = test_bit(0x80, ns_viomap);
+ ioport_ed = test_bit(0xed, ns_viomap);
+ hvm_unmap_guest_frame(ns_viomap, 0);
+ }
svm->ns_iomap = nestedhvm_vcpu_iomap_get(ioport_80, ioport_ed);
@@ -866,40 +868,45 @@ nsvm_vmcb_guest_intercepts_msr(unsigned
static int
nsvm_vmcb_guest_intercepts_ioio(paddr_t iopm_pa, uint64_t exitinfo1)
{
- unsigned long iopm_gfn = iopm_pa >> PAGE_SHIFT;
- unsigned long *io_bitmap = NULL;
+ unsigned long gfn = iopm_pa >> PAGE_SHIFT;
+ unsigned long *io_bitmap;
ioio_info_t ioinfo;
uint16_t port;
+ unsigned int size;
bool_t enabled;
- unsigned long gfn = 0; /* gcc ... */
ioinfo.bytes = exitinfo1;
port = ioinfo.fields.port;
+ size = ioinfo.fields.sz32 ? 4 : ioinfo.fields.sz16 ? 2 : 1;
- switch (port) {
- case 0 ... 32767: /* first 4KB page */
- gfn = iopm_gfn;
+ switch ( port )
+ {
+ case 0 ... 8 * PAGE_SIZE - 1: /* first 4KB page */
break;
- case 32768 ... 65535: /* second 4KB page */
- port -= 32768;
- gfn = iopm_gfn + 1;
+ case 8 * PAGE_SIZE ... 2 * 8 * PAGE_SIZE - 1: /* second 4KB page */
+ port -= 8 * PAGE_SIZE;
+ ++gfn;
break;
default:
BUG();
break;
}
- io_bitmap = hvm_map_guest_frame_ro(gfn, 0);
- if (io_bitmap == NULL) {
- gdprintk(XENLOG_ERR,
- "IOIO intercept: mapping of permission map failed\n");
- return NESTEDHVM_VMEXIT_ERROR;
+ for ( io_bitmap = hvm_map_guest_frame_ro(gfn, 0); ; )
+ {
+ enabled = test_bit(port, io_bitmap);
+ if ( !enabled || !--size )
+ break;
+ if ( unlikely(++port == 8 * PAGE_SIZE) )
+ {
+ hvm_unmap_guest_frame(io_bitmap, 0);
+ io_bitmap = hvm_map_guest_frame_ro(++gfn, 0);
+ port -= 8 * PAGE_SIZE;
+ }
}
-
- enabled = test_bit(port, io_bitmap);
hvm_unmap_guest_frame(io_bitmap, 0);
- if (!enabled)
+ if ( !enabled )
return NESTEDHVM_VMEXIT_HOST;
return NESTEDHVM_VMEXIT_INJECT;
@@ -966,8 +973,8 @@ nsvm_vmcb_guest_intercepts_exitcode(stru
switch (exitcode) {
case VMEXIT_MSR:
ASSERT(regs != NULL);
- nestedsvm_vmcb_map(v, nv->nv_vvmcxaddr);
- ASSERT(nv->nv_vvmcx != NULL);
+ if ( !nestedsvm_vmcb_map(v, nv->nv_vvmcxaddr) )
+ break;
ns_vmcb = nv->nv_vvmcx;
vmexits = nsvm_vmcb_guest_intercepts_msr(svm->ns_cached_msrpm,
regs->ecx, ns_vmcb->exitinfo1 != 0);
@@ -975,8 +982,8 @@ nsvm_vmcb_guest_intercepts_exitcode(stru
return 0;
break;
case VMEXIT_IOIO:
- nestedsvm_vmcb_map(v, nv->nv_vvmcxaddr);
- ASSERT(nv->nv_vvmcx != NULL);
+ if ( !nestedsvm_vmcb_map(v, nv->nv_vvmcxaddr) )
+ break;
ns_vmcb = nv->nv_vvmcx;
vmexits = nsvm_vmcb_guest_intercepts_ioio(ns_vmcb->_iopm_base_pa,
ns_vmcb->exitinfo1);
_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
http://lists.xen.org/xen-devel
Andrew Cooper
2013-Nov-11 11:25 UTC
Re: [PATCH v2] nested SVM: adjust guest handling of structure mappings
On 11/11/13 11:07, Jan Beulich wrote:> For one, nestedsvm_vmcb_map() error checking must not consist of using > assertions: Global (permanent) mappings can fail, and hence failure > needs to be dealt with properly. And non-global (transient) mappings > can''t fail anyway. > > And then the I/O port access bitmap handling was broken: It checked > only to first of the accessed ports rather than each of them. > > Signed-off-by: Jan Beulich <jbeulich@suse.com> > --- > v2: Refine the change to nsvm_vmrun_permissionmap() - > hvm_map_guest_frame_*() can return NULL for reasons other than > map_domain_page_global() doing so (pointed out by Andrew Cooper). > That leaves the function broken though (it ought to handle the > shared/paged out cases in an appropriate way). > > --- a/xen/arch/x86/hvm/svm/nestedsvm.c > +++ b/xen/arch/x86/hvm/svm/nestedsvm.c > @@ -342,7 +342,7 @@ static int nsvm_vmrun_permissionmap(stru > unsigned int i; > enum hvm_copy_result ret; > unsigned long *ns_viomap; > - bool_t ioport_80, ioport_ed; > + bool_t ioport_80 = 1, ioport_ed = 1; > > ns_msrpm_ptr = (unsigned long *)svm->ns_cached_msrpm; > > @@ -360,10 +360,12 @@ static int nsvm_vmrun_permissionmap(stru > svm->ns_iomap_pa = ns_vmcb->_iopm_base_pa; > > ns_viomap = hvm_map_guest_frame_ro(svm->ns_iomap_pa >> PAGE_SHIFT, 0); > - ASSERT(ns_viomap != NULL); > - ioport_80 = test_bit(0x80, ns_viomap); > - ioport_ed = test_bit(0xed, ns_viomap); > - hvm_unmap_guest_frame(ns_viomap, 0); > + if ( ns_viomap ) > + { > + ioport_80 = test_bit(0x80, ns_viomap); > + ioport_ed = test_bit(0xed, ns_viomap); > + hvm_unmap_guest_frame(ns_viomap, 0); > + }Should we not bail on an error here, similar to the failure of hvm_copy_from_guest just out of context above?> > svm->ns_iomap = nestedhvm_vcpu_iomap_get(ioport_80, ioport_ed); > > @@ -866,40 +868,45 @@ nsvm_vmcb_guest_intercepts_msr(unsigned > static int > nsvm_vmcb_guest_intercepts_ioio(paddr_t iopm_pa, uint64_t exitinfo1) > { > - unsigned long iopm_gfn = iopm_pa >> PAGE_SHIFT; > - unsigned long *io_bitmap = NULL; > + unsigned long gfn = iopm_pa >> PAGE_SHIFT; > + unsigned long *io_bitmap; > ioio_info_t ioinfo; > uint16_t port; > + unsigned int size; > bool_t enabled; > - unsigned long gfn = 0; /* gcc ... */ > > ioinfo.bytes = exitinfo1; > port = ioinfo.fields.port; > + size = ioinfo.fields.sz32 ? 4 : ioinfo.fields.sz16 ? 2 : 1; > > - switch (port) { > - case 0 ... 32767: /* first 4KB page */ > - gfn = iopm_gfn; > + switch ( port ) > + { > + case 0 ... 8 * PAGE_SIZE - 1: /* first 4KB page */ > break; > - case 32768 ... 65535: /* second 4KB page */ > - port -= 32768; > - gfn = iopm_gfn + 1; > + case 8 * PAGE_SIZE ... 2 * 8 * PAGE_SIZE - 1: /* second 4KB page */ > + port -= 8 * PAGE_SIZE; > + ++gfn; > break; > default: > BUG(); > break; > } > > - io_bitmap = hvm_map_guest_frame_ro(gfn, 0); > - if (io_bitmap == NULL) { > - gdprintk(XENLOG_ERR, > - "IOIO intercept: mapping of permission map failed\n"); > - return NESTEDHVM_VMEXIT_ERROR; > + for ( io_bitmap = hvm_map_guest_frame_ro(gfn, 0); ; )This needs further checking for NULL. ~Andrew> + { > + enabled = test_bit(port, io_bitmap); > + if ( !enabled || !--size ) > + break; > + if ( unlikely(++port == 8 * PAGE_SIZE) ) > + { > + hvm_unmap_guest_frame(io_bitmap, 0); > + io_bitmap = hvm_map_guest_frame_ro(++gfn, 0); > + port -= 8 * PAGE_SIZE; > + } > } > - > - enabled = test_bit(port, io_bitmap); > hvm_unmap_guest_frame(io_bitmap, 0); > > - if (!enabled) > + if ( !enabled ) > return NESTEDHVM_VMEXIT_HOST; > > return NESTEDHVM_VMEXIT_INJECT; > @@ -966,8 +973,8 @@ nsvm_vmcb_guest_intercepts_exitcode(stru > switch (exitcode) { > case VMEXIT_MSR: > ASSERT(regs != NULL); > - nestedsvm_vmcb_map(v, nv->nv_vvmcxaddr); > - ASSERT(nv->nv_vvmcx != NULL); > + if ( !nestedsvm_vmcb_map(v, nv->nv_vvmcxaddr) ) > + break; > ns_vmcb = nv->nv_vvmcx; > vmexits = nsvm_vmcb_guest_intercepts_msr(svm->ns_cached_msrpm, > regs->ecx, ns_vmcb->exitinfo1 != 0); > @@ -975,8 +982,8 @@ nsvm_vmcb_guest_intercepts_exitcode(stru > return 0; > break; > case VMEXIT_IOIO: > - nestedsvm_vmcb_map(v, nv->nv_vvmcxaddr); > - ASSERT(nv->nv_vvmcx != NULL); > + if ( !nestedsvm_vmcb_map(v, nv->nv_vvmcxaddr) ) > + break; > ns_vmcb = nv->nv_vvmcx; > vmexits = nsvm_vmcb_guest_intercepts_ioio(ns_vmcb->_iopm_base_pa, > ns_vmcb->exitinfo1); > > > > > _______________________________________________ > Xen-devel mailing list > Xen-devel@lists.xen.org > http://lists.xen.org/xen-devel_______________________________________________ Xen-devel mailing list Xen-devel@lists.xen.org http://lists.xen.org/xen-devel
Jan Beulich
2013-Nov-11 12:29 UTC
Re: [PATCH v2] nested SVM: adjust guest handling of structure mappings
>>> On 11.11.13 at 12:25, Andrew Cooper <andrew.cooper3@citrix.com> wrote: > On 11/11/13 11:07, Jan Beulich wrote: >> @@ -360,10 +360,12 @@ static int nsvm_vmrun_permissionmap(stru >> svm->ns_iomap_pa = ns_vmcb->_iopm_base_pa; >> >> ns_viomap = hvm_map_guest_frame_ro(svm->ns_iomap_pa >> PAGE_SHIFT, 0); >> - ASSERT(ns_viomap != NULL); >> - ioport_80 = test_bit(0x80, ns_viomap); >> - ioport_ed = test_bit(0xed, ns_viomap); >> - hvm_unmap_guest_frame(ns_viomap, 0); >> + if ( ns_viomap ) >> + { >> + ioport_80 = test_bit(0x80, ns_viomap); >> + ioport_ed = test_bit(0xed, ns_viomap); >> + hvm_unmap_guest_frame(ns_viomap, 0); >> + } > > Should we not bail on an error here, similar to the failure of > hvm_copy_from_guest just out of context above?It seemed less intrusive (to the guest) to simply disallow (direct) access to these ports in that case.>> - io_bitmap = hvm_map_guest_frame_ro(gfn, 0); >> - if (io_bitmap == NULL) { >> - gdprintk(XENLOG_ERR, >> - "IOIO intercept: mapping of permission map failed\n"); >> - return NESTEDHVM_VMEXIT_ERROR; >> + for ( io_bitmap = hvm_map_guest_frame_ro(gfn, 0); ; ) > > This needs further checking for NULL.Indeed, thanks for spotting. Jan
Jan Beulich
2013-Nov-11 12:53 UTC
[PATCH v3] nested SVM: adjust guest handling of structure mappings
For one, nestedsvm_vmcb_map() error checking must not consist of using
assertions: Global (permanent) mappings can fail, and hence failure
needs to be dealt with properly. And non-global (transient) mappings
can''t fail anyway.
And then the I/O port access bitmap handling was broken: It checked
only to first of the accessed ports rather than each of them.
Signed-off-by: Jan Beulich <jbeulich@suse.com>
---
v3: Add error check to mapping operation in
nsvm_vmcb_guest_intercepts_ioio() (pointed out by Andrew Cooper).
--- a/xen/arch/x86/hvm/svm/nestedsvm.c
+++ b/xen/arch/x86/hvm/svm/nestedsvm.c
@@ -342,7 +342,7 @@ static int nsvm_vmrun_permissionmap(stru
unsigned int i;
enum hvm_copy_result ret;
unsigned long *ns_viomap;
- bool_t ioport_80, ioport_ed;
+ bool_t ioport_80 = 1, ioport_ed = 1;
ns_msrpm_ptr = (unsigned long *)svm->ns_cached_msrpm;
@@ -360,10 +360,12 @@ static int nsvm_vmrun_permissionmap(stru
svm->ns_iomap_pa = ns_vmcb->_iopm_base_pa;
ns_viomap = hvm_map_guest_frame_ro(svm->ns_iomap_pa >> PAGE_SHIFT,
0);
- ASSERT(ns_viomap != NULL);
- ioport_80 = test_bit(0x80, ns_viomap);
- ioport_ed = test_bit(0xed, ns_viomap);
- hvm_unmap_guest_frame(ns_viomap, 0);
+ if ( ns_viomap )
+ {
+ ioport_80 = test_bit(0x80, ns_viomap);
+ ioport_ed = test_bit(0xed, ns_viomap);
+ hvm_unmap_guest_frame(ns_viomap, 0);
+ }
svm->ns_iomap = nestedhvm_vcpu_iomap_get(ioport_80, ioport_ed);
@@ -866,40 +868,45 @@ nsvm_vmcb_guest_intercepts_msr(unsigned
static int
nsvm_vmcb_guest_intercepts_ioio(paddr_t iopm_pa, uint64_t exitinfo1)
{
- unsigned long iopm_gfn = iopm_pa >> PAGE_SHIFT;
- unsigned long *io_bitmap = NULL;
+ unsigned long gfn = iopm_pa >> PAGE_SHIFT;
+ unsigned long *io_bitmap;
ioio_info_t ioinfo;
uint16_t port;
+ unsigned int size;
bool_t enabled;
- unsigned long gfn = 0; /* gcc ... */
ioinfo.bytes = exitinfo1;
port = ioinfo.fields.port;
+ size = ioinfo.fields.sz32 ? 4 : ioinfo.fields.sz16 ? 2 : 1;
- switch (port) {
- case 0 ... 32767: /* first 4KB page */
- gfn = iopm_gfn;
+ switch ( port )
+ {
+ case 0 ... 8 * PAGE_SIZE - 1: /* first 4KB page */
break;
- case 32768 ... 65535: /* second 4KB page */
- port -= 32768;
- gfn = iopm_gfn + 1;
+ case 8 * PAGE_SIZE ... 2 * 8 * PAGE_SIZE - 1: /* second 4KB page */
+ port -= 8 * PAGE_SIZE;
+ ++gfn;
break;
default:
BUG();
break;
}
- io_bitmap = hvm_map_guest_frame_ro(gfn, 0);
- if (io_bitmap == NULL) {
- gdprintk(XENLOG_ERR,
- "IOIO intercept: mapping of permission map failed\n");
- return NESTEDHVM_VMEXIT_ERROR;
+ for ( io_bitmap = hvm_map_guest_frame_ro(gfn, 0); ; )
+ {
+ enabled = io_bitmap && test_bit(port, io_bitmap);
+ if ( !enabled || !--size )
+ break;
+ if ( unlikely(++port == 8 * PAGE_SIZE) )
+ {
+ hvm_unmap_guest_frame(io_bitmap, 0);
+ io_bitmap = hvm_map_guest_frame_ro(++gfn, 0);
+ port -= 8 * PAGE_SIZE;
+ }
}
-
- enabled = test_bit(port, io_bitmap);
hvm_unmap_guest_frame(io_bitmap, 0);
- if (!enabled)
+ if ( !enabled )
return NESTEDHVM_VMEXIT_HOST;
return NESTEDHVM_VMEXIT_INJECT;
@@ -966,8 +973,8 @@ nsvm_vmcb_guest_intercepts_exitcode(stru
switch (exitcode) {
case VMEXIT_MSR:
ASSERT(regs != NULL);
- nestedsvm_vmcb_map(v, nv->nv_vvmcxaddr);
- ASSERT(nv->nv_vvmcx != NULL);
+ if ( !nestedsvm_vmcb_map(v, nv->nv_vvmcxaddr) )
+ break;
ns_vmcb = nv->nv_vvmcx;
vmexits = nsvm_vmcb_guest_intercepts_msr(svm->ns_cached_msrpm,
regs->ecx, ns_vmcb->exitinfo1 != 0);
@@ -975,8 +982,8 @@ nsvm_vmcb_guest_intercepts_exitcode(stru
return 0;
break;
case VMEXIT_IOIO:
- nestedsvm_vmcb_map(v, nv->nv_vvmcxaddr);
- ASSERT(nv->nv_vvmcx != NULL);
+ if ( !nestedsvm_vmcb_map(v, nv->nv_vvmcxaddr) )
+ break;
ns_vmcb = nv->nv_vvmcx;
vmexits = nsvm_vmcb_guest_intercepts_ioio(ns_vmcb->_iopm_base_pa,
ns_vmcb->exitinfo1);
_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
http://lists.xen.org/xen-devel
Andrew Cooper
2013-Nov-11 13:16 UTC
Re: [PATCH v3] nested SVM: adjust guest handling of structure mappings
On 11/11/13 12:53, Jan Beulich wrote:> For one, nestedsvm_vmcb_map() error checking must not consist of using > assertions: Global (permanent) mappings can fail, and hence failure > needs to be dealt with properly. And non-global (transient) mappings > can''t fail anyway. > > And then the I/O port access bitmap handling was broken: It checked > only to first of the accessed ports rather than each of them. > > Signed-off-by: Jan Beulich <jbeulich@suse.com> > --- > v3: Add error check to mapping operation in > nsvm_vmcb_guest_intercepts_ioio() (pointed out by Andrew Cooper). > > --- a/xen/arch/x86/hvm/svm/nestedsvm.c > +++ b/xen/arch/x86/hvm/svm/nestedsvm.c > @@ -342,7 +342,7 @@ static int nsvm_vmrun_permissionmap(stru > unsigned int i; > enum hvm_copy_result ret; > unsigned long *ns_viomap; > - bool_t ioport_80, ioport_ed; > + bool_t ioport_80 = 1, ioport_ed = 1; > > ns_msrpm_ptr = (unsigned long *)svm->ns_cached_msrpm; > > @@ -360,10 +360,12 @@ static int nsvm_vmrun_permissionmap(stru > svm->ns_iomap_pa = ns_vmcb->_iopm_base_pa; > > ns_viomap = hvm_map_guest_frame_ro(svm->ns_iomap_pa >> PAGE_SHIFT, 0); > - ASSERT(ns_viomap != NULL); > - ioport_80 = test_bit(0x80, ns_viomap); > - ioport_ed = test_bit(0xed, ns_viomap); > - hvm_unmap_guest_frame(ns_viomap, 0); > + if ( ns_viomap ) > + { > + ioport_80 = test_bit(0x80, ns_viomap); > + ioport_ed = test_bit(0xed, ns_viomap); > + hvm_unmap_guest_frame(ns_viomap, 0); > + } > > svm->ns_iomap = nestedhvm_vcpu_iomap_get(ioport_80, ioport_ed); > > @@ -866,40 +868,45 @@ nsvm_vmcb_guest_intercepts_msr(unsigned > static int > nsvm_vmcb_guest_intercepts_ioio(paddr_t iopm_pa, uint64_t exitinfo1) > { > - unsigned long iopm_gfn = iopm_pa >> PAGE_SHIFT; > - unsigned long *io_bitmap = NULL; > + unsigned long gfn = iopm_pa >> PAGE_SHIFT; > + unsigned long *io_bitmap; > ioio_info_t ioinfo; > uint16_t port; > + unsigned int size; > bool_t enabled; > - unsigned long gfn = 0; /* gcc ... */ > > ioinfo.bytes = exitinfo1; > port = ioinfo.fields.port; > + size = ioinfo.fields.sz32 ? 4 : ioinfo.fields.sz16 ? 2 : 1; > > - switch (port) { > - case 0 ... 32767: /* first 4KB page */ > - gfn = iopm_gfn; > + switch ( port ) > + { > + case 0 ... 8 * PAGE_SIZE - 1: /* first 4KB page */ > break; > - case 32768 ... 65535: /* second 4KB page */ > - port -= 32768; > - gfn = iopm_gfn + 1; > + case 8 * PAGE_SIZE ... 2 * 8 * PAGE_SIZE - 1: /* second 4KB page */ > + port -= 8 * PAGE_SIZE; > + ++gfn; > break; > default: > BUG(); > break; > } > > - io_bitmap = hvm_map_guest_frame_ro(gfn, 0); > - if (io_bitmap == NULL) { > - gdprintk(XENLOG_ERR, > - "IOIO intercept: mapping of permission map failed\n"); > - return NESTEDHVM_VMEXIT_ERROR; > + for ( io_bitmap = hvm_map_guest_frame_ro(gfn, 0); ; ) > + { > + enabled = io_bitmap && test_bit(port, io_bitmap); > + if ( !enabled || !--size ) > + break; > + if ( unlikely(++port == 8 * PAGE_SIZE) ) > + { > + hvm_unmap_guest_frame(io_bitmap, 0); > + io_bitmap = hvm_map_guest_frame_ro(++gfn, 0); > + port -= 8 * PAGE_SIZE; > + } > }Ok - this safe now, but I don''t understand the reasoning for introducing this loop? The ioio exit value gives us a single port, and the size of access on that specific port. The switch statement tells us exactly which gfn the relevant bit refers to, surely a single hvm_map_guest_frame_ro() is sufficient? ~Andrew> - > - enabled = test_bit(port, io_bitmap); > hvm_unmap_guest_frame(io_bitmap, 0); > > - if (!enabled) > + if ( !enabled ) > return NESTEDHVM_VMEXIT_HOST; > > return NESTEDHVM_VMEXIT_INJECT; > @@ -966,8 +973,8 @@ nsvm_vmcb_guest_intercepts_exitcode(stru > switch (exitcode) { > case VMEXIT_MSR: > ASSERT(regs != NULL); > - nestedsvm_vmcb_map(v, nv->nv_vvmcxaddr); > - ASSERT(nv->nv_vvmcx != NULL); > + if ( !nestedsvm_vmcb_map(v, nv->nv_vvmcxaddr) ) > + break; > ns_vmcb = nv->nv_vvmcx; > vmexits = nsvm_vmcb_guest_intercepts_msr(svm->ns_cached_msrpm, > regs->ecx, ns_vmcb->exitinfo1 != 0); > @@ -975,8 +982,8 @@ nsvm_vmcb_guest_intercepts_exitcode(stru > return 0; > break; > case VMEXIT_IOIO: > - nestedsvm_vmcb_map(v, nv->nv_vvmcxaddr); > - ASSERT(nv->nv_vvmcx != NULL); > + if ( !nestedsvm_vmcb_map(v, nv->nv_vvmcxaddr) ) > + break; > ns_vmcb = nv->nv_vvmcx; > vmexits = nsvm_vmcb_guest_intercepts_ioio(ns_vmcb->_iopm_base_pa, > ns_vmcb->exitinfo1); > >
Egger, Christoph
2013-Nov-11 13:22 UTC
Re: [PATCH v3] nested SVM: adjust guest handling of structure mappings
On 11.11.13 13:53, Jan Beulich wrote:> For one, nestedsvm_vmcb_map() error checking must not consist of using > assertions: Global (permanent) mappings can fail, and hence failure > needs to be dealt with properly. And non-global (transient) mappings > can''t fail anyway. > > And then the I/O port access bitmap handling was broken: It checked > only to first of the accessed ports rather than each of them. > > Signed-off-by: Jan Beulich <jbeulich@suse.com> > --- > v3: Add error check to mapping operation in > nsvm_vmcb_guest_intercepts_ioio() (pointed out by Andrew Cooper).Reviewed-by: Christoph Egger <chegger@amazon.de> <offtopic to the context of this patch> A comment to the MSR algorithm: The performance of the MSR bitmap merging can be greatly improved. By default Xen does intercept MSRs with a few exceptions. Cache those few exceptions and for the nested bitmap only merge the bits from the guest bitmap with those few ones. All other bits are 1 in any case. That shaves off a huge memory footprint from each VMRUN emulation. </offtopic>> > --- a/xen/arch/x86/hvm/svm/nestedsvm.c > +++ b/xen/arch/x86/hvm/svm/nestedsvm.c > @@ -342,7 +342,7 @@ static int nsvm_vmrun_permissionmap(stru > unsigned int i; > enum hvm_copy_result ret; > unsigned long *ns_viomap; > - bool_t ioport_80, ioport_ed; > + bool_t ioport_80 = 1, ioport_ed = 1; > > ns_msrpm_ptr = (unsigned long *)svm->ns_cached_msrpm; > > @@ -360,10 +360,12 @@ static int nsvm_vmrun_permissionmap(stru > svm->ns_iomap_pa = ns_vmcb->_iopm_base_pa; > > ns_viomap = hvm_map_guest_frame_ro(svm->ns_iomap_pa >> PAGE_SHIFT, 0); > - ASSERT(ns_viomap != NULL); > - ioport_80 = test_bit(0x80, ns_viomap); > - ioport_ed = test_bit(0xed, ns_viomap); > - hvm_unmap_guest_frame(ns_viomap, 0); > + if ( ns_viomap ) > + { > + ioport_80 = test_bit(0x80, ns_viomap); > + ioport_ed = test_bit(0xed, ns_viomap); > + hvm_unmap_guest_frame(ns_viomap, 0); > + } > > svm->ns_iomap = nestedhvm_vcpu_iomap_get(ioport_80, ioport_ed); > > @@ -866,40 +868,45 @@ nsvm_vmcb_guest_intercepts_msr(unsigned > static int > nsvm_vmcb_guest_intercepts_ioio(paddr_t iopm_pa, uint64_t exitinfo1) > { > - unsigned long iopm_gfn = iopm_pa >> PAGE_SHIFT; > - unsigned long *io_bitmap = NULL; > + unsigned long gfn = iopm_pa >> PAGE_SHIFT; > + unsigned long *io_bitmap; > ioio_info_t ioinfo; > uint16_t port; > + unsigned int size; > bool_t enabled; > - unsigned long gfn = 0; /* gcc ... */ > > ioinfo.bytes = exitinfo1; > port = ioinfo.fields.port; > + size = ioinfo.fields.sz32 ? 4 : ioinfo.fields.sz16 ? 2 : 1; > > - switch (port) { > - case 0 ... 32767: /* first 4KB page */ > - gfn = iopm_gfn; > + switch ( port ) > + { > + case 0 ... 8 * PAGE_SIZE - 1: /* first 4KB page */ > break; > - case 32768 ... 65535: /* second 4KB page */ > - port -= 32768; > - gfn = iopm_gfn + 1; > + case 8 * PAGE_SIZE ... 2 * 8 * PAGE_SIZE - 1: /* second 4KB page */ > + port -= 8 * PAGE_SIZE; > + ++gfn; > break; > default: > BUG(); > break; > } > > - io_bitmap = hvm_map_guest_frame_ro(gfn, 0); > - if (io_bitmap == NULL) { > - gdprintk(XENLOG_ERR, > - "IOIO intercept: mapping of permission map failed\n"); > - return NESTEDHVM_VMEXIT_ERROR; > + for ( io_bitmap = hvm_map_guest_frame_ro(gfn, 0); ; ) > + { > + enabled = io_bitmap && test_bit(port, io_bitmap); > + if ( !enabled || !--size ) > + break; > + if ( unlikely(++port == 8 * PAGE_SIZE) ) > + { > + hvm_unmap_guest_frame(io_bitmap, 0); > + io_bitmap = hvm_map_guest_frame_ro(++gfn, 0); > + port -= 8 * PAGE_SIZE; > + } > } > - > - enabled = test_bit(port, io_bitmap); > hvm_unmap_guest_frame(io_bitmap, 0); > > - if (!enabled) > + if ( !enabled ) > return NESTEDHVM_VMEXIT_HOST; > > return NESTEDHVM_VMEXIT_INJECT; > @@ -966,8 +973,8 @@ nsvm_vmcb_guest_intercepts_exitcode(stru > switch (exitcode) { > case VMEXIT_MSR: > ASSERT(regs != NULL); > - nestedsvm_vmcb_map(v, nv->nv_vvmcxaddr); > - ASSERT(nv->nv_vvmcx != NULL); > + if ( !nestedsvm_vmcb_map(v, nv->nv_vvmcxaddr) ) > + break; > ns_vmcb = nv->nv_vvmcx; > vmexits = nsvm_vmcb_guest_intercepts_msr(svm->ns_cached_msrpm, > regs->ecx, ns_vmcb->exitinfo1 != 0); > @@ -975,8 +982,8 @@ nsvm_vmcb_guest_intercepts_exitcode(stru > return 0; > break; > case VMEXIT_IOIO: > - nestedsvm_vmcb_map(v, nv->nv_vvmcxaddr); > - ASSERT(nv->nv_vvmcx != NULL); > + if ( !nestedsvm_vmcb_map(v, nv->nv_vvmcxaddr) ) > + break; > ns_vmcb = nv->nv_vvmcx; > vmexits = nsvm_vmcb_guest_intercepts_ioio(ns_vmcb->_iopm_base_pa, > ns_vmcb->exitinfo1); > >
Jan Beulich
2013-Nov-11 13:35 UTC
Re: [PATCH v3] nested SVM: adjust guest handling of structure mappings
>>> On 11.11.13 at 14:16, Andrew Cooper <andrew.cooper3@citrix.com> wrote: > On 11/11/13 12:53, Jan Beulich wrote: >> + for ( io_bitmap = hvm_map_guest_frame_ro(gfn, 0); ; ) >> + { >> + enabled = io_bitmap && test_bit(port, io_bitmap); >> + if ( !enabled || !--size ) >> + break; >> + if ( unlikely(++port == 8 * PAGE_SIZE) ) >> + { >> + hvm_unmap_guest_frame(io_bitmap, 0); >> + io_bitmap = hvm_map_guest_frame_ro(++gfn, 0); >> + port -= 8 * PAGE_SIZE; >> + } >> } > > Ok - this safe now, but I don''t understand the reasoning for introducing > this loop? > > The ioio exit value gives us a single port, and the size of access on > that specific port. > > The switch statement tells us exactly which gfn the relevant bit refers > to, surely a single hvm_map_guest_frame_ro() is sufficient?When the operation spans multiple ports (INW, INL, etc), multiple bits need to be looked at. And when the access is misaligned and crosses a 32k (port number) boundary, more than one page needs looking at. Jan
Andrew Cooper
2013-Nov-11 14:40 UTC
Re: [PATCH v3] nested SVM: adjust guest handling of structure mappings
On 11/11/13 13:35, Jan Beulich wrote:>>>> On 11.11.13 at 14:16, Andrew Cooper <andrew.cooper3@citrix.com> wrote: >> On 11/11/13 12:53, Jan Beulich wrote: >>> + for ( io_bitmap = hvm_map_guest_frame_ro(gfn, 0); ; ) >>> + { >>> + enabled = io_bitmap && test_bit(port, io_bitmap); >>> + if ( !enabled || !--size ) >>> + break; >>> + if ( unlikely(++port == 8 * PAGE_SIZE) ) >>> + { >>> + hvm_unmap_guest_frame(io_bitmap, 0); >>> + io_bitmap = hvm_map_guest_frame_ro(++gfn, 0); >>> + port -= 8 * PAGE_SIZE; >>> + } >>> } >> Ok - this safe now, but I don''t understand the reasoning for introducing >> this loop? >> >> The ioio exit value gives us a single port, and the size of access on >> that specific port. >> >> The switch statement tells us exactly which gfn the relevant bit refers >> to, surely a single hvm_map_guest_frame_ro() is sufficient? > When the operation spans multiple ports (INW, INL, etc), multiple > bits need to be looked at. And when the access is misaligned and > crosses a 32k (port number) boundary, more than one page needs > looking at. > > Jan >Ah of course. Reviewed-by: Andrew Cooper <andrew.cooper3@citrix.com>
Suravee Suthikulpanit
2013-Nov-12 03:57 UTC
Re: [PATCH v3] nested SVM: adjust guest handling of structure mappings
I am on travel until Dec 6th. So, I don''t have machine that I can test this on. But it looks ok. Acked-by: Suravee Suthikulpanit <suravee.suthikulpanit@amd.com> Suravee On 11/11/2013 07:53 PM, Jan Beulich wrote:> For one, nestedsvm_vmcb_map() error checking must not consist of using > assertions: Global (permanent) mappings can fail, and hence failure > needs to be dealt with properly. And non-global (transient) mappings > can''t fail anyway. > > And then the I/O port access bitmap handling was broken: It checked > only to first of the accessed ports rather than each of them. > > Signed-off-by: Jan Beulich <jbeulich@suse.com> > --- > v3: Add error check to mapping operation in > nsvm_vmcb_guest_intercepts_ioio() (pointed out by Andrew Cooper). > > --- a/xen/arch/x86/hvm/svm/nestedsvm.c > +++ b/xen/arch/x86/hvm/svm/nestedsvm.c > @@ -342,7 +342,7 @@ static int nsvm_vmrun_permissionmap(stru > unsigned int i; > enum hvm_copy_result ret; > unsigned long *ns_viomap; > - bool_t ioport_80, ioport_ed; > + bool_t ioport_80 = 1, ioport_ed = 1; > > ns_msrpm_ptr = (unsigned long *)svm->ns_cached_msrpm; > > @@ -360,10 +360,12 @@ static int nsvm_vmrun_permissionmap(stru > svm->ns_iomap_pa = ns_vmcb->_iopm_base_pa; > > ns_viomap = hvm_map_guest_frame_ro(svm->ns_iomap_pa >> PAGE_SHIFT, 0); > - ASSERT(ns_viomap != NULL); > - ioport_80 = test_bit(0x80, ns_viomap); > - ioport_ed = test_bit(0xed, ns_viomap); > - hvm_unmap_guest_frame(ns_viomap, 0); > + if ( ns_viomap ) > + { > + ioport_80 = test_bit(0x80, ns_viomap); > + ioport_ed = test_bit(0xed, ns_viomap); > + hvm_unmap_guest_frame(ns_viomap, 0); > + } > > svm->ns_iomap = nestedhvm_vcpu_iomap_get(ioport_80, ioport_ed); > > @@ -866,40 +868,45 @@ nsvm_vmcb_guest_intercepts_msr(unsigned > static int > nsvm_vmcb_guest_intercepts_ioio(paddr_t iopm_pa, uint64_t exitinfo1) > { > - unsigned long iopm_gfn = iopm_pa >> PAGE_SHIFT; > - unsigned long *io_bitmap = NULL; > + unsigned long gfn = iopm_pa >> PAGE_SHIFT; > + unsigned long *io_bitmap; > ioio_info_t ioinfo; > uint16_t port; > + unsigned int size; > bool_t enabled; > - unsigned long gfn = 0; /* gcc ... */ > > ioinfo.bytes = exitinfo1; > port = ioinfo.fields.port; > + size = ioinfo.fields.sz32 ? 4 : ioinfo.fields.sz16 ? 2 : 1; > > - switch (port) { > - case 0 ... 32767: /* first 4KB page */ > - gfn = iopm_gfn; > + switch ( port ) > + { > + case 0 ... 8 * PAGE_SIZE - 1: /* first 4KB page */ > break; > - case 32768 ... 65535: /* second 4KB page */ > - port -= 32768; > - gfn = iopm_gfn + 1; > + case 8 * PAGE_SIZE ... 2 * 8 * PAGE_SIZE - 1: /* second 4KB page */ > + port -= 8 * PAGE_SIZE; > + ++gfn; > break; > default: > BUG(); > break; > } > > - io_bitmap = hvm_map_guest_frame_ro(gfn, 0); > - if (io_bitmap == NULL) { > - gdprintk(XENLOG_ERR, > - "IOIO intercept: mapping of permission map failed\n"); > - return NESTEDHVM_VMEXIT_ERROR; > + for ( io_bitmap = hvm_map_guest_frame_ro(gfn, 0); ; ) > + { > + enabled = io_bitmap && test_bit(port, io_bitmap); > + if ( !enabled || !--size ) > + break; > + if ( unlikely(++port == 8 * PAGE_SIZE) ) > + { > + hvm_unmap_guest_frame(io_bitmap, 0); > + io_bitmap = hvm_map_guest_frame_ro(++gfn, 0); > + port -= 8 * PAGE_SIZE; > + } > } > - > - enabled = test_bit(port, io_bitmap); > hvm_unmap_guest_frame(io_bitmap, 0); > > - if (!enabled) > + if ( !enabled ) > return NESTEDHVM_VMEXIT_HOST; > > return NESTEDHVM_VMEXIT_INJECT; > @@ -966,8 +973,8 @@ nsvm_vmcb_guest_intercepts_exitcode(stru > switch (exitcode) { > case VMEXIT_MSR: > ASSERT(regs != NULL); > - nestedsvm_vmcb_map(v, nv->nv_vvmcxaddr); > - ASSERT(nv->nv_vvmcx != NULL); > + if ( !nestedsvm_vmcb_map(v, nv->nv_vvmcxaddr) ) > + break; > ns_vmcb = nv->nv_vvmcx; > vmexits = nsvm_vmcb_guest_intercepts_msr(svm->ns_cached_msrpm, > regs->ecx, ns_vmcb->exitinfo1 != 0); > @@ -975,8 +982,8 @@ nsvm_vmcb_guest_intercepts_exitcode(stru > return 0; > break; > case VMEXIT_IOIO: > - nestedsvm_vmcb_map(v, nv->nv_vvmcxaddr); > - ASSERT(nv->nv_vvmcx != NULL); > + if ( !nestedsvm_vmcb_map(v, nv->nv_vvmcxaddr) ) > + break; > ns_vmcb = nv->nv_vvmcx; > vmexits = nsvm_vmcb_guest_intercepts_ioio(ns_vmcb->_iopm_base_pa, > ns_vmcb->exitinfo1); > >