; bootp.asm Bootstrap for protected mode image ; Version 1.0, Dec 14, 1997 ; Sample code ; by John S. Fine johnfine@erols.com ; I do not place any restrictions on your use of this source code ; I do not provide any warranty of the correctness of this source code ;_____________________________________________________________________________ ; ; This program should be useable for three purposes: ; ; 1) Use it unmodified, in the boot sector of a floppy, to load a test image ; from the hard drive. In many environments, a floppy boot is the easiest ; way to have a clean boot for a pmode test. By getting the image itself ; from the hard drive, you eliminate most of the delays and restrictions of ; a floppy boot. ; ; 2) As a bootstrap tutorial, it demonstrates many of the steps required by ; a protected mode bootstrap, and many tricks for keeping code size down ; in a bootstrap. ; ; 3) As the basis for a protected mode, partition level, bootstrap. Just ; remove some of the steps that aren't needed in your environment, and add ; some error messages for unsupported conditions. ;_____________________________________________________________________________ ; ; Documentation: ; ; 1) Most of the documentation is in comments at the END of this file ; 2) Some of the documentation is in a separate file: BOOTP.TXT ; 3) In the comments within the source code are several uses of {}, each ; of these indicates a section of documentation at the end of this file ; which applies to that part of the source code. ; 4) The major way I reduced code size was to rely on left-over values in ; registers and/or to initialize registers at points where doing so was ; cheap. To make that understandable, there are several lines beginning ; with either ";>" or ";>>". These lines describe the register contents ; at that point in the code. ";>" indicates that the value is required ; by the next section of code. ";>>" indicates that the value passes ; through the next section unmodified and is needed later. ;_____________________________________________________________________________ %include "gdt.inc" struc DISK_PARM ;BIOS hard disk parameters DP_cyl resw 1 DP_heads resb 1 resb 5 ;Obsolete DP_ctrl resb 1 resb 5 ;Obsolete DP_sect resb 1 resb 1 endstruc struc PT_ENT ;Partition table entry BootFlag resb 1 BeginHead resb 1 BeginSector resb 1 BeginCyl resb 1 SystemID resb 1 EndHead resb 1 EndSector resb 1 EndCyl resb 1 RelSectorLow resw 1 RelSectorHigh resw 1 NumSectorsLow resw 1 NumSectorsHigh resw 1 endstruc struc BB ;Partition Boot block resb 0xD ;Things we ignore BB_clu resb 1 ;Sectors per cluster BB_res resw 1 ;Reserved sectors BB_fats resb 1 ;Number of FATs BB_root resw 1 ;Root directory entries resb 3 ;Things we ignore BB_fat resw 1 ;Sectors per fat endstruc max_xfer equ 0x78 ;Maximum number of sectors to transfer at once ;Must be divisible by 8 SEGMENT START USE16 jmp short start ;Standard start of boot sector nop resb 0x3B ;Skip over parameters (set by format) start: cli lgdt [cs:gdt] mov al, 0x80 mov es, ax xor eax, eax ;Segment and LBN mov ds, ax ;{1B} mov ss, ax mov sp, start xor edi, edi inc di call read_sectors ;> eax = 0 ;> edi = 1 ;> ds = 0 ;> es = 80 mov si, 0x9BE-PT_ENT_size ;Point at partition table .10: add si, byte PT_ENT_size cmp [si+BootFlag], al ;Active? je .10 ;No jg boot_error mov eax, [si+RelSectorLow] ;LBN sector number of partition call read_sectors ;Read partition boot block ;> eax = LBN of partition ;> ebx = 0000???? ;> edi = 0000???? ;> ds = 0 mov bp, 0x800 ;Point at partition boot block mov bx, [bp+BB_res] ;Sectors before first FAT add eax, ebx ;LBN of FAT push eax ;Need to read the FAT later movzx bx, [bp+BB_fats] ;Number of FATs imul bx, [bp+BB_fat] ;Times size of FAT add eax, ebx ;LBN of Root directory mov di, [bp+BB_root] ;Root directory entries push di ; used again later dec di ;Convert to number of sectors shr di, 4 ; 16 directory entries per sector inc di mov es, bp ;Read directory at 800:0 call read_sectors add eax, edi ;LBN of cluster 2 ;>> [sp+2] = LBN of FAT ;> [sp] = Number of root directory entries ;>> eax = LBN of cluster 2 ;> edi = 0000???? ;>> bp = 800 ;> ds = 0 ;> es = 800 pop bx ;Root directory entries xor di, di ;Point at directory {1C} .20: mov si, file_name ;Name of file we want xor ecx, ecx mov cl, 11 a32 rep cmpsb ;Found the file? je found ;Yes add cl, 21 ;Offset to next directory entry add edi, ecx ;Advance to next entry dec bx ;Loop through all entries jnz .20 ;Couldn't find file in directory boot_error: disk_error: mov ax, 0xE07 ;{3} int 10h jmp short $ ;> [sp] = LBN of FAT ;> eax = LBN of cluster 2 ;> es:edi = Pointer to end of name within directory entry ;> bp = 800 ;> ds = 0 ;> es = 800 eof: mov bh, 0x9C mov es, bx ;es = 9C00 xor di, di ;{1E} Address of page tables WRT es mov bh, 4096/256 ;ebx = 4096 .10: mov ch, 4 ;cx = 1024 mov al, 7 .20: stosd add eax, ebx int 8 ;{8} loop .20 shr eax, 2 ;{4C} (first time only) 4Mb / 4 = 1Mb shr si, 1 ;Done just one page? jo .10 ;Yes cli ;{6} mov eax, 0x9C007 ;First page tbl pointer in page dir stosd ;{1H} mov ax, (1024-3)*2 xchg ax, cx rep stosw mov ax, 0xD007 ;0FF800000 page tbl pointer stosd ;{1F} mov ah, 0xE0 ;Page directory self pointer stosd ;{1G} mov al, 0 mov CR3, eax ;Set up page directory mov eax, CR0 ;Turn on paging and protected mode or eax, 0x80000001 mov CR0, eax mov cl, flat_data ;Setup ds and es push cx ;{5} pop ds mov es, cx jmp dword 8:0xFF800000 ;Go read_sectors: ; Input: ; EAX = LBN ; DI = sector count ; ES = segment ; Output: ; BL = low byte of ES ; EBX high half cleared ; DL = 0x80 ; EDX high half cleared ; ESI = 0 ; Clobbered: ; BH, CX, DH push eax push di push es .10: push eax ;LBN push ds lds si, [0x104] ;Hard drive 0 parameters cdq ;edx = 0 movzx ebx, byte [si+DP_sect] div ebx ;EAX=track ;EDX=sector-1 mov cx, dx ;CL=sector-1 ;CH=0 inc cx ;CL=Sector number xor dx, dx mov bl, [si+DP_heads] pop ds div ebx mov dh, dl mov dl, 0x80 xchg ch, al shr ax, 2 or cl, al mov al, max_xfer ;AX = Maximum sectors to xfer cmp ax, di ;Total is larger? jb .20 ;Yes: transfer maximum xchg ax, di ;No: AX=total ;DI=maximum .20: mov ah, 2 ;Read xor bx, bx int 13h jc near disk_error mov bx, es add bh, max_xfer>>3 ;Advance segment mov es, bx pop eax add eax, byte max_xfer sub di, byte max_xfer ja .10 pop es pop di pop eax xor esi, esi ret gdt start_gdt flat_code desc 0, 0xFFBFF, D_CODE+D_READ+D_BIG+D_BIG_LIM flat_data desc 0, 0xFFFFF, D_DATA+D_WRITE+D_BIG+D_BIG_LIM end_gdt resb 0x1FE+$$-$ db 0x55, 0xAA