786 lines
48 KiB
Plaintext
786 lines
48 KiB
Plaintext
1 ;; -*- fundamental -*-
|
|
2 ;; $Id: memdisk16.asm,v 1.3 2004/12/14 22:46:25 hpa Exp $
|
|
3 ;; -----------------------------------------------------------------------
|
|
4 ;;
|
|
5 ;; Copyright 1994-2004 H. Peter Anvin - All Rights Reserved
|
|
6 ;;
|
|
7 ;; This program is free software; you can redistribute it and/or modify
|
|
8 ;; it under the terms of the GNU General Public License as published by
|
|
9 ;; the Free Software Foundation, Inc., 53 Temple Place Ste 330,
|
|
10 ;; Boston MA 02111-1307, USA; either version 2 of the License, or
|
|
11 ;; (at your option) any later version; incorporated herein by reference.
|
|
12 ;;
|
|
13 ;; -----------------------------------------------------------------------
|
|
14
|
|
15 ;;
|
|
16 ;; init16.asm
|
|
17 ;;
|
|
18 ;; Routine to initialize and to trampoline into 32-bit
|
|
19 ;; protected memory. This code is derived from bcopy32.inc and
|
|
20 ;; com32.inc in the main SYSLINUX distribution.
|
|
21 ;;
|
|
22
|
|
23 MY_CS equ 0x0800 ; Segment address to use
|
|
24 CS_BASE equ (MY_CS << 4) ; Corresponding address
|
|
25
|
|
26 ; Low memory bounce buffer
|
|
27 BOUNCE_SEG equ (MY_CS+0x1000)
|
|
28
|
|
29 %define DO_WBINVD 0
|
|
30
|
|
31 %define STACK_HEAP_SIZE (128*1024)
|
|
32
|
|
33 section .rodata align=16
|
|
34 section .data align=16
|
|
35 section .bss align=16
|
|
36
|
|
37 ;; -----------------------------------------------------------------------
|
|
38 ;; Kernel image header
|
|
39 ;; -----------------------------------------------------------------------
|
|
40
|
|
41 section .text ; Must be first in image
|
|
42 bits 16
|
|
43
|
|
44 00000000 00<rept> cmdline times 497 db 0 ; We put the command line here
|
|
45 000001F1 00 setup_sects db 0
|
|
46 000001F2 0000 root_flags dw 0
|
|
47 000001F4 0000 syssize dw 0
|
|
48 000001F6 0000 swap_dev dw 0
|
|
49 000001F8 0000 ram_size dw 0
|
|
50 000001FA 0000 vid_mode dw 0
|
|
51 000001FC 0000 root_dev dw 0
|
|
52 000001FE 55AA boot_flag dw 0xAA55
|
|
53
|
|
54 00000200 EB2E _start: jmp short start
|
|
55
|
|
56 00000202 48647253 db "HdrS" ; Header signature
|
|
57 00000206 0302 dw 0x0203 ; Header version number
|
|
58
|
|
59 00000208 00000000 realmode_swtch dw 0, 0 ; default_switch, SETUPSEG
|
|
60 0000020C 0010 start_sys_seg dw 0x1000 ; obsolete
|
|
61 0000020E [00FE] version_ptr dw memdisk_version-0x200 ; version string ptr
|
|
62 00000210 00 type_of_loader db 0 ; Filled in by boot loader
|
|
63 00000211 01 loadflags db 1 ; Please load high
|
|
64 00000212 0000 setup_move_size dw 0 ; Unused
|
|
65 00000214 00001000 code32_start dd 0x100000 ; 32-bit start address
|
|
66 00000218 00000000 ramdisk_image dd 0 ; Loaded ramdisk image address
|
|
67 0000021C 00000000 ramdisk_size dd 0 ; Size of loaded ramdisk
|
|
68 00000220 00000000 bootsect_kludge dw 0, 0
|
|
69 00000224 0000 heap_end_ptr dw 0
|
|
70 00000226 0000 pad1 dw 0
|
|
71 00000228 00000000 cmd_line_ptr dd 0 ; Command line
|
|
72 0000022C FFFFFFFF ramdisk_max dd 0xffffffff ; Highest allowed ramdisk address
|
|
73
|
|
74 section .rodata
|
|
75 memdisk_version:
|
|
76 00000000 4D454D4449534B2033- db "MEMDISK ", VERSION, " ", DATE, 0
|
|
77 00000009 2E3039203078343165-
|
|
78 00000012 666662636100
|
|
79
|
|
80 ;; -----------------------------------------------------------------------
|
|
81 ;; End kernel image header
|
|
82 ;; -----------------------------------------------------------------------
|
|
83
|
|
84 ;
|
|
85 ; Move ourselves down into memory to reduce the risk of conflicts;
|
|
86 ; then canonicalize CS to match the other segments.
|
|
87 ;
|
|
88 section .text
|
|
89 bits 16
|
|
90 start:
|
|
91 00000230 B80008 mov ax,MY_CS
|
|
92 00000233 8EC0 mov es,ax
|
|
93 00000235 0FB60E[F101] movzx cx,byte [setup_sects]
|
|
94 0000023A 41 inc cx ; Add one for the boot sector
|
|
95 0000023B C1E107 shl cx,7 ; Convert to dwords
|
|
96 0000023E 31F6 xor si,si
|
|
97 00000240 31FF xor di,di
|
|
98 00000242 8EE6 mov fs,si ; fs <- 0
|
|
99 00000244 FC cld
|
|
100 00000245 F366A5 rep movsd
|
|
101 00000248 8ED8 mov ds,ax
|
|
102 0000024A 8ED0 mov ss,ax
|
|
103 0000024C 6631E4 xor esp,esp ; Stack at top of 64K segment
|
|
104 0000024F EA[5402]0008 jmp MY_CS:.next
|
|
105 .next:
|
|
106
|
|
107 ;
|
|
108 ; Copy the command line, if there is one
|
|
109 ;
|
|
110 copy_cmdline:
|
|
111 00000254 31FF xor di,di ; Bottom of our own segment (= "boot sector")
|
|
112 00000256 66A1[2802] mov eax,[cmd_line_ptr]
|
|
113 0000025A 6621C0 and eax,eax
|
|
114 0000025D 7417 jz .endcmd ; No command line
|
|
115 0000025F 89C6 mov si,ax
|
|
116 00000261 66C1E804 shr eax,4 ; Convert to segment
|
|
117 00000265 83E60F and si,0x000F ; Starting offset only
|
|
118 00000268 8EE8 mov gs,ax
|
|
119 0000026A B9F001 mov cx,496 ; Max number of bytes
|
|
120 .copycmd:
|
|
121 0000026D 65AC gs lodsb
|
|
122 0000026F 20C0 and al,al
|
|
123 00000271 7403 jz .endcmd
|
|
124 00000273 AA stosb
|
|
125 00000274 E2F7 loop .copycmd
|
|
126 .endcmd:
|
|
127 00000276 30C0 xor al,al
|
|
128 00000278 AA stosb
|
|
129
|
|
130 ;
|
|
131 ; Now jump to 32-bit code
|
|
132 ;
|
|
133 00000279 FB sti
|
|
134 0000027A E83D01 call init32
|
|
135 ;
|
|
136 ; When init32 returns, we have been set up, the new boot sector loaded,
|
|
137 ; and we should go and and run the newly loaded boot sector
|
|
138 ;
|
|
139 ; The setup function returns (in AL) the drive number which should be
|
|
140 ; put into DL
|
|
141 ;
|
|
142 0000027D 89C2 mov dx,ax
|
|
143
|
|
144 0000027F FA cli
|
|
145 00000280 6631F6 xor esi,esi ; No partition table involved
|
|
146 00000283 8EDE mov ds,si ; Make all the segments consistent
|
|
147 00000285 8EC6 mov es,si
|
|
148 00000287 8EE6 mov fs,si
|
|
149 00000289 8EEE mov gs,si
|
|
150 0000028B 8ED6 mov ss,si
|
|
151 0000028D 66BC007C0000 mov esp,0x7C00 ; Good place for SP to start out
|
|
152 00000293 EA007C0000 jmp 0:0x7C00
|
|
153
|
|
154 ;
|
|
155 ; We enter protected mode, set up a flat 32-bit environment, run rep movsd
|
|
156 ; and then exit. IMPORTANT: This code assumes cs == MY_CS.
|
|
157 ;
|
|
158 ; This code is probably excessively anal-retentive in its handling of
|
|
159 ; segments, but this stuff is painful enough as it is without having to rely
|
|
160 ; on everything happening "as it ought to."
|
|
161 ;
|
|
162 section .rodata
|
|
163
|
|
164 ; desc base, limit, flags
|
|
165 %macro desc 3
|
|
166 dd (%2 & 0xffff) | ((%1 & 0xffff) << 16)
|
|
167 dd (%1 & 0xff000000) | (%2 & 0xf0000) | ((%3 & 0xf0ff) << 8) | ((%1 & 0x00ff0000) >> 16)
|
|
168 %endmacro
|
|
169
|
|
170 align 8, db 0
|
|
171 00000018 2F00 call32_gdt: dw call32_gdt_size-1 ; Null descriptor - contains GDT
|
|
172 0000001A [18800000] .adj1: dd call32_gdt+CS_BASE ; pointer for LGDT instruction
|
|
173 0000001E 0000 dw 0
|
|
174
|
|
175 ; 0008: Code segment, use16, readable, dpl 0, base CS_BASE, 64K
|
|
176 desc CS_BASE, 0xffff, 0x009b
|
|
177 00000020 FFFF0080 <1> dd (%2 & 0xffff) | ((%1 & 0xffff) << 16)
|
|
178 00000024 009B0000 <1> dd (%1 & 0xff000000) | (%2 & 0xf0000) | ((%3 & 0xf0ff) << 8) | ((%1 & 0x00ff0000) >> 16)
|
|
179
|
|
180 ; 0010: Data segment, use16, read/write, dpl 0, base CS_BASE, 64K
|
|
181 desc CS_BASE, 0xffff, 0x0093
|
|
182 00000028 FFFF0080 <1> dd (%2 & 0xffff) | ((%1 & 0xffff) << 16)
|
|
183 0000002C 00930000 <1> dd (%1 & 0xff000000) | (%2 & 0xf0000) | ((%3 & 0xf0ff) << 8) | ((%1 & 0x00ff0000) >> 16)
|
|
184
|
|
185 ; 0018: Data segment, use16, read/write, dpl 0, base 0, 4G
|
|
186 desc 0, 0xfffff, 0x809b
|
|
187 00000030 FFFF0000 <1> dd (%2 & 0xffff) | ((%1 & 0xffff) << 16)
|
|
188 00000034 009B8F00 <1> dd (%1 & 0xff000000) | (%2 & 0xf0000) | ((%3 & 0xf0ff) << 8) | ((%1 & 0x00ff0000) >> 16)
|
|
189
|
|
190 ; 0020: Code segment, use32, read/write, dpl 0, base 0, 4G
|
|
191 desc 0, 0xfffff, 0xc09b
|
|
192 00000038 FFFF0000 <1> dd (%2 & 0xffff) | ((%1 & 0xffff) << 16)
|
|
193 0000003C 009BCF00 <1> dd (%1 & 0xff000000) | (%2 & 0xf0000) | ((%3 & 0xf0ff) << 8) | ((%1 & 0x00ff0000) >> 16)
|
|
194
|
|
195 ; 0028: Data segment, use32, read/write, dpl 0, base 0, 4G
|
|
196 desc 0, 0xfffff, 0xc093
|
|
197 00000040 FFFF0000 <1> dd (%2 & 0xffff) | ((%1 & 0xffff) << 16)
|
|
198 00000044 0093CF00 <1> dd (%1 & 0xff000000) | (%2 & 0xf0000) | ((%3 & 0xf0ff) << 8) | ((%1 & 0x00ff0000) >> 16)
|
|
199
|
|
200 call32_gdt_size: equ $-call32_gdt
|
|
201
|
|
202 00000048 4552524F523A204132- err_a20: db 'ERROR: A20 gate not responding!',13,10,0
|
|
203 00000051 302067617465206E6F-
|
|
204 0000005A 7420726573706F6E64-
|
|
205 00000063 696E67210D0A00
|
|
206
|
|
207 section .bss
|
|
208 alignb 4
|
|
209 00000000 <res 00000004> SavedSSSP resd 1 ; Place to save SS:SP
|
|
210 00000004 <res 00000004> Return resd 1 ; Return value
|
|
211 00000008 <res 00000002> A20Test resw 1 ; Space to test A20
|
|
212 0000000A <res 00000001> A20Tries resb 1
|
|
213
|
|
214 section .data
|
|
215 alignb 4
|
|
216 00000000 00000000 Target dd 0 ; Target address
|
|
217 00000004 2000 Target_Seg dw 20h ; Target CS
|
|
218
|
|
219 00000006 0000 A20Type dw 0 ; Default = unknown
|
|
220
|
|
221 section .text
|
|
222 bits 16
|
|
223 ;
|
|
224 ; Routines to enable and disable (yuck) A20. These routines are gathered
|
|
225 ; from tips from a couple of sources, including the Linux kernel and
|
|
226 ; http://www.x86.org/. The need for the delay to be as large as given here
|
|
227 ; is indicated by Donnie Barnes of RedHat, the problematic system being an
|
|
228 ; IBM ThinkPad 760EL.
|
|
229 ;
|
|
230 ; We typically toggle A20 twice for every 64K transferred.
|
|
231 ;
|
|
232 %define io_delay call _io_delay
|
|
233 %define IO_DELAY_PORT 80h ; Invalid port (we hope!)
|
|
234 %define disable_wait 32 ; How long to wait for a disable
|
|
235
|
|
236 %define A20_DUNNO 0 ; A20 type unknown
|
|
237 %define A20_NONE 1 ; A20 always on?
|
|
238 %define A20_BIOS 2 ; A20 BIOS enable
|
|
239 %define A20_KBC 3 ; A20 through KBC
|
|
240 %define A20_FAST 4 ; A20 through port 92h
|
|
241
|
|
242 align 2, db 0
|
|
243 00000298 [C302][C302][CD02]- A20List dw a20_dunno, a20_none, a20_bios, a20_kbc, a20_fast
|
|
244 0000029E [DE02][0303]
|
|
245 000002A2 [9403][9403][6603]- A20DList dw a20d_dunno, a20d_none, a20d_bios, a20d_kbc, a20d_fast
|
|
246 000002A8 [7703][6F03]
|
|
247 a20_adjust_cnt equ ($-A20List)/2
|
|
248
|
|
249 000002AC EE slow_out: out dx, al ; Fall through
|
|
250
|
|
251 000002AD E680 _io_delay: out IO_DELAY_PORT,al
|
|
252 000002AF E680 out IO_DELAY_PORT,al
|
|
253 000002B1 C3 ret
|
|
254
|
|
255 enable_a20:
|
|
256 000002B2 6660 pushad
|
|
257 000002B4 C606[0A00]FF mov byte [A20Tries],255 ; Times to try to make this work
|
|
258
|
|
259 try_enable_a20:
|
|
260
|
|
261 ;
|
|
262 ; Flush the caches
|
|
263 ;
|
|
264 %if DO_WBINVD
|
|
265 call try_wbinvd
|
|
266 %endif
|
|
267
|
|
268 ;
|
|
269 ; If the A20 type is known, jump straight to type
|
|
270 ;
|
|
271 000002B9 8B2E[0600] mov bp,[A20Type]
|
|
272 000002BD 01ED add bp,bp ; Convert to word offset
|
|
273 000002BF FFA6[9802] .adj4: jmp word [bp+A20List]
|
|
274
|
|
275 ;
|
|
276 ; First, see if we are on a system with no A20 gate
|
|
277 ;
|
|
278 a20_dunno:
|
|
279 a20_none:
|
|
280 000002C3 C606[0600]01 mov byte [A20Type], A20_NONE
|
|
281 000002C8 E86F00 call a20_test
|
|
282 000002CB 756A jnz a20_done
|
|
283
|
|
284 ;
|
|
285 ; Next, try the BIOS (INT 15h AX=2401h)
|
|
286 ;
|
|
287 a20_bios:
|
|
288 000002CD C606[0600]02 mov byte [A20Type], A20_BIOS
|
|
289 000002D2 B80124 mov ax,2401h
|
|
290 000002D5 9C pushf ; Some BIOSes muck with IF
|
|
291 000002D6 CD15 int 15h
|
|
292 000002D8 9D popf
|
|
293
|
|
294 000002D9 E85E00 call a20_test
|
|
295 000002DC 7559 jnz a20_done
|
|
296
|
|
297 ;
|
|
298 ; Enable the keyboard controller A20 gate
|
|
299 ;
|
|
300 a20_kbc:
|
|
301 000002DE B201 mov dl, 1 ; Allow early exit
|
|
302 000002E0 E8B600 call empty_8042
|
|
303 000002E3 7552 jnz a20_done ; A20 live, no need to use KBC
|
|
304
|
|
305 000002E5 C606[0600]03 mov byte [A20Type], A20_KBC ; Starting KBC command sequence
|
|
306
|
|
307 000002EA B0D1 mov al,0D1h ; Command write
|
|
308 000002EC E664 out 064h, al
|
|
309 000002EE E8A600 call empty_8042_uncond
|
|
310
|
|
311 000002F1 B0DF mov al,0DFh ; A20 on
|
|
312 000002F3 E660 out 060h, al
|
|
313 000002F5 E89F00 call empty_8042_uncond
|
|
314
|
|
315 ; Verify that A20 actually is enabled. Do that by
|
|
316 ; observing a word in low memory and the same word in
|
|
317 ; the HMA until they are no longer coherent. Note that
|
|
318 ; we don't do the same check in the disable case, because
|
|
319 ; we don't want to *require* A20 masking (SYSLINUX should
|
|
320 ; work fine without it, if the BIOS does.)
|
|
321 000002F8 51 .kbc_wait: push cx
|
|
322 000002F9 31C9 xor cx,cx
|
|
323 .kbc_wait_loop:
|
|
324 000002FB E83C00 call a20_test
|
|
325 000002FE 7536 jnz a20_done_pop
|
|
326 00000300 E2F9 loop .kbc_wait_loop
|
|
327
|
|
328 00000302 59 pop cx
|
|
329 ;
|
|
330 ; Running out of options here. Final attempt: enable the "fast A20 gate"
|
|
331 ;
|
|
332 a20_fast:
|
|
333 00000303 C606[0600]04 mov byte [A20Type], A20_FAST ; Haven't used the KBC yet
|
|
334 00000308 E492 in al, 092h
|
|
335 0000030A 0C02 or al,02h
|
|
336 0000030C 24FE and al,~01h ; Don't accidentally reset the machine!
|
|
337 0000030E E692 out 092h, al
|
|
338
|
|
339 00000310 51 .fast_wait: push cx
|
|
340 00000311 31C9 xor cx,cx
|
|
341 .fast_wait_loop:
|
|
342 00000313 E82400 call a20_test
|
|
343 00000316 751E jnz a20_done_pop
|
|
344 00000318 E2F9 loop .fast_wait_loop
|
|
345
|
|
346 0000031A 59 pop cx
|
|
347
|
|
348 ;
|
|
349 ; Oh bugger. A20 is not responding. Try frobbing it again; eventually give up
|
|
350 ; and report failure to the user.
|
|
351 ;
|
|
352
|
|
353 0000031B FE0E[0A00] dec byte [A20Tries]
|
|
354 0000031F 7598 jnz try_enable_a20
|
|
355
|
|
356
|
|
357 ; Error message time
|
|
358 00000321 BE[4800] mov si,err_a20
|
|
359 print_err:
|
|
360 00000324 AC lodsb
|
|
361 00000325 20C0 and al,al
|
|
362 00000327 7409 jz die
|
|
363 00000329 BB0700 mov bx,7
|
|
364 0000032C B40E mov ah,0xe
|
|
365 0000032E CD10 int 10h
|
|
366 00000330 EBF2 jmp print_err
|
|
367
|
|
368
|
|
369 die:
|
|
370 00000332 FB sti
|
|
371 00000333 F4 .hlt: hlt
|
|
372 00000334 EBFD jmp short .hlt
|
|
373
|
|
374 ;
|
|
375 ; A20 unmasked, proceed...
|
|
376 ;
|
|
377 00000336 59 a20_done_pop: pop cx
|
|
378 00000337 6661 a20_done: popad
|
|
379 00000339 C3 ret
|
|
380
|
|
381 ;
|
|
382 ; This routine tests if A20 is enabled (ZF = 0). This routine
|
|
383 ; must not destroy any register contents.
|
|
384 ;
|
|
385 a20_test:
|
|
386 0000033A 06 push es
|
|
387 0000033B 51 push cx
|
|
388 0000033C 50 push ax
|
|
389 0000033D B9FFFF mov cx,0FFFFh ; HMA = segment 0FFFFh
|
|
390 00000340 8EC1 mov es,cx
|
|
391 00000342 B92000 mov cx,32 ; Loop count
|
|
392 00000345 A1[0800] mov ax,[A20Test]
|
|
393 00000348 40 .a20_wait: inc ax
|
|
394 00000349 A3[0800] mov [A20Test],ax
|
|
395 0000034C E85EFF io_delay ; Serialize, and fix delay
|
|
396 0000034F 263B06[1880] cmp ax,[es:A20Test+CS_BASE+10h]
|
|
397 00000354 E1F2 loopz .a20_wait
|
|
398 00000356 58 .a20_done: pop ax
|
|
399 00000357 59 pop cx
|
|
400 00000358 07 pop es
|
|
401 00000359 C3 ret
|
|
402
|
|
403 disable_a20:
|
|
404 0000035A 6660 pushad
|
|
405 ;
|
|
406 ; Flush the caches
|
|
407 ;
|
|
408 %if DO_WBINVD
|
|
409 call try_wbinvd
|
|
410 %endif
|
|
411
|
|
412 0000035C 8B2E[0600] mov bp,[A20Type]
|
|
413 00000360 01ED add bp,bp ; Convert to word offset
|
|
414 00000362 FFA6[A202] .adj5: jmp word [bp+A20DList]
|
|
415
|
|
416 a20d_bios:
|
|
417 00000366 B80024 mov ax,2400h
|
|
418 00000369 9C pushf ; Some BIOSes muck with IF
|
|
419 0000036A CD15 int 15h
|
|
420 0000036C 9D popf
|
|
421 0000036D EB19 jmp short a20d_snooze
|
|
422
|
|
423 ;
|
|
424 ; Disable the "fast A20 gate"
|
|
425 ;
|
|
426 a20d_fast:
|
|
427 0000036F E492 in al, 092h
|
|
428 00000371 24FC and al,~03h
|
|
429 00000373 E692 out 092h, al
|
|
430 00000375 EB11 jmp short a20d_snooze
|
|
431
|
|
432 ;
|
|
433 ; Disable the keyboard controller A20 gate
|
|
434 ;
|
|
435 a20d_kbc:
|
|
436 00000377 E81D00 call empty_8042_uncond
|
|
437 0000037A B0D1 mov al,0D1h
|
|
438 0000037C E664 out 064h, al ; Command write
|
|
439 0000037E E81600 call empty_8042_uncond
|
|
440 00000381 B0DD mov al,0DDh ; A20 off
|
|
441 00000383 E660 out 060h, al
|
|
442 00000385 E80F00 call empty_8042_uncond
|
|
443 ; Wait a bit for it to take effect
|
|
444 a20d_snooze:
|
|
445 00000388 51 push cx
|
|
446 00000389 B92000 mov cx, disable_wait
|
|
447 0000038C E8ABFF .delayloop: call a20_test
|
|
448 0000038F 7402 jz .disabled
|
|
449 00000391 E2F9 loop .delayloop
|
|
450 00000393 59 .disabled: pop cx
|
|
451 a20d_dunno:
|
|
452 a20d_none:
|
|
453 00000394 6661 popad
|
|
454 00000396 C3 ret
|
|
455
|
|
456 ;
|
|
457 ; Routine to empty the 8042 KBC controller. If dl != 0
|
|
458 ; then we will test A20 in the loop and exit if A20 is
|
|
459 ; suddenly enabled.
|
|
460 ;
|
|
461 empty_8042_uncond:
|
|
462 00000397 30D2 xor dl,dl
|
|
463 empty_8042:
|
|
464 00000399 E89EFF call a20_test
|
|
465 0000039C 7404 jz .a20_on
|
|
466 0000039E 20D2 and dl,dl
|
|
467 000003A0 7517 jnz .done
|
|
468 000003A2 E808FF .a20_on: io_delay
|
|
469 000003A5 E464 in al, 064h ; Status port
|
|
470 000003A7 A801 test al,1
|
|
471 000003A9 7407 jz .no_output
|
|
472 000003AB E8FFFE io_delay
|
|
473 000003AE E460 in al, 060h ; Read input
|
|
474 000003B0 EBE7 jmp short empty_8042
|
|
475 .no_output:
|
|
476 000003B2 A802 test al,2
|
|
477 000003B4 75E3 jnz empty_8042
|
|
478 000003B6 E8F4FE io_delay
|
|
479 000003B9 C3 .done: ret
|
|
480
|
|
481 ;
|
|
482 ; Execute a WBINVD instruction if possible on this CPU
|
|
483 ;
|
|
484 %if DO_WBINVD
|
|
485 try_wbinvd:
|
|
486 wbinvd
|
|
487 ret
|
|
488 %endif
|
|
489
|
|
490 section .bss
|
|
491 0000000B <res 00000001> alignb 4
|
|
492 0000000C <res 00000004> PMESP resd 1 ; Protected mode %esp
|
|
493
|
|
494 section .idt nobits align=4096
|
|
495 alignb 4096
|
|
496 00000000 <res 00001000> pm_idt resb 4096 ; Protected-mode IDT, followed by interrupt stubs
|
|
497
|
|
498
|
|
499
|
|
500
|
|
501 pm_entry: equ 0x100000
|
|
502
|
|
503 section .rodata
|
|
504 0000006A 00<rept> align 4, db 0
|
|
505 call32_pmidt:
|
|
506 0000006C 0008 dw 8*256 ; Limit
|
|
507 0000006E [00800000] dd pm_idt+CS_BASE ; Address
|
|
508
|
|
509 call32_rmidt:
|
|
510 00000072 FFFF dw 0ffffh ; Limit
|
|
511 00000074 00000000 dd 0 ; Address
|
|
512
|
|
513 section .text
|
|
514 ;
|
|
515 ; This is the main entrypoint in this function
|
|
516 ;
|
|
517 init32:
|
|
518 000003BA 66BB[06840000] mov ebx,call32_call_start+CS_BASE ; Where to go in PM
|
|
519
|
|
520 call32_enter_pm:
|
|
521 000003C0 8CC8 mov ax,cs
|
|
522 000003C2 8ED8 mov ds,ax
|
|
523 000003C4 FA cli
|
|
524 000003C5 8926[0000] mov [SavedSSSP],sp
|
|
525 000003C9 8C16[0200] mov [SavedSSSP+2],ss
|
|
526 000003CD FC cld
|
|
527 000003CE E869FF call a20_test
|
|
528 000003D1 7503 jnz .a20ok
|
|
529 000003D3 E8DCFE call enable_a20
|
|
530
|
|
531 .a20ok:
|
|
532 000003D6 0F0116[1800] lgdt [call32_gdt] ; Set up GDT
|
|
533 000003DB 0F011E[6C00] lidt [call32_pmidt] ; Set up the IDT
|
|
534 000003E0 0F20C0 mov eax,cr0
|
|
535 000003E3 0C01 or al,1
|
|
536 000003E5 0F22C0 mov cr0,eax ; Enter protected mode
|
|
537 000003E8 66EA[F0830000]2000 jmp 20h:dword .in_pm+CS_BASE
|
|
538
|
|
539 bits 32
|
|
540 .in_pm:
|
|
541 000003F0 31C0 xor eax,eax ; Available for future use...
|
|
542 000003F2 8EE0 mov fs,eax
|
|
543 000003F4 8EE8 mov gs,eax
|
|
544
|
|
545 000003F6 B028 mov al,28h ; Set up data segments
|
|
546 000003F8 8EC0 mov es,eax
|
|
547 000003FA 8ED8 mov ds,eax
|
|
548 000003FC 8ED0 mov ss,eax
|
|
549
|
|
550 000003FE 8B25[0C800000] mov esp,[PMESP+CS_BASE] ; Load protmode %esp if available
|
|
551 00000404 FFE3 jmp ebx ; Go to where we need to go
|
|
552
|
|
553 ;
|
|
554 ; This is invoked before first dispatch of the 32-bit code, in 32-bit mode
|
|
555 ;
|
|
556 call32_call_start:
|
|
557 ;
|
|
558 ; Point the stack into low memory
|
|
559 ; We have: this segment, bounce buffer, then stack+heap
|
|
560 ;
|
|
561 00000406 BC00800400 mov esp, CS_BASE + 0x20000 + STACK_HEAP_SIZE
|
|
562 0000040B 83E4F0 and esp, ~0xf
|
|
563
|
|
564 ;
|
|
565 ; Set up the protmode IDT and the interrupt jump buffers
|
|
566 ;
|
|
567 0000040E BF[00800000] mov edi,pm_idt+CS_BASE
|
|
568
|
|
569 ; Form an interrupt gate descriptor
|
|
570 ; WARNING: This is broken if pm_idt crosses a 64K boundary;
|
|
571 ; however, it can't because of the alignment constraints.
|
|
572 00000413 BB[00880000] mov ebx,pm_idt+CS_BASE+8*256
|
|
573 00000418 B800EE2000 mov eax,0x0020ee00
|
|
574 0000041D 6693 xchg ax,bx
|
|
575 0000041F 31C9 xor ecx,ecx
|
|
576 00000421 FEC5 inc ch ; ecx <- 256
|
|
577
|
|
578 00000423 51 push ecx
|
|
579 .make_idt:
|
|
580 00000424 AB stosd
|
|
581 00000425 83C008 add eax,8
|
|
582 00000428 93 xchg eax,ebx
|
|
583 00000429 AB stosd
|
|
584 0000042A 93 xchg eax,ebx
|
|
585 0000042B E2F7 loop .make_idt
|
|
586
|
|
587 0000042D 59 pop ecx
|
|
588
|
|
589 ; Each entry in the interrupt jump buffer contains
|
|
590 ; the following instructions:
|
|
591 ;
|
|
592 ; 00000000 60 pushad
|
|
593 ; 00000001 B0xx mov al,<interrupt#>
|
|
594 ; 00000003 E9xxxxxxxx jmp call32_handle_interrupt
|
|
595
|
|
596 0000042E B860B000E9 mov eax,0xe900b060
|
|
597 00000433 BB[D7840000] mov ebx,call32_handle_interrupt+CS_BASE
|
|
598 00000438 29FB sub ebx,edi
|
|
599
|
|
600 .make_ijb:
|
|
601 0000043A AB stosd
|
|
602 0000043B 284FFE sub [edi-2],cl ; Interrupt #
|
|
603 0000043E 93 xchg eax,ebx
|
|
604 0000043F 83E808 sub eax,8
|
|
605 00000442 AB stosd
|
|
606 00000443 93 xchg eax,ebx
|
|
607 00000444 E2F4 loop .make_ijb
|
|
608
|
|
609 ; Now everything is set up for interrupts...
|
|
610
|
|
611 00000446 6800800100 push dword (BOUNCE_SEG << 4) ; Bounce buffer address
|
|
612 0000044B 68[EA840000] push dword call32_syscall+CS_BASE ; Syscall entry point
|
|
613 00000450 FB sti ; Interrupts OK now
|
|
614 00000451 E8(00800F00) call pm_entry-CS_BASE ; Run the program...
|
|
615
|
|
616 ; ... on return ...
|
|
617 00000456 A3[04800000] mov [Return+CS_BASE],eax
|
|
618
|
|
619 ; ... fall through to call32_exit ...
|
|
620
|
|
621 call32_exit:
|
|
622 0000045B 66BB[A004] mov bx,call32_done ; Return to command loop
|
|
623
|
|
624 call32_enter_rm:
|
|
625 0000045F FA cli
|
|
626 00000460 FC cld
|
|
627 00000461 8925[0C800000] mov [PMESP+CS_BASE],esp ; Save exit %esp
|
|
628 00000467 31E4 xor esp,esp ; Make sure the high bits are zero
|
|
629 00000469 EA[70040000]0800 jmp 08h:.in_pm16 ; Return to 16-bit mode first
|
|
630
|
|
631 bits 16
|
|
632 .in_pm16:
|
|
633 00000470 B81000 mov ax,10h ; Real-mode-like segment
|
|
634 00000473 8EC0 mov es,ax
|
|
635 00000475 8ED8 mov ds,ax
|
|
636 00000477 8ED0 mov ss,ax
|
|
637 00000479 8EE0 mov fs,ax
|
|
638 0000047B 8EE8 mov gs,ax
|
|
639
|
|
640 0000047D 0F011E[7200] lidt [call32_rmidt] ; Real-mode IDT (rm needs no GDT)
|
|
641 00000482 0F20C0 mov eax,cr0
|
|
642 00000485 24FE and al,~1
|
|
643 00000487 0F22C0 mov cr0,eax
|
|
644 0000048A EA[8F04]0008 jmp MY_CS:.in_rm
|
|
645
|
|
646 .in_rm: ; Back in real mode
|
|
647 0000048F 8CC8 mov ax,cs ; Set up sane segments
|
|
648 00000491 8ED8 mov ds,ax
|
|
649 00000493 8EC0 mov es,ax
|
|
650 00000495 8EE0 mov fs,ax
|
|
651 00000497 8EE8 mov gs,ax
|
|
652 00000499 0FB226[0000] lss sp,[SavedSSSP] ; Restore stack
|
|
653 0000049E FFE3 jmp bx ; Go to whereever we need to go...
|
|
654
|
|
655 call32_done:
|
|
656 000004A0 E8B7FE call disable_a20
|
|
657 000004A3 FB sti
|
|
658 000004A4 A1[0400] mov ax,[Return]
|
|
659 000004A7 C3 ret
|
|
660
|
|
661 ;
|
|
662 ; 16-bit support code
|
|
663 ;
|
|
664 bits 16
|
|
665
|
|
666 ;
|
|
667 ; 16-bit interrupt-handling code
|
|
668 ;
|
|
669 call32_int_rm:
|
|
670 000004A8 9C pushf ; Flags on stack
|
|
671 000004A9 0E push cs ; Return segment
|
|
672 000004AA 68[B004] push word .cont ; Return address
|
|
673 000004AD 6652 push dword edx ; Segment:offset of IVT entry
|
|
674 000004AF CB retf ; Invoke IVT routine
|
|
675 .cont: ; ... on resume ...
|
|
676 000004B0 66BB[E8840000] mov ebx,call32_int_resume+CS_BASE
|
|
677 000004B6 E907FF jmp call32_enter_pm ; Go back to PM
|
|
678
|
|
679 ;
|
|
680 ; 16-bit system call handling code
|
|
681 ;
|
|
682 call32_sys_rm:
|
|
683 000004B9 0FA9 pop gs
|
|
684 000004BB 0FA1 pop fs
|
|
685 000004BD 07 pop es
|
|
686 000004BE 1F pop ds
|
|
687 000004BF 6661 popad
|
|
688 000004C1 669D popfd
|
|
689 000004C3 CB retf ; Invoke routine
|
|
690 .return:
|
|
691 000004C4 669C pushfd
|
|
692 000004C6 6660 pushad
|
|
693 000004C8 1E push ds
|
|
694 000004C9 06 push es
|
|
695 000004CA 0FA0 push fs
|
|
696 000004CC 0FA8 push gs
|
|
697 000004CE 66BB[39850000] mov ebx,call32_sys_resume+CS_BASE
|
|
698 000004D4 E9E9FE jmp call32_enter_pm
|
|
699
|
|
700 ;
|
|
701 ; 32-bit support code
|
|
702 ;
|
|
703 bits 32
|
|
704
|
|
705 ;
|
|
706 ; This is invoked on getting an interrupt in protected mode. At
|
|
707 ; this point, we need to context-switch to real mode and invoke
|
|
708 ; the interrupt routine.
|
|
709 ;
|
|
710 ; When this gets invoked, the registers are saved on the stack and
|
|
711 ; AL contains the register number.
|
|
712 ;
|
|
713 call32_handle_interrupt:
|
|
714 000004D7 0FB6C0 movzx eax,al
|
|
715 000004DA 31DB xor ebx,ebx ; Actually makes the code smaller
|
|
716 000004DC 8B1483 mov edx,[ebx+eax*4] ; Get the segment:offset of the routine
|
|
717 000004DF 66BB[A804] mov bx,call32_int_rm
|
|
718 000004E3 E977FFFFFF jmp call32_enter_rm ; Go to real mode
|
|
719
|
|
720 call32_int_resume:
|
|
721 000004E8 61 popad
|
|
722 000004E9 CF iret
|
|
723
|
|
724 ;
|
|
725 ; Syscall invocation. We manifest a structure on the real-mode stack,
|
|
726 ; containing the call32sys_t structure from <call32.h> as well as
|
|
727 ; the following entries (from low to high address):
|
|
728 ; - Target offset
|
|
729 ; - Target segment
|
|
730 ; - Return offset
|
|
731 ; - Return segment (== real mode cs)
|
|
732 ; - Return flags
|
|
733 ;
|
|
734 call32_syscall:
|
|
735 000004EA 9C pushfd ; Save IF among other things...
|
|
736 000004EB 60 pushad ; We only need to save some, but...
|
|
737 000004EC FC cld
|
|
738
|
|
739 000004ED 0FB73D[00800000] movzx edi,word [SavedSSSP+CS_BASE]
|
|
740 000004F4 0FB705[02800000] movzx eax,word [SavedSSSP+CS_BASE+2]
|
|
741 000004FB 83EF36 sub edi,54 ; Allocate 54 bytes
|
|
742 000004FE 66893D[00800000] mov [SavedSSSP+CS_BASE],di
|
|
743 00000505 C1E004 shl eax,4
|
|
744 00000508 01C7 add edi,eax ; Create linear address
|
|
745
|
|
746 0000050A 8B74242C mov esi,[esp+11*4] ; Source regs
|
|
747 0000050E 31C9 xor ecx,ecx
|
|
748 00000510 B10B mov cl,11 ; 44 bytes to copy
|
|
749 00000512 F3A5 rep movsd
|
|
750
|
|
751 00000514 0FB6442428 movzx eax,byte [esp+10*4] ; Interrupt number
|
|
752 ; ecx == 0 here; adding it to the EA makes the
|
|
753 ; encoding smaller
|
|
754 00000519 8B0481 mov eax,[ecx+eax*4] ; Get IVT entry
|
|
755 0000051C AB stosd ; Save in stack frame
|
|
756 0000051D B8[C4040008] mov eax,call32_sys_rm.return + (MY_CS << 16) ; Return seg:offs
|
|
757 00000522 AB stosd ; Save in stack frame
|
|
758 00000523 8B47F4 mov eax,[edi-12] ; Return flags
|
|
759 00000526 25D70C2000 and eax,0x200cd7 ; Mask (potentially) unsafe flags
|
|
760 0000052B 8947F4 mov [edi-12],eax ; Primary flags entry
|
|
761 0000052E 66AB stosw ; Return flags
|
|
762
|
|
763 00000530 66BB[B904] mov bx,call32_sys_rm
|
|
764 00000534 E926FFFFFF jmp call32_enter_rm ; Go to real mode
|
|
765
|
|
766 ; On return, the 44-byte return structure is on the
|
|
767 ; real-mode stack.
|
|
768 call32_sys_resume:
|
|
769 00000539 0FB735[00800000] movzx esi,word [SavedSSSP+CS_BASE]
|
|
770 00000540 0FB705[02800000] movzx eax,word [SavedSSSP+CS_BASE+2]
|
|
771 00000547 8B7C2430 mov edi,[esp+12*4] ; Dest regs
|
|
772 0000054B C1E004 shl eax,4
|
|
773 0000054E 01C6 add esi,eax ; Create linear address
|
|
774 00000550 21FF and edi,edi ; NULL pointer?
|
|
775 00000552 7502 jnz .do_copy
|
|
776 00000554 89F7 .no_copy: mov edi,esi ; Do a dummy copy-to-self
|
|
777 00000556 31C9 .do_copy: xor ecx,ecx
|
|
778 00000558 B10B mov cl,11 ; 44 bytes
|
|
779 0000055A F3A5 rep movsd ; Copy register block
|
|
780
|
|
781 0000055C 8305[00800000]2C add dword [SavedSSSP+CS_BASE],44 ; Remove from stack
|
|
782
|
|
783 00000563 61 popad
|
|
784 00000564 9D popfd
|
|
785 00000565 C3 ret ; Return to 32-bit program
|