Revision 016e8d44bc06dd3322f26712bdd3f3a6973592d0 authored by Josh Boyer on 19 August 2011, 18:50:26 UTC, committed by Al Viro on 19 November 2011, 16:13:26 UTC
Newer versions of MINIX can create filesystems that allocate an extra
bitmap block.  Mounting of this succeeds, but doing a statfs call will
result in an oops in count_free because of a negative number being used
for the bh index.

Avoid this by verifying the number of allocated blocks at mount time,
erroring out if there are not enough and make statfs ignore the extras
if there are too many.

This fixes https://bugzilla.kernel.org/show_bug.cgi?id=18792

Signed-off-by: Josh Boyer <jwboyer@redhat.com>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
1 parent d633180
Raw File
ab5500-debugfs.c
/*
 * Copyright (C) 2011 ST-Ericsson
 * License terms: GNU General Public License (GPL) version 2
 * Debugfs support for the AB5500 MFD driver
 */

#include <linux/export.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <linux/mfd/ab5500/ab5500.h>
#include <linux/mfd/abx500.h>
#include <linux/uaccess.h>

#include "ab5500-core.h"
#include "ab5500-debugfs.h"

static struct ab5500_i2c_ranges ab5500_reg_ranges[AB5500_NUM_BANKS] = {
	[AB5500_BANK_LED] = {
		.bankid = AB5500_BANK_LED,
		.nranges = 1,
		.range = (struct ab5500_reg_range[]) {
			{
				.first = 0x00,
				.last = 0x0C,
				.perm = AB5500_PERM_RW,
			},
		},
	},
	[AB5500_BANK_ADC] = {
		.bankid = AB5500_BANK_ADC,
		.nranges = 6,
		.range = (struct ab5500_reg_range[]) {
			{
				.first = 0x1F,
				.last = 0x22,
				.perm = AB5500_PERM_RO,
			},
			{
				.first = 0x23,
				.last = 0x24,
				.perm = AB5500_PERM_RW,
			},
			{
				.first = 0x26,
				.last = 0x2D,
				.perm = AB5500_PERM_RO,
			},
			{
				.first = 0x2F,
				.last = 0x34,
				.perm = AB5500_PERM_RW,
			},
			{
				.first = 0x37,
				.last = 0x57,
				.perm = AB5500_PERM_RW,
			},
			{
				.first = 0x58,
				.last = 0x58,
				.perm = AB5500_PERM_RO,
			},
		},
	},
	[AB5500_BANK_RTC] = {
		.bankid = AB5500_BANK_RTC,
		.nranges = 2,
		.range = (struct ab5500_reg_range[]) {
			{
				.first = 0x00,
				.last = 0x04,
				.perm = AB5500_PERM_RW,
			},
			{
				.first = 0x06,
				.last = 0x0C,
				.perm = AB5500_PERM_RW,
			},
		},
	},
	[AB5500_BANK_STARTUP] = {
		.bankid = AB5500_BANK_STARTUP,
		.nranges = 12,
		.range = (struct ab5500_reg_range[]) {
			{
				.first = 0x00,
				.last = 0x01,
				.perm = AB5500_PERM_RW,
			},
			{
				.first = 0x1F,
				.last = 0x1F,
				.perm = AB5500_PERM_RW,
			},
			{
				.first = 0x2E,
				.last = 0x2E,
				.perm = AB5500_PERM_RO,
			},
			{
				.first = 0x2F,
				.last = 0x30,
				.perm = AB5500_PERM_RW,
			},
			{
				.first = 0x50,
				.last = 0x51,
				.perm = AB5500_PERM_RW,
			},
			{
				.first = 0x60,
				.last = 0x61,
				.perm = AB5500_PERM_RW,
			},
			{
				.first = 0x66,
				.last = 0x8A,
				.perm = AB5500_PERM_RW,
			},
			{
				.first = 0x8C,
				.last = 0x96,
				.perm = AB5500_PERM_RW,
			},
			{
				.first = 0xAA,
				.last = 0xB4,
				.perm = AB5500_PERM_RW,
			},
			{
				.first = 0xB7,
				.last = 0xBF,
				.perm = AB5500_PERM_RW,
			},
			{
				.first = 0xC1,
				.last = 0xCA,
				.perm = AB5500_PERM_RW,
			},
			{
				.first = 0xD3,
				.last = 0xE0,
				.perm = AB5500_PERM_RW,
			},
		},
	},
	[AB5500_BANK_DBI_ECI] = {
		.bankid = AB5500_BANK_DBI_ECI,
		.nranges = 3,
		.range = (struct ab5500_reg_range[]) {
			{
				.first = 0x00,
				.last = 0x07,
				.perm = AB5500_PERM_RW,
			},
			{
				.first = 0x10,
				.last = 0x10,
				.perm = AB5500_PERM_RW,
			},
			{
				.first = 0x13,
				.last = 0x13,
				.perm = AB5500_PERM_RW,
			},
		},
	},
	[AB5500_BANK_CHG] = {
		.bankid = AB5500_BANK_CHG,
		.nranges = 2,
		.range = (struct ab5500_reg_range[]) {
			{
				.first = 0x11,
				.last = 0x11,
				.perm = AB5500_PERM_RO,
			},
			{
				.first = 0x12,
				.last = 0x1B,
				.perm = AB5500_PERM_RW,
			},
		},
	},
	[AB5500_BANK_FG_BATTCOM_ACC] = {
		.bankid = AB5500_BANK_FG_BATTCOM_ACC,
		.nranges = 2,
		.range = (struct ab5500_reg_range[]) {
			{
				.first = 0x00,
				.last = 0x0B,
				.perm = AB5500_PERM_RO,
			},
			{
				.first = 0x0C,
				.last = 0x10,
				.perm = AB5500_PERM_RW,
			},
		},
	},
	[AB5500_BANK_USB] = {
		.bankid = AB5500_BANK_USB,
		.nranges = 12,
		.range = (struct ab5500_reg_range[]) {
			{
				.first = 0x01,
				.last = 0x01,
				.perm = AB5500_PERM_RW,
			},
			{
				.first = 0x80,
				.last = 0x83,
				.perm = AB5500_PERM_RW,
			},
			{
				.first = 0x87,
				.last = 0x8A,
				.perm = AB5500_PERM_RW,
			},
			{
				.first = 0x8B,
				.last = 0x8B,
				.perm = AB5500_PERM_RO,
			},
			{
				.first = 0x91,
				.last = 0x92,
				.perm = AB5500_PERM_RO,
			},
			{
				.first = 0x93,
				.last = 0x93,
				.perm = AB5500_PERM_RW,
			},
			{
				.first = 0x94,
				.last = 0x94,
				.perm = AB5500_PERM_RO,
			},
			{
				.first = 0xA8,
				.last = 0xB0,
				.perm = AB5500_PERM_RO,
			},
			{
				.first = 0xB2,
				.last = 0xB2,
				.perm = AB5500_PERM_RO,
			},
			{
				.first = 0xB4,
				.last = 0xBC,
				.perm = AB5500_PERM_RO,
			},
			{
				.first = 0xBF,
				.last = 0xBF,
				.perm = AB5500_PERM_RO,
			},
			{
				.first = 0xC1,
				.last = 0xC5,
				.perm = AB5500_PERM_RO,
			},
		},
	},
	[AB5500_BANK_IT] = {
		.bankid = AB5500_BANK_IT,
		.nranges = 4,
		.range = (struct ab5500_reg_range[]) {
			{
				.first = 0x00,
				.last = 0x02,
				.perm = AB5500_PERM_RO,
			},
			{
				.first = 0x20,
				.last = 0x36,
				.perm = AB5500_PERM_RO,
			},
			{
				.first = 0x40,
				.last = 0x56,
				.perm = AB5500_PERM_RO,
			},
			{
				.first = 0x60,
				.last = 0x76,
				.perm = AB5500_PERM_RO,
			},
		},
	},
	[AB5500_BANK_VDDDIG_IO_I2C_CLK_TST] = {
		.bankid = AB5500_BANK_VDDDIG_IO_I2C_CLK_TST,
		.nranges = 7,
		.range = (struct ab5500_reg_range[]) {
			{
				.first = 0x02,
				.last = 0x02,
				.perm = AB5500_PERM_RW,
			},
			{
				.first = 0x12,
				.last = 0x12,
				.perm = AB5500_PERM_RW,
			},
			{
				.first = 0x30,
				.last = 0x34,
				.perm = AB5500_PERM_RW,
			},
			{
				.first = 0x40,
				.last = 0x44,
				.perm = AB5500_PERM_RW,
			},
			{
				.first = 0x50,
				.last = 0x54,
				.perm = AB5500_PERM_RW,
			},
			{
				.first = 0x60,
				.last = 0x64,
				.perm = AB5500_PERM_RW,
			},
			{
				.first = 0x70,
				.last = 0x74,
				.perm = AB5500_PERM_RW,
			},
		},
	},
	[AB5500_BANK_VIT_IO_I2C_CLK_TST_OTP] = {
		.bankid = AB5500_BANK_VIT_IO_I2C_CLK_TST_OTP,
		.nranges = 13,
		.range = (struct ab5500_reg_range[]) {
			{
				.first = 0x01,
				.last = 0x01,
				.perm = AB5500_PERM_RW,
			},
			{
				.first = 0x02,
				.last = 0x02,
				.perm = AB5500_PERM_RO,
			},
			{
				.first = 0x0D,
				.last = 0x0F,
				.perm = AB5500_PERM_RW,
			},
			{
				.first = 0x1C,
				.last = 0x1C,
				.perm = AB5500_PERM_RW,
			},
			{
				.first = 0x1E,
				.last = 0x1E,
				.perm = AB5500_PERM_RW,
			},
			{
				.first = 0x20,
				.last = 0x21,
				.perm = AB5500_PERM_RW,
			},
			{
				.first = 0x25,
				.last = 0x25,
				.perm = AB5500_PERM_RW,
			},
			{
				.first = 0x28,
				.last = 0x2A,
				.perm = AB5500_PERM_RW,
			},
			{
				.first = 0x30,
				.last = 0x33,
				.perm = AB5500_PERM_RW,
			},
			{
				.first = 0x40,
				.last = 0x43,
				.perm = AB5500_PERM_RW,
			},
			{
				.first = 0x50,
				.last = 0x53,
				.perm = AB5500_PERM_RW,
			},
			{
				.first = 0x60,
				.last = 0x63,
				.perm = AB5500_PERM_RW,
			},
			{
				.first = 0x70,
				.last = 0x73,
				.perm = AB5500_PERM_RW,
			},
		},
	},
	[AB5500_BANK_VIBRA] = {
		.bankid = AB5500_BANK_VIBRA,
		.nranges = 2,
		.range = (struct ab5500_reg_range[]) {
			{
				.first = 0x10,
				.last = 0x13,
				.perm = AB5500_PERM_RW,
			},
			{
				.first = 0xFE,
				.last = 0xFE,
				.perm = AB5500_PERM_RW,
			},
		},
	},
	[AB5500_BANK_AUDIO_HEADSETUSB] = {
		.bankid = AB5500_BANK_AUDIO_HEADSETUSB,
		.nranges = 2,
		.range = (struct ab5500_reg_range[]) {
			{
				.first = 0x00,
				.last = 0x48,
				.perm = AB5500_PERM_RW,
			},
			{
				.first = 0xEB,
				.last = 0xFB,
				.perm = AB5500_PERM_RW,
			},
		},
	},
	[AB5500_BANK_SIM_USBSIM] = {
		.bankid = AB5500_BANK_SIM_USBSIM,
		.nranges = 1,
		.range = (struct ab5500_reg_range[]) {
			{
				.first = 0x13,
				.last = 0x19,
				.perm = AB5500_PERM_RW,
			},
		},
	},
	[AB5500_BANK_VDENC] = {
		.bankid = AB5500_BANK_VDENC,
		.nranges = 12,
		.range = (struct ab5500_reg_range[]) {
			{
				.first = 0x00,
				.last = 0x08,
				.perm = AB5500_PERM_RW,
			},
			{
				.first = 0x09,
				.last = 0x09,
				.perm = AB5500_PERM_RO,
			},
			{
				.first = 0x0A,
				.last = 0x12,
				.perm = AB5500_PERM_RW,
			},
			{
				.first = 0x15,
				.last = 0x19,
				.perm = AB5500_PERM_RW,
			},
			{
				.first = 0x1B,
				.last = 0x21,
				.perm = AB5500_PERM_RW,
			},
			{
				.first = 0x27,
				.last = 0x2C,
				.perm = AB5500_PERM_RW,
			},
			{
				.first = 0x41,
				.last = 0x41,
				.perm = AB5500_PERM_RW,
			},
			{
				.first = 0x45,
				.last = 0x5B,
				.perm = AB5500_PERM_RW,
			},
			{
				.first = 0x5D,
				.last = 0x5D,
				.perm = AB5500_PERM_RW,
			},
			{
				.first = 0x69,
				.last = 0x69,
				.perm = AB5500_PERM_RW,
			},
			{
				.first = 0x6C,
				.last = 0x6D,
				.perm = AB5500_PERM_RW,
			},
			{
				.first = 0x80,
				.last = 0x81,
				.perm = AB5500_PERM_RW,
			},
		},
	},
};

