blob: fb7a421f53f22f3dc41d202dbc91e188eb667911 [file] [log] [blame]
/*
* FIVE-DSMS integration.
*
* Copyright (C) 2020 Samsung Electronics, Inc.
* Yevgen Kopylov, <y.kopylov@samsung.com>
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* 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 <linux/dsms.h>
#include <linux/workqueue.h>
#include <linux/string.h>
#include <linux/crc16.h>
#include "five.h"
#include "five_dsms.h"
#define MESSAGE_BUFFER_SIZE 600
#define MESSAGE_FIVE_INIT_SIZE 32
#define DEFERRED_TIME (1000 * 300)
#define MAX_FIV1_NUM 16
#define MAX_FIV2_NUM 96
#define FIV3_FIRST 1
#define FIV3_FEW 20
#define FIV3_LOT 100
struct sign_err_event {
char comm[TASK_COMM_LEN];
int result;
int count;
};
static struct five_dsms_report_context {
struct delayed_work work;
char message[MESSAGE_FIVE_INIT_SIZE];
} context;
static DEFINE_SPINLOCK(five_dsms_lock);
static struct sign_err_event sign_err_events[MAX_FIV1_NUM];
static bool fiv3_overflow;
static DECLARE_BITMAP(mask, MAX_FIV2_NUM);
static bool oem_unlocking_state __ro_after_init;
static int __init verifiedboot_state_setup(char *str)
{
static const char unlocked[] = "orange";
oem_unlocking_state = !strncmp(str, unlocked, sizeof(unlocked));
if (oem_unlocking_state)
pr_err("FIVE: Device is unlocked\n");
return 0;
}
__setup("androidboot.verifiedbootstate=", verifiedboot_state_setup);
/*`noinline` is required by DSMS.
* Don't rename this function. File_name/function_name
* is used by DSMS in dsms_whitelist as an access rule.
*/
noinline void five_dsms_msg(const char *tag, const char *msg)
{
int ret;
ret = dsms_send_message(tag, msg, 0);
if (ret)
pr_err("FIVE: unable to send dsms message(result:%d)\n", ret);
}
static void five_dsms_init_report(struct work_struct *in_data)
{
struct five_dsms_report_context *context = container_of(in_data,
struct five_dsms_report_context, work.work);
five_dsms_msg("FIV0", context->message);
}
void five_dsms_sign_err(const char *app, int result)
{
int i, current_count;
struct sign_err_event *same_event = NULL;
char dsms_msg[MESSAGE_BUFFER_SIZE];
bool send_overflow = false;
spin_lock(&five_dsms_lock);
for (i = 0; i < MAX_FIV1_NUM; i++) {
if (sign_err_events[i].count == 0) {
same_event = &sign_err_events[i];
current_count = ++same_event->count;
strlcpy(same_event->comm, app, TASK_COMM_LEN);
same_event->result = result;
break;
} else if (sign_err_events[i].result == result &&
!strncmp(sign_err_events[i].comm, app, TASK_COMM_LEN)) {
same_event = &sign_err_events[i];
current_count = ++same_event->count;
break;
}
}
if (!same_event && !fiv3_overflow) {
fiv3_overflow = true;
send_overflow = true;
}
spin_unlock(&five_dsms_lock);
if (same_event) {
switch (current_count) {
case FIV3_FIRST:
case FIV3_FEW:
case FIV3_LOT:
snprintf(dsms_msg, MESSAGE_BUFFER_SIZE,
"%s res = %d count = %d", same_event->comm,
same_event->result, current_count);
five_dsms_msg("FIV3", dsms_msg);
}
} else if (unlikely(send_overflow)) {
five_dsms_msg("FIV3", "data buffer overflow");
}
}
void five_dsms_reset_integrity(const char *task_name, int result,
const char *file_name)
{
char dsms_msg[MESSAGE_BUFFER_SIZE];
unsigned short crc;
int msg_size;
bool sent = true;
if (oem_unlocking_state)
return;
msg_size = snprintf(dsms_msg, MESSAGE_BUFFER_SIZE, "%s|%d|%s",
task_name, result, file_name ? kbasename(file_name) : "");
crc = crc16(0, dsms_msg, msg_size) % MAX_FIV2_NUM;
spin_lock(&five_dsms_lock);
if (!test_bit(crc, mask)) {
sent = false;
set_bit(crc, mask);
}
spin_unlock(&five_dsms_lock);
if (!sent)
five_dsms_msg("FIV2", dsms_msg);
return;
}
void five_dsms_init(const char *version, int result)
{
snprintf(context.message, MESSAGE_FIVE_INIT_SIZE, "%s", version);
INIT_DELAYED_WORK(&context.work, five_dsms_init_report);
schedule_delayed_work(&context.work, msecs_to_jiffies(DEFERRED_TIME));
}