#include <libkern/OSAtomic.h>
#include <IOKit/IOLocks.h>
#include <IOKit/IOPlatformExpert.h>
#include <IOKit/IODeviceTreeSupport.h>
#include <IOKit/IOMapper.h>
extern "C" {
extern ppnum_t pmap_find_phys(pmap_t pmap, addr64_t va);
}
#define U3DARTBASEMASK 0xfffff
#define U3DARTBASESHFT 12
#define U4DARTBASEMASK 0xffffff
#define U4DARTBASESHFT 0
#define U3IDARTTLB 0x00000400 // 1 << 10
#define U3DARTEN 0x00000200 // 1 << 9
#define U4DARTEN 0x80000000 // 1 << 31
#define U4IALLTLB 0x20000000 // 1 << 29
#define U3DARTSIZEMASK 0x1ff
#define U3DARTSIZESHFT 0
#define U4DARTSIZEMASK 0x1fff
#define U4DARTSIZESHFT 0
#define DARTCNTL 0x0000
#define DARTBASE 0x0010
#define DARTSIZE 0x0020
#define DARTEXCPU3 0x0010
#define DARTEXCPU4 0x0030
#define DARTTLBPAGES 4
#define frmDARTTLB(tlb) ((Uint32 *) \
( (vm_address_t) fRegBase + (tlb << DARTSETSHFT) + DARTTAG) )
#define setDARTBASE(b) (fU3 ? (((b) & U3DARTBASEMASK) << U3DARTBASESHFT) : (((b) & U4DARTBASEMASK) << U4DARTBASESHFT))
#define setDARTSIZE(p) (fU3 ? (((p) & U3DARTSIZEMASK) << U3DARTSIZESHFT) : (((p) & U4DARTSIZEMASK) << U4DARTSIZESHFT))
#define setDARTEN() (fU3 ? U3DARTEN : U4DARTEN)
#define setIALLTLB() (fU3 ? U3IDARTTLB : U4IALLTLB)
#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 kSysDARTSize (kSysMappedPages / kTransPerPage)
#define kRegDARTSize (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 AppleDART : public IOMapper
{
OSDeclareDefaultStructors(AppleDART);
#define fMappings ((volatile ppnum_t *) super::fTable)
private:
static vm_size_t gCacheLineSize;
UInt32 fFreeLists[kMaxNumZones];
IOLock *fTableLock;
IOSimpleLock *fInvalidateLock;
void *fDummyPage;
IOMemoryMap *fRegisterVMMap;
UInt8 *fRegBase;
volatile UInt32 *fDARTCNTLReg;
volatile UInt32 *fDARTBASEReg;
volatile UInt32 *fDARTSIZEReg;
volatile UInt32 *fDARTEXCPReg;
UInt32 fDARTCNTLVal; UInt32 fNumZones;
UInt32 fMapperRegionSize;
UInt32 fFreeSleepers;
ppnum_t fDummyPageNumber;
bool fU3;
void flushMappings(volatile void *vaddr, UInt32 numMappings);
void breakUp(unsigned start, unsigned end, unsigned freeInd);
void invalidateDART(ppnum_t pnum, IOItemCount size);
void tlbInvalidate(ppnum_t pnum, IOItemCount size);
virtual void free();
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 AppleDART::gCacheLineSize = 32;
OSDefineMetaClassAndStructors(AppleDART, IOMapper);
static inline void writeReg(volatile UInt32 *reg, UInt32 value)
{
*reg = value;
OSSynchronizeIO();
}
static inline UInt32 readReg(volatile UInt32 *reg)
{
return *reg;
}
typedef struct FreeDARTEntry {
unsigned int
fValid : 1,
fInUse : 1, : 5, fSize : 5,
: 2,
fNext :18; unsigned int
:14,
fPrev :18; } FreeDARTEntry;
#define kInvalidInUse 0x40000000
typedef struct ActiveDARTEntry {
unsigned int
fValid : 1, :12, fPPNum :19; };
#define kValidEntry 0x80000000
#define kActivePerFree (sizeof(freeDART[0]) / sizeof(ActiveDARTEntry))
bool AppleDART::initHardware(IOService *provider)
{
IOPlatformDevice *nub = OSDynamicCast(IOPlatformDevice, provider);
if (!nub)
return false;
UInt32 dartSizePages = 0;
fIsSystem = true;
if (fIsSystem && !IOMapper::gSystem) {
IOLog("DART disabled\n");
kprintf("DART disabled\n");
return false;
}
if (IODTMatchNubWithKeys(provider, "('u3-dart')"))
fU3 = true;
OSNumber *sizeNum = (OSNumber *) getProperty("AppleARTSize");
if (sizeNum)
dartSizePages = sizeNum->unsigned32BitValue();
fTableLock = IOLockAlloc();
fInvalidateLock = IOSimpleLockAlloc();
if (!fTableLock || !fInvalidateLock)
return false;
if (fIsSystem) {
int bootArg;
gCacheLineSize = 128;
if (PE_parse_boot_arg("artsize", &bootArg ))
dartSizePages = bootArg;
if (!dartSizePages)
dartSizePages = kSysDARTSize;
}
else if (!dartSizePages)
dartSizePages = kRegDARTSize;
if (!allocTable((dartSizePages + 1) * kMapperPage))
return false;
fRegisterVMMap = nub->mapDeviceMemoryWithIndex(0);
if (!fRegisterVMMap)
return false;
fRegBase = (UInt8 *) fRegisterVMMap->getVirtualAddress();
if (fU3) {
fDARTCNTLReg = (volatile UInt32 *)(fRegBase + DARTCNTL);
fDARTBASEReg = (volatile UInt32 *)(fRegBase + DARTCNTL);
fDARTSIZEReg = (volatile UInt32 *)(fRegBase + DARTCNTL);
fDARTEXCPReg = (volatile UInt32 *)(fRegBase + DARTEXCPU3);
} else {
fDARTCNTLReg = (volatile UInt32 *)(fRegBase + DARTCNTL);
fDARTBASEReg = (volatile UInt32 *)(fRegBase + DARTBASE);
fDARTSIZEReg = (volatile UInt32 *)(fRegBase + DARTSIZE);
fDARTEXCPReg = (volatile UInt32 *)(fRegBase + DARTEXCPU4);
}
UInt32 canMapPages = dartSizePages * kTransPerPage;
fMapperRegionSize = canMapPages;
for (fNumZones = 0; canMapPages; fNumZones++)
canMapPages >>= 1;
fNumZones -= 3;
fDARTCNTLVal = readReg(fDARTSIZEReg) | setDARTSIZE(dartSizePages);
writeReg(fDARTSIZEReg, fDARTCNTLVal);
fDARTCNTLVal = readReg(fDARTBASEReg) | setDARTBASE(fTablePhys);
writeReg(fDARTBASEReg, fDARTCNTLVal);
fDARTCNTLVal |= setDARTEN(); writeReg(fDARTCNTLReg, fDARTCNTLVal);
fDARTCNTLVal |= setIALLTLB(); invalidateDART(0, dartSizePages * 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 AppleDART::free()
{
if (fDummyPage) {
IOFreeAligned(fDummyPage, 0x1000);
fDummyPage = 0;
fDummyPageNumber = 0;
}
if (fRegisterVMMap) {
fRegBase = 0;
fRegisterVMMap->release();
fRegisterVMMap = 0;
}
#if 0
if (fTagVMMap) {
fRegTagBase = 0;
fTagVMMap->release();
fTagVMMap = 0;
}
#endif
if (fTableLock) {
IOLockFree(fTableLock);
fTableLock = 0;
}
if (fInvalidateLock) {
IOSimpleLockFree(fInvalidateLock);
fInvalidateLock = 0;
}
super::free();
}
void AppleDART::breakUp(unsigned start, unsigned end, unsigned freeInd)
{
unsigned int zoneSize;
FreeDARTEntry *freeDART = (FreeDARTEntry *) fTable;
do {
end--;
zoneSize = (kMinZoneSize/2 << end);
ppnum_t tail = freeInd + zoneSize;
fFreeLists[end] = tail;
freeDART[tail].fSize = end;
freeDART[tail].fNext = freeDART[tail].fPrev = 0;
} while (end != start);
freeDART[freeInd].fSize = end;
}
ppnum_t AppleDART::iovmAlloc(IOItemCount pages)
{
unsigned int zone, zoneSize, z, cnt;
ppnum_t next, ret = 0;
FreeDARTEntry *freeDART = (FreeDARTEntry *) 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)
break;
fFreeSleepers++;
IOLockSleep(fTableLock, fFreeLists, THREAD_UNINT);
fFreeSleepers--;
}
if (zone != z)
breakUp(zone, z, ret);
freeDART[ret].fInUse = true; next = freeDART[ret].fNext;
fFreeLists[z] = next;
if (next)
freeDART[next].fPrev = 0;
IOLockUnlock(fTableLock);
}
ret *= kActivePerFree;
ppnum_t pageEntry = fDummyPageNumber | kValidEntry;
for (cnt = 0; cnt < pages; cnt++) {
volatile ppnum_t *activeDART = &fMappings[ret + cnt];
*activeDART = pageEntry;
}
return ret;
}
void AppleDART::tlbInvalidate(ppnum_t pnum, IOItemCount size)
{
IOSimpleLockLock(fInvalidateLock);
writeReg(fDARTCNTLReg, fDARTCNTLVal); if (readReg(fDARTCNTLReg) & setIALLTLB()) {
AbsoluteTime now, expirationTime;
bool ok;
UInt32 loops = 0;
do {
clock_interval_to_deadline(100, kNanosecondScale, &expirationTime);
do {
ok = !(readReg(fDARTCNTLReg) & setIALLTLB());
if (ok)
break;
clock_get_uptime(&now);
} while (CMP_ABSOLUTETIME(&now, &expirationTime) < 0);
if (ok)
break;
writeReg(fDARTCNTLReg, fDARTCNTLVal & ~setIALLTLB());
writeReg(fDARTCNTLReg, fDARTCNTLVal);
} while (loops < 10);
if (!ok)
panic("AppleDART IDARTTLB");
}
IOSimpleLockUnlock(fInvalidateLock);
}
void AppleDART::invalidateDART(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;
}
flushMappings(&fMappings[pnum], size);
tlbInvalidate(pnum, size);
}
void AppleDART::iovmFree(ppnum_t addr, IOItemCount pages)
{
unsigned int zone, zoneSize, z;
FreeDARTEntry *freeDART = (FreeDARTEntry *) 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);
invalidateDART(addr, pages);
addr /= kActivePerFree;
for (z = zone; z < fNumZones; z++) {
ppnum_t pair = addr ^ (kMinZoneSize/2 << z); if (freeDART[pair].fValid || freeDART[pair].fInUse || (freeDART[pair].fSize != z))
break;
ppnum_t next = freeDART[pair].fNext;
ppnum_t prev = freeDART[pair].fPrev;
if (prev)
freeDART[prev].fNext = next;
else
fFreeLists[z] = next;
if (next)
freeDART[next].fPrev = prev;
if (addr > pair)
addr = pair;
}
freeDART[addr].fSize = z;
freeDART[addr].fNext = fFreeLists[z];
if (fFreeLists[z])
freeDART[fFreeLists[z]].fPrev = addr;
freeDART[addr].fPrev = 0;
fFreeLists[z] = addr;
if (fFreeSleepers)
IOLockWakeup(fTableLock, fFreeLists, false);
IOLockUnlock(fTableLock);
}
addr64_t AppleDART::mapAddr(IOPhysicalAddress addr)
{
if (addr >= ptoa_32(fMapperRegionSize))
{
return (addr64_t) addr; }
else
{
ppnum_t *activeDART = (ppnum_t *) fTable;
UInt offset = addr & PAGE_MASK;
ppnum_t mappedPage = activeDART[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 AppleDART::iovmInsert(ppnum_t addr, IOItemCount offset, ppnum_t page)
{
addr += offset;
volatile ppnum_t *activeDART = &fMappings[addr];
*activeDART = page | kValidEntry;
flushMappings(activeDART, 1);
tlbInvalidate(addr, 1);
}
void AppleDART::iovmInsert(ppnum_t addr, IOItemCount offset,
ppnum_t *pageList, IOItemCount pageCount)
{
addr += offset;
IOItemCount i;
volatile ppnum_t *activeDART = &fMappings[addr];
for (i = 0; i < pageCount; i++)
activeDART[i] = pageList[i] | kValidEntry;
flushMappings(activeDART, pageCount);
tlbInvalidate(addr, pageCount);
}
void AppleDART::iovmInsert(ppnum_t addr, IOItemCount offset,
upl_page_info_t *pageList, IOItemCount pageCount)
{
addr += offset;
IOItemCount i;
volatile ppnum_t *activeDART = &fMappings[addr];
for (i = 0; i < pageCount; i++)
activeDART[i] = pageList[i].phys_addr | kValidEntry;
flushMappings(activeDART, pageCount);
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 AppleDART::flushMappings(volatile void *vaddr, UInt32 numMappings)
{
SInt32 csize = gCacheLineSize;
vm_address_t arithAddr = (vm_address_t) vaddr;
vm_address_t vaddr_cache_aligned = arithAddr & ~(csize-1);
UInt32 len = (numMappings + 1) * sizeof(fMappings[0]); 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();
}