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