[Replicant] [RFC PATCH 1/3] Initial support for herolte (Samsung Galaxy S7 GSM).

Tony Garnock-Jones tonyg at leastfixedpoint.com
Tue Sep 29 20:03:06 UTC 2020


---
 samsung-ipc/Makefile.am               |   2 +
 samsung-ipc/devices/herolte/herolte.c | 515 ++++++++++++++++++++++++++
 samsung-ipc/devices/herolte/herolte.h |  35 ++
 samsung-ipc/devices/ipc_devices.c     |  10 +
 samsung-ipc/devices/ipc_devices.h     |   1 +
 5 files changed, 563 insertions(+)
 create mode 100644 samsung-ipc/devices/herolte/herolte.c
 create mode 100644 samsung-ipc/devices/herolte/herolte.h

diff --git a/samsung-ipc/Makefile.am b/samsung-ipc/Makefile.am
index 87becdd..fd865f5 100644
--- a/samsung-ipc/Makefile.am
+++ b/samsung-ipc/Makefile.am
@@ -52,6 +52,8 @@ libsamsung_ipc_la_SOURCES = \
 	devices/n7100/n7100.h \
 	devices/n5100/n5100.c \
 	devices/n5100/n5100.h \
+	devices/herolte/herolte.c \
+	devices/herolte/herolte.h \
 	utils.c \
 	call.c \
 	sms.c \
