ZXFoundation™ 26h2
Loading...
Searching...
No Matches
lowcore.cxxm
1/// SPDX-License-Identifier: Apache-2.0
2/// @file arch/s390x/cpu/lowcore_types.cxxm
3/// @brief z/Architecture 8 KB prefix (lowcore) layout.
4
5export module arch.s390x.cpu.lowcore;
6import arch.s390x.cpu.psw;
7import arch.s390x.cpu.percpu;
8import arch.s390x.init.zxfl.protocol;
9import zxfoundation.sys.syschk.types;
10import zxfoundation.base.types;
11
12export {
13
14namespace arch::s390x::cpu::lowcore {
15
16 constexpr usize LOWCORE_SIZE = 0x2000UL; ///< 8 KiB per z/Architecture PoP.
17
18 constexpr u64 LC_EXT_OLD_PSW = 0x0130UL; ///< zx_lowcore::external_old_psw
19 constexpr u64 LC_SVC_OLD_PSW = 0x0140UL; ///< zx_lowcore::svc_old_psw
20 constexpr u64 LC_PGM_OLD_PSW = 0x0150UL; ///< zx_lowcore::program_old_psw
21 constexpr u64 LC_MCCK_OLD_PSW = 0x0160UL; ///< zx_lowcore::mcck_old_psw
22 constexpr u64 LC_IO_OLD_PSW = 0x0170UL; ///< zx_lowcore::io_old_psw
23
24 constexpr u64 LC_PSW_TEMP = 0x0248UL; ///< Temporary PSW storage at start of pad_0x0248 – used by entry.S RESTORE_FRAME
25 constexpr u64 LC_RETURN_PSW = 0x0290UL; ///< zx_lowcore::return_psw
26 constexpr u64 LC_KERNEL_STACK = 0x0348UL; ///< zx_lowcore::kernel_stack
27 constexpr u64 LC_ASYNC_STACK = 0x0350UL; ///< zx_lowcore::async_stack
28 constexpr u64 LC_NODAT_STACK = 0x0358UL; ///< zx_lowcore::nodat_stack
29 constexpr u64 LC_RESTART_STACK = 0x0360UL; ///< zx_lowcore::restart_stack
30 constexpr u64 LC_MCCK_STACK = 0x0368UL; ///< zx_lowcore::mcck_stack
31 constexpr u64 LC_AP_CR0 = 0x0330UL; ///< zx_lowcore::ap_cr0 (software-defined)
32 constexpr u64 LC_AP_CR13 = 0x0338UL; ///< zx_lowcore::ap_cr13 (software-defined)
33 constexpr u64 LC_CURRENT_DOMAIN = 0x0340UL; ///< zx_lowcore::current_domain — used by entry.S
34 constexpr u64 LC_KERNEL_ASCE = 0x0388UL; ///< zx_lowcore::kernel_asce
35
36 constexpr u64 LC_PERCPU_OFFSET = 0x0400UL; ///< zx_lowcore::percpu
37 constexpr u64 LC_CPU_ID_OFFSET = 0x0408UL; ///< percpu::cpu_id
38 constexpr u64 LC_PREEMPT_COUNT = 0x0430UL; ///< percpu::preempt_count — used by entry.S
39 constexpr u64 LC_IRQ_NESTING = 0x0438UL; ///< percpu::irq_nesting — used by entry.S
40 constexpr u64 LC_NUCLEUS_NESTING = 0x043CUL; ///< percpu::nucleus_nesting — used by entry.S
41 constexpr u64 LC_IRQ_CLASS = 0x0444UL; ///< percpu::current_irq_class — used by entry.S
42
43 /// @brief z/Architecture 8 KB prefix (lowcore) structure.
44 struct alignas(8192) zx_lowcore {
45 u8 pad_0x0000[0x0014 - 0x0000]{}; /* 0x0000 */
46 u32 ipl_parmblock_ptr{}; /* 0x0014 */
47 u8 pad_0x0018[0x0080 - 0x0018]{}; /* 0x0018 */
48
49 u32 ext_params{}; /* 0x0080 */
50 u16 ext_cpu_addr{}; /* 0x0084 */
51 u16 ext_int_code{}; /* 0x0086 */
52
53 u16 svc_ilc{}; /* 0x0088 */
54 u16 svc_code{}; /* 0x008a */
55 u16 pgm_ilc{}; /* 0x008c */
56 u16 pgm_code{}; /* 0x008e */
57 u32 data_exc_code{}; /* 0x0090 */
58 u16 mon_class_num{}; /* 0x0094 */
59 u16 per_perc_atmid{}; /* 0x0096 */
60 u64 per_address{}; /* 0x0098 */
61 u8 exc_access_id{}; /* 0x00a0 */
62 u8 per_access_id{}; /* 0x00a1 */
63 u8 op_access_id{}; /* 0x00a2 */
64 u8 ar_access_id{}; /* 0x00a3 */
65 u8 pad_0x00a4[0x00a8 - 0x00a4]{}; /* 0x00a4 */
66 u64 trans_exc_code{}; /* 0x00a8 */
67 u64 monitor_code{}; /* 0x00b0 */
68 u16 subchannel_id{}; /* 0x00b8 */
69 u16 subchannel_nr{}; /* 0x00ba */
70 u32 io_int_parm{}; /* 0x00bc */
71 u32 io_int_word{}; /* 0x00c0 */
72 u8 pad_0x00c4[0x00c8 - 0x00c4]{}; /* 0x00c4 */
73 u32 stfl_fac_list{}; /* 0x00c8 */
74 u8 pad_0x00cc[0x00e8 - 0x00cc]{}; /* 0x00cc */
75 u64 mcck_interruption_code{}; /* 0x00e8 */
76 u8 pad_0x00f0[0x00f4 - 0x00f0]{}; /* 0x00f0 */
77 u32 external_damage_code{}; /* 0x00f4 */
78 u64 failing_storage_address{}; /* 0x00f8 */
79 u8 pad_0x0100[0x0110 - 0x0100]{}; /* 0x0100 */
80 u64 breaking_event_addr{}; /* 0x0110 */
81 u8 pad_0x0118[0x0120 - 0x0118]{}; /* 0x0118 */
82
83 psw::zx_psw restart_old_psw{}; /* 0x0120 */
84 psw::zx_psw external_old_psw{}; /* 0x0130 LC_EXT_OLD_PSW */
85 psw::zx_psw svc_old_psw{}; /* 0x0140 LC_SVC_OLD_PSW */
86 psw::zx_psw program_old_psw{}; /* 0x0150 LC_PGM_OLD_PSW */
87 psw::zx_psw mcck_old_psw{}; /* 0x0160 LC_MCCK_OLD_PSW */
88 psw::zx_psw io_old_psw{}; /* 0x0170 LC_IO_OLD_PSW */
89 u8 pad_0x0180[0x01a0 - 0x0180]{}; /* 0x0180 */
90
91 psw::zx_psw restart_psw{}; /* 0x01a0 PSW_LC_RESTART */
92 psw::zx_psw external_new_psw{}; /* 0x01b0 PSW_LC_EXTERNAL */
93 psw::zx_psw svc_new_psw{}; /* 0x01c0 PSW_LC_SVC */
94 psw::zx_psw program_new_psw{}; /* 0x01d0 PSW_LC_PROGRAM */
95 psw::zx_psw mcck_new_psw{}; /* 0x01e0 PSW_LC_MCCK */
96 psw::zx_psw io_new_psw{}; /* 0x01f0 PSW_LC_IO */
97
98 u64 save_area_sync[8]{}; /* 0x0200 */
99
100 u64 stack_canary{}; /* 0x0240 */
101 psw::zx_psw return_psw_temp{}; /* 0x0248 LC_PSW_TEMP – temporary PSW storage for RESTORE_FRAME */
102 u8 pad_0x0258[0x0280 - 0x0258]{}; /* 0x0258 */
103
104 u64 save_area_restart[1]{}; /* 0x0280 */
105 u64 pcpu{}; /* 0x0288 */
106
107 psw::zx_psw return_psw{}; /* 0x0290 LC_RETURN_PSW */
108 psw::zx_psw return_mcck_psw{}; /* 0x02a0 */
109 u64 last_break{}; /* 0x02b0 */
110
111 u64 sys_enter_timer{}; /* 0x02b8 */
112 u64 mcck_enter_timer{}; /* 0x02c0 */
113 u64 exit_timer{}; /* 0x02c8 */
114 u64 user_timer{}; /* 0x02d0 */
115 u64 guest_timer{}; /* 0x02d8 */
116 u64 system_timer{}; /* 0x02e0 */
117 u64 hardirq_timer{}; /* 0x02e8 */
118 u64 softirq_timer{}; /* 0x02f0 */
119 u64 steal_timer{}; /* 0x02f8 */
120 u64 avg_steal_timer{}; /* 0x0300 */
121 u64 last_update_timer{}; /* 0x0308 */
122 u64 last_update_clock{}; /* 0x0310 */
123 u64 int_clock{}; /* 0x0318 */
124 u8 pad_0x0320[0x0328 - 0x0320]{}; /* 0x0320 */
125 u64 clock_comparator{}; /* 0x0328 */
126
127 u64 ap_cr0{}; /* 0x0330 LC_AP_CR0 */
128 u64 ap_cr13{}; /* 0x0338 LC_AP_CR13 */
129
130 u64 current_domain{}; /* 0x0340 */
131 u64 kernel_stack{}; /* 0x0348 LC_KERNEL_STACK */
132
133 u64 async_stack{}; /* 0x0350 LC_ASYNC_STACK */
134 u64 nodat_stack{}; /* 0x0358 LC_NODAT_STACK */
135 u64 restart_stack{}; /* 0x0360 LC_RESTART_STACK */
136 u64 mcck_stack{}; /* 0x0368 LC_MCCK_STACK */
137
138 u64 restart_fn{}; /* 0x0370 */
139 u64 restart_data{}; /* 0x0378 */
140 u32 restart_source{}; /* 0x0380 */
141 u32 restart_flags{}; /* 0x0384 */
142
143 u64 kernel_asce{}; /* 0x0388 LC_KERNEL_ASCE */
144 u64 user_asce{}; /* 0x0390 */
145
146 u32 lpp{}; /* 0x0398 */
147 u32 current_pid{}; /* 0x039c */
148
149 u32 cpu_nr{}; /* 0x03a0 */
150 u32 softirq_pending{}; /* 0x03a4 */
151 i32 preempt_count{}; /* 0x03a8 */
152 u32 spinlock_lockval{}; /* 0x03ac */
153 u32 spinlock_index{}; /* 0x03b0 */
154 u8 pad_0x03b4[0x03b8 - 0x03b4]{}; /* 0x03b4 */
155
156 u64 percpu_offset{}; /* 0x03b8 */
157 u8 pad_0x03c0[0x0400 - 0x03c0]{}; /* 0x03c0 */
158
159 percpu::percpu_data percpu{}; /* 0x0400 */
160
161 u8 pad_percpu_end[0x1200 - (0x0400 + sizeof(percpu::percpu_data))]{};
162
163 u64 floating_pt_save_area[16]{}; /* 0x1200 */
164 u64 gpregs_save_area[16]{}; /* 0x1280 */
165 psw::zx_psw psw_save_area{}; /* 0x1300 */
166 u8 pad_0x1310[0x1318 - 0x1310]{}; /* 0x1310 */
167 u32 prefixreg_save_area{}; /* 0x1318 */
168 u32 fpt_creg_save_area{}; /* 0x131c */
169 u8 pad_0x1320[0x1324 - 0x1320]{}; /* 0x1320 */
170 u32 tod_progreg_save_area{}; /* 0x1324 */
171 u32 cpu_timer_save_area[2]{}; /* 0x1328 */
172 u32 clock_comp_save_area[2]{}; /* 0x1330 */
173 u64 last_break_save_area{}; /* 0x1338 */
174 u32 access_regs_save_area[16]{}; /* 0x1340 */
175 u64 cregs_save_area[16]{}; /* 0x1380 */
176 zxfoundation::sys::syschk::zx_crash_record crash_record{}; /* 0x1400 */
177 u8 pad_0x1400_rest[0x1800 - 0x1400 - sizeof(zxfoundation::sys::syschk::zx_crash_record)]{}; /* after crash_record */
178
179 u64 pgm_tdb[32]{}; /* 0x1800 */
180 u8 pad_0x1900[0x2000 - 0x1900]{}; /* 0x1900 */
181 };
182
183 static_assert(sizeof(zx_lowcore) == LOWCORE_SIZE,
184 "zx_lowcore must be exactly 8192 bytes");
185
186 static_assert(__builtin_offsetof(zx_lowcore, external_old_psw) == LC_EXT_OLD_PSW,
187 "LC_EXT_OLD_PSW mismatch");
188 static_assert(__builtin_offsetof(zx_lowcore, svc_old_psw) == LC_SVC_OLD_PSW,
189 "LC_SVC_OLD_PSW mismatch");
190 static_assert(__builtin_offsetof(zx_lowcore, program_old_psw) == LC_PGM_OLD_PSW,
191 "LC_PGM_OLD_PSW mismatch");
192 static_assert(__builtin_offsetof(zx_lowcore, mcck_old_psw) == LC_MCCK_OLD_PSW,
193 "LC_MCCK_OLD_PSW mismatch");
194 static_assert(__builtin_offsetof(zx_lowcore, io_old_psw) == LC_IO_OLD_PSW,
195 "LC_IO_OLD_PSW mismatch");
196
197 static_assert(__builtin_offsetof(zx_lowcore, return_psw_temp) == LC_PSW_TEMP,
198 "LC_PSW_TEMP mismatch");
199 static_assert(__builtin_offsetof(zx_lowcore, return_psw) == LC_RETURN_PSW,
200 "LC_RETURN_PSW mismatch");
201 static_assert(__builtin_offsetof(zx_lowcore, kernel_stack) == LC_KERNEL_STACK,
202 "LC_KERNEL_STACK mismatch");
203 static_assert(__builtin_offsetof(zx_lowcore, async_stack) == LC_ASYNC_STACK,
204 "LC_ASYNC_STACK mismatch");
205 static_assert(__builtin_offsetof(zx_lowcore, nodat_stack) == LC_NODAT_STACK,
206 "LC_NODAT_STACK mismatch");
207 static_assert(__builtin_offsetof(zx_lowcore, restart_stack) == LC_RESTART_STACK,
208 "LC_RESTART_STACK mismatch");
209 static_assert(__builtin_offsetof(zx_lowcore, mcck_stack) == LC_MCCK_STACK,
210 "LC_MCCK_STACK mismatch");
211 static_assert(__builtin_offsetof(zx_lowcore, ap_cr0) == LC_AP_CR0,
212 "LC_AP_CR0 mismatch");
213 static_assert(__builtin_offsetof(zx_lowcore, ap_cr13) == LC_AP_CR13,
214 "LC_AP_CR13 mismatch");
215 static_assert(__builtin_offsetof(zx_lowcore, current_domain) == LC_CURRENT_DOMAIN,
216 "LC_CURRENT_DOMAIN mismatch");
217 static_assert(__builtin_offsetof(zx_lowcore, kernel_asce) == LC_KERNEL_ASCE,
218 "LC_KERNEL_ASCE mismatch");
219 static_assert(__builtin_offsetof(zx_lowcore, percpu) == LC_PERCPU_OFFSET,
220 "LC_PERCPU_OFFSET mismatch");
221
222 static_assert(
223 __builtin_offsetof(zx_lowcore, percpu) +
224 __builtin_offsetof(percpu::percpu_data, cpu_id) == LC_CPU_ID_OFFSET,
225 "LC_CPU_ID_OFFSET does not match actual cpu_id field offset in zx_lowcore; "
226 "update LC_CPU_ID_OFFSET to match");
227
228 static_assert(
229 __builtin_offsetof(zx_lowcore, percpu) +
230 __builtin_offsetof(percpu::percpu_data, irq_nesting) == LC_IRQ_NESTING,
231 "LC_IRQ_NESTING does not match irq_nesting field; update LC_IRQ_NESTING");
232
233 static_assert(
234 __builtin_offsetof(zx_lowcore, percpu) +
235 __builtin_offsetof(percpu::percpu_data, nucleus_nesting) == LC_NUCLEUS_NESTING,
236 "LC_NUCLEUS_NESTING does not match nucleus_nesting field; update LC_NUCLEUS_NESTING");
237
238 static_assert(
239 __builtin_offsetof(zx_lowcore, percpu) +
240 __builtin_offsetof(percpu::percpu_data, preempt_count) == LC_PREEMPT_COUNT,
241 "LC_PREEMPT_COUNT does not match preempt_count field; update entry.S");
242
243 [[nodiscard]] auto get_current() noexcept -> zx_lowcore*;
244 [[nodiscard]] auto get_current_phys() noexcept -> zx_lowcore*;
245
246 /// @brief Retrieve the lowcore structure pointer for a specific CPU, taking prefixing into account.
247 /// @param[in] cpu_id The logical target CPU ID.
248 /// @return Pointer to the lowcore of the target CPU, or nullptr if unregistered.
249 [[nodiscard]] auto get_for_cpu(u16 cpu_id) noexcept -> zx_lowcore*;
250
251 auto register_cpu_lowcore(u16 cpu_id, u64 phys_lc) noexcept -> void;
252
253 /// @brief Initialize the BSP lowcore. Must be called once after HHDM is active.
254 auto init_bsp(const init::zxfl::protocol& proto) noexcept -> void;
255
256 [[nodiscard]] auto this_cpu_percpu() noexcept -> percpu::percpu_data&;
257
258 [[nodiscard]] auto cpu_percpu(u16 cpu_id) noexcept -> percpu::percpu_data*;
259
260 /// @brief Get the current domain pointer from lowcore.
261 [[nodiscard]] auto current_domain_ptr() noexcept -> void*;
262
263 /// @brief Get the current strand pointer from percpu.
264 [[nodiscard]] auto current_strand_ptr() noexcept -> void*;
265
266 /// @brief Install a runtime restart-interrupt handler on the current CPU.
267 /// @param[in] fn Callback invoked by trap_restart_entry on SIGP RESTART.
268 /// @param[in] data Opaque cookie forwarded as %r2 to the callback.
269 auto register_restart_handler(
270 void(*fn)(void*) noexcept,
271 void* data
272 ) noexcept -> void;
273
274 /// @brief Parameters for AP lowcore setup.
275 struct ap_init_params {
276 u64 phys_lc{}; ///< Physical address of the AP's 8 KB lowcore page.
277 u64 ap_entry_phys{}; ///< Physical address of ap_entry (DAT-off restart target).
278 u64 kernel_asce{}; ///< Kernel ASCE (CR1) to load before enabling DAT.
279 u64 ap_cr0{}; ///< CR0 snapshot to restore on AP.
280 u64 ap_stack_phys{}; ///< Physical top of AP's initial stack (for LC_RESTART_STACK).
281 u16 cpu_id{}; ///< Logical CPU ID assigned to this AP.
282 u16 cpu_addr{}; ///< Hardware CPU address (from SIGP/STAP).
283 };
284
285 /// @brief Prepare an AP lowcore for SIGP RESTART.
286 /// @param lc HHDM-mapped pointer to the AP's lowcore page.
287 /// @param p AP initialization parameters.
288 auto init_ap(zx_lowcore& lc, const ap_init_params& p) noexcept -> void;
289
290} // namespace arch::s390x::cpu::lowcore
291
292} // end export