From zaitcev@redhat.com Mon Aug 15 16:54:17 2005 Date: Mon, 15 Aug 2005 16:53:57 -0700 From: Pete Zaitcev To: greg@kroah.com Subject: usbmon in 2.6.13: peeking into DMA areas Message-Id: <20050815165357.322892c0.zaitcev@redhat.com> This code looks at urb->transfer_dma, maps the page and takes the data. I am looking for volunteers to contribute architectures other than i386 or to develop an architecure-neutral API for it (or point me that it was done already). Signed-off-by: Pete Zaitcev Signed-off-by: Greg Kroah-Hartman --- drivers/usb/mon/Makefile | 2 - drivers/usb/mon/mon_dma.c | 55 +++++++++++++++++++++++++++++++++++++++++++++ drivers/usb/mon/mon_text.c | 35 +++++++++++++--------------- drivers/usb/mon/usb_mon.h | 4 +++ 4 files changed, 77 insertions(+), 19 deletions(-) --- gregkh-2.6.orig/drivers/usb/mon/Makefile 2005-08-19 00:22:44.000000000 -0700 +++ gregkh-2.6/drivers/usb/mon/Makefile 2005-08-19 00:22:45.000000000 -0700 @@ -2,7 +2,7 @@ # Makefile for USB Core files and filesystem # -usbmon-objs := mon_main.o mon_stat.o mon_text.o +usbmon-objs := mon_main.o mon_stat.o mon_text.o mon_dma.o # This does not use CONFIG_USB_MON because we want this to use a tristate. obj-$(CONFIG_USB) += usbmon.o --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ gregkh-2.6/drivers/usb/mon/mon_dma.c 2005-08-19 00:22:45.000000000 -0700 @@ -0,0 +1,55 @@ +/* + * The USB Monitor, inspired by Dave Harding's USBMon. + * + * mon_dma.c: Library which snoops on DMA areas. + * + * Copyright (C) 2005 Pete Zaitcev (zaitcev@redhat.com) + */ +#include +#include +#include +#include + +#include /* Only needed for declarations in usb_mon.h */ +#include "usb_mon.h" + +#ifdef __i386__ /* CONFIG_ARCH_I386 does not exit */ +#define MON_HAS_UNMAP 1 + +#define phys_to_page(phys) pfn_to_page((phys) >> PAGE_SHIFT) + +char mon_dmapeek(unsigned char *dst, dma_addr_t dma_addr, int len) +{ + struct page *pg; + unsigned long flags; + unsigned char *map; + unsigned char *ptr; + + /* + * On i386, a DMA handle is the "physical" address of a page. + * In other words, the bus address is equal to physical address. + * There is no IOMMU. + */ + pg = phys_to_page(dma_addr); + + /* + * We are called from hardware IRQs in case of callbacks. + * But we can be called from softirq or process context in case + * of submissions. In such case, we need to protect KM_IRQ0. + */ + local_irq_save(flags); + map = kmap_atomic(pg, KM_IRQ0); + ptr = map + (dma_addr & (PAGE_SIZE-1)); + memcpy(dst, ptr, len); + kunmap_atomic(map, KM_IRQ0); + local_irq_restore(flags); + return 0; +} +#endif /* __i386__ */ + +#ifndef MON_HAS_UNMAP +char mon_dmapeek(unsigned char *dst, dma_addr_t dma_addr, int len) +{ + return 'D'; +} +#endif --- gregkh-2.6.orig/drivers/usb/mon/mon_text.c 2005-08-19 00:22:44.000000000 -0700 +++ gregkh-2.6/drivers/usb/mon/mon_text.c 2005-08-19 00:22:45.000000000 -0700 @@ -91,25 +91,11 @@ static inline char mon_text_get_data(str int len, char ev_type) { int pipe = urb->pipe; - unsigned char *data; - - /* - * The check to see if it's safe to poke at data has an enormous - * number of corner cases, but it seems that the following is - * more or less safe. - * - * We do not even try to look transfer_buffer, because it can - * contain non-NULL garbage in case the upper level promised to - * set DMA for the HCD. - */ - if (urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP) - return 'D'; if (len <= 0) return 'L'; - - if ((data = urb->transfer_buffer) == NULL) - return 'Z'; /* '0' would be not as pretty. */ + if (len >= DATA_MAX) + len = DATA_MAX; /* * Bulk is easy to shortcut reliably. @@ -126,8 +112,21 @@ static inline char mon_text_get_data(str } } - if (len >= DATA_MAX) - len = DATA_MAX; + /* + * The check to see if it's safe to poke at data has an enormous + * number of corner cases, but it seems that the following is + * more or less safe. + * + * We do not even try to look transfer_buffer, because it can + * contain non-NULL garbage in case the upper level promised to + * set DMA for the HCD. + */ + if (urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP) + return mon_dmapeek(ep->data, urb->transfer_dma, len); + + if (urb->transfer_buffer == NULL) + return 'Z'; /* '0' would be not as pretty. */ + memcpy(ep->data, urb->transfer_buffer, len); return 0; } --- gregkh-2.6.orig/drivers/usb/mon/usb_mon.h 2005-08-19 00:22:44.000000000 -0700 +++ gregkh-2.6/drivers/usb/mon/usb_mon.h 2005-08-19 00:22:45.000000000 -0700 @@ -45,6 +45,10 @@ struct mon_reader { void mon_reader_add(struct mon_bus *mbus, struct mon_reader *r); void mon_reader_del(struct mon_bus *mbus, struct mon_reader *r); +/* + */ +extern char mon_dmapeek(unsigned char *dst, dma_addr_t dma_addr, int len); + extern struct semaphore mon_lock; extern struct file_operations mon_fops_text;