xref: /rk3399_rockchip-uboot/arch/arm/mach-rockchip/rk_mini_dump.c (revision a4719b90cc2f09e5348b830d61f32ab6d991069a)
1 /*
2  * (C) Copyright 2023 Rockchip Electronics Co., Ltd.
3  *
4  * SPDX-License-Identifier:     GPL-2.0+
5  */
6 
7 #include <common.h>
8 #include <linux/types.h>
9 #include <asm/io.h>
10 #include <rk_mini_dump.h>
11 
12 /* don't modify it, it is behind pstore memory space */
13 #ifdef CONFIG_ROCKCHIP_MINIDUMP_SMEM_BASE
14 #define SMEM_BASE		CONFIG_ROCKCHIP_MINIDUMP_SMEM_BASE
15 #else
16 #define SMEM_BASE		0x1f0000
17 #endif
18 
19 #ifdef CONFIG_ROCKCHIP_MINIDUMP_MAX_ELF_SIZE
20 #define MAX_ELF_SIZE		CONFIG_ROCKCHIP_MINIDUMP_MAX_ELF_SIZE
21 #else
22 #define MAX_ELF_SIZE		0x2000000
23 #endif
24 
25 #ifdef CONFIG_ROCKCHIP_MINIDUMP_MAX_ENTRIES
26 #define MAX_NUM_ENTRIES		(CONFIG_ROCKCHIP_MINIDUMP_MAX_ENTRIES + 1)
27 #else
28 #define MAX_NUM_ENTRIES		129
29 #endif
30 
31 /* Bootloader has 16 byte support, 4 bytes reserved for itself */
32 #define MAX_REGION_NAME_LENGTH	16
33 #define MAX_STRTBL_SIZE		(MAX_NUM_ENTRIES * MAX_REGION_NAME_LENGTH)
34 
35 /**
36  * md_table : Local Minidump toc holder
37  * @num_regions : Number of regions requested
38  * @md_ss_toc  : HLOS toc pointer
39  * @md_gbl_toc : Global toc pointer
40  * @md_regions : HLOS regions base pointer
41  * @entry : array of HLOS regions requested
42  */
43 struct md_table {
44 	u32			revision;
45 	u32                     num_regions;
46 	struct md_ss_toc	*md_ss_toc;
47 	struct md_global_toc	*md_gbl_toc;
48 	struct md_ss_region	*md_regions;
49 	struct md_region	entry[MAX_NUM_ENTRIES];
50 };
51 
52 #define MAX_NUM_OF_SS		2
53 
54 /**
55  * md_ss_toc: Sub system SMEM Table of content
56  * @md_ss_toc_init : SS toc init status
57  * @md_ss_enable_status : if set to 1, Bootloader would dump this SS regions
58  * @encryption_status: Encryption status for this subsystem
59  * @encryption_required : Decides to encrypt the SS regions or not
60  * @ss_region_count : Number of regions added in this SS toc
61  * @md_ss_smem_regions_baseptr : regions base pointer of the Subsystem
62  * @elf_header : base pointer of the minidump elf header
63  * @minidump_table : base pointer of the minidump_table
64  */
65 struct md_ss_toc {
66 	u32			md_ss_toc_init;
67 	u32			md_ss_enable_status;
68 	u32			encryption_status;
69 	u32			encryption_required;
70 	u32			ss_region_count;
71 	u64			md_ss_smem_regions_baseptr;
72 	u64			elf_header;
73 	u64			elf_size;
74 	u64			minidump_table;
75 };
76 
77 /**
78  * md_global_toc: Global Table of Content
79  * @md_toc_init : Global Minidump init status
80  * @md_revision : Minidump revision
81  * @md_enable_status : Minidump enable status
82  * @md_ss_toc : Array of subsystems toc
83  */
84 struct md_global_toc {
85 	u32			md_toc_init;
86 	u32			md_revision;
87 	u32			md_enable_status;
88 	struct md_ss_toc	md_ss_toc[MAX_NUM_OF_SS];
89 };
90 
91 /* Bootloader has 16 byte support, 4 bytes reserved for itself */
92 #define MAX_REGION_NAME_LENGTH	16
93 
94 #define MD_REGION_VALID		('V' << 24 | 'A' << 16 | 'L' << 8 | 'I' << 0)
95 #define MD_REGION_INVALID	('I' << 24 | 'N' << 16 | 'V' << 8 | 'A' << 0)
96 #define MD_REGION_INIT		('I' << 24 | 'N' << 16 | 'I' << 8 | 'T' << 0)
97 #define MD_REGION_NOINIT	0
98 
99 #define MD_SS_ENCR_REQ		(0 << 24 | 'Y' << 16 | 'E' << 8 | 'S' << 0)
100 #define MD_SS_ENCR_NOTREQ	(0 << 24 | 0 << 16 | 'N' << 8 | 'R' << 0)
101 #define MD_SS_ENCR_NONE		('N' << 24 | 'O' << 16 | 'N' << 8 | 'E' << 0)
102 #define MD_SS_ENCR_DONE		('D' << 24 | 'O' << 16 | 'N' << 8 | 'E' << 0)
103 #define MD_SS_ENCR_START	('S' << 24 | 'T' << 16 | 'R' << 8 | 'T' << 0)
104 #define MD_SS_ENABLED		('E' << 24 | 'N' << 16 | 'B' << 8 | 'L' << 0)
105 #define MD_SS_DISABLED		('D' << 24 | 'S' << 16 | 'B' << 8 | 'L' << 0)
106 
107 #define EM_AARCH64	183	/* ARM 64 bit */
108 
109 /**
110  * md_ss_region - Minidump region
111  * @name		: Name of the region to be dumped
112  * @seq_num:		: Use to differentiate regions with same name.
113  * @md_valid		: This entry to be dumped (if set to 1)
114  * @region_base_address	: Physical address of region to be dumped
115  * @region_size		: Size of the region
116  */
117 struct md_ss_region {
118 	char	name[MAX_REGION_NAME_LENGTH];
119 	u32	seq_num;
120 	u32	md_valid;
121 	u64	region_base_address;
122 	u64	region_size;
123 };
124 
125 #define NO_FAULT_TAG 0x55aa55aa
126 static u32 no_fault;
127 static struct md_table *minidump_table;
128 
129 u32 md_no_fault_handler(struct pt_regs *pt_regs, unsigned int esr)
130 {
131 	if (no_fault == NO_FAULT_TAG) {
132 		no_fault = 0;
133 		return 1;
134 	}
135 	return 0;
136 }
137 
138 #if defined(CONFIG_ROCKCHIP_RK3588)
139 static u32 md_is_ddr_addr(void *addr)
140 {
141 	/* peripheral address space */
142 	if (addr >= (void *)0xf0000000 && addr <= (void *)0x100000000)
143 		return 0;
144 	/* pcie address space */
145 	if (addr > (void *)0x800000000)
146 		return 0;
147 	return 1;
148 }
149 #else
150 static u32 md_is_ddr_addr(void *addr)
151 {
152 	return 1;
153 }
154 #endif
155 
156 static u32 md_is_uboot_addr(void *addr)
157 {
158 	volatile u32 *p_no_fault = &no_fault;
159 
160 	if(!md_is_ddr_addr(addr))
161 		return 0;
162 
163 	*p_no_fault = NO_FAULT_TAG;
164 	readb(addr);
165 	return *p_no_fault;
166 }
167 
168 struct md_region *md_get_region(char *name)
169 {
170 	struct md_region *mdr;
171 	int i, regno;
172 
173 	if (!md_is_uboot_addr((void *)minidump_table))
174 		return NULL;
175 
176 	regno = minidump_table->num_regions;
177 	for (i = 0; i < regno; i++) {
178 		mdr = &minidump_table->entry[i];
179 		if (!strcmp(mdr->name, name))
180 			return mdr;
181 	}
182 	return NULL;
183 }
184 
185 #ifdef CONFIG_ARM64
186 static Elf64_Xword rk_dump_elf64_image_phdr(void *ram_image,
187 					    Elf64_Addr ehaddr, Elf64_Xword ehsize)
188 {
189 	Elf64_Ehdr *ehdr = (Elf64_Ehdr *)ehaddr;
190 	Elf64_Phdr *phdr = NULL, *phdr_next = NULL;
191 	Elf64_Shdr *shdr = NULL, *shdr_next = NULL;
192 	unsigned int i = 0, error = 0, phdr_off = 0, strtbl_off = 0;
193 	unsigned int size = 0, elf_size = ehsize;
194 
195 	if (!md_is_uboot_addr((void *)ehdr))
196 		return 0;
197 
198 	if (!md_is_uboot_addr((void *)ram_image) ||
199 	    !md_is_uboot_addr((void *)ram_image + MAX_ELF_SIZE - 4))
200 		return 0;
201 
202 	memcpy(ehdr->e_ident, ELFMAG, SELFMAG);
203 	ehdr->e_ident[EI_CLASS] = ELFCLASS64;
204 	ehdr->e_ident[EI_DATA] = ELFDATA2LSB;
205 	ehdr->e_ident[EI_VERSION] = EV_CURRENT;
206 	ehdr->e_ident[EI_OSABI] = ELFOSABI_NONE;
207 
208 	if (ehdr->e_type != ET_CORE) {
209 		error++;
210 		ehdr->e_type = ET_CORE;
211 	}
212 	if (ehdr->e_machine != EM_AARCH64) {
213 		error++;
214 		ehdr->e_machine = EM_AARCH64;
215 	}
216 	if (ehdr->e_version != EV_CURRENT) {
217 		error++;
218 		ehdr->e_version = EV_CURRENT;
219 	}
220 	if (ehdr->e_ehsize != sizeof(*ehdr)) {
221 		error++;
222 		ehdr->e_ehsize = sizeof(*ehdr);
223 	}
224 	if (ehdr->e_phentsize != sizeof(*phdr)) {
225 		error++;
226 		ehdr->e_phentsize = sizeof(*phdr);
227 	}
228 	if (ehdr->e_shentsize != sizeof(*shdr)) {
229 		error++;
230 		ehdr->e_shentsize = sizeof(*shdr);
231 	}
232 	if (ehdr->e_shoff != sizeof(*ehdr)) {
233 		error++;
234 		ehdr->e_shoff = sizeof(*ehdr);
235 	}
236 
237 	phdr_off = sizeof(*ehdr) + (sizeof(*shdr) * MAX_NUM_ENTRIES);
238 
239 	if (ehdr->e_phoff != phdr_off) {
240 		error++;
241 		ehdr->e_phoff = phdr_off;
242 	}
243 
244 	printf("Minidump header error:0x%x\n", error);
245 	/* If there are much error, maybe ehdr address is wrong */
246 	if (error > 6)
247 		return 0;
248 
249 	ehdr->e_shstrndx = 1;
250 	phdr = (Elf64_Phdr *)(ehaddr + ehdr->e_phoff);
251 	shdr = (Elf64_Shdr *)(ehaddr + ehdr->e_shoff);
252 
253 	shdr->sh_name = 0;
254 	shdr->sh_type = 0;
255 	shdr->sh_flags = 0;
256 	shdr->sh_addr = 0;
257 	shdr->sh_offset = 0;
258 	shdr->sh_size = 0;
259 	shdr->sh_link = 0;
260 	shdr->sh_info = 0;
261 	shdr->sh_addralign = 0;
262 	shdr->sh_entsize = 0;
263 
264 	shdr++;
265 	if (shdr->sh_name >= MAX_STRTBL_SIZE)
266 		shdr->sh_name = 0;
267 	shdr->sh_type = SHT_STRTAB;
268 	shdr->sh_flags = 0;
269 	shdr->sh_addr = 0;
270 	shdr->sh_offset = phdr_off + (sizeof(*phdr) * MAX_NUM_ENTRIES);
271 	shdr->sh_size = MAX_STRTBL_SIZE;
272 	shdr->sh_link = 0;
273 	shdr->sh_info = 0;
274 	shdr->sh_addralign = 0;
275 	shdr->sh_entsize = 0;
276 
277 	shdr++;
278 	/* 3rd section is for minidump_table VA, used by parsers */
279 	if (shdr->sh_name >= MAX_STRTBL_SIZE)
280 		shdr->sh_name = 0;
281 	shdr->sh_type = SHT_PROGBITS;
282 	shdr->sh_flags = 0;
283 	shdr->sh_offset = 0;
284 	shdr->sh_size = 0;
285 	shdr->sh_link = 0;
286 	shdr->sh_info = 0;
287 	shdr->sh_addralign = 0;
288 	shdr->sh_entsize = 0;
289 
290 	shdr++;
291 	shdr->sh_flags = 0;
292 	shdr->sh_link = 0;
293 	shdr->sh_info = 0;
294 	shdr->sh_addralign = 0;
295 	shdr->sh_entsize = 0;
296 
297 	strtbl_off = phdr_off + (sizeof(*phdr) * MAX_NUM_ENTRIES);
298 	strtbl_off += MAX_STRTBL_SIZE;
299 
300 	if (phdr->p_offset != strtbl_off)
301 		phdr->p_offset = strtbl_off;
302 	if (shdr->sh_offset != strtbl_off)
303 		shdr->sh_offset = strtbl_off;
304 
305 	phdr->p_filesz &= GENMASK(23, 0);	/* 16MB */
306 	phdr->p_memsz &= GENMASK(23, 0);	/* 16MB */
307 	shdr->sh_size &= GENMASK(23, 0);	/* 16MB */
308 
309 	if (phdr->p_filesz == phdr->p_memsz) {
310 		size = phdr->p_filesz;
311 		shdr->sh_size = size;
312 	} else if (phdr->p_filesz == shdr->sh_size) {
313 		size = phdr->p_filesz;
314 		phdr->p_memsz = size;
315 	} else if (phdr->p_memsz == shdr->sh_size) {
316 		size = phdr->p_memsz;
317 		phdr->p_filesz = size;
318 	} else {
319 		printf("Minidump error first phdr p_filesz:0x%llx p_memsz:0x%llx sh_size:0x%llx\n",
320 		       phdr->p_filesz, phdr->p_memsz, shdr->sh_size);
321 		return 0;
322 	}
323 
324 	phdr++;
325 	shdr++;
326 	phdr_next = phdr + 1;
327 	shdr_next = shdr + 1;
328 
329 	memset(ram_image, 0x0, 0x18000);
330 
331 	phdr->p_offset &= MAX_ELF_SIZE - 1;
332 	shdr->sh_offset &= MAX_ELF_SIZE - 1;
333 	elf_size &= MAX_ELF_SIZE - 1;
334 	if (phdr->p_offset == shdr->sh_offset) {
335 		elf_size = phdr->p_offset;
336 	} else if (phdr->p_offset == elf_size) {
337 		shdr->sh_offset = phdr->p_offset;
338 	} else if (elf_size == shdr->sh_offset) {
339 		phdr->p_offset = shdr->sh_offset;
340 	} else {
341 		printf("Minidump error phdr[1] p_offset:0x%llx sh_offset:0x%llx elf_size:0x%x\n",
342 		       phdr->p_offset, shdr->sh_offset, elf_size);
343 		return 0;
344 	}
345 
346 	/* save phdr space */
347 	for (i = 1; i < MAX_NUM_ENTRIES; i++) {
348 		void *src = NULL;
349 		void *dst = NULL;
350 
351 		if (phdr->p_vaddr == 0 || shdr->sh_addr == 0)
352 			break;
353 
354 		phdr->p_offset &= MAX_ELF_SIZE - 1;
355 		shdr->sh_offset &= MAX_ELF_SIZE - 1;
356 
357 		if (phdr->p_offset != elf_size)
358 			phdr->p_offset = elf_size;
359 
360 		if (shdr->sh_offset != elf_size)
361 			shdr->sh_offset = elf_size;
362 
363 		phdr->p_paddr &= GENMASK(34, 0);	/* 32GB */
364 		phdr->p_align &= GENMASK(34, 0);	/* 32GB */
365 		shdr->sh_info &= GENMASK(34, 0);	/* 32GB */
366 
367 		if (phdr->p_paddr != phdr->p_align && phdr->p_align == shdr->sh_entsize)
368 			phdr->p_paddr = phdr->p_align;
369 
370 		phdr->p_type &= 0xf;
371 		phdr->p_flags &= 0xf;
372 		phdr->p_filesz &= GENMASK(23, 0);	/* 16MB */
373 		phdr->p_memsz &= GENMASK(23, 0);	/* 16MB */
374 		phdr->p_align = 0;
375 
376 		if (phdr->p_vaddr != shdr->sh_addr) {
377 			if (shdr->sh_addr == shdr->sh_addralign)
378 				phdr->p_vaddr = shdr->sh_addr;
379 			else if (phdr->p_vaddr == shdr->sh_addralign)
380 				shdr->sh_addr = phdr->p_vaddr;
381 			else
382 				printf("Minidump error phdr[%d] p_vaddr:0x%llx sh_addr:0x%llx sh_addralign:0x%llx\n",
383 				       i, phdr->p_vaddr, shdr->sh_addr, shdr->sh_addralign);
384 		}
385 
386 		if (shdr->sh_name >= MAX_STRTBL_SIZE)
387 			shdr->sh_name = 0;
388 		shdr->sh_type = SHT_PROGBITS;
389 		shdr->sh_flags = SHF_WRITE;
390 		shdr->sh_size &= GENMASK(23, 0);	/* 16MB */
391 		shdr->sh_link = 0;
392 		shdr->sh_info = 0;
393 		shdr->sh_addralign = 0;
394 		shdr->sh_entsize = 0;
395 
396 		if (phdr->p_filesz == phdr->p_memsz) {
397 			size = phdr->p_filesz;
398 			shdr->sh_size = size;
399 		} else if (phdr->p_filesz == shdr->sh_size) {
400 			size = phdr->p_filesz;
401 			phdr->p_memsz = size;
402 		} else if (phdr->p_memsz == shdr->sh_size) {
403 			size = phdr->p_memsz;
404 			phdr->p_filesz = size;
405 		} else {
406 			if ((phdr_next->p_offset == shdr_next->sh_offset) &&
407 			    (phdr_next->p_offset != 0)) {
408 				size = phdr_next->p_offset - phdr->p_offset;
409 				phdr->p_filesz = size;
410 				phdr->p_memsz = size;
411 				shdr->sh_size = size;
412 			} else {
413 				printf("Minidump error phdr[%d] p_filesz:0x%llx p_memsz:0x%llx sh_size:0x%llx",
414 				       i, phdr->p_filesz, phdr->p_memsz, shdr->sh_size);
415 				printf("p_offset:0x%llx sh_offset:0x%llx\n", phdr_next->p_offset,
416 				       shdr_next->sh_offset);
417 				return 0;
418 			}
419 		}
420 
421 		elf_size += size;
422 		src = (void *)(Elf64_Addr)phdr->p_paddr;
423 		dst = ram_image + phdr->p_offset;
424 
425 		if (size > MAX_ELF_SIZE / 2)
426 			goto donot_cpy;
427 
428 		if (!md_is_uboot_addr(src) || !md_is_uboot_addr(src + size - 1)) {
429 			printf("Minidump error src 0x%p-0x%p\n", src, src + size - 1);
430 			goto donot_cpy;
431 		}
432 		if (!md_is_uboot_addr(dst) || !md_is_uboot_addr(dst + size - 1)) {
433 			printf("Minidump error dst 0x%p-0x%p\n", dst, dst + size - 1);
434 			goto donot_cpy;
435 		}
436 		if (size)
437 			memcpy(dst, src, size);
438 donot_cpy:
439 		phdr++;
440 		shdr++;
441 		phdr_next++;
442 		shdr_next++;
443 	}
444 
445 	if (ehdr->e_phnum != i)
446 		ehdr->e_phnum = i;
447 	if ((ehdr->e_phnum + 3) != ehdr->e_shnum)
448 		ehdr->e_shnum = ehdr->e_phnum + 3;
449 
450 	/* copy ehdr to ram image */
451 	memcpy(ram_image, (void *)ehdr, ehsize);
452 	flush_cache((unsigned long)ram_image, elf_size);
453 	printf("Minidump.elf 0x%x@0x%p\n", elf_size, ram_image);
454 	return elf_size;
455 }
456 #else
457 static Elf32_Word rk_dump_elf32_image_phdr(void *ram_image, Elf32_Addr ehaddr,
458 					   Elf32_Word ehsize)
459 {
460 	Elf32_Ehdr *ehdr = (Elf32_Ehdr *)ehaddr;
461 	Elf32_Phdr *phdr = (Elf32_Phdr *)(ehaddr + ehdr->e_phoff);
462 	Elf32_Word ram_image_size = 0;
463 	int i;
464 
465 	/* copy ehdr to ram image */
466 	memcpy(ram_image, (void *)ehdr, ehsize);
467 
468 	/* save phdr space */
469 	for (i = 0; i < ehdr->e_phnum; ++i) {
470 		void *src = (void *)(Elf32_Addr)phdr->p_paddr;
471 		void *dst = ram_image + phdr->p_offset;
472 
473 		if (phdr->p_filesz)
474 			memcpy(dst, src, phdr->p_filesz);
475 		if (phdr->p_filesz != phdr->p_memsz)
476 			memset(dst + phdr->p_filesz, 0x00,
477 			       phdr->p_memsz - phdr->p_filesz);
478 		++phdr;
479 	}
480 
481 	phdr--;
482 	ram_image_size = phdr->p_memsz + phdr->p_offset;
483 	printf("Minidump.elf 0x%llx@0x%p\n", ram_image_size, ram_image);
484 	return ram_image_size;
485 }
486 #endif
487 
488 void rk_minidump_init(void)
489 {
490 	struct md_global_toc *mdg_toc = (struct md_global_toc *)SMEM_BASE;
491 	struct md_ss_toc *md_ss_toc = &mdg_toc->md_ss_toc[0];
492 	struct md_ss_region *mdreg;
493 
494 	printf("Minidump: init...\n");
495 	mdg_toc->md_toc_init = 1;
496 	mdg_toc->md_revision = 1;
497 	mdg_toc->md_enable_status = 0;
498 
499 	if (md_ss_toc->md_ss_enable_status == MD_SS_ENABLED) {
500 		/* linux would set it 1, so we set it 0 here */
501 		md_ss_toc->md_ss_enable_status = 0;
502 		flush_cache((unsigned long)md_ss_toc, 8);
503 		mdreg = (struct md_ss_region *)md_ss_toc->md_ss_smem_regions_baseptr;
504 		minidump_table = (struct md_table *)md_ss_toc->minidump_table;
505 #ifdef CONFIG_ARM64
506 		md_ss_toc->elf_size = rk_dump_elf64_image_phdr((void *)md_ss_toc->elf_header,
507 					 (Elf64_Addr)mdreg->region_base_address,
508 					 (Elf64_Xword)mdreg->region_size);
509 #else
510 		md_ss_toc->elf_size = rk_dump_elf32_image_phdr((void *)md_ss_toc->elf_header,
511 					 (Elf32_Addr)mdreg->region_base_address,
512 					 (Elf32_Word)mdreg->region_size);
513 #endif
514 	}
515 }
516 
517 #ifdef CONFIG_ARM64
518 void rk_minidump_get_el64(void **ram_image_addr, Elf64_Xword *ram_image_size)
519 {
520 	struct md_global_toc *mdg_toc = (struct md_global_toc *)SMEM_BASE;
521 	struct md_ss_toc *md_ss_toc = &mdg_toc->md_ss_toc[0];
522 
523 	*ram_image_addr = (void *)md_ss_toc->elf_header;
524 	*ram_image_size = md_ss_toc->elf_size;
525 }
526 #else
527 void rk_minidump_get_el32(void **ram_image_addr, Elf32_Word *ram_image_size)
528 {
529 	struct md_global_toc *mdg_toc = (struct md_global_toc *)SMEM_BASE;
530 	struct md_ss_toc *md_ss_toc = &mdg_toc->md_ss_toc[0];
531 
532 	*ram_image_addr = (void *)md_ss_toc->elf_header;
533 	*ram_image_size = md_ss_toc->elf_size;
534 }
535 #endif
536