aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexander Graf <agraf@suse.de>2013-12-11 14:17:44 +0100
committerAlexander Graf <agraf@suse.de>2013-12-20 01:58:03 +0100
commit582b55a96ac4f66cea64d82e47651bd5ef38a8ec (patch)
treeabcae368c4aa57f047e23b76fb9bd25a19a33bcc
parent3978b863a5d8ac1c02848bf57d0a7f7067826a8a (diff)
roms: Flush icache when writing roms to guest memory
We use the rom infrastructure to write firmware and/or initial kernel blobs into guest address space. So we're basically emulating the cache off phase on very early system bootup. That phase is usually responsible for clearing the instruction cache for anything it writes into cachable memory, to ensure that after reboot we don't happen to execute stale bits from the instruction cache. So we need to invalidate the icache every time we write a rom into guest address space. We do not need to do this for every DMA since the guest expects it has to flush the icache manually in that case. This fixes random reboot issues on e5500 (booke ppc) for me. Signed-off-by: Alexander Graf <agraf@suse.de>
-rw-r--r--exec.c44
-rw-r--r--hw/core/loader.c7
-rw-r--r--include/exec/cpu-common.h1
3 files changed, 47 insertions, 5 deletions
diff --git a/exec.c b/exec.c
index 00526d18c0..7e49e8e555 100644
--- a/exec.c
+++ b/exec.c
@@ -50,6 +50,7 @@
#include "translate-all.h"
#include "exec/memory-internal.h"
+#include "qemu/cache-utils.h"
#include "qemu/range.h"
@@ -2070,9 +2071,13 @@ void cpu_physical_memory_rw(hwaddr addr, uint8_t *buf,
address_space_rw(&address_space_memory, addr, buf, len, is_write);
}
-/* used for ROM loading : can write in RAM and ROM */
-void cpu_physical_memory_write_rom(hwaddr addr,
- const uint8_t *buf, int len)
+enum write_rom_type {
+ WRITE_DATA,
+ FLUSH_CACHE,
+};
+
+static inline void cpu_physical_memory_write_rom_internal(
+ hwaddr addr, const uint8_t *buf, int len, enum write_rom_type type)
{
hwaddr l;
uint8_t *ptr;
@@ -2091,8 +2096,15 @@ void cpu_physical_memory_write_rom(hwaddr addr,
addr1 += memory_region_get_ram_addr(mr);
/* ROM/RAM case */
ptr = qemu_get_ram_ptr(addr1);
- memcpy(ptr, buf, l);
- invalidate_and_set_dirty(addr1, l);
+ switch (type) {
+ case WRITE_DATA:
+ memcpy(ptr, buf, l);
+ invalidate_and_set_dirty(addr1, l);
+ break;
+ case FLUSH_CACHE:
+ flush_icache_range((uintptr_t)ptr, (uintptr_t)ptr + l);
+ break;
+ }
}
len -= l;
buf += l;
@@ -2100,6 +2112,28 @@ void cpu_physical_memory_write_rom(hwaddr addr,
}
}
+/* used for ROM loading : can write in RAM and ROM */
+void cpu_physical_memory_write_rom(hwaddr addr,
+ const uint8_t *buf, int len)
+{
+ cpu_physical_memory_write_rom_internal(addr, buf, len, WRITE_DATA);
+}
+
+void cpu_flush_icache_range(hwaddr start, int len)
+{
+ /*
+ * This function should do the same thing as an icache flush that was
+ * triggered from within the guest. For TCG we are always cache coherent,
+ * so there is no need to flush anything. For KVM / Xen we need to flush
+ * the host's instruction cache at least.
+ */
+ if (tcg_enabled()) {
+ return;
+ }
+
+ cpu_physical_memory_write_rom_internal(start, NULL, len, FLUSH_CACHE);
+}
+
typedef struct {
MemoryRegion *mr;
void *buffer;
diff --git a/hw/core/loader.c b/hw/core/loader.c
index 60d2ebd4ac..0634bee20c 100644
--- a/hw/core/loader.c
+++ b/hw/core/loader.c
@@ -785,6 +785,13 @@ static void rom_reset(void *unused)
g_free(rom->data);
rom->data = NULL;
}
+ /*
+ * The rom loader is really on the same level as firmware in the guest
+ * shadowing a ROM into RAM. Such a shadowing mechanism needs to ensure
+ * that the instruction cache for that new region is clear, so that the
+ * CPU definitely fetches its instructions from the just written data.
+ */
+ cpu_flush_icache_range(rom->addr, rom->datasize);
}
}
diff --git a/include/exec/cpu-common.h b/include/exec/cpu-common.h
index e4996e19c3..8f33122c9f 100644
--- a/include/exec/cpu-common.h
+++ b/include/exec/cpu-common.h
@@ -110,6 +110,7 @@ void stq_phys(hwaddr addr, uint64_t val);
void cpu_physical_memory_write_rom(hwaddr addr,
const uint8_t *buf, int len);
+void cpu_flush_icache_range(hwaddr start, int len);
extern struct MemoryRegion io_mem_rom;
extern struct MemoryRegion io_mem_notdirty;