lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Message-ID: <AANLkTim3eMR+5VQT9Cbew70bLZ_3P7eo3FXx_TxMuP1=@mail.gmail.com>
Date:	Wed, 30 Mar 2011 07:07:32 -0300
From:	Luis Filipe Rossi <luisf.rossi@...il.com>
To:	linux-kernel@...r.kernel.org
Subject: Help with dma on PCI driver

I tryied to post this on using google groups, but as i had some
filtering problem on other lists, i am sending using the direct
e-mail. Please answer direct to me as i am not subscribed (i am trying
to use a newsreader). Sorry if i am missing any information. If so,
please just tell me what so i can provide it.

I am developing a driver for a custom PCI board. I am following the
example on Essential Linux Device Drivers, but when i am calling the
pci_alloc_consitent i get the following error:
Mar 29 15:18:42 luis-desktop kernel: [ 4520.075823] BUG: unable to
handle kernel NULL pointer dereference at 00000004
Mar 29 15:18:42 luis-desktop kernel: [ 4520.078277] IP: [<c010797f>]
dma_generic_alloc_coherent+0xaf/0xc0
Mar 29 15:18:42 luis-desktop kernel: [ 4520.078277] *pde = 0a417067
*pte = 00000000
Mar 29 15:18:42 luis-desktop kernel: [ 4520.078277] Oops: 0002 [#1] SMP

I am running on a x86 (Pentium III) with Ubuntu Kernel 2.6.32-24
I tryed to take a look at ldd3 but no answer so far...

The idea is to alloc a buffer so my PCI card can master the bus and
place the data on that buffer....

The driver code is:

//*

 * pci_bridge-driver.c - template Linux driver for the opencores' pci
bridge . Works on * kernel 2.6.x

 *

 * tested on Xubuntu, kernel 2.6.20-15-generic

 *

 *

 * Permission to use, copy, modify, and distribute this software for any

 * purpose with or without fee is hereby granted.

 *

 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR

 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,

 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY

 * RIGHTS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES

 * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,

 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR

 * OTHER DEALINGS IN THE SOFTWARE.

 */



/*

 * Build/use notes (you'll probably need to be superuser to run most of

 * the steps below):

 *

 * 1) How to build the driver:

 *

 *      $ make

 *

 * 2) How to install the driver:

 *

 *      $ insmod pci_bridge-driver.ko

 *

 * TODO: change the code and use misc device and udev, so to avoid the
stuff below (3-4) !!

 * 3) If pci_bridge_init_major (below) is 0, then obtain the major number:

 *

 *      $ cat /proc/devices

 *

 * Look for the line that contains the string "pci_bridge". The major

 * number is to the left.

 *

 * 4) Make the pci_bridge device special file. Substitute the major

 * number obtained above for <major_number>.

 *

 *      $ mknod /dev/pci_bridge c <major_number> 0

 *

 * 5) How to remove the driver:

 *

 *      $ rmmod pci_bridge-driver

 */



#include <linux/kernel.h>

#include <linux/module.h>

#include <linux/pci.h>

#include <linux/init.h>

#include <linux/cdev.h>

#include <linux/fs.h>

#include <linux/ioctl.h>



//#include <asm/byteorder.h>              /* PCI is little endian */

#include <asm/uaccess.h>                /* copy to/from user */



#include "kint.h"



#define PCI_BRIDGE_DEVICE_ID 0x0001

#define PCI_BRIDGE_VENDOR_ID 0x1895

#define PCI_DRIVER_NAME "pci_bridge" /* driver name */

#define BRIDGE_MEM_MAPPED 0

#define BRIDGE_IO_MAPPED 1



/*

 * PCI device IDs supported by this driver. The PCI_DEVICE macro sets

 * the vendor and device fields to its input arguments, sets subvendor

 * and subdevice to PCI_ANY_ID. It does not set the class fields.

 */

static struct pci_device_id pci_bridge_ids[] =

{

	{ PCI_DEVICE(PCI_BRIDGE_VENDOR_ID, PCI_BRIDGE_DEVICE_ID), },

	{ 0, }

};



/*

 * For completeness, tell the module loading and hotplug systems

 * what PCI devices this module supports.

 */

MODULE_DEVICE_TABLE(pci, pci_bridge_ids);



/*

 * pci_register_driver parameter.

 */

static int pci_bridge_probe(struct pci_dev *, const struct pci_device_id *);

static void pci_bridge_remove(struct pci_dev *);



static struct pci_driver pci_bridge_driver =

{

	.name = PCI_DRIVER_NAME,

	.id_table = pci_bridge_ids,

	.probe = pci_bridge_probe,

	.remove = pci_bridge_remove,

};



/*

 * File operations i/f.

 */

int pci_bridge_open(struct inode *, struct file *);

int pci_bridge_release(struct inode *, struct file *);

ssize_t pci_bridge_read(struct file *, char __user *, size_t, loff_t *);

ssize_t pci_bridge_write(struct file *, const char __user *, size_t, loff_t *);

int pci_bridge_ioctl(struct inode *pnode, struct file *filp, unsigned
int cmd, unsigned long arg);

// seek file operation function

loff_t  bridge_lseek(struct file *filp, loff_t offset, int origin);



static void dma_descriptor_release(struct pci_dev *pdev);

static void dma_descriptor_setup(struct pci_dev *pdev);





static struct file_operations pci_bridge_fops =

{

	read: pci_bridge_read,

	write: pci_bridge_write,

	open: pci_bridge_open,

	release: pci_bridge_release,

	ioctl: pci_bridge_ioctl,

	llseek: bridge_lseek

};





static int __init pci_bridge_init(void);

static void __exit pci_bridge_exit(void);



/*

 * Driver major number. 0 = allocate dynamically.

 */

static int pci_bridge_init_major = 0;

static int pci_bridge_major;



/*

 * Per-device structure.

 */

static struct pci_bridge_dev

{

	struct cdev cdev;               /* Char device structure        */

	struct pci_dev *pcidev;         /* PCI device pointer           */

	int current_resource;

	u32 page_addr;

	u8 num_of_bases;

	int base_map[6];

	u32 bases[6];	

	u32 base_size[6];

	u32 base_page_offset;

	u32 offset;

	u8  interrupt_line;

} *pci_bridge_devices;



static struct device_data

{

	void *dma_buffer_rx;

	dma_addr_t dma_bus_rx;

	void *dma_buffer_tx;

	dma_addr_t dma_bus_tx;

}*c_memory_map;



/*

 * pci_bridge_probe - pci_driver probe function. Just enable the PCI device.

 * Could also check various configuration registers, find a specific PCI

 * device, request a specific region, etc.

 */

static int pci_bridge_probe(struct pci_dev *pcidev, const struct
pci_device_id *id)

{

	struct pci_bridge_dev *dev;

	printk("pci_bridge_probe called ...\n");

	if(pcidev == NULL)

	{

        	printk(KERN_NOTICE "pci_bridge_probe: PCI DEV is NULL\n");

        	return -EINVAL;

	}



	dev = pci_bridge_devices; // only one device for now

	if(dev == NULL)

		printk("pci_bridge_probe: device structure not allocated\n");

	else

	{

		pci_enable_device(pcidev);

		pci_set_master(pcidev);

		dev->pcidev = pcidev;

	}



	return 0;

}



/*

 * pci_bridge_remove - pci_driver remove function. Release allocated resources,

 * etc.

 */

static void __devexit pci_bridge_remove(struct pci_dev *dev)

{

    printk("pci_bridge_remove called ...\n");

}



/*

 * pci_bridge_init - module init function. By convention, the function is

 * declared static, even though it is not exported to the rest of the

 * kernel unless explicitly requested via the EXPORT_SYMBOL macro. The

 * __init qualifier tells the loader that the function is only used at

 * module initialization time.

 */

static int __init pci_bridge_init(void)

{

	struct pci_bridge_dev *dev;

	dev_t devno;

	int result;

	unsigned short num_of_bases;

	u32 base_address;

	printk("pci_bridge_init called ...\n");

	

	/*

	* Allocate the per-device structure(s).

	*/

	pci_bridge_devices = kmalloc(sizeof(struct pci_bridge_dev), GFP_KERNEL);

	if(pci_bridge_devices == NULL)

	{

		result = -ENOMEM;

		goto fail;

	}



	/*

	* Get a range of minor numbers to work with, asking for a dynamic

	* major unless directed otherwise at load time.

	*/

	if(pci_bridge_init_major)

	{

		pci_bridge_major = pci_bridge_init_major;

		devno = MKDEV(pci_bridge_major, 0);

		result = register_chrdev_region(devno, 1, PCI_DRIVER_NAME);

	}

	else

	{

		result = alloc_chrdev_region(&devno, 0, 1, PCI_DRIVER_NAME);

		pci_bridge_major = MAJOR(devno);

	}

	if(result < 0)

	{

		printk(KERN_NOTICE "pci_bridge: can't get major %d\n", pci_bridge_major);

		goto fail;

	}



	dev = pci_bridge_devices;/* only one device for now */



	/*

	* Initialize and add this device's character device table entry.

	*/

	dev->pcidev = NULL;

	cdev_init(&dev->cdev, &pci_bridge_fops);

	dev->cdev.owner = THIS_MODULE;

	dev->cdev.ops = &pci_bridge_fops;

	dev->offset = 0;

	result = cdev_add(&dev->cdev, devno, 1);

	if(result)

	{

		printk(KERN_NOTICE "Error %d adding %s device", result, PCI_DRIVER_NAME);

		goto fail;

	}

	

	if((result = pci_register_driver(&pci_bridge_driver)) != 0)

	{

		printk(KERN_NOTICE "Error %d registering %s PCI device",result,
PCI_DRIVER_NAME);

		goto fail;

	}

	

	if(dev->pcidev == NULL)

	{

		printk(KERN_NOTICE "PCI DEV is NULL, probe failed?\n");

		goto fail;

	}



	base_address = pci_resource_start(dev->pcidev, 0);



	printk("<1> First base address register found at %08X \n ",
pci_resource_start(dev->pcidev, 0));

	num_of_bases = 0;



	while

	((base_address = pci_resource_start(dev->pcidev, num_of_bases))!=
0x00000000 && (num_of_bases < 6))

	{

		unsigned long flags;

		flags = pci_resource_flags(dev->pcidev, num_of_bases);

		dev->bases[num_of_bases] = base_address;

		dev->base_size[num_of_bases] = pci_resource_end(dev->pcidev,
num_of_bases) - base_address + 1;

		// check if resource is IO mapped

		if (flags & IORESOURCE_IO)

		    dev->base_map[num_of_bases] = BRIDGE_IO_MAPPED;

		else

		    dev->base_map[num_of_bases] = BRIDGE_MEM_MAPPED;	

		num_of_bases++;

	}



	if (num_of_bases < 1)

		printk("<1>No implemented base address registers found! \n ");



	dev->current_resource = - 1;



	// store number of bases in structure

	dev->num_of_bases = num_of_bases;

	printk("num_of_bases found %d \n", num_of_bases);

	// display information about all base addresses found in this procedure

	for (num_of_bases = 0; num_of_bases < dev->num_of_bases; num_of_bases++)

	{

		printk("<1>BAR%d range from %08X to %08X \n ", num_of_bases,
dev->bases[num_of_bases], dev->bases[num_of_bases] +
dev->base_size[num_of_bases]);

	}

	

	if(pci_read_config_byte(dev->pcidev,PCI_INTERRUPT_LINE, &dev->interrupt_line))

	{

		printk("Could not get interrupt line");

		}

	else

	{

		printk("Interrupt Line is %d \n", dev->interrupt_line);

		}

	dma_descriptor_setup(dev->pcidev);

	



	return 0;

	

fail:

		pci_bridge_exit();

		return result;

}



/*

 * pci_bridge_exit - module exit function. Release resources allocated

 * by pci_bridge_init.

 */

static void __exit pci_bridge_exit(void)

{

	printk("pci_bridge_exit called ...\n");

	

	if(pci_bridge_devices)

	{

		struct pci_bridge_dev *dev;

		dev = &pci_bridge_devices[0];

		dma_descriptor_release(dev->pcidev);

		cdev_del(&dev->cdev);

		kfree(pci_bridge_devices);

		pci_bridge_devices = NULL;

	}

	unregister_chrdev_region(MKDEV(pci_bridge_major, 0), 1);

	pci_bridge_major = 0;

	pci_unregister_driver(&pci_bridge_driver);

}



/*

 * pci_bridge_open - open file processing.

 */

int pci_bridge_open(struct inode *inode, struct file *filep)

{

	struct pci_bridge_dev *dev;

	dev = container_of(inode->i_cdev, struct pci_bridge_dev, cdev);

	filep->private_data = dev; // used by read, write, etc

	dev->current_resource = -1;

	/* Success */

	return 0;

}



/*

 * pci_bridge_release - close processing.

 */

int pci_bridge_release(struct inode *inode, struct file *filep)

{

	/* Success */

	return 0;

}



/*

 * seek file operation function

 */

loff_t  bridge_lseek(struct file *filp, loff_t offset, int origin)

{

	struct pci_bridge_dev *dev;

	loff_t requested_offset;

	int resource_num;



	dev = filp->private_data;

	resource_num = dev->current_resource;

	switch (origin)

	{

		case SEEK_CUR:requested_offset = dev->offset + offset; break;

		case SEEK_END:requested_offset = dev->base_size[resource_num] + offset; break;

		default:requested_offset  = offset; break;

	}

	

	if ((requested_offset < 0) || (requested_offset >
dev->base_size[resource_num]))

		return -EFAULT;



	dev->offset = requested_offset;

	return requested_offset; 				

}





/*

 * pci_bridge_read - read processing.

 */

ssize_t pci_bridge_read (struct file *filp, char *buf, size_t count,
loff_t *offset_out )

{

	struct pci_bridge_dev *dev;		

	unsigned long current_address;

	unsigned long actual_count;

	unsigned long offset;

	int resource_num;

	int i;

	unsigned int value;

        unsigned int *kern_buf;

        unsigned int *kern_buf_tmp;

	unsigned long size;

	int result;



	dev = filp->private_data;

	offset = dev->offset;

	resource_num = dev->current_resource;

	size   = dev->base_size[resource_num];

	current_address = dev->page_addr + dev->base_page_offset + dev->offset;



	if (dev->current_resource < 0)

		return -ENODEV;

	

	if (offset == size)

		return 0;



	if ( (offset + count) > size )

		actual_count = size - offset;

	else

		actual_count = count;



	// verify range if it is OK to copy from

	if ((result = access_ok(VERIFY_WRITE, buf, actual_count)) ==0)

		return result;



	kern_buf = kmalloc(actual_count, GFP_KERNEL | GFP_DMA);

	kern_buf_tmp = kern_buf;

	if (kern_buf <= 0)

		return 0;



    	memcpy_fromio(kern_buf, current_address, actual_count);

	i = actual_count/4;

	while(i--)

	{

//		value = readl(current_address);	

		value = *(kern_buf);

		put_user(value, ((unsigned int *)buf));	

		buf += 4;

		++kern_buf;

//		current_address += 4;

	}



	kfree(kern_buf_tmp);

	dev->offset = dev->offset + actual_count;

	*(offset_out) = dev->offset;



	return actual_count;

 }	



/*

 * pci_bridge_write - write processing.

 */

ssize_t pci_bridge_write (struct file *filp, const char *buf, size_t
count, loff_t *offset_out)

{

	struct pci_bridge_dev *dev;

	unsigned long current_address;

	unsigned long actual_count;

	unsigned long offset;

	int resource_num;

 	int i;

	int value;

	unsigned long size;

	int result;

	int *kern_buf;

	int *kern_buf_tmp;

	

	

    	dev = filp->private_data;

	current_address = dev->page_addr + dev->base_page_offset + dev->offset;

	resource_num = dev->current_resource;

	size  = dev->base_size[resource_num];

	offset = dev->offset;



	if (dev->current_resource < 0)

		return -ENODEV;



	if (offset == size)

		return 0;



	if ( (offset + count) > size )

		actual_count = size - offset;

	else

		actual_count = count;



	// verify range if it is OK to copy from

	if ((result = access_ok(VERIFY_READ, buf, actual_count)) == 0)

		return result;

	kern_buf = kmalloc(actual_count, GFP_KERNEL | GFP_DMA);

	kern_buf_tmp = kern_buf;

	if (kern_buf <= 0)

		return 0;

	i = actual_count/4;

	while(i--)

	{

		get_user(value, ((int *)buf));

//		writel(value, current_address);

		*kern_buf = value;

		buf += 4;

		//current_address += 4;

		++kern_buf;

	}



	memcpy_toio(current_address, kern_buf_tmp, actual_count);

	kfree(kern_buf_tmp);

	dev->offset = dev->offset + actual_count;

	*(offset_out) = dev->offset;



	return actual_count;

}



/*

 * helper function for memory remaping

 */

int open_mem_mapped(struct pci_bridge_dev *dev)

{

	int resource_num = dev->current_resource;

	unsigned long num_of_pages = 0;

	unsigned long base = dev->bases[resource_num];

	unsigned long size = dev->base_size[resource_num];

	//printk("\n current resource=%d , size = %d, base=%08X",
dev->current_resource , size, dev->bases[resource_num]);

	if (!(num_of_pages = (unsigned long)(size/PAGE_SIZE)));

		num_of_pages++;



	dev->base_page_offset = base & ~PAGE_MASK;



	if ((dev->base_page_offset + size) < (num_of_pages*PAGE_SIZE))

		num_of_pages++;

	

	// remap memory mapped space

	dev->page_addr = (unsigned long)ioremap(base & PAGE_MASK,
num_of_pages * PAGE_SIZE);

	

	if (dev->page_addr == 0x00000000)

		return -ENOMEM;

	

	return 0;

}



