#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/IOKitLib.h>
#include <IOKit/graphics/IOFramebufferShared.h>
#include <IOKit/graphics/IOGraphicsTypesPrivate.h>
#include <IOKit/graphics/IOGraphicsLib.h>
#include <libgen.h>
#include <stdio.h>
#include <sysexits.h>
#include <cassert>
#include <utility>
#include <vector>
#include <mach/mach.h>
#include <mach/mach_error.h>
using std::vector;
namespace {
#define SAFE_CFRELEASE(p) \
do { if (static_cast<bool>(p)) { CFRelease(p); p = NULL; } } while(0)
#define CFGETUTF8CPTR(s) CFStringGetCStringPtr((s), kCFStringEncodingUTF8)
const char *sCmdName = "whoami?";
mach_port_t sMasterPort = MACH_PORT_NULL;
auto kDependentIDKey = CFSTR("IOFBDependentID");
auto kDependentIndexKey = CFSTR("IOFBDependentIndex");
struct FBEntry {
CFStringRef fPath = NULL;
CFMutableDictionaryRef fProperties = NULL;
io_connect_t fConnect = MACH_PORT_NULL;
uint32_t fRegId = 0;
uint32_t fDepRegId = 0;
uint32_t fDepInd = 0;
void close() {
if (static_cast<bool>(fConnect)) {
IOServiceClose(fConnect);
fConnect = MACH_PORT_NULL;
}
}
void clean() {
close();
SAFE_CFRELEASE(fPath);
SAFE_CFRELEASE(fProperties);
}
void transfer(FBEntry &&other) {
fPath = other.fPath;
fProperties = other.fProperties;
fConnect = other.fConnect;
fRegId = other.fRegId;
fDepRegId = other.fDepRegId;
fDepInd = other.fDepInd;
other.fPath = NULL;
other.fProperties = NULL;
other.fRegId = 0;
fConnect = MACH_PORT_NULL;
}
FBEntry() { }
~FBEntry() { clean(); }
FBEntry(FBEntry&& other) { transfer(std::move(other)); }
FBEntry& operator=(FBEntry&& other)
{
if (this != &other) {
clean();
transfer(std::move(other));
}
return *this;
}
explicit FBEntry(io_object_t obj)
{
fPath = IORegistryEntryCopyPath(obj, kIOServicePlane);
kern_return_t err = IORegistryEntryCreateCFProperties(
obj, &fProperties, NULL, 0);
if (err) {
fprintf(stderr,
"%s: Unable to create properties for '%s' : %s(%x)\n",
sCmdName, CFGETUTF8CPTR(fPath), mach_error_string(err),err);
exit(EX_OSERR);
}
uint64_t u64 = 0;
err = IORegistryEntryGetRegistryEntryID(obj, &u64);
fRegId = static_cast<uint32_t>(u64);
if (err) {
fprintf(stderr,
"%s: Unable to get RegID for '%s' : %s(%x)\n",
sCmdName, CFGETUTF8CPTR(fPath), mach_error_string(err),err);
exit(EX_OSERR);
}
CFNumberRef num = static_cast<CFNumberRef>(
CFDictionaryGetValue(fProperties, kDependentIDKey));
CFNumberGetValue(num, kCFNumberSInt64Type, &u64);
fDepRegId = static_cast<uint32_t>(u64);
num = static_cast<CFNumberRef>(
CFDictionaryGetValue(fProperties, kDependentIndexKey));
CFNumberGetValue(num, kCFNumberSInt64Type, &u64);
fDepInd = static_cast<uint32_t>(u64);
}
kern_return_t open()
{
if (!static_cast<bool>(fPath))
return kIOReturnBadArgument;
const auto fbre = IORegistryEntryCopyFromPath(sMasterPort, fPath);
if (!static_cast<bool>(fbre))
return kIOReturnNotFound;
io_connect_t fbshared;
kern_return_t err = IOServiceOpen(
fbre, mach_task_self(), kIOFBDiagnoseConnectType, &fbshared);
IOObjectRelease(fbre);
if (!err) {
close();
fConnect = fbshared;
}
return err;
}
kern_return_t restoreCoreDisplayBlob(IOIndex ind, void *blob, size_t n)
{
if (!static_cast<bool>(fConnect))
return kIOReturnNotOpen;
const uint64_t arglist[] = {
1,
static_cast<uint64_t>(ind),
reinterpret_cast<uintptr_t>(blob),
n,
};
const auto argcnt
= static_cast<uint32_t>(sizeof(arglist) / sizeof(arglist[0]));
return IOConnectCallScalarMethod(
fConnect, 21, arglist, argcnt, NULL, NULL);
}
};
vector<FBEntry> sFB;
void populate_fblist()
{
CFDictionaryRef svcMatch = IOServiceMatching("IOFramebuffer");
IOMasterPort(MACH_PORT_NULL, &sMasterPort);
io_iterator_t iter = MACH_PORT_NULL;
kern_return_t err
= IOServiceGetMatchingServices(sMasterPort, svcMatch, &iter);
svcMatch = NULL; if (err) {
fprintf(stderr,
"%s: Unable to create IOFramebuffer iterator: %s(%x)\n",
sCmdName, mach_error_string(err), err);
exit(EX_OSERR);
}
vector<FBEntry> builtlist;
do {
builtlist.clear(); IOIteratorReset(iter);
io_object_t obj;
while ( (obj = IOIteratorNext(iter)) ) {
builtlist.emplace_back(FBEntry(obj));
IOObjectRelease(obj); obj = MACH_PORT_NULL;
}
} while(!IOIteratorIsValid(iter));
IOObjectRelease(iter);
sFB = std::move(builtlist);
}
int list(const int argc, const char * argv[])
{
(void) argc; (void) argv;
int i = 0;
for (const auto& entry : sFB) {
printf("%2d[%3x]: %3x[%d] %s\n",
i, entry.fRegId, entry.fDepRegId, entry.fDepInd,
CFGETUTF8CPTR(entry.fPath));
++i;
}
return EX_OK;
}
int restore(const int argc, const char * argv[])
{
vector<FBEntry>::size_type fbind = 0;
IOIndex blobIndex = 0;
if (argc >= 3)
fbind = strtol(argv[2], NULL, 10);
if (argc == 4)
blobIndex = strtol(argv[3], NULL, 10);
FBEntry& fb = sFB.at(fbind);
kern_return_t err = fb.open();
if (err) {
fprintf(stderr, "%s: Unable to open fb[%d]: %s{%x}\n",
sCmdName, static_cast<int>(fbind), mach_error_string(err), err);
return EX_NOINPUT;
}
int ret = EX_OK;
void *blobData = valloc(4096);
err = fb.restoreCoreDisplayBlob(blobIndex, blobData, 4096);
if (err) {
fprintf(stderr, "%s: fb[%d] unable to restore blob: %s{%x}\n",
sCmdName, static_cast<int>(fbind), mach_error_string(err), err);
ret = EX_OSERR;
}
else
fwrite(blobData, 4096, 1, stdout);
free(blobData);
fb.close();
return ret;
}
int update(const int argc, const char * argv[])
{
vector<FBEntry>::size_type fbind = 0;
IOIndex blobIndex = 0;
if (argc >= 3)
fbind = strtol(argv[2], NULL, 10);
if (argc == 4)
blobIndex = strtol(argv[3], NULL, 10);
FBEntry& fb = sFB.at(fbind);
kern_return_t err = fb.open();
if (err) {
fprintf(stderr, "%s: Unable to open fb[%d]: %s{%x}\n",
sCmdName, static_cast<int>(fbind), mach_error_string(err), err);
return EX_NOINPUT;
}
int ret = EX_OK;
void *blobData = valloc(4096);
err = fb.restoreCoreDisplayBlob(blobIndex, blobData, 4096);
if (err) {
fprintf(stderr, "%s: fb[%d] unable to update blob: %s{%x}\n",
sCmdName, static_cast<int>(fbind), mach_error_string(err), err);
ret = EX_OSERR;
}
else
fread(blobData, 4096, 1, stdin);
free(blobData);
fb.close();
return ret;
}
bool startswith(const char *str, const size_t sl, const char *arg)
{
const size_t al = strlen(arg);
const size_t len = (sl < al) ? sl : al;
return 0 == strncmp(str, arg, len);
}
#define STARTSWITH(str, arg) startswith(str, (sizeof(str)-1), arg)
};
int main(const int argc, char * argv[])
{
sCmdName = basename(argv[0]);
const char * const verb = argv[1];
const char ** cargv = const_cast<const char **>(argv);
populate_fblist();
if (STARTSWITH("list", verb))
return list(argc, cargv);
else if (STARTSWITH("restore", verb))
return restore(argc, cargv);
else if (STARTSWITH("update", verb))
return update(argc, cargv);
else {
fprintf(stderr, "%s: Unknown verb '%s'\n", sCmdName, verb);
return EX_USAGE;
}
}