Re: [Qemu-devel] [PATCH 3/3] PAM: make PAM emulation closer to documentation

2015-07-17 Thread Paolo Bonzini


On 17/07/2015 11:50, Ефимов Василий wrote:
 I will try solution based on address spaces as stated above.

If it works, that would be great.

Paolo



Re: [Qemu-devel] [PATCH 3/3] PAM: make PAM emulation closer to documentation

2015-07-17 Thread Ефимов Василий

16.07.2015 20:52, Paolo Bonzini пишет:



On 16/07/2015 16:41, Ефимов Василий wrote:

The main problem is rendering memory tree to FlatView.


I don't believe it's necessary to render a memory tree to the FlatView.
  You can use existing AddressSpaces.


+/* Read from RAM and write to PCI */
+memory_region_init_io(pam-region[1], OBJECT(dev), pam_ops, pam,
+pam-r-ram-w-pci, size);

This can be done with memory_region_set_readonly on the RAM region.  You
need to set mr-ops in order to point to pam_ops; for a first proof of
concept you can just set the field directly.

The idea is to read directly from system RAM region and to write
to PCI using I/O (because I do not see another way to emulate
access type driven redirection with existent API). If we create RAM
and make it read only then new useless RAM block will be created.


Don't create RAM; modify the existing one.


It is useless because of ram_addr of new region will be set to
one within system RAM block. Hence, cleaner way is to create I/O region.


You can use the existing RAM region and modify its properties (i.e.
toggle mr-readonly) after setting special mr-ops.  The special mr-ops
will be used for writes when mr-readonly = true.

The existing RAMs are ether whole pc.ram or pc.rom and pc.bios beyond
PCI. So, I think we have not invade them. Also, we only need the small 
subrange to be read only. Moreover, if -pflash is used then there is

ROM device instead of pc.bios with its own mr-ops.

You have suggested to create AddressSpace for PCI below. It is flexible
solution which is transparent for existing regions. I suggests to create
AddressSpace for RAM subtree too.  I think, both AS have to be PAM
implementation private for complete transparency for another QEMU parts.

I will try it in next patch version.



Writes to the PCI memory space can use the PCI address space, with
address_space_st*.

There is no PCI AddressSpace (only MemoryRegion). But
address_space_st* requires AddressSpace as argument.


Then create one with address_space_init.

However, can the guest see the difference between real mode 1 and the
fake mode 1 that QEMU implements?  Perhaps mode 1 can be left as is.

Yes, guest can.
If -pfalsh is used then BIOS becomes a programmable ROM device. Then
guest can switch to mode 1 and copy data by reading and writing it
at same location. After that guest can switch to mode 0 (or 3) and
copy data to some another place. Then it switches to mode 3 (or 0) and
compares the data.

I just check it with SeaBIOS and original PAM adding code listed below
to fw/shadow.c: __make_bios_writable_intel.

dprintf(1, PAM mode 1 test begin\n);
unsigned *m = (unsigned *) BUILD_ROM_START;

pci_config_writeb(bdf, pam0 + 1, 0x33);
*m = 0xdeafbeef;

pci_config_writeb(bdf, pam0 + 1, 0x11);
volatile unsigned t = *m;
*m = t;

pci_config_writeb(bdf, pam0 + 1, 0x33);
t = *m;

pci_config_writeb(bdf, pam0 + 1, 0x00);
unsigned t2 = *m;

dprintf(1, t = 0x%x, t2 = 0x%x\n, t, t2);

dprintf(1, PAM mode 1 test end\n);

The output is:

PAM mode 1 test begin
t = 0xdeafbeef, t2 = 0x0
PAM mode 1 test end

With new PAM the output is:

PAM mode 1 test begin
t = 0xdeafbeef, t2 = 0xdeafbeef
PAM mode 1 test end

Note BUILD_ROM_START is 0xc which is pc.rom with write permission
according to info mtree output. The -pflash is not needed.




+/* Read from PCI and write to RAM */
+memory_region_init_io(pam-region[2], OBJECT(dev), pam_ops, pam,
+pam-r-pci-w-ram, size);

Here you cannot run code from ROM, so it can be a pure MMIO region.
Reads can use address_space_ld*, while writes can use
memory_region_get_ram_ptr.


Even in this mode it is possible for code to be executed from ROM. This
can happen when particular PCI address is within ROM device connected
to PCI bus.


If it's just for pc.rom and isa-bios, introduce a new function
pam_create_pci_region that creates pc.rom with
memory_region_init_rom_device.  The mr-ops can write to RAM (mode 2) or
discard the write (mode 0).

They you can make pc.rom 256K instead of 128K, and instead of an alias,
you can manually copy the last 128K of the BIOS into the last 128K of
pc.rom.

Some adjustment will be necessary in order to support migration (perhaps
creating two 128K regions pc.rom and pc.rom.mirror), but for a proof of
concept the above should be enough.
I will try solution based on address spaces as stated above. It is 
cleaner and more generic.


