/* * Copyright (c) 2014, STMicroelectronics International N.V. * * 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. */ #include #include #include #include #include #include "tee_core_priv.h" static ssize_t dump_show(struct device *device, struct device_attribute *attr, char *buf) { struct tee *tee = dev_get_drvdata(device); int len; char *tmp_buf; tmp_buf = kzalloc(PAGE_SIZE, GFP_KERNEL); if (!tmp_buf) { printk(KERN_ALERT "%s : Unable to get buf memory\n", __func__); return -ENOMEM; } len = tee_context_dump(tee, tmp_buf, PAGE_SIZE - 128); if (len > 0) len = snprintf(buf, PAGE_SIZE, "%s", tmp_buf); kfree(tmp_buf); return len; } static ssize_t stat_show(struct device *device, struct device_attribute *attr, char *buf) { struct tee *tee = dev_get_drvdata(device); return snprintf(buf, PAGE_SIZE, "%d/%d %d/%d %d/%d %d/%d\n", atomic_read(&tee->refcount), tee->max_refcount, tee->stats[TEE_STATS_CONTEXT_IDX].count, tee->stats[TEE_STATS_CONTEXT_IDX].max, tee->stats[TEE_STATS_SESSION_IDX].count, tee->stats[TEE_STATS_SESSION_IDX].max, tee->stats[TEE_STATS_SHM_IDX].count, tee->stats[TEE_STATS_SHM_IDX].max); } static ssize_t info_show(struct device *device, struct device_attribute *attr, char *buf) { struct tee *tee = dev_get_drvdata(device); return snprintf(buf, PAGE_SIZE, "%s iminor=%d dev=\"%s\" state=%d\n", dev_name(tee->dev), tee->miscdev.minor, dev_name(tee->miscdev.this_device), tee->state); } static ssize_t name_show(struct device *device, struct device_attribute *attr, char *buf) { struct tee *tee = dev_get_drvdata(device); return snprintf(buf, PAGE_SIZE, "%s\n", tee->name); } static ssize_t type_show(struct device *device, struct device_attribute *attr, char *buf) { struct tee *tee = dev_get_drvdata(device); return snprintf(buf, PAGE_SIZE, "%s\n", tee->ops->type); } static ssize_t refcount_show(struct device *device, struct device_attribute *attr, char *buf) { struct tee *tee = dev_get_drvdata(device); return snprintf(buf, PAGE_SIZE, "%d\n", atomic_read(&tee->refcount)); } static ssize_t conf_show(struct device *device, struct device_attribute *attr, char *buf) { struct tee *tee = dev_get_drvdata(device); return snprintf(buf, PAGE_SIZE, "0x%08x\n", tee->conf); } static ssize_t test_show(struct device *device, struct device_attribute *attr, char *buf) { struct tee *tee = dev_get_drvdata(device); return snprintf(buf, PAGE_SIZE, "%08X\n", tee->test); } static ssize_t test_store(struct device *device, struct device_attribute *attr, const char *buf, size_t count) { struct tee *tee = dev_get_drvdata(device); unsigned long val; int status; status = kstrtoul(buf, 0, &val); if (status) return status; if ((tee->conf & TEE_CONF_TEST_MODE) == TEE_CONF_TEST_MODE) tee->test = val; return count; } /* * A state-to-string lookup table, for exposing a human readable state * via sysfs. Always keep in sync with enum tee_state */ static const char *const tee_state_string[] = { "offline", "online", "suspended", "running", "crashed", "invalid", }; static ssize_t tee_show_state(struct device *device, struct device_attribute *attr, char *buf) { struct tee *tee = dev_get_drvdata(device); int state = tee->state > TEE_LAST ? TEE_LAST : tee->state; return snprintf(buf, PAGE_SIZE, "%s (%d)\n", tee_state_string[state], tee->state); } /* * In the following, 0660 is (S_IWUGO | S_IRUGO) */ static struct device_attribute device_attrs[] = { __ATTR_RO(dump), __ATTR_RO(stat), __ATTR_RO(info), __ATTR(test, (0660), test_show, test_store), __ATTR(state, S_IRUGO, tee_show_state, NULL), __ATTR(name, S_IRUGO, name_show, NULL), __ATTR(refcount, S_IRUGO, refcount_show, NULL), __ATTR(type, S_IRUGO, type_show, NULL), __ATTR(conf, S_IRUGO, conf_show, NULL), }; void tee_init_sysfs(struct tee *tee) { int i, error = 0; if (!tee) return; if (dev_get_drvdata(tee->miscdev.this_device) != tee) { dev_err(_DEV(tee), "drvdata is not valid\n"); return; } for (i = 0; i < ARRAY_SIZE(device_attrs); i++) { error = device_create_file(tee->miscdev.this_device, &device_attrs[i]); if (error) break; } if (error) { while (--i >= 0) device_remove_file(tee->miscdev.this_device, &device_attrs[i]); } /* location /sys/class/// -> * /sys/class/misc/teelx00/info */ } void tee_cleanup_sysfs(struct tee *tee) { int i; if (!tee) return; for (i = 0; i < ARRAY_SIZE(device_attrs); i++) device_remove_file(tee->miscdev.this_device, &device_attrs[i]); }