diff mbox series

x86/hvm: Propagate real error information up through hvm_load()

Message ID 20210719111449.21337-1-andrew.cooper3@citrix.com (mailing list archive)
State New, archived
Headers show
Series x86/hvm: Propagate real error information up through hvm_load() | expand

Commit Message

Andrew Cooper July 19, 2021, 11:14 a.m. UTC
hvm_load() is currently a mix of -errno and -1 style error handling, which
aliases -EPERM.  This leads to the following confusing diagnostics:

From userspace:
  xc: info: Restoring domain
  xc: error: Unable to restore HVM context (1 = Operation not permitted): Internal error
  xc: error: Restore failed (1 = Operation not permitted): Internal error
  xc_domain_restore: [1] Restore failed (1 = Operation not permitted)

From Xen:
  (XEN) HVM10.0 restore: inconsistent xsave state (feat=0x2ff accum=0x21f xcr0=0x7 bv=0x3 err=-22)
  (XEN) HVM10 restore: failed to load entry 16/0

The actual error was a bad backport, but the -EINVAL got converted to -EPERM
on the way out of the hypercall.

The overwhelming majority of *_load() handlers already use -errno consistenty.
Fix up the rest to be consistent, and fix a few other errors noticed along the
way.

 * Failures of hvm_load_entry() indicate a truncated record or other bad data
   size.  Use -ENODATA.
 * Don't use {g,}dprintk().  Omitting diagnostics in release builds is rude,
   and almost everything uses unconditional printk()'s.
 * Switch some errors for more appropriate ones.

Reported-by: Igor Druzhinin <igor.druzhinin@citrix.com>
Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com>
---
CC: Jan Beulich <JBeulich@suse.com>
CC: Roger Pau Monné <roger.pau@citrix.com>
CC: Wei Liu <wl@xen.org>
CC: Igor Druzhinin <igor.druzhinin@citrix.com>
---
 xen/arch/x86/cpu/mcheck/vmce.c |  6 +++---
 xen/arch/x86/emul-i8254.c      |  9 +++++----
 xen/arch/x86/hvm/irq.c         |  6 +++---
 xen/arch/x86/hvm/save.c        | 25 ++++++++++++++-----------
 xen/arch/x86/hvm/vioapic.c     |  5 ++++-
 xen/arch/x86/hvm/vpic.c        |  2 +-
 6 files changed, 30 insertions(+), 23 deletions(-)

Comments

Jan Beulich July 19, 2021, 12:46 p.m. UTC | #1
On 19.07.2021 13:14, Andrew Cooper wrote:
> hvm_load() is currently a mix of -errno and -1 style error handling, which
> aliases -EPERM.  This leads to the following confusing diagnostics:
> 
> From userspace:
>   xc: info: Restoring domain
>   xc: error: Unable to restore HVM context (1 = Operation not permitted): Internal error
>   xc: error: Restore failed (1 = Operation not permitted): Internal error
>   xc_domain_restore: [1] Restore failed (1 = Operation not permitted)
> 
> From Xen:
>   (XEN) HVM10.0 restore: inconsistent xsave state (feat=0x2ff accum=0x21f xcr0=0x7 bv=0x3 err=-22)
>   (XEN) HVM10 restore: failed to load entry 16/0
> 
> The actual error was a bad backport, but the -EINVAL got converted to -EPERM
> on the way out of the hypercall.
> 
> The overwhelming majority of *_load() handlers already use -errno consistenty.
> Fix up the rest to be consistent, and fix a few other errors noticed along the
> way.
> 
>  * Failures of hvm_load_entry() indicate a truncated record or other bad data
>    size.  Use -ENODATA.

But then ...

> @@ -421,8 +421,8 @@ static int pit_load(struct domain *d, hvm_domain_context_t *h)
>  
>      if ( hvm_load_entry(PIT, h, &pit->hw) )
>      {
> -        spin_unlock(&pit->lock);
> -        return 1;
> +        rc = -ENODEV;
> +        goto out;
>      }

... use -ENODATA here as well? Preferably with the adjustment
Reviewed-by: Jan Beulich <jbeulich@suse.com>

