--- tme/machine/sun/sun-mmu.c 2018/04/24 16:37:52 1.1 +++ tme/machine/sun/sun-mmu.c 2018/04/24 16:43:36 1.1.1.4 @@ -1,4 +1,4 @@ -/* $Id: sun-mmu.c,v 1.1 2018/04/24 16:37:52 root Exp $ */ +/* $Id: sun-mmu.c,v 1.1.1.4 2018/04/24 16:43:36 root Exp $ */ /* machine/sun/sun-mmu.c - classic Sun MMU emulation implementation: */ @@ -34,13 +34,14 @@ */ #include -_TME_RCSID("$Id: sun-mmu.c,v 1.1 2018/04/24 16:37:52 root Exp $"); +_TME_RCSID("$Id: sun-mmu.c,v 1.1.1.4 2018/04/24 16:43:36 root Exp $"); /* includes: */ #include /* macros: */ #define TME_SUN_MMU_PMEG_TLBS (16) +#define TME_SUN_MMU_CONTEXT_TLBS (8) /* structures: */ @@ -50,17 +51,8 @@ struct tme_sun_mmu_tlb_set { /* the next allocated TLB set: */ struct tme_sun_mmu_tlb_set *tme_sun_mmu_tlb_set_next; - /* the user's pointer into the TLB set: */ - TME_ATOMIC_POINTER_TYPE(struct tme_bus_tlb **) tme_sun_mmu_tlb_set_pointer; - - /* the base of this TLB set: */ - struct tme_bus_tlb *tme_sun_mmu_tlb_set_base; - - /* the size of one TLB entry: */ - unsigned int tme_sun_mmu_tlb_set_sizeof_one; - - /* the number of TLB entries per context in the set: */ - unsigned int tme_sun_mmu_tlb_set_count; + /* the TLB set information: */ + struct tme_bus_tlb_set_info tme_sun_mmu_tlb_set_info; }; /* one PMEG in a classic two-level Sun MMU: */ @@ -68,7 +60,7 @@ struct tme_sun_mmu_pmeg { /* the current list of TLBs using a page table entry in this PMEG, and the head within that list: */ - struct tme_bus_tlb *tme_sun_mmu_pmeg_tlbs[TME_SUN_MMU_PMEG_TLBS]; + struct tme_token *tme_sun_mmu_pmeg_tlb_tokens[TME_SUN_MMU_PMEG_TLBS]; unsigned int tme_sun_mmu_pmeg_tlbs_head; }; @@ -83,10 +75,16 @@ struct tme_sun_mmu { #define tme_sun_mmu_pteindex_bits tme_sun_mmu_info.tme_sun_mmu_info_pteindex_bits #define tme_sun_mmu_contexts tme_sun_mmu_info.tme_sun_mmu_info_contexts #define tme_sun_mmu_pmegs_count tme_sun_mmu_info.tme_sun_mmu_info_pmegs -#define tme_sun_mmu_seginv tme_sun_mmu_info.tme_sun_mmu_info_seginv #define _tme_sun_mmu_tlb_fill_private tme_sun_mmu_info.tme_sun_mmu_info_tlb_fill_private #define _tme_sun_mmu_tlb_fill tme_sun_mmu_info.tme_sun_mmu_info_tlb_fill + /* if nonzero, this address space has a hole, and this has only the + last true address bit set: */ + tme_uint32_t tme_sun_mmu_address_hole_bit; + + /* a PTE for addresses in the hole. this is always all-bits-zero: */ + struct tme_sun_mmu_pte tme_sun_mmu_address_hole_pte; + /* the number of bits in a segment map index: */ tme_uint8_t tme_sun_mmu_segment_bits; @@ -101,6 +99,11 @@ struct tme_sun_mmu { /* the allocated TLB sets: */ struct tme_sun_mmu_tlb_set *tme_sun_mmu_tlb_sets; + + /* the current list of TLBs that must be invalidated when the + context changes: */ + struct tme_token *tme_sun_mmu_context_tlb_tokens[TME_SUN_MMU_CONTEXT_TLBS]; + unsigned int tme_sun_mmu_context_tlbs_head; }; /* this creates a classic two-level Sun MMU: */ @@ -117,6 +120,22 @@ tme_sun_mmu_new(struct tme_sun_mmu_info /* copy the user-provided information: */ mmu->tme_sun_mmu_info = *info; + /* if there is an address hole: */ + if (mmu->tme_sun_mmu_info.tme_sun_mmu_info_topindex_bits < 0) { + + /* there must be 32 address bits: */ + assert (mmu->tme_sun_mmu_address_bits == 32); + + /* adjust the number of address bits for the hole: */ + mmu->tme_sun_mmu_address_bits += (mmu->tme_sun_mmu_info.tme_sun_mmu_info_topindex_bits + 1); + + /* make the hole address bit: */ + mmu->tme_sun_mmu_address_hole_bit = TME_BIT(mmu->tme_sun_mmu_address_bits - 1); + + /* zero the number of top index bits: */ + mmu->tme_sun_mmu_info.tme_sun_mmu_info_topindex_bits = 0; + } + /* allocate the segment map and initialize it to all invalid: */ mmu->tme_sun_mmu_segment_bits = (mmu->tme_sun_mmu_address_bits - (mmu->tme_sun_mmu_pteindex_bits @@ -125,7 +144,7 @@ tme_sun_mmu_new(struct tme_sun_mmu_info * (1 << mmu->tme_sun_mmu_segment_bits)); mmu->tme_sun_mmu_segment_map = tme_new(unsigned short, segmap_count); for (segmap_i = 0; segmap_i < segmap_count; segmap_i++) { - mmu->tme_sun_mmu_segment_map[segmap_i] = mmu->tme_sun_mmu_seginv; + mmu->tme_sun_mmu_segment_map[segmap_i] = mmu->tme_sun_mmu_pmegs_count - 1; } /* allocate the PMEGs: */ @@ -152,6 +171,17 @@ _tme_sun_mmu_lookup(struct tme_sun_mmu * unsigned short segment_map_index; unsigned short pmeg; + /* if there is an address hole, and this address is in it: */ + if (__tme_predict_false(((address + + (address & mmu->tme_sun_mmu_address_hole_bit)) + & (((tme_uint32_t) 0) + - mmu->tme_sun_mmu_address_hole_bit)) != 0)) { + + /* return the hole PTE, and zero for the segment map index: */ + *_pte = &mmu->tme_sun_mmu_address_hole_pte; + return (0); + } + /* lose the page offset bits: */ address >>= mmu->tme_sun_mmu_pgoffset_bits; @@ -183,17 +213,17 @@ _tme_sun_mmu_pmeg_invalidate(struct tme_ { struct tme_sun_mmu_pmeg *pmeg; int tlb_i; - struct tme_bus_tlb *tlb; + struct tme_token *token; /* get the PMEG: */ pmeg = mmu->tme_sun_mmu_pmegs + mmu->tme_sun_mmu_segment_map[segment_map_index]; /* invalidate all of the TLBs: */ for (tlb_i = 0; tlb_i < TME_SUN_MMU_PMEG_TLBS; tlb_i++) { - tlb = pmeg->tme_sun_mmu_pmeg_tlbs[tlb_i]; - pmeg->tme_sun_mmu_pmeg_tlbs[tlb_i] = NULL; - if (tlb != NULL) { - tme_bus_tlb_invalidate(tlb); + token = pmeg->tme_sun_mmu_pmeg_tlb_tokens[tlb_i]; + pmeg->tme_sun_mmu_pmeg_tlb_tokens[tlb_i] = NULL; + if (token != NULL) { + tme_token_invalidate(token); } } } @@ -211,14 +241,6 @@ tme_sun_mmu_pte_get(void *_mmu, tme_uint mmu = (struct tme_sun_mmu *) _mmu; segment_map_index = _tme_sun_mmu_lookup(mmu, context, address, &pte); -#if 0 - /* if this segment is invalid, return failure: */ - if (mmu->tme_sun_mmu_segment_map[segment_map_index] - == mmu->tme_sun_mmu_seginv) { - return (ENOENT); - } -#endif - /* otherwise, copy the PTE: */ *_pte = *pte; return (TME_OK); @@ -236,14 +258,9 @@ tme_sun_mmu_pte_set(void *_mmu, tme_uint /* lookup this address: */ mmu = (struct tme_sun_mmu *) _mmu; segment_map_index = _tme_sun_mmu_lookup(mmu, context, address, &pte); - -#if 0 - /* if this segment is invalid, return failure: */ - if (mmu->tme_sun_mmu_segment_map[segment_map_index] - == mmu->tme_sun_mmu_seginv) { - return (ENOENT); + if (__tme_predict_false(pte == &mmu->tme_sun_mmu_address_hole_pte)) { + return (TME_OK); } -#endif /* invalidate all TLB entries that are affected by changes to this PMEG: */ _tme_sun_mmu_pmeg_invalidate(mmu, segment_map_index); @@ -264,6 +281,9 @@ tme_sun_mmu_segmap_get(void *_mmu, tme_u /* lookup this address: */ mmu = (struct tme_sun_mmu *) _mmu; segment_map_index = _tme_sun_mmu_lookup(mmu, context, address, &pte); + if (__tme_predict_false(pte == &mmu->tme_sun_mmu_address_hole_pte)) { + return (mmu->tme_sun_mmu_pmegs_count - 1); + } pmeg = mmu->tme_sun_mmu_segment_map[segment_map_index]; tme_log(&mmu->tme_sun_mmu_element->tme_element_log_handle, 1000, TME_OK, (&mmu->tme_sun_mmu_element->tme_element_log_handle, @@ -285,21 +305,14 @@ tme_sun_mmu_segmap_set(void *_mmu, tme_u /* lookup this address: */ mmu = (struct tme_sun_mmu *) _mmu; segment_map_index = _tme_sun_mmu_lookup(mmu, context, address, &pte); - - /* if the old segment is valid, invalidate all TLB entries that - are affected by changes to this PMEG - losing a spot in the - segment map counts as such a change: */ - if ( -#if 0 - mmu->tme_sun_mmu_segment_map[segment_map_index] - != mmu->tme_sun_mmu_seginv -#else - TRUE -#endif - ) { - _tme_sun_mmu_pmeg_invalidate(mmu, segment_map_index); + if (__tme_predict_false(pte == &mmu->tme_sun_mmu_address_hole_pte)) { + return; } + /* invalidate all TLB entries that are affected by changes to this + PMEG - losing a spot in the segment map counts as such a change: */ + _tme_sun_mmu_pmeg_invalidate(mmu, segment_map_index); + /* set the new segment: */ mmu->tme_sun_mmu_segment_map[segment_map_index] = pmeg; tme_log(&mmu->tme_sun_mmu_element->tme_element_log_handle, 1000, TME_OK, @@ -318,11 +331,12 @@ tme_sun_mmu_tlb_fill(void *_mmu, struct struct tme_sun_mmu *mmu; unsigned short segment_map_index; struct tme_sun_mmu_pte *pte; - tme_bus_addr_t addr_first, addr_last; + tme_bus_addr32_t addr_first, addr_last; unsigned short protection, protection_other, tlb_valid_for; tme_uint32_t physical_address; struct tme_sun_mmu_pmeg *pmeg; - struct tme_bus_tlb tlb_virtual, *tlb_old; + struct tme_bus_tlb tlb_virtual; + struct tme_token *token_old; int tlb_i; /* the access must be a read or write by the system or user: */ @@ -339,21 +353,25 @@ tme_sun_mmu_tlb_fill(void *_mmu, struct addr_last = (address | (TME_BIT(mmu->tme_sun_mmu_pgoffset_bits) - 1)); /* remember this TLB entry in the PMEG: */ - pmeg = mmu->tme_sun_mmu_pmegs + mmu->tme_sun_mmu_segment_map[segment_map_index]; - tlb_i = pmeg->tme_sun_mmu_pmeg_tlbs_head; - tlb_old = pmeg->tme_sun_mmu_pmeg_tlbs[tlb_i]; - if (tlb_old != NULL) { - tme_bus_tlb_invalidate(tlb_old); + if (__tme_predict_true(pte != &mmu->tme_sun_mmu_address_hole_pte)) { + pmeg = mmu->tme_sun_mmu_pmegs + mmu->tme_sun_mmu_segment_map[segment_map_index]; + tlb_i = pmeg->tme_sun_mmu_pmeg_tlbs_head; + token_old = pmeg->tme_sun_mmu_pmeg_tlb_tokens[tlb_i]; + if (token_old != NULL + && token_old != tlb->tme_bus_tlb_token) { + tme_token_invalidate(token_old); + } + pmeg->tme_sun_mmu_pmeg_tlb_tokens[tlb_i] + = tlb->tme_bus_tlb_token; + pmeg->tme_sun_mmu_pmeg_tlbs_head = (tlb_i + 1) & (TME_SUN_MMU_PMEG_TLBS - 1); } - pmeg->tme_sun_mmu_pmeg_tlbs[tlb_i] = tlb; - pmeg->tme_sun_mmu_pmeg_tlbs_head = (tlb_i + 1) & (TME_SUN_MMU_PMEG_TLBS - 1); /* if this page is invalid, return the page-invalid cycle handler, which is valid for reading and writing for the user and system: */ if (!(pte->tme_sun_mmu_pte_flags & TME_SUN_MMU_PTE_VALID)) { tme_bus_tlb_initialize(tlb); - TME_ATOMIC_WRITE(tme_bus_addr_t, tlb->tme_bus_tlb_addr_first, addr_first); - TME_ATOMIC_WRITE(tme_bus_addr_t, tlb->tme_bus_tlb_addr_last, addr_last); + tlb->tme_bus_tlb_addr_first = addr_first; + tlb->tme_bus_tlb_addr_last = addr_last; tlb->tme_bus_tlb_cycles_ok = TME_BUS_CYCLE_READ | TME_BUS_CYCLE_WRITE; tlb->tme_bus_tlb_cycle_private = mmu->tme_sun_mmu_info.tme_sun_mmu_info_invalid_private; tlb->tme_bus_tlb_cycle = mmu->tme_sun_mmu_info.tme_sun_mmu_info_invalid; @@ -405,8 +423,8 @@ tme_sun_mmu_tlb_fill(void *_mmu, struct abort(); } tme_bus_tlb_initialize(tlb); - TME_ATOMIC_WRITE(tme_bus_addr_t, tlb->tme_bus_tlb_addr_first, addr_first); - TME_ATOMIC_WRITE(tme_bus_addr_t, tlb->tme_bus_tlb_addr_last, addr_last); + tlb->tme_bus_tlb_addr_first = addr_first; + tlb->tme_bus_tlb_addr_last = addr_last; tlb->tme_bus_tlb_cycles_ok = (TME_BUS_CYCLE_WRITE | (protection == TME_SUN_MMU_PTE_PROT_ERROR ? TME_BUS_CYCLE_READ @@ -431,14 +449,15 @@ tme_sun_mmu_tlb_fill(void *_mmu, struct : TME_BUS_CYCLE_READ)); /* create the mapping TLB entry, and update the PTE flags: */ - TME_ATOMIC_WRITE(tme_bus_addr_t, tlb_virtual.tme_bus_tlb_addr_first, addr_first); - TME_ATOMIC_WRITE(tme_bus_addr_t, tlb_virtual.tme_bus_tlb_addr_last, addr_last); + tlb_virtual.tme_bus_tlb_addr_first = addr_first; + tlb_virtual.tme_bus_tlb_addr_last = addr_last; pte->tme_sun_mmu_pte_flags |= TME_SUN_MMU_PTE_REF; tlb_virtual.tme_bus_tlb_cycles_ok = TME_BUS_CYCLE_READ; if (access == TME_SUN_MMU_PTE_PROT_RW) { pte->tme_sun_mmu_pte_flags |= TME_SUN_MMU_PTE_MOD; } - if (pte->tme_sun_mmu_pte_flags & TME_SUN_MMU_PTE_MOD) { + if (protection == TME_SUN_MMU_PTE_PROT_RW + && (pte->tme_sun_mmu_pte_flags & TME_SUN_MMU_PTE_MOD)) { tlb_virtual.tme_bus_tlb_cycles_ok |= TME_BUS_CYCLE_WRITE; } @@ -455,8 +474,6 @@ tme_sun_mmu_tlbs_invalidate(void *_mmu) { struct tme_sun_mmu *mmu; struct tme_sun_mmu_tlb_set *tlb_set; - struct tme_bus_tlb *tlb; - unsigned long tlb_i; /* recover our MMU: */ mmu = (struct tme_sun_mmu *) _mmu; @@ -465,72 +482,126 @@ tme_sun_mmu_tlbs_invalidate(void *_mmu) for (tlb_set = mmu->tme_sun_mmu_tlb_sets; tlb_set != NULL; tlb_set = tlb_set->tme_sun_mmu_tlb_set_next) { - tlb = tlb_set->tme_sun_mmu_tlb_set_base; - tlb_i = ((unsigned long) tlb_set->tme_sun_mmu_tlb_set_count) * mmu->tme_sun_mmu_contexts; - for (; tlb_i-- > 0; ) { - tme_bus_tlb_invalidate(tlb); - tlb = (struct tme_bus_tlb *) (((tme_uint8_t *) tlb) + tlb_set->tme_sun_mmu_tlb_set_sizeof_one); - } + tme_bus_tlb_set_invalidate(&tlb_set->tme_sun_mmu_tlb_set_info); } } -/* this sets the user context register: */ +/* this adds a TLB entry as dependent on the current context: */ void -tme_sun_mmu_tlbs_context_set(void *_mmu, tme_uint8_t context) +tme_sun_mmu_context_add(void *_mmu, + const struct tme_bus_tlb *tlb) { struct tme_sun_mmu *mmu; - struct tme_sun_mmu_tlb_set *tlb_set; + tme_uint32_t address; + tme_uint32_t segment_bits; + tme_uint32_t segments_per_context; + signed long segment_map_index; + tme_uint32_t pmeg; + unsigned long tlb_i; + struct tme_token *token_old; /* recover our MMU: */ mmu = (struct tme_sun_mmu *) _mmu; - /* we have to update each TLB set to reflect the context change: */ - for (tlb_set = mmu->tme_sun_mmu_tlb_sets; - tlb_set != NULL; - tlb_set = tlb_set->tme_sun_mmu_tlb_set_next) { - TME_ATOMIC_WRITE(struct tme_bus_tlb *, - *tlb_set->tme_sun_mmu_tlb_set_pointer, - (struct tme_bus_tlb *) - (((tme_uint8_t *) tlb_set->tme_sun_mmu_tlb_set_base) - + (((unsigned long) tlb_set->tme_sun_mmu_tlb_set_count) - * tlb_set->tme_sun_mmu_tlb_set_sizeof_one - * context))); - } + /* get the address used with the MMU: */ + /* NB: if there is an address hole, this address could be in it. if + it is, it doesn't matter if we decide that this TLB entry is + valid in all contexts or not - this TLB entry never needs to be + invalidated because of a context change: */ + address = tlb->tme_bus_tlb_addr_first; + + /* get the number of bits in a segment number: */ + segment_bits = mmu->tme_sun_mmu_segment_bits; + + /* get the number of segment map entries per context: */ + segments_per_context = 1 << segment_bits; + + /* get the segment map index for this address in the last + context: */ + segment_map_index + = (((address + >> (mmu->tme_sun_mmu_pgoffset_bits + + mmu->tme_sun_mmu_pteindex_bits)) + & (segments_per_context - 1)) + + ((mmu->tme_sun_mmu_contexts - 1) + << segment_bits)); + + /* get the PMEG for this address in the last context: */ + pmeg = mmu->tme_sun_mmu_segment_map[segment_map_index]; + + /* there must be at least two contexts: */ + assert (mmu->tme_sun_mmu_contexts >= 2); + + /* loop over the segment map indices for this address in all other + contexts: */ + segment_map_index -= segments_per_context; + do { + + /* if the PMEG for this address in this context is different: */ + if (__tme_predict_false(mmu->tme_sun_mmu_segment_map[segment_map_index] != pmeg)) { + + /* this address doesn't have the same mapping in all contexts, + so we must invalidate this TLB entry when the context + changes: */ + tlb_i = mmu->tme_sun_mmu_context_tlbs_head; + token_old = mmu->tme_sun_mmu_context_tlb_tokens[tlb_i]; + if (token_old != NULL + && token_old != tlb->tme_bus_tlb_token) { + tme_token_invalidate(token_old); + } + mmu->tme_sun_mmu_context_tlb_tokens[tlb_i] = tlb->tme_bus_tlb_token; + mmu->tme_sun_mmu_context_tlbs_head = (tlb_i + 1) % TME_SUN_MMU_CONTEXT_TLBS; + + return; + } + + } while ((segment_map_index -= segments_per_context) >= 0); + + /* this address has the same mapping in all contexts, so we don't + need to invalidate this TLB entry when the context changes: */ } -/* this allocates a new TLB set: */ +/* this is called after a context switch, to invalidate TLB entries + that were dependent on the previous context: */ +void +tme_sun_mmu_context_switched(void *_mmu) +{ + struct tme_sun_mmu *mmu; + signed long tlb_i; + struct tme_token *token; + + /* recover our MMU: */ + mmu = (struct tme_sun_mmu *) _mmu; + + /* invalidate all of the TLBs that depended on the previous + context: */ + tlb_i = TME_SUN_MMU_CONTEXT_TLBS - 1; + do { + token = mmu->tme_sun_mmu_context_tlb_tokens[tlb_i]; + mmu->tme_sun_mmu_context_tlb_tokens[tlb_i] = NULL; + if (token != NULL) { + tme_token_invalidate(token); + } + } while (--tlb_i >= 0); +} + +/* this adds a new TLB set: */ int -tme_sun_mmu_tlb_set_allocate(void *_mmu, - unsigned int count, unsigned int sizeof_one, - TME_ATOMIC_POINTER_TYPE(struct tme_bus_tlb **) _tlbs) +tme_sun_mmu_tlb_set_add(void *_mmu, + struct tme_bus_tlb_set_info *tlb_set_info) { struct tme_sun_mmu *mmu; - struct tme_bus_tlb *tlbs, *tlb; - unsigned long tlb_i; struct tme_sun_mmu_tlb_set *tlb_set; /* recover our mmu: */ mmu = (struct tme_sun_mmu *) _mmu; - /* allocate and initialize a set of TLBs: */ - tlb_i = ((unsigned long) mmu->tme_sun_mmu_contexts) * count; - tlbs = (struct tme_bus_tlb *) tme_malloc(tlb_i * sizeof_one); - tlb = tlbs; - for (; tlb_i-- > 0; ) { - tme_bus_tlb_invalidate(tlb); - tlb = (struct tme_bus_tlb *) (((tme_uint8_t *) tlb) + sizeof_one); - } - /* remember this set: */ tlb_set = tme_new0(struct tme_sun_mmu_tlb_set, 1); tlb_set->tme_sun_mmu_tlb_set_next = mmu->tme_sun_mmu_tlb_sets; - tlb_set->tme_sun_mmu_tlb_set_pointer = _tlbs; - tlb_set->tme_sun_mmu_tlb_set_base = tlbs; - tlb_set->tme_sun_mmu_tlb_set_sizeof_one = sizeof_one; - tlb_set->tme_sun_mmu_tlb_set_count = count; + tlb_set->tme_sun_mmu_tlb_set_info = *tlb_set_info; mmu->tme_sun_mmu_tlb_sets = tlb_set; - TME_ATOMIC_WRITE(struct tme_bus_tlb *, *_tlbs, tlbs); return (TME_OK); }