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