1/*	$OpenBSD: i386_softraid.c,v 1.21 2022/11/07 15:56:09 kn Exp $	*/
2/*
3 * Copyright (c) 2012 Joel Sing <jsing@openbsd.org>
4 * Copyright (c) 2010 Otto Moerbeek <otto@drijf.net>
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 */
20#include <sys/disklabel.h>
21#include <sys/dkio.h>
22#include <sys/ioctl.h>
23#include <sys/stat.h>
24
25#include <dev/biovar.h>
26#include <dev/softraidvar.h>
27#include <ufs/ufs/dinode.h>
28
29#include <err.h>
30#include <fcntl.h>
31#include <stdio.h>
32#include <stdlib.h>
33#include <string.h>
34#include <unistd.h>
35
36#include "installboot.h"
37#include "i386_installboot.h"
38
39void	sr_install_bootblk(int, int, int);
40void	sr_install_bootldr(int, char *);
41
42void
43sr_install_bootblk(int devfd, int vol, int disk)
44{
45	struct bioc_disk bd;
46	struct disklabel dl;
47	struct partition *pp;
48	uint32_t poffset;
49	char *dev;
50	char part, efipart;
51	int diskfd;
52
53	diskfd = sr_open_chunk(devfd, vol, disk, &bd, &dev, &part);
54	if (diskfd == -1)
55		return;
56
57	/* Get and check disklabel. */
58	if (ioctl(diskfd, DIOCGDINFO, &dl) == -1)
59		err(1, "disklabel: %s", dev);
60	if (dl.d_magic != DISKMAGIC)
61		err(1, "bad disklabel magic=0x%08x", dl.d_magic);
62
63	/* Warn on unknown disklabel types. */
64	if (dl.d_type == 0)
65		warnx("disklabel type unknown");
66
67	efipart = findgptefisys(diskfd, &dl);
68	if (efipart != -1) {
69		write_filesystem(&dl, (char)efipart);
70		return;
71	}
72
73	/* Determine poffset and set symbol value. */
74	pp = &dl.d_partitions[part - 'a'];
75	if (pp->p_offseth != 0)
76		errx(1, "partition offset too high");
77	poffset = pp->p_offset;			/* Offset of RAID partition. */
78	poffset += SR_BOOT_LOADER_OFFSET;	/* SR boot loader area. */
79	sym_set_value(pbr_symbols, "_p_offset", poffset);
80
81	if (verbose)
82		fprintf(stderr, "%s%c: %s boot blocks on %s, part offset %u\n",
83		    bd.bd_vendor, part,
84		    (nowrite ? "would install" : "installing"), dev, poffset);
85
86	/* Write boot blocks to device. */
87	write_bootblocks(diskfd, dev, &dl);
88
89	close(diskfd);
90}
91
92void
93sr_install_bootldr(int devfd, char *dev)
94{
95	struct bioc_installboot bb;
96	struct stat sb;
97	struct ufs1_dinode *ino_p;
98	uint32_t bootsize, inodeblk, inodedbl;
99	uint16_t bsize = SR_FS_BLOCKSIZE;
100	uint16_t nblocks;
101	uint8_t bshift = 5;		/* fragsize == blocksize */
102	int fd, i;
103	u_char *p;
104
105	/*
106	 * Install boot loader into softraid boot loader storage area.
107	 *
108	 * In order to allow us to reuse the existing biosboot we construct
109	 * a fake FFS filesystem with a single inode, which points to the
110	 * boot loader.
111	 */
112
113	nblocks = howmany(SR_BOOT_LOADER_SIZE, SR_FS_BLOCKSIZE / DEV_BSIZE);
114	inodeblk = nblocks - 1;
115	bootsize = nblocks * SR_FS_BLOCKSIZE;
116
117	p = calloc(1, bootsize);
118	if (p == NULL)
119		err(1, NULL);
120
121	fd = open(stage2, O_RDONLY);
122	if (fd == -1)
123		err(1, NULL);
124
125	if (fstat(fd, &sb) == -1)
126		err(1, NULL);
127
128	nblocks = howmany(sb.st_blocks, SR_FS_BLOCKSIZE / DEV_BSIZE);
129	if (sb.st_blocks * S_BLKSIZE > bootsize -
130	    (int)(sizeof(struct ufs1_dinode)))
131		errx(1, "boot code will not fit");
132
133	/* We only need to fill the direct block array. */
134	ino_p = (struct ufs1_dinode *)&p[bootsize - sizeof(struct ufs1_dinode)];
135
136	ino_p->di_mode = sb.st_mode;
137	ino_p->di_nlink = 1;
138	ino_p->di_inumber = 0xfeebfaab;
139	ino_p->di_size = read(fd, p, sb.st_blocks * S_BLKSIZE);
140	ino_p->di_blocks = nblocks;
141	for (i = 0; i < nblocks; i++)
142		ino_p->di_db[i] = i;
143
144	inodedbl = ((u_char*)&ino_p->di_db[0] -
145	    &p[bootsize - SR_FS_BLOCKSIZE]) + INODEOFF;
146
147	memset(&bb, 0, sizeof(bb));
148	bb.bb_bootldr = p;
149	bb.bb_bootldr_size = bootsize;
150	bb.bb_bootblk = "XXX";
151	bb.bb_bootblk_size = sizeof("XXX");
152	strncpy(bb.bb_dev, dev, sizeof(bb.bb_dev));
153	if (verbose)
154		fprintf(stderr, "%s: %s boot loader on softraid volume\n", dev,
155		    (nowrite ? "would install" : "installing"));
156	if (!nowrite) {
157		if (ioctl(devfd, BIOCINSTALLBOOT, &bb) == -1)
158			errx(1, "softraid installboot failed");
159		sr_status(&bb.bb_bio.bio_status);
160	}
161
162	/*
163	 * Set the values that will need to go into biosboot
164	 * (the partition boot record, a.k.a. the PBR).
165	 */
166	sym_set_value(pbr_symbols, "_fs_bsize_p", (bsize / 16));
167	sym_set_value(pbr_symbols, "_fs_bsize_s", (bsize / 512));
168	sym_set_value(pbr_symbols, "_fsbtodb", bshift);
169	sym_set_value(pbr_symbols, "_inodeblk", inodeblk);
170	sym_set_value(pbr_symbols, "_inodedbl", inodedbl);
171	sym_set_value(pbr_symbols, "_nblocks", nblocks);
172	sym_set_value(pbr_symbols, "_blkincr", 0);
173
174	if (verbose)
175		fprintf(stderr, "%s is %d blocks x %d bytes\n",
176		    stage2, nblocks, bsize);
177
178	free(p);
179	close(fd);
180}
181