1 /* 2 * Copyright 2010 Michael Lotz, mmlr@mlotz.ch 3 * Copyright 2011-2017 Haiku, Inc. All rights reserved. 4 * All rights reserved. Distributed under the terms of the MIT License. 5 * 6 * Authors: 7 * Michael Lotz <mmlr@mlotz.ch> 8 * Alexander von Gluck IV <kallisti5@unixzen.com> 9 */ 10 11 12 #include <errno.h> 13 #include <getopt.h> 14 #include <fcntl.h> 15 #include <unistd.h> 16 #include <stdint.h> 17 #include <stdio.h> 18 #include <stdlib.h> 19 #include <string.h> 20 #include <sys/stat.h> 21 22 #define EXIT_FAILURE 1 23 24 25 //#define DEBUG_ANYBOOT 26 #ifdef DEBUG_ANYBOOT 27 # define TRACE(x...) printf("anyboot: " x) 28 #else 29 # define TRACE(x...) ; 30 #endif 31 32 33 static uint8_t *sCopyBuffer = NULL; 34 static size_t sCopyBufferSize = 2 * 1024 * 1024; 35 static const size_t kBlockSize = 512; 36 37 38 // Haiku Anyboot Image: 39 // (MBR Table + Boot Sector) 40 // ISO (Small Haiku ISO9660) 41 // First Partition (Haiku OS Image, BFS) 42 // Second Partition (EFI Loader, FAT) 43 // Third Partition (EFI Mac, HFS) <not implemented> 44 45 46 static void 47 print_usage(bool error) 48 { 49 printf("\n"); 50 printf("usage: anyboot [-b BIOS-Loader] [-e EFI-Filesystem] <isoFile> <imageFile> <outputFile>\n"); 51 printf(" -b, --bios-loader <file> Legacy BIOS bootloader\n"); 52 printf(" -e, --efi-filesystem <file> EFI filesystem\n"); 53 exit(error ? EXIT_FAILURE : 0); 54 } 55 56 57 static void 58 checkError(bool failed, const char *message) 59 { 60 if (!failed) 61 return; 62 63 printf("%s: %s\n", message, strerror(errno)); 64 free(sCopyBuffer); 65 exit(1); 66 } 67 68 69 #if 0 70 static void 71 chsAddressFor(uint32_t offset, uint8_t *address, uint32_t sectorsPerTrack, 72 uint32_t headsPerCylinder) 73 { 74 if (offset >= 1024 * sectorsPerTrack * headsPerCylinder) { 75 // not encodable, force LBA 76 address[0] = 0xff; 77 address[1] = 0xff; 78 address[2] = 0xff; 79 return; 80 } 81 82 uint32_t cylinders = 0; 83 uint32_t heads = 0; 84 uint32_t sectors = 0; 85 86 uint32_t temp = 0; 87 while (temp * sectorsPerTrack * headsPerCylinder <= offset) 88 cylinders = temp++; 89 90 offset -= (sectorsPerTrack * headsPerCylinder * cylinders); 91 92 temp = 0; 93 while (temp * sectorsPerTrack <= offset) 94 heads = temp++; 95 96 sectors = offset - (sectorsPerTrack * heads) + 1; 97 98 address[0] = heads; 99 address[1] = sectors; 100 address[1] |= (cylinders >> 2) & 0xc0; 101 address[2] = cylinders; 102 } 103 #endif 104 105 106 static void 107 createPartition(int handle, int index, bool active, uint8_t type, 108 uint32_t offset, uint32_t size) 109 { 110 uint8_t bootable = active ? 0x80 : 0x0; 111 uint8_t partition[16] = { 112 bootable, // bootable 113 0xff, 0xff, 0xff, // CHS first block (default to LBA) 114 type, // partition type 115 0xff, 0xff, 0xff, // CHS last block (default to LBA) 116 0x00, 0x00, 0x00, 0x00, // imageOffset in blocks (written below) 117 0x00, 0x00, 0x00, 0x00 // imageSize in blocks (written below) 118 }; 119 120 // fill in LBA values 121 uint32_t partitionOffset = (uint32_t)(offset / kBlockSize); 122 ((uint32_t *)partition)[2] = partitionOffset; 123 ((uint32_t *)partition)[3] = (uint32_t)(size / kBlockSize); 124 125 TRACE("%s: #%d %c bytes: %u-%u, sectors: %u-%u\n", __func__, index, 126 active ? 'b' : '-', offset, offset + size, partitionOffset, 127 partitionOffset + uint32_t(size / kBlockSize)); 128 #if 0 129 // while this should basically work, it makes the boot code needlessly 130 // use chs which has a high potential of failure due to the geometry 131 // being unknown beforehand (a fixed geometry would be used here). 132 133 uint32_t sectorsPerTrack = 63; 134 uint32_t headsPerCylinder = 255; 135 136 // fill in CHS values 137 chsAddressFor(partitionOffset, &partition[1], sectorsPerTrack, 138 headsPerCylinder); 139 chsAddressFor(partitionOffset + (uint32_t)(size / kBlockSize), 140 &partition[5], sectorsPerTrack, headsPerCylinder); 141 #endif 142 143 ssize_t written = pwrite(handle, partition, 16, 512 - 2 - 16 * (4 - index)); 144 checkError(written != 16, "failed to write partition entry"); 145 146 if (active) { 147 // make it bootable 148 written = pwrite(handle, &partitionOffset, 4, offset + 512 - 2 - 4); 149 checkError(written != 4, "failed to make image bootable"); 150 } 151 return; 152 } 153 154 155 static int 156 copyLoop(int from, int to, off_t position) 157 { 158 while (true) { 159 ssize_t copyLength = read(from, sCopyBuffer, sCopyBufferSize); 160 if (copyLength <= 0) 161 return copyLength; 162 163 ssize_t written = pwrite(to, sCopyBuffer, copyLength, position); 164 if (written != copyLength) { 165 if (written < 0) 166 return written; 167 else 168 return -1; 169 } 170 171 position += copyLength; 172 } 173 } 174 175 176 int 177 main(int argc, char *argv[]) 178 { 179 const char *biosFile = NULL; 180 const char *efiFile = NULL; 181 const char *isoFile = NULL; 182 const char *imageFile = NULL; 183 const char *outputFile = NULL; 184 185 while (1) { 186 int c; 187 static struct option long_options[] = { 188 {"bios-loader", required_argument, 0, 'b'}, 189 {"efi-loader", required_argument, 0, 'e'}, 190 {"help", no_argument, 0, 'h'}, 191 {0, 0, 0, 0} 192 }; 193 194 opterr = 1; /* don't print errors */ 195 c = getopt_long(argc, argv, "+hb:e:", long_options, NULL); 196 197 if (c == -1) 198 break; 199 200 switch (c) { 201 case 'h': 202 print_usage(false); 203 break; 204 case 'b': 205 biosFile = optarg; 206 break; 207 case 'e': 208 efiFile = optarg; 209 break; 210 default: 211 print_usage(true); 212 } 213 } 214 215 if ((argc - optind) != 3) 216 print_usage(true); 217 218 for (int index = optind; index < argc; index++) { 219 if (isoFile == NULL) 220 isoFile = argv[index]; 221 else if (imageFile == NULL) 222 imageFile = argv[index]; 223 else if (outputFile == NULL) 224 outputFile = argv[index]; 225 } 226 227 sCopyBuffer = (uint8_t *)malloc(sCopyBufferSize); 228 checkError(sCopyBuffer == NULL, "no memory for copy buffer"); 229 230 int outputFileHandle = open(outputFile, O_WRONLY | O_TRUNC | O_CREAT, 231 S_IRUSR | S_IWUSR); 232 checkError(outputFileHandle < 0, "failed to open output file"); 233 234 int isoFileHandle = open(isoFile, O_RDONLY); 235 checkError(isoFileHandle < 0, "failed to open ISO file"); 236 237 struct stat stat; 238 int result = fstat(isoFileHandle, &stat); 239 checkError(result != 0, "failed to stat ISO file"); 240 off_t isoSize = stat.st_size; 241 242 int biosFileHandle = -1; 243 if (biosFile != NULL) { 244 biosFileHandle = open(biosFile, O_RDONLY); 245 checkError(biosFileHandle < 0, "failed to open BIOS bootloader file"); 246 } 247 248 int efiFileHandle = -1; 249 off_t efiSize = 0; 250 if (efiFile != NULL) { 251 efiFileHandle = open(efiFile, O_RDONLY); 252 checkError(efiFileHandle < 0, "failed to open EFI bootloader file"); 253 254 result = fstat(efiFileHandle, &stat); 255 checkError(result != 0, "failed to stat EFI filesystem image"); 256 efiSize = stat.st_size; 257 } 258 259 int imageFileHandle = open(imageFile, O_RDONLY); 260 checkError(imageFileHandle < 0, "failed to open image file"); 261 262 result = fstat(imageFileHandle, &stat); 263 checkError(result != 0, "failed to stat image file"); 264 off_t imageSize = stat.st_size; 265 266 result = copyLoop(isoFileHandle, outputFileHandle, 0); 267 checkError(result != 0, "failed to copy iso file to output"); 268 269 // isoSize rounded to the next full megabyte 270 off_t alignment = 1 * 1024 * 1024 - 1; 271 off_t imageOffset = (isoSize + alignment) & ~alignment; 272 273 result = copyLoop(imageFileHandle, outputFileHandle, imageOffset); 274 checkError(result != 0, "failed to copy image file to output"); 275 276 if (biosFileHandle >= 0) { 277 result = copyLoop(biosFileHandle, outputFileHandle, 0); 278 checkError(result != 0, "failed to copy BIOS bootloader to output"); 279 } 280 281 // Haiku Image Partition 282 alignment = kBlockSize - 1; 283 imageSize = (imageSize + alignment) & ~alignment; 284 createPartition(outputFileHandle, 0, true, 0xeb, imageOffset, imageSize); 285 286 // Optional EFI Filesystem 287 if (efiFile != NULL) { 288 off_t efiOffset = (imageOffset + imageSize + alignment) & ~alignment; 289 efiSize = (efiSize + alignment) & ~alignment; 290 result = copyLoop(efiFileHandle, outputFileHandle, efiOffset); 291 checkError(result != 0, "failed to copy EFI filesystem image to output"); 292 createPartition(outputFileHandle, 1, false, 0xef, efiOffset, efiSize); 293 } 294 295 // TODO: MacEFI (HFS) maybe someday 296 297 free(sCopyBuffer); 298 return 0; 299 } 300