Paolo


Vasily




Re: [Qemu-devel] [PATCH 3/3] PAM: make PAM emulation closer to documentation

2015-07-16 Thread Paolo Bonzini


On 16/07/2015 16:41, Ефимов Василий wrote:
 The main problem is rendering memory tree to FlatView.

I don't believe it's necessary to render a memory tree to the FlatView.
 You can use existing AddressSpaces.

 +/* Read from RAM and write to PCI */
 +memory_region_init_io(pam-region[1], OBJECT(dev), pam_ops, pam,
 +pam-r-ram-w-pci, size); 

 This can be done with memory_region_set_readonly on the RAM region.  You
 need to set mr-ops in order to point to pam_ops; for a first proof of
 concept you can just set the field directly.
 The idea is to read directly from system RAM region and to write
 to PCI using I/O (because I do not see another way to emulate
 access type driven redirection with existent API). If we create RAM
 and make it read only then new useless RAM block will be created.

Don't create RAM; modify the existing one.

 It is useless because of ram_addr of new region will be set to
 one within system RAM block. Hence, cleaner way is to create I/O region.

You can use the existing RAM region and modify its properties (i.e.
toggle mr-readonly) after setting special mr-ops.  The special mr-ops
will be used for writes when mr-readonly = true.

 Writes to the PCI memory space can use the PCI address space, with
 address_space_st*.
 There is no PCI AddressSpace (only MemoryRegion). But
 address_space_st* requires AddressSpace as argument.

Then create one with address_space_init.

However, can the guest see the difference between real mode 1 and the
fake mode 1 that QEMU implements?  Perhaps mode 1 can be left as is.

 +/* Read from PCI and write to RAM */
 +memory_region_init_io(pam-region[2], OBJECT(dev), pam_ops, pam,
 +pam-r-pci-w-ram, size);

 Here you cannot run code from ROM, so it can be a pure MMIO region.
 Reads can use address_space_ld*, while writes can use
 memory_region_get_ram_ptr.
 
 Even in this mode it is possible for code to be executed from ROM. This
 can happen when particular PCI address is within ROM device connected
 to PCI bus.

If it's just for pc.rom and isa-bios, introduce a new function
pam_create_pci_region that creates pc.rom with
memory_region_init_rom_device.  The mr-ops can write to RAM (mode 2) or
discard the write (mode 0).

They you can make pc.rom 256K instead of 128K, and instead of an alias,
you can manually copy the last 128K of the BIOS into the last 128K of
pc.rom.

Some adjustment will be necessary in order to support migration (perhaps
creating two 128K regions pc.rom and pc.rom.mirror), but for a proof of
concept the above should be enough.

Paolo



Re: [Qemu-devel] [PATCH 3/3] PAM: make PAM emulation closer to documentation

2015-07-16 Thread Ефимов Василий

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






Re: [Qemu-devel] [PATCH 3/3] PAM: make PAM emulation closer to documentation

2015-07-16 Thread Paolo Bonzini


On 16/07/2015 12:51, Ефимов Василий wrote:
 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. 

All these mechanism are extremely low level.  They are encapsulated
within exec.c, and copying code to pam.c is not a good idea because you
already have all the AddressSpaces and RAM MemoryRegions you need.

 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.

It does.  You're right about this:

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

... but an IOMMU region is not needed, and I think you can do everything
without touching exec.c at all.

+/* Read from RAM and write to PCI */
+memory_region_init_io(pam-region[1], OBJECT(dev), pam_ops, pam,
+pam-r-ram-w-pci, size);

This can be done with memory_region_set_readonly on the RAM region.  You
need to set mr-ops in order to point to pam_ops; for a first proof of
concept you can just set the field directly.

Writes to the PCI memory space can use the PCI address space, with
address_space_st*.

+/* Read from PCI and write to RAM */
+memory_region_init_io(pam-region[2], OBJECT(dev), pam_ops, pam,
+pam-r-pci-w-ram, size);

Here you cannot run code from ROM, so it can be a pure MMIO region.
Reads can use address_space_ld*, while writes can use
memory_region_get_ram_ptr.

Paolo



[Qemu-devel] [PATCH 3/3] PAM: make PAM emulation closer to documentation

