Re: [Qemu-devel] [PATCH 3/3] PAM: make PAM emulation closer to documentation
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
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
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
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
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
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
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
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