blob: c3e8c310d00001fbc0f96b424cc8aed333fbbdfd [file] [log] [blame]
Heinrich Schuchardt1c5aab82023-12-23 02:03:34 +01001// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * (C) Copyright 2023 Heinrich Schuchardt <heinrich.schuchardt@canonical.com>
4 */
5
6#define LOG_CATEGORY UCLASS_QFW
7
8#include <efi_loader.h>
9#include <errno.h>
10#include <log.h>
11#include <malloc.h>
12#include <mapmem.h>
13#include <qfw.h>
14#include <smbios.h>
15#include <tables_csum.h>
16#include <linux/sizes.h>
17#include <asm/global_data.h>
18
19DECLARE_GLOBAL_DATA_PTR;
20
21/**
22 * qfw_load_smbios_table() - load a QEMU firmware file
23 *
24 * @dev: QEMU firmware device
25 * @size: parameter to return the size of the loaded table
26 * @name: name of the table to load
27 * Return: address of the loaded table, NULL on error
28 */
29static void *qfw_load_smbios_table(struct udevice *dev, uint32_t *size,
30 char *name)
31{
32 struct fw_file *file;
33 struct bios_linker_entry *table;
34
35 file = qfw_find_file(dev, name);
36 if (!file) {
37 log_debug("Can't find %s\n", name);
38 return NULL;
39 }
40
41 *size = be32_to_cpu(file->cfg.size);
42
43 table = malloc(*size);
44 if (!table) {
45 log_err("Out of memory\n");
46 return NULL;
47 }
48
49 qfw_read_entry(dev, be16_to_cpu(file->cfg.select), *size, table);
50
51 return table;
52}
53
54/**
55 * qfw_parse_smbios_anchor() - parse QEMU's SMBIOS anchor
56 *
57 * @dev: QEMU firmware device
58 * @entry: SMBIOS 3 structure to be filled from QEMU's anchor
59 * Return: 0 for success, -ve on error
60 */
61static int qfw_parse_smbios_anchor(struct udevice *dev,
62 struct smbios3_entry *entry)
63{
64 void *table;
65 uint32_t size;
66 struct smbios_entry *entry2;
67 struct smbios3_entry *entry3;
68 const char smbios_sig[] = "_SM_";
69 const char smbios3_sig[] = "_SM3_";
70 int ret = 0;
71
72 table = qfw_load_smbios_table(dev, &size, "etc/smbios/smbios-anchor");
73 if (!table)
74 return -ENOMEM;
75 if (!memcmp(table, smbios3_sig, sizeof(smbios3_sig) - 1)) {
76 entry3 = table;
77 if (entry3->length != sizeof(struct smbios3_entry)) {
78 ret = -ENOENT;
79 goto out;
80 }
81 memcpy(entry, entry3, sizeof(struct smbios3_entry));
82 } else if (!memcmp(table, smbios_sig, sizeof(smbios_sig) - 1)) {
83 entry2 = table;
84 if (entry2->length != sizeof(struct smbios_entry)) {
85 ret = -ENOENT;
86 goto out;
87 }
88 memset(entry, 0, sizeof(struct smbios3_entry));
89 memcpy(entry, smbios3_sig, sizeof(smbios3_sig));
90 entry->length = sizeof(struct smbios3_entry);
91 entry->major_ver = entry2->major_ver;
92 entry->minor_ver = entry2->minor_ver;
Heinrich Schuchardt406c4102024-01-31 23:49:34 +010093 entry->table_maximum_size = entry2->struct_table_length;
Heinrich Schuchardt1c5aab82023-12-23 02:03:34 +010094 } else {
95 ret = -ENOENT;
96 goto out;
97 }
98 ret = 0;
99out:
100 free(table);
101
102 return ret;
103}
104
105/**
106 * qfw_write_smbios_tables() - copy SMBIOS tables from QEMU
107 *
108 * @addr: target buffer
109 * @size: size of target buffer
110 * Return: 0 for success, -ve on error
111 */
112static int qfw_write_smbios_tables(u8 *addr, uint32_t size)
113{
114 int ret;
115 struct udevice *dev;
116 struct smbios3_entry *entry = (void *)addr;
117 void *table;
118 uint32_t table_size;
119
120 ret = qfw_get_dev(&dev);
121 if (ret) {
122 log_err("No QEMU firmware device\n");
123 return ret;
124 }
125
126 ret = qfw_read_firmware_list(dev);
127 if (ret) {
128 log_err("Can't read firmware file list\n");
129 return ret;
130 }
131
132 ret = qfw_parse_smbios_anchor(dev, entry);
133 if (ret) {
134 log_debug("Can't parse anchor\n");
135 return ret;
136 }
137
138 addr += entry->length;
139 entry->struct_table_address = (uintptr_t)addr;
140 entry->checksum = 0;
141 entry->checksum = table_compute_checksum(entry,
142 sizeof(struct smbios3_entry));
143
144 table = qfw_load_smbios_table(dev, &table_size,
145 "etc/smbios/smbios-tables");
146 if (table_size + sizeof(struct smbios3_entry) > size) {
147 free(table);
148 return -ENOMEM;
149 }
150 memcpy(addr, table, table_size);
151 free(table);
152
153 return 0;
154}
155
156/**
157 * qfw_evt_write_smbios_tables() - event handler for copying QEMU SMBIOS tables
158 *
159 * Return: 0 on success, -ve on error (only out of memory)
160 */
161static int qfw_evt_write_smbios_tables(void)
162{
163 phys_addr_t addr;
164 void *ptr;
165 int ret;
166 /*
167 * TODO:
168 * This size is currently hard coded in lib/efi_loader/efi_smbios.c.
169 * We need a field in global data for the size.
170 */
171 uint32_t size = SZ_4K;
172
173 /* Reserve 64K for SMBIOS tables, aligned to a 4K boundary */
174 ptr = memalign(SZ_4K, size);
175 if (!ptr) {
176 log_err("Out of memory\n");
177 return -ENOMEM;
178 }
179 addr = map_to_sysmem(ptr);
180
181 /* Generate SMBIOS tables */
182 ret = qfw_write_smbios_tables(ptr, size);
183 if (ret) {
184 if (CONFIG_IS_ENABLED(GENERATE_SMBIOS_TABLE)) {
185 log_info("Falling back to U-Boot generated SMBIOS tables\n");
186 write_smbios_table(addr);
187 }
188 } else {
189 log_debug("SMBIOS tables copied from QEMU\n");
190 }
191
192 gd_set_smbios_start(addr);
193
194 return 0;
195}
196
197EVENT_SPY_SIMPLE(EVT_LAST_STAGE_INIT, qfw_evt_write_smbios_tables);