MT8173 PCM firmware analysis tools
authorPaul Kocialkowski <contact@paulk.fr>
Thu, 16 Feb 2017 15:01:24 +0000 (16:01 +0100)
committerPaul Kocialkowski <contact@paulk.fr>
Thu, 23 Feb 2017 18:38:41 +0000 (19:38 +0100)
Signed-off-by: Paul Kocialkowski <contact@paulk.fr>
15 files changed:
.gitignore [new file with mode: 0644]
pcm-analysis/Makefile [new file with mode: 0644]
pcm-analysis/pcm [new symlink]
pcm-analysis/pcm-analysis.c [new file with mode: 0644]
pcm-analysis/pcm-analysis.h [new file with mode: 0644]
pcm-asm/Makefile [new file with mode: 0644]
pcm-asm/pcm [new symlink]
pcm-asm/pcm-asm.c [new file with mode: 0644]
pcm-asm/pcm-asm.h [new file with mode: 0644]
pcm-disasm/Makefile [new file with mode: 0644]
pcm-disasm/pcm [new symlink]
pcm-disasm/pcm-disasm.c [new file with mode: 0644]
pcm-disasm/pcm-disasm.h [new file with mode: 0644]
pcm/pcm.c [new file with mode: 0644]
pcm/pcm.h [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..5434ccb
--- /dev/null
@@ -0,0 +1,2 @@
+build
+mt8173-pcm
diff --git a/pcm-analysis/Makefile b/pcm-analysis/Makefile
new file mode 100644 (file)
index 0000000..9019f01
--- /dev/null
@@ -0,0 +1,83 @@
+# Copyright (C) 2016-2017 Paul Kocialkowski <contact@paulk.fr>
+#
+# 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/>.
+
+# Tools
+
+CC = gcc
+
+# Project
+
+NAME = pcm-analysis
+
+# Directories
+
+BUILD = build
+OUTPUT = .
+
+# Sources
+
+SOURCES = pcm/pcm.c pcm-analysis.c
+OBJECTS = $(SOURCES:.c=.o)
+DEPS = $(SOURCES:.c=.d)
+
+# Compiler
+
+INCLUDES = .
+
+CFLAGS = $(foreach include,$(INCLUDES),-I$(include))
+LDFLAGS =
+
+# Produced files
+
+BUILD_OBJECTS = $(addprefix $(BUILD)/,$(OBJECTS))
+BUILD_DEPS = $(addprefix $(BUILD)/,$(DEPS))
+BUILD_BINARY = $(BUILD)/$(NAME)
+BUILD_DIRS = $(sort $(dir $(BUILD_BINARY) $(BUILD_OBJECTS)))
+
+OUTPUT_BINARY = $(OUTPUT)/$(NAME)
+OUTPUT_DIRS = $(sort $(dir $(OUTPUT_BINARY)))
+
+all: $(OUTPUT_BINARY)
+
+$(BUILD_DIRS):
+       @mkdir -p $@
+
+$(BUILD_OBJECTS): $(BUILD)/%.o: %.c | $(BUILD_DIRS)
+       @echo " CC     $<"
+       @$(CC) $(CFLAGS) -MMD -MF $(BUILD)/$*.d -c $< -o $@
+
+$(BUILD_BINARY): $(BUILD_OBJECTS)
+       @echo " LINK   $@"
+       @$(CC) $(CFLAGS) -o $@ $(BUILD_OBJECTS) $(LDFLAGS)
+
+$(OUTPUT_DIRS):
+       @mkdir -p $@
+
+$(OUTPUT_BINARY): $(BUILD_BINARY) | $(OUTPUT_DIRS)
+       @echo " BINARY $@"
+       @cp $< $@
+
+.PHONY: clean
+clean:
+       @echo " CLEAN"
+       @rm -rf $(foreach object,$(basename $(BUILD_OBJECTS)),$(object)*) $(basename $(BUILD_BINARY))*
+       @rm -rf $(OUTPUT_BINARY)
+
+.PHONY: distclean
+distclean: clean
+       @echo " DISTCLEAN"
+       @rm -rf $(BUILD)
+
+-include $(BUILD_DEPS)
diff --git a/pcm-analysis/pcm b/pcm-analysis/pcm
new file mode 120000 (symlink)
index 0000000..7c6d901
--- /dev/null
@@ -0,0 +1 @@
+../pcm
\ No newline at end of file
diff --git a/pcm-analysis/pcm-analysis.c b/pcm-analysis/pcm-analysis.c
new file mode 100644 (file)
index 0000000..d7d4699
--- /dev/null
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2016-2017 Paul Kocialkowski <contact@paulk.fr>
+ *
+ * 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/>.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <pcm/pcm.h>
+#include "pcm-analysis.h"
+
+struct pcm_ops_data pcm_ops_data = {
+       .address = 0,
+};
+
+struct pcm_ops pcm_ops = {
+       .asm_write = NULL,
+       .disasm_write = pcm_disasm_write,
+       .data = (void *) &pcm_ops_data,
+};
+
+int pcm_disasm_write(void *data, unsigned int value, char *mnemonic)
+{
+       struct pcm_ops_data *ops_data;
+
+       if (data == NULL)
+               return -1;
+
+       ops_data = (struct pcm_ops_data *) data;
+
+       if (mnemonic != NULL)
+               printf("[0x%04x] 0x%08x ; %s\n", ops_data->address, value, mnemonic);
+       else
+               printf("[0x%04x] 0x%08x\n", ops_data->address, value);
+
+       ops_data->address++;
+
+       return 0;
+}
+
+int firmware_values(char *file, unsigned int **values)
+{
+       struct stat stat_data;
+       size_t size;
+       int fd = -1;
+       int rc;
+
+       if (file == NULL || values == NULL)
+               return -1;
+
+       rc = stat(file, &stat_data);
+       if (rc < 0) {
+               fprintf(stderr, "Invalid file: %s\n", file);
+               goto error;
+       }
+
+       size = stat_data.st_size;
+       *values = malloc(size);
+
+       fd = open(file, O_RDONLY, 0644);
+       if (fd < 0) {
+               fprintf(stderr, "Unable to open file: %s\n", file);
+               goto error;
+       }
+
+       rc = read(fd, *values, size);
+       if (rc <= 0) {
+               fprintf(stderr, "Unable to read file: %s\n", file);
+               goto error;
+       }
+
+       rc = size / sizeof(unsigned int);
+       goto complete;
+
+error:
+       rc = -1;
+
+       if (*values != NULL)
+               free(*values);
+
+complete:
+       if (fd >= 0)
+               close(fd);
+
+       return rc;
+}
+
+int main(int argc, char *argv[])
+{
+       unsigned int *values = NULL;
+       unsigned int count;
+       int rc;
+       int i;
+
+       if (argc < 2 || argv[1] == NULL)
+               return 1;
+
+       rc = firmware_values(argv[1], &values);
+       if (rc < 0 || values == NULL)
+               goto error;
+
+       count = rc;
+
+       rc = pcm_ops_register(&pcm_ops);
+       if (rc < 0) {
+               fprintf(stderr, "Unable to set PCM OPS\n");
+               goto error;
+       }
+
+       i = 0;
+
+       while (count > 0) {
+               rc = pcm_op_disasm(&values[i], count);
+               if (rc < 0) {
+                       pcm_disasm_write(pcm_ops.data, values[i], NULL);
+                       rc = 0;
+               }
+
+               count -= 1 + rc;
+               i += 1 + rc;
+       }
+
+       rc = 0;
+       goto complete;
+
+error:
+       rc = 1;
+
+complete:
+       if (values != NULL)
+               free(values);
+
+       return rc;
+}
diff --git a/pcm-analysis/pcm-analysis.h b/pcm-analysis/pcm-analysis.h
new file mode 100644 (file)
index 0000000..d7efb46
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2016-2017 Paul Kocialkowski <contact@paulk.fr>
+ *
+ * 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/>.
+ */
+
+#ifndef _PCM_ANALYSIS_H_
+#define _PCM_ANALYSIS_H_
+
+/*
+ * Structures
+ */
+
+struct pcm_ops_data {
+       unsigned int address;
+};
+
+/*
+ * Functions
+ */
+
+int pcm_disasm_write(void *data, unsigned int value, char *mnemonic);
+
+#endif
diff --git a/pcm-asm/Makefile b/pcm-asm/Makefile
new file mode 100644 (file)
index 0000000..5a0025d
--- /dev/null
@@ -0,0 +1,83 @@
+# Copyright (C) 2016-2017 Paul Kocialkowski <contact@paulk.fr>
+#
+# 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/>.
+
+# Tools
+
+CC = gcc
+
+# Project
+
+NAME = pcm-asm
+
+# Directories
+
+BUILD = build
+OUTPUT = .
+
+# Sources
+
+SOURCES = pcm/pcm.c pcm-asm.c
+OBJECTS = $(SOURCES:.c=.o)
+DEPS = $(SOURCES:.c=.d)
+
+# Compiler
+
+INCLUDES = .
+
+CFLAGS = $(foreach include,$(INCLUDES),-I$(include))
+LDFLAGS =
+
+# Produced files
+
+BUILD_OBJECTS = $(addprefix $(BUILD)/,$(OBJECTS))
+BUILD_DEPS = $(addprefix $(BUILD)/,$(DEPS))
+BUILD_BINARY = $(BUILD)/$(NAME)
+BUILD_DIRS = $(sort $(dir $(BUILD_BINARY) $(BUILD_OBJECTS)))
+
+OUTPUT_BINARY = $(OUTPUT)/$(NAME)
+OUTPUT_DIRS = $(sort $(dir $(OUTPUT_BINARY)))
+
+all: $(OUTPUT_BINARY)
+
+$(BUILD_DIRS):
+       @mkdir -p $@
+
+$(BUILD_OBJECTS): $(BUILD)/%.o: %.c | $(BUILD_DIRS)
+       @echo " CC     $<"
+       @$(CC) $(CFLAGS) -MMD -MF $(BUILD)/$*.d -c $< -o $@
+
+$(BUILD_BINARY): $(BUILD_OBJECTS)
+       @echo " LINK   $@"
+       @$(CC) $(CFLAGS) -o $@ $(BUILD_OBJECTS) $(LDFLAGS)
+
+$(OUTPUT_DIRS):
+       @mkdir -p $@
+
+$(OUTPUT_BINARY): $(BUILD_BINARY) | $(OUTPUT_DIRS)
+       @echo " BINARY $@"
+       @cp $< $@
+
+.PHONY: clean
+clean:
+       @echo " CLEAN"
+       @rm -rf $(foreach object,$(basename $(BUILD_OBJECTS)),$(object)*) $(basename $(BUILD_BINARY))*
+       @rm -rf $(OUTPUT_BINARY)
+
+.PHONY: distclean
+distclean: clean
+       @echo " DISTCLEAN"
+       @rm -rf $(BUILD)
+
+-include $(BUILD_DEPS)
diff --git a/pcm-asm/pcm b/pcm-asm/pcm
new file mode 120000 (symlink)
index 0000000..7c6d901
--- /dev/null
@@ -0,0 +1 @@
+../pcm
\ No newline at end of file
diff --git a/pcm-asm/pcm-asm.c b/pcm-asm/pcm-asm.c
new file mode 100644 (file)
index 0000000..665f344
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2016-2017 Paul Kocialkowski <contact@paulk.fr>
+ *
+ * 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/>.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <pcm/pcm.h>
+#include "pcm-asm.h"
+
+struct pcm_ops pcm_ops = {
+       .asm_write = pcm_asm_write,
+       .disasm_write = NULL,
+       .data = NULL,
+};
+
+int pcm_asm_write(void *data, unsigned int value, char *mnemonic)
+{
+       binary_print(value);
+       printf("0x%08x ; %s\n", value, mnemonic);
+
+       return 0;
+}
+
+int main(int argc, char *argv[])
+{
+       unsigned int count;
+       unsigned int i;
+       int rc;
+
+       if (argc < 2)
+               return 1;
+
+       rc = pcm_ops_register(&pcm_ops);
+       if (rc < 0) {
+               fprintf(stderr, "Unable to set PCM OPS\n");
+               return 1;
+       }
+
+       count = argc - 1;
+       i = 1;
+
+       while (count > 0) {
+               rc = pcm_op_asm(&argv[i], count);
+               if (rc < 0) {
+                       fprintf(stderr, "Unknown or incomplete mnemonic: %s\n", argv[i]);
+                       return 1;
+               }
+
+               count -= 1 + rc;
+               i += 1 + rc;
+
+               if (count > 0)
+                       printf("\n");
+       }
+
+       return 0;
+}
diff --git a/pcm-asm/pcm-asm.h b/pcm-asm/pcm-asm.h
new file mode 100644 (file)
index 0000000..4e18291
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2016-2017 Paul Kocialkowski <contact@paulk.fr>
+ *
+ * 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/>.
+ */
+
+#ifndef _PCM_ASM_H_
+#define _PCM_ASM_H_
+
+/*
+ * Functions
+ */
+
+int pcm_asm_write(void *data, unsigned int value, char *mnemonic);
+
+#endif
diff --git a/pcm-disasm/Makefile b/pcm-disasm/Makefile
new file mode 100644 (file)
index 0000000..d4c89e4
--- /dev/null
@@ -0,0 +1,83 @@
+# Copyright (C) 2016-2017 Paul Kocialkowski <contact@paulk.fr>
+#
+# 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/>.
+
+# Tools
+
+CC = gcc
+
+# Project
+
+NAME = pcm-disasm
+
+# Directories
+
+BUILD = build
+OUTPUT = .
+
+# Sources
+
+SOURCES = pcm/pcm.c pcm-disasm.c
+OBJECTS = $(SOURCES:.c=.o)
+DEPS = $(SOURCES:.c=.d)
+
+# Compiler
+
+INCLUDES = .
+
+CFLAGS = $(foreach include,$(INCLUDES),-I$(include))
+LDFLAGS =
+
+# Produced files
+
+BUILD_OBJECTS = $(addprefix $(BUILD)/,$(OBJECTS))
+BUILD_DEPS = $(addprefix $(BUILD)/,$(DEPS))
+BUILD_BINARY = $(BUILD)/$(NAME)
+BUILD_DIRS = $(sort $(dir $(BUILD_BINARY) $(BUILD_OBJECTS)))
+
+OUTPUT_BINARY = $(OUTPUT)/$(NAME)
+OUTPUT_DIRS = $(sort $(dir $(OUTPUT_BINARY)))
+
+all: $(OUTPUT_BINARY)
+
+$(BUILD_DIRS):
+       @mkdir -p $@
+
+$(BUILD_OBJECTS): $(BUILD)/%.o: %.c | $(BUILD_DIRS)
+       @echo " CC     $<"
+       @$(CC) $(CFLAGS) -MMD -MF $(BUILD)/$*.d -c $< -o $@
+
+$(BUILD_BINARY): $(BUILD_OBJECTS)
+       @echo " LINK   $@"
+       @$(CC) $(CFLAGS) -o $@ $(BUILD_OBJECTS) $(LDFLAGS)
+
+$(OUTPUT_DIRS):
+       @mkdir -p $@
+
+$(OUTPUT_BINARY): $(BUILD_BINARY) | $(OUTPUT_DIRS)
+       @echo " BINARY $@"
+       @cp $< $@
+
+.PHONY: clean
+clean:
+       @echo " CLEAN"
+       @rm -rf $(foreach object,$(basename $(BUILD_OBJECTS)),$(object)*) $(basename $(BUILD_BINARY))*
+       @rm -rf $(OUTPUT_BINARY)
+
+.PHONY: distclean
+distclean: clean
+       @echo " DISTCLEAN"
+       @rm -rf $(BUILD)
+
+-include $(BUILD_DEPS)
diff --git a/pcm-disasm/pcm b/pcm-disasm/pcm
new file mode 120000 (symlink)
index 0000000..7c6d901
--- /dev/null
@@ -0,0 +1 @@
+../pcm
\ No newline at end of file
diff --git a/pcm-disasm/pcm-disasm.c b/pcm-disasm/pcm-disasm.c
new file mode 100644 (file)
index 0000000..2df3d98
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2016-2017 Paul Kocialkowski <contact@paulk.fr>
+ *
+ * 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/>.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <pcm/pcm.h>
+#include "pcm-disasm.h"
+
+struct pcm_ops pcm_ops = {
+       .asm_write = NULL,
+       .disasm_write = pcm_disasm_write,
+       .data = NULL,
+};
+
+int pcm_disasm_write(void *data, unsigned int value, char *mnemonic)
+{
+       binary_print(value);
+       printf("0x%08x ; %s\n", value, mnemonic);
+
+       return 0;
+}
+
+int main(int argc, char *argv[])
+{
+       unsigned int *values = NULL;
+       unsigned int count;
+       unsigned int i;
+       int rc;
+
+       if (argc < 2)
+               return 1;
+
+       rc = pcm_ops_register(&pcm_ops);
+       if (rc < 0) {
+               fprintf(stderr, "Unable to set PCM OPS\n");
+               goto error;
+       }
+
+       count = argc - 1;
+
+       values = malloc(count * sizeof(unsigned int));
+
+       for (i = 0; i < count; i++)
+               values[i] = strtol(argv[i + 1], NULL, 16);
+
+       i = 0;
+
+       while (count > 0) {
+               rc = pcm_op_disasm(&values[i], count);
+               if (rc < 0) {
+                       binary_print(values[i]);
+                       fprintf(stderr, "Unknown op code: 0x%08x\n", values[i]);
+                       rc = 0;
+               }
+
+               count -= 1 + rc;
+               i += 1 + rc;
+
+               if (count > 0)
+                       printf("\n");
+       }
+
+       rc = 0;
+       goto complete;
+
+error:
+       rc = 1;
+
+complete:
+       if (values != NULL)
+               free(values);
+
+       return rc;
+}
diff --git a/pcm-disasm/pcm-disasm.h b/pcm-disasm/pcm-disasm.h
new file mode 100644 (file)
index 0000000..b36f8dd
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2016-2017 Paul Kocialkowski <contact@paulk.fr>
+ *
+ * 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/>.
+ */
+
+#ifndef _PCM_DISASM_H_
+#define _PCM_DISASM_H_
+
+/*
+ * Functions
+ */
+
+int pcm_disasm_write(void *data, unsigned int value, char *mnemonic);
+
+#endif
diff --git a/pcm/pcm.c b/pcm/pcm.c
new file mode 100644 (file)
index 0000000..35a6faa
--- /dev/null
+++ b/pcm/pcm.c
@@ -0,0 +1,479 @@
+/*
+ * Copyright (C) 2016-2017 Paul Kocialkowski <contact@paulk.fr>
+ *
+ * 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/>.
+ */
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <string.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <pcm/pcm.h>
+
+/*
+ * Utils
+ */
+
+int binary_print(unsigned int value)
+{
+       unsigned int count = sizeof(value) * 8;
+       unsigned int index;
+
+       for (index = 0; index < count; index++) {
+               printf("%c", value & (1 << (count - 1 - index)) ? '1' : '0');
+
+               if ((count - 1 - index) % 4 == 0)
+                       printf(" ");
+       }
+
+       printf("(0x%x)\n", value);
+}
+
+/*
+ * PCM OPS
+ */
+
+static struct pcm_ops *pcm_ops = NULL;
+
+int pcm_ops_register(struct pcm_ops *ops)
+{
+       if (pcm_ops != NULL)
+               return -1;
+
+       pcm_ops = ops;
+
+       return 0;
+}
+
+int pcm_ops_asm_write(unsigned int value, char *mnemonic)
+{
+       if (pcm_ops == NULL || pcm_ops->asm_write == NULL)
+               return -1;
+
+       return pcm_ops->asm_write(pcm_ops->data, value, mnemonic);
+}
+
+int pcm_ops_disasm_write(unsigned int value, char *mnemonic)
+{
+       if (pcm_ops == NULL || pcm_ops->disasm_write == NULL)
+               return -1;
+
+       return pcm_ops->disasm_write(pcm_ops->data, value, mnemonic);
+}
+
+static int pcm_ops_asm_format(unsigned int value, const char *format, ...)
+{
+       char *mnemonic = NULL;
+       va_list ap;
+       int rc;
+
+       va_start(ap, format);
+
+       rc = vasprintf(&mnemonic, format, ap);
+       if (rc < 0 || mnemonic == NULL)
+               goto error;
+
+       rc = pcm_ops_asm_write(value, mnemonic);
+       if (rc < 0)
+               goto error;
+
+       rc = 0;
+       goto complete;
+
+error:
+       rc = -1;
+
+complete:
+       va_end(ap);
+
+       return rc;
+}
+
+static int pcm_ops_disasm_format(unsigned int value, const char *format, ...)
+{
+       char *mnemonic = NULL;
+       va_list ap;
+       int rc;
+
+       va_start(ap, format);
+
+       rc = vasprintf(&mnemonic, format, ap);
+       if (rc < 0 || mnemonic == NULL)
+               goto error;
+
+       rc = pcm_ops_disasm_write(value, mnemonic);
+       if (rc < 0)
+               goto error;
+
+       rc = 0;
+       goto complete;
+
+error:
+       rc = -1;
+
+complete:
+       va_end(ap);
+
+       return rc;
+}
+
+/*
+ * PCM OP codes
+ */
+
+static int pcm_op_padding_asm(char *arguments[], unsigned int count, unsigned int padding)
+{
+       unsigned int value;
+       unsigned int i;
+       int rc;
+
+       if (arguments == NULL)
+               return -1;
+
+       i = 0;
+
+       /* Grab padding from following nops first. */
+       while (arguments[i] != NULL && count > 0 && padding > 0) {
+               if (strncmp(arguments[i], "nop", 3) != 0)
+                       break;
+
+               rc = pcm_ops_asm_write(PCM_OP_NOP_VALUE, "nop");
+               if (rc < 0)
+                       return -1;
+
+               padding--;
+               i++;
+       }
+
+       /* Generate leftover padding. */
+       while (padding-- > 0) {
+               rc = pcm_ops_asm_write(PCM_OP_NOP_VALUE, "nop");
+               if (rc < 0)
+                       return -1;
+       }
+
+       return i;
+}
+
+static int pcm_op_immediate_asm(char *arguments[], unsigned int count)
+{
+       unsigned int value;
+       unsigned int i;
+       int rc;
+
+       if (arguments == NULL || count < 1)
+               return -1;
+
+       for (i = 0; i < count && arguments[i] != NULL ; i++) {
+               value = strtol(arguments[i], NULL, 16);
+
+               rc = pcm_ops_asm_write(value, "immediate");
+               if (rc < 0)
+                       return -1;
+       }
+}
+
+static int pcm_op_immediate_disasm(unsigned int *arguments, unsigned int count)
+{
+       unsigned int i;
+       int rc;
+
+       if (arguments == NULL || count < 1)
+               return -1;
+
+       i = 0;
+
+       while (count-- > 0) {
+               rc = pcm_ops_disasm_write(arguments[i], "immediate");
+               if (rc < 0)
+                       return -1;
+
+               i++;
+       }
+}
+
+static int pcm_op_dummy_asm(struct pcm_op_desc *desc, char *arguments[], unsigned int count)
+{
+       int rc;
+
+       if (desc == NULL || arguments == NULL || count < 1)
+               return -1;
+
+       rc = pcm_ops_asm_write(desc->value, arguments[0]);
+       if (rc < 0)
+               return -1;
+
+       return 0;
+}
+
+static int pcm_op_dummy_disasm(struct pcm_op_desc *desc, unsigned int *arguments, unsigned int count)
+{
+       int rc;
+
+       if (desc == NULL || arguments == NULL || count < 1)
+               return -1;
+
+       rc = pcm_ops_disasm_format(arguments[0], "%s", desc->mnemonic);
+       if (rc < 0)
+               return -1;
+
+       return 0;
+}
+
+static int pcm_op_loadi_asm(struct pcm_op_desc *desc, char *arguments[], unsigned int count)
+{
+       unsigned int value;
+       unsigned int reg;
+       char *string;
+       int rc;
+
+       if (desc == NULL || arguments == NULL || arguments[1] == NULL || count < 3)
+               return -1;
+
+       value = desc->value;
+
+       string = arguments[1];
+       if (string[0] == 'r')
+               string = &string[1];
+
+       reg = atoi(string);
+       value |= (reg & PCM_OP_LOADI_REG_MASK) << PCM_OP_LOADI_REG_SHIFT;
+
+       rc = pcm_ops_asm_format(value, "%s r%d", arguments[0], reg);
+       if (rc < 0)
+               return -1;
+
+       rc = pcm_op_immediate_asm(&arguments[2], 1);
+       if (rc < 0)
+               return -1;
+
+       return 2;
+}
+
+static int pcm_op_loadi_disasm(struct pcm_op_desc *desc, unsigned int *arguments, unsigned int count)
+{
+       unsigned int value;
+       unsigned int reg;
+       int rc;
+
+       if (desc == NULL || arguments == NULL || count < 1)
+               return -1;
+
+       value = arguments[0];
+       reg = (value >> PCM_OP_LOADI_REG_SHIFT) & PCM_OP_LOADI_REG_MASK;
+
+       rc = pcm_ops_disasm_format(value, "%s r%d", desc->mnemonic, reg);
+       if (rc < 0)
+               return -1;
+
+       rc = pcm_op_immediate_disasm(&arguments[1], 1);
+       if (rc < 0)
+               return -1;
+
+       return 1;
+}
+
+static int pcm_op_jump_call_asm(struct pcm_op_desc *desc, char *arguments[], unsigned int count)
+{
+       unsigned int value;
+       unsigned int address;
+       int rc;
+
+       if (desc == NULL || arguments == NULL || arguments[1] == NULL || count < 2)
+               return -1;
+
+       value = desc->value;
+       address = strtol(arguments[1], NULL, 16);
+
+       value |= (address & PCM_OP_JUMP_ADDRESS_MASK) << PCM_OP_JUMP_ADDRESS_SHIFT;
+
+       rc = pcm_ops_asm_format(value, "%s 0x%04x", arguments[0], address);
+       if (rc < 0)
+               return -1;
+
+       return 1;
+}
+
+static int pcm_op_jump_call_disasm(struct pcm_op_desc *desc, unsigned int *arguments, unsigned int count)
+{
+       unsigned int value;
+       unsigned int address;
+       int rc;
+
+       if (desc == NULL || arguments == NULL || count < 1)
+               return -1;
+
+       value = arguments[0];
+       address = (value >> PCM_OP_JUMP_ADDRESS_SHIFT) & PCM_OP_JUMP_ADDRESS_MASK;
+
+       rc = pcm_ops_disasm_format(arguments[0], "%s 0x%04x", desc->mnemonic, address);
+       if (rc < 0)
+               return -1;
+
+       return 0;
+}
+
+static struct pcm_op_desc pcm_op_descs[] = {
+       {
+               .mnemonic = "nop",
+               .mask = PCM_OP_NOP_MASK,
+               .value = PCM_OP_NOP_VALUE,
+               .padding = 0,
+               .asm_callback = pcm_op_dummy_asm,
+               .disasm_callback = pcm_op_dummy_disasm,
+       },
+       {
+               .mnemonic = "loadi",
+               .mask = PCM_OP_LOADI_MASK,
+               .value = PCM_OP_LOADI_VALUE,
+               .padding = 0,
+               .asm_callback = pcm_op_loadi_asm,
+               .disasm_callback = pcm_op_loadi_disasm,
+       },
+       {
+               .mnemonic = "jump",
+               .mask = PCM_OP_JUMP_MASK,
+               .value = PCM_OP_JUMP_VALUE,
+               .padding = 1,
+               .asm_callback = pcm_op_jump_call_asm,
+               .disasm_callback = pcm_op_jump_call_disasm,
+       },
+       {
+               .mnemonic = "call",
+               .mask = PCM_OP_CALL_MASK,
+               .value = PCM_OP_CALL_VALUE,
+               .padding = 1,
+               .asm_callback = pcm_op_jump_call_asm,
+               .disasm_callback = pcm_op_jump_call_disasm,
+       },
+       {
+               .mnemonic = "ret",
+               .mask = PCM_OP_RET_MASK,
+               .value = PCM_OP_RET_VALUE,
+               .padding = 1,
+               .asm_callback = pcm_op_dummy_asm,
+               .disasm_callback = pcm_op_dummy_disasm,
+       },
+};
+
+static size_t pcm_op_descs_count = sizeof(pcm_op_descs) / sizeof(struct pcm_op_desc);
+
+static struct pcm_op_desc *pcm_op_desc_find_mnemonic(char *mnemonic)
+{
+       unsigned int i;
+       size_t length;
+
+       for (i = 0; i < pcm_op_descs_count; i++) {
+               length = strlen(pcm_op_descs[i].mnemonic);
+
+               /* Compare mnemonic as a prefix, not as a full string. */
+               if (strncmp(pcm_op_descs[i].mnemonic, mnemonic, length) == 0)
+                       return &pcm_op_descs[i];
+       }
+
+       return NULL;
+}
+
+static struct pcm_op_desc *pcm_op_desc_find_value(unsigned int value)
+{
+       unsigned int i;
+       size_t length;
+
+       for (i = 0; i < pcm_op_descs_count; i++) {
+               if ((value & pcm_op_descs[i].mask) == pcm_op_descs[i].value)
+                       return &pcm_op_descs[i];
+       }
+
+       return NULL;
+}
+
+int pcm_op_asm(char *arguments[], unsigned int count)
+{
+       struct pcm_op_desc *desc;
+       char *mnemonic;
+       int increment;
+       int rc;
+
+       if (arguments == NULL || count < 1)
+               return -1;
+
+       mnemonic = arguments[0];
+       if (mnemonic == NULL)
+               return -1;
+
+       desc = pcm_op_desc_find_mnemonic(mnemonic);
+       if (desc == NULL)
+               return -1;
+
+       if (desc->asm_callback == NULL)
+               return -1;
+
+       rc = desc->asm_callback(desc, arguments, count);
+       if (rc < 0)
+               return -1;
+
+       increment = rc;
+
+       if (desc->padding > 0) {
+               arguments = &arguments[increment + 1];
+
+               if ((increment + 1) < count)
+                       count -= increment + 1;
+               else
+                       count = 0;
+
+               rc = pcm_op_padding_asm(arguments, count, desc->padding);
+               if (rc < 0)
+                       return -1;
+
+               increment += rc;
+       }
+
+       return increment;
+}
+
+int pcm_op_disasm(unsigned int *arguments, unsigned int count)
+{
+       struct pcm_op_desc *desc;
+       unsigned int value;
+       int increment;
+       int rc;
+
+       if (arguments == NULL || count < 1)
+               return -1;
+
+       value = arguments[0];
+
+       desc = pcm_op_desc_find_value(value);
+       if (desc == NULL)
+               return -1;
+
+       if (desc->disasm_callback == NULL)
+               return -1;
+
+       rc = desc->disasm_callback(desc, arguments, count);
+       if (rc < 0)
+               return -1;
+
+       increment = rc;
+
+       return increment;
+}
diff --git a/pcm/pcm.h b/pcm/pcm.h
new file mode 100644 (file)
index 0000000..d7a4cad
--- /dev/null
+++ b/pcm/pcm.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2016-2017 Paul Kocialkowski <contact@paulk.fr>
+ *
+ * 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/>.
+ */
+
+#ifndef _PCM_H_
+#define _PCM_H_
+
+/*
+ * Values
+ */
+
+/* PCM OP codes */
+
+#define PCM_OP_NOP_MASK                                                0xffffffff
+#define PCM_OP_NOP_VALUE                                       0x17c07c1f
+
+#define PCM_OP_LOADI_MASK                                      0x180000ff
+#define PCM_OP_LOADI_VALUE                                     0x1800001f
+#define PCM_OP_LOADI_REG_SHIFT                                 22
+#define PCM_OP_LOADI_REG_MASK                                  0x0f
+
+#define PCM_OP_JUMP_MASK                                       0xf0000000
+#define PCM_OP_JUMP_VALUE                                      0xd0000000
+#define PCM_OP_JUMP_ADDRESS_SHIFT                              5
+#define PCM_OP_JUMP_ADDRESS_MASK                               0x3ff
+
+#define PCM_OP_CALL_MASK                                       0xff000000
+#define PCM_OP_CALL_VALUE                                      0xc2000000
+#define PCM_OP_CALL_ADDRESS_SHIFT                              5
+#define PCM_OP_CALL_ADDRESS_MASK                               0x3ff
+
+#define PCM_OP_RET_MASK                                                0xffffffff
+#define PCM_OP_RET_VALUE                                       0xf0000000
+
+/*
+ * Structures
+ */
+
+/* PCM OPS */
+
+struct pcm_ops {
+       int (*asm_write)(void *data, unsigned int value, char *mnemonic);
+       int (*disasm_write)(void *data, unsigned int value, char *mnemonic);
+       void *data;
+};
+
+/* PCM OP codes */
+
+struct pcm_op_desc {
+       const char *mnemonic;
+       unsigned int mask;
+       unsigned int value;
+       unsigned int padding;
+       int (*asm_callback)(struct pcm_op_desc *desc, char *arguments[], unsigned int count);
+       int (*disasm_callback)(struct pcm_op_desc *desc, unsigned int *arguments, unsigned int count);
+};
+
+/*
+ * Functions
+ */
+
+/* Utils */
+
+int binary_print(unsigned int value);
+
+/* PCM OPS */
+
+int pcm_ops_register(struct pcm_ops *ops);
+int pcm_ops_asm_write(unsigned int value, char *mnemonic);
+int pcm_ops_disasm_write(unsigned int value, char *mnemonic);
+
+/* PCM OP codes */
+
+int pcm_op_asm(char *arguments[], unsigned int count);
+int pcm_op_disasm(unsigned int *arguments, unsigned int count);
+
+#endif