Skip to content

Latest commit

 

History

History
167 lines (120 loc) · 19.6 KB

chapter2.adoc

File metadata and controls

167 lines (120 loc) · 19.6 KB

Terminology and Concepts

This document refers to the term “secure monitor” as the software responsible for managing security-related tasks, including the programming of IOPMPs. The secure monitor is not restricted to operating on a single CPU or hart; instead, it has the flexibility to be distributed across multiple CPUs.

Table 1. Glossary and Acronyms

Term

Description

DC

don’t care

IMP

implementation-dependent

MMIO

memory mapped input/output devices

NA4

naturally aligned four-byte region, one of the address matching mode used in RISC-V PMP and IOPMP

NAPOT

naturally aligned power-of-2 region, one of the address matching mode used in RISC-V PMP and IOPMP

N/A

not available

RX

receiver

TOR

top boundary of an arbitrary range, one of the address matching mode used in RISC-V PMP and IOPMP

TX

transmitter

WARL

write any read legal

W1C

write '1' clear

W1CS

write '1' clear and sticky to 0

W1S

write '1' set

W1SS

write '1' set and sticky to 1

X( n )

the n-th register in the register array X, which starts from 0.

X[ n ]

the n-th bit of a register X or register field X

X( n : m )

the n-th to m-th registers of a register X.

X[ n : m ]

the n-th to m-th bits of a register X or register field X.

Request-Role-ID and Transaction

Request Role ID, RRID for short, is a unique ID to identify a system-defined security context. For example, a unique RRID can be a transaction requestor or a group of transaction requestors with the same permission. When a requestor wants to access a piece of memory, it issues a transaction. A transaction should be tagged with an RRID to identify the issuing requestor. We will discuss about the exception in the next section. Tagging requestors with RRID could be implementation-dependent. The number of bits of an RRID is implementation-dependent as well. If different channels or modes of a requestor could be granted different access permissions, they can have its own RRID.

Source-Enforcement

If all transactions going through the IOPMP are issued by the same transaction requestor or a set of transaction requestors with the same permission, the RRID can be ignored on the transaction requestor side and the above transactions. In the case, we denote the IOPMP performs source enforcement, IOPMP-SE for short.

Requestor Port, Receiver Port and Control Port

An IOPMP has at least an requestor port, at least a receiver port and one control port. A receiver port is where a transaction goes into the IOPMP, and a requestor port is where a transaction leaves it if the transaction passes all the checks. The control port is used to program the IOPMP.

Memory Domain

An RRID is an abstract representation of a transaction requestor. It encompasses one or more transaction requestors that are granted identical permissions. On the other hand, a Memory Domain, MD for short, is an abstract representation of a transaction destination that groups a set of memory regions for a specific purpose. MDs are indexed from zero. For example, a network interface controller, NIC, may have three memory regions: an RX region, a TX region, and a region of control registers. We could group them into one MD. If a processor can fully control the NIC, it can be associated with the MD. An RRID associated with a MD doesn’t mean having full permissions on all memory regions of the MD. The permission of each region is defined in the corresponding IOPMP entry. Additionally, there is an extension to adhere the permission to the MD that will be introduced in the Appendix A3.

It’s important to note that, generally speaking, a single RRID can be associated with multiple Memory Domains (MDs), and vice versa. However, certain configurations may impose restrictions on this flexibility, which will be discussed in the following chapter.

IOPMP Entry and IOPMP Entry Array

The IOPMP entry array, a fundamental structure of an IOPMP, contains IOPMP entries. Each entry, starting from an index of zero, defines how to check a transaction. An entry includes a specified memory region, the corresponding permissions, and optional features about error reactions and user customized attributes.

For an entry indexed by i, ENTRY_ADDR(i), ENTRY_ADDRH(i) and ENTRY_CFG(i).a encode the memory region in the same way as the RISC-V PMP. ENTRY_ADDR(i) and ENTRY_ADDRH(i) are the encoded address for the IOPMP with 64-bit addresses. For 34-bit addresses, only ENTRY_ADDR(i) is in use. To determine whether ENTRY_ADDRH(i) are implemented, one can check HWCFG0.addrh_en. Please refer HWCFG0 for more details of HWCFG0.addrh_en. ENTRY_CFG(i).a is the address mode, which are OFF, NA4, NAPOT, and TOR. Please refer to the RISC-V privileged spec for the details of the encoding schemes. HWCFG0.tor_en = 1 indicates the IOPMP supports TOR address mode.

