boot1.S revision 215285
11590Srgrimes/* 21590Srgrimes * Copyright (c) 1998 Robert Nordier 31590Srgrimes * All rights reserved. 41590Srgrimes * 51590Srgrimes * Redistribution and use in source and binary forms are freely 61590Srgrimes * permitted provided that the above copyright notice and this 71590Srgrimes * paragraph and the following disclaimer are duplicated in all 81590Srgrimes * such forms. 91590Srgrimes * 101590Srgrimes * This software is provided "AS IS" and without any express or 111590Srgrimes * implied warranties, including, without limitation, the implied 121590Srgrimes * warranties of merchantability and fitness for a particular 131590Srgrimes * purpose. 141590Srgrimes * 151590Srgrimes * $FreeBSD: head/sys/boot/i386/boot2/boot1.S 215285 2010-11-14 08:31:02Z brucec $ 161590Srgrimes */ 171590Srgrimes 181590Srgrimes/* Memory Locations */ 191590Srgrimes .set MEM_REL,0x700 # Relocation address 201590Srgrimes .set MEM_ARG,0x900 # Arguments 211590Srgrimes .set MEM_ORG,0x7c00 # Origin 221590Srgrimes .set MEM_BUF,0x8c00 # Load area 231590Srgrimes .set MEM_BTX,0x9000 # BTX start 241590Srgrimes .set MEM_JMP,0x9010 # BTX entry point 251590Srgrimes .set MEM_USR,0xa000 # Client start 261590Srgrimes .set BDA_BOOT,0x472 # Boot howto flag 271590Srgrimes 281590Srgrimes/* Partition Constants */ 291590Srgrimes .set PRT_OFF,0x1be # Partition offset 301590Srgrimes .set PRT_NUM,0x4 # Partitions 311590Srgrimes .set PRT_BSD,0xa5 # Partition type 321590Srgrimes 331590Srgrimes/* Flag Bits */ 341590Srgrimes .set FL_PACKET,0x80 # Packet mode 351590Srgrimes 361590Srgrimes/* Misc. Constants */ 371590Srgrimes .set SIZ_PAG,0x1000 # Page size 381590Srgrimes .set SIZ_SEC,0x200 # Sector size 391590Srgrimes 401590Srgrimes .set NSECT,0x10 411590Srgrimes .globl start 421590Srgrimes .globl xread 431590Srgrimes .code16 441590Srgrimes 451590Srgrimesstart: jmp main # Start recognizably 461590Srgrimes 471590Srgrimes/* 481590Srgrimes * This is the start of a standard BIOS Parameter Block (BPB). Most bootable 491590Srgrimes * FAT disks have this at the start of their MBR. While normal BIOS's will 501590Srgrimes * work fine without this section, IBM's El Torito emulation "fixes" up the 511590Srgrimes * BPB by writing into the memory copy of the MBR. Rather than have data 521590Srgrimes * written into our xread routine, we'll define a BPB to work around it. 531590Srgrimes * The data marked with (T) indicates a field required for a ThinkPad to 541590Srgrimes * recognize the disk and (W) indicates fields written from IBM BIOS code. 551590Srgrimes * The use of the BPB is based on what OpenBSD and NetBSD implemented in 561590Srgrimes * their boot code but the required fields were determined by trial and error. 571590Srgrimes * 581590Srgrimes * Note: If additional space is needed in boot1, one solution would be to 591590Srgrimes * move the "prompt" message data (below) to replace the OEM ID. 601590Srgrimes */ 611590Srgrimes .org 0x03, 0x00 621590Srgrimesoemid: .space 0x08, 0x00 # OEM ID 631590Srgrimes 641590Srgrimes .org 0x0b, 0x00 651590Srgrimesbpb: .word 512 # sector size (T) 661590Srgrimes .byte 0 # sectors/clustor 671590Srgrimes .word 0 # reserved sectors 681590Srgrimes .byte 0 # number of FATs 691590Srgrimes .word 0 # root entries 701590Srgrimes .word 0 # small sectors 711590Srgrimes .byte 0 # media type (W) 721590Srgrimes .word 0 # sectors/fat 731590Srgrimes .word 18 # sectors per track (T) 741590Srgrimes .word 2 # number of heads (T) 751590Srgrimes .long 0 # hidden sectors (W) 766988Sdg .long 0 # large sectors 771590Srgrimes 781590Srgrimes .org 0x24, 0x00 791590Srgrimesebpb: .byte 0 # BIOS physical drive number (W) 801590Srgrimes 811590Srgrimes .org 0x25,0x90 821590Srgrimes/* 831590Srgrimes * Trampoline used by boot2 to call read to read data from the disk via 841590Srgrimes * the BIOS. Call with: 851590Srgrimes * 861590Srgrimes * %cx:%ax - long - LBA to read in 871590Srgrimes * %es:(%bx) - caddr_t - buffer to read data into 881590Srgrimes * %dl - byte - drive to read from 891590Srgrimes * %dh - byte - num sectors to read 901590Srgrimes */ 911590Srgrimes 921590Srgrimesxread: push %ss # Address 931590Srgrimes pop %ds # data 941590Srgrimes/* 951590Srgrimes * Setup an EDD disk packet and pass it to read 961590Srgrimes */ 971590Srgrimesxread.1: # Starting 981590Srgrimes pushl $0x0 # absolute 991590Srgrimes push %cx # block 1001590Srgrimes push %ax # number 1011590Srgrimes push %es # Address of 1021590Srgrimes push %bx # transfer buffer 1031590Srgrimes xor %ax,%ax # Number of 1041590Srgrimes movb %dh,%al # blocks to 1051590Srgrimes push %ax # transfer 1061590Srgrimes push $0x10 # Size of packet 1071590Srgrimes mov %sp,%bp # Packet pointer 1081590Srgrimes callw read # Read from disk 1091590Srgrimes lea 0x10(%bp),%sp # Clear stack 1101590Srgrimes lret # To far caller 1111590Srgrimes/* 1121590Srgrimes * Load the rest of boot2 and BTX up, copy the parts to the right locations, 1131590Srgrimes * and start it all up. 1141590Srgrimes */ 1151590Srgrimes 1161590Srgrimes/* 1171590Srgrimes * Setup the segment registers to flat addressing (segment 0) and setup the 1181590Srgrimes * stack to end just below the start of our code. 1191590Srgrimes */ 1201590Srgrimesmain: cld # String ops inc 1211590Srgrimes xor %cx,%cx # Zero 1221590Srgrimes mov %cx,%es # Address 1231590Srgrimes mov %cx,%ds # data 1241590Srgrimes mov %cx,%ss # Set up 1251590Srgrimes mov $start,%sp # stack 1261590Srgrimes/* 1271590Srgrimes * Relocate ourself to MEM_REL. Since %cx == 0, the inc %ch sets 1281590Srgrimes * %cx == 0x100. 1291590Srgrimes */ 1301590Srgrimes mov %sp,%si # Source 1311590Srgrimes mov $MEM_REL,%di # Destination 1321590Srgrimes incb %ch # Word count 1331590Srgrimes rep # Copy 1341590Srgrimes movsw # code 1351590Srgrimes/* 1361590Srgrimes * If we are on a hard drive, then load the MBR and look for the first 1371590Srgrimes * FreeBSD slice. We use the fake partition entry below that points to 1381590Srgrimes * the MBR when we call nread. The first pass looks for the first active 1391590Srgrimes * FreeBSD slice. The second pass looks for the first non-active FreeBSD 1401590Srgrimes * slice if the first one fails. 1411590Srgrimes */ 1421590Srgrimes mov $part4,%si # Partition 1431590Srgrimes cmpb $0x80,%dl # Hard drive? 1441590Srgrimes jb main.4 # No 1451590Srgrimes movb $0x1,%dh # Block count 1461590Srgrimes callw nread # Read MBR 1471590Srgrimes mov $0x1,%cx # Two passes 1481590Srgrimesmain.1: mov $MEM_BUF+PRT_OFF,%si # Partition table 1491590Srgrimes movb $0x1,%dh # Partition 1501590Srgrimesmain.2: cmpb $PRT_BSD,0x4(%si) # Our partition type? 1511590Srgrimes jne main.3 # No 1521590Srgrimes jcxz main.5 # If second pass 1531590Srgrimes testb $0x80,(%si) # Active? 1541590Srgrimes jnz main.5 # Yes 1551590Srgrimesmain.3: add $0x10,%si # Next entry 1561590Srgrimes incb %dh # Partition 1571590Srgrimes cmpb $0x1+PRT_NUM,%dh # In table? 1586988Sdg jb main.2 # Yes 1596988Sdg dec %cx # Do two 1601590Srgrimes jcxz main.1 # passes 1611590Srgrimes/* 1621590Srgrimes * If we get here, we didn't find any FreeBSD slices at all, so print an 1631590Srgrimes * error message and die. 1641590Srgrimes */ 1651590Srgrimes mov $msg_part,%si # Message 1661590Srgrimes jmp error # Error 1671590Srgrimes/* 1681590Srgrimes * Floppies use partition 0 of drive 0. 1691590Srgrimes */ 1701590Srgrimesmain.4: xor %dx,%dx # Partition:drive 1711590Srgrimes/* 1721590Srgrimes * Ok, we have a slice and drive in %dx now, so use that to locate and load 1737455Sdg * boot2. %si references the start of the slice we are looking for, so go 1741590Srgrimes * ahead and load up the first 16 sectors (boot1 + boot2) from that. When 1751590Srgrimes * we read it in, we conveniently use 0x8c00 as our transfer buffer. Thus, 1761590Srgrimes * boot1 ends up at 0x8c00, and boot2 starts at 0x8c00 + 0x200 = 0x8e00. 1771590Srgrimes * The first part of boot2 is the disklabel, which is 0x200 bytes long. 1784808Sdg * The second part is BTX, which is thus loaded into 0x9000, which is where 1791590Srgrimes * it also runs from. The boot2.bin binary starts right after the end of 1801590Srgrimes * BTX, so we have to figure out where the start of it is and then move the 1811590Srgrimes * binary to 0xc000. Normally, BTX clients start at MEM_USR, or 0xa000, but 1821590Srgrimes * when we use btxld to create boot2, we use an entry point of 0x2000. That 1831590Srgrimes * entry point is relative to MEM_USR; thus boot2.bin starts at 0xc000. 1841590Srgrimes */ 1851590Srgrimesmain.5: mov %dx,MEM_ARG # Save args 1861590Srgrimes movb $NSECT,%dh # Sector count 1871590Srgrimes callw nread # Read disk 1881590Srgrimes mov $MEM_BTX,%bx # BTX 1891590Srgrimes mov 0xa(%bx),%si # Get BTX length and set 1901590Srgrimes add %bx,%si # %si to start of boot2.bin 1911590Srgrimes mov $MEM_USR+SIZ_PAG*2,%di # Client page 2 1921590Srgrimes mov $MEM_BTX+(NSECT-1)*SIZ_SEC,%cx # Byte 1931590Srgrimes sub %si,%cx # count 1941590Srgrimes rep # Relocate 1951590Srgrimes movsb # client 1961590Srgrimes 1971590Srgrimes/* 1981590Srgrimes * Enable A20 so we can access memory above 1 meg. 1991590Srgrimes * Use the zero-valued %cx as a timeout for embedded hardware which do not 2001590Srgrimes * have a keyboard controller. 2011590Srgrimes */ 2021590Srgrimesseta20: cli # Disable interrupts 2031590Srgrimesseta20.1: dec %cx # Timeout? 2041590Srgrimes jz seta20.3 # Yes 2051590Srgrimes inb $0x64,%al # Get status 2061590Srgrimes testb $0x2,%al # Busy? 2071590Srgrimes jnz seta20.1 # Yes 2081590Srgrimes movb $0xd1,%al # Command: Write 2091590Srgrimes outb %al,$0x64 # output port 2101590Srgrimesseta20.2: inb $0x64,%al # Get status 2111590Srgrimes testb $0x2,%al # Busy? 2121590Srgrimes jnz seta20.2 # Yes 2131590Srgrimes movb $0xdf,%al # Enable 2141590Srgrimes outb %al,$0x60 # A20 2151590Srgrimesseta20.3: sti # Enable interrupts 2161590Srgrimes 2171590Srgrimes jmp start+MEM_JMP-MEM_ORG # Start BTX 2181590Srgrimes 2191590Srgrimes 2201590Srgrimes/* 2211590Srgrimes * Trampoline used to call read from within boot1. 2221590Srgrimes */ 2231590Srgrimesnread: mov $MEM_BUF,%bx # Transfer buffer 2241590Srgrimes mov 0x8(%si),%ax # Get 2251590Srgrimes mov 0xa(%si),%cx # LBA 2261590Srgrimes push %cs # Read from 2271590Srgrimes callw xread.1 # disk 2281590Srgrimes jnc return # If success, return 2291590Srgrimes mov $msg_read,%si # Otherwise, set the error 2301590Srgrimes # message and fall through to 2311590Srgrimes # the error routine 2321590Srgrimes/* 2331590Srgrimes * Print out the error message pointed to by %ds:(%si) followed 2341590Srgrimes * by a prompt, wait for a keypress, and then reboot the machine. 2351590Srgrimes */ 2361590Srgrimeserror: callw putstr # Display message 2371590Srgrimes mov $prompt,%si # Display 2381590Srgrimes callw putstr # prompt 2391590Srgrimes xorb %ah,%ah # BIOS: Get 2401590Srgrimes int $0x16 # keypress 2411590Srgrimes movw $0x1234, BDA_BOOT # Do a warm boot 2421590Srgrimes ljmp $0xf000,$0xfff0 # reboot the machine 2431590Srgrimes/* 2441590Srgrimes * Display a null-terminated string using the BIOS output. 2451590Srgrimes */ 2461590Srgrimesputstr.0: mov $0x7,%bx # Page:attribute 2471590Srgrimes movb $0xe,%ah # BIOS: Display 2481590Srgrimes int $0x10 # character 2491590Srgrimesputstr: lodsb # Get char 2501590Srgrimes testb %al,%al # End of string? 2511590Srgrimes jne putstr.0 # No 2521590Srgrimes 2531590Srgrimes/* 2541590Srgrimes * Overused return code. ereturn is used to return an error from the 2551590Srgrimes * read function. Since we assume putstr succeeds, we (ab)use the 2561590Srgrimes * same code when we return from putstr. 2571590Srgrimes */ 2581590Srgrimesereturn: movb $0x1,%ah # Invalid 2591590Srgrimes stc # argument 2601590Srgrimesreturn: retw # To caller 2611590Srgrimes/* 2621590Srgrimes * Reads sectors from the disk. If EDD is enabled, then check if it is 2631590Srgrimes * installed and use it if it is. If it is not installed or not enabled, then 2641590Srgrimes * fall back to using CHS. Since we use a LBA, if we are using CHS, we have to 2651590Srgrimes * fetch the drive parameters from the BIOS and divide it out ourselves. 2661590Srgrimes * Call with: 2671590Srgrimes * 2681590Srgrimes * %dl - byte - drive number 2691590Srgrimes * stack - 10 bytes - EDD Packet 2701590Srgrimes */ 2711590Srgrimesread: testb $FL_PACKET,%cs:MEM_REL+flags-start # LBA support enabled? 2721590Srgrimes jz read.1 # No, use CHS 2731590Srgrimes cmpb $0x80,%dl # Hard drive? 2741590Srgrimes jb read.1 # No, use CHS 2751590Srgrimes mov $0x55aa,%bx # Magic 2761590Srgrimes push %dx # Save 2771590Srgrimes movb $0x41,%ah # BIOS: Check 2781590Srgrimes int $0x13 # extensions present 2791590Srgrimes pop %dx # Restore 2801590Srgrimes jc read.1 # If error, use CHS 2811590Srgrimes cmp $0xaa55,%bx # Magic? 2821590Srgrimes jne read.1 # No, so use CHS 2831590Srgrimes testb $0x1,%cl # Packet interface? 2841590Srgrimes jz read.1 # No, so use CHS 2851590Srgrimes mov %bp,%si # Disk packet 2861590Srgrimes movb $0x42,%ah # BIOS: Extended 2873614Sdg int $0x13 # read 2883614Sdg retw # To caller 2891590Srgrimesread.1: push %dx # Save 2901590Srgrimes movb $0x8,%ah # BIOS: Get drive 2911590Srgrimes int $0x13 # parameters 2921590Srgrimes movb %dh,%ch # Max head number 2931590Srgrimes pop %dx # Restore 2941590Srgrimes jc return # If error 2951590Srgrimes andb $0x3f,%cl # Sectors per track 2967348Sdg jz ereturn # If zero 2977351Sdg cli # Disable interrupts 2987351Sdg mov 0x8(%bp),%eax # Get LBA 2997351Sdg push %dx # Save 3007351Sdg movzbl %cl,%ebx # Divide by 3017351Sdg xor %edx,%edx # sectors 3027351Sdg div %ebx # per track 3037351Sdg movb %ch,%bl # Max head number 3047351Sdg movb %dl,%ch # Sector number 3057351Sdg inc %bx # Divide by 3067351Sdg xorb %dl,%dl # number 3077351Sdg div %ebx # of heads 3087351Sdg movb %dl,%bh # Head number 3091590Srgrimes pop %dx # Restore 3101590Srgrimes cmpl $0x3ff,%eax # Cylinder number supportable? 3111590Srgrimes sti # Enable interrupts 3121590Srgrimes ja ereturn # No, return an error 3134930Sbde xchgb %al,%ah # Set up cylinder 3141590Srgrimes rorb $0x2,%al # number 3151590Srgrimes orb %ch,%al # Merge 3161590Srgrimes inc %ax # sector 3171590Srgrimes xchg %ax,%cx # number 3187012Sphk movb %bh,%dh # Head number 3191590Srgrimes subb %ah,%al # Sectors this track 3201590Srgrimes mov 0x2(%bp),%ah # Blocks to read 3211590Srgrimes cmpb %ah,%al # To read 3221590Srgrimes jb read.2 # this 3231590Srgrimes#ifdef TRACK_AT_A_TIME 3241590Srgrimes movb %ah,%al # track 3251590Srgrimes#else 3261590Srgrimes movb $1,%al # one sector 3271590Srgrimes#endif 3281590Srgrimesread.2: mov $0x5,%di # Try count 3291590Srgrimesread.3: les 0x4(%bp),%bx # Transfer buffer 3301590Srgrimes push %ax # Save 3311590Srgrimes movb $0x2,%ah # BIOS: Read 3321590Srgrimes int $0x13 # from disk 3331590Srgrimes pop %bx # Restore 3341590Srgrimes jnc read.4 # If success 3351590Srgrimes dec %di # Retry? 3367455Sdg jz read.6 # No 3371590Srgrimes xorb %ah,%ah # BIOS: Reset 3381590Srgrimes int $0x13 # disk system 3391590Srgrimes xchg %bx,%ax # Block count 3401590Srgrimes jmp read.3 # Continue 3411590Srgrimesread.4: movzbw %bl,%ax # Sectors read 3421590Srgrimes add %ax,0x8(%bp) # Adjust 3431590Srgrimes jnc read.5 # LBA, 3441590Srgrimes incw 0xa(%bp) # transfer 3451590Srgrimesread.5: shlb %bl # buffer 3461590Srgrimes add %bl,0x5(%bp) # pointer, 3471590Srgrimes sub %al,0x2(%bp) # block count 3481590Srgrimes ja read.1 # If not done 3494930Sbderead.6: retw # To caller 3504930Sbde 3514930Sbde/* Messages */ 3521590Srgrimes 3531590Srgrimesmsg_read: .asciz "Read" 3541590Srgrimesmsg_part: .asciz "Boot" 3551590Srgrimes 3561590Srgrimesprompt: .asciz " error\r\n" 3571590Srgrimes 3581590Srgrimesflags: .byte FLAGS # Flags 3591590Srgrimes 3601590Srgrimes .org PRT_OFF,0x90 3611590Srgrimes 3621590Srgrimes/* Partition table */ 3631590Srgrimes 3641590Srgrimes .fill 0x30,0x1,0x0 3651590Srgrimespart4: .byte 0x80, 0x00, 0x01, 0x00 3661590Srgrimes .byte 0xa5, 0xfe, 0xff, 0xff 3671590Srgrimes .byte 0x00, 0x00, 0x00, 0x00 3681590Srgrimes .byte 0x50, 0xc3, 0x00, 0x00 # 50000 sectors long, bleh 3691590Srgrimes 3701590Srgrimes .word 0xaa55 # Magic number 3711590Srgrimes