diff --git a/drivers/platform/tegra/Makefile b/drivers/platform/tegra/Makefile index 3d1380d1..eff13aa7 100644 --- a/drivers/platform/tegra/Makefile +++ b/drivers/platform/tegra/Makefile @@ -2,6 +2,11 @@ # Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved. obj-m += bad.o + +tegra-bootloader-debug-objs := tegra_bootloader_debug.o +tegra-bootloader-debug-objs += tegra_bootloader_debug_init.o +obj-m += tegra-bootloader-debug.o + tegra-cactmon-objs := cactmon.o tegra-cactmon-objs += actmon_common.o obj-m += tegra-cactmon.o diff --git a/drivers/platform/tegra/tegra_bootloader_debug.c b/drivers/platform/tegra/tegra_bootloader_debug.c new file mode 100644 index 00000000..2c821ffa --- /dev/null +++ b/drivers/platform/tegra/tegra_bootloader_debug.c @@ -0,0 +1,442 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "tegra_bootloader_debug.h" + +static const char *module_name = "tegra_bootloader_debug"; +static const char *dir_name = "tegra_bootloader"; +static const char *gr_file_mb1 = "gr_mb1"; +static const char *gr_file_mb2 = "gr_mb2"; +static const char *gr_file_cpu_bl = "gr_cpu_bl"; +static const char *profiler = "profiler"; +static const char *boot_cfg = "boot_cfg"; + +struct gr_address_value { + unsigned int gr_address; + unsigned int gr_value; +}; + +struct gr_header { + uint32_t mb1_offset; + uint32_t mb1_size; + uint32_t mb2_offset; + uint32_t mb2_size; + uint32_t cpu_bl_offset; + uint32_t cpu_bl_size; +}; + +enum gr_stage { + enum_gr_mb1, + enum_gr_mb2, + enum_gr_cpu_bl, +}; + +struct spi_header { + uint16_t crc; + uint16_t crc_ack; + uint16_t frame_len; + struct { + uint8_t id: 3; + uint8_t version: 3; + uint8_t reserved: 1; + uint8_t has_ts: 1; + } version; +} __packed; + +struct spi_boot_header { + struct spi_header header; + bool rm_respond_evt: 1; + uint8_t rm_respond_data: 4; + uint8_t reserved1: 3; +}; + +struct spi_boot_rx_frame_full { + struct spi_boot_header header; + uint8_t data[8200 - sizeof(struct spi_boot_header)]; +}; + +const uint32_t gr_mb1 = enum_gr_mb1; +const uint32_t gr_mb2 = enum_gr_mb2; +const uint32_t gr_cpu_bl = enum_gr_cpu_bl; + +static int dbg_golden_register_show(struct seq_file *s, void *unused); +static int profiler_show(struct seq_file *s, void *unused); +static int profiler_open(struct inode *inode, struct file *file); +static int dbg_golden_register_open_mb1(struct inode *inode, struct file *file); +static int dbg_golden_register_open_mb2(struct inode *inode, struct file *file); +static int dbg_golden_register_open_cpu_bl(struct inode *inode, struct file *file); +static struct dentry *bl_debug_node; +static struct dentry *bl_debug_verify_reg_node; +static struct dentry *bl_debug_profiler; +static struct dentry *bl_debug_boot_cfg; +static void *tegra_bl_mapped_prof_start; +static void *tegra_bl_mapped_debug_data_start; +static int boot_cfg_show(struct seq_file *s, void *unused); +static int boot_cfg_open(struct inode *inode, struct file *file); +static void *tegra_bl_mapped_boot_cfg_start; + +static const struct file_operations debug_gr_fops_mb1 = { + .open = dbg_golden_register_open_mb1, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + + +static const struct file_operations debug_gr_fops_mb2 = { + .open = dbg_golden_register_open_mb2, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + + +static const struct file_operations debug_gr_fops_cpu_bl = { + .open = dbg_golden_register_open_cpu_bl, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + + +static const struct file_operations profiler_fops = { + .open = profiler_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + + +static const struct file_operations boot_cfg_fops = { + .open = boot_cfg_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +#define MAX_PROFILE_STRLEN 55 + +struct profiler_record { + char str[MAX_PROFILE_STRLEN + 1]; + uint64_t timestamp; +} __packed; + +static int profiler_show(struct seq_file *s, void *unused) +{ + struct profiler_record *profiler_data; + int count = 0; + int i = 0; + int prof_data_section_valid = 0; + + if (!tegra_bl_mapped_prof_start || !tegra_bl_prof_size) { + seq_puts(s, "Error mapping profiling data\n"); + return 0; + } + + profiler_data = (struct profiler_record *)tegra_bl_mapped_prof_start; + count = tegra_bl_prof_size / sizeof(struct profiler_record); + i = -1; + while (count--) { + i++; + if (!profiler_data[i].timestamp) { + if (prof_data_section_valid) { + seq_puts(s, "\n"); + prof_data_section_valid = 0; + } + continue; + } + seq_printf(s, "%-54s\t%16lld", profiler_data[i].str, profiler_data[i].timestamp); + if (i > 0 && profiler_data[i - 1].timestamp) { + seq_printf(s, "\t%16lld", + profiler_data[i].timestamp - profiler_data[i - 1].timestamp); + } + seq_puts(s, "\n"); + prof_data_section_valid = 1; + } + + return 0; +} + +static int profiler_open(struct inode *inode, struct file *file) +{ + return single_open(file, profiler_show, &inode->i_private); +} + +static int dbg_golden_register_show(struct seq_file *s, void *unused) +{ + struct gr_header *golden_reg_header = (struct gr_header *)tegra_bl_mapped_debug_data_start; + struct gr_address_value *gr_memory_dump; + unsigned int gr_entries = 0; + int i; + + switch (*(int *)(s->private)){ + case enum_gr_mb1: + gr_entries = golden_reg_header->mb1_size / sizeof(struct gr_address_value); + gr_memory_dump = (struct gr_address_value *)(golden_reg_header->mb1_offset + + tegra_bl_mapped_debug_data_start + sizeof(struct gr_header)); + break; + case enum_gr_mb2: + gr_entries = golden_reg_header->mb2_size / sizeof(struct gr_address_value); + gr_memory_dump = (struct gr_address_value *)(golden_reg_header->mb2_offset + + tegra_bl_mapped_debug_data_start + sizeof(struct gr_header)); + break; + case enum_gr_cpu_bl: + gr_entries = golden_reg_header->cpu_bl_size / sizeof(struct gr_address_value); + gr_memory_dump = (struct gr_address_value *)(golden_reg_header->cpu_bl_offset + + tegra_bl_mapped_debug_data_start + sizeof(struct gr_header)); + break; + default: + seq_printf(s, "Eiiiirror mapping bootloader debug data%x \n",*(int *)(s->private)); + return 0; + } + if (!gr_entries || !tegra_bl_mapped_debug_data_start) { + seq_puts(s, "Error mapping bootloader debug data\n"); + return 0; + } + + for (i = 0; i < gr_entries; i++) { + seq_printf(s, "{Address 0x%08x}, {Value 0x%08x}\n", + gr_memory_dump->gr_address, gr_memory_dump->gr_value); + + gr_memory_dump++; + } + + return 0; +} + +static int dbg_golden_register_open_mb1(__attribute((unused))struct inode *inode, struct file *file) +{ + return single_open(file, dbg_golden_register_show, (void *)&gr_mb1); +} + +static int dbg_golden_register_open_mb2( __attribute((unused))struct inode *inode, struct file *file) +{ + return single_open(file, dbg_golden_register_show, (void *)&gr_mb2); +} + +static int dbg_golden_register_open_cpu_bl( __attribute((unused))struct inode *inode, struct file *file) +{ + return single_open(file, dbg_golden_register_show, (void *)&gr_cpu_bl); +} + +static int __init tegra_bootloader_debuginit(void) +{ + void __iomem *ptr_bl_prof_start = NULL; + void __iomem *ptr_bl_debug_data_start = NULL; + void __iomem *ptr_bl_boot_cfg_start = NULL; + + bl_debug_node = debugfs_create_dir(dir_name, NULL); + + if (IS_ERR_OR_NULL(bl_debug_node)) { + pr_err("%s: failed to create debugfs entries: %ld\n", + module_name, PTR_ERR(bl_debug_node)); + goto out_err; + } + + pr_info("%s: created %s directory\n", module_name, dir_name); + + bl_debug_verify_reg_node = debugfs_create_file(gr_file_mb1, S_IRUGO, + bl_debug_node, NULL, &debug_gr_fops_mb1); + + if (IS_ERR_OR_NULL(bl_debug_verify_reg_node)) { + pr_err("%s: failed to create debugfs entries: %ld\n", + module_name, PTR_ERR(bl_debug_verify_reg_node)); + goto out_err; + } + + bl_debug_verify_reg_node = debugfs_create_file(gr_file_mb2, S_IRUGO, + bl_debug_node, NULL, &debug_gr_fops_mb2); + + if (IS_ERR_OR_NULL(bl_debug_verify_reg_node)) { + pr_err("%s: failed to create debugfs entries: %ld\n", + module_name, PTR_ERR(bl_debug_verify_reg_node)); + goto out_err; + } + + bl_debug_verify_reg_node = debugfs_create_file(gr_file_cpu_bl, S_IRUGO, + bl_debug_node, NULL, &debug_gr_fops_cpu_bl); + + if (IS_ERR_OR_NULL(bl_debug_verify_reg_node)) { + pr_err("%s: failed to create debugfs entries: %ld\n", + module_name, PTR_ERR(bl_debug_verify_reg_node)); + goto out_err; + } + bl_debug_profiler = debugfs_create_file(profiler, S_IRUGO, + bl_debug_node, NULL, &profiler_fops); + if (IS_ERR_OR_NULL(bl_debug_profiler)) { + pr_err("%s: failed to create debugfs entries: %ld\n", + module_name, PTR_ERR(bl_debug_profiler)); + goto out_err; + } + + tegra_bl_mapped_prof_start = phys_to_virt(tegra_bl_prof_start); + if (tegra_bl_prof_start != 0 + && !pfn_valid(__phys_to_pfn(tegra_bl_prof_start))) { + ptr_bl_prof_start = ioremap(tegra_bl_prof_start, tegra_bl_prof_size); + + WARN_ON(!ptr_bl_prof_start); + if (!ptr_bl_prof_start) { + pr_err("%s: Failed to map tegra_bl_prof_start%08x\n", + __func__, (unsigned int)tegra_bl_prof_start); + goto out_err; + } + pr_info("Remapped tegra_bl_prof_start(0x%llx)" + " to address 0x%llx, size(0x%llx)\n", + (u64)tegra_bl_prof_start, + (u64)ptr_bl_prof_start, + (u64)tegra_bl_prof_size); + + tegra_bl_mapped_prof_start = ptr_bl_prof_start; + } + + tegra_bl_mapped_debug_data_start = + phys_to_virt(tegra_bl_debug_data_start); + if (tegra_bl_debug_data_start != 0 + && !pfn_valid(__phys_to_pfn(tegra_bl_debug_data_start))) { + ptr_bl_debug_data_start = ioremap(tegra_bl_debug_data_start, + tegra_bl_debug_data_size); + + WARN_ON(!ptr_bl_debug_data_start); + if (!ptr_bl_debug_data_start) { + pr_err("%s: Failed to map tegra_bl_debug_data_start%08x\n", + __func__, (unsigned int)tegra_bl_debug_data_start); + goto out_err; + } + + pr_info("Remapped tegra_bl_debug_data_start(0x%llx)" + " to address(0x%llx), size(0x%llx)\n", + (u64)tegra_bl_debug_data_start, + (u64)ptr_bl_debug_data_start, + (u64)tegra_bl_debug_data_size); + tegra_bl_mapped_debug_data_start = ptr_bl_debug_data_start; + } + + /* + * The BCP can be optional, so ignore creating if variables are not set + */ + if (tegra_bl_bcp_start && tegra_bl_bcp_size) { + bl_debug_boot_cfg = debugfs_create_file(boot_cfg, 0444, + bl_debug_node, NULL, &boot_cfg_fops); + if (IS_ERR_OR_NULL(bl_debug_boot_cfg)) { + pr_err("%s: failed to create debugfs entries: %ld\n", + __func__, PTR_ERR(bl_debug_boot_cfg)); + goto out_err; + } + + tegra_bl_mapped_boot_cfg_start = + phys_to_virt(tegra_bl_bcp_start); + if (!pfn_valid(__phys_to_pfn(tegra_bl_bcp_start))) { + ptr_bl_boot_cfg_start = ioremap(tegra_bl_bcp_start, + tegra_bl_bcp_size); + + WARN_ON(!ptr_bl_boot_cfg_start); + if (!ptr_bl_boot_cfg_start) { + pr_err("%s: Failed to map tegra_bl_prof_start %08x\n", + __func__, + (unsigned int)tegra_bl_bcp_start); + goto out_err; + } + tegra_bl_mapped_boot_cfg_start = ptr_bl_boot_cfg_start; + } + } + return 0; + +out_err: + if (!IS_ERR_OR_NULL(bl_debug_node)) + debugfs_remove_recursive(bl_debug_node); + + if (ptr_bl_prof_start) + iounmap(ptr_bl_prof_start); + if (ptr_bl_debug_data_start) + iounmap(ptr_bl_debug_data_start); + if (ptr_bl_boot_cfg_start) + iounmap(ptr_bl_boot_cfg_start); + + return -ENODEV; +} + + +static int boot_cfg_show(struct seq_file *s, void *unused) +{ + uint8_t *data = tegra_bl_mapped_boot_cfg_start; + struct spi_boot_rx_frame_full *spi_frame = + tegra_bl_mapped_boot_cfg_start; + uint32_t i; + + seq_puts(s, "\n Dumping Boot Configuration Protocol "); + seq_printf(s, "0x%08x bytes @ 0x%08x\n", + (unsigned int)tegra_bl_bcp_size, + (unsigned int)tegra_bl_bcp_start); + + seq_puts(s, "\n SPI frame header\n"); + seq_printf(s, " CRC : 0x%02x\n", + spi_frame->header.header.crc); + seq_printf(s, " CRC ACK : 0x%02x\n", + spi_frame->header.header.crc_ack); + seq_printf(s, " Frame len : 0x%02x (%d)\n", + spi_frame->header.header.frame_len, + spi_frame->header.header.frame_len); + seq_printf(s, " Protocol ID : 0x%01x\n", + spi_frame->header.header.version.id); + seq_printf(s, " Version : 0x%01x\n", + spi_frame->header.header.version.version); + seq_printf(s, " Has ts : 0x%01x\n", + spi_frame->header.header.version.has_ts); + seq_printf(s, " Run mode evt: 0x%01x\n", + spi_frame->header.rm_respond_evt); + seq_printf(s, " Run mode : 0x%01x\n", + spi_frame->header.rm_respond_data); + + for (i = 0; i < tegra_bl_bcp_size; i++) { + if (i % 12 == 0) + seq_printf(s, "\n %05d | ", i); + + seq_printf(s, "0x%02x ", data[i]); + } + return 0; +} + +static int boot_cfg_open(struct inode *inode, struct file *file) +{ + return single_open(file, boot_cfg_show, &inode->i_private); +} + +static int __init tegra_bl_debuginit_module_init(void) +{ + return tegra_bootloader_debuginit(); +} + +static void __exit tegra_bl_debuginit_module_exit(void) +{ + if (!IS_ERR_OR_NULL(bl_debug_node)) + debugfs_remove_recursive(bl_debug_node); + + if (tegra_bl_mapped_prof_start) + iounmap(tegra_bl_mapped_prof_start); + + if (tegra_bl_mapped_debug_data_start) + iounmap(tegra_bl_mapped_debug_data_start); + + if (tegra_bl_mapped_boot_cfg_start) + iounmap(tegra_bl_mapped_boot_cfg_start); +} + +module_init(tegra_bl_debuginit_module_init); +module_exit(tegra_bl_debuginit_module_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Driver to enumerate bootloader's debug data"); +MODULE_AUTHOR("Mohit Dhingra "); diff --git a/drivers/platform/tegra/tegra_bootloader_debug.h b/drivers/platform/tegra/tegra_bootloader_debug.h new file mode 100644 index 00000000..d571b9c0 --- /dev/null +++ b/drivers/platform/tegra/tegra_bootloader_debug.h @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved. + +#ifndef __TEGRA_BOOTLOADER_DEBUG_H +#define __TEGRA_BOOTLOADER_DEBUG_H +extern phys_addr_t tegra_bl_debug_data_start; +extern phys_addr_t tegra_bl_debug_data_size; +extern phys_addr_t tegra_bl_prof_start; +extern phys_addr_t tegra_bl_prof_size; +extern phys_addr_t tegra_bl_bcp_start; +extern phys_addr_t tegra_bl_bcp_size; +#endif diff --git a/drivers/platform/tegra/tegra_bootloader_debug_init.c b/drivers/platform/tegra/tegra_bootloader_debug_init.c new file mode 100644 index 00000000..c5ce0cf5 --- /dev/null +++ b/drivers/platform/tegra/tegra_bootloader_debug_init.c @@ -0,0 +1,142 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved. + +#include +#include +#include +#include +#include +#include +#include + +phys_addr_t tegra_bl_debug_data_start; +EXPORT_SYMBOL(tegra_bl_debug_data_start); + +phys_addr_t tegra_bl_debug_data_size; +EXPORT_SYMBOL(tegra_bl_debug_data_size); + +phys_addr_t tegra_bl_prof_start; +EXPORT_SYMBOL(tegra_bl_prof_start); + +phys_addr_t tegra_bl_prof_size; +EXPORT_SYMBOL(tegra_bl_prof_size); + +phys_addr_t tegra_bl_bcp_start; +EXPORT_SYMBOL(tegra_bl_bcp_start); + +phys_addr_t tegra_bl_bcp_size; +EXPORT_SYMBOL(tegra_bl_bcp_size); + +#ifndef MODULE +static int __init tegra_bl_prof_arg(char *option) +{ + char *p = option; + + tegra_bl_prof_size = memparse(p, &p); + + if (!p) + return -EINVAL; + if (*p != '@') + return -EINVAL; + + tegra_bl_prof_start = memparse(p + 1, &p); + + if (!tegra_bl_prof_size || !tegra_bl_prof_start) { + tegra_bl_prof_size = 0; + tegra_bl_prof_start = 0; + return -ENXIO; + } + + if (pfn_valid(__phys_to_pfn(tegra_bl_prof_start))) { + if (memblock_reserve(tegra_bl_prof_start, tegra_bl_prof_size)) { + pr_err("Failed to reserve bl_prof_data %08llx@%08llx\n", + (u64)tegra_bl_prof_size, + (u64)tegra_bl_prof_start); + tegra_bl_prof_size = 0; + tegra_bl_prof_start = 0; + return -ENXIO; + } + } + + return 0; +} + +early_param("bl_prof_dataptr", tegra_bl_prof_arg); + +static int __init tegra_bl_debug_data_arg(char *options) +{ + char *p = options; + + tegra_bl_debug_data_size = memparse(p, &p); + + if (!p) + return -EINVAL; + if (*p != '@') + return -EINVAL; + + tegra_bl_debug_data_start = memparse(p + 1, &p); + + if (!tegra_bl_debug_data_size || !tegra_bl_debug_data_start) { + tegra_bl_debug_data_size = 0; + tegra_bl_debug_data_start = 0; + return -ENXIO; + } + + if (pfn_valid(__phys_to_pfn(tegra_bl_debug_data_start))) { + if (memblock_reserve(tegra_bl_debug_data_start, + tegra_bl_debug_data_size)) { + pr_err("Failed to reserve bl_debug_data %08llx@%08llx\n", + (u64)tegra_bl_debug_data_size, + (u64)tegra_bl_debug_data_start); + tegra_bl_debug_data_size = 0; + tegra_bl_debug_data_start = 0; + return -ENXIO; + } + } + + return 0; +} +early_param("bl_debug_data", tegra_bl_debug_data_arg); + +static int tegra_bl_bcp_arg(char *options) +{ + char *p = options; + + tegra_bl_bcp_size = memparse(p, &p); + + if (!p) + return -EINVAL; + if (*p != '@') + return -EINVAL; + + tegra_bl_bcp_start = memparse(p + 1, &p); + + if (!tegra_bl_bcp_size || !tegra_bl_bcp_start) { + tegra_bl_bcp_size = 0; + tegra_bl_bcp_start = 0; + return -ENXIO; + } + + if (pfn_valid(__phys_to_pfn(tegra_bl_bcp_start))) { + if (memblock_reserve(tegra_bl_bcp_start, + tegra_bl_bcp_size)) { + pr_err("Failed to reserve boot_cfg_data %08llx@%08llx\n", + (u64)tegra_bl_bcp_size, + (u64)tegra_bl_bcp_start); + tegra_bl_bcp_size = 0; + tegra_bl_bcp_start = 0; + return -ENXIO; + } + } + + pr_warn("Got boot_cfg_data %08llx@%08llx\n", + (u64)tegra_bl_bcp_size, + (u64)tegra_bl_bcp_start); + + return 0; +} +early_param("boot_cfg_dataptr", tegra_bl_bcp_arg); + +#else + +#endif