lib: sbi: Add support for Smepmp
authorHimanshu Chauhan <hchauhan@ventanamicro.com>
Wed, 12 Jul 2023 04:34:33 +0000 (10:04 +0530)
committerAnup Patel <anup@brainfault.org>
Thu, 13 Jul 2023 07:12:09 +0000 (12:42 +0530)
- If Smepmp is enabled, the access flags of an entry are determined based on
  truth table defined in Smepmp.
- First PMP entry (index 0) is reserved.
- Existing boot PMP entries start from index 1.
- Since enabling Smepmp revokes the access privileges of the M-mode software
  on S/U-mode region, first PMP entry is used to map/unmap the shared memory
  between M and S/U-mode. This allows a temporary access window for the M-mode
  software to read/write to S/U-mode memory region.

Signed-off-by: Himanshu Chauhan <hchauhan@ventanamicro.com>
Reviewed-by: Anup Patel <anup@brainfault.org>
include/sbi/sbi_hart.h
lib/sbi/sbi_hart.c

index 8b0e896894ec738b58af6eaa4ba0e4f2a4a3f1af..1b78343652d4c00cd33384dfebc4d8504fd0484e 100644 (file)
@@ -45,6 +45,21 @@ enum sbi_hart_extensions {
        SBI_HART_EXT_MAX,
 };
 
+/*
+ * Smepmp enforces access boundaries between M-mode and
+ * S/U-mode. When it is enabled, the PMPs are programmed
+ * such that M-mode doesn't have access to S/U-mode memory.
+ *
+ * To give M-mode R/W access to the shared memory between M and
+ * S/U-mode, first entry is reserved. It is disabled at boot.
+ * When shared memory access is required, the physical address
+ * should be programmed into the first PMP entry with R/W
+ * permissions to the M-mode. Once the work is done, it should be
+ * unmapped. sbi_hart_map_saddr/sbi_hart_unmap_saddr function
+ * pair should be used to map/unmap the shared memory.
+ */
+#define SBI_SMEPMP_RESV_ENTRY          0
+
 struct sbi_hart_features {
        bool detected;
        int priv_version;
index 4dfa1704210ebb10c0608740ddb9e0d6a081b949..12f9c7eadd1eb8e517bd2c39a95ecd873e423509 100644 (file)
@@ -284,11 +284,75 @@ unsigned int sbi_hart_mhpm_bits(struct sbi_scratch *scratch)
        return hfeatures->mhpm_bits;
 }
 
