How could I make a UEFI bootable Disk by writing raw Disk?

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
User avatar
gamingjam60
Member
Member
Posts: 70
Joined: Sat Aug 24, 2024 10:06 pm
Libera.chat IRC: gamingjam60
Location: India
GitHub: https://github.com/baponkar
Contact:

How could I make a UEFI bootable Disk by writing raw Disk?

Post by gamingjam60 »

I want to install My Operating System along with Limine Bootloader in a raw Disk i.e. UEFI bootable disk. My Plan is
1. Run Bootable iso9660 disk to run OS
2. Creating GPT Partition along with FAT32 Partition by FatFS library function f_mkfs() function .
3. Updating Protective MBR (Sector 0)
4. Writing and Updating GPT Partition Header and Partition Entries(Sector 1-33)
5. GPT Backup at Last Sector
6. Copy following files at Sector offset 2048:

Code: Select all

"/BOOT/BOOT_LOA.BMP",
 "/BOOT/KERNEL.BIN",
 "/BOOT/LIMINE/LIMINE_B.BIN",
 "/BOOT/LIMINE/LIMINE_B.SYS",
 "/BOOT/LIMINE/LIMINE_U.BIN",
 "/BOOT/LIMINE.CON",
 "/BOOT/USER_MAI.ELF",
 "/BOOT.CAT",
 "/EFI/BOOT/BOOTIA32.EFI",
 "/EFI/BOOT/BOOTX64.EFI", 
 
I have inspect device disk has BOOT_LOA.BMP at binary location : 0x184000 (2048*512= 0x100000) and inspect the disk image

Code: Select all

gdisk -l disk_img/disk_1.img
GPT fdisk (gdisk) version 1.0.10

Partition table scan:
  MBR: protective
  BSD: not present
  APM: not present
  GPT: present

Found valid GPT with protective MBR; using GPT.
Warning! Main partition table overlaps the first partition by 34 blocks!
You will need to delete this partition or resize it in another utility.
Disk disk_img/disk_1.img: 4194304 sectors, 2.0 GiB
Sector size (logical): 512 bytes
Disk identifier (GUID): 14131211-1615-1817-191A-1B1C1D1E1F20
Partition table holds up to 128 entries
Main partition table begins at sector 2 and ends at sector 33
First usable sector is 34, last usable sector is 4194270
Partitions will be aligned on 2048-sector boundaries
Total free space is 2014 sectors (1007.0 KiB)

Number  Start (sector)    End (sector)  Size       Code  Name
   1            2048         4194270   2.0 GiB     EF00  E
But unfortunately it is not booting OS showing output:

Code: Select all

BdsDxe: failed to load Boot0001 "UEFI QEMU HARDDISK QM00013 " from PciRoot(0x0)/Pci(0x2,0x0)/Sata(0x0,0xFFFF,0x0): Not Found
BdsDxe: failed to load Boot0002 "UEFI QEMU HARDDISK QM00015 " from PciRoot(0x0)/Pci(0x2,0x0)/Sata(0x1,0xFFFF,0x0): Not Found
What is wrong in here?

Here my functionss:

Code: Select all

