GCC Code Coverage Report


Directory: Svc/WasmSequencer/
File: WasmSequencerHost.cpp
Date: 2026-09-23 21:15:03
Exec Total Coverage
Lines: 256 256 100.0%
Functions: 25 25 100.0%
Branches: 98 103 95.1%

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 188 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 188 FW_ASSERT(userdata != nullptr);
91 188 return (static_cast<WasmSequencer*>(userdata)->*Handler)(caller, params, n_params, out_result);
92 }
93
94 339 void WasmSequencer ::hostFprimeV1(spacewasm_host_t* host) {
95 spacewasm_status_t status;
96 339 U32 module_idx;
97
1/1
✓ Branch 1 taken 339 times.
339 status = spacewasm_add_host_module(host, "fprime_v1", 12, 0, &module_idx);
98 339 FW_ASSERT(status == SPACEWASM_OK, status);
99
100
1/1
✓ Branch 1 taken 339 times.
339 status = spacewasm_add_host_function(host, module_idx, "exit", "i", "",
101 &hostFunctionTrampoline<&WasmSequencer::wasmExit>, this);
102 339 FW_ASSERT(status == SPACEWASM_OK, status);
103
104
1/1
✓ Branch 1 taken 339 times.
339 status = spacewasm_add_host_function(host, module_idx, "panic", "i", "",
105 &hostFunctionTrampoline<&WasmSequencer::wasmPanic>, this);
106 339 FW_ASSERT(status == SPACEWASM_OK, status);
107
108
1/1
✓ Branch 1 taken 339 times.
339 status = spacewasm_add_host_function(host, module_idx, "args", "ii", "i",
109 &hostFunctionTrampoline<&WasmSequencer::wasmArgs>, this);
110 339 FW_ASSERT(status == SPACEWASM_OK, status);
111
112
1/1
✓ Branch 1 taken 339 times.
339 status = spacewasm_add_host_function(host, module_idx, "time", "ii", "",
113 &hostFunctionTrampoline<&WasmSequencer::wasmTime>, this);
114 339 FW_ASSERT(status == SPACEWASM_OK, status);
115
116
1/1
✓ Branch 1 taken 339 times.
339 status = spacewasm_add_host_function(host, module_idx, "tlm", "Iiiii", "i",
117 &hostFunctionTrampoline<&WasmSequencer::wasmReadTelemetry>, this);
118 339 FW_ASSERT(status == SPACEWASM_OK, status);
119
120
1/1
✓ Branch 1 taken 339 times.
339 status = spacewasm_add_host_function(host, module_idx, "prm", "Iii", "i",
121 &hostFunctionTrampoline<&WasmSequencer::wasmReadParameter>, this);
122 339 FW_ASSERT(status == SPACEWASM_OK, status);
123
124
1/1
✓ Branch 1 taken 339 times.
339 status = spacewasm_add_host_function(host, module_idx, "cmd", "ii", "i",
125 &hostFunctionTrampoline<&WasmSequencer::wasmCommand>, this);
126 339 FW_ASSERT(status == SPACEWASM_OK, status);
127
128
1/1
✓ Branch 1 taken 339 times.
339 status = spacewasm_add_host_function(host, module_idx, "event", "iii", "",
129 &hostFunctionTrampoline<&WasmSequencer::wasmEvent>, this);
130 339 FW_ASSERT(status == SPACEWASM_OK, status);
131
132
1/1
✓ Branch 1 taken 339 times.
339 status = spacewasm_add_host_function(host, module_idx, "rsleep", "I", "",
133 &hostFunctionTrampoline<&WasmSequencer::wasmRsleep>, this);
134 339 FW_ASSERT(status == SPACEWASM_OK, status);
135
136
1/1
✓ Branch 1 taken 339 times.
339 status = spacewasm_add_host_function(host, module_idx, "asleep", "I", "",
137 &hostFunctionTrampoline<&WasmSequencer::wasmAsleep>, this);
138 339 FW_ASSERT(status == SPACEWASM_OK, status);
139
140
1/1
✓ Branch 1 taken 339 times.
339 status = spacewasm_add_host_function(host, module_idx, "serial_send", "iii", "",
141 &hostFunctionTrampoline<&WasmSequencer::wasmSerialOut>, this);
142 339 FW_ASSERT(status == SPACEWASM_OK, status);
143
144
1/1
✓ Branch 1 taken 339 times.
339 status = spacewasm_add_host_function(host, module_idx, "serial_recv", "iiiii", "i",
145 &hostFunctionTrampoline<&WasmSequencer::wasmSerialRecv>, this);
146 339 FW_ASSERT(status == SPACEWASM_OK, status);
147 339 }
148
149 4 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 4 FW_ASSERT(!this->m_pendingHostFunction.isPending());
154 4 FW_ASSERT(params != nullptr);
155 4 FW_ASSERT(n_params == 1, static_cast<FwAssertArgType>(n_params));
156
157 4 FW_ASSERT(params[0].tag == spacewasm_valtype_t::SPACEWASM_I32, params[0].tag);
158
159 4 this->m_exit.reason = WasmSequencer_ExitReason::HOST_EXIT;
160 4 this->m_exit.code = params[0].u.i32_;
161
162 4 return SPACEWASM_TRAP;
163 }
164
165 2 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 2 FW_ASSERT(!this->m_pendingHostFunction.isPending());
170 2 FW_ASSERT(params != nullptr);
171 2 FW_ASSERT(n_params == 1, static_cast<FwAssertArgType>(n_params));
172
173 2 FW_ASSERT(params[0].tag == spacewasm_valtype_t::SPACEWASM_I32, params[0].tag);
174
175 2 this->m_exit.reason = WasmSequencer_ExitReason::HOST_PANIC;
176 2 this->m_exit.code = params[0].u.i32_;
177
178 2 return SPACEWASM_TRAP;
179 }
180
181 7 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*) {
185 7 FW_ASSERT(!this->m_pendingHostFunction.isPending());
186 7 FW_ASSERT(params != nullptr);
187 7 FW_ASSERT(n_params == 2, static_cast<FwAssertArgType>(n_params));
188
189 7 FW_ASSERT(params[0].tag == spacewasm_valtype_t::SPACEWASM_I32, params[0].tag);
190 7 FW_ASSERT(params[1].tag == spacewasm_valtype_t::SPACEWASM_I32, params[1].tag);
191
192 7 const U32 ptr = static_cast<U32>(params[0].u.i32_);
193 7 const U32 size = static_cast<U32>(params[1].u.i32_);
194
195 7 this->m_pendingHostFunction.kind = WasmSequencer_HostFunction::ARGS;
196 7 this->m_pendingHostFunction.caller = caller;
197 7 this->m_pendingHostFunction.u.args.ptr = ptr;
198 7 this->m_pendingHostFunction.u.args.len = size;
199
200 7 return SPACEWASM_PAUSE;
201 }
202
203 4 spacewasm_hostcall_result_t WasmSequencer::wasmTime(spacewasm_caller_t* caller,
204 const spacewasm_value_t* params,
205 size_t n_params,
206 spacewasm_value_t*) {
207 4 FW_ASSERT(!this->m_pendingHostFunction.isPending());
208 4 FW_ASSERT(params != nullptr);
209 4 FW_ASSERT(n_params == 2, static_cast<FwAssertArgType>(n_params));
210 4 FW_ASSERT(params[0].tag == spacewasm_valtype_t::SPACEWASM_I32, params[0].tag);
211 4 FW_ASSERT(params[1].tag == spacewasm_valtype_t::SPACEWASM_I32, params[1].tag);
212
213 4 const U32 time_ptr = static_cast<U32>(params[0].u.i32_);
214 4 const U32 time_size = static_cast<U32>(params[1].u.i32_);
215
216 spacewasm_hostcall_result_t return_status;
217
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 3 times.
4 if (time_size < Fw::Time::SERIALIZED_SIZE) {
218 // We expect an exact match for serialized time
219
2/2
✓ Branch 6 taken 1 times.
✓ Branch 10 taken 1 times.
1 this->log_WARNING_HI_BufferTooSmall(WasmSequencer_HostFunction::TIME, time_size, Fw::Time::SERIALIZED_SIZE);
220 1 return_status = SPACEWASM_TRAP;
221
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 2 times.
3 } else if (time_size > Fw::Time::SERIALIZED_SIZE) {
222 // We expect an exact match for serialized time
223
2/2
✓ Branch 6 taken 1 times.
✓ Branch 10 taken 1 times.
1 this->log_WARNING_HI_BufferTooLarge(WasmSequencer_HostFunction::TIME, time_size, Fw::Time::SERIALIZED_SIZE);
224 1 return_status = SPACEWASM_TRAP;
225 } else {
226 2 this->m_pendingHostFunction.kind = WasmSequencer_HostFunction::TIME;
227 2 this->m_pendingHostFunction.caller = caller;
228 2 this->m_pendingHostFunction.u.time.ptr = time_ptr;
229 2 this->m_pendingHostFunction.u.time.len = time_size;
230
231 // Always pause the interpreter to allow the state machine to process this request
232 2 return_status = SPACEWASM_PAUSE;
233 }
234
235 4 return return_status;
236 }
237
238 11 spacewasm_hostcall_result_t WasmSequencer::wasmReadTelemetry(spacewasm_caller_t* caller,
239 const spacewasm_value_t* params,
240 size_t n_params,
241 spacewasm_value_t*) {
242 11 FW_ASSERT(!this->m_pendingHostFunction.isPending());
243 // These are automatically validated by spacewasm so it should be safe to assert them
244 11 FW_ASSERT(params != nullptr);
245 11 FW_ASSERT(n_params == 5, static_cast<FwAssertArgType>(n_params));
246
247 // FwChanIdType always in 64-bits
248 11 FW_ASSERT(params[0].tag == spacewasm_valtype_t::SPACEWASM_I64, params[0].tag);
249 11 FW_ASSERT(params[1].tag == spacewasm_valtype_t::SPACEWASM_I32, params[1].tag);
250 11 FW_ASSERT(params[2].tag == spacewasm_valtype_t::SPACEWASM_I32, params[2].tag);
251 11 FW_ASSERT(params[3].tag == spacewasm_valtype_t::SPACEWASM_I32, params[3].tag);
252 11 FW_ASSERT(params[4].tag == spacewasm_valtype_t::SPACEWASM_I32, params[4].tag);
253
254 11 I64 id = params[0].u.i64_;
255 11 const U32 time_ptr = static_cast<U32>(params[1].u.i32_);
256 11 const U32 time_size = static_cast<U32>(params[2].u.i32_);
257 11 const U32 value_ptr = static_cast<U32>(params[3].u.i32_);
258 11 const U32 value_size = static_cast<U32>(params[4].u.i32_);
259
260 // The telemetry port is a plain (not required) output port, so guard against an
261 // unconnected deployment before dispatching; the generated invoker would FW_ASSERT.
262
2/2
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 10 times.
11 if (!this->isConnected_getTlmChan_OutputPort(0)) {
263
2/2
✓ Branch 6 taken 1 times.
✓ Branch 10 taken 1 times.
1 this->log_WARNING_HI_HostFunctionInvalidPort(WasmSequencer_HostFunction::TELEMETRY, 0, 0);
264 1 return SPACEWASM_TRAP;
265 }
266
267 spacewasm_hostcall_result_t return_status;
268
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 9 times.
10 if (time_size < Fw::Time::SERIALIZED_SIZE) {
269 // We expect an exact match for serialized time
270
2/2
✓ Branch 6 taken 1 times.
✓ Branch 10 taken 1 times.
1 this->log_WARNING_HI_BufferTooSmall(WasmSequencer_HostFunction::TELEMETRY, time_size,
271 Fw::Time::SERIALIZED_SIZE);
272 1 return_status = SPACEWASM_TRAP;
273
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 8 times.
9 } else if (time_size > Fw::Time::SERIALIZED_SIZE) {
274 // We expect an exact match for serialized time
275
2/2
✓ Branch 6 taken 1 times.
✓ Branch 10 taken 1 times.
1 this->log_WARNING_HI_BufferTooLarge(WasmSequencer_HostFunction::TELEMETRY, time_size,
276 Fw::Time::SERIALIZED_SIZE);
277 1 return_status = SPACEWASM_TRAP;
278
6/6
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 1 times.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 6 times.
✓ Branch 5 taken 2 times.
✓ Branch 6 taken 6 times.
8 } else if (id < 0 || static_cast<U64>(id) > static_cast<U64>(std::numeric_limits<FwChanIdType>::max())) {
279 // A guest id that does not fit FwChanIdType would silently alias a different,
280 // valid channel if cast directly. Reject it instead of truncating.
281
2/2
✓ Branch 6 taken 2 times.
✓ Branch 10 taken 2 times.
2 this->log_WARNING_HI_HostFunctionInvalidId(WasmSequencer_HostFunction::TELEMETRY, id);
282 2 return_status = SPACEWASM_TRAP;
283 } else {
284 6 this->m_pendingHostFunction.kind = WasmSequencer_HostFunction::TELEMETRY;
285 6 this->m_pendingHostFunction.caller = caller;
286 6 this->m_pendingHostFunction.u.telemetry.chanId = static_cast<FwChanIdType>(id);
287 6 this->m_pendingHostFunction.u.telemetry.timePtr = time_ptr;
288 6 this->m_pendingHostFunction.u.telemetry.timeLen = time_size;
289 6 this->m_pendingHostFunction.u.telemetry.valuePtr = value_ptr;
290 6 this->m_pendingHostFunction.u.telemetry.valueLen = value_size;
291
292 // Always pause the interpreter to allow the state machine to process this request
293 6 return_status = SPACEWASM_PAUSE;
294 }
295
296 10 return return_status;
297 }
298
299 8 spacewasm_hostcall_result_t WasmSequencer::wasmReadParameter(spacewasm_caller_t* caller,
300 const spacewasm_value_t* params,
301 size_t n_params,
302 spacewasm_value_t*) {
303 8 FW_ASSERT(!this->m_pendingHostFunction.isPending());
304 // These are automatically validated by spacewasm so it should be safe to assert them
305 8 FW_ASSERT(params != nullptr);
306 8 FW_ASSERT(n_params == 3, static_cast<FwAssertArgType>(n_params));
307
308 // FwPrmIdType always in 64-bits
309 8 FW_ASSERT(params[0].tag == spacewasm_valtype_t::SPACEWASM_I64, params[0].tag);
310 8 FW_ASSERT(params[1].tag == spacewasm_valtype_t::SPACEWASM_I32, params[1].tag);
311 8 FW_ASSERT(params[2].tag == spacewasm_valtype_t::SPACEWASM_I32, params[2].tag);
312
313 8 I64 id = params[0].u.i64_;
314 8 const U32 ptr = static_cast<U32>(params[1].u.i32_);
315 8 const U32 len = static_cast<U32>(params[2].u.i32_);
316
317 // The parameter port is a plain (not required) output port, so guard against an
318 // unconnected deployment before dispatching; the generated invoker would FW_ASSERT.
319
2/2
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 7 times.
8 if (!this->isConnected_getParam_OutputPort(0)) {
320
2/2
✓ Branch 6 taken 1 times.
✓ Branch 10 taken 1 times.
1 this->log_WARNING_HI_HostFunctionInvalidPort(WasmSequencer_HostFunction::PARAMETER, 0, 0);
321 1 return SPACEWASM_TRAP;
322 }
323
324
6/6
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 1 times.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 2 times.
✓ Branch 6 taken 5 times.
7 if (id < 0 || static_cast<U64>(id) > static_cast<U64>(std::numeric_limits<FwPrmIdType>::max())) {
325 // A guest id that does not fit FwPrmIdType would silently alias a different,
326 // valid parameter if cast directly. Reject it instead of truncating. Compare in U64 so the
327 // bound holds even if FwPrmIdType is configured wider than I64's positive range.
328
2/2
✓ Branch 6 taken 2 times.
✓ Branch 10 taken 2 times.
2 this->log_WARNING_HI_HostFunctionInvalidId(WasmSequencer_HostFunction::PARAMETER, id);
329 2 return SPACEWASM_TRAP;
330 }
331
332 5 this->m_pendingHostFunction.kind = WasmSequencer_HostFunction::PARAMETER;
333 5 this->m_pendingHostFunction.caller = caller;
334 5 this->m_pendingHostFunction.u.parameter.prmId = static_cast<FwPrmIdType>(id);
335 5 this->m_pendingHostFunction.u.parameter.ptr = ptr;
336 5 this->m_pendingHostFunction.u.parameter.len = len;
337
338 // Always pause the interpreter to allow the state machine to process this request
339 5 return SPACEWASM_PAUSE;
340 }
341
342 20 spacewasm_hostcall_result_t WasmSequencer::wasmCommand(struct spacewasm_caller_t* caller,
343 const struct spacewasm_value_t* params,
344 size_t n_params,
345 struct spacewasm_value_t* out_result) {
346 20 FW_ASSERT(!this->m_pendingHostFunction.isPending());
347 // These are automatically validated by spacewasm so it should be safe to assert them
348 20 FW_ASSERT(params != nullptr);
349 20 FW_ASSERT(n_params == 2, static_cast<FwAssertArgType>(n_params));
350 20 FW_ASSERT(params[0].tag == spacewasm_valtype_t::SPACEWASM_I32, params[0].tag);
351 20 FW_ASSERT(params[1].tag == spacewasm_valtype_t::SPACEWASM_I32, params[1].tag);
352
353 20 const U32 ptr = static_cast<U32>(params[0].u.i32_);
354 20 const U32 len = static_cast<U32>(params[1].u.i32_);
355
356 // cmdOut is a plain (not required) output port, so guard against an unconnected
357 // deployment before dispatching; the generated invoker would FW_ASSERT.
358
2/2
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 19 times.
20 if (!this->isConnected_cmdOut_OutputPort(0)) {
359
2/2
✓ Branch 6 taken 1 times.
✓ Branch 10 taken 1 times.
1 this->log_WARNING_HI_HostFunctionInvalidPort(WasmSequencer_HostFunction::COMMAND, 0, 0);
360 1 return SPACEWASM_TRAP;
361 }
362
363 19 constexpr const U32 maxPayload = FW_COM_BUFFER_MAX_SIZE - sizeof(FwPacketDescriptorType);
364 spacewasm_hostcall_result_t return_status;
365
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 18 times.
19 if (len > maxPayload) {
366
2/2
✓ Branch 6 taken 1 times.
✓ Branch 10 taken 1 times.
1 this->log_WARNING_HI_BufferTooLarge(WasmSequencer_HostFunction::COMMAND, len, maxPayload);
367 1 return_status = SPACEWASM_TRAP;
368 } else {
369 18 this->m_pendingHostFunction.kind = WasmSequencer_HostFunction::COMMAND;
370 18 this->m_pendingHostFunction.caller = caller;
371 18 this->m_pendingHostFunction.u.command.ptr = ptr;
372 18 this->m_pendingHostFunction.u.command.len = len;
373
374 // Always pause the interpreter to allow the state machine to process this request
375 18 return_status = SPACEWASM_PAUSE;
376 }
377
378 19 return return_status;
379 }
380
381 11 spacewasm_hostcall_result_t WasmSequencer::wasmEvent(spacewasm_caller_t* caller,
382 const spacewasm_value_t* params,
383 size_t n_params,
384 spacewasm_value_t*) {
385 11 FW_ASSERT(!this->m_pendingHostFunction.isPending());
386 // These are automatically validated by spacewasm so it should be safe to assert them
387 11 FW_ASSERT(params != nullptr);
388 11 FW_ASSERT(n_params == 3, static_cast<FwAssertArgType>(n_params));
389 11 FW_ASSERT(params[0].tag == spacewasm_valtype_t::SPACEWASM_I32, params[0].tag);
390 11 FW_ASSERT(params[1].tag == spacewasm_valtype_t::SPACEWASM_I32, params[1].tag);
391 11 FW_ASSERT(params[2].tag == spacewasm_valtype_t::SPACEWASM_I32, params[2].tag);
392
393 // The requested severity is validated when the event is dispatched (see the
394 // EVENT case in dispatchPendingHostFunction). A forbidden or out-of-range
395 // severity is reported (with the guest message) rather than trapping, so we
396 // read the message here in all cases and stash the raw id.
397 11 U32 ptr = static_cast<U32>(params[1].u.i32_);
398 11 U32 len = static_cast<U32>(params[2].u.i32_);
399
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 10 times.
11 if (len > FW_LOG_STRING_MAX_SIZE) {
400 1 len = FW_LOG_STRING_MAX_SIZE;
401 }
402
403 // Pend this event dispatch to be executed by the sequencer state machine
404 11 this->m_pendingHostFunction.kind = WasmSequencer_HostFunction::EVENT;
405 11 this->m_pendingHostFunction.caller = caller;
406 11 this->m_pendingHostFunction.u.event.rawSeverity = static_cast<U32>(params[0].u.i32_);
407 11 this->m_pendingHostFunction.u.event.msgPtr = ptr;
408 11 this->m_pendingHostFunction.u.event.msgLen = len;
409
410 11 return SPACEWASM_PAUSE;
411 }
412
413 5 spacewasm_hostcall_result_t WasmSequencer::wasmRsleep(spacewasm_caller_t* caller,
414 const spacewasm_value_t* params,
415 size_t n_params,
416 spacewasm_value_t*) {
417 5 FW_ASSERT(!this->m_pendingHostFunction.isPending());
418 5 FW_ASSERT(params != nullptr);
419 5 FW_ASSERT(n_params == 1, static_cast<FwAssertArgType>(n_params));
420 5 FW_ASSERT(params[0].tag == spacewasm_valtype_t::SPACEWASM_I64, params[0].tag);
421
422 5 const U64 us = static_cast<U64>(params[0].u.i64_);
423
424 // The seconds part of the duration must fit the U32 fields of Fw::Time; a
425 // guest value large enough to overflow it would silently truncate. Reject it.
426
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 4 times.
5 if ((us / 1000000u) > static_cast<U64>(std::numeric_limits<U32>::max())) {
427
2/2
✓ Branch 6 taken 1 times.
✓ Branch 10 taken 1 times.
1 this->log_WARNING_HI_SleepDurationTooLarge(WasmSequencer_HostFunction::RSLEEP, us);
428 1 return SPACEWASM_TRAP;
429 }
430
431 4 this->m_pendingHostFunction.kind = WasmSequencer_HostFunction::RSLEEP;
432 4 this->m_pendingHostFunction.caller = caller;
433 4 this->m_pendingHostFunction.u.rsleep.us = us;
434
435 4 return SPACEWASM_PAUSE;
436 }
437
438 2 spacewasm_hostcall_result_t WasmSequencer::wasmAsleep(spacewasm_caller_t* caller,
439 const spacewasm_value_t* params,
440 size_t n_params,
441 spacewasm_value_t*) {
442 2 FW_ASSERT(!this->m_pendingHostFunction.isPending());
443 2 FW_ASSERT(params != nullptr);
444 2 FW_ASSERT(n_params == 1, static_cast<FwAssertArgType>(n_params));
445 2 FW_ASSERT(params[0].tag == spacewasm_valtype_t::SPACEWASM_I64, params[0].tag);
446
447 2 const U64 us = static_cast<U64>(params[0].u.i64_);
448
449 // The seconds part of the duration must fit the U32 fields of Fw::Time; a
450 // guest value large enough to overflow it would silently truncate. Reject it.
451
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 1 times.
2 if ((us / 1000000u) > static_cast<U64>(std::numeric_limits<U32>::max())) {
452
2/2
✓ Branch 6 taken 1 times.
✓ Branch 10 taken 1 times.
1 this->log_WARNING_HI_SleepDurationTooLarge(WasmSequencer_HostFunction::ASLEEP, us);
453 1 return SPACEWASM_TRAP;
454 }
455
456 1 this->m_pendingHostFunction.kind = WasmSequencer_HostFunction::ASLEEP;
457 1 this->m_pendingHostFunction.caller = caller;
458 1 this->m_pendingHostFunction.u.asleep.us = us;
459
460 1 return SPACEWASM_PAUSE;
461 }
462
463 8 spacewasm_hostcall_result_t WasmSequencer::wasmSerialOut(spacewasm_caller_t* caller,
464 const spacewasm_value_t* params,
465 size_t n_params,
466 spacewasm_value_t*) {
467 8 FW_ASSERT(!this->m_pendingHostFunction.isPending());
468 // These are automatically validated by spacewasm so it should be safe to assert them
469 8 FW_ASSERT(params != nullptr);
470 8 FW_ASSERT(n_params == 3, static_cast<FwAssertArgType>(n_params));
471 8 FW_ASSERT(params[0].tag == spacewasm_valtype_t::SPACEWASM_I32, params[0].tag);
472 8 FW_ASSERT(params[1].tag == spacewasm_valtype_t::SPACEWASM_I32, params[1].tag);
473 8 FW_ASSERT(params[2].tag == spacewasm_valtype_t::SPACEWASM_I32, params[2].tag);
474
475 8 const I32 index = params[0].u.i32_;
476 8 const U32 ptr = static_cast<U32>(params[1].u.i32_);
477 8 const U32 len = static_cast<U32>(params[2].u.i32_);
478
479 // Reject if the payload does not fit the copy-out buffer. A serialOut buffer that was never
480 // configured (serialOutMaxSize == 0 in configure()) is null-backed with capacity 0; trap even a
481 // zero-length send in that case rather than forwarding a null-backed buffer to serialOut_out.
482
5/6
✓ Branch 6 taken 5 times.
✓ Branch 7 taken 3 times.
✗ Branch 14 not taken.
✓ Branch 15 taken 5 times.
✓ Branch 16 taken 3 times.
✓ Branch 17 taken 5 times.
8 if (len > this->m_serialOutBuffer.getCapacity() || this->m_serialOutBuffer.getBuffAddr() == nullptr) {
483
2/2
✓ Branch 6 taken 3 times.
✓ Branch 10 taken 3 times.
6 this->log_WARNING_HI_BufferTooLarge(WasmSequencer_HostFunction::SERIAL_OUT, len,
484 3 static_cast<U32>(this->m_serialOutBuffer.getCapacity()));
485 3 return SPACEWASM_TRAP;
486 }
487
488
5/6
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 4 times.
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 2 times.
✓ Branch 6 taken 3 times.
9 if (index < 0 || index >= this->getNum_serialOut_OutputPorts() ||
489
2/2
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 3 times.
4 !this->isConnected_serialOut_OutputPort(static_cast<FwIndexType>(index))) {
490
2/2
✓ Branch 6 taken 2 times.
✓ Branch 10 taken 2 times.
4 this->log_WARNING_HI_HostFunctionInvalidPort(WasmSequencer_HostFunction::SERIAL_OUT, index,
491 2 static_cast<U32>(this->getNum_serialOut_OutputPorts()));
492 2 return SPACEWASM_TRAP;
493 }
494
495 3 this->m_pendingHostFunction.kind = WasmSequencer_HostFunction::SERIAL_OUT;
496 3 this->m_pendingHostFunction.caller = caller;
497 3 this->m_pendingHostFunction.u.serialOut.index = static_cast<U32>(index);
498 3 this->m_pendingHostFunction.u.serialOut.ptr = ptr;
499 3 this->m_pendingHostFunction.u.serialOut.len = len;
500
501 // Always pause the interpreter to allow the state machine to process this request
502 3 return SPACEWASM_PAUSE;
503 }
504
505 12 spacewasm_hostcall_result_t WasmSequencer::wasmSerialRecv(spacewasm_caller_t* caller,
506 const spacewasm_value_t* params,
507 size_t n_params,
508 spacewasm_value_t*) {
509 12 FW_ASSERT(!this->m_pendingHostFunction.isPending());
510 // These are automatically validated by spacewasm so it should be safe to assert them
511 12 FW_ASSERT(params != nullptr);
512 12 FW_ASSERT(n_params == 5, static_cast<FwAssertArgType>(n_params));
513 12 FW_ASSERT(params[0].tag == spacewasm_valtype_t::SPACEWASM_I32, params[0].tag);
514 12 FW_ASSERT(params[1].tag == spacewasm_valtype_t::SPACEWASM_I32, params[1].tag);
515 12 FW_ASSERT(params[2].tag == spacewasm_valtype_t::SPACEWASM_I32, params[2].tag);
516 12 FW_ASSERT(params[3].tag == spacewasm_valtype_t::SPACEWASM_I32, params[3].tag);
517 12 FW_ASSERT(params[4].tag == spacewasm_valtype_t::SPACEWASM_I32, params[4].tag);
518
519 12 const I32 index = params[0].u.i32_;
520 12 const U32 data_ptr = static_cast<U32>(params[1].u.i32_);
521 12 const U32 data_len = static_cast<U32>(params[2].u.i32_);
522 12 const U32 actual_size_ptr = static_cast<U32>(params[3].u.i32_);
523 12 const I32 block_type = params[4].u.i32_;
524
525
3/4
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 11 times.
12 if (index < 0 || index >= NUM_SERIALIN_INPUT_PORTS) {
526
2/2
✓ Branch 6 taken 1 times.
✓ Branch 10 taken 1 times.
1 this->log_WARNING_HI_HostFunctionInvalidPort(WasmSequencer_HostFunction::SERIAL_RECV, index,
527 NUM_SERIALIN_INPUT_PORTS);
528 1 return SPACEWASM_TRAP;
529 }
530
531
5/6
✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 10 times.
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 10 times.
21 if (block_type < 0 || block_type > static_cast<I32>(std::numeric_limits<Svc::BlockState::SerialType>::max()) ||
532
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 10 times.
10 !Svc::BlockState::isValid(static_cast<Svc::BlockState::SerialType>(block_type))) {
533 1 this->log_WARNING_HI_InvalidBlockingTypeValue(block_type);
534 1 return SPACEWASM_TRAP;
535 }
536
537 10 this->m_pendingHostFunction.kind = WasmSequencer_HostFunction::SERIAL_RECV;
538 10 this->m_pendingHostFunction.caller = caller;
539 10 this->m_pendingHostFunction.u.serialRecv.index = static_cast<U32>(index);
540 10 this->m_pendingHostFunction.u.serialRecv.dataPtr = data_ptr;
541 10 this->m_pendingHostFunction.u.serialRecv.dataSize = data_len;
542 10 this->m_pendingHostFunction.u.serialRecv.actualSizePtr = actual_size_ptr;
543 10 this->m_pendingHostFunction.u.serialRecv.blockingType = static_cast<Svc::BlockState::T>(block_type);
544
545 // Always pause the interpreter to allow the state machine to process this request
546 10 return SPACEWASM_PAUSE;
547 }
548
549 } // namespace Svc
550