1 /* 2 * Copyright 2010 Michael Lotz, mmlr@mlotz.ch 3 * All rights reserved. Distributed under the terms of the MIT License. 4 */ 5 6 7 #include <errno.h> 8 #include <fcntl.h> 9 #include <unistd.h> 10 #include <stdint.h> 11 #include <stdio.h> 12 #include <stdlib.h> 13 #include <string.h> 14 #include <sys/stat.h> 15 16 static uint8_t *sCopyBuffer = NULL; 17 static size_t sCopyBufferSize = 2 * 1024 * 1024; 18 19 20 int 21 copyLoop(int from, int to, off_t position) 22 { 23 while (true) { 24 ssize_t copyLength = read(from, sCopyBuffer, sCopyBufferSize); 25 if (copyLength <= 0) 26 return copyLength; 27 28 ssize_t written = pwrite(to, sCopyBuffer, copyLength, position); 29 if (written != copyLength) { 30 if (written < 0) 31 return written; 32 else 33 return -1; 34 } 35 36 position += copyLength; 37 } 38 } 39 40 41 void 42 checkError(bool failed, const char *message) 43 { 44 if (!failed) 45 return; 46 47 printf("%s: %s\n", message, strerror(errno)); 48 free(sCopyBuffer); 49 exit(1); 50 } 51 52 53 void 54 chsAddressFor(uint32_t offset, uint8_t *address, uint32_t sectorsPerTrack, 55 uint32_t headsPerCylinder) 56 { 57 if (offset >= 1024 * sectorsPerTrack * headsPerCylinder) { 58 // not encodable, force LBA 59 address[0] = 0xff; 60 address[1] = 0xff; 61 address[2] = 0xff; 62 return; 63 } 64 65 uint32_t cylinders = 0; 66 uint32_t heads = 0; 67 uint32_t sectors = 0; 68 69 uint32_t temp = 0; 70 while (temp * sectorsPerTrack * headsPerCylinder <= offset) 71 cylinders = temp++; 72 73 offset -= (sectorsPerTrack * headsPerCylinder * cylinders); 74 75 temp = 0; 76 while (temp * sectorsPerTrack <= offset) 77 heads = temp++; 78 79 sectors = offset - (sectorsPerTrack * heads) + 1; 80 81 address[0] = heads; 82 address[1] = sectors; 83 address[1] |= (cylinders >> 2) & 0xc0; 84 address[2] = cylinders; 85 } 86 87 88 int 89 main(int argc, char *argv[]) 90 { 91 if (argc < 5) { 92 printf("usage: %s <outputFile> <isoFile> <mbrFile> <imageFile>\n", 93 argv[0]); 94 return 1; 95 } 96 97 sCopyBuffer = (uint8_t *)malloc(sCopyBufferSize); 98 checkError(sCopyBuffer == NULL, "no memory for copy buffer"); 99 100 static const size_t kBlockSize = 512; 101 102 int outputFile = open(argv[1], O_WRONLY | O_TRUNC | O_CREAT, 103 S_IRUSR | S_IWUSR); 104 checkError(outputFile < 0, "failed to open output file"); 105 106 int isoFile = open(argv[2], O_RDONLY); 107 checkError(isoFile < 0, "failed to open ISO file"); 108 109 struct stat stat; 110 int result = fstat(isoFile, &stat); 111 checkError(result != 0, "failed to stat ISO file"); 112 off_t isoSize = stat.st_size; 113 114 int mbrFile = open(argv[3], O_RDONLY); 115 checkError(mbrFile < 0, "failed to open MBR file"); 116 117 int imageFile = open(argv[4], O_RDONLY); 118 checkError(imageFile < 0, "failed to open image file"); 119 120 result = fstat(imageFile, &stat); 121 checkError(result != 0, "failed to stat image file"); 122 123 off_t imageSize = stat.st_size; 124 125 result = copyLoop(isoFile, outputFile, 0); 126 checkError(result != 0, "failed to copy iso file to output"); 127 128 // isoSize rounded to the next full megabyte 129 off_t alignment = 1 * 1024 * 1024 - 1; 130 off_t imageOffset = (isoSize + alignment) & ~alignment; 131 132 result = copyLoop(imageFile, outputFile, imageOffset); 133 checkError(result != 0, "failed to copy image file to output"); 134 135 result = copyLoop(mbrFile, outputFile, 0); 136 checkError(result != 0, "failed to copy mbr to output"); 137 138 // construct the partition table 139 uint8_t partition[16] = { 140 0x80, // active 141 0xff, 0xff, 0xff, // CHS first block (default to LBA) 142 0xeb, // partition type (BFS) 143 0xff, 0xff, 0xff, // CHS last block (default to LBA) 144 0x00, 0x00, 0x00, 0x00, // imageOffset in blocks (written below) 145 0x00, 0x00, 0x00, 0x00 // imageSize in blocks (written below) 146 }; 147 148 alignment = kBlockSize - 1; 149 imageSize = (imageSize + alignment) & ~alignment; 150 151 // fill in LBA values 152 uint32_t partitionOffset = (uint32_t)(imageOffset / kBlockSize); 153 ((uint32_t *)partition)[2] = partitionOffset; 154 ((uint32_t *)partition)[3] = (uint32_t)(imageSize / kBlockSize); 155 156 #if 0 157 // while this should basically work, it makes the boot code needlessly 158 // use chs which has a high potential of failure due to the geometry 159 // being unknown beforehand (a fixed geometry would be used here). 160 161 uint32_t sectorsPerTrack = 63; 162 uint32_t headsPerCylinder = 255; 163 164 // fill in CHS values 165 chsAddressFor(partitionOffset, &partition[1], sectorsPerTrack, 166 headsPerCylinder); 167 chsAddressFor(partitionOffset + (uint32_t)(imageSize / kBlockSize), 168 &partition[5], sectorsPerTrack, headsPerCylinder); 169 #endif 170 171 ssize_t written = pwrite(outputFile, partition, 16, 512 - 2 - 16 * 4); 172 checkError(written != 16, "failed to write partition entry"); 173 174 // and make the image bootable 175 written = pwrite(outputFile, &partitionOffset, 4, 176 imageOffset + 512 - 2 - 4); 177 checkError(written != 4, "failed to make image bootable"); 178 179 free(sCopyBuffer); 180 return 0; 181 } 182