Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 31724f90 authored by qctecmdr's avatar qctecmdr Committed by Gerrit - the friendly Code Review server
Browse files

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

parents 093f1148 2530ebc7
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -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
+1 −0
Original line number Diff line number Diff line
@@ -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
+138 −0
Original line number Diff line number Diff line
// 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");