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: <1474319674-18172-1-git-send-email-mathieu.poirier@linaro.org>
Date:   Mon, 19 Sep 2016 15:14:34 -0600
From:   Mathieu Poirier <mathieu.poirier@...aro.org>
To:     linux-arm-kernel@...ts.infradead.org
Cc:     linux-kernel@...r.kernel.org, suzuki.poulose@....com,
        alexander.shishkin@...ux.intel.com
Subject: [PATCH] coresight: tmc: implementing TMC-ETR AUX space API

This patch implements the AUX area interfaces required to
use the TMC-ETR (configured to work in scatter-gather mode)
from the Perf sub-system.

Some of this work was inspired from the original implementation
done by Pratik Patel at CodeAurora.

Signed-off-by: Mathieu Poirier <mathieu.poirier@...aro.org>
---
 drivers/hwtracing/coresight/coresight-tmc-etr.c | 629 +++++++++++++++++++++++-
 drivers/hwtracing/coresight/coresight-tmc.h     |   1 +
 2 files changed, 621 insertions(+), 9 deletions(-)

diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c
index 6d7de0309e94..581d6393bb5d 100644
--- a/drivers/hwtracing/coresight/coresight-tmc-etr.c
+++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c
@@ -17,10 +17,60 @@
 
 #include <linux/coresight.h>
 #include <linux/dma-mapping.h>
+#include <linux/slab.h>
+
 #include "coresight-priv.h"
 #include "coresight-tmc.h"
 
-void tmc_etr_enable_hw(struct tmc_drvdata *drvdata)
+/**
+ * struct etr_page - DMA'able and virtual address representation for a page
+ * @daddr:		DMA'able page address returned by dma_map_page()
+ * @vaddr:		Virtual address returned by page_address()
+ */
+struct etr_page {
+	dma_addr_t	daddr;
+	u64		vaddr;
+};
+
+/**
+ * struct cs_etr_buffer - keep track of a recording session' specifics
+ * @dev:		device reference to be used with the DMA API
+ * @tmc:		generic portion of the TMC buffers
+ * @etr_nr_pages:	number of memory pages for the ETR-SG trace storage
+ * @pt_vaddr:		the virtual address of the first page table entry
+ * @page_addr:		quick access to all the pages held in the page table
+ */
+struct cs_etr_buffers {
+	struct device		*dev;
+	struct cs_buffers	tmc;
+	unsigned int		etr_nr_pages;
+	void __iomem		*pt_vaddr;
+	struct etr_page		page_addr[0];
+};
+
+#define TMC_ETR_ENTRIES_PER_PT (PAGE_SIZE / sizeof(u32))
+
+/*
+ * Helpers for scatter-gather descriptors.  Descriptors are defined as follow:
+ *
+ * ---Bit31------------Bit4-------Bit1-----Bit0--
+ * |     Address[39:12]    | SBZ |  Entry Type  |
+ * ----------------------------------------------
+ *
+ * Address: Bits [39:12] of a physical page address. Bits [11:0] are
+ *	    always zero.
+ *
+ * Entry type:	b10 - Normal entry
+ *		b11 - Last entry in a page table
+ *		b01 - Last entry
+ */
+#define TMC_ETR_SG_LST_ENT(phys_pte)	(((phys_pte >> PAGE_SHIFT) << 4) | 0x1)
+#define TMC_ETR_SG_ENT(phys_pte)	(((phys_pte >> PAGE_SHIFT) << 4) | 0x2)
+#define TMC_ETR_SG_NXT_TBL(phys_pte)	(((phys_pte >> PAGE_SHIFT) << 4) | 0x3)
+
+#define TMC_ETR_SG_ENT_TO_PG(entry)	((entry >> 4) << PAGE_SHIFT)
+
+void tmc_etr_enable_hw_cnt_mem(struct tmc_drvdata *drvdata)
 {
 	u32 axictl;
 
@@ -57,7 +107,47 @@ void tmc_etr_enable_hw(struct tmc_drvdata *drvdata)
 	CS_LOCK(drvdata->base);
 }
 
