AppleEmbeddedNVMeController

From iPhone Development Wiki
The printable version is no longer supported and may have rendering errors. Please update your browser bookmarks and please use the default browser print function instead.

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;
}