anyboot: Add EFI partition support
* Enables us to add an optional EFI filesystem to the anyboot image. * All existing anyboot behaviour is preserved. * We still need to figure out how to build bios and EFI loaders at the same time on x86. * The tiny "fake ISO" still needs el-torito alt-boot for the EFI loader to work when burned to a CD. This makes the EFI loader work when written to a hard disk / flash drive.
This commit is contained in:
@@ -12,11 +12,11 @@ rule BuildAnybootImage anybootImage : mbrPart : isoPart : imageFile {
|
|||||||
Depends $(anybootImage) : $(mbrPart) ;
|
Depends $(anybootImage) : $(mbrPart) ;
|
||||||
Depends $(anybootImage) : $(imageFile) ;
|
Depends $(anybootImage) : $(imageFile) ;
|
||||||
|
|
||||||
BuildAnybootImage1 $(anybootImage) : $(anyboot) $(isoPart) $(mbrPart) $(imageFile) ;
|
BuildAnybootImage1 $(anybootImage) : $(anyboot) $(mbrPart) $(isoPart) $(imageFile) ;
|
||||||
}
|
}
|
||||||
|
|
||||||
actions BuildAnybootImage1 {
|
actions BuildAnybootImage1 {
|
||||||
$(2[1]) $(1) $(2[2]) $(2[3]) $(2[4])
|
$(2[1]) -b $(2[2]) $(2[3]) $(2[4]) $(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
local baseMBR = base_mbr.bin ;
|
local baseMBR = base_mbr.bin ;
|
||||||
|
|||||||
+200
-82
@@ -1,10 +1,16 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2010 Michael Lotz, [email protected]
|
* Copyright 2010 Michael Lotz, [email protected]
|
||||||
|
* Copyright 2011-2017 Haiku, Inc. All rights reserved.
|
||||||
* All rights reserved. Distributed under the terms of the MIT License.
|
* All rights reserved. Distributed under the terms of the MIT License.
|
||||||
|
*
|
||||||
|
* Authors:
|
||||||
|
* Michael Lotz <[email protected]>
|
||||||
|
* Alexander von Gluck IV <[email protected]>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
#include <getopt.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
@@ -13,32 +19,42 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
#define EXIT_FAILURE 1
|
||||||
|
|
||||||
|
|
||||||
|
//#define DEBUG_ANYBOOT
|
||||||
|
#ifdef DEBUG_ANYBOOT
|
||||||
|
# define TRACE(x...) printf("anyboot: " x)
|
||||||
|
#else
|
||||||
|
# define TRACE(x...) ;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
static uint8_t *sCopyBuffer = NULL;
|
static uint8_t *sCopyBuffer = NULL;
|
||||||
static size_t sCopyBufferSize = 2 * 1024 * 1024;
|
static size_t sCopyBufferSize = 2 * 1024 * 1024;
|
||||||
|
static const size_t kBlockSize = 512;
|
||||||
|
|
||||||
|
|
||||||
int
|
// Haiku Anyboot Image:
|
||||||
copyLoop(int from, int to, off_t position)
|
// (MBR Table + Boot Sector)
|
||||||
|
// ISO (Small Haiku ISO9660)
|
||||||
|
// First Partition (Haiku OS Image, BFS)
|
||||||
|
// Second Partition (EFI Loader, FAT)
|
||||||
|
// Third Partition (EFI Mac, HFS) <not implemented>
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
print_usage(bool error)
|
||||||
{
|
{
|
||||||
while (true) {
|
printf("\n");
|
||||||
ssize_t copyLength = read(from, sCopyBuffer, sCopyBufferSize);
|
printf("usage: anyboot [-b BIOS-Loader] [-e EFI-Filesystem] <isoFile> <imageFile> <outputFile>\n");
|
||||||
if (copyLength <= 0)
|
printf(" -b, --bios-loader <file> Legacy BIOS bootloader\n");
|
||||||
return copyLength;
|
printf(" -e, --efi-filesystem <file> EFI filesystem\n");
|
||||||
|
exit(error ? EXIT_FAILURE : 0);
|
||||||
ssize_t written = pwrite(to, sCopyBuffer, copyLength, position);
|
|
||||||
if (written != copyLength) {
|
|
||||||
if (written < 0)
|
|
||||||
return written;
|
|
||||||
else
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
position += copyLength;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
static void
|
||||||
checkError(bool failed, const char *message)
|
checkError(bool failed, const char *message)
|
||||||
{
|
{
|
||||||
if (!failed)
|
if (!failed)
|
||||||
@@ -50,7 +66,8 @@ checkError(bool failed, const char *message)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
#if 0
|
||||||
|
static void
|
||||||
chsAddressFor(uint32_t offset, uint8_t *address, uint32_t sectorsPerTrack,
|
chsAddressFor(uint32_t offset, uint8_t *address, uint32_t sectorsPerTrack,
|
||||||
uint32_t headsPerCylinder)
|
uint32_t headsPerCylinder)
|
||||||
{
|
{
|
||||||
@@ -83,76 +100,31 @@ chsAddressFor(uint32_t offset, uint8_t *address, uint32_t sectorsPerTrack,
|
|||||||
address[1] |= (cylinders >> 2) & 0xc0;
|
address[1] |= (cylinders >> 2) & 0xc0;
|
||||||
address[2] = cylinders;
|
address[2] = cylinders;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
int
|
static void
|
||||||
main(int argc, char *argv[])
|
createPartition(int handle, int index, bool active, uint8_t type,
|
||||||
|
uint32_t offset, uint32_t size)
|
||||||
{
|
{
|
||||||
if (argc < 5) {
|
uint8_t bootable = active ? 0x80 : 0x0;
|
||||||
printf("usage: %s <outputFile> <isoFile> <mbrFile> <imageFile>\n",
|
|
||||||
argv[0]);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
sCopyBuffer = (uint8_t *)malloc(sCopyBufferSize);
|
|
||||||
checkError(sCopyBuffer == NULL, "no memory for copy buffer");
|
|
||||||
|
|
||||||
static const size_t kBlockSize = 512;
|
|
||||||
|
|
||||||
int outputFile = open(argv[1], O_WRONLY | O_TRUNC | O_CREAT,
|
|
||||||
S_IRUSR | S_IWUSR);
|
|
||||||
checkError(outputFile < 0, "failed to open output file");
|
|
||||||
|
|
||||||
int isoFile = open(argv[2], O_RDONLY);
|
|
||||||
checkError(isoFile < 0, "failed to open ISO file");
|
|
||||||
|
|
||||||
struct stat stat;
|
|
||||||
int result = fstat(isoFile, &stat);
|
|
||||||
checkError(result != 0, "failed to stat ISO file");
|
|
||||||
off_t isoSize = stat.st_size;
|
|
||||||
|
|
||||||
int mbrFile = open(argv[3], O_RDONLY);
|
|
||||||
checkError(mbrFile < 0, "failed to open MBR file");
|
|
||||||
|
|
||||||
int imageFile = open(argv[4], O_RDONLY);
|
|
||||||
checkError(imageFile < 0, "failed to open image file");
|
|
||||||
|
|
||||||
result = fstat(imageFile, &stat);
|
|
||||||
checkError(result != 0, "failed to stat image file");
|
|
||||||
|
|
||||||
off_t imageSize = stat.st_size;
|
|
||||||
|
|
||||||
result = copyLoop(isoFile, outputFile, 0);
|
|
||||||
checkError(result != 0, "failed to copy iso file to output");
|
|
||||||
|
|
||||||
// isoSize rounded to the next full megabyte
|
|
||||||
off_t alignment = 1 * 1024 * 1024 - 1;
|
|
||||||
off_t imageOffset = (isoSize + alignment) & ~alignment;
|
|
||||||
|
|
||||||
result = copyLoop(imageFile, outputFile, imageOffset);
|
|
||||||
checkError(result != 0, "failed to copy image file to output");
|
|
||||||
|
|
||||||
result = copyLoop(mbrFile, outputFile, 0);
|
|
||||||
checkError(result != 0, "failed to copy mbr to output");
|
|
||||||
|
|
||||||
// construct the partition table
|
|
||||||
uint8_t partition[16] = {
|
uint8_t partition[16] = {
|
||||||
0x80, // active
|
bootable, // bootable
|
||||||
0xff, 0xff, 0xff, // CHS first block (default to LBA)
|
0xff, 0xff, 0xff, // CHS first block (default to LBA)
|
||||||
0xeb, // partition type (BFS)
|
type, // partition type
|
||||||
0xff, 0xff, 0xff, // CHS last block (default to LBA)
|
0xff, 0xff, 0xff, // CHS last block (default to LBA)
|
||||||
0x00, 0x00, 0x00, 0x00, // imageOffset in blocks (written below)
|
0x00, 0x00, 0x00, 0x00, // imageOffset in blocks (written below)
|
||||||
0x00, 0x00, 0x00, 0x00 // imageSize in blocks (written below)
|
0x00, 0x00, 0x00, 0x00 // imageSize in blocks (written below)
|
||||||
};
|
};
|
||||||
|
|
||||||
alignment = kBlockSize - 1;
|
|
||||||
imageSize = (imageSize + alignment) & ~alignment;
|
|
||||||
|
|
||||||
// fill in LBA values
|
// fill in LBA values
|
||||||
uint32_t partitionOffset = (uint32_t)(imageOffset / kBlockSize);
|
uint32_t partitionOffset = (uint32_t)(offset / kBlockSize);
|
||||||
((uint32_t *)partition)[2] = partitionOffset;
|
((uint32_t *)partition)[2] = partitionOffset;
|
||||||
((uint32_t *)partition)[3] = (uint32_t)(imageSize / kBlockSize);
|
((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
|
#if 0
|
||||||
// while this should basically work, it makes the boot code needlessly
|
// while this should basically work, it makes the boot code needlessly
|
||||||
// use chs which has a high potential of failure due to the geometry
|
// use chs which has a high potential of failure due to the geometry
|
||||||
@@ -164,17 +136,163 @@ main(int argc, char *argv[])
|
|||||||
// fill in CHS values
|
// fill in CHS values
|
||||||
chsAddressFor(partitionOffset, &partition[1], sectorsPerTrack,
|
chsAddressFor(partitionOffset, &partition[1], sectorsPerTrack,
|
||||||
headsPerCylinder);
|
headsPerCylinder);
|
||||||
chsAddressFor(partitionOffset + (uint32_t)(imageSize / kBlockSize),
|
chsAddressFor(partitionOffset + (uint32_t)(size / kBlockSize),
|
||||||
&partition[5], sectorsPerTrack, headsPerCylinder);
|
&partition[5], sectorsPerTrack, headsPerCylinder);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
ssize_t written = pwrite(outputFile, partition, 16, 512 - 2 - 16 * 4);
|
ssize_t written = pwrite(handle, partition, 16, 512 - 2 - 16 * (4 - index));
|
||||||
checkError(written != 16, "failed to write partition entry");
|
checkError(written != 16, "failed to write partition entry");
|
||||||
|
|
||||||
// and make the image bootable
|
if (active) {
|
||||||
written = pwrite(outputFile, &partitionOffset, 4,
|
// make it bootable
|
||||||
imageOffset + 512 - 2 - 4);
|
written = pwrite(handle, &partitionOffset, 4, offset + 512 - 2 - 4);
|
||||||
checkError(written != 4, "failed to make image bootable");
|
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; /* don't print errors */
|
||||||
|
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");
|
||||||
|
|
||||||
|
// isoSize rounded to the next full megabyte
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Haiku Image Partition
|
||||||
|
alignment = kBlockSize - 1;
|
||||||
|
imageSize = (imageSize + alignment) & ~alignment;
|
||||||
|
createPartition(outputFileHandle, 0, true, 0xeb, imageOffset, imageSize);
|
||||||
|
|
||||||
|
// Optional EFI Filesystem
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: MacEFI (HFS) maybe someday
|
||||||
|
|
||||||
free(sCopyBuffer);
|
free(sCopyBuffer);
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
Reference in New Issue
Block a user