| /* |
| * 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)); |
| } |
| |