aboutsummaryrefslogtreecommitdiffstats
path: root/core/com32.inc
diff options
context:
space:
mode:
Diffstat (limited to 'core/com32.inc')
-rw-r--r--core/com32.inc421
1 files changed, 421 insertions, 0 deletions
diff --git a/core/com32.inc b/core/com32.inc
new file mode 100644
index 00000000..99954206
--- /dev/null
+++ b/core/com32.inc
@@ -0,0 +1,421 @@
+;; -----------------------------------------------------------------------
+;;
+;; Copyright 1994-2008 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 eax ; Save file length
+
+ 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 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
+ mov bx,abort_check ; Don't print dots, but allow abort
+ call load_high
+
+com32_start:
+ mov ebx,com32_call_start ; Where to go in PM
+
+com32_enter_pm:
+ cli
+ mov ax,cs
+ mov ds,ax
+ mov [RealModeSSSP],sp
+ mov [RealModeSSSP+2],ss
+ cld
+ call a20_test
+ jnz .a20ok
+ call enable_a20
+
+.a20ok:
+ mov byte [bcopy_gdt.TSS+5],89h ; Mark TSS unbusy
+
+ 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 PM_CS32:.in_pm
+
+ bits 32
+.in_pm:
+ xor eax,eax ; Available for future use...
+ mov fs,eax
+ mov gs,eax
+ lldt ax
+
+ mov al,PM_DS32 ; Set up data segments
+ mov es,eax
+ mov ds,eax
+ mov ss,eax
+
+ mov al,PM_TSS ; Be nice to Intel's VT by
+ ltr ax ; giving it a valid TR
+
+ 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 (permitted) high memory
+ ;
+ mov esp,[word HighMemRsvd]
+ xor sp,sp ; Align to a 64K boundary
+
+ ;
+ ; 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_cfarcall ; Cfarcall entry point
+ 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 6 ; 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 PM_CS16:.in_pm16 ; Return to 16-bit mode first
+
+ bits 16
+.in_pm16:
+ mov ax,PM_DS16_RM ; 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,[RealModeSSSP] ; Restore stack
+ jmp bx ; Go to whereever we need to go...
+
+com32_done:
+%if DISABLE_A20
+ call disable_a20
+%endif
+ 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 intcall/farcall 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_syscall.resume
+ jmp com32_enter_pm
+
+;
+; 16-bit cfarcall handing code
+;
+com32_cfar_rm:
+ retf
+.return:
+ mov sp,[cs:Com32SysSP]
+ mov [cs:RealModeEAX],eax
+ mov ebx,com32_cfarcall.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 RealModeSSSP]
+ movzx ebx,word [word RealModeSSSP+2]
+ sub edi,54 ; Allocate 54 bytes
+ mov [word RealModeSSSP],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.)
+.resume:
+ movzx esi,word [word RealModeSSSP]
+ movzx eax,word [word RealModeSSSP+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 RealModeSSSP],54 ; Remove from stack
+
+ popad
+ popfd
+ ret ; Return to 32-bit program
+
+;
+; Cfarcall invocation. We copy the stack frame to the real-mode stack,
+; followed by the return CS:IP and the CS:IP of the target function.
+;
+com32_cfarcall:
+ pushfd
+ pushad
+
+ cld
+ mov ecx,[esp+12*4] ; Size of stack frame
+
+ movzx edi,word [word RealModeSSSP]
+ movzx ebx,word [word RealModeSSSP+2]
+ mov [word Com32SysSP],di
+ sub edi,ecx ; Allocate space for stack frame
+ and edi,~3 ; Round
+ sub edi,4*2 ; Return pointer, return value
+ mov [word RealModeSSSP],di
+ shl ebx,4
+ add edi,ebx ; Create linear address
+
+ mov eax,[esp+10*4] ; CS:IP
+ stosd ; Save to stack frame
+ mov eax,com32_cfar_rm.return ; Return seg:off
+ stosd
+ mov esi,[esp+11*4] ; Stack frame
+ mov eax,ecx ; Copy the stack frame
+ shr ecx,2
+ rep movsd
+ mov ecx,eax
+ and ecx,3
+ rep movsb
+
+ mov bx,com32_cfar_rm
+ jmp com32_enter_rm
+
+.resume:
+ popad
+ mov eax,[word RealModeEAX]
+ popfd
+ ret
+
+ bits 16
+
+ section .bss1
+ alignb 4
+RealModeSSSP resd 1 ; Real-mode SS:SP
+RealModeEAX resd 1 ; Real mode EAX
+PMESP resd 1 ; Protected-mode ESP
+Com32SysSP resw 1 ; SP saved during COM32 syscall
+
+ section .text