Skip to content

Commit

Permalink
Added support for sending hda-verbs from user-space (#614)
Browse files Browse the repository at this point in the history
  • Loading branch information
black-dragon74 authored Oct 24, 2020
1 parent 09a0177 commit 61e2bbf
Show file tree
Hide file tree
Showing 13 changed files with 974 additions and 39 deletions.
209 changes: 207 additions & 2 deletions AppleALC.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

78 changes: 78 additions & 0 deletions AppleALC/ALCUserClient/ALCUserClient.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
//
// ALCUserClient.cpp
// AppleALC
//
// Created by Nick on 10/14/20.
// Copyright © 2020 vit9696. All rights reserved.
//

#include <IOKit/IOLib.h>

#include "ALCUserClient.hpp"

OSDefineMetaClassAndStructors(ALCUserClient, IOUserClient);

const IOExternalMethodDispatch ALCUserClient::sMethods[kNumberOfMethods] = {
{ //kMethodExecuteVerb
reinterpret_cast<IOExternalMethodAction>(&ALCUserClient::methodExecuteVerb), // Method pointer
3, // Num of scalar input values
0, // Num of struct input values
1, // Num of scalar output values
0 // Num of struct output values
}
};

IOReturn ALCUserClient::externalMethod(uint32_t selector, IOExternalMethodArguments* arguments, IOExternalMethodDispatch* dispatch, OSObject* target, void* reference) {
if (selector >= kNumberOfMethods)
return kIOReturnUnsupported;

dispatch = const_cast<IOExternalMethodDispatch*>(&sMethods[selector]);

target = mProvider;
reference = NULL;

return super::externalMethod(selector, arguments, dispatch, target, reference);
}

bool ALCUserClient::initWithTask(task_t owningTask, void* securityToken, UInt32 type, OSDictionary* properties) {
if (!owningTask)
return false;

if (!super::initWithTask(owningTask, securityToken, type))
return false;

mTask = owningTask;
mProvider = NULL;

return true;
}

bool ALCUserClient::start(IOService* provider) {
bool success;

mProvider = OSDynamicCast(ALCUserClientProvider, provider);
success = (mProvider != NULL);

if (success)
success = super::start(provider);

return success;
}

IOReturn ALCUserClient::clientClose() {
if (!isInactive())
terminate();

return kIOReturnSuccess;
}

IOReturn ALCUserClient::methodExecuteVerb(ALCUserClientProvider* target, void* ref, IOExternalMethodArguments* args) {
uint16_t nid, verb, params;

nid = static_cast<uint16_t>(args->scalarInput[0]);
verb = static_cast<uint16_t>(args->scalarInput[1]);
params = static_cast<uint16_t>(args->scalarInput[2]);

args->scalarOutput[0] = target->sendHdaCommand(nid, verb, params);
return kIOReturnSuccess;
}
43 changes: 43 additions & 0 deletions AppleALC/ALCUserClient/ALCUserClient.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
//
// ALCUserClient.hpp
// AppleALC
//
// Created by Nick on 10/14/20.
// Copyright © 2020 vit9696. All rights reserved.
//

#ifndef ALCUserClient_hpp
#define ALCUserClient_hpp

#include <IOKit/IOService.h>
#include <IOKit/IOUserClient.h>

#include "../UserKernelShared.h"
#include "../ALCUserClientProvider/ALCUserClientProvider.hpp"

class ALCUserClient : public IOUserClient {
typedef IOUserClient super;
OSDeclareDefaultStructors(ALCUserClient);

private:
ALCUserClientProvider* mProvider { nullptr };
task_t mTask;
static const IOExternalMethodDispatch sMethods[kNumberOfMethods];

public:
virtual bool start(IOService* provider) override;

virtual bool initWithTask(task_t owningTask, void* securityToken,
UInt32 type, OSDictionary* properties) override;

virtual IOReturn clientClose() override;
virtual IOReturn externalMethod(uint32_t selector, IOExternalMethodArguments* arguments,
IOExternalMethodDispatch* dispatch, OSObject* target,
void* reference) override;

protected:
static IOReturn methodExecuteVerb(ALCUserClientProvider* target, void* ref,
IOExternalMethodArguments* args);
};

#endif /* ALCUserClient_hpp */
68 changes: 68 additions & 0 deletions AppleALC/ALCUserClientProvider/ALCUserClientProvider.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
//
// ALCUserClientProvider.cpp
// AppleALC
//
// Created by Nick on 10/14/20.
// Copyright © 2020 vit9696. All rights reserved.
//

#include "ALCUserClientProvider.hpp"

OSDefineMetaClassAndStructors(ALCUserClientProvider, IOService);

bool ALCUserClientProvider::start(IOService* provider) {
if (!super::start(provider))
return false;

auto matchingDict = IOService::nameMatching(kIOHDACodecDevice);
if (!matchingDict) {
DBGLOG("client", "Failed to allocate matching dictionary");
return false;
}

mHDACodecDevice = IOService::waitForMatchingService(matchingDict, 100000000); // Wait for 0.1s
matchingDict->release();

if (!mHDACodecDevice)
{
DBGLOG("client", "Timeout in waiting for IOHDACodecDevice, will retry");
return false;
}

// We are ready for verbs
DBGLOG("client", "ALCUserClient is ready for hda-verbs");
setProperty("ReadyForALCVerbs", kOSBooleanTrue);
readyForVerbs = true;

// Publish the service
registerService();

return true;
}

void ALCUserClientProvider::stop(IOService* provider) {
super::stop(provider);

OSSafeReleaseNULL(mHDACodecDevice);
}

uint64_t ALCUserClientProvider::sendHdaCommand(uint16_t nid, uint16_t verb, uint16_t param) {
if (!readyForVerbs)
{
DBGLOG("client", "Provider not ready to accept hda-verb commands");
return kIOReturnError;
}

auto sharedAlc = AlcEnabler::getShared();

if (!sharedAlc) {
DBGLOG("client", "Unable to get shared AlcEnabler instance");
return kIOReturnError;
}

UInt ret = 0;
sharedAlc->IOHDACodecDevice_executeVerb(reinterpret_cast<void*>(mHDACodecDevice), nid, verb, param, &ret, true);
DBGLOG("client", "Send HDA command nid=0x%X, verb=0x%X, param=0x%X, result=0x%08x", nid, verb, param, ret);

return ret;
}
43 changes: 43 additions & 0 deletions AppleALC/ALCUserClientProvider/ALCUserClientProvider.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
//
// ALCUserClientProvider.hpp
// AppleALC
//
// Created by Nick on 10/14/20.
// Copyright © 2020 vit9696. All rights reserved.
//

#ifndef ALCUserClientProvider_hpp
#define ALCUserClientProvider_hpp

#define kIOHDACodecDevice "IOHDACodecDevice"

#include<IOKit/IOService.h>
#include <IOKit/IOUserClient.h>

#include "../kern_alc.hpp"

class ALCUserClientProvider : public IOService {
typedef IOService super;
OSDeclareDefaultStructors(ALCUserClientProvider);

private:
IOService* mHDACodecDevice { nullptr };
bool readyForVerbs { false };

public:
virtual bool start(IOService* provider) override;
virtual void stop(IOService* provider) override;

/**
* Called by user-client to set the codec verbs
*
* @param nid Node ID
* @param verb The hda-verb command to send (as defined in hdaverb.h)
* @param param The parameters for the verb
*
* @return kIOReturnSuccess on successful execution
*/
virtual uint64_t sendHdaCommand(uint16_t nid, uint16_t verb, uint16_t param);
};

#endif /* ALCUserClientProvider_hpp */
35 changes: 25 additions & 10 deletions AppleALC/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,25 @@
<string>????</string>
<key>CFBundleVersion</key>
<string>$(MODULE_VERSION)</string>
<key>OSBundleCompatibleVersion</key>
<string>1.0</string>
<key>IOKitPersonalities</key>
<dict>
<key>ALCUserClientProvider</key>
<dict>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>IOClass</key>
<string>ALCUserClientProvider</string>
<key>IOMatchCategory</key>
<string>ALCUserClientProvider</string>
<key>IOProbeScore</key>
<integer>1000</integer>
<key>IOProviderClass</key>
<string>IOResources</string>
<key>IOResourceMatch</key>
<string>IOKit</string>
<key>IOUserClientClass</key>
<string>ALCUserClient</string>
</dict>
<key>as.vit9696.AppleALC</key>
<dict>
<key>CFBundleIdentifier</key>
Expand All @@ -40,26 +55,26 @@
</dict>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2017 vit9696. All rights reserved.</string>
<key>OSBundleCompatibleVersion</key>
<string>1.0</string>
<key>OSBundleLibraries</key>
<dict>
<key>as.vit9696.Lilu</key>
<string>1.2.0</string>
<key>com.apple.iokit.IOPCIFamily</key>
<string>1.0.0b1</string>
<key>com.apple.kpi.bsd</key>
<string>12.0.0</string>
<key>com.apple.kpi.dsep</key>
<string>12.0.0</string>
<key>com.apple.kpi.iokit</key>
<string>12.0.0</string>
<key>com.apple.kpi.libkern</key>
<string>12.0.0</string>
<key>com.apple.kpi.mach</key>
<string>12.0.0</string>
<key>com.apple.kpi.dsep</key>
<string>12.0.0</string>
<key>com.apple.kpi.unsupported</key>
<string>12.0.0</string>
<key>com.apple.iokit.IOPCIFamily</key>
<string>1.0.0b1</string>
<key>as.vit9696.Lilu</key>
<string>1.2.0</string>
<key>as.vit9696.PinConfigs</key>
<string>1.0.0</string>
</dict>
<key>OSBundleRequired</key>
<string>Root</string>
Expand Down
18 changes: 18 additions & 0 deletions AppleALC/UserKernelShared.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//
// UserKernelShared.h
// AppleALC
//
// Created by Nick on 10/14/20.
// Copyright © 2020 vit9696. All rights reserved.
//

#ifndef UserKernelShared_h
#define UserKernelShared_h

enum {
kMethodExecuteVerb,

kNumberOfMethods // Must be last
};

#endif /* UserKernelShared_h */
29 changes: 17 additions & 12 deletions AppleALC/kern_alc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,22 @@
#include "kern_alc.hpp"
#include "kern_resources.hpp"

static AlcEnabler alcEnabler;

// Only used in apple-driven callbacks
static AlcEnabler *callbackAlc;
AlcEnabler* AlcEnabler::callbackAlc = nullptr;

void AlcEnabler::createShared() {
if (callbackAlc)
PANIC("alc", "Attempted to assign alc callback again");

callbackAlc = &alcEnabler;

if (!callbackAlc)
PANIC("alc", "Failed to assign alc callback");
}

void AlcEnabler::init() {
callbackAlc = this;

lilu.onPatcherLoadForce(
[](void *user, KernelPatcher &pathcer) {
Expand Down Expand Up @@ -317,15 +328,11 @@ bool AlcEnabler::AppleHDAController_start(IOService* service, IOService* provide
return FunctionCast(AppleHDAController_start, callbackAlc->orgAppleHDAController_start)(service, provider);
}

#ifdef DEBUG
IOReturn AlcEnabler::IOHDACodecDevice_executeVerb(void *that, uint16_t a1, uint16_t a2, uint16_t a3, unsigned int *a4, bool a5)
IOReturn AlcEnabler::IOHDACodecDevice_executeVerb(void *hdaCodecDevice, uint16_t nid, uint16_t verb, uint16_t param, unsigned int *output, bool waitForSuccess)
{
IOReturn result = FunctionCast(IOHDACodecDevice_executeVerb, callbackAlc->orgIOHDACodecDevice_executeVerb)(that, a1, a2, a3, a4, a5);
if (result != KERN_SUCCESS)
DBGLOG("alc", "IOHDACodecDevice::executeVerb with parameters a1 = %u, a2 = %u, a3 = %u failed with result = %x", a1, a2, a3, result);
return result;
DBGLOG("alc", "IOHDACodecDevice::executeVerb with parameters nid = %u, verb = %u, param = %u", nid, verb, param);
return FunctionCast(IOHDACodecDevice_executeVerb, callbackAlc->orgIOHDACodecDevice_executeVerb)(hdaCodecDevice, nid, verb, param, output, waitForSuccess);
}
#endif

uint32_t AlcEnabler::getAudioLayout(IOService *hdaDriver) {
auto parent = hdaDriver->getParentEntry(gIOServicePlane);
Expand Down Expand Up @@ -623,13 +630,11 @@ void AlcEnabler::processKext(KernelPatcher &patcher, size_t index, mach_vm_addre
eraseRedundantLogs(patcher, kextIndex);
}

#ifdef DEBUG
if (ADDPR(debugEnabled) && !(progressState & ProcessingState::PatchHDAFamily) && kextIndex == KextIdIOHDAFamily) {
if (!(progressState & ProcessingState::PatchHDAFamily) && kextIndex == KextIdIOHDAFamily) {
progressState |= ProcessingState::PatchHDAFamily;
KernelPatcher::RouteRequest request("__ZN16IOHDACodecDevice11executeVerbEtttPjb", IOHDACodecDevice_executeVerb, orgIOHDACodecDevice_executeVerb);
patcher.routeMultiple(index, &request, 1, address, size);
}
#endif

if (!(progressState & ProcessingState::PatchHDAController) && kextIndex == KextIdAppleHDAController) {
progressState |= ProcessingState::PatchHDAController;
Expand Down
Loading

0 comments on commit 61e2bbf

Please sign in to comment.