src/ssx/ppc405/ppc405_mmu.c
/// Define a memory region for MMU protection purposes
///
/// \param effective_address The effective (virtual) base address of the
/// region. This address must be at least 1KB aligned.
///
/// \param real_address The real base address of the region.  This address
/// must be at least 1KB aligned.
///
/// \param size The size of the region in bytes.  The size must be a multiple
/// of 1KB. A \a size of 0 is legal and creates no entries.
///
/// \param tlbhi_flags An OR combination of PPC405_TLBHI_* bits (excluding the
/// V-bit). This parameter is unlikely to be non-0 as it contains only the
/// little-endian (E) and U0 flags.
///
/// - TLBHI_E : Little Endian
/// - TLBHI_U0 : U0 mode access
///
/// \param tlblo_flags An OR combination of PPC405_TLBLO_* bits.  This
/// parameter defines WR and EX protection, as well as the 'WIMG' bits.
///
/// - TLBLO_EX : Executable code
/// - TLBLO_WR : Writable data
/// - TLBLO_W  : Write-through mode
/// - TLBLO_I  : Cache-inhibited
/// - TLBLO_M  : Memory Coherent (Implemented but Ignored)
/// - TLBLO_G  : Guarded
///
/// \param map If non-0, then a Ppc405TlbMap for the region is returned.
/// This map can be later used as an argument to ppc405_mmu_unmap() to unmap
/// the region.
///
/// This API creates fixed TLB entries that provide virtual-to-real address
/// translation and protection using a minimum number of TLB entries.  The
/// number of TLB entries is fixed, so there is no guarantee in general that
/// any particular memory map is feasible. In general it is helpful to make
/// sure that the effective and real memory ranges have similar alignment,
/// otherwise the algorithm will be forced to use small page sizes.
///
/// The caller is responsible for cache-correctness of this API.  If necessary
/// the caller should flush or invalidate memory areas whose protection
/// attributes have changed prior to and/or after invoking this API.
///
/// Note the the simple translation/protection protocol supported by this SSX
/// API does not support the "zone selection" field of the PPC405 TLB
/// entry. In SSX the PID is always 0.
///
/// If SSX_ERROR_CHECK_API is configured, the API checks each new TLB entry to
/// ensure that it does not duplicate an existing entry.  The check only
/// covers duplicated effective addresses (which are not supported by the
/// hardware), not the real addresses.
///
/// \retval 0 Success
///
/// \retval -PPC405_MMU_INVALID_ARGUMENT Can be signalled by numerous errors
/// including improperly aigned memory regions, region size not a multiple of
/// 1KB, and illegal flags.
///
/// \retval -PPC405_TOO_MANY_TLB_ENTRIES There are not enough TLB entries left
/// to completely map the region. The state of the TLB at this point may be
/// inconsistent.
///
/// \retval -PPC405_DUPLICATE_TLB_ENTRY The requested mapping would duplicate
/// all or part of a currently existing TLB entry.  Duplicate entries are not
/// supported in the 405 core and will cause a TLB miss if an address in a
/// duplicated range is accessed.
Define a memory region for MMU protection purposes
\param effective_address The effective (virtual) base address of the
region. This address must be at least 1KB aligned.

\param real_address The real base address of the region.  This address
must be at least 1KB aligned.

\param size The size of the region in bytes.  The size must be a multiple
of 1KB. A \a size of 0 is legal and creates no entries.

\param tlbhi_flags An OR combination of PPC405_TLBHI_* bits (excluding the
V-bit). This parameter is unlikely to be non-0 as it contains only the
little-endian (E) and U0 flags.

TLBHI_E : Little Endian
TLBHI_U0 : U0 mode access

\param tlblo_flags An OR combination of PPC405_TLBLO_* bits.  This
parameter defines WR and EX protection, as well as the 'WIMG' bits.



\param map If non-0, then a Ppc405TlbMap for the region is returned.
This map can be later used as an argument to ppc405_mmu_unmap() to unmap
the region.

This API creates fixed TLB entries that provide virtual-to-real address
translation and protection using a minimum number of TLB entries.  The
number of TLB entries is fixed, so there is no guarantee in general that
any particular memory map is feasible. In general it is helpful to make
sure that the effective and real memory ranges have similar alignment,
otherwise the algorithm will be forced to use small page sizes.

