projects/linux·blob master · refs

214 lines · 4.7 KiB

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