/* * decode_audio_data_block() */ static void decode_audio_data_block(unsigned char *e, int n, edid_info_t *edid) { int ne = n / SAD_SIZE; void *sad_offset; otm_hdmi_audio_cap_t *adb = (otm_hdmi_audio_cap_t *) &edid->audio_caps; VERIFY_QUICK(ne > 0, exit); LOG_PRINT(LOG_LEVEL_DETAIL, "[audio data block... %d entries]\n", ne); /* Do we have enough space in SAD table? */ if (edid->short_audio_descriptor_count + ne > MAX_CAPS) { LOG_PRINT(LOG_LEVEL_ERROR, "Too many SADs in EDID. Not adding %d SADs\n", ne); return; } sad_offset = edid->short_audio_descriptor_data + edid->short_audio_descriptor_count * SAD_SIZE; memcpy(sad_offset, e, n); edid->short_audio_descriptor_count += ne; while (ne-- > 0) { /* Do we have room for another capability? */ if (edid->num_caps < MAX_CAPS) { adb[edid->num_caps].format = (*e & 0x78) >> 3; adb[edid->num_caps].max_channels = (*e & 0x07) + 1; adb[edid->num_caps].fs = *(e + 1) & 0x7F; adb[edid->num_caps].ss_bitrate = *(e + 2); print_audio_capability(&adb[edid->num_caps]); edid->num_caps++; } /* Go to the next entry of the block */ e += SAD_SIZE; }
//----------------------------------------------------------------------------- // get_copy_sg_list_from_smd_contig_memory // // Utility method for calling the respective methods for forming the // scatter-gather list. //----------------------------------------------------------------------------- static sec_result_t get_sg_list(sec_multipart_buff_list_t * multipart_list, bool aes_part, bool read_write, uint32_t nParts, user_buf_t **user_buf_list, sfaf_mem_ptr_t **sg_list, unsigned int *sg_count ) { sec_result_t rc = SEC_FAIL; sec_buffer_type_t buffer_type; buffer_type = (read_write == USER_BUF_RO) ? multipart_list->src_buffer_type : multipart_list->dst_buffer_type; if (aes_part) { if (buffer_type == SEC_BUFFER_NONCONTIG) { rc = get_aes_sg_list_from_non_contig_memory(multipart_list, read_write, nParts, user_buf_list, sg_list, sg_count); } else { rc = get_aes_sg_list_from_smd_contig_memory(multipart_list, read_write, nParts, sg_list, sg_count); } } else { if (buffer_type == SEC_BUFFER_NONCONTIG) { rc = get_copy_sg_list_from_non_contig_memory(multipart_list, read_write, nParts, user_buf_list, sg_list, sg_count); } else { rc = get_copy_sg_list_from_smd_contig_memory(multipart_list, read_write, nParts, sg_list, sg_count); } } VERIFY_QUICK( rc == SEC_SUCCESS, exit); //Handle TDP mode by flushing the SG list buffers if(g_fast_path) { if ((*sg_list) && (*sg_count)) { cache_flush_buffer(*sg_list, (*sg_count) * sizeof(sfaf_mem_ptr_t)); } } exit: return rc; }
//----------------------------------------------------------------------------- // get_copy_sg_list_from_non_contig_memory // // Utility method for forming the scatter-gather list for multipart copy // buffers which are non-contiguous in DRAM. //----------------------------------------------------------------------------- static sec_result_t get_copy_sg_list_from_non_contig_memory(sec_multipart_buff_list_t *buff_list, int read_write, int copy_parts, user_buf_t **user_buf_list, sfaf_mem_ptr_t **sg_list, unsigned int *sg_count ) { sec_result_t rc = SEC_SUCCESS; user_buf_t * user_buff = NULL; int i = 0; //index for a part in multipart list int j = 0; //index for a user_buf_t in user_buf_list sec_address_t vir_addr; *sg_count = 0; *sg_list = NULL; if (!copy_parts) goto exit; *user_buf_list = (user_buf_t*)OS_ALLOC (sizeof(user_buf_t) * copy_parts); VERIFY( *user_buf_list != NULL, exit, rc, SEC_OUT_OF_MEMORY); memset(*user_buf_list, 0, (sizeof(user_buf_t) * copy_parts)); for(i =0; i < buff_list->nParts; i++) { if ( buff_list->part[i].copyonly == 1) //deals with ONLY copy parts { user_buff = ((user_buf_t*)(*user_buf_list) + j); j++; vir_addr = (sec_address_t)(read_write == USER_BUF_RO ? buff_list->part[i].src_buffer : buff_list->part[i].dst_buffer); rc = form_sfaf_sg_list( user_buff, vir_addr, buff_list->part[i].size_bytes, read_write, sg_list, sg_count ); VERIFY_QUICK( rc == SEC_SUCCESS, exit); } } exit: return rc; }
//----------------------------------------------------------------------------- // form_sfaf_sg_list // // This method forms the scatter-gather list understood by SEC firmware. // This is the sequence of steps: // 1. Lock physical pages corresponding to virtual memory by using // get_user_pages Linux Kernel API. // 2. Alloc memory for a scatter-gather element // 3. Set physical start address, length and various parameters in the // scatter-gather element. // 4. get_user_pages returns a reference to every page found in // vir_buffer - vir+buffer+vir_buffer_size range. Whereas, SEC needs // a SG list with elements which are contiguous, irrespective of page // boundaries. Therefore, we need additional manipulation for this. // 5. Loop 2(by expanding memory for SG list) & 3 until all the physical // pages are covered. //----------------------------------------------------------------------------- static sec_result_t form_sfaf_sg_list(user_buf_t * user_buffer, sec_address_t vir_buffer, unsigned long vir_buffer_size, int write, sfaf_mem_ptr_t **sg_list, unsigned int *sg_count ) { sec_result_t rc = SEC_SUCCESS; int xfer_size = 0; int num_pages_processed = 0; //to keep track of pages returned by get_user_pages API bool need_another_sg_entry = true; //to keep track of elements in SG list provided to SEC sfaf_mem_ptr_t * new_sg_list = NULL; int new_sg_count = 0; unsigned long* sg_args = NULL; VERIFY( sg_list != NULL, exit, rc, SEC_NULL_POINTER); VERIFY( sg_count != NULL, exit, rc, SEC_NULL_POINTER); //Step 1 : get_user_pages API call rc = sec_kernel_user_buf_lock( user_buffer, (sec_address_t) vir_buffer, vir_buffer_size, write); VERIFY_QUICK(rc == SEC_SUCCESS, exit); //We traverse the 'user_buffer' list returned by get_user_pages kernel API, while (need_another_sg_entry) { //Step 2: realloc memory for a additional sfaf_const_mem_ptr data structure and //update it's variables new_sg_count = (*sg_count) + 1; new_sg_list = (sfaf_mem_ptr_t*) OS_ALLOC(sizeof(sfaf_mem_ptr_t) * (new_sg_count)); VERIFY( new_sg_list != NULL, exit, rc, SEC_OUT_OF_MEMORY); if (*sg_list) { memcpy(new_sg_list, *sg_list, sizeof(sfaf_mem_ptr_t) * (new_sg_count -1)); OS_FREE(*sg_list); } //Update the method output pointers *sg_count = new_sg_count; *sg_list = new_sg_list; //Step 3: Set SG elements xfer_size = PWU_MIN(user_buffer->page_bytes, user_buffer->size); new_sg_list[new_sg_count - 1].address = (void*)(user_buffer->page_addr + user_buffer->offset); new_sg_list[new_sg_count - 1].length = xfer_size; new_sg_list[new_sg_count - 1].external = 1; new_sg_list[new_sg_count - 1].swap = 1; new_sg_list[new_sg_count - 1].pmr_type = 0; new_sg_list[new_sg_count - 1].rsvd = 0; //swap the endianness for SEC sg_args = (unsigned long *)&(new_sg_list[new_sg_count - 1].external); sg_args[0] = bswap(sg_args[0]); need_another_sg_entry = false; num_pages_processed++; if (num_pages_processed == user_buffer->num_pages) { //we are done processing the buffer...get out now goto exit; } //Step 4: get_user_pages returns a reference to every page found in //vir_buffer - vir+buffer+vir_buffer_size range. Whereas, SEC needs //a SG list with elements which are contiguous, irrespective of page //boundaries. Therefore, we need additional manipulation for this. rc = user_buf_advance( user_buffer, xfer_size); VERIFY_QUICK(rc == SEC_SUCCESS , exit); xfer_size = PWU_MIN(user_buffer->page_bytes, user_buffer->size); while ((num_pages_processed < user_buffer->num_pages) && (!need_another_sg_entry)) { //navigate through for every page in user_buf_t if ((void*)(user_buffer->page_addr + user_buffer->offset) != (new_sg_list[new_sg_count - 1].address + new_sg_list[new_sg_count - 1].length)) { //until you finish the list or the page's start address is not next to previous page's end address //generate another sfaf_const_mem_ptr data structure and follow the process need_another_sg_entry = true; } else { new_sg_list[new_sg_count - 1].length += xfer_size; num_pages_processed++; if (num_pages_processed == user_buffer->num_pages) { //we are done processing the buffer...get out now goto exit; } rc = user_buf_advance( user_buffer, xfer_size); VERIFY_QUICK(rc == SEC_SUCCESS , exit); xfer_size = PWU_MIN(user_buffer->page_bytes, user_buffer->size); } } } exit: return rc; }
sec_result_t pr2_multipart_op(sec_kernel_ipc_t * ipc_arg, ipl_t * ipl, opl_t * opl, ipc_shmem_t * ish_pl ) { sec_ipc_return_t ipc_ret = IPC_RET_COMMAND_COMPLETE; sec_result_t rc = SEC_SUCCESS; sec_address_t buf_addr; uint32_t buf_size = 0; uint32_t sg_count = 0; user_buf_t *user_buf_list = NULL; sfaf_mem_ptr_t *sg_list = NULL; buf_addr = (sec_address_t) ipc_arg->src; buf_size = ipc_arg->src_size; if(buf_addr && buf_size) { /* Enter here only if we have to do scatter gather */ user_buf_list = (user_buf_t*)OS_ALLOC (sizeof(user_buf_t)); VERIFY( user_buf_list != NULL, exit, rc, SEC_OUT_OF_MEMORY); memset(user_buf_list, 0, (sizeof(user_buf_t))); rc = form_sfaf_sg_list( user_buf_list, buf_addr, buf_size, USER_BUF_RO, &sg_list, &sg_count ); VERIFY_QUICK( rc == SEC_SUCCESS, exit); #if 0 { int i; sfaf_mem_ptr_t *temp = sg_list; for(i=0; i<sg_count; i++) { pv(temp[i].address, temp[i].length); } } #endif // Put the scatter gather list in the ipl if(ipc_arg->sub_cmd.sc_pr2 == IPC_SC_PR2_CALCULATE_OMAC) { ipl->pr2_sg_op.pr2_calc_omac.pr2_sg_data_start = (uint32_t) OS_VIRT_TO_PHYS(sg_list); ipl->pr2_sg_op.pr2_calc_omac.pr2_sg_data_count = sg_count; } else if(ipc_arg->sub_cmd.sc_pr2 == IPC_SC_PR2_HASH_VALUE) { ipl->pr2_sg_op.pr2_hash_value.pr2_sg_data_start = (uint32_t) OS_VIRT_TO_PHYS(sg_list); ipl->pr2_sg_op.pr2_hash_value.pr2_sg_data_count = sg_count; } } ipc_ret = sec_kernel_ipc(ipc_arg->cmd, ipc_arg->sub_cmd, ipc_arg->io_sizes, ipl, opl, ish_pl, NULL); rc = ipc2sec(ipc_ret); exit: free_user_buffer_list( user_buf_list, 1); OS_FREE(sg_list); return rc; }
//----------------------------------------------------------------------------- // aes_multipart_op // // This method forms the payload for Multipart AES CTR decryption IPC. // There are 4 scatter-gather lists provided to SEC firmware for processing: // 1. aes_src_sg_list -- List of physical buffers which contain AES-CTR // encrypted media content. // 2. aes_dst_sg_list -- List of physical PMR buffers where SEC will put // AES-CTR decrypted media content. // 3. copy_src_sg_list -- List of physical buffers which contain plaintext // media headers. // 4. copy_dst_sg_list -- List of physical buffers where SEC will copy // plaintext media headers. //----------------------------------------------------------------------------- sec_result_t aes_multipart_op(sec_kernel_ipc_t * ipc_arg, ipl_t * ipl, opl_t * opl, ipc_shmem_t * ish_pl ) { sec_result_t rc = SEC_INVALID_INPUT; sec_ipc_return_t ipc_ret = IPC_RET_COMMAND_COMPLETE; sfaf_mem_ptr_t * aes_src_sg_list = NULL; unsigned int aes_src_sg_list_count = 0; user_buf_t * aes_src_user_buf_list = NULL; sfaf_mem_ptr_t * aes_dst_sg_list = NULL; unsigned int aes_dst_sg_list_count = 0; user_buf_t * aes_dst_user_buf_list = NULL; sfaf_mem_ptr_t * copy_src_sg_list = NULL; unsigned int copy_src_sg_list_count = 0; user_buf_t * copy_src_user_buf_list = NULL; sfaf_mem_ptr_t * copy_dst_sg_list = NULL; unsigned int copy_dst_sg_list_count = 0; user_buf_t * copy_dst_user_buf_list = NULL; int encrypted_parts = 0; int i = 0; sec_multipart_buff_list_t * multipart_list = (sec_multipart_buff_list_t *) (phys_to_virt(ipl->aes_crypt_multipart.multipart_data.multipart_list)); if (!multipart_list) goto exit; for(i =0; i < multipart_list->nParts; i++) { if (multipart_list->part[i].copyonly == false) encrypted_parts++; } if (!encrypted_parts) goto exit; //Form AES content source scatter-gather list rc = get_sg_list( multipart_list, true, USER_BUF_RO, encrypted_parts, &aes_src_user_buf_list, &aes_src_sg_list, &aes_src_sg_list_count); VERIFY_QUICK( rc == SEC_SUCCESS, exit); ipl->aes_crypt_multipart.sg_data.aes_src_sg = (aes_src_sg_list_count != 0) ? (uint32_t)OS_VIRT_TO_PHYS(aes_src_sg_list) : 0; ipl->aes_crypt_multipart.sg_data.aes_src_sg_count = aes_src_sg_list_count; //Form AES content destination scatter-gather list rc = get_sg_list( multipart_list, true, USER_BUF_RW, encrypted_parts, &aes_dst_user_buf_list, &aes_dst_sg_list, &aes_dst_sg_list_count); VERIFY_QUICK( rc == SEC_SUCCESS, exit); ipl->aes_crypt_multipart.sg_data.aes_dst_sg = (aes_dst_sg_list_count != 0) ? (uint32_t)OS_VIRT_TO_PHYS(aes_dst_sg_list) : 0; ipl->aes_crypt_multipart.sg_data.aes_dst_sg_count = aes_dst_sg_list_count; //Form COPY content source scatter-gather list rc = get_sg_list( multipart_list, false, USER_BUF_RO, multipart_list->nParts - encrypted_parts, ©_src_user_buf_list, ©_src_sg_list, ©_src_sg_list_count); VERIFY_QUICK( rc == SEC_SUCCESS, exit); ipl->aes_crypt_multipart.sg_data.copy_src_sg = (copy_src_sg_list_count != 0) ? (uint32_t)OS_VIRT_TO_PHYS(copy_src_sg_list) : 0; ipl->aes_crypt_multipart.sg_data.copy_src_sg_count = copy_src_sg_list_count; //Form COPY content destination scatter-gather list rc = get_sg_list( multipart_list, false, USER_BUF_RW, multipart_list->nParts - encrypted_parts, ©_dst_user_buf_list, ©_dst_sg_list, ©_dst_sg_list_count); VERIFY_QUICK( rc == SEC_SUCCESS, exit); ipl->aes_crypt_multipart.sg_data.copy_dst_sg = (copy_dst_sg_list_count != 0) ? (uint32_t)OS_VIRT_TO_PHYS(copy_dst_sg_list) : 0; ipl->aes_crypt_multipart.sg_data.copy_dst_sg_count = copy_dst_sg_list_count; //provide the SG list to FW ipc_ret = sec_kernel_ipc( ipc_arg->cmd, ipc_arg->sub_cmd, ipc_arg->io_sizes, ipl, opl, ish_pl, NULL); if (ipc_ret == IPC_RET_COMMAND_COMPLETE) { sec_kernel_copy_to_user(ipc_arg, opl, NULL); } rc = ipc2sec(ipc_ret); exit: //Free up all the resources allocated above free_user_buffer_list( aes_src_user_buf_list, encrypted_parts); free_user_buffer_list( aes_dst_user_buf_list, encrypted_parts); free_user_buffer_list( copy_src_user_buf_list, multipart_list->nParts - encrypted_parts); free_user_buffer_list( copy_dst_user_buf_list, multipart_list->nParts - encrypted_parts); OS_FREE(aes_src_sg_list); OS_FREE(aes_dst_sg_list); OS_FREE(copy_src_sg_list); OS_FREE(copy_dst_sg_list); return rc; }