1 /* $Id: sun44c-memerr.c,v 1.3 2010/06/05 19:29:11 fredette Exp $ */
2
3 /* machine/sun4/sun44c-memerr.c - implementation of Sun 4/4c memory error emulation: */
4
5 /*
6 * Copyright (c) 2006 Matt Fredette
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 3. All advertising materials mentioning features or use of this software
18 * must display the following acknowledgement:
19 * This product includes software developed by Matt Fredette.
20 * 4. The name of the author may not be used to endorse or promote products
21 * derived from this software without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
24 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
25 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
26 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
27 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
28 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
29 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
31 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
32 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33 * POSSIBILITY OF SUCH DAMAGE.
34 */
35
36 #include <tme/common.h>
37 _TME_RCSID("$Id: sun44c-memerr.c,v 1.3 2010/06/05 19:29:11 fredette Exp $");
38
39 /* includes: */
40 #include "sun4-impl.h"
41
42 /* this calls out a memory error interrupt change: */
43 static void
_tme_sun44c_memerr_callout(struct tme_sun4 * sun4)44 _tme_sun44c_memerr_callout(struct tme_sun4 *sun4)
45 {
46 unsigned int int_asserted;
47 struct tme_bus_connection *conn_bus;
48 int rc;
49
50 /* on a sun4c, asynchronous memory errors can't really be tested, so
51 we never need to assert the memory error interrupt: */
52 if (TME_SUN4_IS_SUN4C(sun4)) {
53 return;
54 }
55
56 /* see if the memory error interrupt should be asserted: */
57 int_asserted
58 = ((sun4->tme_sun44c_memerr_csr[0]
59 & (TME_SUN4_MEMERR_X_INT_ACTIVE
60 | TME_SUN4_MEMERR_X_ENABLE_INT))
61 == (TME_SUN4_MEMERR_X_INT_ACTIVE
62 | TME_SUN4_MEMERR_X_ENABLE_INT));
63
64 /* if we need to call out an interrupt change: */
65 if (!int_asserted != !sun4->tme_sun4_memerr_int_asserted) {
66
67 /* get our bus connection: */
68 conn_bus = sun4->tme_sun4_buses[TME_SUN4_32_CONN_REG_MEMERR];
69
70 /* call out the bus interrupt signal edge: */
71 rc = (*conn_bus->tme_bus_signal)
72 (conn_bus,
73 TME_BUS_SIGNAL_INT(TME_SPARC_IPL_NMI)
74 | (int_asserted
75 ? TME_BUS_SIGNAL_LEVEL_ASSERTED
76 : TME_BUS_SIGNAL_LEVEL_NEGATED));
77
78 /* if this callout was successful, note the new state of the
79 interrupt signal: */
80 if (rc == TME_OK) {
81 sun4->tme_sun4_memerr_int_asserted = int_asserted;
82 }
83
84 /* otherwise, abort: */
85 else {
86 abort();
87 }
88 }
89 }
90
91 /* this updates bad memory: */
92 void
_tme_sun44c_memerr_update(struct tme_sun4 * sun4,tme_uint32_t pte,const tme_shared tme_uint8_t * memory,unsigned int cycle_size)93 _tme_sun44c_memerr_update(struct tme_sun4 *sun4,
94 tme_uint32_t pte,
95 const tme_shared tme_uint8_t *memory,
96 unsigned int cycle_size)
97 {
98 unsigned int address_i;
99 unsigned int address_i_free;
100 int which_csr;
101 int memory_bad;
102 int visible_before;
103
104 /* on the SS2, code name "Calvin", the second 64MB of memory is on
105 an expansion board, which uses the second memory error register: */
106 which_csr = (TME_SUN4_IS_MODEL(sun4, TME_SUN_IDPROM_TYPE_CODE_CALVIN)
107 && (pte & TME_SUN4C_PTE_PGFRAME) >= ((64 * 1024 * 1024) / TME_SUN4C_PAGE_SIZE));
108
109 /* if the memory is being written with bad parity: */
110 memory_bad = (sun4->tme_sun44c_memerr_csr[which_csr] & TME_SUN44C_MEMERR_PAR_TEST) != 0;
111
112 /* see if the memory error registers are visible before: */
113 visible_before = TME_SUN44C_MEMERR_VISIBLE(sun4);
114
115 /* this is obviously a poor implementation, but this is not used
116 during normal operation: */
117
118 /* loop over all of the written memory: */
119 for (; cycle_size > 0; memory++, cycle_size--) {
120
121 /* search the bad memory list for this memory, and for a free
122 entry in the list: */
123 address_i = 0;
124 address_i_free = TME_ARRAY_ELS(sun4->tme_sun4_memerr_bad_memory);
125 do {
126 if (sun4->tme_sun4_memerr_bad_memory[address_i] == NULL) {
127 address_i_free = address_i;
128 }
129 else if (sun4->tme_sun4_memerr_bad_memory[address_i] == memory) {
130 address_i_free = address_i;
131 break;
132 }
133 } while (++address_i < TME_ARRAY_ELS(sun4->tme_sun4_memerr_bad_memory));
134
135 /* if the memory is being written with bad parity: */
136 if (memory_bad) {
137
138 /* if the bad memory list is full: */
139 if (address_i_free == TME_ARRAY_ELS(sun4->tme_sun4_memerr_bad_memory)) {
140 abort();
141 }
142
143 /* if this bad memory isn't already on the list: */
144 if (sun4->tme_sun4_memerr_bad_memory[address_i_free] == NULL) {
145
146 /* this memory is now bad: */
147 sun4->tme_sun4_memerr_bad_memory[address_i_free] = memory;
148 sun4->tme_sun4_memerr_bad_memory_count++;
149 }
150 }
151
152 /* otherwise, the memory is being written with good parity: */
153 else {
154
155 /* if this memory was previously written with bad parity: */
156 if (address_i_free < TME_ARRAY_ELS(sun4->tme_sun4_memerr_bad_memory)
157 && sun4->tme_sun4_memerr_bad_memory[address_i] == memory) {
158
159 /* this memory is good again: */
160 sun4->tme_sun4_memerr_bad_memory[address_i] = NULL;
161 sun4->tme_sun4_memerr_bad_memory_count--;
162 }
163 }
164 }
165
166 /* if the memory error registers were visible before, and are no
167 longer visible: */
168 if (visible_before
169 && !TME_SUN44C_MEMERR_VISIBLE(sun4)) {
170
171 /* unless the cache is visible, update the TLB fill function: */
172 if (!sun4->tme_sun4_cache_visible) {
173 sun4->tme_sun4_tlb_fill = _tme_sun44c_tlb_fill_mmu;
174 }
175 }
176 }
177
178 /* this checks for bad memory during a read: */
179 int
_tme_sun44c_memerr_check(const struct tme_bus_connection * conn_bus_init,tme_uint32_t address,tme_uint32_t pte,const tme_shared tme_uint8_t * memory,unsigned int cycle_size)180 _tme_sun44c_memerr_check(const struct tme_bus_connection *conn_bus_init,
181 tme_uint32_t address,
182 tme_uint32_t pte,
183 const tme_shared tme_uint8_t *memory,
184 unsigned int cycle_size)
185 {
186 struct tme_sun4 *sun4;
187 unsigned int address_i;
188 tme_uint32_t csr;
189 int write_csr;
190
191 /* recover our sun4: */
192 sun4 = (struct tme_sun4 *) conn_bus_init->tme_bus_connection.tme_connection_element->tme_element_private;
193
194 /* this is obviously a poor implementation, but this is not used
195 during normal operation: */
196
197 /* loop over all of the bad memory: */
198 csr = 0;
199 for (; cycle_size > 0; address++, memory++, cycle_size--) {
200
201 /* see if this byte is bad: */
202 address_i = 0;
203 do {
204 if (sun4->tme_sun4_memerr_bad_memory[address_i] == memory) {
205 csr
206 |= (TME_SUN4_IS_SUN4C(sun4)
207 ? TME_SUN44C_MEMERR_PAR_ERR_BL0 << (address % sizeof(tme_uint32_t))
208 : TME_SUN44C_MEMERR_PAR_ERR_BL3 >> (address % sizeof(tme_uint32_t)));
209 break;
210 }
211 } while (++address_i < TME_ARRAY_ELS(sun4->tme_sun4_memerr_bad_memory));
212 }
213
214 /* if no bad memory was accessed, return now: */
215 if (csr == 0) {
216 return (FALSE);
217 }
218
219 /* if this is a sun4c: */
220 if (TME_SUN4_IS_SUN4C(sun4)) {
221
222 /* on the SS2, code name "Calvin", the second 64MB of memory is on
223 an expansion board, which uses the second memory error
224 register: */
225 write_csr = (TME_SUN4_IS_MODEL(sun4, TME_SUN_IDPROM_TYPE_CODE_CALVIN)
226 && (pte & TME_SUN4C_PTE_PGFRAME) >= ((64 * 1024 * 1024) / TME_SUN4C_PAGE_SIZE));
227
228 sun4->tme_sun44c_memerr_csr[write_csr]
229 |= (csr
230 | TME_SUN4C_MEMERR_PAR_ERROR
231 | ((sun4->tme_sun44c_memerr_csr[write_csr] & TME_SUN4C_MEMERR_PAR_ERROR)
232 ? TME_SUN4C_MEMERR_PAR_MULTI
233 : 0));
234 }
235
236 /* otherwise, this is a sun4: */
237 else {
238 abort();
239 }
240
241 /* call out an interrupt: */
242 _tme_sun44c_memerr_callout(sun4);
243
244 /* a memory error has happened: */
245 return (TRUE);
246 }
247
248 /* the bus cycle handler for the memory error register: */
249 int
_tme_sun44c_memerr_cycle_control(void * _sun4,struct tme_bus_cycle * cycle_init)250 _tme_sun44c_memerr_cycle_control(void *_sun4, struct tme_bus_cycle *cycle_init)
251 {
252 struct tme_sun4 *sun4;
253 tme_uint32_t memerr_reg[2][TME_SUN44C_MEMERR_SIZ_REG / sizeof(tme_uint32_t)];
254 int write_csr, unlatch;
255 int write_parctl;
256 tme_uint32_t csr_old;
257 tme_uint32_t csr_new;
258 tme_uint32_t csr_ro;
259 int visible_before;
260
261 /* recover our sun4: */
262 sun4 = (struct tme_sun4 *) _sun4;
263
264 /* start filling the memory error register(s): */
265 memerr_reg[0][TME_SUN44C_MEMERR_REG_CSR / sizeof(tme_uint32_t)]
266 = tme_htobe_u32(sun4->tme_sun44c_memerr_csr[0]);
267
268 /* assume this cycle won't write or unlatch a memory error register: */
269 write_csr = -1;
270 unlatch = -1;
271 write_parctl = -1;
272
273 /* we only tolerate aligned 32-bit accesses: */
274 if (!((cycle_init->tme_bus_cycle_address % sizeof(tme_uint32_t)) == 0
275 && cycle_init->tme_bus_cycle_size == sizeof(tme_uint32_t))) {
276 abort();
277 }
278
279 /* if this is a sun4c: */
280 if (TME_SUN4_IS_SUN4C(sun4)) {
281
282 /* finish filling the memory error registers: */
283 memerr_reg[0][TME_SUN4C_MEMERR_REG_PARCTL / sizeof(tme_uint32_t)]
284 = tme_htobe_u32(sun4->tme_sun4c_memerr_parctl[0]);
285 memerr_reg[1][TME_SUN44C_MEMERR_REG_CSR / sizeof(tme_uint32_t)]
286 = tme_htobe_u32(sun4->tme_sun44c_memerr_csr[1]);
287 memerr_reg[1][TME_SUN4C_MEMERR_REG_PARCTL / sizeof(tme_uint32_t)]
288 = tme_htobe_u32(sun4->tme_sun4c_memerr_parctl[1]);
289
290 assert ((cycle_init->tme_bus_cycle_address
291 + cycle_init->tme_bus_cycle_size
292 - 1) < sizeof(memerr_reg));
293
294 /* if this access is to a parity control register: */
295 if (cycle_init->tme_bus_cycle_address & TME_SUN4C_MEMERR_REG_PARCTL) {
296
297 /* if this is a write: */
298 if (cycle_init->tme_bus_cycle_type == TME_BUS_CYCLE_WRITE) {
299 write_parctl = (cycle_init->tme_bus_cycle_address / TME_SUN44C_MEMERR_SIZ_REG);
300 }
301 }
302
303 /* otherwise, this access is to a CSR: */
304 else {
305
306 /* "The information bits in the memory error register are
307 cleared by reading it." */
308
309 /* any write writes either of the CSRs, and any read reads one
310 of the CSRs: */
311 if (cycle_init->tme_bus_cycle_type == TME_BUS_CYCLE_READ) {
312 unlatch = (cycle_init->tme_bus_cycle_address / TME_SUN44C_MEMERR_SIZ_REG);
313 }
314 else {
315 write_csr = (cycle_init->tme_bus_cycle_address / TME_SUN44C_MEMERR_SIZ_REG);
316 }
317 }
318 }
319
320 /* otherwise, this is a sun4: */
321 else {
322
323 /* finish filling the memory error register: */
324 memerr_reg[0][TME_SUN4_MEMERR_REG_VADDR / sizeof(tme_uint32_t)]
325 = tme_htobe_u32(sun4->tme_sun4_memerr_vaddr);
326
327 assert ((cycle_init->tme_bus_cycle_address
328 + cycle_init->tme_bus_cycle_size
329 - 1) < TME_SUN44C_MEMERR_SIZ_REG);
330
331 /* if this is a write: */
332 if (cycle_init->tme_bus_cycle_type == TME_BUS_CYCLE_WRITE) {
333
334 /* "The interrupt is held pending and the error information in
335 the memory error register is latched (frozen) until it is
336 cleared (unfrozen) by a write to bits <31..24> of the memory
337 error address register." */
338
339 /* see if this writes the vaddr or the CSR: */
340 if (cycle_init->tme_bus_cycle_address & TME_SUN4_MEMERR_REG_VADDR) {
341 unlatch = 0;
342 }
343 else {
344 write_csr = 0;
345 }
346 }
347 }
348
349 /* do the transfer: */
350 tme_bus_cycle_xfer_memory(cycle_init,
351 (tme_uint8_t *) memerr_reg,
352 sizeof(memerr_reg) - 1);
353
354 /* if this is a write: */
355 if (cycle_init->tme_bus_cycle_type == TME_BUS_CYCLE_WRITE) {
356
357 /* if the sun4c parity control register has been written: */
358 if (write_parctl >= 0) {
359 sun4->tme_sun4c_memerr_parctl[write_parctl]
360 = tme_betoh_u32(memerr_reg[write_parctl][TME_SUN4C_MEMERR_REG_PARCTL / sizeof(tme_uint32_t)]);
361 }
362
363 /* if the sun4 vaddr register has been written: */
364 if (TME_SUN4_IS_SUN4(sun4)
365 && unlatch) {
366 sun4->tme_sun4c_memerr_parctl[0]
367 = tme_betoh_u32(memerr_reg[0][TME_SUN4_MEMERR_REG_VADDR / sizeof(tme_uint32_t)]);
368 }
369 }
370
371 /* assume that there are no CSR changes: */
372 csr_old = csr_new = 0;
373 csr_ro = ~(TME_SUN44C_MEMERR_PAR_TEST
374 | TME_SUN44C_MEMERR_PAR_ENABLE
375 | (TME_SUN4_IS_SUN4C(sun4)
376 ? 0
377 : TME_SUN4_MEMERR_X_ENABLE_INT));
378
379 /* if a CSR register has been written: */
380 if (write_csr >= 0) {
381
382 /* get the new and old CSR values, preserving the read-only bits: */
383 csr_old = sun4->tme_sun44c_memerr_csr[write_csr];
384 csr_new = tme_betoh_u32(memerr_reg[write_csr][TME_SUN44C_MEMERR_REG_CSR / sizeof(tme_uint32_t)]);
385 csr_new = ((csr_old & csr_ro)
386 | (csr_new & ~csr_ro));
387 }
388
389 /* otherwise, if a CSR register has been unlatched: */
390 else if (unlatch >= 0) {
391
392 /* get the old and new CSR values, clearing the read-only bits: */
393 csr_old = sun4->tme_sun44c_memerr_csr[unlatch];
394 csr_new &= ~csr_ro;
395 write_csr = unlatch;
396 }
397
398 /* if a CSR register has changed: */
399 if (csr_new != csr_old) {
400
401 /* see if the memory error registers were visible before: */
402 visible_before = TME_SUN44C_MEMERR_VISIBLE(sun4);
403
404 /* set the new CSR value and call out an interrupt change: */
405 sun4->tme_sun44c_memerr_csr[write_csr] = csr_new;
406 _tme_sun44c_memerr_callout(sun4);
407
408 /* if the write-inverse-parity testing feature is being enabled or
409 disabled: */
410 if ((csr_new ^ csr_old) & TME_SUN44C_MEMERR_PAR_TEST) {
411
412 /* if this feature is being enabled: */
413 if (csr_new & TME_SUN44C_MEMERR_PAR_TEST) {
414
415 /* if the memory error registers weren't visible before: */
416 if (!visible_before) {
417
418 /* the memory error registers are now visible: */
419 assert (sun4->tme_sun4_memerr_bad_memory_count == 0);
420
421 /* unless the cache is visible, update the TLB fill function: */
422 if (!sun4->tme_sun4_cache_visible) {
423 sun4->tme_sun4_tlb_fill = _tme_sun44c_tlb_fill_memerr;
424 }
425
426 /* invalidate all TLBs: */
427 tme_sun_mmu_tlbs_invalidate(sun4->tme_sun44c_mmu);
428 }
429 }
430
431 /* otherwise, this feature is being disabled: */
432 else {
433
434 assert (visible_before);
435
436 /* if the memory error registers are no longer visible: */
437 if (!TME_SUN44C_MEMERR_VISIBLE(sun4)) {
438
439 /* unless the cache is visible, update the TLB fill function: */
440 if (!sun4->tme_sun4_cache_visible) {
441 sun4->tme_sun4_tlb_fill = _tme_sun44c_tlb_fill_mmu;
442 }
443
444 /* invalidate all TLBs: */
445 tme_sun_mmu_tlbs_invalidate(sun4->tme_sun44c_mmu);
446 }
447 }
448 }
449 }
450
451 return (TME_OK);
452 }
453
454 /* the bus cycle handler for memory error testing: */
455 int
_tme_sun44c_memerr_cycle_bus(void * _conn_bus_init,struct tme_bus_cycle * cycle_init)456 _tme_sun44c_memerr_cycle_bus(void *_conn_bus_init,
457 struct tme_bus_cycle *cycle_init)
458 {
459 const struct tme_bus_connection *conn_bus_init;
460 struct tme_sun4 *sun4;
461 struct tme_bus_tlb *tlb;
462 tme_uint32_t address;
463 unsigned int cycle_size;
464 union {
465 tme_uint8_t memory_buffer_8s[sizeof(tme_uint32_t) / sizeof(tme_uint8_t)];
466 tme_uint16_t memory_buffer_16s[sizeof(tme_uint32_t) / sizeof(tme_uint16_t)];
467 tme_uint32_t memory_buffer_32s[sizeof(tme_uint32_t) / sizeof(tme_uint32_t)];
468 } memory_buffer;
469 struct tme_sun_mmu_pte pte_mmu;
470 const tme_shared tme_uint8_t *memory_data_read;
471 tme_shared tme_uint8_t *memory_data_write;
472 int rc;
473
474 /* recover our initiator's bus connection and sun4: */
475 conn_bus_init = (struct tme_bus_connection *) _conn_bus_init;
476 sun4 = (struct tme_sun4 *) conn_bus_init->tme_bus_connection.tme_connection_element->tme_element_private;
477
478 /* get the address and size for this cycle: */
479 address = cycle_init->tme_bus_cycle_address;
480 cycle_size = cycle_init->tme_bus_cycle_size;
481
482 /* recover the TLB entry for this cycle: */
483 tlb = sun4->tme_sun4_memtest_tlb;
484 assert (tlb != NULL);
485
486 /* busy the TLB entry that triggered this cycle: */
487 tme_bus_tlb_busy(tlb);
488
489 /* if the TLB entry is invalid, return now: */
490 if (tme_bus_tlb_is_invalid(tlb)) {
491
492 /* unbusy the TLB entry and return now: */
493 tme_bus_tlb_unbusy(tlb);
494 return (EBADF);
495 }
496
497 /* get the PTE for this address and context from the MMU: */
498 rc = tme_sun_mmu_pte_get(sun4->tme_sun44c_mmu,
499 TME_SUN44C_BUS_MMU_CONTEXT(sun4, conn_bus_init),
500 address,
501 &pte_mmu);
502 assert (rc == TME_OK);
503
504 /* if this cycle is a read: */
505 if (cycle_init->tme_bus_cycle_type == TME_BUS_CYCLE_READ) {
506
507 /* get a pointer to the data to read: */
508 memory_data_read
509 = (sun4->tme_sun4_memerr_tlb_emulator_off_read
510 + address);
511
512 /* dispatch on the cycle size, to transfer the data from memory
513 into the memory buffer: */
514 switch (cycle_size) {
515 default:
516 assert (FALSE);
517 /* FALLTHROUGH */
518 case sizeof(tme_uint8_t):
519 memory_buffer.memory_buffer_8s[0]
520 = tme_memory_bus_read8(memory_data_read,
521 tlb->tme_bus_tlb_rwlock,
522 sizeof(tme_uint8_t),
523 sizeof(tme_uint32_t));
524 break;
525 case sizeof(tme_uint16_t):
526 memory_buffer.memory_buffer_16s[0]
527 = tme_memory_bus_read16((const tme_shared tme_uint16_t *) memory_data_read,
528 tlb->tme_bus_tlb_rwlock,
529 sizeof(tme_uint16_t),
530 sizeof(tme_uint32_t));
531 break;
532 case sizeof(tme_uint32_t):
533 memory_buffer.memory_buffer_32s[0]
534 = tme_memory_bus_read32((const tme_shared tme_uint32_t *) memory_data_read,
535 tlb->tme_bus_tlb_rwlock,
536 sizeof(tme_uint32_t),
537 sizeof(tme_uint32_t));
538 break;
539 }
540
541 /* run the bus cycle against the memory buffer: */
542 tme_bus_cycle_xfer_memory(cycle_init,
543 &memory_buffer.memory_buffer_8s[0] - address,
544 address + cycle_size - 1);
545 assert (cycle_init->tme_bus_cycle_size == cycle_size);
546
547 /* check for memory errors on this cache line fill: */
548 rc = (_tme_sun44c_memerr_check(conn_bus_init,
549 address,
550 pte_mmu.tme_sun_mmu_pte_raw,
551 memory_data_read,
552 cycle_size)
553 ? EIO
554 : TME_OK);
555 }
556
557 /* otherwise, this cycle must be a write: */
558 else {
559 assert (cycle_init->tme_bus_cycle_type == TME_BUS_CYCLE_WRITE);
560
561 /* run the bus cycle against the memory buffer: */
562 tme_bus_cycle_xfer_memory(cycle_init,
563 &memory_buffer.memory_buffer_8s[0] - address,
564 address + cycle_size - 1);
565 assert (cycle_init->tme_bus_cycle_size == cycle_size);
566
567 /* get a pointer to the data to write: */
568 memory_data_write
569 = (sun4->tme_sun4_memerr_tlb_emulator_off_write
570 + address);
571
572 /* dispatch on the cycle size, to transfer the data from the
573 memory buffer to memory: */
574 switch (cycle_size) {
575 default:
576 assert (FALSE);
577 /* FALLTHROUGH */
578 case sizeof(tme_uint8_t):
579 tme_memory_bus_write8(memory_data_write,
580 memory_buffer.memory_buffer_8s[0],
581 tlb->tme_bus_tlb_rwlock,
582 sizeof(tme_uint8_t),
583 sizeof(tme_uint32_t));
584 break;
585 case sizeof(tme_uint16_t):
586 tme_memory_bus_write16((tme_shared tme_uint16_t *) memory_data_write,
587 memory_buffer.memory_buffer_16s[0],
588 tlb->tme_bus_tlb_rwlock,
589 sizeof(tme_uint16_t),
590 sizeof(tme_uint32_t));
591 break;
592 case sizeof(tme_uint32_t):
593 tme_memory_bus_write32((tme_shared tme_uint32_t *) memory_data_write,
594 memory_buffer.memory_buffer_32s[0],
595 tlb->tme_bus_tlb_rwlock,
596 sizeof(tme_uint32_t),
597 sizeof(tme_uint32_t));
598 break;
599 }
600
601 /* add any memory errors caused by this write: */
602 _tme_sun44c_memerr_update(sun4,
603 pte_mmu.tme_sun_mmu_pte_raw,
604 memory_data_write,
605 cycle_size);
606 rc = TME_OK;
607 }
608
609 /* unbusy the TLB entry and invalidate it. this hurts performance,
610 since it keeps TLB entries valid only for one cycle at a time,
611 but it's simple and handles the problem of memory error testing
612 in the presence of virtual address aliases (addresses that have
613 never been written to can suddenly have read errors): */
614 tme_bus_tlb_unbusy(tlb);
615 tme_token_invalidate(tlb->tme_bus_tlb_token);
616 sun4->tme_sun4_memtest_tlb = NULL;
617
618 return (rc);
619 }
620
621 /* this fills TLBs when memory error testing is visible: */
622 int
_tme_sun44c_tlb_fill_memerr(const struct tme_bus_connection * conn_bus_init,struct tme_bus_tlb * tlb,tme_uint32_t * _asi_mask,tme_uint32_t address,unsigned int cycle_type)623 _tme_sun44c_tlb_fill_memerr(const struct tme_bus_connection *conn_bus_init,
624 struct tme_bus_tlb *tlb,
625 tme_uint32_t *_asi_mask,
626 tme_uint32_t address,
627 unsigned int cycle_type)
628 {
629 struct tme_sun4 *sun4;
630 tme_uint32_t asi_mask;
631 struct tme_sun_mmu_pte pte_mmu;
632 int rc;
633
634 /* recover our sun4: */
635 sun4 = (struct tme_sun4 *) conn_bus_init->tme_bus_connection.tme_connection_element->tme_element_private;
636
637 /* recover the ASI mask: */
638 asi_mask = *_asi_mask;
639
640 /* this ASI mask must be a single ASI, for user or supervisor
641 instruction or data: */
642 assert (asi_mask == TME_SPARC32_ASI_MASK_UD
643 || asi_mask == TME_SPARC32_ASI_MASK_UI
644 || asi_mask == TME_SPARC32_ASI_MASK_SD
645 || asi_mask == TME_SPARC32_ASI_MASK_SI);
646
647 /* invalidate any other memory test TLB entry: */
648 if (sun4->tme_sun4_memtest_tlb != NULL
649 && sun4->tme_sun4_memtest_tlb != tlb) {
650 tme_token_invalidate(sun4->tme_sun4_memtest_tlb->tme_bus_tlb_token);
651 }
652 sun4->tme_sun4_memtest_tlb = NULL;
653 #ifndef NDEBUG
654 sun4->tme_sun4_memerr_tlb_emulator_off_read = TME_EMULATOR_OFF_UNDEF;
655 sun4->tme_sun4_memerr_tlb_emulator_off_write = TME_EMULATOR_OFF_UNDEF;
656 #endif /* !NDEBUG */
657
658 /* memory error testing must be active: */
659 assert (TME_SUN44C_MEMERR_VISIBLE(sun4));
660
661 /* fill this TLB entry from the MMU: */
662 rc = _tme_sun44c_tlb_fill_mmu(conn_bus_init,
663 tlb,
664 _asi_mask,
665 address,
666 cycle_type);
667 assert (rc == TME_OK);
668
669 /* get the PTE for this address and context from the MMU: */
670 rc = tme_sun_mmu_pte_get(sun4->tme_sun44c_mmu,
671 TME_SUN44C_BUS_MMU_CONTEXT(sun4, conn_bus_init),
672 address,
673 &pte_mmu);
674 assert (rc == TME_OK);
675
676 /* if this PTE is valid and for onboard memory: */
677 if ((pte_mmu.tme_sun_mmu_pte_raw
678 & (TME_SUN44C_PTE_VALID
679 | TME_SUN44C_PTE_PGTYPE))
680 == (TME_SUN44C_PTE_VALID
681 | 0 /* TME_SUN44C_PGTYPE_OBMEM */)) {
682
683 /* this TLB entry should have no shift: */
684 if (tlb->tme_bus_tlb_addr_shift != 0) {
685 abort();
686 }
687
688 /* if this is a read: */
689 if (cycle_type == TME_BUS_CYCLE_READ) {
690
691 /* invalidate this TLB entry for writes: */
692 tlb->tme_bus_tlb_emulator_off_write = TME_EMULATOR_OFF_UNDEF;
693
694 /* if this TLB entry allows fast reading, and there are bad
695 memory addresses: */
696 if (tlb->tme_bus_tlb_emulator_off_read != TME_EMULATOR_OFF_UNDEF
697 && sun4->tme_sun4_memerr_bad_memory_count > 0) {
698
699 /* we will handle this read: */
700 sun4->tme_sun4_memerr_tlb_emulator_off_read = tlb->tme_bus_tlb_emulator_off_read;
701 tlb->tme_bus_tlb_emulator_off_read = TME_EMULATOR_OFF_UNDEF;
702 tlb->tme_bus_tlb_cycle_private = (void *) conn_bus_init;
703 tlb->tme_bus_tlb_cycle = _tme_sun44c_memerr_cycle_bus;
704 }
705 }
706
707 /* otherwise, this must be a write: */
708 else {
709 if (cycle_type != TME_BUS_CYCLE_WRITE) {
710 abort();
711 }
712
713 /* invalidate this TLB entry for reads: */
714 tlb->tme_bus_tlb_emulator_off_read = TME_EMULATOR_OFF_UNDEF;
715
716 /* if this TLB entry allows fast writing: */
717 if (tlb->tme_bus_tlb_emulator_off_write != TME_EMULATOR_OFF_UNDEF) {
718
719 /* we will handle this write: */
720 sun4->tme_sun4_memerr_tlb_emulator_off_write = tlb->tme_bus_tlb_emulator_off_write;
721 tlb->tme_bus_tlb_emulator_off_write = TME_EMULATOR_OFF_UNDEF;
722 tlb->tme_bus_tlb_cycle_private = (void *) conn_bus_init;
723 tlb->tme_bus_tlb_cycle = _tme_sun44c_memerr_cycle_bus;
724 }
725 }
726
727 /* finish invalidating this TLB entry for the other cycle type: */
728 assert (tlb->tme_bus_tlb_cycles_ok & cycle_type);
729 tlb->tme_bus_tlb_cycles_ok = cycle_type;
730 tlb->tme_bus_tlb_addr_offset = 0;
731 }
732
733 /* remember this TLB entry: */
734 sun4->tme_sun4_memtest_tlb = tlb;
735
736 /* return success: */
737 return (TME_OK);
738 }
739