uboot/arch/x86/lib/sfi.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Copyright (c) 2015 Google, Inc
   4 * Written by Simon Glass <sjg@chromium.org>
   5 */
   6
   7/*
   8 * Intel Simple Firmware Interface (SFI)
   9 *
  10 * Yet another way to pass information to the Linux kernel.
  11 *
  12 * See https://simplefirmware.org/ for details
  13 */
  14
  15#include <common.h>
  16#include <cpu.h>
  17#include <dm.h>
  18#include <asm/cpu.h>
  19#include <asm/ioapic.h>
  20#include <asm/sfi.h>
  21#include <asm/tables.h>
  22#include <dm/uclass-internal.h>
  23
  24struct table_info {
  25        u32 base;
  26        int ptr;
  27        u32 entry_start;
  28        u64 table[SFI_TABLE_MAX_ENTRIES];
  29        int count;
  30};
  31
  32static void *get_entry_start(struct table_info *tab)
  33{
  34        if (tab->count == SFI_TABLE_MAX_ENTRIES)
  35                return NULL;
  36        tab->entry_start = tab->base + tab->ptr;
  37        tab->table[tab->count] = tab->entry_start;
  38        tab->entry_start += sizeof(struct sfi_table_header);
  39
  40        return (void *)(uintptr_t)tab->entry_start;
  41}
  42
  43static void finish_table(struct table_info *tab, const char *sig, void *entry)
  44{
  45        struct sfi_table_header *hdr;
  46
  47        hdr = (struct sfi_table_header *)(uintptr_t)(tab->base + tab->ptr);
  48        strcpy(hdr->sig, sig);
  49        hdr->len = sizeof(*hdr) + ((ulong)entry - tab->entry_start);
  50        hdr->rev = 1;
  51        strncpy(hdr->oem_id, "U-Boot", SFI_OEM_ID_SIZE);
  52        strncpy(hdr->oem_table_id, "Table v1", SFI_OEM_TABLE_ID_SIZE);
  53        hdr->csum = 0;
  54        hdr->csum = table_compute_checksum(hdr, hdr->len);
  55        tab->ptr += hdr->len;
  56        tab->ptr = ALIGN(tab->ptr, 16);
  57        tab->count++;
  58}
  59
  60static int sfi_write_system_header(struct table_info *tab)
  61{
  62        u64 *entry = get_entry_start(tab);
  63        int i;
  64
  65        if (!entry)
  66                return -ENOSPC;
  67
  68        for (i = 0; i < tab->count; i++)
  69                *entry++ = tab->table[i];
  70        finish_table(tab, SFI_SIG_SYST, entry);
  71
  72        return 0;
  73}
  74
  75static int sfi_write_cpus(struct table_info *tab)
  76{
  77        struct sfi_cpu_table_entry *entry = get_entry_start(tab);
  78        struct udevice *dev;
  79        int count = 0;
  80
  81        if (!entry)
  82                return -ENOSPC;
  83
  84        for (uclass_find_first_device(UCLASS_CPU, &dev);
  85             dev;
  86             uclass_find_next_device(&dev)) {
  87                struct cpu_platdata *plat = dev_get_parent_platdata(dev);
  88
  89                if (!device_active(dev))
  90                        continue;
  91                entry->apic_id = plat->cpu_id;
  92                entry++;
  93                count++;
  94        }
  95
  96        /* Omit the table if there is only one CPU */
  97        if (count > 1)
  98                finish_table(tab, SFI_SIG_CPUS, entry);
  99
 100        return 0;
 101}
 102
 103static int sfi_write_apic(struct table_info *tab)
 104{
 105        struct sfi_apic_table_entry *entry = get_entry_start(tab);
 106
 107        if (!entry)
 108                return -ENOSPC;
 109
 110        entry->phys_addr = IO_APIC_ADDR;
 111        entry++;
 112        finish_table(tab, SFI_SIG_APIC, entry);
 113
 114        return 0;
 115}
 116
 117static int sfi_write_xsdt(struct table_info *tab)
 118{
 119        struct sfi_xsdt_header *entry = get_entry_start(tab);
 120
 121        if (!entry)
 122                return -ENOSPC;
 123
 124        entry->oem_revision = 1;
 125        entry->creator_id = 1;
 126        entry->creator_revision = 1;
 127        entry++;
 128        finish_table(tab, SFI_SIG_XSDT, entry);
 129
 130        return 0;
 131}
 132
 133ulong write_sfi_table(ulong base)
 134{
 135        struct table_info table;
 136
 137        table.base = base;
 138        table.ptr = 0;
 139        table.count = 0;
 140        sfi_write_cpus(&table);
 141        sfi_write_apic(&table);
 142
 143        /*
 144         * The SFI specification marks the XSDT table as option, but Linux 4.0
 145         * crashes on start-up when it is not provided.
 146         */
 147        sfi_write_xsdt(&table);
 148
 149        /* Finally, write out the system header which points to the others */
 150        sfi_write_system_header(&table);
 151
 152        return base + table.ptr;
 153}
 154