GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/vmd/vmboot.c Lines: 0 212 0.0 %
Date: 2017-11-13 Branches: 0 124 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: vmboot.c,v 1.4 2017/08/29 21:10:20 deraadt Exp $	*/
2
3
/*
4
 * Copyright (c) 2016 Reyk Floeter <reyk@openbsd.org>
5
 *
6
 * Permission to use, copy, modify, and distribute this software for any
7
 * purpose with or without fee is hereby granted, provided that the above
8
 * copyright notice and this permission notice appear in all copies.
9
 *
10
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17
 */
18
19
#include <sys/param.h>	/* DEV_BSIZE roundup */
20
#include <sys/reboot.h>
21
#include <sys/time.h>
22
#include <sys/stat.h>
23
#include <sys/disklabel.h>
24
25
#include <ufs/ffs/fs.h>
26
#include <ufs/ufs/dinode.h>
27
#include <ufs/ufs/dir.h>
28
29
#include <stdio.h>
30
#include <unistd.h>
31
#include <ctype.h>
32
#include <fcntl.h>
33
#include <err.h>
34
#include <vis.h>
35
36
#include "vmd.h"
37
#include "vmboot.h"
38
39
int	 vmboot_bootconf(char *, size_t, struct vmboot_params *);
40
int	 vmboot_bootcmd(char *, struct vmboot_params *);
41
int	 vmboot_bootargs(int argc, char **argv, struct vmboot_params *);
42
uint32_t vmboot_bootdevice(const char *);
43
44
int	 vmboot_strategy(void *, int, daddr32_t, size_t, void *, size_t *);
45
off_t	 vmboot_findopenbsd(struct open_file *, off_t, struct disklabel *);
46
void	*vmboot_loadfile(struct open_file *, char *, size_t *);
47
48
int
49
vmboot_bootcmd(char *line, struct vmboot_params *bp)
50
{
51
	char *p, *args[16];
52
	int ac = 0;
53
	char *last;
54
55
	for (args[0] = NULL, (p = strtok_r(line, " ", &last)); p;
56
	    (p = strtok_r(NULL, " ", &last))) {
57
		if (ac < (int)(sizeof(args) / sizeof(args[0])) - 1)
58
			args[ac++] = p;
59
	}
60
	if (ac == 0)
61
		return (0);
62
	args[ac] = NULL;
63
64
	/*
65
	 * Subset of boot.conf(8) options
66
	 */
67
	if (strcmp("boot", args[0]) == 0)
68
		return (vmboot_bootargs(ac, args, bp));
69
	else if (strcmp("set", args[0]) == 0) {
70
		if (ac < 3)
71
			return (-1);
72
		if (strcmp("device", args[1]) == 0) {
73
			if ((size_t)strnvis(bp->vbp_device, args[2],
74
			    sizeof(bp->vbp_device), VIS_SAFE) >=
75
			    sizeof(bp->vbp_device)) {
76
				log_warnx("invalid device name");
77
				return (-1);
78
			}
79
		} else if (strcmp("image", args[1]) == 0) {
80
			if ((size_t)strnvis(bp->vbp_image, args[2],
81
			    sizeof(bp->vbp_image), VIS_SAFE) >=
82
			    sizeof(bp->vbp_image)) {
83
				log_warnx("invalid image name");
84
				return (-1);
85
			}
86
		}
87
	}
88
89
	return (0);
90
}
91
92
int
93
vmboot_bootargs(int ac, char **av, struct vmboot_params *bp)
94
{
95
	char *p;
96
	int ch;
97
98
	if (ac < 2)
99
		return (0);
100
101
	/*
102
	 * Syntax is based on boot(8): boot "[hd0a[:/file]] [-asdc]"
103
	 */
104
	if (*av[1] != '-') {
105
		if ((p = strchr(av[1], ':')) != NULL) {
106
			*p++ = '\0';
107
			if (!strlen(p)) {
108
				log_warnx("invalid file syntax");
109
				return (-1);
110
			}
111
			if ((size_t)strnvis(bp->vbp_device, av[1],
112
			    sizeof(bp->vbp_device), VIS_SAFE) >=
113
			    sizeof(bp->vbp_device)) {
114
				log_warnx("invalid device name");
115
				return (-1);
116
			}
117
		} else {
118
			p = av[1];
119
		}
120
		if ((size_t)strnvis(bp->vbp_image, p,
121
		    sizeof(bp->vbp_image), VIS_SAFE) >= sizeof(bp->vbp_image)) {
122
			log_warnx("invalid image name");
123
			return (-1);
124
		}
125
		ac--;
126
		av++;
127
	}
128
129
	optreset = optind = opterr = 1;
130
	while ((ch = getopt(ac, av, "acds")) != -1) {
131
		switch (ch) {
132
		case 'a':
133
			bp->vbp_howto |= RB_ASKNAME;
134
			break;
135
		case 'c':
136
			bp->vbp_howto |= RB_CONFIG;
137
			break;
138
		case 'd':
139
			bp->vbp_howto |= RB_KDB;
140
			break;
141
		case 's':
142
			bp->vbp_howto |= RB_SINGLE;
143
			break;
144
		default:
145
			log_warnx("invalid boot option: %c", ch);
146
			return (-1);
147
		}
148
	}
149
150
	return (0);
151
}
152
153
uint32_t
154
vmboot_bootdevice(const char *word)
155
{
156
	uint32_t	 bootdev = 0;
157
	int		 disk, part;
158
159
	if (strlen(word) != strlen("hd0a")) {
160
		log_warnx("invalid boot device: %s", word);
161
		goto done;
162
	}
163
164
	if (strncmp("hd", word, 2) != 0) {
165
		log_warnx("unsupported boot device type: %s", word);
166
		goto done;
167
	}
168
169
	disk = (int)word[2];
170
	part = (int)word[3];
171
172
	if (!(isdigit(disk) && isalpha(part) && islower(part))) {
173
		log_warnx("invalid boot partition: %s", word);
174
		goto done;
175
	}
176
177
	disk -= '0';
178
	part -= 'a';
179
180
	if (disk != 0 || part > MAXPARTITIONS) {
181
		log_warnx("cannot boot from device: %s", word);
182
		goto done;
183
	}
184
185
	bootdev = MAKEBOOTDEV(0x4, 0, 0, disk, part);
186
187
 done:
188
	/* returns 0 on error */
189
	return (bootdev);
190
}
191
192
int
193
vmboot_bootconf(char *conf, size_t size, struct vmboot_params *bp)
194
{
195
	char	 buf[BUFSIZ];
196
	FILE	*fp;
197
198
	if ((fp = fmemopen(conf, size, "r")) == NULL) {
199
		log_debug("%s: failed to boot.conf memory stream", __func__);
200
		return (-1);
201
	}
202
203
	while (fgets(buf, sizeof(buf), fp) != NULL) {
204
		buf[strcspn(buf, "\n")] = '\0';
205
		vmboot_bootcmd(buf, bp);
206
	}
207
	fclose(fp);
208
209
	if (strlen(bp->vbp_device))
210
		log_debug("%s: set device %s", __func__, bp->vbp_device);
211
	if (strlen(bp->vbp_image))
212
		log_debug("%s: set image %s", __func__, bp->vbp_image);
213
	if (bp->vbp_howto) {
214
		snprintf(buf, sizeof(buf), "boot -%s%s%s%s",
215
		    (bp->vbp_howto & RB_ASKNAME) ? "a" : "",
216
		    (bp->vbp_howto & RB_CONFIG) ? "c" : "",
217
		    (bp->vbp_howto & RB_KDB) ? "d" : "",
218
		    (bp->vbp_howto & RB_SINGLE) ? "s" : "");
219
		log_debug("%s: %s", __func__, buf);
220
	}
221
222
	return (0);
223
}
224
225
226
/*
227
 * For ufs.c
228
 */
