From ed5b67aa73ee067f9c7b62055ad3bcea792955d3 Mon Sep 17 00:00:00 2001 From: Manish Bhardwaj Date: Fri, 6 Dec 2024 17:42:41 +0000 Subject: [PATCH] nvidia-oot: add support for nvlog buffer Using this patch we have done below changes :- - exposed buf_count, buf_size, region_size to userspace via sysfs node to read meta data related to nvlog buffers - mmap nvlog buffer to the userspace Bug 4985113 Change-Id: If5824f7199d63364847eecffd0f490213d2acda0 Signed-off-by: Manish Bhardwaj Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/3268716 Reviewed-by: Suresh Venkatachalam GVS: buildbot_gerritrpt Reviewed-by: Sumeet Gupta --- drivers/virt/tegra/hvc_sysfs.c | 253 +++++++++++++++++++++++++++--- include/soc/tegra/virt/syscalls.h | 66 +++++++- 2 files changed, 293 insertions(+), 26 deletions(-) diff --git a/drivers/virt/tegra/hvc_sysfs.c b/drivers/virt/tegra/hvc_sysfs.c index 67ed37f5..87e945ee 100644 --- a/drivers/virt/tegra/hvc_sysfs.c +++ b/drivers/virt/tegra/hvc_sysfs.c @@ -1,18 +1,13 @@ /* - * SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. - * SPDX-License-Identifier: GPL-2.0-only + * SPDX-FileCopyrightText: Copyright (c) 2022-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: LicenseRef-NvidiaProprietary * - * 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. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . + * NVIDIA CORPORATION, its affiliates and licensors retain all intellectual + * property and proprietary rights in and to this material, related + * documentation and any modifications thereto. Any use, reproduction, + * disclosure or distribution of this material and related documentation + * without an express license agreement from NVIDIA CORPORATION or + * its affiliates is strictly prohibited. */ #include @@ -34,12 +29,27 @@ /* * This file implements a hypervisor control driver that can be accessed - * from user-space via the sysfs interface. Currently, the only supported - * use case is retrieval of the HV trace log when it is available. + * from user-space via the sysfs interface. Currently, supported use case are + * retrieval of the HV trace log when it is available and mapping nvlog buffers + * to user space. */ #define MAX_NAME_SIZE 50 +struct nvlog_shmem_info { + struct bin_attribute attr; + struct bin_attribute region_size_attr; + struct bin_attribute buf_size_attr; + struct bin_attribute buf_count_attr; + struct kobject *kobj; + char node_name[MAX_NAME_SIZE]; + uint64_t ipa; + uint64_t region_size; + uint64_t buf_size; + uint64_t buf_count; +}; +static struct nvlog_shmem_info nvlog_shmem_attrs[MAX_NVLOG_PRODUCERS]; + struct hyp_shared_memory_info { char node_name[MAX_NAME_SIZE]; struct bin_attribute attr; @@ -51,6 +61,181 @@ struct hyp_shared_memory_info { static struct hyp_shared_memory_info hyp_shared_memory_attrs[HYP_SHM_ID_NUM]; +/* Map the HV trace buffer to the calling user process */ +static int nvlog_buffer_mmap(struct file *fp, struct kobject *ko, + struct bin_attribute *attr, struct vm_area_struct *vma) +{ + struct nvlog_shmem_info *info = container_of(attr, struct nvlog_shmem_info, attr); + + if ((info->ipa == 0) || (info->region_size == 0)) + return -EINVAL; + + + if ((vma->vm_end - vma->vm_start) != attr->size) + return -EINVAL; + + return remap_pfn_range( + vma, vma->vm_start, + info->ipa >> PAGE_SHIFT, + info->region_size, vma->vm_page_prot); +} + +static ssize_t nvlog_region_size_read(struct file *fp, struct kobject *ko, + struct bin_attribute *attr, char *buf, loff_t pos, size_t size) +{ + struct nvlog_shmem_info *info = container_of(attr, struct nvlog_shmem_info, region_size_attr); + + snprintf(buf, size, "%llu\n", info->region_size); + return size; +} + +static ssize_t nvlog_buffer_size_read(struct file *fp, struct kobject *ko, + struct bin_attribute *attr, char *buf, loff_t pos, size_t size) +{ + struct nvlog_shmem_info *info = container_of(attr, struct nvlog_shmem_info, buf_size_attr); + + snprintf(buf, size, "%llu\n", info->buf_size); + return size; +} + +static ssize_t nvlog_buffer_count_read(struct file *fp, struct kobject *ko, + struct bin_attribute *attr, char *buf, loff_t pos, size_t size) +{ + struct nvlog_shmem_info *info = container_of(attr, struct nvlog_shmem_info, buf_count_attr); + + snprintf(buf, size, "%llu\n", info->buf_count); + return size; +} + +static int nvlog_create_sysfs_nodes(struct nvlog_shmem_info *info) +{ + int ret = 0; + + if (info == NULL) + return -EINVAL; + + sysfs_bin_attr_init((struct bin_attribute *)&info->attr); + info->attr.attr.name = info->node_name; + info->attr.attr.mode = 0600; + info->attr.size = info->region_size; + info->attr.mmap = nvlog_buffer_mmap; + ret = sysfs_create_bin_file(info->kobj, &info->attr); + if (ret != 0) + goto fail; + + sysfs_bin_attr_init((struct bin_attribute *)&info->region_size_attr); + info->region_size_attr.attr.name = "region_size"; + info->region_size_attr.attr.mode = 0444; + info->region_size_attr.size = 16u; + info->region_size_attr.read = nvlog_region_size_read; + ret = sysfs_create_bin_file(info->kobj, &info->region_size_attr); + if (ret != 0) + goto del_attr; + + sysfs_bin_attr_init((struct bin_attribute *)&info->buf_size_attr); + info->buf_size_attr.attr.name = "buf_size"; + info->buf_size_attr.attr.mode = 0444; + info->buf_size_attr.size = 16u; + info->buf_size_attr.read = nvlog_buffer_size_read; + ret = sysfs_create_bin_file(info->kobj, &info->buf_size_attr); + if (ret != 0) + goto del_region_size; + + sysfs_bin_attr_init((struct bin_attribute *)&info->buf_count_attr); + info->buf_count_attr.attr.name = "buf_count"; + info->buf_count_attr.attr.mode = 0444; + info->buf_count_attr.size = 16u; + info->buf_count_attr.read = nvlog_buffer_count_read; + ret = sysfs_create_bin_file(info->kobj, &info->buf_count_attr); + if (ret != 0) + goto del_buf_size; + + return 0; + +del_buf_size: + sysfs_remove_bin_file(info->kobj, &info->buf_size_attr); +del_region_size: + sysfs_remove_bin_file(info->kobj, &info->region_size_attr); +del_attr: + sysfs_remove_bin_file(info->kobj, &info->attr); +fail: + return ret; +} + +static int hyp_nvlog_buffer_init(void) +{ + struct vm_info_region *info; + struct kobject *parent; + uint64_t ipa, cntr; + char dir_name[MAX_NAME_SIZE]; + int ret; + + parent = kobject_create_and_add("nvlog", NULL); + if (parent == NULL) { + TEGRA_HV_INFO("failed to add kobject\n"); + return -ENOMEM; + } + + if (hyp_read_vm_info(&ipa) != 0) { + ret = -EINVAL; + goto fail; + } + + info = (__force struct vm_info_region *)ioremap(ipa, sizeof(*info)); + if (info == NULL) { + ret = -EFAULT; + goto fail; + } + + for (cntr = 0; cntr < MAX_NVLOG_PRODUCERS; cntr++) { + if (info->nvlog_producers[cntr].ipa != 0U + && info->nvlog_producers[cntr].region_size != 0U) { + nvlog_shmem_attrs[cntr].ipa = info->nvlog_producers[cntr].ipa; + nvlog_shmem_attrs[cntr].region_size = info->nvlog_producers[cntr].region_size; + nvlog_shmem_attrs[cntr].buf_size = info->nvlog_producers[cntr].buf_size; + nvlog_shmem_attrs[cntr].buf_count = info->nvlog_producers[cntr].buf_count; + + ret = snprintf(nvlog_shmem_attrs[cntr].node_name, MAX_NAME_SIZE, "%s", + info->nvlog_producers[cntr].name); + if (ret > 0U) { + nvlog_shmem_attrs[cntr].node_name[ret] = '\0'; + } else { + TEGRA_HV_INFO("snprintf failure - %s\n", + info->nvlog_producers[cntr].name); + iounmap((void __iomem *)info); + ret = -EFAULT; + goto fail; + } + + strncpy(dir_name, nvlog_shmem_attrs[cntr].node_name, MAX_NAME_SIZE); + strncat(dir_name, "_dir", 4); + nvlog_shmem_attrs[cntr].kobj = kobject_create_and_add(dir_name, parent); + if (nvlog_shmem_attrs[cntr].kobj == NULL) { + TEGRA_HV_INFO("failed to add kobject\n"); + ret = -ENOMEM; + goto fail; + } + + ret = nvlog_create_sysfs_nodes(&nvlog_shmem_attrs[cntr]); + if (ret != 0) + goto fail; + } + } + + iounmap((void __iomem *)info); + return 0; + +fail: + for (cntr = 0; cntr < MAX_NVLOG_PRODUCERS; cntr++) { + if (nvlog_shmem_attrs[cntr].kobj) + kobject_del(nvlog_shmem_attrs[cntr].kobj); + } + + if (parent) + kobject_del(parent); + + return ret; +} /* Map the HV trace buffer to the calling user process */ static int hvc_sysfs_mmap(struct file *fp, struct kobject *ko, @@ -128,8 +313,7 @@ static int create_log_mask_node(struct kobject *kobj) return sysfs_create_bin_file(kobj, &log_mask_attr); } -/* Set up all relevant hypervisor control nodes */ -static int __init hvc_sysfs_register(void) +static int hyp_trace_buffer_init(void) { struct kobject *kobj; int ret; @@ -139,13 +323,6 @@ static int __init hvc_sysfs_register(void) struct trace_buf *buffs; uint16_t i, count; - if (is_tegra_hypervisor_mode() == false) { - TEGRA_HV_INFO("hypervisor is not present\n"); - /*retunring success in case of native kernel otherwise - systemd-modules-load service will failed.*/ - return 0; - } - kobj = kobject_create_and_add("hvc", NULL); if (kobj == NULL) { TEGRA_HV_INFO("failed to add kobject\n"); @@ -233,6 +410,34 @@ fail: return ret; } +/* Set up all relevant hypervisor control nodes */ +static int __init hvc_sysfs_register(void) +{ + int ret; + + if (is_tegra_hypervisor_mode() == false) { + TEGRA_HV_INFO("hypervisor is not present\n"); + /* retunring success in case of native kernel otherwise + * systemd-modules-load service will failed. + */ + return 0; + } + + ret = hyp_trace_buffer_init(); + if (ret != 0) + TEGRA_HV_ERR("Error: Hypervisor trace buffer init failed\n"); + else + TEGRA_HV_INFO("Hypervisor trace buffer initialized successfully\n"); + + ret = hyp_nvlog_buffer_init(); + if (ret != 0) + TEGRA_HV_ERR("Error: Hypervisor nvlog buffer init failed\n"); + else + TEGRA_HV_INFO("Hypervisor nvlog buffer initialized successfully\n"); + + return 0; +} + late_initcall(hvc_sysfs_register); MODULE_LICENSE("GPL"); diff --git a/include/soc/tegra/virt/syscalls.h b/include/soc/tegra/virt/syscalls.h index a942a27b..29ec1afd 100644 --- a/include/soc/tegra/virt/syscalls.h +++ b/include/soc/tegra/virt/syscalls.h @@ -1,6 +1,13 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ /* - * Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-FileCopyrightText: Copyright (c) 2022-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: LicenseRef-NvidiaProprietary + * + * NVIDIA CORPORATION, its affiliates and licensors retain all intellectual + * property and proprietary rights in and to this material, related + * documentation and any modifications thereto. Any use, reproduction, + * disclosure or distribution of this material and related documentation + * without an express license agreement from NVIDIA CORPORATION or + * its affiliates is strictly prohibited. */ #ifndef __TEGRA_SYSCALLS_H__ @@ -18,11 +25,13 @@ #define HVC_NR_READ_HYP_INFO 9 #define HVC_NR_GUEST_RESET 10 #define HVC_NR_SYSINFO_IPA 13 +#define HVC_NR_READ_VM_INFO 16 #define HVC_NR_TRACE_GET_EVENT_MASK 0x8003U #define HVC_NR_TRACE_SET_EVENT_MASK 0x8004U #define GUEST_PRIMARY 0 #define GUEST_IVC_SERVER 0 +#define MAX_NVLOG_PRODUCERS 32U #define HVC_NR_CPU_FREQ 0xC6000022 #define NGUESTS_MAX 16 @@ -33,6 +42,45 @@ #include #endif +/* + * @brief Structure describing a single NvLog producer. A producer can have + * multiple buffers (if it has multiple threads) and the buffers are stored + * contiguously in memory as an array. This structure provides the base + * address, stride, and length of the array so that the consumer can locate all + * of the buffers belonging to the producer. + */ +struct nvlog_producer { + /* Base IPA of an array of NvLog buffers belonging to the producer. */ + uint64_t ipa; + /* Size of the IPA region containing the NvLog buffer array. */ + uint64_t region_size; + /* Size of a single NvLog buffer. This is the stride of the array of + * NvLog buffers belonging to the producer. + */ + uint64_t buf_size; + /* Number of NvLog buffers belonging to the producer. */ + uint64_t buf_count; + /* Name of the NvLog producer. */ + char name[32]; +}; + +/* + * Data structure for the VM Information Region. + * + * The Intermediate Physical Address (IPA) of this structure is returned by the + * hyp_read_vm_info Hypercall. + */ +struct vm_info_region { + /* + * Table of NvLog producers. + * + * This table is only populated for VMs that have the 'log_access' PCT flag + * set. Valid entries in the table have non-zero 'region_ipa', 'buf_size', + * and buf_count' fields. + */ + struct nvlog_producer nvlog_producers[MAX_NVLOG_PRODUCERS]; +}; + struct tegra_hv_queue_data { uint32_t id; /* IVC id */ uint32_t peers[2]; @@ -212,6 +260,20 @@ __attribute__((no_sanitize_address)) static inline int hyp_read_nguests(unsigned return (int)r0; } +__attribute__((no_sanitize_address)) static inline int hyp_read_vm_info(uint64_t *vm_info_region_pa) +{ + register uint64_t r0 asm("x0"); + register uint64_t r1 asm("x1"); + + asm("hvc %2" + : "=r"(r0), "=r"(r1) + : "i"(HVC_NR_READ_VM_INFO) + : "x2", "x3", _X4_X17); + + *vm_info_region_pa = r1; + return (int)r0; +} + __attribute__((no_sanitize_address)) static inline int hyp_read_ivc_info(uint64_t *ivc_info_page_pa) { register uint64_t r0 asm("x0");