diff --git a/drivers/misc/nvsciipc/Makefile b/drivers/misc/nvsciipc/Makefile index 8b2eae28..7bc3e620 100644 --- a/drivers/misc/nvsciipc/Makefile +++ b/drivers/misc/nvsciipc/Makefile @@ -1,10 +1,9 @@ # SPDX-License-Identifier: GPL-2.0 -# Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# NOTE: Do not change or add anything in this makefile. -# The source code and makefile rules are copied from the -# kernel/nvidia/drivers/misc/nvsciipc. This file is just place- -# holder for empty makefile to avoid any build issue when copy -# is not done from command line and building the tree independent -# of source copy. +# +# Makefile for NvSciIpc driver +# + +obj-m += nvsciipc.o diff --git a/drivers/misc/nvsciipc/README b/drivers/misc/nvsciipc/README new file mode 100644 index 00000000..b765b2ec --- /dev/null +++ b/drivers/misc/nvsciipc/README @@ -0,0 +1,56 @@ +Build NvSciIpc KMD for DRIVE x86 +================================ + +1) install kernel header package +sudo apt-get install linux-headers-`uname -r` + +check version of desktop using "uname -r" command. +kernel version must be 5.4.0+. + +2) link common header files (NVIDIA internal dev only) +export NVSCIIPC_INCLUDE=${PWD}/../../../include +ln -s ${NVSCIIPC_INCLUDE}/linux ./ +ln -s ${NVSCIIPC_INCLUDE}/uapi ./ + +3) build +make -C /lib/modules/`uname -r`/build M=${PWD} CONFIG_TEGRA_OOT_MODULE=m modules + +4) install NvSciIpc KMD +sudo make -C /lib/modules/`uname -r`/build M=${PWD} CONFIG_TEGRA_OOT_MODULE=m modules_install +sudo depmod -a + +nvsciipc.ko will be installed in /lib/modules/`uname -r`/extra/nvsciipc.ko + +[NOTE] If kernel module installation is failed by missing signing key, follow steps below + +** create x509.genkey +echo -e "[ req ] \n\ +default_bits = 4096 \n\ +distinguished_name = req_distinguished_name \n\ +prompt = no \n\ +x509_extensions = myexts \n\ +\n\ +[ req_distinguished_name ] \n\ +CN = Modules \n\ +\n\ +[ myexts ] \n\ +basicConstraints=critical,CA:FALSE \n\ +keyUsage=digitalSignature \n\ +subjectKeyIdentifier=hash \n\ +authorityKeyIdentifier=keyid" > x509.genkey + +** generate signing key +openssl req -new -nodes -utf8 -sha512 -days 36500 -batch -x509 -config x509.genkey -outform DER -out signing_key.x509 -keyout signing_key.pem + +** move signing key to kernel module folder of desktop +sudo mv signing_key.* /lib/modules/`uname -r`/build/certs/ + +5) clean +make -C /lib/modules/`uname -r`/build M=${PWD} CONFIG_TEGRA_OOT_MODULE=m clean + +6) load NvSciIpc KMD +You can load NvSciIpc KMD during desktop boot. +sudo vi /etc/modules-load.d/modules.conf +add "nvsciipc" to this file + +For manual loading KMD, do "sudo insmod nvsciipc.ko" diff --git a/drivers/misc/nvsciipc/nvsciipc.c b/drivers/misc/nvsciipc/nvsciipc.c new file mode 100644 index 00000000..51474c04 --- /dev/null +++ b/drivers/misc/nvsciipc/nvsciipc.c @@ -0,0 +1,820 @@ +/* + * Copyright (c) 2019-2022, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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. + */ + +/* + * This is NvSciIpc kernel driver. At present its only use is to support + * secure buffer sharing use case across processes. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "nvsciipc.h" + +/* enable it to debug auth API via ioctl. + * enable LINUX_DEBUG_KMD_API in test_nvsciipc_nvmap tool either. + */ +//#define DEBUG_AUTH_API + +DEFINE_MUTEX(nvsciipc_mutex); + +static struct platform_device *nvsciipc_pdev; +static struct nvsciipc *ctx; +static int32_t s_guestid = -1; + +NvSciError NvSciIpcEndpointGetAuthToken(NvSciIpcEndpoint handle, + NvSciIpcEndpointAuthToken *authToken) +{ + INFO("Not supported in KMD, but in userspace library\n"); + + return NvSciError_NotSupported; +} +EXPORT_SYMBOL(NvSciIpcEndpointGetAuthToken); + +NvSciError NvSciIpcEndpointGetVuid(NvSciIpcEndpoint handle, + NvSciIpcEndpointVuid *vuid) +{ + INFO("Not supported in KMD, but in userspace library\n"); + + return NvSciError_NotSupported; +} +EXPORT_SYMBOL(NvSciIpcEndpointGetVuid); + +NvSciError NvSciIpcEndpointValidateAuthTokenLinuxCurrent( + NvSciIpcEndpointAuthToken authToken, + NvSciIpcEndpointVuid *localUserVuid) +{ + struct fd f; + struct file *filp; + int i, ret; + char node[NVSCIIPC_MAX_EP_NAME+16]; + + if ((ctx == NULL) || (ctx->set_db_f != true)) { + ERR("not initialized\n"); + return NvSciError_NotInitialized; + } + + f = fdget((int)authToken); + if (!f.file) { + ERR("invalid auth token\n"); + return NvSciError_BadParameter; + } + filp = f.file; + + for (i = 0; i < ctx->num_eps; i++) { + ret = snprintf(node, sizeof(node), "%s%d", + ctx->db[i]->dev_name, ctx->db[i]->id); + + if ((ret < 0) || (ret >= sizeof(node))) + continue; + + /* compare node name itself only (w/o directory) */ + if (!strncmp(filp->f_path.dentry->d_name.name, node, ret)) { + *localUserVuid = ctx->db[i]->vuid; + break; + } + } + + if (i == ctx->num_eps) { + fdput(f); + ERR("wrong auth token passed\n"); + return NvSciError_BadParameter; + } + + fdput(f); + + return NvSciError_Success; +} +EXPORT_SYMBOL(NvSciIpcEndpointValidateAuthTokenLinuxCurrent); + +NvSciError NvSciIpcEndpointMapVuid(NvSciIpcEndpointVuid localUserVuid, + NvSciIpcTopoId *peerTopoId, NvSciIpcEndpointVuid *peerUserVuid) +{ + uint32_t backend = NVSCIIPC_BACKEND_UNKNOWN; + struct nvsciipc_config_entry *entry; + int i; + NvSciError ret; + + if ((peerTopoId == NULL) || (peerUserVuid == NULL)) { + ERR("Invalid parameter\n"); + return NvSciError_BadParameter; + } + + if ((ctx == NULL) || (ctx->set_db_f != true)) { + ERR("not initialized\n"); + return NvSciError_NotInitialized; + } + + for (i = 0; i < ctx->num_eps; i++) { + if (ctx->db[i]->vuid == localUserVuid) { + backend = ctx->db[i]->backend; + entry = ctx->db[i]; + break; + } + } + + if (i == ctx->num_eps) { + ERR("wrong localUserVuid passed\n"); + return NvSciError_BadParameter; + } + + switch (backend) { + case NVSCIIPC_BACKEND_ITC: + case NVSCIIPC_BACKEND_IPC: + peerTopoId->SocId = NVSCIIPC_SELF_SOCID; + peerTopoId->VmId = NVSCIIPC_SELF_VMID; + *peerUserVuid = (localUserVuid ^ 1UL); + ret = NvSciError_Success; + break; +#if !defined(__x86_64__) + case NVSCIIPC_BACKEND_IVC: + { + union nvsciipc_vuid_64 vuid64; + + peerTopoId->SocId = NVSCIIPC_SELF_SOCID; + peerTopoId->VmId = entry->peer_vmid; + vuid64.value = entry->vuid; + vuid64.bit.vmid = entry->peer_vmid; + *peerUserVuid = vuid64.value; + + ret = NvSciError_Success; + } + break; +#endif /* __x86_64__ */ + default: + ret = NvSciError_NotSupported; + break; + } + + return ret; +} +EXPORT_SYMBOL(NvSciIpcEndpointMapVuid); + +static int nvsciipc_dev_open(struct inode *inode, struct file *filp) +{ + struct nvsciipc *ctx = container_of(inode->i_cdev, + struct nvsciipc, cdev); + + filp->private_data = ctx; + + return 0; +} + +static void nvsciipc_free_db(struct nvsciipc *ctx) +{ + int i; + + if ((ctx->num_eps != 0) && (ctx->set_db_f == true)) { + for (i = 0; i < ctx->num_eps; i++) + kfree(ctx->db[i]); + + kfree(ctx->db); + } + + ctx->num_eps = 0; +} + +static int nvsciipc_dev_release(struct inode *inode, struct file *filp) +{ + filp->private_data = NULL; + + return 0; +} + +#if defined(DEBUG_AUTH_API) +static int nvsciipc_ioctl_validate_auth_token(struct nvsciipc *ctx, + unsigned int cmd, unsigned long arg) +{ + struct nvsciipc_validate_auth_token op; + NvSciError err; + int32_t ret = 0; + + if ((ctx->num_eps == 0) || (ctx->set_db_f != true)) { + ERR("need to set endpoint database first\n"); + ret = -EPERM; + goto exit; + } + + if (copy_from_user(&op, (void __user *)arg, _IOC_SIZE(cmd))) { + ERR("%s : copy_from_user failed\n", __func__); + ret = -EFAULT; + goto exit; + } + + err = NvSciIpcEndpointValidateAuthTokenLinuxCurrent(op.auth_token, + &op.local_vuid); + if (err != NvSciError_Success) { + ERR("%s : 0x%x\n", __func__, err); + ret = -EINVAL; + goto exit; + } + + if (copy_to_user((void __user *)arg, &op, _IOC_SIZE(cmd))) { + ERR("%s : copy_to_user failed\n", __func__); + ret = -EFAULT; + goto exit; + } + +exit: + return ret; +} + +static int nvsciipc_ioctl_map_vuid(struct nvsciipc *ctx, unsigned int cmd, + unsigned long arg) +{ + struct nvsciipc_map_vuid op; + NvSciError err; + int32_t ret = 0; + + if ((ctx->num_eps == 0) || (ctx->set_db_f != true)) { + ERR("need to set endpoint database first\n"); + ret = -EPERM; + goto exit; + } + + if (copy_from_user(&op, (void __user *)arg, _IOC_SIZE(cmd))) { + ERR("%s : copy_from_user failed\n", __func__); + ret = -EFAULT; + goto exit; + } + + err = NvSciIpcEndpointMapVuid(op.vuid, (NvSciIpcTopoId *)&op.peer_topoid, + &op.peer_vuid); + if (err != NvSciError_Success) { + ERR("%s : 0x%x\n", __func__, err); + ret = -EINVAL; + goto exit; + } + + if (copy_to_user((void __user *)arg, &op, _IOC_SIZE(cmd))) { + ERR("%s : copy_to_user failed\n", __func__); + ret = -EFAULT; + goto exit; + } + +exit: + return ret; +} +#endif /* DEBUG_AUTH_API */ + +static int nvsciipc_ioctl_get_db_by_name(struct nvsciipc *ctx, unsigned int cmd, + unsigned long arg) +{ + struct nvsciipc_get_db_by_name get_db; + int i; + + if ((ctx->num_eps == 0) || (ctx->set_db_f != true)) { + ERR("need to set endpoint database first\n"); + return -EPERM; + } + + if (copy_from_user(&get_db, (void __user *)arg, _IOC_SIZE(cmd))) { + ERR("%s : copy_from_user failed\n", __func__); + return -EFAULT; + } + + /* read operation */ + for (i = 0; i < ctx->num_eps; i++) { + if (!strncmp(get_db.ep_name, ctx->db[i]->ep_name, + NVSCIIPC_MAX_EP_NAME)) { + get_db.entry = *ctx->db[i]; + get_db.idx = i; + break; + } + } + + if (i == ctx->num_eps) { + return -ENOENT; + } else if (copy_to_user((void __user *)arg, &get_db, + _IOC_SIZE(cmd))) { + ERR("%s : copy_to_user failed\n", __func__); + return -EFAULT; + } + + return 0; +} + +static int nvsciipc_ioctl_get_db_by_vuid(struct nvsciipc *ctx, unsigned int cmd, + unsigned long arg) +{ + struct nvsciipc_get_db_by_vuid get_db; + int i; + + if ((ctx->num_eps == 0) || (ctx->set_db_f != true)) { + ERR("need to set endpoint database first\n"); + return -EPERM; + } + + if (copy_from_user(&get_db, (void __user *)arg, _IOC_SIZE(cmd))) { + ERR("%s : copy_from_user failed\n", __func__); + return -EFAULT; + } + + /* read operation */ + for (i = 0; i < ctx->num_eps; i++) { + if (get_db.vuid == ctx->db[i]->vuid) { + get_db.entry = *ctx->db[i]; + get_db.idx = i; + break; + } + } + + if (i == ctx->num_eps) { + return -ENOENT; + } else if (copy_to_user((void __user *)arg, &get_db, + _IOC_SIZE(cmd))) { + ERR("%s : copy_to_user failed\n", __func__); + return -EFAULT; + } + + return 0; +} + +static int nvsciipc_ioctl_get_vuid(struct nvsciipc *ctx, unsigned int cmd, + unsigned long arg) +{ + struct nvsciipc_get_vuid get_vuid; + int i; + + if ((ctx->num_eps == 0) || (ctx->set_db_f != true)) { + ERR("need to set endpoint database first\n"); + return -EPERM; + } + + if (copy_from_user(&get_vuid, (void __user *)arg, _IOC_SIZE(cmd))) { + ERR("%s : copy_from_user failed\n", __func__); + return -EFAULT; + } + + /* read operation */ + for (i = 0; i < ctx->num_eps; i++) { + if (!strncmp(get_vuid.ep_name, ctx->db[i]->ep_name, + NVSCIIPC_MAX_EP_NAME)) { + get_vuid.vuid = ctx->db[i]->vuid; + break; + } + } + + if (i == ctx->num_eps) { + return -ENOENT; + } else if (copy_to_user((void __user *)arg, &get_vuid, + _IOC_SIZE(cmd))) { + ERR("%s : copy_to_user failed\n", __func__); + return -EFAULT; + } + + return 0; +} + +static int nvsciipc_ioctl_set_db(struct nvsciipc *ctx, unsigned int cmd, + unsigned long arg) +{ + struct nvsciipc_db user_db; + struct nvsciipc_config_entry **entry_ptr; + int ret = 0; + int i; + + /* check root user */ + if (current_cred()->uid.val != 0) { + ERR("no permission to set db\n"); + return -EPERM; + } + + if ((ctx->num_eps != 0) || (ctx->set_db_f == true)) { + ERR("nvsciipc db is set already\n"); + return -EPERM; + } + + if (copy_from_user(&user_db, (void __user *)arg, _IOC_SIZE(cmd))) { + ERR("copying user db failed\n"); + return -EFAULT; + } + + if (user_db.num_eps <= 0) { + ERR("invalid value passed for num_eps\n"); + return -EINVAL; + } + + ctx->num_eps = user_db.num_eps; + + entry_ptr = (struct nvsciipc_config_entry **) + kzalloc(ctx->num_eps * sizeof(struct nvsciipc_config_entry *), + GFP_KERNEL); + + if (entry_ptr == NULL) { + ERR("memory allocation for entry_ptr failed\n"); + ret = -EFAULT; + goto ptr_error; + } + + ret = copy_from_user(entry_ptr, (void __user *)user_db.entry, + ctx->num_eps * sizeof(struct nvsciipc_config_entry *)); + if (ret < 0) { + ERR("copying entry ptr failed\n"); + ret = -EFAULT; + goto ptr_error; + } + + ctx->db = (struct nvsciipc_config_entry **) + kzalloc(ctx->num_eps * sizeof(struct nvsciipc_config_entry *), + GFP_KERNEL); + + if (ctx->db == NULL) { + ERR("memory allocation for ctx->db failed\n"); + ret = -EFAULT; + goto ptr_error; + } + + for (i = 0; i < ctx->num_eps; i++) { + ctx->db[i] = (struct nvsciipc_config_entry *) + kzalloc(sizeof(struct nvsciipc_config_entry), + GFP_KERNEL); + + if (ctx->db[i] == NULL) { + ERR("memory allocation for ctx->db[%d] failed\n", i); + ret = -EFAULT; + goto ptr_error; + } + + ret = copy_from_user(ctx->db[i], (void __user *)entry_ptr[i], + sizeof(struct nvsciipc_config_entry)); + if (ret < 0) { + ERR("copying config entry failed\n"); + ret = -EFAULT; + goto ptr_error; + } + } + + if (s_guestid != -1) { + struct nvsciipc_config_entry *entry; + union nvsciipc_vuid_64 vuid64; + + for (i = 0; i < ctx->num_eps; i++) { + entry = ctx->db[i]; + + /* update vmid field of vuid */ + vuid64.value = entry->vuid; + vuid64.bit.vmid = s_guestid; + entry->vuid = vuid64.value; + + /* fill peer vmid */ + if (entry->backend == NVSCIIPC_BACKEND_IVC) + /* Sometimes it fails to find vmid due to bad configuration + * in PCT but it is not error. Hence ignore result + */ + (void)ivc_cdev_get_peer_vmid(entry->id, &entry->peer_vmid); + } + } + + kfree(entry_ptr); + + ctx->set_db_f = true; + + return ret; + +ptr_error: + if (ctx->db != NULL) { + for (i = 0; i < ctx->num_eps; i++) { + if (ctx->db[i] != NULL) { + memset(ctx->db[i], 0, sizeof(struct nvsciipc_config_entry)); + kfree(ctx->db[i]); + } + } + + kfree(ctx->db); + ctx->db = NULL; + } + + if (entry_ptr != NULL) + kfree(entry_ptr); + + ctx->num_eps = 0; + + return ret; +} + +static int nvsciipc_ioctl_get_dbsize(struct nvsciipc *ctx, unsigned int cmd, + unsigned long arg) +{ + int32_t ret = 0; + + if (ctx->set_db_f != true) { + ERR("need to set endpoint database first\n"); + ret = -EPERM; + goto exit; + } + + if (copy_to_user((void __user *)arg, (void *)&ctx->num_eps, + _IOC_SIZE(cmd))) { + ERR("%s : copy_to_user failed\n", __func__); + ret = -EFAULT; + goto exit; + } + + DBG("%s : entry count: %d\n", __func__, ctx->num_eps); + +exit: + return ret; +} + +static long nvsciipc_dev_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg) +{ + struct nvsciipc *ctx = filp->private_data; + long ret = 0; + + if (_IOC_TYPE(cmd) != NVSCIIPC_IOCTL_MAGIC) { + ERR("%s: not a nvsciipc ioctl\n", __func__); + ret = -ENOTTY; + goto exit; + } + + if (_IOC_NR(cmd) > NVSCIIPC_IOCTL_NUMBER_MAX) { + ERR("%s: wrong nvsciipc ioctl cmd: 0x%x\n", __func__, cmd); + ret = -ENOTTY; + goto exit; + } + + switch (cmd) { + case NVSCIIPC_IOCTL_SET_DB: + mutex_lock(&nvsciipc_mutex); + ret = nvsciipc_ioctl_set_db(ctx, cmd, arg); + mutex_unlock(&nvsciipc_mutex); + break; + case NVSCIIPC_IOCTL_GET_VUID: + ret = nvsciipc_ioctl_get_vuid(ctx, cmd, arg); + break; + case NVSCIIPC_IOCTL_GET_DB_BY_NAME: + ret = nvsciipc_ioctl_get_db_by_name(ctx, cmd, arg); + break; + case NVSCIIPC_IOCTL_GET_DB_BY_VUID: + ret = nvsciipc_ioctl_get_db_by_vuid(ctx, cmd, arg); + break; + case NVSCIIPC_IOCTL_GET_DB_SIZE: + ret = nvsciipc_ioctl_get_dbsize(ctx, cmd, arg); + break; +#if defined(DEBUG_AUTH_API) + case NVSCIIPC_IOCTL_VALIDATE_AUTH_TOKEN: + ret = nvsciipc_ioctl_validate_auth_token(ctx, cmd, arg); + break; + case NVSCIIPC_IOCTL_MAP_VUID: + ret = nvsciipc_ioctl_map_vuid(ctx, cmd, arg); + break; +#endif /* DEBUG_AUTH_API */ + case NVSCIIPC_IOCTL_GET_VMID: + if (copy_to_user((void __user *) arg, &s_guestid, + sizeof(s_guestid))) { + ret = -EFAULT; + } + break; + default: + ERR("unrecognised ioctl cmd: 0x%x\n", cmd); + ret = -ENOTTY; + break; + } + +exit: + return ret; +} + +static ssize_t nvsciipc_dbg_read(struct file *filp, char __user *buf, + size_t count, loff_t *f_pos) +{ + struct nvsciipc *ctx = filp->private_data; + int i; + + /* check root user */ + if (current_cred()->uid.val != 0) { + ERR("no permission to read db\n"); + return -EPERM; + } + + if (ctx->set_db_f != true) { + ERR("need to set endpoint database first\n"); + return -EPERM; + } + + for (i = 0; i < ctx->num_eps; i++) { + INFO("EP[%03d]: ep_name: %s, dev_name: %s, backend: %u, nframes: %u, " + "frame_size: %u, id: %u\n", i, + ctx->db[i]->ep_name, + ctx->db[i]->dev_name, + ctx->db[i]->backend, + ctx->db[i]->nframes, + ctx->db[i]->frame_size, + ctx->db[i]->id); + } + + return 0; +} + +static const struct file_operations nvsciipc_fops = { + .owner = THIS_MODULE, + .open = nvsciipc_dev_open, + .release = nvsciipc_dev_release, + .unlocked_ioctl = nvsciipc_dev_ioctl, + .llseek = no_llseek, + .read = nvsciipc_dbg_read, +}; + +static int nvsciipc_probe(struct platform_device *pdev) +{ + int ret = 0; + + if (pdev == NULL) { + ERR("invalid platform device\n"); + ret = -EINVAL; + goto error; + } + + ctx = devm_kzalloc(&pdev->dev, sizeof(struct nvsciipc), GFP_KERNEL); + if (ctx == NULL) { + ERR("devm_kzalloc failed for nvsciipc\n"); + ret = -ENOMEM; + goto error; + } + ctx->set_db_f = false; + + ctx->dev = &(pdev->dev); + platform_set_drvdata(pdev, ctx); + + ctx->nvsciipc_class = class_create(THIS_MODULE, MODULE_NAME); + if (IS_ERR(ctx->nvsciipc_class)) { + ERR("failed to create class: %ld\n", + PTR_ERR(ctx->nvsciipc_class)); + ret = PTR_ERR(ctx->nvsciipc_class); + goto error; + } + + ret = alloc_chrdev_region(&(ctx->dev_t), 0, 1, MODULE_NAME); + if (ret != 0) { + ERR("alloc_chrdev_region() failed\n"); + goto error; + } + + ctx->dev_t = MKDEV(MAJOR(ctx->dev_t), 0); + cdev_init(&ctx->cdev, &nvsciipc_fops); + ctx->cdev.owner = THIS_MODULE; + + ret = cdev_add(&(ctx->cdev), ctx->dev_t, 1); + if (ret != 0) { + ERR("cdev_add() failed\n"); + goto error; + } + + if (snprintf(ctx->device_name, (MAX_NAME_SIZE - 1), "%s", MODULE_NAME) < 0) { + pr_err("snprintf() failed\n"); + ret = -ENOMEM; + goto error; + } + + ctx->device = device_create(ctx->nvsciipc_class, NULL, + ctx->dev_t, ctx, + ctx->device_name, 0); + if (IS_ERR(ctx->device)) { + ret = PTR_ERR(ctx->device); + ERR("device_create() failed\n"); + goto error; + } + dev_set_drvdata(ctx->device, ctx); + + if (is_tegra_hypervisor_mode()) { + ret = hyp_read_gid(&s_guestid); + if (ret != 0) { + ERR("Failed to read guest id\n"); + goto error; + } + INFO("guestid: %d\n", s_guestid); + } + + INFO("loaded module\n"); + + return ret; + +error: + nvsciipc_cleanup(ctx); + + return ret; +} + +static void nvsciipc_cleanup(struct nvsciipc *ctx) +{ + if (ctx == NULL) + return; + + nvsciipc_free_db(ctx); + + if (ctx->nvsciipc_class && ctx->dev_t) + device_destroy(ctx->nvsciipc_class, ctx->dev_t); + + if (ctx->device != NULL) { + cdev_del(&ctx->cdev); + ctx->device = NULL; + } + + if (ctx->dev_t) { + unregister_chrdev_region(ctx->dev_t, 1); + ctx->dev_t = 0; + } + + if (ctx->nvsciipc_class) { + class_destroy(ctx->nvsciipc_class); + ctx->nvsciipc_class = NULL; + } + + devm_kfree(ctx->dev, ctx); + ctx = NULL; +} + +static int nvsciipc_remove(struct platform_device *pdev) +{ + struct nvsciipc *ctx = NULL; + + if (pdev == NULL) { + ERR("%s: pdev is NULL\n", __func__); + goto exit; + } + + ctx = (struct nvsciipc *)platform_get_drvdata(pdev); + if (ctx == NULL) { + ERR("%s: ctx is NULL\n", __func__); + goto exit; + } + + nvsciipc_cleanup(ctx); + +exit: + INFO("Unloaded module\n"); + + return 0; +} + +static struct platform_driver nvsciipc_driver = { + .probe = nvsciipc_probe, + .remove = nvsciipc_remove, + .driver = { + .name = MODULE_NAME, + }, +}; + +static int __init nvsciipc_module_init(void) +{ + int ret; + + ret = platform_driver_register(&nvsciipc_driver); + if (ret) { + ERR("%s: platform_driver_register: %d\n", __func__, ret); + return ret; + } + + INFO("platform_device_register_simple+++\n"); + nvsciipc_pdev = platform_device_register_simple(MODULE_NAME, -1, + NULL, 0); + if (IS_ERR(nvsciipc_pdev)) { + ERR("%s: platform_device_register_simple\n", __func__); + platform_driver_unregister(&nvsciipc_driver); + return PTR_ERR(nvsciipc_pdev); + } + + return 0; +} + +static void __exit nvsciipc_module_deinit(void) +{ + // calls nvsciipc_remove internally + platform_device_unregister(nvsciipc_pdev); + + platform_driver_unregister(&nvsciipc_driver); +} + +module_init(nvsciipc_module_init); +module_exit(nvsciipc_module_deinit); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Nvidia Corporation"); diff --git a/drivers/misc/nvsciipc/nvsciipc.h b/drivers/misc/nvsciipc/nvsciipc.h new file mode 100644 index 00000000..d1f40ac4 --- /dev/null +++ b/drivers/misc/nvsciipc/nvsciipc.h @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2019-2022, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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. + */ + +#ifndef __NVSCIIPC_KERNEL_H__ +#define __NVSCIIPC_KERNEL_H__ + +#include +#include +#include + +#define ERR(...) pr_err("nvsciipc: " __VA_ARGS__) +#define INFO(...) pr_info("nvsciipc: " __VA_ARGS__) +#define DBG(...) pr_debug("nvsciipc: " __VA_ARGS__) + +#define MODULE_NAME "nvsciipc" +#define MAX_NAME_SIZE 64 + +#define NVSCIIPC_BACKEND_ITC 0U +#define NVSCIIPC_BACKEND_IPC 1U +#define NVSCIIPC_BACKEND_IVC 2U +#define NVSCIIPC_BACKEND_C2C_PCIE 3U +#define NVSCIIPC_BACKEND_C2C_NPM 4U +#define NVSCIIPC_BACKEND_UNKNOWN 0xFFFFFFFFU + +struct nvsciipc { + struct device *dev; + + dev_t dev_t; + struct class *nvsciipc_class; + struct cdev cdev; + struct device *device; + char device_name[MAX_NAME_SIZE]; + + int num_eps; + struct nvsciipc_config_entry **db; + volatile bool set_db_f; +}; + +struct vuid_bitfield_64 { + uint64_t index : 16; + uint64_t type : 4; + uint64_t vmid : 8; + uint64_t socid : 28; + uint64_t reserved : 8; +}; + +union nvsciipc_vuid_64 { + uint64_t value; + struct vuid_bitfield_64 bit; +}; + +/***********************************************************************/ +/********************* Functions declaration ***************************/ +/***********************************************************************/ + +static void nvsciipc_cleanup(struct nvsciipc *ctx); + +static int nvsciipc_dev_open(struct inode *inode, struct file *filp); +static int nvsciipc_dev_release(struct inode *inode, struct file *filp); +static long nvsciipc_dev_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg); +static int nvsciipc_ioctl_get_vuid(struct nvsciipc *ctx, unsigned int cmd, + unsigned long arg); +static int nvsciipc_ioctl_set_db(struct nvsciipc *ctx, unsigned int cmd, + unsigned long arg); + +#endif /* __NVSCIIPC_KERNEL_H__ */ diff --git a/kernel-src-files-copy-list.txt b/kernel-src-files-copy-list.txt index 28263ab6..55ca5e23 100644 --- a/kernel-src-files-copy-list.txt +++ b/kernel-src-files-copy-list.txt @@ -1,5 +1,4 @@ #files/directory for the nvsciipc module -nvidia/drivers/misc/nvsciipc drivers/misc nvidia/include/linux/nvscierror.h include/linux/nvscierror.h nvidia/include/linux/nvsciipc_interface.h include/linux/nvsciipc_interface.h nvidia/include/uapi/linux/nvsciipc_ioctl.h include/uapi/linux/nvsciipc_ioctl.h