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
  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
  93/*
  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)
  98{
  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
 108static void __ref sfi_unmap_memory(void __iomem *virt, u32 size)
 109{
 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 */
 132static int sfi_verify_table(struct sfi_table_header *table)
 133{
 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 *
 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.
 223 *
 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
 228 */
 229struct sfi_table_header *
 230 __ref sfi_check_table(u64 pa, struct sfi_table_key *key)
 231{
 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}
 417