File Index
Files and directories
Sign in
FILE

kexec_handover_debugfs.c

File preview with syntax highlighting.

linux-6.19.14/kernel/liveupdate/kexec_handover_debugfs.c C
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * kexec_handover_debugfs.c - kexec handover debugfs interfaces
  4 * Copyright (C) 2023 Alexander Graf <graf@amazon.com>
  5 * Copyright (C) 2025 Microsoft Corporation, Mike Rapoport <rppt@kernel.org>
  6 * Copyright (C) 2025 Google LLC, Changyuan Lyu <changyuanl@google.com>
  7 * Copyright (C) 2025 Google LLC, Pasha Tatashin <pasha.tatashin@soleen.com>
  8 */
  9
 10#define pr_fmt(fmt) "KHO: " fmt
 11
 12#include <linux/init.h>
 13#include <linux/io.h>
 14#include <linux/libfdt.h>
 15#include <linux/mm.h>
 16#include "kexec_handover_internal.h"
 17
 18static struct dentry *debugfs_root;
 19
 20struct fdt_debugfs {
 21	struct list_head list;
 22	struct debugfs_blob_wrapper wrapper;
 23	struct dentry *file;
 24};
 25
 26static int __kho_debugfs_fdt_add(struct list_head *list, struct dentry *dir,
 27				 const char *name, const void *fdt)
 28{
 29	struct fdt_debugfs *f;
 30	struct dentry *file;
 31
 32	f = kmalloc(sizeof(*f), GFP_KERNEL);
 33	if (!f)
 34		return -ENOMEM;
 35
 36	f->wrapper.data = (void *)fdt;
 37	f->wrapper.size = fdt_totalsize(fdt);
 38
 39	file = debugfs_create_blob(name, 0400, dir, &f->wrapper);
 40	if (IS_ERR(file)) {
 41		kfree(f);
 42		return PTR_ERR(file);
 43	}
 44
 45	f->file = file;
 46	list_add(&f->list, list);
 47
 48	return 0;
 49}
 50
 51int kho_debugfs_fdt_add(struct kho_debugfs *dbg, const char *name,
 52			const void *fdt, bool root)
 53{
 54	struct dentry *dir;
 55
 56	if (root)
 57		dir = dbg->dir;
 58	else
 59		dir = dbg->sub_fdt_dir;
 60
 61	return __kho_debugfs_fdt_add(&dbg->fdt_list, dir, name, fdt);
 62}
 63
 64void kho_debugfs_fdt_remove(struct kho_debugfs *dbg, void *fdt)
 65{
 66	struct fdt_debugfs *ff;
 67
 68	list_for_each_entry(ff, &dbg->fdt_list, list) {
 69		if (ff->wrapper.data == fdt) {
 70			debugfs_remove(ff->file);
 71			list_del(&ff->list);
 72			kfree(ff);
 73			break;
 74		}
 75	}
 76}
 77
 78static int kho_out_finalize_get(void *data, u64 *val)
 79{
 80	*val = kho_finalized();
 81
 82	return 0;
 83}
 84
 85static int kho_out_finalize_set(void *data, u64 val)
 86{
 87	if (val)
 88		return kho_finalize();
 89	else
 90		return -EINVAL;
 91}
 92
 93DEFINE_DEBUGFS_ATTRIBUTE(kho_out_finalize_fops, kho_out_finalize_get,
 94			 kho_out_finalize_set, "%llu\n");
 95
 96static int scratch_phys_show(struct seq_file *m, void *v)
 97{
 98	for (int i = 0; i < kho_scratch_cnt; i++)
 99		seq_printf(m, "0x%llx\n", kho_scratch[i].addr);
100
101	return 0;
102}
103DEFINE_SHOW_ATTRIBUTE(scratch_phys);
104
105static int scratch_len_show(struct seq_file *m, void *v)
106{
107	for (int i = 0; i < kho_scratch_cnt; i++)
108		seq_printf(m, "0x%llx\n", kho_scratch[i].size);
109
110	return 0;
111}
112DEFINE_SHOW_ATTRIBUTE(scratch_len);
113
114__init void kho_in_debugfs_init(struct kho_debugfs *dbg, const void *fdt)
115{
116	struct dentry *dir, *sub_fdt_dir;
117	int err, child;
118
119	INIT_LIST_HEAD(&dbg->fdt_list);
120
121	dir = debugfs_create_dir("in", debugfs_root);
122	if (IS_ERR(dir)) {
123		err = PTR_ERR(dir);
124		goto err_out;
125	}
126
127	sub_fdt_dir = debugfs_create_dir("sub_fdts", dir);
128	if (IS_ERR(sub_fdt_dir)) {
129		err = PTR_ERR(sub_fdt_dir);
130		goto err_rmdir;
131	}
132
133	err = __kho_debugfs_fdt_add(&dbg->fdt_list, dir, "fdt", fdt);
134	if (err)
135		goto err_rmdir;
136
137	fdt_for_each_subnode(child, fdt, 0) {
138		int len = 0;
139		const char *name = fdt_get_name(fdt, child, NULL);
140		const u64 *fdt_phys;
141
142		fdt_phys = fdt_getprop(fdt, child, "fdt", &len);
143		if (!fdt_phys)
144			continue;
145		if (len != sizeof(*fdt_phys)) {
146			pr_warn("node %s prop fdt has invalid length: %d\n",
147				name, len);
148			continue;
149		}
150		err = __kho_debugfs_fdt_add(&dbg->fdt_list, sub_fdt_dir, name,
151					    phys_to_virt(*fdt_phys));
152		if (err) {
153			pr_warn("failed to add fdt %s to debugfs: %pe\n", name,
154				ERR_PTR(err));
155			continue;
156		}
157	}
158
159	dbg->dir = dir;
160	dbg->sub_fdt_dir = sub_fdt_dir;
161
162	return;
163err_rmdir:
164	debugfs_remove_recursive(dir);
165err_out:
166	/*
167	 * Failure to create /sys/kernel/debug/kho/in does not prevent
168	 * reviving state from KHO and setting up KHO for the next
169	 * kexec.
170	 */
171	if (err) {
172		pr_err("failed exposing handover FDT in debugfs: %pe\n",
173		       ERR_PTR(err));
174	}
175}
176
177__init int kho_out_debugfs_init(struct kho_debugfs *dbg)
178{
179	struct dentry *dir, *f, *sub_fdt_dir;
180
181	INIT_LIST_HEAD(&dbg->fdt_list);
182
183	dir = debugfs_create_dir("out", debugfs_root);
184	if (IS_ERR(dir))
185		return -ENOMEM;
186
187	sub_fdt_dir = debugfs_create_dir("sub_fdts", dir);
188	if (IS_ERR(sub_fdt_dir))
189		goto err_rmdir;
190
191	f = debugfs_create_file("scratch_phys", 0400, dir, NULL,
192				&scratch_phys_fops);
193	if (IS_ERR(f))
194		goto err_rmdir;
195
196	f = debugfs_create_file("scratch_len", 0400, dir, NULL,
197				&scratch_len_fops);
198	if (IS_ERR(f))
199		goto err_rmdir;
200
201	f = debugfs_create_file("finalize", 0600, dir, NULL,
202				&kho_out_finalize_fops);
203	if (IS_ERR(f))
204		goto err_rmdir;
205
206	dbg->dir = dir;
207	dbg->sub_fdt_dir = sub_fdt_dir;
208	return 0;
209
210err_rmdir:
211	debugfs_remove_recursive(dir);
212	return -ENOENT;
213}
214
215__init int kho_debugfs_init(void)
216{
217	debugfs_root = debugfs_create_dir("kho", NULL);
218	if (IS_ERR(debugfs_root))
219		return -ENOENT;
220	return 0;
221}
~/ Read-only file preview.