+/*
+ * Returns Smepmp flags for a given domain and region based on permissions.
+ */
+unsigned int sbi_hart_get_smepmp_flags(struct sbi_scratch *scratch,
+                                      struct sbi_domain *dom,
+                                      struct sbi_domain_memregion *reg)
+{
+       unsigned int pmp_flags = 0;
+
+       if (SBI_DOMAIN_MEMREGION_IS_SHARED(reg->flags)) {
+               /* Read only for both M and SU modes */
+               if (SBI_DOMAIN_MEMREGION_IS_SUR_MR(reg->flags))
+                       pmp_flags = (PMP_R | PMP_W | PMP_X);
+
+               /* Execute for SU but Read/Execute for M mode */
+               else if (SBI_DOMAIN_MEMREGION_IS_SUX_MRX(reg->flags))
+                       /* locked region */
+                       pmp_flags = (PMP_L | PMP_W | PMP_X);
+
+               /* Execute only for both M and SU modes */
+               else if (SBI_DOMAIN_MEMREGION_IS_SUX_MX(reg->flags))
+                       pmp_flags = (PMP_L | PMP_W);
+
+               /* Read/Write for both M and SU modes */
+               else if (SBI_DOMAIN_MEMREGION_IS_SURW_MRW(reg->flags))
+                       pmp_flags = (PMP_W | PMP_X);
+
+               /* Read only for SU mode but Read/Write for M mode */
+               else if (SBI_DOMAIN_MEMREGION_IS_SUR_MRW(reg->flags))
+                       pmp_flags = (PMP_W);
+       } else if (SBI_DOMAIN_MEMREGION_M_ONLY_ACCESS(reg->flags)) {
+               /*
+                * When smepmp is supported and used, M region cannot have RWX
+                * permissions on any region.
+                */
+               if ((reg->flags & SBI_DOMAIN_MEMREGION_M_ACCESS_MASK)
+                   == SBI_DOMAIN_MEMREGION_M_RWX) {
+                       sbi_printf("%s: M-mode only regions cannot have"
+                                  "RWX permissions\n", __func__);
+                       return 0;
+               }
+
+               /* M-mode only access regions are always locked */
+               pmp_flags |= PMP_L;
+
+               if (reg->flags & SBI_DOMAIN_MEMREGION_M_READABLE)
+                       pmp_flags |= PMP_R;
+               if (reg->flags & SBI_DOMAIN_MEMREGION_M_WRITABLE)
+                       pmp_flags |= PMP_W;
+               if (reg->flags & SBI_DOMAIN_MEMREGION_M_EXECUTABLE)
+                       pmp_flags |= PMP_X;
+       } else if (SBI_DOMAIN_MEMREGION_SU_ONLY_ACCESS(reg->flags)) {
+               if (reg->flags & SBI_DOMAIN_MEMREGION_SU_READABLE)
+                       pmp_flags |= PMP_R;
+               if (reg->flags & SBI_DOMAIN_MEMREGION_SU_WRITABLE)
+                       pmp_flags |= PMP_W;
+               if (reg->flags & SBI_DOMAIN_MEMREGION_SU_EXECUTABLE)
+                       pmp_flags |= PMP_X;
+       }
+
+       return pmp_flags;
+}
+
 int sbi_hart_pmp_configure(struct sbi_scratch *scratch)
 {
        struct sbi_domain_memregion *reg;
        struct sbi_domain *dom = sbi_domain_thishart_ptr();
-       unsigned int pmp_idx = 0, pmp_flags, pmp_bits, pmp_gran_log2;
+       unsigned int pmp_idx = 0;
+       unsigned int pmp_flags, pmp_bits, pmp_gran_log2;
        unsigned int pmp_count = sbi_hart_pmp_count(scratch);
        unsigned long pmp_addr = 0, pmp_addr_max = 0;
 
@@ -299,36 +363,68 @@ int sbi_hart_pmp_configure(struct sbi_scratch *scratch)
        pmp_bits = sbi_hart_pmp_addrbits(scratch) - 1;
        pmp_addr_max = (1UL << pmp_bits) | ((1UL << pmp_bits) - 1);
 
+       if (sbi_hart_has_extension(scratch, SBI_HART_EXT_SMEPMP)) {
+               /* Reserve first entry for dynamic shared mappings */
+                pmp_idx = SBI_SMEPMP_RESV_ENTRY + 1;
+
+               /*
+                * Set the RLB and clear MML so that, we can write to
+                * entries without enforcement even if some entries
+                * are locked.
+                */
+               csr_set(CSR_MSECCFG, MSECCFG_RLB);
+               csr_clear(CSR_MSECCFG, MSECCFG_MML);
+
+               /* Disable the reserved entry */
+               pmp_disable(SBI_SMEPMP_RESV_ENTRY);
+       }
+
        sbi_domain_for_each_memregion(dom, reg) {
                if (pmp_count <= pmp_idx)
                        break;
 
                pmp_flags = 0;
 
-               /*
-                * If permissions are to be enforced for all modes on this
-                * region, the lock bit should be set.
-                */
-               if (reg->flags & SBI_DOMAIN_MEMREGION_ENF_PERMISSIONS)
-                       pmp_flags |= PMP_L;
-
-               if (reg->flags & SBI_DOMAIN_MEMREGION_SU_READABLE)
-                       pmp_flags |= PMP_R;
-               if (reg->flags & SBI_DOMAIN_MEMREGION_SU_WRITABLE)
-                       pmp_flags |= PMP_W;
-               if (reg->flags & SBI_DOMAIN_MEMREGION_SU_EXECUTABLE)
-                       pmp_flags |= PMP_X;
+               if (sbi_hart_has_extension(scratch, SBI_HART_EXT_SMEPMP)) {
+                       pmp_flags = sbi_hart_get_smepmp_flags(scratch, dom, reg);
+
+                       if (pmp_flags == 0)
+                               return 0;
+               } else {
+                       /*
+                        * If permissions are to be enforced for all modes on
+                        * this region, the lock bit should be set.
+                        */
+                       if (reg->flags & SBI_DOMAIN_MEMREGION_ENF_PERMISSIONS)
+                               pmp_flags |= PMP_L;
+
+                       if (reg->flags & SBI_DOMAIN_MEMREGION_SU_READABLE)
+                               pmp_flags |= PMP_R;
+                       if (reg->flags & SBI_DOMAIN_MEMREGION_SU_WRITABLE)
+                               pmp_flags |= PMP_W;
+                       if (reg->flags & SBI_DOMAIN_MEMREGION_SU_EXECUTABLE)
+                               pmp_flags |= PMP_X;
+               }
 
                pmp_addr =  reg->base >> PMP_SHIFT;
-               if (pmp_gran_log2 <= reg->order && pmp_addr < pmp_addr_max)
+               if (pmp_gran_log2 <= reg->order && pmp_addr < pmp_addr_max) {
                        pmp_set(pmp_idx++, pmp_flags, reg->base, reg->order);
-               else {
-                       sbi_printf("Can not configure pmp for domain %s", dom->name);
-                       sbi_printf(" because memory region address %lx or size %lx is not in range\n",
-                                   reg->base, reg->order);
+               } else {
+                       sbi_printf("Can not configure pmp for domain %s because"
+                                  " memory region address 0x%lx or size 0x%lx "
+                                  "is not in range.\n", dom->name, reg->base,
+                                  reg->order);
                }
        }
 
+       if (sbi_hart_has_extension(scratch, SBI_HART_EXT_SMEPMP)) {
+               /*
+                * All entries are programmed. Enable MML bit.
+                * Keep the RLB bit so that dynamic mappings can be done.
+                */
+               csr_set(CSR_MSECCFG, (MSECCFG_RLB | MSECCFG_MML));
+       }
+
        /*
         * As per section 3.7.2 of privileged specification v1.12,
         * virtual address translations can be speculatively performed