master
c 288 lines 8.66 KB
Raw
1 /*
2 * SPDX-License-Identifier: GPL-2.0-or-later
3 *
4 * print ovmf debug log
5 *
6 * see OvmfPkg/Library/MemDebugLogLib/ in edk2
7 */
8
9 #include "qemu/osdep.h"
10 #include "qemu/units.h"
11 #include "qemu/target-info-qapi.h"
12 #include "hw/core/boards.h"
13 #include "hw/i386/x86.h"
14 #include "hw/arm/virt.h"
15 #include "system/dma.h"
16 #include "monitor/hmp.h"
17 #include "monitor/monitor.h"
18 #include "qapi/error.h"
19 #include "qapi/type-helpers.h"
20 #include "qapi/qapi-commands-machine.h"
21 #include "qobject/qdict.h"
22
23
24 /* ----------------------------------------------------------------------- */
25 /* copy from edk2 */
26
27 #define MEM_DEBUG_LOG_MAGIC1 0x3167646d666d766f /* "ovmfmdg1" */
28 #define MEM_DEBUG_LOG_MAGIC2 0x3267646d666d766f /* "ovmfmdg2" */
29
30 /*
31 * Mem Debug Log buffer header.
32 * The Log buffer is circular. Only the most
33 * recent messages are retained. Older messages
34 * will be discarded if the buffer overflows.
35 * The Debug Log starts just after the header.
36 */
37 typedef struct {
38 /*
39 * Magic values
40 * These fields are used by tools to locate the buffer in
41 * memory. These MUST be the first two fields of the structure.
42 * Use a 128 bit Magic to vastly reduce the possibility of
43 * a collision with random data in memory.
44 */
45 uint64_t Magic1;
46 uint64_t Magic2;
47 /*
48 * Header Size
49 * This MUST be the third field of the structure
50 */
51 uint64_t HeaderSize;
52 /*
53 * Debug log size (minus header)
54 */
55 uint64_t DebugLogSize;
56 /*
57 * edk2 uses this for locking access.
58 */
59 uint64_t MemDebugLogLock;
60 /*
61 * Debug log head offset
62 */
63 uint64_t DebugLogHeadOffset;
64 /*
65 * Debug log tail offset
66 */
67 uint64_t DebugLogTailOffset;
68 /*
69 * Flag to indicate if the buffer wrapped and was thus truncated.
70 */
71 uint64_t Truncated;
72 /*
73 * Firmware Build Version (PcdFirmwareVersionString)
74 */
75 char FirmwareVersion[128];
76 } MEM_DEBUG_LOG_HDR;
77
78
79 /* ----------------------------------------------------------------------- */
80 /* qemu monitor command */
81
82 typedef struct {
83 uint64_t magic1;
84 uint64_t magic2;
85 } MemDebugLogMagic;
86
87 /* find log buffer in guest memory by searching for the magic cookie */
88 static dma_addr_t find_ovmf_log_range(dma_addr_t start, dma_addr_t end)
89 {
90 static const MemDebugLogMagic magic = {
91 .magic1 = MEM_DEBUG_LOG_MAGIC1,
92 .magic2 = MEM_DEBUG_LOG_MAGIC2,
93 };
94 MemDebugLogMagic check;
95 dma_addr_t step = 4 * KiB;
96 dma_addr_t offset;
97
98 for (offset = start; offset < end; offset += step) {
99 if (dma_memory_read(&address_space_memory, offset,
100 &check, sizeof(check),
101 MEMTXATTRS_UNSPECIFIED)) {
102 /* dma error -> stop searching */
103 break;
104 }
105 if (memcmp(&magic, &check, sizeof(check)) == 0) {
106 return offset;
107 }
108 }
109 return (dma_addr_t)-1;
110 }
111
112 static dma_addr_t find_ovmf_log(void)
113 {
114 MachineState *ms = MACHINE(qdev_get_machine());
115 dma_addr_t start, end, offset;
116
117 if (target_arch() == SYS_EMU_TARGET_X86_64 &&
118 object_dynamic_cast(OBJECT(ms), TYPE_X86_MACHINE)) {
119 X86MachineState *x86ms = X86_MACHINE(ms);
120
121 /* early log buffer, static allocation in memfd, sec + early pei */
122 offset = find_ovmf_log_range(0x800000, 0x900000);
123 if (offset != -1) {
124 return offset;
125 }
126
127 /*
128 * normal log buffer, dynamically allocated close to end of low memory,
129 * late pei + dxe phase
130 */
131 end = x86ms->below_4g_mem_size;
132 start = end - MIN(end, 128 * MiB);
133 return find_ovmf_log_range(start, end);
134 }
135
136 if (target_arch() == SYS_EMU_TARGET_AARCH64 &&
137 object_dynamic_cast(OBJECT(ms), TYPE_VIRT_MACHINE)) {
138 VirtMachineState *vms = VIRT_MACHINE(ms);
139
140 /* edk2 ArmVirt firmware allocations are in the first 128 MB */
141 start = vms->memmap[VIRT_MEM].base;
142 end = start + 128 * MiB;
143 return find_ovmf_log_range(start, end);
144 }
145
146 return (dma_addr_t)-1;
147 }
148
149 static void handle_ovmf_log_range(GString *out,
150 dma_addr_t start,
151 dma_addr_t end,
152 Error **errp)
153 {
154 if (start > end) {
155 return;
156 }
157
158 size_t len = end - start;
159 g_string_set_size(out, out->len + len);
160 if (dma_memory_read(&address_space_memory, start,
161 out->str + (out->len - len),
162 len, MEMTXATTRS_UNSPECIFIED)) {
163 error_setg(errp, "can not read firmware log buffer contents");
164 return;
165 }
166 }
167
168 FirmwareLog *qmp_query_firmware_log(bool have_max_size, uint64_t max_size,
169 Error **errp)
170 {
171 MEM_DEBUG_LOG_HDR header;
172 dma_addr_t offset, base;
173 FirmwareLog *ret;
174 g_autoptr(GString) log = g_string_new("");
175
176 offset = find_ovmf_log();
177 if (offset == -1) {
178 error_setg(errp, "firmware log buffer not found");
179 return NULL;
180 }
181
182 if (dma_memory_read(&address_space_memory, offset,
183 &header, sizeof(header),
184 MEMTXATTRS_UNSPECIFIED)) {
185 error_setg(errp, "can not read firmware log buffer header");
186 return NULL;
187 }
188
189 if (header.DebugLogHeadOffset > header.DebugLogSize ||
190 header.DebugLogTailOffset > header.DebugLogSize) {
191 error_setg(errp, "firmware log buffer header is invalid");
192 return NULL;
193 }
194
195 if (have_max_size) {
196 if (max_size > MiB) {
197 error_setg(errp, "parameter 'max-size' exceeds 1MiB");
198 return NULL;
199 }
200 } else {
201 max_size = MiB;
202 }
203
204 /* adjust header.DebugLogHeadOffset so we return at most maxsize bytes */
205 if (header.DebugLogHeadOffset > header.DebugLogTailOffset) {
206 /* wrap around */
207 if (header.DebugLogTailOffset > max_size) {
208 header.DebugLogHeadOffset = header.DebugLogTailOffset - max_size;
209 } else {
210 uint64_t max_chunk = max_size - header.DebugLogTailOffset;
211 if (header.DebugLogSize > max_chunk &&
212 header.DebugLogHeadOffset < header.DebugLogSize - max_chunk) {
213 header.DebugLogHeadOffset = header.DebugLogSize - max_chunk;
214 }
215 }
216 } else {
217 if (header.DebugLogTailOffset > max_size &&
218 header.DebugLogHeadOffset < header.DebugLogTailOffset - max_size) {
219 header.DebugLogHeadOffset = header.DebugLogTailOffset - max_size;
220 }
221 }
222
223 base = offset + header.HeaderSize;
224 if (header.DebugLogHeadOffset > header.DebugLogTailOffset) {
225 /* wrap around */
226 handle_ovmf_log_range(log,
227 base + header.DebugLogHeadOffset,
228 base + header.DebugLogSize,
229 errp);
230 if (*errp) {
231 return NULL;
232 }
233 handle_ovmf_log_range(log,
234 base + 0,
235 base + header.DebugLogTailOffset,
236 errp);
237 if (*errp) {
238 return NULL;
239 }
240 } else {
241 handle_ovmf_log_range(log,
242 base + header.DebugLogHeadOffset,
243 base + header.DebugLogTailOffset,
244 errp);
245 if (*errp) {
246 return NULL;
247 }
248 }
249
250 ret = g_new0(FirmwareLog, 1);
251 if (header.FirmwareVersion[0] != '\0') {
252 ret->version = g_strndup(header.FirmwareVersion,
253 sizeof(header.FirmwareVersion));
254 }
255 ret->log = g_base64_encode((const guchar *)log->str, log->len);
256 return ret;
257 }
258
259 #ifdef CONFIG_HMP
260 void hmp_info_firmware_log(MonitorHMP *hmp, const QDict *qdict)
261 {
262 g_autofree gchar *log_esc = NULL;
263 g_autofree guchar *log_out = NULL;
264 Error *err = NULL;
265 g_autoptr(FirmwareLog) log = NULL;
266 gsize log_len;
267 int64_t maxsize;
268
269 maxsize = qdict_get_try_int(qdict, "max-size", -1);
270 log = qmp_query_firmware_log(maxsize != -1, (uint64_t)maxsize, &err);
271 if (err) {
272 hmp_handle_error(hmp, err);
273 return;
274 }
275
276 g_assert(log != NULL);
277 g_assert(log->log != NULL);
278
279 if (log->version) {
280 g_autofree gchar *esc = g_strescape(log->version, NULL);
281 monitor_hmp_printf(hmp, "[ firmware version: %s ]\n", esc);
282 }
283
284 log_out = g_base64_decode(log->log, &log_len);
285 log_esc = g_strescape((gchar *)log_out, "\r\n");
286 monitor_hmp_printf(hmp, "%s\n", log_esc);
287 }
288 #endif