154936004Sbellard /* 2fd6ce8f6Sbellard * virtual page mapping and translated block handling 354936004Sbellard * 454936004Sbellard * Copyright (c) 2003 Fabrice Bellard 554936004Sbellard * 654936004Sbellard * This library is free software; you can redistribute it and/or 754936004Sbellard * modify it under the terms of the GNU Lesser General Public 854936004Sbellard * License as published by the Free Software Foundation; either 954936004Sbellard * version 2 of the License, or (at your option) any later version. 1054936004Sbellard * 1154936004Sbellard * This library is distributed in the hope that it will be useful, 1254936004Sbellard * but WITHOUT ANY WARRANTY; without even the implied warranty of 1354936004Sbellard * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 1454936004Sbellard * Lesser General Public License for more details. 1554936004Sbellard * 1654936004Sbellard * You should have received a copy of the GNU Lesser General Public 1754936004Sbellard * License along with this library; if not, write to the Free Software 1854936004Sbellard * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 1954936004Sbellard */ 2054936004Sbellard #include <stdlib.h> 2154936004Sbellard #include <stdio.h> 2254936004Sbellard #include <stdarg.h> 2354936004Sbellard #include <string.h> 2454936004Sbellard #include <errno.h> 2554936004Sbellard #include <unistd.h> 2654936004Sbellard #include <inttypes.h> 27fd6ce8f6Sbellard #include <sys/mman.h> 2854936004Sbellard 29ea041c0eSbellard #include "config.h" 306180a181Sbellard #include "cpu.h" 316180a181Sbellard #include "exec-all.h" 3254936004Sbellard 33fd6ce8f6Sbellard //#define DEBUG_TB_INVALIDATE 3466e85a21Sbellard //#define DEBUG_FLUSH 35fd6ce8f6Sbellard 36fd6ce8f6Sbellard /* make various TB consistency checks */ 37fd6ce8f6Sbellard //#define DEBUG_TB_CHECK 38fd6ce8f6Sbellard 39fd6ce8f6Sbellard /* threshold to flush the translated code buffer */ 40fd6ce8f6Sbellard #define CODE_GEN_BUFFER_MAX_SIZE (CODE_GEN_BUFFER_SIZE - CODE_GEN_MAX_SIZE) 41fd6ce8f6Sbellard 42fd6ce8f6Sbellard #define CODE_GEN_MAX_BLOCKS (CODE_GEN_BUFFER_SIZE / 64) 43fd6ce8f6Sbellard 44fd6ce8f6Sbellard TranslationBlock tbs[CODE_GEN_MAX_BLOCKS]; 45fd6ce8f6Sbellard TranslationBlock *tb_hash[CODE_GEN_HASH_SIZE]; 46fd6ce8f6Sbellard int nb_tbs; 47eb51d102Sbellard /* any access to the tbs or the page table must use this lock */ 48eb51d102Sbellard spinlock_t tb_lock = SPIN_LOCK_UNLOCKED; 49fd6ce8f6Sbellard 50fd6ce8f6Sbellard uint8_t code_gen_buffer[CODE_GEN_BUFFER_SIZE]; 51fd6ce8f6Sbellard uint8_t *code_gen_ptr; 52fd6ce8f6Sbellard 5354936004Sbellard /* XXX: pack the flags in the low bits of the pointer ? */ 5454936004Sbellard typedef struct PageDesc { 5554936004Sbellard unsigned long flags; 56fd6ce8f6Sbellard TranslationBlock *first_tb; 5754936004Sbellard } PageDesc; 5854936004Sbellard 5954936004Sbellard #define L2_BITS 10 6054936004Sbellard #define L1_BITS (32 - L2_BITS - TARGET_PAGE_BITS) 6154936004Sbellard 6254936004Sbellard #define L1_SIZE (1 << L1_BITS) 6354936004Sbellard #define L2_SIZE (1 << L2_BITS) 6454936004Sbellard 65fd6ce8f6Sbellard static void tb_invalidate_page(unsigned long address); 6633417e70Sbellard static void io_mem_init(void); 67fd6ce8f6Sbellard 6854936004Sbellard unsigned long real_host_page_size; 6954936004Sbellard unsigned long host_page_bits; 7054936004Sbellard unsigned long host_page_size; 7154936004Sbellard unsigned long host_page_mask; 7254936004Sbellard 7354936004Sbellard static PageDesc *l1_map[L1_SIZE]; 7454936004Sbellard 7533417e70Sbellard /* io memory support */ 7633417e70Sbellard static unsigned long *l1_physmap[L1_SIZE]; 7733417e70Sbellard CPUWriteMemoryFunc *io_mem_write[IO_MEM_NB_ENTRIES][4]; 7833417e70Sbellard CPUReadMemoryFunc *io_mem_read[IO_MEM_NB_ENTRIES][4]; 7933417e70Sbellard static int io_mem_nb; 8033417e70Sbellard 8134865134Sbellard /* log support */ 8234865134Sbellard char *logfilename = "/tmp/qemu.log"; 8334865134Sbellard FILE *logfile; 8434865134Sbellard int loglevel; 8534865134Sbellard 86b346ff46Sbellard static void page_init(void) 8754936004Sbellard { 8854936004Sbellard /* NOTE: we can always suppose that host_page_size >= 8954936004Sbellard TARGET_PAGE_SIZE */ 9054936004Sbellard real_host_page_size = getpagesize(); 9154936004Sbellard if (host_page_size == 0) 9254936004Sbellard host_page_size = real_host_page_size; 9354936004Sbellard if (host_page_size < TARGET_PAGE_SIZE) 9454936004Sbellard host_page_size = TARGET_PAGE_SIZE; 9554936004Sbellard host_page_bits = 0; 9654936004Sbellard while ((1 << host_page_bits) < host_page_size) 9754936004Sbellard host_page_bits++; 9854936004Sbellard host_page_mask = ~(host_page_size - 1); 9954936004Sbellard } 10054936004Sbellard 10154936004Sbellard /* dump memory mappings */ 10254936004Sbellard void page_dump(FILE *f) 10354936004Sbellard { 10454936004Sbellard unsigned long start, end; 10554936004Sbellard int i, j, prot, prot1; 10654936004Sbellard PageDesc *p; 10754936004Sbellard 10854936004Sbellard fprintf(f, "%-8s %-8s %-8s %s\n", 10954936004Sbellard "start", "end", "size", "prot"); 11054936004Sbellard start = -1; 11154936004Sbellard end = -1; 11254936004Sbellard prot = 0; 11354936004Sbellard for(i = 0; i <= L1_SIZE; i++) { 11454936004Sbellard if (i < L1_SIZE) 11554936004Sbellard p = l1_map[i]; 11654936004Sbellard else 11754936004Sbellard p = NULL; 11854936004Sbellard for(j = 0;j < L2_SIZE; j++) { 11954936004Sbellard if (!p) 12054936004Sbellard prot1 = 0; 12154936004Sbellard else 12254936004Sbellard prot1 = p[j].flags; 12354936004Sbellard if (prot1 != prot) { 12454936004Sbellard end = (i << (32 - L1_BITS)) | (j << TARGET_PAGE_BITS); 12554936004Sbellard if (start != -1) { 12654936004Sbellard fprintf(f, "%08lx-%08lx %08lx %c%c%c\n", 12754936004Sbellard start, end, end - start, 12854936004Sbellard prot & PAGE_READ ? 'r' : '-', 12954936004Sbellard prot & PAGE_WRITE ? 'w' : '-', 13054936004Sbellard prot & PAGE_EXEC ? 'x' : '-'); 13154936004Sbellard } 13254936004Sbellard if (prot1 != 0) 13354936004Sbellard start = end; 13454936004Sbellard else 13554936004Sbellard start = -1; 13654936004Sbellard prot = prot1; 13754936004Sbellard } 13854936004Sbellard if (!p) 13954936004Sbellard break; 14054936004Sbellard } 14154936004Sbellard } 14254936004Sbellard } 14354936004Sbellard 144fd6ce8f6Sbellard static inline PageDesc *page_find_alloc(unsigned int index) 14554936004Sbellard { 14654936004Sbellard PageDesc **lp, *p; 14754936004Sbellard 14854936004Sbellard lp = &l1_map[index >> L2_BITS]; 14954936004Sbellard p = *lp; 15054936004Sbellard if (!p) { 15154936004Sbellard /* allocate if not found */ 15254936004Sbellard p = malloc(sizeof(PageDesc) * L2_SIZE); 153fd6ce8f6Sbellard memset(p, 0, sizeof(PageDesc) * L2_SIZE); 15454936004Sbellard *lp = p; 15554936004Sbellard } 15654936004Sbellard return p + (index & (L2_SIZE - 1)); 15754936004Sbellard } 15854936004Sbellard 159fd6ce8f6Sbellard static inline PageDesc *page_find(unsigned int index) 16054936004Sbellard { 16154936004Sbellard PageDesc *p; 16254936004Sbellard 16354936004Sbellard p = l1_map[index >> L2_BITS]; 16454936004Sbellard if (!p) 16554936004Sbellard return 0; 166fd6ce8f6Sbellard return p + (index & (L2_SIZE - 1)); 16754936004Sbellard } 16854936004Sbellard 169fd6ce8f6Sbellard int page_get_flags(unsigned long address) 170fd6ce8f6Sbellard { 171fd6ce8f6Sbellard PageDesc *p; 172fd6ce8f6Sbellard 173fd6ce8f6Sbellard p = page_find(address >> TARGET_PAGE_BITS); 174fd6ce8f6Sbellard if (!p) 175fd6ce8f6Sbellard return 0; 176fd6ce8f6Sbellard return p->flags; 177fd6ce8f6Sbellard } 178fd6ce8f6Sbellard 179fd6ce8f6Sbellard /* modify the flags of a page and invalidate the code if 180fd6ce8f6Sbellard necessary. The flag PAGE_WRITE_ORG is positionned automatically 181fd6ce8f6Sbellard depending on PAGE_WRITE */ 18254936004Sbellard void page_set_flags(unsigned long start, unsigned long end, int flags) 18354936004Sbellard { 18454936004Sbellard PageDesc *p; 18554936004Sbellard unsigned long addr; 18654936004Sbellard 18754936004Sbellard start = start & TARGET_PAGE_MASK; 18854936004Sbellard end = TARGET_PAGE_ALIGN(end); 189fd6ce8f6Sbellard if (flags & PAGE_WRITE) 190fd6ce8f6Sbellard flags |= PAGE_WRITE_ORG; 191eb51d102Sbellard spin_lock(&tb_lock); 19254936004Sbellard for(addr = start; addr < end; addr += TARGET_PAGE_SIZE) { 193fd6ce8f6Sbellard p = page_find_alloc(addr >> TARGET_PAGE_BITS); 194fd6ce8f6Sbellard /* if the write protection is set, then we invalidate the code 195fd6ce8f6Sbellard inside */ 196fd6ce8f6Sbellard if (!(p->flags & PAGE_WRITE) && 197fd6ce8f6Sbellard (flags & PAGE_WRITE) && 198fd6ce8f6Sbellard p->first_tb) { 199fd6ce8f6Sbellard tb_invalidate_page(addr); 200fd6ce8f6Sbellard } 20154936004Sbellard p->flags = flags; 20254936004Sbellard } 203eb51d102Sbellard spin_unlock(&tb_lock); 20454936004Sbellard } 205fd6ce8f6Sbellard 206b346ff46Sbellard void cpu_exec_init(void) 207fd6ce8f6Sbellard { 208fd6ce8f6Sbellard if (!code_gen_ptr) { 209fd6ce8f6Sbellard code_gen_ptr = code_gen_buffer; 210b346ff46Sbellard page_init(); 21133417e70Sbellard io_mem_init(); 212fd6ce8f6Sbellard } 213fd6ce8f6Sbellard } 214fd6ce8f6Sbellard 215fd6ce8f6Sbellard /* set to NULL all the 'first_tb' fields in all PageDescs */ 216fd6ce8f6Sbellard static void page_flush_tb(void) 217fd6ce8f6Sbellard { 218fd6ce8f6Sbellard int i, j; 219fd6ce8f6Sbellard PageDesc *p; 220fd6ce8f6Sbellard 221fd6ce8f6Sbellard for(i = 0; i < L1_SIZE; i++) { 222fd6ce8f6Sbellard p = l1_map[i]; 223fd6ce8f6Sbellard if (p) { 224fd6ce8f6Sbellard for(j = 0; j < L2_SIZE; j++) 225fd6ce8f6Sbellard p[j].first_tb = NULL; 226fd6ce8f6Sbellard } 227fd6ce8f6Sbellard } 228fd6ce8f6Sbellard } 229fd6ce8f6Sbellard 230fd6ce8f6Sbellard /* flush all the translation blocks */ 231d4e8164fSbellard /* XXX: tb_flush is currently not thread safe */ 232fd6ce8f6Sbellard void tb_flush(void) 233fd6ce8f6Sbellard { 234fd6ce8f6Sbellard int i; 235fd6ce8f6Sbellard #ifdef DEBUG_FLUSH 236fd6ce8f6Sbellard printf("qemu: flush code_size=%d nb_tbs=%d avg_tb_size=%d\n", 237fd6ce8f6Sbellard code_gen_ptr - code_gen_buffer, 238fd6ce8f6Sbellard nb_tbs, 239fd6ce8f6Sbellard (code_gen_ptr - code_gen_buffer) / nb_tbs); 240fd6ce8f6Sbellard #endif 241fd6ce8f6Sbellard nb_tbs = 0; 242fd6ce8f6Sbellard for(i = 0;i < CODE_GEN_HASH_SIZE; i++) 243fd6ce8f6Sbellard tb_hash[i] = NULL; 244fd6ce8f6Sbellard page_flush_tb(); 245fd6ce8f6Sbellard code_gen_ptr = code_gen_buffer; 246d4e8164fSbellard /* XXX: flush processor icache at this point if cache flush is 247d4e8164fSbellard expensive */ 248fd6ce8f6Sbellard } 249fd6ce8f6Sbellard 250fd6ce8f6Sbellard #ifdef DEBUG_TB_CHECK 251fd6ce8f6Sbellard 252fd6ce8f6Sbellard static void tb_invalidate_check(unsigned long address) 253fd6ce8f6Sbellard { 254fd6ce8f6Sbellard TranslationBlock *tb; 255fd6ce8f6Sbellard int i; 256fd6ce8f6Sbellard address &= TARGET_PAGE_MASK; 257fd6ce8f6Sbellard for(i = 0;i < CODE_GEN_HASH_SIZE; i++) { 258fd6ce8f6Sbellard for(tb = tb_hash[i]; tb != NULL; tb = tb->hash_next) { 259fd6ce8f6Sbellard if (!(address + TARGET_PAGE_SIZE <= tb->pc || 260fd6ce8f6Sbellard address >= tb->pc + tb->size)) { 261fd6ce8f6Sbellard printf("ERROR invalidate: address=%08lx PC=%08lx size=%04x\n", 262fd6ce8f6Sbellard address, tb->pc, tb->size); 263fd6ce8f6Sbellard } 264fd6ce8f6Sbellard } 265fd6ce8f6Sbellard } 266fd6ce8f6Sbellard } 267fd6ce8f6Sbellard 268fd6ce8f6Sbellard /* verify that all the pages have correct rights for code */ 269fd6ce8f6Sbellard static void tb_page_check(void) 270fd6ce8f6Sbellard { 271fd6ce8f6Sbellard TranslationBlock *tb; 272fd6ce8f6Sbellard int i, flags1, flags2; 273fd6ce8f6Sbellard 274fd6ce8f6Sbellard for(i = 0;i < CODE_GEN_HASH_SIZE; i++) { 275fd6ce8f6Sbellard for(tb = tb_hash[i]; tb != NULL; tb = tb->hash_next) { 276fd6ce8f6Sbellard flags1 = page_get_flags(tb->pc); 277fd6ce8f6Sbellard flags2 = page_get_flags(tb->pc + tb->size - 1); 278fd6ce8f6Sbellard if ((flags1 & PAGE_WRITE) || (flags2 & PAGE_WRITE)) { 279fd6ce8f6Sbellard printf("ERROR page flags: PC=%08lx size=%04x f1=%x f2=%x\n", 280fd6ce8f6Sbellard tb->pc, tb->size, flags1, flags2); 281fd6ce8f6Sbellard } 282fd6ce8f6Sbellard } 283fd6ce8f6Sbellard } 284fd6ce8f6Sbellard } 285fd6ce8f6Sbellard 286d4e8164fSbellard void tb_jmp_check(TranslationBlock *tb) 287d4e8164fSbellard { 288d4e8164fSbellard TranslationBlock *tb1; 289d4e8164fSbellard unsigned int n1; 290d4e8164fSbellard 291d4e8164fSbellard /* suppress any remaining jumps to this TB */ 292d4e8164fSbellard tb1 = tb->jmp_first; 293d4e8164fSbellard for(;;) { 294d4e8164fSbellard n1 = (long)tb1 & 3; 295d4e8164fSbellard tb1 = (TranslationBlock *)((long)tb1 & ~3); 296d4e8164fSbellard if (n1 == 2) 297d4e8164fSbellard break; 298d4e8164fSbellard tb1 = tb1->jmp_next[n1]; 299d4e8164fSbellard } 300d4e8164fSbellard /* check end of list */ 301d4e8164fSbellard if (tb1 != tb) { 302d4e8164fSbellard printf("ERROR: jmp_list from 0x%08lx\n", (long)tb); 303d4e8164fSbellard } 304d4e8164fSbellard } 305d4e8164fSbellard 306fd6ce8f6Sbellard #endif 307fd6ce8f6Sbellard 308fd6ce8f6Sbellard /* invalidate one TB */ 309fd6ce8f6Sbellard static inline void tb_remove(TranslationBlock **ptb, TranslationBlock *tb, 310fd6ce8f6Sbellard int next_offset) 311fd6ce8f6Sbellard { 312fd6ce8f6Sbellard TranslationBlock *tb1; 313fd6ce8f6Sbellard for(;;) { 314fd6ce8f6Sbellard tb1 = *ptb; 315fd6ce8f6Sbellard if (tb1 == tb) { 316fd6ce8f6Sbellard *ptb = *(TranslationBlock **)((char *)tb1 + next_offset); 317fd6ce8f6Sbellard break; 318fd6ce8f6Sbellard } 319fd6ce8f6Sbellard ptb = (TranslationBlock **)((char *)tb1 + next_offset); 320fd6ce8f6Sbellard } 321fd6ce8f6Sbellard } 322fd6ce8f6Sbellard 323d4e8164fSbellard static inline void tb_jmp_remove(TranslationBlock *tb, int n) 324d4e8164fSbellard { 325d4e8164fSbellard TranslationBlock *tb1, **ptb; 326d4e8164fSbellard unsigned int n1; 327d4e8164fSbellard 328d4e8164fSbellard ptb = &tb->jmp_next[n]; 329d4e8164fSbellard tb1 = *ptb; 330d4e8164fSbellard if (tb1) { 331d4e8164fSbellard /* find tb(n) in circular list */ 332d4e8164fSbellard for(;;) { 333d4e8164fSbellard tb1 = *ptb; 334d4e8164fSbellard n1 = (long)tb1 & 3; 335d4e8164fSbellard tb1 = (TranslationBlock *)((long)tb1 & ~3); 336d4e8164fSbellard if (n1 == n && tb1 == tb) 337d4e8164fSbellard break; 338d4e8164fSbellard if (n1 == 2) { 339d4e8164fSbellard ptb = &tb1->jmp_first; 340d4e8164fSbellard } else { 341d4e8164fSbellard ptb = &tb1->jmp_next[n1]; 342d4e8164fSbellard } 343d4e8164fSbellard } 344d4e8164fSbellard /* now we can suppress tb(n) from the list */ 345d4e8164fSbellard *ptb = tb->jmp_next[n]; 346d4e8164fSbellard 347d4e8164fSbellard tb->jmp_next[n] = NULL; 348d4e8164fSbellard } 349d4e8164fSbellard } 350d4e8164fSbellard 351d4e8164fSbellard /* reset the jump entry 'n' of a TB so that it is not chained to 352d4e8164fSbellard another TB */ 353d4e8164fSbellard static inline void tb_reset_jump(TranslationBlock *tb, int n) 354d4e8164fSbellard { 355d4e8164fSbellard tb_set_jmp_target(tb, n, (unsigned long)(tb->tc_ptr + tb->tb_next_offset[n])); 356d4e8164fSbellard } 357d4e8164fSbellard 358fd6ce8f6Sbellard static inline void tb_invalidate(TranslationBlock *tb, int parity) 359fd6ce8f6Sbellard { 360fd6ce8f6Sbellard PageDesc *p; 361fd6ce8f6Sbellard unsigned int page_index1, page_index2; 362d4e8164fSbellard unsigned int h, n1; 363d4e8164fSbellard TranslationBlock *tb1, *tb2; 364fd6ce8f6Sbellard 36536bdbe54Sbellard tb_invalidated_flag = 1; 36636bdbe54Sbellard 367fd6ce8f6Sbellard /* remove the TB from the hash list */ 368fd6ce8f6Sbellard h = tb_hash_func(tb->pc); 369fd6ce8f6Sbellard tb_remove(&tb_hash[h], tb, 370fd6ce8f6Sbellard offsetof(TranslationBlock, hash_next)); 371fd6ce8f6Sbellard /* remove the TB from the page list */ 372fd6ce8f6Sbellard page_index1 = tb->pc >> TARGET_PAGE_BITS; 373fd6ce8f6Sbellard if ((page_index1 & 1) == parity) { 374fd6ce8f6Sbellard p = page_find(page_index1); 375fd6ce8f6Sbellard tb_remove(&p->first_tb, tb, 376fd6ce8f6Sbellard offsetof(TranslationBlock, page_next[page_index1 & 1])); 377fd6ce8f6Sbellard } 378fd6ce8f6Sbellard page_index2 = (tb->pc + tb->size - 1) >> TARGET_PAGE_BITS; 379fd6ce8f6Sbellard if ((page_index2 & 1) == parity) { 380fd6ce8f6Sbellard p = page_find(page_index2); 381fd6ce8f6Sbellard tb_remove(&p->first_tb, tb, 382fd6ce8f6Sbellard offsetof(TranslationBlock, page_next[page_index2 & 1])); 383fd6ce8f6Sbellard } 384d4e8164fSbellard 385d4e8164fSbellard /* suppress this TB from the two jump lists */ 386d4e8164fSbellard tb_jmp_remove(tb, 0); 387d4e8164fSbellard tb_jmp_remove(tb, 1); 388d4e8164fSbellard 389d4e8164fSbellard /* suppress any remaining jumps to this TB */ 390d4e8164fSbellard tb1 = tb->jmp_first; 391d4e8164fSbellard for(;;) { 392d4e8164fSbellard n1 = (long)tb1 & 3; 393d4e8164fSbellard if (n1 == 2) 394d4e8164fSbellard break; 395d4e8164fSbellard tb1 = (TranslationBlock *)((long)tb1 & ~3); 396d4e8164fSbellard tb2 = tb1->jmp_next[n1]; 397d4e8164fSbellard tb_reset_jump(tb1, n1); 398d4e8164fSbellard tb1->jmp_next[n1] = NULL; 399d4e8164fSbellard tb1 = tb2; 400d4e8164fSbellard } 401d4e8164fSbellard tb->jmp_first = (TranslationBlock *)((long)tb | 2); /* fail safe */ 402fd6ce8f6Sbellard } 403fd6ce8f6Sbellard 404fd6ce8f6Sbellard /* invalidate all TBs which intersect with the target page starting at addr */ 405fd6ce8f6Sbellard static void tb_invalidate_page(unsigned long address) 406fd6ce8f6Sbellard { 407fd6ce8f6Sbellard TranslationBlock *tb_next, *tb; 408fd6ce8f6Sbellard unsigned int page_index; 409fd6ce8f6Sbellard int parity1, parity2; 410fd6ce8f6Sbellard PageDesc *p; 411fd6ce8f6Sbellard #ifdef DEBUG_TB_INVALIDATE 412fd6ce8f6Sbellard printf("tb_invalidate_page: %lx\n", address); 413fd6ce8f6Sbellard #endif 414fd6ce8f6Sbellard 415fd6ce8f6Sbellard page_index = address >> TARGET_PAGE_BITS; 416fd6ce8f6Sbellard p = page_find(page_index); 417fd6ce8f6Sbellard if (!p) 418fd6ce8f6Sbellard return; 419fd6ce8f6Sbellard tb = p->first_tb; 420fd6ce8f6Sbellard parity1 = page_index & 1; 421fd6ce8f6Sbellard parity2 = parity1 ^ 1; 422fd6ce8f6Sbellard while (tb != NULL) { 423fd6ce8f6Sbellard tb_next = tb->page_next[parity1]; 424fd6ce8f6Sbellard tb_invalidate(tb, parity2); 425fd6ce8f6Sbellard tb = tb_next; 426fd6ce8f6Sbellard } 427fd6ce8f6Sbellard p->first_tb = NULL; 428fd6ce8f6Sbellard } 429fd6ce8f6Sbellard 430fd6ce8f6Sbellard /* add the tb in the target page and protect it if necessary */ 431fd6ce8f6Sbellard static inline void tb_alloc_page(TranslationBlock *tb, unsigned int page_index) 432fd6ce8f6Sbellard { 433fd6ce8f6Sbellard PageDesc *p; 434fd6ce8f6Sbellard unsigned long host_start, host_end, addr, page_addr; 435fd6ce8f6Sbellard int prot; 436fd6ce8f6Sbellard 437fd6ce8f6Sbellard p = page_find_alloc(page_index); 438fd6ce8f6Sbellard tb->page_next[page_index & 1] = p->first_tb; 439fd6ce8f6Sbellard p->first_tb = tb; 440fd6ce8f6Sbellard if (p->flags & PAGE_WRITE) { 441fd6ce8f6Sbellard /* force the host page as non writable (writes will have a 442fd6ce8f6Sbellard page fault + mprotect overhead) */ 443fd6ce8f6Sbellard page_addr = (page_index << TARGET_PAGE_BITS); 444fd6ce8f6Sbellard host_start = page_addr & host_page_mask; 445fd6ce8f6Sbellard host_end = host_start + host_page_size; 446fd6ce8f6Sbellard prot = 0; 447fd6ce8f6Sbellard for(addr = host_start; addr < host_end; addr += TARGET_PAGE_SIZE) 448fd6ce8f6Sbellard prot |= page_get_flags(addr); 44961382a50Sbellard #if !defined(CONFIG_SOFTMMU) 450fd6ce8f6Sbellard mprotect((void *)host_start, host_page_size, 451fd6ce8f6Sbellard (prot & PAGE_BITS) & ~PAGE_WRITE); 45261382a50Sbellard #endif 45361382a50Sbellard #if !defined(CONFIG_USER_ONLY) 45461382a50Sbellard /* suppress soft TLB */ 45561382a50Sbellard /* XXX: must flush on all processor with same address space */ 45661382a50Sbellard tlb_flush_page_write(cpu_single_env, host_start); 45761382a50Sbellard #endif 458fd6ce8f6Sbellard #ifdef DEBUG_TB_INVALIDATE 459fd6ce8f6Sbellard printf("protecting code page: 0x%08lx\n", 460fd6ce8f6Sbellard host_start); 461fd6ce8f6Sbellard #endif 462fd6ce8f6Sbellard p->flags &= ~PAGE_WRITE; 463fd6ce8f6Sbellard } 464fd6ce8f6Sbellard } 465fd6ce8f6Sbellard 466fd6ce8f6Sbellard /* Allocate a new translation block. Flush the translation buffer if 467fd6ce8f6Sbellard too many translation blocks or too much generated code. */ 468d4e8164fSbellard TranslationBlock *tb_alloc(unsigned long pc) 469fd6ce8f6Sbellard { 470fd6ce8f6Sbellard TranslationBlock *tb; 471fd6ce8f6Sbellard 472fd6ce8f6Sbellard if (nb_tbs >= CODE_GEN_MAX_BLOCKS || 473fd6ce8f6Sbellard (code_gen_ptr - code_gen_buffer) >= CODE_GEN_BUFFER_MAX_SIZE) 474d4e8164fSbellard return NULL; 475fd6ce8f6Sbellard tb = &tbs[nb_tbs++]; 476fd6ce8f6Sbellard tb->pc = pc; 477d4e8164fSbellard return tb; 478d4e8164fSbellard } 479d4e8164fSbellard 480d4e8164fSbellard /* link the tb with the other TBs */ 481d4e8164fSbellard void tb_link(TranslationBlock *tb) 482d4e8164fSbellard { 483d4e8164fSbellard unsigned int page_index1, page_index2; 484fd6ce8f6Sbellard 485fd6ce8f6Sbellard /* add in the page list */ 486d4e8164fSbellard page_index1 = tb->pc >> TARGET_PAGE_BITS; 487fd6ce8f6Sbellard tb_alloc_page(tb, page_index1); 488d4e8164fSbellard page_index2 = (tb->pc + tb->size - 1) >> TARGET_PAGE_BITS; 489fd6ce8f6Sbellard if (page_index2 != page_index1) { 490fd6ce8f6Sbellard tb_alloc_page(tb, page_index2); 491fd6ce8f6Sbellard } 49261382a50Sbellard #ifdef DEBUG_TB_CHECK 49361382a50Sbellard tb_page_check(); 49461382a50Sbellard #endif 495d4e8164fSbellard tb->jmp_first = (TranslationBlock *)((long)tb | 2); 496d4e8164fSbellard tb->jmp_next[0] = NULL; 497d4e8164fSbellard tb->jmp_next[1] = NULL; 498d4e8164fSbellard 499d4e8164fSbellard /* init original jump addresses */ 500d4e8164fSbellard if (tb->tb_next_offset[0] != 0xffff) 501d4e8164fSbellard tb_reset_jump(tb, 0); 502d4e8164fSbellard if (tb->tb_next_offset[1] != 0xffff) 503d4e8164fSbellard tb_reset_jump(tb, 1); 504fd6ce8f6Sbellard } 505fd6ce8f6Sbellard 506fd6ce8f6Sbellard /* called from signal handler: invalidate the code and unprotect the 507fd6ce8f6Sbellard page. Return TRUE if the fault was succesfully handled. */ 508fd6ce8f6Sbellard int page_unprotect(unsigned long address) 509fd6ce8f6Sbellard { 5101565b7bcSbellard unsigned int page_index, prot, pindex; 5111565b7bcSbellard PageDesc *p, *p1; 512fd6ce8f6Sbellard unsigned long host_start, host_end, addr; 513fd6ce8f6Sbellard 5141565b7bcSbellard host_start = address & host_page_mask; 5151565b7bcSbellard page_index = host_start >> TARGET_PAGE_BITS; 5161565b7bcSbellard p1 = page_find(page_index); 5171565b7bcSbellard if (!p1) 518fd6ce8f6Sbellard return 0; 5191565b7bcSbellard host_end = host_start + host_page_size; 5201565b7bcSbellard p = p1; 5211565b7bcSbellard prot = 0; 5221565b7bcSbellard for(addr = host_start;addr < host_end; addr += TARGET_PAGE_SIZE) { 5231565b7bcSbellard prot |= p->flags; 5241565b7bcSbellard p++; 5251565b7bcSbellard } 526fd6ce8f6Sbellard /* if the page was really writable, then we change its 527fd6ce8f6Sbellard protection back to writable */ 5281565b7bcSbellard if (prot & PAGE_WRITE_ORG) { 52961382a50Sbellard pindex = (address - host_start) >> TARGET_PAGE_BITS; 53061382a50Sbellard if (!(p1[pindex].flags & PAGE_WRITE)) { 53161382a50Sbellard #if !defined(CONFIG_SOFTMMU) 532fd6ce8f6Sbellard mprotect((void *)host_start, host_page_size, 533fd6ce8f6Sbellard (prot & PAGE_BITS) | PAGE_WRITE); 53461382a50Sbellard #endif 5351565b7bcSbellard p1[pindex].flags |= PAGE_WRITE; 536fd6ce8f6Sbellard /* and since the content will be modified, we must invalidate 537fd6ce8f6Sbellard the corresponding translated code. */ 538fd6ce8f6Sbellard tb_invalidate_page(address); 539fd6ce8f6Sbellard #ifdef DEBUG_TB_CHECK 540fd6ce8f6Sbellard tb_invalidate_check(address); 541fd6ce8f6Sbellard #endif 542fd6ce8f6Sbellard return 1; 543fd6ce8f6Sbellard } 544fd6ce8f6Sbellard } 54561382a50Sbellard return 0; 54661382a50Sbellard } 547fd6ce8f6Sbellard 548fd6ce8f6Sbellard /* call this function when system calls directly modify a memory area */ 549fd6ce8f6Sbellard void page_unprotect_range(uint8_t *data, unsigned long data_size) 550fd6ce8f6Sbellard { 551fd6ce8f6Sbellard unsigned long start, end, addr; 552fd6ce8f6Sbellard 553fd6ce8f6Sbellard start = (unsigned long)data; 554fd6ce8f6Sbellard end = start + data_size; 555fd6ce8f6Sbellard start &= TARGET_PAGE_MASK; 556fd6ce8f6Sbellard end = TARGET_PAGE_ALIGN(end); 557fd6ce8f6Sbellard for(addr = start; addr < end; addr += TARGET_PAGE_SIZE) { 558fd6ce8f6Sbellard page_unprotect(addr); 559fd6ce8f6Sbellard } 560fd6ce8f6Sbellard } 561a513fe19Sbellard 562a513fe19Sbellard /* find the TB 'tb' such that tb[0].tc_ptr <= tc_ptr < 563a513fe19Sbellard tb[1].tc_ptr. Return NULL if not found */ 564a513fe19Sbellard TranslationBlock *tb_find_pc(unsigned long tc_ptr) 565a513fe19Sbellard { 566a513fe19Sbellard int m_min, m_max, m; 567a513fe19Sbellard unsigned long v; 568a513fe19Sbellard TranslationBlock *tb; 569a513fe19Sbellard 570a513fe19Sbellard if (nb_tbs <= 0) 571a513fe19Sbellard return NULL; 572a513fe19Sbellard if (tc_ptr < (unsigned long)code_gen_buffer || 573a513fe19Sbellard tc_ptr >= (unsigned long)code_gen_ptr) 574a513fe19Sbellard return NULL; 575a513fe19Sbellard /* binary search (cf Knuth) */ 576a513fe19Sbellard m_min = 0; 577a513fe19Sbellard m_max = nb_tbs - 1; 578a513fe19Sbellard while (m_min <= m_max) { 579a513fe19Sbellard m = (m_min + m_max) >> 1; 580a513fe19Sbellard tb = &tbs[m]; 581a513fe19Sbellard v = (unsigned long)tb->tc_ptr; 582a513fe19Sbellard if (v == tc_ptr) 583a513fe19Sbellard return tb; 584a513fe19Sbellard else if (tc_ptr < v) { 585a513fe19Sbellard m_max = m - 1; 586a513fe19Sbellard } else { 587a513fe19Sbellard m_min = m + 1; 588a513fe19Sbellard } 589a513fe19Sbellard } 590a513fe19Sbellard return &tbs[m_max]; 591a513fe19Sbellard } 5927501267eSbellard 593ea041c0eSbellard static void tb_reset_jump_recursive(TranslationBlock *tb); 594ea041c0eSbellard 595ea041c0eSbellard static inline void tb_reset_jump_recursive2(TranslationBlock *tb, int n) 596ea041c0eSbellard { 597ea041c0eSbellard TranslationBlock *tb1, *tb_next, **ptb; 598ea041c0eSbellard unsigned int n1; 599ea041c0eSbellard 600ea041c0eSbellard tb1 = tb->jmp_next[n]; 601ea041c0eSbellard if (tb1 != NULL) { 602ea041c0eSbellard /* find head of list */ 603ea041c0eSbellard for(;;) { 604ea041c0eSbellard n1 = (long)tb1 & 3; 605ea041c0eSbellard tb1 = (TranslationBlock *)((long)tb1 & ~3); 606ea041c0eSbellard if (n1 == 2) 607ea041c0eSbellard break; 608ea041c0eSbellard tb1 = tb1->jmp_next[n1]; 609ea041c0eSbellard } 610ea041c0eSbellard /* we are now sure now that tb jumps to tb1 */ 611ea041c0eSbellard tb_next = tb1; 612ea041c0eSbellard 613ea041c0eSbellard /* remove tb from the jmp_first list */ 614ea041c0eSbellard ptb = &tb_next->jmp_first; 615ea041c0eSbellard for(;;) { 616ea041c0eSbellard tb1 = *ptb; 617ea041c0eSbellard n1 = (long)tb1 & 3; 618ea041c0eSbellard tb1 = (TranslationBlock *)((long)tb1 & ~3); 619ea041c0eSbellard if (n1 == n && tb1 == tb) 620ea041c0eSbellard break; 621ea041c0eSbellard ptb = &tb1->jmp_next[n1]; 622ea041c0eSbellard } 623ea041c0eSbellard *ptb = tb->jmp_next[n]; 624ea041c0eSbellard tb->jmp_next[n] = NULL; 625ea041c0eSbellard 626ea041c0eSbellard /* suppress the jump to next tb in generated code */ 627ea041c0eSbellard tb_reset_jump(tb, n); 628ea041c0eSbellard 629ea041c0eSbellard /* suppress jumps in the tb on which we could have jump */ 630ea041c0eSbellard tb_reset_jump_recursive(tb_next); 631ea041c0eSbellard } 632ea041c0eSbellard } 633ea041c0eSbellard 634ea041c0eSbellard static void tb_reset_jump_recursive(TranslationBlock *tb) 635ea041c0eSbellard { 636ea041c0eSbellard tb_reset_jump_recursive2(tb, 0); 637ea041c0eSbellard tb_reset_jump_recursive2(tb, 1); 638ea041c0eSbellard } 639ea041c0eSbellard 640c33a346eSbellard /* add a breakpoint. EXCP_DEBUG is returned by the CPU loop if a 641c33a346eSbellard breakpoint is reached */ 6424c3a88a2Sbellard int cpu_breakpoint_insert(CPUState *env, uint32_t pc) 6434c3a88a2Sbellard { 6444c3a88a2Sbellard #if defined(TARGET_I386) 6454c3a88a2Sbellard int i; 6464c3a88a2Sbellard 6474c3a88a2Sbellard for(i = 0; i < env->nb_breakpoints; i++) { 6484c3a88a2Sbellard if (env->breakpoints[i] == pc) 6494c3a88a2Sbellard return 0; 6504c3a88a2Sbellard } 6514c3a88a2Sbellard 6524c3a88a2Sbellard if (env->nb_breakpoints >= MAX_BREAKPOINTS) 6534c3a88a2Sbellard return -1; 6544c3a88a2Sbellard env->breakpoints[env->nb_breakpoints++] = pc; 6554c3a88a2Sbellard tb_invalidate_page(pc); 6564c3a88a2Sbellard return 0; 6574c3a88a2Sbellard #else 6584c3a88a2Sbellard return -1; 6594c3a88a2Sbellard #endif 6604c3a88a2Sbellard } 6614c3a88a2Sbellard 6624c3a88a2Sbellard /* remove a breakpoint */ 6634c3a88a2Sbellard int cpu_breakpoint_remove(CPUState *env, uint32_t pc) 6644c3a88a2Sbellard { 6654c3a88a2Sbellard #if defined(TARGET_I386) 6664c3a88a2Sbellard int i; 6674c3a88a2Sbellard for(i = 0; i < env->nb_breakpoints; i++) { 6684c3a88a2Sbellard if (env->breakpoints[i] == pc) 6694c3a88a2Sbellard goto found; 6704c3a88a2Sbellard } 6714c3a88a2Sbellard return -1; 6724c3a88a2Sbellard found: 6734c3a88a2Sbellard memmove(&env->breakpoints[i], &env->breakpoints[i + 1], 6744c3a88a2Sbellard (env->nb_breakpoints - (i + 1)) * sizeof(env->breakpoints[0])); 6754c3a88a2Sbellard env->nb_breakpoints--; 6764c3a88a2Sbellard tb_invalidate_page(pc); 6774c3a88a2Sbellard return 0; 6784c3a88a2Sbellard #else 6794c3a88a2Sbellard return -1; 6804c3a88a2Sbellard #endif 6814c3a88a2Sbellard } 6824c3a88a2Sbellard 683c33a346eSbellard /* enable or disable single step mode. EXCP_DEBUG is returned by the 684c33a346eSbellard CPU loop after each instruction */ 685c33a346eSbellard void cpu_single_step(CPUState *env, int enabled) 686c33a346eSbellard { 687c33a346eSbellard #if defined(TARGET_I386) 688c33a346eSbellard if (env->singlestep_enabled != enabled) { 689c33a346eSbellard env->singlestep_enabled = enabled; 690c33a346eSbellard /* must flush all the translated code to avoid inconsistancies */ 691c33a346eSbellard tb_flush(); 692c33a346eSbellard } 693c33a346eSbellard #endif 694c33a346eSbellard } 695c33a346eSbellard 69634865134Sbellard /* enable or disable low levels log */ 69734865134Sbellard void cpu_set_log(int log_flags) 69834865134Sbellard { 69934865134Sbellard loglevel = log_flags; 70034865134Sbellard if (loglevel && !logfile) { 70134865134Sbellard logfile = fopen(logfilename, "w"); 70234865134Sbellard if (!logfile) { 70334865134Sbellard perror(logfilename); 70434865134Sbellard _exit(1); 70534865134Sbellard } 70634865134Sbellard setvbuf(logfile, NULL, _IOLBF, 0); 70734865134Sbellard } 70834865134Sbellard } 70934865134Sbellard 71034865134Sbellard void cpu_set_log_filename(const char *filename) 71134865134Sbellard { 71234865134Sbellard logfilename = strdup(filename); 71334865134Sbellard } 714c33a346eSbellard 71568a79315Sbellard /* mask must never be zero */ 71668a79315Sbellard void cpu_interrupt(CPUState *env, int mask) 717ea041c0eSbellard { 718ea041c0eSbellard TranslationBlock *tb; 719ea041c0eSbellard 72068a79315Sbellard env->interrupt_request |= mask; 721ea041c0eSbellard /* if the cpu is currently executing code, we must unlink it and 722ea041c0eSbellard all the potentially executing TB */ 723ea041c0eSbellard tb = env->current_tb; 724ea041c0eSbellard if (tb) { 725ea041c0eSbellard tb_reset_jump_recursive(tb); 726ea041c0eSbellard } 727ea041c0eSbellard } 728ea041c0eSbellard 729ea041c0eSbellard 7307501267eSbellard void cpu_abort(CPUState *env, const char *fmt, ...) 7317501267eSbellard { 7327501267eSbellard va_list ap; 7337501267eSbellard 7347501267eSbellard va_start(ap, fmt); 7357501267eSbellard fprintf(stderr, "qemu: fatal: "); 7367501267eSbellard vfprintf(stderr, fmt, ap); 7377501267eSbellard fprintf(stderr, "\n"); 7387501267eSbellard #ifdef TARGET_I386 7397501267eSbellard cpu_x86_dump_state(env, stderr, X86_DUMP_FPU | X86_DUMP_CCOP); 7407501267eSbellard #endif 7417501267eSbellard va_end(ap); 7427501267eSbellard abort(); 7437501267eSbellard } 7447501267eSbellard 74566e85a21Sbellard #ifdef TARGET_I386 74666e85a21Sbellard /* unmap all maped pages and flush all associated code */ 74766e85a21Sbellard void page_unmap(void) 74866e85a21Sbellard { 74961382a50Sbellard PageDesc *pmap; 75061382a50Sbellard int i; 75166e85a21Sbellard 75266e85a21Sbellard for(i = 0; i < L1_SIZE; i++) { 75366e85a21Sbellard pmap = l1_map[i]; 75466e85a21Sbellard if (pmap) { 75561382a50Sbellard #if !defined(CONFIG_SOFTMMU) 75661382a50Sbellard PageDesc *p; 75761382a50Sbellard unsigned long addr; 75861382a50Sbellard int j, ret, j1; 75961382a50Sbellard 76066e85a21Sbellard p = pmap; 7617c2d6a78Sbellard for(j = 0;j < L2_SIZE;) { 76266e85a21Sbellard if (p->flags & PAGE_VALID) { 76366e85a21Sbellard addr = (i << (32 - L1_BITS)) | (j << TARGET_PAGE_BITS); 7647c2d6a78Sbellard /* we try to find a range to make less syscalls */ 7657c2d6a78Sbellard j1 = j; 7667c2d6a78Sbellard p++; 7677c2d6a78Sbellard j++; 7687c2d6a78Sbellard while (j < L2_SIZE && (p->flags & PAGE_VALID)) { 7697c2d6a78Sbellard p++; 7707c2d6a78Sbellard j++; 7717c2d6a78Sbellard } 7727c2d6a78Sbellard ret = munmap((void *)addr, (j - j1) << TARGET_PAGE_BITS); 77366e85a21Sbellard if (ret != 0) { 77466e85a21Sbellard fprintf(stderr, "Could not unmap page 0x%08lx\n", addr); 77566e85a21Sbellard exit(1); 77666e85a21Sbellard } 7777c2d6a78Sbellard } else { 77866e85a21Sbellard p++; 7797c2d6a78Sbellard j++; 7807c2d6a78Sbellard } 78166e85a21Sbellard } 78261382a50Sbellard #endif 78366e85a21Sbellard free(pmap); 78466e85a21Sbellard l1_map[i] = NULL; 78566e85a21Sbellard } 78666e85a21Sbellard } 78766e85a21Sbellard tb_flush(); 78866e85a21Sbellard } 78966e85a21Sbellard #endif 79033417e70Sbellard 79133417e70Sbellard void tlb_flush(CPUState *env) 79233417e70Sbellard { 79361382a50Sbellard #if !defined(CONFIG_USER_ONLY) 79433417e70Sbellard int i; 79533417e70Sbellard for(i = 0; i < CPU_TLB_SIZE; i++) { 79633417e70Sbellard env->tlb_read[0][i].address = -1; 79733417e70Sbellard env->tlb_write[0][i].address = -1; 79833417e70Sbellard env->tlb_read[1][i].address = -1; 79933417e70Sbellard env->tlb_write[1][i].address = -1; 80033417e70Sbellard } 80133417e70Sbellard #endif 80233417e70Sbellard } 80333417e70Sbellard 80461382a50Sbellard static inline void tlb_flush_entry(CPUTLBEntry *tlb_entry, uint32_t addr) 80561382a50Sbellard { 80661382a50Sbellard if (addr == (tlb_entry->address & 80761382a50Sbellard (TARGET_PAGE_MASK | TLB_INVALID_MASK))) 80861382a50Sbellard tlb_entry->address = -1; 80961382a50Sbellard } 81061382a50Sbellard 81133417e70Sbellard void tlb_flush_page(CPUState *env, uint32_t addr) 81233417e70Sbellard { 81361382a50Sbellard #if !defined(CONFIG_USER_ONLY) 81433417e70Sbellard int i; 81533417e70Sbellard 81661382a50Sbellard addr &= TARGET_PAGE_MASK; 81733417e70Sbellard i = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); 81861382a50Sbellard tlb_flush_entry(&env->tlb_read[0][i], addr); 81961382a50Sbellard tlb_flush_entry(&env->tlb_write[0][i], addr); 82061382a50Sbellard tlb_flush_entry(&env->tlb_read[1][i], addr); 82161382a50Sbellard tlb_flush_entry(&env->tlb_write[1][i], addr); 82261382a50Sbellard #endif 82361382a50Sbellard } 82461382a50Sbellard 82561382a50Sbellard /* make all write to page 'addr' trigger a TLB exception to detect 82661382a50Sbellard self modifying code */ 82761382a50Sbellard void tlb_flush_page_write(CPUState *env, uint32_t addr) 82861382a50Sbellard { 82961382a50Sbellard #if !defined(CONFIG_USER_ONLY) 83061382a50Sbellard int i; 83161382a50Sbellard 83261382a50Sbellard addr &= TARGET_PAGE_MASK; 83361382a50Sbellard i = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); 83461382a50Sbellard tlb_flush_entry(&env->tlb_write[0][i], addr); 83561382a50Sbellard tlb_flush_entry(&env->tlb_write[1][i], addr); 83633417e70Sbellard #endif 83733417e70Sbellard } 83833417e70Sbellard 83933417e70Sbellard static inline unsigned long *physpage_find_alloc(unsigned int page) 84033417e70Sbellard { 84133417e70Sbellard unsigned long **lp, *p; 84233417e70Sbellard unsigned int index, i; 84333417e70Sbellard 84433417e70Sbellard index = page >> TARGET_PAGE_BITS; 84533417e70Sbellard lp = &l1_physmap[index >> L2_BITS]; 84633417e70Sbellard p = *lp; 84733417e70Sbellard if (!p) { 84833417e70Sbellard /* allocate if not found */ 84933417e70Sbellard p = malloc(sizeof(unsigned long) * L2_SIZE); 85033417e70Sbellard for(i = 0; i < L2_SIZE; i++) 85133417e70Sbellard p[i] = IO_MEM_UNASSIGNED; 85233417e70Sbellard *lp = p; 85333417e70Sbellard } 85433417e70Sbellard return p + (index & (L2_SIZE - 1)); 85533417e70Sbellard } 85633417e70Sbellard 85733417e70Sbellard /* return NULL if no page defined (unused memory) */ 85833417e70Sbellard unsigned long physpage_find(unsigned long page) 85933417e70Sbellard { 86033417e70Sbellard unsigned long *p; 86133417e70Sbellard unsigned int index; 86233417e70Sbellard index = page >> TARGET_PAGE_BITS; 86333417e70Sbellard p = l1_physmap[index >> L2_BITS]; 86433417e70Sbellard if (!p) 86533417e70Sbellard return IO_MEM_UNASSIGNED; 86633417e70Sbellard return p[index & (L2_SIZE - 1)]; 86733417e70Sbellard } 86833417e70Sbellard 86933417e70Sbellard /* register physical memory. 'size' must be a multiple of the target 87033417e70Sbellard page size. If (phys_offset & ~TARGET_PAGE_MASK) != 0, then it is an 87133417e70Sbellard io memory page */ 87233417e70Sbellard void cpu_register_physical_memory(unsigned long start_addr, unsigned long size, 87333417e70Sbellard long phys_offset) 87433417e70Sbellard { 87533417e70Sbellard unsigned long addr, end_addr; 87633417e70Sbellard unsigned long *p; 87733417e70Sbellard 87833417e70Sbellard end_addr = start_addr + size; 87933417e70Sbellard for(addr = start_addr; addr < end_addr; addr += TARGET_PAGE_SIZE) { 88033417e70Sbellard p = physpage_find_alloc(addr); 88133417e70Sbellard *p = phys_offset; 88233417e70Sbellard if ((phys_offset & ~TARGET_PAGE_MASK) == 0) 88333417e70Sbellard phys_offset += TARGET_PAGE_SIZE; 88433417e70Sbellard } 88533417e70Sbellard } 88633417e70Sbellard 88733417e70Sbellard static uint32_t unassigned_mem_readb(uint32_t addr) 88833417e70Sbellard { 88933417e70Sbellard return 0; 89033417e70Sbellard } 89133417e70Sbellard 89233417e70Sbellard static void unassigned_mem_writeb(uint32_t addr, uint32_t val) 89333417e70Sbellard { 89433417e70Sbellard } 89533417e70Sbellard 89633417e70Sbellard static CPUReadMemoryFunc *unassigned_mem_read[3] = { 89733417e70Sbellard unassigned_mem_readb, 89833417e70Sbellard unassigned_mem_readb, 89933417e70Sbellard unassigned_mem_readb, 90033417e70Sbellard }; 90133417e70Sbellard 90233417e70Sbellard static CPUWriteMemoryFunc *unassigned_mem_write[3] = { 90333417e70Sbellard unassigned_mem_writeb, 90433417e70Sbellard unassigned_mem_writeb, 90533417e70Sbellard unassigned_mem_writeb, 90633417e70Sbellard }; 90733417e70Sbellard 90833417e70Sbellard 90933417e70Sbellard static void io_mem_init(void) 91033417e70Sbellard { 91133417e70Sbellard io_mem_nb = 1; 91233417e70Sbellard cpu_register_io_memory(0, unassigned_mem_read, unassigned_mem_write); 91333417e70Sbellard } 91433417e70Sbellard 91533417e70Sbellard /* mem_read and mem_write are arrays of functions containing the 91633417e70Sbellard function to access byte (index 0), word (index 1) and dword (index 91733417e70Sbellard 2). All functions must be supplied. If io_index is non zero, the 91833417e70Sbellard corresponding io zone is modified. If it is zero, a new io zone is 91933417e70Sbellard allocated. The return value can be used with 92033417e70Sbellard cpu_register_physical_memory(). (-1) is returned if error. */ 92133417e70Sbellard int cpu_register_io_memory(int io_index, 92233417e70Sbellard CPUReadMemoryFunc **mem_read, 92333417e70Sbellard CPUWriteMemoryFunc **mem_write) 92433417e70Sbellard { 92533417e70Sbellard int i; 92633417e70Sbellard 92733417e70Sbellard if (io_index <= 0) { 92833417e70Sbellard if (io_index >= IO_MEM_NB_ENTRIES) 92933417e70Sbellard return -1; 93033417e70Sbellard io_index = io_mem_nb++; 93133417e70Sbellard } else { 93233417e70Sbellard if (io_index >= IO_MEM_NB_ENTRIES) 93333417e70Sbellard return -1; 93433417e70Sbellard } 93533417e70Sbellard 93633417e70Sbellard for(i = 0;i < 3; i++) { 93733417e70Sbellard io_mem_read[io_index][i] = mem_read[i]; 93833417e70Sbellard io_mem_write[io_index][i] = mem_write[i]; 93933417e70Sbellard } 94033417e70Sbellard return io_index << IO_MEM_SHIFT; 94133417e70Sbellard } 94261382a50Sbellard 94361382a50Sbellard #if !defined(CONFIG_USER_ONLY) 94461382a50Sbellard 94561382a50Sbellard #define MMUSUFFIX _cmmu 94661382a50Sbellard #define GETPC() NULL 94761382a50Sbellard #define env cpu_single_env 94861382a50Sbellard 94961382a50Sbellard #define SHIFT 0 95061382a50Sbellard #include "softmmu_template.h" 95161382a50Sbellard 95261382a50Sbellard #define SHIFT 1 95361382a50Sbellard #include "softmmu_template.h" 95461382a50Sbellard 95561382a50Sbellard #define SHIFT 2 95661382a50Sbellard #include "softmmu_template.h" 95761382a50Sbellard 95861382a50Sbellard #define SHIFT 3 95961382a50Sbellard #include "softmmu_template.h" 96061382a50Sbellard 96161382a50Sbellard #undef env 96261382a50Sbellard 96361382a50Sbellard #endif 964