16.07.2015 12:05, Paolo Bonzini пишет:


On 16/07/2015 10:35, Efimov Vasily wrote:
This patch improves PAM emulation.

PAM defines 4 memory access redirection modes. In mode 1 reads are directed to
RAM and writes are directed to PCI. In mode 2 it is contrary. In mode 0 all
access is directed to PCI. In mode 3 it is directed to RAM. Modes 0 and 3 are
well emulated but modes 1 and 2 are not. The cause is: aliases are used
while more complicated logic is required.

The idea is to use ROM device like memory regions for mode 1 and 2 emulation
instead of aliases. Writes are directed to proper destination region by
specified I/O callback. Read redirection depends on type of source region.
In most cases source region is RAM (or ROM), so ram_addr of PAM region is set to
ram_addr of source region with offset. Otherwise, when source region is an I/O
region, reading is redirected to source region read callback by PAM region one.

Read source and write destination regions are updated by the memory
commit callback.

Note that we cannot use I/O region for PAM as it will violate "trying to execute
code outside RAM or ROM" assertion.

Signed-off-by: Efimov Vasily <r...@ispras.ru>
---
  hw/pci-host/pam.c         | 238 +++++++++++++++++++++++++++++++++++++++++-----
  include/hw/pci-host/pam.h |  10 +-
  2 files changed, 223 insertions(+), 25 deletions(-)

diff --git a/hw/pci-host/pam.c b/hw/pci-host/pam.c
index 17d826c..9729b2b 100644
--- a/hw/pci-host/pam.c
+++ b/hw/pci-host/pam.c
@@ -27,43 +27,233 @@
   * THE SOFTWARE.
   */

-#include "qom/object.h"
-#include "sysemu/sysemu.h"
  #include "hw/pci-host/pam.h"
+#include "exec/address-spaces.h"
+#include "exec/memory-internal.h"
+#include "qemu/bswap.h"
+
+static void pam_write(void *opaque, hwaddr addr, uint64_t data,
+                      unsigned size)
+{
+    PAMMemoryRegion *pam = (PAMMemoryRegion *) opaque;
+    void *ptr;
+
+    /* Destination region can be both RAM and IO. */
+    if (!memory_access_is_direct(pam->mr_write_to, true)) {
+        memory_region_dispatch_write(pam->mr_write_to,
+                                     addr + pam->write_offset, data, size,
+                                     MEMTXATTRS_UNSPECIFIED);
+    } else {
+        ptr = memory_region_get_ram_ptr(pam->mr_write_to) + addr
+                                                          + pam->write_offset;
+
+        switch (size) {
+        case 1:
+            stb_p(ptr, data);
+            break;
+        case 2:
+            stw_he_p(ptr, data);
+            break;
+        case 4:
+            stl_he_p(ptr, data);
+            break;
+        case 8:
+            stq_he_p(ptr, data);
+            break;
+        default:
+            abort();
+        }
+
+        invalidate_and_set_dirty(pam->mr_write_to, addr + pam->pam_offset,
+                                 size);
+    }
+}
+

The idea is very good, but the implementation relies on copying a lot of
code from exec.c.
If it is about pam_write then, for instance, I could suggest to outline
corresponding part of address_space_rw to dedicated public function.
The solution will require endianness conversion because of the
part works with uint8_t buffer but not with uint64_t values.

The rest of code looks up destination or source region or child region
offset in memory sub-tree which root is PCI or RAM region provided on
PAM creation. We cannon use common address_space_translate because it
searches from address space root and will return current PAM region.
To summarize, I suggest to move the code to exec.c. It is generic
enough.

Could you use an IOMMU memory region instead?  Then a single region can
be used to implement all four modes, and you don't hit the "trying to
execute code outside RAM or RAM".
Did you mean MemoryRegion.iommu_ops ? The feature does not allow to
change destination memory region. Also I has no find its using during
write access from CPU. And there is:

exec.c: address_space_translate_for_iotlb:
                                    assert(!section->mr->iommu_ops);


Paolo



Reply via email to