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
7779struct 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+
161188static 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) {
581613static 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 ) + 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