229
230
struct devsw vmboot_devsw = {
231
	.dv_name =	"vmboot",
232
	.dv_strategy =	vmboot_strategy,
233
	/* other fields are not needed */
234
};
235
236
struct open_file vmboot_file = {
237
	.f_dev =	&vmboot_devsw,
238
	.f_devdata =	 NULL
239
};
240
241
int
242
vmboot_strategy(void *devdata, int rw,
243
    daddr32_t blk, size_t size, void *buf, size_t *rsize)
244
{
245
	struct vmboot_params	*vmboot = devdata;
246
	ssize_t			 rlen;
247
248
	if (vmboot->vbp_fd == -1)
249
		return (EIO);
250
251
	switch (rw) {
252
	case F_READ:
253
		rlen = pread(vmboot->vbp_fd, buf, size,
254
		    (blk + vmboot->vbp_partoff) * DEV_BSIZE);
255
		if (rlen == -1)
256
			return (errno);
257
		*rsize = (size_t)rlen;
258
		break;
259
	case F_WRITE:
260
		rlen = pwrite(vmboot->vbp_fd, buf, size,
261
		    (blk + vmboot->vbp_partoff) * DEV_BSIZE);
262
		if (rlen == -1)
263
			return (errno);
264
		*rsize = (size_t)rlen;
265
		break;
266
	default:
267
		return (EINVAL);
268
	}
269
	return (0);
270
}
271
272
/*
273
 * Based on findopenbsd() from biosdev.c that was partially written by me.
274
 */
