]>
Commit | Line | Data |
---|---|---|
83d290c5 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
6388e357 SG |
2 | /* |
3 | * Copyright (c) 2015 Google, Inc | |
4 | * Written by Simon Glass <sjg@chromium.org> | |
6388e357 SG |
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 | ||
d678a59d | 15 | #include <common.h> |
6388e357 SG |
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 | ||
42fd8c19 | 40 | return (void *)(uintptr_t)tab->entry_start; |
6388e357 SG |
41 | } |
42 | ||
43 | static void finish_table(struct table_info *tab, const char *sig, void *entry) | |
44 | { | |
45 | struct sfi_table_header *hdr; | |
46 | ||
42fd8c19 | 47 | hdr = (struct sfi_table_header *)(uintptr_t)(tab->base + tab->ptr); |
6388e357 SG |
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)) { | |
8a8d24bd | 87 | struct cpu_plat *plat = dev_get_parent_plat(dev); |
6388e357 SG |
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 | ||
42fd8c19 | 133 | ulong write_sfi_table(ulong base) |
6388e357 SG |
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 | } |