blob: b204a09291393406acd9450ba641d754fd0707ed [file] [log] [blame]
Feng Tang6ae69962009-08-14 15:13:46 -04001/* sfi_core.c Simple Firmware Interface - core internals */
2
3/*
4
5 This file is provided under a dual BSD/GPLv2 license. When using or
6 redistributing this file, you may do so under either license.
7
8 GPL LICENSE SUMMARY
9
10 Copyright(c) 2009 Intel Corporation. All rights reserved.
11
12 This program is free software; you can redistribute it and/or modify
13 it under the terms of version 2 of the GNU General Public License as
14 published by the Free Software Foundation.
15
16 This program is distributed in the hope that it will be useful, but
17 WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 General Public License for more details.
20
21 You should have received a copy of the GNU General Public License
22 along with this program; if not, write to the Free Software
23 Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
24 The full GNU General Public License is included in this distribution
25 in the file called LICENSE.GPL.
26
27 BSD LICENSE
28
29 Copyright(c) 2009 Intel Corporation. All rights reserved.
30
31 Redistribution and use in source and binary forms, with or without
32 modification, are permitted provided that the following conditions
33 are met:
34
35 * Redistributions of source code must retain the above copyright
36 notice, this list of conditions and the following disclaimer.
37 * Redistributions in binary form must reproduce the above copyright
38 notice, this list of conditions and the following disclaimer in
39 the documentation and/or other materials provided with the
40 distribution.
41 * Neither the name of Intel Corporation nor the names of its
42 contributors may be used to endorse or promote products derived
43 from this software without specific prior written permission.
44
45 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
46 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
47 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
48 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
49 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
50 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
51 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
52 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
53 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
54 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
55 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
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>
Feng Tang6ae69962009-08-14 15:13:46 -040065#include <linux/errno.h>
66#include <linux/types.h>
67#include <linux/acpi.h>
68#include <linux/init.h>
Feng Tang6ae69962009-08-14 15:13:46 -040069#include <linux/sfi.h>
Feng Tang6ae69962009-08-14 15:13:46 -040070
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 * FW creates and saves the SFI tables in memory. When these tables get
87 * used, they may need to be mapped to virtual address space, and the mapping
88 * can happen before or after the ioremap() is ready, so a flag is needed
89 * to indicating this
90 */
91static u32 sfi_use_ioremap __read_mostly;
92
Rakib Mullick01674da2009-09-30 16:09:55 -070093/*
94 * sfi_un/map_memory calls early_ioremap/iounmap which is a __init function
95 * and introduces section mismatch. So use __ref to make it calm.
96 */
97static void __iomem * __ref sfi_map_memory(u64 phys, u32 size)
Feng Tang6ae69962009-08-14 15:13:46 -040098{
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
Rakib Mullick01674da2009-09-30 16:09:55 -0700108static void __ref sfi_unmap_memory(void __iomem *virt, u32 size)
Feng Tang6ae69962009-08-14 15:13:46 -0400109{
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 * sfi_verify_table()
130 * Sanity check table lengh, calculate checksum
131 */
Arjan van de Ven011a6062009-10-02 07:29:47 -0700132static int sfi_verify_table(struct sfi_table_header *table)
Feng Tang6ae69962009-08-14 15:13:46 -0400133{
134
135 u8 checksum = 0;
136 u8 *puchar = (u8 *)table;
137 u32 length = table->len;
138
139 /* Sanity check table length against arbitrary 1MB limit */
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 * sfi_map_table()
158 *
159 * Return address of mapped table
160 * Check for common case that we can re-use mapping to SYST,
161 * which requires syst_pa, syst_va to be initialized.
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 /* If table fits on same page as its header, we are done */
174 if (TABLE_ON_PAGE(th, th, th->len))
175 return th;
176
177 /* Entire table does not fit on same page as SYST */
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 * sfi_unmap_table()
187 *
188 * Undoes effect of sfi_map_table() by unmapping table
189 * if it did not completely fit on same page as SYST.
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 * This function will be used in 2 cases:
214 * 1. used to enumerate and verify the tables addressed by SYST/XSDT,
215 * thus no signature will be given (in kernel boot phase)
216 * 2. used to parse one specific table, signature must exist, and
217 * the mapped virt address will be returned, and the virt space
218 * will be released by call sfi_put_table() later
219 *
Rakib Mullick01674da2009-09-30 16:09:55 -0700220 * This two cases are from two different functions with two different
221 * sections and causes section mismatch warning. So use __ref to tell
222 * modpost not to make any noise.
223 *
Feng Tang6ae69962009-08-14 15:13:46 -0400224 * Return value:
225 * NULL: when can't find a table matching the key
226 * ERR_PTR(error): error value
227 * virt table address: when a matched table is found
228 */
Rakib Mullick01674da2009-09-30 16:09:55 -0700229struct sfi_table_header *
230 __ref sfi_check_table(u64 pa, struct sfi_table_key *key)
Feng Tang6ae69962009-08-14 15:13:46 -0400231{
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; /* Success */
246 }
247
248 sfi_unmap_table(th);
249 return ret;
250}
251
252/*
253 * sfi_get_table()
254 *
255 * Search SYST for the specified table with the signature in
256 * the key, and return the mapped table
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/* Find table with signature, run handler on it */
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 * sfi_parse_syst()
306 * Checksum all the tables in SYST and print their headers
307 *
308 * success: set syst_va, return 0
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 * The OS finds the System Table by searching 16-byte boundaries between
332 * physical address 0x000E0000 and 0x000FFFFF. The OS shall search this region
333 * starting at the low address and shall stop searching when the 1st valid SFI
334 * System Table is found.
335 *
336 * success: set syst_pa, return 0
337 * fail: return -1
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 * Enforce SFI spec mandate that SYST reside within a page.
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 /* Success */
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 /* Use ioremap now after it is ready */
412 sfi_use_ioremap = 1;
413 syst_va = sfi_map_memory(syst_pa, length);
414
415 sfi_acpi_init();
416}