aboutsummaryrefslogtreecommitdiffstats
path: root/com32.inc
blob: 6b65dd93540321c4ae83001a07c19c70fd3f79b3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
;; -----------------------------------------------------------------------
;;
;;   Copyright 1994-2006 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
		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:
		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,[RealModeSSSP]	; 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 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.)
com32_sys_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

		bits 16

		section .bss
		alignb 4
RealModeSSSP	resd 1			; Real-mode SS:SP
PMESP		resd 1			; Protected-mode ESP
Com32SysSP	resw 1			; SP saved during COM32 syscall

		section .text