mirror of
git://nv-tegra.nvidia.com/linux-nv-oot.git
synced 2025-12-22 09:11:26 +03:00
Guest VM has access to all of the profiler carveout right now so that it can read full carveout and dump all the entries. Right now address and size of Guest VM owned profiler carveout is parsed from kernel command line. And the address of full profiler carveout (containing logs of other VMs too) is calculated based on assumptions. Current architecture is now getting reworked so that only a privileged Guest VM can access full carveout and that too in read-only mode. Each VM will have read-write access to its own carveout to read/write profiler entries of that VM. Update tegra_bootloader_debug_init.c to parse tegra_bl_prof_ro_start and tegra_bl_prof_ro_size from kernel command line. These values indicate address and size of the full read only carveout. In tegra_bootloader_debuginit(), map the address of read only carveout to tegra_bl_mapped_prof_ro_start and set is_privileged_vm to true. Only privileged VM will be assigned this read only address by BL. tegra_bl_mapped_prof_start continues to map to read-write carveout owned by the VM. Update profiler_show() so that it reads entries from the read-only carveout if is_privileged_vm is set, otherwise it reads from read write carveout. Bug 3566403 Change-Id: I3c1e5d42d67f7724c6fa43b7e27f08ce2cd607b7 Signed-off-by: Deepak Nibade <dnibade@nvidia.com> Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/2868921 Reviewed-by: Bharat Nihalani <bnihalani@nvidia.com> GVS: Gerrit_Virtual_Submit <buildbot_gerritrpt@nvidia.com>
606 lines
17 KiB
C
606 lines
17 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
// Copyright (c) 2022-2023, NVIDIA CORPORATION. All rights reserved.
|
|
|
|
#include <linux/init.h>
|
|
#include <linux/module.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/spinlock.h>
|
|
#include <linux/io.h>
|
|
#include <linux/seq_file.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/debugfs.h>
|
|
#include <linux/string.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/memblock.h>
|
|
#include <asm/page.h>
|
|
#include "tegra_bootloader_debug.h"
|
|
|
|
static const char *module_name = "tegra_bootloader_debug";
|
|
static const char *dir_name = "tegra_bootloader";
|
|
static void __iomem *usc;
|
|
|
|
#ifdef CONFIG_DEBUG_FS
|
|
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 *boot_cfg = "boot_cfg";
|
|
#endif
|
|
|
|
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)];
|
|
};
|
|
|
|
static spinlock_t tegra_bl_lock;
|
|
static struct kobject *boot_profiler_kobj;
|
|
static void *tegra_bl_mapped_prof_start;
|
|
static void *tegra_bl_mapped_prof_ro_start;
|
|
static bool is_privileged_vm;
|
|
|
|
#ifdef CONFIG_DEBUG_FS
|
|
static const uint32_t gr_mb1 = enum_gr_mb1;
|
|
static const uint32_t gr_mb2 = enum_gr_mb2;
|
|
static const uint32_t gr_cpu_bl = enum_gr_cpu_bl;
|
|
|
|
static int dbg_golden_register_show(struct seq_file *s, void *unused);
|
|
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_boot_cfg;
|
|
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 boot_cfg_fops = {
|
|
.open = boot_cfg_open,
|
|
.read = seq_read,
|
|
.llseek = seq_lseek,
|
|
.release = single_release,
|
|
};
|
|
#endif /* CONFIG_DEBUG_FS */
|
|
|
|
#define MAX_PROFILE_STRLEN 55
|
|
/* This address corresponds to T234
|
|
* TBD - get this information from DT node
|
|
*/
|
|
#define TEGRA_US_COUNTER_REG 0x0C6B0000
|
|
/* Size is currently hardcoded to 64k
|
|
* as QB is using the same size.
|
|
*/
|
|
#define SIZE_OF_FULL_CARVEOUT (64*1024)
|
|
|
|
struct profiler_record {
|
|
char str[MAX_PROFILE_STRLEN + 1];
|
|
uint64_t timestamp;
|
|
} __packed;
|
|
|
|
static void profiler_show_entries(void *addr, int size)
|
|
{
|
|
struct profiler_record *profiler_data;
|
|
int count = 0;
|
|
int i = 0;
|
|
int prof_data_section_valid = 0;
|
|
|
|
profiler_data = (struct profiler_record *)addr;
|
|
count = size / sizeof(struct profiler_record);
|
|
i = -1;
|
|
pr_info("\n");
|
|
while (count--) {
|
|
i++;
|
|
if (!profiler_data[i].timestamp) {
|
|
if (prof_data_section_valid) {
|
|
pr_info("\n");
|
|
prof_data_section_valid = 0;
|
|
}
|
|
continue;
|
|
}
|
|
pr_info("%-54s\t%16lld",
|
|
profiler_data[i].str, profiler_data[i].timestamp);
|
|
if (i > 0 && profiler_data[i - 1].timestamp) {
|
|
pr_cont("\t%16lld",
|
|
profiler_data[i].timestamp - profiler_data[i - 1].timestamp);
|
|
}
|
|
prof_data_section_valid = 1;
|
|
}
|
|
}
|
|
|
|
static ssize_t profiler_show(struct kobject *kobj,
|
|
struct kobj_attribute *attr,
|
|
char *buf)
|
|
{
|
|
if (is_privileged_vm) {
|
|
if (!tegra_bl_mapped_prof_ro_start) {
|
|
pr_err("%s\n", "Error mapping RO profiling data\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
profiler_show_entries(tegra_bl_mapped_prof_ro_start, tegra_bl_prof_ro_size);
|
|
} else {
|
|
if (!tegra_bl_mapped_prof_start) {
|
|
pr_err("%s\n", "Error mapping RW profiling data\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
profiler_show_entries(tegra_bl_mapped_prof_start, tegra_bl_prof_size);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct kobj_attribute profiler_attribute =
|
|
__ATTR(profiler, 0400, profiler_show, NULL);
|
|
|
|
/**
|
|
* tegra_bl_add_profiler_entry - add a new profiling point
|
|
* @buf: string to add as a profiling marker
|
|
* @len: length of the string
|
|
*
|
|
* Return: 0 on success or error code in case of failure.
|
|
*/
|
|
size_t tegra_bl_add_profiler_entry(const char *buf, size_t len)
|
|
{
|
|
int count = 0;
|
|
int i = 0;
|
|
struct profiler_record *profiler_data;
|
|
|
|
if (len > MAX_PROFILE_STRLEN) {
|
|
pr_err("%s: Failed to add record, invalid length: %ld\n",
|
|
module_name, len);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (!tegra_bl_mapped_prof_start || !tegra_bl_prof_size) {
|
|
pr_err("Error mapping profiling data\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
spin_lock(&tegra_bl_lock);
|
|
|
|
profiler_data = (struct profiler_record *)tegra_bl_mapped_prof_start;
|
|
count = tegra_bl_prof_size / sizeof(struct profiler_record);
|
|
while (i < count) {
|
|
if (!profiler_data[i].timestamp)
|
|
break;
|
|
i++;
|
|
}
|
|
|
|
if (i == count) {
|
|
pr_err("Error profiling data buffer full\n");
|
|
spin_unlock(&tegra_bl_lock);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
profiler_data[i].timestamp = readl(usc);
|
|
spin_unlock(&tegra_bl_lock);
|
|
|
|
strncpy(profiler_data[i].str, buf, len);
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(tegra_bl_add_profiler_entry);
|
|
|
|
static ssize_t add_profiler_record_store(struct kobject *kobj,
|
|
struct kobj_attribute *attr,
|
|
const char *buf, size_t len)
|
|
{
|
|
if (tegra_bl_add_profiler_entry(buf, len))
|
|
pr_err("Error adding profiler entry failed\n");
|
|
|
|
return len;
|
|
}
|
|
|
|
static struct kobj_attribute add_profiler_record_attribute =
|
|
__ATTR_WO(add_profiler_record);
|
|
|
|
#ifdef CONFIG_DEBUG_FS
|
|
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);
|
|
}
|
|
#endif /* CONFIG_DEBUG_FS */
|
|
|
|
static int __init tegra_bootloader_debuginit(void)
|
|
{
|
|
void __iomem *ptr_bl_prof_ro_carveout = NULL;
|
|
void __iomem *ptr_bl_prof_carveout = NULL;
|
|
int bl_debug_verify_file_entry;
|
|
#ifdef CONFIG_DEBUG_FS
|
|
void __iomem *ptr_bl_debug_data_start = NULL;
|
|
void __iomem *ptr_bl_boot_cfg_start = NULL;
|
|
|
|
if (debugfs_initialized()) {
|
|
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;
|
|
}
|
|
|
|
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,
|
|
(__force u64)ptr_bl_debug_data_start,
|
|
(u64)tegra_bl_debug_data_size);
|
|
tegra_bl_mapped_debug_data_start = (__force void *)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 =
|
|
(__force void *)ptr_bl_boot_cfg_start;
|
|
}
|
|
}
|
|
}
|
|
#endif /* CONFIG_DEBUG_FS */
|
|
boot_profiler_kobj = kobject_create_and_add(dir_name, kernel_kobj);
|
|
if (IS_ERR_OR_NULL(boot_profiler_kobj)) {
|
|
pr_err("%s: failed to create sysfs entries: %ld\n",
|
|
module_name, PTR_ERR(boot_profiler_kobj));
|
|
goto out_err;
|
|
}
|
|
|
|
bl_debug_verify_file_entry = sysfs_create_file(boot_profiler_kobj,
|
|
&profiler_attribute.attr);
|
|
if (bl_debug_verify_file_entry) {
|
|
pr_err("%s: failed to create sysfs file : %d\n",
|
|
module_name, bl_debug_verify_file_entry);
|
|
goto out_err;
|
|
}
|
|
|
|
bl_debug_verify_file_entry = sysfs_create_file(boot_profiler_kobj,
|
|
&add_profiler_record_attribute.attr);
|
|
if (bl_debug_verify_file_entry) {
|
|
pr_err("%s: failed to create sysfs file : %d\n",
|
|
module_name, bl_debug_verify_file_entry);
|
|
goto out_err;
|
|
}
|
|
|
|
if (tegra_bl_prof_start != 0 && tegra_bl_prof_size != 0 &&
|
|
!pfn_valid(__phys_to_pfn(tegra_bl_prof_start))) {
|
|
ptr_bl_prof_carveout = ioremap(tegra_bl_prof_start, tegra_bl_prof_size);
|
|
if (!ptr_bl_prof_carveout) {
|
|
pr_err("%s: failed to map tegra_bl_prof_start\n", __func__);
|
|
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,
|
|
(__force u64)ptr_bl_prof_carveout,
|
|
(u64)tegra_bl_prof_size);
|
|
|
|
tegra_bl_mapped_prof_start = (__force void *)ptr_bl_prof_carveout;
|
|
}
|
|
|
|
if (tegra_bl_prof_ro_start != 0 && tegra_bl_prof_ro_size != 0 &&
|
|
!pfn_valid(__phys_to_pfn(tegra_bl_prof_ro_start))) {
|
|
ptr_bl_prof_ro_carveout = ioremap(tegra_bl_prof_ro_start, tegra_bl_prof_ro_size);
|
|
if (!ptr_bl_prof_ro_carveout) {
|
|
pr_err("%s: failed to map tegra_bl_prof_ro_start\n", __func__);
|
|
goto out_err;
|
|
}
|
|
|
|
pr_info("Remapped tegra_bl_prof_ro_start(0x%llx) "
|
|
"to address 0x%llx, size(0x%llx)\n",
|
|
(u64)tegra_bl_prof_ro_start,
|
|
(__force u64)ptr_bl_prof_ro_carveout,
|
|
(u64)tegra_bl_prof_ro_size);
|
|
|
|
tegra_bl_mapped_prof_ro_start = (__force void *)ptr_bl_prof_ro_carveout;
|
|
|
|
is_privileged_vm = true;
|
|
} else {
|
|
is_privileged_vm = false;
|
|
}
|
|
|
|
usc = ioremap(TEGRA_US_COUNTER_REG, 4);
|
|
if (!usc) {
|
|
pr_err("Failed to map TEGRA_US_COUNTER_REG\n");
|
|
goto out_err;
|
|
}
|
|
|
|
spin_lock_init(&tegra_bl_lock);
|
|
|
|
return 0;
|
|
|
|
out_err:
|
|
#ifdef CONFIG_DEBUG_FS
|
|
if (!IS_ERR_OR_NULL(bl_debug_node))
|
|
debugfs_remove_recursive(bl_debug_node);
|
|
if (ptr_bl_debug_data_start)
|
|
iounmap(ptr_bl_debug_data_start);
|
|
if (ptr_bl_boot_cfg_start)
|
|
iounmap(ptr_bl_boot_cfg_start);
|
|
#endif
|
|
if (ptr_bl_prof_carveout)
|
|
iounmap(ptr_bl_prof_carveout);
|
|
if (ptr_bl_prof_ro_carveout)
|
|
iounmap(ptr_bl_prof_ro_carveout);
|
|
|
|
if (boot_profiler_kobj) {
|
|
sysfs_remove_file(boot_profiler_kobj,
|
|
&profiler_attribute.attr);
|
|
sysfs_remove_file(boot_profiler_kobj,
|
|
&add_profiler_record_attribute.attr);
|
|
kobject_put(boot_profiler_kobj);
|
|
boot_profiler_kobj = NULL;
|
|
}
|
|
|
|
return -ENODEV;
|
|
}
|
|
|
|
#ifdef CONFIG_DEBUG_FS
|
|
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);
|
|
}
|
|
#endif /* CONFIG_DEBUG_FS */
|
|
|
|
static int __init tegra_bl_debuginit_module_init(void)
|
|
{
|
|
return tegra_bootloader_debuginit();
|
|
}
|
|
|
|
static void __exit tegra_bl_debuginit_module_exit(void)
|
|
{
|
|
#ifdef CONFIG_DEBUG_FS
|
|
if (!IS_ERR_OR_NULL(bl_debug_node))
|
|
debugfs_remove_recursive(bl_debug_node);
|
|
|
|
if (tegra_bl_mapped_debug_data_start)
|
|
iounmap((void __iomem *)tegra_bl_mapped_debug_data_start);
|
|
|
|
if (tegra_bl_mapped_boot_cfg_start)
|
|
iounmap((void __iomem *)tegra_bl_mapped_boot_cfg_start);
|
|
#endif
|
|
if (tegra_bl_mapped_prof_ro_start)
|
|
iounmap((void __iomem *)tegra_bl_mapped_prof_ro_start);
|
|
|
|
if (tegra_bl_mapped_prof_start)
|
|
iounmap((void __iomem *)tegra_bl_mapped_prof_start);
|
|
|
|
if (boot_profiler_kobj) {
|
|
sysfs_remove_file(boot_profiler_kobj,
|
|
&profiler_attribute.attr);
|
|
sysfs_remove_file(boot_profiler_kobj,
|
|
&add_profiler_record_attribute.attr);
|
|
kobject_put(boot_profiler_kobj);
|
|
boot_profiler_kobj = NULL;
|
|
}
|
|
|
|
if (usc)
|
|
iounmap(usc);
|
|
}
|
|
|
|
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 <mdhingra@nvidia.com>");
|