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