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