1128707Sru/* 2128707Sru * Copyright (c) 1998 Robert Nordier 3128707Sru * All rights reserved. 4128707Sru * 5128707Sru * Redistribution and use in source and binary forms are freely 6128707Sru * permitted provided that the above copyright notice and this 7128707Sru * paragraph and the following disclaimer are duplicated in all 8128707Sru * such forms. 9128707Sru * 10128707Sru * This software is provided "AS IS" and without any express or 11128707Sru * implied warranties, including, without limitation, the implied 12128707Sru * warranties of merchantability and fitness for a particular 13128707Sru * purpose. 14128707Sru * 15128707Sru * $FreeBSD: stable/11/stand/i386/boot2/boot1.S 232623 2012-03-06 20:45:13Z jhb $ 16128707Sru */ 1740269Srnordier 18128707Sru/* Memory Locations */ 19129240Sru .set MEM_REL,0x700 # Relocation address 20129240Sru .set MEM_ARG,0x900 # Arguments 21129240Sru .set MEM_ORG,0x7c00 # Origin 22189500Smarcel .set MEM_BUF,0x8c00 # Load area 23129240Sru .set MEM_BTX,0x9000 # BTX start 24129240Sru .set MEM_JMP,0x9010 # BTX entry point 25129240Sru .set MEM_USR,0xa000 # Client start 26129240Sru .set BDA_BOOT,0x472 # Boot howto flag 2761836Sjhb 28128707Sru/* Partition Constants */ 29129240Sru .set PRT_OFF,0x1be # Partition offset 30129240Sru .set PRT_NUM,0x4 # Partitions 31129240Sru .set PRT_BSD,0xa5 # Partition type 3240269Srnordier 33128707Sru/* Flag Bits */ 34129240Sru .set FL_PACKET,0x80 # Packet mode 3561836Sjhb 36128707Sru/* Misc. Constants */ 37129240Sru .set SIZ_PAG,0x1000 # Page size 38129240Sru .set SIZ_SEC,0x200 # Sector size 3940269Srnordier 40104635Sphk .set NSECT,0x10 4140269Srnordier .globl start 4240269Srnordier .globl xread 4360821Sjhb .code16 4440269Srnordier 45129240Srustart: jmp main # Start recognizably 4640269Srnordier 47128707Sru/* 48128707Sru * This is the start of a standard BIOS Parameter Block (BPB). Most bootable 49128707Sru * FAT disks have this at the start of their MBR. While normal BIOS's will 50128707Sru * work fine without this section, IBM's El Torito emulation "fixes" up the 51128707Sru * BPB by writing into the memory copy of the MBR. Rather than have data 52128707Sru * written into our xread routine, we'll define a BPB to work around it. 53128707Sru * The data marked with (T) indicates a field required for a ThinkPad to 54128707Sru * recognize the disk and (W) indicates fields written from IBM BIOS code. 55128707Sru * The use of the BPB is based on what OpenBSD and NetBSD implemented in 56128707Sru * their boot code but the required fields were determined by trial and error. 57128707Sru * 58128707Sru * Note: If additional space is needed in boot1, one solution would be to 59128707Sru * move the "prompt" message data (below) to replace the OEM ID. 60128707Sru */ 6180751Sjhb .org 0x03, 0x00 62129240Sruoemid: .space 0x08, 0x00 # OEM ID 6380751Sjhb 6480751Sjhb .org 0x0b, 0x00 65129240Srubpb: .word 512 # sector size (T) 66129240Sru .byte 0 # sectors/clustor 67129240Sru .word 0 # reserved sectors 68129240Sru .byte 0 # number of FATs 69129240Sru .word 0 # root entries 70129240Sru .word 0 # small sectors 71129240Sru .byte 0 # media type (W) 72129240Sru .word 0 # sectors/fat 73129240Sru .word 18 # sectors per track (T) 74129240Sru .word 2 # number of heads (T) 75129240Sru .long 0 # hidden sectors (W) 76129240Sru .long 0 # large sectors 7780751Sjhb 7880751Sjhb .org 0x24, 0x00 79129240Sruebpb: .byte 0 # BIOS physical drive number (W) 8080751Sjhb 8180751Sjhb .org 0x25,0x90 82128707Sru/* 83128707Sru * Trampoline used by boot2 to call read to read data from the disk via 84128707Sru * the BIOS. Call with: 85128707Sru * 86128707Sru * %cx:%ax - long - LBA to read in 87128707Sru * %es:(%bx) - caddr_t - buffer to read data into 88128707Sru * %dl - byte - drive to read from 89128707Sru * %dh - byte - num sectors to read 90128707Sru */ 9140269Srnordier 92129240Sruxread: push %ss # Address 93129240Sru pop %ds # data 94128707Sru/* 95128707Sru * Setup an EDD disk packet and pass it to read 96128707Sru */ 97129240Sruxread.1: # Starting 98129240Sru pushl $0x0 # absolute 99129240Sru push %cx # block 100129240Sru push %ax # number 101129240Sru push %es # Address of 102129240Sru push %bx # transfer buffer 103129240Sru xor %ax,%ax # Number of 104129240Sru movb %dh,%al # blocks to 105129240Sru push %ax # transfer 106129240Sru push $0x10 # Size of packet 107129240Sru mov %sp,%bp # Packet pointer 108129240Sru callw read # Read from disk 109129240Sru lea 0x10(%bp),%sp # Clear stack 110129240Sru lret # To far caller 111128707Sru/* 112128707Sru * Load the rest of boot2 and BTX up, copy the parts to the right locations, 113128707Sru * and start it all up. 114128707Sru */ 11540269Srnordier 116128707Sru/* 117128707Sru * Setup the segment registers to flat addressing (segment 0) and setup the 118128707Sru * stack to end just below the start of our code. 119128707Sru */ 120129240Srumain: cld # String ops inc 121129240Sru xor %cx,%cx # Zero 122129240Sru mov %cx,%es # Address 123129240Sru mov %cx,%ds # data 124129240Sru mov %cx,%ss # Set up 125129240Sru mov $start,%sp # stack 126128707Sru/* 127128707Sru * Relocate ourself to MEM_REL. Since %cx == 0, the inc %ch sets 128232623Sjhb * %cx == 0x100. Note that boot1 does not use this relocated copy 129232623Sjhb * of itself while loading boot2; however, BTX reclaims the memory 130232623Sjhb * used by boot1 during its initialization. As a result, boot2 uses 131232623Sjhb * xread from the relocated copy. 132128707Sru */ 133129240Sru mov %sp,%si # Source 134129240Sru mov $MEM_REL,%di # Destination 135129240Sru incb %ch # Word count 136129240Sru rep # Copy 137129240Sru movsw # code 138128707Sru/* 139128707Sru * If we are on a hard drive, then load the MBR and look for the first 140128707Sru * FreeBSD slice. We use the fake partition entry below that points to 141128707Sru * the MBR when we call nread. The first pass looks for the first active 142128707Sru * FreeBSD slice. The second pass looks for the first non-active FreeBSD 143128707Sru * slice if the first one fails. 144128707Sru */ 145129240Sru mov $part4,%si # Partition 146129240Sru cmpb $0x80,%dl # Hard drive? 147129240Sru jb main.4 # No 148129240Sru movb $0x1,%dh # Block count 149129240Sru callw nread # Read MBR 150129240Sru mov $0x1,%cx # Two passes 151129240Srumain.1: mov $MEM_BUF+PRT_OFF,%si # Partition table 152129240Sru movb $0x1,%dh # Partition 153129240Srumain.2: cmpb $PRT_BSD,0x4(%si) # Our partition type? 154129240Sru jne main.3 # No 155129240Sru jcxz main.5 # If second pass 156129240Sru testb $0x80,(%si) # Active? 157129240Sru jnz main.5 # Yes 158129240Srumain.3: add $0x10,%si # Next entry 159129240Sru incb %dh # Partition 160129240Sru cmpb $0x1+PRT_NUM,%dh # In table? 161129240Sru jb main.2 # Yes 162129240Sru dec %cx # Do two 163129240Sru jcxz main.1 # passes 164128707Sru/* 165128707Sru * If we get here, we didn't find any FreeBSD slices at all, so print an 166128707Sru * error message and die. 167128707Sru */ 168129240Sru mov $msg_part,%si # Message 169129240Sru jmp error # Error 170128707Sru/* 171128707Sru * Floppies use partition 0 of drive 0. 172128707Sru */ 173129240Srumain.4: xor %dx,%dx # Partition:drive 174128707Sru/* 175128707Sru * Ok, we have a slice and drive in %dx now, so use that to locate and load 176128707Sru * boot2. %si references the start of the slice we are looking for, so go 177128707Sru * ahead and load up the first 16 sectors (boot1 + boot2) from that. When 178189500Smarcel * we read it in, we conveniently use 0x8c00 as our transfer buffer. Thus, 179189500Smarcel * boot1 ends up at 0x8c00, and boot2 starts at 0x8c00 + 0x200 = 0x8e00. 180189500Smarcel * The first part of boot2 is the disklabel, which is 0x200 bytes long. 181128707Sru * The second part is BTX, which is thus loaded into 0x9000, which is where 182128707Sru * it also runs from. The boot2.bin binary starts right after the end of 183128707Sru * BTX, so we have to figure out where the start of it is and then move the 184128707Sru * binary to 0xc000. Normally, BTX clients start at MEM_USR, or 0xa000, but 185128707Sru * when we use btxld to create boot2, we use an entry point of 0x2000. That 186128707Sru * entry point is relative to MEM_USR; thus boot2.bin starts at 0xc000. 187128707Sru */ 188129240Srumain.5: mov %dx,MEM_ARG # Save args 189129240Sru movb $NSECT,%dh # Sector count 190129240Sru callw nread # Read disk 191129240Sru mov $MEM_BTX,%bx # BTX 192129240Sru mov 0xa(%bx),%si # Get BTX length and set 193129240Sru add %bx,%si # %si to start of boot2.bin 194129240Sru mov $MEM_USR+SIZ_PAG*2,%di # Client page 2 195129240Sru mov $MEM_BTX+(NSECT-1)*SIZ_SEC,%cx # Byte 196129240Sru sub %si,%cx # count 197129240Sru rep # Relocate 198129240Sru movsb # client 199129993Sphk 200128707Sru/* 201128707Sru * Enable A20 so we can access memory above 1 meg. 202129993Sphk * Use the zero-valued %cx as a timeout for embedded hardware which do not 203129993Sphk * have a keyboard controller. 204128707Sru */ 205129240Sruseta20: cli # Disable interrupts 206129993Sphkseta20.1: dec %cx # Timeout? 207129993Sphk jz seta20.3 # Yes 208129993Sphk inb $0x64,%al # Get status 209129240Sru testb $0x2,%al # Busy? 210129240Sru jnz seta20.1 # Yes 211129240Sru movb $0xd1,%al # Command: Write 212129240Sru outb %al,$0x64 # output port 213129240Sruseta20.2: inb $0x64,%al # Get status 214129240Sru testb $0x2,%al # Busy? 215129240Sru jnz seta20.2 # Yes 216129240Sru movb $0xdf,%al # Enable 217129240Sru outb %al,$0x60 # A20 218157667Sjhbseta20.3: sti # Enable interrupts 219129993Sphk 220129993Sphk jmp start+MEM_JMP-MEM_ORG # Start BTX 221129993Sphk 222129993Sphk 223128707Sru/* 224128707Sru * Trampoline used to call read from within boot1. 225128707Sru */ 226129240Srunread: mov $MEM_BUF,%bx # Transfer buffer 227129240Sru mov 0x8(%si),%ax # Get 228129240Sru mov 0xa(%si),%cx # LBA 229129240Sru push %cs # Read from 230129240Sru callw xread.1 # disk 231129240Sru jnc return # If success, return 232129240Sru mov $msg_read,%si # Otherwise, set the error 233129240Sru # message and fall through to 234129240Sru # the error routine 235128707Sru/* 236128707Sru * Print out the error message pointed to by %ds:(%si) followed 237128707Sru * by a prompt, wait for a keypress, and then reboot the machine. 238128707Sru */ 239129240Sruerror: callw putstr # Display message 240129240Sru mov $prompt,%si # Display 241129240Sru callw putstr # prompt 242129240Sru xorb %ah,%ah # BIOS: Get 243129240Sru int $0x16 # keypress 244129240Sru movw $0x1234, BDA_BOOT # Do a warm boot 245215285Sbrucec ljmp $0xf000,$0xfff0 # reboot the machine 246128707Sru/* 247128707Sru * Display a null-terminated string using the BIOS output. 248128707Sru */ 249129240Sruputstr.0: mov $0x7,%bx # Page:attribute 250129240Sru movb $0xe,%ah # BIOS: Display 251129240Sru int $0x10 # character 252129240Sruputstr: lodsb # Get char 253129240Sru testb %al,%al # End of string? 254129240Sru jne putstr.0 # No 25540269Srnordier 256128707Sru/* 257128707Sru * Overused return code. ereturn is used to return an error from the 258128707Sru * read function. Since we assume putstr succeeds, we (ab)use the 259128707Sru * same code when we return from putstr. 260128707Sru */ 261129240Sruereturn: movb $0x1,%ah # Invalid 262129240Sru stc # argument 263129240Srureturn: retw # To caller 264128707Sru/* 265128707Sru * Reads sectors from the disk. If EDD is enabled, then check if it is 266128707Sru * installed and use it if it is. If it is not installed or not enabled, then 267128707Sru * fall back to using CHS. Since we use a LBA, if we are using CHS, we have to 268128707Sru * fetch the drive parameters from the BIOS and divide it out ourselves. 269128707Sru * Call with: 270128707Sru * 271128707Sru * %dl - byte - drive number 272128707Sru * stack - 10 bytes - EDD Packet 273128707Sru */ 274134430Syarread: testb $FL_PACKET,%cs:MEM_REL+flags-start # LBA support enabled? 275134430Syar jz read.1 # No, use CHS 276134430Syar cmpb $0x80,%dl # Hard drive? 277134430Syar jb read.1 # No, use CHS 278134430Syar mov $0x55aa,%bx # Magic 279134430Syar push %dx # Save 280134430Syar movb $0x41,%ah # BIOS: Check 281134430Syar int $0x13 # extensions present 282134430Syar pop %dx # Restore 283134430Syar jc read.1 # If error, use CHS 284134430Syar cmp $0xaa55,%bx # Magic? 285134430Syar jne read.1 # No, so use CHS 286134430Syar testb $0x1,%cl # Packet interface? 287134430Syar jz read.1 # No, so use CHS 288134430Syar mov %bp,%si # Disk packet 289134430Syar movb $0x42,%ah # BIOS: Extended 290134430Syar int $0x13 # read 291134430Syar retw # To caller 292134430Syarread.1: push %dx # Save 293129240Sru movb $0x8,%ah # BIOS: Get drive 294129240Sru int $0x13 # parameters 295129240Sru movb %dh,%ch # Max head number 296129240Sru pop %dx # Restore 297129240Sru jc return # If error 298129240Sru andb $0x3f,%cl # Sectors per track 299129240Sru jz ereturn # If zero 300129240Sru cli # Disable interrupts 301129240Sru mov 0x8(%bp),%eax # Get LBA 302129240Sru push %dx # Save 303129240Sru movzbl %cl,%ebx # Divide by 304129240Sru xor %edx,%edx # sectors 305129240Sru div %ebx # per track 306129240Sru movb %ch,%bl # Max head number 307129240Sru movb %dl,%ch # Sector number 308129240Sru inc %bx # Divide by 309129240Sru xorb %dl,%dl # number 310129240Sru div %ebx # of heads 311129240Sru movb %dl,%bh # Head number 312129240Sru pop %dx # Restore 313129240Sru cmpl $0x3ff,%eax # Cylinder number supportable? 314129240Sru sti # Enable interrupts 315134430Syar ja ereturn # No, return an error 316129240Sru xchgb %al,%ah # Set up cylinder 317129240Sru rorb $0x2,%al # number 318129240Sru orb %ch,%al # Merge 319129240Sru inc %ax # sector 320129240Sru xchg %ax,%cx # number 321129240Sru movb %bh,%dh # Head number 322129240Sru subb %ah,%al # Sectors this track 323129240Sru mov 0x2(%bp),%ah # Blocks to read 324129240Sru cmpb %ah,%al # To read 325129240Sru jb read.2 # this 326119253Simp#ifdef TRACK_AT_A_TIME 327129240Sru movb %ah,%al # track 328119253Simp#else 329129240Sru movb $1,%al # one sector 330119253Simp#endif 331129240Sruread.2: mov $0x5,%di # Try count 332129240Sruread.3: les 0x4(%bp),%bx # Transfer buffer 333129240Sru push %ax # Save 334129240Sru movb $0x2,%ah # BIOS: Read 335129240Sru int $0x13 # from disk 336129240Sru pop %bx # Restore 337129240Sru jnc read.4 # If success 338129240Sru dec %di # Retry? 339129240Sru jz read.6 # No 340129240Sru xorb %ah,%ah # BIOS: Reset 341129240Sru int $0x13 # disk system 342129240Sru xchg %bx,%ax # Block count 343129240Sru jmp read.3 # Continue 344129240Sruread.4: movzbw %bl,%ax # Sectors read 345129240Sru add %ax,0x8(%bp) # Adjust 346129240Sru jnc read.5 # LBA, 347129240Sru incw 0xa(%bp) # transfer 348129240Sruread.5: shlb %bl # buffer 349129240Sru add %bl,0x5(%bp) # pointer, 350129240Sru sub %al,0x2(%bp) # block count 351134430Syar ja read.1 # If not done 352129240Sruread.6: retw # To caller 35340269Srnordier 354128707Sru/* Messages */ 35540269Srnordier 35641008Srnordiermsg_read: .asciz "Read" 35741008Srnordiermsg_part: .asciz "Boot" 35840269Srnordier 35941085Srnordierprompt: .asciz " error\r\n" 36040940Srnordier 361129240Sruflags: .byte FLAGS # Flags 36248919Srnordier 36340269Srnordier .org PRT_OFF,0x90 36440269Srnordier 365128707Sru/* Partition table */ 36640269Srnordier 36740269Srnordier .fill 0x30,0x1,0x0 36840269Srnordierpart4: .byte 0x80, 0x00, 0x01, 0x00 36985805Speter .byte 0xa5, 0xfe, 0xff, 0xff 37040269Srnordier .byte 0x00, 0x00, 0x00, 0x00 371129240Sru .byte 0x50, 0xc3, 0x00, 0x00 # 50000 sectors long, bleh 37240269Srnordier 373129240Sru .word 0xaa55 # Magic number 374