I'll pick this up for backporting once I see it in the tree.

Jan
Andrew Cooper July 19, 2021, 12:56 p.m. UTC | #2
On 19/07/2021 13:46, Jan Beulich wrote:
> On 19.07.2021 13:14, Andrew Cooper wrote:
>> hvm_load() is currently a mix of -errno and -1 style error handling, which
>> aliases -EPERM.  This leads to the following confusing diagnostics:
>>
>> From userspace:
>>   xc: info: Restoring domain
>>   xc: error: Unable to restore HVM context (1 = Operation not permitted): Internal error
>>   xc: error: Restore failed (1 = Operation not permitted): Internal error
>>   xc_domain_restore: [1] Restore failed (1 = Operation not permitted)
>>
>> From Xen:
>>   (XEN) HVM10.0 restore: inconsistent xsave state (feat=0x2ff accum=0x21f xcr0=0x7 bv=0x3 err=-22)
>>   (XEN) HVM10 restore: failed to load entry 16/0
>>
>> The actual error was a bad backport, but the -EINVAL got converted to -EPERM
>> on the way out of the hypercall.
>>
>> The overwhelming majority of *_load() handlers already use -errno consistenty.
>> Fix up the rest to be consistent, and fix a few other errors noticed along the
>> way.
>>
>>  * Failures of hvm_load_entry() indicate a truncated record or other bad data
>>    size.  Use -ENODATA.
> But then ...
>
>> @@ -421,8 +421,8 @@ static int pit_load(struct domain *d, hvm_domain_context_t *h)
>>  
>>      if ( hvm_load_entry(PIT, h, &pit->hw) )
>>      {
>> -        spin_unlock(&pit->lock);
>> -        return 1;
>> +        rc = -ENODEV;
>> +        goto out;
>>      }
> ... use -ENODATA here as well?

Hmm - that was intended to be ENODATA.  Will fix.

>  Preferably with the adjustment
> Reviewed-by: Jan Beulich <jbeulich@suse.com>

Thanks,

> I'll pick this up for backporting once I see it in the tree.

I don't know how much the call tree has changed over time.  Every
handler will need a quick check on each release.

~Andrew
Andrew Cooper July 19, 2021, 1:22 p.m. UTC | #3
On 19/07/2021 13:46, Jan Beulich wrote:
> On 19.07.2021 13:14, Andrew Cooper wrote:
>> hvm_load() is currently a mix of -errno and -1 style error handling,
>> which
>> aliases -EPERM. This leads to the following confusing diagnostics:
>>
>> From userspace:
>> xc: info: Restoring domain
>> xc: error: Unable to restore HVM context (1 = Operation not
>> permitted): Internal error
>> xc: error: Restore failed (1 = Operation not permitted): Internal error
>> xc_domain_restore: [1] Restore failed (1 = Operation not permitted)
>>
>> From Xen:
>> (XEN) HVM10.0 restore: inconsistent xsave state (feat=0x2ff
>> accum=0x21f xcr0=0x7 bv=0x3 err=-22)
>> (XEN) HVM10 restore: failed to load entry 16/0
>>
>> The actual error was a bad backport, but the -EINVAL got converted to
>> -EPERM
>> on the way out of the hypercall.
>>
>> The overwhelming majority of *_load() handlers already use -errno
>> consistenty.
>> Fix up the rest to be consistent, and fix a few other errors noticed
>> along the
>> way.
>>
>> * Failures of hvm_load_entry() indicate a truncated record or other
>> bad data
>> size. Use -ENODATA.
> But then ...
>
>> @@ -421,8 +421,8 @@ static int pit_load(struct domain *d,
>> hvm_domain_context_t *h)
>> if ( hvm_load_entry(PIT, h, &pit->hw) )
>> {
>> - spin_unlock(&pit->lock);
>> - return 1;
>> + rc = -ENODEV;
>> + goto out;
>> }
> ... use -ENODATA here as well?

Hmm - that was intended to be ENODATA.  Will fix.

> Preferably with the adjustment
> Reviewed-by: Jan Beulich <jbeulich@suse.com>

Thanks,

> I'll pick this up for backporting once I see it in the tree.

