It's nice to see people starting to get serious about addressing this long standing issue. We (Ventana) also worked out a proposal for these issues - that is more in the vein of what Brian mentioned (for the reasons he mentioned, plus additional reasons). With this proposal:
- All per-counter state resides in the associated mhpmevent CSR.
- This proposal avoids adding any new CSR registers that would need to be context switched by a hypervisor. Not mixing counter state from different counters into common registers also greatly simplifies this.
- There is just one added CSR - that is a shadow copy of the overflow status of all counters collected together in one place.
- There are per-mode filter bits that comprehend the H-extension. Full inter-mode security (i.e. lower privilege modes should not be able to count and observe high modes) is mediated by each mode as it receives software calls to configure a counter from below and calls up (ultimately to M-mode through OpenSBI) to perform the mhpmevent CSR write.
- Providing hpmcounter write access (when enabled) to lower privilege modes is properly handled, even with the H-extension, by keying off of read access being mediated by the three *counteren CSRs.
- Nothing extra is needed hardware-wise for virtualization of this Counter extension.
- Lastly, to Brian's point re counter marking, our proposal includes a per-counter Active bit that software can use to enable/disable active counting of events. (We also use it to activate/deactivate counting when various hardware conditions occur - but that's a story for another day.) This state context switches along with the rest of a counter's state. (A new CSR could be provided to enable non-M-mode software to directly control this bit. This proposal doesn't include such, but that could be a point of discussion.)
Here is a summary of the proposal:
The following bits are added to 'mhpmevent' (with proposed bit positions):
bit  Active - If set, then counting of events is enabled
bit  Overflow - Sticky overflow status bit that is set when counter overflows
bit  IntrEnable - If set, an interrupt request is raised while Overflow=1
bit  CntrWrEn - If set, then lower privilege modes that can read hpmcounter can also write it
bit  Mdisable - If set, then counting of events in M-mode is disabled
bit  HSdisable - If set, then counting of events in S/HS-mode is disabled
bit  Udisable - If set, then counting of events in U-mode is disabled
bit  VSdisable - If set, then counting of events in VS-mode is disabled
bit  VUdisable - If set, then counting of events in VU-mode is disabled
- Since hpmcounter values are unsigned values, overflow (to be consistent) is defined as unsigned overflow. (This matches x86 and ARMv8.) Note that there is no loss of information after an overflow since the counter wraps around and keeps counting while the sticky Overflow bit is set. (For a 64-bit counter it will be an awfully long time before another overflow could possibly occur.)
- A single level-sensitive "overflow interrupt request" signal is asserted while any Overflow bits are set. This goes to whatever interrupt controller is present in the system (whether as some form of hart-local interrupt or as a global interrupt). (This proposal doesn't try to introduce per-privilege mode overflow interrupt request signals. ARMv8 doesn't have this and I don't think (?) x86 does either.)
The following one CSR is added: hpmoverflow
- This is a 32-bit register that contains shadow copies of the Overflow bits in the 32 mhpmevent CSRs. hpmoverflow bit X corresponds to mhpmeventX. This register enables overflow interrupt handler software to quickly and easily determine which counter(s) have overflowed (and to directly clear the overflow bits as they are serviced)
- This register is readable and write-one-to-clear. This read/write access is subject to the same *counteren CSRs that mediate access to the hpmcounter CSRs by each privilege mode. In other words, the same "visibility" controls apply both to the hpmcounter's and to their associated hpmoverflow Overflow bits. Bits that should not be visible are RAZ/WI (read-as-zero / write-ignored).