AppleEmbeddedNVMeController
Revision as of 11:55, 30 January 2020 by Userlandkernel (talk | contribs)
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; }