1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17#include <linux/string.h>
18#include <linux/firmware-map.h>
19#include <linux/kernel.h>
20#include <linux/module.h>
21#include <linux/types.h>
22#include <linux/bootmem.h>
23#include <linux/slab.h>
24#include <linux/mm.h>
25
26
27
28
29
30
31
32
33
34
35struct firmware_map_entry {
36
37
38
39
40 u64 start;
41 u64 end;
42 const char *type;
43 struct list_head list;
44 struct kobject kobj;
45};
46
47
48
49
50static ssize_t memmap_attr_show(struct kobject *kobj,
51 struct attribute *attr, char *buf);
52static ssize_t start_show(struct firmware_map_entry *entry, char *buf);
53static ssize_t end_show(struct firmware_map_entry *entry, char *buf);
54static ssize_t type_show(struct firmware_map_entry *entry, char *buf);
55
56static struct firmware_map_entry * __meminit
57firmware_map_find_entry(u64 start, u64 end, const char *type);
58
59
60
61
62
63struct memmap_attribute {
64 struct attribute attr;
65 ssize_t (*show)(struct firmware_map_entry *entry, char *buf);
66};
67
68static struct memmap_attribute memmap_start_attr = __ATTR_RO(start);
69static struct memmap_attribute memmap_end_attr = __ATTR_RO(end);
70static struct memmap_attribute memmap_type_attr = __ATTR_RO(type);
71
72
73
74
75static struct attribute *def_attrs[] = {
76 &memmap_start_attr.attr,
77 &memmap_end_attr.attr,
78 &memmap_type_attr.attr,
79 NULL
80};
81
82static const struct sysfs_ops memmap_attr_ops = {
83 .show = memmap_attr_show,
84};
85
86
87static LIST_HEAD(map_entries);
88static DEFINE_SPINLOCK(map_entries_lock);
89
90
91
92
93
94
95
96static LIST_HEAD(map_entries_bootmem);
97static DEFINE_SPINLOCK(map_entries_bootmem_lock);
98
99
100static inline struct firmware_map_entry *
101to_memmap_entry(struct kobject *kobj)
102{
103 return container_of(kobj, struct firmware_map_entry, kobj);
104}
105
106static void __meminit release_firmware_map_entry(struct kobject *kobj)
107{
108 struct firmware_map_entry *entry = to_memmap_entry(kobj);
109
110 if (PageReserved(virt_to_page(entry))) {
111
112
113
114
115
116
117 spin_lock(&map_entries_bootmem_lock);
118 list_add(&entry->list, &map_entries_bootmem);
119 spin_unlock(&map_entries_bootmem_lock);
120
121 return;
122 }
123
124 kfree(entry);
125}
126
127static struct kobj_type __refdata memmap_ktype = {
128 .release = release_firmware_map_entry,
129 .sysfs_ops = &memmap_attr_ops,
130 .default_attrs = def_attrs,
131};
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148static int firmware_map_add_entry(u64 start, u64 end,
149 const char *type,
150 struct firmware_map_entry *entry)
151{
152 BUG_ON(start > end);
153
154 entry->start = start;
155 entry->end = end - 1;
156 entry->type = type;
157 INIT_LIST_HEAD(&entry->list);
158 kobject_init(&entry->kobj, &memmap_ktype);
159
160 spin_lock(&map_entries_lock);
161 list_add_tail(&entry->list, &map_entries);
162 spin_unlock(&map_entries_lock);
163
164 return 0;
165}
166
167
168
169
170
171
172
173
174static inline void firmware_map_remove_entry(struct firmware_map_entry *entry)
175{
176 list_del(&entry->list);
177}
178
179
180
181
182static int add_sysfs_fw_map_entry(struct firmware_map_entry *entry)
183{
184 static int map_entries_nr;
185 static struct kset *mmap_kset;
186
187 if (!mmap_kset) {
188 mmap_kset = kset_create_and_add("memmap", NULL, firmware_kobj);
189 if (!mmap_kset)
190 return -ENOMEM;
191 }
192
193 entry->kobj.kset = mmap_kset;
194 if (kobject_add(&entry->kobj, NULL, "%d", map_entries_nr++))
195 kobject_put(&entry->kobj);
196
197 return 0;
198}
199
200
201
202
203static inline void remove_sysfs_fw_map_entry(struct firmware_map_entry *entry)
204{
205 kobject_put(&entry->kobj);
206}
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221static struct firmware_map_entry * __meminit
222firmware_map_find_entry_in_list(u64 start, u64 end, const char *type,
223 struct list_head *list)
224{
225 struct firmware_map_entry *entry;
226
227 list_for_each_entry(entry, list, list)
228 if ((entry->start == start) && (entry->end == end) &&
229 (!strcmp(entry->type, type))) {
230 return entry;
231 }
232
233 return NULL;
234}
235
236
237
238
239
240
241
242
243
244
245
246
247
248static struct firmware_map_entry * __meminit
249firmware_map_find_entry(u64 start, u64 end, const char *type)
250{
251 return firmware_map_find_entry_in_list(start, end, type, &map_entries);
252}
253
254
255
256
257
258
259
260
261
262
263
264
265static struct firmware_map_entry * __meminit
266firmware_map_find_entry_bootmem(u64 start, u64 end, const char *type)
267{
268 return firmware_map_find_entry_in_list(start, end, type,
269 &map_entries_bootmem);
270}
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285int __meminit firmware_map_add_hotplug(u64 start, u64 end, const char *type)
286{
287 struct firmware_map_entry *entry;
288
289 entry = firmware_map_find_entry_bootmem(start, end, type);
290 if (!entry) {
291 entry = kzalloc(sizeof(struct firmware_map_entry), GFP_ATOMIC);
292 if (!entry)
293 return -ENOMEM;
294 } else {
295
296 spin_lock(&map_entries_bootmem_lock);
297 list_del(&entry->list);
298 spin_unlock(&map_entries_bootmem_lock);
299
300 memset(entry, 0, sizeof(*entry));
301 }
302
303 firmware_map_add_entry(start, end, type, entry);
304
305 add_sysfs_fw_map_entry(entry);
306
307 return 0;
308}
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323int __init firmware_map_add_early(u64 start, u64 end, const char *type)
324{
325 struct firmware_map_entry *entry;
326
327 entry = alloc_bootmem(sizeof(struct firmware_map_entry));
328 if (WARN_ON(!entry))
329 return -ENOMEM;
330
331 return firmware_map_add_entry(start, end, type, entry);
332}
333
334
335
336
337
338
339
340
341
342
343
344int __meminit firmware_map_remove(u64 start, u64 end, const char *type)
345{
346 struct firmware_map_entry *entry;
347
348 spin_lock(&map_entries_lock);
349 entry = firmware_map_find_entry(start, end - 1, type);
350 if (!entry) {
351 spin_unlock(&map_entries_lock);
352 return -EINVAL;
353 }
354
355 firmware_map_remove_entry(entry);
356 spin_unlock(&map_entries_lock);
357
358
359 remove_sysfs_fw_map_entry(entry);
360
361 return 0;
362}
363
364
365
366
367
368static ssize_t start_show(struct firmware_map_entry *entry, char *buf)
369{
370 return snprintf(buf, PAGE_SIZE, "0x%llx\n",
371 (unsigned long long)entry->start);
372}
373
374static ssize_t end_show(struct firmware_map_entry *entry, char *buf)
375{
376 return snprintf(buf, PAGE_SIZE, "0x%llx\n",
377 (unsigned long long)entry->end);
378}
379
380static ssize_t type_show(struct firmware_map_entry *entry, char *buf)
381{
382 return snprintf(buf, PAGE_SIZE, "%s\n", entry->type);
383}
384
385static inline struct memmap_attribute *to_memmap_attr(struct attribute *attr)
386{
387 return container_of(attr, struct memmap_attribute, attr);
388}
389
390static ssize_t memmap_attr_show(struct kobject *kobj,
391 struct attribute *attr, char *buf)
392{
393 struct firmware_map_entry *entry = to_memmap_entry(kobj);
394 struct memmap_attribute *memmap_attr = to_memmap_attr(attr);
395
396 return memmap_attr->show(entry, buf);
397}
398
399
400
401
402
403
404
405
406
407static int __init firmware_memmap_init(void)
408{
409 struct firmware_map_entry *entry;
410
411 list_for_each_entry(entry, &map_entries, list)
412 add_sysfs_fw_map_entry(entry);
413
414 return 0;
415}
416late_initcall(firmware_memmap_init);
417
418