1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59#define KMSG_COMPONENT "SFI"
60#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
61
62#include <linux/bootmem.h>
63#include <linux/kernel.h>
64#include <linux/module.h>
65#include <linux/errno.h>
66#include <linux/types.h>
67#include <linux/acpi.h>
68#include <linux/init.h>
69#include <linux/sfi.h>
70#include <linux/slab.h>
71
72#include "sfi_core.h"
73
74#define ON_SAME_PAGE(addr1, addr2) \
75 (((unsigned long)(addr1) & PAGE_MASK) == \
76 ((unsigned long)(addr2) & PAGE_MASK))
77#define TABLE_ON_PAGE(page, table, size) (ON_SAME_PAGE(page, table) && \
78 ON_SAME_PAGE(page, table + size))
79
80int sfi_disabled __read_mostly;
81EXPORT_SYMBOL(sfi_disabled);
82
83static u64 syst_pa __read_mostly;
84static struct sfi_table_simple *syst_va __read_mostly;
85
86
87
88
89
90
91
92static u32 sfi_use_ioremap __read_mostly;
93
94
95
96
97
98static void __iomem * __ref sfi_map_memory(u64 phys, u32 size)
99{
100 if (!phys || !size)
101 return NULL;
102
103 if (sfi_use_ioremap)
104 return ioremap_cache(phys, size);
105 else
106 return early_ioremap(phys, size);
107}
108
109static void __ref sfi_unmap_memory(void __iomem *virt, u32 size)
110{
111 if (!virt || !size)
112 return;
113
114 if (sfi_use_ioremap)
115 iounmap(virt);
116 else
117 early_iounmap(virt, size);
118}
119
120static void sfi_print_table_header(unsigned long long pa,
121 struct sfi_table_header *header)
122{
123 pr_info("%4.4s %llX, %04X (v%d %6.6s %8.8s)\n",
124 header->sig, pa,
125 header->len, header->rev, header->oem_id,
126 header->oem_table_id);
127}
128
129
130
131
132
133static int sfi_verify_table(struct sfi_table_header *table)
134{
135
136 u8 checksum = 0;
137 u8 *puchar = (u8 *)table;
138 u32 length = table->len;
139
140
141 if (length > 0x100000) {
142 pr_err("Invalid table length 0x%x\n", length);
143 return -1;
144 }
145
146 while (length--)
147 checksum += *puchar++;
148
149 if (checksum) {
150 pr_err("Checksum %2.2X should be %2.2X\n",
151 table->csum, table->csum - checksum);
152 return -1;
153 }
154 return 0;
155}
156
157
158
159
160
161
162
163
164struct sfi_table_header *sfi_map_table(u64 pa)
165{
166 struct sfi_table_header *th;
167 u32 length;
168
169 if (!TABLE_ON_PAGE(syst_pa, pa, sizeof(struct sfi_table_header)))
170 th = sfi_map_memory(pa, sizeof(struct sfi_table_header));
171 else
172 th = (void *)syst_va + (pa - syst_pa);
173
174
175 if (TABLE_ON_PAGE(th, th, th->len))
176 return th;
177
178
179 length = th->len;
180 if (!TABLE_ON_PAGE(syst_pa, pa, sizeof(struct sfi_table_header)))
181 sfi_unmap_memory(th, sizeof(struct sfi_table_header));
182
183 return sfi_map_memory(pa, length);
184}
185
186
187
188
189
190
191
192void sfi_unmap_table(struct sfi_table_header *th)
193{
194 if (!TABLE_ON_PAGE(syst_va, th, th->len))
195 sfi_unmap_memory(th, TABLE_ON_PAGE(th, th, th->len) ?
196 sizeof(*th) : th->len);
197}
198
199static int sfi_table_check_key(struct sfi_table_header *th,
200 struct sfi_table_key *key)
201{
202
203 if (strncmp(th->sig, key->sig, SFI_SIGNATURE_SIZE)
204 || (key->oem_id && strncmp(th->oem_id,
205 key->oem_id, SFI_OEM_ID_SIZE))
206 || (key->oem_table_id && strncmp(th->oem_table_id,
207 key->oem_table_id, SFI_OEM_TABLE_ID_SIZE)))
208 return -1;
209
210 return 0;
211}
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230struct sfi_table_header *
231 __ref sfi_check_table(u64 pa, struct sfi_table_key *key)
232{
233 struct sfi_table_header *th;
234 void *ret = NULL;
235
236 th = sfi_map_table(pa);
237 if (!th)
238 return ERR_PTR(-ENOMEM);
239
240 if (!key->sig) {
241 sfi_print_table_header(pa, th);
242 if (sfi_verify_table(th))
243 ret = ERR_PTR(-EINVAL);
244 } else {
245 if (!sfi_table_check_key(th, key))
246 return th;
247 }
248
249 sfi_unmap_table(th);
250 return ret;
251}
252
253
254
255
256
257
258
259struct sfi_table_header *sfi_get_table(struct sfi_table_key *key)
260{
261 struct sfi_table_header *th;
262 u32 tbl_cnt, i;
263
264 tbl_cnt = SFI_GET_NUM_ENTRIES(syst_va, u64);
265 for (i = 0; i < tbl_cnt; i++) {
266 th = sfi_check_table(syst_va->pentry[i], key);
267 if (!IS_ERR(th) && th)
268 return th;
269 }
270
271 return NULL;
272}
273
274void sfi_put_table(struct sfi_table_header *th)
275{
276 sfi_unmap_table(th);
277}
278
279
280int sfi_table_parse(char *signature, char *oem_id, char *oem_table_id,
281 sfi_table_handler handler)
282{
283 struct sfi_table_header *table = NULL;
284 struct sfi_table_key key;
285 int ret = -EINVAL;
286
287 if (sfi_disabled || !handler || !signature)
288 goto exit;
289
290 key.sig = signature;
291 key.oem_id = oem_id;
292 key.oem_table_id = oem_table_id;
293
294 table = sfi_get_table(&key);
295 if (!table)
296 goto exit;
297
298 ret = handler(table);
299 sfi_put_table(table);
300exit:
301 return ret;
302}
303EXPORT_SYMBOL_GPL(sfi_table_parse);
304
305
306
307
308
309
310
311static int __init sfi_parse_syst(void)
312{
313 struct sfi_table_key key = SFI_ANY_KEY;
314 int tbl_cnt, i;
315 void *ret;
316
317 syst_va = sfi_map_memory(syst_pa, sizeof(struct sfi_table_simple));
318 if (!syst_va)
319 return -ENOMEM;
320
321 tbl_cnt = SFI_GET_NUM_ENTRIES(syst_va, u64);
322 for (i = 0; i < tbl_cnt; i++) {
323 ret = sfi_check_table(syst_va->pentry[i], &key);
324 if (IS_ERR(ret))
325 return PTR_ERR(ret);
326 }
327
328 return 0;
329}
330
331
332
333
334
335
336
337
338
339
340static __init int sfi_find_syst(void)
341{
342 unsigned long offset, len;
343 void *start;
344
345 len = SFI_SYST_SEARCH_END - SFI_SYST_SEARCH_BEGIN;
346 start = sfi_map_memory(SFI_SYST_SEARCH_BEGIN, len);
347 if (!start)
348 return -1;
349
350 for (offset = 0; offset < len; offset += 16) {
351 struct sfi_table_header *syst_hdr;
352
353 syst_hdr = start + offset;
354 if (strncmp(syst_hdr->sig, SFI_SIG_SYST,
355 SFI_SIGNATURE_SIZE))
356 continue;
357
358 if (syst_hdr->len > PAGE_SIZE)
359 continue;
360
361 sfi_print_table_header(SFI_SYST_SEARCH_BEGIN + offset,
362 syst_hdr);
363
364 if (sfi_verify_table(syst_hdr))
365 continue;
366
367
368
369
370 if (!ON_SAME_PAGE(syst_pa, syst_pa + syst_hdr->len)) {
371 pr_info("SYST 0x%llx + 0x%x crosses page\n",
372 syst_pa, syst_hdr->len);
373 continue;
374 }
375
376
377 syst_pa = SFI_SYST_SEARCH_BEGIN + offset;
378 sfi_unmap_memory(start, len);
379 return 0;
380 }
381
382 sfi_unmap_memory(start, len);
383 return -1;
384}
385
386static struct kobject *sfi_kobj;
387static struct kobject *tables_kobj;
388
389static ssize_t sfi_table_show(struct file *filp, struct kobject *kobj,
390 struct bin_attribute *bin_attr, char *buf,
391 loff_t offset, size_t count)
392{
393 struct sfi_table_attr *tbl_attr =
394 container_of(bin_attr, struct sfi_table_attr, attr);
395 struct sfi_table_header *th = NULL;
396 struct sfi_table_key key;
397 ssize_t cnt;
398
399 key.sig = tbl_attr->name;
400 key.oem_id = NULL;
401 key.oem_table_id = NULL;
402
403 if (strncmp(SFI_SIG_SYST, tbl_attr->name, SFI_SIGNATURE_SIZE)) {
404 th = sfi_get_table(&key);
405 if (!th)
406 return 0;
407
408 cnt = memory_read_from_buffer(buf, count, &offset,
409 th, th->len);
410 sfi_put_table(th);
411 } else
412 cnt = memory_read_from_buffer(buf, count, &offset,
413 syst_va, syst_va->header.len);
414
415 return cnt;
416}
417
418struct sfi_table_attr __init *sfi_sysfs_install_table(u64 pa)
419{
420 struct sfi_table_attr *tbl_attr;
421 struct sfi_table_header *th;
422 int ret;
423
424 tbl_attr = kzalloc(sizeof(struct sfi_table_attr), GFP_KERNEL);
425 if (!tbl_attr)
426 return NULL;
427
428 th = sfi_map_table(pa);
429 if (!th || !th->sig[0]) {
430 kfree(tbl_attr);
431 return NULL;
432 }
433
434 sysfs_attr_init(&tbl_attr->attr.attr);
435 memcpy(tbl_attr->name, th->sig, SFI_SIGNATURE_SIZE);
436
437 tbl_attr->attr.size = 0;
438 tbl_attr->attr.read = sfi_table_show;
439 tbl_attr->attr.attr.name = tbl_attr->name;
440 tbl_attr->attr.attr.mode = 0400;
441
442 ret = sysfs_create_bin_file(tables_kobj,
443 &tbl_attr->attr);
444 if (ret) {
445 kfree(tbl_attr);
446 tbl_attr = NULL;
447 }
448
449 sfi_unmap_table(th);
450 return tbl_attr;
451}
452
453static int __init sfi_sysfs_init(void)
454{
455 int tbl_cnt, i;
456
457 if (sfi_disabled)
458 return 0;
459
460 sfi_kobj = kobject_create_and_add("sfi", firmware_kobj);
461 if (!sfi_kobj)
462 return 0;
463
464 tables_kobj = kobject_create_and_add("tables", sfi_kobj);
465 if (!tables_kobj) {
466 kobject_put(sfi_kobj);
467 return 0;
468 }
469
470 sfi_sysfs_install_table(syst_pa);
471
472 tbl_cnt = SFI_GET_NUM_ENTRIES(syst_va, u64);
473
474 for (i = 0; i < tbl_cnt; i++)
475 sfi_sysfs_install_table(syst_va->pentry[i]);
476
477 sfi_acpi_sysfs_init();
478 kobject_uevent(sfi_kobj, KOBJ_ADD);
479 kobject_uevent(tables_kobj, KOBJ_ADD);
480 pr_info("SFI sysfs interfaces init success\n");
481 return 0;
482}
483
484void __init sfi_init(void)
485{
486 if (!acpi_disabled)
487 disable_sfi();
488
489 if (sfi_disabled)
490 return;
491
492 pr_info("Simple Firmware Interface v0.81 http://simplefirmware.org\n");
493
494 if (sfi_find_syst() || sfi_parse_syst() || sfi_platform_init())
495 disable_sfi();
496
497 return;
498}
499
500void __init sfi_init_late(void)
501{
502 int length;
503
504 if (sfi_disabled)
505 return;
506
507 length = syst_va->header.len;
508 sfi_unmap_memory(syst_va, sizeof(struct sfi_table_simple));
509
510
511 sfi_use_ioremap = 1;
512 syst_va = sfi_map_memory(syst_pa, length);
513
514 sfi_acpi_init();
515}
516
517
518
519
520
521core_initcall(sfi_sysfs_init);
522