linux_dsm_epyc7002/drivers/net/can/sja1000/sja1000_isa.c
David Howells e43f2c52a0 Annotate hardware config module parameters in drivers/net/can/
When the kernel is running in secure boot mode, we lock down the kernel to
prevent userspace from modifying the running kernel image.  Whilst this
includes prohibiting access to things like /dev/mem, it must also prevent
access by means of configuring driver modules in such a way as to cause a
device to access or modify the kernel image.

To this end, annotate module_param* statements that refer to hardware
configuration and indicate for future reference what type of parameter they
specify.  The parameter parser in the core sees this information and can
skip such parameters with an error message if the kernel is locked down.
The module initialisation then runs as normal, but just sees whatever the
default values for those parameters is.

Note that we do still need to do the module initialisation because some
drivers have viable defaults set in case parameters aren't specified and
some drivers support automatic configuration (e.g. PNP or PCI) in addition
to manually coded parameters.

This patch annotates drivers in drivers/net/can/.

Suggested-by: Alan Cox <gnomes@lxorguk.ukuu.org.uk>
Signed-off-by: David Howells <dhowells@redhat.com>
Acked-by: Marc Kleine-Budde <mkl@pengutronix.de>
cc: Wolfgang Grandegger <wg@grandegger.com>
cc: linux-can@vger.kernel.org
cc: netdev@vger.kernel.org
2017-04-20 12:02:32 +01:00

324 lines
8.2 KiB
C

