soc: qcom: Add driver to print xbl log on console from fs

Add driver to capture xbl log from reserved memory region
and print on console when user requests.

Use cat /sys/module/xbl_log/xbl_log to see xbl log.

Change-Id: I5d34a4be63244f354ce70cfac8df2724b6a0537b
Signed-off-by: Vinay Sudra <vsudra@codeaurora.org>
This commit is contained in:
Vinay Sudra 2021-03-17 14:53:25 +05:30
commit 2530ebc788
3 changed files with 148 additions and 0 deletions

View file

@ -1282,5 +1282,14 @@ config QCOM_AOP_SET_DDR
into an exposed sysfs node to send to AOP. The driver expects the Cap
frequency of DDR based on SKU.
config DUMP_XBL_LOG
bool "print xbl log on console from fs"
help
This driver is used to capture xbl log from reserved memory region
defined separately in each device tree and print on console when user
requests. User can perform below command to print xbl log stored in
reserved memory region:
cat /sys/module/xbl_log/xbl_log
source "drivers/soc/qcom/icnss2/Kconfig"
endmenu

View file

@ -116,3 +116,4 @@ obj-$(CONFIG_MSM_SLATECOM) += slatecom_spi.o
obj-$(CONFIG_MSM_SLATECOM_INTERFACE) += slatecom_interface.o
obj-$(CONFIG_QCOM_AOP_SET_DDR) += aop-set-ddr.o
obj-$(CONFIG_CPU_V7) += idle-v7.o
obj-$(CONFIG_DUMP_XBL_LOG) += dump_boot_log.o

View file

@ -0,0 +1,138 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2021, The Linux Foundation. All rights reserved.
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/device.h>
#include <linux/ioport.h>
#include <linux/memblock.h>
#include <linux/of_address.h>
#include <linux/kthread.h>
#include <linux/slab.h>
static char *xbl_log_buf;
size_t xbl_log_size;
static ssize_t xbl_log_show(struct file *fp,
struct kobject *kobj, struct bin_attribute *bin_attr,
char *buf, loff_t offset, size_t count)
{
if (offset < xbl_log_size)
return scnprintf(buf, count, "%s", xbl_log_buf + offset);
return 0;
}
static struct bin_attribute attribute =
__BIN_ATTR(xbl_log, 0444, xbl_log_show, NULL, 0);
static void free_xbl_log_buf(phys_addr_t paddr, size_t size)
{
unsigned long pfn_start = 0, pfn_end = 0, pfn_idx = 0;
memblock_free(paddr, size);
pfn_start = paddr >> PAGE_SHIFT;
pfn_end = (paddr + size) >> PAGE_SHIFT;
for (pfn_idx = pfn_start; pfn_idx < pfn_end; pfn_idx++)
free_reserved_page(pfn_to_page(pfn_idx));
}
static int xbl_log_kthread(void *arg)
{
int err = 1;
struct module_kobject *mkobj;
struct device_node *parent = NULL, *node = NULL;
struct resource res_log = {0,};
phys_addr_t xbl_log_paddr = 0;
void *addr = NULL;
mkobj = kcalloc(1, sizeof(*mkobj), GFP_KERNEL);
if (!mkobj)
return 1;
mkobj->mod = THIS_MODULE;
mkobj->kobj.kset = module_kset;
err = kobject_init_and_add(&mkobj->kobj, &module_ktype, NULL, "xbl_log");
if (err) {
pr_err("xbl_log: cannot create kobject\n");
goto kobj_fail;
}
kobject_get(&mkobj->kobj);
if (IS_ERR_OR_NULL(&mkobj->kobj)) {
err = PTR_ERR(&mkobj->kobj);
goto kobj_fail;
}
err = sysfs_create_bin_file(&mkobj->kobj, &attribute);
if (err) {
pr_err("xbl_log: sysfs entry creation failed\n");
goto kobj_fail;
}
parent = of_find_node_by_path("/reserved-memory");
if (!parent) {
pr_err("xbl_log: reserved-memory node missing\n");
goto kobj_fail;
}
node = of_find_node_by_name(parent, "uefi_log");
if (!node) {
pr_err("xbl_log: uefi_log node missing\n");
goto node_fail;
}
if (of_address_to_resource(node, 0, &res_log))
goto node_fail;
xbl_log_paddr = res_log.start;
xbl_log_size = resource_size(&res_log) - 1;
pr_debug("xbl_log_addr = %x, size=%d\n", xbl_log_paddr, xbl_log_size);
addr = memremap(xbl_log_paddr, xbl_log_size, MEMREMAP_WB);
if (!addr) {
pr_err("xbl_log: memremap failed\n");
goto remap_fail;
}
xbl_log_buf = kzalloc(xbl_log_size, GFP_KERNEL);
if (xbl_log_buf) {
memcpy(xbl_log_buf, addr, xbl_log_size);
xbl_log_buf[xbl_log_size-1] = '\0';
memunmap(addr);
if (xbl_log_size)
free_xbl_log_buf(xbl_log_paddr, xbl_log_size);
return 0;
}
kfree(xbl_log_buf);
remap_fail:
if (node)
of_node_put(node);
node_fail:
if (parent)
of_node_put(parent);
kobj_fail:
kobject_del(&mkobj->kobj);
kfree(mkobj);
return 1;
}
static int __init dump_boot_log_init(void)
{
struct task_struct *xbl_log_task =
kthread_run(xbl_log_kthread, NULL, "xbl_log");
if (PTR_ERR_OR_ZERO(xbl_log_task))
return PTR_ERR(xbl_log_task);
else
return 0;
}
subsys_initcall(dump_boot_log_init);
MODULE_DESCRIPTION("dump xbl log");
MODULE_LICENSE("GPL v2");