diff --git a/iommu_ref_model/libiommu/include/iommu_command_queue.h b/iommu_ref_model/libiommu/include/iommu_command_queue.h index 83628c5..84ab959 100644 --- a/iommu_ref_model/libiommu/include/iommu_command_queue.h +++ b/iommu_ref_model/libiommu/include/iommu_command_queue.h @@ -29,11 +29,13 @@ typedef union { uint64_t pscv:1; uint64_t gv:1; - uint64_t rsvd1:10; + uint64_t nl:1; + uint64_t rsvd1:9; uint64_t gscid:16; uint64_t rsvd2:4; - uint64_t rsvd3:10; + uint64_t rsvd3:9; + uint64_t s:1; uint64_t addr_63_12:52; uint64_t rsvd4:2; } iotinval; @@ -94,8 +96,10 @@ typedef union { void do_inval_ddt(uint8_t DV, uint32_t DID); void do_inval_pdt(uint32_t DID, uint32_t PID); -void do_iotinval_vma(uint8_t GV, uint8_t AV, uint8_t PSCV, uint32_t GSCID, uint32_t PSCID, uint64_t ADDR); -void do_iotinval_gvma(uint8_t GV, uint8_t AV, uint32_t GSCID, uint64_t ADDR); +void do_iotinval_vma(uint8_t GV, uint8_t AV, uint8_t NL, uint8_t PSCV, uint32_t + GSCID, uint32_t PSCID, uint64_t ADDR, uint8_t S); +void do_iotinval_gvma(uint8_t GV, uint8_t AV, uint8_t NL, uint32_t GSCID, + uint64_t ADDR, uint8_t S); void do_ats_msg( uint8_t MSGCODE, uint8_t TAG, uint8_t DSV, uint8_t DSEG, uint16_t RID, uint8_t PV, uint32_t PID, uint64_t PAYLOAD); uint8_t do_iofence_c(uint8_t PR, uint8_t PW, uint8_t AV, uint8_t WIS_BIT, uint64_t ADDR, uint32_t DATA); diff --git a/iommu_ref_model/libiommu/include/iommu_registers.h b/iommu_ref_model/libiommu/include/iommu_registers.h index b0ebf5d..8e65a76 100644 --- a/iommu_ref_model/libiommu/include/iommu_registers.h +++ b/iommu_ref_model/libiommu/include/iommu_registers.h @@ -75,7 +75,9 @@ typedef union { uint64_t pd17 : 1; // Two level PDT with 17-bit process_id supported. uint64_t pd20 : 1; // Three level PDT with 20-bit process_id supported. uint64_t qosid : 1; // Associating QoS IDs with requests is supported. - uint64_t rsvd3 : 14; // Reserved for standard use + uint64_t nl : 1; // Non-leaf PTE invalidation extension is supported. + uint64_t s : 1; // Address range invalidation extension is supported. + uint64_t rsvd3 : 12; // Reserved for standard use uint64_t custom : 8; // _Designated for custom use_ }; uint64_t raw; diff --git a/iommu_ref_model/libiommu/include/iommu_utils.h b/iommu_ref_model/libiommu/include/iommu_utils.h index b952965..9af2cbb 100644 --- a/iommu_ref_model/libiommu/include/iommu_utils.h +++ b/iommu_ref_model/libiommu/include/iommu_utils.h @@ -6,5 +6,5 @@ #define __IOMMU_UTILS_H__ #define get_bits(__MS_BIT, __LS_BIT, __FIELD)\ ((__FIELD >> __LS_BIT) & (((uint64_t)1 << (((__MS_BIT - __LS_BIT) + 1))) - 1)) -extern uint8_t match_address_range( uint64_t ADDR, uint64_t PPN, uint8_t S); +extern uint8_t match_address_range( uint64_t ADDR, uint8_t ADDR_S, uint64_t PPN, uint8_t S); #endif // __IOMMU_UTILS_H__ diff --git a/iommu_ref_model/libiommu/src/iommu_atc.c b/iommu_ref_model/libiommu/src/iommu_atc.c index baacfcd..6adee25 100644 --- a/iommu_ref_model/libiommu/src/iommu_atc.c +++ b/iommu_ref_model/libiommu/src/iommu_atc.c @@ -164,7 +164,7 @@ lookup_ioatc_iotlb( if ( tlb[i].valid == 1 && tlb[i].GV == GV && tlb[i].GSCID == GSCID && tlb[i].PSCV == PSCV && tlb[i].PSCID == PSCID && - match_address_range(vpn, tlb[i].vpn, tlb[i].S) ) { + match_address_range(vpn, 0, tlb[i].vpn, tlb[i].S) ) { hit = i; break; } diff --git a/iommu_ref_model/libiommu/src/iommu_command_queue.c b/iommu_ref_model/libiommu/src/iommu_command_queue.c index cb54d43..b0a6205 100644 --- a/iommu_ref_model/libiommu/src/iommu_command_queue.c +++ b/iommu_ref_model/libiommu/src/iommu_command_queue.c @@ -90,20 +90,35 @@ process_commands( // implemented as determined by the IOMMU capabilities register. switch ( command.any.opcode ) { case IOTINVAL: + // The non-leaf PTE invalidation extension is implemented if the + // capabilities.NL (bit 42) is 1. When the capabilities.NL bit is 1, a + // non-leaf (NL) field is defined at bit 34 in the IOTINVAL.VMA and + // IOTINVAL.GVMA commands by this extension. When the capabilities.NL + // bit is 0, bit 34 remains reserved. + // The address range invalidation extension is implemented if + // `capabilities.S` (bit 43) is 1. When `capabilities.S` is 1, a + // range-size (`S`) operand is defined at bit 73 in the `IOTINVAL.VMA` and + // `IOTINVAL.GVMA` commands by this extension. When the `capabilities.S` + // bit is 0, bit 73 remains reserved. if ( command.iotinval.rsvd != 0 || command.iotinval.rsvd1 != 0 || command.iotinval.rsvd2 != 0 || command.iotinval.rsvd3 != 0 || - command.iotinval.rsvd4 != 0 ) goto command_illegal; + command.iotinval.rsvd4 != 0 || + (g_reg_file.capabilities.nl == 0 && command.iotinval.nl != 0) || + (g_reg_file.capabilities.s == 0 && command.iotinval.s != 0) ) + goto command_illegal; switch ( command.any.func3 ) { case VMA: do_iotinval_vma(command.iotinval.gv, command.iotinval.av, - command.iotinval.pscv, command.iotinval.gscid, - command.iotinval.pscid, command.iotinval.addr_63_12); + command.iotinval.nl, command.iotinval.pscv, + command.iotinval.gscid, command.iotinval.pscid, + command.iotinval.addr_63_12, command.iotinval.s); break; case GVMA: // Setting PSCV to 1 with IOTINVAL.GVMA is illegal. if ( command.iotinval.pscv != 0 ) goto command_illegal; do_iotinval_gvma(command.iotinval.gv, command.iotinval.av, - command.iotinval.gscid, command.iotinval.addr_63_12); + command.iotinval.nl, command.iotinval.gscid, + command.iotinval.addr_63_12, command.iotinval.s); break; default: goto command_illegal; } @@ -283,7 +298,8 @@ do_inval_pdt( void do_iotinval_vma( - uint8_t GV, uint8_t AV, uint8_t PSCV, uint32_t GSCID, uint32_t PSCID, uint64_t ADDR_63_12) { + uint8_t GV, uint8_t AV, uint8_t NL, uint8_t PSCV, uint32_t GSCID, + uint32_t PSCID, uint64_t ADDR_63_12, uint8_t S) { // IOMMU operations cause implicit reads to PDT, first-stage and second-stage // page tables. To reduce latency of such reads, the IOMMU may cache entries @@ -339,6 +355,21 @@ do_iotinval_vma( uint8_t i, gscid_match, pscid_match, addr_match, global_match; + // The address range invalidation extension adds the S bit. + // When the AV operand is 0, the S operand is ignored in both the IOTINVAL.VMA and + // IOTINVAL.GVMA commands. When the S operand is ignored or set to 0, the operations of + // the IOTINVAL.VMA and IOTINVAL.GVMA commands are as specified in the RISC-V IOMMU + // Version 1.0 specification. + // When the S operand is not ignored and is 1, the ADDR operand represents a NAPOT + // range encoded in the operand itself. Starting from bit position 0 of the ADDR operand, + // if the first 0 bit is at position X, the range size is 2(X+1) * 4 KiB. When X is 0, + // the size of the range is 8 KiB. If the S operand is not ignored and is 1 and all bits + // of the ADDR operand are 1, the behavior is UNSPECIFIED. + // * The model treats this unspecified behavior as matching the entire address space. + // If the S operand is not ignored and is 1 and the most significant bit of the ADDR + // operand is 0 while all other bits are 1, the specified address range covers the entire + // address space + for ( i = 0; i < TLB_SIZE; i++ ) { gscid_match = pscid_match = addr_match = global_match = 0; if ( (GV == 0 && tlb[i].GV == 0 ) || @@ -351,16 +382,44 @@ do_iotinval_vma( (PSCV == 1 && tlb[i].G == 0) ) global_match = 1; if ( (AV == 0) || - (AV == 1 && match_address_range(ADDR_63_12, tlb[i].vpn, tlb[i].S)) ) + (AV == 1 && match_address_range(ADDR_63_12, S, tlb[i].vpn, tlb[i].S)) ) addr_match = 1; if ( gscid_match && pscid_match && addr_match && global_match ) tlb[i].valid = 0; } + // This model implementation does not have non-leaf PTE caches. This + // information is for documentation only. + // * When the `AV` operand is 0, the `NL` operand is ignored and the `IOTINVAL.VMA` + // command operations are as specified in RISC-V IOMMU Version 1.0 specification. + // * When the `AV` operand is 1 and the `NL` operand is 0, the `IOTINVAL.VMA` + // command operations are as specified in RISC-V IOMMU Version 1.0 specification. + // * When both the `AV` and `NL` operands are 1, the `IOTINVAL.VMA` command + // performs the following operations: + // ** When `GV=0` and `PSCV=0`: Invalidates information cached from all levels of + // first-stage page table entries corresponding to the IOVA in the `ADDR` + // operand for all host address spaces, including entries containing global + // mappings. + // ** When `GV=0` and `PSCV=1`: Invalidates information cached from all levels of + // first-stage page table entries corresponding to the IOVA in the `ADDR` + // operand and the host address space identified by the `PSCID` operand, except + // for entries containing global mappings. + // ** When `GV=1` and `PSCV=0`: Invalidates information cached from all levels of + // first-stage page table entries corresponding to the IOVA in the `ADDR` + // operand for all VM address spaces associated with the `GSCID` operand, + // including entries that contain global mappings. + // ** When `GV=1` and `PSCV=1`: Invalidates information cached from all levels of + // first-stage page table entries corresponding to the IOVA in the `ADDR` + // operand and the VM address space identified by the `PSCID` and `GSCID` + // operands, except for entries containing global mappings. + // + // if (AV == 1 && NL == 1) { + // invalidate_vs_stage_nl_pte_caches(GV, AV, NL, PSCV, GSCID, PSCID, ADDR_63_12); + // } return; } void do_iotinval_gvma( - uint8_t GV, uint8_t AV, uint32_t GSCID, uint64_t ADDR_63_12) { + uint8_t GV, uint8_t AV, uint8_t NL, uint32_t GSCID, uint64_t ADDR_63_12, uint8_t S) { uint8_t i, gscid_match, addr_match; // Conceptually, an implementation might contain two address-translation @@ -393,6 +452,23 @@ do_iotinval_gvma( // table entries corresponding to the guest-physical-address in // `ADDR` operand, for only for VM address spaces identified // `GSCID` operand. + // + // The address range invalidation extension adds the S bit. + // When the GV operand is 0, both the AV and S operands are ignored by the + // IOTINVAL.GVMA command. + // When the AV operand is 0, the S operand is ignored in both the IOTINVAL.VMA and + // IOTINVAL.GVMA commands. When the S operand is ignored or set to 0, the operations of + // the IOTINVAL.VMA and IOTINVAL.GVMA commands are as specified in the RISC-V IOMMU + // Version 1.0 specification. + // When the S operand is not ignored and is 1, the ADDR operand represents a NAPOT + // range encoded in the operand itself. Starting from bit position 0 of the ADDR operand, + // if the first 0 bit is at position X, the range size is 2(X+1) * 4 KiB. When X is 0, + // the size of the range is 8 KiB. If the S operand is not ignored and is 1 and all bits + // of the ADDR operand are 1, the behavior is UNSPECIFIED. + // * The model treats this unspecified behavior as matching the entire address space. + // If the S operand is not ignored and is 1 and the most significant bit of the ADDR + // operand is 0 while all other bits are 1, the specified address range covers the entire + // address space for ( i = 0; i < TLB_SIZE; i++ ) { if ( tlb[i].valid == 0 ) continue; if ( (GV == 0 && tlb[i].GV == 1) || @@ -402,11 +478,31 @@ do_iotinval_gvma( // it. If PSCV is 0 then it holds a GPA. If AV is 0 then all entries are // eligible else match the address if ( (tlb[i].PSCV == 1) || (AV == 0) || - (tlb[i].PSCV == 0 && AV == 1 && match_address_range(ADDR_63_12, tlb[i].vpn, tlb[i].S)) ) + (tlb[i].PSCV == 0 && AV == 1 && match_address_range(ADDR_63_12, S, tlb[i].vpn, tlb[i].S)) ) addr_match = 1; if ( gscid_match && addr_match ) tlb[i].valid = 0; } + // This model implementation does not have non-leaf PTE caches. This + // information is for documentation only. + // * When the `GV` operand is 0, both the `AV` and `NL` operands are ignored and + // the `IOTINVAL.GVMA` command operations are as specified in RISC-V IOMMU + // Version 1.0 specification. + // * When the `GV` operand is 1 and the `AV` operand is 0, the `NL` operand is + // ignored and the `IOTINVAL.GVMA` command operations are as specified in + // RISC-V IOMMU Version 1.0 specification. + // * When the `GV` and `AV` operands are 1 and the `NL` operand is 0, the + // `IOTINVAL.GVMA` command operations are as specified in RISC-V IOMMU Version + // 1.0 specification. + // * When `GV`, `AV`, and `NL` are all 1, the `IOTINVAL.GVMA` command performs the + // following operations: + // ** Invalidates information cached from all levels of second-stage page table + // entries corresponding to the guest-physical address in the `ADDR` operand and + // the VM address spaces identified by the `GSCID` operand. + // + // if (GV == 1 && AV == 1 && NL == 1) { + // invalidate_g_stage_NL_pte_caches(GV, AV, NL, GSCID, ADDR_63_12); + // } return; } void diff --git a/iommu_ref_model/libiommu/src/iommu_utils.c b/iommu_ref_model/libiommu/src/iommu_utils.c index f2bae79..ab3d546 100644 --- a/iommu_ref_model/libiommu/src/iommu_utils.c +++ b/iommu_ref_model/libiommu/src/iommu_utils.c @@ -6,10 +6,12 @@ // Match address to a NAPOT range uint8_t match_address_range( - uint64_t VPN, uint64_t BASE_PN, uint8_t S) { + uint64_t VPN, uint8_t VPN_S, uint64_t BASE_PN, uint8_t S) { uint64_t RANGE_MASK; - + uint64_t VPN_RANGE_MASK; + VPN_RANGE_MASK = (VPN_S == 0) ? ~0UL : ~((VPN) ^ (VPN + 1)); RANGE_MASK = (S == 0) ? ~0UL : ~((BASE_PN) ^ (BASE_PN + 1)); - return ((VPN & RANGE_MASK) == (BASE_PN & RANGE_MASK)) ? 1 : 0; + return ((VPN & RANGE_MASK & VPN_RANGE_MASK) == + (BASE_PN & RANGE_MASK & VPN_RANGE_MASK)) ? 1 : 0; } diff --git a/iommu_ref_model/test/test_app.c b/iommu_ref_model/test/test_app.c index 8600ebb..b4f1a30 100644 --- a/iommu_ref_model/test/test_app.c +++ b/iommu_ref_model/test/test_app.c @@ -3702,8 +3702,10 @@ main(void) { cmd.iotinval.rsvd2 == 0 && cmd.iotinval.rsvd3 == 0 && cmd.iotinval.rsvd4 == 0 ) { - if ( temp == 0 ) + if ( temp == 0 ) { + cmd.iotinval.s = 0; cmd.iotinval.func3 = 0x7; + } if ( temp == 1 ) cmd.iotinval.rsvd = 1; if ( temp == 2 ) @@ -4323,7 +4325,6 @@ main(void) { g_reg_file.capabilities.ats = 1; write_register(ICVEC_OFFSET, 8, 0x0000000000005555); fail_if( ( read_register(ICVEC_OFFSET, 8) != 0x0000000000005555) ); - // test iommu_qosid g_reg_file.capabilities.qosid = 1; g_iommu_qosid_mask = 0x00FF00FF;