diff options
author | Amit Pundir <amit.pundir@linaro.org> | 2020-02-07 22:26:08 +0530 |
---|---|---|
committer | John Stultz <john.stultz@linaro.org> | 2020-03-17 04:10:56 +0000 |
commit | d477f821bb485acbb232cb896e06b99ed98ba25c (patch) | |
tree | 2cb8babdc907b5eb23c85cedb70742e360beeba5 /qcom/rmtfs | |
parent | 4789995b56420cfbd17b71dd242b54d36fca6361 (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.bp | 13 | ||||
-rw-r--r-- | qcom/rmtfs/LICENSE | 27 | ||||
-rw-r--r-- | qcom/rmtfs/Makefile | 27 | ||||
-rw-r--r-- | qcom/rmtfs/qmi_rmtfs.c | 256 | ||||
-rw-r--r-- | qcom/rmtfs/qmi_rmtfs.h | 101 | ||||
-rw-r--r-- | qcom/rmtfs/qmi_rmtfs.qmi | 77 | ||||
-rw-r--r-- | qcom/rmtfs/qmi_tlv.c | 233 | ||||
-rw-r--r-- | qcom/rmtfs/rmtfs.c | 564 | ||||
-rw-r--r-- | qcom/rmtfs/rmtfs.h | 42 | ||||
-rw-r--r-- | qcom/rmtfs/rmtfs.service.in | 12 | ||||
-rw-r--r-- | qcom/rmtfs/rproc.c | 137 | ||||
-rw-r--r-- | qcom/rmtfs/sharedmem.c | 460 | ||||
-rw-r--r-- | qcom/rmtfs/storage.c | 291 | ||||
-rw-r--r-- | qcom/rmtfs/util.c | 48 | ||||
-rw-r--r-- | qcom/rmtfs/util.h | 9 |
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, ®, 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 |