13dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner/* 23dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner * Common CPU TLB handling 33dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner * 43dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner * Copyright (c) 2003 Fabrice Bellard 53dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner * 63dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner * This library is free software; you can redistribute it and/or 73dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner * modify it under the terms of the GNU Lesser General Public 83dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner * License as published by the Free Software Foundation; either 93dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner * version 2 of the License, or (at your option) any later version. 103dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner * 113dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner * This library is distributed in the hope that it will be useful, 123dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner * but WITHOUT ANY WARRANTY; without even the implied warranty of 133dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 143dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner * Lesser General Public License for more details. 153dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner * 163dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner * You should have received a copy of the GNU Lesser General Public 173dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner * License along with this library; if not, see <http://www.gnu.org/licenses/>. 183dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner */ 193dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner 203dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner#include "config.h" 213dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner#include "cpu.h" 223dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner#include "exec/exec-all.h" 233dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner#include "exec/cputlb.h" 243dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner 253dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner/* statistics */ 263dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turnerint tlb_flush_count; 273dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner 283dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turnerstatic const CPUTLBEntry s_cputlb_empty_entry = { 293dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner .addr_read = -1, 303dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner .addr_write = -1, 313dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner .addr_code = -1, 323dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner .addend = -1, 333dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner}; 343dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner 353e0677df2819b1366819fe4112dc8464425b6edaDavid 'Digit' Turner/* NOTE: 363e0677df2819b1366819fe4112dc8464425b6edaDavid 'Digit' Turner * If flush_global is true (the usual case), flush all tlb entries. 373e0677df2819b1366819fe4112dc8464425b6edaDavid 'Digit' Turner * If flush_global is false, flush (at least) all tlb entries not 383e0677df2819b1366819fe4112dc8464425b6edaDavid 'Digit' Turner * marked global. 393e0677df2819b1366819fe4112dc8464425b6edaDavid 'Digit' Turner * 403e0677df2819b1366819fe4112dc8464425b6edaDavid 'Digit' Turner * Since QEMU doesn't currently implement a global/not-global flag 413e0677df2819b1366819fe4112dc8464425b6edaDavid 'Digit' Turner * for tlb entries, at the moment tlb_flush() will also flush all 423e0677df2819b1366819fe4112dc8464425b6edaDavid 'Digit' Turner * tlb entries in the flush_global == false case. This is OK because 433e0677df2819b1366819fe4112dc8464425b6edaDavid 'Digit' Turner * CPU architectures generally permit an implementation to drop 443e0677df2819b1366819fe4112dc8464425b6edaDavid 'Digit' Turner * entries from the TLB at any time, so flushing more entries than 453e0677df2819b1366819fe4112dc8464425b6edaDavid 'Digit' Turner * required is only an efficiency issue, not a correctness issue. 463e0677df2819b1366819fe4112dc8464425b6edaDavid 'Digit' Turner */ 473dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turnervoid tlb_flush(CPUArchState *env, int flush_global) 483dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner{ 493dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner int i; 503dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner 513dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner#if defined(DEBUG_TLB) 523dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner printf("tlb_flush:\n"); 533dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner#endif 543dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner /* must reset current TB so that interrupts cannot modify the 553dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner links while we are modifying them */ 563dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner env->current_tb = NULL; 573dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner 583dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner for (i = 0; i < CPU_TLB_SIZE; i++) { 593dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner int mmu_idx; 603dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner 613dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) { 623dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner env->tlb_table[mmu_idx][i] = s_cputlb_empty_entry; 633dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner } 643dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner } 653dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner 663dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner memset(env->tb_jmp_cache, 0, TB_JMP_CACHE_SIZE * sizeof (void *)); 670d8b235c0c6c02de86a4e7415d574175b4518ff0David 'Digit' Turner 680d8b235c0c6c02de86a4e7415d574175b4518ff0David 'Digit' Turner env->tlb_flush_addr = -1; 690d8b235c0c6c02de86a4e7415d574175b4518ff0David 'Digit' Turner env->tlb_flush_mask = 0; 703dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner tlb_flush_count++; 713dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner} 723dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner 733dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turnerstatic inline void tlb_flush_entry(CPUTLBEntry *tlb_entry, target_ulong addr) 743dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner{ 753dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner if (addr == (tlb_entry->addr_read & 763dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner (TARGET_PAGE_MASK | TLB_INVALID_MASK)) || 773dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner addr == (tlb_entry->addr_write & 783dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner (TARGET_PAGE_MASK | TLB_INVALID_MASK)) || 793dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner addr == (tlb_entry->addr_code & 803dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner (TARGET_PAGE_MASK | TLB_INVALID_MASK))) { 813dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner *tlb_entry = s_cputlb_empty_entry; 823dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner } 833dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner} 843dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner 853dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turnervoid tlb_flush_page(CPUArchState *env, target_ulong addr) 863dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner{ 873dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner int i; 883dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner int mmu_idx; 893dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner 903dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner#if defined(DEBUG_TLB) 913dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner printf("tlb_flush_page: " TARGET_FMT_lx "\n", addr); 923dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner#endif 930d8b235c0c6c02de86a4e7415d574175b4518ff0David 'Digit' Turner /* Check if we need to flush due to large pages. */ 940d8b235c0c6c02de86a4e7415d574175b4518ff0David 'Digit' Turner if ((addr & env->tlb_flush_mask) == env->tlb_flush_addr) { 950d8b235c0c6c02de86a4e7415d574175b4518ff0David 'Digit' Turner#if defined(DEBUG_TLB) 960d8b235c0c6c02de86a4e7415d574175b4518ff0David 'Digit' Turner printf("tlb_flush_page: forced full flush (" 970d8b235c0c6c02de86a4e7415d574175b4518ff0David 'Digit' Turner TARGET_FMT_lx "/" TARGET_FMT_lx ")\n", 980d8b235c0c6c02de86a4e7415d574175b4518ff0David 'Digit' Turner env->tlb_flush_addr, env->tlb_flush_mask); 990d8b235c0c6c02de86a4e7415d574175b4518ff0David 'Digit' Turner#endif 1000d8b235c0c6c02de86a4e7415d574175b4518ff0David 'Digit' Turner tlb_flush(env, 1); 1010d8b235c0c6c02de86a4e7415d574175b4518ff0David 'Digit' Turner return; 1020d8b235c0c6c02de86a4e7415d574175b4518ff0David 'Digit' Turner } 1033dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner /* must reset current TB so that interrupts cannot modify the 1043dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner links while we are modifying them */ 1053dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner env->current_tb = NULL; 1063dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner 1073dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner addr &= TARGET_PAGE_MASK; 1083dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner i = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); 1093dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) { 1103dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner tlb_flush_entry(&env->tlb_table[mmu_idx][i], addr); 1113dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner } 1123dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner 1133dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner tb_flush_jmp_cache(env, addr); 1143dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner} 1153dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner 1163dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner/* update the TLBs so that writes to code in the virtual page 'addr' 1173dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner can be detected */ 1183dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turnervoid tlb_protect_code(ram_addr_t ram_addr) 1193dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner{ 1203dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner cpu_physical_memory_reset_dirty(ram_addr, 1213dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner ram_addr + TARGET_PAGE_SIZE, 1223dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner CODE_DIRTY_FLAG); 1233dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner} 1243dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner 1253dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner/* update the TLB so that writes in physical page 'phys_addr' are no longer 1263dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner tested for self modifying code */ 1273dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turnervoid tlb_unprotect_code_phys(CPUArchState *env, ram_addr_t ram_addr, 1283dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner target_ulong vaddr) 1293dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner{ 1303dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner cpu_physical_memory_set_dirty_flags(ram_addr, CODE_DIRTY_FLAG); 1313dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner} 1323dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner 1333e0677df2819b1366819fe4112dc8464425b6edaDavid 'Digit' Turnerstatic bool tlb_is_dirty_ram(CPUTLBEntry *tlbe) 1343e0677df2819b1366819fe4112dc8464425b6edaDavid 'Digit' Turner{ 1353e0677df2819b1366819fe4112dc8464425b6edaDavid 'Digit' Turner return (tlbe->addr_write & ~TARGET_PAGE_MASK) == IO_MEM_RAM; 1363e0677df2819b1366819fe4112dc8464425b6edaDavid 'Digit' Turner} 1373e0677df2819b1366819fe4112dc8464425b6edaDavid 'Digit' Turner 1383e0677df2819b1366819fe4112dc8464425b6edaDavid 'Digit' Turnervoid tlb_reset_dirty_range(CPUTLBEntry *tlb_entry, uintptr_t start, 1393e0677df2819b1366819fe4112dc8464425b6edaDavid 'Digit' Turner uintptr_t length) 1403dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner{ 1413dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner uintptr_t addr; 1423e0677df2819b1366819fe4112dc8464425b6edaDavid 'Digit' Turner 1433e0677df2819b1366819fe4112dc8464425b6edaDavid 'Digit' Turner if (tlb_is_dirty_ram(tlb_entry)) { 1443dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner addr = (tlb_entry->addr_write & TARGET_PAGE_MASK) + tlb_entry->addend; 1453dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner if ((addr - start) < length) { 1463e0677df2819b1366819fe4112dc8464425b6edaDavid 'Digit' Turner tlb_entry->addr_write &= TARGET_PAGE_MASK; 1473e0677df2819b1366819fe4112dc8464425b6edaDavid 'Digit' Turner tlb_entry->addr_write |= TLB_NOTDIRTY; 1483dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner } 1493dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner } 1503dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner} 1513dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner 1523dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turnerstatic inline void tlb_set_dirty1(CPUTLBEntry *tlb_entry, target_ulong vaddr) 1533dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner{ 1543e0677df2819b1366819fe4112dc8464425b6edaDavid 'Digit' Turner if (tlb_entry->addr_write == (vaddr | TLB_NOTDIRTY)) { 1553dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner tlb_entry->addr_write = vaddr; 1563e0677df2819b1366819fe4112dc8464425b6edaDavid 'Digit' Turner } 1573dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner} 1583dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner 1593dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner/* update the TLB corresponding to virtual page vaddr 1603dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner so that it is no longer dirty */ 1613dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turnervoid tlb_set_dirty(CPUArchState *env, target_ulong vaddr) 1623dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner{ 1633dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner int i; 1643dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner int mmu_idx; 1653dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner 1663dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner vaddr &= TARGET_PAGE_MASK; 1673dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner i = (vaddr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); 1683e0677df2819b1366819fe4112dc8464425b6edaDavid 'Digit' Turner for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) { 1693dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner tlb_set_dirty1(&env->tlb_table[mmu_idx][i], vaddr); 1703e0677df2819b1366819fe4112dc8464425b6edaDavid 'Digit' Turner } 1713dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner} 1723dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner 1730d8b235c0c6c02de86a4e7415d574175b4518ff0David 'Digit' Turner/* Our TLB does not support large pages, so remember the area covered by 1740d8b235c0c6c02de86a4e7415d574175b4518ff0David 'Digit' Turner large pages and trigger a full TLB flush if these are invalidated. */ 1750d8b235c0c6c02de86a4e7415d574175b4518ff0David 'Digit' Turnerstatic void tlb_add_large_page(CPUArchState *env, target_ulong vaddr, 1760d8b235c0c6c02de86a4e7415d574175b4518ff0David 'Digit' Turner target_ulong size) 1770d8b235c0c6c02de86a4e7415d574175b4518ff0David 'Digit' Turner{ 1780d8b235c0c6c02de86a4e7415d574175b4518ff0David 'Digit' Turner target_ulong mask = ~(size - 1); 1790d8b235c0c6c02de86a4e7415d574175b4518ff0David 'Digit' Turner 1800d8b235c0c6c02de86a4e7415d574175b4518ff0David 'Digit' Turner if (env->tlb_flush_addr == (target_ulong)-1) { 1810d8b235c0c6c02de86a4e7415d574175b4518ff0David 'Digit' Turner env->tlb_flush_addr = vaddr & mask; 1820d8b235c0c6c02de86a4e7415d574175b4518ff0David 'Digit' Turner env->tlb_flush_mask = mask; 1830d8b235c0c6c02de86a4e7415d574175b4518ff0David 'Digit' Turner return; 1840d8b235c0c6c02de86a4e7415d574175b4518ff0David 'Digit' Turner } 1850d8b235c0c6c02de86a4e7415d574175b4518ff0David 'Digit' Turner /* Extend the existing region to include the new page. 1860d8b235c0c6c02de86a4e7415d574175b4518ff0David 'Digit' Turner This is a compromise between unnecessary flushes and the cost 1870d8b235c0c6c02de86a4e7415d574175b4518ff0David 'Digit' Turner of maintaining a full variable size TLB. */ 1880d8b235c0c6c02de86a4e7415d574175b4518ff0David 'Digit' Turner mask &= env->tlb_flush_mask; 1890d8b235c0c6c02de86a4e7415d574175b4518ff0David 'Digit' Turner while (((env->tlb_flush_addr ^ vaddr) & mask) != 0) { 1900d8b235c0c6c02de86a4e7415d574175b4518ff0David 'Digit' Turner mask <<= 1; 1910d8b235c0c6c02de86a4e7415d574175b4518ff0David 'Digit' Turner } 1920d8b235c0c6c02de86a4e7415d574175b4518ff0David 'Digit' Turner env->tlb_flush_addr &= mask; 1930d8b235c0c6c02de86a4e7415d574175b4518ff0David 'Digit' Turner env->tlb_flush_mask = mask; 1940d8b235c0c6c02de86a4e7415d574175b4518ff0David 'Digit' Turner} 1950d8b235c0c6c02de86a4e7415d574175b4518ff0David 'Digit' Turner 1960d8b235c0c6c02de86a4e7415d574175b4518ff0David 'Digit' Turner/* Add a new TLB entry. At most one entry for a given virtual address 1970d8b235c0c6c02de86a4e7415d574175b4518ff0David 'Digit' Turner is permitted. Only a single TARGET_PAGE_SIZE region is mapped, the 1980d8b235c0c6c02de86a4e7415d574175b4518ff0David 'Digit' Turner supplied size is only used by tlb_flush_page. */ 1990d8b235c0c6c02de86a4e7415d574175b4518ff0David 'Digit' Turnervoid tlb_set_page(CPUArchState *env, target_ulong vaddr, 2000d8b235c0c6c02de86a4e7415d574175b4518ff0David 'Digit' Turner hwaddr paddr, int prot, 2010d8b235c0c6c02de86a4e7415d574175b4518ff0David 'Digit' Turner int mmu_idx, target_ulong size) 2023dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner{ 2033dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner PhysPageDesc *p; 2043dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner unsigned long pd; 2053dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner unsigned int index; 2063dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner target_ulong address; 2073dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner target_ulong code_address; 2083dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner ptrdiff_t addend; 2093dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner CPUTLBEntry *te; 2103dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner CPUWatchpoint *wp; 2113dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner hwaddr iotlb; 2123dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner 2130d8b235c0c6c02de86a4e7415d574175b4518ff0David 'Digit' Turner assert(size >= TARGET_PAGE_SIZE); 2140d8b235c0c6c02de86a4e7415d574175b4518ff0David 'Digit' Turner if (size != TARGET_PAGE_SIZE) { 2150d8b235c0c6c02de86a4e7415d574175b4518ff0David 'Digit' Turner tlb_add_large_page(env, vaddr, size); 2160d8b235c0c6c02de86a4e7415d574175b4518ff0David 'Digit' Turner } 2173dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner p = phys_page_find(paddr >> TARGET_PAGE_BITS); 2183dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner if (!p) { 2193dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner pd = IO_MEM_UNASSIGNED; 2203dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner } else { 2213dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner pd = p->phys_offset; 2223dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner } 2233dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner#if defined(DEBUG_TLB) 2243e0677df2819b1366819fe4112dc8464425b6edaDavid 'Digit' Turner printf("tlb_set_page: vaddr=" TARGET_FMT_lx " paddr=0x" TARGET_FMT_plx 2250d8b235c0c6c02de86a4e7415d574175b4518ff0David 'Digit' Turner " prot=%x idx=%d pd=0x%08lx\n", 2260d8b235c0c6c02de86a4e7415d574175b4518ff0David 'Digit' Turner vaddr, paddr, prot, mmu_idx, pd); 2273dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner#endif 2283dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner 2293dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner address = vaddr; 2303dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner if ((pd & ~TARGET_PAGE_MASK) > IO_MEM_ROM && !(pd & IO_MEM_ROMD)) { 2313dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner /* IO memory case (romd handled later) */ 2323dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner address |= TLB_MMIO; 2333dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner } 2343dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner addend = (ptrdiff_t)qemu_get_ram_ptr(pd & TARGET_PAGE_MASK); 2353dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner if ((pd & ~TARGET_PAGE_MASK) <= IO_MEM_ROM) { 2363dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner /* Normal RAM. */ 2373dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner iotlb = pd & TARGET_PAGE_MASK; 2383dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner if ((pd & ~TARGET_PAGE_MASK) == IO_MEM_RAM) 2393dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner iotlb |= IO_MEM_NOTDIRTY; 2403dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner else 2413dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner iotlb |= IO_MEM_ROM; 2423dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner } else { 2433dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner /* IO handlers are currently passed a physical address. 2443dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner It would be nice to pass an offset from the base address 2453dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner of that region. This would avoid having to special case RAM, 2463dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner and avoid full address decoding in every device. 2473dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner We can't use the high bits of pd for this because 2483dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner IO_MEM_ROMD uses these as a ram address. */ 2493dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner iotlb = (pd & ~TARGET_PAGE_MASK); 2503dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner if (p) { 2513dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner iotlb += p->region_offset; 2523dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner } else { 2533dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner iotlb += paddr; 2543dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner } 2553dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner } 2563dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner 2573dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner code_address = address; 2583dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner /* Make accesses to pages with watchpoints go via the 2593dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner watchpoint trap routines. */ 2603dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner QTAILQ_FOREACH(wp, &env->watchpoints, entry) { 2613dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner if (vaddr == (wp->vaddr & TARGET_PAGE_MASK)) { 2623dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner iotlb = io_mem_watch + paddr; 2633dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner /* TODO: The memory case can be optimized by not trapping 2643dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner reads of pages with a write breakpoint. */ 2653dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner address |= TLB_MMIO; 2663dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner } 2673dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner } 2683dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner 2693dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner index = (vaddr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); 2703dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner env->iotlb[mmu_idx][index] = iotlb - vaddr; 2713dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner te = &env->tlb_table[mmu_idx][index]; 2723dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner te->addend = addend - vaddr; 2733dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner if (prot & PAGE_READ) { 2743dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner te->addr_read = address; 2753dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner } else { 2763dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner te->addr_read = -1; 2773dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner } 2783dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner 2793dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner if (prot & PAGE_EXEC) { 2803dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner te->addr_code = code_address; 2813dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner } else { 2823dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner te->addr_code = -1; 2833dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner } 2843dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner if (prot & PAGE_WRITE) { 2853dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner if ((pd & ~TARGET_PAGE_MASK) == IO_MEM_ROM || 2863dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner (pd & IO_MEM_ROMD)) { 2873dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner /* Write access calls the I/O callback. */ 2883dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner te->addr_write = address | TLB_MMIO; 2893dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner } else if ((pd & ~TARGET_PAGE_MASK) == IO_MEM_RAM && 2903dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner !cpu_physical_memory_is_dirty(pd)) { 2913dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner te->addr_write = address | TLB_NOTDIRTY; 2923dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner } else { 2933dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner te->addr_write = address; 2943dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner } 2953dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner } else { 2963dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner te->addr_write = -1; 2973dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner } 2980e0515410009c5bdd4d2d77a4a9131081573f040David 'Digit' Turner} 2990e0515410009c5bdd4d2d77a4a9131081573f040David 'Digit' Turner 30001ee5b8ded901c76731bab7a12a87c2002479014David 'Digit' Turner/* NOTE: this function can trigger an exception */ 30101ee5b8ded901c76731bab7a12a87c2002479014David 'Digit' Turner/* NOTE2: the returned address is not exactly the physical address: it 30201ee5b8ded901c76731bab7a12a87c2002479014David 'Digit' Turner is the offset relative to phys_ram_base */ 30301ee5b8ded901c76731bab7a12a87c2002479014David 'Digit' Turnertb_page_addr_t get_page_addr_code(CPUArchState *env1, target_ulong addr) 30401ee5b8ded901c76731bab7a12a87c2002479014David 'Digit' Turner{ 30501ee5b8ded901c76731bab7a12a87c2002479014David 'Digit' Turner int mmu_idx, page_index, pd; 30601ee5b8ded901c76731bab7a12a87c2002479014David 'Digit' Turner void *p; 30701ee5b8ded901c76731bab7a12a87c2002479014David 'Digit' Turner 30801ee5b8ded901c76731bab7a12a87c2002479014David 'Digit' Turner page_index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); 30901ee5b8ded901c76731bab7a12a87c2002479014David 'Digit' Turner mmu_idx = cpu_mmu_index(env1); 31001ee5b8ded901c76731bab7a12a87c2002479014David 'Digit' Turner if (unlikely(env1->tlb_table[mmu_idx][page_index].addr_code != 31101ee5b8ded901c76731bab7a12a87c2002479014David 'Digit' Turner (addr & TARGET_PAGE_MASK))) { 312eca7bc24e45fb6809582795ff88f13384b5ce7dfDavid 'Digit' Turner cpu_ldub_code(env1, addr); 31301ee5b8ded901c76731bab7a12a87c2002479014David 'Digit' Turner } 31401ee5b8ded901c76731bab7a12a87c2002479014David 'Digit' Turner pd = env1->tlb_table[mmu_idx][page_index].addr_code & ~TARGET_PAGE_MASK; 31501ee5b8ded901c76731bab7a12a87c2002479014David 'Digit' Turner if (pd > IO_MEM_ROM && !(pd & IO_MEM_ROMD)) { 31601ee5b8ded901c76731bab7a12a87c2002479014David 'Digit' Turner#if defined(TARGET_SPARC) || defined(TARGET_MIPS) 31701ee5b8ded901c76731bab7a12a87c2002479014David 'Digit' Turner cpu_unassigned_access(env1, addr, 0, 1, 0, 4); 31801ee5b8ded901c76731bab7a12a87c2002479014David 'Digit' Turner#else 31901ee5b8ded901c76731bab7a12a87c2002479014David 'Digit' Turner cpu_abort(env1, "Trying to execute code outside RAM or ROM at 0x" TARGET_FMT_lx "\n", addr); 32001ee5b8ded901c76731bab7a12a87c2002479014David 'Digit' Turner#endif 32101ee5b8ded901c76731bab7a12a87c2002479014David 'Digit' Turner } 32201ee5b8ded901c76731bab7a12a87c2002479014David 'Digit' Turner p = (void *)((uintptr_t)addr + env1->tlb_table[mmu_idx][page_index].addend); 32301ee5b8ded901c76731bab7a12a87c2002479014David 'Digit' Turner return qemu_ram_addr_from_host_nofail(p); 32401ee5b8ded901c76731bab7a12a87c2002479014David 'Digit' Turner} 32501ee5b8ded901c76731bab7a12a87c2002479014David 'Digit' Turner 3263dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner#define MMUSUFFIX _cmmu 3273dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner#define SOFTMMU_CODE_ACCESS 3283dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner 3293dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner#define SHIFT 0 3303dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner#include "exec/softmmu_template.h" 3313dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner 3323dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner#define SHIFT 1 3333dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner#include "exec/softmmu_template.h" 3343dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner 3353dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner#define SHIFT 2 3363dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner#include "exec/softmmu_template.h" 3373dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner 3383dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner#define SHIFT 3 3393dc53fc5342d24fae977049a40c34cc63ba04ad6David 'Digit' Turner#include "exec/softmmu_template.h" 340