blob: 93c4a80286bd13c7f10118949a91bd4990c863b6 [file] [log] [blame]
Heinrich Schuchardt08d931a2023-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
Simon Glass4d8c5202025-01-10 17:00:17 -07008#include <bloblist.h>
Heinrich Schuchardt08d931a2023-12-23 02:03:34 +01009#include <efi_loader.h>
10#include <errno.h>
11#include <log.h>
12#include <malloc.h>
13#include <mapmem.h>
14#include <qfw.h>
15#include <smbios.h>
16#include <tables_csum.h>
17#include <linux/sizes.h>
18#include <asm/global_data.h>
Simon Glass4d8c5202025-01-10 17:00:17 -070019#include <linux/err.h>
Heinrich Schuchardt08d931a2023-12-23 02:03:34 +010020
21DECLARE_GLOBAL_DATA_PTR;
22
23/**
24 * qfw_load_smbios_table() - load a QEMU firmware file
25 *
26 * @dev: QEMU firmware device
27 * @size: parameter to return the size of the loaded table
28 * @name: name of the table to load
29 * Return: address of the loaded table, NULL on error
30 */
31static void *qfw_load_smbios_table(struct udevice *dev, uint32_t *size,
32 char *name)
33{
34 struct fw_file *file;
35 struct bios_linker_entry *table;
36
37 file = qfw_find_file(dev, name);
38 if (!file) {
39 log_debug("Can't find %s\n", name);
40 return NULL;
41 }
42
43 *size = be32_to_cpu(file->cfg.size);
44
45 table = malloc(*size);
46 if (!table) {
47 log_err("Out of memory\n");
48 return NULL;
49 }
50
51 qfw_read_entry(dev, be16_to_cpu(file->cfg.select), *size, table);
52
53 return table;
54}
55
56/**
57 * qfw_parse_smbios_anchor() - parse QEMU's SMBIOS anchor
58 *
59 * @dev: QEMU firmware device
60 * @entry: SMBIOS 3 structure to be filled from QEMU's anchor
61 * Return: 0 for success, -ve on error
62 */
63static int qfw_parse_smbios_anchor(struct udevice *dev,
64 struct smbios3_entry *entry)
65{
66 void *table;
67 uint32_t size;
68 struct smbios_entry *entry2;
69 struct smbios3_entry *entry3;
70 const char smbios_sig[] = "_SM_";
71 const char smbios3_sig[] = "_SM3_";
72 int ret = 0;
73
74 table = qfw_load_smbios_table(dev, &size, "etc/smbios/smbios-anchor");
75 if (!table)
76 return -ENOMEM;
77 if (!memcmp(table, smbios3_sig, sizeof(smbios3_sig) - 1)) {
78 entry3 = table;
79 if (entry3->length != sizeof(struct smbios3_entry)) {
80 ret = -ENOENT;
81 goto out;
82 }
83 memcpy(entry, entry3, sizeof(struct smbios3_entry));
84 } else if (!memcmp(table, smbios_sig, sizeof(smbios_sig) - 1)) {
85 entry2 = table;
86 if (entry2->length != sizeof(struct smbios_entry)) {
87 ret = -ENOENT;
88 goto out;
89 }
90 memset(entry, 0, sizeof(struct smbios3_entry));
91 memcpy(entry, smbios3_sig, sizeof(smbios3_sig));
92 entry->length = sizeof(struct smbios3_entry);
93 entry->major_ver = entry2->major_ver;
94 entry->minor_ver = entry2->minor_ver;
Heinrich Schuchardt68e948a2024-01-31 23:49:34 +010095 entry->table_maximum_size = entry2->struct_table_length;
Heinrich Schuchardt08d931a2023-12-23 02:03:34 +010096 } else {
97 ret = -ENOENT;
98 goto out;
99 }
100 ret = 0;
101out:
102 free(table);
103
104 return ret;
105}
106
107/**
108 * qfw_write_smbios_tables() - copy SMBIOS tables from QEMU
109 *
Simon Glass4d8c5202025-01-10 17:00:17 -0700110 * @addr: address of target buffer
Heinrich Schuchardt08d931a2023-12-23 02:03:34 +0100111 * Return: 0 for success, -ve on error
112 */
Simon Glass4d8c5202025-01-10 17:00:17 -0700113ulong write_smbios_table(ulong addr)
Heinrich Schuchardt08d931a2023-12-23 02:03:34 +0100114{
115 int ret;
116 struct udevice *dev;
117 struct smbios3_entry *entry = (void *)addr;
118 void *table;
119 uint32_t table_size;
120
121 ret = qfw_get_dev(&dev);
122 if (ret) {
123 log_err("No QEMU firmware device\n");
124 return ret;
125 }
126
127 ret = qfw_read_firmware_list(dev);
128 if (ret) {
129 log_err("Can't read firmware file list\n");
130 return ret;
131 }
132
133 ret = qfw_parse_smbios_anchor(dev, entry);
134 if (ret) {
135 log_debug("Can't parse anchor\n");
136 return ret;
137 }
138
139 addr += entry->length;
140 entry->struct_table_address = (uintptr_t)addr;
141 entry->checksum = 0;
142 entry->checksum = table_compute_checksum(entry,
143 sizeof(struct smbios3_entry));
144
145 table = qfw_load_smbios_table(dev, &table_size,
146 "etc/smbios/smbios-tables");
Simon Glass4d8c5202025-01-10 17:00:17 -0700147 memcpy((void *)addr, table, table_size);
Heinrich Schuchardt08d931a2023-12-23 02:03:34 +0100148 free(table);
149
Simon Glass4d8c5202025-01-10 17:00:17 -0700150 return addr + table_size;
Heinrich Schuchardt08d931a2023-12-23 02:03:34 +0100151}
152
Simon Glass4d8c5202025-01-10 17:00:17 -0700153#ifndef CONFIG_X86
Heinrich Schuchardt08d931a2023-12-23 02:03:34 +0100154/**
155 * qfw_evt_write_smbios_tables() - event handler for copying QEMU SMBIOS tables
156 *
157 * Return: 0 on success, -ve on error (only out of memory)
158 */
159static int qfw_evt_write_smbios_tables(void)
160{
Simon Glass4d8c5202025-01-10 17:00:17 -0700161 ulong addr, end;
Heinrich Schuchardt08d931a2023-12-23 02:03:34 +0100162 void *ptr;
Simon Glass4d8c5202025-01-10 17:00:17 -0700163
Heinrich Schuchardt08d931a2023-12-23 02:03:34 +0100164 /*
165 * TODO:
166 * This size is currently hard coded in lib/efi_loader/efi_smbios.c.
167 * We need a field in global data for the size.
168 */
169 uint32_t size = SZ_4K;
170
Simon Glass4d8c5202025-01-10 17:00:17 -0700171 log_debug("qfw_evt_write_smbios_tables bloblist\n");
172 /* Reserve 4K for SMBIOS tables, aligned to a 4K boundary */
173 ptr = bloblist_add(BLOBLISTT_SMBIOS_TABLES, size, 12);
174 if (!ptr)
175 return log_msg_ret("bloblist", -ENOBUFS);
176
Heinrich Schuchardt08d931a2023-12-23 02:03:34 +0100177 addr = map_to_sysmem(ptr);
178
179 /* Generate SMBIOS tables */
Simon Glass4d8c5202025-01-10 17:00:17 -0700180 end = write_smbios_table(addr);
181 if (IS_ERR_VALUE(end)) {
182 log_warning("SMBIOS: Failed to write (err=%dE)\n", (int)end);
Heinrich Schuchardt08d931a2023-12-23 02:03:34 +0100183 } else {
Simon Glass4d8c5202025-01-10 17:00:17 -0700184 if (end - addr > size)
185 return -ENOMEM;
Heinrich Schuchardt08d931a2023-12-23 02:03:34 +0100186 log_debug("SMBIOS tables copied from QEMU\n");
187 }
188
189 gd_set_smbios_start(addr);
190
191 return 0;
192}
Heinrich Schuchardt08d931a2023-12-23 02:03:34 +0100193EVENT_SPY_SIMPLE(EVT_LAST_STAGE_INIT, qfw_evt_write_smbios_tables);
Simon Glass4d8c5202025-01-10 17:00:17 -0700194#endif /* !X86 */