diff options
Diffstat (limited to '')
-rw-r--r-- | src/VBox/ValidationKit/bootsectors/bs3kit/bs3-bootsector.asm | 594 |
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 + |