Skip to content

Commit 4a9b8fe

Browse files
committed
host/limine: Perform automatic conversion of GPT to MBR for ISOHYBRIDs if possible
1 parent 9f937d9 commit 4a9b8fe

File tree

1 file changed

+153
-0
lines changed

1 file changed

+153
-0
lines changed

host/limine.c

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <errno.h>
1414
#include <inttypes.h>
1515
#include <limits.h>
16+
#include <time.h>
1617

1718
#ifndef LIMINE_NO_BIOS
1819
#include "limine-bios-hdd.h"
@@ -72,6 +73,7 @@ static int set_pos(FILE *stream, uint64_t pos) {
7273
return 0;
7374
}
7475

76+
#define SIZEOF_ARRAY(array) (sizeof(array) / sizeof(array[0]))
7577
#define DIV_ROUNDUP(a, b) (((a) + ((b) - 1)) / (b))
7678

7779
struct gpt_table_header {
@@ -158,6 +160,31 @@ static const uint32_t crc32_table[] = {
158160
0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
159161
};
160162

163+
struct gpt2mbr_type_conv {
164+
uint64_t gpt_type1;
165+
uint64_t gpt_type2;
166+
uint8_t mbr_type;
167+
};
168+
169+
// This table is very incomplete, but it should be enough for covering
170+
// all that matters for ISOHYBRIDs.
171+
// Of course, though, expansion is welcome.
172+
static struct gpt2mbr_type_conv gpt2mbr_type_conv_table[] = {
173+
{ 0x11d2f81fc12a7328, 0x3bc93ec9a0004bba, 0xef }, // EFI system partition
174+
{ 0x4433b9e5ebd0a0a2, 0xc79926b7b668c087, 0x07 }, // Microsoft basic data
175+
{ 0x11aa000048465300, 0xacec4365300011aa, 0xaf }, // HFS/HFS+
176+
};
177+
178+
static int gpt2mbr_type(uint64_t gpt_type1, uint64_t gpt_type2) {
179+
for (size_t i = 0; i < SIZEOF_ARRAY(gpt2mbr_type_conv_table); i++) {
180+
if (gpt2mbr_type_conv_table[i].gpt_type1 == gpt_type1
181+
&& gpt2mbr_type_conv_table[i].gpt_type2 == gpt_type2) {
182+
return gpt2mbr_type_conv_table[i].mbr_type;
183+
}
184+
}
185+
return -1;
186+
}
187+
161188
static uint32_t crc32(void *_stream, size_t len) {
162189
uint8_t *stream = _stream;
163190
uint32_t ret = 0xffffffff;
@@ -572,6 +599,11 @@ static void bios_install_usage(void) {
572599
printf(" Set the input (for --uninstall) or output file\n");
573600
printf(" name of the file which contains uninstall data\n");
574601
printf("\n");
602+
printf(" --no-gpt-to-mbr-isohybrid-conversion\n");
603+
printf(" Do not automatically convert a GPT partition table found on\n");
604+
printf(" an ISOHYBRID image into an MBR partition table (which is\n");
605+
printf(" done for better hardware compatibility)\n");
606+
printf("\n");
575607
printf(" --quiet Do not print verbose diagnostic messages\n");
576608
printf("\n");
577609
printf(" --help | -h Display this help message\n");
@@ -581,6 +613,7 @@ static void bios_install_usage(void) {
581613
static int bios_install(int argc, char *argv[]) {
582614
int ok = EXIT_FAILURE;
583615
int force_mbr = 0;
616+
bool gpt2mbr_allowed = true;
584617
bool uninstall_mode = false;
585618
const uint8_t *bootloader_img = binary_limine_hdd_bin_data;
586619
size_t bootloader_file_size = sizeof(binary_limine_hdd_bin_data);
@@ -612,6 +645,8 @@ static int bios_install(int argc, char *argv[]) {
612645
fprintf(stderr, "%s: warning: --force-mbr already set.\n", program_name);
613646
}
614647
force_mbr = 1;
648+
} else if (strcmp(argv[i], "--no-gpt-to-mbr-isohybrid-conversion") == 0) {
649+
gpt2mbr_allowed = false;
615650
} else if (strcmp(argv[i], "--uninstall") == 0) {
616651
if (uninstall_mode && !quiet) {
617652
fprintf(stderr, "%s: warning: --uninstall already set.\n", program_name);
@@ -705,6 +740,124 @@ static int bios_install(int argc, char *argv[]) {
705740
}
706741
}
707742

743+
// Check if this is an ISO w/ a GPT, in which case try converting it
744+
// to MBR for improved compatibility with a whole range of hardware that
745+
// does not like booting off of GPT in BIOS or CSM mode, and other
746+
// broken hardware.
747+
if (gpt && gpt2mbr_allowed == true) {
748+
char iso_signature[5];
749+
device_read(iso_signature, 32769, 5);
750+
751+
if (strncmp(iso_signature, "CD001", 5) != 0) {
752+
goto no_mbr_conv;
753+
}
754+
755+
if (!quiet) {
756+
fprintf(stderr, "Detected ISOHYBRID with a GPT partition table.\n");
757+
fprintf(stderr, "Converting to MBR for improved compatibility...\n");
758+
}
759+
760+
// Gather the (up to 4) GPT partition to convert.
761+
struct {
762+
uint64_t lba_start;
763+
uint64_t lba_end;
764+
uint8_t type;
765+
} part_to_conv[4];
766+
size_t part_to_conv_i = 0;
767+
768+
for (int64_t i = 0; i < (int64_t)ENDSWAP(gpt_header.number_of_partition_entries); i++) {
769+
struct gpt_entry gpt_entry;
770+
device_read(&gpt_entry,
771+
(ENDSWAP(gpt_header.partition_entry_lba) * lb_size)
772+
+ (i * ENDSWAP(gpt_header.size_of_partition_entry)),
773+
sizeof(struct gpt_entry));
774+
775+
if (gpt_entry.unique_partition_guid[0] == 0 &&
776+
gpt_entry.unique_partition_guid[1] == 0) {
777+
continue;
778+
}
779+
780+
if (ENDSWAP(gpt_entry.starting_lba) > UINT32_MAX) {
781+
if (!quiet) {
782+
fprintf(stderr, "Starting LBA of partition %zu is greater than UINT32_MAX, will not convert GPT.\n", i + 1);
783+
}
784+
goto no_mbr_conv;
785+
}
786+
part_to_conv[part_to_conv_i].lba_start = ENDSWAP(gpt_entry.starting_lba);
787+
if (ENDSWAP(gpt_entry.ending_lba) > UINT32_MAX) {
788+
if (!quiet) {
789+
fprintf(stderr, "Ending LBA of partition %zu is greater than UINT32_MAX, will not convert GPT.\n", i + 1);
790+
}
791+
goto no_mbr_conv;
792+
}
793+
part_to_conv[part_to_conv_i].lba_end = ENDSWAP(gpt_entry.ending_lba);
794+
795+
int type = gpt2mbr_type(ENDSWAP(gpt_entry.partition_type_guid[0]),
796+
ENDSWAP(gpt_entry.partition_type_guid[1]));
797+
if (type == -1) {
798+
if (!quiet) {
799+
fprintf(stderr, "Cannot convert partition type for partition %zu, will not convert GPT.\n", i + 1);
800+
}
801+
goto no_mbr_conv;
802+
}
803+
804+
if (part_to_conv_i == 4) {
805+
if (!quiet) {
806+
fprintf(stderr, "GPT contains more than 4 partitions, will not convert.\n");
807+
}
808+
goto no_mbr_conv;
809+
}
810+
811+
part_to_conv[part_to_conv_i].type = type;
812+
813+
part_to_conv_i++;
814+
}
815+
816+
// Nuke the GPTs.
817+
void *empty_lba = calloc(1, lb_size);
818+
if (empty_lba == NULL) {
819+
perror_wrap("error: bios_install(): malloc()");
820+
goto cleanup;
821+
}
822+
823+
// ... nuke primary GPT + protective MBR.
824+
for (size_t i = 0; i < 34; i++) {
825+
device_write(empty_lba, i * lb_size, lb_size);
826+
}
827+
828+
// ... nuke secondary GPT.
829+
for (size_t i = 0; i < 33; i++) {
830+
device_write(empty_lba, ((ENDSWAP(gpt_header.alternate_lba) - 32) + i) * lb_size, lb_size);
831+
}
832+
833+
free(empty_lba);
834+
835+
// We're no longer GPT.
836+
gpt = 0;
837+
838+
// Generate pseudorandom MBR disk ID.
839+
srand(time(NULL));
840+
for (size_t i = 0; i < 4; i++) {
841+
uint8_t r = rand();
842+
device_write(&r, 0x1b8 + i, 1);
843+
}
844+
845+
// Write out the partition entries.
846+
for (size_t i = 0; i < part_to_conv_i; i++) {
847+
device_write(&part_to_conv[i].type, 0x1be + i * 16 + 0x04, 1);
848+
uint32_t lba_start = ENDSWAP(part_to_conv[i].lba_start);
849+
device_write(&lba_start, 0x1be + i * 16 + 0x08, 4);
850+
uint32_t sect_count = ENDSWAP((part_to_conv[i].lba_end - part_to_conv[i].lba_start) + 1);
851+
device_write(&sect_count, 0x1be + i * 16 + 0x0c, 4);
852+
}
853+
854+
if (!quiet) {
855+
fprintf(stderr, "Conversion successful.\n");
856+
}
857+
}
858+
859+
no_mbr_conv:;
860+
708861
int mbr = 0;
709862
if (gpt == 0) {
710863
// Do all sanity checks on MBR

0 commit comments

Comments
 (0)