diff --git a/samsung-ipc/devices/herolte/herolte.c b/samsung-ipc/devices/herolte/herolte.c
new file mode 100644
index 0000000..d45b272
--- /dev/null
+++ b/samsung-ipc/devices/herolte/herolte.c
@@ -0,0 +1,515 @@
+/*
+ * This file is part of libsamsung-ipc.
+ *
+ * Copyright (C) 2013-2014 Paul Kocialkowski <contact at paulk.fr>
+ * Copyright (C) 2017 Wolfgang Wiedmeyer <wolfgit at wiedmeyer.de>
+ * Copyright (C) 2020 Tony Garnock-Jones <tonyg at leastfixedpoint.com>
+ *
+ * libsamsung-ipc is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * libsamsung-ipc is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with libsamsung-ipc.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <fcntl.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "ipc.h"
+#include "devices/herolte/herolte.h"
+#include "modems/xmm626/xmm626.h"
+#include "modems/xmm626/xmm626_modem_prj.h"
+#include "modems/xmm626/xmm626_kernel_smdk4412.h"
+
+struct __attribute__((__packed__)) firmware_toc_entry {
+        char name[12];
+        uint32_t offset;        /* offset within firmware file/partition */
+        uint32_t loadaddr;      /* target memory address for this blob */
+        uint32_t size;          /* size of this blob in bytes */
+        uint32_t crc;
+        uint32_t entryid;
+};
+
+struct __attribute__((__packed__)) security_req {
+        uint32_t mode;
+        uint32_t size_boot;
+        uint32_t size_main;
+        uint32_t pad_zero;
+};
+
+struct __attribute__((__packed__)) modem_firmware {
+        uint64_t binary;
+        uint32_t size;
+        uint32_t m_offset;
+        uint32_t b_offset;
+        uint32_t mode;
+        uint32_t len;
+};
+
+#define IOCTL_SECURITY_REQ              _IO('o', 0x53)
+
+#define N_TOC_ENTRIES (512 / sizeof(struct firmware_toc_entry))
+
+static struct firmware_toc_entry const *find_toc_entry(
+        char const *name,
+        struct firmware_toc_entry const *toc)
+{
+        /* We don't know, yet, details of the TOC format; for now, we assume two things:
+           1. reading 512 bytes of TOC is enough, and
+           2. the first entry with an empty name field ends the list. */
+
+        for (int index = 0; index < N_TOC_ENTRIES; index++) {
+                if (toc[index].name[0] == '\0')
+                        break;
+                if (strncmp(toc[index].name, name, sizeof(toc[index].name)) == 0)
+                        return &toc[index];
+        }
+        return NULL;
+}
+
+#define MAX_CHUNK_LEN (62 * 1024) /* This is just what cbd uses.
+                                   * Perhaps a larger value would also
+                                   * work. */
+
+static int upload_chunk(struct ipc_client *client,
+                        int device_fd,
+                        int firmware_fd,
+                        struct firmware_toc_entry const *toc,
+                        char const *name,
+                        uint32_t *size)
+{
+        int rc = -1;
+        uint8_t *buffer = NULL;
+        struct modem_firmware header;
+        struct firmware_toc_entry const *boot_toc_entry;
+        struct firmware_toc_entry const *current_toc_entry;
+        uint32_t remaining;
+
+        ipc_client_log(client, "Uploading %s", name);
+
+        boot_toc_entry = find_toc_entry("BOOT", toc);
+        if (boot_toc_entry == NULL)
+                goto exit;
+
+        current_toc_entry = find_toc_entry(name, toc);
+        if (current_toc_entry == NULL)
+                goto exit;
+
+        if (size != NULL)
+                *size = current_toc_entry->size;
+        ipc_client_log(client, " - blob size for %s is %lu", name, current_toc_entry->size);
+
+        buffer = calloc(1, MAX_CHUNK_LEN);
+        if (buffer == NULL)
+                goto exit;
+
+        header.binary = (uint64_t) buffer;
+        header.size = current_toc_entry->size;
+        header.m_offset = current_toc_entry->loadaddr - boot_toc_entry->loadaddr;
+        header.b_offset = current_toc_entry->offset;
+        header.mode = 0;
+        header.len = 0;
+
+        if (lseek(firmware_fd, header.b_offset, SEEK_SET) < 0)
+                goto exit;
+
+        remaining = header.size;
+        while (remaining > 0) {
+                header.len = remaining < MAX_CHUNK_LEN ? remaining : MAX_CHUNK_LEN;
+                if (read(firmware_fd, buffer, header.len) != header.len)
+                        goto exit;
+                if (ioctl(device_fd, IOCTL_DPRAM_SEND_BOOT, &header) < 0)
+                        goto exit;
+                header.m_offset += header.len;
+                header.b_offset += header.len;
+                remaining -= header.len;
+        }
+
+        rc = 0;
+
+exit:
+        if (buffer != NULL)
+                free(buffer);
+        return rc;
+}
+
+static int select_secure_mode(struct ipc_client *client,
+                              int boot0_fd,
+                              int secure,
+                              uint32_t size_boot,
+                              uint32_t size_main)
+{
+        struct security_req req;
+
+        ipc_client_log(client,
+                       "Issuing IOCTL_SECURITY_REQ - setting %s mode",
+                       secure ? "secure" : "insecure");
+
+        req.mode = secure ? 0 : 2;
+        req.size_boot = size_boot;
+        req.size_main = size_main;
+        req.pad_zero = 0;
+
+        if (ioctl(boot0_fd, IOCTL_SECURITY_REQ, &req) < 0)
+                return -1;
+
+        return 0;
+}
+
+static char const * const modem_image_devices[] = {
+        "/dev/disk/by-partlabel/RADIO", /* PostmarketOS */
+        "/dev/block/platform/155a0000.ufs/by-name/RADIO", /* LineageOS */
+        NULL
+};
+
+static int open_image_device(struct ipc_client *client)
+{
+        for (int i = 0; modem_image_devices[i] != NULL; i++) {
+                char const * const path = modem_image_devices[i];
+                int fd;
+
+                ipc_client_log(client, "  ... trying device path %s", path);
+
+                fd = open(path, O_RDONLY);
+                if (fd < 0) {
+                        if (errno == ENOENT)
+                                continue;
+                        return -1;
+                }
+                return fd;
+        }
+
+        ipc_client_log(client, "  ... no modem image device found!");
+        errno = ENOENT;
+        return -1;
+}
+
+int herolte_boot(struct ipc_client *client)
+{
+        struct firmware_toc_entry toc[N_TOC_ENTRIES];
+        int imagefd = -1;
+        int boot0_fd = -1;
+        int nvfd = -1;
+        int rc = -1;
+        uint32_t size_boot;
+        uint32_t size_main;
+
+	ipc_client_log(client, "Loading firmware TOC");
+
+        imagefd = open_image_device(client);
+        if (imagefd < 0)
+                goto exit;
+
+        if (read(imagefd, &toc[0], sizeof(toc)) != sizeof(toc))
+                goto exit;
+
+	ipc_client_log(client, "Loaded firmware TOC");
+
+        nvfd = open(herolte_nv_data_specs.nv_data_path, O_RDONLY | O_NOCTTY);
+        if (nvfd < 0)
+                goto exit;
+        ipc_client_log(client, "Opened NV data file");
+
+        boot0_fd = open(XMM626_SEC_MODEM_BOOT0_DEVICE, O_RDWR | O_NOCTTY);
+        if (boot0_fd < 0)
+                goto exit;
+
+	ipc_client_log(client, "Resetting modem");
+        if (ioctl(boot0_fd, IOCTL_MODEM_RESET, 0) < 0)
+                goto exit;
+
+        if (select_secure_mode(client, boot0_fd, 0, 0, 0) < 0)
+                goto exit;
+
+        if (upload_chunk(client, boot0_fd, imagefd, toc, "BOOT", &size_boot) < 0)
+                goto exit;
+
+        if (upload_chunk(client, boot0_fd, imagefd, toc, "MAIN", &size_main) < 0)
+                goto exit;
+
+        if (upload_chunk(client, boot0_fd, nvfd, toc, "NV", NULL) < 0)
+                goto exit;
+
+        if (select_secure_mode(client, boot0_fd, 1, size_boot, size_main) < 0)
+                goto exit;
+
+        ipc_client_log(client, "Powering on modem");
+        if (xmm626_kernel_smdk4412_power(client, boot0_fd, 1) < 0)
+                goto exit;
+
+        ipc_client_log(client, "Starting modem boot process");
+        if (xmm626_kernel_smdk4412_boot_power(client, boot0_fd, 1) < 0)
+                goto exit;
+
+        ipc_client_log(client, "Kicking off firmware download");
+        if (ioctl(boot0_fd, IOCTL_MODEM_DL_START, 0) < 0)
+                goto exit;
+
+        ipc_client_log(client, "Handshaking with modem");
+        /* At this point, cbd engages in a little dance with the
+         * newly-booted modem, apparently to verify that it is running
+         * as expected. I don’t know the sources of these magic
+         * numbers, I just faithfully reproduce them. */
+        {
+                uint32_t buf;
+
+                buf = 0x900d;
+                if (write(boot0_fd, &buf, sizeof(buf)) != sizeof(buf))
+                        goto exit;
+                if (read(boot0_fd, &buf, sizeof(buf)) != sizeof(buf))
+                        goto exit;
+                if (buf != 0xa00d)
+                        goto exit;
+                ipc_client_log(client, "Handshake stage I passed");
+
+                buf = 0x9f00;
+                if (write(boot0_fd, &buf, sizeof(buf)) != sizeof(buf))
+                        goto exit;
+                if (read(boot0_fd, &buf, sizeof(buf)) != sizeof(buf))
+                        goto exit;
+                if (buf != 0xaf00)
+                        goto exit;
+                ipc_client_log(client, "Handshake stage II passed");
+        }
+
+        ipc_client_log(client, "Finishing modem boot process");
+        if (xmm626_kernel_smdk4412_boot_power(client, boot0_fd, 0) < 0)
+                goto exit;
+
+        ipc_client_log(client, "Modem boot complete");
+        rc = 0;
+
+        /* Samsung's official daemons continue to read from umts_boot0
+         * at this point, but at present we don't have any means of
+         * doing that within the design of this framework. This is
+         * probably (?) ok, since I have never seen anything actually
+         * come out of umts_boot0 after booting is complete! */
+
+        /* One thing that could be an issue, though, is the kernel's
+         * use of its function rild_ready(). The phone wants *both*
+         * umts_ipc0 and umts_rfs0 to be open for it to be fully
+         * happy. Any upper-level clients of this library will have to
+         * make sure to open both for things to work. */
+
+exit:
+        if (boot0_fd != -1)
+                close(boot0_fd);
+        if (imagefd != -1)
+                close(imagefd);
+        if (nvfd != -1)
+                close(nvfd);
+        return rc;
+}
+
+int herolte_open(__attribute__((unused)) struct ipc_client *client, void *data,
+	       int type)
+{
+	struct herolte_transport_data *transport_data;
+
+	if (data == NULL)
+		return -1;
+
+	transport_data = (struct herolte_transport_data *) data;
+
+	transport_data->fd = xmm626_kernel_smdk4412_open(client, type);
+	if (transport_data->fd < 0)
+		return -1;
+
+	return 0;
+}
+
+int herolte_close(__attribute__((unused)) struct ipc_client *client, void *data)
+{
+	struct herolte_transport_data *transport_data;
+
+	if (data == NULL)
+		return -1;
+
+	transport_data = (struct herolte_transport_data *) data;
+
+	xmm626_kernel_smdk4412_close(client, transport_data->fd);
+	transport_data->fd = -1;
+
+	return 0;
+}
+
+int herolte_read(__attribute__((unused)) struct ipc_client *client, void *data,
+	       void *buffer, size_t length)
+{
+	struct herolte_transport_data *transport_data;
+	int rc;
+
+	if (data == NULL)
+		return -1;
+
+	transport_data = (struct herolte_transport_data *) data;
+
+	rc = xmm626_kernel_smdk4412_read(client, transport_data->fd, buffer,
+					 length);
+
+	return rc;
+}
+
+int herolte_write(__attribute__((unused)) struct ipc_client *client, void *data,
+		const void *buffer, size_t length)
+{
+	struct herolte_transport_data *transport_data;
+	int rc;
+
+	if (data == NULL)
+		return -1;
+
+	transport_data = (struct herolte_transport_data *) data;
+
+	rc = xmm626_kernel_smdk4412_write(client, transport_data->fd, buffer,
+					  length);
+
+	return rc;
+}
+
+int herolte_poll(__attribute__((unused)) struct ipc_client *client, void *data,
+	       struct ipc_poll_fds *fds, struct timeval *timeout)
+{
+	struct herolte_transport_data *transport_data;
+	int rc;
+
+	if (data == NULL)
+		return -1;
+
+	transport_data = (struct herolte_transport_data *) data;
+
+	rc = xmm626_kernel_smdk4412_poll(client, transport_data->fd, fds,
+					 timeout);
+
+	return rc;
+}
+
+int herolte_power_on(__attribute__((unused)) struct ipc_client *client,
+		   __attribute__((unused)) void *data)
+{
+	return 0;
+}
+
+int herolte_power_off(__attribute__((unused)) struct ipc_client *client,
+		    __attribute__((unused)) void *data)
+{
+	int fd;
+	int rc;
+
+	fd = open(XMM626_SEC_MODEM_BOOT0_DEVICE, O_RDWR | O_NOCTTY |
+		  O_NONBLOCK);
+	if (fd < 0)
+		return -1;
+
+	rc = xmm626_kernel_smdk4412_power(client, fd, 0);
+
+	close(fd);
+
+	if (rc < 0)
+		return -1;
+
+	return 0;
+}
+
+int herolte_gprs_activate(__attribute__((unused)) struct ipc_client *client,
+			__attribute__((unused)) void *data,
+			__attribute__((unused)) unsigned int cid)
+{
+        /* TODO: For now, we don't have enough information to
+         * implement this sensibly, hence this placeholder. */
+	return 0;
+}
+
+int herolte_gprs_deactivate(__attribute__((unused)) struct ipc_client *client,
+			  __attribute__((unused)) void *data,
+			  __attribute__((unused)) unsigned int cid)
+{
+        /* TODO: For now, we don't have enough information to
+         * implement this sensibly, hence this placeholder. */
+	return 0;
+}
+
+int herolte_data_create(__attribute__((unused)) struct ipc_client *client,
+		      void **transport_data,
+		      __attribute__((unused)) void **power_data,
+		      __attribute__((unused)) void **gprs_data)
+{
+	if (transport_data == NULL)
+		return -1;
+
+	*transport_data = calloc(1, sizeof(struct herolte_transport_data));
+
+	return 0;
+}
+
+int herolte_data_destroy(__attribute__((unused)) struct ipc_client *client,
+		       void *transport_data,
+		       __attribute__((unused)) void *power_data,
+		       __attribute__((unused)) void *gprs_data)
+{
+	if (transport_data == NULL)
+		return -1;
+
+	free(transport_data);
+
+	return 0;
+}
+
+struct ipc_client_ops herolte_fmt_ops = {
+	.boot = herolte_boot,
+	.send = xmm626_kernel_smdk4412_fmt_send,
+	.recv = xmm626_kernel_smdk4412_fmt_recv,
+};
+
+struct ipc_client_ops herolte_rfs_ops = {
+	.boot = NULL,
+	.send = xmm626_kernel_smdk4412_rfs_send,
+	.recv = xmm626_kernel_smdk4412_rfs_recv,
+};
+
+struct ipc_client_handlers herolte_handlers = {
+	.read = herolte_read,
+	.write = herolte_write,
+	.open = herolte_open,
+	.close = herolte_close,
+	.poll = herolte_poll,
+	.transport_data = NULL,
+	.power_on = herolte_power_on,
+	.power_off = herolte_power_off,
+	.power_data = NULL,
+	.gprs_activate = herolte_gprs_activate,
+	.gprs_deactivate = herolte_gprs_deactivate,
+	.gprs_data = NULL,
+	.data_create = herolte_data_create,
+	.data_destroy = herolte_data_destroy,
+};
+
+struct ipc_client_gprs_specs herolte_gprs_specs = {
+	.gprs_get_iface = xmm626_kernel_smdk4412_gprs_get_iface,
+	.gprs_get_capabilities = xmm626_kernel_smdk4412_gprs_get_capabilities,
+};
+
+struct ipc_client_nv_data_specs herolte_nv_data_specs = {
+	.nv_data_path = XMM626_NV_DATA_PATH,
+	.nv_data_md5_path = XMM626_NV_DATA_MD5_PATH,
+	.nv_data_backup_path = XMM626_NV_DATA_BACKUP_PATH,
+	.nv_data_backup_md5_path = XMM626_NV_DATA_BACKUP_MD5_PATH,
+	.nv_data_secret = XMM626_NV_DATA_SECRET,
+	.nv_data_size = XMM626_NV_DATA_SIZE,
+	.nv_data_chunk_size = XMM626_NV_DATA_CHUNK_SIZE,
+};
diff --git a/samsung-ipc/devices/herolte/herolte.h b/samsung-ipc/devices/herolte/herolte.h
new file mode 100644
index 0000000..32d1638
--- /dev/null
+++ b/samsung-ipc/devices/herolte/herolte.h
@@ -0,0 +1,35 @@
+/*
+ * This file is part of libsamsung-ipc.
+ *
+ * Copyright (C) 2013-2014 Paul Kocialkowski <contact at paulk.fr>
+ * Copyright (C) 2017 Wolfgang Wiedmeyer <wolfgit at wiedmeyer.de>
+ * Copyright (C) 2020 Tony Garnock-Jones <tonyg at leastfixedpoint.com>
+ *
+ * libsamsung-ipc is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * libsamsung-ipc is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with libsamsung-ipc.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __HEROLTE_H__
+#define __HEROLTE_H__
+
+struct herolte_transport_data {
+	int fd;
+};
+
+extern struct ipc_client_ops herolte_fmt_ops;
+extern struct ipc_client_ops herolte_rfs_ops;
+extern struct ipc_client_handlers herolte_handlers;
+extern struct ipc_client_gprs_specs herolte_gprs_specs;
+extern struct ipc_client_nv_data_specs herolte_nv_data_specs;
+
+#endif /* __HEROLTE_H__ */
diff --git a/samsung-ipc/devices/ipc_devices.c b/samsung-ipc/devices/ipc_devices.c
index 91663f6..a0fe955 100644
--- a/samsung-ipc/devices/ipc_devices.c
+++ b/samsung-ipc/devices/ipc_devices.c
@@ -154,6 +154,16 @@ struct ipc_device_desc ipc_devices[] = {
 		.gprs_specs = &n5100_gprs_specs,
 		.nv_data_specs = &n5100_nv_data_specs,
 	},
+	{
+		.name = "herolte",
+		.board_name = NULL,
+		.kernel_version = NULL,
+		.fmt_ops = &herolte_fmt_ops,
+		.rfs_ops = &herolte_rfs_ops,
+		.handlers = &herolte_handlers,
+		.gprs_specs = &herolte_gprs_specs,
+		.nv_data_specs = &herolte_nv_data_specs,
+	},
 };
 
 unsigned int ipc_devices_count = sizeof(ipc_devices) /
diff --git a/samsung-ipc/devices/ipc_devices.h b/samsung-ipc/devices/ipc_devices.h
index 176607c..c5df457 100644
--- a/samsung-ipc/devices/ipc_devices.h
+++ b/samsung-ipc/devices/ipc_devices.h
@@ -28,6 +28,7 @@
 #include "devices/i9300/i9300.h"
 #include "devices/n7100/n7100.h"
 #include "devices/n5100/n5100.h"
+#include "devices/herolte/herolte.h"
 
 #ifndef __IPC_DEVICES_H__
 #define __IPC_DEVICES_H__
-- 
2.28.0



More information about the Replicant mailing list