kernel_samsung_a34x-permissive/security/samsung/dsms/dsms_netlink.c

156 lines
3.6 KiB
C
Raw Normal View History

/*
* Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. 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.
*/
#include <linux/delay.h>
#include <linux/dsms.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/version.h>
#include <net/genetlink.h>
#include "dsms_kernel_api.h"
#include "dsms_netlink.h"
#include "dsms_netlink_protocol.h"
#include "dsms_preboot_buffer.h"
#include "dsms_test.h"
__visible_for_testing int dsms_daemon_callback(struct sk_buff *skb,
struct genl_info *info);
__visible_for_testing atomic_t daemon_ready = ATOMIC_INIT(0);
static struct nla_policy dsms_netlink_policy[DSMS_ATTR_COUNT + 1] = {
[DSMS_VALUE] = { .type = NLA_U64 },
[DSMS_FEATURE_CODE] = { .type = NLA_STRING, .len = FEATURE_CODE_LENGTH + 1},
[DSMS_DETAIL] = { .type = NLA_STRING, .len = MAX_ALLOWED_DETAIL_LENGTH + 1},
[DSMS_DAEMON_READY] = { .type = NLA_U32 },
};
static const struct genl_ops dsms_kernel_ops[] = {
{
.cmd = DSMS_MSG_CMD,
.doit = dsms_daemon_callback,
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 2, 0)
.policy = dsms_netlink_policy,
#endif
},
};
struct genl_multicast_group dsms_group[] = {
{
.name = DSMS_GROUP,
},
};
static struct genl_family dsms_family = {
.name = DSMS_FAMILY,
.version = 1,
.maxattr = DSMS_ATTR_MAX,
.module = THIS_MODULE,
.ops = dsms_kernel_ops,
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 2, 0)
.policy = dsms_netlink_policy,
#endif
.mcgrps = dsms_group,
.n_mcgrps = ARRAY_SIZE(dsms_group),
.n_ops = ARRAY_SIZE(dsms_kernel_ops),
};
int __kunit_init dsms_netlink_init(void)
{
int ret;
ret = genl_register_family(&dsms_family);
if (ret != 0)
DSMS_LOG_ERROR("Netlink register failed: %d.", ret);
return ret;
}
void __kunit_exit dsms_netlink_exit(void)
{
int ret;
ret = genl_unregister_family(&dsms_family);
if (ret != 0)
DSMS_LOG_ERROR("Netlink unregister failed: %d.", ret);
}
__visible_for_testing int dsms_daemon_callback(struct sk_buff *skb,
struct genl_info *info)
{
if (atomic_add_unless(&daemon_ready, 1, 1)) {
DSMS_LOG_DEBUG("Netlink daemon ready.");
wakeup_preboot_sender();
}
return 0;
}
int dsms_daemon_ready(void)
{
return atomic_read(&daemon_ready);
}
int dsms_send_netlink_message(const char *feature_code,
const char *detail,
int64_t value)
{
int ret;
void *msg_head;
size_t detail_len;
struct sk_buff *skb;
DSMS_LOG_DEBUG("Sending netlink message.");
skb = genlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC);
if (skb == NULL) {
DSMS_LOG_ERROR("genlmsg_new error.");
return -ENOMEM;
}
msg_head = genlmsg_put(skb, 0, 0,
&dsms_family, 0, DSMS_MSG_CMD);
if (msg_head == NULL) {
DSMS_LOG_ERROR("genlmsg_put error.");
nlmsg_free(skb);
return -ENOMEM;
}
ret = nla_put(skb, DSMS_VALUE, sizeof(value), &value);
if (ret) {
DSMS_LOG_ERROR("nla_put value error.");
nlmsg_free(skb);
return ret;
}
ret = nla_put(skb, DSMS_FEATURE_CODE,
FEATURE_CODE_LENGTH + 1, feature_code);
if (ret) {
DSMS_LOG_ERROR("nla_put feature error.");
nlmsg_free(skb);
return ret;
}
detail_len = strnlen(detail, MAX_ALLOWED_DETAIL_LENGTH);
ret = nla_put(skb, DSMS_DETAIL, detail_len + 1, detail);
if (ret) {
DSMS_LOG_ERROR("nla_put detail error.");
nlmsg_free(skb);
return ret;
}
genlmsg_end(skb, msg_head);
ret = genlmsg_multicast(&dsms_family, skb, 0, 0, GFP_ATOMIC);
if (ret) {
DSMS_LOG_ERROR("genlmsg_multicast error.");
return ret;
}
return DSMS_SUCCESS;
}