AppleEmbeddedNVMeController

From iPhone Development Wiki
Jump to: navigation, search

AppleEmbeddedNVMeController is a kernel extension for managing the NAND disk 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 uint64_t state -
7 setPCIPortState uint64_t state -
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;
}