#define P4D_TYPE_MASK          (_AT(p4dval_t, 3) << 0)
 #define P4D_TYPE_SECT          (_AT(p4dval_t, 1) << 0)
 #define P4D_SECT_RDONLY                (_AT(p4dval_t, 1) << 7)         /* AP[2] */
+#define P4D_TABLE_PXN          (_AT(p4dval_t, 1) << 59)
+#define P4D_TABLE_UXN          (_AT(p4dval_t, 1) << 60)
 
 /*
  * Level 1 descriptor (PUD).
 #define PUD_TYPE_MASK          (_AT(pudval_t, 3) << 0)
 #define PUD_TYPE_SECT          (_AT(pudval_t, 1) << 0)
 #define PUD_SECT_RDONLY                (_AT(pudval_t, 1) << 7)         /* AP[2] */
+#define PUD_TABLE_PXN          (_AT(pudval_t, 1) << 59)
+#define PUD_TABLE_UXN          (_AT(pudval_t, 1) << 60)
 
 /*
  * Level 2 descriptor (PMD).
 #define PMD_SECT_CONT          (_AT(pmdval_t, 1) << 52)
 #define PMD_SECT_PXN           (_AT(pmdval_t, 1) << 53)
 #define PMD_SECT_UXN           (_AT(pmdval_t, 1) << 54)
+#define PMD_TABLE_PXN          (_AT(pmdval_t, 1) << 59)
+#define PMD_TABLE_UXN          (_AT(pmdval_t, 1) << 60)
 
 /*
  * AttrIndx[2:0] encoding (mapping attributes defined in the MAIR* registers).
 
 
 #define NO_BLOCK_MAPPINGS      BIT(0)
 #define NO_CONT_MAPPINGS       BIT(1)
+#define NO_EXEC_MAPPINGS       BIT(2)  /* assumes FEAT_HPDS is not used */
 
 u64 idmap_t0sz = TCR_T0SZ(VA_BITS_MIN);
 u64 idmap_ptrs_per_pgd = PTRS_PER_PGD;
 
        BUG_ON(pmd_sect(pmd));
        if (pmd_none(pmd)) {
+               pmdval_t pmdval = PMD_TYPE_TABLE | PMD_TABLE_UXN;
                phys_addr_t pte_phys;
+
+               if (flags & NO_EXEC_MAPPINGS)
+                       pmdval |= PMD_TABLE_PXN;
                BUG_ON(!pgtable_alloc);
                pte_phys = pgtable_alloc(PAGE_SHIFT);
-               __pmd_populate(pmdp, pte_phys, PMD_TYPE_TABLE);
+               __pmd_populate(pmdp, pte_phys, pmdval);
                pmd = READ_ONCE(*pmdp);
        }
        BUG_ON(pmd_bad(pmd));
         */
        BUG_ON(pud_sect(pud));
        if (pud_none(pud)) {
+               pudval_t pudval = PUD_TYPE_TABLE | PUD_TABLE_UXN;
                phys_addr_t pmd_phys;
+
+               if (flags & NO_EXEC_MAPPINGS)
+                       pudval |= PUD_TABLE_PXN;
                BUG_ON(!pgtable_alloc);
                pmd_phys = pgtable_alloc(PMD_SHIFT);
-               __pud_populate(pudp, pmd_phys, PUD_TYPE_TABLE);
+               __pud_populate(pudp, pmd_phys, pudval);
                pud = READ_ONCE(*pudp);
        }
        BUG_ON(pud_bad(pud));
        p4d_t p4d = READ_ONCE(*p4dp);
 
        if (p4d_none(p4d)) {
+               p4dval_t p4dval = P4D_TYPE_TABLE | P4D_TABLE_UXN;
                phys_addr_t pud_phys;
+
+               if (flags & NO_EXEC_MAPPINGS)
+                       p4dval |= P4D_TABLE_PXN;
                BUG_ON(!pgtable_alloc);
                pud_phys = pgtable_alloc(PUD_SHIFT);
-               __p4d_populate(p4dp, pud_phys, P4D_TYPE_TABLE);
+               __p4d_populate(p4dp, pud_phys, p4dval);
                p4d = READ_ONCE(*p4dp);
        }
        BUG_ON(p4d_bad(p4d));
 
 static void __init map_mem(pgd_t *pgdp)
 {
+       static const u64 direct_map_end = _PAGE_END(VA_BITS_MIN);
        phys_addr_t kernel_start = __pa_symbol(_stext);
        phys_addr_t kernel_end = __pa_symbol(__init_begin);
        phys_addr_t start, end;
-       int flags = 0;
+       int flags = NO_EXEC_MAPPINGS;
        u64 i;
 
+       /*
+        * Setting hierarchical PXNTable attributes on table entries covering
+        * the linear region is only possible if it is guaranteed that no table
+        * entries at any level are being shared between the linear region and
+        * the vmalloc region. Check whether this is true for the PGD level, in
+        * which case it is guaranteed to be true for all other levels as well.
+        */
+       BUILD_BUG_ON(pgd_index(direct_map_end - 1) == pgd_index(direct_map_end));
+
        if (rodata_full || crash_mem_map || debug_pagealloc_enabled())
-               flags = NO_BLOCK_MAPPINGS | NO_CONT_MAPPINGS;
+               flags |= NO_BLOCK_MAPPINGS | NO_CONT_MAPPINGS;
 
        /*
         * Take care not to create a writable alias for the
 int arch_add_memory(int nid, u64 start, u64 size,
                    struct mhp_params *params)
 {
-       int ret, flags = 0;
+       int ret, flags = NO_EXEC_MAPPINGS;
 
        VM_BUG_ON(!mhp_range_allowed(start, size, true));
 
         */
        if (rodata_full || debug_pagealloc_enabled() ||
            IS_ENABLED(CONFIG_KFENCE))
-               flags = NO_BLOCK_MAPPINGS | NO_CONT_MAPPINGS;
+               flags |= NO_BLOCK_MAPPINGS | NO_CONT_MAPPINGS;
 
        __create_pgd_mapping(swapper_pg_dir, start, __phys_to_virt(start),
                             size, params->pgprot, __pgd_pgtable_alloc,