; $Id: bootsector2-vbinstst-kernel.asm 93115 2022-01-01 11:31:46Z vboxsync $
;; @file
; bootsector #2 kernel for big instruction testcases.
;   VBoxManage setextradata bs-vbinstst-64-1 VBoxInternal/Devices/VMMDev/0/Config/TestingEnabled  1
;

;
; Copyright (C) 2007-2022 Oracle Corporation
;
; This file is part of VirtualBox Open Source Edition (OSE), as
; available from http://www.virtualbox.org. This file is free software;
; you can redistribute it and/or modify it under the terms of the GNU
; General Public License (GPL) as published by the Free Software
; Foundation, in version 2 as it comes in the "COPYING" file of the
; VirtualBox OSE distribution. VirtualBox OSE is distributed in the
; hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
;
; The contents of this file may alternatively be used under the terms
; of the Common Development and Distribution License Version 1.0
; (CDDL) only, as it comes in the "COPYING.CDDL" file of the
; VirtualBox OSE 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.
;


;
; This is always the first include file.
;
%include "bootsector2-first.mac"

;
; Include and execute the init code.
;
        %define BS2_INIT_RM
        %define BS2_WITH_TRAPS
        %define BS2_WITHOUT_RAW_MODE ; causes troubles with PIC/floppy.

        %define BS2_INC_RM
        %define BS2_INC_PE16
        %define BS2_INC_PE32
        %define BS2_INC_PP16
        %define BS2_INC_PP32
        %define BS2_INC_PAE16
        %define BS2_INC_PAE32
        %define BS2_INC_LM16
        %define BS2_INC_LM32
        %define BS2_INC_LM64
        %include "bootsector2-common-init-code.mac"
        %include "bootsector2-api.mac"
        %include "iprt/formats/pe.mac"
        %include "iprt/formats/mz.mac"


BEGINCODE
BEGINPROC main
;
; Set up the runtime environment.
;
        call    Bs2EnableA20_r86

        ; 16-bit real mode.
%undef BS2_API_TEMPLATE_ACTION
%define BS2_API_TEMPLATE_ACTION(a_Name) mov dword [NAME(g_pfn %+ a_Name %+ _r86)], dword NAME(a_Name %+ _r86)
        BS2_API_TEMPLATE

        ; 16-bit protected mode.
%undef BS2_API_TEMPLATE_ACTION
%define BS2_API_TEMPLATE_ACTION(a_Name) mov word [NAME(g_pfn %+ a_Name %+ _p16)], word NAME(a_Name %+ _p16)
        BS2_API_TEMPLATE
        mov     eax, BS2_SEL_CS16
%undef BS2_API_TEMPLATE_ACTION
%define BS2_API_TEMPLATE_ACTION(a_Name) mov [NAME(g_pfn %+ a_Name %+ _p16) + 2], ax
        BS2_API_TEMPLATE

        ; 32-bit
%undef BS2_API_TEMPLATE_ACTION
%define BS2_API_TEMPLATE_ACTION(a_Name) mov dword [NAME(g_pfn %+ a_Name %+ _p32)], dword NAME(a_Name %+ _p32)
        BS2_API_TEMPLATE

        ; 64-bit
%undef BS2_API_TEMPLATE_ACTION
%define BS2_API_TEMPLATE_ACTION(a_Name) mov dword [NAME(g_pfn %+ a_Name %+ _p64)], dword NAME(a_Name %+ _p64)
        BS2_API_TEMPLATE
        xor eax, eax
%undef BS2_API_TEMPLATE_ACTION
%define BS2_API_TEMPLATE_ACTION(a_Name) mov dword [NAME(g_pfn %+ a_Name %+ _p64) + 4], eax
        BS2_API_TEMPLATE

        ; The magic markers and version number.
        mov     dword [g_u32Bs2ApiMagic],    BS2_API_MAGIC
        mov     dword [g_u32Bs2ApiEndMagic], BS2_API_MAGIC
        mov     dword [g_u32Bs2ApiVersion],  BS2_API_VERSION

