Skip to content

Commit 4b42bf2

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

File tree

1 file changed

+139
-0
lines changed

1 file changed

+139
-0
lines changed

host/limine.c

Lines changed: 139 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,110 @@ 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 (gpt_entry.starting_lba > UINT32_MAX) {
781+
fprintf(stderr, "Starting LBA of partition %zu is greater than UINT32_MAX, will not convert GPT.\n", i + 1);
782+
goto no_mbr_conv;
783+
}
784+
part_to_conv[part_to_conv_i].lba_start = ENDSWAP(gpt_entry.starting_lba);
785+
if (gpt_entry.ending_lba > UINT32_MAX) {
786+
fprintf(stderr, "Ending LBA of partition %zu is greater than UINT32_MAX, will not convert GPT.\n", i + 1);
787+
goto no_mbr_conv;
788+
}
789+
part_to_conv[part_to_conv_i].lba_end = ENDSWAP(gpt_entry.ending_lba);
790+
791+
int type = gpt2mbr_type(ENDSWAP(gpt_entry.partition_type_guid[0]),
792+
ENDSWAP(gpt_entry.partition_type_guid[1]));
793+
if (type == -1) {
794+
fprintf(stderr, "Cannot convert partition type for partition %zu, will not convert GPT.\n", i + 1);
795+
goto no_mbr_conv;
796+
}
797+
798+
if (part_to_conv_i == 4) {
799+
fprintf(stderr, "GPT contains more than 4 partitions, will not convert.\n");
800+
goto no_mbr_conv;
801+
}
802+
803+
part_to_conv[part_to_conv_i].type = type;
804+
805+
part_to_conv_i++;
806+
}
807+
808+
// Nuke the GPTs.
809+
void *empty_lba = calloc(1, lb_size);
810+
811+
// ... nuke primary GPT + protective MBR.
812+
for (size_t i = 0; i < 34; i++) {
813+
device_write(empty_lba, i * lb_size, lb_size);
814+
}
815+
816+
// ... nuke secondary GPT.
817+
for (size_t i = 0; i < 33; i++) {
818+
device_write(empty_lba, ENDSWAP(gpt_header.alternate_lba) + i * lb_size, lb_size);
819+
}
820+
821+
free(empty_lba);
822+
823+
// We're no longer GPT.
824+
gpt = 0;
825+
826+
// Generate pseudorandom MBR disk ID.
827+
srand(time(NULL));
828+
for (size_t i = 0; i < 4; i++) {
829+
uint8_t r = rand();
830+
device_write(&r, 0x1b8 + i, 1);
831+
}
832+
833+
// Write out the partition entries.
834+
for (size_t i = 0; i < part_to_conv_i; i++) {
835+
device_write(&part_to_conv[i].type, 0x1be + i * 16 + 0x04, 1);
836+
uint32_t lba_start = ENDSWAP(part_to_conv[i].lba_start);
837+
device_write(&lba_start, 0x1be + i * 16 + 0x08, 4);
838+
uint32_t sect_count = ENDSWAP((part_to_conv[i].lba_end - part_to_conv[i].lba_start) + 1);
839+
device_write(&sect_count, 0x1be + i * 16 + 0x0c, 4);
840+
}
841+
842+
fprintf(stderr, "Conversion successful.\n");
843+
}
844+
845+
no_mbr_conv:;
846+
708847
int mbr = 0;
709848
if (gpt == 0) {
710849
// Do all sanity checks on MBR

0 commit comments

Comments
 (0)