]>
Commit | Line | Data |
---|---|---|
032c76bc CW |
1 | /* |
2 | * Altera Nios II MMU emulation for qemu. | |
3 | * | |
4 | * Copyright (C) 2012 Chris Wulff <[email protected]> | |
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" | |
fad866da | 22 | #include "qemu/qemu-print.h" |
032c76bc CW |
23 | #include "cpu.h" |
24 | #include "exec/exec-all.h" | |
25 | #include "mmu.h" | |
304c05df | 26 | #include "exec/helper-proto.h" |
6f83e277 | 27 | #include "trace/trace-target_nios2.h" |
032c76bc | 28 | |
032c76bc | 29 | |
032c76bc CW |
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 | { | |
29168c65 | 35 | Nios2CPU *cpu = env_archcpu(env); |
17c20fe3 | 36 | int pid = FIELD_EX32(env->mmu.tlbmisc_wr, CR_TLBMISC, PID); |
032c76bc | 37 | int vpn = vaddr >> 12; |
6f83e277 | 38 | int way, n_ways = cpu->tlb_num_ways; |
032c76bc | 39 | |
6f83e277 RH |
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]; | |
032c76bc CW |
43 | |
44 | if (((entry->tag >> 12) != vpn) || | |
45 | (((entry->tag & (1 << 11)) == 0) && | |
46 | ((entry->tag & ((1 << cpu->pid_num_bits) - 1)) != pid))) { | |
6f83e277 | 47 | trace_nios2_mmu_translate_miss(vaddr, pid, index, entry->tag); |
032c76bc CW |
48 | continue; |
49 | } | |
6f83e277 | 50 | |
032c76bc | 51 | lu->vaddr = vaddr & TARGET_PAGE_MASK; |
9d636563 | 52 | lu->paddr = FIELD_EX32(entry->data, CR_TLBACC, PFN) << TARGET_PAGE_BITS; |
032c76bc CW |
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 | ||
6f83e277 | 57 | trace_nios2_mmu_translate_hit(vaddr, pid, index, lu->paddr, lu->prot); |
032c76bc CW |
58 | return 1; |
59 | } | |
60 | return 0; | |
61 | } | |
62 | ||
63 | static void mmu_flush_pid(CPUNios2State *env, uint32_t pid) | |
64 | { | |
29a0af61 | 65 | CPUState *cs = env_cpu(env); |
29168c65 | 66 | Nios2CPU *cpu = env_archcpu(env); |
032c76bc | 67 | int idx; |
032c76bc CW |
68 | |
69 | for (idx = 0; idx < cpu->tlb_num_entries; idx++) { | |
70 | Nios2TLBEntry *entry = &env->mmu.tlb[idx]; | |
71 | ||
032c76bc CW |
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 | ||
6f83e277 | 76 | trace_nios2_mmu_flush_pid_hit(pid, idx, vaddr); |
032c76bc | 77 | tlb_flush_page(cs, vaddr); |
6f83e277 RH |
78 | } else { |
79 | trace_nios2_mmu_flush_pid_miss(pid, idx, entry->tag); | |
032c76bc CW |
80 | } |
81 | } | |
82 | } | |
83 | ||
304c05df | 84 | void helper_mmu_write_tlbacc(CPUNios2State *env, uint32_t v) |
032c76bc | 85 | { |
29a0af61 | 86 | CPUState *cs = env_cpu(env); |
29168c65 | 87 | Nios2CPU *cpu = env_archcpu(env); |
032c76bc | 88 | |
9d636563 | 89 | trace_nios2_mmu_write_tlbacc(FIELD_EX32(v, CR_TLBACC, IG), |
304c05df RH |
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' : '.', | |
9d636563 | 95 | FIELD_EX32(v, CR_TLBACC, PFN)); |
304c05df RH |
96 | |
97 | /* if tlbmisc.WE == 1 then trigger a TLB write on writes to TLBACC */ | |
80362815 | 98 | if (env->ctrl[CR_TLBMISC] & CR_TLBMISC_WE) { |
17c20fe3 | 99 | int way = FIELD_EX32(env->ctrl[CR_TLBMISC], CR_TLBMISC, WAY); |
0a1fc630 | 100 | int vpn = FIELD_EX32(env->mmu.pteaddr_wr, CR_PTEADDR, VPN); |
17c20fe3 | 101 | int pid = FIELD_EX32(env->mmu.tlbmisc_wr, CR_TLBMISC, PID); |
9d636563 RH |
102 | int g = FIELD_EX32(v, CR_TLBACC, G); |
103 | int valid = FIELD_EX32(vpn, CR_TLBACC, PFN) < 0xC0000; | |
304c05df RH |
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 | | |
9d636563 | 109 | CR_TLBACC_X | R_CR_TLBACC_PFN_MASK); |
304c05df RH |
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); | |
032c76bc | 115 | } |
304c05df RH |
116 | entry->tag = newTag; |
117 | entry->data = newData; | |
032c76bc | 118 | } |
304c05df | 119 | /* Auto-increment tlbmisc.WAY */ |
17c20fe3 RH |
120 | env->ctrl[CR_TLBMISC] = FIELD_DP32(env->ctrl[CR_TLBMISC], |
121 | CR_TLBMISC, WAY, | |
122 | (way + 1) & (cpu->tlb_num_ways - 1)); | |
304c05df | 123 | } |
032c76bc | 124 | |
304c05df RH |
125 | /* Writes to TLBACC don't change the read-back value */ |
126 | env->mmu.tlbacc_wr = v; | |
127 | } | |
032c76bc | 128 | |
304c05df RH |
129 | void helper_mmu_write_tlbmisc(CPUNios2State *env, uint32_t v) |
130 | { | |
131 | Nios2CPU *cpu = env_archcpu(env); | |
17c20fe3 RH |
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); | |
032c76bc | 135 | |
17c20fe3 | 136 | trace_nios2_mmu_write_tlbmisc(way, |
304c05df | 137 | (v & CR_TLBMISC_RD) ? 'R' : '.', |
80362815 | 138 | (v & CR_TLBMISC_WE) ? 'W' : '.', |
304c05df RH |
139 | (v & CR_TLBMISC_DBL) ? '2' : '.', |
140 | (v & CR_TLBMISC_BAD) ? 'B' : '.', | |
141 | (v & CR_TLBMISC_PERM) ? 'P' : '.', | |
142 | (v & CR_TLBMISC_D) ? 'D' : '.', | |
17c20fe3 | 143 | new_pid); |
304c05df | 144 | |
17c20fe3 RH |
145 | if (new_pid != old_pid) { |
146 | mmu_flush_pid(env, old_pid); | |
304c05df | 147 | } |
17c20fe3 | 148 | |
304c05df RH |
149 | /* if tlbmisc.RD == 1 then trigger a TLB read on writes to TLBMISC */ |
150 | if (v & CR_TLBMISC_RD) { | |
0a1fc630 | 151 | int vpn = FIELD_EX32(env->mmu.pteaddr_wr, CR_PTEADDR, VPN); |
304c05df RH |
152 | Nios2TLBEntry *entry = |
153 | &env->mmu.tlb[(way * cpu->tlb_num_ways) + | |
154 | (vpn & env->mmu.tlb_entry_mask)]; | |
155 | ||
9d636563 | 156 | env->ctrl[CR_TLBACC] &= R_CR_TLBACC_IG_MASK; |
b8f036a9 RH |
157 | env->ctrl[CR_TLBACC] |= entry->data; |
158 | env->ctrl[CR_TLBACC] |= (entry->tag & (1 << 11)) ? CR_TLBACC_G : 0; | |
17c20fe3 RH |
159 | env->ctrl[CR_TLBMISC] = FIELD_DP32(v, CR_TLBMISC, PID, |
160 | entry->tag & | |
161 | ((1 << cpu->pid_num_bits) - 1)); | |
0a1fc630 RH |
162 | env->ctrl[CR_PTEADDR] = FIELD_DP32(env->ctrl[CR_PTEADDR], |
163 | CR_PTEADDR, VPN, | |
164 | entry->tag >> TARGET_PAGE_BITS); | |
304c05df | 165 | } else { |
b8f036a9 | 166 | env->ctrl[CR_TLBMISC] = v; |
304c05df RH |
167 | } |
168 | ||
169 | env->mmu.tlbmisc_wr = v; | |
170 | } | |
032c76bc | 171 | |
304c05df RH |
172 | void helper_mmu_write_pteaddr(CPUNios2State *env, uint32_t v) |
173 | { | |
0a1fc630 RH |
174 | trace_nios2_mmu_write_pteaddr(FIELD_EX32(v, CR_PTEADDR, PTBASE), |
175 | FIELD_EX32(v, CR_PTEADDR, VPN)); | |
032c76bc | 176 | |
304c05df | 177 | /* Writes to PTEADDR don't change the read-back VPN value */ |
0a1fc630 RH |
178 | env->ctrl[CR_PTEADDR] = ((v & ~R_CR_PTEADDR_VPN_MASK) | |
179 | (env->ctrl[CR_PTEADDR] & R_CR_PTEADDR_VPN_MASK)); | |
304c05df | 180 | env->mmu.pteaddr_wr = v; |
032c76bc CW |
181 | } |
182 | ||
183 | void mmu_init(CPUNios2State *env) | |
184 | { | |
29168c65 | 185 | Nios2CPU *cpu = env_archcpu(env); |
032c76bc CW |
186 | Nios2MMU *mmu = &env->mmu; |
187 | ||
032c76bc CW |
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 | ||
fad866da | 192 | void dump_mmu(CPUNios2State *env) |
032c76bc | 193 | { |
29168c65 | 194 | Nios2CPU *cpu = env_archcpu(env); |
032c76bc CW |
195 | int i; |
196 | ||
fad866da | 197 | qemu_printf("MMU: ways %d, entries %d, pid bits %d\n", |
032c76bc CW |
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]; | |
fad866da | 203 | qemu_printf("TLB[%d] = %08X %08X %c VPN %05X " |
032c76bc CW |
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' : '-', | |
9d636563 | 210 | FIELD_EX32(entry->data, CR_TLBACC, PFN), |
032c76bc CW |
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 | } |