360 lines
8.0 KiB
PHP
360 lines
8.0 KiB
PHP
;; $Id: com32.inc,v 1.9 2005/01/06 22:34:06 hpa Exp $
|
|
;; -----------------------------------------------------------------------
|
|
;;
|
|
;; Copyright 1994-2003 H. Peter Anvin - All Rights Reserved
|
|
;;
|
|
;; 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, Inc., 53 Temple Place Ste 330,
|
|
;; Boston MA 02111-1307, USA; either version 2 of the License, or
|
|
;; (at your option) any later version; incorporated herein by reference.
|
|
;;
|
|
;; -----------------------------------------------------------------------
|
|
|
|
;;
|
|
;; com32.inc
|
|
;;
|
|
;; Common code for running a COM32 image
|
|
;;
|
|
|
|
;
|
|
; Load a COM32 image. A COM32 image is the 32-bit analogue to a DOS
|
|
; .com file. A COM32 image is loaded at address 0x101000, with %esp
|
|
; set to the high end of usable memory.
|
|
;
|
|
; A COM32 image should begin with the magic bytes:
|
|
; B8 FF 4C CD 21, which is "mov eax,0x21cd4cff" in 32-bit mode and
|
|
; "mov ax,0x4cff; int 0x21" in 16-bit mode. This will abort the
|
|
; program with an error if run in 16-bit mode.
|
|
;
|
|
pm_idt: equ 0x100000
|
|
pm_entry: equ 0x101000
|
|
|
|
bits 16
|
|
section .data
|
|
align 2, db 0
|
|
com32_pmidt:
|
|
dw 8*256 ; Limit
|
|
dd pm_idt ; Address
|
|
|
|
com32_rmidt:
|
|
dw 0ffffh ; Limit
|
|
dd 0 ; Address
|
|
|
|
section .text
|
|
is_com32_image:
|
|
push si ; Save file handle
|
|
push dx ; File length held in DX:AX
|
|
push ax
|
|
|
|
call make_plain_cmdline
|
|
; Copy the command line into the low cmdline buffer
|
|
mov ax,real_mode_seg
|
|
mov fs,ax
|
|
mov si,cmd_line_here
|
|
mov di,command_line
|
|
mov cx,[CmdLinePtr]
|
|
inc cx ; Include final null
|
|
sub cx,si
|
|
fs rep movsb
|
|
|
|
call highmemsize ; We need the high memory size...
|
|
call comboot_setup_api ; Set up the COMBOOT-style API
|
|
|
|
mov edi,pm_entry ; Load address
|
|
pop eax ; File length
|
|
pop si ; File handle
|
|
xor dx,dx ; No padding
|
|
call load_high
|
|
call crlf
|
|
|
|
com32_start:
|
|
mov ebx,com32_call_start ; Where to go in PM
|
|
|
|
com32_enter_pm:
|
|
cli
|
|
mov ax,cs
|
|
mov ds,ax
|
|
mov [SavedSSSP],sp
|
|
mov [SavedSSSP+2],ss
|
|
cld
|
|
call a20_test
|
|
jnz .a20ok
|
|
call enable_a20
|
|
|
|
.a20ok:
|
|
lgdt [bcopy_gdt] ; We can use the same GDT just fine
|
|
lidt [com32_pmidt] ; Set up the IDT
|
|
mov eax,cr0
|
|
or al,1
|
|
mov cr0,eax ; Enter protected mode
|
|
jmp 20h:.in_pm
|
|
|
|
bits 32
|
|
.in_pm:
|
|
xor eax,eax ; Available for future use...
|
|
mov fs,eax
|
|
mov gs,eax
|
|
|
|
mov al,28h ; Set up data segments
|
|
mov es,eax
|
|
mov ds,eax
|
|
mov ss,eax
|
|
|
|
mov esp,[PMESP] ; Load protmode %esp if available
|
|
jmp ebx ; Go to where we need to go
|
|
|
|
;
|
|
; This is invoked right before the actually starting the COM32
|
|
; progam, in 32-bit mode...
|
|
;
|
|
com32_call_start:
|
|
;
|
|
; Point the stack to the end of high memory
|
|
;
|
|
mov esp,[word HighMemSize]
|
|
|
|
;
|
|
; Set up the protmode IDT and the interrupt jump buffers
|
|
; We set these up in the system area at 0x100000,
|
|
; but we could also put them beyond the stack.
|
|
;
|
|
mov edi,pm_idt
|
|
|
|
; Form an interrupt gate descriptor
|
|
mov eax,0x00200000+((pm_idt+8*256)&0x0000ffff)
|
|
mov ebx,0x0000ee00+((pm_idt+8*256)&0xffff0000)
|
|
xor ecx,ecx
|
|
inc ch ; ecx <- 256
|
|
|
|
push ecx
|
|
.make_idt:
|
|
stosd
|
|
add eax,8
|
|
xchg eax,ebx
|
|
stosd
|
|
xchg eax,ebx
|
|
loop .make_idt
|
|
|
|
pop ecx
|
|
|
|
; Each entry in the interrupt jump buffer contains
|
|
; the following instructions:
|
|
;
|
|
; 00000000 60 pushad
|
|
; 00000001 B0xx mov al,<interrupt#>
|
|
; 00000003 E9xxxxxxxx jmp com32_handle_interrupt
|
|
|
|
mov eax,0e900b060h
|
|
mov ebx,com32_handle_interrupt-(pm_idt+8*256+8)
|
|
|
|
.make_ijb:
|
|
stosd
|
|
sub [edi-2],cl ; Interrupt #
|
|
xchg eax,ebx
|
|
stosd
|
|
sub eax,8
|
|
xchg eax,ebx
|
|
loop .make_ijb
|
|
|
|
; Now everything is set up for interrupts...
|
|
|
|
push dword com32_farcall ; Farcall entry point
|
|
push dword (1 << 16) ; 64K bounce buffer
|
|
push dword (comboot_seg << 4) ; Bounce buffer address
|
|
push dword com32_intcall ; Intcall entry point
|
|
push dword command_line ; Command line pointer
|
|
push dword 5 ; Argument count
|
|
sti ; Interrupts OK now
|
|
call pm_entry ; Run the program...
|
|
; ... on return, fall through to com32_exit ...
|
|
|
|
com32_exit:
|
|
mov bx,com32_done ; Return to command loop
|
|
|
|
com32_enter_rm:
|
|
cli
|
|
cld
|
|
mov [PMESP],esp ; Save exit %esp
|
|
xor esp,esp ; Make sure the high bits are zero
|
|
jmp 08h:.in_pm16 ; Return to 16-bit mode first
|
|
|
|
bits 16
|
|
.in_pm16:
|
|
mov ax,18h ; Real-mode-like segment
|
|
mov es,ax
|
|
mov ds,ax
|
|
mov ss,ax
|
|
mov fs,ax
|
|
mov gs,ax
|
|
|
|
lidt [com32_rmidt] ; Real-mode IDT (rm needs no GDT)
|
|
mov eax,cr0
|
|
and al,~1
|
|
mov cr0,eax
|
|
jmp 0:.in_rm
|
|
|
|
.in_rm: ; Back in real mode
|
|
mov ax,cs ; Set up sane segments
|
|
mov ds,ax
|
|
mov es,ax
|
|
mov fs,ax
|
|
mov gs,ax
|
|
lss sp,[SavedSSSP] ; Restore stack
|
|
jmp bx ; Go to whereever we need to go...
|
|
|
|
com32_done:
|
|
call disable_a20
|
|
sti
|
|
jmp enter_command
|
|
|
|
;
|
|
; 16-bit support code
|
|
;
|
|
bits 16
|
|
|
|
;
|
|
; 16-bit interrupt-handling code
|
|
;
|
|
com32_int_rm:
|
|
pushf ; Flags on stack
|
|
push cs ; Return segment
|
|
push word .cont ; Return address
|
|
push dword edx ; Segment:offset of IVT entry
|
|
retf ; Invoke IVT routine
|
|
.cont: ; ... on resume ...
|
|
mov ebx,com32_int_resume
|
|
jmp com32_enter_pm ; Go back to PM
|
|
|
|
;
|
|
; 16-bit system call handling code
|
|
;
|
|
com32_sys_rm:
|
|
pop gs
|
|
pop fs
|
|
pop es
|
|
pop ds
|
|
popad
|
|
popfd
|
|
mov [cs:Com32SysSP],sp
|
|
retf ; Invoke routine
|
|
.return:
|
|
; We clean up SP here because we don't know if the
|
|
; routine returned with RET, RETF or IRET
|
|
mov sp,[cs:Com32SysSP]
|
|
pushfd
|
|
pushad
|
|
push ds
|
|
push es
|
|
push fs
|
|
push gs
|
|
mov ebx,com32_sys_resume
|
|
jmp com32_enter_pm
|
|
|
|
;
|
|
; 32-bit support code
|
|
;
|
|
bits 32
|
|
|
|
;
|
|
; This is invoked on getting an interrupt in protected mode. At
|
|
; this point, we need to context-switch to real mode and invoke
|
|
; the interrupt routine.
|
|
;
|
|
; When this gets invoked, the registers are saved on the stack and
|
|
; AL contains the register number.
|
|
;
|
|
com32_handle_interrupt:
|
|
movzx eax,al
|
|
xor ebx,ebx ; Actually makes the code smaller
|
|
mov edx,[ebx+eax*4] ; Get the segment:offset of the routine
|
|
mov bx,com32_int_rm
|
|
jmp com32_enter_rm ; Go to real mode
|
|
|
|
com32_int_resume:
|
|
popad
|
|
iret
|
|
|
|
;
|
|
; Intcall/farcall invocation. We manifest a structure on the real-mode stack,
|
|
; containing the com32sys_t structure from <com32.h> as well as
|
|
; the following entries (from low to high address):
|
|
; - Target offset
|
|
; - Target segment
|
|
; - Return offset
|
|
; - Return segment (== real mode cs == 0)
|
|
; - Return flags
|
|
;
|
|
com32_farcall:
|
|
pushfd ; Save IF among other things...
|
|
pushad ; We only need to save some, but...
|
|
|
|
mov eax,[esp+10*4] ; CS:IP
|
|
jmp com32_syscall
|
|
|
|
|
|
com32_intcall:
|
|
pushfd ; Save IF among other things...
|
|
pushad ; We only need to save some, but...
|
|
|
|
movzx eax,byte [esp+10*4] ; INT number
|
|
mov eax,[eax*4] ; Get CS:IP from low memory
|
|
|
|
com32_syscall:
|
|
cld
|
|
|
|
movzx edi,word [word SavedSSSP]
|
|
movzx ebx,word [word SavedSSSP+2]
|
|
sub edi,54 ; Allocate 54 bytes
|
|
mov [word SavedSSSP],di
|
|
shl ebx,4
|
|
add edi,ebx ; Create linear address
|
|
|
|
mov esi,[esp+11*4] ; Source regs
|
|
xor ecx,ecx
|
|
mov cl,11 ; 44 bytes to copy
|
|
rep movsd
|
|
|
|
; EAX is already set up to be CS:IP
|
|
stosd ; Save in stack frame
|
|
mov eax,com32_sys_rm.return ; Return seg:offs
|
|
stosd ; Save in stack frame
|
|
mov eax,[edi-12] ; Return flags
|
|
and eax,0x200cd7 ; Mask (potentially) unsafe flags
|
|
mov [edi-12],eax ; Primary flags entry
|
|
stosw ; Return flags
|
|
|
|
mov bx,com32_sys_rm
|
|
jmp com32_enter_rm ; Go to real mode
|
|
|
|
; On return, the 44-byte return structure is on the
|
|
; real-mode stack, plus the 10 additional bytes used
|
|
; by the target address (see above.)
|
|
com32_sys_resume:
|
|
movzx esi,word [word SavedSSSP]
|
|
movzx eax,word [word SavedSSSP+2]
|
|
mov edi,[esp+12*4] ; Dest regs
|
|
shl eax,4
|
|
add esi,eax ; Create linear address
|
|
and edi,edi ; NULL pointer?
|
|
jnz .do_copy
|
|
.no_copy: mov edi,esi ; Do a dummy copy-to-self
|
|
.do_copy: xor ecx,ecx
|
|
mov cl,11 ; 44 bytes
|
|
rep movsd ; Copy register block
|
|
|
|
add dword [word SavedSSSP],54 ; Remove from stack
|
|
|
|
popad
|
|
popfd
|
|
ret ; Return to 32-bit program
|
|
|
|
bits 16
|
|
|
|
section .bss
|
|
alignb 4
|
|
PMESP resd 1 ; Protected-mode ESP
|
|
Com32SysSP resw 1 ; SP saved during COM32 syscall
|
|
|
|
section .text
|