# Stage 1 — the actual MBR boot sector. Hard-limited to 510 bytes (+2-byte # 0x55AA signature) by the BIOS, so there's no room here for anything but # "load stage 2 off disk and jump to it". All the VBE/A20/protected-mode/ # splash/kernel-loading work now lives in stage2.S + stage2main.c, which # aren't sector-size-constrained. # # How many sectors of stage2 to pull in. This, the padding the Makefile gives # stage2's binary, and the "+(STAGE2_SECTORS+1)" sector math in stage2main.c's # readseg() all have to agree on where the kernel starts on disk -- so the # Makefile passes -DSTAGE2_SECTORS and is the single authority. The value below # is only a fallback for reading this file standalone. # # It is 48, not the old 24, because stage2 now carries the boot logo # (include/logo.h, 8 KiB). The real ceiling is memory rather than this number: # stage2 loads at 0x7E00 and the multiboot kernel-search buffer starts at # 0x10000, so stage2 cannot exceed 0x8200 bytes = 65 sectors. #ifndef STAGE2_SECTORS #define STAGE2_SECTORS 48 #endif #define STAGE2_ADDR 0x7E00 .code16 .global start start: cli xorw %ax, %ax movw %ax, %ds movw %ax, %es movw %ax, %ss movw $0x7c00, %sp # Read STAGE2_SECTORS sectors, starting at disk sector 1 (LBA), into # memory right after this boot sector (0x7E00). Raw IDE PIO (LBA28) — # same register convention as stage2main.c's own readsect(), just # inlined here since we can't call C code yet. Port numbers 0x1F2-0x1F7 # are all > 0xFF, so they must go through %dx — `out $imm8,%al` only # encodes an 8-bit port and silently truncates anything larger. # "You were not booted from a CD." stage2main.c reads this byte to choose # between ATA and ATAPI when it loads the kernel, and nothing else clears # it -- so a warm reboot from a disc could otherwise leave the flag set and # send a perfectly ordinary hard-disk boot looking for an optical drive. # Placed one byte past the verbose flag; both sit clear of the 256-byte VBE # mode info block that stage2.S is about to write at 0x30000. movw $0x3000, %ax movw %ax, %es movw $0x101, %di movb $0, %al movb %al, %es:(%di) xorw %ax, %ax movw %ax, %es movw $STAGE2_ADDR, %di # ES:DI destination, ES=0 movw $1, %si # first LBA sector to read movb $STAGE2_SECTORS, %bl .Lsector: .Lwait1: movw $0x1F7, %dx inb %dx, %al andb $0xC0, %al cmpb $0x40, %al jne .Lwait1 movw $0x1F2, %dx movb $1, %al outb %al, %dx # sector count = 1 incw %dx # 0x1F3 movw %si, %ax outb %al, %dx # LBA [7:0] incw %dx # 0x1F4 xorb %al, %al outb %al, %dx # LBA [15:8] incw %dx # 0x1F5 outb %al, %dx # LBA [23:16] incw %dx # 0x1F6 movb $0xE0, %al outb %al, %dx # drive 0, LBA mode incw %dx # 0x1F7 movb $0x20, %al outb %al, %dx # READ SECTORS command .Lwait2: inb %dx, %al # %dx still 0x1F7 andb $0xC0, %al cmpb $0x40, %al jne .Lwait2 movw $256, %cx # 256 words = 512 bytes movw $0x1F0, %dx rep insw # reads into ES:DI, DI += 512 automatically incw %si decb %bl jnz .Lsector ljmp $0, $STAGE2_ADDR