static int mmap_region_attr(mmap_region_t *mm, unsigned long base_va, unsigned long size) { int attr = mm->attr; int old_mem_type, new_mem_type; for (;;) { ++mm; if (!mm->size) return attr; /* Reached end of list */ if (mm->base_va >= base_va + size) return attr; /* Next region is after area so end */ if (mm->base_va + mm->size <= base_va) continue; /* Next region has already been overtaken */ if ((mm->attr & attr) == attr) continue; /* Region doesn't override attribs so skip */ /* * Update memory mapping attributes in 2 steps: * 1) Update access permissions and security state flags * 2) Update memory type. * * See xlat_tables.h for details about the attributes priority * system and the rules dictating whether attributes should be * updated. */ old_mem_type = MT_TYPE(attr); new_mem_type = MT_TYPE(mm->attr); attr &= mm->attr; if (new_mem_type < old_mem_type) attr = (attr & ~MT_TYPE_MASK) | new_mem_type; if (mm->base_va > base_va || mm->base_va + mm->size < base_va + size) return -1; /* Region doesn't fully cover our area */ } }
static unsigned long mmap_desc(unsigned attr, unsigned long addr_pa, unsigned level) { unsigned long desc = addr_pa; int mem_type; desc |= level == 3 ? TABLE_DESC : BLOCK_DESC; desc |= attr & MT_NS ? LOWER_ATTRS(NS) : 0; desc |= attr & MT_RW ? LOWER_ATTRS(AP_RW) : LOWER_ATTRS(AP_RO); desc |= LOWER_ATTRS(ACCESS_FLAG); mem_type = MT_TYPE(attr); if (mem_type == MT_MEMORY) { desc |= LOWER_ATTRS(ATTR_IWBWA_OWBWA_NTR_INDEX | ISH); if (attr & MT_RW) desc |= UPPER_ATTRS(XN); } else if (mem_type == MT_NON_CACHEABLE) { desc |= LOWER_ATTRS(ATTR_NON_CACHEABLE_INDEX | OSH); if (attr & MT_RW) desc |= UPPER_ATTRS(XN); } else { assert(mem_type == MT_DEVICE); desc |= LOWER_ATTRS(ATTR_DEVICE_INDEX | OSH); desc |= UPPER_ATTRS(XN); } debug_print((mem_type == MT_MEMORY) ? "MEM" : ((mem_type == MT_NON_CACHEABLE) ? "NC" : "DEV")); debug_print(attr & MT_RW ? "-RW" : "-RO"); debug_print(attr & MT_NS ? "-NS" : "-S"); return desc; }
/* * Returns a block/page table descriptor for the given level and attributes. */ uint64_t xlat_desc(const xlat_ctx_t *ctx, mmap_attr_t attr, unsigned long long addr_pa, int level) { uint64_t desc; int mem_type; /* Make sure that the granularity is fine enough to map this address. */ assert((addr_pa & XLAT_BLOCK_MASK(level)) == 0); desc = addr_pa; /* * There are different translation table descriptors for level 3 and the * rest. */ desc |= (level == XLAT_TABLE_LEVEL_MAX) ? PAGE_DESC : BLOCK_DESC; /* * Always set the access flag, as TF doesn't manage access flag faults. * Deduce other fields of the descriptor based on the MT_NS and MT_RW * memory region attributes. */ desc |= LOWER_ATTRS(ACCESS_FLAG); desc |= (attr & MT_NS) ? LOWER_ATTRS(NS) : 0; desc |= (attr & MT_RW) ? LOWER_ATTRS(AP_RW) : LOWER_ATTRS(AP_RO); /* * Do not allow unprivileged access when the mapping is for a privileged * EL. For translation regimes that do not have mappings for access for * lower exception levels, set AP[2] to AP_NO_ACCESS_UNPRIVILEGED. */ if (ctx->xlat_regime == EL1_EL0_REGIME) { if (attr & MT_USER) { /* EL0 mapping requested, so we give User access */ desc |= LOWER_ATTRS(AP_ACCESS_UNPRIVILEGED); } else { /* EL1 mapping requested, no User access granted */ desc |= LOWER_ATTRS(AP_NO_ACCESS_UNPRIVILEGED); } } else { assert(ctx->xlat_regime == EL3_REGIME); desc |= LOWER_ATTRS(AP_NO_ACCESS_UNPRIVILEGED); } /* * Deduce shareability domain and executability of the memory region * from the memory type of the attributes (MT_TYPE). * * Data accesses to device memory and non-cacheable normal memory are * coherent for all observers in the system, and correspondingly are * always treated as being Outer Shareable. Therefore, for these 2 types * of memory, it is not strictly needed to set the shareability field * in the translation tables. */ mem_type = MT_TYPE(attr); if (mem_type == MT_DEVICE) { desc |= LOWER_ATTRS(ATTR_DEVICE_INDEX | OSH); /* * Always map device memory as execute-never. * This is to avoid the possibility of a speculative instruction * fetch, which could be an issue if this memory region * corresponds to a read-sensitive peripheral. */ desc |= xlat_arch_regime_get_xn_desc(ctx->xlat_regime); } else { /* Normal memory */ /* * Always map read-write normal memory as execute-never. * (Trusted Firmware doesn't self-modify its code, therefore * R/W memory is reserved for data storage, which must not be * executable.) * Note that setting the XN bit here is for consistency only. * The function that enables the MMU sets the SCTLR_ELx.WXN bit, * which makes any writable memory region to be treated as * execute-never, regardless of the value of the XN bit in the * translation table. * * For read-only memory, rely on the MT_EXECUTE/MT_EXECUTE_NEVER * attribute to figure out the value of the XN bit. The actual * XN bit(s) to set in the descriptor depends on the context's * translation regime and the policy applied in * xlat_arch_regime_get_xn_desc(). */ if ((attr & MT_RW) || (attr & MT_EXECUTE_NEVER)) { desc |= xlat_arch_regime_get_xn_desc(ctx->xlat_regime); } if (mem_type == MT_MEMORY) { desc |= LOWER_ATTRS(ATTR_IWBWA_OWBWA_NTR_INDEX | ISH); } else { assert(mem_type == MT_NON_CACHEABLE); desc |= LOWER_ATTRS(ATTR_NON_CACHEABLE_INDEX | OSH); } } return desc; }