~pmdj/ubuntu/trusty/qemu/2.9+applesmc+fadtv3

« back to all changes in this revision

Viewing changes to roms/ipxe/src/arch/x86/interface/pcbios/rtc_entropy.c

  • Committer: Phil Dennis-Jordan
  • Date: 2017-07-21 08:03:43 UTC
  • mfrom: (1.1.1)
  • Revision ID: phil@philjordan.eu-20170721080343-2yr2vdj7713czahv
New upstream release 2.9.0.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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 <errno.h>
 
35
#include <unistd.h>
 
36
#include <biosint.h>
 
37
#include <pic8259.h>
 
38
#include <rtc.h>
 
39
#include <ipxe/entropy.h>
 
40
 
 
41
/** Maximum time to wait for an RTC interrupt, in milliseconds */
 
42
#define RTC_MAX_WAIT_MS 100
 
43
 
 
44
/** RTC interrupt handler */
 
45
extern void rtc_isr ( void );
 
46
 
 
47
/** Previous RTC interrupt handler */
 
48
static struct segoff rtc_old_handler;
 
49
 
 
50
/** Flag set by RTC interrupt handler */
 
51
extern volatile uint8_t __text16 ( rtc_flag );
 
52
#define rtc_flag __use_text16 ( rtc_flag )
 
53
 
 
54
/**
 
55
 * Hook RTC interrupt handler
 
56
 *
 
57
 */
 
58
static void rtc_hook_isr ( void ) {
 
59
 
 
60
        /* RTC interrupt handler */
 
61
        __asm__ __volatile__ (
 
62
                TEXT16_CODE ( "\nrtc_isr:\n\t"
 
63
                              /* Preserve registers */
 
64
                              "pushw %%ax\n\t"
 
65
                              /* Set "interrupt triggered" flag */
 
66
                              "movb $0x01, %%cs:rtc_flag\n\t"
 
67
                              /* Read RTC status register C to
 
68
                               * acknowledge interrupt
 
69
                               */
 
70
                              "movb %2, %%al\n\t"
 
71
                              "outb %%al, %0\n\t"
 
72
                              "inb %1\n\t"
 
73
                              /* Send EOI */
 
74
                              "movb $0x20, %%al\n\t"
 
75
                              "outb %%al, $0xa0\n\t"
 
76
                              "outb %%al, $0x20\n\t"
 
77
                              /* Restore registers and return */
 
78
                              "popw %%ax\n\t"
 
79
                              "iret\n\t"
 
80
                              "\nrtc_flag:\n\t"
 
81
                              ".byte 0\n\t" )
 
82
                :
 
83
                : "i" ( CMOS_ADDRESS ), "i" ( CMOS_DATA ),
 
84
                  "i" ( RTC_STATUS_C ) );
 
85
 
 
86
        hook_bios_interrupt ( RTC_INT, ( intptr_t ) rtc_isr, &rtc_old_handler );
 
87
}
 
88
 
 
89
/**
 
90
 * Unhook RTC interrupt handler
 
91
 *
 
92
 */
 
93
static void rtc_unhook_isr ( void ) {
 
94
        int rc;
 
95
 
 
96
        rc = unhook_bios_interrupt ( RTC_INT, ( intptr_t ) rtc_isr,
 
97
                                     &rtc_old_handler );
 
98
        assert ( rc == 0 ); /* Should always be able to unhook */
 
99
}
 
100
 
 
101
/**
 
102
 * Enable RTC interrupts
 
103
 *
 
104
 */
 
105
static void rtc_enable_int ( void ) {
 
106
        uint8_t status_b;
 
107
 
 
108
        /* Clear any stale pending interrupts via status register C */
 
109
        outb ( ( RTC_STATUS_C | CMOS_DISABLE_NMI ), CMOS_ADDRESS );
 
110
        inb ( CMOS_DATA );
 
111
 
 
112
        /* Set 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
 
 
118
        /* Re-enable NMI and reset to default address */
 
119
        outb ( CMOS_DEFAULT_ADDRESS, CMOS_ADDRESS );
 
120
        inb ( CMOS_DATA ); /* Discard; may be needed on some platforms */
 
121
}
 
122
 
 
123
/**
 
124
 * Disable RTC interrupts
 
125
 *
 
126
 */
 
127
static void rtc_disable_int ( void ) {
 
128
        uint8_t status_b;
 
129
 
 
130
        /* Clear Periodic Interrupt Enable bit in status register B */
 
131
        outb ( ( RTC_STATUS_B | CMOS_DISABLE_NMI ), CMOS_ADDRESS );
 
132
        status_b = inb ( CMOS_DATA );
 
133
        outb ( ( RTC_STATUS_B | CMOS_DISABLE_NMI ), CMOS_ADDRESS );
 
134
        outb ( ( status_b & ~RTC_STATUS_B_PIE ), CMOS_DATA );
 
135
 
 
136
        /* Re-enable NMI and reset to default address */
 
137
        outb ( CMOS_DEFAULT_ADDRESS, CMOS_ADDRESS );
 
138
        inb ( CMOS_DATA ); /* Discard; may be needed on some platforms */
 
139
}
 
