ZXFoundation™ 26h2
Loading...
Searching...
No Matches
mcck.cxxm
Go to the documentation of this file.
1/// SPDX-License-Identifier: Apache-2.0
2/// @file arch/s390x/trap/mcck.cxxm
3/// @brief Centralized Machine-Check (MCCK) dispatcher.
4
5export module arch.s390x.trap.mcck;
6import arch.s390x.cpu.irq;
7import arch.s390x.cpu.lowcore;
8import arch.s390x.cpu.clock;
9import arch.s390x.cpu.psw;
10import arch.s390x.cpu.percpu;
11import zxfoundation.sys.irq.core;
12import zxfoundation.sys.irq.consts;
13import zxfoundation.base.types;
14import zxfoundation.sys.syschk.core;
15import zxfoundation.sys.printk.core;
16import zxfoundation.dgp.domain.types;
17import zxfoundation.dgp.signal;
18import zxfoundation.dgp.domain.types;
19import zxfoundation.scoms.kobject.ref;
20import zxfoundation.sched.core;
21import lib.error;
22
23export {
24
25namespace arch::s390x::trap::mcck {
26
27 /// @brief Machine-Check Interruption Code bits per PoP §11-14, Figure 11-5.
28 /// Bit N in PoP notation = mask 1ULL<<(63-N). Bits not listed here
29 /// are unassigned per the 13th-edition PoP.
30 /// @{
31 constexpr u64 MCIC_SD = 1ULL << 63; // bit 0 — system damage
32 constexpr u64 MCIC_PD = 1ULL << 62; // bit 1 — instruction-processing damage
33 constexpr u64 MCIC_SR = 1ULL << 61; // bit 2 — system recovery
34 constexpr u64 MCIC_CD = 1ULL << 59; // bit 4 — timing-facility damage
35 constexpr u64 MCIC_ED = 1ULL << 58; // bit 5 — external damage
36 constexpr u64 MCIC_BACKLOG = 1ULL << 57; // bit 6 — stacked machine check (MCIC not cleared)
37 constexpr u64 MCIC_DG = 1ULL << 56; // bit 7 — degradation
38 constexpr u64 MCIC_W = 1ULL << 55; // bit 8 — warning
39 constexpr u64 MCIC_CP = 1ULL << 54; // bit 9 — channel-report pending
40 constexpr u64 MCIC_SP = 1ULL << 53; // bit 10 — service-processor damage
41 constexpr u64 MCIC_CK = 1ULL << 52; // bit 11 — channel-subsystem damage
42 constexpr u64 MCIC_B = 1ULL << 49; // bit 14 — backed-up (nullifying IPD survived)
43 constexpr u64 MCIC_SE = 1ULL << 47; // bit 16 — storage error uncorrected
44 constexpr u64 MCIC_SC = 1ULL << 46; // bit 17 — storage error corrected
45 constexpr u64 MCIC_KE = 1ULL << 45; // bit 18 — storage-key error uncorrected
46 constexpr u64 MCIC_DS = 1ULL << 44; // bit 19 — storage degradation
47 constexpr u64 MCIC_WP = 1ULL << 43; // bit 20 — PSW-MWP validity
48 constexpr u64 MCIC_MS = 1ULL << 42; // bit 21 — PSW mask and key validity
49 constexpr u64 MCIC_PM = 1ULL << 41; // bit 22 — PSW program-mask and CC validity
50 constexpr u64 MCIC_IA = 1ULL << 40; // bit 23 — PSW-instruction-address validity
51 constexpr u64 MCIC_FA = 1ULL << 39; // bit 24 — failing-storage-address validity
52 constexpr u64 MCIC_VR = 1ULL << 38; // bit 25 — vector-register validity
53 constexpr u64 MCIC_EC = 1ULL << 37; // bit 26 — external-damage-code validity
54 constexpr u64 MCIC_FP = 1ULL << 36; // bit 27 — floating-point-register validity
55 constexpr u64 MCIC_GR = 1ULL << 35; // bit 28 — general-register validity
56 constexpr u64 MCIC_CR = 1ULL << 34; // bit 29 — control-register validity
57 constexpr u64 MCIC_ST = 1ULL << 32; // bit 31 — storage logical validity
58 constexpr u64 MCIC_IE = 1ULL << 31; // bit 32 — indirect storage error
59 constexpr u64 MCIC_AR = 1ULL << 30; // bit 33 — access-register validity
60 constexpr u64 MCIC_DA = 1ULL << 29; // bit 34 — delayed-access exception
61 constexpr u64 MCIC_GS = 1ULL << 27; // bit 36 — guarded-storage-registers validity
62 constexpr u64 MCIC_PR = 1ULL << 21; // bit 42 — TOD-programmable-register validity
63 constexpr u64 MCIC_FC = 1ULL << 20; // bit 43 — FP-control-register validity
64 constexpr u64 MCIC_AP = 1ULL << 19; // bit 44 — ancillary report
65 constexpr u64 MCIC_CT = 1ULL << 17; // bit 46 — CPU-timer validity
66 constexpr u64 MCIC_CC = 1ULL << 16; // bit 47 — clock-comparator validity
67 /// @}
68
69 /// @brief IPD damage-window constants (mirror Linux nmi.c MAX_IPD_COUNT /
70 /// MAX_IPD_TIME). The window is 5 minutes in TOD-clock units
71 /// (1 µs = 4096 ticks -> 5*60*4096*1e6 ticks).
72 /// @{
73 constexpr u32 MAX_IPD_COUNT = 29;
74 constexpr u64 MAX_IPD_WINDOW_TOD = 5ULL * 60ULL * 4'096'000'000ULL;
75 /// @}
76
77 namespace detail {
78
79 /// @brief Validate register-state validity per Linux's
80 /// @c nmi_registers_valid() and PoP §11-15.
81 [[nodiscard]] auto nmi_registers_valid(u64 mcic) noexcept -> bool {
82 constexpr u64 required =
83 MCIC_GR | MCIC_FP | MCIC_FC | MCIC_AR |
84 MCIC_MS | MCIC_PM | MCIC_IA;
85 if ((mcic & required) != required) return false;
86 if ((mcic & MCIC_VR) == 0ULL) return false;
87 return true;
88 }
89
90 /// @brief Structured MCESA diagnosis dump.
91 auto dump_mcesa(const arch::s390x::cpu::lowcore::zx_lowcore& lc) noexcept
92 -> void
93 {
94 zxfoundation::sys::printk::pcrit(
95 "mcck: mcesa dump — psw={:#018x}:{:#018x}\n",
96 static_cast<u64>(lc.psw_save_area.mask),
97 static_cast<u64>(lc.psw_save_area.addr));
98 zxfoundation::sys::printk::pcrit(
99 "mcck: mcesa gprs:\n {:#018x} {:#018x} {:#018x} {:#018x}\n "
100 "{:#018x} {:#018x} {:#018x} {:#018x}\n "
101 "{:#018x} {:#018x} {:#018x} {:#018x}\n "
102 "{:#018x} {:#018x} {:#018x} {:#018x}\n",
103 lc.gpregs_save_area[0], lc.gpregs_save_area[1],
104 lc.gpregs_save_area[2], lc.gpregs_save_area[3],
105 lc.gpregs_save_area[4], lc.gpregs_save_area[5],
106 lc.gpregs_save_area[6], lc.gpregs_save_area[7],
107 lc.gpregs_save_area[8], lc.gpregs_save_area[9],
108 lc.gpregs_save_area[10], lc.gpregs_save_area[11],
109 lc.gpregs_save_area[12], lc.gpregs_save_area[13],
110 lc.gpregs_save_area[14], lc.gpregs_save_area[15]);
111 zxfoundation::sys::printk::pcrit(
112 "mcck: mcesa CRS:\n {:#018x} {:#018x} {:#018x} {:#018x}\n",
113 lc.cregs_save_area[0], lc.cregs_save_area[1],
114 lc.cregs_save_area[2], lc.cregs_save_area[3]);
115 zxfoundation::sys::printk::pcrit(
116 "mcck: mcesa — last_break={:#018x} prefix={:#010x} "
117 "fpc={:#010x} tod_pg={:#010x}\n",
118 lc.last_break_save_area, lc.prefixreg_save_area,
119 lc.fpt_creg_save_area, lc.tod_progreg_save_area);
120 zxfoundation::sys::printk::pcrit(
121 "mcck: mcesa — cpu_timer={:#010x}{:#010x} clock_comp="
122 "{:#010x}{:#010x}\n",
123 lc.cpu_timer_save_area[1], lc.cpu_timer_save_area[0],
124 lc.clock_comp_save_area[1], lc.clock_comp_save_area[0]);
125 }
126
127 [[nodiscard]] auto ipd_window_expired(
128 arch::s390x::cpu::percpu::percpu_data& pcp,
129 u64 now
130 ) noexcept -> bool {
131 return now >= pcp.ipd_window_deadline;
132 }
133
134 } // namespace detail
135
136 /// @brief Centralized machine-check dispatcher
137 /// @param[in] frame Saved interrupt frame from entry.S SAVE_FRAME on the
138 /// MCCK stack. Untouched by all fatal paths except via
139 /// dump_stack at the bottom of syschk_fatal.
140 auto handle(cpu::irq::irq_frame* frame) noexcept -> void {
141 auto* lc = cpu::lowcore::get_current();
142 const u64 mcic = lc->mcck_interruption_code;
143 if (mcic == 0ULL) return;
144
145 lc->mcck_interruption_code = 0ULL;
146
147 if ((mcic & MCIC_BACKLOG) != 0ULL) {
148 zxfoundation::sys::syschk::syschk_warning(
149 ::lib::kernel_error::from_machine(
150 ::lib::machine_error::backlog),
151 "mcck: backlog set — stacked machine-check detected; "
152 "prior MCCK not fully processed before this interruption");
153 }
154
155 if ((mcic & MCIC_SD) != 0ULL) {
156 if ((mcic & MCIC_AP) != 0ULL)
157 zxfoundation::sys::printk::pnotice(
158 "mcck: ancillary-report (AP) available\n");
159 zxfoundation::sys::syschk::syschk_fatal(
160 ::lib::kernel_error::from_machine(::lib::machine_error::system_damage),
161 "mcck: system damage — unrecoverable");
162 }
163
164 if ((mcic & MCIC_CD) != 0ULL) {
165 zxfoundation::sys::syschk::syschk_fatal(
166 ::lib::kernel_error::from_machine(::lib::machine_error::timing_facility),
167 "mcck: timing-facility damage — TOD / clock comparator stale");
168 }
169
170 if ((mcic & MCIC_PD) != 0ULL) {
171 if ((mcic & MCIC_B) != 0ULL) {
172 auto& pcp = cpu::lowcore::this_cpu_percpu();
173 const u64 now = static_cast<u64>(cpu::clock::stcke().tod);
174 if (detail::ipd_window_expired(pcp, now)) {
175 pcp.ipd_count = 0U;
176 pcp.ipd_window_deadline = now + MAX_IPD_WINDOW_TOD;
177 }
178 ++pcp.ipd_count;
179 if (pcp.ipd_count >= MAX_IPD_COUNT) {
180 zxfoundation::sys::syschk::syschk_critical(
181 ::lib::kernel_error::from_machine(
182 ::lib::machine_error::inst_process_damage),
183 "mcck: {} nullifying IPDs within 5 min — unrecoverable",
184 pcp.ipd_count);
185 } else {
186 zxfoundation::sys::syschk::syschk_notice(
187 ::lib::kernel_error::from_machine(
188 ::lib::machine_error::inst_process_damage),
189 "mcck: nullifying IPD ({} of {}) — survived",
190 pcp.ipd_count, MAX_IPD_COUNT);
191 }
192 } else {
193 // PD without B: straight fatal — instruction was not nullified.
194 zxfoundation::sys::syschk::syschk_critical(
195 ::lib::kernel_error::from_machine(
196 ::lib::machine_error::inst_process_damage),
197 "mcck: instruction-processing damage — unrecoverable");
198 }
199 }
200
201 if ((mcic & MCIC_ED) != 0ULL && (mcic & MCIC_EC) != 0ULL) {
202 const u32 ext_code = lc->external_damage_code;
203 zxfoundation::sys::syschk::syschk_warning(
204 ::lib::kernel_error::from_machine(::lib::machine_error::external_damage),
205 "mcck: external damage code={:#010x}", ext_code);
206 zxfoundation::sys::irq::deferred_set(
207 zxfoundation::sys::irq::DEFERRED_MCCK_PENDING_BIT);
208 }
209
210 if ((mcic & MCIC_SE) != 0ULL) {
211 zxfoundation::sys::syschk::syschk_fatal(
212 ::lib::kernel_error::from_machine(
213 ::lib::machine_error::storage_error_uncorrected),
214 "mcck: storage error uncorrected");
215 }
216 if ((mcic & MCIC_KE) != 0ULL) {
217 zxfoundation::sys::syschk::syschk_fatal(
218 ::lib::kernel_error::from_machine(
219 ::lib::machine_error::storage_key_error),
220 "mcck: storage-key error uncorrected");
221 }
222 if ((mcic & MCIC_DS) != 0ULL && (mcic & MCIC_FA) != 0ULL) {
223 const u64 fsa = lc->failing_storage_address;
224 zxfoundation::sys::syschk::syschk_fatal(
225 ::lib::kernel_error::from_machine(
226 ::lib::machine_error::storage_degradation),
227 "mcck: storage degradation at fsa={:#018x}", fsa);
228 } else if ((mcic & MCIC_DS) != 0ULL) {
229 // DS set but no FA validity — failing address is unreliable.
230 // Still fatal (storage degradation implies a poisoned frame), but
231 // we record the address-unknown status for forensics.
232 zxfoundation::sys::syschk::syschk_fatal(
233 ::lib::kernel_error::from_machine(
234 ::lib::machine_error::storage_degradation),
235 "mcck: storage degradation — failing address unknown");
236 }
237 if ((mcic & MCIC_FA) != 0ULL) {
238 const u64 fsa = lc->failing_storage_address;
239 zxfoundation::sys::syschk::syschk_warning(
240 ::lib::kernel_error::from_machine(
241 ::lib::machine_error::failing_storage_addr),
242 "mcck: failing storage address={:#018x}", fsa);
243 zxfoundation::sys::irq::deferred_set(
244 zxfoundation::sys::irq::DEFERRED_MCCK_PENDING_BIT);
245 }
246
247 if ((mcic & MCIC_SR) != 0ULL) {
248 zxfoundation::sys::syschk::syschk_notice(
249 ::lib::kernel_error::from_machine(::lib::machine_error::system_recovery),
250 "mcck: system recovery completed");
251 }
252 if ((mcic & MCIC_DG) != 0ULL) {
253 zxfoundation::sys::syschk::syschk_warning(
254 ::lib::kernel_error::from_machine(::lib::machine_error::degradation),
255 "mcck: degradation reported");
256 zxfoundation::sys::irq::deferred_set(
257 zxfoundation::sys::irq::DEFERRED_MCCK_PENDING_BIT);
258 }
259 if ((mcic & MCIC_W) != 0ULL) {
260 zxfoundation::sys::syschk::syschk_notice(
261 ::lib::kernel_error::from_machine(::lib::machine_error::warning),
262 "mcck: warning pending");
263 zxfoundation::sys::irq::deferred_set(
264 zxfoundation::sys::irq::DEFERRED_MCCK_PENDING_BIT);
265 }
266 if ((mcic & MCIC_CP) != 0ULL) {
267 zxfoundation::sys::syschk::syschk_notice(
268 ::lib::kernel_error::from_machine(::lib::machine_error::channel_report_pending),
269 "mcck: channel-report pending — CRW handling armed");
270 zxfoundation::sys::irq::deferred_set(
271 zxfoundation::sys::irq::DEFERRED_MCCK_PENDING_BIT);
272 }
273 if ((mcic & MCIC_SP) != 0ULL) {
274 zxfoundation::sys::syschk::syschk_warning(
275 ::lib::kernel_error::from_machine(
276 ::lib::machine_error::service_processor_damage),
277 "mcck: service-processor damage");
278 }
279 if ((mcic & MCIC_CK) != 0ULL) {
280 zxfoundation::sys::syschk::syschk_warning(
281 ::lib::kernel_error::from_machine(
282 ::lib::machine_error::channel_subsystem_damage),
283 "mcck: channel-subsystem damage");
284 }
285 if ((mcic & MCIC_SC) != 0ULL) {
286 zxfoundation::sys::printk::pinfo(
287 "mcck: storage error corrected (informational)\n");
288 }
289
290 if (!detail::nmi_registers_valid(mcic)) {
291 const bool in_user =
292 (frame->psw_mask & cpu::psw::PSW_BIT_PSTATE) != 0;
293 if (!in_user) {
294 detail::dump_mcesa(*lc);
295 zxfoundation::sys::syschk::syschk_fatal(
296 ::lib::kernel_error::from_machine(::lib::machine_error::system_damage),
297 "mcck: nmi registers invalid in nucleus — register state unreliable");
298 }
299 auto* d =
300 reinterpret_cast<zxfoundation::dgp::domain::domain*>(lc->current_domain);
301 if (d != nullptr) {
302 zxfoundation::dgp::signal::signal_enqueue(
303 *d,
304 zxfoundation::dgp::signal::signal_kind::fault,
305 static_cast<u64>(lc->failing_storage_address));
306 zxfoundation::sched::sched_resched_current();
307 }
308 detail::dump_mcesa(*lc);
309 }
310
311 zxfoundation::sys::irq::irq_dispatch(
312 zxfoundation::sys::irq::IRQ_BASE_MCCK, frame,
313 arch::s390x::cpu::irq::ZX_IRQ_CLASS_MCCK);
314 }
315
316} // namespace arch::s390x::trap::mcck
317
318} // end export