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