#include <libkern/OSAtomic.h>
#include <IOKit/IOLocks.h>
#include <IOKit/IOPlatformExpert.h>
#include <IOKit/IOMapper.h>
extern "C" {
extern ppnum_t pmap_find_phys(pmap_t pmap, addr64_t va);
}
#define DARTBASEMASK 0xfffff
#define DARTBASESHFT 12
#define IDARTTLB 0x400 // 1 << 10
#define DARTEN 0x200 // 1 << 9
#define DARTSIZEMASK 0x1ff
#define DARTSIZESHFT 0
#define DARTLPNMASK 0x1fff
#define DARTLPNSHFT 19
#define DARTVxMASK 0xf
#define DARTVxSHFT 14
#define DARTSETMASK 0x1f
#define DARTSETSHFT 14
#define DARTWAYSETMASK 0x7f
#define DARTWAYSETSHFT 14
#define DARTCNTL 0x0000
#define DARTEXCP 0x0010
#define DARTTAG 0x1000
#define DARTTAGSZ 0x0740
#define DARTDATA 0x5000
#define DARTDATASZ 0x1ff0
#define DARTTLBPAGES 4
#define frmDARTTLB(tlb) ((Uint32 *) \
( (vm_address_t) fRegBase + (tlb << DARTSETSHFT) + DARTTAG) )
#define setDARTSIZE(p) (((p) & DARTSIZEMASK) << DARTSIZESHFT)
#define setDARTBASE(b) ( ptoa_32(b) & (DARTBASEMASK << DARTBASESHFT) )
#define invalidDARTLPN(a) ( (a) & (DARTLPNMASK << DARTLPNSHFT) )
#define kMapperPage (4 * 1024)
#define kTransPerPage (kMapperPage / sizeof(ppnum_t))
#define kSysTableMapped (2UL * 1024 * 1024 * 1024)
#define kRegTableMapped (32UL * 1024 * 1024)
#define kSysMappedPages (kSysTableMapped / kMapperPage)
#define kRegMappedPages (kRegTableMapped / kMapperPage)
#define kSysARTSize (kSysMappedPages / kTransPerPage)
#define kRegARTSize (kRegMappedPages / kTransPerPage)
#define kMinZoneSize 4 // Minimum Zone size in pages
#define kMaxNumZones (31 - 14) // 31 bit mapped in 16K super pages
#define super IOMapper
class AppleU3ART : public IOMapper
{
OSDeclareDefaultStructors(AppleU3ART);
#define fMappings ((volatile ppnum_t *) super::fTable)
protected:
static vm_size_t gCacheLineSize;
void * fDummyPage;
ppnum_t fDummyPageNumber;
UInt8 * fRegBase;
IOMemoryMap * fRegisterVMMap;
volatile UInt32 * fRegTagBase;
IOMemoryMap * fTagVMMap;
#define fDARTCNTLReg ((volatile UInt32 *) (fRegBase + DARTCNTL))
#define fDARTEXCPReg ((volatile UInt32 *) (fRegBase + DARTEXCP))
#define fDARTTAGReg ((volatile UInt32 *) (fRegBase + DARTTAG))
#define fDARTDATAPReg ((volatile UInt32 *) (fRegBase + DARTDATA))
UInt32 fDARTCNTLVal; UInt32 fFreeLists[kMaxNumZones];
UInt32 fNumZones;
UInt32 fMapperRegionSize;
IOLock * fTableLock;
IOSimpleLock * fInvalidateLock;
virtual void free();
void flushMemory(volatile void *vaddr, UInt32 len);
void breakUp(unsigned start, unsigned end, unsigned freeInd);
void invalidateArt(ppnum_t pnum, IOItemCount size);
void tlbInvalidate(ppnum_t pnum, IOItemCount size);
virtual bool initHardware(IOService *provider);
virtual ppnum_t iovmAlloc(IOItemCount pages);
virtual void iovmFree(ppnum_t addr, IOItemCount pages);
virtual void iovmInsert(ppnum_t addr, IOItemCount offset, ppnum_t page);
virtual void iovmInsert(ppnum_t addr, IOItemCount offset,
ppnum_t *pageList, IOItemCount pageCount);
virtual void iovmInsert(ppnum_t addr, IOItemCount offset,
upl_page_info_t *pageList, IOItemCount pageCount);
virtual addr64_t mapAddr(IOPhysicalAddress addr);
};
vm_size_t AppleU3ART::gCacheLineSize = 32;
OSDefineMetaClassAndStructors(AppleU3ART, IOMapper);
typedef struct FreeArtEntry {
unsigned int
fValid : 1,
fInUse : 1, : 5, fSize : 5,
: 2,
fNext :18; unsigned int
:14,
fPrev :18; } FreeArtEntry;
#define kInvalidInUse 0x40000000
typedef struct ActiveArtEntry {
unsigned int
fValid : 1, :12, fPPNum :19; };
#define kValidEntry 0x80000000
#define kActivePerFree (sizeof(freeArt[0]) / sizeof(ActiveArtEntry))
bool AppleU3ART::initHardware(IOService *provider)
{
IOPlatformDevice *nub = OSDynamicCast(IOPlatformDevice, provider);
if (!nub)
return false;
UInt32 artSizePages = 0;
fIsSystem = true;
if (fIsSystem && !IOMapper::gSystem)
{
IOLog("DART disabled\n");
kprintf("DART disabled\n");
return false;
}
OSNumber *sizeNum = (OSNumber *) getProperty("AppleARTSize");
if (sizeNum)
artSizePages = sizeNum->unsigned32BitValue();
fTableLock = IOLockAlloc();
fInvalidateLock = IOSimpleLockAlloc();
if (!fTableLock || !fInvalidateLock)
return false;
if (fIsSystem) {
int bootArg;
gCacheLineSize = 128;
if (PE_parse_boot_arg( "artsize", &bootArg ))
artSizePages = bootArg;
if (!artSizePages)
artSizePages = kSysARTSize;
}
else if (!artSizePages)
artSizePages = kRegARTSize;
if (!allocTable(artSizePages * kMapperPage))
return false;
fRegisterVMMap = nub->mapDeviceMemoryWithIndex(0);
if (!fRegisterVMMap)
return false;
fRegBase = (UInt8 *) fRegisterVMMap->getVirtualAddress();
IODeviceMemory * md = IODeviceMemory::withRange( 0xf8034000, 0x800 );
if (!md)
return false;
fTagVMMap = md->map();
md->release();
if (!fTagVMMap)
return false;
fRegTagBase = (volatile UInt32 *) fTagVMMap->getVirtualAddress();
fDARTCNTLVal = setDARTBASE(fTablePhys) | DARTEN | setDARTSIZE(artSizePages);
UInt32 canMapPages = artSizePages * kTransPerPage;
fMapperRegionSize = canMapPages;
for (fNumZones = 0; canMapPages; fNumZones++)
canMapPages >>= 1;
fNumZones -= 3;
*fDARTCNTLReg = fDARTCNTLVal;
OSSynchronizeIO();
fDARTCNTLVal |= IDARTTLB; invalidateArt(0, artSizePages * kTransPerPage);
breakUp(0, fNumZones, 0);
*(ppnum_t *) fTable = kInvalidInUse;
fDummyPage = IOMallocAligned(0x1000, 0x1000);
fDummyPageNumber = pmap_find_phys(kernel_pmap,
(addr64_t) (uintptr_t) fDummyPage);
IOLog("DART enabled\n");
kprintf("DART enabled\n");
return true;
}
void AppleU3ART::free()
{
if (fDummyPage) {
IOFreeAligned(fDummyPage, 0x1000);
fDummyPage = 0;
fDummyPageNumber = 0;
}
if (fRegisterVMMap) {
fRegBase = 0;
fRegisterVMMap->release();
fRegisterVMMap = 0;
}
if (fTagVMMap) {
fRegTagBase = 0;
fTagVMMap->release();
fTagVMMap = 0;
}
if (fTableLock) {
IOLockFree(fTableLock);
fTableLock = 0;
}
if (fInvalidateLock) {
IOSimpleLockFree(fInvalidateLock);
fInvalidateLock = 0;
}
super::free();
}
void AppleU3ART::breakUp(unsigned start, unsigned end, unsigned freeInd)
{
unsigned int zoneSize;
FreeArtEntry *freeArt = (FreeArtEntry *) fTable;
do {
end--;
zoneSize = (kMinZoneSize/2 << end);
ppnum_t tail = freeInd + zoneSize;
fFreeLists[end] = tail;
freeArt[tail].fSize = end;
freeArt[tail].fNext = freeArt[tail].fPrev = 0;
} while (end != start);
freeArt[freeInd].fSize = end;
}
ppnum_t AppleU3ART::iovmAlloc(IOItemCount pages)
{
unsigned int zone, zoneSize, z, cnt;
ppnum_t ret, next;
FreeArtEntry *freeArt = (FreeArtEntry *) fTable;
pages += 1;
if (pages < kMinZoneSize)
pages = kMinZoneSize;
if (pages >= fMapperRegionSize/2)
{
panic("iovmAlloc");
return 0;
}
for (zone = 0, zoneSize = kMinZoneSize; pages > zoneSize; zone++)
zoneSize <<= 1;
{
IOLockLock(fTableLock);
for (;;) {
for (z = zone; z < fNumZones; z++) {
if ( (ret = fFreeLists[z]) )
break;
}
if (!ret)
panic("AppleU3ART: Out of IOVM Space");
break;
}
if (zone != z)
breakUp(zone, z, ret);
freeArt[ret].fInUse = true; next = freeArt[ret].fNext;
fFreeLists[z] = next;
if (next)
freeArt[next].fPrev = 0;
IOLockUnlock(fTableLock);
}
ret *= kActivePerFree;
for (cnt = 0; cnt < pages; cnt++) {
iovmInsert(ret, cnt, fDummyPageNumber);
}
return ret;
}
void AppleU3ART::tlbInvalidate(ppnum_t pnum, IOItemCount size)
{
IOSimpleLockLock(fInvalidateLock);
*fDARTCNTLReg = fDARTCNTLVal;
OSSynchronizeIO();
if (*fDARTCNTLReg & IDARTTLB)
{
AbsoluteTime now, expirationTime;
bool ok;
UInt32 loops = 0;
do
{
clock_interval_to_deadline(100, kNanosecondScale, &expirationTime);
do
{
ok = (0 == (*fDARTCNTLReg & IDARTTLB));
if (ok)
break;
clock_get_uptime(&now);
}
while (CMP_ABSOLUTETIME(&now, &expirationTime) < 0);
if (ok)
break;
#if 0
if (loops++ > 0)
{
UInt32 excptReg = *fDARTEXCPReg;
IOItemCount i;
static UInt32 tags[128];
for (i = 0; i < 128; i++)
tags[i] = fRegTagBase[i << 2];
kprintf("IDARTTLB didn't (%d) [%x:%x] - ", loops++, pnum, size);
kprintf("DARTEXCP: %08lx", excptReg);
for (i = 0; i < 128; i++)
{
if (0 == (i & 7))
kprintf("\n%02x: ", i);
kprintf("%04lx:%01lx ", (tags[i] >> 19), (tags[i] >> 14) & 15);
}
kprintf("\n");
}
#endif
*fDARTCNTLReg = (fDARTCNTLVal & ~IDARTTLB);
OSSynchronizeIO();
*fDARTCNTLReg = fDARTCNTLVal;
OSSynchronizeIO();
}
while (loops < 10);
if (!ok)
panic("AppleU3ART IDARTTLB");
}
IOSimpleLockUnlock(fInvalidateLock);
}
void AppleU3ART::invalidateArt(ppnum_t pnum, IOItemCount size)
{
if (size >= (2 * gCacheLineSize))
bzero((void *) &fMappings[pnum], size * sizeof(fMappings[0]));
else
{ for (vm_size_t i = 0; i < size; i++)
fMappings[pnum+i] = 0;
}
flushMemory(&fMappings[pnum], size * sizeof(fMappings[0]));
tlbInvalidate(pnum, size);
}
void AppleU3ART::iovmFree(ppnum_t addr, IOItemCount pages)
{
unsigned int zone, zoneSize, z;
FreeArtEntry *freeArt = (FreeArtEntry *) fTable;
pages += 1;
if (pages < kMinZoneSize)
pages = kMinZoneSize;
if (pages >= fMapperRegionSize/2)
return;
for (zone = 0, zoneSize = kMinZoneSize; pages > zoneSize; zone++)
zoneSize <<= 1;
IOLockLock(fTableLock);
invalidateArt(addr, pages);
addr /= kActivePerFree;
for (z = zone; z < fNumZones; z++) {
ppnum_t pair = addr ^ (kMinZoneSize/2 << z); if (freeArt[pair].fValid || freeArt[pair].fInUse || (freeArt[pair].fSize != z))
break;
ppnum_t next = freeArt[pair].fNext;
ppnum_t prev = freeArt[pair].fPrev;
if (prev)
freeArt[prev].fNext = next;
else
fFreeLists[z] = next;
if (next)
freeArt[next].fPrev = prev;
if (addr > pair)
addr = pair;
}
freeArt[addr].fSize = z;
freeArt[addr].fNext = fFreeLists[z];
if (fFreeLists[z])
freeArt[fFreeLists[z]].fPrev = addr;
freeArt[addr].fPrev = 0;
fFreeLists[z] = addr;
IOLockUnlock(fTableLock);
}
addr64_t AppleU3ART::mapAddr(IOPhysicalAddress addr)
{
if (addr >= ptoa_32(fMapperRegionSize))
{
return (addr64_t) addr; }
else
{
ppnum_t *activeArt = (ppnum_t *) fTable;
UInt offset = addr & PAGE_MASK;
ppnum_t mappedPage = activeArt[atop_32(addr)];
if (mappedPage & kValidEntry) {
mappedPage ^= kValidEntry; return ptoa_64(mappedPage) | offset;
}
panic("%s::mapAddr(0x%08lx) not mapped for I/O\n", getName(), addr);
return 0;
}
}
void AppleU3ART::iovmInsert(ppnum_t addr, IOItemCount offset, ppnum_t page)
{
addr += offset;
volatile ppnum_t *activeArt = &fMappings[addr];
*activeArt = page | kValidEntry;
flushMemory(activeArt, sizeof(fMappings[0]));
tlbInvalidate(addr, 1);
}
void AppleU3ART::iovmInsert(ppnum_t addr, IOItemCount offset,
ppnum_t *pageList, IOItemCount pageCount)
{
addr += offset;
IOItemCount i;
volatile ppnum_t *activeArt = &fMappings[addr];
for (i = 0; i < pageCount; i++)
activeArt[i] = pageList[i] | kValidEntry;
flushMemory(activeArt, pageCount * sizeof(fMappings[0]));
tlbInvalidate(addr, pageCount);
}
void AppleU3ART::iovmInsert(ppnum_t addr, IOItemCount offset,
upl_page_info_t *pageList, IOItemCount pageCount)
{
addr += offset;
IOItemCount i;
volatile ppnum_t *activeArt = &fMappings[addr];
for (i = 0; i < pageCount; i++)
activeArt[i] = pageList[i].phys_addr | kValidEntry;
flushMemory(activeArt, pageCount * sizeof(fMappings[0]));
tlbInvalidate(addr, pageCount);
}
static inline void __sync(void) { __asm__ ("sync"); }
static inline void __isync(void) { __asm__ ("isync"); }
static inline void __dcbf(vm_address_t base, unsigned long offset)
{
__asm__ ("dcbf %0, %1"
:
: "r" (base), "r" (offset)
: "r0");
}
static inline void __dcbst(vm_address_t base, unsigned long offset)
{
__asm__ ("dcbst %0, %1"
:
: "r" (base), "r" (offset)
: "r0");
}
static inline unsigned long __lwzx(vm_address_t base, unsigned long offset)
{
unsigned long result;
__asm__ volatile("lwzx %0, %1, %2"
: "=r" (result)
: "r" (base), "r" (offset)
: "r0");
return result;
}
void AppleU3ART::flushMemory(volatile void *vaddr, UInt32 len)
{
SInt32 csize = gCacheLineSize;
vm_address_t arithAddr = (vm_address_t) vaddr;
vm_address_t vaddr_cache_aligned = arithAddr & ~(csize-1);
SInt c, end = ((SInt)((arithAddr & (csize-1)) + len)) - csize;
for (c = 0; c < end; c += csize)
__dcbf(vaddr_cache_aligned, c);
__sync();
__isync();
__dcbf(vaddr_cache_aligned, c);
__sync();
__isync();
__lwzx(vaddr_cache_aligned, c);
__isync();
}