2 * Copyright (C) 2012 Michael Brown <mbrown@fensystems.co.uk>.
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.
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.
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
20 FILE_LICENCE ( GPL2_OR_LATER );
24 * RTC-based entropy source
33 #include <ipxe/entropy.h>
35 /** RTC "interrupt triggered" flag */
36 static uint8_t __text16 ( rtc_flag );
37 #define rtc_flag __use_text16 ( rtc_flag )
39 /** RTC interrupt handler */
40 extern void rtc_isr ( void );
42 /** Previous RTC interrupt handler */
43 static struct segoff rtc_old_handler;
46 * Hook RTC interrupt handler
49 static void rtc_hook_isr ( void ) {
51 /* RTC interrupt handler */
52 __asm__ __volatile__ (
53 TEXT16_CODE ( "\nrtc_isr:\n\t"
54 /* Preserve registers */
56 /* Set "interrupt triggered" flag */
57 "cs movb $0x01, %c0\n\t"
58 /* Read RTC status register C to
59 * acknowledge interrupt
65 "movb $0x20, %%al\n\t"
66 "outb %%al, $0xa0\n\t"
67 "outb %%al, $0x20\n\t"
68 /* Restore registers and return */
72 : "p" ( __from_text16 ( &rtc_flag ) ),
73 "i" ( CMOS_ADDRESS ), "i" ( CMOS_DATA ),
74 "i" ( RTC_STATUS_C ) );
76 hook_bios_interrupt ( RTC_INT, ( unsigned int ) rtc_isr,
81 * Unhook RTC interrupt handler
84 static void rtc_unhook_isr ( void ) {
87 rc = unhook_bios_interrupt ( RTC_INT, ( unsigned int ) rtc_isr,
89 assert ( rc == 0 ); /* Should always be able to unhook */
93 * Enable RTC interrupts
96 static void rtc_enable_int ( void ) {
99 /* Set Periodic Interrupt Enable bit in status register B */
100 outb ( ( RTC_STATUS_B | CMOS_DISABLE_NMI ), CMOS_ADDRESS );
101 status_b = inb ( CMOS_DATA );
102 outb ( ( RTC_STATUS_B | CMOS_DISABLE_NMI ), CMOS_ADDRESS );
103 outb ( ( status_b | RTC_STATUS_B_PIE ), CMOS_DATA );
105 /* Re-enable NMI and reset to default address */
106 outb ( CMOS_DEFAULT_ADDRESS, CMOS_ADDRESS );
107 inb ( CMOS_DATA ); /* Discard; may be needed on some platforms */
111 * Disable RTC interrupts
114 static void rtc_disable_int ( void ) {
117 /* Clear Periodic Interrupt Enable bit in status register B */
118 outb ( ( RTC_STATUS_B | CMOS_DISABLE_NMI ), CMOS_ADDRESS );
119 status_b = inb ( CMOS_DATA );
120 outb ( ( RTC_STATUS_B | CMOS_DISABLE_NMI ), CMOS_ADDRESS );
121 outb ( ( status_b & ~RTC_STATUS_B_PIE ), CMOS_DATA );
123 /* Re-enable NMI and reset to default address */
124 outb ( CMOS_DEFAULT_ADDRESS, CMOS_ADDRESS );
125 inb ( CMOS_DATA ); /* Discard; may be needed on some platforms */
129 * Enable entropy gathering
131 * @ret rc Return status code
133 static int rtc_entropy_enable ( void ) {
136 enable_irq ( RTC_IRQ );
143 * Disable entropy gathering
146 static void rtc_entropy_disable ( void ) {
149 disable_irq ( RTC_IRQ );
154 * Measure a single RTC tick
156 * @ret delta Length of RTC tick (in TSC units)
158 uint8_t rtc_sample ( void ) {
163 __asm__ __volatile__ (
164 REAL_CODE ( /* Enable interrupts */
166 /* Wait for RTC interrupt */
167 "cs movb %b2, %c4\n\t"
169 "cs xchgb %b2, %c4\n\t" /* Serialize */
172 /* Read "before" TSC */
174 /* Store "before" TSC on stack */
176 /* Wait for another RTC interrupt */
178 "cs movb %b2, %c4\n\t"
180 "cs xchgb %b2, %c4\n\t" /* Serialize */
183 /* Read "after" TSC */
185 /* Retrieve "before" TSC on stack */
187 /* Disable interrupts */
190 : "=a" ( after ), "=d" ( before ), "=q" ( temp )
191 : "2" ( 0 ), "p" ( __from_text16 ( &rtc_flag ) ) );
193 return ( after - before );
196 PROVIDE_ENTROPY_INLINE ( rtc, min_entropy_per_sample );
197 PROVIDE_ENTROPY ( rtc, entropy_enable, rtc_entropy_enable );
198 PROVIDE_ENTROPY ( rtc, entropy_disable, rtc_entropy_disable );
199 PROVIDE_ENTROPY_INLINE ( rtc, get_noise );