blob: 79b40348748e796bf7369255adba77264a5c3fb7 [file] [log] [blame] [edit]
/**
* 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(&current->pending.signal, SIGKILL)) {
dprintk(KERN_INFO "A SIGKILL is pending\n");
return true;
} else if (sigismember(
&current->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;
}