Simon Glass | 6388e35 | 2015-04-28 20:25:10 -0600 | [diff] [blame] | 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 | |
Simon Glass | 42fd8c1 | 2017-01-16 07:03:35 -0700 | [diff] [blame] | 41 | return (void *)(uintptr_t)tab->entry_start; |
Simon Glass | 6388e35 | 2015-04-28 20:25:10 -0600 | [diff] [blame] | 42 | } |
| 43 | |
| 44 | static void finish_table(struct table_info *tab, const char *sig, void *entry) |
| 45 | { |
| 46 | struct sfi_table_header *hdr; |
| 47 | |
Simon Glass | 42fd8c1 | 2017-01-16 07:03:35 -0700 | [diff] [blame] | 48 | hdr = (struct sfi_table_header *)(uintptr_t)(tab->base + tab->ptr); |
Simon Glass | 6388e35 | 2015-04-28 20:25:10 -0600 | [diff] [blame] | 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 | |
Simon Glass | 42fd8c1 | 2017-01-16 07:03:35 -0700 | [diff] [blame] | 134 | ulong write_sfi_table(ulong base) |
Simon Glass | 6388e35 | 2015-04-28 20:25:10 -0600 | [diff] [blame] | 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 | } |