diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
index 0cc40bb2b..18e3208be 100644
--- a/grub-core/Makefile.core.def
+++ b/grub-core/Makefile.core.def
@@ -1686,6 +1686,22 @@ module = {
x86 = loader/xnu.c;
enable = x86;
+}
+
+module = {
+ name = random;
+ x86 = lib/i386/random.c;
+ common = lib/random.c;
+
+ i386_multiboot = kern/i386/tsc_pmtimer.c;
+ i386_coreboot = kern/i386/tsc_pmtimer.c;
+ i386_pc = kern/i386/tsc_pmtimer.c;
+
+ enable = i386_multiboot;
+ enable = i386_coreboot;
+ enable = i386_pc;
+ enable = i386_efi;
+ enable = x86_64_efi;
};
module = {
diff --git a/grub-core/lib/i386/random.c b/grub-core/lib/i386/random.c
new file mode 100644
index 000000000..cd83d2f8f
--- /dev/null
+++ b/grub-core/lib/i386/random.c
@@ -0,0 +1,103 @@
+/*
+ * 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);
+}
diff --git a/grub-core/lib/random.c b/grub-core/lib/random.c
new file mode 100644
index 000000000..43b966438
--- /dev/null
+++ b/grub-core/lib/random.c
@@ -0,0 +1,120 @@
+/*
+ * 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
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+grub_err_t
+grub_crypto_get_random (void *buffer, grub_size_t sz)
+{
+ /* This is an arbitrer between different methods.
+ TODO: Add more methods in the future. */
+ /* TODO: Add some PRNG smartness to reduce damage from bad entropy. */
+ if (grub_crypto_arch_get_random (buffer, sz))
+ return GRUB_ERR_NONE;
+ return grub_error (GRUB_ERR_IO, "no random sources found");
+}
+
+static int
+get_num_digits (int val)
+{
+ int ret = 0;
+ while (val != 0)
+ {
+ ret++;
+ val /= 10;
+ }
+ if (ret == 0)
+ return 1;
+ return ret;
+}
+
+#pragma GCC diagnostic ignored "-Wformat-nonliteral"
+
+static grub_err_t
+grub_cmd_hexdump_random (grub_command_t cmd __attribute__ ((unused)), int argc, char **args)
+{
+ grub_size_t length = 64;
+ grub_err_t err;
+ void *buffer;
+ grub_uint8_t *ptr;
+ int stats[256];
+ int i, digits = 2;
+ char template[10];
+
+ if (argc >= 1)
+ length = grub_strtoull (args[0], 0, 0);
+
+ if (length == 0)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "length pust be positive");
+
+ buffer = grub_malloc (length);
+ if (!buffer)
+ return grub_errno;
+
+ err = grub_crypto_get_random (buffer, length);
+ if (err)
+ {
+ grub_free (buffer);
+ return err;
+ }
+
+ hexdump (0, buffer, length);
+ grub_memset(stats, 0, sizeof(stats));
+ for (ptr = buffer; ptr < (grub_uint8_t *) buffer + length; ptr++)
+ stats[*ptr]++;
+ grub_printf ("Statistics:\n");
+ for (i = 0; i < 256; i++)
+ {
+ int z = get_num_digits (stats[i]);
+ if (z > digits)
+ digits = z;
+ }
+
+ grub_snprintf (template, sizeof (template), "%%0%dd ", digits);
+
+ for (i = 0; i < 256; i++)
+ {
+ grub_printf ("%s", template);//, stats[i]);
+ if ((i & 0xf) == 0xf)
+ grub_printf ("\n");
+ }
+
+ grub_free (buffer);
+
+ return 0;
+}
+
+static grub_command_t cmd;
+
+GRUB_MOD_INIT (random)
+{
+ cmd = grub_register_command ("hexdump_random", grub_cmd_hexdump_random,
+ N_("[LENGTH]"),
+ N_("Hexdump random data."));
+}
+
+GRUB_MOD_FINI (random)
+{
+ grub_unregister_command (cmd);
+}
diff --git a/include/grub/random.h b/include/grub/random.h
new file mode 100644
index 000000000..4b75c0e2b
--- /dev/null
+++ b/include/grub/random.h
@@ -0,0 +1,33 @@
+/*
+ * 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 .
+ */
+
+#ifndef GRUB_RANDOM_HEADER
+#define GRUB_RANDOM_HEADER 1
+
+#include
+#include
+
+/* Not peer-reviewed. May not be any better than string of zeros. */
+grub_err_t
+grub_crypto_get_random (void *buffer, grub_size_t sz);
+
+/* Do not use directly. Use grub_crypto_get_random instead. */
+int
+grub_crypto_arch_get_random (void *buffer, grub_size_t sz);
+
+#endif