AppleEmbeddedNVMeController is a kernel extension for managing the NAND (disk) of via Peripheral Component Interconnect Express (PCIe) bus.
Methods
Selector | Action | Input | Output |
---|---|---|---|
2 | sendNVMECommand | struct NVMeCommand* | __n128 unknown |
3 | isBFHMode | - | bool bfhMode |
4 | performBFH | - | - |
5 | getNANDDescriptor | - | - |
6 | setNVMeState | - | - |
7 | setPCIPortState | - | - |
8 | setBFHGPIO | uint64_t value | uint64_t unknown |
NOTE: this table currently is only showing the selectors, the input / output will be documented later.
By reverse engineering a userland daemon (nvmefwupdater) that can be found on the update ramdisk in /usr/local/bin the following structures could be derrived:
typedef union __attribute__((aligned(8))) __n128
{
uint64_t n128_u64[2];
uint32_t n128_u32[4];
uint16_t n128_u16[8];
uint8_t n128_u8[16];
int64_t n128_i64[2];
int32_t n128_i32[4];
int16_t n128_i16[8];
int8_t n128_i8[16];
float n128_f32[4];
double n128_f64[2];
} __n128;
enum AppleEmbeddedNVMeControllerAction {
kNVMECTL_sendNVMECommandAction = 2,
kNVMECTL_isBFHModeAction = 3,
kNVMECTL_performBFHAction = 4,
kNVMECTL_getNandDescriptorAction = 5,
kNVMECTL_setNVMeStateAction = 6,
kNVMECTL_setPCIPortStateAction = 7,
kNVMECTL_setBFHGPIOAction = 8,
};
typedef struct NVMeIdentifyControllerStruct {
char unknown[0x1000];
} NVMeIdentifyControllerStruct;
typedef struct NVMeCommand {
} *NVMeCommand;
typedef struct FTLVersion {
uint8_t clogMajor;
uint8_t clogMinor;
uint8_t dm;
} FTLVersion;
To connect to an IOKit service in general by its name the following snippet can be used.
io_service_t io_get_service(const char *name) {
io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching(name));
io_name_t className = {};
if(MACH_PORT_VALID(service)) {
IOObjectGetClass(service, className);
printf("We got a userclient: %s::%s\n", name, className);
}
return service;
}
io_connect_t io_connect_service(io_service_t service) {
kern_return_t err = KERN_SUCCESS;
io_connect_t conn = IO_OBJECT_NULL;
io_name_t className = {};
err = IOObjectGetClass(service,className); // Retrieve the classname of the userclient
printf("Got UserClient: %s\n", className);
err = IOServiceOpen(service, mach_task_self(), 0, &conn); // Try to connect to the service
if(err != KERN_SUCCESS){
printf("Can not connect to %s\n", className);
return IO_OBJECT_NULL;
}
return conn; // Return the connection to the service
}
And finally, this is how logic implementation around the userclient looks:
#define IOSVC_NVMECTL "AppleEmbeddedNVMeController"
kern_return_t nvmecontroller_setBFHGPIO(uint64_t value) {
kern_return_t err = KERN_SUCCESS;
io_service_t nvmesvc = io_get_service(IOSVC_NVMECTL); // Try-get nvme-controller service
io_connect_t conn = IO_OBJECT_NULL;
// First make sure that we found the iombf service
if( !MACH_PORT_VALID(nvmesvc) ) {
printf("Couldn't find AppleEmbeddedNVMeController service.\n");
return KERN_FAILURE;
}
conn = io_connect_service(nvmesvc); // Open a connection to AppleEmbeddedNVMeController
// Now make sure that the connection succeeded (sandbox might change in the future)
if( !MACH_PORT_VALID(conn) ) {
printf("Could not connect to AppleEmbeddedNVMeController service.\n");
return KERN_FAILURE;
}
uint64_t output = 0;
uint32_t outputCnt = 1;
err = IOConnectCallMethod(conn, kNVMECTL_setBFHGPIOAction, &value, 1, NULL, 0, &output, &outputCnt, NULL, NULL);
if(err != KERN_SUCCESS) {
return err;
}
return err;
}
kern_return_t nvmecontroller_isBFHMode(bool *bfhMode) {
kern_return_t err = KERN_SUCCESS;
io_service_t nvmesvc = io_get_service(IOSVC_NVMECTL); // Try-get nvme-controller service
io_connect_t conn = IO_OBJECT_NULL;
if( !bfhMode )
return KERN_INVALID_ARGUMENT;
// First make sure that we found the iombf service
if( !MACH_PORT_VALID(nvmesvc) ) {
printf("Couldn't find AppleEmbeddedNVMeController service.\n");
return KERN_FAILURE;
}
conn = io_connect_service(nvmesvc); // Open a connection to AppleEmbeddedNVMeController
// Now make sure that the connection succeeded (sandbox might change in the future)
if( !MACH_PORT_VALID(conn) ) {
printf("Could not connect to AppleEmbeddedNVMeController service.\n");
return KERN_FAILURE;
}
uint64_t output = 0;
uint32_t outputCnt = 1;
err = IOConnectCallMethod(conn, kNVMECTL_isBFHModeAction, NULL, 0, NULL, 0, &output, &outputCnt, NULL, NULL);
if(err != KERN_SUCCESS) {
return err;
}
*bfhMode = output != 0;
return err;
}