I don't know how much the call tree has changed over time.  Every
handler will need a quick check on each release.

~Andrew
diff mbox series

Patch

diff --git a/xen/arch/x86/cpu/mcheck/vmce.c b/xen/arch/x86/cpu/mcheck/vmce.c
index b1df9e9efd66..eb6434a3ba20 100644
--- a/xen/arch/x86/cpu/mcheck/vmce.c
+++ b/xen/arch/x86/cpu/mcheck/vmce.c
@@ -82,11 +82,11 @@  int vmce_restore_vcpu(struct vcpu *v, const struct hvm_vmce_vcpu *ctxt)
 
     if ( ctxt->caps & ~guest_mcg_cap & ~MCG_CAP_COUNT & ~MCG_CTL_P )
     {
-        dprintk(XENLOG_G_ERR, "%s restore: unsupported MCA capabilities"
-                " %#" PRIx64 " for %pv (supported: %#Lx)\n",
+        printk(XENLOG_G_ERR
+               "%s restore: unsupported MCA capabilities %#"PRIx64" for %pv (supported: %#Lx)\n",
                 is_hvm_vcpu(v) ? "HVM" : "PV", ctxt->caps,
                 v, guest_mcg_cap & ~MCG_CAP_COUNT);
-        return -EPERM;
+        return -EINVAL;
     }
 
     v->arch.vmce.mcg_cap = ctxt->caps;
diff --git a/xen/arch/x86/emul-i8254.c b/xen/arch/x86/emul-i8254.c
index 73be4188ad41..83d7156799c8 100644
--- a/xen/arch/x86/emul-i8254.c
+++ b/xen/arch/x86/emul-i8254.c
@@ -412,7 +412,7 @@  static int pit_save(struct vcpu *v, hvm_domain_context_t *h)
 static int pit_load(struct domain *d, hvm_domain_context_t *h)
 {
     PITState *pit = domain_vpit(d);
-    int i;
+    int i, rc = 0;
 
     if ( !has_vpit(d) )
         return -ENODEV;
@@ -421,8 +421,8 @@  static int pit_load(struct domain *d, hvm_domain_context_t *h)
 
     if ( hvm_load_entry(PIT, h, &pit->hw) )
     {
-        spin_unlock(&pit->lock);
-        return 1;
+        rc = -ENODEV;
+        goto out;
     }
     
     /*
@@ -434,9 +434,10 @@  static int pit_load(struct domain *d, hvm_domain_context_t *h)
     for ( i = 0; i < 3; i++ )
         pit_load_count(pit, i, pit->hw.channels[i].count);
 
+ out:
     spin_unlock(&pit->lock);
 
-    return 0;
+    return rc;
 }
 
 HVM_REGISTER_SAVE_RESTORE(PIT, pit_save, pit_load, 1, HVMSR_PER_DOM);
diff --git a/xen/arch/x86/hvm/irq.c b/xen/arch/x86/hvm/irq.c
index 38ac5fb6c7c2..52aae4565f0c 100644
--- a/xen/arch/x86/hvm/irq.c
+++ b/xen/arch/x86/hvm/irq.c
@@ -773,9 +773,9 @@  static int irq_load_link(struct domain *d, hvm_domain_context_t *h)
     for ( link = 0; link < 4; link++ )
         if ( hvm_irq->pci_link.route[link] > 15 )
         {
-            gdprintk(XENLOG_ERR, 
-                     "HVM restore: PCI-ISA link %u out of range (%u)\n",
-                     link, hvm_irq->pci_link.route[link]);
+            printk(XENLOG_G_ERR
+                   "HVM restore: PCI-ISA link %u out of range (%u)\n",
+                   link, hvm_irq->pci_link.route[link]);
             return -EINVAL;
         }
 
diff --git a/xen/arch/x86/hvm/save.c b/xen/arch/x86/hvm/save.c
index 584620985bf5..86c82cbd7456 100644
--- a/xen/arch/x86/hvm/save.c
+++ b/xen/arch/x86/hvm/save.c
@@ -51,14 +51,14 @@  int arch_hvm_load(struct domain *d, struct hvm_save_header *hdr)
     {
         printk(XENLOG_G_ERR "HVM%d restore: bad magic number %#"PRIx32"\n",
                d->domain_id, hdr->magic);
-        return -1;
+        return -EINVAL;
     }
 
     if ( hdr->version != HVM_FILE_VERSION )
     {
         printk(XENLOG_G_ERR "HVM%d restore: unsupported version %u\n",
                d->domain_id, hdr->version);
-        return -1;
+        return -EINVAL;
     }
 
     cpuid(1, &eax, &ebx, &ecx, &edx);
@@ -294,16 +294,18 @@  int hvm_load(struct domain *d, hvm_domain_context_t *h)
     struct hvm_save_descriptor *desc;
     hvm_load_handler handler;
     struct vcpu *v;
+    int rc;
 
     if ( d->is_dying )
         return -EINVAL;
 
     /* Read the save header, which must be first */
     if ( hvm_load_entry(HEADER, h, &hdr) != 0 )
-        return -1;
+        return -ENODATA;
 
-    if ( arch_hvm_load(d, &hdr) )
-        return -1;
+    rc = arch_hvm_load(d, &hdr);
+    if ( rc )
+        return rc;
 
     /* Down all the vcpus: we only re-enable the ones that had state saved. */
     for_each_vcpu(d, v)
@@ -318,7 +320,7 @@  int hvm_load(struct domain *d, hvm_domain_context_t *h)
             printk(XENLOG_G_ERR
                    "HVM%d restore: save did not end with a null entry\n",
                    d->domain_id);
-            return -1;
+            return -ENODATA;
         }
 
         /* Read the typecode of the next entry  and check for the end-marker */
@@ -332,17 +334,18 @@  int hvm_load(struct domain *d, hvm_domain_context_t *h)
         {
             printk(XENLOG_G_ERR "HVM%d restore: unknown entry typecode %u\n",
                    d->domain_id, desc->typecode);
-            return -1;
+            return -EINVAL;
         }
 
         /* Load the entry */
         printk(XENLOG_G_INFO "HVM%d restore: %s %"PRIu16"\n", d->domain_id,
                hvm_sr_handlers[desc->typecode].name, desc->instance);
-        if ( handler(d, h) != 0 )
+        rc = handler(d, h);
+        if ( rc )
         {
-            printk(XENLOG_G_ERR "HVM%d restore: failed to load entry %u/%u\n",
-                   d->domain_id, desc->typecode, desc->instance);
-            return -1;
+            printk(XENLOG_G_ERR "HVM%d restore: failed to load entry %u/%u rc %d\n",
+                   d->domain_id, desc->typecode, desc->instance, rc);
+            return rc;
         }
         process_pending_softirqs();
     }
