From 1461046fe978d658df6d4823052374a0c02cbc0e Mon Sep 17 00:00:00 2001 From: paul-andes <93809688+paul-andes@users.noreply.github.com> Date: Wed, 9 Oct 2024 17:25:45 +0800 Subject: [PATCH] for the last updates by 2024Q3 for more details, please check the minutes of Q3. Signed-off-by: paul-andes <93809688+paul-andes@users.noreply.github.com> --- appendix_a1.adoc | 10 ++- appendix_a2.adoc | 3 +- appendix_a3.adoc | 5 +- chapter2.adoc | 222 +++++++++++++++++++++++++++-------------------- chapter3.adoc | 189 +++++++++++++++++++++------------------- chapter5.adoc | 176 ++++++++++++++++++++++--------------- chapter7.adoc | 176 +++++++++++++++++++------------------ intro.adoc | 25 +++--- iopmp.adoc | 152 ++++++++++++++++---------------- 9 files changed, 532 insertions(+), 426 deletions(-) diff --git a/appendix_a1.adoc b/appendix_a1.adoc index d60caf2..b0ad1e8 100644 --- a/appendix_a1.adoc +++ b/appendix_a1.adoc @@ -1,4 +1,6 @@ -[Appendix_A1] -== A1: Multi-Faults Record Extension -A first violation is one that is detected and logged in the error report. However, since the error report can only accommodate one first violation, any additional violations that are detected but not logged in the error report are termed as subsequent violations. The issue at hand is that these subsequent violations become completely invisible. The Multi-Faults Record Extension is used to record which RRIDs make subsequent violations. The extension maintains a bit, referred to as SV[_s_], for each RRID _s_. When one or more subsequent violations are issued from an RRID, the corresponding bit is set. -To retrieve these SVs, a 32-bit register *ERR_MFR* is used. Every 16 contiguous SVs are grouped together into a record window, which is indexed by a 12-bit field, *svi*. When *ERR_MFR* is read, the *svi* sequentially scans all windows from its original position until a violation is found. Once *svi* is overflowed, it rounds to zero. If found, the status bit *svs* is set, and *svi* stops in the window containing the first found set SV. The 16-bit field *svw* reflects the record window indexed by *svi*, where *svw[_j_]*=SV[*svi* * 32 + _j_]. After the register is read out, all bits in the record window are cleared. If not found, *svs* and *svw* return zeros and *svi* keeps the same. Moreover, the bit *svc* in the *ERR_REQINFO* indicates if any subsequent violation is in the log. +[Appendix_A1] + +== A1: Multi-Faults Record Extension + +A first violation is one that is detected and logged in the error report. However, since the error report can only accommodate one first violation, any additional violations that are detected but not logged in the error report are termed as subsequent violations. The issue at hand is that these subsequent violations become completely invisible. The Multi-Faults Record Extension is used to record which RRIDs make subsequent violations. The extension maintains a bit, referred to as SV[_s_], for each RRID _s_. When one or more subsequent violations are issued from an RRID, the corresponding bit is set. +To retrieve these SVs, a 32-bit register *ERR_MFR* is used. Every 16 contiguous SVs are grouped together into a record window, which is indexed by a 12-bit field, *svi*. When *ERR_MFR* is read, the *svi* sequentially scans all windows from its original position until a violation is found. Once *svi* is overflowed, it rounds to zero. If found, the status bit *svs* is set, and *svi* stops in the window containing the first found set SV. The 16-bit field *svw* reflects the record window indexed by *svi*, where *svw[_j_]*=SV[*svi* * 16 + _j_]. After the register is read out, all bits in the record window are cleared. If not found, *svs* and *svw* return zeros and *svi* keeps the same. Moreover, the bit *svc* in the *ERR_REQINFO* indicates if any subsequent violation is in the log. diff --git a/appendix_a2.adoc b/appendix_a2.adoc index 7264f44..121edb9 100644 --- a/appendix_a2.adoc +++ b/appendix_a2.adoc @@ -1,3 +1,4 @@ +[#APPENDIX_A2] [Appendix_A2] == A2: Run Out Memory Domains In this specification, the support is capped at 63 memory domains. However, this chapter provides pertinent recommendations for situations that necessitate a larger number of memory domains. @@ -5,7 +6,7 @@ In this specification, the support is capped at 63 memory domains. However, this *A2.1 Parallel IOPMP* -Multiple IOPMPs can be placed in parallel. A transaction should be directed to one of these IOPMPs for its check. The chosen IOPMP then determines its legality. There are two potential methods for routing the transaction: by address or by RRID. Address-based routing divides the address space into multiple disjoint sets, and a transaction is directed to the IOPMP based on its starting address. Similarly, RRID-based routing divides all possible SIDs, and a transaction is directed to the IOPMP based on its RRID. +Multiple IOPMPs can be placed in parallel. A transaction should be directed to one of these IOPMPs for its check. The chosen IOPMP then determines its legality. There are two potential methods for routing the transaction: by address or by RRID. Address-based routing divides the address space into multiple disjoint sets, and a transaction is directed to the IOPMP based on its starting address. Similarly, RRID-based routing divides all possible RRIDs, and a transaction is directed to the IOPMP based on its RRID. [NOTE] ==== diff --git a/appendix_a3.adoc b/appendix_a3.adoc index c92016c..884b529 100644 --- a/appendix_a3.adoc +++ b/appendix_a3.adoc @@ -1,11 +1,12 @@ +[#APPENDIX_A3] [Appendix_A3] == A3: Secondary Permission Setting -IOPMP/SPS (Secondary Permission Setting) is an extension to support different sources to share memory domain while allowing each sources to have different R/W/X permission to a single memory domain. +IOPMP/SPS (Secondary Permission Setting) is an extension to support different sources to share memory domain while allowing each sources to have different read, write and intstruction permission to a single memory domain. IOPMP/SPS is only supported when the SRCMD table is in the format 0, a.k.a. *HWCFG0.srcmd_fmt*=0. *HWCFG0.sps_en*=1 indicates IOPMP/SPS extention is implemented. If IOPMP/SPS extension is implemented, each SRCMD table entry shall additionally define read and write permission registers: *SRCMD_R(_s_)* and *SRCMD_W(_s_)*, and *SRCMD_RH(_s_)* and *SRCMD_WH(_s_)* if applicable. Register *SRCMD_R(_s_)* and *SRCMD_W(_s_)* each has a single fields, *SRCMD_R(_s_).md* and *SRCMD_W(_s_).md* respectively representing the read and write permission for each memory domain for source _s_. Setting lock to *SRCMD_EN(_s_).l* also locks *SRCMD_R(_s_)*, *SRCMD_RH(_s_)*, *SRCMD_W(_s_)*, and *SRCMD_WH(_s_)*. IOPMP/SPS has two sets of permission settings: one from IOPMP entry and the other from *SRCMD_R*/*SRCMD_W*. IOPMP/SPS shall check read and write permission on both the SRCMD table and entries, a transaction fail the IOPMP/SPS check if it violates either of the permission settings. -The IOPMP/SPS register for setting instruction fetch permission on each memory domain is [TBD]. +The instruction fetch permission on IOPMP/SPS is considered the same as the read permission. diff --git a/chapter2.adoc b/chapter2.adoc index c643423..4e68da6 100644 --- a/chapter2.adoc +++ b/chapter2.adoc @@ -1,92 +1,130 @@ -[[Concepts]] -== 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. - -{set:cellbgcolor:#0000} -[cols="<1,<3",stripes=even] -|=== -2+h|Glossary/ Acronyms{set:cellbgcolor:#D3D3D3} -h|Term h|Description -|{set:cellbgcolor:#FFFFFF}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 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 bus initiator or a group of bus initiators with the same permission. When a bus initiator wants to access a piece of memory, it issues a transaction. A transaction should be tagged with an RRID to identify the issuing bus initiator. We will discuss about the exception in the next section. Tagging bus initiators 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 bus initiator 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 bus initiator or a set of bus initiators with the same permission, the Request-Role-ID can be ignored on the bus initiator side and the above transactions. In the case, we denote the IOPMP performs source enforcement, IOPMP-SE for short. - -=== Initiator Port, Receiver Port and Control Port - -An IOPMP has at least an initiator port, at least a receiver port and one control port. A receiver port is where a transaction goes into the IOPMP, and a initiator 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 source. It encompasses one or more transaction initiators 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 models 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 and the corresponding read/write permissions. - -IOPMP entry encodes the memory region in the same way as the RISC-V PMP, which are OFF, NA4, NAPOT, and TOR. Please refer to the RISC-V unprivileged spec for the details of the encoding schemes. - -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 two kinds of 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, one should avoid adopting TOR for the first entry of a memory domain. The second issue is that the memory region of the last entry is referred by the next memory domain. To avoid it, one can set an OFF for the last entry of a memory domain with the maximal address. - -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 - -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 implement-dependent. - -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, irrespective of its permission. - -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. If the entry doesn't grant it permission but suppresses to trigger the corresponding interrupt or to respond by an error, this entry is also considered a hit. If no such above entry exists, the transaction is illegal with error code = "not hit" (0x05). - -.IOPMP Block Diagram. -image::iopmp_unit_block_diagram.png[] - -=== Error Reactions -Upon detecting an illegal transaction, the IOPMP could initiate three of the following actions: - -. Trigger an interrupt to notify the system of the violation. - -. Retrun bus error (or a decode error) or not with an implementation-defined value; - -. 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 each *ENTRY_CFG* register. 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 bus error-suppressing bits in register *ENTRY_CFG(_i_)*, *sire*, *siwe*, and *sixe* to suppress interrupt triggering due to illegal reads, illegal writes and illegal instruction fetchs on the corresponding the 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_REQINFO.v* bit. An IOPMP interrupt will be triggered when: - -* interrupt_on_read = ERR_CFG.ie && !ENTRY_CFG.sire -* interrupt_on_write = ERR_CFG.ie && !ENTRY_CFG.siwe -* interrupt_on_execution = ERR_CFG.ie && !ENTRY_CFG.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. - -Similarly, the bus error response behavior can be configured per each IOPMP entry. Every entry _i_ has three optional bus error-suppressing bits in register *ENTRY_CFG(_i_)*, *sere*, *sewe*, and *sexe* to suppress bus error response due to illegal reads, illegal writes and illegal instruction fetchs on the corresponding the entry, respectively. *HWCFG0.pees* is 1 if an implementation supports *sere*, *sewe*, and *sexe*. Regardless of the value on *ERR_CFG.rs*, IOPMP will indicate a "bus error suppression" when *sere* on an entry is set to 1. The suppression behavior is also user defined. Like wise for the bus error response for a write violation or an illegal instruction fetch. - -The error capture record maintains the specifics of the first illegal access detected, except 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_REQINFO.v* is ‘0’. If there exists an pending error (*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 is implemented but not *ERR_REQID.eid*. In this case, *ERR_REQID.eid* should be wired to 0xffff. - +[[Concepts]] +== 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. + +{set:cellbgcolor:#0000} +[cols="<1,<3",stripes=even] +|=== +2+h|Glossary/ Acronyms{set:cellbgcolor:#D3D3D3} +h|Term h|Description +|{set:cellbgcolor:#FFFFFF}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 bus initiator or a group of bus initiators with the same permission. When a bus initiator wants to access a piece of memory, it issues a transaction. A transaction should be tagged with an RRID to identify the issuing bus initiator. We will discuss about the exception in the next section. Tagging bus initiators 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 bus initiator 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 bus initiator or a set of bus initiators with the same permission, the Request-Role-ID can be ignored on the bus initiator side and the above transactions. In the case, we denote the IOPMP performs source enforcement, IOPMP-SE for short. + +=== Initiator Port, Receiver Port and Control Port +An IOPMP has at least an initiator port, at least a receiver port and one control port. A receiver port is where a transaction goes into the IOPMP, and a initiator 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 source. It encompasses one or more transaction initiators 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, 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*. *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 two kinds of 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, one should avoid adopting TOR for the first entry of a memory domain. The second issue is that the memory region of the last entry is referred by the next memory domain. To avoid it, one can set an OFF for the last entry of a memory domain with the maximal address. + +*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 <<#SECTION_2_7, 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. + +[#SECTION_2_6] +=== 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 implement-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. If a priority entry is matched but doesn't grant a transaction permission to operate, the transaction is illegal with error type = "illegal read access" (0x01), "illegal write access" (0x02) or "illegal instruction fetch" (0x03), depending on its transaction type. + +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 code = "not hit any rule" (0x05). + +Finally, if no such above entry exists, the transaction is illegal with error code = "not hit any rule" (0x05). + + +.IOPMP Block Diagram. +image::iopmp_unit_block_diagram.png[] + +[#SECTION_2_7] +=== Error Reactions +Upon detecting an illegal transaction, the IOPMP could initiate three of the following actions: + +. Trigger an interrupt to notify the system of the violation. + +. Return bus error (or a decode error) or not with an implementation-defined value. + +. 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 the 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_REQINFO.v* bit. An IOPMP interrupt will be triggered when a transaction is illegal and the interrupt is not suppressed. The relation of interrupt suppression can be more precisely described as follows: + +Entries indexed by _i_~0~, _i_~1~, …​, _i~N~_ match all bytes of the transaction, and the transaction is: + +* Read access transaction: + +*ERR_CFG.ie* && ( !*ENTRY_CFG(_i_~0~).sire* || !*ENTRY_CFG(_i_~1~).sire* || ... || !*ENTRY_CFG(_i~N~_).sire* ) +* Write access transaction: + +*ERR_CFG.ie* && ( !*ENTRY_CFG(_i_~0~).siwe* || !*ENTRY_CFG(_i_~1~).siwe* || ... || *ENTRY_CFG(_i~N~_).siwe* ) +* Instruction fetch transaction: + +*ERR_CFG.ie* && ( !*ENTRY_CFG(_i_~0~).sixe* || !*ENTRY_CFG(_i_~1~).sixe* || ... || *ENTRY_CFG(_i~N~_).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. The global suppression can be overwritten by individual per-entry suppression bits, which are *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. The relation of bus error response suppression can be more precisely described as follows: + +Entries indexed by _i_~0~, _i_~1~, …​, _i~N~_ match all bytes of the transaction, and the transaction is: + +* Read access transaction: + +!*ERR_CFG.rs* && ( !*ENTRY_CFG(_i_~0~).sere* || !*ENTRY_CFG(_i_~1~).sere* || ... || !*ENTRY_CFG(_i~N~_).sere* ) +* Write access transaction: + +!*ERR_CFG.rs* && ( !*ENTRY_CFG(_i_~0~).sewe* || *ENTRY_CFG(_i_~1~).sewe* || ... || !*ENTRY_CFG(_i~N~_).sewe* ) +* Instruction fetch transaction: + +!*ERR_CFG.rs* && ! ( !*ENTRY_CFG(_i_~0~).sexe* || !*ENTRY_CFG(_i_~1~).sexe* || ... || !*ENTRY_CFG(_i~N~_).sexe* ) + +The error capture record maintains the specifics of the first illegal access detected, except 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_REQINFO.v* is ‘0’. If there exists an pending error (*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 is implemented but not *ERR_REQID.eid*. In this case, *ERR_REQID.eid* should be wired to 0xffff. + +The following table shows the error types: +|=== +2+h| Error type + |0x00 | No error + |0x01 | Illegal read access + |0x02 | Illegal write access + |0x03 | Illegal instruction fetch + |0x04 | Partial hit on a priority rule + |0x05 | Not hit any rule + |0x06 | Unknown RRID + |0x07 | User-defined error +|=== \ No newline at end of file diff --git a/chapter3.adoc b/chapter3.adoc index 8c44127..346836d 100644 --- a/chapter3.adoc +++ b/chapter3.adoc @@ -1,87 +1,102 @@ -[[IOPMP_Models_and_Configuration_Protection]] -== IOPMP Models and Configuration Protection - -The spec offers several IOPMP configuration models to accommodate varied platforms. Users can choose one of the models that best fits the use cases, including those for low area, low power, low latency, high throughput, high portability, and other criteria. - -=== Full Model - -When a Full model IOPMP receives a transaction with RRID _s_, IOPMP first lookups SRCMD table to find out all the memory domains associated to requestor _s_. An IOPMP instance can support up to 65,535 requestor, the actual number of requestor can be implementation-defined and is indicated in *HWCFG1* register. Each entry in SRCMD table defines the mapping of MDs to a specific requestor with RRID _s_. An SRCMD entry must impelment an *SRCMD_EN(_s_)* register. If SPS extension described in Appendix A3 is supported, *SRCMD_R(_s_)* and *SRCMD_W(_s_)* must be implemented. -If the number of MDs is more than 31, *SRCMD_ENH(_s_)* must be implemented, same for *SRCMD_RH(_s_)* and *SRCMD_WH(_s_)* if SPS extension is implemented. - -For easier description, *SRCMD(_s_)* is a 64-bit register representing the concatenation of *SRCMD_ENH(_s_)* for the higher word and *SRCMD_EN(_s_)* for the lower word. Field *SRCMD(_s_).md* is the concatenation of *SRCMD_ENH(_s_).mdh* and *SRCMD_EN(_s_).md*, and bit *SRCMD(_s_).l* is bit *SRCMD_EN(_s_).l*. - -Field *SRCMD(_s_).md* is a bitmapped field and has up to 63 bits. Bit *md[_j_]* in *SRCMD(_s_)* indicates if MD _j_ is associated with RRID _s_. For unimplemented memory domains, the corresponding bits should be zero. A Full model IOPMP supports up to 63 memory domains. For a system requiring more memory domains than 63, please refer to Appendix A2. - -When a transaction with RRID _s_ arrives at an IOPMP, the IOPMP retrieves all associated MDs with RRID _s_ by looking up SRCMD table. Then, by using MDCFG table, the IOPMP can obtain all entries for a MD. MDCFG table, viewed as a partition of the entries in the IOPMP, contains an array of registers. Each register in this array, denoted as *MDCFG(_m_)*, corresponds to a specific memory domain _m_. Field *MDCFG(_m_).t* indicates the top index of IOPMP entry belonging to the memory domain _m_. An IOPMP entry with index _j_ belongs to MD _m_ if *MDCFG(_m_-1).t* ≤ _j_ < *MDCFG(_m_).t*, where _m_ > 0. MD 0 owns the IOPMP entries with index _j_ < *MDCFG(0).t*. *MDCFG(_m_+1).t* should be programmed larger than or equal to *MDCFG(_m_).t*. The IOPMP behavior of improperly programming them is implementation-dependent as long as (1) an entry must belong to at most one memory domain and (2) the index of an entry in a lower-indexed memory domain should be lower than that in a higher-indexed memory domain. - -[NOTE] -==== -For programmers, to ensure the portability, MDCFG(_m_).t should be increased monotonically for _m_ during the runtime, that is, MDCFG(_m_).t ≤ MDCFG(_m_+1).t for any legal _m_. Programming or initializing MDCFGs may temporarily cause improper settings; however, these registers are typically not changed at a high frequency, and one could stall the IOPMP while programming them. Thus, the specification doesn't impose too many requirements on the case. -==== - -After retrieving all associated IOPMP entries, a Full model IOPMP checks the transaction according to these entries. - -=== Configuration Protection - -The term 'lock' refers to a hardware feature that renders one or more fields or registers nonprogrammable until the IOPMP is reset. This feature serves to maintain the integrity of essential configurations in the event of a compromise of secure software. In cases where a lock bit is programmable, it is expected to be reset to '0' and sticky to '1' upon receiving a write of '1'. - -==== SRCMD Table Protection -The associations between a specific MD _j_ and all RRIDs can be effectively locked to prevent any subsequent modifications, ensuring that *SRCMD(_s_).md[_j_]* remains nonprogrammable for all _s_. The registers MDLCK and MDLCKH are specifically to secure these associations. To lock MD _j_, one can set *MDLCK.md[_j_]* for _j_<31 or set *MDLCKH.mdh[_j_-31]* for _j_ ≥ 31. - -Bit *MDLCK.l* is a stickly to 1 and indicates if *MDLCK* is locked. - -*MDLCK.md* is optional, if not implemented, *MDLCK.md* should be wired to 0 and *MDLCK.l* should be wired to 1. - -Besides, every *SRCMD_EN(_s_)* register has a bit *l*, which is used to lock registers *SRCMD_EN(_s_)*, *SRCMD_ENH(_s_)*, *SRCMD_R(_s_)*, *SRCMD_RH(_s_)*, *SRCMD_W(_s_)*, and *SRCMD_WH(_s_)* if any. - -[NOTE] -==== -Locking SRCMD table in either way can prevent the table from being altered accidentally or maliciously. -By locking the association of the MD containing the configuration regions of a component, one can prevent the component from being configured by unwanted RRIDs. To make it more secure, one can use another high-priority MD containing the same regions but no permission, let it be associated with all unwanted RRIDs, and then lock the two MDs' associations by *MDLCK*/*MDLCKH*. By adopting this approach, it is possible to safeguard the configuration from direct access by potentially compromised security software. -==== - -==== MDCFG Table Protection -Register *MDCFGLCK* is designed to partially or fully lock MDCFG table. *MDCFGLCK* is consisted of two fields: *MDCFGLCK.l* and *MDCFGLCK.f*. *MDCFG(_j_)* is locked if _j_< *MDCFGLCK.f*. *MDCFGLCK.f* is incremental-only. Any smaller value can not be written into it. Bit *MDCFGLCK.l* is used to lock *MDCFGLCK*. - -[NOTE] -==== -If a MD is locked, while its preceding MD is not locked, it could lead to the potential addition or removal of unexpected entries within the locked MD. This can occur by manipulating the top index of the preceding unlocked MD. Thus, the specification asks that one MD is locked, all its preceding MDs should be locked. -==== - -==== Entry Protection -IOPMP entry protection is also related to the other IOPMP entries belonging to the same memory domain. For a MD, locked entries should be placed in the higher priority. Otherwise, when the secure monitor is compromised, one unlocked entry in higher priority can overwrite all the other locked or non-locked entries in lower priority. A register *ENTRYLCK* is define to indicate the number of nonprogrammable entries. *ENTRYLCK* register has two fields: *ENTRYLCK.l* and *ENTRYLCK.f*. Any IOPMP entry with index _i_ ≤ *ENTRYLCK.f* is not programmable. *ENTRYLCK.f* is initialized to 0 and can be increased only when written. Besides, *ENTRYLCK.l* is the lock to *ENTRYLCK.f* and itself. If *ENTRYLCK* is hardwired, *ENTRYLCK.l* should be wired to 1. - -=== Other IOPMP Models -==== Tables Reduction -Full model comprises two tables and an array, offering substantial flexibility for configuring an IOPMP. However, this comes at the cost of increased latency and area usage. The chapter presents the other models designed to simplify these tables, thereby catering to diverse design requirements. - -The IOPMP array functions as the primary repository for IOPMP entries with it's size adjustable. Sharing memory domains among RRIDs can result in shared entries between them. This sharing approach has the potential to decrease the overall footprint of the IOPMP array. Nevertheless, if a design doesn't encompass many shared regions, simplifying the SRCMD table, as demonstrated in the isolation and compact-_k_ models, could be a viable consideration. - -As to MDCFG table, its primary function is to partition the entries within the IOPMP. Besides programming each *MDCFG(_m_).t* for every MD _m_, an alternative approach involves evenly distributing entries across each MD. This distribution method is implemented in the Rapid-_k_, dynamic-_k_, and compact-_k_ models. - -==== Rapid-k Model -The Rapid-_k_ model is based on Full model and aims to reduce latency by eliminating the need to lookup the MDCFG table. In this model, each memory domain has exactly _k_ entries where _k_ is implementation-dependent and non-programmable. Only *MDCFG(0)* is utilized, rendering the implementation of MDCFG(j) unnecessary for j greater than 0. *MDCFG(0).t* store the value _k_, while *MDCFGLCK.f* is ignored and *MDCFGLCK.l* should be 1. - -==== Dynamic-k Model -Dynamic-_k_ model is based on rapid-_k_ model, except the _k_ value is programmable. That is, *MDCFG(0).t* is WARL and accepts a limited set of values. *MDCFGLCK.f* is ignored, and *MDCFGLCK.l* indicates if *MDCFG(0).t* is still programmable or locked. - -==== Isolation Model -The bitmap implementation of SRCMD table facilitates the sharing of regions between RRIDs. The Isolation model is specifically tailored for scenarios where there are minimal to no shared regions. In this model, each RRID is exactly associated with a single MD, eliminating the necessity for SRCMD table lookups. Each RRID _i_ is directly associated with MD _i_, resulting in advantages in terms of area, latency, and system complexity. However, duplication of entries occurs when shared regions are required, representing a potential drawback. In this model, SRCMD table and *MDLCK(H)* registers are omitted. - -The number of RRIDs to support is bounded by the maximal number of MDs, 63. - -There is no constraint imposed on MDCFG table and *MDCFGLCK* register. - -==== Compact-k Model -The Compact-_k_ model, being the most compact variant, is based on the Isolation model. It mandates that each MD has exactly _k_ entries with _k_ being non-programmable. Only *MDCFG(0)* is implemented. *MDCFG(0).t* holds the value _k_, *MDCFGLCK.f* is ignored and *MDCFGLCK.l* is 1. - -==== Model Detections - -To distinguish the above models, the user can read register *HWCFG0.model* to determine the current implemented IOPMP model. - -==== Prelocked Configurations - -Prelocked configurations in the specification mean the fields or registers are locked right after reset. They are not programmable at all. In practice, they could be hardwired and/or implemented by read-only memory. The obvious benefits are saving gate counts, no programming mistakes, and no malicious modification. Every lock mechanism in this chapter can be optionally pre-locked. -The non-zero reset value of *MDCFGLCK.f* reflects the pre-locked *MDCFG(_j_)*, where _j_< *MDCFGLCK.f*. The non-zero reset value of *ENTRYLCK.f* reflects the existing pre-locked entries. *SRCMD_EN/R/W(H)* can have prelocked bits fully or partially based on presets of *MDLCK.md* and *SRCMD_EN.l*. -The rest of the lock bits can be preset, too. They are *ERR_CFG.l*, *MDLCK.l*, *MDCFGLCK.l*, and *ENTRYLCK.l*. - +[[IOPMP_Tables_and_Configuration_Protection]] +== IOPMP Tables and Configuration Protection +The spec offers several IOPMP configurations to accommodate varied platforms. Users can choose one that best fits their design requirements, such as area, power consumption, latency, throughput, flexibility, and portability. + +[#SECTION_3_1] +=== SRCMD Table and MDCFG Table +When a IOPMP receives a transaction with RRID _s_, IOPMP first lookups SRCMD table to find out all the memory domains associated to requestor _s_. An IOPMP instance can support up to 65,535 requestors, the actual number of requestor can be implementation-defined and is indicated in *HWCFG1* register. The SRCMD table defines the mapping from a transaction requestor to its associated MDs. The mapping can be in one of three formats specified in *HWCFG0.srcmd_fmt* and described in <<#SECTION_3_2, SRCMD Table Formats>>. + +When the associated memory domains are retrieved, an IOPMP finds the entries. The MDCFG defines which memory domain every entry belongs to. An entry can belong to at most one memory domain, but a memory domain can own zero or more entries. MDCFG can be in one of three formats, specified in *HWCFG0.mdcfg_fmt* and described in <<#SECTION_3_3, MDCFG Table Formats>>. + +After retrieving all related entries, an IOPMP checks the transaction according to them. + +[#SECTION_3_2] +=== SRCMD Table Formats +Format 0 and 2 of the SRCMD Table use a bit map to associate every RRID with memory domains. Format 1 is a simplified version that uses the RRID as the memory domain index. That is, the RRIDs and MD indexes are one-to-one mapping. + +[#SECTION_3_2_1] +==== SRCMD Table Format 0 +In Format 0, the SRCMD table is an array and indexed by RRID. Every entry in the array is a set of registers. The register *SRCMD_EN(_s_)* must be implemented for the existing RRID _s_. If SPS extension described in <<#APPENDIX_A3, Appendix A3>> is supported, *SRCMD_R(_s_)* and *SRCMD_W(_s_)* are implemented. Every bit in the 31-bit field *SRCMD_EN(_s_).md* indicates a memory domain. If the number of MDs is more than 31, the register *SRCMD_ENH(_s_)* and the field *SRCMD_ENH(_s_).mdh* can be implemented to support up to 63 memory domains. On the other hand, SPS also includes the registers *SRCMD_RH(_s_)* and *SRCMD_WH(_s_)*, and the fields *SRCMD_RH(_s_).mdh* and *SRCMD_WH(_s_).mdh*. Besides, the bit *SRCMD_EN(_s_).l* is used to lock the set of registers indexed by RRID _s_. + +For easier description, in the following, we denote *SRCMD(_s_)* a 64-bit register representing the concatenation of *SRCMD_ENH(_s_)* for the higher word and *SRCMD_EN(_s_)* for the lower word. Field *SRCMD(_s_).md* is the concatenation of *SRCMD_ENH(_s_).mdh* and *SRCMD_EN(_s_).md*, and bit *SRCMD(_s_).l* is bit *SRCMD_EN(_s_).l*. + +Field *SRCMD(_s_).md* is a bitmapped field with up to 63 bits and is implemented from the lowest bit. Bit *md[_j_]* in *SRCMD(_s_)* indicates if MD _j_ is associated with RRID _s_. For unimplemented memory domains, the corresponding bits should be read-only zero. An IOPMP supports up to 63 memory domains. For a system requiring more memory domains than 63, please refer to <<#APPENDIX_A2, Appendix A2>>. + +[#SECTION_3_2_2] +==== SRCMD Table Format 1 +The bitmap implementation of Format 1 facilitates the sharing of regions between RRIDs. It is specifically tailored for scenarios where there are minimal to no shared regions. In this format, each RRID is exactly associated with a single MD, eliminating the necessity for SRCMD table lookups. Each RRID _i_ is directly associated with MD _i_, resulting in advantages in terms of area, latency, and system complexity. However, duplication of entries occurs when shared regions are required, representing a potential drawback. In the format, the SRCMD table and *MDLCK(H)* registers are not implemented. Thus, the SPS extension is not supported as well. + +[#SECTION_3_2_3] +==== SRCMD Table Format 2 +This format also has a physical SRCMD table of an array, like Format 0, but the array is indexed by the memory domain index. For MD _i_, *SRCMD_PERM(_i_)* and *SRCMD_PERMH(_i_)* are implemented at the same addresses as *SRCMD_EN(_i_)* and *SRCMD_ENH(_i_)*. That is, there is no *SRCMD_EN(H)* in the format. There is no *SRCMD_R(H)* and *SRCMD_W(H)* as well. + +*SRCMD_PERM(_m_).r[_s_]* indicates the read permission and instruction fetch permission of RRID _s_ belongs to the memory domain _m_. *SRCMD_PERM(_m_).w[_s_]* indicates write permission of RRID _s_ belongs to the memory domain _m_. *SRCMD_PERM(_m_).r* and *SRCMD_PERM(_m_).w* are adjacent. That is, *SRCMD_PERM(_m_).r[_s_]* and *SRCMD_PERM(_m_).w[_s_]* are located at the bits (_s_ * 2) and (_s_ * 2 + 1), respectively. For RRID indexed higher than 15, *SRCMD_PERMH(_m_)* is used. IOPMP looks at permissions in *SRCMD_PERM(H)* and *ENTRY_CFG.r/w/x*. As long as one of them grants the transaction under checking, it is legal. If one wants to use only *SRCMD_PERM(H)*, all *ENTRY_CFG.r/w/x* can be wired to zero. + +[#SECTION_3_3] +=== MDCFG Table Formats +The MDCFG table is used to map a memory domain to its own entries. It can be viewed as a partition of all entries in the IOPMP. Its three formats aim to fit different design targets. + +[#SECTION_3_3_1] +==== MDCFG Table Format 0 +In the format, every memory domain _m_ has a register, *MDCFG(_m_)*. Field *MDCFG(_m_).t* indicates the top index of IOPMP entry belonging to the memory domain _m_. An entry with index _j_ belongs to MD _m_ if *MDCFG(_m_-1).t* ≤ _j_ < *MDCFG(_m_).t*, where _m_ > 0. MD 0 owns the IOPMP entries with index _j_ < *MDCFG(0).t*. *MDCFG(_m_+1).t* should be programmed larger than or equal to *MDCFG(_m_).t*. The IOPMP behavior of improperly programming them is implementation-dependent as long as (1) an entry must belong to at most one memory domain and (2) the index of an entry in a lower-indexed memory domain should be lower than that in a higher-indexed memory domain. + +[NOTE] +==== +For programmers, to ensure the portability, *MDCFG(_m_).t* should be increased monotonically for _m_ during the runtime, that is, *MDCFG(_m_).t* ≤ *MDCFG(_m_+1).t* for any legal _m_. Programming or initializing MDCFGs may temporarily cause improper settings; however, these registers are typically not changed at a high frequency, and one could stall the IOPMP while programming them. Thus, the specification doesn't impose too many requirements on the case. +==== + +[#SECTION_3_3_2] +==== MDCFG Table Format 1 +There is no physical MDCFG table in the format. Each memory domain has exactly _k_ entries, where _k_ is implementation-dependent and non-programmable. The value of the read-only field *HWCFG0.md_entry_num* is the _k_ value minus 1. The register *MDCFGLCK* should not be implemented. + +For a particular case of _k_=1 (*HWCFG0.md_entry_num*=0), every memory domain has exactly one entry. One can skip the concept of the memory domain when using such an IOPMP. + +[#SECTION_3_3_3] +==== MDCFG Table Format 2 +This format is based on Format 1, except *HWCFG0.md_entry_num* is programmable. *md_entry_num* is locked when *HWCFG0* is locked, a.k.a. *HWCFG0.enable* is 1. + +[#SECTION_3_4] +=== IOPMP Models +For the sake of convenience of discussion, some highly used combinations of *HWCFG0* have an alias name, which are *srcmd_fmt*=0 and *mdcfg_fmt*=0 as the full model, *srcmd_fmt*=0 and *mdcfg_fmt*=1 as the rapid-_k_ model, where _k_ = (*md_entry_num* + 1), *srcmd_fmt*=0 and *mdcfg_fmt*=2 as the dynamic-_k_ model, where _k_ = (*md_entry_num* + 1), and *srcmd_fmt*=1 and *mdcfg_fmt*=1 as the compact-_k_ model, where _k_ = (*md_entry_num* + 1). + +[#SECTION_3_5] +=== Configuration Protection +The term 'lock' refers to a hardware feature that renders one or more fields or registers nonprogrammable until the IOPMP is reset. This feature serves to maintain the integrity of essential configurations in the event of a compromise of secure software. In cases where a lock bit is programmable, it is expected to be reset to '0' and sticky to '1' upon receiving a write of '1'. + +[#SECTION_3_5_1] +==== SRCMD Table Protection +The two fields *MDLCK.md* and *MDLCKH.mdh* have 63 bits together. Every bit is used to lock the association bits with a memory domain in the SRCMD table. In Format 0, for MD _m_≤31, *MDLCK.md[_m_]* looks *SRCMD(_s_).md[_m_]* for all existing RRID _s_. In Format 1, there is no *MDLCK*. In Format 2, *MDLCK.md[_m_]* locks both *SRCMD_PERM(_m_).r* and *SRCMD_PERM(_m_).w*. For MD _m_≥32, one should use *MDLCKH.mdh* to lock corresponding bits. + +Bit *MDLCK.l* is a sticky to 1 and indicates if *MDLCK* and *MDLCKH* are locked. + +*MDLCK.md* is optional, if not implemented, *MDLCK.md* should be wired to 0 and *MDLCK.l* should be wired to 1. *MDLCKH* is optional. + +Besides, in Format 0, every *SRCMD_EN(_s_)* register has a bit *l*, which is used to lock registers *SRCMD_EN(_s_)*, *SRCMD_ENH(_s_)*, *SRCMD_R(_s_)*, *SRCMD_RH(_s_)*, *SRCMD_W(_s_)*, and *SRCMD_WH(_s_)* if any. + +[NOTE] +==== +Locking SRCMD table in either way can prevent the table from being altered accidentally or maliciously. +By locking the association of the MD containing the configuration regions of a component, one can prevent the component from being configured by unwanted RRIDs. To make it more secure, one can use another high-priority MD containing the same regions but no permission, let it be associated with all unwanted RRIDs, and then lock the two MDs' associations by *MDLCK*/*MDLCKH*. By adopting this approach, it is possible to safeguard the configuration from direct access by potentially compromised security software. +==== + +[#SECTION_3_5_2] +==== MDCFG Table Protection +Register *MDCFGLCK* is designed to partially or fully lock the MDCFG table for Format 0. *MDCFGLCK* consists of two fields: *MDCFGLCK.l* and *MDCFGLCK.f*. *MDCFG(_m_)* is locked if _m_< *MDCFGLCK.f*. *MDCFGLCK.f* is incremental-only. Any smaller value can not be written into it. The bit *MDCFGLCK.l* is used to lock *MDCFGLCK*. + +Format 1 and 2 do not implement the register *MDCFGLCK*. + +[NOTE] +==== +If a MD is locked, while its preceding MD is not locked, it could lead to the potential addition or removal of unexpected entries within the locked MD. This can occur by manipulating the top index of the preceding unlocked MD. Thus, the specification asks that one MD is locked, all its preceding MDs should be locked. +==== + +[#SECTION_3_5_3] +==== Entry Protection +IOPMP entry protection is also related to the other IOPMP entries belonging to the same memory domain. For a MD, locked entries should be placed in the higher priority. Otherwise, when the secure monitor is compromised, one unlocked entry in higher priority can overwrite all the other locked or non-locked entries in lower priority. A register *ENTRYLCK* is define to indicate the number of nonprogrammable entries. *ENTRYLCK* register has two fields: *ENTRYLCK.l* and *ENTRYLCK.f*. Any IOPMP entry with index _i_ ≤ *ENTRYLCK.f* is not programmable. *ENTRYLCK.f* is initialized to 0 and can be increased only when written. Besides, *ENTRYLCK.l* is the lock to *ENTRYLCK.f* and itself. If *ENTRYLCK* is hardwired, *ENTRYLCK.l* should be wired to 1. + + +[#SECTION_3_6] +=== Prelocked Configurations +Prelocked configurations in the specification mean the fields or registers are locked right after reset. They are not programmable at all. In practice, they could be hardwired and/or implemented by read-only memory. The obvious benefits are saving gate counts, no programming mistakes, and no malicious modification. Every lock mechanism in this chapter can be optionally pre-locked. +The non-zero reset value of *MDCFGLCK.f* reflects the pre-locked *MDCFG(_j_)*, where _j_< *MDCFGLCK.f*. The non-zero reset value of *ENTRYLCK.f* reflects the existing pre-locked entries. *SRCMD_EN/R/W(H)* can have prelocked bits fully or partially based on presets of *MDLCK.md* and *SRCMD_EN.l*. Similarly, *SRCMD_PERM(H)* also can have prelocked bits fully or partially based on presets of *MDLCK.md*. +The rest of the lock bits can be preset, too. They are *ERR_CFG.l*, *MDLCK.l*, *MDCFGLCK.l*, and *ENTRYLCK.l*. diff --git a/chapter5.adoc b/chapter5.adoc index 636fbb2..4922386 100644 --- a/chapter5.adoc +++ b/chapter5.adoc @@ -1,5 +1,6 @@ [[Registers]] == Registers +If an optional register is not implemented, the behavior is implementation-dependent unless otherwise specified. An optional field in an implemented register means being WARL. If it is not programmable, it should be hardwired to value matching its meaning and should not cause any effect when written. [cols="<3,<6,<14",stripes=even,options="header"] |=== @@ -21,33 +22,32 @@ |{set:cellbgcolor:#FFFFFF} ENTRYLCK | Lock register for IOPMP entry array. 2+|{set:cellbgcolor:#D3D3D3} Error Reporting -|{set:cellbgcolor:#FFFFFF} ERR_CFG | Indicates the reactions for the violaions +|{set:cellbgcolor:#FFFFFF} ERR_CFG | Indicates the reactions for the violations |{set:cellbgcolor:#FFFFFF}ERR_REQINFO .3+.^| Indicate the information regarding the first captured violation. |ERR_REQID |{set:cellbgcolor:#FFFFFF} ERR_REQADDR/ERR_REQADDRH |ERR_MFR| (Optional) To retrieve which RRIDs make subsequent violations. -|ERR_USER(0~7) | (Optional) User-defined violation information. +|ERR_USER(0:7) | (Optional) User-defined violation information. -.2+|0x0800 2+|{set:cellbgcolor:#D3D3D3} MDCFG Table, _m_ =0...HWCFG0.md_num -1 +.2+|0x0800 2+|{set:cellbgcolor:#D3D3D3} MDCFG Table, _m_ = 0...*HWCFG0.md_num*-1 |{set:cellbgcolor:#FFFFFF}MDCFG(_m_) |MD config register, which is to specify the indices of IOPMP entries belonging to a MD. -.4+|0x1000 2+|{set:cellbgcolor:#D3D3D3} SRCMD Table, _s_ = 0...HWCFG1.rrid_num-1 -|{set:cellbgcolor:#FFFFFF}SRCMD_EN(_s_)/SRCMD_ENH(_s_) |The bitmapped MD enabling register of the requestor _s_ that *SRCMD_EN(_s_)[_j_]* indicates if the requestor is associated with MD _j_ and *SRCMD_ENH(_s_)[_j_]* indicates if the requestor is associated with MD (_j_+31). -|SRCMD_R(_s_)/SRCMD_RH(_s_)|(Optional) bitmapped MD read eanble register, _s_ corresponding to number of requestors, it indicates requestor _s_ read permission on MDs. -|SRCMD_W(_s_)/SRCMD_WH(_s_)|(Optional) bitmapped MD write eanble register, _s_ corresponding to number of requestors, it indicates requestor _s_ write permission on MDs. +.6+|0x1000 2+|{set:cellbgcolor:#D3D3D3} SRCMD Table, _s_ = 0...*HWCFG1.rrid_num*-1, only available when *HWCFG0.srcmd_fmt* = 0. +|{set:cellbgcolor:#FFFFFF}SRCMD_EN(_s_)/SRCMD_ENH(_s_) |The bitmapped MD enabling register of the requestor _s_ that *SRCMD_EN(_s_)[_m_]* indicates if the requestor is associated with MD _m_ and *SRCMD_ENH(_s_)[_m_]* indicates if the requestor is associated with MD (_m_+31). -.5+|ENTRYOFFSET 2+|{set:cellbgcolor:#D3D3D3} Entry Array, _i_ =0…HWCFG1.entry_num-1 -|{set:cellbgcolor:#FFFFFF}ENTRY_ADDR(_i_)|The address (region) for entry _i_. -|ENTRY_ADDRH(_i_) |(Optional for 32-bit system). +|SRCMD_R(_s_)/SRCMD_RH(_s_)|(Optional) bitmapped MD read eanble register, _s_ corresponding to number of requestors, it indicates requestor _s_ read permission on MDs. +|SRCMD_W(_s_)/SRCMD_WH(_s_)|(Optional) bitmapped MD write eanble register, _s_ corresponding to number of requestors, it indicates requestor _s_ write permission on MDs. +2+|{set:cellbgcolor:#D3D3D3} SRCMD Table, _m_ =0...*HWCFG0.md_num*-1, only available when *HWCFG0.srcmd_fmt* = 2. +|{set:cellbgcolor:#FFFFFF}SRCMD_PERM(_m_)/SRCMD_PERMH(_m_) +|The bitmapped permission register of the MD _m_. Bit 2*_s_ of *SRCMD_PERM(_m_).perm* holds the read permission for RRID _s_, and bit 2*_s_+1 of *perm* holds the write permission for RRID _s_, where _s_ ≤15. Similarly, bit 2*(_s_-16) of *SRCMD_PERMH(_m_).permh* holds the read permission for RRID _s_, and bit 2*(_s_-16)+1 of *permh* holds the write permission for RRID _s_, where _s_≥16. + +.5+|ENTRYOFFSET 2+|{set:cellbgcolor:#D3D3D3} Entry Array, _i_ =0…*HWCFG1.entry_num*-1 +|{set:cellbgcolor:#FFFFFF}ENTRY_ADDR(_i_)| Bit 33:2 of address (region) for entry _i_. +|ENTRY_ADDRH(_i_) |(Optional) bit 65:34 of the address (region) for entry _i_. |ENTRY_CFG(_i_) |The configuration of entry _i_. |ENTRY_USER_CFG(_i_) |(Optional) extension to support user customized attributes. |=== -[NOTE] -==== -If a field is optional and not implemented in an implemented register, it should return zero when read, unless otherwise specified. If an optional register is not implemented, the behavior is implementation-dependent, unless otherwise specified. -==== - === *INFO registers* INFO registers are use to indicate the IOPMP instance configuration info. @@ -57,7 +57,7 @@ INFO registers are use to indicate the IOPMP instance configuration info. 5+h|VERSION{set:cellbgcolor:#D3D3D3} 5+h|0x0000 h|Field |Bits |R/W |Default |Description -|{set:cellbgcolor:#FFFFFF}vendor|23:0 |R |IMP |The vendor ID +|{set:cellbgcolor:#FFFFFF}vendor|23:0 |R |IMP |The JEDEC manufacturer ID. |specver |31:24 |R |IMP |The specification version |=== @@ -66,7 +66,7 @@ h|Field |Bits |R/W |Default |Description 5+h|IMPLEMENTATION{set:cellbgcolor:#D3D3D3} 5+h|0x0004 h|Field |Bits |R/W |Default |Description -|{set:cellbgcolor:#FFFFFF}impid |31:0 |R |IMP |The implementation ID +|{set:cellbgcolor:#FFFFFF}impid |31:0 |R |IMP |The user-defined implementation ID. |=== [cols="<2,<1,<1,<1,<6",stripes=even] @@ -74,25 +74,35 @@ h|Field |Bits |R/W |Default |Description 5+h|HWCFG0{set:cellbgcolor:#D3D3D3} 5+h|0x0008 h|Field |Bits |R/W |Default |Description -|{set:cellbgcolor:#FFFFFF}model |3:0 |R |IMP a|Indicate the iopmp instance model +|{set:cellbgcolor:#FFFFFF}mdcfg_fmt|1:0 |R |IMP a|Indicate the MDCFG format + +* 0x0: Format 0. MDCFG table is implemented. + +* 0x1: Format 1. No MDCFG table. *HWCFG.md_entry_num* is fixed. + +* 0x2: Format 2. No MDCFG table. *HWCFG.md_entry_num* is programmable. -* 0x0: Full model: the number of MDCFG registers is equal to *HWCFG0.md_num*, all MDCFG registers are readable and writable. +* 0x3: reserved. -* 0x1: Rapid-k model: a single MDCFG register to indicate the k value, read only. +Please refer to <<#SECTION_3_3, MDCFG Table Formats>> for details. +|srcmd_fmt |3:2 |R |IMP a|Indicate the SRCMD format -* 0x2: Dynamic-k model: a single MDCFG register to indicate the k value, readable and writable. +* 0x0: Format 0. *SRCMD_EN(_s_)* and *SRCMD_ENH(_s_)* are available. -* 0x3: Isolation model: the number of MDCFG registers is equal to *HWCFG0.md_num*, all MDCFG registers are readable and writable. +* 0x1: Format 1. No SRCMD table. -* 0x4: Compact-k model: a single MDCFG register to indicate the k value, read only. +* 0x2: Format 2. *SRCMD_PERM(_m_)* and *SRCMD_PERMH(_m_)* are available. +* 0x3: reserved. + +Please refer to <<#SECTION_3_2, SRCMD Table Formats>> for details. |tor_en |4:4 |R |IMP |Indicate if TOR is supported -|sps_en |5:5 |R |IMP |Indicate secondary permission settings is supported; which are *SRCMD_R/RH(_i_)* and *SRCMD_W/WH* registers. +|sps_en |5:5 |R |IMP |Indicate secondary permission settings is supported; which are *SRCMD_R/RH(_i_)* and *SRCMD_W/WH(_i_)* registers. |user_cfg_en |6:6 |R |IMP |Indicate if user customized attributes is supported; which are *ENTRY_USER_CFG(_i_)* registers. |prient_prog |7:7 |W1CS |IMP |A write-1-clear bit is sticky to 0 and indicates if *HWCFG2.prio_entry* is programmable. Reset to 1 if the implementation supports programmable *prio_entry*, otherwise, wired to 0. |rrid_transl_en |8:8 |R |IMP |Indicate the if tagging a new RRID on the initiator port is supported |rrid_transl_prog |9:9 |W1CS |IMP |A write-1-set bit is sticky to 0 and indicate if the field rrid_transl is programmable. Support only for *rrid_transl_en*=1, otherwise, wired to 0. -|chk_x|10:10 |R | IMP| Indicate if the IOPMP implements the check of an instruction fetch. On *chk_x*=0, all fields of illegal instruction fetchs are ignored, including *HWCFG0.no_x*, *ERR_CFG.ixe*, *ERR_CFG.rxe*, *ENTRY_CFG(_i_).sixe*, *ENTRY_CFG(_i_).esxe*, and *ENTRY_CFG(_i_).x*. It should be wired to zero if there is no indication for an instruction fetch. +|chk_x|10:10 |R | IMP| Indicate if the IOPMP implements the check of an instruction fetch. On *chk_x*=0, all fields of illegal instruction fetches are ignored, including *HWCFG0.no_x*, *ENTRY_CFG(_i_).sixe*, *ENTRY_CFG(_i_).esxe*, and *ENTRY_CFG(_i_).x*. It should be wired to zero if there is no indication for an instruction fetch. |no_x|11:11 |R | IMP| For *chk_x*=1, the IOPMP with *no_x*=1 always fails on an instruction fetch; otherwise, it should depend on *x*-bit in *ENTRY_CFG(_i_)*. For *chk_x*=0, *no_x* has no effect. |no_w|12:12 |R | IMP| Indicate if the IOPMP always fails write accesses considered as as no rule matched. |stall_en|13:13 |R | IMP| Indicate if the IOPMP implements stall-related features, which are *MDSTALL*, *MDSTALLH*, and *RRIDSCP* registers. @@ -100,9 +110,16 @@ h|Field |Bits |R/W |Default |Description |pees|15:15 | R |IMP| Indicate if the IOPMP implements the error suppression per entry, including fields *esre*, *eswe*, and *esxe* in *ENTRY_CFG(_i_)*. |mfr_en|16:16 | R |IMP| Indicate if the IOPMP implements Multi Faults Record Extension, that is *ERR_MFR* and *ERR_REQINFO.svc*. -|rsv |23:16 |ZERO |0 | Must be zero on write, reserved for future -|md_num |30:24 |R |IMP |Indicate the supported number of MD in the instance -|enable |31:31 |W1SS |0 |Indicate if the IOPMP checks transactions by default. If it is implemented, it should be initial to 0 and sticky to 1. If it is not implemented, it should be wired to 1. +|md_entry_num |23:16 |WARL |IMP a| When *HWCFG0.mdcfg_fmt* = + +* 0x0: must be zero + +* 0x1 or 0x2: *md_entry_num* indicates each memory domain exactly has (*md_entry_num* + 1) entries in a memory domain + +*md_entry_num* is locked if *HWCFG0.enable* is 1. +|md_num |29:24 |R |IMP |Indicate the supported number of MD in the instance +|addrh_en |30 |R |IMP |Indicate if the IOPMP implements *ENTRY_ADDRH(_i_)* +|enable |31:31 |W1SS |0 |Indicate if the IOPMP checks transactions by default. If it is implemented, it should be initial to 0 and sticky to 1. If it is not implemented, it should be wired to 1. *HWCFG0.md_entry_num* is locked if *enable* is 1. |=== [cols="<2,<1,<1,<1,<6",stripes=even] @@ -144,7 +161,7 @@ h|Field |Bits |R/W |Default |Description |{set:cellbgcolor:#FFFFFF}exempt|0:0 |W |N/A | Stall transactions with exempt selected MDs, or Stall selected MDs. |is_stalled |0:0 |R |0 | After the last writing of *MDSTALL* (included) plus any following writing *RRIDSCP*, 1 indicates that all requested stalls take effect; otherwise, 0. After the last writing *MDSTALLH* (if any) and then *MDSTALL* by zero, 0 indicates that all transactions have been resumed; otherwise, 1. -|md |31:1 |WARL |0 |Writting *md[__i__]*=1 selects MD __i__; reading *md[__i__]* = 1 means MD __i__ selected. +|md |31:1 |WARL |0 |Writing *md[__m__]*=1 selects MD _m__; reading *md[__m__]* = 1 means MD __m__ selected. |=== [cols="<2,<1,<1,<1,<6",stripes=even] @@ -152,7 +169,7 @@ After the last writing *MDSTALLH* (if any) and then *MDSTALL* by zero, 0 indicat 5+h|MDSTALLH{set:cellbgcolor:#D3D3D3} 5+h|0x0034 h|Field |Bits |R/W |Default |Description -|{set:cellbgcolor:#FFFFFF}mdh |31:0 |WARL |0 |Writting *mdh[__i__]*=1 selects MD (__i__+31); reading *mdh[__i__]* = 1 means MD (__i__+31) selected. +|{set:cellbgcolor:#FFFFFF}mdh |31:0 |WARL |0 |Writing *mdh[__m__]*=1 selects MD (__m__+31); reading *mdh[__m__]* = 1 means MD (__m__+31) selected. |=== [cols="<2,<1,<1,<1,<6",stripes=even] @@ -176,7 +193,7 @@ h|Field |Bits |R/W |Default |Description === *Configuration Protection Registers* -*MDLCK* and *MDLCKH* are optional registers with a bitmap field to indicate which MDs are locked in SRCMD table. +*MDLCK* and *MDLCKH* are optional registers with a bitmap field to indicate which MDs are locked in the SRCMD table. [cols="<2,<1,<1,<1,<6",stripes=even] |=== @@ -184,7 +201,7 @@ h|Field |Bits |R/W |Default |Description 5+h|0x0040 h|Field |Bits |R/W |Default |Description |{set:cellbgcolor:#FFFFFF}l |0:0 |W1SS |0 | Lock bit to *MDLCK* and *MDLCKH* register. -|md |31:1 |WARL |0 | *md[__j__]* is stickly to 1 and indicates if *SRCMD_EN(__i__).md[__j__]*, *SRCMD_R(__i__).md[__j__]* and *SRCMD_W(__i__).md[__j__]* are locked for all __i__. +|md |31:1 |WARL |0 | *md[_m_]* is sticky to 1 and indicates if *SRCMD_EN(_s_).md[_m_]*, *SRCMD_R(_i_).md[_m_]* and *SRCMD_W(_s_).md[_m_]* are locked for all RRID _s_. |=== [cols="<2,<1,<1,<1,<6",stripes=even] @@ -192,19 +209,19 @@ h|Field |Bits |R/W |Default |Description 5+h|{set:cellbgcolor:#D3D3D3} MDLCKH 5+h|0x0044 h|Field |Bits |R/W |Default |Description -|{set:cellbgcolor:#FFFFFF}mdh |31:0 |WARL |0 | *mdh[__j__]* is stickly to 1 and indicates if *SRCMD_ENH(__i__).mdh[__j__]*, *SRCMD_RH(__i__).mdh[__j__]* and *SRCMD_WH(__i__).mdh[__j__]* are locked for all __i__. +|{set:cellbgcolor:#FFFFFF}mdh |31:0 |WARL |0 | *mdh[_m_]* is sticky to 1 and indicates if *SRCMD_ENH(_s_).mdh[_m_]*, *SRCMD_RH(_s_).mdh[_m_]* and *SRCMD_WH(_s_).mdh[_m_]* are locked for all RRID _s_. |=== -*MDCFGLCK* is the lock register to MDCFG table. +*MDCFGLCK* is the lock register to MDCFG table. Available only when MDCFG is in Format 0. [cols="<2,<1,<1,<1,<6",stripes=even] |=== 5+h|{set:cellbgcolor:#D3D3D3} MDCFGLCK 5+h|0x0048 h|Field |Bits |R/W |Default |Description -|{set:cellbgcolor:#FFFFFF}l |0:0 |W1SS |0 | Lock bit to *MDCFGLCK* register. For Rapid-k model and Compact-k model, *l* should be 1. For Dynamic-K model, *l* indicates if *MDCFG(0).t* is still programmable or locked. -|f |7:1 |WARL |IMP | Indicate the number of locked MDCFG entries – *MDCFG(_i_)* is locked for _i_ < *f*. -For Rapid-k model, Dynamic-k model and Compact-k model, *f* is ignored. For the rest of the models, the field should be monotonically increased only until the next reset cycle. +|{set:cellbgcolor:#FFFFFF}l |0:0 |W1SS |0 | Lock bit to *MDCFGLCK* register. +|f |7:1 |WARL |IMP | Indicate the number of locked MDCFG entries - *MDCFG(_m_)* is locked for _m_ < *f*. +The field should be monotonically increased only until the next reset cycle. |{set:cellbgcolor:#FFFFFF}rsv |31:8 |ZERO |0 | Must be zero on write, reserved for future |=== @@ -216,7 +233,7 @@ For Rapid-k model, Dynamic-k model and Compact-k model, *f* is ignored. For the 5+h|0x004C h|Field |Bits |R/W |Default |Description |{set:cellbgcolor:#FFFFFF}l |0:0 |W1SS |0 | Lock bit to *ENTRYLCK* register. -|{set:cellbgcolor:#FFFFFF}f |16:1 |WARL |IMP | Indicate the number of locked IOPMP entries – *ENTRY_ADDR(_i_)*, *ENTRY_ADDRH(_i_)*, *ENTRY_CFG(_i_)*, and *ENTRY_USER_CFG(_i_)* are locked for _i_ < *f*. The field should be monotonically increased only until the next reset cycle. +|{set:cellbgcolor:#FFFFFF}f |16:1 |WARL |IMP | Indicate the number of locked IOPMP entries - *ENTRY_ADDR(_i_)*, *ENTRY_ADDRH(_i_)*, *ENTRY_CFG(_i_)*, and *ENTRY_USER_CFG(_i_)* are locked for _i_ < *f*. The field should be monotonically increased only until the next reset cycle. |{set:cellbgcolor:#FFFFFF}rsv |31:17 |ZERO |0 | Must be zero on write, reserved for future |=== @@ -231,21 +248,23 @@ h|Field |Bits |R/W |Default |Description h|Field |Bits |R/W |Default |Description |{set:cellbgcolor:#FFFFFF}l |0:0 |W1SS |0 |Lock fields to *ERR_CFG* register |{set:cellbgcolor:#FFFFFF}ie |1:1 |RW |0 |Enable the interrupt of the IOPMP rule violation. -|{set:cellbgcolor:#FFFFFF}rs |2:2 |WARL |0 | To suppress an error response on an IOPMP rule violation. +|{set:cellbgcolor:#FFFFFF}rs |2:2 |WARL |0 a| To suppress an error response on an IOPMP rule violation. * 0x0: respond an implementation-dependent error, such as a bus error * 0x1: respond a success with a pre-defined value to the initiator instead of an error |{set:cellbgcolor:#FFFFFF}rsv |31:3 |ZERO |0 | Must be zero on write, reserved for future |=== -*ERR_REQINFO* captures more detailed error infomation. + +*ERR_REQINFO* captures more detailed error information. +[#REG_ERR_REQINFO] [cols="<2,<1,<1,<1,<6",stripes=even] |=== 5+h|{set:cellbgcolor:#D3D3D3} ERR_REQINFO 5+h|0x0064 h|Field |Bits |R/W |Default |Description -|{set:cellbgcolor:#FFFFFF}v |0:0 |R |0 | Indicate if the illegal capture recorder (*ERR_REQID*, *ERR_REQADDR*, *ERR_REQADDRH*, and fields in this register) has a valid content and will keep the content until the bit is cleared. An interrupt will be triggered if *ERR_CFG.ie* or *ERR_CFG.ire/iwe/ixe* is not disabled, the interrupt will keep asserted until the error valid is cleared. +|{set:cellbgcolor:#FFFFFF}v |0:0 |R |0 | Indicate if the illegal capture recorder (*ERR_REQID*, *ERR_REQADDR*, *ERR_REQADDRH*, and fields in this register) has a valid content and will keep the content until the bit is cleared. An interrupt will be triggered if a violation is detected and related interrupt enable/supression configure bits are not disabled, the interrupt will keep asserted until the error valid is cleared. |{set:cellbgcolor:#FFFFFF}v |0:0 |W1C |N/A | Write 1 clears the bit, the illegal recorder reactivates and the interrupt (if enabled). Write 0 causes no effect on the bit. |{set:cellbgcolor:#FFFFFF} ttype |2:1 |R |0 a|{set:cellbgcolor:#FFFFFF}Indicated the transaction type @@ -256,7 +275,7 @@ h|Field |Bits |R/W |Default |Description |{set:cellbgcolor:#FFFFFF} rsv1 |3:3 |ZERO |0 |Must be zero on write, reserved for future -|{set:cellbgcolor:#FFFFFF} etype |6:4 |R |0 a| {set:cellbgcolor:#FFFFFF}Indicated the type of violation +|{set:cellbgcolor:#FFFFFF} etype |6:4 |R |0 a| {set:cellbgcolor:#FFFFFF} Indicated the type of violation - 0x00 = no error - 0x01 = illegal read access @@ -269,7 +288,7 @@ h|Field |Bits |R/W |Default |Description |{set:cellbgcolor:#FFFFFF} svc |7:7 |R |0 |Indicate there is a subsequent violation caught in *ERR_MFR*. Implemented only for *HWCFG0.mfr_en*=1, otherwise, ZERO. -|{set:cellbgcolor:#FFFFFF} rsv2 |30:8 |ZERO |0 |Must be zero on write, reserved for future +|{set:cellbgcolor:#FFFFFF} rsv2 |31:8 |ZERO |0 |Must be zero on write, reserved for future |=== When the bus matrix doesn't have a signal to indicate an instruction fetch, the *ttype* and *etype* can never return "instruction fetch" (0x03) and "instruction fetch error" (0x03), respectively. @@ -309,14 +328,13 @@ h|Field |Bits |R/W |Default |Description |{set:cellbgcolor:#FFFFFF}svw |15:0 |R |DC | Subsequent violations in the window indexed by *svi*. *svw[_j_]*=1 for the at lease one subsequent violation issued from RRID= *svi**16 + _j_. |{set:cellbgcolor:#FFFFFF}svi |27:16 |RW |0 | Window's index to search subsequent violations. When read, *svi* moves forward until one subsequent violation is found or *svi* has been rounded back to the same value. After read, the window's content, *svw*, should be clean. |{set:cellbgcolor:#FFFFFF}rsv |30:28 |ZERO |0 |Must be zero on write, reserved for future -|{set:cellbgcolor:#FFFFFF}svs |31:31 |R |DC a| the status of this window's content: +|{set:cellbgcolor:#FFFFFF}svs |31:31 |R |DC a| The status of this window's content: * 0x0 : no subsequent violation found * 0x1 : subsequent violation found |=== - -*ERR_USER(0..7)* are optional registers to provide users to define their own error capture information. +*ERR_USER(0:7)* are optional registers to provide users to define their own error capture information. [cols="<2,<1,<1,<1,<6",stripes=even] |=== 5+h|{set:cellbgcolor:#D3D3D3} ERR_USER(_i_) @@ -325,15 +343,12 @@ h|Field |Bits |R/W |Default |Description |{set:cellbgcolor:#FFFFFF}user |31:0 |IMP |IMP a|(Optional) user-defined registers |=== - === *MDCFG Table* -MDCFG table is a lookup to specify the number of IOPMP entries that is associated with each MD. For different models: +MDCFG table is a lookup to specify the number of IOPMP entries that is associated with each MD. For different formats: + +. Format 0: MDCFG table is implemented. -. Full model: the number of MDCFG registers is equal to *HWCFG0.md_num*, all MDCFG registers are readable and writable. -. Rapid-_k_ model: a single MDCFG register to indicate the _k_ value, read only. Only *MDCFG(0)* is implemented. -. Dynamic-_k_ model: a single MDCFG register to indicate the _k_ value, readable and writable. Only *MDCFG(0)* is implemented. -. Isolation model: the number of MDCFG registers is equal to *HWCFG0.md_num*, all MDCFG registers are readable and writable. -. Compact-_k_ model: a single MDCFG register to indicate the _k_ value, read only. Only *MDCFG(0)* is implemented. +. Format 1 and format 2: No MDCFG table. [cols="<2,<1,<1,<1,<6",stripes=even] |=== @@ -344,13 +359,14 @@ h|Field |Bits |R/W |Default |Description - If *MDCFG(_m_-1).t* ≤ _j_ < *MDCFG(_m_).t,* where m>0. MD0 owns the IOPMP entries with index _j_ < *MDCFG(0).t*. - If *MDCFG(_m_-1).t* >= *MDCFG(_m_).t*, then MD _m_ is empty. - - For rapid-_k_, dynamic-_k_ and compact-_k_ models, *MDCFG(0).t* indicates the number of IOPMP entries belongs to each MD, that is, the _k_ value. The *MDCFG(_i_)* can be omitted for _i_>0. |{set:cellbgcolor:#FFFFFF}rsv |31:16 |ZERO |0 |Must be zero on write, reserved for future |=== === *SRCMD Table Registers* -Only full model, rapid-_k_ model and dynamic-_k_ model implement SRCMD table. +Format 1 does not implement the SRCMD table registers. + +*SRCMD_EN(_s_)* and *SRCMD_ENH(_s_)* are available when the SRCMD table format (*HWCFG0.srcmd_fmt*) is 0. [cols="<2,<1,<1,<1,<6",stripes=even] |=== @@ -358,7 +374,7 @@ Only full model, rapid-_k_ model and dynamic-_k_ model implement SRCMD table. 5+h|0x1000 + (_s_)*32 h|Field |Bits |R/W |Default |Description |{set:cellbgcolor:#FFFFFF}l |0:0 |W1SS |0 | A sticky lock bit. When set, locks *SRCMD_EN(_s_)*, *SRCMD_ENH(_s_)*, *SRCMD_R(_s_)*, *SRCMD_RH(_s_)*, *SRCMD_W(_s_)*, and *SRCMD_WH(_s_)* if any. -|md |31:1 |WARL |DC | *md[_j_]* = 1 indicates MD _j_ is associated with RRID _s_. +|md |31:1 |WARL |DC | *md[_m_]* = 1 indicates MD _m_ is associated with RRID _s_. |=== [cols="<2,<1,<1,<1,<6",stripes=even] @@ -366,10 +382,34 @@ h|Field |Bits |R/W |Default |Description 5+h|{set:cellbgcolor:#D3D3D3} SRCMD_ENH(_s_), _s_ = 0...HWCFG1.rrid_num-1 5+h|0x1004 + (_s_)*32 h|Field |Bits |R/W |Default |Description -|{set:cellbgcolor:#FFFFFF}mdh |31:0 |WARL |DC | *mdh[_j_]* = 1 indicates MD (_j_+31) is associated with RRID _s_. +|{set:cellbgcolor:#FFFFFF}mdh |31:0 |WARL |DC | *mdh[_m_]* = 1 indicates MD (_m_+31) is associated with RRID _s_. +|=== + +// Paul (10/8): rewritten +// TY: (10/09) +// 1. Remove r variable. +// 2. Replace character entity reference for special character (e.g., ≥, from $gt; to ≤) because asciidoctor-pdf will fail during parsing the formatted text. +// 3. Revert the width of colums. +*SRCMD_PERM(_m_)* and *SRCMD_PERMH(_m_)* are available when *HWCFG0.srcmd_fmt* = 2. +In Format 2, an IOPMP checks both the permission of *SRCMD_PERM(H)(_m_)* and the *ENTRY_CFG.r/w/x* permission. A transaction is legal if any of them allows the transaction. + +[cols="<2,<1,<1,<1,<6",stripes=even] +|=== +5+h|{set:cellbgcolor:#D3D3D3} SRCMD_PERM(_m_), _m_ = 0...HWCFG0.md_num-1 +5+h|0x1000 + (_m_)*32 +h|Field |Bits |R/W |Default |Description +|{set:cellbgcolor:#FFFFFF}perm | 31:0 | WARL | DC | Holds two bits per RRID that give the RRID’s read and write permissions for the entry. Bit 2*_s_ holds the read permission for RRID _s_, and bit 2*_s_+1 holds the write permission for RRID _s_, where _s_≤15. +|=== + +[cols="<2,<1,<1,<1,<6",stripes=even] +|=== +5+h|{set:cellbgcolor:#D3D3D3} SRCMD_PERMH(_m_), _m_ = 0...HWCFG0.md_num-1 +5+h|0x1004 + (_m_)*32 +h|Field |Bits |R/W |Default |Description +|{set:cellbgcolor:#FFFFFF}permh | 31:0 | WARL | DC | Holds two bits per RRID that give the RRID’s read and write permissions for the entry. Bit 2*(_s_-16) holds the read permission for RRID _s_, and bit 2*(_s_-16)+1 holds the write permission for RRID _s_, where _s_ ≥16. The register is implemented when *HWCFG0.num_rrid* > 16. |=== -*SRCMD_R*, *SRCMD_RH*, *SRCMD_W* and *SRCMD_WH* are optional registers; When SPS extension is enabled, the IOPMP checks both the R/W and the *ENTRY_CFG.r/w* permission and follows a fail-first rule. +*SRCMD_R*, *SRCMD_RH*, *SRCMD_W* and *SRCMD_WH* are optional registers for the SRCMD table in Format 0; When SPS extension is enabled, the IOPMP checks both the R/W/X and the *ENTRY_CFG.r/w/x* permission and follows a fail-first rule. [cols="<2,<1,<1,<1,<6",stripes=even] |=== @@ -377,7 +417,7 @@ h|Field |Bits |R/W |Default |Description 5+h|0x1008 + (_s_)*32 h|Field |Bits |R/W |Default |Description |{set:cellbgcolor:#FFFFFF}rsv |0:0 |ZERO |0|Must be zero on write, reserved for future -|{set:cellbgcolor:#FFFFFF}md |31:1 |WARL |DC | *md[_j_]* = 1 indicates RRID _s_ has read permission to the corresponding MD _j_. +|{set:cellbgcolor:#FFFFFF}md |31:1 |WARL |DC | *md[_m_]* = 1 indicates RRID _s_ has read access and instruction fetch permission to the corresponding MD _m_. |=== [cols="<2,<1,<1,<1,<6",stripes=even] @@ -385,7 +425,7 @@ h|Field |Bits |R/W |Default |Description 5+h|{set:cellbgcolor:#D3D3D3} SRCMD_RH(s), _s_ = 0...HWCFG1.rrid_num-1 5+h|0x100C + (_s_)*32 h|Field |Bits |R/W |Default |Description -|{set:cellbgcolor:#FFFFFF}mdh |31:0 |WARL |DC | *mdh[_j_]* = 1 indicates RRID _s_ has read permission to MD (_j_+31). +|{set:cellbgcolor:#FFFFFF}mdh |31:0 |WARL |DC | *mdh[_m_]* = 1 indicates RRID _s_ has read access and instruction fetch permission to MD (_m_+31). |=== [cols="<2,<1,<1,<1,<6",stripes=even] @@ -394,7 +434,7 @@ h|Field |Bits |R/W |Default |Description 5+h|0x1010 + (_s_)*32 h|Field |Bits |R/W |Default |Description |{set:cellbgcolor:#FFFFFF}rsv |0:0 |ZERO |0| Must be zero on write, reserved for future -|{set:cellbgcolor:#FFFFFF}md |31:1 |WARL |DC | *md[_j_]* = 1 indicates RRID _s_ has write permission to the corresponding MD _j_. +|{set:cellbgcolor:#FFFFFF}md |31:1 |WARL |DC | *md[_m_]* = 1 indicates RRID _s_ has write permission to the corresponding MD _m_. |=== [cols="<2,<1,<1,<1,<6",stripes=even] @@ -402,7 +442,7 @@ h|Field |Bits |R/W |Default |Description 5+h|{set:cellbgcolor:#D3D3D3} SRCMD_WH(_s_), _s_ = 0...HWCFG1.rrid_num-1 5+h|0x1014 + (_s_)*32 h|Field |Bits |R/W |Default |Description -|{set:cellbgcolor:#FFFFFF}mdh |31:0 |WARL |DC | *mdh[_j_]* = 1 indicates RRID _s_ has write permission to MD (_j_+31). +|{set:cellbgcolor:#FFFFFF}mdh |31:0 |WARL |DC | *mdh[_m_]* = 1 indicates RRID _s_ has write permission to MD (_m_+31). |=== === *Entry Array Registers* @@ -443,19 +483,19 @@ h|Field |Bits |R/W |Default |Description |{set:cellbgcolor:#FFFFFF}sixe |7:7 |WARL |0 |Suppress interrupt on an illegal instruction fetch caught by the entry -|{set:cellbgcolor:#FFFFFF}sere |8:8 |WARL |0 a| Supress the (bus) error on an illegal read access caught by the entry +|{set:cellbgcolor:#FFFFFF}sere |8:8 |WARL |0 a| Suppress the (bus) error on an illegal read access caught by the entry -* 0x0: respond an error if ERR_CFG.rs is 0x0. +* 0x0: respond an error if *ERR_CFG.rs* is 0x0. * 0x1: do not respond an error. User to define the behavior, e.g., respond a success with an implementation-dependent value to the initiator. -|{set:cellbgcolor:#FFFFFF}sewe |9:9 |WARL |0 a| Supress the (bus) error on an illegal write access caught by the entry +|{set:cellbgcolor:#FFFFFF}sewe |9:9 |WARL |0 a| Suppress the (bus) error on an illegal write access caught by the entry -* 0x0: respond an error if ERR_CFG.rs is 0x0. +* 0x0: respond an error if *ERR_CFG.rs* is 0x0. * 0x1: do not respond an error. User to define the behavior, e.g., respond a success if response is needed -|{set:cellbgcolor:#FFFFFF}sexe |10:10 |WARL |0 a| Supress the (bus) error on an illegal instruction fetch caught by the entry +|{set:cellbgcolor:#FFFFFF}sexe |10:10 |WARL |0 a| Suppress the (bus) error on an illegal instruction fetch caught by the entry -* 0x0: respond an error if ERR_CFG.rs is 0x0. +* 0x0: respond an error if *ERR_CFG.rs* is 0x0. * 0x1: do not respond an error. User to define the behavior, e.g., respond a success with an implementation-dependent value to the initiator. |{set:cellbgcolor:#FFFFFF}rsv |31:11 |ZERO |0 |Must be zero on write, reserved for future |=== diff --git a/chapter7.adoc b/chapter7.adoc index 90ae04b..228ed70 100644 --- a/chapter7.adoc +++ b/chapter7.adoc @@ -1,85 +1,91 @@ -[[Program_IOPMPs]] -:numbered: -== Programming IOPMPs -At times, it can be difficult or even impossible to configure all IOPMP settings when the system starts, especially before I/O agents are active or connected to the system. As a result, it is necessary to update IOPMP settings during runtime. This may occur when a device is enabled, disabled, added, removed, or when the accessible area of a device changes. When updating, it is important to avoid putting IOPMP in a transient metastable state due to incomplete settings. However, updating IOPMP settings often involves a series of control accesses, and if a transaction check occurs during the update, it can potentially create a vulnerability. -It can be difficult for the security software to guarantee that no transactions are in progress from all related initiators. A false alarm could result in significant performance issues. This chapter describes an optional method for updating IOPMP's settings without intervening transaction initiators. - -=== Atomicity Requirement -The term here "stable" refers to meeting the atomicity requirement. This implies that when updating an IOPMP, all transactions from input ports must be checked either before any changes or after completing all changes. Essentially, using partial settings in an IOPMP should be avoided. The succeeding sections will describe the mechanism to satisfy this requirement. - -=== Programming Steps -The general approach to the atomicity requirement has three major steps, conceptually described as follows: - -** Step 1: Stall related transactions. Before proceeding with any updates, delay checking the transactions that may be impacted. -** Step 2: Update IOPMP's settings. -** Step 3: Resume stalled transactions. - -For step 1, it's important to verify if the necessary stalling transactions have taken place since they might not be instantaneous in certain implementations. Following this, execute the IOPMP update as step 2, and finally, resume all stalled transactions in step 3. - -[NOTE] -==== -In some cases, Step 1 and Step 3 may be skipped as long as no transaction check can interrupt Step 2. Updating MDs associated with a specific RRID to other MDs is an example. -==== - -=== Stall Transactions -For Step 1, it's possible to postpone all transactions until all updates are finished. However, this could cause unrelated transactions to experience unnecessary delays. This might not be tolerable for devices that require low latency, like a display controller that periodically retrieves a frame from its video buffer. This section explains the mechanism that only stalls specific transactions to prevent the aforementioned scenario and ensure the atomicity requirement. All the features mentioned below are optional. - -Since the stalls occur when updating is in progress, determining wheater a transaction's check should wait cannot be based on any IOPMP's configuration about to change. Therefore, the only information that can be relied upon for this decision is the RRID carried by the transaction. To simplify the following description, we use a conceptual signal called rrid_stall[_i_] to indicate whether the transaction with RRID=_i_ must wait. Please note that it may not be an actual signal in practice and is not accessible directly for software. - -A conceptual internal signal rrid_stall has the same number of bits as the RRIDs in the IOPMP. rrid_stall is generated by the bit *MDSTALL.exempt*. stall_by_md is the concatenation of *MDSTALL.mdh* and *MDSTALL.md*, that is, stall_by_md[30:0] is *MDSTALL.md[31:1]* and stall_by_md[62:31] is *MDSTALLH.mdh[31:0]* if any. When *MDSTALL.exempt* is zero, any non-zero value in stall_by_md[_j_] will cause transactions with RRID=_i_ to be stalled for all RRID _i_ associated with MD _j_. On the contrary, on *MDSTALL.exempt*=1, checks of all transactions must wait except those with RRID=_i_ associated with any MD _j_ and stall_by_md[_j_] = 1. This relation can be more precisely described as follows: - -[.text-center] -`rrid_stall[_i_] <= MDSTALL.exempt ^ ( Reduction_OR (SRCMD(_i_).md & stall_by_md));` - -For any unimplemented memory domain, the corresponding bit in *MDSTALL.md* or *MDSTALLh.mdh* should be wired to 0. - -rrid_stall should be captured only when *MDSTALL.exempt* is written, that is, when *MDSTALL* is written. When *MDSTALLH* is written, the only action is to hold the value. - -NOTE: Although rrid_stall is related to SRCMD table, but should be captured only when *MDSTALL.exempt* is written. The behavior of writing *MDSTALL* is used to capture a momentary snapshot of the table because the table may not be stable during the updating. - -NOTE: When writing *MDSTALL*, the specification only defines the transactions with RRID=_i_ must wait for all rrid_stall[_i_]=1. It doesn't request the rest of the transactions to stall or not. It only asks that all transactions be resumed when writing *MDSTALL* with a zero. - -=== Cherry Pick - -If *MDSTALL* doesn't stall all the desired transactions, there is an optional method to pick the transaction with specific RRIDs. The *RRIDSCP* register comprises two fields: a 2-bit *RRIDSCP.op* and a field for *RRIDSCP.rrid*. By setting *RRIDSCP.op*=1, the rrid_stall[_i_] is activated for __i__=*RRIDSCP.rrid*. Conversely, by setting *RRIDSCP.op*=2, the rrid_stall[_i_] is deactivated for _i_=*RRIDSCP.rrid*. This register is used to fine-tune the result of writing *MDSTALL*. The value of *RRIDSCP.op*=0 is to query the rrid_stall indirectly, and the value of 3 is reserved. - -=== Resume Stall - -In order to resume all stalled transactions, the IOPMP can be prompted by writing 0 to *MDSTALL*. This corresponds to Step 3 of the "Programming Steps" section. After *MDSTALL* is written by zero, an IOPMP should de-assert *MDSTALL.is_stalled* within some time, at which point all transactions have been resumed. - -=== The Order to Stall -In Step 1 of programming IOPMP, *MDSTALL* can be written at most once and before any *RRIDSCP* is written. After a resume, writing a non-zero value to *MDSTALL* multiple times leads to an undefined situation. - -*RRIDSCP* can be written multiple times or not at all. To determine whether all requested stalls take effect, one can read back the bit *MDSTALL.is_stalled*, which is in the same location as *MDSTALL.exempt* on a write. *MDSTALL.is_stalled*=1 indicates all requested stalls taking effect after the last writing *MDSTALL* (included) plus any following writing *RRIDSCP*. - -[NOTE] -==== -After writing any non-zero value to *MDSTALL*, *MDSTALL.is_stalled* must be asserted within some time, no matter whether any RRID is stalled. The software polling the status bit doesn't need to consider whether any RRID will be stalled. On the other hand, after writing zero to *MDSTALLH* (if any) and then *MDSTALL*, *MDSTALL.is_stalled* must be de-asserted within some time. -==== - -Based on the aforementioned, complete steps to program an IOPMP are suggested. - -** Step 1.1: write MDSTALL once // exactly once -** Step 1.2: write RRIDSCP zero or more times -** Step 1.3: poll until MDSTALL.is_stalled == 1 // to ensure all stalls takes effect -** Step 2: update IOPMP's configuration -** Step 3.1: write MDSTALL=0 // resume all transactions -** Step 3.2: poll until MDSTALL.is_stalled == 0 // optional, to ensure all resumes take effect. - -Some steps may be skipped according to the actual implementation. - - -To query if all transactions associated with a specific RRID are stalled, do the following. First, write 0 to *RRIDSCP.op* and the RRID you want to query to *RRIDSCP.rrid*. Then, read back *RRIDSCP*. The readback of *RRIDSCP.stat* = 1 means that transactions with the queried RRID have stalled, that is, the corresponding bit in rrid_stall is 1. If the value is 2, it means they are not stalled. A value of 3 indicates an unimplemented or unselectable RRID in *RRIDSCP.rrid*. *RRIDSCP.stat* is in the same location as *RRIDSCP.op* on a write. *RRIDSCP.rrid* should keep the last written legal RRID and *RRIDSCP.stat* reflects the current state of this RRID. This method is considered an indirect way to read rrid_stall. - -=== Implementation Options -All registers described in this chapter are optional. Moreover, these features could be partially implemented. In *MDSTALL.md* and *MDSTALLH.mdh*, not every bit should be implemented even though the corresponding MD is implemented. An unimplemented bit means unselectable and should be wired to zero. To test which bits are implemented, one can write all 1's to *MDSTALL.md* and *MDSTALLH.mdh* and then read them back. An implemented bit returns 1. - -If an IOPMP implementation has fewer than 32 memory domains, *MDSTALLH* should be wired to zero. - -NOTE: An example of partial implementation of *MDSTALL.md*/*MDSTALLH.mdh* is a system with a display controller, which is a latency-sensitive device. On updating the IOPMP, the transactions initiated from the display controller should not be stalled. Thus, one can always use *MDSTALL.exempt*=1 and *MDSTALL.md[_j_]*=1, where MD _j_ is the memory domain for the frame buffer that the display controller keeps accessing. Thus, the system only needs to implement *MDSTALL.md[_j_]*. - -If whole *MDSTALL* is not implemented, *MDSTALL* and *MDSTALLH* should always return zero. - -If *RRIDSCP* is not implemented, it always returns zero. One can test if it is implemented by writing a zero and then reading it back. Any IOPMP implementing *RRIDSCP* should not return a zero in *RRIDSCP.stat* in this case. - -It is unnecessary to allow every implemented RRID to be selectable by *RRIDSCP.rrid*. If an unimplemented or unselectable RRID is written into *RRIDSCP.rrid*, it returns *RRIDSCP.stat* = 3. +[[Program_IOPMPs]] +:numbered: +== Programming IOPMPs +At times, it can be difficult or even impossible to configure all IOPMP settings when the system starts, especially before I/O agents are active or connected to the system. As a result, it is necessary to update IOPMP settings during runtime. This may occur when a device is enabled, disabled, added, removed, or when the accessible area of a device changes. When updating, it is important to avoid putting IOPMP in a transient metastable state due to incomplete settings. However, updating IOPMP settings often involves a series of control accesses, and if a transaction check occurs during the update, it can potentially create a vulnerability. +It can be difficult for the security software to guarantee that no transactions are in progress from all related initiators. A false alarm could result in significant performance issues. This chapter describes an optional method for updating IOPMP's settings without intervening transaction initiators. + +=== Atomicity Requirement +The term here "stable" refers to meeting the atomicity requirement. This implies that when updating an IOPMP, all transactions from input ports must be checked either before any changes or after completing all changes. Essentially, using partial settings in an IOPMP should be avoided. The succeeding sections will describe the mechanism to satisfy this requirement. + +=== Programming Steps +The general approach to the atomicity requirement has three major steps, conceptually described as follows: + +** Step 1: Stall related transactions. Before proceeding with any updates, delay checking the transactions that may be impacted. +** Step 2: Update IOPMP's settings. +** Step 3: Resume stalled transactions. + +For step 1, it's important to verify if the necessary stalling transactions have taken place since they might not be instantaneous in certain implementations. Following this, execute the IOPMP update as step 2, and finally, resume all stalled transactions in step 3. + +[NOTE] +==== +In some cases, Step 1 and Step 3 may be skipped as long as no transaction check can interrupt Step 2. Updating MDs associated with a specific RRID to other MDs is an example. +==== + +=== Stall Transactions +For Step 1, it's possible to postpone all transactions until all updates are finished. However, this could cause unrelated transactions to experience unnecessary delays. This might not be tolerable for devices that require low latency, like a display controller that periodically retrieves a frame from its video buffer. This section explains the mechanism that only stalls specific transactions to prevent the aforementioned scenario and ensure the atomicity requirement. All the features mentioned below are optional. + +Since the stalls occur when updating is in progress, determining whether a transaction's check should wait cannot be based on any IOPMP's configuration about to change. Therefore, the only information that can be relied upon for this decision is the RRID carried by the transaction. To simplify the following description, we use a conceptual signal called rrid_stall[_s_] to indicate whether the transaction with RRID=_s_ must wait. Please note that it may not be an actual signal in practice and is not accessible directly for software. + +A conceptual internal signal rrid_stall has the same number of bits as the RRIDs in the IOPMP. rrid_stall is generated by the bit *MDSTALL.exempt*. stall_by_md is the concatenation of *MDSTALL.mdh* and *MDSTALL.md*, that is, stall_by_md[30:0] is *MDSTALL.md[31:1]* and stall_by_md[62:31] is *MDSTALLH.mdh[31:0]* if any. When *MDSTALL.exempt* is zero, any non-zero value in stall_by_md[_s_] will cause transactions with RRID=_s_ to be stalled for all RRID _s_ associated with MD _m_. On the contrary, on *MDSTALL.exempt*=1, checks of all transactions must wait except those with RRID=_s_ associated with any MD _m_ and stall_by_md[_m_] = 1. This relation of SRCMD table Format 0 and Format 1 can be more precisely described as follows: + +[.text-center] +`rrid_stall[_s_] <= MDSTALL.exempt ^ ( Reduction_OR (SRCMD(_s_).md & stall_by_md));` + +As to SRCMD table Format 2, for easier description, *SRCMD_PERM(_m_)* is a conceptual 64-bit register representing the concatenation of *SRCMD_PERMH(_m_)* for the higher word and the physical 32-bit *SRCMD_PERM(_m_)* for the lower word. The conceptual *SRCMD_PERM(_m_)* is only described in this paragraph and the following equation. *SRCMD_PERM(_m_)* has two fields, *r* and *w*. *r* is at bit 2*_s_ and holds the read permission for RRID _s_. *w* is at bit 2*_s_+1 and holds the write permission for RRID _s_. This relation of format 2 can be more precisely described as follows: + +[.text-center] +`rrid_stall[_s_] <= MDSTALL.exempt ^ ( Reduction_OR ( ( SRCMD_PERM(62:0).r[_s_] | SRCMD_PERM(62:0).w[_s_]) & stall_by_md));` + + +For any unimplemented memory domain, the corresponding bit in *MDSTALL.md* or *MDSTALLH.mdh* should be wired to 0. + +rrid_stall should be captured only when *MDSTALL.exempt* is written, that is, when *MDSTALL* is written. When *MDSTALLH* is written, the only action is to hold the value. + +NOTE: Although rrid_stall is related to SRCMD table, but should be captured only when *MDSTALL.exempt* is written. The behavior of writing *MDSTALL* is used to capture a momentary snapshot of the table because the table may not be stable during the updating. + +NOTE: When writing *MDSTALL*, the specification only defines the transactions with RRID=_i_ must wait for all rrid_stall[_i_]=1. It doesn't request the rest of the transactions to stall or not. It only asks that all transactions be resumed when writing *MDSTALL* with a zero. + +=== Cherry Pick + +If *MDSTALL* doesn't stall all the desired transactions, there is an optional method to pick the transaction with specific RRIDs. The *RRIDSCP* register comprises two fields: a 2-bit *RRIDSCP.op* and a field for *RRIDSCP.rrid*. By setting *RRIDSCP.op*=1, the rrid_stall[_i_] is activated for __i__=*RRIDSCP.rrid*. Conversely, by setting *RRIDSCP.op*=2, the rrid_stall[_i_] is deactivated for _i_=*RRIDSCP.rrid*. This register is used to fine-tune the result of writing *MDSTALL*. The value of *RRIDSCP.op*=0 is to query the rrid_stall indirectly, and the value of 3 is reserved. + +=== Resume Stall + +In order to resume all stalled transactions, the IOPMP can be prompted by writing 0 to *MDSTALL*. This corresponds to Step 3 of the "Programming Steps" section. After *MDSTALL* is written by zero, an IOPMP should de-assert *MDSTALL.is_stalled* within some time, at which point all transactions have been resumed. + +=== The Order to Stall +In Step 1 of programming IOPMP, *MDSTALL* can be written at most once and before any *RRIDSCP* is written. After a resume, writing a non-zero value to *MDSTALL* multiple times leads to an undefined situation. + +*RRIDSCP* can be written multiple times or not at all. To determine whether all requested stalls take effect, one can read back the bit *MDSTALL.is_stalled*, which is in the same location as *MDSTALL.exempt* on a write. *MDSTALL.is_stalled*=1 indicates all requested stalls taking effect after the last writing *MDSTALL* (included) plus any following writing *RRIDSCP*. + +[NOTE] +==== +After writing any non-zero value to *MDSTALL*, *MDSTALL.is_stalled* must be asserted within some time, no matter whether any RRID is stalled. The software polling the status bit doesn't need to consider whether any RRID will be stalled. On the other hand, after writing zero to *MDSTALLH* (if any) and then *MDSTALL*, *MDSTALL.is_stalled* must be de-asserted within some time. +==== + +Based on the aforementioned, complete steps to program an IOPMP are suggested. + +** Step 1.1: write MDSTALL once // exactly once +** Step 1.2: write RRIDSCP zero or more times +** Step 1.3: poll until MDSTALL.is_stalled == 1 // to ensure all stalls takes effect +** Step 2: update IOPMP's configuration +** Step 3.1: write MDSTALL=0 // resume all transactions +** Step 3.2: poll until MDSTALL.is_stalled == 0 // optional, to ensure all resumes take effect. + +Some steps may be skipped according to the actual implementation. + + +To query if all transactions associated with a specific RRID are stalled, do the following. First, write 0 to *RRIDSCP.op* and the RRID you want to query to *RRIDSCP.rrid*. Then, read back *RRIDSCP*. The readback of *RRIDSCP.stat* = 1 means that transactions with the queried RRID have stalled, that is, the corresponding bit in rrid_stall is 1. If the value is 2, it means they are not stalled. A value of 3 indicates an unimplemented or unselectable RRID in *RRIDSCP.rrid*. *RRIDSCP.stat* is in the same location as *RRIDSCP.op* on a write. *RRIDSCP.rrid* should keep the last written legal RRID and *RRIDSCP.stat* reflects the current state of this RRID. This method is considered an indirect way to read rrid_stall. + +=== Implementation Options +All registers described in this chapter are optional. Moreover, these features could be partially implemented. In *MDSTALL.md* and *MDSTALLH.mdh*, not every bit should be implemented even though the corresponding MD is implemented. An unimplemented bit means unselectable and should be wired to zero. To test which bits are implemented, one can write all 1's to *MDSTALL.md* and *MDSTALLH.mdh* and then read them back. An implemented bit returns 1. + +If an IOPMP implementation has fewer than 32 memory domains, *MDSTALLH* should be wired to zero. + +NOTE: An example of partial implementation of *MDSTALL.md*/*MDSTALLH.mdh* is a system with a display controller, which is a latency-sensitive device. On updating the IOPMP, the transactions initiated from the display controller should not be stalled. Thus, one can always use *MDSTALL.exempt*=1 and *MDSTALL.md[_j_]*=1, where MD _j_ is the memory domain for the frame buffer that the display controller keeps accessing. Thus, the system only needs to implement *MDSTALL.md[_j_]*. + +If whole *MDSTALL* is not implemented, *MDSTALL* and *MDSTALLH* should always return zero. + +If *RRIDSCP* is not implemented, it always returns zero. One can test if it is implemented by writing a zero and then reading it back. Any IOPMP implementing *RRIDSCP* should not return a zero in *RRIDSCP.stat* in this case. + +It is unnecessary to allow every implemented RRID to be selectable by *RRIDSCP.rrid*. If an unimplemented or unselectable RRID is written into *RRIDSCP.rrid*, it returns *RRIDSCP.stat* = 3. diff --git a/intro.adoc b/intro.adoc index 563c67d..9dd72eb 100644 --- a/intro.adoc +++ b/intro.adoc @@ -1,11 +1,14 @@ -[[intro]] -== Introduction - -This document describes a mechanism to improve the security of a platform. In a platform, the bus initiators on it can access the target devices, just like a RISC-V hart. The introduction of I/O agents like the DMA (Direct Memory Access Unit) to systems improves performance but exposes the system to vulnerabilities such as DMA attacks. In the RISC-V eco-system, there already exists the PMP/ePMP which provides standard protection scheme for accesses from a RISC-V hart to the physical address space, but there is not a likewise standard for safeguarding non-CPU initiators. Here we propose the Physical Memory Protection Unit of Input/Output Devices, IOPMP for short, to control the accesses issued from the bus initiators. - -IOPMP is considered a hardware component in a bus fabric. But why is a pure-software solution not enough? For a RISC-V-based platform, a software solution mainly refers to the security monitor, a program running on the M-mode in charge of handling security-related requests. Once a requirement from another mode asks for a DMA transfer, for example, the security monitor checks if the requirement satisfies all the security rules and then decides whether the requirement is legal. Only a legal requirement will be performed. However, the check could take a long time when the requirement is not as simple as a DMA transfer. A GPU, for example, can take a piece of program to run. Generally, examining whether a program violates access rules is an NP-hard problem. Even though we only consider the average execution time, the latency is not tolerable in most cases. A hardware component that can check accesses on the fly becomes a reasonable solution. That is the subject of this document, IOPMP. - -.Examplary Integration of IOPMP(s) in System. -image::iopmp_system_position.png[] - - +[[intro]] +== Introduction + +This document describes a mechanism to improve the security of a platform. In a platform, the bus initiators on it can access the target devices, just like a RISC-V hart. The introduction of I/O agents like the DMA (Direct Memory Access Unit) to systems improves performance but exposes the system to vulnerabilities such as DMA attacks. In the RISC-V eco-system, there already exists the PMP/ePMP which provides standard protection scheme for accesses from a RISC-V hart to the physical address space, but there is not a likewise standard for safeguarding non-CPU initiators. Here we propose the Physical Memory Protection Unit of Input/Output Devices, IOPMP for short, to control the accesses issued from the bus initiators. + + +IOPMP is considered a hardware component in a bus fabric. But why is a pure-software solution not enough? For a RISC-V-based platform, a software solution mainly refers to the security monitor, a program running on the M-mode in charge of handling security-related requests. Once a requirement from another mode asks for a DMA transfer, for example, the security monitor checks if the requirement satisfies all the security rules and then decides whether the requirement is legal. Only a legal requirement will be performed. However, the check could take a long time when the requirement is not as simple as a DMA transfer. A GPU, for example, can take a piece of program to run. Generally, examining whether a program violates access rules is an NP-hard problem. Even though we only consider the average execution time, the latency is not tolerable in most cases. A hardware component that can check accesses on the fly becomes a reasonable solution. That is the subject of this document, IOPMP. + +Another hardware component in a bus matrix, the Input-Output Memory Management Unit (IOMMU), is used to translate a virtual address in a device to a physical address in the bus matrix by one or more stages of page tables. It could be operated in a trusted or untrusted environment and typically is a tool of a hypervisor or an OS. It provides excellent flexibility and reduces external memory fragmentation. However, it may not be an ideal tool for security software running in M-mode because it needs relatively large memory and relatively complicated software compared to a typical security monitor. Besides, the tables are usually stored in the DRAM, which is outside the chip and needs extra protection. For simple systems, this burden may not be affordable. IOPMP, however, consumes much less memory and stores rules inside the chip. Both of them have their own useful areas of application, so IOMMU and IOPMP could coexist and collaborate. IOPMP can be used as a tool for the secure monitor by checking access from devices controlled by other software, while IOMMU can be used by software with richer resources. + +.Examplary Integration of IOPMP(s) in System. +image::iopmp_system_position.png[] + + diff --git a/iopmp.adoc b/iopmp.adoc index bac4632..173251a 100644 --- a/iopmp.adoc +++ b/iopmp.adoc @@ -1,76 +1,76 @@ -[[header]] -:description: RISC-V IOPMP Architecture Specification -:company: RISC-V.org -:revdate: May, 2024 -:revnumber: 0.9.0 -:revremark: This document is in development. Assume everything can change. See http://riscv.org/spec-state for details. -:url-riscv: http://riscv.org -:doctype: book -:preface-title: Preamble -:colophon: -:appendix-caption: Appendix -:imagesdir: images -:title-logo-image: image:risc-v_logo.svg[pdfwidth=3.25in,align=center] -// Settings: -:experimental: -:reproducible: -// needs to be changed? bug discussion started -//:WaveDromEditorApp: app/wavedrom-editor.app -:imagesoutdir: images -:bibtex-file: example.bib -:bibtex-order: alphabetical -:bibtex-style: apa -:icons: font -:lang: en -:listing-caption: Listing -:sectnums: -:toc: left -:toclevels: 4 -:source-highlighter: pygments -ifdef::backend-pdf[] -:source-highlighter: coderay -endif::[] -:data-uri: -:hide-uri-scheme: -:stem: latexmath -:footnote: -:xrefstyle: short - -= RISC-V IOPMP Architecture Specification -RISC-V IOPMP Task Group - -// Preamble -[WARNING] -.This document is in the link:http://riscv.org/spec-state[Development state] -==== -Assume everything can change. This draft specification will change before -being accepted as standard, so implementations made to this draft -specification will likely not conform to the future standard. -==== - -[preface] -== Copyright and license information -This specification is licensed under the Creative Commons -Attribution 4.0 International License (CC-BY 4.0). The full -license text is available at -https://creativecommons.org/licenses/by/4.0/. - -Copyright 2024 by RISC-V International. - -[preface] -include::contributors.adoc[] - -include::intro.adoc[] -include::chapter2.adoc[] -include::chapter3.adoc[] -//include::chapter4.adoc[] -include::chapter6.adoc[] -include::chapter7.adoc[] -include::chapter5.adoc[] -include::appendix_a1.adoc[] -include::appendix_a2.adoc[] -include::appendix_a3.adoc[] - -//the index must precede the bibliography -include::index.adoc[] -include::bibliography.adoc[] +[[header]] +:description: RISC-V IOPMP Architecture Specification +:company: RISC-V.org +:revdate: Oct, 2024 +:revnumber: 0.9.2 +:revremark: This document is in development. Assume everything can change. See http://riscv.org/spec-state for details. +:url-riscv: http://riscv.org +:doctype: book +:preface-title: Preamble +:colophon: +:appendix-caption: Appendix +:imagesdir: images +:title-logo-image: image:risc-v_logo.svg[pdfwidth=3.25in,align=center] +// Settings: +:experimental: +:reproducible: +// needs to be changed? bug discussion started +//:WaveDromEditorApp: app/wavedrom-editor.app +:imagesoutdir: images +:bibtex-file: example.bib +:bibtex-order: alphabetical +:bibtex-style: apa +:icons: font +:lang: en +:listing-caption: Listing +:sectnums: +:toc: left +:toclevels: 4 +:source-highlighter: pygments +ifdef::backend-pdf[] +:source-highlighter: coderay +endif::[] +:data-uri: +:hide-uri-scheme: +:stem: latexmath +:footnote: +:xrefstyle: short + += RISC-V IOPMP Architecture Specification +RISC-V IOPMP Task Group + +// Preamble +[WARNING] +.This document is in the link:http://riscv.org/spec-state[Development state] +==== +Assume everything can change. This draft specification will change before +being accepted as standard, so implementations made to this draft +specification will likely not conform to the future standard. +==== + +[preface] +== Copyright and license information +This specification is licensed under the Creative Commons +Attribution 4.0 International License (CC-BY 4.0). The full +license text is available at +https://creativecommons.org/licenses/by/4.0/. + +Copyright 2024 by RISC-V International. + +[preface] +include::contributors.adoc[] + +include::intro.adoc[] +include::chapter2.adoc[] +include::chapter3.adoc[] +//include::chapter4.adoc[] +//include::chapter6.adoc[] +include::chapter7.adoc[] +include::chapter5.adoc[] +include::appendix_a1.adoc[] +include::appendix_a2.adoc[] +include::appendix_a3.adoc[] + +//the index must precede the bibliography +include::index.adoc[] +include::bibliography.adoc[]