/* Encode the parameters and type of an operation from the TEE API format into an SChannel message. */ void TEEC_encode_parameters(u16 *param_types, union tf_command_param *params, TEEC_Operation *operation) { unsigned i; if (operation == NULL) { *param_types = 0; return; } *param_types = operation->paramTypes; for (i = 0; i < 4; i++) { unsigned ty = TF_GET_PARAM_TYPE(operation->paramTypes, i); TEEC_Parameter *op = operation->params + i; if (ty & TF_PARAM_TYPE_REGISTERED_MEMREF_FLAG) { TEEC_SharedMemory *sm = op->memref.parent; params[i].memref.block = sm->imp._block; if (ty == TEEC_MEMREF_WHOLE) { TEEC_encode_whole_memref_flags(param_types, i, sm->flags); params[i].memref.size = sm->size; params[i].memref.offset = 0; } else { params[i].memref.size = op->memref.size; params[i].memref.offset = op->memref.offset; } } else if (ty & TF_PARAM_TYPE_MEMREF_FLAG) { /* Set up what tf_map_temp_shmem (called by tf_open_client_session and tf_invoke_client_command) expects: .descriptor and .offset to both be set to the address of the buffer. */ u32 address = (u32)op->tmpref.buffer; params[i].temp_memref.descriptor = address; params[i].temp_memref.size = op->tmpref.size; params[i].temp_memref.offset = address; } else if (ty & TF_PARAM_TYPE_INPUT_FLAG) { params[i].value.a = op->value.a; params[i].value.b = op->value.b; } else { /* output-only value or none, so nothing to do */ } } }
/* Decode updated parameters from an SChannel answer into the TEE API format. */ void TEEC_decode_parameters(union tf_answer_param *params, TEEC_Operation *operation) { unsigned i; if (operation == NULL) { return; } for (i = 0; i < 4; i++) { unsigned ty = TF_GET_PARAM_TYPE(operation->paramTypes, i); TEEC_Parameter *op = operation->params + i; if (!(ty & TF_PARAM_TYPE_OUTPUT_FLAG)) { /* input-only or none, so nothing to do */ } else if (ty & TF_PARAM_TYPE_REGISTERED_MEMREF_FLAG) { op->memref.size = params[i].size.size; } else if (ty & TF_PARAM_TYPE_MEMREF_FLAG) { op->tmpref.size = params[i].size.size; } else { op->value.a = params[i].value.a; op->value.b = params[i].value.b; } } }
/* * 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; }