Linux Audio

Check our new training course

Loading...
Note: File does not exist in v6.9.4.
  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/memblock.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#include <linux/io.h>
 72
 73#include "sfi_core.h"
 74
 75#define ON_SAME_PAGE(addr1, addr2) \
 76	(((unsigned long)(addr1) & PAGE_MASK) == \
 77	((unsigned long)(addr2) & PAGE_MASK))
 78#define TABLE_ON_PAGE(page, table, size) (ON_SAME_PAGE(page, table) && \
 79				ON_SAME_PAGE(page, table + size))
 80
 81int sfi_disabled __read_mostly;
 82EXPORT_SYMBOL(sfi_disabled);
 83
 84static u64 syst_pa __read_mostly;
 85static struct sfi_table_simple *syst_va __read_mostly;
 86
 87/*
 88 * FW creates and saves the SFI tables in memory. When these tables get
 89 * used, they may need to be mapped to virtual address space, and the mapping
 90 * can happen before or after the memremap() is ready, so a flag is needed
 91 * to indicating this
 92 */
 93static u32 sfi_use_memremap __read_mostly;
 94
 95/*
 96 * sfi_un/map_memory calls early_memremap/memunmap which is a __init function
 97 * and introduces section mismatch. So use __ref to make it calm.
 98 */
 99static void __iomem * __ref sfi_map_memory(u64 phys, u32 size)
