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