iPXE
rtc_entropy.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2012 Michael Brown <mbrown@fensystems.co.uk>.
00003  *
00004  * This program is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU General Public License as
00006  * published by the Free Software Foundation; either version 2 of the
00007  * License, or any later version.
00008  *
00009  * This program is distributed in the hope that it will be useful, but
00010  * WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012  * General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software
00016  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
00017  * 02110-1301, USA.
00018  *
00019  * You can also choose to distribute this program under the terms of
00020  * the Unmodified Binary Distribution Licence (as given in the file
00021  * COPYING.UBDL), provided that you have satisfied its requirements.
00022  */
00023 
00024 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
00025 
00026 /** @file
00027  *
00028  * RTC-based entropy source
00029  *
00030  */
00031 
00032 #include <stdint.h>
00033 #include <string.h>
00034 #include <errno.h>
00035 #include <unistd.h>
00036 #include <biosint.h>
00037 #include <pic8259.h>
00038 #include <rtc.h>
00039 #include <ipxe/entropy.h>
00040 
00041 /** Maximum time to wait for an RTC interrupt, in milliseconds */
00042 #define RTC_MAX_WAIT_MS 100
00043 
00044 /** RTC interrupt handler */
00045 extern void rtc_isr ( void );
00046 
00047 /** Previous RTC interrupt handler */
00048 static struct segoff rtc_old_handler;
00049 
00050 /** Flag set by RTC interrupt handler */
00051 extern volatile uint8_t __text16 ( rtc_flag );
00052 #define rtc_flag __use_text16 ( rtc_flag )
00053 
00054 /**
00055  * Hook RTC interrupt handler
00056  *
00057  */
00058 static void rtc_hook_isr ( void ) {
00059 
00060         /* RTC interrupt handler */
00061         __asm__ __volatile__ (
00062                 TEXT16_CODE ( "\nrtc_isr:\n\t"
00063                               /* Preserve registers */
00064                               "pushw %%ax\n\t"
00065                               /* Set "interrupt triggered" flag */
00066                               "movb $0x01, %%cs:rtc_flag\n\t"
00067                               /* Read RTC status register C to
00068                                * acknowledge interrupt
00069                                */
00070                               "movb %2, %%al\n\t"
00071                               "outb %%al, %0\n\t"
00072                               "inb %1\n\t"
00073                               /* Send EOI */
00074                               "movb $0x20, %%al\n\t"
00075                               "outb %%al, $0xa0\n\t"
00076                               "outb %%al, $0x20\n\t"
00077                               /* Restore registers and return */
00078                               "popw %%ax\n\t"
00079                               "iret\n\t"
00080                               "\nrtc_flag:\n\t"
00081                               ".byte 0\n\t" )
00082                 :
00083                 : "i" ( CMOS_ADDRESS ), "i" ( CMOS_DATA ),
00084                   "i" ( RTC_STATUS_C ) );
00085 
00086         hook_bios_interrupt ( RTC_INT, ( intptr_t ) rtc_isr, &rtc_old_handler );
00087 }
00088 
00089 /**
00090  * Unhook RTC interrupt handler
00091  *
00092  */
00093 static void rtc_unhook_isr ( void ) {
00094         int rc;
00095 
00096         rc = unhook_bios_interrupt ( RTC_INT, ( intptr_t ) rtc_isr,
00097                                      &rtc_old_handler );
00098         assert ( rc == 0 ); /* Should always be able to unhook */
00099 }
00100 
00101 /**
00102  * Enable RTC interrupts
00103  *
00104  */
00105 static void rtc_enable_int ( void ) {
00106         uint8_t status_b;
00107 
00108         /* Clear any stale pending interrupts via status register C */
00109         outb ( ( RTC_STATUS_C | CMOS_DISABLE_NMI ), CMOS_ADDRESS );
00110         inb ( CMOS_DATA );
00111 
00112         /* Set Periodic Interrupt Enable bit in status register B */
00113         outb ( ( RTC_STATUS_B | CMOS_DISABLE_NMI ), CMOS_ADDRESS );
00114         status_b = inb ( CMOS_DATA );
00115         outb ( ( RTC_STATUS_B | CMOS_DISABLE_NMI ), CMOS_ADDRESS );
00116         outb ( ( status_b | RTC_STATUS_B_PIE ), CMOS_DATA );
00117 
00118         /* Re-enable NMI and reset to default address */
00119         outb ( CMOS_DEFAULT_ADDRESS, CMOS_ADDRESS );
00120         inb ( CMOS_DATA ); /* Discard; may be needed on some platforms */
00121 }
00122 
00123 /**
00124  * Disable RTC interrupts
00125  *
00126  */
00127 static void rtc_disable_int ( void ) {
00128         uint8_t status_b;
00129 
00130         /* Clear Periodic Interrupt Enable bit in status register B */
00131         outb ( ( RTC_STATUS_B | CMOS_DISABLE_NMI ), CMOS_ADDRESS );
00132         status_b = inb ( CMOS_DATA );
00133         outb ( ( RTC_STATUS_B | CMOS_DISABLE_NMI ), CMOS_ADDRESS );
00134         outb ( ( status_b & ~RTC_STATUS_B_PIE ), CMOS_DATA );
00135 
00136         /* Re-enable NMI and reset to default address */
00137         outb ( CMOS_DEFAULT_ADDRESS, CMOS_ADDRESS );
00138         inb ( CMOS_DATA ); /* Discard; may be needed on some platforms */
00139 }
00140 
00141 /**
00142  * Check that entropy gathering is functional
00143  *
00144  * @ret rc              Return status code
00145  */
00146 static int rtc_entropy_check ( void ) {
00147         unsigned int i;
00148 
00149         /* Check that RTC interrupts are working */
00150         rtc_flag = 0;
00151         for ( i = 0 ; i < RTC_MAX_WAIT_MS ; i++ ) {
00152 
00153                 /* Allow interrupts to occur */
00154                 __asm__ __volatile__ ( "sti\n\t"
00155                                        "nop\n\t"
00156                                        "nop\n\t"
00157                                        "cli\n\t" );
00158 
00159                 /* Check for RTC interrupt flag */
00160                 if ( rtc_flag )
00161                         return 0;
00162 
00163                 /* Delay */
00164                 mdelay ( 1 );
00165         }
00166 
00167         DBGC ( &rtc_flag, "RTC timed out waiting for interrupt\n" );
00168         return -ETIMEDOUT;
00169 }
00170 
00171 /**
00172  * Enable entropy gathering
00173  *
00174  * @ret rc              Return status code
00175  */
00176 static int rtc_entropy_enable ( void ) {
00177         int rc;
00178 
00179         /* Hook ISR and enable RTC interrupts */
00180         rtc_hook_isr();
00181         enable_irq ( RTC_IRQ );
00182         rtc_enable_int();
00183 
00184         /* Check that RTC interrupts are working */
00185         if ( ( rc = rtc_entropy_check() ) != 0 )
00186                 goto err_check;
00187 
00188         return 0;
00189 
00190  err_check:
00191         rtc_disable_int();
00192         disable_irq ( RTC_IRQ );
00193         rtc_unhook_isr();
00194         return rc;
00195 }
00196 
00197 /**
00198  * Disable entropy gathering
00199  *
00200  */
00201 static void rtc_entropy_disable ( void ) {
00202 
00203         /* Disable RTC interrupts and unhook ISR */
00204         rtc_disable_int();
00205         disable_irq ( RTC_IRQ );
00206         rtc_unhook_isr();
00207 }
00208 
00209 /**
00210  * Measure a single RTC tick
00211  *
00212  * @ret delta           Length of RTC tick (in TSC units)
00213  */
00214 uint8_t rtc_sample ( void ) {
00215         uint32_t before;
00216         uint32_t after;
00217         uint32_t temp;
00218 
00219         __asm__ __volatile__ (
00220                 REAL_CODE ( /* Enable interrupts */
00221                             "sti\n\t"
00222                             /* Wait for RTC interrupt */
00223                             "movb %b2, %%cs:rtc_flag\n\t"
00224                             "\n1:\n\t"
00225                             "xchgb %b2, %%cs:rtc_flag\n\t" /* Serialize */
00226                             "testb %b2, %b2\n\t"
00227                             "jz 1b\n\t"
00228                             /* Read "before" TSC */
00229                             "rdtsc\n\t"
00230                             /* Store "before" TSC on stack */
00231                             "pushl %0\n\t"
00232                             /* Wait for another RTC interrupt */
00233                             "xorb %b2, %b2\n\t"
00234                             "movb %b2, %%cs:rtc_flag\n\t"
00235                             "\n1:\n\t"
00236                             "xchgb %b2, %%cs:rtc_flag\n\t" /* Serialize */
00237                             "testb %b2, %b2\n\t"
00238                             "jz 1b\n\t"
00239                             /* Read "after" TSC */
00240                             "rdtsc\n\t"
00241                             /* Retrieve "before" TSC on stack */
00242                             "popl %1\n\t"
00243                             /* Disable interrupts */
00244                             "cli\n\t"
00245                             )
00246                 : "=a" ( after ), "=d" ( before ), "=Q" ( temp )
00247                 : "2" ( 0 ) );
00248 
00249         return ( after - before );
00250 }
00251 
00252 PROVIDE_ENTROPY_INLINE ( rtc, min_entropy_per_sample );
00253 PROVIDE_ENTROPY ( rtc, entropy_enable, rtc_entropy_enable );
00254 PROVIDE_ENTROPY ( rtc, entropy_disable, rtc_entropy_disable );
00255 PROVIDE_ENTROPY_INLINE ( rtc, get_noise );