xref: /openbsd/usr.sbin/vmd/fw_cfg.c (revision 65bbee46)
1 /*	$OpenBSD: fw_cfg.c,v 1.10 2024/09/26 01:45:13 jsg Exp $	*/
2 /*
3  * Copyright (c) 2018 Claudio Jeker <claudio@openbsd.org>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 #include <sys/types.h>
18 #include <machine/biosvar.h>	/* bios_memmap_t */
19 #include <dev/pv/virtioreg.h>
20 #include <dev/vmm/vmm.h>
21 
22 #include <stdlib.h>
23 #include <string.h>
24 #include <unistd.h>
25 
26 #include "atomicio.h"
27 #include "pci.h"
28 #include "vmd.h"
29 #include "vmm.h"
30 #include "fw_cfg.h"
31 
32 #define	FW_CFG_SIGNATURE	0x0000
33 #define	FW_CFG_ID		0x0001
34 #define	FW_CFG_NOGRAPHIC	0x0004
35 #define	FW_CFG_FILE_DIR		0x0019
36 #define	FW_CFG_FILE_FIRST	0x0020
37 
38 #define FW_CFG_DMA_SIGNATURE	0x51454d5520434647ULL /* QEMU CFG */
39 
40 struct fw_cfg_dma_access {
41 	uint32_t	control;
42 #define FW_CFG_DMA_ERROR	0x0001
43 #define FW_CFG_DMA_READ		0x0002
44 #define FW_CFG_DMA_SKIP		0x0004
45 #define FW_CFG_DMA_SELECT	0x0008
46 #define FW_CFG_DMA_WRITE	0x0010	/* not implemented */
47 	uint32_t	length;
48 	uint64_t	address;
49 };
50 
51 struct fw_cfg_file {
52 	uint32_t	size;
53 	uint16_t	selector;
54 	uint16_t	reserved;
55 	char		name[56];
56 };
57 
58 extern char *__progname;
59 
60 static struct fw_cfg_state {
61 	size_t offset;
62 	size_t size;
63 	uint8_t *data;
64 } fw_cfg_state;
65 
66 static uint64_t	fw_cfg_dma_addr;
67 
68 static bios_memmap_t e820[VMM_MAX_MEM_RANGES];
69 
70 static int	fw_cfg_select_file(uint16_t);
71 static void	fw_cfg_file_dir(void);
72 
73 void
fw_cfg_init(struct vmop_create_params * vmc)74 fw_cfg_init(struct vmop_create_params *vmc)
75 {
76 	unsigned int sd = 0;
77 	size_t i, e820_len = 0;
78 	char bootorder[64];
79 	const char *bootfmt;
80 	int bootidx = -1;
81 
82 	/* Define e820 memory ranges. */
83 	memset(&e820, 0, sizeof(e820));
84 	for (i = 0; i < vmc->vmc_params.vcp_nmemranges; i++) {
85 		struct vm_mem_range *range = &vmc->vmc_params.vcp_memranges[i];
86 		bios_memmap_t *entry = &e820[i];
87 		entry->addr = range->vmr_gpa;
88 		entry->size = range->vmr_size;
89 		if (range->vmr_type == VM_MEM_RAM)
90 			entry->type = BIOS_MAP_FREE;
91 		else
92 			entry->type = BIOS_MAP_RES;
93 		e820_len += sizeof(bios_memmap_t);
94 	}
95 	fw_cfg_add_file("etc/e820", &e820, e820_len);
96 
97 	/* do not double print chars on serial port */
98 	fw_cfg_add_file("etc/screen-and-debug", &sd, sizeof(sd));
99 
100 	switch (vmc->vmc_bootdevice) {
101 	case VMBOOTDEV_DISK:
102 		bootidx = pci_find_first_device(PCI_PRODUCT_VIRTIO_BLOCK);
103 		bootfmt = "/pci@i0cf8/*@%d\nHALT";
104 		break;
105 	case VMBOOTDEV_CDROM:
106 		bootidx = pci_find_first_device(PCI_PRODUCT_VIRTIO_SCSI);
107 		bootfmt = "/pci@i0cf8/*@%d/*@0/*@0,40000100\nHALT";
108 		break;
109 	case VMBOOTDEV_NET:
110 		/* XXX not yet */
111 		bootidx = pci_find_first_device(PCI_PRODUCT_VIRTIO_NETWORK);
112 		bootfmt = "HALT";
113 		break;
114 	}
115 	if (bootidx > -1) {
116 		snprintf(bootorder, sizeof(bootorder), bootfmt, bootidx);
117 		log_debug("%s: bootorder: %s", __func__, bootorder);
118 		fw_cfg_add_file("bootorder", bootorder, strlen(bootorder) + 1);
119 	}
120 }
121 
122 int
fw_cfg_dump(int fd)123 fw_cfg_dump(int fd)
124 {
125 	log_debug("%s: sending fw_cfg state", __func__);
126 	if (atomicio(vwrite, fd, &fw_cfg_dma_addr,
127 	    sizeof(fw_cfg_dma_addr)) != sizeof(fw_cfg_dma_addr)) {
128 		log_warnx("%s: error writing fw_cfg to fd", __func__);
129 		return -1;
130 	}
131 	if (atomicio(vwrite, fd, &fw_cfg_state.offset,
132 	    sizeof(fw_cfg_state.offset)) != sizeof(fw_cfg_state.offset)) {
133 		log_warnx("%s: error writing fw_cfg to fd", __func__);
134 		return -1;
135 	}
136 	if (atomicio(vwrite, fd, &fw_cfg_state.size,
137 	    sizeof(fw_cfg_state.size)) != sizeof(fw_cfg_state.size)) {
138 		log_warnx("%s: error writing fw_cfg to fd", __func__);
139 		return -1;
140 	}
141 	if (fw_cfg_state.size != 0)
142 		if (atomicio(vwrite, fd, fw_cfg_state.data,
143 		    fw_cfg_state.size) != fw_cfg_state.size) {
144 			log_warnx("%s: error writing fw_cfg to fd", __func__);
145 			return (-1);
146 		}
147 	return 0;
148 }
149 
150 int
fw_cfg_restore(int fd)151 fw_cfg_restore(int fd)
152 {
153 	log_debug("%s: receiving fw_cfg state", __func__);
154 	if (atomicio(read, fd, &fw_cfg_dma_addr,
155 	    sizeof(fw_cfg_dma_addr)) != sizeof(fw_cfg_dma_addr)) {
156 		log_warnx("%s: error reading fw_cfg from fd", __func__);
157 		return -1;
158 	}
159 	if (atomicio(read, fd, &fw_cfg_state.offset,
160 	    sizeof(fw_cfg_state.offset)) != sizeof(fw_cfg_state.offset)) {
161 		log_warnx("%s: error reading fw_cfg from fd", __func__);
162 		return -1;
163 	}
164 	if (atomicio(read, fd, &fw_cfg_state.size,
165 	    sizeof(fw_cfg_state.size)) != sizeof(fw_cfg_state.size)) {
166 		log_warnx("%s: error reading fw_cfg from fd", __func__);
167 		return -1;
168 	}
169 	fw_cfg_state.data = NULL;
170 	if (fw_cfg_state.size != 0) {
171 		if ((fw_cfg_state.data = malloc(fw_cfg_state.size)) == NULL)
172 			fatal("%s", __func__);
173 		if (atomicio(read, fd, fw_cfg_state.data,
174 		    fw_cfg_state.size) != fw_cfg_state.size) {
175 			log_warnx("%s: error reading fw_cfg from fd", __func__);
176 			return -1;
177 		}
178 	}
179 	return 0;
180 }
181 
182 static void
fw_cfg_reset_state(void)183 fw_cfg_reset_state(void)
184 {
185 	free(fw_cfg_state.data);
186 	fw_cfg_state.offset = 0;
187 	fw_cfg_state.size = 0;
188 	fw_cfg_state.data = NULL;
189 }
190 
191 static void
fw_cfg_set_state(void * data,size_t len)192 fw_cfg_set_state(void *data, size_t len)
193 {
194 	if ((fw_cfg_state.data = malloc(len)) == NULL) {
195 		log_warn("%s", __func__);
196 		return;
197 	}
198 	memcpy(fw_cfg_state.data, data, len);
199 	fw_cfg_state.size = len;
200 	fw_cfg_state.offset = 0;
201 }
202 
203 static void
fw_cfg_select(uint16_t selector)204 fw_cfg_select(uint16_t selector)
205 {
206 	uint16_t one = 1;
207 	uint32_t id = htole32(0x3);
208 
209 	fw_cfg_reset_state();
210 	switch (selector) {
211 	case FW_CFG_SIGNATURE:
212 		fw_cfg_set_state("QEMU", 4);
213 		break;
214 	case FW_CFG_ID:
215 		fw_cfg_set_state(&id, sizeof(id));
216 		break;
217 	case FW_CFG_NOGRAPHIC:
218 		fw_cfg_set_state(&one, sizeof(one));
219 		break;
220 	case FW_CFG_FILE_DIR:
221 		fw_cfg_file_dir();
222 		break;
223 	default:
224 		if (!fw_cfg_select_file(selector))
225 			log_debug("%s: unhandled selector %x",
226 			    __func__, selector);
227 		break;
228 	}
229 }
230 
231 static void
fw_cfg_handle_dma(struct fw_cfg_dma_access * fw)232 fw_cfg_handle_dma(struct fw_cfg_dma_access *fw)
233 {
234 	uint32_t len = 0, control = fw->control;
235 
236 	fw->control = 0;
237 	if (control & FW_CFG_DMA_SELECT) {
238 		uint16_t selector = control >> 16;
239 		log_debug("%s: selector 0x%04x", __func__, selector);
240 		fw_cfg_select(selector);
241 	}
242 
243 	/* calculate correct length of operation */
244 	if (fw_cfg_state.offset < fw_cfg_state.size)
245 		len = fw_cfg_state.size - fw_cfg_state.offset;
246 	if (len > fw->length)
247 		len = fw->length;
248 
249 	if (control & FW_CFG_DMA_WRITE) {
250 		fw->control |= FW_CFG_DMA_ERROR;
251 	} else if (control & FW_CFG_DMA_READ) {
252 		if (write_mem(fw->address,
253 		    fw_cfg_state.data + fw_cfg_state.offset, len)) {
254 			log_warnx("%s: write_mem error", __func__);
255 			fw->control |= FW_CFG_DMA_ERROR;
256 		}
257 		/* clear rest of buffer */
258 		if (len < fw->length)
259 			if (write_mem(fw->address + len, NULL,
260 			    fw->length - len)) {
261 			log_warnx("%s: write_mem error", __func__);
262 			fw->control |= FW_CFG_DMA_ERROR;
263 		}
264 	}
265 	fw_cfg_state.offset += len;
266 
267 	if (fw_cfg_state.offset == fw_cfg_state.size)
268 		fw_cfg_reset_state();
269 }
270 
271 uint8_t
vcpu_exit_fw_cfg(struct vm_run_params * vrp)272 vcpu_exit_fw_cfg(struct vm_run_params *vrp)
273 {
274 	uint32_t data = 0;
275 	struct vm_exit *vei = vrp->vrp_exit;
276 
277 	get_input_data(vei, &data);
278 
279 	switch (vei->vei.vei_port) {
280 	case FW_CFG_IO_SELECT:
281 		if (vei->vei.vei_dir == VEI_DIR_IN) {
282 			log_warnx("%s: fw_cfg: read from selector port "
283 			    "unsupported", __progname);
284 			set_return_data(vei, 0);
285 			break;
286 		}
287 		log_debug("%s: selector 0x%04x", __func__, data);
288 		fw_cfg_select(data);
289 		break;
290 	case FW_CFG_IO_DATA:
291 		if (vei->vei.vei_dir == VEI_DIR_OUT) {
292 			log_debug("%s: fw_cfg: discarding data written to "
293 			    "data port", __progname);
294 			break;
295 		}
296 		/* fw_cfg only defines 1-byte reads via IO port */
297 		if (fw_cfg_state.offset < fw_cfg_state.size) {
298 			set_return_data(vei,
299 			    fw_cfg_state.data[fw_cfg_state.offset++]);
300 			if (fw_cfg_state.offset == fw_cfg_state.size)
301 				fw_cfg_reset_state();
302 		} else
303 			set_return_data(vei, 0);
304 		break;
305 	}
306 
307 	return 0xFF;
308 }
309 
310 uint8_t
vcpu_exit_fw_cfg_dma(struct vm_run_params * vrp)311 vcpu_exit_fw_cfg_dma(struct vm_run_params *vrp)
312 {
313 	struct fw_cfg_dma_access fw_dma;
314 	uint32_t data = 0;
315 	struct vm_exit *vei = vrp->vrp_exit;
316 
317 	if (vei->vei.vei_size != 4) {
318 		log_debug("%s: fw_cfg_dma: discarding data written to "
319 		    "dma addr", __progname);
320 		if (vei->vei.vei_dir == VEI_DIR_OUT)
321 			fw_cfg_dma_addr = 0;
322 		return 0xFF;
323 	}
324 
325 	if (vei->vei.vei_dir == VEI_DIR_OUT) {
326 		get_input_data(vei, &data);
327 		switch (vei->vei.vei_port) {
328 		case FW_CFG_IO_DMA_ADDR_HIGH:
329 			fw_cfg_dma_addr = (uint64_t)be32toh(data) << 32;
330 			break;
331 		case FW_CFG_IO_DMA_ADDR_LOW:
332 			fw_cfg_dma_addr |= be32toh(data);
333 
334 			/* writing least significant half triggers operation */
335 			if (read_mem(fw_cfg_dma_addr, &fw_dma, sizeof(fw_dma)))
336 				break;
337 			/* adjust byteorder */
338 			fw_dma.control = be32toh(fw_dma.control);
339 			fw_dma.length = be32toh(fw_dma.length);
340 			fw_dma.address = be64toh(fw_dma.address);
341 
342 			fw_cfg_handle_dma(&fw_dma);
343 
344 			/* just write control byte back */
345 			data = be32toh(fw_dma.control);
346 			if (write_mem(fw_cfg_dma_addr, &data, sizeof(data)))
347 				break;
348 
349 			/* done, reset base address */
350 			fw_cfg_dma_addr = 0;
351 			break;
352 		}
353 	} else {
354 		uint64_t sig = htobe64(FW_CFG_DMA_SIGNATURE);
355 		switch (vei->vei.vei_port) {
356 		case FW_CFG_IO_DMA_ADDR_HIGH:
357 			set_return_data(vei, sig >> 32);
358 			break;
359 		case FW_CFG_IO_DMA_ADDR_LOW:
360 			set_return_data(vei, sig & 0xffffffff);
361 			break;
362 		}
363 	}
364 	return 0xFF;
365 }
366 
367 static uint16_t file_id = FW_CFG_FILE_FIRST;
368 
369 struct fw_cfg_file_entry {
370 	TAILQ_ENTRY(fw_cfg_file_entry)	entry;
371 	struct fw_cfg_file		file;
372 	void				*data;
373 };
374 
375 TAILQ_HEAD(, fw_cfg_file_entry) fw_cfg_files =
376 					TAILQ_HEAD_INITIALIZER(fw_cfg_files);
377 
378 static struct fw_cfg_file_entry *
fw_cfg_lookup_file(const char * name)379 fw_cfg_lookup_file(const char *name)
380 {
381 	struct fw_cfg_file_entry *f;
382 
383 	TAILQ_FOREACH(f, &fw_cfg_files, entry) {
384 		if (strcmp(name, f->file.name) == 0)
385 			return f;
386 	}
387 	return NULL;
388 }
389 
390 void
fw_cfg_add_file(const char * name,const void * data,size_t len)391 fw_cfg_add_file(const char *name, const void *data, size_t len)
392 {
393 	struct fw_cfg_file_entry *f;
394 
395 	if (fw_cfg_lookup_file(name))
396 		fatalx("%s: fw_cfg: file %s exists", __progname, name);
397 
398 	if ((f = calloc(1, sizeof(*f))) == NULL)
399 		fatal("%s", __func__);
400 
401 	if ((f->data = malloc(len)) == NULL)
402 		fatal("%s", __func__);
403 
404 	if (strlcpy(f->file.name, name, sizeof(f->file.name)) >=
405 	    sizeof(f->file.name))
406 		fatalx("%s: fw_cfg: file name too long", __progname);
407 
408 	f->file.size = htobe32(len);
409 	f->file.selector = htobe16(file_id++);
410 	memcpy(f->data, data, len);
411 
412 	TAILQ_INSERT_TAIL(&fw_cfg_files, f, entry);
413 }
414 
415 static int
fw_cfg_select_file(uint16_t id)416 fw_cfg_select_file(uint16_t id)
417 {
418 	struct fw_cfg_file_entry *f;
419 
420 	id = htobe16(id);
421 	TAILQ_FOREACH(f, &fw_cfg_files, entry)
422 		if (f->file.selector == id) {
423 			size_t size = be32toh(f->file.size);
424 			fw_cfg_set_state(f->data, size);
425 			log_debug("%s: accessing file %s", __func__,
426 			    f->file.name);
427 			return 1;
428 		}
429 	return 0;
430 }
431 
432 static void
fw_cfg_file_dir(void)433 fw_cfg_file_dir(void)
434 {
435 	struct fw_cfg_file_entry *f;
436 	struct fw_cfg_file *fp;
437 	uint32_t count = 0;
438 	uint32_t *data;
439 	size_t size;
440 
441 	TAILQ_FOREACH(f, &fw_cfg_files, entry)
442 		count++;
443 
444 	size = sizeof(count) + count * sizeof(struct fw_cfg_file);
445 	if ((data = malloc(size)) == NULL)
446 		fatal("%s", __func__);
447 	*data = htobe32(count);
448 	fp = (struct fw_cfg_file *)(data + 1);
449 
450 	log_debug("%s: file directory with %d files", __func__, count);
451 	TAILQ_FOREACH(f, &fw_cfg_files, entry) {
452 		log_debug("  %6dB %04x %s", be32toh(f->file.size),
453 		    be16toh(f->file.selector), f->file.name);
454 		memcpy(fp, &f->file, sizeof(f->file));
455 		fp++;
456 	}
457 
458 	/* XXX should sort by name but SeaBIOS does not care */
459 
460 	fw_cfg_set_state(data, size);
461 }
462