]> git.proxmox.com Git - qemu.git/blame - exec.c
better locking - added PowerPC signal handler (add it for the other archs too because...
[qemu.git] / exec.c
CommitLineData
54936004 1/*
fd6ce8f6 2 * virtual page mapping and translated block handling
54936004
FB
3 *
4 * Copyright (c) 2003 Fabrice Bellard
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20#include <stdlib.h>
21#include <stdio.h>
22#include <stdarg.h>
23#include <string.h>
24#include <errno.h>
25#include <unistd.h>
26#include <inttypes.h>
fd6ce8f6 27#include <sys/mman.h>
54936004
FB
28
29#include "cpu-i386.h"
30
fd6ce8f6
FB
31//#define DEBUG_TB_INVALIDATE
32#define DEBUG_FLUSH
33
34/* make various TB consistency checks */
35//#define DEBUG_TB_CHECK
36
37/* threshold to flush the translated code buffer */
38#define CODE_GEN_BUFFER_MAX_SIZE (CODE_GEN_BUFFER_SIZE - CODE_GEN_MAX_SIZE)
39
40#define CODE_GEN_MAX_BLOCKS (CODE_GEN_BUFFER_SIZE / 64)
41
42TranslationBlock tbs[CODE_GEN_MAX_BLOCKS];
43TranslationBlock *tb_hash[CODE_GEN_HASH_SIZE];
44int nb_tbs;
45
46uint8_t code_gen_buffer[CODE_GEN_BUFFER_SIZE];
47uint8_t *code_gen_ptr;
48
54936004
FB
49/* XXX: pack the flags in the low bits of the pointer ? */
50typedef struct PageDesc {
54936004 51 unsigned long flags;
fd6ce8f6 52 TranslationBlock *first_tb;
54936004
FB
53} PageDesc;
54
55#define L2_BITS 10
56#define L1_BITS (32 - L2_BITS - TARGET_PAGE_BITS)
57
58#define L1_SIZE (1 << L1_BITS)
59#define L2_SIZE (1 << L2_BITS)
60
fd6ce8f6
FB
61static void tb_invalidate_page(unsigned long address);
62
54936004
FB
63unsigned long real_host_page_size;
64unsigned long host_page_bits;
65unsigned long host_page_size;
66unsigned long host_page_mask;
67
68static PageDesc *l1_map[L1_SIZE];
69
70void page_init(void)
71{
72 /* NOTE: we can always suppose that host_page_size >=
73 TARGET_PAGE_SIZE */
74 real_host_page_size = getpagesize();
75 if (host_page_size == 0)
76 host_page_size = real_host_page_size;
77 if (host_page_size < TARGET_PAGE_SIZE)
78 host_page_size = TARGET_PAGE_SIZE;
79 host_page_bits = 0;
80 while ((1 << host_page_bits) < host_page_size)
81 host_page_bits++;
82 host_page_mask = ~(host_page_size - 1);
83}
84
85/* dump memory mappings */
86void page_dump(FILE *f)
87{
88 unsigned long start, end;
89 int i, j, prot, prot1;
90 PageDesc *p;
91
92 fprintf(f, "%-8s %-8s %-8s %s\n",
93 "start", "end", "size", "prot");
94 start = -1;
95 end = -1;
96 prot = 0;
97 for(i = 0; i <= L1_SIZE; i++) {
98 if (i < L1_SIZE)
99 p = l1_map[i];
100 else
101 p = NULL;
102 for(j = 0;j < L2_SIZE; j++) {
103 if (!p)
104 prot1 = 0;
105 else
106 prot1 = p[j].flags;
107 if (prot1 != prot) {
108 end = (i << (32 - L1_BITS)) | (j << TARGET_PAGE_BITS);
109 if (start != -1) {
110 fprintf(f, "%08lx-%08lx %08lx %c%c%c\n",
111 start, end, end - start,
112 prot & PAGE_READ ? 'r' : '-',
113 prot & PAGE_WRITE ? 'w' : '-',
114 prot & PAGE_EXEC ? 'x' : '-');
115 }
116 if (prot1 != 0)
117 start = end;
118 else
119 start = -1;
120 prot = prot1;
121 }
122 if (!p)
123 break;
124 }
125 }
126}
127
fd6ce8f6 128static inline PageDesc *page_find_alloc(unsigned int index)
54936004 129{
54936004
FB
130 PageDesc **lp, *p;
131
54936004
FB
132 lp = &l1_map[index >> L2_BITS];
133 p = *lp;
134 if (!p) {
135 /* allocate if not found */
136 p = malloc(sizeof(PageDesc) * L2_SIZE);
fd6ce8f6 137 memset(p, 0, sizeof(PageDesc) * L2_SIZE);
54936004
FB
138 *lp = p;
139 }
140 return p + (index & (L2_SIZE - 1));
141}
142
fd6ce8f6 143static inline PageDesc *page_find(unsigned int index)
54936004 144{
54936004
FB
145 PageDesc *p;
146
54936004
FB
147 p = l1_map[index >> L2_BITS];
148 if (!p)
149 return 0;
fd6ce8f6
FB
150 return p + (index & (L2_SIZE - 1));
151}
152
153int page_get_flags(unsigned long address)
154{
155 PageDesc *p;
156
157 p = page_find(address >> TARGET_PAGE_BITS);
158 if (!p)
159 return 0;
160 return p->flags;
54936004
FB
161}
162
fd6ce8f6
FB
163/* modify the flags of a page and invalidate the code if
164 necessary. The flag PAGE_WRITE_ORG is positionned automatically
165 depending on PAGE_WRITE */
54936004
FB
166void page_set_flags(unsigned long start, unsigned long end, int flags)
167{
168 PageDesc *p;
169 unsigned long addr;
170
171 start = start & TARGET_PAGE_MASK;
172 end = TARGET_PAGE_ALIGN(end);
fd6ce8f6
FB
173 if (flags & PAGE_WRITE)
174 flags |= PAGE_WRITE_ORG;
54936004 175 for(addr = start; addr < end; addr += TARGET_PAGE_SIZE) {
fd6ce8f6
FB
176 p = page_find_alloc(addr >> TARGET_PAGE_BITS);
177 /* if the write protection is set, then we invalidate the code
178 inside */
179 if (!(p->flags & PAGE_WRITE) &&
180 (flags & PAGE_WRITE) &&
181 p->first_tb) {
182 tb_invalidate_page(addr);
183 }
54936004
FB
184 p->flags = flags;
185 }
186}
fd6ce8f6
FB
187
188void cpu_x86_tblocks_init(void)
189{
190 if (!code_gen_ptr) {
191 code_gen_ptr = code_gen_buffer;
192 }
193}
194
195/* set to NULL all the 'first_tb' fields in all PageDescs */
196static void page_flush_tb(void)
197{
198 int i, j;
199 PageDesc *p;
200
201 for(i = 0; i < L1_SIZE; i++) {
202 p = l1_map[i];
203 if (p) {
204 for(j = 0; j < L2_SIZE; j++)
205 p[j].first_tb = NULL;
206 }
207 }
208}
209
210/* flush all the translation blocks */
211void tb_flush(void)
212{
213 int i;
214#ifdef DEBUG_FLUSH
215 printf("qemu: flush code_size=%d nb_tbs=%d avg_tb_size=%d\n",
216 code_gen_ptr - code_gen_buffer,
217 nb_tbs,
218 (code_gen_ptr - code_gen_buffer) / nb_tbs);
219#endif
220 nb_tbs = 0;
221 for(i = 0;i < CODE_GEN_HASH_SIZE; i++)
222 tb_hash[i] = NULL;
223 page_flush_tb();
224 code_gen_ptr = code_gen_buffer;
225 /* XXX: flush processor icache at this point */
226}
227
228#ifdef DEBUG_TB_CHECK
229
230static void tb_invalidate_check(unsigned long address)
231{
232 TranslationBlock *tb;
233 int i;
234 address &= TARGET_PAGE_MASK;
235 for(i = 0;i < CODE_GEN_HASH_SIZE; i++) {
236 for(tb = tb_hash[i]; tb != NULL; tb = tb->hash_next) {
237 if (!(address + TARGET_PAGE_SIZE <= tb->pc ||
238 address >= tb->pc + tb->size)) {
239 printf("ERROR invalidate: address=%08lx PC=%08lx size=%04x\n",
240 address, tb->pc, tb->size);
241 }
242 }
243 }
244}
245
246/* verify that all the pages have correct rights for code */
247static void tb_page_check(void)
248{
249 TranslationBlock *tb;
250 int i, flags1, flags2;
251
252 for(i = 0;i < CODE_GEN_HASH_SIZE; i++) {
253 for(tb = tb_hash[i]; tb != NULL; tb = tb->hash_next) {
254 flags1 = page_get_flags(tb->pc);
255 flags2 = page_get_flags(tb->pc + tb->size - 1);
256 if ((flags1 & PAGE_WRITE) || (flags2 & PAGE_WRITE)) {
257 printf("ERROR page flags: PC=%08lx size=%04x f1=%x f2=%x\n",
258 tb->pc, tb->size, flags1, flags2);
259 }
260 }
261 }
262}
263
264#endif
265
266/* invalidate one TB */
267static inline void tb_remove(TranslationBlock **ptb, TranslationBlock *tb,
268 int next_offset)
269{
270 TranslationBlock *tb1;
271 for(;;) {
272 tb1 = *ptb;
273 if (tb1 == tb) {
274 *ptb = *(TranslationBlock **)((char *)tb1 + next_offset);
275 break;
276 }
277 ptb = (TranslationBlock **)((char *)tb1 + next_offset);
278 }
279}
280
281static inline void tb_invalidate(TranslationBlock *tb, int parity)
282{
283 PageDesc *p;
284 unsigned int page_index1, page_index2;
285 unsigned int h;
286
287 /* remove the TB from the hash list */
288 h = tb_hash_func(tb->pc);
289 tb_remove(&tb_hash[h], tb,
290 offsetof(TranslationBlock, hash_next));
291 /* remove the TB from the page list */
292 page_index1 = tb->pc >> TARGET_PAGE_BITS;
293 if ((page_index1 & 1) == parity) {
294 p = page_find(page_index1);
295 tb_remove(&p->first_tb, tb,
296 offsetof(TranslationBlock, page_next[page_index1 & 1]));
297 }
298 page_index2 = (tb->pc + tb->size - 1) >> TARGET_PAGE_BITS;
299 if ((page_index2 & 1) == parity) {
300 p = page_find(page_index2);
301 tb_remove(&p->first_tb, tb,
302 offsetof(TranslationBlock, page_next[page_index2 & 1]));
303 }
304}
305
306/* invalidate all TBs which intersect with the target page starting at addr */
307static void tb_invalidate_page(unsigned long address)
308{
309 TranslationBlock *tb_next, *tb;
310 unsigned int page_index;
311 int parity1, parity2;
312 PageDesc *p;
313#ifdef DEBUG_TB_INVALIDATE
314 printf("tb_invalidate_page: %lx\n", address);
315#endif
316
317 page_index = address >> TARGET_PAGE_BITS;
318 p = page_find(page_index);
319 if (!p)
320 return;
321 tb = p->first_tb;
322 parity1 = page_index & 1;
323 parity2 = parity1 ^ 1;
324 while (tb != NULL) {
325 tb_next = tb->page_next[parity1];
326 tb_invalidate(tb, parity2);
327 tb = tb_next;
328 }
329 p->first_tb = NULL;
330}
331
332/* add the tb in the target page and protect it if necessary */
333static inline void tb_alloc_page(TranslationBlock *tb, unsigned int page_index)
334{
335 PageDesc *p;
336 unsigned long host_start, host_end, addr, page_addr;
337 int prot;
338
339 p = page_find_alloc(page_index);
340 tb->page_next[page_index & 1] = p->first_tb;
341 p->first_tb = tb;
342 if (p->flags & PAGE_WRITE) {
343 /* force the host page as non writable (writes will have a
344 page fault + mprotect overhead) */
345 page_addr = (page_index << TARGET_PAGE_BITS);
346 host_start = page_addr & host_page_mask;
347 host_end = host_start + host_page_size;
348 prot = 0;
349 for(addr = host_start; addr < host_end; addr += TARGET_PAGE_SIZE)
350 prot |= page_get_flags(addr);
351 mprotect((void *)host_start, host_page_size,
352 (prot & PAGE_BITS) & ~PAGE_WRITE);
353#ifdef DEBUG_TB_INVALIDATE
354 printf("protecting code page: 0x%08lx\n",
355 host_start);
356#endif
357 p->flags &= ~PAGE_WRITE;
358#ifdef DEBUG_TB_CHECK
359 tb_page_check();
360#endif
361 }
362}
363
364/* Allocate a new translation block. Flush the translation buffer if
365 too many translation blocks or too much generated code. */
366TranslationBlock *tb_alloc(unsigned long pc,
367 unsigned long size)
368{
369 TranslationBlock *tb;
370 unsigned int page_index1, page_index2;
371
372 if (nb_tbs >= CODE_GEN_MAX_BLOCKS ||
373 (code_gen_ptr - code_gen_buffer) >= CODE_GEN_BUFFER_MAX_SIZE)
374 tb_flush();
375 tb = &tbs[nb_tbs++];
376 tb->pc = pc;
377 tb->size = size;
378
379 /* add in the page list */
380 page_index1 = pc >> TARGET_PAGE_BITS;
381 tb_alloc_page(tb, page_index1);
382 page_index2 = (pc + size - 1) >> TARGET_PAGE_BITS;
383 if (page_index2 != page_index1) {
384 tb_alloc_page(tb, page_index2);
385 }
386 return tb;
387}
388
389/* called from signal handler: invalidate the code and unprotect the
390 page. Return TRUE if the fault was succesfully handled. */
391int page_unprotect(unsigned long address)
392{
393 unsigned int page_index, prot;
394 PageDesc *p;
395 unsigned long host_start, host_end, addr;
396
397 page_index = address >> TARGET_PAGE_BITS;
398 p = page_find(page_index);
399 if (!p)
400 return 0;
401 if ((p->flags & (PAGE_WRITE_ORG | PAGE_WRITE)) == PAGE_WRITE_ORG) {
402 /* if the page was really writable, then we change its
403 protection back to writable */
404 host_start = address & host_page_mask;
405 host_end = host_start + host_page_size;
406 prot = 0;
407 for(addr = host_start; addr < host_end; addr += TARGET_PAGE_SIZE)
408 prot |= page_get_flags(addr);
409 mprotect((void *)host_start, host_page_size,
410 (prot & PAGE_BITS) | PAGE_WRITE);
411 p->flags |= PAGE_WRITE;
412
413 /* and since the content will be modified, we must invalidate
414 the corresponding translated code. */
415 tb_invalidate_page(address);
416#ifdef DEBUG_TB_CHECK
417 tb_invalidate_check(address);
418#endif
419 return 1;
420 } else {
421 return 0;
422 }
423}
424
425/* call this function when system calls directly modify a memory area */
426void page_unprotect_range(uint8_t *data, unsigned long data_size)
427{
428 unsigned long start, end, addr;
429
430 start = (unsigned long)data;
431 end = start + data_size;
432 start &= TARGET_PAGE_MASK;
433 end = TARGET_PAGE_ALIGN(end);
434 for(addr = start; addr < end; addr += TARGET_PAGE_SIZE) {
435 page_unprotect(addr);
436 }
437}