Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
tree: 7535e2e499
Fetching contributors…

Cannot retrieve contributors at this time

698 lines (576 sloc) 17.908 kB
/*
* Copyright (c) 2006-2010 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/atomic.h>
#include <linux/uaccess.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/mm.h>
#include <linux/page-flags.h>
#include <linux/pm.h>
#include <linux/sysdev.h>
#include <linux/vmalloc.h>
#include <linux/signal.h>
#ifdef CONFIG_ANDROID
#include <linux/device.h>
#endif
#include "scx_protocol.h"
#include "scxlnx_defs.h"
#include "scxlnx_util.h"
#include "scxlnx_conn.h"
#include "scxlnx_comm.h"
#ifdef CONFIG_TF_ZEBRA
#include <plat/cpu.h>
#include "scxlnx_zebra.h"
#endif
#include "s_version.h"
/*----------------------------------------------------------------------------
* Forward Declarations
*----------------------------------------------------------------------------*/
/*
* Creates and registers the device to be managed by the specified driver.
*
* Returns zero upon successful completion, or an appropriate error code upon
* failure.
*/
static int SCXLNXDeviceRegister(void);
/*
* Implements the device Open callback.
*/
static int SCXLNXDeviceOpen(
struct inode *inode,
struct file *file);
/*
* Implements the device Release callback.
*/
static int SCXLNXDeviceRelease(
struct inode *inode,
struct file *file);
/*
* Implements the device ioctl callback.
*/
static long SCXLNXDeviceIoctl(
struct file *file,
unsigned int ioctl_num,
unsigned long ioctl_param);
/*
* Implements the device shutdown callback.
*/
static int SCXLNXDeviceShutdown(
struct sys_device *sysdev);
/*
* Implements the device suspend callback.
*/
static int SCXLNXDeviceSuspend(
struct sys_device *sysdev,
pm_message_t state);
/*
* Implements the device resume callback.
*/
static int SCXLNXDeviceResume(
struct sys_device *sysdev);
/*---------------------------------------------------------------------------
* Module Parameters
*---------------------------------------------------------------------------*/
/*
* The device major number used to register a unique character device driver.
* Let the default value be 122
*/
static int device_major_number = 122;
module_param(device_major_number, int, 0000);
MODULE_PARM_DESC(device_major_number,
"The device major number used to register a unique character "
"device driver");
#ifdef CONFIG_TF_TRUSTZONE
/**
* The softint interrupt line used by the Secure World.
*/
static int soft_interrupt = -1;
module_param(soft_interrupt, int, 0000);
MODULE_PARM_DESC(soft_interrupt,
"The softint interrupt line used by the Secure world");
#endif
#ifdef CONFIG_ANDROID
static struct class *tf_class;
#endif
/*----------------------------------------------------------------------------
* Global Variables
*----------------------------------------------------------------------------*/
/*
* tf_driver character device definitions.
* read and write methods are not defined
* and will return an error if used by user space
*/
static const struct file_operations g_SCXLNXDeviceFileOps = {
.owner = THIS_MODULE,
.open = SCXLNXDeviceOpen,
.release = SCXLNXDeviceRelease,
.unlocked_ioctl = SCXLNXDeviceIoctl,
.llseek = no_llseek,
};
static struct sysdev_class g_SCXLNXDeviceSysClass = {
.name = SCXLNX_DEVICE_BASE_NAME,
.shutdown = SCXLNXDeviceShutdown,
.suspend = SCXLNXDeviceSuspend,
.resume = SCXLNXDeviceResume,
};
/* The single device supported by this driver */
static struct SCXLNX_DEVICE g_SCXLNXDevice = {0, };
/*----------------------------------------------------------------------------
* Implementations
*----------------------------------------------------------------------------*/
struct SCXLNX_DEVICE *SCXLNXGetDevice(void)
{
return &g_SCXLNXDevice;
}
/*
* displays the driver stats
*/
static ssize_t kobject_show(struct kobject *pkobject,
struct attribute *pattributes, char *buf)
{
struct SCXLNX_DEVICE_STATS *pDeviceStats = &g_SCXLNXDevice.sDeviceStats;
u32 nStatPagesAllocated;
u32 nStatPagesLocked;
u32 nStatMemoriesAllocated;
nStatMemoriesAllocated =
atomic_read(&(pDeviceStats->stat_memories_allocated));
nStatPagesAllocated =
atomic_read(&(pDeviceStats->stat_pages_allocated));
nStatPagesLocked = atomic_read(&(pDeviceStats->stat_pages_locked));
/*
* AFY: could we add the number of context switches (call to the SMI
* instruction)
*/
return snprintf(buf, PAGE_SIZE,
"stat.memories.allocated: %d\n"
"stat.pages.allocated: %d\n"
"stat.pages.locked: %d\n",
nStatMemoriesAllocated,
nStatPagesAllocated,
nStatPagesLocked);
}
static const struct sysfs_ops kobj_sysfs_operations = {
.show = kobject_show,
};
/*----------------------------------------------------------------------------*/
/*
* First routine called when the kernel module is loaded
*/
static int __init SCXLNXDeviceRegister(void)
{
int nError;
struct SCXLNX_DEVICE *pDevice = &g_SCXLNXDevice;
struct SCXLNX_DEVICE_STATS *pDeviceStats = &pDevice->sDeviceStats;
dprintk(KERN_INFO "SCXLNXDeviceRegister()\n");
#ifdef CONFIG_TF_ZEBRA
nError = SCXLNXCtrlDeviceInit();
if (nError <= 0)
return nError;
#endif
/*
* Initialize the device
*/
pDevice->nDevNum = MKDEV(device_major_number,
SCXLNX_DEVICE_MINOR_NUMBER);
cdev_init(&pDevice->cdev, &g_SCXLNXDeviceFileOps);
pDevice->cdev.owner = THIS_MODULE;
pDevice->sysdev.id = 0;
pDevice->sysdev.cls = &g_SCXLNXDeviceSysClass;
INIT_LIST_HEAD(&pDevice->conns);
spin_lock_init(&pDevice->connsLock);
/* register the sysfs object driver stats */
pDeviceStats->kobj_type.sysfs_ops = &kobj_sysfs_operations;
pDeviceStats->kobj_stat_attribute.name = "info";
pDeviceStats->kobj_stat_attribute.mode = S_IRUGO;
pDeviceStats->kobj_attribute_list[0] =
&pDeviceStats->kobj_stat_attribute;
pDeviceStats->kobj_type.default_attrs =
pDeviceStats->kobj_attribute_list,
kobject_init_and_add(&(pDeviceStats->kobj),
&(pDeviceStats->kobj_type), NULL, "%s",
SCXLNX_DEVICE_BASE_NAME);
/*
* Register the system device.
*/
nError = sysdev_class_register(&g_SCXLNXDeviceSysClass);
if (nError != 0) {
printk(KERN_ERR "SCXLNXDeviceRegister():"
" sysdev_class_register failed (error %d)!\n",
nError);
goto sysdev_class_register_failed;
}
nError = sysdev_register(&pDevice->sysdev);
if (nError != 0) {
dprintk(KERN_ERR "SCXLNXDeviceRegister(): "
"sysdev_register failed (error %d)!\n",
nError);
goto sysdev_register_failed;
}
/*
* Register the char device.
*/
printk(KERN_INFO "Registering char device %s (%u:%u)\n",
SCXLNX_DEVICE_BASE_NAME,
MAJOR(pDevice->nDevNum),
MINOR(pDevice->nDevNum));
nError = register_chrdev_region(pDevice->nDevNum, 1,
SCXLNX_DEVICE_BASE_NAME);
if (nError != 0) {
printk(KERN_ERR "SCXLNXDeviceRegister():"
" register_chrdev_region failed (error %d)!\n",
nError);
goto register_chrdev_region_failed;
}
nError = cdev_add(&pDevice->cdev, pDevice->nDevNum, 1);
if (nError != 0) {
printk(KERN_ERR "SCXLNXDeviceRegister(): "
"cdev_add failed (error %d)!\n",
nError);
goto cdev_add_failed;
}
/*
* Initialize the communication with the Secure World.
*/
#ifdef CONFIG_TF_TRUSTZONE
pDevice->sm.nSoftIntIrq = soft_interrupt;
#endif
nError = SCXLNXCommInit(&g_SCXLNXDevice.sm);
if (nError != S_SUCCESS) {
dprintk(KERN_ERR "SCXLNXDeviceRegister(): "
"SCXLNXCommInit failed (error %d)!\n",
nError);
goto init_failed;
}
#ifdef CONFIG_ANDROID
tf_class = class_create(THIS_MODULE, SCXLNX_DEVICE_BASE_NAME);
device_create(tf_class, NULL,
pDevice->nDevNum,
NULL, SCXLNX_DEVICE_BASE_NAME);
#endif
#ifdef CONFIG_TF_ZEBRA
/*
* Initializes the /dev/tf_ctrl device node.
*/
nError = SCXLNXCtrlDeviceRegister();
if (nError)
goto init_failed;
#endif
#ifdef CONFIG_BENCH_SECURE_CYCLE
runBogoMIPS();
addressCacheProperty((unsigned long) &SCXLNXDeviceRegister);
#endif
/*
* Successful completion.
*/
dprintk(KERN_INFO "SCXLNXDeviceRegister(): Success\n");
return 0;
/*
* Error: undo all operations in the reverse order
*/
init_failed:
cdev_del(&pDevice->cdev);
cdev_add_failed:
unregister_chrdev_region(pDevice->nDevNum, 1);
register_chrdev_region_failed:
sysdev_unregister(&(pDevice->sysdev));
sysdev_register_failed:
sysdev_class_unregister(&g_SCXLNXDeviceSysClass);
sysdev_class_register_failed:
kobject_del(&g_SCXLNXDevice.sDeviceStats.kobj);
dprintk(KERN_INFO "SCXLNXDeviceRegister(): Failure (error %d)\n",
nError);
return nError;
}
/*----------------------------------------------------------------------------*/
static int SCXLNXDeviceOpen(struct inode *inode, struct file *file)
{
int nError;
struct SCXLNX_DEVICE *pDevice = &g_SCXLNXDevice;
struct SCXLNX_CONNECTION *pConn = NULL;
dprintk(KERN_INFO "SCXLNXDeviceOpen(%u:%u, %p)\n",
imajor(inode), iminor(inode), file);
/* Dummy lseek for non-seekable driver */
nError = nonseekable_open(inode, file);
if (nError != 0) {
dprintk(KERN_ERR "SCXLNXDeviceOpen(%p): "
"nonseekable_open failed (error %d)!\n",
file, nError);
goto error;
}
#ifndef CONFIG_ANDROID
/*
* Check file flags. We only autthorize the O_RDWR access
*/
if (file->f_flags != O_RDWR) {
dprintk(KERN_ERR "SCXLNXDeviceOpen(%p): "
"Invalid access mode %u\n",
file, file->f_flags);
nError = -EACCES;
goto error;
}
#endif
/*
* Open a new connection.
*/
nError = SCXLNXConnOpen(pDevice, file, &pConn);
if (nError != 0) {
dprintk(KERN_ERR "SCXLNXDeviceOpen(%p): "
"SCXLNXConnOpen failed (error %d)!\n",
file, nError);
goto error;
}
/*
* Attach the connection to the device.
*/
spin_lock(&(pDevice->connsLock));
list_add(&(pConn->list), &(pDevice->conns));
spin_unlock(&(pDevice->connsLock));
file->private_data = pConn;
/*
* Send the CreateDeviceContext command to the secure
*/
nError = SCXLNXConnCreateDeviceContext(pConn);
if (nError != 0) {
dprintk(KERN_ERR "SCXLNXDeviceOpen(%p): "
"SCXLNXConnCreateDeviceContext failed (error %d)!\n",
file, nError);
goto error1;
}
/*
* Successful completion.
*/
dprintk(KERN_INFO "SCXLNXDeviceOpen(%p): Success (pConn=%p)\n",
file, pConn);
return 0;
/*
* Error handling.
*/
error1:
SCXLNXConnClose(pConn);
error:
dprintk(KERN_INFO "SCXLNXDeviceOpen(%p): Failure (error %d)\n",
file, nError);
return nError;
}
/*----------------------------------------------------------------------------*/
static int SCXLNXDeviceRelease(struct inode *inode, struct file *file)
{
struct SCXLNX_CONNECTION *pConn;
dprintk(KERN_INFO "SCXLNXDeviceRelease(%u:%u, %p)\n",
imajor(inode), iminor(inode), file);
pConn = SCXLNXConnFromFile(file);
spin_lock(&g_SCXLNXDevice.connsLock);
list_del(&pConn->list);
spin_unlock(&g_SCXLNXDevice.connsLock);
SCXLNXConnClose(pConn);
dprintk(KERN_INFO "SCXLNXDeviceRelease(%p): Success\n", file);
return 0;
}
/*----------------------------------------------------------------------------*/
static long SCXLNXDeviceIoctl(struct file *file, unsigned int ioctl_num,
unsigned long ioctl_param)
{
int nResult = S_SUCCESS;
struct SCXLNX_CONNECTION *pConn;
union SCX_COMMAND_MESSAGE sMessage;
struct SCX_COMMAND_HEADER sCommandHeader;
union SCX_ANSWER_MESSAGE sAnswer;
u32 nCommandSize;
u32 nAnswerSize;
void *pUserAnswer;
dprintk(KERN_INFO "SCXLNXDeviceIoctl(%p, %u, %p)\n",
file, ioctl_num, (void *) ioctl_param);
switch (ioctl_num) {
case IOCTL_SCX_GET_VERSION:
/* ioctl is asking for the driver interface version */
nResult = SCX_DRIVER_INTERFACE_VERSION;
goto exit;
case IOCTL_SCX_EXCHANGE:
/*
* ioctl is asking to perform a message exchange with the Secure
* Module
*/
/*
* Make a local copy of the data from the user application
* This routine checks the data is readable
*
* Get the header first.
*/
if (copy_from_user(&sCommandHeader,
(struct SCX_COMMAND_HEADER *)ioctl_param,
sizeof(struct SCX_COMMAND_HEADER))) {
dprintk(KERN_ERR "SCXLNXDeviceIoctl(%p): "
"Cannot access ioctl parameter %p\n",
file, (void *) ioctl_param);
nResult = -EFAULT;
goto exit;
}
/* size in words of u32 */
nCommandSize = sCommandHeader.nMessageSize +
sizeof(struct SCX_COMMAND_HEADER)/sizeof(u32);
if (nCommandSize > sizeof(sMessage)/sizeof(u32)) {
dprintk(KERN_ERR "SCXLNXDeviceIoctl(%p): "
"Buffer overflow: too many bytes to copy %d\n",
file, nCommandSize);
nResult = -EFAULT;
goto exit;
}
if (copy_from_user(&sMessage,
(union SCX_COMMAND_MESSAGE *)ioctl_param,
nCommandSize * sizeof(u32))) {
dprintk(KERN_ERR "SCXLNXDeviceIoctl(%p): "
"Cannot access ioctl parameter %p\n",
file, (void *) ioctl_param);
nResult = -EFAULT;
goto exit;
}
pConn = SCXLNXConnFromFile(file);
BUG_ON(pConn == NULL);
/*
* The answer memory space address is in the nOperationID field
*/
pUserAnswer = (void *) sMessage.sHeader.nOperationID;
atomic_inc(&(pConn->nPendingOpCounter));
dprintk(KERN_WARNING "SCXLNXDeviceIoctl(%p): "
"Sending message type 0x%08x\n",
file, sMessage.sHeader.nMessageType);
switch (sMessage.sHeader.nMessageType) {
case SCX_MESSAGE_TYPE_OPEN_CLIENT_SESSION:
nResult = SCXLNXConnOpenClientSession(pConn,
&sMessage, &sAnswer);
break;
case SCX_MESSAGE_TYPE_CLOSE_CLIENT_SESSION:
nResult = SCXLNXConnCloseClientSession(pConn,
&sMessage, &sAnswer);
break;
case SCX_MESSAGE_TYPE_REGISTER_SHARED_MEMORY:
nResult = SCXLNXConnRegisterSharedMemory(pConn,
&sMessage, &sAnswer);
break;
case SCX_MESSAGE_TYPE_RELEASE_SHARED_MEMORY:
nResult = SCXLNXConnReleaseSharedMemory(pConn,
&sMessage, &sAnswer);
break;
case SCX_MESSAGE_TYPE_INVOKE_CLIENT_COMMAND:
nResult = SCXLNXConnInvokeClientCommand(pConn,
&sMessage, &sAnswer);
break;
case SCX_MESSAGE_TYPE_CANCEL_CLIENT_COMMAND:
nResult = SCXLNXConnCancelClientCommand(pConn,
&sMessage, &sAnswer);
break;
default:
dprintk(KERN_ERR "SCXLNXDeviceIoctlExchange(%p): "
"Incorrect message type (0x%08x)!\n",
pConn, sMessage.sHeader.nMessageType);
nResult = -EOPNOTSUPP;
break;
}
atomic_dec(&(pConn->nPendingOpCounter));
if (nResult != 0) {
dprintk(KERN_WARNING "SCXLNXDeviceIoctl(%p): "
"Operation returning error code 0x%08x)!\n",
file, nResult);
goto exit;
}
/*
* Copy the answer back to the user space application.
* The driver does not check this field, only copy back to user
* space the data handed over by Secure World
*/
nAnswerSize = sAnswer.sHeader.nMessageSize +
sizeof(struct SCX_ANSWER_HEADER)/sizeof(u32);
if (copy_to_user(pUserAnswer,
&sAnswer, nAnswerSize * sizeof(u32))) {
dprintk(KERN_WARNING "SCXLNXDeviceIoctl(%p): "
"Failed to copy back the full command "
"answer to %p\n", file, pUserAnswer);
nResult = -EFAULT;
goto exit;
}
/* successful completion */
dprintk(KERN_INFO "SCXLNXDeviceIoctl(%p): Success\n", file);
break;
case IOCTL_SCX_GET_DESCRIPTION: {
/* ioctl asking for the version information buffer */
struct SCX_VERSION_INFORMATION_BUFFER *pInfoBuffer;
dprintk(KERN_INFO "IOCTL_SCX_GET_DESCRIPTION:(%p, %u, %p)\n",
file, ioctl_num, (void *) ioctl_param);
pInfoBuffer =
((struct SCX_VERSION_INFORMATION_BUFFER *) ioctl_param);
dprintk(KERN_INFO "IOCTL_SCX_GET_DESCRIPTION1: "
"sDriverDescription=\"%64s\"\n", S_VERSION_STRING);
if (copy_to_user(pInfoBuffer->sDriverDescription,
S_VERSION_STRING,
strlen(S_VERSION_STRING) + 1)) {
dprintk(KERN_ERR "SCXLNXDeviceIoctl(%p): "
"Fail to copy back the driver description "
"to %p\n",
file, pInfoBuffer->sDriverDescription);
nResult = -EFAULT;
goto exit;
}
dprintk(KERN_INFO "IOCTL_SCX_GET_DESCRIPTION2: "
"sSecureWorldDescription=\"%64s\"\n",
SCXLNXCommGetDescription(&g_SCXLNXDevice.sm));
if (copy_to_user(pInfoBuffer->sSecureWorldDescription,
SCXLNXCommGetDescription(&g_SCXLNXDevice.sm),
SCX_DESCRIPTION_BUFFER_LENGTH)) {
dprintk(KERN_WARNING "SCXLNXDeviceIoctl(%p): "
"Failed to copy back the secure world "
"description to %p\n",
file, pInfoBuffer->sSecureWorldDescription);
nResult = -EFAULT;
goto exit;
}
break;
}
default:
dprintk(KERN_ERR "SCXLNXDeviceIoctl(%p): "
"Unknown IOCTL code 0x%08x!\n",
file, ioctl_num);
nResult = -EOPNOTSUPP;
goto exit;
}
exit:
return nResult;
}
/*----------------------------------------------------------------------------*/
static int SCXLNXDeviceShutdown(struct sys_device *sysdev)
{
return SCXLNXCommPowerManagement(&g_SCXLNXDevice.sm,
SCXLNX_POWER_OPERATION_SHUTDOWN);
}
/*----------------------------------------------------------------------------*/
static int SCXLNXDeviceSuspend(struct sys_device *sysdev, pm_message_t state)
{
printk(KERN_INFO "SCXLNXDeviceSuspend: Enter\n");
return SCXLNXCommPowerManagement(&g_SCXLNXDevice.sm,
SCXLNX_POWER_OPERATION_HIBERNATE);
}
/*----------------------------------------------------------------------------*/
static int SCXLNXDeviceResume(struct sys_device *sysdev)
{
return SCXLNXCommPowerManagement(&g_SCXLNXDevice.sm,
SCXLNX_POWER_OPERATION_RESUME);
}
/*----------------------------------------------------------------------------*/
module_init(SCXLNXDeviceRegister);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Trusted Logic S.A.");
Jump to Line
Something went wrong with that request. Please try again.