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