linux/drivers/sfi/sfi_core.c
<<
>>
Prefs
   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_cache(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 because 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
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.