Commit 6e4797ba authored by Ton Damen's avatar Ton Damen
Browse files

- Extended driver with extra bar (4)

- Preserve access beyond region bounderies in read/write calls
- Included README file
parent 7b7f9efd
......@@ -2,6 +2,7 @@
*.o
*.ko
*.mod.c
*.mod
nbproject/
.tmp_versions/
Module.symvers
......
KERNEL MODULE spec7
-------------------
This kernel module supports the access of the PCI Axi4 memory of the SPEC7 card.
Currently the IP of the SPEC7 presents two bars: bar0 (1MB) and bar4 (16MB).
When the kernel module (spec7.ko) is loaded, the following should be seen among the kernel messages:
spec7:probe_one: PCI device id: [10ee:7022], address: 0000:01:00.0
spec7:probe_one: size bar0 = 1048576, bar4 = 16777216
spec7:spec7_module_init: loaded
The PCI address is here "0000:01:00.0" which gives the following sysfs entries:
- /sys/bus/pci/devices/0000:01:00.0/deadbe
Writing either '0' or '1' will write this values into the SYSCON register of the LM32.
- /sys/bus/pci/devices/0000:01:00.0/lm32_mem
Gives file based access to the LM32 memory.
BAR interfaces
--------------
The module creates to file entries in /dev:
/dev/spec7_bar0
/dev/spec7_bar4
These character devices supports access to the memory regions of each bar in a stream-like fashion through systems calls read() and write(). The region boundaries are protected by returning EOF when reading and EFAULT (invalid address) when writing beyond.
Both cp(3) and dd(3) can be used to transfer files to and from the bar memory regions.
The system call ioctl() can be used to query the size of the bar regions:
ioctl(fd, 0xdc01, 0)
returns the size in bytes.
Both devices implements also the mmap(2) system call, which enables the mapping of the BAR regions to user-space memory.
PERMISSIONS
------------
The default permissions for the device files are granted for root only. To customize this, an udev rules file must be created in /usr/lib/udev/rules.d.
This example gives users who are member of group “spec7” read-write access and others only read access:
file 60-spec7.rules contents:
KERNEL=="spec7_*", MODE="0664", GROUP="spec7"
To reload the udev config:
# udevadm control --reload
To add the group “spec7”
# addgroup spec7
To add a groups membership to an existing user the following command is given:
# usermod -a -G spec7 username
Nb. reload the driver to effectuate the new configuration.
Ton Damen, tond@nilhef.nl
September 2021
/*
* Copyright (C) 2020 Ton Damen <tond@nikhef.nl>
* Copyright (C) 2021 NIKHEF Amsterdam
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
......@@ -31,32 +31,36 @@
#define VMEM_FLAGS (VM_IO | VM_DONTEXPAND | VM_DONTDUMP)
static int miscdev_open(struct inode *inode, struct file *file)
static int miscdev_open(struct inode *inode, struct file *filep)
{
struct miscdevice *mdev_ptr = file->private_data;
file->private_data = container_of(mdev_ptr, struct spec7_dev, mdev);
return 0;
struct miscdevice *mdev_ptr = filep->private_data;
filep->private_data = container_of(mdev_ptr, struct spec7_bar, mdev);
return 0;
}
/*
* character device file operations for control bus (through control bridge)
*/
static ssize_t miscdev_read(struct file *file, char __user *buf, size_t count,
static ssize_t miscdev_read(struct file *filep, char __user *buf, size_t count,
loff_t *pos)
{
void *reg;
void *addr;
u32 w;
int rv;
struct spec7_dev *spec = file->private_data;
struct spec7_bar *bar = filep->private_data;
/* only 32-bit aligned and 32-bit multiples */
if (*pos & 3)
return -EPROTO;
/* first address is BAR base plus file position offset */
reg = spec->bar_base + *pos;
addr = bar->base + *pos;
if (addr >= (bar->base + bar->size))
return 0;
//w = read_register(reg);
w = ioread32(reg);
w = ioread32(addr);
rv = copy_to_user(buf, &w, 4);
if (rv)
......@@ -66,47 +70,50 @@ static ssize_t miscdev_read(struct file *file, char __user *buf, size_t count,
return 4;
}
static ssize_t miscdev_write(struct file *file, const char __user *buf,
static ssize_t miscdev_write(struct file *filep, const char __user *buf,
size_t count, loff_t *pos)
{
void *reg;
void *addr;
u32 w;
int rv;
struct spec7_dev *spec = file->private_data;
struct spec7_bar *bar = filep->private_data;
/* only 32-bit aligned and 32-bit multiples */
if (*pos & 3)
return -EPROTO;
/* first address is BAR base plus file position offset */
reg = spec->bar_base + *pos;
addr = bar->base + *pos;
if (addr >= (bar->base + bar->size))
return -EFAULT;
rv = copy_from_user(&w, buf, 4);
if (rv) {
pr_info("copy from user failed %d/4, but continuing.\n", rv);
}
iowrite32(w, reg);
iowrite32(w, addr);
*pos += 4;
return 4;
}
/* maps the PCIe BAR into user space for memory-like access using mmap() */
int miscdev_mmap(struct file *file, struct vm_area_struct *vma)
static int miscdev_mmap(struct file *filep, struct vm_area_struct *vma)
{
unsigned long offs, vsize, psize, phys;
struct spec7_dev *spec = file->private_data;
struct spec7_bar *bar = filep->private_data;
offs = vma->vm_pgoff << PAGE_SHIFT;
phys = pci_resource_start(spec->pdev, BAR_IDX) + offs;
// physical address
phys = bar->phys + offs;
vsize = vma->vm_end - vma->vm_start;
/* complete resource */
psize = pci_resource_end(spec->pdev, BAR_IDX) -
pci_resource_start(spec->pdev, BAR_IDX) + 1 - offs;
psize = bar->size - offs;
if (vsize > psize)
return -EINVAL;
......@@ -128,27 +135,37 @@ int miscdev_mmap(struct file *file, struct vm_area_struct *vma)
return 0;
}
#if 0
static ssize_t miscdev_write(struct file *f, const char __user *buf,
size_t count, loff_t *offp)
static long miscdev_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
{
struct spec7_dev *spec = f->private_data;
return count;
struct spec7_bar *bar = filep->private_data;
switch(cmd){
case SPEC7_IOCQSIZE:
return bar->size;
default:
return -ENOTTY;
}
return 0;
}
#endif
static const struct file_operations spec7_fops = {
.owner = THIS_MODULE,
.open = miscdev_open,
.read = miscdev_read,
.write = miscdev_write,
.unlocked_ioctl = miscdev_ioctl,
.mmap = miscdev_mmap
};
int miscdev_init(struct miscdevice *mdev)
int miscdev_init(const char *name, struct miscdevice *mdev)
{
int rc;
mdev->minor = MISC_DYNAMIC_MINOR;
mdev->fops = &spec7_fops;
mdev->name = "spec7";
return misc_register(mdev);
mdev->name = name;
rc = misc_register(mdev);
if (rc == 0)
return mdev->minor;
return rc;
}
\ No newline at end of file
/*
* Copyright (C) 2020 Ton Damen <tond@nikhef.nl>
* Copyright (C) 2021 NIKHEF Amsterdam
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
......@@ -26,7 +26,7 @@
#ifndef MISC_DEV_H
#define MISC_DEV_H
int miscdev_init(struct miscdevice *mdev);
int miscdev_init(const char *, struct miscdevice *mdev);
#endif /* MISC_DEV_H */
......
/*
* Copyright (C) 2021 NIKHEF Amsterdam
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* 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, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
/*
* File: spec7.h
......@@ -12,26 +29,38 @@
#include <linux/pci.h>
#include <linux/miscdevice.h>
#include <linux/ioctl.h>
#define BAR_0 0
#define BAR_4 4
// If 'PCIe to AXI Lite Master' is enabled in the IP, this will be available via BAR0
#define BAR_IDX 0
#define BAR0_FNAME "spec7_bar0"
#define BAR4_FNAME "spec7_bar4"
#define PCI_VENDOR_ID_CERN 0x10dc
#define PCI_VENDOR_ID_XILINX 0x10ee
#define LM32_MEM_SIZE (128*1024)
#define SYSCON_REG_ADDR 0x20400
#define LM32_MEM_SIZE (128*1024)
#define SYSCON_REG_ADDR 0x20400
#define SPEC7_IOC_MAGIC 0xdc
#define SPEC7_IOCQSIZE _IO(SPEC7_IOC_MAGIC, 1)
struct spec7_bar {
int idx, inode_minor;
void *__iomem base;
struct miscdevice mdev;
ulong size, phys;
};
struct spec7_dev {
struct pci_dev *pdev;
void *__iomem bar_base; // mapped address
struct spec7_bar bar0, bar4;
u32 *lm32_mem;
u32 *syscon_reg_addr;
struct miscdevice mdev;
};
int populate_sysfs(struct spec7_dev *dev);
void cleanup_sysfs(struct spec7_dev *dev);
#endif /* SPEC7_H */
/*
* Copyright (C) 2021 NIKHEF Amsterdam
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* 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, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
/*
* File: spec7_mod.c
* Author: Ton Damen <tond@nikhef.nl>
*
* Created on April 23, 2020, 13:14 AM
* Modified on September 14, 2021, Added BAR4
*/
#define DEBUG
......@@ -14,16 +33,17 @@
#include <linux/kernel.h>
#include <linux/pci.h>
#include "spec7.h"
#include "sysfs.h"
#include "misc_dev.h"
#define MODULE_NAME "spec7"
#define MODULE_DESC "SPEC7 experimental Driver"
#define MODULE_RELDATE "April 2020"
#define MODULE_RELDATE "September 2021"
MODULE_AUTHOR("<Ton Damen>tond@nikhef.nl");
MODULE_DESCRIPTION(MODULE_DESC);
MODULE_VERSION("0.1");
MODULE_VERSION("0.2");
MODULE_LICENSE("GPL");
//pci_device_id=0x18d pci_vendor_id=0x10dc
......@@ -36,37 +56,56 @@ static const struct pci_device_id pci_ids[] = {
MODULE_DEVICE_TABLE(pci, pci_ids);
static int probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
{
static int probe_one(struct pci_dev *pdev, const struct pci_device_id *id) {
struct spec7_dev *spec;
int rc;
pr_devel("PCI device id: [%x:%x], address: %s\n", pdev->vendor, pdev->device, dev_name(&pdev->dev));
//pr_info("Buid %s %s\n", __DATE__, __TIME__);
pr_info("PCI device id: [%x:%x], address: %s\n", pdev->vendor, pdev->device, dev_name(&pdev->dev));
if (pci_enable_device(pdev) < 0)
return -EINVAL;
if ((spec = kzalloc(sizeof (struct spec7_dev), GFP_KERNEL)) == NULL)
return -ENOMEM;
spec->pdev = pdev;
spec->bar_base = pci_iomap(pdev, BAR_IDX, pci_resource_len(pdev, BAR_IDX));
spec->lm32_mem = spec->bar_base;
spec->syscon_reg_addr = spec->bar_base + SYSCON_REG_ADDR;
spec->bar0.size = pci_resource_len(pdev, BAR_0);
spec->bar4.size = pci_resource_len(pdev, BAR_4);
spec->bar0.phys = pci_resource_start(pdev, BAR_0);
spec->bar4.phys = pci_resource_start(pdev, BAR_4);
spec->bar0.base = pci_iomap(pdev, BAR_0, spec->bar0.size);
spec->bar4.base = pci_iomap(pdev, BAR_4, spec->bar4.size);
spec->bar0.idx = BAR_0;
spec->bar4.idx = BAR_4;
spec->lm32_mem = spec->bar0.base;
spec->syscon_reg_addr = spec->bar0.base + SYSCON_REG_ADDR;
pci_set_drvdata(pdev, spec);
populate_sysfs(spec);
miscdev_init(&spec->mdev);
rc = miscdev_init(BAR0_FNAME, &spec->bar0.mdev);
if (rc > 0)
spec->bar0.inode_minor = rc;
else
return rc;
rc = miscdev_init(BAR4_FNAME, &spec->bar4.mdev);
if (rc > 0)
spec->bar4.inode_minor = rc;
else
return rc;
pr_info("size bar0 = %lu, bar4 = %lu\n", spec->bar0.size, spec->bar4.size);
return 0;
}
static void remove_one(struct pci_dev *pdev)
{
static void remove_one(struct pci_dev *pdev) {
struct spec7_dev *spec = pci_get_drvdata(pdev);
misc_deregister(&spec->mdev);
misc_deregister(&spec->bar0.mdev);
misc_deregister(&spec->bar4.mdev);
cleanup_sysfs(spec);
pci_set_drvdata(pdev, NULL);
kfree(spec);
......@@ -79,8 +118,7 @@ static struct pci_driver pci_driver = {
.remove = remove_one
};
static int __init spec7_module_init(void)
{
static int __init spec7_module_init(void) {
int rc = pci_register_driver(&pci_driver);
if (rc == 0)
pr_info("loaded\n"); // (compiled at %s %s)\n", __DATE__, __TIME__);
......@@ -89,8 +127,7 @@ static int __init spec7_module_init(void)
return rc;
}
static void __exit spec7_module_exit(void)
{
static void __exit spec7_module_exit(void) {
pci_unregister_driver(&pci_driver);
pr_info("removed\n");
}
......
/*
* Copyright (C) 2021 NIKHEF Amsterdam
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* 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, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ":%s: " fmt, __func__
#include <linux/device.h>
......@@ -38,7 +56,7 @@ ssize_t lm32_mem_read(struct file *filep, struct kobject *kobj, struct bin_attri
u32 *src_addr = spec->lm32_mem + (pos >> 2);
unsigned n;
pr_info("pos = %llu, size = %lu\n", pos, size);
pr_debug("pos = %llu, size = %lu\n", pos, size);
/* only 32-bit aligned and 32-bit multiples */
if (pos & 3)
......@@ -60,7 +78,7 @@ ssize_t lm32_mem_write(struct file *filep, struct kobject *kobj, struct bin_attr
u32 *dest_addr = spec->lm32_mem + (pos >> 2);
unsigned n;
pr_info("pos = %llu, size = %lu\n", pos, size);
pr_debug("pos = %llu, size = %lu\n", pos, size);
/* only 32-bit aligned and 32-bit multiples */
if (pos & 3)
......
/*
* Copyright (C) 2021 Ton damen, tond@nikhef.nl
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* 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, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
/*
* File: sysfs.h
* Author: Ton Damen, tond@nikhef.nl
*
* Created on 15 september 2021, 10:11
*/
#ifndef SYSFS_H
#define SYSFS_H
int populate_sysfs(struct spec7_dev *dev);
void cleanup_sysfs(struct spec7_dev *dev);
#endif /* SYSFS_H */
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment