xref: /reactos/hal/halx86/generic/systimer.S (revision 5100859e)
1/*
2 * FILE:            hal/halx86/generic/systimer.S
3 * COPYRIGHT:       See COPYING in the top level directory
4 * PURPOSE:         System Timer Interrupt and Management
5 * PROGRAMMER:      Alex Ionescu (alex@relsoft.net)
6 */
7
8/* INCLUDES ******************************************************************/
9
10#include <asm.inc>
11
12#include <ks386.inc>
13
14EXTERN _HalpAcquireCmosSpinLock@0:PROC
15EXTERN _HalpReleaseCmosSpinLock@0:PROC
16EXTERN _DbgBreakPoint@0:PROC
17
18#define PIC1_BASE         HEX(20) /* IO base address for master PIC */
19#define PIC2_BASE         HEX(A0) /* IO base address for slave PIC */
20#define PIC1_COMMAND      PIC1_BASE
21#define PIC1_DATA         (PIC1_BASE+1)
22#define PIC2_COMMAND      PIC2_BASE
23#define PIC2_DATA         (PIC2_BASE+1)
24#define PIC_EOI           HEX(20)
25#define PIC_SPECIFIC_EOI2 HEX(62)
26
27#define CMOS_ADDR HEX(70)
28#define CMOS_DATA HEX(71)
29#define CMOS_REGISTER_A HEX(0A)
30#define CMOS_REGISTER_B HEX(0B)
31#define CMOS_REGISTER_C HEX(0C)
32#define CMOS_REGISTER_D HEX(0D)
33
34#define PIT_CH0    HEX(40)
35#define PIT_MODE   HEX(43)
36#define SYSTEM_CTRL_PORT_A HEX(92)
37
38/* GLOBALS *******************************************************************/
39
40.data
41ASSUME CS:NOTHING, DS:NOTHING, ES:NOTHING, FS:NOTHING, GS:NOTHING
42
43/* FUNCTIONS *****************************************************************/
44
45.code
46PUBLIC _HalpCalibrateStallExecution@0
47_HalpCalibrateStallExecution@0:
48
49    /* Setup the stack frame */
50    push ebp
51    mov ebp, esp
52    sub esp, 12
53
54    /* Save EFLAGS and kill interrupts */
55    pushfd
56    cli
57
58    /* Get the current interrupt mask on the PICs */
59    xor eax, eax
60    in al, PIC2_DATA
61    shl eax, 8
62    in al, PIC1_DATA
63
64    /* Save it */
65    push eax
66
67    /* Now mask everything except the RTC and PIC 2 chain-interrupt */
68    mov eax, NOT (HEX(04) OR HEX(100))
69
70    /* Program the PICs */
71    out PIC1_DATA, al
72    shr eax, 8
73    out PIC2_DATA, al
74
75    /* Now get the IDT */
76    sidt [ebp-8]
77    mov ecx, [ebp-6]
78
79    /* Get the IDT entry for the RTC */
80    mov eax, HEX(38)
81    shl eax, 3
82    add ecx, eax
83
84    /* Save the original RTC ISR */
85    push [ecx]
86    push [ecx+4]
87    push ecx
88
89    /* Now load our new handler */
90    mov eax, offset OnlyOnePersonCanWriteHalCode
91    mov [ecx], ax
92    mov word ptr [ecx+2], KGDT_R0_CODE
93    mov word ptr [ecx+4], HEX(08E00)
94    shr eax, 16
95    mov [ecx+6], ax
96
97    /* Reset our counter */
98    mov dword ptr [ebp-12], 0
99
100    /* Acquire CMOS lock */
101    call _HalpAcquireCmosSpinLock@0
102
103    /* Now initialize register A on the CMOS */
104    mov ax, HEX(2D00) OR CMOS_REGISTER_A
105    out CMOS_ADDR, al
106    jmp $+2
107    mov al, ah
108    out CMOS_DATA, al
109    jmp $+2
110
111    /* Read register B */
112    mov ax, CMOS_REGISTER_B
113    out CMOS_ADDR, al
114    jmp $+2
115    in al, CMOS_DATA
116    jmp $+2
117
118    /* Don't touch the LastKnownGoodConfig hack */
119    and al, 1
120    mov ah, al
121
122    /* Enable the interrupt */
123    or ah, HEX(42)
124
125    /* Now write the register B */
126    mov al, CMOS_REGISTER_B
127    out CMOS_ADDR, al
128    jmp $+2
129    mov al, ah
130    out CMOS_DATA, al
131    jmp $+2
132
133    /* Read register C */
134    mov al, CMOS_REGISTER_C
135    out CMOS_ADDR, al
136    jmp $+2
137    in al, CMOS_DATA
138    jmp $+2
139
140    /* Read register D */
141    mov al, CMOS_REGISTER_D
142    out CMOS_ADDR, al
143    jmp $+2
144    in al, CMOS_DATA
145    jmp $+2
146
147    /* Release CMOS lock */
148    mov dword ptr [ebp-12], 0
149    call _HalpReleaseCmosSpinLock@0
150
151    /* Initialize looper */
152    xor eax, eax
153
154    /* Align to 16 bytes */
155    .align 16
156
157    /* Enable interrupts! */
158    sti
159    jmp Looper
160
161    /* Align to 16 bytes */
162    .align 16
163
164    /* Subtract one count */
165Looper:
166    sub eax, 1
167    jnz Looper
168
169    /* ASSERT: If we got here, then the RTC never fired */
170    call _DbgBreakPoint@0
171    jmp Looper
172
173OnlyOnePersonCanWriteHalCode:
174    /*********************** THIS IS THE RTC HANDLER **************************/
175
176    /* Increment the interrupt count and check if this is the first one */
177    inc dword ptr [ebp-12]
178    cmp dword ptr [ebp-12], 1
179    jnz ComputeStall
180
181    /*
182     * It is the first one -- we'll ignore it, since it fires randomly!
183     * Get rid of the old return address and push the new one in (our looper)
184     */
185    pop eax
186    push offset Looper
187
188    /* Acquire CMOS lock */
189    call _HalpAcquireCmosSpinLock@0
190
191    /* Now initialize register A on the CMOS */
192    mov ax, HEX(2D00) OR CMOS_REGISTER_A
193    out CMOS_ADDR, al
194    jmp $+2
195    mov al, ah
196    out CMOS_DATA, al
197    jmp $+2
198
199    /* Read register B */
200    mov ax, CMOS_REGISTER_B
201    out CMOS_ADDR, al
202    jmp $+2
203    in al, CMOS_DATA
204    jmp $+2
205
206    /* Don't touch the LastKnownGoodConfig hack */
207    and al, 1
208    mov ah, al
209
210    /* Enable the interrupt */
211    or ah, HEX(42)
212
213    /* Now write the register B */
214    mov al, CMOS_REGISTER_B
215    out CMOS_ADDR, al
216    jmp $+2
217    mov al, ah
218    out CMOS_DATA, al
219    jmp $+2
220
221    /* Read register C */
222    mov al, CMOS_REGISTER_C
223    out CMOS_ADDR, al
224    jmp $+2
225    in al, CMOS_DATA
226    jmp $+2
227
228    /* Read register D */
229    mov al, CMOS_REGISTER_D
230    out CMOS_ADDR, al
231    jmp $+2
232    in al, CMOS_DATA
233    jmp $+2
234
235    /* Release CMOS lock */
236    call _HalpReleaseCmosSpinLock@0
237
238    /* Dismiss the interrupt */
239    mov al, PIC_EOI
240    out PIC2_COMMAND, al
241    mov al, PIC_SPECIFIC_EOI2
242    out PIC1_COMMAND, al
243
244    /* Reset the counter and return back to the looper */
245    xor eax, eax
246    iretd
247
248    /******************* THIS IS THE 2ND RTC HANDLER **************************/
249ComputeStall:
250
251    /* Do the calculation */
252    neg eax
253    xor edx, edx
254    mov ecx, 125000 /* RTC fires every 125 ms */
255    div ecx
256
257    /* Is the remainder 0? */
258    cmp edx, 0
259    jz FoundFactor
260
261    /* Otherwise fix-up the loop count */
262    inc eax
263
264FoundFactor:
265    /* Save the stall scale factor */
266    mov fs:[KPCR_STALL_SCALE_FACTOR], eax
267
268    /* Prepare for interrupt return */
269    pop eax
270    push offset AndItsNotYou
271    mov eax, HEX(13)
272
273    /* Acquire CMOS lock */
274    call _HalpAcquireCmosSpinLock@0
275
276    /* Now initialize register A on the CMOS */
277    mov ax, HEX(2D00) OR CMOS_REGISTER_A
278    out CMOS_ADDR, al
279    jmp $+2
280    mov al, ah
281    out CMOS_DATA, al
282    jmp $+2
283
284    /* Read register B */
285    mov ax, CMOS_REGISTER_B
286    out CMOS_ADDR, al
287    jmp $+2
288    in al, CMOS_DATA
289    jmp $+2
290
291    /* Don't touch the LastKnownGoodConfig hack */
292    and al, 1
293    mov ah, al
294
295    /* Disable the interrupt */
296    or ah, 2
297
298    /* Now write the register B */
299    mov al, CMOS_REGISTER_B
300    out CMOS_ADDR, al
301    jmp $+2
302    mov al, ah
303    out CMOS_DATA, al
304    jmp $+2
305
306    /* Read register C */
307    mov al, CMOS_REGISTER_C
308    out CMOS_ADDR, al
309    jmp $+2
310    in al, CMOS_DATA
311    jmp $+2
312
313    /* Release CMOS lock */
314    call _HalpReleaseCmosSpinLock@0
315
316    /* Dismiss the interrupt */
317    mov al, PIC_EOI
318    out PIC2_COMMAND, al
319    mov al, PIC_SPECIFIC_EOI2
320    out PIC1_COMMAND, al
321
322    /* Disable interrupts on return */
323    and word ptr [esp+8], NOT EFLAGS_INTERRUPT_MASK
324    iretd
325
326    /************************* WE ARE BACK FROM RTC ***************************/
327AndItsNotYou:
328
329    /* Restore the IDT */
330    pop ecx
331    pop [ecx+4]
332    pop [ecx]
333
334    /* Restore the mask */
335    pop eax
336    out PIC1_DATA, al
337    shr eax, 8
338    out PIC2_DATA, al
339
340    /* Restore EFLAGS */
341    popfd
342
343    /* Restore stack and return */
344    mov esp, ebp
345    pop ebp
346    ret
347
348
349#ifndef _MINIHAL_
350PUBLIC _KeStallExecutionProcessor@4
351_KeStallExecutionProcessor@4:
352
353    /* Get the number of microseconds required */
354    mov ecx, [esp+4]
355    jecxz Done
356
357    /* Multiply by the stall factor */
358    mov eax, fs:[KPCR_STALL_SCALE_FACTOR]
359    mul ecx
360
361    /* Jump to subtraction loop */
362    jmp SubtractLoop
363
364    /* Align to 16 bytes */
365    .align 16
366
367    /* Subtract one count */
368SubtractLoop:
369    sub eax, 1
370    jnz SubtractLoop
371
372Done:
373    /* Return */
374    ret 4
375#endif
376
377END
378