|
|
1.1 root 1: /* $Id: sun44c-cache.c,v 1.2 2007/03/29 01:34:44 fredette Exp $ */
2:
3: /* machine/sun4/sun44c-cache.c - implementation of Sun 4/4c cache 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-cache.c,v 1.2 2007/03/29 01:34:44 fredette Exp $");
38:
39: /* includes: */
40: #include "sun4-impl.h"
41:
42: /* macros: */
43:
44: /* real sun4/4c cache tag bits: */
45: #define TME_SUN44C_CACHETAG_CONTEXT (0x03c00000)
46: #define TME_SUN44C_CACHETAG_WRITE (0x00200000)
47: #define TME_SUN44C_CACHETAG_SYSTEM (0x00100000)
48: #define TME_SUN44C_CACHETAG_VALID (0x00080000)
49: #define TME_SUN44C_CACHETAG_TAG (0x0000fffc)
50:
51: /* actions that the cache may take for an access: */
52: #define _TME_SUN44C_CACHE_ACTION_NULL (0)
53: #define _TME_SUN44C_CACHE_ACTION_FLUSH TME_BIT(0)
54: #define _TME_SUN44C_CACHE_ACTION_INVALIDATE TME_BIT(1)
55: #define _TME_SUN44C_CACHE_ACTION_ALLOCATE (TME_BIT(2) | _TME_SUN44C_CACHE_ACTION_FLUSH | _TME_SUN44C_CACHE_ACTION_INVALIDATE)
56: #define _TME_SUN44C_CACHE_ACTION_READ TME_BIT(3)
57: #define _TME_SUN44C_CACHE_ACTION_WRITE_BACK TME_BIT(4)
58: #define _TME_SUN44C_CACHE_ACTION_WRITE_THROUGH TME_BIT(5)
59: #define _TME_SUN44C_CACHE_ACTION_ERROR_WRITE TME_BIT(6)
60: #define _TME_SUN44C_CACHE_ACTION_ERROR_SYSTEM TME_BIT(7)
61: #define _TME_SUN44C_CACHE_ACTION_ERROR_MEMERR TME_BIT(8)
62:
63: /* each time the cache is enabled or its tag or data memory is
64: accessed directly, the cache remains visible for this many more bus
65: cycles. eventually, the cache becomes invisible again, to allow
66: for maximum performance - but if this value is too low, diagnostics
67: may fail.
68:
69: some diagnostics do one bus cycle per cache address, so this must
70: be at least the size of the largest system's cache, plus some
71: slack: */
72: #define _TME_SUN44C_CACHE_VISIBLE_BUS_CYCLES (65536 + 1024)
73:
74: /* this returns the next TLB fill function: */
75: #define _tme_sun44c_cache_tlb_fill_next(sun4) \
76: (TME_SUN44C_MEMERR_VISIBLE(sun4) \
77: ? _tme_sun44c_tlb_fill_memerr \
78: : _tme_sun44c_tlb_fill_mmu)
79:
80: /* this returns a valid cache tag for an address and context: */
81: static tme_uint32_t
82: _tme_sun44c_cache_tag(struct tme_sun4 *sun4,
83: tme_uint32_t context,
84: tme_uint32_t address)
85: {
86: tme_uint32_t cache_tag;
87:
88: /* start with the address tag: */
89: cache_tag
90: = (((address >> sun4->tme_sun4_cache_size_log2)
91: * _TME_FIELD_MASK_FACTOR(TME_SUN44C_CACHETAG_TAG))
92: & TME_SUN44C_CACHETAG_TAG);
93:
94: /* add the context: */
95: TME_FIELD_MASK_DEPOSITU(cache_tag, TME_SUN44C_CACHETAG_CONTEXT, context);
96:
97: /* add the valid bit: */
98: cache_tag |= TME_SUN44C_CACHETAG_VALID;
99:
100: return (cache_tag);
101: }
102:
103: /* this returns the mask of actions that the cache will take for an
104: access: */
105: static unsigned int
106: _tme_sun44c_cache_actions(const struct tme_bus_connection *conn_bus_init,
107: tme_uint32_t asi_mask,
108: tme_uint32_t address,
109: unsigned int cycles)
110: {
111: struct tme_sun4 *sun4;
112: tme_uint32_t context;
113: tme_uint32_t cache_line_index;
114: tme_uint32_t cache_tag_current;
115: tme_uint32_t cache_tag_mask;
116: struct tme_sun_mmu_pte pte_mmu;
117: tme_uint32_t pte;
118: unsigned int cache_actions;
119: int rc;
120:
121: /* recover our sun4: */
122: sun4 = (struct tme_sun4 *) conn_bus_init->tme_bus_connection.tme_connection_element->tme_element_private;
123:
124: /* get the context: */
125: context = TME_SUN44C_BUS_MMU_CONTEXT(sun4, conn_bus_init);
126:
127: /* this ASI mask must be a single ASI, for user or supervisor
128: instruction or data: */
129: assert (asi_mask == TME_SPARC32_ASI_MASK_UD
130: || asi_mask == TME_SPARC32_ASI_MASK_UI
131: || asi_mask == TME_SPARC32_ASI_MASK_SD
132: || asi_mask == TME_SPARC32_ASI_MASK_SI);
133:
134: /* this must be a plain read or write: */
135: assert (cycles == TME_BUS_CYCLE_READ
136: || cycles == TME_BUS_CYCLE_WRITE);
137:
138: /* turn this address into the cache line index: */
139: cache_line_index
140: = (address >> sun4->tme_sun4_cache_size_line_log2
141: & ((1 << (sun4->tme_sun4_cache_size_log2
142: - sun4->tme_sun4_cache_size_line_log2))
143: - 1));
144:
145: /* get the current cache tag for this cache line: */
146: cache_tag_current = sun4->tme_sun44c_cache_tags[cache_line_index];
147:
148: /* NB: apparently the context field of a valid cache tag for system
149: memory isn't significant when doing a tag comparison: */
150: cache_tag_mask
151: = (TME_SUN44C_CACHETAG_VALID
152: | TME_SUN44C_CACHETAG_CONTEXT
153: | TME_SUN44C_CACHETAG_TAG);
154: if (cache_tag_current & TME_SUN44C_CACHETAG_SYSTEM) {
155: cache_tag_mask
156: = (TME_SUN44C_CACHETAG_VALID
157: | TME_SUN44C_CACHETAG_TAG);
158: }
159:
160: /* if this address and context hit in the cache: */
161: if (((cache_tag_current
162: ^ _tme_sun44c_cache_tag(sun4, context, address))
163: & cache_tag_mask) == 0) {
164:
165: /* if the current cache tag only allows system access, but this is
166: a user access: */
167: if ((cache_tag_current & TME_SUN44C_CACHETAG_SYSTEM)
168: && TME_SPARC_ASI_MASK_OVERLAP(asi_mask,
169: (TME_SPARC32_ASI_MASK_UD
170: | TME_SPARC32_ASI_MASK_UI))) {
171: return (_TME_SUN44C_CACHE_ACTION_ERROR_SYSTEM);
172: }
173:
174: /* if the current cache tag only allows read access, but this is a
175: write access: */
176: if (!(cache_tag_current & TME_SUN44C_CACHETAG_WRITE)
177: && cycles == TME_BUS_CYCLE_WRITE) {
178: return (_TME_SUN44C_CACHE_ACTION_ERROR_WRITE);
179: }
180:
181: /* this access is a hit in the cache: */
182: cache_actions = 0;
183: }
184:
185: /* otherwise, this address and context miss in the cache: */
186: else {
187:
188: /* if this ASI mask is for supervisor instructions, and we're in
189: the boot state: */
190: if (asi_mask == TME_SPARC32_ASI_MASK_SI
191: && (sun4->tme_sun44c_enable & TME_SUN44C_ENA_NOTBOOT) == 0) {
192: return (_TME_SUN44C_CACHE_ACTION_NULL);
193: }
194:
195: /* get the PTE for this address and context from the MMU: */
196: rc = tme_sun_mmu_pte_get(sun4->tme_sun44c_mmu,
197: context,
198: address,
199: &pte_mmu);
200: assert (rc == TME_OK);
201: pte = pte_mmu.tme_sun_mmu_pte_raw;
202:
203: /* if this PTE is not valid: */
204: if (!(pte & TME_SUN44C_PTE_VALID)) {
205: return (_TME_SUN44C_CACHE_ACTION_NULL);
206: }
207:
208: /* if this PTE is not for cacheable space: */
209: if (pte & TME_SUN44C_PTE_NC) {
210: return (_TME_SUN44C_CACHE_ACTION_NULL);
211: }
212:
213: /* if this PTE only allows system access, but this is a user
214: access: */
215: if ((pte & TME_SUN44C_PTE_SYSTEM)
216: && TME_SPARC_ASI_MASK_OVERLAP(asi_mask,
217: (TME_SPARC32_ASI_MASK_UD
218: | TME_SPARC32_ASI_MASK_UI))) {
219: return (_TME_SUN44C_CACHE_ACTION_ERROR_SYSTEM);
220: }
221:
222: /* if this is a write access: */
223: if (cycles == TME_BUS_CYCLE_WRITE) {
224:
225: /* if this PTE doesn't allow write access: */
226: if (!(pte & TME_SUN44C_PTE_WRITE)) {
227: return (_TME_SUN44C_CACHE_ACTION_ERROR_WRITE);
228: }
229:
230: /* on a write miss, we invalidate, but we don't allocate: */
231: sun4->tme_sun44c_cache_tags[cache_line_index] &= ~TME_SUN44C_CACHETAG_VALID;
232: return (_TME_SUN44C_CACHE_ACTION_NULL);
233: }
234:
235: /* otherwise, this PTE is valid and cacheable, so it will
236: cause an allocation in the cache: */
237: cache_actions = _TME_SUN44C_CACHE_ACTION_ALLOCATE;
238: }
239:
240: /* this access will be handled by the cache: */
241: return (cache_actions
242: | (cycles == TME_BUS_CYCLE_READ
243: ? _TME_SUN44C_CACHE_ACTION_READ
244: : (sun4->tme_sun4_cache_writeback
245: ? _TME_SUN44C_CACHE_ACTION_WRITE_BACK
246: : _TME_SUN44C_CACHE_ACTION_WRITE_THROUGH)));
247: }
248:
249: /* for a particular address, this returns the PTE entry for the
250: address, and fills the cache internal TLB entry to point to the
251: main memory for the address' cache line. NB that this returns with
252: the cache internal TLB entry busy: */
253: static tme_uint32_t
254: _tme_sun4_cache_tlb_internal_fill(const struct tme_bus_connection *conn_bus_init,
255: tme_uint32_t address,
256: unsigned int cycle_type)
257: {
258: struct tme_sun4 *sun4;
259: int rc;
260: struct tme_sun_mmu_pte pte_mmu;
261: tme_uint32_t context;
262: tme_uint32_t asi_mask;
263: tme_uint32_t pte;
264: tme_uint32_t cache_size_line;
265:
266: /* recover our sun4: */
267: sun4 = (struct tme_sun4 *) conn_bus_init->tme_bus_connection.tme_connection_element->tme_element_private;
268:
269: /* get the context: */
270: context = TME_SUN44C_BUS_MMU_CONTEXT(sun4, conn_bus_init);
271:
272: /* get the PTE for this address and context from the MMU: */
273: rc = tme_sun_mmu_pte_get(sun4->tme_sun44c_mmu,
274: context,
275: address,
276: &pte_mmu);
277: assert (rc == TME_OK);
278:
279: /* this PTE must be valid for cacheable obmem space, and, if we're
280: writing, it must allow writing: */
281: pte = pte_mmu.tme_sun_mmu_pte_raw;
282: assert (((pte
283: & (TME_SUN44C_PTE_VALID
284: | TME_SUN44C_PTE_NC))
285: == TME_SUN44C_PTE_VALID)
286: && (cycle_type == TME_BUS_CYCLE_READ
287: || (pte & TME_SUN44C_PTE_WRITE)));
288: if ((pte & TME_SUN44C_PTE_PGTYPE) != 0 /* TME_SUN44C_PGTYPE_OBMEM */) {
289: abort();
290: }
291:
292: /* get the size of one cache line: */
293: cache_size_line = 1 << sun4->tme_sun4_cache_size_line_log2;
294:
295: /* busy the cache internal TLB entry: */
296: tme_bus_tlb_busy(&sun4->tme_sun4_cache_tlb_internal);
297:
298: /* loop until we can get a valid TLB entry: */
299: do {
300:
301: /* unbusy the cache internal TLB entry for filling: */
302: tme_bus_tlb_unbusy_fill(&sun4->tme_sun4_cache_tlb_internal);
303:
304: /* fill the cache internal TLB entry directly from the MMU. we
305: handle memory errors ourselves, so we don't need to call
306: _tme_sun44c_tlb_fill_memerr when memory errors are visible: */
307: asi_mask = TME_SPARC32_ASI_MASK_SD;
308: rc = _tme_sun44c_tlb_fill_mmu(conn_bus_init,
309: &sun4->tme_sun4_cache_tlb_internal,
310: &asi_mask,
311: address & (((tme_uint32_t) 0) - cache_size_line),
312: cycle_type);
313: assert (rc == TME_OK);
314:
315: /* rebusy the cache internal TLB entry: */
316: tme_bus_tlb_busy(&sun4->tme_sun4_cache_tlb_internal);
317:
318: /* loop if the cache internal TLB entry is invalid: */
319: } while (tme_bus_tlb_is_invalid(&sun4->tme_sun4_cache_tlb_internal));
320:
321: /* this TLB entry must allow fast access to the entire cache line.
322: this limitation is caused by our poor emulation, but the real
323: hardware may not deal well either with addresses that are marked
324: cacheable but don't map to memory: */
325: assert ((sun4->tme_sun4_cache_tlb_internal.tme_bus_tlb_emulator_off_read
326: != TME_EMULATOR_OFF_UNDEF)
327: && (cycle_type == TME_BUS_CYCLE_READ
328: || (sun4->tme_sun4_cache_tlb_internal.tme_bus_tlb_emulator_off_write
329: != TME_EMULATOR_OFF_UNDEF))
330: && (sun4->tme_sun4_cache_tlb_internal.tme_bus_tlb_addr_first
331: <= (address & -cache_size_line))
332: && (sun4->tme_sun4_cache_tlb_internal.tme_bus_tlb_addr_last
333: >= (address | (cache_size_line - 1))));
334:
335: /* done: */
336: return (pte_mmu.tme_sun_mmu_pte_raw);
337: }
338:
339: /* this flushes a line from the cache: */
340: static void
341: _tme_sun44c_cache_line_flush(struct tme_sun4 *sun4,
342: tme_uint32_t cache_line_index)
343: {
344:
345: /* if this is a write-through cache, return now: */
346: if (!sun4->tme_sun4_cache_writeback) {
347: return;
348: }
349:
350: /* XXX WRITEME: */
351: abort();
352: }
353:
354: /* this handles a bus cycle in the cache: */
355: static int
356: _tme_sun44c_cache_cycle_bus(void *_conn_bus_init,
357: struct tme_bus_cycle *cycle)
358: {
359: const struct tme_bus_connection *conn_bus_init;
360: struct tme_sun4 *sun4;
361: tme_uint32_t address;
362: tme_uint8_t context;
363: tme_uint32_t asi_mask;
364: unsigned int cache_actions;
365: tme_uint32_t cache_size;
366: tme_uint32_t cache_size_line;
367: tme_uint32_t cache_data_offset;
368: tme_shared tme_uint8_t *cache_data;
369: tme_uint32_t cache_word;
370: struct tme_bus_tlb *tlb;
371: const tme_shared tme_uint8_t *memory_data_read;
372: tme_shared tme_uint8_t *memory_data_write;
373: tme_uint32_t cache_line_index;
374: tme_uint32_t cache_tag;
375: tme_uint32_t pte;
376:
377: /* recover our initiator's bus connection and sun4: */
378: conn_bus_init = (struct tme_bus_connection *) _conn_bus_init;
379: sun4 = (struct tme_sun4 *) conn_bus_init->tme_bus_connection.tme_connection_element->tme_element_private;
380:
381: /* get the context, address, and ASI mask: */
382: context = TME_SUN44C_BUS_MMU_CONTEXT(sun4, conn_bus_init);
383: address = cycle->tme_bus_cycle_address;
384: asi_mask = sun4->tme_sun4_memtest_tlb_asi_mask;
385:
386: /* invalidate the TLB entry that triggered this cycle. this hurts
387: performance, since it keeps TLB entries valid only for one cycle
388: at a time, but it's simple and handles the problem of PTE changes
389: while the cache is visible (addresses can suddenly become
390: cacheable): */
391: assert (sun4->tme_sun4_memtest_tlb != NULL);
392: tme_bus_tlb_invalidate(sun4->tme_sun4_memtest_tlb);
393: sun4->tme_sun4_memtest_tlb = NULL;
394:
395: /* get the cache actions for this access: */
396: cache_actions = _tme_sun44c_cache_actions(conn_bus_init,
397: asi_mask,
398: address,
399: cycle->tme_bus_cycle_type);
400: assert (cache_actions != _TME_SUN44C_CACHE_ACTION_NULL);
401:
402: /* if the cache actions include any involving the cache line for
403: this address: */
404: if (cache_actions
405: & ~(_TME_SUN44C_CACHE_ACTION_ERROR_WRITE
406: | _TME_SUN44C_CACHE_ACTION_ERROR_SYSTEM)) {
407:
408: /* get the total size of the cache, and the size of one line: */
409: cache_size = 1 << sun4->tme_sun4_cache_size_log2;
410: cache_size_line = 1 << sun4->tme_sun4_cache_size_line_log2;
411:
412: /* turn this address into a pointer to the cache data for its
413: cache line, and into the cache line index: */
414: cache_data_offset = address & (cache_size - cache_size_line);
415: cache_data = sun4->tme_sun4_cache_data + cache_data_offset;
416: cache_line_index = cache_data_offset >> sun4->tme_sun4_cache_size_line_log2;
417:
418: /* if this cache line needs to be flushed: */
419: if (cache_actions & _TME_SUN44C_CACHE_ACTION_FLUSH) {
420: _tme_sun44c_cache_line_flush(sun4, cache_line_index);
421: }
422:
423: /* if this cache line needs to be invalidated: */
424: if (cache_actions & _TME_SUN44C_CACHE_ACTION_INVALIDATE) {
425: sun4->tme_sun44c_cache_tags[cache_line_index] &= ~TME_SUN44C_CACHETAG_VALID;
426: }
427:
428: /* assume that we won't access main memory: */
429: pte = 0;
430: tlb = NULL;
431:
432: /* if this cache line needs to be allocated or written-through: */
433: if (((cache_actions & _TME_SUN44C_CACHE_ACTION_ALLOCATE)
434: == _TME_SUN44C_CACHE_ACTION_ALLOCATE)
435: || (cache_actions & _TME_SUN44C_CACHE_ACTION_WRITE_THROUGH)) {
436:
437: /* we will access main memory, so get this address' PTE entry
438: and fill the cache internal TLB: */
439: pte = _tme_sun4_cache_tlb_internal_fill(conn_bus_init,
440: address,
441: ((cache_actions & _TME_SUN44C_CACHE_ACTION_WRITE_THROUGH)
442: ? TME_BUS_CYCLE_WRITE
443: : TME_BUS_CYCLE_READ));
444: tlb = &sun4->tme_sun4_cache_tlb_internal;
445: }
446:
447: /* if this cache line needs to be allocated: */
448: if ((cache_actions & _TME_SUN44C_CACHE_ACTION_ALLOCATE) == _TME_SUN44C_CACHE_ACTION_ALLOCATE) {
449:
450: /* make the new cache tag: */
451: cache_tag = _tme_sun44c_cache_tag(sun4, context, address);
452: if (pte & TME_SUN44C_PTE_SYSTEM) {
453: cache_tag |= TME_SUN44C_CACHETAG_SYSTEM;
454: }
455: if (pte & TME_SUN44C_PTE_WRITE) {
456: cache_tag |= TME_SUN44C_CACHETAG_WRITE;
457: }
458:
459: /* fill the cache line: */
460: memory_data_read
461: = (tlb->tme_bus_tlb_emulator_off_read
462: + (address & -cache_size_line));
463: cache_data_offset = 0;
464: do {
465: cache_word
466: = tme_memory_bus_read32(((const tme_shared tme_uint32_t *)
467: (memory_data_read
468: + cache_data_offset)),
469: tlb->tme_bus_tlb_rwlock,
470: sizeof(cache_word),
471: sizeof(cache_word));
472: tme_memory_bus_write32(((tme_shared tme_uint32_t *)
473: (cache_data
474: + cache_data_offset)),
475: cache_word,
476: &sun4->tme_sun4_cache_rwlock,
477: sizeof(cache_word),
478: sizeof(cache_word));
479: cache_data_offset += sizeof(cache_word);
480: } while (cache_data_offset < cache_size_line);
481:
482: /* update the cache tag: */
483: sun4->tme_sun44c_cache_tags[cache_line_index] = cache_tag;
484:
485: /* check for memory errors on this cache line fill: */
486: if (_tme_sun44c_memerr_check(conn_bus_init,
487: (address & -cache_size_line),
488: pte,
489: memory_data_read,
490: cache_size_line) != TME_OK) {
491: cache_actions |= _TME_SUN44C_CACHE_ACTION_ERROR_MEMERR;
492: }
493: }
494:
495: /* if we're reading or writing this cache line: */
496: if (cache_actions
497: & (_TME_SUN44C_CACHE_ACTION_READ
498: | _TME_SUN44C_CACHE_ACTION_WRITE_BACK
499: | _TME_SUN44C_CACHE_ACTION_WRITE_THROUGH)) {
500:
501: /* the address must be size-aligned, and the transfer must
502: fit within the cache line: */
503: assert ((address % cycle->tme_bus_cycle_size) == 0
504: && ((address
505: ^ (address
506: + cycle->tme_bus_cycle_size
507: - 1))
508: < cache_size_line));
509:
510: /* get a pointer to the cache data to transfer: */
511: cache_data += (address & (cache_size_line - 1));
512:
513: /* run the bus cycle against the cache line data: */
514: /* XXX FIXME this is not thread-safe: */
515: tme_bus_cycle_xfer_memory(cycle,
516: ((tme_uint8_t *) cache_data) - address,
517: address + cycle->tme_bus_cycle_size - 1);
518:
519: /* if we're writing through this cache line: */
520: if (cache_actions & _TME_SUN44C_CACHE_ACTION_WRITE_THROUGH) {
521:
522: /* write the data through the cache: */
523: memory_data_write
524: = (tlb->tme_bus_tlb_emulator_off_write
525: + address);
526: switch (cycle->tme_bus_cycle_size) {
527: default:
528: assert (FALSE);
529: /* FALLTHROUGH */
530: case sizeof(tme_uint8_t):
531: tme_memory_bus_write8(memory_data_write,
532: tme_memory_bus_read8(cache_data,
533: &sun4->tme_sun4_cache_rwlock,
534: sizeof(tme_uint8_t),
535: sizeof(tme_uint32_t)),
536: tlb->tme_bus_tlb_rwlock,
537: sizeof(tme_uint8_t),
538: sizeof(tme_uint32_t));
539: break;
540: case sizeof(tme_uint16_t):
541: tme_memory_bus_write16((tme_shared tme_uint16_t *) memory_data_write,
542: tme_memory_bus_read16((const tme_shared tme_uint16_t *) cache_data,
543: &sun4->tme_sun4_cache_rwlock,
544: sizeof(tme_uint16_t),
545: sizeof(tme_uint32_t)),
546: tlb->tme_bus_tlb_rwlock,
547: sizeof(tme_uint16_t),
548: sizeof(tme_uint32_t));
549: break;
550: case sizeof(tme_uint32_t):
551: tme_memory_bus_write32((tme_shared tme_uint32_t *) memory_data_write,
552: tme_memory_bus_read32((const tme_shared tme_uint32_t *) cache_data,
553: &sun4->tme_sun4_cache_rwlock,
554: sizeof(tme_uint32_t),
555: sizeof(tme_uint32_t)),
556: tlb->tme_bus_tlb_rwlock,
557: sizeof(tme_uint32_t),
558: sizeof(tme_uint32_t));
559: break;
560: }
561:
562: /* update any memory errors caused by this write: */
563: _tme_sun44c_memerr_update(sun4,
564: pte,
565: memory_data_write,
566: cycle->tme_bus_cycle_size);
567: }
568: }
569:
570: /* if we accessed main memory: */
571: if (tlb != NULL) {
572:
573: /* unbusy the cache internal TLB entry: */
574: tme_bus_tlb_unbusy(tlb);
575: }
576: }
577:
578: /* if we're returning a protection error: */
579: if (cache_actions
580: & (_TME_SUN44C_CACHE_ACTION_ERROR_SYSTEM
581: | _TME_SUN44C_CACHE_ACTION_ERROR_WRITE)) {
582: return (_tme_sun44c_mmu_proterr((void *) conn_bus_init, cycle));
583: }
584:
585: /* if we're returning a memory error: */
586: if (cache_actions & _TME_SUN44C_CACHE_ACTION_ERROR_MEMERR) {
587: return (_tme_sun44c_ob_fault_handler((void *) conn_bus_init, NULL, cycle, EIO));
588: }
589:
590: /* return success: */
591: return (TME_OK);
592: }
593:
594: /* this fills TLBs when the cache is visible: */
595: int
596: _tme_sun44c_tlb_fill_cache(const struct tme_bus_connection *conn_bus_init,
597: struct tme_bus_tlb *tlb,
598: tme_uint32_t *_asi_mask,
599: tme_uint32_t address,
600: unsigned int cycles)
601: {
602: struct tme_sun4 *sun4;
603: tme_uint32_t asi_mask;
604: tme_uint32_t cache_size_line;
605:
606: /* recover our sun4: */
607: sun4 = (struct tme_sun4 *) conn_bus_init->tme_bus_connection.tme_connection_element->tme_element_private;
608:
609: /* recover the ASI mask: */
610: asi_mask = *_asi_mask;
611:
612: /* this ASI mask must be a single ASI, for user or supervisor
613: instruction or data: */
614: assert (asi_mask == TME_SPARC32_ASI_MASK_UD
615: || asi_mask == TME_SPARC32_ASI_MASK_UI
616: || asi_mask == TME_SPARC32_ASI_MASK_SD
617: || asi_mask == TME_SPARC32_ASI_MASK_SI);
618:
619: /* invalidate any other memory test TLB entry: */
620: if (sun4->tme_sun4_memtest_tlb != NULL
621: && sun4->tme_sun4_memtest_tlb != tlb) {
622: tme_bus_tlb_invalidate(sun4->tme_sun4_memtest_tlb);
623: }
624: sun4->tme_sun4_memtest_tlb = NULL;
625:
626: /* the cache must be visible: */
627: assert (sun4->tme_sun4_cache_visible > 0);
628:
629: /* if the cache is no longer visible: */
630: if ((--sun4->tme_sun4_cache_visible) == 0) {
631:
632: /* update the TLB fill function: */
633: sun4->tme_sun4_tlb_fill = _tme_sun44c_cache_tlb_fill_next(sun4);
634: }
635:
636: /* otherwise, if this access will be handled by the cache: */
637: else if (_tme_sun44c_cache_actions(conn_bus_init,
638: asi_mask,
639: address,
640: cycles)
641: != _TME_SUN44C_CACHE_ACTION_NULL) {
642:
643: /* fill this TLB entry: */
644: tme_bus_tlb_initialize(tlb);
645: cache_size_line = 1 << sun4->tme_sun4_cache_size_line_log2;
646: tlb->tme_bus_tlb_addr_first = address & -cache_size_line;
647: tlb->tme_bus_tlb_addr_last = address | (cache_size_line - 1);
648: tlb->tme_bus_tlb_cycles_ok = (TME_BUS_CYCLE_READ | TME_BUS_CYCLE_WRITE);
649: tlb->tme_bus_tlb_emulator_off_read = TME_EMULATOR_OFF_UNDEF;
650: tlb->tme_bus_tlb_emulator_off_write = TME_EMULATOR_OFF_UNDEF;
651: tlb->tme_bus_tlb_cycle_private = (void *) conn_bus_init;
652: tlb->tme_bus_tlb_cycle = _tme_sun44c_cache_cycle_bus;
653:
654: /* remember this TLB entry and ASI mask: */
655: sun4->tme_sun4_memtest_tlb = tlb;
656: sun4->tme_sun4_memtest_tlb_asi_mask = asi_mask;
657:
658: /* return success: */
659: return (TME_OK);
660: }
661:
662: /* chain to the next TLB fill function: */
663: return ((*_tme_sun44c_cache_tlb_fill_next(sun4))(conn_bus_init,
664: tlb,
665: _asi_mask,
666: address,
667: cycles));
668: }
669:
670: /* this updates the visibility of the cache: */
671: void
672: _tme_sun44c_cache_enable_change(struct tme_sun4 *sun4)
673: {
674:
675: /* if the cache is enabled in the boot state, it is visible: */
676: if ((sun4->tme_sun44c_enable
677: & (TME_SUN44C_ENA_CACHE
678: | TME_SUN44C_ENA_NOTBOOT)) == TME_SUN44C_ENA_CACHE) {
679:
680: /* if the cache is currently invisible: */
681: if (!sun4->tme_sun4_cache_visible) {
682:
683: /* update the TLB fill function: */
684: sun4->tme_sun4_tlb_fill = _tme_sun44c_tlb_fill_cache;
685:
686: /* invalidate all TLBs: */
687: tme_sun_mmu_tlbs_invalidate(sun4->tme_sun44c_mmu);
688: }
689:
690: /* refresh the cache visibility: */
691: sun4->tme_sun4_cache_visible = _TME_SUN44C_CACHE_VISIBLE_BUS_CYCLES;
692: }
693:
694: /* otherwise, the cache is not visible: */
695: else {
696:
697: /* if the cache is currently visible: */
698: if (sun4->tme_sun4_cache_visible) {
699:
700: /* the next TLB fill will not see the cache: */
701: sun4->tme_sun4_cache_visible = 1;
702: }
703: }
704: }
705:
706: /* this does a control bus cycle with either the tag or data cache
707: memory: */
708: int
709: _tme_sun44c_cache_cycle_control(struct tme_sun4 *sun4,
710: struct tme_bus_cycle *cycle)
711: {
712: tme_uint32_t address;
713: tme_uint32_t cache_size;
714: tme_uint32_t cache_line_index;
715: tme_uint32_t value32;
716:
717: /* get the total size of the cache: */
718: cache_size = 1 << sun4->tme_sun4_cache_size_log2;
719:
720: /* get the address: */
721: address = cycle->tme_bus_cycle_address;
722:
723: /* if this is an access to the cache tags: */
724: if ((address ^ TME_SUN44C_CONTROL_CACHE_TAGS) < cache_size) {
725:
726: /* if the address isn't 32-bit aligned, we can't emulate this
727: access: */
728: if (address % sizeof(tme_uint32_t)) {
729: abort();
730: }
731:
732: /* get the cache line index: */
733: cache_line_index
734: = ((address
735: ^ TME_SUN44C_CONTROL_CACHE_TAGS)
736: >> sun4->tme_sun4_cache_size_line_log2);
737:
738: /* transfer the cache tag: */
739: value32 = tme_htobe_u32(sun4->tme_sun44c_cache_tags[cache_line_index]);
740: tme_bus_cycle_xfer_memory(cycle,
741: (((tme_uint8_t *) &value32)
742: - cycle->tme_bus_cycle_address),
743: (cycle->tme_bus_cycle_address
744: + cycle->tme_bus_cycle_size
745: - 1));
746:
747: /* if this was a write: */
748: if (cycle->tme_bus_cycle_type == TME_BUS_CYCLE_WRITE) {
749:
750: /* only certain bits in a cache tag are writable: */
751: value32 = tme_betoh_u32(value32);
752: value32
753: &= (TME_SUN44C_CACHETAG_CONTEXT
754: | TME_SUN44C_CACHETAG_WRITE
755: | TME_SUN44C_CACHETAG_SYSTEM
756: | TME_SUN44C_CACHETAG_VALID
757: | TME_SUN44C_CACHETAG_TAG);
758:
759: /* if the cache is enabled but not visible, and the tag being
760: written is not zero, we can't emulate this access: */
761: if ((sun4->tme_sun44c_enable & TME_SUN44C_ENA_CACHE)
762: && !sun4->tme_sun4_cache_visible
763: && value32 != 0) {
764: abort();
765: }
766:
767: /* write the cache tag: */
768: sun4->tme_sun44c_cache_tags[cache_line_index] = value32;
769: }
770: }
771:
772: /* otherwise, if this is an access to the cache data: */
773: else if ((address ^ TME_SUN44C_CONTROL_CACHE_DATA) < cache_size) {
774:
775: /* if the cache is enabled but not visible, we can't emulate this
776: access: */
777: if ((sun4->tme_sun44c_enable & TME_SUN44C_ENA_CACHE)
778: && !sun4->tme_sun4_cache_visible) {
779: abort();
780: }
781:
782: /* transfer the cache data: */
783: /* XXX FIXME this is not thread-safe: */
784: tme_bus_cycle_xfer_memory(cycle,
785: (tme_uint8_t *)
786: (sun4->tme_sun4_cache_data
787: - TME_SUN44C_CONTROL_CACHE_DATA),
788: (TME_SUN44C_CONTROL_CACHE_DATA
789: + cache_size
790: - 1));
791: }
792:
793: /* otherwise, we can't emulate this cycle: */
794: else {
795: abort();
796: }
797:
798: /* refresh the cache visibility: */
799: _tme_sun44c_cache_enable_change(sun4);
800: return (TME_OK);
801: }
802:
803: /* this does a flush bus cycle: */
804: void
805: _tme_sun44c_cache_cycle_flush(struct tme_sun4 *sun4,
806: tme_uint32_t asi,
807: tme_uint32_t address)
808: {
809: tme_uint32_t cache_tag_mask;
810: tme_uint32_t cache_tag_value;
811: tme_uint32_t address_mask;
812: tme_uint32_t cache_size;
813: tme_uint32_t cache_line_index;
814: tme_uint32_t cache_line_count;
815:
816: /* assume that this is not a hardware-assisted flush: */
817: cache_line_count = 1;
818:
819: /* if this is a sun4c: */
820: if (TME_SUN4_IS_SUN4C(sun4)) {
821:
822: /* if this is a hardware-assisted flush: */
823: #if ((TME_SUN44C_ASI_FLUSH_SEG + 1) != TME_SUN44C_ASI_FLUSH_PG) || ((TME_SUN44C_ASI_FLUSH_PG + 1) != TME_SUN44C_ASI_FLUSH_CONTEXT)
824: #error "bad sun4c unassisted flushing ASIs"
825: #endif
826: if (asi < TME_SUN44C_ASI_FLUSH_SEG
827: || asi > TME_SUN44C_ASI_FLUSH_CONTEXT) {
828:
829: /* XXX FIXME - what happens when the address is not page-aligned? */
830: if (address % TME_SUN4C_PAGE_SIZE) {
831: abort();
832: }
833:
834: /* we flush one page's worth of cache lines: */
835: cache_line_count = TME_SUN4C_PAGE_SIZE >> sun4->tme_sun4_cache_size_line_log2;
836: }
837: }
838:
839: /* otherwise, this is a sun4: */
840: else {
841: assert(asi != TME_SUN4C_ASI_HW_FLUSH_SEG
842: && asi != TME_SUN4C_ASI_HW_FLUSH_PG);
843: }
844:
845: /* the sun4c hw-flush-all and sun4 flush-user are handled specially: */
846: if (asi == TME_SUN4C_ASI_HW_FLUSH_ALL /* TME_SUN4_ASI_FLUSH_USER */) {
847:
848: /* on the sun4c, this flushes any valid cache line.
849: on the sun4, this flushes any valid user cache line: */
850: cache_tag_value = TME_SUN44C_CACHETAG_VALID;
851: cache_tag_mask
852: = (TME_SUN4_IS_SUN4C(sun4)
853: ? TME_SUN44C_CACHETAG_VALID
854: : TME_SUN44C_CACHETAG_VALID | TME_SUN44C_CACHETAG_SYSTEM);
855: }
856:
857: /* otherwise, this is flushing some subset of the context/address
858: combinations that might be in the cache: */
859: else {
860:
861: /* dispatch on the flush type, to get an mask of bits that we will
862: ignore in the address when we make the tag: */
863: address_mask = 0;
864: cache_tag_mask = 0;
865: switch(asi) {
866: default:
867: assert(FALSE);
868: /* FALLTHROUGH */
869: case TME_SUN4_ASI_FLUSH_REG: /* TME_SUN4C_ASI_HW_FLUSH_CONTEXT */
870: if (!TME_SUN4_IS_SUN4C(sun4)) {
871: abort();
872: break;
873: }
874: /* FALLTHROUGH */
875: case TME_SUN44C_ASI_FLUSH_CONTEXT:
876: address_mask -= 1;
877: cache_tag_mask = TME_SUN44C_CACHETAG_SYSTEM;
878: break;
879: case TME_SUN44C_ASI_FLUSH_SEG:
880: case TME_SUN4C_ASI_HW_FLUSH_SEG:
881: address_mask -= TME_SUN4_32_SEGMENT_SIZE;
882: break;
883: case TME_SUN44C_ASI_FLUSH_PG:
884: case TME_SUN4C_ASI_HW_FLUSH_PG:
885: address_mask -= TME_SUN4C_PAGE_SIZE;
886: break;
887: }
888:
889: /* turn this address and mask into a cache tag value and mask: */
890: cache_tag_value = _tme_sun44c_cache_tag(sun4, sun4->tme_sun44c_context, address & address_mask);
891: cache_tag_mask |= _tme_sun44c_cache_tag(sun4, 0xff, address_mask);
892: }
893:
894: /* get the total size of the cache: */
895: cache_size = 1 << sun4->tme_sun4_cache_size_log2;
896:
897: /* turn this address into its cache line index: */
898: cache_line_index = (address & (cache_size - 1)) >> sun4->tme_sun4_cache_size_line_log2;
899:
900: /* flush cache lines: */
901: do {
902:
903: /* if this cache line tag matches what we're supposed to flush: */
904: if (((sun4->tme_sun44c_cache_tags[cache_line_index]
905: ^ cache_tag_value)
906: & cache_tag_mask) == 0) {
907:
908: /* flush this cache line: */
909: _tme_sun44c_cache_line_flush(sun4, cache_line_index);
910:
911: /* invalidate this cache line: */
912: sun4->tme_sun44c_cache_tags[cache_line_index] &= ~TME_SUN44C_CACHETAG_VALID;
913: }
914:
915: /* loop: */
916: cache_line_index++;
917: } while (--cache_line_count > 0);
918: }
919:
920: /* this creates a sun4/4c cache: */
921: void
922: _tme_sun44c_cache_new(struct tme_sun4 *sun4)
923: {
924: tme_uint32_t cache_size;
925: tme_uint32_t cache_size_line;
926: tme_uint32_t cache_size_log2;
927: tme_uint32_t cache_size_line_log2;
928: int cache_writeback;
929:
930: /* assume that if this is a sun4c, the cache is write-through.
931: otherwise, assume the cache is write-back: */
932: cache_writeback = !TME_SUN4_IS_SUN4C(sun4);
933:
934: /* SS2, code name "Calvin": */
935: if (TME_SUN4_IS_MODEL(sun4, TME_SUN_IDPROM_TYPE_CODE_CALVIN)) {
936: cache_size = 64 * 1024;
937: cache_size_line = 32;
938: }
939: else {
940: abort();
941: }
942:
943: /* get the log2 of the cache sizes: */
944: assert (cache_size >= cache_size_line && cache_size_line > sizeof(tme_uint32_t));
945: assert ((cache_size & (cache_size - 1)) == 0);
946: assert ((cache_size_line & (cache_size_line - 1)) == 0);
947: for (cache_size_log2 = 0; (tme_uint32_t) (1 << cache_size_log2) < cache_size; cache_size_log2++);
948: for (cache_size_line_log2 = 0; (tme_uint32_t) (1 << cache_size_line_log2) < cache_size_line; cache_size_line_log2++);
949:
950: /* create the cache: */
951: sun4->tme_sun4_cache_size_log2 = cache_size_log2;
952: sun4->tme_sun4_cache_size_line_log2 = cache_size_line_log2;
953: sun4->tme_sun4_cache_writeback = cache_writeback;
954: sun4->tme_sun4_cache_data = (tme_uint8_t *) tme_new(tme_uint32_t, (cache_size / sizeof(tme_uint32_t)));
955: sun4->tme_sun44c_cache_tags = tme_new(tme_uint32_t, (cache_size / cache_size_line));
956: tme_rwlock_init(&sun4->tme_sun4_cache_rwlock);
957: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.