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>] [day] [month] [year] [list]
Date:	Wed, 26 Nov 2008 00:42:29 -0800 (PST)
From:	eranian@...glemail.com
To:	linux-kernel@...r.kernel.org
Cc:	akpm@...ux-foundation.org, mingo@...e.hu, x86@...nel.org,
	andi@...stfloor.org, eranian@...il.com, sfr@...b.auug.org.au
Subject: [patch 12/24] perfmon: read and write registers

This patch adds the functions to read and write PMU data and
config registers.

Signed-off-by: Stephane Eranian <eranian@...il.com>
--

Index: o3/perfmon/perfmon_rw.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ o3/perfmon/perfmon_rw.c	2008-11-25 18:59:05.000000000 +0100
@@ -0,0 +1,449 @@
+/*
+ * perfmon.c: perfmon2 PMC/PMD read/write system calls
+ *
+ * This file implements the perfmon2 interface which
+ * provides access to the hardware performance counters
+ * of the host processor.
+ *
+ * The initial version of perfmon.c was written by
+ * Ganesh Venkitachalam, IBM Corp.
+ *
+ * Then it was modified for perfmon-1.x by Stephane Eranian and
+ * David Mosberger, Hewlett Packard Co.
+ *
+ * Version Perfmon-2.x is a complete rewrite of perfmon-1.x
+ * by Stephane Eranian, Hewlett Packard Co.
+ *
+ * Copyright (c) 1999-2006 Hewlett-Packard Development Company, L.P.
+ * Contributed by Stephane Eranian <eranian@....hp.com>
+ *                David Mosberger-Tang <davidm@....hp.com>
+ *
+ * More information about perfmon available at:
+ * 	http://perfmon2.sf.net/
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/perfmon_kern.h>
+#include "perfmon_priv.h"
+
+/**
+ * is_invalid -- check if register index is within limits
+ * @cnum: register index
+ * @impl: bitmask of implemented registers
+ * @max: highest implemented registers + 1
+ *
+ * return:
+ *    0 is register index is valid
+ *    1 if invalid
+ */
+static inline int is_invalid(u16 cnum, u64 *impl, u16 max)
+{
+	return cnum >= max || !pfm_arch_bv_test_bit(cnum, impl);
+}
+
+/**
+ * update_used_reg -- updated used_pmcs for a single PMD
+ * @set: set to update
+ * @cnum: new PMD to add
+ *
+ * This function adds the pmds and pmcs depending on PMD cnum
+ */
+static inline void update_used_reg(struct pfm_context *ctx,
+				   struct pfm_event_set *set, u16 cnum)
+{
+	pfm_arch_bv_or(set->used_pmcs,
+		       set->used_pmcs,
+		       pfm_pmu_conf->pmd_desc[cnum].dep_pmcs,
+		       ctx->regs.max_pmc);
+}
+
+/**
+ * update_changes -- update nused_pmcs, nused_pmds, write newly touched pmcs
+ * @ctx: context to use
+ * @set: event set to use
+ * @old_used_pmcs: former used_pmc bitmask
+ *
+ * This function updates nused_pmcs and nused_pmds after the last modificiation
+ * to an event set. When new pmcs are used, then they must be initialized such
+ * that we do not pick up stale values from another session.
+ */
+static inline int update_changes(struct pfm_context *ctx, struct pfm_event_set *set,
+				 u64 *old_used_pmcs)
+{
+	struct pfarg_pmr req;
+	u16 max_pmc, max_pmd;
+	int n, p, q, ret = 0;
+
+	max_pmd = ctx->regs.max_pmd;
+	max_pmc = ctx->regs.max_pmc;
+
+	/*
+	 * update used counts
+	 */
+	set->nused_pmds = pfm_arch_bv_weight(set->used_pmds, max_pmd);
+	set->nused_pmcs = pfm_arch_bv_weight(set->used_pmcs, max_pmc);
+
+	PFM_DBG("u_pmds=0x%llx nu_pmds=%u u_pmcs=0x%llx nu_pmcs=%u",
+		(unsigned long long)set->used_pmds[0],
+		set->nused_pmds,
+		(unsigned long long)set->used_pmcs[0],
+		set->nused_pmcs);
+
+	memset(&req, 0, sizeof(req));
+
+	n = pfm_arch_bv_weight(set->used_pmcs, max_pmc);
+	for(p = 0; n; n--, p = q+1) {
+		q = pfm_arch_bv_find_next_bit(set->used_pmcs, max_pmc, p);
+
+		if (pfm_arch_bv_test_bit(q, old_used_pmcs))
+			continue;
+
+		req.reg_num = q;
+		req.reg_value = set->pmcs[q];
+
+		ret = __pfm_write_pmcs(ctx, &req, 1);
+		if (ret)
+			break;
+	}
+	return ret;
+}
+
+/**
+ * __pfm_write_pmds - modify data registers
+ * @ctx: context to operate on
+ * @req: pfarg_pmd_t request from user
+ * @count: number of element in the pfarg_pmd_t vector
+ *
+ * The function succeeds whether the context is attached or not.
+ * When attached to another thread, that thread must be stopped.
+ *
+ * The context is locked and interrupts are disabled.
+ */
+int __pfm_write_pmds(struct pfm_context *ctx, struct pfarg_pmr *req, int count)
+{
+	struct pfm_event_set *set;
+	u64 old_used_pmcs[PFM_PMC_BV];
+	u64 value, ovfl_mask;
+	u64 *impl_pmds;
+	u16 cnum, pmd_type, max_pmd;
+	int i, can_access_pmu;
+	int ret;
+	pfm_pmd_check_t	wr_func;
+
+	ovfl_mask = pfm_pmu_conf->ovfl_mask;
+	max_pmd	= ctx->regs.max_pmd;
+	impl_pmds = ctx->regs.pmds;
+	wr_func = pfm_pmu_conf->pmd_write_check;
+
+	can_access_pmu = 0;
+
+	/*
+	 * we cannot access the actual PMD registers when monitoring is masked
+	 */
+	if (unlikely(ctx->state == PFM_CTX_LOADED))
+		can_access_pmu = __get_cpu_var(pmu_owner) == ctx->task;
+
+	ret = -EINVAL;
+	set = ctx->active_set;
+
+	pfm_arch_bv_copy(old_used_pmcs, set->used_pmcs,
+			 ctx->regs.max_pmc);
+
+	for (i = 0; i < count; i++, req++) {
+
+		cnum = req->reg_num;
+
+		/*
+		 * cannot write to unexisting
+		 * writes to read-only register are ignored
+		 */
+		if (unlikely(is_invalid(cnum, impl_pmds, max_pmd))) {
+			PFM_DBG("pmd%u is not available", cnum);
+			goto error;
+		}
+
+		pmd_type = pfm_pmu_conf->pmd_desc[cnum].type;
+
+		/*
+		 * execute write checker, if any
+		 */
+		if (unlikely(wr_func && (pmd_type & PFM_REG_WC))) {
+			ret = (*wr_func)(ctx, set, req);
+			if (ret)
+				goto error;
+
+		}
+
+		value = req->reg_value;
+
+		/*
+		 * we reprogram the PMD hence, we clear any pending
+		 * ovfl. Does affect ovfl switch on restart but new
+		 * value has already been established here
+		 */
+		if (pfm_arch_bv_test_bit(cnum, set->povfl_pmds)) {
+			set->npend_ovfls--;
+			pfm_arch_bv_clear_bit(cnum, set->povfl_pmds);
+		}
+
+		/*
+		 * update value
+		 */
+		set->pmds[cnum] = value;
+
+		pfm_arch_bv_set_bit(cnum, set->used_pmds);
+		update_used_reg(ctx, set, cnum);
+
+		set->priv_flags |= PFM_SETFL_PRIV_MOD_PMDS;
+		if (can_access_pmu)
+			pfm_write_pmd(ctx, cnum, value);
+
+		/*
+		 * update number of used PMD registers
+		 */
+		set->nused_pmds = pfm_arch_bv_weight(set->used_pmds,
+						     max_pmd);
+
+		PFM_DBG("pmd%u=0x%llx a_pmu=%d "
+			"ctx_pmd=0x%llx "
+			" u_pmds=0x%llx nu_pmds=%u ",
+			cnum,
+			(unsigned long long)value,
+			can_access_pmu,
+			(unsigned long long)set->pmds[cnum],
+			(unsigned long long)set->used_pmds[0],
+			set->nused_pmds);
+	}
+	ret = 0;
+error:
+	update_changes(ctx, set, old_used_pmcs);
+	/*
+	 * make changes visible
+	 */
+	if (can_access_pmu)
+		pfm_arch_serialize();
+
+	return ret;
+}
+
+/**
+ * __pfm_write_pmcs - modify config registers
+ * @ctx: context to operate on
+ * @req: pfarg_pmc_t request from user
+ * @count: number of element in the pfarg_pmc_t vector
+ *
+ *
+ * The function succeeds whether the context is * attached or not.
+ * When attached to another thread, that thread must be stopped.
+ *
+ * The context is locked and interrupts are disabled.
+ */
+int __pfm_write_pmcs(struct pfm_context *ctx, struct pfarg_pmr *req, int count)
+{
+	struct pfm_event_set *set;
+	u64 value, dfl_val, rsvd_msk;
+	u64 *impl_pmcs;
+	int i, can_access_pmu;
+	int ret;
+	u16 cnum, pmc_type, max_pmc;
+	pfm_pmc_check_t	wr_func;
+
+	wr_func = pfm_pmu_conf->pmc_write_check;
+	max_pmc = ctx->regs.max_pmc;
+	impl_pmcs = ctx->regs.pmcs;
+
+	can_access_pmu = 0;
+
+	/*
+	 * we cannot access the actual PMC registers when monitoring is masked
+	 */
+	if (unlikely(ctx->state == PFM_CTX_LOADED))
+		can_access_pmu = __get_cpu_var(pmu_owner) == ctx->task;
+
+	ret = -EINVAL;
+	set = ctx->active_set;
+
+	for (i = 0; i < count; i++, req++) {
+
+		cnum = req->reg_num;
+		value = req->reg_value;
+
+		/*
+		 * no access to unavailable PMC register
+		 */
+		if (unlikely(is_invalid(cnum, impl_pmcs, max_pmc))) {
+			PFM_DBG("pmc%u is not available", cnum);
+			goto error;
+		}
+
+		pmc_type = pfm_pmu_conf->pmc_desc[cnum].type;
+		dfl_val = pfm_pmu_conf->pmc_desc[cnum].dfl_val;
+		rsvd_msk = pfm_pmu_conf->pmc_desc[cnum].rsvd_msk;
+
+		/*
+		 * set reserved bits to default values
+		 * (reserved bits must be 1 in rsvd_msk)
+		 */
+		value = (value & ~rsvd_msk) | (dfl_val & rsvd_msk);
+
+		/*
+		 * execute write checker, if any
+		 */
+		if (likely(wr_func && (pmc_type & PFM_REG_WC))) {
+			req->reg_value = value;
+			ret = (*wr_func)(ctx, set, req);
+			if (ret)
+				goto error;
+			value = req->reg_value;
+		}
+
+		/*
+		 * Now we commit the changes
+		 */
+
+		/*
+		 * mark PMC register as used
+		 * We do not track associated PMC register based on
+		 * the fact that they will likely need to be written
+		 * in order to become useful at which point the statement
+		 * below will catch that.
+		 *
+		 * The used_pmcs bitmask is only useful on architectures where
+		 * the PMC needs to be modified for particular bits, especially
+		 * on overflow or to stop/start.
+		 */
+		if (!pfm_arch_bv_test_bit(cnum, set->used_pmcs)) {
+			pfm_arch_bv_set_bit(cnum, set->used_pmcs);
+			set->nused_pmcs++;
+		}
+
+		set->pmcs[cnum] = value;
+
+		set->priv_flags |= PFM_SETFL_PRIV_MOD_PMCS;
+		if (can_access_pmu)
+			pfm_arch_write_pmc(ctx, cnum, value);
+
+		PFM_DBG("pmc%u=0x%llx a_pmu=%d "
+			"u_pmcs=0x%llx nu_pmcs=%u",
+			cnum,
+			(unsigned long long)value,
+			can_access_pmu,
+			(unsigned long long)set->used_pmcs[0],
+			set->nused_pmcs);
+	}
+	ret = 0;
+error:
+	/*
+	 * make sure the changes are visible
+	 */
+	if (can_access_pmu)
+		pfm_arch_serialize();
+
+	return ret;
+}
+
+/**
+ * __pfm_read_pmds - read data registers
+ * @ctx: context to operate on
+ * @req: pfarg_pmd_t request from user
+ * @count: number of element in the pfarg_pmd_t vector
+ *
+ *
+ * The function succeeds whether the context is attached or not.
+ * When attached to another thread, that thread must be stopped.
+ *
+ * The context is locked and interrupts are disabled.
+ */
+int __pfm_read_pmds(struct pfm_context *ctx, struct pfarg_pmr *req, int count)
+{
+	u64 val = 0, ovfl_mask, hw_val;
+	u64 *impl_pmds;
+	struct pfm_event_set *set;
+	int i, ret, can_access_pmu = 0;
+	u16 cnum, pmd_type, max_pmd;
+
+	ovfl_mask = pfm_pmu_conf->ovfl_mask;
+	impl_pmds = ctx->regs.pmds;
+	max_pmd   = ctx->regs.max_pmd;
+
+	if (likely(ctx->state == PFM_CTX_LOADED)) {
+		can_access_pmu = __get_cpu_var(pmu_owner) == ctx->task;
+		if (can_access_pmu)
+			pfm_arch_serialize();
+	}
+
+	/*
+	 * on both UP and SMP, we can only read the PMD from the hardware
+	 * register when the task is the owner of the local PMU.
+	 */
+	ret = -EINVAL;
+	set = ctx->active_set;
+
+	for (i = 0; i < count; i++, req++) {
+
+		cnum = req->reg_num;
+
+		if (unlikely(is_invalid(cnum, impl_pmds, max_pmd))) {
+			PFM_DBG("pmd%u is not implemented/unaccessible", cnum);
+			goto error;
+		}
+
+		pmd_type = pfm_pmu_conf->pmd_desc[cnum].type;
+
+		/*
+		 * it is not possible to read a PMD which was not requested:
+		 * 	- explicitly written via pfm_write_pmds()
+		 * 	- provided as a reg_smpl_pmds[] to another PMD during
+		 * 	  pfm_write_pmds()
+		 *
+		 * This is motivated by security and for optimization purposes:
+		 * 	- on context switch restore, we can restore only what
+		 * 	  we use (except when regs directly readable at user
+		 * 	  level, e.g., IA-64 self-monitoring, I386 RDPMC).
+		 * 	- do not need to maintain PMC -> PMD dependencies
+		 */
+		if (unlikely(!pfm_arch_bv_test_bit(cnum, set->used_pmds))) {
+			PFM_DBG("pmd%u cannot read, because not used", cnum);
+			goto error;
+		}
+
+		val = set->pmds[cnum];
+
+		/*
+		 * If the task is not the current one, then we check if the
+		 * PMU state is still in the local live register due to lazy
+		 * ctxsw. If true, then we read directly from the registers.
+		 */
+		if (can_access_pmu) {
+			hw_val = pfm_read_pmd(ctx, cnum);
+			if (pmd_type & PFM_REG_C64)
+				val = (val & ~ovfl_mask)
+				    | (hw_val & ovfl_mask);
+			else
+				val = hw_val;
+		}
+
+		PFM_DBG("pmd%u=0x%llx ",
+			cnum,
+			(unsigned long long)val);
+
+		req->reg_value = val;
+	}
+	ret = 0;
+error:
+	return ret;
+}
Index: o3/include/linux/perfmon.h
===================================================================
--- o3.orig/include/linux/perfmon.h	2008-11-25 18:29:29.000000000 +0100
+++ o3/include/linux/perfmon.h	2008-11-25 18:58:39.000000000 +0100
@@ -46,6 +46,22 @@
 #define PFM_PMC_BV	PFM_BVSIZE(PFM_MAX_PMCS)
 
 /*
+ * PMC and PMD generic register description
+ */
+struct pfarg_pmr {
+	__u16 reg_num;		/* which register */
+	__u16 reg_res1;		/* reserved */
+	__u32 reg_flags;	/* REGFL flags */
+	__u64 reg_value;	/* 64-bit value */
+};
+
+/*
+ * pfm_write, pfm_read type:
+ */
+#define PFM_RW_PMD	0x01 /* accessing PMD registers */
+#define PFM_RW_PMC	0x02 /* accessing PMC registers */
+
+/*
  * default value for the user and group security parameters in
  * /proc/sys/kernel/perfmon/sys_group
  * /proc/sys/kernel/perfmon/task_group
Index: o3/perfmon/perfmon_priv.h
===================================================================
--- o3.orig/perfmon/perfmon_priv.h	2008-11-25 18:58:32.000000000 +0100
+++ o3/perfmon/perfmon_priv.h	2008-11-25 18:58:39.000000000 +0100
@@ -44,6 +44,11 @@
 }
 
 int pfm_init_ctx(void);
+int __pfm_write_pmcs(struct pfm_context *ctx, struct pfarg_pmr *req,
+		     int count);
+int __pfm_write_pmds(struct pfm_context *ctx, struct pfarg_pmr *req,
+		     int count);
+int __pfm_read_pmds(struct pfm_context *ctx, struct pfarg_pmr *req, int count);
 
 int pfm_session_acquire(void);
 void pfm_session_release(void);

-- 

--
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