blob: ad664a6ee02904cd5a500ccd8d5cfbcba676f61a [file] [log] [blame]
Angel Pons585495e2020-04-03 01:21:38 +02001/* SPDX-License-Identifier: GPL-2.0-only */
Gerd Hoffmann2b962032013-05-30 15:41:48 +02002
Kyösti Mälkki36955932019-03-03 17:43:29 +02003#include <endian.h>
Elyes HAOUAS70a03dd2019-12-02 20:47:50 +01004#include <stdlib.h>
Gerd Hoffmann2b962032013-05-30 15:41:48 +02005#include <string.h>
Gerd Hoffmanne851a682013-11-13 12:56:30 +01006#include <smbios.h>
Gerd Hoffmann2b962032013-05-30 15:41:48 +02007#include <console/console.h>
8#include <arch/io.h>
Furquan Shaikh76cedd22020-05-02 10:24:23 -07009#include <acpi/acpi.h>
Ryan Salsamendid4f994b2017-06-11 19:39:06 -070010#include <commonlib/endian.h>
Gerd Hoffmann2b962032013-05-30 15:41:48 +020011
12#include "fw_cfg.h"
13#include "fw_cfg_if.h"
14
15#define FW_CFG_PORT_CTL 0x0510
16#define FW_CFG_PORT_DATA 0x0511
Himanshu Sahdev8dc95dd2019-09-12 12:02:54 +053017#define FW_CFG_DMA_ADDR_HIGH 0x0514
18#define FW_CFG_DMA_ADDR_LOW 0x0518
Gerd Hoffmann2b962032013-05-30 15:41:48 +020019
Thomas Heijligen0065b692019-01-10 19:42:21 +010020static int fw_cfg_detected;
Himanshu Sahdev8dc95dd2019-09-12 12:02:54 +053021static uint8_t fw_ver;
22
23static void fw_cfg_dma(int control, void *buf, int len);
Gerd Hoffmann2b962032013-05-30 15:41:48 +020024
25static int fw_cfg_present(void)
26{
27 static const char qsig[] = "QEMU";
Himanshu Sahdev8dc95dd2019-09-12 12:02:54 +053028 unsigned char sig[FW_CFG_SIG_SIZE];
Thomas Heijligen0065b692019-01-10 19:42:21 +010029 int detected = 0;
Gerd Hoffmann2b962032013-05-30 15:41:48 +020030
Thomas Heijligen0065b692019-01-10 19:42:21 +010031 if (fw_cfg_detected == 0) {
Gerd Hoffmann2b962032013-05-30 15:41:48 +020032 fw_cfg_get(FW_CFG_SIGNATURE, sig, sizeof(sig));
Himanshu Sahdev8dc95dd2019-09-12 12:02:54 +053033 detected = memcmp(sig, qsig, FW_CFG_SIG_SIZE) == 0;
Gerd Hoffmann2b962032013-05-30 15:41:48 +020034 printk(BIOS_INFO, "QEMU: firmware config interface %s\n",
Thomas Heijligen0065b692019-01-10 19:42:21 +010035 detected ? "detected" : "not found");
Himanshu Sahdev8dc95dd2019-09-12 12:02:54 +053036 if (detected) {
37 fw_cfg_get(FW_CFG_ID, &fw_ver, sizeof(fw_ver));
38 printk(BIOS_INFO, "Firmware config version id: %d\n", fw_ver);
39 }
Thomas Heijligen0065b692019-01-10 19:42:21 +010040 fw_cfg_detected = detected + 1;
Gerd Hoffmann2b962032013-05-30 15:41:48 +020041 }
Thomas Heijligen0065b692019-01-10 19:42:21 +010042 return fw_cfg_detected - 1;
Gerd Hoffmann2b962032013-05-30 15:41:48 +020043}
44
Thomas Heijligena05f8a92019-01-10 12:55:59 +010045static void fw_cfg_select(uint16_t entry)
Gerd Hoffmann2b962032013-05-30 15:41:48 +020046{
Alper Nebi Yasakf6ea67c2024-02-06 21:28:16 +030047 if (fw_ver & FW_CFG_VERSION_DMA)
48 fw_cfg_dma(FW_CFG_DMA_CTL_SELECT | entry << 16, NULL, 0);
49 else
50 outw(entry, FW_CFG_PORT_CTL);
Thomas Heijligena05f8a92019-01-10 12:55:59 +010051}
52
53static void fw_cfg_read(void *dst, int dstlen)
54{
Himanshu Sahdev8dc95dd2019-09-12 12:02:54 +053055 if (fw_ver & FW_CFG_VERSION_DMA)
56 fw_cfg_dma(FW_CFG_DMA_CTL_READ, dst, dstlen);
57 else
58 insb(FW_CFG_PORT_DATA, dst, dstlen);
Gerd Hoffmann2b962032013-05-30 15:41:48 +020059}
60
Thomas Heijligena05f8a92019-01-10 12:55:59 +010061void fw_cfg_get(uint16_t entry, void *dst, int dstlen)
62{
63 fw_cfg_select(entry);
64 fw_cfg_read(dst, dstlen);
65}
66
Thomas Heijligen721c8b42019-01-10 17:39:00 +010067static int fw_cfg_find_file(FWCfgFile *file, const char *name)
Gerd Hoffmann289b45f2013-06-12 10:18:58 +020068{
Thomas Heijligen721c8b42019-01-10 17:39:00 +010069 uint32_t count = 0;
Gerd Hoffmann289b45f2013-06-12 10:18:58 +020070
Thomas Heijligen721c8b42019-01-10 17:39:00 +010071 fw_cfg_select(FW_CFG_FILE_DIR);
72 fw_cfg_read(&count, sizeof(count));
73 count = be32_to_cpu(count);
Gerd Hoffmann289b45f2013-06-12 10:18:58 +020074
Thomas Heijligen721c8b42019-01-10 17:39:00 +010075 for (int i = 0; i < count; i++) {
76 fw_cfg_read(file, sizeof(*file));
77 if (strcmp(file->name, name) == 0) {
78 file->size = be32_to_cpu(file->size);
79 file->select = be16_to_cpu(file->select);
Nico Huberef59c932020-07-14 12:19:07 +020080 printk(BIOS_INFO, "QEMU: firmware config: Found '%s'\n", name);
Thomas Heijligen721c8b42019-01-10 17:39:00 +010081 return 0;
82 }
Gerd Hoffmann289b45f2013-06-12 10:18:58 +020083 }
Nico Huberef59c932020-07-14 12:19:07 +020084 printk(BIOS_INFO, "QEMU: firmware config: Couldn't find '%s'\n", name);
Thomas Heijligen721c8b42019-01-10 17:39:00 +010085 return -1;
Gerd Hoffmann289b45f2013-06-12 10:18:58 +020086}
87
Thomas Heijligenbcd84fe2019-01-10 16:53:34 +010088int fw_cfg_check_file(FWCfgFile *file, const char *name)
Gerd Hoffmann289b45f2013-06-12 10:18:58 +020089{
Gerd Hoffmann289b45f2013-06-12 10:18:58 +020090 if (!fw_cfg_present())
91 return -1;
Thomas Heijligen721c8b42019-01-10 17:39:00 +010092 return fw_cfg_find_file(file, name);
Gerd Hoffmann289b45f2013-06-12 10:18:58 +020093}
94
Thomas Heijligenda9aa6d2019-01-10 18:19:06 +010095static int fw_cfg_e820_select(uint32_t *size)
96{
97 FWCfgFile file;
98
99 if (!fw_cfg_present() || fw_cfg_find_file(&file, "etc/e820"))
100 return -1;
101 fw_cfg_select(file.select);
102 *size = file.size;
103 return 0;
104}
105
106static int fw_cfg_e820_read(FwCfgE820Entry *entry, uint32_t *size,
107 uint32_t *pos)
108{
109 if (*pos + sizeof(*entry) > *size)
110 return -1;
111
112 fw_cfg_read(entry, sizeof(*entry));
113 *pos += sizeof(*entry);
114 return 0;
115}
116
117/* returns tolud on success or 0 on failure */
118uintptr_t fw_cfg_tolud(void)
119{
120 FwCfgE820Entry e;
121 uint64_t top = 0;
122 uint32_t size = 0, pos = 0;
123
Nico Huberd2245d82020-07-14 12:21:19 +0200124 if (fw_cfg_e820_select(&size) == 0) {
Thomas Heijligenda9aa6d2019-01-10 18:19:06 +0100125 while (!fw_cfg_e820_read(&e, &size, &pos)) {
126 uint64_t limit = e.address + e.length;
127 if (e.type == 1 && limit < 4ULL * GiB && limit > top)
128 top = limit;
129 }
130 }
131 return (uintptr_t)top;
132}
133
Gerd Hoffmann2b962032013-05-30 15:41:48 +0200134int fw_cfg_max_cpus(void)
135{
136 unsigned short max_cpus;
137
138 if (!fw_cfg_present())
Angel Ponsd16d00b2021-11-03 16:42:48 +0100139 return 0;
Gerd Hoffmann2b962032013-05-30 15:41:48 +0200140
141 fw_cfg_get(FW_CFG_MAX_CPUS, &max_cpus, sizeof(max_cpus));
142 return max_cpus;
143}
Gerd Hoffmannd69da842013-07-25 15:59:07 +0200144
145/* ---------------------------------------------------------------------- */
Gerd Hoffmann590e8d42013-12-03 09:40:39 +0100146
Gerd Hoffmannd69da842013-07-25 15:59:07 +0200147/*
Elyes HAOUAS8ab989e2016-07-30 17:46:17 +0200148 * Starting with release 1.7 qemu provides ACPI tables via fw_cfg.
Gerd Hoffmannd69da842013-07-25 15:59:07 +0200149 * Main advantage is that new (virtual) hardware which needs acpi
150 * support JustWorks[tm] without having to patch & update the firmware
151 * (seabios, coreboot, ...) accordingly.
152 *
153 * Qemu provides a etc/table-loader file with instructions for the
154 * firmware:
Elyes HAOUAS8ab989e2016-07-30 17:46:17 +0200155 * - A "load" instruction to fetch ACPI data from fw_cfg.
Gerd Hoffmannd69da842013-07-25 15:59:07 +0200156 * - A "pointer" instruction to patch a pointer. This is needed to
157 * get table-to-table references right, it is basically a
Elyes HAOUAS8ab989e2016-07-30 17:46:17 +0200158 * primitive dynamic linker for ACPI tables.
159 * - A "checksum" instruction to generate ACPI table checksums.
Gerd Hoffmannd69da842013-07-25 15:59:07 +0200160 *
161 * If a etc/table-loader file is found we'll go try loading the acpi
Elyes HAOUAS8ab989e2016-07-30 17:46:17 +0200162 * tables from fw_cfg, otherwise we'll fallback to the ACPI tables
Gerd Hoffmannd69da842013-07-25 15:59:07 +0200163 * compiled in.
164 */
165
166#define BIOS_LINKER_LOADER_FILESZ 56
167
168struct BiosLinkerLoaderEntry {
169 uint32_t command;
170 union {
171 /*
172 * COMMAND_ALLOCATE - allocate a table from @alloc.file
173 * subject to @alloc.align alignment (must be power of 2)
174 * and @alloc.zone (can be HIGH or FSEG) requirements.
175 *
176 * Must appear exactly once for each file, and before
177 * this file is referenced by any other command.
178 */
179 struct {
180 char file[BIOS_LINKER_LOADER_FILESZ];
181 uint32_t align;
182 uint8_t zone;
183 } alloc;
184
185 /*
186 * COMMAND_ADD_POINTER - patch the table (originating from
187 * @dest_file) at @pointer.offset, by adding a pointer to the table
188 * originating from @src_file. 1,2,4 or 8 byte unsigned
189 * addition is used depending on @pointer.size.
190 */
191 struct {
192 char dest_file[BIOS_LINKER_LOADER_FILESZ];
193 char src_file[BIOS_LINKER_LOADER_FILESZ];
194 uint32_t offset;
195 uint8_t size;
196 } pointer;
197
198 /*
199 * COMMAND_ADD_CHECKSUM - calculate checksum of the range specified by
200 * @cksum_start and @cksum_length fields,
201 * and then add the value at @cksum.offset.
202 * Checksum simply sums -X for each byte X in the range
203 * using 8-bit math.
204 */
205 struct {
206 char file[BIOS_LINKER_LOADER_FILESZ];
207 uint32_t offset;
208 uint32_t start;
209 uint32_t length;
210 } cksum;
211
212 /* padding */
213 char pad[124];
214 };
Stefan Reinauer6a001132017-07-13 02:20:27 +0200215} __packed;
Gerd Hoffmannd69da842013-07-25 15:59:07 +0200216typedef struct BiosLinkerLoaderEntry BiosLinkerLoaderEntry;
217
218enum {
219 BIOS_LINKER_LOADER_COMMAND_ALLOCATE = 0x1,
220 BIOS_LINKER_LOADER_COMMAND_ADD_POINTER = 0x2,
221 BIOS_LINKER_LOADER_COMMAND_ADD_CHECKSUM = 0x3,
222};
223
224enum {
225 BIOS_LINKER_LOADER_ALLOC_ZONE_HIGH = 0x1,
226 BIOS_LINKER_LOADER_ALLOC_ZONE_FSEG = 0x2,
227};
228
229unsigned long fw_cfg_acpi_tables(unsigned long start)
230{
Thomas Heijligenbcd84fe2019-01-10 16:53:34 +0100231 FWCfgFile f;
Gerd Hoffmannd69da842013-07-25 15:59:07 +0200232 BiosLinkerLoaderEntry *s;
233 unsigned long *addrs, current;
Ryan Salsamendid4f994b2017-06-11 19:39:06 -0700234 uint8_t *ptr;
Thomas Heijligenbcd84fe2019-01-10 16:53:34 +0100235 int i, j, src, dst, max;
Gerd Hoffmannd69da842013-07-25 15:59:07 +0200236
Thomas Heijligenbcd84fe2019-01-10 16:53:34 +0100237 if (fw_cfg_check_file(&f, "etc/table-loader"))
Gerd Hoffmannd69da842013-07-25 15:59:07 +0200238 return 0;
239
Elyes HAOUAS8ab989e2016-07-30 17:46:17 +0200240 printk(BIOS_DEBUG, "QEMU: found ACPI tables in fw_cfg.\n");
Gerd Hoffmannd69da842013-07-25 15:59:07 +0200241
Thomas Heijligenbcd84fe2019-01-10 16:53:34 +0100242 max = f.size / sizeof(*s);
243 s = malloc(f.size);
Gerd Hoffmannd69da842013-07-25 15:59:07 +0200244 addrs = malloc(max * sizeof(*addrs));
Thomas Heijligenbcd84fe2019-01-10 16:53:34 +0100245 fw_cfg_get(f.select, s, f.size);
Gerd Hoffmannd69da842013-07-25 15:59:07 +0200246
247 current = start;
248 for (i = 0; i < max && s[i].command != 0; i++) {
Ryan Salsamendid4f994b2017-06-11 19:39:06 -0700249 void *cksum_data;
250 uint32_t cksum;
251 uint32_t addr4;
252 uint64_t addr8;
Gerd Hoffmannd69da842013-07-25 15:59:07 +0200253 switch (s[i].command) {
254 case BIOS_LINKER_LOADER_COMMAND_ALLOCATE:
Elyes Haouasd6b6b222022-10-10 12:34:21 +0200255 current = ALIGN_UP(current, s[i].alloc.align);
Thomas Heijligenbcd84fe2019-01-10 16:53:34 +0100256 if (fw_cfg_check_file(&f, s[i].alloc.file))
Gerd Hoffmannd69da842013-07-25 15:59:07 +0200257 goto err;
Thomas Heijligenbcd84fe2019-01-10 16:53:34 +0100258
Gerd Hoffmannacfe1e52014-08-27 13:21:43 +0200259 printk(BIOS_DEBUG, "QEMU: loading \"%s\" to 0x%lx (len %d)\n",
Thomas Heijligenbcd84fe2019-01-10 16:53:34 +0100260 s[i].alloc.file, current, f.size);
Patrick Rudolph87abccd2019-02-23 11:45:01 +0100261 fw_cfg_get(f.select, (void *)current, f.size);
Gerd Hoffmannd69da842013-07-25 15:59:07 +0200262 addrs[i] = current;
Thomas Heijligenbcd84fe2019-01-10 16:53:34 +0100263 current += f.size;
Gerd Hoffmannd69da842013-07-25 15:59:07 +0200264 break;
265
266 case BIOS_LINKER_LOADER_COMMAND_ADD_POINTER:
267 src = -1; dst = -1;
268 for (j = 0; j < i; j++) {
269 if (s[j].command != BIOS_LINKER_LOADER_COMMAND_ALLOCATE)
270 continue;
271 if (strcmp(s[j].alloc.file, s[i].pointer.dest_file) == 0)
272 dst = j;
273 if (strcmp(s[j].alloc.file, s[i].pointer.src_file) == 0)
274 src = j;
275 }
276 if (src == -1 || dst == -1)
277 goto err;
278
279 switch (s[i].pointer.size) {
280 case 4:
Ryan Salsamendid4f994b2017-06-11 19:39:06 -0700281 ptr = (uint8_t *)addrs[dst];
282 ptr += s[i].pointer.offset;
283 addr4 = read_le32(ptr);
284 addr4 += addrs[src];
285 write_le32(ptr, addr4);
Gerd Hoffmannd69da842013-07-25 15:59:07 +0200286 break;
287
288 case 8:
Ryan Salsamendid4f994b2017-06-11 19:39:06 -0700289 ptr = (uint8_t *)addrs[dst];
290 ptr += s[i].pointer.offset;
291 addr8 = read_le64(ptr);
292 addr8 += addrs[src];
293 write_le64(ptr, addr8);
Gerd Hoffmannd69da842013-07-25 15:59:07 +0200294 break;
295
296 default:
297 /*
Elyes HAOUAS8ab989e2016-07-30 17:46:17 +0200298 * Should not happen. ACPI knows 1 and 2 byte ptrs
Gerd Hoffmannd69da842013-07-25 15:59:07 +0200299 * too, but we are operating with 32bit offsets which
300 * would simply not fit in there ...
301 */
302 printk(BIOS_DEBUG, "QEMU: acpi: unimplemented ptr size %d\n",
303 s[i].pointer.size);
304 goto err;
305 }
306 break;
307
308 case BIOS_LINKER_LOADER_COMMAND_ADD_CHECKSUM:
309 dst = -1;
310 for (j = 0; j < i; j++) {
311 if (s[j].command != BIOS_LINKER_LOADER_COMMAND_ALLOCATE)
312 continue;
313 if (strcmp(s[j].alloc.file, s[i].cksum.file) == 0)
314 dst = j;
315 }
316 if (dst == -1)
317 goto err;
318
Ryan Salsamendid4f994b2017-06-11 19:39:06 -0700319 ptr = (uint8_t *)(addrs[dst] + s[i].cksum.offset);
320 cksum_data = (void *)(addrs[dst] + s[i].cksum.start);
321 cksum = acpi_checksum(cksum_data, s[i].cksum.length);
Oleksii Kurochko4886a652018-04-20 15:38:57 +0300322 write_le8(ptr, cksum);
Gerd Hoffmannd69da842013-07-25 15:59:07 +0200323 break;
324
325 default:
326 printk(BIOS_DEBUG, "QEMU: acpi: unknown script cmd 0x%x @ %p\n",
327 s[i].command, s+i);
328 goto err;
329 };
330 }
331
Elyes HAOUAS8ab989e2016-07-30 17:46:17 +0200332 printk(BIOS_DEBUG, "QEMU: loaded ACPI tables from fw_cfg.\n");
Gerd Hoffmannd69da842013-07-25 15:59:07 +0200333 free(s);
334 free(addrs);
Elyes Haouasd6b6b222022-10-10 12:34:21 +0200335 return ALIGN_UP(current, 16);
Gerd Hoffmannd69da842013-07-25 15:59:07 +0200336
337err:
Elyes HAOUAS8ab989e2016-07-30 17:46:17 +0200338 printk(BIOS_DEBUG, "QEMU: loading ACPI tables from fw_cfg failed.\n");
Gerd Hoffmannd69da842013-07-25 15:59:07 +0200339 free(s);
340 free(addrs);
341 return 0;
342}
Gerd Hoffmanne851a682013-11-13 12:56:30 +0100343
344/* ---------------------------------------------------------------------- */
345/* pick up smbios information from fw_cfg */
346
Alper Nebi Yasakdd634182024-02-06 23:09:33 +0300347#if CONFIG(GENERATE_SMBIOS_TABLES)
Gerd Hoffmanne851a682013-11-13 12:56:30 +0100348static const char *type1_manufacturer;
349static const char *type1_product_name;
350static const char *type1_version;
351static const char *type1_serial_number;
352static const char *type1_family;
353static u8 type1_uuid[16];
354
355static void fw_cfg_smbios_init(void)
356{
357 static int done = 0;
358 uint16_t i, count = 0;
359 FwCfgSmbios entry;
360 char *buf;
361
362 if (done)
363 return;
364 done = 1;
365
366 fw_cfg_get(FW_CFG_SMBIOS_ENTRIES, &count, sizeof(count));
367 for (i = 0; i < count; i++) {
Alper Nebi Yasak2009f7c2024-02-07 11:02:59 +0300368 fw_cfg_read(&entry, sizeof(entry));
Gerd Hoffmanne851a682013-11-13 12:56:30 +0100369 buf = malloc(entry.length - sizeof(entry));
Alper Nebi Yasak2009f7c2024-02-07 11:02:59 +0300370 fw_cfg_read(buf, entry.length - sizeof(entry));
Gerd Hoffmanne851a682013-11-13 12:56:30 +0100371 if (entry.headertype == SMBIOS_FIELD_ENTRY &&
372 entry.tabletype == 1) {
373 switch (entry.fieldoffset) {
374 case offsetof(struct smbios_type1, manufacturer):
375 type1_manufacturer = strdup(buf);
376 break;
377 case offsetof(struct smbios_type1, product_name):
378 type1_product_name = strdup(buf);
379 break;
380 case offsetof(struct smbios_type1, version):
381 type1_version = strdup(buf);
382 break;
383 case offsetof(struct smbios_type1, serial_number):
384 type1_serial_number = strdup(buf);
385 break;
386 case offsetof(struct smbios_type1, family):
387 type1_family = strdup(buf);
388 break;
389 case offsetof(struct smbios_type1, uuid):
390 memcpy(type1_uuid, buf, 16);
391 break;
392 }
393 }
394 free(buf);
395 }
396}
397
Gerd Hoffmanndb9d1692014-08-27 11:25:13 +0200398static unsigned long smbios_next(unsigned long current)
399{
Angel Ponsfff1b2f2021-06-28 19:09:22 +0200400 struct smbios_header *header;
Gerd Hoffmanndb9d1692014-08-27 11:25:13 +0200401 int l, count = 0;
402 char *s;
403
Angel Ponsfff1b2f2021-06-28 19:09:22 +0200404 header = (void *)current;
405 current += header->length;
Gerd Hoffmanndb9d1692014-08-27 11:25:13 +0200406 for (;;) {
Angel Ponsfff1b2f2021-06-28 19:09:22 +0200407 s = (void *)current;
Gerd Hoffmanndb9d1692014-08-27 11:25:13 +0200408 l = strlen(s);
409 if (!l)
410 return current + (count ? 1 : 2);
411 current += l + 1;
412 count++;
413 }
414}
415
416/*
417 * Starting with version 2.1 qemu provides a full set of smbios tables
418 * for the virtual hardware emulated, except type 0 (bios information).
419 *
420 * What we are going to do here is find the type0 table, keep it, and
421 * override everything else generated by coreboot with the qemu smbios
422 * tables.
423 *
424 * It's a bit hackish, but qemu is a special case (compared to real
425 * hardware) and this way we don't need special qemu support in the
426 * generic smbios code.
427 */
428unsigned long fw_cfg_smbios_tables(int *handle, unsigned long *current)
429{
Thomas Heijligenbcd84fe2019-01-10 16:53:34 +0100430 FWCfgFile f;
Angel Ponsfff1b2f2021-06-28 19:09:22 +0200431 struct smbios_header *header;
Gerd Hoffmanndb9d1692014-08-27 11:25:13 +0200432 unsigned long start, end;
Thomas Heijligenbcd84fe2019-01-10 16:53:34 +0100433 int ret, i, count = 1;
Gerd Hoffmanndb9d1692014-08-27 11:25:13 +0200434 char *str;
435
Thomas Heijligenbcd84fe2019-01-10 16:53:34 +0100436 if (fw_cfg_check_file(&f, "etc/smbios/smbios-tables"))
Gerd Hoffmanndb9d1692014-08-27 11:25:13 +0200437 return 0;
Thomas Heijligenbcd84fe2019-01-10 16:53:34 +0100438
439 printk(BIOS_DEBUG, "QEMU: found smbios tables in fw_cfg (len %d).\n", f.size);
Gerd Hoffmanndb9d1692014-08-27 11:25:13 +0200440
441 /*
442 * Search backwards for "coreboot" (first string in type0 table,
443 * see src/arch/x86/boot/smbios.c), then find type0 table.
444 */
445 for (i = 0; i < 16384; i++) {
446 str = (char*)(*current - i);
447 if (strcmp(str, "coreboot") == 0)
448 break;
449 }
450 if (i == 16384)
451 return 0;
452 i += sizeof(struct smbios_type0) - 2;
Angel Ponsfff1b2f2021-06-28 19:09:22 +0200453 header = (struct smbios_header *)(*current - i);
454 if (header->type != SMBIOS_BIOS_INFORMATION || header->handle != 0)
Gerd Hoffmanndb9d1692014-08-27 11:25:13 +0200455 return 0;
456 printk(BIOS_DEBUG, "QEMU: coreboot type0 table found at 0x%lx.\n",
457 *current - i);
458 start = smbios_next(*current - i);
459
460 /*
461 * Fetch smbios tables from qemu, go find the end marker.
462 * We'll exclude the end marker as coreboot will add one.
463 */
464 printk(BIOS_DEBUG, "QEMU: loading smbios tables to 0x%lx\n", start);
Patrick Rudolph87abccd2019-02-23 11:45:01 +0100465 fw_cfg_get(f.select, (void *)start, f.size);
Gerd Hoffmanndb9d1692014-08-27 11:25:13 +0200466 end = start;
467 do {
Angel Ponsfff1b2f2021-06-28 19:09:22 +0200468 header = (struct smbios_header *)end;
469 if (header->type == SMBIOS_END_OF_TABLE)
Gerd Hoffmanndb9d1692014-08-27 11:25:13 +0200470 break;
471 end = smbios_next(end);
472 count++;
Thomas Heijligenbcd84fe2019-01-10 16:53:34 +0100473 } while (end < start + f.size);
Gerd Hoffmanndb9d1692014-08-27 11:25:13 +0200474
475 /* final fixups. */
476 ret = end - *current;
477 *current = end;
478 *handle = count;
479 return ret;
480}
481
Gerd Hoffmanne851a682013-11-13 12:56:30 +0100482const char *smbios_mainboard_manufacturer(void)
483{
484 fw_cfg_smbios_init();
485 return type1_manufacturer ?: CONFIG_MAINBOARD_SMBIOS_MANUFACTURER;
486}
487
488const char *smbios_mainboard_product_name(void)
489{
490 fw_cfg_smbios_init();
491 return type1_product_name ?: CONFIG_MAINBOARD_SMBIOS_PRODUCT_NAME;
492}
493
494const char *smbios_mainboard_version(void)
495{
496 fw_cfg_smbios_init();
497 return type1_version ?: CONFIG_MAINBOARD_VERSION;
498}
499
500const char *smbios_mainboard_serial_number(void)
501{
502 fw_cfg_smbios_init();
503 return type1_serial_number ?: CONFIG_MAINBOARD_SERIAL_NUMBER;
504}
505
Nico Huberebd8a4f2017-11-01 09:49:16 +0100506void smbios_system_set_uuid(u8 *uuid)
Gerd Hoffmanne851a682013-11-13 12:56:30 +0100507{
508 fw_cfg_smbios_init();
509 memcpy(uuid, type1_uuid, 16);
510}
Alper Nebi Yasakdd634182024-02-06 23:09:33 +0300511#endif /* CONFIG(GENERATE_SMBIOS_TABLES) */
Himanshu Sahdev8dc95dd2019-09-12 12:02:54 +0530512
513/*
514 * Configure DMA setup
515 */
516
517static void fw_cfg_dma(int control, void *buf, int len)
518{
519 volatile FwCfgDmaAccess dma;
520 uintptr_t dma_desc_addr;
521 uint32_t dma_desc_addr_hi, dma_desc_addr_lo;
522
523 dma.control = be32_to_cpu(control);
524 dma.length = be32_to_cpu(len);
525 dma.address = be64_to_cpu((uintptr_t)buf);
526
527 dma_desc_addr = (uintptr_t)&dma;
528 dma_desc_addr_lo = (uint32_t)(dma_desc_addr & 0xFFFFFFFFU);
529 dma_desc_addr_hi = sizeof(uintptr_t) > sizeof(uint32_t)
530 ? (uint32_t)(dma_desc_addr >> 32) : 0;
531
532 // Skip writing high half if unnecessary.
533 if (dma_desc_addr_hi)
534 outl(be32_to_cpu(dma_desc_addr_hi), FW_CFG_DMA_ADDR_HIGH);
535 outl(be32_to_cpu(dma_desc_addr_lo), FW_CFG_DMA_ADDR_LOW);
536
537 while (be32_to_cpu(dma.control) & ~FW_CFG_DMA_CTL_ERROR)
538 ;
539}