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 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