1/*
2 * Copyright (c) 1998-2004 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
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 License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28#include <IOKit/IOLib.h>
29#include <IOKit/IOMapper.h>
30#include <IOKit/IODMACommand.h>
31#include <libkern/c++/OSData.h>
32#include <libkern/OSDebug.h>
33
34__BEGIN_DECLS
35extern ppnum_t pmap_find_phys(pmap_t pmap, addr64_t va);
36__END_DECLS
37
38#define super IOService
39OSDefineMetaClassAndAbstractStructors(IOMapper, IOService);
40
41OSMetaClassDefineReservedUsed(IOMapper, 0);
42OSMetaClassDefineReservedUsed(IOMapper, 1);
43OSMetaClassDefineReservedUsed(IOMapper, 2);
44OSMetaClassDefineReservedUsed(IOMapper, 3);
45OSMetaClassDefineReservedUnused(IOMapper, 4);
46OSMetaClassDefineReservedUnused(IOMapper, 5);
47OSMetaClassDefineReservedUnused(IOMapper, 6);
48OSMetaClassDefineReservedUnused(IOMapper, 7);
49OSMetaClassDefineReservedUnused(IOMapper, 8);
50OSMetaClassDefineReservedUnused(IOMapper, 9);
51OSMetaClassDefineReservedUnused(IOMapper, 10);
52OSMetaClassDefineReservedUnused(IOMapper, 11);
53OSMetaClassDefineReservedUnused(IOMapper, 12);
54OSMetaClassDefineReservedUnused(IOMapper, 13);
55OSMetaClassDefineReservedUnused(IOMapper, 14);
56OSMetaClassDefineReservedUnused(IOMapper, 15);
57
58IOMapper * IOMapper::gSystem = (IOMapper *) IOMapper::kUnknown;
59
60class IOMapperLock {
61    IOLock *fWaitLock;
62public:
63    IOMapperLock() { fWaitLock = IOLockAlloc(); };
64    ~IOMapperLock() { IOLockFree(fWaitLock); };
65
66    void lock()   { IOLockLock(fWaitLock); };
67    void unlock() { IOLockUnlock(fWaitLock); };
68    void sleep(void *event)  { IOLockSleep(fWaitLock, event, THREAD_UNINT); };
69    void wakeup(void *event) { IOLockWakeup(fWaitLock, event, false); };
70};
71
72static IOMapperLock sMapperLock;
73
74bool IOMapper::start(IOService *provider)
75{
76    OSObject * obj;
77    if (!super::start(provider))
78        return false;
79
80    if (!initHardware(provider))
81        return false;
82
83    if (fIsSystem) {
84        sMapperLock.lock();
85        IOMapper::gSystem = this;
86        sMapperLock.wakeup(&IOMapper::gSystem);
87        sMapperLock.unlock();
88    }
89
90    if (provider)
91    {
92    	obj = provider->getProperty("iommu-id");
93	if (!obj)
94	    obj = provider->getProperty("AAPL,phandle");
95	if (obj)
96	    setProperty(gIOMapperIDKey, obj);
97    }
98    return true;
99}
100
101bool IOMapper::allocTable(IOByteCount size)
102{
103    assert(!fTable);
104
105    fTableSize = size;
106    fTableHandle = NewARTTable(size, &fTable, &fTablePhys);
107    return fTableHandle != 0;
108}
109
110void IOMapper::free()
111{
112    if (fTableHandle) {
113        FreeARTTable(fTableHandle, fTableSize);
114        fTableHandle = 0;
115    }
116
117    super::free();
118}
119
120void IOMapper::setMapperRequired(bool hasMapper)
121{
122    if (hasMapper)
123        IOMapper::gSystem = (IOMapper *) kHasMapper;
124    else {
125        sMapperLock.lock();
126        IOMapper::gSystem = (IOMapper *) kNoMapper;
127        sMapperLock.unlock();
128        sMapperLock.wakeup(&IOMapper::gSystem);
129    }
130}
131
132void IOMapper::waitForSystemMapper()
133{
134    sMapperLock.lock();
135    while ((uintptr_t) IOMapper::gSystem & kWaitMask)
136    {
137		OSReportWithBacktrace("waitForSystemMapper");
138        sMapperLock.sleep(&IOMapper::gSystem);
139    }
140    sMapperLock.unlock();
141}
142
143IOMapper * IOMapper::copyMapperForDevice(IOService * device)
144{
145    return copyMapperForDeviceWithIndex(device, 0);
146}
147
148IOMapper * IOMapper::copyMapperForDeviceWithIndex(IOService * device, unsigned int index)
149{
150    OSData *data;
151    OSObject * obj;
152    IOMapper * mapper = NULL;
153    OSDictionary * matching;
154
155    obj = device->copyProperty("iommu-parent");
156    if (!obj)
157        return (NULL);
158
159    if ((mapper = OSDynamicCast(IOMapper, obj)))
160        return (mapper);
161
162    if ((data = OSDynamicCast(OSData, obj)))
163    {
164        if (index >= data->getLength() / sizeof(UInt32))
165            goto done;
166
167        data = OSData::withBytesNoCopy((UInt32 *)data->getBytesNoCopy() + index, sizeof(UInt32));
168        if (!data)
169            goto done;
170
171        matching = IOService::propertyMatching(gIOMapperIDKey, data);
172        data->release();
173    }
174    else
175        matching = IOService::propertyMatching(gIOMapperIDKey, obj);
176
177    if (matching)
178    {
179        mapper = OSDynamicCast(IOMapper, IOService::waitForMatchingService(matching));
180            matching->release();
181    }
182
183done:
184    if (obj)
185            obj->release();
186    return (mapper);
187}
188
189ppnum_t IOMapper::iovmAllocDMACommand(IODMACommand * command, IOItemCount pageCount)
190{
191    return (0);
192}
193
194void IOMapper::iovmFreeDMACommand(IODMACommand * command,
195				  ppnum_t addr, IOItemCount pageCount)
196{
197}
198
199ppnum_t IOMapper::iovmMapMemory(
200    			  OSObject                    * memory,   // dma command or iomd
201			  ppnum_t                       offsetPage,
202			  ppnum_t                       pageCount,
203			  uint32_t                      options,
204			  upl_page_info_t             * pageList,
205			  const IODMAMapSpecification * mapSpecification)
206{
207    return (0);
208}
209
210void IOMapper::iovmInsert(ppnum_t addr, IOItemCount offset,
211                            ppnum_t *pageList, IOItemCount pageCount)
212{
213    while (pageCount--)
214        iovmInsert(addr, offset++, *pageList++);
215}
216
217void IOMapper::iovmInsert(ppnum_t addr, IOItemCount offset,
218                            upl_page_info_t *pageList, IOItemCount pageCount)
219{
220    for (IOItemCount i = 0; i < pageCount; i++)
221        iovmInsert(addr, offset + i, pageList[i].phys_addr);
222}
223
224OSData * IOMapper::
225NewARTTable(IOByteCount size, void ** virtAddrP, ppnum_t *physAddrP)
226{
227    if (!virtAddrP || !physAddrP)
228	return 0;
229
230    kern_return_t kr;
231    vm_address_t address;
232
233    size = round_page(size);
234    kr = kmem_alloc_contig(kernel_map, &address, size, PAGE_MASK, 0 /*max_pnum*/, 0 /*pnum_mask*/, false);
235    if (kr)
236        return 0;
237
238    ppnum_t pagenum = pmap_find_phys(kernel_pmap, (addr64_t) address);
239    if (pagenum)
240	*physAddrP = pagenum;
241    else {
242	FreeARTTable((OSData *) address, size);
243	address = 0;
244    }
245
246    *virtAddrP = (void *) address;
247
248    return (OSData *) address;
249}
250
251void IOMapper::FreeARTTable(OSData *artHandle, IOByteCount size)
252{
253    vm_address_t address = (vm_address_t) artHandle;
254
255    size = round_page(size);
256    kmem_free(kernel_map, address, size);	// Just panic if address is 0
257}
258
259bool IOMapper::getBypassMask(addr64_t *maskP) const
260{
261    return false;
262}
263
264__BEGIN_DECLS
265
266// These are C accessors to the system mapper for non-IOKit clients
267ppnum_t IOMapperIOVMAlloc(unsigned pages)
268{
269    IOMapper::checkForSystemMapper();
270
271    if (IOMapper::gSystem)
272        return IOMapper::gSystem->iovmAlloc((IOItemCount) pages);
273    else
274        return 0;
275}
276
277void IOMapperIOVMFree(ppnum_t addr, unsigned pages)
278{
279    if (IOMapper::gSystem)
280        IOMapper::gSystem->iovmFree(addr, (IOItemCount) pages);
281}
282
283ppnum_t IOMapperInsertPage(ppnum_t addr, unsigned offset, ppnum_t page)
284{
285    if (IOMapper::gSystem) {
286		if (!addr) panic("!addr");
287        IOMapper::gSystem->iovmInsert(addr, (IOItemCount) offset, page);
288        return addr + offset;
289    }
290    else
291        return page;
292}
293
294void IOMapperInsertPPNPages(ppnum_t addr, unsigned offset,
295                            ppnum_t *pageList, unsigned pageCount)
296{
297    if (!IOMapper::gSystem)
298        panic("IOMapperInsertPPNPages no system mapper");
299    else
300        assert(!((vm_address_t) IOMapper::gSystem & 3));
301
302    IOMapper::gSystem->
303        iovmInsert(addr, (IOItemCount) offset, pageList, pageCount);
304}
305
306void IOMapperInsertUPLPages(ppnum_t addr, unsigned offset,
307                            upl_page_info_t *pageList, unsigned pageCount)
308{
309    if (!IOMapper::gSystem)
310        panic("IOMapperInsertUPLPages no system mapper");
311    else
312        assert(!((vm_address_t) IOMapper::gSystem & 3));
313
314    IOMapper::gSystem->iovmInsert(addr,
315                                 (IOItemCount) offset,
316                                  pageList,
317                                  (IOItemCount) pageCount);
318}
319
320/////////////////////////////////////////////////////////////////////////////
321//
322//
323//	IOLib.h APIs
324//
325//
326/////////////////////////////////////////////////////////////////////////////
327
328#include <machine/machine_routines.h>
329
330UInt8 IOMappedRead8(IOPhysicalAddress address)
331{
332    IOMapper::checkForSystemMapper();
333
334    if (IOMapper::gSystem) {
335        addr64_t addr = IOMapper::gSystem->mapAddr(address);
336        return (UInt8) ml_phys_read_byte_64(addr);
337    }
338    else
339        return (UInt8) ml_phys_read_byte((vm_offset_t) address);
340}
341
342UInt16 IOMappedRead16(IOPhysicalAddress address)
343{
344    IOMapper::checkForSystemMapper();
345
346    if (IOMapper::gSystem) {
347        addr64_t addr = IOMapper::gSystem->mapAddr(address);
348        return (UInt16) ml_phys_read_half_64(addr);
349    }
350    else
351        return (UInt16) ml_phys_read_half((vm_offset_t) address);
352}
353
354UInt32 IOMappedRead32(IOPhysicalAddress address)
355{
356    IOMapper::checkForSystemMapper();
357
358    if (IOMapper::gSystem) {
359        addr64_t addr = IOMapper::gSystem->mapAddr(address);
360	return (UInt32) ml_phys_read_word_64(addr);
361    }
362    else
363        return (UInt32) ml_phys_read_word((vm_offset_t) address);
364}
365
366UInt64 IOMappedRead64(IOPhysicalAddress address)
367{
368    IOMapper::checkForSystemMapper();
369
370    if (IOMapper::gSystem) {
371        addr64_t addr = IOMapper::gSystem->mapAddr(address);
372        return (UInt64) ml_phys_read_double_64(addr);
373    }
374    else
375        return (UInt64) ml_phys_read_double((vm_offset_t) address);
376}
377
378void IOMappedWrite8(IOPhysicalAddress address, UInt8 value)
379{
380    IOMapper::checkForSystemMapper();
381
382    if (IOMapper::gSystem) {
383        addr64_t addr = IOMapper::gSystem->mapAddr(address);
384        ml_phys_write_byte_64(addr, value);
385    }
386    else
387        ml_phys_write_byte((vm_offset_t) address, value);
388}
389
390void IOMappedWrite16(IOPhysicalAddress address, UInt16 value)
391{
392    IOMapper::checkForSystemMapper();
393
394    if (IOMapper::gSystem) {
395        addr64_t addr = IOMapper::gSystem->mapAddr(address);
396        ml_phys_write_half_64(addr, value);
397    }
398    else
399        ml_phys_write_half((vm_offset_t) address, value);
400}
401
402void IOMappedWrite32(IOPhysicalAddress address, UInt32 value)
403{
404    IOMapper::checkForSystemMapper();
405
406    if (IOMapper::gSystem) {
407        addr64_t addr = IOMapper::gSystem->mapAddr(address);
408        ml_phys_write_word_64(addr, value);
409    }
410    else
411        ml_phys_write_word((vm_offset_t) address, value);
412}
413
414void IOMappedWrite64(IOPhysicalAddress address, UInt64 value)
415{
416    IOMapper::checkForSystemMapper();
417
418    if (IOMapper::gSystem) {
419        addr64_t addr = IOMapper::gSystem->mapAddr(address);
420        ml_phys_write_double_64(addr, value);
421    }
422    else
423        ml_phys_write_double((vm_offset_t) address, value);
424}
425
426__END_DECLS
427