diff options
Diffstat (limited to 'security/tf_driver/tf_device.c')
-rw-r--r-- | security/tf_driver/tf_device.c | 827 |
1 files changed, 827 insertions, 0 deletions
diff --git a/security/tf_driver/tf_device.c b/security/tf_driver/tf_device.c new file mode 100644 index 000000000000..47ff39e6561d --- /dev/null +++ b/security/tf_driver/tf_device.c @@ -0,0 +1,827 @@ +/** + * 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 <linux/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 "tf_protocol.h" +#include "tf_defs.h" +#include "tf_util.h" +#include "tf_conn.h" +#include "tf_comm.h" +#ifdef CONFIG_TF_ZEBRA +#include <plat/cpu.h> +#include "tf_zebra.h" +#endif +#ifdef CONFIG_TF_DRIVER_CRYPTO_FIPS +#include "tf_crypto.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 tf_device_register(void); + + +/* + * Implements the device Open callback. + */ +static int tf_device_open( + struct inode *inode, + struct file *file); + + +/* + * Implements the device Release callback. + */ +static int tf_device_release( + struct inode *inode, + struct file *file); + + +/* + * Implements the device ioctl callback. + */ +static long tf_device_ioctl( + struct file *file, + unsigned int ioctl_num, + unsigned long ioctl_param); + + +/* + * Implements the device shutdown callback. + */ +static int tf_device_shutdown( + struct sys_device *sysdev); + + +/* + * Implements the device suspend callback. + */ +static int tf_device_suspend( + struct sys_device *sysdev, + pm_message_t state); + + +/* + * Implements the device resume callback. + */ +static int tf_device_resume( + 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_TF_DRIVER_DEBUG_SUPPORT +unsigned tf_debug_level = UINT_MAX; +module_param_named(debug, tf_debug_level, uint, 0644); +#endif + +#ifdef CONFIG_TF_DRIVER_CRYPTO_FIPS +char *tf_integrity_hmac_sha256_expected_value; +module_param_named(hmac_sha256, tf_integrity_hmac_sha256_expected_value, + charp, 0444); + +#ifdef CONFIG_TF_DRIVER_FAULT_INJECTION +unsigned tf_fault_injection_mask; +module_param_named(fault, tf_fault_injection_mask, uint, 0644); +#endif + +int tf_self_test_blkcipher_align; +module_param_named(post_align, tf_self_test_blkcipher_align, int, 0644); +int tf_self_test_blkcipher_use_vmalloc; +module_param_named(post_vmalloc, tf_self_test_blkcipher_use_vmalloc, int, 0644); +#endif + +#ifdef CONFIG_ANDROID +static struct class *tf_class; +#endif + +/* + * Interfaces the system device with the kernel. + */ +struct sys_device g_tf_sysdev; + +/*---------------------------------------------------------------------------- + * 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_tf_device_file_ops = { + .owner = THIS_MODULE, + .open = tf_device_open, + .release = tf_device_release, + .unlocked_ioctl = tf_device_ioctl, + .llseek = no_llseek, +}; + + +static struct sysdev_class g_tf_device_sys_class = { + .name = TF_DEVICE_BASE_NAME, + .shutdown = tf_device_shutdown, + .suspend = tf_device_suspend, + .resume = tf_device_resume, +}; + +/* The single device supported by this driver */ +static struct tf_device g_tf_dev; + +/*---------------------------------------------------------------------------- + * Implementations + *----------------------------------------------------------------------------*/ + +struct tf_device *tf_get_device(void) +{ + return &g_tf_dev; +} + +/* + * sysfs entries + */ +struct tf_sysfs_entry { + struct attribute attr; + ssize_t (*show)(struct tf_device *, char *); + ssize_t (*store)(struct tf_device *, const char *, size_t); +}; + +/* + * sysfs entry showing allocation stats + */ +static ssize_t info_show(struct tf_device *dev, char *buf) +{ + struct tf_device_stats *dev_stats = &dev->stats; + + return snprintf(buf, PAGE_SIZE, + "stat.memories.allocated: %d\n" + "stat.pages.allocated: %d\n" + "stat.pages.locked: %d\n", + atomic_read(&dev_stats->stat_memories_allocated), + atomic_read(&dev_stats->stat_pages_allocated), + atomic_read(&dev_stats->stat_pages_locked)); +} +static struct tf_sysfs_entry tf_info_entry = __ATTR_RO(info); + +#ifdef CONFIG_TF_ZEBRA +/* + * sysfs entry showing whether secure world is up and running + */ +static ssize_t tf_started_show(struct tf_device *dev, char *buf) +{ + int tf_started = test_bit(TF_COMM_FLAG_PA_AVAILABLE, + &dev->sm.flags); + + return snprintf(buf, PAGE_SIZE, "%s\n", tf_started ? "yes" : "no"); +} +static struct tf_sysfs_entry tf_started_entry = + __ATTR_RO(tf_started); + +static ssize_t workspace_addr_show(struct tf_device *dev, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "0x%08x\n", dev->workspace_addr); +} +static struct tf_sysfs_entry tf_workspace_addr_entry = + __ATTR_RO(workspace_addr); + +static ssize_t workspace_size_show(struct tf_device *dev, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "0x%08x\n", dev->workspace_size); +} +static struct tf_sysfs_entry tf_workspace_size_entry = + __ATTR_RO(workspace_size); +#endif + +static ssize_t tf_attr_show(struct kobject *kobj, struct attribute *attr, + char *page) +{ + struct tf_sysfs_entry *entry = container_of(attr, struct tf_sysfs_entry, + attr); + struct tf_device *dev = container_of(kobj, struct tf_device, kobj); + + if (!entry->show) + return -EIO; + + return entry->show(dev, page); +} + +static ssize_t tf_attr_store(struct kobject *kobj, struct attribute *attr, + const char *page, size_t length) +{ + struct tf_sysfs_entry *entry = container_of(attr, struct tf_sysfs_entry, + attr); + struct tf_device *dev = container_of(kobj, struct tf_device, kobj); + + if (!entry->store) + return -EIO; + + return entry->store(dev, page, length); +} + +static void tf_kobj_release(struct kobject *kobj) {} + +static struct attribute *tf_default_attrs[] = { + &tf_info_entry.attr, +#ifdef CONFIG_TF_ZEBRA + &tf_started_entry.attr, + &tf_workspace_addr_entry.attr, + &tf_workspace_size_entry.attr, +#endif + NULL, +}; +static const struct sysfs_ops tf_sysfs_ops = { + .show = tf_attr_show, + .store = tf_attr_store, +}; +static struct kobj_type tf_ktype = { + .release = tf_kobj_release, + .sysfs_ops = &tf_sysfs_ops, + .default_attrs = tf_default_attrs +}; + +/*----------------------------------------------------------------------------*/ + +#if defined(MODULE) && defined(CONFIG_TF_ZEBRA) +static char *smc_mem; +module_param(smc_mem, charp, S_IRUGO); +#endif + +/* + * First routine called when the kernel module is loaded + */ +static int __init tf_device_register(void) +{ + int error; + struct tf_device *dev = &g_tf_dev; + + dprintk(KERN_INFO "tf_device_register()\n"); + + /* + * Initialize the device + */ + dev->dev_number = MKDEV(device_major_number, + TF_DEVICE_MINOR_NUMBER); + cdev_init(&dev->cdev, &g_tf_device_file_ops); + dev->cdev.owner = THIS_MODULE; + + g_tf_sysdev.id = 0; + g_tf_sysdev.cls = &g_tf_device_sys_class; + + INIT_LIST_HEAD(&dev->connection_list); + spin_lock_init(&dev->connection_list_lock); + +#if defined(MODULE) && defined(CONFIG_TF_ZEBRA) + error = (*tf_comm_early_init)(); + if (error) + goto module_early_init_failed; + + error = tf_device_mshield_init(smc_mem); + if (error) + goto mshield_init_failed; + +#ifdef CONFIG_TF_DRIVER_CRYPTO_FIPS + error = tf_crypto_hmac_module_init(); + if (error) + goto hmac_init_failed; + + error = tf_self_test_register_device(); + if (error) + goto self_test_register_device_failed; +#endif +#endif + + /* register the sysfs object driver stats */ + error = kobject_init_and_add(&dev->kobj, &tf_ktype, NULL, "%s", + TF_DEVICE_BASE_NAME); + if (error) { + printk(KERN_ERR "tf_device_register(): " + "kobject_init_and_add failed (error %d)!\n", error); + kobject_put(&dev->kobj); + goto kobject_init_and_add_failed; + } + + /* + * Register the system device. + */ + + error = sysdev_class_register(&g_tf_device_sys_class); + if (error != 0) { + printk(KERN_ERR "tf_device_register():" + " sysdev_class_register failed (error %d)!\n", + error); + goto sysdev_class_register_failed; + } + + error = sysdev_register(&g_tf_sysdev); + if (error != 0) { + dprintk(KERN_ERR "tf_device_register(): " + "sysdev_register failed (error %d)!\n", + error); + goto sysdev_register_failed; + } + + /* + * Register the char device. + */ + printk(KERN_INFO "Registering char device %s (%u:%u)\n", + TF_DEVICE_BASE_NAME, + MAJOR(dev->dev_number), + MINOR(dev->dev_number)); + error = register_chrdev_region(dev->dev_number, 1, + TF_DEVICE_BASE_NAME); + if (error != 0) { + printk(KERN_ERR "tf_device_register():" + " register_chrdev_region failed (error %d)!\n", + error); + goto register_chrdev_region_failed; + } + + error = cdev_add(&dev->cdev, dev->dev_number, 1); + if (error != 0) { + printk(KERN_ERR "tf_device_register(): " + "cdev_add failed (error %d)!\n", + error); + goto cdev_add_failed; + } + + /* + * Initialize the communication with the Secure World. + */ +#ifdef CONFIG_TF_TRUSTZONE + dev->sm.soft_int_irq = soft_interrupt; +#endif + error = tf_init(&g_tf_dev.sm); + if (error != S_SUCCESS) { + dprintk(KERN_ERR "tf_device_register(): " + "tf_init failed (error %d)!\n", + error); + goto init_failed; + } + +#ifdef CONFIG_TF_DRIVER_CRYPTO_FIPS + error = tf_self_test_post_init(&(dev_stats->kobj)); + /* N.B. error > 0 indicates a POST failure, which will not + prevent the module from loading. */ + if (error < 0) { + dprintk(KERN_ERR "tf_device_register(): " + "tf_self_test_post_vectors failed (error %d)!\n", + error); + goto post_failed; + } +#endif + +#ifdef CONFIG_ANDROID + tf_class = class_create(THIS_MODULE, TF_DEVICE_BASE_NAME); + device_create(tf_class, NULL, + dev->dev_number, + NULL, TF_DEVICE_BASE_NAME); +#endif + +#ifdef CONFIG_TF_ZEBRA + /* + * Initializes the /dev/tf_ctrl device node. + */ + error = tf_ctrl_device_register(); + if (error) + goto ctrl_failed; +#endif + +#ifdef CONFIG_TF_DRIVER_DEBUG_SUPPORT + address_cache_property((unsigned long) &tf_device_register); +#endif + /* + * Successful completion. + */ + + dprintk(KERN_INFO "tf_device_register(): Success\n"); + return 0; + + /* + * Error: undo all operations in the reverse order + */ +#ifdef CONFIG_TF_ZEBRA +ctrl_failed: +#endif +#ifdef CONFIG_TF_DRIVER_CRYPTO_FIPS + tf_self_test_post_exit(); +post_failed: +#endif +init_failed: + cdev_del(&dev->cdev); +cdev_add_failed: + unregister_chrdev_region(dev->dev_number, 1); +register_chrdev_region_failed: + sysdev_unregister(&g_tf_sysdev); +sysdev_register_failed: + sysdev_class_unregister(&g_tf_device_sys_class); +sysdev_class_register_failed: +kobject_init_and_add_failed: + kobject_del(&g_tf_dev.kobj); + +#if defined(MODULE) && defined(CONFIG_TF_ZEBRA) +#ifdef CONFIG_TF_DRIVER_CRYPTO_FIPS + tf_self_test_unregister_device(); +self_test_register_device_failed: + tf_crypto_hmac_module_exit(); +hmac_init_failed: +#endif + tf_device_mshield_exit(); +mshield_init_failed: +module_early_init_failed: +#endif + dprintk(KERN_INFO "tf_device_register(): Failure (error %d)\n", + error); + return error; +} + +/*----------------------------------------------------------------------------*/ + +static int tf_device_open(struct inode *inode, struct file *file) +{ + int error; + struct tf_device *dev = &g_tf_dev; + struct tf_connection *connection = NULL; + + dprintk(KERN_INFO "tf_device_open(%u:%u, %p)\n", + imajor(inode), iminor(inode), file); + + /* Dummy lseek for non-seekable driver */ + error = nonseekable_open(inode, file); + if (error != 0) { + dprintk(KERN_ERR "tf_device_open(%p): " + "nonseekable_open failed (error %d)!\n", + file, error); + goto error; + } + +#ifndef CONFIG_ANDROID + /* + * Check file flags. We only autthorize the O_RDWR access + */ + if (file->f_flags != O_RDWR) { + dprintk(KERN_ERR "tf_device_open(%p): " + "Invalid access mode %u\n", + file, file->f_flags); + error = -EACCES; + goto error; + } +#endif + + /* + * Open a new connection. + */ + + error = tf_open(dev, file, &connection); + if (error != 0) { + dprintk(KERN_ERR "tf_device_open(%p): " + "tf_open failed (error %d)!\n", + file, error); + goto error; + } + + file->private_data = connection; + + /* + * Send the CreateDeviceContext command to the secure + */ + error = tf_create_device_context(connection); + if (error != 0) { + dprintk(KERN_ERR "tf_device_open(%p): " + "tf_create_device_context failed (error %d)!\n", + file, error); + goto error1; + } + + /* + * Successful completion. + */ + + dprintk(KERN_INFO "tf_device_open(%p): Success (connection=%p)\n", + file, connection); + return 0; + + /* + * Error handling. + */ + +error1: + tf_close(connection); +error: + dprintk(KERN_INFO "tf_device_open(%p): Failure (error %d)\n", + file, error); + return error; +} + +/*----------------------------------------------------------------------------*/ + +static int tf_device_release(struct inode *inode, struct file *file) +{ + struct tf_connection *connection; + + dprintk(KERN_INFO "tf_device_release(%u:%u, %p)\n", + imajor(inode), iminor(inode), file); + + connection = tf_conn_from_file(file); + tf_close(connection); + + dprintk(KERN_INFO "tf_device_release(%p): Success\n", file); + return 0; +} + +/*----------------------------------------------------------------------------*/ + +static long tf_device_ioctl(struct file *file, unsigned int ioctl_num, + unsigned long ioctl_param) +{ + int result = S_SUCCESS; + struct tf_connection *connection; + union tf_command command; + struct tf_command_header header; + union tf_answer answer; + u32 command_size; + u32 answer_size; + void *user_answer; + + dprintk(KERN_INFO "tf_device_ioctl(%p, %u, %p)\n", + file, ioctl_num, (void *) ioctl_param); + + switch (ioctl_num) { + case IOCTL_TF_GET_VERSION: + /* ioctl is asking for the driver interface version */ + result = TF_DRIVER_INTERFACE_VERSION; + goto exit; + + case IOCTL_TF_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(&header, + (struct tf_command_header *)ioctl_param, + sizeof(struct tf_command_header))) { + dprintk(KERN_ERR "tf_device_ioctl(%p): " + "Cannot access ioctl parameter %p\n", + file, (void *) ioctl_param); + result = -EFAULT; + goto exit; + } + + /* size in words of u32 */ + command_size = header.message_size + + sizeof(struct tf_command_header)/sizeof(u32); + if (command_size > sizeof(command)/sizeof(u32)) { + dprintk(KERN_ERR "tf_device_ioctl(%p): " + "Buffer overflow: too many bytes to copy %d\n", + file, command_size); + result = -EFAULT; + goto exit; + } + + if (copy_from_user(&command, + (union tf_command *)ioctl_param, + command_size * sizeof(u32))) { + dprintk(KERN_ERR "tf_device_ioctl(%p): " + "Cannot access ioctl parameter %p\n", + file, (void *) ioctl_param); + result = -EFAULT; + goto exit; + } + + connection = tf_conn_from_file(file); + BUG_ON(connection == NULL); + + /* + * The answer memory space address is in the operation_id field + */ + user_answer = (void *) command.header.operation_id; + + atomic_inc(&(connection->pending_op_count)); + + dprintk(KERN_WARNING "tf_device_ioctl(%p): " + "Sending message type 0x%08x\n", + file, command.header.message_type); + + switch (command.header.message_type) { + case TF_MESSAGE_TYPE_OPEN_CLIENT_SESSION: + result = tf_open_client_session(connection, + &command, &answer); + break; + + case TF_MESSAGE_TYPE_CLOSE_CLIENT_SESSION: + result = tf_close_client_session(connection, + &command, &answer); + break; + + case TF_MESSAGE_TYPE_REGISTER_SHARED_MEMORY: + result = tf_register_shared_memory(connection, + &command, &answer); + break; + + case TF_MESSAGE_TYPE_RELEASE_SHARED_MEMORY: + result = tf_release_shared_memory(connection, + &command, &answer); + break; + + case TF_MESSAGE_TYPE_INVOKE_CLIENT_COMMAND: + result = tf_invoke_client_command(connection, + &command, &answer); + break; + + case TF_MESSAGE_TYPE_CANCEL_CLIENT_COMMAND: + result = tf_cancel_client_command(connection, + &command, &answer); + break; + + default: + dprintk(KERN_ERR "tf_device_ioctl(%p): " + "Incorrect message type (0x%08x)!\n", + connection, command.header.message_type); + result = -EOPNOTSUPP; + break; + } + + atomic_dec(&(connection->pending_op_count)); + + if (result != 0) { + dprintk(KERN_WARNING "tf_device_ioctl(%p): " + "Operation returning error code 0x%08x)!\n", + file, result); + 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 + */ + answer_size = answer.header.message_size + + sizeof(struct tf_answer_header)/sizeof(u32); + if (copy_to_user(user_answer, + &answer, answer_size * sizeof(u32))) { + dprintk(KERN_WARNING "tf_device_ioctl(%p): " + "Failed to copy back the full command " + "answer to %p\n", file, user_answer); + result = -EFAULT; + goto exit; + } + + /* successful completion */ + dprintk(KERN_INFO "tf_device_ioctl(%p): Success\n", file); + break; + + case IOCTL_TF_GET_DESCRIPTION: { + /* ioctl asking for the version information buffer */ + struct tf_version_information_buffer *pInfoBuffer; + + dprintk(KERN_INFO "IOCTL_TF_GET_DESCRIPTION:(%p, %u, %p)\n", + file, ioctl_num, (void *) ioctl_param); + + pInfoBuffer = + ((struct tf_version_information_buffer *) ioctl_param); + + dprintk(KERN_INFO "IOCTL_TF_GET_DESCRIPTION1: " + "driver_description=\"%64s\"\n", S_VERSION_STRING); + + if (copy_to_user(pInfoBuffer->driver_description, + S_VERSION_STRING, + strlen(S_VERSION_STRING) + 1)) { + dprintk(KERN_ERR "tf_device_ioctl(%p): " + "Fail to copy back the driver description " + "to %p\n", + file, pInfoBuffer->driver_description); + result = -EFAULT; + goto exit; + } + + dprintk(KERN_INFO "IOCTL_TF_GET_DESCRIPTION2: " + "secure_world_description=\"%64s\"\n", + tf_get_description(&g_tf_dev.sm)); + + if (copy_to_user(pInfoBuffer->secure_world_description, + tf_get_description(&g_tf_dev.sm), + TF_DESCRIPTION_BUFFER_LENGTH)) { + dprintk(KERN_WARNING "tf_device_ioctl(%p): " + "Failed to copy back the secure world " + "description to %p\n", + file, pInfoBuffer->secure_world_description); + result = -EFAULT; + goto exit; + } + break; + } + + default: + dprintk(KERN_ERR "tf_device_ioctl(%p): " + "Unknown IOCTL code 0x%08x!\n", + file, ioctl_num); + result = -EOPNOTSUPP; + goto exit; + } + +exit: + return result; +} + +/*----------------------------------------------------------------------------*/ + +static int tf_device_shutdown(struct sys_device *sysdev) +{ + + return tf_power_management(&g_tf_dev.sm, + TF_POWER_OPERATION_SHUTDOWN); +} + +/*----------------------------------------------------------------------------*/ + +static int tf_device_suspend(struct sys_device *sysdev, pm_message_t state) +{ + dprintk(KERN_INFO "tf_device_suspend: Enter\n"); + return tf_power_management(&g_tf_dev.sm, + TF_POWER_OPERATION_HIBERNATE); +} + + +/*----------------------------------------------------------------------------*/ + +static int tf_device_resume(struct sys_device *sysdev) +{ + return tf_power_management(&g_tf_dev.sm, + TF_POWER_OPERATION_RESUME); +} + + +/*----------------------------------------------------------------------------*/ + +module_init(tf_device_register); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Trusted Logic S.A."); |