zfsldr.S revision 328866
1185029Spjd/* 2185029Spjd * Copyright (c) 1998 Robert Nordier 3185029Spjd * All rights reserved. 4185029Spjd * 5185029Spjd * Redistribution and use in source and binary forms are freely 6185029Spjd * permitted provided that the above copyright notice and this 7185029Spjd * paragraph and the following disclaimer are duplicated in all 8185029Spjd * such forms. 9185029Spjd * 10185029Spjd * This software is provided "AS IS" and without any express or 11185029Spjd * implied warranties, including, without limitation, the implied 12185029Spjd * warranties of merchantability and fitness for a particular 13185029Spjd * purpose. 14185029Spjd * 15185029Spjd * $FreeBSD: stable/11/sys/boot/i386/zfsboot/zfsldr.S 328866 2018-02-05 04:00:59Z kevans $ 16185029Spjd */ 17185029Spjd 18185029Spjd/* Memory Locations */ 19185029Spjd .set MEM_ARG,0x900 # Arguments 20185029Spjd .set MEM_ORG,0x7c00 # Origin 21185029Spjd .set MEM_BUF,0x8000 # Load area 22185029Spjd .set MEM_BTX,0x9000 # BTX start 23185029Spjd .set MEM_JMP,0x9010 # BTX entry point 24185029Spjd .set MEM_USR,0xa000 # Client start 25185029Spjd .set BDA_BOOT,0x472 # Boot howto flag 26185029Spjd 27185029Spjd/* Partition Constants */ 28185029Spjd .set PRT_OFF,0x1be # Partition offset 29185029Spjd .set PRT_NUM,0x4 # Partitions 30185029Spjd .set PRT_BSD,0xa5 # Partition type 31185029Spjd 32185029Spjd/* Misc. Constants */ 33185029Spjd .set SIZ_PAG,0x1000 # Page size 34185029Spjd .set SIZ_SEC,0x200 # Sector size 35328866Skevans .set COPY_BLKS,0x8 # Number of blocks 36328866Skevans # to copy for boot2 37328866Skevans .set COPY_BLK_SZ,0x8000 # Copy in 32k blocks; must be 38328866Skevans # a multiple of 16 bytes 39328866Skevans .set NSECT,(COPY_BLK_SZ / SIZ_SEC * COPY_BLKS) 40223611Sjhb .globl start 41185029Spjd .code16 42185029Spjd 43185029Spjd/* 44221177Sjhb * Load the rest of zfsboot2 and BTX up, copy the parts to the right locations, 45185029Spjd * and start it all up. 46185029Spjd */ 47185029Spjd 48185029Spjd/* 49185029Spjd * Setup the segment registers to flat addressing (segment 0) and setup the 50185029Spjd * stack to end just below the start of our code. 51185029Spjd */ 52223611Sjhbstart: cld # String ops inc 53185029Spjd xor %cx,%cx # Zero 54185029Spjd mov %cx,%es # Address 55185029Spjd mov %cx,%ds # data 56185029Spjd mov %cx,%ss # Set up 57185029Spjd mov $start,%sp # stack 58185029Spjd/* 59223597Sjhb * Load the MBR and look for the first FreeBSD slice. We use the fake 60223597Sjhb * partition entry below that points to the MBR when we call read. 61223597Sjhb * The first pass looks for the first active FreeBSD slice. The 62223597Sjhb * second pass looks for the first non-active FreeBSD slice if the 63223597Sjhb * first one fails. 64185029Spjd */ 65223597Sjhb call check_edd # Make sure EDD works 66223477Sjhb mov $part4,%si # Dummy partition 67223477Sjhb xor %eax,%eax # Read MBR 68223477Sjhb movl $MEM_BUF,%ebx # from first 69223597Sjhb call read # sector 70185029Spjd mov $0x1,%cx # Two passes 71185029Spjdmain.1: mov $MEM_BUF+PRT_OFF,%si # Partition table 72185029Spjd movb $0x1,%dh # Partition 73185029Spjdmain.2: cmpb $PRT_BSD,0x4(%si) # Our partition type? 74185029Spjd jne main.3 # No 75185029Spjd jcxz main.5 # If second pass 76185029Spjd testb $0x80,(%si) # Active? 77185029Spjd jnz main.5 # Yes 78185029Spjdmain.3: add $0x10,%si # Next entry 79185029Spjd incb %dh # Partition 80185029Spjd cmpb $0x1+PRT_NUM,%dh # In table? 81185029Spjd jb main.2 # Yes 82185029Spjd dec %cx # Do two 83185029Spjd jcxz main.1 # passes 84185029Spjd/* 85185029Spjd * If we get here, we didn't find any FreeBSD slices at all, so print an 86185029Spjd * error message and die. 87185029Spjd */ 88185029Spjd mov $msg_part,%si # Message 89185029Spjd jmp error # Error 90185029Spjd 91185029Spjd/* 92185029Spjd * Ok, we have a slice and drive in %dx now, so use that to locate and 93185029Spjd * load boot2. %si references the start of the slice we are looking 94328866Skevans * for, so go ahead and load up the COPY_BLKS*COPY_BLK_SZ/SIZ_SEC sectors 95328866Skevans * starting at sector 1024 (i.e. after the two vdev labels). We don't 96328866Skevans * have do anything fancy here to allow for an extra copy of boot1 and 97328866Skevans * a partition table (compare to this section of the UFS bootstrap) so we 98328866Skevans * just load it all at 0x9000. The first part of boot2 is BTX, which wants 99328866Skevans * to run at 0x9000. The boot2.bin binary starts right after the end of BTX, 100185029Spjd * so we have to figure out where the start of it is and then move the 101223477Sjhb * binary to 0xc000. Normally, BTX clients start at MEM_USR, or 0xa000, 102223477Sjhb * but when we use btxld to create zfsboot2, we use an entry point of 103223477Sjhb * 0x2000. That entry point is relative to MEM_USR; thus boot2.bin 104223477Sjhb * starts at 0xc000. 105185029Spjd * 106185029Spjd * The load area and the target area for the client overlap so we have 107185029Spjd * to use a decrementing string move. We also play segment register 108185029Spjd * games with the destination address for the move so that the client 109185029Spjd * can be larger than 16k (which would overflow the zero segment since 110223477Sjhb * the client starts at 0xc000). 111185029Spjd */ 112185029Spjdmain.5: mov %dx,MEM_ARG # Save args 113223477Sjhb mov $NSECT,%cx # Sector count 114199579Sjhb movl $1024,%eax # Offset to boot2 115223477Sjhb mov $MEM_BTX,%ebx # Destination buffer 116223477Sjhbmain.6: pushal # Save params 117223597Sjhb call read # Read disk 118223477Sjhb popal # Restore 119223477Sjhb incl %eax # Advance to 120223477Sjhb add $SIZ_SEC,%ebx # next sector 121223477Sjhb loop main.6 # If not last, read another 122328866Skevans 123328866Skevans mov $MEM_BTX,%bx # BTX 124328866Skevans mov 0xa(%bx),%si # Get BTX length and set 125328866Skevans add %bx,%si # %si to start of boot2 126328866Skevans dec %si # Set %ds:%si to point at the 127328866Skevans mov %si,%ax # last byte we want to copy 128328866Skevans shr $4,%ax # from boot2, with %si made as 129328866Skevans add $(COPY_BLKS*COPY_BLK_SZ/16),%ax # small as possible. 130328866Skevans and $0xf,%si # 131328866Skevans mov %ax,%ds # 132328866Skevans mov $(MEM_USR+2*SIZ_PAG)/16,%ax # Set %es:(-1) to point at 133328866Skevans add $(COPY_BLKS*COPY_BLK_SZ/16),%ax # the last byte we 134328866Skevans mov %ax,%es # want to copy boot2 into. 135328866Skevans mov $COPY_BLKS,%bx # Copy COPY_BLKS 32k blocks 136328866Skevanscopyloop: 137328866Skevans add $COPY_BLK_SZ,%si # Adjust %ds:%si to point at 138328866Skevans mov %ds,%ax # the end of the next 32k to 139328866Skevans sub $COPY_BLK_SZ/16,%ax # copy from boot2 140328866Skevans mov %ax,%ds 141328866Skevans mov $COPY_BLK_SZ-1,%di # Adjust %es:%di to point at 142328866Skevans mov %es,%ax # the end of the next 32k into 143328866Skevans sub $COPY_BLK_SZ/16,%ax # which we want boot2 copied 144328866Skevans mov %ax,%es 145328866Skevans mov $COPY_BLK_SZ,%cx # Copy 32k 146328866Skevans std 147328866Skevans rep movsb 148328866Skevans dec %bx 149328866Skevans jnz copyloop 150328866Skevans mov %cx,%ds # Reset %ds and %es 151328866Skevans mov %cx,%es 152223477Sjhb cld # Back to increment 153185029Spjd 154185029Spjd/* 155185029Spjd * Enable A20 so we can access memory above 1 meg. 156185029Spjd * Use the zero-valued %cx as a timeout for embedded hardware which do not 157185029Spjd * have a keyboard controller. 158185029Spjd */ 159185029Spjdseta20: cli # Disable interrupts 160185029Spjdseta20.1: dec %cx # Timeout? 161185029Spjd jz seta20.3 # Yes 162185029Spjd inb $0x64,%al # Get status 163185029Spjd testb $0x2,%al # Busy? 164185029Spjd jnz seta20.1 # Yes 165185029Spjd movb $0xd1,%al # Command: Write 166185029Spjd outb %al,$0x64 # output port 167185029Spjdseta20.2: inb $0x64,%al # Get status 168185029Spjd testb $0x2,%al # Busy? 169185029Spjd jnz seta20.2 # Yes 170185029Spjd movb $0xdf,%al # Enable 171185029Spjd outb %al,$0x60 # A20 172185029Spjdseta20.3: sti # Enable interrupts 173185029Spjd 174185029Spjd jmp start+MEM_JMP-MEM_ORG # Start BTX 175185029Spjd 176185029Spjd 177185029Spjd/* 178223597Sjhb * Read a sector from the disk. Sets up an EDD packet on the stack 179223597Sjhb * and passes it to read. We assume that the destination address is 180223597Sjhb * always segment-aligned. 181221177Sjhb * 182221177Sjhb * %eax - int - LBA to read in relative to partition start 183223477Sjhb * %ebx - ptr - destination address 184221177Sjhb * %dl - byte - drive to read from 185221177Sjhb * %si - ptr - MBR partition entry 186185029Spjd */ 187223597Sjhbread: xor %ecx,%ecx # Get 188199579Sjhb addl 0x8(%si),%eax # LBA 189199579Sjhb adc $0,%ecx 190221177Sjhb pushl %ecx # Starting absolute block 191221177Sjhb pushl %eax # block number 192223477Sjhb shr $4,%ebx # Convert to segment 193223477Sjhb push %bx # Address of 194223477Sjhb push $0 # transfer buffer 195223477Sjhb push $0x1 # Read 1 sector 196221177Sjhb push $0x10 # Size of packet 197223597Sjhb mov %sp,%si # Packet pointer 198223597Sjhb mov $0x42,%ah # BIOS: Extended 199223597Sjhb int $0x13 # read 200223597Sjhb jc read.1 # If error, fail 201223597Sjhb lea 0x10(%si),%sp # Clear stack 202223477Sjhb ret # If success, return 203223597Sjhbread.1: mov %ah,%al # Format 204223477Sjhb mov $read_err,%di # error 205223477Sjhb call hex8 # code 206223477Sjhb mov $msg_read,%si # Set the error message and 207223477Sjhb # fall through to the error 208223477Sjhb # routine 209185029Spjd/* 210185029Spjd * Print out the error message pointed to by %ds:(%si) followed 211185029Spjd * by a prompt, wait for a keypress, and then reboot the machine. 212185029Spjd */ 213185029Spjderror: callw putstr # Display message 214185029Spjd mov $prompt,%si # Display 215185029Spjd callw putstr # prompt 216185029Spjd xorb %ah,%ah # BIOS: Get 217185029Spjd int $0x16 # keypress 218185029Spjd movw $0x1234, BDA_BOOT # Do a warm boot 219185029Spjd ljmp $0xffff,$0x0 # reboot the machine 220185029Spjd/* 221185029Spjd * Display a null-terminated string using the BIOS output. 222185029Spjd */ 223185029Spjdputstr.0: mov $0x7,%bx # Page:attribute 224185029Spjd movb $0xe,%ah # BIOS: Display 225185029Spjd int $0x10 # character 226185029Spjdputstr: lodsb # Get char 227185029Spjd testb %al,%al # End of string? 228185029Spjd jne putstr.0 # No 229223597Sjhb ret # To caller 230185029Spjd/* 231223597Sjhb * Check to see if the disk supports EDD. zfsboot requires EDD and does not 232223597Sjhb * support older C/H/S disk I/O. 233185029Spjd */ 234223597Sjhbcheck_edd: cmpb $0x80,%dl # Hard drive? 235223597Sjhb jb check_edd.1 # No, fail to boot 236185029Spjd mov $0x55aa,%bx # Magic 237185029Spjd push %dx # Save 238185029Spjd movb $0x41,%ah # BIOS: Check 239185029Spjd int $0x13 # extensions present 240185029Spjd pop %dx # Restore 241223597Sjhb jc check_edd.1 # If error, fail 242185029Spjd cmp $0xaa55,%bx # Magic? 243223597Sjhb jne check_edd.1 # No, so fail 244185029Spjd testb $0x1,%cl # Packet interface? 245223597Sjhb jz check_edd.1 # No, so fail 246223597Sjhb ret # EDD ok, keep booting 247223597Sjhbcheck_edd.1: mov $msg_chs,%si # Warn that CHS is 248223597Sjhb jmp error # unsupported and fail 249223477Sjhb/* 250223477Sjhb * AL to hex, saving the result to [EDI]. 251223477Sjhb */ 252223477Sjhbhex8: push %ax # Save 253223477Sjhb shrb $0x4,%al # Do upper 254223477Sjhb call hex8.1 # 4 255223477Sjhb pop %ax # Restore 256223477Sjhbhex8.1: andb $0xf,%al # Get lower 4 257223477Sjhb cmpb $0xa,%al # Convert 258223477Sjhb sbbb $0x69,%al # to hex 259223477Sjhb das # digit 260223477Sjhb orb $0x20,%al # To lower case 261223477Sjhb stosb # Save char 262223477Sjhb ret # (Recursive) 263223477Sjhb 264185029Spjd/* Messages */ 265185029Spjd 266223477Sjhbmsg_chs: .asciz "CHS not supported" 267223477Sjhbmsg_read: .ascii "Read error: " 268223477Sjhbread_err: .asciz "XX" 269223477Sjhbmsg_part: .asciz "Boot error" 270185029Spjd 271223477Sjhbprompt: .asciz "\r\n" 272185029Spjd 273185029Spjd .org PRT_OFF,0x90 274185029Spjd 275185029Spjd/* Partition table */ 276185029Spjd 277185029Spjd .fill 0x30,0x1,0x0 278185029Spjdpart4: .byte 0x80, 0x00, 0x01, 0x00 279185029Spjd .byte 0xa5, 0xfe, 0xff, 0xff 280185029Spjd .byte 0x00, 0x00, 0x00, 0x00 281185029Spjd .byte 0x50, 0xc3, 0x00, 0x00 # 50000 sectors long, bleh 282185029Spjd 283185029Spjd .word 0xaa55 # Magic number 284