diff options
author | Uwe Kleine-König <ukleinek@users.noreply.github.com> | 2018-03-16 11:44:31 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-03-16 11:44:31 +0100 |
commit | 1ddbb8ef36c2f148c5579ab8c31b35d0eb951006 (patch) | |
tree | 8067d359c44e10fb7fcdd0e42e6fcc3de590228c | |
parent | a9e244d4da0f1153381b9410b2c7eada6d6ef52c (diff) | |
parent | 433635f857a25ed35e7b3ad8d172a297c65304c1 (diff) | |
download | memtool-1ddbb8ef36c2f148c5579ab8c31b35d0eb951006.tar.gz memtool-1ddbb8ef36c2f148c5579ab8c31b35d0eb951006.tar.xz |
Merge pull request #2 from pengutronix/ukl/mdio
Teach memtool to access mdio devices
-rw-r--r-- | .gitignore | 3 | ||||
-rw-r--r-- | Makefile.am | 6 | ||||
-rw-r--r-- | acc_mdio.c | 186 | ||||
-rw-r--r-- | acc_mmap.c | 213 | ||||
-rw-r--r-- | configure.ac | 23 | ||||
-rw-r--r-- | fileaccess.c | 57 | ||||
-rw-r--r-- | fileaccess.h | 21 | ||||
-rw-r--r-- | fileaccpriv.h | 24 | ||||
-rw-r--r-- | memtool.1 | 16 | ||||
-rw-r--r-- | memtool.c | 200 |
10 files changed, 646 insertions, 103 deletions
@@ -1,3 +1,5 @@ +/acc_mdio.o +/acc_mmap.o /aclocal.m4 /autom4te.cache /compile @@ -6,6 +8,7 @@ /configure /depcomp /.deps +/fileaccess.o /install-sh /Makefile /Makefile.in diff --git a/Makefile.am b/Makefile.am index 3786362..a32517f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -2,7 +2,11 @@ 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 +if MDIO +memtool_SOURCES += acc_mdio.c +endif dist_man_MANS = memtool.1 diff --git a/acc_mdio.c b/acc_mdio.c new file mode 100644 index 0000000..23876c7 --- /dev/null +++ b/acc_mdio.c @@ -0,0 +1,186 @@ +/* + * 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 <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <sys/ioctl.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <unistd.h> + +#include <linux/if.h> +#include <linux/mii.h> +#include <linux/sockios.h> + +#include "fileaccpriv.h" + +#define container_of(ptr, type, member) \ + (type *)((char *)(ptr) - (char *) &((type *)0)->member) + +struct memtool_mdio_fd { + struct memtool_fd mfd; + int fd; + uint16_t phy_id; + char ifrn_name[IFNAMSIZ]; +}; + +static ssize_t mdio_read(struct memtool_fd *handle, off_t offset, + void *buf, size_t nbytes, int width) +{ + struct memtool_mdio_fd *mdio_fd = + container_of(handle, struct memtool_mdio_fd, mfd); + struct ifreq ifr; + struct mii_ioctl_data *mii = (void *)&ifr.ifr_data; + size_t i = 0; + int ret; + + if (width != 2) { + fprintf(stderr, + "mdio can only be accessed with memory width 2\n"); + return -EINVAL; + } + + assert((nbytes & 1) == 0); + + memset(&ifr, 0, sizeof(ifr)); + strcpy(ifr.ifr_name, mdio_fd->ifrn_name); + + mii->phy_id = mdio_fd->phy_id; + + while (2 * i < nbytes) { + mii->reg_num = offset / 2 + i; + + ret = ioctl(mdio_fd->fd, SIOCGMIIREG, &ifr); + if (ret < 0) { + perror("Failure to read register"); + return -1; + } + + ((uint16_t *)buf)[i] = mii->val_out; + ++i; + } + + return 2 * i; +} + +static ssize_t mdio_write(struct memtool_fd *handle, off_t offset, + const void *buf, size_t nbytes, int width) +{ + struct memtool_mdio_fd *mdio_fd = + container_of(handle, struct memtool_mdio_fd, mfd); + struct ifreq ifr; + struct mii_ioctl_data *mii = (void *)&ifr.ifr_data; + size_t i = 0; + int ret; + + if (width != 2) { + fprintf(stderr, + "mdio can only be accessed with memory width 2\n"); + return -EINVAL; + } + + assert((nbytes & 1) == 0); + + memset(&ifr, 0, sizeof(ifr)); + strcpy(ifr.ifr_name, mdio_fd->ifrn_name); + + mii->phy_id = mdio_fd->phy_id; + + while (2 * i < nbytes) { + mii->reg_num = offset / 2 + i; + mii->val_in = ((uint16_t *)buf)[i]; + + ret = ioctl(mdio_fd->fd, SIOCSMIIREG, &ifr); + if (ret < 0) { + perror("Failure to write register"); + return -1; + } + + ++i; + } + + return 2 * i; +} + +static int mdio_close(struct memtool_fd *handle) +{ + struct memtool_mdio_fd *mdio_fd = + container_of(handle, struct memtool_mdio_fd, mfd); + int ret; + + ret = close(mdio_fd->fd); + + free(mdio_fd); + + return ret; +} + +struct memtool_fd *mdio_open(const char *spec, int flags) +{ + struct memtool_mdio_fd *mdio_fd; + char *delim; + char *endp; + long int val; + + mdio_fd = malloc(sizeof(*mdio_fd)); + if (!mdio_fd) { + fprintf(stderr, "Failure to allocate mdio_fd\n"); + return NULL; + } + + mdio_fd->mfd.read = mdio_read; + mdio_fd->mfd.write = mdio_write; + mdio_fd->mfd.close = mdio_close; + + mdio_fd->fd = socket(AF_INET, SOCK_DGRAM, 0); + if (mdio_fd->fd < 0) { + perror("socket"); + goto err_socket; + } + + delim = strrchr(spec, '.'); + if (!delim) { + fprintf(stderr, "Failed to parse phy specifier, no \".\"\n"); + goto err_parse; + } + + if (delim - spec >= sizeof(mdio_fd->ifrn_name)) { + fprintf(stderr, "device string too long\n"); + goto err_parse; + } + + memcpy(mdio_fd->ifrn_name, spec, delim - spec); + mdio_fd->ifrn_name[delim - spec] = '\0'; + + val = strtol(delim + 1, &endp, 0); + if (*endp != '\0') { + fprintf(stderr, "failed to parse phy_id\n"); + goto err_parse; + } + + if (val < 0 || val >= (1 << 16)) { + fprintf(stderr, "phy_id out of range\n"); +err_parse: + close(mdio_fd->fd); +err_socket: + free(mdio_fd); + return NULL; + } + mdio_fd->phy_id = val; + + return &mdio_fd->mfd; +} 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/configure.ac b/configure.ac index 55273a2..dca7faa 100644 --- a/configure.ac +++ b/configure.ac @@ -8,5 +8,28 @@ AC_PROG_MAKE_SET AC_SYS_LARGEFILE +AC_ARG_ENABLE([mdio], [AS_HELP_STRING([--enable-mdio], [enable mdio access method @<:@default=check@:>@])],, [enable_mdio=check]) + +AS_IF([test "x$enable_mdio" != "xno"], + [AC_CHECK_HEADERS([sys/socket.h linux/if.h linux/mii.h linux/sockios.h],, + [AS_IF([test "x$enable_mdio" != "xyes"], [enable_mdio=no; break], [AC_MSG_ERROR([mdio depends on Linux mdio headers])])], + dnl some versions of linux/if.h are not self-contained and need struct sockaddr + dnl see https://git.kernel.org/linus/2618be7dccf8739b89e1906b64bd8d551af351e6 + [[#ifdef HAVE_SYS_SOCKET_H + # include <sys/socket.h> + #endif + ]]) + AS_IF([test "x$enable_mdio" = "xcheck"], [enable_mdio=yes]) + ]) + +AC_MSG_CHECKING([for mdio support]) +AS_IF([test "x$enable_mdio" = "xyes"], + [AC_DEFINE([USE_MDIO], [1], [Define if the mdio access method should be built-in]) + AC_MSG_RESULT([yes]) + ], [AC_MSG_RESULT([no])]) + +AM_CONDITIONAL([MDIO], [test "x$enable_mdio" = "xyes"]) + AC_CONFIG_FILES([Makefile]) + AC_OUTPUT diff --git a/fileaccess.c b/fileaccess.c new file mode 100644 index 0000000..4fa89c2 --- /dev/null +++ b/fileaccess.c @@ -0,0 +1,57 @@ +/* + * 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 <string.h> +#include <stdio.h> + +#include "fileaccess.h" +#include "fileaccpriv.h" + +void *memtool_open(const char *spec, int flags) +{ + if (!strncmp(spec, "mmap:", 5)) { + return mmap_open(spec + 5, flags); + } else if (!strncmp(spec, "mdio:", 5)) { +#ifdef USE_MDIO + return mdio_open(spec + 5, flags); +#else + fprintf(stderr, "mdio support not compiled in\n"); + return NULL; +#endif + } else { + 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..25fc4d6 --- /dev/null +++ b/fileaccpriv.h @@ -0,0 +1,24 @@ +/* + * 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 *mdio_open(const char *spec, int flags); +struct memtool_fd *mmap_open(const char *spec, int flags); @@ -29,6 +29,22 @@ to write to memory/a file; and .B md to read from memory/a file. +Usually memtool operates on files (regular or devices) using mmap(2). If +.I filename +is of the form +.RI mdio: ethname . id +with +.I ethname +being the name of an ethernet device and +.I id +being an MDIO address, the phy with address +.I id +on the MDIO bus related to the ethernet device +.I ethname +is accessed instead. To prevent ambiguities when using the mmap access method, use +.RI mmap: filename +as parameter. + Note that on some machines there are alignment restrictions that forbid for example to read a word from an address that is not word aligned. memtool doesn't try to be smart here but simply tries what is requested by the caller. @@ -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 { |