SchOS/boot/bios/bootasm.S
Nexoniarz 08a1a9033a boot: separate BIOS from UEFI
boot/bios/ now holds the MBR, the El Torito stub and the real-mode stage
2; boot/uefi/ is empty and documented.

Separate directories rather than #ifdefs, because the two share almost
nothing. A BIOS loader lives in 16-bit real mode, reaches the firmware
through software interrupts, and walks itself up into long mode. A UEFI
loader is a 64-bit PE that the firmware has already placed in long mode
with paging on, calling services through a table of function pointers.
What they do share is the hand-off contract, which boot/README.md now
writes down: ELF segments placed, long mode, a 24-byte-entry memory map
at E820_MAP_ADDR, framebuffer geometry at VBE_INFO_ADDR.

That contract is the point of documenting it now. UEFI's GetMemoryMap
returns EFI_MEMORY_DESCRIPTORs, and the right move is to translate them
into the entries kernel/arch/x86_64/e820.c already parses rather than
teach the kernel a second format.

Build output paths under $(BUILD)/boot are unchanged; only sources moved.
2026-08-09 16:00:03 +02:00

101 lines
3.4 KiB
ArmAsm

# 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