diff options
| -rw-r--r-- | drivers/virt/tegra/hvc_sysfs.c | 146 | ||||
| -rw-r--r-- | drivers/virt/tegra/ivc-cdev.c | 468 | ||||
| -rw-r--r-- | drivers/virt/tegra/tegra_hv.c | 785 | ||||
| -rw-r--r-- | drivers/virt/tegra/tegra_hv.h | 18 | ||||
| -rw-r--r-- | drivers/virt/tegra/userspace_ivc_mempool.c | 394 | ||||
| -rw-r--r-- | include/linux/nvhvivc_mempool_ioctl.h | 30 |
6 files changed, 1841 insertions, 0 deletions
diff --git a/drivers/virt/tegra/hvc_sysfs.c b/drivers/virt/tegra/hvc_sysfs.c new file mode 100644 index 000000000..9d8e9074f --- /dev/null +++ b/drivers/virt/tegra/hvc_sysfs.c | |||
| @@ -0,0 +1,146 @@ | |||
| 1 | /* | ||
| 2 | * drivers/virt/tegra/hvc_sysfs.c | ||
| 3 | * | ||
| 4 | * Copyright (c) 2016-2017, NVIDIA CORPORATION. All rights reserved. | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU General Public License as published by | ||
| 8 | * the Free Software Foundation; either version 2 of the License, or | ||
| 9 | * (at your option) any later version. | ||
| 10 | * | ||
| 11 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 14 | * more details. | ||
| 15 | * | ||
| 16 | */ | ||
| 17 | |||
| 18 | #include <linux/errno.h> | ||
| 19 | #include <soc/tegra/chip-id.h> | ||
| 20 | #include <linux/kobject.h> | ||
| 21 | #include <linux/sysfs.h> | ||
| 22 | #include <linux/mm.h> | ||
| 23 | #include <linux/bug.h> | ||
| 24 | #include <linux/io.h> | ||
| 25 | #include <soc/tegra/virt/syscalls.h> | ||
| 26 | |||
| 27 | #define TEGRA_HV_ERR(...) pr_err("hvc_sysfs: " __VA_ARGS__) | ||
| 28 | #define TEGRA_HV_INFO(...) pr_info("hvc_sysfs: " __VA_ARGS__) | ||
| 29 | |||
| 30 | |||
| 31 | /* | ||
| 32 | * This file implements a hypervisor control driver that can be accessed | ||
| 33 | * from user-space via the sysfs interface. Currently, the only supported | ||
| 34 | * use case is retrieval of the HV trace log when it is available. | ||
| 35 | */ | ||
| 36 | |||
| 37 | struct hyp_shared_memory_info { | ||
| 38 | const char *node_name; | ||
| 39 | struct bin_attribute attr; | ||
| 40 | uint64_t ipa; | ||
| 41 | unsigned long size; | ||
| 42 | }; | ||
| 43 | |||
| 44 | enum HYP_SHM_ID { | ||
| 45 | HYP_SHM_ID_LOG, | ||
| 46 | HYP_SHM_ID_PCT, | ||
| 47 | |||
| 48 | HYP_SHM_ID_NUM | ||
| 49 | }; | ||
| 50 | |||
| 51 | static struct hyp_shared_memory_info hyp_shared_memory_attrs[HYP_SHM_ID_NUM]; | ||
| 52 | |||
| 53 | |||
| 54 | |||
| 55 | /* Map the HV trace buffer to the calling user process */ | ||
| 56 | static int hvc_sysfs_mmap(struct file *fp, struct kobject *ko, | ||
| 57 | struct bin_attribute *attr, struct vm_area_struct *vma) | ||
| 58 | { | ||
| 59 | struct hyp_shared_memory_info *hyp_shm_info = | ||
| 60 | container_of(attr, struct hyp_shared_memory_info, attr); | ||
| 61 | |||
| 62 | if ((hyp_shm_info->ipa == 0) || (hyp_shm_info->size == 0)) | ||
| 63 | return -EINVAL; | ||
| 64 | |||
| 65 | |||
| 66 | if ((vma->vm_end - vma->vm_start) != attr->size) | ||
| 67 | return -EINVAL; | ||
| 68 | |||
| 69 | return remap_pfn_range( | ||
| 70 | vma, | ||
| 71 | vma->vm_start, | ||
| 72 | hyp_shm_info->ipa >> PAGE_SHIFT, | ||
| 73 | hyp_shm_info->size, | ||
| 74 | vma->vm_page_prot); | ||
| 75 | } | ||
| 76 | |||
| 77 | /* Discover availability and placement of the HV trace buffer */ | ||
| 78 | static int hvc_create_sysfs( | ||
| 79 | struct kobject *kobj, | ||
| 80 | struct hyp_shared_memory_info *hyp_shm_info) | ||
| 81 | { | ||
| 82 | sysfs_bin_attr_init((struct bin_attribute *)&hyp_shm_info->attr); | ||
| 83 | |||
| 84 | hyp_shm_info->attr.attr.name = hyp_shm_info->node_name; | ||
| 85 | hyp_shm_info->attr.attr.mode = S_IRUSR | S_IRGRP | S_IROTH; | ||
| 86 | hyp_shm_info->attr.mmap = hvc_sysfs_mmap; | ||
| 87 | hyp_shm_info->attr.size = (size_t)hyp_shm_info->size; | ||
| 88 | |||
| 89 | if ((hyp_shm_info->ipa == 0) || (hyp_shm_info->size == 0)) | ||
| 90 | return -EINVAL; | ||
| 91 | |||
| 92 | return sysfs_create_bin_file(kobj, &hyp_shm_info->attr); | ||
| 93 | } | ||
| 94 | |||
| 95 | /* Set up all relevant hypervisor control nodes */ | ||
| 96 | static int __init hvc_sysfs_register(void) | ||
| 97 | { | ||
| 98 | struct kobject *kobj; | ||
| 99 | int ret; | ||
| 100 | uint64_t ipa; | ||
| 101 | struct hyp_info_page *info; | ||
| 102 | |||
| 103 | if (is_tegra_hypervisor_mode() == false) { | ||
| 104 | TEGRA_HV_INFO("hypervisor is not present\n"); | ||
| 105 | return -EPERM; | ||
| 106 | } | ||
| 107 | |||
| 108 | kobj = kobject_create_and_add("hvc", NULL); | ||
| 109 | if (kobj == NULL) { | ||
| 110 | TEGRA_HV_INFO("failed to add kobject\n"); | ||
| 111 | return -ENOMEM; | ||
| 112 | } | ||
| 113 | |||
| 114 | if (hyp_read_hyp_info(&ipa) != 0) | ||
| 115 | return -EINVAL; | ||
| 116 | |||
| 117 | info = (struct hyp_info_page *)ioremap(ipa, sizeof(*info)); | ||
| 118 | if (info == NULL) | ||
| 119 | return -EFAULT; | ||
| 120 | |||
| 121 | hyp_shared_memory_attrs[HYP_SHM_ID_LOG].ipa = info->log_ipa; | ||
| 122 | hyp_shared_memory_attrs[HYP_SHM_ID_LOG].size = (size_t)info->log_size; | ||
| 123 | hyp_shared_memory_attrs[HYP_SHM_ID_LOG].node_name = "log"; | ||
| 124 | |||
| 125 | ret = hvc_create_sysfs(kobj, &hyp_shared_memory_attrs[HYP_SHM_ID_LOG]); | ||
| 126 | if (ret == 0) | ||
| 127 | TEGRA_HV_INFO("log is available\n"); | ||
| 128 | else | ||
| 129 | TEGRA_HV_INFO("log is unavailable\n"); | ||
| 130 | |||
| 131 | hyp_shared_memory_attrs[HYP_SHM_ID_PCT].ipa = info->pct_ipa; | ||
| 132 | hyp_shared_memory_attrs[HYP_SHM_ID_PCT].size = (size_t)info->pct_size; | ||
| 133 | hyp_shared_memory_attrs[HYP_SHM_ID_PCT].node_name = "pct"; | ||
| 134 | |||
| 135 | ret = hvc_create_sysfs(kobj, &hyp_shared_memory_attrs[HYP_SHM_ID_PCT]); | ||
| 136 | if (ret == 0) | ||
| 137 | TEGRA_HV_INFO("pct is available\n"); | ||
| 138 | else | ||
| 139 | TEGRA_HV_INFO("pct is unavailable\n"); | ||
| 140 | |||
| 141 | iounmap(info); | ||
| 142 | |||
| 143 | return 0; | ||
| 144 | } | ||
| 145 | |||
| 146 | late_initcall(hvc_sysfs_register); | ||
diff --git a/drivers/virt/tegra/ivc-cdev.c b/drivers/virt/tegra/ivc-cdev.c new file mode 100644 index 000000000..f75a0474d --- /dev/null +++ b/drivers/virt/tegra/ivc-cdev.c | |||
| @@ -0,0 +1,468 @@ | |||
| 1 | /* | ||
| 2 | * IVC character device driver | ||
| 3 | * | ||
| 4 | * Copyright (C) 2014-2018, NVIDIA CORPORATION. All rights reserved. | ||
| 5 | * | ||
| 6 | * This file is licensed under the terms of the GNU General Public License | ||
| 7 | * version 2. This program is licensed "as is" without any warranty of any | ||
| 8 | * kind, whether express or implied. | ||
| 9 | * | ||
| 10 | */ | ||
| 11 | |||
| 12 | #include <linux/tegra-ivc.h> | ||
| 13 | #include <linux/tegra-ivc-instance.h> | ||
| 14 | #include <linux/module.h> | ||
| 15 | #include <linux/init.h> | ||
| 16 | #include <linux/workqueue.h> | ||
| 17 | #include <linux/cdev.h> | ||
| 18 | #include <linux/poll.h> | ||
| 19 | #include <linux/interrupt.h> | ||
| 20 | #include <linux/device.h> | ||
| 21 | #include <linux/slab.h> | ||
| 22 | #include <linux/sched.h> | ||
| 23 | |||
| 24 | #include "tegra_hv.h" | ||
| 25 | |||
| 26 | #define ERR(...) pr_err("ivc: " __VA_ARGS__) | ||
| 27 | #define DBG(...) pr_debug("ivc: " __VA_ARGS__) | ||
| 28 | |||
| 29 | struct ivc_dev { | ||
| 30 | int minor; | ||
| 31 | dev_t dev; | ||
| 32 | struct cdev cdev; | ||
| 33 | struct device *device; | ||
| 34 | char name[32]; | ||
| 35 | |||
| 36 | /* channel configuration */ | ||
| 37 | struct tegra_hv_ivc_cookie *ivck; | ||
| 38 | const struct tegra_hv_queue_data *qd; | ||
| 39 | |||
| 40 | /* File mode */ | ||
| 41 | wait_queue_head_t wq; | ||
| 42 | /* | ||
| 43 | * Lock for synchronizing access to the IVC channel between the threaded | ||
| 44 | * IRQ handler's notification processing and file ops. | ||
| 45 | */ | ||
| 46 | struct mutex file_lock; | ||
| 47 | }; | ||
| 48 | |||
| 49 | static dev_t ivc_dev; | ||
| 50 | static const struct ivc_info_page *info; | ||
| 51 | |||
| 52 | static irqreturn_t ivc_dev_handler(int irq, void *data) | ||
| 53 | { | ||
| 54 | struct ivc_dev *ivc = data; | ||
| 55 | |||
| 56 | BUG_ON(!ivc->ivck); | ||
| 57 | |||
| 58 | mutex_lock(&ivc->file_lock); | ||
| 59 | tegra_ivc_channel_notified(tegra_hv_ivc_convert_co | ||
