Loading...
--- xnu/xnu-12377.101.15/iokit/Kernel/IOUserServer.cpp
+++ /dev/null
@@ -1,8044 +0,0 @@
-/*
- * Copyright (c) 1998-2021 Apple Inc. All rights reserved.
- *
- * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. The rights granted to you under the License
- * may not be used to create, or enable the creation or redistribution of,
- * unlawful or unlicensed copies of an Apple operating system, or to
- * circumvent, violate, or enable the circumvention or violation of, any
- * terms of an Apple operating system software license agreement.
- *
- * Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
- */
-
-#include <IOKit/IORPC.h>
-#include <IOKit/IOKitServer.h>
-#include <IOKit/IOKitKeysPrivate.h>
-#include <IOKit/IOKernelReportStructs.h>
-#include <IOKit/IOUserClient.h>
-#include <IOKit/IOService.h>
-#include <IOKit/IORegistryEntry.h>
-#include <IOKit/IOCatalogue.h>
-#include <IOKit/IOMemoryDescriptor.h>
-#include <IOKit/IOBufferMemoryDescriptor.h>
-#include <IOKit/IOSubMemoryDescriptor.h>
-#include <IOKit/IOMultiMemoryDescriptor.h>
-#include <IOKit/IOMapper.h>
-#include <IOKit/IOLib.h>
-#include <IOKit/IOHibernatePrivate.h>
-#include <IOKit/IOBSD.h>
-#include <IOKit/system.h>
-#include "IOServicePrivate.h"
-#include <IOKit/IOUserServer.h>
-#include <IOKit/IOInterruptEventSource.h>
-#include <IOKit/IOTimerEventSource.h>
-#include <IOKit/IODeviceTreeSupport.h>
-#include <IOKit/pwr_mgt/RootDomain.h>
-#include <IOKit/pwr_mgt/IOPowerConnection.h>
-#include <libkern/c++/OSAllocation.h>
-#include <libkern/c++/OSKext.h>
-#include <libkern/c++/OSSharedPtr.h>
-#include <libkern/OSDebug.h>
-#include <libkern/Block.h>
-#include <kern/cs_blobs.h>
-#include <kern/thread_call.h>
-#include <os/atomic_private.h>
-#include <sys/proc.h>
-#include <sys/reboot.h>
-#include <sys/codesign.h>
-#include <vm/vm_iokit.h>
-#include <mach_debug/mach_debug_types.h>
-#include "IOKitKernelInternal.h"
-#include "IOServicePMPrivate.h"
-
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-
-#include <DriverKit/IODispatchQueue.h>
-#include <DriverKit/OSObject.h>
-#include <DriverKit/OSAction.h>
-#include <DriverKit/IODispatchSource.h>
-#include <DriverKit/IOInterruptDispatchSource.h>
-#include <DriverKit/IOService.h>
-#include <DriverKit/IOMemoryDescriptor.h>
-#include <DriverKit/IOBufferMemoryDescriptor.h>
-#include <DriverKit/IOMemoryMap.h>
-#include <DriverKit/IODataQueueDispatchSource.h>
-#include <DriverKit/IOServiceNotificationDispatchSource.h>
-#include <DriverKit/IOServiceStateNotificationDispatchSource.h>
-#include <DriverKit/IOEventLink.h>
-#include <DriverKit/IOWorkGroup.h>
-#include <DriverKit/IOUserServer.h>
-
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-
-#include <System/IODataQueueDispatchSourceShared.h>
-
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-
-enum{
- kIOUserServerCheckInTimeoutMSecs = 120000ULL,
- kIOUserServerCheckInMaxRetry = 3,
-};
-
-TUNABLE(SInt64, gIODKDebug, "dk", kIODKEnable);
-
-#if DEBUG || DEVELOPMENT
-uint64_t driverkit_checkin_timed_out = 0;
-TUNABLE(bool, disable_dext_crash_reboot, "disable_dext_crash_reboot", 0);
-extern "C" kern_return_t kern_register_userspace_coredump(task_t task, const char * name, boolean_t emergency);
-#endif /* DEBUG || DEVELOPMENT */
-
-extern bool restore_boot;
-
-static OSString * gIOSystemStateSleepDescriptionKey;
-static const OSSymbol * gIOSystemStateSleepDescriptionReasonKey;
-static const OSSymbol * gIOSystemStateSleepDescriptionHibernateStateKey;
-
-static OSString * gIOSystemStateWakeDescriptionKey;
-static const OSSymbol * gIOSystemStateWakeDescriptionWakeReasonKey;
-static const OSSymbol * gIOSystemStateWakeDescriptionContinuousTimeOffsetKey;
-
-static OSString * gIOSystemStateHaltDescriptionKey;
-static const OSSymbol * gIOSystemStateHaltDescriptionHaltStateKey;
-
-static OSString * gIOSystemStatePowerSourceDescriptionKey;
-static const OSSymbol * gIOSystemStatePowerSourceDescriptionACAttachedKey;
-
-extern bool gInUserspaceReboot;
-
-extern void iokit_clear_registered_ports(task_t task);
-
-static IORPCMessage *
-IORPCMessageFromMachReply(IORPCMessageMach * msg);
-
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-
-struct IOPStrings;
-
-class OSUserMetaClass : public OSObject
-{
- OSDeclareDefaultStructors(OSUserMetaClass);
-public:
- const OSSymbol * name;
- const OSMetaClass * meta;
- OSUserMetaClass * superMeta;
-
- queue_chain_t link;
-
- OSClassDescription * description;
- IOPStrings * queueNames;
- uint32_t methodCount;
- uint64_t * methods;
-
- virtual void free() override;
- virtual kern_return_t Dispatch(const IORPC rpc) APPLE_KEXT_OVERRIDE;
-};
-OSDefineMetaClassAndStructors(OSUserMetaClass, OSObject);
-
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-
-class IOUserService : public IOService
-{
- friend class IOService;
-
- OSDeclareDefaultStructors(IOUserService)
-
- virtual bool
- start(IOService * provider) APPLE_KEXT_OVERRIDE;
-};
-
-OSDefineMetaClassAndStructors(IOUserService, IOService)
-
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-
-OSDefineMetaClassAndStructors(IOUserServerCheckInToken, OSObject);
-OSDefineMetaClassAndStructors(_IOUserServerCheckInCancellationHandler, OSObject);
-
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-
-
-bool
-IOUserService::start(IOService * provider)
-{
- bool ok = true;
- IOReturn ret;
-
- ret = Start(provider);
- if (kIOReturnSuccess != ret) {
- return false;
- }
-
- return ok;
-}
-
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-
-#undef super
-
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-
-struct IODispatchQueue_IVars {
- IOUserServer * userServer;
- IODispatchQueue * queue;
- queue_chain_t link;
- uint64_t tid;
-
- mach_port_t serverPort;
-};
-
-struct OSAction_IVars {
- OSObject * target;
- uint64_t targetmsgid;
- uint64_t msgid;
- IOUserServer * userServer;
- OSActionAbortedHandler abortedHandler;
- OSString * typeName;
- void * reference;
- size_t referenceSize;
- bool aborted;
-};
-
-struct IOWorkGroup_IVars {
- IOUserServer * userServer;
- OSString * name;
- IOUserUserClient * userClient;
-};
-
-struct IOEventLink_IVars {
- IOUserServer * userServer;
- OSString * name;
- IOUserUserClient * userClient;
-};
-
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-
-kern_return_t
-IOService::GetRegistryEntryID_Impl(
- uint64_t * registryEntryID)
-{
- IOReturn ret = kIOReturnSuccess;
-
- *registryEntryID = getRegistryEntryID();
-
- return ret;
-}
-
-kern_return_t
-IOService::SetName_Impl(
- const char * name)
-{
- IOReturn ret = kIOReturnSuccess;
-
- setName(name);
-
- return ret;
-}
-
-kern_return_t
-IOService::CopyName_Impl(
- OSString ** name)
-{
- const OSString * str = copyName();
- *name = __DECONST(OSString *, str);
- return str ? kIOReturnSuccess : kIOReturnError;
-}
-
-
-kern_return_t
-IOService::Start_Impl(
- IOService * provider)
-{
- IOReturn ret = kIOReturnSuccess;
- return ret;
-}
-
-
-IOReturn
-IOService::UpdateReport_Impl(OSData *channels, uint32_t action,
- uint32_t *outElementCount,
- uint64_t offset, uint64_t capacity,
- IOMemoryDescriptor *buffer)
-{
- return kIOReturnUnsupported;
-}
-
-IOReturn
-IOService::ConfigureReport_Impl(OSData *channels, uint32_t action, uint32_t *outCount)
-{
- return kIOReturnUnsupported;
-}
-
-// adapt old signature of configureReport to the iig-friendly signature of ConfigureReport
-IOReturn
-IOService::_ConfigureReport(IOReportChannelList *channelList,
- IOReportConfigureAction action,
- void *result,
- void *destination)
-{
- if (action != kIOReportEnable && action != kIOReportGetDimensions && action != kIOReportDisable) {
- return kIOReturnUnsupported;
- }
- static_assert(sizeof(IOReportChannelList) == 8);
- static_assert(sizeof(IOReportChannel) == 16);
- unsigned int size_of_channels;
- bool overflow = os_mul_and_add_overflow(channelList->nchannels, sizeof(IOReportChannel), sizeof(IOReportChannelList), &size_of_channels);
- if (overflow) {
- return kIOReturnOverrun;
- }
- OSSharedPtr<OSData> sp_channels(OSData::withBytesNoCopy(channelList, size_of_channels), libkern::no_retain);
- if (!sp_channels) {
- return kIOReturnNoMemory;
- }
- int *resultp = (int*) result;
- uint32_t count = 0;
- IOReturn r = ConfigureReport(sp_channels.get(), action, &count);
- int new_result;
- overflow = os_add_overflow(*resultp, count, &new_result);
- if (overflow) {
- return kIOReturnOverrun;
- }
- *resultp = new_result;
- return r;
-}
-
-// adapt old signature of updateReport to the iig-friendly signature of UpdateReport
-IOReturn
-IOService::_UpdateReport(IOReportChannelList *channelList,
- IOReportUpdateAction action,
- void *result,
- void *destination)
-{
- if (action != kIOReportCopyChannelData) {
- return kIOReturnUnsupported;
- }
- unsigned int size_of_channels;
- bool overflow = os_mul_and_add_overflow(channelList->nchannels, sizeof(IOReportChannel), sizeof(IOReportChannelList), &size_of_channels);
- if (overflow) {
- return kIOReturnOverrun;
- }
- OSSharedPtr<OSData> sp_channels(OSData::withBytesNoCopy(channelList, size_of_channels), libkern::no_retain);
- if (!sp_channels) {
- return kIOReturnNoMemory;
- }
- int *resultp = (int*) result;
- uint32_t count = 0;
- auto buffer = (IOBufferMemoryDescriptor*) destination;
- uint64_t length = buffer->getLength();
- buffer->setLength(buffer->getCapacity());
- IOReturn r = UpdateReport(sp_channels.get(), action, &count, length, buffer->getCapacity() - length, buffer);
- int new_result;
- overflow = os_add_overflow(*resultp, count, &new_result);
- size_t new_length;
- overflow = overflow || os_mul_and_add_overflow(count, sizeof(IOReportElement), length, &new_length);
- if (overflow || new_length > buffer->getCapacity()) {
- buffer->setLength(length);
- return kIOReturnOverrun;
- }
- *resultp = new_result;
- buffer->setLength(new_length);
- return r;
-}
-
-
-IOReturn
-IOService::SetLegend_Impl(OSArray *legend, bool is_public)
-{
- bool ok = setProperty(kIOReportLegendKey, legend);
- ok = ok && setProperty(kIOReportLegendPublicKey, is_public);
- return ok ? kIOReturnSuccess : kIOReturnError;
-}
-
-
-kern_return_t
-IOService::RegisterService_Impl()
-{
- IOReturn ret = kIOReturnSuccess;
- bool started;
-
- IOUserServer *us = (typeof(us))thread_iokit_tls_get(0);
- if (reserved != NULL && reserved->uvars != NULL && reserved->uvars->userServer == us) {
- started = reserved->uvars->started;
- } else {
- // assume started
- started = true;
- }
-
- if (OSDynamicCast(IOUserServer, this) != NULL || started) {
- registerService(kIOServiceAsynchronous);
- } else {
- assert(reserved != NULL && reserved->uvars != NULL);
- reserved->uvars->deferredRegisterService = true;
- }
-
- return ret;
-}
-
-kern_return_t
-IOService::CopyDispatchQueue_Impl(
- const char * name,
- IODispatchQueue ** queue)
-{
- IODispatchQueue * result;
- IOService * service;
- IOReturn ret;
- uint32_t index;
-
- if (!reserved->uvars) {
- return kIOReturnError;
- }
-
- if (!reserved->uvars->queueArray) {
- // CopyDispatchQueue should not be called after the service has stopped
- return kIOReturnError;
- }
-
- ret = kIOReturnNotFound;
- index = -1U;
- if (!strcmp("Default", name)) {
- index = 0;
- } else if (reserved->uvars->userMeta
- && reserved->uvars->userMeta->queueNames) {
- index = reserved->uvars->userServer->stringArrayIndex(reserved->uvars->userMeta->queueNames, name);
- if (index != -1U) {
- index++;
- }
- }
- if (index == -1U) {
- if ((service = getProvider())) {
- ret = service->CopyDispatchQueue(name, queue);
- }
- } else {
- result = reserved->uvars->queueArray[index];
- if (result) {
- result->retain();
- *queue = result;
- ret = kIOReturnSuccess;
- }
- }
-
- return ret;
-}
-
-kern_return_t
-IOService::CreateDefaultDispatchQueue_Impl(
- IODispatchQueue ** queue)
-{
- return kIOReturnError;
-}
-
-kern_return_t
-IOService::CoreAnalyticsSendEvent_Impl(
- uint64_t options,
- OSString * eventName,
- OSDictionary * eventPayload)
-{
- kern_return_t ret;
-
- if (NULL == gIOCoreAnalyticsSendEventProc) {
- // perhaps save for later?
- return kIOReturnNotReady;
- }
-
- ret = (*gIOCoreAnalyticsSendEventProc)(options, eventName, eventPayload);
-
- return ret;
-}
-
-kern_return_t
-IOService::SetDispatchQueue_Impl(
- const char * name,
- IODispatchQueue * queue)
-{
- IOReturn ret = kIOReturnSuccess;
- uint32_t index;
-
- if (!reserved->uvars) {
- return kIOReturnError;
- }
-
- if (kIODKLogSetup & gIODKDebug) {
- DKLOG(DKS "::SetDispatchQueue(%s)\n", DKN(this), name);
- }
- queue->ivars->userServer = reserved->uvars->userServer;
- index = -1U;
- if (!strcmp("Default", name)) {
- index = 0;
- } else if (reserved->uvars->userMeta
- && reserved->uvars->userMeta->queueNames) {
- index = reserved->uvars->userServer->stringArrayIndex(reserved->uvars->userMeta->queueNames, name);
- if (index != -1U) {
- index++;
- }
- }
- if (index == -1U) {
- ret = kIOReturnBadArgument;
- } else {
- reserved->uvars->queueArray[index] = queue;
- queue->retain();
- }
-
- return ret;
-}
-
-IOService *
-IOService::GetProvider() const
-{
- return getProvider();
-}
-
-kern_return_t
-IOService::SetProperties_Impl(
- OSDictionary * properties)
-{
- IOUserServer * us;
- OSDictionary * dict;
- IOReturn ret;
-
- us = (typeof(us))thread_iokit_tls_get(0);
- dict = OSDynamicCast(OSDictionary, properties);
- if (NULL == us) {
- if (!dict) {
- return kIOReturnBadArgument;
- }
- bool ok __block = true;
- dict->iterateObjects(^bool (const OSSymbol * key, OSObject * value) {
- ok = setProperty(key, value);
- return !ok;
- });
- ret = ok ? kIOReturnSuccess : kIOReturnNotWritable;
- return ret;
- }
-
- ret = setProperties(properties);
-
- if (kIOReturnUnsupported == ret) {
- if (dict && reserved->uvars && (reserved->uvars->userServer == us)) {
- ret = runPropertyActionBlock(^IOReturn (void) {
- OSDictionary * userProps;
- IOReturn ret;
-
- userProps = OSDynamicCast(OSDictionary, getProperty(gIOUserServicePropertiesKey));
- if (userProps) {
- userProps = (typeof(userProps))userProps->copyCollection();
- } else {
- userProps = OSDictionary::withCapacity(4);
- }
- if (!userProps) {
- ret = kIOReturnNoMemory;
- } else {
- bool ok = userProps->merge(dict);
- if (ok) {
- ok = setProperty(gIOUserServicePropertiesKey, userProps);
- }
- OSSafeReleaseNULL(userProps);
- ret = ok ? kIOReturnSuccess : kIOReturnNotWritable;
- }
- return ret;
- });
- }
- }
-
- return ret;
-}
-
-kern_return_t
-IOService::RemoveProperty_Impl(OSString * propertyName)
-{
- IOUserServer * us = (IOUserServer *)thread_iokit_tls_get(0);
- IOReturn ret = kIOReturnUnsupported;
-
- if (NULL == propertyName) {
- return kIOReturnUnsupported;
- }
- if (NULL == us) {
- removeProperty(propertyName);
- return kIOReturnSuccess;
- }
- if (reserved && reserved->uvars && reserved->uvars->userServer == us) {
- ret = runPropertyActionBlock(^IOReturn (void) {
- OSDictionary * userProps;
- userProps = OSDynamicCast(OSDictionary, getProperty(gIOUserServicePropertiesKey));
- if (userProps) {
- userProps = (OSDictionary *)userProps->copyCollection();
- if (!userProps) {
- return kIOReturnNoMemory;
- }
- userProps->removeObject(propertyName);
- bool ok = setProperty(gIOUserServicePropertiesKey, userProps);
- OSSafeReleaseNULL(userProps);
- return ok ? kIOReturnSuccess : kIOReturnNotWritable;
- } else {
- return kIOReturnNotFound;
- }
- });
- }
- return ret;
-}
-
-kern_return_t
-IOService::CopyProperties_Local(
- OSDictionary ** properties)
-{
- OSDictionary * props;
- OSDictionary * userProps;
-
- props = dictionaryWithProperties();
- userProps = OSDynamicCast(OSDictionary, props->getObject(gIOUserServicePropertiesKey));
- if (userProps) {
- props->merge(userProps);
- props->removeObject(gIOUserServicePropertiesKey);
- }
-
- *properties = props;
-
- return props ? kIOReturnSuccess : kIOReturnNoMemory;
-}
-
-kern_return_t
-IOService::CopyProperties_Impl(
- OSDictionary ** properties)
-{
- return CopyProperties_Local(properties);
-}
-
-kern_return_t
-IOService::RequireMaxBusStall_Impl(
- uint64_t u64ns)
-{
- IOReturn ret;
- UInt32 ns;
-
- if (os_convert_overflow(u64ns, &ns)) {
- return kIOReturnBadArgument;
- }
- ret = requireMaxBusStall(ns);
-
- return ret;
-}
-
-#if PRIVATE_WIFI_ONLY
-kern_return_t
-IOService::UserSetProperties_Impl(
- OSContainer * properties)
-{
- return kIOReturnUnsupported;
-}
-
-kern_return_t
-IOService::SendIOMessageServicePropertyChange_Impl(void)
-{
- return messageClients(kIOMessageServicePropertyChange);
-}
-#endif /* PRIVATE_WIFI_ONLY */
-
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-
-kern_return_t
-IOMemoryDescriptor::_CopyState_Impl(
- _IOMDPrivateState * state)
-{
- IOReturn ret;
-
- state->length = _length;
- state->options = _flags;
-
- ret = kIOReturnSuccess;
-
- return ret;
-}
-
-kern_return_t
-IOMemoryDescriptor::GetLength(uint64_t * returnLength)
-{
- *returnLength = getLength();
-
- return kIOReturnSuccess;
-}
-
-kern_return_t
-IOMemoryDescriptor::CreateMapping_Impl(
- uint64_t options,
- uint64_t address,
- uint64_t offset,
- uint64_t length,
- uint64_t alignment,
- IOMemoryMap ** map)
-{
- IOReturn ret;
- IOMemoryMap * resultMap;
- IOOptionBits koptions;
- mach_vm_address_t atAddress;
-
- ret = kIOReturnSuccess;
- koptions = 0;
- resultMap = NULL;
-
- if (kIOMemoryMapFixedAddress & options) {
- atAddress = address;
- koptions = 0;
- } else {
- switch (kIOMemoryMapGuardedMask & options) {
- default:
- case kIOMemoryMapGuardedDefault:
- koptions |= kIOMapGuardedSmall;
- break;
- case kIOMemoryMapGuardedNone:
- break;
- case kIOMemoryMapGuardedSmall:
- koptions |= kIOMapGuardedSmall;
- break;
- case kIOMemoryMapGuardedLarge:
- koptions |= kIOMapGuardedLarge;
- break;
- }
- atAddress = 0;
- koptions |= kIOMapAnywhere;
- }
-
- if ((kIOMemoryMapReadOnly & options) || (kIODirectionOut == getDirection())) {
- if (!reserved || (current_task() != reserved->creator)) {
- koptions |= kIOMapReadOnly;
- }
- }
-
- switch (0xFF00 & options) {
- case kIOMemoryMapCacheModeDefault:
- koptions |= kIOMapDefaultCache;
- break;
- case kIOMemoryMapCacheModeInhibit:
- koptions |= kIOMapInhibitCache;
- break;
- case kIOMemoryMapCacheModeCopyback:
- koptions |= kIOMapCopybackCache;
- break;
- case kIOMemoryMapCacheModeWriteThrough:
- koptions |= kIOMapWriteThruCache;
- break;
- case kIOMemoryMapCacheModeRealTime:
- koptions |= kIOMapRealTimeCache;
- break;
- default:
- ret = kIOReturnBadArgument;
- }
-
- if (kIOReturnSuccess == ret) {
- resultMap = createMappingInTask(current_task(), atAddress, koptions, offset, length);
- if (!resultMap) {
- ret = kIOReturnError;
- }
- }
-
- *map = resultMap;
-
- return ret;
-}
-
-kern_return_t
-IOMemoryDescriptor::CreateSubMemoryDescriptor_Impl(
- uint64_t memoryDescriptorCreateOptions,
- uint64_t offset,
- uint64_t length,
- IOMemoryDescriptor * ofDescriptor,
- IOMemoryDescriptor ** memory)
-{
- IOReturn ret;
- IOMemoryDescriptor * iomd;
- IOByteCount mdOffset;
- IOByteCount mdLength;
- IOByteCount mdEnd;
-
- if (!ofDescriptor) {
- return kIOReturnBadArgument;
- }
- if (memoryDescriptorCreateOptions & ~kIOMemoryDirectionOutIn) {
- return kIOReturnBadArgument;
- }
- if (os_convert_overflow(offset, &mdOffset)) {
- return kIOReturnBadArgument;
- }
- if (os_convert_overflow(length, &mdLength)) {
- return kIOReturnBadArgument;
- }
- if (os_add_overflow(mdOffset, mdLength, &mdEnd)) {
- return kIOReturnBadArgument;
- }
- if (mdEnd > ofDescriptor->getLength()) {
- return kIOReturnBadArgument;
- }
-
- iomd = IOSubMemoryDescriptor::withSubRange(
- ofDescriptor, mdOffset, mdLength, (IOOptionBits) memoryDescriptorCreateOptions);
-
- if (iomd) {
- ret = kIOReturnSuccess;
- *memory = iomd;
- } else {
- ret = kIOReturnNoMemory;
- *memory = NULL;
- }
-
- return ret;
-}
-
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-
-kern_return_t
-IOMemoryDescriptor::CreateWithMemoryDescriptors_Impl(
- uint64_t memoryDescriptorCreateOptions,
- uint32_t withDescriptorsCount,
- IOMemoryDescriptor ** const withDescriptors,
- IOMemoryDescriptor ** memory)
-{
- IOReturn ret;
- IOMemoryDescriptor * iomd;
-
- if (!withDescriptors) {
- return kIOReturnBadArgument;
- }
- if (!withDescriptorsCount) {
- return kIOReturnBadArgument;
- }
- if (memoryDescriptorCreateOptions & ~kIOMemoryDirectionOutIn) {
- return kIOReturnBadArgument;
- }
-
- for (unsigned int idx = 0; idx < withDescriptorsCount; idx++) {
- if (NULL == withDescriptors[idx]) {
- return kIOReturnBadArgument;
- }
- }
-
- iomd = IOMultiMemoryDescriptor::withDescriptors(withDescriptors, withDescriptorsCount,
- (IODirection) memoryDescriptorCreateOptions, false);
-
- if (iomd) {
- ret = kIOReturnSuccess;
- *memory = iomd;
- } else {
- ret = kIOReturnNoMemory;
- *memory = NULL;
- }
-
- return ret;
-}
-
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-
-kern_return_t
-IOUserClient::CreateMemoryDescriptorFromClient_Impl(
- uint64_t memoryDescriptorCreateOptions,
- uint32_t segmentsCount,
- const IOAddressSegment segments[32],
- IOMemoryDescriptor ** memory)
-{
- IOReturn ret;
- IOMemoryDescriptor * iomd;
- IOOptionBits mdOptions;
- IOUserUserClient * me;
- IOAddressRange * ranges;
-
- me = OSDynamicCast(IOUserUserClient, this);
- if (!me) {
- return kIOReturnBadArgument;
- }
- if (!me->fTask) {
- return kIOReturnNotReady;
- }
-
- mdOptions = kIOMemoryThreadSafe;
- if (kIOMemoryDirectionOut & memoryDescriptorCreateOptions) {
- mdOptions |= kIODirectionOut;
- }
- if (kIOMemoryDirectionIn & memoryDescriptorCreateOptions) {
- mdOptions |= kIODirectionIn;
- }
- if (!(kIOMemoryDisableCopyOnWrite & memoryDescriptorCreateOptions)) {
- mdOptions |= kIOMemoryMapCopyOnWrite;
- }
-
- static_assert(sizeof(IOAddressRange) == sizeof(IOAddressSegment));
- ranges = __DECONST(IOAddressRange *, &segments[0]);
-
- iomd = IOMemoryDescriptor::withAddressRanges(
- ranges, segmentsCount,
- mdOptions, me->fTask);
-
- if (iomd) {
- ret = kIOReturnSuccess;
- *memory = iomd;
- } else {
- ret = kIOReturnNoMemory;
- *memory = NULL;
- }
-
- return ret;
-}
-
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-
-kern_return_t
-IOMemoryMap::_CopyState_Impl(
- _IOMemoryMapPrivateState * state)
-{
- IOReturn ret;
-
- state->offset = fOffset;
- state->length = getLength();
- state->address = getAddress();
- state->options = getMapOptions();
-
- ret = kIOReturnSuccess;
-
- return ret;
-}
-
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-
-kern_return_t
-IOBufferMemoryDescriptor::Create_Impl(
- uint64_t options,
- uint64_t capacity,
- uint64_t alignment,
- IOBufferMemoryDescriptor ** memory)
-{
- IOReturn ret;
- IOOptionBits bmdOptions;
- IOBufferMemoryDescriptor * bmd;
- IOMemoryDescriptorReserved * reserved;
-
- if (options & ~((uint64_t) kIOMemoryDirectionOutIn)) {
- // no other options currently defined
- return kIOReturnBadArgument;
- }
- bmdOptions = (options & kIOMemoryDirectionOutIn) | kIOMemoryKernelUserShared | kIOMemoryThreadSafe;
- bmd = IOBufferMemoryDescriptor::inTaskWithOptions(
- kernel_task, bmdOptions, capacity, alignment);
-
- *memory = bmd;
-
- if (!bmd) {
- return kIOReturnNoMemory;
- }
-
- reserved = bmd->getKernelReserved();
- reserved->creator = current_task();
- task_reference(reserved->creator);
-
- ret = kIOReturnSuccess;
-
- return ret;
-}
-
-kern_return_t
-IOBufferMemoryDescriptor::SetLength_Impl(
- uint64_t length)
-{
- setLength(length);
- return kIOReturnSuccess;
-}
-
-
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-
-kern_return_t
-IODMACommand::Create_Impl(
- IOService * device,
- uint64_t options,
- const IODMACommandSpecification * specification,
- IODMACommand ** command)
-{
- IOReturn ret;
- IODMACommand * dma;
- IODMACommand::SegmentOptions segmentOptions;
- IOMapper * mapper;
-
- if (options & ~((uint64_t) kIODMACommandCreateNoOptions)) {
- // no other options currently defined
- return kIOReturnBadArgument;
- }
-
- if (os_convert_overflow(specification->maxAddressBits, &segmentOptions.fNumAddressBits)) {
- return kIOReturnBadArgument;
- }
- segmentOptions.fMaxSegmentSize = 0;
- segmentOptions.fMaxTransferSize = 0;
- segmentOptions.fAlignment = 1;
- segmentOptions.fAlignmentLength = 1;
- segmentOptions.fAlignmentInternalSegments = 1;
- segmentOptions.fStructSize = sizeof(segmentOptions);
-
- mapper = IOMapper::copyMapperForDevice(device);
-
- dma = IODMACommand::withSpecification(
- kIODMACommandOutputHost64,
- &segmentOptions,
- kIODMAMapOptionDextOwner |
- kIODMAMapOptionMapped,
- mapper,
- NULL);
-
- OSSafeReleaseNULL(mapper);
- *command = dma;
-
- if (!dma) {
- return kIOReturnNoMemory;
- }
- ret = kIOReturnSuccess;
-
- return ret;
-}
-
-#define fInternalState reserved
-
-kern_return_t
-IODMACommand::PrepareForDMA_Impl(
- uint64_t options,
- IOMemoryDescriptor * memory,
- uint64_t offset,
- uint64_t length,
- uint64_t * flags,
- uint32_t * segmentsCount,
- IOAddressSegment * segments)
-{
- IOReturn ret;
- uint64_t lflags, mdFlags;
- UInt32 numSegments;
- UInt64 genOffset;
-
- if (options & ~((uint64_t) kIODMACommandPrepareForDMANoOptions)) {
- // no other options currently defined
- return kIOReturnBadArgument;
- }
-
- if (memory == NULL) {
- return kIOReturnBadArgument;
- }
-
- assert(fInternalState->fDextLock);
- IOLockLock(fInternalState->fDextLock);
-
- // uses IOMD direction
- ret = memory->prepare();
- if (kIOReturnSuccess != ret) {
- goto exit;
- }
-
- ret = setMemoryDescriptor(memory, false);
- if (kIOReturnSuccess != ret) {
- memory->complete();
- goto exit;
- }
-
- ret = prepare(offset, length);
- if (kIOReturnSuccess != ret) {
- clearMemoryDescriptor(false);
- memory->complete();
- goto exit;
- }
-
- static_assert(sizeof(IODMACommand::Segment64) == sizeof(IOAddressSegment));
-
- numSegments = *segmentsCount;
- genOffset = 0;
- ret = genIOVMSegments(&genOffset, segments, &numSegments);
-
- if (kIOReturnSuccess != ret) {
- clearMemoryDescriptor(true);
- memory->complete();
- goto exit;
- }
-
- mdFlags = fMemory->getFlags();
- lflags = 0;
- if (kIODirectionOut & mdFlags) {
- lflags |= kIOMemoryDirectionOut;
- }
- if (kIODirectionIn & mdFlags) {
- lflags |= kIOMemoryDirectionIn;
- }
- *flags = lflags;
- *segmentsCount = numSegments;
-
-exit:
- IOLockUnlock(fInternalState->fDextLock);
-
- return ret;
-}
-
-kern_return_t
-IODMACommand::CompleteDMA_Impl(
- uint64_t options)
-{
- IOReturn ret, completeRet;
- IOMemoryDescriptor * md;
-
- if (options & ~((uint64_t) kIODMACommandCompleteDMANoOptions)) {
- // no other options currently defined
- return kIOReturnBadArgument;
- }
-
- assert(fInternalState->fDextLock);
- IOLockLock(fInternalState->fDextLock);
-
- if (!fInternalState->fPrepared) {
- ret = kIOReturnNotReady;
- goto exit;
- }
-
- md = __DECONST(IOMemoryDescriptor *, fMemory);
- if (md) {
- md->retain();
- }
-
- ret = clearMemoryDescriptor(true);
-
- if (md) {
- completeRet = md->complete();
- OSSafeReleaseNULL(md);
- if (kIOReturnSuccess == ret) {
- ret = completeRet;
- }
- }
-exit:
- IOLockUnlock(fInternalState->fDextLock);
-
- return ret;
-}
-
-kern_return_t
-IODMACommand::GetPreparation_Impl(
- uint64_t * offset,
- uint64_t * length,
- IOMemoryDescriptor ** memory)
-{
- IOReturn ret;
- IOMemoryDescriptor * md;
-
- if (!fActive) {
- return kIOReturnNotReady;
- }
-
- ret = getPreparedOffsetAndLength(offset, length);
- if (kIOReturnSuccess != ret) {
- return ret;
- }
-
- if (memory) {
- md = __DECONST(IOMemoryDescriptor *, fMemory);
- *memory = md;
- if (!md) {
- ret = kIOReturnNotReady;
- } else {
- md->retain();
- }
- }
- return ret;
-}
-
-kern_return_t
-IODMACommand::PerformOperation_Impl(
- uint64_t options,
- uint64_t dmaOffset,
- uint64_t length,
- uint64_t dataOffset,
- IOMemoryDescriptor * data)
-{
- IOReturn ret;
- OSDataAllocation<uint8_t> buffer;
- UInt64 copiedDMA;
- IOByteCount mdOffset, mdLength, copied;
-
- if (options & ~((uint64_t)
- (kIODMACommandPerformOperationOptionRead
- | kIODMACommandPerformOperationOptionWrite
- | kIODMACommandPerformOperationOptionZero))) {
- // no other options currently defined
- return kIOReturnBadArgument;
- }
-
- if (!fActive) {
- return kIOReturnNotReady;
- }
- if (os_convert_overflow(dataOffset, &mdOffset)) {
- return kIOReturnBadArgument;
- }
- if (os_convert_overflow(length, &mdLength)) {
- return kIOReturnBadArgument;
- }
- if (length > fMemory->getLength()) {
- return kIOReturnBadArgument;
- }
- buffer = OSDataAllocation<uint8_t>(length, OSAllocateMemory);
- if (!buffer) {
- return kIOReturnNoMemory;
- }
-
- switch (options) {
- case kIODMACommandPerformOperationOptionZero:
- bzero(buffer.data(), length);
- copiedDMA = writeBytes(dmaOffset, buffer.data(), length);
- if (copiedDMA != length) {
- ret = kIOReturnUnderrun;
- break;
- }
- ret = kIOReturnSuccess;
- break;
-
- case kIODMACommandPerformOperationOptionRead:
- case kIODMACommandPerformOperationOptionWrite:
-
- if (!data) {
- ret = kIOReturnBadArgument;
- break;
- }
- if (length > data->getLength()) {
- ret = kIOReturnBadArgument;
- break;
- }
- if (kIODMACommandPerformOperationOptionWrite == options) {
- copied = data->readBytes(mdOffset, buffer.data(), mdLength);
- if (copied != mdLength) {
- ret = kIOReturnUnderrun;
- break;
- }
- copiedDMA = writeBytes(dmaOffset, buffer.data(), length);
- if (copiedDMA != length) {
- ret = kIOReturnUnderrun;
- break;
- }
- } else { /* kIODMACommandPerformOperationOptionRead */
- copiedDMA = readBytes(dmaOffset, buffer.data(), length);
- if (copiedDMA != length) {
- ret = kIOReturnUnderrun;
- break;
- }
- copied = data->writeBytes(mdOffset, buffer.data(), mdLength);
- if (copied != mdLength) {
- ret = kIOReturnUnderrun;
- break;
- }
- }
- ret = kIOReturnSuccess;
- break;
- default:
- ret = kIOReturnBadArgument;
- break;
- }
-
- return ret;
-}
-
-
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-
-static kern_return_t
-OSActionCreateWithTypeNameInternal(OSObject * target, uint64_t targetmsgid, uint64_t msgid, size_t referenceSize, OSString * typeName, bool fromKernel, OSAction ** action)
-{
- OSAction * inst = NULL;
- void * reference = NULL; // must release
- const OSSymbol *sym = NULL; // must release
- OSObject *obj = NULL; // must release
- const OSMetaClass *actionMetaClass = NULL; // do not release
- kern_return_t ret;
-
- if (fromKernel && typeName) {
- /* The action is being constructed in the kernel with a type name */
- sym = OSSymbol::withString(typeName);
- actionMetaClass = OSMetaClass::getMetaClassWithName(sym);
- if (actionMetaClass && actionMetaClass->getSuperClass() == OSTypeID(OSAction)) {
- obj = actionMetaClass->alloc();
- if (!obj) {
- ret = kIOReturnNoMemory;
- goto finish;
- }
- inst = OSDynamicCast(OSAction, obj);
- obj = NULL; // prevent release
- assert(inst); // obj is a subclass of OSAction so the dynamic cast should always work
- } else {
- DKLOG("Attempted to create action object with type \"%s\" which does not inherit from OSAction\n", typeName->getCStringNoCopy());
- ret = kIOReturnBadArgument;
- goto finish;
- }
- } else {
- inst = OSTypeAlloc(OSAction);
- if (!inst) {
- ret = kIOReturnNoMemory;
- goto finish;
- }
- }
-
- if (referenceSize != 0) {
- reference = IONewZeroData(uint8_t, referenceSize);
- if (reference == NULL) {
- ret = kIOReturnNoMemory;
- goto finish;
- }
- }
-
- inst->ivars = IONewZero(OSAction_IVars, 1);
- if (!inst->ivars) {
- ret = kIOReturnNoMemory;
- goto finish;
- }
- if (target) {
- target->retain();
- if (!fromKernel && !OSDynamicCast(IOService, target)) {
- IOUserServer * us;
- us = (typeof(us))thread_iokit_tls_get(0);
- inst->ivars->userServer = OSDynamicCast(IOUserServer, us);
- assert(inst->ivars->userServer);
- inst->ivars->userServer->retain();
- }
- }
- inst->ivars->target = target;
- inst->ivars->targetmsgid = targetmsgid;
- inst->ivars->msgid = msgid;
-
- inst->ivars->reference = reference;
- inst->ivars->referenceSize = referenceSize;
- reference = NULL; // prevent release
-
- if (typeName) {
- typeName->retain();
- }
- inst->ivars->typeName = typeName;
-
- *action = inst;
- inst = NULL; // prevent release
- ret = kIOReturnSuccess;
-
-finish:
- OSSafeReleaseNULL(obj);
- OSSafeReleaseNULL(sym);
- OSSafeReleaseNULL(inst);
- if (reference) {
- IODeleteData(reference, uint8_t, referenceSize);
- }
-
- return ret;
-}
-
-kern_return_t
-OSAction::Create(OSAction_Create_Args)
-{
- return OSAction::CreateWithTypeName(target, targetmsgid, msgid, referenceSize, NULL, action);
-}
-
-kern_return_t
-OSAction::CreateWithTypeName(OSAction_CreateWithTypeName_Args)
-{
- return OSActionCreateWithTypeNameInternal(target, targetmsgid, msgid, referenceSize, typeName, true, action);
-}
-
-kern_return_t
-OSAction::Create_Impl(
- OSObject * target,
- uint64_t targetmsgid,
- uint64_t msgid,
- size_t referenceSize,
- OSAction ** action)
-{
- return OSAction::CreateWithTypeName_Impl(target, targetmsgid, msgid, referenceSize, NULL, action);
-}
-
-kern_return_t
-OSAction::CreateWithTypeName_Impl(
- OSObject * target,
- uint64_t targetmsgid,
- uint64_t msgid,
- size_t referenceSize,
- OSString * typeName,
- OSAction ** action)
-{
- return OSActionCreateWithTypeNameInternal(target, targetmsgid, msgid, referenceSize, typeName, false, action);
-}
-
-void
-OSAction::free()
-{
- if (ivars) {
- if (ivars->abortedHandler) {
- Block_release(ivars->abortedHandler);
- ivars->abortedHandler = NULL;
- }
- OSSafeReleaseNULL(ivars->target);
- OSSafeReleaseNULL(ivars->typeName);
- OSSafeReleaseNULL(ivars->userServer);
- if (ivars->reference) {
- assert(ivars->referenceSize > 0);
- IODeleteData(ivars->reference, uint8_t, ivars->referenceSize);
- }
- IOSafeDeleteNULL(ivars, OSAction_IVars, 1);
- }
- return super::free();
-}
-
-void *
-OSAction::GetReference()
-{
- assert(ivars && ivars->referenceSize && ivars->reference);
- return ivars->reference;
-}
-
-kern_return_t
-OSAction::SetAbortedHandler(OSActionAbortedHandler handler)
-{
- ivars->abortedHandler = Block_copy(handler);
- return kIOReturnSuccess;
-}
-
-void
-OSAction::Aborted_Impl(void)
-{
- if (!os_atomic_cmpxchg(&ivars->aborted, false, true, relaxed)) {
- // already aborted
- return;
- }
- if (ivars->abortedHandler) {
- ivars->abortedHandler();
- }
-}
-
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-
-struct IODispatchSource_IVars {
- queue_chain_t link;
- IODispatchSource * source;
- IOUserServer * server;
- IODispatchQueue_IVars * queue;
- bool enabled;
-};
-
-bool
-IODispatchSource::init()
-{
- if (!super::init()) {
- return false;
- }
-
- ivars = IOMallocType(IODispatchSource_IVars);
-
- ivars->source = this;
-
- return true;
-}
-
-void
-IODispatchSource::free()
-{
- IOFreeType(ivars, IODispatchSource_IVars);
- super::free();
-}
-
-kern_return_t
-IODispatchSource::SetEnable_Impl(
- bool enable)
-{
- return SetEnableWithCompletion(enable, NULL);
-}
-
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-
-struct IOInterruptDispatchSource_IVars {
- IOService * provider;
- uint32_t intIndex;
- uint32_t flags;
- int interruptType;
- IOSimpleLock * lock;
- thread_t waiter;
- uint64_t count;
- uint64_t time;
- OSAction * action;
- bool enable;
- bool canceled;
-};
-
-void
-IOInterruptDispatchSourceInterrupt(OSObject * target, void * refCon,
- IOService * nub, int source );
-
-void
-IOInterruptDispatchSourceInterrupt(OSObject * target, void * refCon,
- IOService * nub, int source )
-{
- IOInterruptDispatchSource_IVars * ivars = (typeof(ivars))refCon;
- IOInterruptState is;
-
- is = IOSimpleLockLockDisableInterrupt(ivars->lock);
- ivars->count++;
- ivars->time = (kIOInterruptSourceContinuousTime & ivars->flags)
- ? mach_continuous_time() : mach_absolute_time();
- if (ivars->waiter) {
- thread_wakeup_thread((event_t) ivars, ivars->waiter);
- ivars->waiter = NULL;
- }
- if (kIOInterruptTypeLevel & ivars->interruptType) {
- ivars->provider->disableInterrupt(ivars->intIndex);
- }
- IOSimpleLockUnlockEnableInterrupt(ivars->lock, is);
-}
-
-kern_return_t
-IOInterruptDispatchSource::Create_Impl(
- IOService * provider,
- uint32_t indexAndFlags,
- IODispatchQueue * queue,
- IOInterruptDispatchSource ** source)
-{
- IOReturn ret;
- IOInterruptDispatchSource * inst;
- uint32_t index;
- uint32_t flags;
-
- index = indexAndFlags & kIOInterruptSourceIndexMask;
- flags = indexAndFlags & ~kIOInterruptSourceIndexMask;
-
- inst = OSTypeAlloc(IOInterruptDispatchSource);
- if (!inst->init()) {
- inst->free();
- return kIOReturnNoMemory;
- }
-
- inst->ivars->lock = IOSimpleLockAlloc();
-
- ret = provider->getInterruptType(index, &inst->ivars->interruptType);
- if (kIOReturnSuccess != ret) {
- OSSafeReleaseNULL(inst);
- return ret;
- }
- ret = provider->registerInterrupt(index, inst, IOInterruptDispatchSourceInterrupt, inst->ivars);
- if (kIOReturnSuccess == ret) {
- inst->ivars->intIndex = index;
- inst->ivars->flags = flags;
- inst->ivars->provider = provider;
- inst->ivars->provider->retain();
- *source = inst;
- }
- return ret;
-}
-
-kern_return_t
-IOInterruptDispatchSource::GetInterruptType_Impl(
- IOService * provider,
- uint32_t index,
- uint64_t * interruptType)
-{
- IOReturn ret;
- int type;
-
- *interruptType = 0;
- ret = provider->getInterruptType(index, &type);
- if (kIOReturnSuccess == ret) {
- *interruptType = type;
- }
-
- return ret;
-}
-
-bool
-IOInterruptDispatchSource::init()
-{
- if (!super::init()) {
- return false;
- }
- ivars = IOMallocType(IOInterruptDispatchSource_IVars);
-
- return true;
-}
-
-void
-IOInterruptDispatchSource::free()
-{
- if (ivars && ivars->provider) {
- (void) ivars->provider->unregisterInterrupt(ivars->intIndex);
- ivars->provider->release();
- }
-
- if (ivars && ivars->lock) {
- IOSimpleLockFree(ivars->lock);
- }
-
- IOFreeType(ivars, IOInterruptDispatchSource_IVars);
-
- super::free();
-}
-
-kern_return_t
-IOInterruptDispatchSource::SetHandler_Impl(
- OSAction * action)
-{
- IOReturn ret;
- OSAction * oldAction;
-
- oldAction = (typeof(oldAction))ivars->action;
- if (oldAction && OSCompareAndSwapPtr(oldAction, NULL, &ivars->action)) {
- oldAction->release();
- }
- action->retain();
- ivars->action = action;
-
- ret = kIOReturnSuccess;
-
- return ret;
-}
-
-kern_return_t
-IOInterruptDispatchSource::SetEnableWithCompletion_Impl(
- bool enable,
- IODispatchSourceCancelHandler handler)
-{
- IOReturn ret;
- IOInterruptState is;
-
- if (enable == ivars->enable) {
- return kIOReturnSuccess;
- }
-
- if (ivars->canceled) {
- return kIOReturnUnsupported;
- }
- assert(ivars->provider != NULL);
-
- if (enable) {
- is = IOSimpleLockLockDisableInterrupt(ivars->lock);
- ivars->enable = enable;
- IOSimpleLockUnlockEnableInterrupt(ivars->lock, is);
- ret = ivars->provider->enableInterrupt(ivars->intIndex);
- } else {
- ret = ivars->provider->disableInterrupt(ivars->intIndex);
- is = IOSimpleLockLockDisableInterrupt(ivars->lock);
- ivars->enable = enable;
- IOSimpleLockUnlockEnableInterrupt(ivars->lock, is);
- }
-
- return ret;
-}
-
-kern_return_t
-IOInterruptDispatchSource::Cancel_Impl(
- IODispatchSourceCancelHandler handler)
-{
- IOInterruptState is;
- IOService * provider;
-
- is = IOSimpleLockLockDisableInterrupt(ivars->lock);
- ivars->canceled = true;
- if (ivars->waiter) {
- thread_wakeup_thread((event_t) ivars, ivars->waiter);
- ivars->waiter = NULL;
- }
- provider = ivars->provider;
- if (provider) {
- ivars->provider = NULL;
- }
- IOSimpleLockUnlockEnableInterrupt(ivars->lock, is);
-
- if (provider) {
- (void) provider->unregisterInterrupt(ivars->intIndex);
- provider->release();
- }
-
- return kIOReturnSuccess;
-}
-
-kern_return_t
-IOInterruptDispatchSource::CheckForWork_Impl(
- const IORPC rpc,
- bool synchronous)
-{
- IOReturn ret = kIOReturnNotReady;
- IOInterruptState is;
- bool willWait;
- bool canceled;
- wait_result_t waitResult;
- uint64_t icount;
- uint64_t itime;
- thread_t self;
-
- self = current_thread();
- icount = 0;
- do {
- willWait = false;
- is = IOSimpleLockLockDisableInterrupt(ivars->lock);
- canceled = ivars->canceled;
- if (!canceled) {
- if ((icount = ivars->count)) {
- itime = ivars->time;
- ivars->count = 0;
- waitResult = THREAD_AWAKENED;
- } else if (synchronous) {
- assert(NULL == ivars->waiter);
- ivars->waiter = self;
- waitResult = assert_wait((event_t) ivars, THREAD_INTERRUPTIBLE);
- }
- willWait = (synchronous && (waitResult == THREAD_WAITING));
- if (willWait && (kIOInterruptTypeLevel & ivars->interruptType) && ivars->enable) {
- IOSimpleLockUnlockEnableInterrupt(ivars->lock, is);
- ivars->provider->enableInterrupt(ivars->intIndex);
- } else {
- IOSimpleLockUnlockEnableInterrupt(ivars->lock, is);
- }
- } else {
- IOSimpleLockUnlockEnableInterrupt(ivars->lock, is);
- }
- if (willWait) {
- waitResult = thread_block(THREAD_CONTINUE_NULL);
- if (THREAD_INTERRUPTED == waitResult) {
- is = IOSimpleLockLockDisableInterrupt(ivars->lock);
- ivars->waiter = NULL;
- IOSimpleLockUnlockEnableInterrupt(ivars->lock, is);
- canceled = true;
- break;
- }
- }
- } while (synchronous && !icount && !canceled);
-
- if (icount && ivars->action) {
- ret = InterruptOccurred(rpc, ivars->action, icount, itime);
- }
-
- return ret;
-}
-
-void
-IOInterruptDispatchSource::InterruptOccurred_Impl(
- OSAction * action,
- uint64_t count,
- uint64_t time)
-{
-}
-
-kern_return_t
-IOInterruptDispatchSource::GetLastInterrupt_Impl(
- uint64_t * pCount,
- uint64_t * pTime)
-{
- IOInterruptState is;
- uint64_t count, time;
-
- is = IOSimpleLockLockDisableInterrupt(ivars->lock);
- count = ivars->count;
- time = ivars->time;
- IOSimpleLockUnlockEnableInterrupt(ivars->lock, is);
-
- if (pCount) {
- *pCount = count;
- }
- if (pTime) {
- *pTime = time;
- }
- return kIOReturnSuccess;
-}
-
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-
-enum {
- kIOServiceNotificationTypeCount = kIOServiceNotificationTypeLast + 1,
-};
-
-struct IOServiceNotificationDispatchSource_IVars {
- OSObject * serverName;
- OSAction * action;
- IOLock * lock;
- IONotifier * notifier;
- OSDictionary * interestNotifiers;
- OSBoundedArray<OSArray *, kIOServiceNotificationTypeCount> pending;
- bool enable;
-};
-
-kern_return_t
-IOServiceNotificationDispatchSource::Create_Impl(
- OSDictionary * matching,
- uint64_t options,
- IODispatchQueue * queue,
- IOServiceNotificationDispatchSource ** notification)
-{
- IOUserServer * us;
- IOReturn ret;
- IOServiceNotificationDispatchSource * inst;
-
- inst = OSTypeAlloc(IOServiceNotificationDispatchSource);
- if (!inst->init()) {
- OSSafeReleaseNULL(inst);
- return kIOReturnNoMemory;
- }
-
- us = (typeof(us))thread_iokit_tls_get(0);
- assert(OSDynamicCast(IOUserServer, us));
- if (!us) {
- OSSafeReleaseNULL(inst);
- return kIOReturnError;
- }
- inst->ivars->serverName = us->copyProperty(gIOUserServerNameKey);
- if (!inst->ivars->serverName) {
- OSSafeReleaseNULL(inst);
- return kIOReturnNoMemory;
- }
-
- inst->ivars->lock = IOLockAlloc();
- if (!inst->ivars->lock) {
- OSSafeReleaseNULL(inst);
- return kIOReturnNoMemory;
- }
- for (uint32_t idx = 0; idx < kIOServiceNotificationTypeCount; idx++) {
- inst->ivars->pending[idx] = OSArray::withCapacity(4);
- if (!inst->ivars->pending[idx]) {
- OSSafeReleaseNULL(inst);
- return kIOReturnNoMemory;
- }
- }
- inst->ivars->interestNotifiers = OSDictionary::withCapacity(4);
- if (!inst->ivars->interestNotifiers) {
- OSSafeReleaseNULL(inst);
- return kIOReturnNoMemory;
- }
-
- inst->ivars->notifier = IOService::addMatchingNotification(gIOMatchedNotification, matching, 0 /*priority*/,
- ^bool (IOService * newService, IONotifier * notifier) {
- bool notifyReady = false;
- IONotifier * interest = NULL;
- OSObject * serverName;
- bool okToUse;
-
- serverName = newService->copyProperty(gIOUserServerNameKey);
- okToUse = (serverName && inst->ivars->serverName->isEqualTo(serverName));
- OSSafeReleaseNULL(serverName);
- if (!okToUse) {
- OSObject * prop;
- OSObject * str;
-
- if (!newService->reserved->uvars || !newService->reserved->uvars->userServer) {
- return false;
- }
- str = OSString::withCStringNoCopy(kIODriverKitAllowsPublishEntitlementsKey);
- if (!str) {
- return false;
- }
- okToUse = newService->reserved->uvars->userServer->checkEntitlements(str, NULL, NULL);
- if (!okToUse) {
- if (kIODKLogSetup & gIODKDebug) {
- DKLOG(DKS ": publisher entitlements check failed\n", DKN(newService));
- }
- return false;
- }
- prop = newService->copyProperty(kIODriverKitPublishEntitlementsKey);
- if (!prop) {
- return false;
- }
- okToUse = us->checkEntitlements(prop, NULL, NULL);
- if (!okToUse) {
- if (kIODKLogSetup & gIODKDebug) {
- DKLOG(DKS ": subscriber entitlements check failed\n", DKN(newService));
- }
- return false;
- }
- }
-
- IOLockLock(inst->ivars->lock);
- notifyReady = (0 == inst->ivars->pending[kIOServiceNotificationTypeMatched]->getCount());
- inst->ivars->pending[kIOServiceNotificationTypeMatched]->setObject(newService);
- bool needInterest = (NULL == inst->ivars->interestNotifiers->getObject((const OSSymbol *) newService));
- IOLockUnlock(inst->ivars->lock);
-
- if (needInterest) {
- interest = newService->registerInterest(gIOGeneralInterest,
- ^IOReturn (uint32_t messageType, IOService * provider,
- void * messageArgument, size_t argSize) {
- IONotifier * interest;
- bool notifyReady = false;
-
- // after the notifier remove, IOServiceNotificationDispatchSource::free
- // will not wait for this code to complete
- if (!inst->taggedTryRetain(NULL)) {
- return kIOReturnSuccess;
- }
-
- switch (messageType) {
- case kIOMessageServiceIsTerminated:
- IOLockLock(inst->ivars->lock);
- notifyReady = (0 == inst->ivars->pending[kIOServiceNotificationTypeTerminated]->getCount());
- inst->ivars->pending[kIOServiceNotificationTypeTerminated]->setObject(provider);
- if (inst->ivars->interestNotifiers != NULL) {
- interest = (typeof(interest))inst->ivars->interestNotifiers->getObject((const OSSymbol *) newService);
- assert(interest);
- interest->remove();
- inst->ivars->interestNotifiers->removeObject((const OSSymbol *) newService);
- }
- IOLockUnlock(inst->ivars->lock);
- break;
- default:
- break;
- }
- if (notifyReady && inst->ivars->action) {
- inst->ServiceNotificationReady(inst->ivars->action);
- }
- inst->release();
- return kIOReturnSuccess;
- });
- }
- if (interest) {
- IOLockLock(inst->ivars->lock);
- inst->ivars->interestNotifiers->setObject((const OSSymbol *) newService, interest);
- IOLockUnlock(inst->ivars->lock);
- }
- if (notifyReady) {
- if (inst->ivars->action) {
- inst->ServiceNotificationReady(inst->ivars->action);
- }
- }
- return false;
- });
-
- if (!inst->ivars->notifier) {
- OSSafeReleaseNULL(inst);
- ret = kIOReturnError;
- }
-
- *notification = inst;
- ret = kIOReturnSuccess;
-
- return ret;
-}
-
-kern_return_t
-IOServiceNotificationDispatchSource::CopyNextNotification_Impl(
- uint64_t * type,
- IOService ** service,
- uint64_t * options)
-{
- IOService * next;
- uint32_t idx;
-
- IOLockLock(ivars->lock);
- for (idx = 0; idx < kIOServiceNotificationTypeCount; idx++) {
- next = (IOService *) ivars->pending[idx]->getObject(0);
- if (next) {
- next->retain();
- ivars->pending[idx]->removeObject(0);
- break;
- }
- }
- IOLockUnlock(ivars->lock);
-
- if (idx == kIOServiceNotificationTypeCount) {
- idx = kIOServiceNotificationTypeNone;
- }
- *type = idx;
- *service = next;
- *options = 0;
-
- return kIOReturnSuccess;
-}
-
-bool
-IOServiceNotificationDispatchSource::init()
-{
- if (!super::init()) {
- return false;
- }
- ivars = IOMallocType(IOServiceNotificationDispatchSource_IVars);
-
- return true;
-}
-
-void
-IOServiceNotificationDispatchSource::free()
-{
- if (ivars) {
- if (ivars->notifier) {
- ivars->notifier->remove();
- ivars->notifier = NULL;
- }
- if (ivars->interestNotifiers) {
- OSDictionary * savedInterestNotifiers = NULL;
-
- // the lock is always initialized first, so it should exist
- assert(ivars->lock);
-
- // Prevent additional changes to interestNotifiers
- IOLockLock(ivars->lock);
- savedInterestNotifiers = ivars->interestNotifiers;
- ivars->interestNotifiers = NULL;
- IOLockUnlock(ivars->lock);
-
- // Remove all interest notifiers
- savedInterestNotifiers->iterateObjects(^bool (const OSSymbol * key, OSObject * object) {
- IONotifier * interest = (typeof(interest))object;
- interest->remove();
- return false;
- });
- OSSafeReleaseNULL(savedInterestNotifiers);
- }
- for (uint32_t idx = 0; idx < kIOServiceNotificationTypeCount; idx++) {
- OSSafeReleaseNULL(ivars->pending[idx]);
- }
- OSSafeReleaseNULL(ivars->action);
- if (ivars->lock) {
- IOLockFree(ivars->lock);
- ivars->lock = NULL;
- }
- OSSafeReleaseNULL(ivars->serverName);
- IOFreeType(ivars, IOServiceNotificationDispatchSource_IVars);
- }
-
- super::free();
-}
-
-kern_return_t
-IOServiceNotificationDispatchSource::SetHandler_Impl(
- OSAction * action)
-{
- IOReturn ret;
- bool notifyReady;
-
- notifyReady = false;
-
- IOLockLock(ivars->lock);
- OSSafeReleaseNULL(ivars->action);
- action->retain();
- ivars->action = action;
- if (action) {
- for (uint32_t idx = 0; idx < kIOServiceNotificationTypeCount; idx++) {
- notifyReady = (ivars->pending[idx]->getCount());
- if (notifyReady) {
- break;
- }
- }
- }
- IOLockUnlock(ivars->lock);
-
- if (notifyReady) {
- ServiceNotificationReady(action);
- }
- ret = kIOReturnSuccess;
-
- return ret;
-}
-
-kern_return_t
-IOServiceNotificationDispatchSource::SetEnableWithCompletion_Impl(
- bool enable,
- IODispatchSourceCancelHandler handler)
-{
- if (enable == ivars->enable) {
- return kIOReturnSuccess;
- }
-
- IOLockLock(ivars->lock);
- ivars->enable = enable;
- IOLockUnlock(ivars->lock);
-
- return kIOReturnSuccess;
-}
-
-kern_return_t
-IOServiceNotificationDispatchSource::Cancel_Impl(
- IODispatchSourceCancelHandler handler)
-{
- return kIOReturnUnsupported;
-}
-
-kern_return_t
-IOServiceNotificationDispatchSource::CheckForWork_Impl(
- const IORPC rpc,
- bool synchronous)
-{
- return kIOReturnNotReady;
-}
-
-kern_return_t
-IOServiceNotificationDispatchSource::DeliverNotifications(IOServiceNotificationBlock block)
-{
- return kIOReturnUnsupported;
-}
-
-
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-
-OSDictionary *
-IOService::CreatePropertyMatchingDictionary(const char * key, OSObjectPtr value, OSDictionary * matching)
-{
- OSDictionary * result;
- const OSSymbol * keySym;
-
- keySym = OSSymbol::withCString(key);
- result = propertyMatching(keySym, (const OSObject *) value, matching);
- OSSafeReleaseNULL(keySym);
-
- return result;
-}
-
-OSDictionary *
-IOService::CreatePropertyMatchingDictionary(const char * key, const char * stringValue, OSDictionary * matching)
-{
- OSDictionary * result;
- OSString * value;
-
- value = OSString::withCString(stringValue);
- result = CreatePropertyMatchingDictionary(key, value, matching);
- OSSafeReleaseNULL(value);
-
- return result;
-}
-
-OSDictionary *
-IOService::CreateKernelClassMatchingDictionary(OSString * className, OSDictionary * matching)
-{
- if (!className) {
- return NULL;
- }
- if (!matching) {
- matching = OSDictionary::withCapacity(2);
- if (!matching) {
- return NULL;
- }
- }
- matching->setObject(kIOProviderClassKey, className);
-
- return matching;
-}
-
-OSDictionary *
-IOService::CreateKernelClassMatchingDictionary(const char * className, OSDictionary * matching)
-{
- OSDictionary * result;
- OSString * string;
-
- string = OSString::withCString(className);
- result = CreateKernelClassMatchingDictionary(string, matching);
- OSSafeReleaseNULL(string);
-
- return result;
-}
-
-OSDictionary *
-IOService::CreateUserClassMatchingDictionary(OSString * className, OSDictionary * matching)
-{
- return CreatePropertyMatchingDictionary(kIOUserClassKey, className, matching);
-}
-
-OSDictionary *
-IOService::CreateUserClassMatchingDictionary(const char * className, OSDictionary * matching)
-{
- return CreatePropertyMatchingDictionary(kIOUserClassKey, className, matching);
-}
-
-OSDictionary *
-IOService::CreateNameMatchingDictionary(OSString * serviceName, OSDictionary * matching)
-{
- if (!serviceName) {
- return NULL;
- }
- if (!matching) {
- matching = OSDictionary::withCapacity(2);
- if (!matching) {
- return NULL;
- }
- }
- matching->setObject(kIONameMatchKey, serviceName);
-
- return matching;
-}
-
-OSDictionary *
-IOService::CreateNameMatchingDictionary(const char * serviceName, OSDictionary * matching)
-{
- OSDictionary * result;
- OSString * string;
-
- string = OSString::withCString(serviceName);
- result = CreateNameMatchingDictionary(string, matching);
- OSSafeReleaseNULL(string);
-
- return result;
-}
-
-
-
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-
-kern_return_t
-IOUserServer::waitInterruptTrap(void * p1, void * p2, void * p3, void * p4, void * p5, void * p6)
-{
- IOReturn ret = kIOReturnBadArgument;
- IOInterruptState is;
- IOInterruptDispatchSource * interrupt;
- IOInterruptDispatchSource_IVars * ivars;
- IOInterruptDispatchSourcePayload payload;
-
- bool willWait;
- bool canceled;
- wait_result_t waitResult;
- thread_t self;
-
- OSObject * object;
-
- object = iokit_lookup_object_with_port_name((mach_port_name_t)(uintptr_t)p1, IKOT_UEXT_OBJECT, current_task());
-
- if (!object) {
- return kIOReturnBadArgument;
- }
- if (!(interrupt = OSDynamicCast(IOInterruptDispatchSource, object))) {
- ret = kIOReturnBadArgument;
- } else {
- self = current_thread();
- ivars = interrupt->ivars;
- payload.count = 0;
- do {
- willWait = false;
- is = IOSimpleLockLockDisableInterrupt(ivars->lock);
- canceled = ivars->canceled;
- if (!canceled) {
- if ((payload.count = ivars->count)) {
- payload.time = ivars->time;
- ivars->count = 0;
- waitResult = THREAD_AWAKENED;
- } else {
- assert(NULL == ivars->waiter);
- ivars->waiter = self;
- waitResult = assert_wait((event_t) ivars, THREAD_INTERRUPTIBLE);
- }
- willWait = (waitResult == THREAD_WAITING);
- if (willWait && (kIOInterruptTypeLevel & ivars->interruptType) && ivars->enable) {
- IOSimpleLockUnlockEnableInterrupt(ivars->lock, is);
- ivars->provider->enableInterrupt(ivars->intIndex);
- } else {
- IOSimpleLockUnlockEnableInterrupt(ivars->lock, is);
- }
- } else {
- IOSimpleLockUnlockEnableInterrupt(ivars->lock, is);
- }
- if (willWait) {
- waitResult = thread_block(THREAD_CONTINUE_NULL);
- if (THREAD_INTERRUPTED == waitResult) {
- is = IOSimpleLockLockDisableInterrupt(ivars->lock);
- ivars->waiter = NULL;
- IOSimpleLockUnlockEnableInterrupt(ivars->lock, is);
- canceled = true;
- break;
- }
- }
- } while (!payload.count && !canceled);
- ret = (payload.count ? kIOReturnSuccess : kIOReturnAborted);
- }
-
- if (kIOReturnSuccess == ret) {
- int copyerr = copyout(&payload, (user_addr_t) p2, sizeof(payload));
- if (copyerr) {
- ret = kIOReturnVMError;
- }
- }
-
- object->release();
-
- return ret;
-}
-
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-
-kern_return_t
-IOUserServer::Create_Impl(
- const char * name,
- uint64_t tag,
- uint64_t options,
- OSString * bundleID,
- IOUserServer ** server)
-{
- IOReturn ret;
- IOUserServer * us;
- const OSSymbol * sym;
- OSNumber * serverTag;
- io_name_t rname;
- OSKext * kext;
-
- us = (typeof(us))thread_iokit_tls_get(0);
- assert(OSDynamicCast(IOUserServer, us));
- if (kIODKLogSetup & gIODKDebug) {
- DKLOG(DKS "::Create(" DKS ") %p\n", DKN(us), name, tag, us);
- }
- if (!us) {
- return kIOReturnError;
- }
-
- if (bundleID) {
- kext = OSKext::lookupKextWithIdentifier(bundleID->getCStringNoCopy());
- if (kext) {
- us->setTaskLoadTag(kext);
- us->setDriverKitUUID(kext);
- us->setDriverKitStatistics(kext);
- OSKext::OSKextLogDriverKitInfoLoad(kext);
- OSSafeReleaseNULL(kext);
- } else {
- DKLOG(DKS "::Create(" DKS "): could not find OSKext for %s\n", DKN(us), name, tag, bundleID->getCStringNoCopy());
- }
-
- us->fAllocationName = kern_allocation_name_allocate(bundleID->getCStringNoCopy(), 0);
- assert(us->fAllocationName);
- }
-
- sym = OSSymbol::withCString(name);
- serverTag = OSNumber::withNumber(tag, 64);
-
- us->setProperty(gIOUserServerNameKey, (OSObject *) sym);
- us->setProperty(gIOUserServerTagKey, serverTag);
-
- serverTag->release();
- OSSafeReleaseNULL(sym);
-
- snprintf(rname, sizeof(rname), "IOUserServer(%s-0x%qx)", name, tag);
- us->setName(rname);
-
- us->retain();
- *server = us;
- ret = kIOReturnSuccess;
-
- return ret;
-}
-
-kern_return_t
-IOUserServer::Panic_Impl(const char * reason)
-{
- if (isPlatformDriver()) {
- if (strnlen(reason, kIOUserServrMaxPanicReasonLength) == kIOUserServrMaxPanicReasonLength) {
- // Invalid panic message, panic anyways
- panic("%s: dext requested panic", getName());
- } else {
- panic("%s: dext requested panic: \"%s\"", getName(), reason);
- }
- return kIOReturnSuccess;
- }
- return kIOReturnNotPermitted;
-}
-
-kern_return_t
-IOUserServer::RegisterService_Impl()
-{
- kern_return_t ret = IOService::RegisterService_Impl();
-
- return ret;
-}
-
-kern_return_t
-IOUserServer::Exit_Impl(
- const char * reason)
-{
- return kIOReturnUnsupported;
-}
-
-kern_return_t
-IOUserServer::LoadModule_Impl(
- const char * path)
-{
- return kIOReturnUnsupported;
-}
-
-
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-
-kern_return_t
-IODispatchQueue::Create_Impl(
- const char * name,
- uint64_t options,
- uint64_t priority,
- IODispatchQueue ** queue)
-{
- IODispatchQueue * result;
- IOUserServer * us;
-
- result = OSTypeAlloc(IODispatchQueue);
- if (!result) {
- return kIOReturnNoMemory;
- }
- if (!result->init()) {
- OSSafeReleaseNULL(result);
- return kIOReturnNoMemory;
- }
-
- *queue = result;
-
- if (!strcmp("Root", name)) {
- us = (typeof(us))thread_iokit_tls_get(0);
- assert(OSDynamicCast(IOUserServer, us));
- us->setRootQueue(result);
- }
-
- if (kIODKLogSetup & gIODKDebug) {
- DKLOG("IODispatchQueue::Create %s %p\n", name, result);
- }
-
- return kIOReturnSuccess;
-}
-
-kern_return_t
-IODispatchQueue::SetPort_Impl(
- mach_port_t port)
-{
- if (MACH_PORT_NULL != ivars->serverPort) {
- return kIOReturnNotReady;
- }
- ivars->serverPort = ipc_port_copy_send_mqueue(port);
- if (ivars->serverPort == MACH_PORT_NULL) {
- return kIOReturnBadArgument;
- }
- return kIOReturnSuccess;
-}
-
-bool
-IODispatchQueue::init()
-{
- ivars = IOMallocType(IODispatchQueue_IVars);
- ivars->queue = this;
-
- return true;
-}
-
-void
-IODispatchQueue::free()
-{
- if (ivars && ivars->serverPort) {
- ipc_port_release_send(ivars->serverPort);
- ivars->serverPort = MACH_PORT_NULL;
- }
- IOFreeType(ivars, IODispatchQueue_IVars);
- super::free();
-}
-
-bool
-IODispatchQueue::OnQueue()
-{
- return false;
-}
-
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-
-
-kern_return_t
-OSMetaClassBase::Dispatch(IORPC rpc)
-{
- return kIOReturnUnsupported;
-}
-
-kern_return_t
-OSMetaClassBase::Invoke(IORPC rpc)
-{
- IOReturn ret = kIOReturnUnsupported;
- OSMetaClassBase * object;
- OSAction * action;
- IOService * service;
- IOUserServer * us;
- IORPCMessage * message;
-
- assert(rpc.sendSize >= (sizeof(IORPCMessageMach) + sizeof(IORPCMessage)));
- message = rpc.kernelContent;
- if (!message) {
- return kIOReturnIPCError;
- }
- message->flags |= kIORPCMessageKernel;
-
- us = NULL;
- if (!(kIORPCMessageLocalHost & message->flags)) {
- us = OSDynamicCast(IOUserServer, this);
- if (!us) {
- IOEventLink * eventLink = NULL;
- IOWorkGroup * workgroup = NULL;
-
- if ((action = OSDynamicCast(OSAction, this))) {
- object = IOUserServer::target(action, message);
- } else {
- object = this;
- }
- if ((service = OSDynamicCast(IOService, object))
- && service->reserved->uvars) {
- // xxx other classes
- us = service->reserved->uvars->userServer;
- } else if (action) {
- us = action->ivars->userServer;
- } else if ((eventLink = OSDynamicCast(IOEventLink, object))) {
- us = eventLink->ivars->userServer;
- } else if ((workgroup = OSDynamicCast(IOWorkGroup, object))) {
- us = workgroup->ivars->userServer;
- }
- }
- }
- if (us) {
- message->flags |= kIORPCMessageRemote;
- ret = us->rpc(rpc);
- if (kIOReturnSuccess != ret) {
- if (kIODKLogIPC & gIODKDebug) {
- DKLOG("OSMetaClassBase::Invoke user 0x%x\n", ret);
- }
- }
- } else {
- if (kIODKLogIPC & gIODKDebug) {
- DKLOG("OSMetaClassBase::Invoke kernel %s 0x%qx\n", getMetaClass()->getClassName(), message->msgid);
- }
- void * prior = thread_iokit_tls_get(0);
- thread_iokit_tls_set(0, NULL);
- ret = Dispatch(rpc);
- thread_iokit_tls_set(0, prior);
- }
-
- return ret;
-}
-
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-
-struct IOPStrings {
- uint32_t dataSize;
- uint32_t count;
- const char strings[0];
-};
-
-kern_return_t
-OSUserMetaClass::Dispatch(IORPC rpc)
-{
- if (meta) {
- return const_cast<OSMetaClass *>(meta)->Dispatch(rpc);
- } else {
- return kIOReturnUnsupported;
- }
-}
-
-void
-OSUserMetaClass::free()
-{
- if (queueNames) {
- IOFreeData(queueNames, sizeof(IOPStrings) + queueNames->dataSize * sizeof(char));
- queueNames = NULL;
- }
- if (description) {
- IOFreeData(description, description->descriptionSize);
- description = NULL;
- }
- IODeleteData(methods, uint64_t, 2 * methodCount);
- if (meta) {
- meta->releaseMetaClass();
- }
- if (name) {
- name->release();
- }
- OSObject::free();
-}
-
-/*
- * Sets the loadTag of the associated OSKext
- * in the dext task.
- * NOTE: different instances of the same OSKext
- * (so same BounleID but different tasks)
- * will have the same loadTag.
- */
-void
-IOUserServer::setTaskLoadTag(OSKext *kext)
-{
- task_t owningTask;
- uint32_t loadTag, prev_taskloadTag;
-
- owningTask = this->fOwningTask;
- if (!owningTask) {
- printf("%s: fOwningTask not found\n", __FUNCTION__);
- return;
- }
-
- loadTag = kext->getLoadTag();
- prev_taskloadTag = set_task_loadTag(owningTask, loadTag);
- if (prev_taskloadTag) {
- printf("%s: found the task loadTag already set to %u (set to %u)\n",
- __FUNCTION__, prev_taskloadTag, loadTag);
- }
-}
-
-/*
- * Sets the OSKext uuid as the uuid of the userspace
- * dext executable.
- */
-void
-IOUserServer::setDriverKitUUID(OSKext *kext)
-{
- task_t task;
- proc_t p;
- uuid_t p_uuid, k_uuid;
- OSData *k_data_uuid;
- OSData *new_uuid;
- uuid_string_t uuid_string = "";
-
- task = this->fOwningTask;
- if (!task) {
- printf("%s: fOwningTask not found\n", __FUNCTION__);
- return;
- }
-
- p = (proc_t)(get_bsdtask_info(task));
- if (!p) {
- printf("%s: proc not found\n", __FUNCTION__);
- return;
- }
- proc_getexecutableuuid(p, p_uuid, sizeof(p_uuid));
-
- k_data_uuid = kext->copyUUID();
- if (k_data_uuid) {
- memcpy(&k_uuid, k_data_uuid->getBytesNoCopy(), sizeof(k_uuid));
- OSSafeReleaseNULL(k_data_uuid);
- if (uuid_compare(k_uuid, p_uuid) != 0) {
- printf("%s: uuid not matching\n", __FUNCTION__);
- }
- return;
- }
-
- uuid_unparse(p_uuid, uuid_string);
- new_uuid = OSData::withValue(p_uuid);
- kext->setDriverKitUUID(new_uuid);
-}
-
-void
-IOUserServer::setDriverKitStatistics(OSKext *kext)
-{
- OSDextStatistics * statistics = kext->copyDextStatistics();
- if (statistics == NULL) {
- panic("Kext %s was not a DriverKit OSKext", kext->getIdentifierCString());
- }
- fStatistics = statistics;
-}
-
-IOReturn
-IOUserServer::setCheckInToken(IOUserServerCheckInToken *token)
-{
- IOReturn ret = kIOReturnError;
- if (token != NULL && fCheckInToken == NULL) {
- token->retain();
- fCheckInToken = token;
- ret = fCheckInToken->complete();
- iokit_clear_registered_ports(fOwningTask);
- } else {
- printf("%s: failed to set check in token. token=%p, fCheckInToken=%p\n", __FUNCTION__, token, fCheckInToken);
- }
- return ret;
-}
-
-bool
-IOUserServer::serviceMatchesCheckInToken(IOUserServerCheckInToken *token)
-{
- if (token != NULL) {
- bool result = token == fCheckInToken;
- return result;
- } else {
- printf("%s: null check in token\n", __FUNCTION__);
- return false;
- }
-}
-
-// entitlements - dict of entitlements to check
-// prop - string - if present return true
-// - array of strings - if any present return true
-// - array of arrays of strings - in each leaf array all must be present
-// - if any top level array succeeds return true
-// consumes one reference of prop
-bool
-IOUserServer::checkEntitlements(
- OSDictionary * entitlements, OSObject * prop,
- IOService * provider, IOService * dext)
-{
- OSDictionary * matching;
-
- if (!prop) {
- return true;
- }
- if (!entitlements) {
- OSSafeReleaseNULL(prop);
- return false;
- }
-
- matching = NULL;
- if (dext) {
- matching = dext->dictionaryWithProperties();
- if (!matching) {
- OSSafeReleaseNULL(prop);
- return false;
- }
- }
-
- bool allPresent __block = false;
- prop->iterateObjects(^bool (OSObject * object) {
- allPresent = false;
- object->iterateObjects(^bool (OSObject * object) {
- OSString * string;
- OSObject * value;
- string = OSDynamicCast(OSString, object);
- value = entitlements->getObject(string);
- if (matching && value) {
- matching->setObject(string, value);
- }
- allPresent = (NULL != value);
- // early terminate if not found
- return !allPresent;
- });
- // early terminate if found
- return allPresent;
- });
-
- if (allPresent && matching && provider) {
- allPresent = provider->matchPropertyTable(matching);
- }
-
- OSSafeReleaseNULL(matching);
- OSSafeReleaseNULL(prop);
-
- return allPresent;
-}
-
-bool
-IOUserServer::checkEntitlements(OSObject * prop, IOService * provider, IOService * dext)
-{
- return checkEntitlements(fEntitlements, prop, provider, dext);
-}
-
-bool
-IOUserServer::checkEntitlements(IOService * provider, IOService * dext)
-{
- OSObject * prop;
- bool ok;
-
- if (!fOwningTask) {
- return false;
- }
-
- prop = provider->copyProperty(gIOServiceDEXTEntitlementsKey);
- ok = checkEntitlements(fEntitlements, prop, provider, dext);
- if (!ok) {
- DKLOG(DKS ": provider entitlements check failed\n", DKN(dext));
- }
- if (ok) {
- prop = dext->copyProperty(gIOServiceDEXTEntitlementsKey);
- ok = checkEntitlements(fEntitlements, prop, NULL, NULL);
- if (!ok) {
- DKLOG(DKS ": family entitlements check failed\n", DKN(dext));
- }
- }
-
- return ok;
-}
-
-IOReturn
-IOUserServer::exit(const char * reason)
-{
- DKLOG(DKS "::exit(%s)\n", DKN(this), reason);
- Exit(reason);
- return kIOReturnSuccess;
-}
-
-IOReturn
-IOUserServer::kill(const char * reason)
-{
- IOReturn ret = kIOReturnError;
- if (fOwningTask != NULL) {
- DKLOG(DKS"::kill(%s)\n", DKN(this), reason);
- task_bsdtask_kill(fOwningTask);
- ret = kIOReturnSuccess;
- }
- return ret;
-}
-
-void
-IOUserServer::emergencyPanicCoreDumpEnable()
-{
-#if DEVELOPMENT || DEBUG
- if (isPlatformDriver()) {
- // Enable coredump for the first party dext that is causing an imminent panic
- // This is enabled on non-release without requiring an entitlement,
- // so this coredump has only a generic name
- char core_name[MACH_CORE_FILEHEADER_NAMELEN];
- snprintf(core_name, sizeof(core_name), "dext-%d", pid_from_task(fOwningTask));
- kern_register_userspace_coredump(fOwningTask, core_name, TRUE);
- }
-#endif /* DEVELOPMENT || DEBUG */
-}
-
-OSObjectUserVars *
-IOUserServer::varsForObject(OSObject * obj)
-{
- IOService * service;
-
- if ((service = OSDynamicCast(IOService, obj))) {
- return service->reserved->uvars;
- }
-
- return NULL;
-}
-
-IOPStrings *
-IOUserServer::copyInStringArray(const char * string, uint32_t userSize)
-{
- IOPStrings * array;
- vm_size_t alloc;
- size_t len;
- const char * end;
- OSBoundedPtr<const char> cstr;
-
- if (userSize <= 1) {
- return NULL;
- }
-
- if (os_add_overflow(sizeof(IOPStrings), userSize, &alloc)) {
- assert(false);
- return NULL;
- }
- if (alloc > 16384) {
- assert(false);
- return NULL;
- }
- array = (typeof(array))IOMallocData(alloc);
- if (!array) {
- return NULL;
- }
- array->dataSize = userSize;
- bcopy(string, (void *) &array->strings[0], userSize);
-
- array->count = 0;
- end = &array->strings[array->dataSize];
- cstr = OSBoundedPtr<const char>(&array->strings[0], &array->strings[0], end);
- while ((len = (unsigned char)cstr[0])) {
- cstr++;
- if ((cstr + len) >= end) {
- break;
- }
- cstr += len;
- array->count++;
- }
- if (len) {
- IOFreeData(array, alloc);
- array = NULL;
- }
-
- return array;
-}
-
-uint32_t
-IOUserServer::stringArrayIndex(IOPStrings * array, const char * look)
-{
- uint32_t idx;
- size_t len, llen;
- OSBoundedPtr<const char> cstr;
- const char * end;
-
- idx = 0;
- end = &array->strings[array->dataSize];
- cstr = OSBoundedPtr<const char>(&array->strings[0], &array->strings[0], end);
-
- llen = strlen(look);
- while ((len = (unsigned char)cstr[0])) {
- cstr++;
- if ((cstr + len) >= end) {
- break;
- }
- if ((len == llen) && !strncmp(cstr.discard_bounds(), look, len)) {
- return idx;
- }
- cstr += len;
- idx++;
- }
-
- return -1U;
-}
-#define kIODispatchQueueStopped ((IODispatchQueue *) -1L)
-
-IODispatchQueue *
-IOUserServer::queueForObject(OSObject * obj, uint64_t msgid)
-{
- IODispatchQueue * queue;
- OSObjectUserVars * uvars;
- uint64_t option;
-
- uvars = varsForObject(obj);
- if (!uvars) {
- return NULL;
- }
- if (!uvars->queueArray) {
- if (uvars->stopped) {
- return kIODispatchQueueStopped;
- }
- return NULL;
- }
- queue = uvars->queueArray[0];
-
- if (uvars->userMeta
- && uvars->userMeta->methods) {
- uint32_t idx, baseIdx;
- uint32_t lim;
- // bsearch
- for (baseIdx = 0, lim = uvars->userMeta->methodCount; lim; lim >>= 1) {
- idx = baseIdx + (lim >> 1);
- if (msgid == uvars->userMeta->methods[idx]) {
- option = uvars->userMeta->methods[uvars->userMeta->methodCount + idx];
- option &= 0xFF;
- if (option < uvars->userMeta->queueNames->count) {
- queue = uvars->queueArray[option + 1];
- }
- break;
- } else if (msgid > uvars->userMeta->methods[idx]) {
- // move right
- baseIdx += (lim >> 1) + 1;
- lim--;
- }
- // else move left
- }
- }
- return queue;
-}
-
-IOReturn
-IOUserServer::objectInstantiate(OSObject * obj, IORPC rpc, IORPCMessage * message)
-{
- IOReturn ret;
- OSString * str;
- OSObject * prop;
- IOService * service;
-
- OSAction * action;
- OSObject * target;
- uint32_t queueCount, queueAlloc;
- const char * resultClassName;
- uint64_t resultFlags;
-
- mach_msg_size_t replySize;
- uint32_t methodCount;
- const uint64_t * methods;
- IODispatchQueue * queue;
- OSUserMetaClass * userMeta;
- OSObjectUserVars * uvars;
- uint32_t idx;
- ipc_port_t sendPort;
- bool serviceInactive;
-
- OSObject_Instantiate_Rpl_Content * reply;
- IODispatchQueue ** unboundedQueueArray = NULL;
- queueCount = 0;
- methodCount = 0;
- methods = NULL;
- str = NULL;
- prop = NULL;
- userMeta = NULL;
- resultClassName = NULL;
- resultFlags = 0;
- ret = kIOReturnUnsupportedMode;
-
- service = OSDynamicCast(IOService, obj);
- action = OSDynamicCast(OSAction, obj);
- if (!service) {
- // xxx other classes hosted
- resultFlags |= kOSObjectRPCKernel;
- resultFlags |= kOSObjectRPCRemote;
- } else {
- serviceInactive = false;
- if (service->lockForArbitration()) {
- if (service->isInactive() && (service->__state[1] & kIOServiceStartState) == 0) {
- serviceInactive = true;
- }
- service->unlockForArbitration();
- }
- if (serviceInactive) {
- DKLOG(DKS "::instantiate inactive\n", DKN(service));
- return kIOReturnOffline;
- }
- prop = service->copyProperty(gIOUserClassKey);
- str = OSDynamicCast(OSString, prop);
- if (!service->reserved->uvars) {
- resultFlags |= kOSObjectRPCRemote;
- resultFlags |= kOSObjectRPCKernel;
- } else if (this != service->reserved->uvars->userServer) {
- // remote, use base class
- resultFlags |= kOSObjectRPCRemote;
- }
- if (service->reserved->uvars && service->reserved->uvars->userServer) {
- if (!str) {
- DKLOG("no IOUserClass defined for " DKS "\n", DKN(service));
- OSSafeReleaseNULL(prop);
- return kIOReturnError;
- }
- IOLockLock(service->reserved->uvars->userServer->fLock);
- service->reserved->uvars->instantiated = true;
- userMeta = (typeof(userMeta))service->reserved->uvars->userServer->fClasses->getObject(str);
- IOLockUnlock(service->reserved->uvars->userServer->fLock);
- }
- }
- if (!str && !userMeta) {
- const OSMetaClass * meta;
- meta = obj->getMetaClass();
- IOLockLock(fLock);
- if (action) {
- str = action->ivars->typeName;
- if (str) {
- userMeta = (typeof(userMeta))fClasses->getObject(str);
- }
- }
- while (meta && !userMeta) {
- str = (OSString *) meta->getClassNameSymbol();
- userMeta = (typeof(userMeta))fClasses->getObject(str);
- if (!userMeta) {
- meta = meta->getSuperClass();
- }
- }
- IOLockUnlock(fLock);
- }
- if (str) {
- if (!userMeta) {
- IOLockLock(fLock);
- userMeta = (typeof(userMeta))fClasses->getObject(str);
- IOLockUnlock(fLock);
- }
- if (kIODKLogSetup & gIODKDebug) {
- DKLOG("userMeta %s %p\n", str->getCStringNoCopy(), userMeta);
- }
- if (userMeta) {
- if (kOSObjectRPCRemote & resultFlags) {
- if (!action) {
- /* Special case: For OSAction subclasses, do not use the superclass */
- while (userMeta && !(kOSClassCanRemote & userMeta->description->flags)) {
- userMeta = userMeta->superMeta;
- }
- }
- if (userMeta) {
- resultClassName = userMeta->description->name;
- ret = kIOReturnSuccess;
- }
- } else {
- service->reserved->uvars->userMeta = userMeta;
- queueAlloc = 1;
- if (userMeta->queueNames) {
- queueAlloc += userMeta->queueNames->count;
- }
- unboundedQueueArray = IONewZero(IODispatchQueue *, queueAlloc);
- service->reserved->uvars->queueArray =
- OSBoundedArrayRef<IODispatchQueue *>(unboundedQueueArray, queueAlloc);
- resultClassName = str->getCStringNoCopy();
- ret = kIOReturnSuccess;
- }
- } else if (kIODKLogSetup & gIODKDebug) {
- DKLOG("userMeta %s was not found in fClasses\n", str->getCStringNoCopy());
- IOLockLock(fLock);
- fClasses->iterateObjects(^bool (const OSSymbol * key, OSObject * val) {
- DKLOG(" fClasses[\"%s\"] => %p\n", key->getCStringNoCopy(), val);
- return false;
- });
- IOLockUnlock(fLock);
- }
- }
- OSSafeReleaseNULL(prop);
-
- IORPCMessageMach * machReply = rpc.reply;
- replySize = sizeof(OSObject_Instantiate_Rpl);
-
- if ((kIOReturnSuccess == ret) && (kOSObjectRPCRemote & resultFlags)) {
- target = obj;
- if (action) {
- if (action->ivars->referenceSize) {
- resultFlags |= kOSObjectRPCKernel;
- } else {
- resultFlags &= ~kOSObjectRPCKernel;
- if (action->ivars->target) {
- target = action->ivars->target;
- queueCount = 1;
- queue = queueForObject(target, action->ivars->targetmsgid);
- if (!queue && action->ivars->userServer) {
- queue = action->ivars->userServer->fRootQueue;
- }
- idx = 0;
- sendPort = NULL;
- if (queue && (kIODispatchQueueStopped != queue)) {
- sendPort = ipc_port_copy_send_mqueue(queue->ivars->serverPort);
- }
- replySize = sizeof(OSObject_Instantiate_Rpl)
- + queueCount * sizeof(machReply->objects[0])
- + 2 * methodCount * sizeof(reply->methods[0]);
- if (replySize > rpc.replySize) {
- assert(false);
- return kIOReturnIPCError;
- }
- machReply->objects[idx].type = MACH_MSG_PORT_DESCRIPTOR;
- machReply->objects[idx].disposition = MACH_MSG_TYPE_MOVE_SEND;
- machReply->objects[idx].name = sendPort;
- machReply->objects[idx].pad2 = 0;
- machReply->objects[idx].pad_end = 0;
- }
- }
- } else {
- uvars = varsForObject(target);
- if (uvars && uvars->userMeta) {
- queueCount = 1;
- if (uvars->userMeta->queueNames) {
- queueCount += uvars->userMeta->queueNames->count;
- }
- methods = &uvars->userMeta->methods[0];
- methodCount = uvars->userMeta->methodCount;
- replySize = sizeof(OSObject_Instantiate_Rpl)
- + queueCount * sizeof(machReply->objects[0])
- + 2 * methodCount * sizeof(reply->methods[0]);
- if (replySize > rpc.replySize) {
- assert(false);
- return kIOReturnIPCError;
- }
- for (idx = 0; idx < queueCount; idx++) {
- queue = uvars->queueArray[idx];
- sendPort = NULL;
- if (queue) {
- sendPort = ipc_port_copy_send_mqueue(queue->ivars->serverPort);
- }
- machReply->objects[idx].type = MACH_MSG_PORT_DESCRIPTOR;
- machReply->objects[idx].disposition = MACH_MSG_TYPE_MOVE_SEND;
- machReply->objects[idx].name = sendPort;
- machReply->objects[idx].pad2 = 0;
- machReply->objects[idx].pad_end = 0;
- }
- }
- }
- }
-
- if (kIODKLogIPC & gIODKDebug) {
- DKLOG("instantiate object %s with user class %s\n", obj->getMetaClass()->getClassName(), str ? str->getCStringNoCopy() : "(null)");
- }
-
- if (kIOReturnSuccess != ret) {
- DKLOG("%s: no user class found\n", str ? str->getCStringNoCopy() : obj->getMetaClass()->getClassName());
- resultClassName = "unknown";
- }
-
- machReply->msgh.msgh_id = kIORPCVersionCurrentReply;
- machReply->msgh.msgh_size = replySize;
- machReply->msgh_body.msgh_descriptor_count = queueCount;
-
- reply = (typeof(reply))IORPCMessageFromMachReply(machReply);
- if (!reply) {
- return kIOReturnIPCError;
- }
- if (methodCount) {
- bcopy(methods, &reply->methods[0], methodCount * 2 * sizeof(reply->methods[0]));
- }
- reply->__hdr.msgid = OSObject_Instantiate_ID;
- reply->__hdr.flags = kIORPCMessageOneway;
- reply->__hdr.objectRefs = 0;
- reply->__pad = 0;
- reply->flags = resultFlags;
- strlcpy(reply->classname, resultClassName, sizeof(reply->classname));
- reply->__result = ret;
-
- ret = kIOReturnSuccess;
-
- return ret;
-}
-
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-
-IOReturn
-IOUserServer::kernelDispatch(OSObject * obj, IORPC rpc)
-{
- IOReturn ret;
- IORPCMessage * message;
-
- message = rpc.kernelContent;
- if (!message) {
- return kIOReturnIPCError;
- }
-
- if (OSObject_Instantiate_ID == message->msgid) {
- ret = objectInstantiate(obj, rpc, message);
- if (kIOReturnSuccess != ret) {
- DKLOG(DKS ": %s: instantiate failed 0x%x\n", DKN(this), obj->getMetaClass()->getClassName(), ret);
- }
- } else {
- if (kIODKLogIPC & gIODKDebug) {
- DKLOG(DKS ": %s::Dispatch kernel 0x%qx\n", DKN(this), obj->getMetaClass()->getClassName(), message->msgid);
- }
- ret = obj->Dispatch(rpc);
- if (kIODKLogIPC & gIODKDebug) {
- DKLOG(DKS ": %s::Dispatch kernel 0x%qx result 0x%x\n", DKN(this), obj->getMetaClass()->getClassName(), message->msgid, ret);
- }
- }
-
- return ret;
-}
-
-
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-
-OSObject *
-IOUserServer::target(OSAction * action, IORPCMessage * message)
-{
- OSObject * object;
-
- if (message->msgid != action->ivars->msgid) {
- return action;
- }
- object = action->ivars->target;
- if (!object) {
- return action;
- }
- message->msgid = action->ivars->targetmsgid;
- message->objects[0] = (OSObjectRef) object;
- if (kIORPCMessageRemote & message->flags) {
- object->retain();
-#ifndef __clang_analyzer__
- // Hide the release of 'action' from the clang static analyzer to suppress
- // an overrelease diagnostic. The analyzer doesn't have a way to express the
- // non-standard contract of this method, which is that it releases 'action' when
- // the message flags have kIORPCMessageRemote set.
- action->release();
-#endif
- }
- if (kIODKLogIPC & gIODKDebug) {
- DKLOG("TARGET %s msg 0x%qx from 0x%qx\n", object->getMetaClass()->getClassName(), message->msgid, action->ivars->msgid);
- }
-
- return object;
-}
-
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-
-kern_return_t
-uext_server(ipc_port_t receiver, ipc_kmsg_t requestkmsg, ipc_kmsg_t * pReply)
-{
- kern_return_t ret;
- OSObject * object;
- IOUserServer * server;
-
- object = IOUserServer::copyObjectForSendRight(receiver, IKOT_UEXT_OBJECT);
- server = OSDynamicCast(IOUserServer, object);
- if (!server) {
- OSSafeReleaseNULL(object);
- return KERN_INVALID_NAME;
- }
-
- IORPCMessage * message = (typeof(message))ikm_udata_from_header(requestkmsg);
-
- ret = server->server(requestkmsg, message, pReply);
- object->release();
-
- return ret;
-}
-
-/*
- * Chosen to hit kalloc zones (as opposed to the VM).
- * doesn't include the trailer size which ipc_kmsg_alloc() will add
- */
-#define MAX_UEXT_REPLY_SIZE 0x17c0
-static_assert(MAX_UEXT_REPLY_SIZE + MAX_TRAILER_SIZE <= KALLOC_SAFE_ALLOC_SIZE);
-
-kern_return_t
-IOUserServer::server(ipc_kmsg_t requestkmsg, IORPCMessage * message, ipc_kmsg_t * pReply)
-{
- kern_return_t ret;
- mach_msg_size_t replyAlloc;
- ipc_kmsg_t replykmsg;
- IORPCMessageMach * msgin;
- IORPCMessageMach * msgout;
- IORPCMessage * reply;
- uint32_t replySize;
- OSObject * object;
- OSAction * action;
- bool oneway;
- uint64_t msgid;
-
- msgin = (typeof(msgin))ikm_header(requestkmsg);
- replyAlloc = 0;
- msgout = NULL;
- replykmsg = NULL;
-
- if (msgin->msgh.msgh_size < (sizeof(IORPCMessageMach) + sizeof(IORPCMessage))) {
- if (kIODKLogIPC & gIODKDebug) {
- DKLOG("UEXT notify %o\n", msgin->msgh.msgh_id);
- }
- return KERN_NOT_SUPPORTED;
- }
-
- if (!(MACH_MSGH_BITS_COMPLEX & msgin->msgh.msgh_bits)) {
- msgin->msgh_body.msgh_descriptor_count = 0;
- }
- if (!message) {
- return kIOReturnIPCError;
- }
- if (message->objectRefs == 0) {
- return kIOReturnIPCError;
- }
- ret = copyInObjects(msgin, message, msgin->msgh.msgh_size, true, false);
- if (kIOReturnSuccess != ret) {
- if (kIODKLogIPC & gIODKDebug) {
- DKLOG("UEXT copyin(0x%x) %x\n", ret, msgin->msgh.msgh_id);
- }
- // release objects and ports
- consumeObjects(msgin, message, msgin->msgh.msgh_size);
- copyInObjects(msgin, message, msgin->msgh.msgh_size, false, true);
- return KERN_NOT_SUPPORTED;
- }
-
- if (msgin->msgh_body.msgh_descriptor_count < 1) {
- return KERN_NOT_SUPPORTED;
- }
- object = (OSObject *) message->objects[0];
- msgid = message->msgid;
- message->flags &= ~kIORPCMessageKernel;
- message->flags |= kIORPCMessageRemote;
-
- if ((action = OSDynamicCast(OSAction, object))) {
- object = target(action, message);
- msgid = message->msgid;
- }
-
- oneway = (0 != (kIORPCMessageOneway & message->flags));
- assert(oneway || (MACH_PORT_NULL != msgin->msgh.msgh_local_port));
-
- replyAlloc = oneway ? 0 : MAX_UEXT_REPLY_SIZE;
-
-
-
-
- if (replyAlloc) {
- /*
- * Same as:
- * ipc_kmsg_alloc(MAX_UEXT_REPLY_SIZE_MACH, MAX_UEXT_REPLY_SIZE_MESSAGE,
- * IPC_KMSG_ALLOC_KERNEL | IPC_KMSG_ALLOC_ZERO | IPC_KMSG_ALLOC_LINEAR |
- * IPC_KMSG_ALLOC_NOFAIL);
- */
- replykmsg = ipc_kmsg_alloc_uext_reply(MAX_UEXT_REPLY_SIZE);
- msgout = (typeof(msgout))ikm_header(replykmsg);
- }
-
- IORPC rpc = { .message = msgin, .reply = msgout, .sendSize = msgin->msgh.msgh_size, .replySize = replyAlloc, .kernelContent = message };
-
- if (object) {
- kern_allocation_name_t prior;
- bool setAllocationName;
-
- setAllocationName = (NULL != fAllocationName);
- if (setAllocationName) {
- prior = thread_set_allocation_name(fAllocationName);
- }
- thread_iokit_tls_set(0, this);
- ret = kernelDispatch(object, rpc);
- thread_iokit_tls_set(0, NULL);
- if (setAllocationName) {
- thread_set_allocation_name(prior);
- }
- } else {
- ret = kIOReturnBadArgument;
- }
-
- // release objects
- consumeObjects(msgin, message, msgin->msgh.msgh_size);
-
- // release ports
- copyInObjects(msgin, message, msgin->msgh.msgh_size, false, true);
-
- if (!oneway) {
- if (kIOReturnSuccess == ret) {
- replySize = msgout->msgh.msgh_size;
- reply = IORPCMessageFromMachReply(msgout);
- if (!reply) {
- ret = kIOReturnIPCError;
- } else {
- ret = copyOutObjects(msgout, reply, replySize, (kIORPCVersionCurrentReply == msgout->msgh.msgh_id) /* =>!InvokeReply */);
- }
- }
- if (kIOReturnSuccess != ret) {
- IORPCMessageErrorReturnContent * errorMsg;
-
- msgout->msgh_body.msgh_descriptor_count = 0;
- msgout->msgh.msgh_id = kIORPCVersionCurrentReply;
- errorMsg = (typeof(errorMsg))IORPCMessageFromMachReply(msgout);
- errorMsg->hdr.msgid = message->msgid;
- errorMsg->hdr.flags = kIORPCMessageOneway | kIORPCMessageError;
- errorMsg->hdr.objectRefs = 0;
- errorMsg->result = ret;
- errorMsg->pad = 0;
- replySize = sizeof(IORPCMessageErrorReturn);
- }
-
- msgout->msgh.msgh_bits = MACH_MSGH_BITS_COMPLEX |
- MACH_MSGH_BITS_SET(MACH_MSGH_BITS_LOCAL(msgin->msgh.msgh_bits) /*remote*/, 0 /*local*/, 0, 0);
-
- msgout->msgh.msgh_remote_port = msgin->msgh.msgh_local_port;
- msgout->msgh.msgh_local_port = MACH_PORT_NULL;
- msgout->msgh.msgh_voucher_port = (mach_port_name_t) 0;
- msgout->msgh.msgh_reserved = 0;
- msgout->msgh.msgh_size = replySize;
- }
-
- *pReply = replykmsg;
- return KERN_SUCCESS;
-}
-
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-
-static inline uint32_t
-MAX_OBJECT_COUNT(IORPCMessageMach *mach, size_t size, IORPCMessage *message __unused)
-{
- assert(mach->msgh.msgh_size == size);
- size_t used_size;
- size_t remaining_size;
- if (os_mul_and_add_overflow(
- mach->msgh_body.msgh_descriptor_count,
- sizeof(mach_msg_port_descriptor_t),
- sizeof(mach->msgh) + sizeof(mach->msgh_body) + offsetof(IORPCMessage, objects[0]),
- &used_size)) {
- return 0;
- }
- if (os_sub_overflow(size, used_size, &remaining_size)) {
- return 0;
- }
- return (uint32_t)(remaining_size / sizeof(OSObjectRef));
-}
-
-#pragma pack(push, 4)
-struct UEXTTrapReply {
- uint64_t replySize;
- IORPCMessage replyMessage;
-};
-#pragma pack(pop)
-
-kern_return_t
-IOUserServerUEXTTrap(OSObject * object, void * p1, void * p2, void * p3, void * p4, void * p5, void * p6)
-{
- const user_addr_t msg = (uintptr_t) p1;
- size_t inSize = (uintptr_t) p2;
- user_addr_t out = (uintptr_t) p3;
- size_t outSize = (uintptr_t) p4;
- mach_port_name_t objectName1 = (mach_port_name_t)(uintptr_t) p5;
- size_t totalSize;
- OSObject * objectArg1;
-
- IORPCMessageMach * mach;
- mach_msg_port_descriptor_t * descs;
-
-#pragma pack(4)
- struct {
- uint32_t pad;
- IORPCMessageMach mach;
- mach_msg_port_descriptor_t objects[2];
- IOTrapMessageBuffer buffer;
- } buffer;
-#pragma pack()
-
- IOReturn ret;
- OSAction * action;
- int copyerr;
- IORPCMessage * message;
- IORPCMessage * reply;
- IORPC rpc;
- uint64_t refs;
- uint32_t maxObjectCount;
- size_t copySize;
- UEXTTrapReply * replyHdr;
- uintptr_t p;
-
- bzero(&buffer, sizeof(buffer));
-
- p = (typeof(p)) & buffer.buffer[0];
- if (os_add_overflow(inSize, outSize, &totalSize)) {
- return kIOReturnMessageTooLarge;
- }
- if (totalSize > sizeof(buffer.buffer)) {
- return kIOReturnMessageTooLarge;
- }
- if (inSize < sizeof(IORPCMessage)) {
- return kIOReturnIPCError;
- }
- copyerr = copyin(msg, &buffer.buffer[0], inSize);
- if (copyerr) {
- return kIOReturnVMError;
- }
-
- message = (typeof(message))p;
- refs = message->objectRefs;
- if ((refs > 2) || !refs) {
- return kIOReturnUnsupported;
- }
- if (!(kIORPCMessageSimpleReply & message->flags)) {
- return kIOReturnUnsupported;
- }
- message->flags &= ~(kIORPCMessageKernel | kIORPCMessageRemote);
-
- descs = (typeof(descs))(p - refs * sizeof(*descs));
- mach = (typeof(mach))(p - refs * sizeof(*descs) - sizeof(*mach));
-
- mach->msgh.msgh_id = kIORPCVersionCurrent;
- mach->msgh.msgh_size = (mach_msg_size_t) (sizeof(IORPCMessageMach) + refs * sizeof(*descs) + inSize); // totalSize was checked
- mach->msgh_body.msgh_descriptor_count = ((mach_msg_size_t) refs);
-
- rpc.message = mach;
- rpc.sendSize = mach->msgh.msgh_size;
- rpc.reply = (IORPCMessageMach *) (p + inSize);
- rpc.replySize = ((uint32_t) (sizeof(buffer.buffer) - inSize)); // inSize was checked
- rpc.kernelContent = message;
-
- message->objects[0] = 0;
- if ((action = OSDynamicCast(OSAction, object))) {
- maxObjectCount = MAX_OBJECT_COUNT(rpc.message, rpc.sendSize, message);
- if (refs > maxObjectCount) {
- return kIOReturnBadArgument;
- }
- if (refs < 2) {
- DKLOG("invalid refs count %qd in message id 0x%qx\n", refs, message->msgid);
- return kIOReturnBadArgument;
- }
- object = IOUserServer::target(action, message);
- message->objects[1] = (OSObjectRef) action;
- if (kIODKLogIPC & gIODKDebug) {
- DKLOG("%s::Dispatch(trap) kernel 0x%qx\n", object->getMetaClass()->getClassName(), message->msgid);
- }
- ret = object->Dispatch(rpc);
- } else {
- objectArg1 = NULL;
- if (refs > 1) {
- if (objectName1) {
- objectArg1 = iokit_lookup_uext_ref_current_task(objectName1);
- if (!objectArg1) {
- return kIOReturnIPCError;
- }
- }
- message->objects[1] = (OSObjectRef) objectArg1;
- }
- if (kIODKLogIPC & gIODKDebug) {
- DKLOG("%s::Dispatch(trap) kernel 0x%qx\n", object->getMetaClass()->getClassName(), message->msgid);
- }
- ret = object->Dispatch(rpc);
- if (kIODKLogIPC & gIODKDebug) {
- DKLOG("%s::Dispatch(trap) kernel 0x%qx 0x%x\n", object->getMetaClass()->getClassName(), message->msgid, ret);
- }
- OSSafeReleaseNULL(objectArg1);
-
- if (kIOReturnSuccess == ret) {
- if (rpc.reply->msgh_body.msgh_descriptor_count) {
- return kIOReturnIPCError;
- }
- reply = IORPCMessageFromMachReply(rpc.reply);
- if (!reply) {
- return kIOReturnIPCError;
- }
- copySize = rpc.reply->msgh.msgh_size - (((uintptr_t) reply) - ((uintptr_t) rpc.reply)) + sizeof(uint64_t);
- if (copySize > outSize) {
- return kIOReturnIPCError;
- }
- replyHdr = (UEXTTrapReply *) ((uintptr_t)reply - sizeof(uint64_t));
- replyHdr->replySize = copySize;
- copyerr = copyout(replyHdr, out, copySize);
- if (copyerr) {
- return kIOReturnVMError;
- }
- }
- }
-
- return ret;
-}
-
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-
-IOReturn
-IOUserServer::rpc(IORPC rpc)
-{
- if (isInactive() && !fRootQueue) {
- return kIOReturnOffline;
- }
-
- IOReturn ret;
- IORPCMessage * message;
- IORPCMessageMach * mach;
- mach_msg_id_t machid;
- uint32_t sendSize, replySize;
- bool oneway;
- uint64_t msgid;
- IODispatchQueue * queue;
- IOService * service;
- ipc_port_t port;
- ipc_port_t sendPort;
-
- queue = NULL;
- port = NULL;
- sendPort = NULL;
-
- mach = rpc.message;
- sendSize = rpc.sendSize;
- replySize = rpc.replySize;
-
- assert(sendSize >= (sizeof(IORPCMessageMach) + sizeof(IORPCMessage)));
-
- message = rpc.kernelContent;
- if (!message) {
- return kIOReturnIPCError;
- }
- msgid = message->msgid;
- machid = (msgid >> 32);
-
- if (mach->msgh_body.msgh_descriptor_count < 1) {
- return kIOReturnNoMedia;
- }
-
- IOLockLock(gIOUserServerLock);
- if ((service = OSDynamicCast(IOService, (OSObject *) message->objects[0]))) {
- queue = queueForObject(service, msgid);
- }
- if (!queue) {
- queue = fRootQueue;
- }
- if (queue && (kIODispatchQueueStopped != queue)) {
- port = queue->ivars->serverPort;
- }
- if (port) {
- sendPort = ipc_port_copy_send_mqueue(port);
- }
- IOLockUnlock(gIOUserServerLock);
- if (!sendPort) {
- return kIOReturnNotReady;
- }
-
- oneway = (0 != (kIORPCMessageOneway & message->flags));
-
- ret = copyOutObjects(mach, message, sendSize, false);
-
- mach->msgh.msgh_bits = MACH_MSGH_BITS_COMPLEX |
- MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, (oneway ? 0 : MACH_MSG_TYPE_MAKE_SEND_ONCE));
- mach->msgh.msgh_remote_port = sendPort;
- mach->msgh.msgh_local_port = (oneway ? MACH_PORT_NULL : mig_get_reply_port());
- mach->msgh.msgh_id = kIORPCVersionCurrent;
- mach->msgh.msgh_reserved = 0;
-
- boolean_t message_moved;
-
- if (oneway) {
- ret = kernel_mach_msg_send(&mach->msgh, sendSize,
- MACH_SEND_KERNEL_DEFAULT, 0, &message_moved);
- } else {
- assert(replySize >= (sizeof(IORPCMessageMach) + sizeof(IORPCMessage)));
- ret = kernel_mach_msg_rpc(&mach->msgh, sendSize, replySize, FALSE, &message_moved);
- }
-
- ipc_port_release_send(sendPort);
-
- if (MACH_MSG_SUCCESS != ret) {
- if (kIODKLogIPC & gIODKDebug) {
- DKLOG("mach_msg() failed 0x%x\n", ret);
- }
- if (!message_moved) {
- // release ports
- copyInObjects(mach, message, sendSize, false, true);
- }
- }
-
- if ((KERN_SUCCESS == ret) && !oneway) {
- if (kIORPCVersionCurrentReply != mach->msgh.msgh_id) {
- ret = (MACH_NOTIFY_SEND_ONCE == mach->msgh.msgh_id) ? MIG_SERVER_DIED : MIG_REPLY_MISMATCH;
- } else if ((replySize = mach->msgh.msgh_size) < (sizeof(IORPCMessageMach) + sizeof(IORPCMessage))) {
-// printf("BAD REPLY SIZE\n");
- ret = MIG_BAD_ARGUMENTS;
- } else {
- if (!(MACH_MSGH_BITS_COMPLEX & mach->msgh.msgh_bits)) {
- mach->msgh_body.msgh_descriptor_count = 0;
- }
- message = IORPCMessageFromMachReply(mach);
- if (!message) {
- ret = kIOReturnIPCError;
- } else if (message->msgid != msgid) {
-// printf("BAD REPLY ID\n");
- ret = MIG_BAD_ARGUMENTS;
- } else {
- bool isError = (0 != (kIORPCMessageError & message->flags));
- ret = copyInObjects(mach, message, replySize, !isError, true);
- if (kIOReturnSuccess != ret) {
- if (kIODKLogIPC & gIODKDebug) {
- DKLOG("rpc copyin(0x%x) %x\n", ret, mach->msgh.msgh_id);
- }
- if (!isError) {
- consumeObjects(mach, message, replySize);
- copyInObjects(mach, message, replySize, false, true);
- }
- return KERN_NOT_SUPPORTED;
- }
- if (isError) {
- IORPCMessageErrorReturnContent * errorMsg = (typeof(errorMsg))message;
- ret = errorMsg->result;
- }
- }
- }
- }
-
- return ret;
-}
-
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-
-static IORPCMessage *
-IORPCMessageFromMachReply(IORPCMessageMach * msg)
-{
- mach_msg_size_t idx, count;
- mach_msg_port_descriptor_t * desc;
- mach_msg_port_descriptor_t * maxDesc;
- size_t size, msgsize;
- bool upgrade;
- bool reply = true;
-
- msgsize = msg->msgh.msgh_size;
- count = msg->msgh_body.msgh_descriptor_count;
- desc = &msg->objects[0];
- maxDesc = (typeof(maxDesc))(((uintptr_t) msg) + msgsize);
- upgrade = (msg->msgh.msgh_id != (reply ? kIORPCVersionCurrentReply : kIORPCVersionCurrent));
-
- if (upgrade) {
- OSReportWithBacktrace("obsolete message");
- return NULL;
- }
-
- for (idx = 0; idx < count; idx++) {
- if (desc >= maxDesc) {
- return NULL;
- }
- switch (desc->type) {
- case MACH_MSG_PORT_DESCRIPTOR:
- size = sizeof(mach_msg_port_descriptor_t);
- break;
- case MACH_MSG_OOL_DESCRIPTOR:
- size = sizeof(mach_msg_ool_descriptor_t);
- break;
- default:
- return NULL;
- }
- desc = (typeof(desc))(((uintptr_t) desc) + size);
- }
- return (IORPCMessage *)(uintptr_t) desc;
-}
-
-ipc_port_t
-IOUserServer::copySendRightForObject(OSObject * object, ipc_kobject_type_t type)
-{
- return iokit_port_make_send_for_object(object, type);
-}
-
-OSObject *
-IOUserServer::copyObjectForSendRight(ipc_port_t port, ipc_kobject_type_t type)
-{
- return iokit_lookup_io_object(port, type);
-}
-
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-
-// Create a vm_map_copy_t or kalloc'ed data for memory
-// to be copied out. ipc will free after the copyout.
-
-static kern_return_t
-copyoutkdata(const void * data, vm_size_t len, void ** buf)
-{
- kern_return_t err;
- vm_map_copy_t copy;
-
- err = vm_map_copyin( kernel_map, CAST_USER_ADDR_T(data), len,
- false /* src_destroy */, ©);
-
- assert( err == KERN_SUCCESS );
- if (err == KERN_SUCCESS) {
- *buf = (char *) copy;
- }
-
- return err;
-}
-
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-
-IOReturn
-IOUserServer::copyOutObjects(IORPCMessageMach * mach, IORPCMessage * message,
- size_t size, bool consume)
-{
- uint64_t refs;
- uint32_t idx, maxObjectCount;
- ipc_port_t port;
- OSObject * object;
- size_t descsize;
- mach_msg_port_descriptor_t * desc;
- mach_msg_ool_descriptor_t * ool;
- vm_map_copy_t copy;
- void * address;
- mach_msg_size_t length;
- kern_return_t kr;
- OSSerialize * s;
-
- refs = message->objectRefs;
- maxObjectCount = MAX_OBJECT_COUNT(mach, size, message);
-// assert(refs <= mach->msgh_body.msgh_descriptor_count);
-// assert(refs <= maxObjectCount);
- if (refs > mach->msgh_body.msgh_descriptor_count) {
- return kIOReturnBadArgument;
- }
- if (refs > maxObjectCount) {
- return kIOReturnBadArgument;
- }
-
- desc = &mach->objects[0];
- for (idx = 0; idx < refs; idx++) {
- object = (OSObject *) message->objects[idx];
-
- switch (desc->type) {
- case MACH_MSG_PORT_DESCRIPTOR:
- descsize = sizeof(mach_msg_port_descriptor_t);
- port = NULL;
- if (object) {
-#if DEVELOPMENT || DEBUG
- if (kIODKLogIPC & gIODKDebug) {
- IOMemoryDescriptor * iomd = OSDynamicCast(IOMemoryDescriptor, object);
- if (iomd != NULL && (iomd->getFlags() & kIOMemoryThreadSafe) == 0) {
- OSReportWithBacktrace("IOMemoryDescriptor %p was created without kIOMemoryThreadSafe flag", iomd);
- }
- }
-#endif /* DEVELOPMENT || DEBUG */
-
- port = copySendRightForObject(object, IKOT_UEXT_OBJECT);
- if (!port) {
- break;
- }
- if (consume) {
- object->release();
- }
- message->objects[idx] = 0;
- }
-// desc->type = MACH_MSG_PORT_DESCRIPTOR;
- desc->disposition = MACH_MSG_TYPE_MOVE_SEND;
- desc->name = port;
- desc->pad2 = 0;
- desc->pad_end = 0;
- break;
-
- case MACH_MSG_OOL_DESCRIPTOR:
- descsize = sizeof(mach_msg_ool_descriptor_t);
-
- length = 0;
- address = NULL;
- if (object) {
- s = OSSerialize::binaryWithCapacity(4096);
- assert(s);
- if (!s) {
- break;
- }
- s->setIndexed(true);
- if (!object->serialize(s)) {
- assert(false);
- descsize = -1UL;
- s->release();
- break;
- }
- length = s->getLength();
- kr = copyoutkdata(s->text(), length, &address);
- s->release();
- if (KERN_SUCCESS != kr) {
- descsize = -1UL;
- address = NULL;
- length = 0;
- }
- if (consume) {
- object->release();
- }
- message->objects[idx] = 0;
- }
- ool = (typeof(ool))desc;
-// ool->type = MACH_MSG_OOL_DESCRIPTOR;
- ool->deallocate = false;
- ool->copy = MACH_MSG_PHYSICAL_COPY;
- ool->size = length;
- ool->address = address;
- break;
-
- default:
- descsize = -1UL;
- break;
- }
- if (-1UL == descsize) {
- break;
- }
- desc = (typeof(desc))(((uintptr_t) desc) + descsize);
- }
-
- if (idx >= refs) {
- return kIOReturnSuccess;
- }
-
- desc = &mach->objects[0];
- while (idx--) {
- switch (desc->type) {
- case MACH_MSG_PORT_DESCRIPTOR:
- descsize = sizeof(mach_msg_port_descriptor_t);
- port = desc->name;
- if (port) {
- ipc_port_release_send(port);
- }
- break;
-
- case MACH_MSG_OOL_DESCRIPTOR:
- descsize = sizeof(mach_msg_ool_descriptor_t);
- ool = (typeof(ool))desc;
- copy = (vm_map_copy_t) ool->address;
- if (copy) {
- vm_map_copy_discard(copy);
- }
- break;
-
- default:
- descsize = -1UL;
- break;
- }
- if (-1UL == descsize) {
- break;
- }
- desc = (typeof(desc))(((uintptr_t) desc) + descsize);
- }
-
- return kIOReturnBadArgument;
-}
-
-IOReturn
-IOUserServer::copyInObjects(IORPCMessageMach * mach, IORPCMessage * message,
- size_t size, bool copyObjects, bool consumePorts)
-{
- uint64_t refs;
- uint32_t idx, maxObjectCount;
- ipc_port_t port;
- OSObject * object;
- size_t descsize;
- mach_msg_port_descriptor_t * desc;
- mach_msg_ool_descriptor_t * ool;
- vm_map_address_t copyoutdata;
- kern_return_t kr;
-
- refs = message->objectRefs;
- maxObjectCount = MAX_OBJECT_COUNT(mach, size, message);
-// assert(refs <= mach->msgh_body.msgh_descriptor_count);
-// assert(refs <= maxObjectCount);
- if (refs > mach->msgh_body.msgh_descriptor_count) {
- return kIOReturnBadArgument;
- }
- if (refs > maxObjectCount) {
- return kIOReturnBadArgument;
- }
-
- for (idx = 0; idx < refs; idx++) {
- message->objects[idx] = (OSObjectRef) NULL;
- }
-
- desc = &mach->objects[0];
- for (idx = 0; idx < mach->msgh_body.msgh_descriptor_count; idx++) {
- bool isObjectPort = idx < refs;
-
- switch (desc->type) {
- case MACH_MSG_PORT_DESCRIPTOR:
- descsize = sizeof(mach_msg_port_descriptor_t);
-
- object = NULL;
- port = desc->name;
- if (port) {
- if (isObjectPort && copyObjects) {
- object = copyObjectForSendRight(port, IKOT_UEXT_OBJECT);
- if (!object) {
- descsize = -1UL;
- break;
- }
- }
- if (consumePorts) {
- ipc_port_release_send(port);
- desc->name = MACH_PORT_NULL;
- }
- }
- break;
-
- case MACH_MSG_OOL_DESCRIPTOR:
- descsize = sizeof(mach_msg_ool_descriptor_t);
- ool = (typeof(ool))desc;
-
- object = NULL;
- if (isObjectPort && copyObjects && ool->size && ool->address) {
- kr = vm_map_copyout(kernel_map, ©outdata, (vm_map_copy_t) ool->address);
- if (KERN_SUCCESS == kr) {
- object = OSUnserializeXML((const char *) copyoutdata, ool->size);
- kr = vm_deallocate(kernel_map, copyoutdata, ool->size);
- assert(KERN_SUCCESS == kr);
- // vm_map_copyout() has consumed the vm_map_copy_t in the message
- ool->size = 0;
- ool->address = NULL;
- }
- if (!object) {
- descsize = -1UL;
- break;
- }
- }
- break;
-
- default:
- descsize = -1UL;
- break;
- }
- if (-1UL == descsize) {
- break;
- }
- if (isObjectPort && copyObjects) {
- message->objects[idx] = (OSObjectRef) object;
- }
- desc = (typeof(desc))(((uintptr_t) desc) + descsize);
- }
-
- if (idx >= refs) {
- return kIOReturnSuccess;
- }
-
- while (idx--) {
- object = (OSObject *) message->objects[idx];
- OSSafeReleaseNULL(object);
- message->objects[idx] = 0;
- }
-
- return kIOReturnBadArgument;
-}
-
-IOReturn
-IOUserServer::consumeObjects(IORPCMessageMach *mach, IORPCMessage * message, size_t messageSize)
-{
- uint64_t refs, idx;
- OSObject * object;
-
- refs = message->objectRefs;
- uint32_t maxObjectCount = MAX_OBJECT_COUNT(mach, messageSize, message);
- if (refs > mach->msgh_body.msgh_descriptor_count) {
- return kIOReturnBadArgument;
- }
- if (refs > maxObjectCount) {
- return kIOReturnBadArgument;
- }
-
- for (idx = 0; idx < refs; idx++) {
- object = (OSObject *) message->objects[idx];
- if (object) {
- object->release();
- message->objects[idx] = 0;
- }
- }
-
- return kIOReturnSuccess;
-}
-
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-
-static kern_return_t
-acknowledgeSetPowerState(IOService * service);
-
-bool
-IOUserServer::finalize(IOOptionBits options)
-{
- OSArray * services;
-
- if (kIODKLogSetup & gIODKDebug) {
- DKLOG(DKS "::finalize(%p)\n", DKN(this), this);
- }
-
- IOLockLock(gIOUserServerLock);
- OSSafeReleaseNULL(fRootQueue);
- IOLockUnlock(gIOUserServerLock);
-
- services = NULL;
- IOLockLock(fLock);
- if (fServices) {
- services = OSArray::withArray(fServices);
- }
- IOLockUnlock(fLock);
-
- IOOptionBits terminateFlags = kIOServiceTerminateNeedWillTerminate | kIOServiceTerminateWithRematch;
- if (fCheckInToken) {
- bool can_rematch = fCheckInToken->dextTerminate();
- if (can_rematch) {
- terminateFlags |= kIOServiceTerminateWithRematchCurrentDext;
- } else {
- DKLOG(DKS "::finalize(%p) dext was replaced, do not rematch current dext\n", DKN(this), this);
- }
- } else {
- terminateFlags |= kIOServiceTerminateWithRematchCurrentDext;
- DKLOG(DKS "::finalize(%p) could not find fCheckInToken\n", DKN(this), this);
- }
-
- if (services) {
- services->iterateObjects(^bool (OSObject * obj) {
- int service __unused; // hide outer defn
- IOService * nextService;
- IOService * provider;
- bool started = false;
- bool instantiated = false;
-
- nextService = (IOService *) obj;
- if (kIODKLogSetup & gIODKDebug) {
- DKLOG(DKS "::terminate(" DKS ")\n", DKN(this), DKN(nextService));
- }
- if (nextService->reserved->uvars) {
- IOUserClient * nextUserClient = OSDynamicCast(IOUserClient, nextService);
- provider = nextService->getProvider();
- if (nextUserClient) {
- nextUserClient->setTerminateDefer(provider, false);
- }
- (void)::acknowledgeSetPowerState(nextService);
- started = nextService->reserved->uvars->started;
- instantiated = nextService->reserved->uvars->instantiated;
- nextService->reserved->uvars->serverDied = true;
-
- serviceDidStop(nextService, provider);
- if (provider != NULL && (terminateFlags & kIOServiceTerminateWithRematchCurrentDext) == 0) {
- provider->resetRematchProperties();
- }
- if (started) {
- IOService * provider = nextService;
- while ((provider = provider->getProvider())) {
- if (-1U != services->getNextIndexOfObject(provider, 0)) {
- break;
- }
- }
- if (!provider) {
- // this service is the root of the set, so only terminate it
- nextService->terminate(terminateFlags);
- }
- }
- }
- if (!started || !instantiated) {
- DKLOG(DKS "::terminate(" DKS ") server exit before start() instantiated %d\n", DKN(this), DKN(nextService), instantiated);
- // Override started since we are forcing serviceStop to happen
- nextService->reserved->uvars->started = true;
- serviceStop(nextService, NULL);
- }
- return false;
- });
- services->release();
- }
-
- return IOUserClient::finalize(options);
-}
-
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-
-#undef super
-#define super IOUserClient2022
-
-OSDefineMetaClassAndStructors(IOUserServer, IOUserClient2022)
-
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-
-IOUserClient * IOUserServer::withTask(task_t owningTask)
-{
- IOUserServer * inst;
-
- assert(owningTask == current_task());
- if (!task_is_driver(owningTask)) {
- DKLOG("IOUserServer may only be created with driver tasks\n");
- return NULL;
- }
-
- inst = new IOUserServer;
- if (inst && !inst->init()) {
- inst->release();
- inst = NULL;
- return inst;
- }
- OS_ANALYZER_SUPPRESS("82033761") inst->PMinit();
-
- inst->fOwningTask = current_task();
- task_reference(inst->fOwningTask);
-
- inst->fEntitlements = IOUserClient::copyClientEntitlements(inst->fOwningTask);
-
- if (!(kIODKDisableEntitlementChecking & gIODKDebug)) {
- proc_t p;
- pid_t pid;
- const char * name;
- p = (proc_t)get_bsdtask_info(inst->fOwningTask);
- if (p) {
- name = proc_best_name(p);
- pid = proc_pid(p);
- } else {
- name = "unknown";
- pid = 0;
- }
-
- if (inst->fEntitlements == NULL) {
-#if DEVELOPMENT || DEBUG
- panic("entitlements are missing for %s[%d]\n", name, pid);
-#else
- DKLOG("entitlements are missing for %s[%d]\n", name, pid);
-#endif /* DEVELOPMENT || DEBUG */
- }
-
-
- const char * dextTeamID = csproc_get_teamid(p);
- if (dextTeamID != NULL) {
- inst->fTeamIdentifier = OSString::withCString(dextTeamID);
- DKLOG("%s[%d] has team identifier %s\n", name, pid, dextTeamID);
- }
-
- if (!IOCurrentTaskHasEntitlement(gIODriverKitEntitlementKey->getCStringNoCopy())) {
- IOLog(kIODriverKitEntitlementKey " entitlement check failed for %s[%d]\n", name, pid);
- inst->release();
- inst = NULL;
- return inst;
- }
- }
-
- /* Mark the current task's space as eligible for uext object ports */
- iokit_label_dext_task(inst->fOwningTask);
-
- inst->fLock = IOLockAlloc();
- inst->fServices = OSArray::withCapacity(4);
- inst->fClasses = OSDictionary::withCapacity(16);
- inst->fClasses->setOptions(OSCollection::kSort, OSCollection::kSort);
- inst->fPlatformDriver = task_get_platform_binary(inst->fOwningTask);
- if (csproc_get_validation_category(current_proc(), &inst->fCSValidationCategory) != KERN_SUCCESS) {
- inst->fCSValidationCategory = CS_VALIDATION_CATEGORY_INVALID;
- }
- inst->fWorkLoop = IOWorkLoop::workLoop();
-
- inst->setProperty(kIOUserClientDefaultLockingKey, kOSBooleanTrue);
- inst->setProperty(kIOUserClientDefaultLockingSetPropertiesKey, kOSBooleanTrue);
- inst->setProperty(kIOUserClientDefaultLockingSingleThreadExternalMethodKey, kOSBooleanTrue);
- //requirement for gIODriverKitEntitlementKey is enforced elsewhere conditionally
- inst->setProperty(kIOUserClientEntitlementsKey, kOSBooleanFalse);
-
- return inst;
-}
-
-static bool gIOUserServerLeakObjects = false;
-
-bool
-IOUserServer::shouldLeakObjects()
-{
- return gIOUserServerLeakObjects;
-}
-
-void
-IOUserServer::beginLeakingObjects()
-{
- gIOUserServerLeakObjects = true;
-}
-
-bool
-IOUserServer::isPlatformDriver()
-{
- return fPlatformDriver;
-}
-
-int
-IOUserServer::getCSValidationCategory()
-{
- return fCSValidationCategory;
-}
-
-
-struct IOUserServerRecordExitReasonContext {
- task_t task;
- os_reason_t reason;
-};
-
-static bool
-IOUserServerRecordExitReasonMatch(const OSObject *obj, void * context)
-{
- IOUserServerRecordExitReasonContext * ctx = (IOUserServerRecordExitReasonContext *)context;
- IOUserServer * us = OSDynamicCast(IOUserServer, obj);
- if (us == NULL) {
- return false;
- }
-
- if (us->fOwningTask == ctx->task) {
- assert(us->fTaskCrashReason == OS_REASON_NULL);
- assert(ctx->reason != OS_REASON_NULL);
- os_reason_ref(ctx->reason);
- us->fTaskCrashReason = ctx->reason;
- return true;
- }
-
- return false;
-}
-
-extern "C" void
-IOUserServerRecordExitReason(task_t task, os_reason_t reason)
-{
- IOUserServerRecordExitReasonContext ctx { task, reason };
- IOUserServer::gMetaClass.applyToInstances(IOUserServerRecordExitReasonMatch, &ctx);
-}
-
-IOReturn
-IOUserServer::clientClose(void)
-{
- OSArray * services;
- bool __block unexpectedExit = false;
-
- if (kIODKLogSetup & gIODKDebug) {
- DKLOG(DKS "::clientClose(%p)\n", DKN(this), this);
- }
- services = NULL;
- IOLockLock(fLock);
- if (fServices) {
- services = OSArray::withArray(fServices);
- }
- IOLockUnlock(fLock);
-
- // if this was a an expected exit, termination and stop should have detached at this
- // point, so send any provider still attached and not owned by this user server
- // the ClientCrashed() notification
- if (services) {
- services->iterateObjects(^bool (OSObject * obj) {
- int service __unused; // hide outer defn
- IOService * nextService;
- IOService * provider;
-
- nextService = (IOService *) obj;
- if (nextService->isInactive()) {
- return false;
- }
- if (nextService->reserved && nextService->reserved->uvars && nextService->reserved->uvars->started) {
- unexpectedExit = true;
- }
- provider = nextService->getProvider();
- if (provider
- && (!provider->reserved->uvars || (provider->reserved->uvars->userServer != this))) {
- if (kIODKLogSetup & gIODKDebug) {
- DKLOG(DKS "::ClientCrashed(" DKS ")\n", DKN(provider), DKN(nextService));
- }
- if (unexpectedExit) {
- provider->unregisterAllInterrupts();
- }
- provider->ClientCrashed(nextService, 0);
- }
- return false;
- });
- services->release();
- }
-
- if (unexpectedExit &&
- !gInUserspaceReboot &&
- (fTaskCrashReason != OS_REASON_NULL && fTaskCrashReason->osr_namespace != OS_REASON_JETSAM && fTaskCrashReason->osr_namespace != OS_REASON_RUNNINGBOARD) &&
- fStatistics != NULL) {
- OSDextCrashPolicy policy = fStatistics->recordCrash();
- bool allowPanic;
-#if DEVELOPMENT || DEBUG
- allowPanic = !restore_boot && fPlatformDriver && fEntitlements->getObject(gIODriverKitTestDriverEntitlementKey) != kOSBooleanTrue && !disable_dext_crash_reboot;
-#else
- allowPanic = !restore_boot && fPlatformDriver;
-#endif /* DEVELOPMENT || DEBUG */
-
- if (policy == kOSDextCrashPolicyReboot && allowPanic) {
- emergencyPanicCoreDumpEnable();
- panic("Driver %s has crashed too many times (reason %u:%llu)\n",
- getName(), fTaskCrashReason->osr_namespace, fTaskCrashReason->osr_code);
- }
-
- IOPMrootDomain *rootDomain = IOService::getPMRootDomain();
- if (rootDomain) {
- rootDomain->requestRunMode(kIOPMRunModeFullWake);
- }
- }
-
- terminate();
- return kIOReturnSuccess;
-}
-
-IOReturn
-IOUserServer::setProperties(OSObject * properties)
-{
- IOReturn kr = kIOReturnUnsupported;
- return kr;
-}
-
-void
-IOUserServer::stop(IOService * provider)
-{
- if (fOwningTask) {
- task_deallocate(fOwningTask);
- fOwningTask = TASK_NULL;
- }
-
- PMstop();
-
- IOServicePH::serverRemove(this);
-
- OSSafeReleaseNULL(fRootQueue);
-
- if (fInterruptLock) {
- IOSimpleLockFree(fInterruptLock);
- }
-}
-
-IOWorkLoop *
-IOUserServer::getWorkLoop() const
-{
- return fWorkLoop;
-}
-
-void
-IOUserServer::free()
-{
- OSSafeReleaseNULL(fEntitlements);
- OSSafeReleaseNULL(fClasses);
- if (fOwningTask) {
- task_deallocate(fOwningTask);
- fOwningTask = TASK_NULL;
- }
- if (fLock) {
- IOLockFree(fLock);
- }
- OSSafeReleaseNULL(fServices);
- OSSafeReleaseNULL(fCheckInToken);
- OSSafeReleaseNULL(fStatistics);
- OSSafeReleaseNULL(fTeamIdentifier);
- if (fAllocationName) {
- kern_allocation_name_release(fAllocationName);
- fAllocationName = NULL;
- }
- if (fTaskCrashReason != OS_REASON_NULL) {
- os_reason_free(fTaskCrashReason);
- }
- OSSafeReleaseNULL(fWorkLoop);
- IOUserClient::free();
-}
-
-IOReturn
-IOUserServer::registerClass(OSClassDescription * desc, uint32_t size, OSUserMetaClass ** pCls)
-{
- OSUserMetaClass * cls;
- const OSSymbol * sym;
- uint64_t * methodOptions;
- const char * queueNames;
- uint32_t methodOptionsEnd, queueNamesEnd;
- IOReturn ret = kIOReturnSuccess;
-
- if (size < sizeof(OSClassDescription)) {
- assert(false);
- return kIOReturnBadArgument;
- }
-
- if (kIODKLogSetup & gIODKDebug) {
- DKLOG(DKS "::registerClass %s, %d, %d\n", DKN(this), desc->name, desc->queueNamesSize, desc->methodNamesSize);
- }
-
- if (desc->descriptionSize != size) {
- assert(false);
- return kIOReturnBadArgument;
- }
- if (os_add_overflow(desc->queueNamesOffset, desc->queueNamesSize, &queueNamesEnd)) {
- assert(false);
- return kIOReturnBadArgument;
- }
- if (queueNamesEnd > size) {
- assert(false);
- return kIOReturnBadArgument;
- }
- if (os_add_overflow(desc->methodOptionsOffset, desc->methodOptionsSize, &methodOptionsEnd)) {
- assert(false);
- return kIOReturnBadArgument;
- }
- if (methodOptionsEnd > size) {
- assert(false);
- return kIOReturnBadArgument;
- }
- // overlaps?
- if ((desc->queueNamesOffset >= desc->methodOptionsOffset) && (desc->queueNamesOffset < methodOptionsEnd)) {
- assert(false);
- return kIOReturnBadArgument;
- }
- if ((queueNamesEnd >= desc->methodOptionsOffset) && (queueNamesEnd < methodOptionsEnd)) {
- assert(false);
- return kIOReturnBadArgument;
- }
-
- if (desc->methodOptionsSize & ((2 * sizeof(uint64_t)) - 1)) {
- assert(false);
- return kIOReturnBadArgument;
- }
- if (sizeof(desc->name) == strnlen(desc->name, sizeof(desc->name))) {
- assert(false);
- return kIOReturnBadArgument;
- }
- if (sizeof(desc->superName) == strnlen(desc->superName, sizeof(desc->superName))) {
- assert(false);
- return kIOReturnBadArgument;
- }
-
- cls = OSTypeAlloc(OSUserMetaClass);
- assert(cls);
- if (!cls) {
- return kIOReturnNoMemory;
- }
-
- cls->description = (typeof(cls->description))IOMallocData(size);
- assert(cls->description);
- if (!cls->description) {
- assert(false);
- cls->release();
- return kIOReturnNoMemory;
- }
- bcopy(desc, cls->description, size);
-
- cls->methodCount = desc->methodOptionsSize / (2 * sizeof(uint64_t));
- cls->methods = IONewData(uint64_t, 2 * cls->methodCount);
- if (!cls->methods) {
- assert(false);
- cls->release();
- return kIOReturnNoMemory;
- }
-
- methodOptions = (typeof(methodOptions))(((uintptr_t) desc) + desc->methodOptionsOffset);
- bcopy(methodOptions, cls->methods, 2 * cls->methodCount * sizeof(uint64_t));
-
- queueNames = (typeof(queueNames))(((uintptr_t) desc) + desc->queueNamesOffset);
- cls->queueNames = copyInStringArray(queueNames, desc->queueNamesSize);
-
- sym = OSSymbol::withCString(desc->name);
- assert(sym);
- if (!sym) {
- assert(false);
- cls->release();
- return kIOReturnNoMemory;
- }
-
- cls->name = sym;
- cls->meta = OSMetaClass::copyMetaClassWithName(sym);
- IOLockLock(fLock);
- cls->superMeta = OSDynamicCast(OSUserMetaClass, fClasses->getObject(desc->superName));
- if (fClasses->getObject(sym) != NULL) {
- /* class with this name exists */
- ret = kIOReturnBadArgument;
- } else {
- if (fClasses->setObject(sym, cls)) {
- *pCls = cls;
- } else {
- /* could not add class to fClasses */
- ret = kIOReturnNoMemory;
- }
- }
- IOLockUnlock(fLock);
- cls->release();
- return ret;
-}
-
-IOReturn
-IOUserServer::registerClass(OSClassDescription * desc, uint32_t size, OSSharedPtr<OSUserMetaClass>& pCls)
-{
- OSUserMetaClass* pClsRaw = NULL;
- IOReturn result = registerClass(desc, size, &pClsRaw);
- if (result == kIOReturnSuccess) {
- pCls.reset(pClsRaw, OSRetain);
- }
- return result;
-}
-
-IOReturn
-IOUserServer::setRootQueue(IODispatchQueue * queue)
-{
- assert(!fRootQueue);
- if (fRootQueue) {
- return kIOReturnStillOpen;
- }
- queue->retain();
- fRootQueue = queue;
-
- return kIOReturnSuccess;
-}
-
-
-IOReturn
-IOUserServer::externalMethod(uint32_t selector, IOExternalMethodArgumentsOpaque * args)
-{
- static const IOExternalMethodDispatch2022 dispatchArray[] = {
- [kIOUserServerMethodRegisterClass] = {
- .function = &IOUserServer::externalMethodRegisterClass,
- .checkScalarInputCount = 0,
- .checkStructureInputSize = kIOUCVariableStructureSize,
- .checkScalarOutputCount = 2,
- .checkStructureOutputSize = 0,
- .allowAsync = false,
- .checkEntitlement = NULL,
- },
- [kIOUserServerMethodStart] = {
- .function = &IOUserServer::externalMethodStart,
- .checkScalarInputCount = 1,
- .checkStructureInputSize = 0,
- .checkScalarOutputCount = 1,
- .checkStructureOutputSize = 0,
- .allowAsync = false,
- .checkEntitlement = NULL,
- },
- };
-
- return dispatchExternalMethod(selector, args, dispatchArray, sizeof(dispatchArray) / sizeof(dispatchArray[0]), this, NULL);
-}
-
-IOReturn
-IOUserServer::externalMethodRegisterClass(OSObject * target, void * reference, IOExternalMethodArguments * args)
-{
- IOReturn ret = kIOReturnBadArgument;
- mach_port_name_t portname;
-
- IOUserServer * me = (typeof(me))target;
-
- OSUserMetaClass * cls;
- if (!args->structureInputSize) {
- return kIOReturnBadArgument;
- }
-
- ret = me->registerClass((OSClassDescription *) args->structureInput, args->structureInputSize, &cls);
- if (kIOReturnSuccess == ret) {
- portname = iokit_make_send_right(me->fOwningTask, cls, IKOT_UEXT_OBJECT);
- assert(portname);
- args->scalarOutput[0] = portname;
- args->scalarOutput[1] = kOSObjectRPCRemote;
- }
-
- return ret;
-}
-
-IOReturn
-IOUserServer::externalMethodStart(OSObject * target, void * reference, IOExternalMethodArguments * args)
-{
- mach_port_name_t portname = 0;
- IOReturn ret = kIOReturnSuccess;
-
- IOUserServer * me = (typeof(me))target;
-
- if (!(kIODKDisableCheckInTokenVerification & gIODKDebug)) {
- mach_port_name_t checkInPortName = ((typeof(checkInPortName))args->scalarInput[0]);
- OSObject * obj = iokit_lookup_object_with_port_name(checkInPortName, IKOT_IOKIT_IDENT, me->fOwningTask);
- IOUserServerCheckInToken * retrievedToken = OSDynamicCast(IOUserServerCheckInToken, obj);
- if (retrievedToken != NULL) {
- ret = me->setCheckInToken(retrievedToken);
- } else {
- ret = kIOReturnBadArgument;
- }
- OSSafeReleaseNULL(obj);
- }
- if (ret == kIOReturnSuccess) {
- portname = iokit_make_send_right(me->fOwningTask, me, IKOT_UEXT_OBJECT);
- assert(portname);
- }
- args->scalarOutput[0] = portname;
- return ret;
-}
-IOExternalTrap *
-IOUserServer::getTargetAndTrapForIndex( IOService **targetP, UInt32 index )
-{
- static const OSBoundedArray<IOExternalTrap, 1> trapTemplate = {{
- { NULL, (IOTrap) & IOUserServer::waitInterruptTrap},
- }};
- if (index >= trapTemplate.size()) {
- return NULL;
- }
- *targetP = this;
- return (IOExternalTrap *)&trapTemplate[index];
-}
-
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-
-void
-IOUserServer::pageout()
-{
- fPageout = 1;
-}
-
-IOReturn
-IOUserServer::serviceAttach(IOService * service, IOService * provider)
-{
- IOReturn ret;
- OSObjectUserVars * vars;
- OSObject * prop;
- OSString * str;
- OSSymbol const* bundleID;
- char execPath[1024];
-
- vars = IOMallocType(OSObjectUserVars);
- service->reserved->uvars = vars;
-
- vars->userServer = this;
- vars->userServer->retain();
- vars->uvarsLock = IOLockAlloc();
- vars->originalProperties = service->dictionaryWithProperties();
- IOLockLock(fLock);
- if (-1U == fServices->getNextIndexOfObject(service, 0)) {
- fServices->setObject(service);
-
- // Add to IOAssociatedServices
- OSObject * serviceArrayObj = copyProperty(gIOAssociatedServicesKey);
- OSArray * serviceArray = OSDynamicCast(OSArray, serviceArrayObj);
- if (!serviceArray) {
- serviceArray = OSArray::withCapacity(0);
- } else {
- serviceArray = OSDynamicCast(OSArray, serviceArray->copyCollection());
- assert(serviceArray != NULL);
- }
-
- OSNumber * registryEntryNumber = OSNumber::withNumber(service->getRegistryEntryID(), 64);
- serviceArray->setObject(registryEntryNumber);
- setProperty(gIOAssociatedServicesKey, serviceArray);
- OSSafeReleaseNULL(registryEntryNumber);
- OSSafeReleaseNULL(serviceArray);
- OSSafeReleaseNULL(serviceArrayObj);
-
- // populate kIOUserClassesKey
-
- OSUserMetaClass * userMeta;
- OSArray * classesArray;
- const OSString * str2;
-
- classesArray = OSArray::withCapacity(4);
- prop = service->copyProperty(gIOUserClassKey);
- str2 = OSDynamicCast(OSString, prop);
- userMeta = (typeof(userMeta))service->reserved->uvars->userServer->fClasses->getObject(str2);
- while (str2 && userMeta) {
- classesArray->setObject(str2);
- userMeta = userMeta->superMeta;
- if (userMeta) {
- str2 = userMeta->name;
- }
- }
- service->setProperty(gIOUserClassesKey, classesArray);
- OSSafeReleaseNULL(classesArray);
- OSSafeReleaseNULL(prop);
- }
- IOLockUnlock(fLock);
-
- prop = service->copyProperty(gIOUserClassKey);
- str = OSDynamicCast(OSString, prop);
- if (str) {
- service->setName(str);
- }
- OSSafeReleaseNULL(prop);
-
- prop = service->copyProperty(gIOModuleIdentifierKey);
- bundleID = OSDynamicCast(OSSymbol, prop);
- if (bundleID) {
- execPath[0] = 0;
- bool ok = OSKext::copyUserExecutablePath(bundleID, execPath, sizeof(execPath));
- if (ok) {
- ret = LoadModule(execPath);
- if (kIODKLogSetup & gIODKDebug) {
- DKLOG(DKS "::LoadModule 0x%x %s\n", DKN(this), ret, execPath);
- }
- }
- }
- OSSafeReleaseNULL(prop);
-
- ret = kIOReturnSuccess;
- if (kIODKLogSetup & gIODKDebug) {
- DKLOG(DKS "::serviceAttach(" DKS ", " DKS ")\n", DKN(this), DKN(service), DKN(provider));
- }
-
- return ret;
-}
-
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-
-IOReturn
-IOUserServer::serviceNewUserClient(IOService * service, task_t owningTask, void * securityID,
- uint32_t type, OSDictionary * properties, IOUserClient ** handler)
-{
- IOReturn ret;
- IOUserClient * uc;
- IOUserUserClient * userUC;
- OSDictionary * entitlements;
- OSObject * prop;
- OSObject * bundleID;
- bool ok = false;
-
- entitlements = IOUserClient::copyClientEntitlements(owningTask);
- if (!entitlements) {
- entitlements = OSDictionary::withCapacity(8);
- }
- if (entitlements) {
- if (kIOReturnSuccess == clientHasPrivilege((void *) owningTask, kIOClientPrivilegeAdministrator)) {
- entitlements->setObject(kIODriverKitUserClientEntitlementAdministratorKey, kOSBooleanTrue);
- }
- OSString * creatorName = IOCopyLogNameForPID(proc_selfpid());
- if (creatorName) {
- entitlements->setObject(kIOUserClientCreatorKey, creatorName);
- OSSafeReleaseNULL(creatorName);
- }
- }
-
- *handler = NULL;
- ret = service->_NewUserClient(type, entitlements, &uc);
- if (kIOReturnSuccess != ret || uc == NULL) {
- OSSafeReleaseNULL(uc);
- OSSafeReleaseNULL(entitlements);
- return ret;
- }
- userUC = OSDynamicCast(IOUserUserClient, uc);
- if (!userUC) {
- if (uc) {
- uc->terminate(kIOServiceTerminateNeedWillTerminate);
- uc->setTerminateDefer(service, false);
- OSSafeReleaseNULL(uc);
- }
- OSSafeReleaseNULL(entitlements);
- return kIOReturnUnsupported;
- }
- userUC->setTask(owningTask);
-
- if (!(kIODKDisableEntitlementChecking & gIODKDebug)) {
- do {
- bool checkiOS3pEntitlements;
-
- // check if client has com.apple.private.driverkit.driver-access and the required entitlements match the driver's entitlements
- if (entitlements && (prop = entitlements->getObject(gIODriverKitRequiredEntitlementsKey))) {
- prop->retain();
- ok = checkEntitlements(fEntitlements, prop, NULL, NULL);
- if (ok) {
- break;
- } else {
- DKLOG(DKS ":UC failed required entitlement check\n", DKN(userUC));
- }
- }
-
-#if XNU_TARGET_OS_IOS
- checkiOS3pEntitlements = !fPlatformDriver;
- if (checkiOS3pEntitlements && fTeamIdentifier == NULL) {
- DKLOG("warning: " DKS " does not have a team identifier\n", DKN(this));
- }
-#else
- checkiOS3pEntitlements = false;
-#endif
- if (checkiOS3pEntitlements) {
- // App must have com.apple.developer.driverkit.communicates-with-drivers
- ok = entitlements && entitlements->getObject(gIODriverKitUserClientEntitlementCommunicatesWithDriversKey) == kOSBooleanTrue;
- if (ok) {
- // check team ID
- const char * clientTeamID = csproc_get_teamid(current_proc());
- bool sameTeam = fTeamIdentifier != NULL && clientTeamID != NULL && strncmp(fTeamIdentifier->getCStringNoCopy(), clientTeamID, CS_MAX_TEAMID_LEN) == 0;
-
- if (sameTeam) {
- ok = true;
- } else {
- // different team IDs, dext must have com.apple.developer.driverkit.allow-third-party-userclients
- ok = fEntitlements && fEntitlements->getObject(gIODriverKitUserClientEntitlementAllowThirdPartyUserClientsKey) == kOSBooleanTrue;
- }
- if (!ok) {
- DKLOG(DKS ":UC failed team ID check. client team=%s, driver team=%s\n", DKN(userUC), clientTeamID ? clientTeamID : "(null)", fTeamIdentifier ? fTeamIdentifier->getCStringNoCopy() : "(null)");
- }
- } else {
- DKLOG(DKS ":UC entitlement check failed, app does not have %s entitlement\n", DKN(userUC), gIODriverKitUserClientEntitlementCommunicatesWithDriversKey->getCStringNoCopy());
- }
-
- // When checking iOS 3rd party entitlements, do not fall through to other entitlement checks
- break;
- }
-
- // first party dexts and third party macOS dexts
-
- // check if driver has com.apple.developer.driverkit.allow-any-userclient-access
- if (fEntitlements && fEntitlements->getObject(gIODriverKitUserClientEntitlementAllowAnyKey)) {
- ok = true;
- break;
- }
-
- // check if client has com.apple.developer.driverkit.userclient-access and its value matches the bundle ID of the service
- bundleID = service->copyProperty(gIOModuleIdentifierKey);
- ok = (entitlements
- && bundleID
- && (prop = entitlements->getObject(gIODriverKitUserClientEntitlementsKey)));
- if (ok) {
- bool found __block = false;
- ok = prop->iterateObjects(^bool (OSObject * object) {
- found = object->isEqualTo(bundleID);
- return found;
- });
- ok = found;
- } else {
- OSString * bundleIDStr = OSDynamicCast(OSString, bundleID);
- DKLOG(DKS ":UC failed userclient-access check, needed bundle ID %s\n", DKN(userUC), bundleIDStr ? bundleIDStr->getCStringNoCopy() : "(null)");
- }
- OSSafeReleaseNULL(bundleID);
- } while (false);
-
- if (ok) {
- prop = userUC->copyProperty(gIOServiceDEXTEntitlementsKey);
- ok = checkEntitlements(entitlements, prop, NULL, NULL);
- }
-
- if (!ok) {
- DKLOG(DKS ":UC entitlements check failed\n", DKN(userUC));
- uc->terminate(kIOServiceTerminateNeedWillTerminate);
- uc->setTerminateDefer(service, false);
- OSSafeReleaseNULL(uc);
- OSSafeReleaseNULL(entitlements);
- return kIOReturnNotPermitted;
- }
- }
-
- OSSafeReleaseNULL(entitlements);
- *handler = userUC;
-
- return ret;
-}
-
-IOReturn
-IOUserServer::serviceNewUserClient(IOService * service, task_t owningTask, void * securityID,
- uint32_t type, OSDictionary * properties, OSSharedPtr<IOUserClient>& handler)
-{
- IOUserClient* handlerRaw = NULL;
- IOReturn result = serviceNewUserClient(service, owningTask, securityID, type, properties, &handlerRaw);
- handler.reset(handlerRaw, OSNoRetain);
- return result;
-}
-
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-
-static IOPMPowerState
- sPowerStates[] = {
- { .version = kIOPMPowerStateVersion1,
- .capabilityFlags = 0,
- .outputPowerCharacter = 0,
- .inputPowerRequirement = 0},
- { .version = kIOPMPowerStateVersion1,
- .capabilityFlags = kIOPMLowPower,
- .outputPowerCharacter = kIOPMLowPower,
- .inputPowerRequirement = kIOPMLowPower},
- { .version = kIOPMPowerStateVersion1,
- .capabilityFlags = kIOPMAOTPower,
- .outputPowerCharacter = kIOPMAOTPower,
- .inputPowerRequirement = kIOPMAOTPower},
- { .version = kIOPMPowerStateVersion1,
- .capabilityFlags = kIOPMPowerOn,
- .outputPowerCharacter = kIOPMPowerOn,
- .inputPowerRequirement = kIOPMPowerOn},
-};
-
-enum {
- kUserServerMaxPowerState = 3
-};
-
-IOReturn
-IOUserServer::serviceJoinPMTree(IOService * service)
-{
- IOReturn ret;
- IOService * pmProvider;
- bool joinTree;
-
- if (service->reserved->uvars->userServerPM) {
- return kIOReturnSuccess;
- }
-
- if (!fRootNotifier) {
- ret = registerPowerDriver(this, sPowerStates, sizeof(sPowerStates) / sizeof(sPowerStates[0]));
- assert(kIOReturnSuccess == ret);
- IOServicePH::serverAdd(this);
- fRootNotifier = true;
- }
-
- joinTree = false;
- if (!(kIODKDisablePM & gIODKDebug) && !service->pm_vars) {
- kern_return_t kr;
- OSDictionary * props;
- kr = service->CopyProperties_Local(&props);
- if (kIOReturnSuccess == kr) {
- if (props->getObject(kIOPMResetPowerStateOnWakeKey) == kOSBooleanTrue) {
- service->setProperty(kIOPMResetPowerStateOnWakeKey, kOSBooleanTrue);
- }
- fAOTAllow |= (NULL != props->getObject(kIOPMAOTAllowKey));
- if (!(kIODKDisableIOPMSystemOffPhase2Allow & gIODKDebug)) {
- fSystemOffPhase2Allow |= (NULL != props->getObject(kIOPMSystemOffPhase2AllowKey));
- }
- OSSafeReleaseNULL(props);
- }
- service->PMinit();
- ret = service->registerPowerDriver(this, sPowerStates, sizeof(sPowerStates) / sizeof(sPowerStates[0]));
- assert(kIOReturnSuccess == ret);
- joinTree = true;
- }
-
- pmProvider = service;
- while (pmProvider && !pmProvider->inPlane(gIOPowerPlane)) {
- pmProvider = pmProvider->getProvider();
- }
- if (!pmProvider) {
- pmProvider = getPMRootDomain();
- }
- if (pmProvider) {
- IOService * entry;
- OSObject * prop;
- OSObject * nextProp;
- OSString * str;
-
- entry = pmProvider;
- prop = NULL;
- do {
- nextProp = entry->copyProperty("non-removable");
- if (nextProp) {
- OSSafeReleaseNULL(prop);
- prop = nextProp;
- }
- entry = entry->getProvider();
- } while (entry);
- if (prop) {
- str = OSDynamicCast(OSString, prop);
- if (str && str->isEqualTo("yes")) {
- pmProvider = NULL;
- }
- prop->release();
- }
- }
-
- if (!(kIODKDisablePM & gIODKDebug) && pmProvider) {
- IOLockLock(fLock);
- service->reserved->uvars->powerState = true;
- IOLockUnlock(fLock);
-
- if (joinTree) {
- pmProvider->joinPMtree(service);
- service->reserved->uvars->userServerPM = true;
- service->reserved->uvars->resetPowerOnWake = service->propertyExists(kIOPMResetPowerStateOnWakeKey);
- }
- }
-
- service->registerInterestedDriver(this);
- return kIOReturnSuccess;
-}
-
-IOReturn
-IOUserServer::setPowerState(unsigned long state, IOService * service)
-{
- if (kIODKLogPM & gIODKDebug) {
- DKLOG(DKS "::setPowerState(%ld) %d\n", DKN(service), state, fSystemPowerAck);
- }
- return kIOPMAckImplied;
-}
-
-
-IOReturn
-IOUserServer::serviceSetPowerState(IOService * controllingDriver, IOService * service, IOPMPowerFlags flags, unsigned long state)
-{
- IOReturn ret;
- bool sendIt = false;
-
- IOLockLock(fLock);
- if (service->reserved->uvars) {
- if (!fSystemOff && !(kIODKDisablePM & gIODKDebug)) {
- service->reserved->uvars->willPower = true;
- service->reserved->uvars->willPowerState = state;
- service->reserved->uvars->controllingDriver = controllingDriver;
- sendIt = true;
- } else {
- service->reserved->uvars->willPower = false;
- }
- }
- IOLockUnlock(fLock);
-
- if (sendIt) {
- uint32_t driverFlags = (uint32_t) flags;
- if (kIODKLogPM & gIODKDebug) {
- DKLOG(DKS "::serviceSetPowerState(%ld, 0x%x) %d\n", DKN(service), state, driverFlags, fSystemPowerAck);
- }
-#if DEBUG || DEVELOPMENT
- bool pageout = false;
- uint64_t pageincount = 0;
- if (gLPWFlags) {
- pageout = fPageout;
- if (pageout) {
- fPageout = false;
- DKLOG(DKS " pageout\n", DKN(service));
- pageincount = vm_task_evict_shared_cache(fOwningTask);
- }
- }
-#endif /* DEBUG || DEVELOPMENT */
-
- ret = service->SetPowerState(driverFlags);
-
-#if DEBUG || DEVELOPMENT
- if (pageout) {
- DKLOG(DKS " state %ld pageins %qd\n", DKN(service), state, vm_task_pageins(fOwningTask) - pageincount);
- }
-#endif /* DEBUG || DEVELOPMENT */
-
- if (kIOReturnSuccess == ret) {
- return 20 * 1000 * 1000;
- } else {
- IOLockLock(fLock);
- service->reserved->uvars->willPower = false;
- IOLockUnlock(fLock);
- }
- }
-
- return kIOPMAckImplied;
-}
-
-IOReturn
-IOUserServer::powerStateWillChangeTo(IOPMPowerFlags flags, unsigned long state, IOService * service)
-{
- return kIOPMAckImplied;
-}
-
-IOReturn
-IOUserServer::powerStateDidChangeTo(IOPMPowerFlags flags, unsigned long state, IOService * service)
-{
- unsigned int idx;
- bool pmAck;
-
- pmAck = false;
- IOLockLock(fLock);
- idx = fServices->getNextIndexOfObject(service, 0);
- if (-1U == idx) {
- IOLockUnlock(fLock);
- return kIOPMAckImplied;
- }
-
- service->reserved->uvars->powerState = (0 != state);
- bool allPowerStates __block = service->reserved->uvars->powerState;
- if (!allPowerStates) {
- // any service on?
- fServices->iterateObjects(^bool (OSObject * obj) {
- int service __unused; // hide outer defn
- IOService * nextService;
- nextService = (IOService *) obj;
- allPowerStates = nextService->reserved->uvars->powerState;
- // early terminate if true
- return allPowerStates;
- });
- }
- if (kIODKLogPM & gIODKDebug) {
- DKLOG(DKS "::powerStateDidChangeTo(%ld) %d, %d\n", DKN(service), state, allPowerStates, fSystemPowerAck);
- }
- if (!allPowerStates && (pmAck = fSystemPowerAck)) {
- fSystemPowerAck = false;
- fSystemOff = true;
- }
- IOLockUnlock(fLock);
-
- if (pmAck) {
- serverAck();
- }
-
- return kIOPMAckImplied;
-}
-
-bool
-IOUserServer::checkPMReady()
-{
- bool __block ready = true;
-
- IOLockLock(fLock);
- // Check if any services have not completely joined the PM tree (i.e.
- // addPowerChild has not compeleted).
- fServices->iterateObjects(^bool (OSObject * obj) {
- IOPowerConnection *conn;
- IOService *service = (IOService *) obj;
- IORegistryEntry *parent = service->getParentEntry(gIOPowerPlane);
- if ((conn = OSDynamicCast(IOPowerConnection, parent))) {
- if (!conn->getReadyFlag()) {
- ready = false;
- return true;
- }
- }
- return false;
- });
- IOLockUnlock(fLock);
-
- return ready;
-}
-
-IOReturn
-IOUserServer::serviceCreatePMAssertion(IOService * service, uint32_t assertionBits, uint64_t * assertionID, bool synced)
-{
- IOReturn ret = kIOReturnSuccess;
-
- *assertionID = kIOPMUndefinedDriverAssertionID;
-
- if (!service->reserved->uvars || service->reserved->uvars->userServer != this) {
- return kIOReturnError;
- }
-
- if (!service->reserved->uvars->userServerPM) {
- // Cannot create PM assertion unless joined PM tree
- return kIOReturnNotReady;
- }
-
- // Check to make sure the bits are allowed
- uint32_t userAllowedBits = kIOPMDriverAssertionCPUBit |
- kIOPMDriverAssertionForceFullWakeupBit;
- if (synced) {
- userAllowedBits = kIOPMDriverAssertionCPUBit;
- }
- if (0 == (assertionBits & ~userAllowedBits)) {
- if (synced) {
- ret = getPMRootDomain()->acquireDriverKitSyncedAssertion(service, assertionID);
- assert(ret != kIOReturnSuccess || *assertionID != kIOPMUndefinedDriverAssertionID);
- } else {
- *assertionID = getPMRootDomain()->createPMAssertion(assertionBits,
- kIOPMDriverAssertionLevelOn,
- getPMRootDomain(),
- service->getName());
- if (!*assertionID) {
- ret = kIOReturnInternalError;
- }
- }
- } else {
- ret = kIOReturnBadArgument;
- }
- if (*assertionID != kIOPMUndefinedDriverAssertionID) {
- IOLockLock(fLock);
- OSNumber * assertionIDNumber = OSNumber::withNumber(*assertionID, 64);
- OSArray ** pmAssertions = (synced ? &service->reserved->uvars->pmAssertionsSynced : &service->reserved->uvars->pmAssertions);
- if (!*pmAssertions) {
- *pmAssertions = OSArray::withCapacity(1);
- }
- (*pmAssertions)->setObject(assertionIDNumber);
- assertionIDNumber->release();
- IOLockUnlock(fLock);
- }
-
- return ret;
-}
-
-IOReturn
-IOUserServer::serviceReleasePMAssertion(IOService * service, IOPMDriverAssertionID assertionID)
-{
- kern_return_t ret = kIOReturnSuccess;
- bool synced = false;
-
- bool (^findAndRemoveAssertionID)(OSArray *) = ^(OSArray * assertions) {
- unsigned index;
- if (!assertions) {
- return false;
- }
- for (index = 0; index < assertions->getCount(); index++) {
- OSNumber * theID = (OSNumber *)assertions->getObject(index);
- if (theID->unsigned64BitValue() == assertionID) {
- break;
- }
- }
- if (index == assertions->getCount()) {
- return false;
- }
- assertions->removeObject(index);
- return true;
- };
-
- if (!service->reserved->uvars || !service->reserved->uvars->userServer) {
- return kIOReturnError;
- }
-
- IOLockLock(fLock);
- if (findAndRemoveAssertionID(service->reserved->uvars->pmAssertionsSynced)) {
- synced = true;
- } else if (!findAndRemoveAssertionID(service->reserved->uvars->pmAssertions)) {
- ret = kIOReturnNotFound;
- }
- IOLockUnlock(fLock);
-
- if (ret == kIOReturnSuccess) {
- if (synced) {
- getPMRootDomain()->releaseDriverKitSyncedAssertion(assertionID);
- } else {
- getPMRootDomain()->releasePMAssertion(assertionID);
- }
- }
-
- return ret;
-}
-
-kern_return_t
-IOService::JoinPMTree_Impl(void)
-{
- if (!reserved->uvars || !reserved->uvars->userServer) {
- return kIOReturnNotReady;
- }
- return reserved->uvars->userServer->serviceJoinPMTree(this);
-}
-
-static kern_return_t
-acknowledgeSetPowerState(IOService * service)
-{
- if (service->reserved->uvars
- && service->reserved->uvars->userServer
- && service->reserved->uvars->willPower) {
- IOReturn ret;
- service->reserved->uvars->willPower = false;
- ret = service->reserved->uvars->controllingDriver->setPowerState(service->reserved->uvars->willPowerState, service);
- if (kIOPMAckImplied == ret) {
- service->acknowledgeSetPowerState();
- }
- return kIOReturnSuccess;
- }
- return kIOReturnNotReady;
-}
-
-kern_return_t
-IOService::SetPowerState_Impl(
- uint32_t powerFlags)
-{
- if (kIODKLogPM & gIODKDebug) {
- DKLOG(DKS "::SetPowerState(%d), %d\n", DKN(this), powerFlags, reserved->uvars->willPower);
- }
- return ::acknowledgeSetPowerState(this);
-}
-
-kern_return_t
-IOService::ChangePowerState_Impl(
- uint32_t powerFlags)
-{
- switch (powerFlags) {
- case kIOServicePowerCapabilityOff:
- changePowerStateToPriv(0);
- break;
- case kIOServicePowerCapabilityLow:
- changePowerStateToPriv(1);
- break;
- case kIOServicePowerCapabilityOn:
- changePowerStateToPriv(kUserServerMaxPowerState);
- break;
- default:
- return kIOReturnBadArgument;
- }
-
- return kIOReturnSuccess;
-}
-
-kern_return_t
-IOService::SetPowerOverride_Impl(
- bool enable)
-{
- kern_return_t ret;
-
- if (enable) {
- ret = powerOverrideOnPriv();
- } else {
- ret = powerOverrideOffPriv();
- }
-
- return ret == IOPMNoErr ? kIOReturnSuccess : kIOReturnError;
-}
-
-kern_return_t
-IOService::_ClaimSystemWakeEvent_Impl(
- IOService * device,
- uint64_t flags,
- const char * reason,
- OSContainer * details)
-{
- IOPMrootDomain * rootDomain;
- IOOptionBits pmFlags;
-
- rootDomain = getPMRootDomain();
- if (!rootDomain) {
- return kIOReturnNotReady;
- }
- if (os_convert_overflow(flags, &pmFlags)) {
- return kIOReturnBadArgument;
- }
- rootDomain->claimSystemWakeEvent(device, pmFlags, reason, details);
-
- return kIOReturnSuccess;
-}
-
-kern_return_t
-IOService::Create_Impl(
- IOService * provider,
- const char * propertiesKey,
- IOService ** result)
-{
- OSObject * inst;
- IOService * service;
- OSString * str;
- const OSSymbol * sym;
- OSObject * prop = NULL;
- OSObject * moduleIdentifier = NULL;
- OSObject * userServerName = NULL;
- OSDictionary * properties = NULL;
- OSDictionary * copyProperties = NULL;
- kern_return_t ret;
-
- if (provider != this) {
- return kIOReturnUnsupported;
- }
- if (reserved == NULL || reserved->uvars == NULL) {
- return kIOReturnUnsupported;
- }
-
- ret = kIOReturnUnsupported;
- inst = NULL;
- service = NULL;
-
- prop = reserved->uvars->originalProperties->getObject(propertiesKey);
- if (!prop) {
- return kIOReturnBadArgument;
- }
- prop->retain();
- properties = OSDynamicCast(OSDictionary, prop);
- if (!properties) {
- ret = kIOReturnBadArgument;
- goto finish;
- }
- copyProperties = OSDynamicCast(OSDictionary, properties->copyCollection());
- if (!copyProperties) {
- ret = kIOReturnNoMemory;
- goto finish;
- }
- moduleIdentifier = copyProperty(gIOModuleIdentifierKey);
- if (moduleIdentifier) {
- copyProperties->setObject(gIOModuleIdentifierKey, moduleIdentifier);
- }
- userServerName = reserved->uvars->userServer->copyProperty(gIOUserServerNameKey);
- if (userServerName) {
- copyProperties->setObject(gIOUserServerNameKey, userServerName);
- }
-
- str = OSDynamicCast(OSString, copyProperties->getObject(gIOClassKey));
- if (!str) {
- ret = kIOReturnBadArgument;
- goto finish;
- }
- sym = OSSymbol::withString(str);
- if (sym) {
- inst = OSMetaClass::allocClassWithName(sym);
- service = OSDynamicCast(IOService, inst);
- if (service && service->init(copyProperties) && service->attach(this)) {
- reserved->uvars->userServer->serviceAttach(service, this);
- service->reserved->uvars->started = true;
- ret = kIOReturnSuccess;
- *result = service;
- }
- OSSafeReleaseNULL(sym);
- }
-
-finish:
- OSSafeReleaseNULL(prop);
- OSSafeReleaseNULL(copyProperties);
- OSSafeReleaseNULL(moduleIdentifier);
- OSSafeReleaseNULL(userServerName);
- if (kIOReturnSuccess != ret) {
- OSSafeReleaseNULL(inst);
- }
-
- return ret;
-}
-
-kern_return_t
-IOService::Terminate_Impl(
- uint64_t options)
-{
- IOUserServer * us;
-
- if (options) {
- return kIOReturnUnsupported;
- }
-
- us = (typeof(us))thread_iokit_tls_get(0);
- if (us && (!reserved->uvars
- || (reserved->uvars->userServer != us))) {
- return kIOReturnNotPermitted;
- }
- terminate(kIOServiceTerminateNeedWillTerminate);
-
- return kIOReturnSuccess;
-}
-
-kern_return_t
-IOService::NewUserClient_Impl(
- uint32_t type,
- IOUserClient ** userClient)
-{
- return kIOReturnError;
-}
-
-kern_return_t
-IOService::_NewUserClient_Impl(
- uint32_t type,
- OSDictionary * entitlements,
- IOUserClient ** userClient)
-{
- return kIOReturnError;
-}
-
-kern_return_t
-IOService::SearchProperty_Impl(
- const char * name,
- const char * plane,
- uint64_t options,
- OSContainer ** property)
-{
- OSObject * object __block;
- IOService * provider;
- IOOptionBits regOptions;
-
- if (kIOServiceSearchPropertyParents & options) {
- regOptions = kIORegistryIterateParents | kIORegistryIterateRecursively;
- } else {
- regOptions = 0;
- }
-
- object = copyProperty(name, IORegistryEntry::getPlane(plane), regOptions);
-
- if (NULL == object) {
- for (provider = this; provider; provider = provider->getProvider()) {
- provider->runPropertyActionBlock(^IOReturn (void) {
- OSDictionary * userProps;
- object = provider->getProperty(name);
- if (!object
- && (userProps = OSDynamicCast(OSDictionary, provider->getProperty(gIOUserServicePropertiesKey)))) {
- object = userProps->getObject(name);
- }
- if (object) {
- object->retain();
- }
- return kIOReturnSuccess;
- });
- if (object || !(kIORegistryIterateParents & regOptions)) {
- break;
- }
- }
- }
-
- *property = object;
-
- return object ? kIOReturnSuccess : kIOReturnNotFound;
-}
-
-kern_return_t
-IOService::StringFromReturn_Impl(
- IOReturn retval,
- OSString ** str)
-{
- OSString *obj = OSString::withCString(stringFromReturn(retval));
- *str = obj;
- return obj ? kIOReturnSuccess : kIOReturnError;
-}
-
-#if PRIVATE_WIFI_ONLY
-const char *
-IOService::StringFromReturn(
- IOReturn retval)
-{
- return stringFromReturn(retval);
-}
-#endif /* PRIVATE_WIFI_ONLY */
-
-kern_return_t
-IOService::CopyProviderProperties_Impl(
- OSArray * propertyKeys,
- OSArray ** properties)
-{
- IOReturn ret;
- OSArray * result;
- IOService * provider;
-
- result = OSArray::withCapacity(8);
- if (!result) {
- return kIOReturnNoMemory;
- }
-
- ret = kIOReturnSuccess;
- for (provider = this; provider; provider = provider->getProvider()) {
- OSObject * obj;
- OSDictionary * props;
-
- obj = provider->copyProperty(gIOSupportedPropertiesKey);
- props = OSDynamicCast(OSDictionary, obj);
- if (!props) {
- OSSafeReleaseNULL(obj);
- props = provider->dictionaryWithProperties();
- }
- if (!props) {
- ret = kIOReturnNoMemory;
- break;
- }
-
- bool __block addClass = true;
- if (propertyKeys) {
- OSDictionary * retProps;
- retProps = OSDictionary::withCapacity(4);
- addClass = false;
- if (!retProps) {
- ret = kIOReturnNoMemory;
- OSSafeReleaseNULL(props);
- break;
- }
- propertyKeys->iterateObjects(^bool (OSObject * _key) {
- OSString * key = OSDynamicCast(OSString, _key);
- if (gIOClassKey->isEqualTo(key)) {
- addClass = true;
- return false;
- }
- retProps->setObject(key, props->getObject(key));
- return false;
- });
- OSSafeReleaseNULL(props);
- props = retProps;
- }
- if (addClass) {
- OSArray * classes = OSArray::withCapacity(8);
- if (!classes) {
- OSSafeReleaseNULL(props);
- ret = kIOReturnNoMemory;
- break;
- }
- for (const OSMetaClass * meta = provider->getMetaClass(); meta; meta = meta->getSuperClass()) {
- classes->setObject(meta->getClassNameSymbol());
- }
- props->setObject(gIOClassKey, classes);
- OSSafeReleaseNULL(classes);
- }
- bool ok = result->setObject(props);
- props->release();
- if (!ok) {
- ret = kIOReturnNoMemory;
- break;
- }
- }
- if (kIOReturnSuccess != ret) {
- OSSafeReleaseNULL(result);
- }
- *properties = result;
- return ret;
-}
-
-IOReturn
-IOService::AdjustBusy_Impl(int32_t delta)
-{
- adjustBusy(delta);
- return kIOReturnSuccess;
-}
-
-IOReturn
-IOService::GetBusyState_Impl(uint32_t *busyState)
-{
- *busyState = getBusyState();
- return kIOReturnSuccess;
-}
-
-kern_return_t
-IOService::CreatePMAssertion_Impl(uint32_t assertionBits, uint64_t * assertionID, bool synced)
-{
- *assertionID = kIOPMUndefinedDriverAssertionID;
-
- if (!reserved->uvars || !reserved->uvars->userServer) {
- return kIOReturnError;
- }
-
- return reserved->uvars->userServer->serviceCreatePMAssertion(this, assertionBits, assertionID, synced);
-}
-
-kern_return_t
-IOService::ReleasePMAssertion_Impl(uint64_t assertionID)
-{
- if (!reserved->uvars || !reserved->uvars->userServer) {
- return kIOReturnError;
- }
-
- return reserved->uvars->userServer->serviceReleasePMAssertion(this, assertionID);
-}
-
-void
-IOUserServer::serverAck(void)
-{
- IOServicePH::serverAck(this);
-}
-
-OSArray *
-IOUserServer::servicesWithPowerState(bool state)
-{
- OSArray * result = OSArray::withCapacity(1);
- if (!result) {
- return NULL;
- }
- IOLockLock(fLock);
- fServices->iterateObjects(^(OSObject * object) {
- IOService * service = OSDynamicCast(IOService, object);
- if (service && service->reserved->uvars->powerState == state) {
- result->setObject(service);
- }
- return false;
- });
- IOLockUnlock(fLock);
- if (!result->getCount()) {
- OSSafeReleaseNULL(result);
- }
- return result;
-}
-
-void
-IOUserServer::systemSuspend()
-{
- if (fSystemOff && !fSuspended) {
- KDBG(MACHDBG_CODE(DBG_MACH_SCHED, MACH_SUSPEND_DRIVERKIT_USERSPACE) | DBG_FUNC_START,
- task_pid(fOwningTask));
- task_suspend_internal(fOwningTask);
- DKLOG(DKS " did task_suspend_internal\n", DKN(this));
- fSuspended = true;
- }
-}
-
-void
-IOUserServer::systemPower(uint8_t systemState, bool hibernate)
-{
- OSArray * services;
- {
- OSDictionary * sleepDescription;
- OSObject * prop;
-
- sleepDescription = OSDictionary::withCapacity(3);
- if (sleepDescription) {
- prop = getPMRootDomain()->copyProperty(kRootDomainSleepReasonKey);
- if (prop) {
- sleepDescription->setObject(gIOSystemStateSleepDescriptionReasonKey, prop);
- OSSafeReleaseNULL(prop);
- }
- prop = getPMRootDomain()->copyProperty(kIOHibernateStateKey);
- if (prop) {
- sleepDescription->setObject(gIOSystemStateSleepDescriptionHibernateStateKey, prop);
- OSSafeReleaseNULL(prop);
- }
- if (hibernate) {
- uint32_t correctHibernateState = kIOSystemStateSleepDescriptionHibernateStateHibernating;
- OSData *correctHibernateStateData = OSData::withValue(correctHibernateState);
- assert(correctHibernateStateData != NULL);
- sleepDescription->setObject(gIOSystemStateSleepDescriptionHibernateStateKey, correctHibernateStateData);
- OSSafeReleaseNULL(correctHibernateStateData);
- }
- getSystemStateNotificationService()->StateNotificationItemSet(gIOSystemStateSleepDescriptionKey, sleepDescription);
- OSSafeReleaseNULL(sleepDescription);
- }
- }
- if (!IsIOServiceSystemStateOff(systemState)) {
- OSDictionary * wakeDescription;
- OSObject * prop;
- char wakeReasonString[128];
-
- wakeDescription = OSDictionary::withCapacity(2);
- if (wakeDescription) {
- wakeReasonString[0] = 0;
- getPMRootDomain()->copyWakeReasonString(wakeReasonString, sizeof(wakeReasonString));
-
- if (wakeReasonString[0]) {
- prop = OSString::withCString(&wakeReasonString[0]);
- wakeDescription->setObject(gIOSystemStateWakeDescriptionWakeReasonKey, prop);
- OSSafeReleaseNULL(prop);
- }
-#if defined(__arm__) || defined(__arm64__)
- prop = OSNumber::withNumber(ml_get_conttime_offset(), sizeof(uint64_t) * CHAR_BIT);
- wakeDescription->setObject(gIOSystemStateWakeDescriptionContinuousTimeOffsetKey, prop);
- OSSafeReleaseNULL(prop);
-#endif /* defined(__arm__) || defined(__arm64__) */
- getSystemStateNotificationService()->StateNotificationItemSet(gIOSystemStateWakeDescriptionKey, wakeDescription);
- OSSafeReleaseNULL(wakeDescription);
- }
- }
-
- IOLockLock(fLock);
-
- services = OSArray::withArray(fServices);
-
- bool allPowerStates __block = 0;
- // any service on?
- fServices->iterateObjects(^bool (OSObject * obj) {
- int service __unused; // hide outer defn
- IOService * nextService;
- nextService = (IOService *) obj;
- allPowerStates = nextService->reserved->uvars->powerState;
- // early terminate if true
- return allPowerStates;
- });
-
- // figure what phase this DK server process will be suspended in,
- // and make sure its power changes complete before suspension
-
- bool effectiveOff = IsIOServiceSystemStateOff(systemState) && !allPowerStates;
- effectiveOff |= ((kIOServiceSystemStateOffPhase1 == systemState) && !fSystemOffPhase2Allow);
- effectiveOff |= (kIOServiceSystemStateOffPhase2 == systemState);
- effectiveOff |= ((kIOServiceSystemStateAOT == systemState) && !fAOTAllow);
-
- DKLOG(DKS "::systemPower(0x%x) effective %d current %d\n", DKN(this), systemState, !effectiveOff, allPowerStates != 0);
-
- if (effectiveOff) {
- fSystemPowerAck = allPowerStates;
- if (!fSystemPowerAck) {
- fSystemOff = true;
- }
- IOLockUnlock(fLock);
-
- if (!fSystemPowerAck) {
- serverAck();
- } else {
- if (services) {
- services->iterateObjects(^bool (OSObject * obj) {
- int service __unused; // hide outer defn
- IOService * nextService;
- nextService = (IOService *) obj;
- if (kIODKLogPM & gIODKDebug) {
- DKLOG("changePowerStateWithOverrideTo(" DKS ", %d)\n", DKN(nextService), 0);
- }
- nextService->reserved->uvars->powerOverride = nextService->reserved->uvars->userServerPM ? kUserServerMaxPowerState : nextService->getPowerState();
- nextService->changePowerStateWithOverrideTo(0, 0);
- return false;
- });
- }
- }
- } else if (fSystemOff) {
- fSystemOff = false;
-
- if (fSuspended) {
- KDBG(MACHDBG_CODE(DBG_MACH_SCHED, MACH_SUSPEND_DRIVERKIT_USERSPACE) | DBG_FUNC_END,
- task_pid(fOwningTask));
- task_resume_internal(fOwningTask);
- DKLOG(DKS " did task_resume_internal\n", DKN(this));
- fSuspended = false;
- }
-
- IOLockUnlock(fLock);
- if (services) {
- services->iterateObjects(^bool (OSObject * obj) {
- int service __unused; // hide outer defn
- IOService * nextService;
- nextService = (IOService *) obj;
- if (-1U != nextService->reserved->uvars->powerOverride) {
- if (kIODKLogPM & gIODKDebug) {
- DKLOG("%schangePowerStateWithOverrideTo(" DKS ", %d)\n", nextService->reserved->uvars->resetPowerOnWake ? "!" : "", DKN(nextService), nextService->reserved->uvars->powerOverride);
- }
- if (!nextService->reserved->uvars->resetPowerOnWake) {
- nextService->changePowerStateWithOverrideTo(nextService->reserved->uvars->powerOverride, 0);
- }
- nextService->reserved->uvars->powerOverride = -1U;
- }
- return false;
- });
- }
- } else {
- IOLockUnlock(fLock);
- serverAck();
- }
- OSSafeReleaseNULL(services);
-}
-
-void
-IOUserServer::systemHalt(int howto)
-{
- OSArray * services;
-
- if (true || (kIODKLogPM & gIODKDebug)) {
- DKLOG(DKS "::systemHalt()\n", DKN(this));
- }
-
- {
- OSDictionary * haltDescription;
- OSNumber * state;
- uint64_t haltStateFlags;
-
- haltDescription = OSDictionary::withCapacity(4);
- if (haltDescription) {
- haltStateFlags = 0;
- if (RB_HALT & howto) {
- haltStateFlags |= kIOServiceHaltStatePowerOff;
- } else {
- haltStateFlags |= kIOServiceHaltStateRestart;
- }
- state = OSNumber::withNumber(haltStateFlags, 64);
- haltDescription->setObject(gIOSystemStateHaltDescriptionHaltStateKey, state);
- getSystemStateNotificationService()->StateNotificationItemSet(gIOSystemStateHaltDescriptionKey, haltDescription);
-
- OSSafeReleaseNULL(state);
- OSSafeReleaseNULL(haltDescription);
- }
- }
-
- IOLockLock(fLock);
- services = OSArray::withArray(fServices);
- IOLockUnlock(fLock);
-
- if (services) {
- services->iterateObjects(^bool (OSObject * obj) {
- int service __unused; // hide outer defn
- IOService * nextService;
- IOService * provider;
- IOOptionBits terminateOptions;
- bool root;
-
- nextService = (IOService *) obj;
- provider = nextService->getProvider();
- if (!provider) {
- DKLOG("stale service " DKS " found, skipping termination\n", DKN(nextService));
- return false;
- }
- root = (NULL == provider->getProperty(gIOUserServerNameKey, gIOServicePlane));
- if (true || (kIODKLogPM & gIODKDebug)) {
- DKLOG("%d: terminate(" DKS ")\n", root, DKN(nextService));
- }
- if (!root) {
- return false;
- }
- if (nextService->reserved && nextService->reserved->uvars) {
- if (nextService->reserved->uvars->started) {
- terminateOptions = kIOServiceRequired | kIOServiceTerminateNeedWillTerminate;
- if (!nextService->terminate(terminateOptions)) {
- IOLog("failed to terminate service %s-0x%llx\n", nextService->getName(), nextService->getRegistryEntryID());
- }
- } else {
- IOLog("service %s-0x%llx not started, skipped termination\n", nextService->getName(), nextService->getRegistryEntryID());
- }
- }
- return false;
- });
- }
- OSSafeReleaseNULL(services);
-}
-
-void
-IOUserServer::powerSourceChanged(bool acAttached)
-{
- OSDictionary * powerSourceDescription;
-
- powerSourceDescription = OSDictionary::withCapacity(4);
- if (!powerSourceDescription) {
- return;
- }
- powerSourceDescription->setObject(gIOSystemStatePowerSourceDescriptionACAttachedKey, acAttached ? kOSBooleanTrue : kOSBooleanFalse);
- getSystemStateNotificationService()->StateNotificationItemSet(gIOSystemStatePowerSourceDescriptionKey, powerSourceDescription);
-
- OSSafeReleaseNULL(powerSourceDescription);
-}
-
-IOReturn
-IOUserServer::serviceStarted(IOService * service, IOService * provider, bool result)
-{
- IOReturn ret;
- bool needStop = false;
-
- DKLOG(DKS "::start(" DKS ") %s\n", DKN(service), DKN(provider), result ? "ok" : "fail");
-
- if (!result) {
- if (!service->reserved->uvars->instantiated && provider) {
- // Object instantiation did not happen. This can happen if,
- // 1. Dext crashed, in which case the user server has been terminated when the task is marked as corpse
- // 2. Kernel IOService failed start, and it did not attempt Start
- // A rematch should be attempted for 1, not 2
- bool shouldReRegister = true;
- if (lockForArbitration()) {
- shouldReRegister = (__state[0] & kIOServiceInactiveState);
- unlockForArbitration();
- }
- if (shouldReRegister) {
- provider->registerService(kIOServiceAsynchronous);
- }
- }
- ret = kIOReturnSuccess;
- return ret;
- }
-
- ret = serviceJoinPMTree(service);
-
- IOLockLock(service->reserved->uvars->uvarsLock);
- service->reserved->uvars->started = true;
- needStop = service->reserved->uvars->needStop;
- IOLockUnlock(service->reserved->uvars->uvarsLock);
- if (needStop) {
- serviceStop(service, provider);
- return kIOReturnSuccess;
- }
-
- if (service->reserved->uvars->deferredRegisterService) {
- service->registerService(kIOServiceAsynchronous | kIOServiceDextRequirePowerForMatching);
- service->reserved->uvars->deferredRegisterService = false;
- }
-
- return kIOReturnSuccess;
-}
-
-
-IOReturn
-IOUserServer::serviceOpen(IOService * provider, IOService * client)
-{
- OSObjectUserVars * uvars;
- IOReturn ret;
-
- IOLockLock(client->reserved->uvars->uvarsLock);
- uvars = client->reserved->uvars;
- if (uvars->willTerminate || uvars->stopped) {
- DKLOG(DKS "- " DKS " blocked attempt to open " DKS "\n", DKN(this), DKN(client), DKN(provider));
- ret = kIOReturnBadArgument;
- } else {
- if (!uvars->openProviders) {
- uvars->openProviders = OSArray::withObjects((const OSObject **) &provider, 1);
- } else if (-1U == uvars->openProviders->getNextIndexOfObject(provider, 0)) {
- uvars->openProviders->setObject(provider);
- }
- ret = kIOReturnSuccess;
- }
-
- IOLockUnlock(client->reserved->uvars->uvarsLock);
-
- return ret;
-}
-
-IOReturn
-IOUserServer::serviceClose(IOService * provider, IOService * client)
-{
- OSObjectUserVars * uvars;
- unsigned int idx;
- IOReturn ret;
-
- IOLockLock(client->reserved->uvars->uvarsLock);
- uvars = client->reserved->uvars;
- if (!uvars->openProviders) {
- ret = kIOReturnNotOpen;
- goto finish;
- }
- idx = uvars->openProviders->getNextIndexOfObject(provider, 0);
- if (-1U == idx) {
- ret = kIOReturnNotOpen;
- goto finish;
- }
- uvars->openProviders->removeObject(idx);
- if (!uvars->openProviders->getCount()) {
- OSSafeReleaseNULL(uvars->openProviders);
- }
-
- ret = kIOReturnSuccess;
-
-finish:
- IOLockUnlock(client->reserved->uvars->uvarsLock);
- if (kIODKLogSetup & gIODKDebug) {
- DKLOG(DKS "::serviceClose(" DKS ", " DKS ") -> %x\n", DKN(this), DKN(provider), DKN(client), ret);
- }
-
- return ret;
-}
-
-
-IOReturn
-IOUserServer::serviceStop(IOService * service, IOService *provider)
-{
- IOReturn ret;
- uint32_t idx;
- bool pmAck;
- bool deferred = false;
- OSObjectUserVars * uvars = service->reserved->uvars;
-
- IOLockLock(uvars->uvarsLock);
- if (!uvars->started) {
- // started will be set, at a later point
- uvars->needStop = true;
- deferred = true;
- }
- IOLockUnlock(uvars->uvarsLock);
- if (deferred) {
- return kIOReturnSuccess;
- }
-
- pmAck = false;
- IOLockLock(fLock);
- idx = fServices->getNextIndexOfObject(service, 0);
- if (-1U != idx) {
- fServices->removeObject(idx);
-
- // Remove the service from IOAssociatedServices
- OSObject * serviceArrayObj = copyProperty(gIOAssociatedServicesKey);
- OSArray * serviceArray = OSDynamicCast(OSArray, serviceArrayObj);
- assert(serviceArray != NULL);
-
- serviceArray = OSDynamicCast(OSArray, serviceArray->copyCollection());
- assert(serviceArray != NULL);
-
- // Index should be the same as it was in fServices
- OSNumber * __assert_only registryEntryID = OSDynamicCast(OSNumber, serviceArray->getObject(idx));
- assert(registryEntryID);
-
- // ensure it is the right service
- assert(registryEntryID->unsigned64BitValue() == service->getRegistryEntryID());
- serviceArray->removeObject(idx);
-
- setProperty(gIOAssociatedServicesKey, serviceArray);
- OSSafeReleaseNULL(serviceArray);
- OSSafeReleaseNULL(serviceArrayObj);
-
- uvars->stopped = true;
- uvars->powerState = 0;
-
- bool allPowerStates __block = 0;
- // any service on?
- fServices->iterateObjects(^bool (OSObject * obj) {
- int service __unused; // hide outer defn
- IOService * nextService;
- nextService = (IOService *) obj;
- allPowerStates = nextService->reserved->uvars->powerState;
- // early terminate if true
- return allPowerStates;
- });
-
- if (!allPowerStates && (pmAck = fSystemPowerAck)) {
- fSystemPowerAck = false;
- fSystemOff = true;
- }
- }
- IOLockUnlock(fLock);
- if (pmAck) {
- serverAck();
- }
-
- if (-1U == idx) {
- if (kIODKLogSetup & gIODKDebug) {
- DKLOG(DKS "::serviceStop(" DKS ", " DKS "): could not find service\n", DKN(this), DKN(service), DKN(provider));
- }
- return kIOReturnSuccess;
- }
-
- (void) service->deRegisterInterestedDriver(this);
- if (uvars->userServerPM) {
- IOPMrootDomain * rootDomain = getPMRootDomain();
- service->PMstop();
- service->acknowledgeSetPowerState();
- if (uvars->pmAssertions) {
- uvars->pmAssertions->iterateObjects(^(OSObject * obj) {
- rootDomain->releasePMAssertion(((OSNumber *)obj)->unsigned64BitValue());
- return false;
- });
- OSSafeReleaseNULL(uvars->pmAssertions);
- }
- if (uvars->pmAssertionsSynced) {
- uvars->pmAssertionsSynced->iterateObjects(^(OSObject * obj) {
- rootDomain->releaseDriverKitSyncedAssertion(((OSNumber *)obj)->unsigned64BitValue());
- return false;
- });
- OSSafeReleaseNULL(uvars->pmAssertionsSynced);
- }
- }
- if (kIODKLogSetup & gIODKDebug) {
- DKLOG(DKS "::serviceStop(" DKS ", " DKS ")\n", DKN(this), DKN(service), DKN(provider));
- }
-
- ret = kIOReturnSuccess;
- return ret;
-}
-
-void
-IOUserServer::serviceFree(IOService * service)
-{
- OSObjectUserVars * uvars;
- uint32_t idx, queueAlloc;
- IODispatchQueue ** unboundedQueueArray = NULL;
-
- uvars = service->reserved->uvars;
- if (!uvars) {
- return;
- }
- if (uvars->queueArray && uvars->userMeta) {
- queueAlloc = 1;
- if (uvars->userMeta->queueNames) {
- queueAlloc += uvars->userMeta->queueNames->count;
- }
- for (idx = 0; idx < queueAlloc; idx++) {
- OSSafeReleaseNULL(uvars->queueArray[idx]);
- }
- unboundedQueueArray = uvars->queueArray.data();
- IOSafeDeleteNULL(unboundedQueueArray, IODispatchQueue *, queueAlloc);
- uvars->queueArray = OSBoundedArrayRef<IODispatchQueue *>();
- }
- OSSafeReleaseNULL(uvars->userServer);
- IOLockFree(uvars->uvarsLock);
- OSSafeReleaseNULL(service->reserved->uvars->originalProperties);
- IOFreeType(service->reserved->uvars, OSObjectUserVars);
-}
-
-void
-IOUserServer::serviceWillTerminate(IOService * client, IOService * provider, IOOptionBits options)
-{
- IOReturn ret;
- bool willTerminate;
-
- willTerminate = false;
- IOLockLock(client->reserved->uvars->uvarsLock);
- if (!client->reserved->uvars->serverDied
- && !client->reserved->uvars->willTerminate) {
- client->reserved->uvars->willTerminate = true;
- willTerminate = true;
- }
- IOLockUnlock(client->reserved->uvars->uvarsLock);
- if (kIODKLogSetup & gIODKDebug) {
- DKLOG("serviceWillTerminate(" DKS ", " DKS ")\n", DKN(client), DKN(provider));
- }
-
- if (willTerminate) {
- if (provider->isInactive() || IOServicePH::serverSlept()) {
- if (kIODKLogIPC & gIODKDebug) {
- DKLOG(DKS "->Stop_async(" DKS ")\n", DKN(client), DKN(provider));
- }
- client->Stop_async(provider);
- ret = kIOReturnOffline;
- } else {
- if (kIODKLogIPC & gIODKDebug) {
- DKLOG(DKS "->Stop(" DKS ")\n", DKN(client), DKN(provider));
- }
- ret = client->Stop(provider);
- if (kIODKLogIPC & gIODKDebug) {
- DKLOG(DKS "->Stop(" DKS ") returned %x\n", DKN(client), DKN(provider), ret);
- }
- }
- if (kIOReturnSuccess != ret) {
- IOUserServer::serviceDidStop(client, provider);
- ret = kIOReturnSuccess;
- }
- }
-}
-
-void
-IOUserServer::serviceDidTerminate(IOService * client, IOService * provider, IOOptionBits options, bool * defer)
-{
- IOLockLock(client->reserved->uvars->uvarsLock);
- client->reserved->uvars->didTerminate = true;
- if (!client->reserved->uvars->serverDied
- && !client->reserved->uvars->stopped) {
- *defer = true;
- }
- IOLockUnlock(client->reserved->uvars->uvarsLock);
- if (kIODKLogSetup & gIODKDebug) {
- DKLOG("serviceDidTerminate(" DKS ", " DKS ") -> defer %d\n", DKN(client), DKN(provider), *defer);
- }
-}
-
-void
-IOUserServer::serviceDidStop(IOService * client, IOService * provider)
-{
- bool complete;
- OSArray * closeArray;
-
- complete = false;
- closeArray = NULL;
-
- IOLockLock(client->reserved->uvars->uvarsLock);
- if (client->reserved->uvars
- && client->reserved->uvars->willTerminate
- && !client->reserved->uvars->stopped) {
- client->reserved->uvars->stopped = true;
- complete = client->reserved->uvars->didTerminate;
- }
-
- if (client->reserved->uvars) {
- closeArray = client->reserved->uvars->openProviders;
- client->reserved->uvars->openProviders = NULL;
- }
- IOLockUnlock(client->reserved->uvars->uvarsLock);
-
- if (kIODKLogSetup & gIODKDebug) {
- DKLOG("serviceDidStop(" DKS ", " DKS ") -> complete %d\n", DKN(client), DKN(provider), complete);
- }
-
- if (closeArray) {
- closeArray->iterateObjects(^bool (OSObject * obj) {
- IOService * toClose;
- toClose = OSDynamicCast(IOService, obj);
- if (toClose) {
- DKLOG(DKS ":force close (" DKS ")\n", DKN(client), DKN(toClose));
- toClose->close(client);
- }
- return false;
- });
- closeArray->release();
- }
-
- if (complete) {
- bool defer = false;
- client->didTerminate(provider, 0, &defer);
- }
-}
-
-kern_return_t
-IOService::ClientCrashed_Impl(
- IOService * client,
- uint64_t options)
-{
- return kIOReturnUnsupported;
-}
-
-kern_return_t
-IOService::Stop_Impl(
- IOService * provider)
-{
- IOUserServer::serviceDidStop(this, provider);
-
- return kIOReturnSuccess;
-}
-
-void
-IOService::Stop_async_Impl(
- IOService * provider)
-{
-}
-
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-
-#undef super
-#define super IOUserClient
-
-OSDefineMetaClassAndStructors(IOUserUserClient, IOUserClient)
-
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-
-bool
-IOUserUserClient::init(OSDictionary * properties)
-{
- if (!super::init(properties)) {
- return false;
- }
-
- fWorkGroups = OSDictionary::withCapacity(0);
- if (fWorkGroups == NULL) {
- return false;
- }
-
- fEventLinks = OSDictionary::withCapacity(0);
- if (fEventLinks == NULL) {
- return false;
- }
-
- fLock = IOLockAlloc();
-
- return true;
-}
-
-void
-IOUserUserClient::free()
-{
- OSSafeReleaseNULL(fWorkGroups);
- OSSafeReleaseNULL(fEventLinks);
- if (fLock) {
- IOLockFree(fLock);
- }
-
- super::free();
-}
-
-IOReturn
-IOUserUserClient::setTask(task_t task)
-{
- task_reference(task);
- fTask = task;
-
- return kIOReturnSuccess;
-}
-
-void
-IOUserUserClient::stop(IOService * provider)
-{
- if (fTask) {
- task_deallocate(fTask);
- fTask = NULL;
- }
- super::stop(provider);
-}
-
-IOReturn
-IOUserUserClient::clientClose(void)
-{
- terminate(kIOServiceTerminateNeedWillTerminate);
- return kIOReturnSuccess;
-}
-
-IOReturn
-IOUserUserClient::setProperties(OSObject * properties)
-{
- IOReturn ret = kIOReturnUnsupported;
- return ret;
-}
-
-// p1 - name of object
-// p2 - length of object name
-// p3 - mach port name
-
-kern_return_t
-IOUserUserClient::eventlinkConfigurationTrap(void * p1, void * p2, void * p3, void * p4, void * p5, void * p6)
-{
- user_addr_t userObjectName = (user_addr_t)p1;
- mach_port_name_t portName = (mach_port_name_t)(uintptr_t)p3;
- mach_port_t port = MACH_PORT_NULL;
- char eventlinkName[kIOEventLinkMaxNameLength + 1] = {0};
- size_t eventLinkNameLen;
- OSString * eventlinkNameStr = NULL; // must release
- IOEventLink * eventLink = NULL; // do not release
- kern_return_t ret;
-
- ret = copyinstr(userObjectName, &eventlinkName[0], sizeof(eventlinkName), &eventLinkNameLen);
- if (ret != kIOReturnSuccess) {
- goto finish;
- }
-
- // ensure string length matches trap argument
- if (eventLinkNameLen != (size_t)p2 + 1) {
- ret = kIOReturnBadArgument;
- goto finish;
- }
-
- eventlinkNameStr = OSString::withCStringNoCopy(eventlinkName);
- if (eventlinkNameStr == NULL) {
- ret = kIOReturnNoMemory;
- goto finish;
- }
-
- IOLockLock(fLock);
- eventLink = OSDynamicCast(IOEventLink, fEventLinks->getObject(eventlinkNameStr));
- if (eventLink) {
- eventLink->retain();
- }
- IOLockUnlock(fLock);
-
- if (eventLink == NULL) {
- ret = kIOReturnNotFound;
- goto finish;
- }
-
- ret = iokit_lookup_raw_current_task(portName, IKOT_EVENTLINK, &port);
- if (ret != kIOReturnSuccess) {
- goto finish;
- }
-
- ret = eventLink->SetEventlinkPort(port);
- if (ret != kIOReturnSuccess) {
- if (kIODKLogSetup & gIODKDebug) {
- DKLOG(DKS " %s SetEventlinkPort() returned %x\n", DKN(this), eventlinkNameStr->getCStringNoCopy(), ret);
- }
- goto finish;
- }
-
-finish:
- if (port != NULL) {
- iokit_release_port_send(port);
- }
-
- OSSafeReleaseNULL(eventlinkNameStr);
- OSSafeReleaseNULL(eventLink);
-
- return ret;
-}
-
-kern_return_t
-IOUserUserClient::workgroupConfigurationTrap(void * p1, void * p2, void * p3, void * p4, void * p5, void * p6)
-{
- user_addr_t userObjectName = (user_addr_t)p1;
- mach_port_name_t portName = (mach_port_name_t)(uintptr_t)p3;
- mach_port_t port = MACH_PORT_NULL;
- char workgroupName[kIOWorkGroupMaxNameLength + 1] = {0};
- size_t workgroupNameLen;
- OSString * workgroupNameStr = NULL; // must release
- IOWorkGroup * workgroup = NULL; // do not release
- kern_return_t ret;
-
- ret = copyinstr(userObjectName, &workgroupName[0], sizeof(workgroupName), &workgroupNameLen);
- if (ret != kIOReturnSuccess) {
- goto finish;
- }
-
- // ensure string length matches trap argument
- if (workgroupNameLen != (size_t)p2 + 1) {
- ret = kIOReturnBadArgument;
- goto finish;
- }
-
- workgroupNameStr = OSString::withCStringNoCopy(workgroupName);
- if (workgroupNameStr == NULL) {
- ret = kIOReturnNoMemory;
- goto finish;
- }
-
- IOLockLock(fLock);
- workgroup = OSDynamicCast(IOWorkGroup, fWorkGroups->getObject(workgroupNameStr));
- if (workgroup) {
- workgroup->retain();
- }
- IOLockUnlock(fLock);
-
- if (workgroup == NULL) {
- ret = kIOReturnNotFound;
- goto finish;
- }
-
- ret = iokit_lookup_raw_current_task(portName, IKOT_WORK_INTERVAL, &port);
- if (ret != kIOReturnSuccess) {
- goto finish;
- }
-
- ret = workgroup->SetWorkGroupPort(port);
- if (ret != kIOReturnSuccess) {
- if (kIODKLogSetup & gIODKDebug) {
- DKLOG(DKS " %s SetWorkGroupPort() returned %x\n", DKN(this), workgroupNameStr->getCStringNoCopy(), ret);
- }
- goto finish;
- }
-
-finish:
-
- if (port != NULL) {
- iokit_release_port_send(port);
- }
-
- OSSafeReleaseNULL(workgroupNameStr);
- OSSafeReleaseNULL(workgroup);
-
- return ret;
-}
-
-IOExternalTrap *
-IOUserUserClient::getTargetAndTrapForIndex( IOService **targetP, UInt32 index )
-{
- static const OSBoundedArray<IOExternalTrap, 2> trapTemplate = {{
- { NULL, (IOTrap) & IOUserUserClient::eventlinkConfigurationTrap},
- { NULL, (IOTrap) & IOUserUserClient::workgroupConfigurationTrap},
- }};
- if (index >= trapTemplate.size()) {
- return NULL;
- }
- *targetP = this;
- return (IOExternalTrap *)&trapTemplate[index];
-}
-
-kern_return_t
-IOUserClient::CopyClientEntitlements_Impl(OSDictionary ** entitlements)
-{
- return kIOReturnUnsupported;
-};
-
-struct IOUserUserClientActionRef {
- OSAsyncReference64 asyncRef;
-};
-
-void
-IOUserClient::KernelCompletion_Impl(
- OSAction * action,
- IOReturn status,
- const unsigned long long * asyncData,
- uint32_t asyncDataCount)
-{
- IOUserUserClientActionRef * ref;
-
- ref = (typeof(ref))action->GetReference();
-
- IOUserClient::sendAsyncResult64(ref->asyncRef, status, (io_user_reference_t *) asyncData, asyncDataCount);
-}
-
-kern_return_t
-IOUserClient::_ExternalMethod_Impl(
- uint64_t selector,
- const unsigned long long * scalarInput,
- uint32_t scalarInputCount,
- OSData * structureInput,
- IOMemoryDescriptor * structureInputDescriptor,
- unsigned long long * scalarOutput,
- uint32_t * scalarOutputCount,
- uint64_t structureOutputMaximumSize,
- OSData ** structureOutput,
- IOMemoryDescriptor * structureOutputDescriptor,
- OSAction * completion)
-{
- return kIOReturnUnsupported;
-}
-
-IOReturn
-IOUserUserClient::clientMemoryForType(UInt32 type,
- IOOptionBits * koptions,
- IOMemoryDescriptor ** kmemory)
-{
- IOReturn kr;
- uint64_t options;
- IOMemoryDescriptor * memory;
-
- kr = CopyClientMemoryForType(type, &options, &memory);
-
- *koptions = 0;
- *kmemory = NULL;
- if (kIOReturnSuccess != kr) {
- return kr;
- }
-
- if (kIOUserClientMemoryReadOnly & options) {
- *koptions |= kIOMapReadOnly;
- }
- *kmemory = memory;
-
- return kr;
-}
-
-IOReturn
-IOUserUserClient::externalMethod(uint32_t selector, IOExternalMethodArguments * args,
- IOExternalMethodDispatch * dispatch, OSObject * target, void * reference)
-{
- IOReturn kr;
- OSData * structureInput;
- OSData * structureOutput;
- size_t copylen;
- uint64_t structureOutputSize;
- OSAction * action;
- IOUserUserClientActionRef * ref;
- mach_port_t wake_port = MACH_PORT_NULL;
-
- kr = kIOReturnUnsupported;
- structureInput = NULL;
- action = NULL;
- ref = NULL;
-
- if (args->structureInputSize) {
- structureInput = OSData::withBytesNoCopy((void *) args->structureInput, args->structureInputSize);
- }
-
- if (MACH_PORT_NULL != args->asyncWakePort) {
- // this retain is for the OSAction to release
- wake_port = ipc_port_make_send_mqueue(args->asyncWakePort);
- kr = CreateActionKernelCompletion(sizeof(IOUserUserClientActionRef), &action);
- assert(KERN_SUCCESS == kr);
- ref = (typeof(ref))action->GetReference();
- bcopy(args->asyncReference, &ref->asyncRef[0], args->asyncReferenceCount * sizeof(ref->asyncRef[0]));
- kr = action->SetAbortedHandler(^(void) {
- IOUserUserClientActionRef * ref;
- IOReturn ret;
-
- ref = (typeof(ref))action->GetReference();
- ret = releaseAsyncReference64(ref->asyncRef);
- assert(kIOReturnSuccess == ret);
- bzero(&ref->asyncRef[0], sizeof(ref->asyncRef));
- });
- assert(KERN_SUCCESS == kr);
- }
-
- if (args->structureVariableOutputData) {
- structureOutputSize = kIOUserClientVariableStructureSize;
- } else if (args->structureOutputDescriptor) {
- structureOutputSize = args->structureOutputDescriptor->getLength();
- } else {
- structureOutputSize = args->structureOutputSize;
- }
-
- kr = _ExternalMethod(selector, &args->scalarInput[0], args->scalarInputCount,
- structureInput, args->structureInputDescriptor,
- args->scalarOutput, &args->scalarOutputCount,
- structureOutputSize, &structureOutput, args->structureOutputDescriptor,
- action);
-
- OSSafeReleaseNULL(structureInput);
- OSSafeReleaseNULL(action);
-
- if (kr == kIOReturnSuccess && structureOutput) {
- if (args->structureVariableOutputData) {
- *args->structureVariableOutputData = structureOutput;
- } else {
- copylen = structureOutput->getLength();
- if (copylen > args->structureOutputSize) {
- kr = kIOReturnBadArgument;
- } else {
- bcopy((const void *) structureOutput->getBytesNoCopy(), args->structureOutput, copylen);
- args->structureOutputSize = (uint32_t) copylen;
- }
- OSSafeReleaseNULL(structureOutput);
- }
- }
-
- if (kIOReturnSuccess != kr) {
- // mig will destroy any async port
- return kr;
- }
-
- // We must never return error after this point in order to preserve MIG ownership semantics
- assert(kr == kIOReturnSuccess);
- if (MACH_PORT_NULL != wake_port) {
- // this release is for the mig created send right
- iokit_release_port_send(wake_port);
- }
-
- return kr;
-}
-
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-
-/*
- * IOUserServerCheckInToken state machine
- *
- * token
- * creation
- * |
- * |
- * v dext
- * +-----------+ check-in +-----------+
- * | Pending +--------------->| Complete |
- * +-----+-----+ +-----+-----+
- * | |
- * dext crash | | dext crash
- * before | | before server
- * check-in | | registration
- * | +-----------+ |
- * +----->| Canceled |<--------+
- * +-----------+
- */
-
-extern IORecursiveLock * gDriverKitLaunchLock;
-extern OSSet * gDriverKitLaunches;
-
-_IOUserServerCheckInCancellationHandler *
-IOUserServerCheckInToken::setCancellationHandler(IOUserServerCheckInCancellationHandler handler,
- void* handlerArgs)
-{
- _IOUserServerCheckInCancellationHandler * handlerObj = _IOUserServerCheckInCancellationHandler::withHandler(handler, handlerArgs);
- if (!handlerObj) {
- goto finish;
- }
-
- IORecursiveLockLock(gDriverKitLaunchLock);
-
- if (fState == kIOUserServerCheckInCanceled) {
- // Send cancel notification if we set the handler after this was canceled
- handlerObj->call(this);
- } else if (fState == kIOUserServerCheckInPending) {
- fHandlers->setObject(handlerObj);
- }
-
- IORecursiveLockUnlock(gDriverKitLaunchLock);
-
-finish:
- return handlerObj;
-}
-
-void
-IOUserServerCheckInToken::removeCancellationHandler(_IOUserServerCheckInCancellationHandler * handler)
-{
- IORecursiveLockLock(gDriverKitLaunchLock);
-
- fHandlers->removeObject(handler);
-
- IORecursiveLockUnlock(gDriverKitLaunchLock);
-}
-
-void
-IOUserServerCheckInToken::cancel()
-{
- IORecursiveLockLock(gDriverKitLaunchLock);
-
- if (fState != kIOUserServerCheckInCanceled) {
- // Move the state to canceled even if the token has completed
- // This is to cover the gap between dext check-in and the registration of user server
- // Cancellation listeners must be informed of a crash in between
- fState = kIOUserServerCheckInCanceled;
-
- if (gDriverKitLaunches != NULL) {
- // Remove pending launch from list, if we have not shut down yet.
- gDriverKitLaunches->removeObject(this);
- }
-
- fHandlers->iterateObjects(^bool (OSObject * obj){
- _IOUserServerCheckInCancellationHandler * handlerObj = OSDynamicCast(_IOUserServerCheckInCancellationHandler, obj);
- if (handlerObj) {
- handlerObj->call(this);
- }
- return false;
- });
- fHandlers->flushCollection();
- }
-
- IORecursiveLockUnlock(gDriverKitLaunchLock);
-}
-
-IOReturn
-IOUserServerCheckInToken::complete()
-{
- IOReturn ret;
- IORecursiveLockLock(gDriverKitLaunchLock);
-
- if (fState == kIOUserServerCheckInCanceled) {
- ret = kIOReturnError;
- } else {
- ret = kIOReturnSuccess;
- }
-
- if (fState == kIOUserServerCheckInPending) {
- fState = kIOUserServerCheckInComplete;
- if (gDriverKitLaunches != NULL) {
- // Remove pending launch from list, if we have not shut down yet.
- gDriverKitLaunches->removeObject(this);
- }
-
- // Do not flush the cancellation handlers, as we might still trigger them
- }
-
- IORecursiveLockUnlock(gDriverKitLaunchLock);
- return ret;
-}
-
-bool
-IOUserServerCheckInToken::init(const OSSymbol * serverName, OSNumber * serverTag, OSKext *driverKext, OSData *serverDUI)
-{
- if (!OSObject::init()) {
- return false;
- }
-
- if (!serverName) {
- return false;
- }
- fServerName = serverName;
- fServerName->retain();
-
- if (!serverTag) {
- return false;
- }
- fServerTag = serverTag;
- fServerTag->retain();
-
- fHandlers = OSSet::withCapacity(0);
- if (!fHandlers) {
- return false;
- }
-
- fState = kIOUserServerCheckInPending;
-
- fKextBundleID = NULL;
- fNeedDextDec = false;
-
- fExecutableName = NULL;
-
- if (driverKext) {
- fExecutableName = OSDynamicCast(OSSymbol, driverKext->getBundleExecutable());
-
- if (fExecutableName) {
- fExecutableName->retain();
- }
-
- /*
- * We need to keep track of how many dexts we have started.
- * For every new dext we are going to create a new token, and
- * we consider the token creation as the initial step to
- * create a dext as it is the data structure that will back up
- * the userspace dance to start a dext.
- * We later have to decrement only once per token.
- * If no error occurs we consider the finalize() call on IOUserServer
- * as the moment in which we do not consider the dext "alive" anymore;
- * however in case of errors we will still need to decrement the count
- * otherwise upgrades of the dext will never make progress.
- */
- if (OSKext::incrementDextLaunchCount(driverKext, serverDUI)) {
- /*
- * If fKext holds a pointer,
- * it is the indication that a decrements needs
- * to be called.
- */
- fNeedDextDec = true;
- fKextBundleID = OSDynamicCast(OSString, driverKext->getIdentifier());
- fKextBundleID->retain();
- } else {
- return false;
- }
- }
-
- return true;
-}
-
-/*
- * Returns if the dext can be re-used
- * for matching.
- */
-bool
-IOUserServerCheckInToken::dextTerminate(void)
-{
- bool ret = true;
-
- if (fNeedDextDec == true) {
- /*
- * We can decrement DextLaunchCount only
- * once per token.
- */
- ret = !(OSKext::decrementDextLaunchCount(fKextBundleID));
- fNeedDextDec = false;
- }
-
- return ret;
-}
-
-void
-IOUserServerCheckInToken::free()
-{
- OSSafeReleaseNULL(fServerName);
- OSSafeReleaseNULL(fServerTag);
- OSSafeReleaseNULL(fExecutableName);
- OSSafeReleaseNULL(fHandlers);
- if (fKextBundleID != NULL) {
- dextTerminate();
- OSSafeReleaseNULL(fKextBundleID);
- }
-
- OSObject::free();
-}
-
-const OSSymbol *
-IOUserServerCheckInToken::copyServerName() const
-{
- fServerName->retain();
- return fServerName;
-}
-
-OSNumber *
-IOUserServerCheckInToken::copyServerTag() const
-{
- fServerTag->retain();
- return fServerTag;
-}
-
-/*
- * Wait for a IOUserServer to check in
- */
-
-static
-__attribute__((noinline, not_tail_called))
-IOUserServer *
-__WAITING_FOR_USER_SERVER__(IOUserServerCheckInToken * token, uint64_t * timeoutMS)
-{
- IOUserServer * result = NULL;
- IOService * server = NULL;
- const OSSymbol * serverName = token->copyServerName();
- OSNumber * serverTag = token->copyServerTag();
- OSDictionary * matching = IOService::serviceMatching(gIOUserServerClassKey);
- uint64_t startTime = 0, endTime = 0;
-
- if (!matching || !serverName || !serverTag) {
- goto finish;
- }
- IOService::propertyMatching(gIOUserServerNameKey, serverName, matching);
- if (!(kIODKDisableDextTag & gIODKDebug)) {
- IOService::propertyMatching(gIOUserServerTagKey, serverTag, matching);
- }
-
- absolutetime_to_nanoseconds(mach_absolute_time(), &startTime);
- startTime /= NSEC_PER_MSEC;
- server = IOService::waitForMatchingServiceWithToken(matching, (*timeoutMS) * NSEC_PER_MSEC, token);
- result = OSDynamicCast(IOUserServer, server);
- if (!result) {
- // Calculate the remaining timeout if the server isn't registered
- OSSafeReleaseNULL(server);
- token->cancel();
- absolutetime_to_nanoseconds(mach_absolute_time(), &endTime);
- endTime /= NSEC_PER_MSEC;
- if (endTime > startTime) {
- if (os_sub_overflow(*timeoutMS, endTime - startTime, timeoutMS)) {
- *timeoutMS = 0;
- }
- }
- }
-
-finish:
- OSSafeReleaseNULL(matching);
- OSSafeReleaseNULL(serverName);
- OSSafeReleaseNULL(serverTag);
-
- return result;
-}
-
-IOUserServer *
-IOUserServer::launchUserServer(IOService * provider, IOService * service, OSString * bundleID, const OSSymbol * serverName, OSNumber * serverTag, bool reuseIfExists, OSData *serverDUI)
-{
- IOUserServer *me = NULL, *providerServer = NULL;
- IOUserServerCheckInToken * token = NULL;
- OSDictionary * matching = NULL; // must release
- OSKext * driverKext = NULL; // must release
- OSDextStatistics * driverStatistics = NULL; // must release
- bool reslide = false;
- uint32_t retries = kIOUserServerCheckInMaxRetry;
- uint64_t timeRemainingMS = kIOUserServerCheckInTimeoutMSecs;
-
- /* TODO: Check we are looking for same dextID
- * and if it is not the same
- * restart the matching process.
- */
- driverKext = OSKext::lookupDextWithIdentifier(bundleID, serverDUI);
- if (driverKext != NULL) {
- driverStatistics = driverKext->copyDextStatistics();
- if (driverStatistics == NULL) {
- panic("Kext %s was not a DriverKit OSKext", bundleID->getCStringNoCopy());
- }
- IOLog("Driver %s has crashed %zu time(s)\n", bundleID->getCStringNoCopy(), driverStatistics->getCrashCount());
- reslide = driverStatistics->getCrashCount() > 0;
- } else {
- DKLOG("Could not find OSKext for %s\n", bundleID->getCStringNoCopy());
- return NULL;
- }
-
- if (reuseIfExists) {
- const char * serverNameCStr;
- const char * bundleIDCStr;
- const char * endOrgCStr;
-
- serverNameCStr = serverName->getCStringNoCopy();
- bundleIDCStr = bundleID->getCStringNoCopy();
- (endOrgCStr = strchr(bundleIDCStr, '.')) && (endOrgCStr = strchr(endOrgCStr + 1, '.'));
- reuseIfExists = endOrgCStr && (0 == strncmp(bundleIDCStr, serverNameCStr, endOrgCStr + 1 - bundleIDCStr));
- if (!reuseIfExists) {
- IOLog(kIOUserServerNameKey " \"%s\" not correct organization for bundleID \"%s\"\n", serverNameCStr, bundleIDCStr);
- }
- }
-
- if (reuseIfExists) {
- // Check provider's user server, if exists
- if (provider->reserved && provider->reserved->uvars && (providerServer = provider->reserved->uvars->userServer) != NULL) {
- OSString * providerServerName = OSDynamicCast(OSString, providerServer->getProperty(gIOUserServerNameKey));
- if (providerServerName && providerServerName->isEqualTo(serverName)) {
- // Reuse is required
-
- DKLOG("using existing server " DKS " from provider " DKS "\n", DKN(providerServer), DKN(provider));
-
- // If provider has the user server that we are supposed to reuse, and it has become inactive
- // start of this service should simply fail
- // If the user server become inactive after this check, start should fail at a later stage
- if (!providerServer->isInactive()) {
- providerServer->retain();
- me = providerServer;
- } else {
- DKLOG(DKS " cannot reuse inactive server\n", DKN(service));
- // Must not create a token as reuse is required
- }
- goto finish;
- }
- }
- }
-
- do {
- const OSSymbol * tokenServerName;
- OSNumber * tokenServerTag;
-
- IORecursiveLockLock(gDriverKitLaunchLock);
-
- if (gDriverKitLaunches == NULL) {
- // About to shut down, don't launch anything
- IORecursiveLockUnlock(gDriverKitLaunchLock);
- goto finish;
- }
-
- // Find existing server
- if (reuseIfExists) {
- token = IOUserServerCheckInToken::findExistingToken(serverName);
- if (!token) {
- // Check if launch completed
-
- matching = IOService::serviceMatching(gIOUserServerClassKey);
- if (!matching) {
- IORecursiveLockUnlock(gDriverKitLaunchLock);
- goto finish;
- }
- IOService::propertyMatching(gIOUserServerNameKey, serverName, matching);
- IOService * service = IOService::copyMatchingService(matching);
- IOUserServer * userServer = OSDynamicCast(IOUserServer, service);
- if (userServer) {
- // found existing user server
- me = userServer;
- IORecursiveLockUnlock(gDriverKitLaunchLock);
- goto finish;
- } else {
- OSSafeReleaseNULL(service);
- }
- }
- }
-
- if (!token) {
- // No existing server, request launch
- token = new IOUserServerCheckInToken;
- if (!token) {
- IORecursiveLockUnlock(gDriverKitLaunchLock);
- goto finish;
- }
-
- /*
- * TODO: If the init fails because the personalities are not up to date
- * restart the whole matching process.
- */
- if (token && !token->init(serverName, serverTag, driverKext, serverDUI)) {
- DKLOG(DKS " could not initialize token\n", DKN(service));
- IORecursiveLockUnlock(gDriverKitLaunchLock);
- OSSafeReleaseNULL(token);
- goto finish;
- }
-
- /*
- * If the launch fails at any point terminate() will
- * be called on this IOUserServer.
- */
- gDriverKitLaunches->setObject(token);
- OSKext::requestDaemonLaunch(bundleID, (OSString *)serverName, serverTag, reslide ? kOSBooleanTrue : kOSBooleanFalse, token, serverDUI);
- }
-
- IORecursiveLockUnlock(gDriverKitLaunchLock);
-
- tokenServerName = token->copyServerName();
- tokenServerTag = token->copyServerTag();
- assert(tokenServerName && tokenServerTag);
- DKLOG(DKS " waiting for server %s-%llx\n", DKN(service), tokenServerName->getCStringNoCopy(), tokenServerTag->unsigned64BitValue());
-
- me = __WAITING_FOR_USER_SERVER__(token, &timeRemainingMS);
- if (me) {
- OSSafeReleaseNULL(tokenServerName);
- OSSafeReleaseNULL(tokenServerTag);
- break;
- }
- DKLOG(DKS " failed to find server %s-%llx, remaining %llums, retries %u\n", DKN(service),
- tokenServerName->getCStringNoCopy(), tokenServerTag->unsigned64BitValue(), timeRemainingMS, retries - 1);
- OSSafeReleaseNULL(tokenServerName);
- OSSafeReleaseNULL(tokenServerTag);
- OSSafeReleaseNULL(token);
- // If the loop continues it means the dext has been killed
- // We don't record a crash since start never happened. No client code has run
- } while (--retries && timeRemainingMS && !gInUserspaceReboot);
-
- if (me) {
- DKLOG(DKS " server launched, validating\n", DKN(service));
- if (token && !(kIODKDisableCheckInTokenVerification & gIODKDebug)) {
- if (!me->serviceMatchesCheckInToken(token)) {
- DKLOG(DKS " server does not match token\n", DKN(service));
- me->exit("Check In Token verification failed");
- OSSafeReleaseNULL(me);
- }
- }
- } else {
-#if DEVELOPMENT || DEBUG
- driverkit_checkin_timed_out = mach_absolute_time();
-#endif
- }
-
-finish:
- OSSafeReleaseNULL(matching);
- OSSafeReleaseNULL(driverStatistics);
- OSSafeReleaseNULL(driverKext);
- OSSafeReleaseNULL(token);
-
- return me;
-}
-
-/*
- * IOUserServerCheckInTokens are used to track dext launches. They have three possible states:
- *
- * - Pending: A dext launch is pending
- * - Canceled: Dext launch failed
- * - Complete: Dext launch is complete
- *
- * A token can be shared among multiple IOServices that are waiting for dexts if the IOUserServerName
- * is the same. This allows dexts to be reused and host multiple services. All pending tokens are stored
- * in gDriverKitLaunches and we check here before creating a new token when launching a dext.
- *
- * A token starts in the pending state with a pending count of 1. When we reuse a token, we increase the
- * pending count of the token.
- *
- * The token is sent to userspace as a mach port through kernelmanagerd/driverkitd to the dext. The dext then
- * uses that token to check in to the kernel. If any part of the dext launch failed (dext crashed, kmd crashed, etc.)
- * we get a no-senders notification for the token in the kernel and the token goes into the Canceled state.
- *
- * Once the dext checks in to the kernel, we decrement the pending count for the token. When the pending count reaches
- * 0, the token goes into the Complete state. So if the token is in the Complete state, there are no kernel matching threads
- * waiting on the dext to check in.
- */
-
-IOUserServerCheckInToken *
-IOUserServerCheckInToken::findExistingToken(const OSSymbol * serverName)
-{
- IOUserServerCheckInToken * __block result = NULL;
-
- IORecursiveLockLock(gDriverKitLaunchLock);
- if (gDriverKitLaunches == NULL) {
- goto finish;
- }
-
- gDriverKitLaunches->iterateObjects(^(OSObject * obj) {
- IOUserServerCheckInToken * token = OSDynamicCast(IOUserServerCheckInToken, obj);
- if (token) {
- // Check if server name matches
- const OSSymbol * tokenServerName = token->fServerName;
- if (tokenServerName->isEqualTo(serverName)) {
- assert(token->fState == kIOUserServerCheckInPending);
- result = token;
- result->retain();
- }
- }
- return result != NULL;
- });
-
-finish:
- IORecursiveLockUnlock(gDriverKitLaunchLock);
- return result;
-}
-
-void
-IOUserServerCheckInToken::cancelAll()
-{
- OSSet * tokensToCancel;
-
- IORecursiveLockLock(gDriverKitLaunchLock);
- tokensToCancel = gDriverKitLaunches;
- gDriverKitLaunches = NULL;
-
-
- tokensToCancel->iterateObjects(^(OSObject *obj) {
- IOUserServerCheckInToken * token = OSDynamicCast(IOUserServerCheckInToken, obj);
- if (token) {
- token->cancel();
- }
- return false;
- });
-
- IORecursiveLockUnlock(gDriverKitLaunchLock);
-
- OSSafeReleaseNULL(tokensToCancel);
-}
-
-void
-_IOUserServerCheckInCancellationHandler::call(IOUserServerCheckInToken * token)
-{
- fHandler(token, fHandlerArgs);
-}
-
-_IOUserServerCheckInCancellationHandler *
-_IOUserServerCheckInCancellationHandler::withHandler(IOUserServerCheckInCancellationHandler handler, void * args)
-{
- _IOUserServerCheckInCancellationHandler * handlerObj = NULL;
- if (!handler) {
- goto finish;
- }
-
- handlerObj = new _IOUserServerCheckInCancellationHandler;
- if (!handlerObj) {
- goto finish;
- }
-
- handlerObj->fHandler = handler;
- handlerObj->fHandlerArgs = args;
-
-finish:
- return handlerObj;
-}
-
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-
-struct IOServiceStateNotificationDispatchSource_IVars {
- IOLock * fLock;
- IOService * fStateNotification;
- IOStateNotificationListenerRef fListener;
- OSAction * fAction;
- bool fEnable;
- bool fArmed;
-};
-
-kern_return_t
-IOServiceStateNotificationDispatchSource::Create_Impl(IOService * service, OSArray * items,
- IODispatchQueue * queue, IOServiceStateNotificationDispatchSource ** outSource)
-{
- kern_return_t kr;
- IOServiceStateNotificationDispatchSource * source;
-
- source = OSTypeAlloc(IOServiceStateNotificationDispatchSource);
- source->init();
-
- source->ivars->fStateNotification = service;
- kr = service->stateNotificationListenerAdd(items, &source->ivars->fListener, ^kern_return_t () {
- OSAction * action;
-
- action = NULL;
- IOLockLock(source->ivars->fLock);
- if (source->ivars->fArmed && source->ivars->fAction) {
- source->ivars->fArmed = false;
- action = source->ivars->fAction;
- action->retain();
- }
- IOLockUnlock(source->ivars->fLock);
- if (action) {
- source->StateNotificationReady(action);
- OSSafeReleaseNULL(action);
- }
- return kIOReturnSuccess;
- });
-
- if (kIOReturnSuccess != kr) {
- OSSafeReleaseNULL(source);
- }
- *outSource = source;
-
- return kr;
-}
-
-
-bool
-IOServiceStateNotificationDispatchSource::init()
-{
- if (!IODispatchSource::init()) {
- return false;
- }
- ivars = IOMallocType(IOServiceStateNotificationDispatchSource_IVars);
- if (!ivars) {
- return false;
- }
- ivars->fLock = IOLockAlloc();
- if (!ivars->fLock) {
- return false;
- }
- ivars->fArmed = true;
-
- return true;
-}
-
-void
-IOServiceStateNotificationDispatchSource::free()
-{
- if (ivars) {
- if (ivars->fListener) {
- ivars->fStateNotification->stateNotificationListenerRemove(ivars->fListener);
- }
- if (ivars->fLock) {
- IOLockFree(ivars->fLock);
- }
- IOFreeType(ivars, IOServiceStateNotificationDispatchSource_IVars);
- }
- IODispatchSource::free();
-}
-
-kern_return_t
-IOServiceStateNotificationDispatchSource::SetHandler_Impl(OSAction * action)
-{
- IOReturn ret;
- bool notifyReady;
-
- notifyReady = false;
-
- IOLockLock(ivars->fLock);
- action->retain();
- OSSafeReleaseNULL(ivars->fAction);
- ivars->fAction = action;
- if (action) {
- notifyReady = true;
- }
- IOLockUnlock(ivars->fLock);
-
- if (notifyReady) {
- StateNotificationReady(action);
- }
- ret = kIOReturnSuccess;
-
- return ret;
-}
-
-kern_return_t
-IOServiceStateNotificationDispatchSource::SetEnableWithCompletion_Impl(
- bool enable,
- IODispatchSourceCancelHandler handler)
-{
- if (enable == ivars->fEnable) {
- return kIOReturnSuccess;
- }
-
- IOLockLock(ivars->fLock);
- ivars->fEnable = enable;
- IOLockUnlock(ivars->fLock);
-
- return kIOReturnSuccess;
-}
-
-kern_return_t
-IOServiceStateNotificationDispatchSource::Cancel_Impl(
- IODispatchSourceCancelHandler handler)
-{
- return kIOReturnUnsupported;
-}
-
-kern_return_t
-IOServiceStateNotificationDispatchSource::StateNotificationBegin_Impl(void)
-{
- IOLockLock(ivars->fLock);
- ivars->fArmed = true;
- IOLockUnlock(ivars->fLock);
-
- return kIOReturnSuccess;
-}
-
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-
-#include <IOKit/IOServiceStateNotificationEventSource.h>
-
-OSDefineMetaClassAndStructors(IOServiceStateNotificationEventSource, IOEventSource)
-OSMetaClassDefineReservedUnused(IOServiceStateNotificationEventSource, 0);
-OSMetaClassDefineReservedUnused(IOServiceStateNotificationEventSource, 1);
-OSMetaClassDefineReservedUnused(IOServiceStateNotificationEventSource, 2);
-OSMetaClassDefineReservedUnused(IOServiceStateNotificationEventSource, 3);
-OSMetaClassDefineReservedUnused(IOServiceStateNotificationEventSource, 4);
-OSMetaClassDefineReservedUnused(IOServiceStateNotificationEventSource, 5);
-OSMetaClassDefineReservedUnused(IOServiceStateNotificationEventSource, 6);
-OSMetaClassDefineReservedUnused(IOServiceStateNotificationEventSource, 7);
-
-OSPtr<IOServiceStateNotificationEventSource>
-IOServiceStateNotificationEventSource::serviceStateNotificationEventSource(IOService *service,
- OSArray * items,
- ActionBlock inAction)
-{
- kern_return_t kr;
- IOServiceStateNotificationEventSource * source;
-
- source = OSTypeAlloc(IOServiceStateNotificationEventSource);
- if (source && !source->init(service, NULL)) {
- OSSafeReleaseNULL(source);
- }
-
- if (!source) {
- return nullptr;
- }
-
- source->fStateNotification = service;
- kr = service->stateNotificationListenerAdd(items, &source->fListener, ^kern_return_t () {
- if (!source->workLoop) {
- return kIOReturnSuccess;
- }
- source->workLoop->runActionBlock(^IOReturn (void) {
- source->fArmed = true;
- return kIOReturnSuccess;
- });
- source->signalWorkAvailable();
- return kIOReturnSuccess;
- });
-
- if (kIOReturnSuccess != kr) {
- OSSafeReleaseNULL(source);
- }
-
- if (source) {
- source->setActionBlock((IOEventSource::ActionBlock) inAction);
- }
-
- return source;
-}
-
-void
-IOServiceStateNotificationEventSource::free()
-{
- if (fListener) {
- fStateNotification->stateNotificationListenerRemove(fListener);
- }
- IOEventSource::free();
-}
-
-void
-IOServiceStateNotificationEventSource::enable()
-{
- fEnable = true;
-}
-
-void
-IOServiceStateNotificationEventSource::disable()
-{
- fEnable = false;
-}
-
-void
-IOServiceStateNotificationEventSource::setWorkLoop(IOWorkLoop *inWorkLoop)
-{
- IOEventSource::setWorkLoop(inWorkLoop);
-}
-
-bool
-IOServiceStateNotificationEventSource::checkForWork()
-{
- ActionBlock intActionBlock = (ActionBlock) actionBlock;
-
- if (fArmed) {
- fArmed = false;
- (intActionBlock)();
- }
-
- return false;
-}
-
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-
-OSDefineMetaClassAndStructors(IOSystemStateNotification, IOService);
-
-class IOStateNotificationItem : public OSObject
-{
- OSDeclareDefaultStructors(IOStateNotificationItem);
-
-public:
- virtual bool init() override;
-
- OSDictionary * fValue;
- OSSet * fListeners;
-};
-OSDefineMetaClassAndStructors(IOStateNotificationItem, OSObject);
-
-
-class IOStateNotificationListener : public OSObject
-{
- OSDeclareDefaultStructors(IOStateNotificationListener);
-
-public:
- virtual bool init() override;
- virtual void free() override;
-
- IOStateNotificationHandler fHandler;
-};
-OSDefineMetaClassAndStructors(IOStateNotificationListener, OSObject);
-
-
-bool
-IOStateNotificationItem::init()
-{
- return OSObject::init();
-}
-
-bool
-IOStateNotificationListener::init()
-{
- return OSObject::init();
-}
-
-void
-IOStateNotificationListener::free()
-{
- if (fHandler) {
- Block_release(fHandler);
- }
- OSObject::free();
-}
-
-
-struct IOServiceStateChangeVars {
- IOLock * fLock;
- OSDictionary * fItems;
-};
-
-IOService *
-IOSystemStateNotification::initialize(void)
-{
- IOSystemStateNotification * me;
- IOServiceStateChangeVars * vars;
-
- me = OSTypeAlloc(IOSystemStateNotification);
- me->init();
- vars = IOMallocType(IOServiceStateChangeVars);
- me->reserved->svars = vars;
- vars->fLock = IOLockAlloc();
- vars->fItems = OSDictionary::withCapacity(16);
- {
- kern_return_t ret;
-
- gIOSystemStateSleepDescriptionKey = (OSString *)OSSymbol::withCStringNoCopy(kIOSystemStateSleepDescriptionKey);
- gIOSystemStateSleepDescriptionHibernateStateKey = OSSymbol::withCStringNoCopy(kIOSystemStateSleepDescriptionHibernateStateKey);
- gIOSystemStateSleepDescriptionReasonKey = OSSymbol::withCStringNoCopy(kIOSystemStateSleepDescriptionReasonKey);
-
- ret = me->StateNotificationItemCreate(gIOSystemStateSleepDescriptionKey, NULL);
- assert(kIOReturnSuccess == ret);
-
- gIOSystemStateWakeDescriptionKey = (OSString *)OSSymbol::withCStringNoCopy(kIOSystemStateWakeDescriptionKey);
- gIOSystemStateWakeDescriptionWakeReasonKey = OSSymbol::withCStringNoCopy(kIOSystemStateWakeDescriptionWakeReasonKey);
- gIOSystemStateWakeDescriptionContinuousTimeOffsetKey = OSSymbol::withCStringNoCopy(kIOSystemStateWakeDescriptionContinuousTimeOffsetKey);
-
-#if defined(__arm__) || defined(__arm64__)
- // Make ml_get_conttime_offset available before systemPower
- OSDictionary * wakeDescription = OSDictionary::withCapacity(1);
- OSObject * prop = OSNumber::withNumber(ml_get_conttime_offset(), sizeof(uint64_t) * CHAR_BIT);
- wakeDescription->setObject(gIOSystemStateWakeDescriptionContinuousTimeOffsetKey, prop);
- ret = me->StateNotificationItemCreate(gIOSystemStateWakeDescriptionKey, wakeDescription);
- OSSafeReleaseNULL(prop);
- OSSafeReleaseNULL(wakeDescription);
- assert(kIOReturnSuccess == ret);
-#else /* !defined(__arm__) && !defined(__arm64__) */
- ret = me->StateNotificationItemCreate(gIOSystemStateWakeDescriptionKey, NULL);
- assert(kIOReturnSuccess == ret);
-#endif /* defined(__arm__) || defined(__arm64__) */
-
- gIOSystemStateHaltDescriptionKey = (OSString *)OSSymbol::withCStringNoCopy(kIOSystemStateHaltDescriptionKey);
- gIOSystemStateHaltDescriptionHaltStateKey = OSSymbol::withCStringNoCopy(kIOSystemStateHaltDescriptionHaltStateKey);
-
- ret = me->StateNotificationItemCreate(gIOSystemStateHaltDescriptionKey, NULL);
- assert(kIOReturnSuccess == ret);
-
- gIOSystemStatePowerSourceDescriptionKey = (OSString *)OSSymbol::withCStringNoCopy(kIOSystemStatePowerSourceDescriptionKey);
- gIOSystemStatePowerSourceDescriptionACAttachedKey = OSSymbol::withCStringNoCopy(kIOSystemStatePowerSourceDescriptionACAttachedKey);
-
- ret = me->StateNotificationItemCreate(gIOSystemStatePowerSourceDescriptionKey, NULL);
- assert(kIOReturnSuccess == ret);
- }
-
- return me;
-}
-
-bool
-IOSystemStateNotification::serializeProperties(OSSerialize * s) const
-{
- IOServiceStateChangeVars * ivars = reserved->svars;
-
- bool ok;
- OSDictionary * result;
-
- result = OSDictionary::withCapacity(16);
-
- IOLockLock(ivars->fLock);
- ivars->fItems->iterateObjects(^bool (const OSSymbol * key, OSObject * object) {
- IOStateNotificationItem * item;
-
- item = (typeof(item))object;
- if (!item->fValue) {
- return false;
- }
- result->setObject(key, item->fValue);
- return false;
- });
- IOLockUnlock(ivars->fLock);
-
- ok = result->serialize(s);
- OSSafeReleaseNULL(result);
-
- return ok;
-}
-
-kern_return_t
-IOSystemStateNotification::setProperties(OSObject * properties)
-{
- kern_return_t kr;
- OSDictionary * dict;
- OSDictionary * value;
- OSString * itemName;
-
- dict = OSDynamicCast(OSDictionary, properties);
- if (!dict) {
- return kIOReturnBadArgument;
- }
-
- if (!IOCurrentTaskHasEntitlement(kIOSystemStateEntitlement)) {
- return kIOReturnNotPermitted;
- }
-
- if ((value = OSDynamicCast(OSDictionary, dict->getObject(kIOStateNotificationItemCreateKey)))) {
- itemName = OSDynamicCast(OSString, value->getObject(kIOStateNotificationNameKey));
- itemName->retain();
- value->removeObject(kIOStateNotificationNameKey);
- kr = StateNotificationItemCreate(itemName, value);
- itemName->release();
- } else if ((value = OSDynamicCast(OSDictionary, dict->getObject(kIOStateNotificationItemSetKey)))) {
- itemName = OSDynamicCast(OSString, value->getObject(kIOStateNotificationNameKey));
- itemName->retain();
- value->removeObject(kIOStateNotificationNameKey);
- kr = StateNotificationItemSet(itemName, value);
- itemName->release();
- } else {
- kr = kIOReturnError;
- }
-
- return kr;
-}
-
-kern_return_t
-IOService::CopySystemStateNotificationService_Impl(IOService ** outService)
-{
- IOService * service;
-
- service = getSystemStateNotificationService();
- service->retain();
- *outService = service;
-
- return kIOReturnSuccess;
-}
-
-IOStateNotificationItem *
-IOService::stateNotificationItemCopy(OSString * itemName, OSDictionary * initialValue)
-{
- IOServiceStateChangeVars * ivars = reserved->svars;
-
- const OSSymbol * name;
- IOStateNotificationItem * item;
-
- name = OSSymbol::withString(itemName);
-
- IOLockLock(ivars->fLock);
- if ((item = (typeof(item))ivars->fItems->getObject(name))) {
- item->retain();
- } else {
- item = OSTypeAlloc(IOStateNotificationItem);
- item->init();
- item->fListeners = OSSet::withCapacity(16);
-
- if (initialValue) {
- initialValue->retain();
- item->fValue = initialValue;
- }
- ivars->fItems->setObject(name, item);
- }
- IOLockUnlock(ivars->fLock);
-
- OSSafeReleaseNULL(name);
-
- return item;
-}
-
-kern_return_t
-IOService::StateNotificationItemCreate_Impl(OSString * itemName, OSDictionary * value)
-{
- IOStateNotificationItem * item;
-
- item = stateNotificationItemCopy(itemName, value);
- if (!item) {
- return kIOReturnNoMemory;
- }
- item->release();
-
- return kIOReturnSuccess;
-}
-
-kern_return_t
-IOService::StateNotificationItemSet_Impl(OSString * itemName, OSDictionary * value)
-{
- kern_return_t ret = kIOReturnSuccess;
- IOServiceStateChangeVars * ivars = reserved->svars;
-
- OSSet * listeners = NULL;
- IOStateNotificationItem * item;
-
- value->retain();
- IOLockLock(ivars->fLock);
- do {
- item = (typeof(item))ivars->fItems->getObject(itemName);
- if (!item) {
- ret = kIOReturnNotFound;
- value->release();
- break;
- }
- OSSafeReleaseNULL(item->fValue);
- item->fValue = value;
- if (item->fListeners->getCount()) {
- listeners = OSSet::withSet(item->fListeners);
- }
- } while (false);
- IOLockUnlock(ivars->fLock);
-
- if (listeners) {
- listeners->iterateObjects(^bool (OSObject * object) {
- IOStateNotificationListener * listener;
-
- listener = (typeof(listener))object;
- listener->fHandler();
- return false;
- });
- OSSafeReleaseNULL(listeners);
- }
-
- return ret;
-}
-
-kern_return_t
-IOService::StateNotificationItemCopy_Impl(OSString * itemName, OSDictionary ** outValue)
-{
- IOServiceStateChangeVars * ivars = reserved->svars;
-
- kern_return_t ret;
- IOStateNotificationItem * item;
- OSDictionary * value;
-
- IOLockLock(ivars->fLock);
- item = (typeof(item))ivars->fItems->getObject(itemName);
- if (item) {
- value = item->fValue;
- } else {
- value = NULL;
- }
- if (!value) {
- ret = kIOReturnNotFound;
- } else {
- value->retain();
- ret = kIOReturnSuccess;
- }
- IOLockUnlock(ivars->fLock);
-
- *outValue = value;
-
- return ret;
-}
-
-kern_return_t
-IOService::stateNotificationListenerAdd(OSArray * items,
- IOStateNotificationListenerRef * outRef,
- IOStateNotificationHandler handler)
-{
- IOServiceStateChangeVars * ivars = reserved->svars;
-
- kern_return_t kr __block;
- IOStateNotificationListener * listener;
-
- listener = OSTypeAlloc(IOStateNotificationListener);
- listener->init();
- listener->fHandler = Block_copy(handler);
-
- kr = kIOReturnSuccess;
- items->iterateObjects(^bool (OSObject * object) {
- OSString * itemName;
- IOStateNotificationItem * item;
-
- itemName = OSDynamicCast(OSString, object);
- if (!itemName) {
- kr = kIOReturnBadArgument;
- return true;
- }
- item = stateNotificationItemCopy(itemName, NULL);
- if (!item) {
- kr = kIOReturnNoMemory;
- return true;
- }
- IOLockLock(ivars->fLock);
- item->fListeners->setObject(listener);
- IOLockUnlock(ivars->fLock);
- item->release();
- return false;
- });
-
- if (kIOReturnSuccess != kr) {
- stateNotificationListenerRemove(listener);
- OSSafeReleaseNULL(listener);
- }
- *outRef = listener;
-
- return kr;
-}
-
-
-kern_return_t
-IOService::stateNotificationListenerRemove(IOStateNotificationListenerRef ref)
-{
- IOServiceStateChangeVars * ivars = reserved->svars;
-
- IOStateNotificationListener * listener;
- kern_return_t kr;
-
- kr = kIOReturnSuccess;
- listener = (typeof(listener))ref;
-
- IOLockLock(ivars->fLock);
- ivars->fItems->iterateObjects(^bool (const OSSymbol * key, OSObject * object) {
- IOStateNotificationItem * item;
-
- item = (typeof(item))object;
- item->fListeners->removeObject(listener);
- return false;
- });
- IOLockUnlock(ivars->fLock);
-
- return kr;
-}
-
-
-
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-
-kern_return_t
-IOWorkGroup::Create_Impl(OSString * name, IOUserClient * userClient, IOWorkGroup ** workgroup)
-{
- IOWorkGroup * inst = NULL;
- IOUserUserClient * uc = NULL;
- kern_return_t ret = kIOReturnError;
- IOUserServer * us;
-
- if (name == NULL) {
- ret = kIOReturnBadArgument;
- goto finish;
- }
-
- if (name->getLength() > kIOWorkGroupMaxNameLength) {
- ret = kIOReturnBadArgument;
- goto finish;
- }
-
- uc = OSDynamicCast(IOUserUserClient, userClient);
- if (uc == NULL) {
- ret = kIOReturnBadArgument;
- goto finish;
- }
-
- inst = OSTypeAlloc(IOWorkGroup);
- if (!inst->init()) {
- inst->free();
- inst = NULL;
- ret = kIOReturnNoMemory;
- goto finish;
- }
-
- us = (typeof(us))thread_iokit_tls_get(0);
- inst->ivars->userServer = OSDynamicCast(IOUserServer, us);
-
- if (inst->ivars->userServer == NULL) {
- ret = kIOReturnBadArgument;
- goto finish;
- }
- inst->ivars->userServer->retain();
-
- inst->ivars->name = name;
- inst->ivars->name->retain();
-
- inst->ivars->userClient = uc; // no retain
-
- IOLockLock(uc->fLock);
- uc->fWorkGroups->setObject(name, inst);
- IOLockUnlock(uc->fLock);
- ret = kIOReturnSuccess;
-
-finish:
- if (ret != kIOReturnSuccess) {
- OSSafeReleaseNULL(inst);
- } else {
- *workgroup = inst;
- }
-
- return ret;
-}
-
-kern_return_t
-IOWorkGroup::InvalidateKernel_Impl(IOUserClient * client)
-{
- IOUserUserClient * uc = OSDynamicCast(IOUserUserClient, client);
-
- if (uc == NULL) {
- return kIOReturnBadArgument;
- }
-
- if (uc != ivars->userClient) {
- return kIOReturnBadArgument;
- }
-
- IOLockLock(uc->fLock);
- uc->fWorkGroups->removeObject(ivars->name);
- IOLockUnlock(uc->fLock);
-
- return kIOReturnSuccess;
-}
-
-kern_return_t
-IOWorkGroup::SetWorkGroupPort_Impl(mach_port_t port)
-{
- return kIOReturnUnsupported;
-}
-
-bool
-IOWorkGroup::init()
-{
- if (!OSObject::init()) {
- return false;
- }
- ivars = IOMallocType(IOWorkGroup_IVars);
-
- return true;
-}
-
-void
-IOWorkGroup::free()
-{
- if (ivars) {
- OSSafeReleaseNULL(ivars->userServer);
- OSSafeReleaseNULL(ivars->name);
- IOFreeType(ivars, IOWorkGroup_IVars);
- }
-
- OSObject::free();
-}
-
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-
-kern_return_t
-IOEventLink::Create_Impl(OSString * name, IOUserClient * userClient, IOEventLink ** eventlink)
-{
- IOEventLink * inst = NULL;
- IOUserUserClient * uc = NULL;
- IOUserServer * us;
- kern_return_t ret = kIOReturnError;
-
- if (name == NULL) {
- ret = kIOReturnBadArgument;
- goto finish;
- }
-
- if (name->getLength() > kIOEventLinkMaxNameLength) {
- ret = kIOReturnBadArgument;
- goto finish;
- }
-
- uc = OSDynamicCast(IOUserUserClient, userClient);
- if (uc == NULL) {
- ret = kIOReturnBadArgument;
- goto finish;
- }
-
- inst = OSTypeAlloc(IOEventLink);
- if (!inst->init()) {
- inst->free();
- inst = NULL;
- ret = kIOReturnNoMemory;
- goto finish;
- }
-
- us = (typeof(us))thread_iokit_tls_get(0);
- inst->ivars->userServer = OSDynamicCast(IOUserServer, us);
-
- if (inst->ivars->userServer == NULL) {
- ret = kIOReturnBadArgument;
- goto finish;
- }
- inst->ivars->userServer->retain();
-
- inst->ivars->name = name;
- inst->ivars->name->retain();
-
- inst->ivars->userClient = uc; // no retain
-
- IOLockLock(uc->fLock);
- uc->fEventLinks->setObject(name, inst);
- IOLockUnlock(uc->fLock);
-
- ret = kIOReturnSuccess;
-
-finish:
- if (ret != kIOReturnSuccess) {
- OSSafeReleaseNULL(inst);
- } else {
- *eventlink = inst;
- }
-
- return ret;
-}
-
-kern_return_t
-IOEventLink::InvalidateKernel_Impl(IOUserClient * client)
-{
- IOUserUserClient * uc = OSDynamicCast(IOUserUserClient, client);
-
- if (uc == NULL) {
- return kIOReturnBadArgument;
- }
-
- if (uc != ivars->userClient) {
- return kIOReturnBadArgument;
- }
-
- IOLockLock(uc->fLock);
- uc->fEventLinks->removeObject(ivars->name);
- IOLockUnlock(uc->fLock);
-
- return kIOReturnSuccess;
-}
-
-bool
-IOEventLink::init()
-{
- if (!OSObject::init()) {
- return false;
- }
- ivars = IOMallocType(IOEventLink_IVars);
-
- return true;
-}
-
-void
-IOEventLink::free()
-{
- if (ivars) {
- OSSafeReleaseNULL(ivars->userServer);
- OSSafeReleaseNULL(ivars->name);
- IOFreeType(ivars, IOEventLink_IVars);
- }
-
- OSObject::free();
-}
-
-kern_return_t
-IOEventLink::SetEventlinkPort_Impl(mach_port_t port __unused)
-{
- return kIOReturnUnsupported;
-}