;
; Load the extended image into high memory.
;
        mov     dl, [g_bBootDrv]
        call    NAME(bs2LoadBigImage)

;
; Hand control over to the extended image.
;
%ifdef BS2_BIG_IMAGE_LM64
        call    Bs2EnterMode_rm_lm64
BITS 64
        mov     eax, BS2_BIG_LOAD_ADDR
        call    rax
        call    Bs2ExitMode_lm64
BITS 16

%elifdef BS2_BIG_IMAGE_PP32
        call    Bs2EnterMode_rm_pp32
BITS 32
        mov     eax, BS2_BIG_LOAD_ADDR
        call    eax
        call    Bs2ExitMode_pp32
BITS 16

%elifdef BS2_BIG_IMAGE_PAE32
        call    Bs2EnterMode_rm_pae32
BITS 32
        mov     eax, BS2_BIG_LOAD_ADDR
        call    eax
        call    Bs2ExitMode_pae32
BITS 16

%else
        ;
        ; Probe the image, looking for an executable format we can deal with.
        ; Not doing a lot of checking here, but who cares right now...
        ;
        call    Bs2EnterMode_rm_pp32
BITS 32
        mov     eax, BS2_BIG_LOAD_ADDR
        cmp     word [eax], IMAGE_DOS_SIGNATURE
        jne     .not_dos
        add     eax, [eax + IMAGE_DOS_HEADER.e_lfanew]
.not_dos:
        cmp     dword [eax], IMAGE_NT_SIGNATURE
        je      .is_pe
        mov     eax, BS2_BIG_LOAD_ADDR
        jmp     .start_32

.is_pe:
        lea     edx, [eax + IMAGE_NT_HEADERS32.FileHeader]
        cmp     word [edx + IMAGE_FILE_HEADER.Machine], IMAGE_FILE_MACHINE_I386
        je      .is_pe32
        cmp     word [edx + IMAGE_FILE_HEADER.Machine], IMAGE_FILE_MACHINE_AMD64
        je      .is_pe64
        jmp     .panic_32

.is_pe32:
        add     edx, IMAGE_FILE_HEADER_size
        mov     eax, [edx + IMAGE_OPTIONAL_HEADER32.AddressOfEntryPoint]
        add     eax, BS2_BIG_LOAD_ADDR
        jmp     .start_32

.is_pe64:
        add     edx, IMAGE_FILE_HEADER_size
        mov     eax, [edx + IMAGE_OPTIONAL_HEADER64.AddressOfEntryPoint]
        add     eax, BS2_BIG_LOAD_ADDR
        jmp     .start_64

        ; Start executing at eax in 32-bit mode (current).
.start_32:
        call    eax
.panic_32:
        call    Bs2ExitMode_pp32
BITS 16
        jmp     .panic

        ; Start executing at eax in 64-bit mode.
BITS 32
.start_64:
        call    Bs2ExitMode_pp32
BITS 16
        call    Bs2EnterMode_rm_lm64
BITS 64
        mov     eax, eax
        call    rax
        call    Bs2ExitMode_lm64
BITS 16
        jmp     .panic

.panic:
%endif
        call    Bs2Panic
ENDPROC main




;;
; Loads the big image off the floppy.
;
; This uses the the_end label to figure out the starting offset.
; The length is assumed to be the whole floppy.
;
; Clobbers nothing, except for 68KB of memory beyond the_end.
;
; @param    dl          The boot drive number (from BIOS).
;
BITS 16
BEGINPROC bs2LoadBigImage
        push    ebp
        movzx   ebp, sp

%define bSavedDiskNo    byte [bp - 02h]
        push    dx
%define bMaxSector      byte [bp - 04h]
        push    0
%define bMaxHead        byte [bp - 06h]
        push    0
%define bMaxCylinder    byte [bp - 08h]
        push    0
%define pbHighDst       dword [bp - 0ch]
        push    dword BS2_BIG_LOAD_ADDR
