summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorUwe Kleine-König <u.kleine-koenig@pengutronix.de>2018-03-14 17:46:49 +0100
committerUwe Kleine-König <u.kleine-koenig@pengutronix.de>2018-03-15 21:23:33 +0100
commit797c1a15eeb35c46284f651c3d82bcee2d750bec (patch)
treeee7f1f93b2d3ef2326ef8d9935db4763f724251b
parent312bcb715f61d2c3b05b1559eca2cba5b9a630c7 (diff)
downloadmemtool-797c1a15eeb35c46284f651c3d82bcee2d750bec.tar.gz
memtool-797c1a15eeb35c46284f651c3d82bcee2d750bec.tar.xz
Introduce abstraction layer for file access
This prepares to support further backends to access other memory-like devices. There are only little changes in the intended behaviour for some corner cases: - md -w x+y now rounds y down to a multiple of 2 instead of up. Same for -l with multiples of 4 and -q with multiples of 8. - The size of a memory map is limited to 4096, which for big requests increases the count of map + unmap operations. This is expected to not make a difference in the effects though. Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
-rw-r--r--.gitignore2
-rw-r--r--Makefile.am3
-rw-r--r--acc_mmap.c213
-rw-r--r--fileaccess.c43
-rw-r--r--fileaccess.h21
-rw-r--r--fileaccpriv.h23
-rw-r--r--memtool.c200
7 files changed, 402 insertions, 103 deletions
diff --git a/.gitignore b/.gitignore
index e8b677b..df5d3c1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
+/acc_mmap.o
/aclocal.m4
/autom4te.cache
/compile
@@ -6,6 +7,7 @@
/configure
/depcomp
/.deps
+/fileaccess.o
/install-sh
/Makefile
/Makefile.in
diff --git a/Makefile.am b/Makefile.am
index 3786362..56eef9e 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -2,7 +2,8 @@ EXTRA_DIST = README.devel
bin_PROGRAMS = memtool
-memtool_SOURCES = memtool.c
+noinst_HEADERS = fileaccess.h fileaccpriv.h
+memtool_SOURCES = memtool.c fileaccess.c acc_mmap.c
dist_man_MANS = memtool.1
diff --git a/acc_mmap.c b/acc_mmap.c
new file mode 100644
index 0000000..2b91cdc
--- /dev/null
+++ b/acc_mmap.c
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2018 Pengutronix, Uwe Kleine-König <oss-tools@pengutronix.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, as published by the Free Software Foundation.
+ *
+ * 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 <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "fileaccpriv.h"
+
+#define container_of(ptr, type, member) \
+ (type *)((char *)(ptr) - (char *) &((type *)0)->member)
+
+static off_t mmap_pagesize(void) __attribute__((const));
+static off_t mmap_pagesize(void)
+{
+ static off_t pagesize;
+
+ if (pagesize == 0)
+ pagesize = sysconf(_SC_PAGE_SIZE);
+
+ if (pagesize == 0)
+ pagesize = 4096;
+
+ return pagesize;
+}
+
+struct memtool_mmap_fd {
+ struct memtool_fd mfd;
+ struct stat s;
+ int fd;
+};
+
+static ssize_t mmap_read(struct memtool_fd *handle, off_t offset,
+ void *buf, size_t nbytes, int width)
+{
+ struct memtool_mmap_fd *mmap_fd =
+ container_of(handle, struct memtool_mmap_fd, mfd);
+ struct stat *s = &mmap_fd->s;
+ off_t map_start, map_off;
+ void *map;
+ size_t i = 0;
+ int ret;
+
+ if (S_ISREG(s->st_mode)) {
+ if (s->st_size <= offset) {
+ errno = EINVAL;
+ perror("File to small");
+ return -1;
+ }
+
+ if (s->st_size < offset + nbytes)
+ /* truncating */
+ nbytes = s->st_size - offset;
+ }
+
+ map_start = offset & ~(mmap_pagesize() - 1);
+ map_off = offset - map_start;
+
+ map = mmap(NULL, nbytes + map_off, PROT_READ,
+ MAP_SHARED, mmap_fd->fd, map_start);
+ if (map == MAP_FAILED) {
+ perror("mmap");
+ return -1;
+ }
+
+ while (i * width + width <= nbytes) {
+ switch (width) {
+ case 1:
+ ((uint8_t *)buf)[i] = ((uint8_t *)(map + map_off))[i];
+ break;
+ case 2:
+ ((uint16_t *)buf)[i] = ((uint16_t *)(map + map_off))[i];
+ break;
+ case 4:
+ ((uint32_t *)buf)[i] = ((uint32_t *)(map + map_off))[i];
+ break;
+ case 8:
+ ((uint64_t *)buf)[i] = ((uint64_t *)(map + map_off))[i];
+ break;
+ }
+ ++i;
+ }
+
+ ret = munmap(map, nbytes + map_off);
+ if (ret < 0) {
+ perror("munmap");
+ return -1;
+ }
+
+ return i * width;
+}
+
+static ssize_t mmap_write(struct memtool_fd *handle, off_t offset,
+ const void *buf, size_t nbytes, int width)
+{
+ struct memtool_mmap_fd *mmap_fd =
+ container_of(handle, struct memtool_mmap_fd, mfd);
+ struct stat *s = &mmap_fd->s;
+ off_t map_start, map_off;
+ void *map;
+ size_t i = 0;
+ int ret;
+
+ if (S_ISREG(s->st_mode) && s->st_size < offset + nbytes) {
+ ret = posix_fallocate(mmap_fd->fd, offset, nbytes);
+ if (ret) {
+ errno = ret;
+ perror("fallocate");
+ return -1;
+ }
+ s->st_size = offset + nbytes;
+ }
+
+ map_start = offset & ~(mmap_pagesize() - 1);
+ map_off = offset - map_start;
+
+ map = mmap(NULL, nbytes + map_off, PROT_WRITE,
+ MAP_SHARED, mmap_fd->fd, map_start);
+ if (map == MAP_FAILED) {
+ perror("mmap");
+ return -1;
+ }
+
+ while (i * width + width <= nbytes) {
+ switch (width) {
+ case 1:
+ ((uint8_t *)(map + map_off))[i] = ((uint8_t *)buf)[i];
+ break;
+ case 2:
+ ((uint16_t *)(map + map_off))[i] = ((uint16_t *)buf)[i];
+ break;
+ case 4:
+ ((uint32_t *)(map + map_off))[i] = ((uint32_t *)buf)[i];
+ break;
+ case 8:
+ ((uint64_t *)(map + map_off))[i] = ((uint64_t *)buf)[i];
+ break;
+ }
+ ++i;
+ }
+
+ ret = munmap(map, nbytes + map_off);
+ if (ret < 0) {
+ perror("munmap");
+ return -1;
+ }
+
+ return i * width;
+}
+
+static int mmap_close(struct memtool_fd *handle)
+{
+ struct memtool_mmap_fd *mmap_fd =
+ container_of(handle, struct memtool_mmap_fd, mfd);
+ int ret;
+
+ ret = close(mmap_fd->fd);
+
+ free(mmap_fd);
+
+ return ret;
+}
+
+struct memtool_fd *mmap_open(const char *spec, int flags)
+{
+ struct memtool_mmap_fd *mmap_fd;
+ int ret;
+
+ mmap_fd = malloc(sizeof(*mmap_fd));
+ if (!mmap_fd) {
+ fprintf(stderr, "Failure to allocate mmap_fd\n");
+ return NULL;
+ }
+
+ mmap_fd->mfd.read = mmap_read;
+ mmap_fd->mfd.write = mmap_write;
+ mmap_fd->mfd.close = mmap_close;
+
+ mmap_fd->fd = open(spec, flags, S_IRUSR | S_IWUSR);
+ if (mmap_fd->fd < 0) {
+ perror("open");
+ free(mmap_fd);
+ return NULL;
+ }
+
+ ret = fstat(mmap_fd->fd, &mmap_fd->s);
+ if (ret) {
+ perror("fstat");
+ close(mmap_fd->fd);
+ free(mmap_fd);
+ return NULL;
+ }
+
+ return &mmap_fd->mfd;
+}
diff --git a/fileaccess.c b/fileaccess.c
new file mode 100644
index 0000000..11b5b06
--- /dev/null
+++ b/fileaccess.c
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2018 Pengutronix, Uwe Kleine-König <oss-tools@pengutronix.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, as published by the Free Software Foundation.
+ *
+ * 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 "fileaccess.h"
+#include "fileaccpriv.h"
+
+void *memtool_open(const char *spec, int flags)
+{
+ return mmap_open(spec, flags);
+}
+
+ssize_t memtool_read(void *handle,
+ off_t offset, void *buf, size_t nbytes, int width)
+{
+ struct memtool_fd *mfd = handle;
+
+ return mfd->read(mfd, offset, buf, nbytes, width);
+}
+
+ssize_t memtool_write(void *handle,
+ off_t offset, const void *buf, size_t nbytes, int width)
+{
+ struct memtool_fd *mfd = handle;
+
+ return mfd->write(mfd, offset, buf, nbytes, width);
+}
+
+int memtool_close(void *handle)
+{
+ struct memtool_fd *mfd = handle;
+
+ return mfd->close(mfd);
+}
diff --git a/fileaccess.h b/fileaccess.h
new file mode 100644
index 0000000..ba237d0
--- /dev/null
+++ b/fileaccess.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2018 Pengutronix, Uwe Kleine-König <oss-tools@pengutronix.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, as published by the Free Software Foundation.
+ *
+ * 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 <sys/types.h>
+
+void *memtool_open(const char *spec, int flags);
+ssize_t memtool_read(void *handle, off_t offset,
+ void *buf, size_t nbytes, int width);
+ssize_t memtool_write(void *handle, off_t offset,
+ const void *buf, size_t nbytes, int width);
+int memtool_close(void *handle);
diff --git a/fileaccpriv.h b/fileaccpriv.h
new file mode 100644
index 0000000..47c7104
--- /dev/null
+++ b/fileaccpriv.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2018 Pengutronix, Uwe Kleine-König <oss-tools@pengutronix.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, as published by the Free Software Foundation.
+ *
+ * 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 <sys/types.h>
+
+struct memtool_fd {
+ ssize_t (*read)(struct memtool_fd *handle, off_t offset,
+ void *buf, size_t nbytes, int width);
+ ssize_t (*write)(struct memtool_fd *handle, off_t offset,
+ const void *buf, size_t nbytes, int width);
+ int (*close)(struct memtool_fd *handle);
+};
+
+struct memtool_fd *mmap_open(const char *spec, int flags);
diff --git a/memtool.c b/memtool.c
index c095550..0c46ae5 100644
--- a/memtool.c
+++ b/memtool.c
@@ -11,7 +11,9 @@
* GNU General Public License for more details.
*/
+#include <assert.h>
#include <libgen.h>
+#include <stdint.h>
#include <stdio.h>
#include <sys/mman.h>
#include <sys/types.h>
@@ -26,6 +28,8 @@
#include <string.h>
#include <inttypes.h>
+#include "fileaccess.h"
+
#define DISP_LINE_LEN 16
/*
@@ -194,72 +198,6 @@ static int memory_display(const void *addr, off_t offs,
return 0;
}
-static int memfd;
-
-static void *memmap(const char *file, off_t addr, size_t *size, int readonly)
-{
- off_t mmap_start;
- size_t ofs;
- void *mem;
- long pagesize = sysconf(_SC_PAGE_SIZE);
- struct stat s;
- int ret;
-
- if (pagesize < 0)
- pagesize = 4096;
-
- memfd = open(file, readonly ? O_RDONLY : (O_RDWR | O_CREAT),
- S_IRUSR | S_IWUSR);
- if (memfd < 0) {
- perror("open");
- return NULL;
- }
-
- ret = fstat(memfd, &s);
- if (ret) {
- perror("fstat");
- goto out;
- }
-
- if (S_ISREG(s.st_mode)) {
- if (readonly) {
- if (s.st_size <= addr) {
- errno = EINVAL;
- perror("File to small");
- goto out;
- }
-
- if (s.st_size < addr + *size)
- /* truncating */
- *size = s.st_size - addr;
-
- } else if (s.st_size < addr + *size) {
- int ret = posix_fallocate(memfd, addr, *size);
- if (ret) {
- errno = ret;
- perror("Failed to fallocate");
- goto out;
- }
- }
- }
-
- mmap_start = addr & ~((off_t)pagesize - 1);
- ofs = addr - mmap_start;
-
- mem = mmap(NULL, *size + ofs, PROT_READ | (readonly ? 0 : PROT_WRITE),
- MAP_SHARED, memfd, mmap_start);
- if (mem == MAP_FAILED) {
- perror("mmap");
- goto out;
- }
-
- return mem + ofs;
-out:
- close(memfd);
-
- return NULL;
-}
-
static void usage_md(void)
{
printf(
@@ -289,9 +227,10 @@ static int cmd_memory_display(int argc, char **argv)
{
int opt;
int width = 4;
- size_t size = 0x100;
+ size_t bufsize, size = 0x100;
+ char *buf;
+ void *handle;
off_t start = 0x0;
- void *mem;
char *file = "/dev/mem";
int swap = 0;
@@ -330,13 +269,47 @@ static int cmd_memory_display(int argc, char **argv)
size = 0x100;
}
- mem = memmap(file, start, &size, 1);
- if (!mem)
+ if (size & (width - 1)) {
+ size &= ~(width - 1);
+ fprintf(stderr, "warning: skipping truncated read, size=%zu\n",
+ size);
+ }
+
+ if (!size)
+ return EXIT_SUCCESS;
+
+ bufsize = size;
+ if (bufsize > 4096)
+ bufsize = 4096;
+
+ buf = malloc(bufsize);
+ if (!buf) {
+ fprintf(stderr, "could not allocate memory\n");
return EXIT_FAILURE;
+ }
+
+ handle = memtool_open(file, O_RDONLY);
+ if (!handle)
+ return EXIT_FAILURE;
+
+ while (size) {
+ int ret;
+
+ if (size < bufsize)
+ bufsize = size;
+
+ ret = memtool_read(handle, start, buf, bufsize, width);
+ if (ret < 0)
+ return EXIT_FAILURE;
+
+ assert(ret == bufsize);
+ memory_display(buf, start, bufsize, width, swap);
- memory_display(mem, start, size, width, swap);
+ start += bufsize;
+ size -= bufsize;
+ }
- close(memfd);
+ memtool_close(handle);
return EXIT_SUCCESS;
}
@@ -362,10 +335,12 @@ static void usage_mw(void)
static int cmd_memory_write(int argc, char *argv[])
{
off_t adr;
- size_t size;
+ size_t bufsize, size;
+ char *buf;
+ void *handle;
int width = 4;
int opt;
- void *mem;
+ int i, ret;
char *file = "/dev/mem";
while ((opt = getopt(argc, argv, "bwlqd:h")) != -1) {
@@ -399,41 +374,62 @@ static int cmd_memory_write(int argc, char *argv[])
adr = strtoull_suffix(argv[optind++], NULL, 0);
size = (argc - optind) * width;
- mem = memmap(file, adr, &size, 0);
- if (!mem)
+ if (!size)
+ return EXIT_SUCCESS;
+
+ bufsize = size;
+ if (bufsize > 4096)
+ bufsize = 4096;
+
+ buf = malloc(bufsize);
+ if (!buf) {
+ fprintf(stderr, "could not allocate memory\n");
+ return EXIT_FAILURE;
+ }
+
+ handle = memtool_open(file, O_RDWR | O_CREAT);
+ if (!handle)
return EXIT_FAILURE;
while (optind < argc) {
- uint8_t val8;
- uint16_t val16;
- uint32_t val32;
- uint64_t val64;
-
- switch (width) {
- case 1:
- val8 = strtoul(argv[optind], NULL, 0);
- *(volatile uint8_t *)mem = val8;
- break;
- case 2:
- val16 = strtoul(argv[optind], NULL, 0);
- *(volatile uint16_t *)mem = val16;
- break;
- case 4:
- val32 = strtoul(argv[optind], NULL, 0);
- *(volatile uint32_t *)mem = val32;
- break;
- case 8:
- val64 = strtoull(argv[optind], NULL, 0);
- *(volatile uint64_t *)mem = val64;
- break;
+ i = 0;
+
+ while (optind < argc && i * width < bufsize) {
+ switch (width) {
+ case 1:
+ ((uint8_t *)buf)[i] =
+ strtoull(argv[optind], NULL, 0);
+ break;
+ case 2:
+ ((uint16_t *)buf)[i] =
+ strtoull(argv[optind], NULL, 0);
+ break;
+ case 4:
+ ((uint32_t *)buf)[i] =
+ strtoull(argv[optind], NULL, 0);
+ break;
+ case 8:
+ ((uint64_t *)buf)[i] =
+ strtoull(argv[optind], NULL, 0);
+ break;
+ }
+ ++i;
+ ++optind;
}
- mem += width;
- optind++;
+
+ ret = memtool_write(handle, adr, buf, i * width, width);
+ if (ret < 0)
+ break;
+
+ assert(ret == i * width);
+ adr += i * width;
}
- close(memfd);
- return 0;
+ memtool_close(handle);
+ free(buf);
+
+ return ret < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}
struct cmd {