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
print_usage(bool error)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
checkError(bool failed,const char * message)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
createPartition(int handle,int index,bool active,uint8_t type,uint32_t offset,uint32_t size)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
copyLoop(int from,int to,off_t position)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
main(int argc,char * argv[])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