root/core/pm.inc

Revision dc10f7b8d3d500fe9efd389eb01bb111644d74ae, 9.5 KB (checked in by H. Peter Anvin <hpa@…>, 3 years ago)

core: move the generated IDT stubs to a separate .gentext16 section

Move the generated IDT stubs to a separate section, .gentext16, so
that they aren't immediately adjacent to a piece of highly mutable
data.

Signed-off-by: H. Peter Anvin <hpa@…>

  • Property mode set to 100644
Line 
1;; -----------------------------------------------------------------------
2;;
3;;   Copyright 1994-2009 H. Peter Anvin - All Rights Reserved
4;;   Copyright 2009 Intel Corporation; author: H. Peter Anvin
5;;
6;;   This program is free software; you can redistribute it and/or modify
7;;   it under the terms of the GNU General Public License as published by
8;;   the Free Software Foundation, Inc., 53 Temple Place Ste 330,
9;;   Boston MA 02111-1307, USA; either version 2 of the License, or
10;;   (at your option) any later version; incorporated herein by reference.
11;;
12;; -----------------------------------------------------------------------
13
14;;
15;; pm.inc
16;;
17;; Functions to enter and exit 32-bit protected mode, handle interrupts
18;; and cross-mode calls.
19;;
20;; PM refers to 32-bit flat protected mode; RM to 16-bit real mode.
21;;
22
23                bits 16
24                section .text16
25;
26; _pm_call: call PM routine in low memory from RM
27;
28;       on stack        = PM routine to call (a 32-bit address)
29;
30;       ECX, ESI, EDI passed to the called function;
31;       EAX = EBP in the called function points to the stack frame
32;       which includes all registers (which can be changed if desired.)
33;
34;       All registers and the flags saved/restored
35;
36;       This routine is invoked by the pm_call macro.
37;
38_pm_call:
39                pushfd
40                pushad
41                push ds
42                push es
43                push fs
44                push gs
45                mov bp,sp
46                mov ax,cs
47                mov ebx,.pm
48                mov ds,ax
49                jmp enter_pm
50
51                bits 32
52                section .textnr
53.pm:
54                ; EAX points to the top of the RM stack, which is EFLAGS
55                test RM_FLAGSH,02h              ; RM EFLAGS.IF
56                jz .no_sti
57                sti
58.no_sti:
59                call [ebp+4*2+9*4+2]            ; Entrypoint on RM stack
60                mov bx,.rm
61                jmp enter_rm
62
63                bits 16
64                section .text16
65.rm:
66                pop gs
67                pop fs
68                pop es
69                pop ds
70                popad
71                popfd
72                ret 4           ; Drop entrypoint
73
74;
75; enter_pm: Go to PM with interrupt service configured
76;       EBX       = PM entry point
77;       EAX = EBP = on exit, points to the RM stack as a 32-bit value
78;       ECX, EDX, ESI, EDI preserved across this routine
79;
80;       Assumes CS == DS
81;
82; This routine doesn't enable interrupts, but the target routine
83; can enable interrupts by executing STI.
84;
85                bits 16
86                section .text16
87enter_pm:
88                cli
89                xor eax,eax
90                mov ds,ax
91                mov ax,ss
92                mov [RealModeSSSP],sp
93                mov [RealModeSSSP+2],ax
94                movzx ebp,sp
95                shl eax,4
96                add ebp,eax             ; EBP -> top of real-mode stack
97                cld
98                call enable_a20
99
100.a20ok:
101                mov byte [bcopy_gdt.TSS+5],89h  ; Mark TSS unbusy
102
103                lgdt [bcopy_gdt]        ; We can use the same GDT just fine
104                lidt [PM_IDT_ptr]       ; Set up the IDT
105                mov eax,cr0
106                or al,1
107                mov cr0,eax             ; Enter protected mode
108                jmp PM_CS32:.in_pm
109
110                bits 32
111                section .textnr
112.in_pm:
113                xor eax,eax             ; Available for future use...
114                mov fs,eax
115                mov gs,eax
116                lldt ax
117
118                mov al,PM_DS32          ; Set up data segments
119                mov es,eax
120                mov ds,eax
121                mov ss,eax
122
123                mov al,PM_TSS           ; Be nice to Intel's VT by
124                ltr ax                  ; giving it a valid TR
125
126                mov esp,[PMESP]         ; Load protmode %esp
127                mov eax,ebp             ; EAX -> top of real-mode stack
128                jmp ebx                 ; Go to where we need to go
129
130;
131; enter_rm: Return to RM from PM
132;
133;       BX      = RM entry point (CS = 0)
134;       ECX, EDX, ESI, EDI preserved across this routine
135;       EAX     clobbered
136;       EBP     reserved
137;
138; This routine doesn't enable interrupts, but the target routine
139; can enable interrupts by executing STI.
140;
141                bits 32
142                section .textnr
143enter_rm:
144                cli
145                cld
146                mov [PMESP],esp         ; Save exit %esp
147                jmp PM_CS16:.in_pm16    ; Return to 16-bit mode first
148
149                bits 16
150                section .text16
151.in_pm16:
152                mov ax,PM_DS16          ; Real-mode-like segment
153                mov es,ax
154                mov ds,ax
155                mov ss,ax
156                mov fs,ax
157                mov gs,ax
158
159                lidt [RM_IDT_ptr]       ; Real-mode IDT (rm needs no GDT)
160                xor dx,dx
161                mov eax,cr0
162                and al,~1
163                mov cr0,eax
164                jmp 0:.in_rm
165
166.in_rm:                                 ; Back in real mode
167                lss sp,[cs:RealModeSSSP]        ; Restore stack
168                movzx esp,sp            ; Make sure the high bits are zero
169                mov ds,dx               ; Set up sane segments
170                mov es,dx
171                mov fs,dx
172                mov gs,dx
173                jmp bx                  ; Go to whereever we need to go...
174
175                section .data16
176                alignz 4
177
178                extern __stack_end
179PMESP           dd __stack_end          ; Protected-mode ESP
180
181PM_IDT_ptr:     dw 8*256-1              ; Length
182                dd IDT                  ; Offset
183
184;
185; This is invoked on getting an interrupt in protected mode.  At
186; this point, we need to context-switch to real mode and invoke
187; the interrupt routine.
188;
189; When this gets invoked, the registers are saved on the stack and
190; AL contains the register number.
191;
192                bits 32
193                section .textnr
194pm_irq:
195                pushad
196                movzx esi,byte [esp+8*4] ; Interrupt number
197                mov ebx,.rm
198                jmp enter_rm            ; Go to real mode
199
200                bits 16
201                section .text16
202.rm:
203                pushf                   ; Flags on stack
204                call far [cs:esi*4]     ; Call IVT entry
205                mov ebx,.pm
206                jmp enter_pm            ; Go back to PM
207
208                bits 32
209                section .textnr
210.pm:
211                popad
212                add esp,4               ; Drop interrupt number
213                iretd
214
215                bits 16
216                section .text16
217;
218; Routines to enable and disable (yuck) A20.  These routines are gathered
219; from tips from a couple of sources, including the Linux kernel and
220; http://www.x86.org/.  The need for the delay to be as large as given here
221; is indicated by Donnie Barnes of RedHat, the problematic system being an
222; IBM ThinkPad 760EL.
223;
224
225                section .data16
226                alignz 2
227A20Ptr          dw a20_dunno
228
229                section .bss16
230                alignb 4
231A20Test         resd 1                  ; Counter for testing A20 status
232A20Tries        resb 1                  ; Times until giving up on A20
233
234                section .text16
235enable_a20:
236                pushad
237                mov byte [cs:A20Tries],255 ; Times to try to make this work
238
239try_enable_a20:
240
241;
242; First, see if we are on a system with no A20 gate, or the A20 gate
243; is already enabled for us...
244;
245a20_none:
246                call a20_test
247                jnz a20_done
248                ; Otherwise, see if we had something memorized...
249                jmp word [cs:A20Ptr]
250
251;
252; Next, try the BIOS (INT 15h AX=2401h)
253;
254a20_dunno:
255a20_bios:
256                mov word [cs:A20Ptr], a20_bios
257                mov ax,2401h
258                pushf                           ; Some BIOSes muck with IF
259                int 15h
260                popf
261
262                call a20_test
263                jnz a20_done
264
265;
266; Enable the keyboard controller A20 gate
267;
268a20_kbc:
269                mov dl, 1                       ; Allow early exit
270                call empty_8042
271                jnz a20_done                    ; A20 live, no need to use KBC
272
273                mov word [cs:A20Ptr], a20_kbc   ; Starting KBC command sequence
274
275                mov al,0D1h                     ; Write output port
276                out 064h, al
277                call empty_8042_uncond
278
279                mov al,0DFh                     ; A20 on
280                out 060h, al
281                call empty_8042_uncond
282
283                ; Apparently the UHCI spec assumes that A20 toggle
284                ; ends with a null command (assumed to be for sychronization?)
285                ; Put it here to see if it helps anything...
286                mov al,0FFh                     ; Null command
287                out 064h, al
288                call empty_8042_uncond
289
290                ; Verify that A20 actually is enabled.  Do that by
291                ; observing a word in low memory and the same word in
292                ; the HMA until they are no longer coherent.  Note that
293                ; we don't do the same check in the disable case, because
294                ; we don't want to *require* A20 masking (SYSLINUX should
295                ; work fine without it, if the BIOS does.)
296.kbc_wait:      push cx
297                xor cx,cx
298.kbc_wait_loop:
299                call a20_test
300                jnz a20_done_pop
301                loop .kbc_wait_loop
302
303                pop cx
304;
305; Running out of options here.  Final attempt: enable the "fast A20 gate"
306;
307a20_fast:
308                mov word [cs:A20Ptr], a20_fast
309                in al, 092h
310                or al,02h
311                and al,~01h                     ; Don't accidentally reset the machine!
312                out 092h, al
313
314.fast_wait:     push cx
315                xor cx,cx
316.fast_wait_loop:
317                call a20_test
318                jnz a20_done_pop
319                loop .fast_wait_loop
320
321                pop cx
322
323;
324; Oh bugger.  A20 is not responding.  Try frobbing it again; eventually give up
325; and report failure to the user.
326;
327                dec byte [cs:A20Tries]
328                jnz a20_dunno           ; Did we get the wrong type?
329
330                mov si, err_a20
331                jmp abort_load
332
333                section .data16
334err_a20         db CR, LF, 'A20 gate not responding!', CR, LF, 0
335                section .text16
336
337;
338; A20 unmasked, proceed...
339;
340a20_done_pop:   pop cx
341a20_done:       popad
342                ret
343
344;
345; This routine tests if A20 is enabled (ZF = 0).  This routine
346; must not destroy any register contents.
347;
348; The no-write early out avoids the io_delay in the (presumably common)
349; case of A20 already enabled (e.g. from a previous call.)
350;
351a20_test:
352                push es
353                push cx
354                push eax
355                mov cx,0FFFFh                   ; HMA = segment 0FFFFh
356                mov es,cx
357                mov eax,[cs:A20Test]
358                mov cx,32                       ; Loop count
359                jmp .test                       ; First iteration = early out
360.wait:          add eax,0x430aea41              ; A large prime number
361                mov [cs:A20Test],eax
362                io_delay                        ; Serialize, and fix delay
363.test:          cmp eax,[es:A20Test+10h]
364                loopz .wait
365.done:          pop eax
366                pop cx
367                pop es
368                ret
369
370;
371; Routine to empty the 8042 KBC controller.  If dl != 0
372; then we will test A20 in the loop and exit if A20 is
373; suddenly enabled.
374;
375empty_8042_uncond:
376                xor dl,dl
377empty_8042:
378                call a20_test
379                jz .a20_on
380                and dl,dl
381                jnz .done
382.a20_on:        io_delay
383                in al, 064h             ; Status port
384                test al,1
385                jz .no_output
386                io_delay
387                in al, 060h             ; Read input
388                jmp short empty_8042
389.no_output:
390                test al,2
391                jnz empty_8042
392                io_delay
393.done:          ret
394
395;
396; This initializes the protected-mode interrupt thunk set
397;
398                section .text16
399pm_init:
400                xor edi,edi
401                mov bx,IDT
402                mov di,IRQStubs
403
404                mov eax,7aeb006ah       ; push byte .. jmp short ..
405
406                mov cx,8                ; 8 groups of 32 IRQs
407.gloop:
408                push cx
409                mov cx,32               ; 32 entries per group
410.eloop:
411                mov [bx],di             ; IDT offset [15:0]
412                mov word [bx+2],PM_CS32 ; IDT segment
413                mov dword [bx+4],08e00h ; IDT offset [31:16], 32-bit interrupt
414                                        ; gate, CPL 0 (we don't have a TSS
415                                        ; set up...)
416                add bx,8
417
418                stosd
419                ; Increment IRQ, decrement jmp short offset
420                add eax,(-4 << 24)+(1 << 8)
421
422                loop .eloop
423
424                ; At the end of each group, replace the EBxx with
425                ; the final E9xxxxxxxx
426                add di,3
427                mov byte [di-5],0E9h    ; JMP NEAR
428                mov edx,pm_irq
429                sub edx,edi
430                mov [di-4],edx
431
432                add eax,(0x80 << 24)    ; Proper offset for the next one
433                pop cx
434                loop .gloop
435
436                ret
437
438                ; pm_init is called before bss clearing, so put these
439                ; in .earlybss!
440                section .earlybss
441                alignb 8
442IDT:            resq 256
443RealModeSSSP    resd 1                  ; Real-mode SS:SP
444
445                section .gentextnr      ; Autogenerated 32-bit code
446IRQStubs:       resb 4*256+3*8
447
448                section .text16
449
450%include "callback.inc"                 ; Real-mode callbacks
Note: See TracBrowser for help on using the browser.