[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[PATCH v4] Add io space MMAP for memory mapping devices and files
From: |
Andreas Klinger |
Subject: |
[PATCH v4] Add io space MMAP for memory mapping devices and files |
Date: |
Sun, 14 Jan 2024 19:49:14 +0100 |
2024-01-14 Andreas Klinger <ak@it-klinger.de>
* libpoke/ios-dev-mmap.c: New file.
* libpoke/Makefile.am (libpoke_la_SOURCES): Add ios-dev-mmap.c
* libpoke/ios.c (IOS_DEV_MMAP): Define.
(ios_dev_ifs): Initialize IOS_DEV_MMAP.
* poke/pk-cmd-ios.c (pk_cmd_mmap): New function.
* poke/pk-cmd.c (dot_cmds): Add mmap_cmd.
* poke/pk-help.pk: Add help text for .mmap.
* doc/poke.texi (mmap command): New section.
(file command): Add reference to .mmap.
* testsuite/Makefile.am (EXTRA_DIST): Add mmap-{1,2,3}.pk.
* testsuite/poke.cmd/mmap-1.pk: New file.
* testsuite/poke.cmd/mmap-2.pk: New file.
* libpoke/ios-dev.h: New define IOD_EMMAP.
* libpoke/libpoke.h: New define PK_IOD_EMMAP.
* libpoke/std.pk: New function openmmap.
* bootstrap.conf (libpoke_modules): Add getpagesize.
---
Thanks for the reviews, feedback and write access to the poke repo.
See also the branches in the repo:
anderl/ios-dev-mmap-v4:
- contains PATCH v4 as one single commit as submitted with this mail.
- commit f0ea96a3 (".gitignore: ignore cscope file") is for development and not
subject to be included
anderl/ios-dev-mmap-v3:
- contains PATCH v3 plus another delta commit with changes for v4.
- commit f0ea96a3 (".gitignore: ignore cscope file") is for development and not
subject to be included
Changes to v3 are:
- Add getpagessize to libpoke_modules
- Add more doc to poke.texi
- Conform to coding conventions
- Add file name completion for .mmap
bootstrap.conf | 1 +
configure.ac | 5 +
doc/poke.texi | 83 ++++++-
libpoke/Makefile.am | 4 +
libpoke/ios-dev-mmap.c | 459 +++++++++++++++++++++++++++++++++++
libpoke/ios-dev.h | 1 +
libpoke/ios.c | 9 +
libpoke/libpoke.h | 1 +
libpoke/std.pk | 7 +
po/POTFILES.in | 1 +
poke/pk-cmd-ios.c | 55 +++++
poke/pk-cmd.c | 6 +
poke/pk-help.pk | 22 ++
testsuite/Makefile.am | 2 +
testsuite/lib/poke-dg.exp | 5 +
testsuite/poke.cmd/mmap-1.pk | 8 +
testsuite/poke.cmd/mmap-2.pk | 8 +
17 files changed, 676 insertions(+), 1 deletion(-)
create mode 100644 libpoke/ios-dev-mmap.c
create mode 100644 testsuite/poke.cmd/mmap-1.pk
create mode 100644 testsuite/poke.cmd/mmap-2.pk
diff --git a/bootstrap.conf b/bootstrap.conf
index c5cf0452..fe4eebe6 100644
--- a/bootstrap.conf
+++ b/bootstrap.conf
@@ -81,6 +81,7 @@ libpoke_modules="
gcd
gettext-h
gettime
+ getpagesize
intprops
isatty
linkedhash-set
diff --git a/configure.ac b/configure.ac
index f8675239..ffcc4c46 100644
--- a/configure.ac
+++ b/configure.ac
@@ -196,6 +196,11 @@ else
fi
AM_CONDITIONAL([NBD], [test "x$libnbd_enabled" = "xyes"])
+dnl Check for mmap
+AC_FUNC_MMAP
+
+AM_CONDITIONAL([HAVE_MMAP], [test "x$ac_cv_func_mmap_fixed_mapped" = "xyes"])
+
dnl Used in Makefile.am. See the note there.
WITH_JITTER=$with_jitter
AC_SUBST([WITH_JITTER])
diff --git a/doc/poke.texi b/doc/poke.texi
index 1c75d91c..d6a7cb9b 100644
--- a/doc/poke.texi
+++ b/doc/poke.texi
@@ -209,6 +209,7 @@ Dot-Commands
* source command:: Executing commands in files.
* file command:: Opening and selecting file IO spaces.
* mem command:: Opening and selecting memory IO spaces.
+* mmap command:: Opening and selecting memory mapped devices.
* nbd command:: Opening and selecting NBD IO spaces.
* proc command:: Opening and selecting process IO spaces.
* ios command:: Switching between IO spaces.
@@ -8820,6 +8821,7 @@ au BufRead,BufNewFile *.pk set filetype=poke
* source command:: Executing commands in files.
* file command:: Opening and selecting file IO spaces.
* mem command:: Opening and selecting memory IO spaces.
+* mmap command:: Opening and selecting memory mapped devices.
* nbd command:: Opening and selecting NBD IO spaces.
* proc command:: Opening and selecting process IO spaces.
* sub command:: Opening IO sub-spaces.
@@ -8874,7 +8876,11 @@ where @var{path} is a path to the file containing the
commands.
@cindex opening files
@cindex IO space
The @command{.file} command opens a new IO space backed by a file, or
-switches to a previously opened file. The syntax is:
+switches to a previously opened file. When reading or writing the file is
+accessed via the read and write syscalls. An alternative using the mmap
syscall
+is implemented as @command{.mmap} command. @xref{file command}
+The syntax is:
+
@example
.file @var{path}
@@ -8932,6 +8938,81 @@ the buffer.
When a new memory buffer IOS is opened it becomes the current IO
space. @xref{file command}.
+@node mmap command
+@section @code{.mmap}
+@cindex @code{.mmap}
+@cindex opening memory mapped device
+@cindex IO space
+The @command{.mmap} command opens a new IO space where the content is memory
+mapped from the device driver or file system. This is especially useful when a
+kernel driver offers a mmap function for mapping the content of kernel memory
+into userspace. The most famous example is the mem device with its device node
+/dev/mem. But there are also many drivers especially in embedded systems using
+it.
+
+In the case of a regular file the mapping is done by the file system driver.
+For regular files the @command{.file} command would be an alternative and
should
+deliver the same result. The difference is that @command{.file} is using read
+and write system calls to retrieve or set the content of the file whereas
+@command{.mmap} is using the mmap system call which is copying the data from
the
+file into ram memory. This is done by the file system driver and on nowadays
+file systems @command{.file} and @command{.mmap} are offered by them. When
+having enough free ram and accessing the file very often the @command{.mmap} is
+supposed to be faster because there are less system calls to be executed.
+@xref{file command}
+
+In the case of a device driver the behavior depends on the implementation of
the
+driver. It particularly depends on the offered read, write and mmap functions.
+With /dev/mem for example @command{.file} and @command{.mmap} should deliver
the
+same results.
+But there are many drivers used in embedded systems especially for customer
+hardware which are offering either read and write or mmap. In such a case only
+the corresponding command can be used.
+For example when dealing with large amounts of data (video stream) which should
+be visible to the userspace for further processing it's almost impossible to
+copy every single picture to userspace with a driver read function. In such
+cases device drivers often only offer a mmap function which avoids the extra
+copying operation.
+
+The syntax is:
+
+@example
+.mmap @var{filename}, @var{base}, @var{size}
+@end example
+
+@noindent
+where @var{filename} is the name of the device node or the file name,
@var{base}
+is the starting offset (or base address) of the mapping and @var{size} is the
+length of the mapped area.
+
+The @var{base} has to be a multiple of the MMU page size on the system. It can
+be retrieved via the C function getpagesize(). On most systems it's 4096 Bytes.
+
+The following example is mapping the GPIO registers via /dev/mem on the
+Beaglebone black board and reading the output enable (GPIO_OE) and data out
+(GPIO_DATAOUT) registers of the IO memory:
+
+@example
+$ poke
+(poke) .mmap /dev/mem, 0x4804c000, 0x1000
+(poke) int @ 0x13C#B
+(poke) int @ 0x134#B
+@end example
+
+When using int on 32 bit systems the full address bus width is used when
reading
+and the same applies to 64 bit systems with the long datatype. All accesses
+with less bytes than the address bus width are done via a byte by byte copy.
+
+Please write to the poke-devel mailing list if you need a different alignment.
+
+Warning:
+One should be aware that when writing to mapped registers one is writing to the
+hardware. Therefore one should be aware of the meaning of the registers as
well
+as the electrical circuit connected. Otherwise the hardware can easily be
+destroyed. To give an example this could happen when setting a GPIO as output
+with a high signal while in fact it is connected as an input on the electrical
+circuit with low signal.
+
@node nbd command
@section @code{.nbd}
@cindex @code{.nbd}
diff --git a/libpoke/Makefile.am b/libpoke/Makefile.am
index 841ecab6..85e76cf1 100644
--- a/libpoke/Makefile.am
+++ b/libpoke/Makefile.am
@@ -66,6 +66,10 @@ libpoke_la_SOURCES = libpoke.h libpoke.c \
libpoke_la_SOURCES += ../common/pk-utils.c ../common/pk-utils.h
+if HAVE_MMAP
+libpoke_la_SOURCES += ios-dev-mmap.c
+endif HAVE_MMAP
+
if NBD
libpoke_la_SOURCES += ios-dev-nbd.c
endif NBD
diff --git a/libpoke/ios-dev-mmap.c b/libpoke/ios-dev-mmap.c
new file mode 100644
index 00000000..7650b795
--- /dev/null
+++ b/libpoke/ios-dev-mmap.c
@@ -0,0 +1,459 @@
+/* ios-dev-map.c - Memory mapped devices. */
+
+/* Copyright (C) 2024 Andreas Klinger */
+
+/* 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 3 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* This file implements an IO device that can be used in order to edit
+ the memory mapped from device drivers via dev_map syscall. */
+
+#include <config.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+/* We want 64-bit file offsets in all systems. */
+#define _FILE_OFFSET_BITS 64
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <errno.h>
+
+#include "ios.h"
+#include "ios-dev.h"
+
+#include "pkt.h"
+
+/* State associated with a file device. */
+struct ios_dev_mmap
+{
+ char *filename;
+ int fd;
+ int reg_file;
+ uint64_t flags;
+ int open_flags;
+ int prot;
+ uint64_t base;
+ uint64_t size;
+ void *addr;
+};
+
+static const char *
+ios_dev_mmap_get_if_name () {
+ return "MMAP";
+}
+
+static char *
+ios_dev_mmap_handler_normalize (const char *handler, uint64_t flags, int*
error)
+{
+ char *new_handler = NULL;
+
+ if (strlen (handler) > 6
+ && handler[0] == 'm'
+ && handler[1] == 'm'
+ && handler[2] == 'a'
+ && handler[3] == 'p'
+ && handler[4] == ':'
+ && handler[5] == '/'
+ && handler[6] == '/')
+ {
+ new_handler = strdup (handler);
+ if (new_handler == NULL && error)
+ *error = IOD_ENOMEM;
+ }
+
+ if (error)
+ *error = IOD_OK;
+ return new_handler;
+}
+
+/* Returns 0 when the flags are inconsistent. */
+static inline int
+ios_dev_mmap_convert_flags_open (int mode_flags)
+{
+ int flags_for_open = 0;
+
+ if ((mode_flags & IOS_F_READ)
+ && (mode_flags & IOS_F_WRITE))
+ {
+ flags_for_open |= O_RDWR;
+ }
+ else if (mode_flags & IOS_F_READ)
+ {
+ flags_for_open |= O_RDONLY;
+ }
+ else if (mode_flags & IOS_F_WRITE)
+ {
+ flags_for_open |= O_WRONLY;
+ }
+ else
+ /* Cannot open a file neither to write nor to read. */
+ return -1;
+
+ return flags_for_open;
+}
+
+/* Returns 0 when the flags are inconsistent. */
+static inline int
+ios_dev_mmap_convert_mmap_prot (int open_flags)
+{
+ int mmap_prot = 0;
+
+ if (open_flags & O_RDWR)
+ {
+ mmap_prot |= PROT_READ | PROT_WRITE;
+ }
+ else if (open_flags & O_RDONLY)
+ {
+ mmap_prot |= PROT_READ;
+ }
+ else if (open_flags & O_WRONLY)
+ {
+ mmap_prot |= PROT_WRITE;
+ }
+ else
+ /* Cannot dev_map neither to write nor to read. */
+ return -1;
+
+ return mmap_prot;
+}
+
+static void *
+ios_dev_mmap_open (const char *handler, uint64_t flags, int *error,
+ void *data __attribute__ ((unused)))
+{
+ struct ios_dev_mmap *dev_map = NULL;
+ int internal_error = IOD_ERROR;
+ uint8_t mode_flags = flags & IOS_FLAGS_MODE;
+ int open_flags = 0;
+ int fd;
+ const char *p;
+ char *end;
+ struct stat st;
+ int ret;
+
+ dev_map = malloc (sizeof (struct ios_dev_mmap));
+ if (!dev_map)
+ goto err;
+
+ memset (dev_map, 0, sizeof (struct ios_dev_mmap));
+
+ /* Format of handler:
+ mmap://BASE/SIZE/FILE-NAME */
+
+ /* Skip the mmap:// */
+ p = handler + 7;
+
+ /* parse the base address of memory mapped area. This is an uint64. */
+ dev_map->base = strtoull (p, &end, 0);
+ if (*p != '\0' && *end == '/')
+ /* Valid integer found. */;
+ else
+ goto err;
+ p = end + 1;
+
+ /* parse the size of the memory mapped area. This is an uint64. */
+ dev_map->size = strtoull (p, &end, 0);
+ if (*p != '\0' && *end == '/')
+ /* Valid integer found. */;
+ else
+ goto err;
+ p = end + 1;
+
+ /* The rest of the string is the name, which may be empty. */
+ dev_map->filename = strdup (p);
+ if (!p)
+ goto err;
+
+ /* Ok now do some validation. */
+ /* base needs to be a multiple of PAGE_SIZE */
+ if (dev_map->base % getpagesize ())
+ {
+ internal_error = IOD_EFLAGS;
+ goto err;
+ }
+
+ if (mode_flags)
+ {
+ /* Decide what mode to use to open the file. */
+ open_flags = ios_dev_mmap_convert_flags_open (mode_flags);
+ if (open_flags == -1)
+ {
+ internal_error = IOD_EFLAGS;
+ goto err;
+ }
+ fd = open (dev_map->filename, open_flags);
+ if (fd == -1)
+ {
+ pk_printf ("Error in open of %s err: %s\n",
+ dev_map->filename, strerror (errno));
+ goto err;
+ }
+ flags = mode_flags;
+ }
+ else
+ {
+ /* Try read-write initially.
+ If that fails, then try read-only.
+ If that fails, then try write-only. */
+ open_flags = O_RDWR;
+ fd = open (dev_map->filename, open_flags);
+ flags |= (IOS_F_READ | IOS_F_WRITE);
+ if (fd == -1)
+ {
+ open_flags = O_RDONLY;
+ fd = open (dev_map->filename, open_flags);
+ if (fd != -1)
+ flags &= ~IOS_F_WRITE;
+ }
+ if (fd == -1)
+ {
+ open_flags = O_WRONLY;
+ fd = open (dev_map->filename, open_flags);
+ if (fd != -1)
+ flags &= ~IOS_F_READ;
+ }
+ if (fd == -1)
+ {
+ pk_printf ("Error in open of %s err: %s\n",
+ dev_map->filename, strerror (errno));
+ goto err;
+ }
+ }
+
+ /* limit the size of the mapping for regular files for avoiding
+ SIGBUS when accessing memory outside of the file */
+ ret = fstat (fd, &st);
+ if (ret == -1)
+ {
+ pk_printf ("Error in fstat of %s err: %s\n",
+ dev_map->filename, strerror (errno));
+ goto err;
+ }
+ if ((st.st_mode & S_IFMT) == S_IFREG)
+ dev_map->reg_file = 1;
+ else
+ dev_map->reg_file = 0;
+
+ if (dev_map->reg_file && (st.st_size < dev_map->size))
+ dev_map->size = st.st_size;
+
+ dev_map->fd = fd;
+ dev_map->flags = flags;
+ dev_map->open_flags = open_flags;
+ dev_map->prot = ios_dev_mmap_convert_mmap_prot (open_flags);
+
+ dev_map->addr = mmap (0, dev_map->size, dev_map->prot, MAP_SHARED,
+ fd, dev_map->base);
+ if (dev_map->addr == MAP_FAILED)
+ {
+ pk_printf ("Error in mmap of %s base: 0x%x len: 0x%x prot: 0x%x err:
%s\n",
+ dev_map->filename, dev_map->base, dev_map->size,
+ dev_map->prot, strerror (errno));
+ internal_error = IOD_EMMAP;
+ goto err;
+ }
+
+ /* should never be the case that returned address is not page aligned as mmap
+ fails if dev_map->base is not aligned.
+ But we double check because pread and pwrite rely on this alignment */
+ if ((unsigned long)dev_map->addr & ((unsigned long)getpagesize () - 1))
+ {
+ pk_printf ("Alignment issue treated as error in mmap of %s addr: 0x%x\n",
+ dev_map->filename, dev_map->addr);
+ internal_error = IOD_EMMAP;
+ goto err;
+ }
+
+ if (error)
+ *error = IOD_OK;
+
+ return dev_map;
+
+err:
+ if (dev_map)
+ free (dev_map->filename);
+ free (dev_map);
+
+ if (error)
+ *error = internal_error;
+ return NULL;
+}
+
+static int
+ios_dev_mmap_close (void *iod)
+{
+ struct ios_dev_mmap *dev_map = iod;
+
+ munmap (dev_map->addr, dev_map->size);
+ close (dev_map->fd);
+
+ free (dev_map->filename);
+ free (dev_map);
+ return IOD_OK;
+}
+
+static uint64_t
+ios_dev_mmap_get_flags (void *iod)
+{
+ struct ios_dev_mmap *dev_map = iod;
+
+ return dev_map->flags;
+}
+
+static int
+ios_dev_mmap_pread (void *iod, void *buf, size_t count, ios_dev_off offset)
+{
+ struct ios_dev_mmap *dev_map = iod;
+ int align = sizeof (void*);
+ uint8_t *m = buf;
+
+ if (offset > dev_map->size || count > dev_map->size - offset)
+ return IOD_EOF;
+
+ /* copy unaligned bytes
+ dev_mmap->addr is always page aligned because of mmap */
+ while (count && offset % align)
+ {
+ *m = *(const volatile uint8_t*)(dev_map->addr + offset);
+ count--;
+ offset++;
+ m++;
+ }
+
+ /* copy with the address bus size */
+ while (count >= align)
+ {
+ if (align == 4)
+ {
+ *(uint32_t*)m = *(const volatile uint32_t*)(dev_map->addr + offset);
+ }
+ else if (align == 8)
+ {
+ *(uint64_t*)m = *(const volatile uint64_t*)(dev_map->addr + offset);
+ }
+ else
+ break;
+ count -= align;
+ offset += align;
+ m += align;
+ }
+
+ /* copy remaining unaligned bytes */
+ while (count)
+ {
+ *m = *(const volatile uint8_t*)(dev_map->addr + offset);
+ count--;
+ offset++;
+ m++;
+ }
+
+ return IOD_OK;
+}
+
+static int
+ios_dev_mmap_pwrite (void *iod, const void *buf, size_t count,
+ ios_dev_off offset)
+{
+ struct ios_dev_mmap *dev_map = iod;
+ int align = sizeof (void*);
+ const uint8_t *m = buf;
+
+ if (offset > dev_map->size || count > dev_map->size - offset)
+ return IOD_EOF;
+
+ /* copy unaligned bytes
+ dev_mmap->addr is always page aligned because of mmap */
+ while (count && offset % align)
+ {
+ *(volatile uint8_t*)(dev_map->addr + offset) = *(const uint8_t*)m;
+ count--;
+ offset++;
+ m++;
+ }
+
+ /* copy with the address bus size */
+ while (count >= align)
+ {
+ if (align == 4)
+ {
+ *(volatile uint32_t*)(dev_map->addr + offset) = *(const uint32_t*)m;
+ }
+ else if (align == 8)
+ {
+ *(volatile uint64_t*)(dev_map->addr + offset) = *(const uint64_t*)m;
+ }
+ else
+ break;
+ count -= align;
+ offset += align;
+ m += align;
+ }
+
+ /* copy remaining unaligned bytes */
+ while (count)
+ {
+ *(volatile uint8_t*)(dev_map->addr + offset) = *(const uint8_t*)m;
+ count--;
+ offset++;
+ m++;
+ }
+
+ return IOD_OK;
+}
+
+static ios_dev_off
+ios_dev_mmap_size (void *iod)
+{
+ struct ios_dev_mmap *dev_map = iod;
+
+ return dev_map->size;
+}
+
+static int
+ios_dev_mmap_flush (void *iod, ios_dev_off offset)
+{
+ struct ios_dev_mmap *dev_map = iod;
+ int ret;
+
+ if (dev_map->reg_file)
+ {
+ ret = msync (dev_map->addr, dev_map->size, MS_SYNC);
+ if (ret == -1)
+ {
+ pk_printf ("Error in msync of %s base: 0x%lx len: 0x%lx err: %s\n",
+ dev_map->filename, dev_map->addr, dev_map->size,
+ strerror (errno));
+ return IOD_ERROR;
+ }
+ }
+
+ return IOD_OK;
+}
+
+struct ios_dev_if ios_dev_mmap =
+ {
+ .get_if_name = ios_dev_mmap_get_if_name,
+ .handler_normalize = ios_dev_mmap_handler_normalize,
+ .open = ios_dev_mmap_open,
+ .close = ios_dev_mmap_close,
+ .pread = ios_dev_mmap_pread,
+ .pwrite = ios_dev_mmap_pwrite,
+ .get_flags = ios_dev_mmap_get_flags,
+ .size = ios_dev_mmap_size,
+ .flush = ios_dev_mmap_flush
+ };
diff --git a/libpoke/ios-dev.h b/libpoke/ios-dev.h
index 95a30e9f..14a6b515 100644
--- a/libpoke/ios-dev.h
+++ b/libpoke/ios-dev.h
@@ -45,6 +45,7 @@ typedef uint64_t ios_dev_off;
#define IOD_ENOMEM -4 /* Memory allocation failure. */
#define IOD_EOF -5 /* End of file / input. */
#define IOD_EINVAL -6 /* Invalid argument. */
+#define IOD_EMMAP -7 /* Memory mapping error. */
/* Each IO backend should implement a device interface, by filling an
instance of the struct defined below.
diff --git a/libpoke/ios.c b/libpoke/ios.c
index a88ae509..335c1e6d 100644
--- a/libpoke/ios.c
+++ b/libpoke/ios.c
@@ -103,6 +103,9 @@ extern struct ios_dev_if ios_dev_nbd; /* ios-dev-nbd.c */
extern struct ios_dev_if ios_dev_proc; /* ios-dev-proc.c */
#endif
extern struct ios_dev_if ios_dev_sub; /* ios-dev-sub.c */
+#ifdef HAVE_MMAP
+extern struct ios_dev_if ios_dev_mmap; /* ios-dev-mmap.c */
+#endif
enum
{
@@ -112,6 +115,7 @@ enum
IOS_DEV_NBD,
IOS_DEV_PROC,
IOS_DEV_SUB,
+ IOS_DEV_MMAP,
IOS_DEV_FILE, /* File must be last */
};
@@ -131,6 +135,11 @@ static const struct ios_dev_if *ios_dev_ifs[] =
[IOS_DEV_PROC] = NULL,
#endif
[IOS_DEV_SUB] = &ios_dev_sub,
+#ifdef HAVE_MMAP
+ [IOS_DEV_MMAP] = &ios_dev_mmap,
+#else
+ [IOS_DEV_MMAP] = NULL,
+#endif
[IOS_DEV_FILE] = &ios_dev_file, /* File must be last */
};
diff --git a/libpoke/libpoke.h b/libpoke/libpoke.h
index 7c592f9d..baee3b91 100644
--- a/libpoke/libpoke.h
+++ b/libpoke/libpoke.h
@@ -1310,6 +1310,7 @@ typedef uint64_t pk_iod_off;
#define PK_IOD_ENOMEM -4
#define PK_IOD_EOF -5
#define PK_IOD_EINVAL -6
+#define PK_IOD_EMMAP -7
struct pk_iod_if
{
diff --git a/libpoke/std.pk b/libpoke/std.pk
index 48b2fa4c..87a51bb7 100644
--- a/libpoke/std.pk
+++ b/libpoke/std.pk
@@ -435,6 +435,13 @@ fun openproc = (uint<64> pid, uint<64> flags = 0) int<32>:
return open (format ("pid://%u64d", pid), flags);
}
+fun openmmap = (string path,
+ offset<uint<64>,B> base,
+ offset<uint<64>,B> size) int<32>:
+{
+ return open (format ("mmap://%u64d/%u64d/%s", base/#B, size/#B, path));
+}
+
/*** Miscellanea. */
var NULL = 0#B;
diff --git a/po/POTFILES.in b/po/POTFILES.in
index dcac9788..785d1597 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -4,6 +4,7 @@ libpoke/ios.c
libpoke/ios-dev-file.c
libpoke/ios-dev.h
libpoke/ios-dev-mem.c
+libpoke/ios-dev-mmap.c
libpoke/ios-dev-nbd.c
libpoke/ios.h
libpoke/pkl-anal.c
diff --git a/poke/pk-cmd-ios.c b/poke/pk-cmd-ios.c
index 7e47ed09..034cc09c 100644
--- a/poke/pk-cmd-ios.c
+++ b/poke/pk-cmd-ios.c
@@ -576,6 +576,55 @@ pk_cmd_nbd (int argc, struct pk_cmd_arg argv[], uint64_t
uflags)
}
#endif /* HAVE_LIBNBD */
+#ifdef HAVE_MMAP
+static int
+pk_cmd_mmap (int argc, struct pk_cmd_arg argv[], uint64_t uflags)
+{
+ /* mmap FILENAME BASE SIZE */
+ uint64_t base, size;
+ char *handler;
+ int ios_id;
+
+ assert (argc == 4);
+ assert (PK_CMD_ARG_TYPE (argv[1]) == PK_CMD_ARG_STR);
+
+ /* Create a new IO space. */
+ const char *filename = PK_CMD_ARG_STR (argv[1]);
+
+ assert (PK_CMD_ARG_TYPE (argv[2]) == PK_CMD_ARG_INT);
+ base = PK_CMD_ARG_INT (argv[2]);
+
+ assert (PK_CMD_ARG_TYPE (argv[3]) == PK_CMD_ARG_INT);
+ size = PK_CMD_ARG_INT (argv[3]);
+
+ if (access (filename, F_OK) == 0)
+
+ if (pk_ios_search (poke_compiler, filename, PK_IOS_SEARCH_F_EXACT) != NULL)
+ {
+ printf (_("File %s already opened. Use `.ios IOS' to switch.\n"),
+ filename);
+ return 0;
+ }
+
+ /* Build the handler for the mmap IOS. */
+ if (asprintf (&handler, "mmap://0x%" PRIx64 "/0x%" PRIx64 "/%s",
+ base, size, filename) == -1)
+ return 0;
+
+ /* Open the IOS. */
+ ios_id = pk_ios_open (poke_compiler, handler, 0, 1);
+ if (ios_id == PK_IOS_NOID)
+ {
+ pk_printf (_("Error creating mmap IOS %s\n"), handler);
+ free (handler);
+ return 0;
+ }
+ free (handler);
+
+ return 1;
+}
+#endif /* HAVE_MMAP */
+
const struct pk_cmd ios_cmd =
{"ios", "?s", "", 0, NULL, NULL, pk_cmd_ios, ".ios IOS",
poke_completion_function};
@@ -598,6 +647,12 @@ const struct pk_cmd nbd_cmd =
{"nbd", "s", "", 0, NULL, NULL, pk_cmd_nbd, ".nbd URI", NULL};
#endif
+#ifdef HAVE_MMAP
+const struct pk_cmd mmap_cmd =
+ {"mmap", "s,i,i", "", 0, NULL, NULL, pk_cmd_mmap, ".mmap FILE-NAME, BASE,
SIZE",
+ rl_filename_completion_function};
+#endif
+
const struct pk_cmd close_cmd =
{"close", "s", "", PK_CMD_F_REQ_IO, NULL, NULL, pk_cmd_close,
".close [IOS]", poke_completion_function};
diff --git a/poke/pk-cmd.c b/poke/pk-cmd.c
index 40761ead..76dd4cf3 100644
--- a/poke/pk-cmd.c
+++ b/poke/pk-cmd.c
@@ -48,6 +48,9 @@ extern const struct pk_cmd mem_cmd; /* pk-cmd-ios.c */
#ifdef HAVE_LIBNBD
extern const struct pk_cmd nbd_cmd; /* pk-cmd-ios.c */
#endif
+#ifdef HAVE_MMAP
+extern const struct pk_cmd mmap_cmd; /* pk-cmd-mmap.c */
+#endif
extern const struct pk_cmd close_cmd; /* pk-cmd-file.c */
extern const struct pk_cmd load_cmd; /* pk-cmd-file.c */
extern const struct pk_cmd source_cmd; /* pk-cmd-ios.c */
@@ -90,6 +93,9 @@ static const struct pk_cmd *dot_cmds[] =
&mem_cmd,
#ifdef HAVE_LIBNBD
&nbd_cmd,
+#endif
+#ifdef HAVE_MMAP
+ &mmap_cmd,
#endif
&null_cmd
};
diff --git a/poke/pk-help.pk b/poke/pk-help.pk
index e8c489bb..e86e9fef 100644
--- a/poke/pk-help.pk
+++ b/poke/pk-help.pk
@@ -245,6 +245,28 @@ Create a IO space providing access to a Network Block
Device
identified by the given URI."
};
+pk_help_add_topic
+ :entry Poke_HelpEntry {
+ category = "dot-commands",
+ topic = ".mmap",
+ synopsis = ".mmap FILENAME, BASE, SIZE",
+ summary = "open a device driver or file with memory mapping",
+ description = "\
+Create a IO space providing access to a device driver with mmap function
+or also a file where the mmap is provided by the file system driver.
+
+.mmap /dev/mem, BASE, SIZE
+Mapps physical memory area at BASE address which have to be
+a multiple of the page size.
+
+.mmap /dev/mem, 0x4804c000, 0x1000
+int @ 0x13C#B
+int @ 0x134#B
+Example on a Beaglebone black for watching GPIO registers and possibly further
+changing them.
+"
+ };
+
pk_help_add_topic
:entry Poke_HelpEntry {
category = "dot-commands",
diff --git a/testsuite/Makefile.am b/testsuite/Makefile.am
index af858984..dc663ab4 100644
--- a/testsuite/Makefile.am
+++ b/testsuite/Makefile.am
@@ -95,6 +95,8 @@ EXTRA_DIST = \
poke.cmd/maps-8.pk \
poke.cmd/maps-9.pk \
poke.cmd/maps-alien-1.pk \
+ poke.cmd/mmap-1.pk \
+ poke.cmd/mmap-2.pk \
poke.cmd/nbd-1.pk \
poke.cmd/save-1.pk \
poke.cmd/save-2.pk \
diff --git a/testsuite/lib/poke-dg.exp b/testsuite/lib/poke-dg.exp
index 5c4bc0b2..b8ece63b 100644
--- a/testsuite/lib/poke-dg.exp
+++ b/testsuite/lib/poke-dg.exp
@@ -71,6 +71,11 @@ proc dg-require { args } {
# Mark the test as unsupported
set do-what [list [lindex do-what 0] N P]
}
+ if {[lindex $args 1] == "memzero" \
+ && ! [file exists "/dev/zero"]} {
+ # Mark the test as unsupported
+ set do-what [list [lindex do-what 0] N P]
+ }
if {[lindex $args 1] == "nbd" \
&& $::env(NBDKIT) == "no"} {
# Mark the test as unsupported
diff --git a/testsuite/poke.cmd/mmap-1.pk b/testsuite/poke.cmd/mmap-1.pk
new file mode 100644
index 00000000..b61f60d3
--- /dev/null
+++ b/testsuite/poke.cmd/mmap-1.pk
@@ -0,0 +1,8 @@
+/* { dg-do run } */
+/* { dg-require memzero } */
+
+/* Check that it's possible to memory map /dev/zero. */
+
+/* { dg-command { .mmap /dev/zero,0x0,0x100} } */
+/* { dg-command { dump :from 0#B :size 4#B :ruler 0 :ascii 0} } */
+/* { dg-output "00000000: 0000 0000" } */
diff --git a/testsuite/poke.cmd/mmap-2.pk b/testsuite/poke.cmd/mmap-2.pk
new file mode 100644
index 00000000..3d27918d
--- /dev/null
+++ b/testsuite/poke.cmd/mmap-2.pk
@@ -0,0 +1,8 @@
+/* { dg-do run } */
+/* { dg-data {c*} {0x10 0x20 0x30 0x40 0x50 0x60 0x70 0x80} foo.data } */
+
+/* Check that it's possible to memory map a regular file. */
+
+/* { dg-command { .mmap foo.data,0x0,0x100} } */
+/* { dg-command { dump :from 0#B :size 4#B :ruler 0 :ascii 0} } */
+/* { dg-output "00000000: 1020 3040" } */
--
2.39.2
signature.asc
Description: PGP signature
- [PATCH v4] Add io space MMAP for memory mapping devices and files,
Andreas Klinger <=