/** * Author......: See docs/credits.txt * License.....: MIT */ #include "common.h" #include "types.h" #include "memory.h" #include "event.h" #include "timer.h" #include "ext_metal.h" #include #include #include #include /* typedef NS_ENUM(NSUInteger, hc_mtlFeatureSet) { MTL_FEATURESET_MACOS_GPUFAMILY_1_V1 = 10000, MTL_FEATURESET_MACOS_GPUFAMILY_1_V2 = 10001, MTL_FEATURESET_MACOS_GPUFAMILY_1_V3 = 10003, MTL_FEATURESET_MACOS_GPUFAMILY_1_V4 = 10004, MTL_FEATURESET_MACOS_GPUFAMILY_2_V1 = 10005, } metalDeviceFeatureSet_macOS_t; */ typedef NS_ENUM(NSUInteger, hc_mtlLanguageVersion) { MTL_LANGUAGEVERSION_1_0 = (1 << 16), MTL_LANGUAGEVERSION_1_1 = (1 << 16) + 1, MTL_LANGUAGEVERSION_1_2 = (1 << 16) + 2, MTL_LANGUAGEVERSION_2_0 = (2 << 16), MTL_LANGUAGEVERSION_2_1 = (2 << 16) + 1, MTL_LANGUAGEVERSION_2_2 = (2 << 16) + 2, MTL_LANGUAGEVERSION_2_3 = (2 << 16) + 3, MTL_LANGUAGEVERSION_2_4 = (2 << 16) + 4, } metalLanguageVersion_t; static bool iokit_getGPUCore (void *hashcat_ctx, int *gpu_core) { bool rc = false; CFMutableDictionaryRef matching = IOServiceMatching ("IOAccelerator"); io_service_t service = IOServiceGetMatchingService (hc_IOMasterPortDefault, matching); if (!service) { event_log_error (hashcat_ctx, "IOServiceGetMatchingService(): %08x", service); return rc; } // "gpu-core-count" is present only on Apple Silicon CFNumberRef num = IORegistryEntryCreateCFProperty (service, CFSTR ("gpu-core-count"), kCFAllocatorDefault, 0); int gc = 0; if (num == nil || CFNumberGetValue (num, kCFNumberIntType, &gc) == false) { //event_log_error (hashcat_ctx, "IORegistryEntryCreateCFProperty(): 'gpu-core-count' entry not found"); } else { *gpu_core = gc; rc = true; } IOObjectRelease (service); return rc; } static int hc_mtlInvocationHelper (id target, SEL selector, void *returnValue) { if (target == nil) return -1; if (selector == nil) return -1; if ([target respondsToSelector: selector]) { NSMethodSignature *signature = [object_getClass (target) instanceMethodSignatureForSelector: selector]; if (signature == nil) return -1; NSInvocation *invocation = [NSInvocation invocationWithMethodSignature: signature]; if (invocation == nil) return -1; [invocation setTarget: target]; [invocation setSelector: selector]; [invocation invoke]; [invocation getReturnValue: returnValue]; return 0; } return -1; } static int hc_mtlBuildOptionsToDict (void *hashcat_ctx, const char *build_options_buf, const char *include_path, NSMutableDictionary *build_options_dict) { if (build_options_buf == nil) { event_log_error (hashcat_ctx, "%s(): build_options_buf is NULL", __func__); return -1; } if (build_options_dict == nil) { event_log_error (hashcat_ctx, "%s(): build_options_dict is NULL", __func__); return -1; } // NSString from build_options_buf NSString *options = [NSString stringWithCString: build_options_buf encoding: NSUTF8StringEncoding]; if (options == nil) { event_log_error (hashcat_ctx, "%s(): stringWithCString failed", __func__); return -1; } // replace '-D ' to '' options = [options stringByReplacingOccurrencesOfString:@"-D " withString:@""]; if (options == nil) { event_log_error (hashcat_ctx, "%s(): stringByReplacingOccurrencesOfString(-D) failed", __func__); return -1; } // replace '-I OpenCL ' to '' options = [options stringByReplacingOccurrencesOfString:@"-I OpenCL " withString:@""]; if (options == nil) { event_log_error (hashcat_ctx, "%s(): stringByReplacingOccurrencesOfString(-I OpenCL) failed", __func__); return -1; } //NSLog(@"options: '%@'", options); // creating NSDictionary from options NSArray *lines = [options componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; for (NSString *aKeyValue in lines) { NSArray *components = [aKeyValue componentsSeparatedByString:@"="]; NSString *key = [components[0] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; NSString *value = nil; if ([components count] != 2) { if ([key isEqualToString:[NSString stringWithUTF8String:"KERNEL_STATIC"]] || [key isEqualToString:[NSString stringWithUTF8String:"IS_APPLE_SILICON"]] || [key isEqualToString:[NSString stringWithUTF8String:"DYNAMIC_LOCAL"]] || [key isEqualToString:[NSString stringWithUTF8String:"_unroll"]] || [key isEqualToString:[NSString stringWithUTF8String:"NO_UNROLL"]] || [key isEqualToString:[NSString stringWithUTF8String:"FORCE_DISABLE_SHM"]]) { value = @"1"; } else { #ifdef DEBUG const char *tmp = [key UTF8String]; if (tmp != nil && strlen (tmp) > 0) { event_log_warning (hashcat_ctx, "%s(): skipping malformed build option: '%s'", __func__, tmp); } #endif continue; } } else { value = [components[1] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; } [build_options_dict setObject:value forKey:key]; } // if set, add INCLUDE_PATH to hack Apple kernel build from source limitation on -I usage if (include_path != nil) { NSString *path_key = @"INCLUDE_PATH"; NSString *path_value = [NSString stringWithCString: include_path encoding: NSUTF8StringEncoding]; // Include path may contain spaces, escape them with a backslash path_value = [path_value stringByReplacingOccurrencesOfString:@" " withString:@"\\ "]; [build_options_dict setObject:path_value forKey:path_key]; } //NSLog(@"Dict:\n%@", build_options_dict); return 0; } int mtl_init (void *hashcat_ctx) { backend_ctx_t *backend_ctx = ((hashcat_ctx_t *) hashcat_ctx)->backend_ctx; MTL_PTR *mtl = (MTL_PTR *) backend_ctx->mtl; memset (mtl, 0, sizeof (MTL_PTR)); mtl->devices = nil; if (MTLCreateSystemDefaultDevice () == nil) { event_log_error (hashcat_ctx, "Metal is not supported on this computer"); return -1; } return 0; } void mtl_close (void *hashcat_ctx) { backend_ctx_t *backend_ctx = ((hashcat_ctx_t *) hashcat_ctx)->backend_ctx; MTL_PTR *mtl = (MTL_PTR *) backend_ctx->mtl; if (mtl) { if (mtl->devices) { int count = (int) CFArrayGetCount (mtl->devices); for (int i = 0; i < count; i++) { mtl_device_id device = (mtl_device_id) CFArrayGetValueAtIndex (mtl->devices, i); if (device != nil) { hc_mtlReleaseDevice (hashcat_ctx, device); } } mtl->devices = nil; } hcfree (backend_ctx->mtl); backend_ctx->mtl = NULL; } } int hc_mtlDeviceGetCount (void *hashcat_ctx, int *count) { backend_ctx_t *backend_ctx = ((hashcat_ctx_t *) hashcat_ctx)->backend_ctx; MTL_PTR *mtl = (MTL_PTR *) backend_ctx->mtl; if (mtl == nil) return -1; CFArrayRef devices = (CFArrayRef) MTLCopyAllDevices (); if (devices == nil) { event_log_error (hashcat_ctx, "metalDeviceGetCount(): empty device objects"); return -1; } mtl->devices = devices; *count = CFArrayGetCount (devices); return 0; } int hc_mtlDeviceGet (void *hashcat_ctx, mtl_device_id *metal_device, int ordinal) { backend_ctx_t *backend_ctx = ((hashcat_ctx_t *) hashcat_ctx)->backend_ctx; MTL_PTR *mtl = (MTL_PTR *) backend_ctx->mtl; if (mtl == nil) return -1; if (mtl->devices == nil) { event_log_error (hashcat_ctx, "%s(): invalid devices pointer", __func__); return -1; } mtl_device_id device = (mtl_device_id) CFArrayGetValueAtIndex (mtl->devices, ordinal); if (device == nil) { event_log_error (hashcat_ctx, "metalDeviceGet(): invalid index"); return -1; } *metal_device = device; return 0; } int hc_mtlDeviceGetName (void *hashcat_ctx, char *name, size_t len, mtl_device_id metal_device) { backend_ctx_t *backend_ctx = ((hashcat_ctx_t *) hashcat_ctx)->backend_ctx; MTL_PTR *mtl = (MTL_PTR *) backend_ctx->mtl; if (mtl == NULL) return -1; if (metal_device == nil) { event_log_error (hashcat_ctx, "%s(): invalid device", __func__); return -1; } if (len <= 0) { event_log_error (hashcat_ctx, "%s(): buffer length", __func__); return -1; } id device_name_ptr = [metal_device name]; if (device_name_ptr == nil) { event_log_error (hashcat_ctx, "%s(): failed to get device name", __func__); return -1; } const char *device_name_str = [device_name_ptr UTF8String]; if (device_name_str == nil) { event_log_error (hashcat_ctx, "%s(): failed to get UTF8String from device name", __func__); return -1; } const size_t device_name_len = strlen (device_name_str); if (device_name_len <= 0) { event_log_error (hashcat_ctx, "%s(): invalid device name length", __func__); return -1; } if (strncpy (name, device_name_str, (device_name_len > len) ? len : device_name_len) != name) { event_log_error (hashcat_ctx, "%s(): strncpy failed", __func__); return -1; } return 0; } int hc_mtlDeviceGetAttribute (void *hashcat_ctx, int *pi, metalDeviceAttribute_t attrib, mtl_device_id metal_device) { backend_ctx_t *backend_ctx = ((hashcat_ctx_t *) hashcat_ctx)->backend_ctx; MTL_PTR *mtl = (MTL_PTR *) backend_ctx->mtl; if (mtl == NULL) return -1; if (metal_device == nil) { event_log_error (hashcat_ctx, "%s(): invalid device", __func__); return -1; } uint64_t val64 = 0; bool valBool = false; unsigned long valULong = 0; switch (attrib) { case MTL_DEVICE_ATTRIBUTE_MULTIPROCESSOR_COUNT: // works only with Apple Silicon if (iokit_getGPUCore (hashcat_ctx, pi) == false) *pi = 1; break; case MTL_DEVICE_ATTRIBUTE_UNIFIED_MEMORY: *pi = 0; SEL hasUnifiedMemorySelector = NSSelectorFromString (@"hasUnifiedMemory"); hc_mtlInvocationHelper (metal_device, hasUnifiedMemorySelector, &valBool); *pi = (valBool == true) ? 1 : 0; break; case MTL_DEVICE_ATTRIBUTE_WARP_SIZE: // return a fake size of 32, it will be updated later *pi = 32; break; /* unused and deprecated case MTL_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MAJOR: *pi = 0; if (*pi == 0 && [metal_device supportsFeatureSet:MTL_FEATURESET_MACOS_GPUFAMILY_2_V1] == true) *pi = 2; if (*pi == 0 && [metal_device supportsFeatureSet:MTL_FEATURESET_MACOS_GPUFAMILY_1_V4] == true) *pi = 1; if (*pi == 0 && [metal_device supportsFeatureSet:MTL_FEATURESET_MACOS_GPUFAMILY_1_V3] == true) *pi = 1; if (*pi == 0 && [metal_device supportsFeatureSet:MTL_FEATURESET_MACOS_GPUFAMILY_1_V2] == true) *pi = 1; if (*pi == 0 && [metal_device supportsFeatureSet:MTL_FEATURESET_MACOS_GPUFAMILY_1_V1] == true) *pi = 1; if (*pi == 0) { //event_log_error (hashcat_ctx, "%s(): no feature sets supported", __func__); return -1; } break; */ /* unused and deprecated case MTL_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MINOR: *pi = 0; if (*pi == 0 && [metal_device supportsFeatureSet:MTL_FEATURESET_MACOS_GPUFAMILY_2_V1] == true) *pi = 1; if (*pi == 0 && [metal_device supportsFeatureSet:MTL_FEATURESET_MACOS_GPUFAMILY_1_V4] == true) *pi = 4; if (*pi == 0 && [metal_device supportsFeatureSet:MTL_FEATURESET_MACOS_GPUFAMILY_1_V3] == true) *pi = 3; if (*pi == 0 && [metal_device supportsFeatureSet:MTL_FEATURESET_MACOS_GPUFAMILY_1_V2] == true) *pi = 2; if (*pi == 0 && [metal_device supportsFeatureSet:MTL_FEATURESET_MACOS_GPUFAMILY_1_V1] == true) *pi = 1; if (*pi == 0) { //event_log_error (hashcat_ctx, "%s(): no feature sets supported", __func__); return -1; } break; */ case MTL_DEVICE_ATTRIBUTE_MAX_THREADS_PER_BLOCK: // M1 max is 1024 // [MTLComputePipelineState maxTotalThreadsPerThreadgroup] *pi = 1024; break; case MTL_DEVICE_ATTRIBUTE_CLOCK_RATE: // unknown *pi = 1000000; break; case MTL_DEVICE_ATTRIBUTE_MAX_SHARED_MEMORY_PER_BLOCK: // 32k *pi = 32768; break; case MTL_DEVICE_ATTRIBUTE_TOTAL_CONSTANT_MEMORY: // Maximum function memory allocation for a buffer in the constant address space // 64k *pi = 64 * 1024; break; case MTL_DEVICE_ATTRIBUTE_MAX_TRANSFER_RATE: val64 = 0; SEL maxTransferRateSelector = NSSelectorFromString (@"maxTransferRate"); hc_mtlInvocationHelper (metal_device, maxTransferRateSelector, &val64); *pi = (val64 == 0) ? 0 : val64 / 125; // kb/s break; case MTL_DEVICE_ATTRIBUTE_HEADLESS: valBool = [metal_device isHeadless]; *pi = (valBool == true) ? 1 : 0; break; case MTL_DEVICE_ATTRIBUTE_LOW_POWER: valBool = [metal_device isLowPower]; *pi = (valBool == true) ? 1 : 0; break; case MTL_DEVICE_ATTRIBUTE_REMOVABLE: valBool = [metal_device isRemovable]; *pi = (valBool == true) ? 1 : 0; break; case MTL_DEVICE_ATTRIBUTE_REGISTRY_ID: *pi = (int) [metal_device registryID]; break; case MTL_DEVICE_ATTRIBUTE_PHYSICAL_LOCATION: *pi = 0; SEL locationSelector = NSSelectorFromString (@"location"); valULong = 0; hc_mtlInvocationHelper (metal_device, locationSelector, &valULong); *pi = valULong; break; case MTL_DEVICE_ATTRIBUTE_LOCATION_NUMBER: *pi = 0; SEL locationNumberSelector = NSSelectorFromString (@"locationNumber"); valULong = 0; hc_mtlInvocationHelper (metal_device, locationNumberSelector, &valULong); *pi = valULong; break; default: event_log_error (hashcat_ctx, "%s(): unknown attribute (%d)", __func__, attrib); return -1; } return 0; } int hc_mtlMemGetInfo (void *hashcat_ctx, size_t *mem_free, size_t *mem_total) { backend_ctx_t *backend_ctx = ((hashcat_ctx_t *) hashcat_ctx)->backend_ctx; MTL_PTR *mtl = (MTL_PTR *) backend_ctx->mtl; if (mtl == NULL) return -1; struct vm_statistics64 vm_stats; vm_size_t page_size = 0; unsigned int count = HOST_VM_INFO64_COUNT; mach_port_t port = mach_host_self (); if (host_page_size (port, &page_size) != KERN_SUCCESS) { event_log_error (hashcat_ctx, "metalMemGetInfo(): cannot get page_size"); return -1; } if (host_statistics64 (port, HOST_VM_INFO64, (host_info64_t) &vm_stats, &count) != KERN_SUCCESS) { event_log_error (hashcat_ctx, "metalMemGetInfo(): cannot get vm_stats"); return -1; } uint64_t mem_free_tmp = (uint64_t) (vm_stats.free_count - vm_stats.speculative_count) * page_size; uint64_t mem_used_tmp = (uint64_t) (vm_stats.active_count + vm_stats.inactive_count + vm_stats.wire_count) * page_size; *mem_free = (size_t) mem_free_tmp; *mem_total = (size_t) (mem_free_tmp + mem_used_tmp); return 0; } int hc_mtlDeviceMaxMemAlloc (void *hashcat_ctx, size_t *bytes, mtl_device_id metal_device) { backend_ctx_t *backend_ctx = ((hashcat_ctx_t *) hashcat_ctx)->backend_ctx; MTL_PTR *mtl = (MTL_PTR *) backend_ctx->mtl; if (mtl == NULL) return -1; if (metal_device == nil) { event_log_error (hashcat_ctx, "%s(): invalid device", __func__); return -1; } uint64_t memsize = 0; SEL maxBufferLengthSelector = NSSelectorFromString (@"maxBufferLength"); if (hc_mtlInvocationHelper (metal_device, maxBufferLengthSelector, &memsize) == -1) return -1; if (memsize == 0) { event_log_error (hashcat_ctx, "%s(): invalid maxBufferLength", __func__); return -1; } *bytes = (size_t) memsize; return 0; } int hc_mtlDeviceTotalMem (void *hashcat_ctx, size_t *bytes, mtl_device_id metal_device) { backend_ctx_t *backend_ctx = ((hashcat_ctx_t *) hashcat_ctx)->backend_ctx; MTL_PTR *mtl = (MTL_PTR *) backend_ctx->mtl; if (mtl == NULL) return -1; if (metal_device == nil) { event_log_error (hashcat_ctx, "%s(): invalid device", __func__); return -1; } uint64_t memsize = 0; if (true) { memsize = [metal_device recommendedMaxWorkingSetSize]; } else { size_t len = sizeof (memsize); if (sysctlbyname ("hw.memsize", &memsize, &len, NULL, 0) != 0) { event_log_error (hashcat_ctx, "%s(): sysctlbyname(hw.memsize) failed", __func__); return -1; } } if (memsize == 0) { event_log_error (hashcat_ctx, "%s(): invalid memory size", __func__); return -1; } *bytes = (size_t) memsize; return 0; } int hc_mtlCreateCommandQueue (void *hashcat_ctx, mtl_device_id metal_device, mtl_command_queue *command_queue) { backend_ctx_t *backend_ctx = ((hashcat_ctx_t *) hashcat_ctx)->backend_ctx; MTL_PTR *mtl = (MTL_PTR *) backend_ctx->mtl; if (mtl == NULL) return -1; if (metal_device == nil) { event_log_error (hashcat_ctx, "%s(): invalid device", __func__); return -1; } mtl_command_queue queue = [metal_device newCommandQueue]; if (queue == nil) { event_log_error (hashcat_ctx, "%s(): failed to create newCommandQueue", __func__); return -1; } *command_queue = queue; return 0; } int hc_mtlCreateKernel (void *hashcat_ctx, mtl_device_id metal_device, mtl_library metal_library, const char *func_name, mtl_function *metal_function, mtl_pipeline *metal_pipeline) { backend_ctx_t *backend_ctx = ((hashcat_ctx_t *) hashcat_ctx)->backend_ctx; user_options_t *user_options = ((hashcat_ctx_t *) hashcat_ctx)->user_options; MTL_PTR *mtl = (MTL_PTR *) backend_ctx->mtl; if (mtl == NULL) return -1; if (metal_device == nil) { event_log_error (hashcat_ctx, "%s(): invalid device", __func__); return -1; } if (metal_library == nil) { event_log_error (hashcat_ctx, "%s(): invalid library", __func__); return -1; } if (func_name == nil) { event_log_error (hashcat_ctx, "%s(): invalid function name", __func__); return -1; } __block NSError *error = nil; NSString *f_name = [NSString stringWithCString: func_name encoding: NSUTF8StringEncoding]; mtl_function mtl_func = [metal_library newFunctionWithName: f_name]; if (mtl_func == nil) { event_log_error (hashcat_ctx, "%s(): failed to create '%s' function", __func__, func_name); return -1; } // workaround for MTLCompilerService 'Infinite Loop' bug /* mtl_pipeline mtl_pipe = [metal_device newComputePipelineStateWithFunction: mtl_func error: &error]; if (error != nil) { event_log_error (hashcat_ctx, "%s(): failed to create '%s' pipeline, %s", __func__, func_name, [[error localizedDescription] UTF8String]); return -1; } */ error = nil; __block mtl_pipeline mtl_pipe; dispatch_group_t group = dispatch_group_create (); dispatch_queue_t queue = dispatch_get_global_queue (DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); // if no user-defined runtime, set to METAL_COMPILER_RUNTIME long timeout = (user_options->metal_compiler_runtime > 0) ? user_options->metal_compiler_runtime : METAL_COMPILER_RUNTIME; dispatch_time_t when = dispatch_time (DISPATCH_TIME_NOW,NSEC_PER_SEC * timeout); __block int rc_async_err = 0; dispatch_group_async (group, queue, ^(void) { mtl_pipe = [metal_device newComputePipelineStateWithFunction: mtl_func error: &error]; if (error != nil) { event_log_error (hashcat_ctx, "%s(): failed to create '%s' pipeline, %s", __func__, func_name, [[error localizedDescription] UTF8String]); rc_async_err = -1; } }); long rc_queue = dispatch_group_wait (group, when); dispatch_release (group); if (rc_async_err != 0) return -1; if (rc_queue != 0) { event_log_error (hashcat_ctx, "%s(): failed to create '%s' pipeline, timeout reached (status %ld)", __func__, func_name, rc_queue); return -1; } if (mtl_pipe == nil) { event_log_error (hashcat_ctx, "%s(): failed to create '%s' pipeline", __func__, func_name); return -1; } *metal_function = mtl_func; *metal_pipeline = mtl_pipe; return 0; } int hc_mtlGetMaxTotalThreadsPerThreadgroup (void *hashcat_ctx, mtl_pipeline metal_pipeline, unsigned int *maxTotalThreadsPerThreadgroup) { backend_ctx_t *backend_ctx = ((hashcat_ctx_t *) hashcat_ctx)->backend_ctx; MTL_PTR *mtl = (MTL_PTR *) backend_ctx->mtl; if (mtl == NULL) return -1; if (metal_pipeline == nil) { event_log_error (hashcat_ctx, "%s(): invalid pipeline", __func__); return -1; } *maxTotalThreadsPerThreadgroup = [metal_pipeline maxTotalThreadsPerThreadgroup]; return 0; } int hc_mtlGetThreadExecutionWidth (void *hashcat_ctx, mtl_pipeline metal_pipeline, unsigned int *threadExecutionWidth) { backend_ctx_t *backend_ctx = ((hashcat_ctx_t *) hashcat_ctx)->backend_ctx; MTL_PTR *mtl = (MTL_PTR *) backend_ctx->mtl; if (mtl == NULL) return -1; if (metal_pipeline == nil) { event_log_error (hashcat_ctx, "%s(): invalid pipeline", __func__); return -1; } *threadExecutionWidth = [metal_pipeline threadExecutionWidth]; return 0; } int hc_mtlCreateBuffer (void *hashcat_ctx, mtl_device_id metal_device, size_t size, void *ptr, mtl_mem *metal_buffer) { backend_ctx_t *backend_ctx = ((hashcat_ctx_t *) hashcat_ctx)->backend_ctx; MTL_PTR *mtl = (MTL_PTR *) backend_ctx->mtl; if (mtl == NULL) return -1; if (metal_device == nil) { event_log_error (hashcat_ctx, "%s(): invalid device", __func__); return -1; } mtl_mem buf = NULL; MTLResourceOptions bufferOptions = MTLResourceStorageModeShared; if (ptr == NULL) { buf = [metal_device newBufferWithLength:size options:bufferOptions]; } else { buf = [metal_device newBufferWithBytes:ptr length:size options:bufferOptions]; } if (buf == nil) { event_log_error (hashcat_ctx, "%s(): %s failed (size: %zu)", __func__, (ptr == NULL) ? "newBufferWithLength" : "newBufferWithBytes", size); return -1; } *metal_buffer = buf; return 0; } int hc_mtlReleaseMemObject (void *hashcat_ctx, mtl_mem metal_buffer) { backend_ctx_t *backend_ctx = ((hashcat_ctx_t *) hashcat_ctx)->backend_ctx; MTL_PTR *mtl = (MTL_PTR *) backend_ctx->mtl; if (mtl == NULL) return -1; if (metal_buffer == nil) { event_log_error (hashcat_ctx, "%s(): invalid metal buffer", __func__); return -1; } [metal_buffer setPurgeableState:MTLPurgeableStateEmpty]; [metal_buffer release]; return 0; } int hc_mtlReleaseFunction (void *hashcat_ctx, mtl_function metal_function) { if (metal_function == nil) { event_log_error (hashcat_ctx, "%s(): invalid metal function", __func__); return -1; } [metal_function release]; return 0; } int hc_mtlReleaseLibrary (void *hashcat_ctx, mtl_library metal_library) { if (metal_library == nil) { event_log_error (hashcat_ctx, "%s(): invalid metal library", __func__); return -1; } [metal_library release]; metal_library = nil; return 0; } int hc_mtlReleaseCommandQueue (void *hashcat_ctx, mtl_command_queue command_queue) { if (command_queue == nil) { event_log_error (hashcat_ctx, "%s(): invalid metal command queue", __func__); return -1; } [command_queue release]; command_queue = nil; return 0; } int hc_mtlReleaseDevice (void *hashcat_ctx, mtl_device_id metal_device) { if (metal_device == nil) { event_log_error (hashcat_ctx, "%s(): invalid metal device", __func__); return -1; } [metal_device release]; metal_device = nil; return 0; } // device to device int hc_mtlMemcpyDtoD (void *hashcat_ctx, mtl_command_queue command_queue, mtl_mem buf_dst, size_t buf_dst_off, mtl_mem buf_src, size_t buf_src_off, size_t buf_size) { if (command_queue == nil) { event_log_error (hashcat_ctx, "%s(): metal command queue is invalid", __func__); return -1; } if (buf_src == nil) { event_log_error (hashcat_ctx, "%s(): metal src buffer is invalid", __func__); return -1; } if (buf_src_off < 0) { event_log_error (hashcat_ctx, "%s(): src buffer offset is invalid", __func__); return -1; } if (buf_dst == nil) { event_log_error (hashcat_ctx, "%s(): metal dst buffer is invalid", __func__); return -1; } if (buf_dst_off < 0) { event_log_error (hashcat_ctx, "%s(): dst buffer offset is invalid", __func__); return -1; } if (buf_size <= 0) { event_log_error (hashcat_ctx, "%s(): buffer size is invalid", __func__); return -1; } id command_buffer = [command_queue commandBuffer]; if (command_buffer == nil) { event_log_error (hashcat_ctx, "%s(): failed to create a new command buffer", __func__); return -1; } id blit_encoder = [command_buffer blitCommandEncoder]; if (blit_encoder == nil) { event_log_error (hashcat_ctx, "%s(): failed to create a blit command encoder", __func__); return -1; } // copy [blit_encoder copyFromBuffer: buf_src sourceOffset: buf_src_off toBuffer: buf_dst destinationOffset: buf_dst_off size: buf_size]; // finish encoding and start the data transfer [blit_encoder endEncoding]; [command_buffer commit]; // Wait for complete [command_buffer waitUntilCompleted]; return 0; } // host to device int hc_mtlMemcpyHtoD (void *hashcat_ctx, mtl_command_queue command_queue, mtl_mem buf_dst, size_t buf_dst_off, const void *buf_src, size_t buf_size) { if (command_queue == nil) { event_log_error (hashcat_ctx, "%s(): metal command queue is invalid", __func__); return -1; } if (buf_src == nil) { event_log_error (hashcat_ctx, "%s(): metal src buffer is invalid", __func__); return -1; } if (buf_dst == nil) { event_log_error (hashcat_ctx, "%s(): host dst buffer is invalid", __func__); return -1; } if (buf_size <= 0) { event_log_error (hashcat_ctx, "%s(): buffer size is invalid", __func__); return -1; } if (buf_dst_off < 0) { event_log_error (hashcat_ctx, "%s(): buffer dst offset is invalid", __func__); return -1; } void *buf_dst_ptr = [buf_dst contents]; if (buf_dst_ptr == nil) { event_log_error (hashcat_ctx, "%s(): failed to get metal buffer data pointer", __func__); return -1; } if (memcpy (buf_dst_ptr + buf_dst_off, buf_src, buf_size) != buf_dst_ptr + buf_dst_off) { event_log_error (hashcat_ctx, "%s(): memcpy failed", __func__); return -1; } [buf_dst didModifyRange: NSMakeRange (buf_dst_off, buf_size)]; return 0; } // device to host int hc_mtlMemcpyDtoH (void *hashcat_ctx, mtl_command_queue command_queue, void *buf_dst, mtl_mem buf_src, size_t buf_src_off, size_t buf_size) { if (command_queue == nil) { event_log_error (hashcat_ctx, "%s(): metal command queue is invalid", __func__); return -1; } if (buf_src == nil) { event_log_error (hashcat_ctx, "%s(): metal src buffer is invalid", __func__); return -1; } if (buf_dst == nil) { event_log_error (hashcat_ctx, "%s(): host dst buffer is invalid", __func__); return -1; } if (buf_size <= 0) { event_log_error (hashcat_ctx, "%s(): buffer size is invalid", __func__); return -1; } id command_buffer = [command_queue commandBuffer]; if (command_buffer == nil) { event_log_error (hashcat_ctx, "%s(): failed to create a new command buffer", __func__); return -1; } id blit_encoder = [command_buffer blitCommandEncoder]; [blit_encoder synchronizeResource: buf_src]; // Finish encoding and start the data transfer to the CPU [blit_encoder endEncoding]; [command_buffer commit]; // Wait for complete [command_buffer waitUntilCompleted]; // get src buf ptr void *buf_src_ptr = [buf_src contents]; if (buf_src_ptr == nil) { event_log_error (hashcat_ctx, "%s(): failed to get metal buffer data pointer", __func__); return -1; } if (memcpy (buf_dst, buf_src_ptr + buf_src_off, buf_size) != buf_dst) { event_log_error (hashcat_ctx, "%s(): memcpy failed", __func__); return -1; } return 0; } int hc_mtlRuntimeGetVersionString (void *hashcat_ctx, char *runtimeVersion_str, size_t *size) { CFURLRef plist_url = CFURLCreateWithFileSystemPath (kCFAllocatorDefault, CFSTR ("/System/Library/Frameworks/Metal.framework/Versions/Current/Resources/version.plist"), kCFURLPOSIXPathStyle, false); if (plist_url == NULL) { event_log_error (hashcat_ctx, "%s(): CFURLCreateWithFileSystemPath() failed\n", __func__); return -1; } CFReadStreamRef plist_stream = CFReadStreamCreateWithFile (NULL, plist_url); if (plist_stream == NULL) { event_log_error (hashcat_ctx, "%s(): CFReadStreamCreateWithFile() failed\n", __func__); CFRelease (plist_url); return -1; } if (CFReadStreamOpen (plist_stream) == false) { event_log_error (hashcat_ctx, "%s(): CFReadStreamOpen() failed\n", __func__); CFRelease (plist_stream); CFRelease (plist_url); return -1; } CFPropertyListRef plist_prop = CFPropertyListCreateWithStream (NULL, plist_stream, 0, kCFPropertyListImmutable, NULL, NULL); if (plist_prop == NULL) { event_log_error (hashcat_ctx, "%s(): CFPropertyListCreateWithStream() failed\n", __func__); CFReadStreamClose (plist_stream); CFRelease (plist_stream); CFRelease (plist_url); return -1; } CFStringRef runtime_version_str = CFRetain (CFDictionaryGetValue (plist_prop, CFSTR ("CFBundleVersion"))); if (runtime_version_str != NULL) { if (runtimeVersion_str == NULL) { CFIndex len = CFStringGetLength (runtime_version_str); CFIndex maxSize = CFStringGetMaximumSizeForEncoding (len, kCFStringEncodingUTF8) + 1; *size = maxSize; return 0; } CFIndex maxSize = *size; if (CFStringGetCString (runtime_version_str, runtimeVersion_str, maxSize, kCFStringEncodingUTF8) == false) { event_log_error (hashcat_ctx, "%s(): CFStringGetCString() failed\n", __func__); hcfree (runtimeVersion_str); return -1; } return 0; } return -1; } int hc_mtlEncodeComputeCommand_pre (void *hashcat_ctx, mtl_pipeline metal_pipeline, mtl_command_queue metal_command_queue, mtl_command_buffer *metal_command_buffer, mtl_command_encoder *metal_command_encoder) { if (metal_pipeline == nil) { event_log_error (hashcat_ctx, "%s(): invalid metal_pipeline", __func__); return -1; } if (metal_command_queue == nil) { event_log_error (hashcat_ctx, "%s(): invalid metal_command_queue", __func__); return -1; } id metal_commandBuffer = [metal_command_queue commandBuffer]; if (metal_commandBuffer == nil) { event_log_error (hashcat_ctx, "%s(): invalid metal_commandBuffer", __func__); return -1; } id metal_commandEncoder = [metal_commandBuffer computeCommandEncoder]; if (metal_commandEncoder == nil) { event_log_error (hashcat_ctx, "%s(): invalid metal_commandBuffer", __func__); return -1; } [metal_commandEncoder setComputePipelineState: metal_pipeline]; *metal_command_buffer = metal_commandBuffer; *metal_command_encoder = metal_commandEncoder; return 0; } int hc_mtlSetCommandEncoderArg (void *hashcat_ctx, mtl_command_encoder metal_command_encoder, size_t off, size_t idx, mtl_mem buf, void *host_data, size_t host_data_size) { if (metal_command_encoder == nil) { event_log_error (hashcat_ctx, "%s(): invalid metal_command_encoder", __func__); return -1; } if (buf == nil && host_data == nil) { event_log_error (hashcat_ctx, "%s(): invalid buf/host_data", __func__); return -1; } if (buf == nil) { if (host_data_size <= 0) { event_log_error (hashcat_ctx, "%s(): invalid host_data size", __func__); return -1; } } else { if (off < 0) { event_log_error (hashcat_ctx, "%s(): invalid buf off", __func__); return -1; } } if (idx < 0) { event_log_error (hashcat_ctx, "%s(): invalid buf/host_data idx", __func__); return -1; } if (host_data == nil) { [metal_command_encoder setBuffer: buf offset: off atIndex: idx]; } else { [metal_command_encoder setBytes: host_data length: host_data_size atIndex: idx]; } return 0; } int hc_mtlEncodeComputeCommand (void *hashcat_ctx, mtl_command_encoder metal_command_encoder, mtl_command_buffer metal_command_buffer, size_t global_work_size, size_t local_work_size, double *ms) { MTLSize numThreadgroups = {local_work_size, 1, 1}; MTLSize threadsGroup = {global_work_size, 1, 1}; if (metal_command_encoder == nil) { event_log_error (hashcat_ctx, "%s(): invalid metal_command_encoder", __func__); return -1; } if (metal_command_buffer == nil) { event_log_error (hashcat_ctx, "%s(): invalid metal_command_buffer", __func__); return -1; } [metal_command_encoder dispatchThreadgroups: threadsGroup threadsPerThreadgroup: numThreadgroups]; [metal_command_encoder endEncoding]; [metal_command_buffer commit]; [metal_command_buffer waitUntilCompleted]; CFTimeInterval myGPUStartTime = 0; CFTimeInterval myGPUEndTime = 0; SEL myGPUStartTimeSelector = NSSelectorFromString (@"GPUStartTime"); SEL myGPUEndTimeSelector = NSSelectorFromString (@"GPUEndTime"); if (hc_mtlInvocationHelper (metal_command_buffer, myGPUStartTimeSelector, &myGPUStartTime) == -1) return -1; if (hc_mtlInvocationHelper (metal_command_buffer, myGPUEndTimeSelector, &myGPUEndTime) == -1) return -1; CFTimeInterval elapsed = myGPUEndTime - myGPUStartTime; *ms = (1000.0 * elapsed); return 0; } int hc_mtlCreateLibraryWithFile (void *hashcat_ctx, mtl_device_id metal_device, const char *cached_file, mtl_library *metal_library) { NSError *error = nil; if (metal_device == nil) { event_log_error (hashcat_ctx, "%s(): invalid metal device", __func__); return -1; } if (cached_file == nil) { event_log_error (hashcat_ctx, "%s(): invalid metallib", __func__); return -1; } NSString *k_string = [NSString stringWithCString: cached_file encoding: NSUTF8StringEncoding]; if (k_string != nil) { id r = [metal_device newLibraryWithFile: k_string error: &error]; if (error != nil) { event_log_error (hashcat_ctx, "%s(): failed to create metal library from metallib, %s", __func__, [[error localizedDescription] UTF8String]); return -1; } *metal_library = r; return 0; } return -1; } int hc_mtlCreateLibraryWithSource (void *hashcat_ctx, mtl_device_id metal_device, const char *kernel_sources, const char *build_options_buf, const char *cpath, mtl_library *metal_library) { NSError *error = nil; NSString *k_string = [NSString stringWithCString: kernel_sources encoding: NSUTF8StringEncoding]; if (k_string != nil) { MTLCompileOptions *compileOptions = [MTLCompileOptions new]; NSMutableDictionary *build_options_dict = nil; if (build_options_buf != nil) { //printf ("using build_opts from arg:\n%s\n", build_options_buf); build_options_dict = [NSMutableDictionary dictionary]; //[[NSMutableDictionary alloc] init]; if (hc_mtlBuildOptionsToDict (hashcat_ctx, build_options_buf, cpath, build_options_dict) == -1) { event_log_error (hashcat_ctx, "%s(): failed to build options dictionary", __func__); [build_options_dict release]; return -1; } compileOptions.preprocessorMacros = build_options_dict; } // todo: detect current os version and choose the right // compileOptions.languageVersion = MTL_LANGUAGEVERSION_2_3; /* if (@available(macOS 12.0, *)) { compileOptions.languageVersion = MTL_LANGUAGEVERSION_2_4; } else if (@available(macOS 11.0, *)) { compileOptions.languageVersion = MTL_LANGUAGEVERSION_2_3; } else if (@available(macOS 10.15, *)) { compileOptions.languageVersion = MTL_LANGUAGEVERSION_2_2; } else if (@available(macOS 10.14, *)) { compileOptions.languageVersion = MTL_LANGUAGEVERSION_2_1; } else if (@available(macOS 10.13, *)) { compileOptions.languageVersion = MTL_LANGUAGEVERSION_2_0; } else if (@available(macOS 10.12, *)) { compileOptions.languageVersion = MTL_LANGUAGEVERSION_1_2; } else if (@available(macOS 10.11, *)) { compileOptions.languageVersion = MTL_LANGUAGEVERSION_1_1; } */ id r = [metal_device newLibraryWithSource: k_string options: compileOptions error: &error]; [compileOptions release]; compileOptions = nil; if (build_options_dict != nil) { [build_options_dict release]; build_options_dict = nil; } if (error != nil) { event_log_error (hashcat_ctx, "%s(): failed to create metal library, %s", __func__, [[error localizedDescription] UTF8String]); return -1; } *metal_library = r; return 0; } return -1; }