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 (entry->kobj.state_in_sysfs)
188 return -EEXIST;
189
190 if (!mmap_kset) {
191 mmap_kset = kset_create_and_add("memmap", NULL, firmware_kobj);
192 if (!mmap_kset)
193 return -ENOMEM;
194 }
195
196 entry->kobj.kset = mmap_kset;
197 if (kobject_add(&entry->kobj, NULL, "%d", map_entries_nr++))
198 kobject_put(&entry->kobj);
199
200 return 0;
201}
202
203
204
205
206static inline void remove_sysfs_fw_map_entry(struct firmware_map_entry *entry)
207{
208 kobject_put(&entry->kobj);
209}
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224static struct firmware_map_entry * __meminit
225firmware_map_find_entry_in_list(u64 start, u64 end, const char *type,
226 struct list_head *list)
227{
228 struct firmware_map_entry *entry;
229
230 list_for_each_entry(entry, list, list)
231 if ((entry->start == start) && (entry->end == end) &&
232 (!strcmp(entry->type, type))) {
233 return entry;
234 }
235
236 return NULL;
237}
238
239
240
241
242
243
244
245
246
247
248
249
250
251static struct firmware_map_entry * __meminit
252firmware_map_find_entry(u64 start, u64 end, const char *type)
253{
254 return firmware_map_find_entry_in_list(start, end, type, &map_entries);
255}
256
257
258
259
260
261
262
263
264
265
266
267
268static struct firmware_map_entry * __meminit
269firmware_map_find_entry_bootmem(u64 start, u64 end, const char *type)
270{
271 return firmware_map_find_entry_in_list(start, end, type,
272 &map_entries_bootmem);
273}
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288int __meminit firmware_map_add_hotplug(u64 start, u64 end, const char *type)
289{
290 struct firmware_map_entry *entry;
291
292 entry = firmware_map_find_entry(start, end - 1, type);
293 if (entry)
294 return 0;
295
296 entry = firmware_map_find_entry_bootmem(start, end - 1, type);
297 if (!entry) {
298 entry = kzalloc(sizeof(struct firmware_map_entry), GFP_ATOMIC);
299 if (!entry)
300 return -ENOMEM;
301 } else {
302
303 spin_lock(&map_entries_bootmem_lock);
304 list_del(&entry->list);
305 spin_unlock(&map_entries_bootmem_lock);
306
307 memset(entry, 0, sizeof(*entry));
308 }
309
310 firmware_map_add_entry(start, end, type, entry);
311
312 add_sysfs_fw_map_entry(entry);
313
314 return 0;
315}
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330int __init firmware_map_add_early(u64 start, u64 end, const char *type)
331{
332 struct firmware_map_entry *entry;
333
334 entry = memblock_virt_alloc(sizeof(struct firmware_map_entry), 0);
335 if (WARN_ON(!entry))
336 return -ENOMEM;
337
338 return firmware_map_add_entry(start, end, type, entry);
339}
340
341
342
343
344
345
346
347
348
349
350
351int __meminit firmware_map_remove(u64 start, u64 end, const char *type)
352{
353 struct firmware_map_entry *entry;
354
355 spin_lock(&map_entries_lock);
356 entry = firmware_map_find_entry(start, end - 1, type);
357 if (!entry) {
358 spin_unlock(&map_entries_lock);
359 return -EINVAL;
360 }
361
362 firmware_map_remove_entry(entry);
363 spin_unlock(&map_entries_lock);
364
365
366 remove_sysfs_fw_map_entry(entry);
367
368 return 0;
369}
370
371
372
373
374
375static ssize_t start_show(struct firmware_map_entry *entry, char *buf)
376{
377 return snprintf(buf, PAGE_SIZE, "0x%llx\n",
378 (unsigned long long)entry->start);
379}
380
381static ssize_t end_show(struct firmware_map_entry *entry, char *buf)
382{
383 return snprintf(buf, PAGE_SIZE, "0x%llx\n",
384 (unsigned long long)entry->end);
385}
386
387static ssize_t type_show(struct firmware_map_entry *entry, char *buf)
388{
389 return snprintf(buf, PAGE_SIZE, "%s\n", entry->type);
390}
391
392static inline struct memmap_attribute *to_memmap_attr(struct attribute *attr)
393{
394 return container_of(attr, struct memmap_attribute, attr);
395}
396
397static ssize_t memmap_attr_show(struct kobject *kobj,
398 struct attribute *attr, char *buf)
399{
400 struct firmware_map_entry *entry = to_memmap_entry(kobj);
401 struct memmap_attribute *memmap_attr = to_memmap_attr(attr);
402
403 return memmap_attr->show(entry, buf);
404}
405
406
407
408
409
410
411
412
413
414static int __init firmware_memmap_init(void)
415{
416 struct firmware_map_entry *entry;
417
418 list_for_each_entry(entry, &map_entries, list)
419 add_sysfs_fw_map_entry(entry);
420
421 return 0;
422}
423late_initcall(firmware_memmap_init);
424
425