diff options
author | Uwe Kleine-König <u.kleine-koenig@pengutronix.de> | 2018-03-14 17:46:49 +0100 |
---|---|---|
committer | Uwe Kleine-König <u.kleine-koenig@pengutronix.de> | 2018-03-15 21:23:33 +0100 |
commit | 797c1a15eeb35c46284f651c3d82bcee2d750bec (patch) | |
tree | ee7f1f93b2d3ef2326ef8d9935db4763f724251b | |
parent | 312bcb715f61d2c3b05b1559eca2cba5b9a630c7 (diff) | |
download | memtool-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-- | .gitignore | 2 | ||||
-rw-r--r-- | Makefile.am | 3 | ||||
-rw-r--r-- | acc_mmap.c | 213 | ||||
-rw-r--r-- | fileaccess.c | 43 | ||||
-rw-r--r-- | fileaccess.h | 21 | ||||
-rw-r--r-- | fileaccpriv.h | 23 | ||||
-rw-r--r-- | memtool.c | 200 |
7 files changed, 402 insertions, 103 deletions
@@ -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); @@ -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 { |