blob: 79948eef4d81146ebb90c5e4c94b41784cdd8424 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2015 Google, Inc.
*/
#include <linux/platform_device.h>
#include <linux/trusty/smcall.h>
#include <linux/trusty/trusty.h>
#include <linux/notifier.h>
#include <linux/scatterlist.h>
#include <linux/slab.h>
#include <linux/mm.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/log2.h>
#include <linux/miscdevice.h>
#include <linux/poll.h>
#include <linux/seq_file.h>
#include <asm/page.h>
#include "trusty-log.h"
/*
* Rationale for the chosen default log buffer size:
* - the log buffer shall contain unthrottled Trusty crash dump.
* - the register list portion of a crash dump is about 1KB
* - the memory-around-registers portion of a crash dump can be up to 12 KB
* - an average size backtrace is about 1 KB
* - average length of non-crash trusty logs during boot is about 85 characters
* - a crash dump with 50 lines of context therefore requires up to 18 KB
* - buffer size needs to be power-of-two number of bytes
* - rounding up to power of two from 18 KB gives 32 KB
* The log size can be adjusted by setting the "trusty_log.log_size" parameter
* on the kernel command line. The specified value will be adjusted as needed.
*/
#define TRUSTY_LOG_DEFAULT_SIZE (32768)
#define TRUSTY_LOG_MIN_SIZE (PAGE_SIZE / 2)
#define TRUSTY_LOG_MAX_SIZE (1 * 1024 * 1024 * 1024)
#define TRUSTY_LINE_BUFFER_SIZE (256)
static size_t log_size_param = TRUSTY_LOG_DEFAULT_SIZE;
static int trusty_log_size_set(const char *val, const struct kernel_param *kp)
{
unsigned long long requested = memparse(val, NULL);
if (requested < TRUSTY_LOG_MIN_SIZE)
requested = TRUSTY_LOG_MIN_SIZE;
if (requested > TRUSTY_LOG_MAX_SIZE)
requested = TRUSTY_LOG_MAX_SIZE;
requested = rounddown_pow_of_two(requested);
log_size_param = requested;
return 0;
}
static int trusty_log_size_get(char *buffer, const struct kernel_param *kp)
{
return scnprintf(buffer, PAGE_SIZE, "%zu\n", log_size_param);
}
module_param_call(log_size, trusty_log_size_set, trusty_log_size_get, NULL,
0644);
/*
* The "log_to_dmesg" parameter can have three values: "never", "always",
* and "until_first_reader". "never" indicates Trusty logs will never be
* copied to the linux kernel log, while "always" indicates they will always
* be copied to the linux kernel log. In both cases Trusty logs can still
* be read from the /dev/trusty-logX virtual file. In the case of "always"
* that may mean logs show up duplicated in logcat.
* The third option, "until_first_reader", copies Trusty logs to the linux
* kernel log, but only until /dev/trusty-logX is first opened. After that
* Trusty logs will no longer be copied to the kernel log and are only
* available from /dev/trusty-logX.
*/
enum log_to_dmesg_options {
NEVER,
ALWAYS,
UNTIL_FIRST_READER
};
static const char * const log_to_dmesg_opt_names[] = {
"never", "always", "until_first_reader"
};
static int log_to_dmesg_param = NEVER;
static int trusty_log_mode_set(const char *val, const struct kernel_param *kp)
{
int i = sysfs_match_string(log_to_dmesg_opt_names, val);
if (i < 0)
return i;
log_to_dmesg_param = i;
return 0;
}
static int trusty_log_mode_get(char *buffer, const struct kernel_param *kp)
{
int i;
/*
* Output buffer is PAGE_SIZE, of which we'll use only 35 bytes,
* so bounds checks are not necessary in the following code.
*/
*buffer = 0;
for (i = 0; i < ARRAY_SIZE(log_to_dmesg_opt_names); i++) {
if (log_to_dmesg_param == i)
strcat(buffer, "[");
strcat(buffer, log_to_dmesg_opt_names[i]);
if (log_to_dmesg_param == i)
strcat(buffer, "]");
strcat(buffer, " ");
}
strcat(buffer, "\n");
return strlen(buffer);
}
module_param_call(log_to_dmesg, trusty_log_mode_set, trusty_log_mode_get, NULL,
0644);
/*
* If we log too much and a UART or other slow source is connected, we can stall
* out another thread which is doing printk.
*
* Trusty crash logs are currently ~16 lines, so 100 should include context and
* the crash most of the time.
*/
static struct ratelimit_state trusty_log_rate_limit =
RATELIMIT_STATE_INIT("trusty_log", 1 * HZ, 100);
module_param_named(log_ratelimit_burst, trusty_log_rate_limit.burst, int, 0644);
module_param_named(log_ratelimit_interval, trusty_log_rate_limit.interval, int,
0644);
/**
* struct trusty_log_sfile - trusty log misc device state
*
* @misc: misc device created for the trusty log virtual file
* @device_name: misc device name following the convention
* "trusty-<name><id>"
*/
struct trusty_log_sfile {
struct miscdevice misc;
char device_name[64];
};
/**
* struct trusty_log_sink_state - trusty log sink state
*
* @get: current read unwrapped index
* @last_successful_next:
* index for the next line after the last successful get
* @trusty_panicked: trusty panic status at the start of the sink interation
* (only used for kernel log sink)
* @sfile: seq_file used for sinking to a virtual file (misc device);
* set to NULL for the kernel log sink.
* @ignore_overflow: ignore_overflow used to coalesce overflow messages and
* avoid reporting an overflow when sinking the oldest
* line to the virtual file (only used for virtual file sink)
*
* A sink state structure is used for both the kernel log sink
* and the virtual device sink.
* An instance of the sink state structure is dynamically created
* for each read iteration of the trusty log virtual file (misc device).
*
*/
struct trusty_log_sink_state {
u32 get;
u32 last_successful_next;
bool trusty_panicked;
/* virtual file sink specific attributes */
struct seq_file *sfile;
bool ignore_overflow;
};
struct trusty_log_state {
struct device *dev;
struct device *trusty_dev;
struct trusty_log_sfile log_sfile;
struct kref refcount;
/*
* This lock is here to ensure only one consumer will read
* from the log ring buffer at a time.
*/
spinlock_t lock;
struct log_rb *log;
struct trusty_log_sink_state klog_sink;
u32 log_num_pages;
struct scatterlist *sg;
trusty_shared_mem_id_t log_pages_shared_mem_id;
struct notifier_block call_notifier;
struct notifier_block panic_notifier;
char line_buffer[TRUSTY_LINE_BUFFER_SIZE];
wait_queue_head_t poll_waiters;
/* this lock protects access to wake_put */
spinlock_t wake_up_lock;
u32 last_wake_put;
bool have_first_reader;
bool registered;
};
static inline u32 u32_add_overflow(u32 a, u32 b)
{
u32 d;
if (check_add_overflow(a, b, &d)) {
/*
* silence the overflow,
* what matters in the log buffer context
* is the casted addition
*/
}
return d;
}
static inline u32 u32_sub_overflow(u32 a, u32 b)
{
u32 d;
if (check_sub_overflow(a, b, &d)) {
/*
* silence the overflow,
* what matters in the log buffer context
* is the casted substraction
*/
}
return d;
}
static int log_read_line(struct trusty_log_state *s, u32 put, u32 get)
{
struct log_rb *log = s->log;
int i;
char c = '\0';
size_t max_to_read =
min_t(size_t,
u32_sub_overflow(put, get),
sizeof(s->line_buffer) - 1);
size_t mask = log->sz - 1;
for (i = 0; i < max_to_read && c != '\n';) {
c = log->data[get & mask];
s->line_buffer[i++] = c;
get = u32_add_overflow(get, 1);
}
s->line_buffer[i] = '\0';
return i;
}
/**
* trusty_log_has_data() - returns true when more data is available to sink
* @s: Current log state.
* @sink: trusty_log_sink_state holding the get index on a given sink
*
* Return: true if data is available.
*/
static bool trusty_log_has_data(struct trusty_log_state *s,
struct trusty_log_sink_state *sink)
{
struct log_rb *log = s->log;
return (log->put != sink->get);
}
/**
* trusty_log_start() - initialize the sink iteration either to kernel log
* or to secondary log_sfile
* @s: Current log state.
* @sink: trusty_log_sink_state holding the get index on a given sink
* @index: Unwrapped ring buffer index from where iteration shall start
*
* Return: 0 if successful, negative error code otherwise
*/
static int trusty_log_start(struct trusty_log_state *s,
struct trusty_log_sink_state *sink,
u32 index)
{
struct log_rb *log;
if (WARN_ON(!s))
return -EINVAL;
log = s->log;
if (WARN_ON(!is_power_of_2(log->sz)))
return -EINVAL;
sink->get = index;
return 0;
}
/**
* trusty_log_show() - sink log entry at current iteration
* @s: Current log state.
* @sink: trusty_log_sink_state holding the get index on a given sink
*/
static void trusty_log_show(struct trusty_log_state *s,
struct trusty_log_sink_state *sink)
{
struct log_rb *log = s->log;
u32 alloc, put, get;
int read_chars;
if (sink->sfile && sink == &s->klog_sink)
dev_warn(s->dev, "klog_sink has seq_file\n");
/*
* For this ring buffer, at any given point, alloc >= put >= get.
* The producer side of the buffer is not locked, so the put and alloc
* pointers must be read in a defined order (put before alloc) so
* that the above condition is maintained. A read barrier is needed
* to make sure the hardware and compiler keep the reads ordered.
*/
get = sink->get;
put = log->put;
/* Make sure that the read of put occurs before the read of log data */
rmb();
/* Read a line from the log */
read_chars = log_read_line(s, put, get);
/* Force the loads from log_read_line to complete. */
rmb();
alloc = log->alloc;
/*
* Discard the line that was just read if the data could
* have been corrupted by the producer.
*/
if (u32_sub_overflow(alloc, get) > log->sz) {
/*
* this condition is acceptable in the case of the sfile sink
* when attempting to read the oldest entry (at alloc-log->sz)
* which may be overrun by a new one when ring buffer write
* index wraps around.
* So the overrun is not reported in case the oldest line
* was being read.
*/
if (sink->sfile) {
if (!sink->ignore_overflow)
seq_puts(sink->sfile, "log overflow.\n");
/* coalesce subsequent contiguous overflows. */
sink->ignore_overflow = true;
} else {
dev_err(s->dev, "log overflow.\n");
}
sink->get = u32_sub_overflow(alloc, log->sz);
return;
}
/* compute next line index */
sink->get = u32_add_overflow(get, read_chars);
/* once a line is valid, ignore_overflow must be disabled */
sink->ignore_overflow = false;
if (sink->sfile) {
seq_printf(sink->sfile, "%s", s->line_buffer);
} else {
if (sink->trusty_panicked ||
__ratelimit(&trusty_log_rate_limit)) {
dev_info(s->dev, "%s", s->line_buffer);
/* next line after last successful get */
sink->last_successful_next = sink->get;
}
}
}
static void *trusty_log_seq_start(struct seq_file *sfile, loff_t *pos)
{
struct trusty_log_sfile *lb;
struct trusty_log_state *s;
struct log_rb *log;
struct trusty_log_sink_state *log_sfile_sink;
u32 index;
int rc;
if (WARN_ON(!pos))
return ERR_PTR(-EINVAL);
lb = sfile->private;
if (WARN_ON(!lb))
return ERR_PTR(-EINVAL);
log_sfile_sink = kzalloc(sizeof(*log_sfile_sink), GFP_KERNEL);
if (!log_sfile_sink)
return ERR_PTR(-ENOMEM);
s = container_of(lb, struct trusty_log_state, log_sfile);
log_sfile_sink->sfile = sfile;
log = s->log;
if (*pos == 0) {
/* start at the oldest line */
index = 0;
if (log->alloc > log->sz)
index = u32_sub_overflow(log->alloc, log->sz);
} else {
/*
* '*pos>0': pos hold the 32bits unwrapped index from where
* to start iterating
*/
index = (u32)*pos;
}
pr_debug("%s start=%u\n", __func__, index);
log_sfile_sink->ignore_overflow = true;
rc = trusty_log_start(s, log_sfile_sink, index);
if (rc < 0)
goto free_sink;
if (!trusty_log_has_data(s, log_sfile_sink))
goto free_sink;
return log_sfile_sink;
free_sink:
pr_debug("%s kfree\n", __func__);
kfree(log_sfile_sink);
return rc < 0 ? ERR_PTR(rc) : NULL;
}
static void *trusty_log_seq_next(struct seq_file *sfile, void *v, loff_t *pos)
{
struct trusty_log_sfile *lb;
struct trusty_log_state *s;
struct trusty_log_sink_state *log_sfile_sink = v;
int rc = 0;
if (WARN_ON(!log_sfile_sink))
return ERR_PTR(-EINVAL);
lb = sfile->private;
if (WARN_ON(!lb)) {
rc = -EINVAL;
goto end_of_iter;
}
s = container_of(lb, struct trusty_log_state, log_sfile);
if (WARN_ON(!pos)) {
rc = -EINVAL;
goto end_of_iter;
}
/*
* When starting a virtual file sink, the start function is invoked
* with a pos argument which value is set to zero.
* Subsequent starts are invoked with pos being set to
* the unwrapped read index (get).
* Upon u32 wraparound, the get index could be reset to zero.
* Thus a msb is used to distinguish the `get` zero value
* from the `start of file` zero value.
*/
*pos = (1UL << 32) + log_sfile_sink->get;
if (!trusty_log_has_data(s, log_sfile_sink))
goto end_of_iter;
return log_sfile_sink;
end_of_iter:
pr_debug("%s kfree\n", __func__);
kfree(log_sfile_sink);
return rc < 0 ? ERR_PTR(rc) : NULL;
}
static void trusty_log_seq_stop(struct seq_file *sfile, void *v)
{
/*
* When iteration completes or on error, the next callback frees
* the sink structure and returns NULL/error-code.
* In that case stop (being invoked with void* v set to the last next
* return value) would be invoked with v == NULL or error code.
* When user space stops the iteration earlier than the end
* (in case of user-space memory allocation limit for example)
* then the stop function receives a non NULL get pointer
* and is in charge or freeing the sink structure.
*/
struct trusty_log_sink_state *log_sfile_sink = v;
/* nothing to do - sink structure already freed */
if (IS_ERR_OR_NULL(log_sfile_sink))
return;
kfree(log_sfile_sink);
pr_debug("%s kfree\n", __func__);
}
static int trusty_log_seq_show(struct seq_file *sfile, void *v)
{
struct trusty_log_sfile *lb;
struct trusty_log_state *s;
struct trusty_log_sink_state *log_sfile_sink = v;
if (WARN_ON(!log_sfile_sink))
return -EINVAL;
lb = sfile->private;
if (WARN_ON(!lb))
return -EINVAL;
s = container_of(lb, struct trusty_log_state, log_sfile);
trusty_log_show(s, log_sfile_sink);
return 0;
}
static void trusty_dump_logs(struct trusty_log_state *s)
{
u32 start;
int rc;
/*
* note: klopg_sink.get and last_successful_next
* initialized to zero by kzalloc
*/
s->klog_sink.trusty_panicked = trusty_get_panic_status(s->trusty_dev);
start = s->klog_sink.trusty_panicked ?
s->klog_sink.last_successful_next :
s->klog_sink.get;
rc = trusty_log_start(s, &s->klog_sink, start);
if (rc < 0)
return;
while (trusty_log_has_data(s, &s->klog_sink))
trusty_log_show(s, &s->klog_sink);
}
static int trusty_log_call_notify(struct notifier_block *nb,
unsigned long action, void *data)
{
struct trusty_log_state *s;
unsigned long flags;
u32 cur_put;
if (action != TRUSTY_CALL_RETURNED)
return NOTIFY_DONE;
s = container_of(nb, struct trusty_log_state, call_notifier);
spin_lock_irqsave(&s->wake_up_lock, flags);
cur_put = s->log->put;
if (cur_put != s->last_wake_put) {
s->last_wake_put = cur_put;
wake_up_all(&s->poll_waiters);
}
spin_unlock_irqrestore(&s->wake_up_lock, flags);
if (log_to_dmesg_param == ALWAYS ||
(log_to_dmesg_param == UNTIL_FIRST_READER &&
!s->have_first_reader)) {
spin_lock_irqsave(&s->lock, flags);
trusty_dump_logs(s);
spin_unlock_irqrestore(&s->lock, flags);
}
return NOTIFY_OK;
}
static int trusty_log_panic_notify(struct notifier_block *nb,
unsigned long action, void *data)
{
struct trusty_log_state *s;
/*
* Don't grab the spin lock to hold up the panic notifier, even
* though this is racy.
*/
s = container_of(nb, struct trusty_log_state, panic_notifier);
dev_info(s->dev, "panic notifier - trusty version %s",
trusty_version_str_get(s->trusty_dev));
trusty_dump_logs(s);
return NOTIFY_OK;
}
const struct seq_operations trusty_log_seq_ops = {
.start = trusty_log_seq_start,
.stop = trusty_log_seq_stop,
.next = trusty_log_seq_next,
.show = trusty_log_seq_show,
};
static int trusty_log_sfile_dev_open(struct inode *inode, struct file *file)
{
struct trusty_log_sfile *ls;
struct trusty_log_state *s;
struct seq_file *sfile;
int rc;
/*
* file->private_data contains a pointer to the misc_device struct
* passed to misc_register()
*/
if (WARN_ON(!file->private_data))
return -EINVAL;
ls = container_of(file->private_data, struct trusty_log_sfile, misc);
/*
* seq_open uses file->private_data to store the seq_file associated
* with the struct file, but it must be NULL when seq_open is called
*/
file->private_data = NULL;
rc = seq_open(file, &trusty_log_seq_ops);
if (rc < 0)
return rc;
sfile = file->private_data;
if (WARN_ON(!sfile))
return -EINVAL;
sfile->private = ls;
s = container_of(ls, struct trusty_log_state, log_sfile);
s->have_first_reader = true;
kref_get(&s->refcount);
return 0;
}
static unsigned int trusty_log_sfile_dev_poll(struct file *filp,
struct poll_table_struct *wait)
{
struct seq_file *sfile;
struct trusty_log_sfile *lb;
struct trusty_log_state *s;
struct log_rb *log;
/*
* trusty_log_sfile_dev_open() pointed filp->private_data to a
* seq_file, and that seq_file->private to the trusty_log_sfile
* field of a trusty_log_state
*/
sfile = filp->private_data;
lb = sfile->private;
s = container_of(lb, struct trusty_log_state, log_sfile);
if (!s->registered) {
dev_err(s->dev, "invalid poll fd\n");
return -EINVAL;
}
poll_wait(filp, &s->poll_waiters, wait);
log = s->log;
/*
* Userspace has read up to sfile->index so far. Update klog_sink
* to indicate that, so that we don't end up dumping the entire
* Trusty log in case of panic. Only do this when not logging to
* klog_sink, since logging to klog_sink already updates this.
*/
if (log_to_dmesg_param != ALWAYS)
s->klog_sink.last_successful_next = (u32)sfile->index;
if (log->put != (u32)sfile->index) {
/* data ready to read */
return EPOLLIN | EPOLLRDNORM;
}
/* no data available, go to sleep */
return 0;
}
static void trusty_log_cleanup(struct kref *ref);
static int trusty_log_sfile_dev_release(struct inode *inode,
struct file *filp)
{
struct seq_file *sfile;
struct trusty_log_sfile *lb;
struct trusty_log_state *s;
sfile = filp->private_data;
lb = sfile->private;
s = container_of(lb, struct trusty_log_state, log_sfile);
kref_put(&s->refcount, trusty_log_cleanup);
seq_release(inode, filp);
return 0;
}
ssize_t trusty_log_sfile_dev_read(struct file *filp, char __user *buf,
size_t size, loff_t *ppos)
{
struct seq_file *sfile;
struct trusty_log_sfile *lb;
struct trusty_log_state *s;
sfile = filp->private_data;
lb = sfile->private;
s = container_of(lb, struct trusty_log_state, log_sfile);
if (!s->registered) {
dev_err(s->dev, "invalid read fd\n");
return -EINVAL;
}
return seq_read(filp, buf, size, ppos);
}
static const struct file_operations log_sfile_dev_operations = {
.owner = THIS_MODULE,
.open = trusty_log_sfile_dev_open,
.poll = trusty_log_sfile_dev_poll,
.read = trusty_log_sfile_dev_read,
.release = trusty_log_sfile_dev_release,
};
static int trusty_log_sfile_register(struct trusty_log_state *s)
{
int ret;
struct trusty_log_sfile *ls = &s->log_sfile;
if (WARN_ON(!ls))
return -EINVAL;
snprintf(ls->device_name, sizeof(ls->device_name),
"trusty-log%d", s->dev->id);
ls->misc.minor = MISC_DYNAMIC_MINOR;
ls->misc.name = ls->device_name;
ls->misc.fops = &log_sfile_dev_operations;
ret = misc_register(&ls->misc);
if (ret) {
dev_err(s->dev,
"log_sfile error while doing misc_register ret=%d\n",
ret);
return ret;
}
s->registered = true;
dev_info(s->dev, "/dev/%s registered\n",
ls->device_name);
return 0;
}
static void trusty_log_sfile_unregister(struct trusty_log_state *s)
{
struct trusty_log_sfile *ls = &s->log_sfile;
misc_deregister(&ls->misc);
if (s->dev) {
dev_info(s->dev, "/dev/%s unregistered\n",
ls->misc.name);
s->registered = false;
}
}
static bool trusty_supports_logging(struct device *device)
{
int result;
result = trusty_std_call32(device, SMC_SC_SHARED_LOG_VERSION,
TRUSTY_LOG_API_VERSION, 0, 0);
if (result == SM_ERR_UNDEFINED_SMC) {
dev_info(device, "trusty-log not supported on secure side.\n");
return false;
} else if (result < 0) {
dev_err(device,
"trusty std call (SMC_SC_SHARED_LOG_VERSION) failed: %d\n",
result);
return false;
}
if (result != TRUSTY_LOG_API_VERSION) {
dev_info(device, "unsupported api version: %d, supported: %d\n",
result, TRUSTY_LOG_API_VERSION);
return false;
}
return true;
}
static int trusty_log_init(struct platform_device *pdev)
{
struct trusty_log_state *s;
struct scatterlist *sg;
unsigned char *mem;
int i;
int result;
trusty_shared_mem_id_t mem_id;
int log_size;
s = kzalloc(sizeof(*s), GFP_KERNEL);
if (!s) {
result = -ENOMEM;
goto error_alloc_state;
}
spin_lock_init(&s->lock);
s->dev = &pdev->dev;
s->trusty_dev = s->dev->parent;
s->log_num_pages = DIV_ROUND_UP(log_size_param + sizeof(struct log_rb),
PAGE_SIZE);
s->sg = kcalloc(s->log_num_pages, sizeof(*s->sg), GFP_KERNEL);
if (!s->sg) {
result = -ENOMEM;
goto error_alloc_sg;
}
log_size = s->log_num_pages * PAGE_SIZE;
mem = vzalloc(log_size);
if (!mem) {
result = -ENOMEM;
goto error_alloc_log;
}
s->log = (struct log_rb *)mem;
sg_init_table(s->sg, s->log_num_pages);
for_each_sg(s->sg, sg, s->log_num_pages, i) {
struct page *pg = vmalloc_to_page(mem + (i * PAGE_SIZE));
if (!pg) {
result = -ENOMEM;
goto err_share_memory;
}
sg_set_page(sg, pg, PAGE_SIZE, 0);
}
/*
* This will fail for Trusty api version < TRUSTY_API_VERSION_MEM_OBJ
* if s->log_num_pages > 1
* Use trusty_share_memory_compat instead of trusty_share_memory in case
* s->log_num_pages == 1 and api version < TRUSTY_API_VERSION_MEM_OBJ,
* In that case SMC_SC_SHARED_LOG_ADD expects a different value than
* what trusty_share_memory returns
*/
result = trusty_share_memory_compat(s->trusty_dev, &mem_id, s->sg,
s->log_num_pages, PAGE_KERNEL);
if (result) {
dev_err(s->dev, "trusty_share_memory failed: %d\n", result);
goto err_share_memory;
}
s->log_pages_shared_mem_id = mem_id;
result = trusty_std_call32(s->trusty_dev,
SMC_SC_SHARED_LOG_ADD,
(u32)(mem_id), (u32)(mem_id >> 32),
log_size);
if (result < 0) {
dev_err(s->dev,
"trusty std call (SMC_SC_SHARED_LOG_ADD) failed: %d 0x%llx\n",
result, mem_id);
goto error_std_call;
}
init_waitqueue_head(&s->poll_waiters);
spin_lock_init(&s->wake_up_lock);
s->call_notifier.notifier_call = trusty_log_call_notify;
result = trusty_call_notifier_register(s->trusty_dev,
&s->call_notifier);
if (result < 0) {
dev_err(&pdev->dev,
"failed to register trusty call notifier\n");
goto error_call_notifier;
}
s->panic_notifier.notifier_call = trusty_log_panic_notify;
result = atomic_notifier_chain_register(&panic_notifier_list,
&s->panic_notifier);
if (result < 0) {
dev_err(&pdev->dev,
"failed to register panic notifier\n");
goto error_panic_notifier;
}
result = trusty_log_sfile_register(s);
if (result < 0) {
dev_err(&pdev->dev, "failed to register log_sfile\n");
goto error_log_sfile;
}
kref_init(&s->refcount);
platform_set_drvdata(pdev, s);
return 0;
error_log_sfile:
atomic_notifier_chain_unregister(&panic_notifier_list,
&s->panic_notifier);
error_panic_notifier:
trusty_call_notifier_unregister(s->trusty_dev, &s->call_notifier);
error_call_notifier:
trusty_std_call32(s->trusty_dev, SMC_SC_SHARED_LOG_RM,
(u32)mem_id, (u32)(mem_id >> 32), 0);
error_std_call:
if (WARN_ON(trusty_reclaim_memory(s->trusty_dev, mem_id, s->sg,
s->log_num_pages))) {
dev_err(&pdev->dev, "trusty_revoke_memory failed: %d 0x%llx\n",
result, mem_id);
/*
* It is not safe to free this memory if trusty_revoke_memory
* fails. Leak it in that case.
*/
} else {
err_share_memory:
vfree(s->log);
}
error_alloc_log:
kfree(s->sg);
error_alloc_sg:
kfree(s);
error_alloc_state:
return result;
}
static int trusty_log_probe(struct platform_device *pdev)
{
int rc;
if (!trusty_supports_logging(pdev->dev.parent))
return -ENXIO;
rc = trusty_log_init(pdev);
if (rc && log_size_param > TRUSTY_LOG_MIN_SIZE) {
dev_warn(&pdev->dev, "init failed, retrying with 1-page log\n");
log_size_param = TRUSTY_LOG_MIN_SIZE;
rc = trusty_log_init(pdev);
}
return rc;
}
static int trusty_log_remove(struct platform_device *pdev)
{
struct trusty_log_state *s = platform_get_drvdata(pdev);
trusty_log_sfile_unregister(s);
kref_put(&s->refcount, trusty_log_cleanup);
return 0;
}
static void trusty_log_cleanup(struct kref *ref)
{
int result;
struct trusty_log_state *s;
trusty_shared_mem_id_t mem_id;
s = container_of(ref, struct trusty_log_state, refcount);
dev_info(s->dev, "log_cleanup\n");
mem_id = s->log_pages_shared_mem_id;
atomic_notifier_chain_unregister(&panic_notifier_list,
&s->panic_notifier);
trusty_call_notifier_unregister(s->trusty_dev, &s->call_notifier);
result = trusty_std_call32(s->trusty_dev, SMC_SC_SHARED_LOG_RM,
(u32)mem_id, (u32)(mem_id >> 32), 0);
if (result) {
dev_err(s->dev,
"trusty std call (SMC_SC_SHARED_LOG_RM) failed: %d\n",
result);
}
result = trusty_reclaim_memory(s->trusty_dev, mem_id, s->sg,
s->log_num_pages);
if (WARN_ON(result)) {
dev_err(s->dev,
"trusty failed to remove shared memory: %d\n", result);
} else {
/*
* It is not safe to free this memory if trusty_revoke_memory
* fails. Leak it in that case.
*/
vfree(s->log);
}
kfree(s->sg);
kfree(s);
}
static const struct of_device_id trusty_test_of_match[] = {
{ .compatible = "android,trusty-log-v1", },
{},
};
MODULE_DEVICE_TABLE(trusty, trusty_test_of_match);
static struct platform_driver trusty_log_driver = {
.probe = trusty_log_probe,
.remove = trusty_log_remove,
.driver = {
.name = "trusty-log",
.of_match_table = trusty_test_of_match,
},
};
module_platform_driver(trusty_log_driver);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("Trusty logging driver");