Annotation of tme/ic/stp22xx/stp222x-iommu.c, revision 1.1

1.1     ! root        1: /* $Id: stp222x-iommu.c,v 1.3 2010/06/05 18:59:29 fredette Exp $ */
        !             2: 
        !             3: /* ic/stp222x-iommu.c - emulation of the IOMMU of the UPA to SBus
        !             4:    interface controller (STP2220) and the UPA to PCI interface
        !             5:    controller (STP2222): */
        !             6: 
        !             7: /*
        !             8:  * Copyright (c) 2009 Matt Fredette
        !             9:  * All rights reserved.
        !            10:  *
        !            11:  * Redistribution and use in source and binary forms, with or without
        !            12:  * modification, are permitted provided that the following conditions
        !            13:  * are met:
        !            14:  * 1. Redistributions of source code must retain the above copyright
        !            15:  *    notice, this list of conditions and the following disclaimer.
        !            16:  * 2. Redistributions in binary form must reproduce the above copyright
        !            17:  *    notice, this list of conditions and the following disclaimer in the
        !            18:  *    documentation and/or other materials provided with the distribution.
        !            19:  * 3. All advertising materials mentioning features or use of this software
        !            20:  *    must display the following acknowledgement:
        !            21:  *      This product includes software developed by Matt Fredette.
        !            22:  * 4. The name of the author may not be used to endorse or promote products
        !            23:  *    derived from this software without specific prior written permission.
        !            24:  *
        !            25:  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
        !            26:  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
        !            27:  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
        !            28:  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
        !            29:  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
        !            30:  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
        !            31:  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
        !            32:  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
        !            33:  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
        !            34:  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
        !            35:  * POSSIBILITY OF SUCH DAMAGE.
        !            36:  */
        !            37: 
        !            38: #include <tme/common.h>
        !            39: _TME_RCSID("$Id: stp222x-iommu.c,v 1.3 2010/06/05 18:59:29 fredette Exp $");
        !            40: 
        !            41: /* includes: */
        !            42: #include "stp222x-impl.h"
        !            43: 
        !            44: /* macros: */
        !            45: 
        !            46: /* IOMMU register offsets: */
        !            47: #define TME_STP222X_IOMMU_REGGROUP_INDEX_CR            TME_STP222X_REGGROUP_INDEX(0x00)
        !            48: #define TME_STP222X_IOMMU_REGGROUP_INDEX_TSB           TME_STP222X_REGGROUP_INDEX(0x08)
        !            49: #define TME_STP222X_IOMMU_REGGROUP_INDEX_FLUSH         TME_STP222X_REGGROUP_INDEX(0x10)
        !            50: #define TME_STP222X_IOMMU_REGGROUP_INDEX_DIAG4_VA      TME_STP222X_REGGROUP_INDEX(0x00)
        !            51: #define TME_STP222X_IOMMU_REGGROUP_INDEX_DIAG4_COMPARE TME_STP222X_REGGROUP_INDEX(0x08)
        !            52: 
        !            53: /* the IOMMU control register: */
        !            54: #define TME_STP222X_IOMMU_CR_XLT_ERR_MASK              ((2 << 26) - (1 << 25))
        !            55: #define TME_STP222X_IOMMU_CR_XLT_ERR                   (1 << 24)
        !            56: #define TME_STP222X_IOMMU_CR_LRU_LCKEN                 (1 << 23)
        !            57: #define TME_STP222X_IOMMU_CR_LRU_LCKPTR                        ((2 << 22) - (1 << 19))
        !            58: #define TME_STP222X_IOMMU_CR_TSB_SIZE_MASK             ((2 << 18) - (1 << 16))
        !            59: #define TME_STP222X_IOMMU_CR_TBW_SIZE_MASK             (1 << 2)
        !            60: #define  TME_STP222X_IOMMU_CR_TBW_SIZE_64KB             (1 << 2)
        !            61: #define  TME_STP222X_IOMMU_CR_TBW_SIZE_8KB              (0 << 2)
        !            62: #define TME_STP222X_IOMMU_CR_MMU_DE                    (1 << 1)
        !            63: #define TME_STP222X_IOMMU_CR_MMU_EN                    (1 << 0)
        !            64: #define TME_STP222X_IOMMU_CR_MBZ                       \
        !            65:   (((((tme_uint32_t) 2) << 31) - (1 << 27))            \
        !            66:    | ((2 << 15) - (1 << 3)))
        !            67: 
        !            68: /* an IOMMU TTE: */
        !            69: #define TME_STP222X_IOMMU_TTE_DATA_V                   (((tme_uint64_t) 1) << 63)
        !            70: #define TME_STP222X_IOMMU_TTE_DATA_SIZE_MASK           (((tme_uint64_t) 1) << 61)
        !            71: #define  TME_STP222X_IOMMU_TTE_DATA_SIZE_64KB           (((tme_uint64_t) 1) << 61)
        !            72: #define  TME_STP222X_IOMMU_TTE_DATA_SIZE_8KB            (((tme_uint64_t) 0) << 61)
        !            73: #define TME_STP222X_IOMMU_TTE_STREAM                   (((tme_uint64_t) 1) << 60)
        !            74: #define TME_STP2220_IOMMU_TTE_LOCALBUS                 (((tme_uint64_t) 1) << 59)
        !            75: #define TME_STP222X_IOMMU_TTE_DATA_PA                  ((((tme_uint64_t) 2) << 40) - (1 << 13))
        !            76: #define TME_STP222X_IOMMU_TTE_CACHEABLE                        (1 << 4)
        !            77: #define TME_STP222X_IOMMU_TTE_DATA_W                   (1 << 1)
        !            78: 
        !            79: /* an IOMMU TLB data: */
        !            80: #define TME_STP222X_IOMMU_TLB_DATA_V                   (1 << 30)
        !            81: #define TME_STP2220_IOMMU_TLB_DATA_LOCAL               (1 << 29)
        !            82: #define TME_STP222X_IOMMU_TLB_DATA_C                   (1 << 28)
        !            83: #define TME_STP222X_IOMMU_TLB_DATA_PPN                 ((2 << 27) - (1 << 0))
        !            84: 
        !            85: /* an IOMMU TLB tag: */
        !            86: #define TME_STP222X_IOMMU_TLB_TAG_ERROR_MASK           ((2 << 24) - (1 << 23))
        !            87: #define  TME_STP222X_IOMMU_TLB_TAG_ERROR_PROTECTION     (0 << 23)
        !            88: #define  TME_STP222X_IOMMU_TLB_TAG_ERROR_INVALID        (1 << 23)
        !            89: #define  TME_STP222X_IOMMU_TLB_TAG_ERROR_TIMEOUT        (2 << 23)
        !            90: #define  TME_STP222X_IOMMU_TLB_TAG_ERROR_ECC_UE                 (3 << 23)
        !            91: #define TME_STP222X_IOMMU_TLB_TAG_ERROR                        (1 << 22)
        !            92: #define TME_STP222X_IOMMU_TLB_TAG_W                    (1 << 21)
        !            93: #define TME_STP222X_IOMMU_TLB_TAG_S                    (1 << 20)
        !            94: #define TME_STP222X_IOMMU_TLB_TAG_SIZE_MASK            (1 << 19)
        !            95: #define  TME_STP222X_IOMMU_TLB_TAG_SIZE_64KB            (1 << 19)
        !            96: #define  TME_STP222X_IOMMU_TLB_TAG_SIZE_8KB             (0 << 19)
        !            97: #define TME_STP222X_IOMMU_TLB_TAG_VPN                  ((2 << 18) - (1 << 0))
        !            98: 
        !            99: /* _tme_stp222x_iommu_lookup() returns a mash of TLB tag and TLB data
        !           100:    information.  the non-page-number fields of tag and data don't
        !           101:    overlap, and we don't include the page numbers, which leaves bits
        !           102:    left over.  we use these bits for additional information: */
        !           103: /* NB: the specific values for TME_STP222X_IOMMU_TLB_MASH_UPA_* are
        !           104:    chosen to make it easy to quickly generate the corresponding
        !           105:    address mask: */
        !           106: #define TME_STP222X_IOMMU_TLB_MASH_UPA_41              (1 << 27)
        !           107: #define TME_STP222X_IOMMU_TLB_MASH_UPA_31              (1 << 17)
        !           108: #define TME_STP222X_IOMMU_TLB_MASH_FIXED               (1 << 16)
        !           109: #define TME_STP222X_IOMMU_TLB_MASH_INVALID_REQUEST     (1 << 15)
        !           110: #define TME_STP222X_IOMMU_TLB_MASH_MISS                        (1 << 14)
        !           111: #define TME_STP222X_IOMMU_TLB_MASH_TLB_I(n)            (n)
        !           112: #define TME_STP222X_IOMMU_TLB_MASH_TLB_I_WHICH(x)      ((x) % TME_STP222X_IOMMU_TLB_SIZE)
        !           113: 
        !           114: /* sizes: */
        !           115: #define TME_STP222X_IOMMU_SIZE_LOG2_8KB                        (13)
        !           116: #define TME_STP222X_IOMMU_SIZE_LOG2_64KB               (16)
        !           117: #define TME_STP222X_IOMMU_SIZE_8KB                     (1 << TME_STP222X_IOMMU_SIZE_LOG2_8KB)
        !           118: #define TME_STP222X_IOMMU_SIZE_64KB                    (1 << TME_STP222X_IOMMU_SIZE_LOG2_64KB)
        !           119: 
        !           120: /* this returns the log2 of the TBW size: */
        !           121: static tme_uint32_t
        !           122: _tme_stp222x_iommu_tbw_size_log2(tme_uint32_t iommu_cr)
        !           123: {
        !           124:   tme_uint32_t tbw_size;
        !           125: 
        !           126:   /* shift TBW_SIZE down to bit zero and clear all other bits: */
        !           127:   tbw_size
        !           128:     = ((iommu_cr
        !           129:        / TME_STP222X_IOMMU_CR_TBW_SIZE_MASK)
        !           130:        & 1);
        !           131: 
        !           132:   /* multiply this by the difference between log2(64KB) and log2(8KB),
        !           133:      and then add log2(8KB).  if TBW_SIZE was set, the result will be
        !           134:      log2(64KB), otherwise it will be log2(8KB): */
        !           135:   return ((tbw_size
        !           136:           * (TME_STP222X_IOMMU_SIZE_LOG2_64KB
        !           137:              - TME_STP222X_IOMMU_SIZE_LOG2_8KB))
        !           138:          + TME_STP222X_IOMMU_SIZE_LOG2_8KB);
        !           139: }
        !           140: 
        !           141: /* this returns the VPN mask for a TLB entry: */
        !           142: static tme_uint32_t
        !           143: _tme_stp222x_iommu_tlb_tag_vpn_mask(tme_uint32_t tlb_tag)
        !           144: {
        !           145:   tme_uint32_t tlb_tag_vpn_mask;
        !           146: 
        !           147:   /* copy the TLB entry tag into the TLB tag VPN mask, and shift the
        !           148:      page size bit down to bit zero and clear all other bits: */
        !           149:   tlb_tag_vpn_mask
        !           150:     = ((tlb_tag
        !           151:        / TME_STP222X_IOMMU_TLB_TAG_SIZE_64KB)
        !           152:        & 1);
        !           153: 
        !           154:   /* add in (64KB / 8KB) - 1.  if the page size is 64KB, this will
        !           155:      leave bits 0..2 zero and bit 3 set, otherwise the page size is
        !           156:      8KB and this will leave bits 0..2 one and bit 3 clear: */
        !           157:   tlb_tag_vpn_mask
        !           158:     += ((TME_STP222X_IOMMU_SIZE_64KB
        !           159:         / TME_STP222X_IOMMU_SIZE_8KB)
        !           160:        - 1);
        !           161: 
        !           162:   /* set bits 3..18 of the TLB tag VPN mask: */
        !           163:   tlb_tag_vpn_mask
        !           164:     |= (TME_STP222X_IOMMU_TLB_TAG_VPN
        !           165:        - ((TME_STP222X_IOMMU_SIZE_64KB
        !           166:            / TME_STP222X_IOMMU_SIZE_8KB)
        !           167:           - 1));
        !           168: 
        !           169:   /* return the VPN mask: */
        !           170:   return (tlb_tag_vpn_mask);
        !           171: }
        !           172: 
        !           173: /* this returns the page size from a TLB tag: */
        !           174: static tme_uint32_t
        !           175: _tme_stp222x_iommu_tlb_page_size(tme_uint32_t tlb_tag)
        !           176: {
        !           177:   tme_uint32_t page_size;
        !           178: 
        !           179:   /* shift the page size bit down into bit 13 (8KB) and clear all
        !           180:      other bits: */
        !           181: #if TME_STP222X_IOMMU_TLB_TAG_SIZE_MASK < TME_STP222X_IOMMU_SIZE_8KB
        !           182: #error "TME_STP222X_IOMMU_TLB_TAG_SIZE_MASK changed"
        !           183: #endif
        !           184:   page_size
        !           185:     = ((tlb_tag
        !           186:        / (TME_STP222X_IOMMU_TLB_TAG_SIZE_MASK
        !           187:           / TME_STP222X_IOMMU_SIZE_8KB))
        !           188:        & TME_STP222X_IOMMU_SIZE_8KB);
        !           189: 
        !           190:   /* add 64KB - 8KB to this value.  the result will be 64KB if the
        !           191:      page size is 64KB, and (64KB - 8KB) if the page size is 8KB: */
        !           192: #if TME_STP222X_IOMMU_TLB_TAG_SIZE_64KB != TME_STP222X_IOMMU_TLB_TAG_SIZE_MASK
        !           193: #error "TME_STP222X_IOMMU_TLB_TAG_SIZE_ values changed"
        !           194: #endif
        !           195:   page_size
        !           196:     += (TME_STP222X_IOMMU_SIZE_64KB
        !           197:        - TME_STP222X_IOMMU_SIZE_8KB);
        !           198: 
        !           199:   /* clear all other bits except for 64KB and 8KB.  only one of them
        !           200:      will be set: */
        !           201:   page_size
        !           202:     &= (TME_STP222X_IOMMU_SIZE_64KB
        !           203:        | TME_STP222X_IOMMU_SIZE_8KB);
        !           204: 
        !           205:   return (page_size);
        !           206: }
        !           207: 
        !           208: /* this looks up an address in the IOMMU and returns a mash of TLB
        !           209:    entry tag and data information: */
        !           210: static tme_uint32_t
        !           211: _tme_stp222x_iommu_tlb_mash(const struct tme_stp222x *stp222x,
        !           212:                            tme_bus_addr64_t io_address,
        !           213:                            unsigned int cycle_type)
        !           214: {
        !           215:   tme_uint32_t iommu_cr;
        !           216:   tme_uint32_t io_space_size_log2;
        !           217:   tme_uint32_t io_address_0_31;
        !           218:   tme_uint32_t io_address_tag;
        !           219:   unsigned long tlb_i;
        !           220:   unsigned int tlb_count;
        !           221:   tme_uint32_t tlb_tag;
        !           222: 
        !           223:   /* get the IOMMU control register: */
        !           224:   iommu_cr = stp222x->tme_stp222x_iommu_cr;
        !           225: 
        !           226:   /* if this is an stp2220: */
        !           227:   if (TME_STP222X_IS_2220(stp222x)) {
        !           228: 
        !           229:     /* if the IOMMU is disabled: */
        !           230:     if ((iommu_cr & TME_STP222X_IOMMU_CR_MMU_EN) == 0) {
        !           231:       
        !           232:       /* XXX FIXME - what does the stp2220 do if the IOMMU is
        !           233:         disabled? */
        !           234:       abort();
        !           235:     }
        !           236: 
        !           237:     /* otherwise, this address is translated: */
        !           238:     /* XXX FIXME - does the STP2220 IOMMU have any address ranges that
        !           239:        are bypassed, even when the IOMMU is enabled? */
        !           240:   }
        !           241: 
        !           242:   /* otherwise, this is an stp2222: */
        !           243:   else {
        !           244: 
        !           245:     /* if this lookup is for a Dual Address Cycle: */
        !           246:     if (FALSE) {
        !           247: 
        !           248:       /* if this is a bypass address: */
        !           249:       if ((io_address >> 50) == 0x3fff) {
        !           250: 
        !           251:        /* bits 0..40 of the address become the UPA address.  if bit
        !           252:           40 is clear, the address is cacheable: */
        !           253:        return ((io_address & (((tme_uint64_t) 1) << 40))
        !           254:                ? (TME_STP222X_IOMMU_TLB_MASH_FIXED
        !           255:                   | TME_STP222X_IOMMU_TLB_MASH_UPA_41
        !           256:                   | TME_STP222X_IOMMU_TLB_DATA_V
        !           257:                   | TME_STP222X_IOMMU_TLB_TAG_W
        !           258:                   | TME_STP222X_IOMMU_TLB_TAG_SIZE_8KB)
        !           259:                : (TME_STP222X_IOMMU_TLB_MASH_FIXED
        !           260:                   | TME_STP222X_IOMMU_TLB_MASH_UPA_41
        !           261:                   | TME_STP222X_IOMMU_TLB_DATA_V
        !           262:                   | TME_STP222X_IOMMU_TLB_DATA_C
        !           263:                   | TME_STP222X_IOMMU_TLB_TAG_W
        !           264:                   | TME_STP222X_IOMMU_TLB_TAG_SIZE_8KB));
        !           265:       }
        !           266: 
        !           267:       /* otherwise, this address is local: */
        !           268:       else {
        !           269:        return (TME_STP222X_IOMMU_TLB_MASH_FIXED
        !           270:                | TME_STP222X_IOMMU_TLB_DATA_V
        !           271:                | TME_STP2220_IOMMU_TLB_DATA_LOCAL
        !           272:                | TME_STP222X_IOMMU_TLB_TAG_W
        !           273:                | TME_STP222X_IOMMU_TLB_TAG_SIZE_8KB);
        !           274:       }
        !           275:     }
        !           276: 
        !           277:     /* otherwise, this is a Single Address Cycle: */
        !           278:     else {
        !           279: 
        !           280:       /* if this address is translated or passed-through: */
        !           281:       if (io_address & (((tme_uint32_t) 1) << 31)) {
        !           282: 
        !           283:        /* if this address is passed-through, bits 0..30 of the
        !           284:           address become the cacheable UPA address: */
        !           285:        if ((iommu_cr & TME_STP222X_IOMMU_CR_MMU_EN) == 0) {
        !           286:          return (TME_STP222X_IOMMU_TLB_MASH_FIXED
        !           287:                  | TME_STP222X_IOMMU_TLB_MASH_UPA_31
        !           288:                  | TME_STP222X_IOMMU_TLB_DATA_V
        !           289:                  | TME_STP222X_IOMMU_TLB_TAG_W
        !           290:                  | TME_STP222X_IOMMU_TLB_DATA_C
        !           291:                  | TME_STP222X_IOMMU_TLB_TAG_SIZE_8KB);
        !           292:        }
        !           293: 
        !           294:        /* otherwise, this address is translated: */
        !           295:       }
        !           296: 
        !           297:       /* otherwise, this address is local: */
        !           298:       else {
        !           299:        return (TME_STP222X_IOMMU_TLB_MASH_FIXED
        !           300:                | TME_STP222X_IOMMU_TLB_DATA_V
        !           301:                | TME_STP2220_IOMMU_TLB_DATA_LOCAL
        !           302:                | TME_STP222X_IOMMU_TLB_TAG_W
        !           303:                | TME_STP222X_IOMMU_TLB_TAG_SIZE_8KB);
        !           304:       }
        !           305:     }
        !           306:   }
        !           307: 
        !           308:   /* truncate the I/O address to 32 bits: */
        !           309:   io_address_0_31 = io_address;
        !           310: 
        !           311:   /* get the size of the translatable I/O address space: */
        !           312:   io_space_size_log2
        !           313:     = (_tme_stp222x_iommu_tbw_size_log2(iommu_cr)
        !           314:        + (10   /* log2(one TSB table size unit entry count) */
        !           315:          + TME_FIELD_MASK_EXTRACTU(iommu_cr, TME_STP222X_IOMMU_CR_TSB_SIZE_MASK)));
        !           316: 
        !           317:   /* if this I/O address is not within the translatable space: */
        !           318:   /* "Hardware does not prevent illegal combinations [of TSB_SIZ and
        !           319:      TBW_SIZ] from being programmed.  If an illegal combination is
        !           320:      programmed into the IOMMU, all translation requests will be
        !           321:      rejected as invalid." */
        !           322:   if (io_space_size_log2 >= 32
        !           323:       || (io_address_0_31
        !           324:          < (0
        !           325:             - (((tme_uint32_t) 1) << io_space_size_log2)))) {
        !           326: 
        !           327:     /* this translation request is invalid: */
        !           328:     return (TME_STP222X_IOMMU_TLB_MASH_FIXED
        !           329:            | TME_STP222X_IOMMU_TLB_MASH_INVALID_REQUEST
        !           330:            | TME_STP222X_IOMMU_TLB_TAG_SIZE_8KB);
        !           331:   }
        !           332: 
        !           333:   /* convert the I/O address into something that can be easily
        !           334:      compared to a TLB tag: */
        !           335:   io_address_tag = io_address_0_31 / TME_STP222X_IOMMU_SIZE_8KB;
        !           336: 
        !           337:   /* loop over the TLB entries from most to least recently used: */
        !           338:   tlb_i = stp222x->tme_stp222x_iommu_tlb_i_mru;
        !           339:   tlb_count = TME_STP222X_IOMMU_TLB_SIZE;
        !           340:   for (;;) {
        !           341: 
        !           342:     /* get this TLB entry tag: */
        !           343:     tlb_tag = stp222x->tme_stp222x_iommu_tlb_tags[tlb_i];
        !           344: 
        !           345:     /* if we hit in this TLB entry: */
        !           346:     if (((tlb_tag
        !           347:          ^ io_address_tag)
        !           348:         & _tme_stp222x_iommu_tlb_tag_vpn_mask(tlb_tag)) == 0) {
        !           349: 
        !           350:       /* this address hit in the TLB: */
        !           351:       return (TME_STP222X_IOMMU_TLB_MASH_TLB_I(tlb_i)
        !           352:              | (tlb_tag
        !           353:                 & (TME_STP222X_IOMMU_TLB_TAG_ERROR_MASK
        !           354:                    | TME_STP222X_IOMMU_TLB_TAG_ERROR
        !           355:                    | TME_STP222X_IOMMU_TLB_TAG_W
        !           356:                    | TME_STP222X_IOMMU_TLB_TAG_S
        !           357:                    | TME_STP222X_IOMMU_TLB_TAG_SIZE_MASK))
        !           358:              | (stp222x->tme_stp222x_iommu_tlb_data[tlb_i]
        !           359:                 & (TME_STP222X_IOMMU_TLB_DATA_V
        !           360:                    | TME_STP2220_IOMMU_TLB_DATA_LOCAL
        !           361:                    | TME_STP222X_IOMMU_TLB_DATA_C)));
        !           362:     }
        !           363: 
        !           364:     /* advance to the next TLB entry in the LRU list: */
        !           365:     tlb_i = stp222x->tme_stp222x_iommu_lru_next(tlb_i);
        !           366:     tlb_count--;
        !           367:     assert ((tlb_count == 0) == (tlb_i == stp222x->tme_stp222x_iommu_tlb_i_mru));
        !           368: 
        !           369:     /* if we missed in all of the TLB entries: */
        !           370:     if (__tme_predict_false(tlb_count == 0)) {
        !           371: 
        !           372:       /* this address missed in the TLB: */
        !           373:       return (TME_STP222X_IOMMU_TLB_MASH_MISS
        !           374:              | TME_STP222X_IOMMU_TLB_TAG_SIZE_8KB);
        !           375:     }
        !           376:   }
        !           377:   /* NOTREACHED */
        !           378: }
        !           379: 
        !           380: /* this looks up an address in the IOMMU and returns a mash of TLB
        !           381:    entry tag and data information and any slave connection index.  if
        !           382:    there is a slave connection index, the address is converted to be
        !           383:    connection-relative: */
        !           384: static tme_uint32_t
        !           385: _tme_stp222x_iommu_tlb_mash_slave(struct tme_bus_connection *io_conn_bus,
        !           386:                                  tme_bus_addr64_t *_io_to_slave_address,
        !           387:                                  unsigned int cycle_type,
        !           388:                                  tme_uint32_t *_slave_conn_index)
        !           389: {
        !           390:   struct tme_stp222x *stp222x;
        !           391:   tme_uint32_t tlb_mash;
        !           392:   unsigned long tlb_i;
        !           393:   unsigned long tlb_i_next;
        !           394:   unsigned long tlb_i_prev;
        !           395:   tme_bus_addr64_t upa_address;
        !           396:   tme_uint32_t page_size_m1;
        !           397: 
        !           398:   /* recover our data structure: */
        !           399:   stp222x = io_conn_bus->tme_bus_connection.tme_connection_element->tme_element_private;
        !           400: 
        !           401:   /* look up this address in the IOMMU TLB: */
        !           402:   tlb_mash = _tme_stp222x_iommu_tlb_mash(stp222x,
        !           403:                                         *_io_to_slave_address,
        !           404:                                         cycle_type);
        !           405: 
        !           406:   /* XXX FIXME - is the LRU only updated on valid translations? */
        !           407: 
        !           408:   /* if this address hit in the IOMMU TLB: */
        !           409:   if ((tlb_mash
        !           410:        & (TME_STP222X_IOMMU_TLB_MASH_FIXED
        !           411:          | TME_STP222X_IOMMU_TLB_MASH_MISS)) == 0) {
        !           412: 
        !           413:     /* if the hit TLB entry is not already the most recently used: */
        !           414:     tlb_i = TME_STP222X_IOMMU_TLB_MASH_TLB_I_WHICH(tlb_mash);
        !           415:     if (tlb_i != stp222x->tme_stp222x_iommu_tlb_i_mru) {
        !           416: 
        !           417:       /* remove this TLB entry from the LRU list: */
        !           418:       tlb_i_next = stp222x->tme_stp222x_iommu_lru_next(tlb_i);
        !           419:       tlb_i_prev = stp222x->tme_stp222x_iommu_lru_prev(tlb_i);
        !           420:       stp222x->tme_stp222x_iommu_lru_next(tlb_i_prev) = tlb_i_next;
        !           421:       stp222x->tme_stp222x_iommu_lru_prev(tlb_i_next) = tlb_i_prev;
        !           422: 
        !           423:       /* add this TLB entry in the LRU list before the old most
        !           424:         recently used TLB entry: */
        !           425:       tlb_i_next = stp222x->tme_stp222x_iommu_tlb_i_mru;
        !           426:       tlb_i_prev = stp222x->tme_stp222x_iommu_lru_prev(tlb_i_next);
        !           427:       stp222x->tme_stp222x_iommu_lru_next(tlb_i_prev) = tlb_i;
        !           428:       stp222x->tme_stp222x_iommu_lru_prev(tlb_i) = tlb_i_prev;
        !           429:       stp222x->tme_stp222x_iommu_lru_next(tlb_i) = tlb_i_next;
        !           430:       stp222x->tme_stp222x_iommu_lru_prev(tlb_i_next) = tlb_i;
        !           431: 
        !           432:       /* this TLB entry is now the most recently used: */
        !           433:       stp222x->tme_stp222x_iommu_tlb_i_mru = tlb_i;
        !           434:     }
        !           435:   }
        !           436: 
        !           437:   /* if we don't have a valid translation for this address: */
        !           438:   if ((tlb_mash & TME_STP222X_IOMMU_TLB_DATA_V) == 0) {
        !           439: 
        !           440:     /* return no connection index: */
        !           441:     *_slave_conn_index = TME_STP222X_CONN_NULL;
        !           442:     return (tlb_mash);
        !           443:   }
        !           444: 
        !           445:   /* if this address is translated to itself, on its local bus: */
        !           446:   if (tlb_mash & TME_STP2220_IOMMU_TLB_DATA_LOCAL) {
        !           447: 
        !           448:     /* look up this address on the connection's bus: */
        !           449:     *_slave_conn_index
        !           450:       = tme_stp222x_aspace_lookup(stp222x,
        !           451:                                  (TME_STP222X_IS_2220(stp222x)
        !           452:                                   ? TME_STP2220_ASPACE_SBUS
        !           453:                                   : ((io_conn_bus->tme_bus_connection.tme_connection_id 
        !           454:                                       & TME_STP2222_CONNID_BUS_WHICH) == 0)
        !           455:                                   ? TME_STP2222_ASPACE_PCI_MEMORY(0)
        !           456:                                   : TME_STP2222_ASPACE_PCI_MEMORY(1)),
        !           457:                                  _io_to_slave_address);
        !           458:   }
        !           459: 
        !           460:   /* otherwise, this address is not local: */
        !           461:   else {
        !           462: 
        !           463:     /* if this address is translated directly into a UPA address: */
        !           464:     if (tlb_mash
        !           465:        & (TME_STP222X_IOMMU_TLB_MASH_UPA_41
        !           466:           | TME_STP222X_IOMMU_TLB_MASH_UPA_31)) {
        !           467: 
        !           468:       /* convert the address into the UPA address: */
        !           469: #if (TME_STP222X_IOMMU_TLB_MASH_UPA_41 / TME_STP222X_IOMMU_TLB_MASH_UPA_31) != (1 << (41 - 31))
        !           470: #error "TME_STP222X_IOMMU_TLB_MASH_ values changed"
        !           471: #endif
        !           472:       upa_address = *_io_to_slave_address;
        !           473:       upa_address
        !           474:        &= (((tlb_mash
        !           475:              & (TME_STP222X_IOMMU_TLB_MASH_UPA_41
        !           476:                 | TME_STP222X_IOMMU_TLB_MASH_UPA_31))
        !           477:             * ((((tme_bus_addr64_t) 1) << 31)
        !           478:                / TME_STP222X_IOMMU_TLB_MASH_UPA_31))
        !           479:            - 1);
        !           480:     }
        !           481: 
        !           482:     /* otherwise, this address is translated through the TLB entry data
        !           483:        into a UPA address: */
        !           484:     else {
        !           485: 
        !           486:       /* get the page size mask: */
        !           487:       page_size_m1 = _tme_stp222x_iommu_tlb_page_size(tlb_mash) - 1;
        !           488: 
        !           489:       /* convert the TLB entry data into the base UPA address: */
        !           490:       upa_address
        !           491:        = (stp222x->tme_stp222x_iommu_tlb_data[TME_STP222X_IOMMU_TLB_MASH_TLB_I_WHICH(tlb_mash)]
        !           492:           & TME_STP222X_IOMMU_TLB_DATA_PPN);
        !           493:       upa_address *= TME_STP222X_IOMMU_SIZE_8KB;
        !           494:       upa_address &= ~ (tme_bus_addr64_t) page_size_m1;
        !           495: 
        !           496:       /* add in the offset: */
        !           497:       upa_address |= ((tme_bus_addr32_t) *_io_to_slave_address) & page_size_m1;
        !           498: 
        !           499:       /* return the UPA bus and address: */
        !           500:       *_slave_conn_index = TME_STP222X_CONN_UPA;
        !           501:       *_io_to_slave_address = upa_address;
        !           502:     }
        !           503:   }
        !           504: 
        !           505:   return (tlb_mash);
        !           506: }
        !           507: 
        !           508: /* this handles an IOMMU bus cycle: */
        !           509: void
        !           510: tme_stp222x_iommu_cycle(struct tme_bus_connection *master_conn_bus,
        !           511:                        struct tme_bus_cycle *master_cycle,
        !           512:                        tme_uint32_t *_master_fast_cycle_types,
        !           513:                        struct tme_completion *master_completion)
        !           514: {
        !           515:   struct tme_stp222x *stp222x;
        !           516:   struct tme_bus_tlb *tsb_tlb;
        !           517:   unsigned long tlb_i_must_hit;
        !           518:   tme_bus_addr64_t slave_address;
        !           519:   unsigned int slave_conn_index;
        !           520:   tme_uint32_t tlb_mash;
        !           521:   tme_uint32_t iommu_cr;
        !           522:   tme_uint32_t tsb_index;
        !           523:   tme_bus_addr64_t tsb_address;
        !           524:   struct tme_bus_connection *slave_conn_bus;
        !           525:   struct tme_bus_connection *slave_conn_bus_other;
        !           526:   struct tme_bus_tlb tsb_tlb_local;
        !           527: #if TME_STP22XX_BUS_TRANSITION
        !           528:   int rc;
        !           529: #endif /* TME_STP22XX_BUS_TRANSITION */
        !           530:   tme_uint64_t tte;
        !           531:   unsigned long tlb_i;
        !           532:   struct tme_stp222x_tlb_list *tlb_list;
        !           533:   signed long tlb_list_i;
        !           534:   struct tme_token *token;
        !           535:   tme_uint32_t tlb_data;
        !           536:   tme_uint32_t tlb_tag;
        !           537:   tme_uint32_t tlb_tag_error;
        !           538: 
        !           539:   /* enter: */
        !           540:   stp222x = tme_stp222x_enter_master_bus(master_conn_bus);
        !           541: 
        !           542:   /* start this cycle: */
        !           543:   assert (stp222x->tme_stp222x_master_completion == NULL);
        !           544:   stp222x->tme_stp222x_master_completion = &master_completion;
        !           545: 
        !           546:   /* start out using the stored TSB TLB: */
        !           547:   tsb_tlb = &stp222x->tme_stp222x_iommu_tsb_tlb;
        !           548: 
        !           549:   /* start out allowing the address to miss in the IOMMU TLB: */
        !           550:   tlb_i_must_hit = TME_STP222X_IOMMU_TLB_SIZE;
        !           551: 
        !           552:   /* loop forever: */
        !           553:   for (;;) {
        !           554: 
        !           555:     /* if this cycle has been aborted: */
        !           556:     if (stp222x->tme_stp222x_master_completion != &master_completion) {
        !           557:       tme_stp222x_leave(stp222x);
        !           558:       return;
        !           559:     }
        !           560: 
        !           561:     /* translate this address: */
        !           562:     slave_address = master_cycle->tme_bus_cycle_address;
        !           563:     tlb_mash = _tme_stp222x_iommu_tlb_mash_slave(master_conn_bus,
        !           564:                                                 &slave_address,
        !           565:                                                 master_cycle->tme_bus_cycle_type,
        !           566:                                                 &slave_conn_index);
        !           567: 
        !           568:     /* if the translation for this address is valid: */
        !           569:     if (tlb_mash & TME_STP222X_IOMMU_TLB_DATA_V) {
        !           570: 
        !           571:       /* stop now: */
        !           572:       break;
        !           573:     }
        !           574: 
        !           575:     /* if the translation could not miss in the IOMMU TLB: */
        !           576:     if (tlb_i_must_hit < TME_STP222X_IOMMU_TLB_SIZE) {
        !           577: 
        !           578:       /* the translation must have hit the specified IOMMU TLB entry,
        !           579:         even though that TLB entry is apparently invalid: */
        !           580:       assert ((tlb_mash & TME_STP222X_IOMMU_TLB_MASH_FIXED) == 0
        !           581:              && TME_STP222X_IOMMU_TLB_MASH_TLB_I_WHICH(tlb_mash) == tlb_i_must_hit);
        !           582:       
        !           583:       /* stop now: */
        !           584:       break;
        !           585:     }
        !           586: 
        !           587:     /* get the IOMMU control register: */
        !           588:     iommu_cr = stp222x->tme_stp222x_iommu_cr;
        !           589: 
        !           590:     /* get the index in the TSB table to read: */
        !           591:     tsb_index = master_cycle->tme_bus_cycle_address;
        !           592:     tsb_index >>= _tme_stp222x_iommu_tbw_size_log2(iommu_cr);
        !           593:     tsb_index
        !           594:       &= ((2 <<
        !           595:           (22
        !           596:            + TME_FIELD_MASK_EXTRACTU(iommu_cr, TME_STP222X_IOMMU_CR_TSB_SIZE_MASK)
        !           597:            - TME_STP222X_IOMMU_SIZE_LOG2_8KB))
        !           598:          - 1);
        !           599: 
        !           600:     /* get the address in the TSB table to read: */
        !           601:     tsb_address = stp222x->tme_stp222x_iommu_tsb + (tsb_index * sizeof(tme_uint64_t));
        !           602: 
        !           603:     /* busy the TSB TLB: */
        !           604:     tme_bus_tlb_busy(tsb_tlb);
        !           605: 
        !           606:     /* if the TSB TLB is invalid or doesn't apply: */
        !           607:     if (tme_bus_tlb_is_invalid(tsb_tlb)
        !           608:        || tsb_address < (tme_bus_addr64_t) tsb_tlb->tme_bus_tlb_addr_first
        !           609:        || tsb_address > (tme_bus_addr64_t) tsb_tlb->tme_bus_tlb_addr_last) {
        !           610: 
        !           611:       /* force the TLB entry to be invalid, since after we clear its
        !           612:         invalid token, we may abort this cycle without storing it: */
        !           613:       stp222x->tme_stp222x_iommu_tsb_tlb.tme_bus_tlb_addr_first = 1;
        !           614:       stp222x->tme_stp222x_iommu_tsb_tlb.tme_bus_tlb_addr_last = 0;
        !           615: 
        !           616:       /* unbusy the TSB TLB for filling: */
        !           617:       tme_bus_tlb_unbusy_fill(tsb_tlb);
        !           618: 
        !           619:       /* busy the UPA connection: */
        !           620:       slave_conn_bus = tme_stp222x_slave_busy_bus(stp222x, TME_STP222X_CONN_UPA);
        !           621: 
        !           622:       /* leave: */
        !           623:       tme_stp222x_leave(stp222x);
        !           624: 
        !           625:       /* fill the local TLB entry: */
        !           626:       slave_conn_bus_other = (struct tme_bus_connection *) slave_conn_bus->tme_bus_connection.tme_connection_other;
        !           627:       tsb_tlb_local.tme_bus_tlb_token = &stp222x->tme_stp222x_iommu_tsb_tlb_token;
        !           628: #if TME_STP22XX_BUS_TRANSITION
        !           629:       rc =
        !           630: #endif /* TME_STP22XX_BUS_TRANSITION */
        !           631:       (*slave_conn_bus_other->tme_bus_tlb_fill)
        !           632:        (slave_conn_bus_other,
        !           633:         &tsb_tlb_local,
        !           634:         tsb_address,
        !           635:         TME_BUS_CYCLE_READ);
        !           636: #if TME_STP22XX_BUS_TRANSITION
        !           637:       assert (rc == TME_OK);
        !           638: #endif /* TME_STP22XX_BUS_TRANSITION */
        !           639: 
        !           640:       /* reenter: */
        !           641:       stp222x = tme_stp222x_enter_bus(master_conn_bus);
        !           642: 
        !           643:       /* unbusy the UPA connection: */
        !           644:       tme_stp222x_slave_unbusy(stp222x);
        !           645: 
        !           646:       /* switch to using the newly filled local TLB entry: */
        !           647:       tsb_tlb = &tsb_tlb_local;
        !           648: 
        !           649:       /* loop now, to make sure we're still running: */
        !           650:       continue;
        !           651:     }
        !           652: 
        !           653:     /* if we switched to the local TLB entry: */
        !           654:     if (tsb_tlb == &tsb_tlb_local) {
        !           655: 
        !           656:       /* store the local TLB entry: */
        !           657:       stp222x->tme_stp222x_iommu_tsb_tlb = tsb_tlb_local;
        !           658:     }
        !           659: 
        !           660:     /* the TSB TLB must allow fast reading: */
        !           661:     if (tsb_tlb->tme_bus_tlb_emulator_off_read == TME_EMULATOR_OFF_UNDEF) {
        !           662:       abort();
        !           663:     }
        !           664: 
        !           665:     /* read the TTE: */
        !           666:     tte = tme_memory_bus_read64(((_tme_const tme_shared tme_uint64_t *) 
        !           667:                                 (tsb_tlb->tme_bus_tlb_emulator_off_read
        !           668:                                  + tsb_address)),
        !           669:                                tsb_tlb->tme_bus_tlb_rwlock,
        !           670:                                sizeof(tme_uint64_t),
        !           671:                                sizeof(tme_uint64_t));
        !           672:     tte = tme_betoh_u64(tte);
        !           673: 
        !           674:     /* unbusy the TSB TLB: */
        !           675:     tme_bus_tlb_unbusy(tsb_tlb);
        !           676: 
        !           677:     /* if a TLB entry is already allocated to this address: */
        !           678:     if ((tlb_mash & TME_STP222X_IOMMU_TLB_MASH_MISS) == 0) {
        !           679: 
        !           680:       /* replace the same TLB entry: */
        !           681:       tlb_i = TME_STP222X_IOMMU_TLB_MASH_TLB_I_WHICH(tlb_mash);
        !           682:     }
        !           683: 
        !           684:     /* otherwise, a TLB entry isn't already allocated to this address: */
        !           685:     else {
        !           686: 
        !           687:       /* get the IOMMU control register: */
        !           688:       iommu_cr = stp222x->tme_stp222x_iommu_cr;
        !           689: 
        !           690:       /* if the LRU is locked: */
        !           691:       if (__tme_predict_false(iommu_cr & TME_STP222X_IOMMU_CR_LRU_LCKEN)) {
        !           692: 
        !           693:        /* replace the locked TLB entry: */
        !           694:        tlb_i = TME_FIELD_MASK_EXTRACTU(iommu_cr, TME_STP222X_IOMMU_CR_LRU_LCKPTR);
        !           695:       }
        !           696: 
        !           697:       /* otherwise, the LRU is not locked: */
        !           698:       else {
        !           699: 
        !           700:        /* replace the least recently used TLB entry: */
        !           701:        tlb_i = stp222x->tme_stp222x_iommu_lru_prev(stp222x->tme_stp222x_iommu_tlb_i_mru);
        !           702:       }
        !           703:     }
        !           704: 
        !           705:     /* invalidate all of the TLBs associated with this IOMMU TLB
        !           706:        entry: */
        !           707:     tlb_list = &stp222x->tme_stp222x_iommu_tlb_list[tlb_i];
        !           708:     tlb_list_i = TME_STP222X_TLB_LIST_TOKENS_COUNT - 1;
        !           709:     do {
        !           710:       token = tlb_list->tme_stp222x_tlb_list_tokens[tlb_list_i];
        !           711:       if (token != NULL) {
        !           712:        tlb_list->tme_stp222x_tlb_list_tokens[tlb_list_i] = NULL;
        !           713:        tme_token_invalidate(token);
        !           714:       }
        !           715:     } while (--tlb_list_i >= 0);
        !           716: 
        !           717:     /* make and store the TLB data: */
        !           718: #if (TME_STP222X_IOMMU_TLB_DATA_PPN & 1) == 0
        !           719: #error "TME_STP222X_IOMMU_TLB_DATA_PPN changed"
        !           720: #endif
        !           721:     tlb_data = TME_FIELD_MASK_EXTRACTU(tte, TME_STP222X_IOMMU_TTE_DATA_PA);
        !           722:     if (tte & TME_STP222X_IOMMU_TTE_DATA_V) {
        !           723:       tlb_data += TME_STP222X_IOMMU_TLB_DATA_V;
        !           724:     }
        !           725:     if (tte & TME_STP2220_IOMMU_TTE_LOCALBUS) {
        !           726:       if (TME_STP222X_IS_2220(stp222x)) {
        !           727:        tlb_data += TME_STP2220_IOMMU_TLB_DATA_LOCAL;
        !           728:       }
        !           729:     }
        !           730:     if (tte & TME_STP222X_IOMMU_TTE_CACHEABLE) {
        !           731:       tlb_data += TME_STP222X_IOMMU_TLB_DATA_C;
        !           732:     }
        !           733:     stp222x->tme_stp222x_iommu_tlb_data[tlb_i] = tlb_data;
        !           734: 
        !           735:     /* make and store the TLB tag: */
        !           736:     tlb_tag = (tme_uint32_t) master_cycle->tme_bus_cycle_address;
        !           737:     tlb_tag /= TME_STP222X_IOMMU_SIZE_8KB;
        !           738:     if (tte & TME_STP222X_IOMMU_TTE_DATA_W) {
        !           739:       tlb_tag += TME_STP222X_IOMMU_TLB_TAG_W;
        !           740:     }
        !           741:     if (tte & TME_STP222X_IOMMU_TTE_STREAM) {
        !           742:       tlb_tag += TME_STP222X_IOMMU_TLB_TAG_S;
        !           743:     }
        !           744:     if (tte & TME_STP222X_IOMMU_TTE_DATA_SIZE_MASK) {
        !           745:       tlb_tag += TME_STP222X_IOMMU_TLB_TAG_SIZE_MASK;
        !           746:     }
        !           747:     stp222x->tme_stp222x_iommu_tlb_tags[tlb_i] = tlb_tag;
        !           748: 
        !           749:     /* loop to look up this address in the IOMMU again.  this time, it
        !           750:        must hit this entry: */
        !           751:     tlb_i_must_hit = tlb_i;
        !           752: 
        !           753: #if TME_STP22XX_BUS_TRANSITION
        !           754: 
        !           755:     /* invalidate the last TLB that missed the IOMMU TLB (because we
        !           756:        just filled the IOMMU TLB, we should get the master to refill
        !           757:        its TLB): */
        !           758:     if (stp222x->tme_stp222x_iommu_tlb_missed_token != NULL) {
        !           759:       tme_token_invalidate(stp222x->tme_stp222x_iommu_tlb_missed_token);
        !           760:       stp222x->tme_stp222x_iommu_tlb_missed_token = NULL;
        !           761:     }
        !           762: 
        !           763: #endif /* TME_STP22XX_BUS_TRANSITION */
        !           764:   }
        !           765: 
        !           766:   /* assume that there is no IOMMU error: */
        !           767:   tlb_tag_error = !TME_STP222X_IOMMU_TLB_TAG_ERROR;
        !           768: 
        !           769:   /* if the address is not writable: */
        !           770:   if (__tme_predict_false((tlb_mash & TME_STP222X_IOMMU_TLB_TAG_W) == 0)) {
        !           771: 
        !           772:     /* the master can't do fast writes: */
        !           773:     *_master_fast_cycle_types &= ~TME_BUS_CYCLE_WRITE;
        !           774: 
        !           775:     /* if this is a write: */
        !           776:     if (__tme_predict_false(master_cycle->tme_bus_cycle_type & TME_BUS_CYCLE_WRITE)) {
        !           777: 
        !           778:       /* there is an IOMMU protection error: */
        !           779:       tlb_tag_error
        !           780:        = (TME_STP222X_IOMMU_TLB_TAG_ERROR
        !           781:           + TME_STP222X_IOMMU_TLB_TAG_ERROR_PROTECTION);
        !           782:     }
        !           783:   }
        !           784: 
        !           785:   /* if we don't have a valid translation: */
        !           786:   if (__tme_predict_false((tlb_mash & TME_STP222X_IOMMU_TLB_DATA_V) == 0)) {
        !           787: 
        !           788:     /* there is an IOMMU invalid error: */
        !           789:     tlb_tag_error
        !           790:       = (TME_STP222X_IOMMU_TLB_TAG_ERROR
        !           791:         + TME_STP222X_IOMMU_TLB_TAG_ERROR_INVALID);
        !           792:   }
        !           793: 
        !           794:   /* if there is an IOMMU error: */
        !           795:   if (tlb_tag_error != !TME_STP222X_IOMMU_TLB_TAG_ERROR) {
        !           796: 
        !           797:     /* if this isn't a fixed translation: */
        !           798:     if ((tlb_mash & TME_STP222X_IOMMU_TLB_MASH_FIXED) == 0) {
        !           799: 
        !           800:       /* update the error in the tag for the TLB entry: */
        !           801:       tlb_i = TME_STP222X_IOMMU_TLB_MASH_TLB_I_WHICH(tlb_mash);
        !           802:       stp222x->tme_stp222x_iommu_tlb_tags[tlb_i]
        !           803:        = ((stp222x->tme_stp222x_iommu_tlb_tags[tlb_i]
        !           804:            & ~(TME_STP222X_IOMMU_TLB_TAG_ERROR_MASK
        !           805:                | TME_STP222X_IOMMU_TLB_TAG_ERROR))
        !           806:           + tlb_tag_error);
        !           807:     }
        !           808: 
        !           809:     /* update the error in the IOMMU control register: */
        !           810: #if (TME_STP222X_IOMMU_TLB_TAG_ERROR_MASK * (TME_STP222X_IOMMU_CR_XLT_ERR_MASK / TME_STP222X_IOMMU_TLB_TAG_ERROR_MASK)) != TME_STP222X_IOMMU_CR_XLT_ERR_MASK
        !           811: #error "IOMMU error masks changed"
        !           812: #endif
        !           813: #if (TME_STP222X_IOMMU_TLB_TAG_ERROR * (TME_STP222X_IOMMU_CR_XLT_ERR_MASK / TME_STP222X_IOMMU_TLB_TAG_ERROR_MASK)) != TME_STP222X_IOMMU_CR_XLT_ERR
        !           814: #error "IOMMU error flags changed"
        !           815: #endif
        !           816:     stp222x->tme_stp222x_iommu_cr
        !           817:       = ((stp222x->tme_stp222x_iommu_cr
        !           818:          & ~(TME_STP222X_IOMMU_CR_XLT_ERR_MASK
        !           819:              | TME_STP222X_IOMMU_CR_XLT_ERR))
        !           820:         + (tlb_tag_error
        !           821:            * (TME_STP222X_IOMMU_CR_XLT_ERR_MASK
        !           822:               / TME_STP222X_IOMMU_TLB_TAG_ERROR_MASK)));
        !           823: 
        !           824:     /* force no connection: */
        !           825:     slave_conn_index = TME_STP222X_CONN_NULL;
        !           826:   }
        !           827: 
        !           828:   /* run the slave bus cycle: */
        !           829:   master_cycle->tme_bus_cycle_address = slave_address;
        !           830:   tme_stp22xx_slave_cycle(master_conn_bus,
        !           831:                          slave_conn_index,
        !           832:                          master_cycle,
        !           833:                          _master_fast_cycle_types,
        !           834:                          &master_completion);
        !           835:  
        !           836:   /* leave: */
        !           837:   tme_stp222x_leave(stp222x);
        !           838: }
        !           839: 
        !           840: /* this fills a TLB entry from the IOMMU: */
        !           841: void
        !           842: tme_stp222x_iommu_tlb_fill(struct tme_bus_connection *io_conn_bus,
        !           843:                           struct tme_bus_tlb *tlb,
        !           844:                           tme_bus_addr_t io_address_wider,
        !           845:                           unsigned int cycle_type)
        !           846: {
        !           847:   struct tme_stp222x *stp222x;
        !           848:   tme_bus_addr64_t slave_address;
        !           849:   tme_uint32_t tlb_mash;
        !           850:   tme_uint32_t slave_conn_index;
        !           851:   struct tme_stp222x_tlb_list *tlb_list;
        !           852:   unsigned int tlb_list_head;
        !           853:   struct tme_token *token;
        !           854:   struct tme_token *token_other;
        !           855:   struct tme_bus_tlb tlb_mapping;
        !           856:   tme_uint32_t page_size_m1;
        !           857:   tme_bus_addr64_t io_address;
        !           858: 
        !           859:   /* enter: */
        !           860:   stp222x = tme_stp222x_enter_bus(io_conn_bus);
        !           861: 
        !           862:   /* translate this address: */
        !           863:   slave_address = io_address_wider;
        !           864:   tlb_mash = _tme_stp222x_iommu_tlb_mash_slave(io_conn_bus,
        !           865:                                               &slave_address,
        !           866:                                               cycle_type,
        !           867:                                               &slave_conn_index);
        !           868: 
        !           869:   /* if the translation for this address is valid: */
        !           870:   if (tlb_mash & TME_STP222X_IOMMU_TLB_DATA_V) {
        !           871: 
        !           872:     /* if this translation is fixed: */
        !           873:     if (tlb_mash & TME_STP222X_IOMMU_TLB_MASH_FIXED) {
        !           874: 
        !           875:       /* track this TLB entry in the fixed list: */
        !           876:       tlb_list = &stp222x->tme_stp222x_iommu_tlb_list_fixed;
        !           877:     }
        !           878: 
        !           879:     /* otherwise, this translation is not fixed: */
        !           880:     else {
        !           881: 
        !           882:       /* track this TLB entry in the list for the IOMMU TLB entry: */
        !           883:       tlb_list = &stp222x->tme_stp222x_iommu_tlb_list[TME_STP222X_IOMMU_TLB_MASH_TLB_I_WHICH(tlb_mash)];
        !           884:     }
        !           885: 
        !           886:     /* track this TLB entry: */
        !           887:     tlb_list_head = tlb_list->tme_stp222x_tlb_list_head;
        !           888:     token = tlb->tme_bus_tlb_token;
        !           889:     token_other = tlb_list->tme_stp222x_tlb_list_tokens[tlb_list_head];
        !           890:     tlb_list->tme_stp222x_tlb_list_tokens[tlb_list_head] = token;
        !           891:     tlb_list->tme_stp222x_tlb_list_head = (tlb_list_head + 1) % TME_STP222X_TLB_LIST_TOKENS_COUNT;
        !           892:     if (token_other != NULL
        !           893:        && token_other != token) {
        !           894:       tme_token_invalidate(token_other);
        !           895:     }
        !           896:   }
        !           897: 
        !           898:   /* otherwise, the translation for this address is not valid: */
        !           899:   else {
        !           900: 
        !           901:     /* we must not have any connection for this address: */
        !           902:     assert (slave_conn_index == TME_STP222X_CONN_NULL);
        !           903: 
        !           904: #if TME_STP22XX_BUS_TRANSITION
        !           905: 
        !           906:     /* track the TLB for the most recent address that didn't have a
        !           907:        valid translation, so we can invalidate it when the slow cycle
        !           908:        happens (assuming the slow cycle will fill the IOMMU TLB with a
        !           909:        valid translation, we want the slave to then refill its
        !           910:        TLB): */
        !           911:     token = tlb->tme_bus_tlb_token;
        !           912:     token_other = stp222x->tme_stp222x_iommu_tlb_missed_token;
        !           913:     stp222x->tme_stp222x_iommu_tlb_missed_token = token;
        !           914:     if (token_other != NULL
        !           915:        && token_other != token) {
        !           916:       tme_token_invalidate(token_other);
        !           917:     }
        !           918: 
        !           919: #endif /* TME_STP22XX_BUS_TRANSITION */
        !           920:   }
        !           921: 
        !           922:   /* fill the TLB entry: */
        !           923:   tme_stp22xx_tlb_fill(io_conn_bus,
        !           924:                       tlb,
        !           925:                       slave_conn_index,
        !           926:                       slave_address,
        !           927:                       cycle_type);
        !           928: 
        !           929:   /* leave: */
        !           930:   tme_stp222x_leave(stp222x);
        !           931: 
        !           932:   /* get the page size mask: */
        !           933:   page_size_m1 = _tme_stp222x_iommu_tlb_page_size(tlb_mash) - 1;
        !           934: 
        !           935:   /* map the filled TLB entry: */
        !           936:   io_address = ~ (tme_bus_addr64_t) page_size_m1;
        !           937:   io_address &= io_address_wider;
        !           938:   tlb_mapping.tme_bus_tlb_addr_first = io_address;
        !           939:   io_address |= page_size_m1;
        !           940:   tlb_mapping.tme_bus_tlb_addr_last = io_address;
        !           941: #if TME_STP22XX_BUS_TRANSITION
        !           942:   tlb_mapping.tme_bus_tlb_cycles_ok = (TME_BUS_CYCLE_READ | TME_BUS_CYCLE_WRITE);
        !           943: #endif /* TME_STP22XX_BUS_TRANSITION */
        !           944:   tme_bus_tlb_map(tlb, slave_address, &tlb_mapping, io_address_wider);
        !           945: 
        !           946:   /* if this address is not writable: */
        !           947:   if ((tlb_mash & TME_STP222X_IOMMU_TLB_TAG_W) == 0) {
        !           948: 
        !           949:     /* this TLB entry doesn't support writes: */
        !           950:     tlb->tme_bus_tlb_emulator_off_write = TME_EMULATOR_OFF_UNDEF;
        !           951: #if TME_STP22XX_BUS_TRANSITION
        !           952:     tlb->tme_bus_tlb_cycles_ok &= ~TME_BUS_CYCLE_WRITE;
        !           953: #endif /* TME_STP22XX_BUS_TRANSITION */
        !           954:   }
        !           955: }
        !           956: 
        !           957: /* the IOMMU register handler: */
        !           958: void
        !           959: tme_stp222x_iommu_regs(struct tme_stp222x *stp222x,
        !           960:                       struct tme_stp222x_reg *reg)
        !           961: {
        !           962:   tme_uint32_t reggroup_index;
        !           963:   const char *name;
        !           964:   tme_uint32_t io_address;
        !           965:   tme_uint32_t tlb_mash;
        !           966:   tme_uint32_t tlb_i;
        !           967:   struct tme_stp222x_tlb_list *tlb_list;
        !           968:   signed long tlb_list_i;
        !           969:   struct tme_token *token;
        !           970: 
        !           971:   /* get the register: */
        !           972:   reggroup_index = TME_STP222X_REGGROUP_INDEX(reg->tme_stp222x_reg_address);
        !           973: 
        !           974:   /* if this is a write: */
        !           975:   if (reg->tme_stp222x_reg_write) {
        !           976: 
        !           977:     /* dispatch on the register: */
        !           978:     switch (reggroup_index) {
        !           979: 
        !           980:     case TME_STP222X_IOMMU_REGGROUP_INDEX_CR:
        !           981:       stp222x->tme_stp222x_iommu_cr
        !           982:        = (reg->tme_stp222x_reg_value
        !           983:           & ~TME_STP222X_IOMMU_CR_MBZ);
        !           984:       name = "CR";
        !           985:       break;
        !           986: 
        !           987:     case TME_STP222X_IOMMU_REGGROUP_INDEX_TSB:
        !           988:       stp222x->tme_stp222x_iommu_tsb
        !           989:        = (reg->tme_stp222x_reg_value
        !           990:           & ((((tme_uint64_t) 2) << 40)
        !           991:              - (1 << 13)));
        !           992:       name = "TSB";
        !           993:       break;
        !           994: 
        !           995:     case TME_STP222X_IOMMU_REGGROUP_INDEX_FLUSH:
        !           996: 
        !           997:       /* get the address: */
        !           998:       io_address = 0 - (tme_uint32_t) TME_STP222X_IOMMU_SIZE_8KB;
        !           999:       io_address &= reg->tme_stp222x_reg_value;
        !          1000: 
        !          1001:       /* translate this address: */
        !          1002:       tlb_mash
        !          1003:        = _tme_stp222x_iommu_tlb_mash(stp222x,
        !          1004:                                      io_address,
        !          1005:                                      TME_BUS_CYCLE_READ);
        !          1006: 
        !          1007:       /* if this address hit an IOMMU TLB entry: */
        !          1008:       if ((tlb_mash
        !          1009:           & (TME_STP222X_IOMMU_TLB_MASH_FIXED
        !          1010:              | TME_STP222X_IOMMU_TLB_MASH_MISS)) == 0) {
        !          1011: 
        !          1012:        /* invalidate this entry: */
        !          1013:        tlb_i = TME_STP222X_IOMMU_TLB_MASH_TLB_I_WHICH(tlb_mash);
        !          1014:        stp222x->tme_stp222x_iommu_tlb_data[tlb_i] &= ~TME_STP222X_IOMMU_TLB_DATA_V;
        !          1015: 
        !          1016:        /* invalidate all of the TLBs associated with this IOMMU TLB
        !          1017:           entry: */
        !          1018:        tlb_list = &stp222x->tme_stp222x_iommu_tlb_list[tlb_i];
        !          1019:        tlb_list_i = TME_STP222X_TLB_LIST_TOKENS_COUNT - 1;
        !          1020:        do {
        !          1021:          token = tlb_list->tme_stp222x_tlb_list_tokens[tlb_list_i];
        !          1022:          if (token != NULL) {
        !          1023:            tlb_list->tme_stp222x_tlb_list_tokens[tlb_list_i] = NULL;
        !          1024:            tme_token_invalidate(token);
        !          1025:          }
        !          1026:        } while (--tlb_list_i >= 0);
        !          1027:       }
        !          1028:       name = "FLUSH";
        !          1029:       break;
        !          1030: 
        !          1031:     default:
        !          1032:       return;
        !          1033:     }
        !          1034:   }
        !          1035: 
        !          1036:   /* otherwise, this is a read: */
        !          1037:   else {
        !          1038: 
        !          1039:     /* dispatch on the register: */
        !          1040:     switch (reggroup_index) {
        !          1041:     case TME_STP222X_IOMMU_REGGROUP_INDEX_CR:
        !          1042:       reg->tme_stp222x_reg_value = stp222x->tme_stp222x_iommu_cr;
        !          1043:       name = "CR";
        !          1044:       break;
        !          1045:     case TME_STP222X_IOMMU_REGGROUP_INDEX_TSB:
        !          1046:       reg->tme_stp222x_reg_value = stp222x->tme_stp222x_iommu_tsb;
        !          1047:       name = "TSB";
        !          1048:       break;
        !          1049: /*  case TME_STP222X_IOMMU_REGGROUP_INDEX_FLUSH: */
        !          1050:     default:
        !          1051:       return;
        !          1052:     }
        !          1053:   }
        !          1054: 
        !          1055:   tme_log(TME_STP222X_LOG_HANDLE(stp222x), 2000, TME_OK,
        !          1056:          (TME_STP222X_LOG_HANDLE(stp222x),
        !          1057:           _("IOMMU %s %s 0x%" TME_PRIx64),
        !          1058:           name,
        !          1059:           (reg->tme_stp222x_reg_write
        !          1060:            ? "<-"
        !          1061:            : "->"),
        !          1062:           reg->tme_stp222x_reg_value));
        !          1063: 
        !          1064:   /* this register access has been completed: */
        !          1065:   reg->tme_stp222x_reg_completed = TRUE;
        !          1066: }
        !          1067: 
        !          1068: /* the IOMMU diagnostic register handler: */
        !          1069: void
        !          1070: tme_stp222x_iommu_regs_diag(struct tme_stp222x *stp222x,
        !          1071:                            struct tme_stp222x_reg *reg)
        !          1072: {
        !          1073:   tme_uint32_t reggroup_0_3;
        !          1074:   tme_uint32_t reggroup_index;
        !          1075:   const char *name;
        !          1076:   signed long tlb_i;
        !          1077:   unsigned int lru_i;
        !          1078:   tme_uint32_t io_address;
        !          1079:   tme_uint32_t io_address_tag;
        !          1080:   tme_uint32_t compare;
        !          1081:   tme_uint32_t tlb_tag;
        !          1082: 
        !          1083:   /* XXX FIXME - what happens if diagnostics aren't enabled? */
        !          1084:   if ((stp222x->tme_stp222x_iommu_cr
        !          1085:        & TME_STP222X_IOMMU_CR_MMU_DE) == 0) {
        !          1086:     abort();
        !          1087:   }
        !          1088: 
        !          1089:   /* get the register: */
        !          1090:   reggroup_0_3 = TME_STP222X_REGGROUP_WHICH(reg->tme_stp222x_reg_address) & 0xf;
        !          1091:   reggroup_index = TME_STP222X_REGGROUP_INDEX(reg->tme_stp222x_reg_address);
        !          1092: 
        !          1093:   /* if this is a write: */
        !          1094:   if (reg->tme_stp222x_reg_write) {
        !          1095: 
        !          1096:     switch (reggroup_0_3) {
        !          1097:     case 0x4: /* STP2220 0x44, STP2222 0xa4 */
        !          1098:       switch (reggroup_index) {
        !          1099:       case TME_STP222X_IOMMU_REGGROUP_INDEX_DIAG4_VA:
        !          1100: 
        !          1101:        /* get the address: */
        !          1102:        io_address = 0 - (tme_uint32_t) TME_STP222X_IOMMU_SIZE_8KB;
        !          1103:        io_address &= reg->tme_stp222x_reg_value;
        !          1104:        stp222x->tme_stp222x_iommu_va = io_address;
        !          1105: 
        !          1106:        /* convert the address into a VPN: */
        !          1107:        io_address_tag = io_address / TME_STP222X_IOMMU_SIZE_8KB;
        !          1108: 
        !          1109:        /* compare this address to all TLB tags: */
        !          1110:        compare = 0;
        !          1111:        tlb_i = TME_STP222X_IOMMU_TLB_SIZE - 1;
        !          1112:        do {
        !          1113:          compare <<= 1;
        !          1114:          tlb_tag = stp222x->tme_stp222x_iommu_tlb_tags[tlb_i];
        !          1115:          if (((tlb_tag
        !          1116:                ^ io_address_tag)
        !          1117:               & _tme_stp222x_iommu_tlb_tag_vpn_mask(tlb_tag)) == 0) {
        !          1118:            compare++;
        !          1119:          }
        !          1120:        } while (--tlb_i >= 0);
        !          1121:        stp222x->tme_stp222x_iommu_compare = compare;
        !          1122:        name = "VA";
        !          1123:        break;
        !          1124: 
        !          1125:       case TME_STP222X_IOMMU_REGGROUP_INDEX_DIAG4_COMPARE:
        !          1126:        reg->tme_stp222x_reg_completed = TRUE;
        !          1127:        return;
        !          1128:       default:
        !          1129:        return;
        !          1130:       }
        !          1131: 
        !          1132:     default: /* STP2220 0x45, STP2222 0xa5 */
        !          1133:       assert (reggroup_0_3 == 0x5);
        !          1134:       if (__tme_predict_false(reggroup_index < TME_STP222X_IOMMU_TLB_SIZE)) {
        !          1135:        return;
        !          1136:       }
        !          1137:       stp222x->tme_stp222x_iommu_tlb_tags[reggroup_index - TME_STP222X_IOMMU_TLB_SIZE]
        !          1138:        = reg->tme_stp222x_reg_value;
        !          1139:       name = "TLB_TAG";
        !          1140:       break;
        !          1141: 
        !          1142:     case 0x6: /* STP2220 0x46, STP2222 0xa6 */
        !          1143:       if (__tme_predict_false(reggroup_index >= TME_STP222X_IOMMU_TLB_SIZE)) {
        !          1144:        return;
        !          1145:       }
        !          1146:       stp222x->tme_stp222x_iommu_tlb_data[reggroup_index]
        !          1147:        = reg->tme_stp222x_reg_value;
        !          1148:       name = "TLB_DATA";
        !          1149:       break;
        !          1150:     }
        !          1151:   }
        !          1152: 
        !          1153:   /* otherwise, this is a read: */
        !          1154:   else {
        !          1155: 
        !          1156:     switch (reggroup_0_3) {
        !          1157:     case 0x4: /* STP2220 0x44, STP2222 0xa4 */
        !          1158:       switch (reggroup_index) {
        !          1159:       case TME_STP222X_IOMMU_REGGROUP_INDEX_DIAG4_VA:
        !          1160:        reg->tme_stp222x_reg_value = stp222x->tme_stp222x_iommu_va;
        !          1161:        name = "VA";
        !          1162:        break;
        !          1163:       case TME_STP222X_IOMMU_REGGROUP_INDEX_DIAG4_COMPARE:
        !          1164:        reg->tme_stp222x_reg_value = stp222x->tme_stp222x_iommu_compare;
        !          1165:        name = "COMPARE";
        !          1166:        break;
        !          1167:       default:
        !          1168:        return;
        !          1169:       }
        !          1170: 
        !          1171:     default: /* STP2220 0x45, STP2222 0xa5 */
        !          1172:       assert (reggroup_0_3 == 0x5);
        !          1173:       if (__tme_predict_false(reggroup_index < TME_STP222X_IOMMU_TLB_SIZE)) {
        !          1174:        tlb_i = stp222x->tme_stp222x_iommu_tlb_i_mru;
        !          1175:        for (lru_i = reggroup_index; ++lru_i != TME_STP222X_IOMMU_TLB_SIZE; ) {
        !          1176:          tlb_i = stp222x->tme_stp222x_iommu_lru_next(tlb_i);
        !          1177:        }
        !          1178:        reg->tme_stp222x_reg_value = tlb_i;
        !          1179:        name = "LRU";
        !          1180:       }
        !          1181:       else {
        !          1182:        reg->tme_stp222x_reg_value
        !          1183:          = stp222x->tme_stp222x_iommu_tlb_tags[reggroup_index - TME_STP222X_IOMMU_TLB_SIZE];
        !          1184:        name = "TLB_TAG";
        !          1185:       }
        !          1186:       break;
        !          1187: 
        !          1188:     case 0x6: /* STP2220 0x46, STP2222 0xa6 */
        !          1189:       if (__tme_predict_false(reggroup_index >= TME_STP222X_IOMMU_TLB_SIZE)) {
        !          1190:        return;
        !          1191:       }
        !          1192:       reg->tme_stp222x_reg_value
        !          1193:        = stp222x->tme_stp222x_iommu_tlb_data[reggroup_index];
        !          1194:       name = "TLB_DATA";
        !          1195:       break;
        !          1196:     }
        !          1197:   }
        !          1198: 
        !          1199:   if (reggroup_0_3 == 0x4) {  /* STP2220 0x44, STP2222 0xa4 */
        !          1200:     tme_log(TME_STP222X_LOG_HANDLE(stp222x), 2000, TME_OK,
        !          1201:            (TME_STP222X_LOG_HANDLE(stp222x),
        !          1202:             _("IOMMU %s %s 0x%" TME_PRIx64),
        !          1203:             name,
        !          1204:             (reg->tme_stp222x_reg_write
        !          1205:              ? "<-"
        !          1206:              : "->"),
        !          1207:             reg->tme_stp222x_reg_value));
        !          1208:   }
        !          1209:   else {
        !          1210:     tme_log(TME_STP222X_LOG_HANDLE(stp222x), 2000, TME_OK,
        !          1211:            (TME_STP222X_LOG_HANDLE(stp222x),
        !          1212:             _("IOMMU %s[%u] %s 0x%" TME_PRIx64),
        !          1213:             name,
        !          1214:             (reggroup_index % TME_STP222X_IOMMU_TLB_SIZE),
        !          1215:             (reg->tme_stp222x_reg_write
        !          1216:              ? "<-"
        !          1217:              : "->"),
        !          1218:             reg->tme_stp222x_reg_value));
        !          1219:   }
        !          1220: 
        !          1221:   /* this register access has been completed: */
        !          1222:   reg->tme_stp222x_reg_completed = TRUE;
        !          1223: }
        !          1224: 
        !          1225: /* this initializes the IOMMU: */
        !          1226: void
        !          1227: tme_stp222x_iommu_init(struct tme_stp222x *stp222x)
        !          1228: {
        !          1229:   unsigned long tlb_i;
        !          1230: 
        !          1231:   /* initialize the TSB TLB: */
        !          1232:   tme_token_init(&stp222x->tme_stp222x_iommu_tsb_tlb_token);
        !          1233:   stp222x->tme_stp222x_iommu_tsb_tlb.tme_bus_tlb_token = &stp222x->tme_stp222x_iommu_tsb_tlb_token;
        !          1234: 
        !          1235:   /* initialize the LRU list: */
        !          1236:   tlb_i = 0;
        !          1237:   stp222x->tme_stp222x_iommu_tlb_i_mru = tlb_i;
        !          1238:   do {
        !          1239:     stp222x->tme_stp222x_iommu_lru_prev(tlb_i) = (tlb_i - 1) % TME_STP222X_IOMMU_TLB_SIZE;
        !          1240:     stp222x->tme_stp222x_iommu_lru_next(tlb_i) = (tlb_i + 1) % TME_STP222X_IOMMU_TLB_SIZE;
        !          1241:     tlb_i = (tlb_i + 1) % TME_STP222X_IOMMU_TLB_SIZE;
        !          1242:   } while (tlb_i != 0);
        !          1243: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.