qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

mmu.c (8470B)


      1 /*
      2  * Altera Nios II MMU emulation for qemu.
      3  *
      4  * Copyright (C) 2012 Chris Wulff <crwulff@gmail.com>
      5  *
      6  * This library is free software; you can redistribute it and/or
      7  * modify it under the terms of the GNU Lesser General Public
      8  * License as published by the Free Software Foundation; either
      9  * version 2.1 of the License, or (at your option) any later version.
     10  *
     11  * This library is distributed in the hope that it will be useful,
     12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     14  * Lesser General Public License for more details.
     15  *
     16  * You should have received a copy of the GNU Lesser General Public
     17  * License along with this library; if not, see
     18  * <http://www.gnu.org/licenses/lgpl-2.1.html>
     19  */
     20 
     21 #include "qemu/osdep.h"
     22 #include "qemu/qemu-print.h"
     23 #include "cpu.h"
     24 #include "exec/exec-all.h"
     25 #include "mmu.h"
     26 #include "exec/helper-proto.h"
     27 #include "trace/trace-target_nios2.h"
     28 
     29 
     30 /* rw - 0 = read, 1 = write, 2 = fetch.  */
     31 unsigned int mmu_translate(CPUNios2State *env,
     32                            Nios2MMULookup *lu,
     33                            target_ulong vaddr, int rw, int mmu_idx)
     34 {
     35     Nios2CPU *cpu = env_archcpu(env);
     36     int pid = FIELD_EX32(env->mmu.tlbmisc_wr, CR_TLBMISC, PID);
     37     int vpn = vaddr >> 12;
     38     int way, n_ways = cpu->tlb_num_ways;
     39 
     40     for (way = 0; way < n_ways; way++) {
     41         uint32_t index = (way * n_ways) + (vpn & env->mmu.tlb_entry_mask);
     42         Nios2TLBEntry *entry = &env->mmu.tlb[index];
     43 
     44         if (((entry->tag >> 12) != vpn) ||
     45             (((entry->tag & (1 << 11)) == 0) &&
     46             ((entry->tag & ((1 << cpu->pid_num_bits) - 1)) != pid))) {
     47             trace_nios2_mmu_translate_miss(vaddr, pid, index, entry->tag);
     48             continue;
     49         }
     50 
     51         lu->vaddr = vaddr & TARGET_PAGE_MASK;
     52         lu->paddr = FIELD_EX32(entry->data, CR_TLBACC, PFN) << TARGET_PAGE_BITS;
     53         lu->prot = ((entry->data & CR_TLBACC_R) ? PAGE_READ : 0) |
     54                    ((entry->data & CR_TLBACC_W) ? PAGE_WRITE : 0) |
     55                    ((entry->data & CR_TLBACC_X) ? PAGE_EXEC : 0);
     56 
     57         trace_nios2_mmu_translate_hit(vaddr, pid, index, lu->paddr, lu->prot);
     58         return 1;
     59     }
     60     return 0;
     61 }
     62 
     63 static void mmu_flush_pid(CPUNios2State *env, uint32_t pid)
     64 {
     65     CPUState *cs = env_cpu(env);
     66     Nios2CPU *cpu = env_archcpu(env);
     67     int idx;
     68 
     69     for (idx = 0; idx < cpu->tlb_num_entries; idx++) {
     70         Nios2TLBEntry *entry = &env->mmu.tlb[idx];
     71 
     72         if ((entry->tag & (1 << 10)) && (!(entry->tag & (1 << 11))) &&
     73             ((entry->tag & ((1 << cpu->pid_num_bits) - 1)) == pid)) {
     74             uint32_t vaddr = entry->tag & TARGET_PAGE_MASK;
     75 
     76             trace_nios2_mmu_flush_pid_hit(pid, idx, vaddr);
     77             tlb_flush_page(cs, vaddr);
     78         } else {
     79             trace_nios2_mmu_flush_pid_miss(pid, idx, entry->tag);
     80         }
     81     }
     82 }
     83 
     84 void helper_mmu_write_tlbacc(CPUNios2State *env, uint32_t v)
     85 {
     86     CPUState *cs = env_cpu(env);
     87     Nios2CPU *cpu = env_archcpu(env);
     88 
     89     trace_nios2_mmu_write_tlbacc(FIELD_EX32(v, CR_TLBACC, IG),
     90                                  (v & CR_TLBACC_C) ? 'C' : '.',
     91                                  (v & CR_TLBACC_R) ? 'R' : '.',
     92                                  (v & CR_TLBACC_W) ? 'W' : '.',
     93                                  (v & CR_TLBACC_X) ? 'X' : '.',
     94                                  (v & CR_TLBACC_G) ? 'G' : '.',
     95                                  FIELD_EX32(v, CR_TLBACC, PFN));
     96 
     97     /* if tlbmisc.WE == 1 then trigger a TLB write on writes to TLBACC */
     98     if (env->ctrl[CR_TLBMISC] & CR_TLBMISC_WE) {
     99         int way = FIELD_EX32(env->ctrl[CR_TLBMISC], CR_TLBMISC, WAY);
    100         int vpn = FIELD_EX32(env->mmu.pteaddr_wr, CR_PTEADDR, VPN);
    101         int pid = FIELD_EX32(env->mmu.tlbmisc_wr, CR_TLBMISC, PID);
    102         int g = FIELD_EX32(v, CR_TLBACC, G);
    103         int valid = FIELD_EX32(vpn, CR_TLBACC, PFN) < 0xC0000;
    104         Nios2TLBEntry *entry =
    105             &env->mmu.tlb[(way * cpu->tlb_num_ways) +
    106                           (vpn & env->mmu.tlb_entry_mask)];
    107         uint32_t newTag = (vpn << 12) | (g << 11) | (valid << 10) | pid;
    108         uint32_t newData = v & (CR_TLBACC_C | CR_TLBACC_R | CR_TLBACC_W |
    109                                 CR_TLBACC_X | R_CR_TLBACC_PFN_MASK);
    110 
    111         if ((entry->tag != newTag) || (entry->data != newData)) {
    112             if (entry->tag & (1 << 10)) {
    113                 /* Flush existing entry */
    114                 tlb_flush_page(cs, entry->tag & TARGET_PAGE_MASK);
    115             }
    116             entry->tag = newTag;
    117             entry->data = newData;
    118         }
    119         /* Auto-increment tlbmisc.WAY */
    120         env->ctrl[CR_TLBMISC] = FIELD_DP32(env->ctrl[CR_TLBMISC],
    121                                            CR_TLBMISC, WAY,
    122                                            (way + 1) & (cpu->tlb_num_ways - 1));
    123     }
    124 
    125     /* Writes to TLBACC don't change the read-back value */
    126     env->mmu.tlbacc_wr = v;
    127 }
    128 
    129 void helper_mmu_write_tlbmisc(CPUNios2State *env, uint32_t v)
    130 {
    131     Nios2CPU *cpu = env_archcpu(env);
    132     uint32_t new_pid = FIELD_EX32(v, CR_TLBMISC, PID);
    133     uint32_t old_pid = FIELD_EX32(env->mmu.tlbmisc_wr, CR_TLBMISC, PID);
    134     uint32_t way = FIELD_EX32(v, CR_TLBMISC, WAY);
    135 
    136     trace_nios2_mmu_write_tlbmisc(way,
    137                                   (v & CR_TLBMISC_RD) ? 'R' : '.',
    138                                   (v & CR_TLBMISC_WE) ? 'W' : '.',
    139                                   (v & CR_TLBMISC_DBL) ? '2' : '.',
    140                                   (v & CR_TLBMISC_BAD) ? 'B' : '.',
    141                                   (v & CR_TLBMISC_PERM) ? 'P' : '.',
    142                                   (v & CR_TLBMISC_D) ? 'D' : '.',
    143                                   new_pid);
    144 
    145     if (new_pid != old_pid) {
    146         mmu_flush_pid(env, old_pid);
    147     }
    148 
    149     /* if tlbmisc.RD == 1 then trigger a TLB read on writes to TLBMISC */
    150     if (v & CR_TLBMISC_RD) {
    151         int vpn = FIELD_EX32(env->mmu.pteaddr_wr, CR_PTEADDR, VPN);
    152         Nios2TLBEntry *entry =
    153             &env->mmu.tlb[(way * cpu->tlb_num_ways) +
    154                           (vpn & env->mmu.tlb_entry_mask)];
    155 
    156         env->ctrl[CR_TLBACC] &= R_CR_TLBACC_IG_MASK;
    157         env->ctrl[CR_TLBACC] |= entry->data;
    158         env->ctrl[CR_TLBACC] |= (entry->tag & (1 << 11)) ? CR_TLBACC_G : 0;
    159         env->ctrl[CR_TLBMISC] = FIELD_DP32(v, CR_TLBMISC, PID,
    160                                            entry->tag &
    161                                            ((1 << cpu->pid_num_bits) - 1));
    162         env->ctrl[CR_PTEADDR] = FIELD_DP32(env->ctrl[CR_PTEADDR],
    163                                            CR_PTEADDR, VPN,
    164                                            entry->tag >> TARGET_PAGE_BITS);
    165     } else {
    166         env->ctrl[CR_TLBMISC] = v;
    167     }
    168 
    169     env->mmu.tlbmisc_wr = v;
    170 }
    171 
    172 void helper_mmu_write_pteaddr(CPUNios2State *env, uint32_t v)
    173 {
    174     trace_nios2_mmu_write_pteaddr(FIELD_EX32(v, CR_PTEADDR, PTBASE),
    175                                   FIELD_EX32(v, CR_PTEADDR, VPN));
    176 
    177     /* Writes to PTEADDR don't change the read-back VPN value */
    178     env->ctrl[CR_PTEADDR] = ((v & ~R_CR_PTEADDR_VPN_MASK) |
    179                              (env->ctrl[CR_PTEADDR] & R_CR_PTEADDR_VPN_MASK));
    180     env->mmu.pteaddr_wr = v;
    181 }
    182 
    183 void mmu_init(CPUNios2State *env)
    184 {
    185     Nios2CPU *cpu = env_archcpu(env);
    186     Nios2MMU *mmu = &env->mmu;
    187 
    188     mmu->tlb_entry_mask = (cpu->tlb_num_entries / cpu->tlb_num_ways) - 1;
    189     mmu->tlb = g_new0(Nios2TLBEntry, cpu->tlb_num_entries);
    190 }
    191 
    192 void dump_mmu(CPUNios2State *env)
    193 {
    194     Nios2CPU *cpu = env_archcpu(env);
    195     int i;
    196 
    197     qemu_printf("MMU: ways %d, entries %d, pid bits %d\n",
    198                 cpu->tlb_num_ways, cpu->tlb_num_entries,
    199                 cpu->pid_num_bits);
    200 
    201     for (i = 0; i < cpu->tlb_num_entries; i++) {
    202         Nios2TLBEntry *entry = &env->mmu.tlb[i];
    203         qemu_printf("TLB[%d] = %08X %08X %c VPN %05X "
    204                     "PID %02X %c PFN %05X %c%c%c%c\n",
    205                     i, entry->tag, entry->data,
    206                     (entry->tag & (1 << 10)) ? 'V' : '-',
    207                     entry->tag >> 12,
    208                     entry->tag & ((1 << cpu->pid_num_bits) - 1),
    209                     (entry->tag & (1 << 11)) ? 'G' : '-',
    210                     FIELD_EX32(entry->data, CR_TLBACC, PFN),
    211                     (entry->data & CR_TLBACC_C) ? 'C' : '-',
    212                     (entry->data & CR_TLBACC_R) ? 'R' : '-',
    213                     (entry->data & CR_TLBACC_W) ? 'W' : '-',
    214                     (entry->data & CR_TLBACC_X) ? 'X' : '-');
    215     }
    216 }