/*

 * ioctl: see kint.h for the meaning of args

 */

int pci_bridge_ioctl(struct inode *pnode, struct file *filp, unsigned
int cmd, unsigned long arg)

{



	int error = 0;

	unsigned long base;

	unsigned long base_size;

	struct pci_bridge_dev *dev;

	dev = filp->private_data;



	if (_IOC_TYPE(cmd) != BRIDGE_IOC_NUM) return -EINVAL;

	if (_IOC_NR(cmd) > BRIDGE_IOC_MAX_NUM) return -EINVAL;



	switch (cmd)

	{

		case BRIDGE_IOC_CURRESGET:

			// current resource - they start at 1

			return (dev->current_resource + 1);

		

		case BRIDGE_IOC_CURRESSET:

			// check if resource is in a range of implemented resources

			if (arg < 0 )

				return -EINVAL;



			// unmap previous resource if it was mapped

			if (dev->current_resource >= 0)

			{

				iounmap((void *)dev->page_addr);

			}	



			if (arg == 0)

			{

				// previous resource unmaped - that's all

				dev->current_resource = -1;

				return 0;	

			}



			if (dev->num_of_bases < arg)

				return -ENODEV;



			// IO mapped not supported yet

			if (dev->base_map[arg-1] == BRIDGE_IO_MAPPED)

			{

				// set current resource to none, since it was unmapped

				dev->current_resource = -1;

				return -ENODEV;

			}

			dev->current_resource= (int)(arg-1);

			// remap new resource

			if ( (error = open_mem_mapped(dev)) )

			{

				dev->current_resource = -1;

				return error;

			}

			return 0;



		case BRIDGE_IOC_CURBASE:

			// check if any resource is currently activated

			if (dev->current_resource>=0)

			{

				base = dev->bases[dev->current_resource];

				printk("\n CURR_RES = %d",dev->current_resource );

			}

			else

				base = 0x00000000;

			

			*(unsigned long *)arg = base;

			return 0;



		case BRIDGE_IOC_CURBASEMAP:

			// check if any resource is currently activated

			if (dev->current_resource>=0)

				base = dev->page_addr;

			else

				base = 0x00000000;

	

			*(unsigned long *)arg = base;



			return 0;



		case BRIDGE_IOC_CURBASESIZE:	

			// check if any resource is currently activated

			if (dev->current_resource>=0)

				base_size = dev->base_size[dev->current_resource];

			else

				base_size = 0x00000000;

			

			*(unsigned long *)arg = base_size;

			return 0;



		case BRIDGE_IOC_NUMOFRES:	

			return (dev->num_of_bases);



		default:

			return -EINVAL;

		

	}



}

