1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * Copyright (c) 2017-2021, The Linux Foundation. All rights reserved.
4*4882a593Smuzhiyun * Copyright (c) 2023 Rockchip Electronics Co., Ltd.
5*4882a593Smuzhiyun */
6*4882a593Smuzhiyun
7*4882a593Smuzhiyun #define pr_fmt(fmt) "Minidump: " fmt
8*4882a593Smuzhiyun
9*4882a593Smuzhiyun #include <linux/init.h>
10*4882a593Smuzhiyun #include <linux/export.h>
11*4882a593Smuzhiyun #include <linux/kernel.h>
12*4882a593Smuzhiyun #include <linux/module.h>
13*4882a593Smuzhiyun #include <linux/of.h>
14*4882a593Smuzhiyun #include <linux/platform_device.h>
15*4882a593Smuzhiyun #include <linux/err.h>
16*4882a593Smuzhiyun #include <linux/elf.h>
17*4882a593Smuzhiyun #include <linux/errno.h>
18*4882a593Smuzhiyun #include <linux/string.h>
19*4882a593Smuzhiyun #include <linux/slab.h>
20*4882a593Smuzhiyun #include <linux/android_debug_symbols.h>
21*4882a593Smuzhiyun #include <linux/soc/qcom/smem.h>
22*4882a593Smuzhiyun #include <soc/rockchip/rk_minidump.h>
23*4882a593Smuzhiyun #include <linux/of_address.h>
24*4882a593Smuzhiyun #include <linux/proc_fs.h>
25*4882a593Smuzhiyun #include <asm/cacheflush.h>
26*4882a593Smuzhiyun #include "minidump_private.h"
27*4882a593Smuzhiyun #include "elf.h"
28*4882a593Smuzhiyun
29*4882a593Smuzhiyun #define MAX_NUM_ENTRIES (CONFIG_ROCKCHIP_MINIDUMP_MAX_ENTRIES + 1)
30*4882a593Smuzhiyun #define MAX_STRTBL_SIZE (MAX_NUM_ENTRIES * MAX_REGION_NAME_LENGTH)
31*4882a593Smuzhiyun
32*4882a593Smuzhiyun /**
33*4882a593Smuzhiyun * md_table : Local Minidump toc holder
34*4882a593Smuzhiyun * @num_regions : Number of regions requested
35*4882a593Smuzhiyun * @md_ss_toc : HLOS toc pointer
36*4882a593Smuzhiyun * @md_gbl_toc : Global toc pointer
37*4882a593Smuzhiyun * @md_regions : HLOS regions base pointer
38*4882a593Smuzhiyun * @entry : array of HLOS regions requested
39*4882a593Smuzhiyun */
40*4882a593Smuzhiyun struct md_table {
41*4882a593Smuzhiyun u32 revision;
42*4882a593Smuzhiyun u32 num_regions;
43*4882a593Smuzhiyun struct md_ss_toc *md_ss_toc;
44*4882a593Smuzhiyun struct md_global_toc *md_gbl_toc;
45*4882a593Smuzhiyun struct md_ss_region *md_regions;
46*4882a593Smuzhiyun struct md_region entry[MAX_NUM_ENTRIES];
47*4882a593Smuzhiyun };
48*4882a593Smuzhiyun
49*4882a593Smuzhiyun /**
50*4882a593Smuzhiyun * md_elfhdr: Minidump table elf header
51*4882a593Smuzhiyun * @ehdr: elf main header
52*4882a593Smuzhiyun * @shdr: Section header
53*4882a593Smuzhiyun * @phdr: Program header
54*4882a593Smuzhiyun * @elf_offset: section offset in elf
55*4882a593Smuzhiyun * @strtable_idx: string table current index position
56*4882a593Smuzhiyun */
57*4882a593Smuzhiyun struct md_elfhdr {
58*4882a593Smuzhiyun struct elfhdr *ehdr;
59*4882a593Smuzhiyun struct elf_shdr *shdr;
60*4882a593Smuzhiyun struct elf_phdr *phdr;
61*4882a593Smuzhiyun u64 elf_offset;
62*4882a593Smuzhiyun u64 strtable_idx;
63*4882a593Smuzhiyun };
64*4882a593Smuzhiyun
65*4882a593Smuzhiyun /* Protect elfheader and smem table from deferred calls contention */
66*4882a593Smuzhiyun static DEFINE_SPINLOCK(mdt_lock);
67*4882a593Smuzhiyun static DEFINE_RWLOCK(mdt_remove_lock);
68*4882a593Smuzhiyun static struct md_table minidump_table;
69*4882a593Smuzhiyun static struct md_elfhdr minidump_elfheader;
70*4882a593Smuzhiyun static int first_removed_entry = INT_MAX;
71*4882a593Smuzhiyun static bool md_init_done;
72*4882a593Smuzhiyun static void __iomem *md_elf_mem;
73*4882a593Smuzhiyun static resource_size_t md_elf_size;
74*4882a593Smuzhiyun static struct proc_dir_entry *proc_rk_minidump;
75*4882a593Smuzhiyun
76*4882a593Smuzhiyun /* Number of pending entries to be added in ToC regions */
77*4882a593Smuzhiyun static unsigned int pendings;
78*4882a593Smuzhiyun
elf_lookup_string(struct elfhdr * hdr,int offset)79*4882a593Smuzhiyun static inline char *elf_lookup_string(struct elfhdr *hdr, int offset)
80*4882a593Smuzhiyun {
81*4882a593Smuzhiyun char *strtab = elf_str_table(hdr);
82*4882a593Smuzhiyun
83*4882a593Smuzhiyun if ((strtab == NULL) || (minidump_elfheader.strtable_idx < offset))
84*4882a593Smuzhiyun return NULL;
85*4882a593Smuzhiyun return strtab + offset;
86*4882a593Smuzhiyun }
87*4882a593Smuzhiyun
set_section_name(const char * name)88*4882a593Smuzhiyun static inline unsigned int set_section_name(const char *name)
89*4882a593Smuzhiyun {
90*4882a593Smuzhiyun char *strtab = elf_str_table(minidump_elfheader.ehdr);
91*4882a593Smuzhiyun int idx = minidump_elfheader.strtable_idx;
92*4882a593Smuzhiyun int ret = 0;
93*4882a593Smuzhiyun
94*4882a593Smuzhiyun if ((strtab == NULL) || (name == NULL))
95*4882a593Smuzhiyun return 0;
96*4882a593Smuzhiyun
97*4882a593Smuzhiyun ret = idx;
98*4882a593Smuzhiyun idx += strscpy((strtab + idx), name, MAX_REGION_NAME_LENGTH);
99*4882a593Smuzhiyun minidump_elfheader.strtable_idx = idx + 1;
100*4882a593Smuzhiyun
101*4882a593Smuzhiyun return ret;
102*4882a593Smuzhiyun }
103*4882a593Smuzhiyun
md_get_region(char * name)104*4882a593Smuzhiyun struct md_region *md_get_region(char *name)
105*4882a593Smuzhiyun {
106*4882a593Smuzhiyun struct md_region *mdr;
107*4882a593Smuzhiyun int i, regno = minidump_table.num_regions;
108*4882a593Smuzhiyun
109*4882a593Smuzhiyun for (i = 0; i < regno; i++) {
110*4882a593Smuzhiyun mdr = &minidump_table.entry[i];
111*4882a593Smuzhiyun if (!strcmp(mdr->name, name))
112*4882a593Smuzhiyun return mdr;
113*4882a593Smuzhiyun }
114*4882a593Smuzhiyun return NULL;
115*4882a593Smuzhiyun }
116*4882a593Smuzhiyun
md_region_num(const char * name,int * seqno)117*4882a593Smuzhiyun static inline int md_region_num(const char *name, int *seqno)
118*4882a593Smuzhiyun {
119*4882a593Smuzhiyun struct md_ss_region *mde = minidump_table.md_regions;
120*4882a593Smuzhiyun int i, regno = minidump_table.md_ss_toc->ss_region_count;
121*4882a593Smuzhiyun int ret = -EINVAL;
122*4882a593Smuzhiyun
123*4882a593Smuzhiyun for (i = 0; i < regno; i++, mde++) {
124*4882a593Smuzhiyun if (!strcmp(mde->name, name)) {
125*4882a593Smuzhiyun ret = i;
126*4882a593Smuzhiyun if (mde->seq_num > *seqno)
127*4882a593Smuzhiyun *seqno = mde->seq_num;
128*4882a593Smuzhiyun }
129*4882a593Smuzhiyun }
130*4882a593Smuzhiyun return ret;
131*4882a593Smuzhiyun }
132*4882a593Smuzhiyun
md_entry_num(const struct md_region * entry)133*4882a593Smuzhiyun static inline int md_entry_num(const struct md_region *entry)
134*4882a593Smuzhiyun {
135*4882a593Smuzhiyun struct md_region *mdr;
136*4882a593Smuzhiyun int i, regno = minidump_table.num_regions;
137*4882a593Smuzhiyun
138*4882a593Smuzhiyun for (i = 0; i < regno; i++) {
139*4882a593Smuzhiyun mdr = &minidump_table.entry[i];
140*4882a593Smuzhiyun if (!strcmp(mdr->name, entry->name))
141*4882a593Smuzhiyun return i;
142*4882a593Smuzhiyun }
143*4882a593Smuzhiyun return -ENOENT;
144*4882a593Smuzhiyun }
145*4882a593Smuzhiyun
146*4882a593Smuzhiyun /* Update Mini dump table in SMEM */
md_update_ss_toc(const struct md_region * entry)147*4882a593Smuzhiyun static void md_update_ss_toc(const struct md_region *entry)
148*4882a593Smuzhiyun {
149*4882a593Smuzhiyun struct md_ss_region *mdr;
150*4882a593Smuzhiyun struct elfhdr *hdr = minidump_elfheader.ehdr;
151*4882a593Smuzhiyun struct elf_shdr *shdr = elf_section(hdr, hdr->e_shnum++);
152*4882a593Smuzhiyun struct elf_phdr *phdr = elf_program(hdr, hdr->e_phnum++);
153*4882a593Smuzhiyun int seq = 0, reg_cnt = minidump_table.md_ss_toc->ss_region_count;
154*4882a593Smuzhiyun
155*4882a593Smuzhiyun mdr = &minidump_table.md_regions[reg_cnt];
156*4882a593Smuzhiyun
157*4882a593Smuzhiyun strscpy(mdr->name, entry->name, sizeof(mdr->name));
158*4882a593Smuzhiyun mdr->region_base_address = entry->phys_addr;
159*4882a593Smuzhiyun mdr->region_size = entry->size;
160*4882a593Smuzhiyun if (md_region_num(entry->name, &seq) >= 0)
161*4882a593Smuzhiyun mdr->seq_num = seq + 1;
162*4882a593Smuzhiyun
163*4882a593Smuzhiyun /* Update elf header */
164*4882a593Smuzhiyun shdr->sh_type = SHT_PROGBITS;
165*4882a593Smuzhiyun shdr->sh_name = set_section_name(mdr->name);
166*4882a593Smuzhiyun shdr->sh_addr = (elf_addr_t)entry->virt_addr;
167*4882a593Smuzhiyun shdr->sh_size = mdr->region_size;
168*4882a593Smuzhiyun shdr->sh_flags = SHF_WRITE;
169*4882a593Smuzhiyun shdr->sh_offset = minidump_elfheader.elf_offset;
170*4882a593Smuzhiyun shdr->sh_entsize = 0;
171*4882a593Smuzhiyun
172*4882a593Smuzhiyun if (strstr((const char *)mdr->name, "note"))
173*4882a593Smuzhiyun phdr->p_type = PT_NOTE;
174*4882a593Smuzhiyun else
175*4882a593Smuzhiyun phdr->p_type = PT_LOAD;
176*4882a593Smuzhiyun phdr->p_offset = minidump_elfheader.elf_offset;
177*4882a593Smuzhiyun phdr->p_vaddr = entry->virt_addr;
178*4882a593Smuzhiyun phdr->p_paddr = entry->phys_addr;
179*4882a593Smuzhiyun phdr->p_filesz = phdr->p_memsz = mdr->region_size;
180*4882a593Smuzhiyun phdr->p_flags = PF_R | PF_W;
181*4882a593Smuzhiyun minidump_elfheader.elf_offset += shdr->sh_size;
182*4882a593Smuzhiyun mdr->md_valid = MD_REGION_VALID;
183*4882a593Smuzhiyun minidump_table.md_ss_toc->ss_region_count++;
184*4882a593Smuzhiyun }
185*4882a593Smuzhiyun
rk_minidump_enabled(void)186*4882a593Smuzhiyun bool rk_minidump_enabled(void)
187*4882a593Smuzhiyun {
188*4882a593Smuzhiyun bool ret = false;
189*4882a593Smuzhiyun unsigned long flags;
190*4882a593Smuzhiyun
191*4882a593Smuzhiyun spin_lock_irqsave(&mdt_lock, flags);
192*4882a593Smuzhiyun if (minidump_table.md_ss_toc &&
193*4882a593Smuzhiyun (minidump_table.md_ss_toc->md_ss_enable_status ==
194*4882a593Smuzhiyun MD_SS_ENABLED))
195*4882a593Smuzhiyun ret = true;
196*4882a593Smuzhiyun spin_unlock_irqrestore(&mdt_lock, flags);
197*4882a593Smuzhiyun return ret;
198*4882a593Smuzhiyun }
199*4882a593Smuzhiyun EXPORT_SYMBOL(rk_minidump_enabled);
200*4882a593Smuzhiyun
validate_region(const struct md_region * entry)201*4882a593Smuzhiyun static inline int validate_region(const struct md_region *entry)
202*4882a593Smuzhiyun {
203*4882a593Smuzhiyun if (!entry)
204*4882a593Smuzhiyun return -EINVAL;
205*4882a593Smuzhiyun
206*4882a593Smuzhiyun if ((strlen(entry->name) > MD_MAX_NAME_LENGTH) || !entry->virt_addr ||
207*4882a593Smuzhiyun (!IS_ALIGNED(entry->size, 4))) {
208*4882a593Smuzhiyun pr_err("Invalid entry details\n");
209*4882a593Smuzhiyun return -EINVAL;
210*4882a593Smuzhiyun }
211*4882a593Smuzhiyun
212*4882a593Smuzhiyun return 0;
213*4882a593Smuzhiyun }
214*4882a593Smuzhiyun
rk_minidump_update_region(int regno,const struct md_region * entry)215*4882a593Smuzhiyun int rk_minidump_update_region(int regno, const struct md_region *entry)
216*4882a593Smuzhiyun {
217*4882a593Smuzhiyun int ret = 0;
218*4882a593Smuzhiyun struct md_region *mdr;
219*4882a593Smuzhiyun struct md_ss_region *mdssr;
220*4882a593Smuzhiyun struct elfhdr *hdr = minidump_elfheader.ehdr;
221*4882a593Smuzhiyun struct elf_shdr *shdr;
222*4882a593Smuzhiyun struct elf_phdr *phdr;
223*4882a593Smuzhiyun unsigned long flags;
224*4882a593Smuzhiyun
225*4882a593Smuzhiyun /* Ensure that init completes before we update regions */
226*4882a593Smuzhiyun if (!smp_load_acquire(&md_init_done))
227*4882a593Smuzhiyun return -EINVAL;
228*4882a593Smuzhiyun
229*4882a593Smuzhiyun if (validate_region(entry) || (regno >= MAX_NUM_ENTRIES))
230*4882a593Smuzhiyun return -EINVAL;
231*4882a593Smuzhiyun
232*4882a593Smuzhiyun read_lock_irqsave(&mdt_remove_lock, flags);
233*4882a593Smuzhiyun
234*4882a593Smuzhiyun if (regno >= first_removed_entry) {
235*4882a593Smuzhiyun pr_err("Region:[%s] was moved\n", entry->name);
236*4882a593Smuzhiyun ret = -EINVAL;
237*4882a593Smuzhiyun goto err_unlock;
238*4882a593Smuzhiyun }
239*4882a593Smuzhiyun
240*4882a593Smuzhiyun ret = md_entry_num(entry);
241*4882a593Smuzhiyun if (ret < 0) {
242*4882a593Smuzhiyun pr_err("Region:[%s] does not exist to update.\n", entry->name);
243*4882a593Smuzhiyun goto err_unlock;
244*4882a593Smuzhiyun }
245*4882a593Smuzhiyun
246*4882a593Smuzhiyun mdr = &minidump_table.entry[regno];
247*4882a593Smuzhiyun mdr->virt_addr = entry->virt_addr;
248*4882a593Smuzhiyun mdr->phys_addr = entry->phys_addr;
249*4882a593Smuzhiyun
250*4882a593Smuzhiyun mdssr = &minidump_table.md_regions[regno + 1];
251*4882a593Smuzhiyun mdssr->region_base_address = entry->phys_addr;
252*4882a593Smuzhiyun
253*4882a593Smuzhiyun shdr = elf_section(hdr, regno + 4);
254*4882a593Smuzhiyun phdr = elf_program(hdr, regno + 1);
255*4882a593Smuzhiyun
256*4882a593Smuzhiyun shdr->sh_addr = (elf_addr_t)entry->virt_addr;
257*4882a593Smuzhiyun phdr->p_vaddr = entry->virt_addr;
258*4882a593Smuzhiyun phdr->p_paddr = entry->phys_addr;
259*4882a593Smuzhiyun
260*4882a593Smuzhiyun err_unlock:
261*4882a593Smuzhiyun read_unlock_irqrestore(&mdt_remove_lock, flags);
262*4882a593Smuzhiyun rk_md_flush_dcache_area((void *)entry, sizeof(*entry));
263*4882a593Smuzhiyun return ret;
264*4882a593Smuzhiyun }
265*4882a593Smuzhiyun EXPORT_SYMBOL(rk_minidump_update_region);
266*4882a593Smuzhiyun
rk_minidump_add_region(const struct md_region * entry)267*4882a593Smuzhiyun int rk_minidump_add_region(const struct md_region *entry)
268*4882a593Smuzhiyun {
269*4882a593Smuzhiyun u32 entries;
270*4882a593Smuzhiyun u32 toc_init;
271*4882a593Smuzhiyun struct md_region *mdr;
272*4882a593Smuzhiyun unsigned long flags;
273*4882a593Smuzhiyun
274*4882a593Smuzhiyun if (validate_region(entry))
275*4882a593Smuzhiyun return -EINVAL;
276*4882a593Smuzhiyun
277*4882a593Smuzhiyun spin_lock_irqsave(&mdt_lock, flags);
278*4882a593Smuzhiyun if (md_entry_num(entry) >= 0) {
279*4882a593Smuzhiyun spin_unlock_irqrestore(&mdt_lock, flags);
280*4882a593Smuzhiyun pr_info("Entry name already exist.\n");
281*4882a593Smuzhiyun return -EEXIST;
282*4882a593Smuzhiyun }
283*4882a593Smuzhiyun
284*4882a593Smuzhiyun entries = minidump_table.num_regions;
285*4882a593Smuzhiyun if (entries >= MAX_NUM_ENTRIES) {
286*4882a593Smuzhiyun pr_err("Maximum entries reached.\n");
287*4882a593Smuzhiyun spin_unlock_irqrestore(&mdt_lock, flags);
288*4882a593Smuzhiyun return -ENOMEM;
289*4882a593Smuzhiyun }
290*4882a593Smuzhiyun
291*4882a593Smuzhiyun toc_init = 0;
292*4882a593Smuzhiyun if (minidump_table.md_ss_toc &&
293*4882a593Smuzhiyun (minidump_table.md_ss_toc->md_ss_enable_status ==
294*4882a593Smuzhiyun MD_SS_ENABLED)) {
295*4882a593Smuzhiyun toc_init = 1;
296*4882a593Smuzhiyun if (minidump_table.md_ss_toc->ss_region_count >= MAX_NUM_ENTRIES) {
297*4882a593Smuzhiyun spin_unlock_irqrestore(&mdt_lock, flags);
298*4882a593Smuzhiyun pr_err("Maximum regions in minidump table reached.\n");
299*4882a593Smuzhiyun return -ENOMEM;
300*4882a593Smuzhiyun }
301*4882a593Smuzhiyun }
302*4882a593Smuzhiyun
303*4882a593Smuzhiyun mdr = &minidump_table.entry[entries];
304*4882a593Smuzhiyun strscpy(mdr->name, entry->name, sizeof(mdr->name));
305*4882a593Smuzhiyun mdr->virt_addr = entry->virt_addr;
306*4882a593Smuzhiyun mdr->phys_addr = entry->phys_addr;
307*4882a593Smuzhiyun mdr->size = entry->size;
308*4882a593Smuzhiyun mdr->id = entry->id;
309*4882a593Smuzhiyun
310*4882a593Smuzhiyun minidump_table.num_regions = entries + 1;
311*4882a593Smuzhiyun
312*4882a593Smuzhiyun if (toc_init)
313*4882a593Smuzhiyun md_update_ss_toc(entry);
314*4882a593Smuzhiyun else
315*4882a593Smuzhiyun pendings++;
316*4882a593Smuzhiyun
317*4882a593Smuzhiyun spin_unlock_irqrestore(&mdt_lock, flags);
318*4882a593Smuzhiyun
319*4882a593Smuzhiyun return entries;
320*4882a593Smuzhiyun }
321*4882a593Smuzhiyun EXPORT_SYMBOL(rk_minidump_add_region);
322*4882a593Smuzhiyun
rk_minidump_clear_headers(const struct md_region * entry)323*4882a593Smuzhiyun int rk_minidump_clear_headers(const struct md_region *entry)
324*4882a593Smuzhiyun {
325*4882a593Smuzhiyun struct elfhdr *hdr = minidump_elfheader.ehdr;
326*4882a593Smuzhiyun struct elf_shdr *shdr = NULL, *tshdr = NULL;
327*4882a593Smuzhiyun struct elf_phdr *phdr = NULL, *tphdr = NULL;
328*4882a593Smuzhiyun int pidx, shidx, strln, i;
329*4882a593Smuzhiyun char *shname;
330*4882a593Smuzhiyun u64 esize;
331*4882a593Smuzhiyun
332*4882a593Smuzhiyun esize = entry->size;
333*4882a593Smuzhiyun for (i = 0; i < hdr->e_phnum; i++) {
334*4882a593Smuzhiyun phdr = elf_program(hdr, i);
335*4882a593Smuzhiyun if ((phdr->p_paddr == entry->phys_addr) &&
336*4882a593Smuzhiyun (phdr->p_memsz == entry->size))
337*4882a593Smuzhiyun break;
338*4882a593Smuzhiyun }
339*4882a593Smuzhiyun if (i == hdr->e_phnum) {
340*4882a593Smuzhiyun pr_err("Cannot find entry in elf\n");
341*4882a593Smuzhiyun return -EINVAL;
342*4882a593Smuzhiyun }
343*4882a593Smuzhiyun pidx = i;
344*4882a593Smuzhiyun
345*4882a593Smuzhiyun for (i = 0; i < hdr->e_shnum; i++) {
346*4882a593Smuzhiyun shdr = elf_section(hdr, i);
347*4882a593Smuzhiyun shname = elf_lookup_string(hdr, shdr->sh_name);
348*4882a593Smuzhiyun if (shname && !strcmp(shname, entry->name))
349*4882a593Smuzhiyun if ((shdr->sh_addr == entry->virt_addr) &&
350*4882a593Smuzhiyun (shdr->sh_size == entry->size))
351*4882a593Smuzhiyun break;
352*4882a593Smuzhiyun
353*4882a593Smuzhiyun }
354*4882a593Smuzhiyun if (i == hdr->e_shnum) {
355*4882a593Smuzhiyun pr_err("Cannot find entry in elf\n");
356*4882a593Smuzhiyun return -EINVAL;
357*4882a593Smuzhiyun }
358*4882a593Smuzhiyun shidx = i;
359*4882a593Smuzhiyun
360*4882a593Smuzhiyun if (shdr->sh_offset != phdr->p_offset) {
361*4882a593Smuzhiyun pr_err("Invalid entry details in elf, Minidump broken..\n");
362*4882a593Smuzhiyun return -EINVAL;
363*4882a593Smuzhiyun }
364*4882a593Smuzhiyun
365*4882a593Smuzhiyun /* Clear name in string table */
366*4882a593Smuzhiyun strln = strlen(shname) + 1;
367*4882a593Smuzhiyun memmove(shname, shname + strln,
368*4882a593Smuzhiyun (minidump_elfheader.strtable_idx - shdr->sh_name));
369*4882a593Smuzhiyun minidump_elfheader.strtable_idx -= strln;
370*4882a593Smuzhiyun
371*4882a593Smuzhiyun /* Clear program header */
372*4882a593Smuzhiyun tphdr = elf_program(hdr, pidx);
373*4882a593Smuzhiyun for (i = pidx; i < hdr->e_phnum - 1; i++) {
374*4882a593Smuzhiyun tphdr = elf_program(hdr, i + 1);
375*4882a593Smuzhiyun phdr = elf_program(hdr, i);
376*4882a593Smuzhiyun memcpy(phdr, tphdr, sizeof(struct elf_phdr));
377*4882a593Smuzhiyun phdr->p_offset = phdr->p_offset - esize;
378*4882a593Smuzhiyun }
379*4882a593Smuzhiyun memset(tphdr, 0, sizeof(struct elf_phdr));
380*4882a593Smuzhiyun hdr->e_phnum--;
381*4882a593Smuzhiyun
382*4882a593Smuzhiyun /* Clear section header */
383*4882a593Smuzhiyun tshdr = elf_section(hdr, shidx);
384*4882a593Smuzhiyun for (i = shidx; i < hdr->e_shnum - 1; i++) {
385*4882a593Smuzhiyun tshdr = elf_section(hdr, i + 1);
386*4882a593Smuzhiyun shdr = elf_section(hdr, i);
387*4882a593Smuzhiyun memcpy(shdr, tshdr, sizeof(struct elf_shdr));
388*4882a593Smuzhiyun shdr->sh_offset -= esize;
389*4882a593Smuzhiyun shdr->sh_name -= strln;
390*4882a593Smuzhiyun }
391*4882a593Smuzhiyun memset(tshdr, 0, sizeof(struct elf_shdr));
392*4882a593Smuzhiyun hdr->e_shnum--;
393*4882a593Smuzhiyun
394*4882a593Smuzhiyun minidump_elfheader.elf_offset -= esize;
395*4882a593Smuzhiyun return 0;
396*4882a593Smuzhiyun }
397*4882a593Smuzhiyun
rk_minidump_remove_region(const struct md_region * entry)398*4882a593Smuzhiyun int rk_minidump_remove_region(const struct md_region *entry)
399*4882a593Smuzhiyun {
400*4882a593Smuzhiyun int rcount, ecount, seq = 0, rgno, entryno, ret;
401*4882a593Smuzhiyun unsigned long flags;
402*4882a593Smuzhiyun
403*4882a593Smuzhiyun if (!entry || !minidump_table.md_ss_toc ||
404*4882a593Smuzhiyun (minidump_table.md_ss_toc->md_ss_enable_status !=
405*4882a593Smuzhiyun MD_SS_ENABLED))
406*4882a593Smuzhiyun return -EINVAL;
407*4882a593Smuzhiyun
408*4882a593Smuzhiyun spin_lock_irqsave(&mdt_lock, flags);
409*4882a593Smuzhiyun write_lock(&mdt_remove_lock);
410*4882a593Smuzhiyun ret = md_entry_num(entry);
411*4882a593Smuzhiyun if (ret < 0) {
412*4882a593Smuzhiyun write_unlock(&mdt_remove_lock);
413*4882a593Smuzhiyun spin_unlock_irqrestore(&mdt_lock, flags);
414*4882a593Smuzhiyun pr_info("Not able to find the entry %s in table\n", entry->name);
415*4882a593Smuzhiyun return ret;
416*4882a593Smuzhiyun }
417*4882a593Smuzhiyun entryno = ret;
418*4882a593Smuzhiyun rgno = md_region_num(entry->name, &seq);
419*4882a593Smuzhiyun if (rgno < 0) {
420*4882a593Smuzhiyun write_unlock(&mdt_remove_lock);
421*4882a593Smuzhiyun spin_unlock_irqrestore(&mdt_lock, flags);
422*4882a593Smuzhiyun pr_err("Not able to find the region %s (%d,%d) in table\n",
423*4882a593Smuzhiyun entry->name, entryno, rgno);
424*4882a593Smuzhiyun return -EINVAL;
425*4882a593Smuzhiyun }
426*4882a593Smuzhiyun ecount = minidump_table.num_regions;
427*4882a593Smuzhiyun rcount = minidump_table.md_ss_toc->ss_region_count;
428*4882a593Smuzhiyun if (first_removed_entry > entryno)
429*4882a593Smuzhiyun first_removed_entry = entryno;
430*4882a593Smuzhiyun minidump_table.md_ss_toc->md_ss_toc_init = 0;
431*4882a593Smuzhiyun
432*4882a593Smuzhiyun /* Remove entry from: entry list, ss region list and elf header */
433*4882a593Smuzhiyun memmove(&minidump_table.entry[entryno],
434*4882a593Smuzhiyun &minidump_table.entry[entryno + 1],
435*4882a593Smuzhiyun ((ecount - entryno - 1) * sizeof(struct md_region)));
436*4882a593Smuzhiyun memset(&minidump_table.entry[ecount - 1], 0, sizeof(struct md_region));
437*4882a593Smuzhiyun
438*4882a593Smuzhiyun
439*4882a593Smuzhiyun memmove(&minidump_table.md_regions[rgno],
440*4882a593Smuzhiyun &minidump_table.md_regions[rgno + 1],
441*4882a593Smuzhiyun ((rcount - rgno - 1) * sizeof(struct md_ss_region)));
442*4882a593Smuzhiyun memset(&minidump_table.md_regions[rcount - 1], 0,
443*4882a593Smuzhiyun sizeof(struct md_ss_region));
444*4882a593Smuzhiyun
445*4882a593Smuzhiyun ret = rk_minidump_clear_headers(entry);
446*4882a593Smuzhiyun if (ret)
447*4882a593Smuzhiyun goto out;
448*4882a593Smuzhiyun
449*4882a593Smuzhiyun minidump_table.md_ss_toc->ss_region_count--;
450*4882a593Smuzhiyun minidump_table.md_ss_toc->md_ss_toc_init = 1;
451*4882a593Smuzhiyun minidump_table.num_regions--;
452*4882a593Smuzhiyun out:
453*4882a593Smuzhiyun write_unlock(&mdt_remove_lock);
454*4882a593Smuzhiyun spin_unlock_irqrestore(&mdt_lock, flags);
455*4882a593Smuzhiyun
456*4882a593Smuzhiyun if (ret)
457*4882a593Smuzhiyun pr_err("Minidump is broken..disable Minidump collection\n");
458*4882a593Smuzhiyun return ret;
459*4882a593Smuzhiyun }
460*4882a593Smuzhiyun EXPORT_SYMBOL(rk_minidump_remove_region);
461*4882a593Smuzhiyun
rk_minidump_flush_elfheader(void)462*4882a593Smuzhiyun void rk_minidump_flush_elfheader(void)
463*4882a593Smuzhiyun {
464*4882a593Smuzhiyun rk_md_flush_dcache_area((void *)minidump_elfheader.ehdr, minidump_table.md_regions[0].region_size);
465*4882a593Smuzhiyun }
466*4882a593Smuzhiyun
rk_minidump_add_header(void)467*4882a593Smuzhiyun static int rk_minidump_add_header(void)
468*4882a593Smuzhiyun {
469*4882a593Smuzhiyun struct md_ss_region *mdreg = &minidump_table.md_regions[0];
470*4882a593Smuzhiyun struct elfhdr *ehdr;
471*4882a593Smuzhiyun struct elf_shdr *shdr;
472*4882a593Smuzhiyun struct elf_phdr *phdr;
473*4882a593Smuzhiyun unsigned int strtbl_off, elfh_size, phdr_off;
474*4882a593Smuzhiyun char *banner, *linux_banner;
475*4882a593Smuzhiyun #ifdef CONFIG_ANDROID_DEBUG_SYMBOLS
476*4882a593Smuzhiyun linux_banner = android_debug_symbol(ADS_LINUX_BANNER);
477*4882a593Smuzhiyun #else
478*4882a593Smuzhiyun linux_banner = "This is rockchip minidump, welcome!";
479*4882a593Smuzhiyun #endif
480*4882a593Smuzhiyun
481*4882a593Smuzhiyun /* Header buffer contains:
482*4882a593Smuzhiyun * elf header, MAX_NUM_ENTRIES+4 of section and program elf headers,
483*4882a593Smuzhiyun * string table section and linux banner.
484*4882a593Smuzhiyun */
485*4882a593Smuzhiyun elfh_size = sizeof(*ehdr) + MAX_STRTBL_SIZE +
486*4882a593Smuzhiyun (strlen(linux_banner) + 1) +
487*4882a593Smuzhiyun ((sizeof(*shdr) + sizeof(*phdr))
488*4882a593Smuzhiyun * (MAX_NUM_ENTRIES + 4));
489*4882a593Smuzhiyun
490*4882a593Smuzhiyun elfh_size = ALIGN(elfh_size, 4);
491*4882a593Smuzhiyun
492*4882a593Smuzhiyun minidump_elfheader.ehdr = kzalloc(elfh_size, GFP_KERNEL);
493*4882a593Smuzhiyun if (!minidump_elfheader.ehdr)
494*4882a593Smuzhiyun return -ENOMEM;
495*4882a593Smuzhiyun
496*4882a593Smuzhiyun strscpy(mdreg->name, "KELF_HEADER", sizeof(mdreg->name));
497*4882a593Smuzhiyun mdreg->region_base_address = virt_to_phys(minidump_elfheader.ehdr);
498*4882a593Smuzhiyun mdreg->region_size = elfh_size;
499*4882a593Smuzhiyun
500*4882a593Smuzhiyun ehdr = minidump_elfheader.ehdr;
501*4882a593Smuzhiyun /* Assign section/program headers offset */
502*4882a593Smuzhiyun minidump_elfheader.shdr = shdr = (struct elf_shdr *)(ehdr + 1);
503*4882a593Smuzhiyun minidump_elfheader.phdr = phdr =
504*4882a593Smuzhiyun (struct elf_phdr *)(shdr + MAX_NUM_ENTRIES);
505*4882a593Smuzhiyun phdr_off = sizeof(*ehdr) + (sizeof(*shdr) * MAX_NUM_ENTRIES);
506*4882a593Smuzhiyun
507*4882a593Smuzhiyun memcpy(ehdr->e_ident, ELFMAG, SELFMAG);
508*4882a593Smuzhiyun ehdr->e_ident[EI_CLASS] = ELF_CLASS;
509*4882a593Smuzhiyun ehdr->e_ident[EI_DATA] = ELF_DATA;
510*4882a593Smuzhiyun ehdr->e_ident[EI_VERSION] = EV_CURRENT;
511*4882a593Smuzhiyun ehdr->e_ident[EI_OSABI] = ELFOSABI_NONE;
512*4882a593Smuzhiyun ehdr->e_type = ET_CORE;
513*4882a593Smuzhiyun ehdr->e_machine = ELF_ARCH;
514*4882a593Smuzhiyun ehdr->e_version = EV_CURRENT;
515*4882a593Smuzhiyun ehdr->e_ehsize = sizeof(*ehdr);
516*4882a593Smuzhiyun ehdr->e_phoff = phdr_off;
517*4882a593Smuzhiyun ehdr->e_phentsize = sizeof(*phdr);
518*4882a593Smuzhiyun ehdr->e_shoff = sizeof(*ehdr);
519*4882a593Smuzhiyun ehdr->e_shentsize = sizeof(*shdr);
520*4882a593Smuzhiyun ehdr->e_shstrndx = 1;
521*4882a593Smuzhiyun
522*4882a593Smuzhiyun minidump_elfheader.elf_offset = elfh_size;
523*4882a593Smuzhiyun
524*4882a593Smuzhiyun /*
525*4882a593Smuzhiyun * First section header should be NULL,
526*4882a593Smuzhiyun * 2nd section is string table.
527*4882a593Smuzhiyun */
528*4882a593Smuzhiyun minidump_elfheader.strtable_idx = 1;
529*4882a593Smuzhiyun strtbl_off = sizeof(*ehdr) +
530*4882a593Smuzhiyun ((sizeof(*phdr) + sizeof(*shdr)) * MAX_NUM_ENTRIES);
531*4882a593Smuzhiyun shdr++;
532*4882a593Smuzhiyun shdr->sh_type = SHT_STRTAB;
533*4882a593Smuzhiyun shdr->sh_offset = (elf_addr_t)strtbl_off;
534*4882a593Smuzhiyun shdr->sh_size = MAX_STRTBL_SIZE;
535*4882a593Smuzhiyun shdr->sh_entsize = 0;
536*4882a593Smuzhiyun shdr->sh_flags = 0;
537*4882a593Smuzhiyun shdr->sh_name = set_section_name("STR_TBL");
538*4882a593Smuzhiyun shdr++;
539*4882a593Smuzhiyun
540*4882a593Smuzhiyun /* 3rd section is for minidump_table VA, used by parsers */
541*4882a593Smuzhiyun shdr->sh_type = SHT_PROGBITS;
542*4882a593Smuzhiyun shdr->sh_entsize = 0;
543*4882a593Smuzhiyun shdr->sh_flags = 0;
544*4882a593Smuzhiyun shdr->sh_addr = (elf_addr_t)&minidump_table;
545*4882a593Smuzhiyun shdr->sh_name = set_section_name("minidump_table");
546*4882a593Smuzhiyun shdr++;
547*4882a593Smuzhiyun
548*4882a593Smuzhiyun /* 4th section is linux banner */
549*4882a593Smuzhiyun banner = (char *)ehdr + strtbl_off + MAX_STRTBL_SIZE;
550*4882a593Smuzhiyun strscpy(banner, linux_banner, MAX_STRTBL_SIZE);
551*4882a593Smuzhiyun
552*4882a593Smuzhiyun shdr->sh_type = SHT_PROGBITS;
553*4882a593Smuzhiyun shdr->sh_offset = (elf_addr_t)(strtbl_off + MAX_STRTBL_SIZE);
554*4882a593Smuzhiyun shdr->sh_size = strlen(linux_banner) + 1;
555*4882a593Smuzhiyun shdr->sh_addr = (elf_addr_t)linux_banner;
556*4882a593Smuzhiyun shdr->sh_entsize = 0;
557*4882a593Smuzhiyun shdr->sh_flags = SHF_WRITE;
558*4882a593Smuzhiyun shdr->sh_name = set_section_name("linux_banner");
559*4882a593Smuzhiyun
560*4882a593Smuzhiyun phdr->p_type = PT_LOAD;
561*4882a593Smuzhiyun phdr->p_offset = (elf_addr_t)(strtbl_off + MAX_STRTBL_SIZE);
562*4882a593Smuzhiyun phdr->p_vaddr = (elf_addr_t)linux_banner;
563*4882a593Smuzhiyun phdr->p_paddr = virt_to_phys(linux_banner);
564*4882a593Smuzhiyun phdr->p_filesz = phdr->p_memsz = strlen(linux_banner) + 1;
565*4882a593Smuzhiyun phdr->p_flags = PF_R | PF_W;
566*4882a593Smuzhiyun
567*4882a593Smuzhiyun /* Update headers count*/
568*4882a593Smuzhiyun ehdr->e_phnum = 1;
569*4882a593Smuzhiyun ehdr->e_shnum = 4;
570*4882a593Smuzhiyun
571*4882a593Smuzhiyun mdreg->md_valid = MD_REGION_VALID;
572*4882a593Smuzhiyun return 0;
573*4882a593Smuzhiyun }
574*4882a593Smuzhiyun
rk_minidump_driver_remove(struct platform_device * pdev)575*4882a593Smuzhiyun static int rk_minidump_driver_remove(struct platform_device *pdev)
576*4882a593Smuzhiyun {
577*4882a593Smuzhiyun /* TO-DO.
578*4882a593Smuzhiyun *Free the required resources and set the global
579*4882a593Smuzhiyun * variables as minidump is not initialized.
580*4882a593Smuzhiyun */
581*4882a593Smuzhiyun return 0;
582*4882a593Smuzhiyun }
583*4882a593Smuzhiyun
rk_minidump_read_elf(struct file * file,char __user * buffer,size_t buflen,loff_t * fpos)584*4882a593Smuzhiyun static ssize_t rk_minidump_read_elf(struct file *file, char __user *buffer,
585*4882a593Smuzhiyun size_t buflen, loff_t *fpos)
586*4882a593Smuzhiyun {
587*4882a593Smuzhiyun size_t size = 0;
588*4882a593Smuzhiyun
589*4882a593Smuzhiyun size = simple_read_from_buffer(buffer, buflen, fpos, (const void *)md_elf_mem, md_elf_size);
590*4882a593Smuzhiyun
591*4882a593Smuzhiyun return size;
592*4882a593Smuzhiyun }
593*4882a593Smuzhiyun
594*4882a593Smuzhiyun static const struct proc_ops rk_minidump_proc_ops = {
595*4882a593Smuzhiyun .proc_read = rk_minidump_read_elf,
596*4882a593Smuzhiyun };
597*4882a593Smuzhiyun
rk_minidump_driver_probe(struct platform_device * pdev)598*4882a593Smuzhiyun static int rk_minidump_driver_probe(struct platform_device *pdev)
599*4882a593Smuzhiyun {
600*4882a593Smuzhiyun unsigned int i;
601*4882a593Smuzhiyun struct md_region *mdr;
602*4882a593Smuzhiyun struct md_global_toc *md_global_toc;
603*4882a593Smuzhiyun struct md_ss_toc *md_ss_toc;
604*4882a593Smuzhiyun unsigned long flags;
605*4882a593Smuzhiyun struct device_node *np;
606*4882a593Smuzhiyun struct resource r;
607*4882a593Smuzhiyun resource_size_t r_size;
608*4882a593Smuzhiyun struct device *dev = &pdev->dev;
609*4882a593Smuzhiyun Elf64_Ehdr *ehdr; /* Elf header structure pointer */
610*4882a593Smuzhiyun Elf64_Phdr *phdr; /* Program header structure pointer */
611*4882a593Smuzhiyun int ret;
612*4882a593Smuzhiyun struct proc_dir_entry *base_dir = proc_mkdir("rk_md", NULL);
613*4882a593Smuzhiyun
614*4882a593Smuzhiyun if (!base_dir) {
615*4882a593Smuzhiyun dev_err(dev, "Couldn't create base dir /proc/rk_md\n");
616*4882a593Smuzhiyun return -ENOMEM;
617*4882a593Smuzhiyun }
618*4882a593Smuzhiyun
619*4882a593Smuzhiyun np = of_parse_phandle(dev->of_node, "smem-region", 0);
620*4882a593Smuzhiyun if (!np) {
621*4882a593Smuzhiyun dev_err(dev, "No smem-region specified\n");
622*4882a593Smuzhiyun return -EINVAL;
623*4882a593Smuzhiyun }
624*4882a593Smuzhiyun ret = of_address_to_resource(np, 0, &r);
625*4882a593Smuzhiyun of_node_put(np);
626*4882a593Smuzhiyun if (ret)
627*4882a593Smuzhiyun return ret;
628*4882a593Smuzhiyun r_size = resource_size(&r);
629*4882a593Smuzhiyun md_global_toc = devm_ioremap_wc(dev, r.start, r_size);
630*4882a593Smuzhiyun if (!md_global_toc) {
631*4882a593Smuzhiyun pr_err("unable to map memory region: %pa+%pa\n", &r.start, &r_size);
632*4882a593Smuzhiyun return -ENOMEM;
633*4882a593Smuzhiyun }
634*4882a593Smuzhiyun
635*4882a593Smuzhiyun np = of_parse_phandle(dev->of_node, "minidump-region", 0);
636*4882a593Smuzhiyun if (!np) {
637*4882a593Smuzhiyun dev_err(dev, "No minidump-region specified\n");
638*4882a593Smuzhiyun return -EINVAL;
639*4882a593Smuzhiyun }
640*4882a593Smuzhiyun
641*4882a593Smuzhiyun ret = of_address_to_resource(np, 0, &r);
642*4882a593Smuzhiyun of_node_put(np);
643*4882a593Smuzhiyun if (ret)
644*4882a593Smuzhiyun return ret;
645*4882a593Smuzhiyun r_size = resource_size(&r);
646*4882a593Smuzhiyun md_elf_mem = devm_ioremap_wc(dev, r.start, r_size);
647*4882a593Smuzhiyun if (!md_elf_mem) {
648*4882a593Smuzhiyun pr_err("unable to map memory region: %pa+%pa\n", &r.start, &r_size);
649*4882a593Smuzhiyun return -ENOMEM;
650*4882a593Smuzhiyun }
651*4882a593Smuzhiyun
652*4882a593Smuzhiyun ehdr = (Elf64_Ehdr *)md_elf_mem;
653*4882a593Smuzhiyun
654*4882a593Smuzhiyun if (!strncmp((const char *)ehdr, ELFMAG, 4)) {
655*4882a593Smuzhiyun phdr = (Elf64_Phdr *)(md_elf_mem + (ulong)ehdr->e_phoff);
656*4882a593Smuzhiyun phdr += ehdr->e_phnum - 1;
657*4882a593Smuzhiyun md_elf_size = phdr->p_memsz + phdr->p_offset;
658*4882a593Smuzhiyun
659*4882a593Smuzhiyun pr_info("Create /proc/rk_md/minidump...\n");
660*4882a593Smuzhiyun proc_rk_minidump = proc_create("minidump", 0400, base_dir, &rk_minidump_proc_ops);
661*4882a593Smuzhiyun }
662*4882a593Smuzhiyun
663*4882a593Smuzhiyun /* Check global minidump support initialization */
664*4882a593Smuzhiyun if (!md_global_toc->md_toc_init) {
665*4882a593Smuzhiyun pr_err("System Minidump TOC not initialized\n");
666*4882a593Smuzhiyun return -ENODEV;
667*4882a593Smuzhiyun }
668*4882a593Smuzhiyun
669*4882a593Smuzhiyun minidump_table.md_gbl_toc = md_global_toc;
670*4882a593Smuzhiyun minidump_table.revision = md_global_toc->md_revision;
671*4882a593Smuzhiyun md_ss_toc = &md_global_toc->md_ss_toc[MD_SS_HLOS_ID];
672*4882a593Smuzhiyun
673*4882a593Smuzhiyun md_ss_toc->encryption_status = MD_SS_ENCR_NONE;
674*4882a593Smuzhiyun md_ss_toc->encryption_required = MD_SS_ENCR_REQ;
675*4882a593Smuzhiyun md_ss_toc->elf_header = (u64)r.start;
676*4882a593Smuzhiyun
677*4882a593Smuzhiyun minidump_table.md_ss_toc = md_ss_toc;
678*4882a593Smuzhiyun minidump_table.md_regions = devm_kzalloc(&pdev->dev, (MAX_NUM_ENTRIES *
679*4882a593Smuzhiyun sizeof(struct md_ss_region)), GFP_KERNEL);
680*4882a593Smuzhiyun if (!minidump_table.md_regions)
681*4882a593Smuzhiyun return -ENOMEM;
682*4882a593Smuzhiyun
683*4882a593Smuzhiyun md_ss_toc->md_ss_smem_regions_baseptr =
684*4882a593Smuzhiyun virt_to_phys(minidump_table.md_regions);
685*4882a593Smuzhiyun
686*4882a593Smuzhiyun /* First entry would be ELF header */
687*4882a593Smuzhiyun md_ss_toc->ss_region_count = 1;
688*4882a593Smuzhiyun rk_minidump_add_header();
689*4882a593Smuzhiyun
690*4882a593Smuzhiyun /* Add pending entries to HLOS TOC */
691*4882a593Smuzhiyun spin_lock_irqsave(&mdt_lock, flags);
692*4882a593Smuzhiyun md_ss_toc->md_ss_toc_init = 1;
693*4882a593Smuzhiyun md_ss_toc->md_ss_enable_status = MD_SS_ENABLED;
694*4882a593Smuzhiyun for (i = 0; i < pendings; i++) {
695*4882a593Smuzhiyun mdr = &minidump_table.entry[i];
696*4882a593Smuzhiyun md_update_ss_toc(mdr);
697*4882a593Smuzhiyun }
698*4882a593Smuzhiyun
699*4882a593Smuzhiyun pendings = 0;
700*4882a593Smuzhiyun spin_unlock_irqrestore(&mdt_lock, flags);
701*4882a593Smuzhiyun
702*4882a593Smuzhiyun /* All updates above should be visible, before init completes */
703*4882a593Smuzhiyun smp_store_release(&md_init_done, true);
704*4882a593Smuzhiyun rk_minidump_log_init();
705*4882a593Smuzhiyun pr_info("Enabled with max number of regions %d\n",
706*4882a593Smuzhiyun CONFIG_ROCKCHIP_MINIDUMP_MAX_ENTRIES);
707*4882a593Smuzhiyun
708*4882a593Smuzhiyun return 0;
709*4882a593Smuzhiyun }
710*4882a593Smuzhiyun
711*4882a593Smuzhiyun static const struct of_device_id rk_minidump_of_match[] = {
712*4882a593Smuzhiyun { .compatible = "rockchip,minidump" },
713*4882a593Smuzhiyun { }
714*4882a593Smuzhiyun };
715*4882a593Smuzhiyun MODULE_DEVICE_TABLE(of, rk_minidump_of_match);
716*4882a593Smuzhiyun
717*4882a593Smuzhiyun static struct platform_driver rk_minidump_driver = {
718*4882a593Smuzhiyun .driver = {
719*4882a593Smuzhiyun .name = "rockchip-minidump",
720*4882a593Smuzhiyun .of_match_table = rk_minidump_of_match,
721*4882a593Smuzhiyun },
722*4882a593Smuzhiyun .probe = rk_minidump_driver_probe,
723*4882a593Smuzhiyun .remove = rk_minidump_driver_remove,
724*4882a593Smuzhiyun };
725*4882a593Smuzhiyun module_platform_driver(rk_minidump_driver);
726*4882a593Smuzhiyun
727*4882a593Smuzhiyun MODULE_DESCRIPTION("RK Mini Dump Driver");
728*4882a593Smuzhiyun MODULE_LICENSE("GPL");
729