xref: /kernel/linux/linux-5.10/drivers/mtd/ubi/debug.c (revision 8c2ecf20)
18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (c) International Business Machines Corp., 2006
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Author: Artem Bityutskiy (Битюцкий Артём)
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include "ubi.h"
98c2ecf20Sopenharmony_ci#include <linux/debugfs.h>
108c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
118c2ecf20Sopenharmony_ci#include <linux/module.h>
128c2ecf20Sopenharmony_ci#include <linux/seq_file.h>
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci/**
168c2ecf20Sopenharmony_ci * ubi_dump_flash - dump a region of flash.
178c2ecf20Sopenharmony_ci * @ubi: UBI device description object
188c2ecf20Sopenharmony_ci * @pnum: the physical eraseblock number to dump
198c2ecf20Sopenharmony_ci * @offset: the starting offset within the physical eraseblock to dump
208c2ecf20Sopenharmony_ci * @len: the length of the region to dump
218c2ecf20Sopenharmony_ci */
228c2ecf20Sopenharmony_civoid ubi_dump_flash(struct ubi_device *ubi, int pnum, int offset, int len)
238c2ecf20Sopenharmony_ci{
248c2ecf20Sopenharmony_ci	int err;
258c2ecf20Sopenharmony_ci	size_t read;
268c2ecf20Sopenharmony_ci	void *buf;
278c2ecf20Sopenharmony_ci	loff_t addr = (loff_t)pnum * ubi->peb_size + offset;
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci	buf = vmalloc(len);
308c2ecf20Sopenharmony_ci	if (!buf)
318c2ecf20Sopenharmony_ci		return;
328c2ecf20Sopenharmony_ci	err = mtd_read(ubi->mtd, addr, len, &read, buf);
338c2ecf20Sopenharmony_ci	if (err && err != -EUCLEAN) {
348c2ecf20Sopenharmony_ci		ubi_err(ubi, "err %d while reading %d bytes from PEB %d:%d, read %zd bytes",
358c2ecf20Sopenharmony_ci			err, len, pnum, offset, read);
368c2ecf20Sopenharmony_ci		goto out;
378c2ecf20Sopenharmony_ci	}
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci	ubi_msg(ubi, "dumping %d bytes of data from PEB %d, offset %d",
408c2ecf20Sopenharmony_ci		len, pnum, offset);
418c2ecf20Sopenharmony_ci	print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, 32, 1, buf, len, 1);
428c2ecf20Sopenharmony_ciout:
438c2ecf20Sopenharmony_ci	vfree(buf);
448c2ecf20Sopenharmony_ci	return;
458c2ecf20Sopenharmony_ci}
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci/**
488c2ecf20Sopenharmony_ci * ubi_dump_ec_hdr - dump an erase counter header.
498c2ecf20Sopenharmony_ci * @ec_hdr: the erase counter header to dump
508c2ecf20Sopenharmony_ci */
518c2ecf20Sopenharmony_civoid ubi_dump_ec_hdr(const struct ubi_ec_hdr *ec_hdr)
528c2ecf20Sopenharmony_ci{
538c2ecf20Sopenharmony_ci	pr_err("Erase counter header dump:\n");
548c2ecf20Sopenharmony_ci	pr_err("\tmagic          %#08x\n", be32_to_cpu(ec_hdr->magic));
558c2ecf20Sopenharmony_ci	pr_err("\tversion        %d\n", (int)ec_hdr->version);
568c2ecf20Sopenharmony_ci	pr_err("\tec             %llu\n", (long long)be64_to_cpu(ec_hdr->ec));
578c2ecf20Sopenharmony_ci	pr_err("\tvid_hdr_offset %d\n", be32_to_cpu(ec_hdr->vid_hdr_offset));
588c2ecf20Sopenharmony_ci	pr_err("\tdata_offset    %d\n", be32_to_cpu(ec_hdr->data_offset));
598c2ecf20Sopenharmony_ci	pr_err("\timage_seq      %d\n", be32_to_cpu(ec_hdr->image_seq));
608c2ecf20Sopenharmony_ci	pr_err("\thdr_crc        %#08x\n", be32_to_cpu(ec_hdr->hdr_crc));
618c2ecf20Sopenharmony_ci	pr_err("erase counter header hexdump:\n");
628c2ecf20Sopenharmony_ci	print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, 32, 1,
638c2ecf20Sopenharmony_ci		       ec_hdr, UBI_EC_HDR_SIZE, 1);
648c2ecf20Sopenharmony_ci}
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci/**
678c2ecf20Sopenharmony_ci * ubi_dump_vid_hdr - dump a volume identifier header.
688c2ecf20Sopenharmony_ci * @vid_hdr: the volume identifier header to dump
698c2ecf20Sopenharmony_ci */
708c2ecf20Sopenharmony_civoid ubi_dump_vid_hdr(const struct ubi_vid_hdr *vid_hdr)
718c2ecf20Sopenharmony_ci{
728c2ecf20Sopenharmony_ci	pr_err("Volume identifier header dump:\n");
738c2ecf20Sopenharmony_ci	pr_err("\tmagic     %08x\n", be32_to_cpu(vid_hdr->magic));
748c2ecf20Sopenharmony_ci	pr_err("\tversion   %d\n",  (int)vid_hdr->version);
758c2ecf20Sopenharmony_ci	pr_err("\tvol_type  %d\n",  (int)vid_hdr->vol_type);
768c2ecf20Sopenharmony_ci	pr_err("\tcopy_flag %d\n",  (int)vid_hdr->copy_flag);
778c2ecf20Sopenharmony_ci	pr_err("\tcompat    %d\n",  (int)vid_hdr->compat);
788c2ecf20Sopenharmony_ci	pr_err("\tvol_id    %d\n",  be32_to_cpu(vid_hdr->vol_id));
798c2ecf20Sopenharmony_ci	pr_err("\tlnum      %d\n",  be32_to_cpu(vid_hdr->lnum));
808c2ecf20Sopenharmony_ci	pr_err("\tdata_size %d\n",  be32_to_cpu(vid_hdr->data_size));
818c2ecf20Sopenharmony_ci	pr_err("\tused_ebs  %d\n",  be32_to_cpu(vid_hdr->used_ebs));
828c2ecf20Sopenharmony_ci	pr_err("\tdata_pad  %d\n",  be32_to_cpu(vid_hdr->data_pad));
838c2ecf20Sopenharmony_ci	pr_err("\tsqnum     %llu\n",
848c2ecf20Sopenharmony_ci		(unsigned long long)be64_to_cpu(vid_hdr->sqnum));
858c2ecf20Sopenharmony_ci	pr_err("\thdr_crc   %08x\n", be32_to_cpu(vid_hdr->hdr_crc));
868c2ecf20Sopenharmony_ci	pr_err("Volume identifier header hexdump:\n");
878c2ecf20Sopenharmony_ci	print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, 32, 1,
888c2ecf20Sopenharmony_ci		       vid_hdr, UBI_VID_HDR_SIZE, 1);
898c2ecf20Sopenharmony_ci}
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci/**
928c2ecf20Sopenharmony_ci * ubi_dump_vol_info - dump volume information.
938c2ecf20Sopenharmony_ci * @vol: UBI volume description object
948c2ecf20Sopenharmony_ci */
958c2ecf20Sopenharmony_civoid ubi_dump_vol_info(const struct ubi_volume *vol)
968c2ecf20Sopenharmony_ci{
978c2ecf20Sopenharmony_ci	pr_err("Volume information dump:\n");
988c2ecf20Sopenharmony_ci	pr_err("\tvol_id          %d\n", vol->vol_id);
998c2ecf20Sopenharmony_ci	pr_err("\treserved_pebs   %d\n", vol->reserved_pebs);
1008c2ecf20Sopenharmony_ci	pr_err("\talignment       %d\n", vol->alignment);
1018c2ecf20Sopenharmony_ci	pr_err("\tdata_pad        %d\n", vol->data_pad);
1028c2ecf20Sopenharmony_ci	pr_err("\tvol_type        %d\n", vol->vol_type);
1038c2ecf20Sopenharmony_ci	pr_err("\tname_len        %d\n", vol->name_len);
1048c2ecf20Sopenharmony_ci	pr_err("\tusable_leb_size %d\n", vol->usable_leb_size);
1058c2ecf20Sopenharmony_ci	pr_err("\tused_ebs        %d\n", vol->used_ebs);
1068c2ecf20Sopenharmony_ci	pr_err("\tused_bytes      %lld\n", vol->used_bytes);
1078c2ecf20Sopenharmony_ci	pr_err("\tlast_eb_bytes   %d\n", vol->last_eb_bytes);
1088c2ecf20Sopenharmony_ci	pr_err("\tcorrupted       %d\n", vol->corrupted);
1098c2ecf20Sopenharmony_ci	pr_err("\tupd_marker      %d\n", vol->upd_marker);
1108c2ecf20Sopenharmony_ci	pr_err("\tskip_check      %d\n", vol->skip_check);
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	if (vol->name_len <= UBI_VOL_NAME_MAX &&
1138c2ecf20Sopenharmony_ci	    strnlen(vol->name, vol->name_len + 1) == vol->name_len) {
1148c2ecf20Sopenharmony_ci		pr_err("\tname            %s\n", vol->name);
1158c2ecf20Sopenharmony_ci	} else {
1168c2ecf20Sopenharmony_ci		pr_err("\t1st 5 characters of name: %c%c%c%c%c\n",
1178c2ecf20Sopenharmony_ci		       vol->name[0], vol->name[1], vol->name[2],
1188c2ecf20Sopenharmony_ci		       vol->name[3], vol->name[4]);
1198c2ecf20Sopenharmony_ci	}
1208c2ecf20Sopenharmony_ci}
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci/**
1238c2ecf20Sopenharmony_ci * ubi_dump_vtbl_record - dump a &struct ubi_vtbl_record object.
1248c2ecf20Sopenharmony_ci * @r: the object to dump
1258c2ecf20Sopenharmony_ci * @idx: volume table index
1268c2ecf20Sopenharmony_ci */
1278c2ecf20Sopenharmony_civoid ubi_dump_vtbl_record(const struct ubi_vtbl_record *r, int idx)
1288c2ecf20Sopenharmony_ci{
1298c2ecf20Sopenharmony_ci	int name_len = be16_to_cpu(r->name_len);
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	pr_err("Volume table record %d dump:\n", idx);
1328c2ecf20Sopenharmony_ci	pr_err("\treserved_pebs   %d\n", be32_to_cpu(r->reserved_pebs));
1338c2ecf20Sopenharmony_ci	pr_err("\talignment       %d\n", be32_to_cpu(r->alignment));
1348c2ecf20Sopenharmony_ci	pr_err("\tdata_pad        %d\n", be32_to_cpu(r->data_pad));
1358c2ecf20Sopenharmony_ci	pr_err("\tvol_type        %d\n", (int)r->vol_type);
1368c2ecf20Sopenharmony_ci	pr_err("\tupd_marker      %d\n", (int)r->upd_marker);
1378c2ecf20Sopenharmony_ci	pr_err("\tname_len        %d\n", name_len);
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	if (r->name[0] == '\0') {
1408c2ecf20Sopenharmony_ci		pr_err("\tname            NULL\n");
1418c2ecf20Sopenharmony_ci		return;
1428c2ecf20Sopenharmony_ci	}
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	if (name_len <= UBI_VOL_NAME_MAX &&
1458c2ecf20Sopenharmony_ci	    strnlen(&r->name[0], name_len + 1) == name_len) {
1468c2ecf20Sopenharmony_ci		pr_err("\tname            %s\n", &r->name[0]);
1478c2ecf20Sopenharmony_ci	} else {
1488c2ecf20Sopenharmony_ci		pr_err("\t1st 5 characters of name: %c%c%c%c%c\n",
1498c2ecf20Sopenharmony_ci			r->name[0], r->name[1], r->name[2], r->name[3],
1508c2ecf20Sopenharmony_ci			r->name[4]);
1518c2ecf20Sopenharmony_ci	}
1528c2ecf20Sopenharmony_ci	pr_err("\tcrc             %#08x\n", be32_to_cpu(r->crc));
1538c2ecf20Sopenharmony_ci}
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci/**
1568c2ecf20Sopenharmony_ci * ubi_dump_av - dump a &struct ubi_ainf_volume object.
1578c2ecf20Sopenharmony_ci * @av: the object to dump
1588c2ecf20Sopenharmony_ci */
1598c2ecf20Sopenharmony_civoid ubi_dump_av(const struct ubi_ainf_volume *av)
1608c2ecf20Sopenharmony_ci{
1618c2ecf20Sopenharmony_ci	pr_err("Volume attaching information dump:\n");
1628c2ecf20Sopenharmony_ci	pr_err("\tvol_id         %d\n", av->vol_id);
1638c2ecf20Sopenharmony_ci	pr_err("\thighest_lnum   %d\n", av->highest_lnum);
1648c2ecf20Sopenharmony_ci	pr_err("\tleb_count      %d\n", av->leb_count);
1658c2ecf20Sopenharmony_ci	pr_err("\tcompat         %d\n", av->compat);
1668c2ecf20Sopenharmony_ci	pr_err("\tvol_type       %d\n", av->vol_type);
1678c2ecf20Sopenharmony_ci	pr_err("\tused_ebs       %d\n", av->used_ebs);
1688c2ecf20Sopenharmony_ci	pr_err("\tlast_data_size %d\n", av->last_data_size);
1698c2ecf20Sopenharmony_ci	pr_err("\tdata_pad       %d\n", av->data_pad);
1708c2ecf20Sopenharmony_ci}
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci/**
1738c2ecf20Sopenharmony_ci * ubi_dump_aeb - dump a &struct ubi_ainf_peb object.
1748c2ecf20Sopenharmony_ci * @aeb: the object to dump
1758c2ecf20Sopenharmony_ci * @type: object type: 0 - not corrupted, 1 - corrupted
1768c2ecf20Sopenharmony_ci */
1778c2ecf20Sopenharmony_civoid ubi_dump_aeb(const struct ubi_ainf_peb *aeb, int type)
1788c2ecf20Sopenharmony_ci{
1798c2ecf20Sopenharmony_ci	pr_err("eraseblock attaching information dump:\n");
1808c2ecf20Sopenharmony_ci	pr_err("\tec       %d\n", aeb->ec);
1818c2ecf20Sopenharmony_ci	pr_err("\tpnum     %d\n", aeb->pnum);
1828c2ecf20Sopenharmony_ci	if (type == 0) {
1838c2ecf20Sopenharmony_ci		pr_err("\tlnum     %d\n", aeb->lnum);
1848c2ecf20Sopenharmony_ci		pr_err("\tscrub    %d\n", aeb->scrub);
1858c2ecf20Sopenharmony_ci		pr_err("\tsqnum    %llu\n", aeb->sqnum);
1868c2ecf20Sopenharmony_ci	}
1878c2ecf20Sopenharmony_ci}
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci/**
1908c2ecf20Sopenharmony_ci * ubi_dump_mkvol_req - dump a &struct ubi_mkvol_req object.
1918c2ecf20Sopenharmony_ci * @req: the object to dump
1928c2ecf20Sopenharmony_ci */
1938c2ecf20Sopenharmony_civoid ubi_dump_mkvol_req(const struct ubi_mkvol_req *req)
1948c2ecf20Sopenharmony_ci{
1958c2ecf20Sopenharmony_ci	char nm[17];
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	pr_err("Volume creation request dump:\n");
1988c2ecf20Sopenharmony_ci	pr_err("\tvol_id    %d\n",   req->vol_id);
1998c2ecf20Sopenharmony_ci	pr_err("\talignment %d\n",   req->alignment);
2008c2ecf20Sopenharmony_ci	pr_err("\tbytes     %lld\n", (long long)req->bytes);
2018c2ecf20Sopenharmony_ci	pr_err("\tvol_type  %d\n",   req->vol_type);
2028c2ecf20Sopenharmony_ci	pr_err("\tname_len  %d\n",   req->name_len);
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	memcpy(nm, req->name, 16);
2058c2ecf20Sopenharmony_ci	nm[16] = 0;
2068c2ecf20Sopenharmony_ci	pr_err("\t1st 16 characters of name: %s\n", nm);
2078c2ecf20Sopenharmony_ci}
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci/*
2108c2ecf20Sopenharmony_ci * Root directory for UBI stuff in debugfs. Contains sub-directories which
2118c2ecf20Sopenharmony_ci * contain the stuff specific to particular UBI devices.
2128c2ecf20Sopenharmony_ci */
2138c2ecf20Sopenharmony_cistatic struct dentry *dfs_rootdir;
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci/**
2168c2ecf20Sopenharmony_ci * ubi_debugfs_init - create UBI debugfs directory.
2178c2ecf20Sopenharmony_ci *
2188c2ecf20Sopenharmony_ci * Create UBI debugfs directory. Returns zero in case of success and a negative
2198c2ecf20Sopenharmony_ci * error code in case of failure.
2208c2ecf20Sopenharmony_ci */
2218c2ecf20Sopenharmony_ciint ubi_debugfs_init(void)
2228c2ecf20Sopenharmony_ci{
2238c2ecf20Sopenharmony_ci	if (!IS_ENABLED(CONFIG_DEBUG_FS))
2248c2ecf20Sopenharmony_ci		return 0;
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	dfs_rootdir = debugfs_create_dir("ubi", NULL);
2278c2ecf20Sopenharmony_ci	if (IS_ERR_OR_NULL(dfs_rootdir)) {
2288c2ecf20Sopenharmony_ci		int err = dfs_rootdir ? PTR_ERR(dfs_rootdir) : -ENODEV;
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci		pr_err("UBI error: cannot create \"ubi\" debugfs directory, error %d\n",
2318c2ecf20Sopenharmony_ci		       err);
2328c2ecf20Sopenharmony_ci		return err;
2338c2ecf20Sopenharmony_ci	}
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	return 0;
2368c2ecf20Sopenharmony_ci}
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci/**
2398c2ecf20Sopenharmony_ci * ubi_debugfs_exit - remove UBI debugfs directory.
2408c2ecf20Sopenharmony_ci */
2418c2ecf20Sopenharmony_civoid ubi_debugfs_exit(void)
2428c2ecf20Sopenharmony_ci{
2438c2ecf20Sopenharmony_ci	if (IS_ENABLED(CONFIG_DEBUG_FS))
2448c2ecf20Sopenharmony_ci		debugfs_remove(dfs_rootdir);
2458c2ecf20Sopenharmony_ci}
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci/* Read an UBI debugfs file */
2488c2ecf20Sopenharmony_cistatic ssize_t dfs_file_read(struct file *file, char __user *user_buf,
2498c2ecf20Sopenharmony_ci			     size_t count, loff_t *ppos)
2508c2ecf20Sopenharmony_ci{
2518c2ecf20Sopenharmony_ci	unsigned long ubi_num = (unsigned long)file->private_data;
2528c2ecf20Sopenharmony_ci	struct dentry *dent = file->f_path.dentry;
2538c2ecf20Sopenharmony_ci	struct ubi_device *ubi;
2548c2ecf20Sopenharmony_ci	struct ubi_debug_info *d;
2558c2ecf20Sopenharmony_ci	char buf[8];
2568c2ecf20Sopenharmony_ci	int val;
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci	ubi = ubi_get_device(ubi_num);
2598c2ecf20Sopenharmony_ci	if (!ubi)
2608c2ecf20Sopenharmony_ci		return -ENODEV;
2618c2ecf20Sopenharmony_ci	d = &ubi->dbg;
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	if (dent == d->dfs_chk_gen)
2648c2ecf20Sopenharmony_ci		val = d->chk_gen;
2658c2ecf20Sopenharmony_ci	else if (dent == d->dfs_chk_io)
2668c2ecf20Sopenharmony_ci		val = d->chk_io;
2678c2ecf20Sopenharmony_ci	else if (dent == d->dfs_chk_fastmap)
2688c2ecf20Sopenharmony_ci		val = d->chk_fastmap;
2698c2ecf20Sopenharmony_ci	else if (dent == d->dfs_disable_bgt)
2708c2ecf20Sopenharmony_ci		val = d->disable_bgt;
2718c2ecf20Sopenharmony_ci	else if (dent == d->dfs_emulate_bitflips)
2728c2ecf20Sopenharmony_ci		val = d->emulate_bitflips;
2738c2ecf20Sopenharmony_ci	else if (dent == d->dfs_emulate_io_failures)
2748c2ecf20Sopenharmony_ci		val = d->emulate_io_failures;
2758c2ecf20Sopenharmony_ci	else if (dent == d->dfs_emulate_power_cut) {
2768c2ecf20Sopenharmony_ci		snprintf(buf, sizeof(buf), "%u\n", d->emulate_power_cut);
2778c2ecf20Sopenharmony_ci		count = simple_read_from_buffer(user_buf, count, ppos,
2788c2ecf20Sopenharmony_ci						buf, strlen(buf));
2798c2ecf20Sopenharmony_ci		goto out;
2808c2ecf20Sopenharmony_ci	} else if (dent == d->dfs_power_cut_min) {
2818c2ecf20Sopenharmony_ci		snprintf(buf, sizeof(buf), "%u\n", d->power_cut_min);
2828c2ecf20Sopenharmony_ci		count = simple_read_from_buffer(user_buf, count, ppos,
2838c2ecf20Sopenharmony_ci						buf, strlen(buf));
2848c2ecf20Sopenharmony_ci		goto out;
2858c2ecf20Sopenharmony_ci	} else if (dent == d->dfs_power_cut_max) {
2868c2ecf20Sopenharmony_ci		snprintf(buf, sizeof(buf), "%u\n", d->power_cut_max);
2878c2ecf20Sopenharmony_ci		count = simple_read_from_buffer(user_buf, count, ppos,
2888c2ecf20Sopenharmony_ci						buf, strlen(buf));
2898c2ecf20Sopenharmony_ci		goto out;
2908c2ecf20Sopenharmony_ci	}
2918c2ecf20Sopenharmony_ci	else {
2928c2ecf20Sopenharmony_ci		count = -EINVAL;
2938c2ecf20Sopenharmony_ci		goto out;
2948c2ecf20Sopenharmony_ci	}
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	if (val)
2978c2ecf20Sopenharmony_ci		buf[0] = '1';
2988c2ecf20Sopenharmony_ci	else
2998c2ecf20Sopenharmony_ci		buf[0] = '0';
3008c2ecf20Sopenharmony_ci	buf[1] = '\n';
3018c2ecf20Sopenharmony_ci	buf[2] = 0x00;
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci	count = simple_read_from_buffer(user_buf, count, ppos, buf, 2);
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ciout:
3068c2ecf20Sopenharmony_ci	ubi_put_device(ubi);
3078c2ecf20Sopenharmony_ci	return count;
3088c2ecf20Sopenharmony_ci}
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci/* Write an UBI debugfs file */
3118c2ecf20Sopenharmony_cistatic ssize_t dfs_file_write(struct file *file, const char __user *user_buf,
3128c2ecf20Sopenharmony_ci			      size_t count, loff_t *ppos)
3138c2ecf20Sopenharmony_ci{
3148c2ecf20Sopenharmony_ci	unsigned long ubi_num = (unsigned long)file->private_data;
3158c2ecf20Sopenharmony_ci	struct dentry *dent = file->f_path.dentry;
3168c2ecf20Sopenharmony_ci	struct ubi_device *ubi;
3178c2ecf20Sopenharmony_ci	struct ubi_debug_info *d;
3188c2ecf20Sopenharmony_ci	size_t buf_size;
3198c2ecf20Sopenharmony_ci	char buf[8] = {0};
3208c2ecf20Sopenharmony_ci	int val;
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	ubi = ubi_get_device(ubi_num);
3238c2ecf20Sopenharmony_ci	if (!ubi)
3248c2ecf20Sopenharmony_ci		return -ENODEV;
3258c2ecf20Sopenharmony_ci	d = &ubi->dbg;
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	buf_size = min_t(size_t, count, (sizeof(buf) - 1));
3288c2ecf20Sopenharmony_ci	if (copy_from_user(buf, user_buf, buf_size)) {
3298c2ecf20Sopenharmony_ci		count = -EFAULT;
3308c2ecf20Sopenharmony_ci		goto out;
3318c2ecf20Sopenharmony_ci	}
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci	if (dent == d->dfs_power_cut_min) {
3348c2ecf20Sopenharmony_ci		if (kstrtouint(buf, 0, &d->power_cut_min) != 0)
3358c2ecf20Sopenharmony_ci			count = -EINVAL;
3368c2ecf20Sopenharmony_ci		goto out;
3378c2ecf20Sopenharmony_ci	} else if (dent == d->dfs_power_cut_max) {
3388c2ecf20Sopenharmony_ci		if (kstrtouint(buf, 0, &d->power_cut_max) != 0)
3398c2ecf20Sopenharmony_ci			count = -EINVAL;
3408c2ecf20Sopenharmony_ci		goto out;
3418c2ecf20Sopenharmony_ci	} else if (dent == d->dfs_emulate_power_cut) {
3428c2ecf20Sopenharmony_ci		if (kstrtoint(buf, 0, &val) != 0)
3438c2ecf20Sopenharmony_ci			count = -EINVAL;
3448c2ecf20Sopenharmony_ci		else
3458c2ecf20Sopenharmony_ci			d->emulate_power_cut = val;
3468c2ecf20Sopenharmony_ci		goto out;
3478c2ecf20Sopenharmony_ci	}
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci	if (buf[0] == '1')
3508c2ecf20Sopenharmony_ci		val = 1;
3518c2ecf20Sopenharmony_ci	else if (buf[0] == '0')
3528c2ecf20Sopenharmony_ci		val = 0;
3538c2ecf20Sopenharmony_ci	else {
3548c2ecf20Sopenharmony_ci		count = -EINVAL;
3558c2ecf20Sopenharmony_ci		goto out;
3568c2ecf20Sopenharmony_ci	}
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci	if (dent == d->dfs_chk_gen)
3598c2ecf20Sopenharmony_ci		d->chk_gen = val;
3608c2ecf20Sopenharmony_ci	else if (dent == d->dfs_chk_io)
3618c2ecf20Sopenharmony_ci		d->chk_io = val;
3628c2ecf20Sopenharmony_ci	else if (dent == d->dfs_chk_fastmap)
3638c2ecf20Sopenharmony_ci		d->chk_fastmap = val;
3648c2ecf20Sopenharmony_ci	else if (dent == d->dfs_disable_bgt)
3658c2ecf20Sopenharmony_ci		d->disable_bgt = val;
3668c2ecf20Sopenharmony_ci	else if (dent == d->dfs_emulate_bitflips)
3678c2ecf20Sopenharmony_ci		d->emulate_bitflips = val;
3688c2ecf20Sopenharmony_ci	else if (dent == d->dfs_emulate_io_failures)
3698c2ecf20Sopenharmony_ci		d->emulate_io_failures = val;
3708c2ecf20Sopenharmony_ci	else
3718c2ecf20Sopenharmony_ci		count = -EINVAL;
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ciout:
3748c2ecf20Sopenharmony_ci	ubi_put_device(ubi);
3758c2ecf20Sopenharmony_ci	return count;
3768c2ecf20Sopenharmony_ci}
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci/* File operations for all UBI debugfs files except
3798c2ecf20Sopenharmony_ci * detailed_erase_block_info
3808c2ecf20Sopenharmony_ci */
3818c2ecf20Sopenharmony_cistatic const struct file_operations dfs_fops = {
3828c2ecf20Sopenharmony_ci	.read   = dfs_file_read,
3838c2ecf20Sopenharmony_ci	.write  = dfs_file_write,
3848c2ecf20Sopenharmony_ci	.open	= simple_open,
3858c2ecf20Sopenharmony_ci	.llseek = no_llseek,
3868c2ecf20Sopenharmony_ci	.owner  = THIS_MODULE,
3878c2ecf20Sopenharmony_ci};
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci/* As long as the position is less then that total number of erase blocks,
3908c2ecf20Sopenharmony_ci * we still have more to print.
3918c2ecf20Sopenharmony_ci */
3928c2ecf20Sopenharmony_cistatic void *eraseblk_count_seq_start(struct seq_file *s, loff_t *pos)
3938c2ecf20Sopenharmony_ci{
3948c2ecf20Sopenharmony_ci	struct ubi_device *ubi = s->private;
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci	if (*pos < ubi->peb_count)
3978c2ecf20Sopenharmony_ci		return pos;
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci	return NULL;
4008c2ecf20Sopenharmony_ci}
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci/* Since we are using the position as the iterator, we just need to check if we
4038c2ecf20Sopenharmony_ci * are done and increment the position.
4048c2ecf20Sopenharmony_ci */
4058c2ecf20Sopenharmony_cistatic void *eraseblk_count_seq_next(struct seq_file *s, void *v, loff_t *pos)
4068c2ecf20Sopenharmony_ci{
4078c2ecf20Sopenharmony_ci	struct ubi_device *ubi = s->private;
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci	(*pos)++;
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci	if (*pos < ubi->peb_count)
4128c2ecf20Sopenharmony_ci		return pos;
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci	return NULL;
4158c2ecf20Sopenharmony_ci}
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_cistatic void eraseblk_count_seq_stop(struct seq_file *s, void *v)
4188c2ecf20Sopenharmony_ci{
4198c2ecf20Sopenharmony_ci}
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_cistatic int eraseblk_count_seq_show(struct seq_file *s, void *iter)
4228c2ecf20Sopenharmony_ci{
4238c2ecf20Sopenharmony_ci	struct ubi_device *ubi = s->private;
4248c2ecf20Sopenharmony_ci	struct ubi_wl_entry *wl;
4258c2ecf20Sopenharmony_ci	int *block_number = iter;
4268c2ecf20Sopenharmony_ci	int erase_count = -1;
4278c2ecf20Sopenharmony_ci	int err;
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci	/* If this is the start, print a header */
4308c2ecf20Sopenharmony_ci	if (*block_number == 0)
4318c2ecf20Sopenharmony_ci		seq_puts(s, "physical_block_number\terase_count\n");
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_ci	err = ubi_io_is_bad(ubi, *block_number);
4348c2ecf20Sopenharmony_ci	if (err)
4358c2ecf20Sopenharmony_ci		return err;
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci	spin_lock(&ubi->wl_lock);
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci	wl = ubi->lookuptbl[*block_number];
4408c2ecf20Sopenharmony_ci	if (wl)
4418c2ecf20Sopenharmony_ci		erase_count = wl->ec;
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci	spin_unlock(&ubi->wl_lock);
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci	if (erase_count < 0)
4468c2ecf20Sopenharmony_ci		return 0;
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci	seq_printf(s, "%-22d\t%-11d\n", *block_number, erase_count);
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci	return 0;
4518c2ecf20Sopenharmony_ci}
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_cistatic const struct seq_operations eraseblk_count_seq_ops = {
4548c2ecf20Sopenharmony_ci	.start = eraseblk_count_seq_start,
4558c2ecf20Sopenharmony_ci	.next = eraseblk_count_seq_next,
4568c2ecf20Sopenharmony_ci	.stop = eraseblk_count_seq_stop,
4578c2ecf20Sopenharmony_ci	.show = eraseblk_count_seq_show
4588c2ecf20Sopenharmony_ci};
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_cistatic int eraseblk_count_open(struct inode *inode, struct file *f)
4618c2ecf20Sopenharmony_ci{
4628c2ecf20Sopenharmony_ci	struct seq_file *s;
4638c2ecf20Sopenharmony_ci	int err;
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_ci	err = seq_open(f, &eraseblk_count_seq_ops);
4668c2ecf20Sopenharmony_ci	if (err)
4678c2ecf20Sopenharmony_ci		return err;
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci	s = f->private_data;
4708c2ecf20Sopenharmony_ci	s->private = ubi_get_device((unsigned long)inode->i_private);
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_ci	if (!s->private)
4738c2ecf20Sopenharmony_ci		return -ENODEV;
4748c2ecf20Sopenharmony_ci	else
4758c2ecf20Sopenharmony_ci		return 0;
4768c2ecf20Sopenharmony_ci}
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_cistatic int eraseblk_count_release(struct inode *inode, struct file *f)
4798c2ecf20Sopenharmony_ci{
4808c2ecf20Sopenharmony_ci	struct seq_file *s = f->private_data;
4818c2ecf20Sopenharmony_ci	struct ubi_device *ubi = s->private;
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci	ubi_put_device(ubi);
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_ci	return seq_release(inode, f);
4868c2ecf20Sopenharmony_ci}
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_cistatic const struct file_operations eraseblk_count_fops = {
4898c2ecf20Sopenharmony_ci	.owner = THIS_MODULE,
4908c2ecf20Sopenharmony_ci	.open = eraseblk_count_open,
4918c2ecf20Sopenharmony_ci	.read = seq_read,
4928c2ecf20Sopenharmony_ci	.llseek = seq_lseek,
4938c2ecf20Sopenharmony_ci	.release = eraseblk_count_release,
4948c2ecf20Sopenharmony_ci};
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_ci/**
4978c2ecf20Sopenharmony_ci * ubi_debugfs_init_dev - initialize debugfs for an UBI device.
4988c2ecf20Sopenharmony_ci * @ubi: UBI device description object
4998c2ecf20Sopenharmony_ci *
5008c2ecf20Sopenharmony_ci * This function creates all debugfs files for UBI device @ubi. Returns zero in
5018c2ecf20Sopenharmony_ci * case of success and a negative error code in case of failure.
5028c2ecf20Sopenharmony_ci */
5038c2ecf20Sopenharmony_ciint ubi_debugfs_init_dev(struct ubi_device *ubi)
5048c2ecf20Sopenharmony_ci{
5058c2ecf20Sopenharmony_ci	unsigned long ubi_num = ubi->ubi_num;
5068c2ecf20Sopenharmony_ci	struct ubi_debug_info *d = &ubi->dbg;
5078c2ecf20Sopenharmony_ci	int n;
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ci	if (!IS_ENABLED(CONFIG_DEBUG_FS))
5108c2ecf20Sopenharmony_ci		return 0;
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_ci	n = snprintf(d->dfs_dir_name, UBI_DFS_DIR_LEN + 1, UBI_DFS_DIR_NAME,
5138c2ecf20Sopenharmony_ci		     ubi->ubi_num);
5148c2ecf20Sopenharmony_ci	if (n == UBI_DFS_DIR_LEN) {
5158c2ecf20Sopenharmony_ci		/* The array size is too small */
5168c2ecf20Sopenharmony_ci		return -EINVAL;
5178c2ecf20Sopenharmony_ci	}
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_ci	d->dfs_dir = debugfs_create_dir(d->dfs_dir_name, dfs_rootdir);
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ci	d->dfs_chk_gen = debugfs_create_file("chk_gen", S_IWUSR, d->dfs_dir,
5228c2ecf20Sopenharmony_ci					     (void *)ubi_num, &dfs_fops);
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_ci	d->dfs_chk_io = debugfs_create_file("chk_io", S_IWUSR, d->dfs_dir,
5258c2ecf20Sopenharmony_ci					    (void *)ubi_num, &dfs_fops);
5268c2ecf20Sopenharmony_ci
5278c2ecf20Sopenharmony_ci	d->dfs_chk_fastmap = debugfs_create_file("chk_fastmap", S_IWUSR,
5288c2ecf20Sopenharmony_ci						 d->dfs_dir, (void *)ubi_num,
5298c2ecf20Sopenharmony_ci						 &dfs_fops);
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_ci	d->dfs_disable_bgt = debugfs_create_file("tst_disable_bgt", S_IWUSR,
5328c2ecf20Sopenharmony_ci						 d->dfs_dir, (void *)ubi_num,
5338c2ecf20Sopenharmony_ci						 &dfs_fops);
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_ci	d->dfs_emulate_bitflips = debugfs_create_file("tst_emulate_bitflips",
5368c2ecf20Sopenharmony_ci						      S_IWUSR, d->dfs_dir,
5378c2ecf20Sopenharmony_ci						      (void *)ubi_num,
5388c2ecf20Sopenharmony_ci						      &dfs_fops);
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ci	d->dfs_emulate_io_failures = debugfs_create_file("tst_emulate_io_failures",
5418c2ecf20Sopenharmony_ci							 S_IWUSR, d->dfs_dir,
5428c2ecf20Sopenharmony_ci							 (void *)ubi_num,
5438c2ecf20Sopenharmony_ci							 &dfs_fops);
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_ci	d->dfs_emulate_power_cut = debugfs_create_file("tst_emulate_power_cut",
5468c2ecf20Sopenharmony_ci						       S_IWUSR, d->dfs_dir,
5478c2ecf20Sopenharmony_ci						       (void *)ubi_num,
5488c2ecf20Sopenharmony_ci						       &dfs_fops);
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_ci	d->dfs_power_cut_min = debugfs_create_file("tst_emulate_power_cut_min",
5518c2ecf20Sopenharmony_ci						   S_IWUSR, d->dfs_dir,
5528c2ecf20Sopenharmony_ci						   (void *)ubi_num, &dfs_fops);
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_ci	d->dfs_power_cut_max = debugfs_create_file("tst_emulate_power_cut_max",
5558c2ecf20Sopenharmony_ci						   S_IWUSR, d->dfs_dir,
5568c2ecf20Sopenharmony_ci						   (void *)ubi_num, &dfs_fops);
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_ci	debugfs_create_file("detailed_erase_block_info", S_IRUSR, d->dfs_dir,
5598c2ecf20Sopenharmony_ci			    (void *)ubi_num, &eraseblk_count_fops);
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_ci	return 0;
5628c2ecf20Sopenharmony_ci}
5638c2ecf20Sopenharmony_ci
5648c2ecf20Sopenharmony_ci/**
5658c2ecf20Sopenharmony_ci * dbg_debug_exit_dev - free all debugfs files corresponding to device @ubi
5668c2ecf20Sopenharmony_ci * @ubi: UBI device description object
5678c2ecf20Sopenharmony_ci */
5688c2ecf20Sopenharmony_civoid ubi_debugfs_exit_dev(struct ubi_device *ubi)
5698c2ecf20Sopenharmony_ci{
5708c2ecf20Sopenharmony_ci	if (IS_ENABLED(CONFIG_DEBUG_FS))
5718c2ecf20Sopenharmony_ci		debugfs_remove_recursive(ubi->dbg.dfs_dir);
5728c2ecf20Sopenharmony_ci}
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_ci/**
5758c2ecf20Sopenharmony_ci * ubi_dbg_power_cut - emulate a power cut if it is time to do so
5768c2ecf20Sopenharmony_ci * @ubi: UBI device description object
5778c2ecf20Sopenharmony_ci * @caller: Flags set to indicate from where the function is being called
5788c2ecf20Sopenharmony_ci *
5798c2ecf20Sopenharmony_ci * Returns non-zero if a power cut was emulated, zero if not.
5808c2ecf20Sopenharmony_ci */
5818c2ecf20Sopenharmony_ciint ubi_dbg_power_cut(struct ubi_device *ubi, int caller)
5828c2ecf20Sopenharmony_ci{
5838c2ecf20Sopenharmony_ci	unsigned int range;
5848c2ecf20Sopenharmony_ci
5858c2ecf20Sopenharmony_ci	if ((ubi->dbg.emulate_power_cut & caller) == 0)
5868c2ecf20Sopenharmony_ci		return 0;
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_ci	if (ubi->dbg.power_cut_counter == 0) {
5898c2ecf20Sopenharmony_ci		ubi->dbg.power_cut_counter = ubi->dbg.power_cut_min;
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_ci		if (ubi->dbg.power_cut_max > ubi->dbg.power_cut_min) {
5928c2ecf20Sopenharmony_ci			range = ubi->dbg.power_cut_max - ubi->dbg.power_cut_min;
5938c2ecf20Sopenharmony_ci			ubi->dbg.power_cut_counter += prandom_u32() % range;
5948c2ecf20Sopenharmony_ci		}
5958c2ecf20Sopenharmony_ci		return 0;
5968c2ecf20Sopenharmony_ci	}
5978c2ecf20Sopenharmony_ci
5988c2ecf20Sopenharmony_ci	ubi->dbg.power_cut_counter--;
5998c2ecf20Sopenharmony_ci	if (ubi->dbg.power_cut_counter)
6008c2ecf20Sopenharmony_ci		return 0;
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_ci	ubi_msg(ubi, "XXXXXXXXXXXXXXX emulating a power cut XXXXXXXXXXXXXXXX");
6038c2ecf20Sopenharmony_ci	ubi_ro_mode(ubi);
6048c2ecf20Sopenharmony_ci	return 1;
6058c2ecf20Sopenharmony_ci}
606