aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTony Garnock-Jones <tonyg@leastfixedpoint.com>2020-09-29 22:03:06 +0200
committerDenis 'GNUtoo' Carikli <GNUtoo@cyberdimension.org>2020-11-26 02:45:36 +0100
commite8d7052e249cc01bfb5a16fbeaefae34ee1da26f (patch)
tree08f07724a52191155b8765f357a09f6498eda189
parent7c578596a1c8b83f7652b373eb147e8c8ad124cf (diff)
downloadhardware_replicant_libsamsung-ipc-e8d7052e249cc01bfb5a16fbeaefae34ee1da26f.tar.gz
hardware_replicant_libsamsung-ipc-e8d7052e249cc01bfb5a16fbeaefae34ee1da26f.tar.bz2
hardware_replicant_libsamsung-ipc-e8d7052e249cc01bfb5a16fbeaefae34ee1da26f.zip
Initial support for herolte (Samsung Galaxy S7 GSM).
Signed-off-by: Tony Garnock-Jones <tonyg@leastfixedpoint.com>
-rw-r--r--samsung-ipc/Makefile.am2
-rw-r--r--samsung-ipc/devices/herolte/herolte.c515
-rw-r--r--samsung-ipc/devices/herolte/herolte.h35
-rw-r--r--samsung-ipc/devices/ipc_devices.c10
-rw-r--r--samsung-ipc/devices/ipc_devices.h1
5 files changed, 563 insertions, 0 deletions
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@paulk.fr>
+ * Copyright (C) 2017 Wolfgang Wiedmeyer <wolfgit@wiedmeyer.de>
+ * Copyright (C) 2020 Tony Garnock-Jones <tonyg@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@paulk.fr>
+ * Copyright (C) 2017 Wolfgang Wiedmeyer <wolfgit@wiedmeyer.de>
+ * Copyright (C) 2020 Tony Garnock-Jones <tonyg@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__