* Copyright 2010 Michael Lotz, mmlr@mlotz.ch
* Copyright 2011-2017 Haiku, Inc. All rights reserved.
* All rights reserved. Distributed under the terms of the MIT License.
*
* Authors:
* Michael Lotz <mmlr@mlotz.ch>
* Alexander von Gluck IV <kallisti5@unixzen.com>
*/
#include <errno.h>
#include <getopt.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#define EXIT_FAILURE 1
#ifdef DEBUG_ANYBOOT
# define TRACE(x...) printf("anyboot: " x)
#else
# define TRACE(x...) ;
#endif
static uint8_t *sCopyBuffer = NULL;
static size_t sCopyBufferSize = 2 * 1024 * 1024;
static const size_t kBlockSize = 512;
static void
print_usage(bool error)
{
printf("\n");
printf("usage: anyboot [-b BIOS-Loader] [-e EFI-Filesystem] <isoFile> <imageFile> <outputFile>\n");
printf(" -b, --bios-loader <file> Legacy BIOS bootloader\n");
printf(" -e, --efi-filesystem <file> EFI filesystem\n");
exit(error ? EXIT_FAILURE : 0);
}
static void
checkError(bool failed, const char *message)
{
if (!failed)
return;
printf("%s: %s\n", message, strerror(errno));
free(sCopyBuffer);
exit(1);
}
#if 0
static void
chsAddressFor(uint32_t offset, uint8_t *address, uint32_t sectorsPerTrack,
uint32_t headsPerCylinder)
{
if (offset >= 1024 * sectorsPerTrack * headsPerCylinder) {
address[0] = 0xff;
address[1] = 0xff;
address[2] = 0xff;
return;
}
uint32_t cylinders = 0;
uint32_t heads = 0;
uint32_t sectors = 0;
uint32_t temp = 0;
while (temp * sectorsPerTrack * headsPerCylinder <= offset)
cylinders = temp++;
offset -= (sectorsPerTrack * headsPerCylinder * cylinders);
temp = 0;
while (temp * sectorsPerTrack <= offset)
heads = temp++;
sectors = offset - (sectorsPerTrack * heads) + 1;
address[0] = heads;
address[1] = sectors;
address[1] |= (cylinders >> 2) & 0xc0;
address[2] = cylinders;
}
#endif
static void
createPartition(int handle, int index, bool active, uint8_t type,
uint32_t offset, uint32_t size)
{
uint8_t bootable = active ? 0x80 : 0x0;
uint8_t partition[16] = {
bootable,
0xff, 0xff, 0xff,
type,
0xff, 0xff, 0xff,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00
};
uint32_t partitionOffset = (uint32_t)(offset / kBlockSize);
((uint32_t *)partition)[2] = partitionOffset;
((uint32_t *)partition)[3] = (uint32_t)(size / kBlockSize);
TRACE("%s: #%d %c bytes: %u-%u, sectors: %u-%u\n", __func__, index,
active ? 'b' : '-', offset, offset + size, partitionOffset,
partitionOffset + uint32_t(size / kBlockSize));
#if 0
uint32_t sectorsPerTrack = 63;
uint32_t headsPerCylinder = 255;
chsAddressFor(partitionOffset, &partition[1], sectorsPerTrack,
headsPerCylinder);
chsAddressFor(partitionOffset + (uint32_t)(size / kBlockSize),
&partition[5], sectorsPerTrack, headsPerCylinder);
#endif
ssize_t written = pwrite(handle, partition, 16, 512 - 2 - 16 * (4 - index));
checkError(written != 16, "failed to write partition entry");
if (active) {
written = pwrite(handle, &partitionOffset, 4, offset + 512 - 2 - 4);
checkError(written != 4, "failed to make image bootable");
}
return;
}
static int
copyLoop(int from, int to, off_t position)
{
while (true) {
ssize_t copyLength = read(from, sCopyBuffer, sCopyBufferSize);
if (copyLength <= 0)
return copyLength;
ssize_t written = pwrite(to, sCopyBuffer, copyLength, position);
if (written != copyLength) {
if (written < 0)
return written;
else
return -1;
}
position += copyLength;
}
}
int
main(int argc, char *argv[])
{
const char *biosFile = NULL;
const char *efiFile = NULL;
const char *isoFile = NULL;
const char *imageFile = NULL;
const char *outputFile = NULL;
while (1) {
int c;
static struct option long_options[] = {
{"bios-loader", required_argument, 0, 'b'},
{"efi-loader", required_argument, 0, 'e'},
{"help", no_argument, 0, 'h'},
{0, 0, 0, 0}
};
opterr = 1;
c = getopt_long(argc, argv, "+hb:e:", long_options, NULL);
if (c == -1)
break;
switch (c) {
case 'h':
print_usage(false);
break;
case 'b':
biosFile = optarg;
break;
case 'e':
efiFile = optarg;
break;
default:
print_usage(true);
}
}
if ((argc - optind) != 3)
print_usage(true);
for (int index = optind; index < argc; index++) {
if (isoFile == NULL)
isoFile = argv[index];
else if (imageFile == NULL)
imageFile = argv[index];
else if (outputFile == NULL)
outputFile = argv[index];
}
sCopyBuffer = (uint8_t *)malloc(sCopyBufferSize);
checkError(sCopyBuffer == NULL, "no memory for copy buffer");
int outputFileHandle = open(outputFile, O_WRONLY | O_TRUNC | O_CREAT,
S_IRUSR | S_IWUSR);
checkError(outputFileHandle < 0, "failed to open output file");
int isoFileHandle = open(isoFile, O_RDONLY);
checkError(isoFileHandle < 0, "failed to open ISO file");
struct stat stat;
int result = fstat(isoFileHandle, &stat);
checkError(result != 0, "failed to stat ISO file");
off_t isoSize = stat.st_size;
int biosFileHandle = -1;
if (biosFile != NULL) {
biosFileHandle = open(biosFile, O_RDONLY);
checkError(biosFileHandle < 0, "failed to open BIOS bootloader file");
}
int efiFileHandle = -1;
off_t efiSize = 0;
if (efiFile != NULL) {
efiFileHandle = open(efiFile, O_RDONLY);
checkError(efiFileHandle < 0, "failed to open EFI bootloader file");
result = fstat(efiFileHandle, &stat);
checkError(result != 0, "failed to stat EFI filesystem image");
efiSize = stat.st_size;
}
int imageFileHandle = open(imageFile, O_RDONLY);
checkError(imageFileHandle < 0, "failed to open image file");
result = fstat(imageFileHandle, &stat);
checkError(result != 0, "failed to stat image file");
off_t imageSize = stat.st_size;
result = copyLoop(isoFileHandle, outputFileHandle, 0);
checkError(result != 0, "failed to copy iso file to output");
off_t alignment = 1 * 1024 * 1024 - 1;
off_t imageOffset = (isoSize + alignment) & ~alignment;
result = copyLoop(imageFileHandle, outputFileHandle, imageOffset);
checkError(result != 0, "failed to copy image file to output");
if (biosFileHandle >= 0) {
result = copyLoop(biosFileHandle, outputFileHandle, 0);
checkError(result != 0, "failed to copy BIOS bootloader to output");
}
alignment = kBlockSize - 1;
imageSize = (imageSize + alignment) & ~alignment;
createPartition(outputFileHandle, 0, true, 0xeb, imageOffset, imageSize);
if (efiFile != NULL) {
off_t efiOffset = (imageOffset + imageSize + alignment) & ~alignment;
efiSize = (efiSize + alignment) & ~alignment;
result = copyLoop(efiFileHandle, outputFileHandle, efiOffset);
checkError(result != 0, "failed to copy EFI filesystem image to output");
createPartition(outputFileHandle, 1, false, 0xef, efiOffset, efiSize);
}
free(sCopyBuffer);
return 0;
}