// Function to create protective MBR
bool create_protective_mbr(int disk_no) {
    Disk disk = disks[disk_no];
    uint64_t total_sectors = disk.total_sectors;

    uint8_t mbr[512];
    memset(mbr, 0, sizeof(mbr));

    // ---- Partition Entry @ offset 0x1BE ----
    // Boot indicator
    mbr[446] = 0x00; // non-bootable

    // Starting CHS (C=0, H=0, S=2)
    mbr[447] = 0x00; // head
    mbr[448] = 0x02; // sector (2)
    mbr[449] = 0x00; // cylinder low

    // Partition type: 0xEE = GPT protective
    mbr[450] = 0xEE;

    // Ending CHS (max out)
    mbr[451] = 0xFF;
    mbr[452] = 0xFF;
    mbr[453] = 0xFF;

    // Starting LBA = 1
    mbr[454] = 0x01;
    mbr[455] = 0x00;
    mbr[456] = 0x00;
    mbr[457] = 0x00;

    // Size in sectors = total_sectors - 1 (limited to 32-bit)
    uint32_t size_lba = (total_sectors > 0xFFFFFFFFULL) ? 0xFFFFFFFF : (uint32_t)(total_sectors - 1);
    mbr[458] = (size_lba >> 0) & 0xFF;
    mbr[459] = (size_lba >> 8) & 0xFF;
    mbr[460] = (size_lba >> 16) & 0xFF;
    mbr[461] = (size_lba >> 24) & 0xFF;

    // ---- Boot signature ----
    mbr[510] = 0x55;
    mbr[511] = 0xAA;

    // ---- Write LBA 0 ----
    return kebla_disk_write(disk_no, 0, 1, mbr);
}



// CRC32 lookup table
static uint32_t crc32_table[256];
static int crc32_table_computed = 0;

// Generate CRC32 lookup table
static void generate_crc32_table(void) {
    if (crc32_table_computed) return;
    
    for (uint32_t i = 0; i < 256; i++) {
        uint32_t c = i;
        for (int j = 0; j < 8; j++) {
            if (c & 1) {
                c = 0xEDB88320 ^ (c >> 1);
            } else {
                c = c >> 1;
            }
        }
        crc32_table[i] = c;
    }
    crc32_table_computed = 1;
}


// Calculate CRC32 for data buffer
uint32_t crc32(const void *buf, size_t len){

    generate_crc32_table();
    
    const uint8_t *data = (const uint8_t *)buf;
    uint32_t crc = 0xFFFFFFFF;
    
    for (size_t i = 0; i < len; i++) {
        uint8_t byte = data[i];
        uint32_t index = (crc ^ byte) & 0xFF;
        crc = (crc >> 8) ^ crc32_table[index];
    }
    
    return ~crc;
}

// Function to create GPT header
bool create_gpt_header(int disk_no){

    Disk disk = disks[disk_no];
    uint64_t total_sectors = disk.total_sectors;

    gpt_header_t header = {0};
    
    // GPT signature
    header.signature = 0x5452415020494645ULL;   // "EFI PART"
    header.revision = 0x00010000;
    header.header_size = sizeof(gpt_header_t);
    header.current_lba = 1;
    header.backup_lba = total_sectors - 1; 
    header.first_usable_lba = 34;
    header.last_usable_lba = total_sectors - 34; // Use actual disk size
    header.partition_entries_lba = 2;
    header.num_partition_entries = 128;
    header.partition_entry_size = sizeof(gpt_partition_entry_t);
    
    // Generate disk GUID
    for(int i = 0; i < 16; i++) {
        header.disk_guid[i] = 0x11 + i;
    }
    
    // Calculate header CRC32 (temporarily set to 0 for calculation)
    header.header_crc32 = 0;
    header.header_crc32 = crc32(&header, header.header_size);
    
    return kebla_disk_write(disk_no, 1, 1, &header);
}


