2 * Copyright (c) 2006 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_OSREFERENCE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the
10 * License may not be used to create, or enable the creation or
11 * redistribution of, unlawful or unlicensed copies of an Apple operating
12 * system, or to circumvent, violate, or enable the circumvention or
13 * violation of, any terms of an Apple operating system software license
16 * Please obtain a copy of the License at
17 * http://www.opensource.apple.com/apsl/ and read it before using this
20 * The Original Code and all software distributed under the License are
21 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
22 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
23 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
24 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
25 * Please see the License for the specific language governing rights and
26 * limitations under the License.
28 * @APPLE_LICENSE_OSREFERENCE_HEADER_END@
30 // 45678901234567890123456789012345678901234567890123456789012345678901234567890
32 #include "IOCopyMapper.h"
33 #include <sys/sysctl.h>
36 #define DEBG(fmt, args...) { kprintf(fmt, ## args); }
38 #define DEBG(fmt, args...) {}
42 extern ppnum_t
pmap_find_phys(pmap_t pmap
, addr64_t va
);
43 extern void ml_get_bouncepool_info(
44 vm_offset_t
*phys_addr
,
46 extern unsigned int vm_lopage_max_count
;
47 extern unsigned int vm_himemory_mode
;
50 #define super IOMapper
52 OSDefineMetaClassAndStructors(IOCopyMapper
, IOMapper
);
54 // Remember no value can be bigger than 31 bits as the sign bit indicates
55 // that this entry is valid to the hardware and that would be bad if it wasn't
56 typedef struct FreeDARTEntry
{
59 /* bool */ fValid
: 1,
60 /* bool */ fInUse
: 1, // Allocated but not inserted yet
61 /* bool */ : 5, // Align size on nibble boundary for debugging
64 /* uint */ fNext
:18; // offset of FreeDARTEntry's
66 #elif __LITTLE_ENDIAN__
68 /* uint */ fNext
:18, // offset of FreeDARTEntry's
71 /* bool */ : 5, // Align size on nibble boundary for debugging
72 /* bool */ fInUse
: 1, // Allocated but not inserted yet
73 /* bool */ fValid
: 1;
78 /* uint */ fPrev
:18; // offset of FreeDARTEntry's
80 #elif __LITTLE_ENDIAN__
82 /* uint */ fPrev
:18, // offset of FreeDARTEntry's
87 typedef struct ActiveDARTEntry
{
90 /* bool */ fValid
: 1, // Must be set to one if valid
91 /* uint */ fPPNum
:31; // ppnum_t page of translation
92 #define ACTIVEDARTENTRY(page) { true, page }
94 #elif __LITTLE_ENDIAN__
96 /* uint */ fPPNum
:31, // ppnum_t page of translation
97 /* bool */ fValid
: 1; // Must be set to one if valid
98 #define ACTIVEDARTENTRY(page) { page, true }
103 #define kActivePerFree (sizeof(freeDART[0]) / sizeof(ActiveDARTEntry))
105 static SYSCTL_UINT(_kern
, OID_AUTO
, copyregionmax
,
106 CTLFLAG_RD
| CTLFLAG_NOAUTO
| CTLFLAG_KERN
,
109 static SYSCTL_UINT(_kern
, OID_AUTO
, lowpagemax
,
110 CTLFLAG_RD
| CTLFLAG_NOAUTO
| CTLFLAG_KERN
,
111 &vm_lopage_max_count
, 0, "");
113 static SYSCTL_UINT(_kern
, OID_AUTO
, himemorymode
,
114 CTLFLAG_RD
| CTLFLAG_NOAUTO
| CTLFLAG_KERN
,
115 &vm_himemory_mode
, 0, "");
117 bool IOCopyMapper::initHardware(IOService
* provider
)
119 UInt32 dartSizePages
= 0;
121 vm_offset_t phys_addr
;
123 ml_get_bouncepool_info(&phys_addr
, &size
);
128 fBufferPage
= atop_32(phys_addr
);
129 dartSizePages
= (atop_32(size
) + kTransPerPage
- 1) / kTransPerPage
;
131 fTableLock
= IOLockAlloc();
136 if (!allocTable(dartSizePages
* kMapperPage
))
139 UInt32 canMapPages
= dartSizePages
* kTransPerPage
;
140 fMapperRegionSize
= canMapPages
;
141 for (fNumZones
= 0; canMapPages
; fNumZones
++)
143 fNumZones
-= 3; // correct for overshoot and minumum 16K pages allocation
145 invalidateDART(0, fMapperRegionSize
);
147 breakUp(0, fNumZones
, 0);
148 ((FreeDARTEntry
*) fTable
)->fInUse
= true;
150 fMapperRegionUsed
= kMinZoneSize
;
151 fMapperRegionMaxUsed
= fMapperRegionUsed
;
153 sysctl__kern_copyregionmax
.oid_arg1
= &fMapperRegionMaxUsed
;
155 sysctl_register_oid(&sysctl__kern_copyregionmax
);
156 sysctl_register_oid(&sysctl__kern_lowpagemax
);
157 sysctl_register_oid(&sysctl__kern_himemorymode
);
159 fDummyPage
= IOMallocAligned(0x1000, 0x1000);
161 pmap_find_phys(kernel_pmap
, (addr64_t
) (uintptr_t) fDummyPage
);
166 void IOCopyMapper::free()
169 IOFreeAligned(fDummyPage
, 0x1000);
171 fDummyPageNumber
= 0;
175 IOLockFree(fTableLock
);
182 // Must be called while locked
183 void IOCopyMapper::breakUp(unsigned startIndex
, unsigned endIndex
, unsigned freeInd
)
185 unsigned int zoneSize
;
186 FreeDARTEntry
*freeDART
= (FreeDARTEntry
*) fTable
;
189 // Need to break up bigger blocks of memory till we get one in our
192 zoneSize
= (kMinZoneSize
/2 << endIndex
);
193 ppnum_t tail
= freeInd
+ zoneSize
;
195 DEBG("breakup z %d start %x tail %x\n", endIndex
, freeInd
, tail
);
197 // By definition free lists must be empty
198 fFreeLists
[endIndex
] = tail
;
199 freeDART
[tail
].fSize
= endIndex
;
200 freeDART
[tail
].fNext
= freeDART
[tail
].fPrev
= 0;
201 } while (endIndex
!= startIndex
);
202 freeDART
[freeInd
].fSize
= endIndex
;
205 // Zero is never a valid page to return
206 ppnum_t
IOCopyMapper::iovmAlloc(IOItemCount pages
)
208 unsigned int zone
, zoneSize
, z
, cnt
;
209 ppnum_t next
, ret
= 0;
210 FreeDARTEntry
*freeDART
= (FreeDARTEntry
*) fTable
;
212 // Can't alloc anything of less than minumum
213 if (pages
< kMinZoneSize
)
214 pages
= kMinZoneSize
;
216 // Can't alloc anything bigger than 1/2 table
217 if (pages
>= fMapperRegionSize
/2)
219 panic("iovmAlloc 0x%x", pages
);
223 // Find the appropriate zone for this allocation
224 for (zone
= 0, zoneSize
= kMinZoneSize
; pages
> zoneSize
; zone
++)
228 IOLockLock(fTableLock
);
231 for (z
= zone
; z
< fNumZones
; z
++) {
232 if ( (ret
= fFreeLists
[z
]) )
239 IOLockSleep(fTableLock
, fFreeLists
, THREAD_UNINT
);
243 // If we didn't find a entry in our size then break up the free block
247 DEBG("breakup %d, %d, 0x%x\n", zone
, z
, ret
);
248 breakUp(zone
, z
, ret
);
251 freeDART
[ret
].fInUse
= true; // Mark entry as In Use
252 next
= freeDART
[ret
].fNext
;
253 DEBG("va: 0x%x, %d, ret %x next %x\n", (ret
* kActivePerFree
) + fBufferPage
, pages
, ret
, next
);
255 fFreeLists
[z
] = next
;
257 freeDART
[next
].fPrev
= 0;
259 // ret is free list offset not page offset;
260 ret
*= kActivePerFree
;
262 ActiveDARTEntry pageEntry
= ACTIVEDARTENTRY(fDummyPageNumber
);
263 for (cnt
= 0; cnt
< pages
; cnt
++) {
264 ActiveDARTEntry
*activeDART
= &fMappings
[ret
+ cnt
];
265 *activeDART
= pageEntry
;
268 fMapperRegionUsed
+= pages
;
269 if (fMapperRegionUsed
> fMapperRegionMaxUsed
)
270 fMapperRegionMaxUsed
= fMapperRegionUsed
;
272 IOLockUnlock(fTableLock
);
282 void IOCopyMapper::invalidateDART(ppnum_t pnum
, IOItemCount size
)
284 bzero((void *) &fMappings
[pnum
], size
* sizeof(fMappings
[0]));
287 void IOCopyMapper::iovmFree(ppnum_t addr
, IOItemCount pages
)
289 unsigned int zone
, zoneSize
, z
;
290 FreeDARTEntry
*freeDART
= (FreeDARTEntry
*) fTable
;
292 if (addr
< fBufferPage
)
293 IOPanic("addr < fBufferPage");
296 // Can't free anything of less than minumum
297 if (pages
< kMinZoneSize
)
298 pages
= kMinZoneSize
;
300 // Can't free anything bigger than 1/2 table
301 if (pages
>= fMapperRegionSize
/2)
304 // Find the appropriate zone for this allocation
305 for (zone
= 0, zoneSize
= kMinZoneSize
; pages
> zoneSize
; zone
++)
308 // Grab lock that protects the dart
309 IOLockLock(fTableLock
);
311 invalidateDART(addr
, pages
);
313 addr
/= kActivePerFree
;
315 // We are freeing a block, check to see if pairs are available for
316 // coalescing. We will walk up the entire chain if we can.
317 for (z
= zone
; z
< fNumZones
; z
++) {
318 ppnum_t pair
= addr
^ (kMinZoneSize
/2 << z
); // Find pair address
319 if (freeDART
[pair
].fValid
|| freeDART
[pair
].fInUse
|| (freeDART
[pair
].fSize
!= z
))
322 // The paired alloc entry is free if we are here
323 ppnum_t next
= freeDART
[pair
].fNext
;
324 ppnum_t prev
= freeDART
[pair
].fPrev
;
326 // Remove the pair from its freeList
328 freeDART
[prev
].fNext
= next
;
330 fFreeLists
[z
] = next
;
333 freeDART
[next
].fPrev
= prev
;
335 // Sort the addr and the pair
340 DEBG("vf: 0x%x, %d, z %d, head %x, new %x\n", addr
* kActivePerFree
+ fBufferPage
, pages
, z
, fFreeLists
[z
], addr
);
342 // Add the allocation entry into it's free list and re-init it
343 freeDART
[addr
].fSize
= z
;
344 freeDART
[addr
].fNext
= fFreeLists
[z
];
346 freeDART
[fFreeLists
[z
]].fPrev
= addr
;
347 freeDART
[addr
].fPrev
= 0;
348 fFreeLists
[z
] = addr
;
350 fMapperRegionUsed
-= pages
;
353 IOLockWakeup(fTableLock
, fFreeLists
, /* oneThread */ false);
355 IOLockUnlock(fTableLock
);
358 addr64_t
IOCopyMapper::mapAddr(IOPhysicalAddress addr
)
360 if (addr
< ptoa_32(fBufferPage
))
362 return (addr64_t
) addr
; // Not mapped by us anyway
365 addr
-= ptoa_32(fBufferPage
);
366 if (addr
>= ptoa_32(fMapperRegionSize
))
368 return (addr64_t
) addr
; // Not mapped by us anyway
372 ActiveDARTEntry
*activeDART
= (ActiveDARTEntry
*) fTable
;
373 UInt offset
= addr
& PAGE_MASK
;
375 ActiveDARTEntry mappedPage
= activeDART
[atop_32(addr
)];
376 if (mappedPage
.fValid
)
378 return (ptoa_64(mappedPage
.fPPNum
) | offset
);
381 panic("%s::mapAddr(0x%08lx) not mapped for I/O\n", getName(), addr
);
386 void IOCopyMapper::iovmInsert(ppnum_t addr
, IOItemCount offset
, ppnum_t page
)
389 addr
+= offset
; // Add the offset page to the base address
391 ActiveDARTEntry
*activeDART
= &fMappings
[addr
];
392 ActiveDARTEntry entry
= ACTIVEDARTENTRY(page
);
396 void IOCopyMapper::iovmInsert(ppnum_t addr
, IOItemCount offset
,
397 ppnum_t
*pageList
, IOItemCount pageCount
)
400 addr
+= offset
; // Add the offset page to the base address
403 ActiveDARTEntry
*activeDART
= &fMappings
[addr
];
405 for (i
= 0; i
< pageCount
; i
++)
407 ActiveDARTEntry entry
= ACTIVEDARTENTRY(pageList
[i
]);
408 activeDART
[i
] = entry
;
412 void IOCopyMapper::iovmInsert(ppnum_t addr
, IOItemCount offset
,
413 upl_page_info_t
*pageList
, IOItemCount pageCount
)
416 addr
+= offset
; // Add the offset page to the base address
419 ActiveDARTEntry
*activeDART
= &fMappings
[addr
];
421 for (i
= 0; i
< pageCount
; i
++)
423 ActiveDARTEntry entry
= ACTIVEDARTENTRY(pageList
[i
].phys_addr
);
424 activeDART
[i
] = entry
;