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