Some initial iokitd progress

This commit is contained in:
Lubos Dolezel 2020-02-10 21:57:11 +01:00
parent 0d64152ab8
commit 024f5490fb
17 changed files with 405 additions and 129 deletions

View File

@ -6,11 +6,12 @@ add_compile_options(
-Wno-gcc-compat
)
add_definitions(-DIOKIT=1 -DIOKIT_ALL_IPC=1)
add_definitions(-DIOKIT=1 -DIOKIT_ALL_IPC=1 -DIOKIT_SERVER_VERSION=20150101)
include_directories(
${CMAKE_SOURCE_DIR}/src/launchd
${CMAKE_SOURCE_DIR}/src/lkm/osfmk
${CMAKE_SOURCE_DIR}/src/external/IOKitUser
)
include_directories(BEFORE ${CMAKE_SOURCE_DIR}/src/external/libcxx/include ${CMAKE_CURRENT_BINARY_DIR})
@ -19,11 +20,20 @@ mig(iokitmig.defs)
set(iokitd_sources
src/main.cpp
src/stubs.c
src/iokitd.cpp
src/Registry.mm
src/IOObject.cpp
src/IOIterator.cpp
src/IOService.mm
src/IODisplayConnect.mm
src/IODisplayConnectX11.mm
${CMAKE_CURRENT_BINARY_DIR}/iokitmigServer.c
${CMAKE_CURRENT_SOURCE_DIR}/../IOKitUser/IOCFSerialize.c
${CMAKE_CURRENT_SOURCE_DIR}/../IOKitUser/IOCFUnserialize.tab.c
)
add_darling_executable(iokitd ${iokitd_sources})
target_link_libraries(iokitd cxx)
target_link_libraries(iokitd cxx CoreFoundation Foundation)
install(TARGETS iokitd DESTINATION libexec/darling/usr/sbin)
install(FILES org.darlinghq.iokitd.plist DESTINATION libexec/darling/System/Library/LaunchDaemons)

12
src/IODisplayConnect.h Normal file
View File

@ -0,0 +1,12 @@
#ifndef IOKITD_IODISPLAYCONNECT_H
#define IOKITD_IODISPLAYCONNECT_H
#include "IOService.h"
class IODisplayConnect : public IOService
{
public:
NSDictionary* matchingDictionary() override;
};
#endif

11
src/IODisplayConnect.mm Normal file
View File

@ -0,0 +1,11 @@
#include "IODisplayConnect.h"
#import <Foundation/NSString.h>
NSDictionary* IODisplayConnect::matchingDictionary()
{
const char* clsName = typeid(*this).name();
return @{
@"IOProviderClass": @"IODisplayConnect",
@"IOClass": [NSString stringWithUTF8String: clsName]
};
}

View File

@ -0,0 +1,9 @@
#ifndef IOKITD_IODISPLAYCONNECTX11_H
#define IOKITD_IODISPLAYCONNECTX11_H
class IODisplayConnectX11 : IODisplayConnect
{
public:
};
#endif

View File

78
src/IOIterator.cpp Normal file
View File

@ -0,0 +1,78 @@
#include "IOIterator.h"
extern "C" {
#include "iokitmigServer.h"
}
IOIterator::IOIterator(const std::vector<IOObject*>& objects)
: IOObject(true), m_objects(objects)
{
}
void IOIterator::reset()
{
m_pos = 0;
}
const char* IOIterator::className() const
{
return "IOUserIterator";
}
IOObject* IOIterator::next()
{
if (m_pos >= m_objects.size())
return nullptr;
return m_objects[m_pos++];
}
kern_return_t is_io_iterator_next
(
mach_port_t iterator,
mach_port_t *object
)
{
IOIterator* iter = dynamic_cast<IOIterator*>(IOObject::lookup(iterator));
*object = MACH_PORT_NULL;
if (iter != nullptr)
{
IOObject* next = iter->next();
if (next != nullptr)
{
*object = next->port();
return KERN_SUCCESS;
}
else
return KERN_SUCCESS;
}
else
return KERN_INVALID_ARGUMENT;
}
kern_return_t is_io_iterator_reset
(
mach_port_t iterator
)
{
IOIterator* iter = dynamic_cast<IOIterator*>(IOObject::lookup(iterator));
if (!iter)
return KERN_INVALID_ARGUMENT;
else
{
iter->reset();
return KERN_SUCCESS;
}
}
kern_return_t is_io_iterator_is_valid
(
mach_port_t iterator,
boolean_t *is_valid
)
{
IOIterator* iter = dynamic_cast<IOIterator*>(IOObject::lookup(iterator));
*is_valid = iter != nullptr;
return KERN_SUCCESS;
}

21
src/IOIterator.h Normal file
View File

