I found the answer here. This is an MVE for an octet (i. e. 8bit byte aka uint8_t, requires euid=0 of course):
For a userspace app:
main.c:
#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/IOKitLib.h>
int main(int argc, char * argv[])
{ const char * serviceName = "AppleEFINVRAM";
const char * keyName = "MytestKey";
io_service_t ioService = IOServiceGetMatchingService(kIOMainPortDefault,
IOServiceMatching(serviceName));
if (!ioService)
{ printf("Service \"%s\" not found\n", serviceName);
return (1);
}
CFStringRef ioKey = CFStringCreateWithCString(kCFAllocatorDefault,
keyName,
CFStringGetSystemEncoding());
CFNumberRef ioNumValue;
uint8_t value = 0;
CFTypeRef ioValue = IORegistryEntryCreateCFProperty(ioService, ioKey, kCFAllocatorDefault, 0);
if (ioValue && CFGetTypeID(ioValue) == CFNumberGetTypeID())
{ ioNumValue = ioValue;
CFNumberGetValue(ioNumValue, kCFNumberSInt8Type, &value);
printf("Got key, value=%d\n", (int)value);
}
if (ioValue) CFRelease(ioValue);
value++;
printf("Value will be set to %d\n", (int)value);
ioNumValue = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt8Type, &value);
kern_return_t res = IORegistryEntrySetCFProperty(ioService, ioKey, ioNumValue);
if (res != KERN_SUCCESS)
{ printf("Cannot set value, error 0x%X\n", res);
}
CFRelease(ioNumValue);
IOObjectRelease(ioService);
return (0);
}
Makefile:
nvramTest: main.c
${CC} $^ -framework CoreFoundation -framework IOKit -o $@
For a kext:
void GenericPower::readFromNvram(void)
{ IORegistryEntry *nvram = IORegistryEntry::fromPath("/options", gIODTPlane);
if (!nvram || !OSDynamicCast(IODTNVRAM, nvram))
{ IOLog("readFromNVRAM: failed to read NVRAM\n");
OSSafeReleaseNULL(nvram);
return;
}
OSData *storedMode = OSDynamicCast(OSData, nvram->getProperty(ENABLED_PROP_NAME));
if (storedMode == nullptr || storedMode->getLength() <= 0)
{ IOLog("readFromNVRAM: key " ENABLED_PROP_NAME " not found, defaulting to \"disabled\"\n");
isEnabled = false;
OSSafeReleaseNULL(nvram);
return;
}
const uint8_t * storedModeData = static_cast<const uint8_t *>(storedMode->getBytesNoCopy());
IOLog("readFromNVRAM: stored value for \"" ENABLED_PROP_NAME "\" is (%d bytes): %2.2X...\n", (int)storedMode->getLength(), (unsigned int)storedModeData[0]);
isEnabled = storedModeData[0];
IOLog("readFromNVRAM: stored value for \"" ENABLED_PROP_NAME "\" is %s\n", isEnabled ? "ON" : "OFF");
OSSafeReleaseNULL(nvram);
}
void GenericPower::writeToNvram(void)
{ IORegistryEntry *nvram = IORegistryEntry::fromPath("/options", gIODTPlane);
if (!nvram || !OSDynamicCast(IODTNVRAM, nvram))
{ IOLog("writeToNVRAM: failed to read NVRAM\n");
OSSafeReleaseNULL(nvram);
return;
}
OSData *storedMode = OSData::withBytes(isEnabled ? "\x01" : "\x00", 1);
if (!nvram->setProperty(ENABLED_PROP_NAME, storedMode))
{ IOLog("writeToNVRAM: failed to write NVRAM\n");
}
OSSafeReleaseNULL(storedMode);
OSSafeReleaseNULL(nvram);
}