summaryrefslogtreecommitdiffstats
path: root/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-bootsector.asm
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/ValidationKit/bootsectors/bs3kit/bs3-bootsector.asm')
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-bootsector.asm594
1 files changed, 594 insertions, 0 deletions
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-bootsector.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-bootsector.asm
new file mode 100644
index 00000000..edd26f4d
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-bootsector.asm
@@ -0,0 +1,594 @@
+; $Id: bs3-bootsector.asm $
+;; @file
+; Generic bootsector for BS3.
+;
+; This sets up stack at %fff0 and loads the next sectors from the floppy at
+; %10000 (1000:0000 in real mode), then starts executing at cs:ip=1000:0000.
+;
+
+;
+; Copyright (C) 2007-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+
+
+;*********************************************************************************************************************************
+;* Header Files *
+;*********************************************************************************************************************************
+%include "bs3kit.mac"
+%include "iprt/asmdefs.mac"
+%include "iprt/x86.mac"
+
+
+;*********************************************************************************************************************************
+;* Defined Constants And Macros *
+;*********************************************************************************************************************************
+;; Enabled faster loading.
+%define BS3KIT_BOOTSECTOR_FASTER_LOAD
+;; Enabled load progress dots.
+%define BS3KIT_BOOTSECTOR_LOAD_DOTS
+
+;; Halts on failure location. For debugging.
+;%define HLT_ON_FAILURE 1
+
+;; Enables saving of initial register state.
+;; Dropping this is useful for making more room for debugging.
+%define BS3KIT_BOOTSECTOR_SAVE_INITIAL_STATE
+
+
+%ifdef __YASM__
+[map all]
+%endif
+
+;
+; Start with a jump just to follow the convention.
+; Also declare all segments/sections to establish them and their order.
+;
+ ORG 07c00h
+
+BITS 16
+CPU 8086
+start:
+ jmp short bs3InitCode
+ db 0ah ; Should be nop, but this looks better.
+g_OemId: ; 003h
+ db 'BS3Kit', 0ah, 0ah
+
+;
+; DOS 4.0 Extended Bios Parameter Block:
+;
+g_cBytesPerSector: ; 00bh
+ dw 512
+g_cSectorsPerCluster: ; 00dh
+ db 1
+g_cReservedSectors: ; 00eh
+ dw 1
+g_cFATs: ; 010h
+ db 0
+g_cRootDirEntries: ; 011h
+ dw 0
+g_cTotalSectors: ; 013h
+ dw 0
+g_bMediaDescriptor: ; 015h
+ db 0
+g_cSectorsPerFAT: ; 016h
+ dw 0
+g_cPhysSectorsPerTrack: ; 018h
+ dw 18
+g_cHeads: ; 01ah
+ dw 2
+g_cHiddentSectors: ; 01ch
+ dd 1
+g_cLargeTotalSectors: ; 020h - We (ab)use this to indicate the number of sectors to load.
+ dd 0
+g_bBootDrv: ; 024h
+ db 80h
+g_bFlagsEtc: ; 025h
+ db 0
+g_bExtendedSignature: ; 026h
+ db 0x29
+g_dwSerialNumber: ; 027h
+ dd 0x0a458634
+g_abLabel: ; 02bh
+ db 'VirtualBox', 0ah
+g_abFSType: ; 036h
+ db 'RawCode', 0ah
+g_BpbEnd: ; 03ch
+
+
+;
+; Where to real init code starts.
+;
+bs3InitCode:
+ cli
+
+%ifdef BS3KIT_BOOTSECTOR_SAVE_INITIAL_STATE
+ ; save the registers.
+ mov [cs:BS3_ADDR_REG_SAVE + BS3REGCTX.rax], ax
+ mov [cs:BS3_ADDR_REG_SAVE + BS3REGCTX.ds], ds
+%endif
+
+ ; set up the DS segment reister so we can skip the CS prefix when saving more prefixes..
+ mov ax, 0
+ mov ds, ax
+
+%ifdef BS3KIT_BOOTSECTOR_SAVE_INITIAL_STATE
+ mov [BS3_ADDR_REG_SAVE + BS3REGCTX.rdi], di
+ mov di, BS3_ADDR_REG_SAVE
+ mov [di + BS3REGCTX.rsp], sp
+ mov [di + BS3REGCTX.ss], ss
+ mov [di + BS3REGCTX.rcx], cx
+ mov [di + BS3REGCTX.es], es
+ mov [di + BS3REGCTX.rbp], bp
+%endif
+
+ ; set up the stack.
+ mov ss, ax
+ mov sp, BS3_ADDR_STACK
+
+ ; Load es and setup bp frame.
+ mov es, ax
+ mov bp, sp
+%if 0
+ mov [bp], ax ; clear the first 8 bytes (terminates the ebp chain)
+ mov [bp + 02h], ax
+ mov [bp + 04h], ax
+ mov [bp + 06h], ax
+%else
+ mov di, sp ; Combine clearing the rbp chain and register save area.
+%endif
+
+ ; Save flags now that we know that there's a valid stack.
+ pushf
+
+ ;
+ ; Clear the register area.
+ ;
+%if 0
+ mov di, BS3_ADDR_REG_SAVE
+ mov cx, BS3REGCTX_size/2
+%else
+ mov cx, (BS3_ADDR_LOAD - BS3_ADDR_STACK) / 2
+%endif
+ cld
+ rep stosw
+
+ ;
+ ; Do basic CPU detection.
+ ;
+
+ ; 0. Load the register save area address into DI to avoid absolute addressing
+ ; when saving additional state. To avoid disp16, offset the address.
+ mov di, BS3_ADDR_REG_SAVE + 0x70
+
+ ; 1. bit 15-bit was fixed to 1 in pre-286 CPUs, and fixed to 0 in 286+.
+ mov ax, [bp - 2]
+ test ah, 080h ; always set on pre 286, clear on 286 and later
+ jnz .pre_80286
+
+ ; 2. On a 286 you cannot popf IOPL and NT from real mode.
+.detect_286_or_386plus:
+CPU 286
+ mov ah, (X86_EFL_IOPL | X86_EFL_NT) >> 8
+ push ax
+ popf
+ pushf
+ cmp ah, [bp - 3]
+ pop ax
+ je .is_386plus
+.is_80286:
+CPU 286
+%ifdef BS3KIT_BOOTSECTOR_SAVE_INITIAL_STATE
+ smsw [di + BS3REGCTX.cr0 - 0x70]
+%endif
+.pre_80286:
+CPU 8086
+%ifdef BS3KIT_BOOTSECTOR_SAVE_INITIAL_STATE
+ mov [di - 0x70 + BS3REGCTX.rbx], bx
+ mov [di - 0x70 + BS3REGCTX.rdx], dx
+ mov [di - 0x70 + BS3REGCTX.rsi], si
+%endif
+ jmp .do_load
+
+ ; Save 386 registers. We can now skip the CS prefix as DS is flat.
+CPU 386
+.is_386plus:
+%ifdef BS3KIT_BOOTSECTOR_SAVE_INITIAL_STATE
+ shr eax, 16
+ mov [di - 0x70 + BS3REGCTX.rax+2], ax
+ mov eax, esp
+ shr eax, 16
+ mov [di - 0x70 + BS3REGCTX.rsp+2], ax
+ mov eax, ebp
+ shr eax, 16
+ mov [di - 0x70 + BS3REGCTX.rbp+2], ax
+ mov eax, edi
+ shr eax, 16
+ mov [di - 0x70 + BS3REGCTX.rdi+2], ax
+ shr ecx, 16
+ mov [di - 0x70 + BS3REGCTX.rcx+2], cx
+ mov [di - 0x70 + BS3REGCTX.fs], fs
+ mov [di - 0x70 + BS3REGCTX.gs], gs
+ mov [di - 0x70 + BS3REGCTX.rbx], ebx
+ mov [di - 0x70 + BS3REGCTX.rdx], edx
+ mov [di - 0x70 + BS3REGCTX.rsi], esi
+ mov eax, cr2
+ mov [di - 0x70 + BS3REGCTX.cr2], eax
+ mov eax, cr3
+ mov [di - 0x70 + BS3REGCTX.cr3], eax
+ mov byte [di - 0x70 + BS3REGCTX.bMode], BS3_MODE_RM
+ mov [di - 0x70 + BS3REGCTX.cs], cs
+ xor eax, eax
+ mov ax, start
+ mov [di - 0x70 + BS3REGCTX.rip], eax
+
+ ; Pentium/486+: CR4 requires VME/CPUID, so we need to detect that before accessing it.
+ mov [di - 0x70 + BS3REGCTX.cr4], eax
+ popf ; (restores IOPL+NT)
+ pushfd
+ pop eax
+ mov [di - 0x70 + BS3REGCTX.rflags], eax
+ xor eax, X86_EFL_ID
+ push eax
+ popfd
+ pushfd
+ pop ebx
+ cmp ebx, eax
+ jne .no_cr4
+ mov eax, cr4
+ mov [di - 0x70 + BS3REGCTX.cr4], eax
+.no_cr4:
+%endif
+ ; Make sure caching is enabled and alignment is off.
+ mov eax, cr0
+%ifdef BS3KIT_BOOTSECTOR_SAVE_INITIAL_STATE
+ mov [di - 0x70 + BS3REGCTX.cr0], eax
+%endif
+ and eax, ~(X86_CR0_NW | X86_CR0_CD | X86_CR0_AM)
+ mov cr0, eax
+
+ ; Load all the code.
+.do_load
+ mov [g_bBootDrv], dl
+ call NAME(bs3InitLoadImage)
+%if 0
+ mov al, '='
+ call bs3PrintChrInAl
+%endif
+
+ ;
+ ; Call the user 'main' procedure (shouldn't return).
+ ;
+ cld
+ call BS3_SEL_TEXT16:0000h
+
+ ; Panic/hang.
+Bs3Panic:
+ cli
+ jmp Bs3Panic
+
+
+;; For debug and error handling.
+; @uses ax
+bs3PrintHexInAl:
+CPU 286
+ push ax
+ shr al, 4
+ call bs3PrintHexDigitInAl
+ pop ax
+bs3PrintHexDigitInAl:
+ and al, 0fh
+ cmp al, 10
+ jb .decimal
+ add al, 'a' - '0' - 10
+.decimal:
+ add al, '0'
+bs3PrintChrInAl:
+ push bx
+ mov ah, 0eh
+ mov bx, 0ff00h
+ int 10h
+ pop bx
+ ret
+
+
+;;
+; Loads the image off the floppy.
+;
+; This uses g_cLargeTotalSectors to figure out how much to load.
+;
+; Clobbers everything except ebp and esp. Panics on failure.
+;
+; @param dl The boot drive number (from BIOS).
+; @uses ax, cx, bx, esi, di
+;
+BEGINPROC bs3InitLoadImage
+ push bp
+ mov bp, sp
+ push es
+%define bSavedDiskNo byte [bp - 04h]
+ push dx
+%define bMaxSector byte [bp - 06h]
+%define wMaxSector word [bp - 06h]
+ xor ax, ax
+ push ax
+%define bMaxHead byte [bp - 08h]
+ push ax
+
+ ;
+ ; Try figure the geometry.
+ ;
+ mov ah, 08h
+ int 13h
+%ifndef HLT_ON_FAILURE
+ jc .failure
+%else
+ jnc .ok_geometry_call
+ cli
+ hlt
+.ok_geometry_call:
+%endif
+ and cl, 63 ; only the sector count.
+ mov bMaxSector, cl
+ mov bMaxHead, dh
+ mov dl, bSavedDiskNo
+
+%if 0 ; bMaxSector=0x12 (18); bMaxHead=0x01; bMaxCylinder=0x4f (79)
+ mov al, 'S'
+ call bs3PrintChrInAl
+ mov al, bMaxSector
+ call bs3PrintHexInAl
+ mov al, 'H'
+ call bs3PrintChrInAl
+ mov al, bMaxHead
+ call bs3PrintHexInAl
+ mov al, 'C'
+ call bs3PrintChrInAl
+ mov al, ch ; first 8-bit of cylinder count.
+ call bs3PrintHexInAl
+ mov al, ';'
+ call bs3PrintChrInAl
+%endif
+
+%ifndef BS3KIT_BOOTSECTOR_FASTER_LOAD
+ ;
+ ; Load the sectors following the boot sector one at a time (avoids problems).
+ ;
+ mov si, [g_cLargeTotalSectors] ; 16-bit sector count ==> max 512 * 65 535 = 33 553 920 bytes.
+ dec si ; Practically max: ca 575 KB, or 1150 sectors. Linker set BS3_MAX_SIZE to 480KB.
+
+ mov di, BS3_ADDR_LOAD / 16 ; The current load segment.
+ mov cx, 0002h ; ch/cylinder=0 (0-based); cl/sector=2 (1-based)
+ xor dh, dh ; dh/head=0
+.the_load_loop:
+ %if 0
+ mov al, 'c'
+ call bs3PrintChrInAl
+ mov al, ch
+ call bs3PrintHexInAl
+ mov al, 's'
+ call bs3PrintChrInAl
+ mov al, cl
+ call bs3PrintHexInAl
+ mov al, 'h'
+ call bs3PrintChrInAl
+ mov al, dh
+ call bs3PrintHexInAl
+ mov al, ';'
+ call bs3PrintChrInAl
+ %elifdef BS3KIT_BOOTSECTOR_LOAD_DOTS
+ mov al, '.'
+ call bs3PrintChrInAl
+ %endif
+ xor bx, bx
+ mov es, di ; es:bx -> buffer
+ mov ax, 0201h ; al=1 sector; ah=read function
+ int 13h
+ %ifndef HLT_ON_FAILURE
+ jc .failure
+ %else
+ jnc .read_ok
+ cli
+ hlt
+.read_ok:
+ %endif
+
+ ; advance to the next sector/head/cylinder.
+ inc cl
+ cmp cl, bMaxSector
+ jbe .adv_addr
+
+ mov cl, 1
+ inc dh
+ cmp dh, bMaxHead
+ jbe .adv_addr
+
+ mov dh, 0
+ inc ch
+
+.adv_addr:
+ add di, 512 / 16
+ dec si
+ jnz .the_load_loop
+
+%else ; BS3KIT_BOOTSECTOR_FASTER_LOAD
+ ;
+ ; Load the sectors following the boot sector, trying to load a whole
+ ; side in each bios call, falling back on single sector reads if we
+ ; run into DMA 64KB boundrary issues (BIOS must tell us).
+ ;
+ mov si, [g_cLargeTotalSectors] ; 16-bit sector count ==> max 512 * 65 535 = 33 553 920 bytes.
+ dec si ; Skip the boot sector, it's not part of the test image we execute.
+ mov di, BS3_ADDR_LOAD / 16 ; The current load segment.
+ mov cx, 0002h ; ch/cylinder=0 (0-based); cl/sector=0 (1-based)
+ xor dh, dh ; dh/head=0
+.the_load_loop:
+ %if 0
+ mov al, 'c'
+ call bs3PrintChrInAl
+ mov al, ch
+ call bs3PrintHexInAl
+ mov al, 's'
+ call bs3PrintChrInAl
+ mov al, cl
+ call bs3PrintHexInAl
+ mov al, 'h'
+ call bs3PrintChrInAl
+ mov al, dh
+ call bs3PrintHexInAl
+ mov al, ';'
+ call bs3PrintChrInAl
+ %elifdef BS3KIT_BOOTSECTOR_LOAD_DOTS
+ mov al, '.'
+ call bs3PrintChrInAl
+ %endif
+ mov ax, wMaxSector ; read to the end of the side by default.
+ sub al, cl
+ inc al
+.read_again:
+ cmp si, ax
+ jae .do_read
+ mov ax, si
+.do_read:
+ mov ah, 02h ; ah=read function
+ xor bx, bx
+ mov es, di ; es:bx -> buffer
+ int 13h
+ jnc .advance_sector
+
+ cmp ah, 9 ; DMA 64KB crossing error
+%if 0 ; This hack doesn't work. If the FDC is in single sided mode we end up with a garbled image. Probably "missing" sides.
+ je .read_one
+
+ cmp ah, 20h ; Controller error, probably because we're reading side 1 on a single sided floppy
+ jne .failure
+ cmp bMaxHead, 0
+ je .failure
+ cmp dh, 1
+ jne .failure
+ xor dh, dh
+ mov bMaxHead, dh
+ inc ch
+ jmp .the_load_loop
+.read_one:
+%elifdef HLT_ON_FAILURE
+ je .read_one_ok
+ cli
+ hlt
+.read_one_ok:
+%else
+ jne .failure
+%endif
+ mov ax, 1 ; Retry reading a single sector.
+ jmp .read_again
+
+ ; advance to the next sector/head/cylinder and address.
+.advance_sector:
+ inc cl
+ cmp cl, bMaxSector
+ jbe .adv_addr
+
+ mov cl, 1
+ inc dh
+ cmp dh, bMaxHead
+ jbe .adv_addr
+
+ mov dh, 0
+ inc ch
+
+.adv_addr:
+ dec si
+ jz .done_reading
+ add di, 512 / 16
+ dec al
+ jnz .advance_sector
+ jmp .the_load_loop
+
+.done_reading:
+%endif ; BS3KIT_BOOTSECTOR_FASTER_LOAD
+%if 0
+ mov al, 'D'
+ call bs3PrintChrInAl
+%elifdef BS3KIT_BOOTSECTOR_LOAD_DOTS
+ mov al, 13
+ call bs3PrintChrInAl
+ mov al, 10
+ call bs3PrintChrInAl
+%endif
+
+ add sp, 2*2
+ pop dx
+ pop es
+ pop bp
+ ret
+
+%ifndef HLT_ON_FAILURE
+ ;
+ ; Something went wrong, display a message.
+ ;
+.failure:
+ %if 1 ; Disable to save space for debugging.
+ %if 1
+ push ax
+ %endif
+
+ ; print message
+ mov si, .s_szErrMsg
+.failure_next_char:
+ lodsb
+ call bs3PrintChrInAl
+ cmp si, .s_szErrMsgEnd
+ jb .failure_next_char
+
+ ; panic
+ %if 1
+ pop ax
+ mov al, ah
+ push bs3PrintHexInAl
+ %endif
+ call Bs3Panic
+.s_szErrMsg:
+ db 13, 10, 'rd err! '
+ %else
+ hlt
+ jmp .failure
+ %endif
+%endif
+.s_szErrMsgEnd:
+;ENDPROC bs3InitLoadImage - don't want the padding.
+
+
+;
+; Pad the remainder of the sector with int3's and end it with the DOS signature.
+;
+bs3Padding:
+ times ( 510 - ( (bs3Padding - start) % 512 ) ) db 0cch
+ db 055h, 0aah
+