| /* $Id: stp103x.c,v 1.5 2010/06/05 18:57:04 fredette Exp $ */ |
| |
| /* ic/sparc/stp103x.c - implementation of UltraSPARC I (STP1030) and II (STP1031) emulation: */ |
| |
| /* |
| * Copyright (c) 2008 Matt Fredette |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * 3. All advertising materials mentioning features or use of this software |
| * must display the following acknowledgement: |
| * This product includes software developed by Matt Fredette. |
| * 4. The name of the author may not be used to endorse or promote products |
| * derived from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
| * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
| * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, |
| * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
| * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
| * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
| * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, |
| * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN |
| * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| * POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include <tme/common.h> |
| _TME_RCSID("$Id: stp103x.c,v 1.5 2010/06/05 18:57:04 fredette Exp $"); |
| |
| /* includes: */ |
| #include "sparc-impl.h" |
| |
| /* macros: */ |
| |
| /* the PSTATE extensions: */ |
| #define TME_STP103X_PSTATE_IG TME_BIT(11) |
| #define TME_STP103X_PSTATE_MG TME_BIT(10) |
| |
| /* the load/store unit control register: */ |
| #define TME_STP103X_LSU_IC TME_BIT(0) |
| #define TME_STP103X_LSU_DC TME_BIT(1) |
| #define TME_STP103X_LSU_IM TME_BIT(2) |
| #define TME_STP103X_LSU_DM TME_BIT(3) |
| #define TME_STP103X_LSU_FM (TME_BIT(20) - TME_BIT(4)) |
| #define TME_STP103X_LSU_VW TME_BIT(21) |
| #define TME_STP103X_LSU_VR TME_BIT(22) |
| #define TME_STP103X_LSU_PW TME_BIT(23) |
| #define TME_STP103X_LSU_PR TME_BIT(24) |
| |
| /* the E-Cache (E-state) error enable register: */ |
| #define TME_STP103X_ESTATE_ERROR_ENABLE_CEEN TME_BIT(0) |
| #define TME_STP103X_ESTATE_ERROR_ENABLE_NCEEN TME_BIT(1) |
| #define TME_STP103X_ESTATE_ERROR_ENABLE_ISAPEN TME_BIT(2) |
| |
| /* the start of the virtual address hole: */ |
| #define TME_STP103X_VA_HOLE_START (((tme_uint64_t) 1) << 43) |
| |
| /* the size of the physical address space: */ |
| #define TME_STP103X_PA_SIZE (((tme_uint64_t) 1) << 41) |
| |
| /* the maximum context number: */ |
| #define TME_STP103X_CONTEXT_MAX (8191) |
| |
| /* the different page sizes: */ |
| #define TME_STP103X_PAGE_SIZE_8KB (8 * 1024) |
| #define TME_STP103X_PAGE_SIZE_64KB (64 * 1024) |
| #define TME_STP103X_PAGE_SIZE_512KB (512 * 1024) |
| #define TME_STP103X_PAGE_SIZE_4MB (4 * 1024 * 1024) |
| |
| /* because we often deal with the tme_uint64_t TLB/TTE entry tag and |
| data as tme_uint32_t halves for performance, we need to easily |
| generate the right bitfield masks for both types. this macro |
| converts the constant x to the type of e, and then shifts it by |
| (shift mod (8 * sizeof(e))). e is only typed, never evaluated: */ |
| #define _TME_STP103X_TLB_X(e, x, shift) ((1 ? (x) : (e)) << ((shift) % (8 * sizeof(e)))) |
| |
| /* TLB/TTE data: */ |
| #define TME_STP103X_TLB_DATA_V(e) _TME_STP103X_TLB_X(e, 1, 63) |
| #define TME_STP103X_TLB_DATA_SIZE_MASK(e) _TME_STP103X_TLB_X(e, 0x3, 61) |
| #define TME_STP103X_TLB_DATA_SIZE_8KB(e) _TME_STP103X_TLB_X(e, 0x0, 61) |
| #define TME_STP103X_TLB_DATA_SIZE_64KB(e) _TME_STP103X_TLB_X(e, 0x1, 61) |
| #define TME_STP103X_TLB_DATA_SIZE_512KB(e) _TME_STP103X_TLB_X(e, 0x2, 61) |
| #define TME_STP103X_TLB_DATA_SIZE_4MB(e) _TME_STP103X_TLB_X(e, 0x3, 61) |
| #define TME_STP103X_TLB_DATA_NFO(e) _TME_STP103X_TLB_X(e, 1, 60) |
| #define TME_STP103X_TLB_DATA_IE(e) _TME_STP103X_TLB_X(e, 1, 59) |
| #define TME_STP103X_TLB_DATA_SOFT2(e) _TME_STP103X_TLB_X(e, 0x1ff, 50) |
| #define TME_STP103X_TLB_DATA_SIZE_CAM_MASK(e) _TME_STP103X_TLB_X(e, 0x7, 45) |
| #define TME_STP103X_TLB_DATA_SIZE_CAM_8KB(e) _TME_STP103X_TLB_X(e, 0x0, 45) |
| #define TME_STP103X_TLB_DATA_SIZE_CAM_64KB(e) _TME_STP103X_TLB_X(e, 0x1, 45) |
| #define TME_STP103X_TLB_DATA_SIZE_CAM_512KB(e) _TME_STP103X_TLB_X(e, 0x3, 45) |
| #define TME_STP103X_TLB_DATA_SIZE_CAM_4MB(e) _TME_STP103X_TLB_X(e, 0x7, 45) |
| #define TME_STP103X_TLB_DATA_SIZE_RAM_MASK(e) _TME_STP103X_TLB_X(e, 0x7, 42) |
| #define TME_STP103X_TLB_DATA_SIZE_RAM_8KB(e) _TME_STP103X_TLB_X(e, 0x0, 42) |
| #define TME_STP103X_TLB_DATA_SIZE_RAM_64KB(e) _TME_STP103X_TLB_X(e, 0x1, 42) |
| #define TME_STP103X_TLB_DATA_SIZE_RAM_512KB(e) _TME_STP103X_TLB_X(e, 0x3, 42) |
| #define TME_STP103X_TLB_DATA_SIZE_RAM_4MB(e) _TME_STP103X_TLB_X(e, 0x7, 42) |
| #define TME_STP103X_TLB_DATA_DIAG_USED(e) _TME_STP103X_TLB_X(e, 1, 41) |
| #define TME_STP103X_TLB_DATA_PA ((((tme_uint64_t) 1) << 41) - (1 << 13)) |
| #define TME_STP103X_TLB_DATA_SOFT(e) _TME_STP103X_TLB_X(e, 0x3f, 7) |
| #define TME_STP103X_TLB_DATA_L(e) _TME_STP103X_TLB_X(e, 1, 6) |
| #define TME_STP103X_TLB_DATA_CP(e) _TME_STP103X_TLB_X(e, 1, 5) |
| #define TME_STP103X_TLB_DATA_CV(e) _TME_STP103X_TLB_X(e, 1, 4) |
| #define TME_STP103X_TLB_DATA_E(e) _TME_STP103X_TLB_X(e, 1, 3) |
| #define TME_STP103X_TLB_DATA_P(e) _TME_STP103X_TLB_X(e, 1, 2) |
| #define TME_STP103X_TLB_DATA_W(e) _TME_STP103X_TLB_X(e, 1, 1) |
| #define TME_STP103X_TLB_DATA_G(e) _TME_STP103X_TLB_X(e, 1, 0) |
| |
| /* the DMMU and IMMU SFSR: */ |
| #define TME_STP103X_SFSR_ASI (0xff << 16) |
| #define TME_STP103X_SFSR_FT_PRIVILEGE (0x01 << 7) |
| #define TME_STP103X_SFSR_FT_SIDE_EFFECTS (0x02 << 7) |
| #define TME_STP103X_SFSR_FT_UNCACHEABLE (0x04 << 7) |
| #define TME_STP103X_SFSR_FT_ILLEGAL (0x08 << 7) |
| #define TME_STP103X_SFSR_FT_NO_FAULT_FAULT (0x10 << 7) |
| #define TME_STP103X_SFSR_FT_VA_RANGE (0x20 << 7) |
| #define TME_STP103X_SFSR_FT_VA_RANGE_NNPC (0x40 << 7) |
| #define TME_STP103X_SFSR_E TME_BIT(6) |
| #define TME_STP103X_SFSR_CT_PRIMARY (0x0 << 4) |
| #define TME_STP103X_SFSR_CT_SECONDARY (0x1 << 4) |
| #define TME_STP103X_SFSR_CT_NUCLEUS (0x2 << 4) |
| #define TME_STP103X_SFSR_CT_RESERVED (0x3 << 4) |
| #define TME_STP103X_SFSR_PR TME_BIT(3) |
| #define TME_STP103X_SFSR_W TME_BIT(2) |
| #define TME_STP103X_SFSR_OW TME_BIT(1) |
| #define TME_STP103X_SFSR_FV TME_BIT(0) |
| |
| /* the AFSR: */ |
| #define TME_STP103X_AFSR_ME (((tme_uint64_t) 1) << 32) |
| #define TME_STP103X_AFSR_PRIV TME_BIT(31) |
| #define TME_STP103X_AFSR_TO TME_BIT(27) |
| |
| /* a TSB register: */ |
| #define TME_STP103X_TSB_SIZE (0x7) |
| #define TME_STP103X_TSB_SPLIT TME_BIT(12) |
| |
| /* specific traps: */ |
| #define TME_STP103X_TRAP_MG _TME_SPARC_TRAP_IMPDEP(0) |
| #define TME_STP103X_TRAP_IG _TME_SPARC_TRAP_IMPDEP(1) |
| #define TME_STP103X_TRAP_interrupt_vector \ |
| (TME_STP103X_TRAP_IG | _TME_SPARC_TRAP(16, 0x060)) |
| #define TME_STP103X_TRAP_fast_instruction_access_MMU_miss \ |
| (TME_STP103X_TRAP_MG | _TME_SPARC_TRAP(2, 0x064)) |
| #define TME_STP103X_TRAP_fast_data_access_MMU_miss \ |
| (TME_STP103X_TRAP_MG | _TME_SPARC_TRAP(12, 0x068)) |
| #define TME_STP103X_TRAP_fast_data_access_protection \ |
| (TME_STP103X_TRAP_MG | _TME_SPARC_TRAP(12, 0x06c)) |
| |
| /* specific ASIs and flags: */ |
| #define TME_STP103X_ASI_LSU_CONTROL_REG (0x45) |
| #define TME_STP103X_ASI_DCACHE_DATA (0x46) |
| #define TME_STP103X_ASI_DCACHE_TAG (0x47) |
| #define TME_STP103X_ASI_INTR_DISPATCH_STATUS (0x48) |
| #define TME_STP103X_ASI_INTR_RECEIVE (0x49) |
| #define TME_STP103X_ASI_UPA_CONFIG_REG (0x4a) |
| #define TME_STP103X_ASI_ESTATE_ERROR_EN_REG (0x4b) |
| #define TME_STP103X_ASI_AFSR (0x4c) |
| #define TME_STP103X_ASI_AFAR (0x4d) |
| #define TME_STP103X_ASI_ECACHE_TAG_DATA (0x4e) |
| #define TME_STP103X_ASI_IMMU (0x50) |
| #define TME_STP103X_ASI_DMMU (0x58) |
| #define TME_STP103X_ASI_FLAG_TSB_8KB_PTR (0x1) |
| #define TME_STP103X_ASI_FLAG_TSB_64KB_PTR (0x2) |
| #define TME_STP103X_ASI_BLK_COMMIT (0xe0) |
| |
| /* the size of the IMMU and DMMU TLBs: */ |
| #define TME_STP103X_TLB_SIZE (64) |
| |
| /* the size of the E-Cache: */ |
| #define TME_STP103X_ECACHE_SIZE (512 * 1024) |
| |
| /* the block size of the I-Cache: */ |
| #define TME_STP103X_ICACHE_BLOCK_SIZE (32) |
| |
| /* the TICK_compare register: */ |
| #define TME_STP103X_TCR_INT_DIS (((tme_uint64_t) 1) << 63) |
| #define TME_STP103X_TCR_TICK_CMPR (TME_STP103X_TCR_INT_DIS - 1) |
| |
| /* the SIR: */ |
| #define TME_STP103X_SIR_SOFTINT(x) (1 << (x)) |
| #define TME_STP103X_SIR_TICK_INT (1 << 0) |
| |
| /* ASI_INTR_RECEIVE: */ |
| #define TME_STP103X_INTR_RECEIVE_BUSY (1 << 5) |
| |
| /* the block load and store sizes: */ |
| #define TME_STP103X_BLOCK_SIZE (64) |
| #define TME_STP103X_BLOCK_FPREGS_DOUBLE (TME_STP103X_BLOCK_SIZE / sizeof(tme_uint64_t)) |
| |
| /* other constants: */ |
| #define TME_STP103X_MAXTL (5) |
| |
| /* the UPA configuration register: */ |
| /* NB: this is a partial list: */ |
| #define TME_STP1030_UPA_CONFIG_PCON ((2 << 29) - (1 << 22)) |
| #define TME_STP1031_UPA_CONFIG_PCON ((((tme_uint64_t) 2) << 32) - (1 << 22)) |
| #define TME_STP1031_UPA_CONFIG_ELIM ((((tme_uint64_t) 2) << 35) - (((tme_uint64_t) 1) << 33)) |
| |
| /* the UPA queue depths and capabilities: */ |
| #define TME_STP103X_UPA_PINT_RDQ (1) |
| #define TME_STP103X_UPA_PREQ_DQ (0) |
| #define TME_STP103X_UPA_PREQ_RQ (1) |
| #define TME_STP103X_UPA_UPACAP \ |
| (TME_UPA_UPACAP_HANDLERSLAVE \ |
| | TME_UPA_UPACAP_INTERRUPTMASTER \ |
| | !TME_UPA_UPACAP_SLAVE_INT_L \ |
| | TME_UPA_UPACAP_CACHEMASTER \ |
| | TME_UPA_UPACAP_MASTER) |
| |
| /* fixed characteristics of the stp103x: */ |
| #undef TME_SPARC_VERSION |
| #define TME_SPARC_VERSION(ic) (9) |
| #undef TME_SPARC_NWINDOWS |
| #define TME_SPARC_NWINDOWS(ic) (8) |
| #undef TME_SPARC_MEMORY_FLAGS |
| #define TME_SPARC_MEMORY_FLAGS(ic) \ |
| (TME_SPARC_MEMORY_FLAG_HAS_NUCLEUS \ |
| + TME_SPARC_MEMORY_FLAG_HAS_INVERT_ENDIAN \ |
| + !TME_SPARC_MEMORY_FLAG_HAS_LDDF_STDF_32) |
| |
| /* this recovers the stp103x state from the generic sparc state: */ |
| #define TME_STP103X(ic) ((struct tme_stp103x *) (TRUE ? (ic) : (struct tme_sparc *) 0)) |
| |
| /* this evaluates to nonzero if an ASI mask from an ASI_DMMU* or |
| ASI_IMMU* ASI is from an ASI_DMMU* ASI: */ |
| #if (TME_STP103X_ASI_DMMU <= TME_STP103X_ASI_IMMU) || ((TME_STP103X_ASI_DMMU ^ TME_STP103X_ASI_IMMU) & ((TME_STP103X_ASI_DMMU ^ TME_STP103X_ASI_IMMU) - 1)) != 0 |
| #error "TME_STP103X_ASI_DMMU or TME_STP103X_ASI_IMMU changed" |
| #endif |
| #define TME_STP103X_ASI_MMU_MASK_IS_DMMU(asi_mask) \ |
| ((asi_mask) \ |
| & TME_SPARC_ASI_MASK_RAW(TME_STP103X_ASI_DMMU \ |
| ^ TME_STP103X_ASI_IMMU)) |
| |
| /* specific load/store information: */ |
| #define TME_STP103X_LSINFO_ASSERT_NO_FAULTS _TME_SPARC_LSINFO_X(0) |
| |
| /* specific load/store faults: */ |
| #define TME_STP103X_LS_FAULT_MMU_MISS _TME_SPARC64_LS_FAULT_X(0) |
| #define TME_STP103X_LS_FAULT_PRIVILEGE _TME_SPARC64_LS_FAULT_X(1) |
| #define TME_STP103X_LS_FAULT_PROTECTION _TME_SPARC64_LS_FAULT_X(2) |
| #define TME_STP103X_LS_FAULT_ILLEGAL _TME_SPARC64_LS_FAULT_X(3) |
| |
| /* update flags: */ |
| #define TME_STP103X_UPDATE_NONE (0) |
| #define TME_STP103X_UPDATE_DMMU TME_BIT(0) |
| #define TME_STP103X_UPDATE_IMMU (0) |
| #define TME_STP103X_UPDATE_MMU_TAG_ACCESS TME_BIT(1) |
| #define TME_STP103X_UPDATE_MMU_SFSR TME_BIT(2) |
| #define TME_STP103X_UPDATE_DMMU_SFAR TME_BIT(3) |
| |
| /* the first IMMU and DMMU TLB entry parts: */ |
| #define TME_STP103X_TLB_PART_0_DMMU (TME_STP103X_TLB_SIZE * 2 * 0) |
| #define TME_STP103X_TLB_PART_0_IMMU (TME_STP103X_TLB_SIZE * 2 * 1) |
| |
| /* this returns nonzero if this is an stp1030: */ |
| #define TME_STP103X_IS_1030(ic) (TME_STP103X(ic)->tme_stp103x_is_1030) |
| |
| /* types: */ |
| |
| /* the stp103x DMMU and IMMU common state: */ |
| struct tme_stp103x_mmu { |
| |
| /* the DMMU or IMMU synchronous fault status register: */ |
| tme_uint64_t tme_stp103x_mmu_sfsr; |
| |
| /* the DMMU or IMMU tag access register: */ |
| tme_uint64_t tme_stp103x_mmu_tag_access; |
| |
| /* the DMMU or IMMU translation storage buffer register: */ |
| tme_uint64_t tme_stp103x_mmu_tsb; |
| }; |
| |
| /* the stp103x state: */ |
| struct tme_stp103x { |
| |
| /* the generic sparc state: */ |
| struct tme_sparc tme_stp103x_sparc; |
| |
| /* the tick comparison register: */ |
| tme_uint64_t tme_stp103x_tcr; |
| |
| /* the softint register: */ |
| tme_uint16_t tme_stp103x_sir; |
| tme_memory_atomic_flag_t tme_stp103x_sir_tick_int; |
| |
| /* the dispatch control register: */ |
| tme_uint8_t tme_stp103x_dcr; |
| |
| /* this is nonzero if this is an stp1030: */ |
| tme_uint8_t tme_stp103x_is_1030; |
| |
| /* the performance control register: */ |
| tme_uint16_t tme_stp103x_pcr; |
| |
| /* the performance instrumentation counters: */ |
| union tme_value64 tme_stp103x_pic; |
| |
| /* the UPA configuration register: */ |
| tme_uint64_t tme_stp103x_upa_config; |
| |
| /* the load/store unit control register: */ |
| tme_uint64_t tme_stp103x_lsu; |
| |
| /* the estate error enable register: */ |
| tme_uint32_t tme_stp103x_estate_error_enable; |
| |
| /* the ecache tag data register: */ |
| tme_uint32_t tme_stp103x_ecache_tag_data; |
| |
| /* the ecache probe line: */ |
| tme_uint64_t tme_stp103x_ecache_data_probe; |
| |
| /* the transmit and receive interrupt vector data: */ |
| tme_uint64_t tme_stp103x_udb_intr_transmit[3]; |
| tme_shared tme_uint64_t tme_stp103x_udb_intr_receive[3]; |
| tme_shared tme_uint8_t tme_stp103x_intr_receive_mid; |
| tme_memory_atomic_flag_t tme_stp103x_intr_receive_busy; |
| |
| /* the tick compare condition and time: */ |
| tme_cond_t tme_stp103x_tick_compare_cond; |
| struct timeval tme_stp103x_tick_compare_time; |
| |
| /* the UDB low and high control registers: */ |
| tme_uint16_t tme_stp103x_udb_control[2]; |
| |
| /* the asynchronous fault address and status registers: */ |
| tme_uint64_t tme_stp103x_afar; |
| tme_uint64_t tme_stp103x_afsr; |
| |
| /* the DMMU and IMMU common state: */ |
| struct tme_stp103x_mmu tme_stp103x_immu; |
| struct tme_stp103x_mmu tme_stp103x_dmmu; |
| |
| /* the DMMU synchronous fault address register: */ |
| tme_uint64_t tme_stp103x_dmmu_sfar; |
| |
| /* this is nonzero if the last fast_data_access_protection trap was |
| for a 64KB page: */ |
| tme_uint8_t tme_stp103x_dmmu_direct_64KB; |
| |
| /* the DMMU and IMMU TLB: */ |
| /* NB: this single array is half IMMU TLB, half DMMU TLB, and each |
| entry has two parts: tag and data: */ |
| union { |
| tme_uint64_t _tme_stp103x_tlb_u_64s[2 * 2 * TME_STP103X_TLB_SIZE]; |
| #define tme_stp103x_tlb_64s(x) _tme_stp103x_tlb_u._tme_stp103x_tlb_u_64s[x] |
| tme_uint32_t _tme_stp103x_tlb_u_32s[2 * 4 * TME_STP103X_TLB_SIZE]; |
| #if (TME_ENDIAN_NATIVE != TME_ENDIAN_BIG) && (TME_ENDIAN_NATIVE != TME_ENDIAN_LITTLE) |
| #error "only big- and little-endian hosts are supported" |
| #endif |
| #define tme_stp103x_tlb_32s(x, y) _tme_stp103x_tlb_u._tme_stp103x_tlb_u_32s[((x) * 2) + ((y) ^ (TME_ENDIAN_NATIVE == TME_ENDIAN_BIG))] |
| } _tme_stp103x_tlb_u; |
| }; |
| |
| /* globals: */ |
| |
| /* the cacheable access bus router: */ |
| static const tme_bus_lane_t _tme_stp103x_bus_router_cacheable[1 << TME_BUS128_LOG2][1 << TME_BUS128_LOG2] = { |
| /* a byte access: */ |
| { |
| TME_BUS_LANE_ROUTE(0), |
| TME_BUS_LANE_UNDEF, |
| TME_BUS_LANE_UNDEF, TME_BUS_LANE_UNDEF, |
| TME_BUS_LANE_UNDEF, TME_BUS_LANE_UNDEF, TME_BUS_LANE_UNDEF, TME_BUS_LANE_UNDEF, |
| TME_BUS_LANE_UNDEF, TME_BUS_LANE_UNDEF, TME_BUS_LANE_UNDEF, TME_BUS_LANE_UNDEF, |
| TME_BUS_LANE_UNDEF, TME_BUS_LANE_UNDEF, TME_BUS_LANE_UNDEF, TME_BUS_LANE_UNDEF |
| }, |
| /* a word access: */ |
| { |
| TME_BUS_LANE_ROUTE(1), |
| TME_BUS_LANE_ROUTE(0), |
| TME_BUS_LANE_UNDEF, TME_BUS_LANE_UNDEF, |
| TME_BUS_LANE_UNDEF, TME_BUS_LANE_UNDEF, TME_BUS_LANE_UNDEF, TME_BUS_LANE_UNDEF, |
| TME_BUS_LANE_UNDEF, TME_BUS_LANE_UNDEF, TME_BUS_LANE_UNDEF, TME_BUS_LANE_UNDEF, |
| TME_BUS_LANE_UNDEF, TME_BUS_LANE_UNDEF, TME_BUS_LANE_UNDEF, TME_BUS_LANE_UNDEF |
| }, |
| /* a doubleword access: */ |
| { |
| TME_BUS_LANE_ROUTE(3), |
| TME_BUS_LANE_ROUTE(2), |
| TME_BUS_LANE_ROUTE(1), |
| TME_BUS_LANE_ROUTE(0), |
| TME_BUS_LANE_UNDEF, TME_BUS_LANE_UNDEF, TME_BUS_LANE_UNDEF, TME_BUS_LANE_UNDEF, |
| TME_BUS_LANE_UNDEF, TME_BUS_LANE_UNDEF, TME_BUS_LANE_UNDEF, TME_BUS_LANE_UNDEF, |
| TME_BUS_LANE_UNDEF, TME_BUS_LANE_UNDEF, TME_BUS_LANE_UNDEF, TME_BUS_LANE_UNDEF |
| }, |
| /* an extended word access: */ |
| { |
| TME_BUS_LANE_ROUTE(7), |
| TME_BUS_LANE_ROUTE(6), |
| TME_BUS_LANE_ROUTE(5), |
| TME_BUS_LANE_ROUTE(4), |
| TME_BUS_LANE_ROUTE(3), |
| TME_BUS_LANE_ROUTE(2), |
| TME_BUS_LANE_ROUTE(1), |
| TME_BUS_LANE_ROUTE(0), |
| TME_BUS_LANE_UNDEF, TME_BUS_LANE_UNDEF, TME_BUS_LANE_UNDEF, TME_BUS_LANE_UNDEF, |
| TME_BUS_LANE_UNDEF, TME_BUS_LANE_UNDEF, TME_BUS_LANE_UNDEF, TME_BUS_LANE_UNDEF |
| }, |
| /* a quadword access: */ |
| { |
| TME_BUS_LANE_ROUTE(15), |
| TME_BUS_LANE_ROUTE(14), |
| TME_BUS_LANE_ROUTE(13), |
| TME_BUS_LANE_ROUTE(12), |
| TME_BUS_LANE_ROUTE(11), |
| TME_BUS_LANE_ROUTE(10), |
| TME_BUS_LANE_ROUTE(9), |
| TME_BUS_LANE_ROUTE(8), |
| TME_BUS_LANE_ROUTE(7), |
| TME_BUS_LANE_ROUTE(6), |
| TME_BUS_LANE_ROUTE(5), |
| TME_BUS_LANE_ROUTE(4), |
| TME_BUS_LANE_ROUTE(3), |
| TME_BUS_LANE_ROUTE(2), |
| TME_BUS_LANE_ROUTE(1), |
| TME_BUS_LANE_ROUTE(0), |
| } |
| }; |
| |
| /* this maps a virtual address: */ |
| static void _tme_stp103x_ls_address_map _TME_P((struct tme_sparc *, struct tme_sparc_ls *)); |
| |
| /* this makes a never struct timeval: */ |
| static inline void |
| tme_misc_timeval_never(struct timeval *tv) |
| { |
| tv->tv_sec = 0; |
| tv->tv_sec--; |
| if (tv->tv_sec < 1) { |
| tv->tv_sec = 1; |
| tv->tv_sec <<= ((8 * sizeof(tv->tv_sec)) - 2); |
| tv->tv_sec += (tv->tv_sec - 1); |
| } |
| tv->tv_usec = 999999; |
| } |
| |
| /* this does an interrupt check: */ |
| /* NB: this may do a preinstruction trap: */ |
| static void |
| _tme_stp103x_interrupt_check(struct tme_sparc *ic, |
| int flags) |
| { |
| tme_uint32_t sir; |
| tme_uint32_t ipl; |
| tme_uint32_t trap; |
| |
| /* if we're replaying instructions, return now: */ |
| if (tme_sparc_recode_verify_replay_last_pc(ic) != 0) { |
| return; |
| } |
| |
| /* if PSTATE.IE is clear, return now: */ |
| if ((ic->tme_sparc64_ireg_pstate & TME_SPARC64_PSTATE_IE) == 0) { |
| return; |
| } |
| |
| /* if the incoming interrupt vector data is busy: */ |
| if (tme_memory_atomic_read_flag(&TME_STP103X(ic)->tme_stp103x_intr_receive_busy)) { |
| |
| /* start interrupt vector trap processing: */ |
| trap = TME_STP103X_TRAP_interrupt_vector; |
| } |
| |
| /* otherwise, the incoming interrupt vector data is not busy: */ |
| else { |
| |
| /* if no SOFTINT bits greater than PIL are set, return now: */ |
| sir = TME_STP103X(ic)->tme_stp103x_sir; |
| if (tme_memory_atomic_read_flag(&TME_STP103X(ic)->tme_stp103x_sir_tick_int)) { |
| sir |= TME_STP103X_SIR_SOFTINT(14); |
| } |
| ipl = ic->tme_sparc64_ireg_pil + 1; |
| sir >>= ipl; |
| if (sir == 0) { |
| return; |
| } |
| |
| /* get the greatest SOFTINT bit greater than PIL that is set: */ |
| for (; sir != 1; sir >>= 1, ipl++); |
| |
| /* start interrupt trap processing: */ |
| trap = TME_SPARC64_TRAP_interrupt_level(ipl); |
| } |
| |
| /* start trap processing: */ |
| if (flags & TME_SPARC_EXTERNAL_CHECK_MUTEX_LOCKED) { |
| tme_mutex_unlock(&ic->tme_sparc_external_mutex); |
| } |
| if (flags & TME_SPARC_EXTERNAL_CHECK_PCS_UPDATED) { |
| ic->tme_sparc_ireg_uint64(TME_SPARC_IREG_PC_NEXT_NEXT) = ic->tme_sparc_ireg_uint64(TME_SPARC_IREG_PC_NEXT); |
| ic->tme_sparc_ireg_uint64(TME_SPARC_IREG_PC_NEXT) = ic->tme_sparc_ireg_uint64(TME_SPARC_IREG_PC); |
| #ifdef _TME_SPARC_RECODE_VERIFY |
| ic->tme_sparc_ireg_uint64(TME_SPARC_IREG_PC) = TME_SPARC_RECODE_VERIFY_PC_NONE; |
| #endif /* _TME_SPARC_RECODE_VERIFY */ |
| } |
| tme_sparc64_trap_preinstruction(ic, trap); |
| } |
| |
| /* this updates the SIR: */ |
| /* NB: this may do a preinstruction trap: */ |
| static void |
| _tme_stp103x_update_sir(struct tme_sparc *ic, |
| tme_uint32_t sir_andn, |
| tme_uint32_t sir_or) |
| { |
| |
| /* if we're clearing SIR.TICK_INT: */ |
| if (sir_andn & TME_STP103X_SIR_TICK_INT) { |
| |
| /* do an interrupt check: */ |
| _tme_stp103x_interrupt_check(ic, TME_SPARC_EXTERNAL_CHECK_PCS_UPDATED); |
| |
| /* clear the tick interrupt atomic flag: */ |
| tme_memory_atomic_write_flag(&TME_STP103X(ic)->tme_stp103x_sir_tick_int, FALSE); |
| } |
| |
| /* if we're setting SIR.TICK_INT: */ |
| if (sir_or & TME_STP103X_SIR_TICK_INT) { |
| |
| /* set the tick interrupt atomic flag: */ |
| tme_memory_atomic_write_flag(&TME_STP103X(ic)->tme_stp103x_sir_tick_int, TRUE); |
| |
| /* we won't set SIR.TICK_INT in the normal SIR image: */ |
| sir_or ^= TME_STP103X_SIR_TICK_INT; |
| } |
| |
| /* update all other bits in SIR: */ |
| TME_STP103X(ic)->tme_stp103x_sir |
| = ((TME_STP103X(ic)->tme_stp103x_sir |
| & ~sir_andn) |
| | sir_or); |
| |
| /* do an interrupt check: */ |
| _tme_stp103x_interrupt_check(ic, TME_SPARC_EXTERNAL_CHECK_NULL); |
| } |
| |
| /* this updates the LSU control register: */ |
| static void |
| _tme_stp103x_update_lsu(struct tme_sparc *ic, tme_uint64_t lsu_new) |
| { |
| tme_uint64_t lsu_xor; |
| struct tme_sparc_tlb *tlb; |
| |
| /* get a change mask: */ |
| lsu_xor = lsu_new ^ TME_STP103X(ic)->tme_stp103x_lsu; |
| |
| /* if LSU.IC or LSU.DC are changing: */ |
| if (lsu_xor |
| & (TME_STP103X_LSU_IC |
| | TME_STP103X_LSU_DC)) { |
| |
| /* nothing to do, since we don't emulate the caches: */ |
| } |
| |
| /* if LSU.IM or LSU.DM are changing: */ |
| if (lsu_xor |
| & (TME_STP103X_LSU_IM |
| | TME_STP103X_LSU_DM)) { |
| |
| /* invalidate all of the sparc TLB entries: */ |
| tlb = &ic->tme_sparc_tlbs[0]; |
| do { |
| tme_token_invalidate_nosync(tlb->tme_sparc_tlb_bus_tlb.tme_bus_tlb_token); |
| } while (++tlb <= &ic->tme_sparc_tlbs[TME_ARRAY_ELS(ic->tme_sparc_tlbs) - 1]); |
| } |
| |
| /* if LSU.FM is changing: */ |
| if (lsu_xor & TME_STP103X_LSU_FM) { |
| /* XXX FIXME WRITEME: */ |
| abort(); |
| } |
| |
| /* if any of LSU.VR, LSU.VW, LSU.PR, LSU.PW are changing: */ |
| if (lsu_xor |
| & (TME_STP103X_LSU_VR |
| | TME_STP103X_LSU_VW |
| | TME_STP103X_LSU_PR |
| | TME_STP103X_LSU_PW)) { |
| /* XXX FIXME WRITEME: */ |
| abort(); |
| } |
| |
| /* update the register: */ |
| TME_STP103X(ic)->tme_stp103x_lsu = lsu_new; |
| } |
| |
| /* this updates the UPA configuration register: */ |
| static void |
| _tme_stp103x_update_upa_config(struct tme_sparc *ic, tme_uint64_t upa_config) |
| { |
| |
| /* if this is an STP1030: */ |
| if (TME_STP103X_IS_1030(ic)) { |
| |
| /* only the PCON field is writable: */ |
| upa_config &= TME_STP1030_UPA_CONFIG_PCON; |
| } |
| |
| /* otherwise, this is an STP1031: */ |
| else { |
| |
| /* only the PCON and ELIM fields are writable: */ |
| upa_config |
| &= (TME_STP1031_UPA_CONFIG_PCON |
| | TME_STP1031_UPA_CONFIG_ELIM); |
| |
| /* all read-only STP1031-specific fields are hardwired to zero: */ |
| } |
| |
| /* add the MID: */ |
| upa_config += (ic->_tme_upa_bus_connection->tme_upa_bus_connection_mid << 17); |
| |
| /* add the PCAP fields: */ |
| upa_config |
| += ((TME_STP103X_UPA_PINT_RDQ << 15) |
| + (TME_STP103X_UPA_PREQ_DQ << 9) |
| + (TME_STP103X_UPA_PREQ_RQ << 5) |
| + (TME_STP103X_UPA_UPACAP << 0)); |
| |
| /* set the UPA configuration register: */ |
| TME_STP103X(ic)->tme_stp103x_upa_config = upa_config; |
| } |
| |
| /* this handles a PSTATE update: */ |
| /* NB: this may do a preinstruction trap: */ |
| static void |
| _tme_stp103x_update_pstate(struct tme_sparc *ic, |
| tme_uint32_t pstate, |
| tme_uint32_t trap) |
| { |
| tme_uint64_t lsu_new; |
| tme_uint32_t pstate_xor; |
| tme_uint64_t address_mask; |
| |
| /* if we are in RED_state: */ |
| if (pstate & TME_SPARC64_PSTATE_RED) { |
| |
| /* traps that enter RED_state, and traps in RED_state, always |
| clear the LSU_Control_Register. a write of one to PSTATE.RED |
| only clears LSU.IM: */ |
| lsu_new |
| = (trap != TME_SPARC_TRAP_none |
| ? 0 |
| : (TME_STP103X(ic)->tme_stp103x_lsu |
| & ~ (tme_uint64_t) TME_STP103X_LSU_IM)); |
| _tme_stp103x_update_lsu(ic, lsu_new); |
| |
| /* if this is a power-on reset: */ |
| if (trap == TME_SPARC64_TRAP_power_on_reset) { |
| |
| /* a POR clears the E-Cache (E-State) error enable register: */ |
| TME_STP103X(ic)->tme_stp103x_estate_error_enable = 0; |
| |
| /* a POR zeroes the writable fields in the UPA configuration |
| register: */ |
| _tme_stp103x_update_upa_config(ic, 0); |
| } |
| } |
| |
| /* if this is a trap that uses the MMU globals: */ |
| if (trap & TME_STP103X_TRAP_MG) { |
| assert (trap != TME_SPARC_TRAP_none); |
| pstate |
| = ((pstate |
| & ~(TME_SPARC64_PSTATE_AG |
| + TME_STP103X_PSTATE_MG |
| + TME_STP103X_PSTATE_IG)) |
| + TME_STP103X_PSTATE_MG); |
| } |
| |
| /* otherwise, if this is a trap that uses the interrupt globals: */ |
| else if (trap & TME_STP103X_TRAP_IG) { |
| assert (trap != TME_SPARC_TRAP_none); |
| pstate |
| = ((pstate |
| & ~(TME_SPARC64_PSTATE_AG |
| + TME_STP103X_PSTATE_MG |
| + TME_STP103X_PSTATE_IG)) |
| + TME_STP103X_PSTATE_IG); |
| } |
| |
| /* otherwise, if this is another trap: */ |
| else if (trap != TME_SPARC_TRAP_none) { |
| pstate |
| &= ~(TME_STP103X_PSTATE_IG |
| + TME_STP103X_PSTATE_MG); |
| assert (pstate & TME_SPARC64_PSTATE_AG); |
| } |
| |
| /* the global register selection can't be reserved: */ |
| assert ((((pstate & TME_SPARC64_PSTATE_AG) != 0) |
| + ((pstate & TME_STP103X_PSTATE_MG) != 0) |
| + ((pstate & TME_STP103X_PSTATE_IG) != 0)) < 2); |
| |
| /* update the global register offset: */ |
| ic->tme_sparc_reg8_offset[0] |
| = ((pstate & TME_SPARC64_PSTATE_AG) |
| #if (TME_SPARC64_IREG_AG_G0 % 8) |
| #error "TME_SPARC64_IREG_AG_G0 must be a multiple of eight" |
| #endif |
| ? (TME_SPARC64_IREG_AG_G0 / 8) |
| : (pstate & TME_STP103X_PSTATE_MG) |
| #if (TME_SPARC64_IREG_MG_G0 % 8) |
| #error "TME_SPARC64_IREG_MG_G0 must be a multiple of eight" |
| #endif |
| ? (TME_SPARC64_IREG_MG_G0 / 8) |
| : (pstate & TME_STP103X_PSTATE_IG) |
| #if (TME_SPARC64_IREG_IG_G0 % 8) |
| #error "TME_SPARC64_IREG_IG_G0 must be a multiple of eight" |
| #endif |
| ? (TME_SPARC64_IREG_IG_G0 / 8) |
| #if (TME_SPARC_IREG_G0 % 8) |
| #error "TME_SPARC_IREG_G0 must be a multiple of eight" |
| #endif |
| : (TME_SPARC_IREG_G0 / 8)); |
| _TME_SPARC_RECODE_CWP_UPDATE(ic, tme_uint64_t); |
| |
| /* make sure that %g0 is zero in the normal global register set and |
| the current global register set. recode instructions thunks |
| always refer to %g0 in the normal global register set: */ |
| ic->tme_sparc_ireg_uint64(TME_SPARC_IREG_G0) = 0; |
| ic->tme_sparc_ireg_uint64(TME_SPARC_G0_OFFSET(ic) + TME_SPARC_IREG_G0) = 0; |
| |
| /* get the changing bits in PSTATE: */ |
| pstate_xor = ic->tme_sparc64_ireg_pstate ^ pstate; |
| |
| #if TME_HAVE_RECODE && TME_RECODE_SIZE_GUEST_MAX > TME_RECODE_SIZE_32 |
| |
| /* if PSTATE.AM and/or PSTATE.CLE are changing: */ |
| if (__tme_predict_false(pstate_xor |
| & (TME_SPARC64_PSTATE_AM |
| | TME_SPARC64_PSTATE_CLE |
| ))) { |
| |
| /* clear the return address stack, since chaining doesn't check that |
| PSTATE matches instructions thunks: */ |
| tme_recode_chain_ras_clear(ic->tme_sparc_recode_ic, |
| &ic->tme_sparc_ic); |
| } |
| |
| #endif /* TME_HAVE_RECODE && TME_RECODE_SIZE_GUEST_MAX > TME_RECODE_SIZE_32 */ |
| |
| /* set PSTATE: */ |
| ic->tme_sparc64_ireg_pstate = pstate; |
| |
| /* update the address mask: */ |
| address_mask |
| = ((0 - (tme_uint64_t) ((~pstate / TME_SPARC64_PSTATE_AM) & 1)) |
| | (tme_uint32_t) (0 - (tme_uint32_t) 1)); |
| ic->tme_sparc_address_mask = address_mask; |
| |
| /* mask the PCs: */ |
| ic->tme_sparc_ireg_uint64(TME_SPARC_IREG_PC_NEXT) &= address_mask; |
| ic->tme_sparc_ireg_uint64(TME_SPARC_IREG_PC_NEXT_NEXT) &= address_mask; |
| |
| /* if interrupts are enabled, we can't be trapping, because |
| _tme_stp103x_interrupt_check() might trap with an interrupt, |
| discarding the initial trap: */ |
| assert ((pstate & TME_SPARC64_PSTATE_IE) == 0 |
| || trap == TME_SPARC_TRAP_none); |
| |
| /* do an interrupt check: */ |
| _tme_stp103x_interrupt_check(ic, TME_SPARC_EXTERNAL_CHECK_NULL); |
| } |
| |
| /* this updates the AFSR: */ |
| static void |
| _tme_stp103x_update_afsr(struct tme_sparc *ic, tme_uint64_t afsr_reset) |
| { |
| |
| /* one bits in positions 20..32 clear those bits in the AFSR: */ |
| TME_STP103X(ic)->tme_stp103x_afsr |
| &= ~(afsr_reset |
| & ((((tme_uint64_t) 1) << (32 + 1)) - (1 << 20))); |
| } |
| |
| /* this updates the interrupt vector receive: */ |
| static void |
| _tme_stp103x_update_intr_receive(struct tme_sparc *ic, tme_uint64_t intr_receive) |
| { |
| tme_memory_atomic_write_flag(&TME_STP103X(ic)->tme_stp103x_intr_receive_busy, |
| (intr_receive & TME_STP103X_INTR_RECEIVE_BUSY) != 0); |
| } |
| |
| /* the stp103x rdasr: */ |
| static |
| TME_SPARC_FORMAT3(_tme_stp103x_rdasr, tme_uint64_t) |
| { |
| unsigned int reg_rs1; |
| tme_uint64_t value; |
| |
| /* if this is an implementation-specific ASR: */ |
| if (TME_SPARC_INSN & (0x10 << 14)) { |
| |
| /* get rs1: */ |
| reg_rs1 = TME_FIELD_MASK_EXTRACTU(TME_SPARC_INSN, TME_SPARC_FORMAT3_MASK_RS1); |
| |
| /* if this is an undefined implementation-specific ASR: */ |
| if (__tme_predict_false(reg_rs1 >= 0x18)) { |
| TME_SPARC_INSN_ILL(ic); |
| } |
| |
| /* if this is a read of GSR: */ |
| if (reg_rs1 == 0x13) { |
| if (__tme_predict_false((ic->tme_sparc64_ireg_pstate & TME_SPARC64_PSTATE_PEF) == 0 |
| || (ic->tme_sparc64_ireg_fprs & TME_SPARC64_FPRS_FEF) == 0)) { |
| tme_sparc64_trap(ic, TME_SPARC64_TRAP_fp_disabled); |
| } |
| value = ic->tme_sparc_vis_gsr; |
| } |
| |
| /* otherwise, this is not a read of GSR: */ |
| else { |
| |
| /* if this is a nonprivileged read: */ |
| if (__tme_predict_false(!TME_SPARC_PRIV(ic))) { |
| |
| /* if this is not a read of PIC, or if PCR.PRIV is set: */ |
| if (reg_rs1 != 0x11 |
| || (TME_STP103X(ic)->tme_stp103x_pcr & TME_BIT(0))) { |
| tme_sparc64_trap(ic, TME_SPARC64_TRAP_privileged_opcode); |
| } |
| } |
| |
| /* dispatch on rs1: */ |
| switch (reg_rs1) { |
| default: TME_SPARC_INSN_ILL(ic); |
| case 0x10: value = TME_STP103X(ic)->tme_stp103x_pcr; break; |
| case 0x11: value = TME_STP103X(ic)->tme_stp103x_pic.tme_value64_uint; break; |
| case 0x12: value = TME_STP103X(ic)->tme_stp103x_dcr; break; |
| case 0x16: |
| value = TME_STP103X(ic)->tme_stp103x_sir; |
| if (tme_memory_atomic_read_flag(&TME_STP103X(ic)->tme_stp103x_sir_tick_int)) { |
| value += TME_STP103X_SIR_TICK_INT; |
| } |
| break; |
| case 0x17: value = TME_STP103X(ic)->tme_stp103x_tcr; break; |
| } |
| } |
| TME_SPARC_FORMAT3_RD = value; |
| TME_SPARC_INSN_OK; |
| } |
| tme_sparc64_rdasr(ic, _rs1, _rs2, _rd); |
| } |
| |
| /* the stp103x rdpr: */ |
| static |
| TME_SPARC_FORMAT3(_tme_stp103x_rdpr, tme_uint64_t) |
| { |
| unsigned long offset_in_insns; |
| tme_uint32_t delay_factor; |
| tme_uint32_t insn; |
| const struct tme_sparc_tlb *itlb_current; |
| |
| /* the PROM (at least, SUNW,501-3082-update7.bin) calculates a delay |
| factor by waiting for a timer interrupt while running these two |
| instructions: |
| |
| f0055720: 10 80 00 00 b 0xf0055720 |
| f0055724: a4 04 a0 01 inc %l2 |
| |
| the PROM then does a delay by running these two instructions: |
| |
| f00557e0: 14 68 00 00 bg %xcc, 0xf00557e0 |
| f00557e4: a2 a4 60 01 deccc %l1 |
| |
| unfortunately, in the stp103x emulation, a deccc is more |
| expensive than an inc, because it must update the condition |
| codes. the difference is significant enough to cause what |
| should be a brief delay between these two PROM messages: |
| |
| Probing Memory Bank #3 0 + 0 : 0 Megabytes |
| Probing /sbus@1f,0 at 0,0 Nothing there |
| |
| to be very, very long. the cost difference appears to be even |
| worse when recode is on, and the extreme delay gives the user the |
| impression that the emulator is stuck. |
| |
| there's a chance that there's a cost difference between inc and |
| deccc even on a real stp103x; "1000 ms" on the PROM on a real |
| Ultra-1 delays noticeably longer than one second. |
| |
| this function is a gross hack that attempts to reduce the delay. |
| it modifies the original loop that calculates the delay factor to |
| use (for the "best" factor) an inccc instruction instead of a |
| inc, to make it more symmetric with the delay loop. |
| |
| it detects the original loop by this instruction that precedes it: |
| |
| f0055708: a7 51 00 00 rdpr %tick, %l3 |
| */ |
| |
| /* if this is a "rdpr %tick, %l3" instruction: */ |
| if (__tme_predict_false(TME_SPARC_INSN == 0xa7510000)) { |
| |
| /* get the offset, in units of instructions, to the next PC that |
| is I-Cache block aligned: */ |
| offset_in_insns = ic->tme_sparc_ireg_uint64(TME_SPARC_IREG_PC); |
| offset_in_insns = ~offset_in_insns; |
| offset_in_insns %= TME_STP103X_ICACHE_BLOCK_SIZE; |
| offset_in_insns = (offset_in_insns + 1) / sizeof(TME_SPARC_INSN); |
| |
| /* if the CPU is in privileged mode, and the first two |
| instructions at that PC are "b ." and "inc %l2": */ |
| if (TME_SPARC_PRIV(ic) |
| && tme_sparc_fetch_nearby(ic, offset_in_insns + 0) == 0x10800000 |
| && tme_sparc_fetch_nearby(ic, offset_in_insns + 1) == 0xa404a001) { |
| |
| /* if the PROM delay factor is to be corrected: */ |
| delay_factor = ic->tme_sparc_prom_delay_factor; |
| if (delay_factor != TME_SPARC_PROM_DELAY_FACTOR_UNCORRECTED) { |
| |
| /* if the PROM delay factor correction is "min": */ |
| if (delay_factor == TME_SPARC_PROM_DELAY_FACTOR_MIN) { |
| |
| /* the delay factor is calculated by running the loop for |
| 64ms and then dividing the count by 64, so a raw count of |
| 64 will get the minimum delay: */ |
| delay_factor = 64; |
| } |
| |
| /* if the PROM delay factor correction is "best": */ |
| if (delay_factor == TME_SPARC_PROM_DELAY_FACTOR_BEST) { |
| |
| /* modify the "inc %l2" to be "inccc %l2": */ |
| insn = 0xa484a001; |
| } |
| |
| /* otherwise, the PROM delay factor is given directly: */ |
| else { |
| |
| /* modify the "inc %l2" to be "or %g0, simm13, %l2": */ |
| insn = 0xa4102000 + TME_MIN(delay_factor, 0xfff); |
| } |
| |
| /* get the current instruction TLB entry: */ |
| /* NB: we don't repeat the assert()s or the address-covering |
| checks of tme_sparc_fetch_nearby(): */ |
| itlb_current = tme_sparc_itlb_current(ic); |
| |
| /* modify the "inc %l2": */ |
| /* NB: we break const here: */ |
| ((tme_shared tme_uint32_t *) |
| (unsigned long) |
| (itlb_current->tme_sparc_tlb_emulator_off_read |
| + ic->tme_sparc_ireg_uint64(TME_SPARC_IREG_PC))) |
| [offset_in_insns + 1] = tme_htobe_u32(insn); |
| |
| /* NB: we don't invalidate the memory page that we just modified |
| (in the tme_sparc_recode_cacheable_valids sense), because we |
| assume that the loop that calculates the delay factor hasn't |
| been recoded yet, and we assume that no other CPUs or other |
| bus masters care about the modification: */ |
| } |
| } |
| } |
| |
| /* do the normal rdpr: */ |
| tme_sparc64_rdpr(ic, _rs1, _rs2, _rd); |
| } |
| |
| /* the stp103x wrasr: */ |
| static |
| TME_SPARC_FORMAT3(_tme_stp103x_wrasr, tme_uint64_t) |
| { |
| tme_uint64_t value_xor; |
| unsigned int reg_rd; |
| tme_uint64_t tick; |
| struct timeval tick_compare_time; |
| tme_uint64_t cycles_scaled; |
| tme_uint64_t usec64; |
| tme_uint32_t usec32; |
| |
| /* if this is an implementation-specific ASR: */ |
| if (TME_SPARC_INSN & (0x10 << 25)) { |
| |
| /* get the value to write: */ |
| value_xor = TME_SPARC_FORMAT3_RS1 ^ TME_SPARC_FORMAT3_RS2; |
| |
| /* get rd: */ |
| reg_rd = TME_FIELD_MASK_EXTRACTU(TME_SPARC_INSN, TME_SPARC_FORMAT3_MASK_RD); |
| |
| /* if this is an undefined implementation-specific ASR: */ |
| if (__tme_predict_false(reg_rd >= 0x18)) { |
| TME_SPARC_INSN_ILL(ic); |
| } |
| |
| /* if this is an write to GSR: */ |
| if (reg_rd == 0x13) { |
| if (__tme_predict_false((ic->tme_sparc64_ireg_pstate & TME_SPARC64_PSTATE_PEF) == 0 |
| || (ic->tme_sparc64_ireg_fprs & TME_SPARC64_FPRS_FEF) == 0)) { |
| tme_sparc64_trap(ic, TME_SPARC64_TRAP_fp_disabled); |
| } |
| ic->tme_sparc_vis_gsr = value_xor; |
| } |
| |
| /* otherwise, this is not a write to GSR: */ |
| else { |
| |
| /* if this is a nonprivileged write: */ |
| if (__tme_predict_false(!TME_SPARC_PRIV(ic))) { |
| |
| /* if this is not a write to PIC, or if PCR.PRIV is set: */ |
| if (reg_rd != 0x11 |
| || (TME_STP103X(ic)->tme_stp103x_pcr & TME_BIT(0))) { |
| tme_sparc64_trap(ic, TME_SPARC64_TRAP_privileged_action); |
| } |
| } |
| |
| /* dispatch on rd: */ |
| switch (reg_rd) { |
| default: assert (FALSE); |
| case 0x10: TME_STP103X(ic)->tme_stp103x_pcr = value_xor; break; |
| case 0x11: TME_STP103X(ic)->tme_stp103x_pic.tme_value64_uint = value_xor; break; |
| case 0x12: TME_STP103X(ic)->tme_stp103x_dcr = value_xor; break; |
| case 0x14: _tme_stp103x_update_sir(ic, 0, value_xor); break; |
| case 0x15: _tme_stp103x_update_sir(ic, value_xor, 0); break; |
| case 0x16: _tme_stp103x_update_sir(ic, 0xffff, value_xor); break; |
| |
| case 0x17: |
| TME_STP103X(ic)->tme_stp103x_tcr = value_xor; |
| |
| /* if we're not replaying instructions: */ |
| if (tme_sparc_recode_verify_replay_last_pc(ic) == 0) { |
| |
| /* if INT_DIS is set: */ |
| if (__tme_predict_false(value_xor & TME_STP103X_TCR_INT_DIS)) { |
| |
| /* the tick compare time is never: */ |
| tme_misc_timeval_never(&tick_compare_time); |
| } |
| |
| /* otherwise, INT_DIS is clear: */ |
| else { |
| |
| /* get the current value of TICK.counter: */ |
| tick = tme_misc_cycles_scaled(&ic->tme_sparc_cycles_scaling, 0).tme_value64_uint; |
| tick += ic->tme_sparc64_ireg_tick_offset; |
| tick &= TME_SPARC64_TICK_COUNTER; |
| |
| /* get the current time: */ |
| gettimeofday(&tick_compare_time, NULL); |
| |
| /* get the number of cycles until the compare value is |
| reached: */ |
| cycles_scaled = TME_STP103X(ic)->tme_stp103x_tcr - tick; |
| cycles_scaled &= TME_SPARC64_TICK_COUNTER; |
| |
| /* if the number of cycles doesn't fit in 32 bits: */ |
| if (__tme_predict_false(cycles_scaled > (tme_uint32_t) (0 - (tme_uint32_t) 1))) { |
| |
| /* convert cycles into microseconds: */ |
| usec64 = cycles_scaled / ic->tme_sparc_cycles_scaled_per_usec; |
| |
| /* add in the whole seconds: */ |
| tick_compare_time.tv_sec += (usec64 / 1000000); |
| |
| /* get the remaining microseconds: */ |
| usec32 = (usec64 % 1000000); |
| } |
| |
| /* otherwise, the number of cycles fits in 32 bits: */ |
| else { |
| |
| /* convert cycles into microseconds: */ |
| usec32 = ((tme_uint32_t) cycles_scaled) / ic->tme_sparc_cycles_scaled_per_usec; |
| |
| /* if there is at least one whole second: */ |
| if (__tme_predict_false(usec32 >= 1000000)) { |
| |
| /* add in the whole seconds: */ |
| tick_compare_time.tv_sec += (usec32 / 1000000); |
| |
| /* get the remaining microseconds: */ |
| usec32 %= 1000000; |
| } |
| } |
| |
| /* add in the microseconds: */ |
| usec32 += tick_compare_time.tv_usec; |
| if (usec32 >= 1000000) { |
| tick_compare_time.tv_sec++; |
| usec32 -= 1000000; |
| } |
| tick_compare_time.tv_usec = usec32; |
| } |
| |
| /* lock the external mutex: */ |
| tme_mutex_lock(&ic->tme_sparc_external_mutex); |
| |
| /* set the tick compare time: */ |
| TME_STP103X(ic)->tme_stp103x_tick_compare_time = tick_compare_time; |
| |
| /* notify the tick compare thread: */ |
| tme_cond_notify(&TME_STP103X(ic)->tme_stp103x_tick_compare_cond, FALSE); |
| |
| /* unlock the external mutex: */ |
| tme_mutex_unlock(&ic->tme_sparc_external_mutex); |
| } |
| break; |
| } |
| } |
| TME_SPARC_INSN_OK; |
| } |
| tme_sparc64_wrasr(ic, _rs1, _rs2, _rd); |
| } |
| |
| /* the stp103x impdep1: */ |
| static |
| TME_SPARC_FORMAT3(_tme_stp103x_impdep1, tme_uint64_t) |
| { |
| tme_uint32_t opf; |
| tme_uint64_t rd; |
| unsigned int alignaddr_off; |
| |
| /* extract the opf field: */ |
| opf = TME_FIELD_MASK_EXTRACTU(TME_SPARC_INSN, (0x1ff << 5)); |
| |
| /* if this is a shutdown instruction: */ |
| if (opf == 0x80) { |
| TME_SPARC_INSN_PRIV; |
| |
| /* XXX WRITEME: */ |
| abort(); |
| } |
| |
| /* if this is the VIS alignaddr instruction: */ |
| if ((opf | TME_BIT(1)) == 0x1a) { |
| |
| /* add the two registers into an address: */ |
| rd = TME_SPARC_FORMAT3_RS1 + TME_SPARC_FORMAT3_RS2; |
| |
| /* store the alignaddr offset: */ |
| alignaddr_off |
| = (((opf & TME_BIT(1)) |
| ? (sizeof(tme_uint64_t) - 1) |
| : 0) |
| ^ rd); |
| TME_FIELD_MASK_DEPOSITU(ic->tme_sparc_vis_gsr, |
| TME_SPARC_VIS_GSR_ALIGNADDR_OFF, |
| alignaddr_off); |
| |
| /* truncate the address: */ |
| TME_SPARC_FORMAT3_RD = rd & (0 - (tme_uint64_t) sizeof(tme_uint64_t)); |
| |
| TME_SPARC_INSN_OK; |
| } |
| |
| /* otherwise, assume that this is some other VIS instruction: */ |
| tme_sparc_vis(ic); |
| TME_SPARC_INSN_OK; |
| } |
| |
| /* the stp103x impdep2: */ |
| static |
| TME_SPARC_FORMAT3(_tme_stp103x_impdep2, tme_uint64_t) |
| { |
| TME_SPARC_INSN_ILL(ic); |
| } |
| |
| /* the stp103x flush: */ |
| static |
| TME_SPARC_FORMAT3(_tme_stp103x_flush, tme_uint64_t) |
| { |
| tme_uint64_t address; |
| tme_uint32_t context; |
| struct tme_sparc_tlb *dtlb; |
| tme_uint64_t rd; |
| |
| /* if we're replaying instructions, return now: */ |
| if (tme_sparc_recode_verify_replay_last_pc(ic) != 0) { |
| TME_SPARC_INSN_OK; |
| } |
| |
| /* get the address: */ |
| address = TME_SPARC_FORMAT3_RS1 + TME_SPARC_FORMAT3_RS2; |
| address &= ic->tme_sparc_address_mask; |
| |
| /* get the context: */ |
| context = ic->tme_sparc_memory_context_default; |
| |
| /* get and busy the DTLB entry: */ |
| dtlb = &ic->tme_sparc_tlbs[TME_SPARC_DTLB_ENTRY(ic, TME_SPARC_TLB_HASH(ic, context, address))]; |
| tme_sparc_tlb_busy(dtlb); |
| |
| /* a flush instruction behaves like a non-faulting load: */ |
| tme_sparc64_ls(ic, |
| address, |
| &rd, |
| (TME_SPARC_LSINFO_OP_LD |
| + TME_SPARC_LSINFO_ASI_FLAGS(TME_SPARC64_ASI_FLAG_NO_FAULT) |
| + sizeof(tme_uint8_t))); |
| |
| /* unbusy the DTLB entry: */ |
| tme_sparc_tlb_unbusy(dtlb); |
| |
| TME_SPARC_INSN_OK; |
| } |
| |
| /* the format three opcode map: */ |
| #define _TME_SPARC_EXECUTE_OPMAP tme_sparc_opcodes_stp103x |
| static const _tme_sparc64_format3 _TME_SPARC_EXECUTE_OPMAP[] = { |
| |
| /* op=2: arithmetic, logical, shift, and remaining: */ |
| |
| /* 000000 */ tme_sparc64_add, |
| /* 000001 */ tme_sparc64_and, |
| /* 000010 */ tme_sparc64_or, |
| /* 000011 */ tme_sparc64_xor, |
| /* 000100 */ tme_sparc64_sub, |
| /* 000101 */ tme_sparc64_andn, |
| /* 000110 */ tme_sparc64_orn, |
| /* 000111 */ tme_sparc64_xnor, |
| /* 001000 */ tme_sparc64_addx, |
| /* 001001 */ tme_sparc64_mulx, |
| /* 001010 */ tme_sparc64_umul, |
| /* 001011 */ tme_sparc64_smul, |
| /* 001100 */ tme_sparc64_subx, |
| /* 001101 */ tme_sparc64_udivx, |
| /* 001110 */ tme_sparc64_udiv, |
| /* 001111 */ tme_sparc64_sdiv, |
| /* 010000 */ tme_sparc64_addcc, |
| /* 010001 */ tme_sparc64_andcc, |
| /* 010010 */ tme_sparc64_orcc, |
| /* 010011 */ tme_sparc64_xorcc, |
| /* 010100 */ tme_sparc64_subcc, |
| /* 010101 */ tme_sparc64_andncc, |
| /* 010110 */ tme_sparc64_orncc, |
| /* 010111 */ tme_sparc64_xnorcc, |
| /* 011000 */ tme_sparc64_addxcc, |
| /* 011001 */ NULL, |
| /* 011010 */ tme_sparc64_umulcc, |
| /* 011011 */ tme_sparc64_smulcc, |
| /* 011100 */ tme_sparc64_subxcc, |
| /* 011101 */ NULL, |
| /* 011110 */ tme_sparc64_udivcc, |
| /* 011111 */ tme_sparc64_sdivcc, |
| /* 100000 */ tme_sparc64_taddcc, |
| /* 100001 */ tme_sparc64_tsubcc, |
| /* 100010 */ tme_sparc64_taddcctv, |
| /* 100011 */ tme_sparc64_tsubcctv, |
| /* 100100 */ tme_sparc64_mulscc, |
| /* 100101 */ tme_sparc64_sll, |
| /* 100110 */ tme_sparc64_srl, |
| /* 100111 */ tme_sparc64_sra, |
| /* 101000 */ _tme_stp103x_rdasr, |
| /* 101001 */ NULL, |
| /* 101010 */ _tme_stp103x_rdpr, |
| /* 101011 */ tme_sparc64_flushw, |
| /* 101100 */ tme_sparc64_movcc, |
| /* 101101 */ tme_sparc64_sdivx, |
| /* 101110 */ NULL, |
| /* 101111 */ tme_sparc64_movr, |
| /* 110000 */ _tme_stp103x_wrasr, |
| /* 110001 */ tme_sparc64_saved_restored, |
| /* 110010 */ tme_sparc64_wrpr, |
| /* 110011 */ NULL, |
| /* 110100 */ tme_sparc64_fpop1, |
| /* 110101 */ tme_sparc64_fpop2, |
| /* 110110 */ _tme_stp103x_impdep1, |
| /* 110111 */ _tme_stp103x_impdep2, |
| /* 111000 */ tme_sparc64_jmpl, |
| /* 111001 */ tme_sparc64_return, |
| /* 111010 */ tme_sparc64_tcc, |
| /* 111011 */ _tme_stp103x_flush, |
| /* 111100 */ tme_sparc64_save_restore, |
| /* 111101 */ tme_sparc64_save_restore, |
| /* 111110 */ tme_sparc64_done_retry, |
| /* 111111 */ NULL, |
| |
| /* op=3: memory instructions: */ |
| |
| /* 000000 */ tme_sparc64_ld, |
| /* 000001 */ tme_sparc64_ldb, |
| /* 000010 */ tme_sparc64_ldh, |
| /* 000011 */ tme_sparc64_ldd, |
| /* 000100 */ tme_sparc64_st, |
| /* 000101 */ tme_sparc64_stb, |
| /* 000110 */ tme_sparc64_sth, |
| /* 000111 */ tme_sparc64_std, |
| /* 001000 */ tme_sparc64_ld, |
| /* 001001 */ tme_sparc64_ldb, |
| /* 001010 */ tme_sparc64_ldh, |
| /* 001011 */ tme_sparc64_ldx, |
| /* 001100 */ NULL, |
| /* 001101 */ tme_sparc64_ldstub, |
| /* 001110 */ tme_sparc64_stx, |
| /* 001111 */ tme_sparc64_swap, |
| /* 010000 */ tme_sparc64_lda, |
| /* 010001 */ tme_sparc64_ldba, |
| /* 010010 */ tme_sparc64_ldha, |
| /* 010011 */ tme_sparc64_ldda, |
| /* 010100 */ tme_sparc64_sta, |
| /* 010101 */ tme_sparc64_stba, |
| /* 010110 */ tme_sparc64_stha, |
| /* 010111 */ tme_sparc64_stda, |
| /* 011000 */ tme_sparc64_lda, |
| /* 011001 */ tme_sparc64_ldba, |
| /* 011010 */ tme_sparc64_ldha, |
| /* 011011 */ tme_sparc64_ldxa, |
| /* 011100 */ NULL, |
| /* 011101 */ tme_sparc64_ldstuba, |
| /* 011110 */ tme_sparc64_stxa, |
| /* 011111 */ tme_sparc64_swapa, |
| /* 100000 */ tme_sparc64_ldf, |
| /* 100001 */ tme_sparc64_ldfsr, |
| /* 100010 */ tme_sparc64_illegal_instruction, /* ldqf */ |
| /* 100011 */ tme_sparc64_lddf, |
| /* 100100 */ tme_sparc64_stf, |
| /* 100101 */ tme_sparc64_stfsr, |
| /* 100110 */ tme_sparc64_illegal_instruction, /* stqf */ |
| /* 100111 */ tme_sparc64_stdf, |
| /* 101000 */ NULL, |
| /* 101001 */ NULL, |
| /* 101010 */ NULL, |
| /* 101011 */ NULL, |
| /* 101100 */ NULL, |
| /* 101101 */ tme_sparc64_prefetch, |
| /* 101110 */ NULL, |
| /* 101111 */ NULL, |
| /* 110000 */ tme_sparc64_ldfa, |
| /* 110001 */ NULL, |
| /* 110010 */ tme_sparc64_illegal_instruction, /* ldqfa */ |
| /* 110011 */ tme_sparc64_lddfa, |
| /* 110100 */ tme_sparc64_stfa, |
| /* 110101 */ NULL, |
| /* 110110 */ tme_sparc64_illegal_instruction, /* stqfa */ |
| /* 110111 */ tme_sparc64_stdfa, |
| /* 111000 */ NULL, |
| /* 111001 */ NULL, |
| /* 111010 */ NULL, |
| /* 111011 */ NULL, |
| /* 111100 */ tme_sparc64_casa, |
| /* 111101 */ tme_sparc64_prefetch, |
| /* 111110 */ tme_sparc64_casxa, |
| /* 111111 */ NULL, |
| }; |
| |
| /* make the executor for the STP103x: */ |
| #define _TME_SPARC_EXECUTE_NAME _tme_sparc_execute_stp103x |
| #include "sparc-execute.c" |
| |
| /* this invalidates a specific valid stp103x TLB entry: */ |
| static void |
| _tme_stp103x_tlb_invalidate(struct tme_sparc *ic, |
| signed long tlb_part_i) |
| { |
| tme_uint32_t tlb_data_32_63; |
| struct tme_sparc_tlb *tlb; |
| tme_uint32_t tlb_count; |
| tme_uint32_t size; |
| tme_uint64_t address; |
| tme_uint32_t context; |
| |
| /* load bits 32..63 of the TLB entry's data: */ |
| tlb_data_32_63 = TME_STP103X(ic)->tme_stp103x_tlb_32s((tlb_part_i + 1), 1); |
| |
| /* this TLB entry must be valid: */ |
| assert (tlb_data_32_63 & TME_STP103X_TLB_DATA_V(tlb_data_32_63)); |
| |
| /* this TLB entry is now invalid: */ |
| tlb_data_32_63 &= ~TME_STP103X_TLB_DATA_V(tlb_data_32_63); |
| TME_STP103X(ic)->tme_stp103x_tlb_32s((tlb_part_i + 1), 1) = tlb_data_32_63; |
| |
| /* if this TLB entry is global: */ |
| if (TME_STP103X(ic)->tme_stp103x_tlb_32s((tlb_part_i + 1), 0) |
| & TME_STP103X_TLB_DATA_G((tme_uint32_t) 0)) { |
| |
| /* assume that this is an stp103x DTLB entry, and we need to |
| invalidate sparc DTLB entries: */ |
| tlb = &ic->tme_sparc_tlbs[0]; |
| tlb_count = _TME_SPARC_DTLB_HASH_SIZE; |
| |
| /* if this is an stp103x ITLB entry: */ |
| #if TME_STP103X_TLB_PART_0_DMMU >= TME_STP103X_TLB_PART_0_IMMU |
| #error "TME_STP103X_TLB_PART_0_DMMU or TME_STP103X_TLB_PART_0_IMMU changed" |
| #endif |
| if (tlb_part_i >= TME_STP103X_TLB_PART_0_IMMU) { |
| |
| /* we need to invalidate sparc ITLB entries: */ |
| tlb += _TME_SPARC_DTLB_HASH_SIZE; |
| tlb_count = _TME_SPARC_ITLB_HASH_SIZE; |
| } |
| |
| /* invalidate all of the sparc TLB entries linked to this stp103x |
| TLB entry: */ |
| do { |
| if (tlb->tme_sparc_tlb_link == tlb_part_i) { |
| tme_token_invalidate_nosync(tlb->tme_sparc_tlb_bus_tlb.tme_bus_tlb_token); |
| } |
| tlb++; |
| } while (--tlb_count); |
| } |
| |
| /* otherwise, this TLB entry isn't global: */ |
| else { |
| |
| /* get the size of this mapping: */ |
| size |
| = (TME_STP103X_PAGE_SIZE_8KB |
| << (3 * TME_FIELD_MASK_EXTRACTU(tlb_data_32_63, |
| TME_STP103X_TLB_DATA_SIZE_MASK(tlb_data_32_63)))); |
| |
| /* get the tag, and divide it into context and address: */ |
| address = TME_STP103X(ic)->tme_stp103x_tlb_64s(tlb_part_i + 0); |
| context = address; |
| context &= TME_STP103X_CONTEXT_MAX; |
| address &= (0 - (tme_uint64_t) size); |
| |
| /* we assume that a stride of 8KB will find all sparc TLB entries |
| for this stp103x TLB entry: */ |
| assert ((1 << ic->tme_sparc_tlb_page_size_log2) == TME_STP103X_PAGE_SIZE_8KB); |
| |
| /* if this is an stp103x DTLB entry: */ |
| #if TME_STP103X_TLB_PART_0_DMMU >= TME_STP103X_TLB_PART_0_IMMU |
| #error "TME_STP103X_TLB_PART_0_DMMU or TME_STP103X_TLB_PART_0_IMMU changed" |
| #endif |
| if (tlb_part_i < TME_STP103X_TLB_PART_0_IMMU) { |
| |
| /* invalidate all of the sparc DTLB entries linked to this |
| stp103x DTLB entry: */ |
| do { |
| tlb = &ic->tme_sparc_tlbs[TME_SPARC_DTLB_ENTRY(ic, TME_SPARC_TLB_HASH(ic, context, address))]; |
| if (tlb->tme_sparc_tlb_link == tlb_part_i) { |
| tme_token_invalidate_nosync(tlb->tme_sparc_tlb_bus_tlb.tme_bus_tlb_token); |
| } |
| address += TME_STP103X_PAGE_SIZE_8KB; |
| } while ((size -= TME_STP103X_PAGE_SIZE_8KB) > 0); |
| } |
| |
| /* otherwise, this is an stp103x ITLB entry: */ |
| else { |
| |
| /* invalidate all of the sparc ITLB entries linked to this |
| stp103x ITLB entry: */ |
| do { |
| tlb = &ic->tme_sparc_tlbs[TME_SPARC_ITLB_ENTRY(ic, TME_SPARC_TLB_HASH(ic, context, address))]; |
| if (tlb->tme_sparc_tlb_link == tlb_part_i) { |
| tme_token_invalidate_nosync(tlb->tme_sparc_tlb_bus_tlb.tme_bus_tlb_token); |
| } |
| address += TME_STP103X_PAGE_SIZE_8KB; |
| } while ((size -= TME_STP103X_PAGE_SIZE_8KB) > 0); |
| } |
| } |
| } |
| |
| /* this handles a load/store trap: */ |
| static void |
| _tme_stp103x_ls_trap(struct tme_sparc *ic, |
| struct tme_sparc_ls *ls) |
| { |
| tme_uint32_t lsinfo; |
| tme_uint32_t sfsr; |
| tme_uint32_t ls_faults; |
| tme_uint32_t trap; |
| tme_uint32_t updates; |
| tme_uint32_t insn; |
| tme_uint64_t afsr; |
| tme_uint64_t address; |
| tme_uint32_t asi_mask; |
| struct tme_stp103x_mmu *mmu; |
| |
| /* get the information about this load/store: */ |
| lsinfo = ls->tme_sparc_ls_lsinfo; |
| |
| /* check that we weren't supposed to fault: */ |
| assert ((lsinfo & TME_STP103X_LSINFO_ASSERT_NO_FAULTS) == 0); |
| |
| /* if we're supposed to ignore faults: */ |
| if (__tme_predict_false(lsinfo & TME_SPARC_LSINFO_NO_FAULT)) { |
| |
| /* clear all faults and complete the load/store: */ |
| ls->tme_sparc_ls_faults = TME_SPARC_LS_FAULT_NONE; |
| ls->tme_sparc_ls_size = 0; |
| return; |
| } |
| |
| /* start an SFSR value: */ |
| sfsr = 0; |
| |
| /* get the list of faults from this load/store: */ |
| ls_faults = ls->tme_sparc_ls_faults; |
| |
| /* there must be at least one fault: */ |
| assert (ls_faults != TME_SPARC_LS_FAULT_NONE); |
| |
| /* we don't support generic bus faults: */ |
| assert ((ls_faults |
| & (TME_SPARC_LS_FAULT_BUS_FAULT)) == 0); |
| |
| /* if this is an IMMU miss, this must be the only fault. we rely on |
| this to honor trap priorities, since we handle IMMU misses and |
| DMMU misses as if they have the same priority, even though they |
| don't: */ |
| assert ((ls_faults & TME_STP103X_LS_FAULT_MMU_MISS) == 0 |
| || (lsinfo & TME_SPARC_LSINFO_OP_FETCH) == 0 |
| || ls_faults == TME_STP103X_LS_FAULT_MMU_MISS); |
| |
| /* if this is a instruction access error, it must happen alone. we |
| rely on this to honor trap priorities, since we handle |
| instruction access errors and data access errors as if they have |
| the same priority, even though they don't: */ |
| assert ((ls_faults & TME_SPARC_LS_FAULT_BUS_ERROR) == 0 |
| || (lsinfo & TME_SPARC_LSINFO_OP_FETCH) == 0 |
| || ls_faults == TME_SPARC_LS_FAULT_BUS_ERROR); |
| |
| /* if this is an instruction access exception, it either happens |
| alone (because of a privilege violation), or it's because of a |
| jmpl/return target address out-of-range. we rely on this to |
| honor trap priorities, since we handle privilege violation |
| instruction access exceptions and data access exceptions as if |
| they have the same priority, even though they don't. (a |
| jmpl/return target instruction access exception explicitly has |
| the same priority as a data access exception): */ |
| assert ((ls_faults |
| & (TME_STP103X_LS_FAULT_PRIVILEGE |
| | TME_SPARC64_LS_FAULT_SIDE_EFFECTS |
| | TME_SPARC64_LS_FAULT_UNCACHEABLE |
| | TME_SPARC64_LS_FAULT_NO_FAULT_NON_LOAD |
| | TME_STP103X_LS_FAULT_ILLEGAL |
| | TME_SPARC64_LS_FAULT_NO_FAULT_FAULT |
| | TME_SPARC64_LS_FAULT_VA_RANGE |
| | TME_SPARC64_LS_FAULT_VA_RANGE_NNPC)) == 0 |
| || (lsinfo & TME_SPARC_LSINFO_OP_FETCH) == 0 |
| || ls_faults == TME_STP103X_LS_FAULT_PRIVILEGE |
| || ls_faults == TME_SPARC64_LS_FAULT_VA_RANGE_NNPC); |
| |
| /* these traps are sorted by priority, most important first: */ |
| |
| /* if this was an illegal instruction: */ |
| if (ls_faults |
| & (TME_SPARC_LS_FAULT_LDD_STD_RD_ODD)) { |
| |
| /* make an illegal_instruction trap, which doesn't update any MMU |
| registers: */ |
| trap = TME_SPARC64_TRAP_illegal_instruction; |
| updates = TME_STP103X_UPDATE_NONE; |
| } |
| |
| /* otherwise, if the address isn't aligned: */ |
| else if (ls_faults & TME_SPARC_LS_FAULT_ADDRESS_NOT_ALIGNED) { |
| |
| /* assume that this is not an lddf or stdf instruction, or that |
| the address is not even 32-bit aligned, and make the normal |
| mem_address_not_aligned trap: */ |
| trap = TME_SPARC64_TRAP_mem_address_not_aligned; |
| |
| /* if this is an lddf or stdf instruction: */ |
| insn = ic->_tme_sparc_insn; |
| if ((insn |
| & (0x3b << 19)) |
| == (0x23 << 19)) { |
| |
| /* if the address is 32-bit aligned, but not 64-bit aligned: */ |
| if ((ls->tme_sparc_ls_address64 % sizeof(tme_uint64_t)) |
| == sizeof(tme_uint32_t)) { |
| |
| /* make the LDDF_mem_address_not_aligned or |
| STDF_mem_address_not_aligned trap: */ |
| trap |
| = ((insn & (4 << 19)) |
| ? TME_SPARC64_TRAP_STDF_mem_address_not_aligned |
| : TME_SPARC64_TRAP_LDDF_mem_address_not_aligned); |
| } |
| } |
| |
| /* these traps update the DMMU SFSR and SFAR: */ |
| updates |
| = (TME_STP103X_UPDATE_DMMU |
| + TME_STP103X_UPDATE_MMU_SFSR |
| + TME_STP103X_UPDATE_DMMU_SFAR); |
| } |
| |
| /* otherwise, if the ASI is privileged: */ |
| else if (ls_faults & TME_SPARC64_LS_FAULT_PRIVILEGED_ASI) { |
| |
| /* make a privileged_action trap, which updates the DMMU SFSR and |
| SFAR: */ |
| trap = TME_SPARC64_TRAP_privileged_action; |
| updates |
| = (TME_STP103X_UPDATE_DMMU |
| + TME_STP103X_UPDATE_MMU_SFSR |
| + TME_STP103X_UPDATE_DMMU_SFAR); |
| } |
| |
| /* otherwise, if this is an access exception: */ |
| else if (ls_faults |
| & (TME_STP103X_LS_FAULT_PRIVILEGE |
| | TME_SPARC64_LS_FAULT_SIDE_EFFECTS |
| | TME_SPARC64_LS_FAULT_UNCACHEABLE |
| | TME_SPARC64_LS_FAULT_NO_FAULT_NON_LOAD |
| | TME_STP103X_LS_FAULT_ILLEGAL |
| | TME_SPARC64_LS_FAULT_NO_FAULT_FAULT |
| | TME_SPARC64_LS_FAULT_VA_RANGE |
| | TME_SPARC64_LS_FAULT_VA_RANGE_NNPC)) { |
| |
| /* make the SFSR FT field: */ |
| if (ls_faults & TME_STP103X_LS_FAULT_PRIVILEGE) { |
| sfsr += TME_STP103X_SFSR_FT_PRIVILEGE; |
| } |
| if (ls_faults & TME_SPARC64_LS_FAULT_SIDE_EFFECTS) { |
| sfsr += TME_STP103X_SFSR_FT_SIDE_EFFECTS; |
| } |
| if (ls_faults & TME_SPARC64_LS_FAULT_UNCACHEABLE) { |
| /* XXX FIXME WRITEME table 6-11 hints that it's possible to do a |
| casxa to DTLB_DATA_ACCESS_REG, and that a fault while doing |
| so will set this bit: */ |
| sfsr += TME_STP103X_SFSR_FT_UNCACHEABLE; |
| } |
| if (ls_faults & |
| (TME_SPARC64_LS_FAULT_NO_FAULT_NON_LOAD |
| | TME_STP103X_LS_FAULT_ILLEGAL)) { |
| sfsr += TME_STP103X_SFSR_FT_ILLEGAL; |
| } |
| if (ls_faults & TME_SPARC64_LS_FAULT_NO_FAULT_FAULT) { |
| sfsr += TME_STP103X_SFSR_FT_NO_FAULT_FAULT; |
| } |
| if (ls_faults & TME_SPARC64_LS_FAULT_VA_RANGE) { |
| sfsr += TME_STP103X_SFSR_FT_VA_RANGE; |
| } |
| if (ls_faults & TME_SPARC64_LS_FAULT_VA_RANGE_NNPC) { |
| sfsr += TME_STP103X_SFSR_FT_VA_RANGE_NNPC; |
| } |
| |
| /* if this is an instruction fetch: */ |
| if (lsinfo & TME_SPARC_LSINFO_OP_FETCH) { |
| |
| /* the IMMU SFSR FT field must have exactly one of the privilege |
| and VA range bits set: */ |
| assert (sfsr != 0 |
| && (sfsr |
| & ~(TME_STP103X_SFSR_FT_PRIVILEGE |
| | TME_STP103X_SFSR_FT_VA_RANGE |
| | TME_STP103X_SFSR_FT_VA_RANGE_NNPC)) == 0 |
| && (sfsr & (sfsr - 1)) == 0); |
| |
| /* make an instruction_access_exception trap, which updates the |
| IMMU SFSR and tag access register: */ |
| trap = (TME_STP103X_TRAP_MG | TME_SPARC64_TRAP_instruction_access_exception); |
| updates |
| = (TME_STP103X_UPDATE_IMMU |
| + TME_STP103X_UPDATE_MMU_SFSR |
| + TME_STP103X_UPDATE_MMU_TAG_ACCESS); |
| } |
| |
| /* otherwise, this is not an instruction fetch: */ |
| else { |
| |
| /* make a data_access_exception trap, which updates the DMMU |
| SFSR, SFAR, and tag access register: */ |
| trap = (TME_STP103X_TRAP_MG | TME_SPARC64_TRAP_data_access_exception); |
| updates |
| = (TME_STP103X_UPDATE_DMMU |
| + TME_STP103X_UPDATE_MMU_SFSR |
| + TME_STP103X_UPDATE_DMMU_SFAR |
| + TME_STP103X_UPDATE_MMU_TAG_ACCESS); |
| } |
| } |
| |
| /* otherwise, if this is an MMU miss: */ |
| else if (ls_faults & TME_STP103X_LS_FAULT_MMU_MISS) { |
| |
| /* if this is an instruction fetch: */ |
| if (lsinfo & TME_SPARC_LSINFO_OP_FETCH) { |
| |
| /* make an instruction_access_MMU_miss trap, which updates the |
| IMMU tag access register: */ |
| trap = TME_STP103X_TRAP_fast_instruction_access_MMU_miss; |
| updates |
| = (TME_STP103X_UPDATE_IMMU |
| + TME_STP103X_UPDATE_MMU_TAG_ACCESS); |
| } |
| |
| /* otherwise, this is not an instruction fetch: */ |
| else { |
| |
| /* make a data_access_MMU_miss trap, which updates the DMMU tag |
| access: */ |
| trap = TME_STP103X_TRAP_fast_data_access_MMU_miss; |
| updates |
| = (TME_STP103X_UPDATE_DMMU |
| + TME_STP103X_UPDATE_MMU_TAG_ACCESS); |
| } |
| } |
| |
| /* otherwise, if this is a data access protection fault: */ |
| else if (ls_faults & TME_STP103X_LS_FAULT_PROTECTION) { |
| |
| /* make a fast_data_access_protection trap, which updates the |
| DMMU SFSR, SFAR, and tag access register: */ |
| trap = TME_STP103X_TRAP_fast_data_access_protection; |
| updates |
| = (TME_STP103X_UPDATE_DMMU |
| + TME_STP103X_UPDATE_MMU_SFSR |
| + TME_STP103X_UPDATE_DMMU_SFAR |
| + TME_STP103X_UPDATE_MMU_TAG_ACCESS); |
| } |
| |
| /* otherwise, this must be an access error fault: */ |
| else { |
| assert (ls_faults & TME_SPARC_LS_FAULT_BUS_ERROR); |
| |
| /* get the asynchronous fault status register bit for this |
| fault: */ |
| /* NB: we assume that all bus errors are timeouts: */ |
| afsr = TME_STP103X_AFSR_TO; |
| |
| /* if no same or higher-priority asynchronous fault has already |
| happened: */ |
| if ((TME_STP103X(ic)->tme_stp103x_afsr |
| & (TME_STP103X_AFSR_ME - afsr)) == 0) { |
| |
| /* update the asynchronous fault address register: */ |
| assert (ls->tme_sparc_ls_tlb->tme_sparc_tlb_addr_shift == 0); |
| TME_STP103X(ic)->tme_stp103x_afar |
| = ((ls->tme_sparc_ls_address64 |
| + (tme_uint64_t) ls->tme_sparc_ls_tlb->tme_sparc_tlb_addr_offset) |
| & (0 - (tme_uint64_t) (1 << 4))); |
| } |
| |
| /* update the asynchronous fault status register: */ |
| TME_STP103X(ic)->tme_stp103x_afsr |
| |= (((TME_STP103X(ic)->tme_stp103x_afsr |
| & afsr) |
| ? TME_STP103X_AFSR_ME |
| : !TME_STP103X_AFSR_ME) |
| + (TME_SPARC_PRIV(ic) |
| ? TME_STP103X_AFSR_PRIV |
| : !TME_STP103X_AFSR_PRIV)); |
| |
| /* if noncacheable errors are not enabled: */ |
| if ((TME_STP103X(ic)->tme_stp103x_estate_error_enable & TME_STP103X_ESTATE_ERROR_ENABLE_NCEEN) == 0) { |
| |
| /* clear the fault: */ |
| ls->tme_sparc_ls_faults = TME_SPARC_LS_FAULT_NONE; |
| |
| /* if this is a load: */ |
| if (ls->tme_sparc_ls_lsinfo & TME_SPARC_LSINFO_OP_LD) { |
| |
| /* force all bytes read to be all-bits-one by filling the |
| entire memory buffer with all-bits-one: */ |
| /* NB: we do this even though earlier parts of the load may |
| have succeeded, to keep things simple. we assume that |
| nothing depends on partially successful loads: */ |
| memset (ic->tme_sparc_memory_buffer.tme_sparc_memory_buffer8s, |
| 0xff, |
| sizeof(ic->tme_sparc_memory_buffer.tme_sparc_memory_buffer8s)); |
| } |
| |
| /* stop this load or store: */ |
| /* NB: we do this even though later parts of the load or store |
| may succeed, to keep things simple. we assume that nothing |
| depends on partially successful loads or stores: */ |
| /* NB: we don't set TME_SPARC_LSINFO_LD_COMPLETED because we are |
| only stopping the load or store here, not completing it. we |
| still let the original load instruction function complete the |
| load from the memory buffer: */ |
| ls->tme_sparc_ls_size = 0; |
| |
| /* make no trap: */ |
| trap = TME_SPARC_TRAP_none; |
| } |
| |
| /* otherwise, if this is an instruction fetch: */ |
| else if (lsinfo & TME_SPARC_LSINFO_OP_FETCH) { |
| |
| /* make an instruction_access_error trap: */ |
| trap = TME_SPARC64_TRAP_instruction_access_error; |
| } |
| |
| /* otherwise, this is not an instruction fetch: */ |
| else { |
| |
| /* make a data_access_error trap: */ |
| trap = TME_SPARC64_TRAP_data_access_error; |
| } |
| |
| /* this trap doesn't update any other state: */ |
| updates = TME_STP103X_UPDATE_NONE; |
| } |
| |
| /* get the virtual address, forced to be in range: */ |
| address = ls->tme_sparc_ls_address64; |
| address |= (0 - (TME_STP103X_VA_HOLE_START * 2)); |
| address = (address ^ TME_STP103X_VA_HOLE_START) + TME_STP103X_VA_HOLE_START; |
| |
| /* if this trap updates the DMMU SFAR: */ |
| if (updates & TME_STP103X_UPDATE_DMMU_SFAR) { |
| TME_STP103X(ic)->tme_stp103x_dmmu_sfar = address; |
| } |
| |
| /* assume that this trap updates the IMMU: */ |
| mmu = &TME_STP103X(ic)->tme_stp103x_immu; |
| |
| /* if this trap updates the DMMU: */ |
| if ((updates |
| & (TME_STP103X_UPDATE_DMMU |
| | TME_STP103X_UPDATE_IMMU)) |
| != TME_STP103X_UPDATE_IMMU) { |
| |
| /* assume that this trap updates the DMMU SFSR and define |
| SFSR.E: */ |
| if (ls->tme_sparc_ls_tlb->tme_sparc_tlb_asi_mask |
| & TME_SPARC64_ASI_MASK_FLAG_TLB_SIDE_EFFECTS) { |
| sfsr += TME_STP103X_SFSR_E; |
| } |
| |
| /* update the common DMMU state: */ |
| mmu = &TME_STP103X(ic)->tme_stp103x_dmmu; |
| } |
| |
| /* get the ASI mask from the instruction: */ |
| asi_mask = ls->tme_sparc_ls_asi_mask; |
| |
| /* if this trap updates the DMMU or IMMU tag access register: */ |
| if (updates & TME_STP103X_UPDATE_MMU_TAG_ACCESS) { |
| |
| /* update the tag access: */ |
| mmu->tme_stp103x_mmu_tag_access |
| = ((address |
| & (0 - (tme_uint64_t) (TME_STP103X_CONTEXT_MAX + 1))) |
| + ((asi_mask & TME_SPARC64_ASI_MASK_FLAG_SPECIAL) |
| ? 0 |
| : ls->tme_sparc_ls_context)); |
| } |
| |
| /* if this trap updates the DMMU or IMMU SFSR: */ |
| if (updates & TME_STP103X_UPDATE_MMU_SFSR) { |
| |
| /* define SFSR.ASI: */ |
| TME_FIELD_MASK_DEPOSITU(sfsr, |
| TME_STP103X_SFSR_ASI, |
| TME_SPARC_ASI_MASK_WHICH(asi_mask)); |
| |
| /* define SFSR.CT: */ |
| sfsr |
| += ((asi_mask & TME_SPARC64_ASI_MASK_FLAG_SPECIAL) |
| ? TME_STP103X_SFSR_CT_RESERVED |
| : (asi_mask & TME_SPARC64_ASI_MASK_FLAG_INSN_NUCLEUS) |
| ? TME_STP103X_SFSR_CT_NUCLEUS |
| : (asi_mask & TME_SPARC64_ASI_FLAG_SECONDARY) |
| ? TME_STP103X_SFSR_CT_SECONDARY |
| : TME_STP103X_SFSR_CT_PRIMARY); |
| |
| /* define SFSR.PR: */ |
| if (TME_SPARC_PRIV(ic)) { |
| sfsr += TME_STP103X_SFSR_PR; |
| } |
| |
| /* define SFSR.W: */ |
| if (lsinfo |
| & (TME_SPARC_LSINFO_OP_ST |
| | TME_SPARC_LSINFO_OP_ATOMIC)) { |
| sfsr += TME_STP103X_SFSR_W; |
| } |
| |
| /* define SFSR.FV: */ |
| sfsr += TME_STP103X_SFSR_FV; |
| |
| /* define SFSR.OW: */ |
| if (mmu->tme_stp103x_mmu_sfsr & TME_STP103X_SFSR_FV) { |
| sfsr += TME_STP103X_SFSR_OW; |
| } |
| |
| /* update the DMMU or IMMU SFSR: */ |
| mmu->tme_stp103x_mmu_sfsr = sfsr; |
| } |
| |
| /* if there is a trap: */ |
| if (__tme_predict_true(trap != TME_SPARC_TRAP_none)) { |
| |
| /* trap: */ |
| tme_sparc64_trap(ic, trap); |
| } |
| } |
| |
| /* this checks that the virtual address of a load/store is in range. |
| if it is out of range and ic is non-NULL, it traps immediately: */ |
| static tme_uint64_t |
| _tme_stp103x_ls_address_check(struct tme_sparc *ic, |
| struct tme_sparc_ls *ls) |
| { |
| tme_uint64_t address; |
| tme_uint32_t address_32_63; |
| |
| /* get bits 32..63 of the address: */ |
| address = ls->tme_sparc_ls_address64; |
| address_32_63 = address >> 32; |
| |
| /* if this address is in the address space hole: */ |
| if (__tme_predict_false((address_32_63 + (tme_uint32_t) (TME_STP103X_VA_HOLE_START >> 32)) |
| >= (tme_uint32_t) ((TME_STP103X_VA_HOLE_START * 2) >> 32))) { |
| |
| /* note the fault: */ |
| ls->tme_sparc_ls_faults |= TME_SPARC64_LS_FAULT_VA_RANGE; |
| |
| /* if we can, trap now: */ |
| if (ic != NULL) { |
| _tme_stp103x_ls_trap(ic, ls); |
| abort(); |
| /* NOTREACHED */ |
| } |
| } |
| |
| /* return the address: */ |
| return (address); |
| } |
| |
| /* this maps a virtual address directly into a physical address: */ |
| static void |
| _tme_stp103x_ls_address_map_phys(struct tme_sparc *ic, |
| struct tme_sparc_ls *ls) |
| { |
| tme_uint64_t address; |
| tme_uint32_t asi; |
| tme_uint32_t asi_mask; |
| |
| /* check the address: */ |
| address = _tme_stp103x_ls_address_check(ic, ls); |
| |
| /* get the original ASI: */ |
| asi = TME_SPARC_ASI_MASK_WHICH(ls->tme_sparc_ls_asi_mask); |
| |
| /* all of the bypass ASIs behave as if the P bit were clear: */ |
| asi_mask |
| = (TME_SPARC64_ASI_MASK_PRIV |
| + TME_SPARC64_ASI_MASK_USER); |
| |
| /* ASIs 0x15 and 0x1d behave as if the E bit were set and the CP bit |
| were clear: */ |
| if (asi & 1) { |
| asi_mask |
| += (TME_SPARC64_ASI_MASK_FLAG_TLB_SIDE_EFFECTS |
| + TME_SPARC64_ASI_MASK_FLAG_TLB_UNCACHEABLE); |
| } |
| |
| /* update the flags on the TLB entry: */ |
| ls->tme_sparc_ls_tlb->tme_sparc_tlb_asi_mask |
| |= asi_mask; |
| |
| /* do the truncating mapping: */ |
| address &= (0 - (tme_uint64_t) TME_STP103X_PAGE_SIZE_8KB); |
| ls->tme_sparc_ls_tlb_map.tme_bus_tlb_addr_first = address; |
| address |= (TME_STP103X_PAGE_SIZE_8KB - 1); |
| ls->tme_sparc_ls_tlb_map.tme_bus_tlb_addr_last = address; |
| ls->tme_sparc_ls_tlb_map.tme_bus_tlb_cycles_ok = (TME_BUS_CYCLE_READ | TME_BUS_CYCLE_WRITE); |
| ls->tme_sparc_ls_tlb_map.tme_bus_tlb_addr_offset |
| = ((address % TME_STP103X_PA_SIZE) |
| - address); |
| } |
| |
| /* this maps a virtual address: */ |
| static void |
| _tme_stp103x_ls_address_map(struct tme_sparc *ic, |
| struct tme_sparc_ls *ls) |
| { |
| tme_uint64_t address; |
| tme_uint32_t lsu_0_31; |
| signed long tlb_part_i; |
| tme_uint32_t tlb_tag_match_0_31; |
| tme_uint32_t tlb_tag_match_32_63; |
| tme_uint32_t tlb_tag_xor_0_31; |
| tme_uint32_t tlb_data_32_63; |
| tme_uint32_t size; |
| tme_uint32_t tlb_data_0_31; |
| struct tme_sparc_tlb *tlb; |
| tme_uint32_t asi_mask; |
| tme_uint32_t cycles_ok; |
| |
| /* check the address: */ |
| address = _tme_stp103x_ls_address_check(ic, ls); |
| |
| /* get bits 0..31 of the load/store unit control register: */ |
| lsu_0_31 = TME_STP103X(ic)->tme_stp103x_lsu; |
| |
| /* assume that this is not an instruction fetch, and start at the |
| beginning of the DMMU TLB entries: */ |
| tlb_part_i = TME_STP103X_TLB_PART_0_DMMU; |
| |
| /* if this is an instruction fetch: */ |
| if (ls->tme_sparc_ls_lsinfo & TME_SPARC_LSINFO_OP_FETCH) { |
| |
| /* assume that LSU.IM is set, and set LSU.DM to indicate an |
| enabled IMMU (!): */ |
| lsu_0_31 |= TME_STP103X_LSU_DM; |
| |
| /* if the IMMU is disabled, or if the CPU is in RED_state: */ |
| if (__tme_predict_false((lsu_0_31 & TME_STP103X_LSU_IM) == 0 |
| || (ic->tme_sparc64_ireg_pstate & TME_SPARC64_PSTATE_RED))) { |
| |
| /* clear LSU.DM, to indicate a disabled IMMU (!): */ |
| lsu_0_31 &= ~TME_STP103X_LSU_DM; |
| } |
| |
| /* start at the beginning of the IMMU TLB entries: */ |
| tlb_part_i = TME_STP103X_TLB_PART_0_IMMU; |
| } |
| |
| /* if the MMU is disabled: */ |
| if (__tme_predict_false((lsu_0_31 & TME_STP103X_LSU_DM) == 0)) { |
| |
| /* a disabled MMU behaves as if the E bit were set, and the P and |
| CP bits were clear: */ |
| ls->tme_sparc_ls_tlb->tme_sparc_tlb_asi_mask |
| |= (TME_SPARC64_ASI_MASK_PRIV |
| + TME_SPARC64_ASI_MASK_USER |
| + TME_SPARC64_ASI_MASK_FLAG_TLB_SIDE_EFFECTS |
| + TME_SPARC64_ASI_MASK_FLAG_TLB_UNCACHEABLE); |
| |
| /* do the truncating mapping: */ |
| address &= (0 - (tme_uint64_t) TME_STP103X_PAGE_SIZE_8KB); |
| ls->tme_sparc_ls_tlb_map.tme_bus_tlb_addr_first = address; |
| address |= (TME_STP103X_PAGE_SIZE_8KB - 1); |
| ls->tme_sparc_ls_tlb_map.tme_bus_tlb_addr_last = address; |
| ls->tme_sparc_ls_tlb_map.tme_bus_tlb_cycles_ok = (TME_BUS_CYCLE_READ | TME_BUS_CYCLE_WRITE); |
| ls->tme_sparc_ls_tlb_map.tme_bus_tlb_addr_offset |
| = ((address % TME_STP103X_PA_SIZE) |
| - address); |
| return; |
| } |
| |
| /* make the tag to match: */ |
| tlb_tag_match_32_63 = (address >> 32); |
| tlb_tag_match_0_31 = address; |
| tlb_tag_match_0_31 &= ~TME_STP103X_CONTEXT_MAX; |
| tlb_tag_match_0_31 += ls->tme_sparc_ls_context; |
| |
| /* loop over TLB entries: */ |
| do { |
| |
| /* if bits 32..63 of the tag match: */ |
| if (TME_STP103X(ic)->tme_stp103x_tlb_32s((tlb_part_i + 0), 1) == tlb_tag_match_32_63) { |
| |
| /* if bits 22..31 of the tag match: */ |
| tlb_tag_xor_0_31 = TME_STP103X(ic)->tme_stp103x_tlb_32s((tlb_part_i + 0), 0) ^ tlb_tag_match_0_31; |
| if (tlb_tag_xor_0_31 < TME_STP103X_PAGE_SIZE_4MB) { |
| |
| /* load bits 32..63 of the data: */ |
| tlb_data_32_63 = TME_STP103X(ic)->tme_stp103x_tlb_32s((tlb_part_i + 1), 1); |
| |
| /* if the data is valid: */ |
| if (tlb_data_32_63 & TME_STP103X_TLB_DATA_V(tlb_data_32_63)) { |
| |
| /* get the size of this mapping: */ |
| size |
| = (TME_STP103X_PAGE_SIZE_8KB |
| << (3 * TME_FIELD_MASK_EXTRACTU(tlb_data_32_63, |
| TME_STP103X_TLB_DATA_SIZE_MASK(tlb_data_32_63)))); |
| |
| /* load bits 0..31 of the data: */ |
| tlb_data_0_31 = TME_STP103X(ic)->tme_stp103x_tlb_32s((tlb_part_i + 1), 0); |
| |
| /* if bits 0..31 of the tag match: */ |
| if ((tlb_tag_xor_0_31 |
| & ((0 - size) |
| + ((tlb_data_0_31 |
| & TME_STP103X_TLB_DATA_G(tlb_data_0_31)) |
| ? 0 |
| : TME_STP103X_CONTEXT_MAX))) == 0) { |
| |
| /* set the used bit: */ |
| tlb_data_32_63 |= TME_STP103X_TLB_DATA_DIAG_USED(tlb_data_32_63); |
| TME_STP103X(ic)->tme_stp103x_tlb_32s((tlb_part_i + 1), 1) = tlb_data_32_63; |
| |
| /* get the sparc TLB entry: */ |
| tlb = ls->tme_sparc_ls_tlb; |
| |
| /* if this is a global mapping, update the TLB context: */ |
| if (tlb_data_0_31 & TME_STP103X_TLB_DATA_G(tlb_data_0_31)) { |
| tlb->tme_sparc_tlb_context = TME_STP103X_CONTEXT_MAX + 1; |
| } |
| |
| /* link the TLB entries: */ |
| tlb->tme_sparc_tlb_link = tlb_part_i; |
| |
| /* start the address offset for this mapping: */ |
| ls->tme_sparc_ls_tlb_map.tme_bus_tlb_addr_offset |
| = ((tlb_data_0_31 |
| & (tme_uint32_t) TME_STP103X_TLB_DATA_PA) |
| + (((tme_uint64_t) |
| (tlb_data_32_63 |
| & (tme_uint32_t) (TME_STP103X_TLB_DATA_PA >> 32))) |
| << 32)); |
| |
| /* copy the E, !CP, IE, and NFO bits into the TLB entry's |
| ASI mask. we predict for the common case, which has CP |
| set and all of the other bits clear: */ |
| /* NB: we ignore the CV bit, because it's always zero in |
| the IMMU, and in the DMMU it doesn't count as |
| uncacheable as far as atomic instructions are |
| concerned: */ |
| if (__tme_predict_false((tlb_data_0_31 |
| & (TME_STP103X_TLB_DATA_E(tlb_data_0_31) |
| | TME_STP103X_TLB_DATA_CP(tlb_data_0_31))) |
| != TME_STP103X_TLB_DATA_CP(tlb_data_0_31))) { |
| asi_mask = 0; |
| if (tlb_data_0_31 & TME_STP103X_TLB_DATA_E(tlb_data_0_31)) { |
| asi_mask += TME_SPARC64_ASI_MASK_FLAG_TLB_SIDE_EFFECTS; |
| } |
| if ((tlb_data_0_31 & TME_STP103X_TLB_DATA_CP(tlb_data_0_31)) == 0) { |
| asi_mask += TME_SPARC64_ASI_MASK_FLAG_TLB_UNCACHEABLE; |
| } |
| } |
| else { |
| asi_mask = 0; |
| } |
| if (__tme_predict_false(tlb_data_32_63 |
| & (TME_STP103X_TLB_DATA_NFO(tlb_data_32_63) |
| | TME_STP103X_TLB_DATA_IE(tlb_data_32_63)))) { |
| if (tlb_data_32_63 & TME_STP103X_TLB_DATA_NFO(tlb_data_32_63)) { |
| asi_mask += TME_SPARC64_ASI_FLAG_NO_FAULT; |
| } |
| if (tlb_data_32_63 & TME_STP103X_TLB_DATA_IE(tlb_data_32_63)) { |
| asi_mask += TME_SPARC64_ASI_FLAG_LITTLE; |
| } |
| } |
| |
| /* if this mapping is privileged: */ |
| if (tlb_data_0_31 & TME_STP103X_TLB_DATA_P(tlb_data_0_31)) { |
| |
| /* if this access is not privileged: */ |
| if (__tme_predict_false(!TME_SPARC_PRIV(ic))) { |
| |
| /* trap: */ |
| ls->tme_sparc_ls_faults |= TME_STP103X_LS_FAULT_PRIVILEGE; |
| _tme_stp103x_ls_trap(ic, ls); |
| assert (ls->tme_sparc_ls_lsinfo & TME_SPARC_LSINFO_NO_FAULT); |
| return; |
| } |
| } |
| |
| /* otherwise, this mapping is not privileged: */ |
| else { |
| |
| /* this TLB entry can be used for privileged and nonprivileged |
| accesses: */ |
| asi_mask |
| += (TME_SPARC64_ASI_MASK_PRIV |
| + TME_SPARC64_ASI_MASK_USER); |
| } |
| |
| /* update the TLB entry's ASI mask: */ |
| ls->tme_sparc_ls_tlb->tme_sparc_tlb_asi_mask |= asi_mask; |
| |
| /* if this page is writable: */ |
| if (tlb_data_0_31 & TME_STP103X_TLB_DATA_W(tlb_data_0_31)) { |
| |
| /* this mapping can be read and written: */ |
| cycles_ok = TME_BUS_CYCLE_READ | TME_BUS_CYCLE_WRITE; |
| } |
| |
| /* otherwise, this page is not writable: */ |
| else { |
| |
| /* if this is a store or an atomic: */ |
| if (__tme_predict_false(ls->tme_sparc_ls_lsinfo |
| & (TME_SPARC_LSINFO_OP_ST |
| | TME_SPARC_LSINFO_OP_ATOMIC))) { |
| |
| /* remember if this is a 64KB page: */ |
| TME_STP103X(ic)->tme_stp103x_dmmu_direct_64KB = (size == TME_STP103X_PAGE_SIZE_64KB); |
| |
| /* trap: */ |
| ls->tme_sparc_ls_faults |= TME_STP103X_LS_FAULT_PROTECTION; |
| _tme_stp103x_ls_trap(ic, ls); |
| abort(); |
| /* NOTREACHED */ |
| } |
| |
| /* this mapping can only be read: */ |
| cycles_ok = TME_BUS_CYCLE_READ; |
| } |
| |
| /* set the cycles for this mapping: */ |
| ls->tme_sparc_ls_tlb_map.tme_bus_tlb_cycles_ok = cycles_ok; |
| |
| /* set the first and last addresses for this mapping: */ |
| address = ls->tme_sparc_ls_address64; |
| address |= (size - 1); |
| ls->tme_sparc_ls_tlb_map.tme_bus_tlb_addr_last = address; |
| address &= (0 - (tme_uint64_t) size); |
| ls->tme_sparc_ls_tlb_map.tme_bus_tlb_addr_first = address; |
| |
| /* finish the address offset for this mapping: */ |
| ls->tme_sparc_ls_tlb_map.tme_bus_tlb_addr_offset -= address; |
| |
| return; |
| } |
| } |
| } |
| } |
| |
| tlb_part_i += 2; |
| } while (tlb_part_i % (2 * TME_STP103X_TLB_SIZE)); |
| |
| /* this is a miss: */ |
| ls->tme_sparc_ls_faults |= TME_STP103X_LS_FAULT_MMU_MISS; |
| _tme_stp103x_ls_trap(ic, ls); |
| assert (ls->tme_sparc_ls_lsinfo & TME_SPARC_LSINFO_NO_FAULT); |
| } |
| |
| /* the ASI handler for ASI_PHYS_USE_EC*, ASI_PHYS_BYPASS_EC_WITH_EBIT*: */ |
| static void |
| _tme_stp103x_ls_asi_phys(struct tme_sparc *ic, struct tme_sparc_ls *ls) |
| { |
| |
| /* override the address map function: */ |
| ls->tme_sparc_ls_address_map = _tme_stp103x_ls_address_map_phys; |
| } |
| |
| /* the cycle handler for ASI_NUCLEUS_QUAD_LDD*: */ |
| static void |
| _tme_stp103x_ls_cycle_quad(struct tme_sparc *ic, struct tme_sparc_ls *ls) |
| { |
| struct tme_sparc_tlb *tlb; |
| tme_uint32_t asi_mask; |
| _tme_const tme_shared tme_uint8_t *memory; |
| #if !TME_THREADS_COOPERATIVE |
| #ifdef tme_memory_atomic_read128 |
| tme_uint128_t quad; |
| #endif /* tme_memory_atomic_read128 */ |
| #endif /* !TME_THREADS_COOPERATIVE */ |
| tme_uint64_t quad_64lo; |
| tme_uint64_t quad_64hi; |
| tme_uint64_t *_rd; |
| |
| /* get the TLB entry: */ |
| tlb = ls->tme_sparc_ls_tlb; |
| |
| /* get the ASI mask: */ |
| asi_mask = tlb->tme_sparc_tlb_asi_mask; |
| |
| /* this TLB entry must be for cacheable memory: */ |
| if (__tme_predict_false(asi_mask |
| & TME_SPARC64_ASI_MASK_FLAG_TLB_UNCACHEABLE)) { |
| |
| /* we must have caught this on the first cycle: */ |
| assert (ls->tme_sparc_ls_buffer_offset == 0); |
| |
| /* fault: */ |
| ls->tme_sparc_ls_faults |= TME_SPARC64_LS_FAULT_UNCACHEABLE; |
| return; |
| } |
| |
| /* assume that we can't do a fast transfer: */ |
| memory = TME_EMULATOR_OFF_UNDEF; |
| |
| /* if this is the first cycle: */ |
| if (ls->tme_sparc_ls_buffer_offset == 0) { |
| |
| /* if this TLB entry allows fast transfer of all of the addresses: */ |
| if (__tme_predict_true((((tme_bus_addr64_t) tlb->tme_sparc_tlb_addr_last) |
| - ls->tme_sparc_ls_address64) |
| >= ((sizeof(tme_uint64_t) * 2) - 1))) { |
| |
| /* we may be able do a fast transfer: */ |
| memory = tlb->tme_sparc_tlb_emulator_off_read; |
| } |
| } |
| |
| /* if we can't do a fast transfer: */ |
| if (__tme_predict_false(memory == TME_EMULATOR_OFF_UNDEF)) { |
| |
| /* do a slow cycle: */ |
| tme_sparc64_load(ic, ls); |
| |
| /* if this was not the last cycle, return now: */ |
| if (ls->tme_sparc_ls_size != 0) { |
| return; |
| } |
| |
| /* fake a fast transfer from the memory buffer: */ |
| memory = &ic->tme_sparc_memory_buffer.tme_sparc_memory_buffer8s[0]; |
| memory -= ls->tme_sparc_ls_address64; |
| } |
| |
| /* finish the memory address: */ |
| memory += ls->tme_sparc_ls_address64; |
| |
| /* if threads are cooperative: */ |
| #if TME_THREADS_COOPERATIVE |
| |
| /* do two 64-bit loads: */ |
| quad_64lo |
| = tme_memory_bus_read64(((_tme_const tme_shared tme_uint64_t *) memory) + 0, |
| tlb->tme_sparc_tlb_bus_rwlock, |
| (sizeof(tme_uint64_t) * 2), |
| sizeof(tme_uint64_t)); |
| quad_64hi |
| = tme_memory_bus_read64(((_tme_const tme_shared tme_uint64_t *) memory) + 1, |
| tlb->tme_sparc_tlb_bus_rwlock, |
| (sizeof(tme_uint64_t) * 1), |
| sizeof(tme_uint64_t)); |
| |
| /* otherwise, threads are not cooperative: */ |
| #else /* !TME_THREADS_COOPERATIVE */ |
| |
| /* if host supports an atomic 128-bit read: */ |
| #ifdef tme_memory_atomic_read128 |
| |
| /* do the atomic 128-bit read: */ |
| quad |
| = tme_memory_atomic_read128((_tme_const tme_shared tme_uint128_t *) memory, |
| tlb->tme_sparc_tlb_bus_rwlock, |
| sizeof(tme_uint128_t)); |
| |
| /* get the two parts of the load: */ |
| #if TME_ENDIAN_NATIVE == TME_ENDIAN_BIG |
| quad_64lo = quad >> 64; |
| quad_64hi = quad; |
| #elif TME_ENDIAN_NATIVE == TME_ENDIAN_LITTLE |
| quad_64lo = quad; |
| quad_64hi = quad >> 64; |
| #endif |
| #else /* !tme_memory_atomic_read128 */ |
| #error "non-cooperative threads requires an atomic 128-bit read" |
| #endif /* !tme_memory_atomic_read128 */ |
| #endif /* !TME_THREADS_COOPERATIVE */ |
| |
| /* swap the two 64-bit values as needed: */ |
| if (ls->tme_sparc_ls_lsinfo & TME_SPARC_LSINFO_ENDIAN_LITTLE) { |
| quad_64lo = tme_letoh_u64(quad_64lo); |
| quad_64hi = tme_letoh_u64(quad_64hi); |
| } |
| else { |
| quad_64lo = tme_betoh_u64(quad_64lo); |
| quad_64hi = tme_betoh_u64(quad_64hi); |
| } |
| |
| /* complete the load: */ |
| ls->tme_sparc_ls_size = 0; |
| _rd = ls->tme_sparc_ls_rd64; |
| TME_SPARC_FORMAT3_RD = quad_64lo; |
| TME_SPARC_FORMAT3_RD_ODD(tme_ic_ireg_uint64) = quad_64hi; |
| } |
| |
| /* the ASI handler for ASI_NUCLEUS_QUAD_LDD*: */ |
| static void |
| _tme_stp103x_ls_asi_quad(struct tme_sparc *ic, struct tme_sparc_ls *ls) |
| { |
| |
| /* we need to do the complete load: */ |
| ls->tme_sparc_ls_size = sizeof(tme_uint64_t) * 2; |
| ls->tme_sparc_ls_buffer_offset = 0; |
| ls->tme_sparc_ls_lsinfo |
| |= (TME_SPARC_LSINFO_SLOW_CYCLES |
| + TME_SPARC_LSINFO_LD_COMPLETED); |
| ls->tme_sparc_ls_cycle = _tme_stp103x_ls_cycle_quad; |
| |
| /* an instruction other than ldda is illegal: */ |
| if (__tme_predict_false((ic->_tme_sparc_insn |
| & (0x3f << 19)) |
| != (0x13 << 19))) { |
| ls->tme_sparc_ls_faults |= TME_STP103X_LS_FAULT_ILLEGAL; |
| } |
| |
| /* the address must be 128-bit aligned: */ |
| if (__tme_predict_false(((tme_uint32_t) ls->tme_sparc_ls_address64) |
| % (sizeof(tme_uint64_t) * 2))) { |
| ls->tme_sparc_ls_faults |= TME_SPARC_LS_FAULT_ADDRESS_NOT_ALIGNED; |
| } |
| } |
| |
| /* the ASI handler for various infrequently-used ASIs: */ |
| static void |
| _tme_stp103x_ls_asi_slow(struct tme_sparc *ic, struct tme_sparc_ls *ls) |
| { |
| tme_uint32_t lsinfo_ops; |
| int size_ok; |
| int address_ok; |
| tme_uint64_t value_mask; |
| void (*update_write) _TME_P((struct tme_sparc *, tme_uint64_t)); |
| int redispatch; |
| tme_uint64_t *_value64; |
| tme_uint32_t *_value32; |
| tme_uint64_t value; |
| tme_uint64_t value_now; |
| |
| /* assume that this ASI allows reads and writes: */ |
| lsinfo_ops |
| = (TME_SPARC_LSINFO_OP_LD |
| | TME_SPARC_LSINFO_OP_ST); |
| |
| /* assume that this ASI allows only 64-bit accesses: */ |
| size_ok = (ls->tme_sparc_ls_size == sizeof(tme_uint64_t)); |
| |
| /* assume that this ASI allows only accesses to address zero: */ |
| address_ok = (ls->tme_sparc_ls_address64 == 0); |
| |
| /* assume that this ASI doesn't mask values written: */ |
| value_mask = 0 - (tme_uint64_t) 1; |
| |
| /* assume that this ASI has no write side-effects: */ |
| update_write = NULL; |
| |
| /* assume that a write won't need a redispatch: */ |
| redispatch = FALSE; |
| |
| /* assume that the access is invalid: */ |
| _value64 = NULL; |
| _value32 = NULL; |
| |
| /* dispatch on the ASI: */ |
| switch (TME_SPARC_ASI_MASK_WHICH(ls->tme_sparc_ls_asi_mask)) { |
| |
| /* all unknown ASIs: */ |
| default: |
| address_ok = FALSE; |
| break; |
| |
| case TME_STP103X_ASI_LSU_CONTROL_REG: |
| _value64 = &TME_STP103X(ic)->tme_stp103x_lsu; |
| update_write = _tme_stp103x_update_lsu; |
| break; |
| |
| case TME_STP103X_ASI_INTR_DISPATCH_STATUS: |
| lsinfo_ops = TME_SPARC_LSINFO_OP_LD; |
| /* XXX FIXME WRITEME: */ |
| abort(); |
| break; |
| |
| case TME_STP103X_ASI_INTR_RECEIVE: |
| value_now |
| = ((tme_memory_atomic_read_flag(&TME_STP103X(ic)->tme_stp103x_intr_receive_busy) |
| ? TME_STP103X_INTR_RECEIVE_BUSY |
| : 0) |
| + TME_STP103X(ic)->tme_stp103x_intr_receive_mid); |
| _value64 = &value_now; |
| update_write = _tme_stp103x_update_intr_receive; |
| break; |
| |
| case TME_STP103X_ASI_UPA_CONFIG_REG: |
| _value64 = &TME_STP103X(ic)->tme_stp103x_upa_config; |
| update_write = _tme_stp103x_update_upa_config; |
| break; |
| |
| case TME_STP103X_ASI_ESTATE_ERROR_EN_REG: |
| _value32 = &TME_STP103X(ic)->tme_stp103x_estate_error_enable; |
| value_mask |
| = (TME_STP103X_ESTATE_ERROR_ENABLE_CEEN |
| | TME_STP103X_ESTATE_ERROR_ENABLE_NCEEN |
| | TME_STP103X_ESTATE_ERROR_ENABLE_ISAPEN); |
| break; |
| |
| case TME_STP103X_ASI_AFSR: |
| _value64 = &TME_STP103X(ic)->tme_stp103x_afsr; |
| update_write = _tme_stp103x_update_afsr; |
| break; |
| |
| case TME_STP103X_ASI_AFAR: |
| _value64 = &TME_STP103X(ic)->tme_stp103x_afar; |
| value_mask = TME_STP103X_PA_SIZE - (1 << 4); |
| break; |
| |
| case TME_STP103X_ASI_ECACHE_TAG_DATA: |
| _value32 = &TME_STP103X(ic)->tme_stp103x_ecache_tag_data; |
| break; |
| } |
| |
| /* check the access: */ |
| if (__tme_predict_false((ls->tme_sparc_ls_lsinfo & lsinfo_ops) == 0 |
| || !size_ok |
| || !address_ok)) { |
| ls->tme_sparc_ls_faults |= TME_STP103X_LS_FAULT_ILLEGAL; |
| } |
| |
| /* if there are any faults: */ |
| if (__tme_predict_false(ls->tme_sparc_ls_faults |= TME_SPARC_LS_FAULT_NONE)) { |
| return; |
| } |
| |
| /* get the raw value to read or write: */ |
| value |
| = ((ls->tme_sparc_ls_lsinfo & TME_SPARC_LSINFO_OP_ST) |
| ? *ls->tme_sparc_ls_rd64 |
| : _value64 != NULL |
| ? *_value64 |
| : *_value32); |
| |
| /* mask the value: */ |
| value &= value_mask; |
| |
| /* complete the load or store: */ |
| ls->tme_sparc_ls_size = 0; |
| |
| /* if this is a load: */ |
| if (ls->tme_sparc_ls_lsinfo & TME_SPARC_LSINFO_OP_LD) { |
| |
| /* complete the load: */ |
| *ls->tme_sparc_ls_rd64 = value; |
| ls->tme_sparc_ls_lsinfo |= TME_SPARC_LSINFO_LD_COMPLETED; |
| } |
| |
| /* otherwise, this is a store: */ |
| else { |
| |
| /* if this value has write side-effects: */ |
| if (update_write != NULL) { |
| |
| /* do the write side-effects: */ |
| (*update_write)(ic, value); |
| } |
| |
| /* otherwise, this value has no write side-effects: */ |
| else { |
| |
| /* complete the store: */ |
| if (_value64 != NULL) { |
| *_value64 = value; |
| } |
| else { |
| *_value32 = value; |
| } |
| } |
| |
| /* if this store needs a redispatch: */ |
| if (redispatch) { |
| tme_bus_tlb_unbusy(&ic->tme_sparc_tlbs[ls->tme_sparc_ls_tlb_i].tme_sparc_tlb_bus_tlb); |
| tme_sparc_redispatch(ic); |
| } |
| } |
| } |
| |
| /* the ASI handler for ASI_DMMU and ASI_IMMU: */ |
| static void |
| _tme_stp103x_ls_asi_mmu(struct tme_sparc *ic, struct tme_sparc_ls *ls) |
| { |
| tme_uint64_t address; |
| tme_uint32_t address_0_31; |
| struct tme_stp103x_mmu *mmu; |
| tme_uint32_t lsinfo; |
| tme_uint64_t tag_target; |
| tme_uint32_t context; |
| tme_uint64_t *_value64; |
| tme_uint32_t *_value32; |
| int value_has_va; |
| tme_uint64_t value_mask; |
| tme_uint32_t lsinfo_ops; |
| int redispatch; |
| tme_uint64_t value; |
| |
| /* if this is a 64-bit access that hasn't faulted yet: */ |
| if (__tme_predict_true(ls->tme_sparc_ls_size == sizeof(tme_uint64_t) |
| && ls->tme_sparc_ls_faults == TME_SPARC_LS_FAULT_NONE)) { |
| |
| /* get the address: */ |
| address = ls->tme_sparc_ls_address64; |
| |
| /* if the address fits into 32 bits: */ |
| if (__tme_predict_true((address & (0 - (((tme_uint64_t) 1) << 32))) == 0)) { |
| |
| /* truncate the address to 32 bits: */ |
| address_0_31 = address; |
| |
| /* get the MMU state: */ |
| mmu |
| = (TME_STP103X_ASI_MMU_MASK_IS_DMMU(ls->tme_sparc_ls_asi_mask) |
| ? &TME_STP103X(ic)->tme_stp103x_dmmu |
| : &TME_STP103X(ic)->tme_stp103x_immu); |
| |
| /* get the load/store information: */ |
| lsinfo = ls->tme_sparc_ls_lsinfo; |
| |
| /* address 0x0 is the tag target register: */ |
| if (address_0_31 == 0) { |
| |
| /* if this is a load: */ |
| if (lsinfo & TME_SPARC_LSINFO_OP_LD) { |
| |
| /* make the value for the tag target register: */ |
| tag_target = mmu->tme_stp103x_mmu_tag_access; |
| context = ((tme_uint32_t) tag_target) & TME_STP103X_CONTEXT_MAX; |
| tag_target >>= 22; |
| tag_target |= ((tme_uint64_t) (context << (48 - 32))) << 32; |
| |
| /* complete the load: */ |
| *ls->tme_sparc_ls_rd64 = tag_target; |
| ls->tme_sparc_ls_lsinfo |= TME_SPARC_LSINFO_LD_COMPLETED; |
| ls->tme_sparc_ls_size = 0; |
| return; |
| } |
| } |
| |
| else { |
| |
| /* assume that the register is invalid: */ |
| _value64 = NULL; |
| _value32 = NULL; |
| |
| /* assume that values for this register aren't virtual |
| addresses, and don't have any masked bits: */ |
| value_has_va = FALSE; |
| value_mask = 0 - (tme_uint64_t) 1; |
| |
| /* assume that this register allows reads and writes: */ |
| lsinfo_ops |
| = (TME_SPARC_LSINFO_OP_LD |
| | TME_SPARC_LSINFO_OP_ST); |
| |
| /* assume that a write to this register won't need a redispatch: */ |
| redispatch = FALSE; |
| |
| /* address 0x18 is the synchronous fault status register: */ |
| if (address_0_31 == 0x18) { |
| _value64 = &mmu->tme_stp103x_mmu_sfsr; |
| } |
| |
| /* address 0x28 is the TSB register: */ |
| else if (address_0_31 == 0x28) { |
| _value64 = &mmu->tme_stp103x_mmu_tsb; |
| value_has_va = TRUE; |
| } |
| |
| /* address 0x30 is the tag access register: */ |
| else if (address_0_31 == 0x30) { |
| _value64 = &mmu->tme_stp103x_mmu_tag_access; |
| value_has_va = TRUE; |
| } |
| |
| /* if this is ASI_DMMU: */ |
| else if (mmu == &TME_STP103X(ic)->tme_stp103x_dmmu) { |
| |
| /* address 0x8 is the primary context register: */ |
| if (address_0_31 == 0x8) { |
| _value32 = &ic->tme_sparc_memory_context_primary; |
| value_mask = TME_STP103X_CONTEXT_MAX; |
| redispatch = TRUE; |
| } |
| |
| /* address 0x10 is the secondary context register: */ |
| else if (address_0_31 == 0x10) { |
| _value32 = &ic->tme_sparc_memory_context_secondary; |
| value_mask = TME_STP103X_CONTEXT_MAX; |
| } |
| |
| /* address 0x20 is the synchronous fault address register: */ |
| else if (address_0_31 == 0x20) { |
| _value64 = &TME_STP103X(ic)->tme_stp103x_dmmu_sfar; |
| lsinfo_ops = TME_SPARC_LSINFO_OP_LD; |
| } |
| |
| /* address 0x38 is the VA Data Watchpoint register: */ |
| else if (address_0_31 == 0x38) { |
| abort(); |
| } |
| |
| /* address 0x40 is the PA Data Watchpoint register: */ |
| else if (address_0_31 == 0x38) { |
| abort(); |
| } |
| } |
| |
| /* if the register valid and supports this access: */ |
| if (__tme_predict_true((_value64 != NULL |
| || _value32 != NULL) |
| && (lsinfo & lsinfo_ops) != 0)) { |
| |
| /* get the raw value to read or write: */ |
| value |
| = (lsinfo & TME_SPARC_LSINFO_OP_ST |
| ? *ls->tme_sparc_ls_rd64 |
| : _value64 != NULL |
| ? *_value64 |
| : *_value32); |
| |
| /* if this value has a virtual address: */ |
| if (value_has_va) { |
| |
| /* force the virtual address to be in range: */ |
| value |= (0 - (TME_STP103X_VA_HOLE_START * 2)); |
| value = (value ^ TME_STP103X_VA_HOLE_START) + TME_STP103X_VA_HOLE_START; |
| } |
| |
| /* mask the value: */ |
| value &= value_mask; |
| |
| /* if this is a load: */ |
| if (lsinfo & TME_SPARC_LSINFO_OP_LD) { |
| |
| /* complete the load: */ |
| *ls->tme_sparc_ls_rd64 = value; |
| ls->tme_sparc_ls_lsinfo |= TME_SPARC_LSINFO_LD_COMPLETED; |
| } |
| |
| /* otherwise, this is a store: */ |
| else { |
| |
| /* complete the store: */ |
| if (_value64 != NULL) { |
| *_value64 = value; |
| } |
| else { |
| *_value32 = value; |
| } |
| |
| /* if this store needs a redispatch: */ |
| if (redispatch) { |
| tme_bus_tlb_unbusy(&ic->tme_sparc_tlbs[ls->tme_sparc_ls_tlb_i].tme_sparc_tlb_bus_tlb); |
| tme_sparc_redispatch(ic); |
| } |
| } |
| |
| /* complete the load or store: */ |
| ls->tme_sparc_ls_size = 0; |
| return; |
| } |
| } |
| } |
| } |
| |
| /* this is an illegal access: */ |
| ls->tme_sparc_ls_faults |= TME_STP103X_LS_FAULT_ILLEGAL; |
| } |
| |
| /* the ASI handler for: |
| ASI_DMMU_TSB_8KB_PTR_REG, ASI_DMMU_TSB_64KB_PTR_REG, |
| ASI_IMMU_TSB_8KB_PTR_REG, ASI_IMMU_TSB_64KB_PTR_REG, |
| ASI_DMMU_TSB_DIRECT_PTR_REG: */ |
| static void |
| _tme_stp103x_ls_asi_tsb_ptr(struct tme_sparc *ic, struct tme_sparc_ls *ls) |
| { |
| tme_uint32_t asi_mask; |
| struct tme_stp103x_mmu *mmu; |
| tme_uint32_t pointer_0_31; |
| tme_uint32_t size_64KB; |
| tme_uint32_t tsb_0_31; |
| tme_uint32_t tsb_size; |
| |
| /* if this is not a 64-bit load of address zero: */ |
| if (__tme_predict_false(ls->tme_sparc_ls_size != sizeof(tme_uint64_t) |
| || (ls->tme_sparc_ls_lsinfo & TME_SPARC_LSINFO_OP_LD) == 0 |
| || ls->tme_sparc_ls_address64 != 0)) { |
| |
| /* this is an illegal access: */ |
| ls->tme_sparc_ls_faults |= TME_STP103X_LS_FAULT_ILLEGAL; |
| } |
| |
| /* if this access has faulted: */ |
| if (__tme_predict_false(ls->tme_sparc_ls_faults != TME_SPARC_LS_FAULT_NONE)) { |
| return; |
| } |
| |
| /* get the ASI mask from the instruction: */ |
| asi_mask = ls->tme_sparc_ls_asi_mask; |
| |
| /* get the common MMU state: */ |
| mmu |
| = (TME_STP103X_ASI_MMU_MASK_IS_DMMU(asi_mask) |
| ? &TME_STP103X(ic)->tme_stp103x_dmmu |
| : &TME_STP103X(ic)->tme_stp103x_immu); |
| |
| /* start the TSB pointer with the tag access register: */ |
| pointer_0_31 = mmu->tme_stp103x_mmu_tag_access; |
| |
| /* if this might be a 64KB page: */ |
| size_64KB = asi_mask & TME_SPARC_ASI_MASK_RAW(TME_STP103X_ASI_FLAG_TSB_64KB_PTR); |
| if (size_64KB) { |
| |
| /* if this is ASI_DMMU_TSB_DIRECT_PTR_REG: */ |
| if (asi_mask & TME_SPARC_ASI_MASK_RAW(TME_STP103X_ASI_FLAG_TSB_8KB_PTR)) { |
| |
| /* this is a 64KB page if the last fast_data_access_protection |
| trap was for a 64KB page: */ |
| size_64KB = TME_STP103X(ic)->tme_stp103x_dmmu_direct_64KB; |
| } |
| |
| /* if this is a 64KB page, shift the TSB pointer: */ |
| if (size_64KB) { |
| pointer_0_31 /= (TME_STP103X_PAGE_SIZE_64KB / TME_STP103X_PAGE_SIZE_8KB); |
| } |
| } |
| |
| /* shift the tag access register in the TSB pointer down to index a |
| 16-byte TSB entry: */ |
| pointer_0_31 = (pointer_0_31 / (TME_STP103X_PAGE_SIZE_8KB / 16)) & (0 - (tme_uint32_t) 16); |
| |
| /* get bits 0..31 of the TSB register: */ |
| tsb_0_31 = mmu->tme_stp103x_mmu_tsb; |
| |
| /* get the size of (one half of) the TSB: */ |
| tsb_size = TME_STP103X_PAGE_SIZE_8KB; |
| tsb_size <<= (tsb_0_31 & TME_STP103X_TSB_SIZE); |
| |
| /* finish the offset of the entry in (one half of) the TSB: */ |
| pointer_0_31 &= (tsb_size - 1); |
| |
| /* if this is a split TSB: */ |
| if (tsb_0_31 & TME_STP103X_TSB_SPLIT) { |
| |
| /* if this is a 64KB page, select the other half of the TSB: */ |
| if (size_64KB) { |
| pointer_0_31 += tsb_size; |
| } |
| |
| /* the TSB is actually two halves: */ |
| tsb_size *= 2; |
| } |
| |
| /* finish bits 0..31 of the TSB pointer: */ |
| pointer_0_31 += (tsb_0_31 & (0 - tsb_size)); |
| |
| /* complete the load: */ |
| *ls->tme_sparc_ls_rd64 |
| = ((mmu->tme_stp103x_mmu_tsb |
| & (0 - (((tme_uint64_t) 1) << 32))) |
| | pointer_0_31); |
| ls->tme_sparc_ls_lsinfo |= TME_SPARC_LSINFO_LD_COMPLETED; |
| ls->tme_sparc_ls_size = 0; |
| } |
| |
| /* the ASI handler for ASI_ITLB_DATA_IN_REG, ASI_DTLB_DATA_IN_REG: */ |
| static void |
| _tme_stp103x_ls_asi_tlb_data_in(struct tme_sparc *ic, struct tme_sparc_ls *ls) |
| { |
| signed long tlb_part_i; |
| signed long tlb_part_i_invalid; |
| signed long tlb_part_i_unlocked; |
| signed long tlb_part_i_unlocked_unused; |
| tme_uint32_t tlb_data_32_63; |
| |
| /* if this is not a 64-bit store of address zero: */ |
| if (__tme_predict_false(ls->tme_sparc_ls_size != sizeof(tme_uint64_t) |
| || (ls->tme_sparc_ls_lsinfo & TME_SPARC_LSINFO_OP_ST) == 0 |
| || ls->tme_sparc_ls_address64 != 0)) { |
| |
| /* this is an illegal access: */ |
| ls->tme_sparc_ls_faults |= TME_STP103X_LS_FAULT_ILLEGAL; |
| } |
| |
| /* if this access has faulted: */ |
| if (__tme_predict_false(ls->tme_sparc_ls_faults != TME_SPARC_LS_FAULT_NONE)) { |
| return; |
| } |
| |
| /* if this is ASI_DTLB_DATA_IN_REG, start at the last entry of the |
| DTLB, otherwise start at the last entry of the ITLB: */ |
| tlb_part_i |
| = (TME_STP103X_ASI_MMU_MASK_IS_DMMU(ls->tme_sparc_ls_asi_mask) |
| ? TME_STP103X_TLB_PART_0_DMMU + (TME_STP103X_TLB_SIZE * 2) - 2 |
| : TME_STP103X_TLB_PART_0_IMMU + (TME_STP103X_TLB_SIZE * 2) - 2); |
| |
| /* search for invalid, unlocked, and unlocked+unused TLB entries: */ |
| tlb_part_i_invalid = -1; |
| tlb_part_i_unlocked = -1; |
| tlb_part_i_unlocked_unused = -1; |
| for (;;) { |
| |
| /* load bits 32..63 of the TLB entry's data: */ |
| tlb_data_32_63 = TME_STP103X(ic)->tme_stp103x_tlb_32s((tlb_part_i + 1), 1); |
| |
| /* if this TLB entry is invalid: */ |
| if ((tlb_data_32_63 & TME_STP103X_TLB_DATA_V(tlb_data_32_63)) == 0) { |
| |
| /* track the lowest-numbered invalid TLB entry: */ |
| tlb_part_i_invalid = tlb_part_i; |
| } |
| |
| /* if this TLB entry is not locked: */ |
| if ((TME_STP103X(ic)->tme_stp103x_tlb_32s((tlb_part_i + 1), 0) |
| & TME_STP103X_TLB_DATA_L((tme_uint32_t) 0)) == 0) { |
| |
| /* track the lowest-numbered unlocked TLB entry: */ |
| tlb_part_i_unlocked = tlb_part_i; |
| |
| /* if the TLB entry's used bit is clear: */ |
| if ((tlb_data_32_63 & TME_STP103X_TLB_DATA_DIAG_USED(tlb_data_32_63)) == 0) { |
| |
| /* track the lowest-numbered unlocked and unused TLB entry: */ |
| tlb_part_i_unlocked_unused = tlb_part_i; |
| } |
| } |
| |
| /* if we have not exhausted the TLB: */ |
| if (tlb_part_i % (TME_STP103X_TLB_SIZE * 2)) { |
| tlb_part_i -= 2; |
| continue; |
| } |
| |
| /* if there is an invalid TLB entry: */ |
| if (tlb_part_i_invalid >= 0) { |
| tlb_part_i = tlb_part_i_invalid; |
| break; |
| } |
| |
| /* otherwise, if there is an unlocked and unused TLB entry: */ |
| if (tlb_part_i_unlocked_unused >= 0) { |
| tlb_part_i = tlb_part_i_unlocked_unused; |
| } |
| |
| /* otherwise, there is no invalid TLB entry and no unlocked and |
| unused TLB entry: */ |
| else { |
| |
| /* clear the used bits on all TLB entries: */ |
| do { |
| TME_STP103X(ic)->tme_stp103x_tlb_32s((tlb_part_i + 1), 1) |
| &= ~TME_STP103X_TLB_DATA_DIAG_USED(tlb_data_32_63); |
| tlb_part_i += 2; |
| } while (tlb_part_i % (TME_STP103X_TLB_SIZE * 2)); |
| |
| /* there must be an unlocked TLB entry: */ |
| assert (tlb_part_i_unlocked >= 0); |
| tlb_part_i = tlb_part_i_unlocked; |
| } |
| |
| /* invalidate this TLB entry: */ |
| _tme_stp103x_tlb_invalidate(ic, tlb_part_i); |
| break; |
| } |
| |
| /* complete the store: */ |
| #if TME_STP103X_TLB_PART_0_DMMU >= TME_STP103X_TLB_PART_0_IMMU |
| #error "TME_STP103X_TLB_PART_0_DMMU or TME_STP103X_TLB_PART_0_IMMU changed" |
| #endif |
| TME_STP103X(ic)->tme_stp103x_tlb_64s(tlb_part_i + 0) |
| = (tlb_part_i < TME_STP103X_TLB_PART_0_IMMU |
| ? &TME_STP103X(ic)->tme_stp103x_dmmu |
| : &TME_STP103X(ic)->tme_stp103x_immu)->tme_stp103x_mmu_tag_access; |
| TME_STP103X(ic)->tme_stp103x_tlb_64s(tlb_part_i + 1) = *ls->tme_sparc_ls_rd64; |
| ls->tme_sparc_ls_size = 0; |
| } |
| |
| /* ASI_ITLB_DATA_ACCESS_REG, ASI_DTLB_DATA_ACCESS_REG: */ |
| static void |
| _tme_stp103x_ls_asi_tlb_data_access(struct tme_sparc *ic, struct tme_sparc_ls *ls) |
| { |
| unsigned long tlb_part_i; |
| tme_uint64_t *_tag_access; |
| |
| /* XXX FIXME WRITEME table 6-11 hints that it's possible to do a |
| casxa to DTLB_DATA_ACCESS_REG. also see the WRITEME in |
| _tme_stp103x_ls_trap(): */ |
| assert ((ls->tme_sparc_ls_lsinfo & TME_SPARC_LSINFO_OP_ATOMIC) == 0); |
| |
| /* if this is not a 64-bit load or store: */ |
| if (__tme_predict_false(ls->tme_sparc_ls_size != sizeof(tme_uint64_t) |
| || (ls->tme_sparc_ls_lsinfo |
| & (TME_SPARC_LSINFO_OP_LD |
| | TME_SPARC_LSINFO_OP_ST)) == 0)) { |
| |
| /* this is an illegal access: */ |
| ls->tme_sparc_ls_faults |= TME_STP103X_LS_FAULT_ILLEGAL; |
| } |
| |
| /* if this access has faulted: */ |
| if (__tme_predict_false(ls->tme_sparc_ls_faults != TME_SPARC_LS_FAULT_NONE)) { |
| return; |
| } |
| |
| /* get the addressed TLB entry and common MMU state: */ |
| tlb_part_i = ls->tme_sparc_ls_address64; |
| tlb_part_i %= TME_STP103X_TLB_SIZE * sizeof(tme_uint64_t); |
| tlb_part_i /= (sizeof(tme_uint64_t) / 2); |
| tlb_part_i += TME_STP103X_TLB_PART_0_DMMU; |
| _tag_access = &TME_STP103X(ic)->tme_stp103x_dmmu.tme_stp103x_mmu_tag_access; |
| if (!TME_STP103X_ASI_MMU_MASK_IS_DMMU(ls->tme_sparc_ls_asi_mask)) { |
| tlb_part_i = (tlb_part_i - TME_STP103X_TLB_PART_0_DMMU) + TME_STP103X_TLB_PART_0_IMMU; |
| _tag_access = &TME_STP103X(ic)->tme_stp103x_immu.tme_stp103x_mmu_tag_access; |
| } |
| |
| /* if this is a load: */ |
| if (ls->tme_sparc_ls_lsinfo & TME_SPARC_LSINFO_OP_LD) { |
| |
| /* complete the load: */ |
| *ls->tme_sparc_ls_rd64 = TME_STP103X(ic)->tme_stp103x_tlb_64s(tlb_part_i + 1); |
| ls->tme_sparc_ls_lsinfo |= TME_SPARC_LSINFO_LD_COMPLETED; |
| } |
| |
| /* otherwise, this is a store: */ |
| else { |
| |
| /* if the TLB entry is valid: */ |
| if (TME_STP103X(ic)->tme_stp103x_tlb_32s((tlb_part_i + 1), 1) |
| & TME_STP103X_TLB_DATA_V((tme_uint32_t) 0)) { |
| |
| /* invalidate this TLB entry: */ |
| _tme_stp103x_tlb_invalidate(ic, |
| tlb_part_i); |
| } |
| |
| /* complete the store: */ |
| TME_STP103X(ic)->tme_stp103x_tlb_64s(tlb_part_i + 0) = *_tag_access; |
| TME_STP103X(ic)->tme_stp103x_tlb_64s(tlb_part_i + 1) = *ls->tme_sparc_ls_rd64; |
| } |
| |
| /* we completed the load or store: */ |
| ls->tme_sparc_ls_size = 0; |
| } |
| |
| /* the ASI handler for ASI_ITLB_TAG_READ_REG, ASI_DTLB_TAG_READ_REG: */ |
| static void |
| _tme_stp103x_ls_asi_tlb_tag_read(struct tme_sparc *ic, struct tme_sparc_ls *ls) |
| { |
| unsigned long tlb_part_i; |
| |
| /* if this is not a 64-bit load: */ |
| if (__tme_predict_false(ls->tme_sparc_ls_size != sizeof(tme_uint64_t) |
| || (ls->tme_sparc_ls_lsinfo & TME_SPARC_LSINFO_OP_LD) == 0)) { |
| |
| /* this is an illegal access: */ |
| ls->tme_sparc_ls_faults |= TME_STP103X_LS_FAULT_ILLEGAL; |
| } |
| |
| /* if this access has faulted: */ |
| if (__tme_predict_false(ls->tme_sparc_ls_faults != TME_SPARC_LS_FAULT_NONE)) { |
| return; |
| } |
| |
| /* get the addressed TLB entry and common MMU state: */ |
| tlb_part_i = ls->tme_sparc_ls_address64; |
| tlb_part_i %= TME_STP103X_TLB_SIZE * sizeof(tme_uint64_t); |
| tlb_part_i /= (sizeof(tme_uint64_t) / 2); |
| tlb_part_i += TME_STP103X_TLB_PART_0_DMMU; |
| if (!TME_STP103X_ASI_MMU_MASK_IS_DMMU(ls->tme_sparc_ls_asi_mask)) { |
| tlb_part_i = (tlb_part_i - TME_STP103X_TLB_PART_0_DMMU) + TME_STP103X_TLB_PART_0_IMMU; |
| } |
| |
| /* complete the load: */ |
| *ls->tme_sparc_ls_rd64 = TME_STP103X(ic)->tme_stp103x_tlb_64s(tlb_part_i + 0); |
| ls->tme_sparc_ls_lsinfo |= TME_SPARC_LSINFO_LD_COMPLETED; |
| ls->tme_sparc_ls_size = 0; |
| } |
| |
| /* the ASI handler for ASI_IMMU_DEMAP, ASI_DMMU_DEMAP: */ |
| static void |
| _tme_stp103x_ls_asi_mmu_demap(struct tme_sparc *ic, struct tme_sparc_ls *ls) |
| { |
| tme_uint64_t address; |
| tme_uint32_t tlb_tag_match_32_63; |
| tme_uint32_t tlb_tag_match_0_31; |
| tme_uint32_t context; |
| tme_uint32_t tlb_tag_mask32; |
| unsigned long tlb_part_i; |
| tme_uint32_t tlb_tag_xor_32_63; |
| tme_uint32_t tlb_data_0_31; |
| tme_uint32_t tlb_tag_xor_0_31; |
| tme_uint32_t tlb_data_32_63; |
| tme_uint32_t size; |
| |
| /* if this is not a 64-bit store: */ |
| if (__tme_predict_false(ls->tme_sparc_ls_size != sizeof(tme_uint64_t) |
| || (ls->tme_sparc_ls_lsinfo & TME_SPARC_LSINFO_OP_ST) == 0)) { |
| |
| /* this is an illegal access: */ |
| ls->tme_sparc_ls_faults |= TME_STP103X_LS_FAULT_ILLEGAL; |
| } |
| |
| /* check the address: */ |
| address = _tme_stp103x_ls_address_check(NULL, ls); |
| tlb_tag_match_32_63 = address >> 32; |
| tlb_tag_match_0_31 = address; |
| |
| /* if this access has faulted: */ |
| if (__tme_predict_false(ls->tme_sparc_ls_faults != TME_SPARC_LS_FAULT_NONE)) { |
| return; |
| } |
| |
| /* we will complete this store: */ |
| ls->tme_sparc_ls_size = 0; |
| |
| /* assume that this demap uses the primary context: */ |
| context = ic->tme_sparc_memory_context_primary; |
| |
| /* if this demap might use the secondary context: */ |
| if (tlb_tag_match_0_31 & TME_BIT(4)) { |
| context = ic->tme_sparc_memory_context_secondary; |
| } |
| |
| /* if this demap uses the nucleus context: */ |
| if (tlb_tag_match_0_31 & TME_BIT(5)) { |
| context = 0; |
| |
| /* "Use of the reserved value causes the demap to be ignored" */ |
| if (tlb_tag_match_0_31 & TME_BIT(4)) { |
| return; |
| } |
| } |
| |
| /* if this is a demap page, we must match the VA part of a tag. if |
| this is a demap context, we must ignore the VA part of a tag: */ |
| tlb_tag_mask32 = 0 - (tme_uint32_t) ((tlb_tag_match_0_31 & TME_BIT(6)) == 0); |
| |
| /* finish the tag to match: */ |
| tlb_tag_match_0_31 &= ~TME_STP103X_CONTEXT_MAX; |
| tlb_tag_match_0_31 += context; |
| |
| /* if this is ASI_DMMU_DEMAP, start at the first entry of the DTLB, |
| otherwise start at the first entry of the ITLB: */ |
| tlb_part_i |
| = (TME_STP103X_ASI_MMU_MASK_IS_DMMU(ls->tme_sparc_ls_asi_mask) |
| ? TME_STP103X_TLB_PART_0_DMMU |
| : TME_STP103X_TLB_PART_0_IMMU); |
| |
| /* loop over the TLB entries: */ |
| do { |
| |
| /* if bits 32..63 of the tag match: */ |
| tlb_tag_xor_32_63 = TME_STP103X(ic)->tme_stp103x_tlb_32s((tlb_part_i + 0), 1) ^ tlb_tag_match_32_63; |
| if ((tlb_tag_xor_32_63 & tlb_tag_mask32) == 0) { |
| |
| /* load bits 0..31 of the data: */ |
| tlb_data_0_31 = TME_STP103X(ic)->tme_stp103x_tlb_32s((tlb_part_i + 1), 0); |
| |
| /* exclusive-OR bits 0..31 of the tag with bits 0..31 of the tag |
| to match: */ |
| tlb_tag_xor_0_31 = TME_STP103X(ic)->tme_stp103x_tlb_32s((tlb_part_i + 0), 0) ^ tlb_tag_match_0_31; |
| |
| /* if this is a global entry: */ |
| if (tlb_data_0_31 & TME_STP103X_TLB_DATA_G(tlb_data_0_31)) { |
| |
| /* assume that this is a demap page, which can match a global |
| page, and force a match of the context field of the tag: */ |
| tlb_tag_xor_0_31 &= ~TME_STP103X_CONTEXT_MAX; |
| |
| /* if this a demap context: */ |
| if (tlb_tag_mask32 == 0) { |
| |
| /* a demap context never matches a global page. force a |
| mismatch of the context field of the tag: */ |
| tlb_tag_xor_0_31 += 1; |
| } |
| } |
| |
| /* load bits 32..63 of the data: */ |
| tlb_data_32_63 = TME_STP103X(ic)->tme_stp103x_tlb_32s((tlb_part_i + 1), 1); |
| |
| /* if this TLB entry is valid: */ |
| if (tlb_data_32_63 & TME_STP103X_TLB_DATA_V(tlb_data_32_63)) { |
| |
| /* get the size of this mapping: */ |
| size |
| = (TME_STP103X_PAGE_SIZE_8KB |
| << (3 * TME_FIELD_MASK_EXTRACTU(tlb_data_32_63, |
| TME_STP103X_TLB_DATA_SIZE_MASK(tlb_data_32_63)))); |
| |
| /* if bits 0..31 of the tag match: */ |
| if ((tlb_tag_xor_0_31 |
| & (((0 - size) |
| & tlb_tag_mask32) |
| + TME_STP103X_CONTEXT_MAX)) == 0) { |
| |
| /* invalidate this TLB entry: */ |
| _tme_stp103x_tlb_invalidate(ic, tlb_part_i); |
| } |
| } |
| } |
| |
| tlb_part_i += 2; |
| } while (tlb_part_i % (2 * TME_STP103X_TLB_SIZE)); |
| } |
| |
| /* the ASI handler for ASI_ECACHE_W and ASI_ECACHE_R: */ |
| static void |
| _tme_stp103x_ls_asi_ecache(struct tme_sparc *ic, struct tme_sparc_ls *ls) |
| { |
| tme_uint64_t address; |
| tme_uint32_t address_0_31; |
| tme_uint32_t address_32_63; |
| unsigned int ecache_what; |
| int is_write; |
| |
| /* get the address: */ |
| address = ls->tme_sparc_ls_address64; |
| address_32_63 = (address >> 32); |
| address_0_31 = address; |
| |
| /* see if this is an E-Cache data access, or a tag/state/parity |
| access: */ |
| ecache_what = (address_32_63 >> (39 - 32)) & 0x3; |
| |
| /* truncate the E-Cache address to the cache size, and 64-bit align |
| it: */ |
| address_0_31 &= (TME_STP103X_ECACHE_SIZE - sizeof(tme_uint64_t)); |
| |
| /* see if this is ASI_ECACHE_W or ASI_ECACHE_R: */ |
| is_write = (TME_SPARC_ASI_MASK_WHICH(ls->tme_sparc_ls_asi_mask) == 0x76); |
| assert (is_write || TME_SPARC_ASI_MASK_WHICH(ls->tme_sparc_ls_asi_mask) == 0x7e); |
| |
| /* check the access: */ |
| if (__tme_predict_false((ls->tme_sparc_ls_lsinfo |
| & (is_write |
| ? TME_SPARC_LSINFO_OP_ST |
| : TME_SPARC_LSINFO_OP_LD)) == 0 |
| || ls->tme_sparc_ls_size != sizeof(tme_uint64_t) |
| || (ecache_what != 0x1 |
| && ecache_what != 0x2))) { |
| ls->tme_sparc_ls_faults |= TME_STP103X_LS_FAULT_ILLEGAL; |
| } |
| |
| /* if the access has faulted: */ |
| if (__tme_predict_false(ls->tme_sparc_ls_faults != TME_SPARC_LS_FAULT_NONE)) { |
| return; |
| } |
| |
| /* if this is a data access: */ |
| if (ecache_what == 0x1) { |
| |
| /* the PROM probes the size of the E-Cache by writing power-of-two |
| sizes to those same addresses in the E-Cache, from high sizes |
| to low sizes. then it reads address zero to see the last size |
| that got truncated to address zero. we don't emulate the |
| E-Cache, but we do emulate a single line at address zero, so we |
| appear to the probe as having the smallest E-Cache size |
| (512KB): */ |
| #if TME_STP103X_ECACHE_SIZE != (512 * 1024) |
| #error "TME_STP103X_ECACHE_SIZE changed" |
| #endif |
| if (address_0_31 == 0) { |
| if (is_write) { |
| TME_STP103X(ic)->tme_stp103x_ecache_data_probe = *ls->tme_sparc_ls_rd64; |
| } |
| else { |
| *ls->tme_sparc_ls_rd64 = TME_STP103X(ic)->tme_stp103x_ecache_data_probe; |
| } |
| } |
| else { |
| abort(); |
| } |
| } |
| |
| /* otherwise, this must be a tag access: */ |
| else { |
| assert (ecache_what == 0x2); |
| |
| /* the PROM initializes all tags in the E-Cache. we don't emulate |
| the E-Cache, but we do support initializing any tag: */ |
| if (is_write |
| && ((TME_STP103X(ic)->tme_stp103x_ecache_tag_data % (2 << 28)) |
| == (0x00000 /* a EC_tag of zero */ |
| + (0x0 << 22) /* an EC_state of Invalid */ |
| + (0xf << 25)))) { /* correct odd EC_parity */ |
| /* nothing to do */ |
| } |
| else { |
| abort(); |
| } |
| } |
| |
| /* complete the load or store: */ |
| if (!is_write) { |
| ls->tme_sparc_ls_lsinfo |= TME_SPARC_LSINFO_LD_COMPLETED; |
| } |
| ls->tme_sparc_ls_size = 0; |
| } |
| |
| /* the ASI handler for ASI_DCACHE_DATA and ASI_DCACHE_TAG: */ |
| static void |
| _tme_stp103x_ls_asi_dcache(struct tme_sparc *ic, struct tme_sparc_ls *ls) |
| { |
| tme_uint32_t address_0_31; |
| tme_uint64_t value; |
| |
| /* get the address: */ |
| address_0_31 = ls->tme_sparc_ls_address64 % (16 * 1024); |
| |
| /* check the access: */ |
| if (__tme_predict_false((ls->tme_sparc_ls_lsinfo |
| & (TME_SPARC_LSINFO_OP_ST |
| | TME_SPARC_LSINFO_OP_LD)) == 0 |
| || ls->tme_sparc_ls_size != sizeof(tme_uint64_t))) { |
| ls->tme_sparc_ls_faults |= TME_STP103X_LS_FAULT_ILLEGAL; |
| } |
| |
| /* if the access has faulted: */ |
| if (__tme_predict_false(ls->tme_sparc_ls_faults != TME_SPARC_LS_FAULT_NONE)) { |
| return; |
| } |
| |
| /* if this is a store: */ |
| if (ls->tme_sparc_ls_lsinfo & TME_SPARC_LSINFO_OP_ST) { |
| |
| /* get the value being stored: */ |
| value = *ls->tme_sparc_ls_rd64; |
| |
| /* we support writing zeros to tags - the PROM does this to |
| initialize all tags in the D-cache, and kernels do this to |
| flush the D-cache: */ |
| if (__tme_predict_true(TME_SPARC_ASI_MASK_WHICH(ls->tme_sparc_ls_asi_mask) == TME_STP103X_ASI_DCACHE_TAG |
| && value == 0)) { |
| |
| /* complete this store: */ |
| ls->tme_sparc_ls_size = 0; |
| return; |
| } |
| |
| /* soon after POR, the PROM writes 0xdeadbeef to address zero in |
| both ASI_DCACHE_DATA and ASI_DCACHE_TAG. we support these |
| writes: */ |
| if (address_0_31 == 0 |
| && value == 0xdeadbeef) { |
| |
| /* complete this store: */ |
| ls->tme_sparc_ls_size = 0; |
| return; |
| } |
| } |
| |
| /* otherwise, this is a load: */ |
| else { |
| |
| /* we support reading tags, which always read as zeroes. kernels |
| may read tags when flushing the D-cache: */ |
| if (__tme_predict_true(TME_SPARC_ASI_MASK_WHICH(ls->tme_sparc_ls_asi_mask) == TME_STP103X_ASI_DCACHE_TAG)) { |
| |
| /* complete this load: */ |
| *ls->tme_sparc_ls_rd64 = 0; |
| ls->tme_sparc_ls_lsinfo |= TME_SPARC_LSINFO_LD_COMPLETED; |
| ls->tme_sparc_ls_size = 0; |
| return; |
| } |
| } |
| |
| /* XXX FIXME WRITEME: */ |
| abort(); |
| } |
| |
| /* the ASI handler for ASI_ICACHE_INSTR, ASI_ICACHE_TAG, |
| ASI_ICACHE_PRE_DECODE, ASI_ICACHE_NEXT_FIELD: */ |
| static void |
| _tme_stp103x_ls_asi_icache(struct tme_sparc *ic, struct tme_sparc_ls *ls) |
| { |
| tme_uint32_t address_0_31; |
| tme_uint64_t value; |
| |
| /* get the address: */ |
| address_0_31 = ls->tme_sparc_ls_address64 % (16 * 1024); |
| |
| /* check the access: */ |
| if (__tme_predict_false((ls->tme_sparc_ls_lsinfo |
| & (TME_SPARC_LSINFO_OP_ST |
| | TME_SPARC_LSINFO_OP_LD)) == 0 |
| || ls->tme_sparc_ls_size != sizeof(tme_uint64_t))) { |
| ls->tme_sparc_ls_faults |= TME_STP103X_LS_FAULT_ILLEGAL; |
| } |
| |
| /* if the access has faulted: */ |
| if (__tme_predict_false(ls->tme_sparc_ls_faults != TME_SPARC_LS_FAULT_NONE)) { |
| return; |
| } |
| |
| /* if this is a store: */ |
| if (ls->tme_sparc_ls_lsinfo & TME_SPARC_LSINFO_OP_ST) { |
| |
| /* get the value being stored: */ |
| value = *ls->tme_sparc_ls_rd64; |
| |
| /* soon after POR, the PROM writes 0xdeadbeef to address zero in |
| both ASI_ICACHE_DATA and ASI_ICACHE_TAG. later, the PROM |
| initializes all tags in the I-cache. we don't emulate the |
| I-cache, but we do support these writes: */ |
| if ((address_0_31 == 0 |
| && value == 0xdeadbeef) |
| || (TME_SPARC_ASI_MASK_WHICH(ls->tme_sparc_ls_asi_mask) == 0x67 |
| && value == 0)) { |
| |
| /* complete this store: */ |
| ls->tme_sparc_ls_size = 0; |
| return; |
| } |
| } |
| |
| /* XXX FIXME WRITEME: */ |
| abort(); |
| } |
| |
| /* this swaps double-precision floating-point register values in the |
| memory buffer for the block transfer ASIs: */ |
| static void |
| _tme_stp103x_block_buffer_bswap(struct tme_sparc *ic, |
| const struct tme_sparc_ls *ls) |
| { |
| const struct tme_sparc_tlb *tlb; |
| tme_uint32_t endian_little; |
| signed int value_i; |
| |
| /* get the TLB entry: */ |
| tlb = ls->tme_sparc_ls_tlb; |
| |
| /* get the byte order of the memory: */ |
| endian_little = ls->tme_sparc_ls_lsinfo & TME_SPARC_LSINFO_ENDIAN_LITTLE; |
| |
| /* if the host and memory byte orders don't match: */ |
| if (TME_ENDIAN_NATIVE == TME_ENDIAN_LITTLE |
| ? !endian_little |
| : TME_ENDIAN_NATIVE == TME_ENDIAN_BIG |
| ? endian_little |
| : TRUE) { |
| |
| /* if the host is big- or little-endian: */ |
| if (TME_ENDIAN_NATIVE == TME_ENDIAN_LITTLE |
| || TME_ENDIAN_NATIVE == TME_ENDIAN_BIG) { |
| |
| /* swap the values in the memory buffer: */ |
| value_i = TME_STP103X_BLOCK_FPREGS_DOUBLE - 1; |
| do { |
| ic->tme_sparc_memory_buffer.tme_sparc_memory_buffer64s[value_i] |
| = tme_bswap_u64(ic->tme_sparc_memory_buffer.tme_sparc_memory_buffer64s[value_i]); |
| } while (--value_i >= 0); |
| } |
| |
| /* otherwise, the host has an unusual byte order: */ |
| else { |
| abort(); |
| } |
| } |
| } |
| |
| /* the cycle handler for loads with ASI_BLOCK_AS_IF_USER*, |
| ASI_BLK_COMMIT*, and ASI_BLOCK*: */ |
| static void |
| _tme_stp103x_ls_cycle_block_ld(struct tme_sparc *ic, struct tme_sparc_ls *ls) |
| { |
| unsigned int fpreg_number; |
| |
| /* do a load cycle: */ |
| tme_sparc64_load(ic, ls); |
| |
| /* if this was not the last cycle, return now: */ |
| if (ls->tme_sparc_ls_size != 0) { |
| return; |
| } |
| |
| /* swap the memory buffer: */ |
| _tme_stp103x_block_buffer_bswap(ic, ls); |
| |
| /* save the block load for verification: */ |
| tme_sparc_recode_verify_mem_block(ic, TME_SPARC_RECODE_VERIFY_MEM_LOAD); |
| |
| /* decode rd: */ |
| fpreg_number |
| = tme_sparc_fpu_fpreg_decode(ic, |
| TME_FIELD_MASK_EXTRACTU(ic->_tme_sparc_insn, |
| TME_SPARC_FORMAT3_MASK_RD), |
| TME_IEEE754_FPREG_FORMAT_DOUBLE); |
| |
| /* loop over a block's worth of double-precision floating-point |
| registers: */ |
| do { |
| |
| /* make sure the floating-point register is double-precision: */ |
| tme_sparc_fpu_fpreg_format(ic, |
| fpreg_number, |
| (TME_IEEE754_FPREG_FORMAT_DOUBLE |
| | TME_IEEE754_FPREG_FORMAT_BUILTIN)); |
| |
| /* copy the double-precision value from the memory buffer: */ |
| ic->tme_sparc_fpu_fpregs[fpreg_number].tme_float_format = TME_FLOAT_FORMAT_IEEE754_DOUBLE; |
| ic->tme_sparc_fpu_fpregs[fpreg_number].tme_float_value_ieee754_double.tme_value64_uint |
| = (ic->tme_sparc_memory_buffer.tme_sparc_memory_buffer64s |
| [(fpreg_number / 2) |
| % TME_STP103X_BLOCK_FPREGS_DOUBLE]); |
| |
| /* NB: tme_sparc64_lddfa() will eventually use |
| TME_SPARC_FPU_DIRTY() to mark the right half of the FPU |
| dirty: */ |
| |
| /* log the value loaded, except for the first, which will be |
| logged eventually by tme_sparc64_ldxa(): */ |
| if (((fpreg_number / 2) % TME_STP103X_BLOCK_FPREGS_DOUBLE) != 0) { |
| tme_sparc_log(ic, 1000, TME_OK, |
| (TME_SPARC_LOG_HANDLE(ic), |
| _("ldxa 0x%02x:0x%016" TME_PRIx64 ": 0x%016" TME_PRIx64), |
| TME_SPARC_ASI_MASK_WHICH(ls->tme_sparc_ls_asi_mask), |
| ((ls->tme_sparc_ls_address64 |
| - TME_STP103X_BLOCK_SIZE) |
| + (sizeof(tme_uint64_t) |
| * ((fpreg_number / 2) |
| % TME_STP103X_BLOCK_FPREGS_DOUBLE))), |
| ic->tme_sparc_fpu_fpregs[fpreg_number].tme_float_value_ieee754_double.tme_value64_uint)); |
| } |
| |
| } while ((fpreg_number += 2) |
| % (TME_STP103X_BLOCK_FPREGS_DOUBLE * 2)); |
| |
| /* complete this load for the lddfa function: */ |
| assert (ls->tme_sparc_ls_rd64 |
| == &ic->tme_sparc_ireg_uint64(TME_SPARC_IREG_FPX)); |
| ic->tme_sparc_ireg_uint64(TME_SPARC_IREG_FPX) |
| = ic->tme_sparc_memory_buffer.tme_sparc_memory_buffer64s[0]; |
| } |
| |
| /* the cycle handler for stores with ASI_BLOCK_AS_IF_USER*, |
| ASI_BLK_COMMIT*, and ASI_BLOCK*: */ |
| static void |
| _tme_stp103x_ls_cycle_block_st(struct tme_sparc *ic, struct tme_sparc_ls *ls) |
| { |
| unsigned int fpreg_number; |
| union tme_value64 value_double_buffer; |
| |
| /* this must be the first cycle: */ |
| assert (ls->tme_sparc_ls_buffer_offset == 0); |
| |
| /* decode rd: */ |
| fpreg_number |
| = tme_sparc_fpu_fpreg_decode(ic, |
| TME_FIELD_MASK_EXTRACTU(ic->_tme_sparc_insn, |
| TME_SPARC_FORMAT3_MASK_RD), |
| TME_IEEE754_FPREG_FORMAT_DOUBLE); |
| |
| /* loop over a block's worth of double-precision floating-point |
| registers: */ |
| do { |
| |
| /* make sure the floating-point register is double-precision: */ |
| tme_sparc_fpu_fpreg_format(ic, |
| fpreg_number, |
| (TME_IEEE754_FPREG_FORMAT_DOUBLE |
| | TME_IEEE754_FPREG_FORMAT_BUILTIN)); |
| |
| /* copy the double-precision value into the memory buffer: */ |
| ic->tme_sparc_memory_buffer.tme_sparc_memory_buffer64s |
| [(fpreg_number / 2) |
| % TME_STP103X_BLOCK_FPREGS_DOUBLE] |
| = (tme_ieee754_double_value_get(&ic->tme_sparc_fpu_fpregs[fpreg_number], |
| &value_double_buffer) |
| ->tme_value64_uint); |
| |
| /* log the value stored, except for the first, which was already |
| logged by tme_sparc64_stxa(): */ |
| if (((fpreg_number / 2) % TME_STP103X_BLOCK_FPREGS_DOUBLE) != 0) { |
| tme_sparc_log(ic, 1000, TME_OK, |
| (TME_SPARC_LOG_HANDLE(ic), |
| _("stxa 0x%02x:0x%016" TME_PRIx64 ": 0x%016" TME_PRIx64), |
| TME_SPARC_ASI_MASK_WHICH(ls->tme_sparc_ls_asi_mask), |
| (ls->tme_sparc_ls_address64 |
| + (sizeof(tme_uint64_t) |
| * ((fpreg_number / 2) |
| % TME_STP103X_BLOCK_FPREGS_DOUBLE))), |
| ic->tme_sparc_memory_buffer.tme_sparc_memory_buffer64s |
| [(fpreg_number / 2) |
| % TME_STP103X_BLOCK_FPREGS_DOUBLE])); |
| } |
| |
| } while ((fpreg_number += 2) |
| % (TME_STP103X_BLOCK_FPREGS_DOUBLE * 2)); |
| |
| /* save the block store for verification: */ |
| tme_sparc_recode_verify_mem_block(ic, TME_SPARC_RECODE_VERIFY_MEM_STORE); |
| |
| /* swap the memory buffer: */ |
| _tme_stp103x_block_buffer_bswap(ic, ls); |
| |
| /* do any leftover store cycles directly: */ |
| ls->tme_sparc_ls_cycle = tme_sparc64_store; |
| |
| /* do a store cycle: */ |
| tme_sparc64_store(ic, ls); |
| } |
| |
| /* the ASI handler for ASI_BLOCK_AS_IF_USER*, ASI_BLK_COMMIT*, and |
| ASI_BLOCK*: */ |
| static void |
| _tme_stp103x_ls_asi_block(struct tme_sparc *ic, struct tme_sparc_ls *ls) |
| { |
| tme_uint32_t insn; |
| |
| /* NB: tme_sparc64_lddfa() or tme_sparc64_stdfa() has already done |
| an TME_SPARC_INSN_FPU: */ |
| |
| /* we need to do the complete transfer: */ |
| /* NB: even if this is an stdfa, the TME_SPARC_LSINFO_LD_COMPLETED |
| won't cause any problems: */ |
| assert (sizeof(ic->tme_sparc_memory_buffer) >= TME_STP103X_BLOCK_SIZE); |
| ls->tme_sparc_ls_size = TME_STP103X_BLOCK_SIZE; |
| ls->tme_sparc_ls_buffer_offset = 0; |
| ls->tme_sparc_ls_lsinfo |
| |= (TME_SPARC_LSINFO_SLOW_CYCLES |
| | TME_SPARC_LSINFO_LD_COMPLETED); |
| |
| /* an instruction other than lddfa or stdfa, or an lddfa with an |
| ASI_BLK_COMMIT*, or with an rd that isn't a multiple of 16 is |
| illegal: */ |
| insn = ic->_tme_sparc_insn; |
| /* NB: we flip the stdfa op3 bits, and check that the whole op3 |
| field becomes zero (for ASI_BLK_COMMIT*) or that all op3 bits |
| except the one that differentiates stdfa from lddfa become zero |
| (for all other ASIs): */ |
| /* NB: we only need to test bits 1, 2, and 3 in the double-precision |
| encoded rd, since bit 0 is the encoded bit 5: */ |
| insn ^= (0x37 << 19); |
| if (__tme_predict_false((insn |
| & ((((TME_SPARC_ASI_MASK_WHICH(ls->tme_sparc_ls_asi_mask) |
| ^ TME_STP103X_ASI_BLK_COMMIT) |
| & ~TME_SPARC64_ASI_FLAG_SECONDARY) |
| ? (0x3b << 19) |
| : (0x3f << 19)) |
| | TME_BIT(3 + 25) |
| | TME_BIT(2 + 25) |
| | TME_BIT(1 + 25))))) { |
| ls->tme_sparc_ls_faults |= TME_STP103X_LS_FAULT_ILLEGAL; |
| } |
| |
| /* the address must be block-aligned: */ |
| else if (__tme_predict_false(((tme_uint32_t) ls->tme_sparc_ls_address64) |
| % TME_STP103X_BLOCK_SIZE)) { |
| ls->tme_sparc_ls_faults |= TME_SPARC_LS_FAULT_ADDRESS_NOT_ALIGNED; |
| } |
| |
| /* set the cycle function: */ |
| /* NB: we flipped the stdfa op3 bits in insn above, so bit two of |
| op3 is now set for an lddfa, and clear for an stdfa: */ |
| ls->tme_sparc_ls_cycle |
| = ((insn & (4 << 19)) |
| ? _tme_stp103x_ls_cycle_block_ld |
| : _tme_stp103x_ls_cycle_block_st); |
| } |
| |
| /* the ASI handler for the UDB registers: */ |
| static void |
| _tme_stp103x_ls_asi_udb(struct tme_sparc *ic, struct tme_sparc_ls *ls) |
| { |
| tme_uint32_t address_0_31; |
| int is_write; |
| tme_uint16_t value16; |
| unsigned int intr_reg; |
| |
| /* get the address: */ |
| address_0_31 = ls->tme_sparc_ls_address64; |
| |
| /* see if this is should be a write or a read: */ |
| is_write = (TME_SPARC_ASI_MASK_WHICH(ls->tme_sparc_ls_asi_mask) == 0x77); |
| assert (is_write || TME_SPARC_ASI_MASK_WHICH(ls->tme_sparc_ls_asi_mask) == 0x7f); |
| |
| /* check the access type and size, and that the address fits in 32 |
| bits: */ |
| if (__tme_predict_false((ls->tme_sparc_ls_lsinfo |
| & (is_write |
| ? TME_SPARC_LSINFO_OP_ST |
| : TME_SPARC_LSINFO_OP_LD)) == 0 |
| || ls->tme_sparc_ls_size != sizeof(tme_uint64_t) |
| || ls->tme_sparc_ls_address64 > (tme_uint32_t) (0 - (tme_uint32_t) 1))) { |
| ls->tme_sparc_ls_faults |= TME_STP103X_LS_FAULT_ILLEGAL; |
| } |
| |
| /* if the access hasn't faulted yet: */ |
| if (__tme_predict_true(ls->tme_sparc_ls_faults == TME_SPARC_LS_FAULT_NONE)) { |
| |
| /* dispatch on the address: */ |
| switch (address_0_31) { |
| |
| /* the low and high UDB error registers: */ |
| case 0x00: |
| case 0x18: |
| /* we never generate ECC errors, so these registers |
| always read as zero, and writes are ignored: */ |
| if (!is_write) { |
| *ls->tme_sparc_ls_rd64 = 0; |
| } |
| break; |
| |
| /* the low and high UDB control registers: */ |
| case 0x20: |
| case 0x38: |
| if (is_write) { |
| value16 = *ls->tme_sparc_ls_rd64; |
| if (value16 & TME_BIT(8)) { /* F_MODE */ |
| abort(); |
| } |
| TME_STP103X(ic)->tme_stp103x_udb_control[(address_0_31 & 8) == 0] = value16; |
| } |
| else { |
| *ls->tme_sparc_ls_rd64 |
| = ((0x00 << 9) /* UDB version number */ |
| + (TME_STP103X(ic)->tme_stp103x_udb_control[(address_0_31 & 8) == 0] |
| % (2 << 8))); /* F_MODE, FCBV */ |
| } |
| break; |
| |
| /* the UDB transmit and receive interrupt vector data: */ |
| case 0x40: |
| case 0x50: |
| case 0x60: |
| intr_reg = (address_0_31 - 0x40) / sizeof(tme_uint64_t); |
| if (is_write) { |
| TME_STP103X(ic)->tme_stp103x_udb_intr_transmit[intr_reg] |
| = *ls->tme_sparc_ls_rd64; |
| } |
| else { |
| *ls->tme_sparc_ls_rd64 |
| = TME_STP103X(ic)->tme_stp103x_udb_intr_receive[intr_reg]; |
| } |
| break; |
| |
| default: |
| |
| /* if this isn't a write of the UDB interrupt vector dispatch: */ |
| if (!is_write |
| || (address_0_31 & ~((2 << 18) - (1 << 14))) != 0x70) { |
| |
| /* this is an illegal access: */ |
| ls->tme_sparc_ls_faults |= TME_STP103X_LS_FAULT_ILLEGAL; |
| break; |
| } |
| |
| abort(); |
| break; |
| } |
| } |
| |
| /* if the access has faulted: */ |
| if (__tme_predict_false(ls->tme_sparc_ls_faults != TME_SPARC_LS_FAULT_NONE)) { |
| return; |
| } |
| |
| /* complete the load or store: */ |
| if (!is_write) { |
| ls->tme_sparc_ls_lsinfo |= TME_SPARC_LSINFO_LD_COMPLETED; |
| } |
| ls->tme_sparc_ls_size = 0; |
| } |
| |
| /* the ASI handlers: */ |
| static const _tme_sparc_ls_asi_handler _tme_stp103x_ls_asi_handlers[] = { |
| NULL, |
| _tme_stp103x_ls_asi_mmu, |
| _tme_stp103x_ls_asi_tsb_ptr, |
| _tme_stp103x_ls_asi_quad, |
| _tme_stp103x_ls_asi_tlb_data_in, |
| _tme_stp103x_ls_asi_mmu_demap, |
| _tme_stp103x_ls_asi_slow, |
| _tme_stp103x_ls_asi_phys, |
| _tme_stp103x_ls_asi_dcache, |
| _tme_stp103x_ls_asi_ecache, |
| _tme_stp103x_ls_asi_tlb_data_access, |
| _tme_stp103x_ls_asi_tlb_tag_read, |
| _tme_stp103x_ls_asi_icache, |
| _tme_stp103x_ls_asi_block, |
| _tme_stp103x_ls_asi_udb, |
| tme_sparc64_vis_ls_asi_pst, |
| tme_sparc64_vis_ls_asi_fl, |
| }; |
| |
| /* the tick compare register thread: */ |
| static void |
| _tme_stp103x_tick_compare_th(void *_ic) |
| { |
| struct tme_sparc *ic; |
| struct timeval now; |
| unsigned long now_tv_sec; |
| unsigned long now_tv_usec; |
| unsigned long tick_compare_time_tv_sec; |
| unsigned long tick_compare_time_tv_usec; |
| struct timeval sleep; |
| |
| /* recover our data structure: */ |
| ic = (struct tme_sparc *) _ic; |
| |
| /* lock the external mutex: */ |
| tme_mutex_lock(&ic->tme_sparc_external_mutex); |
| |
| /* loop forever: */ |
| for (;;) { |
| |
| /* get the current time: */ |
| tme_gettimeofday(&now); |
| |
| /* if the current time is greater than or equal to the tick compare time: */ |
| now_tv_sec = now.tv_sec; |
| now_tv_usec = now.tv_usec; |
| tick_compare_time_tv_sec = TME_STP103X(ic)->tme_stp103x_tick_compare_time.tv_sec; |
| tick_compare_time_tv_usec = TME_STP103X(ic)->tme_stp103x_tick_compare_time.tv_usec; |
| if (now_tv_sec > tick_compare_time_tv_sec |
| || (now_tv_sec == tick_compare_time_tv_sec |
| && now_tv_usec >= tick_compare_time_tv_usec)) { |
| |
| /* set the tick interrupt atomic flag: */ |
| tme_memory_atomic_write_flag(&TME_STP103X(ic)->tme_stp103x_sir_tick_int, TRUE); |
| |
| /* set the external flag: */ |
| tme_memory_atomic_write_flag(&ic->tme_sparc_external_flag, TRUE); |
| |
| /* notify any thread waiting on the external condition: */ |
| tme_cond_notify(&ic->tme_sparc_external_cond, FALSE); |
| |
| /* wait on the tick compare condition: */ |
| tme_cond_wait_yield(&TME_STP103X(ic)->tme_stp103x_tick_compare_cond, |
| &ic->tme_sparc_external_mutex); |
| } |
| |
| /* otherwise, the current time is less than the tick compare |
| time: */ |
| else { |
| |
| /* make the sleep time, but don't sleep more than a minute at a time: */ |
| if (tick_compare_time_tv_usec < now_tv_usec) { |
| tick_compare_time_tv_sec--; |
| tick_compare_time_tv_usec += 1000000; |
| } |
| sleep.tv_sec = TME_MIN(tick_compare_time_tv_sec - now_tv_sec, 60); |
| sleep.tv_usec = tick_compare_time_tv_usec - now_tv_usec; |
| |
| /* sleep on the tick compare condition: */ |
| tme_cond_sleep_yield(&TME_STP103X(ic)->tme_stp103x_tick_compare_cond, |
| &ic->tme_sparc_external_mutex, |
| &sleep); |
| } |
| } |
| /* NOTREACHED */ |
| } |
| |
| /* this checks for external signals: */ |
| /* NB: this may do a preinstruction trap: */ |
| static void |
| _tme_stp103x_external_check(struct tme_sparc *ic, |
| int flags) |
| { |
| |
| /* if RESET_L has been negated since the last check: */ |
| if (__tme_predict_false(tme_memory_atomic_read_flag(&ic->tme_sparc_external_reset_negated))) { |
| |
| /* clear the XIR and RESET_L asserted flags, then clear the |
| RESET_L negated flag: */ |
| tme_memory_atomic_write_flag(&ic->tme_sparc_external_halt_asserted, FALSE); |
| tme_memory_atomic_write_flag(&ic->tme_sparc_external_reset_asserted, FALSE); |
| tme_memory_barrier(ic, sizeof(*ic), TME_MEMORY_BARRIER_WRITE_BEFORE_WRITE); |
| tme_memory_atomic_write_flag(&ic->tme_sparc_external_reset_negated, FALSE); |
| |
| /* start POR trap processing: */ |
| if (flags & TME_SPARC_EXTERNAL_CHECK_MUTEX_LOCKED) { |
| tme_mutex_unlock(&ic->tme_sparc_external_mutex); |
| } |
| tme_sparc64_trap_preinstruction(ic, TME_SPARC64_TRAP_power_on_reset); |
| } |
| |
| /* if RESET_L is asserted: */ |
| if (__tme_predict_false(tme_memory_atomic_read_flag(&ic->tme_sparc_external_reset_asserted))) { |
| |
| /* halt: */ |
| if (flags & TME_SPARC_EXTERNAL_CHECK_MUTEX_LOCKED) { |
| tme_mutex_unlock(&ic->tme_sparc_external_mutex); |
| } |
| ic->_tme_sparc_mode = TME_SPARC_MODE_HALT; |
| tme_sparc_redispatch(ic); |
| } |
| |
| /* if XIR has been asserted since the last check: */ |
| if (__tme_predict_false(tme_memory_atomic_read_flag(&ic->tme_sparc_external_halt_asserted))) { |
| |
| /* clear the XIR asserted flag: */ |
| tme_memory_atomic_write_flag(&ic->tme_sparc_external_halt_asserted, FALSE); |
| |
| /* start XIR trap processing: */ |
| if (flags & TME_SPARC_EXTERNAL_CHECK_MUTEX_LOCKED) { |
| tme_mutex_unlock(&ic->tme_sparc_external_mutex); |
| } |
| tme_sparc64_trap_preinstruction(ic, TME_SPARC64_TRAP_externally_initiated_reset); |
| } |
| |
| /* do an interrupt check: */ |
| _tme_stp103x_interrupt_check(ic, flags); |
| } |
| |
| /* the bus cycle function: */ |
| static void |
| _tme_stp103x_ls_bus_cycle(const struct tme_sparc *ic, |
| struct tme_sparc_ls *ls) |
| { |
| tme_uint32_t asi_mask; |
| unsigned int cycle_size_log2; |
| |
| /* NB: we provide the old sparc32 bus routing information when |
| emulating stp103x noncached read and write transactions on the |
| UPA bus. |
| |
| this is a bad hack that we do only to save duplicating that same |
| information in the stp2220 emulation, which needs it for cycles |
| that pass through it onto its SBus. |
| |
| since the old sparc32 bus routing information doesn't have |
| anything to do with how the UPA bus works, we assume that all TLB |
| fills for noncacheable accesses that don't allow fast transfers |
| will either end up at another CPU (which will know about the |
| disagreement and ignore the bus routing information), or end up |
| at an I/O bridge, which will either rely on the sparc32 bus |
| routing information (stp2220), or replace it with its own bus |
| routing information (stp2222), before running the cycle on its |
| I/O bus. if the cycle is for an I/O bridge's internal registers, |
| like a CPU it will know about the disagreement and ignore the bus |
| routing information. |
| |
| we assume that all TLB fills for cacheable accesses target main |
| memory, which we assume will tolerate any bus routing: */ |
| |
| /* get the ASI mask for this TLB entry: */ |
| asi_mask = ls->tme_sparc_ls_tlb->tme_sparc_tlb_asi_mask; |
| |
| /* if the TLB entry is for noncacheable accesses: */ |
| if (asi_mask & TME_SPARC64_ASI_MASK_FLAG_TLB_UNCACHEABLE) { |
| |
| /* call the default sparc32 bus cycle function: */ |
| tme_sparc32_ls_bus_cycle(ic, ls); |
| return; |
| } |
| |
| /* provide a simple bus routing for cacheable accesses: */ |
| cycle_size_log2 = TME_BUS8_LOG2; |
| for (; (1 << cycle_size_log2) != ls->tme_sparc_ls_bus_cycle.tme_bus_cycle_size; ) { |
| assert (cycle_size_log2 < TME_BUS128_LOG2); |
| cycle_size_log2++; |
| } |
| ls->tme_sparc_ls_bus_cycle.tme_bus_cycle_port = TME_BUS_CYCLE_PORT(0, TME_BUS128_LOG2); |
| ls->tme_sparc_ls_bus_cycle.tme_bus_cycle_lane_routing |
| = (&(_tme_stp103x_bus_router_cacheable |
| [cycle_size_log2] |
| [0]) |
| - TME_BUS_ROUTER_INDEX(TME_BUS128_LOG2, TME_BUS128_LOG2, 0)); |
| } |
| |
| /* this fills a TLB for the CPU: */ |
| static int |
| _tme_stp103x_tlb_fill(struct tme_bus_connection *conn_bus, |
| struct tme_bus_tlb *tlb, |
| tme_bus_addr_t address, |
| unsigned int cycle_type) |
| { |
| abort(); |
| } |
| |
| /* this handles an interrupt: */ |
| static void |
| _tme_stp103x_interrupt(struct tme_upa_bus_connection *conn_upa, |
| tme_uint32_t master_mid, |
| const tme_uint64_t *data, |
| struct tme_completion *completion) |
| { |
| struct tme_sparc *ic; |
| |
| /* recover our data structure: */ |
| ic = conn_upa->tme_upa_bus_connection.tme_bus_connection.tme_connection_element->tme_element_private; |
| |
| /* if the receive interrupt vector data is already busy: */ |
| if (tme_memory_atomic_read_flag(&TME_STP103X(ic)->tme_stp103x_intr_receive_busy)) { |
| |
| /* NACK this interrupt: */ |
| completion->tme_completion_error = EAGAIN; |
| tme_memory_barrier(completion, sizeof(*completion), TME_MEMORY_BARRIER_WRITE_BEFORE_WRITE); |
| } |
| |
| /* otherwise, the incoming data isn't busy: */ |
| else { |
| |
| /* save the interrupt data and ACK this interrupt: */ |
| /* NB: the interrupt data is an array of eight big-endian |
| tme_uint64_t, since an interrupt packet is four 128-bit values. |
| the even indices are the least-significant halves of the |
| 128-bit values: */ |
| TME_STP103X(ic)->tme_stp103x_intr_receive_mid = master_mid; |
| TME_STP103X(ic)->tme_stp103x_udb_intr_receive[0] = tme_betoh_u64(data[2 * 0]); |
| TME_STP103X(ic)->tme_stp103x_udb_intr_receive[1] = tme_betoh_u64(data[2 * 1]); |
| TME_STP103X(ic)->tme_stp103x_udb_intr_receive[2] = tme_betoh_u64(data[2 * 2]); |
| completion->tme_completion_error = TME_OK; |
| tme_memory_barrier(0, 0, TME_MEMORY_BARRIER_WRITE_BEFORE_WRITE); |
| tme_memory_atomic_write_flag(&TME_STP103X(ic)->tme_stp103x_intr_receive_busy, 1); |
| tme_memory_barrier(ic, sizeof(*ic), TME_MEMORY_BARRIER_WRITE_BEFORE_WRITE); |
| tme_memory_atomic_write_flag(&ic->tme_sparc_external_flag, 1); |
| tme_cond_notify(&ic->tme_sparc_external_cond, FALSE); |
| } |
| |
| /* validate this completion: */ |
| tme_completion_validate(completion); |
| } |
| |
| /* this returns the version of an FPU for an stp103x: */ |
| static tme_uint32_t |
| _tme_sparc_fpu_ver_stp103x(struct tme_sparc *ic, const char *fpu_name, char **_output) |
| { |
| tme_uint32_t ver; |
| |
| /* if we're returning a usage: */ |
| if (_output != NULL) { |
| tme_output_append_error(_output, |
| "builtin"); |
| return (TME_SPARC_FSR_VER_missing); |
| } |
| |
| if (TME_ARG_IS(fpu_name, "builtin")) { |
| /* XXX FIXME - the stp1030 has an FSR_version of zero. we assume |
| that the stp1031 does too: */ |
| ver = 0; |
| } |
| else { |
| return (TME_SPARC_FSR_VER_missing); |
| } |
| |
| ic->tme_sparc_fpu_flags |
| = (!TME_SPARC_FPU_FLAG_OK_REG_MISALIGNED); |
| return (ver * _TME_FIELD_MASK_FACTOR(TME_SPARC_FSR_VER)); |
| } |
| |
| /* this creates and returns a new stp103x: */ |
| static int |
| _tme_stp103x_new(struct tme_element *element, |
| const char * const *args, |
| const void *extra, |
| char **_output, |
| int is_1030) |
| { |
| struct tme_sparc *ic; |
| tme_uint32_t psr; |
| tme_uint32_t asi; |
| tme_uint32_t asi_mask_flags; |
| void (*handler) _TME_P((struct tme_sparc *, struct tme_sparc_ls *)); |
| tme_uint32_t handler_i; |
| |
| /* allocate the stp103x structure: */ |
| ic = &tme_new0(struct tme_stp103x, 1)->tme_stp103x_sparc; |
| ic->tme_sparc_element = element; |
| |
| /* set the type: */ |
| is_1030 = !!is_1030; |
| TME_STP103X(ic)->tme_stp103x_is_1030 = is_1030; |
| if (TME_STP103X_IS_1030(ic) != is_1030) { |
| tme_free(ic); |
| return (ENXIO); |
| } |
| |
| /* initialize the synchronization parts of the structure: */ |
| tme_sparc_sync_init(ic); |
| |
| /* initialize the stp103x private structure: */ |
| TME_STP103X(ic)->tme_stp103x_tcr = TME_STP103X_TCR_INT_DIS; |
| tme_cond_init(&TME_STP103X(ic)->tme_stp103x_tick_compare_cond); |
| tme_misc_timeval_never(&TME_STP103X(ic)->tme_stp103x_tick_compare_time); |
| tme_memory_atomic_init_flag(&TME_STP103X(ic)->tme_stp103x_sir_tick_int, FALSE); |
| tme_memory_atomic_init_flag(&TME_STP103X(ic)->tme_stp103x_intr_receive_busy, FALSE); |
| |
| /* start the tick compare thread: */ |
| tme_thread_create((tme_thread_t) _tme_stp103x_tick_compare_th, ic); |
| |
| /* fill in the stp103x-specific parts of the structure: */ |
| psr = 0; |
| TME_FIELD_MASK_DEPOSITU(psr, TME_SPARC32_PSR_IMPL, 1); |
| TME_FIELD_MASK_DEPOSITU(psr, TME_SPARC32_PSR_VER, 1); |
| ic->tme_sparc32_ireg_psr = psr; |
| ic->tme_sparc_version = TME_SPARC_VERSION(ic); |
| ic->tme_sparc_nwindows = TME_SPARC_NWINDOWS(ic); |
| ic->tme_sparc_memory_flags = TME_SPARC_MEMORY_FLAGS(ic); |
| ic->tme_sparc64_maxtl = TME_STP103X_MAXTL; |
| ic->tme_sparc_tlb_page_size_log2 = 13; /* log2(TME_STP103X_PAGE_SIZE_8KB) */ |
| ic->tme_sparc_memory_context_max = TME_STP103X_CONTEXT_MAX; |
| ic->_tme_sparc64_execute_opmap = _TME_SPARC_EXECUTE_OPMAP; |
| ic->tme_sparc64_rstvaddr = 0 - (tme_uint64_t) (1 << 28); |
| ic->tme_sparc64_ireg_va_hole_start = TME_STP103X_VA_HOLE_START; |
| ic->tme_sparc64_ireg_ver |
| = ((0x0017 * _TME_FIELD_MASK_FACTOR(TME_SPARC64_VER_MANUF)) |
| + (0x0010 * _TME_FIELD_MASK_FACTOR(TME_SPARC64_VER_IMPL)) |
| + (0x40 * _TME_FIELD_MASK_FACTOR(TME_SPARC64_VER_MASK)) |
| + (ic->tme_sparc64_maxtl * _TME_FIELD_MASK_FACTOR(TME_SPARC64_VER_MAXTL)) |
| + ((TME_SPARC_NWINDOWS(ic) - 1) * _TME_FIELD_MASK_FACTOR(TME_SPARC64_VER_MAXWIN))); |
| ic->tme_sparc64_ireg_winstates_mask |
| = (TME_SPARC64_WINSTATES_CWP(TME_SPARC_NWINDOWS(ic) - 1) |
| + TME_SPARC64_WINSTATES_CANRESTORE(TME_SPARC_NWINDOWS(ic) - 1) |
| + TME_SPARC64_WINSTATES_CANSAVE(TME_SPARC_NWINDOWS(ic) - 1) |
| + TME_SPARC64_WINSTATES_OTHERWIN(TME_SPARC_NWINDOWS(ic) - 1)); |
| ic->_tme_sparc_execute = _tme_sparc_execute_stp103x; |
| ic->_tme_sparc_fpu_ver = _tme_sparc_fpu_ver_stp103x; |
| ic->_tme_sparc_external_check = _tme_stp103x_external_check; |
| ic->_tme_sparc_tlb_fill = _tme_stp103x_tlb_fill; |
| ic->_tme_sparc_upa_interrupt = _tme_stp103x_interrupt; |
| ic->_tme_sparc_ls_asi_misaligned = tme_sparc64_vis_ls_asi_misaligned; |
| ic->_tme_sparc_ls_asi_handlers = _tme_stp103x_ls_asi_handlers; |
| ic->_tme_sparc_ls_address_map = _tme_stp103x_ls_address_map; |
| ic->_tme_sparc_ls_bus_cycle = _tme_stp103x_ls_bus_cycle; |
| ic->_tme_sparc_ls_bus_fault = tme_sparc_ls_bus_fault; |
| ic->_tme_sparc_ls_trap = _tme_stp103x_ls_trap; |
| ic->_tme_sparc64_update_pstate = _tme_stp103x_update_pstate; |
| ic->tme_sparc_vis_ls_fault_illegal = TME_STP103X_LS_FAULT_ILLEGAL; |
| ic->tme_sparc_timing_loop_cycles_each = 1; |
| #ifdef _TME_SPARC_RECODE_VERIFY |
| /* NB: struct tme_stp103x has been deliberately laid out to have all |
| of the verifiable contents first, followed by everything that |
| isn't verifiable (basically, anything that is accessed using |
| loads and stores, since replay only simulates them): */ |
| ic->tme_sparc_recode_verify_ic_size |
| = (((char *) &((struct tme_stp103x *) 0)->tme_stp103x_upa_config) |
| - (char *) ((struct tme_stp103x *) 0)); |
| ic->tme_sparc_recode_verify_ic_size_total = sizeof(struct tme_stp103x); |
| #endif /* _TME_SPARC_RECODE_VERIFY */ |
| |
| /* initialize the ASIs: */ |
| for (asi = 0; asi < TME_ARRAY_ELS(ic->tme_sparc_asis); asi++) { |
| |
| /* dispatch on this ASI: */ |
| switch (asi) { |
| |
| /* ASI_PHYS_USE_EC*, ASI_PHYS_BYPASS_EC_WITH_EBIT*: */ |
| case (0x14): |
| case (0x14 |
| + TME_SPARC64_ASI_FLAG_LITTLE): |
| case (0x15): |
| case (0x15 |
| + TME_SPARC64_ASI_FLAG_LITTLE): |
| asi_mask_flags = TME_SPARC64_ASI_MASK_FLAG_SPECIAL; |
| handler = _tme_stp103x_ls_asi_phys; |
| break; |
| |
| /* ASI_NUCLEUS_QUAD_LDD*: */ |
| case (0x24): |
| case (0x24 |
| + TME_SPARC64_ASI_FLAG_LITTLE): |
| asi_mask_flags |
| = (TME_SPARC64_ASI_MASK_FLAG_INSN_NUCLEUS |
| + (asi |
| & TME_SPARC64_ASI_FLAG_LITTLE)); |
| handler = _tme_stp103x_ls_asi_quad; |
| break; |
| |
| /* ASI_DCACHE_DATA, ASI_DCACHE_TAG: */ |
| case TME_STP103X_ASI_DCACHE_DATA: |
| case TME_STP103X_ASI_DCACHE_TAG: |
| asi_mask_flags = TME_SPARC64_ASI_MASK_FLAG_SPECIAL; |
| handler = _tme_stp103x_ls_asi_dcache; |
| break; |
| |
| /* ASI_ECACHE_W, ASI_ECACHE_R: */ |
| case 0x76: |
| case 0x7e: |
| asi_mask_flags = TME_SPARC64_ASI_MASK_FLAG_SPECIAL; |
| handler = _tme_stp103x_ls_asi_ecache; |
| break; |
| |
| /* ASI_IMMU, ASI_DMMU: */ |
| case TME_STP103X_ASI_IMMU: |
| case TME_STP103X_ASI_DMMU: |
| asi_mask_flags = TME_SPARC64_ASI_MASK_FLAG_SPECIAL; |
| handler = _tme_stp103x_ls_asi_mmu; |
| break; |
| |
| /* ASI_IMMU_TSB_8KB_PTR_REG, ASI_IMMU_TSB_64KB_PTR_REG, |
| ASI_DMMU_TSB_8KB_PTR_REG, ASI_DMMU_TSB_64KB_PTR_REG, |
| ASI_DMMU_TSB_DIRECT_PTR_REG: */ |
| case (TME_STP103X_ASI_IMMU |
| + TME_STP103X_ASI_FLAG_TSB_8KB_PTR): |
| case (TME_STP103X_ASI_IMMU |
| + TME_STP103X_ASI_FLAG_TSB_64KB_PTR): |
| case (TME_STP103X_ASI_DMMU |
| + TME_STP103X_ASI_FLAG_TSB_8KB_PTR): |
| case (TME_STP103X_ASI_DMMU |
| + TME_STP103X_ASI_FLAG_TSB_64KB_PTR): |
| case (TME_STP103X_ASI_DMMU |
| + (TME_STP103X_ASI_FLAG_TSB_8KB_PTR |
| | TME_STP103X_ASI_FLAG_TSB_64KB_PTR)): |
| asi_mask_flags = TME_SPARC64_ASI_MASK_FLAG_SPECIAL; |
| handler = _tme_stp103x_ls_asi_tsb_ptr; |
| break; |
| |
| /* ASI_ITLB_DATA_IN_REG, ASI_DTLB_DATA_IN_REG: */ |
| case (TME_STP103X_ASI_IMMU + 0x4): |
| case (TME_STP103X_ASI_DMMU + 0x4): |
| asi_mask_flags = TME_SPARC64_ASI_MASK_FLAG_SPECIAL; |
| handler = _tme_stp103x_ls_asi_tlb_data_in; |
| break; |
| |
| /* ASI_ITLB_DATA_ACCESS_REG, ASI_DTLB_DATA_ACCESS_REG: */ |
| case (TME_STP103X_ASI_IMMU + 0x5): |
| case (TME_STP103X_ASI_DMMU + 0x5): |
| asi_mask_flags = TME_SPARC64_ASI_MASK_FLAG_SPECIAL; |
| handler = _tme_stp103x_ls_asi_tlb_data_access; |
| break; |
| |
| /* ASI_ITLB_TAG_READ_REG, ASI_DTLB_TAG_READ_REG: */ |
| case (TME_STP103X_ASI_IMMU + 0x6): |
| case (TME_STP103X_ASI_DMMU + 0x6): |
| asi_mask_flags = TME_SPARC64_ASI_MASK_FLAG_SPECIAL; |
| handler = _tme_stp103x_ls_asi_tlb_tag_read; |
| break; |
| |
| /* ASI_IMMU_DEMAP, ASI_DMMU_DEMAP: */ |
| case (TME_STP103X_ASI_IMMU + 0x7): |
| case (TME_STP103X_ASI_DMMU + 0x7): |
| asi_mask_flags = TME_SPARC64_ASI_MASK_FLAG_SPECIAL; |
| handler = _tme_stp103x_ls_asi_mmu_demap; |
| break; |
| |
| /* ASI_ICACHE_INSTR: */ |
| /* ASI_ICACHE_TAG: */ |
| /* ASI_ICACHE_PRE_DECODE: */ |
| /* ASI_ICACHE_NEXT_FIELD: */ |
| case 0x66: |
| case 0x67: |
| case 0x6e: |
| case 0x6f: |
| asi_mask_flags = TME_SPARC64_ASI_MASK_FLAG_SPECIAL; |
| handler = _tme_stp103x_ls_asi_icache; |
| break; |
| |
| /* ASI_BLOCK_AS_IF_USER*: */ |
| case (0x70): |
| case (0x70 |
| + TME_SPARC64_ASI_FLAG_SECONDARY): |
| case (0x70 |
| + TME_SPARC64_ASI_FLAG_LITTLE): |
| case (0x70 |
| + TME_SPARC64_ASI_FLAG_SECONDARY |
| + TME_SPARC64_ASI_FLAG_LITTLE): |
| asi_mask_flags |
| = (TME_SPARC64_ASI_MASK_FLAG_INSN_AS_IF_USER |
| + (asi |
| & (TME_SPARC64_ASI_FLAG_SECONDARY |
| | TME_SPARC64_ASI_FLAG_LITTLE))); |
| handler = _tme_stp103x_ls_asi_block; |
| break; |
| |
| /* ASI_UDB*: */ |
| case 0x77: |
| case 0x7f: |
| asi_mask_flags = TME_SPARC64_ASI_MASK_FLAG_SPECIAL; |
| handler = _tme_stp103x_ls_asi_udb; |
| break; |
| |
| /* the mandatory v9 ASIs: */ |
| #if TME_SPARC64_ASI_MASK_FLAG_INSN_NUCLEUS != 4 |
| #error "TME_SPARC64_ASI_MASK_FLAG_INSN_NUCLEUS changed" |
| #endif |
| case (TME_SPARC64_ASI_MASK_FLAG_INSN_NUCLEUS): |
| case (TME_SPARC64_ASI_MASK_FLAG_INSN_NUCLEUS |
| + TME_SPARC64_ASI_FLAG_LITTLE): |
| #if TME_SPARC64_ASI_MASK_FLAG_INSN_AS_IF_USER != 0x10 |
| #error "TME_SPARC64_ASI_MASK_FLAG_INSN_AS_IF_USER changed" |
| #endif |
| case (TME_SPARC64_ASI_MASK_FLAG_INSN_AS_IF_USER): |
| case (TME_SPARC64_ASI_MASK_FLAG_INSN_AS_IF_USER |
| + TME_SPARC64_ASI_FLAG_SECONDARY): |
| case (TME_SPARC64_ASI_MASK_FLAG_INSN_AS_IF_USER |
| + TME_SPARC64_ASI_FLAG_LITTLE): |
| case (TME_SPARC64_ASI_MASK_FLAG_INSN_AS_IF_USER |
| + TME_SPARC64_ASI_FLAG_SECONDARY |
| + TME_SPARC64_ASI_FLAG_LITTLE): |
| case (TME_SPARC64_ASI_FLAG_UNRESTRICTED): |
| case (TME_SPARC64_ASI_FLAG_UNRESTRICTED |
| + TME_SPARC64_ASI_FLAG_SECONDARY): |
| case (TME_SPARC64_ASI_FLAG_UNRESTRICTED |
| + TME_SPARC64_ASI_FLAG_NO_FAULT): |
| case (TME_SPARC64_ASI_FLAG_UNRESTRICTED |
| + TME_SPARC64_ASI_FLAG_SECONDARY |
| + TME_SPARC64_ASI_FLAG_NO_FAULT): |
| case (TME_SPARC64_ASI_FLAG_UNRESTRICTED |
| + TME_SPARC64_ASI_FLAG_LITTLE): |
| case (TME_SPARC64_ASI_FLAG_UNRESTRICTED |
| + TME_SPARC64_ASI_FLAG_SECONDARY |
| + TME_SPARC64_ASI_FLAG_LITTLE): |
| case (TME_SPARC64_ASI_FLAG_UNRESTRICTED |
| + TME_SPARC64_ASI_FLAG_NO_FAULT |
| + TME_SPARC64_ASI_FLAG_LITTLE): |
| case (TME_SPARC64_ASI_FLAG_UNRESTRICTED |
| + TME_SPARC64_ASI_FLAG_SECONDARY |
| + TME_SPARC64_ASI_FLAG_NO_FAULT |
| + TME_SPARC64_ASI_FLAG_LITTLE): |
| |
| /* the mandatory v9 ASIs are all normal and have no handlers: */ |
| asi_mask_flags = asi; |
| handler = NULL; |
| assert ((asi_mask_flags & TME_SPARC64_ASI_MASK_FLAG_SPECIAL) == 0); |
| assert (_tme_stp103x_ls_asi_handlers[0] == handler); |
| break; |
| |
| /* ASI_PST*: */ |
| case (TME_SPARC_VIS_ASI_PST8): |
| case (TME_SPARC_VIS_ASI_PST8 |
| + TME_SPARC64_ASI_FLAG_SECONDARY): |
| case (TME_SPARC_VIS_ASI_PST8 |
| + TME_SPARC64_ASI_FLAG_LITTLE): |
| case (TME_SPARC_VIS_ASI_PST8 |
| + TME_SPARC64_ASI_FLAG_SECONDARY |
| + TME_SPARC64_ASI_FLAG_LITTLE): |
| case (TME_SPARC_VIS_ASI_PST16): |
| case (TME_SPARC_VIS_ASI_PST16 |
| + TME_SPARC64_ASI_FLAG_SECONDARY): |
| case (TME_SPARC_VIS_ASI_PST16 |
| + TME_SPARC64_ASI_FLAG_LITTLE): |
| case (TME_SPARC_VIS_ASI_PST16 |
| + TME_SPARC64_ASI_FLAG_SECONDARY |
| + TME_SPARC64_ASI_FLAG_LITTLE): |
| case (TME_SPARC_VIS_ASI_PST32): |
| case (TME_SPARC_VIS_ASI_PST32 |
| + TME_SPARC64_ASI_FLAG_SECONDARY): |
| case (TME_SPARC_VIS_ASI_PST32 |
| + TME_SPARC64_ASI_FLAG_LITTLE): |
| case (TME_SPARC_VIS_ASI_PST32 |
| + TME_SPARC64_ASI_FLAG_SECONDARY |
| + TME_SPARC64_ASI_FLAG_LITTLE): |
| asi_mask_flags |
| = (asi |
| & (TME_SPARC64_ASI_FLAG_SECONDARY |
| | TME_SPARC64_ASI_FLAG_LITTLE)); |
| handler = tme_sparc64_vis_ls_asi_pst; |
| break; |
| |
| /* ASI_FL*: */ |
| case (TME_SPARC_VIS_ASI_FL8): |
| case (TME_SPARC_VIS_ASI_FL8 |
| + TME_SPARC64_ASI_FLAG_SECONDARY): |
| case (TME_SPARC_VIS_ASI_FL8 |
| + TME_SPARC64_ASI_FLAG_LITTLE): |
| case (TME_SPARC_VIS_ASI_FL8 |
| + TME_SPARC64_ASI_FLAG_SECONDARY |
| + TME_SPARC64_ASI_FLAG_LITTLE): |
| case (TME_SPARC_VIS_ASI_FL16): |
| case (TME_SPARC_VIS_ASI_FL16 |
| + TME_SPARC64_ASI_FLAG_SECONDARY): |
| case (TME_SPARC_VIS_ASI_FL16 |
| + TME_SPARC64_ASI_FLAG_LITTLE): |
| case (TME_SPARC_VIS_ASI_FL16 |
| + TME_SPARC64_ASI_FLAG_SECONDARY |
| + TME_SPARC64_ASI_FLAG_LITTLE): |
| asi_mask_flags |
| = (asi |
| & (TME_SPARC64_ASI_FLAG_SECONDARY |
| | TME_SPARC64_ASI_FLAG_LITTLE)); |
| handler = tme_sparc64_vis_ls_asi_fl; |
| break; |
| |
| /* ASI_BLK_COMMIT*: */ |
| case (TME_STP103X_ASI_BLK_COMMIT): |
| case (TME_STP103X_ASI_BLK_COMMIT |
| + TME_SPARC64_ASI_FLAG_SECONDARY): |
| asi_mask_flags |
| = (asi |
| & (TME_SPARC64_ASI_FLAG_SECONDARY)); |
| handler = _tme_stp103x_ls_asi_block; |
| break; |
| |
| /* ASI_BLOCK*: */ |
| case (0xf0): |
| case (0xf0 |
| + TME_SPARC64_ASI_FLAG_SECONDARY): |
| case (0xf0 |
| + TME_SPARC64_ASI_FLAG_LITTLE): |
| case (0xf0 |
| + TME_SPARC64_ASI_FLAG_SECONDARY |
| + TME_SPARC64_ASI_FLAG_LITTLE): |
| asi_mask_flags |
| = (asi |
| & (TME_SPARC64_ASI_FLAG_SECONDARY |
| | TME_SPARC64_ASI_FLAG_LITTLE)); |
| handler = _tme_stp103x_ls_asi_block; |
| break; |
| |
| case TME_STP103X_ASI_LSU_CONTROL_REG: |
| case TME_STP103X_ASI_INTR_DISPATCH_STATUS: |
| case TME_STP103X_ASI_INTR_RECEIVE: |
| case TME_STP103X_ASI_UPA_CONFIG_REG: |
| case TME_STP103X_ASI_ESTATE_ERROR_EN_REG: |
| case TME_STP103X_ASI_AFSR: |
| case TME_STP103X_ASI_AFAR: |
| case TME_STP103X_ASI_ECACHE_TAG_DATA: |
| default: |
| asi_mask_flags = TME_SPARC64_ASI_MASK_FLAG_SPECIAL; |
| handler = _tme_stp103x_ls_asi_slow; |
| break; |
| } |
| |
| /* get any ASI handler index: */ |
| for (handler_i = 0; _tme_stp103x_ls_asi_handlers[handler_i] != handler; handler_i++) { |
| assert (handler_i < (TME_ARRAY_ELS(_tme_stp103x_ls_asi_handlers) - 1)); |
| } |
| |
| /* initialize this ASI: */ |
| ic->tme_sparc_asis[asi].tme_sparc_asi_mask_flags = asi_mask_flags; |
| ic->tme_sparc_asis[asi].tme_sparc_asi_handler = handler_i; |
| } |
| |
| /* call the common sparc new function: */ |
| return (tme_sparc_new(ic, args, extra, _output)); |
| } |
| |
| /* this creates and returns a new stp1030: */ |
| TME_ELEMENT_X_NEW_DECL(tme_ic_,sparc,stp1030) { |
| return (_tme_stp103x_new(element, args, extra, _output, TRUE)); |
| } |
| |
| #undef TME_SPARC_VERSION |
| #define TME_SPARC_VERSION(ic) _TME_SPARC_VERSION(ic) |
| #undef TME_SPARC_NWINDOWS |
| #define TME_SPARC_NWINDOWS(ic) _TME_SPARC_NWINDOWS(ic) |
| #undef _TME_SPARC_EXECUTE_NAME |
| #undef _TME_SPARC_EXECUTE_OPMAP |