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.

rtc_entropy.c 6.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  1. /*
  2. * Copyright (C) 2012 Michael Brown <mbrown@fensystems.co.uk>.
  3. *
  4. * This program is free software; you can redistribute it and/or
  5. * modify it under the terms of the GNU General Public License as
  6. * published by the Free Software Foundation; either version 2 of the
  7. * License, or any later version.
  8. *
  9. * This program is distributed in the hope that it will be useful, but
  10. * WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  12. * General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with this program; if not, write to the Free Software
  16. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  17. * 02110-1301, USA.
  18. *
  19. * You can also choose to distribute this program under the terms of
  20. * the Unmodified Binary Distribution Licence (as given in the file
  21. * COPYING.UBDL), provided that you have satisfied its requirements.
  22. */
  23. FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
  24. /** @file
  25. *
  26. * RTC-based entropy source
  27. *
  28. */
  29. #include <stdint.h>
  30. #include <string.h>
  31. #include <errno.h>
  32. #include <unistd.h>
  33. #include <biosint.h>
  34. #include <pic8259.h>
  35. #include <rtc.h>
  36. #include <ipxe/entropy.h>
  37. /** Maximum time to wait for an RTC interrupt, in milliseconds */
  38. #define RTC_MAX_WAIT_MS 100
  39. /** RTC interrupt handler */
  40. extern void rtc_isr ( void );
  41. /** Previous RTC interrupt handler */
  42. static struct segoff rtc_old_handler;
  43. /** Flag set by RTC interrupt handler */
  44. extern volatile uint8_t __text16 ( rtc_flag );
  45. #define rtc_flag __use_text16 ( rtc_flag )
  46. /**
  47. * Hook RTC interrupt handler
  48. *
  49. */
  50. static void rtc_hook_isr ( void ) {
  51. /* RTC interrupt handler */
  52. __asm__ __volatile__ (
  53. TEXT16_CODE ( "\nrtc_isr:\n\t"
  54. /* Preserve registers */
  55. "pushw %%ax\n\t"
  56. /* Set "interrupt triggered" flag */
  57. "movb $0x01, %%cs:rtc_flag\n\t"
  58. /* Read RTC status register C to
  59. * acknowledge interrupt
  60. */
  61. "movb %2, %%al\n\t"
  62. "outb %%al, %0\n\t"
  63. "inb %1\n\t"
  64. /* Send EOI */
  65. "movb $0x20, %%al\n\t"
  66. "outb %%al, $0xa0\n\t"
  67. "outb %%al, $0x20\n\t"
  68. /* Restore registers and return */
  69. "popw %%ax\n\t"
  70. "iret\n\t"
  71. "\nrtc_flag:\n\t"
  72. ".byte 0\n\t" )
  73. :
  74. : "i" ( CMOS_ADDRESS ), "i" ( CMOS_DATA ),
  75. "i" ( RTC_STATUS_C ) );
  76. hook_bios_interrupt ( RTC_INT, ( intptr_t ) rtc_isr, &rtc_old_handler );
  77. }
  78. /**
  79. * Unhook RTC interrupt handler
  80. *
  81. */
  82. static void rtc_unhook_isr ( void ) {
  83. int rc;
  84. rc = unhook_bios_interrupt ( RTC_INT, ( intptr_t ) rtc_isr,
  85. &rtc_old_handler );
  86. assert ( rc == 0 ); /* Should always be able to unhook */
  87. }
  88. /**
  89. * Enable RTC interrupts
  90. *
  91. */
  92. static void rtc_enable_int ( void ) {
  93. uint8_t status_b;
  94. /* Clear any stale pending interrupts via status register C */
  95. outb ( ( RTC_STATUS_C | CMOS_DISABLE_NMI ), CMOS_ADDRESS );
  96. inb ( CMOS_DATA );
  97. /* Set Periodic Interrupt Enable bit in status register B */
  98. outb ( ( RTC_STATUS_B | CMOS_DISABLE_NMI ), CMOS_ADDRESS );
  99. status_b = inb ( CMOS_DATA );
  100. outb ( ( RTC_STATUS_B | CMOS_DISABLE_NMI ), CMOS_ADDRESS );
  101. outb ( ( status_b | RTC_STATUS_B_PIE ), CMOS_DATA );
  102. /* Re-enable NMI and reset to default address */
  103. outb ( CMOS_DEFAULT_ADDRESS, CMOS_ADDRESS );
  104. inb ( CMOS_DATA ); /* Discard; may be needed on some platforms */
  105. }
  106. /**
  107. * Disable RTC interrupts
  108. *
  109. */
  110. static void rtc_disable_int ( void ) {
  111. uint8_t status_b;
  112. /* Clear Periodic Interrupt Enable bit in status register B */
  113. outb ( ( RTC_STATUS_B | CMOS_DISABLE_NMI ), CMOS_ADDRESS );
  114. status_b = inb ( CMOS_DATA );
  115. outb ( ( RTC_STATUS_B | CMOS_DISABLE_NMI ), CMOS_ADDRESS );
  116. outb ( ( status_b & ~RTC_STATUS_B_PIE ), CMOS_DATA );
  117. /* Re-enable NMI and reset to default address */
  118. outb ( CMOS_DEFAULT_ADDRESS, CMOS_ADDRESS );
  119. inb ( CMOS_DATA ); /* Discard; may be needed on some platforms */
  120. }
  121. /**
  122. * Check that entropy gathering is functional
  123. *
  124. * @ret rc Return status code
  125. */
  126. static int rtc_entropy_check ( void ) {
  127. unsigned int i;
  128. /* Check that RTC interrupts are working */
  129. rtc_flag = 0;
  130. for ( i = 0 ; i < RTC_MAX_WAIT_MS ; i++ ) {
  131. /* Allow interrupts to occur */
  132. __asm__ __volatile__ ( "sti\n\t"
  133. "nop\n\t"
  134. "nop\n\t"
  135. "cli\n\t" );
  136. /* Check for RTC interrupt flag */
  137. if ( rtc_flag )
  138. return 0;
  139. /* Delay */
  140. mdelay ( 1 );
  141. }
  142. DBGC ( &rtc_flag, "RTC timed out waiting for interrupt\n" );
  143. return -ETIMEDOUT;
  144. }
  145. /**
  146. * Enable entropy gathering
  147. *
  148. * @ret rc Return status code
  149. */
  150. static int rtc_entropy_enable ( void ) {
  151. int rc;
  152. /* Hook ISR and enable RTC interrupts */
  153. rtc_hook_isr();
  154. enable_irq ( RTC_IRQ );
  155. rtc_enable_int();
  156. /* Check that RTC interrupts are working */
  157. if ( ( rc = rtc_entropy_check() ) != 0 )
  158. goto err_check;
  159. return 0;
  160. err_check:
  161. rtc_disable_int();
  162. disable_irq ( RTC_IRQ );
  163. rtc_unhook_isr();
  164. return rc;
  165. }
  166. /**
  167. * Disable entropy gathering
  168. *
  169. */
  170. static void rtc_entropy_disable ( void ) {
  171. /* Disable RTC interrupts and unhook ISR */
  172. rtc_disable_int();
  173. disable_irq ( RTC_IRQ );
  174. rtc_unhook_isr();
  175. }
  176. /**
  177. * Measure a single RTC tick
  178. *
  179. * @ret delta Length of RTC tick (in TSC units)
  180. */
  181. uint8_t rtc_sample ( void ) {
  182. uint32_t before;
  183. uint32_t after;
  184. uint32_t temp;
  185. __asm__ __volatile__ (
  186. REAL_CODE ( /* Enable interrupts */
  187. "sti\n\t"
  188. /* Wait for RTC interrupt */
  189. "movb %b2, %%cs:rtc_flag\n\t"
  190. "\n1:\n\t"
  191. "xchgb %b2, %%cs:rtc_flag\n\t" /* Serialize */
  192. "testb %b2, %b2\n\t"
  193. "jz 1b\n\t"
  194. /* Read "before" TSC */
  195. "rdtsc\n\t"
  196. /* Store "before" TSC on stack */
  197. "pushl %0\n\t"
  198. /* Wait for another RTC interrupt */
  199. "xorb %b2, %b2\n\t"
  200. "movb %b2, %%cs:rtc_flag\n\t"
  201. "\n1:\n\t"
  202. "xchgb %b2, %%cs:rtc_flag\n\t" /* Serialize */
  203. "testb %b2, %b2\n\t"
  204. "jz 1b\n\t"
  205. /* Read "after" TSC */
  206. "rdtsc\n\t"
  207. /* Retrieve "before" TSC on stack */
  208. "popl %1\n\t"
  209. /* Disable interrupts */
  210. "cli\n\t"
  211. )
  212. : "=a" ( after ), "=d" ( before ), "=Q" ( temp )
  213. : "2" ( 0 ) );
  214. return ( after - before );
  215. }
  216. PROVIDE_ENTROPY_INLINE ( rtc, min_entropy_per_sample );
  217. PROVIDE_ENTROPY ( rtc, entropy_enable, rtc_entropy_enable );
  218. PROVIDE_ENTROPY ( rtc, entropy_disable, rtc_entropy_disable );
  219. PROVIDE_ENTROPY_INLINE ( rtc, get_noise );