/* * GRUB -- GRand Unified Bootloader * Copyright (C) 2016 Free Software Foundation, Inc. * * GRUB is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GRUB is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GRUB. If not, see . */ #include #include #include #include #include static int have_tsc = -1, have_pmtimer = -1; static grub_port_t pmtimer_port; static int detect_pmtimer (void) { struct grub_acpi_fadt *fadt; fadt = grub_acpi_find_fadt (); if (!fadt) return 0; pmtimer_port = fadt->pmtimer; if (!pmtimer_port) return 0; return 1; } static int pmtimer_tsc_get_random_bit (void) { /* It's hard to come up with figures about pmtimer and tsc jitter but 50 ppm seems to be typical. So we need 10^6/50 tsc cycles to get drift of one tsc cycle. With TSC at least of 800 MHz it means 1/(50*800) = 1/40000 s or about 3579545 / 40000 = 90 pmtimer ticks. This gives us rate of 40000 bit/s or 5 kB/s. */ grub_uint64_t tsc_diff; tsc_diff = grub_pmtimer_wait_count_tsc (pmtimer_port, 90); if (tsc_diff == 0) { have_pmtimer = 0; return -1; } return tsc_diff & 1; } static int pmtimer_tsc_get_random_byte (void) { grub_uint8_t ret = 0; int i, c; for (i = 0; i < 8; i++) { c = pmtimer_tsc_get_random_bit (); if (c < 0) return -1; ret |= c << i; } return ret; } static int pmtimer_fill_buffer (void *buffer, grub_size_t sz) { grub_uint8_t *p = buffer; int c; while (sz) { c = pmtimer_tsc_get_random_byte (); if (c < 0) return 0; *p++ = c; sz--; } return 1; } int grub_crypto_arch_get_random (void *buffer, grub_size_t sz) { if (have_tsc == -1) have_tsc = grub_cpu_is_tsc_supported (); if (!have_tsc) return 0; if (have_pmtimer == -1) have_pmtimer = detect_pmtimer (); if (!have_pmtimer) return 0; return pmtimer_fill_buffer (buffer, sz); }