Add qemu 2.4.0
[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
20 FILE_LICENCE ( GPL2_OR_LATER );
21
22 /** @file
23  *
24  * RTC-based entropy source
25  *
26  */
27
28 #include <stdint.h>
29 #include <string.h>
30 #include <biosint.h>
31 #include <pic8259.h>
32 #include <rtc.h>
33 #include <ipxe/entropy.h>
34
35 /** RTC "interrupt triggered" flag */
36 static uint8_t __text16 ( rtc_flag );
37 #define rtc_flag __use_text16 ( rtc_flag )
38
39 /** RTC interrupt handler */
40 extern void rtc_isr ( void );
41
42 /** Previous RTC interrupt handler */
43 static struct segoff rtc_old_handler;
44
45 /**
46  * Hook RTC interrupt handler
47  *
48  */
49 static void rtc_hook_isr ( void ) {
50
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                               "cs movb $0x01, %c0\n\t"
58                               /* Read RTC status register C to
59                                * acknowledge interrupt
60                                */
61                               "movb %3, %%al\n\t"
62                               "outb %%al, %1\n\t"
63                               "inb %2\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                 :
72                 : "p" ( __from_text16 ( &rtc_flag ) ),
73                   "i" ( CMOS_ADDRESS ), "i" ( CMOS_DATA ),
74                   "i" ( RTC_STATUS_C ) );
75
76         hook_bios_interrupt ( RTC_INT, ( unsigned int ) rtc_isr,
77                               &rtc_old_handler );
78 }
79
80 /**
81  * Unhook RTC interrupt handler
82  *
83  */
84 static void rtc_unhook_isr ( void ) {
85         int rc;
86
87         rc = unhook_bios_interrupt ( RTC_INT, ( unsigned int ) rtc_isr,
88                                      &rtc_old_handler );
89         assert ( rc == 0 ); /* Should always be able to unhook */
90 }
91
92 /**
93  * Enable RTC interrupts
94  *
95  */
96 static void rtc_enable_int ( void ) {
97         uint8_t status_b;
98
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 );
104
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 */
108 }
109
110 /**
111  * Disable RTC interrupts
112  *
113  */
114 static void rtc_disable_int ( void ) {
115         uint8_t status_b;
116
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 );
122
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 */
126 }
127
128 /**
129  * Enable entropy gathering
130  *
131  * @ret rc              Return status code
132  */
133 static int rtc_entropy_enable ( void ) {
134
135         rtc_hook_isr();
136         enable_irq ( RTC_IRQ );
137         rtc_enable_int();
138
139         return 0;
140 }
141
142 /**
143  * Disable entropy gathering
144  *
145  */
146 static void rtc_entropy_disable ( void ) {
147
148         rtc_disable_int();
149         disable_irq ( RTC_IRQ );
150         rtc_unhook_isr();
151 }
152
153 /**
154  * Measure a single RTC tick
155  *
156  * @ret delta           Length of RTC tick (in TSC units)
157  */
158 uint8_t rtc_sample ( void ) {
159         uint32_t before;
160         uint32_t after;
161         uint32_t temp;
162
163         __asm__ __volatile__ (
164                 REAL_CODE ( /* Enable interrupts */
165                             "sti\n\t"
166                             /* Wait for RTC interrupt */
167                             "cs movb %b2, %c4\n\t"
168                             "\n1:\n\t"
169                             "cs xchgb %b2, %c4\n\t" /* Serialize */
170                             "testb %b2, %b2\n\t"
171                             "jz 1b\n\t"
172                             /* Read "before" TSC */
173                             "rdtsc\n\t"
174                             /* Store "before" TSC on stack */
175                             "pushl %0\n\t"
176                             /* Wait for another RTC interrupt */
177                             "xorb %b2, %b2\n\t"
178                             "cs movb %b2, %c4\n\t"
179                             "\n1:\n\t"
180                             "cs xchgb %b2, %c4\n\t" /* Serialize */
181                             "testb %b2, %b2\n\t"
182                             "jz 1b\n\t"
183                             /* Read "after" TSC */
184                             "rdtsc\n\t"
185                             /* Retrieve "before" TSC on stack */
186                             "popl %1\n\t"
187                             /* Disable interrupts */
188                             "cli\n\t"
189                             )
190                 : "=a" ( after ), "=d" ( before ), "=q" ( temp )
191                 : "2" ( 0 ), "p" ( __from_text16 ( &rtc_flag ) ) );
192
193         return ( after - before );
194 }
195
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 );