275
off_t
276
vmboot_findopenbsd(struct open_file *f, off_t mbroff, struct disklabel *dl)
277
{
278
	struct dos_mbr		 mbr;
279
	struct dos_partition	*dp;
280
	off_t			 mbr_eoff = DOSBBSECTOR, nextebr;
281
	int			 ret, i;
282
	static int		 maxebr = DOS_MAXEBR;
283
	size_t			 rsize;
284
	char			 buf[DEV_BSIZE], *msg;
285
286
	if (!maxebr--) {
287
		log_debug("%s: too many extended partitions", __func__);
288
		return (-1);
289
	}
290
291
	memset(&mbr, 0, sizeof(mbr));
292
	ret = (f->f_dev->dv_strategy)(f->f_devdata, F_READ,
293
	    mbroff, sizeof(mbr), &mbr, &rsize);
294
	if (ret != 0 || rsize != sizeof(mbr)) {
295
		log_debug("%s: failed to read MBR", __func__);
296
		return (-1);
297
	}
298
299
	if (mbr.dmbr_sign != DOSMBR_SIGNATURE) {
300
		log_debug("%s: bad MBR signature", __func__);
301
		return (-1);
302
	}
303
304
	/* Search for the first OpenBSD partition */
305
	nextebr = 0;
306
	for (i = 0; i < NDOSPART; i++) {
307
		dp = &mbr.dmbr_parts[i];
308
		if (!dp->dp_size)
309
			continue;
310
311
		if (dp->dp_typ == DOSPTYP_OPENBSD) {
312
			if (dp->dp_start > (dp->dp_start + mbroff))
313
				continue;
314
315
			/* Load and parse the disk label */
316
			ret = (f->f_dev->dv_strategy)(f->f_devdata, F_READ,
317
			    dp->dp_start + mbroff + DOS_LABELSECTOR,
318
			    sizeof(buf), buf, &rsize);
319
			if (ret != 0 || rsize != sizeof(buf)) {
320
				log_warn("could not load disk label");
321
				return (-1);
322
			}
323
			if ((msg = getdisklabel(buf, dl)) != NULL) {
324
				log_warnx("%s", msg);
325
				return (-1);
326
			}
327
328
			return (dp->dp_start + mbroff);
329
		}
330
331
		if (!nextebr && (dp->dp_typ == DOSPTYP_EXTEND ||
332
		    dp->dp_typ == DOSPTYP_EXTENDL)) {
333
			nextebr = dp->dp_start + mbr_eoff;
334
			if (nextebr < dp->dp_start)
335
				nextebr = -1;
336
			if (mbr_eoff == DOSBBSECTOR)
337
				mbr_eoff = dp->dp_start;
338
		}
339
	}
340
341
	if (nextebr && nextebr != -1) {
342
		mbroff = nextebr;
343
		return (vmboot_findopenbsd(f, mbroff, dl));
344
	}
345
346
	return (-1);
347
}
348
349
void *
350
vmboot_loadfile(struct open_file *f, char *file, size_t *size)
351
{
352
	char		*buf = NULL, *p = NULL;
353
	struct stat	 st;
354
	size_t		 rsize;
355
	int		 ret;
356
357
	*size = 0;
358
359
	if ((ret = ufs_open(file, f)) != 0)
360
		return (NULL);
361
362
	if ((ret = ufs_stat(f, &st)) != 0) {
363
		log_debug("%s: failed to stat %s", __func__, file);
364
		goto done;
365
	}
366
367
	if ((buf = calloc(1, roundup(st.st_size, DEV_BSIZE))) == NULL) {
368
		log_debug("%s: failed to allocate buffer", __func__);
369
		goto done;
370
	}
371
372
	if ((ret = ufs_read(f, buf, st.st_size, &rsize)) != 0) {
373
		log_debug("%s: failed to read %s", __func__, file);
374
		free(buf);
375
		goto done;
376
	}
377
378
	*size = st.st_size;
379
	p = buf;
380
 done:
381
	ufs_close(f);
382
	return (p);
383
}
384
385
FILE *
386
vmboot_open(int kernel_fd, int disk_fd, struct vmboot_params *vmboot)
387
{
388
	char			 file[PATH_MAX];
389
	char			*buf = NULL;
390
	size_t			 size;
391
	FILE			*fp = NULL;
392
	struct disklabel	 dl;
393
394
	memset(vmboot, 0, sizeof(*vmboot));
395
	memset(&dl, 0, sizeof(dl));
396
397
	/* First open kernel directly if specified by fd */
398
	if (kernel_fd != -1)
399
		return (fdopen(kernel_fd, "r"));
400
401
	if (disk_fd == -1)
402
		return (NULL);
403
404
	vmboot->vbp_fd = disk_fd;
405
	vmboot_file.f_devdata = vmboot;
406
407
	if ((vmboot->vbp_partoff =
408
	    vmboot_findopenbsd(&vmboot_file, 0, &dl)) == -1) {
409
		log_debug("%s: could not find openbsd partition", __func__);
410
		return (NULL);
411
	}
412
413
	/* Set the default kernel boot device and image path */
414
	strlcpy(vmboot->vbp_device, VM_DEFAULT_DEVICE,
415
	    sizeof(vmboot->vbp_device));
416
	strlcpy(vmboot->vbp_image, VM_DEFAULT_KERNEL,
417
	    sizeof(vmboot->vbp_image));
418
419
	/* Try to parse boot.conf to overwrite the default kernel path */
420
	strlcpy(file, VM_BOOT_CONF, sizeof(file));
421
	if ((buf = vmboot_loadfile(&vmboot_file, file, &size)) != NULL) {
422
		if (vmboot_bootconf(buf, size, vmboot) == -1) {
423
			free(buf);
424
			return (NULL);
425
		}
426
		free(buf);
427
	}
428
429
	/* Parse boot device and find partition in disk label */
430
	if ((vmboot->vbp_bootdev =
431
	    vmboot_bootdevice(vmboot->vbp_device)) == 0)
432
		return (NULL);
433
	if (B_PARTITION(vmboot->vbp_bootdev) > dl.d_npartitions) {
434
		log_debug("%s: invalid boot partition: %s",
435
		    __func__, vmboot->vbp_device);
436
		return (NULL);
437
	}
438
	vmboot->vbp_partoff =
439
	    dl.d_partitions[B_PARTITION(vmboot->vbp_bootdev)].p_offset;
440
441
	/* Load the kernel */
442
	if ((buf = vmboot_loadfile(&vmboot_file,
443
	    vmboot->vbp_image, &size)) == NULL) {
444
		log_debug("%s: failed to open kernel %s:%s", __func__,
445
		    vmboot->vbp_device, vmboot->vbp_image);
446
		return (NULL);
447
	}
448
	vmboot->vbp_arg = buf;
449
450
	if ((fp = fmemopen(buf, size, "r")) == NULL) {
451
		log_debug("%s: failed to open memory stream", __func__);
452
		free(buf);
453
		vmboot->vbp_arg = NULL;
454
	} else {
455
		log_debug("%s: kernel %s:%s", __func__,
456
		    vmboot->vbp_device, vmboot->vbp_image);
457
	}
458
459
	return (fp);
460
}
461
462
void
463
vmboot_close(FILE *fp, struct vmboot_params *vmboot)
464
{
465
	fclose(fp);
466
	free(vmboot->vbp_arg);
467
}