;; This is a PC/AT style bootstrap program for sector #0 of discs partitioned ;; with the EFI partition table. It is the PC/AT style bootstrap program ;; that is written to EFI partitioned discs by the NEWMBR command within the ;; DASDPART utility from the TAU system utilities. ;; ;; Unlike older bootstrap programs, such as the one supplied by the NEWMBR ;; command from the OS/2 Command Line Utilities, this program uses INT 13h ;; extensions unconditionally, without checking for their presence. ;; ;; Copyright 2011 Jonathan de Boyne Pollard. "Moral" rights asserted. ;; ;; Permission is hereby granted to copy, modify, and redistribute this code ;; as long as the author is not held responsible for any consequences of its ;; possession or use, as long as this permission notice is retained, and as ;; long as you agree that it comes with no guarantee. ASSUME CS:EFI_BOOT_TEXT, DS:EFI_BOOT_TEXT ;; ;; This code must not be combined with any other segment. It must be ;; located at offset 0100H. Combining would break that. ;; EFI_BOOT_TEXT_GROUP GROUP EFI_BOOT_TEXT ;; ;; The structure of a partition table entry ;; EFITableHeader STRUC EFITableHeaderSig dq ? EFITableHeaderRevision dd ? EFITableHeaderHeaderSize dd ? EFITableHeaderHeaderCRC32 dd ? dd ? EFITableHeaderMyLBA dq ? EFITableHeaderAltLBA dq ? EFITableHeaderFirstLBA dq ? EFITableHeaderLastLBA dq ? EFITableHeaderDiscGUID db 16 dup (?) EFITableHeaderTableLBA dq ? EFITableHeaderEntries dd ? EFITableHeaderEntrySize dd ? EFITableHeaderTableCRC32 dd ? EFITableHeader ENDS EFITableEntry STRUC EFITableEntryTypeGUID db 16 dup (?) EFITableEntryPartitionGUID db 16 dup (?) EFITableEntryStartLBA dq ? EFITableEntryEndLBA dq ? EFITableEntryAttributes dq ? EFITableEntry ENDS EFI_BOOT_TEXT SEGMENT PUBLIC BYTE USE16 'CODE' ;; ************************************************************************ ;; ;; Uninitialized variables ;; ;; ************************************************************************ ;; ;; These must come before the code. WASM crashes, otherwise. Since they ;; are uninitialized, we can put them in an area that is logically outside ;; of the boot sector, giving us a few precious extra bytes to play with ;; within the boot sector. None of these variables are used after the ;; VBR is given control. ;; BSS: ;; ;; An LBA control block ;; LBAPacketSize dw ? LBABlocks dw ? LBABufferOff dw ? LBABufferSeg dw ? LBABlockLo dd ? LBABlockHi dd ? LBAEnd EQU $ DriveNumber db 00h PNPOff dw 0000h PNPSeg dw 0000h ;; ;; This, and subsequent, padding are not required to have any particular ;; values. We choose this so that there is contrast between the actual ;; code/data and the padding in a hexadecimal viewer. ;; db 0100h - ($ - BSS) DUP (0a5h) ORG 0100h ;; ;; The stack resides in the area immediately preceding the boot sector. ;; StackTop EQU $ ;; ************************************************************************ ;; ;; Start of the MBR bootstrap program ;; ;; ************************************************************************ ;; ;; Entry : ;; DL = Firmware drive number ;; ES:DI = Pointer to firmware's '$PNP' Plug-and-Play record ;; CS:IP = 0000:7C00 ;; FS = 0000 ;; Uses : All. ;; Exit : Loads and jumps to a VBR ;; Errors: Terminates by calling INT 18h if anything goes wrong. ;; PUBLIC BeginEFIMasterBootRecord BeginEFIMasterBootRecord PROC ;; ************************************************************************ ;; ;; Start of the main boot code ;; ;; ************************************************************************ start: mov ax, 07B0h mov ds, ax mov [ds:DriveNumber], dl mov [ds:PNPOff], di mov [ds:PNPSeg], es mov ax, 07D0h mov es, ax ;; The stack is immediately below the OLD copy of the program at 0x07B0:0x0100 cli mov ax, ds mov ss, ax mov sp, StackTop sti ;; _fmemcpy(0x07D0:0x0100, 0x07B0:0x0100, 0x0200) ; cld mov di, 0100h mov si, di mov cx, 0200h/4 rep movsd ;; Jump to the following label in the copy just made push es push offset start2 retf start2: ;; Read the EFI partition table header to 0x0800:0x0000 push 0x0800 pop es xor eax, eax mov di, ax ;; ES:DI = 0x0800:0x0000 mov si, ax ;; ES:SI = 0x0800:0x0000 mov [ds:LBABlockHi], eax inc eax mov [ds:LBABlockLo], eax mov cx, ax ;; CX = 0x0001 call ReadSectors ;; Load the EFI partition table proper. mov ax, word ptr [es:si].EFITableHeaderEntries mul word ptr [es:si].EFITableHeaderEntrySize shr ax, 9 shl dx, 7 mov cx, ax or cx, dx ;; CX = entries * entry size / 512 mov eax, dword ptr [es:si].EFITableHeaderTableLBA+0 mov [ds:LBABlockLo], eax mov eax, dword ptr [es:si].EFITableHeaderTableLBA+4 mov [ds:LBABlockHi], eax mov di, 0x0200 ;; ES:DI = 0x0800:0x0200 mov si, di ;; ES:SI = 0x0800:0x0200 call ReadSectors ;; Scan for an active partition. mov di, 0x0000 ;; ES:DI = 0x0800:0x0000 mov cx, word ptr [es:di].EFITableHeaderEntries mov bx, word ptr [es:di].EFITableHeaderEntrySize CheckTableEntry: mov eax, dword ptr [es:si].EFITableEntryTypeGUID or eax, dword ptr [es:si].EFITableEntryTypeGUID+4 or eax, dword ptr [es:si].EFITableEntryTypeGUID+8 or eax, dword ptr [es:si].EFITableEntryTypeGUID+12 jz UnusedEntry mov ax, word ptr [es:si].EFITableEntryAttributes test ax, 0x0004 ;; defined by the UEFI specification 2.3.1 jnz FoundActivePartition UnusedEntry: add si, bx ;; ES:SI += sizeof entry loop CheckTableEntry mov si, offset NoActivePartitionMsg jmp short DisplayErrorAndReboot FoundActivePartition: ;; Load and execute the chosen VBR at 0x07B0:0x0100 mov eax, dword ptr [es:si].EFITableEntryStartLBA+0 mov [ds:LBABlockLo], eax mov eax, dword ptr [es:si].EFITableEntryStartLBA+4 mov [ds:LBABlockHi], eax mov cx, 1 push 0x07B0 pop es mov di, 0x0100 call ReadSectors mov ax, word ptr [es:di + 0x1FE] cmp ax, 0xAA55 jz IsBootSector mov si, offset NotBootSectorMsg jmp short DisplayErrorAndReboot IsBootSector: mov dl, [ds:DriveNumber] push es push di les di, [ds:PNPOff] retf BeginEFIMasterBootRecord ENDP ;; ************************************************************************ ;; ;; Read blocks into a given buffer (LBA version) ;; ;; ************************************************************************ ;; Entry: ;; ES:DI = Buffer address ;; CX = sector count ;; Modifies: AX, BX, CX, DX ReadSectors PROC push es push si push di mov [ds:LBABufferOff], di ;; Buffer address mov [ds:LBABufferSeg], es ;; """""""""""""" mov [ds:LBABlocks], cx ;; Block count mov bx, LBAEnd - LBAPacketSize mov [ds:LBAPacketSize], bx ;; Packet size mov si, offset LBAPacketSize mov ah, 42h mov dl, [ds:DriveNumber] int 13h pop di pop si pop es jc ReadSectorError ret ReadSectors ENDP ;; ************************************************************************ ;; ;; Failure at any stage prints a message and reboots. ;; ;; ************************************************************************ ReadSectorError: mov si, offset ReadErrorMsg DisplayErrorAndReboot: call DisplayMessage mov si, offset RebootPrompt call DisplayMessage Reboot: mov ah, 10h int 16h ;; read keyboard int 18h ;; Next boot device, or ROM BASIC int 19h ;; Just in case it returns DisplayMessageLoop PROC call DisplayChar DisplayMessage: lodsb or al, al jnz DisplayMessageLoop ret DisplayMessageLoop ENDP ;Dot: mov al, 0x2e DisplayChar PROC pusha mov ah, 0Eh mov bx, 0007h int 10h popa ret DisplayChar ENDP ;; ************************************************************************ ;; ;; The messages ;; ;; ************************************************************************ ;; These are the standard IBM OS/2 messages. NoActivePartitionMsg db 'SYS1462',0 ReadErrorMsg db 'SYS1463',0 NotBootSectorMsg db 'SYS1464',0 RebootPrompt db ', press a key!',0Dh,0Ah,0 ;; ************************************************************************ ;; ;; Compatibility data structures ;; ;; ************************************************************************ ;; ;; Note that we don't pre-load the dummy old-style partition table with an ;; 0EEh entry. This is the part of the MBR template that is not used. ;; Programs must substitute for this part of the template either what was ;; already in sector #0 or some entries that they calculate themselves. ;; db (018Ah - ($ - BeginEFIMasterBootRecord)) DUP (0A5h) ;; A dummy old style partition table ORG 028Ah DummyOldStyleBootManagerNames: db 00 db 8 dup (' ') db 00 db 8 dup (' ') db 00 db 8 dup (' ') db 00 db 8 dup (' ') db (01B8h - ($ - BeginEFIMasterBootRecord)) DUP (0A5h) ;; EFI 32-bit disc signature ORG 02B8h EFISignature: dd 0 db (01BEh - ($ - BeginEFIMasterBootRecord)) DUP (0A5h) ;; A dummy old style partition table ORG 02BEh DummyOldStylePartitionTable: db 7Fh db 0Fh dup (0FFh) db 7Fh db 0Fh dup (0FFh) db 7Fh db 0Fh dup (0FFh) db 7Fh db 0Fh dup (0FFh) ;; ************************************************************************ ;; ;; Boot record signature for the firmware ;; ;; ************************************************************************ db (01FEh - ($ - BeginEFIMasterBootRecord)) DUP (0A5h) ORG 02FEh Signature dw 0AA55h PUBLIC EndEFIMasterBootRecord EndEFIMasterBootRecord LABEL BYTE EFI_BOOT_TEXT ENDS END