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