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 }