static int ab5500_registers_print(struct seq_file *s, void *p)
{
	struct ab5500 *ab = s->private;
	unsigned int i;
	u8 bank = (u8)ab->debug_bank;

	seq_printf(s, "ab5500 register values:\n");
	for (bank = 0; bank < AB5500_NUM_BANKS; bank++) {
		seq_printf(s, " bank %u, %s (0x%x):\n", bank,
				bankinfo[bank].name,
				bankinfo[bank].slave_addr);
		for (i = 0; i < ab5500_reg_ranges[bank].nranges; i++) {
			u8 reg;
			int err;

			for (reg = ab5500_reg_ranges[bank].range[i].first;
				reg <= ab5500_reg_ranges[bank].range[i].last;
				reg++) {
				u8 value;

				err = ab5500_get_register_interruptible_raw(ab,
								bank, reg,
								&value);
				if (err < 0) {
					dev_err(ab->dev, "get_reg failed %d"
						"bank 0x%x reg 0x%x\n",
						err, bank, reg);
					return err;
				}

				err = seq_printf(s, "[%d/0x%02X]: 0x%02X\n",
						bank, reg, value);
				if (err < 0) {
					dev_err(ab->dev,
						"seq_printf overflow\n");
					/*
					 * Error is not returned here since
					 * the output is wanted in any case
					 */
					return 0;
				}
			}
		}
	}
	return 0;
}

