/** * 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 #include #include #include #include #include #include #include #include #include #ifdef CONFIG_ANDROID #include #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 #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 void tf_device_shutdown(void); /* * Implements the device suspend callback. */ static int tf_device_suspend(void); /* * Implements the device resume callback. */ static void tf_device_resume(void); /*--------------------------------------------------------------------------- * 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 /*---------------------------------------------------------------------------- * 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 syscore_ops g_tf_device_syscore_ops = { .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; 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. */ register_syscore_ops(&g_tf_device_syscore_ops); /* * 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(&(g_tf_dev.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: unregister_syscore_ops(&g_tf_device_syscore_ops); 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 void tf_device_shutdown(void) { if (0 > tf_power_management(&g_tf_dev.sm, TF_POWER_OPERATION_SHUTDOWN)) dprintk(KERN_ERR "tf_device_shutdown failing\n"); } /*----------------------------------------------------------------------------*/ static int tf_device_suspend(void) { dprintk(KERN_INFO "tf_device_suspend: Enter\n"); return tf_power_management(&g_tf_dev.sm, TF_POWER_OPERATION_HIBERNATE); } /*----------------------------------------------------------------------------*/ static void tf_device_resume(void) { if (0 > tf_power_management(&g_tf_dev.sm, TF_POWER_OPERATION_RESUME)) dprintk(KERN_ERR "tf_device_resume failing\n"); } /*----------------------------------------------------------------------------*/ module_init(tf_device_register); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Trusted Logic S.A.");