1 /* $OpenBSD: mbr.c,v 1.124 2023/05/17 12:59:37 krw Exp $ */
2
3 /*
4 * Copyright (c) 1997 Tobias Weingartner
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/ioctl.h>
21 #include <sys/disklabel.h>
22 #include <sys/dkio.h>
23
24 #include <err.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28
29 #include "part.h"
30 #include "disk.h"
31 #include "misc.h"
32 #include "mbr.h"
33 #include "gpt.h"
34
35 struct dos_mbr default_dmbr;
36
37 void mbr_to_dos_mbr(const struct mbr *, struct dos_mbr *);
38 void dos_mbr_to_mbr(const struct dos_mbr *, const uint64_t,
39 const uint64_t, struct mbr *);
40
41 void
MBR_init(struct mbr * mbr)42 MBR_init(struct mbr *mbr)
43 {
44 struct dos_partition dp;
45 struct prt bootprt, obsdprt;
46 daddr_t daddr;
47
48 memset(&gmbr, 0, sizeof(gmbr));
49 memset(&gh, 0, sizeof(gh));
50 memset(&gp, 0, sizeof(gp));
51
52 if (mbr->mbr_lba_self != 0) {
53 /* Extended MBR - save lba's, set sig, zap everything else. */
54 memset(mbr->mbr_code, 0, sizeof(mbr->mbr_code));
55 memset(mbr->mbr_prt, 0, sizeof(mbr->mbr_prt));
56 mbr->mbr_signature = DOSMBR_SIGNATURE;
57 return;
58 }
59
60 memset(&obsdprt, 0, sizeof(obsdprt));
61 memset(&bootprt, 0, sizeof(bootprt));
62
63 if (disk.dk_bootprt.prt_ns > 0) {
64 bootprt = disk.dk_bootprt;
65 } else {
66 memcpy(&dp, &default_dmbr.dmbr_parts[0], sizeof(dp));
67 PRT_dp_to_prt(&dp, 0, 0, &bootprt);
68 }
69
70 if (bootprt.prt_ns > 0) {
71 /* Start OpenBSD partition immediately after bootprt. */
72 obsdprt.prt_bs = bootprt.prt_bs + bootprt.prt_ns;
73 } else if (disk.dk_heads > 1 || disk.dk_cylinders > 1) {
74 /*
75 * Start OpenBSD partition on power of 2 block number
76 * after the first track.
77 */
78 daddr = 1;
79 while (daddr < DL_SECTOBLK(&dl, disk.dk_sectors))
80 daddr *= 2;
81 obsdprt.prt_bs = DL_BLKTOSEC(&dl, daddr);
82 } else {
83 /* Start OpenBSD partition immediately after MBR. */
84 obsdprt.prt_bs = 1;
85 }
86
87 if (obsdprt.prt_bs >= disk.dk_size) {
88 memset(&obsdprt, 0, sizeof(obsdprt));
89 } else {
90 obsdprt.prt_ns = disk.dk_size - obsdprt.prt_bs;
91 obsdprt.prt_id = DOSPTYP_OPENBSD;
92 if (bootprt.prt_flag != DOSACTIVE)
93 obsdprt.prt_flag = DOSACTIVE;
94 }
95
96 memset(mbr, 0, sizeof(*mbr));
97 memcpy(mbr->mbr_code, default_dmbr.dmbr_boot, sizeof(mbr->mbr_code));
98 mbr->mbr_prt[0] = bootprt;
99 mbr->mbr_prt[3] = obsdprt;
100 mbr->mbr_signature = DOSMBR_SIGNATURE;
101 }
102
103 void
dos_mbr_to_mbr(const struct dos_mbr * dmbr,const uint64_t lba_self,const uint64_t lba_firstembr,struct mbr * mbr)104 dos_mbr_to_mbr(const struct dos_mbr *dmbr, const uint64_t lba_self,
105 const uint64_t lba_firstembr, struct mbr *mbr)
106 {
107 struct dos_partition dos_parts[NDOSPART];
108 uint8_t *p;
109 unsigned int i;
110
111 p = (uint8_t *)dmbr;
112 mbr->mbr_dmbrzeros = 0;
113 for (i = 0; i < sizeof(struct dos_mbr) && *p == 0; i++, p++)
114 mbr->mbr_dmbrzeros++;
115
116 memcpy(mbr->mbr_code, dmbr->dmbr_boot, sizeof(mbr->mbr_code));
117 mbr->mbr_lba_self = lba_self;
118 mbr->mbr_lba_firstembr = lba_firstembr;
119 mbr->mbr_signature = letoh16(dmbr->dmbr_sign);
120
121 memcpy(dos_parts, dmbr->dmbr_parts, sizeof(dos_parts));
122
123 for (i = 0; i < nitems(mbr->mbr_prt); i++) {
124 memset(&mbr->mbr_prt[i], 0, sizeof(mbr->mbr_prt[i]));
125 if (i < nitems(dmbr->dmbr_parts))
126 PRT_dp_to_prt(&dos_parts[i], lba_self, lba_firstembr,
127 &mbr->mbr_prt[i]);
128 }
129 }
130
131 void
mbr_to_dos_mbr(const struct mbr * mbr,struct dos_mbr * dos_mbr)132 mbr_to_dos_mbr(const struct mbr *mbr, struct dos_mbr *dos_mbr)
133 {
134 struct dos_partition dos_partition;
135 unsigned int i;
136
137 memcpy(dos_mbr->dmbr_boot, mbr->mbr_code, sizeof(dos_mbr->dmbr_boot));
138 dos_mbr->dmbr_sign = htole16(DOSMBR_SIGNATURE);
139
140 for (i = 0; i < nitems(dos_mbr->dmbr_parts); i++) {
141 memset(&dos_partition, 0, sizeof(dos_partition));
142 if (i < nitems(mbr->mbr_prt)) {
143 PRT_prt_to_dp(&mbr->mbr_prt[i], mbr->mbr_lba_self,
144 mbr->mbr_lba_firstembr, &dos_partition);
145 }
146 memcpy(&dos_mbr->dmbr_parts[i], &dos_partition,
147 sizeof(dos_mbr->dmbr_parts[i]));
148 }
149 }
150
151 void
MBR_print(const struct mbr * mbr,const char * units)152 MBR_print(const struct mbr *mbr, const char *units)
153 {
154 unsigned int i;
155
156 DISK_printgeometry("s");
157
158 printf("Offset: %llu\t", mbr->mbr_lba_self);
159 printf("Signature: 0x%X\n", (int)mbr->mbr_signature);
160 PRT_print_parthdr();
161
162 for (i = 0; i < nitems(mbr->mbr_prt); i++)
163 PRT_print_part(i, &mbr->mbr_prt[i], units);
164 }
165
166 int
MBR_read(const uint64_t lba_self,const uint64_t lba_firstembr,struct mbr * mbr)167 MBR_read(const uint64_t lba_self, const uint64_t lba_firstembr, struct mbr *mbr)
168 {
169 struct dos_mbr dos_mbr;
170
171 if (DISK_readbytes(&dos_mbr, lba_self, sizeof(dos_mbr)))
172 return -1;
173
174 dos_mbr_to_mbr(&dos_mbr, lba_self, lba_firstembr, mbr);
175
176 return 0;
177 }
178
179 int
MBR_write(const struct mbr * mbr)180 MBR_write(const struct mbr *mbr)
181 {
182 struct dos_mbr dos_mbr;
183
184 mbr_to_dos_mbr(mbr, &dos_mbr);
185
186 if (DISK_writebytes(&dos_mbr, mbr->mbr_lba_self, sizeof(dos_mbr)))
187 return -1;
188
189 /* Refresh in-kernel disklabel from the updated disk information. */
190 if (ioctl(disk.dk_fd, DIOCRLDINFO, 0) == -1)
191 warn("DIOCRLDINFO");
192
193 return 0;
194 }
195
196 int
MBR_valid_prt(const struct mbr * mbr)197 MBR_valid_prt(const struct mbr *mbr)
198 {
199 uint64_t bs, ns;
200 unsigned int i, nprt;
201 unsigned char id;
202
203 if (mbr->mbr_dmbrzeros == sizeof(struct dos_mbr))
204 return 1; /* All zeros struct dos_mbr is editable. */
205
206 nprt = 0;
207 for (i = 0; i < nitems(mbr->mbr_prt); i++) {
208 bs = mbr->mbr_prt[i].prt_bs;
209 ns = mbr->mbr_prt[i].prt_ns;
210 id = mbr->mbr_prt[i].prt_id;
211 if ((bs == 0 && ns == 0 && id == 0) ||
212 (bs < DL_GETDSIZE(&dl) && ns > 0 && ns <= DL_GETDSIZE(&dl)))
213 nprt++;
214 }
215
216 return nprt > 0 && mbr->mbr_signature == DOSMBR_SIGNATURE;
217 }
218