100{
101	if (!phys || !size)
102		return NULL;
103
104	if (sfi_use_memremap)
105		return memremap(phys, size, MEMREMAP_WB);
106	else
107		return early_memremap(phys, size);
108}
109
110static void __ref sfi_unmap_memory(void __iomem *virt, u32 size)
111{
112	if (!virt || !size)
113		return;
114
115	if (sfi_use_memremap)
116		memunmap(virt);
117	else
118		early_memunmap(virt, size);
119}
120
121static void sfi_print_table_header(unsigned long long pa,
122				struct sfi_table_header *header)
123{
124	pr_info("%4.4s %llX, %04X (v%d %6.6s %8.8s)\n",
125		header->sig, pa,
126		header->len, header->rev, header->oem_id,
127		header->oem_table_id);
128}
129
130/*
131 * sfi_verify_table()
132 * Sanity check table lengh, calculate checksum
133 */
134static int sfi_verify_table(struct sfi_table_header *table)
135{
136
137	u8 checksum = 0;
138	u8 *puchar = (u8 *)table;
139	u32 length = table->len;
140
141	/* Sanity check table length against arbitrary 1MB limit */
142	if (length > 0x100000) {
143		pr_err("Invalid table length 0x%x\n", length);
144		return -1;
145	}
146
147	while (length--)
148		checksum += *puchar++;
149
150	if (checksum) {
151		pr_err("Checksum %2.2X should be %2.2X\n",
152			table->csum, table->csum - checksum);
153		return -1;
154	}
155	return 0;
156}
157
158/*
159 * sfi_map_table()
160 *
161 * Return address of mapped table
162 * Check for common case that we can re-use mapping to SYST,
163 * which requires syst_pa, syst_va to be initialized.
164 */
165static struct sfi_table_header *sfi_map_table(u64 pa)
166{
167	struct sfi_table_header *th;
168	u32 length;
169
170	if (!TABLE_ON_PAGE(syst_pa, pa, sizeof(struct sfi_table_header)))
171		th = sfi_map_memory(pa, sizeof(struct sfi_table_header));
172	else
173		th = (void *)syst_va + (pa - syst_pa);
174
175	 /* If table fits on same page as its header, we are done */
176	if (TABLE_ON_PAGE(th, th, th->len))
177		return th;
178
179	/* Entire table does not fit on same page as SYST */
180	length = th->len;
181	if (!TABLE_ON_PAGE(syst_pa, pa, sizeof(struct sfi_table_header)))
182		sfi_unmap_memory(th, sizeof(struct sfi_table_header));
183
184	return sfi_map_memory(pa, length);
185}
186
187/*
188 * sfi_unmap_table()
189 *
190 * Undoes effect of sfi_map_table() by unmapping table
191 * if it did not completely fit on same page as SYST.
192 */
193static void sfi_unmap_table(struct sfi_table_header *th)
194{
195	if (!TABLE_ON_PAGE(syst_va, th, th->len))
196		sfi_unmap_memory(th, TABLE_ON_PAGE(th, th, th->len) ?
197					sizeof(*th) : th->len);
198}
199
200static int sfi_table_check_key(struct sfi_table_header *th,
201				struct sfi_table_key *key)
202{
203
204	if (strncmp(th->sig, key->sig, SFI_SIGNATURE_SIZE)
205		|| (key->oem_id && strncmp(th->oem_id,
206				key->oem_id, SFI_OEM_ID_SIZE))
207		|| (key->oem_table_id && strncmp(th->oem_table_id,
208				key->oem_table_id, SFI_OEM_TABLE_ID_SIZE)))
209		return -1;
210
211	return 0;
212}
213
214/*
215 * This function will be used in 2 cases:
216 * 1. used to enumerate and verify the tables addressed by SYST/XSDT,
217 *    thus no signature will be given (in kernel boot phase)
218 * 2. used to parse one specific table, signature must exist, and
219 *    the mapped virt address will be returned, and the virt space
220 *    will be released by call sfi_put_table() later
221 *
222 * This two cases are from two different functions with two different
223 * sections and causes section mismatch warning. So use __ref to tell
224 * modpost not to make any noise.
225 *
226 * Return value:
227 *	NULL:			when can't find a table matching the key
228 *	ERR_PTR(error):		error value
229 *	virt table address:	when a matched table is found
230 */
231struct sfi_table_header *
232 __ref sfi_check_table(u64 pa, struct sfi_table_key *key)
233{
234	struct sfi_table_header *th;
235	void *ret = NULL;
236
237	th = sfi_map_table(pa);
238	if (!th)
239		return ERR_PTR(-ENOMEM);
240
241	if (!key->sig) {
242		sfi_print_table_header(pa, th);
243		if (sfi_verify_table(th))
244			ret = ERR_PTR(-EINVAL);
245	} else {
246		if (!sfi_table_check_key(th, key))
247			return th;	/* Success */
248	}
249
250	sfi_unmap_table(th);
251	return ret;
252}
253
254/*
255 * sfi_get_table()
256 *
257 * Search SYST for the specified table with the signature in
258 * the key, and return the mapped table
259 */
260struct sfi_table_header *sfi_get_table(struct sfi_table_key *key)
261{
262	struct sfi_table_header *th;
263	u32 tbl_cnt, i;
264
265	tbl_cnt = SFI_GET_NUM_ENTRIES(syst_va, u64);
266	for (i = 0; i < tbl_cnt; i++) {
267		th = sfi_check_table(syst_va->pentry[i], key);
268		if (!IS_ERR(th) && th)
269			return th;
270	}
271
272	return NULL;
273}
274
275void sfi_put_table(struct sfi_table_header *th)
276{
277	sfi_unmap_table(th);
278}
279
280/* Find table with signature, run handler on it */
281int sfi_table_parse(char *signature, char *oem_id, char *oem_table_id,
282			sfi_table_handler handler)
283{
284	struct sfi_table_header *table = NULL;
285	struct sfi_table_key key;
286	int ret = -EINVAL;
287
288	if (sfi_disabled || !handler || !signature)
289		goto exit;
290
291	key.sig = signature;
292	key.oem_id = oem_id;
293	key.oem_table_id = oem_table_id;
294
295	table = sfi_get_table(&key);
296	if (!table)
297		goto exit;
298
299	ret = handler(table);
300	sfi_put_table(table);
301exit:
302	return ret;
303}
304EXPORT_SYMBOL_GPL(sfi_table_parse);
305
306/*
307 * sfi_parse_syst()
308 * Checksum all the tables in SYST and print their headers
309 *
310 * success: set syst_va, return 0
311 */
312static int __init sfi_parse_syst(void)
313{
314	struct sfi_table_key key = SFI_ANY_KEY;
315	int tbl_cnt, i;
316	void *ret;
317
318	syst_va = sfi_map_memory(syst_pa, sizeof(struct sfi_table_simple));
319	if (!syst_va)
320		return -ENOMEM;
321
322	tbl_cnt = SFI_GET_NUM_ENTRIES(syst_va, u64);
323	for (i = 0; i < tbl_cnt; i++) {
324		ret = sfi_check_table(syst_va->pentry[i], &key);
325		if (IS_ERR(ret))
326			return PTR_ERR(ret);
327	}
328
329	return 0;
330}
331
332/*
333 * The OS finds the System Table by searching 16-byte boundaries between
334 * physical address 0x000E0000 and 0x000FFFFF. The OS shall search this region
335 * starting at the low address and shall stop searching when the 1st valid SFI
336 * System Table is found.
337 *
338 * success: set syst_pa, return 0
339 * fail: return -1
340 */
341static __init int sfi_find_syst(void)
342{
343	unsigned long offset, len;
344	void *start;
345
346	len = SFI_SYST_SEARCH_END - SFI_SYST_SEARCH_BEGIN;
347	start = sfi_map_memory(SFI_SYST_SEARCH_BEGIN, len);
348	if (!start)
349		return -1;
350
351	for (offset = 0; offset < len; offset += 16) {
352		struct sfi_table_header *syst_hdr;
353
354		syst_hdr = start + offset;
355		if (strncmp(syst_hdr->sig, SFI_SIG_SYST,
356				SFI_SIGNATURE_SIZE))
357			continue;
358
359		if (syst_hdr->len > PAGE_SIZE)
360			continue;
361
362		sfi_print_table_header(SFI_SYST_SEARCH_BEGIN + offset,
363					syst_hdr);
364
365		if (sfi_verify_table(syst_hdr))
366			continue;
367
368		/*
369		 * Enforce SFI spec mandate that SYST reside within a page.
370		 */
371		if (!ON_SAME_PAGE(syst_pa, syst_pa + syst_hdr->len)) {
372			pr_info("SYST 0x%llx + 0x%x crosses page\n",
373					syst_pa, syst_hdr->len);
374			continue;
375		}
376
377		/* Success */
378		syst_pa = SFI_SYST_SEARCH_BEGIN + offset;
379		sfi_unmap_memory(start, len);
380		return 0;
381	}
382
383	sfi_unmap_memory(start, len);
384	return -1;
385}
386
387static struct kobject *sfi_kobj;
388static struct kobject *tables_kobj;
389
390static ssize_t sfi_table_show(struct file *filp, struct kobject *kobj,
391			       struct bin_attribute *bin_attr, char *buf,
392			       loff_t offset, size_t count)
393{
394	struct sfi_table_attr *tbl_attr =
395	    container_of(bin_attr, struct sfi_table_attr, attr);
396	struct sfi_table_header *th = NULL;
397	struct sfi_table_key key;
398	ssize_t cnt;
399
400	key.sig = tbl_attr->name;
401	key.oem_id = NULL;
402	key.oem_table_id = NULL;
403
404	if (strncmp(SFI_SIG_SYST, tbl_attr->name, SFI_SIGNATURE_SIZE)) {
405		th = sfi_get_table(&key);
406		if (!th)
407			return 0;
408
409		cnt =  memory_read_from_buffer(buf, count, &offset,
410						th, th->len);
411		sfi_put_table(th);
412	} else
413		cnt =  memory_read_from_buffer(buf, count, &offset,
414					syst_va, syst_va->header.len);
415
416	return cnt;
417}
418
419struct sfi_table_attr __init *sfi_sysfs_install_table(u64 pa)
420{
421	struct sfi_table_attr *tbl_attr;
422	struct sfi_table_header *th;
423	int ret;
424
425	tbl_attr = kzalloc(sizeof(struct sfi_table_attr), GFP_KERNEL);
426	if (!tbl_attr)
427		return NULL;
428
429	th = sfi_map_table(pa);
430	if (!th || !th->sig[0]) {
431		kfree(tbl_attr);
432		return NULL;
433	}
434
435	sysfs_attr_init(&tbl_attr->attr.attr);
436	memcpy(tbl_attr->name, th->sig, SFI_SIGNATURE_SIZE);
437
438	tbl_attr->attr.size = 0;
439	tbl_attr->attr.read = sfi_table_show;
440	tbl_attr->attr.attr.name = tbl_attr->name;
441	tbl_attr->attr.attr.mode = 0400;
442
443	ret = sysfs_create_bin_file(tables_kobj,
444				  &tbl_attr->attr);
445	if (ret) {
446		kfree(tbl_attr);
447		tbl_attr = NULL;
448	}
449
450	sfi_unmap_table(th);
451	return tbl_attr;
452}
453
454static int __init sfi_sysfs_init(void)
455{
456	int tbl_cnt, i;
457
458	if (sfi_disabled)
459		return 0;
460
461	sfi_kobj = kobject_create_and_add("sfi", firmware_kobj);
462	if (!sfi_kobj)
463		return 0;
464
465	tables_kobj = kobject_create_and_add("tables", sfi_kobj);
466	if (!tables_kobj) {
467		kobject_put(sfi_kobj);
468		return 0;
469	}
470
471	sfi_sysfs_install_table(syst_pa);
472
473	tbl_cnt = SFI_GET_NUM_ENTRIES(syst_va, u64);
474
475	for (i = 0; i < tbl_cnt; i++)
476		sfi_sysfs_install_table(syst_va->pentry[i]);
477
478	sfi_acpi_sysfs_init();
479	kobject_uevent(sfi_kobj, KOBJ_ADD);
480	kobject_uevent(tables_kobj, KOBJ_ADD);
481	pr_info("SFI sysfs interfaces init success\n");
482	return 0;
483}
484
485void __init sfi_init(void)
486{
487	if (!acpi_disabled)
488		disable_sfi();
489
490	if (sfi_disabled)
491		return;
492
493	pr_info("Simple Firmware Interface v0.81 http://simplefirmware.org\n");
494
495	if (sfi_find_syst() || sfi_parse_syst() || sfi_platform_init())
496		disable_sfi();
497
498	return;
499}
500
501void __init sfi_init_late(void)
502{
503	int length;
504
505	if (sfi_disabled)
506		return;
507
508	length = syst_va->header.len;
509	sfi_unmap_memory(syst_va, sizeof(struct sfi_table_simple));
510
511	/* Use memremap now after it is ready */
512	sfi_use_memremap = 1;
513	syst_va = sfi_map_memory(syst_pa, length);
514
515	sfi_acpi_init();
516}
517
518/*
519 * The reason we put it here because we need wait till the /sys/firmware
520 * is setup, then our interface can be registered in /sys/firmware/sfi
521 */
522core_initcall(sfi_sysfs_init);