cpu: Turn cpu_get_memory_mapping() into a CPUState hook
[sdk/emulator/qemu.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 static CPUArchState *find_paging_enabled_cpu(CPUArchState *start_cpu)
169 {
170     CPUArchState *env;
171
172     for (env = start_cpu; env != NULL; env = env->next_cpu) {
173         if (cpu_paging_enabled(ENV_GET_CPU(env))) {
174             return env;
175         }
176     }
177
178     return NULL;
179 }
180
181 int qemu_get_guest_memory_mapping(MemoryMappingList *list)
182 {
183     CPUArchState *env, *first_paging_enabled_cpu;
184     RAMBlock *block;
185     ram_addr_t offset, length;
186
187     first_paging_enabled_cpu = find_paging_enabled_cpu(first_cpu);
188     if (first_paging_enabled_cpu) {
189         for (env = first_paging_enabled_cpu; env != NULL; env = env->next_cpu) {
190             Error *err = NULL;
191             cpu_get_memory_mapping(ENV_GET_CPU(env), list, &err);
192             if (err) {
193                 error_free(err);
194                 return -1;
195             }
196         }
197         return 0;
198     }
199
200     /*
201      * If the guest doesn't use paging, the virtual address is equal to physical
202      * address.
203      */
204     QTAILQ_FOREACH(block, &ram_list.blocks, next) {
205         offset = block->offset;
206         length = block->length;
207         create_new_memory_mapping(list, offset, offset, length);
208     }
209
210     return 0;
211 }
212
213 void qemu_get_guest_simple_memory_mapping(MemoryMappingList *list)
214 {
215     RAMBlock *block;
216
217     QTAILQ_FOREACH(block, &ram_list.blocks, next) {
218         create_new_memory_mapping(list, block->offset, 0, block->length);
219     }
220 }
221
222 void memory_mapping_filter(MemoryMappingList *list, int64_t begin,
223                            int64_t length)
224 {
225     MemoryMapping *cur, *next;
226
227     QTAILQ_FOREACH_SAFE(cur, &list->head, next, next) {
228         if (cur->phys_addr >= begin + length ||
229             cur->phys_addr + cur->length <= begin) {
230             QTAILQ_REMOVE(&list->head, cur, next);
231             list->num--;
232             continue;
233         }
234
235         if (cur->phys_addr < begin) {
236             cur->length -= begin - cur->phys_addr;
237             if (cur->virt_addr) {
238                 cur->virt_addr += begin - cur->phys_addr;
239             }
240             cur->phys_addr = begin;
241         }
242
243         if (cur->phys_addr + cur->length > begin + length) {
244             cur->length -= cur->phys_addr + cur->length - begin - length;
245         }
246     }
247 }