diff --git a/xen/arch/x86/hvm/vioapic.c b/xen/arch/x86/hvm/vioapic.c
index 87370dd4172b..553c0f76eff8 100644
--- a/xen/arch/x86/hvm/vioapic.c
+++ b/xen/arch/x86/hvm/vioapic.c
@@ -620,7 +620,10 @@  static int ioapic_load(struct domain *d, hvm_domain_context_t *h)
          d->arch.hvm.nr_vioapics != 1 )
         return -EOPNOTSUPP;
 
-    return hvm_load_entry(IOAPIC, h, &s->domU);
+    if ( hvm_load_entry(IOAPIC, h, &s->domU) )
+        return -ENODATA;
+
+    return 0;
 }
 
 HVM_REGISTER_SAVE_RESTORE(IOAPIC, ioapic_save, ioapic_load, 1, HVMSR_PER_DOM);
diff --git a/xen/arch/x86/hvm/vpic.c b/xen/arch/x86/hvm/vpic.c
index f465b7f9979a..af988a868c8a 100644
--- a/xen/arch/x86/hvm/vpic.c
+++ b/xen/arch/x86/hvm/vpic.c
@@ -430,7 +430,7 @@  static int vpic_load(struct domain *d, hvm_domain_context_t *h)
 
     /* Which PIC is this? */
     if ( inst > 1 )
-        return -EINVAL;
+        return -ENOENT;
     s = &d->arch.hvm.vpic[inst];
 
     /* Load the state */