dv-bfin_mmu.c revision 1.1.1.1.2.1 1 1.1 christos /* Blackfin Memory Management Unit (MMU) model.
2 1.1 christos
3 1.1.1.1.2.1 yamt Copyright (C) 2010-2013 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.1 christos value = dv_load_4 (source);
105 1.1 christos
106 1.1 christos mmr_off = addr - mmu->base;
107 1.1 christos valuep = (void *)((unsigned long)mmu + mmr_base() + mmr_off);
108 1.1 christos
109 1.1 christos HW_TRACE_WRITE ();
110 1.1 christos
111 1.1 christos switch (mmr_off)
112 1.1 christos {
113 1.1 christos case mmr_offset(dmem_control):
114 1.1 christos case mmr_offset(imem_control):
115 1.1 christos /* XXX: IMC/DMC bit should add/remove L1 cache regions ... */
116 1.1 christos case mmr_offset(dtest_data[0]) ... mmr_offset(dtest_data[1]):
117 1.1 christos case mmr_offset(itest_data[0]) ... mmr_offset(itest_data[1]):
118 1.1 christos case mmr_offset(dcplb_addr[0]) ... mmr_offset(dcplb_addr[15]):
119 1.1 christos case mmr_offset(dcplb_data[0]) ... mmr_offset(dcplb_data[15]):
120 1.1 christos case mmr_offset(icplb_addr[0]) ... mmr_offset(icplb_addr[15]):
121 1.1 christos case mmr_offset(icplb_data[0]) ... mmr_offset(icplb_data[15]):
122 1.1 christos *valuep = value;
123 1.1 christos break;
124 1.1 christos case mmr_offset(sram_base_address):
125 1.1 christos case mmr_offset(dcplb_fault_status):
126 1.1 christos case mmr_offset(dcplb_fault_addr):
127 1.1 christos case mmr_offset(idk):
128 1.1 christos case mmr_offset(icplb_fault_status):
129 1.1 christos case mmr_offset(icplb_fault_addr):
130 1.1 christos /* Discard writes to these. */
131 1.1 christos break;
132 1.1 christos case mmr_offset(itest_command):
133 1.1 christos /* XXX: Not supported atm. */
134 1.1 christos if (value)
135 1.1 christos hw_abort (me, "ITEST_COMMAND unimplemented");
136 1.1 christos break;
137 1.1 christos case mmr_offset(dtest_command):
138 1.1 christos /* Access L1 memory indirectly. */
139 1.1 christos *valuep = value;
140 1.1 christos if (value)
141 1.1 christos {
142 1.1 christos bu32 addr = mmu->sram_base_address |
143 1.1 christos ((value >> (26 - 11)) & (1 << 11)) | /* addr bit 11 (Way0/Way1) */
144 1.1 christos ((value >> (24 - 21)) & (1 << 21)) | /* addr bit 21 (Data/Inst) */
145 1.1 christos ((value >> (23 - 15)) & (1 << 15)) | /* addr bit 15 (Data Bank) */
146 1.1 christos ((value >> (16 - 12)) & (3 << 12)) | /* addr bits 13:12 (Subbank) */
147 1.1 christos (value & 0x47F8); /* addr bits 14 & 10:3 */
148 1.1 christos
149 1.1 christos if (!(value & TEST_DATA_ARRAY))
150 1.1 christos hw_abort (me, "DTEST_COMMAND tag array unimplemented");
151 1.1 christos if (value & 0xfa7cb801)
152 1.1 christos hw_abort (me, "DTEST_COMMAND bits undefined");
153 1.1 christos
154 1.1 christos if (value & TEST_WRITE)
155 1.1 christos sim_write (hw_system (me), addr, (void *)mmu->dtest_data, 8);
156 1.1 christos else
157 1.1 christos sim_read (hw_system (me), addr, (void *)mmu->dtest_data, 8);
158 1.1 christos }
159 1.1 christos break;
160 1.1 christos default:
161 1.1 christos dv_bfin_mmr_invalid (me, addr, nr_bytes, true);
162 1.1 christos break;
163 1.1 christos }
164 1.1 christos
165 1.1 christos return nr_bytes;
166 1.1 christos }
167 1.1 christos
168 1.1 christos static unsigned
169 1.1 christos bfin_mmu_io_read_buffer (struct hw *me, void *dest,
170 1.1 christos int space, address_word addr, unsigned nr_bytes)
171 1.1 christos {
172 1.1 christos struct bfin_mmu *mmu = hw_data (me);
173 1.1 christos bu32 mmr_off;
174 1.1 christos bu32 *valuep;
175 1.1 christos
176 1.1 christos mmr_off = addr - mmu->base;
177 1.1 christos valuep = (void *)((unsigned long)mmu + mmr_base() + mmr_off);
178 1.1 christos
179 1.1 christos HW_TRACE_READ ();
180 1.1 christos
181 1.1 christos switch (mmr_off)
182 1.1 christos {
183 1.1 christos case mmr_offset(dmem_control):
184 1.1 christos case mmr_offset(imem_control):
185 1.1 christos case mmr_offset(dtest_command):
186 1.1 christos case mmr_offset(dtest_data[0]) ... mmr_offset(dtest_data[2]):
187 1.1 christos case mmr_offset(itest_command):
188 1.1 christos case mmr_offset(itest_data[0]) ... mmr_offset(itest_data[2]):
189 1.1 christos /* XXX: should do something here. */
190 1.1 christos case mmr_offset(dcplb_addr[0]) ... mmr_offset(dcplb_addr[15]):
191 1.1 christos case mmr_offset(dcplb_data[0]) ... mmr_offset(dcplb_data[15]):
192 1.1 christos case mmr_offset(icplb_addr[0]) ... mmr_offset(icplb_addr[15]):
193 1.1 christos case mmr_offset(icplb_data[0]) ... mmr_offset(icplb_data[15]):
194 1.1 christos case mmr_offset(sram_base_address):
195 1.1 christos case mmr_offset(dcplb_fault_status):
196 1.1 christos case mmr_offset(dcplb_fault_addr):
197 1.1 christos case mmr_offset(idk):
198 1.1 christos case mmr_offset(icplb_fault_status):
199 1.1 christos case mmr_offset(icplb_fault_addr):
200 1.1 christos dv_store_4 (dest, *valuep);
201 1.1 christos break;
202 1.1 christos default:
203 1.1 christos while (1) /* Core MMRs -> exception -> doesn't return. */
204 1.1 christos dv_bfin_mmr_invalid (me, addr, nr_bytes, false);
205 1.1 christos break;
206 1.1 christos }
207 1.1 christos
208 1.1 christos return nr_bytes;
209 1.1 christos }
210 1.1 christos
211 1.1 christos static void
212 1.1 christos attach_bfin_mmu_regs (struct hw *me, struct bfin_mmu *mmu)
213 1.1 christos {
214 1.1 christos address_word attach_address;
215 1.1 christos int attach_space;
216 1.1 christos unsigned attach_size;
217 1.1 christos reg_property_spec reg;
218 1.1 christos
219 1.1 christos if (hw_find_property (me, "reg") == NULL)
220 1.1 christos hw_abort (me, "Missing \"reg\" property");
221 1.1 christos
222 1.1 christos if (!hw_find_reg_array_property (me, "reg", 0, ®))
223 1.1 christos hw_abort (me, "\"reg\" property must contain three addr/size entries");
224 1.1 christos
225 1.1 christos hw_unit_address_to_attach_address (hw_parent (me),
226 1.1 christos ®.address,
227 1.1 christos &attach_space, &attach_address, me);
228 1.1 christos hw_unit_size_to_attach_size (hw_parent (me), ®.size, &attach_size, me);
229 1.1 christos
230 1.1 christos if (attach_size != BFIN_COREMMR_MMU_SIZE)
231 1.1 christos hw_abort (me, "\"reg\" size must be %#x", BFIN_COREMMR_MMU_SIZE);
232 1.1 christos
233 1.1 christos hw_attach_address (hw_parent (me),
234 1.1 christos 0, attach_space, attach_address, attach_size, me);
235 1.1 christos
236 1.1 christos mmu->base = attach_address;
237 1.1 christos }
238 1.1 christos
239 1.1 christos static void
240 1.1 christos bfin_mmu_finish (struct hw *me)
241 1.1 christos {
242 1.1 christos struct bfin_mmu *mmu;
243 1.1 christos
244 1.1 christos mmu = HW_ZALLOC (me, struct bfin_mmu);
245 1.1 christos
246 1.1 christos set_hw_data (me, mmu);
247 1.1 christos set_hw_io_read_buffer (me, bfin_mmu_io_read_buffer);
248 1.1 christos set_hw_io_write_buffer (me, bfin_mmu_io_write_buffer);
249 1.1 christos
250 1.1 christos attach_bfin_mmu_regs (me, mmu);
251 1.1 christos
252 1.1 christos /* Initialize the MMU. */
253 1.1 christos mmu->sram_base_address = 0xff800000 - 0;
254 1.1 christos /*(4 * 1024 * 1024 * CPU_INDEX (hw_system_cpu (me)));*/
255 1.1 christos mmu->dmem_control = 0x00000001;
256 1.1 christos mmu->imem_control = 0x00000001;
257 1.1 christos }
258 1.1 christos
259 1.1 christos const struct hw_descriptor dv_bfin_mmu_descriptor[] =
260 1.1 christos {
261 1.1 christos {"bfin_mmu", bfin_mmu_finish,},
262 1.1 christos {NULL, NULL},
263 1.1 christos };
264 1.1 christos
265 1.1 christos /* Device option parsing. */
267 1.1 christos
268 1.1 christos static DECLARE_OPTION_HANDLER (bfin_mmu_option_handler);
269 1.1 christos
270 1.1 christos enum {
271 1.1 christos OPTION_MMU_SKIP_TABLES = OPTION_START,
272 1.1 christos };
273 1.1 christos
274 1.1 christos const OPTION bfin_mmu_options[] =
275 1.1 christos {
276 1.1 christos { {"mmu-skip-cplbs", no_argument, NULL, OPTION_MMU_SKIP_TABLES },
277 1.1 christos '\0', NULL, "Skip parsing of CPLB tables (big speed increase)",
278 1.1 christos bfin_mmu_option_handler, NULL },
279 1.1 christos
280 1.1 christos { {NULL, no_argument, NULL, 0}, '\0', NULL, NULL, NULL, NULL }
281 1.1 christos };
282 1.1 christos
283 1.1 christos static SIM_RC
284 1.1 christos bfin_mmu_option_handler (SIM_DESC sd, sim_cpu *current_cpu, int opt,
285 1.1 christos char *arg, int is_command)
286 1.1 christos {
287 1.1 christos switch (opt)
288 1.1 christos {
289 1.1 christos case OPTION_MMU_SKIP_TABLES:
290 1.1 christos bfin_mmu_skip_cplbs = true;
291 1.1 christos return SIM_RC_OK;
292 1.1 christos
293 1.1 christos default:
294 1.1 christos sim_io_eprintf (sd, "Unknown Blackfin MMU option %d\n", opt);
295 1.1 christos return SIM_RC_FAIL;
296 1.1 christos }
297 1.1 christos }
298 1.1 christos
299 1.1 christos #define MMU_STATE(cpu) DV_STATE_CACHED (cpu, mmu)
301 1.1 christos
302 1.1 christos static void
303 1.1 christos _mmu_log_ifault (SIM_CPU *cpu, struct bfin_mmu *mmu, bu32 pc, bool supv)
304 1.1 christos {
305 1.1 christos mmu->icplb_fault_addr = pc;
306 1.1 christos mmu->icplb_fault_status = supv << 17;
307 1.1 christos }
308 1.1 christos
309 1.1 christos void
310 1.1 christos mmu_log_ifault (SIM_CPU *cpu)
311 1.1 christos {
312 1.1 christos _mmu_log_ifault (cpu, MMU_STATE (cpu), PCREG, cec_get_ivg (cpu) >= 0);
313 1.1 christos }
314 1.1 christos
315 1.1 christos static void
316 1.1 christos _mmu_log_fault (SIM_CPU *cpu, struct bfin_mmu *mmu, bu32 addr, bool write,
317 1.1 christos bool inst, bool miss, bool supv, bool dag1, bu32 faults)
318 1.1 christos {
319 1.1 christos bu32 *fault_status, *fault_addr;
320 1.1 christos
321 1.1 christos /* No logging in non-OS mode. */
322 1.1 christos if (!mmu)
323 1.1 christos return;
324 1.1 christos
325 1.1 christos fault_status = inst ? &mmu->icplb_fault_status : &mmu->dcplb_fault_status;
326 1.1 christos fault_addr = inst ? &mmu->icplb_fault_addr : &mmu->dcplb_fault_addr;
327 1.1 christos /* ICPLB regs always get updated. */
328 1.1 christos if (!inst)
329 1.1 christos _mmu_log_ifault (cpu, mmu, PCREG, supv);
330 1.1 christos
331 1.1 christos *fault_addr = addr;
332 1.1 christos *fault_status =
333 1.1 christos (miss << 19) |
334 1.1 christos (dag1 << 18) |
335 1.1 christos (supv << 17) |
336 1.1 christos (write << 16) |
337 1.1 christos faults;
338 1.1 christos }
339 1.1 christos
340 1.1 christos static void
341 1.1 christos _mmu_process_fault (SIM_CPU *cpu, struct bfin_mmu *mmu, bu32 addr, bool write,
342 1.1 christos bool inst, bool unaligned, bool miss, bool supv, bool dag1)
343 1.1 christos {
344 1.1 christos int excp;
345 1.1 christos
346 1.1 christos /* See order in mmu_check_addr() */
347 1.1 christos if (unaligned)
348 1.1 christos excp = inst ? VEC_MISALI_I : VEC_MISALI_D;
349 1.1 christos else if (addr >= BFIN_SYSTEM_MMR_BASE)
350 1.1 christos excp = VEC_ILL_RES;
351 1.1 christos else if (!mmu)
352 1.1 christos excp = inst ? VEC_CPLB_I_M : VEC_CPLB_M;
353 1.1 christos else
354 1.1 christos {
355 1.1 christos /* Misses are hardware errors. */
356 1.1 christos cec_hwerr (cpu, HWERR_EXTERN_ADDR);
357 1.1 christos return;
358 1.1 christos }
359 1.1 christos
360 1.1 christos _mmu_log_fault (cpu, mmu, addr, write, inst, miss, supv, dag1, 0);
361 1.1 christos cec_exception (cpu, excp);
362 1.1 christos }
363 1.1 christos
364 1.1 christos void
365 1.1 christos mmu_process_fault (SIM_CPU *cpu, bu32 addr, bool write, bool inst,
366 1.1 christos bool unaligned, bool miss)
367 1.1 christos {
368 1.1 christos SIM_DESC sd = CPU_STATE (cpu);
369 1.1 christos struct bfin_mmu *mmu;
370 1.1 christos
371 1.1 christos if (STATE_ENVIRONMENT (sd) != OPERATING_ENVIRONMENT)
372 1.1 christos mmu = NULL;
373 1.1 christos else
374 1.1 christos mmu = MMU_STATE (cpu);
375 1.1 christos
376 1.1 christos _mmu_process_fault (cpu, mmu, addr, write, inst, unaligned, miss,
377 1.1 christos cec_is_supervisor_mode (cpu),
378 1.1 christos BFIN_CPU_STATE.multi_pc == PCREG + 6);
379 1.1 christos }
380 1.1 christos
381 1.1 christos /* Return values:
382 1.1 christos -2: no known problems
383 1.1 christos -1: valid
384 1.1 christos 0: miss
385 1.1 christos 1: protection violation
386 1.1 christos 2: multiple hits
387 1.1 christos 3: unaligned
388 1.1 christos 4: miss; hwerr */
389 1.1 christos static int
390 1.1 christos mmu_check_implicit_addr (SIM_CPU *cpu, bu32 addr, bool inst, int size,
391 1.1 christos bool supv, bool dag1)
392 1.1 christos {
393 1.1 christos bool l1 = ((addr & 0xFF000000) == 0xFF000000);
394 1.1 christos bu32 amask = (addr & 0xFFF00000);
395 1.1 christos
396 1.1 christos if (addr & (size - 1))
397 1.1 christos return 3;
398 1.1 christos
399 1.1 christos /* MMRs may never be executable or accessed from usermode. */
400 1.1 christos if (addr >= BFIN_SYSTEM_MMR_BASE)
401 1.1 christos {
402 1.1 christos if (inst)
403 1.1 christos return 0;
404 1.1 christos else if (!supv || dag1)
405 1.1 christos return 1;
406 1.1 christos else
407 1.1 christos return -1;
408 1.1 christos }
409 1.1 christos else if (inst)
410 1.1 christos {
411 1.1 christos /* Some regions are not executable. */
412 1.1 christos /* XXX: Should this be in the model data ? Core B 561 ? */
413 1.1 christos if (l1)
414 1.1 christos return (amask == 0xFFA00000) ? -1 : 1;
415 1.1 christos }
416 1.1 christos else
417 1.1 christos {
418 1.1 christos /* Some regions are not readable. */
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 : 4;
422 1.1 christos }
423 1.1 christos
424 1.1 christos return -2;
425 1.1 christos }
426 1.1 christos
427 1.1 christos /* Exception order per the PRM (first has highest):
428 1.1 christos Inst Multiple CPLB Hits
429 1.1 christos Inst Misaligned Access
430 1.1 christos Inst Protection Violation
431 1.1 christos Inst CPLB Miss
432 1.1 christos Only the alignment matters in non-OS mode though. */
433 1.1 christos static int
434 1.1 christos _mmu_check_addr (SIM_CPU *cpu, bu32 addr, bool write, bool inst, int size)
435 1.1 christos {
436 1.1 christos SIM_DESC sd = CPU_STATE (cpu);
437 1.1 christos struct bfin_mmu *mmu;
438 1.1 christos bu32 *fault_status, *fault_addr, *mem_control, *cplb_addr, *cplb_data;
439 1.1 christos bu32 faults;
440 1.1 christos bool supv, do_excp, dag1;
441 1.1 christos int i, hits;
442 1.1 christos
443 1.1 christos supv = cec_is_supervisor_mode (cpu);
444 1.1 christos dag1 = (BFIN_CPU_STATE.multi_pc == PCREG + 6);
445 1.1 christos
446 1.1 christos if (STATE_ENVIRONMENT (sd) != OPERATING_ENVIRONMENT || bfin_mmu_skip_cplbs)
447 1.1 christos {
448 1.1 christos int ret = mmu_check_implicit_addr (cpu, addr, inst, size, supv, dag1);
449 1.1 christos /* Valid hits and misses are OK in non-OS envs. */
450 1.1 christos if (ret < 0)
451 1.1 christos return 0;
452 1.1 christos _mmu_process_fault (cpu, NULL, addr, write, inst, (ret == 3), false, supv, dag1);
453 1.1 christos }
454 1.1 christos
455 1.1 christos mmu = MMU_STATE (cpu);
456 1.1 christos fault_status = inst ? &mmu->icplb_fault_status : &mmu->dcplb_fault_status;
457 1.1 christos fault_addr = inst ? &mmu->icplb_fault_addr : &mmu->dcplb_fault_addr;
458 1.1 christos mem_control = inst ? &mmu->imem_control : &mmu->dmem_control;
459 1.1 christos cplb_addr = inst ? &mmu->icplb_addr[0] : &mmu->dcplb_addr[0];
460 1.1 christos cplb_data = inst ? &mmu->icplb_data[0] : &mmu->dcplb_data[0];
461 1.1 christos
462 1.1 christos faults = 0;
463 1.1 christos hits = 0;
464 1.1 christos do_excp = false;
465 1.1 christos
466 1.1 christos /* CPLBs disabled -> little to do. */
467 1.1 christos if (!(*mem_control & ENCPLB))
468 1.1 christos {
469 1.1 christos hits = 1;
470 1.1 christos goto implicit_check;
471 1.1 christos }
472 1.1 christos
473 1.1 christos /* Check all the CPLBs first. */
474 1.1 christos for (i = 0; i < 16; ++i)
475 1.1 christos {
476 1.1 christos const bu32 pages[4] = { 0x400, 0x1000, 0x100000, 0x400000 };
477 1.1 christos bu32 addr_lo, addr_hi;
478 1.1 christos
479 1.1 christos /* Skip invalid entries. */
480 1.1 christos if (!(cplb_data[i] & CPLB_VALID))
481 1.1 christos continue;
482 1.1 christos
483 1.1 christos /* See if this entry covers this address. */
484 1.1 christos addr_lo = cplb_addr[i];
485 1.1 christos addr_hi = cplb_addr[i] + pages[(cplb_data[i] & PAGE_SIZE) >> 16];
486 1.1 christos if (addr < addr_lo || addr >= addr_hi)
487 1.1 christos continue;
488 1.1 christos
489 1.1 christos ++hits;
490 1.1 christos faults |= (1 << i);
491 1.1 christos if (write)
492 1.1 christos {
493 1.1 christos if (!supv && !(cplb_data[i] & CPLB_USER_WR))
494 1.1 christos do_excp = true;
495 1.1 christos if (supv && !(cplb_data[i] & CPLB_SUPV_WR))
496 1.1 christos do_excp = true;
497 1.1 christos if ((cplb_data[i] & (CPLB_WT | CPLB_L1_CHBL | CPLB_DIRTY)) == CPLB_L1_CHBL)
498 1.1 christos do_excp = true;
499 1.1 christos }
500 1.1 christos else
501 1.1 christos {
502 1.1 christos if (!supv && !(cplb_data[i] & CPLB_USER_RD))
503 1.1 christos do_excp = true;
504 1.1 christos }
505 1.1 christos }
506 1.1 christos
507 1.1 christos /* Handle default/implicit CPLBs. */
508 1.1 christos if (!do_excp && hits < 2)
509 1.1 christos {
510 1.1 christos int ihits;
511 1.1 christos implicit_check:
512 1.1 christos ihits = mmu_check_implicit_addr (cpu, addr, inst, size, supv, dag1);
513 1.1 christos switch (ihits)
514 1.1 christos {
515 1.1 christos /* No faults and one match -> good to go. */
516 1.1 christos case -1: return 0;
517 1.1 christos case -2:
518 1.1 christos if (hits == 1)
519 1.1 christos return 0;
520 1.1 christos break;
521 1.1 christos case 4:
522 1.1 christos cec_hwerr (cpu, HWERR_EXTERN_ADDR);
523 1.1 christos return 0;
524 1.1 christos default:
525 1.1 christos hits = ihits;
526 1.1 christos }
527 1.1 christos }
528 1.1 christos else
529 1.1 christos /* Normalize hit count so hits==2 is always multiple hit exception. */
530 1.1 christos hits = MIN (2, hits);
531 1.1 christos
532 1.1 christos _mmu_log_fault (cpu, mmu, addr, write, inst, hits == 0, supv, dag1, faults);
533 1.1 christos
534 1.1 christos if (inst)
535 1.1 christos {
536 1.1 christos int iexcps[] = { VEC_CPLB_I_M, VEC_CPLB_I_VL, VEC_CPLB_I_MHIT, VEC_MISALI_I };
537 1.1 christos return iexcps[hits];
538 1.1 christos }
539 1.1 christos else
540 1.1 christos {
541 1.1 christos int dexcps[] = { VEC_CPLB_M, VEC_CPLB_VL, VEC_CPLB_MHIT, VEC_MISALI_D };
542 1.1 christos return dexcps[hits];
543 1.1 christos }
544 1.1 christos }
545 1.1 christos
546 1.1 christos void
547 1.1 christos mmu_check_addr (SIM_CPU *cpu, bu32 addr, bool write, bool inst, int size)
548 1.1 christos {
549 1.1 christos int excp = _mmu_check_addr (cpu, addr, write, inst, size);
550 1.1 christos if (excp)
551 1.1 christos cec_exception (cpu, excp);
552 1.1 christos }
553 1.1 christos
554 1.1 christos void
555 1.1 christos mmu_check_cache_addr (SIM_CPU *cpu, bu32 addr, bool write, bool inst)
556 1.1 christos {
557 1.1 christos bu32 cacheaddr;
558 1.1 christos int excp;
559 1.1 christos
560 1.1 christos cacheaddr = addr & ~(BFIN_L1_CACHE_BYTES - 1);
561 1.1 christos excp = _mmu_check_addr (cpu, cacheaddr, write, inst, BFIN_L1_CACHE_BYTES);
562 1.1 christos if (excp == 0)
563 1.1 christos return;
564 1.1 christos
565 1.1 christos /* Most exceptions are ignored with cache funcs. */
566 1.1 christos /* XXX: Not sure if we should be ignoring CPLB misses. */
567 1.1 christos if (inst)
568 1.1 christos {
569 1.1 christos if (excp == VEC_CPLB_I_VL)
570 1.1 christos return;
571 1.1 christos }
572 1.1 christos else
573 1.1 christos {
574 1.1 christos if (excp == VEC_CPLB_VL)
575 1.1 christos return;
576 1.1 christos }
577 cec_exception (cpu, excp);
578 }
579