bool create_complete_gpt(int disk_no) {
    Disk disk = disks[disk_no];
    uint64_t total_sectors = disk.total_sectors;
    
    // Create partition entries array
    gpt_partition_entry_t partitions[128] = {0};
    
    // Create EFI system partition (first entry)
    uint8_t efi_type_guid[16] = {0x28, 0x73, 0x2A, 0xC1, 0x1F, 0xF8, 0xD2, 0x11, 
                                 0xBA, 0x4B, 0x00, 0xA0, 0xC9, 0x3E, 0xC9, 0x3B};
    memcpy(partitions[0].type_guid, efi_type_guid, 16);
    
    // Generate partition GUID
    for(int i = 0; i < 16; i++) {
        partitions[0].partition_guid[i] = 0x22 + i;
    }
    
    // FIX: Don't use entire disk - leave space for backup GPT
    partitions[0].first_lba = 2048;
    partitions[0].last_lba = total_sectors - 34;  // Leave last 33 sectors for backup GPT
    // Set EFI system partition attributes (bit 1 = legacy BIOS bootable)
    partitions[0].attributes = 0x1; // OR 0x8000000000000001 for UEFI required
    
    // Partition name
    const char* name = "EFI System Partition";
    for(int i = 0; i < strlen(name) && i < 36; i++) {
        partitions[0].name[i * 2] = name[i];  // UTF-16LE low byte
        partitions[0].name[i * 2 + 1] = 0;    // UTF-16LE high byte
    }
    
    // Calculate partition entries CRC32
    uint32_t num_entries = 128;
    uint32_t entry_size = sizeof(gpt_partition_entry_t);
    uint32_t partition_entries_crc = crc32(partitions, num_entries * entry_size);
    
    // Create primary GPT header
    gpt_header_t primary_header = {0};
    primary_header.signature = 0x5452415020494645ULL;
    primary_header.revision = 0x00010000;
    primary_header.header_size = sizeof(gpt_header_t);
    primary_header.current_lba = 1;
    primary_header.backup_lba = total_sectors - 1;
    primary_header.first_usable_lba = 34;
    primary_header.last_usable_lba = total_sectors - 34;  // Last usable before backup area
    primary_header.partition_entries_lba = 2;
    primary_header.num_partition_entries = num_entries;
    primary_header.partition_entry_size = entry_size;
    primary_header.partition_entries_crc32 = partition_entries_crc;
    
    // Generate disk GUID
    for(int i = 0; i < 16; i++) {
        primary_header.disk_guid[i] = 0x11 + i;
    }
    
    // Calculate primary header CRC32
    primary_header.header_crc32 = 0;
    primary_header.header_crc32 = crc32(&primary_header, primary_header.header_size);
    
    // Create backup GPT header
    gpt_header_t backup_header = primary_header;
    backup_header.current_lba = total_sectors - 1;              // Backup header at end
    backup_header.backup_lba = 1;                               // Points to primary header
    backup_header.partition_entries_lba = total_sectors - 33;   // Backup entries before header
    
    // Calculate backup header CRC32
    backup_header.header_crc32 = 0;
    backup_header.header_crc32 = crc32(&backup_header, backup_header.header_size);
    
    // Write everything
    bool success = true;
    
    // Primary GPT structures
    success = success && kebla_disk_write(disk_no, 1, 1, &primary_header);      // Primary header
    success = success && kebla_disk_write(disk_no, 2, 32, partitions);          // Primary partition table (32 sectors)
    
    // Backup GPT structures  
    success = success && kebla_disk_write(disk_no, (total_sectors - 33), 32, partitions);  // Backup partition table
    success = success && kebla_disk_write(disk_no, (total_sectors - 1), 1, &backup_header); // Backup header
    
    return success;
}
Debugging:

I am getting this disk behavior:

Code: Select all

sudo gdisk disk_img/disk_1.img
GPT fdisk (gdisk) version 1.0.10

Partition table scan:
  MBR: protective
  BSD: not present
  APM: not present
  GPT: present

Found valid GPT with protective MBR; using GPT.
Warning! Main partition table overlaps the first partition by 34 blocks!
You will need to delete this partition or resize it in another utility.

Command (? for help): p
Disk disk_img/disk_1.img: 4194304 sectors, 2.0 GiB
Sector size (logical): 512 bytes
Disk identifier (GUID): 14131211-1615-1817-191A-1B1C1D1E1F20
Partition table holds up to 128 entries
Main partition table begins at sector 2 and ends at sector 33
First usable sector is 34, last usable sector is 4194270
Partitions will be aligned on 2048-sector boundaries
Total free space is 2014 sectors (1007.0 KiB)