Note
Since the address encoding scheme of TOR refers to the previous entry’s memory region, which is not in the same memory domain, it would cause unexpected results. If the first entry of a memory domain selects TOR, the entry refers to the previous memory domain. When the previous memory domain may not change unexpectedly, the region of this entry will be altered. To prevent the unexpected change of memory region, software should (1) avoid adopting TOR for the first entry of a memory domain, or (2) set an OFF for the last entry of a memory domain with the maximal address during programming the IOPMP.

ENTRY_CFG(i).r/w/x indicate the read access, write access and instruction fetch permission and they are WARL. That is, an implementation can decide which bits are programmable or hardwired and which bit combinations are unwanted. HWCFG0.chk_x=1 indicates that the IOPMP allows an instruction fetch for a region according to the bit x; otherwise, according to the bit r. HWCFG0.chk_x=0 is also used for an IOPMP, which is unaware of whether the access is a read or an instruction fetch.

Every entry has several WARL fields sire/siwe/sixe/esre/eswe/esxe, suppressing interrupts and bus error responses for its error reaction, and will be introduced in Error Reactions. The optional register ENTRY_USER_CFG stores customized attributes for an entry. To determine whether the register is implemented, one can check HWCFG0.user_cfg_en.

Memory domains are a way of dividing the entry array into different subarrays. Each entry in the array can belong to at most one memory domain, while a memory domain could have multiple entries.

When an RRID is associated with a Memory Domain, it is also inherently associated with all the entries that belong to that MD. An RRID could be associated with multiple Memory Domains, and one Memory Domain could be associated with multiple RRIDs.

Priority and Matching Logic

There are two read-only bits, HWCFG0.no_w and HWCFG0.no_x, used to decide whether the IOPMP denies a write transaction and an instruction fetch, respectively. An IOPMP always fails a write transaction when HWCFG0.no_w is 1, and fails instruction fetch transaction when HWCFG0.no_x is 1. The error type is "not hit any rule" (0x05).

When a transaction arrives at an IOPMP, the IOPMP first checks whether the RRID carried by the transaction is legal. If the RRID is equal to or greater than HWCFG1.rrid_num, the transaction is illegal with error type = "Unknown RRID" (0x06)". An implementation can also decide whether an RRID is legal even though it < rrid_num.

IOPMP entries exhibit partial prioritization. Entries with indices smaller than HWCFG2.prio_entry are prioritized according to their index, with smaller indices having higher priority. These entries are referred to as priority entries. Conversely, entries with indices greater than or equal to prio_entry are treated equally and assigned the lowest priority. These entries are referred to as non-prioritized entries. The value of prio_entry is implementation-dependent. Additionally, HWCFG0.prient_prog indicates if prio_entry is programmable.

Note
The specification incorporates both priority and non-priority entries due to considerations of security, latency, and area. Priority entries, which are locked, safeguard the most sensitive data, even in the event of secure software being compromised. However, implementing a large number of these priority entries results in higher latency and increased area usage. On the other hand, non-priority entries are treated equally and can be cached in smaller numbers. This approach reduces the amortized latency, power consumption, and area when the locality is sufficiently high. Thus, the mix of entry types in the specification allows for a balance between security and performance.

The entry with the highest priority that (1) matches any byte of the incoming transaction and (2) is associated with the RRID carried by the transaction determines whether the transaction is legal. If the matching entry is priority entry, the matching entry must match all bytes of a transaction, or the transaction is illegal with error type = "partial hit on a priority rule" (0x04), irrespective of its permission. An entry has own permission and permission from Memory Domain to grant a transaction permission. If a priority entry is matched but doesn’t grant transaction permission to operate, the transaction is illegal with error type = "illegal read access" (0x01) for read access transaction, "illegal write access" (0x02) for write access transaction, or "illegal instruction fetch" (0x03) for instruction fetch transaction.

Let’s consider a non-priority entry matching all bytes of a transaction. It is legal if the entry grants the transaction permission to operate. When multiple non-priority entries match all bytes of a transaction and one of them allows the transaction, the transaction is legal. If none of them allows, the transaction is illegal with error type = "illegal read access" (0x01) for read access transaction, "illegal write access" (0x02) for write access transaction, or "illegal instruction fetch" (0x03) for instruction fetch transaction.

