GCC Code Coverage Report


Directory: ./
File: Svc/WasmSequencer/WasmSequencerHost.cpp
Date: 2026-10-14 22:11:56
Exec Total Coverage
Lines: 28 252 11.1%
Functions: 1 25 4.0%
Branches: 13 103 12.6%

Line Branch Exec Source
1 // ======================================================================
2 // \title WasmSequencerHost.cpp
3 // \author tumbar
4 // \brief cpp file for WasmSequencer Wasm Host functions
5 // ======================================================================
6
7 #include <limits>
8
9 #include "Fw/Cmd/CmdResponseEnumAc.hpp"
10 #include "Fw/Log/LogSeverityEnumAc.hpp"
11 #include "Fw/Prm/ParamValidEnumAc.hpp"
12 #include "Fw/Tlm/TlmValidEnumAc.hpp"
13 #include "Fw/Types/Assert.hpp"
14 #include "Svc/Seq/BlockStateEnumAc.hpp"
15 #include "Svc/WasmSequencer/WasmSequencer.hpp"
16 #include "Svc/WasmSequencer/WasmSequencerComponentAc.hpp"
17 #include "Svc/WasmSequencer/WasmSequencer_HostFunctionEnumAc.hpp"
18 #include "config/FppConstantsAc.hpp"
19 #include "config/FwChanIdTypeAliasAc.h"
20 #include "config/FwPacketDescriptorTypeAliasAc.h"
21 #include "config/FwPrmIdTypeAliasAc.h"
22 #include "fprime.h"
23 #include "spacewasm.h"
24
25 // Guest ABI cross-checks
26 namespace {
27 //! True when a guest ABI ordinal equals its framework enum counterpart. The two are
28 //! unrelated enumeration types, so compare their widened values rather than the
29 //! enumerators (which -Wenum-compare rejects).
30 template <typename GuestEnum, typename FrameworkEnum>
31 constexpr bool sameOrdinal(GuestEnum guest, FrameworkEnum framework) {
32 return static_cast<I32>(guest) == static_cast<I32>(framework);
33 }
34 } // namespace
35
36 static_assert(sameOrdinal(FPRIME_TLM_VALID, Fw::TlmValid::VALID), "guest FPRIME_TLM_* must match Fw::TlmValid");
37 static_assert(sameOrdinal(FPRIME_TLM_INVALID, Fw::TlmValid::INVALID), "guest FPRIME_TLM_* must match Fw::TlmValid");
38
39 static_assert(sameOrdinal(FPRIME_PARAM_UNINIT, Fw::ParamValid::UNINIT),
40 "guest FPRIME_PARAM_* must match Fw::ParamValid");
41 static_assert(sameOrdinal(FPRIME_PARAM_VALID, Fw::ParamValid::VALID), "guest FPRIME_PARAM_* must match Fw::ParamValid");
42 static_assert(sameOrdinal(FPRIME_PARAM_INVALID, Fw::ParamValid::INVALID),
43 "guest FPRIME_PARAM_* must match Fw::ParamValid");
44 static_assert(sameOrdinal(FPRIME_PARAM_DEFAULT, Fw::ParamValid::DEFAULT),
45 "guest FPRIME_PARAM_* must match Fw::ParamValid");
46
47 static_assert(sameOrdinal(FPRIME_CMD_OK, Fw::CmdResponse::OK), "guest FPRIME_CMD_* must match Fw::CmdResponse");
48 static_assert(sameOrdinal(FPRIME_CMD_INVALID_OPCODE, Fw::CmdResponse::INVALID_OPCODE),
49 "guest FPRIME_CMD_* must match Fw::CmdResponse");
50 static_assert(sameOrdinal(FPRIME_CMD_VALIDATION_ERROR, Fw::CmdResponse::VALIDATION_ERROR),
51 "guest FPRIME_CMD_* must match Fw::CmdResponse");
52 static_assert(sameOrdinal(FPRIME_CMD_FORMAT_ERROR, Fw::CmdResponse::FORMAT_ERROR),
53 "guest FPRIME_CMD_* must match Fw::CmdResponse");
54 static_assert(sameOrdinal(FPRIME_CMD_EXECUTION_ERROR, Fw::CmdResponse::EXECUTION_ERROR),
55 "guest FPRIME_CMD_* must match Fw::CmdResponse");
56 static_assert(sameOrdinal(FPRIME_CMD_BUSY, Fw::CmdResponse::BUSY), "guest FPRIME_CMD_* must match Fw::CmdResponse");
57 static_assert(sameOrdinal(FPRIME_CMD_CLEARED, Fw::CmdResponse::CLEARED),
58 "guest FPRIME_CMD_* must match Fw::CmdResponse");
59
60 static_assert(sameOrdinal(FPRIME_EVENT_FATAL, Fw::LogSeverity::FATAL),
61 "guest FPRIME_EVENT_* must match Fw::LogSeverity");
62 static_assert(sameOrdinal(FPRIME_EVENT_WARNING_HI, Fw::LogSeverity::WARNING_HI),
63 "guest FPRIME_EVENT_* must match Fw::LogSeverity");
64 static_assert(sameOrdinal(FPRIME_EVENT_WARNING_LO, Fw::LogSeverity::WARNING_LO),
65 "guest FPRIME_EVENT_* must match Fw::LogSeverity");
66 static_assert(sameOrdinal(FPRIME_EVENT_COMMAND, Fw::LogSeverity::COMMAND),
67 "guest FPRIME_EVENT_* must match Fw::LogSeverity");
68 static_assert(sameOrdinal(FPRIME_EVENT_ACTIVITY_HI, Fw::LogSeverity::ACTIVITY_HI),
69 "guest FPRIME_EVENT_* must match Fw::LogSeverity");
70 static_assert(sameOrdinal(FPRIME_EVENT_ACTIVITY_LO, Fw::LogSeverity::ACTIVITY_LO),
71 "guest FPRIME_EVENT_* must match Fw::LogSeverity");
72 static_assert(sameOrdinal(FPRIME_EVENT_DIAGNOSTIC, Fw::LogSeverity::DIAGNOSTIC),
73 "guest FPRIME_EVENT_* must match Fw::LogSeverity");
74
75 namespace Svc {
76
77 //! Member-function signature of a spacewasm host-function handler.
78 using HostFn = spacewasm_hostcall_result_t (WasmSequencer::*)(struct spacewasm_caller_t*,
79 const struct spacewasm_value_t*,
80 size_t,
81 struct spacewasm_value_t*);
82
83 //! Named trampoline adapting spacewasm's C host-function ABI to a member handler.
84 template <HostFn Handler>
85 ✗ static spacewasm_hostcall_result_t hostFunctionTrampoline(struct spacewasm_caller_t* caller,
86 void* userdata,
87 const struct spacewasm_value_t* params,
88 size_t n_params,
89 struct spacewasm_value_t* out_result) {
90 ✗ FW_ASSERT(userdata != nullptr);
91 ✗ return (static_cast<WasmSequencer*>(userdata)->*Handler)(caller, params, n_params, out_result);
92 }
93
94 1 void WasmSequencer ::hostFprimeV1(spacewasm_host_t* host) {
95 spacewasm_status_t status;
96 U32 module_idx;
97
1/1
✓ Branch 1 taken 1 times.
1 status = spacewasm_add_host_module(host, "fprime_v1", 12, 0, &module_idx);
98 1 FW_ASSERT(status == SPACEWASM_OK, status);
99
100
1/1
✓ Branch 1 taken 1 times.
1 status = spacewasm_add_host_function(host, module_idx, "exit", "i", "",
101 &hostFunctionTrampoline<&WasmSequencer::wasmExit>, this);
102 1 FW_ASSERT(status == SPACEWASM_OK, status);
103
104
1/1
✓ Branch 1 taken 1 times.
1 status = spacewasm_add_host_function(host, module_idx, "panic", "i", "",
105 &hostFunctionTrampoline<&WasmSequencer::wasmPanic>, this);
106 1 FW_ASSERT(status == SPACEWASM_OK, status);
107
108
1/1
✓ Branch 1 taken 1 times.
1 status = spacewasm_add_host_function(host, module_idx, "args", "ii", "i",
109 &hostFunctionTrampoline<&WasmSequencer::wasmArgs>, this);
110 1 FW_ASSERT(status == SPACEWASM_OK, status);
111
112
1/1
✓ Branch 1 taken 1 times.
1 status = spacewasm_add_host_function(host, module_idx, "time", "ii", "",
113 &hostFunctionTrampoline<&WasmSequencer::wasmTime>, this);
114 1 FW_ASSERT(status == SPACEWASM_OK, status);
115
116
1/1
✓ Branch 1 taken 1 times.
1 status = spacewasm_add_host_function(host, module_idx, "tlm", "Iiiii", "i",
117 &hostFunctionTrampoline<&WasmSequencer::wasmReadTelemetry>, this);
118 1 FW_ASSERT(status == SPACEWASM_OK, status);
119
120
1/1
✓ Branch 1 taken 1 times.
1 status = spacewasm_add_host_function(host, module_idx, "prm", "Iii", "i",
121 &hostFunctionTrampoline<&WasmSequencer::wasmReadParameter>, this);
122 1 FW_ASSERT(status == SPACEWASM_OK, status);
123
124
1/1
✓ Branch 1 taken 1 times.
1 status = spacewasm_add_host_function(host, module_idx, "cmd", "ii", "i",
125 &hostFunctionTrampoline<&WasmSequencer::wasmCommand>, this);
126 1 FW_ASSERT(status == SPACEWASM_OK, status);
127
128
1/1
✓ Branch 1 taken 1 times.
1 status = spacewasm_add_host_function(host, module_idx, "event", "iii", "",
129 &hostFunctionTrampoline<&WasmSequencer::wasmEvent>, this);
130 1 FW_ASSERT(status == SPACEWASM_OK, status);
131
132
1/1
✓ Branch 1 taken 1 times.
1 status = spacewasm_add_host_function(host, module_idx, "rsleep", "I", "",
133 &hostFunctionTrampoline<&WasmSequencer::wasmRsleep>, this);
134 1 FW_ASSERT(status == SPACEWASM_OK, status);
135
136
1/1
✓ Branch 1 taken 1 times.
1 status = spacewasm_add_host_function(host, module_idx, "asleep", "I", "",
137 &hostFunctionTrampoline<&WasmSequencer::wasmAsleep>, this);
138 1 FW_ASSERT(status == SPACEWASM_OK, status);
139
140
1/1
✓ Branch 1 taken 1 times.
1 status = spacewasm_add_host_function(host, module_idx, "serial_send", "iii", "",
141 &hostFunctionTrampoline<&WasmSequencer::wasmSerialOut>, this);
142 1 FW_ASSERT(status == SPACEWASM_OK, status);
143
144
1/1
✓ Branch 1 taken 1 times.
1 status = spacewasm_add_host_function(host, module_idx, "serial_recv", "iiiii", "i",
145 &hostFunctionTrampoline<&WasmSequencer::wasmSerialRecv>, this);
146 1 FW_ASSERT(status == SPACEWASM_OK, status);
147 1 }
148
149 ✗ spacewasm_hostcall_result_t WasmSequencer::wasmExit(spacewasm_caller_t* caller,
150 const spacewasm_value_t* params,
151 size_t n_params,
152 spacewasm_value_t*) {
153 ✗ FW_ASSERT(!this->m_pendingHostFunction.isPending());
154 ✗ FW_ASSERT(params != nullptr);
155 ✗ FW_ASSERT(n_params == 1, static_cast<FwAssertArgType>(n_params));
156
157 ✗ FW_ASSERT(params[0].tag == spacewasm_valtype_t::SPACEWASM_I32, params[0].tag);
158
159 ✗ this->m_exit.reason = WasmSequencer_ExitReason::HOST_EXIT;
160 ✗ this->m_exit.code = params[0].u.i32_;
161
162 ✗ return SPACEWASM_TRAP;
163 }
164
165 ✗ spacewasm_hostcall_result_t WasmSequencer::wasmPanic(spacewasm_caller_t* caller,
166 const spacewasm_value_t* params,
167 size_t n_params,
168 spacewasm_value_t*) {
169 ✗ FW_ASSERT(!this->m_pendingHostFunction.isPending());
170 ✗ FW_ASSERT(params != nullptr);
171 ✗ FW_ASSERT(n_params == 1, static_cast<FwAssertArgType>(n_params));
172
173 ✗ FW_ASSERT(params[0].tag == spacewasm_valtype_t::SPACEWASM_I32, params[0].tag);
174
175 ✗ this->m_exit.reason = WasmSequencer_ExitReason::HOST_PANIC;
176 ✗ this->m_exit.code = params[0].u.i32_;
177
178 ✗ return SPACEWASM_TRAP;
179 }
180
181 ✗ spacewasm_hostcall_result_t WasmSequencer::wasmArgs(spacewasm_caller_t* caller,
182 const spacewasm_value_t* params,
183 size_t n_params,
184 spacewasm_value_t* ret) {
185 ✗ FW_ASSERT(!this->m_pendingHostFunction.isPending());
186 ✗ FW_ASSERT(params != nullptr);
187 ✗ FW_ASSERT(n_params == 2, static_cast<FwAssertArgType>(n_params));
188
189 ✗ FW_ASSERT(params[0].tag == spacewasm_valtype_t::SPACEWASM_I32, params[0].tag);
190 ✗ FW_ASSERT(params[1].tag == spacewasm_valtype_t::SPACEWASM_I32, params[1].tag);
191 ✗ FW_ASSERT(ret != nullptr);
192
193 // Deprecated: this sequencer does not pass arguments to a sequence
194 ✗ ret->tag = spacewasm_valtype_t::SPACEWASM_I32;
195 ✗ ret->u.i32_ = 0;
196
197 ✗ return SPACEWASM_CONTINUE_SOME;
198 }
199
200 ✗ spacewasm_hostcall_result_t WasmSequencer::wasmTime(spacewasm_caller_t* caller,
201 const spacewasm_value_t* params,
202 size_t n_params,
203 spacewasm_value_t*) {
204 ✗ FW_ASSERT(!this->m_pendingHostFunction.isPending());
205 ✗ FW_ASSERT(params != nullptr);
206 ✗ FW_ASSERT(n_params == 2, static_cast<FwAssertArgType>(n_params));
207 ✗ FW_ASSERT(params[0].tag == spacewasm_valtype_t::SPACEWASM_I32, params[0].tag);
208 ✗ FW_ASSERT(params[1].tag == spacewasm_valtype_t::SPACEWASM_I32, params[1].tag);
209
210 ✗ const U32 time_ptr = static_cast<U32>(params[0].u.i32_);
211 ✗ const U32 time_size = static_cast<U32>(params[1].u.i32_);
212
213 spacewasm_hostcall_result_t return_status;
214 ✗ if (time_size < Fw::Time::SERIALIZED_SIZE) {
215 // We expect an exact match for serialized time
216 ✗ this->log_WARNING_HI_BufferTooSmall(WasmSequencer_HostFunction::TIME, time_size, Fw::Time::SERIALIZED_SIZE);
217 ✗ return_status = SPACEWASM_TRAP;
218 ✗ } else if (time_size > Fw::Time::SERIALIZED_SIZE) {
219 // We expect an exact match for serialized time
220 ✗ this->log_WARNING_HI_BufferTooLarge(WasmSequencer_HostFunction::TIME, time_size, Fw::Time::SERIALIZED_SIZE);
221 ✗ return_status = SPACEWASM_TRAP;
222 } else {
223 ✗ this->m_pendingHostFunction.kind = WasmSequencer_HostFunction::TIME;
224 ✗ this->m_pendingHostFunction.caller = caller;
225 ✗ this->m_pendingHostFunction.u.time.ptr = time_ptr;
226 ✗ this->m_pendingHostFunction.u.time.len = time_size;
227
228 // Always pause the interpreter to allow the state machine to process this request
229 ✗ return_status = SPACEWASM_PAUSE;
230 }
231
232 ✗ return return_status;
233 }
234
235 ✗ spacewasm_hostcall_result_t WasmSequencer::wasmReadTelemetry(spacewasm_caller_t* caller,
236 const spacewasm_value_t* params,
237 size_t n_params,
238 spacewasm_value_t*) {
239 ✗ FW_ASSERT(!this->m_pendingHostFunction.isPending());
240 // These are automatically validated by spacewasm so it should be safe to assert them
241 ✗ FW_ASSERT(params != nullptr);
242 ✗ FW_ASSERT(n_params == 5, static_cast<FwAssertArgType>(n_params));
243
244 // FwChanIdType always in 64-bits
245 ✗ FW_ASSERT(params[0].tag == spacewasm_valtype_t::SPACEWASM_I64, params[0].tag);
246 ✗ FW_ASSERT(params[1].tag == spacewasm_valtype_t::SPACEWASM_I32, params[1].tag);
247 ✗ FW_ASSERT(params[2].tag == spacewasm_valtype_t::SPACEWASM_I32, params[2].tag);
248 ✗ FW_ASSERT(params[3].tag == spacewasm_valtype_t::SPACEWASM_I32, params[3].tag);
249 ✗ FW_ASSERT(params[4].tag == spacewasm_valtype_t::SPACEWASM_I32, params[4].tag);
250
251 ✗ I64 id = params[0].u.i64_;
252 ✗ const U32 time_ptr = static_cast<U32>(params[1].u.i32_);
253 ✗ const U32 time_size = static_cast<U32>(params[2].u.i32_);
254 ✗ const U32 value_ptr = static_cast<U32>(params[3].u.i32_);
255 ✗ const U32 value_size = static_cast<U32>(params[4].u.i32_);
256
257 // The telemetry port is a plain (not required) output port, so guard against an
258 // unconnected deployment before dispatching; the generated invoker would FW_ASSERT.
259 ✗ if (!this->isConnected_getTlmChan_OutputPort(0)) {
260 ✗ this->log_WARNING_HI_HostFunctionInvalidPort(WasmSequencer_HostFunction::TELEMETRY, 0, 0);
261 ✗ return SPACEWASM_TRAP;
262 }
263
264 spacewasm_hostcall_result_t return_status;
265 ✗ if (time_size < Fw::Time::SERIALIZED_SIZE) {
266 // We expect an exact match for serialized time
267 ✗ this->log_WARNING_HI_BufferTooSmall(WasmSequencer_HostFunction::TELEMETRY, time_size,
268 Fw::Time::SERIALIZED_SIZE);
269 ✗ return_status = SPACEWASM_TRAP;
270 ✗ } else if (time_size > Fw::Time::SERIALIZED_SIZE) {
271 // We expect an exact match for serialized time
272 ✗ this->log_WARNING_HI_BufferTooLarge(WasmSequencer_HostFunction::TELEMETRY, time_size,
273 Fw::Time::SERIALIZED_SIZE);
274 ✗ return_status = SPACEWASM_TRAP;
275 ✗ } else if (id < 0 || static_cast<U64>(id) > static_cast<U64>(std::numeric_limits<FwChanIdType>::max())) {
276 // A guest id that does not fit FwChanIdType would silently alias a different,
277 // valid channel if cast directly. Reject it instead of truncating.
278 ✗ this->log_WARNING_HI_HostFunctionInvalidId(WasmSequencer_HostFunction::TELEMETRY, id);
279 ✗ return_status = SPACEWASM_TRAP;
280 } else {
281 ✗ this->m_pendingHostFunction.kind = WasmSequencer_HostFunction::TELEMETRY;
282 ✗ this->m_pendingHostFunction.caller = caller;
283 ✗ this->m_pendingHostFunction.u.telemetry.chanId = static_cast<FwChanIdType>(id);
284 ✗ this->m_pendingHostFunction.u.telemetry.timePtr = time_ptr;
285 ✗ this->m_pendingHostFunction.u.telemetry.timeLen = time_size;
286 ✗ this->m_pendingHostFunction.u.telemetry.valuePtr = value_ptr;
287 ✗ this->m_pendingHostFunction.u.telemetry.valueLen = value_size;
288
289 // Always pause the interpreter to allow the state machine to process this request
290 ✗ return_status = SPACEWASM_PAUSE;
291 }
292
293 ✗ return return_status;
294 }
295
296 ✗ spacewasm_hostcall_result_t WasmSequencer::wasmReadParameter(spacewasm_caller_t* caller,
297 const spacewasm_value_t* params,
298 size_t n_params,
299 spacewasm_value_t*) {
300 ✗ FW_ASSERT(!this->m_pendingHostFunction.isPending());
301 // These are automatically validated by spacewasm so it should be safe to assert them
302 ✗ FW_ASSERT(params != nullptr);
303 ✗ FW_ASSERT(n_params == 3, static_cast<FwAssertArgType>(n_params));
304
305 // FwPrmIdType always in 64-bits
306 ✗ FW_ASSERT(params[0].tag == spacewasm_valtype_t::SPACEWASM_I64, params[0].tag);
307 ✗ FW_ASSERT(params[1].tag == spacewasm_valtype_t::SPACEWASM_I32, params[1].tag);
308 ✗ FW_ASSERT(params[2].tag == spacewasm_valtype_t::SPACEWASM_I32, params[2].tag);
309
310 ✗ I64 id = params[0].u.i64_;
311 ✗ const U32 ptr = static_cast<U32>(params[1].u.i32_);
312 ✗ const U32 len = static_cast<U32>(params[2].u.i32_);
313
314 // The parameter port is a plain (not required) output port, so guard against an
315 // unconnected deployment before dispatching; the generated invoker would FW_ASSERT.
316 ✗ if (!this->isConnected_getParam_OutputPort(0)) {
317 ✗ this->log_WARNING_HI_HostFunctionInvalidPort(WasmSequencer_HostFunction::PARAMETER, 0, 0);
318 ✗ return SPACEWASM_TRAP;
319 }
320
321 ✗ if (id < 0 || static_cast<U64>(id) > static_cast<U64>(std::numeric_limits<FwPrmIdType>::max())) {
322 // A guest id that does not fit FwPrmIdType would silently alias a different,
323 // valid parameter if cast directly. Reject it instead of truncating. Compare in U64 so the
324 // bound holds even if FwPrmIdType is configured wider than I64's positive range.
325 ✗ this->log_WARNING_HI_HostFunctionInvalidId(WasmSequencer_HostFunction::PARAMETER, id);
326 ✗ return SPACEWASM_TRAP;
327 }
328
329 ✗ this->m_pendingHostFunction.kind = WasmSequencer_HostFunction::PARAMETER;
330 ✗ this->m_pendingHostFunction.caller = caller;
331 ✗ this->m_pendingHostFunction.u.parameter.prmId = static_cast<FwPrmIdType>(id);
332 ✗ this->m_pendingHostFunction.u.parameter.ptr = ptr;
333 ✗ this->m_pendingHostFunction.u.parameter.len = len;
334
335 // Always pause the interpreter to allow the state machine to process this request
336 ✗ return SPACEWASM_PAUSE;
337 }
338
339 ✗ spacewasm_hostcall_result_t WasmSequencer::wasmCommand(struct spacewasm_caller_t* caller,
340 const struct spacewasm_value_t* params,
341 size_t n_params,
342 struct spacewasm_value_t* out_result) {
343 ✗ FW_ASSERT(!this->m_pendingHostFunction.isPending());
344 // These are automatically validated by spacewasm so it should be safe to assert them
345 ✗ FW_ASSERT(params != nullptr);
346 ✗ FW_ASSERT(n_params == 2, static_cast<FwAssertArgType>(n_params));
347 ✗ FW_ASSERT(params[0].tag == spacewasm_valtype_t::SPACEWASM_I32, params[0].tag);
348 ✗ FW_ASSERT(params[1].tag == spacewasm_valtype_t::SPACEWASM_I32, params[1].tag);
349
350 ✗ const U32 ptr = static_cast<U32>(params[0].u.i32_);
351 ✗ const U32 len = static_cast<U32>(params[1].u.i32_);
352
353 // cmdOut is a plain (not required) output port, so guard against an unconnected
354 // deployment before dispatching; the generated invoker would FW_ASSERT.
355 ✗ if (!this->isConnected_cmdOut_OutputPort(0)) {
356 ✗ this->log_WARNING_HI_HostFunctionInvalidPort(WasmSequencer_HostFunction::COMMAND, 0, 0);
357 ✗ return SPACEWASM_TRAP;
358 }
359
360 ✗ constexpr const U32 maxPayload = FW_COM_BUFFER_MAX_SIZE - sizeof(FwPacketDescriptorType);
361 spacewasm_hostcall_result_t return_status;
362 ✗ if (len > maxPayload) {
363 ✗ this->log_WARNING_HI_BufferTooLarge(WasmSequencer_HostFunction::COMMAND, len, maxPayload);
364 ✗ return_status = SPACEWASM_TRAP;
365 } else {
366 ✗ this->m_pendingHostFunction.kind = WasmSequencer_HostFunction::COMMAND;
367 ✗ this->m_pendingHostFunction.caller = caller;
368 ✗ this->m_pendingHostFunction.u.command.ptr = ptr;
369 ✗ this->m_pendingHostFunction.u.command.len = len;
370
371 // Always pause the interpreter to allow the state machine to process this request
372 ✗ return_status = SPACEWASM_PAUSE;
373 }
374
375 ✗ return return_status;
376 }
377
378 ✗ spacewasm_hostcall_result_t WasmSequencer::wasmEvent(spacewasm_caller_t* caller,
379 const spacewasm_value_t* params,
380 size_t n_params,
381 spacewasm_value_t*) {
382 ✗ FW_ASSERT(!this->m_pendingHostFunction.isPending());
383 // These are automatically validated by spacewasm so it should be safe to assert them
384 ✗ FW_ASSERT(params != nullptr);
385 ✗ FW_ASSERT(n_params == 3, static_cast<FwAssertArgType>(n_params));
386 ✗ FW_ASSERT(params[0].tag == spacewasm_valtype_t::SPACEWASM_I32, params[0].tag);
387 ✗ FW_ASSERT(params[1].tag == spacewasm_valtype_t::SPACEWASM_I32, params[1].tag);
388 ✗ FW_ASSERT(params[2].tag == spacewasm_valtype_t::SPACEWASM_I32, params[2].tag);
389
390 // The requested severity is validated when the event is dispatched (see the
391 // EVENT case in dispatchPendingHostFunction). A forbidden or out-of-range
392 // severity is reported (with the guest message) rather than trapping, so we
393 // read the message here in all cases and stash the raw id.
394 ✗ U32 ptr = static_cast<U32>(params[1].u.i32_);
395 ✗ U32 len = static_cast<U32>(params[2].u.i32_);
396 ✗ if (len > FW_LOG_STRING_MAX_SIZE) {
397 ✗ len = FW_LOG_STRING_MAX_SIZE;
398 }
399
400 // Pend this event dispatch to be executed by the sequencer state machine
401 ✗ this->m_pendingHostFunction.kind = WasmSequencer_HostFunction::EVENT;
402 ✗ this->m_pendingHostFunction.caller = caller;
403 ✗ this->m_pendingHostFunction.u.event.rawSeverity = static_cast<U32>(params[0].u.i32_);
404 ✗ this->m_pendingHostFunction.u.event.msgPtr = ptr;
405 ✗ this->m_pendingHostFunction.u.event.msgLen = len;
406
407 ✗ return SPACEWASM_PAUSE;
408 }
409
410 ✗ spacewasm_hostcall_result_t WasmSequencer::wasmRsleep(spacewasm_caller_t* caller,
411 const spacewasm_value_t* params,
412 size_t n_params,
413 spacewasm_value_t*) {
414 ✗ FW_ASSERT(!this->m_pendingHostFunction.isPending());
415 ✗ FW_ASSERT(params != nullptr);
416 ✗ FW_ASSERT(n_params == 1, static_cast<FwAssertArgType>(n_params));
417 ✗ FW_ASSERT(params[0].tag == spacewasm_valtype_t::SPACEWASM_I64, params[0].tag);
418
419 ✗ const U64 us = static_cast<U64>(params[0].u.i64_);
420
421 // The seconds part of the duration must fit the U32 fields of Fw::Time; a
422 // guest value large enough to overflow it would silently truncate. Reject it.
423 ✗ if ((us / 1000000u) > static_cast<U64>(std::numeric_limits<U32>::max())) {
424 ✗ this->log_WARNING_HI_SleepDurationTooLarge(WasmSequencer_HostFunction::RSLEEP, us);
425 ✗ return SPACEWASM_TRAP;
426 }
427
428 ✗ this->m_pendingHostFunction.kind = WasmSequencer_HostFunction::RSLEEP;
429 ✗ this->m_pendingHostFunction.caller = caller;
430 ✗ this->m_pendingHostFunction.u.rsleep.us = us;
431
432 ✗ return SPACEWASM_PAUSE;
433 }
434
435 ✗ spacewasm_hostcall_result_t WasmSequencer::wasmAsleep(spacewasm_caller_t* caller,
436 const spacewasm_value_t* params,
437 size_t n_params,
438 spacewasm_value_t*) {
439 ✗ FW_ASSERT(!this->m_pendingHostFunction.isPending());
440 ✗ FW_ASSERT(params != nullptr);
441 ✗ FW_ASSERT(n_params == 1, static_cast<FwAssertArgType>(n_params));
442 ✗ FW_ASSERT(params[0].tag == spacewasm_valtype_t::SPACEWASM_I64, params[0].tag);
443
444 ✗ const U64 us = static_cast<U64>(params[0].u.i64_);
445
446 // The seconds part of the duration must fit the U32 fields of Fw::Time; a
447 // guest value large enough to overflow it would silently truncate. Reject it.
448 ✗ if ((us / 1000000u) > static_cast<U64>(std::numeric_limits<U32>::max())) {
449 ✗ this->log_WARNING_HI_SleepDurationTooLarge(WasmSequencer_HostFunction::ASLEEP, us);
450 ✗ return SPACEWASM_TRAP;
451 }
452
453 ✗ this->m_pendingHostFunction.kind = WasmSequencer_HostFunction::ASLEEP;
454 ✗ this->m_pendingHostFunction.caller = caller;
455 ✗ this->m_pendingHostFunction.u.asleep.us = us;
456
457 ✗ return SPACEWASM_PAUSE;
458 }
459
460 ✗ spacewasm_hostcall_result_t WasmSequencer::wasmSerialOut(spacewasm_caller_t* caller,
461 const spacewasm_value_t* params,
462 size_t n_params,
463 spacewasm_value_t*) {
464 ✗ FW_ASSERT(!this->m_pendingHostFunction.isPending());
465 // These are automatically validated by spacewasm so it should be safe to assert them
466 ✗ FW_ASSERT(params != nullptr);
467 ✗ FW_ASSERT(n_params == 3, static_cast<FwAssertArgType>(n_params));
468 ✗ FW_ASSERT(params[0].tag == spacewasm_valtype_t::SPACEWASM_I32, params[0].tag);
469 ✗ FW_ASSERT(params[1].tag == spacewasm_valtype_t::SPACEWASM_I32, params[1].tag);
470 ✗ FW_ASSERT(params[2].tag == spacewasm_valtype_t::SPACEWASM_I32, params[2].tag);
471
472 ✗ const I32 index = params[0].u.i32_;
473 ✗ const U32 ptr = static_cast<U32>(params[1].u.i32_);
474 ✗ const U32 len = static_cast<U32>(params[2].u.i32_);
475
476 // Reject if the payload does not fit the copy-out buffer. A serialOut buffer that was never
477 // configured (serialOutMaxSize == 0 in configure()) is null-backed with capacity 0; trap even a
478 // zero-length send in that case rather than forwarding a null-backed buffer to serialOut_out.
479 ✗ if (len > this->m_serialOutBuffer.getCapacity() || this->m_serialOutBuffer.getBuffAddr() == nullptr) {
480 ✗ this->log_WARNING_HI_BufferTooLarge(WasmSequencer_HostFunction::SERIAL_OUT, len,
481 ✗ static_cast<U32>(this->m_serialOutBuffer.getCapacity()));
482 ✗ return SPACEWASM_TRAP;
483 }
484
485 ✗ if (index < 0 || index >= this->getNum_serialOut_OutputPorts() ||
486 ✗ !this->isConnected_serialOut_OutputPort(static_cast<FwIndexType>(index))) {
487 ✗ this->log_WARNING_HI_HostFunctionInvalidPort(WasmSequencer_HostFunction::SERIAL_OUT, index,
488 ✗ static_cast<U32>(this->getNum_serialOut_OutputPorts()));
489 ✗ return SPACEWASM_TRAP;
490 }
491
492 ✗ this->m_pendingHostFunction.kind = WasmSequencer_HostFunction::SERIAL_OUT;
493 ✗ this->m_pendingHostFunction.caller = caller;
494 ✗ this->m_pendingHostFunction.u.serialOut.index = static_cast<U32>(index);
495 ✗ this->m_pendingHostFunction.u.serialOut.ptr = ptr;
496 ✗ this->m_pendingHostFunction.u.serialOut.len = len;
497
498 // Always pause the interpreter to allow the state machine to process this request
499 ✗ return SPACEWASM_PAUSE;
500 }
501
502 ✗ spacewasm_hostcall_result_t WasmSequencer::wasmSerialRecv(spacewasm_caller_t* caller,
503 const spacewasm_value_t* params,
504 size_t n_params,
505 spacewasm_value_t*) {
506 ✗ FW_ASSERT(!this->m_pendingHostFunction.isPending());
507 // These are automatically validated by spacewasm so it should be safe to assert them
508 ✗ FW_ASSERT(params != nullptr);
509 ✗ FW_ASSERT(n_params == 5, static_cast<FwAssertArgType>(n_params));
510 ✗ FW_ASSERT(params[0].tag == spacewasm_valtype_t::SPACEWASM_I32, params[0].tag);
511 ✗ FW_ASSERT(params[1].tag == spacewasm_valtype_t::SPACEWASM_I32, params[1].tag);
512 ✗ FW_ASSERT(params[2].tag == spacewasm_valtype_t::SPACEWASM_I32, params[2].tag);
513 ✗ FW_ASSERT(params[3].tag == spacewasm_valtype_t::SPACEWASM_I32, params[3].tag);
514 ✗ FW_ASSERT(params[4].tag == spacewasm_valtype_t::SPACEWASM_I32, params[4].tag);
515
516 ✗ const I32 index = params[0].u.i32_;
517 ✗ const U32 data_ptr = static_cast<U32>(params[1].u.i32_);
518 ✗ const U32 data_len = static_cast<U32>(params[2].u.i32_);
519 ✗ const U32 actual_size_ptr = static_cast<U32>(params[3].u.i32_);
520 ✗ const I32 block_type = params[4].u.i32_;
521
522 ✗ if (index < 0 || index >= NUM_SERIALIN_INPUT_PORTS) {
523 ✗ this->log_WARNING_HI_HostFunctionInvalidPort(WasmSequencer_HostFunction::SERIAL_RECV, index,
524 NUM_SERIALIN_INPUT_PORTS);
525 ✗ return SPACEWASM_TRAP;
526 }
527
528 ✗ if (block_type < 0 || block_type > static_cast<I32>(std::numeric_limits<Svc::BlockState::SerialType>::max()) ||
529 ✗ !Svc::BlockState::isValid(static_cast<Svc::BlockState::SerialType>(block_type))) {
530 ✗ this->log_WARNING_HI_InvalidBlockingTypeValue(block_type);
531 ✗ return SPACEWASM_TRAP;
532 }
533
534 ✗ this->m_pendingHostFunction.kind = WasmSequencer_HostFunction::SERIAL_RECV;
535 ✗ this->m_pendingHostFunction.caller = caller;
536 ✗ this->m_pendingHostFunction.u.serialRecv.index = static_cast<U32>(index);
537 ✗ this->m_pendingHostFunction.u.serialRecv.dataPtr = data_ptr;
538 ✗ this->m_pendingHostFunction.u.serialRecv.dataSize = data_len;
539 ✗ this->m_pendingHostFunction.u.serialRecv.actualSizePtr = actual_size_ptr;
540 ✗ this->m_pendingHostFunction.u.serialRecv.blockingType = static_cast<Svc::BlockState::T>(block_type);
541
542 // Always pause the interpreter to allow the state machine to process this request
543 ✗ return SPACEWASM_PAUSE;
544 }
545
546 } // namespace Svc
547