Tom Rini | 10e4779 | 2018-05-06 17:58:06 -0400 | [diff] [blame] | 1 | // SPDX-License-Identifier: GPL-2.0+ |
Simon Glass | 07e922a | 2015-04-28 20:25:10 -0600 | [diff] [blame] | 2 | /* |
| 3 | * Copyright (c) 2015 Google, Inc |
| 4 | * Written by Simon Glass <sjg@chromium.org> |
Simon Glass | 07e922a | 2015-04-28 20:25:10 -0600 | [diff] [blame] | 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 | |
Simon Glass | ca37a39 | 2017-01-16 07:03:35 -0700 | [diff] [blame] | 40 | return (void *)(uintptr_t)tab->entry_start; |
Simon Glass | 07e922a | 2015-04-28 20:25:10 -0600 | [diff] [blame] | 41 | } |
| 42 | |
| 43 | static void finish_table(struct table_info *tab, const char *sig, void *entry) |
| 44 | { |
| 45 | struct sfi_table_header *hdr; |
| 46 | |
Simon Glass | ca37a39 | 2017-01-16 07:03:35 -0700 | [diff] [blame] | 47 | hdr = (struct sfi_table_header *)(uintptr_t)(tab->base + tab->ptr); |
Simon Glass | 07e922a | 2015-04-28 20:25:10 -0600 | [diff] [blame] | 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 | |
Simon Glass | ca37a39 | 2017-01-16 07:03:35 -0700 | [diff] [blame] | 133 | ulong write_sfi_table(ulong base) |
Simon Glass | 07e922a | 2015-04-28 20:25:10 -0600 | [diff] [blame] | 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 | } |