Finally, if no such above entry exists, the transaction is illegal with error type = "not hit any rule" (0x05).

iopmp unit block diagram
Figure 1: an example block diagram of an IOPMP. It illustrates the checking flow of an IOPMP. This IOPMP takes three inputs: RRID, the transaction type (read/write), and the request range (address/len). It first looks up the SRCMD table according to the RRID carried by the incoming transaction to retrieve associated MD indexes and the corresponding permissions related to these MDs. By the MD indexes, the IOPMP looks up the MDCFG table to get the belonging entry indexes. The final step checks the access right according to the above entry indexes and corresponding permissions. An interrupt, an error response, and/or a record is generated once the transaction fails the permission check in the step.

Error Reactions

Upon detecting an illegal transaction, the IOPMP could initiate three of the following actions:

  1. Trigger an interrupt to notify the system of the violation.

  2. Return bus error (or a decode error) or not with an implementation-defined value.

  3. Log the error details in IOPMP error record registers.

The interrupt enabling on an IOPMP violation can be configured globally via ERR_CFG register or optionally locally through the ENTRY_CFG register for each entry. The ERR_CFG.ie bit serves as the global interrupt enable configure bit. HWCFG0.peis is 1 if an implementation supports sire, siwe, or sixe. Every entry i has three optional interrupt suppressing bits in register ENTRY_CFG(i), sire, siwe, and sixe to suppress interrupt triggering due to illegal reads, illegal writes and illegal instruction fetches on the corresponding entry, respectively. Such local interrupt control mechanism can be beneficial in scenarios such as configuring guard regions for speculative access handling. The interrupt pending indication is equivalent to the error valid indication, both are flagged through the ERR_INFO.v bit. An IOPMP interrupt will be triggered when a transaction is illegal and the interrupt is not suppressed. An IOPMP triggers interrupt by global interrupt enable configure bit ie and suppressing bits (sire, siwe, or sixe) in entries if a transaction only violates permissions on entries and peis is 1. On the other hand, if a transaction doesn’t only violate permissions on entries, an IOPMP triggers interrupt only by global interrupt enable configure bit ie. The permissions include permission bits in entries (ENTRY_CFG(i).r/w/x) and permission bits from SRCMD table (please refer SRCMD Table Formats for the details) to corresponding entries. The relation of interrupt control with interrupt suppression bits for an illegal transaction can be more precisely described as follows:

An entry indexed by i has the highest priority and matches all bytes of the illegal transaction, and error type of the illegal transaction is:

  • Illegal read access (0x01):
    ERR_CFG.ie && !ENTRY_CFG(i).sire

  • Illegal write access (0x02):
    ERR_CFG.ie && !ENTRY_CFG(i).siwe

  • Illegal instruction fetch (0x03):
    ERR_CFG.ie && !ENTRY_CFG(i).sixe

For some cases with multiple matched non-priority entries, the more detailed relation is:

Entries indexed by i0, i1, …​, iN match all bytes of the illegal transaction, and error type of the illegal transaction is:

  • Illegal read access (0x01):
    ERR_CFG.ie && ( !ENTRY_CFG(i0).sire || !ENTRY_CFG(i1).sire || …​ || !ENTRY_CFG(iN).sire )

  • Illegal write access (0x02):
    ERR_CFG.ie && ( !ENTRY_CFG(i0).siwe || !ENTRY_CFG(i1).siwe || …​ || !ENTRY_CFG(iN).siwe )

  • Instruction fetch transaction (0x03):
    ERR_CFG.ie && ( !ENTRY_CFG(i0).sixe || !ENTRY_CFG(i1).sixe || …​ || !ENTRY_CFG(iN).sixe )

Transactions that violates the IOPMP rule will by default yield a bus error. Additionally, the bus error response behavior on an IOPMP violation can be optionally configured globally via ERR_CFG register or locally through each ENTRY_CFG register. The IOPMP will signal the bus to the presence of a violation but will suppress the bus error if ERR_CFG.rs is implemented and set to 1 on a violation. User-defined suppression behavior allows, for example, a read response of 0x0. Likewise, the bus error response on an illegal write or instruction fetch.

