214 lines · 4.7 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-only/* * kexec_handover_debugfs.c - kexec handover debugfs interfaces * Copyright (C) 2023 Alexander Graf <graf@amazon.com> * Copyright (C) 2025 Microsoft Corporation, Mike Rapoport <rppt@kernel.org> * Copyright (C) 2025 Google LLC, Changyuan Lyu <changyuanl@google.com> * Copyright (C) 2025 Google LLC, Pasha Tatashin <pasha.tatashin@soleen.com> */ #define pr_fmt(fmt) "KHO: " fmt #include <linux/init.h>#include <linux/io.h>#include <linux/libfdt.h>#include <linux/mm.h>#include <linux/kho/abi/kexec_handover.h>#include "kexec_handover_internal.h" static struct dentry *debugfs_root; struct fdt_debugfs {	struct list_head list;	struct debugfs_blob_wrapper wrapper;	struct dentry *file;}; static int __kho_debugfs_blob_add(struct list_head *list, struct dentry *dir,				  const char *name, const void *blob,				  size_t size){	struct fdt_debugfs *f;	struct dentry *file; 	f = kmalloc_obj(*f);	if (!f)		return -ENOMEM; 	f->wrapper.data = (void *)blob;	f->wrapper.size = size; 	file = debugfs_create_blob(name, 0400, dir, &f->wrapper);	if (IS_ERR(file)) {		kfree(f);		return PTR_ERR(file);	} 	f->file = file;	list_add(&f->list, list); 	return 0;} int kho_debugfs_blob_add(struct kho_debugfs *dbg, const char *name,			 const void *blob, size_t size, bool root){	struct dentry *dir; 	if (root)		dir = dbg->dir;	else		dir = dbg->sub_fdt_dir; 	return __kho_debugfs_blob_add(&dbg->fdt_list, dir, name, blob, size);} void kho_debugfs_blob_remove(struct kho_debugfs *dbg, void *blob){	struct fdt_debugfs *ff; 	list_for_each_entry(ff, &dbg->fdt_list, list) {		if (ff->wrapper.data == blob) {			debugfs_remove(ff->file);			list_del(&ff->list);			kfree(ff);			break;		}	}} static int scratch_phys_show(struct seq_file *m, void *v){	for (int i = 0; i < kho_scratch_cnt; i++)		seq_printf(m, "0x%llx\n", kho_scratch[i].addr); 	return 0;}DEFINE_SHOW_ATTRIBUTE(scratch_phys); static int scratch_len_show(struct seq_file *m, void *v){	for (int i = 0; i < kho_scratch_cnt; i++)		seq_printf(m, "0x%llx\n", kho_scratch[i].size); 	return 0;}DEFINE_SHOW_ATTRIBUTE(scratch_len); __init void kho_in_debugfs_init(struct kho_debugfs *dbg, const void *fdt){	struct dentry *dir, *sub_fdt_dir;	int err, child; 	INIT_LIST_HEAD(&dbg->fdt_list); 	dir = debugfs_create_dir("in", debugfs_root);	if (IS_ERR(dir)) {		err = PTR_ERR(dir);		goto err_out;	} 	sub_fdt_dir = debugfs_create_dir("sub_fdts", dir);	if (IS_ERR(sub_fdt_dir)) {		err = PTR_ERR(sub_fdt_dir);		goto err_rmdir;	} 	err = __kho_debugfs_blob_add(&dbg->fdt_list, dir, "fdt", fdt,				     fdt_totalsize(fdt));	if (err)		goto err_rmdir; 	fdt_for_each_subnode(child, fdt, 0) {		int len = 0;		const char *name = fdt_get_name(fdt, child, NULL);		const u64 *blob_phys;		const u64 *blob_size;		void *blob; 		blob_phys = fdt_getprop(fdt, child,					KHO_SUB_TREE_PROP_NAME, &len);		if (!blob_phys)			continue;		if (len != sizeof(*blob_phys)) {			pr_warn("node %s prop %s has invalid length: %d\n",				name, KHO_SUB_TREE_PROP_NAME, len);			continue;		} 		blob_size = fdt_getprop(fdt, child,					KHO_SUB_TREE_SIZE_PROP_NAME, &len);		if (!blob_size || len != sizeof(*blob_size)) {			pr_warn("node %s missing or invalid %s property\n",				name, KHO_SUB_TREE_SIZE_PROP_NAME);			continue;		} 		blob = phys_to_virt(*blob_phys);		err = __kho_debugfs_blob_add(&dbg->fdt_list, sub_fdt_dir, name,					     blob, *blob_size);		if (err) {			pr_warn("failed to add blob %s to debugfs: %pe\n",				name, ERR_PTR(err));			continue;		}	} 	dbg->dir = dir;	dbg->sub_fdt_dir = sub_fdt_dir; 	return;err_rmdir:	debugfs_remove_recursive(dir);err_out:	/*	 * Failure to create /sys/kernel/debug/kho/in does not prevent	 * reviving state from KHO and setting up KHO for the next	 * kexec.	 */	if (err) {		pr_err("failed exposing handover FDT in debugfs: %pe\n",		       ERR_PTR(err));	}} __init int kho_out_debugfs_init(struct kho_debugfs *dbg){	struct dentry *dir, *f, *sub_fdt_dir; 	INIT_LIST_HEAD(&dbg->fdt_list); 	dir = debugfs_create_dir("out", debugfs_root);	if (IS_ERR(dir))		return -ENOMEM; 	sub_fdt_dir = debugfs_create_dir("sub_fdts", dir);	if (IS_ERR(sub_fdt_dir))		goto err_rmdir; 	f = debugfs_create_file("scratch_phys", 0400, dir, NULL,				&scratch_phys_fops);	if (IS_ERR(f))		goto err_rmdir; 	f = debugfs_create_file("scratch_len", 0400, dir, NULL,				&scratch_len_fops);	if (IS_ERR(f))		goto err_rmdir; 	dbg->dir = dir;	dbg->sub_fdt_dir = sub_fdt_dir;	return 0; err_rmdir:	debugfs_remove_recursive(dir);	return -ENOENT;} __init int kho_debugfs_init(void){	debugfs_root = debugfs_create_dir("kho", NULL);	if (IS_ERR(debugfs_root))		return -ENOENT;	return 0;}