Thread (34 messages) 34 messages, 5 authors, 2013-01-16

[kvmarm] [PATCH v5 04/12] ARM: KVM: Initial VGIC infrastructure code

From: Alexander Graf <hidden>
Date: 2013-01-14 21:28:52
Also in: kvm


Am 14.01.2013 um 22:08 schrieb Christoffer Dall [off-list ref]:
On Mon, Jan 14, 2013 at 10:31 AM, Will Deacon [off-list ref] wrote:
quoted
On Tue, Jan 08, 2013 at 06:41:51PM +0000, Christoffer Dall wrote:
quoted
From: Marc Zyngier <redacted>

Wire the basic framework code for VGIC support and the initial in-kernel
MMIO support code for the VGIC, used for the distributor emulation.
[...]
quoted
+/**
+ * vgic_reg_access - access vgic register
+ * @mmio:   pointer to the data describing the mmio access
+ * @reg:    pointer to the virtual backing of vgic distributor data
+ * @offset: least significant 2 bits used for word offset
+ * @mode:   ACCESS_ mode (see defines above)
+ *
+ * Helper to make vgic register access easier using one of the access
+ * modes defined for vgic register access
+ * (read,raz,write-ignored,setbit,clearbit,write)
+ */
+static void vgic_reg_access(struct kvm_exit_mmio *mmio, u32 *reg,
+                           phys_addr_t offset, int mode)
+{
+       int shift = (offset & 3) * 8;
+       u32 mask;
+       u32 regval;
+
+       /*
+        * Any alignment fault should have been delivered to the guest
+        * directly (ARM ARM B3.12.7 "Prioritization of aborts").
+        */
+
+       mask = (~0U) >> shift;
+       if (reg) {
+               regval = *reg;
+       } else {
+               BUG_ON(mode != (ACCESS_READ_RAZ | ACCESS_WRITE_IGNORED));
+               regval = 0;
+       }
+
+       if (mmio->is_write) {
+               u32 data = (*((u32 *)mmio->data) & mask) << shift;
+               switch (ACCESS_WRITE_MASK(mode)) {
+               case ACCESS_WRITE_IGNORED:
+                       return;
+
+               case ACCESS_WRITE_SETBIT:
+                       regval |= data;
+                       break;
+
+               case ACCESS_WRITE_CLEARBIT:
+                       regval &= ~data;
+                       break;
+
+               case ACCESS_WRITE_VALUE:
+                       regval = (regval & ~(mask << shift)) | data;
+                       break;
+               }
+               *reg = regval;
+       } else {
+               switch (ACCESS_READ_MASK(mode)) {
+               case ACCESS_READ_RAZ:
+                       regval = 0;
+                       /* fall through */
+
+               case ACCESS_READ_VALUE:
+                       *((u32 *)mmio->data) = (regval >> shift) & mask;
+               }
+       }
+}
As I mentioned previously, I suspect that this doesn't work with big-endian
systems. Whilst that's reasonable for the moment, a comment would be useful
for the unlucky soul that decides to do that work in future (or add
accessors for mmio->data as I suggested before).
admittedly this really hurts my brain, but I think there's actually no
problem with endianness: whatever comes in mmio->data will have native
endianness
IIRC we have a local endianness flag on ppc. Once you introduce big endian guests, you can just add one too and add a CAP for it. I wouldn't worry about it now though.

Alex
quoted hunk ↗ jump to hunk
and the vgic is always little-endian, so a guest would have
to make sure to do its own endianness conversion before writing data,
or did I get this backwards? (some nasty feeling about if the OS is
compiled in another endianness than the hardware everything may
break).

Anyhow, I think there's another bug in this code though. Please take a
look and see if you agree:

commit 3cab2b93a6f6acd3c043e584f23b94ab8f1bbd66
Author: Christoffer Dall [off-list ref]
Date:   Mon Jan 14 15:55:18 2013 -0500

   KVM: ARM: Limit vgic read/writes to load/store length

   The vgic read/write operations did not consider ldrb/strb masks, and
   would therefore unintentionally overwrite parts of a register.

   Consider for example a store of a single byte to a word-aligned address
   of one of the priority registers, that would cause the 3 most
   significant bytes to be overwritten with zeros.

   Cc: Marc Zyniger [off-list ref]
   Cc: Will Deacon [off-list ref]
   Signed-off-by: Christoffer Dall [off-list ref]
