These changes are the raw update to qemu-2.6.
[kvmfornfv.git] / qemu / roms / ipxe / src / arch / i386 / interface / pcbios / rtc_entropy.c
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
24 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
25
26 /** @file
27  *
28  * RTC-based entropy source
29  *
30  */
31
32 #include <stdint.h>
33 #include <string.h>
34 #include <biosint.h>
35 #include <pic8259.h>
36 #include <rtc.h>
37 #include <ipxe/entropy.h>
38
39 /** RTC "interrupt triggered" flag */
40 static uint8_t __text16 ( rtc_flag );
41 #define rtc_flag __use_text16 ( rtc_flag )
42
43 /** RTC interrupt handler */
44 extern void rtc_isr ( void );
45
46 /** Previous RTC interrupt handler */
47 static struct segoff rtc_old_handler;
48
49 /**
50  * Hook RTC interrupt handler
51  *
52  */
53 static void rtc_hook_isr ( void ) {
54
55         /* RTC interrupt handler */
56         __asm__ __volatile__ (
57                 TEXT16_CODE ( "\nrtc_isr:\n\t"
58                               /* Preserve registers */
59                               "pushw %%ax\n\t"
60                               /* Set "interrupt triggered" flag */
61                               "cs movb $0x01, %c0\n\t"
62                               /* Read RTC status register C to
63                                * acknowledge interrupt
64                                */
65                               "movb %3, %%al\n\t"
66                               "outb %%al, %1\n\t"
67                               "inb %2\n\t"
68                               /* Send EOI */
69                               "movb $0x20, %%al\n\t"
70                               "outb %%al, $0xa0\n\t"
71                               "outb %%al, $0x20\n\t"
72                               /* Restore registers and return */
73                               "popw %%ax\n\t"
74                               "iret\n\t" )
75                 :
76                 : "p" ( __from_text16 ( &rtc_flag ) ),
77                   "i" ( CMOS_ADDRESS ), "i" ( CMOS_DATA ),
78                   "i" ( RTC_STATUS_C ) );
79
80         hook_bios_interrupt ( RTC_INT, ( unsigned int ) rtc_isr,
81                               &rtc_old_handler );
82 }
83
84 /**
85  * Unhook RTC interrupt handler
86  *
87  */
88 static void rtc_unhook_isr ( void ) {
89         int rc;
90
91         rc = unhook_bios_interrupt ( RTC_INT, ( unsigned int ) rtc_isr,
92                                      &rtc_old_handler );
93         assert ( rc == 0 ); /* Should always be able to unhook */
94 }
95
96 /**
97  * Enable RTC interrupts
98  *
99  */
100 static void rtc_enable_int ( void ) {
101         uint8_t status_b;
102
103         /* Set Periodic Interrupt Enable bit in status register B */
104         outb ( ( RTC_STATUS_B | CMOS_DISABLE_NMI ), CMOS_ADDRESS );
105         status_b = inb ( CMOS_DATA );
106         outb ( ( RTC_STATUS_B | CMOS_DISABLE_NMI ), CMOS_ADDRESS );
107         outb ( ( status_b | RTC_STATUS_B_PIE ), CMOS_DATA );
108
109         /* Re-enable NMI and reset to default address */
110         outb ( CMOS_DEFAULT_ADDRESS, CMOS_ADDRESS );
111         inb ( CMOS_DATA ); /* Discard; may be needed on some platforms */
112 }
113
114 /**
115  * Disable RTC interrupts
116  *
117  */
118 static void rtc_disable_int ( void ) {
119         uint8_t status_b;
120
121         /* Clear Periodic Interrupt Enable bit in status register B */
122         outb ( ( RTC_STATUS_B | CMOS_DISABLE_NMI ), CMOS_ADDRESS );
123         status_b = inb ( CMOS_DATA );
124         outb ( ( RTC_STATUS_B | CMOS_DISABLE_NMI ), CMOS_ADDRESS );
125         outb ( ( status_b & ~RTC_STATUS_B_PIE ), CMOS_DATA );
126
127         /* Re-enable NMI and reset to default address */
128         outb ( CMOS_DEFAULT_ADDRESS, CMOS_ADDRESS );
129         inb ( CMOS_DATA ); /* Discard; may be needed on some platforms */
130 }
131
132 /**
133  * Enable entropy gathering
134  *
135  * @ret rc              Return status code
136  */
137 static int rtc_entropy_enable ( void ) {
138
139         rtc_hook_isr();
140         enable_irq ( RTC_IRQ );
141         rtc_enable_int();
142
143         return 0;
144 }
145
146 /**
147  * Disable entropy gathering
148  *
149  */
150 static void rtc_entropy_disable ( void ) {
151
152         rtc_disable_int();
153         disable_irq ( RTC_IRQ );
154         rtc_unhook_isr();
155 }
156
157 /**
158  * Measure a single RTC tick
159  *
160  * @ret delta           Length of RTC tick (in TSC units)
161  */
162 uint8_t rtc_sample ( void ) {
163         uint32_t before;
164         uint32_t after;
165         uint32_t temp;
166
167         __asm__ __volatile__ (
168                 REAL_CODE ( /* Enable interrupts */
169                             "sti\n\t"
170                             /* Wait for RTC interrupt */
171                             "cs movb %b2, %c4\n\t"
172                             "\n1:\n\t"
173                             "cs xchgb %b2, %c4\n\t" /* Serialize */
174                             "testb %b2, %b2\n\t"
175                             "jz 1b\n\t"
176                             /* Read "before" TSC */
177                             "rdtsc\n\t"
178                             /* Store "before" TSC on stack */
179                             "pushl %0\n\t"
180                             /* Wait for another RTC interrupt */
181                             "xorb %b2, %b2\n\t"
182                             "cs movb %b2, %c4\n\t"
183                             "\n1:\n\t"
184                             "cs xchgb %b2, %c4\n\t" /* Serialize */
185                             "testb %b2, %b2\n\t"
186                             "jz 1b\n\t"
187                             /* Read "after" TSC */
188                             "rdtsc\n\t"
189                             /* Retrieve "before" TSC on stack */
190                             "popl %1\n\t"
191                             /* Disable interrupts */
192                             "cli\n\t"
193                             )
194                 : "=a" ( after ), "=d" ( before ), "=q" ( temp )
195                 : "2" ( 0 ), "p" ( __from_text16 ( &rtc_flag ) ) );
196
197         return ( after - before );
198 }
199
200 PROVIDE_ENTROPY_INLINE ( rtc, min_entropy_per_sample );
201 PROVIDE_ENTROPY ( rtc, entropy_enable, rtc_entropy_enable );
202 PROVIDE_ENTROPY ( rtc, entropy_disable, rtc_entropy_disable );
203 PROVIDE_ENTROPY_INLINE ( rtc, get_noise );