coreboot
coreboot is an Open Source project aimed at replacing the proprietary BIOS found in most computers.
fw_cfg.c
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-only */
2 
3 #include <endian.h>
4 #include <stdlib.h>
5 #include <string.h>
6 #include <smbios.h>
7 #include <console/console.h>
8 #include <arch/io.h>
9 #include <acpi/acpi.h>
10 #include <commonlib/endian.h>
11 
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
17 #define FW_CFG_DMA_ADDR_HIGH 0x0514
18 #define FW_CFG_DMA_ADDR_LOW 0x0518
19 
20 static int fw_cfg_detected;
21 static uint8_t fw_ver;
22 
23 static void fw_cfg_dma(int control, void *buf, int len);
24 
25 static int fw_cfg_present(void)
26 {
27  static const char qsig[] = "QEMU";
28  unsigned char sig[FW_CFG_SIG_SIZE];
29  int detected = 0;
30 
31  if (fw_cfg_detected == 0) {
32  fw_cfg_get(FW_CFG_SIGNATURE, sig, sizeof(sig));
33  detected = memcmp(sig, qsig, FW_CFG_SIG_SIZE) == 0;
34  printk(BIOS_INFO, "QEMU: firmware config interface %s\n",
35  detected ? "detected" : "not found");
36  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  }
40  fw_cfg_detected = detected + 1;
41  }
42  return fw_cfg_detected - 1;
43 }
44 
45 static void fw_cfg_select(uint16_t entry)
46 {
47  outw(entry, FW_CFG_PORT_CTL);
48 }
49 
50 static void fw_cfg_read(void *dst, int dstlen)
51 {
53  fw_cfg_dma(FW_CFG_DMA_CTL_READ, dst, dstlen);
54  else
55  insb(FW_CFG_PORT_DATA, dst, dstlen);
56 }
57 
58 void fw_cfg_get(uint16_t entry, void *dst, int dstlen)
59 {
60  fw_cfg_select(entry);
61  fw_cfg_read(dst, dstlen);
62 }
63 
64 static int fw_cfg_find_file(FWCfgFile *file, const char *name)
65 {
66  uint32_t count = 0;
67 
69  fw_cfg_read(&count, sizeof(count));
70  count = be32_to_cpu(count);
71 
72  for (int i = 0; i < count; i++) {
73  fw_cfg_read(file, sizeof(*file));
74  if (strcmp(file->name, name) == 0) {
75  file->size = be32_to_cpu(file->size);
76  file->select = be16_to_cpu(file->select);
77  printk(BIOS_INFO, "QEMU: firmware config: Found '%s'\n", name);
78  return 0;
79  }
80  }
81  printk(BIOS_INFO, "QEMU: firmware config: Couldn't find '%s'\n", name);
82  return -1;
83 }
84 
86 {
87  if (!fw_cfg_present())
88  return -1;
89  return fw_cfg_find_file(file, name);
90 }
91 
92 static int fw_cfg_e820_select(uint32_t *size)
93 {
95 
96  if (!fw_cfg_present() || fw_cfg_find_file(&file, "etc/e820"))
97  return -1;
98  fw_cfg_select(file.select);
99  *size = file.size;
100  return 0;
101 }
102 
103 static int fw_cfg_e820_read(FwCfgE820Entry *entry, uint32_t *size,
104  uint32_t *pos)
105 {
106  if (*pos + sizeof(*entry) > *size)
107  return -1;
108 
109  fw_cfg_read(entry, sizeof(*entry));
110  *pos += sizeof(*entry);
111  return 0;
112 }
113 
114 /* returns tolud on success or 0 on failure */
116 {
117  FwCfgE820Entry e;
118  uint64_t top = 0;
119  uint32_t size = 0, pos = 0;
120 
121  if (fw_cfg_e820_select(&size) == 0) {
122  while (!fw_cfg_e820_read(&e, &size, &pos)) {
123  uint64_t limit = e.address + e.length;
124  if (e.type == 1 && limit < 4ULL * GiB && limit > top)
125  top = limit;
126  }
127  }
128  return (uintptr_t)top;
129 }
130 
132 {
133  unsigned short max_cpus;
134 
135  if (!fw_cfg_present())
136  return 0;
137 
138  fw_cfg_get(FW_CFG_MAX_CPUS, &max_cpus, sizeof(max_cpus));
139  return max_cpus;
140 }
141 
142 /* ---------------------------------------------------------------------- */
143 
144 /*
145  * Starting with release 1.7 qemu provides ACPI tables via fw_cfg.
146  * Main advantage is that new (virtual) hardware which needs acpi
147  * support JustWorks[tm] without having to patch & update the firmware
148  * (seabios, coreboot, ...) accordingly.
149  *
150  * Qemu provides a etc/table-loader file with instructions for the
151  * firmware:
152  * - A "load" instruction to fetch ACPI data from fw_cfg.
153  * - A "pointer" instruction to patch a pointer. This is needed to
154  * get table-to-table references right, it is basically a
155  * primitive dynamic linker for ACPI tables.
156  * - A "checksum" instruction to generate ACPI table checksums.
157  *
158  * If a etc/table-loader file is found we'll go try loading the acpi
159  * tables from fw_cfg, otherwise we'll fallback to the ACPI tables
160  * compiled in.
161  */
162 
163 #define BIOS_LINKER_LOADER_FILESZ 56
164 
167  union {
168  /*
169  * COMMAND_ALLOCATE - allocate a table from @alloc.file
170  * subject to @alloc.align alignment (must be power of 2)
171  * and @alloc.zone (can be HIGH or FSEG) requirements.
172  *
173  * Must appear exactly once for each file, and before
174  * this file is referenced by any other command.
175  */
176  struct {
180  } alloc;
181 
182  /*
183  * COMMAND_ADD_POINTER - patch the table (originating from
184  * @dest_file) at @pointer.offset, by adding a pointer to the table
185  * originating from @src_file. 1,2,4 or 8 byte unsigned
186  * addition is used depending on @pointer.size.
187  */
188  struct {
194 
195  /*
196  * COMMAND_ADD_CHECKSUM - calculate checksum of the range specified by
197  * @cksum_start and @cksum_length fields,
198  * and then add the value at @cksum.offset.
199  * Checksum simply sums -X for each byte X in the range
200  * using 8-bit math.
201  */
202  struct {
207  } cksum;
208 
209  /* padding */
210  char pad[124];
211  };
214 
215 enum {
219 };
220 
221 enum {
224 };
225 
226 unsigned long fw_cfg_acpi_tables(unsigned long start)
227 {
228  FWCfgFile f;
230  unsigned long *addrs, current;
231  uint8_t *ptr;
232  int i, j, src, dst, max;
233 
234  if (fw_cfg_check_file(&f, "etc/table-loader"))
235  return 0;
236 
237  printk(BIOS_DEBUG, "QEMU: found ACPI tables in fw_cfg.\n");
238 
239  max = f.size / sizeof(*s);
240  s = malloc(f.size);
241  addrs = malloc(max * sizeof(*addrs));
242  fw_cfg_get(f.select, s, f.size);
243 
244  current = start;
245  for (i = 0; i < max && s[i].command != 0; i++) {
246  void *cksum_data;
247  uint32_t cksum;
248  uint32_t addr4;
249  uint64_t addr8;
250  switch (s[i].command) {
252  current = ALIGN(current, s[i].alloc.align);
253  if (fw_cfg_check_file(&f, s[i].alloc.file))
254  goto err;
255 
256  printk(BIOS_DEBUG, "QEMU: loading \"%s\" to 0x%lx (len %d)\n",
257  s[i].alloc.file, current, f.size);
258  fw_cfg_get(f.select, (void *)current, f.size);
259  addrs[i] = current;
260  current += f.size;
261  break;
262 
264  src = -1; dst = -1;
265  for (j = 0; j < i; j++) {
267  continue;
268  if (strcmp(s[j].alloc.file, s[i].pointer.dest_file) == 0)
269  dst = j;
270  if (strcmp(s[j].alloc.file, s[i].pointer.src_file) == 0)
271  src = j;
272  }
273  if (src == -1 || dst == -1)
274  goto err;
275 
276  switch (s[i].pointer.size) {
277  case 4:
278  ptr = (uint8_t *)addrs[dst];
279  ptr += s[i].pointer.offset;
280  addr4 = read_le32(ptr);
281  addr4 += addrs[src];
282  write_le32(ptr, addr4);
283  break;
284 
285  case 8:
286  ptr = (uint8_t *)addrs[dst];
287  ptr += s[i].pointer.offset;
288  addr8 = read_le64(ptr);
289  addr8 += addrs[src];
290  write_le64(ptr, addr8);
291  break;
292 
293  default:
294  /*
295  * Should not happen. ACPI knows 1 and 2 byte ptrs
296  * too, but we are operating with 32bit offsets which
297  * would simply not fit in there ...
298  */
299  printk(BIOS_DEBUG, "QEMU: acpi: unimplemented ptr size %d\n",
300  s[i].pointer.size);
301  goto err;
302  }
303  break;
304 
306  dst = -1;
307  for (j = 0; j < i; j++) {
309  continue;
310  if (strcmp(s[j].alloc.file, s[i].cksum.file) == 0)
311  dst = j;
312  }
313  if (dst == -1)
314  goto err;
315 
316  ptr = (uint8_t *)(addrs[dst] + s[i].cksum.offset);
317  cksum_data = (void *)(addrs[dst] + s[i].cksum.start);
318  cksum = acpi_checksum(cksum_data, s[i].cksum.length);
319  write_le8(ptr, cksum);
320  break;
321 
322  default:
323  printk(BIOS_DEBUG, "QEMU: acpi: unknown script cmd 0x%x @ %p\n",
324  s[i].command, s+i);
325  goto err;
326  };
327  }
328 
329  printk(BIOS_DEBUG, "QEMU: loaded ACPI tables from fw_cfg.\n");
330  free(s);
331  free(addrs);
332  return ALIGN(current, 16);
333 
334 err:
335  printk(BIOS_DEBUG, "QEMU: loading ACPI tables from fw_cfg failed.\n");
336  free(s);
337  free(addrs);
338  return 0;
339 }
340 
341 /* ---------------------------------------------------------------------- */
342 /* pick up smbios information from fw_cfg */
343 
344 static const char *type1_manufacturer;
345 static const char *type1_product_name;
346 static const char *type1_version;
347 static const char *type1_serial_number;
348 static const char *type1_family;
349 static u8 type1_uuid[16];
350 
351 static void fw_cfg_smbios_init(void)
352 {
353  static int done = 0;
354  uint16_t i, count = 0;
355  FwCfgSmbios entry;
356  char *buf;
357 
358  if (done)
359  return;
360  done = 1;
361 
363  for (i = 0; i < count; i++) {
364  insb(FW_CFG_PORT_DATA, &entry, sizeof(entry));
365  buf = malloc(entry.length - sizeof(entry));
366  insb(FW_CFG_PORT_DATA, buf, entry.length - sizeof(entry));
367  if (entry.headertype == SMBIOS_FIELD_ENTRY &&
368  entry.tabletype == 1) {
369  switch (entry.fieldoffset) {
370  case offsetof(struct smbios_type1, manufacturer):
372  break;
373  case offsetof(struct smbios_type1, product_name):
375  break;
376  case offsetof(struct smbios_type1, version):
378  break;
379  case offsetof(struct smbios_type1, serial_number):
381  break;
382  case offsetof(struct smbios_type1, family):
384  break;
385  case offsetof(struct smbios_type1, uuid):
386  memcpy(type1_uuid, buf, 16);
387  break;
388  }
389  }
390  free(buf);
391  }
392 }
393 
394 static unsigned long smbios_next(unsigned long current)
395 {
396  struct smbios_header *header;
397  int l, count = 0;
398  char *s;
399 
400  header = (void *)current;
401  current += header->length;
402  for (;;) {
403  s = (void *)current;
404  l = strlen(s);
405  if (!l)
406  return current + (count ? 1 : 2);
407  current += l + 1;
408  count++;
409  }
410 }
411 
412 /*
413  * Starting with version 2.1 qemu provides a full set of smbios tables
414  * for the virtual hardware emulated, except type 0 (bios information).
415  *
416  * What we are going to do here is find the type0 table, keep it, and
417  * override everything else generated by coreboot with the qemu smbios
418  * tables.
419  *
420  * It's a bit hackish, but qemu is a special case (compared to real
421  * hardware) and this way we don't need special qemu support in the
422  * generic smbios code.
423  */
424 unsigned long fw_cfg_smbios_tables(int *handle, unsigned long *current)
425 {
426  FWCfgFile f;
427  struct smbios_header *header;
428  unsigned long start, end;
429  int ret, i, count = 1;
430  char *str;
431 
432  if (fw_cfg_check_file(&f, "etc/smbios/smbios-tables"))
433  return 0;
434 
435  printk(BIOS_DEBUG, "QEMU: found smbios tables in fw_cfg (len %d).\n", f.size);
436 
437  /*
438  * Search backwards for "coreboot" (first string in type0 table,
439  * see src/arch/x86/boot/smbios.c), then find type0 table.
440  */
441  for (i = 0; i < 16384; i++) {
442  str = (char*)(*current - i);
443  if (strcmp(str, "coreboot") == 0)
444  break;
445  }
446  if (i == 16384)
447  return 0;
448  i += sizeof(struct smbios_type0) - 2;
449  header = (struct smbios_header *)(*current - i);
450  if (header->type != SMBIOS_BIOS_INFORMATION || header->handle != 0)
451  return 0;
452  printk(BIOS_DEBUG, "QEMU: coreboot type0 table found at 0x%lx.\n",
453  *current - i);
454  start = smbios_next(*current - i);
455 
456  /*
457  * Fetch smbios tables from qemu, go find the end marker.
458  * We'll exclude the end marker as coreboot will add one.
459  */
460  printk(BIOS_DEBUG, "QEMU: loading smbios tables to 0x%lx\n", start);
461  fw_cfg_get(f.select, (void *)start, f.size);
462  end = start;
463  do {
464  header = (struct smbios_header *)end;
465  if (header->type == SMBIOS_END_OF_TABLE)
466  break;
467  end = smbios_next(end);
468  count++;
469  } while (end < start + f.size);
470 
471  /* final fixups. */
472  ret = end - *current;
473  *current = end;
474  *handle = count;
475  return ret;
476 }
477 
479 {
481  return type1_manufacturer ?: CONFIG_MAINBOARD_SMBIOS_MANUFACTURER;
482 }
483 
485 {
487  return type1_product_name ?: CONFIG_MAINBOARD_SMBIOS_PRODUCT_NAME;
488 }
489 
490 const char *smbios_mainboard_version(void)
491 {
493  return type1_version ?: CONFIG_MAINBOARD_VERSION;
494 }
495 
497 {
499  return type1_serial_number ?: CONFIG_MAINBOARD_SERIAL_NUMBER;
500 }
501 
503 {
505  memcpy(uuid, type1_uuid, 16);
506 }
507 
508 /*
509  * Configure DMA setup
510  */
511 
512 static void fw_cfg_dma(int control, void *buf, int len)
513 {
514  volatile FwCfgDmaAccess dma;
515  uintptr_t dma_desc_addr;
516  uint32_t dma_desc_addr_hi, dma_desc_addr_lo;
517 
518  dma.control = be32_to_cpu(control);
519  dma.length = be32_to_cpu(len);
520  dma.address = be64_to_cpu((uintptr_t)buf);
521 
522  dma_desc_addr = (uintptr_t)&dma;
523  dma_desc_addr_lo = (uint32_t)(dma_desc_addr & 0xFFFFFFFFU);
524  dma_desc_addr_hi = sizeof(uintptr_t) > sizeof(uint32_t)
525  ? (uint32_t)(dma_desc_addr >> 32) : 0;
526 
527  // Skip writing high half if unnecessary.
528  if (dma_desc_addr_hi)
529  outl(be32_to_cpu(dma_desc_addr_hi), FW_CFG_DMA_ADDR_HIGH);
530  outl(be32_to_cpu(dma_desc_addr_lo), FW_CFG_DMA_ADDR_LOW);
531 
532  while (be32_to_cpu(dma.control) & ~FW_CFG_DMA_CTL_ERROR)
533  ;
534 }
u8 acpi_checksum(u8 *table, u32 length)
Definition: acpi.c:35
struct arm64_kernel_header header
Definition: fit_payload.c:30
const char * name
Definition: mmu.c:92
static void insb(uint16_t port, void *addr, unsigned long count)
Definition: io.h:76
void * memcpy(void *dest, const void *src, size_t n)
Definition: memcpy.c:7
#define offsetof(TYPE, MEMBER)
Definition: helpers.h:84
static uint64_t read_le64(const void *src)
Definition: endian.h:216
static void write_le64(void *dest, uint64_t val)
Definition: endian.h:232
static void write_le32(void *dest, uint32_t val)
Definition: endian.h:203
static uint32_t read_le32(const void *src)
Definition: endian.h:189
static void write_le8(void *dest, uint8_t val)
Definition: endian.h:153
#define printk(level,...)
Definition: stdlib.h:16
void outl(u32 val, u16 port)
void outw(u16 val, u16 port)
unsigned long fw_cfg_acpi_tables(unsigned long start)
Definition: fw_cfg.c:226
static int fw_cfg_e820_read(FwCfgE820Entry *entry, uint32_t *size, uint32_t *pos)
Definition: fw_cfg.c:103
#define BIOS_LINKER_LOADER_FILESZ
Definition: fw_cfg.c:163
void smbios_system_set_uuid(u8 *uuid)
Definition: fw_cfg.c:502
void fw_cfg_get(uint16_t entry, void *dst, int dstlen)
Definition: fw_cfg.c:58
@ BIOS_LINKER_LOADER_COMMAND_ALLOCATE
Definition: fw_cfg.c:216
@ BIOS_LINKER_LOADER_COMMAND_ADD_CHECKSUM
Definition: fw_cfg.c:218
@ BIOS_LINKER_LOADER_COMMAND_ADD_POINTER
Definition: fw_cfg.c:217
static const char * type1_manufacturer
Definition: fw_cfg.c:344
const char * smbios_mainboard_serial_number(void)
Definition: fw_cfg.c:496
const char * smbios_mainboard_version(void)
Definition: fw_cfg.c:490
const char * smbios_mainboard_manufacturer(void)
Definition: fw_cfg.c:478
static void fw_cfg_read(void *dst, int dstlen)
Definition: fw_cfg.c:50
int fw_cfg_check_file(FWCfgFile *file, const char *name)
Definition: fw_cfg.c:85
const char * smbios_mainboard_product_name(void)
Definition: fw_cfg.c:484
static int fw_cfg_find_file(FWCfgFile *file, const char *name)
Definition: fw_cfg.c:64
struct BiosLinkerLoaderEntry __packed
static uint8_t fw_ver
Definition: fw_cfg.c:21
static const char * type1_family
Definition: fw_cfg.c:348
#define FW_CFG_PORT_DATA
Definition: fw_cfg.c:16
int fw_cfg_max_cpus(void)
Definition: fw_cfg.c:131
uintptr_t fw_cfg_tolud(void)
Definition: fw_cfg.c:115
static const char * type1_version
Definition: fw_cfg.c:346
static int fw_cfg_present(void)
Definition: fw_cfg.c:25
static int fw_cfg_e820_select(uint32_t *size)
Definition: fw_cfg.c:92
#define FW_CFG_PORT_CTL
Definition: fw_cfg.c:15
static const char * type1_product_name
Definition: fw_cfg.c:345
static const char * type1_serial_number
Definition: fw_cfg.c:347
unsigned long fw_cfg_smbios_tables(int *handle, unsigned long *current)
Definition: fw_cfg.c:424
static unsigned long smbios_next(unsigned long current)
Definition: fw_cfg.c:394
static void fw_cfg_dma(int control, void *buf, int len)
Definition: fw_cfg.c:512
@ BIOS_LINKER_LOADER_ALLOC_ZONE_HIGH
Definition: fw_cfg.c:222
@ BIOS_LINKER_LOADER_ALLOC_ZONE_FSEG
Definition: fw_cfg.c:223
static void fw_cfg_smbios_init(void)
Definition: fw_cfg.c:351
static int fw_cfg_detected
Definition: fw_cfg.c:20
#define FW_CFG_DMA_ADDR_HIGH
Definition: fw_cfg.c:17
static void fw_cfg_select(uint16_t entry)
Definition: fw_cfg.c:45
#define FW_CFG_DMA_ADDR_LOW
Definition: fw_cfg.c:18
static u8 type1_uuid[16]
Definition: fw_cfg.c:349
#define FW_CFG_SMBIOS_ENTRIES
Definition: fw_cfg_if.h:51
#define FW_CFG_SIG_SIZE
Definition: fw_cfg_if.h:65
@ FW_CFG_FILE_DIR
Definition: fw_cfg_if.h:39
@ FW_CFG_ID
Definition: fw_cfg_if.h:15
@ FW_CFG_MAX_CPUS
Definition: fw_cfg_if.h:29
@ FW_CFG_SIGNATURE
Definition: fw_cfg_if.h:14
#define FW_CFG_DMA_CTL_ERROR
Definition: fw_cfg_if.h:101
#define FW_CFG_VERSION_DMA
Definition: fw_cfg_if.h:98
#define SMBIOS_FIELD_ENTRY
Definition: fw_cfg_if.h:86
#define FW_CFG_DMA_CTL_READ
Definition: fw_cfg_if.h:102
#define ALIGN
Definition: asm.h:22
@ SMBIOS_END_OF_TABLE
Definition: smbios.h:256
@ SMBIOS_BIOS_INFORMATION
Definition: smbios.h:239
void * malloc(size_t size)
Definition: malloc.c:53
void free(void *ptr)
Definition: malloc.c:67
unsigned int version[2]
Definition: edid.c:55
#define BIOS_INFO
BIOS_INFO - Expected events.
Definition: loglevel.h:113
#define BIOS_DEBUG
BIOS_DEBUG - Verbose output.
Definition: loglevel.h:128
static uint8_t * buf
Definition: uart.c:7
struct @1399 * dma
unsigned short uint16_t
Definition: stdint.h:11
unsigned int uint32_t
Definition: stdint.h:14
unsigned long uintptr_t
Definition: stdint.h:21
unsigned long long uint64_t
Definition: stdint.h:17
uint8_t u8
Definition: stdint.h:45
unsigned char uint8_t
Definition: stdint.h:8
int strcmp(const char *s1, const char *s2)
Definition: string.c:103
int memcmp(const void *s1, const void *s2, size_t n)
Definition: memcmp.c:3
char * strdup(const char *s)
Definition: string.c:7
size_t strlen(const char *src)
Definition: string.c:42
struct BiosLinkerLoaderEntry::@261::@265 cksum
struct BiosLinkerLoaderEntry::@261::@264 pointer
struct BiosLinkerLoaderEntry::@261::@263 alloc
char dest_file[BIOS_LINKER_LOADER_FILESZ]
Definition: fw_cfg.c:189
char src_file[BIOS_LINKER_LOADER_FILESZ]
Definition: fw_cfg.c:190
uint32_t command
Definition: fw_cfg.c:166
uint16_t select
Definition: fw_cfg_if.h:69
uint32_t size
Definition: fw_cfg_if.h:68
uint64_t length
Definition: fw_cfg_if.h:81
uint64_t address
Definition: fw_cfg_if.h:80
uint32_t type
Definition: fw_cfg_if.h:82
uint8_t headertype
Definition: fw_cfg_if.h:91
uint16_t length
Definition: fw_cfg_if.h:90
uint8_t tabletype
Definition: fw_cfg_if.h:92
uint16_t fieldoffset
Definition: fw_cfg_if.h:93
Definition: gcov-glue.c:7
u16 handle
Definition: smbios.h:292
#define s(param, src_bits, pmcreg, dst_bits)
#define count