|
|
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.