140
 
 
141
/**
 
142
 * Check that entropy gathering is functional
 
143
 *
 
144
 * @ret rc              Return status code
 
145
 */
 
146
static int rtc_entropy_check ( void ) {
 
147
        unsigned int i;
 
148
 
 
149
        /* Check that RTC interrupts are working */
 
150
        rtc_flag = 0;
 
151
        for ( i = 0 ; i < RTC_MAX_WAIT_MS ; i++ ) {
 
152
 
 
153
                /* Allow interrupts to occur */
 
154
                __asm__ __volatile__ ( "sti\n\t"
 
155
                                       "nop\n\t"
 
156
                                       "nop\n\t"
 
157
                                       "cli\n\t" );
 
158
 
 
159
                /* Check for RTC interrupt flag */
 
160
                if ( rtc_flag )
 
161
                        return 0;
 
162
 
 
163
                /* Delay */
 
164
                mdelay ( 1 );
 
165
        }
 
166
 
 
167
        DBGC ( &rtc_flag, "RTC timed out waiting for interrupt\n" );
 
168
        return -ETIMEDOUT;
 
169
}
 
170
 
 
171
/**
 
172
 * Enable entropy gathering
 
173
 *
 
174
 * @ret rc              Return status code
 
175
 */
 
176
static int rtc_entropy_enable ( void ) {
 
177
        int rc;
 
178
 
 
179
        /* Hook ISR and enable RTC interrupts */
 
180
        rtc_hook_isr();
 
181
        enable_irq ( RTC_IRQ );
 
182
        rtc_enable_int();
 
183
 
 
184
        /* Check that RTC interrupts are working */
 
185
        if ( ( rc = rtc_entropy_check() ) != 0 )
 
186
                goto err_check;
 
187
 
 
188
        return 0;
 
189
 
 
190
 err_check:
 
191
        rtc_disable_int();
 
192
        disable_irq ( RTC_IRQ );
 
193
        rtc_unhook_isr();
 
194
        return rc;
 
195
}
 
196
 
 
197
/**
 
198
 * Disable entropy gathering
 
199
 *
 
200
 */
 
201
static void rtc_entropy_disable ( void ) {
 
202
 
 
203
        /* Disable RTC interrupts and unhook ISR */
 
204
        rtc_disable_int();
 
205
        disable_irq ( RTC_IRQ );
 
206
        rtc_unhook_isr();
 
207
}
 
208
 
 
209
/**
 
210
 * Measure a single RTC tick
 
211
 *
 
212
 * @ret delta           Length of RTC tick (in TSC units)
 
213
 */
 
214
uint8_t rtc_sample ( void ) {
 
215
        uint32_t before;
 
216
        uint32_t after;
 
217
        uint32_t temp;
 
218
 
 
219
        __asm__ __volatile__ (
 
220
                REAL_CODE ( /* Enable interrupts */
 
221
                            "sti\n\t"
 
222
                            /* Wait for RTC interrupt */
 
223
                            "movb %b2, %%cs:rtc_flag\n\t"
 
224
                            "\n1:\n\t"
 
225
                            "xchgb %b2, %%cs:rtc_flag\n\t" /* Serialize */
 
226
                            "testb %b2, %b2\n\t"
 
227
                            "jz 1b\n\t"
 
228
                            /* Read "before" TSC */
 
229
                            "rdtsc\n\t"
 
230
                            /* Store "before" TSC on stack */
 
231
                            "pushl %0\n\t"
 
232
                            /* Wait for another RTC interrupt */
 
233
                            "xorb %b2, %b2\n\t"
 
234
                            "movb %b2, %%cs:rtc_flag\n\t"
 
235
                            "\n1:\n\t"
 
236
                            "xchgb %b2, %%cs:rtc_flag\n\t" /* Serialize */
 
237
                            "testb %b2, %b2\n\t"
 
238
                            "jz 1b\n\t"
 
239
                            /* Read "after" TSC */
 
240
                            "rdtsc\n\t"
 
241
                            /* Retrieve "before" TSC on stack */
 
242
                            "popl %1\n\t"
 
243
                            /* Disable interrupts */
 
244
                            "cli\n\t"
 
245
                            )
 
246
                : "=a" ( after ), "=d" ( before ), "=Q" ( temp )
 
247
                : "2" ( 0 ) );
 
248
 
 
249
        return ( after - before );
 
250
}
 
251
 
 
252
PROVIDE_ENTROPY_INLINE ( rtc, min_entropy_per_sample );
 
253
PROVIDE_ENTROPY ( rtc, entropy_enable, rtc_entropy_enable );
 
254
PROVIDE_ENTROPY ( rtc, entropy_disable, rtc_entropy_disable );
 
255
PROVIDE_ENTROPY_INLINE ( rtc, get_noise );