@ -0,0 +1,21 @@
#ifndef IOKITD_IOITERATOR_H
#define IOKITD_IOITERATOR_H
#include "IOObject.h"
#include <vector>
class IOIterator : public IOObject
{
public:
IOIterator(const std::vector<IOObject*>& objects);
const char* className() const override;
void reset();
IOObject* next();
private:
std::vector<IOObject*> m_objects;
int m_pos = 0;
};
#endif

74
src/IOObject.cpp Normal file
View File

@ -0,0 +1,74 @@
#include "IOObject.h"
#include "iokitd.h"
#include <stdexcept>
#include <os/log.h>
std::unordered_map<mach_port_t, IOObject*> IOObject::m_objects;
IOObject::IOObject(bool userOwned)
{
auto task = mach_task_self();
kern_return_t kr;
kr = mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE, &m_port);
if (kr != KERN_SUCCESS)
throw std::runtime_error("Failed to allocate Mach port");
if (userOwned)
{
mach_port_t oldTargetOfNotification = MACH_PORT_NULL;
kr = mach_port_request_notification(task, m_port, MACH_NOTIFY_NO_SENDERS, 1, g_masterPort, MACH_MSG_TYPE_MAKE_SEND_ONCE, &oldTargetOfNotification);
if (kr != KERN_SUCCESS)
throw std::runtime_error("Failed to setup MACH_NOTIFY_NO_SENDERS");
}
m_objects.insert(std::make_pair(m_port, this));
}
IOObject::~IOObject()
{
m_objects.erase(m_port);
mach_port_deallocate(mach_task_self(), m_port);
}
IOObject* IOObject::lookup(mach_port_t port)
{
auto it = m_objects.find(port);
if (it != m_objects.end())
return it->second;
return nullptr;
}
boolean_t IOObject::deathNotify(mach_msg_header_t *request, mach_msg_header_t *reply)
{
mach_no_senders_notification_t* Request = (mach_no_senders_notification_t*) request;
mig_reply_error_t* Reply = (mig_reply_error_t*) reply;
reply->msgh_bits = MACH_MSGH_BITS(MACH_MSGH_BITS_REMOTE(request->msgh_bits), 0);
reply->msgh_remote_port = request->msgh_remote_port;
reply->msgh_size = sizeof(mig_reply_error_t);
reply->msgh_local_port = MACH_PORT_NULL;
reply->msgh_id = request->msgh_id + 100;
if (Request->not_header.msgh_id == MACH_NOTIFY_NO_SENDERS)
{
IOObject* object = lookup(request->msgh_remote_port);
if (object != nullptr)
delete object;
else
os_log_error(OS_LOG_DEFAULT, "Received MACH_NOTIFY_NO_SENDERS for port %d, which matches no objects", request->msgh_remote_port);
Reply->Head.msgh_bits = 0;
Reply->Head.msgh_remote_port = MACH_PORT_NULL;
Reply->RetCode = KERN_SUCCESS;
return true;
}
else
{
Reply->NDR = NDR_record;
Reply->RetCode = MIG_BAD_ID;
return false;
}
}

23
src/IOObject.h Normal file
View File

@ -0,0 +1,23 @@
#ifndef IOKITD_IOOBJECT_H
#define IOKITD_IOOBJECT_H
#include <unordered_map>
#include <mach/mach.h>
class IOObject
{
public:
// If userOwned is true, the object will be deallocated when there are no senders left
IOObject(bool userOwned);
virtual ~IOObject();
virtual const char* className() const = 0;
mach_port_t port() { return m_port; }
static IOObject* lookup(mach_port_t port);
static boolean_t deathNotify(mach_msg_header_t *request, mach_msg_header_t *reply);
private:
mach_port_t m_port;
static std::unordered_map<mach_port_t, IOObject*> m_objects;
};
#endif

14
src/IOService.h Normal file
View File

@ -0,0 +1,14 @@
#ifndef IOKITD_IOSERVICE_H
#define IOKITD_IOSERVICE_H
#include <Foundation/NSDictionary.h>
#include "IOObject.h"
class IOService : public IOObject
{
public:
IOService();
virtual NSDictionary* matchingDictionary() = 0;
bool matches(NSDictionary* dict);
};
#endif

20
src/IOService.mm Normal file
View File

@ -0,0 +1,20 @@
#include "IOService.h"
IOService::IOService()
: IOObject(false)
{
}
bool IOService::matches(NSDictionary* dict)
{
NSDictionary* ourProps = matchingDictionary();
for (NSString* key in dict)
{
if (![dict[key] isEqual: ourProps[key]])
return false;
}
return true;
}

11
src/Registry.h Normal file
View File

@ -0,0 +1,11 @@
#ifndef IOKITD_REGISTRY_H
#define IOKITD_REGISTRY_H
#include "IOService.h"
class Registry
{
public:
void registerService(IOService* service);
};
#endif

66
src/Registry.mm Normal file
View File

