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