Validity of ioremap pointer within the kernel module

Eduard Fuchs lists.kel at gmail.com
Wed Aug 19 09:52:30 EDT 2020


Hello,

I am working on a driver for the McASP controller (on BeagleBone Black
device) that works outside the sound subsystem.
The following steps are performed correctly as far as I understand:
 * init() function of the module registers the "platform_driver"
 * Kernel finds the correct device in device-tree and performs the
probe() function
* in the probe() function, a "miscdevice" is created and the ressouce is
mapped with devm_platform_ioremap_resource_byname() (address and size
provided from dts).
* in the probe() function, access to the McASP registers works without
any problems.
* the probe() function is left with "return 0".

If at a later time, triggered by userspace, the open() function of
"miscdevice" is called, then a kernel fault occurs:

[ 1138.192489] DRV1: [drv_mdev_open():169]: misc device open called ...
[ 1138.192517] misc testdrv: [drv_mdev_open():170]: memory for driver
data found at 0xda9fca40
[ 1138.192526] misc testdrv: [drv_mdev_open():171]: virtual address for
McASP registers base mapped at 0xFA038000.
[ 1138.192532] DRV1: [mcasp_dump_registers():107]: Dump McASP register
information:
[ 1138.192538] 8<--- cut here ---
[ 1138.195612] Unhandled fault: external abort on non-linefetch (0x1028)
at 0xfa038000
[ 1138.203302] pgd = 13c9a0d5
[ 1138.206016] [fa038000] *pgd=48011452(bad)
[ 1138.210048] Internal error: : 1028 [#1] PREEMPT THUMB2
[ 1138.215209] Modules linked in: testdrv1(O) ti_eqep counter spidev
8021q garp stp mrp llc evdev usb_f_acm u_serial usb_f_ecm usb_f_rndis
u_ether libcomposite iptable_nat nf_nat nf_conntrack nf_defrag_ipv6
nf_defrag_ipv4 iptable_mangle iptable_filter ip_tables x_tables [last
unloaded: testdrv1]
[ 1138.241265] CPU: 0 PID: 827 Comm: tesdrv_test Tainted: G           O
    5.7.6-tmc02 #1
[ 1138.249386] Hardware name: Generic AM33XX (Flattened Device Tree)
[ 1138.255518] PC is at mcasp_dump_registers+0x1a/0x338 [testdrv1]
[ 1138.261463] LR is at mcasp_dump_registers+0x1b/0x338 [testdrv1]
[ 1138.267404] pc : [<bf8a5086>]    lr : [<bf8a5087>]    psr: 60070033
[ 1138.273694] sp : dc133d60  ip : 00000000  fp : dc133e68
[ 1138.278936] r10: bf8a608c  r9 : c0fb4c20  r8 : daee6030
[ 1138.284179] r7 : daeba000  r6 : 0000003d  r5 : bf8a610c  r4 : fa038000
[ 1138.290731] r3 : c0f05288  r2 : 00000000  r1 : 40070093  r0 : 00000044
[ 1138.297286] Flags: nZCv  IRQs on  FIQs on  Mode SVC_32  ISA Thumb
Segment none
[ 1138.304622] Control: 50c5387d  Table: 98af4019  DAC: 00000051
[ 1138.310390] Process tesdrv_test (pid: 827, stack limit = 0xcf218d31)
[ 1138.316769] Stack: (0xdc133d60 to 0xdc134000)
[ 1138.321145] 3d60: c0f05288 da9fca44 bf8a610c 0000003d daeba000
bf8a553b fa038000 da9fca44
[ 1138.329359] 3d80: c0fb4c2c bf8a54e5 c0fb4c2c c0645d41 c0a7869c
daee6030 dc351b80 daeba000
[ 1138.337572] 3da0: 00000000 dc0f3f00 dc351bac c0274d35 0000003d
c0f05288 daeba000 daee6030
[ 1138.345786] 3dc0: 00000000 c0274ca9 daeba008 daeba000 dc133f18
c026d975 da2685d8 00000000
[ 1138.353999] 3de0: 00000000 00000002 00000000 00000000 daeba000
c027c815 00000002 dfdc34b4
[ 1138.362212] 3e00: dab8c800 00000000 00000000 c010ef31 8813a18f
dc132000 8813a18f d8a35c00
[ 1138.370426] 3e20: 63c00000 00000041 8813a18f 00000002 daee6030
c0110e1f dc133edc c0f05288
[ 1138.378639] 3e40: 8813a18f daafc000 dc133f18 00000001 00000000
dc133f50 ffffff9c 00000005
[ 1138.386852] 3e60: 00000000 c027df4b db2279d0 da2685d8 7271e2e6
00000007 dc14a015 c0260183
[ 1138.395066] 3e80: 00000000 dbf154c8 daee6030 00000101 00000002
00000550 0000094c 00000000
[ 1138.403279] 3ea0: 00000000 00000000 dc133eac c026cb15 00000040
c0289997 dc0e6500 00000ff0
[ 1138.411493] 3ec0: ffffe000 004fe6ec 00000ff0 c0f05288 00000000
00000003 00000100 dc14a000
[ 1138.419706] 3ee0: 00000000 00000002 ffffff9c 00000000 dc1441ed
00000000 00000000 c0f05288
[ 1138.427919] 3f00: dc14a000 00000003 00000000 c026e1a1 00000000
c0f05288 00000000 dc130000
[ 1138.436132] 3f20: 00000004 00000100 00000001 c0f05288 d8a0f03c
dc133f60 dc133f78 00000000
[ 1138.444346] 3f40: 00000000 00000000 00000000 c026f0bb 00000000
00000000 00000000 00000000
[ 1138.452559] 3f60: 00000000 00000000 00000000 00000000 00000000
00000000 00000000 00000000
[ 1138.460772] 3f80: 00000000 c0f05288 b6fdd000 bed70c38 00000000
00000000 00000005 c0100284
[ 1138.468986] 3fa0: dc132000 c0100061 bed70c38 00000000 004fe6ec
00000000 bed70d7c 004fe6ec
[ 1138.477199] 3fc0: bed70c38 00000000 00000000 00000005 00000000
00000000 0050f000 00000000
[ 1138.485412] 3fe0: 00000000 bed70c04 004fe66b b6f59936 00070030
004fe6ec 00000000 00000000
[ 1138.493653] [<bf8a5086>] (mcasp_dump_registers [testdrv1]) from
[<bf8a5087>] (mcasp_dump_registers+0x1b/0x338 [testdrv1])
[ 1138.504660] Code: f6cb 708a f0bc da0f (6822) 2100
[ 1138.509473] ---[ end trace bb0b2efe6c81b12a ]---

Does anyone have an idea that I forgot or do wrong.

Thanks,
Eduard Fuchs
-------------- next part --------------
/**
 * @file    testdrv.c
 * @brief   Test module for the initialisation of the McASP in
 *          interrupt mode.
 */

#ifdef DEBUG
#define pr_fmt(fmt)   "DRV1: [%s():%d]: " fmt,__func__,__LINE__
#define dev_fmt(fmt)  "[%s():%d]: " fmt,__func__,__LINE__
#else
#define pr_fmt(fmt)   "DRV1: " fmt
#define dev_fmt(fmt)  fmt
#endif

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/of_device.h>
#include <linux/fs.h>
#include <linux/io.h>
#include <linux/miscdevice.h>


/*
 * AM335x Register dfinition for McASP controller
 */
#define AM335x_MCASP_REV_REG       0x00  /* Revision Identification Register     */
#define AM335x_MCASP_PWRIDLE_REG   0x04  /* Power Idle SYSCONFIG Register        */
#define AM335x_MCASP_PFUNC_REG     0x10  /* Pin Function Register                */
#define AM335x_MCASP_PDIR_REG      0x14  /* Pin Direction Register               */
#define AM335x_MCASP_PDOUT_REG     0x18  /* Pin Data Output Register             */
#define AM335x_MCASP_PDIN_REG      0x1C  /* Pin Data Input Register              */
#define AM335x_MCASP_PDCLR_REG     0x20  /* Pin Data Clear Register              */
#define AM335x_MCASP_GBLCTL_REG    0x44  /* Global Control Register              */
#define AM335x_MCASP_AMUTE_REG     0x48  /* Audio Mute Control Register          */
#define AM335x_MCASP_DLBCTL_REG    0x4C  /* Digital Loopback Control Register    */
#define AM335x_MCASP_DITCTL_REG    0x50  /* DIT Mode Control Register            */
#define AM335x_MCASP_RGBLCTL_REG   0x60  /* Receiver Global Control Register     */
#define AM335x_MCASP_RMASK_REG     0x64  /* Receive Format Unit Bit Mask Register*/
#define AM335x_MCASP_RFMT_REG      0x68  /* Receive Bit Stream Format Register   */
#define AM335x_MCASP_AFSRCTL_REG   0x6C  /* Receive Frame Sync Control Register  */
#define AM335x_MCASP_ACLKRCTL_REG  0x70  /* Receive Clock Control Register       */
#define AM335x_MCASP_AHCLKRCTL_REG 0x74  /* Receive Hi.-Freq. Clock Control Reg. */
#define AM335x_MCASP_RTDM_REG      0x78  /* Receive TDM Time Slot 0-31 Register  */
#define AM335x_MCASP_RINTCTL_REG   0x7C  /* Receiver Interrupt Control Register  */
#define AM335x_MCASP_RSTAT_REG     0x80  /* Receiver Status Register             */
#define AM335x_MCASP_RSLOT_REG     0x84  /* Current Receive TDM Time Slot Reg.   */
#define AM335x_MCASP_RCLKCHK_REG   0x88  /* Receive Clock Check Control Register */
#define AM335x_MCASP_REVTCTL_REG   0x8C  /* Receiver DMA Event Control Register  */
#define AM335x_MCASP_XGBLCTL_REG   0xA0  /* Transmitter Global Control Register  */
#define AM335x_MCASP_XMASK_REG     0xA4  /* Transmit Format Unit Bit Mask Reg.   */
#define AM335x_MCASP_XFMT_REG      0xA8  /* Transmit Bit Stream Format Register  */
#define AM335x_MCASP_AFSXCTL_REG   0xAC  /* Transmit Frame Sync Control Register */
#define AM335x_MCASP_ACLKXCTL_REG  0xB0  /* Transmit Clock Control Register      */
#define AM335x_MCASP_AHCLKXCTL_REG 0xB4  /* Transmit Hi.-Freq. Clk. Control Reg. */
#define AM335x_MCASP_XTDM_REG      0xB8  /* Transmit TDM Time Slot 0-31 Register */
#define AM335x_MCASP_XINTCTL_REG   0xBC  /* Transmitter Interrupt Control Reg.   */
#define AM335x_MCASP_XSTAT_REG     0xC0  /* Transmitter Status Register          */
#define AM335x_MCASP_XSLOT_REG     0xC4  /* Current Transmit TDM Time Slot Reg.  */
#define AM335x_MCASP_XCLKCHK_REG   0xC8  /* Transmit Clock Check Control Reg.    */
#define AM335x_MCASP_XEVTCTL_REG   0xCC  /* Transmitter DMA Event Control Reg.   */

/*
100h to 114h | DITCSRA_0 to DITCSRA_5 | Left (Even TDM Time Slot) Channel Status Registers (DIT Mode)
118h to 12Ch | DITCSRB_0 to DITCSRB_5 | Right (Odd TDM Time Slot) Channel Status Registers (DIT Mode)
130h to 144h | DITUDRA_0 to DITUDRA_5 | Left (Even TDM Time Slot) Channel User Data Registers (DIT Mode)
148h to 15Ch | DITUDRB_0 to DITUDRB_5 | Right (Odd TDM Time Slot) Channel User Data Registers (DIT Mode)
*/

#define AM335x_MCASP_SRCTL_BASE    0x180 /* Serializer Control Registers 0..5    */
#define AM335x_MCASP_SRCTL_REG(n)  (AM335x_MCASP_SRCTL_BASE + (n << 2))

#define AM335x_MCASP_XBUF_BASE     0x200 /* Transmit Buffer Reg.for Serializers  */
#define AM335x_MCASP_XBUF_REG(n)   (AM335x_MCASP_XBUF_BASE + (n << 2))

#define AM335x_MCASP_RBUF_BASE     0x280 /* Receive Buffer Reg.for Serializers   */
#define AM335x_MCASP_RBUF_REG(n)   (AM335x_MCASP_RBUF_BASE + (n << 2))

#define AM335x_MCASP_WFIFOCTL_REG  0x1000 /* Write FIFO Control Register         */
#define AM335x_MCASP_WFIFOSTS_REG  0x1004 /* Write FIFO Status Register          */
#define AM335x_MCASP_RFIFOCTL_REG  0x1008 /* Read FIFO Control Register          */
#define AM335x_MCASP_RFIFOSTS_REG  0x100C /* Read FIFO Status Register           */


struct testdrv1
{
    void __iomem *mcasp_base;
    struct miscdevice mdev;
};

#define REG_DUMP(str, offs, dev)  printk(KERN_INFO "   [0x%04x] "str, offs, mcasp_get_reg(dev, offs))

static inline u32 mcasp_get_reg(void __iomem *base, u32 offset)
{
    return (u32)__raw_readl(base + offset);
}

void mcasp_dump_registers(void __iomem *base)
{
    int i;

    pr_info("Dump McASP register information:");
    REG_DUMP("REV              = 0x%08x\n", AM335x_MCASP_REV_REG, base);
    REG_DUMP("PWRIDLESYSCONFIG = 0x%08x\n", AM335x_MCASP_PWRIDLE_REG, base);
    REG_DUMP("PFUNC            = 0x%08x\n", AM335x_MCASP_PFUNC_REG, base);
    REG_DUMP("PDIR             = 0x%08x\n", AM335x_MCASP_PDIR_REG, base);
    REG_DUMP("PDOUT            = 0x%08x\n", AM335x_MCASP_PDOUT_REG, base);
    REG_DUMP("PDIN             = 0x%08x\n", AM335x_MCASP_PDIN_REG, base);
    REG_DUMP("PDCLR            = 0x%08x\n", AM335x_MCASP_PDCLR_REG, base);
    REG_DUMP("GBLCTL           = 0x%08x\n", AM335x_MCASP_GBLCTL_REG, base);
    REG_DUMP("AMUTE            = 0x%08x\n", AM335x_MCASP_AMUTE_REG, base);
    REG_DUMP("DLBCTL           = 0x%08x\n", AM335x_MCASP_DLBCTL_REG, base);
    REG_DUMP("DITCTL           = 0x%08x\n", AM335x_MCASP_DITCTL_REG, base);
    REG_DUMP("RGBLCTL          = 0x%08x\n", AM335x_MCASP_RGBLCTL_REG, base);
    REG_DUMP("RMASK            = 0x%08x\n", AM335x_MCASP_RMASK_REG, base);
    REG_DUMP("RFMT             = 0x%08x\n", AM335x_MCASP_RFMT_REG, base);
    REG_DUMP("AFSRCTL          = 0x%08x\n", AM335x_MCASP_AFSRCTL_REG, base);
    REG_DUMP("ACLKRCTL         = 0x%08x\n", AM335x_MCASP_ACLKRCTL_REG, base);
    REG_DUMP("AHCLKRCTL        = 0x%08x\n", AM335x_MCASP_AHCLKRCTL_REG, base);
    REG_DUMP("RTDM             = 0x%08x\n", AM335x_MCASP_RTDM_REG, base);
    REG_DUMP("RINTCTL          = 0x%08x\n", AM335x_MCASP_RINTCTL_REG, base);
    REG_DUMP("RSTAT            = 0x%08x\n", AM335x_MCASP_RSTAT_REG, base);
    REG_DUMP("RSLOT            = 0x%08x\n", AM335x_MCASP_RSLOT_REG, base);
    REG_DUMP("RCLKCHK          = 0x%08x\n", AM335x_MCASP_RCLKCHK_REG, base);
    REG_DUMP("REVTCTL          = 0x%08x\n", AM335x_MCASP_REVTCTL_REG, base);
    REG_DUMP("XGBLCTL          = 0x%08x\n", AM335x_MCASP_XGBLCTL_REG, base);
    REG_DUMP("XMASK            = 0x%08x\n", AM335x_MCASP_XMASK_REG, base);
    REG_DUMP("XFMT             = 0x%08x\n", AM335x_MCASP_XFMT_REG, base);
    REG_DUMP("AFSXCTL          = 0x%08x\n", AM335x_MCASP_AFSXCTL_REG, base);
    REG_DUMP("ACLKXCTL         = 0x%08x\n", AM335x_MCASP_ACLKXCTL_REG, base);
    REG_DUMP("AHCLKXCTL        = 0x%08x\n", AM335x_MCASP_AHCLKXCTL_REG, base);
    REG_DUMP("XTDM             = 0x%08x\n", AM335x_MCASP_XTDM_REG, base);
    REG_DUMP("XINTCTL          = 0x%08x\n", AM335x_MCASP_XINTCTL_REG, base);
    REG_DUMP("XSTAT            = 0x%08x\n", AM335x_MCASP_XSTAT_REG, base);
    REG_DUMP("XSLOT            = 0x%08x\n", AM335x_MCASP_XSLOT_REG, base);
    REG_DUMP("XCLKCHK          = 0x%08x\n", AM335x_MCASP_XCLKCHK_REG, base);
    REG_DUMP("XEVTCTL          = 0x%08x\n", AM335x_MCASP_XEVTCTL_REG, base);

    for(i = 0; i <= 5; i++) {
        printk(KERN_INFO "   [0x%04x] SRCTL_%d          = 0x%08x\n",
               AM335x_MCASP_SRCTL_REG(i), i,
               mcasp_get_reg(base, AM335x_MCASP_SRCTL_REG(i)));
    }
    for(i = 0; i <= 5; i++) {
        printk(KERN_INFO "   [0x%04x] XBUF_%d           = 0x%08x\n",
               AM335x_MCASP_XBUF_REG(i), i,
               mcasp_get_reg(base, AM335x_MCASP_XBUF_REG(i)));
    }
    for(i = 0; i <= 5; i++) {
        printk(KERN_INFO "   [0x%04x] RBUF_%d           = 0x%08x\n",
               AM335x_MCASP_RBUF_REG(i), i, mcasp_get_reg(base, AM335x_MCASP_RBUF_REG(i)));
    }

    REG_DUMP("WFIFOCTL         = 0x%08x\n", AM335x_MCASP_WFIFOCTL_REG, base);
    REG_DUMP("WFIFOSTS         = 0x%08x\n", AM335x_MCASP_WFIFOSTS_REG, base);
    REG_DUMP("RFIFOCTL         = 0x%08x\n", AM335x_MCASP_RFIFOCTL_REG, base);
    REG_DUMP("RFIFOSTS         = 0x%08x\n", AM335x_MCASP_RFIFOSTS_REG, base);
}


static int drv_mdev_open(struct inode* inode, struct file* file)
{
    struct testdrv1 *drv = container_of(file->private_data, struct testdrv1, mdev);
    pr_info("misc device open called ...\n");
    dev_info(drv->mdev.this_device, "memory for driver data found at 0x%08x\n", (unsigned int)drv);
    dev_info(drv->mdev.this_device, "virtual address for McASP registers base mapped at 0x%08X.\n", (unsigned int)drv->mcasp_base);
    mcasp_dump_registers(drv->mcasp_base);
    return 0;
}


static int drv_mdev_close(struct inode* inode, struct file* file)
{
    pr_info("misc device close called ...\n");
    return 0;
}


static long drv_mdev_ioctl(struct file* file, unsigned int cmd, unsigned long arg)
{
    pr_info("misc device ioctl called ...\n");
    return 0;
}


static const struct file_operations drv_ops = {
  .owner = THIS_MODULE,
  .open = drv_mdev_open,
  .release = drv_mdev_close,
  .unlocked_ioctl = drv_mdev_ioctl,
};


static int drv_probe(struct platform_device* pdev)
{
    int ret;
    struct testdrv1 *drv;

    pr_info("probe function called.\n");

    if(!pdev->dev.platform_data && !pdev->dev.of_node) {
        dev_err(&pdev->dev, "no platform data supplied\n");
        return -EINVAL;
    }
    dev_info(&pdev->dev, "platform data supplied\n");


    drv = devm_kzalloc(&pdev->dev, sizeof(struct testdrv1), GFP_KERNEL);
    if(!drv) {
        dev_err(&pdev->dev, "memory allocation failed\n");
        return -ENOMEM;
    }
    dev_info(&pdev->dev, "memory for driver allocated at 0x%08x\n", (unsigned int)drv);
    platform_set_drvdata(pdev, drv);

    /* init miscdevice */
    drv->mdev.minor = MISC_DYNAMIC_MINOR;
    drv->mdev.name = "testdrv";
    drv->mdev.fops = &drv_ops;
    ret = misc_register(&drv->mdev);
    if(ret) {
        dev_err(&pdev->dev, "create misc device failed\n");
        return -ENODEV;
    }
    dev_info(&pdev->dev, "misc device created\n");

    drv->mcasp_base = devm_platform_ioremap_resource_byname(pdev, "mpu");
    if(IS_ERR(drv->mcasp_base)) {
        dev_err(&pdev->dev, "get virtual address for McASP registers base failed.\n");
        return PTR_ERR(drv->mcasp_base);
    }
    dev_info(&pdev->dev, "virtual address for McASP registers base mapped at 0x%08X.\n", (unsigned int)drv->mcasp_base);
    mcasp_dump_registers(drv->mcasp_base);

    return 0;
}

static int drv_remove(struct platform_device* pdev)
{
    struct testdrv1 *drv = (struct testdrv1*)platform_get_drvdata(pdev);

    pr_info("remove function called.\n");
    misc_deregister(&drv->mdev);
    return 0;
}

/* list of devices supported by the driver */
static const struct of_device_id mcasp_ids[] = {
    {
        .compatible = "ti,am33xx-mcasp-audio",
        .data = NULL,
    },
    {}
};
MODULE_DEVICE_TABLE(of, mcasp_ids);

/* Platform driver information */
struct platform_driver test_driver = {
    .probe = drv_probe,
    .remove = drv_remove,
    .driver = {
        .name = "testdrv1",
        .of_match_table = mcasp_ids,
        .owner = THIS_MODULE
    }
};

/** The LKM initialization function.
 *
 * @return returns 0 if successful.
 */ 
static int testdrv1_init(void)
{
    pr_info("module init...\n");
    platform_driver_register(&test_driver);
    return 0;
}
module_init(testdrv1_init);

/** The LKM cleanup function.
 */
static void testdrv1_exit(void)
{
    pr_info("module release ...\n");
    platform_driver_unregister(&test_driver);
}
module_exit(testdrv1_exit);


MODULE_LICENSE("GPL");
MODULE_AUTHOR("Eduard Fuchs");
MODULE_DESCRIPTION("Testdrv1 for inspect McASP interface.");


More information about the Kernelnewbies mailing list