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