qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

memory_mapping.c (11517B)


      1 /*
      2  * QEMU memory mapping
      3  *
      4  * Copyright Fujitsu, Corp. 2011, 2012
      5  *
      6  * Authors:
      7  *     Wen Congyang <wency@cn.fujitsu.com>
      8  *
      9  * This work is licensed under the terms of the GNU GPL, version 2 or later.
     10  * See the COPYING file in the top-level directory.
     11  *
     12  */
     13 
     14 #include "qemu/osdep.h"
     15 #include "qapi/error.h"
     16 
     17 #include "sysemu/memory_mapping.h"
     18 #include "exec/memory.h"
     19 #include "exec/address-spaces.h"
     20 #include "hw/core/cpu.h"
     21 
     22 //#define DEBUG_GUEST_PHYS_REGION_ADD
     23 
     24 static void memory_mapping_list_add_mapping_sorted(MemoryMappingList *list,
     25                                                    MemoryMapping *mapping)
     26 {
     27     MemoryMapping *p;
     28 
     29     QTAILQ_FOREACH(p, &list->head, next) {
     30         if (p->phys_addr >= mapping->phys_addr) {
     31             QTAILQ_INSERT_BEFORE(p, mapping, next);
     32             return;
     33         }
     34     }
     35     QTAILQ_INSERT_TAIL(&list->head, mapping, next);
     36 }
     37 
     38 static void create_new_memory_mapping(MemoryMappingList *list,
     39                                       hwaddr phys_addr,
     40                                       hwaddr virt_addr,
     41                                       ram_addr_t length)
     42 {
     43     MemoryMapping *memory_mapping;
     44 
     45     memory_mapping = g_new(MemoryMapping, 1);
     46     memory_mapping->phys_addr = phys_addr;
     47     memory_mapping->virt_addr = virt_addr;
     48     memory_mapping->length = length;
     49     list->last_mapping = memory_mapping;
     50     list->num++;
     51     memory_mapping_list_add_mapping_sorted(list, memory_mapping);
     52 }
     53 
     54 static inline bool mapping_contiguous(MemoryMapping *map,
     55                                       hwaddr phys_addr,
     56                                       hwaddr virt_addr)
     57 {
     58     return phys_addr == map->phys_addr + map->length &&
     59            virt_addr == map->virt_addr + map->length;
     60 }
     61 
     62 /*
     63  * [map->phys_addr, map->phys_addr + map->length) and
     64  * [phys_addr, phys_addr + length) have intersection?
     65  */
     66 static inline bool mapping_have_same_region(MemoryMapping *map,
     67                                             hwaddr phys_addr,
     68                                             ram_addr_t length)
     69 {
     70     return !(phys_addr + length < map->phys_addr ||
     71              phys_addr >= map->phys_addr + map->length);
     72 }
     73 
     74 /*
     75  * [map->phys_addr, map->phys_addr + map->length) and
     76  * [phys_addr, phys_addr + length) have intersection. The virtual address in the
     77  * intersection are the same?
     78  */
     79 static inline bool mapping_conflict(MemoryMapping *map,
     80                                     hwaddr phys_addr,
     81                                     hwaddr virt_addr)
     82 {
     83     return virt_addr - map->virt_addr != phys_addr - map->phys_addr;
     84 }
     85 
     86 /*
     87  * [map->virt_addr, map->virt_addr + map->length) and
     88  * [virt_addr, virt_addr + length) have intersection. And the physical address
     89  * in the intersection are the same.
     90  */
     91 static inline void mapping_merge(MemoryMapping *map,
     92                                  hwaddr virt_addr,
     93                                  ram_addr_t length)
     94 {
     95     if (virt_addr < map->virt_addr) {
     96         map->length += map->virt_addr - virt_addr;
     97         map->virt_addr = virt_addr;
     98     }
     99 
    100     if ((virt_addr + length) >
    101         (map->virt_addr + map->length)) {
    102         map->length = virt_addr + length - map->virt_addr;
    103     }
    104 }
    105 
    106 void memory_mapping_list_add_merge_sorted(MemoryMappingList *list,
    107                                           hwaddr phys_addr,
    108                                           hwaddr virt_addr,
    109                                           ram_addr_t length)
    110 {
    111     MemoryMapping *memory_mapping, *last_mapping;
    112 
    113     if (QTAILQ_EMPTY(&list->head)) {
    114         create_new_memory_mapping(list, phys_addr, virt_addr, length);
    115         return;
    116     }
    117 
    118     last_mapping = list->last_mapping;
    119     if (last_mapping) {
    120         if (mapping_contiguous(last_mapping, phys_addr, virt_addr)) {
    121             last_mapping->length += length;
    122             return;
    123         }
    124     }
    125 
    126     QTAILQ_FOREACH(memory_mapping, &list->head, next) {
    127         if (mapping_contiguous(memory_mapping, phys_addr, virt_addr)) {
    128             memory_mapping->length += length;
    129             list->last_mapping = memory_mapping;
    130             return;
    131         }
    132 
    133         if (phys_addr + length < memory_mapping->phys_addr) {
    134             /* create a new region before memory_mapping */
    135             break;
    136         }
    137 
    138         if (mapping_have_same_region(memory_mapping, phys_addr, length)) {
    139             if (mapping_conflict(memory_mapping, phys_addr, virt_addr)) {
    140                 continue;
    141             }
    142 
    143             /* merge this region into memory_mapping */
    144             mapping_merge(memory_mapping, virt_addr, length);
    145             list->last_mapping = memory_mapping;
    146             return;
    147         }
    148     }
    149 
    150     /* this region can not be merged into any existed memory mapping. */
    151     create_new_memory_mapping(list, phys_addr, virt_addr, length);
    152 }
    153 
    154 void memory_mapping_list_free(MemoryMappingList *list)
    155 {
    156     MemoryMapping *p, *q;
    157 
    158     QTAILQ_FOREACH_SAFE(p, &list->head, next, q) {
    159         QTAILQ_REMOVE(&list->head, p, next);
    160         g_free(p);
    161     }
    162 
    163     list->num = 0;
    164     list->last_mapping = NULL;
    165 }
    166 
    167 void memory_mapping_list_init(MemoryMappingList *list)
    168 {
    169     list->num = 0;
    170     list->last_mapping = NULL;
    171     QTAILQ_INIT(&list->head);
    172 }
    173 
    174 void guest_phys_blocks_free(GuestPhysBlockList *list)
    175 {
    176     GuestPhysBlock *p, *q;
    177 
    178     QTAILQ_FOREACH_SAFE(p, &list->head, next, q) {
    179         QTAILQ_REMOVE(&list->head, p, next);
    180         memory_region_unref(p->mr);
    181         g_free(p);
    182     }
    183     list->num = 0;
    184 }
    185 
    186 void guest_phys_blocks_init(GuestPhysBlockList *list)
    187 {
    188     list->num = 0;
    189     QTAILQ_INIT(&list->head);
    190 }
    191 
    192 typedef struct GuestPhysListener {
    193     GuestPhysBlockList *list;
    194     MemoryListener listener;
    195 } GuestPhysListener;
    196 
    197 static void guest_phys_block_add_section(GuestPhysListener *g,
    198                                          MemoryRegionSection *section)
    199 {
    200     const hwaddr target_start = section->offset_within_address_space;
    201     const hwaddr target_end = target_start + int128_get64(section->size);
    202     uint8_t *host_addr = memory_region_get_ram_ptr(section->mr) +
    203                          section->offset_within_region;
    204     GuestPhysBlock *predecessor = NULL;
    205 
    206     /* find continuity in guest physical address space */
    207     if (!QTAILQ_EMPTY(&g->list->head)) {
    208         hwaddr predecessor_size;
    209 
    210         predecessor = QTAILQ_LAST(&g->list->head);
    211         predecessor_size = predecessor->target_end - predecessor->target_start;
    212 
    213         /* the memory API guarantees monotonically increasing traversal */
    214         g_assert(predecessor->target_end <= target_start);
    215 
    216         /* we want continuity in both guest-physical and host-virtual memory */
    217         if (predecessor->target_end < target_start ||
    218             predecessor->host_addr + predecessor_size != host_addr ||
    219             predecessor->mr != section->mr) {
    220             predecessor = NULL;
    221         }
    222     }
    223 
    224     if (predecessor == NULL) {
    225         /* isolated mapping, allocate it and add it to the list */
    226         GuestPhysBlock *block = g_malloc0(sizeof *block);
    227 
    228         block->target_start = target_start;
    229         block->target_end   = target_end;
    230         block->host_addr    = host_addr;
    231         block->mr           = section->mr;
    232         memory_region_ref(section->mr);
    233 
    234         QTAILQ_INSERT_TAIL(&g->list->head, block, next);
    235         ++g->list->num;
    236     } else {
    237         /* expand predecessor until @target_end; predecessor's start doesn't
    238          * change
    239          */
    240         predecessor->target_end = target_end;
    241     }
    242 
    243 #ifdef DEBUG_GUEST_PHYS_REGION_ADD
    244     fprintf(stderr, "%s: target_start=" TARGET_FMT_plx " target_end="
    245             TARGET_FMT_plx ": %s (count: %u)\n", __func__, target_start,
    246             target_end, predecessor ? "joined" : "added", g->list->num);
    247 #endif
    248 }
    249 
    250 static int guest_phys_ram_populate_cb(MemoryRegionSection *section,
    251                                       void *opaque)
    252 {
    253     GuestPhysListener *g = opaque;
    254 
    255     guest_phys_block_add_section(g, section);
    256     return 0;
    257 }
    258 
    259 static void guest_phys_blocks_region_add(MemoryListener *listener,
    260                                          MemoryRegionSection *section)
    261 {
    262     GuestPhysListener *g = container_of(listener, GuestPhysListener, listener);
    263 
    264     /* we only care about RAM */
    265     if (!memory_region_is_ram(section->mr) ||
    266         memory_region_is_ram_device(section->mr) ||
    267         memory_region_is_nonvolatile(section->mr)) {
    268         return;
    269     }
    270 
    271     /* for special sparse regions, only add populated parts */
    272     if (memory_region_has_ram_discard_manager(section->mr)) {
    273         RamDiscardManager *rdm;
    274 
    275         rdm = memory_region_get_ram_discard_manager(section->mr);
    276         ram_discard_manager_replay_populated(rdm, section,
    277                                              guest_phys_ram_populate_cb, g);
    278         return;
    279     }
    280 
    281     guest_phys_block_add_section(g, section);
    282 }
    283 
    284 void guest_phys_blocks_append(GuestPhysBlockList *list)
    285 {
    286     GuestPhysListener g = { 0 };
    287 
    288     g.list = list;
    289     g.listener.region_add = &guest_phys_blocks_region_add;
    290     memory_listener_register(&g.listener, &address_space_memory);
    291     memory_listener_unregister(&g.listener);
    292 }
    293 
    294 static CPUState *find_paging_enabled_cpu(CPUState *start_cpu)
    295 {
    296     CPUState *cpu;
    297 
    298     CPU_FOREACH(cpu) {
    299         if (cpu_paging_enabled(cpu)) {
    300             return cpu;
    301         }
    302     }
    303 
    304     return NULL;
    305 }
    306 
    307 void qemu_get_guest_memory_mapping(MemoryMappingList *list,
    308                                    const GuestPhysBlockList *guest_phys_blocks,
    309                                    Error **errp)
    310 {
    311     CPUState *cpu, *first_paging_enabled_cpu;
    312     GuestPhysBlock *block;
    313     ram_addr_t offset, length;
    314 
    315     first_paging_enabled_cpu = find_paging_enabled_cpu(first_cpu);
    316     if (first_paging_enabled_cpu) {
    317         for (cpu = first_paging_enabled_cpu; cpu != NULL;
    318              cpu = CPU_NEXT(cpu)) {
    319             Error *err = NULL;
    320             cpu_get_memory_mapping(cpu, list, &err);
    321             if (err) {
    322                 error_propagate(errp, err);
    323                 return;
    324             }
    325         }
    326         return;
    327     }
    328 
    329     /*
    330      * If the guest doesn't use paging, the virtual address is equal to physical
    331      * address.
    332      */
    333     QTAILQ_FOREACH(block, &guest_phys_blocks->head, next) {
    334         offset = block->target_start;
    335         length = block->target_end - block->target_start;
    336         create_new_memory_mapping(list, offset, offset, length);
    337     }
    338 }
    339 
    340 void qemu_get_guest_simple_memory_mapping(MemoryMappingList *list,
    341                                    const GuestPhysBlockList *guest_phys_blocks)
    342 {
    343     GuestPhysBlock *block;
    344 
    345     QTAILQ_FOREACH(block, &guest_phys_blocks->head, next) {
    346         create_new_memory_mapping(list, block->target_start, 0,
    347                                   block->target_end - block->target_start);
    348     }
    349 }
    350 
    351 void memory_mapping_filter(MemoryMappingList *list, int64_t begin,
    352                            int64_t length)
    353 {
    354     MemoryMapping *cur, *next;
    355 
    356     QTAILQ_FOREACH_SAFE(cur, &list->head, next, next) {
    357         if (cur->phys_addr >= begin + length ||
    358             cur->phys_addr + cur->length <= begin) {
    359             QTAILQ_REMOVE(&list->head, cur, next);
    360             g_free(cur);
    361             list->num--;
    362             continue;
    363         }
    364 
    365         if (cur->phys_addr < begin) {
    366             cur->length -= begin - cur->phys_addr;
    367             if (cur->virt_addr) {
    368                 cur->virt_addr += begin - cur->phys_addr;
    369             }
    370             cur->phys_addr = begin;
    371         }
    372 
    373         if (cur->phys_addr + cur->length > begin + length) {
    374             cur->length -= cur->phys_addr + cur->length - begin - length;
    375         }
    376     }
    377 }