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 } 275