18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2019 SiFive
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#include <linux/efi.h>
78c2ecf20Sopenharmony_ci#include <linux/init.h>
88c2ecf20Sopenharmony_ci#include <linux/debugfs.h>
98c2ecf20Sopenharmony_ci#include <linux/seq_file.h>
108c2ecf20Sopenharmony_ci#include <linux/ptdump.h>
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <asm/ptdump.h>
138c2ecf20Sopenharmony_ci#include <linux/pgtable.h>
148c2ecf20Sopenharmony_ci#include <asm/kasan.h>
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#define pt_dump_seq_printf(m, fmt, args...)	\
178c2ecf20Sopenharmony_ci({						\
188c2ecf20Sopenharmony_ci	if (m)					\
198c2ecf20Sopenharmony_ci		seq_printf(m, fmt, ##args);	\
208c2ecf20Sopenharmony_ci})
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#define pt_dump_seq_puts(m, fmt)	\
238c2ecf20Sopenharmony_ci({					\
248c2ecf20Sopenharmony_ci	if (m)				\
258c2ecf20Sopenharmony_ci		seq_printf(m, fmt);	\
268c2ecf20Sopenharmony_ci})
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci/*
298c2ecf20Sopenharmony_ci * The page dumper groups page table entries of the same type into a single
308c2ecf20Sopenharmony_ci * description. It uses pg_state to track the range information while
318c2ecf20Sopenharmony_ci * iterating over the pte entries. When the continuity is broken it then
328c2ecf20Sopenharmony_ci * dumps out a description of the range.
338c2ecf20Sopenharmony_ci */
348c2ecf20Sopenharmony_cistruct pg_state {
358c2ecf20Sopenharmony_ci	struct ptdump_state ptdump;
368c2ecf20Sopenharmony_ci	struct seq_file *seq;
378c2ecf20Sopenharmony_ci	const struct addr_marker *marker;
388c2ecf20Sopenharmony_ci	unsigned long start_address;
398c2ecf20Sopenharmony_ci	unsigned long start_pa;
408c2ecf20Sopenharmony_ci	unsigned long last_pa;
418c2ecf20Sopenharmony_ci	int level;
428c2ecf20Sopenharmony_ci	u64 current_prot;
438c2ecf20Sopenharmony_ci	bool check_wx;
448c2ecf20Sopenharmony_ci	unsigned long wx_pages;
458c2ecf20Sopenharmony_ci};
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci/* Address marker */
488c2ecf20Sopenharmony_cistruct addr_marker {
498c2ecf20Sopenharmony_ci	unsigned long start_address;
508c2ecf20Sopenharmony_ci	const char *name;
518c2ecf20Sopenharmony_ci};
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci/* Private information for debugfs */
548c2ecf20Sopenharmony_cistruct ptd_mm_info {
558c2ecf20Sopenharmony_ci	struct mm_struct		*mm;
568c2ecf20Sopenharmony_ci	const struct addr_marker	*markers;
578c2ecf20Sopenharmony_ci	unsigned long base_addr;
588c2ecf20Sopenharmony_ci	unsigned long end;
598c2ecf20Sopenharmony_ci};
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_cistatic struct addr_marker address_markers[] = {
628c2ecf20Sopenharmony_ci#ifdef CONFIG_KASAN
638c2ecf20Sopenharmony_ci	{KASAN_SHADOW_START,	"Kasan shadow start"},
648c2ecf20Sopenharmony_ci	{KASAN_SHADOW_END,	"Kasan shadow end"},
658c2ecf20Sopenharmony_ci#endif
668c2ecf20Sopenharmony_ci	{FIXADDR_START,		"Fixmap start"},
678c2ecf20Sopenharmony_ci	{FIXADDR_TOP,		"Fixmap end"},
688c2ecf20Sopenharmony_ci	{PCI_IO_START,		"PCI I/O start"},
698c2ecf20Sopenharmony_ci	{PCI_IO_END,		"PCI I/O end"},
708c2ecf20Sopenharmony_ci#ifdef CONFIG_SPARSEMEM_VMEMMAP
718c2ecf20Sopenharmony_ci	{VMEMMAP_START,		"vmemmap start"},
728c2ecf20Sopenharmony_ci	{VMEMMAP_END,		"vmemmap end"},
738c2ecf20Sopenharmony_ci#endif
748c2ecf20Sopenharmony_ci	{VMALLOC_START,		"vmalloc() area"},
758c2ecf20Sopenharmony_ci	{VMALLOC_END,		"vmalloc() end"},
768c2ecf20Sopenharmony_ci	{PAGE_OFFSET,		"Linear mapping"},
778c2ecf20Sopenharmony_ci	{-1, NULL},
788c2ecf20Sopenharmony_ci};
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_cistatic struct ptd_mm_info kernel_ptd_info = {
818c2ecf20Sopenharmony_ci	.mm		= &init_mm,
828c2ecf20Sopenharmony_ci	.markers	= address_markers,
838c2ecf20Sopenharmony_ci	.base_addr	= KERN_VIRT_START,
848c2ecf20Sopenharmony_ci	.end		= ULONG_MAX,
858c2ecf20Sopenharmony_ci};
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci#ifdef CONFIG_EFI
888c2ecf20Sopenharmony_cistatic struct addr_marker efi_addr_markers[] = {
898c2ecf20Sopenharmony_ci		{ 0,		"UEFI runtime start" },
908c2ecf20Sopenharmony_ci		{ SZ_1G,	"UEFI runtime end" },
918c2ecf20Sopenharmony_ci		{ -1,		NULL }
928c2ecf20Sopenharmony_ci};
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_cistatic struct ptd_mm_info efi_ptd_info = {
958c2ecf20Sopenharmony_ci	.mm		= &efi_mm,
968c2ecf20Sopenharmony_ci	.markers	= efi_addr_markers,
978c2ecf20Sopenharmony_ci	.base_addr	= 0,
988c2ecf20Sopenharmony_ci	.end		= SZ_2G,
998c2ecf20Sopenharmony_ci};
1008c2ecf20Sopenharmony_ci#endif
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci/* Page Table Entry */
1038c2ecf20Sopenharmony_cistruct prot_bits {
1048c2ecf20Sopenharmony_ci	u64 mask;
1058c2ecf20Sopenharmony_ci	u64 val;
1068c2ecf20Sopenharmony_ci	const char *set;
1078c2ecf20Sopenharmony_ci	const char *clear;
1088c2ecf20Sopenharmony_ci};
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_cistatic const struct prot_bits pte_bits[] = {
1118c2ecf20Sopenharmony_ci	{
1128c2ecf20Sopenharmony_ci		.mask = _PAGE_SOFT,
1138c2ecf20Sopenharmony_ci		.val = _PAGE_SOFT,
1148c2ecf20Sopenharmony_ci		.set = "RSW",
1158c2ecf20Sopenharmony_ci		.clear = "   ",
1168c2ecf20Sopenharmony_ci	}, {
1178c2ecf20Sopenharmony_ci		.mask = _PAGE_DIRTY,
1188c2ecf20Sopenharmony_ci		.val = _PAGE_DIRTY,
1198c2ecf20Sopenharmony_ci		.set = "D",
1208c2ecf20Sopenharmony_ci		.clear = ".",
1218c2ecf20Sopenharmony_ci	}, {
1228c2ecf20Sopenharmony_ci		.mask = _PAGE_ACCESSED,
1238c2ecf20Sopenharmony_ci		.val = _PAGE_ACCESSED,
1248c2ecf20Sopenharmony_ci		.set = "A",
1258c2ecf20Sopenharmony_ci		.clear = ".",
1268c2ecf20Sopenharmony_ci	}, {
1278c2ecf20Sopenharmony_ci		.mask = _PAGE_GLOBAL,
1288c2ecf20Sopenharmony_ci		.val = _PAGE_GLOBAL,
1298c2ecf20Sopenharmony_ci		.set = "G",
1308c2ecf20Sopenharmony_ci		.clear = ".",
1318c2ecf20Sopenharmony_ci	}, {
1328c2ecf20Sopenharmony_ci		.mask = _PAGE_USER,
1338c2ecf20Sopenharmony_ci		.val = _PAGE_USER,
1348c2ecf20Sopenharmony_ci		.set = "U",
1358c2ecf20Sopenharmony_ci		.clear = ".",
1368c2ecf20Sopenharmony_ci	}, {
1378c2ecf20Sopenharmony_ci		.mask = _PAGE_EXEC,
1388c2ecf20Sopenharmony_ci		.val = _PAGE_EXEC,
1398c2ecf20Sopenharmony_ci		.set = "X",
1408c2ecf20Sopenharmony_ci		.clear = ".",
1418c2ecf20Sopenharmony_ci	}, {
1428c2ecf20Sopenharmony_ci		.mask = _PAGE_WRITE,
1438c2ecf20Sopenharmony_ci		.val = _PAGE_WRITE,
1448c2ecf20Sopenharmony_ci		.set = "W",
1458c2ecf20Sopenharmony_ci		.clear = ".",
1468c2ecf20Sopenharmony_ci	}, {
1478c2ecf20Sopenharmony_ci		.mask = _PAGE_READ,
1488c2ecf20Sopenharmony_ci		.val = _PAGE_READ,
1498c2ecf20Sopenharmony_ci		.set = "R",
1508c2ecf20Sopenharmony_ci		.clear = ".",
1518c2ecf20Sopenharmony_ci	}, {
1528c2ecf20Sopenharmony_ci		.mask = _PAGE_PRESENT,
1538c2ecf20Sopenharmony_ci		.val = _PAGE_PRESENT,
1548c2ecf20Sopenharmony_ci		.set = "V",
1558c2ecf20Sopenharmony_ci		.clear = ".",
1568c2ecf20Sopenharmony_ci	}
1578c2ecf20Sopenharmony_ci};
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci/* Page Level */
1608c2ecf20Sopenharmony_cistruct pg_level {
1618c2ecf20Sopenharmony_ci	const char *name;
1628c2ecf20Sopenharmony_ci	u64 mask;
1638c2ecf20Sopenharmony_ci};
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_cistatic struct pg_level pg_level[] = {
1668c2ecf20Sopenharmony_ci	{ /* pgd */
1678c2ecf20Sopenharmony_ci		.name = "PGD",
1688c2ecf20Sopenharmony_ci	}, { /* p4d */
1698c2ecf20Sopenharmony_ci		.name = (CONFIG_PGTABLE_LEVELS > 4) ? "P4D" : "PGD",
1708c2ecf20Sopenharmony_ci	}, { /* pud */
1718c2ecf20Sopenharmony_ci		.name = (CONFIG_PGTABLE_LEVELS > 3) ? "PUD" : "PGD",
1728c2ecf20Sopenharmony_ci	}, { /* pmd */
1738c2ecf20Sopenharmony_ci		.name = (CONFIG_PGTABLE_LEVELS > 2) ? "PMD" : "PGD",
1748c2ecf20Sopenharmony_ci	}, { /* pte */
1758c2ecf20Sopenharmony_ci		.name = "PTE",
1768c2ecf20Sopenharmony_ci	},
1778c2ecf20Sopenharmony_ci};
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_cistatic void dump_prot(struct pg_state *st)
1808c2ecf20Sopenharmony_ci{
1818c2ecf20Sopenharmony_ci	unsigned int i;
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(pte_bits); i++) {
1848c2ecf20Sopenharmony_ci		const char *s;
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci		if ((st->current_prot & pte_bits[i].mask) == pte_bits[i].val)
1878c2ecf20Sopenharmony_ci			s = pte_bits[i].set;
1888c2ecf20Sopenharmony_ci		else
1898c2ecf20Sopenharmony_ci			s = pte_bits[i].clear;
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci		if (s)
1928c2ecf20Sopenharmony_ci			pt_dump_seq_printf(st->seq, " %s", s);
1938c2ecf20Sopenharmony_ci	}
1948c2ecf20Sopenharmony_ci}
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci#ifdef CONFIG_64BIT
1978c2ecf20Sopenharmony_ci#define ADDR_FORMAT	"0x%016lx"
1988c2ecf20Sopenharmony_ci#else
1998c2ecf20Sopenharmony_ci#define ADDR_FORMAT	"0x%08lx"
2008c2ecf20Sopenharmony_ci#endif
2018c2ecf20Sopenharmony_cistatic void dump_addr(struct pg_state *st, unsigned long addr)
2028c2ecf20Sopenharmony_ci{
2038c2ecf20Sopenharmony_ci	static const char units[] = "KMGTPE";
2048c2ecf20Sopenharmony_ci	const char *unit = units;
2058c2ecf20Sopenharmony_ci	unsigned long delta;
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	pt_dump_seq_printf(st->seq, ADDR_FORMAT "-" ADDR_FORMAT "   ",
2088c2ecf20Sopenharmony_ci			   st->start_address, addr);
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	pt_dump_seq_printf(st->seq, " " ADDR_FORMAT " ", st->start_pa);
2118c2ecf20Sopenharmony_ci	delta = (addr - st->start_address) >> 10;
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	while (!(delta & 1023) && unit[1]) {
2148c2ecf20Sopenharmony_ci		delta >>= 10;
2158c2ecf20Sopenharmony_ci		unit++;
2168c2ecf20Sopenharmony_ci	}
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	pt_dump_seq_printf(st->seq, "%9lu%c %s", delta, *unit,
2198c2ecf20Sopenharmony_ci			   pg_level[st->level].name);
2208c2ecf20Sopenharmony_ci}
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_cistatic void note_prot_wx(struct pg_state *st, unsigned long addr)
2238c2ecf20Sopenharmony_ci{
2248c2ecf20Sopenharmony_ci	if (!st->check_wx)
2258c2ecf20Sopenharmony_ci		return;
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	if ((st->current_prot & (_PAGE_WRITE | _PAGE_EXEC)) !=
2288c2ecf20Sopenharmony_ci	    (_PAGE_WRITE | _PAGE_EXEC))
2298c2ecf20Sopenharmony_ci		return;
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	WARN_ONCE(1, "riscv/mm: Found insecure W+X mapping at address %p/%pS\n",
2328c2ecf20Sopenharmony_ci		  (void *)st->start_address, (void *)st->start_address);
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	st->wx_pages += (addr - st->start_address) / PAGE_SIZE;
2358c2ecf20Sopenharmony_ci}
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_cistatic void note_page(struct ptdump_state *pt_st, unsigned long addr,
2388c2ecf20Sopenharmony_ci		      int level, u64 val)
2398c2ecf20Sopenharmony_ci{
2408c2ecf20Sopenharmony_ci	struct pg_state *st = container_of(pt_st, struct pg_state, ptdump);
2418c2ecf20Sopenharmony_ci	u64 pa = PFN_PHYS(pte_pfn(__pte(val)));
2428c2ecf20Sopenharmony_ci	u64 prot = 0;
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	if (level >= 0)
2458c2ecf20Sopenharmony_ci		prot = val & pg_level[level].mask;
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	if (st->level == -1) {
2488c2ecf20Sopenharmony_ci		st->level = level;
2498c2ecf20Sopenharmony_ci		st->current_prot = prot;
2508c2ecf20Sopenharmony_ci		st->start_address = addr;
2518c2ecf20Sopenharmony_ci		st->start_pa = pa;
2528c2ecf20Sopenharmony_ci		st->last_pa = pa;
2538c2ecf20Sopenharmony_ci		pt_dump_seq_printf(st->seq, "---[ %s ]---\n", st->marker->name);
2548c2ecf20Sopenharmony_ci	} else if (prot != st->current_prot ||
2558c2ecf20Sopenharmony_ci		   level != st->level || addr >= st->marker[1].start_address) {
2568c2ecf20Sopenharmony_ci		if (st->current_prot) {
2578c2ecf20Sopenharmony_ci			note_prot_wx(st, addr);
2588c2ecf20Sopenharmony_ci			dump_addr(st, addr);
2598c2ecf20Sopenharmony_ci			dump_prot(st);
2608c2ecf20Sopenharmony_ci			pt_dump_seq_puts(st->seq, "\n");
2618c2ecf20Sopenharmony_ci		}
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci		while (addr >= st->marker[1].start_address) {
2648c2ecf20Sopenharmony_ci			st->marker++;
2658c2ecf20Sopenharmony_ci			pt_dump_seq_printf(st->seq, "---[ %s ]---\n",
2668c2ecf20Sopenharmony_ci					   st->marker->name);
2678c2ecf20Sopenharmony_ci		}
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci		st->start_address = addr;
2708c2ecf20Sopenharmony_ci		st->start_pa = pa;
2718c2ecf20Sopenharmony_ci		st->last_pa = pa;
2728c2ecf20Sopenharmony_ci		st->current_prot = prot;
2738c2ecf20Sopenharmony_ci		st->level = level;
2748c2ecf20Sopenharmony_ci	} else {
2758c2ecf20Sopenharmony_ci		st->last_pa = pa;
2768c2ecf20Sopenharmony_ci	}
2778c2ecf20Sopenharmony_ci}
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_cistatic void ptdump_walk(struct seq_file *s, struct ptd_mm_info *pinfo)
2808c2ecf20Sopenharmony_ci{
2818c2ecf20Sopenharmony_ci	struct pg_state st = {
2828c2ecf20Sopenharmony_ci		.seq = s,
2838c2ecf20Sopenharmony_ci		.marker = pinfo->markers,
2848c2ecf20Sopenharmony_ci		.level = -1,
2858c2ecf20Sopenharmony_ci		.ptdump = {
2868c2ecf20Sopenharmony_ci			.note_page = note_page,
2878c2ecf20Sopenharmony_ci			.range = (struct ptdump_range[]) {
2888c2ecf20Sopenharmony_ci				{pinfo->base_addr, pinfo->end},
2898c2ecf20Sopenharmony_ci				{0, 0}
2908c2ecf20Sopenharmony_ci			}
2918c2ecf20Sopenharmony_ci		}
2928c2ecf20Sopenharmony_ci	};
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	ptdump_walk_pgd(&st.ptdump, pinfo->mm, NULL);
2958c2ecf20Sopenharmony_ci}
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_civoid ptdump_check_wx(void)
2988c2ecf20Sopenharmony_ci{
2998c2ecf20Sopenharmony_ci	struct pg_state st = {
3008c2ecf20Sopenharmony_ci		.seq = NULL,
3018c2ecf20Sopenharmony_ci		.marker = (struct addr_marker[]) {
3028c2ecf20Sopenharmony_ci			{0, NULL},
3038c2ecf20Sopenharmony_ci			{-1, NULL},
3048c2ecf20Sopenharmony_ci		},
3058c2ecf20Sopenharmony_ci		.level = -1,
3068c2ecf20Sopenharmony_ci		.check_wx = true,
3078c2ecf20Sopenharmony_ci		.ptdump = {
3088c2ecf20Sopenharmony_ci			.note_page = note_page,
3098c2ecf20Sopenharmony_ci			.range = (struct ptdump_range[]) {
3108c2ecf20Sopenharmony_ci				{KERN_VIRT_START, ULONG_MAX},
3118c2ecf20Sopenharmony_ci				{0, 0}
3128c2ecf20Sopenharmony_ci			}
3138c2ecf20Sopenharmony_ci		}
3148c2ecf20Sopenharmony_ci	};
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	ptdump_walk_pgd(&st.ptdump, &init_mm, NULL);
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	if (st.wx_pages)
3198c2ecf20Sopenharmony_ci		pr_warn("Checked W+X mappings: failed, %lu W+X pages found\n",
3208c2ecf20Sopenharmony_ci			st.wx_pages);
3218c2ecf20Sopenharmony_ci	else
3228c2ecf20Sopenharmony_ci		pr_info("Checked W+X mappings: passed, no W+X pages found\n");
3238c2ecf20Sopenharmony_ci}
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_cistatic int ptdump_show(struct seq_file *m, void *v)
3268c2ecf20Sopenharmony_ci{
3278c2ecf20Sopenharmony_ci	ptdump_walk(m, m->private);
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	return 0;
3308c2ecf20Sopenharmony_ci}
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(ptdump);
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_cistatic int ptdump_init(void)
3358c2ecf20Sopenharmony_ci{
3368c2ecf20Sopenharmony_ci	unsigned int i, j;
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(pg_level); i++)
3398c2ecf20Sopenharmony_ci		for (j = 0; j < ARRAY_SIZE(pte_bits); j++)
3408c2ecf20Sopenharmony_ci			pg_level[i].mask |= pte_bits[j].mask;
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci	debugfs_create_file("kernel_page_tables", 0400, NULL, &kernel_ptd_info,
3438c2ecf20Sopenharmony_ci			    &ptdump_fops);
3448c2ecf20Sopenharmony_ci#ifdef CONFIG_EFI
3458c2ecf20Sopenharmony_ci	if (efi_enabled(EFI_RUNTIME_SERVICES))
3468c2ecf20Sopenharmony_ci		debugfs_create_file("efi_page_tables", 0400, NULL, &efi_ptd_info,
3478c2ecf20Sopenharmony_ci				    &ptdump_fops);
3488c2ecf20Sopenharmony_ci#endif
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	return 0;
3518c2ecf20Sopenharmony_ci}
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_cidevice_initcall(ptdump_init);
354