| /** |
| * Copyright (c) 2011 Trusted Logic S.A. |
| * All Rights Reserved. |
| * |
| * This program is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU General Public License |
| * version 2 as published by the Free Software Foundation. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 59 Temple Place, Suite 330, Boston, |
| * MA 02111-1307 USA |
| */ |
| |
| #include <asm/div64.h> |
| #include <asm/system.h> |
| #include <linux/version.h> |
| #include <asm/cputype.h> |
| #include <linux/interrupt.h> |
| #include <linux/page-flags.h> |
| #include <linux/pagemap.h> |
| #include <linux/vmalloc.h> |
| #include <linux/jiffies.h> |
| #include <linux/freezer.h> |
| |
| #include "tf_defs.h" |
| #include "tf_comm.h" |
| #include "tf_protocol.h" |
| #include "tf_util.h" |
| #include "tf_conn.h" |
| |
| #ifdef CONFIG_TF_ZEBRA |
| #include "tf_zebra.h" |
| #endif |
| |
| /*--------------------------------------------------------------------------- |
| * Internal Constants |
| *---------------------------------------------------------------------------*/ |
| |
| /* |
| * shared memories descriptor constants |
| */ |
| #define DESCRIPTOR_B_MASK (1 << 2) |
| #define DESCRIPTOR_C_MASK (1 << 3) |
| #define DESCRIPTOR_S_MASK (1 << 10) |
| |
| #define L1_COARSE_DESCRIPTOR_BASE (0x00000001) |
| #define L1_COARSE_DESCRIPTOR_ADDR_MASK (0xFFFFFC00) |
| #define L1_COARSE_DESCRIPTOR_V13_12_SHIFT (5) |
| |
| #define L2_PAGE_DESCRIPTOR_BASE (0x00000003) |
| #define L2_PAGE_DESCRIPTOR_AP_APX_READ (0x220) |
| #define L2_PAGE_DESCRIPTOR_AP_APX_READ_WRITE (0x30) |
| |
| #define L2_INIT_DESCRIPTOR_BASE (0x00000003) |
| #define L2_INIT_DESCRIPTOR_V13_12_SHIFT (4) |
| |
| /* |
| * Reject an attempt to share a strongly-Ordered or Device memory |
| * Strongly-Ordered: TEX=0b000, C=0, B=0 |
| * Shared Device: TEX=0b000, C=0, B=1 |
| * Non-Shared Device: TEX=0b010, C=0, B=0 |
| */ |
| #define L2_TEX_C_B_MASK \ |
| ((1<<8) | (1<<7) | (1<<6) | (1<<3) | (1<<2)) |
| #define L2_TEX_C_B_STRONGLY_ORDERED \ |
| ((0<<8) | (0<<7) | (0<<6) | (0<<3) | (0<<2)) |
| #define L2_TEX_C_B_SHARED_DEVICE \ |
| ((0<<8) | (0<<7) | (0<<6) | (0<<3) | (1<<2)) |
| #define L2_TEX_C_B_NON_SHARED_DEVICE \ |
| ((0<<8) | (1<<7) | (0<<6) | (0<<3) | (0<<2)) |
| |
| #define CACHE_S(x) ((x) & (1 << 24)) |
| #define CACHE_DSIZE(x) (((x) >> 12) & 4095) |
| |
| #define TIME_IMMEDIATE ((u64) 0x0000000000000000ULL) |
| #define TIME_INFINITE ((u64) 0xFFFFFFFFFFFFFFFFULL) |
| |
| /*--------------------------------------------------------------------------- |
| * atomic operation definitions |
| *---------------------------------------------------------------------------*/ |
| |
| /* |
| * Atomically updates the sync_serial_n and time_n register |
| * sync_serial_n and time_n modifications are thread safe |
| */ |
| void tf_set_current_time(struct tf_comm *comm) |
| { |
| u32 new_sync_serial; |
| struct timeval now; |
| u64 time64; |
| |
| /* |
| * lock the structure while updating the L1 shared memory fields |
| */ |
| spin_lock(&comm->lock); |
| |
| /* read sync_serial_n and change the TimeSlot bit field */ |
| new_sync_serial = |
| tf_read_reg32(&comm->pBuffer->sync_serial_n) + 1; |
| |
| do_gettimeofday(&now); |
| time64 = now.tv_sec; |
| time64 = (time64 * 1000) + (now.tv_usec / 1000); |
| |
| /* Write the new time64 and nSyncSerial into shared memory */ |
| tf_write_reg64(&comm->pBuffer->time_n[new_sync_serial & |
| TF_SYNC_SERIAL_TIMESLOT_N], time64); |
| tf_write_reg32(&comm->pBuffer->sync_serial_n, |
| new_sync_serial); |
| |
| spin_unlock(&comm->lock); |
| } |
| |
| /* |
| * Performs the specific read timeout operation |
| * The difficulty here is to read atomically 2 u32 |
| * values from the L1 shared buffer. |
| * This is guaranteed by reading before and after the operation |
| * the timeslot given by the Secure World |
| */ |
| static inline void tf_read_timeout(struct tf_comm *comm, u64 *time) |
| { |
| u32 sync_serial_s_initial = 0; |
| u32 sync_serial_s_final = 1; |
| u64 time64; |
| |
| spin_lock(&comm->lock); |
| |
| while (sync_serial_s_initial != sync_serial_s_final) { |
| sync_serial_s_initial = tf_read_reg32( |
| &comm->pBuffer->sync_serial_s); |
| time64 = tf_read_reg64( |
| &comm->pBuffer->timeout_s[sync_serial_s_initial&1]); |
| |
| sync_serial_s_final = tf_read_reg32( |
| &comm->pBuffer->sync_serial_s); |
| } |
| |
| spin_unlock(&comm->lock); |
| |
| *time = time64; |
| } |
| |
| /*---------------------------------------------------------------------------- |
| * SIGKILL signal handling |
| *----------------------------------------------------------------------------*/ |
| |
| static bool sigkill_pending(void) |
| { |
| if (signal_pending(current)) { |
| dprintk(KERN_INFO "A signal is pending\n"); |
| if (sigismember(¤t->pending.signal, SIGKILL)) { |
| dprintk(KERN_INFO "A SIGKILL is pending\n"); |
| return true; |
| } else if (sigismember( |
| ¤t->signal->shared_pending.signal, SIGKILL)) { |
| dprintk(KERN_INFO "A SIGKILL is pending (shared)\n"); |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| /*---------------------------------------------------------------------------- |
| * Shared memory related operations |
| *----------------------------------------------------------------------------*/ |
| |
| struct tf_coarse_page_table *tf_alloc_coarse_page_table( |
| struct tf_coarse_page_table_allocation_context *alloc_context, |
| u32 type) |
| { |
| struct tf_coarse_page_table *coarse_pg_table = NULL; |
| |
| spin_lock(&(alloc_context->lock)); |
| |
| if (!(list_empty(&(alloc_context->free_coarse_page_tables)))) { |
| /* |
| * The free list can provide us a coarse page table |
| * descriptor |
| */ |
| coarse_pg_table = list_first_entry( |
| &alloc_context->free_coarse_page_tables, |
| struct tf_coarse_page_table, list); |
| list_del(&(coarse_pg_table->list)); |
| |
| coarse_pg_table->parent->ref_count++; |
| } else { |
| /* no array of coarse page tables, create a new one */ |
| struct tf_coarse_page_table_array *array; |
| void *page; |
| int i; |
| |
| spin_unlock(&(alloc_context->lock)); |
| |
| /* first allocate a new page descriptor */ |
| array = internal_kmalloc(sizeof(*array), GFP_KERNEL); |
| if (array == NULL) { |
| dprintk(KERN_ERR "tf_alloc_coarse_page_table(%p):" |
| " failed to allocate a table array\n", |
| alloc_context); |
| return NULL; |
| } |
| |
| array->type = type; |
| INIT_LIST_HEAD(&(array->list)); |
| |
| /* now allocate the actual page the page descriptor describes */ |
| page = (void *) internal_get_zeroed_page(GFP_KERNEL); |
| if (page == NULL) { |
| dprintk(KERN_ERR "tf_alloc_coarse_page_table(%p):" |
| " failed allocate a page\n", |
| alloc_context); |
| internal_kfree(array); |
| return NULL; |
| } |
| |
| spin_lock(&(alloc_context->lock)); |
| |
| /* initialize the coarse page table descriptors */ |
| for (i = 0; i < 4; i++) { |
| INIT_LIST_HEAD(&(array->coarse_page_tables[i].list)); |
| array->coarse_page_tables[i].descriptors = |
| page + (i * SIZE_1KB); |
| array->coarse_page_tables[i].parent = array; |
| |
| if (i == 0) { |
| /* |
| * the first element is kept for the current |
| * coarse page table allocation |
| */ |
| coarse_pg_table = |
| &(array->coarse_page_tables[i]); |
| array->ref_count++; |
| } else { |
| /* |
| * The other elements are added to the free list |
| */ |
| list_add(&(array->coarse_page_tables[i].list), |
| &(alloc_context-> |
| free_coarse_page_tables)); |
| } |
| } |
| |
| list_add(&(array->list), |
| &(alloc_context->coarse_page_table_arrays)); |
| } |
| spin_unlock(&(alloc_context->lock)); |
| |
| return coarse_pg_table; |
| } |
| |
| |
| void tf_free_coarse_page_table( |
| struct tf_coarse_page_table_allocation_context *alloc_context, |
| struct tf_coarse_page_table *coarse_pg_table, |
| int force) |
| { |
| struct tf_coarse_page_table_array *array; |
| |
| spin_lock(&(alloc_context->lock)); |
| |
| array = coarse_pg_table->parent; |
| |
| (array->ref_count)--; |
| |
| if (array->ref_count == 0) { |
| /* |
| * no coarse page table descriptor is used |
| * check if we should free the whole page |
| */ |
| |
| if ((array->type == TF_PAGE_DESCRIPTOR_TYPE_PREALLOCATED) |
| && (force == 0)) |
| /* |
| * This is a preallocated page, |
| * add the page back to the free list |
| */ |
| list_add(&(coarse_pg_table->list), |
| &(alloc_context->free_coarse_page_tables)); |
| else { |
| /* |
| * None of the page's coarse page table descriptors |
| * are in use, free the whole page |
| */ |
| int i; |
| u32 *descriptors; |
| |
| /* |
| * remove the page's associated coarse page table |
| * descriptors from the free list |
| */ |
| for (i = 0; i < 4; i++) |
| if (&(array->coarse_page_tables[i]) != |
| coarse_pg_table) |
| list_del(&(array-> |
| coarse_page_tables[i].list)); |
| |
| descriptors = |
| array->coarse_page_tables[0].descriptors; |
| array->coarse_page_tables[0].descriptors = NULL; |
| |
| /* remove the coarse page table from the array */ |
| list_del(&(array->list)); |
| |
| spin_unlock(&(alloc_context->lock)); |
| /* |
| * Free the page. |
| * The address of the page is contained in the first |
| * element |
| */ |
| internal_free_page((unsigned long) descriptors); |
| /* finaly free the array */ |
| internal_kfree(array); |
| |
| spin_lock(&(alloc_context->lock)); |
| } |
| } else { |
| /* |
| * Some coarse page table descriptors are in use. |
| * Add the descriptor to the free list |
| */ |
| list_add(&(coarse_pg_table->list), |
| &(alloc_context->free_coarse_page_tables)); |
| } |
| |
| spin_unlock(&(alloc_context->lock)); |
| } |
| |
| |
| void tf_init_coarse_page_table_allocator( |
| struct tf_coarse_page_table_allocation_context *alloc_context) |
| { |
| spin_lock_init(&(alloc_context->lock)); |
| INIT_LIST_HEAD(&(alloc_context->coarse_page_table_arrays)); |
| INIT_LIST_HEAD(&(alloc_context->free_coarse_page_tables)); |
| } |
| |
| void tf_release_coarse_page_table_allocator( |
| struct tf_coarse_page_table_allocation_context *alloc_context) |
| { |
| spin_lock(&(alloc_context->lock)); |
| |
| /* now clean up the list of page descriptors */ |
| while (!list_empty(&(alloc_context->coarse_page_table_arrays))) { |
| struct tf_coarse_page_table_array *page_desc; |
| u32 *descriptors; |
| |
| page_desc = list_first_entry( |
| &alloc_context->coarse_page_table_arrays, |
| struct tf_coarse_page_table_array, list); |
| |
| descriptors = page_desc->coarse_page_tables[0].descriptors; |
| list_del(&(page_desc->list)); |
| |
| spin_unlock(&(alloc_context->lock)); |
| |
| if (descriptors != NULL) |
| internal_free_page((unsigned long)descriptors); |
| |
| internal_kfree(page_desc); |
| |
| spin_lock(&(alloc_context->lock)); |
| } |
| |
| spin_unlock(&(alloc_context->lock)); |
| } |
| |
| /* |
| * Returns the L1 coarse page descriptor for |
| * a coarse page table located at address coarse_pg_table_descriptors |
| */ |
| u32 tf_get_l1_coarse_descriptor( |
| u32 coarse_pg_table_descriptors[256]) |
| { |
| u32 descriptor = L1_COARSE_DESCRIPTOR_BASE; |
| unsigned int info = read_cpuid(CPUID_CACHETYPE); |
| |
| descriptor |= (virt_to_phys((void *) coarse_pg_table_descriptors) |
| & L1_COARSE_DESCRIPTOR_ADDR_MASK); |
| |
| if (CACHE_S(info) && (CACHE_DSIZE(info) & (1 << 11))) { |
| dprintk(KERN_DEBUG "tf_get_l1_coarse_descriptor " |
| "V31-12 added to descriptor\n"); |
| /* the 16k alignment restriction applies */ |
| descriptor |= (DESCRIPTOR_V13_12_GET( |
| (u32)coarse_pg_table_descriptors) << |
| L1_COARSE_DESCRIPTOR_V13_12_SHIFT); |
| } |
| |
| return descriptor; |
| } |
| |
| |
| #define dprintk_desc(...) |
| /* |
| * Returns the L2 descriptor for the specified user page. |
| */ |
| u32 tf_get_l2_descriptor_common(u32 vaddr, struct mm_struct *mm) |
| { |
| pgd_t *pgd; |
| pud_t *pud; |
| pmd_t *pmd; |
| pte_t *ptep; |
| u32 *hwpte; |
| u32 tex = 0; |
| u32 descriptor = 0; |
| |
| dprintk_desc(KERN_INFO "VirtAddr = %x\n", vaddr); |
| pgd = pgd_offset(mm, vaddr); |
| dprintk_desc(KERN_INFO "pgd = %x, value=%x\n", (unsigned int) pgd, |
| (unsigned int) *pgd); |
| if (pgd_none(*pgd)) |
| goto error; |
| pud = pud_offset(pgd, vaddr); |
| dprintk_desc(KERN_INFO "pud = %x, value=%x\n", (unsigned int) pud, |
| (unsigned int) *pud); |
| if (pud_none(*pud)) |
| goto error; |
| pmd = pmd_offset(pud, vaddr); |
| dprintk_desc(KERN_INFO "pmd = %x, value=%x\n", (unsigned int) pmd, |
| (unsigned int) *pmd); |
| if (pmd_none(*pmd)) |
| goto error; |
| |
| if (PMD_TYPE_SECT&(*pmd)) { |
| /* We have a section */ |
| dprintk_desc(KERN_INFO "Section descr=%x\n", |
| (unsigned int)*pmd); |
| if ((*pmd) & PMD_SECT_BUFFERABLE) |
| descriptor |= DESCRIPTOR_B_MASK; |
| if ((*pmd) & PMD_SECT_CACHEABLE) |
| descriptor |= DESCRIPTOR_C_MASK; |
| if ((*pmd) & PMD_SECT_S) |
| descriptor |= DESCRIPTOR_S_MASK; |
| tex = ((*pmd) >> 12) & 7; |
| } else { |
| /* We have a table */ |
| ptep = pte_offset_map(pmd, vaddr); |
| if (pte_present(*ptep)) { |
| dprintk_desc(KERN_INFO "L2 descr=%x\n", |
| (unsigned int) *ptep); |
| if ((*ptep) & L_PTE_MT_BUFFERABLE) |
| descriptor |= DESCRIPTOR_B_MASK; |
| if ((*ptep) & L_PTE_MT_WRITETHROUGH) |
| descriptor |= DESCRIPTOR_C_MASK; |
| if ((*ptep) & L_PTE_MT_DEV_SHARED) |
| descriptor |= DESCRIPTOR_S_MASK; |
| |
| /* |
| * Linux's pte doesn't keep track of TEX value. |
| * Have to jump to hwpte see include/asm/pgtable.h |
| */ |
| hwpte = (u32 *) (((u32) ptep) - 0x800); |
| if (((*hwpte) & L2_DESCRIPTOR_ADDR_MASK) != |
| ((*ptep) & L2_DESCRIPTOR_ADDR_MASK)) |
| goto error; |
| dprintk_desc(KERN_INFO "hw descr=%x\n", *hwpte); |
| tex = ((*hwpte) >> 6) & 7; |
| pte_unmap(ptep); |
| } else { |
| pte_unmap(ptep); |
| goto error; |
| } |
| } |
| |
| descriptor |= (tex << 6); |
| |
| return descriptor; |
| |
| error: |
| dprintk(KERN_ERR "Error occured in %s\n", __func__); |
| return 0; |
| } |
| |
| |
| /* |
| * Changes an L2 page descriptor back to a pointer to a physical page |
| */ |
| inline struct page *tf_l2_page_descriptor_to_page(u32 l2_page_descriptor) |
| { |
| return pte_page(l2_page_descriptor & L2_DESCRIPTOR_ADDR_MASK); |
| } |
| |
| #define TF_DEFAULT_COMMON_DESCRIPTORS 0x0000044C |
| |
| /* |
| * Returns the L1 descriptor for the 1KB-aligned coarse page table. The address |
| * must be in the kernel address space. |
| */ |
| static void tf_get_l2_page_descriptor( |
| u32 *l2_page_descriptor, |
| u32 flags, struct mm_struct *mm, struct vm_area_struct *vmas) |
| { |
| u32 descriptor; |
| struct page *page; |
| |
| dprintk(KERN_INFO |
| "%s *l2_page_descriptor=%x vm_flags=%lx\n", |
| __func__, *l2_page_descriptor, vmas->vm_flags); |
| |
| if (*l2_page_descriptor == L2_DESCRIPTOR_FAULT) |
| return; |
| |
| if (vmas->vm_flags & VM_IO) { |
| *l2_page_descriptor = L2_DESCRIPTOR_FAULT; |
| dprintk(KERN_ERR "Memory mapped I/O or similar detected\n"); |
| return; |
| } |
| page = (struct page *) (*l2_page_descriptor); |
| |
| descriptor = TF_DEFAULT_COMMON_DESCRIPTORS; |
| descriptor |= L2_PAGE_DESCRIPTOR_BASE; |
| |
| descriptor |= (page_to_phys(page) & L2_DESCRIPTOR_ADDR_MASK); |
| |
| if (!(flags & TF_SHMEM_TYPE_WRITE)) |
| /* only read access */ |
| descriptor |= L2_PAGE_DESCRIPTOR_AP_APX_READ; |
| else |
| /* read and write access */ |
| descriptor |= L2_PAGE_DESCRIPTOR_AP_APX_READ_WRITE; |
| |
| |
| *l2_page_descriptor = descriptor; |
| } |
| |
| |
| /* |
| * Unlocks the physical memory pages |
| * and frees the coarse pages that need to |
| */ |
| void tf_cleanup_shared_memory( |
| struct tf_coarse_page_table_allocation_context *alloc_context, |
| struct tf_shmem_desc *shmem_desc, |
| u32 full_cleanup) |
| { |
| u32 coarse_page_index; |
| |
| dprintk(KERN_INFO "tf_cleanup_shared_memory(%p)\n", |
| shmem_desc); |
| |
| #ifdef DEBUG_COARSE_TABLES |
| printk(KERN_DEBUG "tf_cleanup_shared_memory " |
| "- number of coarse page tables=%d\n", |
| shmem_desc->coarse_pg_table_count); |
| |
| for (coarse_page_index = 0; |
| coarse_page_index < shmem_desc->coarse_pg_table_count; |
| coarse_page_index++) { |
| u32 j; |
| |
| printk(KERN_DEBUG " Descriptor=%p address=%p index=%d\n", |
| shmem_desc->coarse_pg_table[coarse_page_index], |
| shmem_desc->coarse_pg_table[coarse_page_index]-> |
| descriptors, |
| coarse_page_index); |
| if (shmem_desc->coarse_pg_table[coarse_page_index] != NULL) { |
| for (j = 0; |
| j < TF_DESCRIPTOR_TABLE_CAPACITY; |
| j += 8) { |
| int k; |
| printk(KERN_DEBUG " "); |
| for (k = j; k < j + 8; k++) |
| printk(KERN_DEBUG "%p ", |
| shmem_desc->coarse_pg_table[ |
| coarse_page_index]-> |
| descriptors); |
| printk(KERN_DEBUG "\n"); |
| } |
| } |
| } |
| printk(KERN_DEBUG "tf_cleanup_shared_memory() - done\n\n"); |
| #endif |
| |
| /* Parse the coarse page descriptors */ |
| for (coarse_page_index = 0; |
| coarse_page_index < shmem_desc->coarse_pg_table_count; |
| coarse_page_index++) { |
| u32 j; |
| u32 found = 0; |
| |
| /* parse the page descriptors of the coarse page */ |
| for (j = 0; j < TF_DESCRIPTOR_TABLE_CAPACITY; j++) { |
| u32 l2_page_descriptor = (u32) (shmem_desc-> |
| coarse_pg_table[coarse_page_index]-> |
| descriptors[j]); |
| |
| if (l2_page_descriptor != L2_DESCRIPTOR_FAULT) { |
| struct page *page = |
| tf_l2_page_descriptor_to_page( |
| l2_page_descriptor); |
| |
| if (!PageReserved(page)) |
| SetPageDirty(page); |
| internal_page_cache_release(page); |
| |
| found = 1; |
| } else if (found == 1) { |
| break; |
| } |
| } |
| |
| /* |
| * Only free the coarse pages of descriptors not preallocated |
| */ |
| if ((shmem_desc->type == TF_SHMEM_TYPE_REGISTERED_SHMEM) || |
| (full_cleanup != 0)) |
| tf_free_coarse_page_table(alloc_context, |
| shmem_desc->coarse_pg_table[coarse_page_index], |
| 0); |
| } |
| |
| shmem_desc->coarse_pg_table_count = 0; |
| dprintk(KERN_INFO "tf_cleanup_shared_memory(%p) done\n", |
| shmem_desc); |
| } |
| |
| /* |
| * Make sure the coarse pages are allocated. If not allocated, do it Locks down |
| * the physical memory pages |
| * Verifies the memory attributes depending on flags |
| */ |
| int tf_fill_descriptor_table( |
| struct tf_coarse_page_table_allocation_context *alloc_context, |
| struct tf_shmem_desc *shmem_desc, |
| u32 buffer, |
| struct vm_area_struct **vmas, |
| u32 descriptors[TF_MAX_COARSE_PAGES], |
| u32 buffer_size, |
| u32 *buffer_start_offset, |
| bool in_user_space, |
| u32 flags, |
| u32 *descriptor_count) |
| { |
| u32 coarse_page_index; |
| u32 coarse_page_count; |
| u32 page_count; |
| u32 page_shift = 0; |
| int error; |
| unsigned int info = read_cpuid(CPUID_CACHETYPE); |
| |
| dprintk(KERN_INFO "tf_fill_descriptor_table" |
| "(%p, buffer=0x%08X, size=0x%08X, user=%01x " |
| "flags = 0x%08x)\n", |
| shmem_desc, |
| buffer, |
| buffer_size, |
| in_user_space, |
| flags); |
| |
| /* |
| * Compute the number of pages |
| * Compute the number of coarse pages |
| * Compute the page offset |
| */ |
| page_count = ((buffer & ~PAGE_MASK) + |
| buffer_size + ~PAGE_MASK) >> PAGE_SHIFT; |
| |
| /* check whether the 16k alignment restriction applies */ |
| if (CACHE_S(info) && (CACHE_DSIZE(info) & (1 << 11))) |
| /* |
| * The 16k alignment restriction applies. |
| * Shift data to get them 16k aligned |
| */ |
| page_shift = DESCRIPTOR_V13_12_GET(buffer); |
| page_count += page_shift; |
| |
| |
| /* |
| * Check the number of pages fit in the coarse pages |
| */ |
| if (page_count > (TF_DESCRIPTOR_TABLE_CAPACITY * |
| TF_MAX_COARSE_PAGES)) { |
| dprintk(KERN_ERR "tf_fill_descriptor_table(%p): " |
| "%u pages required to map shared memory!\n", |
| shmem_desc, page_count); |
| error = -ENOMEM; |
| goto error; |
| } |
| |
| /* coarse page describe 256 pages */ |
| coarse_page_count = ((page_count + |
| TF_DESCRIPTOR_TABLE_CAPACITY_MASK) >> |
| TF_DESCRIPTOR_TABLE_CAPACITY_BIT_SHIFT); |
| |
| /* |
| * Compute the buffer offset |
| */ |
| *buffer_start_offset = (buffer & ~PAGE_MASK) | |
| (page_shift << PAGE_SHIFT); |
| |
| /* map each coarse page */ |
| for (coarse_page_index = 0; |
| coarse_page_index < coarse_page_count; |
| coarse_page_index++) { |
| u32 j; |
| struct tf_coarse_page_table *coarse_pg_table; |
| |
| /* compute a virtual address with appropriate offset */ |
| u32 buffer_offset_vaddr = buffer + |
| (coarse_page_index * TF_MAX_COARSE_PAGE_MAPPED_SIZE); |
| u32 pages_to_get; |
| |
| /* |
| * Compute the number of pages left for this coarse page. |
| * Decrement page_count each time |
| */ |
| pages_to_get = (page_count >> |
| TF_DESCRIPTOR_TABLE_CAPACITY_BIT_SHIFT) ? |
| TF_DESCRIPTOR_TABLE_CAPACITY : page_count; |
| page_count -= pages_to_get; |
| |
| /* |
| * Check if the coarse page has already been allocated |
| * If not, do it now |
| */ |
| if ((shmem_desc->type == TF_SHMEM_TYPE_REGISTERED_SHMEM) |
| || (shmem_desc->type == |
| TF_SHMEM_TYPE_PM_HIBERNATE)) { |
| coarse_pg_table = tf_alloc_coarse_page_table( |
| alloc_context, |
| TF_PAGE_DESCRIPTOR_TYPE_NORMAL); |
| |
| if (coarse_pg_table == NULL) { |
| dprintk(KERN_ERR |
| "tf_fill_descriptor_table(%p):" |
| " SCXLNXConnAllocateCoarsePageTable " |
| "failed for coarse page %d\n", |
| shmem_desc, coarse_page_index); |
| error = -ENOMEM; |
| goto error; |
| } |
| |
| shmem_desc->coarse_pg_table[coarse_page_index] = |
| coarse_pg_table; |
| } else { |
| coarse_pg_table = |
| shmem_desc->coarse_pg_table[coarse_page_index]; |
| } |
| |
| /* |
| * The page is not necessarily filled with zeroes. |
| * Set the fault descriptors ( each descriptor is 4 bytes long) |
| */ |
| memset(coarse_pg_table->descriptors, 0x00, |
| TF_DESCRIPTOR_TABLE_CAPACITY * sizeof(u32)); |
| |
| if (in_user_space) { |
| int pages; |
| |
| /* |
| * TRICK: use pCoarsePageDescriptor->descriptors to |
| * hold the (struct page*) items before getting their |
| * physical address |
| */ |
| down_read(&(current->mm->mmap_sem)); |
| pages = internal_get_user_pages( |
| current, |
| current->mm, |
| buffer_offset_vaddr, |
| /* |
| * page_shift is cleared after retrieving first |
| * coarse page |
| */ |
| (pages_to_get - page_shift), |
| (flags & TF_SHMEM_TYPE_WRITE) ? 1 : 0, |
| 0, |
| (struct page **) (coarse_pg_table->descriptors |
| + page_shift), |
| vmas); |
| up_read(&(current->mm->mmap_sem)); |
| |
| if ((pages <= 0) || |
| (pages != (pages_to_get - page_shift))) { |
| dprintk(KERN_ERR"tf_fill_descriptor_table:" |
| " get_user_pages got %d pages while " |
| "trying to get %d pages!\n", |
| pages, pages_to_get - page_shift); |
| error = -EFAULT; |
| goto error; |
| } |
| |
| for (j = page_shift; |
| j < page_shift + pages; |
| j++) { |
| /* Get the actual L2 descriptors */ |
| tf_get_l2_page_descriptor( |
| &coarse_pg_table->descriptors[j], |
| flags, |
| current->mm, |
| vmas[j]); |
| /* |
| * Reject Strongly-Ordered or Device Memory |
| */ |
| #define IS_STRONGLY_ORDERED_OR_DEVICE_MEM(x) \ |
| ((((x) & L2_TEX_C_B_MASK) == L2_TEX_C_B_STRONGLY_ORDERED) || \ |
| (((x) & L2_TEX_C_B_MASK) == L2_TEX_C_B_SHARED_DEVICE) || \ |
| (((x) & L2_TEX_C_B_MASK) == L2_TEX_C_B_NON_SHARED_DEVICE)) |
| |
| if (IS_STRONGLY_ORDERED_OR_DEVICE_MEM( |
| coarse_pg_table-> |
| descriptors[j])) { |
| dprintk(KERN_ERR |
| "tf_fill_descriptor_table:" |
| " descriptor 0x%08X use " |
| "strongly-ordered or device " |
| "memory. Rejecting!\n", |
| coarse_pg_table-> |
| descriptors[j]); |
| error = -EFAULT; |
| goto error; |
| } |
| } |
| } else { |
| /* Kernel-space memory */ |
| for (j = page_shift; |
| j < pages_to_get; |
| j++) { |
| void *addr = |
| (void *)(buffer_offset_vaddr + |
| (j - page_shift) * PAGE_SIZE); |
| if (!is_vmalloc_addr(addr)) { |
| dprintk(KERN_ERR |
| "tf_fill_descriptor_table: " |
| "cannot handle address %p\n", |
| addr); |
| goto error; |
| } |
| struct page *page = vmalloc_to_page(addr); |
| if (page == NULL) { |
| dprintk(KERN_ERR |
| "tf_fill_descriptor_table: " |
| "cannot map %p to page\n", |
| addr); |
| goto error; |
| } |
| coarse_pg_table->descriptors[j] = (u32)page; |
| get_page(page); |
| |
| /* change coarse page "page address" */ |
| tf_get_l2_page_descriptor( |
| &coarse_pg_table->descriptors[j], |
| flags, |
| &init_mm, |
| vmas[j]); |
| } |
| } |
| |
| dmac_flush_range((void *)coarse_pg_table->descriptors, |
| (void *)(((u32)(coarse_pg_table->descriptors)) + |
| TF_DESCRIPTOR_TABLE_CAPACITY * sizeof(u32))); |
| |
| outer_clean_range( |
| __pa(coarse_pg_table->descriptors), |
| __pa(coarse_pg_table->descriptors) + |
| TF_DESCRIPTOR_TABLE_CAPACITY * sizeof(u32)); |
| wmb(); |
| |
| /* Update the coarse page table address */ |
| descriptors[coarse_page_index] = |
| tf_get_l1_coarse_descriptor( |
| coarse_pg_table->descriptors); |
| |
| /* |
| * The next coarse page has no page shift, reset the |
| * page_shift |
| */ |
| page_shift = 0; |
| } |
| |
| *descriptor_count = coarse_page_count; |
| shmem_desc->coarse_pg_table_count = coarse_page_count; |
| |
| #ifdef DEBUG_COARSE_TABLES |
| printk(KERN_DEBUG "ntf_fill_descriptor_table - size=0x%08X " |
| "numberOfCoarsePages=%d\n", buffer_size, |
| shmem_desc->coarse_pg_table_count); |
| for (coarse_page_index = 0; |
| coarse_page_index < shmem_desc->coarse_pg_table_count; |
| coarse_page_index++) { |
| u32 j; |
| struct tf_coarse_page_table *coarse_page_table = |
| shmem_desc->coarse_pg_table[coarse_page_index]; |
| |
| printk(KERN_DEBUG " Descriptor=%p address=%p index=%d\n", |
| coarse_page_table, |
| coarse_page_table->descriptors, |
| coarse_page_index); |
| for (j = 0; |
| j < TF_DESCRIPTOR_TABLE_CAPACITY; |
| j += 8) { |
| int k; |
| printk(KERN_DEBUG " "); |
| for (k = j; k < j + 8; k++) |
| printk(KERN_DEBUG "0x%08X ", |
| coarse_page_table->descriptors[k]); |
| printk(KERN_DEBUG "\n"); |
| } |
| } |
| printk(KERN_DEBUG "ntf_fill_descriptor_table() - done\n\n"); |
| #endif |
| |
| return 0; |
| |
| error: |
| tf_cleanup_shared_memory( |
| alloc_context, |
| shmem_desc, |
| 0); |
| |
| return error; |
| } |
| |
| |
| /*---------------------------------------------------------------------------- |
| * Standard communication operations |
| *----------------------------------------------------------------------------*/ |
| |
| u8 *tf_get_description(struct tf_comm *comm) |
| { |
| if (test_bit(TF_COMM_FLAG_L1_SHARED_ALLOCATED, &(comm->flags))) |
| return comm->pBuffer->version_description; |
| |
| return NULL; |
| } |
| |
| /* |
| * Returns a non-zero value if the specified S-timeout has expired, zero |
| * otherwise. |
| * |
| * The placeholder referenced to by relative_timeout_jiffies gives the relative |
| * timeout from now in jiffies. It is set to zero if the S-timeout has expired, |
| * or to MAX_SCHEDULE_TIMEOUT if the S-timeout is infinite. |
| */ |
| static int tf_test_s_timeout( |
| u64 timeout, |
| signed long *relative_timeout_jiffies) |
| { |
| struct timeval now; |
| u64 time64; |
| |
| *relative_timeout_jiffies = 0; |
| |
| /* immediate timeout */ |
| if (timeout == TIME_IMMEDIATE) |
| return 1; |
| |
| /* infinite timeout */ |
| if (timeout == TIME_INFINITE) { |
| dprintk(KERN_DEBUG "tf_test_s_timeout: " |
| "timeout is infinite\n"); |
| *relative_timeout_jiffies = MAX_SCHEDULE_TIMEOUT; |
| return 0; |
| } |
| |
| do_gettimeofday(&now); |
| time64 = now.tv_sec; |
| /* will not overflow as operations are done on 64bit values */ |
| time64 = (time64 * 1000) + (now.tv_usec / 1000); |
| |
| /* timeout expired */ |
| if (time64 >= timeout) { |
| dprintk(KERN_DEBUG "tf_test_s_timeout: timeout expired\n"); |
| return 1; |
| } |
| |
| /* |
| * finite timeout, compute relative_timeout_jiffies |
| */ |
| /* will not overflow as time64 < timeout */ |
| timeout -= time64; |
| |
| /* guarantee *relative_timeout_jiffies is a valid timeout */ |
| if ((timeout >> 32) != 0) |
| *relative_timeout_jiffies = MAX_JIFFY_OFFSET; |
| else |
| *relative_timeout_jiffies = |
| msecs_to_jiffies((unsigned int) timeout); |
| |
| dprintk(KERN_DEBUG "tf_test_s_timeout: timeout is 0x%lx\n", |
| *relative_timeout_jiffies); |
| return 0; |
| } |
| |
| static void tf_copy_answers(struct tf_comm *comm) |
| { |
| u32 first_answer; |
| u32 first_free_answer; |
| struct tf_answer_struct *answerStructureTemp; |
| |
| if (test_bit(TF_COMM_FLAG_L1_SHARED_ALLOCATED, &(comm->flags))) { |
| spin_lock(&comm->lock); |
| first_free_answer = tf_read_reg32( |
| &comm->pBuffer->first_free_answer); |
| first_answer = tf_read_reg32( |
| &comm->pBuffer->first_answer); |
| |
| while (first_answer != first_free_answer) { |
| /* answer queue not empty */ |
| union tf_answer sComAnswer; |
| struct tf_answer_header header; |
| |
| /* |
| * the size of the command in words of 32bit, not in |
| * bytes |
| */ |
| u32 command_size; |
| u32 i; |
| u32 *temp = (uint32_t *) &header; |
| |
| dprintk(KERN_INFO |
| "[pid=%d] tf_copy_answers(%p): " |
| "Read answers from L1\n", |
| current->pid, comm); |
| |
| /* Read the answer header */ |
| for (i = 0; |
| i < sizeof(struct tf_answer_header)/sizeof(u32); |
| i++) |
| temp[i] = comm->pBuffer->answer_queue[ |
| (first_answer + i) % |
| TF_S_ANSWER_QUEUE_CAPACITY]; |
| |
| /* Read the answer from the L1_Buffer*/ |
| command_size = header.message_size + |
| sizeof(struct tf_answer_header)/sizeof(u32); |
| temp = (uint32_t *) &sComAnswer; |
| for (i = 0; i < command_size; i++) |
| temp[i] = comm->pBuffer->answer_queue[ |
| (first_answer + i) % |
| TF_S_ANSWER_QUEUE_CAPACITY]; |
| |
| answerStructureTemp = (struct tf_answer_struct *) |
| sComAnswer.header.operation_id; |
| |
| tf_dump_answer(&sComAnswer); |
| |
| memcpy(answerStructureTemp->answer, &sComAnswer, |
| command_size * sizeof(u32)); |
| answerStructureTemp->answer_copied = true; |
| |
| first_answer += command_size; |
| tf_write_reg32(&comm->pBuffer->first_answer, |
| first_answer); |
| } |
| spin_unlock(&(comm->lock)); |
| } |
| } |
| |
| static void tf_copy_command( |
| struct tf_comm *comm, |
| union tf_command *command, |
| struct tf_connection *connection, |
| enum TF_COMMAND_STATE *command_status) |
| { |
| if ((test_bit(TF_COMM_FLAG_L1_SHARED_ALLOCATED, &(comm->flags))) |
| && (command != NULL)) { |
| /* |
| * Write the message in the message queue. |
| */ |
| |
| if (*command_status == TF_COMMAND_STATE_PENDING) { |
| u32 command_size; |
| u32 queue_words_count; |
| u32 i; |
| u32 first_free_command; |
| u32 first_command; |
| |
| spin_lock(&comm->lock); |
| |
| first_command = tf_read_reg32( |
| &comm->pBuffer->first_command); |
| first_free_command = tf_read_reg32( |
| &comm->pBuffer->first_free_command); |
| |
| queue_words_count = first_free_command - first_command; |
| command_size = command->header.message_size + |
| sizeof(struct tf_command_header)/sizeof(u32); |
| if ((queue_words_count + command_size) < |
| TF_N_MESSAGE_QUEUE_CAPACITY) { |
| /* |
| * Command queue is not full. |
| * If the Command queue is full, |
| * the command will be copied at |
| * another iteration |
| * of the current function. |
| */ |
| |
| /* |
| * Change the conn state |
| */ |
| if (connection == NULL) |
| goto copy; |
| |
| spin_lock(&(connection->state_lock)); |
| |
| if ((connection->state == |
| TF_CONN_STATE_NO_DEVICE_CONTEXT) |
| && |
| (command->header.message_type == |
| TF_MESSAGE_TYPE_CREATE_DEVICE_CONTEXT)) { |
| |
| dprintk(KERN_INFO |
| "tf_copy_command(%p):" |
| "Conn state is DEVICE_CONTEXT_SENT\n", |
| connection); |
| connection->state = |
| TF_CONN_STATE_CREATE_DEVICE_CONTEXT_SENT; |
| } else if ((connection->state != |
| TF_CONN_STATE_VALID_DEVICE_CONTEXT) |
| && |
| (command->header.message_type != |
| TF_MESSAGE_TYPE_CREATE_DEVICE_CONTEXT)) { |
| /* The connection |
| * is no longer valid. |
| * We may not send any command on it, |
| * not even another |
| * DESTROY_DEVICE_CONTEXT. |
| */ |
| dprintk(KERN_INFO |
| "[pid=%d] tf_copy_command(%p): " |
| "Connection no longer valid." |
| "ABORT\n", |
| current->pid, connection); |
| *command_status = |
| TF_COMMAND_STATE_ABORTED; |
| spin_unlock( |
| &(connection->state_lock)); |
| spin_unlock( |
| &comm->lock); |
| return; |
| } else if ( |
| (command->header.message_type == |
| TF_MESSAGE_TYPE_DESTROY_DEVICE_CONTEXT) && |
| (connection->state == |
| TF_CONN_STATE_VALID_DEVICE_CONTEXT) |
| ) { |
| dprintk(KERN_INFO |
| "[pid=%d] tf_copy_command(%p): " |
| "Conn state is " |
| "DESTROY_DEVICE_CONTEXT_SENT\n", |
| current->pid, connection); |
| connection->state = |
| TF_CONN_STATE_DESTROY_DEVICE_CONTEXT_SENT; |
| } |
| spin_unlock(&(connection->state_lock)); |
| copy: |
| /* |
| * Copy the command to L1 Buffer |
| */ |
| dprintk(KERN_INFO |
| "[pid=%d] tf_copy_command(%p): " |
| "Write Message in the queue\n", |
| current->pid, command); |
| tf_dump_command(command); |
| |
| for (i = 0; i < command_size; i++) |
| comm->pBuffer->command_queue[ |
| (first_free_command + i) % |
| TF_N_MESSAGE_QUEUE_CAPACITY] = |
| ((uint32_t *) command)[i]; |
| |
| *command_status = |
| TF_COMMAND_STATE_SENT; |
| first_free_command += command_size; |
| |
| tf_write_reg32( |
| &comm-> |
| pBuffer->first_free_command, |
| first_free_command); |
| } |
| spin_unlock(&comm->lock); |
| } |
| } |
| } |
| |
| /* |
| * Sends the specified message through the specified communication channel. |
| * |
| * This function sends the command and waits for the answer |
| * |
| * Returns zero upon successful completion, or an appropriate error code upon |
| * failure. |
| */ |
| static int tf_send_recv(struct tf_comm *comm, |
| union tf_command *command, |
| struct tf_answer_struct *answerStruct, |
| struct tf_connection *connection, |
| int bKillable |
| #ifdef CONFIG_TF_ZEBRA |
| , bool *secure_is_idle |
| #endif |
| ) |
| { |
| int result; |
| u64 timeout; |
| signed long nRelativeTimeoutJiffies; |
| bool wait_prepared = false; |
| enum TF_COMMAND_STATE command_status = TF_COMMAND_STATE_PENDING; |
| DEFINE_WAIT(wait); |
| #ifdef CONFIG_FREEZER |
| unsigned long saved_flags; |
| #endif |
| dprintk(KERN_INFO "[pid=%d] tf_send_recv(%p)\n", |
| current->pid, command); |
| |
| #ifdef CONFIG_FREEZER |
| saved_flags = current->flags; |
| current->flags |= PF_FREEZER_NOSIG; |
| #endif |
| |
| /* |
| * Read all answers from the answer queue |
| */ |
| copy_answers: |
| tf_copy_answers(comm); |
| |
| tf_copy_command(comm, command, connection, &command_status); |
| |
| /* |
| * Notify all waiting threads |
| */ |
| wake_up(&(comm->wait_queue)); |
| |
| #ifdef CONFIG_FREEZER |
| if (unlikely(freezing(current))) { |
| |
| #ifdef CONFIG_TF_ZEBRA |
| if (!(*secure_is_idle)) { |
| if (tf_schedule_secure_world(comm, true) == |
| STATUS_PENDING) |
| goto copy_answers; |
| |
| tf_l4sec_clkdm_allow_idle(true); |
| *secure_is_idle = true; |
| } |
| #endif |
| |
| dprintk(KERN_INFO |
| "Entering refrigerator.\n"); |
| refrigerator(); |
| dprintk(KERN_INFO |
| "Left refrigerator.\n"); |
| goto copy_answers; |
| } |
| #endif |
| |
| #ifndef CONFIG_PREEMPT |
| if (need_resched()) |
| schedule(); |
| #endif |
| |
| #ifdef CONFIG_TF_ZEBRA |
| /* |
| * Handle RPC (if any) |
| */ |
| if (tf_rpc_execute(comm) == RPC_NON_YIELD) |
| goto schedule_secure_world; |
| #endif |
| |
| /* |
| * Join wait queue |
| */ |
| /*dprintk(KERN_INFO "[pid=%d] tf_send_recv(%p): Prepare to wait\n", |
| current->pid, command);*/ |
| prepare_to_wait(&comm->wait_queue, &wait, |
| bKillable ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE); |
| wait_prepared = true; |
| |
| /* |
| * Check if our answer is available |
| */ |
| if (command_status == TF_COMMAND_STATE_ABORTED) { |
| /* Not waiting for an answer, return error code */ |
| result = -EINTR; |
| dprintk(KERN_ERR "[pid=%d] tf_send_recv: " |
| "Command status is ABORTED." |
| "Exit with 0x%x\n", |
| current->pid, result); |
| goto exit; |
| } |
| if (answerStruct->answer_copied) { |
| dprintk(KERN_INFO "[pid=%d] tf_send_recv: " |
| "Received answer (type 0x%02X)\n", |
| current->pid, |
| answerStruct->answer->header.message_type); |
| result = 0; |
| goto exit; |
| } |
| |
| /* |
| * Check if a signal is pending |
| */ |
| if (bKillable && (sigkill_pending())) { |
| if (command_status == TF_COMMAND_STATE_PENDING) |
| /*Command was not sent. */ |
| result = -EINTR; |
| else |
| /* Command was sent but no answer was received yet. */ |
| result = -EIO; |
| |
| dprintk(KERN_ERR "[pid=%d] tf_send_recv: " |
| "Signal Pending. Return error %d\n", |
| current->pid, result); |
| goto exit; |
| } |
| |
| /* |
| * Check if secure world is schedulable. It is schedulable if at |
| * least one of the following conditions holds: |
| * + it is still initializing (TF_COMM_FLAG_L1_SHARED_ALLOCATED |
| * is not set); |
| * + there is a command in the queue; |
| * + the secure world timeout is zero. |
| */ |
| if (test_bit(TF_COMM_FLAG_L1_SHARED_ALLOCATED, &(comm->flags))) { |
| u32 first_free_command; |
| u32 first_command; |
| spin_lock(&comm->lock); |
| first_command = tf_read_reg32( |
| &comm->pBuffer->first_command); |
| first_free_command = tf_read_reg32( |
| &comm->pBuffer->first_free_command); |
| spin_unlock(&comm->lock); |
| tf_read_timeout(comm, &timeout); |
| if ((first_free_command == first_command) && |
| (tf_test_s_timeout(timeout, |
| &nRelativeTimeoutJiffies) == 0)) |
| /* |
| * If command queue is empty and if timeout has not |
| * expired secure world is not schedulable |
| */ |
| goto wait; |
| } |
| |
| finish_wait(&comm->wait_queue, &wait); |
| wait_prepared = false; |
| |
| /* |
| * Yield to the Secure World |
| */ |
| #ifdef CONFIG_TF_ZEBRA |
| schedule_secure_world: |
| if (*secure_is_idle) { |
| tf_l4sec_clkdm_wakeup(true); |
| *secure_is_idle = false; |
| } |
| #endif |
| |
| result = tf_schedule_secure_world(comm, false); |
| if (result < 0) |
| goto exit; |
| goto copy_answers; |
| |
| wait: |
| if (bKillable && (sigkill_pending())) { |
| if (command_status == TF_COMMAND_STATE_PENDING) |
| result = -EINTR; /* Command was not sent. */ |
| else |
| /* Command was sent but no answer was received yet. */ |
| result = -EIO; |
| |
| dprintk(KERN_ERR "[pid=%d] tf_send_recv: " |
| "Signal Pending while waiting. Return error %d\n", |
| current->pid, result); |
| goto exit; |
| } |
| |
| if (nRelativeTimeoutJiffies == MAX_SCHEDULE_TIMEOUT) |
| dprintk(KERN_INFO "[pid=%d] tf_send_recv: " |
| "prepare to sleep infinitely\n", current->pid); |
| else |
| dprintk(KERN_INFO "tf_send_recv: " |
| "prepare to sleep 0x%lx jiffies\n", |
| nRelativeTimeoutJiffies); |
| |
| #ifdef CONFIG_TF_ZEBRA |
| if (!(*secure_is_idle)) { |
| if (tf_schedule_secure_world(comm, true) == STATUS_PENDING) { |
| finish_wait(&comm->wait_queue, &wait); |
| wait_prepared = false; |
| goto copy_answers; |
| } |
| tf_l4sec_clkdm_allow_idle(true); |
| *secure_is_idle = true; |
| } |
| #endif |
| |
| /* go to sleep */ |
| if (schedule_timeout(nRelativeTimeoutJiffies) == 0) |
| dprintk(KERN_INFO |
| "tf_send_recv: timeout expired\n"); |
| else |
| dprintk(KERN_INFO |
| "tf_send_recv: signal delivered\n"); |
| |
| finish_wait(&comm->wait_queue, &wait); |
| wait_prepared = false; |
| goto copy_answers; |
| |
| exit: |
| if (wait_prepared) { |
| finish_wait(&comm->wait_queue, &wait); |
| wait_prepared = false; |
| } |
| |
| #ifdef CONFIG_TF_ZEBRA |
| if ((!(*secure_is_idle)) && (result != -EIO)) { |
| if (tf_schedule_secure_world(comm, true) == STATUS_PENDING) |
| goto copy_answers; |
| |
| tf_l4sec_clkdm_allow_idle(true); |
| *secure_is_idle = true; |
| } |
| #endif |
| |
| #ifdef CONFIG_FREEZER |
| current->flags &= ~(PF_FREEZER_NOSIG); |
| current->flags |= (saved_flags & PF_FREEZER_NOSIG); |
| #endif |
| |
| return result; |
| } |
| |
| /* |
| * Sends the specified message through the specified communication channel. |
| * |
| * This function sends the message and waits for the corresponding answer |
| * It may return if a signal needs to be delivered. |
| * |
| * Returns zero upon successful completion, or an appropriate error code upon |
| * failure. |
| */ |
| int tf_send_receive(struct tf_comm *comm, |
| union tf_command *command, |
| union tf_answer *answer, |
| struct tf_connection *connection, |
| bool bKillable) |
| { |
| int error; |
| struct tf_answer_struct answerStructure; |
| #ifdef CONFIG_SMP |
| long ret_affinity; |
| cpumask_t saved_cpu_mask; |
| cpumask_t local_cpu_mask = CPU_MASK_NONE; |
| #endif |
| #ifdef CONFIG_TF_ZEBRA |
| bool secure_is_idle = true; |
| #endif |
| |
| answerStructure.answer = answer; |
| answerStructure.answer_copied = false; |
| |
| if (command != NULL) |
| command->header.operation_id = (u32) &answerStructure; |
| |
| dprintk(KERN_INFO "tf_send_receive\n"); |
| |
| #ifdef CONFIG_TF_ZEBRA |
| if (!test_bit(TF_COMM_FLAG_PA_AVAILABLE, &comm->flags)) { |
| dprintk(KERN_ERR "tf_send_receive(%p): " |
| "Secure world not started\n", comm); |
| |
| return -EFAULT; |
| } |
| #endif |
| |
| if (test_bit(TF_COMM_FLAG_TERMINATING, &(comm->flags)) != 0) { |
| dprintk(KERN_DEBUG |
| "tf_send_receive: Flag Terminating is set\n"); |
| return 0; |
| } |
| |
| #ifdef CONFIG_SMP |
| cpu_set(0, local_cpu_mask); |
| sched_getaffinity(0, &saved_cpu_mask); |
| ret_affinity = sched_setaffinity(0, &local_cpu_mask); |
| if (ret_affinity != 0) |
| dprintk(KERN_ERR "sched_setaffinity #1 -> 0x%lX", ret_affinity); |
| #endif |
| |
| |
| /* |
| * Send the command |
| */ |
| error = tf_send_recv(comm, |
| command, &answerStructure, connection, bKillable |
| #ifdef CONFIG_TF_ZEBRA |
| , &secure_is_idle |
| #endif |
| ); |
| |
| if (!bKillable && sigkill_pending()) { |
| if ((command->header.message_type == |
| TF_MESSAGE_TYPE_CREATE_DEVICE_CONTEXT) && |
| (answer->create_device_context.error_code == |
| S_SUCCESS)) { |
| |
| /* |
| * CREATE_DEVICE_CONTEXT was interrupted. |
| */ |
| dprintk(KERN_INFO "tf_send_receive: " |
| "sending DESTROY_DEVICE_CONTEXT\n"); |
| answerStructure.answer = answer; |
| answerStructure.answer_copied = false; |
| |
| command->header.message_type = |
| TF_MESSAGE_TYPE_DESTROY_DEVICE_CONTEXT; |
| command->header.message_size = |
| (sizeof(struct |
| tf_command_destroy_device_context) - |
| sizeof(struct tf_command_header))/sizeof(u32); |
| command->header.operation_id = |
| (u32) &answerStructure; |
| command->destroy_device_context.device_context = |
| answer->create_device_context. |
| device_context; |
| |
| goto destroy_context; |
| } |
| } |
| |
| if (error == 0) { |
| /* |
| * tf_send_recv returned Success. |
| */ |
| if (command->header.message_type == |
| TF_MESSAGE_TYPE_CREATE_DEVICE_CONTEXT) { |
| spin_lock(&(connection->state_lock)); |
| connection->state = TF_CONN_STATE_VALID_DEVICE_CONTEXT; |
| spin_unlock(&(connection->state_lock)); |
| } else if (command->header.message_type == |
| TF_MESSAGE_TYPE_DESTROY_DEVICE_CONTEXT) { |
| spin_lock(&(connection->state_lock)); |
| connection->state = TF_CONN_STATE_NO_DEVICE_CONTEXT; |
| spin_unlock(&(connection->state_lock)); |
| } |
| } else if (error == -EINTR) { |
| /* |
| * No command was sent, return failure. |
| */ |
| dprintk(KERN_ERR |
| "tf_send_receive: " |
| "tf_send_recv failed (error %d) !\n", |
| error); |
| } else if (error == -EIO) { |
| /* |
| * A command was sent but its answer is still pending. |
| */ |
| |
| /* means bKillable is true */ |
| dprintk(KERN_ERR |
| "tf_send_receive: " |
| "tf_send_recv interrupted (error %d)." |
| "Send DESTROY_DEVICE_CONTEXT.\n", error); |
| |
| /* Send the DESTROY_DEVICE_CONTEXT. */ |
| answerStructure.answer = answer; |
| answerStructure.answer_copied = false; |
| |
| command->header.message_type = |
| TF_MESSAGE_TYPE_DESTROY_DEVICE_CONTEXT; |
| command->header.message_size = |
| (sizeof(struct tf_command_destroy_device_context) - |
| sizeof(struct tf_command_header))/sizeof(u32); |
| command->header.operation_id = |
| (u32) &answerStructure; |
| command->destroy_device_context.device_context = |
| connection->device_context; |
| |
| error = tf_send_recv(comm, |
| command, &answerStructure, connection, false |
| #ifdef CONFIG_TF_ZEBRA |
| , &secure_is_idle |
| #endif |
| ); |
| if (error == -EINTR) { |
| /* |
| * Another thread already sent |
| * DESTROY_DEVICE_CONTEXT. |
| * We must still wait for the answer |
| * to the original command. |
| */ |
| command = NULL; |
| goto destroy_context; |
| } else { |
| /* An answer was received. |
| * Check if it is the answer |
| * to the DESTROY_DEVICE_CONTEXT. |
| */ |
| spin_lock(&comm->lock); |
| if (answer->header.message_type != |
| TF_MESSAGE_TYPE_DESTROY_DEVICE_CONTEXT) { |
| answerStructure.answer_copied = false; |
| } |
| spin_unlock(&comm->lock); |
| if (!answerStructure.answer_copied) { |
| /* Answer to DESTROY_DEVICE_CONTEXT |
| * was not yet received. |
| * Wait for the answer. |
| */ |
| dprintk(KERN_INFO |
| "[pid=%d] tf_send_receive:" |
| "Answer to DESTROY_DEVICE_CONTEXT" |
| "not yet received.Retry\n", |
| current->pid); |
| command = NULL; |
| goto destroy_context; |
| } |
| } |
| } |
| |
| dprintk(KERN_INFO "tf_send_receive(): Message answer ready\n"); |
| goto exit; |
| |
| destroy_context: |
| error = tf_send_recv(comm, |
| command, &answerStructure, connection, false |
| #ifdef CONFIG_TF_ZEBRA |
| , &secure_is_idle |
| #endif |
| ); |
| |
| /* |
| * tf_send_recv cannot return an error because |
| * it's not killable and not within a connection |
| */ |
| BUG_ON(error != 0); |
| |
| /* Reset the state, so a new CREATE DEVICE CONTEXT can be sent */ |
| spin_lock(&(connection->state_lock)); |
| connection->state = TF_CONN_STATE_NO_DEVICE_CONTEXT; |
| spin_unlock(&(connection->state_lock)); |
| |
| exit: |
| |
| #ifdef CONFIG_SMP |
| ret_affinity = sched_setaffinity(0, &saved_cpu_mask); |
| if (ret_affinity != 0) |
| dprintk(KERN_ERR "sched_setaffinity #2 -> 0x%lX", ret_affinity); |
| #endif |
| return error; |
| } |
| |
| /*---------------------------------------------------------------------------- |
| * Power management |
| *----------------------------------------------------------------------------*/ |
| |
| |
| /* |
| * Handles all the power management calls. |
| * The operation is the type of power management |
| * operation to be performed. |
| * |
| * This routine will only return if a failure occured or if |
| * the required opwer management is of type "resume". |
| * "Hibernate" and "Shutdown" should lock when doing the |
| * corresponding SMC to the Secure World |
| */ |
| int tf_power_management(struct tf_comm *comm, |
| enum TF_POWER_OPERATION operation) |
| { |
| u32 status; |
| int error = 0; |
| |
| dprintk(KERN_INFO "tf_power_management(%d)\n", operation); |
| |
| #ifdef CONFIG_TF_ZEBRA |
| if (!test_bit(TF_COMM_FLAG_PA_AVAILABLE, &comm->flags)) { |
| dprintk(KERN_INFO "tf_power_management(%p): " |
| "succeeded (not started)\n", comm); |
| |
| return 0; |
| } |
| #endif |
| |
| status = ((tf_read_reg32(&(comm->pBuffer->status_s)) |
| & TF_STATUS_POWER_STATE_MASK) |
| >> TF_STATUS_POWER_STATE_SHIFT); |
| |
| switch (operation) { |
| case TF_POWER_OPERATION_SHUTDOWN: |
| switch (status) { |
| case TF_POWER_MODE_ACTIVE: |
| error = tf_pm_shutdown(comm); |
| |
| if (error) { |
| dprintk(KERN_ERR "tf_power_management(): " |
| "Failed with error code 0x%08x\n", |
| error); |
| goto error; |
| } |
| break; |
| |
| default: |
| goto not_allowed; |
| } |
| break; |
| |
| case TF_POWER_OPERATION_HIBERNATE: |
| switch (status) { |
| case TF_POWER_MODE_ACTIVE: |
| error = tf_pm_hibernate(comm); |
| |
| if (error) { |
| dprintk(KERN_ERR "tf_power_management(): " |
| "Failed with error code 0x%08x\n", |
| error); |
| goto error; |
| } |
| break; |
| |
| default: |
| goto not_allowed; |
| } |
| break; |
| |
| case TF_POWER_OPERATION_RESUME: |
| error = tf_pm_resume(comm); |
| |
| if (error != 0) { |
| dprintk(KERN_ERR "tf_power_management(): " |
| "Failed with error code 0x%08x\n", |
| error); |
| goto error; |
| } |
| break; |
| } |
| |
| dprintk(KERN_INFO "tf_power_management(): succeeded\n"); |
| return 0; |
| |
| not_allowed: |
| dprintk(KERN_ERR "tf_power_management(): " |
| "Power command not allowed in current " |
| "Secure World state %d\n", status); |
| error = -ENOTTY; |
| error: |
| return error; |
| } |