summaryrefslogtreecommitdiff
path: root/com32.inc
diff options
context:
space:
mode:
authorhpa <hpa>2002-06-11 05:48:29 +0000
committerhpa <hpa>2002-06-11 05:48:29 +0000
commitabac6f53695c0b99e0235ca3da81ab3cd822f475 (patch)
tree42c9c90abcb685ec654b25e680f6a0e272d1fe52 /com32.inc
parent73d0682dcdd250939e560d333b591d7a5a8299ca (diff)
downloadsyslinux-abac6f53695c0b99e0235ca3da81ab3cd822f475.tar.gz
Add an API for COMBOOT images, and add support for "COM32" -- 32-bit
linear .com files.
Diffstat (limited to 'com32.inc')
-rw-r--r--com32.inc319
1 files changed, 319 insertions, 0 deletions
diff --git a/com32.inc b/com32.inc
new file mode 100644
index 00000000..ac0440f8
--- /dev/null
+++ b/com32.inc
@@ -0,0 +1,319 @@
+;; $Id$
+;; -----------------------------------------------------------------------
+;;
+;; Copyright 1994-2002 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,
+;; Bostom 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
+ align 2
+com32_pmidt:
+ dw 8*256 ; Limit
+ dd pm_idt ; Address
+
+com32_rmidt:
+ dw 0ffffh ; Limit
+ dd 0 ; Address
+
+is_com32_image:
+ call highmemsize ; We need the high memory size...
+ call comboot_setup_api ; Set up the COMBOOT-style API
+
+ mov edi,pm_entry ; Load address
+ xchg eax,edx ; Gotta fix this insanity...
+ shl eax,16
+ mov ax,dx
+ call load_high
+ call crlf
+
+com32_start:
+ mov ebx,com32_call_start ; Where to go in PM
+
+com32_enter_pm:
+ mov [SavedSSSP],sp
+ mov [SavedSSSP+2],ss
+
+ cli
+ 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 (1 << 16) ; 64K bounce buffer
+ push dword (comboot_seg << 4) ; Bounce buffer address
+ push dword com32_syscall ; Syscall entry point
+ movzx esi,word [word CmdOptPtr]
+ push esi ; Command line pointer
+ push dword 4 ; 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
+ retf ; Invoke routine
+.return:
+ 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
+
+;
+; Syscall 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_syscall:
+ pushfd ; Save IF among other things...
+ pushad ; We only need to save some, but...
+ cld
+
+ movzx edi,word [word SavedSSSP]
+ movzx eax,word [word SavedSSSP+2]
+ sub edi,54 ; Allocate 54 bytes
+ mov [word SavedSSSP],di
+ shl eax,4
+ add edi,eax ; Create linear address
+
+ mov esi,[esp+11*4] ; Source regs
+ xor ecx,ecx
+ mov cl,11 ; 44 bytes to copy
+ rep movsd
+
+ movzx eax,byte [esp+10*4] ; Interrupt number
+ ; ecx == 0 here; adding it to the EA makes the
+ ; encoding smaller
+ mov eax,[ecx+eax*4] ; Get IVT entry
+ 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.
+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],44 ; Remove from stack
+
+ popad
+ popfd
+ ret ; Return to 32-bit program
+
+ bits 16