diff --git a/arch/arm/kvm/vgic.c b/arch/arm/kvm/vgic.c
index 25daa07..5c1bcf5 100644
--- a/arch/arm/kvm/vgic.c
+++ b/arch/arm/kvm/vgic.c
@@ -233,6 +233,16 @@ static void vgic_cpu_irq_clear(struct kvm_vcpu
*vcpu, int irq)
             vcpu->arch.vgic_cpu.pending_shared);
}

+static u32 mmio_data_read(struct kvm_exit_mmio *mmio, u32 mask)
+{
+    return *((u32 *)mmio->data) & mask;
+}
+
+static void mmio_data_write(struct kvm_exit_mmio *mmio, u32 mask, u32 value)
+{
+    *((u32 *)mmio->data) = value & mask;
+}
+
/**
 * vgic_reg_access - access vgic register
 * @mmio:   pointer to the data describing the mmio access
@@ -247,8 +257,8 @@ static void vgic_cpu_irq_clear(struct kvm_vcpu
*vcpu, int irq)
static void vgic_reg_access(struct kvm_exit_mmio *mmio, u32 *reg,
               phys_addr_t offset, int mode)
{
-    int shift = (offset & 3) * 8;
-    u32 mask;
+    int word_offset = (offset & 3) * 8;
+    u32 mask = (1UL << (mmio->len * 8)) - 1;
   u32 regval;

   /*
@@ -256,7 +266,6 @@ static void vgic_reg_access(struct kvm_exit_mmio
*mmio, u32 *reg,
    * directly (ARM ARM B3.12.7 "Prioritization of aborts").
    */

-    mask = (~0U) >> shift;
   if (reg) {
       regval = *reg;
   } else {
@@ -265,7 +274,7 @@ static void vgic_reg_access(struct kvm_exit_mmio
*mmio, u32 *reg,
   }

   if (mmio->is_write) {
-        u32 data = (*((u32 *)mmio->data) & mask) << shift;
+        u32 data = mmio_data_read(mmio, mask) << word_offset;
       switch (ACCESS_WRITE_MASK(mode)) {
       case ACCESS_WRITE_IGNORED:
           return;
@@ -279,7 +288,7 @@ static void vgic_reg_access(struct kvm_exit_mmio
*mmio, u32 *reg,
           break;

       case ACCESS_WRITE_VALUE:
-            regval = (regval & ~(mask << shift)) | data;
+            regval = (regval & ~(mask << word_offset)) | data;
           break;
       }
       *reg = regval;
@@ -290,7 +299,7 @@ static void vgic_reg_access(struct kvm_exit_mmio
*mmio, u32 *reg,
           /* fall through */

       case ACCESS_READ_VALUE:
-            *((u32 *)mmio->data) = (regval >> shift) & mask;
+            mmio_data_write(mmio, mask, regval >> word_offset);
       }
   }
}
@@ -702,6 +711,12 @@ bool vgic_handle_mmio(struct kvm_vcpu *vcpu,
struct kvm_run *run,
       (mmio->phys_addr + mmio->len) > (base + KVM_VGIC_V2_DIST_SIZE))
       return false;

+    /* We don't support ldrd / strd or ldm / stm to the emulated vgic */
+    if (mmio->len > 4) {
+        kvm_inject_dabt(vcpu, mmio->phys_addr);
+        return true;
+    }
+
   range = find_matching_range(vgic_ranges, mmio, base);
   if (unlikely(!range || !range->handle_mmio)) {
       pr_warn("Unhandled access %d %08llx %d\n",
--

Thanks,
-Christoffer
_______________________________________________
kvmarm mailing list
kvmarm at lists.cs.columbia.edu
https://lists.cs.columbia.edu/cucslists/listinfo/kvmarm
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help