Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

495 lines
14 KiB

  1. title "Stall Execution Support"
  2. ;++
  3. ;
  4. ; Copyright (c) 1989 Microsoft Corporation
  5. ;
  6. ; Module Name:
  7. ;
  8. ; ixstall.asm
  9. ;
  10. ; Abstract:
  11. ;
  12. ; This module implements the code necessary to field and process the
  13. ; interval clock interrupt.
  14. ;
  15. ; Author:
  16. ;
  17. ; Shie-Lin Tzong (shielint) 12-Jan-1990
  18. ;
  19. ; Environment:
  20. ;
  21. ; Kernel mode only.
  22. ;
  23. ; Revision History:
  24. ;
  25. ; bryanwi 20-Sep-90
  26. ;
  27. ; Add KiSetProfileInterval, KiStartProfileInterrupt,
  28. ; KiStopProfileInterrupt procedures.
  29. ; KiProfileInterrupt ISR.
  30. ; KiProfileList, KiProfileLock are delcared here.
  31. ;
  32. ; shielint 10-Dec-90
  33. ; Add performance counter support.
  34. ; Move system clock to irq8, ie we now use RTC to generate system
  35. ; clock. Performance count and Profile use timer 1 counter 0.
  36. ; The interval of the irq0 interrupt can be changed by
  37. ; KiSetProfileInterval. Performance counter does not care about the
  38. ; interval of the interrupt as long as it knows the rollover count.
  39. ; Note: Currently I implemented 1 performance counter for the whole
  40. ; i386 NT.
  41. ;
  42. ; John Vert (jvert) 11-Jul-1991
  43. ; Moved from ke\i386 to hal\i386. Removed non-HAL stuff
  44. ;
  45. ; shie-lin tzong (shielint) 13-March-92
  46. ; Move System clock back to irq0 and use RTC (irq8) to generate
  47. ; profile interrupt. Performance counter and system clock use time1
  48. ; counter 0 of 8254.
  49. ;
  50. ; Landy Wang (corollary!landy) 04-Dec-92
  51. ; Created this module by moving routines from ixclock.asm to here.
  52. ;
  53. ;--
  54. .386p
  55. .xlist
  56. include hal386.inc
  57. include callconv.inc ; calling convention macros
  58. include i386\ix8259.inc
  59. include i386\kimacro.inc
  60. include mac386.inc
  61. include i386\ixcmos.inc
  62. .list
  63. EXTRNP _DbgBreakPoint,0,IMPORT
  64. EXTRNP _HalpAcquireCmosSpinLock ,0
  65. EXTRNP _HalpReleaseCmosSpinLock ,0
  66. ;
  67. ; Constants used to initialize CMOS/Real Time Clock
  68. ;
  69. D_INT032 EQU 8E00h ; access word for 386 ring 0 interrupt gate
  70. RTCIRQ EQU 8 ; IRQ number for RTC interrupt
  71. REGISTER_B_ENABLE_PERIODIC_INTERRUPT EQU 01000010B
  72. ; RT/CMOS Register 'B' Init byte
  73. ; Values for byte shown are
  74. ; Bit 7 = Update inhibit
  75. ; Bit 6 = Periodic interrupt enable
  76. ; Bit 5 = Alarm interrupt disable
  77. ; Bit 4 = Update interrupt disable
  78. ; Bit 3 = Square wave disable
  79. ; Bit 2 = BCD data format
  80. ; Bit 1 = 24 hour time mode
  81. ; Bit 0 = Daylight Savings disable
  82. REGISTER_B_DISABLE_PERIODIC_INTERRUPT EQU 00000010B
  83. ;
  84. ; RegisterAInitByte sets 8Hz clock rate, used during init to set up
  85. ; KeStallExecutionProcessor, etc. (See RegASystemClockByte below.)
  86. ;
  87. RegisterAInitByte EQU 00101101B ; RT/CMOS Register 'A' init byte
  88. ; 32.768KHz Base divider rate
  89. ; 8Hz int rate, period = 125.0ms
  90. PeriodInMicroSecond EQU 125000 ;
  91. _DATA SEGMENT DWORD PUBLIC 'DATA'
  92. HalpStallCount dd 0
  93. _DATA ends
  94. INIT SEGMENT PARA PUBLIC 'CODE'
  95. ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING
  96. page ,132
  97. subttl "Initialize Stall Execution Counter"
  98. ;++
  99. ;
  100. ; VOID
  101. ; HalpInitializeStallExecution (
  102. ; IN CCHAR ProcessorNumber
  103. ; )
  104. ;
  105. ; Routine Description:
  106. ;
  107. ; This routine initialize the per Microsecond counter for
  108. ; KeStallExecutionProcessor
  109. ;
  110. ; Arguments:
  111. ;
  112. ; ProcessorNumber - Processor Number
  113. ;
  114. ; Return Value:
  115. ;
  116. ; None.
  117. ;
  118. ; Note:
  119. ;
  120. ; Current implementation assumes that all the processors share
  121. ; the same Real Time Clock. So, the dispatcher database lock is
  122. ; acquired before entering this routine to guarantee only one
  123. ; processor can access the routine.
  124. ;
  125. ;--
  126. KiseInterruptCount equ [ebp-12] ; local variable
  127. cPublicProc _HalpInitializeStallExecution ,1
  128. ifndef NT_UP
  129. ;;
  130. ;; This function currently doesn't work from any processor but the
  131. ;; boot processor - for now stub out the others
  132. ;;
  133. mov eax, PCR[PcPrcb]
  134. cmp byte ptr [eax].PbNumber, 0
  135. je @f
  136. mov eax, HalpStallCount
  137. mov PCR[PcStallScaleFactor], eax
  138. stdRET _HalpInitializeStallExecution
  139. @@:
  140. endif
  141. push ebp ; save ebp
  142. mov ebp, esp ; set up 12 bytes for local use
  143. sub esp, 12
  144. pushfd ; save caller's eflag
  145. ;
  146. ; Initialize Real Time Clock to interrupt us for every 125ms at
  147. ; IRQ 8.
  148. ;
  149. cli ; make sure interrupts are disabled
  150. ;
  151. ; Get and save current 8259 masks
  152. ;
  153. xor eax,eax
  154. ;
  155. ; Assume there is no third and fourth PICs
  156. ;
  157. ; Get interrupt Mask on PIC2
  158. ;
  159. in al,PIC2_PORT1
  160. shl eax, 8
  161. ;
  162. ; Get interrupt Mask on PIC1
  163. ;
  164. in al,PIC1_PORT1
  165. push eax ; save the masks
  166. mov eax, NOT (( 1 SHL PIC_SLAVE_IRQ) + (1 SHL RTCIRQ))
  167. ; Mask all the irqs except irq 2 and 8
  168. SET_8259_MASK ; Set 8259's int mask register
  169. ;
  170. ; Since RTC interrupt will come from IRQ 8, we need to
  171. ; Save original irq 8 descriptor and set the descriptor to point to
  172. ; our own handler.
  173. ;
  174. sidt fword ptr [ebp-8] ; get IDT address
  175. mov ecx, [ebp-6] ; (ecx)->IDT
  176. mov eax, (RTCIRQ+PRIMARY_VECTOR_BASE)
  177. shl eax, 3 ; 8 bytes per IDT entry
  178. add ecx, eax ; now at the correct IDT RTC entry
  179. push dword ptr [ecx] ; (TOS) = original desc of IRQ 8
  180. push dword ptr [ecx + 4] ; each descriptor has 8 bytes
  181. ;
  182. ; Pushing the appropriate entry address now (instead of
  183. ; the IDT start address later) to make the pop at the end simpler.
  184. ;
  185. push ecx ; (TOS) -> &IDT[HalProfileVector]
  186. mov eax, offset FLAT:RealTimeClockHandler
  187. mov word ptr [ecx], ax ; Lower half of handler addr
  188. mov word ptr [ecx+2], KGDT_R0_CODE ; set up selector
  189. mov word ptr [ecx+4], D_INT032 ; 386 interrupt gate
  190. shr eax, 16 ; (ax)=higher half of handler addr
  191. mov word ptr [ecx+6], ax
  192. mov dword ptr KiseinterruptCount, 0 ; set no interrupt yet
  193. stdCall _HalpAcquireCmosSpinLock ; intr disabled
  194. mov ax,(RegisterAInitByte SHL 8) OR 0AH ; Register A
  195. CMOS_WRITE ; Initialize it
  196. ;
  197. ; Don't clobber the Daylight Savings Time bit in register B, because we
  198. ; stash the LastKnownGood "environment variable" there.
  199. ;
  200. mov ax, 0bh
  201. CMOS_READ
  202. and al, 1
  203. mov ah, al
  204. or ah, REGISTER_B_ENABLE_PERIODIC_INTERRUPT
  205. mov al, 0bh
  206. CMOS_WRITE ; Initialize it
  207. mov al,0CH ; Register C
  208. CMOS_READ ; Read to initialize
  209. mov al,0DH ; Register D
  210. CMOS_READ ; Read to initialize
  211. mov dword ptr [KiseInterruptCount], 0
  212. stdCall _HalpReleaseCmosSpinLock
  213. ;
  214. ; Now enable the interrupt and start the counter
  215. ; (As a matter of fact, only IRQ8 can come through.)
  216. ;
  217. xor eax, eax ; (eax) = 0, initialize loopcount
  218. ALIGN 16
  219. sti
  220. jmp kise10
  221. ALIGN 16
  222. kise10:
  223. sub eax, 1 ; increment the loopcount
  224. jnz short kise10
  225. if DBG
  226. ;
  227. ; Counter overflowed
  228. ;
  229. stdCall _DbgBreakPoint
  230. endif
  231. jmp short kise10
  232. ;
  233. ; Our RealTimeClock interrupt handler. The control comes here through
  234. ; irq 8.
  235. ; Note: we discard first real time clock interrupt and compute the
  236. ; permicrosecond loopcount on receiving of the second real time
  237. ; interrupt. This is because the first interrupt is generated
  238. ; based on the previous real time tick interval.
  239. ;
  240. RealTimeClockHandler:
  241. inc dword ptr KiseInterruptCount ; increment interrupt count
  242. cmp dword ptr KiseInterruptCount,1 ; Is this the first interrupt?
  243. jnz kise25 ; no, its the second go process it
  244. pop eax ; get rid of original ret addr
  245. push offset FLAT:kise10 ; set new return addr
  246. stdCall _HalpAcquireCmosSpinLock ; intr disabled
  247. mov ax,(RegisterAInitByte SHL 8) OR 0AH ; Register A
  248. CMOS_WRITE ; Initialize it
  249. ;
  250. ; Don't clobber the Daylight Savings Time bit in register B, because we
  251. ; stash the LastKnownGood "environment variable" there.
  252. ;
  253. mov ax, 0bh
  254. CMOS_READ
  255. and al, 1
  256. mov ah, al
  257. or ah, REGISTER_B_ENABLE_PERIODIC_INTERRUPT
  258. mov al, 0bh
  259. CMOS_WRITE ; Initialize it
  260. mov al,0CH ; Register C
  261. CMOS_READ ; Read to initialize
  262. mov al,0DH ; Register D
  263. CMOS_READ ; Read to initialize
  264. stdCall _HalpReleaseCmosSpinLock
  265. ;
  266. ; Dismiss the interrupt.
  267. ;
  268. mov al, OCW2_NON_SPECIFIC_EOI ; send non specific eoi to slave
  269. out PIC2_PORT0, al
  270. mov al, PIC2_EOI ; specific eoi to master for pic2 eoi
  271. out PIC1_PORT0, al ; send irq2 specific eoi to master
  272. xor eax, eax ; reset loop counter
  273. iretd
  274. kise25:
  275. ;
  276. ; ** temporary - check for incorrect KeStallExecutionProcessorLoopCount
  277. ;
  278. if DBG
  279. cmp eax, 0
  280. jnz short kise30
  281. stdCall _DbgBreakPoint
  282. endif
  283. ; never return
  284. ;
  285. ; ** End temporay code
  286. ;
  287. kise30:
  288. neg eax
  289. xor edx, edx ; (edx:eax) = divident
  290. mov ecx, PeriodInMicroSecond; (ecx) = time spent in the loop
  291. div ecx ; (eax) = loop count per microsecond
  292. cmp edx, 0 ; Is remainder =0?
  293. jz short kise40 ; yes, go kise40
  294. inc eax ; increment loopcount by 1
  295. kise40:
  296. mov PCR[PcStallScaleFactor], eax
  297. mov HalpStallCount, eax
  298. ;
  299. ; Reset return address to kexit
  300. ;
  301. pop eax ; discard original return address
  302. push offset FLAT:kexit ; return to kexit
  303. mov eax, (HIGHEST_LEVEL_FOR_8259 - RTCIRQ)
  304. ;
  305. ; Shutdown periodic interrupt
  306. ;
  307. stdCall _HalpAcquireCmosSpinLock
  308. mov ax,(RegisterAInitByte SHL 8) OR 0AH ; Register A
  309. CMOS_WRITE ; Initialize it
  310. mov ax, 0bh
  311. CMOS_READ
  312. and al, 1
  313. mov ah, al
  314. or ah, REGISTER_B_DISABLE_PERIODIC_INTERRUPT
  315. mov al, 0bh
  316. CMOS_WRITE ; Initialize it
  317. mov al,0CH ; Register C
  318. CMOS_READ ; dismiss pending interrupt
  319. stdCall _HalpReleaseCmosSpinLock
  320. ;
  321. ; Dismiss the interrupt.
  322. ;
  323. mov eax, RTCIRQ
  324. mov al, OCW2_NON_SPECIFIC_EOI ; send non specific eoi to slave
  325. out PIC2_PORT0, al
  326. mov al, PIC2_EOI ; specific eoi to master for pic2 eoi
  327. out PIC1_PORT0, al ; send irq2 specific eoi to master
  328. and word ptr [esp+8], NOT 0200H ; Disable interrupt upon return
  329. iretd
  330. kexit: ; Interrupts are disabled
  331. pop ecx ; (ecx) -> &IDT[HalProfileVector]
  332. pop [ecx+4] ; restore higher half of RTC desc
  333. pop [ecx] ; restore lower half of RTC desc
  334. pop eax ; (eax) = origianl 8259 int masks
  335. SET_8259_MASK
  336. popfd ; restore caller's eflags
  337. mov esp, ebp
  338. pop ebp ; restore ebp
  339. stdRET _HalpInitializeStallExecution
  340. stdENDP _HalpInitializeStallExecution
  341. cPublicProc _HalpRemoveFences
  342. mov word ptr fence1, 0c98bh
  343. stdRET _HalpRemoveFences
  344. stdENDP _HalpRemoveFences
  345. INIT ends
  346. _TEXT SEGMENT PARA PUBLIC 'CODE'
  347. ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING
  348. page ,132
  349. subttl "Stall Execution"
  350. ;++
  351. ;
  352. ; VOID
  353. ; KeStallExecutionProcessor (
  354. ; IN ULONG MicroSeconds
  355. ; )
  356. ;
  357. ; Routine Description:
  358. ;
  359. ; This function stalls execution for the specified number of microseconds.
  360. ; KeStallExecutionProcessor
  361. ;
  362. ; Arguments:
  363. ;
  364. ; MicroSeconds - Supplies the number of microseconds that execution is to be
  365. ; stalled.
  366. ;
  367. ; Return Value:
  368. ;
  369. ; None.
  370. ;
  371. ;--
  372. MicroSeconds equ [esp + 4]
  373. cPublicProc _KeStallExecutionProcessor ,1
  374. cPublicFpo 1, 0
  375. ;
  376. ; Issue a CPUID to implement a "fence"
  377. ;
  378. push ebx ; cpuid uses eax, ebx, ecx, edx
  379. xor eax, eax ; Processor zero
  380. .586p
  381. fence1: cpuid
  382. .386p
  383. pop ebx
  384. mov ecx, MicroSeconds ; (ecx) = Microseconds
  385. jecxz short kese10 ; return if no loop needed
  386. mov eax, PCR[PcStallScaleFactor] ; get per microsecond
  387. ; loop count for the processor
  388. mul ecx ; (eax) = desired loop count
  389. if DBG
  390. ;
  391. ; Make sure we the loopcount is less than 4G and is not equal to zero
  392. ;
  393. cmp edx, 0
  394. jz short @f
  395. int 3
  396. @@: cmp eax,0
  397. jnz short @f
  398. int 3
  399. @@:
  400. endif
  401. ALIGN 16
  402. jmp kese05
  403. ALIGN 16
  404. kese05: sub eax, 1 ; (eax) = (eax) - 1
  405. jnz short kese05
  406. kese10:
  407. stdRET _KeStallExecutionProcessor
  408. stdENDP _KeStallExecutionProcessor
  409. _TEXT ends
  410. end