• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/linux/linux-2.6/drivers/sfi/
1/* 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>
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 * FW creates and saves the SFI tables in memory. When these tables get
88 * used, they may need to be mapped to virtual address space, and the mapping
89 * can happen before or after the ioremap() is ready, so a flag is needed
90 * to indicating this
91 */
92static u32 sfi_use_ioremap __read_mostly;
93
94/*
95 * sfi_un/map_memory calls early_ioremap/iounmap which is a __init function
96 * and introduces section mismatch. So use __ref to make it calm.
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(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 * sfi_verify_table()
131 * Sanity check table lengh, calculate checksum
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	/* Sanity check table length against arbitrary 1MB limit */
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 * sfi_map_table()
159 *
160 * Return address of mapped table
161 * Check for common case that we can re-use mapping to SYST,
162 * which requires syst_pa, syst_va to be initialized.
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	 /* If table fits on same page as its header, we are done */
175	if (TABLE_ON_PAGE(th, th, th->len))
176		return th;
177
178	/* Entire table does not fit on same page as SYST */
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 * sfi_unmap_table()
188 *
189 * Undoes effect of sfi_map_table() by unmapping table
190 * if it did not completely fit on same page as SYST.
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 * This function will be used in 2 cases:
215 * 1. used to enumerate and verify the tables addressed by SYST/XSDT,
216 *    thus no signature will be given (in kernel boot phase)
217 * 2. used to parse one specific table, signature must exist, and
218 *    the mapped virt address will be returned, and the virt space
219 *    will be released by call sfi_put_table() later
220 *
221 * This two cases are from two different functions with two different
222 * sections and causes section mismatch warning. So use __ref to tell
223 * modpost not to make any noise.
224 *
225 * Return value:
226 *	NULL:			when can't find a table matching the key
227 *	ERR_PTR(error):		error value
228 *	virt table address:	when a matched table is found
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;	/* Success */
247	}
248
249	sfi_unmap_table(th);
250	return ret;
251}
252
253/*
254 * sfi_get_table()
255 *
256 * Search SYST for the specified table with the signature in
257 * the key, and return the mapped table
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/* Find table with signature, run handler on it */
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 * sfi_parse_syst()
307 * Checksum all the tables in SYST and print their headers
308 *
309 * success: set syst_va, return 0
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 * The OS finds the System Table by searching 16-byte boundaries between
333 * physical address 0x000E0000 and 0x000FFFFF. The OS shall search this region
334 * starting at the low address and shall stop searching when the 1st valid SFI
335 * System Table is found.
336 *
337 * success: set syst_pa, return 0
338 * fail: return -1
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		 * Enforce SFI spec mandate that SYST reside within a page.
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		/* Success */
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	/* Use ioremap now after it is ready */
511	sfi_use_ioremap = 1;
512	syst_va = sfi_map_memory(syst_pa, length);
513
514	sfi_acpi_init();
515}
516
517/*
518 * The reason we put it here becasue we need wait till the /sys/firmware
519 * is setup, then our interface can be registered in /sys/firmware/sfi
520 */
521core_initcall(sfi_sysfs_init);
522