GCC Code Coverage Report


Directory: ./
File: Svc/WasmSequencer/WasmSequencerHost.cpp
Date: 2026-10-14 21:15:02
Exec Total Coverage
Lines: 253 253 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 176 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 176 FW_ASSERT(userdata != nullptr);
91 176 return (static_cast<WasmSequencer*>(userdata)->*Handler)(caller, params, n_params, out_result);
92 }
93
94 329 void WasmSequencer ::hostFprimeV1(spacewasm_host_t* host) {
95 spacewasm_status_t status;
96 329 U32 module_idx;
97
1/1
✓ Branch 1 taken 329 times.
329 status = spacewasm_add_host_module(host, "fprime_v1", 12, 0, &module_idx);
98 329 FW_ASSERT(status == SPACEWASM_OK, status);
99
100
1/1
✓ Branch 1 taken 329 times.
329 status = spacewasm_add_host_function(host, module_idx, "exit", "i", "",
101 &hostFunctionTrampoline<&WasmSequencer::wasmExit>, this);
102 329 FW_ASSERT(status == SPACEWASM_OK, status);
103
104
1/1
✓ Branch 1 taken 329 times.
329 status = spacewasm_add_host_function(host, module_idx, "panic", "i", "",
105 &hostFunctionTrampoline<&WasmSequencer::wasmPanic>, this);
106 329 FW_ASSERT(status == SPACEWASM_OK, status);
107
108
1/1
✓ Branch 1 taken 329 times.
329 status = spacewasm_add_host_function(host, module_idx, "args", "ii", "i",
109 &hostFunctionTrampoline<&WasmSequencer::wasmArgs>, this);
110 329 FW_ASSERT(status == SPACEWASM_OK, status);
111
112
1/1
✓ Branch 1 taken 329 times.
329 status = spacewasm_add_host_function(host, module_idx, "time", "ii", "",
113 &hostFunctionTrampoline<&WasmSequencer::wasmTime>, this);
114 329 FW_ASSERT(status == SPACEWASM_OK, status);
115
116
1/1
✓ Branch 1 taken 329 times.
329 status = spacewasm_add_host_function(host, module_idx, "tlm", "Iiiii", "i",
117 &hostFunctionTrampoline<&WasmSequencer::wasmReadTelemetry>, this);
118 329 FW_ASSERT(status == SPACEWASM_OK, status);
119
120
1/1
✓ Branch 1 taken 329 times.
329 status = spacewasm_add_host_function(host, module_idx, "prm", "Iii", "i",
121 &hostFunctionTrampoline<&WasmSequencer::wasmReadParameter>, this);
122 329 FW_ASSERT(status == SPACEWASM_OK, status);
123
124
1/1
✓ Branch 1 taken 329 times.
329 status = spacewasm_add_host_function(host, module_idx, "cmd", "ii", "i",
125 &hostFunctionTrampoline<&WasmSequencer::wasmCommand>, this);
126 329 FW_ASSERT(status == SPACEWASM_OK, status);
127
128
1/1
✓ Branch 1 taken 329 times.
329 status = spacewasm_add_host_function(host, module_idx, "event", "iii", "",
129 &hostFunctionTrampoline<&WasmSequencer::wasmEvent>, this);
130 329 FW_ASSERT(status == SPACEWASM_OK, status);
131
132
1/1
✓ Branch 1 taken 329 times.
329 status = spacewasm_add_host_function(host, module_idx, "rsleep", "I", "",
133 &hostFunctionTrampoline<&WasmSequencer::wasmRsleep>, this);
134 329 FW_ASSERT(status == SPACEWASM_OK, status);
135
136
1/1
✓ Branch 1 taken 329 times.
329 status = spacewasm_add_host_function(host, module_idx, "asleep", "I", "",
137 &hostFunctionTrampoline<&WasmSequencer::wasmAsleep>, this);
138 329 FW_ASSERT(status == SPACEWASM_OK, status);
139
140
1/1
✓ Branch 1 taken 329 times.
329 status = spacewasm_add_host_function(host, module_idx, "serial_send", "iii", "",
141 &hostFunctionTrampoline<&WasmSequencer::wasmSerialOut>, this);
142 329 FW_ASSERT(status == SPACEWASM_OK, status);
143
144
1/1
✓ Branch 1 taken 329 times.
329 status = spacewasm_add_host_function(host, module_idx, "serial_recv", "iiiii", "i",
145 &hostFunctionTrampoline<&WasmSequencer::wasmSerialRecv>, this);
146 329 FW_ASSERT(status == SPACEWASM_OK, status);
147 329 }
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 1 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 1 FW_ASSERT(!this->m_pendingHostFunction.isPending());
186 1 FW_ASSERT(params != nullptr);
187 1 FW_ASSERT(n_params == 2, static_cast<FwAssertArgType>(n_params));
188
189 1 FW_ASSERT(params[0].tag == spacewasm_valtype_t::SPACEWASM_I32, params[0].tag);
190 1 FW_ASSERT(params[1].tag == spacewasm_valtype_t::SPACEWASM_I32, params[1].tag);
191 1 FW_ASSERT(ret != nullptr);
192
193 // Deprecated: this sequencer does not pass arguments to a sequence
194 1 ret->tag = spacewasm_valtype_t::SPACEWASM_I32;
195 1 ret->u.i32_ = 0;
196
197 1 return SPACEWASM_CONTINUE_SOME;
198 }
199
200 4 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 4 FW_ASSERT(!this->m_pendingHostFunction.isPending());
205 4 FW_ASSERT(params != nullptr);
206 4 FW_ASSERT(n_params == 2, static_cast<FwAssertArgType>(n_params));
207 4 FW_ASSERT(params[0].tag == spacewasm_valtype_t::SPACEWASM_I32, params[0].tag);
208 4 FW_ASSERT(params[1].tag == spacewasm_valtype_t::SPACEWASM_I32, params[1].tag);
209
210 4 const U32 time_ptr = static_cast<U32>(params[0].u.i32_);
211 4 const U32 time_size = static_cast<U32>(params[1].u.i32_);
212
213 spacewasm_hostcall_result_t return_status;
214
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 3 times.
4 if (time_size < Fw::Time::SERIALIZED_SIZE) {
215 // We expect an exact match for serialized time
216
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);
217 1 return_status = SPACEWASM_TRAP;
218
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 2 times.
3 } else if (time_size > Fw::Time::SERIALIZED_SIZE) {
219 // We expect an exact match for serialized time
220
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);
221 1 return_status = SPACEWASM_TRAP;
222 } else {
223 2 this->m_pendingHostFunction.kind = WasmSequencer_HostFunction::TIME;
224 2 this->m_pendingHostFunction.caller = caller;
225 2 this->m_pendingHostFunction.u.time.ptr = time_ptr;
226 2 this->m_pendingHostFunction.u.time.len = time_size;
227
228 // Always pause the interpreter to allow the state machine to process this request
229 2 return_status = SPACEWASM_PAUSE;
230 }
231
232 4 return return_status;
233 }
234
235 11 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 11 FW_ASSERT(!this->m_pendingHostFunction.isPending());
240 // These are automatically validated by spacewasm so it should be safe to assert them
241 11 FW_ASSERT(params != nullptr);
242 11 FW_ASSERT(n_params == 5, static_cast<FwAssertArgType>(n_params));
243
244 // FwChanIdType always in 64-bits
245 11 FW_ASSERT(params[0].tag == spacewasm_valtype_t::SPACEWASM_I64, params[0].tag);
246 11 FW_ASSERT(params[1].tag == spacewasm_valtype_t::SPACEWASM_I32, params[1].tag);
247 11 FW_ASSERT(params[2].tag == spacewasm_valtype_t::SPACEWASM_I32, params[2].tag);
248 11 FW_ASSERT(params[3].tag == spacewasm_valtype_t::SPACEWASM_I32, params[3].tag);
249 11 FW_ASSERT(params[4].tag == spacewasm_valtype_t::SPACEWASM_I32, params[4].tag);
250
251 11 I64 id = params[0].u.i64_;
252 11 const U32 time_ptr = static_cast<U32>(params[1].u.i32_);
253 11 const U32 time_size = static_cast<U32>(params[2].u.i32_);
254 11 const U32 value_ptr = static_cast<U32>(params[3].u.i32_);
255 11 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
2/2
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 10 times.
11 if (!this->isConnected_getTlmChan_OutputPort(0)) {
260
2/2
✓ Branch 6 taken 1 times.
✓ Branch 10 taken 1 times.
1 this->log_WARNING_HI_HostFunctionInvalidPort(WasmSequencer_HostFunction::TELEMETRY, 0, 0);
261 1 return SPACEWASM_TRAP;
262 }
263
264 spacewasm_hostcall_result_t return_status;
265
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 9 times.
10 if (time_size < Fw::Time::SERIALIZED_SIZE) {
266 // We expect an exact match for serialized time
267
2/2
✓ Branch 6 taken 1 times.
✓ Branch 10 taken 1 times.
1 this->log_WARNING_HI_BufferTooSmall(WasmSequencer_HostFunction::TELEMETRY, time_size,
268 Fw::Time::SERIALIZED_SIZE);
269 1 return_status = SPACEWASM_TRAP;
270
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 8 times.
9 } else if (time_size > Fw::Time::SERIALIZED_SIZE) {
271 // We expect an exact match for serialized time
272
2/2
✓ Branch 6 taken 1 times.
✓ Branch 10 taken 1 times.
1 this->log_WARNING_HI_BufferTooLarge(WasmSequencer_HostFunction::TELEMETRY, time_size,
273 Fw::Time::SERIALIZED_SIZE);
274 1 return_status = SPACEWASM_TRAP;
275
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())) {
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
2/2
✓ Branch 6 taken 2 times.
✓ Branch 10 taken 2 times.
2 this->log_WARNING_HI_HostFunctionInvalidId(WasmSequencer_HostFunction::TELEMETRY, id);
279 2 return_status = SPACEWASM_TRAP;
280 } else {
281 6 this->m_pendingHostFunction.kind = WasmSequencer_HostFunction::TELEMETRY;
282 6 this->m_pendingHostFunction.caller = caller;
283 6 this->m_pendingHostFunction.u.telemetry.chanId = static_cast<FwChanIdType>(id);
284 6 this->m_pendingHostFunction.u.telemetry.timePtr = time_ptr;
285 6 this->m_pendingHostFunction.u.telemetry.timeLen = time_size;
286 6 this->m_pendingHostFunction.u.telemetry.valuePtr = value_ptr;
287 6 this->m_pendingHostFunction.u.telemetry.valueLen = value_size;
288
289 // Always pause the interpreter to allow the state machine to process this request
290 6 return_status = SPACEWASM_PAUSE;
291 }
292
293 10 return return_status;
294 }
295
296 8 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 8 FW_ASSERT(!this->m_pendingHostFunction.isPending());
301 // These are automatically validated by spacewasm so it should be safe to assert them
302 8 FW_ASSERT(params != nullptr);
303 8 FW_ASSERT(n_params == 3, static_cast<FwAssertArgType>(n_params));
304
305 // FwPrmIdType always in 64-bits
306 8 FW_ASSERT(params[0].tag == spacewasm_valtype_t::SPACEWASM_I64, params[0].tag);
307 8 FW_ASSERT(params[1].tag == spacewasm_valtype_t::SPACEWASM_I32, params[1].tag);
308 8 FW_ASSERT(params[2].tag == spacewasm_valtype_t::SPACEWASM_I32, params[2].tag);
309
310 8 I64 id = params[0].u.i64_;
311 8 const U32 ptr = static_cast<U32>(params[1].u.i32_);
312 8 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
2/2
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 7 times.
8 if (!this->isConnected_getParam_OutputPort(0)) {
317
2/2
✓ Branch 6 taken 1 times.
✓ Branch 10 taken 1 times.
1 this->log_WARNING_HI_HostFunctionInvalidPort(WasmSequencer_HostFunction::PARAMETER, 0, 0);
318 1 return SPACEWASM_TRAP;
319 }
320
321
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())) {
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
2/2
✓ Branch 6 taken 2 times.
✓ Branch 10 taken 2 times.
2 this->log_WARNING_HI_HostFunctionInvalidId(WasmSequencer_HostFunction::PARAMETER, id);
326 2 return SPACEWASM_TRAP;
327 }
328
329 5 this->m_pendingHostFunction.kind = WasmSequencer_HostFunction::PARAMETER;
330 5 this->m_pendingHostFunction.caller = caller;
331 5 this->m_pendingHostFunction.u.parameter.prmId = static_cast<FwPrmIdType>(id);
332 5 this->m_pendingHostFunction.u.parameter.ptr = ptr;
333 5 this->m_pendingHostFunction.u.parameter.len = len;
334
335 // Always pause the interpreter to allow the state machine to process this request
336 5 return SPACEWASM_PAUSE;
337 }
338
339 20 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 20 FW_ASSERT(!this->m_pendingHostFunction.isPending());
344 // These are automatically validated by spacewasm so it should be safe to assert them
345 20 FW_ASSERT(params != nullptr);
346 20 FW_ASSERT(n_params == 2, static_cast<FwAssertArgType>(n_params));
347 20 FW_ASSERT(params[0].tag == spacewasm_valtype_t::SPACEWASM_I32, params[0].tag);
348 20 FW_ASSERT(params[1].tag == spacewasm_valtype_t::SPACEWASM_I32, params[1].tag);
349
350 20 const U32 ptr = static_cast<U32>(params[0].u.i32_);
351 20 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
2/2
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 19 times.
20 if (!this->isConnected_cmdOut_OutputPort(0)) {
356
2/2
✓ Branch 6 taken 1 times.
✓ Branch 10 taken 1 times.
1 this->log_WARNING_HI_HostFunctionInvalidPort(WasmSequencer_HostFunction::COMMAND, 0, 0);
357 1 return SPACEWASM_TRAP;
358 }
359
360 19 constexpr const U32 maxPayload = FW_COM_BUFFER_MAX_SIZE - sizeof(FwPacketDescriptorType);
361 spacewasm_hostcall_result_t return_status;
362
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 18 times.
19 if (len > maxPayload) {
363
2/2
✓ Branch 6 taken 1 times.
✓ Branch 10 taken 1 times.
1 this->log_WARNING_HI_BufferTooLarge(WasmSequencer_HostFunction::COMMAND, len, maxPayload);
364 1 return_status = SPACEWASM_TRAP;
365 } else {
366 18 this->m_pendingHostFunction.kind = WasmSequencer_HostFunction::COMMAND;
367 18 this->m_pendingHostFunction.caller = caller;
368 18 this->m_pendingHostFunction.u.command.ptr = ptr;
369 18 this->m_pendingHostFunction.u.command.len = len;
370
371 // Always pause the interpreter to allow the state machine to process this request
372 18 return_status = SPACEWASM_PAUSE;
373 }
374
375 19 return return_status;
376 }
377
378 11 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 11 FW_ASSERT(!this->m_pendingHostFunction.isPending());
383 // These are automatically validated by spacewasm so it should be safe to assert them
384 11 FW_ASSERT(params != nullptr);
385 11 FW_ASSERT(n_params == 3, static_cast<FwAssertArgType>(n_params));
386 11 FW_ASSERT(params[0].tag == spacewasm_valtype_t::SPACEWASM_I32, params[0].tag);
387 11 FW_ASSERT(params[1].tag == spacewasm_valtype_t::SPACEWASM_I32, params[1].tag);
388 11 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 11 U32 ptr = static_cast<U32>(params[1].u.i32_);
395 11 U32 len = static_cast<U32>(params[2].u.i32_);
396
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 10 times.
11 if (len > FW_LOG_STRING_MAX_SIZE) {
397 1 len = FW_LOG_STRING_MAX_SIZE;
398 }
399
400 // Pend this event dispatch to be executed by the sequencer state machine
401 11 this->m_pendingHostFunction.kind = WasmSequencer_HostFunction::EVENT;
402 11 this->m_pendingHostFunction.caller = caller;
403 11 this->m_pendingHostFunction.u.event.rawSeverity = static_cast<U32>(params[0].u.i32_);
404 11 this->m_pendingHostFunction.u.event.msgPtr = ptr;
405 11 this->m_pendingHostFunction.u.event.msgLen = len;
406
407 11 return SPACEWASM_PAUSE;
408 }
409
410 5 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 5 FW_ASSERT(!this->m_pendingHostFunction.isPending());
415 5 FW_ASSERT(params != nullptr);
416 5 FW_ASSERT(n_params == 1, static_cast<FwAssertArgType>(n_params));
417 5 FW_ASSERT(params[0].tag == spacewasm_valtype_t::SPACEWASM_I64, params[0].tag);
418
419 5 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
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 4 times.
5 if ((us / 1000000u) > static_cast<U64>(std::numeric_limits<U32>::max())) {
424
2/2
✓ Branch 6 taken 1 times.
✓ Branch 10 taken 1 times.
1 this->log_WARNING_HI_SleepDurationTooLarge(WasmSequencer_HostFunction::RSLEEP, us);
425 1 return SPACEWASM_TRAP;
426 }
427
428 4 this->m_pendingHostFunction.kind = WasmSequencer_HostFunction::RSLEEP;
429 4 this->m_pendingHostFunction.caller = caller;
430 4 this->m_pendingHostFunction.u.rsleep.us = us;
431
432 4 return SPACEWASM_PAUSE;
433 }
434
435 2 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 2 FW_ASSERT(!this->m_pendingHostFunction.isPending());
440 2 FW_ASSERT(params != nullptr);
441 2 FW_ASSERT(n_params == 1, static_cast<FwAssertArgType>(n_params));
442 2 FW_ASSERT(params[0].tag == spacewasm_valtype_t::SPACEWASM_I64, params[0].tag);
443
444 2 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
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 1 times.
2 if ((us / 1000000u) > static_cast<U64>(std::numeric_limits<U32>::max())) {
449
2/2
✓ Branch 6 taken 1 times.
✓ Branch 10 taken 1 times.
1 this->log_WARNING_HI_SleepDurationTooLarge(WasmSequencer_HostFunction::ASLEEP, us);
450 1 return SPACEWASM_TRAP;
451 }
452
453 1 this->m_pendingHostFunction.kind = WasmSequencer_HostFunction::ASLEEP;
454 1 this->m_pendingHostFunction.caller = caller;
455 1 this->m_pendingHostFunction.u.asleep.us = us;
456
457 1 return SPACEWASM_PAUSE;
458 }
459
460 8 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 8 FW_ASSERT(!this->m_pendingHostFunction.isPending());
465 // These are automatically validated by spacewasm so it should be safe to assert them
466 8 FW_ASSERT(params != nullptr);
467 8 FW_ASSERT(n_params == 3, static_cast<FwAssertArgType>(n_params));
468 8 FW_ASSERT(params[0].tag == spacewasm_valtype_t::SPACEWASM_I32, params[0].tag);
469 8 FW_ASSERT(params[1].tag == spacewasm_valtype_t::SPACEWASM_I32, params[1].tag);
470 8 FW_ASSERT(params[2].tag == spacewasm_valtype_t::SPACEWASM_I32, params[2].tag);
471
472 8 const I32 index = params[0].u.i32_;
473 8 const U32 ptr = static_cast<U32>(params[1].u.i32_);
474 8 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
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) {
480
2/2
✓ Branch 6 taken 3 times.
✓ Branch 10 taken 3 times.
6 this->log_WARNING_HI_BufferTooLarge(WasmSequencer_HostFunction::SERIAL_OUT, len,
481 3 static_cast<U32>(this->m_serialOutBuffer.getCapacity()));
482 3 return SPACEWASM_TRAP;
483 }
484
485
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() ||
486
2/2
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 3 times.
4 !this->isConnected_serialOut_OutputPort(static_cast<FwIndexType>(index))) {
487
2/2
✓ Branch 6 taken 2 times.
✓ Branch 10 taken 2 times.
4 this->log_WARNING_HI_HostFunctionInvalidPort(WasmSequencer_HostFunction::SERIAL_OUT, index,
488 2 static_cast<U32>(this->getNum_serialOut_OutputPorts()));
489 2 return SPACEWASM_TRAP;
490 }
491
492 3 this->m_pendingHostFunction.kind = WasmSequencer_HostFunction::SERIAL_OUT;
493 3 this->m_pendingHostFunction.caller = caller;
494 3 this->m_pendingHostFunction.u.serialOut.index = static_cast<U32>(index);
495 3 this->m_pendingHostFunction.u.serialOut.ptr = ptr;
496 3 this->m_pendingHostFunction.u.serialOut.len = len;
497
498 // Always pause the interpreter to allow the state machine to process this request
499 3 return SPACEWASM_PAUSE;
500 }
501
502 12 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 12 FW_ASSERT(!this->m_pendingHostFunction.isPending());
507 // These are automatically validated by spacewasm so it should be safe to assert them
508 12 FW_ASSERT(params != nullptr);
509 12 FW_ASSERT(n_params == 5, static_cast<FwAssertArgType>(n_params));
510 12 FW_ASSERT(params[0].tag == spacewasm_valtype_t::SPACEWASM_I32, params[0].tag);
511 12 FW_ASSERT(params[1].tag == spacewasm_valtype_t::SPACEWASM_I32, params[1].tag);
512 12 FW_ASSERT(params[2].tag == spacewasm_valtype_t::SPACEWASM_I32, params[2].tag);
513 12 FW_ASSERT(params[3].tag == spacewasm_valtype_t::SPACEWASM_I32, params[3].tag);
514 12 FW_ASSERT(params[4].tag == spacewasm_valtype_t::SPACEWASM_I32, params[4].tag);
515
516 12 const I32 index = params[0].u.i32_;
517 12 const U32 data_ptr = static_cast<U32>(params[1].u.i32_);
518 12 const U32 data_len = static_cast<U32>(params[2].u.i32_);
519 12 const U32 actual_size_ptr = static_cast<U32>(params[3].u.i32_);
520 12 const I32 block_type = params[4].u.i32_;
521
522
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) {
523
2/2
✓ Branch 6 taken 1 times.
✓ Branch 10 taken 1 times.
1 this->log_WARNING_HI_HostFunctionInvalidPort(WasmSequencer_HostFunction::SERIAL_RECV, index,
524 NUM_SERIALIN_INPUT_PORTS);
525 1 return SPACEWASM_TRAP;
526 }
527
528
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()) ||
529
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 10 times.
10 !Svc::BlockState::isValid(static_cast<Svc::BlockState::SerialType>(block_type))) {
530 1 this->log_WARNING_HI_InvalidBlockingTypeValue(block_type);
531 1 return SPACEWASM_TRAP;
532 }
533
534 10 this->m_pendingHostFunction.kind = WasmSequencer_HostFunction::SERIAL_RECV;
535 10 this->m_pendingHostFunction.caller = caller;
536 10 this->m_pendingHostFunction.u.serialRecv.index = static_cast<U32>(index);
537 10 this->m_pendingHostFunction.u.serialRecv.dataPtr = data_ptr;
538 10 this->m_pendingHostFunction.u.serialRecv.dataSize = data_len;
539 10 this->m_pendingHostFunction.u.serialRecv.actualSizePtr = actual_size_ptr;
540 10 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 10 return SPACEWASM_PAUSE;
544 }
545
546 } // namespace Svc
547