summaryrefslogtreecommitdiff
path: root/qcom/rmtfs
diff options
context:
space:
mode:
authorAmit Pundir <amit.pundir@linaro.org>2020-02-07 22:26:08 +0530
committerJohn Stultz <john.stultz@linaro.org>2020-03-17 04:10:56 +0000
commitd477f821bb485acbb232cb896e06b99ed98ba25c (patch)
tree2cb8babdc907b5eb23c85cedb70742e360beeba5 /qcom/rmtfs
parent4789995b56420cfbd17b71dd242b54d36fca6361 (diff)
db845c: qcom: Add userspace tools to talk to dsp and modem
Add Qcom userspace tools and their respective sepolicy rules. Userspace tools are downloaded from following github: To trigger loading of wlan firmware on SDM845 git clone https://github.com/andersson/pd-mapper Userspace reference for net/qrtr in the Linux kernel git clone https://github.com/andersson/qrtr Qualcomm Remote Filesystem Service Implementation git clone https://github.com/andersson/rmtfs Trivial File Transfer Protocol server over AF_QIPCRTR git clone https://github.com/andersson/tqftpserv Change-Id: Ic466af6fef010a9b71c90e38205f49a876b001e2 Signed-off-by: Amit Pundir <amit.pundir@linaro.org> Signed-off-by: John Stultz <john.stultz@linaro.org>
Diffstat (limited to 'qcom/rmtfs')
-rw-r--r--qcom/rmtfs/Android.bp13
-rw-r--r--qcom/rmtfs/LICENSE27
-rw-r--r--qcom/rmtfs/Makefile27
-rw-r--r--qcom/rmtfs/qmi_rmtfs.c256
-rw-r--r--qcom/rmtfs/qmi_rmtfs.h101
-rw-r--r--qcom/rmtfs/qmi_rmtfs.qmi77
-rw-r--r--qcom/rmtfs/qmi_tlv.c233
-rw-r--r--qcom/rmtfs/rmtfs.c564
-rw-r--r--qcom/rmtfs/rmtfs.h42
-rw-r--r--qcom/rmtfs/rmtfs.service.in12
-rw-r--r--qcom/rmtfs/rproc.c137
-rw-r--r--qcom/rmtfs/sharedmem.c460
-rw-r--r--qcom/rmtfs/storage.c291
-rw-r--r--qcom/rmtfs/util.c48
-rw-r--r--qcom/rmtfs/util.h9
15 files changed, 2297 insertions, 0 deletions
diff --git a/qcom/rmtfs/Android.bp b/qcom/rmtfs/Android.bp
new file mode 100644
index 0000000..7865a99
--- /dev/null
+++ b/qcom/rmtfs/Android.bp
@@ -0,0 +1,13 @@
+cc_binary {
+ name: "rmtfs",
+ vendor: true,
+ srcs: [
+ "qmi_rmtfs.c",
+ "rmtfs.c",
+ "rproc.c",
+ "sharedmem.c",
+ "storage.c",
+ "util.c",
+ ],
+ shared_libs: ["libqrtr"],
+}
diff --git a/qcom/rmtfs/LICENSE b/qcom/rmtfs/LICENSE
new file mode 100644
index 0000000..42c13e9
--- /dev/null
+++ b/qcom/rmtfs/LICENSE
@@ -0,0 +1,27 @@
+Copyright (c) 2016, Linaro Ltd
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+3. Neither the name of the copyright holder nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/qcom/rmtfs/Makefile b/qcom/rmtfs/Makefile
new file mode 100644
index 0000000..54ae35f
--- /dev/null
+++ b/qcom/rmtfs/Makefile
@@ -0,0 +1,27 @@
+OUT := rmtfs
+
+CFLAGS += -Wall -g -O2
+LDFLAGS += -lqrtr -ludev -lpthread
+prefix = /usr/local
+bindir := $(prefix)/bin
+servicedir := $(prefix)/lib/systemd/system
+
+SRCS := qmi_rmtfs.c rmtfs.c rproc.c sharedmem.c storage.c util.c
+OBJS := $(SRCS:.c=.o)
+
+$(OUT): $(OBJS)
+ $(CC) -o $@ $^ $(LDFLAGS)
+
+%.c: %.qmi
+ qmic -k < $<
+
+rmtfs.service: rmtfs.service.in
+ @sed 's+RMTFS_PATH+$(bindir)+g' $< > $@
+
+install: $(OUT) rmtfs.service
+ @install -D -m 755 $(OUT) $(DESTDIR)$(prefix)/bin/$(OUT)
+ @install -D -m 644 rmtfs.service $(DESTDIR)$(servicedir)/rmtfs.service
+
+clean:
+ rm -f $(OUT) $(OBJS) rmtfs.service
+
diff --git a/qcom/rmtfs/qmi_rmtfs.c b/qcom/rmtfs/qmi_rmtfs.c
new file mode 100644
index 0000000..bda2b78
--- /dev/null
+++ b/qcom/rmtfs/qmi_rmtfs.c
@@ -0,0 +1,256 @@
+#include <errno.h>
+#include <string.h>
+#include "qmi_rmtfs.h"
+
+struct qmi_elem_info rmtfs_qmi_result_ei[] = {
+ {
+ .data_type = QMI_UNSIGNED_2_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint16_t),
+ .offset = offsetof(struct rmtfs_qmi_result, result),
+ },
+ {
+ .data_type = QMI_UNSIGNED_2_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint16_t),
+ .offset = offsetof(struct rmtfs_qmi_result, error),
+ },
+ {}
+};
+
+struct qmi_elem_info rmtfs_iovec_entry_ei[] = {
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint32_t),
+ .offset = offsetof(struct rmtfs_iovec_entry, sector_addr),
+ },
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint32_t),
+ .offset = offsetof(struct rmtfs_iovec_entry, phys_offset),
+ },
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint32_t),
+ .offset = offsetof(struct rmtfs_iovec_entry, num_sector),
+ },
+ {}
+};
+
+struct qmi_elem_info rmtfs_open_req_ei[] = {
+ {
+ .data_type = QMI_STRING,
+ .elem_len = 256,
+ .elem_size = sizeof(char),
+ .array_type = VAR_LEN_ARRAY,
+ .tlv_type = 1,
+ .offset = offsetof(struct rmtfs_open_req, path)
+ },
+ {}
+};
+
+struct qmi_elem_info rmtfs_open_resp_ei[] = {
+ {
+ .data_type = QMI_STRUCT,
+ .elem_len = 1,
+ .elem_size = sizeof(struct rmtfs_qmi_result),
+ .tlv_type = 2,
+ .offset = offsetof(struct rmtfs_open_resp, result),
+ .ei_array = rmtfs_qmi_result_ei,
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(bool),
+ .tlv_type = 16,
+ .offset = offsetof(struct rmtfs_open_resp, caller_id_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint32_t),
+ .tlv_type = 16,
+ .offset = offsetof(struct rmtfs_open_resp, caller_id),
+ },
+ {}
+};
+
+struct qmi_elem_info rmtfs_close_req_ei[] = {
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint32_t),
+ .tlv_type = 1,
+ .offset = offsetof(struct rmtfs_close_req, caller_id),
+ },
+ {}
+};
+
+struct qmi_elem_info rmtfs_close_resp_ei[] = {
+ {
+ .data_type = QMI_STRUCT,
+ .elem_len = 1,
+ .elem_size = sizeof(struct rmtfs_qmi_result),
+ .tlv_type = 2,
+ .offset = offsetof(struct rmtfs_close_resp, result),
+ .ei_array = rmtfs_qmi_result_ei,
+ },
+ {}
+};
+
+struct qmi_elem_info rmtfs_iovec_req_ei[] = {
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint32_t),
+ .tlv_type = 1,
+ .offset = offsetof(struct rmtfs_iovec_req, caller_id),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .tlv_type = 2,
+ .offset = offsetof(struct rmtfs_iovec_req, direction),
+ },
+ {
+ .data_type = QMI_DATA_LEN,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .tlv_type = 3,
+ .offset = offsetof(struct rmtfs_iovec_req, iovec_len),
+ },
+ {
+ .data_type = QMI_STRUCT,
+ .elem_len = 255,
+ .elem_size = sizeof(struct rmtfs_iovec_entry),
+ .array_type = VAR_LEN_ARRAY,
+ .tlv_type = 3,
+ .offset = offsetof(struct rmtfs_iovec_req, iovec),
+ .ei_array = rmtfs_iovec_entry_ei,
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .tlv_type = 4,
+ .offset = offsetof(struct rmtfs_iovec_req, is_force_sync),
+ },
+ {}
+};
+
+struct qmi_elem_info rmtfs_iovec_resp_ei[] = {
+ {
+ .data_type = QMI_STRUCT,
+ .elem_len = 1,
+ .elem_size = sizeof(struct rmtfs_qmi_result),
+ .tlv_type = 2,
+ .offset = offsetof(struct rmtfs_iovec_resp, result),
+ .ei_array = rmtfs_qmi_result_ei,
+ },
+ {}
+};
+
+struct qmi_elem_info rmtfs_alloc_buf_req_ei[] = {
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint32_t),
+ .tlv_type = 1,
+ .offset = offsetof(struct rmtfs_alloc_buf_req, caller_id),
+ },
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint32_t),
+ .tlv_type = 2,
+ .offset = offsetof(struct rmtfs_alloc_buf_req, buff_size),
+ },
+ {}
+};
+
+struct qmi_elem_info rmtfs_alloc_buf_resp_ei[] = {
+ {
+ .data_type = QMI_STRUCT,
+ .elem_len = 1,
+ .elem_size = sizeof(struct rmtfs_qmi_result),
+ .tlv_type = 2,
+ .offset = offsetof(struct rmtfs_alloc_buf_resp, result),
+ .ei_array = rmtfs_qmi_result_ei,
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(bool),
+ .tlv_type = 16,
+ .offset = offsetof(struct rmtfs_alloc_buf_resp, buff_address_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_8_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint64_t),
+ .tlv_type = 16,
+ .offset = offsetof(struct rmtfs_alloc_buf_resp, buff_address),
+ },
+ {}
+};
+
+struct qmi_elem_info rmtfs_dev_error_req_ei[] = {
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint32_t),
+ .tlv_type = 1,
+ .offset = offsetof(struct rmtfs_dev_error_req, caller_id),
+ },
+ {}
+};
+
+struct qmi_elem_info rmtfs_dev_error_resp_ei[] = {
+ {
+ .data_type = QMI_STRUCT,
+ .elem_len = 1,
+ .elem_size = sizeof(struct rmtfs_qmi_result),
+ .tlv_type = 2,
+ .offset = offsetof(struct rmtfs_dev_error_resp, result),
+ .ei_array = rmtfs_qmi_result_ei,
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(bool),
+ .tlv_type = 16,
+ .offset = offsetof(struct rmtfs_dev_error_resp, status_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .tlv_type = 16,
+ .offset = offsetof(struct rmtfs_dev_error_resp, status),
+ },
+ {}
+};
+
+struct qmi_elem_info rmtfs_force_sync_ei[] = {
+ {
+ .data_type = QMI_DATA_LEN,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .tlv_type = 1,
+ .offset = offsetof(struct rmtfs_force_sync, caller_id_len),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 10,
+ .elem_size = sizeof(uint32_t),
+ .array_type = VAR_LEN_ARRAY,
+ .tlv_type = 1,
+ .offset = offsetof(struct rmtfs_force_sync, caller_id),
+ },
+ {}
+};
+
diff --git a/qcom/rmtfs/qmi_rmtfs.h b/qcom/rmtfs/qmi_rmtfs.h
new file mode 100644
index 0000000..03cbc8f
--- /dev/null
+++ b/qcom/rmtfs/qmi_rmtfs.h
@@ -0,0 +1,101 @@
+#ifndef __QMI_RMTFS_H__
+#define __QMI_RMTFS_H__
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include "libqrtr.h"
+
+#define QMI_RMTFS_RESULT_SUCCESS 0
+#define QMI_RMTFS_RESULT_FAILURE 1
+#define QMI_RMTFS_ERR_NONE 0
+#define QMI_RMTFS_ERR_INTERNAL 1
+#define QMI_RMTFS_ERR_MALFORMED_MSG 2
+#define QMI_RMTFS_OPEN 1
+#define QMI_RMTFS_CLOSE 2
+#define QMI_RMTFS_RW_IOVEC 3
+#define QMI_RMTFS_ALLOC_BUFF 4
+#define QMI_RMTFS_GET_DEV_ERROR 5
+#define QMI_RMTFS_FORCE_SYNC_IND 6
+
+struct rmtfs_qmi_result {
+ uint16_t result;
+ uint16_t error;
+};
+
+struct rmtfs_iovec_entry {
+ uint32_t sector_addr;
+ uint32_t phys_offset;
+ uint32_t num_sector;
+};
+
+struct rmtfs_open_req {
+ uint32_t path_len;
+ char path[256];
+};
+
+struct rmtfs_open_resp {
+ struct rmtfs_qmi_result result;
+ bool caller_id_valid;
+ uint32_t caller_id;
+};
+
+struct rmtfs_close_req {
+ uint32_t caller_id;
+};
+
+struct rmtfs_close_resp {
+ struct rmtfs_qmi_result result;
+};
+
+struct rmtfs_iovec_req {
+ uint32_t caller_id;
+ uint8_t direction;
+ size_t iovec_len;
+ struct rmtfs_iovec_entry iovec[255];
+ uint8_t is_force_sync;
+};
+
+struct rmtfs_iovec_resp {
+ struct rmtfs_qmi_result result;
+};
+
+struct rmtfs_alloc_buf_req {
+ uint32_t caller_id;
+ uint32_t buff_size;
+};
+
+struct rmtfs_alloc_buf_resp {
+ struct rmtfs_qmi_result result;
+ bool buff_address_valid;
+ uint64_t buff_address;
+};
+
+struct rmtfs_dev_error_req {
+ uint32_t caller_id;
+};
+
+struct rmtfs_dev_error_resp {
+ struct rmtfs_qmi_result result;
+ bool status_valid;
+ uint8_t status;
+};
+
+struct rmtfs_force_sync {
+ size_t caller_id_len;
+ uint32_t caller_id[10];
+};
+
+extern struct qmi_elem_info rmtfs_open_req_ei[];
+extern struct qmi_elem_info rmtfs_open_resp_ei[];
+extern struct qmi_elem_info rmtfs_close_req_ei[];
+extern struct qmi_elem_info rmtfs_close_resp_ei[];
+extern struct qmi_elem_info rmtfs_iovec_req_ei[];
+extern struct qmi_elem_info rmtfs_iovec_resp_ei[];
+extern struct qmi_elem_info rmtfs_alloc_buf_req_ei[];
+extern struct qmi_elem_info rmtfs_alloc_buf_resp_ei[];
+extern struct qmi_elem_info rmtfs_dev_error_req_ei[];
+extern struct qmi_elem_info rmtfs_dev_error_resp_ei[];
+extern struct qmi_elem_info rmtfs_force_sync_ei[];
+
+#endif
diff --git a/qcom/rmtfs/qmi_rmtfs.qmi b/qcom/rmtfs/qmi_rmtfs.qmi
new file mode 100644
index 0000000..ca350d7
--- /dev/null
+++ b/qcom/rmtfs/qmi_rmtfs.qmi
@@ -0,0 +1,77 @@
+package rmtfs;
+
+const QMI_RMTFS_RESULT_SUCCESS = 0;
+const QMI_RMTFS_RESULT_FAILURE = 1;
+
+const QMI_RMTFS_ERR_NONE = 0;
+const QMI_RMTFS_ERR_INTERNAL = 1;
+const QMI_RMTFS_ERR_MALFORMED_MSG = 2;
+
+const QMI_RMTFS_OPEN = 1;
+const QMI_RMTFS_CLOSE = 2;
+const QMI_RMTFS_RW_IOVEC = 3;
+const QMI_RMTFS_ALLOC_BUFF = 4;
+const QMI_RMTFS_GET_DEV_ERROR = 5;
+const QMI_RMTFS_FORCE_SYNC_IND = 6;
+
+struct qmi_result {
+ u16 result;
+ u16 error;
+};
+
+struct iovec_entry {
+ u32 sector_addr;
+ u32 phys_offset;
+ u32 num_sector;
+};
+
+request open_req {
+ required string path = 1;
+} = 1;
+
+response open_resp {
+ required qmi_result result = 2;
+ optional u32 caller_id = 0x10;
+} = 1;
+
+request close_req {
+ required u32 caller_id = 1;
+} = 2;
+
+response close_resp {
+ required qmi_result result = 2;
+} = 2;
+
+request iovec_req {
+ required u32 caller_id = 1;
+ required u8 direction = 2;
+ required iovec_entry iovec(255) = 3;
+ required u8 is_force_sync = 4;
+} = 3;
+
+response iovec_resp {
+ required qmi_result result = 2;
+} = 3;
+
+request alloc_buf_req {
+ required u32 caller_id = 1;
+ required u32 buff_size = 2;
+} = 4;
+
+response alloc_buf_resp {
+ required qmi_result result = 2;
+ optional u64 buff_address = 0x10;
+} = 4;
+
+request dev_error_req {
+ required u32 caller_id = 1;
+} = 5;
+
+response dev_error_resp {
+ required qmi_result result = 2;
+ optional u8 status = 0x10;
+} = 5;
+
+indication force_sync {
+ required u32 caller_id(10) = 1;
+} = 6;
diff --git a/qcom/rmtfs/qmi_tlv.c b/qcom/rmtfs/qmi_tlv.c
new file mode 100644
index 0000000..c6d8207
--- /dev/null
+++ b/qcom/rmtfs/qmi_tlv.c
@@ -0,0 +1,233 @@
+#include <errno.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdbool.h>
+
+#include "qmi_rmtfs.h"
+
+struct qmi_packet {
+ uint8_t flags;
+ uint16_t txn_id;
+ uint16_t msg_id;
+ uint16_t msg_len;
+ uint8_t data[];
+} __attribute__((__packed__));
+
+struct qmi_tlv {
+ void *allocated;
+ void *buf;
+ size_t size;
+ int error;
+};
+
+struct qmi_tlv_item {
+ uint8_t key;
+ uint16_t len;
+ uint8_t data[];
+} __attribute__((__packed__));
+
+struct qmi_tlv *qmi_tlv_init(unsigned txn, unsigned msg_id, unsigned msg_type)
+{
+ struct qmi_packet *pkt;
+ struct qmi_tlv *tlv;
+
+ tlv = malloc(sizeof(struct qmi_tlv));
+ memset(tlv, 0, sizeof(struct qmi_tlv));
+
+ tlv->size = sizeof(struct qmi_packet);
+ tlv->allocated = malloc(tlv->size);
+ tlv->buf = tlv->allocated;
+
+ pkt = tlv->buf;
+ pkt->flags = msg_type;
+ pkt->txn_id = txn;
+ pkt->msg_id = msg_id;
+ pkt->msg_len = 0;
+
+ return tlv;
+}
+
+struct qmi_tlv *qmi_tlv_decode(void *buf, size_t len, unsigned *txn, unsigned msg_type)
+{
+ struct qmi_packet *pkt = buf;
+ struct qmi_tlv *tlv;
+
+ if (pkt->flags != msg_type)
+ return NULL;
+
+ tlv = malloc(sizeof(struct qmi_tlv));
+ memset(tlv, 0, sizeof(struct qmi_tlv));
+
+ tlv->buf = buf;
+ tlv->size = len;
+
+ if (txn)
+ *txn = pkt->txn_id;
+
+ return tlv;
+}
+
+void *qmi_tlv_encode(struct qmi_tlv *tlv, size_t *len)
+{
+
+ struct qmi_packet *pkt;
+
+ if (!tlv || tlv->error)
+ return NULL;
+
+ pkt = tlv->buf;
+ pkt->msg_len = tlv->size - sizeof(struct qmi_packet);
+
+ *len = tlv->size;
+ return tlv->buf;
+}
+
+void qmi_tlv_free(struct qmi_tlv *tlv)
+{
+ free(tlv->allocated);
+ free(tlv);
+}
+
+static struct qmi_tlv_item *qmi_tlv_get_item(struct qmi_tlv *tlv, unsigned id)
+{
+ struct qmi_tlv_item *item;
+ struct qmi_packet *pkt;
+ unsigned offset = 0;
+ void *pkt_data;
+
+ pkt = tlv->buf;
+ pkt_data = pkt->data;
+
+ while (offset < tlv->size) {
+ item = pkt_data + offset;
+ if (item->key == id)
+ return pkt_data + offset;
+
+ offset += sizeof(struct qmi_tlv_item) + item->len;
+ }
+ return NULL;
+}
+
+void *qmi_tlv_get(struct qmi_tlv *tlv, unsigned id, size_t *len)
+{
+ struct qmi_tlv_item *item;
+
+ item = qmi_tlv_get_item(tlv, id);
+ if (!item)
+ return NULL;
+
+ *len = item->len;
+ return item->data;
+}
+
+void *qmi_tlv_get_array(struct qmi_tlv *tlv, unsigned id, unsigned len_size, size_t *len, size_t *size)
+{
+ struct qmi_tlv_item *item;
+ unsigned count;
+ void *ptr;
+
+ item = qmi_tlv_get_item(tlv, id);
+ if (!item)
+ return NULL;
+
+ ptr = item->data;
+ switch (len_size) {
+ case 4:
+ count = *(uint32_t*)ptr++;
+ break;
+ case 2:
+ count = *(uint16_t*)ptr++;
+ break;
+ case 1:
+ count = *(uint8_t*)ptr++;
+ break;
+ }
+
+ *len = count;
+ *size = (item->len - len_size) / count;
+
+ return ptr;
+}
+
+static struct qmi_tlv_item *qmi_tlv_alloc_item(struct qmi_tlv *tlv, unsigned id, size_t len)
+{
+ struct qmi_tlv_item *item;
+ size_t new_size;
+ bool migrate;
+ void *newp;
+
+ /* If using user provided buffer, migrate data */
+ migrate = !tlv->allocated;
+
+ new_size = tlv->size + sizeof(struct qmi_tlv_item) + len;
+ newp = realloc(tlv->allocated, new_size);
+ if (!newp)
+ return NULL;
+
+ if (migrate)
+ memcpy(newp, tlv->buf, tlv->size);
+
+ item = newp + tlv->size;
+ item->key = id;
+ item->len = len;
+
+ tlv->buf = tlv->allocated = newp;
+ tlv->size = new_size;
+
+ return item;
+}
+
+int qmi_tlv_set(struct qmi_tlv *tlv, unsigned id, void *buf, size_t len)
+{
+ struct qmi_tlv_item *item;
+
+ if (!tlv)
+ return -EINVAL;
+
+ item = qmi_tlv_alloc_item(tlv, id, len);
+ if (!item) {
+ tlv->error = ENOMEM;
+ return -ENOMEM;
+ }
+
+ memcpy(item->data, buf, len);
+
+ return 0;
+}
+
+int qmi_tlv_set_array(struct qmi_tlv *tlv, unsigned id, unsigned len_size, void *buf, size_t len, size_t size)
+{
+ struct qmi_tlv_item *item;
+ size_t array_size;
+ void *ptr;
+
+ if (!tlv)
+ return -EINVAL;
+
+ array_size = len * size;
+ item = qmi_tlv_alloc_item(tlv, id, len_size + array_size);
+ if (!item) {
+ tlv->error = ENOMEM;
+ return -ENOMEM;
+ }
+
+ ptr = item->data;
+
+ switch (len_size) {
+ case 4:
+ *(uint32_t*)ptr++ = len;
+ break;
+ case 2:
+ *(uint16_t*)ptr++ = len;
+ break;
+ case 1:
+ *(uint8_t*)ptr++ = len;
+ break;
+ }
+ memcpy(ptr, buf, array_size);
+
+ return 0;
+}
+
+
diff --git a/qcom/rmtfs/rmtfs.c b/qcom/rmtfs/rmtfs.c
new file mode 100644
index 0000000..795c021
--- /dev/null
+++ b/qcom/rmtfs/rmtfs.c
@@ -0,0 +1,564 @@
+#include <sys/mman.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <ctype.h>
+#include <dirent.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <libqrtr.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "qmi_rmtfs.h"
+#include "util.h"
+#include "rmtfs.h"
+
+#define RMTFS_QMI_SERVICE 14
+#define RMTFS_QMI_VERSION 1
+#define RMTFS_QMI_INSTANCE 0
+
+static struct rmtfs_mem *rmem;
+static sig_atomic_t sig_int_count;
+
+static bool dbgprintf_enabled;
+static void dbgprintf(const char *fmt, ...)
+{
+ va_list ap;
+
+ if (!dbgprintf_enabled)
+ return;
+
+ va_start(ap, fmt);
+ vprintf(fmt, ap);
+ va_end(ap);
+}
+
+static void qmi_result_error(struct rmtfs_qmi_result *result, unsigned error)
+{
+ /* Only propagate initial error */
+ if (result->result == QMI_RMTFS_RESULT_FAILURE)
+ return;
+
+ result->result = QMI_RMTFS_RESULT_FAILURE;
+ result->error = error;
+}
+
+static void rmtfs_open(int sock, const struct qrtr_packet *pkt)
+{
+ struct rmtfs_open_resp resp = {};
+ struct rmtfs_open_req req = {};
+ DEFINE_QRTR_PACKET(resp_buf, 256);
+ struct rmtfd *rmtfd;
+ unsigned int txn;
+ ssize_t len;
+ int caller_id = -1;
+ int ret;
+
+ ret = qmi_decode_message(&req, &txn, pkt, QMI_REQUEST,
+ QMI_RMTFS_OPEN, rmtfs_open_req_ei);
+ if (ret < 0) {
+ qmi_result_error(&resp.result, QMI_RMTFS_ERR_MALFORMED_MSG);
+ goto respond;
+ }
+
+ rmtfd = storage_open(pkt->node, req.path);
+ if (!rmtfd) {
+ qmi_result_error(&resp.result, QMI_RMTFS_ERR_INTERNAL);
+ goto respond;
+ }
+
+ caller_id = storage_get_caller_id(rmtfd);
+ resp.caller_id = caller_id;
+ resp.caller_id_valid = true;
+
+respond:
+ dbgprintf("[RMTFS] open %s => %d (%d:%d)\n",
+ req.path, caller_id, resp.result.result, resp.result.error);
+
+ len = qmi_encode_message(&resp_buf,
+ QMI_RESPONSE, QMI_RMTFS_OPEN, txn, &resp,
+ rmtfs_open_resp_ei);
+ if (len < 0) {
+ fprintf(stderr, "[RMTFS] failed to encode open-response: %s\n",
+ strerror(-len));
+ return;
+ }
+
+ ret = qrtr_sendto(sock, pkt->node, pkt->port,
+ resp_buf.data, resp_buf.data_len);
+ if (ret < 0)
+ fprintf(stderr, "[RMTFS] failed to send open-response: %s\n",
+ strerror(-ret));
+}
+
+static void rmtfs_close(int sock, const struct qrtr_packet *pkt)
+{
+ struct rmtfs_close_resp resp = {};
+ struct rmtfs_close_req req = {};
+ DEFINE_QRTR_PACKET(resp_buf, 256);
+ struct rmtfd *rmtfd;
+ unsigned int txn;
+ ssize_t len;
+ int ret;
+
+ ret = qmi_decode_message(&req, &txn, pkt, QMI_REQUEST,
+ QMI_RMTFS_CLOSE, rmtfs_close_req_ei);
+ if (ret < 0) {
+ qmi_result_error(&resp.result, QMI_RMTFS_ERR_MALFORMED_MSG);
+ goto respond;
+ }
+
+ rmtfd = storage_get(pkt->node, req.caller_id);
+ if (!rmtfd) {
+ qmi_result_error(&resp.result, QMI_RMTFS_ERR_INTERNAL);
+ goto respond;
+ }
+
+ storage_close(rmtfd);
+ rmtfs_mem_free(rmem);
+
+respond:
+ dbgprintf("[RMTFS] close %s => %d (%d:%d)\n",
+ req.caller_id, resp.result.result, resp.result.error);
+
+ len = qmi_encode_message(&resp_buf,
+ QMI_RESPONSE, QMI_RMTFS_CLOSE, txn, &resp,
+ rmtfs_close_resp_ei);
+ if (len < 0) {
+ fprintf(stderr, "[RMTFS] failed to encode close-response: %s\n",
+ strerror(-len));
+ return;
+ }
+
+ ret = qrtr_sendto(sock, pkt->node, pkt->port,
+ resp_buf.data, resp_buf.data_len);
+ if (ret < 0)
+ fprintf(stderr, "[RMTFS] failed to send close-response: %s\n",
+ strerror(-ret));
+}
+
+static void rmtfs_iovec(int sock, struct qrtr_packet *pkt)
+{
+ struct rmtfs_iovec_entry *entries;
+ struct rmtfs_iovec_resp resp = {};
+ struct rmtfs_iovec_req req = {};
+ DEFINE_QRTR_PACKET(resp_buf, 256);
+ struct rmtfd *rmtfd;
+ uint32_t caller_id = 0;
+ size_t num_entries = 0;
+ off_t sector_base;
+ uint8_t is_write;
+ off_t phys_base;
+ uint8_t force = 0;
+ unsigned txn;
+ off_t offset;
+ ssize_t len;
+ ssize_t n;
+ char buf[SECTOR_SIZE];
+ int ret;
+ int i;
+ int j;
+
+ ret = qmi_decode_message(&req, &txn, pkt, QMI_REQUEST,
+ QMI_RMTFS_RW_IOVEC, rmtfs_iovec_req_ei);
+ if (ret < 0) {
+ qmi_result_error(&resp.result, QMI_RMTFS_ERR_MALFORMED_MSG);
+ goto respond;
+ }
+
+ caller_id = req.caller_id;
+ is_write = req.direction;
+ entries = req.iovec;
+ num_entries = req.iovec_len;
+ force = req.is_force_sync;
+
+ rmtfd = storage_get(pkt->node, caller_id);
+ if (!rmtfd) {
+ fprintf(stderr, "[RMTFS] iovec request for non-existing caller\n");
+ qmi_result_error(&resp.result, QMI_RMTFS_ERR_INTERNAL);
+ goto respond;
+ }
+
+ for (i = 0; i < num_entries; i++) {
+ phys_base = entries[i].phys_offset;
+ sector_base = entries[i].sector_addr * SECTOR_SIZE;
+ offset = 0;
+
+ for (j = 0; j < entries[i].num_sector; j++) {
+ if (is_write) {
+ n = rmtfs_mem_read(rmem, phys_base + offset, buf, SECTOR_SIZE);
+ if (n == SECTOR_SIZE)
+ n = storage_pwrite(rmtfd, buf, n, sector_base + offset);
+ } else {
+ n = storage_pread(rmtfd, buf, SECTOR_SIZE, sector_base + offset);
+ if (n >= 0) {
+ if (n < SECTOR_SIZE)
+ memset(buf + n, 0, SECTOR_SIZE - n);
+ n = rmtfs_mem_write(rmem, phys_base + offset, buf, SECTOR_SIZE);
+ }
+ }
+
+ if (n != SECTOR_SIZE) {
+ fprintf(stderr, "[RMTFS] failed to %s sector %d\n",
+ is_write ? "write" : "read", entries[i].sector_addr + j);
+ qmi_result_error(&resp.result, QMI_RMTFS_ERR_INTERNAL);
+ goto respond;
+ }
+
+ offset += SECTOR_SIZE;
+ }
+ }
+
+respond:
+ dbgprintf("[RMTFS] iovec %d, %sforced => (%d:%d)\n", caller_id, force ? "" : "not ",
+ resp.result.result, resp.result.error);
+ for (i = 0; i < num_entries; i++) {
+ dbgprintf("[RMTFS] %s %d:%d 0x%x\n", is_write ? "write" : "read",
+ entries[i].sector_addr,
+ entries[i].num_sector,
+ entries[i].phys_offset);
+ }
+
+ len = qmi_encode_message(&resp_buf,
+ QMI_RESPONSE, QMI_RMTFS_RW_IOVEC, txn, &resp,
+ rmtfs_iovec_resp_ei);
+ if (len < 0) {
+ fprintf(stderr, "[RMTFS] failed to encode iovec-response: %s\n",
+ strerror(-len));
+ return;
+ }
+
+ ret = qrtr_sendto(sock, pkt->node, pkt->port,
+ resp_buf.data, resp_buf.data_len);
+ if (ret < 0)
+ fprintf(stderr, "[RMTFS] failed to send iovec-response: %s\n",
+ strerror(-ret));
+}
+
+static void rmtfs_alloc_buf(int sock, struct qrtr_packet *pkt)
+{
+ struct rmtfs_alloc_buf_resp resp = {};
+ struct rmtfs_alloc_buf_req req = {};
+ DEFINE_QRTR_PACKET(resp_buf, 256);
+ uint32_t alloc_size = 0;
+ uint32_t caller_id = 0;
+ int64_t address = 0;
+ unsigned txn;
+ ssize_t len;
+ int ret;
+
+ ret = qmi_decode_message(&req, &txn, pkt, QMI_REQUEST,
+ QMI_RMTFS_ALLOC_BUFF, rmtfs_alloc_buf_req_ei);
+ if (ret < 0) {
+ qmi_result_error(&resp.result, QMI_RMTFS_ERR_MALFORMED_MSG);
+ goto respond;
+ }
+
+ caller_id = req.caller_id;
+ alloc_size = req.buff_size;
+
+ address = rmtfs_mem_alloc(rmem, alloc_size);
+ if (address < 0) {
+ qmi_result_error(&resp.result, QMI_RMTFS_ERR_INTERNAL);
+ goto respond;
+ }
+
+ resp.buff_address = address;
+ resp.buff_address_valid = true;
+respond:
+ dbgprintf("[RMTFS] alloc %d, %d => 0x%lx (%d:%d)\n", caller_id, alloc_size, address, resp.result.result, resp.result.error);
+
+ len = qmi_encode_message(&resp_buf,
+ QMI_RESPONSE, QMI_RMTFS_ALLOC_BUFF, txn, &resp,
+ rmtfs_alloc_buf_resp_ei);
+ if (len < 0) {
+ fprintf(stderr, "[RMTFS] failed to encode alloc-buf-response: %s\n",
+ strerror(-len));
+ return;
+ }
+
+ ret = qrtr_sendto(sock, pkt->node, pkt->port,
+ resp_buf.data, resp_buf.data_len);
+ if (ret < 0)
+ fprintf(stderr, "[RMTFS] failed to send alloc-buf-response: %s\n",
+ strerror(-ret));
+}
+
+static void rmtfs_get_dev_error(int sock, struct qrtr_packet *pkt)
+{
+ struct rmtfs_dev_error_resp resp = {};
+ struct rmtfs_dev_error_req req = {};
+ DEFINE_QRTR_PACKET(resp_buf, 256);
+ struct rmtfd *rmtfd;
+ unsigned txn;
+ ssize_t len;
+ int ret;
+
+ ret = qmi_decode_message(&req, &txn, pkt, QMI_REQUEST,
+ QMI_RMTFS_GET_DEV_ERROR,
+ rmtfs_dev_error_req_ei);
+ if (ret < 0) {
+ qmi_result_error(&resp.result, QMI_RMTFS_ERR_MALFORMED_MSG);
+ goto respond;
+ }
+
+ rmtfd = storage_get(pkt->node, req.caller_id);
+ if (rmtfd) {
+ qmi_result_error(&resp.result, QMI_RMTFS_ERR_INTERNAL);
+ goto respond;
+ }
+
+ resp.status = storage_get_error(rmtfd);
+ resp.status_valid = true;
+
+respond:
+ dbgprintf("[RMTFS] dev_error %d => %d (%d:%d)\n", req.caller_id, resp.status, resp.result.result, resp.result.error);
+
+ len = qmi_encode_message(&resp_buf,
+ QMI_RESPONSE, QMI_RMTFS_GET_DEV_ERROR, txn,
+ &resp, rmtfs_dev_error_resp_ei);
+ if (len < 0) {
+ fprintf(stderr, "[RMTFS] failed to encode dev-error-response: %s\n",
+ strerror(-len));
+ return;
+ }
+
+ ret = qrtr_sendto(sock, pkt->node, pkt->port,
+ resp_buf.data, resp_buf.data_len);
+ if (ret < 0)
+ fprintf(stderr, "[RMTFS] failed to send dev-error-response: %s\n",
+ strerror(-ret));
+}
+
+static int rmtfs_bye(uint32_t node)
+{
+ dbgprintf("[RMTFS] bye from %d\n", node);
+
+ return 0;
+}
+
+static int rmtfs_del_client(uint32_t node, uint32_t port)
+{
+ dbgprintf("[RMTFS] del_client %d:%d\n", node, port);
+
+ return 0;
+}
+
+static int handle_rmtfs(int sock)
+{
+ struct sockaddr_qrtr sq;
+ struct qrtr_packet pkt;
+ unsigned int msg_id;
+ socklen_t sl;
+ char buf[4096];
+ int ret;
+
+ sl = sizeof(sq);
+ ret = recvfrom(sock, buf, sizeof(buf), 0, (void *)&sq, &sl);
+ if (ret < 0) {
+ ret = -errno;
+ if (ret != -ENETRESET)
+ fprintf(stderr, "[RMTFS] recvfrom failed: %d\n", ret);
+ return ret;
+ }
+
+ dbgprintf("[RMTFS] packet; from: %d:%d\n", sq.sq_node, sq.sq_port);
+
+ ret = qrtr_decode(&pkt, buf, ret, &sq);
+ if (ret < 0) {
+ fprintf(stderr, "[RMTFS] unable to decode qrtr packet\n");
+ return ret;
+ }
+
+ switch (pkt.type) {
+ case QRTR_TYPE_BYE:
+ return rmtfs_bye(pkt.node);
+ case QRTR_TYPE_DEL_CLIENT:
+ return rmtfs_del_client(pkt.node, pkt.port);
+ case QRTR_TYPE_DATA:
+ ret = qmi_decode_header(&pkt, &msg_id);
+ if (ret < 0)
+ return ret;
+
+ switch (msg_id) {
+ case QMI_RMTFS_OPEN:
+ rmtfs_open(sock, &pkt);
+ break;
+ case QMI_RMTFS_CLOSE:
+ rmtfs_close(sock, &pkt);
+ break;
+ case QMI_RMTFS_RW_IOVEC:
+ rmtfs_iovec(sock, &pkt);
+ break;
+ case QMI_RMTFS_ALLOC_BUFF:
+ rmtfs_alloc_buf(sock, &pkt);
+ break;
+ case QMI_RMTFS_GET_DEV_ERROR:
+ rmtfs_get_dev_error(sock, &pkt);
+ break;
+ default:
+ fprintf(stderr, "[RMTFS] Unknown request: %d\n", msg_id);
+ break;
+ }
+
+ return 0;
+ }
+
+ return ret;
+}
+
+static int sig_int_count;
+
+static int run_rmtfs(int rprocfd)
+{
+ bool sig_int_handled = false;
+ int rmtfs_fd;
+ fd_set rfds;
+ char done;
+ int nfds;
+ int ret;
+
+ rmtfs_fd = qrtr_open(RMTFS_QMI_SERVICE);
+ if (rmtfs_fd < 0) {
+ fprintf(stderr, "failed to create qrtr socket\n");
+ return rmtfs_fd;
+ }
+
+ dbgprintf("registering services\n");
+
+ ret = qrtr_publish(rmtfs_fd, RMTFS_QMI_SERVICE,
+ RMTFS_QMI_VERSION, RMTFS_QMI_INSTANCE);
+ if (ret < 0) {
+ fprintf(stderr, "failed to publish rmtfs service");
+ return ret;
+ }
+
+ if (rprocfd >= 0)
+ rproc_start();
+
+ for (;;) {
+ if (rprocfd >= 0 && sig_int_count == 1 && !sig_int_handled) {
+ rproc_stop();
+ sig_int_handled = true;
+ } else if (sig_int_count > 1) {
+ break;
+ }
+
+ FD_ZERO(&rfds);
+ FD_SET(rmtfs_fd, &rfds);
+ if (rprocfd >= 0)
+ FD_SET(rprocfd, &rfds);
+ nfds = MAX(rmtfs_fd, rprocfd) + 1;
+
+ ret = select(nfds, &rfds, NULL, NULL, NULL);
+ if (ret < 0 && errno != EINTR)
+ break;
+ else if (ret < 0 && errno == EINTR)
+ continue;
+
+ if (rprocfd >= 0 && FD_ISSET(rprocfd, &rfds)) {
+ ret = read(rprocfd, &done, 1);
+ if (!ret || done == 'Y')
+ break;
+ }
+
+ if (FD_ISSET(rmtfs_fd, &rfds)) {
+ ret = handle_rmtfs(rmtfs_fd);
+ if (ret == -ENETRESET)
+ break;
+ }
+ }
+
+ close(rmtfs_fd);
+
+ return ret;
+}
+
+static void sig_int_handler(int signo)
+{
+ sig_int_count++;
+}
+
+int main(int argc, char **argv)
+{
+ struct sigaction action;
+ bool use_partitions = false;
+ bool read_only = false;
+ int rprocfd = -1;
+ int ret;
+ int option;
+ const char *storage_root = NULL;
+
+ while ((option = getopt(argc, argv, "o:Prsv")) != -1) {
+ switch (option) {
+ /* -o sets the directory where EFS images are stored. */
+ case 'o':
+ storage_root = optarg;
+ break;
+
+ /* -P to find and use raw EFS partitions */
+ case 'P':
+ use_partitions = true;
+ break;
+
+ /* -r to avoid writing to storage */
+ case 'r':
+ read_only = true;
+ break;
+
+ /* enable sync for the mss rproc instance */
+ case 's':
+ rprocfd = rproc_init();
+ if (rprocfd < 0) {
+ fprintf(stderr, "Failed to get rprocfd\n");
+ return 1;
+ }
+
+ break;
+
+ /* -v is for verbose */
+ case 'v':
+ dbgprintf_enabled = 1;
+ break;
+
+ case '?':
+ fprintf(stderr, "Unknown option: -%c\n", option);
+ return 1;
+ }
+ }
+
+ sigemptyset(&action.sa_mask);
+ action.sa_handler = sig_int_handler;
+ action.sa_flags = 0;
+
+ sigaction(SIGINT, &action, NULL);
+ sigaction(SIGTERM, &action, NULL);
+
+ rmem = rmtfs_mem_open();
+ if (!rmem)
+ return 1;
+
+ ret = storage_init(storage_root, read_only, use_partitions);
+ if (ret) {
+ fprintf(stderr, "failed to initialize storage system\n");
+ goto close_rmtfs_mem;
+ }
+
+ do {
+ ret = run_rmtfs(rprocfd);
+ } while (ret == -ENETRESET);
+
+ storage_exit();
+close_rmtfs_mem:
+ rmtfs_mem_close(rmem);
+
+ return 0;
+}
diff --git a/qcom/rmtfs/rmtfs.h b/qcom/rmtfs/rmtfs.h
new file mode 100644
index 0000000..242baa5
--- /dev/null
+++ b/qcom/rmtfs/rmtfs.h
@@ -0,0 +1,42 @@
+#ifndef __RMTFS_H__
+#define __RMTFS_H__
+
+#include <stdint.h>
+#include "qmi_rmtfs.h"
+
+#define SECTOR_SIZE 512
+
+struct qmi_packet {
+ uint8_t flags;
+ uint16_t txn_id;
+ uint16_t msg_id;
+ uint16_t msg_len;
+ uint8_t data[];
+} __attribute__((__packed__));
+
+struct rmtfs_mem;
+
+struct rmtfs_mem *rmtfs_mem_open(void);
+void rmtfs_mem_close(struct rmtfs_mem *rmem);
+int64_t rmtfs_mem_alloc(struct rmtfs_mem *rmem, size_t size);
+void rmtfs_mem_free(struct rmtfs_mem *rmem);
+ssize_t rmtfs_mem_read(struct rmtfs_mem *rmem, unsigned long phys_address, void *buf, ssize_t len);
+ssize_t rmtfs_mem_write(struct rmtfs_mem *rmem, unsigned long phys_address, const void *buf, ssize_t len);
+
+struct rmtfd;
+
+int storage_init(const char *storage_root, bool read_only, bool use_partitions);
+struct rmtfd *storage_open(unsigned node, const char *path);
+struct rmtfd *storage_get(unsigned node, int caller_id);
+void storage_close(struct rmtfd *rmtfd);
+int storage_get_caller_id(const struct rmtfd *rmtfd);
+int storage_get_error(const struct rmtfd *rmtfd);
+void storage_exit(void);
+ssize_t storage_pread(const struct rmtfd *rmtfd, void *buf, size_t nbyte, off_t offset);
+ssize_t storage_pwrite(struct rmtfd *rmtfd, const void *buf, size_t nbyte, off_t offset);
+
+int rproc_init(void);
+int rproc_start(void);
+int rproc_stop(void);
+
+#endif
diff --git a/qcom/rmtfs/rmtfs.service.in b/qcom/rmtfs/rmtfs.service.in
new file mode 100644
index 0000000..8384043
--- /dev/null
+++ b/qcom/rmtfs/rmtfs.service.in
@@ -0,0 +1,12 @@
+[Unit]
+Description=Qualcomm remotefs service
+Requires=qrtr-ns.service
+After=qrtr-ns.service
+
+[Service]
+ExecStart=RMTFS_PATH/rmtfs -r -P -s
+Restart=always
+RestartSec=1
+
+[Install]
+WantedBy=multi-user.target
diff --git a/qcom/rmtfs/rproc.c b/qcom/rmtfs/rproc.c
new file mode 100644
index 0000000..a471b3c
--- /dev/null
+++ b/qcom/rmtfs/rproc.c
@@ -0,0 +1,137 @@
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <pthread.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "rmtfs.h"
+
+#define RPROC_BASE_PATH "/sys/bus/platform/drivers/qcom-q6v5-mss/"
+
+static pthread_t start_thread;
+static pthread_t stop_thread;
+static int rproc_state_fd;
+static int rproc_pipe[2];
+
+int rproc_init(void)
+{
+ struct dirent *device_de;
+ struct dirent *rproc_de;
+ int rproc_base_fd;
+ DIR *rproc_dir;
+ DIR *base_dir;
+ int device_fd;
+ int rproc_fd;
+ int base_fd;
+ int ret;
+
+ rproc_state_fd = -1;
+
+ base_fd = open(RPROC_BASE_PATH, O_RDONLY | O_DIRECTORY);
+ if (base_fd < 0)
+ return -1;
+
+ base_dir = fdopendir(base_fd);
+ if (!base_dir) {
+ fprintf(stderr, "failed to open mss driver path\n");
+ close(base_fd);
+ return -1;
+ }
+
+ while (rproc_state_fd < 0 && (device_de = readdir(base_dir)) != NULL) {
+ if (!strcmp(device_de->d_name, ".") ||
+ !strcmp(device_de->d_name, ".."))
+ continue;
+
+ device_fd = openat(base_fd, device_de->d_name, O_RDONLY | O_DIRECTORY);
+ if (device_fd < 0)
+ continue;
+
+ rproc_base_fd = openat(device_fd, "remoteproc", O_RDONLY | O_DIRECTORY);
+ if (rproc_base_fd < 0) {
+ close(device_fd);
+ continue;
+ }
+
+ rproc_dir = fdopendir(rproc_base_fd);
+ while (rproc_state_fd < 0 && (rproc_de = readdir(rproc_dir)) != NULL) {
+ if (!strcmp(rproc_de->d_name, ".") ||
+ !strcmp(rproc_de->d_name, ".."))
+ continue;
+
+ rproc_fd = openat(rproc_base_fd, rproc_de->d_name, O_RDONLY | O_DIRECTORY);
+ if (rproc_fd < 0)
+ continue;
+
+ rproc_state_fd = openat(rproc_fd, "state", O_WRONLY);
+ if (rproc_state_fd < 0) {
+ fprintf(stderr,
+ "unable to open remoteproc \"state\" control file of %s\n",
+ device_de->d_name);
+ }
+
+ close(rproc_fd);
+
+ }
+ closedir(rproc_dir);
+ close(rproc_base_fd);
+ close(device_fd);
+ }
+ closedir(base_dir);
+ close(base_fd);
+
+ if (rproc_state_fd < 0)
+ return -1;
+
+ ret = pipe(rproc_pipe);
+ if (ret < 0) {
+ close(rproc_state_fd);
+ return -1;
+ }
+
+ return rproc_pipe[0];
+}
+
+static void *do_rproc_start(void *unused)
+{
+ ssize_t ret;
+
+ ret = pwrite(rproc_state_fd, "start", 5, 0);
+ if (ret < 4)
+ fprintf(stderr, "failed to update start state\n");
+
+ return NULL;
+}
+
+int rproc_start()
+{
+ return pthread_create(&start_thread, NULL, do_rproc_start, NULL);
+}
+
+static void *do_rproc_stop(void *unused)
+{
+ ssize_t ret;
+
+ ret = pwrite(rproc_state_fd, "stop", 4, 0);
+ if (ret < 4)
+ fprintf(stderr, "failed to update stop state\n");
+
+ ret = write(rproc_pipe[1], "Y", 1);
+ if (ret != 1) {
+ fprintf(stderr, "failed to signal event loop about exit\n");
+ exit(0);
+ }
+
+ return NULL;
+}
+
+int rproc_stop(void)
+{
+ return pthread_create(&stop_thread, NULL, do_rproc_stop, NULL);
+}
diff --git a/qcom/rmtfs/sharedmem.c b/qcom/rmtfs/sharedmem.c
new file mode 100644
index 0000000..66bbd5d
--- /dev/null
+++ b/qcom/rmtfs/sharedmem.c
@@ -0,0 +1,460 @@
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#ifndef ANDROID
+#include <libudev.h>
+#else
+#include <sys/endian.h>
+#endif
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <string.h>
+#include <unistd.h>
+#include "rmtfs.h"
+
+static int rmtfs_mem_enumerate(struct rmtfs_mem *rmem);
+
+struct rmtfs_mem {
+ uint64_t address;
+ uint64_t size;
+ void *base;
+ int fd;
+};
+
+#ifndef ANDROID
+
+static int parse_hex_sysattr(struct udev_device *dev, const char *name,
+ uint64_t *value)
+{
+ unsigned long long val;
+ const char *buf;
+ char *endptr;
+
+ buf = udev_device_get_sysattr_value(dev, name);
+ if (!buf)
+ return -ENOENT;
+
+ errno = 0;
+ val = strtoull(buf, &endptr, 16);
+ if ((val == ULLONG_MAX && errno == ERANGE) || endptr == buf) {
+ return -errno;
+ }
+
+ *value = val;
+
+ return 0;
+}
+
+static int rmtfs_mem_open_rfsa(struct rmtfs_mem *rmem, int client_id)
+{
+ struct udev_device *dev;
+ struct udev *udev;
+ int saved_errno;
+ struct stat sb;
+ char path[32];
+ int ret;
+ int fd;
+
+ sprintf(path, "/dev/qcom_rmtfs_mem%d", client_id);
+
+ fd = open(path, O_RDWR);
+ if (fd < 0) {
+ saved_errno = errno;
+ fprintf(stderr, "failed to open %s: %s\n", path, strerror(errno));
+ return -saved_errno;
+ }
+ rmem->fd = fd;
+
+ ret = fstat(fd, &sb);
+ if (ret < 0) {
+ saved_errno = errno;
+ fprintf(stderr, "failed to stat %s: %s\n", path, strerror(errno));
+ close(fd);
+ goto err_close_fd;
+ }
+
+ udev = udev_new();
+ if (!udev) {
+ saved_errno = errno;
+ fprintf(stderr, "failed to create udev context\n");
+ goto err_close_fd;
+ }
+
+ dev = udev_device_new_from_devnum(udev, 'c', sb.st_rdev);
+ if (!dev) {
+ saved_errno = errno;
+ fprintf(stderr, "unable to find udev device\n");
+ goto err_unref_udev;
+ }
+
+ ret = parse_hex_sysattr(dev, "phys_addr", &rmem->address);
+ if (ret < 0) {
+ fprintf(stderr, "failed to parse phys_addr of %s\n", path);
+ saved_errno = -ret;
+ goto err_unref_dev;
+ }
+
+ ret = parse_hex_sysattr(dev, "size", &rmem->size);
+ if (ret < 0) {
+ fprintf(stderr, "failed to parse size of %s\n", path);
+ saved_errno = -ret;
+ goto err_unref_dev;
+ }
+
+ udev_device_unref(dev);
+ udev_unref(udev);
+
+ return 0;
+
+err_unref_dev:
+ udev_device_unref(dev);
+err_unref_udev:
+ udev_unref(udev);
+err_close_fd:
+ close(fd);
+ return -saved_errno;
+}
+
+static int rmtfs_mem_open_uio(struct rmtfs_mem *rmem, int client_id)
+{
+ struct udev_device *dev;
+ struct udev *udev;
+ int saved_errno;
+ struct stat sb;
+ char path[32];
+ int ret;
+ int fd;
+
+ snprintf(path, sizeof(path), "/dev/qcom_rmtfs_uio%d", client_id);
+
+ fd = open(path, O_RDWR);
+ if (fd < 0) {
+ saved_errno = errno;
+ fprintf(stderr, "failed to open %s: %s\n", path, strerror(errno));
+ return -saved_errno;
+ }
+ rmem->fd = fd;
+
+ ret = fstat(fd, &sb);
+ if (ret < 0) {
+ saved_errno = errno;
+ fprintf(stderr, "failed to stat %s: %s\n", path, strerror(errno));
+ close(fd);
+ goto err_close_fd;
+ }
+
+ udev = udev_new();
+ if (!udev) {
+ saved_errno = errno;
+ fprintf(stderr, "failed to create udev context\n");
+ goto err_close_fd;
+ }
+
+ dev = udev_device_new_from_devnum(udev, 'c', sb.st_rdev);
+ if (!dev) {
+ saved_errno = errno;
+ fprintf(stderr, "unable to find udev device\n");
+ goto err_unref_udev;
+ }
+
+ ret = parse_hex_sysattr(dev, "maps/map0/addr", &rmem->address);
+ if (ret < 0) {
+ fprintf(stderr, "failed to parse phys_addr of %s\n", path);
+ saved_errno = -ret;
+ goto err_unref_dev;
+ }
+
+ ret = parse_hex_sysattr(dev, "maps/map0/size", &rmem->size);
+ if (ret < 0) {
+ fprintf(stderr, "failed to parse size of %s\n", path);
+ saved_errno = -ret;
+ goto err_unref_dev;
+ }
+
+ rmem->base = mmap(0, rmem->size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
+ if (rmem->base == MAP_FAILED) {
+ saved_errno = errno;
+ fprintf(stderr, "failed to mmap: %s\n", strerror(errno));
+ goto err_unref_dev;
+ }
+
+ udev_device_unref(dev);
+ udev_unref(udev);
+
+ return 0;
+
+err_unref_dev:
+ udev_device_unref(dev);
+err_unref_udev:
+ udev_unref(udev);
+err_close_fd:
+ close(fd);
+ return -saved_errno;
+}
+
+#else
+
+#define PAGE_SIZE 4096
+
+static int rmtfs_mem_open_rfsa(struct rmtfs_mem *rmem, int client_id)
+{
+ int saved_errno;
+ int fd;
+ char path[PATH_MAX];
+ char val[PAGE_SIZE];
+ char *endptr;
+
+ errno = 0;
+
+ snprintf(path, sizeof(path), "/sys/class/rmtfs/qcom_rmtfs_mem%d/phys_addr", client_id);
+ fd = open(path, O_RDONLY);
+ if (fd < 0) {
+ saved_errno = errno;
+ fprintf(stderr, "failed to open %s: %s\n", path, strerror(errno));
+ return -saved_errno;
+ }
+ read(fd, val, sizeof(val));
+ rmem->address = strtoull(val, &endptr, 16);
+ if ((rmem->address == ULLONG_MAX && errno == ERANGE) || endptr == val) {
+ saved_errno = errno;
+ goto err_close_fd;
+ }
+ close(fd);
+
+ snprintf(path, sizeof(path), "/sys/class/rmtfs/qcom_rmtfs_mem%d/size", client_id);
+ fd = open(path, O_RDONLY);
+ if (fd < 0) {
+ saved_errno = errno;
+ fprintf(stderr, "failed to open %s: %s\n", path, strerror(errno));
+ return -saved_errno;
+ }
+ read(fd, val, sizeof(val));
+ rmem->size = strtoull(val, &endptr, 16);
+ if ((rmem->size == ULLONG_MAX && errno == ERANGE) || endptr == val) {
+ saved_errno = errno;
+ goto err_close_fd;
+ }
+ close(fd);
+
+ return 0;
+
+err_close_fd:
+ close(fd);
+ return -saved_errno;
+}
+
+static int rmtfs_mem_open_uio(struct rmtfs_mem *rmem, int client_id)
+{
+ fprintf(stderr, "uio access is not supported on ANDROID yet\n");
+ return -EINVAL;
+}
+
+#endif
+
+struct rmtfs_mem *rmtfs_mem_open(void)
+{
+ struct rmtfs_mem *rmem;
+ void *base;
+ int ret;
+ int fd;
+
+ rmem = malloc(sizeof(*rmem));
+ if (!rmem)
+ return NULL;
+
+ memset(rmem, 0, sizeof(*rmem));
+
+ ret = rmtfs_mem_open_rfsa(rmem, 1);
+ if (ret < 0 && ret != -ENOENT) {
+ goto err;
+ } else if (ret < 0) {
+ fprintf(stderr, "falling back to uio access\n");
+ ret = rmtfs_mem_open_uio(rmem, 1);
+ if (ret < 0 && ret != -ENOENT) {
+ goto err;
+ } else if (ret < 0) {
+ fprintf(stderr, "falling back to /dev/mem access\n");
+
+ ret = rmtfs_mem_enumerate(rmem);
+ if (ret < 0)
+ goto err;
+
+ fd = open("/dev/mem", O_RDWR|O_SYNC);
+ if (fd < 0) {
+ fprintf(stderr, "failed to open /dev/mem\n");
+ goto err;
+ }
+
+ base = mmap(0, rmem->size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, rmem->address);
+ if (base == MAP_FAILED) {
+ fprintf(stderr, "failed to mmap: %s\n", strerror(errno));
+ goto err_close_fd;
+ }
+
+ rmem->base = base;
+ rmem->fd = fd;
+ }
+ }
+
+ return rmem;
+
+err_close_fd:
+ close(fd);
+err:
+ free(rmem);
+ return NULL;
+}
+
+int64_t rmtfs_mem_alloc(struct rmtfs_mem *rmem, size_t alloc_size)
+{
+ if (alloc_size > rmem->size) {
+ fprintf(stderr,
+ "[RMTFS] rmtfs shared memory not large enough for allocation request 0x%zx vs 0x%lx\n",
+ alloc_size, rmem->size);
+ return -EINVAL;
+ }
+
+ return rmem->address;
+}
+
+void rmtfs_mem_free(struct rmtfs_mem *rmem)
+{
+}
+
+static void *rmtfs_mem_ptr(struct rmtfs_mem *rmem, unsigned long phys_address, ssize_t len)
+{
+ uint64_t start;
+ uint64_t end;
+
+ if (len < 0)
+ return NULL;
+
+ start = phys_address;
+ end = start + len;
+
+ if (start < rmem->address || end > rmem->address + rmem->size)
+ return NULL;
+
+ return rmem->base + phys_address - rmem->address;
+}
+
+ssize_t rmtfs_mem_read(struct rmtfs_mem *rmem, unsigned long phys_address, void *buf, ssize_t len)
+{
+ off_t offset;
+ void *ptr;
+
+ if (rmem->base) {
+ ptr = rmtfs_mem_ptr(rmem, phys_address, len);
+ if (!ptr)
+ return -EINVAL;
+
+ memcpy(buf, ptr, len);
+ } else {
+ offset = phys_address - rmem->address;
+ len = pread(rmem->fd, buf, len, offset);
+ }
+
+ return len;
+}
+
+ssize_t rmtfs_mem_write(struct rmtfs_mem *rmem, unsigned long phys_address, const void *buf, ssize_t len)
+{
+ off_t offset;
+ void *ptr;
+
+ if (rmem->base) {
+ ptr = rmtfs_mem_ptr(rmem, phys_address, len);
+ if (!ptr)
+ return -EINVAL;
+
+ memcpy(ptr, buf, len);
+ } else {
+ offset = phys_address - rmem->address;
+ len = pwrite(rmem->fd, buf, len, offset);
+ }
+
+ return len;
+}
+
+void rmtfs_mem_close(struct rmtfs_mem *rmem)
+{
+ if (rmem->base)
+ munmap(rmem->base, rmem->size);
+
+ close(rmem->fd);
+
+ free(rmem);
+}
+
+static int rmtfs_mem_enumerate(struct rmtfs_mem *rmem)
+{
+ union {
+ uint32_t dw[2];
+ uint64_t qw[2];
+ } reg;
+ struct dirent *de;
+ int basefd;
+ int dirfd;
+ int regfd;
+ DIR *dir;
+ int ret = 0;
+ int n;
+
+ basefd = open("/proc/device-tree/reserved-memory/", O_DIRECTORY);
+ dir = fdopendir(basefd);
+ if (!dir) {
+ fprintf(stderr,
+ "Unable to open reserved-memory device tree node: %s\n",
+ strerror(-errno));
+ close(basefd);
+ return -1;
+ }
+
+ while ((de = readdir(dir)) != NULL) {
+ if (strncmp(de->d_name, "rmtfs", 5) != 0)
+ continue;
+
+ dirfd = openat(basefd, de->d_name, O_DIRECTORY);
+ if (dirfd < 0) {
+ fprintf(stderr, "failed to open %s: %s\n",
+ de->d_name, strerror(-errno));
+ ret = -1;
+ goto out;
+ }
+
+ regfd = openat(dirfd, "reg", O_RDONLY);
+ if (regfd < 0) {
+ fprintf(stderr, "failed to open reg of %s: %s\n",
+ de->d_name, strerror(-errno));
+ ret = -1;
+ goto out;
+ }
+
+ n = read(regfd, &reg, sizeof(reg));
+ if (n == 2 * sizeof(uint32_t)) {
+ rmem->address = be32toh(reg.dw[0]);
+ rmem->size = be32toh(reg.dw[1]);
+ } else if (n == 2 * sizeof(uint64_t)) {
+ rmem->address = be64toh(reg.qw[0]);
+ rmem->size = be64toh(reg.qw[1]);
+ } else {
+ fprintf(stderr, "failed to read reg of %s: %s\n",
+ de->d_name, strerror(-errno));
+ ret = -1;
+ }
+
+ close(regfd);
+ close(dirfd);
+ break;
+ }
+
+out:
+ closedir(dir);
+ close(basefd);
+ return ret;
+}
diff --git a/qcom/rmtfs/storage.c b/qcom/rmtfs/storage.c
new file mode 100644
index 0000000..d31f757
--- /dev/null
+++ b/qcom/rmtfs/storage.c
@@ -0,0 +1,291 @@
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include "rmtfs.h"
+
+#define MAX_CALLERS 10
+#define STORAGE_MAX_SIZE (16 * 1024 * 1024)
+
+#ifndef ANDROID
+#define BY_PARTLABEL_PATH "/dev/disk/by-partlabel"
+#else
+#define BY_PARTLABEL_PATH "/dev/block/by-name"
+#endif
+
+#define MIN(x, y) ((x) < (y) ? (x) : (y))
+
+struct partition {
+ const char *path;
+ const char *actual;
+ const char *partlabel;
+};
+
+struct rmtfd {
+ unsigned id;
+ unsigned node;
+ int fd;
+ unsigned dev_error;
+ const struct partition *partition;
+
+ void *shadow_buf;
+ size_t shadow_len;
+};
+
+static const char *storage_dir = "/boot";
+static int storage_read_only;
+static int storage_use_partitions;
+
+static const struct partition partition_table[] = {
+ { "/boot/modem_fs1", "modem_fs1", "modemst1" },
+ { "/boot/modem_fs2", "modem_fs2", "modemst2" },
+ { "/boot/modem_fsc", "modem_fsc", "fsc" },
+ { "/boot/modem_fsg", "modem_fsg", "fsg" },
+ {}
+};
+
+static struct rmtfd rmtfds[MAX_CALLERS];
+
+static int storage_populate_shadow_buf(struct rmtfd *rmtfd, const char *file);
+
+int storage_init(const char *storage_root, bool read_only, bool use_partitions)
+{
+ int i;
+
+ if (storage_root)
+ storage_dir = storage_root;
+
+ if (use_partitions) {
+ storage_dir = BY_PARTLABEL_PATH;
+ storage_use_partitions = true;
+ }
+
+ storage_read_only = read_only;
+
+ for (i = 0; i < MAX_CALLERS; i++) {
+ rmtfds[i].id = i;
+ rmtfds[i].fd = -1;
+ rmtfds[i].shadow_buf = NULL;
+ }
+
+ return 0;
+}
+
+struct rmtfd *storage_open(unsigned node, const char *path)
+{
+ char *fspath;
+ const struct partition *part;
+ struct rmtfd *rmtfd = NULL;
+ const char *file;
+ size_t pathlen;
+ int saved_errno;
+ int ret;
+ int fd;
+ int i;
+
+ for (part = partition_table; part->path; part++) {
+ if (strcmp(part->path, path) == 0)
+ goto found;
+ }
+
+ fprintf(stderr, "[RMTFS storage] request for unknown partition '%s', rejecting\n", path);
+ return NULL;
+
+found:
+ /* Check if this node already has the requested path open */
+ for (i = 0; i < MAX_CALLERS; i++) {
+ if ((rmtfds[i].fd != -1 || rmtfds[i].shadow_buf) &&
+ rmtfds[i].node == node &&
+ rmtfds[i].partition == part)
+ return &rmtfds[i];
+ }
+
+ for (i = 0; i < MAX_CALLERS; i++) {
+ if (rmtfds[i].fd == -1 && !rmtfds[i].shadow_buf) {
+ rmtfd = &rmtfds[i];
+ break;
+ }
+ }
+ if (!rmtfd) {
+ fprintf(stderr, "[storage] out of free rmtfd handles\n");
+ return NULL;
+ }
+
+ if (storage_use_partitions)
+ file = part->partlabel;
+ else
+ file = part->actual;
+
+ pathlen = strlen(storage_dir) + strlen(file) + 2;
+ fspath = alloca(pathlen);
+ snprintf(fspath, pathlen, "%s/%s", storage_dir, file);
+ if (!storage_read_only) {
+ fd = open(fspath, O_RDWR);
+ if (fd < 0) {
+ saved_errno = errno;
+ fprintf(stderr, "[storage] failed to open '%s' (requested '%s'): %s\n",
+ fspath, part->path, strerror(saved_errno));
+ errno = saved_errno;
+ return NULL;
+ }
+ rmtfd->fd = fd;
+ rmtfd->shadow_len = 0;
+ } else {
+ ret = storage_populate_shadow_buf(rmtfd, fspath);
+ if (ret < 0) {
+ saved_errno = errno;
+ fprintf(stderr, "[storage] failed to open '%s' (requested '%s'): %s\n",
+ fspath, part->path, strerror(saved_errno));
+ errno = saved_errno;
+ return NULL;
+ }
+ }
+
+ rmtfd->node = node;
+ rmtfd->partition = part;
+
+ return rmtfd;
+}
+
+void storage_close(struct rmtfd *rmtfd)
+{
+ close(rmtfd->fd);
+ rmtfd->fd = -1;
+
+ free(rmtfd->shadow_buf);
+ rmtfd->shadow_buf = NULL;
+ rmtfd->shadow_len = 0;
+
+ rmtfd->partition = NULL;
+}
+
+struct rmtfd *storage_get(unsigned node, int caller_id)
+{
+ struct rmtfd *rmtfd;
+
+ if (caller_id >= MAX_CALLERS)
+ return NULL;
+
+ rmtfd = &rmtfds[caller_id];
+ if (rmtfd->node != node)
+ return NULL;
+
+ return rmtfd;
+}
+
+int storage_get_caller_id(const struct rmtfd *rmtfd)
+{
+ return rmtfd->id;
+}
+
+int storage_get_error(const struct rmtfd *rmtfd)
+{
+ return rmtfd->dev_error;
+}
+
+void storage_exit(void)
+{
+ int i;
+
+ for (i = 0; i < MAX_CALLERS; i++) {
+ if (rmtfds[i].fd >= 0)
+ close(rmtfds[i].fd);
+ }
+}
+
+ssize_t storage_pread(const struct rmtfd *rmtfd, void *buf, size_t nbyte, off_t offset)
+{
+ ssize_t n;
+
+ if (!storage_read_only) {
+ n = pread(rmtfd->fd, buf, nbyte, offset);
+ } else {
+ n = MIN(nbyte, rmtfd->shadow_len - offset);
+ if (n > 0)
+ memcpy(buf, rmtfd->shadow_buf + offset, n);
+ else
+ n = 0;
+ }
+
+ if (n < nbyte)
+ memset(buf + n, 0, nbyte - n);
+
+ return nbyte;
+}
+
+ssize_t storage_pwrite(struct rmtfd *rmtfd, const void *buf, size_t nbyte, off_t offset)
+{
+ size_t new_len = offset + nbyte;
+ void *new_buf;
+
+ if (!storage_read_only)
+ return pwrite(rmtfd->fd, buf, nbyte, offset);
+
+ if (new_len >= STORAGE_MAX_SIZE) {
+ fprintf(stderr, "write to %zd bytes exceededs max size\n", new_len);
+ errno = -EINVAL;
+ return -1;
+ }
+
+ if (new_len > rmtfd->shadow_len) {
+ new_buf = realloc(rmtfd->shadow_buf, new_len);
+ if (!new_buf) {
+ errno = -ENOMEM;
+ return -1;
+ }
+
+ rmtfd->shadow_buf = new_buf;
+ rmtfd->shadow_len = new_len;
+ }
+
+ memcpy(rmtfd->shadow_buf + offset, buf, nbyte);
+
+ return nbyte;
+}
+
+static int storage_populate_shadow_buf(struct rmtfd *rmtfd, const char *file)
+{
+ ssize_t len;
+ ssize_t n;
+ void *buf;
+ int ret;
+ int fd;
+
+ fd = open(file, O_RDONLY);
+ if (fd < 0)
+ return -1;
+
+ len = lseek(fd, 0, SEEK_END);
+ if (len < 0) {
+ ret = -1;
+ goto err_close_fd;
+ }
+
+ lseek(fd, 0, SEEK_SET);
+
+ buf = calloc(1, len);
+ if (!buf) {
+ ret = -1;
+ goto err_close_fd;
+ }
+
+ n = read(fd, buf, len);
+ if (n < 0) {
+ ret = -1;
+ goto err_close_fd;
+ }
+
+ rmtfd->shadow_buf = buf;
+ rmtfd->shadow_len = n;
+
+ ret = 0;
+
+err_close_fd:
+ close(fd);
+
+ return ret;
+}
diff --git a/qcom/rmtfs/util.c b/qcom/rmtfs/util.c
new file mode 100644
index 0000000..5ca5bba
--- /dev/null
+++ b/qcom/rmtfs/util.c
@@ -0,0 +1,48 @@
+#include <ctype.h>
+#include <stdint.h>
+#include <stdio.h>
+#include "util.h"
+
+static uint8_t to_hex(uint8_t ch)
+{
+ ch &= 0xf;
+ return ch <= 9 ? '0' + ch : 'a' + ch - 10;
+}
+
+void print_hex_dump(const char *prefix, const void *buf, size_t len)
+{
+ const uint8_t *ptr = buf;
+ size_t linelen;
+ uint8_t ch;
+ char line[16 * 3 + 16 + 1];
+ int li;
+ int i;
+ int j;
+
+ for (i = 0; i < len; i += 16) {
+ linelen = MIN(16, len - i);
+ li = 0;
+
+ for (j = 0; j < linelen; j++) {
+ ch = ptr[i + j];
+ line[li++] = to_hex(ch >> 4);
+ line[li++] = to_hex(ch);
+ line[li++] = ' ';
+ }
+
+ for (; j < 16; j++) {
+ line[li++] = ' ';
+ line[li++] = ' ';
+ line[li++] = ' ';
+ }
+
+ for (j = 0; j < linelen; j++) {
+ ch = ptr[i + j];
+ line[li++] = isprint(ch) ? ch : '.';
+ }
+
+ line[li] = '\0';
+
+ printf("%s %04x: %s\n", prefix, i, line);
+ }
+}
diff --git a/qcom/rmtfs/util.h b/qcom/rmtfs/util.h
new file mode 100644
index 0000000..3a9ec3e
--- /dev/null
+++ b/qcom/rmtfs/util.h
@@ -0,0 +1,9 @@
+#ifndef __UTIL_H__
+#define __UTIL_H__
+
+#define MIN(x, y) ((x) < (y) ? (x) : (y))
+#define MAX(x, y) ((x) > (y) ? (x) : (y))
+
+void print_hex_dump(const char *prefix, const void *buf, size_t len);
+
+#endif