diff --git a/conf/i386.rmk b/conf/i386.rmk
index 93f84ce..5338457 100644
--- a/conf/i386.rmk
+++ b/conf/i386.rmk
@@ -14,3 +14,8 @@ pkglib_MODULES += vga_text.mod
vga_text_mod_SOURCES = term/i386/pc/vga_text.c term/i386/vga_common.c
vga_text_mod_CFLAGS = $(COMMON_CFLAGS)
vga_text_mod_LDFLAGS = $(COMMON_LDFLAGS)
+
+pkglib_MODULES += uppermem.mod
+uppermem_mod_SOURCES = lib/i386/uppermem.c
+uppermem_mod_CFLAGS = $(COMMON_CFLAGS)
+uppermem_mod_LDFLAGS = $(COMMON_LDFLAGS)
diff --git a/lib/i386/uppermem.c b/lib/i386/uppermem.c
new file mode 100644
index 0000000..623535f
--- /dev/null
+++ b/lib/i386/uppermem.c
@@ -0,0 +1,127 @@
+/* Compute amount of lower and upper memory till the first hole */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2009 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 EFIEMU
+#include
+#include
+#endif
+
+#include
+#include
+
+struct region
+{
+ grub_uint64_t start;
+ grub_uint64_t end;
+};
+
+#ifdef EFIEMU
+grub_err_t
+grub_efiemu_get_lower_upper_memory (grub_uint64_t *lower, grub_uint64_t *upper)
+#else
+grub_err_t
+grub_get_lower_upper_memory (grub_uint64_t *lower, grub_uint64_t *upper)
+#endif
+{
+ grub_size_t count = 0;
+ struct region *regions = 0;
+ int done = 1;
+ unsigned i;
+ struct region t;
+ grub_uint64_t last_addr;
+
+ auto int NESTED_FUNC_ATTR hook (grub_uint64_t, grub_uint64_t, grub_uint32_t);
+ int NESTED_FUNC_ATTR hook (grub_uint64_t addr, grub_uint64_t size,
+ grub_uint32_t type)
+ {
+#ifdef EFIEMU
+ if (type != GRUB_EFIEMU_MEMORY_AVAILABLE)
+#else
+ if (type != GRUB_MACHINE_MEMORY_AVAILABLE)
+#endif
+ return 0;
+ regions = (struct region *)
+ grub_realloc (regions, (count + 1) * sizeof (struct region));
+ regions[count].start = addr;
+ regions[count].end = addr + size;
+ count++;
+ return 0;
+ }
+
+#ifdef EFIEMU
+ grub_efiemu_mmap_iterate (hook);
+#else
+ grub_machine_mmap_iterate (hook);
+#endif
+
+ /* Bubble-sort the memory map */
+ while (done)
+ {
+ done = 0;
+ for (i = 0; i < count - 1; i++)
+ if (regions[i].start > regions[i + 1].start)
+ {
+ done = 1;
+ t = regions[i];
+ regions[i] = regions[i + 1];
+ regions[i + 1] = t;
+ }
+ }
+
+ /* Set mem_upper and mem_lower */
+ last_addr = 0;
+ for (i = 0; i < count; i++)
+ {
+ grub_uint64_t end = regions[i].end;
+ /* Don't use memory after 0xa0000*/
+ if (end > 0xa0000)
+ end = 0xa0000;
+
+ /* low memory is finished */
+ if (regions[i].start > end)
+ break;
+
+ /* A hole */
+ if (regions[i].start > last_addr)
+ break;
+
+ last_addr = end;
+ }
+
+ *lower = last_addr;
+
+ /* Skip low memory */
+ for (i = 0; i < count && regions[i].end <= 0x100000;
+ i++);
+
+ last_addr = 0x100000;
+ for (; i < count; i++)
+ {
+ /* A hole */
+ if (regions[i].start > last_addr)
+ break;
+
+ last_addr = regions[i].end;
+ }
+
+ *upper = (last_addr - 0x100000);
+ grub_free (regions);
+
+ return GRUB_ERR_NONE;
+}