48c1c25ba5
effect (i386 only of course). Also change one branch in the bootselector code to an explicit 16 bit one, and check both the boot menu and partition tables to see if the partition requested by the user should be booted. This check just in case, should the bootselector menu and partition table get out of sync somehow. (mmm, bytesqueezing)
526 lines
13 KiB
ArmAsm
526 lines
13 KiB
ArmAsm
/* $NetBSD: mbr_bootsel.S,v 1.2 1999/04/17 01:38:00 fvdl Exp $ */
|
|
|
|
|
|
/*-
|
|
* Copyright (c) 1999 The NetBSD Foundation, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This code is derived from software contributed to The NetBSD Foundation
|
|
* by Frank van der Linden.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. All advertising materials mentioning features or use of this software
|
|
* must display the following acknowledgement:
|
|
* This product includes software developed by the NetBSD
|
|
* Foundation, Inc. and its contributors.
|
|
* 4. Neither the name of The NetBSD Foundation nor the names of its
|
|
* contributors may be used to endorse or promote products derived
|
|
* from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
|
|
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
|
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
|
|
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
/*
|
|
* Copyright (C) 1998 Wolfgang Solfrank.
|
|
* Copyright (C) 1998 TooLs GmbH.
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. All advertising materials mentioning features or use of this software
|
|
* must display the following acknowledgement:
|
|
* This product includes software developed by TooLs GmbH.
|
|
* 4. The name of TooLs GmbH may not be used to endorse or promote products
|
|
* derived from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
* IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
|
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
|
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
|
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
|
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
/*
|
|
* i386 master boot code
|
|
*/
|
|
|
|
#include <machine/asm.h>
|
|
|
|
#define data32 .byte 0x66
|
|
|
|
/*
|
|
* Gas can't emit 16 bit code. Define each instruction that we need where
|
|
* a 32-bit value (either data or addr) may be inserted in the generated
|
|
* code, or where we would need a prefix but can do without (saving some
|
|
* space).
|
|
*/
|
|
|
|
/*
|
|
* Register values used in the i386 instruction set
|
|
*/
|
|
#define AL 0
|
|
#define BL 3
|
|
#define CL 1
|
|
#define DL 2
|
|
#define AH 4
|
|
#define BH 7
|
|
#define CH 5
|
|
#define DH 6
|
|
|
|
#define AX 0
|
|
#define BX 3
|
|
#define CX 1
|
|
#define DX 2
|
|
#define SP 4
|
|
#define BP 5
|
|
#define SI 6
|
|
#define DI 7
|
|
|
|
#define SI_INDEX 4
|
|
#define DI_INDEX 5
|
|
#define BP_INDEX 7
|
|
|
|
|
|
/*
|
|
* .. and the 16 bit instruction defines.
|
|
*/
|
|
#define call16(label) .byte 0xe8 ; .word label - . - 2
|
|
#define jmp16(label) .byte 0xe9 ; .word label - . - 2
|
|
#define jznear16(label) .byte 0x0f ; .byte 0x84 ; .word label - . - 2
|
|
#define jmpfar(segval,addr) .byte 0xea ; .word addr ; .word segval
|
|
|
|
#define movb_mem_al(mem) .byte 0xa0 ; .word mem
|
|
#define movb_al_mem(mem) .byte 0xa2 ; .word mem
|
|
|
|
#define movw_mem_ax(mem) .byte 0xa1 ; .word mem
|
|
#define movw_ax_mem(mem) .byte 0xa3 ; .word mem
|
|
|
|
#define movw_mem_reg(mem,reg) \
|
|
.byte 0x8b ; .byte (0x06 | ((reg) << 3) ; .word mem
|
|
|
|
#define movw_imm_reg(imm,reg) \
|
|
.byte 0xb8 | reg ; .word imm
|
|
|
|
#define movb_ireg0_reg(ireg,reg) .byte 0x8a ; .byte (reg << 3) | ireg
|
|
#define movb_iregoff_reg(ireg,off,reg) \
|
|
.byte 0x8a ; .byte 0x40 | (reg << 3) | ireg ; .byte off
|
|
#define movw_iregoff_reg(ireg,off,reg) \
|
|
.byte 0x8b ; .byte 0x40 | (reg << 3) | ireg ; .byte off
|
|
|
|
#define movb_reg_mem(reg,mem) \
|
|
.byte 0x88 ; .byte 0x6 | (reg << 3) ; .word mem
|
|
#define movb_mem_reg(mem,reg) \
|
|
.byte 0x8a ; .byte 0x6 | (reg << 3) ; .word mem
|
|
|
|
#define movl_imm_ireg0(imm,ireg) \
|
|
.byte 0x66 ; .byte 0xc7 ; .byte ireg ; .long imm
|
|
#define movl_imm_iregoff(imm,ireg,off) \
|
|
.byte 0x66 ; .byte 0xc7 ; .byte 0x40 | ireg ; .byte off ; .long imm
|
|
|
|
#define cmp_imm_ax(imm) .byte 0x3d ; .word imm
|
|
|
|
#define cmpb_imm_ireg0(imm,reg) \
|
|
.byte 0x80 ; .byte 0x38 + reg ; .byte imm
|
|
|
|
#define cmpb_imm_iregoff(imm,reg,off) \
|
|
.byte 0x80 ; .byte 0x78 + reg ; .byte off ; .byte imm
|
|
|
|
#define cmpb_imm_ireg0_bx(imm,reg) \
|
|
.byte 0x80 ; .byte 0x34 + reg ; .byte imm
|
|
|
|
#define cmpb_mem_reg(mem,reg) .byte 0x3a ; .byte 0x06 | (reg << 3) ; .word mem
|
|
#define cmpw_imm_reg(imm,reg) .byte 0x81 ; .byte 0xf8 + reg ; .word imm
|
|
|
|
#define and_imm_ax(imm) .byte 0x25 ; .word imm
|
|
|
|
|
|
|
|
#define BOOTADDR 0x7c00
|
|
|
|
/*
|
|
* Each entry in the boot select table is a nul-terminated string
|
|
* of 8 bytes (not including the 0). A zero-length string (i.e.
|
|
* the first char is 0) indicates an unused entry.
|
|
*/
|
|
#define PARTNAMESIZE 8
|
|
#define TABENTRYSIZE (PARTNAMESIZE + 1)
|
|
#define NAMETABSIZE 4 * TABENTRYSIZE
|
|
|
|
/*
|
|
* Flag defines. Currently only used to make the boot selector active
|
|
* or inactive. There may never be more because of space constraints.
|
|
*/
|
|
#define BFL_SELACTIVE 0x01
|
|
|
|
/*
|
|
* Scan values for the various keys we use, as returned by the BIOS
|
|
*/
|
|
#define SCAN_ENTER 0x1c
|
|
#define SCAN_F1 0x3b
|
|
#define SCAN_F5 0x3f
|
|
#define SCAN_F10 0x44
|
|
|
|
/*
|
|
* Minimum and maximum drive number that is considered to be valid.
|
|
*/
|
|
#define MINDRV 0x80
|
|
#define MAXDRV 0x88
|
|
|
|
/*
|
|
* Error codes. Done this way to save space.
|
|
*/
|
|
#define ERR_INVPART '1' /* Invalid partition table */
|
|
#define ERR_READ '2' /* Read error */
|
|
#define ERR_NOOS '3' /* Magic no. check failed for part. */
|
|
|
|
.text
|
|
/*
|
|
* Move ourselves out of the way first.
|
|
*/
|
|
ENTRY(start)
|
|
data32
|
|
xorl %eax, %eax
|
|
movl %ax, %ss
|
|
movl $BOOTADDR, %sp
|
|
movl %ax, %es
|
|
movl %ax, %ds
|
|
xorl %si,%si
|
|
movl %sp, %si
|
|
movw_imm_reg(_C_LABEL(start),DI)
|
|
movw_imm_reg(0x100,CX)
|
|
rep
|
|
movsl
|
|
jmpfar(0,1f)
|
|
/*
|
|
* Sanity check the drive number passed by the BIOS. Some BIOSs may not
|
|
* do this and pass garbage.
|
|
*/
|
|
1:
|
|
cmpb $MINDRV,%dl
|
|
jb 2f
|
|
cmpb $MAXDRV,%dl
|
|
jbe 3f
|
|
2:
|
|
movb $0x80,%dl
|
|
3:
|
|
movb_reg_mem(DL,drvno)
|
|
bootsel:
|
|
movb_mem_al(flags)
|
|
testb $BFL_SELACTIVE,%al
|
|
jznear16(getactive)
|
|
/*
|
|
* The bootselector is active. Walk through the selector (name) table,
|
|
* printing used entries.
|
|
*/
|
|
movw_imm_reg(nametab,DI)
|
|
movb $0x31,%al
|
|
movb $4,%cl
|
|
1:
|
|
cmpb_imm_ireg0(0,DI_INDEX)
|
|
jz 2f
|
|
movb_al_mem(prefix+1)
|
|
movw_imm_reg(prefix,SI)
|
|
call16(putasciz)
|
|
movl %edi,%esi
|
|
call16(putasciz)
|
|
movw_imm_reg(newline,SI);
|
|
call16(putasciz)
|
|
2:
|
|
incb %al
|
|
addl $TABENTRYSIZE,%di
|
|
loop 1b
|
|
|
|
/*
|
|
* Get the initial time value for the timeout comparison. It is returned
|
|
* by int 1a in cx:dx. Make sure to grab the whole 32 bit value, otherwise
|
|
* it'd wrap around every hour. Now it'll only do so every 24 hours. Boots
|
|
* should happen infrequent enough that this isn't a problem.
|
|
*
|
|
* Loop around checking for a keypress until we have one, or timeout is
|
|
* reached.
|
|
*/
|
|
xorb %ah,%ah
|
|
int $0x1a
|
|
data32
|
|
shll $16,%ecx
|
|
movl %edx,%edi
|
|
data32
|
|
orl %ecx,%edi
|
|
3:
|
|
movb $1,%ah
|
|
int $0x16
|
|
jnz 4f
|
|
xorb %ah,%ah
|
|
int $0x1a
|
|
data32
|
|
shll $16,%ecx
|
|
data32
|
|
orl %ecx,%edx
|
|
data32
|
|
subl %edi,%edx
|
|
movw_mem_ax(timeout)
|
|
cmpl %eax,%edx
|
|
jb 3b
|
|
movb_mem_al(defkey)
|
|
jmp default
|
|
4:
|
|
xorb %ah,%ah
|
|
int $0x16
|
|
movb %ah,%al
|
|
default:
|
|
/*
|
|
* <enter> -> boot active partition.
|
|
*/
|
|
cmpb $SCAN_ENTER,%al
|
|
je getactive
|
|
/*
|
|
* F1-F4 -> boot partition 1-4
|
|
*/
|
|
subb $SCAN_F1,%al
|
|
cmpb $9,%al
|
|
ja 4b
|
|
cmpb $3,%al
|
|
jbe 5f
|
|
/*
|
|
* F5-F10 -> boot disk 0-5. Check if the requested disk isn't above
|
|
* the number of disks actually in the system as stored in 0:0475 by
|
|
* the BIOS.
|
|
*/
|
|
subb $4,%al
|
|
cmpb_mem_reg(0x0475,AL)
|
|
jae 4b
|
|
movw_imm_reg(fakeent,SI)
|
|
addb $0x80,%al
|
|
movb %al,%dl
|
|
jmp boot2
|
|
5:
|
|
/*
|
|
* Check if the requested entry is actually active in the partition and
|
|
* bootmenu table. If not, just do nothing.
|
|
*/
|
|
movw_imm_reg(parttab,SI)
|
|
and_imm_ax(0xff)
|
|
movl %eax,%ebx
|
|
shll $4,%eax
|
|
add %eax,%esi
|
|
cmpb_imm_iregoff(0,SI_INDEX,4)
|
|
je 4b
|
|
movw_imm_reg(nametab,DI)
|
|
imul $TABENTRYSIZE,%ebx
|
|
cmpb_imm_ireg0_bx(0,DI_INDEX)
|
|
je 4b
|
|
jmp boot
|
|
|
|
/*
|
|
* Look for the (first) active partition
|
|
*/
|
|
getactive:
|
|
movw_imm_reg(parttab,SI)
|
|
movw_imm_reg(4,CX)
|
|
1:
|
|
cmpb_imm_ireg0(0x80,SI_INDEX)
|
|
jz boot
|
|
addl $0x10, %esi
|
|
loop 1b
|
|
/*
|
|
* No active partition.
|
|
*/
|
|
invpart:
|
|
movb $ERR_INVPART,%al
|
|
jmp errhang
|
|
|
|
/*
|
|
* Active partition pointed to by si.
|
|
* Read the first sector.
|
|
*
|
|
* First determine whether we have int13-extensions, by calling
|
|
* int 13, function 41. Check for the magic number returned,
|
|
* and the disk packet capability.
|
|
*/
|
|
boot:
|
|
movb_mem_reg(drvno,DL)
|
|
boot2:
|
|
push %esi
|
|
push %edx
|
|
movw_imm_reg(0x55aa,BX)
|
|
movb $0x41, %ah
|
|
int $0x13
|
|
pop %edx
|
|
pop %esi
|
|
jc noext
|
|
cmpw_imm_reg(0xaa55,BX)
|
|
jnz noext
|
|
testb $1, %cl
|
|
jz noext
|
|
|
|
/*
|
|
* Modify the partition table entry to look like an int13-extension
|
|
* parameter block, so we can feed it to the extended read call.
|
|
* XXX this means that we can only use it once, we can't jump back
|
|
* here if the read fails.
|
|
*/
|
|
movl_imm_ireg0(0x10010,SI_INDEX)
|
|
movl_imm_iregoff(BOOTADDR,SI_INDEX,4)
|
|
movl_imm_iregoff(0,SI_INDEX,12)
|
|
movb $5, %dh
|
|
1:
|
|
push %esi
|
|
push %edx
|
|
movb $0x42, %ah
|
|
int $0x13
|
|
pop %edx
|
|
pop %esi
|
|
jnc ok
|
|
dec %dh
|
|
jnz 1b
|
|
rderr:
|
|
movb $ERR_READ,%al
|
|
errhang:
|
|
movw_imm_reg(err,SI)
|
|
call16(putasciz)
|
|
call16(putc)
|
|
hang:
|
|
sti
|
|
jmp hang
|
|
/*
|
|
* print a 0-terminated ASCII string. Address in si.
|
|
*/
|
|
putasciz:
|
|
push %eax
|
|
1:
|
|
lodsb
|
|
testb %al, %al
|
|
jz 2f
|
|
call16(putc)
|
|
jmp 1b
|
|
2:
|
|
pop %eax
|
|
ret
|
|
putc:
|
|
movb $0xe, %ah
|
|
movb $7, %bl
|
|
int $0x10
|
|
ret
|
|
|
|
/*
|
|
* No int13-extensions available, try old the method to load the frirst
|
|
* sector of the partition.
|
|
*/
|
|
noext:
|
|
movb_iregoff_reg(SI_INDEX,1,DH)
|
|
movw_iregoff_reg(SI_INDEX,2,CX)
|
|
load:
|
|
movw_imm_reg(5,DI)
|
|
1:
|
|
movw_imm_reg(BOOTADDR,BX)
|
|
movw_imm_reg(0x201,AX)
|
|
push %edi
|
|
push %edx
|
|
int $0x13
|
|
pop %edx
|
|
pop %edi
|
|
jnc ok
|
|
dec %edi
|
|
jnz 1b
|
|
jmp rderr
|
|
|
|
/*
|
|
* Check signature for valid bootcode
|
|
*/
|
|
ok:
|
|
movw_mem_ax(BOOTADDR+0x1fe)
|
|
cmp_imm_ax(0xaa55)
|
|
jz 1f
|
|
movb $ERR_NOOS,%al
|
|
jmp errhang
|
|
1:
|
|
jmpfar(0,BOOTADDR)
|
|
|
|
newline:
|
|
.asciz "\r\n"
|
|
prefix:
|
|
.asciz "F1: "
|
|
err:
|
|
.asciz "Err "
|
|
/*
|
|
* Fake partition entry used to boot from other disks. First byte is
|
|
* overloaded, it's also used as storage for the drive number. We're
|
|
* not using that in the entry.
|
|
*/
|
|
drvno:
|
|
fakeent:
|
|
.byte 0x80, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00
|
|
.long 0x0000, 0x0001
|
|
. = _C_LABEL(start) + (0x1bc - NAMETABSIZE - 4)
|
|
/*
|
|
* Default action, as a keyvalue we'd normally read from the BIOS. This
|
|
* saves a bit of space.
|
|
*/
|
|
defkey:
|
|
.byte SCAN_ENTER
|
|
flags:
|
|
.byte 0x01
|
|
/*
|
|
* Timeout value. 65536 ticks per hour, which is about 18.2 times per second.
|
|
*/
|
|
timeout:
|
|
.word 0x00b6
|
|
/*
|
|
* Space for name/select table and partition table. If DEBUG is defined,
|
|
* use some values from one of my machines, for testing.
|
|
*/
|
|
nametab:
|
|
#ifdef DEBUG
|
|
.asciz "Windows "
|
|
.asciz "NetBSD "
|
|
.byte 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
|
.byte 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
|
#else
|
|
.fill NAMETABSIZE,0x01,0x00
|
|
#endif
|
|
. = _C_LABEL(start) + 0x1bc
|
|
ourmagic:
|
|
.byte 0x55, 0xaa
|
|
. = _C_LABEL(start) + 0x1be
|
|
#ifdef DEBUG
|
|
parttab:
|
|
.word 0x0100
|
|
.word 0x0001, 0xde06, 0x1f3e, 0x003f, 0x0000, 0xd000, 0x0007, 0xde80
|
|
.word 0x1f3f, 0x24a5, 0xe57e, 0xd03f, 0x0007, 0x1c80, 0x006f, 0x0000
|
|
.word 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000
|
|
.word 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000
|
|
#else
|
|
parttab:
|
|
.fill 0x40,0x01,0x00
|
|
#endif
|
|
. = _C_LABEL(start) + 0x1fe
|
|
.byte 0x55, 0xaa
|