/* * Releases a shared memory from the Secure World */ int tf_release_shared_memory( struct tf_connection *connection, union tf_command *command, union tf_answer *answer) { int error = 0; dprintk(KERN_DEBUG "tf_release_shared_memory(%p)\n", connection); command->release_shared_memory.message_size = (sizeof(struct tf_command_release_shared_memory) - sizeof(struct tf_command_header)) / 4; command->release_shared_memory.device_context = connection->device_context; error = tf_send_receive( &connection->dev->sm, command, answer, connection, true); if ((error != 0) || (answer->release_shared_memory.error_code != S_SUCCESS)) goto error; /* Use block_id to get back the pointer to shmem_desc */ tf_unmap_shmem( connection, (struct tf_shmem_desc *) answer->release_shared_memory.block_id, 0); /* successful completion */ dprintk(KERN_INFO "tf_release_shared_memory(%p):" " block_id=0x%08x block=0x%08x\n", connection, answer->release_shared_memory.block_id, command->release_shared_memory.block); return 0; error: if (error != 0) dprintk(KERN_ERR "tf_release_shared_memory returns %d\n", error); else dprintk(KERN_ERR "tf_release_shared_memory returns " "nChannelStatus 0x%08X\n", answer->release_shared_memory.error_code); return error; }
/* * Clean up a list of shared memory descriptors. */ static void tf_shared_memory_cleanup_list( struct tf_connection *connection, struct list_head *shmem_desc_list) { while (!list_empty(shmem_desc_list)) { struct tf_shmem_desc *shmem_desc; shmem_desc = list_first_entry(shmem_desc_list, struct tf_shmem_desc, list); tf_unmap_shmem(connection, shmem_desc, 1); } }
static long tf_ctrl_device_ioctl(struct file *file, unsigned int ioctl_num, unsigned long ioctl_param) { int result = S_SUCCESS; struct tf_pa_ctrl pa_ctrl; struct tf_device *dev = tf_get_device(); dpr_info("%s(%p, %u, %p)\n", __func__, file, ioctl_num, (void *) ioctl_param); mutex_lock(&dev->dev_mutex); if (ioctl_num != IOCTL_TF_PA_CTRL) { dpr_err("%s(%p): ioctl number is invalid (%p)\n", __func__, file, (void *)ioctl_num); result = -EFAULT; goto exit; } if ((ioctl_param & 0x3) != 0) { dpr_err("%s(%p): ioctl command message pointer is not word " "aligned (%p)\n", __func__, file, (void *)ioctl_param); result = -EFAULT; goto exit; } if (copy_from_user(&pa_ctrl, (struct tf_pa_ctrl *)ioctl_param, sizeof(struct tf_pa_ctrl))) { dpr_err("%s(%p): cannot access ioctl parameter (%p)\n", __func__, file, (void *)ioctl_param); result = -EFAULT; goto exit; } switch (pa_ctrl.nPACommand) { case TF_PA_CTRL_START: { struct tf_shmem_desc *shmem_desc = NULL; u32 shared_mem_descriptors[TF_MAX_COARSE_PAGES]; u32 descriptor_count; u32 offset; struct tf_connection *connection; dpr_info("%s(%p): Start the SMC PA (%d bytes) with conf " "(%d bytes)\n", __func__, file, pa_ctrl.pa_size, pa_ctrl.conf_size); connection = tf_conn_from_file(file); if (dev->workspace_addr == 0) { result = -ENOMEM; goto start_exit; } result = tf_validate_shmem_and_flags( (u32)pa_ctrl.conf_buffer, pa_ctrl.conf_size, TF_SHMEM_TYPE_READ); if (result != 0) goto start_exit; offset = 0; result = tf_map_shmem( connection, (u32)pa_ctrl.conf_buffer, TF_SHMEM_TYPE_READ, true, /* in user space */ shared_mem_descriptors, &offset, pa_ctrl.conf_size, &shmem_desc, &descriptor_count); if (result != 0) goto start_exit; if (descriptor_count > 1) { dpr_err("%s(%p): configuration file is too long (%d)\n", __func__, file, descriptor_count); result = -ENOMEM; goto start_exit; } result = tf_start(&dev->sm, dev->workspace_addr, dev->workspace_size, pa_ctrl.pa_buffer, pa_ctrl.pa_size, shared_mem_descriptors[0], offset, pa_ctrl.conf_size); if (result) dpr_err("SMC: start failed\n"); else dpr_info("SMC: started\n"); start_exit: tf_unmap_shmem(connection, shmem_desc, true); /* full cleanup */ break; } case TF_PA_CTRL_STOP: dpr_info("%s(%p): Stop the SMC PA\n", __func__, file); result = tf_power_management(&dev->sm, TF_POWER_OPERATION_SHUTDOWN); if (result) dpr_err("SMC: stop failed [0x%x]\n", result); else dpr_info("SMC: stopped\n"); break; default: result = -EOPNOTSUPP; break; } exit: mutex_unlock(&dev->dev_mutex); return result; }
/* * Opens a client session to the Secure World */ int tf_open_client_session( struct tf_connection *connection, union tf_command *command, union tf_answer *answer) { int error = 0; struct tf_shmem_desc *shmem_desc[4] = {NULL}; u32 i; dprintk(KERN_INFO "tf_open_client_session(%p)\n", connection); /* * Initialize the message size with no login data. This will be later * adjusted the the cases below */ command->open_client_session.message_size = (sizeof(struct tf_command_open_client_session) - 20 - sizeof(struct tf_command_header))/4; switch (command->open_client_session.login_type) { case TF_LOGIN_PUBLIC: /* Nothing to do */ break; case TF_LOGIN_USER: /* * Send the EUID of the calling application in the login data. * Update message size. */ *(u32 *) &command->open_client_session.login_data = current_euid(); #ifndef CONFIG_ANDROID command->open_client_session.login_type = (u32) TF_LOGIN_USER_LINUX_EUID; #else command->open_client_session.login_type = (u32) TF_LOGIN_USER_ANDROID_EUID; #endif /* Added one word */ command->open_client_session.message_size += 1; break; case TF_LOGIN_GROUP: { /* Check requested GID */ gid_t requested_gid = *(u32 *) command->open_client_session.login_data; if (!tf_check_gid(requested_gid)) { dprintk(KERN_ERR "tf_open_client_session(%p) " "TF_LOGIN_GROUP: requested GID (0x%x) does " "not match real eGID (0x%x)" "or any of the supplementary GIDs\n", connection, requested_gid, current_egid()); error = -EACCES; goto error; } #ifndef CONFIG_ANDROID command->open_client_session.login_type = TF_LOGIN_GROUP_LINUX_GID; #else command->open_client_session.login_type = TF_LOGIN_GROUP_ANDROID_GID; #endif command->open_client_session.message_size += 1; /* GID */ break; } #ifndef CONFIG_ANDROID case TF_LOGIN_APPLICATION: { /* * Compute SHA-1 hash of the application fully-qualified path * name. Truncate the hash to 16 bytes and send it as login * data. Update message size. */ u8 pSHA1Hash[SHA1_DIGEST_SIZE]; error = tf_hash_application_path_and_data(pSHA1Hash, NULL, 0); if (error != 0) { dprintk(KERN_ERR "tf_open_client_session: " "error in tf_hash_application_path_and_data\n"); goto error; } memcpy(&command->open_client_session.login_data, pSHA1Hash, 16); command->open_client_session.login_type = TF_LOGIN_APPLICATION_LINUX_PATH_SHA1_HASH; /* 16 bytes */ command->open_client_session.message_size += 4; break; } #else case TF_LOGIN_APPLICATION: /* * Send the real UID of the calling application in the login * data. Update message size. */ *(u32 *) &command->open_client_session.login_data = current_uid(); command->open_client_session.login_type = (u32) TF_LOGIN_APPLICATION_ANDROID_UID; /* Added one word */ command->open_client_session.message_size += 1; break; #endif #ifndef CONFIG_ANDROID case TF_LOGIN_APPLICATION_USER: { /* * Compute SHA-1 hash of the concatenation of the application * fully-qualified path name and the EUID of the calling * application. Truncate the hash to 16 bytes and send it as * login data. Update message size. */ u8 pSHA1Hash[SHA1_DIGEST_SIZE]; error = tf_hash_application_path_and_data(pSHA1Hash, (u8 *) &(current_euid()), sizeof(current_euid())); if (error != 0) { dprintk(KERN_ERR "tf_open_client_session: " "error in tf_hash_application_path_and_data\n"); goto error; } memcpy(&command->open_client_session.login_data, pSHA1Hash, 16); command->open_client_session.login_type = TF_LOGIN_APPLICATION_USER_LINUX_PATH_EUID_SHA1_HASH; /* 16 bytes */ command->open_client_session.message_size += 4; break; } #else case TF_LOGIN_APPLICATION_USER: /* * Send the real UID and the EUID of the calling application in * the login data. Update message size. */ *(u32 *) &command->open_client_session.login_data = current_uid(); *(u32 *) &command->open_client_session.login_data[4] = current_euid(); command->open_client_session.login_type = TF_LOGIN_APPLICATION_USER_ANDROID_UID_EUID; /* Added two words */ command->open_client_session.message_size += 2; break; #endif #ifndef CONFIG_ANDROID case TF_LOGIN_APPLICATION_GROUP: { /* * Check requested GID. Compute SHA-1 hash of the concatenation * of the application fully-qualified path name and the * requested GID. Update message size */ gid_t requested_gid; u8 pSHA1Hash[SHA1_DIGEST_SIZE]; requested_gid = *(u32 *) &command->open_client_session. login_data; if (!tf_check_gid(requested_gid)) { dprintk(KERN_ERR "tf_open_client_session(%p) " "TF_LOGIN_APPLICATION_GROUP: requested GID (0x%x) " "does not match real eGID (0x%x)" "or any of the supplementary GIDs\n", connection, requested_gid, current_egid()); error = -EACCES; goto error; } error = tf_hash_application_path_and_data(pSHA1Hash, &requested_gid, sizeof(u32)); if (error != 0) { dprintk(KERN_ERR "tf_open_client_session: " "error in tf_hash_application_path_and_data\n"); goto error; } memcpy(&command->open_client_session.login_data, pSHA1Hash, 16); command->open_client_session.login_type = TF_LOGIN_APPLICATION_GROUP_LINUX_PATH_GID_SHA1_HASH; /* 16 bytes */ command->open_client_session.message_size += 4; break; } #else case TF_LOGIN_APPLICATION_GROUP: { /* * Check requested GID. Send the real UID and the requested GID * in the login data. Update message size. */ gid_t requested_gid; requested_gid = *(u32 *) &command->open_client_session. login_data; if (!tf_check_gid(requested_gid)) { dprintk(KERN_ERR "tf_open_client_session(%p) " "TF_LOGIN_APPLICATION_GROUP: requested GID (0x%x) " "does not match real eGID (0x%x)" "or any of the supplementary GIDs\n", connection, requested_gid, current_egid()); error = -EACCES; goto error; } *(u32 *) &command->open_client_session.login_data = current_uid(); *(u32 *) &command->open_client_session.login_data[4] = requested_gid; command->open_client_session.login_type = TF_LOGIN_APPLICATION_GROUP_ANDROID_UID_GID; /* Added two words */ command->open_client_session.message_size += 2; break; } #endif case TF_LOGIN_PRIVILEGED: /* A privileged login may be performed only on behalf of the kernel itself or on behalf of a process with euid=0 or egid=0 or euid=system or egid=system. */ if (connection->owner == TF_CONNECTION_OWNER_KERNEL) { dprintk(KERN_DEBUG "tf_open_client_session: " "TF_LOGIN_PRIVILEGED for kernel API\n"); } else if ((current_euid() != TF_PRIVILEGED_UID_GID) && (current_egid() != TF_PRIVILEGED_UID_GID) && (current_euid() != 0) && (current_egid() != 0)) { dprintk(KERN_ERR "tf_open_client_session: " " user %d, group %d not allowed to open " "session with TF_LOGIN_PRIVILEGED\n", current_euid(), current_egid()); error = -EACCES; goto error; } else { dprintk(KERN_DEBUG "tf_open_client_session: " "TF_LOGIN_PRIVILEGED for %u:%u\n", current_euid(), current_egid()); } command->open_client_session.login_type = TF_LOGIN_PRIVILEGED; break; case TF_LOGIN_AUTHENTICATION: { /* * Compute SHA-1 hash of the application binary * Send this hash as the login data (20 bytes) */ u8 *hash; hash = &(command->open_client_session.login_data[0]); error = tf_get_current_process_hash(hash); if (error != 0) { dprintk(KERN_ERR "tf_open_client_session: " "error in tf_get_current_process_hash\n"); goto error; } command->open_client_session.login_type = TF_LOGIN_AUTHENTICATION_BINARY_SHA1_HASH; /* 20 bytes */ command->open_client_session.message_size += 5; break; } case TF_LOGIN_PRIVILEGED_KERNEL: /* A kernel login may be performed only on behalf of the kernel itself. */ if (connection->owner == TF_CONNECTION_OWNER_KERNEL) { dprintk(KERN_DEBUG "tf_open_client_session: " "TF_LOGIN_PRIVILEGED_KERNEL for kernel API\n"); command->open_client_session.login_type = TF_LOGIN_PRIVILEGED_KERNEL; } else { dprintk(KERN_ERR "tf_open_client_session: " " user %d, group %d not allowed to open " "session with TF_LOGIN_PRIVILEGED_KERNEL\n", current_euid(), current_egid()); error = -EACCES; goto error; } command->open_client_session.login_type = TF_LOGIN_PRIVILEGED_KERNEL; break; default: dprintk(KERN_ERR "tf_open_client_session: " "unknown login_type(%08X)\n", command->open_client_session.login_type); error = -EOPNOTSUPP; goto error; } /* Map the temporary memory references */ for (i = 0; i < 4; i++) { int param_type; param_type = TF_GET_PARAM_TYPE( command->open_client_session.param_types, i); if ((param_type & (TF_PARAM_TYPE_MEMREF_FLAG | TF_PARAM_TYPE_REGISTERED_MEMREF_FLAG)) == TF_PARAM_TYPE_MEMREF_FLAG) { /* Map temp mem ref */ error = tf_map_temp_shmem(connection, &command->open_client_session. params[i].temp_memref, param_type, &shmem_desc[i]); if (error != 0) { dprintk(KERN_ERR "tf_open_client_session: " "unable to map temporary memory block " "(%08X)\n", error); goto error; } } } /* Fill the handle of the Device Context */ command->open_client_session.device_context = connection->device_context; error = tf_send_receive( &connection->dev->sm, command, answer, connection, true); error: /* Unmap the temporary memory references */ for (i = 0; i < 4; i++) if (shmem_desc[i] != NULL) tf_unmap_shmem(connection, shmem_desc[i], 0); if (error != 0) dprintk(KERN_ERR "tf_open_client_session returns %d\n", error); else dprintk(KERN_ERR "tf_open_client_session returns " "error_code 0x%08X\n", answer->open_client_session.error_code); return error; }
/* * Invokes a client command to the Secure World */ int tf_invoke_client_command( struct tf_connection *connection, union tf_command *command, union tf_answer *answer) { int error = 0; struct tf_shmem_desc *shmem_desc[4] = {NULL}; int i; #ifdef CONFIG_TF_ION struct ion_handle *new_handle = NULL; #endif /* CONFIG_TF_ION */ dprintk(KERN_INFO "tf_invoke_client_command(%p)\n", connection); command->release_shared_memory.message_size = (sizeof(struct tf_command_invoke_client_command) - sizeof(struct tf_command_header)) / 4; #ifdef CONFIG_TF_ZEBRA error = tf_crypto_try_shortcuted_update(connection, (struct tf_command_invoke_client_command *) command, (struct tf_answer_invoke_client_command *) answer); if (error == 0) return error; #endif /* Map the tmprefs */ for (i = 0; i < 4; i++) { int param_type = TF_GET_PARAM_TYPE( command->invoke_client_command.param_types, i); if ((param_type & (TF_PARAM_TYPE_MEMREF_FLAG | TF_PARAM_TYPE_REGISTERED_MEMREF_FLAG)) == TF_PARAM_TYPE_MEMREF_FLAG) { /* A temporary memref: map it */ error = tf_map_temp_shmem(connection, &command->invoke_client_command. params[i].temp_memref, param_type, &shmem_desc[i]); if (error != 0) { dprintk(KERN_ERR "tf_invoke_client_command: " "unable to map temporary memory " "block\n (%08X)", error); goto error; } } #ifdef CONFIG_TF_ION else if (param_type == TF_PARAM_TYPE_MEMREF_ION_HANDLE) { struct tf_command_invoke_client_command *invoke; ion_phys_addr_t ion_addr; size_t ion_len; struct ion_buffer *buffer; if (connection->ion_client == NULL) { connection->ion_client = ion_client_create( zebra_ion_device, (1 << ION_HEAP_TYPE_CARVEOUT), "tf"); } if (connection->ion_client == NULL) { dprintk(KERN_ERR "%s(%p): " "unable to create ion client\n", __func__, connection); error = -EFAULT; goto error; } invoke = &command->invoke_client_command; dprintk(KERN_INFO "ion_handle %x", invoke->params[i].value.a); buffer = ion_share(connection->ion_client, (struct ion_handle *)invoke->params[i].value.a); if (buffer == NULL) { dprintk(KERN_ERR "%s(%p): " "unable to share ion handle\n", __func__, connection); error = -EFAULT; goto error; } dprintk(KERN_INFO "ion_buffer %p", buffer); new_handle = ion_import(connection->ion_client, buffer); if (new_handle == NULL) { dprintk(KERN_ERR "%s(%p): " "unable to import ion buffer\n", __func__, connection); error = -EFAULT; goto error; } dprintk(KERN_INFO "new_handle %x", new_handle); error = ion_phys(connection->ion_client, new_handle, &ion_addr, &ion_len); if (error) { dprintk(KERN_ERR "%s: unable to convert ion handle " "0x%08X (error code 0x%08X)\n", __func__, new_handle, error); error = -EINVAL; goto error; } dprintk(KERN_INFO "%s: handle=0x%08x phys_add=0x%08x length=0x%08x\n", __func__, invoke->params[i].value.a, ion_addr, ion_len); invoke->params[i].value.a = (u32) ion_addr; invoke->params[i].value.b = (u32) ion_len; invoke->param_types &= ~((0xF) << (4*i)); invoke->param_types |= TF_PARAM_TYPE_VALUE_INPUT << (4*i); } #endif /* CONFIG_TF_ION */ } command->invoke_client_command.device_context = connection->device_context; error = tf_send_receive(&connection->dev->sm, command, answer, connection, true); error: #ifdef CONFIG_TF_ION if (new_handle != NULL) ion_free(connection->ion_client, new_handle); #endif /* CONFIG_TF_ION */ /* Unmap de temp mem refs */ for (i = 0; i < 4; i++) { if (shmem_desc[i] != NULL) { dprintk(KERN_INFO "tf_invoke_client_command: " "UnMatemp_memref %d\n ", i); tf_unmap_shmem(connection, shmem_desc[i], 0); } } if (error != 0) dprintk(KERN_ERR "tf_invoke_client_command returns %d\n", error); else dprintk(KERN_ERR "tf_invoke_client_command returns " "error_code 0x%08X\n", answer->invoke_client_command.error_code); return error; }
/** * Find the first available slot for a new block of shared memory * and map the user buffer. * Update the descriptors to L1 descriptors * Update the buffer_start_offset and buffer_size fields * shmem_desc is updated to the mapped shared memory descriptor **/ int tf_map_shmem( struct tf_connection *connection, u32 buffer, /* flags for read-write access rights on the memory */ u32 flags, bool in_user_space, u32 descriptors[TF_MAX_COARSE_PAGES], u32 *buffer_start_offset, u32 buffer_size, struct tf_shmem_desc **shmem_desc, u32 *descriptor_count) { struct tf_shmem_desc *desc = NULL; int error; dprintk(KERN_INFO "tf_map_shmem(%p, %p, flags = 0x%08x)\n", connection, (void *) buffer, flags); mutex_lock(&(connection->shmem_mutex)); /* * Check the list of free shared memory * is not empty */ if (list_empty(&(connection->free_shmem_list))) { if (atomic_read(&(connection->shmem_count)) == TF_SHMEM_MAX_COUNT) { printk(KERN_ERR "tf_map_shmem(%p):" " maximum shared memories already registered\n", connection); error = -ENOMEM; goto error; } /* no descriptor available, allocate a new one */ desc = (struct tf_shmem_desc *) internal_kmalloc( sizeof(*desc), GFP_KERNEL); if (desc == NULL) { printk(KERN_ERR "tf_map_shmem(%p):" " failed to allocate descriptor\n", connection); error = -ENOMEM; goto error; } /* Initialize the structure */ desc->type = TF_SHMEM_TYPE_REGISTERED_SHMEM; atomic_set(&desc->ref_count, 1); INIT_LIST_HEAD(&(desc->list)); atomic_inc(&(connection->shmem_count)); } else { /* take the first free shared memory descriptor */ desc = list_first_entry(&(connection->free_shmem_list), struct tf_shmem_desc, list); list_del(&(desc->list)); } /* Add the descriptor to the used list */ list_add(&(desc->list), &(connection->used_shmem_list)); error = tf_fill_descriptor_table( &(connection->cpt_alloc_context), desc, buffer, connection->vmas, descriptors, buffer_size, buffer_start_offset, in_user_space, flags, descriptor_count); if (error != 0) { dprintk(KERN_ERR "tf_map_shmem(%p):" " tf_fill_descriptor_table failed with error " "code %d!\n", connection, error); goto error; } desc->client_buffer = (u8 *) buffer; /* * Successful completion. */ *shmem_desc = desc; mutex_unlock(&(connection->shmem_mutex)); dprintk(KERN_DEBUG "tf_map_shmem: success\n"); return 0; /* * Error handling. */ error: mutex_unlock(&(connection->shmem_mutex)); dprintk(KERN_ERR "tf_map_shmem: failure with error code %d\n", error); tf_unmap_shmem( connection, desc, 0); return error; }
/* * Registers a shared memory to the Secure World */ int tf_register_shared_memory( struct tf_connection *connection, union tf_command *command, union tf_answer *answer) { int error = 0; struct tf_shmem_desc *shmem_desc = NULL; bool in_user_space = connection->owner != TF_CONNECTION_OWNER_KERNEL; struct tf_command_register_shared_memory *msg = &command->register_shared_memory; dprintk(KERN_INFO "tf_register_shared_memory(%p) " "%p[0x%08X][0x%08x]\n", connection, (void *)msg->shared_mem_descriptors[0], msg->shared_mem_size, (u32)msg->memory_flags); if (in_user_space) { error = tf_validate_shmem_and_flags( msg->shared_mem_descriptors[0], msg->shared_mem_size, (u32)msg->memory_flags); if (error != 0) goto error; } /* Initialize message_size with no descriptors */ msg->message_size = (offsetof(struct tf_command_register_shared_memory, shared_mem_descriptors) - sizeof(struct tf_command_header)) / 4; /* Map the shmem block and update the message */ if (msg->shared_mem_size == 0) { /* Empty shared mem */ msg->shared_mem_start_offset = msg->shared_mem_descriptors[0]; } else { u32 descriptor_count; error = tf_map_shmem( connection, msg->shared_mem_descriptors[0], msg->memory_flags, in_user_space, msg->shared_mem_descriptors, &(msg->shared_mem_start_offset), msg->shared_mem_size, &shmem_desc, &descriptor_count); if (error != 0) { dprintk(KERN_ERR "tf_register_shared_memory: " "unable to map shared memory block\n"); goto error; } msg->message_size += descriptor_count; } /* * write the correct device context handle and the address of the shared * memory descriptor in the message */ msg->device_context = connection->device_context; msg->block_id = (u32)shmem_desc; /* Send the updated message */ error = tf_send_receive( &connection->dev->sm, command, answer, connection, true); if ((error != 0) || (answer->register_shared_memory.error_code != S_SUCCESS)) { dprintk(KERN_ERR "tf_register_shared_memory: " "operation failed. Unmap block\n"); goto error; } /* Saves the block handle returned by the secure world */ if (shmem_desc != NULL) shmem_desc->block_identifier = answer->register_shared_memory.block; /* successful completion */ dprintk(KERN_INFO "tf_register_shared_memory(%p):" " block_id=0x%08x block=0x%08x\n", connection, msg->block_id, answer->register_shared_memory.block); return 0; /* error completion */ error: tf_unmap_shmem( connection, shmem_desc, 0); if (error != 0) dprintk(KERN_ERR "tf_register_shared_memory returns %d\n", error); else dprintk(KERN_ERR "tf_register_shared_memory returns " "error_code 0x%08X\n", answer->register_shared_memory.error_code); return error; }