static int ab5500_registers_open(struct inode *inode, struct file *file)
{
	return single_open(file, ab5500_registers_print, inode->i_private);
}

static const struct file_operations ab5500_registers_fops = {
	.open = ab5500_registers_open,
	.read = seq_read,
	.llseek = seq_lseek,
	.release = single_release,
	.owner = THIS_MODULE,
};

static int ab5500_bank_print(struct seq_file *s, void *p)
{
	struct ab5500 *ab = s->private;

	seq_printf(s, "%d\n", ab->debug_bank);
	return 0;
}

static int ab5500_bank_open(struct inode *inode, struct file *file)
{
	return single_open(file, ab5500_bank_print, inode->i_private);
}

static ssize_t ab5500_bank_write(struct file *file,
	const char __user *user_buf,
	size_t count, loff_t *ppos)
{
	struct ab5500 *ab = ((struct seq_file *)(file->private_data))->private;
	char buf[32];
	int buf_size;
	unsigned long user_bank;
	int err;

	/* Get userspace string and assure termination */
	buf_size = min(count, (sizeof(buf) - 1));
	if (copy_from_user(buf, user_buf, buf_size))
		return -EFAULT;
	buf[buf_size] = 0;

	err = strict_strtoul(buf, 0, &user_bank);
	if (err)
		return -EINVAL;

	if (user_bank >= AB5500_NUM_BANKS) {
		dev_err(ab->dev,
			"debugfs error input > number of banks\n");
		return -EINVAL;
	}

	ab->debug_bank = user_bank;

	return buf_size;
}

