mmap: tool to mmap /sys resource files and poke pci registers
* now with test cases!
Change-Id: I96ada77ff477868d34fd52de6c2ffc38832db641
diff --git a/cmds/Makefile b/cmds/Makefile
index dfbb363..11b2c8b 100644
--- a/cmds/Makefile
+++ b/cmds/Makefile
@@ -10,6 +10,7 @@
isoping \
isostream \
mcastreceive \
+ mmap \
randomdata \
setuid \
udpburst \
diff --git a/cmds/mmap.c b/cmds/mmap.c
new file mode 100644
index 0000000..1f60a98
--- /dev/null
+++ b/cmds/mmap.c
@@ -0,0 +1,559 @@
+/*
+ * This is a scriptable tool to use mmap and write to registers.
+ */
+
+#define _POSIX_SOURCE /* for fileno! */
+#define _BSD_SOURCE /* for usleep! */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <memory.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <linux/limits.h>
+#include <fcntl.h>
+
+#define MAXSLOTS 10
+#define MAXARGS 10
+
+struct slot {
+ char path[PATH_MAX];
+ int fd;
+ off_t fileLength;
+ void* map;
+ uint64_t addr;
+ uint64_t length;
+};
+
+static struct slot slots[MAXSLOTS];
+static char* posPrefix = "";
+
+void usage(const char* prog)
+{
+ fprintf(stderr, "Usage: %s [command-file]\n", prog);
+ fprintf(stderr, "\twhere command-file or stdin contains:\n");
+ fprintf(stderr, "\t\tfile 0 /sys/bus/pci/devices/0000:01:00.0/resource0 "
+ "0 0x10000\n");
+ fprintf(stderr, "\t\tfile 2 /sys/bus/pci/devices/0000:01:00.0/resource2 "
+ "0 0x10000\n");
+ fprintf(stderr, "\t\tfile 4 /sys/bus/pci/devices/0000:01:00.0/resource4 "
+ "0 0x10000\n");
+ fprintf(stderr, "\t\tread 2 16 4 "
+ "# read file 2, addr 16, length 4\n");
+ fprintf(stderr, "\t\twrite 4 18 4 0xffff "
+ "# write file 4, addr 18, length 4, value 0xffff\n");
+ fprintf(stderr, "\t\tdump 4 18 4 100 "
+ "# dump file 4, addr 18, length 4, 100 values\n");
+ fprintf(stderr, "\t\tclose 0 "
+ "# close a file\n");
+}
+
+int asUnsigned(uint64_t* ip, const char* str)
+{
+ char* end;
+
+ uint64_t value = strtoull(str, &end, 0);
+ if (end == str || *end != '\0') {
+ fprintf(stderr, "%s", posPrefix);
+ fprintf(stderr, "failed to parse '%s' as unsigned (eg 0x10 or 16)\n", str);
+ return -1;
+ }
+ *ip = value;
+ return 0;
+}
+
+int asFileAddr(uint64_t* ip, const char* str, int slot)
+{
+ if (asUnsigned(ip, str) < 0) {
+ return -1;
+ }
+ if (*ip >= (uint64_t) slots[slot].fileLength) {
+ fprintf(stderr, "%s", posPrefix);
+ fprintf(stderr, "address '%s' exceeds bounds of file\n", str);
+ return -1;
+ }
+ return 0;
+}
+
+int asFileLength(uint64_t* ip, const char* str, int slot, uint64_t fileAddr)
+{
+ if (asUnsigned(ip, str) < 0) {
+ return -1;
+ }
+ if (fileAddr + *ip >= (uint64_t) slots[slot].fileLength) {
+ fprintf(stderr, "%s", posPrefix);
+ fprintf(stderr, "length '%s' exceeds bounds of file\n", str);
+ return -1;
+ }
+ return 0;
+}
+
+int asAddr(uint64_t* ip, const char* str, int slot)
+{
+ if (asUnsigned(ip, str) < 0) {
+ return -1;
+ }
+ if (*ip >= (uint64_t) slots[slot].length) {
+ fprintf(stderr, "%s", posPrefix);
+ fprintf(stderr, "address '%s' out of range 0x%" PRIx64 "..0x%" PRIx64 "\n",
+ str, slots[slot].addr, slots[slot].addr + slots[slot].length-1);
+ return -1;
+ }
+ return 0;
+}
+
+int asLength(uint64_t* ip, const char* str, int slot, uint64_t addr)
+{
+ if (asUnsigned(ip, str) < 0) {
+ return -1;
+ }
+ if (addr + *ip >= addr + slots[slot].length) {
+ fprintf(stderr, "%s", posPrefix);
+ fprintf(stderr, "address '%s' out of range 0..0x%" PRIx64 "\n",
+ str, slots[slot].length-1);
+ return -1;
+ }
+ return 0;
+}
+
+int asWordLen(int *ip, const char* str)
+{
+ uint64_t value;
+ if (asUnsigned(&value, str) < 0) {
+ return -1;
+ }
+ if (value != 1 && value != 2 && value != 4 && value != 8) {
+ fprintf(stderr, "%s", posPrefix);
+ fprintf(stderr, "length '%s' must be 1, 2, 4 or 8\n", str);
+ return -1;
+ }
+ *ip = value;
+ return 0;
+}
+
+int asSlot(int *ip, const char* str, int wantOpen)
+{
+ uint64_t value;
+ if (asUnsigned(&value, str) < 0) {
+ return -1;
+ }
+ if (value >= MAXSLOTS) {
+ fprintf(stderr, "%s", posPrefix);
+ fprintf(stderr, "slot '%s' is out of range 0-%d or is not open\n",
+ str, MAXSLOTS-1);
+ return -1;
+ }
+ if (wantOpen && slots[value].map == NULL) {
+ fprintf(stderr, "%s", posPrefix);
+ fprintf(stderr, "slot '%s' is not open\n", str);
+ return -1;
+ } else if (!wantOpen && slots[value].map != NULL) {
+ fprintf(stderr, "%s", posPrefix);
+ fprintf(stderr, "slot '%s' is already open\n", str);
+ return -1;
+ }
+ *ip = value;
+ return 0;
+}
+
+int do_open(char* path, int slot, uint64_t fileAddr, uint64_t length)
+{
+ int fd = open(path, O_RDWR);
+ if (fd < 0) {
+ perror(path);
+ return -1;
+ }
+
+ struct stat stats;
+ if (fstat(fd, &stats) < 0) {
+ perror("fstat");
+ close(fd);
+ return -1;
+ }
+ uint64_t fileLength = stats.st_size;
+
+ if (fileAddr + length > fileLength) {
+ fprintf(stderr, "mapped range (0x%" PRIx64 ",0x%" PRIx64 ") "
+ "is outside of size of file (0x%" PRIx64 ")\n",
+ fileAddr, length, fileLength);
+ close(fd);
+ return -1;
+ }
+
+ void* mm = mmap(NULL, length, PROT_READ|PROT_WRITE, MAP_SHARED, fd, fileAddr);
+ if (mm == NULL) {
+ perror("mmap");
+ close(fd);
+ return -1;
+ }
+ strncpy(slots[slot].path, path, sizeof slots[slot].path);
+ slots[slot].fd = fd;
+ slots[slot].fileLength = fileLength;
+ slots[slot].map = mm;
+ slots[slot].addr = fileAddr;
+ slots[slot].length = length;
+ return 0;
+}
+
+void do_close(int slot) {
+ munmap(slots[slot].map, slots[slot].length);
+ close(slots[slot].fd);
+ memset(&slots[slot], 0, sizeof (slots[slot]));
+ slots[slot].fd = -1;
+}
+
+int do_read_helper(int slot, uint64_t addr, int wordlen, uint64_t* valueP)
+{
+ void* vp = slots[slot].map + addr;
+ unsigned char uc;
+ unsigned short us;
+ unsigned int ui;
+ unsigned long long ull;
+ void *dp;
+
+ switch (wordlen) {
+ case sizeof(ull): dp = &ull; break;
+ case sizeof(ui): dp = &ui; break;
+ case sizeof(us): dp = &us; break;
+ case sizeof(uc): dp = &uc; break;
+ default:
+ fprintf(stderr, "Can't find datatype for wordlen '%d'\n", wordlen);
+ return -1;
+ }
+
+ memcpy(dp, vp, wordlen);
+
+ switch (wordlen) {
+ case sizeof(ull): *valueP = ull; break;
+ case sizeof(ui): *valueP = ui; break;
+ case sizeof(us): *valueP = us; break;
+ case sizeof(uc): *valueP = uc; break;
+ default:
+ fprintf(stderr, "Can't find datatype for wordlen '%d'\n", wordlen);
+ return -1;
+ }
+ return 0;
+}
+
+int do_read(int slot, uint64_t addr, int wordlen)
+{
+ uint64_t value;
+
+ if (do_read_helper(slot, addr, wordlen, &value) < 0) {
+ return -1;
+ }
+ printf("0x%0*" PRIx64 "\n", 2*wordlen, value);
+ return 0;
+}
+
+void do_write(int slot, uint64_t addr, int wordlen, uint64_t value)
+{
+ void* vp = slots[slot].map + addr;
+ memcpy(vp, &value, wordlen);
+}
+
+int do_hexdump(int slot, uint64_t addr, int wordlen, uint64_t count)
+{
+ uint64_t perline = 16/wordlen;
+ int err = 0;
+
+ for (uint64_t i = 0; i < count; i += perline) {
+ printf("%08" PRIx64 ":", addr + i * wordlen);
+ for (uint64_t j = 0; j < perline; j++) {
+ if (i + j >= count) {
+ break;
+ }
+ uint64_t value = 0;
+ if (do_read_helper(slot, addr + (i + j) * wordlen, wordlen, &value) < 0) {
+ err = -1;
+ }
+ printf(" 0x%0*" PRIx64, 2*wordlen, value);
+ }
+ printf("\n");
+ }
+ return err;
+}
+
+int cmd_open(int ac, char* av[])
+{
+ char* usage = "open slot file offset length";
+
+ if (ac > 1 && strcmp(av[1], "help") == 0) {
+ printf("\t%s\n", usage);
+ return 0;
+ }
+ if (ac != 5) {
+ fprintf(stderr, "Usage: %s\n", usage);
+ return -1;
+ }
+
+ int slot;
+ char* path = av[2];
+ uint64_t offset;
+ uint64_t len;
+
+ if (asSlot(&slot, av[1], 0) < 0 ||
+ asUnsigned(&offset, av[3]) < 0 ||
+ asUnsigned(&len, av[4]) < 0) {
+ return -1;
+ }
+ if (do_open(path, slot, offset, len) < 0) {
+ return -1;
+ }
+ return 0;
+}
+
+int cmd_close(int ac, char* av[])
+{
+ char* usage = "close slot";
+
+ if (ac > 1 && strcmp(av[1], "help") == 0) {
+ printf("\t%s\n", usage);
+ return 0;
+ }
+ if (ac != 2) {
+ fprintf(stderr, "Usage: %s\n", usage);
+ return -1;
+ }
+
+ int slot;
+
+ if (asSlot(&slot, av[1], 1) < 0) {
+ return -1;
+ }
+ do_close(slot);
+ return 0;
+}
+
+int cmd_read(int ac, char* av[])
+{
+ char* usage = "read slot addr wordlen";
+
+ if (ac > 1 && strcmp(av[1], "help") == 0) {
+ printf("\t%s\n", usage);
+ return 0;
+ }
+ if (ac != 4) {
+ fprintf(stderr, "Usage: %s\n", usage);
+ return -1;
+ }
+
+ int slot;
+ uint64_t addr;
+ int wordlen;
+
+ if (asSlot(&slot, av[1], 1) < 0 ||
+ asAddr(&addr, av[2], slot) < 0 ||
+ asWordLen(&wordlen, av[3]) < 0) {
+ return -1;
+ }
+ if (do_read(slot, addr, wordlen) < 0) {
+ return -1;
+ }
+ return 0;
+}
+
+int cmd_write(int ac, char* av[])
+{
+ char* usage = "write slot addr wordlen value";
+
+ if (ac > 1 && strcmp(av[1], "help") == 0) {
+ printf("\t%s\n", usage);
+ return 0;
+ }
+ if (ac != 5) {
+ fprintf(stderr, "Usage: %s\n", usage);
+ return -1;
+ }
+
+ int slot;
+ uint64_t addr;
+ int wordlen;
+ uint64_t value;
+
+ if (asSlot(&slot, av[1], 1) < 0 ||
+ asAddr(&addr, av[2], slot) < 0 ||
+ asWordLen(&wordlen, av[3]) < 0 ||
+ asUnsigned(&value, av[4]) < 0) {
+ return -1;
+ }
+ do_write(slot, addr, wordlen, value);
+ return 0;
+}
+
+int cmd_dump(int ac, char* av[])
+{
+ char* usage = "dump slot addr wordlen count";
+
+ if (ac > 1 && strcmp(av[1], "help") == 0) {
+ printf("\t%s\n", usage);
+ return 0;
+ }
+ if (ac != 5) {
+ fprintf(stderr, "Usage: %s\n", usage);
+ return -1;
+ }
+
+ int slot;
+ uint64_t addr;
+ int wordlen;
+ uint64_t count;
+
+ if (asSlot(&slot, av[1], 1) < 0 ||
+ asUnsigned(&addr, av[2]) < 0 ||
+ asWordLen(&wordlen, av[3]) < 0 ||
+ asUnsigned(&count, av[4]) < 0) {
+ return -1;
+ }
+ if (do_hexdump(slot, addr, wordlen, count) < 0) {
+ return -1;
+ }
+ return 0;
+}
+
+int cmd_msleep(int ac, char* av[])
+{
+ char* usage = "msleep msecs";
+
+ if (ac > 1 && strcmp(av[1], "help") == 0) {
+ printf("\t%s\n", usage);
+ return 0;
+ }
+ if (ac != 2) {
+ fprintf(stderr, "Usage: %s\n", usage);
+ return -1;
+ }
+
+ uint64_t msecs;
+
+ if (asUnsigned(&msecs, av[1]) < 0) {
+ return -1;
+ }
+ usleep(msecs*1000);
+ return 0;
+}
+
+typedef int (*cmdFunc)(int ac, char* av[]);
+
+struct commands {
+ char* name;
+ cmdFunc func;
+};
+
+struct commands cmds[] = {
+ { "open", cmd_open, },
+ { "close", cmd_close, },
+ { "read", cmd_read, },
+ { "write", cmd_write, },
+ { "dump", cmd_dump, },
+ { "msleep", cmd_msleep, },
+ { NULL, NULL },
+};
+
+int main(int argc, char* argv[])
+{
+ char* fpName = "stdin";
+ FILE* fp = stdin;
+ char* delim= " \t\n";
+
+ setbuf(stdout, NULL); // unbuffered
+
+ if (argc > 1) {
+ fpName = argv[1];
+ fp = fopen(fpName, "r");
+ if (fp == NULL) {
+ perror(fpName);
+ usage(argv[0]);
+ exit(1);
+ }
+ }
+ int isTTY = isatty(fileno(fp));
+
+ int lineno = 0;
+ int err = 0;
+ for (;;) {
+ char line[1024] = { 0 };
+ if (isTTY) {
+ printf("mmap>> ");
+ }
+ if (fgets(line, sizeof line, fp) == NULL) {
+ break; // EOF
+ }
+
+ lineno++;
+ char fileLine[128];
+ sprintf(fileLine, "%s:%d: ", fpName, lineno);
+ posPrefix = fileLine;
+
+ int len = strlen(line);
+ if (len == 0) {
+ continue;
+ }
+ if (line[len-1] != '\n') {
+ fprintf(stderr, "%s", posPrefix);
+ fprintf(stderr, "line too long\n");
+ err++;
+ continue;
+ }
+ if (line[0] == '#') {
+ continue; // skip comments
+ }
+
+ printf("# %s", line);
+
+ int ac = 0;
+ char* av[MAXARGS];
+ char** avP = av;
+
+ *avP = strtok(line, delim);
+ while (*avP != NULL) {
+ if (ac >= MAXARGS) {
+ break;
+ }
+ ac++;
+ avP++;
+ *avP = strtok(NULL, delim);
+ }
+ if (ac > MAXARGS) {
+ fprintf(stderr, "%s", posPrefix);
+ fprintf(stderr, "too many arguments\n");
+ err++;
+ continue;
+ }
+ if (ac == 0) {
+ continue; // skip whitespace only
+ }
+
+ if (strcmp(av[0], "help") == 0) {
+ char* args[] = { "", "help" };
+ for (struct commands* cp = cmds; cp->func != NULL; cp++) {
+ cp->func(2, args);
+ }
+ continue;
+ }
+
+ int match = 0;
+ for (struct commands* cp = cmds; cp->func != NULL; cp++) {
+ if (strcmp(av[0], cp->name) == 0) {
+ if (cp->func(ac, av) < 0) {
+ fprintf(stderr, "%s", posPrefix);
+ fprintf(stderr, "command '%s' failed\n", av[0]);
+ err++;
+ }
+ match = 1;
+ break;
+ }
+ }
+ if (!match) {
+ fprintf(stderr, "%s", posPrefix);
+ fprintf(stderr, "unknown command '%s', try help\n", av[0]);
+ err++;
+ continue;
+ }
+ }
+ exit(err != 0);
+}
diff --git a/cmds/test-mmap.sh b/cmds/test-mmap.sh
new file mode 100755
index 0000000..15aabd2
--- /dev/null
+++ b/cmds/test-mmap.sh
@@ -0,0 +1,48 @@
+exit=0
+run=.run.$$
+for n in tests.mmap/*.test; do
+ rm -rf $run
+ mkdir $run
+ : > $run/LOG
+ (
+ cd $run
+ . ../$n
+ echo -n "$INPUT" > INPUT
+ echo -n "$OUTPUT" > WANT
+ for m in $FILES; do
+ eval IN="\$${m}_1"
+ eval OUT="\$${m}_2"
+ echo -n "$IN" > $m
+ echo -n "$OUT" > $m.expected
+ done
+ $PREFIX ../host-mmap < INPUT >& GOT
+ status=$?
+ if [ -n "$PREFIX" ]; then
+ sleep .5 # script mysteriously delays output
+ fi
+ if [ "$status" != "$EXIT" ]; then
+ echo "exit code: expected '$EXIT', got '$status'"
+ fi
+ if ! cmp WANT GOT; then
+ echo "output differs from expected:"
+ diff -u WANT GOT
+ fi
+ for m in $FILES; do
+ if ! cmp $m $m.expected; then
+ echo "mapped file result differs from expected:"
+ diff -u $m $m.expected
+ fi
+ done
+ ) >& $run/LOG
+ if [ ! -s $run/LOG ]; then
+ echo PASS $n
+ else
+ echo ======================================================================
+ echo FAIL $n
+ cat $run/LOG
+ echo ======================================================================
+ exit=1
+ fi
+done
+rm -rf $run
+exit $exit
diff --git a/cmds/tests.mmap/close_bad_slot.test b/cmds/tests.mmap/close_bad_slot.test
new file mode 100644
index 0000000..4914a5d
--- /dev/null
+++ b/cmds/tests.mmap/close_bad_slot.test
@@ -0,0 +1,16 @@
+EXIT=1
+
+FILES="A B"
+
+A_1='0123456789012345678901234567890123456789'
+A_2='0123456789012345678901234567890123456789'
+
+INPUT='
+close 0
+'
+
+OUTPUT="#
+# close 0
+stdin:2: slot '0' is not open
+stdin:2: command 'close' failed
+"
diff --git a/cmds/tests.mmap/close_twice.test b/cmds/tests.mmap/close_twice.test
new file mode 100644
index 0000000..29d424a
--- /dev/null
+++ b/cmds/tests.mmap/close_twice.test
@@ -0,0 +1,20 @@
+EXIT=1
+
+FILES="A B"
+
+A_1='0123456789012345678901234567890123456789'
+A_2='0123456789012345678901234567890123456789'
+
+INPUT='
+open 0 A 0 40
+close 0
+close 0
+'
+
+OUTPUT="#
+# open 0 A 0 40
+# close 0
+# close 0
+stdin:4: slot '0' is not open
+stdin:4: command 'close' failed
+"
diff --git a/cmds/tests.mmap/dump-unaligned.test b/cmds/tests.mmap/dump-unaligned.test
new file mode 100644
index 0000000..f2a8560
--- /dev/null
+++ b/cmds/tests.mmap/dump-unaligned.test
@@ -0,0 +1,36 @@
+EXIT=0
+
+FILES="A B"
+
+A_1='x0123456789012345678901234567890123456789'
+A_2='x0123456789012345678901234567890123456789'
+
+INPUT='
+open 0 A 0 40
+dump 0 1 1 40
+dump 0 1 2 20
+dump 0 1 4 10
+dump 0 1 8 5
+close 0
+'
+
+OUTPUT="#
+# open 0 A 0 40
+# dump 0 1 1 40
+00000001: 0x30 0x31 0x32 0x33 0x34 0x35 0x36 0x37 0x38 0x39 0x30 0x31 0x32 0x33 0x34 0x35
+00000011: 0x36 0x37 0x38 0x39 0x30 0x31 0x32 0x33 0x34 0x35 0x36 0x37 0x38 0x39 0x30 0x31
+00000021: 0x32 0x33 0x34 0x35 0x36 0x37 0x38 0x39
+# dump 0 1 2 20
+00000001: 0x3130 0x3332 0x3534 0x3736 0x3938 0x3130 0x3332 0x3534
+00000011: 0x3736 0x3938 0x3130 0x3332 0x3534 0x3736 0x3938 0x3130
+00000021: 0x3332 0x3534 0x3736 0x3938
+# dump 0 1 4 10
+00000001: 0x33323130 0x37363534 0x31303938 0x35343332
+00000011: 0x39383736 0x33323130 0x37363534 0x31303938
+00000021: 0x35343332 0x39383736
+# dump 0 1 8 5
+00000001: 0x3736353433323130 0x3534333231303938
+00000011: 0x3332313039383736 0x3130393837363534
+00000021: 0x3938373635343332
+# close 0
+"
diff --git a/cmds/tests.mmap/dump.test b/cmds/tests.mmap/dump.test
new file mode 100644
index 0000000..c209096
--- /dev/null
+++ b/cmds/tests.mmap/dump.test
@@ -0,0 +1,31 @@
+EXIT=0
+
+FILES="A B"
+
+A_1='0123456789012345678901234567890123456789'
+A_2='0123456789012345678901234567890123456789'
+
+INPUT='
+open 0 A 0 40
+dump 0 0 1 40
+dump 0 0 2 20
+dump 0 0 4 10
+close 0
+'
+
+OUTPUT="#
+# open 0 A 0 40
+# dump 0 0 1 40
+00000000: 0x30 0x31 0x32 0x33 0x34 0x35 0x36 0x37 0x38 0x39 0x30 0x31 0x32 0x33 0x34 0x35
+00000010: 0x36 0x37 0x38 0x39 0x30 0x31 0x32 0x33 0x34 0x35 0x36 0x37 0x38 0x39 0x30 0x31
+00000020: 0x32 0x33 0x34 0x35 0x36 0x37 0x38 0x39
+# dump 0 0 2 20
+00000000: 0x3130 0x3332 0x3534 0x3736 0x3938 0x3130 0x3332 0x3534
+00000010: 0x3736 0x3938 0x3130 0x3332 0x3534 0x3736 0x3938 0x3130
+00000020: 0x3332 0x3534 0x3736 0x3938
+# dump 0 0 4 10
+00000000: 0x33323130 0x37363534 0x31303938 0x35343332
+00000010: 0x39383736 0x33323130 0x37363534 0x31303938
+00000020: 0x35343332 0x39383736
+# close 0
+"
diff --git a/cmds/tests.mmap/help.test b/cmds/tests.mmap/help.test
new file mode 100644
index 0000000..2db1443
--- /dev/null
+++ b/cmds/tests.mmap/help.test
@@ -0,0 +1,33 @@
+EXIT=0
+
+INPUT='
+help
+open help
+close help
+read help
+write help
+dump help
+msleep help
+'
+
+OUTPUT='#
+# help
+ open slot file offset length
+ close slot
+ read slot addr wordlen
+ write slot addr wordlen value
+ dump slot addr wordlen count
+ msleep msecs
+# open help
+ open slot file offset length
+# close help
+ close slot
+# read help
+ read slot addr wordlen
+# write help
+ write slot addr wordlen value
+# dump help
+ dump slot addr wordlen count
+# msleep help
+ msleep msecs
+'
diff --git a/cmds/tests.mmap/interactive.test b/cmds/tests.mmap/interactive.test
new file mode 100644
index 0000000..b50d751
--- /dev/null
+++ b/cmds/tests.mmap/interactive.test
@@ -0,0 +1,20 @@
+PREFIX="script -e -f -q -c"
+EXIT=0
+
+FILES="A B"
+
+A_1='0123456789012345678901234567890123456789'
+A_2='0123456789A12345678901234567890123456789'
+
+INPUT='
+open 0 A 0 40
+write 0 10 1 0x41
+close 0
+'
+
+OUTPUT=\
+"mmap>> #
+mmap>> # open 0 A 0 40
+mmap>> # write 0 10 1 0x41
+mmap>> # close 0
+mmap>> "
diff --git a/cmds/tests.mmap/map_fail.test b/cmds/tests.mmap/map_fail.test
new file mode 100644
index 0000000..afb04de
--- /dev/null
+++ b/cmds/tests.mmap/map_fail.test
@@ -0,0 +1,20 @@
+EXIT=1
+
+FILES="A B"
+
+A_1='0123456789012345678901234567890123456789'
+A_2='0123456789012345678901234567890123456789'
+
+INPUT='
+open 0 A 0 41
+close 0
+'
+
+OUTPUT="#
+# open 0 A 0 41
+mapped range (0x0,0x29) is outside of size of file (0x28)
+stdin:2: command 'open' failed
+# close 0
+stdin:3: slot '0' is not open
+stdin:3: command 'close' failed
+"
diff --git a/cmds/tests.mmap/missing_file.test b/cmds/tests.mmap/missing_file.test
new file mode 100644
index 0000000..c3136a0
--- /dev/null
+++ b/cmds/tests.mmap/missing_file.test
@@ -0,0 +1,16 @@
+EXIT=1
+
+FILES="A B"
+
+A_1='0123456789012345678901234567890123456789'
+A_2='0123456789012345678901234567890123456789'
+
+INPUT='
+open 0 this-file-does-not-exist 0 40
+'
+
+OUTPUT="#
+# open 0 this-file-does-not-exist 0 40
+this-file-does-not-exist: No such file or directory
+stdin:2: command 'open' failed
+"
diff --git a/cmds/tests.mmap/msleep.test b/cmds/tests.mmap/msleep.test
new file mode 100644
index 0000000..569107e
--- /dev/null
+++ b/cmds/tests.mmap/msleep.test
@@ -0,0 +1,14 @@
+EXIT=0
+
+FILES="A B"
+
+A_1='0123456789012345678901234567890123456789'
+A_2='0123456789012345678901234567890123456789'
+
+INPUT='
+msleep 1000
+'
+
+OUTPUT="#
+# msleep 1000
+"
diff --git a/cmds/tests.mmap/open_close.test b/cmds/tests.mmap/open_close.test
new file mode 100644
index 0000000..31f184d
--- /dev/null
+++ b/cmds/tests.mmap/open_close.test
@@ -0,0 +1,20 @@
+EXIT=0
+
+FILES="A B"
+
+A_1='0123456789012345678901234567890123456789'
+A_2='0123456789012345678901234567890123456789'
+
+INPUT='
+open 0 A 0 40
+close 0
+open 1 A 0 40
+close 1
+'
+
+OUTPUT='#
+# open 0 A 0 40
+# close 0
+# open 1 A 0 40
+# close 1
+'
diff --git a/cmds/tests.mmap/open_twice.test b/cmds/tests.mmap/open_twice.test
new file mode 100644
index 0000000..34d23fe
--- /dev/null
+++ b/cmds/tests.mmap/open_twice.test
@@ -0,0 +1,18 @@
+EXIT=1
+
+FILES="A B"
+
+A_1='0123456789012345678901234567890123456789'
+A_2='0123456789012345678901234567890123456789'
+
+INPUT='
+open 0 A 0 40
+open 0 A 0 40
+'
+
+OUTPUT="#
+# open 0 A 0 40
+# open 0 A 0 40
+stdin:3: slot '0' is already open
+stdin:3: command 'open' failed
+"
diff --git a/cmds/tests.mmap/read-unaligned.test b/cmds/tests.mmap/read-unaligned.test
new file mode 100644
index 0000000..6940acc
--- /dev/null
+++ b/cmds/tests.mmap/read-unaligned.test
@@ -0,0 +1,28 @@
+EXIT=0
+
+FILES="A B"
+
+A_1='x012345678901234567890123456789012345678'
+A_2='x012345678901234567890123456789012345678'
+
+INPUT='
+open 0 A 0 40
+read 0 1 1
+read 0 1 2
+read 0 1 4
+read 0 1 8
+close 0
+'
+
+OUTPUT="#
+# open 0 A 0 40
+# read 0 1 1
+0x30
+# read 0 1 2
+0x3130
+# read 0 1 4
+0x33323130
+# read 0 1 8
+0x3736353433323130
+# close 0
+"
diff --git a/cmds/tests.mmap/read.test b/cmds/tests.mmap/read.test
new file mode 100644
index 0000000..7db9eac
--- /dev/null
+++ b/cmds/tests.mmap/read.test
@@ -0,0 +1,28 @@
+EXIT=0
+
+FILES="A B"
+
+A_1='0123456789012345678901234567890123456789'
+A_2='0123456789012345678901234567890123456789'
+
+INPUT='
+open 0 A 0 40
+read 0 0 1
+read 0 0 2
+read 0 0 4
+read 0 0 8
+close 0
+'
+
+OUTPUT="#
+# open 0 A 0 40
+# read 0 0 1
+0x30
+# read 0 0 2
+0x3130
+# read 0 0 4
+0x33323130
+# read 0 0 8
+0x3736353433323130
+# close 0
+"
diff --git a/cmds/tests.mmap/signed.test b/cmds/tests.mmap/signed.test
new file mode 100644
index 0000000..0ba44aa
--- /dev/null
+++ b/cmds/tests.mmap/signed.test
@@ -0,0 +1,20 @@
+EXIT=1
+
+FILES="A B"
+
+A_1='0123456789012345678901234567890123456789'
+A_2='0123456789012345678901234567890123456789'
+
+INPUT='
+open 0 A 0 -1
+close 0
+'
+
+OUTPUT="#
+# open 0 A 0 -1
+mapped range (0x0,0xffffffffffffffff) is outside of size of file (0x28)
+stdin:2: command 'open' failed
+# close 0
+stdin:3: slot '0' is not open
+stdin:3: command 'close' failed
+"
diff --git a/cmds/tests.mmap/wordlen.test b/cmds/tests.mmap/wordlen.test
new file mode 100644
index 0000000..d6b08bd
--- /dev/null
+++ b/cmds/tests.mmap/wordlen.test
@@ -0,0 +1,18 @@
+EXIT=1
+
+FILES="A B"
+
+A_1='0123456789012345678901234567890123456789'
+A_2='0123456789012345678901234567890123456789'
+
+INPUT='
+open 0 A 0 40
+write 0 0 0 0x41
+'
+
+OUTPUT="#
+# open 0 A 0 40
+# write 0 0 0 0x41
+stdin:3: length '0' must be 1, 2, 4 or 8
+stdin:3: command 'write' failed
+"
diff --git a/cmds/tests.mmap/write-unaligned.test b/cmds/tests.mmap/write-unaligned.test
new file mode 100644
index 0000000..0652094
--- /dev/null
+++ b/cmds/tests.mmap/write-unaligned.test
@@ -0,0 +1,24 @@
+EXIT=0
+
+FILES="A B"
+
+A_1='x012345678901234567890123456789012345678'
+A_2='xA1CB4567GFED2345ONMLKJIH456789012345678'
+
+INPUT='
+open 0 A 0 40
+write 0 1 1 0x41
+write 0 3 2 0x4243
+write 0 9 4 0x44454647
+write 0 17 8 0x48494a4b4c4d4e4f
+close 0
+'
+
+OUTPUT="#
+# open 0 A 0 40
+# write 0 1 1 0x41
+# write 0 3 2 0x4243
+# write 0 9 4 0x44454647
+# write 0 17 8 0x48494a4b4c4d4e4f
+# close 0
+"
diff --git a/cmds/tests.mmap/write.test b/cmds/tests.mmap/write.test
new file mode 100644
index 0000000..eb64c62
--- /dev/null
+++ b/cmds/tests.mmap/write.test
@@ -0,0 +1,24 @@
+EXIT=0
+
+FILES="A B"
+
+A_1='0123456789012345678901234567890123456789'
+A_2='A1CB4567GFED2345ONMLKJIH4567890123456789'
+
+INPUT='
+open 0 A 0 40
+write 0 0 1 0x41
+write 0 2 2 0x4243
+write 0 8 4 0x44454647
+write 0 16 8 0x48494a4b4c4d4e4f
+close 0
+'
+
+OUTPUT="#
+# open 0 A 0 40
+# write 0 0 1 0x41
+# write 0 2 2 0x4243
+# write 0 8 4 0x44454647
+# write 0 16 8 0x48494a4b4c4d4e4f
+# close 0
+"
diff --git a/cmds/tests.mmap/write_fail.test b/cmds/tests.mmap/write_fail.test
new file mode 100644
index 0000000..ebd559a
--- /dev/null
+++ b/cmds/tests.mmap/write_fail.test
@@ -0,0 +1,20 @@
+EXIT=1
+
+FILES="A B"
+
+A_1='0123456789012345678901234567890123456789'
+A_2='0123456789012345678901234567890123456789'
+
+INPUT='
+open 0 A 0 40
+write 0 999 1 0x41
+close 0
+'
+
+OUTPUT="#
+# open 0 A 0 40
+# write 0 999 1 0x41
+stdin:3: address '999' out of range 0x0..0x27
+stdin:3: command 'write' failed
+# close 0
+"