In the same way, the bus error response behavior can be set up globally and individually for each IOPMP entry. ERR_CFG.rs globally suppresses returning a bus error on illegal access. When global suppression is disabled, individual per-entry suppression is possible using sere, sewe, and sexe for illegal read, illegal write, and illegal instruction fetch, respectively. HWCFG0.pees is 1 if an IOPMP implements sere, sewe, and sexe. An IOPMP will respond with a bus error when a transaction is illegal and the bus error is not suppressed. Bus error response behavior of an IOPMP is controlled by global interrupt enable configure bit rs and suppressing bits (sere, sewe, or sexe) in entries if a transaction only violates permissions on entries and pees is 1. On the other hand, if a transaction doesn’t only violate permissions on entries, bus error response behavior of an IOPMP is controlled only by global interrupt enable configure bit ie. The permissions include permission bits in entries (ENTRY_CFG(i).r/w/x) and permission bits from SRCMD table (please refer SRCMD Table Formats for the details) to corresponding entries. The relation of bus error response suppression control with supression bits in entries for an illegal transaction can be more precisely described as follows:

An entry indexed by i has the highest priority and matches all bytes of the illegal transaction, and error type of the illegal transaction is:

  • Illegal read access (0x01):
    ERR_CFG.ie && !ENTRY_CFG(i).sere

  • Illegal write access (0x02):
    ERR_CFG.ie && !ENTRY_CFG(i).sewe

  • Illegal instruction fetch (0x03):
    ERR_CFG.ie && !ENTRY_CFG(i).sexe

For some cases with multiple matched non-priority entries, the more detailed relation is:

Entries indexed by i0, i1, …​, iN match all bytes of the illegal transaction, and error type of the illegal transaction is:

  • Illegal read access (0x01):
    !ERR_CFG.rs && ( !ENTRY_CFG(i0).sere || !ENTRY_CFG(i1).sere || …​ || !ENTRY_CFG(iN).sere )

  • Illegal write access (0x02):
    !ERR_CFG.rs && ( !ENTRY_CFG(i0).sewe || !ENTRY_CFG(i1).sewe || …​ || !ENTRY_CFG(iN).sewe )

  • Illegal instruction fetch (0x03):
    !ERR_CFG.rs && ( !ENTRY_CFG(i0).sexe || !ENTRY_CFG(i1).sexe || …​ || !ENTRY_CFG(iN).sexe )

The error capture record maintains the specifics of the first illegal access detected, except if the following two conditions are held: (1) any interrupt-suppress bit regarding the access is set, and (2) no bus error is returned. New error capture only occurs when there is no currently pending error, namely ERR_INFO.v is ‘0’. If a pending error exists (v is ‘1’), the record will not be updated, even if a new illegal access is detected. In other words, v indicates whether the content of the capture record is valid and should be intentionally cleared in order to capture subsequent illegal accesses. One can write 1 to the bit to clear it. The error capture record is optional. If it is not implemented, v should be wired to zero. One can implement the error capture record but not ERR_REQID.eid. In this case, eid should be wired to 0xffff.

If a transaction violates IOPMP rules on multiple non-priority entries which don’t suppress interrupt or bus error response, the ERR_REQID.eid should be one of the entries.

The following table shows (1) the error types, and (2) related control bits about interrupt triggering and bus error response if the IOPMP supports local control bits (HWCFG0.peis is 1 and/or HWCFG0.pees is 1):

Table 2. Error types and corresponding control bits

Error type

Control bits

0x00

No error

N/A

0x01

Illegal read access

Global1. and local2.

0x02

Illegal write access

Global and local

0x03

Illegal instruction fetch

Global and local

0x04

Partial hit on a priority rule

Global

0x05

Not hit any rule

  • No entry matches all bytes of a transaction

  • Receives a write access transaction when HWCFG0.no_w is 1

  • Receives an instruction fetch transaction when HWCFG0.no_x is 1

Global

0x06

Unknown RRID

Global

0x07

Error due to a stalled transaction. Please refer Faulting stalled transactions.

Global

0x08 ~ 0x0D

N/A, reserved for future

N/A

0x0E ~ 0x0F

User-defined error

Implementation-dependent

1. Bit ie or rs in ERR_CFG. It depends on which reaction (i.e., interrupt or bus error response).

2. Bits sire, siwe, sixe, sere, sewe, or sexe in ENTRY_CFG(i). It depends on which reaction (i.e., interrupt or bus error response) and which transaction type of the illegal transaction (i.e., read access, write access or instruction fetch).