@ -0,0 +1,66 @@
#include "iokitd.h"
#include "Registry.h"
#include <IOCFUnserialize.h>
#include <CoreFoundation/CFString.h>
#include <os/log.h>
#include <stdexcept>
extern "C" {
#include "iokitmigServer.h"
}
void Registry::registerService(IOService* service)
{
// TODO
}
kern_return_t is_io_service_get_matching_services_ool
(
mach_port_t master_port,
io_buf_ptr_t matching,
mach_msg_type_number_t matchingCnt,
kern_return_t *result,
mach_port_t *existing
)
{
return KERN_NOT_SUPPORTED;
}
kern_return_t is_io_service_get_matching_services_bin
(
mach_port_t master_port,
io_struct_inband_t matching,
mach_msg_type_number_t matchingCnt,
mach_port_t *existing
)
{
CFStringRef errorString = nullptr;
try
{
CFTypeRef criteria = IOCFUnserializeBinary(matching, matchingCnt, nullptr, 0, &errorString);
if (!criteria)
throwCFStringException(CFSTR("io_service_get_matching_services_bin(): cannot parse 'matching': %@"), errorString);
if (CFGetTypeID(criteria) != CFDictionaryGetTypeID())
throw std::runtime_error("io_service_get_matching_services_bin(): dictionary expected");
NSDictionary* dictCriteria = (NSDictionary*) criteria;
// dictCriteria example:
// IOProviderClass -> IODisplayConnect
CFShow(criteria);
CFRelease(criteria);
return KERN_NOT_SUPPORTED;
}
catch (const std::exception& e)
{
os_log_error(OS_LOG_DEFAULT, e.what());
if (errorString)
CFRelease(errorString);
return KERN_INVALID_ARGUMENT;
}
}

21
src/iokitd.cpp Normal file
View File

@ -0,0 +1,21 @@
#include "iokitd.h"
#include <stdexcept>
#include <stdarg.h>
#include <string>
void throwCFStringException(CFStringRef format, ...)
{
CFStringRef text;
va_list ap;
va_start(ap, format);
text = CFStringCreateWithFormatAndArguments(nullptr, nullptr, format, ap);
va_end(ap);
std::string str = CFStringGetCStringPtr(text, kCFStringEncodingASCII);
CFRelease(text);
throw std::runtime_error(str);
}

View File

@ -1,8 +1,12 @@
#ifndef _IOKITD_H
#define _IOKITD_H
#include <mach/mach.h>
#include <CoreFoundation/CFString.h>
#define asldebug(...) os_log_debug(OS_LOG_DEFAULT, __VA_ARGS__)
void throwCFStringException(CFStringRef format, ...);
extern mach_port_t g_masterPort, g_deathPort;
#endif

View File

