Răsfoiți Sursa

Merge tag 'gpio-v4.9-2' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-gpio

Pull GPIO fixes from Linus Walleij:
 "Here is a set of GPIO fixes for the v4.9 kernel series:

   - Fix up off-by one and line offset validation, info leak to
     userspace, and reject invalid flags. Those are especially valuable
     hardening patches from Lars-Peter Clausen, all tagged for stable.

   - Fix module autoload for TS4800 and ATH79.

   - Correct the IRQ handler for MPC8xxx to use handle_level_irq() as it
     (a) reacts to edges not levels and (b) even implements .irq_ack().
     We were missing IRQs here.

   - Fix the error path for acpi_dev_gpio_irq_get()

   - Fix a memory leak in the MXS driver.

   - Fix an annoying typo in the STMPE driver.

   - Put a dependency on sysfs to the mockup driver"

* tag 'gpio-v4.9-2' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-gpio:
  gpio: mpc8xxx: Correct irq handler function
  gpio: ath79: Fix module autoload
  gpio: ts4800: Fix module autoload
  gpio: GPIO_GET_LINEEVENT_IOCTL: Reject invalid line and event flags
  gpio: GPIO_GET_LINEHANDLE_IOCTL: Reject invalid line flags
  gpio: GPIOHANDLE_GET_LINE_VALUES_IOCTL: Fix information leak
  gpio: GPIO_GET_LINEEVENT_IOCTL: Validate line offset
  gpio: GPIOHANDLE_GET_LINE_VALUES_IOCTL: Fix information leak
  gpio: GPIO_GET_LINEHANDLE_IOCTL: Validate line offset
  gpio: GPIO_GET_CHIPINFO_IOCTL: Fix information leak
  gpio: GPIO_GET_CHIPINFO_IOCTL: Fix line offset validation
  gpio / ACPI: fix returned error from acpi_dev_gpio_irq_get()
  gpio: mockup: add sysfs dependency
  gpio: stmpe: || vs && typo
  gpio: mxs: Unmap region obtained by of_iomap
  gpio/board.txt: point to gpiod_set_value
Linus Torvalds 8 ani în urmă
părinte
comite
1ce5bdb831

+ 7 - 4
Documentation/gpio/board.txt

@@ -6,7 +6,7 @@ Note that it only applies to the new descriptor-based interface. For a
 description of the deprecated integer-based GPIO interface please refer to
 description of the deprecated integer-based GPIO interface please refer to
 gpio-legacy.txt (actually, there is no real mapping possible with the old
 gpio-legacy.txt (actually, there is no real mapping possible with the old
 interface; you just fetch an integer from somewhere and request the
 interface; you just fetch an integer from somewhere and request the
-corresponding GPIO.
+corresponding GPIO).
 
 
 All platforms can enable the GPIO library, but if the platform strictly
 All platforms can enable the GPIO library, but if the platform strictly
 requires GPIO functionality to be present, it needs to select GPIOLIB from its
 requires GPIO functionality to be present, it needs to select GPIOLIB from its
@@ -162,6 +162,9 @@ The driver controlling "foo.0" will then be able to obtain its GPIOs as follows:
 
 
 Since the "led" GPIOs are mapped as active-high, this example will switch their
 Since the "led" GPIOs are mapped as active-high, this example will switch their
 signals to 1, i.e. enabling the LEDs. And for the "power" GPIO, which is mapped
 signals to 1, i.e. enabling the LEDs. And for the "power" GPIO, which is mapped
-as active-low, its actual signal will be 0 after this code. Contrary to the legacy
-integer GPIO interface, the active-low property is handled during mapping and is
-thus transparent to GPIO consumers.
+as active-low, its actual signal will be 0 after this code. Contrary to the
+legacy integer GPIO interface, the active-low property is handled during
+mapping and is thus transparent to GPIO consumers.
+
+A set of functions such as gpiod_set_value() is available to work with
+the new descriptor-oriented interface.

+ 1 - 1
drivers/gpio/Kconfig

@@ -284,7 +284,7 @@ config GPIO_MM_LANTIQ
 
 
 config GPIO_MOCKUP
 config GPIO_MOCKUP
 	tristate "GPIO Testing Driver"
 	tristate "GPIO Testing Driver"
-	depends on GPIOLIB
+	depends on GPIOLIB && SYSFS
 	select GPIO_SYSFS
 	select GPIO_SYSFS
 	help
 	help
 	  This enables GPIO Testing driver, which provides a way to test GPIO
 	  This enables GPIO Testing driver, which provides a way to test GPIO

+ 1 - 0
drivers/gpio/gpio-ath79.c

@@ -219,6 +219,7 @@ static const struct of_device_id ath79_gpio_of_match[] = {
 	{ .compatible = "qca,ar9340-gpio" },
 	{ .compatible = "qca,ar9340-gpio" },
 	{},
 	{},
 };
 };
