random.c 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. /*
  2. * GRUB -- GRand Unified Bootloader
  3. * Copyright (C) 2016 Free Software Foundation, Inc.
  4. *
  5. * GRUB is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation, either version 3 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * GRUB is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. #include <grub/random.h>
  19. #include <grub/i386/io.h>
  20. #include <grub/i386/tsc.h>
  21. #include <grub/i386/pmtimer.h>
  22. #include <grub/acpi.h>
  23. static int have_tsc = -1, have_pmtimer = -1;
  24. static grub_port_t pmtimer_port;
  25. static int
  26. detect_pmtimer (void)
  27. {
  28. struct grub_acpi_fadt *fadt;
  29. fadt = grub_acpi_find_fadt ();
  30. if (!fadt)
  31. return 0;
  32. pmtimer_port = fadt->pmtimer;
  33. if (!pmtimer_port)
  34. return 0;
  35. return 1;
  36. }
  37. static int
  38. pmtimer_tsc_get_random_bit (void)
  39. {
  40. /* It's hard to come up with figures about pmtimer and tsc jitter but
  41. 50 ppm seems to be typical. So we need 10^6/50 tsc cycles to get drift
  42. of one tsc cycle. With TSC at least of 800 MHz it means 1/(50*800)
  43. = 1/40000 s or about 3579545 / 40000 = 90 pmtimer ticks.
  44. This gives us rate of 40000 bit/s or 5 kB/s.
  45. */
  46. grub_uint64_t tsc_diff;
  47. tsc_diff = grub_pmtimer_wait_count_tsc (pmtimer_port, 90);
  48. if (tsc_diff == 0)
  49. {
  50. have_pmtimer = 0;
  51. return -1;
  52. }
  53. return tsc_diff & 1;
  54. }
  55. static int
  56. pmtimer_tsc_get_random_byte (void)
  57. {
  58. grub_uint8_t ret = 0;
  59. int i, c;
  60. for (i = 0; i < 8; i++)
  61. {
  62. c = pmtimer_tsc_get_random_bit ();
  63. if (c < 0)
  64. return -1;
  65. ret |= c << i;
  66. }
  67. return ret;
  68. }
  69. static int
  70. pmtimer_fill_buffer (void *buffer, grub_size_t sz)
  71. {
  72. grub_uint8_t *p = buffer;
  73. int c;
  74. while (sz)
  75. {
  76. c = pmtimer_tsc_get_random_byte ();
  77. if (c < 0)
  78. return 0;
  79. *p++ = c;
  80. sz--;
  81. }
  82. return 1;
  83. }
  84. int
  85. grub_crypto_arch_get_random (void *buffer, grub_size_t sz)
  86. {
  87. if (have_tsc == -1)
  88. have_tsc = grub_cpu_is_tsc_supported ();
  89. if (!have_tsc)
  90. return 0;
  91. if (have_pmtimer == -1)
  92. have_pmtimer = detect_pmtimer ();
  93. if (!have_pmtimer)
  94. return 0;
  95. return pmtimer_fill_buffer (buffer, sz);
  96. }