| /* NOTE: this boot sector contains instructions that need at least an 80186. |
| * Yes, as86 has a bug somewhere in the valid instruction set checks. |
| * |
| */ |
| |
| /* floppyload.S Copyright (C) 1991, 1992 Linus Torvalds |
| * modified by Drew Eckhardt |
| * modified by Bruce Evans (bde) |
| * |
| * floppyprefix.S is loaded at 0x0000:0x7c00 by the bios-startup routines. |
| * |
| * It then loads the system at SYSSEG<<4, using BIOS interrupts. |
| * |
| * The loader has been made as simple as possible, and continuous read errors |
| * will result in a unbreakable loop. Reboot by hand. It loads pretty fast by |
| * getting whole tracks at a time whenever possible. |
| */ |
| |
| FILE_LICENCE ( GPL2_ONLY ) |
| |
| #include <librm.h> |
| |
| .equ BOOTSEG, 0x07C0 /* original address of boot-sector */ |
| |
| .equ SYSSEG, 0x1000 /* system loaded at SYSSEG<<4 */ |
| |
| .org 0 |
| .arch i386 |
| .text |
| .section ".prefix", "ax", @progbits |
| .code16 |
| .globl _dsk_start |
| _dsk_start: |
| |
| jmp $BOOTSEG, $go /* reload cs:ip to match relocation addr */ |
| go: |
| movw $0x2000-12, %di /* 0x2000 is arbitrary value >= length */ |
| /* of bootsect + room for stack + 12 for */ |
| /* saved disk parm block */ |
| |
| movw $BOOTSEG, %ax |
| movw %ax,%ds |
| movw %ax,%es |
| movw %ax,%ss /* put stack at BOOTSEG:0x4000-12. */ |
| movw %di,%sp |
| |
| /* Many BIOS's default disk parameter tables will not recognize multi-sector |
| * reads beyond the maximum sector number specified in the default diskette |
| * parameter tables - this may mean 7 sectors in some cases. |
| * |
| * Since single sector reads are slow and out of the question, we must take care |
| * of this by creating new parameter tables (for the first disk) in RAM. We |
| * will set the maximum sector count to 36 - the most we will encounter on an |
| * ED 2.88. High doesn't hurt. Low does. |
| * |
| * Segments are as follows: ds=es=ss=cs - BOOTSEG |
| */ |
| |
| xorw %cx,%cx |
| movw %cx,%es /* access segment 0 */ |
| movw $0x78, %bx /* 0:bx is parameter table address */ |
| pushw %ds /* save ds */ |
| /* 0:bx is parameter table address */ |
| ldsw %es:(%bx),%si /* loads ds and si */ |
| |
| movw %ax,%es /* ax is BOOTSECT (loaded above) */ |
| movb $6, %cl /* copy 12 bytes */ |
| cld |
| pushw %di /* keep a copy for later */ |
| rep |
| movsw /* ds:si is source, es:di is dest */ |
| popw %di |
| |
| movb $36,%es:4(%di) |
| |
| movw %cx,%ds /* access segment 0 */ |
| xchgw %di,(%bx) |
| movw %es,%si |
| xchgw %si,2(%bx) |
| popw %ds /* restore ds */ |
| movw %di, dpoff /* save old parameters */ |
| movw %si, dpseg /* to restore just before finishing */ |
| pushw %ds |
| popw %es /* reload es */ |
| |
| /* Note that es is already set up. Also cx is 0 from rep movsw above. */ |
| |
| xorb %ah,%ah /* reset FDC */ |
| xorb %dl,%dl |
| int $0x13 |
| |
| /* Get disk drive parameters, specifically number of sectors/track. |
| * |
| * It seems that there is no BIOS call to get the number of sectors. Guess |
| * 36 sectors if sector 36 can be read, 18 sectors if sector 18 can be read, |
| * 15 if sector 15 can be read. Otherwise guess 9. |
| */ |
| |
| movw $disksizes, %si /* table of sizes to try */ |
| |
| probe_loop: |
| lodsb |
| cbtw /* extend to word */ |
| movw %ax, sectors |
| cmpw $disksizes+4, %si |
| jae got_sectors /* if all else fails, try 9 */ |
| xchgw %cx,%ax /* cx = track and sector */ |
| xorw %dx,%dx /* drive 0, head 0 */ |
| movw $0x0200, %bx /* address after boot sector */ |
| /* (512 bytes from origin, es = cs) */ |
| movw $0x0201, %ax /* service 2, 1 sector */ |
| int $0x13 |
| jc probe_loop /* try next value */ |
| |
| got_sectors: |
| movw $msg1end-msg1, %cx |
| movw $msg1, %si |
| call print_str |
| |
| /* ok, we've written the Loading... message, now we want to load the system */ |
| |
| movw $SYSSEG, %ax |
| movw %ax,%es /* segment of SYSSEG<<4 */ |
| pushw %es |
| call read_it |
| |
| /* This turns off the floppy drive motor, so that we enter the kernel in a |
| * known state, and don't have to worry about it later. |
| */ |
| movw $0x3f2, %dx |
| xorb %al,%al |
| outb %al,%dx |
| |
| call print_nl |
| pop %es /* = SYSSEG */ |
| |
| /* Restore original disk parameters */ |
| movw $0x78, %bx |
| movw dpoff, %di |
| movw dpseg, %si |
| xorw %ax,%ax |
| movw %ax,%ds |
| movw %di,(%bx) |
| movw %si,2(%bx) |
| |
| /* Everything now loaded. %es = SYSSEG, so %es:0000 points to |
| * start of loaded image. |
| */ |
| |
| /* Jump to loaded copy */ |
| ljmp $SYSSEG, $start_runtime |
| |
| endseg: .word SYSSEG |
| .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */ |
| .ascii "ADDW" |
| .long endseg |
| .long 16 |
| .long 0 |
| .previous |
| |
| /* This routine loads the system at address SYSSEG<<4, making sure no 64kB |
| * boundaries are crossed. We try to load it as fast as possible, loading whole |
| * tracks whenever we can. |
| * |
| * in: es - starting address segment (normally SYSSEG) |
| */ |
| read_it: |
| movw $0,sread /* load whole image including prefix */ |
| movw %es,%ax |
| testw $0x0fff, %ax |
| die: jne die /* es must be at 64kB boundary */ |
| xorw %bx,%bx /* bx is starting address within segment */ |
| rp_read: |
| movw %es,%ax |
| movw %bx,%dx |
| movb $4, %cl |
| shrw %cl,%dx /* bx is always divisible by 16 */ |
| addw %dx,%ax |
| cmpw endseg, %ax /* have we loaded all yet? */ |
| jb ok1_read |
| ret |
| ok1_read: |
| movw sectors, %ax |
| subw sread, %ax |
| movw %ax,%cx |
| shlw $9, %cx |
| addw %bx,%cx |
| jnc ok2_read |
| je ok2_read |
| xorw %ax,%ax |
| subw %bx,%ax |
| shrw $9, %ax |
| ok2_read: |
| call read_track |
| movw %ax,%cx |
| addw sread, %ax |
| cmpw sectors, %ax |
| jne ok3_read |
| movw $1, %ax |
| subw head, %ax |
| jne ok4_read |
| incw track |
| ok4_read: |
| movw %ax, head |
| xorw %ax,%ax |
| ok3_read: |
| movw %ax, sread |
| shlw $9, %cx |
| addw %cx,%bx |
| jnc rp_read |
| movw %es,%ax |
| addb $0x10, %ah |
| movw %ax,%es |
| xorw %bx,%bx |
| jmp rp_read |
| |
| read_track: |
| pusha |
| pushw %ax |
| pushw %bx |
| pushw %bp /* just in case the BIOS is buggy */ |
| movw $0x0e2e, %ax /* 0x2e = . */ |
| movw $0x0007, %bx |
| int $0x10 |
| popw %bp |
| popw %bx |
| popw %ax |
| |
| movw track, %dx |
| movw sread, %cx |
| incw %cx |
| movb %dl,%ch |
| movw head, %dx |
| movb %dl,%dh |
| andw $0x0100, %dx |
| movb $2, %ah |
| |
| pushw %dx /* save for error dump */ |
| pushw %cx |
| pushw %bx |
| pushw %ax |
| |
| int $0x13 |
| jc bad_rt |
| addw $8, %sp |
| popa |
| ret |
| |
| bad_rt: pushw %ax /* save error code */ |
| call print_all /* ah = error, al = read */ |
| |
| xorb %ah,%ah |
| xorb %dl,%dl |
| int $0x13 |
| |
| addw $10, %sp |
| popa |
| jmp read_track |
| |
| /* print_all is for debugging purposes. It will print out all of the registers. |
| * The assumption is that this is called from a routine, with a stack frame like |
| * dx |
| * cx |
| * bx |
| * ax |
| * error |
| * ret <- sp |
| */ |
| |
| print_all: |
| call print_nl /* nl for readability */ |
| movw $5, %cx /* error code + 4 registers */ |
| movw %sp,%bp |
| |
| print_loop: |
| pushw %cx /* save count left */ |
| |
| cmpb $5, %cl |
| jae no_reg /* see if register name is needed */ |
| |
| movw $0x0007, %bx /* page 0, attribute 7 (normal) */ |
| movw $0xe05+0x41-1, %ax |
| subb %cl,%al |
| int $0x10 |
| |
| movb $0x58, %al /* 'X' */ |
| int $0x10 |
| |
| movb $0x3A, %al /* ':' */ |
| int $0x10 |
| |
| no_reg: |
| addw $2, %bp /* next register */ |
| call print_hex /* print it */ |
| movb $0x20, %al /* print a space */ |
| int $0x10 |
| popw %cx |
| loop print_loop |
| call print_nl /* nl for readability */ |
| ret |
| |
| print_str: |
| movw $0x0007, %bx /* page 0, attribute 7 (normal) */ |
| movb $0x0e, %ah /* write char, tty mode */ |
| prloop: |
| lodsb |
| int $0x10 |
| loop prloop |
| ret |
| |
| print_nl: |
| movw $0x0007, %bx /* page 0, attribute 7 (normal) */ |
| movw $0xe0d, %ax /* CR */ |
| int $0x10 |
| movb $0xa, %al /* LF */ |
| int $0x10 |
| ret |
| |
| /* print_hex prints the word pointed to by ss:bp in hexadecimal. */ |
| |
| print_hex: |
| movw (%bp),%dx /* load word into dx */ |
| movb $4, %cl |
| movb $0x0e, %ah /* write char, tty mode */ |
| movw $0x0007, %bx /* page 0, attribute 7 (normal) */ |
| call print_digit |
| call print_digit |
| call print_digit |
| /* fall through */ |
| print_digit: |
| rol %cl,%dx /* rotate so that lowest 4 bits are used */ |
| movb $0x0f, %al /* mask for nybble */ |
| andb %dl,%al |
| addb $0x90, %al /* convert al to ascii hex (four instructions) */ |
| daa |
| adcb $0x40, %al |
| daa |
| int $0x10 |
| ret |
| |
| sread: .word 0 /* sectors read of current track */ |
| head: .word 0 /* current head */ |
| track: .word 0 /* current track */ |
| |
| sectors: |
| .word 0 |
| |
| dpseg: .word 0 |
| dpoff: .word 0 |
| |
| disksizes: |
| .byte 36,18,15,9 |
| |
| msg1: |
| .ascii "Loading ROM image" |
| msg1end: |
| |
| .org 510, 0 |
| .word 0xAA55 |
| |
| start_runtime: |
| /* Install iPXE */ |
| call install |
| |
| /* Set up real-mode stack */ |
| movw %bx, %ss |
| movw $_estack16, %sp |
| |
| /* Jump to .text16 segment */ |
| pushw %ax |
| pushw $1f |
| lret |
| .section ".text16", "awx", @progbits |
| 1: |
| /* Run iPXE */ |
| virtcall main |
| |
| /* Uninstall iPXE */ |
| call uninstall |
| |
| /* Boot next device */ |
| int $0x18 |
| |