mirror of
https://github.com/memtest86plus/memtest86plus.git
synced 2024-11-23 08:26:23 -06:00
368 lines
7.4 KiB
ArmAsm
368 lines
7.4 KiB
ArmAsm
// SPDX-License-Identifier: GPL-2.0
|
|
//
|
|
// bootsect.S supports booting directly from the BIOS or via an intermediate
|
|
// bootloader that supports the Linux boot protocol. If booted directly from
|
|
// the BIOS, it is loaded at address 0x7c00. It then loads setup.S immediately
|
|
// after itself (address 0x7e00) and the main program code at segment MAIN_SEG,
|
|
// using BIOS interrupts to read the data from disk. When using an intermediate
|
|
// bootloader, it provides the first few bytes of the Linux boot header (at the
|
|
// end of the boot sector), with the remainder of the header being provided by
|
|
// setup.S.
|
|
//
|
|
// Copyright (C) 2020 Martin Whitaker.
|
|
//
|
|
// Derived from memtest86+ bootsect.S:
|
|
//
|
|
// bootsect.s Copyright (C) 1991, 1992 Linus Torvalds
|
|
//
|
|
// 1-Jan-96 Modified by Chris Brady for use as a boot loader for MemTest-86.
|
|
|
|
#define __ASSEMBLY__
|
|
|
|
#include "boot.h"
|
|
|
|
.section ".bootsect", "ax", @progbits
|
|
.code16
|
|
|
|
# The BIOS boot entry point. This will be located at 0x7c00.
|
|
|
|
.globl boot
|
|
boot:
|
|
# Initialise the segment registers and the stack.
|
|
|
|
ljmp $BOOT_SEG, $init
|
|
init:
|
|
movw %cs, %ax
|
|
movw %ax, %ds
|
|
movw %ax, %es
|
|
movw %ax, %ss
|
|
movw $BOOT_STACK_TOP, %ax
|
|
movw %ax, %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 18 - the most we will encounter on an HD 1.44.
|
|
#
|
|
# High doesn't hurt. Low does.
|
|
#
|
|
# Segments are as follows:
|
|
# ds=es=ss=cs = BOOT_SEG,
|
|
# fs = 0, gs = parameter table segment
|
|
|
|
pushw $0
|
|
popw %fs
|
|
movw $0x78, %bx # fs:bx is parameter table address
|
|
lgs %fs:(%bx),%si # gs:si is source
|
|
|
|
movw %dx, %di # es:di is destination
|
|
movw $6, %cx # copy 12 bytes
|
|
cld
|
|
|
|
rep movsw %gs:(%si), (%di)
|
|
|
|
movw %dx, %di
|
|
movb $18, 4(%di) # patch sector count
|
|
|
|
movw %di, %fs:(%bx)
|
|
movw %es, %fs:2(%bx)
|
|
|
|
movw %cs, %ax
|
|
movw %ax, %fs
|
|
movw %ax, %gs
|
|
|
|
xorb %ah, %ah # reset FDC
|
|
xorb %dl, %dl
|
|
int $0x13
|
|
|
|
# Load the setup sectors directly after the boot block.
|
|
# Note that 'es' is already set up.
|
|
|
|
load_setup:
|
|
xorw %dx, %dx # drive 0, head 0
|
|
movw $0x0002, %cx # sector 2, track 0
|
|
movw $0x0200, %bx # address = 512, in BOOT_SEG
|
|
movw $(0x0200 + SETUP_SECS), %ax # service 2, nr of sectors
|
|
# (assume all on head 0, track 0)
|
|
int $0x13 # read it
|
|
jnc load_setup_done # ok - continue
|
|
|
|
pushw %ax # dump error code
|
|
call print_nl
|
|
movw %sp, %bp
|
|
call print_hex
|
|
popw %ax
|
|
|
|
xorb %dl, %dl # reset FDC
|
|
xorb %ah, %ah
|
|
int $0x13
|
|
jmp load_setup
|
|
|
|
load_setup_done:
|
|
|
|
# Get disk drive parameters, specifically number of sectors/track.
|
|
# It seems that there is no BIOS call to get the number of sectors.
|
|
# Guess 18 sectors if sector 18 can be read, 15 if sector 15 can be
|
|
# read. Otherwise guess 9.
|
|
|
|
xorw %dx, %dx # drive 0, head 0
|
|
movw $0x0012, %cx # sector 18, track 0
|
|
movw $BOOT_STACK, %bx # use the bottom of the stack (es = cs)
|
|
movw $0x0201, %ax # service 2, 1 sector
|
|
int $0x13
|
|
jnc got_sectors
|
|
movb $0x0f, %cl # sector 15
|
|
movw $0x0201, %ax # service 2, 1 sector
|
|
int $0x13
|
|
jnc got_sectors
|
|
movb $0x09, %cl
|
|
|
|
got_sectors:
|
|
movw %cx, %cs:sectors
|
|
movw $BOOT_SEG, %ax
|
|
movw %ax, %es
|
|
|
|
# Print a message.
|
|
|
|
movb $0x03, %ah # read cursor pos
|
|
xorb %bh, %bh
|
|
int $0x10
|
|
|
|
leaw boot_msg, %bp
|
|
movw $(boot_msg_end - boot_msg), %cx
|
|
movw $0x0007, %bx # page 0, attribute 7 (normal)
|
|
movw $0x1301, %ax # write string, move cursor
|
|
int $0x10
|
|
|
|
# Load the main test program.
|
|
|
|
movw $MAIN_SEG, %ax
|
|
movw %ax, %es
|
|
call read_it
|
|
call kill_motor
|
|
call turn_off_cursor
|
|
call print_nl
|
|
|
|
# Fix up the Linux boot header to indicate we've loaded into low memory.
|
|
|
|
movl $LOW_LOAD_ADDR, code32_start
|
|
|
|
# After that (everything loaded), we jump to the setup code loaded
|
|
# directly after the boot block.
|
|
|
|
ljmp $SETUP_SEG, $0
|
|
|
|
# This subroutine loads the system at address 0x10000, 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 0x1000)
|
|
#
|
|
sread: .word 1 + SETUP_SECS # sectors read of current track
|
|
head: .word 0 # current head
|
|
track: .word 0 # current track
|
|
|
|
read_it:
|
|
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
|
|
subw $MAIN_SEG, %ax # have we loaded all yet?
|
|
cmpw sys_size, %ax
|
|
jbe ok1_read
|
|
ret
|
|
ok1_read:
|
|
movw %cs: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
|
|
add sread, %ax
|
|
cmpw %cs: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
|
|
pusha
|
|
movw $0xe2e, %ax # loading... message 2e = .
|
|
movw $7, %bx
|
|
int $0x10
|
|
popa
|
|
|
|
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
|
|
|
|
# This subroutine 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
|
|
# err
|
|
# ret <- sp
|
|
|
|
print_all:
|
|
movw $5, %cx # error code + 4 registers
|
|
movw %sp, %bp
|
|
|
|
print_loop:
|
|
pushw %cx # save count left
|
|
call print_nl # nl for readability
|
|
|
|
cmpb 5, %cl # see if register name is needed
|
|
jae no_reg
|
|
|
|
movw $(0xe05 + 'A' - 1), %ax
|
|
subb %cl, %al
|
|
int $0x10
|
|
movb $'X', %al
|
|
int $0x10
|
|
movb $':', %al
|
|
int $0x10
|
|
|
|
no_reg:
|
|
addw $2, %bp # next register
|
|
call print_hex # print it
|
|
popw %cx
|
|
loop print_loop
|
|
ret
|
|
|
|
print_nl:
|
|
movw $0xe0d, %ax # CR
|
|
int $0x10
|
|
movb $0x0a, %al # LF
|
|
int $0x10
|
|
ret
|
|
|
|
# This subroutine is for debugging purposes, and prints the word pointed to
|
|
# by ss:bp in hexadecimal.
|
|
|
|
print_hex:
|
|
movw $4, %cx # 4 hex digits
|
|
movw (%bp), %dx # load word into dx
|
|
|
|
print_digit:
|
|
rolw $4, %dx # rotate so that lowest 4 bits are used
|
|
movb $0xe, %ah
|
|
movb %dl, %al # mask off so we have only next nibble
|
|
andb $0xf, %al
|
|
addb $'0', %al # convert to 0-based digit
|
|
cmpb $'9', %al # check for overflow
|
|
jbe good_digit
|
|
addb $('A' - '0' - 10), %al
|
|
|
|
good_digit:
|
|
int $0x10
|
|
loop print_digit
|
|
ret
|
|
|
|
# This subroutine 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.
|
|
|
|
kill_motor:
|
|
pushw %dx
|
|
movw $0x3f2, %dx
|
|
xorb %al, %al
|
|
outb %al, %dx
|
|
popw %dx
|
|
ret
|
|
|
|
# This subroutine turns off the text display cursor.
|
|
|
|
turn_off_cursor:
|
|
movb $0x01, %ah
|
|
movb $0x00, %bh
|
|
movw $0x2000, %cx
|
|
int $0x10
|
|
ret
|
|
|
|
# Local variables.
|
|
|
|
sectors:
|
|
.word 0
|
|
|
|
boot_msg:
|
|
.ascii "Loading Memtest86+"
|
|
boot_msg_end:
|
|
|
|
# Emulate the Linux boot header, to allow loading by intermediate boot loaders.
|
|
|
|
.org 497
|
|
setup_sects:
|
|
.byte SETUP_SECS
|
|
root_flags:
|
|
.word 0
|
|
sys_size:
|
|
.long _sys_size
|
|
ram_size:
|
|
.word 0
|
|
vid_mode:
|
|
.word 0
|
|
root_dev:
|
|
.word 0
|
|
boot_flag:
|
|
.word 0xAA55
|