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
71#include "sfi_core.h"
72
73#define ON_SAME_PAGE(addr1, addr2) \
74 (((unsigned long)(addr1) & PAGE_MASK) == \
75 ((unsigned long)(addr2) & PAGE_MASK))
76#define TABLE_ON_PAGE(page, table, size) (ON_SAME_PAGE(page, table) && \
77 ON_SAME_PAGE(page, table + size))
78
79int sfi_disabled __read_mostly;
80EXPORT_SYMBOL(sfi_disabled);
81
82static u64 syst_pa __read_mostly;
83static struct sfi_table_simple *syst_va __read_mostly;
84
85
86
87
88
89
90
91static u32 sfi_use_ioremap __read_mostly;
92
93
94
95
96
97static void __iomem * __ref sfi_map_memory(u64 phys, u32 size)
98{
99 if (!phys || !size)
100 return NULL;
101
102 if (sfi_use_ioremap)
103 return ioremap(phys, size);
104 else
105 return early_ioremap(phys, size);
106}
107
108static void __ref sfi_unmap_memory(void __iomem *virt, u32 size)
109{
110 if (!virt || !size)
111 return;
112
113 if (sfi_use_ioremap)
114 iounmap(virt);
115 else
116 early_iounmap(virt, size);
117}
118
119static void sfi_print_table_header(unsigned long long pa,
120 struct sfi_table_header *header)
121{
122 pr_info("%4.4s %llX, %04X (v%d %6.6s %8.8s)\n",
123 header->sig, pa,
124 header->len, header->rev, header->oem_id,
125 header->oem_table_id);
126}
127
128
129
130
131
132static int sfi_verify_table(struct sfi_table_header *table)
133{
134
135 u8 checksum = 0;
136 u8 *puchar = (u8 *)table;
137 u32 length = table->len;
138
139
140 if (length > 0x100000) {
141 pr_err("Invalid table length 0x%x\n", length);
142 return -1;
143 }
144
145 while (length--)
146 checksum += *puchar++;
147
148 if (checksum) {
149 pr_err("Checksum %2.2X should be %2.2X\n",
150 table->csum, table->csum - checksum);
151 return -1;
152 }
153 return 0;
154}
155
156
157
158
159
160
161
162
163struct sfi_table_header *sfi_map_table(u64 pa)
164{
165 struct sfi_table_header *th;
166 u32 length;
167
168 if (!TABLE_ON_PAGE(syst_pa, pa, sizeof(struct sfi_table_header)))
169 th = sfi_map_memory(pa, sizeof(struct sfi_table_header));
170 else
171 th = (void *)syst_va + (pa - syst_pa);
172
173
174 if (TABLE_ON_PAGE(th, th, th->len))
175 return th;
176
177
178 length = th->len;
179 if (!TABLE_ON_PAGE(syst_pa, pa, sizeof(struct sfi_table_header)))
180 sfi_unmap_memory(th, sizeof(struct sfi_table_header));
181
182 return sfi_map_memory(pa, length);
183}
184
185
186
187
188
189
190
191void sfi_unmap_table(struct sfi_table_header *th)
192{
193 if (!TABLE_ON_PAGE(syst_va, th, th->len))
194 sfi_unmap_memory(th, TABLE_ON_PAGE(th, th, th->len) ?
195 sizeof(*th) : th->len);
196}
197
198static int sfi_table_check_key(struct sfi_table_header *th,
199 struct sfi_table_key *key)
200{
201
202 if (strncmp(th->sig, key->sig, SFI_SIGNATURE_SIZE)
203 || (key->oem_id && strncmp(th->oem_id,
204 key->oem_id, SFI_OEM_ID_SIZE))
205 || (key->oem_table_id && strncmp(th->oem_table_id,
206 key->oem_table_id, SFI_OEM_TABLE_ID_SIZE)))
207 return -1;
208
209 return 0;
210}
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229struct sfi_table_header *
230 __ref sfi_check_table(u64 pa, struct sfi_table_key *key)
231{
232 struct sfi_table_header *th;
233 void *ret = NULL;
234
235 th = sfi_map_table(pa);
236 if (!th)
237 return ERR_PTR(-ENOMEM);
238
239 if (!key->sig) {
240 sfi_print_table_header(pa, th);
241 if (sfi_verify_table(th))
242 ret = ERR_PTR(-EINVAL);
243 } else {
244 if (!sfi_table_check_key(th, key))
245 return th;
246 }
247
248 sfi_unmap_table(th);
249 return ret;
250}
251
252
253
254
255
256
257
258struct sfi_table_header *sfi_get_table(struct sfi_table_key *key)
259{
260 struct sfi_table_header *th;
261 u32 tbl_cnt, i;
262
263 tbl_cnt = SFI_GET_NUM_ENTRIES(syst_va, u64);
264 for (i = 0; i < tbl_cnt; i++) {
265 th = sfi_check_table(syst_va->pentry[i], key);
266 if (!IS_ERR(th) && th)
267 return th;
268 }
269
270 return NULL;
271}
272
273void sfi_put_table(struct sfi_table_header *th)
274{
275 sfi_unmap_table(th);
276}
277
278
279int sfi_table_parse(char *signature, char *oem_id, char *oem_table_id,
280 sfi_table_handler handler)
281{
282 struct sfi_table_header *table = NULL;
283 struct sfi_table_key key;
284 int ret = -EINVAL;
285
286 if (sfi_disabled || !handler || !signature)
287 goto exit;
288
289 key.sig = signature;
290 key.oem_id = oem_id;
291 key.oem_table_id = oem_table_id;
292
293 table = sfi_get_table(&key);
294 if (!table)
295 goto exit;
296
297 ret = handler(table);
298 sfi_put_table(table);
299exit:
300 return ret;
301}
302EXPORT_SYMBOL_GPL(sfi_table_parse);
303
304
305
306
307
308
309
310static int __init sfi_parse_syst(void)
311{
312 struct sfi_table_key key = SFI_ANY_KEY;
313 int tbl_cnt, i;
314 void *ret;
315
316 syst_va = sfi_map_memory(syst_pa, sizeof(struct sfi_table_simple));
317 if (!syst_va)
318 return -ENOMEM;
319
320 tbl_cnt = SFI_GET_NUM_ENTRIES(syst_va, u64);
321 for (i = 0; i < tbl_cnt; i++) {
322 ret = sfi_check_table(syst_va->pentry[i], &key);
323 if (IS_ERR(ret))
324 return PTR_ERR(ret);
325 }
326
327 return 0;
328}
329
330
331
332
333
334
335
336
337
338
339static __init int sfi_find_syst(void)
340{
341 unsigned long offset, len;
342 void *start;
343
344 len = SFI_SYST_SEARCH_END - SFI_SYST_SEARCH_BEGIN;
345 start = sfi_map_memory(SFI_SYST_SEARCH_BEGIN, len);
346 if (!start)
347 return -1;
348
349 for (offset = 0; offset < len; offset += 16) {
350 struct sfi_table_header *syst_hdr;
351
352 syst_hdr = start + offset;
353 if (strncmp(syst_hdr->sig, SFI_SIG_SYST,
354 SFI_SIGNATURE_SIZE))
355 continue;
356
357 if (syst_hdr->len > PAGE_SIZE)
358 continue;
359
360 sfi_print_table_header(SFI_SYST_SEARCH_BEGIN + offset,
361 syst_hdr);
362
363 if (sfi_verify_table(syst_hdr))
364 continue;
365
366
367
368
369 if (!ON_SAME_PAGE(syst_pa, syst_pa + syst_hdr->len)) {
370 pr_info("SYST 0x%llx + 0x%x crosses page\n",
371 syst_pa, syst_hdr->len);
372 continue;
373 }
374
375
376 syst_pa = SFI_SYST_SEARCH_BEGIN + offset;
377 sfi_unmap_memory(start, len);
378 return 0;
379 }
380
381 sfi_unmap_memory(start, len);
382 return -1;
383}
384
385void __init sfi_init(void)
386{
387 if (!acpi_disabled)
388 disable_sfi();
389
390 if (sfi_disabled)
391 return;
392
393 pr_info("Simple Firmware Interface v0.7 http://simplefirmware.org\n");
394
395 if (sfi_find_syst() || sfi_parse_syst() || sfi_platform_init())
396 disable_sfi();
397
398 return;
399}
400
401void __init sfi_init_late(void)
402{
403 int length;
404
405 if (sfi_disabled)
406 return;
407
408 length = syst_va->header.len;
409 sfi_unmap_memory(syst_va, sizeof(struct sfi_table_simple));
410
411
412 sfi_use_ioremap = 1;
413 syst_va = sfi_map_memory(syst_pa, length);
414
415 sfi_acpi_init();
416}
417