]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/bhyve/qemu_loader.c
Remove $FreeBSD$: two-line .h pattern
[FreeBSD/FreeBSD.git] / usr.sbin / bhyve / qemu_loader.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2022 Beckhoff Automation GmbH & Co. KG
5  * Author: Corvin Köhne <c.koehne@beckhoff.com>
6  */
7
8 #include <sys/types.h>
9 #include <sys/param.h>
10 #include <sys/endian.h>
11 #include <sys/queue.h>
12
13 #include <machine/vmm.h>
14
15 #include <err.h>
16 #include <errno.h>
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <vmmapi.h>
21
22 #include "qemu_fwcfg.h"
23 #include "qemu_loader.h"
24
25 struct qemu_loader_entry {
26         uint32_t cmd_le;
27         union {
28                 struct {
29                         uint8_t name[QEMU_FWCFG_MAX_NAME];
30                         uint32_t alignment_le;
31                         uint8_t zone;
32                 } alloc;
33                 struct {
34                         uint8_t dest_name[QEMU_FWCFG_MAX_NAME];
35                         uint8_t src_name[QEMU_FWCFG_MAX_NAME];
36                         uint32_t off_le;
37                         uint8_t size;
38                 } add_pointer;
39                 struct {
40                         uint8_t name[QEMU_FWCFG_MAX_NAME];
41                         uint32_t off_le;
42                         uint32_t start_le;
43                         uint32_t len_le;
44                 } add_checksum;
45                 struct {
46                         uint8_t dest_name[QEMU_FWCFG_MAX_NAME];
47                         uint8_t src_name[QEMU_FWCFG_MAX_NAME];
48                         uint32_t dest_off_le;
49                         uint32_t src_off_le;
50                         uint8_t size;
51                 } write_pointer;
52
53                 /* padding */
54                 uint8_t pad[124];
55         };
56 } __packed;
57
58 enum qemu_loader_command {
59         QEMU_LOADER_CMD_ALLOC = 0x1,
60         QEMU_LOADER_CMD_ADD_POINTER = 0x2,
61         QEMU_LOADER_CMD_ADD_CHECKSUM = 0x3,
62         QEMU_LOADER_CMD_WRITE_POINTER = 0x4,
63 };
64
65 struct qemu_loader_element {
66         STAILQ_ENTRY(qemu_loader_element) chain;
67         struct qemu_loader_entry entry;
68 };
69
70 struct qemu_loader {
71         uint8_t fwcfg_name[QEMU_FWCFG_MAX_NAME];
72         STAILQ_HEAD(qemu_loader_list, qemu_loader_element) list;
73 };
74
75 int
76 qemu_loader_alloc(struct qemu_loader *const loader, const uint8_t *name,
77     const uint32_t alignment, const enum qemu_loader_zone zone)
78 {
79         struct qemu_loader_element *element;
80
81         if (strlen(name) >= QEMU_FWCFG_MAX_NAME)
82                 return (EINVAL);
83
84         element = calloc(1, sizeof(struct qemu_loader_element));
85         if (element == NULL) {
86                 warnx("%s: failed to allocate command", __func__);
87                 return (ENOMEM);
88         }
89
90         element->entry.cmd_le = htole32(QEMU_LOADER_CMD_ALLOC);
91         strncpy(element->entry.alloc.name, name, QEMU_FWCFG_MAX_NAME);
92         element->entry.alloc.alignment_le = htole32(alignment);
93         element->entry.alloc.zone = zone;
94
95         /*
96          * The guest always works on copies of the fwcfg item, which where
97          * loaded into guest memory. Loading a fwcfg item is caused by ALLOC.
98          * For that reason, ALLOC should be scheduled in front of any other
99          * commands.
100          */
101         STAILQ_INSERT_HEAD(&loader->list, element, chain);
102
103         return (0);
104 }
105
106 int
107 qemu_loader_add_checksum(struct qemu_loader *const loader, const uint8_t *name,
108     const uint32_t off, const uint32_t start, const uint32_t len)
109 {
110         struct qemu_loader_element *element;
111
112         if (strlen(name) >= QEMU_FWCFG_MAX_NAME)
113                 return (EINVAL);
114
115         element = calloc(1, sizeof(struct qemu_loader_element));
116         if (element == NULL) {
117                 warnx("%s: failed to allocate command", __func__);
118                 return (ENOMEM);
119         }
120
121         element->entry.cmd_le = htole32(QEMU_LOADER_CMD_ADD_CHECKSUM);
122         strncpy(element->entry.add_checksum.name, name, QEMU_FWCFG_MAX_NAME);
123         element->entry.add_checksum.off_le = htole32(off);
124         element->entry.add_checksum.start_le = htole32(start);
125         element->entry.add_checksum.len_le = htole32(len);
126
127         STAILQ_INSERT_TAIL(&loader->list, element, chain);
128
129         return (0);
130 }
131
132 int
133 qemu_loader_add_pointer(struct qemu_loader *const loader,
134     const uint8_t *dest_name, const uint8_t *src_name, const uint32_t off,
135     const uint8_t size)
136 {
137         struct qemu_loader_element *element;
138
139         if (strlen(dest_name) >= QEMU_FWCFG_MAX_NAME ||
140             strlen(src_name) >= QEMU_FWCFG_MAX_NAME)
141                 return (EINVAL);
142
143         element = calloc(1, sizeof(struct qemu_loader_element));
144         if (element == NULL) {
145                 warnx("%s: failed to allocate command", __func__);
146                 return (ENOMEM);
147         }
148
149         element->entry.cmd_le = htole32(QEMU_LOADER_CMD_ADD_POINTER);
150         strncpy(element->entry.add_pointer.dest_name, dest_name,
151             QEMU_FWCFG_MAX_NAME);
152         strncpy(element->entry.add_pointer.src_name, src_name,
153             QEMU_FWCFG_MAX_NAME);
154         element->entry.add_pointer.off_le = htole32(off);
155         element->entry.add_pointer.size = size;
156
157         STAILQ_INSERT_TAIL(&loader->list, element, chain);
158
159         return (0);
160 }
161
162 int
163 qemu_loader_create(struct qemu_loader **const new_loader,
164     const uint8_t *fwcfg_name)
165 {
166         struct qemu_loader *loader;
167
168         if (new_loader == NULL || strlen(fwcfg_name) >= QEMU_FWCFG_MAX_NAME) {
169                 return (EINVAL);
170         }
171
172         loader = calloc(1, sizeof(struct qemu_loader));
173         if (loader == NULL) {
174                 warnx("%s: failed to allocate loader", __func__);
175                 return (ENOMEM);
176         }
177
178         strncpy(loader->fwcfg_name, fwcfg_name, QEMU_FWCFG_MAX_NAME);
179         STAILQ_INIT(&loader->list);
180
181         *new_loader = loader;
182
183         return (0);
184 }
185
186 static const uint8_t *
187 qemu_loader_get_zone_name(const enum qemu_loader_zone zone)
188 {
189         switch (zone) {
190         case QEMU_LOADER_ALLOC_HIGH:
191                 return ("HIGH");
192         case QEMU_LOADER_ALLOC_FSEG:
193                 return ("FSEG");
194         default:
195                 return ("Unknown");
196         }
197 }
198
199 static void __unused
200 qemu_loader_dump_entry(const struct qemu_loader_entry *const entry)
201 {
202         switch (le32toh(entry->cmd_le)) {
203         case QEMU_LOADER_CMD_ALLOC:
204                 printf("CMD_ALLOC\n\r");
205                 printf("  name     : %s\n\r", entry->alloc.name);
206                 printf("  alignment: %8x\n\r",
207                     le32toh(entry->alloc.alignment_le));
208                 printf("  zone     : %s\n\r",
209                     qemu_loader_get_zone_name(entry->alloc.zone));
210                 break;
211         case QEMU_LOADER_CMD_ADD_POINTER:
212                 printf("CMD_ADD_POINTER\n\r");
213                 printf("  dest_name: %s\n\r", entry->add_pointer.dest_name);
214                 printf("  src_name : %s\n\r", entry->add_pointer.src_name);
215                 printf("  off      : %8x\n\r",
216                     le32toh(entry->add_pointer.off_le));
217                 printf("  size     : %8x\n\r", entry->add_pointer.size);
218                 break;
219         case QEMU_LOADER_CMD_ADD_CHECKSUM:
220                 printf("CMD_ADD_CHECKSUM\n\r");
221                 printf("  name     : %s\n\r", entry->add_checksum.name);
222                 printf("  off      : %8x\n\r",
223                     le32toh(entry->add_checksum.off_le));
224                 printf("  start    : %8x\n\r",
225                     le32toh(entry->add_checksum.start_le));
226                 printf("  length   : %8x\n\r",
227                     le32toh(entry->add_checksum.len_le));
228                 break;
229         case QEMU_LOADER_CMD_WRITE_POINTER:
230                 printf("CMD_WRITE_POINTER\n\r");
231                 printf("  dest_name: %s\n\r", entry->write_pointer.dest_name);
232                 printf("  src_name : %s\n\r", entry->write_pointer.src_name);
233                 printf("  dest_off : %8x\n\r",
234                     le32toh(entry->write_pointer.dest_off_le));
235                 printf("  src_off  : %8x\n\r",
236                     le32toh(entry->write_pointer.src_off_le));
237                 printf("  size     : %8x\n\r", entry->write_pointer.size);
238                 break;
239         default:
240                 printf("UNKNOWN\n\r");
241                 break;
242         }
243 }
244
245 int
246 qemu_loader_finish(struct qemu_loader *const loader)
247 {
248         struct qemu_loader_element *element;
249         struct qemu_loader_entry *data;
250         size_t len = 0;
251
252         STAILQ_FOREACH(element, &loader->list, chain) {
253                 len += sizeof(struct qemu_loader_entry);
254         }
255         if (len == 0) {
256                 warnx("%s: bios loader empty", __func__);
257                 return (EFAULT);
258         }
259
260         data = calloc(1, len);
261         if (data == NULL) {
262                 warnx("%s: failed to allocate fwcfg data", __func__);
263                 return (ENOMEM);
264         }
265
266         int i = 0;
267         STAILQ_FOREACH(element, &loader->list, chain) {
268                 memcpy(&data[i], &element->entry,
269                     sizeof(struct qemu_loader_entry));
270                 ++i;
271         }
272
273         return (qemu_fwcfg_add_file(loader->fwcfg_name, len, data));
274 }