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 365fd6ce8f6Sbellard /* remove the TB from the hash list */ 366fd6ce8f6Sbellard h = tb_hash_func(tb->pc); 367fd6ce8f6Sbellard tb_remove(&tb_hash[h], tb, 368fd6ce8f6Sbellard offsetof(TranslationBlock, hash_next)); 369fd6ce8f6Sbellard /* remove the TB from the page list */ 370fd6ce8f6Sbellard page_index1 = tb->pc >> TARGET_PAGE_BITS; 371fd6ce8f6Sbellard if ((page_index1 & 1) == parity) { 372fd6ce8f6Sbellard p = page_find(page_index1); 373fd6ce8f6Sbellard tb_remove(&p->first_tb, tb, 374fd6ce8f6Sbellard offsetof(TranslationBlock, page_next[page_index1 & 1])); 375fd6ce8f6Sbellard } 376fd6ce8f6Sbellard page_index2 = (tb->pc + tb->size - 1) >> TARGET_PAGE_BITS; 377fd6ce8f6Sbellard if ((page_index2 & 1) == parity) { 378fd6ce8f6Sbellard p = page_find(page_index2); 379fd6ce8f6Sbellard tb_remove(&p->first_tb, tb, 380fd6ce8f6Sbellard offsetof(TranslationBlock, page_next[page_index2 & 1])); 381fd6ce8f6Sbellard } 382d4e8164fSbellard 383d4e8164fSbellard /* suppress this TB from the two jump lists */ 384d4e8164fSbellard tb_jmp_remove(tb, 0); 385d4e8164fSbellard tb_jmp_remove(tb, 1); 386d4e8164fSbellard 387d4e8164fSbellard /* suppress any remaining jumps to this TB */ 388d4e8164fSbellard tb1 = tb->jmp_first; 389d4e8164fSbellard for(;;) { 390d4e8164fSbellard n1 = (long)tb1 & 3; 391d4e8164fSbellard if (n1 == 2) 392d4e8164fSbellard break; 393d4e8164fSbellard tb1 = (TranslationBlock *)((long)tb1 & ~3); 394d4e8164fSbellard tb2 = tb1->jmp_next[n1]; 395d4e8164fSbellard tb_reset_jump(tb1, n1); 396d4e8164fSbellard tb1->jmp_next[n1] = NULL; 397d4e8164fSbellard tb1 = tb2; 398d4e8164fSbellard } 399d4e8164fSbellard tb->jmp_first = (TranslationBlock *)((long)tb | 2); /* fail safe */ 400fd6ce8f6Sbellard } 401fd6ce8f6Sbellard 402fd6ce8f6Sbellard /* invalidate all TBs which intersect with the target page starting at addr */ 403fd6ce8f6Sbellard static void tb_invalidate_page(unsigned long address) 404fd6ce8f6Sbellard { 405fd6ce8f6Sbellard TranslationBlock *tb_next, *tb; 406fd6ce8f6Sbellard unsigned int page_index; 407fd6ce8f6Sbellard int parity1, parity2; 408fd6ce8f6Sbellard PageDesc *p; 409fd6ce8f6Sbellard #ifdef DEBUG_TB_INVALIDATE 410fd6ce8f6Sbellard printf("tb_invalidate_page: %lx\n", address); 411fd6ce8f6Sbellard #endif 412fd6ce8f6Sbellard 413fd6ce8f6Sbellard page_index = address >> TARGET_PAGE_BITS; 414fd6ce8f6Sbellard p = page_find(page_index); 415fd6ce8f6Sbellard if (!p) 416fd6ce8f6Sbellard return; 417fd6ce8f6Sbellard tb = p->first_tb; 418fd6ce8f6Sbellard parity1 = page_index & 1; 419fd6ce8f6Sbellard parity2 = parity1 ^ 1; 420fd6ce8f6Sbellard while (tb != NULL) { 421fd6ce8f6Sbellard tb_next = tb->page_next[parity1]; 422fd6ce8f6Sbellard tb_invalidate(tb, parity2); 423fd6ce8f6Sbellard tb = tb_next; 424fd6ce8f6Sbellard } 425fd6ce8f6Sbellard p->first_tb = NULL; 426fd6ce8f6Sbellard } 427fd6ce8f6Sbellard 428fd6ce8f6Sbellard /* add the tb in the target page and protect it if necessary */ 429fd6ce8f6Sbellard static inline void tb_alloc_page(TranslationBlock *tb, unsigned int page_index) 430fd6ce8f6Sbellard { 431fd6ce8f6Sbellard PageDesc *p; 432fd6ce8f6Sbellard unsigned long host_start, host_end, addr, page_addr; 433fd6ce8f6Sbellard int prot; 434fd6ce8f6Sbellard 435fd6ce8f6Sbellard p = page_find_alloc(page_index); 436fd6ce8f6Sbellard tb->page_next[page_index & 1] = p->first_tb; 437fd6ce8f6Sbellard p->first_tb = tb; 438fd6ce8f6Sbellard if (p->flags & PAGE_WRITE) { 439fd6ce8f6Sbellard /* force the host page as non writable (writes will have a 440fd6ce8f6Sbellard page fault + mprotect overhead) */ 441fd6ce8f6Sbellard page_addr = (page_index << TARGET_PAGE_BITS); 442fd6ce8f6Sbellard host_start = page_addr & host_page_mask; 443fd6ce8f6Sbellard host_end = host_start + host_page_size; 444fd6ce8f6Sbellard prot = 0; 445fd6ce8f6Sbellard for(addr = host_start; addr < host_end; addr += TARGET_PAGE_SIZE) 446fd6ce8f6Sbellard prot |= page_get_flags(addr); 44761382a50Sbellard #if !defined(CONFIG_SOFTMMU) 448fd6ce8f6Sbellard mprotect((void *)host_start, host_page_size, 449fd6ce8f6Sbellard (prot & PAGE_BITS) & ~PAGE_WRITE); 45061382a50Sbellard #endif 45161382a50Sbellard #if !defined(CONFIG_USER_ONLY) 45261382a50Sbellard /* suppress soft TLB */ 45361382a50Sbellard /* XXX: must flush on all processor with same address space */ 45461382a50Sbellard tlb_flush_page_write(cpu_single_env, host_start); 45561382a50Sbellard #endif 456fd6ce8f6Sbellard #ifdef DEBUG_TB_INVALIDATE 457fd6ce8f6Sbellard printf("protecting code page: 0x%08lx\n", 458fd6ce8f6Sbellard host_start); 459fd6ce8f6Sbellard #endif 460fd6ce8f6Sbellard p->flags &= ~PAGE_WRITE; 461fd6ce8f6Sbellard } 462fd6ce8f6Sbellard } 463fd6ce8f6Sbellard 464fd6ce8f6Sbellard /* Allocate a new translation block. Flush the translation buffer if 465fd6ce8f6Sbellard too many translation blocks or too much generated code. */ 466d4e8164fSbellard TranslationBlock *tb_alloc(unsigned long pc) 467fd6ce8f6Sbellard { 468fd6ce8f6Sbellard TranslationBlock *tb; 469fd6ce8f6Sbellard 470fd6ce8f6Sbellard if (nb_tbs >= CODE_GEN_MAX_BLOCKS || 471fd6ce8f6Sbellard (code_gen_ptr - code_gen_buffer) >= CODE_GEN_BUFFER_MAX_SIZE) 472d4e8164fSbellard return NULL; 473fd6ce8f6Sbellard tb = &tbs[nb_tbs++]; 474fd6ce8f6Sbellard tb->pc = pc; 475d4e8164fSbellard return tb; 476d4e8164fSbellard } 477d4e8164fSbellard 478d4e8164fSbellard /* link the tb with the other TBs */ 479d4e8164fSbellard void tb_link(TranslationBlock *tb) 480d4e8164fSbellard { 481d4e8164fSbellard unsigned int page_index1, page_index2; 482fd6ce8f6Sbellard 483fd6ce8f6Sbellard /* add in the page list */ 484d4e8164fSbellard page_index1 = tb->pc >> TARGET_PAGE_BITS; 485fd6ce8f6Sbellard tb_alloc_page(tb, page_index1); 486d4e8164fSbellard page_index2 = (tb->pc + tb->size - 1) >> TARGET_PAGE_BITS; 487fd6ce8f6Sbellard if (page_index2 != page_index1) { 488fd6ce8f6Sbellard tb_alloc_page(tb, page_index2); 489fd6ce8f6Sbellard } 49061382a50Sbellard #ifdef DEBUG_TB_CHECK 49161382a50Sbellard tb_page_check(); 49261382a50Sbellard #endif 493d4e8164fSbellard tb->jmp_first = (TranslationBlock *)((long)tb | 2); 494d4e8164fSbellard tb->jmp_next[0] = NULL; 495d4e8164fSbellard tb->jmp_next[1] = NULL; 496d4e8164fSbellard 497d4e8164fSbellard /* init original jump addresses */ 498d4e8164fSbellard if (tb->tb_next_offset[0] != 0xffff) 499d4e8164fSbellard tb_reset_jump(tb, 0); 500d4e8164fSbellard if (tb->tb_next_offset[1] != 0xffff) 501d4e8164fSbellard tb_reset_jump(tb, 1); 502fd6ce8f6Sbellard } 503fd6ce8f6Sbellard 504fd6ce8f6Sbellard /* called from signal handler: invalidate the code and unprotect the 505fd6ce8f6Sbellard page. Return TRUE if the fault was succesfully handled. */ 506fd6ce8f6Sbellard int page_unprotect(unsigned long address) 507fd6ce8f6Sbellard { 5081565b7bcSbellard unsigned int page_index, prot, pindex; 5091565b7bcSbellard PageDesc *p, *p1; 510fd6ce8f6Sbellard unsigned long host_start, host_end, addr; 511fd6ce8f6Sbellard 5121565b7bcSbellard host_start = address & host_page_mask; 5131565b7bcSbellard page_index = host_start >> TARGET_PAGE_BITS; 5141565b7bcSbellard p1 = page_find(page_index); 5151565b7bcSbellard if (!p1) 516fd6ce8f6Sbellard return 0; 5171565b7bcSbellard host_end = host_start + host_page_size; 5181565b7bcSbellard p = p1; 5191565b7bcSbellard prot = 0; 5201565b7bcSbellard for(addr = host_start;addr < host_end; addr += TARGET_PAGE_SIZE) { 5211565b7bcSbellard prot |= p->flags; 5221565b7bcSbellard p++; 5231565b7bcSbellard } 524fd6ce8f6Sbellard /* if the page was really writable, then we change its 525fd6ce8f6Sbellard protection back to writable */ 5261565b7bcSbellard if (prot & PAGE_WRITE_ORG) { 52761382a50Sbellard pindex = (address - host_start) >> TARGET_PAGE_BITS; 52861382a50Sbellard if (!(p1[pindex].flags & PAGE_WRITE)) { 52961382a50Sbellard #if !defined(CONFIG_SOFTMMU) 530fd6ce8f6Sbellard mprotect((void *)host_start, host_page_size, 531fd6ce8f6Sbellard (prot & PAGE_BITS) | PAGE_WRITE); 53261382a50Sbellard #endif 5331565b7bcSbellard p1[pindex].flags |= PAGE_WRITE; 534fd6ce8f6Sbellard /* and since the content will be modified, we must invalidate 535fd6ce8f6Sbellard the corresponding translated code. */ 536fd6ce8f6Sbellard tb_invalidate_page(address); 537fd6ce8f6Sbellard #ifdef DEBUG_TB_CHECK 538fd6ce8f6Sbellard tb_invalidate_check(address); 539fd6ce8f6Sbellard #endif 540fd6ce8f6Sbellard return 1; 541fd6ce8f6Sbellard } 542fd6ce8f6Sbellard } 54361382a50Sbellard return 0; 54461382a50Sbellard } 545fd6ce8f6Sbellard 546fd6ce8f6Sbellard /* call this function when system calls directly modify a memory area */ 547fd6ce8f6Sbellard void page_unprotect_range(uint8_t *data, unsigned long data_size) 548fd6ce8f6Sbellard { 549fd6ce8f6Sbellard unsigned long start, end, addr; 550fd6ce8f6Sbellard 551fd6ce8f6Sbellard start = (unsigned long)data; 552fd6ce8f6Sbellard end = start + data_size; 553fd6ce8f6Sbellard start &= TARGET_PAGE_MASK; 554fd6ce8f6Sbellard end = TARGET_PAGE_ALIGN(end); 555fd6ce8f6Sbellard for(addr = start; addr < end; addr += TARGET_PAGE_SIZE) { 556fd6ce8f6Sbellard page_unprotect(addr); 557fd6ce8f6Sbellard } 558fd6ce8f6Sbellard } 559a513fe19Sbellard 560a513fe19Sbellard /* find the TB 'tb' such that tb[0].tc_ptr <= tc_ptr < 561a513fe19Sbellard tb[1].tc_ptr. Return NULL if not found */ 562a513fe19Sbellard TranslationBlock *tb_find_pc(unsigned long tc_ptr) 563a513fe19Sbellard { 564a513fe19Sbellard int m_min, m_max, m; 565a513fe19Sbellard unsigned long v; 566a513fe19Sbellard TranslationBlock *tb; 567a513fe19Sbellard 568a513fe19Sbellard if (nb_tbs <= 0) 569a513fe19Sbellard return NULL; 570a513fe19Sbellard if (tc_ptr < (unsigned long)code_gen_buffer || 571a513fe19Sbellard tc_ptr >= (unsigned long)code_gen_ptr) 572a513fe19Sbellard return NULL; 573a513fe19Sbellard /* binary search (cf Knuth) */ 574a513fe19Sbellard m_min = 0; 575a513fe19Sbellard m_max = nb_tbs - 1; 576a513fe19Sbellard while (m_min <= m_max) { 577a513fe19Sbellard m = (m_min + m_max) >> 1; 578a513fe19Sbellard tb = &tbs[m]; 579a513fe19Sbellard v = (unsigned long)tb->tc_ptr; 580a513fe19Sbellard if (v == tc_ptr) 581a513fe19Sbellard return tb; 582a513fe19Sbellard else if (tc_ptr < v) { 583a513fe19Sbellard m_max = m - 1; 584a513fe19Sbellard } else { 585a513fe19Sbellard m_min = m + 1; 586a513fe19Sbellard } 587a513fe19Sbellard } 588a513fe19Sbellard return &tbs[m_max]; 589a513fe19Sbellard } 5907501267eSbellard 591ea041c0eSbellard static void tb_reset_jump_recursive(TranslationBlock *tb); 592ea041c0eSbellard 593ea041c0eSbellard static inline void tb_reset_jump_recursive2(TranslationBlock *tb, int n) 594ea041c0eSbellard { 595ea041c0eSbellard TranslationBlock *tb1, *tb_next, **ptb; 596ea041c0eSbellard unsigned int n1; 597ea041c0eSbellard 598ea041c0eSbellard tb1 = tb->jmp_next[n]; 599ea041c0eSbellard if (tb1 != NULL) { 600ea041c0eSbellard /* find head of list */ 601ea041c0eSbellard for(;;) { 602ea041c0eSbellard n1 = (long)tb1 & 3; 603ea041c0eSbellard tb1 = (TranslationBlock *)((long)tb1 & ~3); 604ea041c0eSbellard if (n1 == 2) 605ea041c0eSbellard break; 606ea041c0eSbellard tb1 = tb1->jmp_next[n1]; 607ea041c0eSbellard } 608ea041c0eSbellard /* we are now sure now that tb jumps to tb1 */ 609ea041c0eSbellard tb_next = tb1; 610ea041c0eSbellard 611ea041c0eSbellard /* remove tb from the jmp_first list */ 612ea041c0eSbellard ptb = &tb_next->jmp_first; 613ea041c0eSbellard for(;;) { 614ea041c0eSbellard tb1 = *ptb; 615ea041c0eSbellard n1 = (long)tb1 & 3; 616ea041c0eSbellard tb1 = (TranslationBlock *)((long)tb1 & ~3); 617ea041c0eSbellard if (n1 == n && tb1 == tb) 618ea041c0eSbellard break; 619ea041c0eSbellard ptb = &tb1->jmp_next[n1]; 620ea041c0eSbellard } 621ea041c0eSbellard *ptb = tb->jmp_next[n]; 622ea041c0eSbellard tb->jmp_next[n] = NULL; 623ea041c0eSbellard 624ea041c0eSbellard /* suppress the jump to next tb in generated code */ 625ea041c0eSbellard tb_reset_jump(tb, n); 626ea041c0eSbellard 627ea041c0eSbellard /* suppress jumps in the tb on which we could have jump */ 628ea041c0eSbellard tb_reset_jump_recursive(tb_next); 629ea041c0eSbellard } 630ea041c0eSbellard } 631ea041c0eSbellard 632ea041c0eSbellard static void tb_reset_jump_recursive(TranslationBlock *tb) 633ea041c0eSbellard { 634ea041c0eSbellard tb_reset_jump_recursive2(tb, 0); 635ea041c0eSbellard tb_reset_jump_recursive2(tb, 1); 636ea041c0eSbellard } 637ea041c0eSbellard 638c33a346eSbellard /* add a breakpoint. EXCP_DEBUG is returned by the CPU loop if a 639c33a346eSbellard breakpoint is reached */ 6404c3a88a2Sbellard int cpu_breakpoint_insert(CPUState *env, uint32_t pc) 6414c3a88a2Sbellard { 6424c3a88a2Sbellard #if defined(TARGET_I386) 6434c3a88a2Sbellard int i; 6444c3a88a2Sbellard 6454c3a88a2Sbellard for(i = 0; i < env->nb_breakpoints; i++) { 6464c3a88a2Sbellard if (env->breakpoints[i] == pc) 6474c3a88a2Sbellard return 0; 6484c3a88a2Sbellard } 6494c3a88a2Sbellard 6504c3a88a2Sbellard if (env->nb_breakpoints >= MAX_BREAKPOINTS) 6514c3a88a2Sbellard return -1; 6524c3a88a2Sbellard env->breakpoints[env->nb_breakpoints++] = pc; 6534c3a88a2Sbellard tb_invalidate_page(pc); 6544c3a88a2Sbellard return 0; 6554c3a88a2Sbellard #else 6564c3a88a2Sbellard return -1; 6574c3a88a2Sbellard #endif 6584c3a88a2Sbellard } 6594c3a88a2Sbellard 6604c3a88a2Sbellard /* remove a breakpoint */ 6614c3a88a2Sbellard int cpu_breakpoint_remove(CPUState *env, uint32_t pc) 6624c3a88a2Sbellard { 6634c3a88a2Sbellard #if defined(TARGET_I386) 6644c3a88a2Sbellard int i; 6654c3a88a2Sbellard for(i = 0; i < env->nb_breakpoints; i++) { 6664c3a88a2Sbellard if (env->breakpoints[i] == pc) 6674c3a88a2Sbellard goto found; 6684c3a88a2Sbellard } 6694c3a88a2Sbellard return -1; 6704c3a88a2Sbellard found: 6714c3a88a2Sbellard memmove(&env->breakpoints[i], &env->breakpoints[i + 1], 6724c3a88a2Sbellard (env->nb_breakpoints - (i + 1)) * sizeof(env->breakpoints[0])); 6734c3a88a2Sbellard env->nb_breakpoints--; 6744c3a88a2Sbellard tb_invalidate_page(pc); 6754c3a88a2Sbellard return 0; 6764c3a88a2Sbellard #else 6774c3a88a2Sbellard return -1; 6784c3a88a2Sbellard #endif 6794c3a88a2Sbellard } 6804c3a88a2Sbellard 681c33a346eSbellard /* enable or disable single step mode. EXCP_DEBUG is returned by the 682c33a346eSbellard CPU loop after each instruction */ 683c33a346eSbellard void cpu_single_step(CPUState *env, int enabled) 684c33a346eSbellard { 685c33a346eSbellard #if defined(TARGET_I386) 686c33a346eSbellard if (env->singlestep_enabled != enabled) { 687c33a346eSbellard env->singlestep_enabled = enabled; 688c33a346eSbellard /* must flush all the translated code to avoid inconsistancies */ 689c33a346eSbellard tb_flush(); 690c33a346eSbellard } 691c33a346eSbellard #endif 692c33a346eSbellard } 693c33a346eSbellard 69434865134Sbellard /* enable or disable low levels log */ 69534865134Sbellard void cpu_set_log(int log_flags) 69634865134Sbellard { 69734865134Sbellard loglevel = log_flags; 69834865134Sbellard if (loglevel && !logfile) { 69934865134Sbellard logfile = fopen(logfilename, "w"); 70034865134Sbellard if (!logfile) { 70134865134Sbellard perror(logfilename); 70234865134Sbellard _exit(1); 70334865134Sbellard } 70434865134Sbellard setvbuf(logfile, NULL, _IOLBF, 0); 70534865134Sbellard } 70634865134Sbellard } 70734865134Sbellard 70834865134Sbellard void cpu_set_log_filename(const char *filename) 70934865134Sbellard { 71034865134Sbellard logfilename = strdup(filename); 71134865134Sbellard } 712c33a346eSbellard 71368a79315Sbellard /* mask must never be zero */ 71468a79315Sbellard void cpu_interrupt(CPUState *env, int mask) 715ea041c0eSbellard { 716ea041c0eSbellard TranslationBlock *tb; 717ea041c0eSbellard 71868a79315Sbellard env->interrupt_request |= mask; 719ea041c0eSbellard /* if the cpu is currently executing code, we must unlink it and 720ea041c0eSbellard all the potentially executing TB */ 721ea041c0eSbellard tb = env->current_tb; 722ea041c0eSbellard if (tb) { 723ea041c0eSbellard tb_reset_jump_recursive(tb); 724ea041c0eSbellard } 725ea041c0eSbellard } 726ea041c0eSbellard 727ea041c0eSbellard 7287501267eSbellard void cpu_abort(CPUState *env, const char *fmt, ...) 7297501267eSbellard { 7307501267eSbellard va_list ap; 7317501267eSbellard 7327501267eSbellard va_start(ap, fmt); 7337501267eSbellard fprintf(stderr, "qemu: fatal: "); 7347501267eSbellard vfprintf(stderr, fmt, ap); 7357501267eSbellard fprintf(stderr, "\n"); 7367501267eSbellard #ifdef TARGET_I386 7377501267eSbellard cpu_x86_dump_state(env, stderr, X86_DUMP_FPU | X86_DUMP_CCOP); 7387501267eSbellard #endif 7397501267eSbellard va_end(ap); 7407501267eSbellard abort(); 7417501267eSbellard } 7427501267eSbellard 74366e85a21Sbellard #ifdef TARGET_I386 74466e85a21Sbellard /* unmap all maped pages and flush all associated code */ 74566e85a21Sbellard void page_unmap(void) 74666e85a21Sbellard { 74761382a50Sbellard PageDesc *pmap; 74861382a50Sbellard int i; 74966e85a21Sbellard 75066e85a21Sbellard for(i = 0; i < L1_SIZE; i++) { 75166e85a21Sbellard pmap = l1_map[i]; 75266e85a21Sbellard if (pmap) { 75361382a50Sbellard #if !defined(CONFIG_SOFTMMU) 75461382a50Sbellard PageDesc *p; 75561382a50Sbellard unsigned long addr; 75661382a50Sbellard int j, ret, j1; 75761382a50Sbellard 75866e85a21Sbellard p = pmap; 7597c2d6a78Sbellard for(j = 0;j < L2_SIZE;) { 76066e85a21Sbellard if (p->flags & PAGE_VALID) { 76166e85a21Sbellard addr = (i << (32 - L1_BITS)) | (j << TARGET_PAGE_BITS); 7627c2d6a78Sbellard /* we try to find a range to make less syscalls */ 7637c2d6a78Sbellard j1 = j; 7647c2d6a78Sbellard p++; 7657c2d6a78Sbellard j++; 7667c2d6a78Sbellard while (j < L2_SIZE && (p->flags & PAGE_VALID)) { 7677c2d6a78Sbellard p++; 7687c2d6a78Sbellard j++; 7697c2d6a78Sbellard } 7707c2d6a78Sbellard ret = munmap((void *)addr, (j - j1) << TARGET_PAGE_BITS); 77166e85a21Sbellard if (ret != 0) { 77266e85a21Sbellard fprintf(stderr, "Could not unmap page 0x%08lx\n", addr); 77366e85a21Sbellard exit(1); 77466e85a21Sbellard } 7757c2d6a78Sbellard } else { 77666e85a21Sbellard p++; 7777c2d6a78Sbellard j++; 7787c2d6a78Sbellard } 77966e85a21Sbellard } 78061382a50Sbellard #endif 78166e85a21Sbellard free(pmap); 78266e85a21Sbellard l1_map[i] = NULL; 78366e85a21Sbellard } 78466e85a21Sbellard } 78566e85a21Sbellard tb_flush(); 78666e85a21Sbellard } 78766e85a21Sbellard #endif 78833417e70Sbellard 78933417e70Sbellard void tlb_flush(CPUState *env) 79033417e70Sbellard { 79161382a50Sbellard #if !defined(CONFIG_USER_ONLY) 79233417e70Sbellard int i; 79333417e70Sbellard for(i = 0; i < CPU_TLB_SIZE; i++) { 79433417e70Sbellard env->tlb_read[0][i].address = -1; 79533417e70Sbellard env->tlb_write[0][i].address = -1; 79633417e70Sbellard env->tlb_read[1][i].address = -1; 79733417e70Sbellard env->tlb_write[1][i].address = -1; 79833417e70Sbellard } 79933417e70Sbellard #endif 80033417e70Sbellard } 80133417e70Sbellard 80261382a50Sbellard static inline void tlb_flush_entry(CPUTLBEntry *tlb_entry, uint32_t addr) 80361382a50Sbellard { 80461382a50Sbellard if (addr == (tlb_entry->address & 80561382a50Sbellard (TARGET_PAGE_MASK | TLB_INVALID_MASK))) 80661382a50Sbellard tlb_entry->address = -1; 80761382a50Sbellard } 80861382a50Sbellard 80933417e70Sbellard void tlb_flush_page(CPUState *env, uint32_t addr) 81033417e70Sbellard { 81161382a50Sbellard #if !defined(CONFIG_USER_ONLY) 81233417e70Sbellard int i; 81333417e70Sbellard 81461382a50Sbellard addr &= TARGET_PAGE_MASK; 81533417e70Sbellard i = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); 81661382a50Sbellard tlb_flush_entry(&env->tlb_read[0][i], addr); 81761382a50Sbellard tlb_flush_entry(&env->tlb_write[0][i], addr); 81861382a50Sbellard tlb_flush_entry(&env->tlb_read[1][i], addr); 81961382a50Sbellard tlb_flush_entry(&env->tlb_write[1][i], addr); 82061382a50Sbellard #endif 82161382a50Sbellard } 82261382a50Sbellard 82361382a50Sbellard /* make all write to page 'addr' trigger a TLB exception to detect 82461382a50Sbellard self modifying code */ 82561382a50Sbellard void tlb_flush_page_write(CPUState *env, uint32_t addr) 82661382a50Sbellard { 82761382a50Sbellard #if !defined(CONFIG_USER_ONLY) 82861382a50Sbellard int i; 82961382a50Sbellard 83061382a50Sbellard addr &= TARGET_PAGE_MASK; 83161382a50Sbellard i = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); 83261382a50Sbellard tlb_flush_entry(&env->tlb_write[0][i], addr); 83361382a50Sbellard tlb_flush_entry(&env->tlb_write[1][i], addr); 83433417e70Sbellard #endif 83533417e70Sbellard } 83633417e70Sbellard 83733417e70Sbellard static inline unsigned long *physpage_find_alloc(unsigned int page) 83833417e70Sbellard { 83933417e70Sbellard unsigned long **lp, *p; 84033417e70Sbellard unsigned int index, i; 84133417e70Sbellard 84233417e70Sbellard index = page >> TARGET_PAGE_BITS; 84333417e70Sbellard lp = &l1_physmap[index >> L2_BITS]; 84433417e70Sbellard p = *lp; 84533417e70Sbellard if (!p) { 84633417e70Sbellard /* allocate if not found */ 84733417e70Sbellard p = malloc(sizeof(unsigned long) * L2_SIZE); 84833417e70Sbellard for(i = 0; i < L2_SIZE; i++) 84933417e70Sbellard p[i] = IO_MEM_UNASSIGNED; 85033417e70Sbellard *lp = p; 85133417e70Sbellard } 85233417e70Sbellard return p + (index & (L2_SIZE - 1)); 85333417e70Sbellard } 85433417e70Sbellard 85533417e70Sbellard /* return NULL if no page defined (unused memory) */ 85633417e70Sbellard unsigned long physpage_find(unsigned long page) 85733417e70Sbellard { 85833417e70Sbellard unsigned long *p; 85933417e70Sbellard unsigned int index; 86033417e70Sbellard index = page >> TARGET_PAGE_BITS; 86133417e70Sbellard p = l1_physmap[index >> L2_BITS]; 86233417e70Sbellard if (!p) 86333417e70Sbellard return IO_MEM_UNASSIGNED; 86433417e70Sbellard return p[index & (L2_SIZE - 1)]; 86533417e70Sbellard } 86633417e70Sbellard 86733417e70Sbellard /* register physical memory. 'size' must be a multiple of the target 86833417e70Sbellard page size. If (phys_offset & ~TARGET_PAGE_MASK) != 0, then it is an 86933417e70Sbellard io memory page */ 87033417e70Sbellard void cpu_register_physical_memory(unsigned long start_addr, unsigned long size, 87133417e70Sbellard long phys_offset) 87233417e70Sbellard { 87333417e70Sbellard unsigned long addr, end_addr; 87433417e70Sbellard unsigned long *p; 87533417e70Sbellard 87633417e70Sbellard end_addr = start_addr + size; 87733417e70Sbellard for(addr = start_addr; addr < end_addr; addr += TARGET_PAGE_SIZE) { 87833417e70Sbellard p = physpage_find_alloc(addr); 87933417e70Sbellard *p = phys_offset; 88033417e70Sbellard if ((phys_offset & ~TARGET_PAGE_MASK) == 0) 88133417e70Sbellard phys_offset += TARGET_PAGE_SIZE; 88233417e70Sbellard } 88333417e70Sbellard } 88433417e70Sbellard 88533417e70Sbellard static uint32_t unassigned_mem_readb(uint32_t addr) 88633417e70Sbellard { 88733417e70Sbellard return 0; 88833417e70Sbellard } 88933417e70Sbellard 89033417e70Sbellard static void unassigned_mem_writeb(uint32_t addr, uint32_t val) 89133417e70Sbellard { 89233417e70Sbellard } 89333417e70Sbellard 89433417e70Sbellard static CPUReadMemoryFunc *unassigned_mem_read[3] = { 89533417e70Sbellard unassigned_mem_readb, 89633417e70Sbellard unassigned_mem_readb, 89733417e70Sbellard unassigned_mem_readb, 89833417e70Sbellard }; 89933417e70Sbellard 90033417e70Sbellard static CPUWriteMemoryFunc *unassigned_mem_write[3] = { 90133417e70Sbellard unassigned_mem_writeb, 90233417e70Sbellard unassigned_mem_writeb, 90333417e70Sbellard unassigned_mem_writeb, 90433417e70Sbellard }; 90533417e70Sbellard 90633417e70Sbellard 90733417e70Sbellard static void io_mem_init(void) 90833417e70Sbellard { 90933417e70Sbellard io_mem_nb = 1; 91033417e70Sbellard cpu_register_io_memory(0, unassigned_mem_read, unassigned_mem_write); 91133417e70Sbellard } 91233417e70Sbellard 91333417e70Sbellard /* mem_read and mem_write are arrays of functions containing the 91433417e70Sbellard function to access byte (index 0), word (index 1) and dword (index 91533417e70Sbellard 2). All functions must be supplied. If io_index is non zero, the 91633417e70Sbellard corresponding io zone is modified. If it is zero, a new io zone is 91733417e70Sbellard allocated. The return value can be used with 91833417e70Sbellard cpu_register_physical_memory(). (-1) is returned if error. */ 91933417e70Sbellard int cpu_register_io_memory(int io_index, 92033417e70Sbellard CPUReadMemoryFunc **mem_read, 92133417e70Sbellard CPUWriteMemoryFunc **mem_write) 92233417e70Sbellard { 92333417e70Sbellard int i; 92433417e70Sbellard 92533417e70Sbellard if (io_index <= 0) { 92633417e70Sbellard if (io_index >= IO_MEM_NB_ENTRIES) 92733417e70Sbellard return -1; 92833417e70Sbellard io_index = io_mem_nb++; 92933417e70Sbellard } else { 93033417e70Sbellard if (io_index >= IO_MEM_NB_ENTRIES) 93133417e70Sbellard return -1; 93233417e70Sbellard } 93333417e70Sbellard 93433417e70Sbellard for(i = 0;i < 3; i++) { 93533417e70Sbellard io_mem_read[io_index][i] = mem_read[i]; 93633417e70Sbellard io_mem_write[io_index][i] = mem_write[i]; 93733417e70Sbellard } 93833417e70Sbellard return io_index << IO_MEM_SHIFT; 93933417e70Sbellard } 94061382a50Sbellard 94161382a50Sbellard #if !defined(CONFIG_USER_ONLY) 94261382a50Sbellard 94361382a50Sbellard #define MMUSUFFIX _cmmu 94461382a50Sbellard #define GETPC() NULL 94561382a50Sbellard #define env cpu_single_env 94661382a50Sbellard 94761382a50Sbellard #define SHIFT 0 94861382a50Sbellard #include "softmmu_template.h" 94961382a50Sbellard 95061382a50Sbellard #define SHIFT 1 95161382a50Sbellard #include "softmmu_template.h" 95261382a50Sbellard 95361382a50Sbellard #define SHIFT 2 95461382a50Sbellard #include "softmmu_template.h" 95561382a50Sbellard 95661382a50Sbellard #define SHIFT 3 95761382a50Sbellard #include "softmmu_template.h" 95861382a50Sbellard 95961382a50Sbellard #undef env 96061382a50Sbellard 96161382a50Sbellard #endif 962