1/* NOTE: this boot sector contains instructions that need at least an 80186. 2 * Yes, as86 has a bug somewhere in the valid instruction set checks. 3 * 4 */ 5 6/* floppyload.S Copyright (C) 1991, 1992 Linus Torvalds 7 * modified by Drew Eckhardt 8 * modified by Bruce Evans (bde) 9 * 10 * floppyprefix.S is loaded at 0x0000:0x7c00 by the bios-startup routines. 11 * 12 * It then loads the system at SYSSEG<<4, using BIOS interrupts. 13 * 14 * The loader has been made as simple as possible, and continuous read errors 15 * will result in a unbreakable loop. Reboot by hand. It loads pretty fast by 16 * getting whole tracks at a time whenever possible. 17 */ 18 19FILE_LICENCE ( GPL2_ONLY ) 20 21.equ BOOTSEG, 0x07C0 /* original address of boot-sector */ 22 23.equ SYSSEG, 0x1000 /* system loaded at SYSSEG<<4 */ 24 25 .org 0 26 .arch i386 27 .text 28 .section ".prefix", "ax", @progbits 29 .code16 30 31 jmp $BOOTSEG, $go /* reload cs:ip to match relocation addr */ 32go: 33 movw $0x2000-12, %di /* 0x2000 is arbitrary value >= length */ 34 /* of bootsect + room for stack + 12 for */ 35 /* saved disk parm block */ 36 37 movw $BOOTSEG, %ax 38 movw %ax,%ds 39 movw %ax,%es 40 movw %ax,%ss /* put stack at BOOTSEG:0x4000-12. */ 41 movw %di,%sp 42 43/* Many BIOS's default disk parameter tables will not recognize multi-sector 44 * reads beyond the maximum sector number specified in the default diskette 45 * parameter tables - this may mean 7 sectors in some cases. 46 * 47 * Since single sector reads are slow and out of the question, we must take care 48 * of this by creating new parameter tables (for the first disk) in RAM. We 49 * will set the maximum sector count to 36 - the most we will encounter on an 50 * ED 2.88. High doesn't hurt. Low does. 51 * 52 * Segments are as follows: ds=es=ss=cs - BOOTSEG 53 */ 54 55 xorw %cx,%cx 56 movw %cx,%es /* access segment 0 */ 57 movw $0x78, %bx /* 0:bx is parameter table address */ 58 pushw %ds /* save ds */ 59/* 0:bx is parameter table address */ 60 ldsw %es:(%bx),%si /* loads ds and si */ 61 62 movw %ax,%es /* ax is BOOTSECT (loaded above) */ 63 movb $6, %cl /* copy 12 bytes */ 64 cld 65 pushw %di /* keep a copy for later */ 66 rep 67 movsw /* ds:si is source, es:di is dest */ 68 popw %di 69 70 movb $36,%es:4(%di) 71 72 movw %cx,%ds /* access segment 0 */ 73 xchgw %di,(%bx) 74 movw %es,%si 75 xchgw %si,2(%bx) 76 popw %ds /* restore ds */ 77 movw %di, dpoff /* save old parameters */ 78 movw %si, dpseg /* to restore just before finishing */ 79 pushw %ds 80 popw %es /* reload es */ 81 82/* Note that es is already set up. Also cx is 0 from rep movsw above. */ 83 84 xorb %ah,%ah /* reset FDC */ 85 xorb %dl,%dl 86 int $0x13 87 88/* Get disk drive parameters, specifically number of sectors/track. 89 * 90 * It seems that there is no BIOS call to get the number of sectors. Guess 91 * 36 sectors if sector 36 can be read, 18 sectors if sector 18 can be read, 92 * 15 if sector 15 can be read. Otherwise guess 9. 93 */ 94 95 movw $disksizes, %si /* table of sizes to try */ 96 97probe_loop: 98 lodsb 99 cbtw /* extend to word */ 100 movw %ax, sectors 101 cmpw $disksizes+4, %si 102 jae got_sectors /* if all else fails, try 9 */ 103 xchgw %cx,%ax /* cx = track and sector */ 104 xorw %dx,%dx /* drive 0, head 0 */ 105 movw $0x0200, %bx /* address after boot sector */ 106 /* (512 bytes from origin, es = cs) */ 107 movw $0x0201, %ax /* service 2, 1 sector */ 108 int $0x13 109 jc probe_loop /* try next value */ 110 111got_sectors: 112 movw $msg1end-msg1, %cx 113 movw $msg1, %si 114 call print_str 115 116/* ok, we've written the Loading... message, now we want to load the system */ 117 118 movw $SYSSEG, %ax 119 movw %ax,%es /* segment of SYSSEG<<4 */ 120 pushw %es 121 call read_it 122 123/* This turns off the floppy drive motor, so that we enter the kernel in a 124 * known state, and don't have to worry about it later. 125 */ 126 movw $0x3f2, %dx 127 xorb %al,%al 128 outb %al,%dx 129 130 call print_nl 131 pop %es /* = SYSSEG */ 132 133/* Restore original disk parameters */ 134 movw $0x78, %bx 135 movw dpoff, %di 136 movw dpseg, %si 137 xorw %ax,%ax 138 movw %ax,%ds 139 movw %di,(%bx) 140 movw %si,2(%bx) 141 142 /* Everything now loaded. %es = SYSSEG, so %es:0000 points to 143 * start of loaded image. 144 */ 145 146 /* Jump to loaded copy */ 147 ljmp $SYSSEG, $start_runtime 148 149endseg: .word SYSSEG 150 .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */ 151 .ascii "ADDW" 152 .long endseg 153 .long 16 154 .long 0 155 .previous 156 157/* This routine loads the system at address SYSSEG<<4, making sure no 64kB 158 * boundaries are crossed. We try to load it as fast as possible, loading whole 159 * tracks whenever we can. 160 * 161 * in: es - starting address segment (normally SYSSEG) 162 */ 163read_it: 164 movw $0,sread /* load whole image including prefix */ 165 movw %es,%ax 166 testw $0x0fff, %ax 167die: jne die /* es must be at 64kB boundary */ 168 xorw %bx,%bx /* bx is starting address within segment */ 169rp_read: 170 movw %es,%ax 171 movw %bx,%dx 172 movb $4, %cl 173 shrw %cl,%dx /* bx is always divisible by 16 */ 174 addw %dx,%ax 175 cmpw endseg, %ax /* have we loaded all yet? */ 176 jb ok1_read 177 ret 178ok1_read: 179 movw sectors, %ax 180 subw sread, %ax 181 movw %ax,%cx 182 shlw $9, %cx 183 addw %bx,%cx 184 jnc ok2_read 185 je ok2_read 186 xorw %ax,%ax 187 subw %bx,%ax 188 shrw $9, %ax 189ok2_read: 190 call read_track 191 movw %ax,%cx 192 addw sread, %ax 193 cmpw sectors, %ax 194 jne ok3_read 195 movw $1, %ax 196 subw head, %ax 197 jne ok4_read 198 incw track 199ok4_read: 200 movw %ax, head 201 xorw %ax,%ax 202ok3_read: 203 movw %ax, sread 204 shlw $9, %cx 205 addw %cx,%bx 206 jnc rp_read 207 movw %es,%ax 208 addb $0x10, %ah 209 movw %ax,%es 210 xorw %bx,%bx 211 jmp rp_read 212 213read_track: 214 pusha 215 pushw %ax 216 pushw %bx 217 pushw %bp /* just in case the BIOS is buggy */ 218 movw $0x0e2e, %ax /* 0x2e = . */ 219 movw $0x0007, %bx 220 int $0x10 221 popw %bp 222 popw %bx 223 popw %ax 224 225 movw track, %dx 226 movw sread, %cx 227 incw %cx 228 movb %dl,%ch 229 movw head, %dx 230 movb %dl,%dh 231 andw $0x0100, %dx 232 movb $2, %ah 233 234 pushw %dx /* save for error dump */ 235 pushw %cx 236 pushw %bx 237 pushw %ax 238 239 int $0x13 240 jc bad_rt 241 addw $8, %sp 242 popa 243 ret 244 245bad_rt: pushw %ax /* save error code */ 246 call print_all /* ah = error, al = read */ 247 248 xorb %ah,%ah 249 xorb %dl,%dl 250 int $0x13 251 252 addw $10, %sp 253 popa 254 jmp read_track 255 256/* print_all is for debugging purposes. It will print out all of the registers. 257 * The assumption is that this is called from a routine, with a stack frame like 258 * dx 259 * cx 260 * bx 261 * ax 262 * error 263 * ret <- sp 264 */ 265 266print_all: 267 call print_nl /* nl for readability */ 268 movw $5, %cx /* error code + 4 registers */ 269 movw %sp,%bp 270 271print_loop: 272 pushw %cx /* save count left */ 273 274 cmpb $5, %cl 275 jae no_reg /* see if register name is needed */ 276 277 movw $0x0007, %bx /* page 0, attribute 7 (normal) */ 278 movw $0xe05+0x41-1, %ax 279 subb %cl,%al 280 int $0x10 281 282 movb $0x58, %al /* 'X' */ 283 int $0x10 284 285 movb $0x3A, %al /* ':' */ 286 int $0x10 287 288no_reg: 289 addw $2, %bp /* next register */ 290 call print_hex /* print it */ 291 movb $0x20, %al /* print a space */ 292 int $0x10 293 popw %cx 294 loop print_loop 295 call print_nl /* nl for readability */ 296 ret 297 298print_str: 299 movw $0x0007, %bx /* page 0, attribute 7 (normal) */ 300 movb $0x0e, %ah /* write char, tty mode */ 301prloop: 302 lodsb 303 int $0x10 304 loop prloop 305 ret 306 307print_nl: 308 movw $0x0007, %bx /* page 0, attribute 7 (normal) */ 309 movw $0xe0d, %ax /* CR */ 310 int $0x10 311 movb $0xa, %al /* LF */ 312 int $0x10 313 ret 314 315/* print_hex prints the word pointed to by ss:bp in hexadecimal. */ 316 317print_hex: 318 movw (%bp),%dx /* load word into dx */ 319 movb $4, %cl 320 movb $0x0e, %ah /* write char, tty mode */ 321 movw $0x0007, %bx /* page 0, attribute 7 (normal) */ 322 call print_digit 323 call print_digit 324 call print_digit 325/* fall through */ 326print_digit: 327 rol %cl,%dx /* rotate so that lowest 4 bits are used */ 328 movb $0x0f, %al /* mask for nybble */ 329 andb %dl,%al 330 addb $0x90, %al /* convert al to ascii hex (four instructions) */ 331 daa 332 adcb $0x40, %al 333 daa 334 int $0x10 335 ret 336 337sread: .word 0 /* sectors read of current track */ 338head: .word 0 /* current head */ 339track: .word 0 /* current track */ 340 341sectors: 342 .word 0 343 344dpseg: .word 0 345dpoff: .word 0 346 347disksizes: 348 .byte 36,18,15,9 349 350msg1: 351 .ascii "Loading ROM image" 352msg1end: 353 354 .org 510, 0 355 .word 0xAA55 356 357start_runtime: 358 /* Install gPXE */ 359 call install 360 361 /* Set up real-mode stack */ 362 movw %bx, %ss 363 movw $_estack16, %sp 364 365 /* Jump to .text16 segment */ 366 pushw %ax 367 pushw $1f 368 lret 369 .section ".text16", "awx", @progbits 3701: 371 pushl $main 372 pushw %cs 373 call prot_call 374 popl %ecx /* discard */ 375 376 /* Uninstall gPXE */ 377 call uninstall 378 379 /* Boot next device */ 380 int $0x18 381 382