+MODULE_DEVICE_TABLE(of, ath79_gpio_of_match);
 
 
 static int ath79_gpio_probe(struct platform_device *pdev)
 static int ath79_gpio_probe(struct platform_device *pdev)
 {
 {

+ 1 - 1
drivers/gpio/gpio-mpc8xxx.c

@@ -239,7 +239,7 @@ static int mpc8xxx_gpio_irq_map(struct irq_domain *h, unsigned int irq,
 				irq_hw_number_t hwirq)
 				irq_hw_number_t hwirq)
 {
 {
 	irq_set_chip_data(irq, h->host_data);
 	irq_set_chip_data(irq, h->host_data);
-	irq_set_chip_and_handler(irq, &mpc8xxx_irq_chip, handle_level_irq);
+	irq_set_chip_and_handler(irq, &mpc8xxx_irq_chip, handle_edge_irq);
 
 
 	return 0;
 	return 0;
 }
 }

+ 6 - 2
drivers/gpio/gpio-mxs.c

@@ -308,8 +308,10 @@ static int mxs_gpio_probe(struct platform_device *pdev)
 	writel(~0U, port->base + PINCTRL_IRQSTAT(port) + MXS_CLR);
 	writel(~0U, port->base + PINCTRL_IRQSTAT(port) + MXS_CLR);
 
 
 	irq_base = irq_alloc_descs(-1, 0, 32, numa_node_id());
 	irq_base = irq_alloc_descs(-1, 0, 32, numa_node_id());
-	if (irq_base < 0)
-		return irq_base;
+	if (irq_base < 0) {
+		err = irq_base;
+		goto out_iounmap;
+	}
 
 
 	port->domain = irq_domain_add_legacy(np, 32, irq_base, 0,
 	port->domain = irq_domain_add_legacy(np, 32, irq_base, 0,
 					     &irq_domain_simple_ops, NULL);
 					     &irq_domain_simple_ops, NULL);
@@ -349,6 +351,8 @@ out_irqdomain_remove:
 	irq_domain_remove(port->domain);
 	irq_domain_remove(port->domain);
 out_irqdesc_free:
 out_irqdesc_free:
 	irq_free_descs(irq_base, 32);
 	irq_free_descs(irq_base, 32);
+out_iounmap:
+	iounmap(port->base);
 	return err;
 	return err;
 }
 }
 
 

+ 1 - 1
drivers/gpio/gpio-stmpe.c

@@ -409,7 +409,7 @@ static irqreturn_t stmpe_gpio_irq(int irq, void *dev)
 		 * 801/1801/1600, bits are cleared when read.
 		 * 801/1801/1600, bits are cleared when read.
 		 * Edge detect register is not present on 801/1600/1801
 		 * Edge detect register is not present on 801/1600/1801
 		 */
 		 */
