Open RehabMan opened 11 years ago
Backtrace info:
Jul 11 10:33:44 localhost kernel[0]: HWSensors Project Copyright 2013 netkas, slice, usr-sse2, kozlek, navi, THe KiNG, RehabMan. All rights reserved.
Jul 11 10:33:44 localhost kernel[0]: waitForSystemMapper
Jul 11 10:33:44 localhost kernel[0]: Backtrace 0xffffff800065204f 0xffffff8000651bc9 0xffffff8000651b30 0xffffff7f810f9eac 0xffffff7f810f9789 0xffffff80006311bc 0xffffff800062d400
Jul 11 10:33:44 localhost kernel[0]: Kernel Extensions in backtrace:
Jul 11 10:33:44 localhost kernel[0]: org.netkas.driver.FakeSMC(818.0)[57213B04-1A4A-3FFD-88A5-E5F5D94ECD9A]@0xffffff7f810f4000->0xffffff7f81102fff
Jul 11 10:33:44 localhost kernel[0]: dependency: com.apple.iokit.IOACPIFamily(1.4)[A35915E8-C1B0-3C0F-81DF-5515BC9002FC]@0xffffff7f80d8d000
Hang can be a crash. Many times crash in fakesmc causes for me hangs like this, without panic. Just added check if serviceMatching returns zero.. Maybe this will help. I'll think on this code later. If you'll send me your IORegistryExplorer dump this would help.
serviceMatching is not returning NULL.
I added a log:
OSDictionary *matching = serviceMatching("IODTNVRAM");
HWSensorsInfoLog("serviceMatching(\"IODTNVRAM\") returns %p\n", matching);
Output:
Jul 11 13:09:27 localhost kernel[0]: FakeSMC: serviceMatching("IODTNVRAM") returns 0xffffff801f19a700
I would not expect it to return NULL either. All it does is construct a dictionary with matching for that service. The service does not have to exist to construct the dictionary. Only failure would be insufficient memory.
I think it has something to do with service actually being called 'AppleEFINVRAM'... Will test and report back.
Regarding to documentation, serviceMatching can return zero.
AppleEFINVRAM is inheritance of IODTNVRAM.
Anyway I am interesting in your IORegistryExplorer dump especially if you are using Chameleon/Chimera bootloader.
I realize serviceMatching can return NULL. But it isn't, so it is not the cause of this issue.
I am using Chameleon:
localhost:kozlek.git Admin$ bdmesg|head -n1
Chameleon 2.2svn (svn-r2254) [2013-07-08 21:18:27]
I will upload ioreg as soon as I figure out how with github issues...
I see serviceMatching matches against class or sub-class... Nice. So either AppleEFINVRAM or IODTNVRAM should work. I tried AppleEFINVRAM and it didn't work.
Looks like problem is caused before the code even runs. Backtrace shows up in log before suspect code is run. Could this be a bundle dependency issue? I'll do a test and report back...
You can use https://mega.co.nz for temporary encrypted uploads.
ioreg dump made with IORegistryExplorer 2.1. Should load on 3.0 too...
https://mega.co.nz/#!FYMnkAKQ!LsIHWUN3QjyuYBJuZy-Kkwzc7plcFed00ZoQcgo2Rzk
Realize I don't really understand the entire purpose of OSBundleLibraries in Info.plist, and I'm just making a guess here, but I found the kext AppleEFINVRAM.kext that has this AppleEFINVRAM class. It has a dependency on com.apple.driver.AppleEFIRuntime, and if I add that to FakeSMC Info.plist, then it works:
<key>OSBundleLibraries</key>
<dict>
+ <key>com.apple.driver.AppleEFIRuntime</key>
+ <string>1.7</string>
<key>com.apple.iokit.IOACPIFamily</key>
- <string>1.0.0d1</string>
+ <string>1.3</string>
Also, as you can see, I bumped the version number of IOACPIFamily to 1.3 (which is SL version). I did that as a first test and it didn't help. I don't think that change is necessary.
The 1.7 version for com.apple.driver.AppleEFIRuntime is from ML 10.8.4. SL is 1.4 so perhaps it should use that version. I will try that...
Or maybe the dependency should be on com.apple.driver.AppleEFINVRAM which, similar to AppleEFIRuntime, SL version is 1.4, ML 10.8.4 is 1.7.
Using 1.4 is problematic on ML because of the way these kext define OSBundleCompatibleVersion. Version number must be called out specifically matching the system.
Or...maybe not. It just hung on me during boot with those dependencies. Might be my testing too many ideas at once and backtrace messages sometimes scroll off the screen where I can't see them (but can check logs).
Could the code use IORegistryEntry::fromPath instead?
If we set AppleEFIRuntime or AppleEFINVRAM dependency then FakeSMC will never start and will newer create FakeSMCDevice (virtual SMC device).
waitForService is necessary, because NVRAM driver not available yet when FakeSMC starting...
I see with Chameleon bootloader all these things work different...
I am using Clover bootloader.
The dependencies didn't work.. see above. They didn't cause the problem you describe either, but they also didn't provide a solution.
Looks like IORegistryEntry::fromPath is a no-go. Problem must be it hasn't loaded yet. And trying to wait for it causes the problem.
If this is Chameleon specific, do you have any ideas?
What is this code for anyway? What issue is it trying to fix?
I don't know, I am just trying to do something to find solution...
I see that setting sync setProperty(kIONVRAMSyncNowPropertyKey) to NVRAM doesn't work on your system... It is add this property to NVRAM.
Something completely different with Chameleon and Clover...
Maybe IODeviceTree:/chosen/nvram offers an idea?
if (IORegistryEntry* nvram = IORegistryEntry::fromPath("IODeviceTree:/chosen/nvram")) {
It seems to be present at the time FakeSMC starts:
Jul 11 15:35:50 localhost kernel[0]: FakeSMC: key NTOK added from NVRAM
Jul 11 15:35:50 localhost kernel[0]: FakeSMC: key NATi added from NVRAM
Jul 11 15:35:50 localhost kernel[0]: FakeSMC: key NATJ added from NVRAM
Jul 11 15:35:50 localhost kernel[0]: FakeSMC: key CLKT added from NVRAM
Jul 11 15:35:50 localhost kernel[0]: FakeSMC: key CLKH added from NVRAM
Jul 11 15:35:50 localhost kernel[0]: FakeSMC: key MSDW added from NVRAM
Jul 11 15:35:50 localhost kernel[0]: FakeSMC: 6 keys added from NVRAM
Only if you booted with Chameleon/Chimera.
But yes! This is an option! If IODeviceTree:/chosen/nvram is unavailable, we can fallback to waitForMatchingService. Or we can determine bootloader other way.
Probably best to explicitly test for boot loader type if possible... given what happens on Chameleon when waitForMatchingService is called.
Is there a way for me to test this save/load nvram. Like you say the IONVRAM-SYNCNOW-PROPERTY seems to not be working, but it looks like the fakesmc-keys are ending up in the nvram (they would not have been there before this build of FakeSMC)
Latest commit works with one additional change:
localhost:kozlek.git Admin$ git diff
diff --git a/FakeSMC/FakeSMC.cpp b/FakeSMC/FakeSMC.cpp
index db504db..85dd22b 100755
--- a/FakeSMC/FakeSMC.cpp
+++ b/FakeSMC/FakeSMC.cpp
@@ -91,7 +91,7 @@ bool FakeSMC::start(IOService *provider)
registerService();
// Load keys from NVRAM
- IODTNVRAM* nvram = NULL;
+ IORegistryEntry* nvram = NULL;
if (vendor && vendor->getLength() == 14 && 0 == memcmp(vendor->getBytesNoCopy(), "C\0L\0O\0V\0E\0R\0\0\0",
// System booted with Clover
@@ -102,7 +102,7 @@ bool FakeSMC::start(IOService *provider)
}
else {
// System booted with other non-Apple bootloader
- nvram = OSDynamicCast(IODTNVRAM, IORegistryEntry::fromPath("/chosen/nvram", gIODTPlane));
+ nvram = IORegistryEntry::fromPath("/chosen/nvram", gIODTPlane);
}
if (nvram) {
Problem is /chosen/nvram class graph is IOService : IORegistryEntry : OSObject.
Added!
Thanks... All working now.
I see you went for the probe/fallback method instead. Should be fine...
BTW, I don't know if you care about code efficiency/size, but you can make some improvements:
localhost:kozlek.git Admin$ git diff
diff --git a/FakeSMC/FakeSMC.cpp b/FakeSMC/FakeSMC.cpp
index 1114aeb..b55d2ed 100755
--- a/FakeSMC/FakeSMC.cpp
+++ b/FakeSMC/FakeSMC.cpp
@@ -86,7 +86,8 @@ bool FakeSMC::start(IOService *provider)
return false;
}
- smcDevice->registerService();
+ //REVIEW: registerService for smcDevice already called in FakeSMCDevice::init (above...)
+ ////smcDevice->registerService();
registerService();
// Chameleon/Chimera exporting NVRAM to IODeviceTree:/chosen/nvram
@@ -108,11 +109,17 @@ bool FakeSMC::start(IOService *provider)
int count = 0;
unsigned int offset = 0;
- while (offset + 9 < keys->getLength()) {
- char name[5]; memcpy(name, keys->getBytesNoCopy(offset, 4), 4); name[4] = '\0'; offset += 4;
- char type[5]; memcpy(type, keys->getBytesNoCopy(offset, 4), 4); type[4] = '\0'; offset += 4;
- unsigned char size = 0; memcpy(&size, keys->getBytesNoCopy(offset, 1), 1); offset++;
- const void *value = keys->getBytesNoCopy(offset, size); offset += size;
+ const unsigned char* data = static_cast<const unsigned char*>(keys->getBytesNoCopy());
+ unsigned int length = keys->getLength();
+
+ char name[5]; name[4] = 0;
+ char type[5]; type[4] = 0;
+
+ while (offset + 9 < length) {
+ memcpy(name, data + offset, 4); offset += 4;
+ memcpy(type, data + offset, 4); offset += 4;
+ unsigned char size = data[offset++];
+ const void *value = data + offset; offset += size;
if (FakeSMCKey *key = smcDevice->addKeyWithValue(name, type, size, value)) {
// Add key to NVRAM keys list
diff --git a/FakeSMC/FakeSMCDevice.cpp b/FakeSMC/FakeSMCDevice.cpp
index c2ac27e..66e2923 100755
--- a/FakeSMC/FakeSMCDevice.cpp
+++ b/FakeSMC/FakeSMCDevice.cpp
@@ -257,7 +257,7 @@ void FakeSMCDevice::saveKeyToNVRAM(FakeSMCKey *key, bool sync)
if (sync) {
if (IODTNVRAM *nvram = OSDynamicCast(IODTNVRAM, fromPath("/options", gIODTPlane))) {
- OSData *data = OSData::withCapacity(0);
+ OSData *data = OSData::withCapacity(512);
if (OSCollectionIterator *iterator = OSCollectionIterator::withCollection(nvramKeys)) {
Instead of 4-calls to virtual function getBytesNoCopy each loop iteration, you can call it only once at the beginning (O(1), instead of O(4n)). Same goes for virtual function getLength, instead of once per iteration, once at the beginning (O(1) instead of O(n)). Same goes for initializing the NUL terminators on name and type. Also, no reason to call memcpy to copy a single byte. Faster and smaller code as a result...
And you'll notice that I removed the extra call to smcDevice->registerService. I'm not sure there's any harm in calling it twice, but it certainly is not necessary.
The FakeSMCDevice.cpp diffs show another little optimization. According to the documentation OSData potentially can reallocate on each call to append*. So, it is a good idea to pick a reasonable allocation to start (in this case 512 bytes is probably more than enough to store these keys).
If you don't really mind having inefficient code (arguably this code is not called often), then just ignore me and close. For years, I have strived to write the most efficient code possible having worked on machines with much less resources than today's computers. It is hard to turn off.
It's ok. First we make working code. After that it's time for optimizations.
I saw you made the changes I suggested. Great.
Interesting comment "First we make working code. After that it's time for optimizations."
Different philosophies for working, I guess. I always try to write optimized code the first time. It saves having to re-write it, and then re-test it. Because, of course, it is possible to create bugs while optimizing, so I'd rather have to test/debug only once. But that's me...
FYI: Problem on my laptop...
The call to saveKeyToNVRAM is causing problems on wake from sleep. Laptop locks up on wake. Problem is in saveKeyToNVRAM, specifically the part that:
((IORegistryEntry*)nvram)->setProperty(kFakeSMCPropertyKeys, data);
nvram->sync();
If I comment both of these lines out (I tried commenting the sync out first, still lockup, then tried commenting the setProperty, then there is no problem on wake from sleep.
So, I guess some component is doing an APPLESMC_WRITE_CMD on wake, and it is a bad time for the setProperty...
For now, I'll #ifdef all related key/nvram code. Let me know if you want me to try something with regard to this new issue.
There is a method in IODTNVRAM:
virtual bool safeToSync(void);
Can you try it on your laptop?
nvram->safeToSync()
You mean something like:
if (nvram->safeToSync()) {
((IORegistryEntry*)nvram)->setProperty(kFakeSMCPropertyKeys, data);
nvram->sync();
}
???
Yes. I think, because you are using chameleon. If you setProperty it automatically synced as opposite then booting with Clover. Because I must call sync or setProperty(kIONVRAMSyncNowPropertyKey, kFakeSMCPropertyKeys) on my system to get keys stored in NVRAM. But with Chameleon you also get kIONVRAMSyncNowPropertyKey property in NVRAM. That means kIONVRAMSyncNowPropertyKey got on your system as property. Not as command as on my system
Not good. Calling nvram->safeToSync as above... causes KP during boot.
Please check this changes: https://github.com/kozlek/HWSensors/commit/ef056aac9c8d3eec5f11c8218803d4440b0f5e2d
Not good. Still lockup on wake from sleep. Problem is nvram->setProperty call.
Probably best to do this kind of thing in workloop thread, triggered by "interrupt source"...
I don't have time to try it now, but I'll give it a shot when I do... unless you get there first.
Does AppleEFINVRAM still active after wake from sleep?
Maybe you can try this code:
void FakeSMCDevice::saveKeyToNVRAM(FakeSMCKey *key, bool sync)
if (IORegistryEntry options = OSDynamicCast(IORegistryEntry, fromPath(doSyncNVRAM ? "/options" : "/chosen/nvram", gIODTPlane))) {
So, do the setProperty to /chosen/nvram node instead of /options node? I can try, but I really think it is a bad idea to call this during smc-write. I probably won't have time to try until after the weekend, but we'll see...
BTW, I was having some instability on my desktop, I think caused by some of these recent changes, although the instability seemed to be 'going to sleep' not 'waking from sleep'. I saw that you removed some SMC keys from Info.plist and I have since added them back... and so far no instability when desktop tries to go to sleep:
diff --git a/Plists/FakeSMC-Info.plist b/Plists/FakeSMC-Info.plist
index f9a799a..2debc4f 100755
--- a/Plists/FakeSMC-Info.plist
+++ b/Plists/FakeSMC-Info.plist
@@ -90,6 +90,21 @@
<string>si8</string>
<data>BQ==</data>
</array>
+ <key>NATJ</key>
+ <array>
+ <string>ui8</string>
+ <data>AA==</data>
+ </array>
+ <key>NATi</key>
+ <array>
+ <string>ui16</string>
+ <data>MDAwMA==</data>
+ </array>
+ <key>NTOK</key>
+ <array>
+ <string>ui8</string>
+ <data>AQ==</data>
+ </array>
<key>OSK0</key>
<array>
<string>ch8*</string>
These changes were included in the commit related to code optimization, although it is unrelated to optimizing... What is the purpose of these keys and why remove them?
FYI: With nvram loading/writing disabled, yes, AppleEFINVRAM does show in ioreg after wake from sleep under AppleACPIPlatformExpert/AppleEFIRuntime. And "Active" box is checked...
Is that what you're asking as far as "Is AppleEFINVRAM still active after wake from sleep?"
Really?? This is the last thing influenced I can imaging.
NATi, NTAJ - ninja action. I'll get it back.
As I remember, you can't just write to IORegisty where you want, you'll get kernel panic because of denial of access..
Realize I have no clue what 'ninja action' is... I just observed some behavior that seemed to be new, and looked for recent changes without having a clue what the changes were for, and then reversed the changes to test... No problem lately, but I'll keep an eye on it...
I tried the idea of writing to /chosen/nvram instead as you mentioned earlier. It doesn't crash on sleep or wake from sleep, but the fakesmc keys also do not end up in nvram (as shown by nvram -p).
Still experimenting...
There are two key writes at the same time after wake from sleep: MSDW and LSSS. Need to add lock to saveNvram function maybe...
I'm trying that (lock). And also trying workloop thread triggered by interruptsource (to delay the nvram setProperty). I see you have some changes coming though, so I guess I'll merge that first and see what happens.
OK... I've tried a lot of different things here...
Tried having interruptSource to delay setProperty to workloop thread. It didn't help, in fact it caused problems on transition to sleep, and even when I fixed that issue, there was still the problem of waking (eg. running the setProperty on the workloop thread didn't help).
I also tried tracking power states, so I could see when transition to sleep is happening and when wake is happening. Problem is the notification on wake happens very early (before the smc writes) so doesn't help...
I considered trying a timer to delay into workloop thread, but since timers are high-priority, but never tried. Seems too much of a hack anyway. Eventually, I may try it just to learn something.
In the end, I implemented an exception list for certain keys. Keys in the exception list don't get written to nvram via setProperty on /options. The diff:
diff --git a/FakeSMC/FakeSMCDevice.cpp b/FakeSMC/FakeSMCDevice.cpp
index dde2936..f694b8e 100755
--- a/FakeSMC/FakeSMCDevice.cpp
+++ b/FakeSMC/FakeSMCDevice.cpp
@@ -264,6 +264,10 @@ void FakeSMCDevice::saveKeyToNVRAM(FakeSMCKey *key)
if (ignoreNVRAM)
return;
+ if (exceptionKeys && exceptionKeys->getObject(key->getKey())) {
+ return;
+ }
+
@@ -567,6 +571,25 @@ bool FakeSMCDevice::initAndStart(IOService *platform, IOService *provider)
}
}
+ // Load NVRAM exception keys
+ FakeSMCDebugLog("loading NVRAM exceptions...");
+
+ exceptionKeys = NULL;
+ if (OSDictionary *dictionary = OSDynamicCast(OSDictionary, properties->getObject("ExceptionKeys"))) {
+ exceptionKeys = OSDictionary::withCapacity(dictionary->getCount());
+ if (OSIterator *iterator = OSCollectionIterator::withCollection(dictionary)) {
+ while (OSString *key = OSDynamicCast(OSString, iterator->getNextObject())) {
+ if (OSNumber *value = OSDynamicCast(OSNumber, dictionary->getObject(key))) {
+ if (value->unsigned32BitValue())
+ exceptionKeys->setObject(key, value);
+ }
+ }
+ OSSafeRelease(iterator);
+ }
+ }
+
// Set Clover platform keys
if (OSDictionary *dictionary = OSDynamicCast(OSDictionary, properties->getObject("Clover"))) {
UInt32 count = 0;
diff --git a/FakeSMC/FakeSMCDevice.h b/FakeSMC/FakeSMCDevice.h
index 0ee113f..2df6cd4 100755
--- a/FakeSMC/FakeSMCDevice.h
+++ b/FakeSMC/FakeSMCDevice.h
@@ -28,7 +28,7 @@
#define APPLESMC_GET_KEY_TYPE_CMD 0x13
struct AppleSMCStatus {
uint8_t cmd;
@@ -62,8 +62,8 @@ private:
OSDictionary *types;
OSDictionary *exposedValues;
bool ignoreNVRAM;
+ OSDictionary *exceptionKeys;
FakeSMCKey *keyCounterKey;
diff --git a/Plists/FakeSMC-Info.plist b/Plists/FakeSMC-Info.plist
index e0386c6..2f9c9d0 100755
--- a/Plists/FakeSMC-Info.plist
+++ b/Plists/FakeSMC-Info.plist
@@ -166,6 +166,23 @@
<key>RPlt</key>
<string>ch8*</string>
</dict>
+ <key>ExceptionKeys</key>
+ <dict>
+ <key>NATi</key>
+ <integer>1</integer>
+ <key>NATJ</key>
+ <integer>1</integer>
+ <key>MSDW</key>
+ <integer>1</integer>
+ <key>NTOK</key>
+ <integer>1</integer>
+ <key>CLKT</key>
+ <integer>1</integer>
+ <key>CLKH</key>
+ <integer>1</integer>
+ <key>WKTP</key>
+ <integer>0</integer>
+ </dict>
<key>debug</key>
<false/>
<key>smc-compatible</key>
Originally, I had WKTP enabled for exception, but it seems to not pose a problem. It is written at wake though. I may eventually disable nvram saving for it too. NATi and NATJ also don't cause a problem, but it seems that these keys do not really need to persist in nvram.
What do you think?
So, you confirm MSDW and LSSS causing problems if written on wake, into NVRAM without delay?
If I'll implement delayed write into NVRAM you think this will help?
I prefer automatic solutions without using configuration files.. But, if it's only case for now, to fix this problem.. Also ok.
Delaying won't help, at least not a short delay... As I mentioned above, I tried delaying to triggered interruptSource to force delay until scheduled in workloop and it didn't work. Also... delaying introduced new problems on transition to sleep (where it is not a problem if written immediately).
So far, the only working solution is the one above: exception list. LSSS is not on the list. My desktop is writing LSSS without issue (not sure when that write happens).
Later this afternoon, I'll try a 2 second delay using a timerEventSource and see what happens (I already have all the code in place... workloop setup, etc... in another branch, so adding a timer will be easy) ... I'll let you know. It could be that these specific keys cause a problem, not necessarily the timing.
Just tried it (didn't have to wait until this afternoon). I implemented a two second delay for saving keys and disabled the exception list. Also, if the timer fires after the system has started to sleep (power management tracking is there), it will just ignore. I should probably put something in that kicks off a timer after wake, just in case no keys are written during wake (right now they are being written, so they eventually get updated as they are in the _keysToSync dictionary).
And... It works! So far anyway... Need to test some more. So far just tested on laptop. Will try on desktop next.
I pushed to separate branch. If you want to look at it: https://github.com/RehabMan/OS-X-FakeSMC-kozlek/tree/nvramsync (Note: code cleanup not done yet. Error handling not right, etc... Please ignore)
It seems system writes MSDW or some other key before going to sleep. May be the problem is that this write is going to nvram->ioregistryentry and just before or after sleep has occured. And system writes the same key just after wake then the previous sequence was interrapted.. If it's even possible, because ioregistry issunchronized.. But the nvram writes dirrectly fron the nvram driver?
Ok, works, seems the pronlem is synchronization... So, can you try to write not to iodtnvram but in ioregistryentry (because this write is synchronized)?
I'm not sure what you're suggesting. Currently writes go to registry entry IODeviceTree:/options.
I tried this:
//nvram->setProperty(tempName, OSData::withBytes(key->getValue(), key->getSize()));
nvram->IORegistryEntry::setProperty(tempName, OSData::withBytes(key->getValue(), key->getSize()));
This steps past any overrides AppleNVRAM may have and goes direct to IORegistryEntry::setProperty implementation. And... it works! And writes seem to show up in nvram -p.
Ok, and if you try this code:
// if (IODTNVRAM nvram = OSDynamicCast(IODTNVRAM, fromPath("/options", gIODTPlane))) { if (IORegistryEntry nvram = OSDynamicCast(IORegistryEntry, fromPath("/options", gIODTPlane))) {
// nvram->IORegistryEntry::setProperty(tempName, OSData::withBytes(key->getValue(), key->getSize())); nvram->setProperty(tempName, OSData::withBytes(key->getValue(), key->getSize()));
That won't work. setProperty is a virtual function. The call is always indirect through the vtable unless you override it like I did. You'll end up calling the same thing (AppleNVRAM::setProperty or IODTNVRAM::setProperty depending on where the override is in the inheritance chain).
C++ lesson:
class A
{
public:
virtual void m1(void) { }
void m2(void) { }
};
class B : public A
{
public:
virtual void m2(void) { }
void m2(void) { }
};
void test()
{
// no surprises here...
A* pa = new A;
pa->m1(); // calls A::m1
pa->m2(); // calls A::m2
B* pb = new B;
pb->m1(); // calls B::m1
pb->m2(); // calls B::m2
// surprises to people new to C++ virtual functions
pa = pb; // pa is typed as A*, but points to a B
pa->m1(); // calls B::m1 (analogous to your code above IORegistryEntry=class A, IODTNVRAM=class B)
pa->A::m1(); // calls A::m1 (like my code for IORegistryEntry::setProperty)
pa->m2(); // calls A::m2 (because it isn't virtual)
}
End of lesson for today.
What do you think should be the real fix? By passing the override is a bit of a hack (they may have overridden it for a reason). And so is doing the timer... because you always wonder if you're just getting lucky to not run into the timing problem by delaying it like that.
I'm going to think on it a bit...
This is on my desktop system Intel DH67GD/Core i7-2600k.
I have commented out the nvram loading code for now in my version:
If I move the #if 0 just one line down to allow the waitForMatchingService(matching) to execute, it causes kernel backtrace on boot (using "-v") and system does not boot.
So... waiting for this IODTNVRAM service is causing a problem.