-static void tmc_etr_dump_hw(struct tmc_drvdata *drvdata)
+void tmc_etr_enable_hw_sg_mem(struct tmc_drvdata *drvdata)
+{
+	u32 axictl;
+
+	CS_UNLOCK(drvdata->base);
+
+	/* Wait for TMCSReady bit to be set */
+	tmc_wait_for_tmcready(drvdata);
+
+	writel_relaxed(TMC_MODE_CIRCULAR_BUFFER, drvdata->base + TMC_MODE);
+
+	axictl = readl_relaxed(drvdata->base + TMC_AXICTL);
+	/* half the write buffer depth */
+	axictl |= TMC_AXICTL_WR_BURST_08;
+	/* enable scatter-gather mode */
+	axictl |= TMC_AXICTL_SCT_GAT_MODE;
+	/* enable non-secure, priviledged access */
+	axictl |= (TMC_AXICTL_PROT_CTL_B0 | TMC_AXICTL_PROT_CTL_B1);
+
+	writel_relaxed(axictl, drvdata->base + TMC_AXICTL);
+
+	writel_relaxed(drvdata->paddr, drvdata->base + TMC_DBALO);
+
+	/*
+	 * DBAHI Holds the upper eight bits of the 40-bit address used to
+	 * locate the trace buffer in system memory.
+	 */
+	writel_relaxed((drvdata->paddr >> 32) & 0xFF,
+			drvdata->base + TMC_DBAHI);
+
+	writel_relaxed(TMC_FFCR_EN_FMT | TMC_FFCR_EN_TI |
+		       TMC_FFCR_FON_FLIN | TMC_FFCR_FON_TRIG_EVT |
+		       TMC_FFCR_TRIGON_TRIGIN,
+		       drvdata->base + TMC_FFCR);
+	writel_relaxed(drvdata->trigger_cntr, drvdata->base + TMC_TRG);
+	tmc_enable_hw(drvdata);
+
+	CS_LOCK(drvdata->base);
+}
+
+static void tmc_etr_dump_hw_cnt_mem(struct tmc_drvdata *drvdata)
 {
 	u32 rwp, val;
 
@@ -87,7 +177,8 @@ static void tmc_etr_disable_hw(struct tmc_drvdata *drvdata)
 	 * read before the TMC is disabled.
 	 */
 	if (local_read(&drvdata->mode) == CS_MODE_SYSFS)
-		tmc_etr_dump_hw(drvdata);
+		tmc_etr_dump_hw_cnt_mem(drvdata);
+
 	tmc_disable_hw(drvdata);
 
 	CS_LOCK(drvdata->base);
@@ -157,7 +248,7 @@ static int tmc_enable_etr_sink_sysfs(struct coresight_device *csdev, u32 mode)
 
 	memset(drvdata->vaddr, 0, drvdata->size);
 
-	tmc_etr_enable_hw(drvdata);
+	tmc_etr_enable_hw_cnt_mem(drvdata);
 out:
 	spin_unlock_irqrestore(&drvdata->spinlock, flags);
 
@@ -199,7 +290,7 @@ static int tmc_enable_etr_sink_perf(struct coresight_device *csdev, u32 mode)
 		goto out;
 	}
 
-	tmc_etr_enable_hw(drvdata);
+	tmc_etr_enable_hw_sg_mem(drvdata);
 out:
 	spin_unlock_irqrestore(&drvdata->spinlock, flags);
 
@@ -241,9 +332,528 @@ static void tmc_disable_etr_sink(struct coresight_device *csdev)
 	dev_info(drvdata->dev, "TMC-ETR disabled\n");
 }
 
