Add support to map the contents of a file

Add a package HW.File with a single procedure Map():

   procedure Map
     (Path     : in     String;
      Addr     : in     Word64;
      Len      : in     Natural;
      Readable : in     Boolean := False;
      Writable : in     Boolean := False;
      Map_Copy : in     Boolean := False;
      Success  :    out Boolean)
   with
      Pre => (Readable or Writable) and
             (if Map_Copy then Readable and not Writable);

If `Map_Copy` is `False`, it should map `Len` bytes from the start of
the file given by `Path` into the application's address space at `Addr`
using mmap(). If `Map_Copy` is `True`, anonymous memory should be map-
ped instead and be filled with a copy of the file's content using
read().

The current implementation is backed by C code to reduce dependencies
to external libraries (e.g. Florist is not packaged by a famous Linux
distro).

While we are at it, also add a configuration file for common POSIX
environments (configs/posix).

Change-Id: Ic10c35b35d3216dab6a5b2baba7ba619c885b346
Signed-off-by: Nico Huber <nico.h@gmx.de>
Reviewed-on: https://review.coreboot.org/18779
Reviewed-by: Stefan Reinauer <stefan.reinauer@coreboot.org>
diff --git a/Makefile.inc b/Makefile.inc
index d4fac0b..d37ce51 100644
--- a/Makefile.inc
+++ b/Makefile.inc
@@ -1,2 +1,2 @@
-subdirs-y += ada common proof
+subdirs-y += ada common proof c
 subdirs-y += debug
diff --git a/ada/Makefile.inc b/ada/Makefile.inc
index 2233094..2dd0c5a 100644
--- a/ada/Makefile.inc
+++ b/ada/Makefile.inc
@@ -5,3 +5,5 @@
 hw-$(CONFIG_HWBASE_TIMER_MUTIME) += $(muen-common-path)/musinfo/musinfo.ads
 hw-$(CONFIG_HWBASE_TIMER_MUTIME) += $(muen-common-path)/muschedinfo/muschedinfo.ads
 hw-$(CONFIG_HWBASE_TIMER_MUTIME) += mutime/hw-time-timer.adb
