9ae3a8
From 2ee2492513f9685cb716dc1cb4cf5b580da43e07 Mon Sep 17 00:00:00 2001
9ae3a8
From: Bandan Das <bsd@redhat.com>
9ae3a8
Date: Wed, 25 Jan 2017 03:36:07 +0100
9ae3a8
Subject: [PATCH 01/11] memory: Allow access only upto the maximum alignment
9ae3a8
 for memory_region_* functions
9ae3a8
9ae3a8
RH-Author: Bandan Das <bsd@redhat.com>
9ae3a8
Message-id: <jpgefzrn74o.fsf@linux.bootlegged.copy>
9ae3a8
Patchwork-id: 73367
9ae3a8
O-Subject: [RHEL-7.4 qemu-kvm PATCH] memory: Allow access only upto the maximum alignment for memory_region_* functions
9ae3a8
Bugzilla: 1342768
9ae3a8
RH-Acked-by: Paolo Bonzini <pbonzini@redhat.com>
9ae3a8
RH-Acked-by: Laurent Vivier <lvivier@redhat.com>
9ae3a8
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
9ae3a8
9ae3a8
Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1342768
9ae3a8
Brew: https://brewweb.engineering.redhat.com/brew/taskinfo?taskID=12437870
9ae3a8
Upstream: N/A, upstream doesn't exhibit this behavior
9ae3a8
9ae3a8
Currently, there is no check in memory_region_iorange_* functions for whether
9ae3a8
the size requested is greater than the maximum alignment. This causes
9ae3a8
an abort with a specific version of the Linux kernel (4.7.0-RC1):
9ae3a8
/usr/libexec/qemu-kvm -kernel ~/vmlinuz-4.7.0-rc1 --enable-kvm -m 1G -vnc :2 -monitor stdio
9ae3a8
9ae3a8
0  0x00007fb057cb65f7 in raise () from /lib64/libc.so.6
9ae3a8
1  0x00007fb057cb7ce8 in abort () from /lib64/libc.so.6
9ae3a8
2  0x00007fb05eca5537 in acpi_gpe_ioport_readb ()
9ae3a8
3  0x00007fb05eca5ff0 in gpe_readb ()
9ae3a8
4  0x00007fb05ede6f4c in memory_region_read_accessor ()
9ae3a8
5  0x00007fb05ede6993 in access_with_adjusted_size ()
9ae3a8
6  0x00007fb05ede7ce8 in memory_region_iorange_read ()
9ae3a8
7  0x00007fb05ede2ac7 in ioport_readl_thunk ()
9ae3a8
8  0x00007fb05ede3141 in cpu_inl ()
9ae3a8
9  0x00007fb05ede5c49 in kvm_cpu_exec ()
9ae3a8
10 0x00007fb05ed98485 in qemu_kvm_cpu_thread_fn ()
9ae3a8
11 0x00007fb05bcc9dc5 in start_thread () from /lib64/libpthread.so.0
9ae3a8
12 0x00007fb057d77ced in clone () from /lib64/libc.so.6
9ae3a8
9ae3a8
This happens because guest code tries to read(l=4) from 0xafe2
9ae3a8
with GPE base being 0xafe0 which causes the abort in
9ae3a8
acpi_gpe_ioport_get_ptr() to trigger. This change adds a
9ae3a8
memory_access_size() which is similar to the one in upstream that
9ae3a8
forces size to be equal to the maximum alignment if it's greater.
9ae3a8
It also keeps the other checks present in upstream for safety and
9ae3a8
is called from the memory_region_read/write functions before
9ae3a8
calling the call specific access functions.
9ae3a8
9ae3a8
Signed-off-by: Bandan Das <bsd@redhat.com>
9ae3a8
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
9ae3a8
---
9ae3a8
 memory.c | 44 ++++++++++++++++++++++++++++++++++++++++----
9ae3a8
 1 file changed, 40 insertions(+), 4 deletions(-)
9ae3a8
9ae3a8
diff --git a/memory.c b/memory.c
9ae3a8
index 7bd6e87..573ecdd 100644
9ae3a8
--- a/memory.c
9ae3a8
+++ b/memory.c
9ae3a8
@@ -381,6 +381,33 @@ static const MemoryRegionPortio *find_portio(MemoryRegion *mr, uint64_t offset,
9ae3a8
     return NULL;
9ae3a8
 }
9ae3a8
 
