diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index 534ddd0e4168..d7fb28a64936 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -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 diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index 0cc6fa88a236..a8e5cad801d9 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile @@ -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 diff --git a/drivers/soc/qcom/dump_boot_log.c b/drivers/soc/qcom/dump_boot_log.c new file mode 100644 index 000000000000..d6b212a27ae4 --- /dev/null +++ b/drivers/soc/qcom/dump_boot_log.c @@ -0,0 +1,138 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2021, The Linux Foundation. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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");