diff options
Diffstat (limited to '')
-rw-r--r-- | src/VBox/ValidationKit/bootsectors/bootsector2-common-init-code.mac | 2313 |
1 files changed, 2313 insertions, 0 deletions
diff --git a/src/VBox/ValidationKit/bootsectors/bootsector2-common-init-code.mac b/src/VBox/ValidationKit/bootsectors/bootsector2-common-init-code.mac new file mode 100644 index 00000000..a95217d5 --- /dev/null +++ b/src/VBox/ValidationKit/bootsectors/bootsector2-common-init-code.mac @@ -0,0 +1,2313 @@ +; $Id: bootsector2-common-init-code.mac $ +;; @file +; Common bootsector code init. +; +; In addition to initialize the stack at %7bf0 it loads the first 512KB of the +; floppy image at %7c00. The control is handed over with interrupts disabled +; to a 'main' function defined by the includer. +; +; The following defines controls the mode we call main in: +; - BS2_INIT_RM (default) +; - BS2_INIT_PE32 +; - BS2_INIT_PP32 +; - BS2_INIT_PAE32 +; - BS2_INIT_LM64 +; +; The following defines controls code inclusion: +; - BS2_INC_RM +; - BS2_INC_PE +; - BS2_INC_PE16 +; - BS2_INC_PE32 +; - BS2_INC_PEV86 +; - BS2_INC_PP +; - BS2_INC_PP16 +; - BS2_INC_PP32 +; - BS2_INC_PPV86 +; - BS2_INC_PAE +; - BS2_INC_PAE16 +; - BS2_INC_PAE32 +; - BS2_INC_PAEV86 +; - BS2_INC_LM +; - BS2_INC_LM16 +; - BS2_INC_LM32 +; - BS2_INC_LM64 +; - BS2_INC_CMN_R86 +; - BS2_INC_CMN_V86 +; - BS2_INC_CMN_P16 +; - BS2_INC_CMN_P32 +; - BS2_INC_CMN_P64 +; - BS2_WITH_TRAPS +; + +; +; 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 +; + + +; map files should include symbols and everything. +[map all] + +;******************************************************************************* +;* Header Files * +;******************************************************************************* +%include "bootsector2-structures.mac" +%include "bootsector2-common-macros-1.mac" +%include "VBox/bios.mac" + + +;******************************************************************************* +;* Defined Constants And Macros * +;******************************************************************************* +;; @name Static Memory Allocation +; @{ +;; The boot sector load address. +%define BS2_ADDR 07c00h +;; The stack is located before the code (and will overflow into the interrupt +; table and other essential system data). +%define STACK_ADDR (BS2_ADDR - 256) + +%ifdef BS2_WITH_TRAPS +;; The address of the ring-0 stack in bs2Tss32BitDf. +%define BS2_DF_R0_STACK_ADDR 06800h +;; The address of the ring-0 stack in TSSxx. +%define BS2_R0_STACK_ADDR 06000h +;; The address of the ring-1 stack in TSSxx. +%define BS2_R1_STACK_ADDR 05000h +;; The address of the ring-2 stack in TSSxx. +%define BS2_R2_STACK_ADDR 04800h +%endif ; BS2_WITH_TRAPS + +;; +; Where we save the boot registers during init. +%define BS2_REG_SAVE_ADDR 06000h + +;; The start of the memory area used for paging, stacks and so forth. +%define BS2_PXX_BASE 080000h + +;; The page map level 4 address (all entries point to BS2_LM_PDP_ADDR). +%define BS2_LM_PML4_ADDR 080000h +;; The long mode page directory pointer table address. +%define BS2_LM_PDP_ADDR 081000h +;; The PAE page directory pointer table address. +%define BS2_PAE_PDP_ADDR 082000h +;; The address of the 4 PAE page directories. Also used by long mode. +%define BS2_PAE_PD_ADDR 083000h +;; The address of the 32-bit page directory. +%define BS2_32B_PD_ADDR 087000h +;; User page table #0. +%define BS2_USER_PX_0_ADDR 088000h +;; User page table #1. +%define BS2_USER_PX_1_ADDR 089000h +;; User page table #2. +%define BS2_USER_PX_2_ADDR 08a000h +;; User page table #3. +%define BS2_USER_PX_3_ADDR 08b000h +;; User page table #4. +%define BS2_USER_PX_4_ADDR 08c000h +;; User page table #5. +%define BS2_USER_PX_5_ADDR 08d000h +;; User page table #6. +%define BS2_USER_PX_6_ADDR 08e000h +;; User page table #7. +%define BS2_USER_PX_7_ADDR 08f000h +;; The selector to use when accessing the PDP and PD from real mode. +%define BS2_PXX_SEL 08000h +;; Converts a BS2_P*_ADDR into a BS2_PXX_SEL selector offset. +%define BS2_PXX_OFF(Addr) ( (Addr) - (BS2_PXX_SEL * 16) ) + +;; The base address in the default address spaces of the range where we are +; free to muck about as much as we like. (This is a virtual address.) +%define BS2_MUCK_ABOUT_BASE 000400000h + +; We have some free space here 090000h...09a000h (stacks moved) + +;; The address of the LDT. +%define BS2_LDT_BASE 09b000h +;; The size of the LDT in bytes. +%define BS2_LDT_SIZE 001fffh + +;; The start of the memory area used for paging, stacks and so forth. +%define BS2_PXX_LAST 09ffffh +;; @} + + +;; +; @name Group of 32-bit, 16-bit and 64-bit selectors for one ring. +; @{ +%define BS2_SEL_GRP_CS32 00h +%define BS2_SEL_GRP_DS32 08h +%define BS2_SEL_GRP_SS32 10h +%define BS2_SEL_GRP_CS16 18h +%define BS2_SEL_GRP_DS16 20h +%define BS2_SEL_GRP_SS16 28h +%define BS2_SEL_GRP_CS64 30h +%define BS2_SEL_GRP_DS64 38h +%define BS2_SEL_GRP_SS64 38h +%define BS2_SEL_GRP_SIZE 40h +;; @} + + +;; Move to program. +%ifndef BS2_WITHOUT_RAW_MODE + %define BS2_WITH_RAW_MODE +%endif + +; Implicit code inclusion based on the init mode. +%ifdef BS2_INIT_PE32 + %define BS2_INC_PE32 +%elifdef BS2_INIT_PP32 + %define BS2_INC_PP32 +%elifdef BS2_INIT_PAE32 + %define BS2_INC_PAE32 +%elifdef BS2_INIT_LM64 + %define BS2_INC_LM64 +%else + %define BS2_INIT_RM ; the default + %define BS2_INC_RM +%endif + +; Aliases. +%ifdef BS2_INC_PE + %define BS2_INC_PE16 + %define BS2_INC_PE32 + %define BS2_INC_PEV86 +%endif +%ifdef BS2_INC_PP + %define BS2_INC_PP16 + %define BS2_INC_PP32 + %define BS2_INC_PPV86 +%endif +%ifdef BS2_INC_PAE + %define BS2_INC_PAE16 + %define BS2_INC_PAE32 + %define BS2_INC_PAEV86 +%endif +%ifdef BS2_INC_LM + %define BS2_INC_LM16 + %define BS2_INC_LM32 + %define BS2_INC_LM64 +%endif + +; Common code. +%ifdef BS2_INC_RM + %define BS2_INC_CMN_R86 +%endif +%ifdef BS2_INC_PE16 + %define BS2_INC_CMN_P16 + %define BS2_INC_CMN_PE + %define BS2_INC_CMN_PM +%endif +%ifdef BS2_INC_PE32 + %define BS2_INC_CMN_P32 + %define BS2_INC_CMN_PE + %define BS2_INC_CMN_PM +%endif +%ifdef BS2_INC_PEV86 + %define BS2_INC_CMN_R86 + %define BS2_INC_CMN_V86 + %define BS2_INC_CMN_PE + %define BS2_INC_CMN_PM +%endif +%ifdef BS2_INC_PP16 + %define BS2_INC_CMN_P16 + %define BS2_INC_CMN_PP + %define BS2_INC_CMN_PM +%endif +%ifdef BS2_INC_PP32 + %define BS2_INC_CMN_P32 + %define BS2_INC_CMN_PP + %define BS2_INC_CMN_PM +%endif +%ifdef BS2_INC_PPV86 + %define BS2_INC_CMN_R86 + %define BS2_INC_CMN_V86 + %define BS2_INC_CMN_PP + %define BS2_INC_CMN_PM +%endif +%ifdef BS2_INC_PAE16 + %define BS2_INC_CMN_P16 + %define BS2_INC_CMN_PAE + %define BS2_INC_CMN_PM + %define BS2_INC_CMN_PAE_LM +%endif +%ifdef BS2_INC_PAE32 + %define BS2_INC_CMN_P32 + %define BS2_INC_CMN_PAE + %define BS2_INC_CMN_PM + %define BS2_INC_CMN_PAE_LM +%endif +%ifdef BS2_INC_PAEV86 + %define BS2_INC_CMN_R86 + %define BS2_INC_CMN_V86 + %define BS2_INC_CMN_PAE + %define BS2_INC_CMN_PM + %define BS2_INC_CMN_PAE_LM +%endif +%ifdef BS2_INC_LM16 + %define BS2_INC_CMN_P16 + %define BS2_INC_CMN_LM + %define BS2_INC_CMN_PAE_LM +%endif +%ifdef BS2_INC_LM32 + %define BS2_INC_CMN_P32 + %define BS2_INC_CMN_LM + %define BS2_INC_CMN_PAE_LM +%endif +%ifdef BS2_INC_LM64 + %define BS2_INC_CMN_LM64 + %define BS2_INC_CMN_LM + %define BS2_INC_CMN_PAE_LM +%endif + + +; +; Misc defines. +; +;; The offset of the TSS32.CR3 field. +%define BS2_TSS32_CR3_OFF 01ch + + +;******************************************************************************* +;* Structures and Typedefs * +;******************************************************************************* + + +; +; Start with a jump just to follow the convention. +; Also declare all segments/sections to establish them and their order. +; + ORG BS2_ADDR + +section .text valign=16 align=16 progbits +section .data vfollows=.text follows=.text valign=16 align=16 progbits +section .texthigh vfollows=.data follows=.data valign=16 align=16 progbits +section .traprecs vfollows=.texthigh follows=.texthigh valign=8 align=8 progbits +section .end vfollows=.traprecs follows=.traprecs valign=512 align=512 progbits + +%define BEGINCODELOW section .text ;;< For 16-bit code. +%define BEGINCODEHIGH section .texthigh ;;< For 32-bit and 64-bit code. +%define BEGINEND section .end ;;< For aligning image to 512 bytes. + +BEGINCODELOW +BITS 16 +start: + jmp short bs2InitCode + nop + nop ; alignment + +; +; Abuse the bios parameter block area for data storage. +; +gdtr: + dw bs2GdtEnd - bs2Gdt - 1 ; limit 15:00 + dw bs2Gdt ; base 15:00 + db 0 ; base 23:16 + db 0 ; unused + +idtr_null: + dw 0 ; limit 15:00 + dw bs2Gdt ; base 15:00 + db 0 ; base 23:16 + db 0 ; unused + +%ifdef BS2_WITH_TRAPS + %ifdef BS2_INC_CMN_PM +idtr_32bit: + dw bs2Idt32bitEnd - bs2Idt32bit -1 ; limit 15:00 + dw bs2Idt32bit ; base 15:00 + db 0 ; base 23:16 + db 0 ; unused + %endif + + %ifdef BS2_INC_CMN_LM +idtr_64bit: + dw bs2Idt64bitEnd - bs2Idt64bit -1 ; limit 15:00 + dw bs2Idt64bit ; base 15:00 + db 0 ; base 23:16 + db 0 ; unused + %endif + +%elifdef BS2_WITH_RAW_MODE +idtr_dummy_32bit: + dw bs2DummyIdt32bitEnd - bs2DummyIdt32bit -1 ; limit 15:00 + dw bs2DummyIdt32bit ; base 15:00 + db 0 ; base 23:16 + db 0 ; unused +%endif + +idtr_real_mode: + dw 01ffh ; limit 15:00 + dw 0 ; base 15:00 + db 0 ; base 23:16 + db 0 ; unused + +g_achHex: + db '0123456789abcdef', 0 + +g_bBootDrv: + db 80h ; Not in the official BPB location, but whatever. +g_fCpuIntel: + db 0 +g_fCpuAmd: + db 0 + +bs2BpbPadding: + times 3dh - (bs2BpbPadding - start) db 0 + + +; +; Where to real init code starts. +; +bs2InitCode: + cli + +%ifdef BS2_INIT_SAVE_REGS + ; save the registers if we've been asked to do so. + mov [cs:BS2_REG_SAVE_ADDR + BS2REGS.rax], eax + mov [cs:BS2_REG_SAVE_ADDR + BS2REGS.rsp], esp + mov [cs:BS2_REG_SAVE_ADDR + BS2REGS.rbp], ebp + mov ax, ss + mov [cs:BS2_REG_SAVE_ADDR + BS2REGS.ss], ax + mov ax, ds + mov [cs:BS2_REG_SAVE_ADDR + BS2REGS.ds], ax + mov ax, es + mov [cs:BS2_REG_SAVE_ADDR + BS2REGS.es], ax + mov ax, fs + mov [cs:BS2_REG_SAVE_ADDR + BS2REGS.fs], ax + mov ax, gs +%endif + + ; set up the segment reisters and stack. + xor eax, eax + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + mov ss, ax + mov esp, STACK_ADDR + mov [esp], eax ; clear the first 16 bytes + mov [esp + 04h], eax + mov [esp + 08h], eax ; fake rbp. + mov [esp + 0ch], eax ; fake ebp and bp + mov ebp, esp + +%ifdef BS2_INIT_SAVE_REGS + ; Save more registers now that ds is known and the stack is usable. + pushfd + pop eax + mov [BS2_REG_SAVE_ADDR + BS2REGS.rflags], eax + mov [BS2_REG_SAVE_ADDR + BS2REGS.rbx], ebx + mov [BS2_REG_SAVE_ADDR + BS2REGS.rcx], ecx + mov [BS2_REG_SAVE_ADDR + BS2REGS.rdx], edx + mov [BS2_REG_SAVE_ADDR + BS2REGS.rsi], esi + mov [BS2_REG_SAVE_ADDR + BS2REGS.rdi], edi +%endif + + ; Make sure caching is enabled and alignment is off. + mov eax, cr0 +%ifdef BS2_INIT_SAVE_REGS + mov [BS2_REG_SAVE_ADDR + BS2REGS.cr0], eax +%endif + and eax, ~(X86_CR0_NW | X86_CR0_CD | X86_CR0_AM) + mov cr0, eax + + ; Load all the code. + call bs2InitLoadImage + mov [g_bBootDrv], dl + + ; Initialize the data structures for the included modes requiring this. +%ifdef BS2_INC_CMN_PP + call bs2InitPagedProtMode +%endif +%ifdef BS2_INC_CMN_PAE + call bs2InitPaeProtMode +%endif +%ifdef BS2_INC_CMN_LM + call bs2InitLongMode +%endif + + ; Entered the desired mode. +%ifdef BS2_INIT_PE32 + call Bs2EnterMode_rm_pe32 +BITS 32 +%endif +%ifdef BS2_INIT_PP32 + call Bs2EnterMode_rm_pp32 +BITS 32 +%endif +%ifdef BS2_INIT_PAE32 + call Bs2EnterMode_rm_pae32 +BITS 32 +%endif +%ifdef BS2_INIT_LM64 + call Bs2EnterMode_rm_lm64 +BITS 64 +%endif +%ifdef BS2_INIT_RM + call SetCpuModeGlobals_rm +%endif + +%ifdef BS2_WITH_RAW_MODE + ; + ; Mask interrupts and then set IF. + ; + mov al, 0ffh + out 021h, al + out 0a1h, al + sti +%endif + + jmp bs2DoneInit + + +;; +; Loads the image off the floppy. +; +; This uses the the_end label to figure out the length. For this to work +; cleanly the label must be aligned on a sector boundrary. Use BS2_PAD_IMAGE +; to make sure this is the case. +; +; Clobbers everything except ebp and esp. Panics on failure. +; +; @param dl The boot drive number (from BIOS). +; @uses ax, cx, bx, esi, di +; +BEGINCODELOW +BITS 16 +BEGINPROC bs2InitLoadImage + push bp + mov bp, sp + push es +%define bSavedDiskNo byte [bp - 04h] + push dx +%define bMaxSector byte [bp - 06h] + push 0 +%define bMaxHead byte [bp - 08h] + push 0 +%define bMaxCylinder byte [bp - 0ah] + push 0 + + ; + ; Try figure the geometry. + ; + mov ah, 08h + int 13h + jc .failure + mov bMaxSector, cl + mov bMaxHead, dh + mov bMaxCylinder, ch + mov dl, bSavedDiskNo + + ; + ; Reload all the sectors one at a time (avoids problems). + ; + lea esi, [dword the_end] + sub esi, start + shr esi, 9 ; si = number of sectors to load. + mov di, BS2_ADDR / 16 ; The current load segment. + mov cx, 0001h ; ch/cylinder=0 (0-based); cl/sector=1 (1-based) + xor dh, dh ; dh/head=0 +.the_load_loop: + xor bx, bx + mov es, di ; es:bx -> buffer + mov ax, 0201h ; al=1 sector; ah=read function + int 13h + jc .failure + + ; 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 + + add sp, 3*2 + pop dx + pop es + leave + ret + + ; + ; Something went wrong, display a message. + ; +.failure: + pusha + + ; print message + mov si, .s_szErrMsg + mov ah, 0eh + xor bx, bx +.failure_next_char: + lodsb + int 10h + cmp si, .s_szErrMsgEnd + jb .failure_next_char + + ; format the error number. + movzx bx, byte [bp - 2 - 1] ; read the ah of the pusha frame + shr bl, 4 + mov al, [bx + g_achHex] + int 10h + + movzx bx, byte [bp - 2 - 1] ; read the ah of the pusha frame + and bl, 0fh + mov al, [bx + g_achHex] + int 10h + + ; panic + popa + call Bs2Panic +.s_szErrMsg: + db 13, 10, 'read error: ' +.s_szErrMsgEnd: +ENDPROC bs2InitLoadImage + +;; Pads the image so bs2InitLoadImage can load it without trouble. +%macro BS2_PAD_IMAGE 0 +bs2PadImageLabel: +; times ( (512*18*2) - ( (bs2PadImageLabel - start) % (512*18*2) ) ) db 0 ; track aligned size. + times ( 512 - ( (bs2PadImageLabel - start) % 512 ) ) db 0 ; sector aligned size. +; times ( 10000h - BS2_ADDR - (bs2PadImageLabel - start) ) db 0 ; full segment 0 size. +%endmacro + + +;; +; Shutdown routine that will work in real and protected mode, providing +; that SS is valid that we can load it into DS. +; +; Does not return. +; +BEGINCODELOW +BITS 16 +BEGINPROC Bs2Shutdown + cli + mov bl, 64 + mov ax, ss + mov ds, ax + mov dx, VBOX_BIOS_SHUTDOWN_PORT + mov ax, VBOX_BIOS_OLD_SHUTDOWN_PORT +.retry: + mov ecx, 8 + mov esi, .s_szShutdown + rep outsb + xchg dx, ax ; alternate between the new (VBox) and old (Bochs) ports. + dec bl + jnz .retry + ; Shutdown failed! + jmp Bs2Panic +.s_szShutdown: + db 'Shutdown', 0 +ENDPROC Bs2Shutdown + + +;; +; Panic routine for real mode. +; +; Does not return. +; +BEGINCODELOW +BITS 16 +BEGINPROC Bs2Panic + cli +.hlt_again: + hlt + jmp .hlt_again +ENDPROC Bs2Panic + + +; +; Padd the remainder of the sector with zeros and +; end it with the dos signature. +; +bs2Padding: + times 510 - (bs2Padding - start) db 0 + db 055h, 0aah + + +; +; The GDT (X86DESCGENERIC). +; +align 8, db 0 +bs2Gdt: + dw 00000h, 00000h, 00000h, 00000h ; null selector +%define BS2_SEL_R0_BASE 08h +%define BS2_SEL_CS32 08h + dw 0ffffh, 00000h, 09b00h, 000cfh ; 32-bit flat code segment. +%define BS2_SEL_DS32 10h + dw 0ffffh, 00000h, 09300h, 000cfh ; 32-bit flat data segment. +%define BS2_SEL_SS32 18h + dw 0ffffh, 00000h, 09300h, 000cfh ; 32-bit flat stack segment. +%define BS2_SEL_CS16 20h + dw 0ffffh, 00000h, 09b00h, 00000h ; 16-bit code segment with base 0. +%define BS2_SEL_DS16 28h + dw 0ffffh, 00000h, 09300h, 00000h ; 16-bit data segment with base 0. +%define BS2_SEL_SS16 30h + dw 0ffffh, 00000h, 09300h, 00000h ; 16-bit stack segment with base 0. +%define BS2_SEL_CS64 38h + dw 0ffffh, 00000h, 09a00h, 000afh ; 64-bit code segment. +%define BS2_SEL_DS64 40h +%define BS2_SEL_SS64 40h + dw 0ffffh, 00000h, 09300h, 000afh ; 64-bit stack and data segment. + dw 00000h, 00000h, 00000h, 00000h ; Unused +%define BS2_SEL_MMIO16 50h +%define BS2_SEL_MMIO16_BASE 00df000h + dw 0ffffh, 0f000h, 0930dh, 00000h ; 16-bit VMMDev MMIO segment with base 0df000h. + dw 00000h, 00000h, 00000h, 00000h ; Unused +%define BS2_SEL_LDT 60h ; LDT usage requires manual LLDT and setting up. + dw BS2_LDT_SIZE, BS2_LDT_BASE & 0xffff, 08200h | ((BS2_LDT_BASE >> 16) & 0xff), 00000h + dw 00000h, 00000h, 00000h, 00000h ; zero for 64-bit mode. +%define BS2_SEL_CS16_EO 70h + dw 0fffeh, 00000h, 09800h, 00000h ; 16-bit code segment with base 0, not accessed, execute only, short limit. + dw 00000h, 00000h, 00000h, 00000h ; unused. +%ifdef BS2_WITH_TRAPS + %ifdef BS2_INC_CMN_PM + %define BS2_SEL_TSS32 80h + dw (bs2Tss32BitEnd - bs2Tss32Bit) - 1 ; 32-bit TSS. + dw bs2Tss32Bit + db 0 + db X86_SEL_TYPE_SYS_386_TSS_AVAIL | 0x80 + dw 0 + %define BS2_SEL_TSS32_DF 88h + dw (bs2Tss32BitDfEnd - bs2Tss32BitDf) - 1; 32-bit TSS, double fault. + dw bs2Tss32BitDf + db 0 + db X86_SEL_TYPE_SYS_386_TSS_AVAIL | 0x80 + dw 0 + %else + dw 00000h, 00000h, 00000h, 00000h + dw 00000h, 00000h, 00000h, 00000h + %endif + %ifdef BS2_INC_CMN_LM + %define BS2_SEL_TSS64 90h + dw (bs2Tss64BitEnd - bs2Tss64Bit) - 1 ; 32-bit TSS. + dw bs2Tss64Bit + db 0 + db AMD64_SEL_TYPE_SYS_TSS_AVAIL | 0x80 + dw 0 + dw 00000h, 00000h, 00000h, 00000h ; 2nd half of the 64-bit selector (not necessary). + %else + dw 00000h, 00000h, 00000h, 00000h + dw 00000h, 00000h, 00000h, 00000h + %endif +%endif + ; Ring-1 selectors. +%define BS2_SEL_R1_BASE 0a0h +%define BS2_SEL_R1_CS32 0a0h + dw 0ffffh, 00000h, 0bb00h, 000cfh ; Ring-1 32-bit flat code segment. +%define BS2_SEL_R1_DS32 0a8h + dw 0ffffh, 00000h, 0b300h, 000cfh ; Ring-1 32-bit flat data segment. +%define BS2_SEL_R1_SS32 0b0h + dw 0ffffh, 00000h, 0b300h, 000cfh ; Ring-1 32-bit flat stack segment. +%define BS2_SEL_R1_CS16 0b8h + dw 0ffffh, 00000h, 0bb00h, 00000h ; Ring-1 16-bit code segment with base 0. +%define BS2_SEL_R1_DS16 0c0h + dw 0ffffh, 00000h, 0b300h, 00000h ; Ring-1 16-bit data segment with base 0. +%define BS2_SEL_R1_SS16 0c8h + dw 0ffffh, 00000h, 0b300h, 00000h ; Ring-1 16-bit stack segment with base 0. +%define BS2_SEL_R1_CS64 0d0h + dw 0ffffh, 00000h, 0ba00h, 000afh ; Ring-1 64-bit code segment. +%define BS2_SEL_R1_DS64 0d8h +%define BS2_SEL_R1_SS64 0d8h + dw 0ffffh, 00000h, 0b300h, 000afh ; Ring-1 64-bit stack and data segment. + + ; Ring-2 selectors. +%define BS2_SEL_R2_BASE 0e0h +%define BS2_SEL_R2_CS32 0e0h + dw 0ffffh, 00000h, 0db00h, 000cfh ; Ring-2 32-bit flat code segment. +%define BS2_SEL_R2_DS32 0e8h + dw 0ffffh, 00000h, 0d300h, 000cfh ; Ring-2 32-bit flat data segment. +%define BS2_SEL_R2_SS32 0f0h + dw 0ffffh, 00000h, 0d300h, 000cfh ; Ring-2 32-bit flat stack segment. +%define BS2_SEL_R2_CS16 0f8h + dw 0ffffh, 00000h, 0db00h, 00000h ; Ring-2 16-bit code segment with base 0. +%define BS2_SEL_R2_DS16 0f0h + dw 0ffffh, 00000h, 0d300h, 00000h ; Ring-2 16-bit data segment with base 0. +%define BS2_SEL_R2_SS16 108h + dw 0ffffh, 00000h, 0d300h, 00000h ; Ring-2 16-bit stack segment with base 0. +%define BS2_SEL_R2_CS64 110h + dw 0ffffh, 00000h, 0da00h, 000afh ; Ring-2 64-bit code segment. +%define BS2_SEL_R2_DS64 118h +%define BS2_SEL_R2_SS64 118h + dw 0ffffh, 00000h, 0d300h, 000afh ; Ring-2 64-bit stack and data segment. + + ; Ring-3 selectors. +%define BS2_SEL_R3_BASE 120h +%define BS2_SEL_R3_CS32 120h + dw 0ffffh, 00000h, 0fb00h, 000cfh ; Ring-3 32-bit flat code segment. +%define BS2_SEL_R3_DS32 128h + dw 0ffffh, 00000h, 0f300h, 000cfh ; Ring-3 32-bit flat data segment. +%define BS2_SEL_R3_SS32 130h + dw 0ffffh, 00000h, 0f300h, 000cfh ; Ring-3 32-bit flat stack segment. +%define BS2_SEL_R3_CS16 138h + dw 0ffffh, 00000h, 0fb00h, 00000h ; Ring-3 16-bit code segment with base 0. +%define BS2_SEL_R3_DS16 140h + dw 0ffffh, 00000h, 0f300h, 00000h ; Ring-3 16-bit data segment with base 0. +%define BS2_SEL_R3_SS16 148h + dw 0ffffh, 00000h, 0f300h, 00000h ; Ring-3 16-bit stack segment with base 0. +%define BS2_SEL_R3_CS64 150h + dw 0ffffh, 00000h, 0fa00h, 000afh ; Ring-1 64-bit code segment. +%define BS2_SEL_R3_DS64 158h +%define BS2_SEL_R3_SS64 158h + dw 0ffffh, 00000h, 0f300h, 000afh ; Ring-1 64-bit stack and data segment. + + ; Here follows a bunch of spare GDT entries for (ab)use in testing. +%define BS2_SEL_SPARE0 160h +bs2GdtSpare0: + dq 0 +%define BS2_SEL_SPARE1 (BS2_SEL_SPARE0 + 08h) +bs2GdtSpare1: + dq 0 +%define BS2_SEL_SPARE2 (BS2_SEL_SPARE0 + 10h) +bs2GdtSpare2: + dq 0 +%define BS2_SEL_SPARE3 (BS2_SEL_SPARE0 + 18h) +bs2GdtSpare3: + dq 0 +bs2GdtEnd: + + +%ifndef BS2_WITH_TRAPS + %ifdef BS2_WITH_RAW_MODE +; +; Dummy 32-bit IDT for making CSAM happy. +; +align 16, db 0 +bs2DummyIdt32bit: + dw 0, 0, 0, 0 + dw 0, 0, 0, 0 + dw 0, 0, 0, 0 + dw 0, 0, 0, 0 +bs2DummyIdt32bitEnd + %endif +%endif + + +; +; Mode initialization routines. +; + +%ifdef BS2_INC_CMN_PP +;; +; Initializes the paged protected mode structures during init. +; +; @uses ebx, esi +; +BEGINCODELOW +BITS 16 +BEGINPROC bs2InitPagedProtMode + push ds + + ; + ; Create a paging hierarchy + ; + mov bx, BS2_PXX_SEL + mov ds, bx + mov ebx, BS2_PXX_OFF(BS2_32B_PD_ADDR) + xor esi, esi ; physical address + + ; The page directory. +.pd_loop: + mov dword [bx], esi + or word [bx], X86_PDE4M_P | X86_PDE4M_RW | X86_PDE4M_PS | X86_PDE4M_US + add esi, _4M + add bx, 4 + test bx, 0fffh + jnz .pd_loop + +%ifdef BS2_WITH_RAW_MODE + ; + ; Make sure there is some free space for the hypervisor near the top + ; of the address space (last 4MB is mapped). + ; + and byte [bx - 08h], 0feh + and byte [bx - 0ch], 0feh + and byte [bx - 10h], 0feh + and byte [bx - 14h], 0feh +%endif + + pop ds + ret +ENDPROC bs2InitPagedProtMode +%endif ; BS2_INC_CMN_PP + + +%ifdef BS2_INC_CMN_PAE_LM +;; +; Initializes the PAE page directories. +; +; Assumes ds is set to BS2_PXX_SEL already and that edx is zero. +; +; @uses ebx, esi +; @internal +; +BEGINPROC bs2InitPaePageDirs + mov esi, X86_PDE4M_P | X86_PDE4M_RW | X86_PDE4M_PS | X86_PDE4M_US + mov ebx, BS2_PXX_OFF(BS2_PAE_PD_ADDR) +.pd_loop: + mov [bx], esi + mov [bx + 4], edx + add esi, _2M + add bx, 8 + test bx, 0fffh + jnz .pd_loop + cmp bx, BS2_PXX_OFF(BS2_PAE_PD_ADDR + 4*_4K) + jne .pd_loop + +%ifdef BS2_WITH_RAW_MODE + ; + ; Make sure there is some free space for the hypervisor near the top + ; of the address space (last 4MB is mapped). + ; + and byte [bx - 10h], 0feh + and byte [bx - 18h], 0feh + and byte [bx - 20h], 0feh + and byte [bx - 28h], 0feh +%endif + + ret +ENDPROC bs2InitPaePageDirs +%endif ; BS2_INC_CMN_PAE_LM + + +%ifdef BS2_INC_CMN_PAE +;; +; Initializes the PAE protected mode structures during init. +; +; @uses edx, ebx, esi. +; +BEGINCODELOW +BITS 16 +BEGINPROC bs2InitPaeProtMode + push ds + + mov dx, BS2_PXX_SEL + mov ds, dx + xor edx, edx + + ; + ; Join paths with long mode. + ; + call bs2InitPaePageDirs + + ; + ; Create the page directory pointer table. + ; + mov ebx, BS2_PXX_OFF(BS2_PAE_PDP_ADDR) + mov dword [bx], (BS2_PAE_PD_ADDR ) | X86_PDPE_P + mov dword [bx + 04h], edx + mov dword [bx + 08h], (BS2_PAE_PD_ADDR + 1000h) | X86_PDPE_P + mov dword [bx + 0ch], edx + mov dword [bx + 10h], (BS2_PAE_PD_ADDR + 2000h) | X86_PDPE_P + mov dword [bx + 14h], edx + mov dword [bx + 18h], (BS2_PAE_PD_ADDR + 3000h) | X86_PDPE_P + mov dword [bx + 1ch], edx + + pop ds + ret +ENDPROC bs2InitPaeProtMode +%endif ; BS2_INC_CMN_PAE + + +%ifdef BS2_INC_CMN_LM +;; +; Initializes the long mode structures during init. +; +; @uses edx, ebx, esi +; +BEGINCODELOW +BITS 16 +BEGINPROC bs2InitLongMode + push ds + + mov dx, BS2_PXX_SEL + mov ds, dx + xor edx, edx + + ; + ; Join paths with the PAE code. + ; + call bs2InitPaePageDirs + + ; + ; Create the long mode page directory pointer table. + ; + mov ebx, BS2_PXX_OFF(BS2_LM_PDP_ADDR) +.pdptr_loop: + mov dword [bx], (BS2_PAE_PD_ADDR ) | X86_PDPE_P | X86_PDPE_RW | X86_PDPE_US + mov dword [bx + 04h], edx + mov dword [bx + 08h], (BS2_PAE_PD_ADDR + 1000h) | X86_PDPE_P | X86_PDPE_RW | X86_PDPE_US + mov dword [bx + 0ch], edx + mov dword [bx + 10h], (BS2_PAE_PD_ADDR + 2000h) | X86_PDPE_P | X86_PDPE_RW | X86_PDPE_US + mov dword [bx + 14h], edx + mov dword [bx + 18h], (BS2_PAE_PD_ADDR + 3000h) | X86_PDPE_P | X86_PDPE_RW | X86_PDPE_US + mov dword [bx + 1ch], edx + add bx, 20h + test bx, 0fffh + jnz .pdptr_loop + + ; + ; Set up the page map level 4 table, all entries mapping the same PDPTR. + ; + mov ebx, BS2_PXX_OFF(BS2_LM_PML4_ADDR) +.pml4_loop: + mov dword [bx], BS2_LM_PDP_ADDR | X86_PML4E_P | X86_PML4E_RW | X86_PML4E_US + mov dword [bx + 4], edx + add bx, 8 + test bx, 0fffh + jnz .pml4_loop + + pop ds + ret +ENDPROC bs2InitLongMode +%endif ; BS2_INC_CMN_LM + + + +; +; Routines for entering the different modes. +; + +%ifdef BS2_INC_RM +;; +; Dummy. +BEGINCODELOW +BITS 16 +BEGINPROC Bs2EnterMode_rm_rm + ret +ENDPROC Bs2EnterMode_rm_rm +%endif ; BS2_INC_RM + + +%ifdef BS2_INC_PE16 +;; +; Enters unpaged protected mode from real mode (cs = 0). +; +; @returns cs,ds,ss,es,gs,fs loaded with 16-bit selectors. +; ebp and esp converted to 32/16-bit. +; All other registers are preserved. +; @uses nothing +; +BEGINCODELOW +BITS 16 +BEGINPROC Bs2EnterMode_rm_pe16 + push eax + pushfd + cli +%ifndef BS2_NOINC_COMMON + push word SetCpuModeGlobals_pe16 +%endif + + ; + ; Switch to protected mode. + ; + xor ax, ax + mov ds, ax + lgdt [gdtr] +%ifdef BS2_WITH_TRAPS + lidt [idtr_32bit] +%elifdef BS2_WITH_RAW_MODE + lidt [idtr_dummy_32bit] +%else + lidt [idtr_null] +%endif + + mov eax, cr0 + or eax, X86_CR0_PE + and eax, 0ffffffffh - X86_CR0_PG + mov cr0, eax + jmp far BS2_SEL_CS16:bs2ProtModeCode16Start_p16 +ENDPROC Bs2EnterMode_rm_pe16 +%endif ; BS2_INC_PE16 + + +%ifdef BS2_INC_PE32 +;; +; Enters unpaged protected mode from real mode (cs = 0). +; +; @returns cs,ds,ss,es,gs,fs loaded with 32-bit selectors. +; ebp and esp converted to 32-bit. +; All other registers are preserved. +; @uses nothing +; +BEGINCODELOW +BITS 16 +BEGINPROC Bs2EnterMode_rm_pe32 + push word 0 + push eax + pushfd + cli +%ifndef BS2_NOINC_COMMON + push dword SetCpuModeGlobals_pe32 +%endif + + ; Do the mode switch. + xor ax, ax + mov ds, ax + lgdt [gdtr] +%ifdef BS2_WITH_TRAPS + lidt [idtr_32bit] +%elifdef BS2_WITH_RAW_MODE + lidt [idtr_dummy_32bit] +%else + lidt [idtr_null] +%endif + + mov eax, cr0 + or eax, X86_CR0_PE + and eax, 0ffffffffh - X86_CR0_PG + mov cr0, eax + jmp far BS2_SEL_CS32:bs2ProtModeCode32Start_p32 +ENDPROC Bs2EnterMode_rm_pe32 +%endif ; BS2_INC_PE32 + + +;; @todo BS2_INC_PEV86 + + +%ifdef BS2_INC_PP16 +;; +; Enters paged protected mode from real mode (cs = 0). +; +; @returns cs,ds,ss,es,gs,fs loaded with 16-bit selectors. +; ebp and esp converted to 16/32-bit. +; All other registers are preserved. +; @uses nothing +; +BEGINCODELOW +BITS 16 +BEGINPROC Bs2EnterMode_rm_pp16 + push eax + pushfd + cli +%ifndef BS2_NOINC_COMMON + push word SetCpuModeGlobals_pp16 +%endif + + ; Do the mode switch. + xor ax, ax + mov ds, ax + lgdt [gdtr] +%ifdef BS2_WITH_TRAPS + lidt [idtr_32bit] +%elifdef BS2_WITH_RAW_MODE + lidt [idtr_dummy_32bit] +%else + lidt [idtr_null] +%endif + + mov eax, BS2_32B_PD_ADDR + mov cr3, eax +%ifdef BS2_WITH_TRAPS + mov [bs2Tss32BitDf + BS2_TSS32_CR3_OFF], eax +%endif + + mov eax, cr4 + or eax, X86_CR4_PSE + and eax, ~X86_CR4_PAE + mov cr4, eax + + mov eax, cr0 + or eax, X86_CR0_PE | X86_CR0_PG | X86_CR0_WP + mov cr0, eax + jmp far BS2_SEL_CS16:bs2ProtModeCode16Start_p16 +ENDPROC Bs2EnterMode_rm_pp16 +%endif ; BS2_INC_PP16 + + +%ifdef BS2_INC_PP32 +;; +; Enters paged protected mode from real mode (cs = 0). +; +; @returns cs,ds,ss,es,gs,fs loaded with 32-bit selectors. +; ebp and esp converted to 32-bit. +; All other registers are preserved. +; @uses nothing +; +BEGINCODELOW +BITS 16 +BEGINPROC Bs2EnterMode_rm_pp32 + push word 0 + push eax + pushfd + cli +%ifndef BS2_NOINC_COMMON + push dword SetCpuModeGlobals_pp32 +%endif + + ; Do the mode switch. + xor ax, ax + mov ds, ax + lgdt [gdtr] +%ifdef BS2_WITH_TRAPS + lidt [idtr_32bit] +%elifdef BS2_WITH_RAW_MODE + lidt [idtr_dummy_32bit] +%else + lidt [idtr_null] +%endif + + mov eax, BS2_32B_PD_ADDR + mov cr3, eax +%ifdef BS2_WITH_TRAPS + mov [bs2Tss32BitDf + BS2_TSS32_CR3_OFF], eax +%endif + + mov eax, cr4 + or eax, X86_CR4_PSE + and eax, ~X86_CR4_PAE + mov cr4, eax + + mov eax, cr0 + or eax, X86_CR0_PE | X86_CR0_PG | X86_CR0_WP + mov cr0, eax + jmp far BS2_SEL_CS32:bs2ProtModeCode32Start_p32 +ENDPROC Bs2EnterMode_rm_pp32 +%endif ; BS2_INC_PP16 + + +;; @todo BS2_INC_PPV86 + + +%ifdef BS2_INC_PAE16 +;; +; Enters PAE protected mode from real mode (cs = 0). +; +; @returns cs,ds,ss,es,gs,fs loaded with 16-bit selectors. +; ebp and esp converted to 16/32-bit. +; All other registers are preserved. +; @uses nothing +; +BEGINCODELOW +BITS 16 +BEGINPROC Bs2EnterMode_rm_pae16 + push eax + pushfd + cli +%ifndef BS2_NOINC_COMMON + push word SetCpuModeGlobals_pae16 +%endif + + ; Do the mode switch. + xor ax, ax + mov ds, ax + lgdt [gdtr] +%ifdef BS2_WITH_TRAPS + lidt [idtr_32bit] +%elifdef BS2_WITH_RAW_MODE + lidt [idtr_dummy_32bit] +%else + lidt [idtr_null] +%endif + + mov eax, BS2_PAE_PDP_ADDR + mov cr3, eax +%ifdef BS2_WITH_TRAPS + mov [bs2Tss32BitDf + BS2_TSS32_CR3_OFF], eax +%endif + + mov eax, cr4 + or eax, X86_CR4_PAE | X86_CR4_PSE + mov cr4, eax + + mov eax, cr0 + or eax, X86_CR0_PE | X86_CR0_PG | X86_CR0_WP + mov cr0, eax + jmp far BS2_SEL_CS16:bs2ProtModeCode16Start_p16 +ENDPROC Bs2EnterMode_rm_pae16 +%endif ; BS2_INC_PAE16 + + +%ifdef BS2_INC_PAE32 +;; +; Enters PAE protected mode from real mode (cs = 0). +; +; @returns cs,ds,ss,es,gs,fs loaded with 32-bit selectors. +; ebp and esp converted to 32-bit. +; All other registers are preserved. +; @uses nothing +; +BEGINCODELOW +BITS 16 +BEGINPROC Bs2EnterMode_rm_pae32 + push word 0 + push eax + pushfd + cli +%ifndef BS2_NOINC_COMMON + push dword SetCpuModeGlobals_pae32 +%endif + + ; Do the mode switch. + xor ax, ax + mov ds, ax + lgdt [gdtr] +%ifdef BS2_WITH_TRAPS + lidt [idtr_32bit] +%elifdef BS2_WITH_RAW_MODE + lidt [idtr_dummy_32bit] +%else + lidt [idtr_null] +%endif + + mov eax, BS2_PAE_PDP_ADDR + mov cr3, eax +%ifdef BS2_WITH_TRAPS + mov [bs2Tss32BitDf + BS2_TSS32_CR3_OFF], eax +%endif + + mov eax, cr4 + or eax, X86_CR4_PAE | X86_CR4_PSE + mov cr4, eax + + mov eax, cr0 + or eax, X86_CR0_PE | X86_CR0_PG | X86_CR0_WP + mov cr0, eax + jmp far BS2_SEL_CS32:bs2ProtModeCode32Start_p32 +ENDPROC Bs2EnterMode_rm_pae32 +%endif ; BS2_INC_PAE32 + + +;; @todo BS2_INC_PAEV86 + + +%ifdef BS2_INC_LM16 +;; +; Enters long mode from real mode (cs = 0). +; +; @returns cs,ds,ss,es,gs,fs loaded with 16-bit selectors. +; rbp and rsp converted to 16/32/64-bit. +; All other registers are preserved. +; @uses nothing +; +BEGINCODELOW +BITS 16 +BEGINPROC Bs2EnterMode_rm_lm16 + call Bs2EnterMode_rm_lm64 +BITS 64 + call Bs2Thunk_lm64_lm16 +BITS 16 + ret +ENDPROC Bs2EnterMode_rm_lm16 +%endif ; BS2_INC_LM16 + + +%ifdef BS2_INC_LM32 +;; +; Enters long mode from real mode (cs = 0). +; +; @returns cs,ds,ss,es,gs,fs loaded with 32-bit selectors. +; rbp and rsp converted to 16/32/64-bit. +; All other registers are preserved. +; @uses nothing +; +BEGINCODELOW +BITS 16 +BEGINPROC Bs2EnterMode_rm_lm32 + ; Change the return address into a 32-bit one. + push word 0 ; Reserved 2 extra bytes for 32-bit return address. + push eax ; Save eax. + movzx eax, word [esp + 6h] ; Read narrow return address. + mov [esp + 4h], eax ; Store wide return address. + pop eax ; Restore eax. + + ; Do the mode switch and thunking. + call Bs2EnterMode_rm_lm64 +BITS 64 + call Bs2Thunk_lm64_lm32 +BITS 32 + ret +ENDPROC Bs2EnterMode_rm_lm32 +%endif ; BS2_INC_LM32 + + +%ifdef BS2_INC_LM64 +;; +; Enters long mode from real mode (cs = 0). +; +; @returns cs,ds,ss,es,gs,fs loaded with 64-bit selectors. +; rbp and rsp converted to 64-bit. +; All other registers are preserved. +; @uses nothing +; +BEGINCODELOW +BITS 16 +BEGINPROC Bs2EnterMode_rm_lm64 + push word 0 ; reserve bytes for 64-bit return address. + push dword 0 + push dword 0 ; rax + push eax + push dword 0 ; rcx + push ecx + push dword 0 ; rdx + push edx + push dword 0 ; rflags + pushfd + cli + + ; Fix the return address. + mov ax, [esp + 20h + 6h] + mov word [esp + 20h + 6h], 0 + mov [esp + 20h], ax + + ; + ; Switch to long mode. + ; + xor ax, ax + mov ds, ax + lgdt [gdtr] +%ifdef BS2_WITH_TRAPS + lidt [idtr_64bit] +%else + lidt [idtr_null] +%endif + + mov eax, BS2_LM_PML4_ADDR + mov cr3, eax + + mov eax, cr4 + or eax, X86_CR4_PAE | X86_CR4_PSE + mov cr4, eax + + mov ecx, MSR_K6_EFER + rdmsr + or eax, MSR_K6_EFER_LME + wrmsr + + mov eax, cr0 + or eax, X86_CR0_PE | X86_CR0_PG | X86_CR0_WP + mov cr0, eax + jmp far BS2_SEL_CS64:.code64_start + +BITS 64 +.code64_start: + mov eax, BS2_SEL_DS64 + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + mov ax, BS2_SEL_SS64 + mov ss, ax +%ifdef BS2_WITH_TRAPS + btr dword [bs2Gdt + BS2_SEL_TSS64 + 32/8], 1+8 ; busy -> avail + %ifndef BS2_WITH_MANUAL_LTR + mov ax, BS2_SEL_TSS64 + ltr ax + %endif +%endif + + and ebp, 0ffffh + and esp, 0ffffh + and edi, 0ffffffffh + and esi, 0ffffffffh + and ebx, 0ffffffffh + +%ifndef BS2_NOINC_COMMON + call SetCpuModeGlobals_lm64 +%endif + + popf + pop rdx + pop rcx + pop rax + ret +ENDPROC Bs2EnterMode_rm_lm64 +%endif ; BS2_INC_PAE32 + + + +%ifdef BS2_INC_CMN_P16 +;; +; Code shared by the three 16-bit protected mode switchers. +; +; @internal +; +BEGINCODELOW +BITS 16 +BEGINPROC bs2ProtModeCode16Start_p16 + ; Initialize the registers. + mov ax, BS2_SEL_DS16 + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + mov ax, BS2_SEL_SS16 + mov ss, ax + and esp, 0ffffh + and ebp, 0ffffh +%ifdef BS2_WITH_TRAPS + btr word [bs2Gdt + BS2_SEL_TSS32 + 32/8], 1+8 ; busy -> avail + btr word [bs2Gdt + BS2_SEL_TSS32_DF + 32/8], 1+8 ; busy -> avail + %ifndef BS2_WITH_MANUAL_LTR + mov ax, BS2_SEL_TSS32 + ltr ax + %endif +%endif + +%ifndef BS2_NOINC_COMMON + ; Set up mode specific global variables. + pop ax + call ax +%endif + + popfd + pop eax + ret +ENDPROC bs2ProtModeCode16Start_p16 +%endif ; BS2_INC_CMN_P16 + + +%ifdef BS2_INC_CMN_P32 +;; +; Code shared by the three 32-bit protected mode switchers. +; +; @internal +; +BEGINCODELOW +BITS 32 +BEGINPROC bs2ProtModeCode32Start_p32 + ; Initialize the registers. + mov ax, BS2_SEL_DS32 + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + mov ax, BS2_SEL_SS32 + mov ss, ax + and esp, 0ffffh + and ebp, 0ffffh +%ifdef BS2_WITH_TRAPS + btr dword [bs2Gdt + BS2_SEL_TSS32 + 32/8], 1+8 ; busy -> avail + btr dword [bs2Gdt + BS2_SEL_TSS32_DF + 32/8], 1+8 ; busy -> avail + %ifndef BS2_WITH_MANUAL_LTR + mov ax, BS2_SEL_TSS32 + ltr ax + %endif +%endif + +%ifndef BS2_NOINC_COMMON + ; Set up mode specific global variables. + pop eax + call eax +%endif + + ; Make the return address 32-bit and then return. + movzx eax, word [esp + 0ah] + mov [esp + 8h], eax + popfd + pop eax + ret +ENDPROC bs2ProtModeCode32Start_p32 +%endif ; BS2_INC_CMN_P32 + + + +; +; Routines for exitting the different modes. +; + + +%ifdef BS2_INC_RM +;; +; Dummy. +BEGINCODELOW +BITS 16 +BEGINPROC Bs2ExitMode_rm + ret +ENDPROC Bs2ExitMode_rm +%endif ; BS2_INC_RM + + +%ifdef BS2_INC_PE16 +;; +; See bs2ExitMode_p16. +BEGINCODELOW +BITS 16 +BEGINPROC Bs2ExitMode_pe16 + push bs2ExitModeCleanupNop_rm + jmp bs2ExitMode_p16 +ENDPROC Bs2ExitMode_pe16 +%endif ; BS2_INC_PE16 + + +%ifdef BS2_INC_PE32 +;; +; See bs2ExitMode_p32. +BEGINCODEHIGH +BITS 32 +BEGINPROC Bs2ExitMode_pe32 + push bs2ExitModeCleanupNop_rm + jmp bs2ExitMode_p32 +ENDPROC Bs2ExitMode_pe32 +%endif ; BS2_INC_PE32 + + +%ifdef BS2_INC_PEV86 +;; +; See Bs2ExitMode_v86. +BEGINCODELOW +BITS 16 +BEGINPROC Bs2ExitMode_pev86 + push bs2ExitModeCleanupNop_rm + jmp Bs2ExitMode_pv86 +ENDPROC Bs2ExitMode_pev86 +%endif ; BS2_INC_PEV86 + + +%ifdef BS2_INC_PP16 +;; +; See bs2ExitMode_p16. +BEGINCODELOW +BITS 16 +BEGINPROC Bs2ExitMode_pp16 + push bs2ExitModeCleanupNop_rm + jmp bs2ExitMode_p16 +ENDPROC Bs2ExitMode_pp16 +%endif ; BS2_INC_PP16 + + +%ifdef BS2_INC_PP32 +;; +; See bs2ExitMode_p32. +BEGINCODEHIGH +BITS 32 +BEGINPROC Bs2ExitMode_pp32 + push bs2ExitModeCleanupNop_rm + jmp bs2ExitMode_p32 +ENDPROC Bs2ExitMode_pp32 +%endif ; BS2_INC_PP32 + + +%ifdef BS2_INC_PPV86 +;; +; See Bs2ExitMode_v86. +BEGINCODELOW +BITS 16 +BEGINPROC Bs2ExitMode_ppv86 + push bs2ExitModeCleanupNop_rm + jmp Bs2ExitMode_pv86 +ENDPROC Bs2ExitMode_ppv86 +%endif ; BS2_INC_PPV86 + + + +%ifdef BS2_INC_PAE16 +;; +; See bs2ExitMode_p16. +BEGINCODELOW +BITS 16 +BEGINPROC Bs2ExitMode_pae16 + push bs2ExitModeCleanupPae_rm + jmp bs2ExitMode_p16 +ENDPROC Bs2ExitMode_pae16 +%endif ; BS2_INC_pae16 + + +%ifdef BS2_INC_PAE32 +;; +; See bs2ExitMode_p32. +BEGINCODEHIGH +BITS 32 +BEGINPROC Bs2ExitMode_pae32 + push bs2ExitModeCleanupPae_rm + jmp bs2ExitMode_p32 +ENDPROC Bs2ExitMode_pae32 +%endif ; BS2_INC_PAE32 + + +%ifdef BS2_INC_PAEV86 +;; +; See Bs2ExitMode_v86. +BEGINCODELOW +BITS 16 +BEGINPROC Bs2ExitMode_paev86 + push bs2ExitModeCleanupPae_rm + jmp Bs2ExitMode_pv86 +ENDPROC Bs2ExitMode_paev86 +%endif ; BS2_INC_PAEV86 + + +%ifdef BS2_INC_LM16 +;; +; See bs2ExitMode_p16. +BEGINCODELOW +BITS 16 +BEGINPROC Bs2ExitMode_lm16 + push bs2ExitModeCleanupLm_rm + jmp bs2ExitMode_p16 +ENDPROC Bs2ExitMode_lm16 +%endif ; BS2_INC_lm16 + + +%ifdef BS2_INC_LM32 +;; +; See bs2ExitMode_p32. +BEGINCODEHIGH +BITS 32 +BEGINPROC Bs2ExitMode_lm32 + push bs2ExitModeCleanupLm_rm + jmp bs2ExitMode_p32 +ENDPROC Bs2ExitMode_lm32 +%endif ; BS2_INC_LM32 + + +%ifdef BS2_INC_LM64 +;; +; See Bs2ExitMode_v86. +BEGINCODELOW +BITS 64 +BEGINPROC Bs2ExitMode_lm64 + call Bs2Thunk_lm64_lm16 +BITS 16 + pop dword [esp] + pop word [esp] + push bs2ExitModeCleanupLm_rm + jmp bs2ExitMode_p16 +ENDPROC Bs2ExitMode_lm64 +%endif ; BS2_INC_LM64 + + +; Isn't there a better way to do this in yasm? +%ifdef BS2_INC_CMN_P16 + %define BS2_INC_BS2EXITMODE_P16 +%elifdef BS2_INC_CMN_LM + %define BS2_INC_BS2EXITMODE_P16 +%elifdef BS2_INC_CMN_P32 + %define BS2_INC_BS2EXITMODE_P16 +%endif + +%ifdef BS2_INC_BS2EXITMODE_P16 +;; +; Exit 16-bit protected or long mode (cs = CS16, ss = SS16). +; +; @returns cs,ds,ss,es,gs,fs loaded with the 0 selector. +; All other registers are preserved. +; @uses nothing +; +BEGINCODELOW +BITS 16 +BEGINPROC bs2ExitMode_p16 + push eax + pushfd + cli + + ; Make sure we've got the right SS and CS values (paranoia). + mov ax, BS2_SEL_SS16 + mov ss, ax + jmp far BS2_SEL_CS16:.code16_start + +.code16_start: + ; Turn off paging and protected mode. + mov eax, cr0 + and eax, 0ffffffffh - X86_CR0_PE - X86_CR0_PG + mov cr0, eax + jmp far 0000:.real_mode_start + +.real_mode_start: + ; Load the correct real mode segment registers. + xor ax, ax + mov ss, ax + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + + ; Load the real mode idtr. + lidt [idtr_real_mode] + + ; Cleanup. + mov ax, [esp + 8h] + call ax + +%ifndef BS2_NOINC_COMMON + ; Set globals. + call SetCpuModeGlobals_rm +%endif + + popfd + pop eax + add sp, 2h ; cleanup routine address + ret +ENDPROC bs2ExitMode_p16 +%endif ; BS2_INC_CMN_P16 + + +%ifdef BS2_INC_CMN_P32 +;; +; Exit 32-bit protected or long mode (cs = CS32, ss = SS32). +; +; The return address as well as the stack registers must be somewhere within +; the first 64KB of the address space. +; +; @returns cs,ds,ss,es,gs,fs loaded with the 0 selector. +; All other registers are preserved. +; @uses nothing +; +BEGINCODELOW +BITS 32 +BEGINPROC bs2ExitMode_p32 + push eax + mov eax, [esp + 8h] ; return address + mov [esp + 0ah], ax + mov eax, [esp + 4h] ; cleanup routine address + mov [esp + 08h], ax + pop eax + add esp, 4h + + call Bs2Thunk_p32_p16 +BITS 16 + jmp bs2ExitMode_p16 +ENDPROC bs2ExitMode_p32 +%endif ; BS2_INC_CMN_P32 + + +;; +; Dummy cleanup routine. +BEGINCODELOW +BITS 16 +BEGINPROC bs2ExitModeCleanupNop_rm + ret +ENDPROC bs2ExitModeCleanupNop_rm + + +%ifdef BS2_INC_CMN_PAE +;; +; Cleans up after leaving PAE mode. +; @uses nothing +BEGINCODELOW +BITS 16 +BEGINPROC bs2ExitModeCleanupPae_rm + push eax + mov eax, cr4 + and eax, ~X86_CR4_PAE + mov cr4, eax + pop eax + ret +ENDPROC bs2ExitModeCleanupPae_rm +%endif + + +%ifdef BS2_INC_CMN_LM +;; +; Cleans up after leaving long mode. +; @uses nothing +BEGINCODELOW +BITS 16 +BEGINPROC bs2ExitModeCleanupLm_rm + push eax + push edx + push ecx + + mov ecx, MSR_K6_EFER + rdmsr + and eax, ~MSR_K6_EFER_LME + wrmsr + + pop ecx + pop edx + pop eax + ret +ENDPROC bs2ExitModeCleanupLm_rm +%endif + + + +; +; Thunking routines for switching between 16/32/64-bit code. +; + + +%ifdef BS2_INC_CMN_PM +;; +; Switches from 32-bit to 16-bit mode ({eip,esp,ebp} < 64KB). +; +; @returns cs,ds,ss,es,gs,fs loaded with 16-bit selectors. +; All other registers are preserved. +; @uses nothing +; +BEGINCODELOW +BITS 32 +BEGINPROC Bs2Thunk_p32_p16 + push eax + pushf + cli + mov ax, BS2_SEL_SS16 + jmp far BS2_SEL_CS16:.code16_start +BITS 16 +.code16_start: + mov ss, ax + mov ax, BS2_SEL_DS16 + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + + ; Fix the return address and then return. + mov ax, [esp + 08h] + mov [esp + 0ah], ax + popfd + pop eax + add sp, 2h + ret +ENDPROC Bs2Thunk_p32_p16 + %define Bs2Thunk_p32_pe16 Bs2Thunk_p32_p16 ; Alternative name for TMPL_NM + %define Bs2Thunk_p32_pp16 Bs2Thunk_p32_p16 ; Alternative name for TMPL_NM + %define Bs2Thunk_p32_pae16 Bs2Thunk_p32_p16 ; Alternative name for TMPL_NM +%endif ; BS2_INC_CMN_PM + + +%ifdef BS2_INC_CMN_PM +;; +; Switches from 16-bit to 32-bit mode (cs = CS16, ds = DS16). +; +; @returns cs,ds,ss,es,gs,fs loaded with 32-bit selectors. +; ebp and esp converted to 32bit. +; All other registers are preserved. +; @uses nothing +; +BEGINCODELOW +BITS 16 +BEGINPROC Bs2Thunk_p16_p32 + push word 0 + push eax + pushfd + cli + jmp far BS2_SEL_CS32:.code32_start +BITS 32 +.code32_start: + mov eax, BS2_SEL_SS32 + mov ss, ax + mov ax, BS2_SEL_DS32 + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + and ebp, 0ffffh + and esp, 0ffffh + + ; Fix the return address and then return. + movzx eax, word [esp + 0ah] + mov [esp + 08h], eax + popfd + pop eax + ret +ENDPROC Bs2Thunk_p16_p32 + %define Bs2Thunk_p16_pe32 Bs2Thunk_p16_p32 ; Alternative name for TMPL_NM + %define Bs2Thunk_p16_pp32 Bs2Thunk_p16_p32 ; Alternative name for TMPL_NM + %define Bs2Thunk_p16_pae32 Bs2Thunk_p16_p32 ; Alternative name for TMPL_NM +%endif ; BS2_INC_CMN_PM + + +%ifdef BS2_INC_CMN_LM +;; +; Switches from 64-bit to 16-bit mode ({rip,rsp,rbp} < 64KB). +; +; @returns cs,ds,ss,es,gs,fs loaded with 16-bit selectors. +; All other registers are preserved. +; @uses nothing +; +BEGINCODELOW +BITS 64 +BEGINPROC Bs2Thunk_lm64_lm16 + push rax + pushf + cli + mov ax, BS2_SEL_SS16 + push BS2_SEL_CS16 + push .code16_start + retf +BITS 16 +.code16_start: + mov ss, ax + mov ax, BS2_SEL_DS16 + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + + ; Fix the return address and then return. + mov ax, [esp + 10h] + mov [esp + 16h], ax + popfd + add sp, 4h + pop eax + add sp, 4h + 6h + ret +ENDPROC Bs2Thunk_lm64_lm16 + %define Bs2Thunk_p64_lm16 Bs2Thunk_lm64_lm16 ; Alternative name for TMPL_NM +%endif ; BS2_INC_CMN_LM + + +%ifdef BS2_INC_LM16 +;; +; Switches from 16-bit to 64-bit mode (cs = CS16, ss = SS16). +; +; @returns cs,ds,ss,es,gs,fs loaded with 64-bit selectors. +; rbp and rsp converted to 16/32/64-bit. +; All other registers are preserved. +; @uses nothing +; +BEGINCODELOW +BITS 16 +BEGINPROC Bs2Thunk_lm16_lm64 + push word 0 + push dword 0 + push dword 0 + push eax + push dword 0 + pushfd + cli + mov eax, BS2_SEL_SS64 + jmp far BS2_SEL_CS64:.code64_start +BITS 64 +.code64_start: + mov ss, ax + mov ax, BS2_SEL_DS64 + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + + and ebp, 0ffffh + and esp, 0ffffh + and edi, 0ffffffffh + and esi, 0ffffffffh + and ebx, 0ffffffffh + + ; Fix the return address and then return. + movzx rax, word [rsp + 16h] + mov [rsp + 10h], rax + popf + pop rax + ret +ENDPROC Bs2Thunk_lm16_lm64 + %define Bs2Thunk_p16_lm64 Bs2Thunk_lm16_lm64 ; Alternative name for TMPL_NM +%endif ; BS2_INC_LM16 + + +%ifdef BS2_INC_LM32 +;; +; Switches from 64-bit to 32-bit mode ({rip,rsp,rbp} < 4GB). +; +; @returns cs,ds,ss,es,gs,fs loaded with 32-bit selectors. +; All other registers are preserved. +; @uses nothing +; +BEGINCODEHIGH +BITS 64 +BEGINPROC Bs2Thunk_lm64_lm32 + push rax + pushf + cli + mov ax, BS2_SEL_SS32 + push BS2_SEL_CS32 + push .code32_start + retf +BITS 32 +.code32_start: + mov ss, ax + mov ax, BS2_SEL_DS32 + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + + ; Fix the return address and then return. + mov eax, [esp + 10h] + mov [esp + 14h], eax + popfd + mov eax, [esp + 4h] + lea esp, [esp + 10h] + ret +ENDPROC Bs2Thunk_lm64_lm32 + %define Bs2Thunk_p64_lm32 Bs2Thunk_lm64_lm32 ; Alternative name for TMPL_NM +%endif ; BS2_INC_LM32 + + +%ifdef BS2_INC_LM32 +;; +; Switches from 32-bit to 64-bit mode (cs = CS32, ss = SS32). +; +; @returns cs,ds,ss,es,gs,fs loaded with 64-bit selectors. +; rbp and rsp converted to 32/64-bit. +; All other registers are preserved. +; @uses nothing +; +BEGINCODEHIGH +BITS 32 +BEGINPROC Bs2Thunk_lm32_lm64 + push dword 0 + push dword 0 + push eax + push dword 0 + pushfd + cli + mov eax, BS2_SEL_SS64 + jmp far BS2_SEL_CS64:.code64_start +BITS 64 +.code64_start: + mov ss, ax + mov ax, BS2_SEL_DS64 + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + + and ebp, 0ffffffffh + and esp, 0ffffffffh + and edi, 0ffffffffh + and esi, 0ffffffffh + and ebx, 0ffffffffh + + ; Fix the return address and then return. + mov eax, [rsp + 14h] + mov [rsp + 10h], rax + popf + pop rax + ret +ENDPROC Bs2Thunk_lm32_lm64 + %define Bs2Thunk_p32_lm64 Bs2Thunk_lm32_lm64 ; Alternative name for TMPL_NM +%endif ; BS2_INC_LM32 + + + + +; +; Routines for checking if mode is supported or not. +; +; @returns al=1 & ZF=0 if supported, al=0 & ZF=1 if not. +; @uses nothing. +; + +; These are easy. +BEGINCODELOW +BITS 16 +BEGINPROC bs2IsModeSupportedYes_rm +GLOBALNAME Bs2IsModeSupported_rm_rm +GLOBALNAME Bs2IsModeSupported_rm_pe16 +GLOBALNAME Bs2IsModeSupported_rm_pe32 +GLOBALNAME Bs2IsModeSupported_rm_pev86 +GLOBALNAME Bs2IsModeSupported_rm_pp16 +GLOBALNAME Bs2IsModeSupported_rm_pp32 +GLOBALNAME Bs2IsModeSupported_rm_ppv86 + mov al, 1 + test al, al + ret +ENDPROC bs2IsModeSupportedYes_rm + + +%ifdef BS2_INC_CMN_PAE +BEGINCODELOW +BITS 16 +BEGINPROC Bs2IsPaeSupported_16 +GLOBALNAME Bs2IsModeSupported_rm_pae16 +GLOBALNAME Bs2IsModeSupported_rm_pae32 +GLOBALNAME Bs2IsModeSupported_rm_paev86 + push eax + push ebx + push ecx + push edx + + mov eax, 0 + cpuid + cmp eax, 1 + jb .no + cmp eax, 1000h + ja .no + + mov eax, 1 + cpuid + test edx, X86_CPUID_FEATURE_EDX_PAE + + pop edx + pop ecx + pop ebx + pop eax + + mov al, 0 + jz .no + mov al, 1 +.no: + ret +ENDPROC Bs2IsPaeSupported_16 +%endif ; BS2_INC_CMN_PAE + + +%ifdef BS2_INC_CMN_LM +BEGINCODELOW +BITS 16 +BEGINPROC Bs2IsLongModeSupported_16 +GLOBALNAME Bs2IsModeSupported_rm_lm16 +GLOBALNAME Bs2IsModeSupported_rm_lm32 +GLOBALNAME Bs2IsModeSupported_rm_lm64 + push eax + push ebx + push ecx + push edx + + mov eax, 080000000h + cpuid + cmp eax, 080000001h + jb .no + cmp eax, 080001000h + ja .no + + mov eax, 080000001h + cpuid + test edx, X86_CPUID_EXT_FEATURE_EDX_LONG_MODE + + pop edx + pop ecx + pop ebx + pop eax + + mov al, 0 + jz .no + mov al, 1 +.no: + ret +ENDPROC Bs2IsLongModeSupported_16 +%endif ; BS2_INC_CMN_LM + + +; +; Include addition init/base code. +; +%ifdef BS2_WITH_TRAPS + %include "bootsector2-common-init-traps.mac" +%endif + + +; +; Include common code. +; +%ifndef BS2_NOINC_COMMON + %include "bootsector2-common-routines.mac" +%endif + +; +; Include trap records if requested. +; +%ifdef BS2_WITH_TRAPRECS + %include "bootsector2-common-traprec.mac" +%endif + +; +; Map stuff for the initial environment. +; +%ifdef BS2_INIT_RM + %define TMPL_RM +%endif +%ifdef BS2_INIT_PE32 + %define TMPL_PE32 +%endif +%ifdef BS2_INIT_PP32 + %define TMPL_PP32 +%endif +%ifdef BS2_INIT_PAE32 + %define TMPL_PAE32 +%endif +%ifdef BS2_INIT_LM64 + %define TMPL_LM64 +%endif +%include "bootsector2-template-header.mac" + + +; +; Where we jump after initialization. +; +TMPL_BEGINCODE +BITS TMPL_BITS +bs2DoneInit: +%ifdef BS2_INIT_SAVE_REGS + mov eax, cr2 + mov [BS2_REG_SAVE_ADDR + BS2REGS.cr2], eax + mov eax, cr3 + mov [BS2_REG_SAVE_ADDR + BS2REGS.cr3], eax + mov eax, cr4 + mov [BS2_REG_SAVE_ADDR + BS2REGS.cr4], eax + mov byte [BS2_REG_SAVE_ADDR + BS2REGS.cBits], 16 + xor eax, eax + mov [cs:BS2_REG_SAVE_ADDR + BS2REGS.cs], ax + mov ax, start + mov [cs:BS2_REG_SAVE_ADDR + BS2REGS.rip], eax +%endif + +%ifdef BS2_WITH_TRAPRECS + ; + ; Install the trap records. + ; + BS2_TRAP_RECS_INSTALL +%endif + + ; + ; Detect the CPU. + ; + xor eax, eax + mov [g_fCpuIntel], al + mov [g_fCpuAmd], al + cpuid + cmp ecx, 0x444d4163 + jne .not_amd + cmp edx, 0x69746e65 + jne .not_amd + cmp ebx, 0x68747541 + jne .not_amd + mov byte [g_fCpuAmd], 1 + jmp .not_intel + +.not_amd: + cmp ecx, 0x6c65746e + jne .not_intel + cmp edx, 0x49656e69 + jne .not_intel + cmp ebx, 0x756e6547 + jne .not_intel + mov byte [g_fCpuIntel], 1 +.not_intel: + + ; + ; Call the user 'main' procedure (shouldn't return). + ; + call main +.panic_again + call Bs2Panic + jmp .panic_again + |