@@ -203,7 +203,7 @@ void ulmk_arch_csa_pool_init(uintptr_t pool_base, size_t pool_size)
203203 */
204204}
205205
206- extern void _ulmk_thread_trampoline (void );
206+ extern void ulmk_user_thread_entry (void ( * entry )( void * arg ), void * arg );
207207
208208/*
209209 * Fabricate the initial two-frame CSA chain for a new thread.
@@ -212,22 +212,24 @@ extern void _ulmk_thread_trampoline(void);
212212 * Upper context (CALL/RFE): PCXI PSW A10 A11 D8-D11 A12-A15 D12-D15
213213 * Lower context (SVLCX/RSLCX): PCXI A11 A2 A3 D0-D3 A4-A7 D4-D7
214214 *
215- * RFE restores the upper context; PC is taken from A11 pre-restore.
216- * lower_csa[1] = trampoline sets the jump target for the first RFE.
215+ * First PC after RSLCX+RFE is upper A11. That must be
216+ * ulmk_user_thread_entry in .user_runtime (CPR 1): driver/user threads run
217+ * with PSW.PRS=1 and cannot fetch from .kernel_text (CPR 0). A kernel-side
218+ * trampoline here causes class-1 MPX (tin=4) on the first instruction.
217219 *
218220 * Fabricated frames:
219221 *
220222 * Upper CSA (UL=1):
221223 * [0] PCXI = 0 (terminal)
222- * [1] PSW (privilege)
224+ * [1] PSW (privilege + PRS )
223225 * [2] A10 (stack_top)
224- * [3] A11 (trampoline — restored into A11 register by RFE)
225- * [10] A14 (entry function address — loaded by RFE into A14)
226+ * [3] A11 (ulmk_user_thread_entry — RFE PC)
226227 *
227228 * Lower CSA (UL=0):
228- * [0] PCXI (upper_link | UL_FLAG)
229- * [1] A11 (trampoline — RFE uses this for PC before restore)
230- * [8] A4 (arg pointer — restored by RSLCX, ABI-correct)
229+ * [0] PCXI (upper_link | UL_FLAG | PIE)
230+ * [1] A11 (same entry; consistent with RSLCX restore)
231+ * [8] A4 (thread entry fn — ABI arg0)
232+ * [9] A5 (thread arg — ABI arg1)
231233 *
232234 * ctx->pcxi = lower_link (UL=0).
233235 */
@@ -241,6 +243,7 @@ void ulmk_arch_ctx_init(ulmk_arch_ctx_t *ctx,
241243 uint32_t * lower_csa ;
242244 uint32_t psw ;
243245 uint32_t i ;
246+ uint32_t entry_pc ;
244247
245248 /*
246249 * Build the initial PSW from the current kernel PSW, overriding the
@@ -266,14 +269,15 @@ void ulmk_arch_ctx_init(ulmk_arch_ctx_t *ctx,
266269 if (priv != ULMK_PRIV_KERNEL )
267270 psw |= (uint32_t )ULMK_ARCH_PRS_USER << 12 ;
268271
272+ entry_pc = (uint32_t )(uintptr_t )ulmk_user_thread_entry ;
273+
269274 upper_link = csa_alloc ();
270275 upper_csa = csa_link_to_addr (upper_link );
271276 for (i = 0u ; i < 16u ; i ++ )
272277 upper_csa [i ] = 0u ;
273- upper_csa [1 ] = psw ;
274- upper_csa [2 ] = (uint32_t )stack_top ;
275- upper_csa [3 ] = (uint32_t )(uintptr_t )_ulmk_thread_trampoline ;
276- upper_csa [10 ] = (uint32_t )(uintptr_t )entry ; /* A14: entry fn */
278+ upper_csa [1 ] = psw ;
279+ upper_csa [2 ] = (uint32_t )stack_top ;
280+ upper_csa [3 ] = entry_pc ;
277281
278282 lower_link = csa_alloc ();
279283 lower_csa = csa_link_to_addr (lower_link );
@@ -285,8 +289,9 @@ void ulmk_arch_ctx_init(ulmk_arch_ctx_t *ctx,
285289 * are left disabled in every new thread, blocking timer preemption.
286290 */
287291 lower_csa [0 ] = upper_link | UL_CSA_UL_FLAG | (1u << 21 );
288- lower_csa [1 ] = (uint32_t )(uintptr_t )_ulmk_thread_trampoline ;
289- lower_csa [8 ] = (uint32_t )(uintptr_t )arg ; /* A4: pointer arg */
292+ lower_csa [1 ] = entry_pc ;
293+ lower_csa [8 ] = (uint32_t )(uintptr_t )entry ; /* A4 */
294+ lower_csa [9 ] = (uint32_t )(uintptr_t )arg ; /* A5 */
290295
291296 ctx -> pcxi = lower_link ;
292297}
@@ -493,15 +498,17 @@ static void mpu_write_cpr(uint8_t slot, uint32_t lower, uint32_t upper)
493498}
494499
495500/*
496- * Write DPRE, DPWE, CPRE, CPXE enable registers for a given PRS.
501+ * Write DPRE, DPWE, CPXE enable registers for a given PRS.
497502 * ISYNC is issued at the end to flush the pipeline.
503+ *
504+ * TC2xx/TC3xx have no separate CPRE CSFR — only CPXE at 0xE000+prs*4.
505+ * Writing a phantom "CPXE" at 0xE040 hits Safety RGNLA on TC27x.
498506 */
499507static void mpu_write_enables (uint8_t prs , uint32_t dpre , uint32_t dpwe ,
500- uint32_t cpre , uint32_t cpxe )
508+ uint32_t cpxe )
501509{
502510 mpu_mtcr (ULMK_ARCH_CSFR_DPRE_0 + (uint32_t )prs * 4u , dpre );
503511 mpu_mtcr (ULMK_ARCH_CSFR_DPWE_0 + (uint32_t )prs * 4u , dpwe );
504- mpu_mtcr (ULMK_ARCH_CSFR_CPRE_0 + (uint32_t )prs * 4u , cpre );
505512 mpu_mtcr (ULMK_ARCH_CSFR_CPXE_0 + (uint32_t )prs * 4u , cpxe );
506513 __asm__ volatile ("isync" ::: "memory" );
507514}
@@ -532,9 +539,15 @@ static uint8_t g_mpu_count;
532539static uint8_t g_mpu_prs = 0xFFu ;
533540static uint8_t g_mpu_live ;
534541
542+ /*
543+ * AURIX CPR/DPR: address belongs to the range iff
544+ * lower <= addr < upper
545+ * (`end` is the exclusive linker symbol). Bits [2:0] of the bound registers
546+ * are ignored; keep an 8-byte aligned exclusive upper.
547+ */
535548static uint32_t mpu_range_upper (uintptr_t end )
536549{
537- return (uint32_t )end - 8u ;
550+ return (uint32_t )end & ~ 7u ;
538551}
539552
540553void ulmk_arch_mpu_init (void )
@@ -549,7 +562,6 @@ void ulmk_arch_mpu_init(void)
549562 uintptr_t kram_hi ;
550563 uintptr_t uram_lo ;
551564 uintptr_t uram_hi ;
552- uint32_t prs1_cpre ;
553565 uint32_t prs1_cpxe ;
554566 uint32_t prs0_cpr ;
555567
@@ -562,10 +574,12 @@ void ulmk_arch_mpu_init(void)
562574 extern uint8_t _ulmk_user_ram_start [];
563575 extern uint8_t _ulmk_user_pool_end [];
564576
565- /* Disable protection during reconfiguration */
577+ /* Disable protection during reconfiguration (SYSCON is EndInit). */
578+ ulmk_board_cpu_endinit_clear ();
566579 __asm__ volatile ("mfcr %0, 0xFE14" : "=d" (syscon ));
567580 mpu_mtcr (ULMK_ARCH_CSFR_SYSCON , syscon & ~ULMK_ARCH_SYSCON_PROTEN );
568581 __asm__ volatile ("isync" ::: "memory" );
582+ ulmk_board_cpu_endinit_set ();
569583
570584 g_mpu_regions = NULL ;
571585 g_mpu_count = 0u ;
@@ -580,7 +594,7 @@ void ulmk_arch_mpu_init(void)
580594
581595 /* Zero all PRS enable registers */
582596 for (i = 0u ; i < ULMK_ARCH_NUM_PRS ; i ++ )
583- mpu_write_enables (i , 0u , 0u , 0u , 0u );
597+ mpu_write_enables (i , 0u , 0u , 0u );
584598
585599 kexec_lo = (uintptr_t )_ulmk_kernel_exec_start ;
586600 kexec_hi = (uintptr_t )_ulmk_kernel_exec_end ;
@@ -624,20 +638,17 @@ void ulmk_arch_mpu_init(void)
624638
625639 __asm__ volatile ("isync" ::: "memory" );
626640
627- prs1_cpre = 0u ;
628641 prs1_cpxe = 0u ;
629- if (utext_hi > utext_lo ) {
630- prs1_cpre = (1u << ULMK_ARCH_MPU_CPR_USER );
642+ if (utext_hi > utext_lo )
631643 prs1_cpxe = (1u << ULMK_ARCH_MPU_CPR_USER );
632- }
633644
634645 /*
635646 * PRS 0 (kernel): all static DPR slots + kernel CPR execute.
636647 * DPR 0 already covers the full address space. The kernel also needs
637- * execute over the userspace CPR: the thread trampoline and the common
638- * userspace entry live in user text, and kernel-privileged threads
639- * (e.g. idle) start there too. Granting the trusted kernel execute of
640- * user text keeps isolation intact (PRS 1 still cannot reach CPR 0).
648+ * execute over the userspace CPR: ulmk_user_thread_entry lives in user
649+ * text, and kernel-privileged threads (e.g. idle) start there too.
650+ * Granting the trusted kernel execute of user text keeps isolation
651+ * intact (PRS 1 still cannot reach CPR 0).
641652 */
642653 prs0_cpr = (1u << ULMK_ARCH_MPU_CPR_KERNEL );
643654 if (utext_hi > utext_lo )
@@ -646,7 +657,6 @@ void ulmk_arch_mpu_init(void)
646657 mpu_write_enables (0u ,
647658 (1u << ULMK_ARCH_MPU_NUM_DPR ) - 1u ,
648659 (1u << ULMK_ARCH_MPU_NUM_DPR ) - 1u ,
649- prs0_cpr ,
650660 prs0_cpr );
651661
652662 /*
@@ -658,7 +668,6 @@ void ulmk_arch_mpu_init(void)
658668 (1u << ULMK_ARCH_MPU_MMIO_DPR ),
659669 (1u << ULMK_ARCH_MPU_URAM_DPR ) |
660670 (1u << ULMK_ARCH_MPU_MMIO_DPR ),
661- prs1_cpre ,
662671 prs1_cpxe );
663672
664673 /* PRS 2, 3: remain zeroed (unused) */
@@ -668,22 +677,26 @@ void ulmk_arch_mpu_enable(void)
668677{
669678 uint32_t syscon ;
670679
680+ ulmk_board_cpu_endinit_clear ();
671681 __asm__ volatile ("mfcr %0, 0xFE14" : "=d" (syscon ));
672682 mpu_mtcr (ULMK_ARCH_CSFR_SYSCON , syscon | ULMK_ARCH_SYSCON_PROTEN );
673683 __asm__ volatile ("isync" ::: "memory" );
684+ ulmk_board_cpu_endinit_set ();
674685}
675686
676687void ulmk_arch_mpu_disable (void )
677688{
678689 uint32_t syscon ;
679690
691+ ulmk_board_cpu_endinit_clear ();
680692 __asm__ volatile ("mfcr %0, 0xFE14" : "=d" (syscon ));
681693 mpu_mtcr (ULMK_ARCH_CSFR_SYSCON , syscon & ~ULMK_ARCH_SYSCON_PROTEN );
682694 __asm__ volatile ("isync" ::: "memory" );
695+ ulmk_board_cpu_endinit_set ();
683696}
684697
685698static void mpu_prs1_static_enables (uint32_t * dpre , uint32_t * dpwe ,
686- uint32_t * cpre , uint32_t * cpxe )
699+ uint32_t * cpxe )
687700{
688701 uintptr_t utext_lo ;
689702 uintptr_t utext_hi ;
@@ -698,12 +711,9 @@ static void mpu_prs1_static_enables(uint32_t *dpre, uint32_t *dpwe,
698711 (1u << ULMK_ARCH_MPU_MMIO_DPR );
699712 * dpwe = (1u << ULMK_ARCH_MPU_URAM_DPR ) |
700713 (1u << ULMK_ARCH_MPU_MMIO_DPR );
701- * cpre = 0u ;
702714 * cpxe = 0u ;
703- if (utext_hi > utext_lo ) {
704- * cpre = (1u << ULMK_ARCH_MPU_CPR_USER );
715+ if (utext_hi > utext_lo )
705716 * cpxe = (1u << ULMK_ARCH_MPU_CPR_USER );
706- }
707717}
708718
709719static void mpu_write_user_slot (uint8_t idx , const ulmk_arch_region_t * r ,
@@ -750,7 +760,6 @@ static void mpu_program_regions(uint8_t prs, const ulmk_arch_region_t *regions,
750760{
751761 uint32_t dpre ;
752762 uint32_t dpwe ;
753- uint32_t cpre ;
754763 uint32_t cpxe ;
755764 uint8_t i ;
756765 uint8_t prog ;
@@ -774,7 +783,6 @@ static void mpu_program_regions(uint8_t prs, const ulmk_arch_region_t *regions,
774783 mpu_write_enables (0u ,
775784 (1u << ULMK_ARCH_MPU_NUM_DPR ) - 1u ,
776785 (1u << ULMK_ARCH_MPU_NUM_DPR ) - 1u ,
777- prs0_cpr ,
778786 prs0_cpr );
779787 g_mpu_prs = 0u ;
780788 g_mpu_regions = NULL ;
@@ -804,7 +812,7 @@ static void mpu_program_regions(uint8_t prs, const ulmk_arch_region_t *regions,
804812 return ;
805813 }
806814
807- mpu_prs1_static_enables (& dpre , & dpwe , & cpre , & cpxe );
815+ mpu_prs1_static_enables (& dpre , & dpwe , & cpxe );
808816
809817 /*
810818 * Fast append: mem_map grew the table by one non-STACK region and the
@@ -826,7 +834,7 @@ static void mpu_program_regions(uint8_t prs, const ulmk_arch_region_t *regions,
826834 if (regions [i ].perms & ULMK_PERM_WRITE )
827835 dpwe |= (1u << d_slot );
828836 }
829- mpu_write_enables (prs , dpre , dpwe , cpre , cpxe );
837+ mpu_write_enables (prs , dpre , dpwe , cpxe );
830838 g_mpu_count = count ;
831839 g_mpu_live = count ;
832840 return ;
@@ -848,7 +856,7 @@ static void mpu_program_regions(uint8_t prs, const ulmk_arch_region_t *regions,
848856 mpu_write_dpr (d_slot , 0u , 0u );
849857 }
850858
851- mpu_write_enables (prs , dpre , dpwe , cpre , cpxe );
859+ mpu_write_enables (prs , dpre , dpwe , cpxe );
852860
853861 g_mpu_prs = prs ;
854862 g_mpu_regions = regions ;
@@ -951,11 +959,13 @@ void ulmk_arch_irq_vectors_init(uintptr_t btv, uintptr_t biv, uintptr_t isp_top)
951959 uint32_t biv32 = (uint32_t )biv ;
952960 uint32_t isp32 = (uint32_t )isp_top ;
953961
962+ ulmk_board_cpu_endinit_clear ();
954963 __asm__ volatile ("dsync" ::: "memory" );
955964 __asm__ volatile ("mtcr 0xFE24, %0" :: "d" (btv32 )); /* BTV */
956965 __asm__ volatile ("mtcr 0xFE20, %0" :: "d" (biv32 )); /* BIV, VSS=0 → 32-byte slots */
957966 __asm__ volatile ("mtcr 0xFE28, %0" :: "d" (isp32 )); /* ISP */
958967 __asm__ volatile ("isync" ::: "memory" );
968+ ulmk_board_cpu_endinit_set ();
959969}
960970
961971void ulmk_arch_irq_src_configure (uint8_t srpn , uint8_t priority , uint8_t cpu_id )
@@ -1454,3 +1464,14 @@ void ulmk_arch_spin_unlock_irqrestore(ulmk_spinlock_t *lock,
14541464}
14551465
14561466/* send_ipi / secondary / park — arch/tricore/smp.c */
1467+
1468+ /*
1469+ * Weak no-ops: QEMU and boards without CPU EndInit. tc275_lite overrides.
1470+ */
1471+ __attribute__((weak )) void ulmk_board_cpu_endinit_clear (void )
1472+ {
1473+ }
1474+
1475+ __attribute__((weak )) void ulmk_board_cpu_endinit_set (void )
1476+ {
1477+ }
0 commit comments