#define TX_BUFFER_FLAGS	 	0x00

#define RX_BUFFER_FLAGS 	0x00

#define TX_DATA_LEN		0x04

#define RX_DATA_LEN		0x04	

#define TX_BUFFER_OFFSET 	0x0c

#define RX_BUFFER_OFFSET 	0x0c

#define TX_BUFFER_SIZE 		4096

#define RX_BUFFER_SIZE		4096

#define WB_RX_BUFFER_AM		0xFF000000

#define WB_TX_BUFFER_AM		0xFF000000



static void dma_descriptor_setup(struct pci_dev *pdev)

{	

	unsigned long current_address;

	unsigned int  register_value;

	int error = 0;

	unsigned int c_tx_data;

	printk("DMA ALLOC ROUTINE STARTED\n");

	c_memory_map->dma_buffer_rx = pci_alloc_consistent(pdev,
RX_BUFFER_SIZE+RX_BUFFER_OFFSET, &c_memory_map->dma_bus_rx);

	printk("RX DMA BUFFER READY\n");

	c_memory_map->dma_buffer_tx = pci_alloc_consistent(pdev,
TX_BUFFER_SIZE+TX_BUFFER_OFFSET, &c_memory_map->dma_bus_tx);

	printk("TX DMA BUFFER READY\n");

	pci_bridge_devices->current_resource = 0;

	if ( (error = open_mem_mapped(pci_bridge_devices)) )

	{

		pci_bridge_devices->current_resource = -1;

		printk("Could not set BAR0 on DMA alloc\n");

		return;

	}

	printk("PCI ADDRESS FOR TX IS %d AND PCI ADDRESS FOR RX IS %d\n",
c_memory_map->dma_bus_rx, c_memory_map->dma_bus_tx);

	current_address = pci_bridge_devices->page_addr +
pci_bridge_devices->base_page_offset + BRIDGE_W_AM1_ADDR;

	register_value = WB_RX_BUFFER_AM;

	memcpy_toio(current_address, &register_value, 4);

	current_address = pci_bridge_devices->page_addr +
pci_bridge_devices->base_page_offset + BRIDGE_W_AM2_ADDR;

	register_value = WB_TX_BUFFER_AM;

	memcpy_toio(current_address, &register_value, 4);	

}



static void dma_descriptor_release(struct pci_dev *pdev)

{

	pci_free_consistent(pdev, RX_BUFFER_SIZE+RX_BUFFER_OFFSET,
c_memory_map->dma_buffer_rx, c_memory_map->dma_bus_rx);

	pci_free_consistent(pdev, TX_BUFFER_SIZE+TX_BUFFER_OFFSET,
c_memory_map->dma_buffer_tx, c_memory_map->dma_bus_tx);

}



MODULE_LICENSE("GPL");



module_init(pci_bridge_init);

module_exit(pci_bridge_exit);



Any help would be apreciated..
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