+
+hw-$(CONFIG_HWBASE_POSIX_FILE) += posix/hw-file.adb
diff --git a/ada/posix/hw-file.adb b/ada/posix/hw-file.adb
new file mode 100644
index 0000000..ae0792b
--- /dev/null
+++ b/ada/posix/hw-file.adb
@@ -0,0 +1,68 @@
+--
+-- Copyright (C) 2017 Nico Huber <nico.h@gmx.de>
+--
+-- This program 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 2 of the License, or
+-- (at your option) any later version.
+--
+-- This program 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.
+--
+
+with Interfaces.C;
+with Interfaces.C.Strings;
+
+with HW.Debug;
+
+use Interfaces.C;
+use Interfaces.C.Strings;
+
+package body HW.File is
+
+   READ  : constant := 16#01#;
+   WRITE : constant := 16#02#;
+
+   function c_map
+     (path  : chars_ptr;
+      addr  : Word64;
+      len   : Word32;
+      mode  : Word32;
+      copy  : int)
+      return int;
+   pragma Import (C, c_map, "hw_file_map");
+
+   procedure Map
+     (Path     : in     String;
+      Addr     : in     Word64;
+      Len      : in     Natural;
+      Readable : in     Boolean := False;
+      Writable : in     Boolean := False;
+      Map_Copy : in     Boolean := False;
+      Success  :    out Boolean)
+   is
+      use type HW.Word32;
+
+      cpath : chars_ptr := New_String (Path);
+      ret : constant int := c_map
+        (path  => cpath,
+         addr  => Addr,
+         len   => Word32 (Len),
+         mode  => (if Readable then READ else 0) or
+                  (if Writable then WRITE else 0),
+         copy  => (if Map_Copy then 1 else 0));
+   begin
+      pragma Warnings(GNAT, Off, """cpath"" modified*, but* never referenced",
+                      Reason => "Free() demands to set it to null_ptr");
+      Free (cpath);
+      pragma Warnings(GNAT, On, """cpath"" modified*, but* never referenced");
+      Success := ret = 0;
+
+      pragma Debug (not Success, Debug.Put ("Mapping failed: "));
+      pragma Debug (not Success, Debug.Put_Int32 (Int32 (ret)));
+      pragma Debug (not Success, Debug.New_Line);
+   end Map;
+
+end HW.File;
diff --git a/c/Makefile.inc b/c/Makefile.inc
new file mode 100644
index 0000000..50b0c4a
--- /dev/null
+++ b/c/Makefile.inc
@@ -0,0 +1 @@
+hw-$(CONFIG_HWBASE_POSIX_FILE) += hw-file.c
diff --git a/c/hw-file.c b/c/hw-file.c
new file mode 100644
index 0000000..4d756ea
--- /dev/null
+++ b/c/hw-file.c
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2017 Nico Huber <nico.h@gmx.de>
+ *
+ * This program 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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.
+ */
+
+#include <stdint.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#define HW_FILE_READ	0x01
+#define HW_FILE_WRITE	0x02
+
+static int map_file(const char *const path,
+		    const uint64_t addr,
+		    const uint32_t len,
+		    const uint32_t mode)
+{
+	const int prot = (mode & HW_FILE_READ ? PROT_READ : 0) |
+			 (mode & HW_FILE_WRITE ? PROT_WRITE : 0);
+
+	if (mode & ~(HW_FILE_READ | HW_FILE_WRITE) ||
+	    !(mode & (HW_FILE_READ | HW_FILE_WRITE)))
+		return EINVAL;
+
+	const int fd = open(path, mode & HW_FILE_WRITE ? O_RDWR : O_RDONLY);
+	if (fd < 0)
+		return errno;
+
+	void *const mapped = mmap((void *)(uintptr_t)addr, len, prot,
+				  MAP_SHARED | MAP_FIXED, fd, (off_t)0);
+	close(fd);
+	if (mapped == MAP_FAILED)
+		return errno;
+
+	return 0;
+}
+
+static int map_fill_from_file(const char *const path,
+			      const uint64_t addr,
+			      const uint32_t len,
+			      const uint32_t mode)
+{
+	uint32_t xferred;
+	ssize_t read_ret;
+
+	if (mode != HW_FILE_READ)
+		return EINVAL;
+
+	void *const mapped = mmap((void *)(uintptr_t)addr, len,
+				  PROT_READ | PROT_WRITE,
+				  MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED,
+				  -1, (off_t)0);
+	if (mapped == MAP_FAILED)
+		return errno;
+
+	const int fd = open(path, O_RDONLY);
+	if (fd < 0)
+		goto _munmap;
+
+	xferred = 0;
+	do {
+		read_ret = read(fd, (uint8_t *)mapped + xferred, len - xferred);
+		if (read_ret > 0)
+			xferred += read_ret;
+	} while ((read_ret > 0 || (read_ret == -1 && errno == EINTR))
+		 && xferred < len);
+	close(fd);
+
+	if (xferred < len)
+		goto _munmap;
+
+	if (mprotect(mapped, len, PROT_READ))
+		goto _munmap;
+
+	return 0;
+
+_munmap:
+	munmap(mapped, len);
+	return errno;
+}
+
+int hw_file_map(const char *const path,
+		const uint64_t addr,
+		const uint32_t len,
+		const uint32_t mode,
+		const int copy)
+{
+	if (copy)
+		return map_fill_from_file(path, addr, len, mode);
+	else
+		return map_file(path, addr, len, mode);
+}
diff --git a/common/Makefile.inc b/common/Makefile.inc
index e74081d..a08a7df 100644
--- a/common/Makefile.inc
+++ b/common/Makefile.inc
@@ -9,3 +9,5 @@
 hw-y += hw-time.ads
 hw-y += hw-time.adb
 hw-y += hw-time-timer.ads
+
+hw-$(CONFIG_HWBASE_POSIX_FILE) += hw-file.ads
diff --git a/common/hw-file.ads b/common/hw-file.ads
new file mode 100644
index 0000000..9f5e452
--- /dev/null
+++ b/common/hw-file.ads
@@ -0,0 +1,36 @@
+--
+-- Copyright (C) 2017 Nico Huber <nico.h@gmx.de>
+--
+-- This program 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 2 of the License, or
+-- (at your option) any later version.
+--
+-- This program 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.
+--
+
+package HW.File is
+
+   -- Map a file's content into our address space
+   --
+   -- If `Map_Copy` is `False`, `Len` bytes from the start of the file
+   -- given by `Path` shall be mapped into the application's address
+   -- space at `Addr` using mmap(). If `Map_Copy` is `True`, anonymous
+   -- memory should be mapped instead and be filled with a copy of the
+   -- file's content using read().
+   procedure Map
+     (Path     : in     String;
+      Addr     : in     Word64;
+      Len      : in     Natural;
+      Readable : in     Boolean := False;
+      Writable : in     Boolean := False;
+      Map_Copy : in     Boolean := False;
+      Success  :    out Boolean)
+   with
+      Pre => (Readable or Writable) and
+             (if Map_Copy then Readable and not Writable);
+
+end HW.File;
diff --git a/configs/defconfig b/configs/defconfig
index eb8cafb..c0db7c3 100644
--- a/configs/defconfig
+++ b/configs/defconfig
@@ -4,3 +4,4 @@
 CONFIG_HWBASE_DYNAMIC_MMIO		=
 CONFIG_HWBASE_TIMER_CLOCK_GETTIME	= y
 CONFIG_HWBASE_TIMER_MUTIME		=
+CONFIG_HWBASE_POSIX_FILE		=
diff --git a/configs/posix b/configs/posix
new file mode 100644
index 0000000..183a5b5
--- /dev/null
+++ b/configs/posix
@@ -0,0 +1,7 @@
+CONFIG_HWBASE_DEBUG_NULL		=
+CONFIG_HWBASE_DEBUG_TEXT_IO		= y
+CONFIG_HWBASE_STATIC_MMIO		=
+CONFIG_HWBASE_DYNAMIC_MMIO		= y
+CONFIG_HWBASE_TIMER_CLOCK_GETTIME	= y
+CONFIG_HWBASE_TIMER_MUTIME		=
+CONFIG_HWBASE_POSIX_FILE		= y