This patch series 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.

Qemu distribution includes SeaBIOS which has hacks to work around incorrect
modes 1 and 2 emulation.

This patch series is tested using modified SeaBIOS. It is forced to use mode 2
for copying its data. BIOS reads a value from memory and immediately writes it
to same address. According to PAM definition, reads are directed to PCI (i.e. to
BIOS ROM) and writes are directed to RAM.

The patch for SeaBIOS is listed below.

Both SeaBIOS versions works with new PAM but the modified one does not work with
old PAM.

======
diff --git a/src/fw/shadow.c b/src/fw/shadow.c
index 4f00006..5b0e527 100644
--- a/src/fw/shadow.c
+++ b/src/fw/shadow.c
@@ -26,32 +26,43 @@ static void
 __make_bios_writable_intel(u16 bdf, u32 pam0)
 {
     // Make ram from 0xc0000-0xf0000 writable
-    int clear = 0;
     int i;
+    unsigned *mem, *mem_limit;
     for (i=0; i<6; i++) {
         u32 pam = pam0 + 1 + i;
         int reg = pci_config_readb(bdf, pam);
-        if (CONFIG_OPTIONROMS_DEPLOYED && (reg & 0x11) != 0x11) {
-            // Need to copy optionroms to work around qemu implementation
-            void *mem = (void*)(BUILD_ROM_START + i * 32*1024);
-            memcpy((void*)BUILD_BIOS_TMP_ADDR, mem, 32*1024);
+        if ((reg & 0x11) != 0x11) {
+            mem = (unsigned *)(BUILD_ROM_START + i * 32 * 1024);
+            pci_config_writeb(bdf, pam, 0x22);
+            mem_limit = mem + 32 * 1024 / sizeof(unsigned);
+
+            while (mem < mem_limit) {
+                volatile unsigned tmp = *mem;
+                *mem = tmp;
+                mem++;
+            }
             pci_config_writeb(bdf, pam, 0x33);
-            memcpy(mem, (void*)BUILD_BIOS_TMP_ADDR, 32*1024);
-            clear = 1;
         } else {
             pci_config_writeb(bdf, pam, 0x33);
         }
     }
-    if (clear)
-        memset((void*)BUILD_BIOS_TMP_ADDR, 0, 32*1024);
 
     // Make ram from 0xf0000-0x100000 writable
     int reg = pci_config_readb(bdf, pam0);
-    pci_config_writeb(bdf, pam0, 0x30);
     if (reg & 0x10)
         // Ram already present.
         return;
 
+    pci_config_writeb(bdf, pam0, 0x22);
+    mem = (unsigned *)BUILD_BIOS_ADDR;
+    mem_limit = mem + 32 * 1024 / sizeof(unsigned);
+    while (mem < mem_limit) {
+        volatile unsigned tmp = *mem;
+        *mem = tmp;
+        mem++;
+    }
+    pci_config_writeb(bdf, pam0, 0x33);
+
     // Copy bios.
     extern u8 code32flat_start[], code32flat_end[];
     memcpy(code32flat_start, code32flat_start + BIOS_SRC_OFFSET
@@ -61,17 +72,6 @@ __make_bios_writable_intel(u16 bdf, u32 pam0)
 static void
 make_bios_writable_intel(u16 bdf, u32 pam0)
 {
-    int reg = pci_config_readb(bdf, pam0);
-    if (!(reg & 0x10)) {
-        // QEMU doesn't fully implement the piix shadow capabilities -
-        // if ram isn't backing the bios segment when shadowing is
-        // disabled, the code itself wont be in memory.  So, run the
-        // code from the high-memory flash location.
-        u32 pos = (u32)__make_bios_writable_intel + BIOS_SRC_OFFSET;
-        void (*func)(u16 bdf, u32 pam0) = (void*)pos;
-        func(bdf, pam0);
-        return;
-    }
     // Ram already present - just enable writes
     __make_bios_writable_intel(bdf, pam0);
 }

Efimov Vasily (3):
  memory: make function invalidate_and_set_dirty public
  memory: make function memory_access_is_direct public
  PAM: make PAM emulation closer to documentation

 exec.c                         |  14 +--
 hw/pci-host/pam.c              | 238 ++++++++++++++++++++++++++++++++++++-----
 include/exec/memory-internal.h |  15 +++
 include/hw/pci-host/pam.h      |  10 +-
 4 files changed, 239 insertions(+), 38 deletions(-)

-- 
1.9.1


Reply via email to