+/*
+ * The default perf ring buffer size is 32 and 1024 pages for user and kernel
+ * space respectively.  The size of the intermediate SG list is allowed
+ * to match the size of the perf ring buffer but cap it to the default
+ * kernel size.
+ */
+#define DEFAULT_NR_KERNEL_PAGES	1024
+static int tmc_get_etr_pages(int nr_pages)
+{
+	if (nr_pages <= DEFAULT_NR_KERNEL_PAGES)
+		return nr_pages;
+
+	return DEFAULT_NR_KERNEL_PAGES;
+}
+
+/*
+ * Go through all the pages in the SG list and check if @phys_addr
+ * falls within one of those.  If so record the information in
+ * @page and @offset.
+ */
+static int
+tmc_get_sg_page_index(struct cs_etr_buffers *etr_buffer,
+		      u64 phys_addr, u32 *page, u32 *offset)
+{
+	int i = 0, pte = 0, nr_pages = etr_buffer->etr_nr_pages;
+	u32 *page_table_itr = etr_buffer->pt_vaddr;
+	phys_addr_t phys_page_addr;
+
+	/* Circle through all the pages in the SG list */
+	while (pte < nr_pages) {
+		phys_page_addr = TMC_ETR_SG_ENT_TO_PG((u64)*page_table_itr);
+
+		/* Does @phys_addr falls within this page? */
+		if (phys_addr >= phys_page_addr &&
+		    phys_addr < (phys_page_addr + PAGE_SIZE)) {
+			*page = pte;
+			*offset = phys_addr - phys_page_addr;
+			return 0;
+		}
+
+		if (pte == nr_pages - 1) {
+			/* The last page in the SG list */
+			pte++;
+		} else if (i == TMC_ETR_ENTRIES_PER_PT - 1) {
+			/*
+			 * The last entry in this page table - get a reference
+			 * on the next page table and do _not_ increment @pte
+			 */
+			page_table_itr = phys_to_virt(phys_page_addr);
+			i = 0;
+		} else {
+			/* A normal page in the SG list */
+			page_table_itr++;
+			pte++;
+			i++;
+		}
+	}
+
+	return -EINVAL;
+}
+
+static void tmc_sg_page_sync(struct cs_etr_buffers *etr_buffer,
+			     int start_page, u64 to_sync)
+{
+	int i, index;
+	int pages_to_sync = DIV_ROUND_UP_ULL(to_sync, PAGE_SIZE);
+	dma_addr_t daddr;
+	struct device *dev = etr_buffer->dev;
+
+	for (i = start_page; i < (start_page + pages_to_sync); i++) {
+		/* Wrap around the etr page list if need be */
+		index = i % etr_buffer->etr_nr_pages;
+		daddr = etr_buffer->page_addr[index].daddr;
+		dma_sync_single_for_cpu(dev, daddr, PAGE_SIZE, DMA_FROM_DEVICE);
+	}
+}
+
+static void tmc_free_sg_buffer(struct cs_etr_buffers *etr_buffer, int nr_pages)
+{
+	int i = 0, pte = 0;
+	u32 *page_addr, *page_table_itr;
+	u32 *page_table_addr = etr_buffer->pt_vaddr;
+	phys_addr_t phys_page_addr;
+	dma_addr_t daddr;
+	struct device *dev = etr_buffer->dev;
+
+	if (!page_table_addr)
+		return;
+
+	page_table_itr = page_table_addr;
+	while (pte < nr_pages) {
+		phys_page_addr = TMC_ETR_SG_ENT_TO_PG((u64)*page_table_itr);
+		page_addr = phys_to_virt(phys_page_addr);
+
+		if (pte == nr_pages - 1) {
+			/* The last page in the SG list */
+			daddr = etr_buffer->page_addr[pte].daddr;
+			page_addr = (u32 *)etr_buffer->page_addr[pte].vaddr;
+
+			dma_unmap_page(dev, daddr, PAGE_SIZE,
+				       DMA_FROM_DEVICE);
+
+			/* Free the current page */
+			free_page((unsigned long)page_addr);
+			/* Free the current page table */
+			free_page((unsigned long)page_table_addr);
+
+			pte++;
+		} else if (i == TMC_ETR_ENTRIES_PER_PT - 1) {
+			/* The last entry in this page table */
+			page_addr = phys_to_virt(phys_page_addr);
+
+			/* Free the current page table */
+			free_page((unsigned long)page_table_addr);
+			/* Move along to the next one */
+			page_table_addr = page_addr;
+			page_table_itr = page_table_addr;
+
+			i = 0;
+		} else {
+			/* A normal page in the SG list */
+			daddr = etr_buffer->page_addr[pte].daddr;
+			page_addr = (u32 *)etr_buffer->page_addr[pte].vaddr;
+
+			dma_unmap_page(dev, daddr, PAGE_SIZE,
+				       DMA_FROM_DEVICE);
+
+			/* Free the current page */
+			free_page((unsigned long)page_addr);
+
+			page_table_itr++;
+			pte++;
+			i++;
+		}
+	}
+}
+
+static dma_addr_t tmc_setup_dma_page(struct device *dev, struct page *page)
+{
+	dma_addr_t daddr;
+
+	/*
+	 * No data is communicated to the device, as such there is no point
+	 * in setting the direction to DMA_BIDIRECTIONAL.  See
+	 * Documentation/DMA-API-HOWTO.txt for details.
+	 */
+	daddr = dma_map_page(dev, page, 0, PAGE_SIZE, DMA_FROM_DEVICE);
+	if (dma_mapping_error(dev, daddr)) {
+		__free_page(page);
+		return -EINVAL;
+	}
+
+	return daddr;
+}
+
+static int
+tmc_alloc_sg_buffer(struct cs_etr_buffers *etr_buffer, int cpu, int nr_pages)
+{
+	int i = 0, node, pte = 0, ret = 0;
+	dma_addr_t dma_page_addr;
+	u32 *page_table_addr, *page_addr;
+	struct page *page;
+	struct device *dev = etr_buffer->dev;
+
+	if (cpu == -1)
+		cpu = smp_processor_id();
+	node = cpu_to_node(cpu);
+
+	/* Allocate the first page table */
+	page = alloc_pages_node(node, GFP_KERNEL | __GFP_ZERO, 0);
+	if (!page)
+		return -ENOMEM;
+
+	page_table_addr = page_address(page);
+	/*
+	 * Keep track of the first page table, the rest will be chained
+	 * in the last page table entry.
+	 */
+	etr_buffer->pt_vaddr = page_table_addr;
+
+	while (pte < nr_pages) {
+		page = alloc_pages_node(node,
+					GFP_KERNEL | __GFP_ZERO, 0);
+		if (!page) {
+			ret = -ENOMEM;
+			goto err;
+		}
+
+		page_addr = page_address(page);
+
+		if (pte == nr_pages - 1) {
+			/* The last page in the list */
+			dma_page_addr = tmc_setup_dma_page(dev, page);
+			if (dma_page_addr == -EINVAL) {
+				ret = -EINVAL;
+				goto err;
+			}
+
+			*page_table_addr = TMC_ETR_SG_LST_ENT(dma_page_addr);
+
+			etr_buffer->page_addr[pte].vaddr = (u64)page_addr;
+			etr_buffer->page_addr[pte].daddr = dma_page_addr;
+
+			pte++;
+		} else if (i == TMC_ETR_ENTRIES_PER_PT - 1) {
+			/* The last entry in this page table */
+			*page_table_addr =
+				TMC_ETR_SG_NXT_TBL(virt_to_phys(page_addr));
+			/* Move on to the next page table */
+			page_table_addr = page_addr;
+
+			i = 0;
+		} else {
+			/* A normal page in the SG list */
+			dma_page_addr = tmc_setup_dma_page(dev, page);
+			if (dma_page_addr == -EINVAL) {
+				ret = -EINVAL;
+				goto err;
+			}
+
+			*page_table_addr = TMC_ETR_SG_ENT(dma_page_addr);
+
+			etr_buffer->page_addr[pte].vaddr = (u64)page_addr;
+			etr_buffer->page_addr[pte].daddr = dma_page_addr;
+
+			page_table_addr++;
+			pte++;
+			i++;
+		}
+	}
+
+	return 0;
+
+err:
+	tmc_free_sg_buffer(etr_buffer, pte);
+	etr_buffer->pt_vaddr = NULL;
+	return ret;
+}
+
+static void *tmc_alloc_etr_buffer(struct coresight_device *csdev, int cpu,
+				  void **pages, int nr_pages, bool overwrite)
+{
+	int etr_pages, node;
+	struct device *dev = csdev->dev.parent;
+	struct cs_etr_buffers *buf;
+
+	if (cpu == -1)
+		cpu = smp_processor_id();
+	node = cpu_to_node(cpu);
+
+	/* Register DBALO and DBAHI form a 40-bit address range */
+	if (dma_set_mask(dev, DMA_BIT_MASK(40)))
+		return NULL;
+
+	/*
+	 * The HW can't start collecting data in the middle of the SG list,
+	 * it must start at the beginning.  As such we can't use the ring
+	 * buffer provided by perf as entries into the page tables since
+	 * it is not guaranteed that user space will have the chance to
+	 * consume the data before the next trace run begins.
+	 *
+	 * To work around this reserve a set of pages that will be used as
+	 * and intermediate (SG) buffer.  This isn't optimal but the best we
+	 * can do with the current HW revision.
+	 */
+	etr_pages = tmc_get_etr_pages(nr_pages);
+
+	/* Allocate memory structure for interaction with Perf */
+	buf = kzalloc_node(offsetof(struct cs_etr_buffers,
+			   page_addr[etr_pages]),
+			   GFP_KERNEL, node);
+	if (!buf)
+		return NULL;
+
+	buf->dev = dev;
+
+	if (tmc_alloc_sg_buffer(buf, cpu, etr_pages)) {
+		kfree(buf);
+		return NULL;
+	}
+
+	buf->etr_nr_pages = etr_pages;
+	buf->tmc.snapshot = overwrite;
+	buf->tmc.nr_pages = nr_pages;
+	buf->tmc.data_pages = pages;
+
+	return buf;
+}
+
+static void tmc_free_etr_buffer(void *config)
+{
+	struct cs_etr_buffers *buf = config;
+
+	tmc_free_sg_buffer(buf, buf->etr_nr_pages);
+	kfree(buf);
+}
+
+static int tmc_set_etr_buffer(struct coresight_device *csdev,
+			      struct perf_output_handle *handle,
+			      void *sink_config)
+{
+	unsigned long head;
+	struct cs_etr_buffers *buf = sink_config;
+	struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
+
+	/* wrap head around to the amount of space we have */
+	head = handle->head & ((buf->tmc.nr_pages << PAGE_SHIFT) - 1);
+
+	/* find the page to write to */
+	buf->tmc.cur = head / PAGE_SIZE;
+
+	/* and offset within that page */
+	buf->tmc.offset = head % PAGE_SIZE;
+
+	local_set(&buf->tmc.data_size, 0);
+
+	/* Keep track of how big the internal SG list is */
+	drvdata->size = buf->etr_nr_pages << PAGE_SHIFT;
+
+	/* Tell the HW where to put the trace data */
+	drvdata->paddr = virt_to_phys(buf->pt_vaddr);
+
+	return 0;
+}
+
+static unsigned long tmc_reset_etr_buffer(struct coresight_device *csdev,
+					  struct perf_output_handle *handle,
+					  void *sink_config, bool *lost)
+{
+	long size = 0;
+	struct cs_etr_buffers *buf = sink_config;
+	struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
+
+	if (buf) {
+		/*
+		 * In snapshot mode ->data_size holds the new address of the
+		 * ring buffer's head.  The size itself is the whole address
+		 * range since we want the latest information.
+		 */
+		if (buf->tmc.snapshot) {
+			size = buf->tmc.nr_pages << PAGE_SHIFT;
+			handle->head = local_xchg(&buf->tmc.data_size, size);
+		}
+
+		/*
+		 * Tell the tracer PMU how much we got in this run and if
+		 * something went wrong along the way.  Nobody else can use
+		 * this cs_etr_buffers instance until we are done.  As such
+		 * resetting parameters here and squaring off with the ring
+		 * buffer API in the tracer PMU is fine.
+		 */
+		*lost = !!local_xchg(&buf->tmc.lost, 0);
+		size = local_xchg(&buf->tmc.data_size, 0);
+	}
+
+	/* Get ready for another run */
+	drvdata->vaddr = NULL;
+	drvdata->paddr = 0;
+
+	return size;
+}
+
+static void tmc_update_etr_buffer(struct coresight_device *csdev,
+				  struct perf_output_handle *handle,
+				  void *sink_config)
+{
+	bool full;
+	int i, rb_index, sg_index = 0;
+	u32 rwplo, rwphi, rb_offset, sg_offset = 0;
+	u32 stop_index, stop_offset, to_copy, sg_size;
+	u32 *rb_ptr, *sg_ptr;
+	u64 rwp, to_read;
+	struct cs_etr_buffers *etr_buf = sink_config;
+	struct cs_buffers *cs_buf = &etr_buf->tmc;
+	struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
+
+	if (!etr_buf)
+		return;
+
+	/* This shouldn't happen */
+	if (WARN_ON_ONCE(local_read(&drvdata->mode) != CS_MODE_PERF))
+		return;
+
+	CS_UNLOCK(drvdata->base);
+
+	tmc_flush_and_stop(drvdata);
+
+	rwplo = readl_relaxed(drvdata->base + TMC_RWP);
+	rwphi = readl_relaxed(drvdata->base + TMC_RWPHI);
+	full = (readl_relaxed(drvdata->base + TMC_STS) & TMC_STS_FULL);
+
+	/* Combine the high and low part of the rwp to make a full address */
+	rwp = (u64)rwphi << 32;
+	rwp |= rwplo;
+
+	/* Convert the stop address in RAM to a page and an offset */
+	if (tmc_get_sg_page_index(etr_buf, rwp, &stop_index, &stop_offset))
+		goto out;
+
+	if (full) {
+		/*
+		 * The buffer head has wrapped around.  As such the size
+		 * is the entire buffer length and the index and offset in
+		 * the scatter-gather list are moved forward.
+		 */
+		local_inc(&cs_buf->lost);
+		to_read = drvdata->size;
+		sg_index = stop_index;
+		sg_offset = stop_offset;
+	} else {
+		to_read = (stop_index * PAGE_SIZE) + stop_offset;
+	}
+
+	/*
+	 * The TMC RAM buffer may be bigger than the space available in the
+	 * perf ring buffer (handle->size).  If so advance the RRP so that we
+	 * get the latest trace data.
+	 */
+	if (to_read > handle->size) {
+		u64 rrp;
+
+		/*
+		 * Compute where we should start reading from
+		 * relative to rwp.
+		 */
+		rrp = rwp + drvdata->size;
+		/* Go back just enough */
+		rrp -= handle->size;
+		/* Make sure we are still within our limits */
+		rrp %= drvdata->size;
+
+		/* Get a new index and offset based on rrp */
+		if (tmc_get_sg_page_index(etr_buf, rrp,
+					  &stop_index, &stop_offset))
+			goto out;
+
+		/* Tell user space we lost data */
+		local_inc(&cs_buf->lost);
+		to_read = handle->size;
+		/* Adjust start index and offset */
+		sg_index = stop_index;
+		sg_offset = stop_offset;
+	}
+
+	/* Get a handle on where the Perf ring buffer is */
+	rb_index = cs_buf->cur;
+	rb_offset = cs_buf->offset;
+
+	/* Refresh the SG list */
+	tmc_sg_page_sync(etr_buf, sg_index, to_read);
+
+	for (i = to_read; i > 0; ) {
+		/* Get current location of the perf ring buffer */
+		rb_ptr = cs_buf->data_pages[rb_index] + rb_offset;
+		/* Get current location in the ETR SG list */
+		sg_ptr = (u32 *)(etr_buf->page_addr[sg_index].vaddr +
+				 sg_offset);
+
+		/*
+		 * First figure out the maximum amount of data we can get out
+		 * of the ETR SG list.
+		 */
+		if (i < PAGE_SIZE)
+			sg_size = i;
+		else
+			sg_size = PAGE_SIZE - sg_offset;
+
+		/*
+		 * We have two page table buffer, one is the Perf ring
+		 * buffer while the other one is the internal ETR SG list.
+		 * Get the maximum amount of information we can copy from the
+		 * ETR SG list to the Perf ring buffer, which happens to be
+		 * the minimum space available in the current pages
+		 * (both of them).
+		 */
+		to_copy = min((u32)(PAGE_SIZE - rb_offset), sg_size);
+
+		/* Transfer trace data from ETR SG list to Perf ring buffer */
+		memcpy(rb_ptr, sg_ptr, to_copy);
+
+		rb_offset += to_copy;
+		sg_offset += to_copy;
+		i -= to_copy;
+
+		/* If a page is full, move to the next one */
+		if (rb_offset == PAGE_SIZE) {
+			rb_offset = 0;
+			rb_index++;
+			rb_index %= cs_buf->nr_pages;
+		}
+
+		if (sg_offset == PAGE_SIZE) {
+			sg_offset = 0;
+			sg_index++;
+			sg_index %= etr_buf->etr_nr_pages;
+		}
+	}
+
+	/*
+	 * In snapshot mode all we have to do is communicate to
+	 * perf_aux_output_end() the address of the current head.  In full
+	 * trace mode the same function expects a size to move rb->aux_head
+	 * forward.
+	 */
+	if (etr_buf->tmc.snapshot)
+		local_set(&etr_buf->tmc.data_size,
+			  stop_index * PAGE_SIZE + stop_offset);
+	else
+		local_add(to_read, &etr_buf->tmc.data_size);
+
+out:
+	CS_LOCK(drvdata->base);
+}
+
 static const struct coresight_ops_sink tmc_etr_sink_ops = {
 	.enable		= tmc_enable_etr_sink,
 	.disable	= tmc_disable_etr_sink,
+	.alloc_buffer	= tmc_alloc_etr_buffer,
+	.free_buffer	= tmc_free_etr_buffer,
+	.set_buffer	= tmc_set_etr_buffer,
+	.reset_buffer	= tmc_reset_etr_buffer,
+	.update_buffer	= tmc_update_etr_buffer,
 };
 
 const struct coresight_ops tmc_etr_cs_ops = {
@@ -306,11 +916,12 @@ int tmc_read_unprepare_etr(struct tmc_drvdata *drvdata)
 	if (local_read(&drvdata->mode) == CS_MODE_SYSFS) {
 		/*
 		 * The trace run will continue with the same allocated trace
-		 * buffer. The trace buffer is cleared in tmc_etr_enable_hw(),
-		 * so we don't have to explicitly clear it. Also, since the
-		 * tracer is still enabled drvdata::buf can't be NULL.
+		 * buffer. The trace buffer is cleared in
+		 * tmc_etr_enable_hw_cnt_mem(), so we don't have to explicitly
+		 * clear it. Also, since the tracer is still enabled
+		 * drvdata::buf can't be NULL.
 		 */
-		tmc_etr_enable_hw(drvdata);
+		tmc_etr_enable_hw_cnt_mem(drvdata);
 	} else {
 		/*
 		 * The ETR is not tracing and the buffer was just read.
diff --git a/drivers/hwtracing/coresight/coresight-tmc.h b/drivers/hwtracing/coresight/coresight-tmc.h
index 44b3ae346118..05dc00d79732 100644
--- a/drivers/hwtracing/coresight/coresight-tmc.h
+++ b/drivers/hwtracing/coresight/coresight-tmc.h
@@ -59,6 +59,7 @@
 #define TMC_AXICTL_PROT_CTL_B1	BIT(1)
 #define TMC_AXICTL_SCT_GAT_MODE	BIT(7)
 #define TMC_AXICTL_WR_BURST_16	0xF00
+#define TMC_AXICTL_WR_BURST_08	0x700
 /* TMC_FFCR - 0x304 */
 #define TMC_FFCR_FLUSHMAN_BIT	6
 #define TMC_FFCR_EN_FMT		BIT(0)
-- 
2.7.4

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