/* * Copyright (c) 2026, BayLibre SAS * * SPDX-License-Identifier: BSD-3-Clause */ #ifndef PLAT_HOLD_PEN_H #define PLAT_HOLD_PEN_H #ifndef __ASSEMBLER__ #include #include #include #include #endif #include #include #include /* * Generic hold pen for SMP secondary CPU bring-up. * * Platforms call plat_hold_pen_init() once at boot to write * HOLD_STATE_WAIT followed by magic tags into every slot, then flush * to main memory. * * The primary CPU signals a secondary by calling plat_hold_pen_signal(), * which writes the warm boot entrypoint into the target slot, * flushes it, and issues SEV. * * Secondary CPUs may reach their polling loop before plat_hold_pen_init() * has run (they diverge in el3_entrypoint_common before C runtime * init). To guard against branching to stale SRAM content, each * slot carries two magic tags that must both match before the entry * field is considered. Random memory is unlikely to contain both * magic values in sequence. The entry field is written before the * magic tags during init so that a valid entry value is always in * place before the tags make the slot "live". * * Before branching, the secondary writes HOLD_STATE_WAIT back into * its entry field so that a subsequent warm boot re-entering the * polling loop does not see a stale entrypoint. * * Each slot is cache-line aligned so that flushing one core's slot * cannot accidentally evict or corrupt another's. */ #define HOLD_MAGIC1 UL(0xCAFECAFE) #define HOLD_MAGIC2 UL(0xBEEFBEEF) /* * All-ones sentinel: no valid entrypoint can live here. * Evaluates to 0xffffffff on AArch32 and 0xffffffffffffffff on AArch64. */ #define HOLD_STATE_WAIT (~UL(0)) #define HOLD_SLOT_SIZE CACHE_WRITEBACK_GRANULE /* * Field sizes and offsets within struct hold_slot, following the * pattern established in include/lib/cpus/cpu_ops.h. */ #define HOLD_SLOT_ENTRY_SIZE CPU_WORD_SIZE #define HOLD_SLOT_MAGIC1_SIZE CPU_WORD_SIZE #define HOLD_SLOT_MAGIC2_SIZE CPU_WORD_SIZE #define HOLD_SLOT_ENTRY 0 #define HOLD_SLOT_MAGIC1 HOLD_SLOT_ENTRY + HOLD_SLOT_ENTRY_SIZE #define HOLD_SLOT_MAGIC2 HOLD_SLOT_MAGIC1 + HOLD_SLOT_MAGIC1_SIZE #ifndef __ASSEMBLER__ /* * Per-core hold pen slot, cache-line aligned. * The 'entry' field holds HOLD_STATE_WAIT while the core should keep * polling, or the warm boot entrypoint address when it should go. */ struct hold_slot { uintptr_t entry; uintptr_t magic1; uintptr_t magic2; /* Padded to cache line boundary by __aligned. */ } __aligned(HOLD_SLOT_SIZE); CASSERT(sizeof(struct hold_slot) == HOLD_SLOT_SIZE, hold_slot_not_cacheline_sized); CASSERT(offsetof(struct hold_slot, entry) == HOLD_SLOT_ENTRY, hold_slot_entry_off_mismatch); CASSERT(offsetof(struct hold_slot, magic1) == HOLD_SLOT_MAGIC1, hold_slot_magic1_off_mismatch); CASSERT(offsetof(struct hold_slot, magic2) == HOLD_SLOT_MAGIC2, hold_slot_magic2_off_mismatch); void plat_hold_pen_init(struct hold_slot *hold_pen, unsigned int core_count); void plat_hold_pen_signal(struct hold_slot *hold_pen, unsigned int core_idx, uintptr_t entry_point); #endif /* __ASSEMBLER__ */ #endif /* PLAT_HOLD_PEN_H */