static int ab5500_address_print(struct seq_file *s, void *p)
{
	struct ab5500 *ab = s->private;

	seq_printf(s, "0x%02X\n", ab->debug_address);
	return 0;
}

static int ab5500_address_open(struct inode *inode, struct file *file)
{
	return single_open(file, ab5500_address_print, inode->i_private);
}

static ssize_t ab5500_address_write(struct file *file,
	const char __user *user_buf,
	size_t count, loff_t *ppos)
{
	struct ab5500 *ab = ((struct seq_file *)(file->private_data))->private;
	char buf[32];
	int buf_size;
	unsigned long user_address;
	int err;

	/* Get userspace string and assure termination */
	buf_size = min(count, (sizeof(buf) - 1));
	if (copy_from_user(buf, user_buf, buf_size))
		return -EFAULT;
	buf[buf_size] = 0;

	err = strict_strtoul(buf, 0, &user_address);
	if (err)
		return -EINVAL;
	if (user_address > 0xff) {
		dev_err(ab->dev,
			"debugfs error input > 0xff\n");
		return -EINVAL;
	}
	ab->debug_address = user_address;
	return buf_size;
}

static int ab5500_val_print(struct seq_file *s, void *p)
{
	struct ab5500 *ab = s->private;
	int err;
	u8 regvalue;

	err = ab5500_get_register_interruptible_raw(ab, (u8)ab->debug_bank,
		(u8)ab->debug_address, &regvalue);
	if (err) {
		dev_err(ab->dev, "get_reg failed %d, bank 0x%x"
			", reg 0x%x\n", err, ab->debug_bank,
			ab->debug_address);
		return -EINVAL;
	}
	seq_printf(s, "0x%02X\n", regvalue);

	return 0;
}

