如何在IORegistry中注册键并在kext中响应其变更?
我正在学习kext开发,尝试实现一个简单的“通用”kext,使其在IORegistry中发布一个键并响应该键的变更。该类通过IOService::registerPowerDriver完成注册并发布键。根据资料:
当用户空间应用设置驱动属性时,对应驱动对象的setProperties()方法会被调用,参数包含已设置的属性字典。
我按此实现了代码,但在IORegistryExplorer.app中修改该键时,完全没有触发IOService::setProperties的调用。
头文件代码
#define ENABLED_PROP_NAME "Enabled" class GenericPower : public IOService { OSDeclareDefaultStructors(GenericPower); public: virtual bool init(OSDictionary* dict) override; virtual IOService* probe(IOService* provider, SInt32* score) override; virtual void free(void) override; virtual bool start(IOService* provider) override; virtual void stop(IOService * provider) override; virtual IOReturn setProperties(OSObject* properties) override; protected: virtual IOReturn powerStateWillChangeTo(IOPMPowerFlags capabilities, unsigned long stateNumber, IOService* whatDevice) override; virtual IOReturn setPowerState(unsigned long powerStateOrdinal, IOService* whatDevice) override; virtual IOReturn powerStateDidChangeTo(IOPMPowerFlags capabilities, unsigned long stateNumber, IOService* whatDevice) override; void updateProperties(void); private: // Property to publish: bool isEnabled = true; };
CPP文件代码
#define super IOService OSDefineMetaClassAndStructors(GenericPower, super); bool GenericPower::init(OSDictionary* dict) { if (!super::init(dict)) { IOLog("GenericPower: init failed\n"); return (false); } IOLog("GenericPower: init\n"); return (true); } void GenericPower::free(void) { IOLog("GenericPower: free\n"); super::free(); } IOService* GenericPower::probe(IOService* provider, SInt32* score) { IOLog("GenericPower: probe\n"); return (super::probe(provider, score)); } IOReturn GenericPower::setProperties(OSObject* properties) { IOLog("GenericPower: setProperties\n"); if (properties == nullptr) { IOLog("GenericPower: setProperties: no properties\n"); return (kIOReturnUnsupported); } OSDictionary* propertiesDict; propertiesDict = OSDynamicCast(OSDictionary, properties); if (propertiesDict == nullptr) { IOLog("GenericPower: setProperties: cannot get properties\n"); return (kIOReturnUnsupported); } OSObject * rawValue = propertiesDict->getObject(ENABLED_PROP_NAME); OSBoolean * boolValue = OSDynamicCast(OSBoolean, rawValue); if (boolValue == nullptr) { IOLog("GenericPower: setProperties: cannot get \"" ENABLED_PROP_NAME "\" property\n"); } else { IOLog("GenericPower: setProperties: new value %s\n", boolValue->getValue() ? "Enabled" : "Disabled"); isEnabled = boolValue->getValue(); } IOLog("GenericPower: setProperties: \"" ENABLED_PROP_NAME "\" set to %s\n", isEnabled ? "ON" : "OFF"); updateProperties(); return (kIOReturnSuccess); } enum { kPowerStateOff, kPowerStateSleep, kPowerStateOn, // kNumPowerStates }; static IOPMPowerState powerStates[kNumPowerStates] = { { // Off kIOPMPowerStateVersion1, // version 0, // capabilityFlags 0, // outputPowerCharacter 0, // inputPowerRequirement 0, 0, 0, 0, 0, 0, 0, 0 }, { // Sleep kIOPMPowerStateVersion1, // version kIOPMSleepCapability, // capabilityFlags kIOPMSleep, // outputPowerCharacter kIOPMSleep, // inputPowerRequirement 0, 0, 0, 0, 0, 0, 0, 0 }, { // On kIOPMPowerStateVersion1, // version kIOPMPowerOn | kIOPMDeviceUsable, // capabilityFlags kIOPMPowerOn, // outputPowerCharacter kIOPMPowerOn, // inputPowerRequirement 0, 0, 0, 0, 0, 0, 0, 0 }, }; bool GenericPower::start(IOService* provider) { if (!super::start(provider)) { IOLog("GenericPower: start: IOService start error\n"); return (false); } PMinit(); provider->joinPMtree(this); registerPowerDriver(this, powerStates, kNumPowerStates); makeUsable(); registerService(); updateProperties(); return (true); } void GenericPower::stop(IOService* provider) { IOLog("GenericPower: stop. isEnabled=%d\n", (int)isEnabled); PMstop(); super::stop(provider); } // ///////////////////////////////////////////////////////////// IOReturn GenericPower::powerStateWillChangeTo(IOPMPowerFlags capabilities, unsigned long stateNumber, IOService* whatDevice) { IOLog("GenericPower: powerStateWillChangeTo %lu\n", stateNumber); return (kIOPMAckImplied); } IOReturn GenericPower::setPowerState(unsigned long powerStateOrdinal, IOService* whatDevice) { IOLog("GenericPower: setPowerState %lu\n", powerStateOrdinal); return (kIOPMAckImplied); } IOReturn GenericPower::powerStateDidChangeTo(IOPMPowerFlags capabilities, unsigned long stateNumber, IOService* whatDevice) { IOLog("GenericPower: powerStateDidChangeTo %lu\n", stateNumber); return (kIOPMAckImplied); } void GenericPower::updateProperties(void) { // Properties init OSBoolean * osProp = OSBoolean::withBoolean(isEnabled); setProperty(ENABLED_PROP_NAME, osProp); OSSafeReleaseNULL(osProp); }
问题原因及解决方法
1. 权限问题
用户空间修改kext属性必须拥有root权限,IORegistryExplorer需要以sudo启动才能触发内核层的属性修改:
sudo /Applications/IORegistryExplorer.app/Contents/MacOS/IORegistryExplorer
2. 属性可写性验证
默认发布的属性可能未标记为可写,你可以在updateProperties方法中,通过IORegistryEntry::setProperty的扩展方式明确设置权限(如果需要),或者用命令行工具ioreg测试修改:
# 查找驱动节点并修改属性 sudo ioreg -w 0 -r -c GenericPower -d 1 -s Enabled -k Enabled -v -w true
3. 驱动节点挂载验证
确认IORegistryExplorer中能找到GenericPower节点,且该节点正确挂载到父设备下。如果驱动未正确匹配到设备,将无法接收属性修改消息,可检查Info.plist中IOKitPersonalities的IOProviderClass是否与父设备匹配。
4. setProperties方法覆盖确认
虽然代码中使用了override,但可在setProperties开头添加更醒目的日志(比如IOLog("=== setProperties triggered ===\n");),同时检查kext是否正确加载(用kextstat | grep GenericPower验证)。
内容的提问来源于stack exchange,提问作者BUKTOP