@ -2,26 +2,25 @@
#include <os/log.h>
#include <liblaunch/bootstrap.h>
#include <dispatch/dispatch.h>
#include <dispatch/private.h>
#include <cstdlib>
#include "iokitd.h"
#include "iokitmig.h"
#include "IOObject.h"
extern "C" {
#include "iokitmigServer.h"
}
static const char* SERVICE_NAME = "org.darlinghq.iokitd";
static void service_mach_message(mach_port_t serverPort);
mach_port_t g_masterPort, g_deathPort;
int main(int argc, const char** argv)
{
mach_port_t bs, mp;
mach_port_t bs;
kern_return_t ret;
task_get_bootstrap_port(mach_task_self(), &bs);
ret = bootstrap_check_in(bs, SERVICE_NAME, &mp);
mach_port_destroy(mach_task_self(), bs);
ret = bootstrap_check_in(bootstrap_port, SERVICE_NAME, &g_masterPort);
if (ret != KERN_SUCCESS)
{
@ -29,83 +28,40 @@ int main(int argc, const char** argv)
return 1;
}
// TODO: Build IOKit registry here
ret = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &g_deathPort);
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_source_t portSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV, mp, 0, queue);
dispatch_source_t portSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV, g_masterPort, 0, queue);
if (!portSource)
{
os_log_error(OS_LOG_DEFAULT, "%d dispatch_source_create() failed", getpid());
os_log_error(OS_LOG_DEFAULT, "%d dispatch_source_create() failed for main port", getpid());
return 1;
}
dispatch_source_set_event_handler(portSource, ^{
service_mach_message(mp);
dispatch_mig_server(portSource, is_iokit_subsystem.maxsize, iokit_server);
});
dispatch_source_t deathSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV, g_deathPort, 0, queue);
if (!deathSource)
{
os_log_error(OS_LOG_DEFAULT, "%d dispatch_source_create() failed for notification port", getpid());
return 1;
}
dispatch_source_set_event_handler(deathSource, ^{
dispatch_mig_server(deathSource, sizeof(mach_no_senders_notification_t), IOObject::deathNotify);
});
dispatch_resume(portSource);
dispatch_resume(deathSource);
os_log(OS_LOG_DEFAULT, "iokitd up and running.");
dispatch_main();
return 0;
}
typedef union
{
mach_msg_header_t head;
union __RequestUnion__iokit_subsystem request;
} iokit_request_msg;
typedef union
{
mach_msg_header_t head;
union __ReplyUnion__iokit_subsystem reply;
} iokit_reply_msg;
static void service_mach_message(mach_port_t serverPort)
{
__block kern_return_t status;
uint32_t rbits, sbits;
iokit_request_msg *request;
iokit_reply_msg *reply;
char rbuf[sizeof(iokit_request_msg) + MAX_TRAILER_SIZE];
char sbuf[sizeof(iokit_reply_msg) + MAX_TRAILER_SIZE];
while (true)
{
memset(rbuf, 0, sizeof(rbuf));
memset(sbuf, 0, sizeof(sbuf));
request = (iokit_request_msg *)rbuf;
reply = (iokit_reply_msg *)sbuf;
request->head.msgh_local_port = serverPort;
request->head.msgh_size = sizeof(iokit_request_msg) + MAX_TRAILER_SIZE;
rbits = MACH_RCV_MSG | MACH_RCV_TIMEOUT | MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AUDIT) | MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0) | MACH_RCV_VOUCHER;
sbits = MACH_SEND_MSG;
status = mach_msg(&(request->head), rbits, 0, request->head.msgh_size, serverPort, 0, MACH_PORT_NULL);
if (status != KERN_SUCCESS) return;
voucher_mach_msg_state_t voucher = voucher_mach_msg_adopt(&(request->head));
status = iokit_server(&(request->head), &(reply->head));
if (!status && (request->head.msgh_bits & MACH_MSGH_BITS_COMPLEX))
{
/* destroy the request - but not the reply port */
request->head.msgh_remote_port = MACH_PORT_NULL;
mach_msg_destroy(&(request->head));
}
if (reply->head.msgh_remote_port)
{
status = mach_msg(&(reply->head), sbits, reply->head.msgh_size, 0, MACH_PORT_NULL, 0, MACH_PORT_NULL);
if (status == MACH_SEND_INVALID_DEST || status == MACH_SEND_TIMED_OUT)
{
/* deallocate reply port rights not consumed by failed mach_msg() send */
mach_msg_destroy(&(reply->head));
}
}
voucher_mach_msg_revert(voucher);
}
}

View File

@ -1,7 +1,7 @@
#include "iokitmigServer.h"
#include <os/log.h>
#define STUB() os_log_error(OS_LOG_DEFAULT, "%d STUB called: %s", getpid(), __FUNCTION__)
#define STUB() os_log(OS_LOG_DEFAULT, "%d STUB called: %s", getpid(), __FUNCTION__)
kern_return_t is_io_object_get_class
(
@ -24,25 +24,6 @@ kern_return_t is_io_object_conforms_to
return KERN_NOT_SUPPORTED;
}
kern_return_t is_io_iterator_next
(
mach_port_t iterator,
mach_port_t *object
)
{
STUB();
return KERN_NOT_SUPPORTED;
}
kern_return_t is_io_iterator_reset
(
mach_port_t iterator
)
{
STUB();
return KERN_NOT_SUPPORTED;
}
kern_return_t is_io_service_get_matching_services
(
mach_port_t master_port,
@ -371,16 +352,6 @@ kern_return_t is_io_registry_entry_create_iterator
return KERN_NOT_SUPPORTED;
}
kern_return_t is_io_iterator_is_valid
(
mach_port_t iterator,
boolean_t *is_valid
)
{
STUB();
return KERN_NOT_SUPPORTED;
}
kern_return_t is_io_catalog_send_data
(
mach_port_t master_port,
@ -647,19 +618,6 @@ kern_return_t is_io_service_get_state
return KERN_NOT_SUPPORTED;
}
kern_return_t is_io_service_get_matching_services_ool
(
mach_port_t master_port,
io_buf_ptr_t matching,
mach_msg_type_number_t matchingCnt,
kern_return_t *result,
mach_port_t *existing
)
{
STUB();
return KERN_NOT_SUPPORTED;
}
kern_return_t is_io_service_match_property_table_ool
(
mach_port_t service,
@ -983,18 +941,6 @@ kern_return_t is_io_service_get_matching_service_bin
return KERN_NOT_SUPPORTED;
}
kern_return_t is_io_service_get_matching_services_bin
(
mach_port_t master_port,
io_struct_inband_t matching,
mach_msg_type_number_t matchingCnt,
mach_port_t *existing
)
{
STUB();
return KERN_NOT_SUPPORTED;
}
kern_return_t is_io_service_match_property_table_bin
(
mach_port_t service,