|
|
1.1.1.2 ! root 1: /* $Id: sun44c-cache.c,v 1.3 2009/08/30 14:04:24 fredette Exp $ */ 1.1 root 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> 1.1.1.2 ! root 37: _TME_RCSID("$Id: sun44c-cache.c,v 1.3 2009/08/30 14:04:24 fredette Exp $"); 1.1 root 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); 1.1.1.2 ! root 392: tme_token_invalidate(sun4->tme_sun4_memtest_tlb->tme_bus_tlb_token); 1.1 root 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) { 1.1.1.2 ! root 622: tme_token_invalidate(sun4->tme_sun4_memtest_tlb->tme_bus_tlb_token); 1.1 root 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); 1.1.1.2 ! root 957: sun4->tme_sun4_cache_tlb_internal.tme_bus_tlb_token = &sun4->tme_sun4_cache_tlb_internal_token; ! 958: tme_token_init(sun4->tme_sun4_cache_tlb_internal.tme_bus_tlb_token); 1.1 root 959: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.