gigaset: reduce syslog clutter
[linux/fpc-iii.git] / drivers / sfi / sfi_core.c
blobb204a09291393406acd9450ba641d754fd0707ed
1 /* sfi_core.c Simple Firmware Interface - core internals */
3 /*
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.
8 GPL LICENSE SUMMARY
10 Copyright(c) 2009 Intel Corporation. All rights reserved.
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.
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.
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.
27 BSD LICENSE
29 Copyright(c) 2009 Intel Corporation. All rights reserved.
31 Redistribution and use in source and binary forms, with or without
32 modification, are permitted provided that the following conditions
33 are met:
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.
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.
59 #define KMSG_COMPONENT "SFI"
60 #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
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>
71 #include "sfi_core.h"
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))
79 int sfi_disabled __read_mostly;
80 EXPORT_SYMBOL(sfi_disabled);
82 static u64 syst_pa __read_mostly;
83 static struct sfi_table_simple *syst_va __read_mostly;
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
91 static u32 sfi_use_ioremap __read_mostly;
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.
97 static void __iomem * __ref sfi_map_memory(u64 phys, u32 size)
99 if (!phys || !size)
100 return NULL;
102 if (sfi_use_ioremap)
103 return ioremap(phys, size);
104 else
105 return early_ioremap(phys, size);
108 static void __ref sfi_unmap_memory(void __iomem *virt, u32 size)
110 if (!virt || !size)
111 return;
113 if (sfi_use_ioremap)
114 iounmap(virt);
115 else
116 early_iounmap(virt, size);
119 static void sfi_print_table_header(unsigned long long pa,
120 struct sfi_table_header *header)
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);
129 * sfi_verify_table()
130 * Sanity check table lengh, calculate checksum
132 static int sfi_verify_table(struct sfi_table_header *table)
135 u8 checksum = 0;
136 u8 *puchar = (u8 *)table;
137 u32 length = table->len;
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;
145 while (length--)
146 checksum += *puchar++;
148 if (checksum) {
149 pr_err("Checksum %2.2X should be %2.2X\n",
150 table->csum, table->csum - checksum);
151 return -1;
153 return 0;
157 * sfi_map_table()
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.
163 struct sfi_table_header *sfi_map_table(u64 pa)
165 struct sfi_table_header *th;
166 u32 length;
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);
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;
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));
182 return sfi_map_memory(pa, length);
186 * sfi_unmap_table()
188 * Undoes effect of sfi_map_table() by unmapping table
189 * if it did not completely fit on same page as SYST.
191 void sfi_unmap_table(struct sfi_table_header *th)
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);
198 static int sfi_table_check_key(struct sfi_table_header *th,
199 struct sfi_table_key *key)
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;
209 return 0;
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
220 * 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.
224 * 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
229 struct sfi_table_header *
230 __ref sfi_check_table(u64 pa, struct sfi_table_key *key)
232 struct sfi_table_header *th;
233 void *ret = NULL;
235 th = sfi_map_table(pa);
236 if (!th)
237 return ERR_PTR(-ENOMEM);
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 */
248 sfi_unmap_table(th);
249 return ret;
253 * sfi_get_table()
255 * Search SYST for the specified table with the signature in
256 * the key, and return the mapped table
258 struct sfi_table_header *sfi_get_table(struct sfi_table_key *key)
260 struct sfi_table_header *th;
261 u32 tbl_cnt, i;
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;
270 return NULL;
273 void sfi_put_table(struct sfi_table_header *th)
275 sfi_unmap_table(th);
278 /* Find table with signature, run handler on it */
279 int sfi_table_parse(char *signature, char *oem_id, char *oem_table_id,
280 sfi_table_handler handler)
282 struct sfi_table_header *table = NULL;
283 struct sfi_table_key key;
284 int ret = -EINVAL;
286 if (sfi_disabled || !handler || !signature)
287 goto exit;
289 key.sig = signature;
290 key.oem_id = oem_id;
291 key.oem_table_id = oem_table_id;
293 table = sfi_get_table(&key);
294 if (!table)
295 goto exit;
297 ret = handler(table);
298 sfi_put_table(table);
299 exit:
300 return ret;
302 EXPORT_SYMBOL_GPL(sfi_table_parse);
305 * sfi_parse_syst()
306 * Checksum all the tables in SYST and print their headers
308 * success: set syst_va, return 0
310 static int __init sfi_parse_syst(void)
312 struct sfi_table_key key = SFI_ANY_KEY;
313 int tbl_cnt, i;
314 void *ret;
316 syst_va = sfi_map_memory(syst_pa, sizeof(struct sfi_table_simple));
317 if (!syst_va)
318 return -ENOMEM;
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);
327 return 0;
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.
336 * success: set syst_pa, return 0
337 * fail: return -1
339 static __init int sfi_find_syst(void)
341 unsigned long offset, len;
342 void *start;
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;
349 for (offset = 0; offset < len; offset += 16) {
350 struct sfi_table_header *syst_hdr;
352 syst_hdr = start + offset;
353 if (strncmp(syst_hdr->sig, SFI_SIG_SYST,
354 SFI_SIGNATURE_SIZE))
355 continue;
357 if (syst_hdr->len > PAGE_SIZE)
358 continue;
360 sfi_print_table_header(SFI_SYST_SEARCH_BEGIN + offset,
361 syst_hdr);
363 if (sfi_verify_table(syst_hdr))
364 continue;
367 * Enforce SFI spec mandate that SYST reside within a page.
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;
375 /* Success */
376 syst_pa = SFI_SYST_SEARCH_BEGIN + offset;
377 sfi_unmap_memory(start, len);
378 return 0;
381 sfi_unmap_memory(start, len);
382 return -1;
385 void __init sfi_init(void)
387 if (!acpi_disabled)
388 disable_sfi();
390 if (sfi_disabled)
391 return;
393 pr_info("Simple Firmware Interface v0.7 http://simplefirmware.org\n");
395 if (sfi_find_syst() || sfi_parse_syst() || sfi_platform_init())
396 disable_sfi();
398 return;
401 void __init sfi_init_late(void)
403 int length;
405 if (sfi_disabled)
406 return;
408 length = syst_va->header.len;
409 sfi_unmap_memory(syst_va, sizeof(struct sfi_table_simple));
411 /* Use ioremap now after it is ready */
412 sfi_use_ioremap = 1;
413 syst_va = sfi_map_memory(syst_pa, length);
415 sfi_acpi_init();