;; $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, ; 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 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