From f03ef4f2d47b9c2a14c62c3311fc24b0be01e90f Mon Sep 17 00:00:00 2001 From: Nico Huber Date: Sat, 4 Mar 2017 13:57:09 +0100 Subject: [PATCH] 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 Reviewed-on: https://review.coreboot.org/18779 Reviewed-by: Stefan Reinauer --- Makefile.inc | 2 +- ada/Makefile.inc | 2 + ada/posix/hw-file.adb | 68 ++++++++++++++++++++++++++++ c/Makefile.inc | 1 + c/hw-file.c | 103 ++++++++++++++++++++++++++++++++++++++++++ common/Makefile.inc | 2 + common/hw-file.ads | 36 +++++++++++++++ configs/defconfig | 1 + configs/posix | 7 +++ 9 files changed, 221 insertions(+), 1 deletion(-) create mode 100644 ada/posix/hw-file.adb create mode 100644 c/Makefile.inc create mode 100644 c/hw-file.c create mode 100644 common/hw-file.ads create mode 100644 configs/posix diff --git a/Makefile.inc b/Makefile.inc index d4fac0b971..d37ce513e9 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 2233094ec6..2dd0c5a3c7 100644 --- a/ada/Makefile.inc +++ b/ada/Makefile.inc @@ -5,3 +5,5 @@ hw-$(CONFIG_HWBASE_TIMER_CLOCK_GETTIME) += clock_gettime/hw-time-timer.adb 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 0000000000..ae0792b2d5 --- /dev/null +++ b/ada/posix/hw-file.adb @@ -0,0 +1,68 @@ +-- +-- Copyright (C) 2017 Nico Huber +-- +-- 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 0000000000..50b0c4a37d --- /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 0000000000..4d756eab44 --- /dev/null +++ b/c/hw-file.c @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2017 Nico Huber + * + * 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 +#include +#include +#include +#include + +#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 e74081daf9..a08a7df785 100644 --- a/common/Makefile.inc +++ b/common/Makefile.inc @@ -9,3 +9,5 @@ hw-y += hw-sub_regs.ads 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 0000000000..9f5e452fe6 --- /dev/null +++ b/common/hw-file.ads @@ -0,0 +1,36 @@ +-- +-- Copyright (C) 2017 Nico Huber +-- +-- 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 eb8cafb84e..c0db7c35b9 100644 --- a/configs/defconfig +++ b/configs/defconfig @@ -4,3 +4,4 @@ CONFIG_HWBASE_STATIC_MMIO = y 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 0000000000..183a5b5359 --- /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