%define SegTemp         word [bp - 0eh]
        push    0
%define fStatus         byte [bp - 10h]
        push    0

        push    es
        push    ds
        push    eax
        push    edx
        push    ecx
        push    ebx
        push    edi
        push    esi
        push    ebp

        ; Display message.
        push    cs
        push    .s_szLoadingBigImage
        call    PrintF_r86
        add     sp, 4


        ;
        ; Try figure the geometry.  This defines how much we'll read.
        ;
        mov     ah, 08h
        xor     di, di                  ; (es:di = 0000:0000 works around some buggy bioses, says wikipedia.)
        mov     es, di
        int     13h
        jc      .param_error
        mov     bMaxSector, cl          ; Do the cl[7:6]+ch stuff so we can address 255 sectors on the fake 63MB floppy.
        mov     bMaxHead, dh
        mov     bMaxCylinder, ch        ; See above.
        mov     dl, bSavedDiskNo
%if 0
        movzx   ax, bMaxCylinder
        push    ax
        movzx   cx, bMaxHead
        push    cx
        movzx   ax, bMaxSector
        push    ax
        push    ds
        push    .s_szDbgParam
        call    PrintF_r86
        jmp     .dprintf_param_done
.s_szDbgParam:
        db 13, 10, 'Floppy params max: sectors=%RX16 heads=%RX16 cylinders=%RX16', 13, 10, 0
.dprintf_param_done:
%endif

        ;
        ; Skip the kernel image (this could be done more efficiently, but this
        ; also does the trick).
        ;
        lea     eax, [dword the_end]
        sub     eax, start
        shr     eax, 9                  ; sectors to skip
        mov     cx, 0001h               ; sector (1-based), cylinder (0-based).
        xor     dh, dh                  ; head (0-based).
.skip_one_more:
        inc     cl
        cmp     cl, bMaxSector
        jbe     .decrement_sector_count

        mov     cl, 1
        inc     dh
        cmp     dh, bMaxHead            ; ASSUMES bMaxHead < 255.
        jbe     .decrement_sector_count

        mov     dh, 0
        inc     ch

.decrement_sector_count:
        dec     ax
        jnz     .skip_one_more



        ;
        ; Load loop. We load and copy 64 KB at the time into the high location.
        ; Fixed registers (above): dl=drive, cl[7:6]:ch=cylinder, dh=head, cl[5:0]=sector.
        ;
        lea     eax, [dword the_end + 0ffffh]
        and     eax, 0ffff0000h
        shr     eax, 4
        mov     SegTemp, ax             ; the 64KB segment we use for temporary storage.

