1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
|
/* Copyright (c) 2014-2015, 2017, 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 <asm/cacheflush.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/init.h>
#include <linux/export.h>
#include <linux/err.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/kmemleak.h>
#include <soc/qcom/memory_dump.h>
#include <soc/qcom/scm.h>
#include <soc/qcom/minidump.h>
#define MSM_DUMP_TABLE_VERSION MSM_DUMP_MAKE_VERSION(2, 0)
#define SCM_CMD_DEBUG_LAR_UNLOCK 0x4
struct msm_dump_table {
uint32_t version;
uint32_t num_entries;
struct msm_dump_entry entries[MAX_NUM_ENTRIES];
};
struct msm_memory_dump {
uint64_t table_phys;
struct msm_dump_table *table;
};
static struct msm_memory_dump memdump;
uint32_t msm_dump_table_version(void)
{
return MSM_DUMP_TABLE_VERSION;
}
EXPORT_SYMBOL(msm_dump_table_version);
static int msm_dump_table_register(struct msm_dump_entry *entry)
{
struct msm_dump_entry *e;
struct msm_dump_table *table = memdump.table;
if (!table || table->num_entries >= MAX_NUM_ENTRIES)
return -EINVAL;
e = &table->entries[table->num_entries];
e->id = entry->id;
e->type = MSM_DUMP_TYPE_TABLE;
e->addr = entry->addr;
table->num_entries++;
dmac_flush_range(table, (void *)table + sizeof(struct msm_dump_table));
return 0;
}
static struct msm_dump_table *msm_dump_get_table(enum msm_dump_table_ids id)
{
struct msm_dump_table *table = memdump.table;
int i;
if (!table) {
pr_err("mem dump base table does not exist\n");
return ERR_PTR(-EINVAL);
}
for (i = 0; i < MAX_NUM_ENTRIES; i++) {
if (table->entries[i].id == id)
break;
}
if (i == MAX_NUM_ENTRIES || !table->entries[i].addr) {
pr_err("mem dump base table entry %d invalid\n", id);
return ERR_PTR(-EINVAL);
}
/* Get the apps table pointer */
table = phys_to_virt(table->entries[i].addr);
return table;
}
int msm_dump_data_add_minidump(struct msm_dump_entry *entry)
{
struct msm_dump_data *data;
struct md_region md_entry;
data = (struct msm_dump_data *)(phys_to_virt(entry->addr));
if (!strcmp(data->name, "")) {
pr_debug("Entry name is NULL, Use ID %d for minidump\n",
entry->id);
snprintf(md_entry.name, sizeof(md_entry.name), "KMDT0x%X",
entry->id);
} else {
strlcpy(md_entry.name, data->name, sizeof(md_entry.name));
}
md_entry.phys_addr = data->addr;
md_entry.virt_addr = (uintptr_t)phys_to_virt(data->addr);
md_entry.size = data->len;
md_entry.id = entry->id;
return msm_minidump_add_region(&md_entry);
}
int msm_dump_data_register(enum msm_dump_table_ids id,
struct msm_dump_entry *entry)
{
struct msm_dump_entry *e;
struct msm_dump_table *table;
table = msm_dump_get_table(id);
if (IS_ERR(table))
return PTR_ERR(table);
if (!table || table->num_entries >= MAX_NUM_ENTRIES)
return -EINVAL;
e = &table->entries[table->num_entries];
e->id = entry->id;
e->type = MSM_DUMP_TYPE_DATA;
e->addr = entry->addr;
table->num_entries++;
dmac_flush_range(table, (void *)table + sizeof(struct msm_dump_table));
if (msm_dump_data_add_minidump(entry))
pr_err("Failed to add entry in Minidump table\n");
return 0;
}
EXPORT_SYMBOL(msm_dump_data_register);
static int __init init_memory_dump(void)
{
struct msm_dump_table *table;
struct msm_dump_entry entry;
struct device_node *np;
void __iomem *imem_base;
int ret;
np = of_find_compatible_node(NULL, NULL,
"qcom,msm-imem-mem_dump_table");
if (!np) {
pr_err("mem dump base table DT node does not exist\n");
return -ENODEV;
}
imem_base = of_iomap(np, 0);
if (!imem_base) {
pr_err("mem dump base table imem offset mapping failed\n");
return -ENOMEM;
}
memdump.table = kzalloc(sizeof(struct msm_dump_table), GFP_KERNEL);
if (!memdump.table) {
pr_err("mem dump base table allocation failed\n");
ret = -ENOMEM;
goto err0;
}
memdump.table->version = MSM_DUMP_TABLE_VERSION;
memdump.table_phys = virt_to_phys(memdump.table);
memcpy_toio(imem_base, &memdump.table_phys, sizeof(memdump.table_phys));
/* Ensure write to imem_base is complete before unmapping */
mb();
pr_info("MSM Memory Dump base table set up\n");
iounmap(imem_base);
table = kzalloc(sizeof(struct msm_dump_table), GFP_KERNEL);
if (!table) {
pr_err("mem dump apps data table allocation failed\n");
ret = -ENOMEM;
goto err1;
}
kmemleak_not_leak(table);
table->version = MSM_DUMP_TABLE_VERSION;
entry.id = MSM_DUMP_TABLE_APPS;
entry.addr = virt_to_phys(table);
ret = msm_dump_table_register(&entry);
if (ret) {
pr_info("mem dump apps data table register failed\n");
goto err2;
}
pr_info("MSM Memory Dump apps data table set up\n");
return 0;
err2:
kfree(table);
err1:
kfree(memdump.table);
return ret;
err0:
iounmap(imem_base);
return ret;
}
early_initcall(init_memory_dump);
#ifdef CONFIG_MSM_DEBUG_LAR_UNLOCK
static int __init init_debug_lar_unlock(void)
{
int ret;
uint32_t argument = 0;
struct scm_desc desc = {0};
if (!is_scm_armv8())
ret = scm_call(SCM_SVC_TZ, SCM_CMD_DEBUG_LAR_UNLOCK, &argument,
sizeof(argument), NULL, 0);
else
ret = scm_call2(SCM_SIP_FNID(SCM_SVC_TZ,
SCM_CMD_DEBUG_LAR_UNLOCK), &desc);
if (ret)
pr_err("Core Debug Lock unlock failed, ret: %d\n", ret);
else
pr_info("Core Debug Lock unlocked\n");
return ret;
}
early_initcall(init_debug_lar_unlock);
#endif
|