xref: /haiku/src/tools/anyboot/anyboot.cpp (revision 7457ccb4b2f4786525d3b7bda42598487d57ab7d)
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