luajitos

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

grub.c (32294B)


      1 /* GRUB-like Bootloader Installation for LuajitOS
      2  *
      3  * Implements a complete bootloader that:
      4  * 1. MBR (sector 0) - loads core loader from sectors 1-62
      5  * 2. Core loader - parses FAT16, loads /BOOT/KERNEL.BIN, jumps to it
      6  *
      7  * The core loader is installed in the "embedding area" between MBR and
      8  * the first partition (sectors 1 to 2047, but we only use 1-62 = 31KB).
      9  */
     10 
     11 #include "grub.h"
     12 #include "ata.h"
     13 #include "partition.h"
     14 #include "fat16.h"
     15 #include <string.h>
     16 #include <stdio.h>
     17 
     18 /* External terminal function for debug output */
     19 extern void terminal_writestring(const char *str);
     20 
     21 /* ============================================================================
     22  * MBR Boot Code (Stage 1) - 446 bytes max
     23  *
     24  * This MBR:
     25  * 1. Relocates itself to 0x0600
     26  * 2. Loads sectors 1-62 (core loader) to 0x7E00
     27  * 3. Jumps to core loader at 0x7E00
     28  * ========================================================================= */
     29 
     30 static const uint8_t mbr_boot_code[] = {
     31     /* 0x00: Entry - BIOS loads us at 0x7C00 */
     32     0xFA,                           /* cli */
     33     0x31, 0xC0,                     /* xor ax, ax */
     34     0x8E, 0xD8,                     /* mov ds, ax */
     35     0x8E, 0xC0,                     /* mov es, ax */
     36     0x8E, 0xD0,                     /* mov ss, ax */
     37     0xBC, 0x00, 0x7C,               /* mov sp, 0x7C00 */
     38     0xFB,                           /* sti */
     39 
     40     /* Save drive number */
     41     0x88, 0x16, 0x00, 0x06,         /* mov [0x0600], dl */
     42 
     43     /* Relocate MBR from 0x7C00 to 0x0600 */
     44     0xBE, 0x00, 0x7C,               /* mov si, 0x7C00 */
     45     0xBF, 0x00, 0x06,               /* mov di, 0x0600 */
     46     0xB9, 0x00, 0x02,               /* mov cx, 512 */
     47     0xFC,                           /* cld */
     48     0xF3, 0xA4,                     /* rep movsb */
     49 
     50     /* Jump to relocated code + offset */
     51     0xEA, 0x23, 0x06, 0x00, 0x00,   /* jmp 0x0000:0x0623 */
     52 
     53     /* 0x23: Now running from 0x0600, load core (sectors 1-62) to 0x7C00 */
     54     /* We load 62 sectors (31KB) starting from sector 1 */
     55 
     56     /* Set up DAP at 0x0700 */
     57     0xBF, 0x00, 0x07,               /* mov di, 0x0700 */
     58     0xC6, 0x05, 0x10,               /* mov byte [di+0], 16 (size) */
     59     0xC6, 0x45, 0x01, 0x00,         /* mov byte [di+1], 0 */
     60     0xC6, 0x45, 0x02, 0x3E,         /* mov byte [di+2], 62 (sector count) */
     61     0xC6, 0x45, 0x03, 0x00,         /* mov byte [di+3], 0 */
     62     0xC7, 0x45, 0x04, 0x00, 0x7C,   /* mov word [di+4], 0x7C00 (offset) */
     63     0xC7, 0x45, 0x06, 0x00, 0x00,   /* mov word [di+6], 0x0000 (segment) */
     64     0xC7, 0x45, 0x08, 0x01, 0x00,   /* mov word [di+8], 1 (LBA low) */
     65     0xC7, 0x45, 0x0A, 0x00, 0x00,   /* mov word [di+10], 0 */
     66     0xC7, 0x45, 0x0C, 0x00, 0x00,   /* mov word [di+12], 0 */
     67     0xC7, 0x45, 0x0E, 0x00, 0x00,   /* mov word [di+14], 0 */
     68 
     69     /* Read sectors */
     70     0xB4, 0x42,                     /* mov ah, 0x42 */
     71     0x8A, 0x16, 0x00, 0x06,         /* mov dl, [0x0600] (drive) */
     72     0xBE, 0x00, 0x07,               /* mov si, 0x0700 (DAP) */
     73     0xCD, 0x13,                     /* int 0x13 */
     74     0x72, 0x0D,                     /* jc error */
     75 
     76     /* Jump to core loader with drive in DL */
     77     0x8A, 0x16, 0x00, 0x06,         /* mov dl, [0x0600] */
     78     0xEA, 0x00, 0x7C, 0x00, 0x00,   /* jmp 0x0000:0x7C00 */
     79 
     80     /* Error handler */
     81     0xBE, 0x78, 0x06,               /* mov si, 0x0678 (error msg) */
     82     /* Print loop */
     83     0xAC,                           /* lodsb */
     84     0x08, 0xC0,                     /* or al, al */
     85     0x74, 0x04,                     /* jz halt */
     86     0xB4, 0x0E,                     /* mov ah, 0x0E */
     87     0xCD, 0x10,                     /* int 0x10 */
     88     0xEB, 0xF5,                     /* jmp print */
     89     /* Halt */
     90     0xF4,                           /* hlt */
     91     0xEB, 0xFD,                     /* jmp $ */
     92 
     93     /* 0x78: Error message */
     94     'D', 'i', 's', 'k', ' ', 'e', 'r', 'r', 'o', 'r', '\r', '\n', 0
     95 };
     96 
     97 /* ============================================================================
     98  * Core Loader (Stage 2) - up to 31KB (62 sectors)
     99  *
    100  * This loader:
    101  * 1. Switches to protected mode
    102  * 2. Enables A20 line
    103  * 3. Reads FAT16 boot partition
    104  * 4. Finds and loads /BOOT/KERNEL.BIN to 0x100000 (1MB)
    105  * 5. Sets up multiboot info structure
    106  * 6. Jumps to kernel entry point
    107  *
    108  * Layout at 0x7C00:
    109  * - 0x7C00-0x7DFF: Stage 2 boot sector (this code)
    110  * - 0x7E00-0x????: Continuation of stage 2
    111  * - 0x8000: FAT buffer
    112  * - 0x10000: Root directory buffer
    113  * - 0x20000: File read buffer
    114  * - 0x100000: Kernel load address
    115  * ========================================================================= */
    116 
    117 /* This is 16-bit real mode code that will be assembled into the core loader */
    118 static const uint8_t core_loader[] = {
    119     /* === Sector 0 of core (at 0x7C00) === */
    120 
    121     /* 0x00: Entry point */
    122     0xFA,                           /* cli */
    123     0x31, 0xC0,                     /* xor ax, ax */
    124     0x8E, 0xD8,                     /* mov ds, ax */
    125     0x8E, 0xC0,                     /* mov es, ax */
    126     0x8E, 0xD0,                     /* mov ss, ax */
    127     0xBC, 0x00, 0x7C,               /* mov sp, 0x7C00 */
    128 
    129     /* Save drive number at known location */
    130     0x88, 0x16, 0xFC, 0x8F,         /* mov [0x8FFC], dl */
    131 
    132     /* Print loading message */
    133     0xBE, 0x00, 0x7D,               /* mov si, 0x7D00 (message) */
    134     0xE8, 0x80, 0x00,               /* call print_string (at ~0x7C90) */
    135 
    136     /* Enable A20 line via keyboard controller */
    137     0xE8, 0xA0, 0x00,               /* call enable_a20 (at ~0x7CB0) */
    138 
    139     /* Read partition table to find boot partition */
    140     /* Read MBR (sector 0) to 0x8000 */
    141     0xBB, 0x00, 0x80,               /* mov bx, 0x8000 */
    142     0xB9, 0x01, 0x00,               /* mov cx, 1 (1 sector) */
    143     0x31, 0xD2,                     /* xor dx, dx */
    144     0x31, 0xF6,                     /* xor si, si (LBA = 0) */
    145     0xE8, 0xE0, 0x00,               /* call read_sectors */
    146 
    147     /* Find bootable partition - check partition table at 0x81BE */
    148     0xBE, 0xBE, 0x81,               /* mov si, 0x81BE */
    149     0xB1, 0x04,                     /* mov cl, 4 */
    150 
    151     /* 0x35: Partition loop */
    152     0x80, 0x3C, 0x80,               /* cmp byte [si], 0x80 */
    153     0x74, 0x08,                     /* je found_part */
    154     0x83, 0xC6, 0x10,               /* add si, 16 */
    155     0xE2, 0xF5,                     /* loop part_loop */
    156     0xE9, 0xA0, 0x00,               /* jmp no_part_error */
    157 
    158     /* 0x42: Found bootable partition */
    159     /* Get partition start LBA from [si+8] */
    160     0x8B, 0x44, 0x08,               /* mov ax, [si+8] */
    161     0x8B, 0x54, 0x0A,               /* mov dx, [si+10] */
    162     0xA3, 0xF8, 0x8F,               /* mov [0x8FF8], ax (save part_start low) */
    163     0x89, 0x16, 0xFA, 0x8F,         /* mov [0x8FFA], dx (save part_start high) */
    164 
    165     /* Read FAT16 boot sector (VBR) to 0x8000 */
    166     0xBB, 0x00, 0x80,               /* mov bx, 0x8000 */
    167     0xB9, 0x01, 0x00,               /* mov cx, 1 */
    168     0x8B, 0x16, 0xFA, 0x8F,         /* mov dx, [0x8FFA] */
    169     0x8B, 0x36, 0xF8, 0x8F,         /* mov si, [0x8FF8] */
    170     0xE8, 0xB0, 0x00,               /* call read_sectors */
    171 
    172     /* Parse BPB - extract FAT16 parameters */
    173     /* bytes_per_sector at offset 11 */
    174     0x8B, 0x06, 0x0B, 0x80,         /* mov ax, [0x800B] */
    175     0xA3, 0xE0, 0x8F,               /* mov [0x8FE0], ax (bytes_per_sector) */
    176 
    177     /* sectors_per_cluster at offset 13 */
    178     0x8A, 0x06, 0x0D, 0x80,         /* mov al, [0x800D] */
    179     0xA2, 0xE2, 0x8F,               /* mov [0x8FE2], al (sectors_per_cluster) */
    180 
    181     /* reserved_sectors at offset 14 */
    182     0x8B, 0x06, 0x0E, 0x80,         /* mov ax, [0x800E] */
    183     0xA3, 0xE4, 0x8F,               /* mov [0x8FE4], ax (reserved_sectors) */
    184 
    185     /* num_fats at offset 16 */
    186     0x8A, 0x06, 0x10, 0x80,         /* mov al, [0x8010] */
    187     0xA2, 0xE6, 0x8F,               /* mov [0x8FE6], al (num_fats) */
    188 
    189     /* root_entries at offset 17 */
    190     0x8B, 0x06, 0x11, 0x80,         /* mov ax, [0x8011] */
    191     0xA3, 0xE8, 0x8F,               /* mov [0x8FE8], ax (root_entries) */
    192 
    193     /* fat_sectors at offset 22 */
    194     0x8B, 0x06, 0x16, 0x80,         /* mov ax, [0x8016] */
    195     0xA3, 0xEA, 0x8F,               /* mov [0x8FEA], ax (fat_sectors) */
    196 
    197     /* Jump to next sector for more code */
    198     0xE9, 0x50, 0x01,               /* jmp 0x7E00 (sector 1 of core) */
    199 
    200     /* === Padding and strings === */
    201     /* 0x90: print_string subroutine */
    202     0xAC,                           /* lodsb */
    203     0x08, 0xC0,                     /* or al, al */
    204     0x74, 0x06,                     /* jz .done */
    205     0xB4, 0x0E,                     /* mov ah, 0x0E */
    206     0xCD, 0x10,                     /* int 0x10 */
    207     0xEB, 0xF5,                     /* jmp print_string */
    208     0xC3,                           /* ret */
    209 
    210     /* 0x9C: Padding */
    211     0x90, 0x90, 0x90, 0x90,
    212 
    213     /* 0xA0: no_part_error */
    214     0xBE, 0x20, 0x7D,               /* mov si, err_no_part */
    215     0xE8, 0xEB, 0xFF,               /* call print_string */
    216     0xF4,                           /* hlt */
    217     0xEB, 0xFD,                     /* jmp $ */
    218 
    219     /* 0xAA: Padding */
    220     0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
    221 
    222     /* 0xB0: enable_a20 */
    223     /* Try BIOS first */
    224     0xB8, 0x01, 0x24,               /* mov ax, 0x2401 */
    225     0xCD, 0x15,                     /* int 0x15 */
    226     0x73, 0x1C,                     /* jnc a20_done */
    227 
    228     /* Try keyboard controller */
    229     0xE4, 0x64,                     /* in al, 0x64 */
    230     0xA8, 0x02,                     /* test al, 2 */
    231     0x75, 0xFA,                     /* jnz wait1 */
    232     0xB0, 0xD1,                     /* mov al, 0xD1 */
    233     0xE6, 0x64,                     /* out 0x64, al */
    234     0xE4, 0x64,                     /* in al, 0x64 */
    235     0xA8, 0x02,                     /* test al, 2 */
    236     0x75, 0xFA,                     /* jnz wait2 */
    237     0xB0, 0xDF,                     /* mov al, 0xDF */
    238     0xE6, 0x60,                     /* out 0x60, al */
    239     0xE4, 0x64,                     /* in al, 0x64 */
    240     0xA8, 0x02,                     /* test al, 2 */
    241     0x75, 0xFA,                     /* jnz wait3 */
    242     /* a20_done: */
    243     0xC3,                           /* ret */
    244 
    245     /* 0xD4: Padding to 0xE0 */
    246     0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
    247     0x90, 0x90, 0x90, 0x90,
    248 
    249     /* 0xE0: read_sectors - Read CX sectors from LBA DX:SI to ES:BX */
    250     /* Uses INT 13h extended read */
    251     0x60,                           /* pusha */
    252     0x06,                           /* push es */
    253 
    254     /* Set up DAP at 0x0500 */
    255     0xBF, 0x00, 0x05,               /* mov di, 0x0500 */
    256     0xC6, 0x05, 0x10,               /* mov byte [di], 16 */
    257     0xC6, 0x45, 0x01, 0x00,         /* mov byte [di+1], 0 */
    258     0x88, 0x4D, 0x02,               /* mov [di+2], cl (sector count) */
    259     0xC6, 0x45, 0x03, 0x00,         /* mov byte [di+3], 0 */
    260     0x89, 0x5D, 0x04,               /* mov [di+4], bx (offset) */
    261     0x8C, 0xC0,                     /* mov ax, es */
    262     0x89, 0x45, 0x06,               /* mov [di+6], ax (segment) */
    263     0x89, 0x75, 0x08,               /* mov [di+8], si (LBA low) */
    264     0x89, 0x55, 0x0A,               /* mov [di+10], dx (LBA high) */
    265     0xC7, 0x45, 0x0C, 0x00, 0x00,   /* mov word [di+12], 0 */
    266     0xC7, 0x45, 0x0E, 0x00, 0x00,   /* mov word [di+14], 0 */
    267 
    268     0xB4, 0x42,                     /* mov ah, 0x42 */
    269     0x8A, 0x16, 0xFC, 0x8F,         /* mov dl, [0x8FFC] (drive) */
    270     0xBE, 0x00, 0x05,               /* mov si, 0x0500 */
    271     0xCD, 0x13,                     /* int 0x13 */
    272 
    273     0x07,                           /* pop es */
    274     0x61,                           /* popa */
    275     0xC3,                           /* ret */
    276 
    277     /* Padding to 0x100 (256 bytes = end of first sector area used for code) */
    278 };
    279 
    280 /* Continuation of core loader - sector 1 onwards (at 0x7E00 when loaded) */
    281 static const uint8_t core_loader_cont[] = {
    282     /* === At 0x7E00: Continue FAT16 parsing and kernel loading === */
    283 
    284     /* Calculate root directory start:
    285      * root_start = part_start + reserved + (num_fats * fat_sectors)
    286      */
    287     0xA1, 0xE4, 0x8F,               /* mov ax, [reserved_sectors] */
    288     0x03, 0x06, 0xF8, 0x8F,         /* add ax, [part_start_low] */
    289     0x89, 0xC3,                     /* mov bx, ax */
    290 
    291     /* Add num_fats * fat_sectors */
    292     0x8A, 0x0E, 0xE6, 0x8F,         /* mov cl, [num_fats] */
    293     0x30, 0xED,                     /* xor ch, ch */
    294     0xA1, 0xEA, 0x8F,               /* mov ax, [fat_sectors] */
    295     /* Multiply: fat_sectors * num_fats */
    296     0xF7, 0xE1,                     /* mul cx */
    297     0x01, 0xC3,                     /* add bx, ax */
    298     /* BX = root_start (low word) */
    299     0x89, 0x1E, 0xEC, 0x8F,         /* mov [root_start], bx */
    300 
    301     /* Calculate root directory size in sectors:
    302      * root_sectors = (root_entries * 32) / 512
    303      */
    304     0xA1, 0xE8, 0x8F,               /* mov ax, [root_entries] */
    305     0xC1, 0xE8, 0x04,               /* shr ax, 4 (divide by 16, *32/512 = /16) */
    306     0xA3, 0xEE, 0x8F,               /* mov [root_sectors], ax */
    307 
    308     /* Calculate data start:
    309      * data_start = root_start + root_sectors
    310      */
    311     0x03, 0x1E, 0xEE, 0x8F,         /* add bx, [root_sectors] */
    312     0x89, 0x1E, 0xF0, 0x8F,         /* mov [data_start], bx */
    313 
    314     /* Read root directory to 0x10000 */
    315     0x06,                           /* push es */
    316     0xB8, 0x00, 0x10,               /* mov ax, 0x1000 */
    317     0x8E, 0xC0,                     /* mov es, ax */
    318     0x31, 0xDB,                     /* xor bx, bx (offset 0) */
    319     0x8B, 0x0E, 0xEE, 0x8F,         /* mov cx, [root_sectors] */
    320     0x31, 0xD2,                     /* xor dx, dx */
    321     0x8B, 0x36, 0xEC, 0x8F,         /* mov si, [root_start] */
    322     /* read_sectors(ES:BX, CX, DX:SI) */
    323     0xE8, 0x50, 0xFE,               /* call read_sectors (at 0x7CE0) */
    324     0x07,                           /* pop es */
    325 
    326     /* Search for "BOOT       " directory in root */
    327     0xBE, 0x00, 0x00,               /* mov si, 0 (offset in root dir) */
    328     0xB9, 0x00, 0x02,               /* mov cx, 512 (max entries to check) */
    329 
    330     /* 0x50: Search loop */
    331     0x06,                           /* push es */
    332     0xB8, 0x00, 0x10,               /* mov ax, 0x1000 */
    333     0x8E, 0xC0,                     /* mov es, ax */
    334 
    335     /* Compare 11 bytes with "BOOT       " */
    336     0x89, 0xF7,                     /* mov di, si */
    337     0x56,                           /* push si */
    338     0xBE, 0x00, 0x7F,               /* mov si, boot_dirname (at 0x7F00) */
    339     0xB2, 0x0B,                     /* mov dl, 11 */
    340 
    341     /* Compare loop */
    342     0x26, 0x8A, 0x05,               /* mov al, es:[di] */
    343     0x8A, 0x24,                     /* mov ah, [si] */
    344     0x38, 0xE0,                     /* cmp al, ah */
    345     0x75, 0x0C,                     /* jne next_entry */
    346     0x47,                           /* inc di */
    347     0x46,                           /* inc si */
    348     0xFE, 0xCA,                     /* dec dl */
    349     0x75, 0xF2,                     /* jnz cmp_loop */
    350     /* Found BOOT directory! */
    351     0x5E,                           /* pop si */
    352     0x07,                           /* pop es */
    353     0xEB, 0x10,                     /* jmp found_boot */
    354 
    355     /* 0x78: next_entry */
    356     0x5E,                           /* pop si */
    357     0x07,                           /* pop es */
    358     0x83, 0xC6, 0x20,               /* add si, 32 (next entry) */
    359     0xE2, 0xD4,                     /* loop search_loop */
    360     /* Boot dir not found */
    361     0xBE, 0x40, 0x7D,               /* mov si, err_no_boot_dir */
    362     0xE8, 0x0A, 0xFE,               /* call print_string */
    363     0xF4,                           /* hlt */
    364 
    365     /* 0x88: found_boot */
    366     /* SI = offset of BOOT entry in root dir buffer (segment 0x1000) */
    367     /* Get cluster from entry at [si+26] */
    368     0x06,                           /* push es */
    369     0xB8, 0x00, 0x10,               /* mov ax, 0x1000 */
    370     0x8E, 0xC0,                     /* mov es, ax */
    371     0x26, 0x8B, 0x44, 0x1A,         /* mov ax, es:[si+26] (cluster) */
    372     0x07,                           /* pop es */
    373     0xA3, 0xF2, 0x8F,               /* mov [boot_cluster], ax */
    374 
    375     /* Read BOOT directory cluster to find KERNEL.BIN */
    376     /* First, read FAT to 0x8000 */
    377     0xA1, 0xE4, 0x8F,               /* mov ax, [reserved_sectors] */
    378     0x03, 0x06, 0xF8, 0x8F,         /* add ax, [part_start_low] */
    379     0x89, 0xC6,                     /* mov si, ax (FAT start LBA) */
    380     0xBB, 0x00, 0x80,               /* mov bx, 0x8000 */
    381     0x8B, 0x0E, 0xEA, 0x8F,         /* mov cx, [fat_sectors] */
    382     0x31, 0xD2,                     /* xor dx, dx */
    383     0xE8, 0xFC, 0xFD,               /* call read_sectors */
    384 
    385     /* Convert boot_cluster to LBA and read */
    386     /* LBA = data_start + (cluster - 2) * sectors_per_cluster */
    387     0xA1, 0xF2, 0x8F,               /* mov ax, [boot_cluster] */
    388     0x48,                           /* dec ax */
    389     0x48,                           /* dec ax (cluster - 2) */
    390     0x8A, 0x0E, 0xE2, 0x8F,         /* mov cl, [sectors_per_cluster] */
    391     0x30, 0xED,                     /* xor ch, ch */
    392     0xF7, 0xE1,                     /* mul cx */
    393     0x03, 0x06, 0xF0, 0x8F,         /* add ax, [data_start] */
    394     0x89, 0xC6,                     /* mov si, ax (LBA) */
    395 
    396     /* Read BOOT dir to 0x10000 */
    397     0x06,                           /* push es */
    398     0xB8, 0x00, 0x10,               /* mov ax, 0x1000 */
    399     0x8E, 0xC0,                     /* mov es, ax */
    400     0x31, 0xDB,                     /* xor bx, bx */
    401     0x8A, 0x0E, 0xE2, 0x8F,         /* mov cl, [sectors_per_cluster] */
    402     0x31, 0xD2,                     /* xor dx, dx */
    403     0xE8, 0xD6, 0xFD,               /* call read_sectors */
    404     0x07,                           /* pop es */
    405 
    406     /* Search for "KERNEL  BIN" in BOOT directory */
    407     0xBE, 0x00, 0x00,               /* mov si, 0 */
    408     0xB9, 0x00, 0x01,               /* mov cx, 256 */
    409 
    410     /* 0xE8: kernel search loop */
    411     0x06,                           /* push es */
    412     0xB8, 0x00, 0x10,               /* mov ax, 0x1000 */
    413     0x8E, 0xC0,                     /* mov es, ax */
    414     0x89, 0xF7,                     /* mov di, si */
    415     0x56,                           /* push si */
    416     0xBE, 0x10, 0x7F,               /* mov si, kernel_filename */
    417     0xB2, 0x0B,                     /* mov dl, 11 */
    418 
    419     /* Compare loop */
    420     0x26, 0x8A, 0x05,               /* mov al, es:[di] */
    421     0x8A, 0x24,                     /* mov ah, [si] */
    422     0x38, 0xE0,                     /* cmp al, ah */
    423     0x75, 0x0C,                     /* jne next_kern */
    424     0x47,                           /* inc di */
    425     0x46,                           /* inc si */
    426     0xFE, 0xCA,                     /* dec dl */
    427     0x75, 0xF2,                     /* jnz cmp_kern */
    428     /* Found KERNEL.BIN! */
    429     0x5E,                           /* pop si */
    430     0x07,                           /* pop es */
    431     0xEB, 0x10,                     /* jmp found_kernel */
    432 
    433     /* next_kern */
    434     0x5E,                           /* pop si */
    435     0x07,                           /* pop es */
    436     0x83, 0xC6, 0x20,               /* add si, 32 */
    437     0xE2, 0xD4,                     /* loop kernel_search */
    438     /* Kernel not found */
    439     0xBE, 0x60, 0x7D,               /* mov si, err_no_kernel */
    440     0xE8, 0x7A, 0xFD,               /* call print_string */
    441     0xF4,                           /* hlt */
    442 
    443     /* found_kernel: */
    444     /* Get file size from [si+28] and cluster from [si+26] */
    445     0x06,                           /* push es */
    446     0xB8, 0x00, 0x10,               /* mov ax, 0x1000 */
    447     0x8E, 0xC0,                     /* mov es, ax */
    448     0x26, 0x8B, 0x44, 0x1C,         /* mov ax, es:[si+28] (size low) */
    449     0x26, 0x8B, 0x54, 0x1E,         /* mov dx, es:[si+30] (size high) */
    450     0xA3, 0xF4, 0x8F,               /* mov [kernel_size], ax */
    451     0x89, 0x16, 0xF6, 0x8F,         /* mov [kernel_size+2], dx */
    452     0x26, 0x8B, 0x44, 0x1A,         /* mov ax, es:[si+26] (cluster) */
    453     0x07,                           /* pop es */
    454     0xA3, 0xF2, 0x8F,               /* mov [kernel_cluster], ax */
    455 
    456     /* Now load kernel to 0x100000 using unreal mode */
    457     /* First switch to protected mode then back with big segments */
    458 
    459     /* Load GDT */
    460     0x0F, 0x01, 0x16, 0x80, 0x7F,   /* lgdt [gdt_desc] */
    461 
    462     /* Enter protected mode */
    463     0x0F, 0x20, 0xC0,               /* mov eax, cr0 */
    464     0x0C, 0x01,                     /* or al, 1 */
    465     0x0F, 0x22, 0xC0,               /* mov cr0, eax */
    466 
    467     /* Jump to clear prefetch */
    468     0xEA, 0x60, 0x7F, 0x08, 0x00,   /* jmp 0x08:0x7F60 */
    469 };
    470 
    471 /* Variables stored at 0x8F00 area:
    472  * 0x8FE0: bytes_per_sector (2)
    473  * 0x8FE2: sectors_per_cluster (1)
    474  * 0x8FE4: reserved_sectors (2)
    475  * 0x8FE6: num_fats (1)
    476  * 0x8FE8: root_entries (2)
    477  * 0x8FEA: fat_sectors (2)
    478  * 0x8FEC: root_start (2)
    479  * 0x8FEE: root_sectors (2)
    480  * 0x8FF0: data_start (2)
    481  * 0x8FF2: boot_cluster / kernel_cluster (2)
    482  * 0x8FF4: kernel_size (4)
    483  * 0x8FF8: part_start (4)
    484  * 0x8FFC: drive_number (1)
    485  */
    486 
    487 /* Strings and data at 0x7D00 */
    488 static const uint8_t core_loader_data[] = {
    489     /* 0x7D00: Loading message */
    490     'L', 'u', 'a', 'j', 'i', 't', 'O', 'S', ' ', 'B', 'o', 'o', 't', 'l', 'o', 'a',
    491     'd', 'e', 'r', '\r', '\n', 0,
    492 
    493     /* Padding to 0x7D20 */
    494     0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    495 
    496     /* 0x7D20: err_no_part */
    497     'N', 'o', ' ', 'b', 'o', 'o', 't', ' ', 'p', 'a', 'r', 't', 'i', 't', 'i', 'o',
    498     'n', '\r', '\n', 0,
    499 
    500     /* Padding to 0x7D40 */
    501     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    502 
    503     /* 0x7D40: err_no_boot_dir */
    504     'B', 'O', 'O', 'T', ' ', 'd', 'i', 'r', ' ', 'n', 'o', 't', ' ', 'f', 'o', 'u',
    505     'n', 'd', '\r', '\n', 0,
    506 
    507     /* Padding to 0x7D60 */
    508     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    509 
    510     /* 0x7D60: err_no_kernel */
    511     'K', 'E', 'R', 'N', 'E', 'L', '.', 'B', 'I', 'N', ' ', 'n', 'o', 't', ' ', 'f',
    512     'o', 'u', 'n', 'd', '\r', '\n', 0,
    513 };
    514 
    515 /* Directory/filename patterns at 0x7F00 */
    516 static const uint8_t core_filenames[] = {
    517     /* 0x7F00: "BOOT       " (8.3 format, space padded) */
    518     'B', 'O', 'O', 'T', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 0,
    519 
    520     /* 0x7F10: "KERNEL  BIN" (8.3 format) */
    521     'K', 'E', 'R', 'N', 'E', 'L', ' ', ' ', 'B', 'I', 'N', 0,
    522 };
    523 
    524 /* Protected mode code at 0x7F60 */
    525 static const uint8_t core_pmode[] = {
    526     /* 0x7F60: Now in 32-bit protected mode */
    527     0x66, 0xB8, 0x10, 0x00,         /* mov ax, 0x10 (data segment) */
    528     0x8E, 0xD8,                     /* mov ds, ax */
    529     0x8E, 0xC0,                     /* mov es, ax */
    530     0x8E, 0xD0,                     /* mov ss, ax */
    531 
    532     /* Go back to real mode but keep 4GB segment limits */
    533     0x0F, 0x20, 0xC0,               /* mov eax, cr0 */
    534     0x24, 0xFE,                     /* and al, 0xFE */
    535     0x0F, 0x22, 0xC0,               /* mov cr0, eax */
    536 
    537     /* Far jump back to real mode */
    538     0xEA, 0x78, 0x7F, 0x00, 0x00,   /* jmp 0x0000:0x7F78 */
    539 
    540     /* 0x7F78: Back in real mode with unreal flat segments */
    541     0x31, 0xC0,                     /* xor ax, ax */
    542     0x8E, 0xD8,                     /* mov ds, ax */
    543     0x8E, 0xC0,                     /* mov es, ax */
    544 
    545     /* Now load kernel cluster by cluster to 0x100000 */
    546     /* This is simplified - just load first cluster for now */
    547     /* A full implementation would follow the FAT chain */
    548 
    549     /* Print loading kernel message */
    550     0xBE, 0x90, 0x7F,               /* mov si, loading_kernel_msg */
    551     0xE8, 0x0A, 0xFD,               /* call print_string */
    552 
    553     /* Set up multiboot header at 0xFF000 */
    554     /* Magic */
    555     0x66, 0xB8, 0x02, 0xB0, 0xAD, 0x2B, /* mov eax, 0x2BADB002 */
    556     0x67, 0x66, 0xA3, 0x00, 0xF0, 0x0F, 0x00, /* mov [0x0FF000], eax */
    557 
    558     /* Jump to kernel at 0x100000 */
    559     /* Put multiboot magic in EAX, info pointer in EBX */
    560     0x66, 0xB8, 0x02, 0xB0, 0xAD, 0x2B, /* mov eax, 0x2BADB002 */
    561     0x66, 0xBB, 0x00, 0xF0, 0x0F, 0x00, /* mov ebx, 0x000FF000 */
    562 
    563     /* Far jump to kernel (needs 32-bit protected mode) */
    564     /* Re-enter protected mode */
    565     0x0F, 0x20, 0xC0,               /* mov eax, cr0 */
    566     0x0C, 0x01,                     /* or al, 1 */
    567     0x0F, 0x22, 0xC0,               /* mov cr0, eax */
    568     0x66, 0xEA, 0x00, 0x00, 0x10, 0x00, 0x08, 0x00, /* jmp 0x08:0x00100000 */
    569 
    570     /* 0x7F90: loading_kernel_msg */
    571     'L', 'o', 'a', 'd', 'i', 'n', 'g', ' ', 'k', 'e', 'r', 'n', 'e', 'l', '.', '.',
    572     '.', '\r', '\n', 0,
    573 };
    574 
    575 /* GDT at 0x7F80 */
    576 static const uint8_t core_gdt[] = {
    577     /* 0x7F80: GDT descriptor */
    578     0x17, 0x00,                     /* limit (3 entries * 8 - 1 = 23) */
    579     0x88, 0x7F, 0x00, 0x00,         /* base (0x7F88) */
    580 
    581     /* 0x7F86: padding */
    582     0x00, 0x00,
    583 
    584     /* 0x7F88: GDT entries */
    585     /* Null descriptor */
    586     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    587     /* Code segment (0x08): base=0, limit=4GB, 32-bit, executable */
    588     0xFF, 0xFF, 0x00, 0x00, 0x00, 0x9A, 0xCF, 0x00,
    589     /* Data segment (0x10): base=0, limit=4GB, 32-bit, writable */
    590     0xFF, 0xFF, 0x00, 0x00, 0x00, 0x92, 0xCF, 0x00,
    591 };
    592 
    593 /* ============================================================================
    594  * Assemble the complete core loader image
    595  * ========================================================================= */
    596 
    597 #define CORE_SIZE (62 * 512)  /* 62 sectors = 31KB */
    598 
    599 static void build_core_image(uint8_t *buffer) {
    600     memset(buffer, 0, CORE_SIZE);
    601 
    602     /* Copy main loader code at offset 0 */
    603     memcpy(buffer, core_loader, sizeof(core_loader));
    604 
    605     /* Copy data strings at offset 0x100 (0x7D00 - 0x7C00) */
    606     memcpy(buffer + 0x100, core_loader_data, sizeof(core_loader_data));
    607 
    608     /* Copy continuation code at offset 0x200 (0x7E00 - 0x7C00) */
    609     memcpy(buffer + 0x200, core_loader_cont, sizeof(core_loader_cont));
    610 
    611     /* Copy filenames at offset 0x300 (0x7F00 - 0x7C00) */
    612     memcpy(buffer + 0x300, core_filenames, sizeof(core_filenames));
    613 
    614     /* Copy protected mode code at offset 0x360 (0x7F60 - 0x7C00) */
    615     memcpy(buffer + 0x360, core_pmode, sizeof(core_pmode));
    616 
    617     /* Copy GDT at offset 0x380 (0x7F80 - 0x7C00) */
    618     memcpy(buffer + 0x380, core_gdt, sizeof(core_gdt));
    619 }
    620 
    621 /* ============================================================================
    622  * Public API Implementation
    623  * ========================================================================= */
    624 
    625 int grub_install_mbr(uint8_t bus, uint8_t drive) {
    626     /* Read current MBR to preserve partition table */
    627     mbr_t mbr;
    628     if (ata_read_sectors(bus, drive, 0, 1, &mbr) != ATA_OK) {
    629         terminal_writestring("[GRUB] Error: Failed to read MBR\n");
    630         return GRUB_ERR_IO;
    631     }
    632 
    633     /* Check for valid partition table */
    634     if (mbr.signature != MBR_SIGNATURE) {
    635         terminal_writestring("[GRUB] Error: No valid partition table\n");
    636         return GRUB_ERR_NO_PART;
    637     }
    638 
    639     {
    640         char dbg[80];
    641         snprintf(dbg, sizeof(dbg), "[GRUB] Installing MBR boot code\n");
    642         terminal_writestring(dbg);
    643     }
    644 
    645     /* Copy boot code to MBR (preserving partition table) */
    646     memset(mbr.bootstrap, 0, sizeof(mbr.bootstrap));
    647     memcpy(mbr.bootstrap, mbr_boot_code, sizeof(mbr_boot_code));
    648 
    649     /* Make sure signature is intact */
    650     mbr.signature = MBR_SIGNATURE;
    651 
    652     /* Write MBR back */
    653     if (ata_write_sectors(bus, drive, 0, 1, &mbr) != ATA_OK) {
    654         terminal_writestring("[GRUB] Error: Failed to write MBR\n");
    655         return GRUB_ERR_IO;
    656     }
    657 
    658     terminal_writestring("[GRUB] MBR boot code installed\n");
    659     return GRUB_OK;
    660 }
    661 
    662 int grub_install_core(uint8_t bus, uint8_t drive) {
    663     terminal_writestring("[GRUB] Building core loader image...\n");
    664 
    665     /* Build the core loader image */
    666     uint8_t *core_image = (uint8_t *)0x200000;  /* Use 2MB mark as temp buffer */
    667     build_core_image(core_image);
    668 
    669     {
    670         char dbg[80];
    671         snprintf(dbg, sizeof(dbg), "[GRUB] Writing core loader (62 sectors) to sectors 1-62\n");
    672         terminal_writestring(dbg);
    673     }
    674 
    675     /* Write core loader to sectors 1-62 */
    676     for (int i = 0; i < 62; i++) {
    677         if (ata_write_sectors(bus, drive, 1 + i, 1, core_image + i * 512) != ATA_OK) {
    678             char err[80];
    679             snprintf(err, sizeof(err), "[GRUB] Error: Failed to write sector %d\n", 1 + i);
    680             terminal_writestring(err);
    681             return GRUB_ERR_IO;
    682         }
    683     }
    684 
    685     terminal_writestring("[GRUB] Core loader installed\n");
    686     return GRUB_OK;
    687 }
    688 
    689 int grub_install(uint8_t bus, uint8_t drive) {
    690     terminal_writestring("[GRUB] Starting full bootloader installation...\n");
    691 
    692     /* Verify partition table exists */
    693     if (!partition_has_mbr(bus, drive)) {
    694         terminal_writestring("[GRUB] Error: No partition table found\n");
    695         return GRUB_ERR_NO_PART;
    696     }
    697 
    698     /* Get boot partition info */
    699     partition_info_t parts[4];
    700     if (partition_read_table(bus, drive, parts) != 0) {
    701         terminal_writestring("[GRUB] Error: Failed to read partition table\n");
    702         return GRUB_ERR_IO;
    703     }
    704 
    705     /* Find FAT16 boot partition */
    706     int boot_idx = -1;
    707     for (int i = 0; i < 4; i++) {
    708         if (parts[i].exists && parts[i].type == PART_TYPE_FAT16) {
    709             boot_idx = i;
    710             break;
    711         }
    712     }
    713 
    714     if (boot_idx < 0) {
    715         terminal_writestring("[GRUB] Error: No FAT16 boot partition found\n");
    716         return GRUB_ERR_NO_FAT16;
    717     }
    718 
    719     {
    720         char dbg[80];
    721         snprintf(dbg, sizeof(dbg), "[GRUB] Found FAT16 partition %d at sector %u\n",
    722                  boot_idx + 1, (unsigned)parts[boot_idx].start_lba);
    723         terminal_writestring(dbg);
    724     }
    725 
    726     /* Install MBR boot code */
    727     int result = grub_install_mbr(bus, drive);
    728     if (result != GRUB_OK) {
    729         return result;
    730     }
    731 
    732     /* Install core loader */
    733     result = grub_install_core(bus, drive);
    734     if (result != GRUB_OK) {
    735         return result;
    736     }
    737 
    738     terminal_writestring("[GRUB] Bootloader installation complete!\n");
    739     terminal_writestring("[GRUB] Ensure /BOOT/KERNEL.BIN exists on the FAT16 partition\n");
    740     return GRUB_OK;
    741 }
    742 
    743 int grub_install_vbr(uint8_t bus, uint8_t drive, uint32_t part_start) {
    744     /* VBR is not needed with our approach - the MBR loads the core directly */
    745     (void)bus;
    746     (void)drive;
    747     (void)part_start;
    748     terminal_writestring("[GRUB] VBR installation not needed (core loader handles FAT16)\n");
    749     return GRUB_OK;
    750 }
    751 
    752 const char *grub_error_string(int error) {
    753     switch (error) {
    754         case GRUB_OK:        return "Success";
    755         case GRUB_ERR_IO:    return "I/O error";
    756         case GRUB_ERR_NO_PART: return "No partition table";
    757         case GRUB_ERR_NO_FAT16: return "No FAT16 boot partition";
    758         case GRUB_ERR_INVALID: return "Invalid parameter";
    759         default:             return "Unknown error";
    760     }
    761 }
    762 
    763 /* ============================================================================
    764  * Lua Bindings
    765  * ========================================================================= */
    766 
    767 #include "include/lua.h"
    768 #include "include/lauxlib.h"
    769 
    770 /* grub.install(bus, drive) -> boolean, error_string */
    771 static int lua_grub_install(lua_State *L) {
    772     int bus = luaL_checkinteger(L, 1);
    773     int drive = luaL_checkinteger(L, 2);
    774 
    775     int result = grub_install(bus, drive);
    776 
    777     lua_pushboolean(L, result == GRUB_OK);
    778     if (result != GRUB_OK) {
    779         lua_pushstring(L, grub_error_string(result));
    780     } else {
    781         lua_pushnil(L);
    782     }
    783     return 2;
    784 }
    785 
    786 /* grub.installMbr(bus, drive) -> boolean, error_string */
    787 static int lua_grub_install_mbr(lua_State *L) {
    788     int bus = luaL_checkinteger(L, 1);
    789     int drive = luaL_checkinteger(L, 2);
    790 
    791     int result = grub_install_mbr(bus, drive);
    792 
    793     lua_pushboolean(L, result == GRUB_OK);
    794     if (result != GRUB_OK) {
    795         lua_pushstring(L, grub_error_string(result));
    796     } else {
    797         lua_pushnil(L);
    798     }
    799     return 2;
    800 }
    801 
    802 /* grub.installCore(bus, drive) -> boolean, error_string */
    803 static int lua_grub_install_core(lua_State *L) {
    804     int bus = luaL_checkinteger(L, 1);
    805     int drive = luaL_checkinteger(L, 2);
    806 
    807     int result = grub_install_core(bus, drive);
    808 
    809     lua_pushboolean(L, result == GRUB_OK);
    810     if (result != GRUB_OK) {
    811         lua_pushstring(L, grub_error_string(result));
    812     } else {
    813         lua_pushnil(L);
    814     }
    815     return 2;
    816 }
    817 
    818 static const luaL_Reg grub_funcs[] = {
    819     {"install", lua_grub_install},
    820     {"installMbr", lua_grub_install_mbr},
    821     {"installCore", lua_grub_install_core},
    822     {NULL, NULL}
    823 };
    824 
    825 int luaopen_grub(lua_State *L) {
    826     luaL_newlib(L, grub_funcs);
    827     return 1;
    828 }