9ae3a8
+static int memory_access_size(MemoryRegion *mr, unsigned l, hwaddr addr)
9ae3a8
+{
9ae3a8
+  unsigned access_size_max = mr->ops->valid.max_access_size;
9ae3a8
+
9ae3a8
+  /* Regions are assumed to support 1-4 byte accesses unless
9ae3a8
+     otherwise specified.  */
9ae3a8
+  if (access_size_max == 0) {
9ae3a8
+    access_size_max = 4;
9ae3a8
+  }
9ae3a8
+
9ae3a8
+  /* Bound the maximum access by the alignment of the address.  */
9ae3a8
+  if (!mr->ops->impl.unaligned) {
9ae3a8
+    unsigned align_size_max = addr & -addr;
9ae3a8
+    if (align_size_max != 0 && align_size_max < access_size_max) {
9ae3a8
+        access_size_max = align_size_max;
9ae3a8
+    }
9ae3a8
+  }
9ae3a8
+
9ae3a8
+  /* Don't attempt accesses larger than the maximum.  */
9ae3a8
+  if (l > access_size_max) {
9ae3a8
+    l = access_size_max;
9ae3a8
+  }
9ae3a8
+  l = pow2floor(l);
9ae3a8
+
9ae3a8
+  return l;
9ae3a8
+}
9ae3a8
+
9ae3a8
 static void memory_region_iorange_read(IORange *iorange,
9ae3a8
                                        uint64_t offset,
9ae3a8
                                        unsigned width,
9ae3a8
@@ -389,6 +416,7 @@ static void memory_region_iorange_read(IORange *iorange,
9ae3a8
     MemoryRegionIORange *mrio
9ae3a8
         = container_of(iorange, MemoryRegionIORange, iorange);
9ae3a8
     MemoryRegion *mr = mrio->mr;
9ae3a8
+    unsigned l;
9ae3a8
 
9ae3a8
     offset += mrio->offset;
9ae3a8
     if (mr->ops->old_portio) {
9ae3a8
@@ -407,7 +435,8 @@ static void memory_region_iorange_read(IORange *iorange,
9ae3a8
         return;
9ae3a8
     }
9ae3a8
     *data = 0;
9ae3a8
-    access_with_adjusted_size(offset, data, width,
9ae3a8
+    l = memory_access_size(mr, width, offset);
9ae3a8
+    access_with_adjusted_size(offset, data, l,
9ae3a8
                               mr->ops->impl.min_access_size,
9ae3a8
                               mr->ops->impl.max_access_size,
9ae3a8
                               memory_region_read_accessor, mr);
9ae3a8
@@ -421,6 +450,7 @@ static void memory_region_iorange_write(IORange *iorange,
9ae3a8
     MemoryRegionIORange *mrio
9ae3a8
         = container_of(iorange, MemoryRegionIORange, iorange);
9ae3a8
     MemoryRegion *mr = mrio->mr;
9ae3a8
+    unsigned l;
9ae3a8
 
9ae3a8
     offset += mrio->offset;
9ae3a8
     if (mr->ops->old_portio) {
9ae3a8
@@ -437,7 +467,8 @@ static void memory_region_iorange_write(IORange *iorange,
9ae3a8
         }
9ae3a8
         return;
9ae3a8
     }
9ae3a8
-    access_with_adjusted_size(offset, &data, width,
9ae3a8
+    l = memory_access_size(mr, width, offset);
9ae3a8
+    access_with_adjusted_size(offset, &data, l,
9ae3a8
                               mr->ops->impl.min_access_size,
9ae3a8
                               mr->ops->impl.max_access_size,
9ae3a8
                               memory_region_write_accessor, mr);
9ae3a8
@@ -850,6 +881,7 @@ static uint64_t memory_region_dispatch_read1(MemoryRegion *mr,
9ae3a8
                                              unsigned size)
9ae3a8
 {
9ae3a8
     uint64_t data = 0;
9ae3a8
+    unsigned l;
9ae3a8
 
9ae3a8
     if (!memory_region_access_valid(mr, addr, size, false)) {
9ae3a8
         return -1U; /* FIXME: better signalling */
9ae3a8
@@ -859,8 +891,9 @@ static uint64_t memory_region_dispatch_read1(MemoryRegion *mr,
9ae3a8
         return mr->ops->old_mmio.read[ctz32(size)](mr->opaque, addr);
9ae3a8
     }
9ae3a8
 
9ae3a8
+    l = memory_access_size(mr, size, addr);
9ae3a8
     /* FIXME: support unaligned access */
9ae3a8
-    access_with_adjusted_size(addr, &data, size,
9ae3a8
+    access_with_adjusted_size(addr, &data, l,
9ae3a8
                               mr->ops->impl.min_access_size,
9ae3a8
                               mr->ops->impl.max_access_size,
9ae3a8
                               memory_region_read_accessor, mr);
9ae3a8
@@ -902,6 +935,8 @@ static void memory_region_dispatch_write(MemoryRegion *mr,
9ae3a8
                                          uint64_t data,
9ae3a8
                                          unsigned size)
9ae3a8
 {
9ae3a8
+    unsigned l;
9ae3a8
+
9ae3a8
     if (!memory_region_access_valid(mr, addr, size, true)) {
9ae3a8
         return; /* FIXME: better signalling */
9ae3a8
     }
9ae3a8
@@ -913,8 +948,9 @@ static void memory_region_dispatch_write(MemoryRegion *mr,
9ae3a8
         return;
9ae3a8
     }
9ae3a8
 
9ae3a8
+    l = memory_access_size(mr, size, addr);
9ae3a8
     /* FIXME: support unaligned access */
9ae3a8
-    access_with_adjusted_size(addr, &data, size,
9ae3a8
+    access_with_adjusted_size(addr, &data, l,
9ae3a8
                               mr->ops->impl.min_access_size,
9ae3a8
                               mr->ops->impl.max_access_size,
9ae3a8
                               memory_region_write_accessor, mr);
9ae3a8
-- 
9ae3a8
1.8.3.1
9ae3a8