$NetBSD$

--- src/arch/netbsd/mapping/mapping.c.orig	2017-11-02 06:34:44.095265171 +0000
+++ src/arch/netbsd/mapping/mapping.c
@@ -0,0 +1,781 @@
+/*
+ * (C) Copyright 1992, ..., 2014 the "DOSEMU-Development-Team".
+ *
+ * for details see file COPYING in the DOSEMU distribution
+ */
+
+/*
+ * Purpose: memory mapping library.
+ *
+ * Authors: Stas Sergeev, Bart Oldeman.
+ * Initially started by Hans Lermen, old copyrights below:
+ */
+/* file mapping.c
+ *
+ * generic mapping driver interface
+ * (C) Copyright 2000, Hans Lermen, lermen@fgan.de
+ *
+ * NOTE: We handle _all_ memory mappings within the mapping drivers,
+ *       mmap-type as well as IPC shm, except for X-mitshm (in X.c),
+ *       which is a special case and doesn't involve DOS space atall.
+ *
+ *       If you ever need to do mapping within DOSEMU (except X-mitshm),
+ *       _always_ use the interface supplied by the mapping drivers!
+ *       ^^^^^^^^         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ */
+
+#include "emu.h"
+#include "hma.h"
+#include "utilities.h"
+#include "dos2linux.h"
+#include "kvm.h"
+#include "mapping.h"
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+
+#ifndef __x86_64__
+#undef MAP_32BIT
+#define MAP_32BIT 0
+#endif
+
+struct mem_map_struct {
+  off_t src;
+  void *base;
+  void *bkp_base;
+  dosaddr_t dst;
+  int len;
+  int mapped;
+};
+
+#define MAX_KMEM_MAPPINGS 4096
+static int kmem_mappings = 0;
+static struct mem_map_struct kmem_map[MAX_KMEM_MAPPINGS];
+
+static int init_done = 0;
+unsigned char *mem_base;
+char *lowmem_base;
+
+static struct mappingdrivers *mappingdrv[] = {
+#ifdef HAVE_SHM_OPEN
+  &mappingdriver_shm, /* first try shm_open */
+#endif
+  &mappingdriver_ashm,  /* then anon-shared-mmap */
+  &mappingdriver_file, /* and then a temp file */
+};
+
+static struct mappingdrivers *mappingdriver;
+
+/* The alias map is used to track alias mappings from the first 1MB + HMA
+   to the corresponding addresses in Linux address space (either lowmem,
+   vgaemu, or EMS). The DOS address (&mem_base[address]) may be r/w
+   protected by cpuemu or vgaemu, but the alias is never protected,
+   so it can be used to write without needing to unprotect and reprotect
+   afterwards.
+   If the alias is not used (hardware RAM from /dev/mem, or DPMI memory
+   (aliasing using fn 0x509 is safely ignored here)),
+   the address is identity-mapped to &mem_base[address].
+*/
+static unsigned char *aliasmap[(LOWMEM_SIZE+HMASIZE)/PAGE_SIZE];
+
+static void update_aliasmap(dosaddr_t dosaddr, size_t mapsize,
+			    unsigned char *unixaddr)
+{
+  unsigned int dospage, i;
+
+  if (dosaddr >= LOWMEM_SIZE+HMASIZE)
+    return;
+  dospage = dosaddr >> PAGE_SHIFT;
+  for (i = 0; i < mapsize >> PAGE_SHIFT; i++)
+    aliasmap[dospage + i] = unixaddr ? unixaddr + (i << PAGE_SHIFT) : NULL;
+}
+
+void *dosaddr_to_unixaddr(unsigned int addr)
+{
+  if (addr < LOWMEM_SIZE + HMASIZE && aliasmap[addr >> PAGE_SHIFT])
+    return aliasmap[addr >> PAGE_SHIFT] + (addr & (PAGE_SIZE - 1));
+  return MEM_BASE32(addr);
+}
+
+void *physaddr_to_unixaddr(unsigned int addr)
+{
+  if (addr < LOWMEM_SIZE + HMASIZE)
+    return dosaddr_to_unixaddr(addr);
+  /* XXX something other than XMS? */
+  return &ext_mem_base[addr - (LOWMEM_SIZE + HMASIZE)];
+}
+
+static int map_find_idx(struct mem_map_struct *map, int max, off_t addr)
+{
+  int i;
+  for (i = 0; i < max; i++) {
+    if (map[i].src == addr)
+      return i;
+  }
+  return -1;
+}
+
+static int map_find(struct mem_map_struct *map, int max,
+  dosaddr_t addr, int size, int mapped)
+{
+  int i;
+  dosaddr_t dst, dst1;
+  dosaddr_t min = -1;
+  int idx = -1;
+  dosaddr_t max_addr = addr + size;
+  for (i = 0; i < max; i++) {
+    if (!map[i].dst || !map[i].len || map[i].mapped != mapped)
+      continue;
+    dst = map[i].dst;
+    dst1 = dst + map[i].len;
+    if (dst >= addr && dst < max_addr) {
+      if (min == (dosaddr_t)-1 || dst < min) {
+        min = dst;
+        idx = i;
+      }
+    }
+    if (dst1 > addr && dst < max_addr) {
+      if (min == (dosaddr_t)-1 || dst1 < min) {
+        min = addr;
+        idx = i;
+      }
+    }
+  }
+  return idx;
+}
+
+static dosaddr_t kmem_unmap_single(int idx)
+{
+  kmem_map[idx].base = mmap(0, kmem_map[idx].len, PROT_NONE,
+	       MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+  mremap(MEM_BASE32(kmem_map[idx].dst), kmem_map[idx].len,
+      kmem_map[idx].len, MREMAP_MAYMOVE | MREMAP_FIXED, kmem_map[idx].base);
+  kmem_map[idx].mapped = 0;
+  update_aliasmap(kmem_map[idx].dst, kmem_map[idx].len, NULL);
+  return kmem_map[idx].dst;
+}
+
+static int kmem_unmap_mapping(dosaddr_t addr, int mapsize)
+{
+  int i, cnt = 0;
+
+  while ((i = map_find(kmem_map, kmem_mappings, addr, mapsize, 1)) != -1) {
+    kmem_unmap_single(i);
+    cnt++;
+  }
+  return cnt;
+}
+
+static void kmem_map_single(int idx, dosaddr_t targ)
+{
+  assert(targ != (dosaddr_t)-1);
+  mremap(kmem_map[idx].base, kmem_map[idx].len, kmem_map[idx].len,
+      MREMAP_MAYMOVE | MREMAP_FIXED, MEM_BASE32(targ));
+  kmem_map[idx].dst = targ;
+  kmem_map[idx].mapped = 1;
+  update_aliasmap(targ, kmem_map[idx].len, kmem_map[idx].bkp_base);
+}
+
+void *alias_mapping_high(int cap, size_t mapsize, int protect, void *source)
+{
+  void *target = (void *)-1;
+
+#ifdef __x86_64__
+  /* use MAP_32BIT also for MAPPING_INIT_LOWRAM until simx86 is 64bit-safe */
+  if (cap & (MAPPING_DPMI|MAPPING_VGAEMU|MAPPING_INIT_LOWRAM)) {
+    target = mmap(NULL, mapsize, protect,
+		MAP_PRIVATE | MAP_ANONYMOUS | MAP_32BIT, -1, 0);
+    if (target == MAP_FAILED) {
+      error("mmap MAP_32BIT failed, %s\n", strerror(errno));
+      return MAP_FAILED;
+    }
+  }
+#endif
+
+  return mappingdriver->alias(cap, target, mapsize, protect, source);
+}
+
+int alias_mapping(int cap, dosaddr_t targ, size_t mapsize, int protect, void *source)
+{
+  void *target, *addr;
+  int ku;
+
+  assert(targ != (dosaddr_t)-1);
+  Q__printf("MAPPING: alias, cap=%s, targ=%#x, size=%zx, protect=%x, source=%p\n",
+	cap, targ, mapsize, protect, source);
+  /* for non-zero INIT_LOWRAM the target is a hint */
+  target = MEM_BASE32(targ);
+  ku = kmem_unmap_mapping(targ, mapsize);
+  if (ku)
+    dosemu_error("Found %i kmem mappings at %#x\n", ku, targ);
+
+  addr = mappingdriver->alias(cap, target, mapsize, protect, source);
+  if (addr == MAP_FAILED)
+    return -1;
+  if (targ != (dosaddr_t)-1)
+    update_aliasmap(targ, mapsize, source);
+  if (config.cpu_vm == CPUVM_KVM)
+    mprotect_kvm(addr, mapsize, protect);
+  Q__printf("MAPPING: %s alias created at %p\n", cap, addr);
+
+  return 0;
+}
+
+static void *mmap_mapping_kmem(int cap, dosaddr_t targ, size_t mapsize,
+	off_t source)
+{
+  int i;
+  void *target;
+
+  Q__printf("MAPPING: map kmem, cap=%s, target=%x, size=%zx, source=%#jx\n",
+	cap, targ, mapsize, (intmax_t)source);
+
+  i = map_find_idx(kmem_map, kmem_mappings, source);
+  if (i == -1) {
+	error("KMEM mapping for %#jx was not allocated!\n", (intmax_t)source);
+	return MAP_FAILED;
+  }
+  if (kmem_map[i].len != mapsize) {
+	error("KMEM mapping for %#jx allocated for size %#x, but %#zx requested\n",
+	      (intmax_t)source, kmem_map[i].len, mapsize);
+	return MAP_FAILED;
+  }
+
+  if (targ == (dosaddr_t)-1) {
+    target = mmap(NULL, mapsize, PROT_READ | PROT_WRITE,
+                MAP_PRIVATE | MAP_ANONYMOUS | MAP_32BIT, -1, 0);
+    if (target == MAP_FAILED) {
+      error("mmap MAP_32BIT failed, %s\n", strerror(errno));
+      return target;
+    }
+    targ = DOSADDR_REL(target);
+  } else {
+    target = MEM_BASE32(targ);
+  }
+  kmem_map_single(i, targ);
+
+  return target;
+}
+
+static void munmap_mapping_kmem(int cap, dosaddr_t addr, size_t mapsize)
+{
+  int i, rc;
+
+  Q__printf("MAPPING: unmap kmem, cap=%s, target=%x, size=%zx\n",
+	cap, addr, mapsize);
+
+  while ((i = map_find(kmem_map, kmem_mappings, addr, mapsize, 1)) != -1) {
+    dosaddr_t old_vbase = kmem_unmap_single(i);
+    if (!(cap & MAPPING_SCRATCH))
+      continue;
+    if (old_vbase < LOWMEM_SIZE) {
+      rc = alias_mapping(MAPPING_LOWMEM, old_vbase, mapsize,
+	    PROT_READ | PROT_WRITE, LOWMEM(old_vbase));
+    } else {
+      unsigned char *p;
+      p = mmap_mapping_ux(MAPPING_SCRATCH, MEM_BASE32(old_vbase), mapsize,
+          PROT_READ | PROT_WRITE);
+      if (p == MAP_FAILED)
+        rc = -1;
+    }
+    if (rc == -1) {
+      error("failure unmapping kmem region\n");
+      continue;
+    }
+  }
+}
+
+static int mapping_is_hole(void *start, size_t size)
+{
+  unsigned beg = (uintptr_t)start;
+  unsigned end = beg + size;
+  return (mapping_find_hole(beg, end, size) == start);
+}
+
+static void *do_mmap_mapping(int cap, void *target, size_t mapsize, int protect)
+{
+  void *addr;
+  int flags = (target != (void *)-1) ? MAP_FIXED : 0;
+
+  if (cap & MAPPING_NOOVERLAP) {
+    if (!flags)
+      cap &= ~MAPPING_NOOVERLAP;
+    else if (target)
+      flags &= ~MAP_FIXED;
+    /* not removing MAP_FIXED when mapping to 0 */
+    else if (!mapping_is_hole(target, mapsize))
+      return MAP_FAILED;
+  }
+  if (target == (void *)-1) target = NULL;
+#ifdef __x86_64__
+  if (flags == 0 && (cap & (MAPPING_DPMI|MAPPING_VGAEMU|MAPPING_INIT_LOWRAM)))
+    flags = MAP_32BIT;
+#endif
+  addr = mmap(target, mapsize, protect,
+		MAP_PRIVATE | flags | MAP_ANONYMOUS, -1, 0);
+  if (addr == MAP_FAILED)
+    return addr;
+  if ((cap & MAPPING_NOOVERLAP) && addr != target) {
+    munmap(addr, mapsize);
+    return MAP_FAILED;
+  }
+
+  return addr;
+}
+
+void *mmap_mapping_ux(int cap, void *target, size_t mapsize, int protect)
+{
+  return do_mmap_mapping(cap, target, mapsize, protect);
+}
+
+void *mmap_mapping(int cap, dosaddr_t targ, size_t mapsize, int protect)
+{
+  void *addr;
+  void *target = ((targ == (dosaddr_t)-1) ? (void *)-1 : MEM_BASE32(targ));
+
+  Q__printf("MAPPING: map, cap=%s, target=%p, size=%zx, protect=%x\n",
+	cap, target, mapsize, protect);
+  if (!(cap & MAPPING_INIT_LOWRAM) && targ != (dosaddr_t)-1) {
+    int ku;
+    /* for lowram we use alias_mapping() instead */
+    assert(targ >= LOWMEM_SIZE);
+    ku = kmem_unmap_mapping(targ, mapsize);
+    if (ku)
+      dosemu_error("Found %i kmem mappings at %#x\n", ku, targ);
+  }
+
+  addr = do_mmap_mapping(cap, target, mapsize, protect);
+  if (addr == MAP_FAILED)
+    return MAP_FAILED;
+  if (targ != (dosaddr_t)-1)
+    update_aliasmap(targ, mapsize, addr);
+  Q__printf("MAPPING: map success, cap=%s, addr=%p\n", cap, addr);
+  if (config.cpu_vm == CPUVM_KVM)
+    mprotect_kvm(addr, mapsize, protect);
+  return addr;
+}
+
+int mprotect_mapping(int cap, dosaddr_t targ, size_t mapsize, int protect)
+{
+  int ret;
+  void *addr = MEM_BASE32(targ);
+
+  Q__printf("MAPPING: mprotect, cap=%s, addr=%p, size=%zx, protect=%x\n",
+	cap, addr, mapsize, protect);
+  /* it is important to r/o protect the KVM guest page tables BEFORE
+     calling mprotect as this function is called by parallel threads
+     (vgaemu.c:_vga_emu_update).
+     Otherwise the page can be r/w in the guest but r/o on the host which
+     causes KVM to exit with EFAULT when the guest writes there.
+     We do not need to worry about caching/TLBs because the kernel will
+     walk the guest page tables (see kernel:
+     Documentation/virtual/kvm/mmu.txt:
+     - if needed, walk the guest page tables to determine the guest translation
+       (gva->gpa or ngpa->gpa)
+       - if permissions are insufficient, reflect the fault back to the guest)
+  */
+  if (config.cpu_vm == CPUVM_KVM)
+    mprotect_kvm(addr, mapsize, protect);
+  ret = mprotect(addr, mapsize, protect);
+  if (ret)
+    error("mprotect() failed: %s\n", strerror(errno));
+  return ret;
+}
+
+/*
+ * This gets called on DOSEMU startup to determine the kind of mapping
+ * and setup the appropriate function pointers
+ */
+void mapping_init(void)
+{
+  int i, found = -1;
+  int numdrivers = sizeof(mappingdrv) / sizeof(mappingdrv[0]);
+
+  if (init_done) return;
+
+  if (config.mappingdriver && strcmp(config.mappingdriver, "auto")) {
+    /* first try the mapping driver the user wants */
+    for (i=0; i < numdrivers; i++) {
+      if (!strcmp(mappingdrv[i]->key, config.mappingdriver)) {
+        found = i;
+        break;
+      }
+    }
+    if (found < 0) {
+      error("Wrong mapping driver specified: %s\n", config.mappingdriver);
+      leavedos(2);
+    }
+  }
+  if (found < 0) found = 0;
+  for (i=found; i < numdrivers; i++) {
+    if (mappingdrv[i] && (*mappingdrv[i]->open)(MAPPING_PROBE)) {
+      mappingdriver = mappingdrv[i];
+      Q_printf("MAPPING: using the %s driver\n", mappingdriver->name);
+      init_done = 1;
+      return;
+    }
+    if (found > 0) {
+      found = -1;
+      /* As we want to restart the loop, because of 'i++' at end of loop,
+       * we need to set 'i = -1'
+       */
+      i = -1;
+    }
+  }
+  error("MAPPING: cannot allocate an appropriate mapping driver\n");
+  leavedos(2);
+}
+
+/* this gets called on DOSEMU termination cleanup all mapping stuff */
+void mapping_close(void)
+{
+  if (init_done && mappingdriver->close) close_mapping(MAPPING_ALL);
+}
+
+static char dbuf[256];
+char *decode_mapping_cap(int cap)
+{
+  char *p = dbuf;
+  p[0] = 0;
+  if (!cap) return dbuf;
+  if ((cap & MAPPING_ALL) == MAPPING_ALL) {
+    p += sprintf(p, " ALL");
+  }
+  else {
+    if (cap & MAPPING_OTHER) p += sprintf(p, " OTHER");
+    if (cap & MAPPING_EMS) p += sprintf(p, " EMS");
+    if (cap & MAPPING_DPMI) p += sprintf(p, " DPMI");
+    if (cap & MAPPING_VGAEMU) p += sprintf(p, " VGAEMU");
+    if (cap & MAPPING_VIDEO) p += sprintf(p, " VIDEO");
+    if (cap & MAPPING_VC) p += sprintf(p, " VC");
+    if (cap & MAPPING_HGC) p += sprintf(p, " HGC");
+    if (cap & MAPPING_HMA) p += sprintf(p, " HMA");
+    if (cap & MAPPING_SHARED) p += sprintf(p, " SHARED");
+    if (cap & MAPPING_INIT_HWRAM) p += sprintf(p, " INIT_HWRAM");
+    if (cap & MAPPING_INIT_LOWRAM) p += sprintf(p, " INIT_LOWRAM");
+    if (cap & MAPPING_EXTMEM) p += sprintf(p, " EXTMEM");
+  }
+  if (cap & MAPPING_KMEM) p += sprintf(p, " KMEM");
+  if (cap & MAPPING_LOWMEM) p += sprintf(p, " LOWMEM");
+  if (cap & MAPPING_SCRATCH) p += sprintf(p, " SCRATCH");
+  if (cap & MAPPING_SINGLE) p += sprintf(p, " SINGLE");
+  if (cap & MAPPING_MAYSHARE) p += sprintf(p, " MAYSHARE");
+  return dbuf;
+}
+
+int open_mapping(int cap)
+{
+  return mappingdriver->open(cap);
+}
+
+void close_mapping(int cap)
+{
+  if (mappingdriver->close) mappingdriver->close(cap);
+}
+
+static void *alloc_mapping_kmem(size_t mapsize, off_t source)
+{
+    void *addr, *addr2;
+
+    Q_printf("MAPPING: alloc kmem, source=%#jx size=%#zx\n",
+             (intmax_t)source, mapsize);
+    if (source == -1) {
+      error("KMEM mapping without source\n");
+      leavedos(64);
+    }
+    if (map_find_idx(kmem_map, kmem_mappings, source) != -1) {
+      error("KMEM mapping for %#jx allocated twice!\n", (intmax_t)source);
+      return MAP_FAILED;
+    }
+    open_kmem();
+    addr = mmap(0, mapsize, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_32BIT,
+		mem_fd, source);
+    addr2 = mmap(0, mapsize, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_32BIT,
+		mem_fd, source);
+    close_kmem();
+    if (addr == MAP_FAILED || addr2 == MAP_FAILED)
+      return MAP_FAILED;
+
+    kmem_map[kmem_mappings].src = source;
+    kmem_map[kmem_mappings].base = addr;
+    kmem_map[kmem_mappings].bkp_base = addr2;
+    kmem_map[kmem_mappings].dst = -1;
+    kmem_map[kmem_mappings].len = mapsize;
+    kmem_map[kmem_mappings].mapped = 0;
+    kmem_mappings++;
+    Q_printf("MAPPING: region allocated at %p\n", addr);
+    return addr;
+}
+
+void *alloc_mapping(int cap, size_t mapsize)
+{
+  void *addr;
+
+  Q__printf("MAPPING: alloc, cap=%s size=%#zx\n", cap, mapsize);
+  addr = mappingdriver->alloc(cap, mapsize);
+  mprotect(addr, mapsize, PROT_READ | PROT_WRITE);
+
+  if (cap & MAPPING_INIT_LOWRAM) {
+    Q__printf("MAPPING: LOWRAM_INIT, cap=%s, base=%p\n", cap, addr);
+    lowmem_base = addr;
+  }
+  Q__printf("MAPPING: %s allocated at %p\n", cap, addr);
+  return addr;
+}
+
+void free_mapping(int cap, void *addr, size_t mapsize)
+{
+  if (cap & MAPPING_KMEM) {
+    return;
+  }
+  mprotect(addr, mapsize, PROT_READ | PROT_WRITE);
+  mappingdriver->free(cap, addr, mapsize);
+}
+
+void *realloc_mapping(int cap, void *addr, size_t oldsize, size_t newsize)
+{
+  if (!addr) {
+    if (oldsize)  // no-no, realloc of the lowmem is not good too
+      dosemu_error("realloc_mapping() called with addr=NULL, oldsize=%#zx\n", oldsize);
+    Q_printf("MAPPING: realloc from NULL changed to malloc\n");
+    return alloc_mapping(cap, newsize);
+  }
+  if (!oldsize)
+    dosemu_error("realloc_mapping() addr=%p, oldsize=0\n", addr);
+  return mappingdriver->realloc(cap, addr, oldsize, newsize);
+}
+
+int munmap_mapping(int cap, dosaddr_t targ, size_t mapsize)
+{
+  int ku;
+  /* First of all remap the kmem mappings */
+  ku = kmem_unmap_mapping(targ, mapsize);
+  if (ku)
+    dosemu_error("Found %i kmem mappings at %#x\n", ku, targ);
+
+  if (cap & MAPPING_KMEM) {
+      /* Already done */
+      return 0;
+  }
+
+  return mappingdriver->munmap(cap, MEM_BASE32(targ), mapsize);
+}
+
+struct hardware_ram {
+  size_t base;
+  dosaddr_t default_vbase;
+  dosaddr_t vbase;
+  size_t size;
+  int type;
+  struct hardware_ram *next;
+};
+
+static struct hardware_ram *hardware_ram;
+
+static int do_map_hwram(struct hardware_ram *hw)
+{
+  unsigned char *p;
+
+  p = mmap_mapping_kmem(MAPPING_KMEM, hw->default_vbase, hw->size, hw->base);
+  if (p == MAP_FAILED) {
+    error("mmap error in map_hardware_ram %s\n", strerror (errno));
+    return -1;
+  }
+  hw->vbase = DOSADDR_REL(p);
+  g_printf("mapped hardware ram at 0x%08zx .. 0x%08zx at %#x\n",
+	     hw->base, hw->base+hw->size-1, hw->vbase);
+  return 0;
+}
+
+/*
+ * DANG_BEGIN_FUNCTION init_hardware_ram
+ *
+ * description:
+ *  Initialize the hardware direct-mapped pages
+ *
+ * DANG_END_FUNCTION
+ */
+void init_hardware_ram(void)
+{
+  struct hardware_ram *hw;
+
+  for (hw = hardware_ram; hw != NULL; hw = hw->next) {
+    if (hw->type == 'e')  /* virtual hardware ram mapped later */
+      continue;
+    alloc_mapping_kmem(hw->size, hw->base);
+    if (do_map_hwram(hw) == -1)
+      return;
+  }
+}
+
+int map_hardware_ram(char type)
+{
+  struct hardware_ram *hw;
+
+  for (hw = hardware_ram; hw != NULL; hw = hw->next) {
+    if (hw->type != type || hw->vbase != -1)
+      continue;
+    if (do_map_hwram(hw) == -1)
+      return -1;
+  }
+  return 0;
+}
+
+int map_hardware_ram_manual(size_t base, dosaddr_t vbase)
+{
+  struct hardware_ram *hw;
+
+  for (hw = hardware_ram; hw != NULL; hw = hw->next) {
+    if (hw->base != base)
+      continue;
+    hw->vbase = vbase;
+    return 0;
+  }
+  return -1;
+}
+
+int unmap_hardware_ram(char type)
+{
+  struct hardware_ram *hw;
+  int rc = 0;
+
+  for (hw = hardware_ram; hw != NULL; hw = hw->next) {
+    if (hw->type != type || hw->vbase == -1)
+      continue;
+    munmap_mapping_kmem(MAPPING_KMEM, hw->vbase, hw->size);
+    g_printf("unmapped hardware ram at 0x%08zx .. 0x%08zx at %#x\n",
+	    hw->base, hw->base+hw->size-1, hw->vbase);
+    hw->vbase = -1;
+  }
+  return rc;
+}
+
+int register_hardware_ram(int type, unsigned int base, unsigned int size)
+{
+  struct hardware_ram *hw;
+
+  if (!can_do_root_stuff && type != 'e') {
+    dosemu_error("can't use hardware ram in low feature (non-suid root) DOSEMU\n");
+    return 0;
+  }
+  c_printf("Registering HWRAM, type=%c base=%#x size=%#x\n", type, base, size);
+  hw = malloc(sizeof(*hw));
+  hw->base = base;
+  if (base < LOWMEM_SIZE)
+    hw->default_vbase = hw->base;
+  else
+    hw->default_vbase = -1;
+  hw->vbase = -1;
+  hw->size = size;
+  hw->type = type;
+  hw->next = hardware_ram;
+  hardware_ram = hw;
+  if (base >= LOWMEM_SIZE || type == 'h')
+    memcheck_reserve(type, base, size);
+  return 1;
+}
+
+/* given physical address addr, gives the corresponding vbase or -1 */
+unsigned get_hardware_ram(unsigned addr)
+{
+  struct hardware_ram *hw;
+
+  for (hw = hardware_ram; hw != NULL; hw = hw->next)
+    if (hw->vbase != -1 &&
+	hw->base <= addr && addr < hw->base + hw->size)
+      return hw->vbase + addr - hw->base;
+  return -1;
+}
+
+void list_hardware_ram(void (*print)(const char *, ...))
+{
+  struct hardware_ram *hw;
+
+  (*print)("hardware_ram: %s\n", hardware_ram ? "" : "no");
+  if (!hardware_ram) return;
+  (*print)("hardware_pages:\n");
+  for (hw = hardware_ram; hw != NULL; hw = hw->next)
+    (*print)("%08x-%08x\n", hw->base, hw->base + hw->size - 1);
+}
+
+/* why count ??? */
+/* Because you do not want to open it more than once! */
+static u_char kmem_open_count = 0;
+
+void
+open_kmem (void)
+{
+  PRIV_SAVE_AREA
+  /* as I understad it, /dev/kmem is the kernel's view of memory,
+     * and /dev/mem is the identity-mapped (i.e. physical addressed)
+     * memory. Currently under Linux, both are the same.
+     */
+
+  kmem_open_count++;
+
+  if (mem_fd != -1)
+    return;
+  enter_priv_on();
+  mem_fd = open("/dev/mem", O_RDWR);
+  leave_priv_setting();
+  if (mem_fd < 0)
+    {
+      error("can't open /dev/mem: errno=%d, %s \n",
+	     errno, strerror (errno));
+      leavedos (0);
+      return;
+    }
+  g_printf ("Kmem opened successfully\n");
+}
+
+void
+close_kmem (void)
+{
+
+  if (kmem_open_count)
+    {
+      kmem_open_count--;
+      if (kmem_open_count)
+	return;
+      close (mem_fd);
+      mem_fd = -1;
+      v_printf ("Kmem closed successfully\n");
+    }
+}
+
+void *mapping_find_hole(unsigned long start, unsigned long stop,
+	unsigned long size)
+{
+    FILE *fp;
+    unsigned long beg, end, pend;
+    int fd, ret;
+    /* find out whether the address request is available */
+    fd = dup(dosemu_proc_self_maps_fd);
+    if ((fp = fdopen(fd, "r")) == NULL) {
+	error("can't open /proc/self/maps\n");
+	return MAP_FAILED;
+    }
+    fseek(fp, 0, SEEK_SET);
+    pend = start;
+    while ((ret = fscanf(fp, "%lx-%lx%*[^\n]", &beg, &end)) == 2) {
+	if (beg <= start) {
+	    if (end > pend)
+		pend = end;
+	    continue;
+	}
+	if (beg - pend >= size)
+	    break;
+	if (end + size > stop) {
+	    fclose(fp);
+	    return MAP_FAILED;
+	}
+	pend = end;
+    }
+    fclose(fp);
+    if (ret != 2)
+	return MAP_FAILED;
+    return (void *)pend;
+}
