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