1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Architecture specific debugfs files |
4 | * |
5 | * Copyright (C) 2007, Intel Corp. |
6 | * Huang Ying <ying.huang@intel.com> |
7 | */ |
8 | #include <linux/debugfs.h> |
9 | #include <linux/uaccess.h> |
10 | #include <linux/export.h> |
11 | #include <linux/slab.h> |
12 | #include <linux/init.h> |
13 | #include <linux/stat.h> |
14 | #include <linux/io.h> |
15 | #include <linux/mm.h> |
16 | |
17 | #include <asm/setup.h> |
18 | |
19 | struct dentry *arch_debugfs_dir; |
20 | EXPORT_SYMBOL(arch_debugfs_dir); |
21 | |
22 | #ifdef CONFIG_DEBUG_BOOT_PARAMS |
23 | struct setup_data_node { |
24 | u64 paddr; |
25 | u32 type; |
26 | u32 len; |
27 | }; |
28 | |
29 | static ssize_t setup_data_read(struct file *file, char __user *user_buf, |
30 | size_t count, loff_t *ppos) |
31 | { |
32 | struct setup_data_node *node = file->private_data; |
33 | unsigned long remain; |
34 | loff_t pos = *ppos; |
35 | void *p; |
36 | u64 pa; |
37 | |
38 | if (pos < 0) |
39 | return -EINVAL; |
40 | |
41 | if (pos >= node->len) |
42 | return 0; |
43 | |
44 | if (count > node->len - pos) |
45 | count = node->len - pos; |
46 | |
47 | pa = node->paddr + pos; |
48 | |
49 | /* Is it direct data or invalid indirect one? */ |
50 | if (!(node->type & SETUP_INDIRECT) || node->type == SETUP_INDIRECT) |
51 | pa += sizeof(struct setup_data); |
52 | |
53 | p = memremap(offset: pa, size: count, flags: MEMREMAP_WB); |
54 | if (!p) |
55 | return -ENOMEM; |
56 | |
57 | remain = copy_to_user(to: user_buf, from: p, n: count); |
58 | |
59 | memunmap(addr: p); |
60 | |
61 | if (remain) |
62 | return -EFAULT; |
63 | |
64 | *ppos = pos + count; |
65 | |
66 | return count; |
67 | } |
68 | |
69 | static const struct file_operations fops_setup_data = { |
70 | .read = setup_data_read, |
71 | .open = simple_open, |
72 | .llseek = default_llseek, |
73 | }; |
74 | |
75 | static void __init |
76 | create_setup_data_node(struct dentry *parent, int no, |
77 | struct setup_data_node *node) |
78 | { |
79 | struct dentry *d; |
80 | char buf[16]; |
81 | |
82 | sprintf(buf, fmt: "%d" , no); |
83 | d = debugfs_create_dir(name: buf, parent); |
84 | |
85 | debugfs_create_x32(name: "type" , S_IRUGO, parent: d, value: &node->type); |
86 | debugfs_create_file(name: "data" , S_IRUGO, parent: d, data: node, fops: &fops_setup_data); |
87 | } |
88 | |
89 | static int __init create_setup_data_nodes(struct dentry *parent) |
90 | { |
91 | struct setup_indirect *indirect; |
92 | struct setup_data_node *node; |
93 | struct setup_data *data; |
94 | u64 pa_data, pa_next; |
95 | struct dentry *d; |
96 | int error; |
97 | u32 len; |
98 | int no = 0; |
99 | |
100 | d = debugfs_create_dir(name: "setup_data" , parent); |
101 | |
102 | pa_data = boot_params.hdr.setup_data; |
103 | |
104 | while (pa_data) { |
105 | node = kmalloc(size: sizeof(*node), GFP_KERNEL); |
106 | if (!node) { |
107 | error = -ENOMEM; |
108 | goto err_dir; |
109 | } |
110 | |
111 | data = memremap(offset: pa_data, size: sizeof(*data), flags: MEMREMAP_WB); |
112 | if (!data) { |
113 | kfree(objp: node); |
114 | error = -ENOMEM; |
115 | goto err_dir; |
116 | } |
117 | pa_next = data->next; |
118 | |
119 | if (data->type == SETUP_INDIRECT) { |
120 | len = sizeof(*data) + data->len; |
121 | memunmap(addr: data); |
122 | data = memremap(offset: pa_data, size: len, flags: MEMREMAP_WB); |
123 | if (!data) { |
124 | kfree(objp: node); |
125 | error = -ENOMEM; |
126 | goto err_dir; |
127 | } |
128 | |
129 | indirect = (struct setup_indirect *)data->data; |
130 | |
131 | if (indirect->type != SETUP_INDIRECT) { |
132 | node->paddr = indirect->addr; |
133 | node->type = indirect->type; |
134 | node->len = indirect->len; |
135 | } else { |
136 | node->paddr = pa_data; |
137 | node->type = data->type; |
138 | node->len = data->len; |
139 | } |
140 | } else { |
141 | node->paddr = pa_data; |
142 | node->type = data->type; |
143 | node->len = data->len; |
144 | } |
145 | |
146 | create_setup_data_node(parent: d, no, node); |
147 | pa_data = pa_next; |
148 | |
149 | memunmap(addr: data); |
150 | no++; |
151 | } |
152 | |
153 | return 0; |
154 | |
155 | err_dir: |
156 | debugfs_remove_recursive(dentry: d); |
157 | return error; |
158 | } |
159 | |
160 | static struct debugfs_blob_wrapper boot_params_blob = { |
161 | .data = &boot_params, |
162 | .size = sizeof(boot_params), |
163 | }; |
164 | |
165 | static int __init boot_params_kdebugfs_init(void) |
166 | { |
167 | struct dentry *dbp; |
168 | int error; |
169 | |
170 | dbp = debugfs_create_dir(name: "boot_params" , parent: arch_debugfs_dir); |
171 | |
172 | debugfs_create_x16(name: "version" , S_IRUGO, parent: dbp, value: &boot_params.hdr.version); |
173 | debugfs_create_blob(name: "data" , S_IRUGO, parent: dbp, blob: &boot_params_blob); |
174 | |
175 | error = create_setup_data_nodes(parent: dbp); |
176 | if (error) |
177 | debugfs_remove_recursive(dentry: dbp); |
178 | |
179 | return error; |
180 | } |
181 | #endif /* CONFIG_DEBUG_BOOT_PARAMS */ |
182 | |
183 | static int __init arch_kdebugfs_init(void) |
184 | { |
185 | int error = 0; |
186 | |
187 | arch_debugfs_dir = debugfs_create_dir(name: "x86" , NULL); |
188 | |
189 | #ifdef CONFIG_DEBUG_BOOT_PARAMS |
190 | error = boot_params_kdebugfs_init(); |
191 | #endif |
192 | |
193 | return error; |
194 | } |
195 | arch_initcall(arch_kdebugfs_init); |
196 | |