aboutsummaryrefslogtreecommitdiff
path: root/drivers/soc
diff options
context:
space:
mode:
authorwzedlare <vedatak01@gmail.com>2017-06-18 16:38:26 +0000
committerwzedlare <vedatak01@gmail.com>2017-06-19 16:57:11 +0000
commitc7d4e3fd588e3ba3d3fa4d5cfa224aa54bc288bf (patch)
treeb8b64cb9deb6832c1e41f58f0f143514beafc709 /drivers/soc
parent28c99c87b881bb664c44bb26e80a681f87d54e60 (diff)
p2a42: Import fully working kernel sourceHEADn7.1
Change-Id: Ia4c94f09e29843b1af34d466243378a357e97b70
Diffstat (limited to 'drivers/soc')
-rw-r--r--drivers/soc/qcom/Kconfig35
-rw-r--r--drivers/soc/qcom/Makefile8
-rw-r--r--drivers/soc/qcom/core_ctl_helper.c92
-rw-r--r--drivers/soc/qcom/glink.c39
-rw-r--r--drivers/soc/qcom/glink_ssr.c4
-rw-r--r--drivers/soc/qcom/jtag-fuse.c6
-rw-r--r--drivers/soc/qcom/kernel_protect.c38
-rw-r--r--drivers/soc/qcom/memshare/msm_memshare.c134
-rw-r--r--drivers/soc/qcom/memshare/msm_memshare.h3
-rw-r--r--drivers/soc/qcom/mmi-ram-info.c198
-rw-r--r--drivers/soc/qcom/mmi-unit-info.c219
-rw-r--r--drivers/soc/qcom/mmi-unit-info.h45
-rw-r--r--drivers/soc/qcom/qdsp6v2/voice_svc.c41
-rw-r--r--drivers/soc/qcom/rpm-smd.c33
-rw-r--r--drivers/soc/qcom/rpm_stats.c85
-rw-r--r--drivers/soc/qcom/scm.c10
-rw-r--r--drivers/soc/qcom/smd.c30
-rw-r--r--drivers/soc/qcom/smp2p.c6
-rw-r--r--drivers/soc/qcom/smp2p_debug.c4
-rw-r--r--drivers/soc/qcom/smp2p_test_common.h5
-rw-r--r--drivers/soc/qcom/subsys-pil-tz.c7
-rw-r--r--drivers/soc/qcom/subsystem_restart.c7
-rw-r--r--drivers/soc/qcom/tracer_pkt_private.h2
-rw-r--r--drivers/soc/qcom/unlock_bl/Kconfig5
-rw-r--r--drivers/soc/qcom/unlock_bl/Makefile1
-rw-r--r--drivers/soc/qcom/unlock_bl/unlock_bl.c150
26 files changed, 1021 insertions, 186 deletions
diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig
index 1dbc2efa..ab5c7da2 100644
--- a/drivers/soc/qcom/Kconfig
+++ b/drivers/soc/qcom/Kconfig
@@ -707,14 +707,6 @@ config ICNSS
control messages to FW over QMI channel. It is also responsible for
handling WLAN PD restart notifications.
-config MSM_CORE_CTL_HELPER
- tristate "Core control helper functions for dynamically hotplug CPUs"
- help
- Provide helper functions for core control driver. Core control
- driver dynamically hotplugs CPUs from kernel based on current
- system load and state. It also supports limiting min and
- max online CPUs from userspace.
-
config MSM_BAM_DMUX
bool "BAM Data Mux Driver"
depends on SPS
@@ -782,7 +774,7 @@ config MSM_PACMAN
peripheral (BLSP) ownership.
config MSM_KERNEL_PROTECT
- bool "Protect kernel text by removing write permissions in stage-2"
+ bool "Protect kernel text by removing write permissions"
depends on !FUNCTION_TRACER
help
On hypervisor-enabled targets, this option will make a call into
@@ -795,6 +787,18 @@ config MSM_KERNEL_PROTECT
aarch64_insn_patch_text_nosync, etc. including the various CPU
errata workarounds in arch/arm64/kernel/cpu_errata.c).
+ For MPU based protection-enabled targets please refer to
+ MSM_KERNEL_PROTECT_MPU
+
+config MSM_KERNEL_PROTECT_MPU
+ bool "Protect kernel text from other masters by MPU"
+ depends on MSM_KERNEL_PROTECT
+ help
+ On MPU based protection enabled targets, this option will make a call
+ into TrustZone to request that the kernel text be ptotected for any
+ write access from external bus masters. This protects against
+ malicious devices rewriting kernel code.
+
config MSM_KERNEL_PROTECT_TEST
bool "Bootup test of kernel protection (INTENTIONAL CRASH)"
depends on MSM_KERNEL_PROTECT
@@ -812,6 +816,18 @@ config MSM_REMOTEQDSS
enable/disable these events. Interface located in
/sys/class/remoteqdss.
+config MMI_UNIT_INFO
+ default n
+ bool "Motorola Mobility Unit Info"
+ help
+ Provide mechanism to expose unit information to other cpu via smem.
+
+config MMI_RAM_INFO
+ default n
+ bool "Motorola Mobility RAM Info"
+ help
+ Provide mechanism to access RAM size and MR registers values via sysfs.
+
config QCOM_SMCINVOKE
bool "Secure QSEE Support"
help
@@ -819,5 +835,6 @@ config QCOM_SMCINVOKE
communication between QSEE and HLOS.
source "drivers/soc/qcom/memshare/Kconfig"
+source "drivers/soc/qcom/unlock_bl/Kconfig"
endif # ARCH_MSM
diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile
index a0f69ac8..d7c9b1a7 100644
--- a/drivers/soc/qcom/Makefile
+++ b/drivers/soc/qcom/Makefile
@@ -59,7 +59,11 @@ obj-$(CONFIG_MSM_RPM_RBCPR_STATS_V2_LOG) += rpm_rbcpr_stats_v2.o
obj-$(CONFIG_MEM_SHARE_QMI_SERVICE) += memshare/
obj-$(CONFIG_CP_ACCESS64) += cpaccess64.o
obj-$(CONFIG_MSM_RPM_STATS_LOG) += rpm_stats.o rpm_master_stat.o rpm_rail_stats.o system_stats.o
+ifdef CONFIG_ARCH_MSM8996
obj-$(CONFIG_HW_PERF_EVENTS) += perf_event_kryo.o perf_event_l2.o
+else
+obj-$(CONFIG_HW_PERF_EVENTS) += perf_event_l2.o
+endif
obj-$(CONFIG_MSM_RPM_LOG) += rpm_log.o
obj-$(CONFIG_MSM_JTAG) += jtag-fuse.o jtag.o
obj-$(CONFIG_MSM_JTAG_MM) += jtag-fuse.o jtag-mm.o
@@ -71,7 +75,6 @@ obj-$(CONFIG_MSM_TZ_SMMU) += msm_tz_smmu.o
obj-$(CONFIG_MSM_PIL) += peripheral-loader.o
obj-$(CONFIG_MSM_PIL_SSR_GENERIC) += subsys-pil-tz.o
obj-$(CONFIG_MSM_PIL_MSS_QDSP6V5) += pil-q6v5.o pil-msa.o pil-q6v5-mss.o
-obj-$(CONFIG_MSM_CORE_CTL_HELPER) += core_ctl_helper.o
obj-$(CONFIG_MSM_SCM_ERRATA) += scm-errata.o
obj-$(CONFIG_MSM_PFE_WA) += pfe-wa.o
obj-$(CONFIG_MSM_PERFORMANCE) += msm_performance.o
@@ -98,3 +101,6 @@ obj-$(CONFIG_MSM_KERNEL_PROTECT) += kernel_protect.o
obj-$(CONFIG_MSM_RTB) += msm_rtb-hotplug.o
obj-$(CONFIG_MSM_REMOTEQDSS) += remoteqdss.o
obj-$(CONFIG_QCOM_SMCINVOKE) += smcinvoke.o
+obj-$(CONFIG_MMI_UNIT_INFO) += mmi-unit-info.o
+obj-$(CONFIG_MMI_RAM_INFO) += mmi-ram-info.o
+obj-$(CONFIG_LENOVO_UNLOCK_BL) += unlock_bl/
diff --git a/drivers/soc/qcom/core_ctl_helper.c b/drivers/soc/qcom/core_ctl_helper.c
deleted file mode 100644
index c21ba2f7..00000000
--- a/drivers/soc/qcom/core_ctl_helper.c
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright (c) 2014-2016, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program 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.
- */
-
-#include <linux/cpu.h>
-#include <linux/cpufreq.h>
-#include <linux/ktime.h>
-#include <linux/hrtimer.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <soc/qcom/core_ctl.h>
-
-void core_ctl_block_hotplug(void)
-{
- get_online_cpus();
-}
-EXPORT_SYMBOL(core_ctl_block_hotplug);
-
-void core_ctl_unblock_hotplug(void)
-{
- put_online_cpus();
-}
-EXPORT_SYMBOL(core_ctl_unblock_hotplug);
-
-s64 core_ctl_get_time(void)
-{
- return ktime_to_ms(ktime_get());
-}
-EXPORT_SYMBOL(core_ctl_get_time);
-
-struct cpufreq_policy *core_ctl_get_policy(int cpu)
-{
- return cpufreq_cpu_get(cpu);
-}
-EXPORT_SYMBOL(core_ctl_get_policy);
-
-void core_ctl_put_policy(struct cpufreq_policy *policy)
-{
- cpufreq_cpu_put(policy);
-}
-EXPORT_SYMBOL(core_ctl_put_policy);
-
-struct device *core_ctl_find_cpu_device(unsigned cpu)
-{
- return get_cpu_device(cpu);
-}
-EXPORT_SYMBOL(core_ctl_find_cpu_device);
-
-int __ref core_ctl_online_core(unsigned int cpu)
-{
- int ret;
- struct device *dev;
-
- lock_device_hotplug();
- dev = get_cpu_device(cpu);
- if (!dev) {
- pr_err("%s: failed to get cpu%d device\n", __func__, cpu);
- ret = -ENODEV;
- } else {
- ret = device_online(dev);
- }
- unlock_device_hotplug();
- return ret;
-}
-EXPORT_SYMBOL(core_ctl_online_core);
-
-int __ref core_ctl_offline_core(unsigned int cpu)
-{
- int ret;
- struct device *dev;
-
- lock_device_hotplug();
- dev = get_cpu_device(cpu);
- if (!dev) {
- pr_err("%s: failed to get cpu%d device\n", __func__, cpu);
- ret = -ENODEV;
- } else {
- ret = device_offline(dev);
- }
- unlock_device_hotplug();
- return ret;
-}
-EXPORT_SYMBOL(core_ctl_offline_core);
diff --git a/drivers/soc/qcom/glink.c b/drivers/soc/qcom/glink.c
index 277ab978..f41dca07 100644
--- a/drivers/soc/qcom/glink.c
+++ b/drivers/soc/qcom/glink.c
@@ -1146,6 +1146,7 @@ int ch_pop_remote_rx_intent(struct channel_ctx *ctx, size_t size,
{
struct glink_core_rx_intent *intent;
struct glink_core_rx_intent *intent_tmp;
+ struct glink_core_rx_intent *best_intent = NULL;
unsigned long flags;
if (GLINK_MAX_PKT_SIZE < size) {
@@ -1168,20 +1169,28 @@ int ch_pop_remote_rx_intent(struct channel_ctx *ctx, size_t size,
list_for_each_entry_safe(intent, intent_tmp, &ctx->rmt_rx_intent_list,
list) {
if (intent->intent_size >= size) {
- list_del(&intent->list);
- GLINK_DBG_CH(ctx,
- "%s: R[%u]:%zu Removed remote intent\n",
- __func__,
- intent->id,
- intent->intent_size);
- *riid_ptr = intent->id;
- *intent_size = intent->intent_size;
- kfree(intent);
- spin_unlock_irqrestore(
- &ctx->rmt_rx_intent_lst_lock_lhc2, flags);
- return 0;
+ if (!best_intent)
+ best_intent = intent;
+ else if (best_intent->intent_size > intent->intent_size)
+ best_intent = intent;
+ if (best_intent->intent_size == size)
+ break;
}
}
+ if (best_intent) {
+ list_del(&best_intent->list);
+ GLINK_DBG_CH(ctx,
+ "%s: R[%u]:%zu Removed remote intent\n",
+ __func__,
+ best_intent->id,
+ best_intent->intent_size);
+ *riid_ptr = best_intent->id;
+ *intent_size = best_intent->intent_size;
+ kfree(best_intent);
+ spin_unlock_irqrestore(
+ &ctx->rmt_rx_intent_lst_lock_lhc2, flags);
+ return 0;
+ }
spin_unlock_irqrestore(&ctx->rmt_rx_intent_lst_lock_lhc2, flags);
return -EAGAIN;
}
@@ -2663,7 +2672,7 @@ int glink_close(void *handle)
{
struct glink_core_xprt_ctx *xprt_ctx = NULL;
struct channel_ctx *ctx = (struct channel_ctx *)handle;
- int ret;
+ int ret = 0;
unsigned long flags;
bool is_empty = false;
@@ -2824,7 +2833,7 @@ static int glink_tx_common(void *handle, void *pkt_priv,
tracer_pkt_log_event(data, GLINK_CORE_TX);
}
- /* find matching rx intent (first-fit algorithm for now) */
+ /* find matching rx intent (best-fit algorithm for now) */
if (ch_pop_remote_rx_intent(ctx, size, &riid, &intent_size)) {
if (!(tx_flags & GLINK_TX_REQ_INTENT)) {
/* no rx intent available */
@@ -5228,7 +5237,7 @@ static int glink_scheduler_tx(struct channel_ctx *ctx,
size_t txd_len = 0;
size_t tx_len = 0;
uint32_t num_pkts = 0;
- int ret;
+ int ret = 0;
spin_lock_irqsave(&ctx->tx_lists_lock_lhc3, flags);
while (txd_len < xprt_ctx->mtu &&
diff --git a/drivers/soc/qcom/glink_ssr.c b/drivers/soc/qcom/glink_ssr.c
index 4952e12f..a14d912b 100644
--- a/drivers/soc/qcom/glink_ssr.c
+++ b/drivers/soc/qcom/glink_ssr.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -769,7 +769,7 @@ static int glink_ssr_probe(struct platform_device *pdev)
struct device_node *phandle_node;
struct restart_notifier_block *nb;
struct subsys_info *ss_info;
- struct subsys_info_leaf *ss_info_leaf;
+ struct subsys_info_leaf *ss_info_leaf = NULL;
struct glink_link_info *link_info;
char *key;
const char *edge;
diff --git a/drivers/soc/qcom/jtag-fuse.c b/drivers/soc/qcom/jtag-fuse.c
index 46de4e5f..d7389f39 100644
--- a/drivers/soc/qcom/jtag-fuse.c
+++ b/drivers/soc/qcom/jtag-fuse.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -152,8 +152,6 @@ static int jtag_fuse_probe(struct platform_device *pdev)
drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
if (!drvdata)
return -ENOMEM;
- /* Store the driver data pointer for use in exported functions */
- fusedrvdata = drvdata;
drvdata->dev = &pdev->dev;
platform_set_drvdata(pdev, drvdata);
@@ -174,6 +172,8 @@ static int jtag_fuse_probe(struct platform_device *pdev)
if (!drvdata->base)
return -ENOMEM;
+ /* Store the driver data pointer for use in exported functions */
+ fusedrvdata = drvdata;
dev_info(dev, "JTag Fuse initialized\n");
return 0;
}
diff --git a/drivers/soc/qcom/kernel_protect.c b/drivers/soc/qcom/kernel_protect.c
index 73192971..b48cd0bf 100644
--- a/drivers/soc/qcom/kernel_protect.c
+++ b/drivers/soc/qcom/kernel_protect.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015,2016 The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -14,10 +14,12 @@
#include <linux/printk.h>
#include <linux/init.h>
#include <linux/gfp.h>
+#include <soc/qcom/scm.h>
#include <soc/qcom/secure_buffer.h>
#include <asm/sections.h>
#include <asm/cacheflush.h>
+#define KERNEL_PROTECT_MPU 0x24
#ifdef CONFIG_MSM_KERNEL_PROTECT_TEST
@@ -38,6 +40,10 @@ static void msm_protect_kernel_test(void)
*/
char *addr = (char *)__alloc_pages_nodemask;
+ if (IS_ENABLED(CONFIG_MSM_KERNEL_PROTECT_MPU)) {
+ pr_err("MPU protected kernel code is HLOS writable\n");
+ return;
+ }
pr_err("Checking whether the kernel text is writable...\n");
pr_err("A BUG means it is writable (this is bad)\n");
pr_err("A stage-2 fault means it's not writable (this is good, but we'll still crash)\n");
@@ -84,9 +90,33 @@ static int __init msm_protect_kernel(void)
pr_debug("assigning from phys: %pa to %pa\n",
&kernel_x_start_rounded, &kernel_x_end);
pr_debug("virtual: %p to %p\n", virt_start, virt_end);
- ret = hyp_assign_phys(kernel_x_start_rounded,
- kernel_x_end - kernel_x_start_rounded,
- &vmid_hlos, 1, &vmid_hlos, &dest_perms, 1);
+
+ if (IS_ENABLED(CONFIG_MSM_KERNEL_PROTECT_MPU)) {
+ struct scm_desc desc = {0};
+
+ if (!scm_is_call_available(SCM_SVC_MP, KERNEL_PROTECT_MPU))
+ return 0;
+
+ desc.args[0] = kernel_x_start_rounded;
+ desc.args[1] = kernel_x_end - kernel_x_start_rounded;
+ desc.arginfo = SCM_ARGS(2);
+
+ ret = scm_call2(SCM_SIP_FNID(SCM_SVC_MP,
+ KERNEL_PROTECT_MPU), &desc);
+ if (ret) {
+ /*
+ * must not proceed if failed to MPU protect kernel
+ * text region
+ */
+ panic("Failed to protect kernel region %pa -- %pa\n",
+ &kernel_x_start_rounded,
+ &kernel_x_end);
+ }
+ } else {
+ ret = hyp_assign_phys(kernel_x_start_rounded,
+ kernel_x_end - kernel_x_start_rounded,
+ &vmid_hlos, 1, &vmid_hlos, &dest_perms, 1);
+ }
if (ret)
/*
* We want to fail relatively silently since not all
diff --git a/drivers/soc/qcom/memshare/msm_memshare.c b/drivers/soc/qcom/memshare/msm_memshare.c
index e1e91f56..6f1f3755 100644
--- a/drivers/soc/qcom/memshare/msm_memshare.c
+++ b/drivers/soc/qcom/memshare/msm_memshare.c
@@ -26,6 +26,7 @@
#include "heap_mem_ext_v01.h"
#include <soc/qcom/secure_buffer.h>
+#include <soc/qcom/ramdump.h>
/* Macros */
#define MEMSHARE_DEV_NAME "memshare"
@@ -37,6 +38,7 @@ static void mem_share_svc_recv_msg(struct work_struct *work);
static DECLARE_DELAYED_WORK(work_recv_msg, mem_share_svc_recv_msg);
static struct workqueue_struct *mem_share_svc_workqueue;
static uint64_t bootup_request;
+static void *memshare_ramdump_dev[MAX_CLIENTS];
/* Memshare Driver Structure */
struct memshare_driver {
@@ -114,9 +116,37 @@ static struct msg_desc mem_share_svc_size_query_resp_desc = {
.ei_array = mem_query_size_resp_msg_data_v01_ei,
};
+/*
+ * This API creates ramdump dev handlers
+ * for each of the memshare clients.
+ * These dev handlers will be used for
+ * extracting the ramdump for loaned memory
+ * segments.
+ */
+
+static int mem_share_configure_ramdump(void)
+{
+ char client_name[18] = "memshare_";
+ char *clnt;
+
+ clnt = ((!num_clients) ? "GPS" : ((num_clients == 1) ? "FTM" : "DIAG"));
+ snprintf(client_name, 18, "memshare_%s", clnt);
+
+ memshare_ramdump_dev[num_clients] = create_ramdump_device(client_name,
+ NULL);
+ if (IS_ERR_OR_NULL(memshare_ramdump_dev[num_clients])) {
+ pr_err("memshare: %s: Unable to create memshare ramdump device.\n",
+ __func__);
+ memshare_ramdump_dev[num_clients] = NULL;
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
static int check_client(int client_id, int proc, int request)
{
- int i = 0;
+ int i = 0, rc;
int found = DHMS_MEM_CLIENT_INVALID;
for (i = 0; i < MAX_CLIENTS; i++) {
@@ -127,7 +157,7 @@ static int check_client(int client_id, int proc, int request)
}
}
if ((found == DHMS_MEM_CLIENT_INVALID) && !request) {
- pr_debug("No registered client, adding a new client\n");
+ pr_debug("memshare: No registered client, adding a new client\n");
/* Add a new client */
for (i = 0; i < MAX_CLIENTS; i++) {
if (memblock[i].client_id == DHMS_MEM_CLIENT_INVALID) {
@@ -136,6 +166,16 @@ static int check_client(int client_id, int proc, int request)
memblock[i].guarantee = 0;
memblock[i].peripheral = proc;
found = i;
+
+ if (!memblock[i].file_created) {
+ rc = mem_share_configure_ramdump();
+ if (rc)
+ pr_err("In %s, Cannot create ramdump for client: %d\n",
+ __func__, client_id);
+ else
+ memblock[i].file_created = 1;
+ }
+
break;
}
}
@@ -190,10 +230,63 @@ void initialize_client(void)
memblock[i].memory_type = MEMORY_CMA;
memblock[i].free_memory = 0;
memblock[i].hyp_mapping = 0;
+ memblock[i].file_created = 0;
}
dma_set_attr(DMA_ATTR_NO_KERNEL_MAPPING, &attrs);
}
+/*
+ * This API initializes the ramdump segments
+ * with the physical address and size of
+ * the memshared clients. Extraction of ramdump
+ * is skipped if memshare client is not alloted
+ * This calls the ramdump api in extracting the
+ * ramdump in elf format.
+ */
+
+static int mem_share_do_ramdump(void)
+{
+ int i = 0, ret;
+ char *client_name;
+
+ for (i = 0; i < num_clients; i++) {
+
+ struct ramdump_segment *ramdump_segments_tmp = NULL;
+
+ client_name = (i == 0) ? "GPS" :
+ ((i == 1) ? "FTM" : ((i == 2) ? "DIAG" : "NULL"));
+
+ if (!memblock[i].alloted) {
+ pr_err("memshare:%s memblock is not alloted\n",
+ client_name);
+ continue;
+ }
+
+ ramdump_segments_tmp = kcalloc(1,
+ sizeof(struct ramdump_segment),
+ GFP_KERNEL);
+ if (!ramdump_segments_tmp)
+ return -ENOMEM;
+
+ ramdump_segments_tmp[0].size = memblock[i].size;
+ ramdump_segments_tmp[0].address = memblock[i].phy_addr;
+
+ pr_debug("memshare: %s:%s client:phy_address = %llx, size = %d\n",
+ __func__, client_name,
+ (unsigned long long) memblock[i].phy_addr, memblock[i].size);
+
+ ret = do_elf_ramdump(memshare_ramdump_dev[i],
+ ramdump_segments_tmp, 1);
+ if (ret < 0) {
+ pr_err("memshare: Unable to dump: %d\n", ret);
+ kfree(ramdump_segments_tmp);
+ return ret;
+ }
+ kfree(ramdump_segments_tmp);
+ }
+ return 0;
+}
+
static int modem_notifier_cb(struct notifier_block *this, unsigned long code,
void *_cmd)
{
@@ -202,8 +295,10 @@ static int modem_notifier_cb(struct notifier_block *this, unsigned long code,
u32 source_vmlist[2] = {VMID_HLOS, VMID_MSS_MSA};
int dest_vmids[1] = {VMID_HLOS};
int dest_perms[1] = {PERM_READ|PERM_WRITE};
+ struct notif_data *notifdata = NULL;
mutex_lock(&memsh_drv->mem_share);
+
switch (code) {
case SUBSYS_BEFORE_SHUTDOWN:
@@ -260,6 +355,23 @@ static int modem_notifier_cb(struct notifier_block *this, unsigned long code,
bootup_request++;
break;
+ case SUBSYS_RAMDUMP_NOTIFICATION:
+
+ if (_cmd)
+ notifdata = (struct notif_data *) _cmd;
+ else
+ break;
+
+ if (!(notifdata->enable_ramdump)) {
+ pr_err("In %s, Ramdump collection is disabled\n",
+ __func__);
+ } else {
+ ret = mem_share_do_ramdump();
+ if (ret)
+ pr_err("Ramdump collection failed\n");
+ }
+ break;
+
default:
pr_debug("Memshare: code: %lu\n", code);
break;
@@ -800,6 +912,9 @@ static int memshare_child_probe(struct platform_device *pdev)
memblock[num_clients].size = size;
memblock[num_clients].client_id = client_id;
+ /*
+ * Memshare allocation for guaranteed clients
+ */
if (memblock[num_clients].guarantee) {
rc = memshare_alloc(memsh_child->dev,
memblock[num_clients].size,
@@ -812,6 +927,21 @@ static int memshare_child_probe(struct platform_device *pdev)
memblock[num_clients].alloted = 1;
}
+ /*
+ * call for creating ramdump dev handlers for
+ * memshare clients
+ */
+
+ if (!memblock[num_clients].file_created) {
+ rc = mem_share_configure_ramdump();
+ if (rc)
+ pr_err("In %s, cannot collect dumps for client id: %d\n",
+ __func__,
+ memblock[num_clients].client_id);
+ else
+ memblock[num_clients].file_created = 1;
+ }
+
num_clients++;
return 0;
diff --git a/drivers/soc/qcom/memshare/msm_memshare.h b/drivers/soc/qcom/memshare/msm_memshare.h
index 68a14390..39890753 100644
--- a/drivers/soc/qcom/memshare/msm_memshare.h
+++ b/drivers/soc/qcom/memshare/msm_memshare.h
@@ -51,6 +51,9 @@ struct mem_blocks {
uint8_t free_memory;
/* Need Hypervisor mapping*/
uint8_t hyp_mapping;
+ /* Status flag which checks if ramdump file is created*/
+ int file_created;
+
};
int memshare_alloc(struct device *dev,
diff --git a/drivers/soc/qcom/mmi-ram-info.c b/drivers/soc/qcom/mmi-ram-info.c
new file mode 100644
index 00000000..cc62a1a3
--- /dev/null
+++ b/drivers/soc/qcom/mmi-ram-info.c
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2012 Motorola Mobility LLC
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program 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.
+ */
+
+#include <linux/module.h>
+#include <soc/qcom/smsm.h>
+//#include <linux/pstore.h>
+
+static struct {
+ unsigned mr5;
+ unsigned mr6;
+ unsigned mr7;
+ unsigned mr8;
+ unsigned ramsize;
+} *smem_ddr_info;
+
+static char sysfsram_type_name[20] = "unknown";
+static char sysfsram_vendor_name[20] = "unknown";
+static uint32_t sysfsram_ramsize;
+
+static ssize_t sysfsram_mr_register_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ uint32_t val = 0;
+ const char *name = attr->attr.name;
+
+ if (smem_ddr_info != NULL &&
+ strnlen(name, 4) == 3 && name[0] == 'm' && name[1] == 'r')
+ {
+ switch (name[2]) {
+ case '5': val = smem_ddr_info->mr5; break;
+ case '6': val = smem_ddr_info->mr6; break;
+ case '7': val = smem_ddr_info->mr7; break;
+ case '8': val = smem_ddr_info->mr8; break;
+ }
+ }
+
+ return snprintf(buf, 6, "0x%02x\n", val);
+}
+
+static ssize_t sysfsram_size_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ return snprintf(buf, 12, "%u\n", sysfsram_ramsize);
+}
+
+static ssize_t sysfsram_info_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ return snprintf(buf, 60, "%s:%s:%uMB\n",
+ sysfsram_vendor_name,
+ sysfsram_type_name,
+ sysfsram_ramsize);
+}
+
+static ssize_t sysfsram_type_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ return snprintf(buf, 20, "%s\n", sysfsram_type_name);
+}
+
+static struct kobj_attribute ddr_mr5_register_attr =
+ __ATTR(mr5, 0444, sysfsram_mr_register_show, NULL);
+
+static struct kobj_attribute ddr_mr6_register_attr =
+ __ATTR(mr6, 0444, sysfsram_mr_register_show, NULL);
+
+static struct kobj_attribute ddr_mr7_register_attr =
+ __ATTR(mr7, 0444, sysfsram_mr_register_show, NULL);
+
+static struct kobj_attribute ddr_mr8_register_attr =
+ __ATTR(mr8, 0444, sysfsram_mr_register_show, NULL);
+
+static struct kobj_attribute ddr_size_attr =
+ __ATTR(size, 0444, sysfsram_size_show, NULL);
+
+static struct kobj_attribute ddr_type_attr =
+ __ATTR(type, 0444, sysfsram_type_show, NULL);
+
+static struct kobj_attribute ddr_info_attr =
+ __ATTR(info, 0444, sysfsram_info_show, NULL);
+
+static struct attribute *ram_info_properties_attrs[] = {
+ &ddr_mr5_register_attr.attr,
+ &ddr_mr6_register_attr.attr,
+ &ddr_mr7_register_attr.attr,
+ &ddr_mr8_register_attr.attr,
+ &ddr_size_attr.attr,
+ &ddr_type_attr.attr,
+ &ddr_info_attr.attr,
+ NULL
+};
+
+static struct attribute_group ram_info_properties_attr_group = {
+ .attrs = ram_info_properties_attrs,
+};
+
+static int __init init_mmi_ram_info(void)
+{
+ int status = 0;
+ static struct kobject *ram_info_properties_kobj;
+ uint32_t vid, tid;
+ const char *tname = "unknown";
+ const char *vname = "unknown";
+ static const char *vendors[] = {
+ "unknown",
+ "Samsung",
+ "Qimonda",
+ "Elpida",
+ "Etron",
+ "Nanya",
+ "Hynix",
+ "Mosel",
+ "Winbond",
+ "ESMT",
+ "unknown",
+ "Spansion",
+ "SST",
+ "ZMOS",
+ "Intel"
+ };
+ static const char *types[] = {
+ "S4 SDRAM",
+ "S2 SDRAM",
+ "N NVM",
+ "Reserved"
+ };
+
+ smem_ddr_info = smem_alloc(SMEM_SDRAM_INFO, sizeof(*smem_ddr_info), 0,
+ SMEM_ANY_HOST_FLAG);
+
+ if (smem_ddr_info != NULL) {
+ char apanic_annotation[128];
+
+ /* identify vendor */
+ vid = smem_ddr_info->mr5 & 0xFF;
+ if (vid < (sizeof(vendors)/sizeof(vendors[0])))
+ vname = vendors[vid];
+ else if (vid == 0xFE)
+ vname = "Numonyx";
+ else if (vid == 0xFF)
+ vname = "Micron";
+
+ snprintf(sysfsram_vendor_name, sizeof(sysfsram_vendor_name),
+ "%s", vname);
+
+ /* identify type */
+ tid = smem_ddr_info->mr8 & 0x03;
+ if (tid < (sizeof(types)/sizeof(types[0])))
+ tname = types[tid];
+
+ snprintf(sysfsram_type_name, sizeof(sysfsram_type_name),
+ "%s", tname);
+
+ /* extract size */
+ sysfsram_ramsize = smem_ddr_info->ramsize;
+
+ snprintf(apanic_annotation, sizeof(apanic_annotation),
+ "RAM: %s, %s, %u MB, MR5:0x%02X, MR6:0x%02X, "
+ "MR7:0x%02X, MR8:0x%02X\n",
+ vname, tname, smem_ddr_info->ramsize,
+ smem_ddr_info->mr5, smem_ddr_info->mr6,
+ smem_ddr_info->mr7, smem_ddr_info->mr8);
+ //pstore_annotate(apanic_annotation);
+ }
+ else {
+ /* complain, but do not fail if SMEM was not allocated */
+ /* defaults will be reported */
+ pr_err("%s: failed to access RAM info in SMEM\n", __func__);
+ }
+
+ /* create sysfs object */
+ ram_info_properties_kobj = kobject_create_and_add("ram", NULL);
+
+ if (ram_info_properties_kobj)
+ status = sysfs_create_group(ram_info_properties_kobj,
+ &ram_info_properties_attr_group);
+
+ if (!ram_info_properties_kobj || status) {
+ pr_err("%s: failed to create /sys/ram\n", __func__);
+ return 1;
+ }
+ else
+ return 0;
+}
+
+module_init(init_mmi_ram_info);
+MODULE_DESCRIPTION("Motorola Mobility Inc. RAM Info");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/soc/qcom/mmi-unit-info.c b/drivers/soc/qcom/mmi-unit-info.c
new file mode 100644
index 00000000..6cb9d1fb
--- /dev/null
+++ b/drivers/soc/qcom/mmi-unit-info.c
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2013 Motorola Mobility LLC
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program 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.
+ */
+
+#include <linux/module.h>
+#include <linux/seq_file.h>
+#include <asm/setup.h>
+#include <soc/qcom/bootinfo.h>
+
+#include <soc/qcom/smem.h>
+#include "mmi-unit-info.h"
+
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/slab.h>
+
+#ifndef CONFIG_ARM64
+#include <asm/mach/arch.h>
+#endif
+/* these are defined in kernel/setup.c for "arm" targets
+ but not used, so set 0
+*/
+unsigned int system_rev_local = 0;
+unsigned int system_serial_low_local = 0;
+unsigned int system_serial_high_local = 0;
+static u32 prod_id;
+
+#define SERIALNO_MAX_LEN 64
+static char serialno[SERIALNO_MAX_LEN];
+int __init board_serialno_init(char *s)
+{
+ strlcpy(serialno, s, SERIALNO_MAX_LEN);
+ return 1;
+}
+__setup("androidboot.serialno=", board_serialno_init);
+
+static char carrier[CARRIER_MAX_LEN];
+int __init board_carrier_init(char *s)
+{
+ strlcpy(carrier, s, SERIALNO_MAX_LEN);
+ return 1;
+}
+__setup("androidboot.carrier=", board_carrier_init);
+
+static char baseband[BASEBAND_MAX_LEN];
+int __init board_baseband_init(char *s)
+{
+ strlcpy(baseband, s, SERIALNO_MAX_LEN);
+ return 1;
+}
+__setup("androidboot.baseband=", board_baseband_init);
+
+#define ANDROIDBOOT_DEVICE_MAX_LEN 32
+static char androidboot_device[ANDROIDBOOT_DEVICE_MAX_LEN];
+int __init setup_androidboot_device_init(char *s)
+{
+ strlcpy(androidboot_device, s, ANDROIDBOOT_DEVICE_MAX_LEN);
+ return 1;
+}
+__setup("androidboot.device=", setup_androidboot_device_init);
+
+static unsigned int androidboot_radio;
+static char androidboot_radio_str[RADIO_MAX_LEN];
+int __init setup_androidboot_radio_init(char *s)
+{
+ int retval = kstrtouint(s, 16, &androidboot_radio);
+
+ if (retval < 0) {
+ androidboot_radio = 0;
+ }
+
+ strlcpy(androidboot_radio_str, s, RADIO_MAX_LEN);
+
+ return 1;
+}
+__setup("androidboot.radio=", setup_androidboot_radio_init);
+
+static struct mmi_unit_info *mui;
+void mmi_set_pureason(uint32_t val)
+{
+ if (mui) {
+ mui->powerup_reason = val;
+ pr_debug("%s: Set modem PU reason value in SMEM to %d\n",
+ __func__, mui->powerup_reason);
+ }
+}
+
+static char msm_hw[MSMHW_MAX_LEN+1];
+
+void mach_cpuinfo_show(struct seq_file *m, void *v)
+{
+ seq_printf(m, "Device\t\t: %s\n", androidboot_device);
+ /* Zero is not a valid "Radio" value. */
+ /* Lack of "Radio" entry in cpuinfo means: */
+ /* look for radio in "Revision" */
+ if (strnlen(androidboot_radio_str, RADIO_MAX_LEN))
+ seq_printf(m, "Radio\t\t: %s\n", androidboot_radio_str);
+
+ seq_printf(m, "MSM Hardware\t: %s\n", msm_hw);
+}
+
+static char extended_baseband[BASEBAND_MAX_LEN+1] = "\0";
+
+struct mmi_of_lookup {
+ const char *compatible;
+ unsigned int *data;
+};
+
+static struct mmi_of_lookup mmi_of_setup[] __initdata = {
+ { .compatible = "linux,seriallow", .data = &system_serial_low_local },
+ { .compatible = "linux,serialhigh", .data = &system_serial_high_local },
+ { .compatible = "linux,hwrev", .data = &system_rev_local },
+ { .compatible = "mmi,prod_id", .data = &prod_id },
+ { }
+};
+
+static void __init mmi_of_populate_setup(void)
+{
+ struct device_node *n = of_find_node_by_path("/chosen");
+ struct mmi_of_lookup *tbl = mmi_of_setup;
+ const char *baseband;
+ const char *temp;
+
+ while (tbl->data) {
+ of_property_read_u32(n, tbl->compatible, tbl->data);
+ tbl++;
+ }
+
+ if (0 == of_property_read_string(n, "mmi,baseband", &baseband))
+ strlcpy(extended_baseband, baseband, sizeof(extended_baseband));
+
+ if (0 == of_property_read_string(n, "mmi,msm_hw", &temp))
+ strlcpy(msm_hw, temp, sizeof(msm_hw));
+
+ of_node_put(n);
+}
+
+static int __init mmi_unit_info_init(void)
+{
+ int ret = 0;
+ struct mmi_unit_info *mui_copy;
+
+ mmi_of_populate_setup();
+
+ #define SMEM_KERNEL_RESERVE_SIZE 1024
+ mui_copy = kzalloc(SMEM_KERNEL_RESERVE_SIZE, GFP_KERNEL);
+ if (!mui_copy) {
+ pr_err("%s: failed to allocate space for mmi_unit_info\n",
+ __func__);
+ ret = 1;
+ goto err;
+ }
+
+ mui_copy->version = MMI_UNIT_INFO_VER;
+ mui_copy->system_rev = system_rev_local;
+ mui_copy->system_serial_low = system_serial_low_local;
+ mui_copy->system_serial_high = system_serial_high_local;
+#ifndef CONFIG_ARM64
+ strlcpy(mui_copy->machine, machine_desc->name, MACHINE_MAX_LEN);
+#else
+ strlcpy(mui_copy->machine, "", MACHINE_MAX_LEN);
+#endif
+ strlcpy(mui_copy->barcode, serialno, BARCODE_MAX_LEN);
+ strlcpy(mui_copy->baseband, extended_baseband, BASEBAND_MAX_LEN);
+ strlcpy(mui_copy->carrier, carrier, CARRIER_MAX_LEN);
+ strlcpy(mui_copy->device, androidboot_device, DEVICE_MAX_LEN);
+ mui_copy->radio = androidboot_radio;
+ strlcpy(mui_copy->radio_str, androidboot_radio_str, RADIO_MAX_LEN);
+ mui_copy->powerup_reason = bi_powerup_reason();
+
+ pr_info("mmi_unit_info (SMEM) for modem: version = 0x%02x,"
+ " device = '%s', radio = 0x%x, radio_str = '%s',"
+ " system_rev = 0x%04x, system_serial = 0x%08x%08x,"
+ " machine = '%s', barcode = '%s', baseband = '%s',"
+ " carrier = '%s', pu_reason = 0x%08x\n",
+ mui_copy->version,
+ mui_copy->device,
+ mui_copy->radio,
+ mui_copy->radio_str,
+ mui_copy->system_rev,
+ mui_copy->system_serial_high, mui_copy->system_serial_low,
+ mui_copy->machine, mui_copy->barcode,
+ mui_copy->baseband, mui_copy->carrier,
+ mui_copy->powerup_reason);
+
+ mui = (struct mmi_unit_info *) smem_alloc(SMEM_KERNEL_RESERVE,
+ SMEM_KERNEL_RESERVE_SIZE, 0, SMEM_ANY_HOST_FLAG);
+
+ if (!mui) {
+ pr_err("%s: failed to allocate mmi_unit_info in SMEM\n",
+ __func__);
+ ret = 1;
+ goto err_free;
+ } else if (PTR_ERR(mui_copy) == -EPROBE_DEFER) {
+ pr_err("%s: SMEM not yet initialized\n", __func__);
+ ret = 1;
+ goto err_free;
+ }
+
+ memcpy(mui, mui_copy, SMEM_KERNEL_RESERVE_SIZE);
+
+err_free:
+ kfree(mui_copy);
+err:
+ return ret;
+}
+
+module_init(mmi_unit_info_init);
+MODULE_DESCRIPTION("Motorola Mobility LLC. Unit Info");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/soc/qcom/mmi-unit-info.h b/drivers/soc/qcom/mmi-unit-info.h
new file mode 100644
index 00000000..d41b8ec1
--- /dev/null
+++ b/drivers/soc/qcom/mmi-unit-info.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2013 Motorola Mobility LLC
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program 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.
+ */
+#ifndef __ARCH_ARM_MACH_MSM_MMI_UNIT_INFO_H
+#define __ARCH_ARM_MACH_MSM_MMI_UNIT_INFO_H
+
+/* set of data provided to the modem over SMEM */
+#define MMI_UNIT_INFO_VER 3
+#define BARCODE_MAX_LEN 65
+#define MACHINE_MAX_LEN 33
+#define CARRIER_MAX_LEN 65
+#define BASEBAND_MAX_LEN 97
+#define MSMHW_MAX_LEN 32
+#define DEVICE_MAX_LEN 33
+#define RADIO_MAX_LEN 33
+struct mmi_unit_info {
+ uint32_t version;
+ uint32_t system_rev;
+ uint32_t system_serial_low;
+ uint32_t system_serial_high;
+ char machine[MACHINE_MAX_LEN];
+ char barcode[BARCODE_MAX_LEN];
+ char carrier[CARRIER_MAX_LEN];
+ char baseband[BASEBAND_MAX_LEN];
+ char device[DEVICE_MAX_LEN];
+ uint32_t radio;
+ uint32_t powerup_reason;
+ char radio_str[RADIO_MAX_LEN];
+};
+
+/* Function that sets the modem reset value in the SMEM location
+ * where mmi_unit_info is stored.
+ */
+void mmi_set_pureason(uint32_t val);
+
+#endif
diff --git a/drivers/soc/qcom/qdsp6v2/voice_svc.c b/drivers/soc/qcom/qdsp6v2/voice_svc.c
index 67c58d1e..50dd9256 100644
--- a/drivers/soc/qcom/qdsp6v2/voice_svc.c
+++ b/drivers/soc/qcom/qdsp6v2/voice_svc.c
@@ -223,8 +223,8 @@ static int voice_svc_send_req(struct voice_svc_cmd_request *apr_request,
} else if (!strcmp(apr_request->svc_name, VOICE_SVC_MVM_STR)) {
apr_handle = prtd->apr_q6_mvm;
} else {
- pr_err("%s: Invalid service %s\n", __func__,
- apr_request->svc_name);
+ pr_err("%s: Invalid service %.*s\n", __func__,
+ MAX_APR_SERVICE_NAME_LEN, apr_request->svc_name);
ret = -EINVAL;
goto done;
@@ -338,8 +338,8 @@ static int process_reg_cmd(struct voice_svc_register *apr_reg_svc,
svc = VOICE_SVC_CVS_STR;
handle = &prtd->apr_q6_cvs;
} else {
- pr_err("%s: Invalid Service: %s\n", __func__,
- apr_reg_svc->svc_name);
+ pr_err("%s: Invalid Service: %.*s\n", __func__,
+ MAX_APR_SERVICE_NAME_LEN, apr_reg_svc->svc_name);
ret = -EINVAL;
goto done;
}
@@ -365,7 +365,17 @@ static ssize_t voice_svc_write(struct file *file, const char __user *buf,
pr_debug("%s\n", __func__);
- data = kmalloc(count, GFP_KERNEL);
+ /*
+ * Check if enough memory is allocated to parse the message type.
+ * Will check there is enough to hold the payload later.
+ */
+ if (count >= sizeof(struct voice_svc_write_msg)) {
+ data = kmalloc(count, GFP_KERNEL);
+ } else {
+ pr_debug("%s: invalid data size\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
if (data == NULL) {
pr_err("%s: data kmalloc failed.\n", __func__);
@@ -383,7 +393,7 @@ static ssize_t voice_svc_write(struct file *file, const char __user *buf,
}
cmd = data->msg_type;
- prtd = (struct voice_svc_prvt *)file->private_data;
+ prtd = (struct voice_svc_prvt *) file->private_data;
if (prtd == NULL) {
pr_err("%s: prtd is NULL\n", __func__);
@@ -393,9 +403,13 @@ static ssize_t voice_svc_write(struct file *file, const char __user *buf,
switch (cmd) {
case MSG_REGISTER:
- if (count >=
- (sizeof(struct voice_svc_register) +
- sizeof(*data))) {
+ /*
+ * Check that count reflects the expected size to ensure
+ * sufficient memory was allocated. Since voice_svc_register
+ * has a static size, this should be exact.
+ */
+ if (count == (sizeof(struct voice_svc_write_msg) +
+ sizeof(struct voice_svc_register))) {
ret = process_reg_cmd(
(struct voice_svc_register *)data->payload, prtd);
if (!ret)
@@ -407,8 +421,13 @@ static ssize_t voice_svc_write(struct file *file, const char __user *buf,
}
break;
case MSG_REQUEST:
- if (count >= (sizeof(struct voice_svc_cmd_request) +
- sizeof(*data))) {
+ /*
+ * Check that count reflects the expected size to ensure
+ * sufficient memory was allocated. Since voice_svc_cmd_request
+ * has a variable size, check the minimum value count must be.
+ */
+ if (count >= (sizeof(struct voice_svc_write_msg) +
+ sizeof(struct voice_svc_cmd_request))) {
ret = voice_svc_send_req(
(struct voice_svc_cmd_request *)data->payload, prtd);
if (!ret)
diff --git a/drivers/soc/qcom/rpm-smd.c b/drivers/soc/qcom/rpm-smd.c
index 209e5c64..bb0be81d 100644
--- a/drivers/soc/qcom/rpm-smd.c
+++ b/drivers/soc/qcom/rpm-smd.c
@@ -523,7 +523,6 @@ static int msm_rpm_read_sleep_ack(void)
{
int ret;
char buf[MAX_ERR_BUFFER_SIZE] = {0};
- uint32_t msg_id;
if (glink_enabled)
ret = msm_rpm_glink_rx_poll(glink_data->glink_handle);
@@ -554,38 +553,18 @@ static int msm_rpm_read_sleep_ack(void)
return -EAGAIN;
ret = msm_rpm_read_smd_data(buf);
- if (!ret) {
- /* Mimic Glink behavior to ensure that the
- * data is read and the msg is removed from
- * the wait list. We should have gotten here
- * only when there are no drivers waiting on
- * ACKs. msm_rpm_get_entry_from_msg_id()
- * return non-NULL only then.
- */
- msg_id = msm_rpm_get_msg_id_from_ack(buf);
- msm_rpm_process_ack(msg_id, 0);
+ if (!ret)
ret = smd_is_pkt_avail(msm_rpm_data.ch_info);
- }
}
return ret;
}
-static void msm_rpm_flush_noack_messages(void)
-{
- while (!list_empty(&msm_rpm_wait_list)) {
- if (!msm_rpm_read_sleep_ack())
- break;
- }
-}
-
static int msm_rpm_flush_requests(bool print)
{
struct rb_node *t;
int ret;
int count = 0;
- msm_rpm_flush_noack_messages();
-
for (t = rb_first(&tr_root); t; t = rb_next(t)) {
struct slp_buf *s = rb_entry(t, struct slp_buf, node);
@@ -844,18 +823,14 @@ static void msm_rpm_notify(void *data, unsigned event)
bool msm_rpm_waiting_for_ack(void)
{
- bool ret = false;
+ bool ret;
unsigned long flags;
- struct msm_rpm_wait_data *elem = NULL;
spin_lock_irqsave(&msm_rpm_list_lock, flags);
- elem = list_first_entry_or_null(&msm_rpm_wait_list,
- struct msm_rpm_wait_data, list);
- if (elem)
- ret = !elem->delete_on_ack;
+ ret = list_empty(&msm_rpm_wait_list);
spin_unlock_irqrestore(&msm_rpm_list_lock, flags);
- return ret;
+ return !ret;
}
static struct msm_rpm_wait_data *msm_rpm_get_entry_from_msg_id(uint32_t msg_id)
diff --git a/drivers/soc/qcom/rpm_stats.c b/drivers/soc/qcom/rpm_stats.c
index ec5f2303..10a1d5b6 100644
--- a/drivers/soc/qcom/rpm_stats.c
+++ b/drivers/soc/qcom/rpm_stats.c
@@ -56,7 +56,13 @@ struct msm_rpmstats_private_data {
u32 num_records;
u32 read_idx;
u32 len;
+/* lenovo.sw begin chenyb1 add for adding master vote info in rpm_state */
+#ifdef CONFIG_LENOVO_PM_LOG
+ char buf[480];
+#else
char buf[320];
+#endif
+/* lenovo.sw end chenyb1 add for adding master vote info in rpm_state */
struct msm_rpmstats_platform_data *platform_data;
};
@@ -107,6 +113,20 @@ static inline int msm_rpmstats_append_data_to_buf(char *buf,
time_since_last_mode = get_time_in_sec(time_since_last_mode);
actual_last_sleep = get_time_in_msec(data->accumulated);
+/* lenovo.sw begin chenyb1 add for adding master vote info in rpm_state */
+#ifdef CONFIG_LENOVO_PM_LOG
+ return snprintf(buf , buflength,
+ "RPM Mode:%s\n\t count:%d\ntime in last mode(msec):%llu\n"
+ "time since last mode(sec):%llu\nactual last sleep(msec):%llu\n"
+ "client votes: %#010x\n"
+ "master[0,1]: 0x%08x\n"
+ "master[2,3]: 0x%08x\n"
+ "master[4,5]: 0x%08x\n\n",
+ stat_type, data->count, time_in_last_mode,
+ time_since_last_mode, actual_last_sleep,
+ data->client_votes,
+ data->reserved[0], data->reserved[1], data->reserved[2]);
+#else
return snprintf(buf , buflength,
"RPM Mode:%s\n\t count:%d\ntime in last mode(msec):%llu\n"
"time since last mode(sec):%llu\nactual last sleep(msec):%llu\n"
@@ -114,6 +134,8 @@ static inline int msm_rpmstats_append_data_to_buf(char *buf,
stat_type, data->count, time_in_last_mode,
time_since_last_mode, actual_last_sleep,
data->client_votes);
+#endif
+/* lenovo.sw begin chenyb1 add for adding master vote info in rpm_state */
}
static inline u32 msm_rpmstats_read_long_register_v2(void __iomem *regbase,
@@ -162,6 +184,19 @@ static inline int msm_rpmstats_copy_stats_v2(
data.client_votes = msm_rpmstats_read_long_register_v2(reg,
i, offsetof(struct msm_rpm_stats_data_v2,
client_votes));
+/* lenovo.sw begin chenyb1 add for adding master vote info in rpm_state */
+#ifdef CONFIG_LENOVO_PM_LOG
+ data.reserved[0] = msm_rpmstats_read_long_register_v2(reg,
+ i, offsetof(struct msm_rpm_stats_data_v2,
+ reserved));
+ data.reserved[1] = msm_rpmstats_read_long_register_v2(reg,
+ i, offsetof(struct msm_rpm_stats_data_v2,
+ reserved) + 4);
+ data.reserved[2] = msm_rpmstats_read_long_register_v2(reg,
+ i, offsetof(struct msm_rpm_stats_data_v2,
+ reserved) + 8);
+#endif
+/* lenovo.sw end chenyb1 add for adding master vote info in rpm_state */
length += msm_rpmstats_append_data_to_buf(prvdata->buf + length,
&data, sizeof(prvdata->buf) - length);
prvdata->read_idx++;
@@ -311,6 +346,46 @@ static int msm_rpmstats_file_close(struct inode *inode, struct file *file)
return 0;
}
+/* lenovo.sw begin chenyb1 add for adding master vote info in rpm_state */
+#ifdef CONFIG_LENOVO_PM_LOG
+struct msm_rpmstats_platform_data *rpmstats = NULL;
+
+void msm_rpmstats_get_reverved(u32 reserved[][4])
+{
+ void __iomem *reg_base;
+ int i;
+
+ if(rpmstats == NULL) {
+ pr_err("%s: rpm stats has not been initialized\n", __func__);
+ return;
+ }
+
+ reg_base = ioremap_nocache(rpmstats->phys_addr_base,
+ rpmstats->phys_size);
+ if (reg_base == NULL) {
+ pr_err("%s: ERROR could not ioremap start=%p, len=%u\n",
+ __func__, (void *)rpmstats->phys_addr_base,
+ rpmstats->phys_size);
+ return;
+ }
+
+ for(i = 0; i < 2; i++) {
+ reserved[i][0] = msm_rpmstats_read_long_register_v2(reg_base,
+ i, offsetof(struct msm_rpm_stats_data_v2,
+ reserved));
+ reserved[i][1] = msm_rpmstats_read_long_register_v2(reg_base,
+ i, offsetof(struct msm_rpm_stats_data_v2,
+ reserved) + 4);
+ reserved[i][2] = msm_rpmstats_read_long_register_v2(reg_base,
+ i, offsetof(struct msm_rpm_stats_data_v2,
+ reserved) + 8);
+ }
+
+ iounmap(reg_base);
+}
+#endif
+/* lenovo.sw end chenyb1 add for adding master vote info in rpm_state */
+
static const struct file_operations msm_rpmstats_fops = {
.owner = THIS_MODULE,
.open = msm_rpmstats_file_open,
@@ -528,6 +603,11 @@ static int msm_rpmstats_probe(struct platform_device *pdev)
msm_rpmstats_create_sysfs(pdata);
platform_set_drvdata(pdev, dent);
+/* lenovo.sw begin chenyb1 add for adding master vote info in rpm_state */
+#ifdef CONFIG_LENOVO_PM_LOG
+ rpmstats = pdata;
+#endif
+/* lenovo.sw end chenyb1 add for adding master vote info in rpm_state */
return 0;
}
@@ -539,6 +619,11 @@ static int msm_rpmstats_remove(struct platform_device *pdev)
debugfs_remove(dent);
debugfs_remove(heap_dent);
platform_set_drvdata(pdev, NULL);
+/* lenovo.sw begin chenyb1 add for adding master vote info in rpm_state */
+#ifdef CONFIG_LENOVO_PM_LOG
+ rpmstats = NULL;
+#endif
+/* lenovo.sw end chenyb1 add for adding master vote info in rpm_state */
return 0;
}
diff --git a/drivers/soc/qcom/scm.c b/drivers/soc/qcom/scm.c
index 16115619..f958d98e 100644
--- a/drivers/soc/qcom/scm.c
+++ b/drivers/soc/qcom/scm.c
@@ -662,10 +662,6 @@ int scm_call2(u32 fn_id, struct scm_desc *desc)
desc->ret[0] = desc->ret[1] = desc->ret[2] = 0;
- pr_debug("scm_call: func id %#llx, args: %#x, %#llx, %#llx, %#llx, %#llx\n",
- x0, desc->arginfo, desc->args[0], desc->args[1],
- desc->args[2], desc->x5);
-
trace_scm_call_start(x0, desc);
if (scm_version == SCM_ARMV8_64)
@@ -695,10 +691,8 @@ int scm_call2(u32 fn_id, struct scm_desc *desc)
} while (ret == SCM_V2_EBUSY && (retry_count++ < SCM_EBUSY_MAX_RETRY));
if (ret < 0)
- pr_err("scm_call failed: func id %#llx, arginfo: %#x, args: %#llx, %#llx, %#llx, %#llx, ret: %d, syscall returns: %#llx, %#llx, %#llx\n",
- x0, desc->arginfo, desc->args[0], desc->args[1],
- desc->args[2], desc->x5, ret, desc->ret[0],
- desc->ret[1], desc->ret[2]);
+ pr_err("scm_call failed: func id %#llx, ret: %d, syscall returns: %#llx, %#llx, %#llx\n",
+ x0, ret, desc->ret[0], desc->ret[1], desc->ret[2]);
if (arglen > N_REGISTER_ARGS)
kfree(desc->extra_arg_buf);
diff --git a/drivers/soc/qcom/smd.c b/drivers/soc/qcom/smd.c
index d4efd42c..a8a284ff 100644
--- a/drivers/soc/qcom/smd.c
+++ b/drivers/soc/qcom/smd.c
@@ -548,6 +548,8 @@ struct smd_shared_word_access {
/**
* Maps edge type to local and remote processor ID's.
*/
+/* lenovo.sw begin chenyb1 add for adding master vote info in rpm_state */
+#ifdef CONFIG_LENOVO_PM_LOG
static struct edge_to_pid edge_to_pids[] = {
[SMD_APPS_MODEM] = {SMD_APPS, SMD_MODEM, "modem"},
[SMD_APPS_QDSP] = {SMD_APPS, SMD_Q6, "adsp"},
@@ -564,13 +566,39 @@ static struct edge_to_pid edge_to_pids[] = {
[SMD_QDSP_Q6FW] = {SMD_Q6, SMD_MODEM_Q6_FW},
[SMD_DSPS_Q6FW] = {SMD_DSPS, SMD_MODEM_Q6_FW},
[SMD_WCNSS_Q6FW] = {SMD_WCNSS, SMD_MODEM_Q6_FW},
- [SMD_APPS_RPM] = {SMD_APPS, SMD_RPM},
+ [SMD_APPS_RPM] = {SMD_APPS, SMD_RPM, "rpm"},
[SMD_MODEM_RPM] = {SMD_MODEM, SMD_RPM},
[SMD_QDSP_RPM] = {SMD_Q6, SMD_RPM},
[SMD_WCNSS_RPM] = {SMD_WCNSS, SMD_RPM},
[SMD_TZ_RPM] = {SMD_TZ, SMD_RPM},
};
+
+#else
+static struct edge_to_pid edge_to_pids[] = {
+ [SMD_APPS_MODEM] = {SMD_APPS, SMD_MODEM, "modem"},
+ [SMD_APPS_QDSP] = {SMD_APPS, SMD_Q6, "adsp"},
+ [SMD_MODEM_QDSP] = {SMD_MODEM, SMD_Q6},
+ [SMD_APPS_DSPS] = {SMD_APPS, SMD_DSPS, "dsps"},
+ [SMD_MODEM_DSPS] = {SMD_MODEM, SMD_DSPS},
+ [SMD_QDSP_DSPS] = {SMD_Q6, SMD_DSPS},
+ [SMD_APPS_WCNSS] = {SMD_APPS, SMD_WCNSS, "wcnss"},
+ [SMD_MODEM_WCNSS] = {SMD_MODEM, SMD_WCNSS},
+ [SMD_QDSP_WCNSS] = {SMD_Q6, SMD_WCNSS},
+ [SMD_DSPS_WCNSS] = {SMD_DSPS, SMD_WCNSS},
+ [SMD_APPS_Q6FW] = {SMD_APPS, SMD_MODEM_Q6_FW},
+ [SMD_MODEM_Q6FW] = {SMD_MODEM, SMD_MODEM_Q6_FW},
+ [SMD_QDSP_Q6FW] = {SMD_Q6, SMD_MODEM_Q6_FW},
+ [SMD_DSPS_Q6FW] = {SMD_DSPS, SMD_MODEM_Q6_FW},
+ [SMD_WCNSS_Q6FW] = {SMD_WCNSS, SMD_MODEM_Q6_FW},
+ [SMD_APPS_RPM] = {SMD_APPS, SMD_RPM},
+ [SMD_MODEM_RPM] = {SMD_MODEM, SMD_RPM},
+ [SMD_QDSP_RPM] = {SMD_Q6, SMD_RPM},
+ [SMD_WCNSS_RPM] = {SMD_WCNSS, SMD_RPM},
+ [SMD_TZ_RPM] = {SMD_TZ, SMD_RPM},
+};
+#endif
+/* lenovo.sw begin chenyb1 add for adding master vote info in rpm_state */
struct restart_notifier_block {
unsigned processor;
char *name;
diff --git a/drivers/soc/qcom/smp2p.c b/drivers/soc/qcom/smp2p.c
index fc5688b4..79b8ffbc 100644
--- a/drivers/soc/qcom/smp2p.c
+++ b/drivers/soc/qcom/smp2p.c
@@ -1,6 +1,6 @@
/* drivers/soc/qcom/smp2p.c
*
- * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -519,8 +519,8 @@ static void smp2p_find_entry_v1(struct smp2p_smem __iomem *item,
char entry_name[SMP2P_MAX_ENTRY_NAME];
if (!item || !name || !entry_ptr) {
- SMP2P_ERR("%s: invalid arguments %p, %p, %p\n",
- __func__, item, name, entry_ptr);
+ SMP2P_ERR("%s: invalid arguments %d %d %d\n",
+ __func__, !item, !name, !entry_ptr);
return;
}
diff --git a/drivers/soc/qcom/smp2p_debug.c b/drivers/soc/qcom/smp2p_debug.c
index 4deb05a0..8d98d07c 100644
--- a/drivers/soc/qcom/smp2p_debug.c
+++ b/drivers/soc/qcom/smp2p_debug.c
@@ -1,6 +1,6 @@
/* drivers/soc/qcom/smp2p_debug.c
*
- * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2014,2016 The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -41,7 +41,7 @@ static void smp2p_int_stats(struct seq_file *s)
pid != SMP2P_REMOTE_MOCK_PROC)
continue;
- seq_printf(s, "| %5s (%d) | %11u | %10u | %10u | %p | %08x |\n",
+ seq_printf(s, "| %5s (%d) | %11u | %10u | %10u | %pK | %08x |\n",
int_cfg[pid].name,
pid, int_cfg[pid].in_int_id,
int_cfg[pid].in_interrupt_count,
diff --git a/drivers/soc/qcom/smp2p_test_common.h b/drivers/soc/qcom/smp2p_test_common.h
index 747a812d..3be519bc 100644
--- a/drivers/soc/qcom/smp2p_test_common.h
+++ b/drivers/soc/qcom/smp2p_test_common.h
@@ -1,6 +1,6 @@
/* drivers/soc/qcom/smp2p_test_common.h
*
- * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2014,2016 The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -49,7 +49,8 @@
void *a_tmp = (a); \
void *b_tmp = (b); \
if (!((a_tmp)cmp(b_tmp))) { \
- seq_printf(s, "%s:%d Fail: " #a "(%p) " #cmp " " #b "(%p)\n", \
+ seq_printf(s, "%s:%d Fail: " #a "(%pK) " #cmp \
+ " " #b "(%pK)\n", \
__func__, __LINE__, \
a_tmp, b_tmp); \
failed = 1; \
diff --git a/drivers/soc/qcom/subsys-pil-tz.c b/drivers/soc/qcom/subsys-pil-tz.c
index f98cfbc8..19a306d4 100644
--- a/drivers/soc/qcom/subsys-pil-tz.c
+++ b/drivers/soc/qcom/subsys-pil-tz.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -25,6 +25,7 @@
#include <linux/msm-bus-board.h>
#include <linux/msm-bus.h>
#include <linux/dma-mapping.h>
+#include <linux/highmem.h>
#include <soc/qcom/subsystem_restart.h>
#include <soc/qcom/ramdump.h>
@@ -613,6 +614,10 @@ static int pil_init_image_trusted(struct pil_desc *pil,
return -ENOMEM;
}
+ /* Make sure there are no mappings in PKMAP and fixmap */
+ kmap_flush_unused();
+ kmap_atomic_flush_unused();
+
memcpy(mdata_buf, metadata, size);
request.proc = d->pas_id;
diff --git a/drivers/soc/qcom/subsystem_restart.c b/drivers/soc/qcom/subsystem_restart.c
index 5fc22ce2..e0d566cb 100644
--- a/drivers/soc/qcom/subsystem_restart.c
+++ b/drivers/soc/qcom/subsystem_restart.c
@@ -1640,6 +1640,13 @@ struct subsys_device *subsys_register(struct subsys_desc *desc)
strlcpy(subsys->desc->fw_name, desc->name,
sizeof(subsys->desc->fw_name));
+//lenovo sw, yexh1 add for set the modem resart level to RELATED at init
+ if (!strcmp(desc->name, "modem")){
+ subsys->restart_level = RESET_SUBSYS_COUPLED;
+ pr_info("set the %s restart level to RELATED'\n", desc->name);
+ }
+//lenovo sw, yexh1 end
+
subsys->notify = subsys_notif_add_subsys(desc->name);
snprintf(subsys->wlname, sizeof(subsys->wlname), "ssr(%s)", desc->name);
diff --git a/drivers/soc/qcom/tracer_pkt_private.h b/drivers/soc/qcom/tracer_pkt_private.h
index fc760e6b..79e0bd3c 100644
--- a/drivers/soc/qcom/tracer_pkt_private.h
+++ b/drivers/soc/qcom/tracer_pkt_private.h
@@ -15,7 +15,7 @@
#undef TRACE_SYSTEM
#define TRACE_SYSTEM tracer_pkt
#undef TRACE_INCLUDE_PATH
-#define TRACE_INCLUDE_PATH .
+#define TRACE_INCLUDE_PATH ../../drivers/soc/qcom
#undef TRACE_INCLUDE_FILE
#define TRACE_INCLUDE_FILE tracer_pkt_private
diff --git a/drivers/soc/qcom/unlock_bl/Kconfig b/drivers/soc/qcom/unlock_bl/Kconfig
new file mode 100644
index 00000000..14f57245
--- /dev/null
+++ b/drivers/soc/qcom/unlock_bl/Kconfig
@@ -0,0 +1,5 @@
+config LENOVO_UNLOCK_BL
+ bool "lenovo_unlock_bl"
+ help
+ This driver helps in mitigating unlock code from LK into proc system.
+ Say Y here could be enabled with DTS parsing and copy data into userspace.
diff --git a/drivers/soc/qcom/unlock_bl/Makefile b/drivers/soc/qcom/unlock_bl/Makefile
new file mode 100644
index 00000000..899eb8b3
--- /dev/null
+++ b/drivers/soc/qcom/unlock_bl/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_LENOVO_UNLOCK_BL) += unlock_bl.o
diff --git a/drivers/soc/qcom/unlock_bl/unlock_bl.c b/drivers/soc/qcom/unlock_bl/unlock_bl.c
new file mode 100644
index 00000000..82ac0b8c
--- /dev/null
+++ b/drivers/soc/qcom/unlock_bl/unlock_bl.c
@@ -0,0 +1,150 @@
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/proc_fs.h>
+#include <linux/mm.h>
+#include <linux/err.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <linux/device.h>
+#include <linux/of_fdt.h>
+#include <asm/setup.h>
+#include <linux/seq_file.h>
+#include <asm/setup.h>
+#include <soc/qcom/bootinfo.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#ifndef CONFIG_ARM64
+#include <asm/mach/arch.h>
+#endif
+
+
+#define UNLOCK_CODE_LEN 256
+#define PUBK_SHA1_FP_SIZE 60
+//#define DUMP_UNLOCK_CODE
+
+static char unlock_code_string[2*UNLOCK_CODE_LEN + PUBK_SHA1_FP_SIZE + 1] = "\0";
+struct proc_dir_entry * sec_unlock_entry = NULL;
+
+
+#ifdef DUMP_UNLOCK_CODE
+static void dump_unlock_code(char *src, char *dst, int src_len, int dst_len)
+{
+
+ int i = 0;
+ char temp[3] = {0};
+
+ if (dst_len <= src_len * 2) {
+ pr_err("[Unlock BL]:%s\tinput length exceeds the output one\n", __func__);
+ return;
+ }
+
+ for (i = 0; i < src_len; i++) {
+ sprintf(temp, "%02x", (unsigned char)src[i]);
+ memcpy(&dst[ i*2 ], temp, 2);
+ }
+
+ printk("[Unlock BL]:Dump Hex str %d length is:%s\n", (int)strlen(dst), dst);
+ return;
+}
+#endif
+
+ssize_t sec_unlock_proc_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
+{
+
+ int len = strlen(unlock_code_string);
+#ifdef DUMP_UNLOCK_CODE
+ char hex_buff[2*UNLOCK_CODE_LEN + 1] = {0};
+#endif
+ if (*f_pos > 0) {
+ printk("[Unlock BL]: Need to locate correct position\n");
+ *f_pos = 0;
+ return -EINVAL;
+ }
+
+#ifdef DUMP_UNLOCK_CODE
+ /* Note the original data in the unlock buffer where passed from LK is binary formatter.
+ * Nevertheless it's without meaning since encrypted data may contain '\0' as terminator.
+ */
+ printk("[Unlock BL]: data from LK corruption with real len:(%d)\n", len);
+ dump_unlock_code(unlock_code_string, hex_buff, UNLOCK_CODE_LEN, 2*UNLOCK_CODE_LEN +1);
+
+#endif
+
+ if (copy_to_user((char*)buf, unlock_code_string, len)) {
+ pr_err("[Unlock_BL]: Copy data to userspace error\n");
+ return -EFAULT;
+ }
+
+ *f_pos += len;
+ return len;
+
+}
+
+ssize_t sec_unlock_proc_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)
+{
+ /* Dont allow to write or modification this memory. */
+ return 0;
+}
+
+ssize_t sec_unlock_proc_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos);
+ssize_t sec_unlock_proc_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos);
+
+static struct file_operations unlock_file_fops = {
+ .read = sec_unlock_proc_read,
+ .write = sec_unlock_proc_write,
+};
+
+
+static int __init unlock_bl_of_populate_setup(void)
+{
+ int ret = -1;
+ struct device_node *n = of_find_node_by_path("/chosen");
+ const char *unlock_code = NULL;
+
+ ret = of_property_read_string(n, "mmi,unlock_code", &unlock_code);
+ if (ret == 0) {
+ strlcpy(unlock_code_string, unlock_code, sizeof(unlock_code_string));
+ }
+
+ of_node_put(n);
+
+ return ret;
+}
+
+
+static int __init proc_unlock_init(void)
+{
+ int ret = 0;
+ ret = unlock_bl_of_populate_setup();
+ if (ret != 0) {
+
+ pr_err("[Unlock BL]: Unable to acquire the mmi unlock_code\n\r");
+ return -1;
+ }
+
+
+ sec_unlock_entry = proc_create("sec_unlock_code", 0664, NULL, &unlock_file_fops);
+ if (sec_unlock_entry == NULL) {
+ pr_err("[Unlock BL]: Unable to create /proc entry\n\r");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static void __exit proc_unlock_exit(void) {
+
+ if (NULL != sec_unlock_entry) {
+ proc_remove(sec_unlock_entry);
+ }
+}
+
+
+module_init(proc_unlock_init);
+module_exit(proc_unlock_exit);
+MODULE_DESCRIPTION("Lenovo Mobile Group. Unlock code");
+MODULE_LICENSE("GPL v2");
+