xref: /rk3399_ARM-atf/include/plat/common/plat_hold_pen.h (revision ecab5d9e3f81b7bf093002b8614359adfc8d880d)
1*e45ca16eSNicolas Pitre /*
2*e45ca16eSNicolas Pitre  * Copyright (c) 2026, BayLibre SAS
3*e45ca16eSNicolas Pitre  *
4*e45ca16eSNicolas Pitre  * SPDX-License-Identifier: BSD-3-Clause
5*e45ca16eSNicolas Pitre  */
6*e45ca16eSNicolas Pitre 
7*e45ca16eSNicolas Pitre #ifndef PLAT_HOLD_PEN_H
8*e45ca16eSNicolas Pitre #define PLAT_HOLD_PEN_H
9*e45ca16eSNicolas Pitre 
10*e45ca16eSNicolas Pitre #ifndef __ASSEMBLER__
11*e45ca16eSNicolas Pitre #include <cdefs.h>
12*e45ca16eSNicolas Pitre #include <stddef.h>
13*e45ca16eSNicolas Pitre #include <stdint.h>
14*e45ca16eSNicolas Pitre 
15*e45ca16eSNicolas Pitre #include <lib/cassert.h>
16*e45ca16eSNicolas Pitre #endif
17*e45ca16eSNicolas Pitre 
18*e45ca16eSNicolas Pitre #include <lib/cpus/cpu_ops.h>
19*e45ca16eSNicolas Pitre #include <lib/utils_def.h>
20*e45ca16eSNicolas Pitre #include <platform_def.h>
21*e45ca16eSNicolas Pitre 
22*e45ca16eSNicolas Pitre /*
23*e45ca16eSNicolas Pitre  * Generic hold pen for SMP secondary CPU bring-up.
24*e45ca16eSNicolas Pitre  *
25*e45ca16eSNicolas Pitre  * Platforms call plat_hold_pen_init() once at boot to write
26*e45ca16eSNicolas Pitre  * HOLD_STATE_WAIT followed by magic tags into every slot, then flush
27*e45ca16eSNicolas Pitre  * to main memory.
28*e45ca16eSNicolas Pitre  *
29*e45ca16eSNicolas Pitre  * The primary CPU signals a secondary by calling plat_hold_pen_signal(),
30*e45ca16eSNicolas Pitre  * which writes the warm boot entrypoint into the target slot,
31*e45ca16eSNicolas Pitre  * flushes it, and issues SEV.
32*e45ca16eSNicolas Pitre  *
33*e45ca16eSNicolas Pitre  * Secondary CPUs may reach their polling loop before plat_hold_pen_init()
34*e45ca16eSNicolas Pitre  * has run (they diverge in el3_entrypoint_common before C runtime
35*e45ca16eSNicolas Pitre  * init). To guard against branching to stale SRAM content, each
36*e45ca16eSNicolas Pitre  * slot carries two magic tags that must both match before the entry
37*e45ca16eSNicolas Pitre  * field is considered. Random memory is unlikely to contain both
38*e45ca16eSNicolas Pitre  * magic values in sequence. The entry field is written before the
39*e45ca16eSNicolas Pitre  * magic tags during init so that a valid entry value is always in
40*e45ca16eSNicolas Pitre  * place before the tags make the slot "live".
41*e45ca16eSNicolas Pitre  *
42*e45ca16eSNicolas Pitre  * Before branching, the secondary writes HOLD_STATE_WAIT back into
43*e45ca16eSNicolas Pitre  * its entry field so that a subsequent warm boot re-entering the
44*e45ca16eSNicolas Pitre  * polling loop does not see a stale entrypoint.
45*e45ca16eSNicolas Pitre  *
46*e45ca16eSNicolas Pitre  * Each slot is cache-line aligned so that flushing one core's slot
47*e45ca16eSNicolas Pitre  * cannot accidentally evict or corrupt another's.
48*e45ca16eSNicolas Pitre  */
49*e45ca16eSNicolas Pitre 
50*e45ca16eSNicolas Pitre #define HOLD_MAGIC1		UL(0xCAFECAFE)
51*e45ca16eSNicolas Pitre #define HOLD_MAGIC2		UL(0xBEEFBEEF)
52*e45ca16eSNicolas Pitre 
53*e45ca16eSNicolas Pitre /*
54*e45ca16eSNicolas Pitre  * All-ones sentinel: no valid entrypoint can live here.
55*e45ca16eSNicolas Pitre  * Evaluates to 0xffffffff on AArch32 and 0xffffffffffffffff on AArch64.
56*e45ca16eSNicolas Pitre  */
57*e45ca16eSNicolas Pitre #define HOLD_STATE_WAIT		(~UL(0))
58*e45ca16eSNicolas Pitre 
59*e45ca16eSNicolas Pitre #define HOLD_SLOT_SIZE		CACHE_WRITEBACK_GRANULE
60*e45ca16eSNicolas Pitre 
61*e45ca16eSNicolas Pitre /*
62*e45ca16eSNicolas Pitre  * Field sizes and offsets within struct hold_slot, following the
63*e45ca16eSNicolas Pitre  * pattern established in include/lib/cpus/cpu_ops.h.
64*e45ca16eSNicolas Pitre  */
65*e45ca16eSNicolas Pitre #define HOLD_SLOT_ENTRY_SIZE	CPU_WORD_SIZE
66*e45ca16eSNicolas Pitre #define HOLD_SLOT_MAGIC1_SIZE	CPU_WORD_SIZE
67*e45ca16eSNicolas Pitre #define HOLD_SLOT_MAGIC2_SIZE	CPU_WORD_SIZE
68*e45ca16eSNicolas Pitre 
69*e45ca16eSNicolas Pitre #define HOLD_SLOT_ENTRY		0
70*e45ca16eSNicolas Pitre #define HOLD_SLOT_MAGIC1	HOLD_SLOT_ENTRY + HOLD_SLOT_ENTRY_SIZE
71*e45ca16eSNicolas Pitre #define HOLD_SLOT_MAGIC2	HOLD_SLOT_MAGIC1 + HOLD_SLOT_MAGIC1_SIZE
72*e45ca16eSNicolas Pitre 
73*e45ca16eSNicolas Pitre #ifndef __ASSEMBLER__
74*e45ca16eSNicolas Pitre 
75*e45ca16eSNicolas Pitre /*
76*e45ca16eSNicolas Pitre  * Per-core hold pen slot, cache-line aligned.
77*e45ca16eSNicolas Pitre  * The 'entry' field holds HOLD_STATE_WAIT while the core should keep
78*e45ca16eSNicolas Pitre  * polling, or the warm boot entrypoint address when it should go.
79*e45ca16eSNicolas Pitre  */
80*e45ca16eSNicolas Pitre struct hold_slot {
81*e45ca16eSNicolas Pitre 	uintptr_t entry;
82*e45ca16eSNicolas Pitre 	uintptr_t magic1;
83*e45ca16eSNicolas Pitre 	uintptr_t magic2;
84*e45ca16eSNicolas Pitre 	/* Padded to cache line boundary by __aligned. */
85*e45ca16eSNicolas Pitre } __aligned(HOLD_SLOT_SIZE);
86*e45ca16eSNicolas Pitre 
87*e45ca16eSNicolas Pitre CASSERT(sizeof(struct hold_slot) == HOLD_SLOT_SIZE,
88*e45ca16eSNicolas Pitre 	hold_slot_not_cacheline_sized);
89*e45ca16eSNicolas Pitre CASSERT(offsetof(struct hold_slot, entry) == HOLD_SLOT_ENTRY,
90*e45ca16eSNicolas Pitre 	hold_slot_entry_off_mismatch);
91*e45ca16eSNicolas Pitre CASSERT(offsetof(struct hold_slot, magic1) == HOLD_SLOT_MAGIC1,
92*e45ca16eSNicolas Pitre 	hold_slot_magic1_off_mismatch);
93*e45ca16eSNicolas Pitre CASSERT(offsetof(struct hold_slot, magic2) == HOLD_SLOT_MAGIC2,
94*e45ca16eSNicolas Pitre 	hold_slot_magic2_off_mismatch);
95*e45ca16eSNicolas Pitre 
96*e45ca16eSNicolas Pitre void plat_hold_pen_init(struct hold_slot *hold_pen, unsigned int core_count);
97*e45ca16eSNicolas Pitre void plat_hold_pen_signal(struct hold_slot *hold_pen, unsigned int core_idx,
98*e45ca16eSNicolas Pitre 			  uintptr_t entry_point);
99*e45ca16eSNicolas Pitre 
100*e45ca16eSNicolas Pitre #endif /* __ASSEMBLER__ */
101*e45ca16eSNicolas Pitre 
102*e45ca16eSNicolas Pitre #endif /* PLAT_HOLD_PEN_H */
103