diff --git a/include/uc_priv.h b/include/uc_priv.h index 3917419e..0a6260e5 100644 --- a/include/uc_priv.h +++ b/include/uc_priv.h @@ -199,6 +199,7 @@ struct uc_struct { uintptr_t helper_retaddr; // qemu/memory.c + GHashTable *flat_views; bool global_dirty_log; /* This is a multi-level map on the virtual address space. diff --git a/qemu/glib_compat.c b/qemu/glib_compat.c index 999ef6f8..1c3a4039 100644 --- a/qemu/glib_compat.c +++ b/qemu/glib_compat.c @@ -55,11 +55,29 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * Returns: a hash value corresponding to the key. */ -static guint g_direct_hash (gconstpointer v) +guint g_direct_hash (gconstpointer v) { return GPOINTER_TO_UINT (v); } +/** + * g_direct_equal: + * @v1: a key. + * @v2: a key to compare with @v1. + * + * Compares two #gpointer arguments and returns %TRUE if they are equal. + * It can be passed to g_hash_table_new() as the @key_equal_func + * parameter, when using pointers as keys in a #GHashTable. + * + * Returns: %TRUE if the two keys match. + */ +gboolean +g_direct_equal (gconstpointer v1, + gconstpointer v2) +{ + return v1 == v2; +} + // g_str_hash() is lifted glib-2.28.0/glib/gstring.c /** * g_str_hash: @@ -1438,6 +1456,27 @@ void g_hash_table_insert (GHashTable *hash_table, g_hash_table_insert_internal (hash_table, key, value, FALSE); } +/** + * g_hash_table_replace: + * @hash_table: a #GHashTable. + * @key: a key to insert. + * @value: the value to associate with the key. + * + * Inserts a new key and value into a #GHashTable similar to + * g_hash_table_insert(). The difference is that if the key already exists + * in the #GHashTable, it gets replaced by the new key. If you supplied a + * @value_destroy_func when creating the #GHashTable, the old value is freed + * using that function. If you supplied a @key_destroy_func when creating the + * #GHashTable, the old key is freed using that function. + **/ +void +g_hash_table_replace (GHashTable *hash_table, + gpointer key, + gpointer value) +{ + g_hash_table_insert_internal (hash_table, key, value, TRUE); +} + /* * g_hash_table_lookup_node: * @hash_table: our #GHashTable diff --git a/qemu/include/glib_compat.h b/qemu/include/glib_compat.h index 72b4ddf9..5f3f8af7 100644 --- a/qemu/include/glib_compat.h +++ b/qemu/include/glib_compat.h @@ -57,6 +57,9 @@ typedef void (*GFunc)(gpointer data, gpointer user_data); typedef gint (*GCompareFunc)(gconstpointer v1, gconstpointer v2); typedef void (*GDestroyNotify)(gpointer data); +guint g_direct_hash(gconstpointer v); +gboolean g_direct_equal(gconstpointer v1, gconstpointer v2); + guint g_str_hash(gconstpointer v); gboolean g_str_equal(gconstpointer v1, gconstpointer v2); gboolean g_str_has_suffix(const gchar *str, const gchar *prefix); @@ -137,9 +140,10 @@ void g_hash_table_destroy(GHashTable *hash_table); gpointer g_hash_table_find(GHashTable *hash_table, GHRFunc predicate, gpointer user_data); void g_hash_table_foreach(GHashTable *hash_table, GHFunc func, gpointer user_data); void g_hash_table_insert(GHashTable *hash_table, gpointer key, gpointer value); +void g_hash_table_replace(GHashTable *hash_table, gpointer key, gpointer value); gpointer g_hash_table_lookup(GHashTable *hash_table, gconstpointer key); GHashTable *g_hash_table_new(GHashFunc hash_func, GEqualFunc key_equal_func); -GHashTable *g_hash_table_new_full(GHashFunc hash_func, GEqualFunc key_equal_func, +GHashTable *g_hash_table_new_full(GHashFunc hash_func, GEqualFunc key_equal_func, GDestroyNotify key_destroy_func, GDestroyNotify value_destroy_func); void g_hash_table_remove_all(GHashTable *hash_table); gboolean g_hash_table_remove(GHashTable *hash_table, gconstpointer key); diff --git a/qemu/memory.c b/qemu/memory.c index c1252a7c..769713a3 100644 --- a/qemu/memory.c +++ b/qemu/memory.c @@ -720,6 +720,7 @@ static FlatView *generate_memory_topology(MemoryRegion *mr) flatview_add_to_dispatch(view, &mrs); } address_space_dispatch_compact(view->dispatch); + g_hash_table_replace(mr->uc->flat_views, mr, view); return view; } @@ -801,11 +802,47 @@ static void address_space_update_topology_pass(AddressSpace *as, } -static void address_space_update_topology(AddressSpace *as) +static void flatviews_init(struct uc_struct *uc) +{ + if (uc->flat_views) { + return; + } + + uc->flat_views = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, + (GDestroyNotify) flatview_unref); +} + +static void flatviews_reset(struct uc_struct *uc) +{ + AddressSpace *as; + + if (uc->flat_views) { + g_hash_table_unref(uc->flat_views); + uc->flat_views = NULL; + } + flatviews_init(uc); + + /* Render unique FVs */ + QTAILQ_FOREACH(as, &uc->address_spaces, address_spaces_link) { + MemoryRegion *physmr = memory_region_get_flatview_root(as->root); + + if (g_hash_table_lookup(uc->flat_views, physmr)) { + continue; + } + + generate_memory_topology(physmr); + } +} + +static void address_space_set_flatview(AddressSpace *as) { FlatView *old_view = address_space_get_flatview(as); - MemoryRegion *physmr = memory_region_get_flatview_root(old_view->root); - FlatView *new_view = generate_memory_topology(physmr); + MemoryRegion *physmr = memory_region_get_flatview_root(as->root); + FlatView *new_view = g_hash_table_lookup(as->uc->flat_views, physmr); + + assert(new_view); + + flatview_ref(new_view); if (!QTAILQ_EMPTY(&as->listeners)) { address_space_update_topology_pass(as, old_view, new_view, false); @@ -843,10 +880,12 @@ void memory_region_transaction_commit(struct uc_struct *uc) --uc->memory_region_transaction_depth; if (!uc->memory_region_transaction_depth) { if (uc->memory_region_update_pending) { + flatviews_reset(uc); + MEMORY_LISTENER_CALL_GLOBAL(begin, Forward); QTAILQ_FOREACH(as, &uc->address_spaces, address_spaces_link) { - address_space_update_topology(as); + address_space_set_flatview(as); } MEMORY_LISTENER_CALL_GLOBAL(commit, Forward); @@ -1863,13 +1902,6 @@ AddressSpace *address_space_init_shareable(struct uc_struct *uc, MemoryRegion *r { AddressSpace *as; - QTAILQ_FOREACH(as, &uc->address_spaces, address_spaces_link) { - if (root == as->root && as->malloced) { - as->ref_count++; - return as; - } - } - as = g_malloc0(sizeof *as); address_space_init(uc, as, root, name); as->malloced = true; diff --git a/qemu/unicorn_common.h b/qemu/unicorn_common.h index a737b536..8b3b6565 100644 --- a/qemu/unicorn_common.h +++ b/qemu/unicorn_common.h @@ -78,6 +78,9 @@ static void release_common(void *t) tcg_pool_reset(s); g_hash_table_destroy(s->helpers); + // Destory flat view hash table + g_hash_table_destroy(s->uc->flat_views); + // TODO(danghvu): these function is not available outside qemu // so we keep them here instead of outside uc_close. free_address_spaces(s->uc);