.the_load_loop:
        mov     al, '.'
        call    PrintChr_r86

        ; Fill the segment with int3s (in case we don't read a full 64KB).
        mov     eax, 0cccccccch
        mov     di, SegTemp
        mov     es, di
        xor     edi, edi
        push    ecx
        cld
        mov     cx, 4000h
        rep stosd
        pop     ecx

        ;
        ; Load a bunch of sectors into the temp segment.
        ;
        xor     ebx, ebx
.the_sector_load_loop:
        ; Figure how many sectors we can read without switching track or side.
        movzx   ax, bMaxSector
        sub     al, cl
        inc     al                      ; al = sectors left to read in the current track on the current side.
        mov     di, bx
        shr     di, 9                   ; bx/512 = current sector offset.
        neg     di
        add     di, 10000h / 512        ; di = sectors left to read in the 64KB buffer.
        cmp     ax, di                  ; ax = min(ax, di)
        jbe     .use_ax_sector_count1
        mov     ax, di
.use_ax_sector_count1:
        cmp     ax, 64                  ; ax = min(ax,64) - Our BIOS limitation is 72, play safe.
        jbe     .use_ax_sector_count2
        mov     ax, 64
.use_ax_sector_count2:
        mov     di, ax                  ; save the number of sectors we read

        ; Do the reading.
%if 0
        push    bx
        push    ax
        push    dx
        push    cx
        push    cs
        push    .s_szDbgRead
        call    PrintF_r86
        jmp     .after_read_dprintf
.s_szDbgRead: db 'Reading CX=%RX16 DX=%RX16 AX=%RX16 BX=%RX16', 13, 10, 0
.after_read_dprintf:
%endif
        push    bx
        mov     ah, 02h                 ; ah=read function
        int     13h
        pop     bx
        jc      .read_error

        ; advance to the next sector/head/cylinder and address (lazy impl).
.advance_another_sector:
        cmp     cl, bMaxSector
        je      .next_head
        inc     cl
        jmp     .adv_addr

.next_head:
        mov     cl, 1
        cmp     dh, bMaxHead
        je      .next_cylinder
        inc     dh
        jmp     .adv_addr

.next_cylinder:
        mov     dh, 0
        cmp     ch, bMaxCylinder        ; No the cl[7:6]+ch stuff so we can address 255 sectors on the fake 63MB floppy.
        jb      .update_ch
        mov     fStatus, 1
        jmp     .move_block
.update_ch:
        inc     ch

.adv_addr:
        add     bx, 512
        dec     di
        jnz     .advance_another_sector

        test    bx, bx
        jnz     .the_sector_load_loop

.move_block:
        ;
        ; Copy the memory into high mem.
        ;
%if 0
        mov     edi, pbHighDst
        push    edi
        push    cs
        push    .s_szDbgMove
        call    PrintF_r86
        jmp     .after_move_dprintf
.s_szDbgMove: db 'Moving memory to EDI=%RX32', 13, 10, 0
.after_move_dprintf:
%endif

        push    ecx
        push    edx
        push    ds
        push    es
        call    Bs2EnterMode_rm_pp32
BITS 32
        ; Copy
        mov     edi, pbHighDst
        movzx   esi, SegTemp
        shl     esi, 4
        mov     ecx, 10000h / 4
        cld
        rep movsd

        ; Verify
        mov     edi, pbHighDst
        movzx   esi, SegTemp
        shl     esi, 4
        mov     ecx, 10000h / 4
        cld
        repe cmpsd
        je      .mem_verified_ok
        mov     fStatus, 2

.mem_verified_ok:
        mov     pbHighDst, edi

        call    Bs2ExitMode_pp32
BITS 16
        pop     es
        pop     ds
        pop     edx
        pop     ecx

        ; Continue reading and copying?
        cmp     fStatus, 0
        je      .the_load_loop

        ; Do we quit the loop on a failure?
        cmp     fStatus, 2
        je      .verify_failed_msg

        ;
        ; Done, so end the current message line.
        ;
        mov     al, 13
        call    PrintChr_r86
        mov     al, 10
        call    PrintChr_r86


        pop     esi
        pop     edi
        pop     ebx
        pop     ecx
        pop     edx
        pop     eax
        pop     ds
        pop     es
        mov     sp, bp
        pop     ebp
        ret


        ;
        ; Something went wrong, display a message.
        ;
.verify_failed_msg:
        mov     edi, pbHighDst
        push    edi
        push    cs
        push    .s_szVerifyFailed
        jmp     .print_message_and_panic

.param_error:
        push    ax
        push    cs
        push    .s_szParamError
        jmp     .print_message_and_panic

.read_error:
        push    ax
        push    cs
        push    .s_szReadError
        jmp     .print_message_and_panic

.print_message_and_panic:
        call    PrintF_r86
        call    Bs2Panic
        jmp     .print_message_and_panic

.s_szReadError:
        db 13, 10, 'Error reading: %RX8', 13, 10, 0
.s_szParamError:
        db 13, 10, 'Error getting params: %RX8', 13, 10, 0
.s_szVerifyFailed:
        db 13, 10, 'Failed to move block high... (%RX32) Got enough memory configured?', 13, 10, 0
.s_szLoadingBigImage:
        db 'Loading 2nd image.', 0
ENDPROC bs2LoadBigImage


;
; End sections and image.
;
%include "bootsector2-common-end.mac"