static int ab5500_val_open(struct inode *inode, struct file *file)
{
	return single_open(file, ab5500_val_print, inode->i_private);
}

static ssize_t ab5500_val_write(struct file *file,
	const char __user *user_buf,
	size_t count, loff_t *ppos)
{
	struct ab5500 *ab = ((struct seq_file *)(file->private_data))->private;
	char buf[32];
	int buf_size;
	unsigned long user_val;
	int err;
	u8 regvalue;

	/* Get userspace string and assure termination */
	buf_size = min(count, (sizeof(buf)-1));
	if (copy_from_user(buf, user_buf, buf_size))
		return -EFAULT;
	buf[buf_size] = 0;

	err = strict_strtoul(buf, 0, &user_val);
	if (err)
		return -EINVAL;
	if (user_val > 0xff) {
		dev_err(ab->dev,
			"debugfs error input > 0xff\n");
		return -EINVAL;
	}
	err = ab5500_mask_and_set_register_interruptible_raw(
		ab, (u8)ab->debug_bank,
		(u8)ab->debug_address, 0xFF, (u8)user_val);
	if (err)
		return -EINVAL;

	ab5500_get_register_interruptible_raw(ab, (u8)ab->debug_bank,
		(u8)ab->debug_address, &regvalue);
	if (err)
		return -EINVAL;

	return buf_size;
}

static const struct file_operations ab5500_bank_fops = {
	.open = ab5500_bank_open,
	.write = ab5500_bank_write,
	.read = seq_read,
	.llseek = seq_lseek,
	.release = single_release,
	.owner = THIS_MODULE,
};

static const struct file_operations ab5500_address_fops = {
	.open = ab5500_address_open,
	.write = ab5500_address_write,
	.read = seq_read,
	.llseek = seq_lseek,
	.release = single_release,
	.owner = THIS_MODULE,
};

static const struct file_operations ab5500_val_fops = {
	.open = ab5500_val_open,
	.write = ab5500_val_write,
	.read = seq_read,
	.llseek = seq_lseek,
	.release = single_release,
	.owner = THIS_MODULE,
};

static struct dentry *ab5500_dir;
static struct dentry *ab5500_reg_file;
static struct dentry *ab5500_bank_file;
static struct dentry *ab5500_address_file;
static struct dentry *ab5500_val_file;

void __init ab5500_setup_debugfs(struct ab5500 *ab)
{
	ab->debug_bank = AB5500_BANK_VIT_IO_I2C_CLK_TST_OTP;
	ab->debug_address = AB5500_CHIP_ID;

	ab5500_dir = debugfs_create_dir("ab5500", NULL);
	if (!ab5500_dir)
		goto exit_no_debugfs;

	ab5500_reg_file = debugfs_create_file("all-bank-registers",
		S_IRUGO, ab5500_dir, ab, &ab5500_registers_fops);
	if (!ab5500_reg_file)
		goto exit_destroy_dir;

	ab5500_bank_file = debugfs_create_file("register-bank",
		(S_IRUGO | S_IWUGO), ab5500_dir, ab, &ab5500_bank_fops);
	if (!ab5500_bank_file)
		goto exit_destroy_reg;

	ab5500_address_file = debugfs_create_file("register-address",
		(S_IRUGO | S_IWUGO), ab5500_dir, ab, &ab5500_address_fops);
	if (!ab5500_address_file)
		goto exit_destroy_bank;

	ab5500_val_file = debugfs_create_file("register-value",
		(S_IRUGO | S_IWUGO), ab5500_dir, ab, &ab5500_val_fops);
	if (!ab5500_val_file)
		goto exit_destroy_address;

	return;

exit_destroy_address:
	debugfs_remove(ab5500_address_file);
exit_destroy_bank:
	debugfs_remove(ab5500_bank_file);
exit_destroy_reg:
	debugfs_remove(ab5500_reg_file);
exit_destroy_dir:
	debugfs_remove(ab5500_dir);
exit_no_debugfs:
	dev_err(ab->dev, "failed to create debugfs entries.\n");
	return;
}

void __exit ab5500_remove_debugfs(void)
{
	debugfs_remove(ab5500_val_file);
	debugfs_remove(ab5500_address_file);
	debugfs_remove(ab5500_bank_file);
	debugfs_remove(ab5500_reg_file);
	debugfs_remove(ab5500_dir);
}
back to top