Number  Start (sector)    End (sector)  Size       Code  Name
   1            2048         4194270   2.0 GiB     EF00  E
QEmu Trace Log

Code: Select all

Servicing hardware INT=0x20
   727: v=20 e=0000 i=0 cpl=0 IP=0038:000000007e8dcbcd pc=000000007e8dcbcd SP=0030:000000007fe65850 env->regs[R_EAX]=0000000000000060
RAX=0000000000000060 RBX=000000007e744398 RCX=0000000000000011 RDX=00000000000003fd
RSI=0000000000000005 RDI=000000007e744398 RBP=000000007fe65860 RSP=000000007fe65850
R8 =000000007fe6594c R9 =00000000000003fd R10=00000000000005f2 R11=0000000000000001
R12=000000007e7443a8 R13=0000000000000000 R14=000000007fe6588f R15=000000007fe887c0
RIP=000000007e8dcbcd RFL=00000293 [--S-A-C] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =0030 0000000000000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
CS =0038 0000000000000000 ffffffff 00af9a00 DPL=0 CS64 [-R-]
SS =0030 0000000000000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
DS =0030 0000000000000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
FS =0030 0000000000000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
GS =0030 0000000000000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
LDT=0000 0000000000000000 0000ffff 00008200 DPL=0 LDT
TR =0000 0000000000000000 0000ffff 00008b00 DPL=0 TSS64-busy
GDT=     000000007f5dc000 00000047
IDT=     000000007f137018 00000fff
CR0=80010033 CR2=0000000000000000 CR3=000000007f801000 CR4=00000668
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000 
DR6=00000000ffff0ff0 DR7=0000000000000400
CCS=0000000000000091 CCD=fffffffffffffffe CCO=EFLAGS
EFER=0000000000000d00
Servicing hardware INT=0x20
   728: v=20 e=0000 i=0 cpl=0 IP=0038:000000007e8dcbcd pc=000000007e8dcbcd SP=0030:000000007fe65850 env->regs[R_EAX]=0000000000000060
RAX=0000000000000060 RBX=000000007e744398 RCX=0000000000000011 RDX=00000000000003fd
RSI=0000000000000005 RDI=000000007e744398 RBP=000000007fe65860 RSP=000000007fe65850
R8 =000000007fe6594c R9 =00000000000003fd R10=00000000000005f2 R11=0000000000000001
R12=000000007e7443a8 R13=0000000000000000 R14=000000007fe6588f R15=000000007fe887c0
RIP=000000007e8dcbcd RFL=00000293 [--S-A-C] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =0030 0000000000000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
CS =0038 0000000000000000 ffffffff 00af9a00 DPL=0 CS64 [-R-]
SS =0030 0000000000000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
DS =0030 0000000000000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
FS =0030 0000000000000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
GS =0030 0000000000000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
LDT=0000 0000000000000000 0000ffff 00008200 DPL=0 LDT
TR =0000 0000000000000000 0000ffff 00008b00 DPL=0 TSS64-busy
GDT=     000000007f5dc000 00000047
IDT=     000000007f137018 00000fff
CR0=80010033 CR2=0000000000000000 CR3=000000007f801000 CR4=00000668
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000 
DR6=00000000ffff0ff0 DR7=0000000000000400
CCS=0000000000000091 CCD=0000000000000000 CCO=EFLAGS
EFER=0000000000000d00
Octocontrabass
Member
Member
Posts: 6248
Joined: Mon Mar 25, 2013 7:01 pm

Re: How could I make a UEFI bootable Disk by writing raw Disk?

Post by Octocontrabass »

gamingjam60 wrote: Sat Oct 18, 2025 10:31 am

Code: Select all

Warning! Main partition table overlaps the first partition by 34 blocks!
Partition a blank disk image using gdisk and compare it to the disk image partitioned using your code. What does gdisk do differently? Could any of those differences explain why gdisk shows this warning?
Post Reply