]> rtime.felk.cvut.cz Git - lisovros/qemu_apohw.git/blob - memory_mapping.c
dump: introduce GuestPhysBlockList
[lisovros/qemu_apohw.git] / memory_mapping.c
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 "cpu.h"
15 #include "exec/cpu-all.h"
16 #include "sysemu/memory_mapping.h"
17
18 static void memory_mapping_list_add_mapping_sorted(MemoryMappingList *list,
19                                                    MemoryMapping *mapping)
20 {
21     MemoryMapping *p;
22
23     QTAILQ_FOREACH(p, &list->head, next) {
24         if (p->phys_addr >= mapping->phys_addr) {
25             QTAILQ_INSERT_BEFORE(p, mapping, next);
26             return;
27         }
28     }
29     QTAILQ_INSERT_TAIL(&list->head, mapping, next);
30 }
31
32 static void create_new_memory_mapping(MemoryMappingList *list,
33                                       hwaddr phys_addr,
34                                       hwaddr virt_addr,
35                                       ram_addr_t length)
36 {
37     MemoryMapping *memory_mapping;
38
39     memory_mapping = g_malloc(sizeof(MemoryMapping));
40     memory_mapping->phys_addr = phys_addr;
41     memory_mapping->virt_addr = virt_addr;
42     memory_mapping->length = length;
43     list->last_mapping = memory_mapping;
44     list->num++;
45     memory_mapping_list_add_mapping_sorted(list, memory_mapping);
46 }
47
48 static inline bool mapping_contiguous(MemoryMapping *map,
49                                       hwaddr phys_addr,
50                                       hwaddr virt_addr)
51 {
52     return phys_addr == map->phys_addr + map->length &&
53            virt_addr == map->virt_addr + map->length;
54 }
55
56 /*
57  * [map->phys_addr, map->phys_addr + map->length) and
58  * [phys_addr, phys_addr + length) have intersection?
59  */
60 static inline bool mapping_have_same_region(MemoryMapping *map,
61                                             hwaddr phys_addr,
62                                             ram_addr_t length)
63 {
64     return !(phys_addr + length < map->phys_addr ||
65              phys_addr >= map->phys_addr + map->length);
66 }
67
68 /*
69  * [map->phys_addr, map->phys_addr + map->length) and
70  * [phys_addr, phys_addr + length) have intersection. The virtual address in the
71  * intersection are the same?
72  */
73 static inline bool mapping_conflict(MemoryMapping *map,
74                                     hwaddr phys_addr,
75                                     hwaddr virt_addr)
76 {
77     return virt_addr - map->virt_addr != phys_addr - map->phys_addr;
78 }
79
80 /*
81  * [map->virt_addr, map->virt_addr + map->length) and
82  * [virt_addr, virt_addr + length) have intersection. And the physical address
83  * in the intersection are the same.
84  */
85 static inline void mapping_merge(MemoryMapping *map,
86                                  hwaddr virt_addr,
87                                  ram_addr_t length)
88 {
89     if (virt_addr < map->virt_addr) {
90         map->length += map->virt_addr - virt_addr;
91         map->virt_addr = virt_addr;
92     }
93
94     if ((virt_addr + length) >
95         (map->virt_addr + map->length)) {
96         map->length = virt_addr + length - map->virt_addr;
97     }
98 }
99
100 void memory_mapping_list_add_merge_sorted(MemoryMappingList *list,
101                                           hwaddr phys_addr,
102                                           hwaddr virt_addr,
103                                           ram_addr_t length)
104 {
105     MemoryMapping *memory_mapping, *last_mapping;
106
107     if (QTAILQ_EMPTY(&list->head)) {
108         create_new_memory_mapping(list, phys_addr, virt_addr, length);
109         return;
110     }
111
112     last_mapping = list->last_mapping;
113     if (last_mapping) {
114         if (mapping_contiguous(last_mapping, phys_addr, virt_addr)) {
115             last_mapping->length += length;
116             return;
117         }
118     }
119
120     QTAILQ_FOREACH(memory_mapping, &list->head, next) {
121         if (mapping_contiguous(memory_mapping, phys_addr, virt_addr)) {
122             memory_mapping->length += length;
123             list->last_mapping = memory_mapping;
124             return;
125         }
126
127         if (phys_addr + length < memory_mapping->phys_addr) {
128             /* create a new region before memory_mapping */
129             break;
130         }
131
132         if (mapping_have_same_region(memory_mapping, phys_addr, length)) {
133             if (mapping_conflict(memory_mapping, phys_addr, virt_addr)) {
134                 continue;
135             }
136
137             /* merge this region into memory_mapping */
138             mapping_merge(memory_mapping, virt_addr, length);
139             list->last_mapping = memory_mapping;
140             return;
141         }
142     }
143
144     /* this region can not be merged into any existed memory mapping. */
145     create_new_memory_mapping(list, phys_addr, virt_addr, length);
146 }
147
148 void memory_mapping_list_free(MemoryMappingList *list)
149 {
150     MemoryMapping *p, *q;
151
152     QTAILQ_FOREACH_SAFE(p, &list->head, next, q) {
153         QTAILQ_REMOVE(&list->head, p, next);
154         g_free(p);
155     }
156
157     list->num = 0;
158     list->last_mapping = NULL;
159 }
160
161 void memory_mapping_list_init(MemoryMappingList *list)
162 {
163     list->num = 0;
164     list->last_mapping = NULL;
165     QTAILQ_INIT(&list->head);
166 }
167
168 void guest_phys_blocks_free(GuestPhysBlockList *list)
169 {
170     GuestPhysBlock *p, *q;
171
172     QTAILQ_FOREACH_SAFE(p, &list->head, next, q) {
173         QTAILQ_REMOVE(&list->head, p, next);
174         g_free(p);
175     }
176     list->num = 0;
177 }
178
179 void guest_phys_blocks_init(GuestPhysBlockList *list)
180 {
181     list->num = 0;
182     QTAILQ_INIT(&list->head);
183 }
184
185 static CPUState *find_paging_enabled_cpu(CPUState *start_cpu)
186 {
187     CPUState *cpu;
188
189     for (cpu = start_cpu; cpu != NULL; cpu = cpu->next_cpu) {
190         if (cpu_paging_enabled(cpu)) {
191             return cpu;
192         }
193     }
194
195     return NULL;
196 }
197
198 void qemu_get_guest_memory_mapping(MemoryMappingList *list, Error **errp)
199 {
200     CPUState *cpu, *first_paging_enabled_cpu;
201     RAMBlock *block;
202     ram_addr_t offset, length;
203
204     first_paging_enabled_cpu = find_paging_enabled_cpu(first_cpu);
205     if (first_paging_enabled_cpu) {
206         for (cpu = first_paging_enabled_cpu; cpu != NULL; cpu = cpu->next_cpu) {
207             Error *err = NULL;
208             cpu_get_memory_mapping(cpu, list, &err);
209             if (err) {
210                 error_propagate(errp, err);
211                 return;
212             }
213         }
214         return;
215     }
216
217     /*
218      * If the guest doesn't use paging, the virtual address is equal to physical
219      * address.
220      */
221     QTAILQ_FOREACH(block, &ram_list.blocks, next) {
222         offset = block->offset;
223         length = block->length;
224         create_new_memory_mapping(list, offset, offset, length);
225     }
226 }
227
228 void qemu_get_guest_simple_memory_mapping(MemoryMappingList *list)
229 {
230     RAMBlock *block;
231
232     QTAILQ_FOREACH(block, &ram_list.blocks, next) {
233         create_new_memory_mapping(list, block->offset, 0, block->length);
234     }
235 }
236
237 void memory_mapping_filter(MemoryMappingList *list, int64_t begin,
238                            int64_t length)
239 {
240     MemoryMapping *cur, *next;
241
242     QTAILQ_FOREACH_SAFE(cur, &list->head, next, next) {
243         if (cur->phys_addr >= begin + length ||
244             cur->phys_addr + cur->length <= begin) {
245             QTAILQ_REMOVE(&list->head, cur, next);
246             list->num--;
247             continue;
248         }
249
250         if (cur->phys_addr < begin) {
251             cur->length -= begin - cur->phys_addr;
252             if (cur->virt_addr) {
253                 cur->virt_addr += begin - cur->phys_addr;
254             }
255             cur->phys_addr = begin;
256         }
257
258         if (cur->phys_addr + cur->length > begin + length) {
259             cur->length -= cur->phys_addr + cur->length - begin - length;
260         }
261     }
262 }