* Introduce a new mode CS_MODE_READ_PREVBOOT for reading tracedata captured in previous boot.
* Add special handlers for preparing ETR/ETF for this special mode
* User can read the trace data as below
For example, for reading trace data from tmc_etf sink
1. cd /sys/bus/coresight/devices/tmc_etfXX/
2. Change mode to READ_PREVBOOT
#echo 1 > read_prevboot
3. Dump trace buffer data to a file,
#dd if=/dev/tmc_etrXX of=~/cstrace.bin
4. Reset back to normal mode
#echo 0 > read_prevboot
Signed-off-by: Anil Kumar Reddy areddy3@marvell.com Signed-off-by: Tanmay Jagdale tanmay@marvell.com Signed-off-by: Linu Cherian lcherian@marvell.com --- drivers/hwtracing/coresight/coresight-priv.h | 1 + .../hwtracing/coresight/coresight-tmc-core.c | 38 ++++- .../hwtracing/coresight/coresight-tmc-etf.c | 85 ++++++++++ .../hwtracing/coresight/coresight-tmc-etr.c | 154 +++++++++++++++++- drivers/hwtracing/coresight/coresight-tmc.h | 2 + 5 files changed, 278 insertions(+), 2 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index 595ce5862056..2b8b83e72849 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -86,6 +86,7 @@ enum cs_mode { CS_MODE_DISABLED, CS_MODE_SYSFS, CS_MODE_PERF, + CS_MODE_READ_PREVBOOT, /* Trace data from previous boot */ };
/** diff --git a/drivers/hwtracing/coresight/coresight-tmc-core.c b/drivers/hwtracing/coresight/coresight-tmc-core.c index 67a4bc6f4bff..2f97bb4f5760 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-core.c +++ b/drivers/hwtracing/coresight/coresight-tmc-core.c @@ -152,6 +152,10 @@ static int tmc_open(struct inode *inode, struct file *file) struct tmc_drvdata *drvdata = container_of(file->private_data, struct tmc_drvdata, miscdev);
+ /* Advertise if we are opening with a special mode */ + if (drvdata->mode == CS_MODE_READ_PREVBOOT) + dev_info(&drvdata->csdev->dev, "TMC read mode for previous boot\n"); + ret = tmc_read_prepare(drvdata); if (ret) return ret; @@ -330,9 +334,40 @@ static ssize_t buffer_size_store(struct device *dev,
static DEVICE_ATTR_RW(buffer_size);
+static ssize_t read_prevboot_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct tmc_drvdata *drvdata = dev_get_drvdata(dev->parent); + + return sprintf(buf, "%#x\n", drvdata->size); +} + +static ssize_t read_prevboot_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int ret; + unsigned long val; + struct tmc_drvdata *drvdata = dev_get_drvdata(dev->parent); + + ret = kstrtoul(buf, 0, &val); + if (ret) + return ret; + + if (val) + drvdata->mode = CS_MODE_READ_PREVBOOT; + else + drvdata->mode = CS_MODE_DISABLED; + + return size; +} + +static DEVICE_ATTR_RW(read_prevboot); + static struct attribute *coresight_tmc_attrs[] = { &dev_attr_trigger_cntr.attr, &dev_attr_buffer_size.attr, + &dev_attr_read_prevboot.attr, NULL, };
@@ -632,7 +667,8 @@ static void tmc_shutdown(struct amba_device *adev)
spin_lock_irqsave(&drvdata->spinlock, flags);
- if (drvdata->mode == CS_MODE_DISABLED) + if (drvdata->mode == CS_MODE_DISABLED || + drvdata->mode == CS_MODE_READ_PREVBOOT) goto out;
if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) diff --git a/drivers/hwtracing/coresight/coresight-tmc-etf.c b/drivers/hwtracing/coresight/coresight-tmc-etf.c index 63f59ea1508e..434b1586af54 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etf.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etf.c @@ -664,6 +664,85 @@ const struct coresight_ops tmc_etf_cs_ops = { .panic_ops = &tmc_etf_sync_ops, };
+static int tmc_etb_setup_prevboot_buf(struct tmc_drvdata *drvdata) +{ + u64 trace_addr; + struct tmc_register_snapshot *reg_ptr; + + reg_ptr = drvdata->metadata.vaddr; + trace_addr = reg_ptr->trc_addr | ((u64)reg_ptr->trc_addrhi << 32); + + drvdata->buf = memremap(trace_addr, reg_ptr->size, MEMREMAP_WC); + if (IS_ERR(drvdata->buf)) + return -ENOMEM; + + drvdata->len = reg_ptr->size; + return 0; +} + +static void tmc_etb_free_prevboot_buf(struct tmc_drvdata *drvdata) +{ + void *buf = drvdata->buf; + + if (!buf) + return; + memunmap(buf); + drvdata->buf = NULL; +} + +static int tmc_etb_prepare_prevboot(struct tmc_drvdata *drvdata) +{ + int ret = 0; + unsigned long flags; + struct tmc_register_snapshot *reg_ptr; + + spin_lock_irqsave(&drvdata->spinlock, flags); + + if (drvdata->reading) { + ret = -EBUSY; + goto out; + } + + if (!drvdata->metadata.vaddr) { + ret = -ENOMEM; + goto out; + } + + reg_ptr = drvdata->metadata.vaddr; + if (!reg_ptr->valid) { + dev_err(&drvdata->csdev->dev, + "Invalid metadata captured from previous boot\n"); + ret = -EINVAL; + goto out; + } + + ret = tmc_etb_setup_prevboot_buf(drvdata); + if (ret) + goto out; + + if (reg_ptr->sts & 0x1) + coresight_insert_barrier_packet(drvdata->buf); + drvdata->reading = true; + +out: + spin_unlock_irqrestore(&drvdata->spinlock, flags); + return ret; +} + +static int tmc_etb_unprepare_prevboot(struct tmc_drvdata *drvdata) +{ + unsigned long flags; + + spin_lock_irqsave(&drvdata->spinlock, flags); + + tmc_etb_free_prevboot_buf(drvdata); + drvdata->reading = false; + + spin_unlock_irqrestore(&drvdata->spinlock, flags); + + return 0; +} + int tmc_read_prepare_etb(struct tmc_drvdata *drvdata) { enum tmc_mode mode; @@ -675,6 +754,9 @@ int tmc_read_prepare_etb(struct tmc_drvdata *drvdata) drvdata->config_type != TMC_CONFIG_TYPE_ETF)) return -EINVAL;
+ if (drvdata->mode == CS_MODE_READ_PREVBOOT) + return tmc_etb_prepare_prevboot(drvdata); + spin_lock_irqsave(&drvdata->spinlock, flags);
if (drvdata->reading) { @@ -724,6 +806,9 @@ int tmc_read_unprepare_etb(struct tmc_drvdata *drvdata) drvdata->config_type != TMC_CONFIG_TYPE_ETF)) return -EINVAL;
+ if (drvdata->mode == CS_MODE_READ_PREVBOOT) + return tmc_etb_unprepare_prevboot(drvdata); + spin_lock_irqsave(&drvdata->spinlock, flags);
/* Re-enable the TMC if need be */ diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c index a99b3e9fb9ec..6e8d4069a3fa 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etr.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c @@ -1159,7 +1159,12 @@ ssize_t tmc_etr_get_sysfs_trace(struct tmc_drvdata *drvdata, { s64 offset; ssize_t actual = len; - struct etr_buf *etr_buf = drvdata->sysfs_buf; + struct etr_buf *etr_buf; + + if (drvdata->mode == CS_MODE_READ_PREVBOOT) + etr_buf = drvdata->prevboot_buf; + else + etr_buf = drvdata->sysfs_buf;
if (pos + actual > etr_buf->len) actual = etr_buf->len - pos; @@ -1843,6 +1848,147 @@ const struct coresight_ops tmc_etr_cs_ops = { .panic_ops = &tmc_etr_sync_ops, };
+static int tmc_etr_setup_prevboot_buf(struct tmc_drvdata *drvdata) +{ + int rc = 0; + u64 trace_addr; + struct etr_buf *etr_buf; + struct etr_flat_buf *resrv_buf; + struct tmc_register_snapshot *reg_ptr; + + etr_buf = kzalloc(sizeof(*etr_buf), GFP_KERNEL); + if (!etr_buf) { + rc = -ENOMEM; + goto out; + } + etr_buf->size = drvdata->size; + + resrv_buf = kzalloc(sizeof(*resrv_buf), GFP_KERNEL); + if (!resrv_buf) { + rc = -ENOMEM; + goto rmem_err; + } + + reg_ptr = drvdata->metadata.vaddr; + trace_addr = reg_ptr->trc_addr | ((u64)reg_ptr->trc_addrhi << 32); + + resrv_buf->vaddr = memremap(trace_addr, reg_ptr->size * 4, MEMREMAP_WC); + if (IS_ERR(drvdata->buf)) { + rc = -ENOMEM; + goto map_err; + } + resrv_buf->size = etr_buf->size; + resrv_buf->dev = &drvdata->csdev->dev; + etr_buf->hwaddr = trace_addr; + etr_buf->mode = ETR_MODE_RESRV; + etr_buf->private = resrv_buf; + etr_buf->ops = etr_buf_ops[ETR_MODE_RESRV]; + + drvdata->prevboot_buf = etr_buf; + + return 0; + +map_err: + kfree(resrv_buf); + +rmem_err: + kfree(etr_buf); + +out: + return rc; +} + +static int tmc_etr_sync_prevboot_buf(struct tmc_drvdata *drvdata) +{ + u32 status; + u64 rrp, rwp, dba; + struct tmc_register_snapshot *reg_ptr; + struct etr_buf *etr_buf = drvdata->prevboot_buf; + + reg_ptr = drvdata->metadata.vaddr; + + rrp = reg_ptr->rrp | ((u64)reg_ptr->rrphi << 32); + rwp = reg_ptr->rwp | ((u64)reg_ptr->rwphi << 32); + dba = reg_ptr->dba | ((u64)reg_ptr->dbahi << 32); + status = reg_ptr->sts; + + etr_buf->full = !!(status & TMC_STS_FULL); + + /* Sync the buffer pointers */ + etr_buf->offset = rrp - dba; + if (etr_buf->full) + etr_buf->len = etr_buf->size; + else + etr_buf->len = rwp - rrp; + + return 0; +} + +static void tmc_etr_free_prevboot_buf(struct tmc_drvdata *drvdata) +{ + void *buf = drvdata->prevboot_buf; + + if (!buf) + return; + + memunmap(buf); + drvdata->prevboot_buf = NULL; +} + +static int tmc_etr_prepare_prevboot(struct tmc_drvdata *drvdata) +{ + struct tmc_register_snapshot *reg_ptr; + unsigned long flags; + int ret = 0; + + spin_lock_irqsave(&drvdata->spinlock, flags); + + if (drvdata->reading) { + ret = -EBUSY; + goto out; + } + + if (!drvdata->metadata.vaddr) { + ret = -ENOMEM; + goto out; + } + + reg_ptr = drvdata->metadata.vaddr; + if (!reg_ptr->valid) { + dev_err(&drvdata->csdev->dev, + "Invalid metadata captured from previous boot\n"); + ret = -EINVAL; + goto out; + } + + ret = tmc_etr_setup_prevboot_buf(drvdata); + if (ret) + goto out; + tmc_etr_sync_prevboot_buf(drvdata); + + if (reg_ptr->sts & 0x1) + coresight_insert_barrier_packet(drvdata->buf); + drvdata->reading = true; + +out: + spin_unlock_irqrestore(&drvdata->spinlock, flags); + return ret; +} + +static int tmc_etr_unprepare_prevboot(struct tmc_drvdata *drvdata) +{ + unsigned long flags; + + spin_lock_irqsave(&drvdata->spinlock, flags); + + tmc_etr_free_prevboot_buf(drvdata); + drvdata->reading = false; + + spin_unlock_irqrestore(&drvdata->spinlock, flags); + + return 0; +} + int tmc_read_prepare_etr(struct tmc_drvdata *drvdata) { int ret = 0; @@ -1852,6 +1998,9 @@ int tmc_read_prepare_etr(struct tmc_drvdata *drvdata) if (WARN_ON_ONCE(drvdata->config_type != TMC_CONFIG_TYPE_ETR)) return -EINVAL;
+ if (drvdata->mode == CS_MODE_READ_PREVBOOT) + return tmc_etr_prepare_prevboot(drvdata); + spin_lock_irqsave(&drvdata->spinlock, flags); if (drvdata->reading) { ret = -EBUSY; @@ -1888,6 +2037,9 @@ int tmc_read_unprepare_etr(struct tmc_drvdata *drvdata) if (WARN_ON_ONCE(drvdata->config_type != TMC_CONFIG_TYPE_ETR)) return -EINVAL;
+ if (drvdata->mode == CS_MODE_READ_PREVBOOT) + return tmc_etr_unprepare_prevboot(drvdata); + spin_lock_irqsave(&drvdata->spinlock, flags);
/* RE-enable the TMC if need be */ diff --git a/drivers/hwtracing/coresight/coresight-tmc.h b/drivers/hwtracing/coresight/coresight-tmc.h index 8ebf624b26d9..64d80d95e5eb 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.h +++ b/drivers/hwtracing/coresight/coresight-tmc.h @@ -217,6 +217,7 @@ struct tmc_resrv_buf { * @idr_mutex: Access serialisation for idr. * @sysfs_buf: SYSFS buffer for ETR. * @perf_buf: PERF buffer for ETR. + * @prevboot_buf: Previous boot buffer for ETR * @resrv_buf: Reserved Memory for trace data buffer. Used by ETR/ETF. * @metadata: Reserved memory for metadata. Used by ETR/ETF. */ @@ -243,6 +244,7 @@ struct tmc_drvdata { struct mutex idr_mutex; struct etr_buf *sysfs_buf; struct etr_buf *perf_buf; + struct etr_buf *prevboot_buf; struct tmc_resrv_buf resrv_buf; struct tmc_resrv_buf metadata; };