Skip to content

Commit 8444701

Browse files
pm215mdroth
authored andcommitted
exec: Handle multipage ranges in invalidate_and_set_dirty()
The code in invalidate_and_set_dirty() needs to handle addr/length combinations which cross guest physical page boundaries. This can happen, for example, when disk I/O reads large blocks into guest RAM which previously held code that we have cached translations for. Unfortunately we were only checking the clean/dirty status of the first page in the range, and then were calling a tb_invalidate function which only handles ranges that don't cross page boundaries. Fix the function to deal with multipage ranges. The symptoms of this bug were that guest code would misbehave (eg segfault), in particular after a guest reboot but potentially any time the guest reused a page of its physical RAM for new code. Cc: [email protected] Signed-off-by: Peter Maydell <[email protected]> Reviewed-by: Paolo Bonzini <[email protected]> Message-id: [email protected] (cherry picked from commit f874bf9) Signed-off-by: Michael Roth <[email protected]>
1 parent 05c5feb commit 8444701

File tree

2 files changed

+27
-4
lines changed

2 files changed

+27
-4
lines changed

exec.c

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2009,10 +2009,8 @@ int cpu_memory_rw_debug(CPUState *cpu, target_ulong addr,
20092009
static void invalidate_and_set_dirty(hwaddr addr,
20102010
hwaddr length)
20112011
{
2012-
if (cpu_physical_memory_is_clean(addr)) {
2013-
/* invalidate code */
2014-
tb_invalidate_phys_page_range(addr, addr + length, 0);
2015-
/* set dirty bit */
2012+
if (cpu_physical_memory_range_includes_clean(addr, length)) {
2013+
tb_invalidate_phys_range(addr, addr + length, 0);
20162014
cpu_physical_memory_set_dirty_range_nocode(addr, length);
20172015
}
20182016
xen_modified_memory(addr, length);

include/exec/ram_addr.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,21 @@ static inline bool cpu_physical_memory_get_dirty(ram_addr_t start,
4949
return next < end;
5050
}
5151

52+
static inline bool cpu_physical_memory_get_clean(ram_addr_t start,
53+
ram_addr_t length,
54+
unsigned client)
55+
{
56+
unsigned long end, page, next;
57+
58+
assert(client < DIRTY_MEMORY_NUM);
59+
60+
end = TARGET_PAGE_ALIGN(start + length) >> TARGET_PAGE_BITS;
61+
page = start >> TARGET_PAGE_BITS;
62+
next = find_next_zero_bit(ram_list.dirty_memory[client], end, page);
63+
64+
return next < end;
65+
}
66+
5267
static inline bool cpu_physical_memory_get_dirty_flag(ram_addr_t addr,
5368
unsigned client)
5469
{
@@ -64,6 +79,16 @@ static inline bool cpu_physical_memory_is_clean(ram_addr_t addr)
6479
return !(vga && code && migration);
6580
}
6681

82+
static inline bool cpu_physical_memory_range_includes_clean(ram_addr_t start,
83+
ram_addr_t length)
84+
{
85+
bool vga = cpu_physical_memory_get_clean(start, length, DIRTY_MEMORY_VGA);
86+
bool code = cpu_physical_memory_get_clean(start, length, DIRTY_MEMORY_CODE);
87+
bool migration =
88+
cpu_physical_memory_get_clean(start, length, DIRTY_MEMORY_MIGRATION);
89+
return vga || code || migration;
90+
}
91+
6792
static inline void cpu_physical_memory_set_dirty_flag(ram_addr_t addr,
6893
unsigned client)
6994
{

0 commit comments

Comments
 (0)