Rev 391 | Rev 395 | Go to most recent revision | Show entire file | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed
Rev 391 | Rev 394 | ||
---|---|---|---|
Line 40... | Line 40... | ||
40 | 40 | ||
41 | static void tlb_refill_fail(struct exception_regdump *pstate); |
41 | static void tlb_refill_fail(struct exception_regdump *pstate); |
42 | static void tlb_invalid_fail(struct exception_regdump *pstate); |
42 | static void tlb_invalid_fail(struct exception_regdump *pstate); |
43 | static void tlb_modified_fail(struct exception_regdump *pstate); |
43 | static void tlb_modified_fail(struct exception_regdump *pstate); |
44 | 44 | ||
- | 45 | static pte_t *find_mapping_and_check(__address badvaddr); |
|
- | 46 | static void prepare_entry_lo(struct entry_lo *lo, bool g, bool v, bool d, bool c, __address pfn); |
|
- | 47 | ||
45 | /** Initialize TLB |
48 | /** Initialize TLB |
46 | * |
49 | * |
47 | * Initialize TLB. |
50 | * Initialize TLB. |
48 | * Invalidate all entries and mark wired entries. |
51 | * Invalidate all entries and mark wired entries. |
49 | */ |
52 | */ |
Line 77... | Line 80... | ||
77 | * |
80 | * |
78 | * @param pstate Interrupted register context. |
81 | * @param pstate Interrupted register context. |
79 | */ |
82 | */ |
80 | void tlb_refill(struct exception_regdump *pstate) |
83 | void tlb_refill(struct exception_regdump *pstate) |
81 | { |
84 | { |
82 | struct entry_hi hi; |
85 | struct entry_lo lo; |
83 | __address badvaddr; |
86 | __address badvaddr; |
84 | pte_t *pte; |
87 | pte_t *pte; |
85 | 88 | ||
86 | *((__u32 *) &hi) = cp0_entry_hi_read(); |
- | |
87 | badvaddr = cp0_badvaddr_read(); |
89 | badvaddr = cp0_badvaddr_read(); |
88 | 90 | ||
89 | spinlock_lock(&VM->lock); |
91 | spinlock_lock(&VM->lock); |
90 | - | ||
91 | /* |
- | |
92 | * Refill cannot succeed if the ASIDs don't match. |
- | |
93 | */ |
- | |
94 | if (hi.asid != VM->asid) |
- | |
95 | goto fail; |
- | |
96 | - | ||
97 | /* |
- | |
98 | * Refill cannot succeed if badvaddr is not |
- | |
99 | * associated with any mapping. |
- | |
100 | */ |
- | |
101 | pte = find_mapping(badvaddr, 0); |
92 | pte = find_mapping_and_check(badvaddr); |
102 | if (!pte) |
93 | if (!pte) |
103 | goto fail; |
94 | goto fail; |
104 | 95 | ||
105 | /* |
96 | /* |
106 | * Refill cannot succeed if the mapping is marked as invalid. |
97 | * Record access to PTE. |
107 | */ |
98 | */ |
108 | if (!pte->v) |
99 | pte->a = 1; |
- | 100 | ||
109 | goto fail; |
101 | prepare_entry_lo(&lo, pte->g, pte->v, pte->d, pte->c, pte->pfn); |
110 | 102 | ||
111 | /* |
103 | /* |
112 | * New entry is to be inserted into TLB |
104 | * New entry is to be inserted into TLB |
113 | */ |
105 | */ |
114 | cp0_pagemask_write(TLB_PAGE_MASK_16K); |
- | |
115 | if ((badvaddr/PAGE_SIZE) % 2 == 0) { |
106 | if ((badvaddr/PAGE_SIZE) % 2 == 0) { |
116 | cp0_entry_lo0_write(*((__u32 *) pte)); |
107 | cp0_entry_lo0_write(*((__u32 *) &lo)); |
117 | cp0_entry_lo1_write(0); |
108 | cp0_entry_lo1_write(0); |
118 | } |
109 | } |
119 | else { |
110 | else { |
120 | cp0_entry_lo0_write(0); |
111 | cp0_entry_lo0_write(0); |
121 | cp0_entry_lo1_write(*((__u32 *) pte)); |
112 | cp0_entry_lo1_write(*((__u32 *) &lo)); |
122 | } |
113 | } |
123 | tlbwr(); |
114 | tlbwr(); |
124 | 115 | ||
125 | spinlock_unlock(&VM->lock); |
116 | spinlock_unlock(&VM->lock); |
126 | return; |
117 | return; |
Line 128... | Line 119... | ||
128 | fail: |
119 | fail: |
129 | spinlock_unlock(&VM->lock); |
120 | spinlock_unlock(&VM->lock); |
130 | tlb_refill_fail(pstate); |
121 | tlb_refill_fail(pstate); |
131 | } |
122 | } |
132 | 123 | ||
- | 124 | /** Process TLB Invalid Exception |
|
- | 125 | * |
|
- | 126 | * Process TLB Invalid Exception. |
|
- | 127 | * |
|
- | 128 | * @param pstate Interrupted register context. |
|
- | 129 | */ |
|
133 | void tlb_invalid(struct exception_regdump *pstate) |
130 | void tlb_invalid(struct exception_regdump *pstate) |
134 | { |
131 | { |
- | 132 | struct index index; |
|
- | 133 | __address badvaddr; |
|
- | 134 | struct entry_lo lo; |
|
- | 135 | pte_t *pte; |
|
- | 136 | ||
- | 137 | badvaddr = cp0_badvaddr_read(); |
|
- | 138 | ||
- | 139 | /* |
|
- | 140 | * Locate the faulting entry in TLB. |
|
- | 141 | */ |
|
- | 142 | tlbp(); |
|
- | 143 | *((__u32 *) &index) = cp0_index_read(); |
|
- | 144 | ||
- | 145 | spinlock_lock(&VM->lock); |
|
- | 146 | ||
- | 147 | /* |
|
- | 148 | * Fail if the entry is not in TLB. |
|
- | 149 | */ |
|
- | 150 | if (index.p) |
|
- | 151 | goto fail; |
|
- | 152 | ||
- | 153 | pte = find_mapping_and_check(badvaddr); |
|
- | 154 | if (!pte) |
|
- | 155 | goto fail; |
|
- | 156 | ||
- | 157 | /* |
|
- | 158 | * Read the faulting TLB entry. |
|
- | 159 | */ |
|
- | 160 | tlbr(); |
|
- | 161 | ||
- | 162 | /* |
|
- | 163 | * Record access to PTE. |
|
- | 164 | */ |
|
- | 165 | pte->a = 1; |
|
- | 166 | ||
- | 167 | prepare_entry_lo(&lo, pte->g, pte->v, pte->d, pte->c, pte->pfn); |
|
- | 168 | ||
- | 169 | /* |
|
- | 170 | * The entry is to be updated in TLB. |
|
- | 171 | */ |
|
- | 172 | if ((badvaddr/PAGE_SIZE) % 2 == 0) |
|
- | 173 | cp0_entry_lo0_write(*((__u32 *) &lo)); |
|
- | 174 | else |
|
- | 175 | cp0_entry_lo1_write(*((__u32 *) &lo)); |
|
- | 176 | tlbwi(); |
|
- | 177 | ||
- | 178 | spinlock_unlock(&VM->lock); |
|
- | 179 | return; |
|
- | 180 | ||
- | 181 | fail: |
|
- | 182 | spinlock_unlock(&VM->lock); |
|
135 | tlb_invalid_fail(pstate); |
183 | tlb_invalid_fail(pstate); |
136 | } |
184 | } |
137 | 185 | ||
- | 186 | /** Process TLB Modified Exception |
|
- | 187 | * |
|
- | 188 | * Process TLB Modified Exception. |
|
- | 189 | * |
|
- | 190 | * @param pstate Interrupted register context. |
|
- | 191 | */ |
|
- | 192 | ||
138 | void tlb_modified(struct exception_regdump *pstate) |
193 | void tlb_modified(struct exception_regdump *pstate) |
139 | { |
194 | { |
- | 195 | struct index index; |
|
- | 196 | __address badvaddr; |
|
- | 197 | struct entry_lo lo; |
|
- | 198 | pte_t *pte; |
|
- | 199 | ||
- | 200 | badvaddr = cp0_badvaddr_read(); |
|
- | 201 | ||
- | 202 | /* |
|
- | 203 | * Locate the faulting entry in TLB. |
|
- | 204 | */ |
|
- | 205 | tlbp(); |
|
- | 206 | *((__u32 *) &index) = cp0_index_read(); |
|
- | 207 | ||
- | 208 | spinlock_lock(&VM->lock); |
|
- | 209 | ||
- | 210 | /* |
|
- | 211 | * Fail if the entry is not in TLB. |
|
- | 212 | */ |
|
- | 213 | if (index.p) |
|
- | 214 | goto fail; |
|
- | 215 | ||
- | 216 | pte = find_mapping_and_check(badvaddr); |
|
- | 217 | if (!pte) |
|
- | 218 | goto fail; |
|
- | 219 | ||
- | 220 | /* |
|
- | 221 | * Fail if the page is not writable. |
|
- | 222 | */ |
|
- | 223 | if (!pte->w) |
|
- | 224 | goto fail; |
|
- | 225 | ||
- | 226 | /* |
|
- | 227 | * Read the faulting TLB entry. |
|
- | 228 | */ |
|
- | 229 | tlbr(); |
|
- | 230 | ||
- | 231 | /* |
|
- | 232 | * Record access and write to PTE. |
|
- | 233 | */ |
|
- | 234 | pte->a = 1; |
|
- | 235 | pte->d = 1; |
|
- | 236 | ||
- | 237 | prepare_entry_lo(&lo, pte->g, pte->v, pte->w, pte->c, pte->pfn); |
|
- | 238 | ||
- | 239 | /* |
|
- | 240 | * The entry is to be updated in TLB. |
|
- | 241 | */ |
|
- | 242 | if ((badvaddr/PAGE_SIZE) % 2 == 0) |
|
- | 243 | cp0_entry_lo0_write(*((__u32 *) &lo)); |
|
- | 244 | else |
|
- | 245 | cp0_entry_lo1_write(*((__u32 *) &lo)); |
|
- | 246 | tlbwi(); |
|
- | 247 | ||
- | 248 | spinlock_unlock(&VM->lock); |
|
- | 249 | return; |
|
- | 250 | ||
- | 251 | fail: |
|
- | 252 | spinlock_unlock(&VM->lock); |
|
140 | tlb_modified_fail(pstate); |
253 | tlb_modified_fail(pstate); |
141 | } |
254 | } |
142 | 255 | ||
143 | void tlb_refill_fail(struct exception_regdump *pstate) |
256 | void tlb_refill_fail(struct exception_regdump *pstate) |
144 | { |
257 | { |
Line 160... | Line 273... | ||
160 | char *symbol = ""; |
273 | char *symbol = ""; |
161 | 274 | ||
162 | char *s = get_symtab_entry(pstate->epc); |
275 | char *s = get_symtab_entry(pstate->epc); |
163 | if (s) |
276 | if (s) |
164 | symbol = s; |
277 | symbol = s; |
165 | panic("%X: TLB Invalid Exception at %X(%s)\n", cp0_badvaddr_read(), |
278 | panic("%X: TLB Invalid Exception at %X(%s)\n", cp0_badvaddr_read(), pstate->epc, symbol); |
166 | pstate->epc, symbol); |
- | |
167 | } |
279 | } |
168 | 280 | ||
169 | void tlb_modified_fail(struct exception_regdump *pstate) |
281 | void tlb_modified_fail(struct exception_regdump *pstate) |
170 | { |
282 | { |
171 | char *symbol = ""; |
283 | char *symbol = ""; |
172 | 284 | ||
173 | char *s = get_symtab_entry(pstate->epc); |
285 | char *s = get_symtab_entry(pstate->epc); |
174 | if (s) |
286 | if (s) |
175 | symbol = s; |
287 | symbol = s; |
176 | panic("%X: TLB Modified Exception at %X(%s)\n", cp0_badvaddr_read(), |
288 | panic("%X: TLB Modified Exception at %X(%s)\n", cp0_badvaddr_read(), pstate->epc, symbol); |
177 | pstate->epc, symbol); |
- | |
178 | } |
289 | } |
179 | 290 | ||
180 | 291 | ||
181 | void tlb_invalidate(int asid) |
292 | void tlb_invalidate(int asid) |
182 | { |
293 | { |
Line 186... | Line 297... | ||
186 | 297 | ||
187 | // TODO |
298 | // TODO |
188 | 299 | ||
189 | cpu_priority_restore(pri); |
300 | cpu_priority_restore(pri); |
190 | } |
301 | } |
- | 302 | ||
- | 303 | /** Try to find PTE for faulting address |
|
- | 304 | * |
|
- | 305 | * Try to find PTE for faulting address. |
|
- | 306 | * The VM->lock must be held on entry to this function. |
|
- | 307 | * |
|
- | 308 | * @param badvaddr Faulting virtual address. |
|
- | 309 | * |
|
- | 310 | * @return PTE on success, NULL otherwise. |
|
- | 311 | */ |
|
- | 312 | pte_t *find_mapping_and_check(__address badvaddr) |
|
- | 313 | { |
|
- | 314 | struct entry_hi hi; |
|
- | 315 | pte_t *pte; |
|
- | 316 | ||
- | 317 | *((__u32 *) &hi) = cp0_entry_hi_read(); |
|
- | 318 | ||
- | 319 | /* |
|
- | 320 | * Handler cannot succeed if the ASIDs don't match. |
|
- | 321 | */ |
|
- | 322 | if (hi.asid != VM->asid) |
|
- | 323 | return NULL; |
|
- | 324 | ||
- | 325 | /* |
|
- | 326 | * Handler cannot succeed if badvaddr has no mapping. |
|
- | 327 | */ |
|
- | 328 | pte = find_mapping(badvaddr, 0); |
|
- | 329 | if (!pte) |
|
- | 330 | return NULL; |
|
- | 331 | ||
- | 332 | /* |
|
- | 333 | * Handler cannot succeed if the mapping is marked as invalid. |
|
- | 334 | */ |
|
- | 335 | if (!pte->v) |
|
- | 336 | return NULL; |
|
- | 337 | ||
- | 338 | return pte; |
|
- | 339 | } |
|
- | 340 | ||
- | 341 | void prepare_entry_lo(struct entry_lo *lo, bool g, bool v, bool d, bool c, __address pfn) |
|
- | 342 | { |
|
- | 343 | lo->g = g; |
|
- | 344 | lo->v = v; |
|
- | 345 | lo->d = d; |
|
- | 346 | lo->c = c; |
|
- | 347 | lo->pfn = pfn; |
|
- | 348 | lo->zero = 0; |
|
- | 349 | } |