-		if (stmpe->partnum != STMPE801 || stmpe->partnum != STMPE1600 ||
+		if (stmpe->partnum != STMPE801 && stmpe->partnum != STMPE1600 &&
 		    stmpe->partnum != STMPE1801) {
 		    stmpe->partnum != STMPE1801) {
 			stmpe_reg_write(stmpe, statmsbreg + i, status[i]);
 			stmpe_reg_write(stmpe, statmsbreg + i, status[i]);
 			stmpe_reg_write(stmpe,
 			stmpe_reg_write(stmpe,

+ 1 - 0
drivers/gpio/gpio-ts4800.c

@@ -66,6 +66,7 @@ static const struct of_device_id ts4800_gpio_of_match[] = {
 	{ .compatible = "technologic,ts4800-gpio", },
 	{ .compatible = "technologic,ts4800-gpio", },
 	{},
 	{},
 };
 };
+MODULE_DEVICE_TABLE(of, ts4800_gpio_of_match);
 
 
 static struct platform_driver ts4800_gpio_driver = {
 static struct platform_driver ts4800_gpio_driver = {
 	.driver = {
 	.driver = {

+ 5 - 2
drivers/gpio/gpiolib-acpi.c

@@ -653,14 +653,17 @@ int acpi_dev_gpio_irq_get(struct acpi_device *adev, int index)
 {
 {
 	int idx, i;
 	int idx, i;
 	unsigned int irq_flags;
 	unsigned int irq_flags;
+	int ret = -ENOENT;
 
 
 	for (i = 0, idx = 0; idx <= index; i++) {
 	for (i = 0, idx = 0; idx <= index; i++) {
 		struct acpi_gpio_info info;
 		struct acpi_gpio_info info;
 		struct gpio_desc *desc;
 		struct gpio_desc *desc;
 
 
 		desc = acpi_get_gpiod_by_index(adev, NULL, i, &info);
 		desc = acpi_get_gpiod_by_index(adev, NULL, i, &info);
-		if (IS_ERR(desc))
+		if (IS_ERR(desc)) {
+			ret = PTR_ERR(desc);
 			break;
 			break;
+		}
 		if (info.gpioint && idx++ == index) {
 		if (info.gpioint && idx++ == index) {
 			int irq = gpiod_to_irq(desc);
 			int irq = gpiod_to_irq(desc);
 
 
@@ -679,7 +682,7 @@ int acpi_dev_gpio_irq_get(struct acpi_device *adev, int index)
 		}
 		}
 
 
 	}
 	}
-	return -ENOENT;
+	return ret;
 }
 }
 EXPORT_SYMBOL_GPL(acpi_dev_gpio_irq_get);
 EXPORT_SYMBOL_GPL(acpi_dev_gpio_irq_get);
 
 

+ 41 - 1
drivers/gpio/gpiolib.c

@@ -333,6 +333,13 @@ struct linehandle_state {
 	u32 numdescs;
 	u32 numdescs;
 };
 };
 
 
+#define GPIOHANDLE_REQUEST_VALID_FLAGS \
+	(GPIOHANDLE_REQUEST_INPUT | \
+	GPIOHANDLE_REQUEST_OUTPUT | \
+	GPIOHANDLE_REQUEST_ACTIVE_LOW | \
+	GPIOHANDLE_REQUEST_OPEN_DRAIN | \
+	GPIOHANDLE_REQUEST_OPEN_SOURCE)
+
 static long linehandle_ioctl(struct file *filep, unsigned int cmd,
 static long linehandle_ioctl(struct file *filep, unsigned int cmd,
 			     unsigned long arg)
 			     unsigned long arg)
 {
 {
@@ -344,6 +351,8 @@ static long linehandle_ioctl(struct file *filep, unsigned int cmd,
 	if (cmd == GPIOHANDLE_GET_LINE_VALUES_IOCTL) {
 	if (cmd == GPIOHANDLE_GET_LINE_VALUES_IOCTL) {
 		int val;
 		int val;
 
 
+		memset(&ghd, 0, sizeof(ghd));
+
 		/* TODO: check if descriptors are really input */
 		/* TODO: check if descriptors are really input */
 		for (i = 0; i < lh->numdescs; i++) {
 		for (i = 0; i < lh->numdescs; i++) {
 			val = gpiod_get_value_cansleep(lh->descs[i]);
 			val = gpiod_get_value_cansleep(lh->descs[i]);
@@ -444,6 +453,17 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip)
 		u32 lflags = handlereq.flags;
 		u32 lflags = handlereq.flags;
 		struct gpio_desc *desc;
 		struct gpio_desc *desc;
 
 
+		if (offset >= gdev->ngpio) {
+			ret = -EINVAL;
+			goto out_free_descs;
+		}
+
+		/* Return an error if a unknown flag is set */
+		if (lflags & ~GPIOHANDLE_REQUEST_VALID_FLAGS) {
+			ret = -EINVAL;
+			goto out_free_descs;
+		}
+
 		desc = &gdev->descs[offset];
 		desc = &gdev->descs[offset];
 		ret = gpiod_request(desc, lh->label);
 		ret = gpiod_request(desc, lh->label);
 		if (ret)
 		if (ret)
@@ -536,6 +556,10 @@ struct lineevent_state {
 	struct mutex read_lock;
 	struct mutex read_lock;
 };
 };
 
 
+#define GPIOEVENT_REQUEST_VALID_FLAGS \
+	(GPIOEVENT_REQUEST_RISING_EDGE | \
+	GPIOEVENT_REQUEST_FALLING_EDGE)
+
 static unsigned int lineevent_poll(struct file *filep,
 static unsigned int lineevent_poll(struct file *filep,
 				   struct poll_table_struct *wait)
 				   struct poll_table_struct *wait)
 {
 {
@@ -623,6 +647,8 @@ static long lineevent_ioctl(struct file *filep, unsigned int cmd,
 	if (cmd == GPIOHANDLE_GET_LINE_VALUES_IOCTL) {
 	if (cmd == GPIOHANDLE_GET_LINE_VALUES_IOCTL) {
 		int val;
 		int val;
 
 
+		memset(&ghd, 0, sizeof(ghd));
+
 		val = gpiod_get_value_cansleep(le->desc);
 		val = gpiod_get_value_cansleep(le->desc);
 		if (val < 0)
 		if (val < 0)
 			return val;
 			return val;
@@ -726,6 +752,18 @@ static int lineevent_create(struct gpio_device *gdev, void __user *ip)
 	lflags = eventreq.handleflags;
 	lflags = eventreq.handleflags;
 	eflags = eventreq.eventflags;
 	eflags = eventreq.eventflags;
 
 
+	if (offset >= gdev->ngpio) {
+		ret = -EINVAL;
+		goto out_free_label;
+	}
+
+	/* Return an error if a unknown flag is set */
+	if ((lflags & ~GPIOHANDLE_REQUEST_VALID_FLAGS) ||
+	    (eflags & ~GPIOEVENT_REQUEST_VALID_FLAGS)) {
+		ret = -EINVAL;
+		goto out_free_label;
+	}
+
 	/* This is just wrong: we don't look for events on output lines */
 	/* This is just wrong: we don't look for events on output lines */
 	if (lflags & GPIOHANDLE_REQUEST_OUTPUT) {
 	if (lflags & GPIOHANDLE_REQUEST_OUTPUT) {
 		ret = -EINVAL;
 		ret = -EINVAL;
@@ -823,6 +861,8 @@ static long gpio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 	if (cmd == GPIO_GET_CHIPINFO_IOCTL) {
 	if (cmd == GPIO_GET_CHIPINFO_IOCTL) {
 		struct gpiochip_info chipinfo;
 		struct gpiochip_info chipinfo;
 
 
+		memset(&chipinfo, 0, sizeof(chipinfo));
+
 		strncpy(chipinfo.name, dev_name(&gdev->dev),
 		strncpy(chipinfo.name, dev_name(&gdev->dev),
 			sizeof(chipinfo.name));
 			sizeof(chipinfo.name));
 		chipinfo.name[sizeof(chipinfo.name)-1] = '\0';
 		chipinfo.name[sizeof(chipinfo.name)-1] = '\0';
@@ -839,7 +879,7 @@ static long gpio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 
 
 		if (copy_from_user(&lineinfo, ip, sizeof(lineinfo)))
 		if (copy_from_user(&lineinfo, ip, sizeof(lineinfo)))
 			return -EFAULT;
 			return -EFAULT;
-		if (lineinfo.line_offset > gdev->ngpio)
+		if (lineinfo.line_offset >= gdev->ngpio)
 			return -EINVAL;
 			return -EINVAL;
 
 
 		desc = &gdev->descs[lineinfo.line_offset];
 		desc = &gdev->descs[lineinfo.line_offset];