dv-bfin_mmu.c revision 1.6.4.1 1 1.1 christos /* Blackfin Memory Management Unit (MMU) model.
2 1.1 christos
3 1.6.4.1 christos Copyright (C) 2010-2017 Free Software Foundation, Inc.
4 1.1 christos Contributed by Analog Devices, Inc.
5 1.1 christos
6 1.1 christos This file is part of simulators.
7 1.1 christos
8 1.1 christos This program is free software; you can redistribute it and/or modify
9 1.1 christos it under the terms of the GNU General Public License as published by
10 1.1 christos the Free Software Foundation; either version 3 of the License, or
11 1.1 christos (at your option) any later version.
12 1.1 christos
13 1.1 christos This program is distributed in the hope that it will be useful,
14 1.1 christos but WITHOUT ANY WARRANTY; without even the implied warranty of
15 1.1 christos MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 1.1 christos GNU General Public License for more details.
17 1.1 christos
18 1.1 christos You should have received a copy of the GNU General Public License
19 1.1 christos along with this program. If not, see <http://www.gnu.org/licenses/>. */
20 1.1 christos
21 1.1 christos #include "config.h"
22 1.1 christos
23 1.1 christos #include "sim-main.h"
24 1.1 christos #include "sim-options.h"
25 1.1 christos #include "devices.h"
26 1.1 christos #include "dv-bfin_mmu.h"
27 1.1 christos #include "dv-bfin_cec.h"
28 1.1 christos
29 1.1 christos /* XXX: Should this really be two blocks of registers ? PRM describes
30 1.1 christos these as two Content Addressable Memory (CAM) blocks. */
31 1.1 christos
32 1.1 christos struct bfin_mmu
33 1.1 christos {
34 1.1 christos bu32 base;
35 1.1 christos
36 1.1 christos /* Order after here is important -- matches hardware MMR layout. */
37 1.1 christos bu32 sram_base_address;
38 1.1 christos
39 1.1 christos bu32 dmem_control, dcplb_fault_status, dcplb_fault_addr;
40 1.1 christos char _dpad0[0x100 - 0x0 - (4 * 4)];
41 1.1 christos bu32 dcplb_addr[16];
42 1.1 christos char _dpad1[0x200 - 0x100 - (4 * 16)];
43 1.1 christos bu32 dcplb_data[16];
44 1.1 christos char _dpad2[0x300 - 0x200 - (4 * 16)];
45 1.1 christos bu32 dtest_command;
46 1.1 christos char _dpad3[0x400 - 0x300 - (4 * 1)];
47 1.1 christos bu32 dtest_data[2];
48 1.1 christos
49 1.1 christos char _dpad4[0x1000 - 0x400 - (4 * 2)];
50 1.1 christos
51 1.1 christos bu32 idk; /* Filler MMR; hardware simply ignores. */
52 1.1 christos bu32 imem_control, icplb_fault_status, icplb_fault_addr;
53 1.1 christos char _ipad0[0x100 - 0x0 - (4 * 4)];
54 1.1 christos bu32 icplb_addr[16];
55 1.1 christos char _ipad1[0x200 - 0x100 - (4 * 16)];
56 1.1 christos bu32 icplb_data[16];
57 1.1 christos char _ipad2[0x300 - 0x200 - (4 * 16)];
58 1.1 christos bu32 itest_command;
59 1.1 christos char _ipad3[0x400 - 0x300 - (4 * 1)];
60 1.1 christos bu32 itest_data[2];
61 1.1 christos };
62 1.1 christos #define mmr_base() offsetof(struct bfin_mmu, sram_base_address)
63 1.1 christos #define mmr_offset(mmr) (offsetof(struct bfin_mmu, mmr) - mmr_base())
64 1.1 christos #define mmr_idx(mmr) (mmr_offset (mmr) / 4)
65 1.1 christos
66 1.1 christos static const char * const mmr_names[BFIN_COREMMR_MMU_SIZE / 4] =
67 1.1 christos {
68 1.1 christos "SRAM_BASE_ADDRESS", "DMEM_CONTROL", "DCPLB_FAULT_STATUS", "DCPLB_FAULT_ADDR",
69 1.1 christos [mmr_idx (dcplb_addr[0])] = "DCPLB_ADDR0",
70 1.1 christos "DCPLB_ADDR1", "DCPLB_ADDR2", "DCPLB_ADDR3", "DCPLB_ADDR4", "DCPLB_ADDR5",
71 1.1 christos "DCPLB_ADDR6", "DCPLB_ADDR7", "DCPLB_ADDR8", "DCPLB_ADDR9", "DCPLB_ADDR10",
72 1.1 christos "DCPLB_ADDR11", "DCPLB_ADDR12", "DCPLB_ADDR13", "DCPLB_ADDR14", "DCPLB_ADDR15",
73 1.1 christos [mmr_idx (dcplb_data[0])] = "DCPLB_DATA0",
74 1.1 christos "DCPLB_DATA1", "DCPLB_DATA2", "DCPLB_DATA3", "DCPLB_DATA4", "DCPLB_DATA5",
75 1.1 christos "DCPLB_DATA6", "DCPLB_DATA7", "DCPLB_DATA8", "DCPLB_DATA9", "DCPLB_DATA10",
76 1.1 christos "DCPLB_DATA11", "DCPLB_DATA12", "DCPLB_DATA13", "DCPLB_DATA14", "DCPLB_DATA15",
77 1.1 christos [mmr_idx (dtest_command)] = "DTEST_COMMAND",
78 1.1 christos [mmr_idx (dtest_data[0])] = "DTEST_DATA0", "DTEST_DATA1",
79 1.1 christos [mmr_idx (imem_control)] = "IMEM_CONTROL", "ICPLB_FAULT_STATUS", "ICPLB_FAULT_ADDR",
80 1.1 christos [mmr_idx (icplb_addr[0])] = "ICPLB_ADDR0",
81 1.1 christos "ICPLB_ADDR1", "ICPLB_ADDR2", "ICPLB_ADDR3", "ICPLB_ADDR4", "ICPLB_ADDR5",
82 1.1 christos "ICPLB_ADDR6", "ICPLB_ADDR7", "ICPLB_ADDR8", "ICPLB_ADDR9", "ICPLB_ADDR10",
83 1.1 christos "ICPLB_ADDR11", "ICPLB_ADDR12", "ICPLB_ADDR13", "ICPLB_ADDR14", "ICPLB_ADDR15",
84 1.1 christos [mmr_idx (icplb_data[0])] = "ICPLB_DATA0",
85 1.1 christos "ICPLB_DATA1", "ICPLB_DATA2", "ICPLB_DATA3", "ICPLB_DATA4", "ICPLB_DATA5",
86 1.1 christos "ICPLB_DATA6", "ICPLB_DATA7", "ICPLB_DATA8", "ICPLB_DATA9", "ICPLB_DATA10",
87 1.1 christos "ICPLB_DATA11", "ICPLB_DATA12", "ICPLB_DATA13", "ICPLB_DATA14", "ICPLB_DATA15",
88 1.1 christos [mmr_idx (itest_command)] = "ITEST_COMMAND",
89 1.1 christos [mmr_idx (itest_data[0])] = "ITEST_DATA0", "ITEST_DATA1",
90 1.1 christos };
91 1.1 christos #define mmr_name(off) (mmr_names[(off) / 4] ? : "<INV>")
92 1.1 christos
93 1.1 christos static bool bfin_mmu_skip_cplbs = false;
94 1.1 christos
95 1.1 christos static unsigned
96 1.1 christos bfin_mmu_io_write_buffer (struct hw *me, const void *source,
97 1.1 christos int space, address_word addr, unsigned nr_bytes)
98 1.1 christos {
99 1.1 christos struct bfin_mmu *mmu = hw_data (me);
100 1.1 christos bu32 mmr_off;
101 1.1 christos bu32 value;
102 1.1 christos bu32 *valuep;
103 1.1 christos
104 1.6 christos /* Invalid access mode is higher priority than missing register. */
105 1.6 christos if (!dv_bfin_mmr_require_32 (me, addr, nr_bytes, true))
106 1.6 christos return 0;
107 1.6 christos
108 1.1 christos value = dv_load_4 (source);
109 1.1 christos
110 1.1 christos mmr_off = addr - mmu->base;
111 1.1 christos valuep = (void *)((unsigned long)mmu + mmr_base() + mmr_off);
112 1.1 christos
113 1.1 christos HW_TRACE_WRITE ();
114 1.1 christos
115 1.1 christos switch (mmr_off)
116 1.1 christos {
117 1.1 christos case mmr_offset(dmem_control):
118 1.1 christos case mmr_offset(imem_control):
119 1.1 christos /* XXX: IMC/DMC bit should add/remove L1 cache regions ... */
120 1.1 christos case mmr_offset(dtest_data[0]) ... mmr_offset(dtest_data[1]):
121 1.1 christos case mmr_offset(itest_data[0]) ... mmr_offset(itest_data[1]):
122 1.1 christos case mmr_offset(dcplb_addr[0]) ... mmr_offset(dcplb_addr[15]):
123 1.1 christos case mmr_offset(dcplb_data[0]) ... mmr_offset(dcplb_data[15]):
124 1.1 christos case mmr_offset(icplb_addr[0]) ... mmr_offset(icplb_addr[15]):
125 1.1 christos case mmr_offset(icplb_data[0]) ... mmr_offset(icplb_data[15]):
126 1.1 christos *valuep = value;
127 1.1 christos break;
128 1.1 christos case mmr_offset(sram_base_address):
129 1.1 christos case mmr_offset(dcplb_fault_status):
130 1.1 christos case mmr_offset(dcplb_fault_addr):
131 1.1 christos case mmr_offset(idk):
132 1.1 christos case mmr_offset(icplb_fault_status):
133 1.1 christos case mmr_offset(icplb_fault_addr):
134 1.1 christos /* Discard writes to these. */
135 1.1 christos break;
136 1.1 christos case mmr_offset(itest_command):
137 1.1 christos /* XXX: Not supported atm. */
138 1.1 christos if (value)
139 1.1 christos hw_abort (me, "ITEST_COMMAND unimplemented");
140 1.1 christos break;
141 1.1 christos case mmr_offset(dtest_command):
142 1.1 christos /* Access L1 memory indirectly. */
143 1.1 christos *valuep = value;
144 1.1 christos if (value)
145 1.1 christos {
146 1.1 christos bu32 addr = mmu->sram_base_address |
147 1.1 christos ((value >> (26 - 11)) & (1 << 11)) | /* addr bit 11 (Way0/Way1) */
148 1.1 christos ((value >> (24 - 21)) & (1 << 21)) | /* addr bit 21 (Data/Inst) */
149 1.1 christos ((value >> (23 - 15)) & (1 << 15)) | /* addr bit 15 (Data Bank) */
150 1.1 christos ((value >> (16 - 12)) & (3 << 12)) | /* addr bits 13:12 (Subbank) */
151 1.1 christos (value & 0x47F8); /* addr bits 14 & 10:3 */
152 1.1 christos
153 1.1 christos if (!(value & TEST_DATA_ARRAY))
154 1.1 christos hw_abort (me, "DTEST_COMMAND tag array unimplemented");
155 1.1 christos if (value & 0xfa7cb801)
156 1.1 christos hw_abort (me, "DTEST_COMMAND bits undefined");
157 1.1 christos
158 1.1 christos if (value & TEST_WRITE)
159 1.1 christos sim_write (hw_system (me), addr, (void *)mmu->dtest_data, 8);
160 1.1 christos else
161 1.1 christos sim_read (hw_system (me), addr, (void *)mmu->dtest_data, 8);
162 1.1 christos }
163 1.1 christos break;
164 1.1 christos default:
165 1.1 christos dv_bfin_mmr_invalid (me, addr, nr_bytes, true);
166 1.6 christos return 0;
167 1.1 christos }
168 1.1 christos
169 1.1 christos return nr_bytes;
170 1.1 christos }
171 1.1 christos
172 1.1 christos static unsigned
173 1.1 christos bfin_mmu_io_read_buffer (struct hw *me, void *dest,
174 1.1 christos int space, address_word addr, unsigned nr_bytes)
175 1.1 christos {
176 1.1 christos struct bfin_mmu *mmu = hw_data (me);
177 1.1 christos bu32 mmr_off;
178 1.1 christos bu32 *valuep;
179 1.1 christos
180 1.6 christos /* Invalid access mode is higher priority than missing register. */
181 1.6 christos if (!dv_bfin_mmr_require_32 (me, addr, nr_bytes, false))
182 1.6 christos return 0;
183 1.6 christos
184 1.1 christos mmr_off = addr - mmu->base;
185 1.1 christos valuep = (void *)((unsigned long)mmu + mmr_base() + mmr_off);
186 1.1 christos
187 1.1 christos HW_TRACE_READ ();
188 1.1 christos
189 1.1 christos switch (mmr_off)
190 1.1 christos {
191 1.1 christos case mmr_offset(dmem_control):
192 1.1 christos case mmr_offset(imem_control):
193 1.1 christos case mmr_offset(dtest_command):
194 1.1 christos case mmr_offset(dtest_data[0]) ... mmr_offset(dtest_data[2]):
195 1.1 christos case mmr_offset(itest_command):
196 1.1 christos case mmr_offset(itest_data[0]) ... mmr_offset(itest_data[2]):
197 1.1 christos /* XXX: should do something here. */
198 1.1 christos case mmr_offset(dcplb_addr[0]) ... mmr_offset(dcplb_addr[15]):
199 1.1 christos case mmr_offset(dcplb_data[0]) ... mmr_offset(dcplb_data[15]):
200 1.1 christos case mmr_offset(icplb_addr[0]) ... mmr_offset(icplb_addr[15]):
201 1.1 christos case mmr_offset(icplb_data[0]) ... mmr_offset(icplb_data[15]):
202 1.1 christos case mmr_offset(sram_base_address):
203 1.1 christos case mmr_offset(dcplb_fault_status):
204 1.1 christos case mmr_offset(dcplb_fault_addr):
205 1.1 christos case mmr_offset(idk):
206 1.1 christos case mmr_offset(icplb_fault_status):
207 1.1 christos case mmr_offset(icplb_fault_addr):
208 1.1 christos dv_store_4 (dest, *valuep);
209 1.1 christos break;
210 1.1 christos default:
211 1.6 christos dv_bfin_mmr_invalid (me, addr, nr_bytes, false);
212 1.6 christos return 0;
213 1.1 christos }
214 1.1 christos
215 1.1 christos return nr_bytes;
216 1.1 christos }
217 1.1 christos
218 1.1 christos static void
219 1.1 christos attach_bfin_mmu_regs (struct hw *me, struct bfin_mmu *mmu)
220 1.1 christos {
221 1.1 christos address_word attach_address;
222 1.1 christos int attach_space;
223 1.1 christos unsigned attach_size;
224 1.1 christos reg_property_spec reg;
225 1.1 christos
226 1.1 christos if (hw_find_property (me, "reg") == NULL)
227 1.1 christos hw_abort (me, "Missing \"reg\" property");
228 1.1 christos
229 1.1 christos if (!hw_find_reg_array_property (me, "reg", 0, ®))
230 1.1 christos hw_abort (me, "\"reg\" property must contain three addr/size entries");
231 1.1 christos
232 1.1 christos hw_unit_address_to_attach_address (hw_parent (me),
233 1.1 christos ®.address,
234 1.1 christos &attach_space, &attach_address, me);
235 1.1 christos hw_unit_size_to_attach_size (hw_parent (me), ®.size, &attach_size, me);
236 1.1 christos
237 1.1 christos if (attach_size != BFIN_COREMMR_MMU_SIZE)
238 1.1 christos hw_abort (me, "\"reg\" size must be %#x", BFIN_COREMMR_MMU_SIZE);
239 1.1 christos
240 1.1 christos hw_attach_address (hw_parent (me),
241 1.1 christos 0, attach_space, attach_address, attach_size, me);
242 1.1 christos
243 1.1 christos mmu->base = attach_address;
244 1.1 christos }
245 1.1 christos
246 1.1 christos static void
247 1.1 christos bfin_mmu_finish (struct hw *me)
248 1.1 christos {
249 1.1 christos struct bfin_mmu *mmu;
250 1.1 christos
251 1.1 christos mmu = HW_ZALLOC (me, struct bfin_mmu);
252 1.1 christos
253 1.1 christos set_hw_data (me, mmu);
254 1.1 christos set_hw_io_read_buffer (me, bfin_mmu_io_read_buffer);
255 1.1 christos set_hw_io_write_buffer (me, bfin_mmu_io_write_buffer);
256 1.1 christos
257 1.1 christos attach_bfin_mmu_regs (me, mmu);
258 1.1 christos
259 1.1 christos /* Initialize the MMU. */
260 1.1 christos mmu->sram_base_address = 0xff800000 - 0;
261 1.1 christos /*(4 * 1024 * 1024 * CPU_INDEX (hw_system_cpu (me)));*/
262 1.1 christos mmu->dmem_control = 0x00000001;
263 1.1 christos mmu->imem_control = 0x00000001;
264 1.1 christos }
265 1.1 christos
266 1.1 christos const struct hw_descriptor dv_bfin_mmu_descriptor[] =
267 1.1 christos {
268 1.1 christos {"bfin_mmu", bfin_mmu_finish,},
269 1.1 christos {NULL, NULL},
270 1.1 christos };
271 1.1 christos
272 1.1 christos /* Device option parsing. */
274 1.1 christos
275 1.1 christos static DECLARE_OPTION_HANDLER (bfin_mmu_option_handler);
276 1.1 christos
277 1.1 christos enum {
278 1.1 christos OPTION_MMU_SKIP_TABLES = OPTION_START,
279 1.1 christos };
280 1.1 christos
281 1.1 christos const OPTION bfin_mmu_options[] =
282 1.1 christos {
283 1.1 christos { {"mmu-skip-cplbs", no_argument, NULL, OPTION_MMU_SKIP_TABLES },
284 1.1 christos '\0', NULL, "Skip parsing of CPLB tables (big speed increase)",
285 1.1 christos bfin_mmu_option_handler, NULL },
286 1.1 christos
287 1.1 christos { {NULL, no_argument, NULL, 0}, '\0', NULL, NULL, NULL, NULL }
288 1.1 christos };
289 1.1 christos
290 1.1 christos static SIM_RC
291 1.1 christos bfin_mmu_option_handler (SIM_DESC sd, sim_cpu *current_cpu, int opt,
292 1.1 christos char *arg, int is_command)
293 1.1 christos {
294 1.1 christos switch (opt)
295 1.1 christos {
296 1.1 christos case OPTION_MMU_SKIP_TABLES:
297 1.1 christos bfin_mmu_skip_cplbs = true;
298 1.1 christos return SIM_RC_OK;
299 1.1 christos
300 1.1 christos default:
301 1.1 christos sim_io_eprintf (sd, "Unknown Blackfin MMU option %d\n", opt);
302 1.1 christos return SIM_RC_FAIL;
303 1.1 christos }
304 1.1 christos }
305 1.1 christos
306 1.1 christos #define MMU_STATE(cpu) DV_STATE_CACHED (cpu, mmu)
308 1.1 christos
309 1.1 christos static void
310 1.1 christos _mmu_log_ifault (SIM_CPU *cpu, struct bfin_mmu *mmu, bu32 pc, bool supv)
311 1.1 christos {
312 1.1 christos mmu->icplb_fault_addr = pc;
313 1.1 christos mmu->icplb_fault_status = supv << 17;
314 1.1 christos }
315 1.1 christos
316 1.1 christos void
317 1.1 christos mmu_log_ifault (SIM_CPU *cpu)
318 1.1 christos {
319 1.1 christos _mmu_log_ifault (cpu, MMU_STATE (cpu), PCREG, cec_get_ivg (cpu) >= 0);
320 1.1 christos }
321 1.1 christos
322 1.1 christos static void
323 1.1 christos _mmu_log_fault (SIM_CPU *cpu, struct bfin_mmu *mmu, bu32 addr, bool write,
324 1.1 christos bool inst, bool miss, bool supv, bool dag1, bu32 faults)
325 1.1 christos {
326 1.1 christos bu32 *fault_status, *fault_addr;
327 1.1 christos
328 1.1 christos /* No logging in non-OS mode. */
329 1.1 christos if (!mmu)
330 1.1 christos return;
331 1.1 christos
332 1.1 christos fault_status = inst ? &mmu->icplb_fault_status : &mmu->dcplb_fault_status;
333 1.1 christos fault_addr = inst ? &mmu->icplb_fault_addr : &mmu->dcplb_fault_addr;
334 1.1 christos /* ICPLB regs always get updated. */
335 1.1 christos if (!inst)
336 1.1 christos _mmu_log_ifault (cpu, mmu, PCREG, supv);
337 1.1 christos
338 1.1 christos *fault_addr = addr;
339 1.1 christos *fault_status =
340 1.1 christos (miss << 19) |
341 1.1 christos (dag1 << 18) |
342 1.1 christos (supv << 17) |
343 1.1 christos (write << 16) |
344 1.1 christos faults;
345 1.1 christos }
346 1.1 christos
347 1.1 christos static void
348 1.1 christos _mmu_process_fault (SIM_CPU *cpu, struct bfin_mmu *mmu, bu32 addr, bool write,
349 1.1 christos bool inst, bool unaligned, bool miss, bool supv, bool dag1)
350 1.1 christos {
351 1.1 christos int excp;
352 1.1 christos
353 1.1 christos /* See order in mmu_check_addr() */
354 1.1 christos if (unaligned)
355 1.1 christos excp = inst ? VEC_MISALI_I : VEC_MISALI_D;
356 1.1 christos else if (addr >= BFIN_SYSTEM_MMR_BASE)
357 1.1 christos excp = VEC_ILL_RES;
358 1.1 christos else if (!mmu)
359 1.1 christos excp = inst ? VEC_CPLB_I_M : VEC_CPLB_M;
360 1.1 christos else
361 1.1 christos {
362 1.1 christos /* Misses are hardware errors. */
363 1.1 christos cec_hwerr (cpu, HWERR_EXTERN_ADDR);
364 1.1 christos return;
365 1.1 christos }
366 1.1 christos
367 1.1 christos _mmu_log_fault (cpu, mmu, addr, write, inst, miss, supv, dag1, 0);
368 1.1 christos cec_exception (cpu, excp);
369 1.1 christos }
370 1.1 christos
371 1.1 christos void
372 1.1 christos mmu_process_fault (SIM_CPU *cpu, bu32 addr, bool write, bool inst,
373 1.1 christos bool unaligned, bool miss)
374 1.1 christos {
375 1.1 christos SIM_DESC sd = CPU_STATE (cpu);
376 1.1 christos struct bfin_mmu *mmu;
377 1.1 christos
378 1.1 christos if (STATE_ENVIRONMENT (sd) != OPERATING_ENVIRONMENT)
379 1.1 christos mmu = NULL;
380 1.1 christos else
381 1.1 christos mmu = MMU_STATE (cpu);
382 1.1 christos
383 1.1 christos _mmu_process_fault (cpu, mmu, addr, write, inst, unaligned, miss,
384 1.1 christos cec_is_supervisor_mode (cpu),
385 1.1 christos BFIN_CPU_STATE.multi_pc == PCREG + 6);
386 1.1 christos }
387 1.1 christos
388 1.1 christos /* Return values:
389 1.1 christos -2: no known problems
390 1.1 christos -1: valid
391 1.1 christos 0: miss
392 1.1 christos 1: protection violation
393 1.1 christos 2: multiple hits
394 1.1 christos 3: unaligned
395 1.1 christos 4: miss; hwerr */
396 1.1 christos static int
397 1.1 christos mmu_check_implicit_addr (SIM_CPU *cpu, bu32 addr, bool inst, int size,
398 1.1 christos bool supv, bool dag1)
399 1.1 christos {
400 1.1 christos bool l1 = ((addr & 0xFF000000) == 0xFF000000);
401 1.1 christos bu32 amask = (addr & 0xFFF00000);
402 1.1 christos
403 1.1 christos if (addr & (size - 1))
404 1.1 christos return 3;
405 1.1 christos
406 1.1 christos /* MMRs may never be executable or accessed from usermode. */
407 1.1 christos if (addr >= BFIN_SYSTEM_MMR_BASE)
408 1.1 christos {
409 1.1 christos if (inst)
410 1.1 christos return 0;
411 1.1 christos else if (!supv || dag1)
412 1.1 christos return 1;
413 1.1 christos else
414 1.1 christos return -1;
415 1.1 christos }
416 1.1 christos else if (inst)
417 1.1 christos {
418 1.1 christos /* Some regions are not executable. */
419 1.1 christos /* XXX: Should this be in the model data ? Core B 561 ? */
420 1.1 christos if (l1)
421 1.1 christos return (amask == 0xFFA00000) ? -1 : 1;
422 1.1 christos }
423 1.1 christos else
424 1.1 christos {
425 1.1 christos /* Some regions are not readable. */
426 1.1 christos /* XXX: Should this be in the model data ? Core B 561 ? */
427 1.1 christos if (l1)
428 1.1 christos return (amask != 0xFFA00000) ? -1 : 4;
429 1.1 christos }
430 1.1 christos
431 1.1 christos return -2;
432 1.1 christos }
433 1.1 christos
434 1.1 christos /* Exception order per the PRM (first has highest):
435 1.1 christos Inst Multiple CPLB Hits
436 1.1 christos Inst Misaligned Access
437 1.1 christos Inst Protection Violation
438 1.1 christos Inst CPLB Miss
439 1.1 christos Only the alignment matters in non-OS mode though. */
440 1.1 christos static int
441 1.1 christos _mmu_check_addr (SIM_CPU *cpu, bu32 addr, bool write, bool inst, int size)
442 1.1 christos {
443 1.1 christos SIM_DESC sd = CPU_STATE (cpu);
444 1.1 christos struct bfin_mmu *mmu;
445 1.1 christos bu32 *fault_status, *fault_addr, *mem_control, *cplb_addr, *cplb_data;
446 1.1 christos bu32 faults;
447 1.1 christos bool supv, do_excp, dag1;
448 1.1 christos int i, hits;
449 1.1 christos
450 1.1 christos supv = cec_is_supervisor_mode (cpu);
451 1.1 christos dag1 = (BFIN_CPU_STATE.multi_pc == PCREG + 6);
452 1.1 christos
453 1.1 christos if (STATE_ENVIRONMENT (sd) != OPERATING_ENVIRONMENT || bfin_mmu_skip_cplbs)
454 1.1 christos {
455 1.1 christos int ret = mmu_check_implicit_addr (cpu, addr, inst, size, supv, dag1);
456 1.1 christos /* Valid hits and misses are OK in non-OS envs. */
457 1.1 christos if (ret < 0)
458 1.1 christos return 0;
459 1.1 christos _mmu_process_fault (cpu, NULL, addr, write, inst, (ret == 3), false, supv, dag1);
460 1.1 christos }
461 1.1 christos
462 1.1 christos mmu = MMU_STATE (cpu);
463 1.1 christos fault_status = inst ? &mmu->icplb_fault_status : &mmu->dcplb_fault_status;
464 1.1 christos fault_addr = inst ? &mmu->icplb_fault_addr : &mmu->dcplb_fault_addr;
465 1.1 christos mem_control = inst ? &mmu->imem_control : &mmu->dmem_control;
466 1.1 christos cplb_addr = inst ? &mmu->icplb_addr[0] : &mmu->dcplb_addr[0];
467 1.1 christos cplb_data = inst ? &mmu->icplb_data[0] : &mmu->dcplb_data[0];
468 1.1 christos
469 1.1 christos faults = 0;
470 1.1 christos hits = 0;
471 1.1 christos do_excp = false;
472 1.1 christos
473 1.1 christos /* CPLBs disabled -> little to do. */
474 1.1 christos if (!(*mem_control & ENCPLB))
475 1.1 christos {
476 1.1 christos hits = 1;
477 1.1 christos goto implicit_check;
478 1.1 christos }
479 1.1 christos
480 1.1 christos /* Check all the CPLBs first. */
481 1.1 christos for (i = 0; i < 16; ++i)
482 1.1 christos {
483 1.1 christos const bu32 pages[4] = { 0x400, 0x1000, 0x100000, 0x400000 };
484 1.1 christos bu32 addr_lo, addr_hi;
485 1.1 christos
486 1.1 christos /* Skip invalid entries. */
487 1.1 christos if (!(cplb_data[i] & CPLB_VALID))
488 1.1 christos continue;
489 1.1 christos
490 1.1 christos /* See if this entry covers this address. */
491 1.1 christos addr_lo = cplb_addr[i];
492 1.1 christos addr_hi = cplb_addr[i] + pages[(cplb_data[i] & PAGE_SIZE) >> 16];
493 1.1 christos if (addr < addr_lo || addr >= addr_hi)
494 1.1 christos continue;
495 1.1 christos
496 1.1 christos ++hits;
497 1.1 christos faults |= (1 << i);
498 1.1 christos if (write)
499 1.1 christos {
500 1.1 christos if (!supv && !(cplb_data[i] & CPLB_USER_WR))
501 1.1 christos do_excp = true;
502 1.1 christos if (supv && !(cplb_data[i] & CPLB_SUPV_WR))
503 1.1 christos do_excp = true;
504 1.1 christos if ((cplb_data[i] & (CPLB_WT | CPLB_L1_CHBL | CPLB_DIRTY)) == CPLB_L1_CHBL)
505 1.1 christos do_excp = true;
506 1.1 christos }
507 1.1 christos else
508 1.1 christos {
509 1.1 christos if (!supv && !(cplb_data[i] & CPLB_USER_RD))
510 1.1 christos do_excp = true;
511 1.1 christos }
512 1.1 christos }
513 1.1 christos
514 1.1 christos /* Handle default/implicit CPLBs. */
515 1.1 christos if (!do_excp && hits < 2)
516 1.1 christos {
517 1.1 christos int ihits;
518 1.1 christos implicit_check:
519 1.1 christos ihits = mmu_check_implicit_addr (cpu, addr, inst, size, supv, dag1);
520 1.1 christos switch (ihits)
521 1.1 christos {
522 1.1 christos /* No faults and one match -> good to go. */
523 1.1 christos case -1: return 0;
524 1.1 christos case -2:
525 1.1 christos if (hits == 1)
526 1.1 christos return 0;
527 1.1 christos break;
528 1.1 christos case 4:
529 1.1 christos cec_hwerr (cpu, HWERR_EXTERN_ADDR);
530 1.1 christos return 0;
531 1.1 christos default:
532 1.1 christos hits = ihits;
533 1.1 christos }
534 1.1 christos }
535 1.6 christos else
536 1.1 christos /* Normalize hit count so hits==2 is always multiple hit exception. */
537 1.1 christos hits = min (2, hits);
538 1.1 christos
539 1.1 christos _mmu_log_fault (cpu, mmu, addr, write, inst, hits == 0, supv, dag1, faults);
540 1.1 christos
541 1.1 christos if (inst)
542 1.1 christos {
543 1.1 christos int iexcps[] = { VEC_CPLB_I_M, VEC_CPLB_I_VL, VEC_CPLB_I_MHIT, VEC_MISALI_I };
544 1.1 christos return iexcps[hits];
545 1.1 christos }
546 1.1 christos else
547 1.1 christos {
548 1.1 christos int dexcps[] = { VEC_CPLB_M, VEC_CPLB_VL, VEC_CPLB_MHIT, VEC_MISALI_D };
549 1.1 christos return dexcps[hits];
550 1.1 christos }
551 1.1 christos }
552 1.1 christos
553 1.1 christos void
554 1.1 christos mmu_check_addr (SIM_CPU *cpu, bu32 addr, bool write, bool inst, int size)
555 1.1 christos {
556 1.1 christos int excp = _mmu_check_addr (cpu, addr, write, inst, size);
557 1.1 christos if (excp)
558 1.1 christos cec_exception (cpu, excp);
559 1.1 christos }
560 1.1 christos
561 1.1 christos void
562 1.1 christos mmu_check_cache_addr (SIM_CPU *cpu, bu32 addr, bool write, bool inst)
563 1.1 christos {
564 1.1 christos bu32 cacheaddr;
565 1.1 christos int excp;
566 1.1 christos
567 1.1 christos cacheaddr = addr & ~(BFIN_L1_CACHE_BYTES - 1);
568 1.1 christos excp = _mmu_check_addr (cpu, cacheaddr, write, inst, BFIN_L1_CACHE_BYTES);
569 1.1 christos if (excp == 0)
570 1.1 christos return;
571 1.1 christos
572 1.1 christos /* Most exceptions are ignored with cache funcs. */
573 1.1 christos /* XXX: Not sure if we should be ignoring CPLB misses. */
574 1.1 christos if (inst)
575 1.1 christos {
576 1.1 christos if (excp == VEC_CPLB_I_VL)
577 1.1 christos return;
578 1.1 christos }
579 1.1 christos else
580 1.1 christos {
581 1.1 christos if (excp == VEC_CPLB_VL)
582 1.1 christos return;
583 1.1 christos }
584 cec_exception (cpu, excp);
585 }
586