The caller is responsible for cache-correctness of this API.  If necessary
the caller should flush or invalidate memory areas whose protection
attributes have changed prior to and/or after invoking this API.

Note the the simple translation/protection protocol supported by this SSX
API does not support the "zone selection" field of the PPC405 TLB
entry. In SSX the PID is always 0.

If SSX_ERROR_CHECK_API is configured, the API checks each new TLB entry to
ensure that it does not duplicate an existing entry.  The check only
covers duplicated effective addresses (which are not supported by the
hardware), not the real addresses.

\retval 0 Success

\retval -PPC405_MMU_INVALID_ARGUMENT Can be signalled by numerous errors
including improperly aigned memory regions, region size not a multiple of
1KB, and illegal flags.

\retval -PPC405_TOO_MANY_TLB_ENTRIES There are not enough TLB entries left
to completely map the region. The state of the TLB at this point may be
inconsistent.

\retval -PPC405_DUPLICATE_TLB_ENTRY The requested mapping would duplicate
all or part of a currently existing TLB entry.  Duplicate entries are not
supported in the 405 core and will cause a TLB miss if an address in a
duplicated range is accessed.
Define a memory region for MMU protection purposes \ param effective_address The effective ( virtual ) base address of the region . This address must be at least 1KB aligned . \ param real_address The real base address of the region . This address must be at least 1KB aligned . \ param size The size of the region in bytes . The size must be a multiple of 1KB . A \ a size of 0 is legal and creates no entries . \ param tlbhi_flags An OR combination of PPC405_TLBHI_ * bits ( excluding the V - bit ) . This parameter is unlikely to be non - 0 as it contains only the little - endian ( E ) and U0 flags . TLBHI_E : Little Endian TLBHI_U0 : U0 mode access \ param tlblo_flags An OR combination of PPC405_TLBLO_ * bits . This parameter defines WR and EX protection as well as the ' WIMG ' bits . \ param map If non - 0 then a Ppc405TlbMap for the region is returned . This map can be later used as an argument to ppc405_mmu_unmap () to unmap the region . This API creates fixed TLB entries that provide virtual - to - real address translation and protection using a minimum number of TLB entries . The number of TLB entries is fixed so there is no guarantee in general that any particular memory map is feasible . In general it is helpful to make sure that the effective and real memory ranges have similar alignment otherwise the algorithm will be forced to use small page sizes . The caller is responsible for cache - correctness of this API . If necessary the caller should flush or invalidate memory areas whose protection attributes have changed prior to and / or after invoking this API . Note the the simple translation / protection protocol supported by this SSX API does not support the " zone selection " field of the PPC405 TLB entry . In SSX the PID is always 0 . If SSX_ERROR_CHECK_API is configured the API checks each new TLB entry to ensure that it does not duplicate an existing entry . The check only covers duplicated effective addresses ( which are not supported by the hardware ) not the real addresses . \ retval 0 Success \ retval - PPC405_MMU_INVALID_ARGUMENT Can be signalled by numerous errors including improperly aigned memory regions region size not a multiple of 1KB and illegal flags . \ retval - PPC405_TOO_MANY_TLB_ENTRIES There are not enough TLB entries left to completely map the region . The state of the TLB at this point may be inconsistent . \ retval - PPC405_DUPLICATE_TLB_ENTRY The requested mapping would duplicate all or part of a currently existing TLB entry . Duplicate entries are not supported in the 405 core and will cause a TLB miss if an address in a duplicated range is accessed .
int
ppc405_mmu_map(SsxAddress effective_address,
               SsxAddress real_address,
               size_t size, int tlbhi_flags, int tlblo_flags,
               Ppc405MmuMap* map)
{
    size_t log_page_size;
    size_t page_size = 0;
    Ppc405Tlbhi tlbhi;
    Ppc405Tlblo tlblo;
    Ppc405MmuMap local_map = 0;
    int tlb_entry;
    uint64_t bit;
    SsxMachineContext ctx;
    if (SSX_ERROR_CHECK_API)
    {
        uint64_t allocated;
        SsxAddress this_effective_address;
        int entry, overlap;
        SSX_ERROR_IF((effective_address % PPC405_PAGE_SIZE_MIN)           ||
                     (real_address % PPC405_PAGE_SIZE_MIN)                ||
                     (size % PPC405_PAGE_SIZE_MIN)                        ||
                     ((effective_address + size - 1) < effective_address) ||
                     (tlbhi_flags & ~TLBHI_LEGAL_FLAGS)                   ||
                     (tlblo_flags & ~TLBLO_LEGAL_FLAGS),
                     PPC405_MMU_INVALID_ARGUMENT);
        if (size != 0)
        {
            overlap = tlbsx(effective_address, &entry);
            allocated = ~__ppc405_tlb_free;
            while (!overlap && (allocated != 0))
            {
                entry = cntlz64(allocated);
                allocated &= ~((uint64_t)1 << (63 - entry));
                tlbhi.value = tlbrehi(entry);
                if (tlbhi.fields.v)
                {
                    this_effective_address =
                        tlbhi.fields.epn << PPC405_LOG_PAGE_SIZE_MIN;
                    overlap |=
                        (this_effective_address >=
                         effective_address) &&
                        (this_effective_address <=
                         (effective_address + size - 1));
                }
            }
            SSX_ERROR_IF(overlap, PPC405_DUPLICATE_TLB_ENTRY);
        }
    }
    while (size != 0)
    {
        ssx_critical_section_enter(SSX_CRITICAL, &ctx);
        if (SSX_ERROR_CHECK_API)
        {
            SSX_ERROR_IF_CRITICAL(__ppc405_tlb_free == 0,
                                  PPC405_TOO_MANY_TLB_ENTRIES,
                                  &ctx);
        }
        tlb_entry = cntlz64(__ppc405_tlb_free);
        bit = 0x8000000000000000ull >> tlb_entry;
        __ppc405_tlb_free &= ~bit;
        local_map |= bit;
        ssx_critical_section_exit(&ctx);
        log_page_size = PPC405_LOG_PAGE_SIZE_MAX;
        do
        {
            page_size = POW2_32(log_page_size);
            if ((size >= page_size) &&
                ((effective_address & (page_size - 1)) == 0) &&
                ((real_address & (page_size - 1)) == 0))
            {
                break;
            }
            else
            {
                log_page_size -= 2;
            }
        }
        while (1);
        size -= page_size;
        tlbhi.value = tlbhi_flags;
        tlbhi.fields.epn = effective_address >> PPC405_LOG_PAGE_SIZE_MIN;
        tlbhi.fields.size = (log_page_size - PPC405_LOG_PAGE_SIZE_MIN) / 2;
        tlbhi.fields.v = 1;
        tlblo.value = tlblo_flags;
        tlblo.fields.rpn = real_address >> PPC405_LOG_PAGE_SIZE_MIN;
        ssx_critical_section_enter(SSX_CRITICAL, &ctx);
        tlbwelo(tlb_entry, tlblo.value);
        tlbwehi(tlb_entry, tlbhi.value);
        isync();
        ssx_critical_section_exit(&ctx);
        effective_address += page_size;
        real_address += page_size;
    }
    if (map)
    {
        *map = local_map;
    }
    return 0;
}
int ppc405_mmu_map ( SsxAddress effective_address , SsxAddress real_address , size_t size , int tlbhi_flags , int tlblo_flags , Ppc405MmuMap * map ) { size_t log_page_size ; size_t page_size = 0 ; Ppc405Tlbhi tlbhi ; Ppc405Tlblo tlblo ; Ppc405MmuMap local_map = 0 ; int tlb_entry ; uint64_t bit ; SsxMachineContext ctx ; if ( SSX_ERROR_CHECK_API ) { uint64_t allocated ; SsxAddress this_effective_address ; int entry , overlap ; SSX_ERROR_IF ( ( effective_address % PPC405_PAGE_SIZE_MIN ) || ( real_address % PPC405_PAGE_SIZE_MIN ) || ( size % PPC405_PAGE_SIZE_MIN ) || ( ( effective_address + size - 1 ) < effective_address ) || ( tlbhi_flags & ~ TLBHI_LEGAL_FLAGS ) || ( tlblo_flags & ~ TLBLO_LEGAL_FLAGS ) , PPC405_MMU_INVALID_ARGUMENT ) ; if ( size != 0 ) { overlap = tlbsx ( effective_address , & entry ) ; allocated = ~ __ppc405_tlb_free ; while ( ! overlap && ( allocated != 0 ) ) { entry = cntlz64 ( allocated ) ; allocated &= ~ ( ( uint64_t ) 1 << ( 63 - entry ) ) ; tlbhi . value = tlbrehi ( entry ) ; if ( tlbhi . fields . v ) { this_effective_address = tlbhi . fields . epn << PPC405_LOG_PAGE_SIZE_MIN ; overlap |= ( this_effective_address >= effective_address ) && ( this_effective_address <= ( effective_address + size - 1 ) ) ; } } SSX_ERROR_IF ( overlap , PPC405_DUPLICATE_TLB_ENTRY ) ; } } while ( size != 0 ) { ssx_critical_section_enter ( SSX_CRITICAL , & ctx ) ; if ( SSX_ERROR_CHECK_API ) { SSX_ERROR_IF_CRITICAL ( __ppc405_tlb_free == 0 , PPC405_TOO_MANY_TLB_ENTRIES , & ctx ) ; } tlb_entry = cntlz64 ( __ppc405_tlb_free ) ; bit = 0x8000000000000000ull >> tlb_entry ; __ppc405_tlb_free &= ~ bit ; local_map |= bit ; ssx_critical_section_exit ( & ctx ) ; log_page_size = PPC405_LOG_PAGE_SIZE_MAX ; do { page_size = POW2_32 ( log_page_size ) ; if ( ( size >= page_size ) && ( ( effective_address & ( page_size - 1 ) ) == 0 ) && ( ( real_address & ( page_size - 1 ) ) == 0 ) ) { break ; } else { log_page_size -= 2 ; } } while ( 1 ) ; size -= page_size ; tlbhi . value = tlbhi_flags ; tlbhi . fields . epn = effective_address >> PPC405_LOG_PAGE_SIZE_MIN ; tlbhi . fields . size = ( log_page_size - PPC405_LOG_PAGE_SIZE_MIN ) / 2 ; tlbhi . fields . v = 1 ; tlblo . value = tlblo_flags ; tlblo . fields . rpn = real_address >> PPC405_LOG_PAGE_SIZE_MIN ; ssx_critical_section_enter ( SSX_CRITICAL , & ctx ) ; tlbwelo ( tlb_entry , tlblo . value ) ; tlbwehi ( tlb_entry , tlbhi . value ) ; isync ( ) ; ssx_critical_section_exit ( & ctx ) ; effective_address += page_size ; real_address += page_size ; } if ( map ) { * map = local_map ; } return 0 ; }
Define a memory region for MMU protection purposes
\param effective_address The effective (virtual) base address of the
region.
Define a memory region for MMU protection purposes \ param effective_address The effective ( virtual ) base address of the region .
// Check alignment, wrapping and legal flags. // The check for duplicate entries needs to be done iteratively since // we don't use a fixed page size.  Since this API will probably only // be called during initialization or from thread contexts, and since // the TLB size is small, this overhead is not considered too onerous. // For complete correctness this check would need to be done in its // entirity in an SSX_CRITICAL critical section. In order to reduce // SSX_CRITICAL interrupt latency we simply check against the TLB // entries that were allocated at the time of the API call.  This code // may not protect against multiple threads simultaneously creating // mappings that duplicate each other (a super-bug), but it should // protect against bugs in a single thread's updating of the TLB. // See if the requested effective address is already mapped in the // TLB // Iteratively check the other overlap condition, which is the // case that the effective address of any TLB entry is in the // range of the new request. // See if the first byte of this entry is inside the // requested effective address range.  NB: use actual // address ranges (addr + size - 1) to compute overlap to // avoid overflow. // NB: PPC405 page sizes go from 1K to 16M by factors of 4. This is a // 'greedy' algorithm that packs the region into the fewest number of // pages, by using the largest possible (aligned) page size for the // remaining memory area. // Create and install the TLB entries.  The installation is done in a // critical section to avoid any chance of another entity seeing an // inconsistent TLB.