/* Copyright (C) 2007-2010 The Android Open Source Project ** ** This software is licensed under the terms of the GNU General Public ** License version 2, as published by the Free Software Foundation, and ** may be copied, distributed, and modified under those terms. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. */ /* * Contains implementation of routines that implement a red-black tree of * memory mappings in the guest system. */ #include "memcheck_mmrange_map.h" #include "memcheck_logging.h" /* Memory range descriptor stored in the map. */ typedef struct MMRangeMapEntry { /* R-B tree entry. */ RB_ENTRY(MMRangeMapEntry) rb_entry; /* Memory range descriptor for this entry. */ MMRangeDesc desc; } MMRangeMapEntry; // ============================================================================= // R-B Tree implementation // ============================================================================= /* Compare routine for the map. * Param: * d1 - First map entry to compare. * d2 - Second map entry to compare. * Return: * 0 - Descriptors are equal. Note that descriptors are considered to be * equal iff memory blocks they describe intersect in any part. * 1 - d1 is greater than d2 * -1 - d1 is less than d2. */ static inline int cmp_rb(MMRangeMapEntry* d1, MMRangeMapEntry* d2) { const target_ulong start1 = d1->desc.map_start; const target_ulong start2 = d2->desc.map_start; if (start1 < start2) { return (d1->desc.map_end - 1) < start2 ? -1 : 0; } return (d2->desc.map_end - 1) < start1 ? 1 : 0; } /* Expands RB macros here. */ RB_GENERATE(MMRangeMap, MMRangeMapEntry, rb_entry, cmp_rb); // ============================================================================= // Static routines // ============================================================================= /* Inserts new (or replaces existing) entry into the map. * See comments on mmrangemap_insert routine in the header file for details * about this routine. */ static RBTMapResult mmrangemap_insert_desc(MMRangeMap* map, MMRangeMapEntry* rdesc, MMRangeDesc* replaced) { MMRangeMapEntry* existing = MMRangeMap_RB_INSERT(map, rdesc); if (existing == NULL) { return RBT_MAP_RESULT_ENTRY_INSERTED; } // Matching entry exists. Lets see if we need to replace it. if (replaced == NULL) { return RBT_MAP_RESULT_ENTRY_ALREADY_EXISTS; } /* Copy existing entry to the provided buffer and replace it * with the new one. */ memcpy(replaced, &existing->desc, sizeof(MMRangeDesc)); MMRangeMap_RB_REMOVE(map, existing); qemu_free(existing); MMRangeMap_RB_INSERT(map, rdesc); return RBT_MAP_RESULT_ENTRY_REPLACED; } /* Finds an entry in the map that matches the given address range. * Param: * map - Map where to search for an entry. * start - Starting address of a mapping range. * end - Ending address of a mapping range. * Return: * Address of a map entry that matches the given range, or NULL if no * such entry has been found. */ static inline MMRangeMapEntry* mmrangemap_find_entry(const MMRangeMap* map, target_ulong start, target_ulong end) { MMRangeMapEntry rdesc; rdesc.desc.map_start = start; rdesc.desc.map_end = end; return MMRangeMap_RB_FIND((MMRangeMap*)map, &rdesc); } // ============================================================================= // Map API // ============================================================================= void mmrangemap_init(MMRangeMap* map) { RB_INIT(map); } RBTMapResult mmrangemap_insert(MMRangeMap* map, const MMRangeDesc* desc, MMRangeDesc* replaced) { RBTMapResult ret; // Allocate and initialize new map entry. MMRangeMapEntry* rdesc = qemu_malloc(sizeof(MMRangeMapEntry)); if (rdesc == NULL) { ME("memcheck: Unable to allocate new MMRangeMapEntry on insert."); return RBT_MAP_RESULT_ERROR; } memcpy(&rdesc->desc, desc, sizeof(MMRangeDesc)); // Insert new entry into the map. ret = mmrangemap_insert_desc(map, rdesc, replaced); if (ret == RBT_MAP_RESULT_ENTRY_ALREADY_EXISTS || ret == RBT_MAP_RESULT_ERROR) { /* Another descriptor already exists for this block, or an error * occurred. We have to free new descriptor, as it wasn't inserted. */ qemu_free(rdesc); } return ret; } MMRangeDesc* mmrangemap_find(const MMRangeMap* map, target_ulong start, target_ulong end) { MMRangeMapEntry* rdesc = mmrangemap_find_entry(map, start, end); return rdesc != NULL ? &rdesc->desc : NULL; } int mmrangemap_pull(MMRangeMap* map, target_ulong start, target_ulong end, MMRangeDesc* pulled) { MMRangeMapEntry* rdesc = mmrangemap_find_entry(map, start, end); if (rdesc != NULL) { memcpy(pulled, &rdesc->desc, sizeof(MMRangeDesc)); MMRangeMap_RB_REMOVE(map, rdesc); qemu_free(rdesc); return 0; } else { return -1; } } int mmrangemap_pull_first(MMRangeMap* map, MMRangeDesc* pulled) { MMRangeMapEntry* first = RB_MIN(MMRangeMap, map); if (first != NULL) { memcpy(pulled, &first->desc, sizeof(MMRangeDesc)); MMRangeMap_RB_REMOVE(map, first); qemu_free(first); return 0; } else { return -1; } } int mmrangemap_copy(MMRangeMap* to, const MMRangeMap* from) { MMRangeMapEntry* entry; RB_FOREACH(entry, MMRangeMap, (MMRangeMap*)from) { RBTMapResult ins_res; MMRangeMapEntry* new_entry = (MMRangeMapEntry*)qemu_malloc(sizeof(MMRangeMapEntry)); if (new_entry == NULL) { ME("memcheck: Unable to allocate new MMRangeMapEntry on copy."); return -1; } memcpy(new_entry, entry, sizeof(MMRangeMapEntry)); new_entry->desc.path = qemu_malloc(strlen(entry->desc.path) + 1); if (new_entry->desc.path == NULL) { ME("memcheck: Unable to allocate new path for MMRangeMapEntry on copy."); qemu_free(new_entry); return -1; } strcpy(new_entry->desc.path, entry->desc.path); ins_res = mmrangemap_insert_desc(to, new_entry, NULL); if (ins_res != RBT_MAP_RESULT_ENTRY_INSERTED) { ME("memcheck: Unable to insert new range map entry on copy. Insert returned %u", ins_res); qemu_free(new_entry->desc.path); qemu_free(new_entry); return -1; } } return 0; } int mmrangemap_empty(MMRangeMap* map) { MMRangeDesc pulled; int removed = 0; while (!mmrangemap_pull_first(map, &pulled)) { qemu_free(pulled.path); removed++; } return removed; }