Annotation of tme/ic/stp22xx/stp222x-iommu.c, revision 1.1.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.