mirror of
https://gitlab.com/qemu-project/ipxe.git
synced 2025-11-03 07:59:06 +08:00
Newer versions of the GNU assembler (observed with binutils 2.41) will complain about the ".arch i386" in files assembled with "as --64", with the message "Error: 64bit mode not supported on 'i386'". In files such as stack.S that contain no instructions to be assembled, the ".arch i386" is redundant and may be removed entirely. In the remaining files, fix by moving ".arch i386" below the relevant ".code16" or ".code32" directive, so that the assembler is no longer expecting 64-bit instructions to be used by the time that the ".arch i386" directive is encountered. Reported-by: Ali Mustakim <alim@forwardcomputers.com> Signed-off-by: Michael Brown <mcb30@ipxe.org>
384 lines
8.1 KiB
ArmAsm
384 lines
8.1 KiB
ArmAsm
/* 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 */
|
|
|
|
.section ".note.GNU-stack", "", @progbits
|
|
.org 0
|
|
.code16
|
|
.arch i386
|
|
.section ".prefix", "ax", @progbits
|
|
.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
|
|
|