1 /* $OpenBSD: mbr.c,v 1.67 2016/09/01 16:17:46 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 <stdint.h> 25 #include <stdio.h> 26 #include <stdlib.h> 27 #include <string.h> 28 29 #include "disk.h" 30 #include "part.h" 31 #include "misc.h" 32 #include "mbr.h" 33 #include "gpt.h" 34 35 struct mbr initial_mbr; 36 37 static int gpt_chk_mbr(struct dos_partition *, u_int64_t); 38 39 int 40 MBR_protective_mbr(struct mbr *mbr) 41 { 42 struct dos_partition dp[NDOSPART], dos_partition; 43 int i; 44 45 for (i = 0; i < NDOSPART; i++) { 46 PRT_make(&mbr->part[i], mbr->offset, mbr->reloffset, 47 &dos_partition); 48 memcpy(&dp[i], &dos_partition, sizeof(dp[i])); 49 } 50 51 return (gpt_chk_mbr(dp, DL_GETDSIZE(&dl))); 52 } 53 54 void 55 MBR_init_GPT(struct mbr *mbr) 56 { 57 memset(&mbr->part, 0, sizeof(mbr->part)); 58 59 /* Use whole disk, starting after MBR. 60 * 61 * Always set the partition size to UINT32_MAX (as MS does). EFI 62 * firmware has been encountered that lies in unpredictable ways 63 * about the size of the disk, thus making it impossible to boot 64 * such devices. 65 */ 66 mbr->part[0].id = DOSPTYP_EFI; 67 mbr->part[0].bs = 1; 68 mbr->part[0].ns = UINT32_MAX; 69 70 /* Fix up start/length fields. */ 71 PRT_fix_CHS(&mbr->part[0]); 72 } 73 74 void 75 MBR_init(struct mbr *mbr) 76 { 77 extern u_int32_t b_arg; 78 u_int64_t adj; 79 daddr_t i; 80 81 /* 82 * XXX Do *NOT* zap all MBR parts! Some archs still read initmbr 83 * from disk!! Just mark them inactive until -b goodness spreads 84 * further. 85 */ 86 mbr->part[0].flag = 0; 87 mbr->part[1].flag = 0; 88 mbr->part[2].flag = 0; 89 90 memset(&gh, 0, sizeof(gh)); 91 memset(&gp, 0, sizeof(gp)); 92 93 mbr->part[3].flag = DOSACTIVE; 94 mbr->signature = DOSMBR_SIGNATURE; 95 96 /* Use whole disk. Reserve first track, or first cyl, if possible. */ 97 mbr->part[3].id = DOSPTYP_OPENBSD; 98 if (disk.heads > 1) 99 mbr->part[3].shead = 1; 100 else 101 mbr->part[3].shead = 0; 102 if (disk.heads < 2 && disk.cylinders > 1) 103 mbr->part[3].scyl = 1; 104 else 105 mbr->part[3].scyl = 0; 106 mbr->part[3].ssect = 1; 107 108 /* Go right to the end */ 109 mbr->part[3].ecyl = disk.cylinders - 1; 110 mbr->part[3].ehead = disk.heads - 1; 111 mbr->part[3].esect = disk.sectors; 112 113 /* Fix up start/length fields */ 114 PRT_fix_BN(&mbr->part[3], 3); 115 116 #if defined(__powerpc__) || defined(__mips__) 117 /* Now fix up for the MS-DOS boot partition on PowerPC. */ 118 mbr->part[0].flag = DOSACTIVE; /* Boot from dos part */ 119 mbr->part[3].flag = 0; 120 mbr->part[3].ns += mbr->part[3].bs; 121 mbr->part[3].bs = mbr->part[0].bs + mbr->part[0].ns; 122 mbr->part[3].ns -= mbr->part[3].bs; 123 PRT_fix_CHS(&mbr->part[3]); 124 if ((mbr->part[3].shead != 1) || (mbr->part[3].ssect != 1)) { 125 /* align the partition on a cylinder boundary */ 126 mbr->part[3].shead = 0; 127 mbr->part[3].ssect = 1; 128 mbr->part[3].scyl += 1; 129 } 130 /* Fix up start/length fields */ 131 PRT_fix_BN(&mbr->part[3], 3); 132 #endif 133 #if defined(__i386__) || defined(__amd64__) 134 if (b_arg > 0) { 135 /* Add an EFI system partition on i386/amd64. */ 136 mbr->part[0].id = DOSPTYP_EFISYS; 137 mbr->part[0].bs = 64; 138 mbr->part[0].ns = b_arg; 139 PRT_fix_CHS(&mbr->part[0]); 140 mbr->part[3].ns += mbr->part[3].bs; 141 mbr->part[3].bs = mbr->part[0].bs + mbr->part[0].ns; 142 mbr->part[3].ns -= mbr->part[3].bs; 143 PRT_fix_CHS(&mbr->part[3]); 144 } 145 #endif 146 147 /* Start OpenBSD MBR partition on a power of 2 block number. */ 148 i = 1; 149 while (i < DL_SECTOBLK(&dl, mbr->part[3].bs)) 150 i *= 2; 151 adj = DL_BLKTOSEC(&dl, i) - mbr->part[3].bs; 152 mbr->part[3].bs += adj; 153 mbr->part[3].ns -= adj; 154 PRT_fix_CHS(&mbr->part[3]); 155 } 156 157 void 158 MBR_parse(struct dos_mbr *dos_mbr, off_t offset, off_t reloff, struct mbr *mbr) 159 { 160 struct dos_partition dos_parts[NDOSPART]; 161 int i; 162 163 memcpy(mbr->code, dos_mbr->dmbr_boot, sizeof(mbr->code)); 164 mbr->offset = offset; 165 mbr->reloffset = reloff; 166 mbr->signature = letoh16(dos_mbr->dmbr_sign); 167 168 memcpy(dos_parts, dos_mbr->dmbr_parts, sizeof(dos_parts)); 169 170 for (i = 0; i < NDOSPART; i++) 171 PRT_parse(&dos_parts[i], offset, reloff, &mbr->part[i]); 172 } 173 174 void 175 MBR_make(struct mbr *mbr, struct dos_mbr *dos_mbr) 176 { 177 struct dos_partition dos_partition; 178 int i; 179 180 memcpy(dos_mbr->dmbr_boot, mbr->code, sizeof(dos_mbr->dmbr_boot)); 181 dos_mbr->dmbr_sign = htole16(DOSMBR_SIGNATURE); 182 183 for (i = 0; i < NDOSPART; i++) { 184 PRT_make(&mbr->part[i], mbr->offset, mbr->reloffset, 185 &dos_partition); 186 memcpy(&dos_mbr->dmbr_parts[i], &dos_partition, 187 sizeof(dos_mbr->dmbr_parts[i])); 188 } 189 } 190 191 void 192 MBR_print(struct mbr *mbr, char *units) 193 { 194 int i; 195 196 DISK_printgeometry(NULL); 197 198 /* Header */ 199 printf("Offset: %lld\t", (long long)mbr->offset); 200 printf("Signature: 0x%X\n", (int)mbr->signature); 201 PRT_print(0, NULL, units); 202 203 /* Entries */ 204 for (i = 0; i < NDOSPART; i++) 205 PRT_print(i, &mbr->part[i], units); 206 } 207 208 int 209 MBR_read(off_t where, struct dos_mbr *dos_mbr) 210 { 211 char *secbuf; 212 213 secbuf = DISK_readsector(where); 214 if (secbuf == NULL) 215 return (-1); 216 217 memcpy(dos_mbr, secbuf, sizeof(*dos_mbr)); 218 free(secbuf); 219 220 return (0); 221 } 222 223 int 224 MBR_write(off_t where, struct dos_mbr *dos_mbr) 225 { 226 char *secbuf; 227 228 secbuf = DISK_readsector(where); 229 if (secbuf == NULL) 230 return (-1); 231 232 /* 233 * Place the new MBR at the start of the sector and 234 * write the sector back to "disk". 235 */ 236 memcpy(secbuf, dos_mbr, sizeof(*dos_mbr)); 237 DISK_writesector(secbuf, where); 238 239 /* Refresh in-kernel disklabel from the updated disk information. */ 240 ioctl(disk.fd, DIOCRLDINFO, 0); 241 242 free(secbuf); 243 244 return (0); 245 } 246 247 /* 248 * If *dos_mbr has a 0xee or 0xef partition, nothing needs to happen. If no 249 * such partition is present but the first or last sector on the disk has a 250 * GPT, zero the GPT to ensure the MBR takes priority and fewer BIOSes get 251 * confused. 252 */ 253 void 254 MBR_zapgpt(struct dos_mbr *dos_mbr, uint64_t lastsec) 255 { 256 struct dos_partition dos_parts[NDOSPART]; 257 char *secbuf; 258 uint64_t sig; 259 int i; 260 261 memcpy(dos_parts, dos_mbr->dmbr_parts, sizeof(dos_parts)); 262 263 for (i = 0; i < NDOSPART; i++) 264 if ((dos_parts[i].dp_typ == DOSPTYP_EFI) || 265 (dos_parts[i].dp_typ == DOSPTYP_EFISYS)) 266 return; 267 268 secbuf = DISK_readsector(GPTSECTOR); 269 if (secbuf == NULL) 270 return; 271 272 memcpy(&sig, secbuf, sizeof(sig)); 273 if (letoh64(sig) == GPTSIGNATURE) { 274 memset(secbuf, 0, sizeof(sig)); 275 DISK_writesector(secbuf, GPTSECTOR); 276 } 277 free(secbuf); 278 279 secbuf = DISK_readsector(lastsec); 280 if (secbuf == NULL) 281 return; 282 283 memcpy(&sig, secbuf, sizeof(sig)); 284 if (letoh64(sig) == GPTSIGNATURE) { 285 memset(secbuf, 0, sizeof(sig)); 286 DISK_writesector(secbuf, lastsec); 287 } 288 free(secbuf); 289 } 290 291 /* 292 * Returns 0 if the MBR with the provided partition array is a GPT protective 293 * MBR, and returns 1 otherwise. A GPT protective MBR would have one and only 294 * one MBR partition, an EFI partition that either covers the whole disk or as 295 * much of it as is possible with a 32bit size field. 296 * 297 * Taken from kern/subr_disk.c. 298 * 299 * NOTE: MS always uses a size of UINT32_MAX for the EFI partition!** 300 */ 301 int 302 gpt_chk_mbr(struct dos_partition *dp, u_int64_t dsize) 303 { 304 struct dos_partition *dp2; 305 int efi, found, i; 306 u_int32_t psize; 307 308 found = efi = 0; 309 for (dp2=dp, i=0; i < NDOSPART; i++, dp2++) { 310 if (dp2->dp_typ == DOSPTYP_UNUSED) 311 continue; 312 found++; 313 if (dp2->dp_typ != DOSPTYP_EFI) 314 continue; 315 psize = letoh32(dp2->dp_size); 316 if (psize == (dsize - 1) || 317 psize == UINT32_MAX) { 318 if (letoh32(dp2->dp_start) == 1) 319 efi++; 320 } 321 } 322 if (found == 1 && efi == 1) 323 return (0); 324 325 return (1); 326 } 327 328