/*
* Copyright (C) 2009 Wolfgang Grandegger <wg@grandegger.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the version 2 of the GNU General Public License
* as published by the Free Software Foundation
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/netdevice.h>
#include <linux/delay.h>
#include <linux/irq.h>
#include <linux/io.h>
#include <linux/can/dev.h>
#include <linux/can/platform/sja1000.h>
#include "sja1000.h"
#define DRV_NAME "sja1000_isa"
#define MAXDEV 8
MODULE_AUTHOR("Wolfgang Grandegger <wg@grandegger.com>");
MODULE_DESCRIPTION("Socket-CAN driver for SJA1000 on the ISA bus");
MODULE_LICENSE("GPL v2");
#define CLK_DEFAULT 16000000 /* 16 MHz */
#define CDR_DEFAULT (CDR_CBP | CDR_CLK_OFF)
#define OCR_DEFAULT OCR_TX0_PUSHPULL
static unsigned long port[MAXDEV];
static unsigned long mem[MAXDEV];
static int irq[MAXDEV];
static int clk[MAXDEV];
static unsigned char cdr[MAXDEV] = {[0 ... (MAXDEV - 1)] = 0xff};
static unsigned char ocr[MAXDEV] = {[0 ... (MAXDEV - 1)] = 0xff};
static int indirect[MAXDEV] = {[0 ... (MAXDEV - 1)] = -1};
static spinlock_t indirect_lock[MAXDEV]; /* lock for indirect access mode */
module_param_hw_array(port, ulong, ioport, NULL, S_IRUGO);
MODULE_PARM_DESC(port, "I/O port number");
module_param_hw_array(mem, ulong, iomem, NULL, S_IRUGO);
MODULE_PARM_DESC(mem, "I/O memory address");
module_param_hw_array(indirect, int, ioport, NULL, S_IRUGO);
MODULE_PARM_DESC(indirect, "Indirect access via address and data port");
module_param_hw_array(irq, int, irq, NULL, S_IRUGO);
MODULE_PARM_DESC(irq, "IRQ number");
module_param_array(clk, int, NULL, S_IRUGO);
MODULE_PARM_DESC(clk, "External oscillator clock frequency "
"(default=16000000 [16 MHz])");
module_param_array(cdr, byte, NULL, S_IRUGO);
MODULE_PARM_DESC(cdr, "Clock divider register "
"(default=0x48 [CDR_CBP | CDR_CLK_OFF])");
module_param_array(ocr, byte, NULL, S_IRUGO);
MODULE_PARM_DESC(ocr, "Output control register "
"(default=0x18 [OCR_TX0_PUSHPULL])");
#define SJA1000_IOSIZE 0x20
#define SJA1000_IOSIZE_INDIRECT 0x02
static struct platform_device *sja1000_isa_devs[MAXDEV];
static u8 sja1000_isa_mem_read_reg(const struct sja1000_priv *priv, int reg)
{
return readb(priv->reg_base + reg);
}
static void sja1000_isa_mem_write_reg(const struct sja1000_priv *priv,
int reg, u8 val)
{
writeb(val, priv->reg_base + reg);
}
static u8 sja1000_isa_port_read_reg(const struct sja1000_priv *priv, int reg)
{
return inb((unsigned long)priv->reg_base + reg);
}
static void sja1000_isa_port_write_reg(const struct sja1000_priv *priv,
int reg, u8 val)
{
outb(val, (unsigned long)priv->reg_base + reg);
}
static u8 sja1000_isa_port_read_reg_indirect(const struct sja1000_priv *priv,
int reg)
{
unsigned long flags, base = (unsigned long)priv->reg_base;
u8 readval;
spin_lock_irqsave(&indirect_lock[priv->dev->dev_id], flags);
outb(reg, base);
readval = inb(base + 1);
spin_unlock_irqrestore(&indirect_lock[priv->dev->dev_id], flags);
return readval;
}
static void sja1000_isa_port_write_reg_indirect(const struct sja1000_priv *priv,
int reg, u8 val)
{
unsigned long flags, base = (unsigned long)priv->reg_base;
spin_lock_irqsave(&indirect_lock[priv->dev->dev_id], flags);
outb(reg, base);
outb(val, base + 1);
spin_unlock_irqrestore(&indirect_lock[priv->dev->dev_id], flags);
}
static int sja1000_isa_probe(struct platform_device *pdev)
{
struct net_device *dev;
struct sja1000_priv *priv;
void __iomem *base = NULL;
int iosize = SJA1000_IOSIZE;
int idx = pdev->id;
int err;
dev_dbg(&pdev->dev, "probing idx=%d: port=%#lx, mem=%#lx, irq=%d\n",
idx, port[idx], mem[idx], irq[idx]);
if (mem[idx]) {
if (!request_mem_region(mem[idx], iosize, DRV_NAME)) {
err = -EBUSY;
goto exit;
}
base = ioremap_nocache(mem[idx], iosize);
if (!base) {
err = -ENOMEM;
goto exit_release;
}
} else {
if (indirect[idx] > 0 ||
(indirect[idx] == -1 && indirect[0] > 0))
iosize = SJA1000_IOSIZE_INDIRECT;
if (!request_region(port[idx], iosize, DRV_NAME)) {
err = -EBUSY;
goto exit;
}
}
dev = alloc_sja1000dev(0);
if (!dev) {
err = -ENOMEM;
goto exit_unmap;
}
priv = netdev_priv(dev);
dev->irq = irq[idx];
priv->irq_flags = IRQF_SHARED;
if (mem[idx]) {
priv->reg_base = base;
dev->base_addr = mem[idx];
priv->read_reg = sja1000_isa_mem_read_reg;
priv->write_reg = sja1000_isa_mem_write_reg;
} else {
priv->reg_base = (void __iomem *)port[idx];
dev->base_addr = port[idx];
if (iosize == SJA1000_IOSIZE_INDIRECT) {
priv->read_reg = sja1000_isa_port_read_reg_indirect;
priv->write_reg = sja1000_isa_port_write_reg_indirect;
spin_lock_init(&indirect_lock[idx]);
} else {
priv->read_reg = sja1000_isa_port_read_reg;
priv->write_reg = sja1000_isa_port_write_reg;
}
}
if (clk[idx])
priv->can.clock.freq = clk[idx] / 2;
else if (clk[0])
priv->can.clock.freq = clk[0] / 2;
else
priv->can.clock.freq = CLK_DEFAULT / 2;
if (ocr[idx] != 0xff)
priv->ocr = ocr[idx];
else if (ocr[0] != 0xff)
priv->ocr = ocr[0];
else
priv->ocr = OCR_DEFAULT;
if (cdr[idx] != 0xff)
priv->cdr = cdr[idx];
else if (cdr[0] != 0xff)
priv->cdr = cdr[0];
else
priv->cdr = CDR_DEFAULT;
platform_set_drvdata(pdev, dev);
SET_NETDEV_DEV(dev, &pdev->dev);
dev->dev_id = idx;
err = register_sja1000dev(dev);
if (err) {
dev_err(&pdev->dev, "registering %s failed (err=%d)\n",
DRV_NAME, err);
goto exit_unmap;
}
dev_info(&pdev->dev, "%s device registered (reg_base=0x%p, irq=%d)\n",
DRV_NAME, priv->reg_base, dev->irq);
return 0;
exit_unmap:
if (mem[idx])
iounmap(base);
exit_release:
if (mem[idx])
release_mem_region(mem[idx], iosize);
else
release_region(port[idx], iosize);
exit:
return err;
}
static int sja1000_isa_remove(struct platform_device *pdev)
{
struct net_device *dev = platform_get_drvdata(pdev);
struct sja1000_priv *priv = netdev_priv(dev);
int idx = pdev->id;
unregister_sja1000dev(dev);
if (mem[idx]) {
iounmap(priv->reg_base);
release_mem_region(mem[idx], SJA1000_IOSIZE);
} else {
if (priv->read_reg == sja1000_isa_port_read_reg_indirect)
release_region(port[idx], SJA1000_IOSIZE_INDIRECT);
else
release_region(port[idx], SJA1000_IOSIZE);
}
free_sja1000dev(dev);
return 0;
}
static struct platform_driver sja1000_isa_driver = {
.probe = sja1000_isa_probe,
.remove = sja1000_isa_remove,
.driver = {
.name = DRV_NAME,
},
};
static int __init sja1000_isa_init(void)
{
int idx, err;
for (idx = 0; idx < MAXDEV; idx++) {
if ((port[idx] || mem[idx]) && irq[idx]) {
sja1000_isa_devs[idx] =
platform_device_alloc(DRV_NAME, idx);
if (!sja1000_isa_devs[idx]) {
err = -ENOMEM;
goto exit_free_devices;
}
err = platform_device_add(sja1000_isa_devs[idx]);
if (err) {
platform_device_put(sja1000_isa_devs[idx]);
goto exit_free_devices;
}
pr_debug("%s: platform device %d: port=%#lx, mem=%#lx, "
"irq=%d\n",
DRV_NAME, idx, port[idx], mem[idx], irq[idx]);
} else if (idx == 0 || port[idx] || mem[idx]) {
pr_err("%s: insufficient parameters supplied\n",
DRV_NAME);
err = -EINVAL;
goto exit_free_devices;
}
}
err = platform_driver_register(&sja1000_isa_driver);
if (err)
goto exit_free_devices;
pr_info("Legacy %s driver for max. %d devices registered\n",
DRV_NAME, MAXDEV);
return 0;
exit_free_devices:
while (--idx >= 0) {
if (sja1000_isa_devs[idx])
platform_device_unregister(sja1000_isa_devs[idx]);
}
return err;
}
static void __exit sja1000_isa_exit(void)
{
int idx;
platform_driver_unregister(&sja1000_isa_driver);
for (idx = 0; idx < MAXDEV; idx++) {
if (sja1000_isa_devs[idx])
platform_device_unregister(sja1000_isa_devs[idx]);
}
}
module_init(sja1000_isa_init);
module_exit(sja1000_isa_exit);