singrdk/base/boot/Singldr/singldr0.asm

1391 lines
40 KiB
NASM

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; singldr0.asm - X86 Portion of Singularity Boot Loader
;;
;; Copyright (c) Microsoft Corporation. All rights reserved.
;;
;; Notes:
;; This file has three main purposes.
;; 1) Initialize context for the 16-bit C++ boot loader
;; (BootPhase0 -> BootPhase1).
;; 2) Manage the transition from 16-bit real to 32-bit protected mode
;; (BootPhase2 -> BootPhase3 -> undump).
;; 3) Manage the transition from 32-bit protected to 16-bit real mode
;; (StopPhase0 -> StopPhase1 -> StopPhase2 -> StopPhase3).
;;
;; In addition, this file provides a few helper functions used by the
;; 16-bit loader code in singldr.cpp.
;;
;; Since there is only one .asm function (BiosDiskRead) specific to the
;; disk boot process, it is in here too
;;
;; The three-function-in-one PModeTransfer routine is also located here
;; in order to ensure it links correctly, since it has mixed 16-bit and
;; 32-bit portions
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
.model tiny, c
.686p
.mmx
.xmm
.xlist
.list
OPTION SCOPED
DEBUG_BOOT EQU 0
DEBUG_VESA_MODES EQU 0
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; Bartok Types
;;
uint8 TYPEDEF BYTE
uint16 TYPEDEF WORD
uint32 TYPEDEF DWORD
uint64 TYPEDEF QWORD
int8 TYPEDEF BYTE
int16 TYPEDEF WORD
int32 TYPEDEF DWORD
int64 TYPEDEF QWORD
bool TYPEDEF BYTE
bartok_char TYPEDEF WORD
intptr TYPEDEF DWORD
uintptr TYPEDEF DWORD
uintPtr STRUCT 4
value uintptr ?
uintPtr ENDS
intPtr STRUCT 4
value intptr ?
intPtr ENDS
PTR_uintptr TYPEDEF PTR uintptr
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; Other Types
;;
GDTDesc STRUCT
GDT_limit dw 0
GDT_base1 dw 0
GDT_base2 db 0
GDT_access db 0
GDT_limacc db 0
GDT_base3 db 0
GDTDesc ENDS
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
_TEXT32 segment para public use32 'CODE' ; this segment contains all 32-bit
include halclass.inc
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; Macros
;;
CONSOLE_WRITE macro loc,value
push edx
mov edx, loc
mov ah, 01fh
mov al, value
mov [edx], ax
pop edx
endm
LOAD_EAX_FLAT MACRO reg
xor eax,eax
mov ax,reg
shl eax,4
ENDM
OPERAND_SIZE_OVERLOAD MACRO
uint8 066h
ENDM
MOV_EAX_CR4 MACRO
uint8 00fh
uint8 020h
uint8 0e0h
ENDM
MOV_CR4_EAX MACRO
uint8 00fh
uint8 022h
uint8 0e0h
ENDM
LEA_EAX_REALATIVE MACRO target
call @pusheip
@pusheip:
pop eax
add eax, target - @pusheip
ENDM
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; 16-bit code segment
;;
_TEXT segment public use16 'CODE' ; this segment contains all 16-bit
EXTRN BootPhase1:NEAR
EXTRN StopPhase3:NEAR
EXTRN MpBootPhase1:NEAR
.data
PUBLIC MpStartupLock
MpStartupLock dw 1
.code
ORG 100h
BootPhase0 PROC NEAR
; if we are booting from PXE, the stack holds the return address and the
; !PXE address, and es:bx is the PXENV+ address. CS:IP=0000:7c00
;
; if we are booting from HD/CD, the stack holds a null return address, the
; boot signature ("CD" or "HD"), and the boot drive. CS:IP=5000:7c00
; First, we will move the IP to 100h, and CS to X7b0, where x=0 or x=5
; this lets us build this as a DOS .com file
mov ax, cs
add ax, 07b0h
push ax
push OFFSET begin
retf ; really returns to next statement.
begin:
; if we booted off of the Hard Disk or CD, we're now at 57b0:0100
; relocate this segment to 07b0:1000
cmp ax, 07b0h ; ax holds cs
je ConfigNetboot ; if cs is already 07b0, this was a net boot
; relocate from 57b0:0100 to 07b0:0100
mov ax, 07c0h
mov es, ax
xor di, di ; es:di points to 07c0:0000
mov ax, cs
add ax, 010h
mov ds, ax
xor si, si ; ds:si points to 57c0:0000
mov ecx, 04000h
rep movsd ; transfer 4000h DWORDS (64KB)
; since we are here, this wasn't a PXE boot. set the stack appropriately:
pop eax ; pop the return address for a disk boot
pop ebx ; pop the boot device signature
pop edx ; pop the boot drive
push edx ; on a disk boot, we set the PXE! address to diskid
push eax ; put the return address back on the stack
pushw 07b0h
push OFFSET relocated
retf ; jump to the 'relocated' block
ConfigNetboot:
push es ; ebx holds the PXENV+ address
push bx
pop ebx
xor edx, edx ; on a PXE boot, the bootdriveid=0
relocated:
mov ax, cs
mov ds, ax ; set FS=GS=DS=CS for TINY model.
mov fs, ax
mov gs, ax
pop eax ; pop the return address.
pop ecx ; pop the !PXE address.
push ecx ; return !PXE address. (arg0)
push eax ; push the return address. (ret)
push ss ;
push sp ;
pop ebp ; get the stack pointer.
mov ax, ds ; move the stack to cs:fff0h
mov ss, ax
mov ax, 0fff0h
mov sp, ax
push ebp ; push the old stack pointer.
push edx ; push the disk id (arg2)
push ebx ; push the PXENV+ address (arg1)
push ecx ; push the !PXE address (arg0)
call EnableA20
call BootPhase1
pop ecx ; pop the !PXE address
pop ecx ; pop the PXENV+ address
pop sp ; pop the old stack pointer.
pop ss
retf
BootPhase0 ENDP
; void IoSpaceWrite8(uint16 port, uint8 value);
; Called by 16-bit C++ code to access video registers.
PUBLIC IoSpaceWrite8
IoSpaceWrite8 PROC NEAR
push bp
mov bp, sp
mov dx, [bp+4] ; dx = address segment
mov al, [bp+6] ; eax = value
out dx, al
pop bp
ret
IoSpaceWrite8 ENDP
; void IoSpaceWrite32(uint16 port, uint32 value);
; Called by 16-bit C++ code to access PCI config space.
PUBLIC IoSpaceWrite32
IoSpaceWrite32 PROC NEAR
push bp
mov bp, sp
mov dx, [bp+4] ; dx = address segment
mov eax, [bp+6] ; eax = value
out dx, eax
pop bp
ret
IoSpaceWrite32 ENDP
; uint8 IoSpaceRead8(uint16 port);
; Called by 16-bit C++ code to access video registers.
PUBLIC IoSpaceRead8
IoSpaceRead8 PROC NEAR
push bp
mov bp, sp
mov dx, [bp+4] ; dx = address segment
in al, dx
pop bp
ret
IoSpaceRead8 ENDP
; uint32 IoSpaceRead32(uint16 port);
; Called by 16-bit C++ code to access PCI config space.
PUBLIC IoSpaceRead32
IoSpaceRead32 PROC NEAR
push bp
mov bp, sp
mov dx, [bp+4] ; dx = address segment
in eax, dx
mov edx, eax
shr edx, 16
pop bp
ret
IoSpaceRead32 ENDP
; uint16 BiosDiskRead(uint8 __far * addr, uint32 diskblock, uint16 blocks, uint16 driveid);
; Called by 16-bit C++ code to read from the disk (HD or CD) using extended int13h
PUBLIC BiosDiskRead
BiosDiskRead PROC NEAR
push bp
mov bp, sp
pushad
mov dx, [bp+14] ; dl = drive #
mov eax, [bp+8] ; eax = LBA #
mov cx, [bp+12] ; ecx = count (# of sectors)
mov bx, [bp+6] ; bx = address segment
mov di, [bp+4] ; di = address offset
pushd 00
push eax ; push 64-bit sector address (top half always null)
push bx
push di ; push transfer address
push cx ; # sectors
pushw 0010h ; this request packet is 16 bytes
mov ah,42h ; extended read
mov si,sp ; set ds:si = address of params
int 13h ; perform the read
add sp, 10h ; clean the stack, discard the return code
popad
pop bp
ret
BiosDiskRead ENDP
; uint16 BiosDiskReadCHS(uint8 __far * addr, uint32 diskblock, uint16 driveid, uint16 sectors, uint16 secpertrack, uint16 numheads)
; Called by 16-bit C++ code to read from a Usb disk using CHS-style int13h
PUBLIC BiosDiskReadCHS
BiosDiskReadCHS PROC NEAR
push bp
mov bp, sp
pushad
push es
mov eax, [bp+8] ; eax = 32-bit absolute sector number
xor edx,edx ; clear edx before long divide
xor ecx,ecx
mov cx,[bp+16] ; ecx = sectors per track
div ecx ; eax = track, edx = sector within track (0-62)
inc dl ; dl = sector within track (1-63)
mov cl,dl ; cl = sector within track
mov edx,eax
shr edx,16 ; dx:ax = track
xor ebx, ebx
mov bx, [bp+18]
div bx ; ax = cylinder (0-1023), dx = head (0-255)
xchg dl,dh ; dh = head
mov ch,al ; ch = bits 0-7 of cylinder
shl ah,6
or cl,ah ; bits 6-7 of cl = bits 8-9 of cylinder
mov ax, [bp+12]
mov dl, al ; dl = int13 unit #
mov bx, [bp+6]
mov es, bx ; es = address segment
mov bx, [bp+4] ; bx = address offset
mov ax, [bp+14] ; ax = number of sectors, let's hope it's less than 256!
mov ah, 02h ; read 1 sector
int 13h
pop es
popad
pop bp
ret
BiosDiskReadCHS ENDP
; void BootGetBiosInfo(Struct_Microsoft_Singularity_BootInfo __far * pInfo);
; Called by 16-bit C++ code to fill in portions of BootInfo.
PUBLIC BootGetBiosInfo
BootGetBiosInfo PROC NEAR
push bp
mov bp, sp
mov es, [bp+6]
mov si, [bp+4] ; pInfo
push di
push cx
push dx
mov eax, SIZEOF Struct_Microsoft_Singularity_BootInfo
mov es:[si].Struct_Microsoft_Singularity_BootInfo._RecSize, eax
; Set Bios-related Information.
;
xor edx,edx
mov ax,0b101h
stc
int 1ah
jc skip_1A_B101
mov es, [bp+6]
mov si, [bp+4] ; pInfo
and eax, 0ffffh
mov es:[si].Struct_Microsoft_Singularity_BootInfo._PciBiosAX, eax
and ebx, 0ffffh
mov es:[si].Struct_Microsoft_Singularity_BootInfo._PciBiosBX, ebx
and ecx, 0ffh
mov es:[si].Struct_Microsoft_Singularity_BootInfo._PciBiosCX, ecx
mov es:[si].Struct_Microsoft_Singularity_BootInfo._PciBiosEDX, edx
skip_1A_B101:
; Save the IDT and PIC information.
xor ax, ax
mov dx, 0a0h
in al, dx
mov ah, al
mov dx, 020h
in al, dx
mov es:[si].Struct_Microsoft_Singularity_BootInfo._BiosPicMask, ax
sidt es:[si].Struct_Microsoft_Singularity_BootInfo._BiosIdtPtr._limit
pop dx
pop cx
pop di
pop bp
ret
BootGetBiosInfo ENDP
; void BootPhase2(Struct_Microsoft_Singularity_BootInfo *)
; Entered in 16-bit real mode.
; Transitions to 32-bit protected mode (to BootPhase3).
; Loads EDX = Struct_Microsoft_Singularity_BootInfo far *
PUBLIC BootPhase2
BootPhase2 PROC NEAR
push bp
xor ebp, ebp
mov bp, sp
mov ax, [bp+6]
mov bp, [bp+4] ; pBootInfo
mov ss, ax
; disable interrupts until kernel turns them back on.
cli
; prepare for jump to 32-bit protected mode.
; edx = physical address of Struct_Microsoft_Singularity_BootInfo.
;
mov edx, DWORD PTR [bp].Struct_Microsoft_Singularity_BootInfo._Info32
; set segments before switch
lgdt FWORD PTR [bp].Struct_Microsoft_Singularity_BootInfo._Cpu0._GdtPtr._limit
; Turn prot mode on, flush prefetch
mov eax, cr0
or eax, Struct_Microsoft_Singularity_X86_CR0_PE + Struct_Microsoft_Singularity_X86_CR0_NE
mov cr0, eax
jmp cont
cont:
; Load protected stack segment registers.
mov ax,Struct_Microsoft_Singularity_BootInfo._Cpu0._GdtRS - Struct_Microsoft_Singularity_BootInfo._Cpu0._GdtNull
mov ss,ax
mov esp,ebp
; push the 16:32 address onto the stack, then "return" to it.
push 0
push Struct_Microsoft_Singularity_BootInfo._Cpu0._GdtPC - Struct_Microsoft_Singularity_BootInfo._Cpu0._GdtNull
mov eax, 07b00h + OFFSET BootPhase3
push eax
OPERAND_SIZE_OVERLOAD
retf
BootPhase2 ENDP
; StopPhase1
; Entered in 16-bit protected mode.
; Transitions to 16-bit real mode (StopPhase2).
; Passes through BootInfo __far * in ebx.
;
StopPhase1 PROC NEAR
; Turn off protected mode.
mov eax, cr0
and eax, NOT (Struct_Microsoft_Singularity_X86_CR0_PE + Struct_Microsoft_Singularity_X86_CR0_NE)
mov cr0, eax
db 0EAh ; JMP FAR PTR
dw offset StopPhase2 ; 07b0:StopPhase2
dw 07b0h
StopPhase1 ENDP
; StopPhase2
; Entered in 16-bit real mode. Shuts the machine down.
; Assumes BootInfo __far * is in ebx.
;
PUBLIC StopPhase2
StopPhase2 PROC NEAR
mov ax, 07b0h ; move the stack to 07b0:fff0h (17AF0)
mov ss, ax
mov ds, ax ; set DS=CS for TINY model.
mov es, ax ; set ES=CS for TINY model.
mov fs, ax ; set FS=CS for TINY model.
mov gs, ax ; set GS=CS for TINY model.
mov ax, 0fff0h
mov sp, ax
; Set ES:SI = BootInfo __far *.
mov si, bx
shr ebx, 16
mov es, bx
; Restore the PIC and BIOS IDT.
mov ax, es:[si].Struct_Microsoft_Singularity_BootInfo._BiosPicMask
mov dx, 0a0h
mov ah, al
out dx, al
mov ax, es:[si].Struct_Microsoft_Singularity_BootInfo._BiosPicMask
mov dx, 020h
out dx, al
lidt FWORD PTR es:[si].Struct_Microsoft_Singularity_BootInfo._BiosIdtPtr._limit
call StopPhase3
again:
jmp again
StopPhase2 ENDP
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; empty_8042 -- wait for 8042 input buffer to drain
; al=0, z=0 => 8042 input buffer empty
; Uses:
; ax, flags
;
_8042_STATUS EQU 64h ; 8042 com port
_8042_DATA EQU 60h ; 8042 data port
_8042_BUF_FULL EQU 2 ; 8042 busy bit
empty_8042 PROC NEAR
sub cx,cx ; cx = 0, timeout loop counter
empty:
in al,_8042_STATUS ; read 8042 status port
jmp $+2
jmp $+2
jmp $+2
jmp $+2
and al,_8042_BUF_FULL ; test buffer full bit
loopnz empty
ret
empty_8042 ENDP
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
;EnableA20:
; Enables the A20 line for any machine. If the MachineType global variable
; is set to MCA, then it will call the EnableMcaA20 routine. If not, it
; will execute the ISA code for enabling the A20 line.
;
EnableA20 PROC NEAR
stc
mov ax, 2401h
int 15h ; Call BIOS Enable A20
jnc exit
mov ah,0dfh ; (AH) = Code for enable
; Enable or disable the A20 line (disable=dd, enable=df)
call empty_8042 ; ensure 8042 input buffer empty
jnz exit ; 8042 error return
mov al,0d1h ; 8042 cmd to write output port
out _8042_STATUS,al ; send cmd to 8042
call empty_8042 ; wait for 8042 to accept cmd
jnz exit ; 8042 error return
mov al,ah ; 8042 port data
out _8042_DATA,al ; output port data to 8042
call empty_8042
; We must wait for the a20 line to settle down, which (on an AT)
; may not happen until up to 20 usec after the 8042 has accepted
; the command. We make use of the fact that the 8042 will not
; accept another command until it is finished with the last one.
; The 0FFh command does a NULL 'Pulse Output Port'. Total execution
; time is on the order of 30 usec, easily satisfying the IBM 8042
; settling requirement.
;
mov al,0FFh ; Pulse Output Port (pulse no lines)
out _8042_STATUS,al ; send cmd to 8042
call empty_8042 ; wait for 8042 to accept cmd
exit:
ret
EnableA20 ENDP
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; void Reset()
;
_CMOS_CTRL EQU 70h ; CMOS ctrl port
_CMOS_DATA EQU 71h ; CMOS data port
PUBLIC Reset
Reset PROC NEAR
mov al, 0bh ; Set up for control reg B
out _CMOS_CTRL, al
call empty_8042 ; stall
in al, _CMOS_DATA
and al, 0bfh ; Clear periodic interrupt enable.
out _CMOS_DATA, al
call empty_8042 ; stall
mov al, 0ah ; Set up for control reg A
out _CMOS_CTRL, al
call empty_8042 ; stall
in al, _CMOS_DATA
and al, 0f0h ; clear rate setting
or al, 006h
out _CMOS_DATA, al
mov al, 015h ; Set a "neutral" cmos address.
out _CMOS_CTRL, al
call empty_8042 ; ensure 8042 input buffer empty
mov al,0feh ; 8042 cmd to write output port
out _8042_STATUS,al ; send cmd to 8042
ret
Reset ENDP
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; int GetSystemMap(SMAP_INFO __far *pDesc, uint32 __far *pNext)
;
PUBLIC BootGetSMAP
BootGetSMAP PROC NEAR
push bp
mov bp, sp
push es
push esi
push edi
push ecx
push edx
push ebx
mov es, [bp+10]
mov di, [bp+8] ; pNext
mov ebx, es:[di]
mov es, [bp+6]
mov di, [bp+4] ; pDesc
mov eax, 0e820h
mov ecx, 20
mov edx, 534D4150h ; 'SMAP'
int 15h
mov es, [bp+10]
mov di, [bp+8] ; pNext
mov es:[di], ebx
pop ebx
pop edx
pop ecx
pop edi
pop esi
pop es
pop bp
ret
BootGetSMAP ENDP
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; int BootCallVesa(uint16 ax, uint16 bx, uint16 cx, uint8 __far *pBuffer)
; ax = [bp+4]
; bx = [bp+6]
; cx = [bp+8]
; es:di = [bp+12/10]
;
if DEBUG_VESA_MODES
PUBLIC BootCallVesa
BootCallVesa PROC NEAR
push bp
mov bp, sp
push es
push esi
push edi
push ecx
push edx
push ebx
mov ax, [bp+4]
mov bx, [bp+6]
mov cx, [bp+8]
mov di, [bp+10] ; pNext
mov es, [bp+12]
int 10h
pop ebx
pop edx
pop ecx
pop edi
pop esi
pop es
pop bp
ret
BootCallVesa ENDP
endif
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; void BootHalt(void)
;
PUBLIC BootHalt
BootHalt PROC NEAR
hlt
again: jmp again
ret
BootHalt ENDP
; void PModeTransfer(uint32 StartAddress, uint32 DestinationAddress, uint32 bytes);
; Entered in 16-bit real mode.
; Transitions to 32-bit protected mode and jumps to CopySector
; This is one of 3 functions that together comprise a single function call that moves from real to protected and back
;
; It appears that this can be done through the use of int15 AH=87h instead
; refer to http://www.ctyme.com/intr/rb-1527.htm for more information
;
PUBLIC PModeTransfer
PModeTransfer PROC NEAR
cli ; shut off interrupts
push bp
mov bp, sp
pushad
push ds
push es
push fs
push gs
mov ecx, [bp+12] ; # bytes to copy
mov edx, [bp+8] ; destination address
mov ebx, [bp+4] ; origin address
push dword ptr 0
popfd ; clear flags
xor ax,ax ; set the segments to null
mov gs,ax
mov es,ax
mov fs,ax
mov ss,ax
lgdt FWORD PTR [gdtptr1] ; load the GDT register
xor ax, ax
mov ds, ax ; set ds to null
mov eax, cr0 ; turn on protected mode
or al, Struct_Microsoft_Singularity_X86_CR0_PE
mov cr0, eax
; emit a 32-bit address intersegment jump
db 66h
db 0eah
dd offset _TEXT32:CopySector + 07b00h ; this is the 32-bit address of CopySector
dw offset codeSeg - offset nullSeg ; protected mode code segment = 08h
PModeTransfer ENDP
; this is the GDT we use for the PModeTransfer function
nullSeg GDTDesc <0, 0, 0, 0, 0, 0>
codeSeg GDTDesc <0FFFFh, 0h, 0h, 9ah, 0cfh, 0> ; flat 4GB code segment
dataSeg GDTDesc <0FFFFh, 0h, 0h, 92h, 0cfh, 0> ; flat 4GB data segment
codeSegR GDTDesc <0FFFFh, 07b00h, 000h, 09Ah, 000h, 000h> ; real mode code segment
dataSegR GDTDesc <0FFFFh, 07b00h, 000h, 092h, 000h, 000h> ; real mode data segment
gdtptr1 dw offset gdtptr1 - offset nullSeg
gdtptr2 dd offset nullSeg + 7b00h
; CopyReturn
; This is the 3rd piece of the real-protected-real jump (the 2nd piece is in the 32bit code seg)
CopyReturn PROC NEAR
; Turn off protected mode.
mov eax, cr0
and eax, NOT Struct_Microsoft_Singularity_X86_CR0_PE
mov cr0, eax
db 0EAh ; JMP FAR PTR to the next instruction
dw offset RealMode
dw 07b0h
RealMode:
; reset all segment registers:
mov ax, 07b0h
mov ss, ax
pop gs
pop fs
pop es
pop ds
sti ; reset interrupts and return to calling procedure
popad
pop bp
ret
CopyReturn ENDP
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; void MpEnter(void)
;
; This function must be aligned on a 4KB boundary to be usable by IPI STARTUP.
; It should be the last 16-bit assembly function to avoid wasting space.
; NB: the entry point is 7c00 - 100 + ORG.
;
ORG 500h ; Base at 08000h. Must be 4KB aligned for IPI START.
PUBLIC MpEnter
MpEnter PROC NEAR
cli
mov ax, 07b0h ; Select data segment
mov ds, ax
spinlock:
cmp MpStartupLock, 0
je get_lock
pause
jmp spinlock
get_lock:
mov ax, 1
xchg ax, MpStartupLock
cmp ax, 0
jne spinlock
; Switch to tiny mode with same descriptors as uniprocessor and reuse
; real-mode stack.
mov ax, 07b0h
mov ds, ax ; set GS=FS=ES=DS=CS for TINY model.
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
mov sp, 0fff0h
push ax
push OFFSET init_cpu
retf
init_cpu:
call MpBootPhase1 ; Point of no return
MpEnter ENDP
; void MpBootPhase2(Struct_Microsoft_Singularity_BootInfo __far *, Struct_Microsoft_Singularity_CpuInfo*)
; Entered in 16-bit real-mode and transitions to 32-bit Protected mode.
PUBLIC MpBootPhase2
MpBootPhase2 PROC NEAR
push bp
xor ebp, ebp
mov bp, sp
mov bx, [bp + 8] ; CpuInfo
mov ax, [bp + 6] ; segment(BootInfo)
mov bp, [bp + 4] ; bp + 4 = BootInfo
mov ss, ax
mov ds, ax
; disable interrupts until kernel turns them back on.
cli
; prepare for jump to 32-bit protected mode.
; edx = physical address of Struct_Microsoft_Singularity_BootInfo.
;
mov edx, DWORD PTR [bp].Struct_Microsoft_Singularity_BootInfo._Info32
; prepare segments
lgdt FWORD PTR [bx].Struct_Microsoft_Singularity_CpuInfo._GdtPtr._limit
; Turn protected mode on and jump to flush prefetch
mov eax, cr0
or eax, Struct_Microsoft_Singularity_X86_CR0_PE + Struct_Microsoft_Singularity_X86_CR0_NE
mov cr0, eax
jmp cont
cont:
; Load protected stack segment registers.
mov ax,Struct_Microsoft_Singularity_BootInfo._Cpu0._GdtRS - Struct_Microsoft_Singularity_BootInfo._Cpu0._GdtNull
mov ss,ax
mov esp,ebp
; push the 16:32 address onto the stack, then "return" to it.
push 0
push Struct_Microsoft_Singularity_BootInfo._Cpu0._GdtPC - Struct_Microsoft_Singularity_BootInfo._Cpu0._GdtNull
mov eax, 07b00h + OFFSET MpBootPhase3
push eax
OPERAND_SIZE_OVERLOAD
retf
MpBootPhase2 ENDP
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; 32-bit code segment
;
.686p
_TEXT32 segment para public use32 'CODE' ; this segment contains all 32-bit
assume cs:_TEXT32, ds:_TEXT32
; BootPhase3:
; Entered in 32-bit protected mode with paging disabled.
; Configures the segment registers and stack. Does not turn on paging.
; Transitions to the 32-bit loader (undump.cpp)
PUBLIC BootPhase3
BootPhase3 PROC NEAR
; edx = physical address of Struct_Microsoft_Singularity_BootInfo structure.
; load the 32-bit segment selectors.
mov bx, Struct_Microsoft_Singularity_BootInfo._Cpu0._GdtPD - Struct_Microsoft_Singularity_BootInfo._Cpu0._GdtNull
mov ds, bx
mov es, bx
mov ss, bx
mov bx, Struct_Microsoft_Singularity_BootInfo._Cpu0._GdtPF - Struct_Microsoft_Singularity_BootInfo._Cpu0._GdtNull
mov fs, bx
mov bx, Struct_Microsoft_Singularity_BootInfo._Cpu0._GdtPG - Struct_Microsoft_Singularity_BootInfo._Cpu0._GdtNull
mov gs, bx
; set the initial stack.
mov esp, Struct_Microsoft_Singularity_BootInfo_REAL_STACK
mov ebp, Struct_Microsoft_Singularity_BootInfo_REAL_PBOOTINFO
mov [ebp], edx
mov ebp, esp
; copy the undumper into place.
mov esi, DWORD PTR [edx].Struct_Microsoft_Singularity_BootInfo._Undump
mov edi, undump_dst
mov ecx, undump_siz
call memcopy
; Select the stack
mov esp, Struct_Microsoft_Singularity_BootInfo_REAL_STACK
; Enable super pages. note that MASM doesn't support the opcode
; for these instructions. so we are going to emit them manually
MOV_EAX_CR4
or eax, (Struct_Microsoft_Singularity_X86_CR4_PAE + Struct_Microsoft_Singularity_X86_CR4_PSE + Struct_Microsoft_Singularity_X86_CR4_PCE + Struct_Microsoft_Singularity_X86_CR4_OSFXSR)
MOV_CR4_EAX
;;; ifdef DOUBLE_FAULT_HANDLER
; Set the PDBR
mov eax, DWORD PTR [edx].Struct_Microsoft_Singularity_BootInfo._Pdpt32
mov cr3, eax
; Turn on paging.
mov eax, cr0
or eax, Struct_Microsoft_Singularity_X86_CR0_PG + Struct_Microsoft_Singularity_X86_CR0_WP
mov cr0, eax
jmp @reload_tlb
ALIGN 010h
@reload_tlb:
;;; endif
; Set the TSS selector
mov ax, Struct_Microsoft_Singularity_BootInfo._Cpu0._GdtTSS - Struct_Microsoft_Singularity_BootInfo._Cpu0._GdtNull
ltr ax
; Set the IO privilege level to 3, so that ring-3 apps can disable
; interrupts. (TODO: eliminate CLI from ring-3 apps.)
pushfd
pop eax
or eax, Struct_Microsoft_Singularity_X86_EFlags_IOPL
push eax
popfd
; Set up the initial stack.
mov esp, Struct_Microsoft_Singularity_BootInfo_REAL_STACK
xor ecx, ecx
push ecx
push ecx
push ecx
push ecx
push ecx ; cpu = 0 (bsp)
push edx ; bootinfo
mov eax, undump_ent
call eax
BootPhase3 ENDP
; MpBootPhase3:
; Entered in 32-bit protected mode with paging disabled.
; Configures the segment registers and stack. Does not turn on paging.
; On entry edx contains the physical address of the BootInfo structure.
PUBLIC MpBootPhase3
MpBootPhase3 PROC NEAR
; load the 32-bit segment selectors.
mov bx, Struct_Microsoft_Singularity_BootInfo._Cpu0._GdtPD - Struct_Microsoft_Singularity_BootInfo._Cpu0._GdtNull
mov ds, bx
mov es, bx
mov ss, bx
mov bx, Struct_Microsoft_Singularity_BootInfo._Cpu0._GdtPF - Struct_Microsoft_Singularity_BootInfo._Cpu0._GdtNull
mov fs, bx
mov bx, Struct_Microsoft_Singularity_BootInfo._Cpu0._GdtPG - Struct_Microsoft_Singularity_BootInfo._Cpu0._GdtNull
mov gs, bx
; locate pre-allocated kernel stack
imul eax, [edx].Struct_Microsoft_Singularity_BootInfo._MpCpuCount, SIZEOF Struct_Microsoft_Singularity_CpuInfo
add eax, Struct_Microsoft_Singularity_BootInfo._Cpu0
mov eax, DWORD PTR [edx + eax].Struct_Microsoft_Singularity_CpuInfo._KernelStack
; switch to kernel stack
mov esp, eax
mov ebp, eax
; Save cpu number as other starting CPU's will bump value
mov ecx, [edx].Struct_Microsoft_Singularity_BootInfo._MpCpuCount
mov [edx].Struct_Microsoft_Singularity_BootInfo._MpStatus32, Struct_Microsoft_Singularity_MpBootStatus_Phase3Entry
; Enable super pages. note that MASM doesn't support the opcode
; for these instructions. so we are going to emit them manually
MOV_EAX_CR4
or eax, (Struct_Microsoft_Singularity_X86_CR4_PAE + Struct_Microsoft_Singularity_X86_CR4_PSE + Struct_Microsoft_Singularity_X86_CR4_PCE + Struct_Microsoft_Singularity_X86_CR4_OSFXSR)
MOV_CR4_EAX
mov eax, DWORD PTR [edx].Struct_Microsoft_Singularity_BootInfo._Pdpt32
mov cr3, eax
; Turn on paging.
mov eax, cr0
or eax, Struct_Microsoft_Singularity_X86_CR0_PG + Struct_Microsoft_Singularity_X86_CR0_WP
mov cr0, eax
jmp @reload_tlb_mp
ALIGN 010h
@reload_tlb_mp:
; stack already has cpu number on it
push ecx ; cpu number
push edx ; bootinfo
mov eax, undump_ent
call eax
deadloop:
cli
hlt
jmp deadloop
MpBootPhase3 ENDP
; StopPhase0
; Entered in 32-bit protected mode with paging enabled..
; Turns off paging.
; Transitions to the 16-bit protected mode (StopPhase1)
; Loads BootInfo __far * into ebx.
PUBLIC StopPhase0
StopPhase0 PROC NEAR
; no intrs for a while
; Configures EBX = Struct_Microsoft_Singularity_BootInfo
cli
mov esp, Struct_Microsoft_Singularity_BootInfo_REAL_STACK
mov ebp, Struct_Microsoft_Singularity_BootInfo_REAL_PBOOTINFO
mov ebx, [ebp]
ifdef DEBUG_BOOT
mov edx, 0b8000h
mov ah, 01fh
mov al, '['
mov [edx], ax
mov edx, 0b8002h
mov ah, 01fh
mov al, '1'
mov [edx], ax
endif
; Set the IO privilege level to 0.
pushfd
pop eax
and eax, NOT Struct_Microsoft_Singularity_X86_EFlags_IOPL
push eax
popfd
; Turn off paging.
mov eax, cr0
and eax, NOT (Struct_Microsoft_Singularity_X86_CR0_PG + Struct_Microsoft_Singularity_X86_CR0_WP)
mov cr0, eax
; Flush and reset the TLB.
mov eax,0
mov cr3,eax
jmp reload_tlb
ALIGN 010h
reload_tlb:
; restore the segments to the 16-bit defaults from the Gdt
mov ebx, [ebx].Struct_Microsoft_Singularity_BootInfo._Info16
mov ax, Struct_Microsoft_Singularity_BootInfo._Cpu0._GdtRS - Struct_Microsoft_Singularity_BootInfo._Cpu0._GdtNull
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
db 0EAh ; JMP FAR PTR
dd offset _TEXT:StopPhase1 ; RC:StopPhase1
dw Struct_Microsoft_Singularity_BootInfo._Cpu0._GdtRC - Struct_Microsoft_Singularity_BootInfo._Cpu0._GdtNull
again:
jmp again
StopPhase0 ENDP
; Copy a memory region.
; [in] ds:si = pointer to source buffer.
; [in] es:di = pointer to destination buffer.
; [in] ecx = number of bytes to copy
;
memcopy PROC NEAR
jecxz done
cld
add ecx,3 ; Pad to DWORD
shr ecx,2 ; Shift ECX for count of dwords
rep movsd ; Copy the data.
done:
ret
memcopy ENDP
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
;;; The IDT_ENTER building macros insure that each IDT target has
;;; an offset of form _IdtEnter + 0x10 * interrupt_number.
;;;
public IdtTarget
public IdtEnter0
public IdtEnter1
public IdtEnterN
IDT_TARGET_ARGS STRUCT 1
_ret uint32 ?
_edi uint32 ?
_esi uint32 ?
_ebp uint32 ?
_esp uint32 ?
_ebx uint32 ?
_edx uint32 ?
_ecx uint32 ?
_eax uint32 ?
_num uint32 ?
_err uint32 ?
_eip uint32 ?
_cs0 uint32 ?
_efl uint32 ?
IDT_TARGET_ARGS ENDS
IDT_ENTER_CLEAN MACRO num
push 0 ; No error
push num
jmp _IdtEnterBody
align 16
ENDM
IDT_ENTER_ERR MACRO num
push num
jmp _IdtEnterBody
align 16
ENDM
align 16
IdtEnter PROC NEAR
IdtEnter0 LABEL NEAR
IDT_ENTER_CLEAN 000h ; #DE Divide-by-Zero
IdtEnter1::
IDT_ENTER_CLEAN 001h ; #DB Debug Exception
IDT_ENTER_CLEAN 002h ; NMI Non-Maskable-Interrupt
IDT_ENTER_CLEAN 003h ; #BP Breakpoint
IDT_ENTER_CLEAN 004h ; #OF OVerflow
IDT_ENTER_CLEAN 005h ; #BR Bound-Range
IDT_ENTER_CLEAN 006h ; #UD Invalid Opcode
IDT_ENTER_CLEAN 007h ; #NM Device Not Available
IDT_ENTER_ERR 008h ; #DF Double Fault
IDT_ENTER_CLEAN 009h ; Unused (was x87 segment except)
IDT_ENTER_ERR 00ah ; #TS Invalid TSS
IDT_ENTER_ERR 00bh ; #NP Sgement Not Present
IDT_ENTER_ERR 00ch ; #SS Stack Exception
IDT_ENTER_ERR 00dh ; #GP General Protection
IDT_ENTER_ERR 00eh ; #PF Page Fault
IDT_ENTER_CLEAN 00fh ; Reserved
IDT_ENTER_CLEAN 010h ; #MF x87 Math Error
IDT_ENTER_ERR 011h ; #AC Alignment Check
IDT_ENTER_CLEAN 012h ; #MC Machine Check
IDT_ENTER_CLEAN 013h ; #XF SIMD Exception
_num = 014h ; 014h to 020h
WHILE _num LE 020h
IDT_ENTER_CLEAN _num
_num = _num + 1
ENDM
IdtEnterN::
_IdtEnterBody::
pushad
mov eax, cr2
push eax
LEA_EAX_REALATIVE IdtTarget
mov eax, [eax]
call eax
again: jmp again
align 4
IdtTarget::
dd 0
IdtEnter endp
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
if 1
; Print a DWORD to the screen
; [in] ebx = address of screen
; [in] eax = dword to print
; [in] ecx = trashed for temporary
;
printdw PROC NEAR
mov ecx, eax
shr ecx, 28
and ecx, 0fh
add ecx, 00f30h
cmp ecx, 00f3ah
jl print28
add ecx, 7h
print28:
mov [ebx+0], ecx
add ebx, 2
mov ecx, eax
shr ecx, 24
and ecx, 0fh
add ecx, 00f30h
cmp ecx, 00f3ah
jl print24
add ecx, 7h
print24:
mov [ebx+0], ecx
add ebx, 2
mov ecx, eax
shr ecx, 20
and ecx, 0fh
add ecx, 00f30h
cmp ecx, 00f3ah
jl print20
add ecx, 7h
print20:
mov [ebx+0], ecx
add ebx, 2
mov ecx, eax
shr ecx, 16
and ecx, 0fh
add ecx, 00f30h
cmp ecx, 00f3ah
jl print16
add ecx, 7h
print16:
mov [ebx+0], ecx
add ebx, 2
mov ecx, eax
shr ecx, 12
and ecx, 0fh
add ecx, 00f30h
cmp ecx, 00f3ah
jl print12
add ecx, 7h
print12:
mov [ebx+0], ecx
add ebx, 2
mov ecx, eax
shr ecx, 08
and ecx, 0fh
add ecx, 00f30h
cmp ecx, 00f3ah
jl print08
add ecx, 7h
print08:
mov [ebx+0], ecx
add ebx, 2
mov ecx, eax
shr ecx, 04
and ecx, 0fh
add ecx, 00f30h
cmp ecx, 00f3ah
jl print04
add ecx, 7h
print04:
mov [ebx+0], ecx
add ebx, 2
mov ecx, eax
shr ecx, 00
and ecx, 0fh
add ecx, 00f30h
cmp ecx, 00f3ah
jl print00
add ecx, 7h
print00:
mov [ebx+0], ecx
add ebx, 2
mov ecx, 00f20h
mov [ebx+0], ecx
add ebx, 2
mov [ebx+0], ecx
add ebx, 2
ret
printdw ENDP
endif
; CopySector:
; Entered in 32-bit protected mode with paging disabled.
; this routine moves back into 16-bit code by going to CopyReturn
; on the way, we'll have it move ecx bytes from [ebx] to [edx]
CopySector PROC NEAR
; load the 32-bit segment selectors.
mov ax, dataSeg - nullSeg
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
jecxz copydone
cld
add ecx,3 ; Pad ecx to DWORD
shr ecx,2 ; Shift ecx for count of dwords
mov esi, ebx
mov edi, edx
rep movsd
copydone:
; set the segment registers back to real mode selectors
mov ax, dataSegR - nullSeg
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
; now return to 16-bit code...
db 0EAh ; emit a JMP FAR PTR
dd offset _TEXT:CopyReturn
dw codeSegR - nullSeg
CopySector ENDP
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; 32-bit Code Generated by C Compiler
;
; Contains:
; undump_ent EQU 001112d5h
; undump_dst EQU 00111000h
; undump_siz EQU 00000480h
; undump_dat uint8 000h,080h,00bh,000h,030h,031h,032h,033h,...
;
PUBLIC undump_dat
include undump.asb
_TEXT32 ends
end BootPhase0
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; End of File.