2015-07-16 Thread Efimov Vasily
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);
+}
+}
+
+static uint64_t pam_read(void *opaque, hwaddr addr, unsigned size)
+{
+PAMMemoryRegion *pam = (PAMMemoryRegion *) opaque;
+uint64_t ret = 0;
+
+/* Source region can be IO only. */
+memory_region_dispatch_read(pam-mr_read_from, pam-read_offset + addr,
+ret, size, MEMTXATTRS_UNSPECIFIED);
+
+return ret;
+}
+
+static MemoryRegionOps pam_ops = {
+.write = pam_write,
+.read = pam_read,
+.endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void pam_set_current(PAMMemoryRegion *pam, unsigned new_current)
+{
+assert(new_current = 3);
+
+if (new_current != pam-current) {
+#ifdef DEBUG_PAM
+printf(PAM 0xTARGET_FMT_plx: %d = %s\n, pam-pam_offset,
+   new_current, pam-region[new_current].name);
+#endif
+memory_region_set_enabled(pam-region[pam-current], false);
+pam-current = new_current;
+memory_region_set_enabled(pam-region[new_current], true);
+}
+}
+
+static void pam_leaf_mr_lookup(MemoryRegion *mr, hwaddr offset,
+   MemoryRegion **leaf, hwaddr *offset_within_leaf)
+{
+MemoryRegion *other;
+if (mr-alias) {
+pam_leaf_mr_lookup(mr-alias, offset + mr-alias_offset, leaf,
+   offset_within_leaf);
+} else {
+if (QTAILQ_EMPTY(mr-subregions)
+ int128_lt(int128_make64(offset), mr-size)) {
+*leaf = mr;
+*offset_within_leaf = offset;
+} else {
+QTAILQ_FOREACH(other, mr-subregions, subregions_link) {
+if (!other-enabled) {
+continue;
+}
+if (other-addr = offset  int128_lt(int128_make64(offset),
+int128_add(int128_make64(other-addr), other-size))) {
+pam_leaf_mr_lookup(other, offset - other-addr, leaf,
+   offset_within_leaf);
+if (*leaf) {
+return;
+}
+}
+}
+*leaf = NULL;
+}
+}
+}
+
+static bool mr_has_mr(MemoryRegion *container, MemoryRegion *mr,
+  hwaddr *offset)
+{
+hwaddr tmp_offset;
+

Re: [Qemu-devel] [PATCH 3/3] PAM: make PAM emulation closer to documentation

2015-07-16 Thread 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.

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.

Paolo



Re: [Qemu-devel] [PATCH 3/3] PAM: make PAM emulation closer to documentation

2015-07-16 Thread Ефимов Василий

16.07.2015 14:10, Paolo Bonzini wrote:


 On 16/07/2015 12:51, Ефимов Василий wrote:
 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.

 All these mechanism are extremely low level.  They are encapsulated
 within exec.c, and copying code to pam.c is not a good idea because you
 already have all the AddressSpaces and RAM MemoryRegions you need.
The core problem is there is no region type which can redirects access
depending on whether it is read or write. The access type driven alias
could be perfect. But it is quite difficult to invent such type of
alias (or to generalize existing). The main problem is rendering memory
tree to FlatView.

 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.

 It does.  You're right about this:

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

 ... but an IOMMU region is not needed, and I think you can do everything
 without touching exec.c at all.

 +/* Read from RAM and write to PCI */
 +memory_region_init_io(pam-region[1], OBJECT(dev), pam_ops, pam,
 +pam-r-ram-w-pci, size);

 This can be done with memory_region_set_readonly on the RAM region.  You
 need to set mr-ops in order to point to pam_ops; for a first proof of
 concept you can just set the field directly.
The idea is to read directly from system RAM region and to write
to PCI using I/O (because I do not see another way to emulate
access type driven redirection with existent API). If we create RAM
and make it read only then new useless RAM block will be created.
It is useless because of ram_addr of new region will be set to
one within system RAM block. Hence, cleaner way is to create I/O region.

 Writes to the PCI memory space can use the PCI address space, with
 address_space_st*.
There is no PCI AddressSpace (only MemoryRegion). But
address_space_st* requires AddressSpace as argument.

 +/* Read from PCI and write to RAM */
 +memory_region_init_io(pam-region[2], OBJECT(dev), pam_ops, pam,
 +pam-r-pci-w-ram, size);

 Here you cannot run code from ROM, so it can be a pure MMIO region.
 Reads can use address_space_ld*, while writes can use
 memory_region_get_ram_ptr.

Even in this mode it is possible for code to be executed from ROM. This
can happen when particular PCI address is within ROM device connected
to PCI bus.

I do not have examples where this happens in mode 2, but in mode 0
(where reads are also directed to PCI) this happens at startup time
when BIOS is executed from ROM device on PCI. The path in memory tree
is system - pam-pci - pci - isa-bios - pc.bios where pc.bios is ROM
and pam-pci is alias.

If this happens when PAM is in mode 2 then path should become
system - pam-r-pci-w-ram where pam-r-pci-w-ram is ROM which ram_addr
points to RAM block of pc.bios. Write handler of pam-r-pci-w-ram
performs access to RAM block of pc.ram.

Implemented mechanism performs search for leaf region. And it supports
cases when leaf is I/O too. In this case pam-r-pci-w-ram (and
pam-r-ram-w-pci) becomes clear I/O and both its handlers
redirects access to corresponding region.

 Paolo


Vasily