⇡ lowRISC tagged memory tutorial
Tagged memory support
Tagged memory support
Tagged memory associates metadata with each memory location and can be used to implement fine-grained memory access restrictions. Attacks which hijack control flow can be prevented by using this protection to restrict writes to memory locations containing return addresses, function pointers, and vtable pointers [1].
The implementation of tagged memory presented here provides only basic
support by extending on-chip caches to hold tags and by adding a tag
cache. Instruction set support is provided for testing in the form of
load and store tag (ltag, stag) instructions. Future releases will
add hardware support for particular security policies (e.g.
generating an exception upon modifying data tagged as read-only) and
Linux kernel support.
The tagged memory extensions add a configurable number of tag bits to each 64-bit double word. The size of the L1 data arrays is increased to accommodate the additional tag bits, i.e for 4-bit tags the cache line size increases from 512-bits to 544-bits (+6.5%):

Tags must also be associated with data in main memory. For this implementation tags are stored in a reserved area of physical memory (rather than attempting to use ECC bits to store tags). In order to minimise the performance and memory traffic overheads associated with tags a tag cache is added to the baseline Rocket chip SoC:

The tag cache also performs the role of the original TileLink/MemIO converter, converting TileLink transactions to memory transactions (MemIO). Multiple trackers are provided to allow multiple memory requests to be served in parallel.
Tag cache parameters
| Description | Parameter Name | Default Value | Possible Value |
|---|---|---|---|
| No. of tag bits | TagBits | 4 | >0, <=8 |
| log2(Size of tag partition) | TagMemSize | 24 | [22..25] |
| Base address of tag partition | TagBaseAddr | 0x0F000000 | |
| No. of ways in tag cache | TagCacheWays | 8 | >0, power of 2 |
| No. of sets in tag cache | TagCacheSets | 64 | >0, power of 2 |
| No. of trackers | TagCacheTrackers | 1 | >0 |
The maximum number of tag bits per 64-bit double word currently supported is 8.
| No. of tag bits | Size of tag partition | Base address of partition |
|---|---|---|
| 1 | 4MB (2^22) | 0x0FC00000 |
| 2 | 8MB (2^23) | 0x0F800000 |
| 4 | 16MB (2^24) | 0x0F000000 |
| 8 | 32MB (2^25) | 0x0E000000 |
The number of tag bits does not need to be a power of two, although the size of the tag partition must be a power of 2, e.g. if 6 tag bits are required a 32MB tag partition should be specified (rather than a 24MB one).
If a 32MB tag partition is required the amount of physical memory
available to the Rocket cores must be reduced to 224MB, i.e. edit the
function htif_zedboard_t::mem_mb() in
riscv-tools/riscv-fesvr/fesvr/htif_zedboard.h to return 224.
New load and store tag instructions
Two new instructions have been added to the ISA for loading and storing tags:
# rd: destination register
# rs1: 1st source resister
# imm: immediate number, address offset, 12 bits wide
# load the tag associated with DW located at rs1 + imm to register rd
ltag rd, imm(rs1)
# store the tag in rd to the DW located at rs1 + imm
stag rd, imm(rs1)
Addresses must be double word aligned otherwise a misalignment exception will be raised.
Current support for tags is limited to these simple instructions for testing. Future additions will allow both data and tag to be read atomically.
A step-by-step guide describing how hardware and software support for these new instructions was added is provided here.
Bibliography
[1] Tagged memory and minion cores in the lowRISC SoC, lowRISC-MEMO 2014-001