pax_global_header00006660000000000000000000000064145760633370014530gustar00rootroot0000000000000052 comment=e4e6212c862850c32a452058612110e52c4c68f2 rmtfs-1.1/000077500000000000000000000000001457606333700125245ustar00rootroot00000000000000rmtfs-1.1/.gitignore000066400000000000000000000000301457606333700145050ustar00rootroot00000000000000rmtfs *.o rmtfs.service rmtfs-1.1/Android.bp000066400000000000000000000003371457606333700144320ustar00rootroot00000000000000cc_binary { name: "rmtfs", vendor: true, srcs: [ "qmi_rmtfs.c", "rmtfs.c", "rproc.c", "sharedmem.c", "storage.c", "util.c", ], shared_libs: ["libqrtr"], } rmtfs-1.1/LICENSE000066400000000000000000000027301457606333700135330ustar00rootroot00000000000000Copyright (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. rmtfs-1.1/Makefile000066400000000000000000000011251457606333700141630ustar00rootroot00000000000000OUT := 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 rmtfs-1.1/qmi_rmtfs.c000066400000000000000000000131261457606333700146740ustar00rootroot00000000000000#include #include #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), }, {} }; rmtfs-1.1/qmi_rmtfs.h000066400000000000000000000041241457606333700146770ustar00rootroot00000000000000#ifndef __QMI_RMTFS_H__ #define __QMI_RMTFS_H__ #include #include #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 rmtfs-1.1/qmi_rmtfs.qmi000066400000000000000000000025751457606333700152460ustar00rootroot00000000000000package 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; rmtfs-1.1/qmi_tlv.c000066400000000000000000000077331457606333700143550ustar00rootroot00000000000000#include #include #include #include #include #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; } rmtfs-1.1/rmtfs.c000066400000000000000000000314231457606333700140260ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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); if (is_write) storage_sync(rmtfd); 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 (sig_int_count == 1 && !sig_int_handled) { if (rprocfd < 0) break; 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, * or sets the directory from where raw EFS partitions * can be picked by-name when used with -P option. */ 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; } rmtfs-1.1/rmtfs.h000066400000000000000000000025051457606333700140320ustar00rootroot00000000000000#ifndef __RMTFS_H__ #define __RMTFS_H__ #include #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 storage_sync(struct rmtfd *rmtfd); int rproc_init(void); int rproc_start(void); int rproc_stop(void); #endif rmtfs-1.1/rmtfs.service.in000066400000000000000000000003151457606333700156450ustar00rootroot00000000000000[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 rmtfs-1.1/rproc.c000066400000000000000000000110401457606333700140110ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include "rmtfs.h" #define RPROC_BASE_PATH "/sys/bus/platform/drivers/qcom-q6v5-mss/" #define RPROC_CLASS_PATH "/sys/class/remoteproc/" static pthread_t start_thread; static pthread_t stop_thread; static int rproc_state_fd; static int rproc_pipe[2]; static int rproc_init_by_modalias(void) { struct dirent *rproc_de; char modalias[256]; DIR *base_dir; int modalias_fd; int rproc_fd; int state_fd = -1; int base_fd; int ret; base_fd = open(RPROC_CLASS_PATH, O_RDONLY | O_DIRECTORY); if (base_fd < 0) return -1; base_dir = fdopendir(base_fd); if (!base_dir) { fprintf(stderr, "failed to open remoteproc class path\n"); close(base_fd); return -1; } while (state_fd < 0 && (rproc_de = readdir(base_dir)) != NULL) { if (!strcmp(rproc_de->d_name, ".") || !strcmp(rproc_de->d_name, "..")) continue; rproc_fd = openat(base_fd, rproc_de->d_name, O_RDONLY | O_DIRECTORY); if (rproc_fd < 0) continue; modalias_fd = openat(rproc_fd, "device/modalias", O_RDONLY); if (modalias_fd < 0) goto close_rproc_fd; ret = read(modalias_fd, modalias, sizeof(modalias) - 1); if (ret < 0) goto close_modalias_fd; modalias[ret] = '\0'; if (!strstr(modalias, "-mpss-pas") && !strstr(modalias, "-mss-pil")) goto close_modalias_fd; state_fd = openat(rproc_fd, "state", O_WRONLY); if (state_fd < 0) { fprintf(stderr, "unable to open remoteproc \"state\" control file of %s\n", rproc_de->d_name); } close_modalias_fd: close(modalias_fd); close_rproc_fd: close(rproc_fd); } closedir(base_dir); close(base_fd); return state_fd; } static int rproc_init_by_mss_driver(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 state_fd = -1; int base_fd; 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 (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 (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; state_fd = openat(rproc_fd, "state", O_WRONLY); if (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); return state_fd; } int rproc_init(void) { int state_fd; int ret; state_fd = rproc_init_by_modalias(); if (state_fd < 0) { state_fd = rproc_init_by_mss_driver(); if (state_fd < 0) return -1; } ret = pipe(rproc_pipe); if (ret < 0) { close(state_fd); return -1; } rproc_state_fd = state_fd; 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: %s\n", strerror(errno)); } return NULL; } int rproc_start() { pthread_attr_t attr; pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); return pthread_create(&start_thread, &attr, 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: %s\n", strerror(errno)); } 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) { pthread_attr_t attr; pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); return pthread_create(&stop_thread, &attr, do_rproc_stop, NULL); } rmtfs-1.1/sharedmem.c000066400000000000000000000225311457606333700146400ustar00rootroot00000000000000#include #include #include #include #include #include #ifndef ANDROID #include #else #include #endif #include #include #include #include #include #include #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), "/dev/qcom_rmtfs_mem%d", client_id); rmem->fd = open(path, O_RDWR); if (rmem->fd < 0) { saved_errno = errno; fprintf(stderr, "failed to open %s: %s\n", path, strerror(errno)); return -saved_errno; } 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 (char*)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, ®, 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; } rmtfs-1.1/storage.c000066400000000000000000000132141457606333700143350ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include "rmtfs.h" #define MAX_CALLERS 10 #define STORAGE_MAX_SIZE (16 * 1024 * 1024) #define BY_PARTLABEL_PATH "/dev/disk/by-partlabel" #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" }, { "/boot/modem_tunning", "modem_tunning", "tunning" }, { "/boot/modem_tng", "modem_tng", "tunning" }, {} }; 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) { if (!storage_root) 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) { if (rmtfd->fd >= 0) { 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++) storage_close(&rmtfds[i]); } 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((ssize_t)nbyte, (ssize_t)rmtfd->shadow_len - offset); if (n > 0) memcpy(buf, (char*)rmtfd->shadow_buf + offset, n); else n = 0; } if (n < nbyte) memset((char*)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((char*)rmtfd->shadow_buf + offset, buf, nbyte); return nbyte; } int storage_sync(struct rmtfd *rmtfd) { if (storage_read_only) return 0; return fdatasync(rmtfd->fd); } 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; } rmtfs-1.1/util.c000066400000000000000000000014761457606333700136550ustar00rootroot00000000000000#include #include #include #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); } } rmtfs-1.1/util.h000066400000000000000000000003121457606333700136460ustar00rootroot00000000000000#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