Home | History | Annotate | Download | only in lib
      1 // SPDX-License-Identifier: GPL-2.0+
      2 /*
      3  * Copyright (c) 2015 Google, Inc
      4  * Written by Simon Glass <sjg (at) 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 
     24 struct table_info {
     25 	u32 base;
     26 	int ptr;
     27 	u32 entry_start;
     28 	u64 table[SFI_TABLE_MAX_ENTRIES];
     29 	int count;
     30 };
     31 
     32 static 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 
     43 static 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 
     60 static 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 
     75 static 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 
    103 static 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 
    117 static 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 
    133 ulong 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